summaryrefslogtreecommitdiff
path: root/drivers/staging
diff options
context:
space:
mode:
authorAndré Fabian Silva Delgado <emulatorman@parabola.nu>2015-08-05 17:04:01 -0300
committerAndré Fabian Silva Delgado <emulatorman@parabola.nu>2015-08-05 17:04:01 -0300
commit57f0f512b273f60d52568b8c6b77e17f5636edc0 (patch)
tree5e910f0e82173f4ef4f51111366a3f1299037a7b /drivers/staging
Initial import
Diffstat (limited to 'drivers/staging')
-rw-r--r--drivers/staging/Kconfig117
-rw-r--r--drivers/staging/Makefile51
-rw-r--r--drivers/staging/android/Kconfig63
-rw-r--r--drivers/staging/android/Makefile10
-rw-r--r--drivers/staging/android/TODO17
-rw-r--r--drivers/staging/android/ashmem.c883
-rw-r--r--drivers/staging/android/ashmem.h27
-rw-r--r--drivers/staging/android/ion/Kconfig35
-rw-r--r--drivers/staging/android/ion/Makefile10
-rw-r--r--drivers/staging/android/ion/compat_ion.c195
-rw-r--r--drivers/staging/android/ion/compat_ion.h30
-rw-r--r--drivers/staging/android/ion/ion.c1652
-rw-r--r--drivers/staging/android/ion/ion.h203
-rw-r--r--drivers/staging/android/ion/ion_carveout_heap.c193
-rw-r--r--drivers/staging/android/ion/ion_chunk_heap.c194
-rw-r--r--drivers/staging/android/ion/ion_cma_heap.c195
-rw-r--r--drivers/staging/android/ion/ion_dummy_driver.c154
-rw-r--r--drivers/staging/android/ion/ion_heap.c381
-rw-r--r--drivers/staging/android/ion/ion_page_pool.c182
-rw-r--r--drivers/staging/android/ion/ion_priv.h405
-rw-r--r--drivers/staging/android/ion/ion_system_heap.c422
-rw-r--r--drivers/staging/android/ion/ion_test.c283
-rw-r--r--drivers/staging/android/ion/tegra/Makefile1
-rw-r--r--drivers/staging/android/ion/tegra/tegra_ion.c80
-rw-r--r--drivers/staging/android/lowmemorykiller.c207
-rw-r--r--drivers/staging/android/sw_sync.c267
-rw-r--r--drivers/staging/android/sw_sync.h59
-rw-r--r--drivers/staging/android/sync.c729
-rw-r--r--drivers/staging/android/sync.h356
-rw-r--r--drivers/staging/android/sync_debug.c254
-rw-r--r--drivers/staging/android/timed_gpio.c168
-rw-r--r--drivers/staging/android/timed_gpio.h33
-rw-r--r--drivers/staging/android/timed_output.c120
-rw-r--r--drivers/staging/android/timed_output.h37
-rw-r--r--drivers/staging/android/trace/sync.h82
-rw-r--r--drivers/staging/android/uapi/ashmem.h47
-rw-r--r--drivers/staging/android/uapi/ion.h196
-rw-r--r--drivers/staging/android/uapi/ion_test.h70
-rw-r--r--drivers/staging/android/uapi/sw_sync.h32
-rw-r--r--drivers/staging/android/uapi/sync.h97
-rw-r--r--drivers/staging/board/Kconfig9
-rw-r--r--drivers/staging/board/Makefile2
-rw-r--r--drivers/staging/board/TODO2
-rw-r--r--drivers/staging/board/board.c40
-rw-r--r--drivers/staging/board/board.h20
-rw-r--r--drivers/staging/board/kzm9d.c19
-rw-r--r--drivers/staging/clocking-wizard/Kconfig9
-rw-r--r--drivers/staging/clocking-wizard/Makefile1
-rw-r--r--drivers/staging/clocking-wizard/TODO12
-rw-r--r--drivers/staging/clocking-wizard/clk-xlnx-clock-wizard.c344
-rw-r--r--drivers/staging/clocking-wizard/dt-binding.txt30
-rw-r--r--drivers/staging/comedi/Kconfig1306
-rw-r--r--drivers/staging/comedi/Makefile15
-rw-r--r--drivers/staging/comedi/TODO11
-rw-r--r--drivers/staging/comedi/comedi.h937
-rw-r--r--drivers/staging/comedi/comedi_buf.c550
-rw-r--r--drivers/staging/comedi/comedi_compat32.c463
-rw-r--r--drivers/staging/comedi/comedi_compat32.h36
-rw-r--r--drivers/staging/comedi/comedi_fops.c2921
-rw-r--r--drivers/staging/comedi/comedi_internal.h67
-rw-r--r--drivers/staging/comedi/comedi_pci.c185
-rw-r--r--drivers/staging/comedi/comedi_pci.h64
-rw-r--r--drivers/staging/comedi/comedi_pcmcia.c169
-rw-r--r--drivers/staging/comedi/comedi_pcmcia.h55
-rw-r--r--drivers/staging/comedi/comedi_usb.c131
-rw-r--r--drivers/staging/comedi/comedi_usb.h50
-rw-r--r--drivers/staging/comedi/comedidev.h635
-rw-r--r--drivers/staging/comedi/comedilib.h35
-rw-r--r--drivers/staging/comedi/drivers.c956
-rw-r--r--drivers/staging/comedi/drivers/8255.c342
-rw-r--r--drivers/staging/comedi/drivers/8255.h48
-rw-r--r--drivers/staging/comedi/drivers/8255_pci.c304
-rw-r--r--drivers/staging/comedi/drivers/Makefile145
-rw-r--r--drivers/staging/comedi/drivers/addi-data/hwdrv_apci1564.c204
-rw-r--r--drivers/staging/comedi/drivers/addi-data/hwdrv_apci3501.c173
-rw-r--r--drivers/staging/comedi/drivers/addi_apci_1032.c394
-rw-r--r--drivers/staging/comedi/drivers/addi_apci_1500.c878
-rw-r--r--drivers/staging/comedi/drivers/addi_apci_1516.c225
-rw-r--r--drivers/staging/comedi/drivers/addi_apci_1564.c593
-rw-r--r--drivers/staging/comedi/drivers/addi_apci_16xx.c187
-rw-r--r--drivers/staging/comedi/drivers/addi_apci_2032.c339
-rw-r--r--drivers/staging/comedi/drivers/addi_apci_2200.c152
-rw-r--r--drivers/staging/comedi/drivers/addi_apci_3120.c1129
-rw-r--r--drivers/staging/comedi/drivers/addi_apci_3501.c448
-rw-r--r--drivers/staging/comedi/drivers/addi_apci_3xxx.c968
-rw-r--r--drivers/staging/comedi/drivers/addi_tcw.h56
-rw-r--r--drivers/staging/comedi/drivers/addi_watchdog.c149
-rw-r--r--drivers/staging/comedi/drivers/addi_watchdog.h9
-rw-r--r--drivers/staging/comedi/drivers/adl_pci6208.c211
-rw-r--r--drivers/staging/comedi/drivers/adl_pci7x3x.c275
-rw-r--r--drivers/staging/comedi/drivers/adl_pci8164.c163
-rw-r--r--drivers/staging/comedi/drivers/adl_pci9111.c775
-rw-r--r--drivers/staging/comedi/drivers/adl_pci9118.c1761
-rw-r--r--drivers/staging/comedi/drivers/adq12b.c268
-rw-r--r--drivers/staging/comedi/drivers/adv_pci1710.c1100
-rw-r--r--drivers/staging/comedi/drivers/adv_pci1723.c233
-rw-r--r--drivers/staging/comedi/drivers/adv_pci1724.c220
-rw-r--r--drivers/staging/comedi/drivers/adv_pci_dio.c1113
-rw-r--r--drivers/staging/comedi/drivers/aio_aio12_8.c249
-rw-r--r--drivers/staging/comedi/drivers/aio_iiro_16.c244
-rw-r--r--drivers/staging/comedi/drivers/amcc_s5933.h176
-rw-r--r--drivers/staging/comedi/drivers/amplc_dio200.c273
-rw-r--r--drivers/staging/comedi/drivers/amplc_dio200.h51
-rw-r--r--drivers/staging/comedi/drivers/amplc_dio200_common.c874
-rw-r--r--drivers/staging/comedi/drivers/amplc_dio200_pci.c423
-rw-r--r--drivers/staging/comedi/drivers/amplc_pc236.c85
-rw-r--r--drivers/staging/comedi/drivers/amplc_pc236.h42
-rw-r--r--drivers/staging/comedi/drivers/amplc_pc236_common.c200
-rw-r--r--drivers/staging/comedi/drivers/amplc_pc263.c111
-rw-r--r--drivers/staging/comedi/drivers/amplc_pci224.c1136
-rw-r--r--drivers/staging/comedi/drivers/amplc_pci230.c2568
-rw-r--r--drivers/staging/comedi/drivers/amplc_pci236.c153
-rw-r--r--drivers/staging/comedi/drivers/amplc_pci263.c114
-rw-r--r--drivers/staging/comedi/drivers/c6xdigio.c307
-rw-r--r--drivers/staging/comedi/drivers/cb_das16_cs.c355
-rw-r--r--drivers/staging/comedi/drivers/cb_pcidas.c1582
-rw-r--r--drivers/staging/comedi/drivers/cb_pcidas64.c4079
-rw-r--r--drivers/staging/comedi/drivers/cb_pcidda.c428
-rw-r--r--drivers/staging/comedi/drivers/cb_pcimdas.c482
-rw-r--r--drivers/staging/comedi/drivers/cb_pcimdda.c206
-rw-r--r--drivers/staging/comedi/drivers/comedi_8254.c664
-rw-r--r--drivers/staging/comedi/drivers/comedi_8254.h133
-rw-r--r--drivers/staging/comedi/drivers/comedi_bond.c355
-rw-r--r--drivers/staging/comedi/drivers/comedi_isadma.c264
-rw-r--r--drivers/staging/comedi/drivers/comedi_isadma.h116
-rw-r--r--drivers/staging/comedi/drivers/comedi_parport.c314
-rw-r--r--drivers/staging/comedi/drivers/comedi_test.c456
-rw-r--r--drivers/staging/comedi/drivers/contec_pci_dio.c125
-rw-r--r--drivers/staging/comedi/drivers/dac02.c150
-rw-r--r--drivers/staging/comedi/drivers/daqboard2000.c764
-rw-r--r--drivers/staging/comedi/drivers/das08.c489
-rw-r--r--drivers/staging/comedi/drivers/das08.h51
-rw-r--r--drivers/staging/comedi/drivers/das08_cs.c114
-rw-r--r--drivers/staging/comedi/drivers/das08_isa.c199
-rw-r--r--drivers/staging/comedi/drivers/das08_pci.c105
-rw-r--r--drivers/staging/comedi/drivers/das16.c1207
-rw-r--r--drivers/staging/comedi/drivers/das16m1.c644
-rw-r--r--drivers/staging/comedi/drivers/das1800.c1455
-rw-r--r--drivers/staging/comedi/drivers/das6402.c676
-rw-r--r--drivers/staging/comedi/drivers/das800.c748
-rw-r--r--drivers/staging/comedi/drivers/dmm32at.c627
-rw-r--r--drivers/staging/comedi/drivers/dt2801.c639
-rw-r--r--drivers/staging/comedi/drivers/dt2811.c474
-rw-r--r--drivers/staging/comedi/drivers/dt2814.c297
-rw-r--r--drivers/staging/comedi/drivers/dt2815.c223
-rw-r--r--drivers/staging/comedi/drivers/dt2817.c149
-rw-r--r--drivers/staging/comedi/drivers/dt282x.c1211
-rw-r--r--drivers/staging/comedi/drivers/dt3000.c769
-rw-r--r--drivers/staging/comedi/drivers/dt9812.c885
-rw-r--r--drivers/staging/comedi/drivers/dyna_pci10xx.c282
-rw-r--r--drivers/staging/comedi/drivers/fl512.c156
-rw-r--r--drivers/staging/comedi/drivers/gsc_hpdi.c752
-rw-r--r--drivers/staging/comedi/drivers/icp_multi.c568
-rw-r--r--drivers/staging/comedi/drivers/ii_pci20kc.c526
-rw-r--r--drivers/staging/comedi/drivers/jr3_pci.c828
-rw-r--r--drivers/staging/comedi/drivers/jr3_pci.h682
-rw-r--r--drivers/staging/comedi/drivers/ke_counter.c240
-rw-r--r--drivers/staging/comedi/drivers/me4000.c1443
-rw-r--r--drivers/staging/comedi/drivers/me_daq.c583
-rw-r--r--drivers/staging/comedi/drivers/mf6x4.c330
-rw-r--r--drivers/staging/comedi/drivers/mite.c626
-rw-r--r--drivers/staging/comedi/drivers/mite.h347
-rw-r--r--drivers/staging/comedi/drivers/mpc624.c357
-rw-r--r--drivers/staging/comedi/drivers/multiq3.c289
-rw-r--r--drivers/staging/comedi/drivers/ni_6527.c500
-rw-r--r--drivers/staging/comedi/drivers/ni_65xx.c831
-rw-r--r--drivers/staging/comedi/drivers/ni_660x.c1222
-rw-r--r--drivers/staging/comedi/drivers/ni_670x.c296
-rw-r--r--drivers/staging/comedi/drivers/ni_at_a2150.c796
-rw-r--r--drivers/staging/comedi/drivers/ni_at_ao.c383
-rw-r--r--drivers/staging/comedi/drivers/ni_atmio.c378
-rw-r--r--drivers/staging/comedi/drivers/ni_atmio16d.c760
-rw-r--r--drivers/staging/comedi/drivers/ni_daq_700.c289
-rw-r--r--drivers/staging/comedi/drivers/ni_daq_dio24.c95
-rw-r--r--drivers/staging/comedi/drivers/ni_labpc.c125
-rw-r--r--drivers/staging/comedi/drivers/ni_labpc.h69
-rw-r--r--drivers/staging/comedi/drivers/ni_labpc_common.c1355
-rw-r--r--drivers/staging/comedi/drivers/ni_labpc_cs.c128
-rw-r--r--drivers/staging/comedi/drivers/ni_labpc_isadma.c190
-rw-r--r--drivers/staging/comedi/drivers/ni_labpc_isadma.h42
-rw-r--r--drivers/staging/comedi/drivers/ni_labpc_pci.c141
-rw-r--r--drivers/staging/comedi/drivers/ni_labpc_regs.h75
-rw-r--r--drivers/staging/comedi/drivers/ni_mio_common.c5703
-rw-r--r--drivers/staging/comedi/drivers/ni_mio_cs.c228
-rw-r--r--drivers/staging/comedi/drivers/ni_pcidio.c1024
-rw-r--r--drivers/staging/comedi/drivers/ni_pcimio.c1308
-rw-r--r--drivers/staging/comedi/drivers/ni_stc.h1491
-rw-r--r--drivers/staging/comedi/drivers/ni_tio.c1436
-rw-r--r--drivers/staging/comedi/drivers/ni_tio.h154
-rw-r--r--drivers/staging/comedi/drivers/ni_tio_internal.h245
-rw-r--r--drivers/staging/comedi/drivers/ni_tiocmd.c484
-rw-r--r--drivers/staging/comedi/drivers/ni_usb6501.c620
-rw-r--r--drivers/staging/comedi/drivers/pcl711.c521
-rw-r--r--drivers/staging/comedi/drivers/pcl724.c152
-rw-r--r--drivers/staging/comedi/drivers/pcl726.c432
-rw-r--r--drivers/staging/comedi/drivers/pcl730.c349
-rw-r--r--drivers/staging/comedi/drivers/pcl812.c1317
-rw-r--r--drivers/staging/comedi/drivers/pcl816.c712
-rw-r--r--drivers/staging/comedi/drivers/pcl818.c1146
-rw-r--r--drivers/staging/comedi/drivers/pcm3724.c220
-rw-r--r--drivers/staging/comedi/drivers/pcmad.c158
-rw-r--r--drivers/staging/comedi/drivers/pcmda12.c174
-rw-r--r--drivers/staging/comedi/drivers/pcmmio.c786
-rw-r--r--drivers/staging/comedi/drivers/pcmuio.c633
-rw-r--r--drivers/staging/comedi/drivers/plx9052.h79
-rw-r--r--drivers/staging/comedi/drivers/plx9080.h422
-rw-r--r--drivers/staging/comedi/drivers/quatech_daqp_cs.c815
-rw-r--r--drivers/staging/comedi/drivers/rtd520.c1344
-rw-r--r--drivers/staging/comedi/drivers/rti800.c362
-rw-r--r--drivers/staging/comedi/drivers/rti802.c129
-rw-r--r--drivers/staging/comedi/drivers/s526.c614
-rw-r--r--drivers/staging/comedi/drivers/s626.c2925
-rw-r--r--drivers/staging/comedi/drivers/s626.h760
-rw-r--r--drivers/staging/comedi/drivers/serial2002.c798
-rw-r--r--drivers/staging/comedi/drivers/ssv_dnp.c186
-rw-r--r--drivers/staging/comedi/drivers/unioxx5.c506
-rw-r--r--drivers/staging/comedi/drivers/usbdux.c1751
-rw-r--r--drivers/staging/comedi/drivers/usbduxfast.c1107
-rw-r--r--drivers/staging/comedi/drivers/usbduxsigma.c1659
-rw-r--r--drivers/staging/comedi/drivers/vmk80xx.c885
-rw-r--r--drivers/staging/comedi/drivers/z8536.h202
-rw-r--r--drivers/staging/comedi/kcomedilib/Makefile5
-rw-r--r--drivers/staging/comedi/kcomedilib/kcomedilib_main.c252
-rw-r--r--drivers/staging/comedi/proc.c97
-rw-r--r--drivers/staging/comedi/range.c132
-rw-r--r--drivers/staging/dgap/Kconfig6
-rw-r--r--drivers/staging/dgap/Makefile1
-rw-r--r--drivers/staging/dgap/dgap.c7187
-rw-r--r--drivers/staging/dgap/dgap.h1233
-rw-r--r--drivers/staging/dgnc/Kconfig6
-rw-r--r--drivers/staging/dgnc/Makefile6
-rw-r--r--drivers/staging/dgnc/TODO10
-rw-r--r--drivers/staging/dgnc/dgnc_cls.c1316
-rw-r--r--drivers/staging/dgnc/dgnc_cls.h82
-rw-r--r--drivers/staging/dgnc/dgnc_driver.c729
-rw-r--r--drivers/staging/dgnc/dgnc_driver.h400
-rw-r--r--drivers/staging/dgnc/dgnc_mgmt.c262
-rw-r--r--drivers/staging/dgnc/dgnc_mgmt.h25
-rw-r--r--drivers/staging/dgnc/dgnc_neo.c1846
-rw-r--r--drivers/staging/dgnc/dgnc_neo.h149
-rw-r--r--drivers/staging/dgnc/dgnc_pci.h69
-rw-r--r--drivers/staging/dgnc/dgnc_sysfs.c697
-rw-r--r--drivers/staging/dgnc/dgnc_sysfs.h40
-rw-r--r--drivers/staging/dgnc/dgnc_tty.c3057
-rw-r--r--drivers/staging/dgnc/dgnc_tty.h34
-rw-r--r--drivers/staging/dgnc/dgnc_utils.c18
-rw-r--r--drivers/staging/dgnc/dgnc_utils.h6
-rw-r--r--drivers/staging/dgnc/digi.h179
-rw-r--r--drivers/staging/emxx_udc/Kconfig10
-rw-r--r--drivers/staging/emxx_udc/Makefile1
-rw-r--r--drivers/staging/emxx_udc/TODO4
-rw-r--r--drivers/staging/emxx_udc/emxx_udc.c3475
-rw-r--r--drivers/staging/emxx_udc/emxx_udc.h646
-rw-r--r--drivers/staging/fbtft/Kconfig175
-rw-r--r--drivers/staging/fbtft/Makefile35
-rw-r--r--drivers/staging/fbtft/README32
-rw-r--r--drivers/staging/fbtft/fb_agm1264k-fl.c465
-rw-r--r--drivers/staging/fbtft/fb_bd663474.c193
-rw-r--r--drivers/staging/fbtft/fb_hx8340bn.c220
-rw-r--r--drivers/staging/fbtft/fb_hx8347d.c180
-rw-r--r--drivers/staging/fbtft/fb_hx8353d.c166
-rw-r--r--drivers/staging/fbtft/fb_ili9163.c303
-rw-r--r--drivers/staging/fbtft/fb_ili9320.c234
-rw-r--r--drivers/staging/fbtft/fb_ili9325.c290
-rw-r--r--drivers/staging/fbtft/fb_ili9340.c163
-rw-r--r--drivers/staging/fbtft/fb_ili9341.c179
-rw-r--r--drivers/staging/fbtft/fb_ili9481.c117
-rw-r--r--drivers/staging/fbtft/fb_ili9486.c121
-rw-r--r--drivers/staging/fbtft/fb_pcd8544.c189
-rw-r--r--drivers/staging/fbtft/fb_ra8875.c331
-rw-r--r--drivers/staging/fbtft/fb_s6d02a1.c168
-rw-r--r--drivers/staging/fbtft/fb_s6d1121.c206
-rw-r--r--drivers/staging/fbtft/fb_ssd1289.c205
-rw-r--r--drivers/staging/fbtft/fb_ssd1306.c229
-rw-r--r--drivers/staging/fbtft/fb_ssd1331.c205
-rw-r--r--drivers/staging/fbtft/fb_ssd1351.c255
-rw-r--r--drivers/staging/fbtft/fb_st7735r.c195
-rw-r--r--drivers/staging/fbtft/fb_tinylcd.c124
-rw-r--r--drivers/staging/fbtft/fb_tls8204.c176
-rw-r--r--drivers/staging/fbtft/fb_uc1701.c211
-rw-r--r--drivers/staging/fbtft/fb_upd161704.c206
-rw-r--r--drivers/staging/fbtft/fb_watterott.c324
-rw-r--r--drivers/staging/fbtft/fbtft-bus.c256
-rw-r--r--drivers/staging/fbtft/fbtft-core.c1509
-rw-r--r--drivers/staging/fbtft/fbtft-io.c238
-rw-r--r--drivers/staging/fbtft/fbtft-sysfs.c221
-rw-r--r--drivers/staging/fbtft/fbtft.h445
-rw-r--r--drivers/staging/fbtft/fbtft_device.c1488
-rw-r--r--drivers/staging/fbtft/flexfb.c592
-rw-r--r--drivers/staging/fbtft/internal.h25
-rw-r--r--drivers/staging/fsl-mc/Kconfig1
-rw-r--r--drivers/staging/fsl-mc/Makefile2
-rw-r--r--drivers/staging/fsl-mc/TODO13
-rw-r--r--drivers/staging/fsl-mc/bus/Kconfig24
-rw-r--r--drivers/staging/fsl-mc/bus/Makefile17
-rw-r--r--drivers/staging/fsl-mc/bus/dpbp.c358
-rw-r--r--drivers/staging/fsl-mc/bus/dpmcp-cmd.h136
-rw-r--r--drivers/staging/fsl-mc/bus/dpmcp.c308
-rw-r--r--drivers/staging/fsl-mc/bus/dpmcp.h311
-rw-r--r--drivers/staging/fsl-mc/bus/dpmng-cmd.h47
-rw-r--r--drivers/staging/fsl-mc/bus/dpmng.c78
-rw-r--r--drivers/staging/fsl-mc/bus/dprc-cmd.h84
-rw-r--r--drivers/staging/fsl-mc/bus/dprc-driver.c486
-rw-r--r--drivers/staging/fsl-mc/bus/dprc.c913
-rw-r--r--drivers/staging/fsl-mc/bus/mc-allocator.c573
-rw-r--r--drivers/staging/fsl-mc/bus/mc-bus.c793
-rw-r--r--drivers/staging/fsl-mc/bus/mc-sys.c287
-rw-r--r--drivers/staging/fsl-mc/include/dpbp-cmd.h60
-rw-r--r--drivers/staging/fsl-mc/include/dpbp.h330
-rw-r--r--drivers/staging/fsl-mc/include/dpcon-cmd.h62
-rw-r--r--drivers/staging/fsl-mc/include/dpmng.h80
-rw-r--r--drivers/staging/fsl-mc/include/dprc.h801
-rw-r--r--drivers/staging/fsl-mc/include/mc-cmd.h113
-rw-r--r--drivers/staging/fsl-mc/include/mc-private.h116
-rw-r--r--drivers/staging/fsl-mc/include/mc-sys.h76
-rw-r--r--drivers/staging/fsl-mc/include/mc.h201
-rw-r--r--drivers/staging/ft1000/Kconfig22
-rw-r--r--drivers/staging/ft1000/Makefile3
-rw-r--r--drivers/staging/ft1000/TODO9
-rw-r--r--drivers/staging/ft1000/ft1000-pcmcia/Makefile2
-rw-r--r--drivers/staging/ft1000/ft1000-pcmcia/boot.h34
-rw-r--r--drivers/staging/ft1000/ft1000-pcmcia/ft1000.h71
-rw-r--r--drivers/staging/ft1000/ft1000-pcmcia/ft1000_cs.c158
-rw-r--r--drivers/staging/ft1000/ft1000-pcmcia/ft1000_dnld.c769
-rw-r--r--drivers/staging/ft1000/ft1000-pcmcia/ft1000_hw.c2069
-rw-r--r--drivers/staging/ft1000/ft1000-usb/Makefile3
-rw-r--r--drivers/staging/ft1000/ft1000-usb/ft1000_debug.c779
-rw-r--r--drivers/staging/ft1000/ft1000-usb/ft1000_download.c1052
-rw-r--r--drivers/staging/ft1000/ft1000-usb/ft1000_hw.c1587
-rw-r--r--drivers/staging/ft1000/ft1000-usb/ft1000_ioctl.h123
-rw-r--r--drivers/staging/ft1000/ft1000-usb/ft1000_usb.c254
-rw-r--r--drivers/staging/ft1000/ft1000-usb/ft1000_usb.h150
-rw-r--r--drivers/staging/ft1000/ft1000.h366
-rw-r--r--drivers/staging/fwserial/Kconfig31
-rw-r--r--drivers/staging/fwserial/Makefile2
-rw-r--r--drivers/staging/fwserial/TODO14
-rw-r--r--drivers/staging/fwserial/dma_fifo.c303
-rw-r--r--drivers/staging/fwserial/dma_fifo.h126
-rw-r--r--drivers/staging/fwserial/fwserial.c2954
-rw-r--r--drivers/staging/fwserial/fwserial.h369
-rw-r--r--drivers/staging/gdm724x/Kconfig15
-rw-r--r--drivers/staging/gdm724x/Makefile7
-rw-r--r--drivers/staging/gdm724x/TODO16
-rw-r--r--drivers/staging/gdm724x/gdm_endian.c67
-rw-r--r--drivers/staging/gdm724x/gdm_endian.h49
-rw-r--r--drivers/staging/gdm724x/gdm_lte.c947
-rw-r--r--drivers/staging/gdm724x/gdm_lte.h81
-rw-r--r--drivers/staging/gdm724x/gdm_mux.c691
-rw-r--r--drivers/staging/gdm724x/gdm_mux.h95
-rw-r--r--drivers/staging/gdm724x/gdm_tty.c351
-rw-r--r--drivers/staging/gdm724x/gdm_tty.h71
-rw-r--r--drivers/staging/gdm724x/gdm_usb.c1041
-rw-r--r--drivers/staging/gdm724x/gdm_usb.h109
-rw-r--r--drivers/staging/gdm724x/hci.h55
-rw-r--r--drivers/staging/gdm724x/hci_packet.h93
-rw-r--r--drivers/staging/gdm724x/netlink_k.c150
-rw-r--r--drivers/staging/gdm724x/netlink_k.h25
-rw-r--r--drivers/staging/gdm72xx/Kconfig63
-rw-r--r--drivers/staging/gdm72xx/Makefile6
-rw-r--r--drivers/staging/gdm72xx/TODO2
-rw-r--r--drivers/staging/gdm72xx/gdm_qos.c438
-rw-r--r--drivers/staging/gdm72xx/gdm_qos.h74
-rw-r--r--drivers/staging/gdm72xx/gdm_sdio.c701
-rw-r--r--drivers/staging/gdm72xx/gdm_sdio.h63
-rw-r--r--drivers/staging/gdm72xx/gdm_usb.c789
-rw-r--r--drivers/staging/gdm72xx/gdm_usb.h78
-rw-r--r--drivers/staging/gdm72xx/gdm_wimax.c816
-rw-r--r--drivers/staging/gdm72xx/gdm_wimax.h49
-rw-r--r--drivers/staging/gdm72xx/hci.h213
-rw-r--r--drivers/staging/gdm72xx/netlink_k.c156
-rw-r--r--drivers/staging/gdm72xx/netlink_k.h25
-rw-r--r--drivers/staging/gdm72xx/sdio_boot.c158
-rw-r--r--drivers/staging/gdm72xx/sdio_boot.h21
-rw-r--r--drivers/staging/gdm72xx/usb_boot.c359
-rw-r--r--drivers/staging/gdm72xx/usb_boot.h22
-rw-r--r--drivers/staging/gdm72xx/usb_ids.h82
-rw-r--r--drivers/staging/gdm72xx/wm_ioctl.h96
-rw-r--r--drivers/staging/goldfish/Kconfig13
-rw-r--r--drivers/staging/goldfish/Makefile6
-rw-r--r--drivers/staging/goldfish/README11
-rw-r--r--drivers/staging/goldfish/goldfish_audio.c355
-rw-r--r--drivers/staging/goldfish/goldfish_nand.c442
-rw-r--r--drivers/staging/goldfish/goldfish_nand_reg.h76
-rw-r--r--drivers/staging/gs_fpgaboot/Kconfig8
-rw-r--r--drivers/staging/gs_fpgaboot/Makefile2
-rw-r--r--drivers/staging/gs_fpgaboot/README70
-rw-r--r--drivers/staging/gs_fpgaboot/TODO7
-rw-r--r--drivers/staging/gs_fpgaboot/gs_fpgaboot.c389
-rw-r--r--drivers/staging/gs_fpgaboot/gs_fpgaboot.h56
-rw-r--r--drivers/staging/gs_fpgaboot/io.c122
-rw-r--r--drivers/staging/gs_fpgaboot/io.h90
-rw-r--r--drivers/staging/i2o/Kconfig120
-rw-r--r--drivers/staging/i2o/Makefile16
-rw-r--r--drivers/staging/i2o/README98
-rw-r--r--drivers/staging/i2o/README.ioctl394
-rw-r--r--drivers/staging/i2o/bus-osm.c177
-rw-r--r--drivers/staging/i2o/config-osm.c90
-rw-r--r--drivers/staging/i2o/core.h69
-rw-r--r--drivers/staging/i2o/debug.c473
-rw-r--r--drivers/staging/i2o/device.c592
-rw-r--r--drivers/staging/i2o/driver.c381
-rw-r--r--drivers/staging/i2o/exec-osm.c612
-rw-r--r--drivers/staging/i2o/i2o.h988
-rw-r--r--drivers/staging/i2o/i2o_block.c1228
-rw-r--r--drivers/staging/i2o/i2o_block.h103
-rw-r--r--drivers/staging/i2o/i2o_config.c1162
-rw-r--r--drivers/staging/i2o/i2o_proc.c2049
-rw-r--r--drivers/staging/i2o/i2o_scsi.c814
-rw-r--r--drivers/staging/i2o/iop.c1255
-rw-r--r--drivers/staging/i2o/memory.c312
-rw-r--r--drivers/staging/i2o/pci.c500
-rw-r--r--drivers/staging/iio/Documentation/dac/max51741
-rw-r--r--drivers/staging/iio/Documentation/device.txt79
-rw-r--r--drivers/staging/iio/Documentation/inkernel.txt58
-rw-r--r--drivers/staging/iio/Documentation/light/sysfs-bus-iio-light-tsl25836
-rw-r--r--drivers/staging/iio/Documentation/light/sysfs-bus-iio-light-tsl2x7x13
-rw-r--r--drivers/staging/iio/Documentation/overview.txt57
-rw-r--r--drivers/staging/iio/Documentation/ring.txt47
-rw-r--r--drivers/staging/iio/Documentation/sysfs-bus-iio-ad719220
-rw-r--r--drivers/staging/iio/Documentation/sysfs-bus-iio-adc-ad7280a21
-rw-r--r--drivers/staging/iio/Documentation/sysfs-bus-iio-dds96
-rw-r--r--drivers/staging/iio/Documentation/sysfs-bus-iio-impedance-analyzer-ad593330
-rw-r--r--drivers/staging/iio/Documentation/sysfs-bus-iio-light107
-rw-r--r--drivers/staging/iio/Documentation/sysfs-bus-iio-light-tsl258320
-rw-r--r--drivers/staging/iio/Documentation/trigger.txt35
-rw-r--r--drivers/staging/iio/Kconfig48
-rw-r--r--drivers/staging/iio/Makefile23
-rw-r--r--drivers/staging/iio/TODO84
-rw-r--r--drivers/staging/iio/accel/Kconfig101
-rw-r--r--drivers/staging/iio/accel/Makefile28
-rw-r--r--drivers/staging/iio/accel/adis16201.h66
-rw-r--r--drivers/staging/iio/accel/adis16201_core.c248
-rw-r--r--drivers/staging/iio/accel/adis16203.h59
-rw-r--r--drivers/staging/iio/accel/adis16203_core.c216
-rw-r--r--drivers/staging/iio/accel/adis16204.h68
-rw-r--r--drivers/staging/iio/accel/adis16204_core.c254
-rw-r--r--drivers/staging/iio/accel/adis16209.h105
-rw-r--r--drivers/staging/iio/accel/adis16209_core.c248
-rw-r--r--drivers/staging/iio/accel/adis16220.h140
-rw-r--r--drivers/staging/iio/accel/adis16220_core.c495
-rw-r--r--drivers/staging/iio/accel/adis16240.h129
-rw-r--r--drivers/staging/iio/accel/adis16240_core.c301
-rw-r--r--drivers/staging/iio/accel/lis3l02dq.h212
-rw-r--r--drivers/staging/iio/accel/lis3l02dq_core.c813
-rw-r--r--drivers/staging/iio/accel/lis3l02dq_ring.c426
-rw-r--r--drivers/staging/iio/accel/sca3000.h278
-rw-r--r--drivers/staging/iio/accel/sca3000_core.c1209
-rw-r--r--drivers/staging/iio/accel/sca3000_ring.c348
-rw-r--r--drivers/staging/iio/adc/Kconfig118
-rw-r--r--drivers/staging/iio/adc/Makefile17
-rw-r--r--drivers/staging/iio/adc/ad7192.c720
-rw-r--r--drivers/staging/iio/adc/ad7192.h47
-rw-r--r--drivers/staging/iio/adc/ad7280a.c985
-rw-r--r--drivers/staging/iio/adc/ad7280a.h38
-rw-r--r--drivers/staging/iio/adc/ad7606.h104
-rw-r--r--drivers/staging/iio/adc/ad7606_core.c603
-rw-r--r--drivers/staging/iio/adc/ad7606_par.c152
-rw-r--r--drivers/staging/iio/adc/ad7606_ring.c101
-rw-r--r--drivers/staging/iio/adc/ad7606_spi.c116
-rw-r--r--drivers/staging/iio/adc/ad7780.c276
-rw-r--r--drivers/staging/iio/adc/ad7780.h30
-rw-r--r--drivers/staging/iio/adc/ad7816.c446
-rw-r--r--drivers/staging/iio/adc/lpc32xx_adc.c215
-rw-r--r--drivers/staging/iio/adc/mxs-lradc.c1712
-rw-r--r--drivers/staging/iio/adc/spear_adc.c400
-rw-r--r--drivers/staging/iio/addac/Kconfig37
-rw-r--r--drivers/staging/iio/addac/Makefile7
-rw-r--r--drivers/staging/iio/addac/adt7316-i2c.c136
-rw-r--r--drivers/staging/iio/addac/adt7316-spi.c144
-rw-r--r--drivers/staging/iio/addac/adt7316.c2188
-rw-r--r--drivers/staging/iio/addac/adt7316.h36
-rw-r--r--drivers/staging/iio/cdc/Kconfig36
-rw-r--r--drivers/staging/iio/cdc/Makefile7
-rw-r--r--drivers/staging/iio/cdc/ad7150.c679
-rw-r--r--drivers/staging/iio/cdc/ad7152.c543
-rw-r--r--drivers/staging/iio/cdc/ad7746.c791
-rw-r--r--drivers/staging/iio/cdc/ad7746.h29
-rw-r--r--drivers/staging/iio/frequency/Kconfig26
-rw-r--r--drivers/staging/iio/frequency/Makefile6
-rw-r--r--drivers/staging/iio/frequency/ad9832.c352
-rw-r--r--drivers/staging/iio/frequency/ad9832.h126
-rw-r--r--drivers/staging/iio/frequency/ad9834.c459
-rw-r--r--drivers/staging/iio/frequency/ad9834.h111
-rw-r--r--drivers/staging/iio/frequency/dds.h114
-rw-r--r--drivers/staging/iio/gyro/Kconfig16
-rw-r--r--drivers/staging/iio/gyro/Makefile6
-rw-r--r--drivers/staging/iio/gyro/adis16060_core.c246
-rw-r--r--drivers/staging/iio/iio_dummy_evgen.c239
-rw-r--r--drivers/staging/iio/iio_dummy_evgen.h13
-rw-r--r--drivers/staging/iio/iio_simple_dummy.c749
-rw-r--r--drivers/staging/iio/iio_simple_dummy.h129
-rw-r--r--drivers/staging/iio/iio_simple_dummy_buffer.c192
-rw-r--r--drivers/staging/iio/iio_simple_dummy_events.c267
-rw-r--r--drivers/staging/iio/impedance-analyzer/Kconfig18
-rw-r--r--drivers/staging/iio/impedance-analyzer/Makefile5
-rw-r--r--drivers/staging/iio/impedance-analyzer/ad5933.c804
-rw-r--r--drivers/staging/iio/impedance-analyzer/ad5933.h28
-rw-r--r--drivers/staging/iio/light/Kconfig43
-rw-r--r--drivers/staging/iio/light/Makefile8
-rw-r--r--drivers/staging/iio/light/isl29018.c815
-rw-r--r--drivers/staging/iio/light/isl29028.c562
-rw-r--r--drivers/staging/iio/light/tsl2583.c949
-rw-r--r--drivers/staging/iio/light/tsl2x7x.h100
-rw-r--r--drivers/staging/iio/light/tsl2x7x_core.c2030
-rw-r--r--drivers/staging/iio/magnetometer/Kconfig40
-rw-r--r--drivers/staging/iio/magnetometer/Makefile7
-rw-r--r--drivers/staging/iio/magnetometer/hmc5843.h66
-rw-r--r--drivers/staging/iio/magnetometer/hmc5843_core.c646
-rw-r--r--drivers/staging/iio/magnetometer/hmc5843_i2c.c104
-rw-r--r--drivers/staging/iio/magnetometer/hmc5843_spi.c100
-rw-r--r--drivers/staging/iio/meter/Kconfig78
-rw-r--r--drivers/staging/iio/meter/Makefile15
-rw-r--r--drivers/staging/iio/meter/ade7753.c547
-rw-r--r--drivers/staging/iio/meter/ade7753.h72
-rw-r--r--drivers/staging/iio/meter/ade7754.c588
-rw-r--r--drivers/staging/iio/meter/ade7754.h90
-rw-r--r--drivers/staging/iio/meter/ade7758.h181
-rw-r--r--drivers/staging/iio/meter/ade7758_core.c917
-rw-r--r--drivers/staging/iio/meter/ade7758_ring.c180
-rw-r--r--drivers/staging/iio/meter/ade7758_trigger.c109
-rw-r--r--drivers/staging/iio/meter/ade7759.c503
-rw-r--r--drivers/staging/iio/meter/ade7759.h53
-rw-r--r--drivers/staging/iio/meter/ade7854-i2c.c256
-rw-r--r--drivers/staging/iio/meter/ade7854-spi.c327
-rw-r--r--drivers/staging/iio/meter/ade7854.c578
-rw-r--r--drivers/staging/iio/meter/ade7854.h174
-rw-r--r--drivers/staging/iio/meter/meter.h400
-rw-r--r--drivers/staging/iio/resolver/Kconfig39
-rw-r--r--drivers/staging/iio/resolver/Makefile7
-rw-r--r--drivers/staging/iio/resolver/ad2s1200.c167
-rw-r--r--drivers/staging/iio/resolver/ad2s1210.c748
-rw-r--r--drivers/staging/iio/resolver/ad2s1210.h20
-rw-r--r--drivers/staging/iio/resolver/ad2s90.c120
-rw-r--r--drivers/staging/iio/ring_hw.h27
-rw-r--r--drivers/staging/iio/trigger/Kconfig29
-rw-r--r--drivers/staging/iio/trigger/Makefile6
-rw-r--r--drivers/staging/iio/trigger/iio-trig-bfin-timer.c292
-rw-r--r--drivers/staging/iio/trigger/iio-trig-bfin-timer.h24
-rw-r--r--drivers/staging/iio/trigger/iio-trig-periodic-rtc.c215
-rw-r--r--drivers/staging/lustre/Kconfig3
-rw-r--r--drivers/staging/lustre/Makefile2
-rw-r--r--drivers/staging/lustre/README.txt87
-rw-r--r--drivers/staging/lustre/TODO12
-rw-r--r--drivers/staging/lustre/include/linux/libcfs/curproc.h97
-rw-r--r--drivers/staging/lustre/include/linux/libcfs/libcfs.h187
-rw-r--r--drivers/staging/lustre/include/linux/libcfs/libcfs_cpu.h219
-rw-r--r--drivers/staging/lustre/include/linux/libcfs/libcfs_crypto.h199
-rw-r--r--drivers/staging/lustre/include/linux/libcfs/libcfs_debug.h262
-rw-r--r--drivers/staging/lustre/include/linux/libcfs/libcfs_fail.h171
-rw-r--r--drivers/staging/lustre/include/linux/libcfs/libcfs_hash.h843
-rw-r--r--drivers/staging/lustre/include/linux/libcfs/libcfs_ioctl.h214
-rw-r--r--drivers/staging/lustre/include/linux/libcfs/libcfs_kernelcomm.h118
-rw-r--r--drivers/staging/lustre/include/linux/libcfs/libcfs_prim.h87
-rw-r--r--drivers/staging/lustre/include/linux/libcfs/libcfs_private.h556
-rw-r--r--drivers/staging/lustre/include/linux/libcfs/libcfs_string.h107
-rw-r--r--drivers/staging/lustre/include/linux/libcfs/libcfs_time.h131
-rw-r--r--drivers/staging/lustre/include/linux/libcfs/libcfs_workitem.h110
-rw-r--r--drivers/staging/lustre/include/linux/libcfs/linux/libcfs.h147
-rw-r--r--drivers/staging/lustre/include/linux/libcfs/linux/linux-cpu.h82
-rw-r--r--drivers/staging/lustre/include/linux/libcfs/linux/linux-mem.h80
-rw-r--r--drivers/staging/lustre/include/linux/libcfs/linux/linux-time.h144
-rw-r--r--drivers/staging/lustre/include/linux/lnet/api-support.h44
-rw-r--r--drivers/staging/lustre/include/linux/lnet/api.h217
-rw-r--r--drivers/staging/lustre/include/linux/lnet/lib-lnet.h883
-rw-r--r--drivers/staging/lustre/include/linux/lnet/lib-types.h760
-rw-r--r--drivers/staging/lustre/include/linux/lnet/linux/api-support.h42
-rw-r--r--drivers/staging/lustre/include/linux/lnet/linux/lib-lnet.h71
-rw-r--r--drivers/staging/lustre/include/linux/lnet/linux/lib-types.h45
-rw-r--r--drivers/staging/lustre/include/linux/lnet/linux/lnet.h56
-rw-r--r--drivers/staging/lustre/include/linux/lnet/lnet-sysctl.h49
-rw-r--r--drivers/staging/lustre/include/linux/lnet/lnet.h51
-rw-r--r--drivers/staging/lustre/include/linux/lnet/lnetctl.h80
-rw-r--r--drivers/staging/lustre/include/linux/lnet/lnetst.h491
-rw-r--r--drivers/staging/lustre/include/linux/lnet/ptllnd.h93
-rw-r--r--drivers/staging/lustre/include/linux/lnet/ptllnd_wire.h119
-rw-r--r--drivers/staging/lustre/include/linux/lnet/socklnd.h103
-rw-r--r--drivers/staging/lustre/include/linux/lnet/types.h492
-rw-r--r--drivers/staging/lustre/lnet/Kconfig40
-rw-r--r--drivers/staging/lustre/lnet/Makefile1
-rw-r--r--drivers/staging/lustre/lnet/klnds/Makefile1
-rw-r--r--drivers/staging/lustre/lnet/klnds/o2iblnd/Makefile2
-rw-r--r--drivers/staging/lustre/lnet/klnds/o2iblnd/o2iblnd.c3118
-rw-r--r--drivers/staging/lustre/lnet/klnds/o2iblnd/o2iblnd.h1030
-rw-r--r--drivers/staging/lustre/lnet/klnds/o2iblnd/o2iblnd_cb.c3519
-rw-r--r--drivers/staging/lustre/lnet/klnds/o2iblnd/o2iblnd_modparams.c230
-rw-r--r--drivers/staging/lustre/lnet/klnds/socklnd/Makefile3
-rw-r--r--drivers/staging/lustre/lnet/klnds/socklnd/socklnd.c2886
-rw-r--r--drivers/staging/lustre/lnet/klnds/socklnd/socklnd.h588
-rw-r--r--drivers/staging/lustre/lnet/klnds/socklnd/socklnd_cb.c2634
-rw-r--r--drivers/staging/lustre/lnet/klnds/socklnd/socklnd_lib-linux.c714
-rw-r--r--drivers/staging/lustre/lnet/klnds/socklnd/socklnd_lib-linux.h86
-rw-r--r--drivers/staging/lustre/lnet/klnds/socklnd/socklnd_modparams.c188
-rw-r--r--drivers/staging/lustre/lnet/klnds/socklnd/socklnd_proto.c797
-rw-r--r--drivers/staging/lustre/lnet/lnet/Makefile5
-rw-r--r--drivers/staging/lustre/lnet/lnet/acceptor.c500
-rw-r--r--drivers/staging/lustre/lnet/lnet/api-ni.c1940
-rw-r--r--drivers/staging/lustre/lnet/lnet/config.c1292
-rw-r--r--drivers/staging/lustre/lnet/lnet/lib-eq.c441
-rw-r--r--drivers/staging/lustre/lnet/lnet/lib-md.c454
-rw-r--r--drivers/staging/lustre/lnet/lnet/lib-me.c298
-rw-r--r--drivers/staging/lustre/lnet/lnet/lib-move.c2460
-rw-r--r--drivers/staging/lustre/lnet/lnet/lib-msg.c647
-rw-r--r--drivers/staging/lustre/lnet/lnet/lib-ptl.c935
-rw-r--r--drivers/staging/lustre/lnet/lnet/lo.c120
-rw-r--r--drivers/staging/lustre/lnet/lnet/module.c155
-rw-r--r--drivers/staging/lustre/lnet/lnet/peer.c338
-rw-r--r--drivers/staging/lustre/lnet/lnet/router.c1706
-rw-r--r--drivers/staging/lustre/lnet/lnet/router_proc.c968
-rw-r--r--drivers/staging/lustre/lnet/selftest/Makefile4
-rw-r--r--drivers/staging/lustre/lnet/selftest/brw_test.c508
-rw-r--r--drivers/staging/lustre/lnet/selftest/conctl.c929
-rw-r--r--drivers/staging/lustre/lnet/selftest/conrpc.c1396
-rw-r--r--drivers/staging/lustre/lnet/selftest/conrpc.h146
-rw-r--r--drivers/staging/lustre/lnet/selftest/console.c2096
-rw-r--r--drivers/staging/lustre/lnet/selftest/console.h235
-rw-r--r--drivers/staging/lustre/lnet/selftest/framework.c1804
-rw-r--r--drivers/staging/lustre/lnet/selftest/module.c159
-rw-r--r--drivers/staging/lustre/lnet/selftest/ping_test.c230
-rw-r--r--drivers/staging/lustre/lnet/selftest/rpc.c1673
-rw-r--r--drivers/staging/lustre/lnet/selftest/rpc.h302
-rw-r--r--drivers/staging/lustre/lnet/selftest/selftest.h624
-rw-r--r--drivers/staging/lustre/lnet/selftest/timer.c248
-rw-r--r--drivers/staging/lustre/lnet/selftest/timer.h53
-rw-r--r--drivers/staging/lustre/lustre/Kconfig62
-rw-r--r--drivers/staging/lustre/lustre/Makefile2
-rw-r--r--drivers/staging/lustre/lustre/fid/Makefile3
-rw-r--r--drivers/staging/lustre/lustre/fid/fid_internal.h56
-rw-r--r--drivers/staging/lustre/lustre/fid/fid_lib.c95
-rw-r--r--drivers/staging/lustre/lustre/fid/fid_request.c572
-rw-r--r--drivers/staging/lustre/lustre/fid/lproc_fid.c225
-rw-r--r--drivers/staging/lustre/lustre/fld/Makefile3
-rw-r--r--drivers/staging/lustre/lustre/fld/fld_cache.c546
-rw-r--r--drivers/staging/lustre/lustre/fld/fld_internal.h193
-rw-r--r--drivers/staging/lustre/lustre/fld/fld_request.c526
-rw-r--r--drivers/staging/lustre/lustre/fld/lproc_fld.c172
-rw-r--r--drivers/staging/lustre/lustre/include/cl_object.h3287
-rw-r--r--drivers/staging/lustre/lustre/include/dt_object.h1499
-rw-r--r--drivers/staging/lustre/lustre/include/interval_tree.h124
-rw-r--r--drivers/staging/lustre/lustre/include/lclient.h433
-rw-r--r--drivers/staging/lustre/lustre/include/linux/lustre_compat25.h216
-rw-r--r--drivers/staging/lustre/lustre/include/linux/lustre_lite.h98
-rw-r--r--drivers/staging/lustre/lustre/include/linux/lustre_patchless_compat.h85
-rw-r--r--drivers/staging/lustre/lustre/include/linux/lustre_user.h70
-rw-r--r--drivers/staging/lustre/lustre/include/linux/obd.h125
-rw-r--r--drivers/staging/lustre/lustre/include/lprocfs_status.h1015
-rw-r--r--drivers/staging/lustre/lustre/include/lu_object.h1340
-rw-r--r--drivers/staging/lustre/lustre/include/lu_ref.h182
-rw-r--r--drivers/staging/lustre/lustre/include/lustre/libiam.h145
-rw-r--r--drivers/staging/lustre/lustre/include/lustre/ll_fiemap.h121
-rw-r--r--drivers/staging/lustre/lustre/include/lustre/lustre_build_version.h2
-rw-r--r--drivers/staging/lustre/lustre/include/lustre/lustre_errno.h215
-rw-r--r--drivers/staging/lustre/lustre/include/lustre/lustre_idl.h3734
-rw-r--r--drivers/staging/lustre/lustre/include/lustre/lustre_lfsck_user.h95
-rw-r--r--drivers/staging/lustre/lustre/include/lustre/lustre_user.h1179
-rw-r--r--drivers/staging/lustre/lustre/include/lustre_acl.h49
-rw-r--r--drivers/staging/lustre/lustre/include/lustre_capa.h305
-rw-r--r--drivers/staging/lustre/lustre/include/lustre_cfg.h293
-rw-r--r--drivers/staging/lustre/lustre/include/lustre_debug.h56
-rw-r--r--drivers/staging/lustre/lustre/include/lustre_disk.h547
-rw-r--r--drivers/staging/lustre/lustre/include/lustre_dlm.h1480
-rw-r--r--drivers/staging/lustre/lustre/include/lustre_dlm_flags.h476
-rw-r--r--drivers/staging/lustre/lustre/include/lustre_eacl.h95
-rw-r--r--drivers/staging/lustre/lustre/include/lustre_export.h406
-rw-r--r--drivers/staging/lustre/lustre/include/lustre_fid.h767
-rw-r--r--drivers/staging/lustre/lustre/include/lustre_fld.h160
-rw-r--r--drivers/staging/lustre/lustre/include/lustre_ha.h64
-rw-r--r--drivers/staging/lustre/lustre/include/lustre_handles.h97
-rw-r--r--drivers/staging/lustre/lustre/include/lustre_import.h385
-rw-r--r--drivers/staging/lustre/lustre/include/lustre_intent.h62
-rw-r--r--drivers/staging/lustre/lustre/include/lustre_lib.h666
-rw-r--r--drivers/staging/lustre/lustre/include/lustre_lite.h150
-rw-r--r--drivers/staging/lustre/lustre/include/lustre_log.h545
-rw-r--r--drivers/staging/lustre/lustre/include/lustre_mdc.h191
-rw-r--r--drivers/staging/lustre/lustre/include/lustre_mds.h81
-rw-r--r--drivers/staging/lustre/lustre/include/lustre_net.h2967
-rw-r--r--drivers/staging/lustre/lustre/include/lustre_param.h121
-rw-r--r--drivers/staging/lustre/lustre/include/lustre_quota.h241
-rw-r--r--drivers/staging/lustre/lustre/include/lustre_req_layout.h341
-rw-r--r--drivers/staging/lustre/lustre/include/lustre_sec.h1147
-rw-r--r--drivers/staging/lustre/lustre/include/lustre_ver.h26
-rw-r--r--drivers/staging/lustre/lustre/include/obd.h1496
-rw-r--r--drivers/staging/lustre/lustre/include/obd_cache.h39
-rw-r--r--drivers/staging/lustre/lustre/include/obd_cksum.h176
-rw-r--r--drivers/staging/lustre/lustre/include/obd_class.h1929
-rw-r--r--drivers/staging/lustre/lustre/include/obd_support.h862
-rw-r--r--drivers/staging/lustre/lustre/lclient/glimpse.c269
-rw-r--r--drivers/staging/lustre/lustre/lclient/lcommon_cl.c1287
-rw-r--r--drivers/staging/lustre/lustre/lclient/lcommon_misc.c199
-rw-r--r--drivers/staging/lustre/lustre/ldlm/interval_tree.c751
-rw-r--r--drivers/staging/lustre/lustre/ldlm/l_lock.c76
-rw-r--r--drivers/staging/lustre/lustre/ldlm/ldlm_extent.c241
-rw-r--r--drivers/staging/lustre/lustre/ldlm/ldlm_flock.c859
-rw-r--r--drivers/staging/lustre/lustre/ldlm/ldlm_inodebits.c74
-rw-r--r--drivers/staging/lustre/lustre/ldlm/ldlm_internal.h316
-rw-r--r--drivers/staging/lustre/lustre/ldlm/ldlm_lib.c870
-rw-r--r--drivers/staging/lustre/lustre/ldlm/ldlm_lock.c2322
-rw-r--r--drivers/staging/lustre/lustre/ldlm/ldlm_lockd.c1191
-rw-r--r--drivers/staging/lustre/lustre/ldlm/ldlm_plain.c72
-rw-r--r--drivers/staging/lustre/lustre/ldlm/ldlm_pool.c1455
-rw-r--r--drivers/staging/lustre/lustre/ldlm/ldlm_request.c2294
-rw-r--r--drivers/staging/lustre/lustre/ldlm/ldlm_resource.c1425
-rw-r--r--drivers/staging/lustre/lustre/libcfs/Makefile18
-rw-r--r--drivers/staging/lustre/lustre/libcfs/debug.c460
-rw-r--r--drivers/staging/lustre/lustre/libcfs/fail.c138
-rw-r--r--drivers/staging/lustre/lustre/libcfs/hash.c2098
-rw-r--r--drivers/staging/lustre/lustre/libcfs/kernel_user_comm.c240
-rw-r--r--drivers/staging/lustre/lustre/libcfs/libcfs_cpu.c224
-rw-r--r--drivers/staging/lustre/lustre/libcfs/libcfs_lock.c189
-rw-r--r--drivers/staging/lustre/lustre/libcfs/libcfs_mem.c202
-rw-r--r--drivers/staging/lustre/lustre/libcfs/libcfs_string.c562
-rw-r--r--drivers/staging/lustre/lustre/libcfs/linux/linux-cpu.c1056
-rw-r--r--drivers/staging/lustre/lustre/libcfs/linux/linux-crypto-adler.c141
-rw-r--r--drivers/staging/lustre/lustre/libcfs/linux/linux-crypto.c291
-rw-r--r--drivers/staging/lustre/lustre/libcfs/linux/linux-crypto.h29
-rw-r--r--drivers/staging/lustre/lustre/libcfs/linux/linux-curproc.c111
-rw-r--r--drivers/staging/lustre/lustre/libcfs/linux/linux-debug.c200
-rw-r--r--drivers/staging/lustre/lustre/libcfs/linux/linux-module.c183
-rw-r--r--drivers/staging/lustre/lustre/libcfs/linux/linux-prim.c217
-rw-r--r--drivers/staging/lustre/lustre/libcfs/linux/linux-tcpip.c623
-rw-r--r--drivers/staging/lustre/lustre/libcfs/linux/linux-tracefile.c275
-rw-r--r--drivers/staging/lustre/lustre/libcfs/linux/linux-tracefile.h48
-rw-r--r--drivers/staging/lustre/lustre/libcfs/module.c976
-rw-r--r--drivers/staging/lustre/lustre/libcfs/nidstrings.c842
-rw-r--r--drivers/staging/lustre/lustre/libcfs/prng.c139
-rw-r--r--drivers/staging/lustre/lustre/libcfs/tracefile.c1196
-rw-r--r--drivers/staging/lustre/lustre/libcfs/tracefile.h340
-rw-r--r--drivers/staging/lustre/lustre/libcfs/workitem.c479
-rw-r--r--drivers/staging/lustre/lustre/llite/Makefile11
-rw-r--r--drivers/staging/lustre/lustre/llite/dcache.c363
-rw-r--r--drivers/staging/lustre/lustre/llite/dir.c1971
-rw-r--r--drivers/staging/lustre/lustre/llite/file.c3624
-rw-r--r--drivers/staging/lustre/lustre/llite/llite_capa.c654
-rw-r--r--drivers/staging/lustre/lustre/llite/llite_close.c393
-rw-r--r--drivers/staging/lustre/lustre/llite/llite_internal.h1521
-rw-r--r--drivers/staging/lustre/lustre/llite/llite_lib.c2354
-rw-r--r--drivers/staging/lustre/lustre/llite/llite_mmap.c492
-rw-r--r--drivers/staging/lustre/lustre/llite/llite_nfs.c335
-rw-r--r--drivers/staging/lustre/lustre/llite/llite_rmtacl.c300
-rw-r--r--drivers/staging/lustre/lustre/llite/lloop.c877
-rw-r--r--drivers/staging/lustre/lustre/llite/lproc_llite.c1536
-rw-r--r--drivers/staging/lustre/lustre/llite/namei.c1178
-rw-r--r--drivers/staging/lustre/lustre/llite/remote_perm.c331
-rw-r--r--drivers/staging/lustre/lustre/llite/rw.c1289
-rw-r--r--drivers/staging/lustre/lustre/llite/rw26.c553
-rw-r--r--drivers/staging/lustre/lustre/llite/statahead.c1729
-rw-r--r--drivers/staging/lustre/lustre/llite/super25.c226
-rw-r--r--drivers/staging/lustre/lustre/llite/symlink.c170
-rw-r--r--drivers/staging/lustre/lustre/llite/vvp_dev.c547
-rw-r--r--drivers/staging/lustre/lustre/llite/vvp_internal.h62
-rw-r--r--drivers/staging/lustre/lustre/llite/vvp_io.c1209
-rw-r--r--drivers/staging/lustre/lustre/llite/vvp_lock.c85
-rw-r--r--drivers/staging/lustre/lustre/llite/vvp_object.c201
-rw-r--r--drivers/staging/lustre/lustre/llite/vvp_page.c551
-rw-r--r--drivers/staging/lustre/lustre/llite/xattr.c621
-rw-r--r--drivers/staging/lustre/lustre/llite/xattr_cache.c538
-rw-r--r--drivers/staging/lustre/lustre/lmv/Makefile3
-rw-r--r--drivers/staging/lustre/lustre/lmv/lmv_fld.c83
-rw-r--r--drivers/staging/lustre/lustre/lmv/lmv_intent.c323
-rw-r--r--drivers/staging/lustre/lustre/lmv/lmv_internal.h157
-rw-r--r--drivers/staging/lustre/lustre/lmv/lmv_obd.c2892
-rw-r--r--drivers/staging/lustre/lustre/lmv/lproc_lmv.c237
-rw-r--r--drivers/staging/lustre/lustre/lov/Makefile6
-rw-r--r--drivers/staging/lustre/lustre/lov/lov_cl_internal.h839
-rw-r--r--drivers/staging/lustre/lustre/lov/lov_dev.c528
-rw-r--r--drivers/staging/lustre/lustre/lov/lov_ea.c363
-rw-r--r--drivers/staging/lustre/lustre/lov/lov_internal.h319
-rw-r--r--drivers/staging/lustre/lustre/lov/lov_io.c990
-rw-r--r--drivers/staging/lustre/lustre/lov/lov_lock.c1198
-rw-r--r--drivers/staging/lustre/lustre/lov/lov_merge.c186
-rw-r--r--drivers/staging/lustre/lustre/lov/lov_obd.c2395
-rw-r--r--drivers/staging/lustre/lustre/lov/lov_object.c1001
-rw-r--r--drivers/staging/lustre/lustre/lov/lov_offset.c264
-rw-r--r--drivers/staging/lustre/lustre/lov/lov_pack.c511
-rw-r--r--drivers/staging/lustre/lustre/lov/lov_page.c232
-rw-r--r--drivers/staging/lustre/lustre/lov/lov_pool.c673
-rw-r--r--drivers/staging/lustre/lustre/lov/lov_request.c773
-rw-r--r--drivers/staging/lustre/lustre/lov/lovsub_dev.c209
-rw-r--r--drivers/staging/lustre/lustre/lov/lovsub_io.c55
-rw-r--r--drivers/staging/lustre/lustre/lov/lovsub_lock.c466
-rw-r--r--drivers/staging/lustre/lustre/lov/lovsub_object.c164
-rw-r--r--drivers/staging/lustre/lustre/lov/lovsub_page.c71
-rw-r--r--drivers/staging/lustre/lustre/lov/lproc_lov.c311
-rw-r--r--drivers/staging/lustre/lustre/mdc/Makefile3
-rw-r--r--drivers/staging/lustre/lustre/mdc/lproc_mdc.c220
-rw-r--r--drivers/staging/lustre/lustre/mdc/mdc_internal.h181
-rw-r--r--drivers/staging/lustre/lustre/mdc/mdc_lib.c593
-rw-r--r--drivers/staging/lustre/lustre/mdc/mdc_locks.c1313
-rw-r--r--drivers/staging/lustre/lustre/mdc/mdc_reint.c483
-rw-r--r--drivers/staging/lustre/lustre/mdc/mdc_request.c2731
-rw-r--r--drivers/staging/lustre/lustre/mgc/Makefile3
-rw-r--r--drivers/staging/lustre/lustre/mgc/lproc_mgc.c80
-rw-r--r--drivers/staging/lustre/lustre/mgc/mgc_internal.h73
-rw-r--r--drivers/staging/lustre/lustre/mgc/mgc_request.c1762
-rw-r--r--drivers/staging/lustre/lustre/obdclass/Makefile11
-rw-r--r--drivers/staging/lustre/lustre/obdclass/acl.c548
-rw-r--r--drivers/staging/lustre/lustre/obdclass/capa.c421
-rw-r--r--drivers/staging/lustre/lustre/obdclass/cl_internal.h121
-rw-r--r--drivers/staging/lustre/lustre/obdclass/cl_io.c1669
-rw-r--r--drivers/staging/lustre/lustre/obdclass/cl_lock.c2239
-rw-r--r--drivers/staging/lustre/lustre/obdclass/cl_object.c1139
-rw-r--r--drivers/staging/lustre/lustre/obdclass/cl_page.c1553
-rw-r--r--drivers/staging/lustre/lustre/obdclass/class_obd.c704
-rw-r--r--drivers/staging/lustre/lustre/obdclass/debug.c109
-rw-r--r--drivers/staging/lustre/lustre/obdclass/dt_object.c1059
-rw-r--r--drivers/staging/lustre/lustre/obdclass/genops.c1833
-rw-r--r--drivers/staging/lustre/lustre/obdclass/linux/linux-module.c449
-rw-r--r--drivers/staging/lustre/lustre/obdclass/linux/linux-obdo.c222
-rw-r--r--drivers/staging/lustre/lustre/obdclass/linux/linux-sysctl.c405
-rw-r--r--drivers/staging/lustre/lustre/obdclass/llog.c1007
-rw-r--r--drivers/staging/lustre/lustre/obdclass/llog_cat.c815
-rw-r--r--drivers/staging/lustre/lustre/obdclass/llog_internal.h98
-rw-r--r--drivers/staging/lustre/lustre/obdclass/llog_obd.c262
-rw-r--r--drivers/staging/lustre/lustre/obdclass/llog_swab.c415
-rw-r--r--drivers/staging/lustre/lustre/obdclass/lprocfs_counters.c139
-rw-r--r--drivers/staging/lustre/lustre/obdclass/lprocfs_status.c2059
-rw-r--r--drivers/staging/lustre/lustre/obdclass/lu_object.c2192
-rw-r--r--drivers/staging/lustre/lustre/obdclass/lu_ref.c50
-rw-r--r--drivers/staging/lustre/lustre/obdclass/lustre_handles.c257
-rw-r--r--drivers/staging/lustre/lustre/obdclass/lustre_peer.c217
-rw-r--r--drivers/staging/lustre/lustre/obdclass/obd_config.c1953
-rw-r--r--drivers/staging/lustre/lustre/obdclass/obd_mount.c1319
-rw-r--r--drivers/staging/lustre/lustre/obdclass/obdo.c362
-rw-r--r--drivers/staging/lustre/lustre/obdclass/statfs_pack.c75
-rw-r--r--drivers/staging/lustre/lustre/obdclass/uuid.c82
-rw-r--r--drivers/staging/lustre/lustre/obdecho/Makefile2
-rw-r--r--drivers/staging/lustre/lustre/obdecho/echo_client.c2197
-rw-r--r--drivers/staging/lustre/lustre/obdecho/echo_internal.h47
-rw-r--r--drivers/staging/lustre/lustre/obdecho/lproc_echo.c57
-rw-r--r--drivers/staging/lustre/lustre/osc/Makefile4
-rw-r--r--drivers/staging/lustre/lustre/osc/lproc_osc.c751
-rw-r--r--drivers/staging/lustre/lustre/osc/osc_cache.c2944
-rw-r--r--drivers/staging/lustre/lustre/osc/osc_cl_internal.h685
-rw-r--r--drivers/staging/lustre/lustre/osc/osc_dev.c262
-rw-r--r--drivers/staging/lustre/lustre/osc/osc_internal.h203
-rw-r--r--drivers/staging/lustre/lustre/osc/osc_io.c819
-rw-r--r--drivers/staging/lustre/lustre/osc/osc_lock.c1613
-rw-r--r--drivers/staging/lustre/lustre/osc/osc_object.c271
-rw-r--r--drivers/staging/lustre/lustre/osc/osc_page.c916
-rw-r--r--drivers/staging/lustre/lustre/osc/osc_quota.c327
-rw-r--r--drivers/staging/lustre/lustre/osc/osc_request.c3379
-rw-r--r--drivers/staging/lustre/lustre/ptlrpc/Makefile20
-rw-r--r--drivers/staging/lustre/lustre/ptlrpc/client.c3149
-rw-r--r--drivers/staging/lustre/lustre/ptlrpc/connection.c241
-rw-r--r--drivers/staging/lustre/lustre/ptlrpc/errno.c380
-rw-r--r--drivers/staging/lustre/lustre/ptlrpc/events.c585
-rw-r--r--drivers/staging/lustre/lustre/ptlrpc/import.c1642
-rw-r--r--drivers/staging/lustre/lustre/ptlrpc/layout.c2442
-rw-r--r--drivers/staging/lustre/lustre/ptlrpc/llog_client.c366
-rw-r--r--drivers/staging/lustre/lustre/ptlrpc/llog_net.c72
-rw-r--r--drivers/staging/lustre/lustre/ptlrpc/lproc_ptlrpc.c1366
-rw-r--r--drivers/staging/lustre/lustre/ptlrpc/niobuf.c731
-rw-r--r--drivers/staging/lustre/lustre/ptlrpc/nrs.c1754
-rw-r--r--drivers/staging/lustre/lustre/ptlrpc/nrs_fifo.c270
-rw-r--r--drivers/staging/lustre/lustre/ptlrpc/pack_generic.c2536
-rw-r--r--drivers/staging/lustre/lustre/ptlrpc/pers.c75
-rw-r--r--drivers/staging/lustre/lustre/ptlrpc/pinger.c678
-rw-r--r--drivers/staging/lustre/lustre/ptlrpc/ptlrpc_internal.h312
-rw-r--r--drivers/staging/lustre/lustre/ptlrpc/ptlrpc_module.c171
-rw-r--r--drivers/staging/lustre/lustre/ptlrpc/ptlrpcd.c811
-rw-r--r--drivers/staging/lustre/lustre/ptlrpc/recover.c379
-rw-r--r--drivers/staging/lustre/lustre/ptlrpc/sec.c2459
-rw-r--r--drivers/staging/lustre/lustre/ptlrpc/sec_bulk.c884
-rw-r--r--drivers/staging/lustre/lustre/ptlrpc/sec_config.c901
-rw-r--r--drivers/staging/lustre/lustre/ptlrpc/sec_gc.c252
-rw-r--r--drivers/staging/lustre/lustre/ptlrpc/sec_lproc.c199
-rw-r--r--drivers/staging/lustre/lustre/ptlrpc/sec_null.c458
-rw-r--r--drivers/staging/lustre/lustre/ptlrpc/sec_plain.c1013
-rw-r--r--drivers/staging/lustre/lustre/ptlrpc/service.c3105
-rw-r--r--drivers/staging/lustre/lustre/ptlrpc/wiretest.c4492
-rw-r--r--drivers/staging/media/Kconfig39
-rw-r--r--drivers/staging/media/Makefile8
-rw-r--r--drivers/staging/media/bcm2048/Kconfig13
-rw-r--r--drivers/staging/media/bcm2048/Makefile1
-rw-r--r--drivers/staging/media/bcm2048/TODO24
-rw-r--r--drivers/staging/media/bcm2048/radio-bcm2048.c2708
-rw-r--r--drivers/staging/media/bcm2048/radio-bcm2048.h30
-rw-r--r--drivers/staging/media/cxd2099/Kconfig12
-rw-r--r--drivers/staging/media/cxd2099/Makefile5
-rw-r--r--drivers/staging/media/cxd2099/TODO12
-rw-r--r--drivers/staging/media/cxd2099/cxd2099.c721
-rw-r--r--drivers/staging/media/cxd2099/cxd2099.h51
-rw-r--r--drivers/staging/media/davinci_vpfe/Kconfig10
-rw-r--r--drivers/staging/media/davinci_vpfe/Makefile3
-rw-r--r--drivers/staging/media/davinci_vpfe/TODO37
-rw-r--r--drivers/staging/media/davinci_vpfe/davinci-vpfe-mc.txt154
-rw-r--r--drivers/staging/media/davinci_vpfe/davinci_vpfe_user.h1290
-rw-r--r--drivers/staging/media/davinci_vpfe/dm365_ipipe.c1862
-rw-r--r--drivers/staging/media/davinci_vpfe/dm365_ipipe.h179
-rw-r--r--drivers/staging/media/davinci_vpfe/dm365_ipipe_hw.c1048
-rw-r--r--drivers/staging/media/davinci_vpfe/dm365_ipipe_hw.h558
-rw-r--r--drivers/staging/media/davinci_vpfe/dm365_ipipeif.c1067
-rw-r--r--drivers/staging/media/davinci_vpfe/dm365_ipipeif.h233
-rw-r--r--drivers/staging/media/davinci_vpfe/dm365_ipipeif_user.h93
-rw-r--r--drivers/staging/media/davinci_vpfe/dm365_isif.c2106
-rw-r--r--drivers/staging/media/davinci_vpfe/dm365_isif.h203
-rw-r--r--drivers/staging/media/davinci_vpfe/dm365_isif_regs.h294
-rw-r--r--drivers/staging/media/davinci_vpfe/dm365_resizer.c1995
-rw-r--r--drivers/staging/media/davinci_vpfe/dm365_resizer.h244
-rw-r--r--drivers/staging/media/davinci_vpfe/vpfe.h86
-rw-r--r--drivers/staging/media/davinci_vpfe/vpfe_mc_capture.c716
-rw-r--r--drivers/staging/media/davinci_vpfe/vpfe_mc_capture.h95
-rw-r--r--drivers/staging/media/davinci_vpfe/vpfe_video.c1636
-rw-r--r--drivers/staging/media/davinci_vpfe/vpfe_video.h153
-rw-r--r--drivers/staging/media/dt3155v4l/Kconfig29
-rw-r--r--drivers/staging/media/dt3155v4l/Makefile1
-rw-r--r--drivers/staging/media/dt3155v4l/dt3155v4l.c981
-rw-r--r--drivers/staging/media/dt3155v4l/dt3155v4l.h212
-rw-r--r--drivers/staging/media/lirc/Kconfig66
-rw-r--r--drivers/staging/media/lirc/Makefile12
-rw-r--r--drivers/staging/media/lirc/TODO13
-rw-r--r--drivers/staging/media/lirc/TODO.lirc_zilog36
-rw-r--r--drivers/staging/media/lirc/lirc_bt829.c404
-rw-r--r--drivers/staging/media/lirc/lirc_imon.c1002
-rw-r--r--drivers/staging/media/lirc/lirc_parallel.c744
-rw-r--r--drivers/staging/media/lirc/lirc_parallel.h26
-rw-r--r--drivers/staging/media/lirc/lirc_sasem.c921
-rw-r--r--drivers/staging/media/lirc/lirc_serial.c1213
-rw-r--r--drivers/staging/media/lirc/lirc_sir.c1014
-rw-r--r--drivers/staging/media/lirc/lirc_zilog.c1697
-rw-r--r--drivers/staging/media/mn88472/Kconfig7
-rw-r--r--drivers/staging/media/mn88472/Makefile5
-rw-r--r--drivers/staging/media/mn88472/TODO21
-rw-r--r--drivers/staging/media/mn88472/mn88472.c577
-rw-r--r--drivers/staging/media/mn88472/mn88472_priv.h39
-rw-r--r--drivers/staging/media/mn88473/Kconfig7
-rw-r--r--drivers/staging/media/mn88473/Makefile5
-rw-r--r--drivers/staging/media/mn88473/TODO21
-rw-r--r--drivers/staging/media/mn88473/mn88473.c523
-rw-r--r--drivers/staging/media/mn88473/mn88473_priv.h37
-rw-r--r--drivers/staging/media/omap4iss/Kconfig8
-rw-r--r--drivers/staging/media/omap4iss/Makefile6
-rw-r--r--drivers/staging/media/omap4iss/TODO4
-rw-r--r--drivers/staging/media/omap4iss/iss.c1512
-rw-r--r--drivers/staging/media/omap4iss/iss.h254
-rw-r--r--drivers/staging/media/omap4iss/iss_csi2.c1351
-rw-r--r--drivers/staging/media/omap4iss/iss_csi2.h158
-rw-r--r--drivers/staging/media/omap4iss/iss_csiphy.c281
-rw-r--r--drivers/staging/media/omap4iss/iss_csiphy.h51
-rw-r--r--drivers/staging/media/omap4iss/iss_ipipe.c570
-rw-r--r--drivers/staging/media/omap4iss/iss_ipipe.h67
-rw-r--r--drivers/staging/media/omap4iss/iss_ipipeif.c830
-rw-r--r--drivers/staging/media/omap4iss/iss_ipipeif.h92
-rw-r--r--drivers/staging/media/omap4iss/iss_regs.h903
-rw-r--r--drivers/staging/media/omap4iss/iss_resizer.c874
-rw-r--r--drivers/staging/media/omap4iss/iss_resizer.h75
-rw-r--r--drivers/staging/media/omap4iss/iss_video.c1232
-rw-r--r--drivers/staging/media/omap4iss/iss_video.h204
-rw-r--r--drivers/staging/mt29f_spinand/Kconfig16
-rw-r--r--drivers/staging/mt29f_spinand/Makefile1
-rw-r--r--drivers/staging/mt29f_spinand/TODO13
-rw-r--r--drivers/staging/mt29f_spinand/mt29f_spinand.c963
-rw-r--r--drivers/staging/mt29f_spinand/mt29f_spinand.h107
-rw-r--r--drivers/staging/netlogic/Kconfig7
-rw-r--r--drivers/staging/netlogic/Makefile1
-rw-r--r--drivers/staging/netlogic/TODO11
-rw-r--r--drivers/staging/netlogic/platform_net.c245
-rw-r--r--drivers/staging/netlogic/platform_net.h49
-rw-r--r--drivers/staging/netlogic/xlr_net.c1144
-rw-r--r--drivers/staging/netlogic/xlr_net.h1105
-rw-r--r--drivers/staging/nvec/Kconfig53
-rw-r--r--drivers/staging/nvec/Makefile5
-rw-r--r--drivers/staging/nvec/README14
-rw-r--r--drivers/staging/nvec/TODO8
-rw-r--r--drivers/staging/nvec/nvec-keytable.h307
-rw-r--r--drivers/staging/nvec/nvec.c983
-rw-r--r--drivers/staging/nvec/nvec.h183
-rw-r--r--drivers/staging/nvec/nvec_kbd.c192
-rw-r--r--drivers/staging/nvec/nvec_paz00.c98
-rw-r--r--drivers/staging/nvec/nvec_power.c449
-rw-r--r--drivers/staging/nvec/nvec_ps2.c189
-rw-r--r--drivers/staging/octeon-usb/Kconfig10
-rw-r--r--drivers/staging/octeon-usb/Makefile1
-rw-r--r--drivers/staging/octeon-usb/TODO11
-rw-r--r--drivers/staging/octeon-usb/octeon-hcd.c3770
-rw-r--r--drivers/staging/octeon-usb/octeon-hcd.h1846
-rw-r--r--drivers/staging/octeon/Kconfig13
-rw-r--r--drivers/staging/octeon/Makefile23
-rw-r--r--drivers/staging/octeon/ethernet-defines.h102
-rw-r--r--drivers/staging/octeon/ethernet-mdio.c206
-rw-r--r--drivers/staging/octeon/ethernet-mdio.h47
-rw-r--r--drivers/staging/octeon/ethernet-mem.c176
-rw-r--r--drivers/staging/octeon/ethernet-mem.h29
-rw-r--r--drivers/staging/octeon/ethernet-rgmii.c448
-rw-r--r--drivers/staging/octeon/ethernet-rx.c489
-rw-r--r--drivers/staging/octeon/ethernet-rx.h52
-rw-r--r--drivers/staging/octeon/ethernet-sgmii.c141
-rw-r--r--drivers/staging/octeon/ethernet-spi.c292
-rw-r--r--drivers/staging/octeon/ethernet-tx.c763
-rw-r--r--drivers/staging/octeon/ethernet-tx.h34
-rw-r--r--drivers/staging/octeon/ethernet-util.h70
-rw-r--r--drivers/staging/octeon/ethernet-xaui.c144
-rw-r--r--drivers/staging/octeon/ethernet.c891
-rw-r--r--drivers/staging/octeon/octeon-ethernet.h104
-rw-r--r--drivers/staging/olpc_dcon/Kconfig35
-rw-r--r--drivers/staging/olpc_dcon/Makefile6
-rw-r--r--drivers/staging/olpc_dcon/TODO9
-rw-r--r--drivers/staging/olpc_dcon/olpc_dcon.c817
-rw-r--r--drivers/staging/olpc_dcon/olpc_dcon.h111
-rw-r--r--drivers/staging/olpc_dcon/olpc_dcon_xo_1.c205
-rw-r--r--drivers/staging/olpc_dcon/olpc_dcon_xo_1_5.c161
-rw-r--r--drivers/staging/ozwpan/Kconfig9
-rw-r--r--drivers/staging/ozwpan/Makefile16
-rw-r--r--drivers/staging/ozwpan/README25
-rw-r--r--drivers/staging/ozwpan/TODO14
-rw-r--r--drivers/staging/ozwpan/ozappif.h36
-rw-r--r--drivers/staging/ozwpan/ozcdev.c554
-rw-r--r--drivers/staging/ozwpan/ozcdev.h17
-rw-r--r--drivers/staging/ozwpan/ozdbg.h54
-rw-r--r--drivers/staging/ozwpan/ozeltbuf.c252
-rw-r--r--drivers/staging/ozwpan/ozeltbuf.h65
-rw-r--r--drivers/staging/ozwpan/ozhcd.c2301
-rw-r--r--drivers/staging/ozwpan/ozhcd.h15
-rw-r--r--drivers/staging/ozwpan/ozmain.c71
-rw-r--r--drivers/staging/ozwpan/ozpd.c886
-rw-r--r--drivers/staging/ozwpan/ozpd.h134
-rw-r--r--drivers/staging/ozwpan/ozproto.c813
-rw-r--r--drivers/staging/ozwpan/ozproto.h62
-rw-r--r--drivers/staging/ozwpan/ozprotocol.h375
-rw-r--r--drivers/staging/ozwpan/ozurbparanoia.c54
-rw-r--r--drivers/staging/ozwpan/ozurbparanoia.h19
-rw-r--r--drivers/staging/ozwpan/ozusbif.h43
-rw-r--r--drivers/staging/ozwpan/ozusbsvc.c263
-rw-r--r--drivers/staging/ozwpan/ozusbsvc.h32
-rw-r--r--drivers/staging/ozwpan/ozusbsvc1.c462
-rw-r--r--drivers/staging/panel/Kconfig278
-rw-r--r--drivers/staging/panel/Makefile1
-rw-r--r--drivers/staging/panel/TODO8
-rw-r--r--drivers/staging/panel/lcd-panel-cgram.txt24
-rw-r--r--drivers/staging/panel/panel.c2436
-rw-r--r--drivers/staging/rtl8188eu/Kconfig20
-rw-r--r--drivers/staging/rtl8188eu/Makefile55
-rw-r--r--drivers/staging/rtl8188eu/TODO19
-rw-r--r--drivers/staging/rtl8188eu/core/rtw_ap.c1963
-rw-r--r--drivers/staging/rtl8188eu/core/rtw_cmd.c1395
-rw-r--r--drivers/staging/rtl8188eu/core/rtw_debug.c947
-rw-r--r--drivers/staging/rtl8188eu/core/rtw_efuse.c1014
-rw-r--r--drivers/staging/rtl8188eu/core/rtw_ieee80211.c1395
-rw-r--r--drivers/staging/rtl8188eu/core/rtw_ioctl_set.c663
-rw-r--r--drivers/staging/rtl8188eu/core/rtw_iol.c31
-rw-r--r--drivers/staging/rtl8188eu/core/rtw_led.c497
-rw-r--r--drivers/staging/rtl8188eu/core/rtw_mlme.c2174
-rw-r--r--drivers/staging/rtl8188eu/core/rtw_mlme_ext.c5587
-rw-r--r--drivers/staging/rtl8188eu/core/rtw_pwrctrl.c672
-rw-r--r--drivers/staging/rtl8188eu/core/rtw_recv.c2188
-rw-r--r--drivers/staging/rtl8188eu/core/rtw_rf.c89
-rw-r--r--drivers/staging/rtl8188eu/core/rtw_security.c1681
-rw-r--r--drivers/staging/rtl8188eu/core/rtw_sreset.c64
-rw-r--r--drivers/staging/rtl8188eu/core/rtw_sta_mgt.c565
-rw-r--r--drivers/staging/rtl8188eu/core/rtw_wlan_util.c1611
-rw-r--r--drivers/staging/rtl8188eu/core/rtw_xmit.c2209
-rw-r--r--drivers/staging/rtl8188eu/hal/Hal8188ERateAdaptive.c780
-rw-r--r--drivers/staging/rtl8188eu/hal/bb_cfg.c730
-rw-r--r--drivers/staging/rtl8188eu/hal/fw.c235
-rw-r--r--drivers/staging/rtl8188eu/hal/hal_com.c321
-rw-r--r--drivers/staging/rtl8188eu/hal/hal_intf.c335
-rw-r--r--drivers/staging/rtl8188eu/hal/mac_cfg.c134
-rw-r--r--drivers/staging/rtl8188eu/hal/odm.c1368
-rw-r--r--drivers/staging/rtl8188eu/hal/odm_HWConfig.c433
-rw-r--r--drivers/staging/rtl8188eu/hal/odm_RTL8188E.c372
-rw-r--r--drivers/staging/rtl8188eu/hal/phy.c1470
-rw-r--r--drivers/staging/rtl8188eu/hal/pwrseq.c102
-rw-r--r--drivers/staging/rtl8188eu/hal/pwrseqcmd.c122
-rw-r--r--drivers/staging/rtl8188eu/hal/rf.c318
-rw-r--r--drivers/staging/rtl8188eu/hal/rf_cfg.c320
-rw-r--r--drivers/staging/rtl8188eu/hal/rtl8188e_cmd.c674
-rw-r--r--drivers/staging/rtl8188eu/hal/rtl8188e_dm.c247
-rw-r--r--drivers/staging/rtl8188eu/hal/rtl8188e_hal_init.c694
-rw-r--r--drivers/staging/rtl8188eu/hal/rtl8188e_rxdesc.c205
-rw-r--r--drivers/staging/rtl8188eu/hal/rtl8188e_xmit.c92
-rw-r--r--drivers/staging/rtl8188eu/hal/rtl8188eu_led.c93
-rw-r--r--drivers/staging/rtl8188eu/hal/rtl8188eu_recv.c120
-rw-r--r--drivers/staging/rtl8188eu/hal/rtl8188eu_xmit.c698
-rw-r--r--drivers/staging/rtl8188eu/hal/usb_halinit.c2213
-rw-r--r--drivers/staging/rtl8188eu/include/Hal8188EPhyCfg.h238
-rw-r--r--drivers/staging/rtl8188eu/include/Hal8188EPhyReg.h1094
-rw-r--r--drivers/staging/rtl8188eu/include/Hal8188ERateAdaptive.h75
-rw-r--r--drivers/staging/rtl8188eu/include/HalHWImg8188E_FW.h34
-rw-r--r--drivers/staging/rtl8188eu/include/HalVerDef.h167
-rw-r--r--drivers/staging/rtl8188eu/include/basic_types.h184
-rw-r--r--drivers/staging/rtl8188eu/include/drv_types.h254
-rw-r--r--drivers/staging/rtl8188eu/include/fw.h59
-rw-r--r--drivers/staging/rtl8188eu/include/hal_com.h169
-rw-r--r--drivers/staging/rtl8188eu/include/hal_intf.h325
-rw-r--r--drivers/staging/rtl8188eu/include/ieee80211.h1260
-rw-r--r--drivers/staging/rtl8188eu/include/ieee80211_ext.h290
-rw-r--r--drivers/staging/rtl8188eu/include/mlme_osdep.h35
-rw-r--r--drivers/staging/rtl8188eu/include/mp_custom_oid.h352
-rw-r--r--drivers/staging/rtl8188eu/include/odm.h1109
-rw-r--r--drivers/staging/rtl8188eu/include/odm_HWConfig.h126
-rw-r--r--drivers/staging/rtl8188eu/include/odm_RTL8188E.h52
-rw-r--r--drivers/staging/rtl8188eu/include/odm_RegDefine11N.h171
-rw-r--r--drivers/staging/rtl8188eu/include/odm_debug.h111
-rw-r--r--drivers/staging/rtl8188eu/include/odm_precomp.h82
-rw-r--r--drivers/staging/rtl8188eu/include/odm_reg.h119
-rw-r--r--drivers/staging/rtl8188eu/include/odm_types.h37
-rw-r--r--drivers/staging/rtl8188eu/include/osdep_intf.h51
-rw-r--r--drivers/staging/rtl8188eu/include/osdep_service.h171
-rw-r--r--drivers/staging/rtl8188eu/include/phy.h30
-rw-r--r--drivers/staging/rtl8188eu/include/pwrseq.h341
-rw-r--r--drivers/staging/rtl8188eu/include/pwrseqcmd.h90
-rw-r--r--drivers/staging/rtl8188eu/include/recv_osdep.h51
-rw-r--r--drivers/staging/rtl8188eu/include/rf.h11
-rw-r--r--drivers/staging/rtl8188eu/include/rtl8188e_cmd.h116
-rw-r--r--drivers/staging/rtl8188eu/include/rtl8188e_dm.h61
-rw-r--r--drivers/staging/rtl8188eu/include/rtl8188e_hal.h428
-rw-r--r--drivers/staging/rtl8188eu/include/rtl8188e_led.h35
-rw-r--r--drivers/staging/rtl8188eu/include/rtl8188e_recv.h69
-rw-r--r--drivers/staging/rtl8188eu/include/rtl8188e_spec.h1439
-rw-r--r--drivers/staging/rtl8188eu/include/rtl8188e_xmit.h177
-rw-r--r--drivers/staging/rtl8188eu/include/rtw_android.h64
-rw-r--r--drivers/staging/rtl8188eu/include/rtw_ap.h65
-rw-r--r--drivers/staging/rtl8188eu/include/rtw_cmd.h423
-rw-r--r--drivers/staging/rtl8188eu/include/rtw_debug.h266
-rw-r--r--drivers/staging/rtl8188eu/include/rtw_eeprom.h130
-rw-r--r--drivers/staging/rtl8188eu/include/rtw_efuse.h118
-rw-r--r--drivers/staging/rtl8188eu/include/rtw_event.h115
-rw-r--r--drivers/staging/rtl8188eu/include/rtw_ht.h44
-rw-r--r--drivers/staging/rtl8188eu/include/rtw_ioctl.h122
-rw-r--r--drivers/staging/rtl8188eu/include/rtw_ioctl_rtl.h79
-rw-r--r--drivers/staging/rtl8188eu/include/rtw_ioctl_set.h42
-rw-r--r--drivers/staging/rtl8188eu/include/rtw_iol.h28
-rw-r--r--drivers/staging/rtl8188eu/include/rtw_led.h120
-rw-r--r--drivers/staging/rtl8188eu/include/rtw_mlme.h591
-rw-r--r--drivers/staging/rtl8188eu/include/rtw_mlme_ext.h806
-rw-r--r--drivers/staging/rtl8188eu/include/rtw_mp_phy_regdef.h1084
-rw-r--r--drivers/staging/rtl8188eu/include/rtw_pwrctrl.h270
-rw-r--r--drivers/staging/rtl8188eu/include/rtw_qos.h30
-rw-r--r--drivers/staging/rtl8188eu/include/rtw_recv.h365
-rw-r--r--drivers/staging/rtl8188eu/include/rtw_rf.h146
-rw-r--r--drivers/staging/rtl8188eu/include/rtw_security.h356
-rw-r--r--drivers/staging/rtl8188eu/include/rtw_sreset.h45
-rw-r--r--drivers/staging/rtl8188eu/include/rtw_xmit.h379
-rw-r--r--drivers/staging/rtl8188eu/include/sta_info.h369
-rw-r--r--drivers/staging/rtl8188eu/include/usb_hal.h26
-rw-r--r--drivers/staging/rtl8188eu/include/usb_ops_linux.h86
-rw-r--r--drivers/staging/rtl8188eu/include/wifi.h1099
-rw-r--r--drivers/staging/rtl8188eu/include/wlan_bssdef.h333
-rw-r--r--drivers/staging/rtl8188eu/include/xmit_osdep.h61
-rw-r--r--drivers/staging/rtl8188eu/os_dep/ioctl_linux.c3125
-rw-r--r--drivers/staging/rtl8188eu/os_dep/mlme_linux.c200
-rw-r--r--drivers/staging/rtl8188eu/os_dep/os_intfs.c1163
-rw-r--r--drivers/staging/rtl8188eu/os_dep/osdep_service.c165
-rw-r--r--drivers/staging/rtl8188eu/os_dep/recv_linux.c200
-rw-r--r--drivers/staging/rtl8188eu/os_dep/rtw_android.c270
-rw-r--r--drivers/staging/rtl8188eu/os_dep/usb_intf.c569
-rw-r--r--drivers/staging/rtl8188eu/os_dep/usb_ops_linux.c855
-rw-r--r--drivers/staging/rtl8188eu/os_dep/xmit_linux.c260
-rw-r--r--drivers/staging/rtl8192e/Kconfig47
-rw-r--r--drivers/staging/rtl8192e/Makefile21
-rw-r--r--drivers/staging/rtl8192e/TODO2
-rw-r--r--drivers/staging/rtl8192e/dot11d.c216
-rw-r--r--drivers/staging/rtl8192e/dot11d.h103
-rw-r--r--drivers/staging/rtl8192e/license339
-rw-r--r--drivers/staging/rtl8192e/rtl8192e/Kconfig9
-rw-r--r--drivers/staging/rtl8192e/rtl8192e/Makefile20
-rw-r--r--drivers/staging/rtl8192e/rtl8192e/r8190P_def.h410
-rw-r--r--drivers/staging/rtl8192e/rtl8192e/r8190P_rtl8256.c309
-rw-r--r--drivers/staging/rtl8192e/rtl8192e/r8190P_rtl8256.h31
-rw-r--r--drivers/staging/rtl8192e/rtl8192e/r8192E_cmdpkt.c380
-rw-r--r--drivers/staging/rtl8192e/rtl8192e/r8192E_cmdpkt.h159
-rw-r--r--drivers/staging/rtl8192e/rtl8192e/r8192E_dev.c2416
-rw-r--r--drivers/staging/rtl8192e/rtl8192e/r8192E_dev.h62
-rw-r--r--drivers/staging/rtl8192e/rtl8192e/r8192E_firmware.c319
-rw-r--r--drivers/staging/rtl8192e/rtl8192e/r8192E_firmware.h72
-rw-r--r--drivers/staging/rtl8192e/rtl8192e/r8192E_hw.h453
-rw-r--r--drivers/staging/rtl8192e/rtl8192e/r8192E_hwimg.c565
-rw-r--r--drivers/staging/rtl8192e/rtl8192e/r8192E_hwimg.h51
-rw-r--r--drivers/staging/rtl8192e/rtl8192e/r8192E_phy.c1636
-rw-r--r--drivers/staging/rtl8192e/rtl8192e/r8192E_phy.h120
-rw-r--r--drivers/staging/rtl8192e/rtl8192e/r8192E_phyreg.h852
-rw-r--r--drivers/staging/rtl8192e/rtl8192e/r819xE_phyreg.h908
-rw-r--r--drivers/staging/rtl8192e/rtl8192e/rtl_cam.c277
-rw-r--r--drivers/staging/rtl8192e/rtl8192e/rtl_cam.h44
-rw-r--r--drivers/staging/rtl8192e/rtl8192e/rtl_core.c3120
-rw-r--r--drivers/staging/rtl8192e/rtl8192e/rtl_core.h1076
-rw-r--r--drivers/staging/rtl8192e/rtl8192e/rtl_crypto.h382
-rw-r--r--drivers/staging/rtl8192e/rtl8192e/rtl_dm.c2995
-rw-r--r--drivers/staging/rtl8192e/rtl8192e/rtl_dm.h316
-rw-r--r--drivers/staging/rtl8192e/rtl8192e/rtl_eeprom.c139
-rw-r--r--drivers/staging/rtl8192e/rtl8192e/rtl_eeprom.h29
-rw-r--r--drivers/staging/rtl8192e/rtl8192e/rtl_ethtool.c53
-rw-r--r--drivers/staging/rtl8192e/rtl8192e/rtl_pci.c100
-rw-r--r--drivers/staging/rtl8192e/rtl8192e/rtl_pci.h51
-rw-r--r--drivers/staging/rtl8192e/rtl8192e/rtl_pm.c119
-rw-r--r--drivers/staging/rtl8192e/rtl8192e/rtl_pm.h29
-rw-r--r--drivers/staging/rtl8192e/rtl8192e/rtl_ps.c316
-rw-r--r--drivers/staging/rtl8192e/rtl8192e/rtl_ps.h47
-rw-r--r--drivers/staging/rtl8192e/rtl8192e/rtl_wx.c1341
-rw-r--r--drivers/staging/rtl8192e/rtl8192e/rtl_wx.h30
-rw-r--r--drivers/staging/rtl8192e/rtl819x_BA.h77
-rw-r--r--drivers/staging/rtl8192e/rtl819x_BAProc.c571
-rw-r--r--drivers/staging/rtl8192e/rtl819x_HT.h428
-rw-r--r--drivers/staging/rtl8192e/rtl819x_HTProc.c919
-rw-r--r--drivers/staging/rtl8192e/rtl819x_Qos.h389
-rw-r--r--drivers/staging/rtl8192e/rtl819x_TS.h73
-rw-r--r--drivers/staging/rtl8192e/rtl819x_TSProc.c554
-rw-r--r--drivers/staging/rtl8192e/rtllib.h2975
-rw-r--r--drivers/staging/rtl8192e/rtllib_crypt.c254
-rw-r--r--drivers/staging/rtl8192e/rtllib_crypt.h34
-rw-r--r--drivers/staging/rtl8192e/rtllib_crypt_ccmp.c457
-rw-r--r--drivers/staging/rtl8192e/rtllib_crypt_tkip.c779
-rw-r--r--drivers/staging/rtl8192e/rtllib_crypt_wep.c289
-rw-r--r--drivers/staging/rtl8192e/rtllib_debug.h88
-rw-r--r--drivers/staging/rtl8192e/rtllib_module.c268
-rw-r--r--drivers/staging/rtl8192e/rtllib_rx.c2678
-rw-r--r--drivers/staging/rtl8192e/rtllib_softmac.c3758
-rw-r--r--drivers/staging/rtl8192e/rtllib_softmac_wx.c661
-rw-r--r--drivers/staging/rtl8192e/rtllib_tx.c988
-rw-r--r--drivers/staging/rtl8192e/rtllib_wx.c881
-rw-r--r--drivers/staging/rtl8192u/Kconfig8
-rw-r--r--drivers/staging/rtl8192u/Makefile29
-rw-r--r--drivers/staging/rtl8192u/authors1
-rw-r--r--drivers/staging/rtl8192u/changes4
-rw-r--r--drivers/staging/rtl8192u/copying340
-rw-r--r--drivers/staging/rtl8192u/ieee80211/Makefile27
-rw-r--r--drivers/staging/rtl8192u/ieee80211/dot11d.c175
-rw-r--r--drivers/staging/rtl8192u/ieee80211/dot11d.h100
-rw-r--r--drivers/staging/rtl8192u/ieee80211/ieee80211.h2574
-rw-r--r--drivers/staging/rtl8192u/ieee80211/ieee80211_crypt.c240
-rw-r--r--drivers/staging/rtl8192u/ieee80211/ieee80211_crypt.h86
-rw-r--r--drivers/staging/rtl8192u/ieee80211/ieee80211_crypt_ccmp.c474
-rw-r--r--drivers/staging/rtl8192u/ieee80211/ieee80211_crypt_tkip.c777
-rw-r--r--drivers/staging/rtl8192u/ieee80211/ieee80211_crypt_wep.c298
-rw-r--r--drivers/staging/rtl8192u/ieee80211/ieee80211_module.c309
-rw-r--r--drivers/staging/rtl8192u/ieee80211/ieee80211_rx.c2638
-rw-r--r--drivers/staging/rtl8192u/ieee80211/ieee80211_softmac.c3217
-rw-r--r--drivers/staging/rtl8192u/ieee80211/ieee80211_softmac_wx.c605
-rw-r--r--drivers/staging/rtl8192u/ieee80211/ieee80211_tx.c914
-rw-r--r--drivers/staging/rtl8192u/ieee80211/ieee80211_wx.c852
-rw-r--r--drivers/staging/rtl8192u/ieee80211/rtl819x_BA.h67
-rw-r--r--drivers/staging/rtl8192u/ieee80211/rtl819x_BAProc.c745
-rw-r--r--drivers/staging/rtl8192u/ieee80211/rtl819x_HT.h480
-rw-r--r--drivers/staging/rtl8192u/ieee80211/rtl819x_HTProc.c1410
-rw-r--r--drivers/staging/rtl8192u/ieee80211/rtl819x_Qos.h565
-rw-r--r--drivers/staging/rtl8192u/ieee80211/rtl819x_TS.h55
-rw-r--r--drivers/staging/rtl8192u/ieee80211/rtl819x_TSProc.c603
-rw-r--r--drivers/staging/rtl8192u/r8180_93cx6.c157
-rw-r--r--drivers/staging/rtl8192u/r8180_93cx6.h43
-rw-r--r--drivers/staging/rtl8192u/r8190_rtl8256.c290
-rw-r--r--drivers/staging/rtl8192u/r8190_rtl8256.h24
-rw-r--r--drivers/staging/rtl8192u/r8192U.h1196
-rw-r--r--drivers/staging/rtl8192u/r8192U_core.c4915
-rw-r--r--drivers/staging/rtl8192u/r8192U_dm.c3125
-rw-r--r--drivers/staging/rtl8192u/r8192U_dm.h239
-rw-r--r--drivers/staging/rtl8192u/r8192U_hw.h412
-rw-r--r--drivers/staging/rtl8192u/r8192U_wx.c995
-rw-r--r--drivers/staging/rtl8192u/r8192U_wx.h24
-rw-r--r--drivers/staging/rtl8192u/r819xU_cmdpkt.c571
-rw-r--r--drivers/staging/rtl8192u/r819xU_cmdpkt.h191
-rw-r--r--drivers/staging/rtl8192u/r819xU_firmware.c342
-rw-r--r--drivers/staging/rtl8192u/r819xU_firmware.h26
-rw-r--r--drivers/staging/rtl8192u/r819xU_firmware_img.c548
-rw-r--r--drivers/staging/rtl8192u/r819xU_firmware_img.h28
-rw-r--r--drivers/staging/rtl8192u/r819xU_phy.c1795
-rw-r--r--drivers/staging/rtl8192u/r819xU_phy.h92
-rw-r--r--drivers/staging/rtl8192u/r819xU_phyreg.h878
-rw-r--r--drivers/staging/rtl8712/Kconfig17
-rw-r--r--drivers/staging/rtl8712/Makefile34
-rw-r--r--drivers/staging/rtl8712/TODO13
-rw-r--r--drivers/staging/rtl8712/basic_types.h48
-rw-r--r--drivers/staging/rtl8712/drv_types.h193
-rw-r--r--drivers/staging/rtl8712/ethernet.h33
-rw-r--r--drivers/staging/rtl8712/hal_init.c396
-rw-r--r--drivers/staging/rtl8712/ieee80211.c418
-rw-r--r--drivers/staging/rtl8712/ieee80211.h796
-rw-r--r--drivers/staging/rtl8712/mlme_linux.c171
-rw-r--r--drivers/staging/rtl8712/mlme_osdep.h43
-rw-r--r--drivers/staging/rtl8712/mp_custom_oid.h299
-rw-r--r--drivers/staging/rtl8712/os_intfs.c478
-rw-r--r--drivers/staging/rtl8712/osdep_intf.h44
-rw-r--r--drivers/staging/rtl8712/osdep_service.h96
-rw-r--r--drivers/staging/rtl8712/recv_linux.c153
-rw-r--r--drivers/staging/rtl8712/recv_osdep.h51
-rw-r--r--drivers/staging/rtl8712/rtl8712_bitdef.h40
-rw-r--r--drivers/staging/rtl8712/rtl8712_cmd.c469
-rw-r--r--drivers/staging/rtl8712/rtl8712_cmd.h244
-rw-r--r--drivers/staging/rtl8712/rtl8712_cmdctrl_bitdef.h108
-rw-r--r--drivers/staging/rtl8712/rtl8712_cmdctrl_regdef.h34
-rw-r--r--drivers/staging/rtl8712/rtl8712_debugctrl_bitdef.h55
-rw-r--r--drivers/staging/rtl8712/rtl8712_debugctrl_regdef.h47
-rw-r--r--drivers/staging/rtl8712/rtl8712_edcasetting_bitdef.h77
-rw-r--r--drivers/staging/rtl8712/rtl8712_edcasetting_regdef.h37
-rw-r--r--drivers/staging/rtl8712/rtl8712_efuse.c574
-rw-r--r--drivers/staging/rtl8712/rtl8712_efuse.h43
-rw-r--r--drivers/staging/rtl8712/rtl8712_event.h99
-rw-r--r--drivers/staging/rtl8712/rtl8712_fifoctrl_bitdef.h145
-rw-r--r--drivers/staging/rtl8712/rtl8712_fifoctrl_regdef.h76
-rw-r--r--drivers/staging/rtl8712/rtl8712_gp_bitdef.h79
-rw-r--r--drivers/staging/rtl8712/rtl8712_gp_regdef.h42
-rw-r--r--drivers/staging/rtl8712/rtl8712_hal.h150
-rw-r--r--drivers/staging/rtl8712/rtl8712_interrupt_bitdef.h58
-rw-r--r--drivers/staging/rtl8712/rtl8712_io.c146
-rw-r--r--drivers/staging/rtl8712/rtl8712_led.c1834
-rw-r--r--drivers/staging/rtl8712/rtl8712_macsetting_bitdef.h47
-rw-r--r--drivers/staging/rtl8712/rtl8712_macsetting_regdef.h35
-rw-r--r--drivers/staging/rtl8712/rtl8712_powersave_bitdef.h52
-rw-r--r--drivers/staging/rtl8712/rtl8712_powersave_regdef.h39
-rw-r--r--drivers/staging/rtl8712/rtl8712_ratectrl_bitdef.h49
-rw-r--r--drivers/staging/rtl8712/rtl8712_ratectrl_regdef.h56
-rw-r--r--drivers/staging/rtl8712/rtl8712_recv.c1118
-rw-r--r--drivers/staging/rtl8712/rtl8712_recv.h157
-rw-r--r--drivers/staging/rtl8712/rtl8712_regdef.h44
-rw-r--r--drivers/staging/rtl8712/rtl8712_security_bitdef.h48
-rw-r--r--drivers/staging/rtl8712/rtl8712_spec.h135
-rw-r--r--drivers/staging/rtl8712/rtl8712_syscfg_bitdef.h170
-rw-r--r--drivers/staging/rtl8712/rtl8712_syscfg_regdef.h56
-rw-r--r--drivers/staging/rtl8712/rtl8712_timectrl_bitdef.h63
-rw-r--r--drivers/staging/rtl8712/rtl8712_timectrl_regdef.h39
-rw-r--r--drivers/staging/rtl8712/rtl8712_wmac_bitdef.h62
-rw-r--r--drivers/staging/rtl8712/rtl8712_wmac_regdef.h49
-rw-r--r--drivers/staging/rtl8712/rtl8712_xmit.c764
-rw-r--r--drivers/staging/rtl8712/rtl8712_xmit.h123
-rw-r--r--drivers/staging/rtl8712/rtl871x_cmd.c1042
-rw-r--r--drivers/staging/rtl8712/rtl871x_cmd.h790
-rw-r--r--drivers/staging/rtl8712/rtl871x_debug.h167
-rw-r--r--drivers/staging/rtl8712/rtl871x_eeprom.c233
-rw-r--r--drivers/staging/rtl8712/rtl871x_eeprom.h101
-rw-r--r--drivers/staging/rtl8712/rtl871x_event.h120
-rw-r--r--drivers/staging/rtl8712/rtl871x_ht.h44
-rw-r--r--drivers/staging/rtl8712/rtl871x_io.c161
-rw-r--r--drivers/staging/rtl8712/rtl871x_io.h258
-rw-r--r--drivers/staging/rtl8712/rtl871x_ioctl.h97
-rw-r--r--drivers/staging/rtl8712/rtl871x_ioctl_linux.c2361
-rw-r--r--drivers/staging/rtl8712/rtl871x_ioctl_rtl.c536
-rw-r--r--drivers/staging/rtl8712/rtl871x_ioctl_rtl.h121
-rw-r--r--drivers/staging/rtl8712/rtl871x_ioctl_set.c370
-rw-r--r--drivers/staging/rtl8712/rtl871x_ioctl_set.h57
-rw-r--r--drivers/staging/rtl8712/rtl871x_led.h124
-rw-r--r--drivers/staging/rtl8712/rtl871x_mlme.c1808
-rw-r--r--drivers/staging/rtl8712/rtl871x_mlme.h232
-rw-r--r--drivers/staging/rtl8712/rtl871x_mp.c745
-rw-r--r--drivers/staging/rtl8712/rtl871x_mp.h286
-rw-r--r--drivers/staging/rtl8712/rtl871x_mp_ioctl.c929
-rw-r--r--drivers/staging/rtl8712/rtl871x_mp_ioctl.h434
-rw-r--r--drivers/staging/rtl8712/rtl871x_mp_phy_regdef.h1025
-rw-r--r--drivers/staging/rtl8712/rtl871x_pwrctrl.c245
-rw-r--r--drivers/staging/rtl8712/rtl871x_pwrctrl.h131
-rw-r--r--drivers/staging/rtl8712/rtl871x_recv.c678
-rw-r--r--drivers/staging/rtl8712/rtl871x_recv.h216
-rw-r--r--drivers/staging/rtl8712/rtl871x_rf.h68
-rw-r--r--drivers/staging/rtl8712/rtl871x_security.c1400
-rw-r--r--drivers/staging/rtl8712/rtl871x_security.h222
-rw-r--r--drivers/staging/rtl8712/rtl871x_sta_mgt.c285
-rw-r--r--drivers/staging/rtl8712/rtl871x_wlan_sme.h47
-rw-r--r--drivers/staging/rtl8712/rtl871x_xmit.c1056
-rw-r--r--drivers/staging/rtl8712/rtl871x_xmit.h302
-rw-r--r--drivers/staging/rtl8712/sta_info.h146
-rw-r--r--drivers/staging/rtl8712/usb_halinit.c317
-rw-r--r--drivers/staging/rtl8712/usb_intf.c648
-rw-r--r--drivers/staging/rtl8712/usb_ops.c200
-rw-r--r--drivers/staging/rtl8712/usb_ops.h50
-rw-r--r--drivers/staging/rtl8712/usb_ops_linux.c523
-rw-r--r--drivers/staging/rtl8712/usb_osintf.h47
-rw-r--r--drivers/staging/rtl8712/wifi.h594
-rw-r--r--drivers/staging/rtl8712/wlan_bssdef.h267
-rw-r--r--drivers/staging/rtl8712/xmit_linux.c198
-rw-r--r--drivers/staging/rtl8712/xmit_osdep.h64
-rw-r--r--drivers/staging/rtl8723au/Kconfig30
-rw-r--r--drivers/staging/rtl8723au/Makefile53
-rw-r--r--drivers/staging/rtl8723au/TODO13
-rw-r--r--drivers/staging/rtl8723au/core/rtw_ap.c1910
-rw-r--r--drivers/staging/rtl8723au/core/rtw_cmd.c1469
-rw-r--r--drivers/staging/rtl8723au/core/rtw_efuse.c715
-rw-r--r--drivers/staging/rtl8723au/core/rtw_ieee80211.c854
-rw-r--r--drivers/staging/rtl8723au/core/rtw_mlme.c2339
-rw-r--r--drivers/staging/rtl8723au/core/rtw_mlme_ext.c6224
-rw-r--r--drivers/staging/rtl8723au/core/rtw_pwrctrl.c606
-rw-r--r--drivers/staging/rtl8723au/core/rtw_recv.c2335
-rw-r--r--drivers/staging/rtl8723au/core/rtw_security.c1635
-rw-r--r--drivers/staging/rtl8723au/core/rtw_sreset.c214
-rw-r--r--drivers/staging/rtl8723au/core/rtw_sta_mgt.c451
-rw-r--r--drivers/staging/rtl8723au/core/rtw_wlan_util.c1553
-rw-r--r--drivers/staging/rtl8723au/core/rtw_xmit.c2377
-rw-r--r--drivers/staging/rtl8723au/hal/Hal8723PwrSeq.c80
-rw-r--r--drivers/staging/rtl8723au/hal/Hal8723UHWImg_CE.c136
-rw-r--r--drivers/staging/rtl8723au/hal/HalDMOutSrc8723A_CE.c1097
-rw-r--r--drivers/staging/rtl8723au/hal/HalHWImg8723A_BB.c565
-rw-r--r--drivers/staging/rtl8723au/hal/HalHWImg8723A_MAC.c187
-rw-r--r--drivers/staging/rtl8723au/hal/HalHWImg8723A_RF.c259
-rw-r--r--drivers/staging/rtl8723au/hal/HalPwrSeqCmd.c156
-rw-r--r--drivers/staging/rtl8723au/hal/hal_com.c853
-rw-r--r--drivers/staging/rtl8723au/hal/hal_intf.c42
-rw-r--r--drivers/staging/rtl8723au/hal/odm.c1738
-rw-r--r--drivers/staging/rtl8723au/hal/odm_HWConfig.c400
-rw-r--r--drivers/staging/rtl8723au/hal/odm_RegConfig8723A.c88
-rw-r--r--drivers/staging/rtl8723au/hal/odm_debug.c39
-rw-r--r--drivers/staging/rtl8723au/hal/odm_interface.c49
-rw-r--r--drivers/staging/rtl8723au/hal/rtl8723a_bt-coexist.c11297
-rw-r--r--drivers/staging/rtl8723au/hal/rtl8723a_cmd.c756
-rw-r--r--drivers/staging/rtl8723au/hal/rtl8723a_dm.c194
-rw-r--r--drivers/staging/rtl8723au/hal/rtl8723a_hal_init.c2102
-rw-r--r--drivers/staging/rtl8723au/hal/rtl8723a_phycfg.c980
-rw-r--r--drivers/staging/rtl8723au/hal/rtl8723a_rf6052.c517
-rw-r--r--drivers/staging/rtl8723au/hal/rtl8723a_rxdesc.c69
-rw-r--r--drivers/staging/rtl8723au/hal/rtl8723a_sreset.c55
-rw-r--r--drivers/staging/rtl8723au/hal/rtl8723au_recv.c267
-rw-r--r--drivers/staging/rtl8723au/hal/rtl8723au_xmit.c520
-rw-r--r--drivers/staging/rtl8723au/hal/usb_halinit.c1273
-rw-r--r--drivers/staging/rtl8723au/hal/usb_ops_linux.c694
-rw-r--r--drivers/staging/rtl8723au/include/Hal8723APhyCfg.h162
-rw-r--r--drivers/staging/rtl8723au/include/Hal8723APhyReg.h1078
-rw-r--r--drivers/staging/rtl8723au/include/Hal8723PwrSeq.h126
-rw-r--r--drivers/staging/rtl8723au/include/Hal8723UHWImg_CE.h29
-rw-r--r--drivers/staging/rtl8723au/include/HalDMOutSrc8723A.h64
-rw-r--r--drivers/staging/rtl8723au/include/HalHWImg8723A_BB.h38
-rw-r--r--drivers/staging/rtl8723au/include/HalHWImg8723A_FW.h28
-rw-r--r--drivers/staging/rtl8723au/include/HalHWImg8723A_MAC.h26
-rw-r--r--drivers/staging/rtl8723au/include/HalHWImg8723A_RF.h25
-rw-r--r--drivers/staging/rtl8723au/include/HalPwrSeqCmd.h130
-rw-r--r--drivers/staging/rtl8723au/include/HalVerDef.h114
-rw-r--r--drivers/staging/rtl8723au/include/drv_types.h274
-rw-r--r--drivers/staging/rtl8723au/include/hal_com.h182
-rw-r--r--drivers/staging/rtl8723au/include/hal_intf.h115
-rw-r--r--drivers/staging/rtl8723au/include/ieee80211.h341
-rw-r--r--drivers/staging/rtl8723au/include/ioctl_cfg80211.h66
-rw-r--r--drivers/staging/rtl8723au/include/mlme_osdep.h24
-rw-r--r--drivers/staging/rtl8723au/include/odm.h860
-rw-r--r--drivers/staging/rtl8723au/include/odm_HWConfig.h155
-rw-r--r--drivers/staging/rtl8723au/include/odm_RegConfig8723A.h27
-rw-r--r--drivers/staging/rtl8723au/include/odm_RegDefine11N.h165
-rw-r--r--drivers/staging/rtl8723au/include/odm_debug.h117
-rw-r--r--drivers/staging/rtl8723au/include/odm_interface.h62
-rw-r--r--drivers/staging/rtl8723au/include/odm_precomp.h49
-rw-r--r--drivers/staging/rtl8723au/include/odm_reg.h111
-rw-r--r--drivers/staging/rtl8723au/include/osdep_intf.h45
-rw-r--r--drivers/staging/rtl8723au/include/osdep_service.h88
-rw-r--r--drivers/staging/rtl8723au/include/recv_osdep.h36
-rw-r--r--drivers/staging/rtl8723au/include/rtl8723a_bt-coexist.h1627
-rw-r--r--drivers/staging/rtl8723au/include/rtl8723a_bt_intf.h69
-rw-r--r--drivers/staging/rtl8723au/include/rtl8723a_cmd.h158
-rw-r--r--drivers/staging/rtl8723au/include/rtl8723a_dm.h137
-rw-r--r--drivers/staging/rtl8723au/include/rtl8723a_hal.h535
-rw-r--r--drivers/staging/rtl8723au/include/rtl8723a_pg.h98
-rw-r--r--drivers/staging/rtl8723au/include/rtl8723a_recv.h65
-rw-r--r--drivers/staging/rtl8723au/include/rtl8723a_rf.h58
-rw-r--r--drivers/staging/rtl8723au/include/rtl8723a_spec.h2148
-rw-r--r--drivers/staging/rtl8723au/include/rtl8723a_sreset.h24
-rw-r--r--drivers/staging/rtl8723au/include/rtl8723a_xmit.h225
-rw-r--r--drivers/staging/rtl8723au/include/rtw_ap.h54
-rw-r--r--drivers/staging/rtl8723au/include/rtw_cmd.h817
-rw-r--r--drivers/staging/rtl8723au/include/rtw_debug.h191
-rw-r--r--drivers/staging/rtl8723au/include/rtw_eeprom.h135
-rw-r--r--drivers/staging/rtl8723au/include/rtw_efuse.h109
-rw-r--r--drivers/staging/rtl8723au/include/rtw_event.h74
-rw-r--r--drivers/staging/rtl8723au/include/rtw_ht.h42
-rw-r--r--drivers/staging/rtl8723au/include/rtw_io.h237
-rw-r--r--drivers/staging/rtl8723au/include/rtw_mlme.h340
-rw-r--r--drivers/staging/rtl8723au/include/rtw_mlme_ext.h685
-rw-r--r--drivers/staging/rtl8723au/include/rtw_pwrctrl.h241
-rw-r--r--drivers/staging/rtl8723au/include/rtw_recv.h307
-rw-r--r--drivers/staging/rtl8723au/include/rtw_rf.h102
-rw-r--r--drivers/staging/rtl8723au/include/rtw_security.h331
-rw-r--r--drivers/staging/rtl8723au/include/rtw_sreset.h36
-rw-r--r--drivers/staging/rtl8723au/include/rtw_version.h1
-rw-r--r--drivers/staging/rtl8723au/include/rtw_xmit.h385
-rw-r--r--drivers/staging/rtl8723au/include/sta_info.h373
-rw-r--r--drivers/staging/rtl8723au/include/usb_ops.h68
-rw-r--r--drivers/staging/rtl8723au/include/usb_ops_linux.h41
-rw-r--r--drivers/staging/rtl8723au/include/wifi.h84
-rw-r--r--drivers/staging/rtl8723au/include/wlan_bssdef.h123
-rw-r--r--drivers/staging/rtl8723au/include/xmit_osdep.h38
-rw-r--r--drivers/staging/rtl8723au/os_dep/ioctl_cfg80211.c3356
-rw-r--r--drivers/staging/rtl8723au/os_dep/mlme_linux.c81
-rw-r--r--drivers/staging/rtl8723au/os_dep/os_intfs.c852
-rw-r--r--drivers/staging/rtl8723au/os_dep/recv_linux.c165
-rw-r--r--drivers/staging/rtl8723au/os_dep/usb_intf.c622
-rw-r--r--drivers/staging/rtl8723au/os_dep/usb_ops_linux.c234
-rw-r--r--drivers/staging/rtl8723au/os_dep/xmit_linux.c154
-rw-r--r--drivers/staging/rts5208/Kconfig8
-rw-r--r--drivers/staging/rts5208/Makefile6
-rw-r--r--drivers/staging/rts5208/TODO7
-rw-r--r--drivers/staging/rts5208/general.c36
-rw-r--r--drivers/staging/rts5208/general.h31
-rw-r--r--drivers/staging/rts5208/ms.c4810
-rw-r--r--drivers/staging/rts5208/ms.h227
-rw-r--r--drivers/staging/rts5208/rtsx.c1045
-rw-r--r--drivers/staging/rts5208/rtsx.h191
-rw-r--r--drivers/staging/rts5208/rtsx_card.c1235
-rw-r--r--drivers/staging/rts5208/rtsx_card.h1103
-rw-r--r--drivers/staging/rts5208/rtsx_chip.c2437
-rw-r--r--drivers/staging/rts5208/rtsx_chip.h991
-rw-r--r--drivers/staging/rts5208/rtsx_scsi.c3544
-rw-r--r--drivers/staging/rts5208/rtsx_scsi.h143
-rw-r--r--drivers/staging/rts5208/rtsx_sys.h50
-rw-r--r--drivers/staging/rts5208/rtsx_transport.c775
-rw-r--r--drivers/staging/rts5208/rtsx_transport.h66
-rw-r--r--drivers/staging/rts5208/sd.c5316
-rw-r--r--drivers/staging/rts5208/sd.h301
-rw-r--r--drivers/staging/rts5208/spi.c1036
-rw-r--r--drivers/staging/rts5208/spi.h65
-rw-r--r--drivers/staging/rts5208/trace.c26
-rw-r--r--drivers/staging/rts5208/trace.h40
-rw-r--r--drivers/staging/rts5208/xd.c2345
-rw-r--r--drivers/staging/rts5208/xd.h188
-rw-r--r--drivers/staging/skein/Kconfig16
-rw-r--r--drivers/staging/skein/Makefile10
-rw-r--r--drivers/staging/skein/TODO8
-rw-r--r--drivers/staging/skein/skein_api.c239
-rw-r--r--drivers/staging/skein/skein_api.h230
-rw-r--r--drivers/staging/skein/skein_base.c864
-rw-r--r--drivers/staging/skein/skein_base.h335
-rw-r--r--drivers/staging/skein/skein_block.c782
-rw-r--r--drivers/staging/skein/skein_block.h22
-rw-r--r--drivers/staging/skein/skein_generic.c215
-rw-r--r--drivers/staging/skein/skein_iv.h186
-rw-r--r--drivers/staging/skein/threefish_api.c77
-rw-r--r--drivers/staging/skein/threefish_api.h170
-rw-r--r--drivers/staging/skein/threefish_block.c8258
-rw-r--r--drivers/staging/slicoss/Kconfig14
-rw-r--r--drivers/staging/slicoss/Makefile1
-rw-r--r--drivers/staging/slicoss/README7
-rw-r--r--drivers/staging/slicoss/TODO37
-rw-r--r--drivers/staging/slicoss/slic.h527
-rw-r--r--drivers/staging/slicoss/slichw.h827
-rw-r--r--drivers/staging/slicoss/slicoss.c3176
-rw-r--r--drivers/staging/sm750fb/Kconfig10
-rw-r--r--drivers/staging/sm750fb/Makefile4
-rw-r--r--drivers/staging/sm750fb/TODO15
-rw-r--r--drivers/staging/sm750fb/ddk750.h24
-rw-r--r--drivers/staging/sm750fb/ddk750_chip.c622
-rw-r--r--drivers/staging/sm750fb/ddk750_chip.h87
-rw-r--r--drivers/staging/sm750fb/ddk750_display.c307
-rw-r--r--drivers/staging/sm750fb/ddk750_display.h160
-rw-r--r--drivers/staging/sm750fb/ddk750_dvi.c99
-rw-r--r--drivers/staging/sm750fb/ddk750_dvi.h67
-rw-r--r--drivers/staging/sm750fb/ddk750_help.c19
-rw-r--r--drivers/staging/sm750fb/ddk750_help.h29
-rw-r--r--drivers/staging/sm750fb/ddk750_hwi2c.c271
-rw-r--r--drivers/staging/sm750fb/ddk750_hwi2c.h10
-rw-r--r--drivers/staging/sm750fb/ddk750_mode.c205
-rw-r--r--drivers/staging/sm750fb/ddk750_mode.h43
-rw-r--r--drivers/staging/sm750fb/ddk750_power.c239
-rw-r--r--drivers/staging/sm750fb/ddk750_power.h71
-rw-r--r--drivers/staging/sm750fb/ddk750_reg.h2616
-rw-r--r--drivers/staging/sm750fb/ddk750_sii164.c425
-rw-r--r--drivers/staging/sm750fb/ddk750_sii164.h172
-rw-r--r--drivers/staging/sm750fb/ddk750_swi2c.c522
-rw-r--r--drivers/staging/sm750fb/ddk750_swi2c.h92
-rw-r--r--drivers/staging/sm750fb/modedb.h221
-rw-r--r--drivers/staging/sm750fb/readme38
-rw-r--r--drivers/staging/sm750fb/sm750.c1403
-rw-r--r--drivers/staging/sm750fb/sm750.h186
-rw-r--r--drivers/staging/sm750fb/sm750_accel.c437
-rw-r--r--drivers/staging/sm750fb/sm750_accel.h275
-rw-r--r--drivers/staging/sm750fb/sm750_cursor.c251
-rw-r--r--drivers/staging/sm750fb/sm750_cursor.h17
-rw-r--r--drivers/staging/sm750fb/sm750_help.h111
-rw-r--r--drivers/staging/sm750fb/sm750_hw.c642
-rw-r--r--drivers/staging/sm750fb/sm750_hw.h104
-rw-r--r--drivers/staging/sm7xxfb/Kconfig13
-rw-r--r--drivers/staging/sm7xxfb/Makefile1
-rw-r--r--drivers/staging/sm7xxfb/TODO12
-rw-r--r--drivers/staging/sm7xxfb/sm7xx.h779
-rw-r--r--drivers/staging/sm7xxfb/sm7xxfb.c1058
-rw-r--r--drivers/staging/speakup/DefaultKeyAssignments46
-rw-r--r--drivers/staging/speakup/Kconfig199
-rw-r--r--drivers/staging/speakup/Makefile30
-rw-r--r--drivers/staging/speakup/TODO47
-rw-r--r--drivers/staging/speakup/buffers.c105
-rw-r--r--drivers/staging/speakup/devsynth.c95
-rw-r--r--drivers/staging/speakup/fakekey.c99
-rw-r--r--drivers/staging/speakup/i18n.c629
-rw-r--r--drivers/staging/speakup/i18n.h234
-rw-r--r--drivers/staging/speakup/keyhelp.c224
-rw-r--r--drivers/staging/speakup/kobjects.c1039
-rw-r--r--drivers/staging/speakup/main.c2409
-rw-r--r--drivers/staging/speakup/selection.c186
-rw-r--r--drivers/staging/speakup/serialio.c220
-rw-r--r--drivers/staging/speakup/serialio.h40
-rw-r--r--drivers/staging/speakup/speakup.h123
-rw-r--r--drivers/staging/speakup/speakup_acnt.h16
-rw-r--r--drivers/staging/speakup/speakup_acntpc.c328
-rw-r--r--drivers/staging/speakup/speakup_acntsa.c153
-rw-r--r--drivers/staging/speakup/speakup_apollo.c217
-rw-r--r--drivers/staging/speakup/speakup_audptr.c187
-rw-r--r--drivers/staging/speakup/speakup_bns.c137
-rw-r--r--drivers/staging/speakup/speakup_decext.c246
-rw-r--r--drivers/staging/speakup/speakup_decpc.c503
-rw-r--r--drivers/staging/speakup/speakup_dectlk.c316
-rw-r--r--drivers/staging/speakup/speakup_dtlk.c398
-rw-r--r--drivers/staging/speakup/speakup_dtlk.h54
-rw-r--r--drivers/staging/speakup/speakup_dummy.c138
-rw-r--r--drivers/staging/speakup/speakup_keypc.c328
-rw-r--r--drivers/staging/speakup/speakup_ltlk.c185
-rw-r--r--drivers/staging/speakup/speakup_soft.c359
-rw-r--r--drivers/staging/speakup/speakup_spkout.c156
-rw-r--r--drivers/staging/speakup/speakup_txprt.c137
-rw-r--r--drivers/staging/speakup/speakupmap.h65
-rw-r--r--drivers/staging/speakup/speakupmap.map93
-rw-r--r--drivers/staging/speakup/spk_priv.h80
-rw-r--r--drivers/staging/speakup/spk_priv_keyinfo.h110
-rw-r--r--drivers/staging/speakup/spk_types.h205
-rw-r--r--drivers/staging/speakup/spkguide.txt1575
-rw-r--r--drivers/staging/speakup/synth.c485
-rw-r--r--drivers/staging/speakup/thread.c59
-rw-r--r--drivers/staging/speakup/varhandlers.c332
-rw-r--r--drivers/staging/staging.c19
-rw-r--r--drivers/staging/ste_rmi4/Kconfig9
-rw-r--r--drivers/staging/ste_rmi4/Makefile4
-rw-r--r--drivers/staging/ste_rmi4/TODO7
-rw-r--r--drivers/staging/ste_rmi4/synaptics_i2c_rmi4.c1142
-rw-r--r--drivers/staging/ste_rmi4/synaptics_i2c_rmi4.h46
-rw-r--r--drivers/staging/unisys/Documentation/ABI/sysfs-platform-visorchipset101
-rw-r--r--drivers/staging/unisys/Documentation/overview.txt174
-rw-r--r--drivers/staging/unisys/Documentation/proc-entries.txt93
-rw-r--r--drivers/staging/unisys/Kconfig19
-rw-r--r--drivers/staging/unisys/MAINTAINERS6
-rw-r--r--drivers/staging/unisys/Makefile9
-rw-r--r--drivers/staging/unisys/TODO21
-rw-r--r--drivers/staging/unisys/common-spar/include/channels/channel.h590
-rw-r--r--drivers/staging/unisys/common-spar/include/channels/channel_guid.h61
-rw-r--r--drivers/staging/unisys/common-spar/include/channels/controlframework.h62
-rw-r--r--drivers/staging/unisys/common-spar/include/channels/controlvmchannel.h511
-rw-r--r--drivers/staging/unisys/common-spar/include/channels/diagchannel.h427
-rw-r--r--drivers/staging/unisys/common-spar/include/channels/iochannel.h784
-rw-r--r--drivers/staging/unisys/common-spar/include/channels/vbuschannel.h94
-rw-r--r--drivers/staging/unisys/common-spar/include/controlvmcompletionstatus.h94
-rw-r--r--drivers/staging/unisys/common-spar/include/diagnostics/appos_subsystems.h310
-rw-r--r--drivers/staging/unisys/common-spar/include/iovmcall_gnuc.h49
-rw-r--r--drivers/staging/unisys/common-spar/include/vbusdeviceinfo.h213
-rw-r--r--drivers/staging/unisys/common-spar/include/version.h45
-rw-r--r--drivers/staging/unisys/common-spar/include/vmcallinterface.h163
-rw-r--r--drivers/staging/unisys/include/guestlinuxdebug.h180
-rw-r--r--drivers/staging/unisys/include/periodic_work.h38
-rw-r--r--drivers/staging/unisys/include/procobjecttree.h47
-rw-r--r--drivers/staging/unisys/include/sparstop.h30
-rw-r--r--drivers/staging/unisys/include/timskmod.h153
-rw-r--r--drivers/staging/unisys/include/uisqueue.h396
-rw-r--r--drivers/staging/unisys/include/uisthread.h42
-rw-r--r--drivers/staging/unisys/include/uisutils.h299
-rw-r--r--drivers/staging/unisys/include/vbushelper.h47
-rw-r--r--drivers/staging/unisys/uislib/Kconfig10
-rw-r--r--drivers/staging/unisys/uislib/Makefile12
-rw-r--r--drivers/staging/unisys/uislib/uislib.c1372
-rw-r--r--drivers/staging/unisys/uislib/uisqueue.c322
-rw-r--r--drivers/staging/unisys/uislib/uisthread.c69
-rw-r--r--drivers/staging/unisys/uislib/uisutils.c137
-rw-r--r--drivers/staging/unisys/virthba/Kconfig13
-rw-r--r--drivers/staging/unisys/virthba/Makefile12
-rw-r--r--drivers/staging/unisys/virthba/virthba.c1572
-rw-r--r--drivers/staging/unisys/virthba/virthba.h27
-rw-r--r--drivers/staging/unisys/virtpci/Kconfig10
-rw-r--r--drivers/staging/unisys/virtpci/Makefile10
-rw-r--r--drivers/staging/unisys/virtpci/virtpci.c1394
-rw-r--r--drivers/staging/unisys/virtpci/virtpci.h103
-rw-r--r--drivers/staging/unisys/visorchannel/Kconfig10
-rw-r--r--drivers/staging/unisys/visorchannel/Makefile12
-rw-r--r--drivers/staging/unisys/visorchannel/globals.h27
-rw-r--r--drivers/staging/unisys/visorchannel/visorchannel.h76
-rw-r--r--drivers/staging/unisys/visorchannel/visorchannel_funcs.c665
-rw-r--r--drivers/staging/unisys/visorchannel/visorchannel_main.c50
-rw-r--r--drivers/staging/unisys/visorchipset/Kconfig11
-rw-r--r--drivers/staging/unisys/visorchipset/Makefile15
-rw-r--r--drivers/staging/unisys/visorchipset/file.c160
-rw-r--r--drivers/staging/unisys/visorchipset/file.h27
-rw-r--r--drivers/staging/unisys/visorchipset/globals.h42
-rw-r--r--drivers/staging/unisys/visorchipset/parser.c430
-rw-r--r--drivers/staging/unisys/visorchipset/parser.h46
-rw-r--r--drivers/staging/unisys/visorchipset/visorchipset.h236
-rw-r--r--drivers/staging/unisys/visorchipset/visorchipset_main.c2335
-rw-r--r--drivers/staging/unisys/visorchipset/visorchipset_umode.h35
-rw-r--r--drivers/staging/unisys/visorutil/Kconfig9
-rw-r--r--drivers/staging/unisys/visorutil/Makefile9
-rw-r--r--drivers/staging/unisys/visorutil/charqueue.c127
-rw-r--r--drivers/staging/unisys/visorutil/charqueue.h37
-rw-r--r--drivers/staging/unisys/visorutil/memregion.h43
-rw-r--r--drivers/staging/unisys/visorutil/memregion_direct.c207
-rw-r--r--drivers/staging/unisys/visorutil/periodic_work.c204
-rw-r--r--drivers/staging/unisys/visorutil/visorkmodutils.c71
-rw-r--r--drivers/staging/vhba/Kconfig9
-rw-r--r--drivers/staging/vhba/Makefile4
-rw-r--r--drivers/staging/vhba/vhba.c1071
-rw-r--r--drivers/staging/vme/Makefile1
-rw-r--r--drivers/staging/vme/devices/Kconfig25
-rw-r--r--drivers/staging/vme/devices/Makefile8
-rw-r--r--drivers/staging/vme/devices/vme_pio2.h249
-rw-r--r--drivers/staging/vme/devices/vme_pio2_cntr.c71
-rw-r--r--drivers/staging/vme/devices/vme_pio2_core.c511
-rw-r--r--drivers/staging/vme/devices/vme_pio2_gpio.c226
-rw-r--r--drivers/staging/vme/devices/vme_user.c962
-rw-r--r--drivers/staging/vme/devices/vme_user.h58
-rw-r--r--drivers/staging/vt6655/Kconfig6
-rw-r--r--drivers/staging/vt6655/Makefile18
-rw-r--r--drivers/staging/vt6655/TODO21
-rw-r--r--drivers/staging/vt6655/baseband.c2397
-rw-r--r--drivers/staging/vt6655/baseband.h96
-rw-r--r--drivers/staging/vt6655/card.c1017
-rw-r--r--drivers/staging/vt6655/card.h89
-rw-r--r--drivers/staging/vt6655/channel.c237
-rw-r--r--drivers/staging/vt6655/channel.h32
-rw-r--r--drivers/staging/vt6655/desc.h351
-rw-r--r--drivers/staging/vt6655/device.h424
-rw-r--r--drivers/staging/vt6655/device_cfg.h87
-rw-r--r--drivers/staging/vt6655/device_main.c1914
-rw-r--r--drivers/staging/vt6655/dpc.c162
-rw-r--r--drivers/staging/vt6655/dpc.h36
-rw-r--r--drivers/staging/vt6655/key.c168
-rw-r--r--drivers/staging/vt6655/key.h69
-rw-r--r--drivers/staging/vt6655/mac.c895
-rw-r--r--drivers/staging/vt6655/mac.h946
-rw-r--r--drivers/staging/vt6655/mib.c139
-rw-r--r--drivers/staging/vt6655/mib.h82
-rw-r--r--drivers/staging/vt6655/power.c167
-rw-r--r--drivers/staging/vt6655/power.h53
-rw-r--r--drivers/staging/vt6655/rf.c965
-rw-r--r--drivers/staging/vt6655/rf.h100
-rw-r--r--drivers/staging/vt6655/rxtx.c1522
-rw-r--r--drivers/staging/vt6655/rxtx.h200
-rw-r--r--drivers/staging/vt6655/srom.c153
-rw-r--r--drivers/staging/vt6655/srom.h101
-rw-r--r--drivers/staging/vt6655/test9
-rw-r--r--drivers/staging/vt6655/tmacro.h58
-rw-r--r--drivers/staging/vt6655/upc.h89
-rw-r--r--drivers/staging/vt6656/Kconfig7
-rw-r--r--drivers/staging/vt6656/Makefile20
-rw-r--r--drivers/staging/vt6656/TODO19
-rw-r--r--drivers/staging/vt6656/baseband.c831
-rw-r--r--drivers/staging/vt6656/baseband.h104
-rw-r--r--drivers/staging/vt6656/card.c820
-rw-r--r--drivers/staging/vt6656/card.h58
-rw-r--r--drivers/staging/vt6656/channel.c178
-rw-r--r--drivers/staging/vt6656/channel.h37
-rw-r--r--drivers/staging/vt6656/desc.h105
-rw-r--r--drivers/staging/vt6656/device.h407
-rw-r--r--drivers/staging/vt6656/dpc.c186
-rw-r--r--drivers/staging/vt6656/dpc.h37
-rw-r--r--drivers/staging/vt6656/firmware.c141
-rw-r--r--drivers/staging/vt6656/firmware.h39
-rw-r--r--drivers/staging/vt6656/int.c166
-rw-r--r--drivers/staging/vt6656/int.h61
-rw-r--r--drivers/staging/vt6656/key.c179
-rw-r--r--drivers/staging/vt6656/key.h55
-rw-r--r--drivers/staging/vt6656/mac.c248
-rw-r--r--drivers/staging/vt6656/mac.h387
-rw-r--r--drivers/staging/vt6656/main_usb.c1059
-rw-r--r--drivers/staging/vt6656/power.c144
-rw-r--r--drivers/staging/vt6656/power.h38
-rw-r--r--drivers/staging/vt6656/rf.c951
-rw-r--r--drivers/staging/vt6656/rf.h63
-rw-r--r--drivers/staging/vt6656/rxtx.c1112
-rw-r--r--drivers/staging/vt6656/rxtx.h260
-rw-r--r--drivers/staging/vt6656/usbpipe.c308
-rw-r--r--drivers/staging/vt6656/usbpipe.h45
-rw-r--r--drivers/staging/vt6656/wcmd.c194
-rw-r--r--drivers/staging/vt6656/wcmd.h63
-rw-r--r--drivers/staging/wlan-ng/Kconfig12
-rw-r--r--drivers/staging/wlan-ng/Makefile7
-rw-r--r--drivers/staging/wlan-ng/README8
-rw-r--r--drivers/staging/wlan-ng/cfg80211.c796
-rw-r--r--drivers/staging/wlan-ng/hfa384x.h1431
-rw-r--r--drivers/staging/wlan-ng/hfa384x_usb.c4127
-rw-r--r--drivers/staging/wlan-ng/p80211conv.c661
-rw-r--r--drivers/staging/wlan-ng/p80211conv.h163
-rw-r--r--drivers/staging/wlan-ng/p80211hdr.h213
-rw-r--r--drivers/staging/wlan-ng/p80211ioctl.h89
-rw-r--r--drivers/staging/wlan-ng/p80211meta.h90
-rw-r--r--drivers/staging/wlan-ng/p80211metadef.h261
-rw-r--r--drivers/staging/wlan-ng/p80211metastruct.h271
-rw-r--r--drivers/staging/wlan-ng/p80211mgmt.h520
-rw-r--r--drivers/staging/wlan-ng/p80211msg.h59
-rw-r--r--drivers/staging/wlan-ng/p80211netdev.c1085
-rw-r--r--drivers/staging/wlan-ng/p80211netdev.h242
-rw-r--r--drivers/staging/wlan-ng/p80211req.c250
-rw-r--r--drivers/staging/wlan-ng/p80211req.h53
-rw-r--r--drivers/staging/wlan-ng/p80211types.h375
-rw-r--r--drivers/staging/wlan-ng/p80211wep.c301
-rw-r--r--drivers/staging/wlan-ng/prism2fw.c1217
-rw-r--r--drivers/staging/wlan-ng/prism2mgmt.c1322
-rw-r--r--drivers/staging/wlan-ng/prism2mgmt.h117
-rw-r--r--drivers/staging/wlan-ng/prism2mib.c825
-rw-r--r--drivers/staging/wlan-ng/prism2sta.c2018
-rw-r--r--drivers/staging/wlan-ng/prism2usb.c299
-rw-r--r--drivers/staging/xgifb/Kconfig11
-rw-r--r--drivers/staging/xgifb/Makefile4
-rw-r--r--drivers/staging/xgifb/TODO13
-rw-r--r--drivers/staging/xgifb/XGI_main.h377
-rw-r--r--drivers/staging/xgifb/XGI_main_26.c2127
-rw-r--r--drivers/staging/xgifb/XGIfb.h108
-rw-r--r--drivers/staging/xgifb/vb_def.h257
-rw-r--r--drivers/staging/xgifb/vb_init.c1367
-rw-r--r--drivers/staging/xgifb/vb_init.h6
-rw-r--r--drivers/staging/xgifb/vb_setmode.c5554
-rw-r--r--drivers/staging/xgifb/vb_setmode.h23
-rw-r--r--drivers/staging/xgifb/vb_struct.h166
-rw-r--r--drivers/staging/xgifb/vb_table.h2491
-rw-r--r--drivers/staging/xgifb/vb_util.c42
-rw-r--r--drivers/staging/xgifb/vb_util.h9
-rw-r--r--drivers/staging/xgifb/vgatypes.h49
1788 files changed, 860412 insertions, 0 deletions
diff --git a/drivers/staging/Kconfig b/drivers/staging/Kconfig
new file mode 100644
index 000000000..76bb98f1b
--- /dev/null
+++ b/drivers/staging/Kconfig
@@ -0,0 +1,117 @@
+menuconfig STAGING
+ bool "Staging drivers"
+ default n
+ ---help---
+ This option allows you to select a number of drivers that are
+ not of the "normal" Linux kernel quality level. These drivers
+ are placed here in order to get a wider audience to make use of
+ them. Please note that these drivers are under heavy
+ development, may or may not work, and may contain userspace
+ interfaces that most likely will be changed in the near
+ future.
+
+ Using any of these drivers will taint your kernel which might
+ affect support options from both the community, and various
+ commercial support organizations.
+
+ If you wish to work on these drivers, to help improve them, or
+ to report problems you have with them, please see the
+ driver_name.README file in the drivers/staging/ directory to
+ see what needs to be worked on, and who to contact.
+
+ If in doubt, say N here.
+
+
+if STAGING
+
+source "drivers/staging/slicoss/Kconfig"
+
+source "drivers/staging/wlan-ng/Kconfig"
+
+source "drivers/staging/comedi/Kconfig"
+
+source "drivers/staging/olpc_dcon/Kconfig"
+
+source "drivers/staging/panel/Kconfig"
+
+source "drivers/staging/rtl8192u/Kconfig"
+
+source "drivers/staging/rtl8192e/Kconfig"
+
+source "drivers/staging/rtl8712/Kconfig"
+
+source "drivers/staging/rtl8188eu/Kconfig"
+
+source "drivers/staging/rtl8723au/Kconfig"
+
+source "drivers/staging/rts5208/Kconfig"
+
+source "drivers/staging/octeon/Kconfig"
+
+source "drivers/staging/octeon-usb/Kconfig"
+
+source "drivers/staging/vt6655/Kconfig"
+
+source "drivers/staging/vt6656/Kconfig"
+
+source "drivers/staging/iio/Kconfig"
+
+source "drivers/staging/sm7xxfb/Kconfig"
+
+source "drivers/staging/sm750fb/Kconfig"
+
+source "drivers/staging/xgifb/Kconfig"
+
+source "drivers/staging/emxx_udc/Kconfig"
+
+source "drivers/staging/ft1000/Kconfig"
+
+source "drivers/staging/speakup/Kconfig"
+
+source "drivers/staging/ste_rmi4/Kconfig"
+
+source "drivers/staging/nvec/Kconfig"
+
+source "drivers/staging/media/Kconfig"
+
+source "drivers/staging/android/Kconfig"
+
+source "drivers/staging/board/Kconfig"
+
+source "drivers/staging/ozwpan/Kconfig"
+
+source "drivers/staging/gdm72xx/Kconfig"
+
+source "drivers/staging/gdm724x/Kconfig"
+
+source "drivers/staging/fwserial/Kconfig"
+
+source "drivers/staging/goldfish/Kconfig"
+
+source "drivers/staging/netlogic/Kconfig"
+
+source "drivers/staging/mt29f_spinand/Kconfig"
+
+source "drivers/staging/lustre/Kconfig"
+
+source "drivers/staging/dgnc/Kconfig"
+
+source "drivers/staging/dgap/Kconfig"
+
+source "drivers/staging/gs_fpgaboot/Kconfig"
+
+source "drivers/staging/skein/Kconfig"
+
+source "drivers/staging/unisys/Kconfig"
+
+source "drivers/staging/clocking-wizard/Kconfig"
+
+source "drivers/staging/fbtft/Kconfig"
+
+source "drivers/staging/i2o/Kconfig"
+
+source "drivers/staging/fsl-mc/Kconfig"
+
+source "drivers/staging/vhba/Kconfig"
+
+endif # STAGING
diff --git a/drivers/staging/Makefile b/drivers/staging/Makefile
new file mode 100644
index 000000000..0b6563feb
--- /dev/null
+++ b/drivers/staging/Makefile
@@ -0,0 +1,51 @@
+# Makefile for staging directory
+
+# fix for build system bug...
+obj-$(CONFIG_STAGING) += staging.o
+
+obj-y += media/
+obj-$(CONFIG_SLICOSS) += slicoss/
+obj-$(CONFIG_PRISM2_USB) += wlan-ng/
+obj-$(CONFIG_COMEDI) += comedi/
+obj-$(CONFIG_FB_OLPC_DCON) += olpc_dcon/
+obj-$(CONFIG_PANEL) += panel/
+obj-$(CONFIG_RTL8192U) += rtl8192u/
+obj-$(CONFIG_RTL8192E) += rtl8192e/
+obj-$(CONFIG_R8712U) += rtl8712/
+obj-$(CONFIG_R8188EU) += rtl8188eu/
+obj-$(CONFIG_R8723AU) += rtl8723au/
+obj-$(CONFIG_RTS5208) += rts5208/
+obj-$(CONFIG_NETLOGIC_XLR_NET) += netlogic/
+obj-$(CONFIG_OCTEON_ETHERNET) += octeon/
+obj-$(CONFIG_OCTEON_USB) += octeon-usb/
+obj-$(CONFIG_VT6655) += vt6655/
+obj-$(CONFIG_VT6656) += vt6656/
+obj-$(CONFIG_VME_BUS) += vme/
+obj-$(CONFIG_IIO) += iio/
+obj-$(CONFIG_FB_SM7XX) += sm7xxfb/
+obj-$(CONFIG_FB_SM7XX) += sm750fb/
+obj-$(CONFIG_FB_XGI) += xgifb/
+obj-$(CONFIG_VHBA) += vhba/
+obj-$(CONFIG_USB_EMXX) += emxx_udc/
+obj-$(CONFIG_FT1000) += ft1000/
+obj-$(CONFIG_SPEAKUP) += speakup/
+obj-$(CONFIG_TOUCHSCREEN_SYNAPTICS_I2C_RMI4) += ste_rmi4/
+obj-$(CONFIG_MFD_NVEC) += nvec/
+obj-$(CONFIG_ANDROID) += android/
+obj-$(CONFIG_STAGING_BOARD) += board/
+obj-$(CONFIG_USB_WPAN_HCD) += ozwpan/
+obj-$(CONFIG_WIMAX_GDM72XX) += gdm72xx/
+obj-$(CONFIG_LTE_GDM724X) += gdm724x/
+obj-$(CONFIG_FIREWIRE_SERIAL) += fwserial/
+obj-$(CONFIG_GOLDFISH) += goldfish/
+obj-$(CONFIG_LUSTRE_FS) += lustre/
+obj-$(CONFIG_DGNC) += dgnc/
+obj-$(CONFIG_DGAP) += dgap/
+obj-$(CONFIG_MTD_SPINAND_MT29F) += mt29f_spinand/
+obj-$(CONFIG_GS_FPGABOOT) += gs_fpgaboot/
+obj-$(CONFIG_CRYPTO_SKEIN) += skein/
+obj-$(CONFIG_UNISYSSPAR) += unisys/
+obj-$(CONFIG_COMMON_CLK_XLNX_CLKWZRD) += clocking-wizard/
+obj-$(CONFIG_FB_TFT) += fbtft/
+obj-$(CONFIG_I2O) += i2o/
+obj-$(CONFIG_FSL_MC_BUS) += fsl-mc/
diff --git a/drivers/staging/android/Kconfig b/drivers/staging/android/Kconfig
new file mode 100644
index 000000000..8feb9048e
--- /dev/null
+++ b/drivers/staging/android/Kconfig
@@ -0,0 +1,63 @@
+menu "Android"
+
+if ANDROID
+
+config ASHMEM
+ bool "Enable the Anonymous Shared Memory Subsystem"
+ default n
+ depends on SHMEM
+ ---help---
+ The ashmem subsystem is a new shared memory allocator, similar to
+ POSIX SHM but with different behavior and sporting a simpler
+ file-based API.
+
+ It is, in theory, a good memory allocator for low-memory devices,
+ because it can discard shared memory units when under memory pressure.
+
+config ANDROID_TIMED_OUTPUT
+ bool "Timed output class driver"
+ default y
+
+config ANDROID_TIMED_GPIO
+ tristate "Android timed gpio driver"
+ depends on GPIOLIB && ANDROID_TIMED_OUTPUT
+ default n
+
+config ANDROID_LOW_MEMORY_KILLER
+ bool "Android Low Memory Killer"
+ ---help---
+ Registers processes to be killed when memory is low
+
+config SYNC
+ bool "Synchronization framework"
+ default n
+ select ANON_INODES
+ select DMA_SHARED_BUFFER
+ ---help---
+ This option enables the framework for synchronization between multiple
+ drivers. Sync implementations can take advantage of hardware
+ synchronization built into devices like GPUs.
+
+config SW_SYNC
+ bool "Software synchronization objects"
+ default n
+ depends on SYNC
+ ---help---
+ A sync object driver that uses a 32bit counter to coordinate
+ syncrhronization. Useful when there is no hardware primitive backing
+ the synchronization.
+
+config SW_SYNC_USER
+ bool "Userspace API for SW_SYNC"
+ default n
+ depends on SW_SYNC
+ ---help---
+ Provides a user space API to the sw sync object.
+ *WARNING* improper use of this can result in deadlocking kernel
+ drivers from userspace.
+
+source "drivers/staging/android/ion/Kconfig"
+
+endif # if ANDROID
+
+endmenu
diff --git a/drivers/staging/android/Makefile b/drivers/staging/android/Makefile
new file mode 100644
index 000000000..c7b6c99cc
--- /dev/null
+++ b/drivers/staging/android/Makefile
@@ -0,0 +1,10 @@
+ccflags-y += -I$(src) # needed for trace events
+
+obj-y += ion/
+
+obj-$(CONFIG_ASHMEM) += ashmem.o
+obj-$(CONFIG_ANDROID_TIMED_OUTPUT) += timed_output.o
+obj-$(CONFIG_ANDROID_TIMED_GPIO) += timed_gpio.o
+obj-$(CONFIG_ANDROID_LOW_MEMORY_KILLER) += lowmemorykiller.o
+obj-$(CONFIG_SYNC) += sync.o sync_debug.o
+obj-$(CONFIG_SW_SYNC) += sw_sync.o
diff --git a/drivers/staging/android/TODO b/drivers/staging/android/TODO
new file mode 100644
index 000000000..06954cdf3
--- /dev/null
+++ b/drivers/staging/android/TODO
@@ -0,0 +1,17 @@
+TODO:
+ - checkpatch.pl cleanups
+ - sparse fixes
+ - rename files to be not so "generic"
+ - make sure things build as modules properly
+ - add proper arch dependencies as needed
+ - audit userspace interfaces to make sure they are sane
+ - kuid_t should never be exposed to user space as it is
+ kernel internal type. Data structure for this kuid_t is:
+ typedef struct {
+ uid_t val;
+ } kuid_t;
+ - This bug is introduced by Xiong Zhou in the patch bd471258f2e09
+ - ("staging: android: logger: use kuid_t instead of uid_t")
+
+Please send patches to Greg Kroah-Hartman <greg@kroah.com> and Cc:
+Brian Swetland <swetland@google.com>
diff --git a/drivers/staging/android/ashmem.c b/drivers/staging/android/ashmem.c
new file mode 100644
index 000000000..82effc8b4
--- /dev/null
+++ b/drivers/staging/android/ashmem.c
@@ -0,0 +1,883 @@
+/* mm/ashmem.c
+ *
+ * Anonymous Shared Memory Subsystem, ashmem
+ *
+ * Copyright (C) 2008 Google, Inc.
+ *
+ * Robert Love <rlove@google.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#define pr_fmt(fmt) "ashmem: " fmt
+
+#include <linux/module.h>
+#include <linux/file.h>
+#include <linux/fs.h>
+#include <linux/falloc.h>
+#include <linux/miscdevice.h>
+#include <linux/security.h>
+#include <linux/mm.h>
+#include <linux/mman.h>
+#include <linux/uaccess.h>
+#include <linux/personality.h>
+#include <linux/bitops.h>
+#include <linux/mutex.h>
+#include <linux/shmem_fs.h>
+#include "ashmem.h"
+
+#define ASHMEM_NAME_PREFIX "dev/ashmem/"
+#define ASHMEM_NAME_PREFIX_LEN (sizeof(ASHMEM_NAME_PREFIX) - 1)
+#define ASHMEM_FULL_NAME_LEN (ASHMEM_NAME_LEN + ASHMEM_NAME_PREFIX_LEN)
+
+/**
+ * struct ashmem_area - The anonymous shared memory area
+ * @name: The optional name in /proc/pid/maps
+ * @unpinned_list: The list of all ashmem areas
+ * @file: The shmem-based backing file
+ * @size: The size of the mapping, in bytes
+ * @prot_masks: The allowed protection bits, as vm_flags
+ *
+ * The lifecycle of this structure is from our parent file's open() until
+ * its release(). It is also protected by 'ashmem_mutex'
+ *
+ * Warning: Mappings do NOT pin this structure; It dies on close()
+ */
+struct ashmem_area {
+ char name[ASHMEM_FULL_NAME_LEN];
+ struct list_head unpinned_list;
+ struct file *file;
+ size_t size;
+ unsigned long prot_mask;
+};
+
+/**
+ * struct ashmem_range - A range of unpinned/evictable pages
+ * @lru: The entry in the LRU list
+ * @unpinned: The entry in its area's unpinned list
+ * @asma: The associated anonymous shared memory area.
+ * @pgstart: The starting page (inclusive)
+ * @pgend: The ending page (inclusive)
+ * @purged: The purge status (ASHMEM_NOT or ASHMEM_WAS_PURGED)
+ *
+ * The lifecycle of this structure is from unpin to pin.
+ * It is protected by 'ashmem_mutex'
+ */
+struct ashmem_range {
+ struct list_head lru;
+ struct list_head unpinned;
+ struct ashmem_area *asma;
+ size_t pgstart;
+ size_t pgend;
+ unsigned int purged;
+};
+
+/* LRU list of unpinned pages, protected by ashmem_mutex */
+static LIST_HEAD(ashmem_lru_list);
+
+/**
+ * long lru_count - The count of pages on our LRU list.
+ *
+ * This is protected by ashmem_mutex.
+ */
+static unsigned long lru_count;
+
+/**
+ * ashmem_mutex - protects the list of and each individual ashmem_area
+ *
+ * Lock Ordering: ashmex_mutex -> i_mutex -> i_alloc_sem
+ */
+static DEFINE_MUTEX(ashmem_mutex);
+
+static struct kmem_cache *ashmem_area_cachep __read_mostly;
+static struct kmem_cache *ashmem_range_cachep __read_mostly;
+
+#define range_size(range) \
+ ((range)->pgend - (range)->pgstart + 1)
+
+#define range_on_lru(range) \
+ ((range)->purged == ASHMEM_NOT_PURGED)
+
+#define page_range_subsumes_range(range, start, end) \
+ (((range)->pgstart >= (start)) && ((range)->pgend <= (end)))
+
+#define page_range_subsumed_by_range(range, start, end) \
+ (((range)->pgstart <= (start)) && ((range)->pgend >= (end)))
+
+#define page_in_range(range, page) \
+ (((range)->pgstart <= (page)) && ((range)->pgend >= (page)))
+
+#define page_range_in_range(range, start, end) \
+ (page_in_range(range, start) || page_in_range(range, end) || \
+ page_range_subsumes_range(range, start, end))
+
+#define range_before_page(range, page) \
+ ((range)->pgend < (page))
+
+#define PROT_MASK (PROT_EXEC | PROT_READ | PROT_WRITE)
+
+/**
+ * lru_add() - Adds a range of memory to the LRU list
+ * @range: The memory range being added.
+ *
+ * The range is first added to the end (tail) of the LRU list.
+ * After this, the size of the range is added to @lru_count
+ */
+static inline void lru_add(struct ashmem_range *range)
+{
+ list_add_tail(&range->lru, &ashmem_lru_list);
+ lru_count += range_size(range);
+}
+
+/**
+ * lru_del() - Removes a range of memory from the LRU list
+ * @range: The memory range being removed
+ *
+ * The range is first deleted from the LRU list.
+ * After this, the size of the range is removed from @lru_count
+ */
+static inline void lru_del(struct ashmem_range *range)
+{
+ list_del(&range->lru);
+ lru_count -= range_size(range);
+}
+
+/**
+ * range_alloc() - Allocates and initializes a new ashmem_range structure
+ * @asma: The associated ashmem_area
+ * @prev_range: The previous ashmem_range in the sorted asma->unpinned list
+ * @purged: Initial purge status (ASMEM_NOT_PURGED or ASHMEM_WAS_PURGED)
+ * @start: The starting page (inclusive)
+ * @end: The ending page (inclusive)
+ *
+ * This function is protected by ashmem_mutex.
+ *
+ * Return: 0 if successful, or -ENOMEM if there is an error
+ */
+static int range_alloc(struct ashmem_area *asma,
+ struct ashmem_range *prev_range, unsigned int purged,
+ size_t start, size_t end)
+{
+ struct ashmem_range *range;
+
+ range = kmem_cache_zalloc(ashmem_range_cachep, GFP_KERNEL);
+ if (unlikely(!range))
+ return -ENOMEM;
+
+ range->asma = asma;
+ range->pgstart = start;
+ range->pgend = end;
+ range->purged = purged;
+
+ list_add_tail(&range->unpinned, &prev_range->unpinned);
+
+ if (range_on_lru(range))
+ lru_add(range);
+
+ return 0;
+}
+
+/**
+ * range_del() - Deletes and dealloctes an ashmem_range structure
+ * @range: The associated ashmem_range that has previously been allocated
+ */
+static void range_del(struct ashmem_range *range)
+{
+ list_del(&range->unpinned);
+ if (range_on_lru(range))
+ lru_del(range);
+ kmem_cache_free(ashmem_range_cachep, range);
+}
+
+/**
+ * range_shrink() - Shrinks an ashmem_range
+ * @range: The associated ashmem_range being shrunk
+ * @start: The starting byte of the new range
+ * @end: The ending byte of the new range
+ *
+ * This does not modify the data inside the existing range in any way - It
+ * simply shrinks the boundaries of the range.
+ *
+ * Theoretically, with a little tweaking, this could eventually be changed
+ * to range_resize, and expand the lru_count if the new range is larger.
+ */
+static inline void range_shrink(struct ashmem_range *range,
+ size_t start, size_t end)
+{
+ size_t pre = range_size(range);
+
+ range->pgstart = start;
+ range->pgend = end;
+
+ if (range_on_lru(range))
+ lru_count -= pre - range_size(range);
+}
+
+/**
+ * ashmem_open() - Opens an Anonymous Shared Memory structure
+ * @inode: The backing file's index node(?)
+ * @file: The backing file
+ *
+ * Please note that the ashmem_area is not returned by this function - It is
+ * instead written to "file->private_data".
+ *
+ * Return: 0 if successful, or another code if unsuccessful.
+ */
+static int ashmem_open(struct inode *inode, struct file *file)
+{
+ struct ashmem_area *asma;
+ int ret;
+
+ ret = generic_file_open(inode, file);
+ if (unlikely(ret))
+ return ret;
+
+ asma = kmem_cache_zalloc(ashmem_area_cachep, GFP_KERNEL);
+ if (unlikely(!asma))
+ return -ENOMEM;
+
+ INIT_LIST_HEAD(&asma->unpinned_list);
+ memcpy(asma->name, ASHMEM_NAME_PREFIX, ASHMEM_NAME_PREFIX_LEN);
+ asma->prot_mask = PROT_MASK;
+ file->private_data = asma;
+
+ return 0;
+}
+
+/**
+ * ashmem_release() - Releases an Anonymous Shared Memory structure
+ * @ignored: The backing file's Index Node(?) - It is ignored here.
+ * @file: The backing file
+ *
+ * Return: 0 if successful. If it is anything else, go have a coffee and
+ * try again.
+ */
+static int ashmem_release(struct inode *ignored, struct file *file)
+{
+ struct ashmem_area *asma = file->private_data;
+ struct ashmem_range *range, *next;
+
+ mutex_lock(&ashmem_mutex);
+ list_for_each_entry_safe(range, next, &asma->unpinned_list, unpinned)
+ range_del(range);
+ mutex_unlock(&ashmem_mutex);
+
+ if (asma->file)
+ fput(asma->file);
+ kmem_cache_free(ashmem_area_cachep, asma);
+
+ return 0;
+}
+
+/**
+ * ashmem_read() - Reads a set of bytes from an Ashmem-enabled file
+ * @file: The associated backing file.
+ * @buf: The buffer of data being written to
+ * @len: The number of bytes being read
+ * @pos: The position of the first byte to read.
+ *
+ * Return: 0 if successful, or another return code if not.
+ */
+static ssize_t ashmem_read(struct file *file, char __user *buf,
+ size_t len, loff_t *pos)
+{
+ struct ashmem_area *asma = file->private_data;
+ int ret = 0;
+
+ mutex_lock(&ashmem_mutex);
+
+ /* If size is not set, or set to 0, always return EOF. */
+ if (asma->size == 0)
+ goto out_unlock;
+
+ if (!asma->file) {
+ ret = -EBADF;
+ goto out_unlock;
+ }
+
+ mutex_unlock(&ashmem_mutex);
+
+ /*
+ * asma and asma->file are used outside the lock here. We assume
+ * once asma->file is set it will never be changed, and will not
+ * be destroyed until all references to the file are dropped and
+ * ashmem_release is called.
+ */
+ ret = __vfs_read(asma->file, buf, len, pos);
+ if (ret >= 0) {
+ /** Update backing file pos, since f_ops->read() doesn't */
+ asma->file->f_pos = *pos;
+ }
+ return ret;
+
+out_unlock:
+ mutex_unlock(&ashmem_mutex);
+ return ret;
+}
+
+static loff_t ashmem_llseek(struct file *file, loff_t offset, int origin)
+{
+ struct ashmem_area *asma = file->private_data;
+ int ret;
+
+ mutex_lock(&ashmem_mutex);
+
+ if (asma->size == 0) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ if (!asma->file) {
+ ret = -EBADF;
+ goto out;
+ }
+
+ ret = vfs_llseek(asma->file, offset, origin);
+ if (ret < 0)
+ goto out;
+
+ /** Copy f_pos from backing file, since f_ops->llseek() sets it */
+ file->f_pos = asma->file->f_pos;
+
+out:
+ mutex_unlock(&ashmem_mutex);
+ return ret;
+}
+
+static inline vm_flags_t calc_vm_may_flags(unsigned long prot)
+{
+ return _calc_vm_trans(prot, PROT_READ, VM_MAYREAD) |
+ _calc_vm_trans(prot, PROT_WRITE, VM_MAYWRITE) |
+ _calc_vm_trans(prot, PROT_EXEC, VM_MAYEXEC);
+}
+
+static int ashmem_mmap(struct file *file, struct vm_area_struct *vma)
+{
+ struct ashmem_area *asma = file->private_data;
+ int ret = 0;
+
+ mutex_lock(&ashmem_mutex);
+
+ /* user needs to SET_SIZE before mapping */
+ if (unlikely(!asma->size)) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ /* requested protection bits must match our allowed protection mask */
+ if (unlikely((vma->vm_flags & ~calc_vm_prot_bits(asma->prot_mask)) &
+ calc_vm_prot_bits(PROT_MASK))) {
+ ret = -EPERM;
+ goto out;
+ }
+ vma->vm_flags &= ~calc_vm_may_flags(~asma->prot_mask);
+
+ if (!asma->file) {
+ char *name = ASHMEM_NAME_DEF;
+ struct file *vmfile;
+
+ if (asma->name[ASHMEM_NAME_PREFIX_LEN] != '\0')
+ name = asma->name;
+
+ /* ... and allocate the backing shmem file */
+ vmfile = shmem_file_setup(name, asma->size, vma->vm_flags, 0);
+ if (unlikely(IS_ERR(vmfile))) {
+ ret = PTR_ERR(vmfile);
+ goto out;
+ }
+ asma->file = vmfile;
+ }
+ get_file(asma->file);
+
+ /*
+ * XXX - Reworked to use shmem_zero_setup() instead of
+ * shmem_set_file while we're in staging. -jstultz
+ */
+ if (vma->vm_flags & VM_SHARED) {
+ ret = shmem_zero_setup(vma);
+ if (ret) {
+ fput(asma->file);
+ goto out;
+ }
+ }
+
+ if (vma->vm_file)
+ fput(vma->vm_file);
+ vma->vm_file = asma->file;
+
+out:
+ mutex_unlock(&ashmem_mutex);
+ return ret;
+}
+
+/*
+ * ashmem_shrink - our cache shrinker, called from mm/vmscan.c
+ *
+ * 'nr_to_scan' is the number of objects to scan for freeing.
+ *
+ * 'gfp_mask' is the mask of the allocation that got us into this mess.
+ *
+ * Return value is the number of objects freed or -1 if we cannot
+ * proceed without risk of deadlock (due to gfp_mask).
+ *
+ * We approximate LRU via least-recently-unpinned, jettisoning unpinned partial
+ * chunks of ashmem regions LRU-wise one-at-a-time until we hit 'nr_to_scan'
+ * pages freed.
+ */
+static unsigned long
+ashmem_shrink_scan(struct shrinker *shrink, struct shrink_control *sc)
+{
+ struct ashmem_range *range, *next;
+ unsigned long freed = 0;
+
+ /* We might recurse into filesystem code, so bail out if necessary */
+ if (!(sc->gfp_mask & __GFP_FS))
+ return SHRINK_STOP;
+
+ mutex_lock(&ashmem_mutex);
+ list_for_each_entry_safe(range, next, &ashmem_lru_list, lru) {
+ loff_t start = range->pgstart * PAGE_SIZE;
+ loff_t end = (range->pgend + 1) * PAGE_SIZE;
+
+ vfs_fallocate(range->asma->file,
+ FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE,
+ start, end - start);
+ range->purged = ASHMEM_WAS_PURGED;
+ lru_del(range);
+
+ freed += range_size(range);
+ if (--sc->nr_to_scan <= 0)
+ break;
+ }
+ mutex_unlock(&ashmem_mutex);
+ return freed;
+}
+
+static unsigned long
+ashmem_shrink_count(struct shrinker *shrink, struct shrink_control *sc)
+{
+ /*
+ * note that lru_count is count of pages on the lru, not a count of
+ * objects on the list. This means the scan function needs to return the
+ * number of pages freed, not the number of objects scanned.
+ */
+ return lru_count;
+}
+
+static struct shrinker ashmem_shrinker = {
+ .count_objects = ashmem_shrink_count,
+ .scan_objects = ashmem_shrink_scan,
+ /*
+ * XXX (dchinner): I wish people would comment on why they need on
+ * significant changes to the default value here
+ */
+ .seeks = DEFAULT_SEEKS * 4,
+};
+
+static int set_prot_mask(struct ashmem_area *asma, unsigned long prot)
+{
+ int ret = 0;
+
+ mutex_lock(&ashmem_mutex);
+
+ /* the user can only remove, not add, protection bits */
+ if (unlikely((asma->prot_mask & prot) != prot)) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ /* does the application expect PROT_READ to imply PROT_EXEC? */
+ if ((prot & PROT_READ) && (current->personality & READ_IMPLIES_EXEC))
+ prot |= PROT_EXEC;
+
+ asma->prot_mask = prot;
+
+out:
+ mutex_unlock(&ashmem_mutex);
+ return ret;
+}
+
+static int set_name(struct ashmem_area *asma, void __user *name)
+{
+ int len;
+ int ret = 0;
+ char local_name[ASHMEM_NAME_LEN];
+
+ /*
+ * Holding the ashmem_mutex while doing a copy_from_user might cause
+ * an data abort which would try to access mmap_sem. If another
+ * thread has invoked ashmem_mmap then it will be holding the
+ * semaphore and will be waiting for ashmem_mutex, there by leading to
+ * deadlock. We'll release the mutex and take the name to a local
+ * variable that does not need protection and later copy the local
+ * variable to the structure member with lock held.
+ */
+ len = strncpy_from_user(local_name, name, ASHMEM_NAME_LEN);
+ if (len < 0)
+ return len;
+ if (len == ASHMEM_NAME_LEN)
+ local_name[ASHMEM_NAME_LEN - 1] = '\0';
+ mutex_lock(&ashmem_mutex);
+ /* cannot change an existing mapping's name */
+ if (unlikely(asma->file))
+ ret = -EINVAL;
+ else
+ strcpy(asma->name + ASHMEM_NAME_PREFIX_LEN, local_name);
+
+ mutex_unlock(&ashmem_mutex);
+ return ret;
+}
+
+static int get_name(struct ashmem_area *asma, void __user *name)
+{
+ int ret = 0;
+ size_t len;
+ /*
+ * Have a local variable to which we'll copy the content
+ * from asma with the lock held. Later we can copy this to the user
+ * space safely without holding any locks. So even if we proceed to
+ * wait for mmap_sem, it won't lead to deadlock.
+ */
+ char local_name[ASHMEM_NAME_LEN];
+
+ mutex_lock(&ashmem_mutex);
+ if (asma->name[ASHMEM_NAME_PREFIX_LEN] != '\0') {
+ /*
+ * Copying only `len', instead of ASHMEM_NAME_LEN, bytes
+ * prevents us from revealing one user's stack to another.
+ */
+ len = strlen(asma->name + ASHMEM_NAME_PREFIX_LEN) + 1;
+ memcpy(local_name, asma->name + ASHMEM_NAME_PREFIX_LEN, len);
+ } else {
+ len = sizeof(ASHMEM_NAME_DEF);
+ memcpy(local_name, ASHMEM_NAME_DEF, len);
+ }
+ mutex_unlock(&ashmem_mutex);
+
+ /*
+ * Now we are just copying from the stack variable to userland
+ * No lock held
+ */
+ if (unlikely(copy_to_user(name, local_name, len)))
+ ret = -EFAULT;
+ return ret;
+}
+
+/*
+ * ashmem_pin - pin the given ashmem region, returning whether it was
+ * previously purged (ASHMEM_WAS_PURGED) or not (ASHMEM_NOT_PURGED).
+ *
+ * Caller must hold ashmem_mutex.
+ */
+static int ashmem_pin(struct ashmem_area *asma, size_t pgstart, size_t pgend)
+{
+ struct ashmem_range *range, *next;
+ int ret = ASHMEM_NOT_PURGED;
+
+ list_for_each_entry_safe(range, next, &asma->unpinned_list, unpinned) {
+ /* moved past last applicable page; we can short circuit */
+ if (range_before_page(range, pgstart))
+ break;
+
+ /*
+ * The user can ask us to pin pages that span multiple ranges,
+ * or to pin pages that aren't even unpinned, so this is messy.
+ *
+ * Four cases:
+ * 1. The requested range subsumes an existing range, so we
+ * just remove the entire matching range.
+ * 2. The requested range overlaps the start of an existing
+ * range, so we just update that range.
+ * 3. The requested range overlaps the end of an existing
+ * range, so we just update that range.
+ * 4. The requested range punches a hole in an existing range,
+ * so we have to update one side of the range and then
+ * create a new range for the other side.
+ */
+ if (page_range_in_range(range, pgstart, pgend)) {
+ ret |= range->purged;
+
+ /* Case #1: Easy. Just nuke the whole thing. */
+ if (page_range_subsumes_range(range, pgstart, pgend)) {
+ range_del(range);
+ continue;
+ }
+
+ /* Case #2: We overlap from the start, so adjust it */
+ if (range->pgstart >= pgstart) {
+ range_shrink(range, pgend + 1, range->pgend);
+ continue;
+ }
+
+ /* Case #3: We overlap from the rear, so adjust it */
+ if (range->pgend <= pgend) {
+ range_shrink(range, range->pgstart, pgstart-1);
+ continue;
+ }
+
+ /*
+ * Case #4: We eat a chunk out of the middle. A bit
+ * more complicated, we allocate a new range for the
+ * second half and adjust the first chunk's endpoint.
+ */
+ range_alloc(asma, range, range->purged,
+ pgend + 1, range->pgend);
+ range_shrink(range, range->pgstart, pgstart - 1);
+ break;
+ }
+ }
+
+ return ret;
+}
+
+/*
+ * ashmem_unpin - unpin the given range of pages. Returns zero on success.
+ *
+ * Caller must hold ashmem_mutex.
+ */
+static int ashmem_unpin(struct ashmem_area *asma, size_t pgstart, size_t pgend)
+{
+ struct ashmem_range *range, *next;
+ unsigned int purged = ASHMEM_NOT_PURGED;
+
+restart:
+ list_for_each_entry_safe(range, next, &asma->unpinned_list, unpinned) {
+ /* short circuit: this is our insertion point */
+ if (range_before_page(range, pgstart))
+ break;
+
+ /*
+ * The user can ask us to unpin pages that are already entirely
+ * or partially pinned. We handle those two cases here.
+ */
+ if (page_range_subsumed_by_range(range, pgstart, pgend))
+ return 0;
+ if (page_range_in_range(range, pgstart, pgend)) {
+ pgstart = min_t(size_t, range->pgstart, pgstart),
+ pgend = max_t(size_t, range->pgend, pgend);
+ purged |= range->purged;
+ range_del(range);
+ goto restart;
+ }
+ }
+
+ return range_alloc(asma, range, purged, pgstart, pgend);
+}
+
+/*
+ * ashmem_get_pin_status - Returns ASHMEM_IS_UNPINNED if _any_ pages in the
+ * given interval are unpinned and ASHMEM_IS_PINNED otherwise.
+ *
+ * Caller must hold ashmem_mutex.
+ */
+static int ashmem_get_pin_status(struct ashmem_area *asma, size_t pgstart,
+ size_t pgend)
+{
+ struct ashmem_range *range;
+ int ret = ASHMEM_IS_PINNED;
+
+ list_for_each_entry(range, &asma->unpinned_list, unpinned) {
+ if (range_before_page(range, pgstart))
+ break;
+ if (page_range_in_range(range, pgstart, pgend)) {
+ ret = ASHMEM_IS_UNPINNED;
+ break;
+ }
+ }
+
+ return ret;
+}
+
+static int ashmem_pin_unpin(struct ashmem_area *asma, unsigned long cmd,
+ void __user *p)
+{
+ struct ashmem_pin pin;
+ size_t pgstart, pgend;
+ int ret = -EINVAL;
+
+ if (unlikely(!asma->file))
+ return -EINVAL;
+
+ if (unlikely(copy_from_user(&pin, p, sizeof(pin))))
+ return -EFAULT;
+
+ /* per custom, you can pass zero for len to mean "everything onward" */
+ if (!pin.len)
+ pin.len = PAGE_ALIGN(asma->size) - pin.offset;
+
+ if (unlikely((pin.offset | pin.len) & ~PAGE_MASK))
+ return -EINVAL;
+
+ if (unlikely(((__u32) -1) - pin.offset < pin.len))
+ return -EINVAL;
+
+ if (unlikely(PAGE_ALIGN(asma->size) < pin.offset + pin.len))
+ return -EINVAL;
+
+ pgstart = pin.offset / PAGE_SIZE;
+ pgend = pgstart + (pin.len / PAGE_SIZE) - 1;
+
+ mutex_lock(&ashmem_mutex);
+
+ switch (cmd) {
+ case ASHMEM_PIN:
+ ret = ashmem_pin(asma, pgstart, pgend);
+ break;
+ case ASHMEM_UNPIN:
+ ret = ashmem_unpin(asma, pgstart, pgend);
+ break;
+ case ASHMEM_GET_PIN_STATUS:
+ ret = ashmem_get_pin_status(asma, pgstart, pgend);
+ break;
+ }
+
+ mutex_unlock(&ashmem_mutex);
+
+ return ret;
+}
+
+static long ashmem_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+ struct ashmem_area *asma = file->private_data;
+ long ret = -ENOTTY;
+
+ switch (cmd) {
+ case ASHMEM_SET_NAME:
+ ret = set_name(asma, (void __user *)arg);
+ break;
+ case ASHMEM_GET_NAME:
+ ret = get_name(asma, (void __user *)arg);
+ break;
+ case ASHMEM_SET_SIZE:
+ ret = -EINVAL;
+ if (!asma->file) {
+ ret = 0;
+ asma->size = (size_t) arg;
+ }
+ break;
+ case ASHMEM_GET_SIZE:
+ ret = asma->size;
+ break;
+ case ASHMEM_SET_PROT_MASK:
+ ret = set_prot_mask(asma, arg);
+ break;
+ case ASHMEM_GET_PROT_MASK:
+ ret = asma->prot_mask;
+ break;
+ case ASHMEM_PIN:
+ case ASHMEM_UNPIN:
+ case ASHMEM_GET_PIN_STATUS:
+ ret = ashmem_pin_unpin(asma, cmd, (void __user *)arg);
+ break;
+ case ASHMEM_PURGE_ALL_CACHES:
+ ret = -EPERM;
+ if (capable(CAP_SYS_ADMIN)) {
+ struct shrink_control sc = {
+ .gfp_mask = GFP_KERNEL,
+ .nr_to_scan = LONG_MAX,
+ };
+ ret = ashmem_shrink_count(&ashmem_shrinker, &sc);
+ ashmem_shrink_scan(&ashmem_shrinker, &sc);
+ }
+ break;
+ }
+
+ return ret;
+}
+
+/* support of 32bit userspace on 64bit platforms */
+#ifdef CONFIG_COMPAT
+static long compat_ashmem_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ switch (cmd) {
+ case COMPAT_ASHMEM_SET_SIZE:
+ cmd = ASHMEM_SET_SIZE;
+ break;
+ case COMPAT_ASHMEM_SET_PROT_MASK:
+ cmd = ASHMEM_SET_PROT_MASK;
+ break;
+ }
+ return ashmem_ioctl(file, cmd, arg);
+}
+#endif
+
+static const struct file_operations ashmem_fops = {
+ .owner = THIS_MODULE,
+ .open = ashmem_open,
+ .release = ashmem_release,
+ .read = ashmem_read,
+ .llseek = ashmem_llseek,
+ .mmap = ashmem_mmap,
+ .unlocked_ioctl = ashmem_ioctl,
+#ifdef CONFIG_COMPAT
+ .compat_ioctl = compat_ashmem_ioctl,
+#endif
+};
+
+static struct miscdevice ashmem_misc = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "ashmem",
+ .fops = &ashmem_fops,
+};
+
+static int __init ashmem_init(void)
+{
+ int ret;
+
+ ashmem_area_cachep = kmem_cache_create("ashmem_area_cache",
+ sizeof(struct ashmem_area),
+ 0, 0, NULL);
+ if (unlikely(!ashmem_area_cachep)) {
+ pr_err("failed to create slab cache\n");
+ return -ENOMEM;
+ }
+
+ ashmem_range_cachep = kmem_cache_create("ashmem_range_cache",
+ sizeof(struct ashmem_range),
+ 0, 0, NULL);
+ if (unlikely(!ashmem_range_cachep)) {
+ pr_err("failed to create slab cache\n");
+ return -ENOMEM;
+ }
+
+ ret = misc_register(&ashmem_misc);
+ if (unlikely(ret)) {
+ pr_err("failed to register misc device!\n");
+ return ret;
+ }
+
+ register_shrinker(&ashmem_shrinker);
+
+ pr_info("initialized\n");
+
+ return 0;
+}
+
+static void __exit ashmem_exit(void)
+{
+ int ret;
+
+ unregister_shrinker(&ashmem_shrinker);
+
+ ret = misc_deregister(&ashmem_misc);
+ if (unlikely(ret))
+ pr_err("failed to unregister misc device!\n");
+
+ kmem_cache_destroy(ashmem_range_cachep);
+ kmem_cache_destroy(ashmem_area_cachep);
+
+ pr_info("unloaded\n");
+}
+
+module_init(ashmem_init);
+module_exit(ashmem_exit);
+
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/android/ashmem.h b/drivers/staging/android/ashmem.h
new file mode 100644
index 000000000..5abcfd7aa
--- /dev/null
+++ b/drivers/staging/android/ashmem.h
@@ -0,0 +1,27 @@
+/*
+ * include/linux/ashmem.h
+ *
+ * Copyright 2008 Google Inc.
+ * Author: Robert Love
+ *
+ * This file is dual licensed. It may be redistributed and/or modified
+ * under the terms of the Apache 2.0 License OR version 2 of the GNU
+ * General Public License.
+ */
+
+#ifndef _LINUX_ASHMEM_H
+#define _LINUX_ASHMEM_H
+
+#include <linux/limits.h>
+#include <linux/ioctl.h>
+#include <linux/compat.h>
+
+#include "uapi/ashmem.h"
+
+/* support of 32bit userspace on 64bit platforms */
+#ifdef CONFIG_COMPAT
+#define COMPAT_ASHMEM_SET_SIZE _IOW(__ASHMEMIOC, 3, compat_size_t)
+#define COMPAT_ASHMEM_SET_PROT_MASK _IOW(__ASHMEMIOC, 5, unsigned int)
+#endif
+
+#endif /* _LINUX_ASHMEM_H */
diff --git a/drivers/staging/android/ion/Kconfig b/drivers/staging/android/ion/Kconfig
new file mode 100644
index 000000000..345234624
--- /dev/null
+++ b/drivers/staging/android/ion/Kconfig
@@ -0,0 +1,35 @@
+menuconfig ION
+ bool "Ion Memory Manager"
+ depends on HAVE_MEMBLOCK && HAS_DMA && MMU
+ select GENERIC_ALLOCATOR
+ select DMA_SHARED_BUFFER
+ ---help---
+ Chose this option to enable the ION Memory Manager,
+ used by Android to efficiently allocate buffers
+ from userspace that can be shared between drivers.
+ If you're not using Android its probably safe to
+ say N here.
+
+config ION_TEST
+ tristate "Ion Test Device"
+ depends on ION
+ help
+ Choose this option to create a device that can be used to test the
+ kernel and device side ION functions.
+
+config ION_DUMMY
+ bool "Dummy Ion driver"
+ depends on ION
+ help
+ Provides a dummy ION driver that registers the
+ /dev/ion device and some basic heaps. This can
+ be used for testing the ION infrastructure if
+ one doesn't have access to hardware drivers that
+ use ION.
+
+config ION_TEGRA
+ tristate "Ion for Tegra"
+ depends on ARCH_TEGRA && ION
+ help
+ Choose this option if you wish to use ion on an nVidia Tegra.
+
diff --git a/drivers/staging/android/ion/Makefile b/drivers/staging/android/ion/Makefile
new file mode 100644
index 000000000..b56fd2bf2
--- /dev/null
+++ b/drivers/staging/android/ion/Makefile
@@ -0,0 +1,10 @@
+obj-$(CONFIG_ION) += ion.o ion_heap.o ion_page_pool.o ion_system_heap.o \
+ ion_carveout_heap.o ion_chunk_heap.o ion_cma_heap.o
+obj-$(CONFIG_ION_TEST) += ion_test.o
+ifdef CONFIG_COMPAT
+obj-$(CONFIG_ION) += compat_ion.o
+endif
+
+obj-$(CONFIG_ION_DUMMY) += ion_dummy_driver.o
+obj-$(CONFIG_ION_TEGRA) += tegra/
+
diff --git a/drivers/staging/android/ion/compat_ion.c b/drivers/staging/android/ion/compat_ion.c
new file mode 100644
index 000000000..a402fdaf5
--- /dev/null
+++ b/drivers/staging/android/ion/compat_ion.c
@@ -0,0 +1,195 @@
+/*
+ * drivers/staging/android/ion/compat_ion.c
+ *
+ * Copyright (C) 2013 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ */
+
+#include <linux/compat.h>
+#include <linux/fs.h>
+#include <linux/uaccess.h>
+
+#include "ion.h"
+#include "compat_ion.h"
+
+/* See drivers/staging/android/uapi/ion.h for the definition of these structs */
+struct compat_ion_allocation_data {
+ compat_size_t len;
+ compat_size_t align;
+ compat_uint_t heap_id_mask;
+ compat_uint_t flags;
+ compat_int_t handle;
+};
+
+struct compat_ion_custom_data {
+ compat_uint_t cmd;
+ compat_ulong_t arg;
+};
+
+struct compat_ion_handle_data {
+ compat_int_t handle;
+};
+
+#define COMPAT_ION_IOC_ALLOC _IOWR(ION_IOC_MAGIC, 0, \
+ struct compat_ion_allocation_data)
+#define COMPAT_ION_IOC_FREE _IOWR(ION_IOC_MAGIC, 1, \
+ struct compat_ion_handle_data)
+#define COMPAT_ION_IOC_CUSTOM _IOWR(ION_IOC_MAGIC, 6, \
+ struct compat_ion_custom_data)
+
+static int compat_get_ion_allocation_data(
+ struct compat_ion_allocation_data __user *data32,
+ struct ion_allocation_data __user *data)
+{
+ compat_size_t s;
+ compat_uint_t u;
+ compat_int_t i;
+ int err;
+
+ err = get_user(s, &data32->len);
+ err |= put_user(s, &data->len);
+ err |= get_user(s, &data32->align);
+ err |= put_user(s, &data->align);
+ err |= get_user(u, &data32->heap_id_mask);
+ err |= put_user(u, &data->heap_id_mask);
+ err |= get_user(u, &data32->flags);
+ err |= put_user(u, &data->flags);
+ err |= get_user(i, &data32->handle);
+ err |= put_user(i, &data->handle);
+
+ return err;
+}
+
+static int compat_get_ion_handle_data(
+ struct compat_ion_handle_data __user *data32,
+ struct ion_handle_data __user *data)
+{
+ compat_int_t i;
+ int err;
+
+ err = get_user(i, &data32->handle);
+ err |= put_user(i, &data->handle);
+
+ return err;
+}
+
+static int compat_put_ion_allocation_data(
+ struct compat_ion_allocation_data __user *data32,
+ struct ion_allocation_data __user *data)
+{
+ compat_size_t s;
+ compat_uint_t u;
+ compat_int_t i;
+ int err;
+
+ err = get_user(s, &data->len);
+ err |= put_user(s, &data32->len);
+ err |= get_user(s, &data->align);
+ err |= put_user(s, &data32->align);
+ err |= get_user(u, &data->heap_id_mask);
+ err |= put_user(u, &data32->heap_id_mask);
+ err |= get_user(u, &data->flags);
+ err |= put_user(u, &data32->flags);
+ err |= get_user(i, &data->handle);
+ err |= put_user(i, &data32->handle);
+
+ return err;
+}
+
+static int compat_get_ion_custom_data(
+ struct compat_ion_custom_data __user *data32,
+ struct ion_custom_data __user *data)
+{
+ compat_uint_t cmd;
+ compat_ulong_t arg;
+ int err;
+
+ err = get_user(cmd, &data32->cmd);
+ err |= put_user(cmd, &data->cmd);
+ err |= get_user(arg, &data32->arg);
+ err |= put_user(arg, &data->arg);
+
+ return err;
+};
+
+long compat_ion_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
+{
+ long ret;
+
+ if (!filp->f_op->unlocked_ioctl)
+ return -ENOTTY;
+
+ switch (cmd) {
+ case COMPAT_ION_IOC_ALLOC:
+ {
+ struct compat_ion_allocation_data __user *data32;
+ struct ion_allocation_data __user *data;
+ int err;
+
+ data32 = compat_ptr(arg);
+ data = compat_alloc_user_space(sizeof(*data));
+ if (data == NULL)
+ return -EFAULT;
+
+ err = compat_get_ion_allocation_data(data32, data);
+ if (err)
+ return err;
+ ret = filp->f_op->unlocked_ioctl(filp, ION_IOC_ALLOC,
+ (unsigned long)data);
+ err = compat_put_ion_allocation_data(data32, data);
+ return ret ? ret : err;
+ }
+ case COMPAT_ION_IOC_FREE:
+ {
+ struct compat_ion_handle_data __user *data32;
+ struct ion_handle_data __user *data;
+ int err;
+
+ data32 = compat_ptr(arg);
+ data = compat_alloc_user_space(sizeof(*data));
+ if (data == NULL)
+ return -EFAULT;
+
+ err = compat_get_ion_handle_data(data32, data);
+ if (err)
+ return err;
+
+ return filp->f_op->unlocked_ioctl(filp, ION_IOC_FREE,
+ (unsigned long)data);
+ }
+ case COMPAT_ION_IOC_CUSTOM: {
+ struct compat_ion_custom_data __user *data32;
+ struct ion_custom_data __user *data;
+ int err;
+
+ data32 = compat_ptr(arg);
+ data = compat_alloc_user_space(sizeof(*data));
+ if (data == NULL)
+ return -EFAULT;
+
+ err = compat_get_ion_custom_data(data32, data);
+ if (err)
+ return err;
+
+ return filp->f_op->unlocked_ioctl(filp, ION_IOC_CUSTOM,
+ (unsigned long)data);
+ }
+ case ION_IOC_SHARE:
+ case ION_IOC_MAP:
+ case ION_IOC_IMPORT:
+ case ION_IOC_SYNC:
+ return filp->f_op->unlocked_ioctl(filp, cmd,
+ (unsigned long)compat_ptr(arg));
+ default:
+ return -ENOIOCTLCMD;
+ }
+}
diff --git a/drivers/staging/android/ion/compat_ion.h b/drivers/staging/android/ion/compat_ion.h
new file mode 100644
index 000000000..c2ad5893d
--- /dev/null
+++ b/drivers/staging/android/ion/compat_ion.h
@@ -0,0 +1,30 @@
+/*
+
+ * drivers/staging/android/ion/compat_ion.h
+ *
+ * Copyright (C) 2013 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ */
+
+#ifndef _LINUX_COMPAT_ION_H
+#define _LINUX_COMPAT_ION_H
+
+#if IS_ENABLED(CONFIG_COMPAT)
+
+long compat_ion_ioctl(struct file *filp, unsigned int cmd, unsigned long arg);
+
+#else
+
+#define compat_ion_ioctl NULL
+
+#endif /* CONFIG_COMPAT */
+#endif /* _LINUX_COMPAT_ION_H */
diff --git a/drivers/staging/android/ion/ion.c b/drivers/staging/android/ion/ion.c
new file mode 100644
index 000000000..b0b96ab31
--- /dev/null
+++ b/drivers/staging/android/ion/ion.c
@@ -0,0 +1,1652 @@
+/*
+
+ * drivers/staging/android/ion/ion.c
+ *
+ * Copyright (C) 2011 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ */
+
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/file.h>
+#include <linux/freezer.h>
+#include <linux/fs.h>
+#include <linux/anon_inodes.h>
+#include <linux/kthread.h>
+#include <linux/list.h>
+#include <linux/memblock.h>
+#include <linux/miscdevice.h>
+#include <linux/export.h>
+#include <linux/mm.h>
+#include <linux/mm_types.h>
+#include <linux/rbtree.h>
+#include <linux/slab.h>
+#include <linux/seq_file.h>
+#include <linux/uaccess.h>
+#include <linux/vmalloc.h>
+#include <linux/debugfs.h>
+#include <linux/dma-buf.h>
+#include <linux/idr.h>
+
+#include "ion.h"
+#include "ion_priv.h"
+#include "compat_ion.h"
+
+/**
+ * struct ion_device - the metadata of the ion device node
+ * @dev: the actual misc device
+ * @buffers: an rb tree of all the existing buffers
+ * @buffer_lock: lock protecting the tree of buffers
+ * @lock: rwsem protecting the tree of heaps and clients
+ * @heaps: list of all the heaps in the system
+ * @user_clients: list of all the clients created from userspace
+ */
+struct ion_device {
+ struct miscdevice dev;
+ struct rb_root buffers;
+ struct mutex buffer_lock;
+ struct rw_semaphore lock;
+ struct plist_head heaps;
+ long (*custom_ioctl)(struct ion_client *client, unsigned int cmd,
+ unsigned long arg);
+ struct rb_root clients;
+ struct dentry *debug_root;
+ struct dentry *heaps_debug_root;
+ struct dentry *clients_debug_root;
+};
+
+/**
+ * struct ion_client - a process/hw block local address space
+ * @node: node in the tree of all clients
+ * @dev: backpointer to ion device
+ * @handles: an rb tree of all the handles in this client
+ * @idr: an idr space for allocating handle ids
+ * @lock: lock protecting the tree of handles
+ * @name: used for debugging
+ * @display_name: used for debugging (unique version of @name)
+ * @display_serial: used for debugging (to make display_name unique)
+ * @task: used for debugging
+ *
+ * A client represents a list of buffers this client may access.
+ * The mutex stored here is used to protect both handles tree
+ * as well as the handles themselves, and should be held while modifying either.
+ */
+struct ion_client {
+ struct rb_node node;
+ struct ion_device *dev;
+ struct rb_root handles;
+ struct idr idr;
+ struct mutex lock;
+ const char *name;
+ char *display_name;
+ int display_serial;
+ struct task_struct *task;
+ pid_t pid;
+ struct dentry *debug_root;
+};
+
+/**
+ * ion_handle - a client local reference to a buffer
+ * @ref: reference count
+ * @client: back pointer to the client the buffer resides in
+ * @buffer: pointer to the buffer
+ * @node: node in the client's handle rbtree
+ * @kmap_cnt: count of times this client has mapped to kernel
+ * @id: client-unique id allocated by client->idr
+ *
+ * Modifications to node, map_cnt or mapping should be protected by the
+ * lock in the client. Other fields are never changed after initialization.
+ */
+struct ion_handle {
+ struct kref ref;
+ struct ion_client *client;
+ struct ion_buffer *buffer;
+ struct rb_node node;
+ unsigned int kmap_cnt;
+ int id;
+};
+
+bool ion_buffer_fault_user_mappings(struct ion_buffer *buffer)
+{
+ return (buffer->flags & ION_FLAG_CACHED) &&
+ !(buffer->flags & ION_FLAG_CACHED_NEEDS_SYNC);
+}
+
+bool ion_buffer_cached(struct ion_buffer *buffer)
+{
+ return !!(buffer->flags & ION_FLAG_CACHED);
+}
+
+static inline struct page *ion_buffer_page(struct page *page)
+{
+ return (struct page *)((unsigned long)page & ~(1UL));
+}
+
+static inline bool ion_buffer_page_is_dirty(struct page *page)
+{
+ return !!((unsigned long)page & 1UL);
+}
+
+static inline void ion_buffer_page_dirty(struct page **page)
+{
+ *page = (struct page *)((unsigned long)(*page) | 1UL);
+}
+
+static inline void ion_buffer_page_clean(struct page **page)
+{
+ *page = (struct page *)((unsigned long)(*page) & ~(1UL));
+}
+
+/* this function should only be called while dev->lock is held */
+static void ion_buffer_add(struct ion_device *dev,
+ struct ion_buffer *buffer)
+{
+ struct rb_node **p = &dev->buffers.rb_node;
+ struct rb_node *parent = NULL;
+ struct ion_buffer *entry;
+
+ while (*p) {
+ parent = *p;
+ entry = rb_entry(parent, struct ion_buffer, node);
+
+ if (buffer < entry) {
+ p = &(*p)->rb_left;
+ } else if (buffer > entry) {
+ p = &(*p)->rb_right;
+ } else {
+ pr_err("%s: buffer already found.", __func__);
+ BUG();
+ }
+ }
+
+ rb_link_node(&buffer->node, parent, p);
+ rb_insert_color(&buffer->node, &dev->buffers);
+}
+
+/* this function should only be called while dev->lock is held */
+static struct ion_buffer *ion_buffer_create(struct ion_heap *heap,
+ struct ion_device *dev,
+ unsigned long len,
+ unsigned long align,
+ unsigned long flags)
+{
+ struct ion_buffer *buffer;
+ struct sg_table *table;
+ struct scatterlist *sg;
+ int i, ret;
+
+ buffer = kzalloc(sizeof(struct ion_buffer), GFP_KERNEL);
+ if (!buffer)
+ return ERR_PTR(-ENOMEM);
+
+ buffer->heap = heap;
+ buffer->flags = flags;
+ kref_init(&buffer->ref);
+
+ ret = heap->ops->allocate(heap, buffer, len, align, flags);
+
+ if (ret) {
+ if (!(heap->flags & ION_HEAP_FLAG_DEFER_FREE))
+ goto err2;
+
+ ion_heap_freelist_drain(heap, 0);
+ ret = heap->ops->allocate(heap, buffer, len, align,
+ flags);
+ if (ret)
+ goto err2;
+ }
+
+ buffer->dev = dev;
+ buffer->size = len;
+
+ table = heap->ops->map_dma(heap, buffer);
+ if (WARN_ONCE(table == NULL,
+ "heap->ops->map_dma should return ERR_PTR on error"))
+ table = ERR_PTR(-EINVAL);
+ if (IS_ERR(table)) {
+ heap->ops->free(buffer);
+ kfree(buffer);
+ return ERR_CAST(table);
+ }
+ buffer->sg_table = table;
+ if (ion_buffer_fault_user_mappings(buffer)) {
+ int num_pages = PAGE_ALIGN(buffer->size) / PAGE_SIZE;
+ struct scatterlist *sg;
+ int i, j, k = 0;
+
+ buffer->pages = vmalloc(sizeof(struct page *) * num_pages);
+ if (!buffer->pages) {
+ ret = -ENOMEM;
+ goto err1;
+ }
+
+ for_each_sg(table->sgl, sg, table->nents, i) {
+ struct page *page = sg_page(sg);
+
+ for (j = 0; j < sg->length / PAGE_SIZE; j++)
+ buffer->pages[k++] = page++;
+ }
+
+ if (ret)
+ goto err;
+ }
+
+ buffer->dev = dev;
+ buffer->size = len;
+ INIT_LIST_HEAD(&buffer->vmas);
+ mutex_init(&buffer->lock);
+ /* this will set up dma addresses for the sglist -- it is not
+ technically correct as per the dma api -- a specific
+ device isn't really taking ownership here. However, in practice on
+ our systems the only dma_address space is physical addresses.
+ Additionally, we can't afford the overhead of invalidating every
+ allocation via dma_map_sg. The implicit contract here is that
+ memory coming from the heaps is ready for dma, ie if it has a
+ cached mapping that mapping has been invalidated */
+ for_each_sg(buffer->sg_table->sgl, sg, buffer->sg_table->nents, i)
+ sg_dma_address(sg) = sg_phys(sg);
+ mutex_lock(&dev->buffer_lock);
+ ion_buffer_add(dev, buffer);
+ mutex_unlock(&dev->buffer_lock);
+ return buffer;
+
+err:
+ heap->ops->unmap_dma(heap, buffer);
+ heap->ops->free(buffer);
+err1:
+ vfree(buffer->pages);
+err2:
+ kfree(buffer);
+ return ERR_PTR(ret);
+}
+
+void ion_buffer_destroy(struct ion_buffer *buffer)
+{
+ if (WARN_ON(buffer->kmap_cnt > 0))
+ buffer->heap->ops->unmap_kernel(buffer->heap, buffer);
+ buffer->heap->ops->unmap_dma(buffer->heap, buffer);
+ buffer->heap->ops->free(buffer);
+ vfree(buffer->pages);
+ kfree(buffer);
+}
+
+static void _ion_buffer_destroy(struct kref *kref)
+{
+ struct ion_buffer *buffer = container_of(kref, struct ion_buffer, ref);
+ struct ion_heap *heap = buffer->heap;
+ struct ion_device *dev = buffer->dev;
+
+ mutex_lock(&dev->buffer_lock);
+ rb_erase(&buffer->node, &dev->buffers);
+ mutex_unlock(&dev->buffer_lock);
+
+ if (heap->flags & ION_HEAP_FLAG_DEFER_FREE)
+ ion_heap_freelist_add(heap, buffer);
+ else
+ ion_buffer_destroy(buffer);
+}
+
+static void ion_buffer_get(struct ion_buffer *buffer)
+{
+ kref_get(&buffer->ref);
+}
+
+static int ion_buffer_put(struct ion_buffer *buffer)
+{
+ return kref_put(&buffer->ref, _ion_buffer_destroy);
+}
+
+static void ion_buffer_add_to_handle(struct ion_buffer *buffer)
+{
+ mutex_lock(&buffer->lock);
+ buffer->handle_count++;
+ mutex_unlock(&buffer->lock);
+}
+
+static void ion_buffer_remove_from_handle(struct ion_buffer *buffer)
+{
+ /*
+ * when a buffer is removed from a handle, if it is not in
+ * any other handles, copy the taskcomm and the pid of the
+ * process it's being removed from into the buffer. At this
+ * point there will be no way to track what processes this buffer is
+ * being used by, it only exists as a dma_buf file descriptor.
+ * The taskcomm and pid can provide a debug hint as to where this fd
+ * is in the system
+ */
+ mutex_lock(&buffer->lock);
+ buffer->handle_count--;
+ BUG_ON(buffer->handle_count < 0);
+ if (!buffer->handle_count) {
+ struct task_struct *task;
+
+ task = current->group_leader;
+ get_task_comm(buffer->task_comm, task);
+ buffer->pid = task_pid_nr(task);
+ }
+ mutex_unlock(&buffer->lock);
+}
+
+static struct ion_handle *ion_handle_create(struct ion_client *client,
+ struct ion_buffer *buffer)
+{
+ struct ion_handle *handle;
+
+ handle = kzalloc(sizeof(struct ion_handle), GFP_KERNEL);
+ if (!handle)
+ return ERR_PTR(-ENOMEM);
+ kref_init(&handle->ref);
+ RB_CLEAR_NODE(&handle->node);
+ handle->client = client;
+ ion_buffer_get(buffer);
+ ion_buffer_add_to_handle(buffer);
+ handle->buffer = buffer;
+
+ return handle;
+}
+
+static void ion_handle_kmap_put(struct ion_handle *);
+
+static void ion_handle_destroy(struct kref *kref)
+{
+ struct ion_handle *handle = container_of(kref, struct ion_handle, ref);
+ struct ion_client *client = handle->client;
+ struct ion_buffer *buffer = handle->buffer;
+
+ mutex_lock(&buffer->lock);
+ while (handle->kmap_cnt)
+ ion_handle_kmap_put(handle);
+ mutex_unlock(&buffer->lock);
+
+ idr_remove(&client->idr, handle->id);
+ if (!RB_EMPTY_NODE(&handle->node))
+ rb_erase(&handle->node, &client->handles);
+
+ ion_buffer_remove_from_handle(buffer);
+ ion_buffer_put(buffer);
+
+ kfree(handle);
+}
+
+struct ion_buffer *ion_handle_buffer(struct ion_handle *handle)
+{
+ return handle->buffer;
+}
+
+static void ion_handle_get(struct ion_handle *handle)
+{
+ kref_get(&handle->ref);
+}
+
+static int ion_handle_put(struct ion_handle *handle)
+{
+ struct ion_client *client = handle->client;
+ int ret;
+
+ mutex_lock(&client->lock);
+ ret = kref_put(&handle->ref, ion_handle_destroy);
+ mutex_unlock(&client->lock);
+
+ return ret;
+}
+
+static struct ion_handle *ion_handle_lookup(struct ion_client *client,
+ struct ion_buffer *buffer)
+{
+ struct rb_node *n = client->handles.rb_node;
+
+ while (n) {
+ struct ion_handle *entry = rb_entry(n, struct ion_handle, node);
+
+ if (buffer < entry->buffer)
+ n = n->rb_left;
+ else if (buffer > entry->buffer)
+ n = n->rb_right;
+ else
+ return entry;
+ }
+ return ERR_PTR(-EINVAL);
+}
+
+static struct ion_handle *ion_handle_get_by_id(struct ion_client *client,
+ int id)
+{
+ struct ion_handle *handle;
+
+ mutex_lock(&client->lock);
+ handle = idr_find(&client->idr, id);
+ if (handle)
+ ion_handle_get(handle);
+ mutex_unlock(&client->lock);
+
+ return handle ? handle : ERR_PTR(-EINVAL);
+}
+
+static bool ion_handle_validate(struct ion_client *client,
+ struct ion_handle *handle)
+{
+ WARN_ON(!mutex_is_locked(&client->lock));
+ return idr_find(&client->idr, handle->id) == handle;
+}
+
+static int ion_handle_add(struct ion_client *client, struct ion_handle *handle)
+{
+ int id;
+ struct rb_node **p = &client->handles.rb_node;
+ struct rb_node *parent = NULL;
+ struct ion_handle *entry;
+
+ id = idr_alloc(&client->idr, handle, 1, 0, GFP_KERNEL);
+ if (id < 0)
+ return id;
+
+ handle->id = id;
+
+ while (*p) {
+ parent = *p;
+ entry = rb_entry(parent, struct ion_handle, node);
+
+ if (handle->buffer < entry->buffer)
+ p = &(*p)->rb_left;
+ else if (handle->buffer > entry->buffer)
+ p = &(*p)->rb_right;
+ else
+ WARN(1, "%s: buffer already found.", __func__);
+ }
+
+ rb_link_node(&handle->node, parent, p);
+ rb_insert_color(&handle->node, &client->handles);
+
+ return 0;
+}
+
+struct ion_handle *ion_alloc(struct ion_client *client, size_t len,
+ size_t align, unsigned int heap_id_mask,
+ unsigned int flags)
+{
+ struct ion_handle *handle;
+ struct ion_device *dev = client->dev;
+ struct ion_buffer *buffer = NULL;
+ struct ion_heap *heap;
+ int ret;
+
+ pr_debug("%s: len %zu align %zu heap_id_mask %u flags %x\n", __func__,
+ len, align, heap_id_mask, flags);
+ /*
+ * traverse the list of heaps available in this system in priority
+ * order. If the heap type is supported by the client, and matches the
+ * request of the caller allocate from it. Repeat until allocate has
+ * succeeded or all heaps have been tried
+ */
+ len = PAGE_ALIGN(len);
+
+ if (!len)
+ return ERR_PTR(-EINVAL);
+
+ down_read(&dev->lock);
+ plist_for_each_entry(heap, &dev->heaps, node) {
+ /* if the caller didn't specify this heap id */
+ if (!((1 << heap->id) & heap_id_mask))
+ continue;
+ buffer = ion_buffer_create(heap, dev, len, align, flags);
+ if (!IS_ERR(buffer))
+ break;
+ }
+ up_read(&dev->lock);
+
+ if (buffer == NULL)
+ return ERR_PTR(-ENODEV);
+
+ if (IS_ERR(buffer))
+ return ERR_CAST(buffer);
+
+ handle = ion_handle_create(client, buffer);
+
+ /*
+ * ion_buffer_create will create a buffer with a ref_cnt of 1,
+ * and ion_handle_create will take a second reference, drop one here
+ */
+ ion_buffer_put(buffer);
+
+ if (IS_ERR(handle))
+ return handle;
+
+ mutex_lock(&client->lock);
+ ret = ion_handle_add(client, handle);
+ mutex_unlock(&client->lock);
+ if (ret) {
+ ion_handle_put(handle);
+ handle = ERR_PTR(ret);
+ }
+
+ return handle;
+}
+EXPORT_SYMBOL(ion_alloc);
+
+void ion_free(struct ion_client *client, struct ion_handle *handle)
+{
+ bool valid_handle;
+
+ BUG_ON(client != handle->client);
+
+ mutex_lock(&client->lock);
+ valid_handle = ion_handle_validate(client, handle);
+
+ if (!valid_handle) {
+ WARN(1, "%s: invalid handle passed to free.\n", __func__);
+ mutex_unlock(&client->lock);
+ return;
+ }
+ mutex_unlock(&client->lock);
+ ion_handle_put(handle);
+}
+EXPORT_SYMBOL(ion_free);
+
+int ion_phys(struct ion_client *client, struct ion_handle *handle,
+ ion_phys_addr_t *addr, size_t *len)
+{
+ struct ion_buffer *buffer;
+ int ret;
+
+ mutex_lock(&client->lock);
+ if (!ion_handle_validate(client, handle)) {
+ mutex_unlock(&client->lock);
+ return -EINVAL;
+ }
+
+ buffer = handle->buffer;
+
+ if (!buffer->heap->ops->phys) {
+ pr_err("%s: ion_phys is not implemented by this heap (name=%s, type=%d).\n",
+ __func__, buffer->heap->name, buffer->heap->type);
+ mutex_unlock(&client->lock);
+ return -ENODEV;
+ }
+ mutex_unlock(&client->lock);
+ ret = buffer->heap->ops->phys(buffer->heap, buffer, addr, len);
+ return ret;
+}
+EXPORT_SYMBOL(ion_phys);
+
+static void *ion_buffer_kmap_get(struct ion_buffer *buffer)
+{
+ void *vaddr;
+
+ if (buffer->kmap_cnt) {
+ buffer->kmap_cnt++;
+ return buffer->vaddr;
+ }
+ vaddr = buffer->heap->ops->map_kernel(buffer->heap, buffer);
+ if (WARN_ONCE(vaddr == NULL,
+ "heap->ops->map_kernel should return ERR_PTR on error"))
+ return ERR_PTR(-EINVAL);
+ if (IS_ERR(vaddr))
+ return vaddr;
+ buffer->vaddr = vaddr;
+ buffer->kmap_cnt++;
+ return vaddr;
+}
+
+static void *ion_handle_kmap_get(struct ion_handle *handle)
+{
+ struct ion_buffer *buffer = handle->buffer;
+ void *vaddr;
+
+ if (handle->kmap_cnt) {
+ handle->kmap_cnt++;
+ return buffer->vaddr;
+ }
+ vaddr = ion_buffer_kmap_get(buffer);
+ if (IS_ERR(vaddr))
+ return vaddr;
+ handle->kmap_cnt++;
+ return vaddr;
+}
+
+static void ion_buffer_kmap_put(struct ion_buffer *buffer)
+{
+ buffer->kmap_cnt--;
+ if (!buffer->kmap_cnt) {
+ buffer->heap->ops->unmap_kernel(buffer->heap, buffer);
+ buffer->vaddr = NULL;
+ }
+}
+
+static void ion_handle_kmap_put(struct ion_handle *handle)
+{
+ struct ion_buffer *buffer = handle->buffer;
+
+ if (!handle->kmap_cnt) {
+ WARN(1, "%s: Double unmap detected! bailing...\n", __func__);
+ return;
+ }
+ handle->kmap_cnt--;
+ if (!handle->kmap_cnt)
+ ion_buffer_kmap_put(buffer);
+}
+
+void *ion_map_kernel(struct ion_client *client, struct ion_handle *handle)
+{
+ struct ion_buffer *buffer;
+ void *vaddr;
+
+ mutex_lock(&client->lock);
+ if (!ion_handle_validate(client, handle)) {
+ pr_err("%s: invalid handle passed to map_kernel.\n",
+ __func__);
+ mutex_unlock(&client->lock);
+ return ERR_PTR(-EINVAL);
+ }
+
+ buffer = handle->buffer;
+
+ if (!handle->buffer->heap->ops->map_kernel) {
+ pr_err("%s: map_kernel is not implemented by this heap.\n",
+ __func__);
+ mutex_unlock(&client->lock);
+ return ERR_PTR(-ENODEV);
+ }
+
+ mutex_lock(&buffer->lock);
+ vaddr = ion_handle_kmap_get(handle);
+ mutex_unlock(&buffer->lock);
+ mutex_unlock(&client->lock);
+ return vaddr;
+}
+EXPORT_SYMBOL(ion_map_kernel);
+
+void ion_unmap_kernel(struct ion_client *client, struct ion_handle *handle)
+{
+ struct ion_buffer *buffer;
+
+ mutex_lock(&client->lock);
+ buffer = handle->buffer;
+ mutex_lock(&buffer->lock);
+ ion_handle_kmap_put(handle);
+ mutex_unlock(&buffer->lock);
+ mutex_unlock(&client->lock);
+}
+EXPORT_SYMBOL(ion_unmap_kernel);
+
+static int ion_debug_client_show(struct seq_file *s, void *unused)
+{
+ struct ion_client *client = s->private;
+ struct rb_node *n;
+ size_t sizes[ION_NUM_HEAP_IDS] = {0};
+ const char *names[ION_NUM_HEAP_IDS] = {NULL};
+ int i;
+
+ mutex_lock(&client->lock);
+ for (n = rb_first(&client->handles); n; n = rb_next(n)) {
+ struct ion_handle *handle = rb_entry(n, struct ion_handle,
+ node);
+ unsigned int id = handle->buffer->heap->id;
+
+ if (!names[id])
+ names[id] = handle->buffer->heap->name;
+ sizes[id] += handle->buffer->size;
+ }
+ mutex_unlock(&client->lock);
+
+ seq_printf(s, "%16.16s: %16.16s\n", "heap_name", "size_in_bytes");
+ for (i = 0; i < ION_NUM_HEAP_IDS; i++) {
+ if (!names[i])
+ continue;
+ seq_printf(s, "%16.16s: %16zu\n", names[i], sizes[i]);
+ }
+ return 0;
+}
+
+static int ion_debug_client_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, ion_debug_client_show, inode->i_private);
+}
+
+static const struct file_operations debug_client_fops = {
+ .open = ion_debug_client_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static int ion_get_client_serial(const struct rb_root *root,
+ const unsigned char *name)
+{
+ int serial = -1;
+ struct rb_node *node;
+
+ for (node = rb_first(root); node; node = rb_next(node)) {
+ struct ion_client *client = rb_entry(node, struct ion_client,
+ node);
+
+ if (strcmp(client->name, name))
+ continue;
+ serial = max(serial, client->display_serial);
+ }
+ return serial + 1;
+}
+
+struct ion_client *ion_client_create(struct ion_device *dev,
+ const char *name)
+{
+ struct ion_client *client;
+ struct task_struct *task;
+ struct rb_node **p;
+ struct rb_node *parent = NULL;
+ struct ion_client *entry;
+ pid_t pid;
+
+ if (!name) {
+ pr_err("%s: Name cannot be null\n", __func__);
+ return ERR_PTR(-EINVAL);
+ }
+
+ get_task_struct(current->group_leader);
+ task_lock(current->group_leader);
+ pid = task_pid_nr(current->group_leader);
+ /* don't bother to store task struct for kernel threads,
+ they can't be killed anyway */
+ if (current->group_leader->flags & PF_KTHREAD) {
+ put_task_struct(current->group_leader);
+ task = NULL;
+ } else {
+ task = current->group_leader;
+ }
+ task_unlock(current->group_leader);
+
+ client = kzalloc(sizeof(struct ion_client), GFP_KERNEL);
+ if (!client)
+ goto err_put_task_struct;
+
+ client->dev = dev;
+ client->handles = RB_ROOT;
+ idr_init(&client->idr);
+ mutex_init(&client->lock);
+ client->task = task;
+ client->pid = pid;
+ client->name = kstrdup(name, GFP_KERNEL);
+ if (!client->name)
+ goto err_free_client;
+
+ down_write(&dev->lock);
+ client->display_serial = ion_get_client_serial(&dev->clients, name);
+ client->display_name = kasprintf(
+ GFP_KERNEL, "%s-%d", name, client->display_serial);
+ if (!client->display_name) {
+ up_write(&dev->lock);
+ goto err_free_client_name;
+ }
+ p = &dev->clients.rb_node;
+ while (*p) {
+ parent = *p;
+ entry = rb_entry(parent, struct ion_client, node);
+
+ if (client < entry)
+ p = &(*p)->rb_left;
+ else if (client > entry)
+ p = &(*p)->rb_right;
+ }
+ rb_link_node(&client->node, parent, p);
+ rb_insert_color(&client->node, &dev->clients);
+
+ client->debug_root = debugfs_create_file(client->display_name, 0664,
+ dev->clients_debug_root,
+ client, &debug_client_fops);
+ if (!client->debug_root) {
+ char buf[256], *path;
+
+ path = dentry_path(dev->clients_debug_root, buf, 256);
+ pr_err("Failed to create client debugfs at %s/%s\n",
+ path, client->display_name);
+ }
+
+ up_write(&dev->lock);
+
+ return client;
+
+err_free_client_name:
+ kfree(client->name);
+err_free_client:
+ kfree(client);
+err_put_task_struct:
+ if (task)
+ put_task_struct(current->group_leader);
+ return ERR_PTR(-ENOMEM);
+}
+EXPORT_SYMBOL(ion_client_create);
+
+void ion_client_destroy(struct ion_client *client)
+{
+ struct ion_device *dev = client->dev;
+ struct rb_node *n;
+
+ pr_debug("%s: %d\n", __func__, __LINE__);
+ while ((n = rb_first(&client->handles))) {
+ struct ion_handle *handle = rb_entry(n, struct ion_handle,
+ node);
+ ion_handle_destroy(&handle->ref);
+ }
+
+ idr_destroy(&client->idr);
+
+ down_write(&dev->lock);
+ if (client->task)
+ put_task_struct(client->task);
+ rb_erase(&client->node, &dev->clients);
+ debugfs_remove_recursive(client->debug_root);
+ up_write(&dev->lock);
+
+ kfree(client->display_name);
+ kfree(client->name);
+ kfree(client);
+}
+EXPORT_SYMBOL(ion_client_destroy);
+
+struct sg_table *ion_sg_table(struct ion_client *client,
+ struct ion_handle *handle)
+{
+ struct ion_buffer *buffer;
+ struct sg_table *table;
+
+ mutex_lock(&client->lock);
+ if (!ion_handle_validate(client, handle)) {
+ pr_err("%s: invalid handle passed to map_dma.\n",
+ __func__);
+ mutex_unlock(&client->lock);
+ return ERR_PTR(-EINVAL);
+ }
+ buffer = handle->buffer;
+ table = buffer->sg_table;
+ mutex_unlock(&client->lock);
+ return table;
+}
+EXPORT_SYMBOL(ion_sg_table);
+
+static void ion_buffer_sync_for_device(struct ion_buffer *buffer,
+ struct device *dev,
+ enum dma_data_direction direction);
+
+static struct sg_table *ion_map_dma_buf(struct dma_buf_attachment *attachment,
+ enum dma_data_direction direction)
+{
+ struct dma_buf *dmabuf = attachment->dmabuf;
+ struct ion_buffer *buffer = dmabuf->priv;
+
+ ion_buffer_sync_for_device(buffer, attachment->dev, direction);
+ return buffer->sg_table;
+}
+
+static void ion_unmap_dma_buf(struct dma_buf_attachment *attachment,
+ struct sg_table *table,
+ enum dma_data_direction direction)
+{
+}
+
+void ion_pages_sync_for_device(struct device *dev, struct page *page,
+ size_t size, enum dma_data_direction dir)
+{
+ struct scatterlist sg;
+
+ sg_init_table(&sg, 1);
+ sg_set_page(&sg, page, size, 0);
+ /*
+ * This is not correct - sg_dma_address needs a dma_addr_t that is valid
+ * for the targeted device, but this works on the currently targeted
+ * hardware.
+ */
+ sg_dma_address(&sg) = page_to_phys(page);
+ dma_sync_sg_for_device(dev, &sg, 1, dir);
+}
+
+struct ion_vma_list {
+ struct list_head list;
+ struct vm_area_struct *vma;
+};
+
+static void ion_buffer_sync_for_device(struct ion_buffer *buffer,
+ struct device *dev,
+ enum dma_data_direction dir)
+{
+ struct ion_vma_list *vma_list;
+ int pages = PAGE_ALIGN(buffer->size) / PAGE_SIZE;
+ int i;
+
+ pr_debug("%s: syncing for device %s\n", __func__,
+ dev ? dev_name(dev) : "null");
+
+ if (!ion_buffer_fault_user_mappings(buffer))
+ return;
+
+ mutex_lock(&buffer->lock);
+ for (i = 0; i < pages; i++) {
+ struct page *page = buffer->pages[i];
+
+ if (ion_buffer_page_is_dirty(page))
+ ion_pages_sync_for_device(dev, ion_buffer_page(page),
+ PAGE_SIZE, dir);
+
+ ion_buffer_page_clean(buffer->pages + i);
+ }
+ list_for_each_entry(vma_list, &buffer->vmas, list) {
+ struct vm_area_struct *vma = vma_list->vma;
+
+ zap_page_range(vma, vma->vm_start, vma->vm_end - vma->vm_start,
+ NULL);
+ }
+ mutex_unlock(&buffer->lock);
+}
+
+static int ion_vm_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
+{
+ struct ion_buffer *buffer = vma->vm_private_data;
+ unsigned long pfn;
+ int ret;
+
+ mutex_lock(&buffer->lock);
+ ion_buffer_page_dirty(buffer->pages + vmf->pgoff);
+ BUG_ON(!buffer->pages || !buffer->pages[vmf->pgoff]);
+
+ pfn = page_to_pfn(ion_buffer_page(buffer->pages[vmf->pgoff]));
+ ret = vm_insert_pfn(vma, (unsigned long)vmf->virtual_address, pfn);
+ mutex_unlock(&buffer->lock);
+ if (ret)
+ return VM_FAULT_ERROR;
+
+ return VM_FAULT_NOPAGE;
+}
+
+static void ion_vm_open(struct vm_area_struct *vma)
+{
+ struct ion_buffer *buffer = vma->vm_private_data;
+ struct ion_vma_list *vma_list;
+
+ vma_list = kmalloc(sizeof(struct ion_vma_list), GFP_KERNEL);
+ if (!vma_list)
+ return;
+ vma_list->vma = vma;
+ mutex_lock(&buffer->lock);
+ list_add(&vma_list->list, &buffer->vmas);
+ mutex_unlock(&buffer->lock);
+ pr_debug("%s: adding %p\n", __func__, vma);
+}
+
+static void ion_vm_close(struct vm_area_struct *vma)
+{
+ struct ion_buffer *buffer = vma->vm_private_data;
+ struct ion_vma_list *vma_list, *tmp;
+
+ pr_debug("%s\n", __func__);
+ mutex_lock(&buffer->lock);
+ list_for_each_entry_safe(vma_list, tmp, &buffer->vmas, list) {
+ if (vma_list->vma != vma)
+ continue;
+ list_del(&vma_list->list);
+ kfree(vma_list);
+ pr_debug("%s: deleting %p\n", __func__, vma);
+ break;
+ }
+ mutex_unlock(&buffer->lock);
+}
+
+static struct vm_operations_struct ion_vma_ops = {
+ .open = ion_vm_open,
+ .close = ion_vm_close,
+ .fault = ion_vm_fault,
+};
+
+static int ion_mmap(struct dma_buf *dmabuf, struct vm_area_struct *vma)
+{
+ struct ion_buffer *buffer = dmabuf->priv;
+ int ret = 0;
+
+ if (!buffer->heap->ops->map_user) {
+ pr_err("%s: this heap does not define a method for mapping to userspace\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ if (ion_buffer_fault_user_mappings(buffer)) {
+ vma->vm_flags |= VM_IO | VM_PFNMAP | VM_DONTEXPAND |
+ VM_DONTDUMP;
+ vma->vm_private_data = buffer;
+ vma->vm_ops = &ion_vma_ops;
+ ion_vm_open(vma);
+ return 0;
+ }
+
+ if (!(buffer->flags & ION_FLAG_CACHED))
+ vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot);
+
+ mutex_lock(&buffer->lock);
+ /* now map it to userspace */
+ ret = buffer->heap->ops->map_user(buffer->heap, buffer, vma);
+ mutex_unlock(&buffer->lock);
+
+ if (ret)
+ pr_err("%s: failure mapping buffer to userspace\n",
+ __func__);
+
+ return ret;
+}
+
+static void ion_dma_buf_release(struct dma_buf *dmabuf)
+{
+ struct ion_buffer *buffer = dmabuf->priv;
+
+ ion_buffer_put(buffer);
+}
+
+static void *ion_dma_buf_kmap(struct dma_buf *dmabuf, unsigned long offset)
+{
+ struct ion_buffer *buffer = dmabuf->priv;
+
+ return buffer->vaddr + offset * PAGE_SIZE;
+}
+
+static void ion_dma_buf_kunmap(struct dma_buf *dmabuf, unsigned long offset,
+ void *ptr)
+{
+}
+
+static int ion_dma_buf_begin_cpu_access(struct dma_buf *dmabuf, size_t start,
+ size_t len,
+ enum dma_data_direction direction)
+{
+ struct ion_buffer *buffer = dmabuf->priv;
+ void *vaddr;
+
+ if (!buffer->heap->ops->map_kernel) {
+ pr_err("%s: map kernel is not implemented by this heap.\n",
+ __func__);
+ return -ENODEV;
+ }
+
+ mutex_lock(&buffer->lock);
+ vaddr = ion_buffer_kmap_get(buffer);
+ mutex_unlock(&buffer->lock);
+ return PTR_ERR_OR_ZERO(vaddr);
+}
+
+static void ion_dma_buf_end_cpu_access(struct dma_buf *dmabuf, size_t start,
+ size_t len,
+ enum dma_data_direction direction)
+{
+ struct ion_buffer *buffer = dmabuf->priv;
+
+ mutex_lock(&buffer->lock);
+ ion_buffer_kmap_put(buffer);
+ mutex_unlock(&buffer->lock);
+}
+
+static struct dma_buf_ops dma_buf_ops = {
+ .map_dma_buf = ion_map_dma_buf,
+ .unmap_dma_buf = ion_unmap_dma_buf,
+ .mmap = ion_mmap,
+ .release = ion_dma_buf_release,
+ .begin_cpu_access = ion_dma_buf_begin_cpu_access,
+ .end_cpu_access = ion_dma_buf_end_cpu_access,
+ .kmap_atomic = ion_dma_buf_kmap,
+ .kunmap_atomic = ion_dma_buf_kunmap,
+ .kmap = ion_dma_buf_kmap,
+ .kunmap = ion_dma_buf_kunmap,
+};
+
+struct dma_buf *ion_share_dma_buf(struct ion_client *client,
+ struct ion_handle *handle)
+{
+ struct ion_buffer *buffer;
+ struct dma_buf *dmabuf;
+ bool valid_handle;
+ DEFINE_DMA_BUF_EXPORT_INFO(exp_info);
+
+ mutex_lock(&client->lock);
+ valid_handle = ion_handle_validate(client, handle);
+ if (!valid_handle) {
+ WARN(1, "%s: invalid handle passed to share.\n", __func__);
+ mutex_unlock(&client->lock);
+ return ERR_PTR(-EINVAL);
+ }
+ buffer = handle->buffer;
+ ion_buffer_get(buffer);
+ mutex_unlock(&client->lock);
+
+ exp_info.ops = &dma_buf_ops;
+ exp_info.size = buffer->size;
+ exp_info.flags = O_RDWR;
+ exp_info.priv = buffer;
+
+ dmabuf = dma_buf_export(&exp_info);
+ if (IS_ERR(dmabuf)) {
+ ion_buffer_put(buffer);
+ return dmabuf;
+ }
+
+ return dmabuf;
+}
+EXPORT_SYMBOL(ion_share_dma_buf);
+
+int ion_share_dma_buf_fd(struct ion_client *client, struct ion_handle *handle)
+{
+ struct dma_buf *dmabuf;
+ int fd;
+
+ dmabuf = ion_share_dma_buf(client, handle);
+ if (IS_ERR(dmabuf))
+ return PTR_ERR(dmabuf);
+
+ fd = dma_buf_fd(dmabuf, O_CLOEXEC);
+ if (fd < 0)
+ dma_buf_put(dmabuf);
+
+ return fd;
+}
+EXPORT_SYMBOL(ion_share_dma_buf_fd);
+
+struct ion_handle *ion_import_dma_buf(struct ion_client *client, int fd)
+{
+ struct dma_buf *dmabuf;
+ struct ion_buffer *buffer;
+ struct ion_handle *handle;
+ int ret;
+
+ dmabuf = dma_buf_get(fd);
+ if (IS_ERR(dmabuf))
+ return ERR_CAST(dmabuf);
+ /* if this memory came from ion */
+
+ if (dmabuf->ops != &dma_buf_ops) {
+ pr_err("%s: can not import dmabuf from another exporter\n",
+ __func__);
+ dma_buf_put(dmabuf);
+ return ERR_PTR(-EINVAL);
+ }
+ buffer = dmabuf->priv;
+
+ mutex_lock(&client->lock);
+ /* if a handle exists for this buffer just take a reference to it */
+ handle = ion_handle_lookup(client, buffer);
+ if (!IS_ERR(handle)) {
+ ion_handle_get(handle);
+ mutex_unlock(&client->lock);
+ goto end;
+ }
+ mutex_unlock(&client->lock);
+
+ handle = ion_handle_create(client, buffer);
+ if (IS_ERR(handle))
+ goto end;
+
+ mutex_lock(&client->lock);
+ ret = ion_handle_add(client, handle);
+ mutex_unlock(&client->lock);
+ if (ret) {
+ ion_handle_put(handle);
+ handle = ERR_PTR(ret);
+ }
+
+end:
+ dma_buf_put(dmabuf);
+ return handle;
+}
+EXPORT_SYMBOL(ion_import_dma_buf);
+
+static int ion_sync_for_device(struct ion_client *client, int fd)
+{
+ struct dma_buf *dmabuf;
+ struct ion_buffer *buffer;
+
+ dmabuf = dma_buf_get(fd);
+ if (IS_ERR(dmabuf))
+ return PTR_ERR(dmabuf);
+
+ /* if this memory came from ion */
+ if (dmabuf->ops != &dma_buf_ops) {
+ pr_err("%s: can not sync dmabuf from another exporter\n",
+ __func__);
+ dma_buf_put(dmabuf);
+ return -EINVAL;
+ }
+ buffer = dmabuf->priv;
+
+ dma_sync_sg_for_device(NULL, buffer->sg_table->sgl,
+ buffer->sg_table->nents, DMA_BIDIRECTIONAL);
+ dma_buf_put(dmabuf);
+ return 0;
+}
+
+/* fix up the cases where the ioctl direction bits are incorrect */
+static unsigned int ion_ioctl_dir(unsigned int cmd)
+{
+ switch (cmd) {
+ case ION_IOC_SYNC:
+ case ION_IOC_FREE:
+ case ION_IOC_CUSTOM:
+ return _IOC_WRITE;
+ default:
+ return _IOC_DIR(cmd);
+ }
+}
+
+static long ion_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
+{
+ struct ion_client *client = filp->private_data;
+ struct ion_device *dev = client->dev;
+ struct ion_handle *cleanup_handle = NULL;
+ int ret = 0;
+ unsigned int dir;
+
+ union {
+ struct ion_fd_data fd;
+ struct ion_allocation_data allocation;
+ struct ion_handle_data handle;
+ struct ion_custom_data custom;
+ } data;
+
+ dir = ion_ioctl_dir(cmd);
+
+ if (_IOC_SIZE(cmd) > sizeof(data))
+ return -EINVAL;
+
+ if (dir & _IOC_WRITE)
+ if (copy_from_user(&data, (void __user *)arg, _IOC_SIZE(cmd)))
+ return -EFAULT;
+
+ switch (cmd) {
+ case ION_IOC_ALLOC:
+ {
+ struct ion_handle *handle;
+
+ handle = ion_alloc(client, data.allocation.len,
+ data.allocation.align,
+ data.allocation.heap_id_mask,
+ data.allocation.flags);
+ if (IS_ERR(handle))
+ return PTR_ERR(handle);
+
+ data.allocation.handle = handle->id;
+
+ cleanup_handle = handle;
+ break;
+ }
+ case ION_IOC_FREE:
+ {
+ struct ion_handle *handle;
+
+ handle = ion_handle_get_by_id(client, data.handle.handle);
+ if (IS_ERR(handle))
+ return PTR_ERR(handle);
+ ion_free(client, handle);
+ ion_handle_put(handle);
+ break;
+ }
+ case ION_IOC_SHARE:
+ case ION_IOC_MAP:
+ {
+ struct ion_handle *handle;
+
+ handle = ion_handle_get_by_id(client, data.handle.handle);
+ if (IS_ERR(handle))
+ return PTR_ERR(handle);
+ data.fd.fd = ion_share_dma_buf_fd(client, handle);
+ ion_handle_put(handle);
+ if (data.fd.fd < 0)
+ ret = data.fd.fd;
+ break;
+ }
+ case ION_IOC_IMPORT:
+ {
+ struct ion_handle *handle;
+
+ handle = ion_import_dma_buf(client, data.fd.fd);
+ if (IS_ERR(handle))
+ ret = PTR_ERR(handle);
+ else
+ data.handle.handle = handle->id;
+ break;
+ }
+ case ION_IOC_SYNC:
+ {
+ ret = ion_sync_for_device(client, data.fd.fd);
+ break;
+ }
+ case ION_IOC_CUSTOM:
+ {
+ if (!dev->custom_ioctl)
+ return -ENOTTY;
+ ret = dev->custom_ioctl(client, data.custom.cmd,
+ data.custom.arg);
+ break;
+ }
+ default:
+ return -ENOTTY;
+ }
+
+ if (dir & _IOC_READ) {
+ if (copy_to_user((void __user *)arg, &data, _IOC_SIZE(cmd))) {
+ if (cleanup_handle)
+ ion_free(client, cleanup_handle);
+ return -EFAULT;
+ }
+ }
+ return ret;
+}
+
+static int ion_release(struct inode *inode, struct file *file)
+{
+ struct ion_client *client = file->private_data;
+
+ pr_debug("%s: %d\n", __func__, __LINE__);
+ ion_client_destroy(client);
+ return 0;
+}
+
+static int ion_open(struct inode *inode, struct file *file)
+{
+ struct miscdevice *miscdev = file->private_data;
+ struct ion_device *dev = container_of(miscdev, struct ion_device, dev);
+ struct ion_client *client;
+ char debug_name[64];
+
+ pr_debug("%s: %d\n", __func__, __LINE__);
+ snprintf(debug_name, 64, "%u", task_pid_nr(current->group_leader));
+ client = ion_client_create(dev, debug_name);
+ if (IS_ERR(client))
+ return PTR_ERR(client);
+ file->private_data = client;
+
+ return 0;
+}
+
+static const struct file_operations ion_fops = {
+ .owner = THIS_MODULE,
+ .open = ion_open,
+ .release = ion_release,
+ .unlocked_ioctl = ion_ioctl,
+ .compat_ioctl = compat_ion_ioctl,
+};
+
+static size_t ion_debug_heap_total(struct ion_client *client,
+ unsigned int id)
+{
+ size_t size = 0;
+ struct rb_node *n;
+
+ mutex_lock(&client->lock);
+ for (n = rb_first(&client->handles); n; n = rb_next(n)) {
+ struct ion_handle *handle = rb_entry(n,
+ struct ion_handle,
+ node);
+ if (handle->buffer->heap->id == id)
+ size += handle->buffer->size;
+ }
+ mutex_unlock(&client->lock);
+ return size;
+}
+
+static int ion_debug_heap_show(struct seq_file *s, void *unused)
+{
+ struct ion_heap *heap = s->private;
+ struct ion_device *dev = heap->dev;
+ struct rb_node *n;
+ size_t total_size = 0;
+ size_t total_orphaned_size = 0;
+
+ seq_printf(s, "%16s %16s %16s\n", "client", "pid", "size");
+ seq_puts(s, "----------------------------------------------------\n");
+
+ for (n = rb_first(&dev->clients); n; n = rb_next(n)) {
+ struct ion_client *client = rb_entry(n, struct ion_client,
+ node);
+ size_t size = ion_debug_heap_total(client, heap->id);
+
+ if (!size)
+ continue;
+ if (client->task) {
+ char task_comm[TASK_COMM_LEN];
+
+ get_task_comm(task_comm, client->task);
+ seq_printf(s, "%16s %16u %16zu\n", task_comm,
+ client->pid, size);
+ } else {
+ seq_printf(s, "%16s %16u %16zu\n", client->name,
+ client->pid, size);
+ }
+ }
+ seq_puts(s, "----------------------------------------------------\n");
+ seq_puts(s, "orphaned allocations (info is from last known client):\n");
+ mutex_lock(&dev->buffer_lock);
+ for (n = rb_first(&dev->buffers); n; n = rb_next(n)) {
+ struct ion_buffer *buffer = rb_entry(n, struct ion_buffer,
+ node);
+ if (buffer->heap->id != heap->id)
+ continue;
+ total_size += buffer->size;
+ if (!buffer->handle_count) {
+ seq_printf(s, "%16s %16u %16zu %d %d\n",
+ buffer->task_comm, buffer->pid,
+ buffer->size, buffer->kmap_cnt,
+ atomic_read(&buffer->ref.refcount));
+ total_orphaned_size += buffer->size;
+ }
+ }
+ mutex_unlock(&dev->buffer_lock);
+ seq_puts(s, "----------------------------------------------------\n");
+ seq_printf(s, "%16s %16zu\n", "total orphaned",
+ total_orphaned_size);
+ seq_printf(s, "%16s %16zu\n", "total ", total_size);
+ if (heap->flags & ION_HEAP_FLAG_DEFER_FREE)
+ seq_printf(s, "%16s %16zu\n", "deferred free",
+ heap->free_list_size);
+ seq_puts(s, "----------------------------------------------------\n");
+
+ if (heap->debug_show)
+ heap->debug_show(heap, s, unused);
+
+ return 0;
+}
+
+static int ion_debug_heap_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, ion_debug_heap_show, inode->i_private);
+}
+
+static const struct file_operations debug_heap_fops = {
+ .open = ion_debug_heap_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+#ifdef DEBUG_HEAP_SHRINKER
+static int debug_shrink_set(void *data, u64 val)
+{
+ struct ion_heap *heap = data;
+ struct shrink_control sc;
+ int objs;
+
+ sc.gfp_mask = -1;
+ sc.nr_to_scan = 0;
+
+ if (!val)
+ return 0;
+
+ objs = heap->shrinker.shrink(&heap->shrinker, &sc);
+ sc.nr_to_scan = objs;
+
+ heap->shrinker.shrink(&heap->shrinker, &sc);
+ return 0;
+}
+
+static int debug_shrink_get(void *data, u64 *val)
+{
+ struct ion_heap *heap = data;
+ struct shrink_control sc;
+ int objs;
+
+ sc.gfp_mask = -1;
+ sc.nr_to_scan = 0;
+
+ objs = heap->shrinker.shrink(&heap->shrinker, &sc);
+ *val = objs;
+ return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(debug_shrink_fops, debug_shrink_get,
+ debug_shrink_set, "%llu\n");
+#endif
+
+void ion_device_add_heap(struct ion_device *dev, struct ion_heap *heap)
+{
+ struct dentry *debug_file;
+
+ if (!heap->ops->allocate || !heap->ops->free || !heap->ops->map_dma ||
+ !heap->ops->unmap_dma)
+ pr_err("%s: can not add heap with invalid ops struct.\n",
+ __func__);
+
+ spin_lock_init(&heap->free_lock);
+ heap->free_list_size = 0;
+
+ if (heap->flags & ION_HEAP_FLAG_DEFER_FREE)
+ ion_heap_init_deferred_free(heap);
+
+ if ((heap->flags & ION_HEAP_FLAG_DEFER_FREE) || heap->ops->shrink)
+ ion_heap_init_shrinker(heap);
+
+ heap->dev = dev;
+ down_write(&dev->lock);
+ /* use negative heap->id to reverse the priority -- when traversing
+ the list later attempt higher id numbers first */
+ plist_node_init(&heap->node, -heap->id);
+ plist_add(&heap->node, &dev->heaps);
+ debug_file = debugfs_create_file(heap->name, 0664,
+ dev->heaps_debug_root, heap,
+ &debug_heap_fops);
+
+ if (!debug_file) {
+ char buf[256], *path;
+
+ path = dentry_path(dev->heaps_debug_root, buf, 256);
+ pr_err("Failed to create heap debugfs at %s/%s\n",
+ path, heap->name);
+ }
+
+#ifdef DEBUG_HEAP_SHRINKER
+ if (heap->shrinker.shrink) {
+ char debug_name[64];
+
+ snprintf(debug_name, 64, "%s_shrink", heap->name);
+ debug_file = debugfs_create_file(
+ debug_name, 0644, dev->heaps_debug_root, heap,
+ &debug_shrink_fops);
+ if (!debug_file) {
+ char buf[256], *path;
+
+ path = dentry_path(dev->heaps_debug_root, buf, 256);
+ pr_err("Failed to create heap shrinker debugfs at %s/%s\n",
+ path, debug_name);
+ }
+ }
+#endif
+ up_write(&dev->lock);
+}
+
+struct ion_device *ion_device_create(long (*custom_ioctl)
+ (struct ion_client *client,
+ unsigned int cmd,
+ unsigned long arg))
+{
+ struct ion_device *idev;
+ int ret;
+
+ idev = kzalloc(sizeof(struct ion_device), GFP_KERNEL);
+ if (!idev)
+ return ERR_PTR(-ENOMEM);
+
+ idev->dev.minor = MISC_DYNAMIC_MINOR;
+ idev->dev.name = "ion";
+ idev->dev.fops = &ion_fops;
+ idev->dev.parent = NULL;
+ ret = misc_register(&idev->dev);
+ if (ret) {
+ pr_err("ion: failed to register misc device.\n");
+ return ERR_PTR(ret);
+ }
+
+ idev->debug_root = debugfs_create_dir("ion", NULL);
+ if (!idev->debug_root) {
+ pr_err("ion: failed to create debugfs root directory.\n");
+ goto debugfs_done;
+ }
+ idev->heaps_debug_root = debugfs_create_dir("heaps", idev->debug_root);
+ if (!idev->heaps_debug_root) {
+ pr_err("ion: failed to create debugfs heaps directory.\n");
+ goto debugfs_done;
+ }
+ idev->clients_debug_root = debugfs_create_dir("clients",
+ idev->debug_root);
+ if (!idev->clients_debug_root)
+ pr_err("ion: failed to create debugfs clients directory.\n");
+
+debugfs_done:
+
+ idev->custom_ioctl = custom_ioctl;
+ idev->buffers = RB_ROOT;
+ mutex_init(&idev->buffer_lock);
+ init_rwsem(&idev->lock);
+ plist_head_init(&idev->heaps);
+ idev->clients = RB_ROOT;
+ return idev;
+}
+
+void ion_device_destroy(struct ion_device *dev)
+{
+ misc_deregister(&dev->dev);
+ debugfs_remove_recursive(dev->debug_root);
+ /* XXX need to free the heaps and clients ? */
+ kfree(dev);
+}
+
+void __init ion_reserve(struct ion_platform_data *data)
+{
+ int i;
+
+ for (i = 0; i < data->nr; i++) {
+ if (data->heaps[i].size == 0)
+ continue;
+
+ if (data->heaps[i].base == 0) {
+ phys_addr_t paddr;
+
+ paddr = memblock_alloc_base(data->heaps[i].size,
+ data->heaps[i].align,
+ MEMBLOCK_ALLOC_ANYWHERE);
+ if (!paddr) {
+ pr_err("%s: error allocating memblock for heap %d\n",
+ __func__, i);
+ continue;
+ }
+ data->heaps[i].base = paddr;
+ } else {
+ int ret = memblock_reserve(data->heaps[i].base,
+ data->heaps[i].size);
+ if (ret)
+ pr_err("memblock reserve of %zx@%lx failed\n",
+ data->heaps[i].size,
+ data->heaps[i].base);
+ }
+ pr_info("%s: %s reserved base %lx size %zu\n", __func__,
+ data->heaps[i].name,
+ data->heaps[i].base,
+ data->heaps[i].size);
+ }
+}
diff --git a/drivers/staging/android/ion/ion.h b/drivers/staging/android/ion/ion.h
new file mode 100644
index 000000000..443db8459
--- /dev/null
+++ b/drivers/staging/android/ion/ion.h
@@ -0,0 +1,203 @@
+/*
+ * drivers/staging/android/ion/ion.h
+ *
+ * Copyright (C) 2011 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ */
+
+#ifndef _LINUX_ION_H
+#define _LINUX_ION_H
+
+#include <linux/types.h>
+
+#include "../uapi/ion.h"
+
+struct ion_handle;
+struct ion_device;
+struct ion_heap;
+struct ion_mapper;
+struct ion_client;
+struct ion_buffer;
+
+/* This should be removed some day when phys_addr_t's are fully
+ plumbed in the kernel, and all instances of ion_phys_addr_t should
+ be converted to phys_addr_t. For the time being many kernel interfaces
+ do not accept phys_addr_t's that would have to */
+#define ion_phys_addr_t unsigned long
+
+/**
+ * struct ion_platform_heap - defines a heap in the given platform
+ * @type: type of the heap from ion_heap_type enum
+ * @id: unique identifier for heap. When allocating higher numbers
+ * will be allocated from first. At allocation these are passed
+ * as a bit mask and therefore can not exceed ION_NUM_HEAP_IDS.
+ * @name: used for debug purposes
+ * @base: base address of heap in physical memory if applicable
+ * @size: size of the heap in bytes if applicable
+ * @align: required alignment in physical memory if applicable
+ * @priv: private info passed from the board file
+ *
+ * Provided by the board file.
+ */
+struct ion_platform_heap {
+ enum ion_heap_type type;
+ unsigned int id;
+ const char *name;
+ ion_phys_addr_t base;
+ size_t size;
+ ion_phys_addr_t align;
+ void *priv;
+};
+
+/**
+ * struct ion_platform_data - array of platform heaps passed from board file
+ * @nr: number of structures in the array
+ * @heaps: array of platform_heap structions
+ *
+ * Provided by the board file in the form of platform data to a platform device.
+ */
+struct ion_platform_data {
+ int nr;
+ struct ion_platform_heap *heaps;
+};
+
+/**
+ * ion_reserve() - reserve memory for ion heaps if applicable
+ * @data: platform data specifying starting physical address and
+ * size
+ *
+ * Calls memblock reserve to set aside memory for heaps that are
+ * located at specific memory addresses or of specific sizes not
+ * managed by the kernel
+ */
+void ion_reserve(struct ion_platform_data *data);
+
+/**
+ * ion_client_create() - allocate a client and returns it
+ * @dev: the global ion device
+ * @name: used for debugging
+ */
+struct ion_client *ion_client_create(struct ion_device *dev,
+ const char *name);
+
+/**
+ * ion_client_destroy() - free's a client and all it's handles
+ * @client: the client
+ *
+ * Free the provided client and all it's resources including
+ * any handles it is holding.
+ */
+void ion_client_destroy(struct ion_client *client);
+
+/**
+ * ion_alloc - allocate ion memory
+ * @client: the client
+ * @len: size of the allocation
+ * @align: requested allocation alignment, lots of hardware blocks
+ * have alignment requirements of some kind
+ * @heap_id_mask: mask of heaps to allocate from, if multiple bits are set
+ * heaps will be tried in order from highest to lowest
+ * id
+ * @flags: heap flags, the low 16 bits are consumed by ion, the
+ * high 16 bits are passed on to the respective heap and
+ * can be heap custom
+ *
+ * Allocate memory in one of the heaps provided in heap mask and return
+ * an opaque handle to it.
+ */
+struct ion_handle *ion_alloc(struct ion_client *client, size_t len,
+ size_t align, unsigned int heap_id_mask,
+ unsigned int flags);
+
+/**
+ * ion_free - free a handle
+ * @client: the client
+ * @handle: the handle to free
+ *
+ * Free the provided handle.
+ */
+void ion_free(struct ion_client *client, struct ion_handle *handle);
+
+/**
+ * ion_phys - returns the physical address and len of a handle
+ * @client: the client
+ * @handle: the handle
+ * @addr: a pointer to put the address in
+ * @len: a pointer to put the length in
+ *
+ * This function queries the heap for a particular handle to get the
+ * handle's physical address. It't output is only correct if
+ * a heap returns physically contiguous memory -- in other cases
+ * this api should not be implemented -- ion_sg_table should be used
+ * instead. Returns -EINVAL if the handle is invalid. This has
+ * no implications on the reference counting of the handle --
+ * the returned value may not be valid if the caller is not
+ * holding a reference.
+ */
+int ion_phys(struct ion_client *client, struct ion_handle *handle,
+ ion_phys_addr_t *addr, size_t *len);
+
+/**
+ * ion_map_dma - return an sg_table describing a handle
+ * @client: the client
+ * @handle: the handle
+ *
+ * This function returns the sg_table describing
+ * a particular ion handle.
+ */
+struct sg_table *ion_sg_table(struct ion_client *client,
+ struct ion_handle *handle);
+
+/**
+ * ion_map_kernel - create mapping for the given handle
+ * @client: the client
+ * @handle: handle to map
+ *
+ * Map the given handle into the kernel and return a kernel address that
+ * can be used to access this address.
+ */
+void *ion_map_kernel(struct ion_client *client, struct ion_handle *handle);
+
+/**
+ * ion_unmap_kernel() - destroy a kernel mapping for a handle
+ * @client: the client
+ * @handle: handle to unmap
+ */
+void ion_unmap_kernel(struct ion_client *client, struct ion_handle *handle);
+
+/**
+ * ion_share_dma_buf() - share buffer as dma-buf
+ * @client: the client
+ * @handle: the handle
+ */
+struct dma_buf *ion_share_dma_buf(struct ion_client *client,
+ struct ion_handle *handle);
+
+/**
+ * ion_share_dma_buf_fd() - given an ion client, create a dma-buf fd
+ * @client: the client
+ * @handle: the handle
+ */
+int ion_share_dma_buf_fd(struct ion_client *client, struct ion_handle *handle);
+
+/**
+ * ion_import_dma_buf() - given an dma-buf fd from the ion exporter get handle
+ * @client: the client
+ * @fd: the dma-buf fd
+ *
+ * Given an dma-buf fd that was allocated through ion via ion_share_dma_buf,
+ * import that fd and return a handle representing it. If a dma-buf from
+ * another exporter is passed in this function will return ERR_PTR(-EINVAL)
+ */
+struct ion_handle *ion_import_dma_buf(struct ion_client *client, int fd);
+
+#endif /* _LINUX_ION_H */
diff --git a/drivers/staging/android/ion/ion_carveout_heap.c b/drivers/staging/android/ion/ion_carveout_heap.c
new file mode 100644
index 000000000..9156d8238
--- /dev/null
+++ b/drivers/staging/android/ion/ion_carveout_heap.c
@@ -0,0 +1,193 @@
+/*
+ * drivers/staging/android/ion/ion_carveout_heap.c
+ *
+ * Copyright (C) 2011 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ */
+#include <linux/spinlock.h>
+#include <linux/dma-mapping.h>
+#include <linux/err.h>
+#include <linux/genalloc.h>
+#include <linux/io.h>
+#include <linux/mm.h>
+#include <linux/scatterlist.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+#include "ion.h"
+#include "ion_priv.h"
+
+struct ion_carveout_heap {
+ struct ion_heap heap;
+ struct gen_pool *pool;
+ ion_phys_addr_t base;
+};
+
+ion_phys_addr_t ion_carveout_allocate(struct ion_heap *heap,
+ unsigned long size,
+ unsigned long align)
+{
+ struct ion_carveout_heap *carveout_heap =
+ container_of(heap, struct ion_carveout_heap, heap);
+ unsigned long offset = gen_pool_alloc(carveout_heap->pool, size);
+
+ if (!offset)
+ return ION_CARVEOUT_ALLOCATE_FAIL;
+
+ return offset;
+}
+
+void ion_carveout_free(struct ion_heap *heap, ion_phys_addr_t addr,
+ unsigned long size)
+{
+ struct ion_carveout_heap *carveout_heap =
+ container_of(heap, struct ion_carveout_heap, heap);
+
+ if (addr == ION_CARVEOUT_ALLOCATE_FAIL)
+ return;
+ gen_pool_free(carveout_heap->pool, addr, size);
+}
+
+static int ion_carveout_heap_phys(struct ion_heap *heap,
+ struct ion_buffer *buffer,
+ ion_phys_addr_t *addr, size_t *len)
+{
+ struct sg_table *table = buffer->priv_virt;
+ struct page *page = sg_page(table->sgl);
+ ion_phys_addr_t paddr = PFN_PHYS(page_to_pfn(page));
+
+ *addr = paddr;
+ *len = buffer->size;
+ return 0;
+}
+
+static int ion_carveout_heap_allocate(struct ion_heap *heap,
+ struct ion_buffer *buffer,
+ unsigned long size, unsigned long align,
+ unsigned long flags)
+{
+ struct sg_table *table;
+ ion_phys_addr_t paddr;
+ int ret;
+
+ if (align > PAGE_SIZE)
+ return -EINVAL;
+
+ table = kmalloc(sizeof(struct sg_table), GFP_KERNEL);
+ if (!table)
+ return -ENOMEM;
+ ret = sg_alloc_table(table, 1, GFP_KERNEL);
+ if (ret)
+ goto err_free;
+
+ paddr = ion_carveout_allocate(heap, size, align);
+ if (paddr == ION_CARVEOUT_ALLOCATE_FAIL) {
+ ret = -ENOMEM;
+ goto err_free_table;
+ }
+
+ sg_set_page(table->sgl, pfn_to_page(PFN_DOWN(paddr)), size, 0);
+ buffer->priv_virt = table;
+
+ return 0;
+
+err_free_table:
+ sg_free_table(table);
+err_free:
+ kfree(table);
+ return ret;
+}
+
+static void ion_carveout_heap_free(struct ion_buffer *buffer)
+{
+ struct ion_heap *heap = buffer->heap;
+ struct sg_table *table = buffer->priv_virt;
+ struct page *page = sg_page(table->sgl);
+ ion_phys_addr_t paddr = PFN_PHYS(page_to_pfn(page));
+
+ ion_heap_buffer_zero(buffer);
+
+ if (ion_buffer_cached(buffer))
+ dma_sync_sg_for_device(NULL, table->sgl, table->nents,
+ DMA_BIDIRECTIONAL);
+
+ ion_carveout_free(heap, paddr, buffer->size);
+ sg_free_table(table);
+ kfree(table);
+}
+
+static struct sg_table *ion_carveout_heap_map_dma(struct ion_heap *heap,
+ struct ion_buffer *buffer)
+{
+ return buffer->priv_virt;
+}
+
+static void ion_carveout_heap_unmap_dma(struct ion_heap *heap,
+ struct ion_buffer *buffer)
+{
+}
+
+static struct ion_heap_ops carveout_heap_ops = {
+ .allocate = ion_carveout_heap_allocate,
+ .free = ion_carveout_heap_free,
+ .phys = ion_carveout_heap_phys,
+ .map_dma = ion_carveout_heap_map_dma,
+ .unmap_dma = ion_carveout_heap_unmap_dma,
+ .map_user = ion_heap_map_user,
+ .map_kernel = ion_heap_map_kernel,
+ .unmap_kernel = ion_heap_unmap_kernel,
+};
+
+struct ion_heap *ion_carveout_heap_create(struct ion_platform_heap *heap_data)
+{
+ struct ion_carveout_heap *carveout_heap;
+ int ret;
+
+ struct page *page;
+ size_t size;
+
+ page = pfn_to_page(PFN_DOWN(heap_data->base));
+ size = heap_data->size;
+
+ ion_pages_sync_for_device(NULL, page, size, DMA_BIDIRECTIONAL);
+
+ ret = ion_heap_pages_zero(page, size, pgprot_writecombine(PAGE_KERNEL));
+ if (ret)
+ return ERR_PTR(ret);
+
+ carveout_heap = kzalloc(sizeof(struct ion_carveout_heap), GFP_KERNEL);
+ if (!carveout_heap)
+ return ERR_PTR(-ENOMEM);
+
+ carveout_heap->pool = gen_pool_create(12, -1);
+ if (!carveout_heap->pool) {
+ kfree(carveout_heap);
+ return ERR_PTR(-ENOMEM);
+ }
+ carveout_heap->base = heap_data->base;
+ gen_pool_add(carveout_heap->pool, carveout_heap->base, heap_data->size,
+ -1);
+ carveout_heap->heap.ops = &carveout_heap_ops;
+ carveout_heap->heap.type = ION_HEAP_TYPE_CARVEOUT;
+ carveout_heap->heap.flags = ION_HEAP_FLAG_DEFER_FREE;
+
+ return &carveout_heap->heap;
+}
+
+void ion_carveout_heap_destroy(struct ion_heap *heap)
+{
+ struct ion_carveout_heap *carveout_heap =
+ container_of(heap, struct ion_carveout_heap, heap);
+
+ gen_pool_destroy(carveout_heap->pool);
+ kfree(carveout_heap);
+ carveout_heap = NULL;
+}
diff --git a/drivers/staging/android/ion/ion_chunk_heap.c b/drivers/staging/android/ion/ion_chunk_heap.c
new file mode 100644
index 000000000..3e6ec2ee6
--- /dev/null
+++ b/drivers/staging/android/ion/ion_chunk_heap.c
@@ -0,0 +1,194 @@
+/*
+ * drivers/staging/android/ion/ion_chunk_heap.c
+ *
+ * Copyright (C) 2012 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ */
+#include <linux/dma-mapping.h>
+#include <linux/err.h>
+#include <linux/genalloc.h>
+#include <linux/io.h>
+#include <linux/mm.h>
+#include <linux/scatterlist.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+#include "ion.h"
+#include "ion_priv.h"
+
+struct ion_chunk_heap {
+ struct ion_heap heap;
+ struct gen_pool *pool;
+ ion_phys_addr_t base;
+ unsigned long chunk_size;
+ unsigned long size;
+ unsigned long allocated;
+};
+
+static int ion_chunk_heap_allocate(struct ion_heap *heap,
+ struct ion_buffer *buffer,
+ unsigned long size, unsigned long align,
+ unsigned long flags)
+{
+ struct ion_chunk_heap *chunk_heap =
+ container_of(heap, struct ion_chunk_heap, heap);
+ struct sg_table *table;
+ struct scatterlist *sg;
+ int ret, i;
+ unsigned long num_chunks;
+ unsigned long allocated_size;
+
+ if (align > chunk_heap->chunk_size)
+ return -EINVAL;
+
+ allocated_size = ALIGN(size, chunk_heap->chunk_size);
+ num_chunks = allocated_size / chunk_heap->chunk_size;
+
+ if (allocated_size > chunk_heap->size - chunk_heap->allocated)
+ return -ENOMEM;
+
+ table = kmalloc(sizeof(struct sg_table), GFP_KERNEL);
+ if (!table)
+ return -ENOMEM;
+ ret = sg_alloc_table(table, num_chunks, GFP_KERNEL);
+ if (ret) {
+ kfree(table);
+ return ret;
+ }
+
+ sg = table->sgl;
+ for (i = 0; i < num_chunks; i++) {
+ unsigned long paddr = gen_pool_alloc(chunk_heap->pool,
+ chunk_heap->chunk_size);
+ if (!paddr)
+ goto err;
+ sg_set_page(sg, pfn_to_page(PFN_DOWN(paddr)),
+ chunk_heap->chunk_size, 0);
+ sg = sg_next(sg);
+ }
+
+ buffer->priv_virt = table;
+ chunk_heap->allocated += allocated_size;
+ return 0;
+err:
+ sg = table->sgl;
+ for (i -= 1; i >= 0; i--) {
+ gen_pool_free(chunk_heap->pool, page_to_phys(sg_page(sg)),
+ sg->length);
+ sg = sg_next(sg);
+ }
+ sg_free_table(table);
+ kfree(table);
+ return -ENOMEM;
+}
+
+static void ion_chunk_heap_free(struct ion_buffer *buffer)
+{
+ struct ion_heap *heap = buffer->heap;
+ struct ion_chunk_heap *chunk_heap =
+ container_of(heap, struct ion_chunk_heap, heap);
+ struct sg_table *table = buffer->priv_virt;
+ struct scatterlist *sg;
+ int i;
+ unsigned long allocated_size;
+
+ allocated_size = ALIGN(buffer->size, chunk_heap->chunk_size);
+
+ ion_heap_buffer_zero(buffer);
+
+ if (ion_buffer_cached(buffer))
+ dma_sync_sg_for_device(NULL, table->sgl, table->nents,
+ DMA_BIDIRECTIONAL);
+
+ for_each_sg(table->sgl, sg, table->nents, i) {
+ gen_pool_free(chunk_heap->pool, page_to_phys(sg_page(sg)),
+ sg->length);
+ }
+ chunk_heap->allocated -= allocated_size;
+ sg_free_table(table);
+ kfree(table);
+}
+
+static struct sg_table *ion_chunk_heap_map_dma(struct ion_heap *heap,
+ struct ion_buffer *buffer)
+{
+ return buffer->priv_virt;
+}
+
+static void ion_chunk_heap_unmap_dma(struct ion_heap *heap,
+ struct ion_buffer *buffer)
+{
+}
+
+static struct ion_heap_ops chunk_heap_ops = {
+ .allocate = ion_chunk_heap_allocate,
+ .free = ion_chunk_heap_free,
+ .map_dma = ion_chunk_heap_map_dma,
+ .unmap_dma = ion_chunk_heap_unmap_dma,
+ .map_user = ion_heap_map_user,
+ .map_kernel = ion_heap_map_kernel,
+ .unmap_kernel = ion_heap_unmap_kernel,
+};
+
+struct ion_heap *ion_chunk_heap_create(struct ion_platform_heap *heap_data)
+{
+ struct ion_chunk_heap *chunk_heap;
+ int ret;
+ struct page *page;
+ size_t size;
+
+ page = pfn_to_page(PFN_DOWN(heap_data->base));
+ size = heap_data->size;
+
+ ion_pages_sync_for_device(NULL, page, size, DMA_BIDIRECTIONAL);
+
+ ret = ion_heap_pages_zero(page, size, pgprot_writecombine(PAGE_KERNEL));
+ if (ret)
+ return ERR_PTR(ret);
+
+ chunk_heap = kzalloc(sizeof(struct ion_chunk_heap), GFP_KERNEL);
+ if (!chunk_heap)
+ return ERR_PTR(-ENOMEM);
+
+ chunk_heap->chunk_size = (unsigned long)heap_data->priv;
+ chunk_heap->pool = gen_pool_create(get_order(chunk_heap->chunk_size) +
+ PAGE_SHIFT, -1);
+ if (!chunk_heap->pool) {
+ ret = -ENOMEM;
+ goto error_gen_pool_create;
+ }
+ chunk_heap->base = heap_data->base;
+ chunk_heap->size = heap_data->size;
+ chunk_heap->allocated = 0;
+
+ gen_pool_add(chunk_heap->pool, chunk_heap->base, heap_data->size, -1);
+ chunk_heap->heap.ops = &chunk_heap_ops;
+ chunk_heap->heap.type = ION_HEAP_TYPE_CHUNK;
+ chunk_heap->heap.flags = ION_HEAP_FLAG_DEFER_FREE;
+ pr_info("%s: base %lu size %zu align %ld\n", __func__, chunk_heap->base,
+ heap_data->size, heap_data->align);
+
+ return &chunk_heap->heap;
+
+error_gen_pool_create:
+ kfree(chunk_heap);
+ return ERR_PTR(ret);
+}
+
+void ion_chunk_heap_destroy(struct ion_heap *heap)
+{
+ struct ion_chunk_heap *chunk_heap =
+ container_of(heap, struct ion_chunk_heap, heap);
+
+ gen_pool_destroy(chunk_heap->pool);
+ kfree(chunk_heap);
+ chunk_heap = NULL;
+}
diff --git a/drivers/staging/android/ion/ion_cma_heap.c b/drivers/staging/android/ion/ion_cma_heap.c
new file mode 100644
index 000000000..f4211f1be
--- /dev/null
+++ b/drivers/staging/android/ion/ion_cma_heap.c
@@ -0,0 +1,195 @@
+/*
+ * drivers/staging/android/ion/ion_cma_heap.c
+ *
+ * Copyright (C) Linaro 2012
+ * Author: <benjamin.gaignard@linaro.org> for ST-Ericsson.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ */
+
+#include <linux/device.h>
+#include <linux/slab.h>
+#include <linux/errno.h>
+#include <linux/err.h>
+#include <linux/dma-mapping.h>
+
+#include "ion.h"
+#include "ion_priv.h"
+
+#define ION_CMA_ALLOCATE_FAILED -1
+
+struct ion_cma_heap {
+ struct ion_heap heap;
+ struct device *dev;
+};
+
+#define to_cma_heap(x) container_of(x, struct ion_cma_heap, heap)
+
+struct ion_cma_buffer_info {
+ void *cpu_addr;
+ dma_addr_t handle;
+ struct sg_table *table;
+};
+
+
+/* ION CMA heap operations functions */
+static int ion_cma_allocate(struct ion_heap *heap, struct ion_buffer *buffer,
+ unsigned long len, unsigned long align,
+ unsigned long flags)
+{
+ struct ion_cma_heap *cma_heap = to_cma_heap(heap);
+ struct device *dev = cma_heap->dev;
+ struct ion_cma_buffer_info *info;
+
+ dev_dbg(dev, "Request buffer allocation len %ld\n", len);
+
+ if (buffer->flags & ION_FLAG_CACHED)
+ return -EINVAL;
+
+ if (align > PAGE_SIZE)
+ return -EINVAL;
+
+ info = kzalloc(sizeof(struct ion_cma_buffer_info), GFP_KERNEL);
+ if (!info)
+ return ION_CMA_ALLOCATE_FAILED;
+
+ info->cpu_addr = dma_alloc_coherent(dev, len, &(info->handle),
+ GFP_HIGHUSER | __GFP_ZERO);
+
+ if (!info->cpu_addr) {
+ dev_err(dev, "Fail to allocate buffer\n");
+ goto err;
+ }
+
+ info->table = kmalloc(sizeof(struct sg_table), GFP_KERNEL);
+ if (!info->table)
+ goto free_mem;
+
+ if (dma_common_get_sgtable
+ (dev, info->table, info->cpu_addr, info->handle, len))
+ goto free_table;
+ /* keep this for memory release */
+ buffer->priv_virt = info;
+ dev_dbg(dev, "Allocate buffer %p\n", buffer);
+ return 0;
+
+free_table:
+ kfree(info->table);
+free_mem:
+ dma_free_coherent(dev, len, info->cpu_addr, info->handle);
+err:
+ kfree(info);
+ return ION_CMA_ALLOCATE_FAILED;
+}
+
+static void ion_cma_free(struct ion_buffer *buffer)
+{
+ struct ion_cma_heap *cma_heap = to_cma_heap(buffer->heap);
+ struct device *dev = cma_heap->dev;
+ struct ion_cma_buffer_info *info = buffer->priv_virt;
+
+ dev_dbg(dev, "Release buffer %p\n", buffer);
+ /* release memory */
+ dma_free_coherent(dev, buffer->size, info->cpu_addr, info->handle);
+ /* release sg table */
+ sg_free_table(info->table);
+ kfree(info->table);
+ kfree(info);
+}
+
+/* return physical address in addr */
+static int ion_cma_phys(struct ion_heap *heap, struct ion_buffer *buffer,
+ ion_phys_addr_t *addr, size_t *len)
+{
+ struct ion_cma_heap *cma_heap = to_cma_heap(buffer->heap);
+ struct device *dev = cma_heap->dev;
+ struct ion_cma_buffer_info *info = buffer->priv_virt;
+
+ dev_dbg(dev, "Return buffer %p physical address %pa\n", buffer,
+ &info->handle);
+
+ *addr = info->handle;
+ *len = buffer->size;
+
+ return 0;
+}
+
+static struct sg_table *ion_cma_heap_map_dma(struct ion_heap *heap,
+ struct ion_buffer *buffer)
+{
+ struct ion_cma_buffer_info *info = buffer->priv_virt;
+
+ return info->table;
+}
+
+static void ion_cma_heap_unmap_dma(struct ion_heap *heap,
+ struct ion_buffer *buffer)
+{
+}
+
+static int ion_cma_mmap(struct ion_heap *mapper, struct ion_buffer *buffer,
+ struct vm_area_struct *vma)
+{
+ struct ion_cma_heap *cma_heap = to_cma_heap(buffer->heap);
+ struct device *dev = cma_heap->dev;
+ struct ion_cma_buffer_info *info = buffer->priv_virt;
+
+ return dma_mmap_coherent(dev, vma, info->cpu_addr, info->handle,
+ buffer->size);
+}
+
+static void *ion_cma_map_kernel(struct ion_heap *heap,
+ struct ion_buffer *buffer)
+{
+ struct ion_cma_buffer_info *info = buffer->priv_virt;
+ /* kernel memory mapping has been done at allocation time */
+ return info->cpu_addr;
+}
+
+static void ion_cma_unmap_kernel(struct ion_heap *heap,
+ struct ion_buffer *buffer)
+{
+}
+
+static struct ion_heap_ops ion_cma_ops = {
+ .allocate = ion_cma_allocate,
+ .free = ion_cma_free,
+ .map_dma = ion_cma_heap_map_dma,
+ .unmap_dma = ion_cma_heap_unmap_dma,
+ .phys = ion_cma_phys,
+ .map_user = ion_cma_mmap,
+ .map_kernel = ion_cma_map_kernel,
+ .unmap_kernel = ion_cma_unmap_kernel,
+};
+
+struct ion_heap *ion_cma_heap_create(struct ion_platform_heap *data)
+{
+ struct ion_cma_heap *cma_heap;
+
+ cma_heap = kzalloc(sizeof(struct ion_cma_heap), GFP_KERNEL);
+
+ if (!cma_heap)
+ return ERR_PTR(-ENOMEM);
+
+ cma_heap->heap.ops = &ion_cma_ops;
+ /* get device from private heaps data, later it will be
+ * used to make the link with reserved CMA memory */
+ cma_heap->dev = data->priv;
+ cma_heap->heap.type = ION_HEAP_TYPE_DMA;
+ return &cma_heap->heap;
+}
+
+void ion_cma_heap_destroy(struct ion_heap *heap)
+{
+ struct ion_cma_heap *cma_heap = to_cma_heap(heap);
+
+ kfree(cma_heap);
+}
diff --git a/drivers/staging/android/ion/ion_dummy_driver.c b/drivers/staging/android/ion/ion_dummy_driver.c
new file mode 100644
index 000000000..5678870bf
--- /dev/null
+++ b/drivers/staging/android/ion/ion_dummy_driver.c
@@ -0,0 +1,154 @@
+/*
+ * drivers/gpu/ion/ion_dummy_driver.c
+ *
+ * Copyright (C) 2013 Linaro, Inc
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ */
+
+#include <linux/err.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/bootmem.h>
+#include <linux/memblock.h>
+#include <linux/sizes.h>
+#include <linux/io.h>
+#include "ion.h"
+#include "ion_priv.h"
+
+static struct ion_device *idev;
+static struct ion_heap **heaps;
+
+static void *carveout_ptr;
+static void *chunk_ptr;
+
+static struct ion_platform_heap dummy_heaps[] = {
+ {
+ .id = ION_HEAP_TYPE_SYSTEM,
+ .type = ION_HEAP_TYPE_SYSTEM,
+ .name = "system",
+ },
+ {
+ .id = ION_HEAP_TYPE_SYSTEM_CONTIG,
+ .type = ION_HEAP_TYPE_SYSTEM_CONTIG,
+ .name = "system contig",
+ },
+ {
+ .id = ION_HEAP_TYPE_CARVEOUT,
+ .type = ION_HEAP_TYPE_CARVEOUT,
+ .name = "carveout",
+ .size = SZ_4M,
+ },
+ {
+ .id = ION_HEAP_TYPE_CHUNK,
+ .type = ION_HEAP_TYPE_CHUNK,
+ .name = "chunk",
+ .size = SZ_4M,
+ .align = SZ_16K,
+ .priv = (void *)(SZ_16K),
+ },
+};
+
+static struct ion_platform_data dummy_ion_pdata = {
+ .nr = ARRAY_SIZE(dummy_heaps),
+ .heaps = dummy_heaps,
+};
+
+static int __init ion_dummy_init(void)
+{
+ int i, err;
+
+ idev = ion_device_create(NULL);
+ heaps = kcalloc(dummy_ion_pdata.nr, sizeof(struct ion_heap *),
+ GFP_KERNEL);
+ if (!heaps)
+ return -ENOMEM;
+
+
+ /* Allocate a dummy carveout heap */
+ carveout_ptr = alloc_pages_exact(
+ dummy_heaps[ION_HEAP_TYPE_CARVEOUT].size,
+ GFP_KERNEL);
+ if (carveout_ptr)
+ dummy_heaps[ION_HEAP_TYPE_CARVEOUT].base =
+ virt_to_phys(carveout_ptr);
+ else
+ pr_err("ion_dummy: Could not allocate carveout\n");
+
+ /* Allocate a dummy chunk heap */
+ chunk_ptr = alloc_pages_exact(
+ dummy_heaps[ION_HEAP_TYPE_CHUNK].size,
+ GFP_KERNEL);
+ if (chunk_ptr)
+ dummy_heaps[ION_HEAP_TYPE_CHUNK].base = virt_to_phys(chunk_ptr);
+ else
+ pr_err("ion_dummy: Could not allocate chunk\n");
+
+ for (i = 0; i < dummy_ion_pdata.nr; i++) {
+ struct ion_platform_heap *heap_data = &dummy_ion_pdata.heaps[i];
+
+ if (heap_data->type == ION_HEAP_TYPE_CARVEOUT &&
+ !heap_data->base)
+ continue;
+
+ if (heap_data->type == ION_HEAP_TYPE_CHUNK && !heap_data->base)
+ continue;
+
+ heaps[i] = ion_heap_create(heap_data);
+ if (IS_ERR_OR_NULL(heaps[i])) {
+ err = PTR_ERR(heaps[i]);
+ goto err;
+ }
+ ion_device_add_heap(idev, heaps[i]);
+ }
+ return 0;
+err:
+ for (i = 0; i < dummy_ion_pdata.nr; ++i)
+ ion_heap_destroy(heaps[i]);
+ kfree(heaps);
+
+ if (carveout_ptr) {
+ free_pages_exact(carveout_ptr,
+ dummy_heaps[ION_HEAP_TYPE_CARVEOUT].size);
+ carveout_ptr = NULL;
+ }
+ if (chunk_ptr) {
+ free_pages_exact(chunk_ptr,
+ dummy_heaps[ION_HEAP_TYPE_CHUNK].size);
+ chunk_ptr = NULL;
+ }
+ return err;
+}
+device_initcall(ion_dummy_init);
+
+static void __exit ion_dummy_exit(void)
+{
+ int i;
+
+ ion_device_destroy(idev);
+
+ for (i = 0; i < dummy_ion_pdata.nr; i++)
+ ion_heap_destroy(heaps[i]);
+ kfree(heaps);
+
+ if (carveout_ptr) {
+ free_pages_exact(carveout_ptr,
+ dummy_heaps[ION_HEAP_TYPE_CARVEOUT].size);
+ carveout_ptr = NULL;
+ }
+ if (chunk_ptr) {
+ free_pages_exact(chunk_ptr,
+ dummy_heaps[ION_HEAP_TYPE_CHUNK].size);
+ chunk_ptr = NULL;
+ }
+}
+__exitcall(ion_dummy_exit);
diff --git a/drivers/staging/android/ion/ion_heap.c b/drivers/staging/android/ion/ion_heap.c
new file mode 100644
index 000000000..fd13d05b5
--- /dev/null
+++ b/drivers/staging/android/ion/ion_heap.c
@@ -0,0 +1,381 @@
+/*
+ * drivers/staging/android/ion/ion_heap.c
+ *
+ * Copyright (C) 2011 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ */
+
+#include <linux/err.h>
+#include <linux/freezer.h>
+#include <linux/kthread.h>
+#include <linux/mm.h>
+#include <linux/rtmutex.h>
+#include <linux/sched.h>
+#include <linux/scatterlist.h>
+#include <linux/vmalloc.h>
+#include "ion.h"
+#include "ion_priv.h"
+
+void *ion_heap_map_kernel(struct ion_heap *heap,
+ struct ion_buffer *buffer)
+{
+ struct scatterlist *sg;
+ int i, j;
+ void *vaddr;
+ pgprot_t pgprot;
+ struct sg_table *table = buffer->sg_table;
+ int npages = PAGE_ALIGN(buffer->size) / PAGE_SIZE;
+ struct page **pages = vmalloc(sizeof(struct page *) * npages);
+ struct page **tmp = pages;
+
+ if (!pages)
+ return NULL;
+
+ if (buffer->flags & ION_FLAG_CACHED)
+ pgprot = PAGE_KERNEL;
+ else
+ pgprot = pgprot_writecombine(PAGE_KERNEL);
+
+ for_each_sg(table->sgl, sg, table->nents, i) {
+ int npages_this_entry = PAGE_ALIGN(sg->length) / PAGE_SIZE;
+ struct page *page = sg_page(sg);
+
+ BUG_ON(i >= npages);
+ for (j = 0; j < npages_this_entry; j++)
+ *(tmp++) = page++;
+ }
+ vaddr = vmap(pages, npages, VM_MAP, pgprot);
+ vfree(pages);
+
+ if (vaddr == NULL)
+ return ERR_PTR(-ENOMEM);
+
+ return vaddr;
+}
+
+void ion_heap_unmap_kernel(struct ion_heap *heap,
+ struct ion_buffer *buffer)
+{
+ vunmap(buffer->vaddr);
+}
+
+int ion_heap_map_user(struct ion_heap *heap, struct ion_buffer *buffer,
+ struct vm_area_struct *vma)
+{
+ struct sg_table *table = buffer->sg_table;
+ unsigned long addr = vma->vm_start;
+ unsigned long offset = vma->vm_pgoff * PAGE_SIZE;
+ struct scatterlist *sg;
+ int i;
+ int ret;
+
+ for_each_sg(table->sgl, sg, table->nents, i) {
+ struct page *page = sg_page(sg);
+ unsigned long remainder = vma->vm_end - addr;
+ unsigned long len = sg->length;
+
+ if (offset >= sg->length) {
+ offset -= sg->length;
+ continue;
+ } else if (offset) {
+ page += offset / PAGE_SIZE;
+ len = sg->length - offset;
+ offset = 0;
+ }
+ len = min(len, remainder);
+ ret = remap_pfn_range(vma, addr, page_to_pfn(page), len,
+ vma->vm_page_prot);
+ if (ret)
+ return ret;
+ addr += len;
+ if (addr >= vma->vm_end)
+ return 0;
+ }
+ return 0;
+}
+
+static int ion_heap_clear_pages(struct page **pages, int num, pgprot_t pgprot)
+{
+ void *addr = vm_map_ram(pages, num, -1, pgprot);
+
+ if (!addr)
+ return -ENOMEM;
+ memset(addr, 0, PAGE_SIZE * num);
+ vm_unmap_ram(addr, num);
+
+ return 0;
+}
+
+static int ion_heap_sglist_zero(struct scatterlist *sgl, unsigned int nents,
+ pgprot_t pgprot)
+{
+ int p = 0;
+ int ret = 0;
+ struct sg_page_iter piter;
+ struct page *pages[32];
+
+ for_each_sg_page(sgl, &piter, nents, 0) {
+ pages[p++] = sg_page_iter_page(&piter);
+ if (p == ARRAY_SIZE(pages)) {
+ ret = ion_heap_clear_pages(pages, p, pgprot);
+ if (ret)
+ return ret;
+ p = 0;
+ }
+ }
+ if (p)
+ ret = ion_heap_clear_pages(pages, p, pgprot);
+
+ return ret;
+}
+
+int ion_heap_buffer_zero(struct ion_buffer *buffer)
+{
+ struct sg_table *table = buffer->sg_table;
+ pgprot_t pgprot;
+
+ if (buffer->flags & ION_FLAG_CACHED)
+ pgprot = PAGE_KERNEL;
+ else
+ pgprot = pgprot_writecombine(PAGE_KERNEL);
+
+ return ion_heap_sglist_zero(table->sgl, table->nents, pgprot);
+}
+
+int ion_heap_pages_zero(struct page *page, size_t size, pgprot_t pgprot)
+{
+ struct scatterlist sg;
+
+ sg_init_table(&sg, 1);
+ sg_set_page(&sg, page, size, 0);
+ return ion_heap_sglist_zero(&sg, 1, pgprot);
+}
+
+void ion_heap_freelist_add(struct ion_heap *heap, struct ion_buffer *buffer)
+{
+ spin_lock(&heap->free_lock);
+ list_add(&buffer->list, &heap->free_list);
+ heap->free_list_size += buffer->size;
+ spin_unlock(&heap->free_lock);
+ wake_up(&heap->waitqueue);
+}
+
+size_t ion_heap_freelist_size(struct ion_heap *heap)
+{
+ size_t size;
+
+ spin_lock(&heap->free_lock);
+ size = heap->free_list_size;
+ spin_unlock(&heap->free_lock);
+
+ return size;
+}
+
+static size_t _ion_heap_freelist_drain(struct ion_heap *heap, size_t size,
+ bool skip_pools)
+{
+ struct ion_buffer *buffer;
+ size_t total_drained = 0;
+
+ if (ion_heap_freelist_size(heap) == 0)
+ return 0;
+
+ spin_lock(&heap->free_lock);
+ if (size == 0)
+ size = heap->free_list_size;
+
+ while (!list_empty(&heap->free_list)) {
+ if (total_drained >= size)
+ break;
+ buffer = list_first_entry(&heap->free_list, struct ion_buffer,
+ list);
+ list_del(&buffer->list);
+ heap->free_list_size -= buffer->size;
+ if (skip_pools)
+ buffer->private_flags |= ION_PRIV_FLAG_SHRINKER_FREE;
+ total_drained += buffer->size;
+ spin_unlock(&heap->free_lock);
+ ion_buffer_destroy(buffer);
+ spin_lock(&heap->free_lock);
+ }
+ spin_unlock(&heap->free_lock);
+
+ return total_drained;
+}
+
+size_t ion_heap_freelist_drain(struct ion_heap *heap, size_t size)
+{
+ return _ion_heap_freelist_drain(heap, size, false);
+}
+
+size_t ion_heap_freelist_shrink(struct ion_heap *heap, size_t size)
+{
+ return _ion_heap_freelist_drain(heap, size, true);
+}
+
+static int ion_heap_deferred_free(void *data)
+{
+ struct ion_heap *heap = data;
+
+ while (true) {
+ struct ion_buffer *buffer;
+
+ wait_event_freezable(heap->waitqueue,
+ ion_heap_freelist_size(heap) > 0);
+
+ spin_lock(&heap->free_lock);
+ if (list_empty(&heap->free_list)) {
+ spin_unlock(&heap->free_lock);
+ continue;
+ }
+ buffer = list_first_entry(&heap->free_list, struct ion_buffer,
+ list);
+ list_del(&buffer->list);
+ heap->free_list_size -= buffer->size;
+ spin_unlock(&heap->free_lock);
+ ion_buffer_destroy(buffer);
+ }
+
+ return 0;
+}
+
+int ion_heap_init_deferred_free(struct ion_heap *heap)
+{
+ struct sched_param param = { .sched_priority = 0 };
+
+ INIT_LIST_HEAD(&heap->free_list);
+ init_waitqueue_head(&heap->waitqueue);
+ heap->task = kthread_run(ion_heap_deferred_free, heap,
+ "%s", heap->name);
+ if (IS_ERR(heap->task)) {
+ pr_err("%s: creating thread for deferred free failed\n",
+ __func__);
+ return PTR_ERR_OR_ZERO(heap->task);
+ }
+ sched_setscheduler(heap->task, SCHED_IDLE, &param);
+ return 0;
+}
+
+static unsigned long ion_heap_shrink_count(struct shrinker *shrinker,
+ struct shrink_control *sc)
+{
+ struct ion_heap *heap = container_of(shrinker, struct ion_heap,
+ shrinker);
+ int total = 0;
+
+ total = ion_heap_freelist_size(heap) / PAGE_SIZE;
+ if (heap->ops->shrink)
+ total += heap->ops->shrink(heap, sc->gfp_mask, 0);
+ return total;
+}
+
+static unsigned long ion_heap_shrink_scan(struct shrinker *shrinker,
+ struct shrink_control *sc)
+{
+ struct ion_heap *heap = container_of(shrinker, struct ion_heap,
+ shrinker);
+ int freed = 0;
+ int to_scan = sc->nr_to_scan;
+
+ if (to_scan == 0)
+ return 0;
+
+ /*
+ * shrink the free list first, no point in zeroing the memory if we're
+ * just going to reclaim it. Also, skip any possible page pooling.
+ */
+ if (heap->flags & ION_HEAP_FLAG_DEFER_FREE)
+ freed = ion_heap_freelist_shrink(heap, to_scan * PAGE_SIZE) /
+ PAGE_SIZE;
+
+ to_scan -= freed;
+ if (to_scan <= 0)
+ return freed;
+
+ if (heap->ops->shrink)
+ freed += heap->ops->shrink(heap, sc->gfp_mask, to_scan);
+ return freed;
+}
+
+void ion_heap_init_shrinker(struct ion_heap *heap)
+{
+ heap->shrinker.count_objects = ion_heap_shrink_count;
+ heap->shrinker.scan_objects = ion_heap_shrink_scan;
+ heap->shrinker.seeks = DEFAULT_SEEKS;
+ heap->shrinker.batch = 0;
+ register_shrinker(&heap->shrinker);
+}
+
+struct ion_heap *ion_heap_create(struct ion_platform_heap *heap_data)
+{
+ struct ion_heap *heap = NULL;
+
+ switch (heap_data->type) {
+ case ION_HEAP_TYPE_SYSTEM_CONTIG:
+ heap = ion_system_contig_heap_create(heap_data);
+ break;
+ case ION_HEAP_TYPE_SYSTEM:
+ heap = ion_system_heap_create(heap_data);
+ break;
+ case ION_HEAP_TYPE_CARVEOUT:
+ heap = ion_carveout_heap_create(heap_data);
+ break;
+ case ION_HEAP_TYPE_CHUNK:
+ heap = ion_chunk_heap_create(heap_data);
+ break;
+ case ION_HEAP_TYPE_DMA:
+ heap = ion_cma_heap_create(heap_data);
+ break;
+ default:
+ pr_err("%s: Invalid heap type %d\n", __func__,
+ heap_data->type);
+ return ERR_PTR(-EINVAL);
+ }
+
+ if (IS_ERR_OR_NULL(heap)) {
+ pr_err("%s: error creating heap %s type %d base %lu size %zu\n",
+ __func__, heap_data->name, heap_data->type,
+ heap_data->base, heap_data->size);
+ return ERR_PTR(-EINVAL);
+ }
+
+ heap->name = heap_data->name;
+ heap->id = heap_data->id;
+ return heap;
+}
+
+void ion_heap_destroy(struct ion_heap *heap)
+{
+ if (!heap)
+ return;
+
+ switch (heap->type) {
+ case ION_HEAP_TYPE_SYSTEM_CONTIG:
+ ion_system_contig_heap_destroy(heap);
+ break;
+ case ION_HEAP_TYPE_SYSTEM:
+ ion_system_heap_destroy(heap);
+ break;
+ case ION_HEAP_TYPE_CARVEOUT:
+ ion_carveout_heap_destroy(heap);
+ break;
+ case ION_HEAP_TYPE_CHUNK:
+ ion_chunk_heap_destroy(heap);
+ break;
+ case ION_HEAP_TYPE_DMA:
+ ion_cma_heap_destroy(heap);
+ break;
+ default:
+ pr_err("%s: Invalid heap type %d\n", __func__,
+ heap->type);
+ }
+}
diff --git a/drivers/staging/android/ion/ion_page_pool.c b/drivers/staging/android/ion/ion_page_pool.c
new file mode 100644
index 000000000..4b88f11e5
--- /dev/null
+++ b/drivers/staging/android/ion/ion_page_pool.c
@@ -0,0 +1,182 @@
+/*
+ * drivers/staging/android/ion/ion_mem_pool.c
+ *
+ * Copyright (C) 2011 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ */
+
+#include <linux/debugfs.h>
+#include <linux/dma-mapping.h>
+#include <linux/err.h>
+#include <linux/fs.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/swap.h>
+#include "ion_priv.h"
+
+static void *ion_page_pool_alloc_pages(struct ion_page_pool *pool)
+{
+ struct page *page = alloc_pages(pool->gfp_mask, pool->order);
+
+ if (!page)
+ return NULL;
+ ion_pages_sync_for_device(NULL, page, PAGE_SIZE << pool->order,
+ DMA_BIDIRECTIONAL);
+ return page;
+}
+
+static void ion_page_pool_free_pages(struct ion_page_pool *pool,
+ struct page *page)
+{
+ __free_pages(page, pool->order);
+}
+
+static int ion_page_pool_add(struct ion_page_pool *pool, struct page *page)
+{
+ mutex_lock(&pool->mutex);
+ if (PageHighMem(page)) {
+ list_add_tail(&page->lru, &pool->high_items);
+ pool->high_count++;
+ } else {
+ list_add_tail(&page->lru, &pool->low_items);
+ pool->low_count++;
+ }
+ mutex_unlock(&pool->mutex);
+ return 0;
+}
+
+static struct page *ion_page_pool_remove(struct ion_page_pool *pool, bool high)
+{
+ struct page *page;
+
+ if (high) {
+ BUG_ON(!pool->high_count);
+ page = list_first_entry(&pool->high_items, struct page, lru);
+ pool->high_count--;
+ } else {
+ BUG_ON(!pool->low_count);
+ page = list_first_entry(&pool->low_items, struct page, lru);
+ pool->low_count--;
+ }
+
+ list_del(&page->lru);
+ return page;
+}
+
+struct page *ion_page_pool_alloc(struct ion_page_pool *pool)
+{
+ struct page *page = NULL;
+
+ BUG_ON(!pool);
+
+ mutex_lock(&pool->mutex);
+ if (pool->high_count)
+ page = ion_page_pool_remove(pool, true);
+ else if (pool->low_count)
+ page = ion_page_pool_remove(pool, false);
+ mutex_unlock(&pool->mutex);
+
+ if (!page)
+ page = ion_page_pool_alloc_pages(pool);
+
+ return page;
+}
+
+void ion_page_pool_free(struct ion_page_pool *pool, struct page *page)
+{
+ int ret;
+
+ BUG_ON(pool->order != compound_order(page));
+
+ ret = ion_page_pool_add(pool, page);
+ if (ret)
+ ion_page_pool_free_pages(pool, page);
+}
+
+static int ion_page_pool_total(struct ion_page_pool *pool, bool high)
+{
+ int count = pool->low_count;
+
+ if (high)
+ count += pool->high_count;
+
+ return count << pool->order;
+}
+
+int ion_page_pool_shrink(struct ion_page_pool *pool, gfp_t gfp_mask,
+ int nr_to_scan)
+{
+ int freed;
+ bool high;
+
+ if (current_is_kswapd())
+ high = true;
+ else
+ high = !!(gfp_mask & __GFP_HIGHMEM);
+
+ if (nr_to_scan == 0)
+ return ion_page_pool_total(pool, high);
+
+ for (freed = 0; freed < nr_to_scan; freed++) {
+ struct page *page;
+
+ mutex_lock(&pool->mutex);
+ if (pool->low_count) {
+ page = ion_page_pool_remove(pool, false);
+ } else if (high && pool->high_count) {
+ page = ion_page_pool_remove(pool, true);
+ } else {
+ mutex_unlock(&pool->mutex);
+ break;
+ }
+ mutex_unlock(&pool->mutex);
+ ion_page_pool_free_pages(pool, page);
+ }
+
+ return freed;
+}
+
+struct ion_page_pool *ion_page_pool_create(gfp_t gfp_mask, unsigned int order)
+{
+ struct ion_page_pool *pool = kmalloc(sizeof(struct ion_page_pool),
+ GFP_KERNEL);
+ if (!pool)
+ return NULL;
+ pool->high_count = 0;
+ pool->low_count = 0;
+ INIT_LIST_HEAD(&pool->low_items);
+ INIT_LIST_HEAD(&pool->high_items);
+ pool->gfp_mask = gfp_mask | __GFP_COMP;
+ pool->order = order;
+ mutex_init(&pool->mutex);
+ plist_node_init(&pool->list, order);
+
+ return pool;
+}
+
+void ion_page_pool_destroy(struct ion_page_pool *pool)
+{
+ kfree(pool);
+}
+
+static int __init ion_page_pool_init(void)
+{
+ return 0;
+}
+
+static void __exit ion_page_pool_exit(void)
+{
+}
+
+module_init(ion_page_pool_init);
+module_exit(ion_page_pool_exit);
diff --git a/drivers/staging/android/ion/ion_priv.h b/drivers/staging/android/ion/ion_priv.h
new file mode 100644
index 000000000..18a5f93e1
--- /dev/null
+++ b/drivers/staging/android/ion/ion_priv.h
@@ -0,0 +1,405 @@
+/*
+ * drivers/staging/android/ion/ion_priv.h
+ *
+ * Copyright (C) 2011 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ */
+
+#ifndef _ION_PRIV_H
+#define _ION_PRIV_H
+
+#include <linux/device.h>
+#include <linux/dma-direction.h>
+#include <linux/kref.h>
+#include <linux/mm_types.h>
+#include <linux/mutex.h>
+#include <linux/rbtree.h>
+#include <linux/sched.h>
+#include <linux/shrinker.h>
+#include <linux/types.h>
+
+#include "ion.h"
+
+struct ion_buffer *ion_handle_buffer(struct ion_handle *handle);
+
+/**
+ * struct ion_buffer - metadata for a particular buffer
+ * @ref: refernce count
+ * @node: node in the ion_device buffers tree
+ * @dev: back pointer to the ion_device
+ * @heap: back pointer to the heap the buffer came from
+ * @flags: buffer specific flags
+ * @private_flags: internal buffer specific flags
+ * @size: size of the buffer
+ * @priv_virt: private data to the buffer representable as
+ * a void *
+ * @priv_phys: private data to the buffer representable as
+ * an ion_phys_addr_t (and someday a phys_addr_t)
+ * @lock: protects the buffers cnt fields
+ * @kmap_cnt: number of times the buffer is mapped to the kernel
+ * @vaddr: the kenrel mapping if kmap_cnt is not zero
+ * @dmap_cnt: number of times the buffer is mapped for dma
+ * @sg_table: the sg table for the buffer if dmap_cnt is not zero
+ * @pages: flat array of pages in the buffer -- used by fault
+ * handler and only valid for buffers that are faulted in
+ * @vmas: list of vma's mapping this buffer
+ * @handle_count: count of handles referencing this buffer
+ * @task_comm: taskcomm of last client to reference this buffer in a
+ * handle, used for debugging
+ * @pid: pid of last client to reference this buffer in a
+ * handle, used for debugging
+*/
+struct ion_buffer {
+ struct kref ref;
+ union {
+ struct rb_node node;
+ struct list_head list;
+ };
+ struct ion_device *dev;
+ struct ion_heap *heap;
+ unsigned long flags;
+ unsigned long private_flags;
+ size_t size;
+ union {
+ void *priv_virt;
+ ion_phys_addr_t priv_phys;
+ };
+ struct mutex lock;
+ int kmap_cnt;
+ void *vaddr;
+ int dmap_cnt;
+ struct sg_table *sg_table;
+ struct page **pages;
+ struct list_head vmas;
+ /* used to track orphaned buffers */
+ int handle_count;
+ char task_comm[TASK_COMM_LEN];
+ pid_t pid;
+};
+void ion_buffer_destroy(struct ion_buffer *buffer);
+
+/**
+ * struct ion_heap_ops - ops to operate on a given heap
+ * @allocate: allocate memory
+ * @free: free memory
+ * @phys get physical address of a buffer (only define on
+ * physically contiguous heaps)
+ * @map_dma map the memory for dma to a scatterlist
+ * @unmap_dma unmap the memory for dma
+ * @map_kernel map memory to the kernel
+ * @unmap_kernel unmap memory to the kernel
+ * @map_user map memory to userspace
+ *
+ * allocate, phys, and map_user return 0 on success, -errno on error.
+ * map_dma and map_kernel return pointer on success, ERR_PTR on
+ * error. @free will be called with ION_PRIV_FLAG_SHRINKER_FREE set in
+ * the buffer's private_flags when called from a shrinker. In that
+ * case, the pages being free'd must be truly free'd back to the
+ * system, not put in a page pool or otherwise cached.
+ */
+struct ion_heap_ops {
+ int (*allocate)(struct ion_heap *heap,
+ struct ion_buffer *buffer, unsigned long len,
+ unsigned long align, unsigned long flags);
+ void (*free)(struct ion_buffer *buffer);
+ int (*phys)(struct ion_heap *heap, struct ion_buffer *buffer,
+ ion_phys_addr_t *addr, size_t *len);
+ struct sg_table * (*map_dma)(struct ion_heap *heap,
+ struct ion_buffer *buffer);
+ void (*unmap_dma)(struct ion_heap *heap, struct ion_buffer *buffer);
+ void * (*map_kernel)(struct ion_heap *heap, struct ion_buffer *buffer);
+ void (*unmap_kernel)(struct ion_heap *heap, struct ion_buffer *buffer);
+ int (*map_user)(struct ion_heap *mapper, struct ion_buffer *buffer,
+ struct vm_area_struct *vma);
+ int (*shrink)(struct ion_heap *heap, gfp_t gfp_mask, int nr_to_scan);
+};
+
+/**
+ * heap flags - flags between the heaps and core ion code
+ */
+#define ION_HEAP_FLAG_DEFER_FREE (1 << 0)
+
+/**
+ * private flags - flags internal to ion
+ */
+/*
+ * Buffer is being freed from a shrinker function. Skip any possible
+ * heap-specific caching mechanism (e.g. page pools). Guarantees that
+ * any buffer storage that came from the system allocator will be
+ * returned to the system allocator.
+ */
+#define ION_PRIV_FLAG_SHRINKER_FREE (1 << 0)
+
+/**
+ * struct ion_heap - represents a heap in the system
+ * @node: rb node to put the heap on the device's tree of heaps
+ * @dev: back pointer to the ion_device
+ * @type: type of heap
+ * @ops: ops struct as above
+ * @flags: flags
+ * @id: id of heap, also indicates priority of this heap when
+ * allocating. These are specified by platform data and
+ * MUST be unique
+ * @name: used for debugging
+ * @shrinker: a shrinker for the heap
+ * @free_list: free list head if deferred free is used
+ * @free_list_size size of the deferred free list in bytes
+ * @lock: protects the free list
+ * @waitqueue: queue to wait on from deferred free thread
+ * @task: task struct of deferred free thread
+ * @debug_show: called when heap debug file is read to add any
+ * heap specific debug info to output
+ *
+ * Represents a pool of memory from which buffers can be made. In some
+ * systems the only heap is regular system memory allocated via vmalloc.
+ * On others, some blocks might require large physically contiguous buffers
+ * that are allocated from a specially reserved heap.
+ */
+struct ion_heap {
+ struct plist_node node;
+ struct ion_device *dev;
+ enum ion_heap_type type;
+ struct ion_heap_ops *ops;
+ unsigned long flags;
+ unsigned int id;
+ const char *name;
+ struct shrinker shrinker;
+ struct list_head free_list;
+ size_t free_list_size;
+ spinlock_t free_lock;
+ wait_queue_head_t waitqueue;
+ struct task_struct *task;
+
+ int (*debug_show)(struct ion_heap *heap, struct seq_file *, void *);
+};
+
+/**
+ * ion_buffer_cached - this ion buffer is cached
+ * @buffer: buffer
+ *
+ * indicates whether this ion buffer is cached
+ */
+bool ion_buffer_cached(struct ion_buffer *buffer);
+
+/**
+ * ion_buffer_fault_user_mappings - fault in user mappings of this buffer
+ * @buffer: buffer
+ *
+ * indicates whether userspace mappings of this buffer will be faulted
+ * in, this can affect how buffers are allocated from the heap.
+ */
+bool ion_buffer_fault_user_mappings(struct ion_buffer *buffer);
+
+/**
+ * ion_device_create - allocates and returns an ion device
+ * @custom_ioctl: arch specific ioctl function if applicable
+ *
+ * returns a valid device or -PTR_ERR
+ */
+struct ion_device *ion_device_create(long (*custom_ioctl)
+ (struct ion_client *client,
+ unsigned int cmd,
+ unsigned long arg));
+
+/**
+ * ion_device_destroy - free and device and it's resource
+ * @dev: the device
+ */
+void ion_device_destroy(struct ion_device *dev);
+
+/**
+ * ion_device_add_heap - adds a heap to the ion device
+ * @dev: the device
+ * @heap: the heap to add
+ */
+void ion_device_add_heap(struct ion_device *dev, struct ion_heap *heap);
+
+/**
+ * some helpers for common operations on buffers using the sg_table
+ * and vaddr fields
+ */
+void *ion_heap_map_kernel(struct ion_heap *, struct ion_buffer *);
+void ion_heap_unmap_kernel(struct ion_heap *, struct ion_buffer *);
+int ion_heap_map_user(struct ion_heap *, struct ion_buffer *,
+ struct vm_area_struct *);
+int ion_heap_buffer_zero(struct ion_buffer *buffer);
+int ion_heap_pages_zero(struct page *page, size_t size, pgprot_t pgprot);
+
+/**
+ * ion_heap_init_shrinker
+ * @heap: the heap
+ *
+ * If a heap sets the ION_HEAP_FLAG_DEFER_FREE flag or defines the shrink op
+ * this function will be called to setup a shrinker to shrink the freelists
+ * and call the heap's shrink op.
+ */
+void ion_heap_init_shrinker(struct ion_heap *heap);
+
+/**
+ * ion_heap_init_deferred_free -- initialize deferred free functionality
+ * @heap: the heap
+ *
+ * If a heap sets the ION_HEAP_FLAG_DEFER_FREE flag this function will
+ * be called to setup deferred frees. Calls to free the buffer will
+ * return immediately and the actual free will occur some time later
+ */
+int ion_heap_init_deferred_free(struct ion_heap *heap);
+
+/**
+ * ion_heap_freelist_add - add a buffer to the deferred free list
+ * @heap: the heap
+ * @buffer: the buffer
+ *
+ * Adds an item to the deferred freelist.
+ */
+void ion_heap_freelist_add(struct ion_heap *heap, struct ion_buffer *buffer);
+
+/**
+ * ion_heap_freelist_drain - drain the deferred free list
+ * @heap: the heap
+ * @size: ammount of memory to drain in bytes
+ *
+ * Drains the indicated amount of memory from the deferred freelist immediately.
+ * Returns the total amount freed. The total freed may be higher depending
+ * on the size of the items in the list, or lower if there is insufficient
+ * total memory on the freelist.
+ */
+size_t ion_heap_freelist_drain(struct ion_heap *heap, size_t size);
+
+/**
+ * ion_heap_freelist_shrink - drain the deferred free
+ * list, skipping any heap-specific
+ * pooling or caching mechanisms
+ *
+ * @heap: the heap
+ * @size: amount of memory to drain in bytes
+ *
+ * Drains the indicated amount of memory from the deferred freelist immediately.
+ * Returns the total amount freed. The total freed may be higher depending
+ * on the size of the items in the list, or lower if there is insufficient
+ * total memory on the freelist.
+ *
+ * Unlike with @ion_heap_freelist_drain, don't put any pages back into
+ * page pools or otherwise cache the pages. Everything must be
+ * genuinely free'd back to the system. If you're free'ing from a
+ * shrinker you probably want to use this. Note that this relies on
+ * the heap.ops.free callback honoring the ION_PRIV_FLAG_SHRINKER_FREE
+ * flag.
+ */
+size_t ion_heap_freelist_shrink(struct ion_heap *heap,
+ size_t size);
+
+/**
+ * ion_heap_freelist_size - returns the size of the freelist in bytes
+ * @heap: the heap
+ */
+size_t ion_heap_freelist_size(struct ion_heap *heap);
+
+
+/**
+ * functions for creating and destroying the built in ion heaps.
+ * architectures can add their own custom architecture specific
+ * heaps as appropriate.
+ */
+
+struct ion_heap *ion_heap_create(struct ion_platform_heap *);
+void ion_heap_destroy(struct ion_heap *);
+struct ion_heap *ion_system_heap_create(struct ion_platform_heap *);
+void ion_system_heap_destroy(struct ion_heap *);
+
+struct ion_heap *ion_system_contig_heap_create(struct ion_platform_heap *);
+void ion_system_contig_heap_destroy(struct ion_heap *);
+
+struct ion_heap *ion_carveout_heap_create(struct ion_platform_heap *);
+void ion_carveout_heap_destroy(struct ion_heap *);
+
+struct ion_heap *ion_chunk_heap_create(struct ion_platform_heap *);
+void ion_chunk_heap_destroy(struct ion_heap *);
+struct ion_heap *ion_cma_heap_create(struct ion_platform_heap *);
+void ion_cma_heap_destroy(struct ion_heap *);
+
+/**
+ * kernel api to allocate/free from carveout -- used when carveout is
+ * used to back an architecture specific custom heap
+ */
+ion_phys_addr_t ion_carveout_allocate(struct ion_heap *heap, unsigned long size,
+ unsigned long align);
+void ion_carveout_free(struct ion_heap *heap, ion_phys_addr_t addr,
+ unsigned long size);
+/**
+ * The carveout heap returns physical addresses, since 0 may be a valid
+ * physical address, this is used to indicate allocation failed
+ */
+#define ION_CARVEOUT_ALLOCATE_FAIL -1
+
+/**
+ * functions for creating and destroying a heap pool -- allows you
+ * to keep a pool of pre allocated memory to use from your heap. Keeping
+ * a pool of memory that is ready for dma, ie any cached mapping have been
+ * invalidated from the cache, provides a significant performance benefit on
+ * many systems */
+
+/**
+ * struct ion_page_pool - pagepool struct
+ * @high_count: number of highmem items in the pool
+ * @low_count: number of lowmem items in the pool
+ * @high_items: list of highmem items
+ * @low_items: list of lowmem items
+ * @mutex: lock protecting this struct and especially the count
+ * item list
+ * @gfp_mask: gfp_mask to use from alloc
+ * @order: order of pages in the pool
+ * @list: plist node for list of pools
+ *
+ * Allows you to keep a pool of pre allocated pages to use from your heap.
+ * Keeping a pool of pages that is ready for dma, ie any cached mapping have
+ * been invalidated from the cache, provides a significant performance benefit
+ * on many systems
+ */
+struct ion_page_pool {
+ int high_count;
+ int low_count;
+ struct list_head high_items;
+ struct list_head low_items;
+ struct mutex mutex;
+ gfp_t gfp_mask;
+ unsigned int order;
+ struct plist_node list;
+};
+
+struct ion_page_pool *ion_page_pool_create(gfp_t gfp_mask, unsigned int order);
+void ion_page_pool_destroy(struct ion_page_pool *);
+struct page *ion_page_pool_alloc(struct ion_page_pool *);
+void ion_page_pool_free(struct ion_page_pool *, struct page *);
+
+/** ion_page_pool_shrink - shrinks the size of the memory cached in the pool
+ * @pool: the pool
+ * @gfp_mask: the memory type to reclaim
+ * @nr_to_scan: number of items to shrink in pages
+ *
+ * returns the number of items freed in pages
+ */
+int ion_page_pool_shrink(struct ion_page_pool *pool, gfp_t gfp_mask,
+ int nr_to_scan);
+
+/**
+ * ion_pages_sync_for_device - cache flush pages for use with the specified
+ * device
+ * @dev: the device the pages will be used with
+ * @page: the first page to be flushed
+ * @size: size in bytes of region to be flushed
+ * @dir: direction of dma transfer
+ */
+void ion_pages_sync_for_device(struct device *dev, struct page *page,
+ size_t size, enum dma_data_direction dir);
+
+#endif /* _ION_PRIV_H */
diff --git a/drivers/staging/android/ion/ion_system_heap.c b/drivers/staging/android/ion/ion_system_heap.c
new file mode 100644
index 000000000..da2a63c0a
--- /dev/null
+++ b/drivers/staging/android/ion/ion_system_heap.c
@@ -0,0 +1,422 @@
+/*
+ * drivers/staging/android/ion/ion_system_heap.c
+ *
+ * Copyright (C) 2011 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ */
+
+#include <asm/page.h>
+#include <linux/dma-mapping.h>
+#include <linux/err.h>
+#include <linux/highmem.h>
+#include <linux/mm.h>
+#include <linux/scatterlist.h>
+#include <linux/seq_file.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+#include "ion.h"
+#include "ion_priv.h"
+
+static gfp_t high_order_gfp_flags = (GFP_HIGHUSER | __GFP_ZERO | __GFP_NOWARN |
+ __GFP_NORETRY) & ~__GFP_WAIT;
+static gfp_t low_order_gfp_flags = (GFP_HIGHUSER | __GFP_ZERO | __GFP_NOWARN);
+static const unsigned int orders[] = {8, 4, 0};
+static const int num_orders = ARRAY_SIZE(orders);
+static int order_to_index(unsigned int order)
+{
+ int i;
+
+ for (i = 0; i < num_orders; i++)
+ if (order == orders[i])
+ return i;
+ BUG();
+ return -1;
+}
+
+static inline unsigned int order_to_size(int order)
+{
+ return PAGE_SIZE << order;
+}
+
+struct ion_system_heap {
+ struct ion_heap heap;
+ struct ion_page_pool *pools[0];
+};
+
+static struct page *alloc_buffer_page(struct ion_system_heap *heap,
+ struct ion_buffer *buffer,
+ unsigned long order)
+{
+ bool cached = ion_buffer_cached(buffer);
+ struct ion_page_pool *pool = heap->pools[order_to_index(order)];
+ struct page *page;
+
+ if (!cached) {
+ page = ion_page_pool_alloc(pool);
+ } else {
+ gfp_t gfp_flags = low_order_gfp_flags;
+
+ if (order > 4)
+ gfp_flags = high_order_gfp_flags;
+ page = alloc_pages(gfp_flags | __GFP_COMP, order);
+ if (!page)
+ return NULL;
+ ion_pages_sync_for_device(NULL, page, PAGE_SIZE << order,
+ DMA_BIDIRECTIONAL);
+ }
+
+ return page;
+}
+
+static void free_buffer_page(struct ion_system_heap *heap,
+ struct ion_buffer *buffer, struct page *page)
+{
+ unsigned int order = compound_order(page);
+ bool cached = ion_buffer_cached(buffer);
+
+ if (!cached && !(buffer->private_flags & ION_PRIV_FLAG_SHRINKER_FREE)) {
+ struct ion_page_pool *pool = heap->pools[order_to_index(order)];
+
+ ion_page_pool_free(pool, page);
+ } else {
+ __free_pages(page, order);
+ }
+}
+
+
+static struct page *alloc_largest_available(struct ion_system_heap *heap,
+ struct ion_buffer *buffer,
+ unsigned long size,
+ unsigned int max_order)
+{
+ struct page *page;
+ int i;
+
+ for (i = 0; i < num_orders; i++) {
+ if (size < order_to_size(orders[i]))
+ continue;
+ if (max_order < orders[i])
+ continue;
+
+ page = alloc_buffer_page(heap, buffer, orders[i]);
+ if (!page)
+ continue;
+
+ return page;
+ }
+
+ return NULL;
+}
+
+static int ion_system_heap_allocate(struct ion_heap *heap,
+ struct ion_buffer *buffer,
+ unsigned long size, unsigned long align,
+ unsigned long flags)
+{
+ struct ion_system_heap *sys_heap = container_of(heap,
+ struct ion_system_heap,
+ heap);
+ struct sg_table *table;
+ struct scatterlist *sg;
+ struct list_head pages;
+ struct page *page, *tmp_page;
+ int i = 0;
+ unsigned long size_remaining = PAGE_ALIGN(size);
+ unsigned int max_order = orders[0];
+
+ if (align > PAGE_SIZE)
+ return -EINVAL;
+
+ if (size / PAGE_SIZE > totalram_pages / 2)
+ return -ENOMEM;
+
+ INIT_LIST_HEAD(&pages);
+ while (size_remaining > 0) {
+ page = alloc_largest_available(sys_heap, buffer, size_remaining,
+ max_order);
+ if (!page)
+ goto free_pages;
+ list_add_tail(&page->lru, &pages);
+ size_remaining -= PAGE_SIZE << compound_order(page);
+ max_order = compound_order(page);
+ i++;
+ }
+ table = kmalloc(sizeof(struct sg_table), GFP_KERNEL);
+ if (!table)
+ goto free_pages;
+
+ if (sg_alloc_table(table, i, GFP_KERNEL))
+ goto free_table;
+
+ sg = table->sgl;
+ list_for_each_entry_safe(page, tmp_page, &pages, lru) {
+ sg_set_page(sg, page, PAGE_SIZE << compound_order(page), 0);
+ sg = sg_next(sg);
+ list_del(&page->lru);
+ }
+
+ buffer->priv_virt = table;
+ return 0;
+
+free_table:
+ kfree(table);
+free_pages:
+ list_for_each_entry_safe(page, tmp_page, &pages, lru)
+ free_buffer_page(sys_heap, buffer, page);
+ return -ENOMEM;
+}
+
+static void ion_system_heap_free(struct ion_buffer *buffer)
+{
+ struct ion_system_heap *sys_heap = container_of(buffer->heap,
+ struct ion_system_heap,
+ heap);
+ struct sg_table *table = buffer->sg_table;
+ bool cached = ion_buffer_cached(buffer);
+ struct scatterlist *sg;
+ int i;
+
+ /* uncached pages come from the page pools, zero them before returning
+ for security purposes (other allocations are zerod at alloc time */
+ if (!cached && !(buffer->private_flags & ION_PRIV_FLAG_SHRINKER_FREE))
+ ion_heap_buffer_zero(buffer);
+
+ for_each_sg(table->sgl, sg, table->nents, i)
+ free_buffer_page(sys_heap, buffer, sg_page(sg));
+ sg_free_table(table);
+ kfree(table);
+}
+
+static struct sg_table *ion_system_heap_map_dma(struct ion_heap *heap,
+ struct ion_buffer *buffer)
+{
+ return buffer->priv_virt;
+}
+
+static void ion_system_heap_unmap_dma(struct ion_heap *heap,
+ struct ion_buffer *buffer)
+{
+}
+
+static int ion_system_heap_shrink(struct ion_heap *heap, gfp_t gfp_mask,
+ int nr_to_scan)
+{
+ struct ion_system_heap *sys_heap;
+ int nr_total = 0;
+ int i;
+
+ sys_heap = container_of(heap, struct ion_system_heap, heap);
+
+ for (i = 0; i < num_orders; i++) {
+ struct ion_page_pool *pool = sys_heap->pools[i];
+
+ nr_total += ion_page_pool_shrink(pool, gfp_mask, nr_to_scan);
+ }
+
+ return nr_total;
+}
+
+static struct ion_heap_ops system_heap_ops = {
+ .allocate = ion_system_heap_allocate,
+ .free = ion_system_heap_free,
+ .map_dma = ion_system_heap_map_dma,
+ .unmap_dma = ion_system_heap_unmap_dma,
+ .map_kernel = ion_heap_map_kernel,
+ .unmap_kernel = ion_heap_unmap_kernel,
+ .map_user = ion_heap_map_user,
+ .shrink = ion_system_heap_shrink,
+};
+
+static int ion_system_heap_debug_show(struct ion_heap *heap, struct seq_file *s,
+ void *unused)
+{
+
+ struct ion_system_heap *sys_heap = container_of(heap,
+ struct ion_system_heap,
+ heap);
+ int i;
+
+ for (i = 0; i < num_orders; i++) {
+ struct ion_page_pool *pool = sys_heap->pools[i];
+
+ seq_printf(s, "%d order %u highmem pages in pool = %lu total\n",
+ pool->high_count, pool->order,
+ (PAGE_SIZE << pool->order) * pool->high_count);
+ seq_printf(s, "%d order %u lowmem pages in pool = %lu total\n",
+ pool->low_count, pool->order,
+ (PAGE_SIZE << pool->order) * pool->low_count);
+ }
+ return 0;
+}
+
+struct ion_heap *ion_system_heap_create(struct ion_platform_heap *unused)
+{
+ struct ion_system_heap *heap;
+ int i;
+
+ heap = kzalloc(sizeof(struct ion_system_heap) +
+ sizeof(struct ion_page_pool *) * num_orders,
+ GFP_KERNEL);
+ if (!heap)
+ return ERR_PTR(-ENOMEM);
+ heap->heap.ops = &system_heap_ops;
+ heap->heap.type = ION_HEAP_TYPE_SYSTEM;
+ heap->heap.flags = ION_HEAP_FLAG_DEFER_FREE;
+
+ for (i = 0; i < num_orders; i++) {
+ struct ion_page_pool *pool;
+ gfp_t gfp_flags = low_order_gfp_flags;
+
+ if (orders[i] > 4)
+ gfp_flags = high_order_gfp_flags;
+ pool = ion_page_pool_create(gfp_flags, orders[i]);
+ if (!pool)
+ goto destroy_pools;
+ heap->pools[i] = pool;
+ }
+
+ heap->heap.debug_show = ion_system_heap_debug_show;
+ return &heap->heap;
+
+destroy_pools:
+ while (i--)
+ ion_page_pool_destroy(heap->pools[i]);
+ kfree(heap);
+ return ERR_PTR(-ENOMEM);
+}
+
+void ion_system_heap_destroy(struct ion_heap *heap)
+{
+ struct ion_system_heap *sys_heap = container_of(heap,
+ struct ion_system_heap,
+ heap);
+ int i;
+
+ for (i = 0; i < num_orders; i++)
+ ion_page_pool_destroy(sys_heap->pools[i]);
+ kfree(sys_heap);
+}
+
+static int ion_system_contig_heap_allocate(struct ion_heap *heap,
+ struct ion_buffer *buffer,
+ unsigned long len,
+ unsigned long align,
+ unsigned long flags)
+{
+ int order = get_order(len);
+ struct page *page;
+ struct sg_table *table;
+ unsigned long i;
+ int ret;
+
+ if (align > (PAGE_SIZE << order))
+ return -EINVAL;
+
+ page = alloc_pages(low_order_gfp_flags, order);
+ if (!page)
+ return -ENOMEM;
+
+ split_page(page, order);
+
+ len = PAGE_ALIGN(len);
+ for (i = len >> PAGE_SHIFT; i < (1 << order); i++)
+ __free_page(page + i);
+
+ table = kmalloc(sizeof(struct sg_table), GFP_KERNEL);
+ if (!table) {
+ ret = -ENOMEM;
+ goto free_pages;
+ }
+
+ ret = sg_alloc_table(table, 1, GFP_KERNEL);
+ if (ret)
+ goto free_table;
+
+ sg_set_page(table->sgl, page, len, 0);
+
+ buffer->priv_virt = table;
+
+ ion_pages_sync_for_device(NULL, page, len, DMA_BIDIRECTIONAL);
+
+ return 0;
+
+free_table:
+ kfree(table);
+free_pages:
+ for (i = 0; i < len >> PAGE_SHIFT; i++)
+ __free_page(page + i);
+
+ return ret;
+}
+
+static void ion_system_contig_heap_free(struct ion_buffer *buffer)
+{
+ struct sg_table *table = buffer->priv_virt;
+ struct page *page = sg_page(table->sgl);
+ unsigned long pages = PAGE_ALIGN(buffer->size) >> PAGE_SHIFT;
+ unsigned long i;
+
+ for (i = 0; i < pages; i++)
+ __free_page(page + i);
+ sg_free_table(table);
+ kfree(table);
+}
+
+static int ion_system_contig_heap_phys(struct ion_heap *heap,
+ struct ion_buffer *buffer,
+ ion_phys_addr_t *addr, size_t *len)
+{
+ struct sg_table *table = buffer->priv_virt;
+ struct page *page = sg_page(table->sgl);
+ *addr = page_to_phys(page);
+ *len = buffer->size;
+ return 0;
+}
+
+static struct sg_table *ion_system_contig_heap_map_dma(struct ion_heap *heap,
+ struct ion_buffer *buffer)
+{
+ return buffer->priv_virt;
+}
+
+static void ion_system_contig_heap_unmap_dma(struct ion_heap *heap,
+ struct ion_buffer *buffer)
+{
+}
+
+static struct ion_heap_ops kmalloc_ops = {
+ .allocate = ion_system_contig_heap_allocate,
+ .free = ion_system_contig_heap_free,
+ .phys = ion_system_contig_heap_phys,
+ .map_dma = ion_system_contig_heap_map_dma,
+ .unmap_dma = ion_system_contig_heap_unmap_dma,
+ .map_kernel = ion_heap_map_kernel,
+ .unmap_kernel = ion_heap_unmap_kernel,
+ .map_user = ion_heap_map_user,
+};
+
+struct ion_heap *ion_system_contig_heap_create(struct ion_platform_heap *unused)
+{
+ struct ion_heap *heap;
+
+ heap = kzalloc(sizeof(struct ion_heap), GFP_KERNEL);
+ if (!heap)
+ return ERR_PTR(-ENOMEM);
+ heap->ops = &kmalloc_ops;
+ heap->type = ION_HEAP_TYPE_SYSTEM_CONTIG;
+ return heap;
+}
+
+void ion_system_contig_heap_destroy(struct ion_heap *heap)
+{
+ kfree(heap);
+}
diff --git a/drivers/staging/android/ion/ion_test.c b/drivers/staging/android/ion/ion_test.c
new file mode 100644
index 000000000..3bc461cbb
--- /dev/null
+++ b/drivers/staging/android/ion/ion_test.c
@@ -0,0 +1,283 @@
+/*
+ *
+ * Copyright (C) 2013 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#define pr_fmt(fmt) "ion-test: " fmt
+
+#include <linux/dma-buf.h>
+#include <linux/dma-direction.h>
+#include <linux/fs.h>
+#include <linux/miscdevice.h>
+#include <linux/mm.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+#include <linux/vmalloc.h>
+
+#include "ion.h"
+#include "../uapi/ion_test.h"
+
+#define u64_to_uptr(x) ((void __user *)(unsigned long)(x))
+
+struct ion_test_device {
+ struct miscdevice misc;
+};
+
+struct ion_test_data {
+ struct dma_buf *dma_buf;
+ struct device *dev;
+};
+
+static int ion_handle_test_dma(struct device *dev, struct dma_buf *dma_buf,
+ void __user *ptr, size_t offset, size_t size, bool write)
+{
+ int ret = 0;
+ struct dma_buf_attachment *attach;
+ struct sg_table *table;
+ pgprot_t pgprot = pgprot_writecombine(PAGE_KERNEL);
+ enum dma_data_direction dir = write ? DMA_FROM_DEVICE : DMA_TO_DEVICE;
+ struct sg_page_iter sg_iter;
+ unsigned long offset_page;
+
+ attach = dma_buf_attach(dma_buf, dev);
+ if (IS_ERR(attach))
+ return PTR_ERR(attach);
+
+ table = dma_buf_map_attachment(attach, dir);
+ if (IS_ERR(table))
+ return PTR_ERR(table);
+
+ offset_page = offset >> PAGE_SHIFT;
+ offset %= PAGE_SIZE;
+
+ for_each_sg_page(table->sgl, &sg_iter, table->nents, offset_page) {
+ struct page *page = sg_page_iter_page(&sg_iter);
+ void *vaddr = vmap(&page, 1, VM_MAP, pgprot);
+ size_t to_copy = PAGE_SIZE - offset;
+
+ to_copy = min(to_copy, size);
+ if (!vaddr) {
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ if (write)
+ ret = copy_from_user(vaddr + offset, ptr, to_copy);
+ else
+ ret = copy_to_user(ptr, vaddr + offset, to_copy);
+
+ vunmap(vaddr);
+ if (ret) {
+ ret = -EFAULT;
+ goto err;
+ }
+ size -= to_copy;
+ if (!size)
+ break;
+ ptr += to_copy;
+ offset = 0;
+ }
+
+err:
+ dma_buf_unmap_attachment(attach, table, dir);
+ dma_buf_detach(dma_buf, attach);
+ return ret;
+}
+
+static int ion_handle_test_kernel(struct dma_buf *dma_buf, void __user *ptr,
+ size_t offset, size_t size, bool write)
+{
+ int ret;
+ unsigned long page_offset = offset >> PAGE_SHIFT;
+ size_t copy_offset = offset % PAGE_SIZE;
+ size_t copy_size = size;
+ enum dma_data_direction dir = write ? DMA_FROM_DEVICE : DMA_TO_DEVICE;
+
+ if (offset > dma_buf->size || size > dma_buf->size - offset)
+ return -EINVAL;
+
+ ret = dma_buf_begin_cpu_access(dma_buf, offset, size, dir);
+ if (ret)
+ return ret;
+
+ while (copy_size > 0) {
+ size_t to_copy;
+ void *vaddr = dma_buf_kmap(dma_buf, page_offset);
+
+ if (!vaddr)
+ goto err;
+
+ to_copy = min_t(size_t, PAGE_SIZE - copy_offset, copy_size);
+
+ if (write)
+ ret = copy_from_user(vaddr + copy_offset, ptr, to_copy);
+ else
+ ret = copy_to_user(ptr, vaddr + copy_offset, to_copy);
+
+ dma_buf_kunmap(dma_buf, page_offset, vaddr);
+ if (ret) {
+ ret = -EFAULT;
+ goto err;
+ }
+
+ copy_size -= to_copy;
+ ptr += to_copy;
+ page_offset++;
+ copy_offset = 0;
+ }
+err:
+ dma_buf_end_cpu_access(dma_buf, offset, size, dir);
+ return ret;
+}
+
+static long ion_test_ioctl(struct file *filp, unsigned int cmd,
+ unsigned long arg)
+{
+ struct ion_test_data *test_data = filp->private_data;
+ int ret = 0;
+
+ union {
+ struct ion_test_rw_data test_rw;
+ } data;
+
+ if (_IOC_SIZE(cmd) > sizeof(data))
+ return -EINVAL;
+
+ if (_IOC_DIR(cmd) & _IOC_WRITE)
+ if (copy_from_user(&data, (void __user *)arg, _IOC_SIZE(cmd)))
+ return -EFAULT;
+
+ switch (cmd) {
+ case ION_IOC_TEST_SET_FD:
+ {
+ struct dma_buf *dma_buf = NULL;
+ int fd = arg;
+
+ if (fd >= 0) {
+ dma_buf = dma_buf_get((int)arg);
+ if (IS_ERR(dma_buf))
+ return PTR_ERR(dma_buf);
+ }
+ if (test_data->dma_buf)
+ dma_buf_put(test_data->dma_buf);
+ test_data->dma_buf = dma_buf;
+ break;
+ }
+ case ION_IOC_TEST_DMA_MAPPING:
+ {
+ ret = ion_handle_test_dma(test_data->dev, test_data->dma_buf,
+ u64_to_uptr(data.test_rw.ptr),
+ data.test_rw.offset, data.test_rw.size,
+ data.test_rw.write);
+ break;
+ }
+ case ION_IOC_TEST_KERNEL_MAPPING:
+ {
+ ret = ion_handle_test_kernel(test_data->dma_buf,
+ u64_to_uptr(data.test_rw.ptr),
+ data.test_rw.offset, data.test_rw.size,
+ data.test_rw.write);
+ break;
+ }
+ default:
+ return -ENOTTY;
+ }
+
+ if (_IOC_DIR(cmd) & _IOC_READ) {
+ if (copy_to_user((void __user *)arg, &data, sizeof(data)))
+ return -EFAULT;
+ }
+ return ret;
+}
+
+static int ion_test_open(struct inode *inode, struct file *file)
+{
+ struct ion_test_data *data;
+ struct miscdevice *miscdev = file->private_data;
+
+ data = kzalloc(sizeof(struct ion_test_data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ data->dev = miscdev->parent;
+
+ file->private_data = data;
+
+ return 0;
+}
+
+static int ion_test_release(struct inode *inode, struct file *file)
+{
+ struct ion_test_data *data = file->private_data;
+
+ kfree(data);
+
+ return 0;
+}
+
+static const struct file_operations ion_test_fops = {
+ .owner = THIS_MODULE,
+ .unlocked_ioctl = ion_test_ioctl,
+ .compat_ioctl = ion_test_ioctl,
+ .open = ion_test_open,
+ .release = ion_test_release,
+};
+
+static int __init ion_test_probe(struct platform_device *pdev)
+{
+ int ret;
+ struct ion_test_device *testdev;
+
+ testdev = devm_kzalloc(&pdev->dev, sizeof(struct ion_test_device),
+ GFP_KERNEL);
+ if (!testdev)
+ return -ENOMEM;
+
+ testdev->misc.minor = MISC_DYNAMIC_MINOR;
+ testdev->misc.name = "ion-test";
+ testdev->misc.fops = &ion_test_fops;
+ testdev->misc.parent = &pdev->dev;
+ ret = misc_register(&testdev->misc);
+ if (ret) {
+ pr_err("failed to register misc device.\n");
+ return ret;
+ }
+
+ platform_set_drvdata(pdev, testdev);
+
+ return 0;
+}
+
+static struct platform_driver ion_test_platform_driver = {
+ .driver = {
+ .name = "ion-test",
+ },
+};
+
+static int __init ion_test_init(void)
+{
+ platform_device_register_simple("ion-test", -1, NULL, 0);
+ return platform_driver_probe(&ion_test_platform_driver, ion_test_probe);
+}
+
+static void __exit ion_test_exit(void)
+{
+ platform_driver_unregister(&ion_test_platform_driver);
+}
+
+module_init(ion_test_init);
+module_exit(ion_test_exit);
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/staging/android/ion/tegra/Makefile b/drivers/staging/android/ion/tegra/Makefile
new file mode 100644
index 000000000..11cd003fb
--- /dev/null
+++ b/drivers/staging/android/ion/tegra/Makefile
@@ -0,0 +1 @@
+obj-y += tegra_ion.o
diff --git a/drivers/staging/android/ion/tegra/tegra_ion.c b/drivers/staging/android/ion/tegra/tegra_ion.c
new file mode 100644
index 000000000..5b8ef0e66
--- /dev/null
+++ b/drivers/staging/android/ion/tegra/tegra_ion.c
@@ -0,0 +1,80 @@
+/*
+ * drivers/gpu/tegra/tegra_ion.c
+ *
+ * Copyright (C) 2011 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ */
+
+#include <linux/err.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include "../ion.h"
+#include "../ion_priv.h"
+
+static struct ion_device *idev;
+static int num_heaps;
+static struct ion_heap **heaps;
+
+static int tegra_ion_probe(struct platform_device *pdev)
+{
+ struct ion_platform_data *pdata = pdev->dev.platform_data;
+ int err;
+ int i;
+
+ num_heaps = pdata->nr;
+
+ heaps = devm_kzalloc(&pdev->dev,
+ sizeof(struct ion_heap *) * pdata->nr,
+ GFP_KERNEL);
+
+ idev = ion_device_create(NULL);
+ if (IS_ERR_OR_NULL(idev))
+ return PTR_ERR(idev);
+
+ /* create the heaps as specified in the board file */
+ for (i = 0; i < num_heaps; i++) {
+ struct ion_platform_heap *heap_data = &pdata->heaps[i];
+
+ heaps[i] = ion_heap_create(heap_data);
+ if (IS_ERR_OR_NULL(heaps[i])) {
+ err = PTR_ERR(heaps[i]);
+ goto err;
+ }
+ ion_device_add_heap(idev, heaps[i]);
+ }
+ platform_set_drvdata(pdev, idev);
+ return 0;
+err:
+ for (i = 0; i < num_heaps; ++i)
+ ion_heap_destroy(heaps[i]);
+ return err;
+}
+
+static int tegra_ion_remove(struct platform_device *pdev)
+{
+ struct ion_device *idev = platform_get_drvdata(pdev);
+ int i;
+
+ ion_device_destroy(idev);
+ for (i = 0; i < num_heaps; i++)
+ ion_heap_destroy(heaps[i]);
+ return 0;
+}
+
+static struct platform_driver ion_driver = {
+ .probe = tegra_ion_probe,
+ .remove = tegra_ion_remove,
+ .driver = { .name = "ion-tegra" }
+};
+
+module_platform_driver(ion_driver);
+
diff --git a/drivers/staging/android/lowmemorykiller.c b/drivers/staging/android/lowmemorykiller.c
new file mode 100644
index 000000000..feafa172b
--- /dev/null
+++ b/drivers/staging/android/lowmemorykiller.c
@@ -0,0 +1,207 @@
+/* drivers/misc/lowmemorykiller.c
+ *
+ * The lowmemorykiller driver lets user-space specify a set of memory thresholds
+ * where processes with a range of oom_score_adj values will get killed. Specify
+ * the minimum oom_score_adj values in
+ * /sys/module/lowmemorykiller/parameters/adj and the number of free pages in
+ * /sys/module/lowmemorykiller/parameters/minfree. Both files take a comma
+ * separated list of numbers in ascending order.
+ *
+ * For example, write "0,8" to /sys/module/lowmemorykiller/parameters/adj and
+ * "1024,4096" to /sys/module/lowmemorykiller/parameters/minfree to kill
+ * processes with a oom_score_adj value of 8 or higher when the free memory
+ * drops below 4096 pages and kill processes with a oom_score_adj value of 0 or
+ * higher when the free memory drops below 1024 pages.
+ *
+ * The driver considers memory used for caches to be free, but if a large
+ * percentage of the cached memory is locked this can be very inaccurate
+ * and processes may not get killed until the normal oom killer is triggered.
+ *
+ * Copyright (C) 2007-2008 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/oom.h>
+#include <linux/sched.h>
+#include <linux/swap.h>
+#include <linux/rcupdate.h>
+#include <linux/profile.h>
+#include <linux/notifier.h>
+
+static uint32_t lowmem_debug_level = 1;
+static short lowmem_adj[6] = {
+ 0,
+ 1,
+ 6,
+ 12,
+};
+static int lowmem_adj_size = 4;
+static int lowmem_minfree[6] = {
+ 3 * 512, /* 6MB */
+ 2 * 1024, /* 8MB */
+ 4 * 1024, /* 16MB */
+ 16 * 1024, /* 64MB */
+};
+static int lowmem_minfree_size = 4;
+
+static unsigned long lowmem_deathpending_timeout;
+
+#define lowmem_print(level, x...) \
+ do { \
+ if (lowmem_debug_level >= (level)) \
+ pr_info(x); \
+ } while (0)
+
+static unsigned long lowmem_count(struct shrinker *s,
+ struct shrink_control *sc)
+{
+ return global_page_state(NR_ACTIVE_ANON) +
+ global_page_state(NR_ACTIVE_FILE) +
+ global_page_state(NR_INACTIVE_ANON) +
+ global_page_state(NR_INACTIVE_FILE);
+}
+
+static unsigned long lowmem_scan(struct shrinker *s, struct shrink_control *sc)
+{
+ struct task_struct *tsk;
+ struct task_struct *selected = NULL;
+ unsigned long rem = 0;
+ int tasksize;
+ int i;
+ short min_score_adj = OOM_SCORE_ADJ_MAX + 1;
+ int selected_tasksize = 0;
+ short selected_oom_score_adj;
+ int array_size = ARRAY_SIZE(lowmem_adj);
+ int other_free = global_page_state(NR_FREE_PAGES) - totalreserve_pages;
+ int other_file = global_page_state(NR_FILE_PAGES) -
+ global_page_state(NR_SHMEM) -
+ total_swapcache_pages();
+
+ if (lowmem_adj_size < array_size)
+ array_size = lowmem_adj_size;
+ if (lowmem_minfree_size < array_size)
+ array_size = lowmem_minfree_size;
+ for (i = 0; i < array_size; i++) {
+ if (other_free < lowmem_minfree[i] &&
+ other_file < lowmem_minfree[i]) {
+ min_score_adj = lowmem_adj[i];
+ break;
+ }
+ }
+
+ lowmem_print(3, "lowmem_scan %lu, %x, ofree %d %d, ma %hd\n",
+ sc->nr_to_scan, sc->gfp_mask, other_free,
+ other_file, min_score_adj);
+
+ if (min_score_adj == OOM_SCORE_ADJ_MAX + 1) {
+ lowmem_print(5, "lowmem_scan %lu, %x, return 0\n",
+ sc->nr_to_scan, sc->gfp_mask);
+ return 0;
+ }
+
+ selected_oom_score_adj = min_score_adj;
+
+ rcu_read_lock();
+ for_each_process(tsk) {
+ struct task_struct *p;
+ short oom_score_adj;
+
+ if (tsk->flags & PF_KTHREAD)
+ continue;
+
+ p = find_lock_task_mm(tsk);
+ if (!p)
+ continue;
+
+ if (test_tsk_thread_flag(p, TIF_MEMDIE) &&
+ time_before_eq(jiffies, lowmem_deathpending_timeout)) {
+ task_unlock(p);
+ rcu_read_unlock();
+ return 0;
+ }
+ oom_score_adj = p->signal->oom_score_adj;
+ if (oom_score_adj < min_score_adj) {
+ task_unlock(p);
+ continue;
+ }
+ tasksize = get_mm_rss(p->mm);
+ task_unlock(p);
+ if (tasksize <= 0)
+ continue;
+ if (selected) {
+ if (oom_score_adj < selected_oom_score_adj)
+ continue;
+ if (oom_score_adj == selected_oom_score_adj &&
+ tasksize <= selected_tasksize)
+ continue;
+ }
+ selected = p;
+ selected_tasksize = tasksize;
+ selected_oom_score_adj = oom_score_adj;
+ lowmem_print(2, "select %d (%s), adj %hd, size %d, to kill\n",
+ p->pid, p->comm, oom_score_adj, tasksize);
+ }
+ if (selected) {
+ lowmem_print(1, "send sigkill to %d (%s), adj %hd, size %d\n",
+ selected->pid, selected->comm,
+ selected_oom_score_adj, selected_tasksize);
+ lowmem_deathpending_timeout = jiffies + HZ;
+ /*
+ * FIXME: lowmemorykiller shouldn't abuse global OOM killer
+ * infrastructure. There is no real reason why the selected
+ * task should have access to the memory reserves.
+ */
+ mark_tsk_oom_victim(selected);
+ send_sig(SIGKILL, selected, 0);
+ rem += selected_tasksize;
+ }
+
+ lowmem_print(4, "lowmem_scan %lu, %x, return %lu\n",
+ sc->nr_to_scan, sc->gfp_mask, rem);
+ rcu_read_unlock();
+ return rem;
+}
+
+static struct shrinker lowmem_shrinker = {
+ .scan_objects = lowmem_scan,
+ .count_objects = lowmem_count,
+ .seeks = DEFAULT_SEEKS * 16
+};
+
+static int __init lowmem_init(void)
+{
+ register_shrinker(&lowmem_shrinker);
+ return 0;
+}
+
+static void __exit lowmem_exit(void)
+{
+ unregister_shrinker(&lowmem_shrinker);
+}
+
+module_param_named(cost, lowmem_shrinker.seeks, int, S_IRUGO | S_IWUSR);
+module_param_array_named(adj, lowmem_adj, short, &lowmem_adj_size,
+ S_IRUGO | S_IWUSR);
+module_param_array_named(minfree, lowmem_minfree, uint, &lowmem_minfree_size,
+ S_IRUGO | S_IWUSR);
+module_param_named(debug_level, lowmem_debug_level, uint, S_IRUGO | S_IWUSR);
+
+module_init(lowmem_init);
+module_exit(lowmem_exit);
+
+MODULE_LICENSE("GPL");
+
diff --git a/drivers/staging/android/sw_sync.c b/drivers/staging/android/sw_sync.c
new file mode 100644
index 000000000..c90838d36
--- /dev/null
+++ b/drivers/staging/android/sw_sync.c
@@ -0,0 +1,267 @@
+/*
+ * drivers/base/sw_sync.c
+ *
+ * Copyright (C) 2012 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/export.h>
+#include <linux/file.h>
+#include <linux/fs.h>
+#include <linux/miscdevice.h>
+#include <linux/module.h>
+#include <linux/syscalls.h>
+#include <linux/uaccess.h>
+
+#include "sw_sync.h"
+
+static int sw_sync_cmp(u32 a, u32 b)
+{
+ if (a == b)
+ return 0;
+
+ return ((s32)a - (s32)b) < 0 ? -1 : 1;
+}
+
+struct sync_pt *sw_sync_pt_create(struct sw_sync_timeline *obj, u32 value)
+{
+ struct sw_sync_pt *pt;
+
+ pt = (struct sw_sync_pt *)
+ sync_pt_create(&obj->obj, sizeof(struct sw_sync_pt));
+
+ pt->value = value;
+
+ return (struct sync_pt *)pt;
+}
+EXPORT_SYMBOL(sw_sync_pt_create);
+
+static struct sync_pt *sw_sync_pt_dup(struct sync_pt *sync_pt)
+{
+ struct sw_sync_pt *pt = (struct sw_sync_pt *)sync_pt;
+ struct sw_sync_timeline *obj =
+ (struct sw_sync_timeline *)sync_pt_parent(sync_pt);
+
+ return (struct sync_pt *)sw_sync_pt_create(obj, pt->value);
+}
+
+static int sw_sync_pt_has_signaled(struct sync_pt *sync_pt)
+{
+ struct sw_sync_pt *pt = (struct sw_sync_pt *)sync_pt;
+ struct sw_sync_timeline *obj =
+ (struct sw_sync_timeline *)sync_pt_parent(sync_pt);
+
+ return sw_sync_cmp(obj->value, pt->value) >= 0;
+}
+
+static int sw_sync_pt_compare(struct sync_pt *a, struct sync_pt *b)
+{
+ struct sw_sync_pt *pt_a = (struct sw_sync_pt *)a;
+ struct sw_sync_pt *pt_b = (struct sw_sync_pt *)b;
+
+ return sw_sync_cmp(pt_a->value, pt_b->value);
+}
+
+static int sw_sync_fill_driver_data(struct sync_pt *sync_pt,
+ void *data, int size)
+{
+ struct sw_sync_pt *pt = (struct sw_sync_pt *)sync_pt;
+
+ if (size < sizeof(pt->value))
+ return -ENOMEM;
+
+ memcpy(data, &pt->value, sizeof(pt->value));
+
+ return sizeof(pt->value);
+}
+
+static void sw_sync_timeline_value_str(struct sync_timeline *sync_timeline,
+ char *str, int size)
+{
+ struct sw_sync_timeline *timeline =
+ (struct sw_sync_timeline *)sync_timeline;
+ snprintf(str, size, "%d", timeline->value);
+}
+
+static void sw_sync_pt_value_str(struct sync_pt *sync_pt,
+ char *str, int size)
+{
+ struct sw_sync_pt *pt = (struct sw_sync_pt *)sync_pt;
+
+ snprintf(str, size, "%d", pt->value);
+}
+
+static struct sync_timeline_ops sw_sync_timeline_ops = {
+ .driver_name = "sw_sync",
+ .dup = sw_sync_pt_dup,
+ .has_signaled = sw_sync_pt_has_signaled,
+ .compare = sw_sync_pt_compare,
+ .fill_driver_data = sw_sync_fill_driver_data,
+ .timeline_value_str = sw_sync_timeline_value_str,
+ .pt_value_str = sw_sync_pt_value_str,
+};
+
+struct sw_sync_timeline *sw_sync_timeline_create(const char *name)
+{
+ struct sw_sync_timeline *obj = (struct sw_sync_timeline *)
+ sync_timeline_create(&sw_sync_timeline_ops,
+ sizeof(struct sw_sync_timeline),
+ name);
+
+ return obj;
+}
+EXPORT_SYMBOL(sw_sync_timeline_create);
+
+void sw_sync_timeline_inc(struct sw_sync_timeline *obj, u32 inc)
+{
+ obj->value += inc;
+
+ sync_timeline_signal(&obj->obj);
+}
+EXPORT_SYMBOL(sw_sync_timeline_inc);
+
+#ifdef CONFIG_SW_SYNC_USER
+/* *WARNING*
+ *
+ * improper use of this can result in deadlocking kernel drivers from userspace.
+ */
+
+/* opening sw_sync create a new sync obj */
+static int sw_sync_open(struct inode *inode, struct file *file)
+{
+ struct sw_sync_timeline *obj;
+ char task_comm[TASK_COMM_LEN];
+
+ get_task_comm(task_comm, current);
+
+ obj = sw_sync_timeline_create(task_comm);
+ if (obj == NULL)
+ return -ENOMEM;
+
+ file->private_data = obj;
+
+ return 0;
+}
+
+static int sw_sync_release(struct inode *inode, struct file *file)
+{
+ struct sw_sync_timeline *obj = file->private_data;
+
+ sync_timeline_destroy(&obj->obj);
+ return 0;
+}
+
+static long sw_sync_ioctl_create_fence(struct sw_sync_timeline *obj,
+ unsigned long arg)
+{
+ int fd = get_unused_fd_flags(O_CLOEXEC);
+ int err;
+ struct sync_pt *pt;
+ struct sync_fence *fence;
+ struct sw_sync_create_fence_data data;
+
+ if (fd < 0)
+ return fd;
+
+ if (copy_from_user(&data, (void __user *)arg, sizeof(data))) {
+ err = -EFAULT;
+ goto err;
+ }
+
+ pt = sw_sync_pt_create(obj, data.value);
+ if (pt == NULL) {
+ err = -ENOMEM;
+ goto err;
+ }
+
+ data.name[sizeof(data.name) - 1] = '\0';
+ fence = sync_fence_create(data.name, pt);
+ if (fence == NULL) {
+ sync_pt_free(pt);
+ err = -ENOMEM;
+ goto err;
+ }
+
+ data.fence = fd;
+ if (copy_to_user((void __user *)arg, &data, sizeof(data))) {
+ sync_fence_put(fence);
+ err = -EFAULT;
+ goto err;
+ }
+
+ sync_fence_install(fence, fd);
+
+ return 0;
+
+err:
+ put_unused_fd(fd);
+ return err;
+}
+
+static long sw_sync_ioctl_inc(struct sw_sync_timeline *obj, unsigned long arg)
+{
+ u32 value;
+
+ if (copy_from_user(&value, (void __user *)arg, sizeof(value)))
+ return -EFAULT;
+
+ sw_sync_timeline_inc(obj, value);
+
+ return 0;
+}
+
+static long sw_sync_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ struct sw_sync_timeline *obj = file->private_data;
+
+ switch (cmd) {
+ case SW_SYNC_IOC_CREATE_FENCE:
+ return sw_sync_ioctl_create_fence(obj, arg);
+
+ case SW_SYNC_IOC_INC:
+ return sw_sync_ioctl_inc(obj, arg);
+
+ default:
+ return -ENOTTY;
+ }
+}
+
+static const struct file_operations sw_sync_fops = {
+ .owner = THIS_MODULE,
+ .open = sw_sync_open,
+ .release = sw_sync_release,
+ .unlocked_ioctl = sw_sync_ioctl,
+ .compat_ioctl = sw_sync_ioctl,
+};
+
+static struct miscdevice sw_sync_dev = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "sw_sync",
+ .fops = &sw_sync_fops,
+};
+
+static int __init sw_sync_device_init(void)
+{
+ return misc_register(&sw_sync_dev);
+}
+
+static void __exit sw_sync_device_remove(void)
+{
+ misc_deregister(&sw_sync_dev);
+}
+
+module_init(sw_sync_device_init);
+module_exit(sw_sync_device_remove);
+
+#endif /* CONFIG_SW_SYNC_USER */
diff --git a/drivers/staging/android/sw_sync.h b/drivers/staging/android/sw_sync.h
new file mode 100644
index 000000000..c87ae9ebf
--- /dev/null
+++ b/drivers/staging/android/sw_sync.h
@@ -0,0 +1,59 @@
+/*
+ * include/linux/sw_sync.h
+ *
+ * Copyright (C) 2012 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ */
+
+#ifndef _LINUX_SW_SYNC_H
+#define _LINUX_SW_SYNC_H
+
+#include <linux/types.h>
+#include <linux/kconfig.h>
+#include "sync.h"
+#include "uapi/sw_sync.h"
+
+struct sw_sync_timeline {
+ struct sync_timeline obj;
+
+ u32 value;
+};
+
+struct sw_sync_pt {
+ struct sync_pt pt;
+
+ u32 value;
+};
+
+#if IS_ENABLED(CONFIG_SW_SYNC)
+struct sw_sync_timeline *sw_sync_timeline_create(const char *name);
+void sw_sync_timeline_inc(struct sw_sync_timeline *obj, u32 inc);
+
+struct sync_pt *sw_sync_pt_create(struct sw_sync_timeline *obj, u32 value);
+#else
+static inline struct sw_sync_timeline *sw_sync_timeline_create(const char *name)
+{
+ return NULL;
+}
+
+static inline void sw_sync_timeline_inc(struct sw_sync_timeline *obj, u32 inc)
+{
+}
+
+static inline struct sync_pt *sw_sync_pt_create(struct sw_sync_timeline *obj,
+ u32 value)
+{
+ return NULL;
+}
+#endif /* IS_ENABLED(CONFIG_SW_SYNC) */
+
+#endif /* _LINUX_SW_SYNC_H */
diff --git a/drivers/staging/android/sync.c b/drivers/staging/android/sync.c
new file mode 100644
index 000000000..f83e00c78
--- /dev/null
+++ b/drivers/staging/android/sync.c
@@ -0,0 +1,729 @@
+/*
+ * drivers/base/sync.c
+ *
+ * Copyright (C) 2012 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ */
+
+#include <linux/debugfs.h>
+#include <linux/export.h>
+#include <linux/file.h>
+#include <linux/fs.h>
+#include <linux/kernel.h>
+#include <linux/poll.h>
+#include <linux/sched.h>
+#include <linux/seq_file.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+#include <linux/anon_inodes.h>
+
+#include "sync.h"
+
+#define CREATE_TRACE_POINTS
+#include "trace/sync.h"
+
+static const struct fence_ops android_fence_ops;
+static const struct file_operations sync_fence_fops;
+
+struct sync_timeline *sync_timeline_create(const struct sync_timeline_ops *ops,
+ int size, const char *name)
+{
+ struct sync_timeline *obj;
+
+ if (size < sizeof(struct sync_timeline))
+ return NULL;
+
+ obj = kzalloc(size, GFP_KERNEL);
+ if (obj == NULL)
+ return NULL;
+
+ kref_init(&obj->kref);
+ obj->ops = ops;
+ obj->context = fence_context_alloc(1);
+ strlcpy(obj->name, name, sizeof(obj->name));
+
+ INIT_LIST_HEAD(&obj->child_list_head);
+ INIT_LIST_HEAD(&obj->active_list_head);
+ spin_lock_init(&obj->child_list_lock);
+
+ sync_timeline_debug_add(obj);
+
+ return obj;
+}
+EXPORT_SYMBOL(sync_timeline_create);
+
+static void sync_timeline_free(struct kref *kref)
+{
+ struct sync_timeline *obj =
+ container_of(kref, struct sync_timeline, kref);
+
+ sync_timeline_debug_remove(obj);
+
+ if (obj->ops->release_obj)
+ obj->ops->release_obj(obj);
+
+ kfree(obj);
+}
+
+static void sync_timeline_get(struct sync_timeline *obj)
+{
+ kref_get(&obj->kref);
+}
+
+static void sync_timeline_put(struct sync_timeline *obj)
+{
+ kref_put(&obj->kref, sync_timeline_free);
+}
+
+void sync_timeline_destroy(struct sync_timeline *obj)
+{
+ obj->destroyed = true;
+ /*
+ * Ensure timeline is marked as destroyed before
+ * changing timeline's fences status.
+ */
+ smp_wmb();
+
+ /*
+ * signal any children that their parent is going away.
+ */
+ sync_timeline_signal(obj);
+ sync_timeline_put(obj);
+}
+EXPORT_SYMBOL(sync_timeline_destroy);
+
+void sync_timeline_signal(struct sync_timeline *obj)
+{
+ unsigned long flags;
+ LIST_HEAD(signaled_pts);
+ struct sync_pt *pt, *next;
+
+ trace_sync_timeline(obj);
+
+ spin_lock_irqsave(&obj->child_list_lock, flags);
+
+ list_for_each_entry_safe(pt, next, &obj->active_list_head,
+ active_list) {
+ if (fence_is_signaled_locked(&pt->base))
+ list_del_init(&pt->active_list);
+ }
+
+ spin_unlock_irqrestore(&obj->child_list_lock, flags);
+}
+EXPORT_SYMBOL(sync_timeline_signal);
+
+struct sync_pt *sync_pt_create(struct sync_timeline *obj, int size)
+{
+ unsigned long flags;
+ struct sync_pt *pt;
+
+ if (size < sizeof(struct sync_pt))
+ return NULL;
+
+ pt = kzalloc(size, GFP_KERNEL);
+ if (pt == NULL)
+ return NULL;
+
+ spin_lock_irqsave(&obj->child_list_lock, flags);
+ sync_timeline_get(obj);
+ fence_init(&pt->base, &android_fence_ops, &obj->child_list_lock,
+ obj->context, ++obj->value);
+ list_add_tail(&pt->child_list, &obj->child_list_head);
+ INIT_LIST_HEAD(&pt->active_list);
+ spin_unlock_irqrestore(&obj->child_list_lock, flags);
+ return pt;
+}
+EXPORT_SYMBOL(sync_pt_create);
+
+void sync_pt_free(struct sync_pt *pt)
+{
+ fence_put(&pt->base);
+}
+EXPORT_SYMBOL(sync_pt_free);
+
+static struct sync_fence *sync_fence_alloc(int size, const char *name)
+{
+ struct sync_fence *fence;
+
+ fence = kzalloc(size, GFP_KERNEL);
+ if (fence == NULL)
+ return NULL;
+
+ fence->file = anon_inode_getfile("sync_fence", &sync_fence_fops,
+ fence, 0);
+ if (IS_ERR(fence->file))
+ goto err;
+
+ kref_init(&fence->kref);
+ strlcpy(fence->name, name, sizeof(fence->name));
+
+ init_waitqueue_head(&fence->wq);
+
+ return fence;
+
+err:
+ kfree(fence);
+ return NULL;
+}
+
+static void fence_check_cb_func(struct fence *f, struct fence_cb *cb)
+{
+ struct sync_fence_cb *check;
+ struct sync_fence *fence;
+
+ check = container_of(cb, struct sync_fence_cb, cb);
+ fence = check->fence;
+
+ if (atomic_dec_and_test(&fence->status))
+ wake_up_all(&fence->wq);
+}
+
+/* TODO: implement a create which takes more that one sync_pt */
+struct sync_fence *sync_fence_create(const char *name, struct sync_pt *pt)
+{
+ struct sync_fence *fence;
+
+ fence = sync_fence_alloc(offsetof(struct sync_fence, cbs[1]), name);
+ if (fence == NULL)
+ return NULL;
+
+ fence->num_fences = 1;
+ atomic_set(&fence->status, 1);
+
+ fence->cbs[0].sync_pt = &pt->base;
+ fence->cbs[0].fence = fence;
+ if (fence_add_callback(&pt->base, &fence->cbs[0].cb,
+ fence_check_cb_func))
+ atomic_dec(&fence->status);
+
+ sync_fence_debug_add(fence);
+
+ return fence;
+}
+EXPORT_SYMBOL(sync_fence_create);
+
+struct sync_fence *sync_fence_fdget(int fd)
+{
+ struct file *file = fget(fd);
+
+ if (file == NULL)
+ return NULL;
+
+ if (file->f_op != &sync_fence_fops)
+ goto err;
+
+ return file->private_data;
+
+err:
+ fput(file);
+ return NULL;
+}
+EXPORT_SYMBOL(sync_fence_fdget);
+
+void sync_fence_put(struct sync_fence *fence)
+{
+ fput(fence->file);
+}
+EXPORT_SYMBOL(sync_fence_put);
+
+void sync_fence_install(struct sync_fence *fence, int fd)
+{
+ fd_install(fd, fence->file);
+}
+EXPORT_SYMBOL(sync_fence_install);
+
+static void sync_fence_add_pt(struct sync_fence *fence,
+ int *i, struct fence *pt)
+{
+ fence->cbs[*i].sync_pt = pt;
+ fence->cbs[*i].fence = fence;
+
+ if (!fence_add_callback(pt, &fence->cbs[*i].cb, fence_check_cb_func)) {
+ fence_get(pt);
+ (*i)++;
+ }
+}
+
+struct sync_fence *sync_fence_merge(const char *name,
+ struct sync_fence *a, struct sync_fence *b)
+{
+ int num_fences = a->num_fences + b->num_fences;
+ struct sync_fence *fence;
+ int i, i_a, i_b;
+ unsigned long size = offsetof(struct sync_fence, cbs[num_fences]);
+
+ fence = sync_fence_alloc(size, name);
+ if (fence == NULL)
+ return NULL;
+
+ atomic_set(&fence->status, num_fences);
+
+ /*
+ * Assume sync_fence a and b are both ordered and have no
+ * duplicates with the same context.
+ *
+ * If a sync_fence can only be created with sync_fence_merge
+ * and sync_fence_create, this is a reasonable assumption.
+ */
+ for (i = i_a = i_b = 0; i_a < a->num_fences && i_b < b->num_fences; ) {
+ struct fence *pt_a = a->cbs[i_a].sync_pt;
+ struct fence *pt_b = b->cbs[i_b].sync_pt;
+
+ if (pt_a->context < pt_b->context) {
+ sync_fence_add_pt(fence, &i, pt_a);
+
+ i_a++;
+ } else if (pt_a->context > pt_b->context) {
+ sync_fence_add_pt(fence, &i, pt_b);
+
+ i_b++;
+ } else {
+ if (pt_a->seqno - pt_b->seqno <= INT_MAX)
+ sync_fence_add_pt(fence, &i, pt_a);
+ else
+ sync_fence_add_pt(fence, &i, pt_b);
+
+ i_a++;
+ i_b++;
+ }
+ }
+
+ for (; i_a < a->num_fences; i_a++)
+ sync_fence_add_pt(fence, &i, a->cbs[i_a].sync_pt);
+
+ for (; i_b < b->num_fences; i_b++)
+ sync_fence_add_pt(fence, &i, b->cbs[i_b].sync_pt);
+
+ if (num_fences > i)
+ atomic_sub(num_fences - i, &fence->status);
+ fence->num_fences = i;
+
+ sync_fence_debug_add(fence);
+ return fence;
+}
+EXPORT_SYMBOL(sync_fence_merge);
+
+int sync_fence_wake_up_wq(wait_queue_t *curr, unsigned mode,
+ int wake_flags, void *key)
+{
+ struct sync_fence_waiter *wait;
+
+ wait = container_of(curr, struct sync_fence_waiter, work);
+ list_del_init(&wait->work.task_list);
+
+ wait->callback(wait->work.private, wait);
+ return 1;
+}
+
+int sync_fence_wait_async(struct sync_fence *fence,
+ struct sync_fence_waiter *waiter)
+{
+ int err = atomic_read(&fence->status);
+ unsigned long flags;
+
+ if (err < 0)
+ return err;
+
+ if (!err)
+ return 1;
+
+ init_waitqueue_func_entry(&waiter->work, sync_fence_wake_up_wq);
+ waiter->work.private = fence;
+
+ spin_lock_irqsave(&fence->wq.lock, flags);
+ err = atomic_read(&fence->status);
+ if (err > 0)
+ __add_wait_queue_tail(&fence->wq, &waiter->work);
+ spin_unlock_irqrestore(&fence->wq.lock, flags);
+
+ if (err < 0)
+ return err;
+
+ return !err;
+}
+EXPORT_SYMBOL(sync_fence_wait_async);
+
+int sync_fence_cancel_async(struct sync_fence *fence,
+ struct sync_fence_waiter *waiter)
+{
+ unsigned long flags;
+ int ret = 0;
+
+ spin_lock_irqsave(&fence->wq.lock, flags);
+ if (!list_empty(&waiter->work.task_list))
+ list_del_init(&waiter->work.task_list);
+ else
+ ret = -ENOENT;
+ spin_unlock_irqrestore(&fence->wq.lock, flags);
+ return ret;
+}
+EXPORT_SYMBOL(sync_fence_cancel_async);
+
+int sync_fence_wait(struct sync_fence *fence, long timeout)
+{
+ long ret;
+ int i;
+
+ if (timeout < 0)
+ timeout = MAX_SCHEDULE_TIMEOUT;
+ else
+ timeout = msecs_to_jiffies(timeout);
+
+ trace_sync_wait(fence, 1);
+ for (i = 0; i < fence->num_fences; ++i)
+ trace_sync_pt(fence->cbs[i].sync_pt);
+ ret = wait_event_interruptible_timeout(fence->wq,
+ atomic_read(&fence->status) <= 0,
+ timeout);
+ trace_sync_wait(fence, 0);
+
+ if (ret < 0) {
+ return ret;
+ } else if (ret == 0) {
+ if (timeout) {
+ pr_info("fence timeout on [%p] after %dms\n", fence,
+ jiffies_to_msecs(timeout));
+ sync_dump();
+ }
+ return -ETIME;
+ }
+
+ ret = atomic_read(&fence->status);
+ if (ret) {
+ pr_info("fence error %ld on [%p]\n", ret, fence);
+ sync_dump();
+ }
+ return ret;
+}
+EXPORT_SYMBOL(sync_fence_wait);
+
+static const char *android_fence_get_driver_name(struct fence *fence)
+{
+ struct sync_pt *pt = container_of(fence, struct sync_pt, base);
+ struct sync_timeline *parent = sync_pt_parent(pt);
+
+ return parent->ops->driver_name;
+}
+
+static const char *android_fence_get_timeline_name(struct fence *fence)
+{
+ struct sync_pt *pt = container_of(fence, struct sync_pt, base);
+ struct sync_timeline *parent = sync_pt_parent(pt);
+
+ return parent->name;
+}
+
+static void android_fence_release(struct fence *fence)
+{
+ struct sync_pt *pt = container_of(fence, struct sync_pt, base);
+ struct sync_timeline *parent = sync_pt_parent(pt);
+ unsigned long flags;
+
+ spin_lock_irqsave(fence->lock, flags);
+ list_del(&pt->child_list);
+ if (WARN_ON_ONCE(!list_empty(&pt->active_list)))
+ list_del(&pt->active_list);
+ spin_unlock_irqrestore(fence->lock, flags);
+
+ if (parent->ops->free_pt)
+ parent->ops->free_pt(pt);
+
+ sync_timeline_put(parent);
+ fence_free(&pt->base);
+}
+
+static bool android_fence_signaled(struct fence *fence)
+{
+ struct sync_pt *pt = container_of(fence, struct sync_pt, base);
+ struct sync_timeline *parent = sync_pt_parent(pt);
+ int ret;
+
+ ret = parent->ops->has_signaled(pt);
+ if (ret < 0)
+ fence->status = ret;
+ return ret;
+}
+
+static bool android_fence_enable_signaling(struct fence *fence)
+{
+ struct sync_pt *pt = container_of(fence, struct sync_pt, base);
+ struct sync_timeline *parent = sync_pt_parent(pt);
+
+ if (android_fence_signaled(fence))
+ return false;
+
+ list_add_tail(&pt->active_list, &parent->active_list_head);
+ return true;
+}
+
+static int android_fence_fill_driver_data(struct fence *fence,
+ void *data, int size)
+{
+ struct sync_pt *pt = container_of(fence, struct sync_pt, base);
+ struct sync_timeline *parent = sync_pt_parent(pt);
+
+ if (!parent->ops->fill_driver_data)
+ return 0;
+ return parent->ops->fill_driver_data(pt, data, size);
+}
+
+static void android_fence_value_str(struct fence *fence,
+ char *str, int size)
+{
+ struct sync_pt *pt = container_of(fence, struct sync_pt, base);
+ struct sync_timeline *parent = sync_pt_parent(pt);
+
+ if (!parent->ops->pt_value_str) {
+ if (size)
+ *str = 0;
+ return;
+ }
+ parent->ops->pt_value_str(pt, str, size);
+}
+
+static void android_fence_timeline_value_str(struct fence *fence,
+ char *str, int size)
+{
+ struct sync_pt *pt = container_of(fence, struct sync_pt, base);
+ struct sync_timeline *parent = sync_pt_parent(pt);
+
+ if (!parent->ops->timeline_value_str) {
+ if (size)
+ *str = 0;
+ return;
+ }
+ parent->ops->timeline_value_str(parent, str, size);
+}
+
+static const struct fence_ops android_fence_ops = {
+ .get_driver_name = android_fence_get_driver_name,
+ .get_timeline_name = android_fence_get_timeline_name,
+ .enable_signaling = android_fence_enable_signaling,
+ .signaled = android_fence_signaled,
+ .wait = fence_default_wait,
+ .release = android_fence_release,
+ .fill_driver_data = android_fence_fill_driver_data,
+ .fence_value_str = android_fence_value_str,
+ .timeline_value_str = android_fence_timeline_value_str,
+};
+
+static void sync_fence_free(struct kref *kref)
+{
+ struct sync_fence *fence = container_of(kref, struct sync_fence, kref);
+ int i, status = atomic_read(&fence->status);
+
+ for (i = 0; i < fence->num_fences; ++i) {
+ if (status)
+ fence_remove_callback(fence->cbs[i].sync_pt,
+ &fence->cbs[i].cb);
+ fence_put(fence->cbs[i].sync_pt);
+ }
+
+ kfree(fence);
+}
+
+static int sync_fence_release(struct inode *inode, struct file *file)
+{
+ struct sync_fence *fence = file->private_data;
+
+ sync_fence_debug_remove(fence);
+
+ kref_put(&fence->kref, sync_fence_free);
+ return 0;
+}
+
+static unsigned int sync_fence_poll(struct file *file, poll_table *wait)
+{
+ struct sync_fence *fence = file->private_data;
+ int status;
+
+ poll_wait(file, &fence->wq, wait);
+
+ status = atomic_read(&fence->status);
+
+ if (!status)
+ return POLLIN;
+ else if (status < 0)
+ return POLLERR;
+ return 0;
+}
+
+static long sync_fence_ioctl_wait(struct sync_fence *fence, unsigned long arg)
+{
+ __s32 value;
+
+ if (copy_from_user(&value, (void __user *)arg, sizeof(value)))
+ return -EFAULT;
+
+ return sync_fence_wait(fence, value);
+}
+
+static long sync_fence_ioctl_merge(struct sync_fence *fence, unsigned long arg)
+{
+ int fd = get_unused_fd_flags(O_CLOEXEC);
+ int err;
+ struct sync_fence *fence2, *fence3;
+ struct sync_merge_data data;
+
+ if (fd < 0)
+ return fd;
+
+ if (copy_from_user(&data, (void __user *)arg, sizeof(data))) {
+ err = -EFAULT;
+ goto err_put_fd;
+ }
+
+ fence2 = sync_fence_fdget(data.fd2);
+ if (fence2 == NULL) {
+ err = -ENOENT;
+ goto err_put_fd;
+ }
+
+ data.name[sizeof(data.name) - 1] = '\0';
+ fence3 = sync_fence_merge(data.name, fence, fence2);
+ if (fence3 == NULL) {
+ err = -ENOMEM;
+ goto err_put_fence2;
+ }
+
+ data.fence = fd;
+ if (copy_to_user((void __user *)arg, &data, sizeof(data))) {
+ err = -EFAULT;
+ goto err_put_fence3;
+ }
+
+ sync_fence_install(fence3, fd);
+ sync_fence_put(fence2);
+ return 0;
+
+err_put_fence3:
+ sync_fence_put(fence3);
+
+err_put_fence2:
+ sync_fence_put(fence2);
+
+err_put_fd:
+ put_unused_fd(fd);
+ return err;
+}
+
+static int sync_fill_pt_info(struct fence *fence, void *data, int size)
+{
+ struct sync_pt_info *info = data;
+ int ret;
+
+ if (size < sizeof(struct sync_pt_info))
+ return -ENOMEM;
+
+ info->len = sizeof(struct sync_pt_info);
+
+ if (fence->ops->fill_driver_data) {
+ ret = fence->ops->fill_driver_data(fence, info->driver_data,
+ size - sizeof(*info));
+ if (ret < 0)
+ return ret;
+
+ info->len += ret;
+ }
+
+ strlcpy(info->obj_name, fence->ops->get_timeline_name(fence),
+ sizeof(info->obj_name));
+ strlcpy(info->driver_name, fence->ops->get_driver_name(fence),
+ sizeof(info->driver_name));
+ if (fence_is_signaled(fence))
+ info->status = fence->status >= 0 ? 1 : fence->status;
+ else
+ info->status = 0;
+ info->timestamp_ns = ktime_to_ns(fence->timestamp);
+
+ return info->len;
+}
+
+static long sync_fence_ioctl_fence_info(struct sync_fence *fence,
+ unsigned long arg)
+{
+ struct sync_fence_info_data *data;
+ __u32 size;
+ __u32 len = 0;
+ int ret, i;
+
+ if (copy_from_user(&size, (void __user *)arg, sizeof(size)))
+ return -EFAULT;
+
+ if (size < sizeof(struct sync_fence_info_data))
+ return -EINVAL;
+
+ if (size > 4096)
+ size = 4096;
+
+ data = kzalloc(size, GFP_KERNEL);
+ if (data == NULL)
+ return -ENOMEM;
+
+ strlcpy(data->name, fence->name, sizeof(data->name));
+ data->status = atomic_read(&fence->status);
+ if (data->status >= 0)
+ data->status = !data->status;
+
+ len = sizeof(struct sync_fence_info_data);
+
+ for (i = 0; i < fence->num_fences; ++i) {
+ struct fence *pt = fence->cbs[i].sync_pt;
+
+ ret = sync_fill_pt_info(pt, (u8 *)data + len, size - len);
+
+ if (ret < 0)
+ goto out;
+
+ len += ret;
+ }
+
+ data->len = len;
+
+ if (copy_to_user((void __user *)arg, data, len))
+ ret = -EFAULT;
+ else
+ ret = 0;
+
+out:
+ kfree(data);
+
+ return ret;
+}
+
+static long sync_fence_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ struct sync_fence *fence = file->private_data;
+
+ switch (cmd) {
+ case SYNC_IOC_WAIT:
+ return sync_fence_ioctl_wait(fence, arg);
+
+ case SYNC_IOC_MERGE:
+ return sync_fence_ioctl_merge(fence, arg);
+
+ case SYNC_IOC_FENCE_INFO:
+ return sync_fence_ioctl_fence_info(fence, arg);
+
+ default:
+ return -ENOTTY;
+ }
+}
+
+static const struct file_operations sync_fence_fops = {
+ .release = sync_fence_release,
+ .poll = sync_fence_poll,
+ .unlocked_ioctl = sync_fence_ioctl,
+ .compat_ioctl = sync_fence_ioctl,
+};
+
diff --git a/drivers/staging/android/sync.h b/drivers/staging/android/sync.h
new file mode 100644
index 000000000..a21b79fb4
--- /dev/null
+++ b/drivers/staging/android/sync.h
@@ -0,0 +1,356 @@
+/*
+ * include/linux/sync.h
+ *
+ * Copyright (C) 2012 Google, Inc.
+ *
+ * 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.
+ *
+ */
+
+#ifndef _LINUX_SYNC_H
+#define _LINUX_SYNC_H
+
+#include <linux/types.h>
+#include <linux/kref.h>
+#include <linux/ktime.h>
+#include <linux/list.h>
+#include <linux/spinlock.h>
+#include <linux/wait.h>
+#include <linux/fence.h>
+
+#include "uapi/sync.h"
+
+struct sync_timeline;
+struct sync_pt;
+struct sync_fence;
+
+/**
+ * struct sync_timeline_ops - sync object implementation ops
+ * @driver_name: name of the implementation
+ * @dup: duplicate a sync_pt
+ * @has_signaled: returns:
+ * 1 if pt has signaled
+ * 0 if pt has not signaled
+ * <0 on error
+ * @compare: returns:
+ * 1 if b will signal before a
+ * 0 if a and b will signal at the same time
+ * -1 if a will signal before b
+ * @free_pt: called before sync_pt is freed
+ * @release_obj: called before sync_timeline is freed
+ * @fill_driver_data: write implementation specific driver data to data.
+ * should return an error if there is not enough room
+ * as specified by size. This information is returned
+ * to userspace by SYNC_IOC_FENCE_INFO.
+ * @timeline_value_str: fill str with the value of the sync_timeline's counter
+ * @pt_value_str: fill str with the value of the sync_pt
+ */
+struct sync_timeline_ops {
+ const char *driver_name;
+
+ /* required */
+ struct sync_pt * (*dup)(struct sync_pt *pt);
+
+ /* required */
+ int (*has_signaled)(struct sync_pt *pt);
+
+ /* required */
+ int (*compare)(struct sync_pt *a, struct sync_pt *b);
+
+ /* optional */
+ void (*free_pt)(struct sync_pt *sync_pt);
+
+ /* optional */
+ void (*release_obj)(struct sync_timeline *sync_timeline);
+
+ /* optional */
+ int (*fill_driver_data)(struct sync_pt *syncpt, void *data, int size);
+
+ /* optional */
+ void (*timeline_value_str)(struct sync_timeline *timeline, char *str,
+ int size);
+
+ /* optional */
+ void (*pt_value_str)(struct sync_pt *pt, char *str, int size);
+};
+
+/**
+ * struct sync_timeline - sync object
+ * @kref: reference count on fence.
+ * @ops: ops that define the implementation of the sync_timeline
+ * @name: name of the sync_timeline. Useful for debugging
+ * @destroyed: set when sync_timeline is destroyed
+ * @child_list_head: list of children sync_pts for this sync_timeline
+ * @child_list_lock: lock protecting @child_list_head, destroyed, and
+ * sync_pt.status
+ * @active_list_head: list of active (unsignaled/errored) sync_pts
+ * @sync_timeline_list: membership in global sync_timeline_list
+ */
+struct sync_timeline {
+ struct kref kref;
+ const struct sync_timeline_ops *ops;
+ char name[32];
+
+ /* protected by child_list_lock */
+ bool destroyed;
+ int context, value;
+
+ struct list_head child_list_head;
+ spinlock_t child_list_lock;
+
+ struct list_head active_list_head;
+
+#ifdef CONFIG_DEBUG_FS
+ struct list_head sync_timeline_list;
+#endif
+};
+
+/**
+ * struct sync_pt - sync point
+ * @fence: base fence class
+ * @child_list: membership in sync_timeline.child_list_head
+ * @active_list: membership in sync_timeline.active_list_head
+ * @signaled_list: membership in temporary signaled_list on stack
+ * @fence: sync_fence to which the sync_pt belongs
+ * @pt_list: membership in sync_fence.pt_list_head
+ * @status: 1: signaled, 0:active, <0: error
+ * @timestamp: time which sync_pt status transitioned from active to
+ * signaled or error.
+ */
+struct sync_pt {
+ struct fence base;
+
+ struct list_head child_list;
+ struct list_head active_list;
+};
+
+static inline struct sync_timeline *sync_pt_parent(struct sync_pt *pt)
+{
+ return container_of(pt->base.lock, struct sync_timeline,
+ child_list_lock);
+}
+
+struct sync_fence_cb {
+ struct fence_cb cb;
+ struct fence *sync_pt;
+ struct sync_fence *fence;
+};
+
+/**
+ * struct sync_fence - sync fence
+ * @file: file representing this fence
+ * @kref: reference count on fence.
+ * @name: name of sync_fence. Useful for debugging
+ * @pt_list_head: list of sync_pts in the fence. immutable once fence
+ * is created
+ * @status: 0: signaled, >0:active, <0: error
+ *
+ * @wq: wait queue for fence signaling
+ * @sync_fence_list: membership in global fence list
+ */
+struct sync_fence {
+ struct file *file;
+ struct kref kref;
+ char name[32];
+#ifdef CONFIG_DEBUG_FS
+ struct list_head sync_fence_list;
+#endif
+ int num_fences;
+
+ wait_queue_head_t wq;
+ atomic_t status;
+
+ struct sync_fence_cb cbs[];
+};
+
+struct sync_fence_waiter;
+typedef void (*sync_callback_t)(struct sync_fence *fence,
+ struct sync_fence_waiter *waiter);
+
+/**
+ * struct sync_fence_waiter - metadata for asynchronous waiter on a fence
+ * @waiter_list: membership in sync_fence.waiter_list_head
+ * @callback: function pointer to call when fence signals
+ * @callback_data: pointer to pass to @callback
+ */
+struct sync_fence_waiter {
+ wait_queue_t work;
+ sync_callback_t callback;
+};
+
+static inline void sync_fence_waiter_init(struct sync_fence_waiter *waiter,
+ sync_callback_t callback)
+{
+ INIT_LIST_HEAD(&waiter->work.task_list);
+ waiter->callback = callback;
+}
+
+/*
+ * API for sync_timeline implementers
+ */
+
+/**
+ * sync_timeline_create() - creates a sync object
+ * @ops: specifies the implementation ops for the object
+ * @size: size to allocate for this obj
+ * @name: sync_timeline name
+ *
+ * Creates a new sync_timeline which will use the implementation specified by
+ * @ops. @size bytes will be allocated allowing for implementation specific
+ * data to be kept after the generic sync_timeline struct.
+ */
+struct sync_timeline *sync_timeline_create(const struct sync_timeline_ops *ops,
+ int size, const char *name);
+
+/**
+ * sync_timeline_destroy() - destroys a sync object
+ * @obj: sync_timeline to destroy
+ *
+ * A sync implementation should call this when the @obj is going away
+ * (i.e. module unload.) @obj won't actually be freed until all its children
+ * sync_pts are freed.
+ */
+void sync_timeline_destroy(struct sync_timeline *obj);
+
+/**
+ * sync_timeline_signal() - signal a status change on a sync_timeline
+ * @obj: sync_timeline to signal
+ *
+ * A sync implementation should call this any time one of it's sync_pts
+ * has signaled or has an error condition.
+ */
+void sync_timeline_signal(struct sync_timeline *obj);
+
+/**
+ * sync_pt_create() - creates a sync pt
+ * @parent: sync_pt's parent sync_timeline
+ * @size: size to allocate for this pt
+ *
+ * Creates a new sync_pt as a child of @parent. @size bytes will be
+ * allocated allowing for implementation specific data to be kept after
+ * the generic sync_timeline struct.
+ */
+struct sync_pt *sync_pt_create(struct sync_timeline *parent, int size);
+
+/**
+ * sync_pt_free() - frees a sync pt
+ * @pt: sync_pt to free
+ *
+ * This should only be called on sync_pts which have been created but
+ * not added to a fence.
+ */
+void sync_pt_free(struct sync_pt *pt);
+
+/**
+ * sync_fence_create() - creates a sync fence
+ * @name: name of fence to create
+ * @pt: sync_pt to add to the fence
+ *
+ * Creates a fence containg @pt. Once this is called, the fence takes
+ * ownership of @pt.
+ */
+struct sync_fence *sync_fence_create(const char *name, struct sync_pt *pt);
+
+/*
+ * API for sync_fence consumers
+ */
+
+/**
+ * sync_fence_merge() - merge two fences
+ * @name: name of new fence
+ * @a: fence a
+ * @b: fence b
+ *
+ * Creates a new fence which contains copies of all the sync_pts in both
+ * @a and @b. @a and @b remain valid, independent fences.
+ */
+struct sync_fence *sync_fence_merge(const char *name,
+ struct sync_fence *a, struct sync_fence *b);
+
+/**
+ * sync_fence_fdget() - get a fence from an fd
+ * @fd: fd referencing a fence
+ *
+ * Ensures @fd references a valid fence, increments the refcount of the backing
+ * file, and returns the fence.
+ */
+struct sync_fence *sync_fence_fdget(int fd);
+
+/**
+ * sync_fence_put() - puts a reference of a sync fence
+ * @fence: fence to put
+ *
+ * Puts a reference on @fence. If this is the last reference, the fence and
+ * all it's sync_pts will be freed
+ */
+void sync_fence_put(struct sync_fence *fence);
+
+/**
+ * sync_fence_install() - installs a fence into a file descriptor
+ * @fence: fence to install
+ * @fd: file descriptor in which to install the fence
+ *
+ * Installs @fence into @fd. @fd's should be acquired through
+ * get_unused_fd_flags(O_CLOEXEC).
+ */
+void sync_fence_install(struct sync_fence *fence, int fd);
+
+/**
+ * sync_fence_wait_async() - registers and async wait on the fence
+ * @fence: fence to wait on
+ * @waiter: waiter callback struck
+ *
+ * Returns 1 if @fence has already signaled.
+ *
+ * Registers a callback to be called when @fence signals or has an error.
+ * @waiter should be initialized with sync_fence_waiter_init().
+ */
+int sync_fence_wait_async(struct sync_fence *fence,
+ struct sync_fence_waiter *waiter);
+
+/**
+ * sync_fence_cancel_async() - cancels an async wait
+ * @fence: fence to wait on
+ * @waiter: waiter callback struck
+ *
+ * returns 0 if waiter was removed from fence's async waiter list.
+ * returns -ENOENT if waiter was not found on fence's async waiter list.
+ *
+ * Cancels a previously registered async wait. Will fail gracefully if
+ * @waiter was never registered or if @fence has already signaled @waiter.
+ */
+int sync_fence_cancel_async(struct sync_fence *fence,
+ struct sync_fence_waiter *waiter);
+
+/**
+ * sync_fence_wait() - wait on fence
+ * @fence: fence to wait on
+ * @tiemout: timeout in ms
+ *
+ * Wait for @fence to be signaled or have an error. Waits indefinitely
+ * if @timeout < 0
+ */
+int sync_fence_wait(struct sync_fence *fence, long timeout);
+
+#ifdef CONFIG_DEBUG_FS
+
+extern void sync_timeline_debug_add(struct sync_timeline *obj);
+extern void sync_timeline_debug_remove(struct sync_timeline *obj);
+extern void sync_fence_debug_add(struct sync_fence *fence);
+extern void sync_fence_debug_remove(struct sync_fence *fence);
+extern void sync_dump(void);
+
+#else
+# define sync_timeline_debug_add(obj)
+# define sync_timeline_debug_remove(obj)
+# define sync_fence_debug_add(fence)
+# define sync_fence_debug_remove(fence)
+# define sync_dump()
+#endif
+int sync_fence_wake_up_wq(wait_queue_t *curr, unsigned mode,
+ int wake_flags, void *key);
+
+#endif /* _LINUX_SYNC_H */
diff --git a/drivers/staging/android/sync_debug.c b/drivers/staging/android/sync_debug.c
new file mode 100644
index 000000000..91ed2c4cf
--- /dev/null
+++ b/drivers/staging/android/sync_debug.c
@@ -0,0 +1,254 @@
+/*
+ * drivers/base/sync.c
+ *
+ * Copyright (C) 2012 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ */
+
+#include <linux/debugfs.h>
+#include <linux/export.h>
+#include <linux/file.h>
+#include <linux/fs.h>
+#include <linux/kernel.h>
+#include <linux/poll.h>
+#include <linux/sched.h>
+#include <linux/seq_file.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+#include <linux/anon_inodes.h>
+#include <linux/time64.h>
+#include "sync.h"
+
+#ifdef CONFIG_DEBUG_FS
+
+static LIST_HEAD(sync_timeline_list_head);
+static DEFINE_SPINLOCK(sync_timeline_list_lock);
+static LIST_HEAD(sync_fence_list_head);
+static DEFINE_SPINLOCK(sync_fence_list_lock);
+
+void sync_timeline_debug_add(struct sync_timeline *obj)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&sync_timeline_list_lock, flags);
+ list_add_tail(&obj->sync_timeline_list, &sync_timeline_list_head);
+ spin_unlock_irqrestore(&sync_timeline_list_lock, flags);
+}
+
+void sync_timeline_debug_remove(struct sync_timeline *obj)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&sync_timeline_list_lock, flags);
+ list_del(&obj->sync_timeline_list);
+ spin_unlock_irqrestore(&sync_timeline_list_lock, flags);
+}
+
+void sync_fence_debug_add(struct sync_fence *fence)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&sync_fence_list_lock, flags);
+ list_add_tail(&fence->sync_fence_list, &sync_fence_list_head);
+ spin_unlock_irqrestore(&sync_fence_list_lock, flags);
+}
+
+void sync_fence_debug_remove(struct sync_fence *fence)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&sync_fence_list_lock, flags);
+ list_del(&fence->sync_fence_list);
+ spin_unlock_irqrestore(&sync_fence_list_lock, flags);
+}
+
+static const char *sync_status_str(int status)
+{
+ if (status == 0)
+ return "signaled";
+
+ if (status > 0)
+ return "active";
+
+ return "error";
+}
+
+static void sync_print_pt(struct seq_file *s, struct sync_pt *pt, bool fence)
+{
+ int status = 1;
+ struct sync_timeline *parent = sync_pt_parent(pt);
+
+ if (fence_is_signaled_locked(&pt->base))
+ status = pt->base.status;
+
+ seq_printf(s, " %s%spt %s",
+ fence ? parent->name : "",
+ fence ? "_" : "",
+ sync_status_str(status));
+
+ if (status <= 0) {
+ struct timespec64 ts64 =
+ ktime_to_timespec64(pt->base.timestamp);
+
+ seq_printf(s, "@%lld.%09ld", (s64)ts64.tv_sec, ts64.tv_nsec);
+ }
+
+ if (parent->ops->timeline_value_str &&
+ parent->ops->pt_value_str) {
+ char value[64];
+
+ parent->ops->pt_value_str(pt, value, sizeof(value));
+ seq_printf(s, ": %s", value);
+ if (fence) {
+ parent->ops->timeline_value_str(parent, value,
+ sizeof(value));
+ seq_printf(s, " / %s", value);
+ }
+ }
+
+ seq_puts(s, "\n");
+}
+
+static void sync_print_obj(struct seq_file *s, struct sync_timeline *obj)
+{
+ struct list_head *pos;
+ unsigned long flags;
+
+ seq_printf(s, "%s %s", obj->name, obj->ops->driver_name);
+
+ if (obj->ops->timeline_value_str) {
+ char value[64];
+
+ obj->ops->timeline_value_str(obj, value, sizeof(value));
+ seq_printf(s, ": %s", value);
+ }
+
+ seq_puts(s, "\n");
+
+ spin_lock_irqsave(&obj->child_list_lock, flags);
+ list_for_each(pos, &obj->child_list_head) {
+ struct sync_pt *pt =
+ container_of(pos, struct sync_pt, child_list);
+ sync_print_pt(s, pt, false);
+ }
+ spin_unlock_irqrestore(&obj->child_list_lock, flags);
+}
+
+static void sync_print_fence(struct seq_file *s, struct sync_fence *fence)
+{
+ wait_queue_t *pos;
+ unsigned long flags;
+ int i;
+
+ seq_printf(s, "[%p] %s: %s\n", fence, fence->name,
+ sync_status_str(atomic_read(&fence->status)));
+
+ for (i = 0; i < fence->num_fences; ++i) {
+ struct sync_pt *pt =
+ container_of(fence->cbs[i].sync_pt,
+ struct sync_pt, base);
+
+ sync_print_pt(s, pt, true);
+ }
+
+ spin_lock_irqsave(&fence->wq.lock, flags);
+ list_for_each_entry(pos, &fence->wq.task_list, task_list) {
+ struct sync_fence_waiter *waiter;
+
+ if (pos->func != &sync_fence_wake_up_wq)
+ continue;
+
+ waiter = container_of(pos, struct sync_fence_waiter, work);
+
+ seq_printf(s, "waiter %pF\n", waiter->callback);
+ }
+ spin_unlock_irqrestore(&fence->wq.lock, flags);
+}
+
+static int sync_debugfs_show(struct seq_file *s, void *unused)
+{
+ unsigned long flags;
+ struct list_head *pos;
+
+ seq_puts(s, "objs:\n--------------\n");
+
+ spin_lock_irqsave(&sync_timeline_list_lock, flags);
+ list_for_each(pos, &sync_timeline_list_head) {
+ struct sync_timeline *obj =
+ container_of(pos, struct sync_timeline,
+ sync_timeline_list);
+
+ sync_print_obj(s, obj);
+ seq_puts(s, "\n");
+ }
+ spin_unlock_irqrestore(&sync_timeline_list_lock, flags);
+
+ seq_puts(s, "fences:\n--------------\n");
+
+ spin_lock_irqsave(&sync_fence_list_lock, flags);
+ list_for_each(pos, &sync_fence_list_head) {
+ struct sync_fence *fence =
+ container_of(pos, struct sync_fence, sync_fence_list);
+
+ sync_print_fence(s, fence);
+ seq_puts(s, "\n");
+ }
+ spin_unlock_irqrestore(&sync_fence_list_lock, flags);
+ return 0;
+}
+
+static int sync_debugfs_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, sync_debugfs_show, inode->i_private);
+}
+
+static const struct file_operations sync_debugfs_fops = {
+ .open = sync_debugfs_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static __init int sync_debugfs_init(void)
+{
+ debugfs_create_file("sync", S_IRUGO, NULL, NULL, &sync_debugfs_fops);
+ return 0;
+}
+late_initcall(sync_debugfs_init);
+
+#define DUMP_CHUNK 256
+static char sync_dump_buf[64 * 1024];
+void sync_dump(void)
+{
+ struct seq_file s = {
+ .buf = sync_dump_buf,
+ .size = sizeof(sync_dump_buf) - 1,
+ };
+ int i;
+
+ sync_debugfs_show(&s, NULL);
+
+ for (i = 0; i < s.count; i += DUMP_CHUNK) {
+ if ((s.count - i) > DUMP_CHUNK) {
+ char c = s.buf[i + DUMP_CHUNK];
+
+ s.buf[i + DUMP_CHUNK] = 0;
+ pr_cont("%s", s.buf + i);
+ s.buf[i + DUMP_CHUNK] = c;
+ } else {
+ s.buf[s.count] = 0;
+ pr_cont("%s", s.buf + i);
+ }
+ }
+}
+
+#endif
diff --git a/drivers/staging/android/timed_gpio.c b/drivers/staging/android/timed_gpio.c
new file mode 100644
index 000000000..938a35cd9
--- /dev/null
+++ b/drivers/staging/android/timed_gpio.c
@@ -0,0 +1,168 @@
+/* drivers/misc/timed_gpio.c
+ *
+ * Copyright (C) 2008 Google, Inc.
+ * Author: Mike Lockwood <lockwood@android.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/hrtimer.h>
+#include <linux/err.h>
+#include <linux/gpio.h>
+#include <linux/ktime.h>
+
+#include "timed_output.h"
+#include "timed_gpio.h"
+
+
+struct timed_gpio_data {
+ struct timed_output_dev dev;
+ struct hrtimer timer;
+ spinlock_t lock;
+ unsigned gpio;
+ int max_timeout;
+ u8 active_low;
+};
+
+static enum hrtimer_restart gpio_timer_func(struct hrtimer *timer)
+{
+ struct timed_gpio_data *data =
+ container_of(timer, struct timed_gpio_data, timer);
+
+ gpio_direction_output(data->gpio, data->active_low ? 1 : 0);
+ return HRTIMER_NORESTART;
+}
+
+static int gpio_get_time(struct timed_output_dev *dev)
+{
+ struct timed_gpio_data *data;
+ ktime_t t;
+
+ data = container_of(dev, struct timed_gpio_data, dev);
+
+ if (!hrtimer_active(&data->timer))
+ return 0;
+
+ t = hrtimer_get_remaining(&data->timer);
+
+ return ktime_to_ms(t);
+}
+
+static void gpio_enable(struct timed_output_dev *dev, int value)
+{
+ struct timed_gpio_data *data =
+ container_of(dev, struct timed_gpio_data, dev);
+ unsigned long flags;
+
+ spin_lock_irqsave(&data->lock, flags);
+
+ /* cancel previous timer and set GPIO according to value */
+ hrtimer_cancel(&data->timer);
+ gpio_direction_output(data->gpio, data->active_low ? !value : !!value);
+
+ if (value > 0) {
+ if (value > data->max_timeout)
+ value = data->max_timeout;
+
+ hrtimer_start(&data->timer,
+ ktime_set(value / 1000, (value % 1000) * 1000000),
+ HRTIMER_MODE_REL);
+ }
+
+ spin_unlock_irqrestore(&data->lock, flags);
+}
+
+static int timed_gpio_probe(struct platform_device *pdev)
+{
+ struct timed_gpio_platform_data *pdata = pdev->dev.platform_data;
+ struct timed_gpio *cur_gpio;
+ struct timed_gpio_data *gpio_data, *gpio_dat;
+ int i, ret;
+
+ if (!pdata)
+ return -EBUSY;
+
+ gpio_data = devm_kzalloc(&pdev->dev,
+ sizeof(struct timed_gpio_data) * pdata->num_gpios,
+ GFP_KERNEL);
+ if (!gpio_data)
+ return -ENOMEM;
+
+ for (i = 0; i < pdata->num_gpios; i++) {
+ cur_gpio = &pdata->gpios[i];
+ gpio_dat = &gpio_data[i];
+
+ hrtimer_init(&gpio_dat->timer, CLOCK_MONOTONIC,
+ HRTIMER_MODE_REL);
+ gpio_dat->timer.function = gpio_timer_func;
+ spin_lock_init(&gpio_dat->lock);
+
+ gpio_dat->dev.name = cur_gpio->name;
+ gpio_dat->dev.get_time = gpio_get_time;
+ gpio_dat->dev.enable = gpio_enable;
+ ret = gpio_request(cur_gpio->gpio, cur_gpio->name);
+ if (ret < 0)
+ goto err_out;
+ ret = timed_output_dev_register(&gpio_dat->dev);
+ if (ret < 0) {
+ gpio_free(cur_gpio->gpio);
+ goto err_out;
+ }
+
+ gpio_dat->gpio = cur_gpio->gpio;
+ gpio_dat->max_timeout = cur_gpio->max_timeout;
+ gpio_dat->active_low = cur_gpio->active_low;
+ gpio_direction_output(gpio_dat->gpio, gpio_dat->active_low);
+ }
+
+ platform_set_drvdata(pdev, gpio_data);
+
+ return 0;
+
+err_out:
+ while (--i >= 0) {
+ timed_output_dev_unregister(&gpio_data[i].dev);
+ gpio_free(gpio_data[i].gpio);
+ }
+
+ return ret;
+}
+
+static int timed_gpio_remove(struct platform_device *pdev)
+{
+ struct timed_gpio_platform_data *pdata = pdev->dev.platform_data;
+ struct timed_gpio_data *gpio_data = platform_get_drvdata(pdev);
+ int i;
+
+ for (i = 0; i < pdata->num_gpios; i++) {
+ timed_output_dev_unregister(&gpio_data[i].dev);
+ gpio_free(gpio_data[i].gpio);
+ }
+
+ return 0;
+}
+
+static struct platform_driver timed_gpio_driver = {
+ .probe = timed_gpio_probe,
+ .remove = timed_gpio_remove,
+ .driver = {
+ .name = TIMED_GPIO_NAME,
+ },
+};
+
+module_platform_driver(timed_gpio_driver);
+
+MODULE_AUTHOR("Mike Lockwood <lockwood@android.com>");
+MODULE_DESCRIPTION("timed gpio driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/android/timed_gpio.h b/drivers/staging/android/timed_gpio.h
new file mode 100644
index 000000000..d29e169d7
--- /dev/null
+++ b/drivers/staging/android/timed_gpio.h
@@ -0,0 +1,33 @@
+/* include/linux/timed_gpio.h
+ *
+ * Copyright (C) 2008 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+*/
+
+#ifndef _LINUX_TIMED_GPIO_H
+#define _LINUX_TIMED_GPIO_H
+
+#define TIMED_GPIO_NAME "timed-gpio"
+
+struct timed_gpio {
+ const char *name;
+ unsigned gpio;
+ int max_timeout;
+ u8 active_low;
+};
+
+struct timed_gpio_platform_data {
+ int num_gpios;
+ struct timed_gpio *gpios;
+};
+
+#endif
diff --git a/drivers/staging/android/timed_output.c b/drivers/staging/android/timed_output.c
new file mode 100644
index 000000000..b41429f37
--- /dev/null
+++ b/drivers/staging/android/timed_output.c
@@ -0,0 +1,120 @@
+/* drivers/misc/timed_output.c
+ *
+ * Copyright (C) 2009 Google, Inc.
+ * Author: Mike Lockwood <lockwood@android.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#define pr_fmt(fmt) "timed_output: " fmt
+
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/device.h>
+#include <linux/fs.h>
+#include <linux/err.h>
+
+#include "timed_output.h"
+
+static struct class *timed_output_class;
+static atomic_t device_count;
+
+static ssize_t enable_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct timed_output_dev *tdev = dev_get_drvdata(dev);
+ int remaining = tdev->get_time(tdev);
+
+ return sprintf(buf, "%d\n", remaining);
+}
+
+static ssize_t enable_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t size)
+{
+ struct timed_output_dev *tdev = dev_get_drvdata(dev);
+ int value;
+ int rc;
+
+ rc = kstrtoint(buf, 0, &value);
+ if (rc != 0)
+ return -EINVAL;
+
+ tdev->enable(tdev, value);
+
+ return size;
+}
+static DEVICE_ATTR_RW(enable);
+
+static struct attribute *timed_output_attrs[] = {
+ &dev_attr_enable.attr,
+ NULL,
+};
+ATTRIBUTE_GROUPS(timed_output);
+
+static int create_timed_output_class(void)
+{
+ if (!timed_output_class) {
+ timed_output_class = class_create(THIS_MODULE, "timed_output");
+ if (IS_ERR(timed_output_class))
+ return PTR_ERR(timed_output_class);
+ atomic_set(&device_count, 0);
+ timed_output_class->dev_groups = timed_output_groups;
+ }
+
+ return 0;
+}
+
+int timed_output_dev_register(struct timed_output_dev *tdev)
+{
+ int ret;
+
+ if (!tdev || !tdev->name || !tdev->enable || !tdev->get_time)
+ return -EINVAL;
+
+ ret = create_timed_output_class();
+ if (ret < 0)
+ return ret;
+
+ tdev->index = atomic_inc_return(&device_count);
+ tdev->dev = device_create(timed_output_class, NULL,
+ MKDEV(0, tdev->index), NULL, "%s", tdev->name);
+ if (IS_ERR(tdev->dev))
+ return PTR_ERR(tdev->dev);
+
+ dev_set_drvdata(tdev->dev, tdev);
+ tdev->state = 0;
+ return 0;
+}
+EXPORT_SYMBOL_GPL(timed_output_dev_register);
+
+void timed_output_dev_unregister(struct timed_output_dev *tdev)
+{
+ tdev->enable(tdev, 0);
+ device_destroy(timed_output_class, MKDEV(0, tdev->index));
+}
+EXPORT_SYMBOL_GPL(timed_output_dev_unregister);
+
+static int __init timed_output_init(void)
+{
+ return create_timed_output_class();
+}
+
+static void __exit timed_output_exit(void)
+{
+ class_destroy(timed_output_class);
+}
+
+module_init(timed_output_init);
+module_exit(timed_output_exit);
+
+MODULE_AUTHOR("Mike Lockwood <lockwood@android.com>");
+MODULE_DESCRIPTION("timed output class driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/android/timed_output.h b/drivers/staging/android/timed_output.h
new file mode 100644
index 000000000..13d2ca51c
--- /dev/null
+++ b/drivers/staging/android/timed_output.h
@@ -0,0 +1,37 @@
+/* include/linux/timed_output.h
+ *
+ * Copyright (C) 2008 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+*/
+
+#ifndef _LINUX_TIMED_OUTPUT_H
+#define _LINUX_TIMED_OUTPUT_H
+
+struct timed_output_dev {
+ const char *name;
+
+ /* enable the output and set the timer */
+ void (*enable)(struct timed_output_dev *sdev, int timeout);
+
+ /* returns the current number of milliseconds remaining on the timer */
+ int (*get_time)(struct timed_output_dev *sdev);
+
+ /* private data */
+ struct device *dev;
+ int index;
+ int state;
+};
+
+int timed_output_dev_register(struct timed_output_dev *dev);
+void timed_output_dev_unregister(struct timed_output_dev *dev);
+
+#endif
diff --git a/drivers/staging/android/trace/sync.h b/drivers/staging/android/trace/sync.h
new file mode 100644
index 000000000..77edb977a
--- /dev/null
+++ b/drivers/staging/android/trace/sync.h
@@ -0,0 +1,82 @@
+#undef TRACE_SYSTEM
+#define TRACE_INCLUDE_PATH ../../drivers/staging/android/trace
+#define TRACE_SYSTEM sync
+
+#if !defined(_TRACE_SYNC_H) || defined(TRACE_HEADER_MULTI_READ)
+#define _TRACE_SYNC_H
+
+#include "../sync.h"
+#include <linux/tracepoint.h>
+
+TRACE_EVENT(sync_timeline,
+ TP_PROTO(struct sync_timeline *timeline),
+
+ TP_ARGS(timeline),
+
+ TP_STRUCT__entry(
+ __string(name, timeline->name)
+ __array(char, value, 32)
+ ),
+
+ TP_fast_assign(
+ __assign_str(name, timeline->name);
+ if (timeline->ops->timeline_value_str) {
+ timeline->ops->timeline_value_str(timeline,
+ __entry->value,
+ sizeof(__entry->value));
+ } else {
+ __entry->value[0] = '\0';
+ }
+ ),
+
+ TP_printk("name=%s value=%s", __get_str(name), __entry->value)
+);
+
+TRACE_EVENT(sync_wait,
+ TP_PROTO(struct sync_fence *fence, int begin),
+
+ TP_ARGS(fence, begin),
+
+ TP_STRUCT__entry(
+ __string(name, fence->name)
+ __field(s32, status)
+ __field(u32, begin)
+ ),
+
+ TP_fast_assign(
+ __assign_str(name, fence->name);
+ __entry->status = atomic_read(&fence->status);
+ __entry->begin = begin;
+ ),
+
+ TP_printk("%s name=%s state=%d", __entry->begin ? "begin" : "end",
+ __get_str(name), __entry->status)
+);
+
+TRACE_EVENT(sync_pt,
+ TP_PROTO(struct fence *pt),
+
+ TP_ARGS(pt),
+
+ TP_STRUCT__entry(
+ __string(timeline, pt->ops->get_timeline_name(pt))
+ __array(char, value, 32)
+ ),
+
+ TP_fast_assign(
+ __assign_str(timeline, pt->ops->get_timeline_name(pt));
+ if (pt->ops->fence_value_str) {
+ pt->ops->fence_value_str(pt, __entry->value,
+ sizeof(__entry->value));
+ } else {
+ __entry->value[0] = '\0';
+ }
+ ),
+
+ TP_printk("name=%s value=%s", __get_str(timeline), __entry->value)
+);
+
+#endif /* if !defined(_TRACE_SYNC_H) || defined(TRACE_HEADER_MULTI_READ) */
+
+/* This part must be outside protection */
+#include <trace/define_trace.h>
diff --git a/drivers/staging/android/uapi/ashmem.h b/drivers/staging/android/uapi/ashmem.h
new file mode 100644
index 000000000..ba4743c71
--- /dev/null
+++ b/drivers/staging/android/uapi/ashmem.h
@@ -0,0 +1,47 @@
+/*
+ * drivers/staging/android/uapi/ashmem.h
+ *
+ * Copyright 2008 Google Inc.
+ * Author: Robert Love
+ *
+ * This file is dual licensed. It may be redistributed and/or modified
+ * under the terms of the Apache 2.0 License OR version 2 of the GNU
+ * General Public License.
+ */
+
+#ifndef _UAPI_LINUX_ASHMEM_H
+#define _UAPI_LINUX_ASHMEM_H
+
+#include <linux/ioctl.h>
+
+#define ASHMEM_NAME_LEN 256
+
+#define ASHMEM_NAME_DEF "dev/ashmem"
+
+/* Return values from ASHMEM_PIN: Was the mapping purged while unpinned? */
+#define ASHMEM_NOT_PURGED 0
+#define ASHMEM_WAS_PURGED 1
+
+/* Return values from ASHMEM_GET_PIN_STATUS: Is the mapping pinned? */
+#define ASHMEM_IS_UNPINNED 0
+#define ASHMEM_IS_PINNED 1
+
+struct ashmem_pin {
+ __u32 offset; /* offset into region, in bytes, page-aligned */
+ __u32 len; /* length forward from offset, in bytes, page-aligned */
+};
+
+#define __ASHMEMIOC 0x77
+
+#define ASHMEM_SET_NAME _IOW(__ASHMEMIOC, 1, char[ASHMEM_NAME_LEN])
+#define ASHMEM_GET_NAME _IOR(__ASHMEMIOC, 2, char[ASHMEM_NAME_LEN])
+#define ASHMEM_SET_SIZE _IOW(__ASHMEMIOC, 3, size_t)
+#define ASHMEM_GET_SIZE _IO(__ASHMEMIOC, 4)
+#define ASHMEM_SET_PROT_MASK _IOW(__ASHMEMIOC, 5, unsigned long)
+#define ASHMEM_GET_PROT_MASK _IO(__ASHMEMIOC, 6)
+#define ASHMEM_PIN _IOW(__ASHMEMIOC, 7, struct ashmem_pin)
+#define ASHMEM_UNPIN _IOW(__ASHMEMIOC, 8, struct ashmem_pin)
+#define ASHMEM_GET_PIN_STATUS _IO(__ASHMEMIOC, 9)
+#define ASHMEM_PURGE_ALL_CACHES _IO(__ASHMEMIOC, 10)
+
+#endif /* _UAPI_LINUX_ASHMEM_H */
diff --git a/drivers/staging/android/uapi/ion.h b/drivers/staging/android/uapi/ion.h
new file mode 100644
index 000000000..6aa495673
--- /dev/null
+++ b/drivers/staging/android/uapi/ion.h
@@ -0,0 +1,196 @@
+/*
+ * drivers/staging/android/uapi/ion.h
+ *
+ * Copyright (C) 2011 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ */
+
+#ifndef _UAPI_LINUX_ION_H
+#define _UAPI_LINUX_ION_H
+
+#include <linux/ioctl.h>
+#include <linux/types.h>
+
+typedef int ion_user_handle_t;
+
+/**
+ * enum ion_heap_types - list of all possible types of heaps
+ * @ION_HEAP_TYPE_SYSTEM: memory allocated via vmalloc
+ * @ION_HEAP_TYPE_SYSTEM_CONTIG: memory allocated via kmalloc
+ * @ION_HEAP_TYPE_CARVEOUT: memory allocated from a prereserved
+ * carveout heap, allocations are physically
+ * contiguous
+ * @ION_HEAP_TYPE_DMA: memory allocated via DMA API
+ * @ION_NUM_HEAPS: helper for iterating over heaps, a bit mask
+ * is used to identify the heaps, so only 32
+ * total heap types are supported
+ */
+enum ion_heap_type {
+ ION_HEAP_TYPE_SYSTEM,
+ ION_HEAP_TYPE_SYSTEM_CONTIG,
+ ION_HEAP_TYPE_CARVEOUT,
+ ION_HEAP_TYPE_CHUNK,
+ ION_HEAP_TYPE_DMA,
+ ION_HEAP_TYPE_CUSTOM, /* must be last so device specific heaps always
+ are at the end of this enum */
+ ION_NUM_HEAPS = 16,
+};
+
+#define ION_HEAP_SYSTEM_MASK (1 << ION_HEAP_TYPE_SYSTEM)
+#define ION_HEAP_SYSTEM_CONTIG_MASK (1 << ION_HEAP_TYPE_SYSTEM_CONTIG)
+#define ION_HEAP_CARVEOUT_MASK (1 << ION_HEAP_TYPE_CARVEOUT)
+#define ION_HEAP_TYPE_DMA_MASK (1 << ION_HEAP_TYPE_DMA)
+
+#define ION_NUM_HEAP_IDS (sizeof(unsigned int) * 8)
+
+/**
+ * allocation flags - the lower 16 bits are used by core ion, the upper 16
+ * bits are reserved for use by the heaps themselves.
+ */
+#define ION_FLAG_CACHED 1 /* mappings of this buffer should be
+ cached, ion will do cache
+ maintenance when the buffer is
+ mapped for dma */
+#define ION_FLAG_CACHED_NEEDS_SYNC 2 /* mappings of this buffer will created
+ at mmap time, if this is set
+ caches must be managed manually */
+
+/**
+ * DOC: Ion Userspace API
+ *
+ * create a client by opening /dev/ion
+ * most operations handled via following ioctls
+ *
+ */
+
+/**
+ * struct ion_allocation_data - metadata passed from userspace for allocations
+ * @len: size of the allocation
+ * @align: required alignment of the allocation
+ * @heap_id_mask: mask of heap ids to allocate from
+ * @flags: flags passed to heap
+ * @handle: pointer that will be populated with a cookie to use to
+ * refer to this allocation
+ *
+ * Provided by userspace as an argument to the ioctl
+ */
+struct ion_allocation_data {
+ size_t len;
+ size_t align;
+ unsigned int heap_id_mask;
+ unsigned int flags;
+ ion_user_handle_t handle;
+};
+
+/**
+ * struct ion_fd_data - metadata passed to/from userspace for a handle/fd pair
+ * @handle: a handle
+ * @fd: a file descriptor representing that handle
+ *
+ * For ION_IOC_SHARE or ION_IOC_MAP userspace populates the handle field with
+ * the handle returned from ion alloc, and the kernel returns the file
+ * descriptor to share or map in the fd field. For ION_IOC_IMPORT, userspace
+ * provides the file descriptor and the kernel returns the handle.
+ */
+struct ion_fd_data {
+ ion_user_handle_t handle;
+ int fd;
+};
+
+/**
+ * struct ion_handle_data - a handle passed to/from the kernel
+ * @handle: a handle
+ */
+struct ion_handle_data {
+ ion_user_handle_t handle;
+};
+
+/**
+ * struct ion_custom_data - metadata passed to/from userspace for a custom ioctl
+ * @cmd: the custom ioctl function to call
+ * @arg: additional data to pass to the custom ioctl, typically a user
+ * pointer to a predefined structure
+ *
+ * This works just like the regular cmd and arg fields of an ioctl.
+ */
+struct ion_custom_data {
+ unsigned int cmd;
+ unsigned long arg;
+};
+
+#define ION_IOC_MAGIC 'I'
+
+/**
+ * DOC: ION_IOC_ALLOC - allocate memory
+ *
+ * Takes an ion_allocation_data struct and returns it with the handle field
+ * populated with the opaque handle for the allocation.
+ */
+#define ION_IOC_ALLOC _IOWR(ION_IOC_MAGIC, 0, \
+ struct ion_allocation_data)
+
+/**
+ * DOC: ION_IOC_FREE - free memory
+ *
+ * Takes an ion_handle_data struct and frees the handle.
+ */
+#define ION_IOC_FREE _IOWR(ION_IOC_MAGIC, 1, struct ion_handle_data)
+
+/**
+ * DOC: ION_IOC_MAP - get a file descriptor to mmap
+ *
+ * Takes an ion_fd_data struct with the handle field populated with a valid
+ * opaque handle. Returns the struct with the fd field set to a file
+ * descriptor open in the current address space. This file descriptor
+ * can then be used as an argument to mmap.
+ */
+#define ION_IOC_MAP _IOWR(ION_IOC_MAGIC, 2, struct ion_fd_data)
+
+/**
+ * DOC: ION_IOC_SHARE - creates a file descriptor to use to share an allocation
+ *
+ * Takes an ion_fd_data struct with the handle field populated with a valid
+ * opaque handle. Returns the struct with the fd field set to a file
+ * descriptor open in the current address space. This file descriptor
+ * can then be passed to another process. The corresponding opaque handle can
+ * be retrieved via ION_IOC_IMPORT.
+ */
+#define ION_IOC_SHARE _IOWR(ION_IOC_MAGIC, 4, struct ion_fd_data)
+
+/**
+ * DOC: ION_IOC_IMPORT - imports a shared file descriptor
+ *
+ * Takes an ion_fd_data struct with the fd field populated with a valid file
+ * descriptor obtained from ION_IOC_SHARE and returns the struct with the handle
+ * filed set to the corresponding opaque handle.
+ */
+#define ION_IOC_IMPORT _IOWR(ION_IOC_MAGIC, 5, struct ion_fd_data)
+
+/**
+ * DOC: ION_IOC_SYNC - syncs a shared file descriptors to memory
+ *
+ * Deprecated in favor of using the dma_buf api's correctly (syncing
+ * will happend automatically when the buffer is mapped to a device).
+ * If necessary should be used after touching a cached buffer from the cpu,
+ * this will make the buffer in memory coherent.
+ */
+#define ION_IOC_SYNC _IOWR(ION_IOC_MAGIC, 7, struct ion_fd_data)
+
+/**
+ * DOC: ION_IOC_CUSTOM - call architecture specific ion ioctl
+ *
+ * Takes the argument of the architecture specific ioctl to call and
+ * passes appropriate userdata for that ioctl
+ */
+#define ION_IOC_CUSTOM _IOWR(ION_IOC_MAGIC, 6, struct ion_custom_data)
+
+#endif /* _UAPI_LINUX_ION_H */
diff --git a/drivers/staging/android/uapi/ion_test.h b/drivers/staging/android/uapi/ion_test.h
new file mode 100644
index 000000000..ffef06f63
--- /dev/null
+++ b/drivers/staging/android/uapi/ion_test.h
@@ -0,0 +1,70 @@
+/*
+ * drivers/staging/android/uapi/ion.h
+ *
+ * Copyright (C) 2011 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ */
+
+#ifndef _UAPI_LINUX_ION_TEST_H
+#define _UAPI_LINUX_ION_TEST_H
+
+#include <linux/ioctl.h>
+#include <linux/types.h>
+
+/**
+ * struct ion_test_rw_data - metadata passed to the kernel to read handle
+ * @ptr: a pointer to an area at least as large as size
+ * @offset: offset into the ion buffer to start reading
+ * @size: size to read or write
+ * @write: 1 to write, 0 to read
+ */
+struct ion_test_rw_data {
+ __u64 ptr;
+ __u64 offset;
+ __u64 size;
+ int write;
+ int __padding;
+};
+
+#define ION_IOC_MAGIC 'I'
+
+/**
+ * DOC: ION_IOC_TEST_SET_DMA_BUF - attach a dma buf to the test driver
+ *
+ * Attaches a dma buf fd to the test driver. Passing a second fd or -1 will
+ * release the first fd.
+ */
+#define ION_IOC_TEST_SET_FD \
+ _IO(ION_IOC_MAGIC, 0xf0)
+
+/**
+ * DOC: ION_IOC_TEST_DMA_MAPPING - read or write memory from a handle as DMA
+ *
+ * Reads or writes the memory from a handle using an uncached mapping. Can be
+ * used by unit tests to emulate a DMA engine as close as possible. Only
+ * expected to be used for debugging and testing, may not always be available.
+ */
+#define ION_IOC_TEST_DMA_MAPPING \
+ _IOW(ION_IOC_MAGIC, 0xf1, struct ion_test_rw_data)
+
+/**
+ * DOC: ION_IOC_TEST_KERNEL_MAPPING - read or write memory from a handle
+ *
+ * Reads or writes the memory from a handle using a kernel mapping. Can be
+ * used by unit tests to test heap map_kernel functions. Only expected to be
+ * used for debugging and testing, may not always be available.
+ */
+#define ION_IOC_TEST_KERNEL_MAPPING \
+ _IOW(ION_IOC_MAGIC, 0xf2, struct ion_test_rw_data)
+
+
+#endif /* _UAPI_LINUX_ION_H */
diff --git a/drivers/staging/android/uapi/sw_sync.h b/drivers/staging/android/uapi/sw_sync.h
new file mode 100644
index 000000000..9b5d48695
--- /dev/null
+++ b/drivers/staging/android/uapi/sw_sync.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2012 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ */
+
+#ifndef _UAPI_LINUX_SW_SYNC_H
+#define _UAPI_LINUX_SW_SYNC_H
+
+#include <linux/types.h>
+
+struct sw_sync_create_fence_data {
+ __u32 value;
+ char name[32];
+ __s32 fence; /* fd of new fence */
+};
+
+#define SW_SYNC_IOC_MAGIC 'W'
+
+#define SW_SYNC_IOC_CREATE_FENCE _IOWR(SW_SYNC_IOC_MAGIC, 0,\
+ struct sw_sync_create_fence_data)
+#define SW_SYNC_IOC_INC _IOW(SW_SYNC_IOC_MAGIC, 1, __u32)
+
+#endif /* _UAPI_LINUX_SW_SYNC_H */
diff --git a/drivers/staging/android/uapi/sync.h b/drivers/staging/android/uapi/sync.h
new file mode 100644
index 000000000..e964c751f
--- /dev/null
+++ b/drivers/staging/android/uapi/sync.h
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2012 Google, Inc.
+ *
+ * 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.
+ *
+ */
+
+#ifndef _UAPI_LINUX_SYNC_H
+#define _UAPI_LINUX_SYNC_H
+
+#include <linux/ioctl.h>
+#include <linux/types.h>
+
+/**
+ * struct sync_merge_data - data passed to merge ioctl
+ * @fd2: file descriptor of second fence
+ * @name: name of new fence
+ * @fence: returns the fd of the new fence to userspace
+ */
+struct sync_merge_data {
+ __s32 fd2; /* fd of second fence */
+ char name[32]; /* name of new fence */
+ __s32 fence; /* fd on newly created fence */
+};
+
+/**
+ * struct sync_pt_info - detailed sync_pt information
+ * @len: length of sync_pt_info including any driver_data
+ * @obj_name: name of parent sync_timeline
+ * @driver_name: name of driver implementing the parent
+ * @status: status of the sync_pt 0:active 1:signaled <0:error
+ * @timestamp_ns: timestamp of status change in nanoseconds
+ * @driver_data: any driver dependent data
+ */
+struct sync_pt_info {
+ __u32 len;
+ char obj_name[32];
+ char driver_name[32];
+ __s32 status;
+ __u64 timestamp_ns;
+
+ __u8 driver_data[0];
+};
+
+/**
+ * struct sync_fence_info_data - data returned from fence info ioctl
+ * @len: ioctl caller writes the size of the buffer its passing in.
+ * ioctl returns length of sync_fence_data returned to userspace
+ * including pt_info.
+ * @name: name of fence
+ * @status: status of fence. 1: signaled 0:active <0:error
+ * @pt_info: a sync_pt_info struct for every sync_pt in the fence
+ */
+struct sync_fence_info_data {
+ __u32 len;
+ char name[32];
+ __s32 status;
+
+ __u8 pt_info[0];
+};
+
+#define SYNC_IOC_MAGIC '>'
+
+/**
+ * DOC: SYNC_IOC_WAIT - wait for a fence to signal
+ *
+ * pass timeout in milliseconds. Waits indefinitely timeout < 0.
+ */
+#define SYNC_IOC_WAIT _IOW(SYNC_IOC_MAGIC, 0, __s32)
+
+/**
+ * DOC: SYNC_IOC_MERGE - merge two fences
+ *
+ * Takes a struct sync_merge_data. Creates a new fence containing copies of
+ * the sync_pts in both the calling fd and sync_merge_data.fd2. Returns the
+ * new fence's fd in sync_merge_data.fence
+ */
+#define SYNC_IOC_MERGE _IOWR(SYNC_IOC_MAGIC, 1, struct sync_merge_data)
+
+/**
+ * DOC: SYNC_IOC_FENCE_INFO - get detailed information on a fence
+ *
+ * Takes a struct sync_fence_info_data with extra space allocated for pt_info.
+ * Caller should write the size of the buffer into len. On return, len is
+ * updated to reflect the total size of the sync_fence_info_data including
+ * pt_info.
+ *
+ * pt_info is a buffer containing sync_pt_infos for every sync_pt in the fence.
+ * To iterate over the sync_pt_infos, use the sync_pt_info.len field.
+ */
+#define SYNC_IOC_FENCE_INFO _IOWR(SYNC_IOC_MAGIC, 2,\
+ struct sync_fence_info_data)
+
+#endif /* _UAPI_LINUX_SYNC_H */
diff --git a/drivers/staging/board/Kconfig b/drivers/staging/board/Kconfig
new file mode 100644
index 000000000..0a89ad163
--- /dev/null
+++ b/drivers/staging/board/Kconfig
@@ -0,0 +1,9 @@
+config STAGING_BOARD
+ bool "Staging Board Support"
+ depends on OF_ADDRESS
+ depends on BROKEN
+ help
+ Select to enable per-board staging support code.
+
+ If in doubt, say N here.
+
diff --git a/drivers/staging/board/Makefile b/drivers/staging/board/Makefile
new file mode 100644
index 000000000..65d39ecfa
--- /dev/null
+++ b/drivers/staging/board/Makefile
@@ -0,0 +1,2 @@
+obj-y := board.o
+obj-$(CONFIG_ARCH_EMEV2) += kzm9d.o
diff --git a/drivers/staging/board/TODO b/drivers/staging/board/TODO
new file mode 100644
index 000000000..8db70e10a
--- /dev/null
+++ b/drivers/staging/board/TODO
@@ -0,0 +1,2 @@
+* replace platform device code with DT nodes once the driver supports DT
+* remove staging board code when no more platform devices are needed
diff --git a/drivers/staging/board/board.c b/drivers/staging/board/board.c
new file mode 100644
index 000000000..d5a6abc84
--- /dev/null
+++ b/drivers/staging/board/board.c
@@ -0,0 +1,40 @@
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include "board.h"
+
+static bool find_by_address(u64 base_address)
+{
+ struct device_node *dn = of_find_all_nodes(NULL);
+ struct resource res;
+
+ while (dn) {
+ if (!of_address_to_resource(dn, 0, &res)) {
+ if (res.start == base_address) {
+ of_node_put(dn);
+ return true;
+ }
+ }
+ dn = of_find_all_nodes(dn);
+ }
+
+ return false;
+}
+
+bool __init board_staging_dt_node_available(const struct resource *resource,
+ unsigned int num_resources)
+{
+ unsigned int i;
+
+ for (i = 0; i < num_resources; i++) {
+ const struct resource *r = resource + i;
+
+ if (resource_type(r) == IORESOURCE_MEM)
+ if (find_by_address(r->start))
+ return true; /* DT node available */
+ }
+
+ return false; /* Nothing found */
+}
diff --git a/drivers/staging/board/board.h b/drivers/staging/board/board.h
new file mode 100644
index 000000000..2390ed6c3
--- /dev/null
+++ b/drivers/staging/board/board.h
@@ -0,0 +1,20 @@
+#ifndef __BOARD_H__
+#define __BOARD_H__
+#include <linux/init.h>
+#include <linux/of.h>
+
+bool board_staging_dt_node_available(const struct resource *resource,
+ unsigned int num_resources);
+
+#define board_staging(str, fn) \
+static int __init runtime_board_check(void) \
+{ \
+ if (of_machine_is_compatible(str)) \
+ fn(); \
+ \
+ return 0; \
+} \
+ \
+late_initcall(runtime_board_check)
+
+#endif /* __BOARD_H__ */
diff --git a/drivers/staging/board/kzm9d.c b/drivers/staging/board/kzm9d.c
new file mode 100644
index 000000000..533f3026e
--- /dev/null
+++ b/drivers/staging/board/kzm9d.c
@@ -0,0 +1,19 @@
+/* Staging board support for KZM9D. Enable not-yet-DT-capable devices here. */
+
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include "board.h"
+
+static const struct resource usbs1_res[] __initconst = {
+ DEFINE_RES_MEM(0xe2800000, 0x2000),
+ DEFINE_RES_IRQ(159),
+};
+
+static void __init kzm9d_init(void)
+{
+ if (!board_staging_dt_node_available(usbs1_res, ARRAY_SIZE(usbs1_res)))
+ platform_device_register_simple("emxx_udc", -1, usbs1_res,
+ ARRAY_SIZE(usbs1_res));
+}
+
+board_staging("renesas,kzm9d", kzm9d_init);
diff --git a/drivers/staging/clocking-wizard/Kconfig b/drivers/staging/clocking-wizard/Kconfig
new file mode 100644
index 000000000..357af02c5
--- /dev/null
+++ b/drivers/staging/clocking-wizard/Kconfig
@@ -0,0 +1,9 @@
+#
+# Xilinx Clocking Wizard Driver
+#
+
+config COMMON_CLK_XLNX_CLKWZRD
+ tristate "Xilinx Clocking Wizard"
+ depends on COMMON_CLK && OF
+ ---help---
+ Support for the Xilinx Clocking Wizard IP core clock generator.
diff --git a/drivers/staging/clocking-wizard/Makefile b/drivers/staging/clocking-wizard/Makefile
new file mode 100644
index 000000000..5ad352f52
--- /dev/null
+++ b/drivers/staging/clocking-wizard/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_COMMON_CLK_XLNX_CLKWZRD) += clk-xlnx-clock-wizard.o
diff --git a/drivers/staging/clocking-wizard/TODO b/drivers/staging/clocking-wizard/TODO
new file mode 100644
index 000000000..ebe99db7d
--- /dev/null
+++ b/drivers/staging/clocking-wizard/TODO
@@ -0,0 +1,12 @@
+TODO:
+ - support for fractional multiplier
+ - support for fractional divider (output 0 only)
+ - support for set_rate() operations (may benefit from Stephen Boyd's
+ refactoring of the clk primitives: https://lkml.org/lkml/2014/9/5/766)
+ - review arithmetic
+ - overflow after multiplication?
+ - maximize accuracy before divisions
+
+Patches to:
+ Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+ Sören Brinkmann <soren.brinkmann@xilinx.com>
diff --git a/drivers/staging/clocking-wizard/clk-xlnx-clock-wizard.c b/drivers/staging/clocking-wizard/clk-xlnx-clock-wizard.c
new file mode 100644
index 000000000..5455bf3d5
--- /dev/null
+++ b/drivers/staging/clocking-wizard/clk-xlnx-clock-wizard.c
@@ -0,0 +1,344 @@
+/*
+ * Xilinx 'Clocking Wizard' driver
+ *
+ * Copyright (C) 2013 - 2014 Xilinx
+ *
+ * Sören Brinkmann <soren.brinkmann@xilinx.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License v2 as published by
+ * the Free Software Foundation.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/platform_device.h>
+#include <linux/clk-provider.h>
+#include <linux/slab.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/module.h>
+#include <linux/err.h>
+
+#define WZRD_NUM_OUTPUTS 7
+#define WZRD_ACLK_MAX_FREQ 250000000UL
+
+#define WZRD_CLK_CFG_REG(n) (0x200 + 4 * (n))
+
+#define WZRD_CLkOUT0_FRAC_EN BIT(18)
+#define WZRD_CLkFBOUT_FRAC_EN BIT(26)
+
+#define WZRD_CLKFBOUT_MULT_SHIFT 8
+#define WZRD_CLKFBOUT_MULT_MASK (0xff << WZRD_CLKFBOUT_MULT_SHIFT)
+#define WZRD_DIVCLK_DIVIDE_SHIFT 0
+#define WZRD_DIVCLK_DIVIDE_MASK (0xff << WZRD_DIVCLK_DIVIDE_SHIFT)
+#define WZRD_CLKOUT_DIVIDE_SHIFT 0
+#define WZRD_CLKOUT_DIVIDE_MASK (0xff << WZRD_DIVCLK_DIVIDE_SHIFT)
+
+enum clk_wzrd_int_clks {
+ wzrd_clk_mul,
+ wzrd_clk_mul_div,
+ wzrd_clk_int_max
+};
+
+/**
+ * struct clk_wzrd:
+ * @clk_data: Clock data
+ * @nb: Notifier block
+ * @base: Memory base
+ * @clk_in1: Handle to input clock 'clk_in1'
+ * @axi_clk: Handle to input clock 's_axi_aclk'
+ * @clks_internal: Internal clocks
+ * @clkout: Output clocks
+ * @speed_grade: Speed grade of the device
+ * @suspended: Flag indicating power state of the device
+ */
+struct clk_wzrd {
+ struct clk_onecell_data clk_data;
+ struct notifier_block nb;
+ void __iomem *base;
+ struct clk *clk_in1;
+ struct clk *axi_clk;
+ struct clk *clks_internal[wzrd_clk_int_max];
+ struct clk *clkout[WZRD_NUM_OUTPUTS];
+ int speed_grade;
+ bool suspended;
+};
+#define to_clk_wzrd(_nb) container_of(_nb, struct clk_wzrd, nb)
+
+/* maximum frequencies for input/output clocks per speed grade */
+static const unsigned long clk_wzrd_max_freq[] = {
+ 800000000UL,
+ 933000000UL,
+ 1066000000UL
+};
+
+static int clk_wzrd_clk_notifier(struct notifier_block *nb, unsigned long event,
+ void *data)
+{
+ unsigned long max;
+ struct clk_notifier_data *ndata = data;
+ struct clk_wzrd *clk_wzrd = to_clk_wzrd(nb);
+
+ if (clk_wzrd->suspended)
+ return NOTIFY_OK;
+
+ if (ndata->clk == clk_wzrd->clk_in1)
+ max = clk_wzrd_max_freq[clk_wzrd->speed_grade - 1];
+ else if (ndata->clk == clk_wzrd->axi_clk)
+ max = WZRD_ACLK_MAX_FREQ;
+ else
+ return NOTIFY_DONE; /* should never happen */
+
+ switch (event) {
+ case PRE_RATE_CHANGE:
+ if (ndata->new_rate > max)
+ return NOTIFY_BAD;
+ return NOTIFY_OK;
+ case POST_RATE_CHANGE:
+ case ABORT_RATE_CHANGE:
+ default:
+ return NOTIFY_DONE;
+ }
+}
+
+static int __maybe_unused clk_wzrd_suspend(struct device *dev)
+{
+ struct clk_wzrd *clk_wzrd = dev_get_drvdata(dev);
+
+ clk_disable_unprepare(clk_wzrd->axi_clk);
+ clk_wzrd->suspended = true;
+
+ return 0;
+}
+
+static int __maybe_unused clk_wzrd_resume(struct device *dev)
+{
+ int ret;
+ struct clk_wzrd *clk_wzrd = dev_get_drvdata(dev);
+
+ ret = clk_prepare_enable(clk_wzrd->axi_clk);
+ if (ret) {
+ dev_err(dev, "unable to enable s_axi_aclk\n");
+ return ret;
+ }
+
+ clk_wzrd->suspended = false;
+
+ return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(clk_wzrd_dev_pm_ops, clk_wzrd_suspend,
+ clk_wzrd_resume);
+
+static int clk_wzrd_probe(struct platform_device *pdev)
+{
+ int i, ret;
+ u32 reg;
+ unsigned long rate;
+ const char *clk_name;
+ struct clk_wzrd *clk_wzrd;
+ struct resource *mem;
+ struct device_node *np = pdev->dev.of_node;
+
+ clk_wzrd = devm_kzalloc(&pdev->dev, sizeof(*clk_wzrd), GFP_KERNEL);
+ if (!clk_wzrd)
+ return -ENOMEM;
+ platform_set_drvdata(pdev, clk_wzrd);
+
+ mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ clk_wzrd->base = devm_ioremap_resource(&pdev->dev, mem);
+ if (IS_ERR(clk_wzrd->base))
+ return PTR_ERR(clk_wzrd->base);
+
+ ret = of_property_read_u32(np, "speed-grade", &clk_wzrd->speed_grade);
+ if (!ret) {
+ if (clk_wzrd->speed_grade < 1 || clk_wzrd->speed_grade > 3) {
+ dev_warn(&pdev->dev, "invalid speed grade '%d'\n",
+ clk_wzrd->speed_grade);
+ clk_wzrd->speed_grade = 0;
+ }
+ }
+
+ clk_wzrd->clk_in1 = devm_clk_get(&pdev->dev, "clk_in1");
+ if (IS_ERR(clk_wzrd->clk_in1)) {
+ if (clk_wzrd->clk_in1 != ERR_PTR(-EPROBE_DEFER))
+ dev_err(&pdev->dev, "clk_in1 not found\n");
+ return PTR_ERR(clk_wzrd->clk_in1);
+ }
+
+ clk_wzrd->axi_clk = devm_clk_get(&pdev->dev, "s_axi_aclk");
+ if (IS_ERR(clk_wzrd->axi_clk)) {
+ if (clk_wzrd->axi_clk != ERR_PTR(-EPROBE_DEFER))
+ dev_err(&pdev->dev, "s_axi_aclk not found\n");
+ return PTR_ERR(clk_wzrd->axi_clk);
+ }
+ ret = clk_prepare_enable(clk_wzrd->axi_clk);
+ if (ret) {
+ dev_err(&pdev->dev, "enabling s_axi_aclk failed\n");
+ return ret;
+ }
+ rate = clk_get_rate(clk_wzrd->axi_clk);
+ if (rate > WZRD_ACLK_MAX_FREQ) {
+ dev_err(&pdev->dev, "s_axi_aclk frequency (%lu) too high\n",
+ rate);
+ ret = -EINVAL;
+ goto err_disable_clk;
+ }
+
+ /* we don't support fractional div/mul yet */
+ reg = readl(clk_wzrd->base + WZRD_CLK_CFG_REG(0)) &
+ WZRD_CLkFBOUT_FRAC_EN;
+ reg |= readl(clk_wzrd->base + WZRD_CLK_CFG_REG(2)) &
+ WZRD_CLkOUT0_FRAC_EN;
+ if (reg)
+ dev_warn(&pdev->dev, "fractional div/mul not supported\n");
+
+ /* register multiplier */
+ reg = (readl(clk_wzrd->base + WZRD_CLK_CFG_REG(0)) &
+ WZRD_CLKFBOUT_MULT_MASK) >> WZRD_CLKFBOUT_MULT_SHIFT;
+ clk_name = kasprintf(GFP_KERNEL, "%s_mul", dev_name(&pdev->dev));
+ if (!clk_name) {
+ ret = -ENOMEM;
+ goto err_disable_clk;
+ }
+ clk_wzrd->clks_internal[wzrd_clk_mul] = clk_register_fixed_factor(
+ &pdev->dev, clk_name,
+ __clk_get_name(clk_wzrd->clk_in1),
+ 0, reg, 1);
+ kfree(clk_name);
+ if (IS_ERR(clk_wzrd->clks_internal[wzrd_clk_mul])) {
+ dev_err(&pdev->dev, "unable to register fixed-factor clock\n");
+ ret = PTR_ERR(clk_wzrd->clks_internal[wzrd_clk_mul]);
+ goto err_disable_clk;
+ }
+
+ /* register div */
+ reg = (readl(clk_wzrd->base + WZRD_CLK_CFG_REG(0)) &
+ WZRD_DIVCLK_DIVIDE_MASK) >> WZRD_DIVCLK_DIVIDE_SHIFT;
+ clk_name = kasprintf(GFP_KERNEL, "%s_mul_div", dev_name(&pdev->dev));
+ if (!clk_name) {
+ ret = -ENOMEM;
+ goto err_rm_int_clk;
+ }
+
+ clk_wzrd->clks_internal[wzrd_clk_mul_div] = clk_register_fixed_factor(
+ &pdev->dev, clk_name,
+ __clk_get_name(clk_wzrd->clks_internal[wzrd_clk_mul]),
+ 0, 1, reg);
+ if (IS_ERR(clk_wzrd->clks_internal[wzrd_clk_mul_div])) {
+ dev_err(&pdev->dev, "unable to register divider clock\n");
+ ret = PTR_ERR(clk_wzrd->clks_internal[wzrd_clk_mul_div]);
+ goto err_rm_int_clk;
+ }
+
+ /* register div per output */
+ for (i = WZRD_NUM_OUTPUTS - 1; i >= 0 ; i--) {
+ const char *clkout_name;
+
+ if (of_property_read_string_index(np, "clock-output-names", i,
+ &clkout_name)) {
+ dev_err(&pdev->dev,
+ "clock output name not specified\n");
+ ret = -EINVAL;
+ goto err_rm_int_clks;
+ }
+ reg = readl(clk_wzrd->base + WZRD_CLK_CFG_REG(2) + i * 12);
+ reg &= WZRD_CLKOUT_DIVIDE_MASK;
+ reg >>= WZRD_CLKOUT_DIVIDE_SHIFT;
+ clk_wzrd->clkout[i] = clk_register_fixed_factor(&pdev->dev,
+ clkout_name, clk_name, 0, 1, reg);
+ if (IS_ERR(clk_wzrd->clkout[i])) {
+ int j;
+
+ for (j = i + 1; j < WZRD_NUM_OUTPUTS; j++)
+ clk_unregister(clk_wzrd->clkout[j]);
+ dev_err(&pdev->dev,
+ "unable to register divider clock\n");
+ ret = PTR_ERR(clk_wzrd->clkout[i]);
+ goto err_rm_int_clks;
+ }
+ }
+
+ kfree(clk_name);
+
+ clk_wzrd->clk_data.clks = clk_wzrd->clkout;
+ clk_wzrd->clk_data.clk_num = ARRAY_SIZE(clk_wzrd->clkout);
+ of_clk_add_provider(np, of_clk_src_onecell_get, &clk_wzrd->clk_data);
+
+ if (clk_wzrd->speed_grade) {
+ clk_wzrd->nb.notifier_call = clk_wzrd_clk_notifier;
+
+ ret = clk_notifier_register(clk_wzrd->clk_in1,
+ &clk_wzrd->nb);
+ if (ret)
+ dev_warn(&pdev->dev,
+ "unable to register clock notifier\n");
+
+ ret = clk_notifier_register(clk_wzrd->axi_clk, &clk_wzrd->nb);
+ if (ret)
+ dev_warn(&pdev->dev,
+ "unable to register clock notifier\n");
+ }
+
+ return 0;
+
+err_rm_int_clks:
+ clk_unregister(clk_wzrd->clks_internal[1]);
+err_rm_int_clk:
+ kfree(clk_name);
+ clk_unregister(clk_wzrd->clks_internal[0]);
+err_disable_clk:
+ clk_disable_unprepare(clk_wzrd->axi_clk);
+
+ return ret;
+}
+
+static int clk_wzrd_remove(struct platform_device *pdev)
+{
+ int i;
+ struct clk_wzrd *clk_wzrd = platform_get_drvdata(pdev);
+
+ of_clk_del_provider(pdev->dev.of_node);
+
+ for (i = 0; i < WZRD_NUM_OUTPUTS; i++)
+ clk_unregister(clk_wzrd->clkout[i]);
+ for (i = 0; i < wzrd_clk_int_max; i++)
+ clk_unregister(clk_wzrd->clks_internal[i]);
+
+ if (clk_wzrd->speed_grade) {
+ clk_notifier_unregister(clk_wzrd->axi_clk, &clk_wzrd->nb);
+ clk_notifier_unregister(clk_wzrd->clk_in1, &clk_wzrd->nb);
+ }
+
+ clk_disable_unprepare(clk_wzrd->axi_clk);
+
+ return 0;
+}
+
+static const struct of_device_id clk_wzrd_ids[] = {
+ { .compatible = "xlnx,clocking-wizard" },
+ { },
+};
+MODULE_DEVICE_TABLE(of, clk_wzrd_ids);
+
+static struct platform_driver clk_wzrd_driver = {
+ .driver = {
+ .name = "clk-wizard",
+ .of_match_table = clk_wzrd_ids,
+ .pm = &clk_wzrd_dev_pm_ops,
+ },
+ .probe = clk_wzrd_probe,
+ .remove = clk_wzrd_remove,
+};
+module_platform_driver(clk_wzrd_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Soeren Brinkmann <soren.brinkmann@xilinx.com");
+MODULE_DESCRIPTION("Driver for the Xilinx Clocking Wizard IP core");
diff --git a/drivers/staging/clocking-wizard/dt-binding.txt b/drivers/staging/clocking-wizard/dt-binding.txt
new file mode 100644
index 000000000..723271e93
--- /dev/null
+++ b/drivers/staging/clocking-wizard/dt-binding.txt
@@ -0,0 +1,30 @@
+Binding for Xilinx Clocking Wizard IP Core
+
+This binding uses the common clock binding[1]. Details about the devices can be
+found in the product guide[2].
+
+[1] Documentation/devicetree/bindings/clock/clock-bindings.txt
+[2] Clocking Wizard Product Guide
+http://www.xilinx.com/support/documentation/ip_documentation/clk_wiz/v5_1/pg065-clk-wiz.pdf
+
+Required properties:
+ - compatible: Must be 'xlnx,clocking-wizard'
+ - reg: Base and size of the cores register space
+ - clocks: Handle to input clock
+ - clock-names: Tuple containing 'clk_in1' and 's_axi_aclk'
+ - clock-output-names: Names for the output clocks
+
+Optional properties:
+ - speed-grade: Speed grade of the device (valid values are 1..3)
+
+Example:
+ clock-generator@40040000 {
+ reg = <0x40040000 0x1000>;
+ compatible = "xlnx,clocking-wizard";
+ speed-grade = <1>;
+ clock-names = "clk_in1", "s_axi_aclk";
+ clocks = <&clkc 15>, <&clkc 15>;
+ clock-output-names = "clk_out0", "clk_out1", "clk_out2",
+ "clk_out3", "clk_out4", "clk_out5",
+ "clk_out6", "clk_out7";
+ };
diff --git a/drivers/staging/comedi/Kconfig b/drivers/staging/comedi/Kconfig
new file mode 100644
index 000000000..61c6351f5
--- /dev/null
+++ b/drivers/staging/comedi/Kconfig
@@ -0,0 +1,1306 @@
+config COMEDI
+ tristate "Data acquisition support (comedi)"
+ depends on m
+ ---help---
+ Enable support a wide range of data acquisition devices
+ for Linux.
+
+if COMEDI
+
+config COMEDI_DEBUG
+ bool "Comedi debugging"
+ ---help---
+ This is an option for use by developers; most people should
+ say N here. This enables comedi core and driver debugging.
+
+config COMEDI_DEFAULT_BUF_SIZE_KB
+ int "Comedi default initial asynchronous buffer size in KiB"
+ default "2048"
+ ---help---
+ This is the default asynchronous buffer size which is used for
+ commands running in the background in kernel space. This
+ defaults to 2048 KiB of memory so that a 16 channel card
+ running at 10 kHz has of 2-4 seconds of buffer.
+
+config COMEDI_DEFAULT_BUF_MAXSIZE_KB
+ int "Comedi default maximum asynchronous buffer size in KiB"
+ default "20480"
+ ---help---
+ This is the default maximum asynchronous buffer size which can
+ be requested by a userspace program without root privileges.
+ This is set to 20480 KiB so that a fast I/O card with 16
+ channels running at 100 kHz has 2-4 seconds of buffer.
+
+menuconfig COMEDI_MISC_DRIVERS
+ bool "Comedi misc drivers"
+ ---help---
+ Enable comedi misc drivers to be built
+
+ Note that the answer to this question won't directly affect the
+ kernel: saying N will just cause the configurator to skip all
+ the questions about misc non-hardware comedi drivers.
+
+if COMEDI_MISC_DRIVERS
+
+config COMEDI_BOND
+ tristate "Comedi device bonding support"
+ select COMEDI_KCOMEDILIB
+ ---help---
+ Enable support for a driver to 'bond' (merge) multiple subdevices
+ from multiple devices together as one.
+
+ Currently, it only handles digital I/O subdevices.
+
+ To compile this driver as a module, choose M here: the module will be
+ called comedi_bond.
+
+config COMEDI_TEST
+ tristate "Fake waveform generator support"
+ ---help---
+ Enable support for the fake waveform generator.
+ This driver is mainly for testing purposes, but can also be used to
+ generate sample waveforms on systems that don't have data acquisition
+ hardware.
+
+ To compile this driver as a module, choose M here: the module will be
+ called comedi_test.
+
+config COMEDI_PARPORT
+ tristate "Parallel port support"
+ ---help---
+ Enable support for the standard parallel port.
+ A cheap and easy way to get a few more digital I/O lines. Steal
+ additional parallel ports from old computers or your neighbors'
+ computers.
+
+ To compile this driver as a module, choose M here: the module will be
+ called comedi_parport.
+
+config COMEDI_SERIAL2002
+ tristate "Driver for serial connected hardware"
+ ---help---
+ Enable support for serial connected hardware
+
+ To compile this driver as a module, choose M here: the module will be
+ called serial2002.
+
+config COMEDI_SSV_DNP
+ tristate "SSV Embedded Systems DIL/Net-PC support"
+ depends on X86_32 || COMPILE_TEST
+ ---help---
+ Enable support for SSV Embedded Systems DIL/Net-PC
+
+ To compile this driver as a module, choose M here: the module will be
+ called ssv_dnp.
+
+endif # COMEDI_MISC_DRIVERS
+
+menuconfig COMEDI_ISA_DRIVERS
+ bool "Comedi ISA and PC/104 drivers"
+ ---help---
+ Enable comedi ISA and PC/104 drivers to be built
+
+ Note that the answer to this question won't directly affect the
+ kernel: saying N will just cause the configurator to skip all
+ the questions about ISA and PC/104 comedi drivers.
+
+if COMEDI_ISA_DRIVERS
+
+config COMEDI_PCL711
+ tristate "Advantech PCL-711/711b and ADlink ACL-8112 ISA card support"
+ select COMEDI_8254
+ ---help---
+ Enable support for Advantech PCL-711 and 711b, ADlink ACL-8112
+
+ To compile this driver as a module, choose M here: the module will be
+ called pcl711.
+
+config COMEDI_PCL724
+ tristate "Advantech PCL-722/724/731 and ADlink ACL-7122/7124/PET-48DIO"
+ select COMEDI_8255
+ ---help---
+ Enable support for ISA and PC/104 based 8255 digital i/o boards. This
+ driver provides a legacy comedi driver wrapper for the generic 8255
+ support driver.
+
+ Supported boards include:
+ Advantech PCL-724 24 channels
+ Advantech PCL-722 144 (or 96) channels
+ Advantech PCL-731 48 channels
+ ADlink ACL-7122 144 (or 96) channels
+ ADlink ACL-7124 24 channels
+ ADlink PET-48DIO 48 channels
+ WinSystems PCM-IO48 48 channels (PC/104)
+ Diamond Systems ONYX-MM-DIO 48 channels (PC/104)
+
+ To compile this driver as a module, choose M here: the module will be
+ called pcl724.
+
+config COMEDI_PCL726
+ tristate "Advantech PCL-726 and compatible ISA card support"
+ ---help---
+ Enable support for Advantech PCL-726 and compatible ISA cards.
+
+ To compile this driver as a module, choose M here: the module will be
+ called pcl726.
+
+config COMEDI_PCL730
+ tristate "Simple Digital I/O board support (8-bit ports)"
+ ---help---
+ Enable support for various simple ISA or PC/104 Digital I/O boards.
+ These boards all use 8-bit I/O ports.
+
+ Advantech PCL-730 iso - 16 in/16 out ttl - 16 in/16 out
+ ICP ISO-730 iso - 16 in/16 out ttl - 16 in/16 out
+ ADlink ACL-7130 iso - 16 in/16 out ttl - 16 in/16 out
+ Advantech PCM-3730 iso - 8 in/8 out ttl - 16 in/16 out
+ Advantech PCL-725 iso - 8 in/8 out
+ ICP P8R8-DIO iso - 8 in/8 out
+ ADlink ACL-7225b iso - 16 in/16 out
+ ICP P16R16-DIO iso - 16 in/16 out
+ Advantech PCL-733 iso - 32 in
+ Advantech PCL-734 iso - 32 out
+ Diamond Systems OPMM-1616-XT iso - 16 in/16 out
+ Diamond Systems PEARL-MM-P iso - 16 out
+ Diamond Systems IR104-PBF iso - 20 in/20 out
+
+ To compile this driver as a module, choose M here: the module will be
+ called pcl730.
+
+config COMEDI_PCL812
+ tristate "Advantech PCL-812/813 and ADlink ACL-8112/8113/8113/8216"
+ select COMEDI_ISADMA if ISA_DMA_API
+ select COMEDI_8254
+ ---help---
+ Enable support for Advantech PCL-812/PG, PCL-813/B, ADLink
+ ACL-8112DG/HG/PG, ACL-8113, ACL-8216, ICP DAS A-821PGH/PGL/PGL-NDA,
+ A-822PGH/PGL, A-823PGH/PGL, A-826PG and ICP DAS ISO-813 ISA cards
+
+ To compile this driver as a module, choose M here: the module will be
+ called pcl812.
+
+config COMEDI_PCL816
+ tristate "Advantech PCL-814 and PCL-816 ISA card support"
+ select COMEDI_ISADMA if ISA_DMA_API
+ select COMEDI_8254
+ ---help---
+ Enable support for Advantech PCL-814 and PCL-816 ISA cards
+
+ To compile this driver as a module, choose M here: the module will be
+ called pcl816.
+
+config COMEDI_PCL818
+ tristate "Advantech PCL-718 and PCL-818 ISA card support"
+ select COMEDI_ISADMA if ISA_DMA_API
+ select COMEDI_8254
+ ---help---
+ Enable support for Advantech PCL-818 ISA cards
+ PCL-818L, PCL-818H, PCL-818HD, PCL-818HG, PCL-818 and PCL-718
+
+ To compile this driver as a module, choose M here: the module will be
+ called pcl818.
+
+config COMEDI_PCM3724
+ tristate "Advantech PCM-3724 PC/104 card support"
+ select COMEDI_8255
+ ---help---
+ Enable support for Advantech PCM-3724 PC/104 cards.
+
+ To compile this driver as a module, choose M here: the module will be
+ called pcm3724.
+
+config COMEDI_AMPLC_DIO200_ISA
+ tristate "Amplicon PC212E/PC214E/PC215E/PC218E/PC272E"
+ select COMEDI_AMPLC_DIO200
+ ---help---
+ Enable support for Amplicon PC212E, PC214E, PC215E, PC218E and
+ PC272E ISA DIO boards
+
+ To compile this driver as a module, choose M here: the module will be
+ called amplc_dio200.
+
+config COMEDI_AMPLC_PC236_ISA
+ tristate "Amplicon PC36AT DIO board support"
+ select COMEDI_AMPLC_PC236
+ ---help---
+ Enable support for Amplicon PC36AT ISA DIO board.
+
+ To compile this driver as a module, choose M here: the module will be
+ called amplc_pc236.
+
+config COMEDI_AMPLC_PC263_ISA
+ tristate "Amplicon PC263 relay board support"
+ ---help---
+ Enable support for Amplicon PC263 ISA relay board. This board has
+ 16 reed relay output channels.
+
+ To compile this driver as a module, choose M here: the module will be
+ called amplc_pc263.
+
+config COMEDI_RTI800
+ tristate "Analog Devices RTI-800/815 ISA card support"
+ ---help---
+ Enable support for Analog Devices RTI-800/815 ISA cards
+
+ To compile this driver as a module, choose M here: the module will be
+ called rti800.
+
+config COMEDI_RTI802
+ tristate "Analog Devices RTI-802 ISA card support"
+ ---help---
+ Enable support for Analog Devices RTI-802 ISA cards
+
+ To compile this driver as a module, choose M here: the module will be
+ called rti802.
+
+config COMEDI_DAC02
+ tristate "Keithley Metrabyte DAC02 compatible ISA card support"
+ ---help---
+ Enable support for Keithley Metrabyte DAC02 compatible ISA cards.
+
+ To compile this driver as a module, choose M here: the module will be
+ called dac02.
+
+config COMEDI_DAS16M1
+ tristate "MeasurementComputing CIO-DAS16/M1DAS-16 ISA card support"
+ select COMEDI_8254
+ select COMEDI_8255
+ ---help---
+ Enable support for Measurement Computing CIO-DAS16/M1 ISA cards.
+
+ To compile this driver as a module, choose M here: the module will be
+ called das16m1.
+
+config COMEDI_DAS08_ISA
+ tristate "DAS-08 compatible ISA and PC/104 card support"
+ select COMEDI_DAS08
+ ---help---
+ Enable support for Keithley Metrabyte/ComputerBoards DAS08
+ and compatible ISA and PC/104 cards:
+ Keithley Metrabyte/ComputerBoards DAS08, DAS08-PGM, DAS08-PGH,
+ DAS08-PGL, DAS08-AOH, DAS08-AOL, DAS08-AOM, DAS08/JR-AO,
+ DAS08/JR-16-AO, PC104-DAS08, DAS08/JR/16.
+
+ To compile this driver as a module, choose M here: the module will be
+ called das08_isa.
+
+config COMEDI_DAS16
+ tristate "DAS-16 compatible ISA and PC/104 card support"
+ select COMEDI_ISADMA if ISA_DMA_API
+ select COMEDI_8254
+ select COMEDI_8255
+ ---help---
+ Enable support for Keithley Metrabyte/ComputerBoards DAS16
+ and compatible ISA and PC/104 cards:
+ Keithley Metrabyte DAS-16, DAS-16G, DAS-16F, DAS-1201, DAS-1202,
+ DAS-1401, DAS-1402, DAS-1601, DAS-1602 and
+ ComputerBoards/MeasurementComputing PC104-DAS16/JR/,
+ PC104-DAS16JR/16, CIO-DAS16JR/16, CIO-DAS16/JR, CIO-DAS1401/12,
+ CIO-DAS1402/12, CIO-DAS1402/16, CIO-DAS1601/12, CIO-DAS1602/12,
+ CIO-DAS1602/16, CIO-DAS16/330
+
+ To compile this driver as a module, choose M here: the module will be
+ called das16.
+
+config COMEDI_DAS800
+ tristate "DAS800 and compatible ISA card support"
+ select COMEDI_8254
+ ---help---
+ Enable support for Keithley Metrabyte DAS800 and compatible ISA cards
+ Keithley Metrabyte DAS-800, DAS-801, DAS-802
+ Measurement Computing CIO-DAS800, CIO-DAS801, CIO-DAS802 and
+ CIO-DAS802/16
+
+ To compile this driver as a module, choose M here: the module will be
+ called das800.
+
+config COMEDI_DAS1800
+ tristate "DAS1800 and compatible ISA card support"
+ select COMEDI_ISADMA if ISA_DMA_API
+ select COMEDI_8254
+ ---help---
+ Enable support for DAS1800 and compatible ISA cards
+ Keithley Metrabyte DAS-1701ST, DAS-1701ST-DA, DAS-1701/AO,
+ DAS-1702ST, DAS-1702ST-DA, DAS-1702HR, DAS-1702HR-DA, DAS-1702/AO,
+ DAS-1801ST, DAS-1801ST-DA, DAS-1801HC, DAS-1801AO, DAS-1802ST,
+ DAS-1802ST-DA, DAS-1802HR, DAS-1802HR-DA, DAS-1802HC and
+ DAS-1802AO
+
+ To compile this driver as a module, choose M here: the module will be
+ called das1800.
+
+config COMEDI_DAS6402
+ tristate "DAS6402 and compatible ISA card support"
+ select COMEDI_8254
+ ---help---
+ Enable support for DAS6402 and compatible ISA cards
+ Computerboards, Keithley Metrabyte DAS6402 and compatibles
+
+ To compile this driver as a module, choose M here: the module will be
+ called das6402.
+
+config COMEDI_DT2801
+ tristate "Data Translation DT2801 ISA card support"
+ ---help---
+ Enable support for Data Translation DT2801 ISA cards
+
+ To compile this driver as a module, choose M here: the module will be
+ called dt2801.
+
+config COMEDI_DT2811
+ tristate "Data Translation DT2811 ISA card support"
+ ---help---
+ Enable support for Data Translation DT2811 ISA cards
+
+ To compile this driver as a module, choose M here: the module will be
+ called dt2811.
+
+config COMEDI_DT2814
+ tristate "Data Translation DT2814 ISA card support"
+ ---help---
+ Enable support for Data Translation DT2814 ISA cards
+
+ To compile this driver as a module, choose M here: the module will be
+ called dt2814.
+
+config COMEDI_DT2815
+ tristate "Data Translation DT2815 ISA card support"
+ ---help---
+ Enable support for Data Translation DT2815 ISA cards
+
+ To compile this driver as a module, choose M here: the module will be
+ called dt2815.
+
+config COMEDI_DT2817
+ tristate "Data Translation DT2817 ISA card support"
+ ---help---
+ Enable support for Data Translation DT2817 ISA cards
+
+ To compile this driver as a module, choose M here: the module will be
+ called dt2817.
+
+config COMEDI_DT282X
+ tristate "Data Translation DT2821 series and DT-EZ ISA card support"
+ select COMEDI_ISADMA if ISA_DMA_API
+ ---help---
+ Enable support for Data Translation DT2821 series including DT-EZ
+ DT2821, DT2821-F-16SE, DT2821-F-8DI, DT2821-G-16SE, DT2821-G-8DI,
+ DT2823 (dt2823), DT2824-PGH, DT2824-PGL, DT2825, DT2827, DT2828,
+ DT21-EZ, DT23-EZ, DT24-EZ and DT24-EZ-PGL
+
+ To compile this driver as a module, choose M here: the module will be
+ called dt282x.
+
+config COMEDI_DMM32AT
+ tristate "Diamond Systems MM-32-AT PC/104 board support"
+ select COMEDI_8255
+ ---help---
+ Enable support for Diamond Systems MM-32-AT PC/104 boards
+
+ To compile this driver as a module, choose M here: the module will be
+ called dmm32at.
+
+config COMEDI_UNIOXX5
+ tristate "Fastwel UNIOxx-5 analog and digital io board support"
+ ---help---
+ Enable support for Fastwel UNIOxx-5 (analog and digital i/o) boards
+
+ To compile this driver as a module, choose M here: the module will be
+ called unioxx5.
+
+config COMEDI_FL512
+ tristate "FL512 ISA card support"
+ ---help---
+ Enable support for FL512 ISA card
+
+ To compile this driver as a module, choose M here: the module will be
+ called fl512.
+
+config COMEDI_AIO_AIO12_8
+ tristate "I/O Products PC/104 AIO12-8 Analog I/O Board support"
+ select COMEDI_8255
+ ---help---
+ Enable support for I/O Products PC/104 AIO12-8 Analog I/O Board
+
+ To compile this driver as a module, choose M here: the module will be
+ called aio_aio12_8.
+
+config COMEDI_AIO_IIRO_16
+ tristate "I/O Products PC/104 IIRO16 Board support"
+ ---help---
+ Enable support for I/O Products PC/104 IIRO16 Relay And Isolated
+ Input Board
+
+ To compile this driver as a module, choose M here: the module will be
+ called aio_iiro_16.
+
+config COMEDI_II_PCI20KC
+ tristate "Intelligent Instruments PCI-20001C carrier support"
+ depends on HAS_IOMEM
+ ---help---
+ Enable support for Intelligent Instruments PCI-20001C carrier
+ PCI-20001, PCI-20006 and PCI-20341
+
+ To compile this driver as a module, choose M here: the module will be
+ called ii_pci20kc.
+
+config COMEDI_C6XDIGIO
+ tristate "Mechatronic Systems Inc. C6x_DIGIO DSP daughter card support"
+ ---help---
+ Enable support for Mechatronic Systems Inc. C6x_DIGIO DSP daughter
+ card
+
+ To compile this driver as a module, choose M here: the module will be
+ called c6xdigio.
+
+config COMEDI_MPC624
+ tristate "Micro/sys MPC-624 PC/104 board support"
+ ---help---
+ Enable support for Micro/sys MPC-624 PC/104 board
+
+ To compile this driver as a module, choose M here: the module will be
+ called mpc624.
+
+config COMEDI_ADQ12B
+ tristate "MicroAxial ADQ12-B data acquisition and control card support"
+ ---help---
+ Enable MicroAxial ADQ12-B daq and control card support.
+
+ To compile this driver as a module, choose M here: the module will be
+ called adq12b.
+
+config COMEDI_NI_AT_A2150
+ tristate "NI AT-A2150 ISA card support"
+ select COMEDI_ISADMA if ISA_DMA_API
+ select COMEDI_8254
+ ---help---
+ Enable support for National Instruments AT-A2150 cards
+
+ To compile this driver as a module, choose M here: the module will be
+ called ni_at_a2150.
+
+config COMEDI_NI_AT_AO
+ tristate "NI AT-AO-6/10 EISA card support"
+ select COMEDI_8254
+ ---help---
+ Enable support for National Instruments AT-AO-6/10 cards
+
+ To compile this driver as a module, choose M here: the module will be
+ called ni_at_ao.
+
+config COMEDI_NI_ATMIO
+ tristate "NI AT-MIO E series ISA-PNP card support"
+ select COMEDI_8255
+ select COMEDI_NI_TIO
+ ---help---
+ Enable support for National Instruments AT-MIO E series cards
+ National Instruments AT-MIO-16E-1 (ni_atmio),
+ AT-MIO-16E-2, AT-MIO-16E-10, AT-MIO-16DE-10, AT-MIO-64E-3,
+ AT-MIO-16XE-50, AT-MIO-16XE-10, AT-AI-16XE-10
+
+ To compile this driver as a module, choose M here: the module will be
+ called ni_atmio.
+
+config COMEDI_NI_ATMIO16D
+ tristate "NI AT-MIO-16/AT-MIO-16D series ISA card support"
+ select COMEDI_8255
+ ---help---
+ Enable support for National Instruments AT-MIO-16/AT-MIO-16D cards.
+
+ To compile this driver as a module, choose M here: the module will be
+ called ni_atmio16d.
+
+config COMEDI_NI_LABPC_ISA
+ tristate "NI Lab-PC and compatibles ISA support"
+ select COMEDI_NI_LABPC
+ select COMEDI_NI_LABPC_ISADMA if ISA_DMA_API
+ ---help---
+ Enable support for National Instruments Lab-PC and compatibles
+ Lab-PC-1200, Lab-PC-1200AI, Lab-PC+.
+ Kernel-level ISA plug-and-play support for the lab-pc-1200 boards has
+ not yet been added to the driver.
+
+ To compile this driver as a module, choose M here: the module will be
+ called ni_labpc.
+
+config COMEDI_PCMAD
+ tristate "Winsystems PCM-A/D12 and PCM-A/D16 PC/104 board support"
+ ---help---
+ Enable support for Winsystems PCM-A/D12 and PCM-A/D16 PC/104 boards.
+
+ To compile this driver as a module, choose M here: the module will be
+ called pcmad.
+
+config COMEDI_PCMDA12
+ tristate "Winsystems PCM-D/A-12 8-channel AO PC/104 board support"
+ ---help---
+ Enable support for Winsystems PCM-D/A-12 8-channel AO PC/104 boards.
+ Note that the board is not ISA-PNP capable and thus needs the I/O
+ port comedi_config parameter.
+
+ To compile this driver as a module, choose M here: the module will be
+ called pcmda12.
+
+config COMEDI_PCMMIO
+ tristate "Winsystems PCM-MIO PC/104 board support"
+ ---help---
+ Enable support for Winsystems PCM-MIO multifunction PC/104 boards.
+
+ To compile this driver as a module, choose M here: the module will be
+ called pcmmio.
+
+config COMEDI_PCMUIO
+ tristate "Winsystems PCM-UIO48A and PCM-UIO96A PC/104 board support"
+ ---help---
+ Enable support for PCM-UIO48A and PCM-UIO96A PC/104 boards.
+
+ To compile this driver as a module, choose M here: the module will be
+ called pcmuio.
+
+config COMEDI_MULTIQ3
+ tristate "Quanser Consulting MultiQ-3 ISA card support"
+ ---help---
+ Enable support for Quanser Consulting MultiQ-3 ISA cards
+
+ To compile this driver as a module, choose M here: the module will be
+ called multiq3.
+
+config COMEDI_S526
+ tristate "Sensoray s526 support"
+ ---help---
+ Enable support for Sensoray s526
+
+ To compile this driver as a module, choose M here: the module will be
+ called s526.
+
+endif # COMEDI_ISA_DRIVERS
+
+menuconfig COMEDI_PCI_DRIVERS
+ tristate "Comedi PCI drivers"
+ depends on PCI
+ ---help---
+ Enable support for comedi PCI drivers.
+
+ To compile this support as a module, choose M here: the module will
+ be called comedi_pci.
+
+if COMEDI_PCI_DRIVERS
+
+config COMEDI_8255_PCI
+ tristate "Generic PCI based 8255 digital i/o board support"
+ select COMEDI_8255
+ ---help---
+ Enable support for PCI based 8255 digital i/o boards. This driver
+ provides a PCI wrapper around the generic 8255 driver.
+
+ Supported boards:
+ ADlink - PCI-7224, PCI-7248, and PCI-7296
+ Measurement Computing - PCI-DIO24, PCI-DIO24H, PCI-DIO48H and
+ PCI-DIO96H
+ National Instruments - PCI-DIO-96, PCI-DIO-96B, PXI-6508, PCI-6503,
+ PCI-6503B, PCI-6503X, and PXI-6503
+
+ To compile this driver as a module, choose M here: the module will
+ be called 8255_pci.
+
+config COMEDI_ADDI_WATCHDOG
+ tristate
+ ---help---
+ Provides support for the watchdog subdevice found on many ADDI-DATA
+ boards. This module will be automatically selected when needed. The
+ module will be called addi_watchdog.
+
+config COMEDI_ADDI_APCI_1032
+ tristate "ADDI-DATA APCI_1032 support"
+ ---help---
+ Enable support for ADDI-DATA APCI_1032 cards
+
+ To compile this driver as a module, choose M here: the module will be
+ called addi_apci_1032.
+
+config COMEDI_ADDI_APCI_1500
+ tristate "ADDI-DATA APCI_1500 support"
+ ---help---
+ Enable support for ADDI-DATA APCI_1500 cards
+
+ To compile this driver as a module, choose M here: the module will be
+ called addi_apci_1500.
+
+config COMEDI_ADDI_APCI_1516
+ tristate "ADDI-DATA APCI-1016/1516/2016 support"
+ select COMEDI_ADDI_WATCHDOG
+ ---help---
+ Enable support for ADDI-DATA APCI-1016, APCI-1516 and APCI-2016 boards.
+ These are 16 channel, optically isolated, digital I/O boards. The 1516
+ and 2016 boards also have a watchdog for resetting the outputs to "0".
+
+ To compile this driver as a module, choose M here: the module will be
+ called addi_apci_1516.
+
+config COMEDI_ADDI_APCI_1564
+ tristate "ADDI-DATA APCI_1564 support"
+ select COMEDI_ADDI_WATCHDOG
+ ---help---
+ Enable support for ADDI-DATA APCI_1564 cards
+
+ To compile this driver as a module, choose M here: the module will be
+ called addi_apci_1564.
+
+config COMEDI_ADDI_APCI_16XX
+ tristate "ADDI-DATA APCI_16xx support"
+ ---help---
+ Enable support for ADDI-DATA APCI_16xx cards
+
+ To compile this driver as a module, choose M here: the module will be
+ called addi_apci_16xx.
+
+config COMEDI_ADDI_APCI_2032
+ tristate "ADDI-DATA APCI_2032 support"
+ select COMEDI_ADDI_WATCHDOG
+ ---help---
+ Enable support for ADDI-DATA APCI_2032 cards
+
+ To compile this driver as a module, choose M here: the module will be
+ called addi_apci_2032.
+
+config COMEDI_ADDI_APCI_2200
+ tristate "ADDI-DATA APCI_2200 support"
+ select COMEDI_ADDI_WATCHDOG
+ ---help---
+ Enable support for ADDI-DATA APCI_2200 cards
+
+ To compile this driver as a module, choose M here: the module will be
+ called addi_apci_2200.
+
+config COMEDI_ADDI_APCI_3120
+ tristate "ADDI-DATA APCI_3120/3001 support"
+ depends on HAS_DMA
+ ---help---
+ Enable support for ADDI-DATA APCI_3120/3001 cards
+
+ To compile this driver as a module, choose M here: the module will be
+ called addi_apci_3120.
+
+config COMEDI_ADDI_APCI_3501
+ tristate "ADDI-DATA APCI_3501 support"
+ ---help---
+ Enable support for ADDI-DATA APCI_3501 cards
+
+ To compile this driver as a module, choose M here: the module will be
+ called addi_apci_3501.
+
+config COMEDI_ADDI_APCI_3XXX
+ tristate "ADDI-DATA APCI_3xxx support"
+ ---help---
+ Enable support for ADDI-DATA APCI_3xxx cards
+
+ To compile this driver as a module, choose M here: the module will be
+ called addi_apci_3xxx.
+
+config COMEDI_ADL_PCI6208
+ tristate "ADLink PCI-6208A support"
+ ---help---
+ Enable support for ADLink PCI-6208A cards
+
+ To compile this driver as a module, choose M here: the module will be
+ called adl_pci6208.
+
+config COMEDI_ADL_PCI7X3X
+ tristate "ADLink PCI-723X/743X isolated digital i/o board support"
+ ---help---
+ Enable support for ADlink PCI-723X/743X isolated digital i/o boards.
+ Supported boards include the 32-channel PCI-7230 (16 in/16 out),
+ PCI-7233 (32 in), and PCI-7234 (32 out) as well as the 64-channel
+ PCI-7432 (32 in/32 out), PCI-7433 (64 in), and PCI-7434 (64 out).
+
+ To compile this driver as a module, choose M here: the module will be
+ called adl_pci7x3x.
+
+config COMEDI_ADL_PCI8164
+ tristate "ADLink PCI-8164 4 Axes Motion Control board support"
+ ---help---
+ Enable support for ADlink PCI-8164 4 Axes Motion Control board
+
+ To compile this driver as a module, choose M here: the module will be
+ called adl_pci8164.
+
+config COMEDI_ADL_PCI9111
+ tristate "ADLink PCI-9111HR support"
+ select COMEDI_8254
+ ---help---
+ Enable support for ADlink PCI9111 cards
+
+ To compile this driver as a module, choose M here: the module will be
+ called adl_pci9111.
+
+config COMEDI_ADL_PCI9118
+ tristate "ADLink PCI-9118DG, PCI-9118HG, PCI-9118HR support"
+ depends on HAS_DMA
+ select COMEDI_8254
+ ---help---
+ Enable support for ADlink PCI-9118DG, PCI-9118HG, PCI-9118HR cards
+
+ To compile this driver as a module, choose M here: the module will be
+ called adl_pci9118.
+
+config COMEDI_ADV_PCI1710
+ tristate "Advantech PCI-171x, PCI-1720 and PCI-1731 support"
+ select COMEDI_8254
+ ---help---
+ Enable support for Advantech PCI-1710, PCI-1710HG, PCI-1711,
+ PCI-1713, PCI-1720 and PCI-1731
+
+ To compile this driver as a module, choose M here: the module will be
+ called adv_pci1710.
+
+config COMEDI_ADV_PCI1723
+ tristate "Advantech PCI-1723 support"
+ ---help---
+ Enable support for Advantech PCI-1723 cards
+
+ To compile this driver as a module, choose M here: the module will be
+ called adv_pci1723.
+
+config COMEDI_ADV_PCI1724
+ tristate "Advantech PCI-1724U support"
+ ---help---
+ Enable support for Advantech PCI-1724U cards. These are 32-channel
+ analog output cards with voltage and current loop output ranges and
+ 14-bit resolution.
+
+ To compile this driver as a module, choose M here: the module will be
+ called adv_pci1724.
+
+config COMEDI_ADV_PCI_DIO
+ tristate "Advantech PCI DIO card support"
+ select COMEDI_8254
+ select COMEDI_8255
+ ---help---
+ Enable support for Advantech PCI DIO cards
+ PCI-1730, PCI-1733, PCI-1734, PCI-1735U, PCI-1736UP, PCI-1739U,
+ PCI-1750, PCI-1751, PCI-1752, PCI-1753/E, PCI-1754, PCI-1756,
+ PCI-1760 and PCI-1762
+
+ To compile this driver as a module, choose M here: the module will be
+ called adv_pci_dio.
+
+config COMEDI_AMPLC_DIO200_PCI
+ tristate "Amplicon PCI215/PCI272/PCIe215/PCIe236/PCIe296 DIO support"
+ select COMEDI_AMPLC_DIO200
+ ---help---
+ Enable support for Amplicon PCI215, PCI272, PCIe215, PCIe236
+ and PCIe296 DIO boards.
+
+ To compile this driver as a module, choose M here: the module will be
+ called amplc_dio200_pci.
+
+config COMEDI_AMPLC_PC236_PCI
+ tristate "Amplicon PCI236 DIO board support"
+ select COMEDI_AMPLC_PC236
+ ---help---
+ Enable support for Amplicon PCI236 DIO board.
+
+ To compile this driver as a module, choose M here: the module will be
+ called amplc_pci236.
+
+config COMEDI_AMPLC_PC263_PCI
+ tristate "Amplicon PCI263 relay board support"
+ ---help---
+ Enable support for Amplicon PCI263 relay board. This is a PCI board
+ with 16 reed relay output channels.
+
+ To compile this driver as a module, choose M here: the module will be
+ called amplc_pci263.
+
+config COMEDI_AMPLC_PCI224
+ tristate "Amplicon PCI224 and PCI234 support"
+ select COMEDI_8254
+ ---help---
+ Enable support for Amplicon PCI224 and PCI234 AO boards
+
+ To compile this driver as a module, choose M here: the module will be
+ called amplc_pci224.
+
+config COMEDI_AMPLC_PCI230
+ tristate "Amplicon PCI230 and PCI260 support"
+ select COMEDI_8254
+ select COMEDI_8255
+ ---help---
+ Enable support for Amplicon PCI230 and PCI260 Multifunction I/O
+ boards
+
+ To compile this driver as a module, choose M here: the module will be
+ called amplc_pci230.
+
+config COMEDI_CONTEC_PCI_DIO
+ tristate "Contec PIO1616L digital I/O board support"
+ ---help---
+ Enable support for the Contec PIO1616L digital I/O board
+
+ To compile this driver as a module, choose M here: the module will be
+ called contec_pci_dio.
+
+config COMEDI_DAS08_PCI
+ tristate "DAS-08 PCI support"
+ select COMEDI_DAS08
+ ---help---
+ Enable support for PCI DAS-08 cards.
+
+ To compile this driver as a module, choose M here: the module will be
+ called das08_pci.
+
+config COMEDI_DT3000
+ tristate "Data Translation DT3000 series support"
+ ---help---
+ Enable support for Data Translation DT3000 series
+ DT3001, DT3001-PGL, DT3002, DT3003, DT3003-PGL, DT3004, DT3005 and
+ DT3004-200
+
+ To compile this driver as a module, choose M here: the module will be
+ called dt3000.
+
+config COMEDI_DYNA_PCI10XX
+ tristate "Dynalog PCI DAQ series support"
+ ---help---
+ Enable support for Dynalog PCI DAQ series
+ PCI-1050
+
+ To compile this driver as a module, choose M here: the module will be
+ called dyna_pci10xx.
+
+config COMEDI_GSC_HPDI
+ tristate "General Standards PCI-HPDI32 / PMC-HPDI32 support"
+ ---help---
+ Enable support for General Standards Corporation high speed parallel
+ digital interface rs485 boards PCI-HPDI32 and PMC-HPDI32.
+ Only receive mode works, transmit not supported.
+
+ To compile this driver as a module, choose M here: the module will be
+ called gsc_hpdi.
+
+config COMEDI_MF6X4
+ tristate "Humusoft MF634 and MF624 DAQ Card support"
+ ---help---
+ This driver supports both Humusoft MF634 and MF624 Data acquisition
+ cards. The legacy Humusoft MF614 card is not supported.
+
+config COMEDI_ICP_MULTI
+ tristate "Inova ICP_MULTI support"
+ ---help---
+ Enable support for Inova ICP_MULTI card
+
+ To compile this driver as a module, choose M here: the module will be
+ called icp_multi.
+
+config COMEDI_DAQBOARD2000
+ tristate "IOtech DAQboard/2000 support"
+ select COMEDI_8255
+ ---help---
+ Enable support for the IOtech DAQboard/2000
+
+ To compile this driver as a module, choose M here: the module will be
+ called daqboard2000.
+
+config COMEDI_JR3_PCI
+ tristate "JR3/PCI force sensor board support"
+ ---help---
+ Enable support for JR3/PCI force sensor boards
+
+ To compile this driver as a module, choose M here: the module will be
+ called jr3_pci.
+
+config COMEDI_KE_COUNTER
+ tristate "Kolter-Electronic PCI Counter 1 card support"
+ ---help---
+ Enable support for Kolter-Electronic PCI Counter 1 cards
+
+ To compile this driver as a module, choose M here: the module will be
+ called ke_counter.
+
+config COMEDI_CB_PCIDAS64
+ tristate "MeasurementComputing PCI-DAS 64xx, 60xx, and 4020 support"
+ select COMEDI_8255
+ ---help---
+ Enable support for ComputerBoards/MeasurementComputing PCI-DAS 64xx,
+ 60xx, and 4020 series with the PLX 9080 PCI controller
+
+ To compile this driver as a module, choose M here: the module will be
+ called cb_pcidas64.
+
+config COMEDI_CB_PCIDAS
+ tristate "MeasurementComputing PCI-DAS support"
+ select COMEDI_8254
+ select COMEDI_8255
+ ---help---
+ Enable support for ComputerBoards/MeasurementComputing PCI-DAS with
+ AMCC S5933 PCIcontroller: PCI-DAS1602/16, PCI-DAS1602/16jr,
+ PCI-DAS1602/12, PCI-DAS1200, PCI-DAS1200jr, PCI-DAS1000, PCI-DAS1001
+ and PCI_DAS1002.
+
+ To compile this driver as a module, choose M here: the module will be
+ called cb_pcidas.
+
+config COMEDI_CB_PCIDDA
+ tristate "MeasurementComputing PCI-DDA series support"
+ select COMEDI_8255
+ ---help---
+ Enable support for ComputerBoards/MeasurementComputing PCI-DDA
+ series: PCI-DDA08/12, PCI-DDA04/12, PCI-DDA02/12, PCI-DDA08/16,
+ PCI-DDA04/16 and PCI-DDA02/16
+
+ To compile this driver as a module, choose M here: the module will be
+ called cb_pcidda.
+
+config COMEDI_CB_PCIMDAS
+ tristate "MeasurementComputing PCIM-DAS1602/16, PCIe-DAS1602/16 support"
+ select COMEDI_8254
+ select COMEDI_8255
+ ---help---
+ Enable support for ComputerBoards/MeasurementComputing PCI Migration
+ series PCIM-DAS1602/16 and PCIe-DAS1602/16.
+
+ To compile this driver as a module, choose M here: the module will be
+ called cb_pcimdas.
+
+config COMEDI_CB_PCIMDDA
+ tristate "MeasurementComputing PCIM-DDA06-16 support"
+ select COMEDI_8255
+ ---help---
+ Enable support for ComputerBoards/MeasurementComputing PCIM-DDA06-16
+
+ To compile this driver as a module, choose M here: the module will be
+ called cb_pcimdda.
+
+config COMEDI_ME4000
+ tristate "Meilhaus ME-4000 support"
+ select COMEDI_8254
+ ---help---
+ Enable support for Meilhaus PCI data acquisition cards
+ ME-4650, ME-4670i, ME-4680, ME-4680i and ME-4680is
+
+ To compile this driver as a module, choose M here: the module will be
+ called me4000.
+
+config COMEDI_ME_DAQ
+ tristate "Meilhaus ME-2000i, ME-2600i, ME-3000vm1 support"
+ ---help---
+ Enable support for Meilhaus PCI data acquisition cards
+ ME-2000i, ME-2600i and ME-3000vm1
+
+ To compile this driver as a module, choose M here: the module will be
+ called me_daq.
+
+config COMEDI_NI_6527
+ tristate "NI 6527 support"
+ ---help---
+ Enable support for the National Instruments 6527 PCI card
+
+ To compile this driver as a module, choose M here: the module will be
+ called ni_6527.
+
+config COMEDI_NI_65XX
+ tristate "NI 65xx static dio PCI card support"
+ ---help---
+ Enable support for National Instruments 65xx static dio boards.
+ Supported devices: National Instruments PCI-6509 (ni_65xx),
+ PXI-6509, PCI-6510, PCI-6511, PXI-6511, PCI-6512, PXI-6512, PCI-6513,
+ PXI-6513, PCI-6514, PXI-6514, PCI-6515, PXI-6515, PCI-6516, PCI-6517,
+ PCI-6518, PCI-6519, PCI-6520, PCI-6521, PXI-6521, PCI-6528, PXI-6528
+
+ To compile this driver as a module, choose M here: the module will be
+ called ni_65xx.
+
+config COMEDI_NI_660X
+ tristate "NI 660x counter/timer PCI card support"
+ depends on HAS_DMA
+ select COMEDI_NI_TIOCMD
+ ---help---
+ Enable support for National Instruments PCI-6601 (ni_660x), PCI-6602,
+ PXI-6602, PXI-6608 and PXI-6624.
+
+ To compile this driver as a module, choose M here: the module will be
+ called ni_660x.
+
+config COMEDI_NI_670X
+ tristate "NI 670x PCI card support"
+ ---help---
+ Enable support for National Instruments PCI-6703 and PCI-6704
+
+ To compile this driver as a module, choose M here: the module will be
+ called ni_670x.
+
+config COMEDI_NI_LABPC_PCI
+ tristate "NI Lab-PC PCI-1200 support"
+ select COMEDI_NI_LABPC
+ ---help---
+ Enable support for National Instruments Lab-PC PCI-1200.
+
+ To compile this driver as a module, choose M here: the module will be
+ called ni_labpc_pci.
+
+config COMEDI_NI_PCIDIO
+ tristate "NI PCI-DIO32HS, PCI-6533, PCI-6534 support"
+ depends on HAS_DMA
+ select COMEDI_MITE
+ select COMEDI_8255
+ ---help---
+ Enable support for National Instruments PCI-DIO-32HS, PXI-6533,
+ PCI-6533 and PCI-6534
+
+ To compile this driver as a module, choose M here: the module will be
+ called ni_pcidio.
+
+config COMEDI_NI_PCIMIO
+ tristate "NI PCI-MIO-E series and M series support"
+ depends on HAS_DMA
+ select COMEDI_NI_TIOCMD
+ select COMEDI_8255
+ ---help---
+ Enable support for National Instruments PCI-MIO-E series and M series
+ (all boards): PCI-MIO-16XE-10, PXI-6030E, PCI-MIO-16E-1,
+ PCI-MIO-16E-4, PCI-6014, PCI-6040E, PXI-6040E, PCI-6030E, PCI-6031E,
+ PCI-6032E, PCI-6033E, PCI-6071E, PCI-6023E, PCI-6024E, PCI-6025E,
+ PXI-6025E, PCI-6034E, PCI-6035E, PCI-6052E, PCI-6110, PCI-6111,
+ PCI-6220, PCI-6221, PCI-6224, PXI-6224, PCI-6225, PXI-6225, PCI-6229,
+ PCI-6250, PCI-6251, PCIe-6251, PCI-6254, PCI-6259, PCIe-6259,
+ PCI-6280, PCI-6281, PXI-6281, PCI-6284, PCI-6289, PCI-6711, PXI-6711,
+ PCI-6713, PXI-6713, PXI-6071E, PCI-6070E, PXI-6070E, PXI-6052E,
+ PCI-6036E, PCI-6731, PCI-6733, PXI-6733, PCI-6143, PXI-6143
+
+ To compile this driver as a module, choose M here: the module will be
+ called ni_pcimio.
+
+config COMEDI_RTD520
+ tristate "Real Time Devices PCI4520/DM7520 support"
+ ---help---
+ Enable support for Real Time Devices PCI4520/DM7520
+
+ To compile this driver as a module, choose M here: the module will be
+ called rtd520.
+
+config COMEDI_S626
+ tristate "Sensoray 626 support"
+ ---help---
+ Enable support for Sensoray 626
+
+ To compile this driver as a module, choose M here: the module will be
+ called s626.
+
+config COMEDI_MITE
+ depends on HAS_DMA
+ tristate
+
+config COMEDI_NI_TIOCMD
+ tristate
+ depends on HAS_DMA
+ select COMEDI_NI_TIO
+ select COMEDI_MITE
+
+endif # COMEDI_PCI_DRIVERS
+
+menuconfig COMEDI_PCMCIA_DRIVERS
+ tristate "Comedi PCMCIA drivers"
+ depends on PCMCIA
+ ---help---
+ Enable support for comedi PCMCIA drivers.
+
+ To compile this support as a module, choose M here: the module will
+ be called comedi_pcmcia.
+
+if COMEDI_PCMCIA_DRIVERS
+
+config COMEDI_CB_DAS16_CS
+ tristate "CB DAS16 series PCMCIA support"
+ select COMEDI_8254
+ ---help---
+ Enable support for the ComputerBoards/MeasurementComputing PCMCIA
+ cards DAS16/16, PCM-DAS16D/12 and PCM-DAS16s/16
+
+ To compile this driver as a module, choose M here: the module will be
+ called cb_das16_cs.
+
+config COMEDI_DAS08_CS
+ tristate "CB DAS08 PCMCIA support"
+ select COMEDI_DAS08
+ ---help---
+ Enable support for the ComputerBoards/MeasurementComputing DAS-08
+ PCMCIA card
+
+ To compile this driver as a module, choose M here: the module will be
+ called das08_cs.
+
+config COMEDI_NI_DAQ_700_CS
+ tristate "NI DAQCard-700 PCMCIA support"
+ ---help---
+ Enable support for the National Instruments PCMCIA DAQCard-700 DIO
+
+ To compile this driver as a module, choose M here: the module will be
+ called ni_daq_700.
+
+config COMEDI_NI_DAQ_DIO24_CS
+ tristate "NI DAQ-Card DIO-24 PCMCIA support"
+ select COMEDI_8255
+ ---help---
+ Enable support for the National Instruments PCMCIA DAQ-Card DIO-24
+
+ To compile this driver as a module, choose M here: the module will be
+ called ni_daq_dio24.
+
+config COMEDI_NI_LABPC_CS
+ tristate "NI DAQCard-1200 PCMCIA support"
+ select COMEDI_NI_LABPC
+ ---help---
+ Enable support for the National Instruments PCMCIA DAQCard-1200
+
+ To compile this driver as a module, choose M here: the module will be
+ called ni_labpc_cs.
+
+config COMEDI_NI_MIO_CS
+ tristate "NI DAQCard E series PCMCIA support"
+ select COMEDI_NI_TIO
+ select COMEDI_8255
+ ---help---
+ Enable support for the National Instruments PCMCIA DAQCard E series
+ DAQCard-ai-16xe-50, DAQCard-ai-16e-4, DAQCard-6062E, DAQCard-6024E
+ and DAQCard-6036E
+
+ To compile this driver as a module, choose M here: the module will be
+ called ni_mio_cs.
+
+config COMEDI_QUATECH_DAQP_CS
+ tristate "Quatech DAQP PCMCIA data capture card support"
+ ---help---
+ Enable support for the Quatech DAQP PCMCIA data capture cards
+ DAQP-208 and DAQP-308
+
+ To compile this driver as a module, choose M here: the module will be
+ called quatech_daqp_cs.
+
+endif # COMEDI_PCMCIA_DRIVERS
+
+menuconfig COMEDI_USB_DRIVERS
+ tristate "Comedi USB drivers"
+ depends on USB
+ ---help---
+ Enable support for comedi USB drivers.
+
+ To compile this support as a module, choose M here: the module will
+ be called comedi_usb.
+
+if COMEDI_USB_DRIVERS
+
+config COMEDI_DT9812
+ tristate "DataTranslation DT9812 USB module support"
+ ---help---
+ Enable support for the Data Translation DT9812 USB module
+
+ To compile this driver as a module, choose M here: the module will be
+ called dt9812.
+
+config COMEDI_NI_USB6501
+ tristate "NI USB-6501 support"
+ ---help---
+ Enable support for the National Instruments USB-6501 module.
+
+ The NI USB-6501 is a Full-Speed USB 2.0 (12 Mbit/s) device that
+ provides 24 digital I/O lines channels and one 32-bit counter.
+
+ To compile this driver as a module, choose M here: the module will be
+ called ni_usb6501.
+
+config COMEDI_USBDUX
+ tristate "ITL USB-DUX-D support"
+ ---help---
+ Enable support for the Incite Technology Ltd USB-DUX-D Board
+
+ To compile this driver as a module, choose M here: the module will be
+ called usbdux.
+
+config COMEDI_USBDUXFAST
+ tristate "ITL USB-DUXfast support"
+ ---help---
+ Enable support for the Incite Technology Ltd USB-DUXfast Board
+
+ To compile this driver as a module, choose M here: the module will be
+ called usbduxfast.
+
+config COMEDI_USBDUXSIGMA
+ tristate "ITL USB-DUXsigma support"
+ ---help---
+ Enable support for the Incite Technology Ltd USB-DUXsigma Board
+
+ To compile this driver as a module, choose M here: the module will be
+ called usbduxsigma.
+
+config COMEDI_VMK80XX
+ tristate "Velleman VM110/VM140 USB Board support"
+ ---help---
+ Build the Velleman USB Board Low-Level Driver supporting the
+ K8055/K8061 aka VM110/VM140 devices
+
+ To compile this driver as a module, choose M here: the module will be
+ called vmk80xx.
+
+endif # COMEDI_USB_DRIVERS
+
+config COMEDI_8254
+ tristate
+
+config COMEDI_8255
+ tristate "Generic 8255 support"
+ ---help---
+ Enable generic 8255 support.
+
+ You should enable compilation this driver if you plan to use a board
+ that has an 8255 chip. For multifunction boards, the main driver will
+ configure the 8255 subdevice automatically.
+
+ Note that most PCI based 8255 boards use the 8255_pci driver as a
+ wrapper around this driver.
+
+ To compile this driver as a module, choose M here: the module will be
+ called 8255.
+
+config COMEDI_KCOMEDILIB
+ tristate "Comedi kcomedilib"
+ ---help---
+ Build the kcomedilib.
+
+ This is a kernel module used to open and manipulate Comedi devices
+ from within kernel code. It is currently only used by the
+ comedi_bond driver, and its functionality has been stripped down to
+ the needs of that driver, so is currently not very useful for
+ anything else.
+
+ To compile kcomedilib as a module, choose M here: the module will be
+ called kcomedilib.
+
+config COMEDI_AMPLC_DIO200
+ select COMEDI_8254
+ tristate
+
+config COMEDI_AMPLC_PC236
+ tristate
+ select COMEDI_8255
+
+config COMEDI_DAS08
+ tristate
+ select COMEDI_8254
+ select COMEDI_8255
+
+config COMEDI_ISADMA
+ tristate
+
+config COMEDI_NI_LABPC
+ tristate
+ select COMEDI_8254
+ select COMEDI_8255
+
+config COMEDI_NI_LABPC_ISADMA
+ tristate
+ select COMEDI_ISADMA
+
+config COMEDI_NI_TIO
+ tristate
+
+endif # COMEDI
diff --git a/drivers/staging/comedi/Makefile b/drivers/staging/comedi/Makefile
new file mode 100644
index 000000000..7f9dfb392
--- /dev/null
+++ b/drivers/staging/comedi/Makefile
@@ -0,0 +1,15 @@
+ccflags-$(CONFIG_COMEDI_DEBUG) := -DDEBUG
+
+comedi-y := comedi_fops.o range.o drivers.o \
+ comedi_buf.o
+comedi-$(CONFIG_PROC_FS) += proc.o
+comedi-$(CONFIG_COMPAT) += comedi_compat32.o
+
+obj-$(CONFIG_COMEDI_PCI_DRIVERS) += comedi_pci.o
+obj-$(CONFIG_COMEDI_PCMCIA_DRIVERS) += comedi_pcmcia.o
+obj-$(CONFIG_COMEDI_USB_DRIVERS) += comedi_usb.o
+
+obj-$(CONFIG_COMEDI) += comedi.o
+
+obj-$(CONFIG_COMEDI) += kcomedilib/
+obj-$(CONFIG_COMEDI) += drivers/
diff --git a/drivers/staging/comedi/TODO b/drivers/staging/comedi/TODO
new file mode 100644
index 000000000..b68fbdb5e
--- /dev/null
+++ b/drivers/staging/comedi/TODO
@@ -0,0 +1,11 @@
+TODO:
+ - checkpatch.pl cleanups
+ - Lindent
+ - remove all wrappers
+ - audit userspace interface
+ - cleanup the individual comedi drivers as well
+
+Please send patches to Greg Kroah-Hartman <greg@kroah.com> and
+copy:
+ Ian Abbott <abbotti@mev.co.uk>
+ H Hartley Sweeten <hsweeten@visionengravers.com>
diff --git a/drivers/staging/comedi/comedi.h b/drivers/staging/comedi/comedi.h
new file mode 100644
index 000000000..745574077
--- /dev/null
+++ b/drivers/staging/comedi/comedi.h
@@ -0,0 +1,937 @@
+/*
+ include/comedi.h (installed as /usr/include/comedi.h)
+ header file for comedi
+
+ COMEDI - Linux Control and Measurement Device Interface
+ Copyright (C) 1998-2001 David A. Schleef <ds@schleef.org>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser 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.
+*/
+
+#ifndef _COMEDI_H
+#define _COMEDI_H
+
+#define COMEDI_MAJORVERSION 0
+#define COMEDI_MINORVERSION 7
+#define COMEDI_MICROVERSION 76
+#define VERSION "0.7.76"
+
+/* comedi's major device number */
+#define COMEDI_MAJOR 98
+
+/*
+ maximum number of minor devices. This can be increased, although
+ kernel structures are currently statically allocated, thus you
+ don't want this to be much more than you actually use.
+ */
+#define COMEDI_NDEVICES 16
+
+/* number of config options in the config structure */
+#define COMEDI_NDEVCONFOPTS 32
+
+/*
+ * NOTE: 'comedi_config --init-data' is deprecated
+ *
+ * The following indexes in the config options were used by
+ * comedi_config to pass firmware blobs from user space to the
+ * comedi drivers. The request_firmware() hotplug interface is
+ * now used by all comedi drivers instead.
+ */
+
+/* length of nth chunk of firmware data -*/
+#define COMEDI_DEVCONF_AUX_DATA3_LENGTH 25
+#define COMEDI_DEVCONF_AUX_DATA2_LENGTH 26
+#define COMEDI_DEVCONF_AUX_DATA1_LENGTH 27
+#define COMEDI_DEVCONF_AUX_DATA0_LENGTH 28
+/* most significant 32 bits of pointer address (if needed) */
+#define COMEDI_DEVCONF_AUX_DATA_HI 29
+/* least significant 32 bits of pointer address */
+#define COMEDI_DEVCONF_AUX_DATA_LO 30
+#define COMEDI_DEVCONF_AUX_DATA_LENGTH 31 /* total data length */
+
+/* max length of device and driver names */
+#define COMEDI_NAMELEN 20
+
+/* packs and unpacks a channel/range number */
+
+#define CR_PACK(chan, rng, aref) \
+ ((((aref)&0x3)<<24) | (((rng)&0xff)<<16) | (chan))
+#define CR_PACK_FLAGS(chan, range, aref, flags) \
+ (CR_PACK(chan, range, aref) | ((flags) & CR_FLAGS_MASK))
+
+#define CR_CHAN(a) ((a)&0xffff)
+#define CR_RANGE(a) (((a)>>16)&0xff)
+#define CR_AREF(a) (((a)>>24)&0x03)
+
+#define CR_FLAGS_MASK 0xfc000000
+#define CR_ALT_FILTER (1<<26)
+#define CR_DITHER CR_ALT_FILTER
+#define CR_DEGLITCH CR_ALT_FILTER
+#define CR_ALT_SOURCE (1<<27)
+#define CR_EDGE (1<<30)
+#define CR_INVERT (1<<31)
+
+#define AREF_GROUND 0x00 /* analog ref = analog ground */
+#define AREF_COMMON 0x01 /* analog ref = analog common */
+#define AREF_DIFF 0x02 /* analog ref = differential */
+#define AREF_OTHER 0x03 /* analog ref = other (undefined) */
+
+/* counters -- these are arbitrary values */
+#define GPCT_RESET 0x0001
+#define GPCT_SET_SOURCE 0x0002
+#define GPCT_SET_GATE 0x0004
+#define GPCT_SET_DIRECTION 0x0008
+#define GPCT_SET_OPERATION 0x0010
+#define GPCT_ARM 0x0020
+#define GPCT_DISARM 0x0040
+#define GPCT_GET_INT_CLK_FRQ 0x0080
+
+#define GPCT_INT_CLOCK 0x0001
+#define GPCT_EXT_PIN 0x0002
+#define GPCT_NO_GATE 0x0004
+#define GPCT_UP 0x0008
+#define GPCT_DOWN 0x0010
+#define GPCT_HWUD 0x0020
+#define GPCT_SIMPLE_EVENT 0x0040
+#define GPCT_SINGLE_PERIOD 0x0080
+#define GPCT_SINGLE_PW 0x0100
+#define GPCT_CONT_PULSE_OUT 0x0200
+#define GPCT_SINGLE_PULSE_OUT 0x0400
+
+/* instructions */
+
+#define INSN_MASK_WRITE 0x8000000
+#define INSN_MASK_READ 0x4000000
+#define INSN_MASK_SPECIAL 0x2000000
+
+#define INSN_READ (0 | INSN_MASK_READ)
+#define INSN_WRITE (1 | INSN_MASK_WRITE)
+#define INSN_BITS (2 | INSN_MASK_READ|INSN_MASK_WRITE)
+#define INSN_CONFIG (3 | INSN_MASK_READ|INSN_MASK_WRITE)
+#define INSN_GTOD (4 | INSN_MASK_READ|INSN_MASK_SPECIAL)
+#define INSN_WAIT (5 | INSN_MASK_WRITE|INSN_MASK_SPECIAL)
+#define INSN_INTTRIG (6 | INSN_MASK_WRITE|INSN_MASK_SPECIAL)
+
+/* trigger flags */
+/* These flags are used in comedi_trig structures */
+
+#define TRIG_DITHER 0x0002 /* enable dithering */
+#define TRIG_DEGLITCH 0x0004 /* enable deglitching */
+#define TRIG_CONFIG 0x0010 /* perform configuration, not triggering */
+
+/* command flags */
+/* These flags are used in comedi_cmd structures */
+
+#define CMDF_BOGUS 0x00000001 /* do the motions */
+
+/* try to use a real-time interrupt while performing command */
+#define CMDF_PRIORITY 0x00000008
+
+/* wake up on end-of-scan events */
+#define CMDF_WAKE_EOS 0x00000020
+
+#define CMDF_WRITE 0x00000040
+
+#define CMDF_RAWDATA 0x00000080
+
+/* timer rounding definitions */
+#define CMDF_ROUND_MASK 0x00030000
+#define CMDF_ROUND_NEAREST 0x00000000
+#define CMDF_ROUND_DOWN 0x00010000
+#define CMDF_ROUND_UP 0x00020000
+#define CMDF_ROUND_UP_NEXT 0x00030000
+
+#define COMEDI_EV_START 0x00040000
+#define COMEDI_EV_SCAN_BEGIN 0x00080000
+#define COMEDI_EV_CONVERT 0x00100000
+#define COMEDI_EV_SCAN_END 0x00200000
+#define COMEDI_EV_STOP 0x00400000
+
+/* compatibility definitions */
+#define TRIG_BOGUS CMDF_BOGUS
+#define TRIG_RT CMDF_PRIORITY
+#define TRIG_WAKE_EOS CMDF_WAKE_EOS
+#define TRIG_WRITE CMDF_WRITE
+#define TRIG_ROUND_MASK CMDF_ROUND_MASK
+#define TRIG_ROUND_NEAREST CMDF_ROUND_NEAREST
+#define TRIG_ROUND_DOWN CMDF_ROUND_DOWN
+#define TRIG_ROUND_UP CMDF_ROUND_UP
+#define TRIG_ROUND_UP_NEXT CMDF_ROUND_UP_NEXT
+
+/* trigger sources */
+
+#define TRIG_ANY 0xffffffff
+#define TRIG_INVALID 0x00000000
+
+#define TRIG_NONE 0x00000001 /* never trigger */
+#define TRIG_NOW 0x00000002 /* trigger now + N ns */
+#define TRIG_FOLLOW 0x00000004 /* trigger on next lower level trig */
+#define TRIG_TIME 0x00000008 /* trigger at time N ns */
+#define TRIG_TIMER 0x00000010 /* trigger at rate N ns */
+#define TRIG_COUNT 0x00000020 /* trigger when count reaches N */
+#define TRIG_EXT 0x00000040 /* trigger on external signal N */
+#define TRIG_INT 0x00000080 /* trigger on comedi-internal signal N */
+#define TRIG_OTHER 0x00000100 /* driver defined */
+
+/* subdevice flags */
+
+#define SDF_BUSY 0x0001 /* device is busy */
+#define SDF_BUSY_OWNER 0x0002 /* device is busy with your job */
+#define SDF_LOCKED 0x0004 /* subdevice is locked */
+#define SDF_LOCK_OWNER 0x0008 /* you own lock */
+#define SDF_MAXDATA 0x0010 /* maxdata depends on channel */
+#define SDF_FLAGS 0x0020 /* flags depend on channel */
+#define SDF_RANGETYPE 0x0040 /* range type depends on channel */
+#define SDF_MODE0 0x0080 /* can do mode 0 */
+#define SDF_MODE1 0x0100 /* can do mode 1 */
+#define SDF_MODE2 0x0200 /* can do mode 2 */
+#define SDF_MODE3 0x0400 /* can do mode 3 */
+#define SDF_MODE4 0x0800 /* can do mode 4 */
+#define SDF_CMD 0x1000 /* can do commands (deprecated) */
+#define SDF_SOFT_CALIBRATED 0x2000 /* subdevice uses software calibration */
+#define SDF_CMD_WRITE 0x4000 /* can do output commands */
+#define SDF_CMD_READ 0x8000 /* can do input commands */
+
+/* subdevice can be read (e.g. analog input) */
+#define SDF_READABLE 0x00010000
+/* subdevice can be written (e.g. analog output) */
+#define SDF_WRITABLE 0x00020000
+#define SDF_WRITEABLE SDF_WRITABLE /* spelling error in API */
+/* subdevice does not have externally visible lines */
+#define SDF_INTERNAL 0x00040000
+#define SDF_GROUND 0x00100000 /* can do aref=ground */
+#define SDF_COMMON 0x00200000 /* can do aref=common */
+#define SDF_DIFF 0x00400000 /* can do aref=diff */
+#define SDF_OTHER 0x00800000 /* can do aref=other */
+#define SDF_DITHER 0x01000000 /* can do dithering */
+#define SDF_DEGLITCH 0x02000000 /* can do deglitching */
+#define SDF_MMAP 0x04000000 /* can do mmap() */
+#define SDF_RUNNING 0x08000000 /* subdevice is acquiring data */
+#define SDF_LSAMPL 0x10000000 /* subdevice uses 32-bit samples */
+#define SDF_PACKED 0x20000000 /* subdevice can do packed DIO */
+/* re recyle these flags for PWM */
+#define SDF_PWM_COUNTER SDF_MODE0 /* PWM can automatically switch off */
+#define SDF_PWM_HBRIDGE SDF_MODE1 /* PWM is signed (H-bridge) */
+
+/* subdevice types */
+
+enum comedi_subdevice_type {
+ COMEDI_SUBD_UNUSED, /* unused by driver */
+ COMEDI_SUBD_AI, /* analog input */
+ COMEDI_SUBD_AO, /* analog output */
+ COMEDI_SUBD_DI, /* digital input */
+ COMEDI_SUBD_DO, /* digital output */
+ COMEDI_SUBD_DIO, /* digital input/output */
+ COMEDI_SUBD_COUNTER, /* counter */
+ COMEDI_SUBD_TIMER, /* timer */
+ COMEDI_SUBD_MEMORY, /* memory, EEPROM, DPRAM */
+ COMEDI_SUBD_CALIB, /* calibration DACs */
+ COMEDI_SUBD_PROC, /* processor, DSP */
+ COMEDI_SUBD_SERIAL, /* serial IO */
+ COMEDI_SUBD_PWM /* PWM */
+};
+
+/* configuration instructions */
+
+enum configuration_ids {
+ INSN_CONFIG_DIO_INPUT = 0,
+ INSN_CONFIG_DIO_OUTPUT = 1,
+ INSN_CONFIG_DIO_OPENDRAIN = 2,
+ INSN_CONFIG_ANALOG_TRIG = 16,
+/* INSN_CONFIG_WAVEFORM = 17, */
+/* INSN_CONFIG_TRIG = 18, */
+/* INSN_CONFIG_COUNTER = 19, */
+ INSN_CONFIG_ALT_SOURCE = 20,
+ INSN_CONFIG_DIGITAL_TRIG = 21,
+ INSN_CONFIG_BLOCK_SIZE = 22,
+ INSN_CONFIG_TIMER_1 = 23,
+ INSN_CONFIG_FILTER = 24,
+ INSN_CONFIG_CHANGE_NOTIFY = 25,
+
+ INSN_CONFIG_SERIAL_CLOCK = 26, /*ALPHA*/
+ INSN_CONFIG_BIDIRECTIONAL_DATA = 27,
+ INSN_CONFIG_DIO_QUERY = 28,
+ INSN_CONFIG_PWM_OUTPUT = 29,
+ INSN_CONFIG_GET_PWM_OUTPUT = 30,
+ INSN_CONFIG_ARM = 31,
+ INSN_CONFIG_DISARM = 32,
+ INSN_CONFIG_GET_COUNTER_STATUS = 33,
+ INSN_CONFIG_RESET = 34,
+ /* Use CTR as single pulsegenerator */
+ INSN_CONFIG_GPCT_SINGLE_PULSE_GENERATOR = 1001,
+ /* Use CTR as pulsetraingenerator */
+ INSN_CONFIG_GPCT_PULSE_TRAIN_GENERATOR = 1002,
+ /* Use the counter as encoder */
+ INSN_CONFIG_GPCT_QUADRATURE_ENCODER = 1003,
+ INSN_CONFIG_SET_GATE_SRC = 2001, /* Set gate source */
+ INSN_CONFIG_GET_GATE_SRC = 2002, /* Get gate source */
+ /* Set master clock source */
+ INSN_CONFIG_SET_CLOCK_SRC = 2003,
+ INSN_CONFIG_GET_CLOCK_SRC = 2004, /* Get master clock source */
+ INSN_CONFIG_SET_OTHER_SRC = 2005, /* Set other source */
+ /* INSN_CONFIG_GET_OTHER_SRC = 2006,*//* Get other source */
+ /* Get size in bytes of subdevice's on-board fifos used during
+ * streaming input/output */
+ INSN_CONFIG_GET_HARDWARE_BUFFER_SIZE = 2006,
+ INSN_CONFIG_SET_COUNTER_MODE = 4097,
+ /* INSN_CONFIG_8254_SET_MODE is deprecated */
+ INSN_CONFIG_8254_SET_MODE = INSN_CONFIG_SET_COUNTER_MODE,
+ INSN_CONFIG_8254_READ_STATUS = 4098,
+ INSN_CONFIG_SET_ROUTING = 4099,
+ INSN_CONFIG_GET_ROUTING = 4109,
+ /* PWM */
+ INSN_CONFIG_PWM_SET_PERIOD = 5000, /* sets frequency */
+ INSN_CONFIG_PWM_GET_PERIOD = 5001, /* gets frequency */
+ INSN_CONFIG_GET_PWM_STATUS = 5002, /* is it running? */
+ /* sets H bridge: duty cycle and sign bit for a relay at the
+ * same time */
+ INSN_CONFIG_PWM_SET_H_BRIDGE = 5003,
+ /* gets H bridge data: duty cycle and the sign bit */
+ INSN_CONFIG_PWM_GET_H_BRIDGE = 5004
+};
+
+/*
+ * Settings for INSN_CONFIG_DIGITAL_TRIG:
+ * data[0] = INSN_CONFIG_DIGITAL_TRIG
+ * data[1] = trigger ID
+ * data[2] = configuration operation
+ * data[3] = configuration parameter 1
+ * data[4] = configuration parameter 2
+ * data[5] = configuration parameter 3
+ *
+ * operation parameter 1 parameter 2 parameter 3
+ * --------------------------------- ----------- ----------- -----------
+ * COMEDI_DIGITAL_TRIG_DISABLE
+ * COMEDI_DIGITAL_TRIG_ENABLE_EDGES left-shift rising-edges falling-edges
+ * COMEDI_DIGITAL_TRIG_ENABLE_LEVELS left-shift high-levels low-levels
+ *
+ * COMEDI_DIGITAL_TRIG_DISABLE returns the trigger to its default, inactive,
+ * unconfigured state.
+ *
+ * COMEDI_DIGITAL_TRIG_ENABLE_EDGES sets the rising and/or falling edge inputs
+ * that each can fire the trigger.
+ *
+ * COMEDI_DIGITAL_TRIG_ENABLE_LEVELS sets a combination of high and/or low
+ * level inputs that can fire the trigger.
+ *
+ * "left-shift" is useful if the trigger has more than 32 inputs to specify the
+ * first input for this configuration.
+ *
+ * Some sequences of INSN_CONFIG_DIGITAL_TRIG instructions may have a (partly)
+ * accumulative effect, depending on the low-level driver. This is useful
+ * when setting up a trigger that has more than 32 inputs or has a combination
+ * of edge and level triggered inputs.
+ */
+enum comedi_digital_trig_op {
+ COMEDI_DIGITAL_TRIG_DISABLE = 0,
+ COMEDI_DIGITAL_TRIG_ENABLE_EDGES = 1,
+ COMEDI_DIGITAL_TRIG_ENABLE_LEVELS = 2
+};
+
+enum comedi_io_direction {
+ COMEDI_INPUT = 0,
+ COMEDI_OUTPUT = 1,
+ COMEDI_OPENDRAIN = 2
+};
+
+enum comedi_support_level {
+ COMEDI_UNKNOWN_SUPPORT = 0,
+ COMEDI_SUPPORTED,
+ COMEDI_UNSUPPORTED
+};
+
+/* ioctls */
+
+#define CIO 'd'
+#define COMEDI_DEVCONFIG _IOW(CIO, 0, struct comedi_devconfig)
+#define COMEDI_DEVINFO _IOR(CIO, 1, struct comedi_devinfo)
+#define COMEDI_SUBDINFO _IOR(CIO, 2, struct comedi_subdinfo)
+#define COMEDI_CHANINFO _IOR(CIO, 3, struct comedi_chaninfo)
+#define COMEDI_TRIG _IOWR(CIO, 4, comedi_trig)
+#define COMEDI_LOCK _IO(CIO, 5)
+#define COMEDI_UNLOCK _IO(CIO, 6)
+#define COMEDI_CANCEL _IO(CIO, 7)
+#define COMEDI_RANGEINFO _IOR(CIO, 8, struct comedi_rangeinfo)
+#define COMEDI_CMD _IOR(CIO, 9, struct comedi_cmd)
+#define COMEDI_CMDTEST _IOR(CIO, 10, struct comedi_cmd)
+#define COMEDI_INSNLIST _IOR(CIO, 11, struct comedi_insnlist)
+#define COMEDI_INSN _IOR(CIO, 12, struct comedi_insn)
+#define COMEDI_BUFCONFIG _IOR(CIO, 13, struct comedi_bufconfig)
+#define COMEDI_BUFINFO _IOWR(CIO, 14, struct comedi_bufinfo)
+#define COMEDI_POLL _IO(CIO, 15)
+#define COMEDI_SETRSUBD _IO(CIO, 16)
+#define COMEDI_SETWSUBD _IO(CIO, 17)
+
+/* structures */
+
+struct comedi_trig {
+ unsigned int subdev; /* subdevice */
+ unsigned int mode; /* mode */
+ unsigned int flags;
+ unsigned int n_chan; /* number of channels */
+ unsigned int *chanlist; /* channel/range list */
+ short *data; /* data list, size depends on subd flags */
+ unsigned int n; /* number of scans */
+ unsigned int trigsrc;
+ unsigned int trigvar;
+ unsigned int trigvar1;
+ unsigned int data_len;
+ unsigned int unused[3];
+};
+
+struct comedi_insn {
+ unsigned int insn;
+ unsigned int n;
+ unsigned int __user *data;
+ unsigned int subdev;
+ unsigned int chanspec;
+ unsigned int unused[3];
+};
+
+struct comedi_insnlist {
+ unsigned int n_insns;
+ struct comedi_insn __user *insns;
+};
+
+struct comedi_cmd {
+ unsigned int subdev;
+ unsigned int flags;
+
+ unsigned int start_src;
+ unsigned int start_arg;
+
+ unsigned int scan_begin_src;
+ unsigned int scan_begin_arg;
+
+ unsigned int convert_src;
+ unsigned int convert_arg;
+
+ unsigned int scan_end_src;
+ unsigned int scan_end_arg;
+
+ unsigned int stop_src;
+ unsigned int stop_arg;
+
+ unsigned int *chanlist; /* channel/range list */
+ unsigned int chanlist_len;
+
+ short __user *data; /* data list, size depends on subd flags */
+ unsigned int data_len;
+};
+
+struct comedi_chaninfo {
+ unsigned int subdev;
+ unsigned int __user *maxdata_list;
+ unsigned int __user *flaglist;
+ unsigned int __user *rangelist;
+ unsigned int unused[4];
+};
+
+struct comedi_rangeinfo {
+ unsigned int range_type;
+ void __user *range_ptr;
+};
+
+struct comedi_krange {
+ int min; /* fixed point, multiply by 1e-6 */
+ int max; /* fixed point, multiply by 1e-6 */
+ unsigned int flags;
+};
+
+struct comedi_subdinfo {
+ unsigned int type;
+ unsigned int n_chan;
+ unsigned int subd_flags;
+ unsigned int timer_type;
+ unsigned int len_chanlist;
+ unsigned int maxdata;
+ unsigned int flags; /* channel flags */
+ unsigned int range_type; /* lookup in kernel */
+ unsigned int settling_time_0;
+ /* see support_level enum for values */
+ unsigned insn_bits_support;
+ unsigned int unused[8];
+};
+
+struct comedi_devinfo {
+ unsigned int version_code;
+ unsigned int n_subdevs;
+ char driver_name[COMEDI_NAMELEN];
+ char board_name[COMEDI_NAMELEN];
+ int read_subdevice;
+ int write_subdevice;
+ int unused[30];
+};
+
+struct comedi_devconfig {
+ char board_name[COMEDI_NAMELEN];
+ int options[COMEDI_NDEVCONFOPTS];
+};
+
+struct comedi_bufconfig {
+ unsigned int subdevice;
+ unsigned int flags;
+
+ unsigned int maximum_size;
+ unsigned int size;
+
+ unsigned int unused[4];
+};
+
+struct comedi_bufinfo {
+ unsigned int subdevice;
+ unsigned int bytes_read;
+
+ unsigned int buf_write_ptr;
+ unsigned int buf_read_ptr;
+ unsigned int buf_write_count;
+ unsigned int buf_read_count;
+
+ unsigned int bytes_written;
+
+ unsigned int unused[4];
+};
+
+/* range stuff */
+
+#define __RANGE(a, b) ((((a)&0xffff)<<16)|((b)&0xffff))
+
+#define RANGE_OFFSET(a) (((a)>>16)&0xffff)
+#define RANGE_LENGTH(b) ((b)&0xffff)
+
+#define RF_UNIT(flags) ((flags)&0xff)
+#define RF_EXTERNAL (1<<8)
+
+#define UNIT_volt 0
+#define UNIT_mA 1
+#define UNIT_none 2
+
+#define COMEDI_MIN_SPEED ((unsigned int)0xffffffff)
+
+/**********************************************************/
+/* everything after this line is ALPHA */
+/**********************************************************/
+
+/*
+ 8254 specific configuration.
+
+ It supports two config commands:
+
+ 0 ID: INSN_CONFIG_SET_COUNTER_MODE
+ 1 8254 Mode
+ I8254_MODE0, I8254_MODE1, ..., I8254_MODE5
+ OR'ed with:
+ I8254_BCD, I8254_BINARY
+
+ 0 ID: INSN_CONFIG_8254_READ_STATUS
+ 1 <-- Status byte returned here.
+ B7 = Output
+ B6 = NULL Count
+ B5 - B0 Current mode.
+
+*/
+
+enum i8254_mode {
+ I8254_MODE0 = (0 << 1), /* Interrupt on terminal count */
+ I8254_MODE1 = (1 << 1), /* Hardware retriggerable one-shot */
+ I8254_MODE2 = (2 << 1), /* Rate generator */
+ I8254_MODE3 = (3 << 1), /* Square wave mode */
+ I8254_MODE4 = (4 << 1), /* Software triggered strobe */
+ I8254_MODE5 = (5 << 1), /* Hardware triggered strobe
+ * (retriggerable) */
+ I8254_BCD = 1, /* use binary-coded decimal instead of binary
+ * (pretty useless) */
+ I8254_BINARY = 0
+};
+
+#define NI_USUAL_PFI_SELECT(x) (((x) < 10) ? (0x1 + (x)) : (0xb + (x)))
+#define NI_USUAL_RTSI_SELECT(x) (((x) < 7) ? (0xb + (x)) : 0x1b)
+
+/* mode bits for NI general-purpose counters, set with
+ * INSN_CONFIG_SET_COUNTER_MODE */
+#define NI_GPCT_COUNTING_MODE_SHIFT 16
+#define NI_GPCT_INDEX_PHASE_BITSHIFT 20
+#define NI_GPCT_COUNTING_DIRECTION_SHIFT 24
+enum ni_gpct_mode_bits {
+ NI_GPCT_GATE_ON_BOTH_EDGES_BIT = 0x4,
+ NI_GPCT_EDGE_GATE_MODE_MASK = 0x18,
+ NI_GPCT_EDGE_GATE_STARTS_STOPS_BITS = 0x0,
+ NI_GPCT_EDGE_GATE_STOPS_STARTS_BITS = 0x8,
+ NI_GPCT_EDGE_GATE_STARTS_BITS = 0x10,
+ NI_GPCT_EDGE_GATE_NO_STARTS_NO_STOPS_BITS = 0x18,
+ NI_GPCT_STOP_MODE_MASK = 0x60,
+ NI_GPCT_STOP_ON_GATE_BITS = 0x00,
+ NI_GPCT_STOP_ON_GATE_OR_TC_BITS = 0x20,
+ NI_GPCT_STOP_ON_GATE_OR_SECOND_TC_BITS = 0x40,
+ NI_GPCT_LOAD_B_SELECT_BIT = 0x80,
+ NI_GPCT_OUTPUT_MODE_MASK = 0x300,
+ NI_GPCT_OUTPUT_TC_PULSE_BITS = 0x100,
+ NI_GPCT_OUTPUT_TC_TOGGLE_BITS = 0x200,
+ NI_GPCT_OUTPUT_TC_OR_GATE_TOGGLE_BITS = 0x300,
+ NI_GPCT_HARDWARE_DISARM_MASK = 0xc00,
+ NI_GPCT_NO_HARDWARE_DISARM_BITS = 0x000,
+ NI_GPCT_DISARM_AT_TC_BITS = 0x400,
+ NI_GPCT_DISARM_AT_GATE_BITS = 0x800,
+ NI_GPCT_DISARM_AT_TC_OR_GATE_BITS = 0xc00,
+ NI_GPCT_LOADING_ON_TC_BIT = 0x1000,
+ NI_GPCT_LOADING_ON_GATE_BIT = 0x4000,
+ NI_GPCT_COUNTING_MODE_MASK = 0x7 << NI_GPCT_COUNTING_MODE_SHIFT,
+ NI_GPCT_COUNTING_MODE_NORMAL_BITS =
+ 0x0 << NI_GPCT_COUNTING_MODE_SHIFT,
+ NI_GPCT_COUNTING_MODE_QUADRATURE_X1_BITS =
+ 0x1 << NI_GPCT_COUNTING_MODE_SHIFT,
+ NI_GPCT_COUNTING_MODE_QUADRATURE_X2_BITS =
+ 0x2 << NI_GPCT_COUNTING_MODE_SHIFT,
+ NI_GPCT_COUNTING_MODE_QUADRATURE_X4_BITS =
+ 0x3 << NI_GPCT_COUNTING_MODE_SHIFT,
+ NI_GPCT_COUNTING_MODE_TWO_PULSE_BITS =
+ 0x4 << NI_GPCT_COUNTING_MODE_SHIFT,
+ NI_GPCT_COUNTING_MODE_SYNC_SOURCE_BITS =
+ 0x6 << NI_GPCT_COUNTING_MODE_SHIFT,
+ NI_GPCT_INDEX_PHASE_MASK = 0x3 << NI_GPCT_INDEX_PHASE_BITSHIFT,
+ NI_GPCT_INDEX_PHASE_LOW_A_LOW_B_BITS =
+ 0x0 << NI_GPCT_INDEX_PHASE_BITSHIFT,
+ NI_GPCT_INDEX_PHASE_LOW_A_HIGH_B_BITS =
+ 0x1 << NI_GPCT_INDEX_PHASE_BITSHIFT,
+ NI_GPCT_INDEX_PHASE_HIGH_A_LOW_B_BITS =
+ 0x2 << NI_GPCT_INDEX_PHASE_BITSHIFT,
+ NI_GPCT_INDEX_PHASE_HIGH_A_HIGH_B_BITS =
+ 0x3 << NI_GPCT_INDEX_PHASE_BITSHIFT,
+ NI_GPCT_INDEX_ENABLE_BIT = 0x400000,
+ NI_GPCT_COUNTING_DIRECTION_MASK =
+ 0x3 << NI_GPCT_COUNTING_DIRECTION_SHIFT,
+ NI_GPCT_COUNTING_DIRECTION_DOWN_BITS =
+ 0x00 << NI_GPCT_COUNTING_DIRECTION_SHIFT,
+ NI_GPCT_COUNTING_DIRECTION_UP_BITS =
+ 0x1 << NI_GPCT_COUNTING_DIRECTION_SHIFT,
+ NI_GPCT_COUNTING_DIRECTION_HW_UP_DOWN_BITS =
+ 0x2 << NI_GPCT_COUNTING_DIRECTION_SHIFT,
+ NI_GPCT_COUNTING_DIRECTION_HW_GATE_BITS =
+ 0x3 << NI_GPCT_COUNTING_DIRECTION_SHIFT,
+ NI_GPCT_RELOAD_SOURCE_MASK = 0xc000000,
+ NI_GPCT_RELOAD_SOURCE_FIXED_BITS = 0x0,
+ NI_GPCT_RELOAD_SOURCE_SWITCHING_BITS = 0x4000000,
+ NI_GPCT_RELOAD_SOURCE_GATE_SELECT_BITS = 0x8000000,
+ NI_GPCT_OR_GATE_BIT = 0x10000000,
+ NI_GPCT_INVERT_OUTPUT_BIT = 0x20000000
+};
+
+/* Bits for setting a clock source with
+ * INSN_CONFIG_SET_CLOCK_SRC when using NI general-purpose counters. */
+enum ni_gpct_clock_source_bits {
+ NI_GPCT_CLOCK_SRC_SELECT_MASK = 0x3f,
+ NI_GPCT_TIMEBASE_1_CLOCK_SRC_BITS = 0x0,
+ NI_GPCT_TIMEBASE_2_CLOCK_SRC_BITS = 0x1,
+ NI_GPCT_TIMEBASE_3_CLOCK_SRC_BITS = 0x2,
+ NI_GPCT_LOGIC_LOW_CLOCK_SRC_BITS = 0x3,
+ NI_GPCT_NEXT_GATE_CLOCK_SRC_BITS = 0x4,
+ NI_GPCT_NEXT_TC_CLOCK_SRC_BITS = 0x5,
+ /* NI 660x-specific */
+ NI_GPCT_SOURCE_PIN_i_CLOCK_SRC_BITS = 0x6,
+ NI_GPCT_PXI10_CLOCK_SRC_BITS = 0x7,
+ NI_GPCT_PXI_STAR_TRIGGER_CLOCK_SRC_BITS = 0x8,
+ NI_GPCT_ANALOG_TRIGGER_OUT_CLOCK_SRC_BITS = 0x9,
+ NI_GPCT_PRESCALE_MODE_CLOCK_SRC_MASK = 0x30000000,
+ NI_GPCT_NO_PRESCALE_CLOCK_SRC_BITS = 0x0,
+ /* divide source by 2 */
+ NI_GPCT_PRESCALE_X2_CLOCK_SRC_BITS = 0x10000000,
+ /* divide source by 8 */
+ NI_GPCT_PRESCALE_X8_CLOCK_SRC_BITS = 0x20000000,
+ NI_GPCT_INVERT_CLOCK_SRC_BIT = 0x80000000
+};
+
+/* NI 660x-specific */
+#define NI_GPCT_SOURCE_PIN_CLOCK_SRC_BITS(x) (0x10 + (x))
+
+#define NI_GPCT_RTSI_CLOCK_SRC_BITS(x) (0x18 + (x))
+
+/* no pfi on NI 660x */
+#define NI_GPCT_PFI_CLOCK_SRC_BITS(x) (0x20 + (x))
+
+/* Possibilities for setting a gate source with
+INSN_CONFIG_SET_GATE_SRC when using NI general-purpose counters.
+May be bitwise-or'd with CR_EDGE or CR_INVERT. */
+enum ni_gpct_gate_select {
+ /* m-series gates */
+ NI_GPCT_TIMESTAMP_MUX_GATE_SELECT = 0x0,
+ NI_GPCT_AI_START2_GATE_SELECT = 0x12,
+ NI_GPCT_PXI_STAR_TRIGGER_GATE_SELECT = 0x13,
+ NI_GPCT_NEXT_OUT_GATE_SELECT = 0x14,
+ NI_GPCT_AI_START1_GATE_SELECT = 0x1c,
+ NI_GPCT_NEXT_SOURCE_GATE_SELECT = 0x1d,
+ NI_GPCT_ANALOG_TRIGGER_OUT_GATE_SELECT = 0x1e,
+ NI_GPCT_LOGIC_LOW_GATE_SELECT = 0x1f,
+ /* more gates for 660x */
+ NI_GPCT_SOURCE_PIN_i_GATE_SELECT = 0x100,
+ NI_GPCT_GATE_PIN_i_GATE_SELECT = 0x101,
+ /* more gates for 660x "second gate" */
+ NI_GPCT_UP_DOWN_PIN_i_GATE_SELECT = 0x201,
+ NI_GPCT_SELECTED_GATE_GATE_SELECT = 0x21e,
+ /* m-series "second gate" sources are unknown,
+ * we should add them here with an offset of 0x300 when
+ * known. */
+ NI_GPCT_DISABLED_GATE_SELECT = 0x8000,
+};
+
+#define NI_GPCT_GATE_PIN_GATE_SELECT(x) (0x102 + (x))
+#define NI_GPCT_RTSI_GATE_SELECT(x) NI_USUAL_RTSI_SELECT(x)
+#define NI_GPCT_PFI_GATE_SELECT(x) NI_USUAL_PFI_SELECT(x)
+#define NI_GPCT_UP_DOWN_PIN_GATE_SELECT(x) (0x202 + (x))
+
+/* Possibilities for setting a source with
+INSN_CONFIG_SET_OTHER_SRC when using NI general-purpose counters. */
+enum ni_gpct_other_index {
+ NI_GPCT_SOURCE_ENCODER_A,
+ NI_GPCT_SOURCE_ENCODER_B,
+ NI_GPCT_SOURCE_ENCODER_Z
+};
+
+enum ni_gpct_other_select {
+ /* m-series gates */
+ /* Still unknown, probably only need NI_GPCT_PFI_OTHER_SELECT */
+ NI_GPCT_DISABLED_OTHER_SELECT = 0x8000,
+};
+
+#define NI_GPCT_PFI_OTHER_SELECT(x) NI_USUAL_PFI_SELECT(x)
+
+/* start sources for ni general-purpose counters for use with
+INSN_CONFIG_ARM */
+enum ni_gpct_arm_source {
+ NI_GPCT_ARM_IMMEDIATE = 0x0,
+ NI_GPCT_ARM_PAIRED_IMMEDIATE = 0x1, /* Start both the counter
+ * and the adjacent paired
+ * counter simultaneously */
+ /* NI doesn't document bits for selecting hardware arm triggers.
+ * If the NI_GPCT_ARM_UNKNOWN bit is set, we will pass the least
+ * significant bits (3 bits for 660x or 5 bits for m-series)
+ * through to the hardware. This will at least allow someone to
+ * figure out what the bits do later. */
+ NI_GPCT_ARM_UNKNOWN = 0x1000,
+};
+
+/* digital filtering options for ni 660x for use with INSN_CONFIG_FILTER. */
+enum ni_gpct_filter_select {
+ NI_GPCT_FILTER_OFF = 0x0,
+ NI_GPCT_FILTER_TIMEBASE_3_SYNC = 0x1,
+ NI_GPCT_FILTER_100x_TIMEBASE_1 = 0x2,
+ NI_GPCT_FILTER_20x_TIMEBASE_1 = 0x3,
+ NI_GPCT_FILTER_10x_TIMEBASE_1 = 0x4,
+ NI_GPCT_FILTER_2x_TIMEBASE_1 = 0x5,
+ NI_GPCT_FILTER_2x_TIMEBASE_3 = 0x6
+};
+
+/* PFI digital filtering options for ni m-series for use with
+ * INSN_CONFIG_FILTER. */
+enum ni_pfi_filter_select {
+ NI_PFI_FILTER_OFF = 0x0,
+ NI_PFI_FILTER_125ns = 0x1,
+ NI_PFI_FILTER_6425ns = 0x2,
+ NI_PFI_FILTER_2550us = 0x3
+};
+
+/* master clock sources for ni mio boards and INSN_CONFIG_SET_CLOCK_SRC */
+enum ni_mio_clock_source {
+ NI_MIO_INTERNAL_CLOCK = 0,
+ NI_MIO_RTSI_CLOCK = 1, /* doesn't work for m-series, use
+ NI_MIO_PLL_RTSI_CLOCK() */
+ /* the NI_MIO_PLL_* sources are m-series only */
+ NI_MIO_PLL_PXI_STAR_TRIGGER_CLOCK = 2,
+ NI_MIO_PLL_PXI10_CLOCK = 3,
+ NI_MIO_PLL_RTSI0_CLOCK = 4
+};
+
+#define NI_MIO_PLL_RTSI_CLOCK(x) (NI_MIO_PLL_RTSI0_CLOCK + (x))
+
+/* Signals which can be routed to an NI RTSI pin with INSN_CONFIG_SET_ROUTING.
+ The numbers assigned are not arbitrary, they correspond to the bits required
+ to program the board. */
+enum ni_rtsi_routing {
+ NI_RTSI_OUTPUT_ADR_START1 = 0,
+ NI_RTSI_OUTPUT_ADR_START2 = 1,
+ NI_RTSI_OUTPUT_SCLKG = 2,
+ NI_RTSI_OUTPUT_DACUPDN = 3,
+ NI_RTSI_OUTPUT_DA_START1 = 4,
+ NI_RTSI_OUTPUT_G_SRC0 = 5,
+ NI_RTSI_OUTPUT_G_GATE0 = 6,
+ NI_RTSI_OUTPUT_RGOUT0 = 7,
+ NI_RTSI_OUTPUT_RTSI_BRD_0 = 8,
+ NI_RTSI_OUTPUT_RTSI_OSC = 12 /* pre-m-series always have RTSI
+ * clock on line 7 */
+};
+
+#define NI_RTSI_OUTPUT_RTSI_BRD(x) (NI_RTSI_OUTPUT_RTSI_BRD_0 + (x))
+
+/* Signals which can be routed to an NI PFI pin on an m-series board with
+ * INSN_CONFIG_SET_ROUTING. These numbers are also returned by
+ * INSN_CONFIG_GET_ROUTING on pre-m-series boards, even though their routing
+ * cannot be changed. The numbers assigned are not arbitrary, they correspond
+ * to the bits required to program the board. */
+enum ni_pfi_routing {
+ NI_PFI_OUTPUT_PFI_DEFAULT = 0,
+ NI_PFI_OUTPUT_AI_START1 = 1,
+ NI_PFI_OUTPUT_AI_START2 = 2,
+ NI_PFI_OUTPUT_AI_CONVERT = 3,
+ NI_PFI_OUTPUT_G_SRC1 = 4,
+ NI_PFI_OUTPUT_G_GATE1 = 5,
+ NI_PFI_OUTPUT_AO_UPDATE_N = 6,
+ NI_PFI_OUTPUT_AO_START1 = 7,
+ NI_PFI_OUTPUT_AI_START_PULSE = 8,
+ NI_PFI_OUTPUT_G_SRC0 = 9,
+ NI_PFI_OUTPUT_G_GATE0 = 10,
+ NI_PFI_OUTPUT_EXT_STROBE = 11,
+ NI_PFI_OUTPUT_AI_EXT_MUX_CLK = 12,
+ NI_PFI_OUTPUT_GOUT0 = 13,
+ NI_PFI_OUTPUT_GOUT1 = 14,
+ NI_PFI_OUTPUT_FREQ_OUT = 15,
+ NI_PFI_OUTPUT_PFI_DO = 16,
+ NI_PFI_OUTPUT_I_ATRIG = 17,
+ NI_PFI_OUTPUT_RTSI0 = 18,
+ NI_PFI_OUTPUT_PXI_STAR_TRIGGER_IN = 26,
+ NI_PFI_OUTPUT_SCXI_TRIG1 = 27,
+ NI_PFI_OUTPUT_DIO_CHANGE_DETECT_RTSI = 28,
+ NI_PFI_OUTPUT_CDI_SAMPLE = 29,
+ NI_PFI_OUTPUT_CDO_UPDATE = 30
+};
+
+#define NI_PFI_OUTPUT_RTSI(x) (NI_PFI_OUTPUT_RTSI0 + (x))
+
+/* Signals which can be routed to output on a NI PFI pin on a 660x board
+ with INSN_CONFIG_SET_ROUTING. The numbers assigned are
+ not arbitrary, they correspond to the bits required
+ to program the board. Lines 0 to 7 can only be set to
+ NI_660X_PFI_OUTPUT_DIO. Lines 32 to 39 can only be set to
+ NI_660X_PFI_OUTPUT_COUNTER. */
+enum ni_660x_pfi_routing {
+ NI_660X_PFI_OUTPUT_COUNTER = 1, /* counter */
+ NI_660X_PFI_OUTPUT_DIO = 2, /* static digital output */
+};
+
+/* NI External Trigger lines. These values are not arbitrary, but are related
+ * to the bits required to program the board (offset by 1 for historical
+ * reasons). */
+#define NI_EXT_PFI(x) (NI_USUAL_PFI_SELECT(x) - 1)
+#define NI_EXT_RTSI(x) (NI_USUAL_RTSI_SELECT(x) - 1)
+
+/* status bits for INSN_CONFIG_GET_COUNTER_STATUS */
+enum comedi_counter_status_flags {
+ COMEDI_COUNTER_ARMED = 0x1,
+ COMEDI_COUNTER_COUNTING = 0x2,
+ COMEDI_COUNTER_TERMINAL_COUNT = 0x4,
+};
+
+/* Clock sources for CDIO subdevice on NI m-series boards. Used as the
+ * scan_begin_arg for a comedi_command. These sources may also be bitwise-or'd
+ * with CR_INVERT to change polarity. */
+enum ni_m_series_cdio_scan_begin_src {
+ NI_CDIO_SCAN_BEGIN_SRC_GROUND = 0,
+ NI_CDIO_SCAN_BEGIN_SRC_AI_START = 18,
+ NI_CDIO_SCAN_BEGIN_SRC_AI_CONVERT = 19,
+ NI_CDIO_SCAN_BEGIN_SRC_PXI_STAR_TRIGGER = 20,
+ NI_CDIO_SCAN_BEGIN_SRC_G0_OUT = 28,
+ NI_CDIO_SCAN_BEGIN_SRC_G1_OUT = 29,
+ NI_CDIO_SCAN_BEGIN_SRC_ANALOG_TRIGGER = 30,
+ NI_CDIO_SCAN_BEGIN_SRC_AO_UPDATE = 31,
+ NI_CDIO_SCAN_BEGIN_SRC_FREQ_OUT = 32,
+ NI_CDIO_SCAN_BEGIN_SRC_DIO_CHANGE_DETECT_IRQ = 33
+};
+
+#define NI_CDIO_SCAN_BEGIN_SRC_PFI(x) NI_USUAL_PFI_SELECT(x)
+#define NI_CDIO_SCAN_BEGIN_SRC_RTSI(x) NI_USUAL_RTSI_SELECT(x)
+
+/* scan_begin_src for scan_begin_arg==TRIG_EXT with analog output command on NI
+ * boards. These scan begin sources can also be bitwise-or'd with CR_INVERT to
+ * change polarity. */
+#define NI_AO_SCAN_BEGIN_SRC_PFI(x) NI_USUAL_PFI_SELECT(x)
+#define NI_AO_SCAN_BEGIN_SRC_RTSI(x) NI_USUAL_RTSI_SELECT(x)
+
+/* Bits for setting a clock source with
+ * INSN_CONFIG_SET_CLOCK_SRC when using NI frequency output subdevice. */
+enum ni_freq_out_clock_source_bits {
+ NI_FREQ_OUT_TIMEBASE_1_DIV_2_CLOCK_SRC, /* 10 MHz */
+ NI_FREQ_OUT_TIMEBASE_2_CLOCK_SRC /* 100 KHz */
+};
+
+/* Values for setting a clock source with INSN_CONFIG_SET_CLOCK_SRC for
+ * 8254 counter subdevices on Amplicon DIO boards (amplc_dio200 driver). */
+enum amplc_dio_clock_source {
+ AMPLC_DIO_CLK_CLKN, /* per channel external clock
+ input/output pin (pin is only an
+ input when clock source set to this
+ value, otherwise it is an output) */
+ AMPLC_DIO_CLK_10MHZ, /* 10 MHz internal clock */
+ AMPLC_DIO_CLK_1MHZ, /* 1 MHz internal clock */
+ AMPLC_DIO_CLK_100KHZ, /* 100 kHz internal clock */
+ AMPLC_DIO_CLK_10KHZ, /* 10 kHz internal clock */
+ AMPLC_DIO_CLK_1KHZ, /* 1 kHz internal clock */
+ AMPLC_DIO_CLK_OUTNM1, /* output of preceding counter channel
+ (for channel 0, preceding counter
+ channel is channel 2 on preceding
+ counter subdevice, for first counter
+ subdevice, preceding counter
+ subdevice is the last counter
+ subdevice) */
+ AMPLC_DIO_CLK_EXT, /* per chip external input pin */
+ /* the following are "enhanced" clock sources for PCIe models */
+ AMPLC_DIO_CLK_VCC, /* clock input HIGH */
+ AMPLC_DIO_CLK_GND, /* clock input LOW */
+ AMPLC_DIO_CLK_PAT_PRESENT, /* "pattern present" signal */
+ AMPLC_DIO_CLK_20MHZ /* 20 MHz internal clock */
+};
+
+/* Values for setting a clock source with INSN_CONFIG_SET_CLOCK_SRC for
+ * timer subdevice on some Amplicon DIO PCIe boards (amplc_dio200 driver). */
+enum amplc_dio_ts_clock_src {
+ AMPLC_DIO_TS_CLK_1GHZ, /* 1 ns period with 20 ns granularity */
+ AMPLC_DIO_TS_CLK_1MHZ, /* 1 us period */
+ AMPLC_DIO_TS_CLK_1KHZ /* 1 ms period */
+};
+
+/* Values for setting a gate source with INSN_CONFIG_SET_GATE_SRC for
+ * 8254 counter subdevices on Amplicon DIO boards (amplc_dio200 driver). */
+enum amplc_dio_gate_source {
+ AMPLC_DIO_GAT_VCC, /* internal high logic level */
+ AMPLC_DIO_GAT_GND, /* internal low logic level */
+ AMPLC_DIO_GAT_GATN, /* per channel external gate input */
+ AMPLC_DIO_GAT_NOUTNM2, /* negated output of counter channel
+ minus 2 (for channels 0 or 1,
+ channel minus 2 is channel 1 or 2 on
+ the preceding counter subdevice, for
+ the first counter subdevice the
+ preceding counter subdevice is the
+ last counter subdevice) */
+ AMPLC_DIO_GAT_RESERVED4,
+ AMPLC_DIO_GAT_RESERVED5,
+ AMPLC_DIO_GAT_RESERVED6,
+ AMPLC_DIO_GAT_RESERVED7,
+ /* the following are "enhanced" gate sources for PCIe models */
+ AMPLC_DIO_GAT_NGATN = 6, /* negated per channel gate input */
+ AMPLC_DIO_GAT_OUTNM2, /* non-negated output of counter
+ channel minus 2 */
+ AMPLC_DIO_GAT_PAT_PRESENT, /* "pattern present" signal */
+ AMPLC_DIO_GAT_PAT_OCCURRED, /* "pattern occurred" latched */
+ AMPLC_DIO_GAT_PAT_GONE, /* "pattern gone away" latched */
+ AMPLC_DIO_GAT_NPAT_PRESENT, /* negated "pattern present" */
+ AMPLC_DIO_GAT_NPAT_OCCURRED, /* negated "pattern occurred" */
+ AMPLC_DIO_GAT_NPAT_GONE /* negated "pattern gone away" */
+};
+
+/*
+ * Values for setting a clock source with INSN_CONFIG_SET_CLOCK_SRC for
+ * the counter subdevice on the Kolter Electronic PCI-Counter board
+ * (ke_counter driver).
+ */
+enum ke_counter_clock_source {
+ KE_CLK_20MHZ, /* internal 20MHz (default) */
+ KE_CLK_4MHZ, /* internal 4MHz (option) */
+ KE_CLK_EXT /* external clock on pin 21 of D-Sub */
+};
+
+#endif /* _COMEDI_H */
diff --git a/drivers/staging/comedi/comedi_buf.c b/drivers/staging/comedi/comedi_buf.c
new file mode 100644
index 000000000..19e7b229d
--- /dev/null
+++ b/drivers/staging/comedi/comedi_buf.c
@@ -0,0 +1,550 @@
+/*
+ * comedi_buf.c
+ *
+ * COMEDI - Linux Control and Measurement Device Interface
+ * Copyright (C) 1997-2000 David A. Schleef <ds@schleef.org>
+ * Copyright (C) 2002 Frank Mori Hess <fmhess@users.sourceforge.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/vmalloc.h>
+#include <linux/slab.h>
+
+#include "comedidev.h"
+#include "comedi_internal.h"
+
+#ifdef PAGE_KERNEL_NOCACHE
+#define COMEDI_PAGE_PROTECTION PAGE_KERNEL_NOCACHE
+#else
+#define COMEDI_PAGE_PROTECTION PAGE_KERNEL
+#endif
+
+static void comedi_buf_map_kref_release(struct kref *kref)
+{
+ struct comedi_buf_map *bm =
+ container_of(kref, struct comedi_buf_map, refcount);
+ struct comedi_buf_page *buf;
+ unsigned int i;
+
+ if (bm->page_list) {
+ for (i = 0; i < bm->n_pages; i++) {
+ buf = &bm->page_list[i];
+ clear_bit(PG_reserved,
+ &(virt_to_page(buf->virt_addr)->flags));
+ if (bm->dma_dir != DMA_NONE) {
+#ifdef CONFIG_HAS_DMA
+ dma_free_coherent(bm->dma_hw_dev,
+ PAGE_SIZE,
+ buf->virt_addr,
+ buf->dma_addr);
+#endif
+ } else {
+ free_page((unsigned long)buf->virt_addr);
+ }
+ }
+ vfree(bm->page_list);
+ }
+ if (bm->dma_dir != DMA_NONE)
+ put_device(bm->dma_hw_dev);
+ kfree(bm);
+}
+
+static void __comedi_buf_free(struct comedi_device *dev,
+ struct comedi_subdevice *s)
+{
+ struct comedi_async *async = s->async;
+ struct comedi_buf_map *bm;
+ unsigned long flags;
+
+ if (async->prealloc_buf) {
+ vunmap(async->prealloc_buf);
+ async->prealloc_buf = NULL;
+ async->prealloc_bufsz = 0;
+ }
+
+ spin_lock_irqsave(&s->spin_lock, flags);
+ bm = async->buf_map;
+ async->buf_map = NULL;
+ spin_unlock_irqrestore(&s->spin_lock, flags);
+ comedi_buf_map_put(bm);
+}
+
+static void __comedi_buf_alloc(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ unsigned n_pages)
+{
+ struct comedi_async *async = s->async;
+ struct page **pages = NULL;
+ struct comedi_buf_map *bm;
+ struct comedi_buf_page *buf;
+ unsigned long flags;
+ unsigned i;
+
+ if (!IS_ENABLED(CONFIG_HAS_DMA) && s->async_dma_dir != DMA_NONE) {
+ dev_err(dev->class_dev,
+ "dma buffer allocation not supported\n");
+ return;
+ }
+
+ bm = kzalloc(sizeof(*async->buf_map), GFP_KERNEL);
+ if (!bm)
+ return;
+
+ kref_init(&bm->refcount);
+ spin_lock_irqsave(&s->spin_lock, flags);
+ async->buf_map = bm;
+ spin_unlock_irqrestore(&s->spin_lock, flags);
+ bm->dma_dir = s->async_dma_dir;
+ if (bm->dma_dir != DMA_NONE)
+ /* Need ref to hardware device to free buffer later. */
+ bm->dma_hw_dev = get_device(dev->hw_dev);
+
+ bm->page_list = vzalloc(sizeof(*buf) * n_pages);
+ if (bm->page_list)
+ pages = vmalloc(sizeof(struct page *) * n_pages);
+
+ if (!pages)
+ return;
+
+ for (i = 0; i < n_pages; i++) {
+ buf = &bm->page_list[i];
+ if (bm->dma_dir != DMA_NONE)
+#ifdef CONFIG_HAS_DMA
+ buf->virt_addr = dma_alloc_coherent(bm->dma_hw_dev,
+ PAGE_SIZE,
+ &buf->dma_addr,
+ GFP_KERNEL |
+ __GFP_COMP);
+#else
+ break;
+#endif
+ else
+ buf->virt_addr = (void *)get_zeroed_page(GFP_KERNEL);
+ if (!buf->virt_addr)
+ break;
+
+ set_bit(PG_reserved, &(virt_to_page(buf->virt_addr)->flags));
+
+ pages[i] = virt_to_page(buf->virt_addr);
+ }
+ spin_lock_irqsave(&s->spin_lock, flags);
+ bm->n_pages = i;
+ spin_unlock_irqrestore(&s->spin_lock, flags);
+
+ /* vmap the prealloc_buf if all the pages were allocated */
+ if (i == n_pages)
+ async->prealloc_buf = vmap(pages, n_pages, VM_MAP,
+ COMEDI_PAGE_PROTECTION);
+
+ vfree(pages);
+}
+
+void comedi_buf_map_get(struct comedi_buf_map *bm)
+{
+ if (bm)
+ kref_get(&bm->refcount);
+}
+
+int comedi_buf_map_put(struct comedi_buf_map *bm)
+{
+ if (bm)
+ return kref_put(&bm->refcount, comedi_buf_map_kref_release);
+ return 1;
+}
+
+/* returns s->async->buf_map and increments its kref refcount */
+struct comedi_buf_map *
+comedi_buf_map_from_subdev_get(struct comedi_subdevice *s)
+{
+ struct comedi_async *async = s->async;
+ struct comedi_buf_map *bm = NULL;
+ unsigned long flags;
+
+ if (!async)
+ return NULL;
+
+ spin_lock_irqsave(&s->spin_lock, flags);
+ bm = async->buf_map;
+ /* only want it if buffer pages allocated */
+ if (bm && bm->n_pages)
+ comedi_buf_map_get(bm);
+ else
+ bm = NULL;
+ spin_unlock_irqrestore(&s->spin_lock, flags);
+
+ return bm;
+}
+
+bool comedi_buf_is_mmapped(struct comedi_subdevice *s)
+{
+ struct comedi_buf_map *bm = s->async->buf_map;
+
+ return bm && (atomic_read(&bm->refcount.refcount) > 1);
+}
+
+int comedi_buf_alloc(struct comedi_device *dev, struct comedi_subdevice *s,
+ unsigned long new_size)
+{
+ struct comedi_async *async = s->async;
+
+ /* Round up new_size to multiple of PAGE_SIZE */
+ new_size = (new_size + PAGE_SIZE - 1) & PAGE_MASK;
+
+ /* if no change is required, do nothing */
+ if (async->prealloc_buf && async->prealloc_bufsz == new_size)
+ return 0;
+
+ /* deallocate old buffer */
+ __comedi_buf_free(dev, s);
+
+ /* allocate new buffer */
+ if (new_size) {
+ unsigned n_pages = new_size >> PAGE_SHIFT;
+
+ __comedi_buf_alloc(dev, s, n_pages);
+
+ if (!async->prealloc_buf) {
+ /* allocation failed */
+ __comedi_buf_free(dev, s);
+ return -ENOMEM;
+ }
+ }
+ async->prealloc_bufsz = new_size;
+
+ return 0;
+}
+
+void comedi_buf_reset(struct comedi_subdevice *s)
+{
+ struct comedi_async *async = s->async;
+
+ async->buf_write_alloc_count = 0;
+ async->buf_write_count = 0;
+ async->buf_read_alloc_count = 0;
+ async->buf_read_count = 0;
+
+ async->buf_write_ptr = 0;
+ async->buf_read_ptr = 0;
+
+ async->cur_chan = 0;
+ async->scans_done = 0;
+ async->scan_progress = 0;
+ async->munge_chan = 0;
+ async->munge_count = 0;
+ async->munge_ptr = 0;
+
+ async->events = 0;
+}
+
+static unsigned int comedi_buf_write_n_available(struct comedi_subdevice *s)
+{
+ struct comedi_async *async = s->async;
+ unsigned int free_end = async->buf_read_count + async->prealloc_bufsz;
+
+ return free_end - async->buf_write_alloc_count;
+}
+
+/* allocates chunk for the writer from free buffer space */
+unsigned int comedi_buf_write_alloc(struct comedi_subdevice *s,
+ unsigned int nbytes)
+{
+ struct comedi_async *async = s->async;
+ unsigned int available = comedi_buf_write_n_available(s);
+
+ if (nbytes > available)
+ nbytes = available;
+
+ async->buf_write_alloc_count += nbytes;
+
+ /*
+ * ensure the async buffer 'counts' are read and updated
+ * before we write data to the write-alloc'ed buffer space
+ */
+ smp_mb();
+
+ return nbytes;
+}
+EXPORT_SYMBOL_GPL(comedi_buf_write_alloc);
+
+/*
+ * munging is applied to data by core as it passes between user
+ * and kernel space
+ */
+static unsigned int comedi_buf_munge(struct comedi_subdevice *s,
+ unsigned int num_bytes)
+{
+ struct comedi_async *async = s->async;
+ unsigned int count = 0;
+ const unsigned num_sample_bytes = comedi_bytes_per_sample(s);
+
+ if (!s->munge || (async->cmd.flags & CMDF_RAWDATA)) {
+ async->munge_count += num_bytes;
+ count = num_bytes;
+ } else {
+ /* don't munge partial samples */
+ num_bytes -= num_bytes % num_sample_bytes;
+ while (count < num_bytes) {
+ int block_size = num_bytes - count;
+ unsigned int buf_end;
+
+ buf_end = async->prealloc_bufsz - async->munge_ptr;
+ if (block_size > buf_end)
+ block_size = buf_end;
+
+ s->munge(s->device, s,
+ async->prealloc_buf + async->munge_ptr,
+ block_size, async->munge_chan);
+
+ /*
+ * ensure data is munged in buffer before the
+ * async buffer munge_count is incremented
+ */
+ smp_wmb();
+
+ async->munge_chan += block_size / num_sample_bytes;
+ async->munge_chan %= async->cmd.chanlist_len;
+ async->munge_count += block_size;
+ async->munge_ptr += block_size;
+ async->munge_ptr %= async->prealloc_bufsz;
+ count += block_size;
+ }
+ }
+
+ return count;
+}
+
+unsigned int comedi_buf_write_n_allocated(struct comedi_subdevice *s)
+{
+ struct comedi_async *async = s->async;
+
+ return async->buf_write_alloc_count - async->buf_write_count;
+}
+
+/* transfers a chunk from writer to filled buffer space */
+unsigned int comedi_buf_write_free(struct comedi_subdevice *s,
+ unsigned int nbytes)
+{
+ struct comedi_async *async = s->async;
+ unsigned int allocated = comedi_buf_write_n_allocated(s);
+
+ if (nbytes > allocated)
+ nbytes = allocated;
+
+ async->buf_write_count += nbytes;
+ async->buf_write_ptr += nbytes;
+ comedi_buf_munge(s, async->buf_write_count - async->munge_count);
+ if (async->buf_write_ptr >= async->prealloc_bufsz)
+ async->buf_write_ptr %= async->prealloc_bufsz;
+
+ return nbytes;
+}
+EXPORT_SYMBOL_GPL(comedi_buf_write_free);
+
+unsigned int comedi_buf_read_n_available(struct comedi_subdevice *s)
+{
+ struct comedi_async *async = s->async;
+ unsigned num_bytes;
+
+ if (!async)
+ return 0;
+
+ num_bytes = async->munge_count - async->buf_read_count;
+
+ /*
+ * ensure the async buffer 'counts' are read before we
+ * attempt to read data from the buffer
+ */
+ smp_rmb();
+
+ return num_bytes;
+}
+EXPORT_SYMBOL_GPL(comedi_buf_read_n_available);
+
+/* allocates a chunk for the reader from filled (and munged) buffer space */
+unsigned int comedi_buf_read_alloc(struct comedi_subdevice *s,
+ unsigned int nbytes)
+{
+ struct comedi_async *async = s->async;
+ unsigned int available;
+
+ available = async->munge_count - async->buf_read_alloc_count;
+ if (nbytes > available)
+ nbytes = available;
+
+ async->buf_read_alloc_count += nbytes;
+
+ /*
+ * ensure the async buffer 'counts' are read before we
+ * attempt to read data from the read-alloc'ed buffer space
+ */
+ smp_rmb();
+
+ return nbytes;
+}
+EXPORT_SYMBOL_GPL(comedi_buf_read_alloc);
+
+static unsigned int comedi_buf_read_n_allocated(struct comedi_async *async)
+{
+ return async->buf_read_alloc_count - async->buf_read_count;
+}
+
+/* transfers control of a chunk from reader to free buffer space */
+unsigned int comedi_buf_read_free(struct comedi_subdevice *s,
+ unsigned int nbytes)
+{
+ struct comedi_async *async = s->async;
+ unsigned int allocated;
+
+ /*
+ * ensure data has been read out of buffer before
+ * the async read count is incremented
+ */
+ smp_mb();
+
+ allocated = comedi_buf_read_n_allocated(async);
+ if (nbytes > allocated)
+ nbytes = allocated;
+
+ async->buf_read_count += nbytes;
+ async->buf_read_ptr += nbytes;
+ async->buf_read_ptr %= async->prealloc_bufsz;
+ return nbytes;
+}
+EXPORT_SYMBOL_GPL(comedi_buf_read_free);
+
+static void comedi_buf_memcpy_to(struct comedi_subdevice *s,
+ const void *data, unsigned int num_bytes)
+{
+ struct comedi_async *async = s->async;
+ unsigned int write_ptr = async->buf_write_ptr;
+
+ while (num_bytes) {
+ unsigned int block_size;
+
+ if (write_ptr + num_bytes > async->prealloc_bufsz)
+ block_size = async->prealloc_bufsz - write_ptr;
+ else
+ block_size = num_bytes;
+
+ memcpy(async->prealloc_buf + write_ptr, data, block_size);
+
+ data += block_size;
+ num_bytes -= block_size;
+
+ write_ptr = 0;
+ }
+}
+
+static void comedi_buf_memcpy_from(struct comedi_subdevice *s,
+ void *dest, unsigned int nbytes)
+{
+ void *src;
+ struct comedi_async *async = s->async;
+ unsigned int read_ptr = async->buf_read_ptr;
+
+ while (nbytes) {
+ unsigned int block_size;
+
+ src = async->prealloc_buf + read_ptr;
+
+ if (nbytes >= async->prealloc_bufsz - read_ptr)
+ block_size = async->prealloc_bufsz - read_ptr;
+ else
+ block_size = nbytes;
+
+ memcpy(dest, src, block_size);
+ nbytes -= block_size;
+ dest += block_size;
+ read_ptr = 0;
+ }
+}
+
+/**
+ * comedi_buf_write_samples - write sample data to comedi buffer
+ * @s: comedi_subdevice struct
+ * @data: samples
+ * @nsamples: number of samples
+ *
+ * Writes nsamples to the comedi buffer associated with the subdevice, marks
+ * it as written and updates the acquisition scan progress.
+ *
+ * Returns the amount of data written in bytes.
+ */
+unsigned int comedi_buf_write_samples(struct comedi_subdevice *s,
+ const void *data, unsigned int nsamples)
+{
+ unsigned int max_samples;
+ unsigned int nbytes;
+
+ /*
+ * Make sure there is enough room in the buffer for all the samples.
+ * If not, clamp the nsamples to the number that will fit, flag the
+ * buffer overrun and add the samples that fit.
+ */
+ max_samples = comedi_bytes_to_samples(s,
+ comedi_buf_write_n_available(s));
+ if (nsamples > max_samples) {
+ dev_warn(s->device->class_dev, "buffer overrun\n");
+ s->async->events |= COMEDI_CB_OVERFLOW;
+ nsamples = max_samples;
+ }
+
+ if (nsamples == 0)
+ return 0;
+
+ nbytes = comedi_buf_write_alloc(s,
+ comedi_samples_to_bytes(s, nsamples));
+ comedi_buf_memcpy_to(s, data, nbytes);
+ comedi_buf_write_free(s, nbytes);
+ comedi_inc_scan_progress(s, nbytes);
+ s->async->events |= COMEDI_CB_BLOCK;
+
+ return nbytes;
+}
+EXPORT_SYMBOL_GPL(comedi_buf_write_samples);
+
+/**
+ * comedi_buf_read_samples - read sample data from comedi buffer
+ * @s: comedi_subdevice struct
+ * @data: destination
+ * @nsamples: maximum number of samples to read
+ *
+ * Reads up to nsamples from the comedi buffer associated with the subdevice,
+ * marks it as read and updates the acquisition scan progress.
+ *
+ * Returns the amount of data read in bytes.
+ */
+unsigned int comedi_buf_read_samples(struct comedi_subdevice *s,
+ void *data, unsigned int nsamples)
+{
+ unsigned int max_samples;
+ unsigned int nbytes;
+
+ /* clamp nsamples to the number of full samples available */
+ max_samples = comedi_bytes_to_samples(s,
+ comedi_buf_read_n_available(s));
+ if (nsamples > max_samples)
+ nsamples = max_samples;
+
+ if (nsamples == 0)
+ return 0;
+
+ nbytes = comedi_buf_read_alloc(s,
+ comedi_samples_to_bytes(s, nsamples));
+ comedi_buf_memcpy_from(s, data, nbytes);
+ comedi_buf_read_free(s, nbytes);
+ comedi_inc_scan_progress(s, nbytes);
+ s->async->events |= COMEDI_CB_BLOCK;
+
+ return nbytes;
+}
+EXPORT_SYMBOL_GPL(comedi_buf_read_samples);
diff --git a/drivers/staging/comedi/comedi_compat32.c b/drivers/staging/comedi/comedi_compat32.c
new file mode 100644
index 000000000..25848244c
--- /dev/null
+++ b/drivers/staging/comedi/comedi_compat32.c
@@ -0,0 +1,463 @@
+/*
+ * comedi/comedi_compat32.c
+ * 32-bit ioctl compatibility for 64-bit comedi kernel module.
+ *
+ * Author: Ian Abbott, MEV Ltd. <abbotti@mev.co.uk>
+ * Copyright (C) 2007 MEV Ltd. <http://www.mev.co.uk/>
+ *
+ * COMEDI - Linux Control and Measurement Device Interface
+ * Copyright (C) 1997-2007 David A. Schleef <ds@schleef.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/uaccess.h>
+#include <linux/compat.h>
+#include <linux/fs.h>
+#include "comedi.h"
+#include "comedi_compat32.h"
+
+#define COMEDI32_CHANINFO _IOR(CIO, 3, struct comedi32_chaninfo_struct)
+#define COMEDI32_RANGEINFO _IOR(CIO, 8, struct comedi32_rangeinfo_struct)
+/*
+ * N.B. COMEDI32_CMD and COMEDI_CMD ought to use _IOWR, not _IOR.
+ * It's too late to change it now, but it only affects the command number.
+ */
+#define COMEDI32_CMD _IOR(CIO, 9, struct comedi32_cmd_struct)
+/*
+ * N.B. COMEDI32_CMDTEST and COMEDI_CMDTEST ought to use _IOWR, not _IOR.
+ * It's too late to change it now, but it only affects the command number.
+ */
+#define COMEDI32_CMDTEST _IOR(CIO, 10, struct comedi32_cmd_struct)
+#define COMEDI32_INSNLIST _IOR(CIO, 11, struct comedi32_insnlist_struct)
+#define COMEDI32_INSN _IOR(CIO, 12, struct comedi32_insn_struct)
+
+struct comedi32_chaninfo_struct {
+ unsigned int subdev;
+ compat_uptr_t maxdata_list; /* 32-bit 'unsigned int *' */
+ compat_uptr_t flaglist; /* 32-bit 'unsigned int *' */
+ compat_uptr_t rangelist; /* 32-bit 'unsigned int *' */
+ unsigned int unused[4];
+};
+
+struct comedi32_rangeinfo_struct {
+ unsigned int range_type;
+ compat_uptr_t range_ptr; /* 32-bit 'void *' */
+};
+
+struct comedi32_cmd_struct {
+ unsigned int subdev;
+ unsigned int flags;
+ unsigned int start_src;
+ unsigned int start_arg;
+ unsigned int scan_begin_src;
+ unsigned int scan_begin_arg;
+ unsigned int convert_src;
+ unsigned int convert_arg;
+ unsigned int scan_end_src;
+ unsigned int scan_end_arg;
+ unsigned int stop_src;
+ unsigned int stop_arg;
+ compat_uptr_t chanlist; /* 32-bit 'unsigned int *' */
+ unsigned int chanlist_len;
+ compat_uptr_t data; /* 32-bit 'short *' */
+ unsigned int data_len;
+};
+
+struct comedi32_insn_struct {
+ unsigned int insn;
+ unsigned int n;
+ compat_uptr_t data; /* 32-bit 'unsigned int *' */
+ unsigned int subdev;
+ unsigned int chanspec;
+ unsigned int unused[3];
+};
+
+struct comedi32_insnlist_struct {
+ unsigned int n_insns;
+ compat_uptr_t insns; /* 32-bit 'struct comedi_insn *' */
+};
+
+/* Handle translated ioctl. */
+static int translated_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ if (file->f_op->unlocked_ioctl)
+ return file->f_op->unlocked_ioctl(file, cmd, arg);
+
+ return -ENOTTY;
+}
+
+/* Handle 32-bit COMEDI_CHANINFO ioctl. */
+static int compat_chaninfo(struct file *file, unsigned long arg)
+{
+ struct comedi_chaninfo __user *chaninfo;
+ struct comedi32_chaninfo_struct __user *chaninfo32;
+ int err;
+ union {
+ unsigned int uint;
+ compat_uptr_t uptr;
+ } temp;
+
+ chaninfo32 = compat_ptr(arg);
+ chaninfo = compat_alloc_user_space(sizeof(*chaninfo));
+
+ /* Copy chaninfo structure. Ignore unused members. */
+ if (!access_ok(VERIFY_READ, chaninfo32, sizeof(*chaninfo32)) ||
+ !access_ok(VERIFY_WRITE, chaninfo, sizeof(*chaninfo)))
+ return -EFAULT;
+
+ err = 0;
+ err |= __get_user(temp.uint, &chaninfo32->subdev);
+ err |= __put_user(temp.uint, &chaninfo->subdev);
+ err |= __get_user(temp.uptr, &chaninfo32->maxdata_list);
+ err |= __put_user(compat_ptr(temp.uptr), &chaninfo->maxdata_list);
+ err |= __get_user(temp.uptr, &chaninfo32->flaglist);
+ err |= __put_user(compat_ptr(temp.uptr), &chaninfo->flaglist);
+ err |= __get_user(temp.uptr, &chaninfo32->rangelist);
+ err |= __put_user(compat_ptr(temp.uptr), &chaninfo->rangelist);
+ if (err)
+ return -EFAULT;
+
+ return translated_ioctl(file, COMEDI_CHANINFO, (unsigned long)chaninfo);
+}
+
+/* Handle 32-bit COMEDI_RANGEINFO ioctl. */
+static int compat_rangeinfo(struct file *file, unsigned long arg)
+{
+ struct comedi_rangeinfo __user *rangeinfo;
+ struct comedi32_rangeinfo_struct __user *rangeinfo32;
+ int err;
+ union {
+ unsigned int uint;
+ compat_uptr_t uptr;
+ } temp;
+
+ rangeinfo32 = compat_ptr(arg);
+ rangeinfo = compat_alloc_user_space(sizeof(*rangeinfo));
+
+ /* Copy rangeinfo structure. */
+ if (!access_ok(VERIFY_READ, rangeinfo32, sizeof(*rangeinfo32)) ||
+ !access_ok(VERIFY_WRITE, rangeinfo, sizeof(*rangeinfo)))
+ return -EFAULT;
+
+ err = 0;
+ err |= __get_user(temp.uint, &rangeinfo32->range_type);
+ err |= __put_user(temp.uint, &rangeinfo->range_type);
+ err |= __get_user(temp.uptr, &rangeinfo32->range_ptr);
+ err |= __put_user(compat_ptr(temp.uptr), &rangeinfo->range_ptr);
+ if (err)
+ return -EFAULT;
+
+ return translated_ioctl(file, COMEDI_RANGEINFO,
+ (unsigned long)rangeinfo);
+}
+
+/* Copy 32-bit cmd structure to native cmd structure. */
+static int get_compat_cmd(struct comedi_cmd __user *cmd,
+ struct comedi32_cmd_struct __user *cmd32)
+{
+ int err;
+ union {
+ unsigned int uint;
+ compat_uptr_t uptr;
+ } temp;
+
+ /* Copy cmd structure. */
+ if (!access_ok(VERIFY_READ, cmd32, sizeof(*cmd32)) ||
+ !access_ok(VERIFY_WRITE, cmd, sizeof(*cmd)))
+ return -EFAULT;
+
+ err = 0;
+ err |= __get_user(temp.uint, &cmd32->subdev);
+ err |= __put_user(temp.uint, &cmd->subdev);
+ err |= __get_user(temp.uint, &cmd32->flags);
+ err |= __put_user(temp.uint, &cmd->flags);
+ err |= __get_user(temp.uint, &cmd32->start_src);
+ err |= __put_user(temp.uint, &cmd->start_src);
+ err |= __get_user(temp.uint, &cmd32->start_arg);
+ err |= __put_user(temp.uint, &cmd->start_arg);
+ err |= __get_user(temp.uint, &cmd32->scan_begin_src);
+ err |= __put_user(temp.uint, &cmd->scan_begin_src);
+ err |= __get_user(temp.uint, &cmd32->scan_begin_arg);
+ err |= __put_user(temp.uint, &cmd->scan_begin_arg);
+ err |= __get_user(temp.uint, &cmd32->convert_src);
+ err |= __put_user(temp.uint, &cmd->convert_src);
+ err |= __get_user(temp.uint, &cmd32->convert_arg);
+ err |= __put_user(temp.uint, &cmd->convert_arg);
+ err |= __get_user(temp.uint, &cmd32->scan_end_src);
+ err |= __put_user(temp.uint, &cmd->scan_end_src);
+ err |= __get_user(temp.uint, &cmd32->scan_end_arg);
+ err |= __put_user(temp.uint, &cmd->scan_end_arg);
+ err |= __get_user(temp.uint, &cmd32->stop_src);
+ err |= __put_user(temp.uint, &cmd->stop_src);
+ err |= __get_user(temp.uint, &cmd32->stop_arg);
+ err |= __put_user(temp.uint, &cmd->stop_arg);
+ err |= __get_user(temp.uptr, &cmd32->chanlist);
+ err |= __put_user(compat_ptr(temp.uptr), &cmd->chanlist);
+ err |= __get_user(temp.uint, &cmd32->chanlist_len);
+ err |= __put_user(temp.uint, &cmd->chanlist_len);
+ err |= __get_user(temp.uptr, &cmd32->data);
+ err |= __put_user(compat_ptr(temp.uptr), &cmd->data);
+ err |= __get_user(temp.uint, &cmd32->data_len);
+ err |= __put_user(temp.uint, &cmd->data_len);
+ return err ? -EFAULT : 0;
+}
+
+/* Copy native cmd structure to 32-bit cmd structure. */
+static int put_compat_cmd(struct comedi32_cmd_struct __user *cmd32,
+ struct comedi_cmd __user *cmd)
+{
+ int err;
+ unsigned int temp;
+
+ /*
+ * Copy back most of cmd structure.
+ *
+ * Assume the pointer values are already valid.
+ * (Could use ptr_to_compat() to set them.)
+ */
+ if (!access_ok(VERIFY_READ, cmd, sizeof(*cmd)) ||
+ !access_ok(VERIFY_WRITE, cmd32, sizeof(*cmd32)))
+ return -EFAULT;
+
+ err = 0;
+ err |= __get_user(temp, &cmd->subdev);
+ err |= __put_user(temp, &cmd32->subdev);
+ err |= __get_user(temp, &cmd->flags);
+ err |= __put_user(temp, &cmd32->flags);
+ err |= __get_user(temp, &cmd->start_src);
+ err |= __put_user(temp, &cmd32->start_src);
+ err |= __get_user(temp, &cmd->start_arg);
+ err |= __put_user(temp, &cmd32->start_arg);
+ err |= __get_user(temp, &cmd->scan_begin_src);
+ err |= __put_user(temp, &cmd32->scan_begin_src);
+ err |= __get_user(temp, &cmd->scan_begin_arg);
+ err |= __put_user(temp, &cmd32->scan_begin_arg);
+ err |= __get_user(temp, &cmd->convert_src);
+ err |= __put_user(temp, &cmd32->convert_src);
+ err |= __get_user(temp, &cmd->convert_arg);
+ err |= __put_user(temp, &cmd32->convert_arg);
+ err |= __get_user(temp, &cmd->scan_end_src);
+ err |= __put_user(temp, &cmd32->scan_end_src);
+ err |= __get_user(temp, &cmd->scan_end_arg);
+ err |= __put_user(temp, &cmd32->scan_end_arg);
+ err |= __get_user(temp, &cmd->stop_src);
+ err |= __put_user(temp, &cmd32->stop_src);
+ err |= __get_user(temp, &cmd->stop_arg);
+ err |= __put_user(temp, &cmd32->stop_arg);
+ /* Assume chanlist pointer is unchanged. */
+ err |= __get_user(temp, &cmd->chanlist_len);
+ err |= __put_user(temp, &cmd32->chanlist_len);
+ /* Assume data pointer is unchanged. */
+ err |= __get_user(temp, &cmd->data_len);
+ err |= __put_user(temp, &cmd32->data_len);
+ return err ? -EFAULT : 0;
+}
+
+/* Handle 32-bit COMEDI_CMD ioctl. */
+static int compat_cmd(struct file *file, unsigned long arg)
+{
+ struct comedi_cmd __user *cmd;
+ struct comedi32_cmd_struct __user *cmd32;
+ int rc, err;
+
+ cmd32 = compat_ptr(arg);
+ cmd = compat_alloc_user_space(sizeof(*cmd));
+
+ rc = get_compat_cmd(cmd, cmd32);
+ if (rc)
+ return rc;
+
+ rc = translated_ioctl(file, COMEDI_CMD, (unsigned long)cmd);
+ if (rc == -EAGAIN) {
+ /* Special case: copy cmd back to user. */
+ err = put_compat_cmd(cmd32, cmd);
+ if (err)
+ rc = err;
+ }
+
+ return rc;
+}
+
+/* Handle 32-bit COMEDI_CMDTEST ioctl. */
+static int compat_cmdtest(struct file *file, unsigned long arg)
+{
+ struct comedi_cmd __user *cmd;
+ struct comedi32_cmd_struct __user *cmd32;
+ int rc, err;
+
+ cmd32 = compat_ptr(arg);
+ cmd = compat_alloc_user_space(sizeof(*cmd));
+
+ rc = get_compat_cmd(cmd, cmd32);
+ if (rc)
+ return rc;
+
+ rc = translated_ioctl(file, COMEDI_CMDTEST, (unsigned long)cmd);
+ if (rc < 0)
+ return rc;
+
+ err = put_compat_cmd(cmd32, cmd);
+ if (err)
+ rc = err;
+
+ return rc;
+}
+
+/* Copy 32-bit insn structure to native insn structure. */
+static int get_compat_insn(struct comedi_insn __user *insn,
+ struct comedi32_insn_struct __user *insn32)
+{
+ int err;
+ union {
+ unsigned int uint;
+ compat_uptr_t uptr;
+ } temp;
+
+ /* Copy insn structure. Ignore the unused members. */
+ err = 0;
+ if (!access_ok(VERIFY_READ, insn32, sizeof(*insn32)) ||
+ !access_ok(VERIFY_WRITE, insn, sizeof(*insn)))
+ return -EFAULT;
+
+ err |= __get_user(temp.uint, &insn32->insn);
+ err |= __put_user(temp.uint, &insn->insn);
+ err |= __get_user(temp.uint, &insn32->n);
+ err |= __put_user(temp.uint, &insn->n);
+ err |= __get_user(temp.uptr, &insn32->data);
+ err |= __put_user(compat_ptr(temp.uptr), &insn->data);
+ err |= __get_user(temp.uint, &insn32->subdev);
+ err |= __put_user(temp.uint, &insn->subdev);
+ err |= __get_user(temp.uint, &insn32->chanspec);
+ err |= __put_user(temp.uint, &insn->chanspec);
+ return err ? -EFAULT : 0;
+}
+
+/* Handle 32-bit COMEDI_INSNLIST ioctl. */
+static int compat_insnlist(struct file *file, unsigned long arg)
+{
+ struct combined_insnlist {
+ struct comedi_insnlist insnlist;
+ struct comedi_insn insn[1];
+ } __user *s;
+ struct comedi32_insnlist_struct __user *insnlist32;
+ struct comedi32_insn_struct __user *insn32;
+ compat_uptr_t uptr;
+ unsigned int n_insns, n;
+ int err, rc;
+
+ insnlist32 = compat_ptr(arg);
+
+ /* Get 32-bit insnlist structure. */
+ if (!access_ok(VERIFY_READ, insnlist32, sizeof(*insnlist32)))
+ return -EFAULT;
+
+ err = 0;
+ err |= __get_user(n_insns, &insnlist32->n_insns);
+ err |= __get_user(uptr, &insnlist32->insns);
+ insn32 = compat_ptr(uptr);
+ if (err)
+ return -EFAULT;
+
+ /* Allocate user memory to copy insnlist and insns into. */
+ s = compat_alloc_user_space(offsetof(struct combined_insnlist,
+ insn[n_insns]));
+
+ /* Set native insnlist structure. */
+ if (!access_ok(VERIFY_WRITE, &s->insnlist, sizeof(s->insnlist)))
+ return -EFAULT;
+
+ err |= __put_user(n_insns, &s->insnlist.n_insns);
+ err |= __put_user(&s->insn[0], &s->insnlist.insns);
+ if (err)
+ return -EFAULT;
+
+ /* Copy insn structures. */
+ for (n = 0; n < n_insns; n++) {
+ rc = get_compat_insn(&s->insn[n], &insn32[n]);
+ if (rc)
+ return rc;
+ }
+
+ return translated_ioctl(file, COMEDI_INSNLIST,
+ (unsigned long)&s->insnlist);
+}
+
+/* Handle 32-bit COMEDI_INSN ioctl. */
+static int compat_insn(struct file *file, unsigned long arg)
+{
+ struct comedi_insn __user *insn;
+ struct comedi32_insn_struct __user *insn32;
+ int rc;
+
+ insn32 = compat_ptr(arg);
+ insn = compat_alloc_user_space(sizeof(*insn));
+
+ rc = get_compat_insn(insn, insn32);
+ if (rc)
+ return rc;
+
+ return translated_ioctl(file, COMEDI_INSN, (unsigned long)insn);
+}
+
+/*
+ * compat_ioctl file operation.
+ *
+ * Returns -ENOIOCTLCMD for unrecognised ioctl codes.
+ */
+long comedi_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+ int rc;
+
+ switch (cmd) {
+ case COMEDI_DEVCONFIG:
+ case COMEDI_DEVINFO:
+ case COMEDI_SUBDINFO:
+ case COMEDI_BUFCONFIG:
+ case COMEDI_BUFINFO:
+ /* Just need to translate the pointer argument. */
+ arg = (unsigned long)compat_ptr(arg);
+ rc = translated_ioctl(file, cmd, arg);
+ break;
+ case COMEDI_LOCK:
+ case COMEDI_UNLOCK:
+ case COMEDI_CANCEL:
+ case COMEDI_POLL:
+ case COMEDI_SETRSUBD:
+ case COMEDI_SETWSUBD:
+ /* No translation needed. */
+ rc = translated_ioctl(file, cmd, arg);
+ break;
+ case COMEDI32_CHANINFO:
+ rc = compat_chaninfo(file, arg);
+ break;
+ case COMEDI32_RANGEINFO:
+ rc = compat_rangeinfo(file, arg);
+ break;
+ case COMEDI32_CMD:
+ rc = compat_cmd(file, arg);
+ break;
+ case COMEDI32_CMDTEST:
+ rc = compat_cmdtest(file, arg);
+ break;
+ case COMEDI32_INSNLIST:
+ rc = compat_insnlist(file, arg);
+ break;
+ case COMEDI32_INSN:
+ rc = compat_insn(file, arg);
+ break;
+ default:
+ rc = -ENOIOCTLCMD;
+ break;
+ }
+ return rc;
+}
diff --git a/drivers/staging/comedi/comedi_compat32.h b/drivers/staging/comedi/comedi_compat32.h
new file mode 100644
index 000000000..5ce77f3e8
--- /dev/null
+++ b/drivers/staging/comedi/comedi_compat32.h
@@ -0,0 +1,36 @@
+/*
+ * comedi/comedi_compat32.h
+ * 32-bit ioctl compatibility for 64-bit comedi kernel module.
+ *
+ * Author: Ian Abbott, MEV Ltd. <abbotti@mev.co.uk>
+ * Copyright (C) 2007 MEV Ltd. <http://www.mev.co.uk/>
+ *
+ * COMEDI - Linux Control and Measurement Device Interface
+ * Copyright (C) 1997-2007 David A. Schleef <ds@schleef.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _COMEDI_COMPAT32_H
+#define _COMEDI_COMPAT32_H
+
+#ifdef CONFIG_COMPAT
+
+struct file;
+long comedi_compat_ioctl(struct file *, unsigned int cmd, unsigned long arg);
+
+#else /* CONFIG_COMPAT */
+
+#define comedi_compat_ioctl NULL
+
+#endif /* CONFIG_COMPAT */
+
+#endif /* _COMEDI_COMPAT32_H */
diff --git a/drivers/staging/comedi/comedi_fops.c b/drivers/staging/comedi/comedi_fops.c
new file mode 100644
index 000000000..e78ddbe5a
--- /dev/null
+++ b/drivers/staging/comedi/comedi_fops.c
@@ -0,0 +1,2921 @@
+/*
+ * comedi/comedi_fops.c
+ * comedi kernel module
+ *
+ * COMEDI - Linux Control and Measurement Device Interface
+ * Copyright (C) 1997-2000 David A. Schleef <ds@schleef.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include "comedi_compat32.h"
+
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/fcntl.h>
+#include <linux/delay.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <linux/kmod.h>
+#include <linux/poll.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/vmalloc.h>
+#include <linux/fs.h>
+#include "comedidev.h"
+#include <linux/cdev.h>
+#include <linux/stat.h>
+
+#include <linux/io.h>
+#include <linux/uaccess.h>
+
+#include "comedi_internal.h"
+
+/**
+ * struct comedi_file - per-file private data for comedi device
+ * @dev: comedi_device struct
+ * @read_subdev: current "read" subdevice
+ * @write_subdev: current "write" subdevice
+ * @last_detach_count: last known detach count
+ * @last_attached: last known attached/detached state
+ */
+struct comedi_file {
+ struct comedi_device *dev;
+ struct comedi_subdevice *read_subdev;
+ struct comedi_subdevice *write_subdev;
+ unsigned int last_detach_count;
+ bool last_attached:1;
+};
+
+#define COMEDI_NUM_MINORS 0x100
+#define COMEDI_NUM_SUBDEVICE_MINORS \
+ (COMEDI_NUM_MINORS - COMEDI_NUM_BOARD_MINORS)
+
+static int comedi_num_legacy_minors;
+module_param(comedi_num_legacy_minors, int, S_IRUGO);
+MODULE_PARM_DESC(comedi_num_legacy_minors,
+ "number of comedi minor devices to reserve for non-auto-configured devices (default 0)"
+ );
+
+unsigned int comedi_default_buf_size_kb = CONFIG_COMEDI_DEFAULT_BUF_SIZE_KB;
+module_param(comedi_default_buf_size_kb, uint, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(comedi_default_buf_size_kb,
+ "default asynchronous buffer size in KiB (default "
+ __MODULE_STRING(CONFIG_COMEDI_DEFAULT_BUF_SIZE_KB) ")");
+
+unsigned int comedi_default_buf_maxsize_kb
+ = CONFIG_COMEDI_DEFAULT_BUF_MAXSIZE_KB;
+module_param(comedi_default_buf_maxsize_kb, uint, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(comedi_default_buf_maxsize_kb,
+ "default maximum size of asynchronous buffer in KiB (default "
+ __MODULE_STRING(CONFIG_COMEDI_DEFAULT_BUF_MAXSIZE_KB) ")");
+
+static DEFINE_MUTEX(comedi_board_minor_table_lock);
+static struct comedi_device
+*comedi_board_minor_table[COMEDI_NUM_BOARD_MINORS];
+
+static DEFINE_MUTEX(comedi_subdevice_minor_table_lock);
+/* Note: indexed by minor - COMEDI_NUM_BOARD_MINORS. */
+static struct comedi_subdevice
+*comedi_subdevice_minor_table[COMEDI_NUM_SUBDEVICE_MINORS];
+
+static struct class *comedi_class;
+static struct cdev comedi_cdev;
+
+static void comedi_device_init(struct comedi_device *dev)
+{
+ kref_init(&dev->refcount);
+ spin_lock_init(&dev->spinlock);
+ mutex_init(&dev->mutex);
+ init_rwsem(&dev->attach_lock);
+ dev->minor = -1;
+}
+
+static void comedi_dev_kref_release(struct kref *kref)
+{
+ struct comedi_device *dev =
+ container_of(kref, struct comedi_device, refcount);
+
+ mutex_destroy(&dev->mutex);
+ put_device(dev->class_dev);
+ kfree(dev);
+}
+
+/**
+ * comedi_dev_put - release a use of a comedi device structure
+ * @dev: comedi_device struct
+ *
+ * Must be called when a user of a comedi device is finished with it.
+ * When the last user of the comedi device calls this function, the
+ * comedi device is destroyed.
+ *
+ * Return 1 if the comedi device is destroyed by this call or dev is
+ * NULL, otherwise return 0. Callers must not assume the comedi
+ * device is still valid if this function returns 0.
+ */
+int comedi_dev_put(struct comedi_device *dev)
+{
+ if (dev)
+ return kref_put(&dev->refcount, comedi_dev_kref_release);
+ return 1;
+}
+EXPORT_SYMBOL_GPL(comedi_dev_put);
+
+static struct comedi_device *comedi_dev_get(struct comedi_device *dev)
+{
+ if (dev)
+ kref_get(&dev->refcount);
+ return dev;
+}
+
+static void comedi_device_cleanup(struct comedi_device *dev)
+{
+ struct module *driver_module = NULL;
+
+ if (!dev)
+ return;
+ mutex_lock(&dev->mutex);
+ if (dev->attached)
+ driver_module = dev->driver->module;
+ comedi_device_detach(dev);
+ if (driver_module && dev->use_count)
+ module_put(driver_module);
+ mutex_unlock(&dev->mutex);
+}
+
+static bool comedi_clear_board_dev(struct comedi_device *dev)
+{
+ unsigned int i = dev->minor;
+ bool cleared = false;
+
+ mutex_lock(&comedi_board_minor_table_lock);
+ if (dev == comedi_board_minor_table[i]) {
+ comedi_board_minor_table[i] = NULL;
+ cleared = true;
+ }
+ mutex_unlock(&comedi_board_minor_table_lock);
+ return cleared;
+}
+
+static struct comedi_device *comedi_clear_board_minor(unsigned minor)
+{
+ struct comedi_device *dev;
+
+ mutex_lock(&comedi_board_minor_table_lock);
+ dev = comedi_board_minor_table[minor];
+ comedi_board_minor_table[minor] = NULL;
+ mutex_unlock(&comedi_board_minor_table_lock);
+ return dev;
+}
+
+static void comedi_free_board_dev(struct comedi_device *dev)
+{
+ if (dev) {
+ comedi_device_cleanup(dev);
+ if (dev->class_dev) {
+ device_destroy(comedi_class,
+ MKDEV(COMEDI_MAJOR, dev->minor));
+ }
+ comedi_dev_put(dev);
+ }
+}
+
+static struct comedi_subdevice
+*comedi_subdevice_from_minor(const struct comedi_device *dev, unsigned minor)
+{
+ struct comedi_subdevice *s;
+ unsigned int i = minor - COMEDI_NUM_BOARD_MINORS;
+
+ BUG_ON(i >= COMEDI_NUM_SUBDEVICE_MINORS);
+ mutex_lock(&comedi_subdevice_minor_table_lock);
+ s = comedi_subdevice_minor_table[i];
+ if (s && s->device != dev)
+ s = NULL;
+ mutex_unlock(&comedi_subdevice_minor_table_lock);
+ return s;
+}
+
+static struct comedi_device *comedi_dev_get_from_board_minor(unsigned minor)
+{
+ struct comedi_device *dev;
+
+ BUG_ON(minor >= COMEDI_NUM_BOARD_MINORS);
+ mutex_lock(&comedi_board_minor_table_lock);
+ dev = comedi_dev_get(comedi_board_minor_table[minor]);
+ mutex_unlock(&comedi_board_minor_table_lock);
+ return dev;
+}
+
+static struct comedi_device *comedi_dev_get_from_subdevice_minor(unsigned minor)
+{
+ struct comedi_device *dev;
+ struct comedi_subdevice *s;
+ unsigned int i = minor - COMEDI_NUM_BOARD_MINORS;
+
+ BUG_ON(i >= COMEDI_NUM_SUBDEVICE_MINORS);
+ mutex_lock(&comedi_subdevice_minor_table_lock);
+ s = comedi_subdevice_minor_table[i];
+ dev = comedi_dev_get(s ? s->device : NULL);
+ mutex_unlock(&comedi_subdevice_minor_table_lock);
+ return dev;
+}
+
+/**
+ * comedi_dev_get_from_minor - get comedi device by minor device number
+ * @minor: minor device number
+ *
+ * Finds the comedi device associated by the minor device number, if any,
+ * and increments its reference count. The comedi device is prevented from
+ * being freed until a matching call is made to comedi_dev_put().
+ *
+ * Return a pointer to the comedi device if it exists, with its usage
+ * reference incremented. Return NULL if no comedi device exists with the
+ * specified minor device number.
+ */
+struct comedi_device *comedi_dev_get_from_minor(unsigned minor)
+{
+ if (minor < COMEDI_NUM_BOARD_MINORS)
+ return comedi_dev_get_from_board_minor(minor);
+
+ return comedi_dev_get_from_subdevice_minor(minor);
+}
+EXPORT_SYMBOL_GPL(comedi_dev_get_from_minor);
+
+static struct comedi_subdevice *
+comedi_read_subdevice(const struct comedi_device *dev, unsigned int minor)
+{
+ struct comedi_subdevice *s;
+
+ if (minor >= COMEDI_NUM_BOARD_MINORS) {
+ s = comedi_subdevice_from_minor(dev, minor);
+ if (!s || (s->subdev_flags & SDF_CMD_READ))
+ return s;
+ }
+ return dev->read_subdev;
+}
+
+static struct comedi_subdevice *
+comedi_write_subdevice(const struct comedi_device *dev, unsigned int minor)
+{
+ struct comedi_subdevice *s;
+
+ if (minor >= COMEDI_NUM_BOARD_MINORS) {
+ s = comedi_subdevice_from_minor(dev, minor);
+ if (!s || (s->subdev_flags & SDF_CMD_WRITE))
+ return s;
+ }
+ return dev->write_subdev;
+}
+
+static void comedi_file_reset(struct file *file)
+{
+ struct comedi_file *cfp = file->private_data;
+ struct comedi_device *dev = cfp->dev;
+ struct comedi_subdevice *s, *read_s, *write_s;
+ unsigned int minor = iminor(file_inode(file));
+
+ read_s = dev->read_subdev;
+ write_s = dev->write_subdev;
+ if (minor >= COMEDI_NUM_BOARD_MINORS) {
+ s = comedi_subdevice_from_minor(dev, minor);
+ if (!s || s->subdev_flags & SDF_CMD_READ)
+ read_s = s;
+ if (!s || s->subdev_flags & SDF_CMD_WRITE)
+ write_s = s;
+ }
+ cfp->last_attached = dev->attached;
+ cfp->last_detach_count = dev->detach_count;
+ ACCESS_ONCE(cfp->read_subdev) = read_s;
+ ACCESS_ONCE(cfp->write_subdev) = write_s;
+}
+
+static void comedi_file_check(struct file *file)
+{
+ struct comedi_file *cfp = file->private_data;
+ struct comedi_device *dev = cfp->dev;
+
+ if (cfp->last_attached != dev->attached ||
+ cfp->last_detach_count != dev->detach_count)
+ comedi_file_reset(file);
+}
+
+static struct comedi_subdevice *comedi_file_read_subdevice(struct file *file)
+{
+ struct comedi_file *cfp = file->private_data;
+
+ comedi_file_check(file);
+ return ACCESS_ONCE(cfp->read_subdev);
+}
+
+static struct comedi_subdevice *comedi_file_write_subdevice(struct file *file)
+{
+ struct comedi_file *cfp = file->private_data;
+
+ comedi_file_check(file);
+ return ACCESS_ONCE(cfp->write_subdev);
+}
+
+static int resize_async_buffer(struct comedi_device *dev,
+ struct comedi_subdevice *s, unsigned new_size)
+{
+ struct comedi_async *async = s->async;
+ int retval;
+
+ if (new_size > async->max_bufsize)
+ return -EPERM;
+
+ if (s->busy) {
+ dev_dbg(dev->class_dev,
+ "subdevice is busy, cannot resize buffer\n");
+ return -EBUSY;
+ }
+ if (comedi_buf_is_mmapped(s)) {
+ dev_dbg(dev->class_dev,
+ "subdevice is mmapped, cannot resize buffer\n");
+ return -EBUSY;
+ }
+
+ /* make sure buffer is an integral number of pages (we round up) */
+ new_size = (new_size + PAGE_SIZE - 1) & PAGE_MASK;
+
+ retval = comedi_buf_alloc(dev, s, new_size);
+ if (retval < 0)
+ return retval;
+
+ if (s->buf_change) {
+ retval = s->buf_change(dev, s);
+ if (retval < 0)
+ return retval;
+ }
+
+ dev_dbg(dev->class_dev, "subd %d buffer resized to %i bytes\n",
+ s->index, async->prealloc_bufsz);
+ return 0;
+}
+
+/* sysfs attribute files */
+
+static ssize_t max_read_buffer_kb_show(struct device *csdev,
+ struct device_attribute *attr, char *buf)
+{
+ unsigned int minor = MINOR(csdev->devt);
+ struct comedi_device *dev;
+ struct comedi_subdevice *s;
+ unsigned int size = 0;
+
+ dev = comedi_dev_get_from_minor(minor);
+ if (!dev)
+ return -ENODEV;
+
+ mutex_lock(&dev->mutex);
+ s = comedi_read_subdevice(dev, minor);
+ if (s && (s->subdev_flags & SDF_CMD_READ) && s->async)
+ size = s->async->max_bufsize / 1024;
+ mutex_unlock(&dev->mutex);
+
+ comedi_dev_put(dev);
+ return snprintf(buf, PAGE_SIZE, "%u\n", size);
+}
+
+static ssize_t max_read_buffer_kb_store(struct device *csdev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ unsigned int minor = MINOR(csdev->devt);
+ struct comedi_device *dev;
+ struct comedi_subdevice *s;
+ unsigned int size;
+ int err;
+
+ err = kstrtouint(buf, 10, &size);
+ if (err)
+ return err;
+ if (size > (UINT_MAX / 1024))
+ return -EINVAL;
+ size *= 1024;
+
+ dev = comedi_dev_get_from_minor(minor);
+ if (!dev)
+ return -ENODEV;
+
+ mutex_lock(&dev->mutex);
+ s = comedi_read_subdevice(dev, minor);
+ if (s && (s->subdev_flags & SDF_CMD_READ) && s->async)
+ s->async->max_bufsize = size;
+ else
+ err = -EINVAL;
+ mutex_unlock(&dev->mutex);
+
+ comedi_dev_put(dev);
+ return err ? err : count;
+}
+static DEVICE_ATTR_RW(max_read_buffer_kb);
+
+static ssize_t read_buffer_kb_show(struct device *csdev,
+ struct device_attribute *attr, char *buf)
+{
+ unsigned int minor = MINOR(csdev->devt);
+ struct comedi_device *dev;
+ struct comedi_subdevice *s;
+ unsigned int size = 0;
+
+ dev = comedi_dev_get_from_minor(minor);
+ if (!dev)
+ return -ENODEV;
+
+ mutex_lock(&dev->mutex);
+ s = comedi_read_subdevice(dev, minor);
+ if (s && (s->subdev_flags & SDF_CMD_READ) && s->async)
+ size = s->async->prealloc_bufsz / 1024;
+ mutex_unlock(&dev->mutex);
+
+ comedi_dev_put(dev);
+ return snprintf(buf, PAGE_SIZE, "%u\n", size);
+}
+
+static ssize_t read_buffer_kb_store(struct device *csdev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ unsigned int minor = MINOR(csdev->devt);
+ struct comedi_device *dev;
+ struct comedi_subdevice *s;
+ unsigned int size;
+ int err;
+
+ err = kstrtouint(buf, 10, &size);
+ if (err)
+ return err;
+ if (size > (UINT_MAX / 1024))
+ return -EINVAL;
+ size *= 1024;
+
+ dev = comedi_dev_get_from_minor(minor);
+ if (!dev)
+ return -ENODEV;
+
+ mutex_lock(&dev->mutex);
+ s = comedi_read_subdevice(dev, minor);
+ if (s && (s->subdev_flags & SDF_CMD_READ) && s->async)
+ err = resize_async_buffer(dev, s, size);
+ else
+ err = -EINVAL;
+ mutex_unlock(&dev->mutex);
+
+ comedi_dev_put(dev);
+ return err ? err : count;
+}
+static DEVICE_ATTR_RW(read_buffer_kb);
+
+static ssize_t max_write_buffer_kb_show(struct device *csdev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ unsigned int minor = MINOR(csdev->devt);
+ struct comedi_device *dev;
+ struct comedi_subdevice *s;
+ unsigned int size = 0;
+
+ dev = comedi_dev_get_from_minor(minor);
+ if (!dev)
+ return -ENODEV;
+
+ mutex_lock(&dev->mutex);
+ s = comedi_write_subdevice(dev, minor);
+ if (s && (s->subdev_flags & SDF_CMD_WRITE) && s->async)
+ size = s->async->max_bufsize / 1024;
+ mutex_unlock(&dev->mutex);
+
+ comedi_dev_put(dev);
+ return snprintf(buf, PAGE_SIZE, "%u\n", size);
+}
+
+static ssize_t max_write_buffer_kb_store(struct device *csdev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ unsigned int minor = MINOR(csdev->devt);
+ struct comedi_device *dev;
+ struct comedi_subdevice *s;
+ unsigned int size;
+ int err;
+
+ err = kstrtouint(buf, 10, &size);
+ if (err)
+ return err;
+ if (size > (UINT_MAX / 1024))
+ return -EINVAL;
+ size *= 1024;
+
+ dev = comedi_dev_get_from_minor(minor);
+ if (!dev)
+ return -ENODEV;
+
+ mutex_lock(&dev->mutex);
+ s = comedi_write_subdevice(dev, minor);
+ if (s && (s->subdev_flags & SDF_CMD_WRITE) && s->async)
+ s->async->max_bufsize = size;
+ else
+ err = -EINVAL;
+ mutex_unlock(&dev->mutex);
+
+ comedi_dev_put(dev);
+ return err ? err : count;
+}
+static DEVICE_ATTR_RW(max_write_buffer_kb);
+
+static ssize_t write_buffer_kb_show(struct device *csdev,
+ struct device_attribute *attr, char *buf)
+{
+ unsigned int minor = MINOR(csdev->devt);
+ struct comedi_device *dev;
+ struct comedi_subdevice *s;
+ unsigned int size = 0;
+
+ dev = comedi_dev_get_from_minor(minor);
+ if (!dev)
+ return -ENODEV;
+
+ mutex_lock(&dev->mutex);
+ s = comedi_write_subdevice(dev, minor);
+ if (s && (s->subdev_flags & SDF_CMD_WRITE) && s->async)
+ size = s->async->prealloc_bufsz / 1024;
+ mutex_unlock(&dev->mutex);
+
+ comedi_dev_put(dev);
+ return snprintf(buf, PAGE_SIZE, "%u\n", size);
+}
+
+static ssize_t write_buffer_kb_store(struct device *csdev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ unsigned int minor = MINOR(csdev->devt);
+ struct comedi_device *dev;
+ struct comedi_subdevice *s;
+ unsigned int size;
+ int err;
+
+ err = kstrtouint(buf, 10, &size);
+ if (err)
+ return err;
+ if (size > (UINT_MAX / 1024))
+ return -EINVAL;
+ size *= 1024;
+
+ dev = comedi_dev_get_from_minor(minor);
+ if (!dev)
+ return -ENODEV;
+
+ mutex_lock(&dev->mutex);
+ s = comedi_write_subdevice(dev, minor);
+ if (s && (s->subdev_flags & SDF_CMD_WRITE) && s->async)
+ err = resize_async_buffer(dev, s, size);
+ else
+ err = -EINVAL;
+ mutex_unlock(&dev->mutex);
+
+ comedi_dev_put(dev);
+ return err ? err : count;
+}
+static DEVICE_ATTR_RW(write_buffer_kb);
+
+static struct attribute *comedi_dev_attrs[] = {
+ &dev_attr_max_read_buffer_kb.attr,
+ &dev_attr_read_buffer_kb.attr,
+ &dev_attr_max_write_buffer_kb.attr,
+ &dev_attr_write_buffer_kb.attr,
+ NULL,
+};
+ATTRIBUTE_GROUPS(comedi_dev);
+
+static void __comedi_clear_subdevice_runflags(struct comedi_subdevice *s,
+ unsigned bits)
+{
+ s->runflags &= ~bits;
+}
+
+static void __comedi_set_subdevice_runflags(struct comedi_subdevice *s,
+ unsigned bits)
+{
+ s->runflags |= bits;
+}
+
+static void comedi_update_subdevice_runflags(struct comedi_subdevice *s,
+ unsigned mask, unsigned bits)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&s->spin_lock, flags);
+ __comedi_clear_subdevice_runflags(s, mask);
+ __comedi_set_subdevice_runflags(s, bits & mask);
+ spin_unlock_irqrestore(&s->spin_lock, flags);
+}
+
+static unsigned __comedi_get_subdevice_runflags(struct comedi_subdevice *s)
+{
+ return s->runflags;
+}
+
+static unsigned comedi_get_subdevice_runflags(struct comedi_subdevice *s)
+{
+ unsigned long flags;
+ unsigned runflags;
+
+ spin_lock_irqsave(&s->spin_lock, flags);
+ runflags = __comedi_get_subdevice_runflags(s);
+ spin_unlock_irqrestore(&s->spin_lock, flags);
+ return runflags;
+}
+
+static bool comedi_is_runflags_running(unsigned runflags)
+{
+ return runflags & COMEDI_SRF_RUNNING;
+}
+
+static bool comedi_is_runflags_in_error(unsigned runflags)
+{
+ return runflags & COMEDI_SRF_ERROR;
+}
+
+/**
+ * comedi_is_subdevice_running - check if async command running on subdevice
+ * @s: comedi_subdevice struct
+ *
+ * Return true if an asynchronous comedi command is active on the comedi
+ * subdevice, else return false.
+ */
+bool comedi_is_subdevice_running(struct comedi_subdevice *s)
+{
+ unsigned runflags = comedi_get_subdevice_runflags(s);
+
+ return comedi_is_runflags_running(runflags);
+}
+EXPORT_SYMBOL_GPL(comedi_is_subdevice_running);
+
+static bool __comedi_is_subdevice_running(struct comedi_subdevice *s)
+{
+ unsigned runflags = __comedi_get_subdevice_runflags(s);
+
+ return comedi_is_runflags_running(runflags);
+}
+
+static bool comedi_is_subdevice_idle(struct comedi_subdevice *s)
+{
+ unsigned runflags = comedi_get_subdevice_runflags(s);
+
+ return !(runflags & COMEDI_SRF_BUSY_MASK);
+}
+
+/**
+ * comedi_alloc_spriv() - Allocate memory for the subdevice private data.
+ * @s: comedi_subdevice struct
+ * @size: size of the memory to allocate
+ *
+ * This also sets the subdevice runflags to allow the core to automatically
+ * free the private data during the detach.
+ */
+void *comedi_alloc_spriv(struct comedi_subdevice *s, size_t size)
+{
+ s->private = kzalloc(size, GFP_KERNEL);
+ if (s->private)
+ s->runflags |= COMEDI_SRF_FREE_SPRIV;
+ return s->private;
+}
+EXPORT_SYMBOL_GPL(comedi_alloc_spriv);
+
+/*
+ * This function restores a subdevice to an idle state.
+ */
+static void do_become_nonbusy(struct comedi_device *dev,
+ struct comedi_subdevice *s)
+{
+ struct comedi_async *async = s->async;
+
+ comedi_update_subdevice_runflags(s, COMEDI_SRF_RUNNING, 0);
+ if (async) {
+ comedi_buf_reset(s);
+ async->inttrig = NULL;
+ kfree(async->cmd.chanlist);
+ async->cmd.chanlist = NULL;
+ s->busy = NULL;
+ wake_up_interruptible_all(&async->wait_head);
+ } else {
+ dev_err(dev->class_dev,
+ "BUG: (?) do_become_nonbusy called with async=NULL\n");
+ s->busy = NULL;
+ }
+}
+
+static int do_cancel(struct comedi_device *dev, struct comedi_subdevice *s)
+{
+ int ret = 0;
+
+ if (comedi_is_subdevice_running(s) && s->cancel)
+ ret = s->cancel(dev, s);
+
+ do_become_nonbusy(dev, s);
+
+ return ret;
+}
+
+void comedi_device_cancel_all(struct comedi_device *dev)
+{
+ struct comedi_subdevice *s;
+ int i;
+
+ if (!dev->attached)
+ return;
+
+ for (i = 0; i < dev->n_subdevices; i++) {
+ s = &dev->subdevices[i];
+ if (s->async)
+ do_cancel(dev, s);
+ }
+}
+
+static int is_device_busy(struct comedi_device *dev)
+{
+ struct comedi_subdevice *s;
+ int i;
+
+ if (!dev->attached)
+ return 0;
+
+ for (i = 0; i < dev->n_subdevices; i++) {
+ s = &dev->subdevices[i];
+ if (s->busy)
+ return 1;
+ if (s->async && comedi_buf_is_mmapped(s))
+ return 1;
+ }
+
+ return 0;
+}
+
+/*
+ * COMEDI_DEVCONFIG ioctl
+ * attaches (and configures) or detaches a legacy device
+ *
+ * arg:
+ * pointer to comedi_devconfig structure (NULL if detaching)
+ *
+ * reads:
+ * comedi_devconfig structure (if attaching)
+ *
+ * writes:
+ * nothing
+ */
+static int do_devconfig_ioctl(struct comedi_device *dev,
+ struct comedi_devconfig __user *arg)
+{
+ struct comedi_devconfig it;
+
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+
+ if (!arg) {
+ if (is_device_busy(dev))
+ return -EBUSY;
+ if (dev->attached) {
+ struct module *driver_module = dev->driver->module;
+
+ comedi_device_detach(dev);
+ module_put(driver_module);
+ }
+ return 0;
+ }
+
+ if (copy_from_user(&it, arg, sizeof(it)))
+ return -EFAULT;
+
+ it.board_name[COMEDI_NAMELEN - 1] = 0;
+
+ if (it.options[COMEDI_DEVCONF_AUX_DATA_LENGTH]) {
+ dev_warn(dev->class_dev,
+ "comedi_config --init_data is deprecated\n");
+ return -EINVAL;
+ }
+
+ if (dev->minor >= comedi_num_legacy_minors)
+ /* don't re-use dynamically allocated comedi devices */
+ return -EBUSY;
+
+ /* This increments the driver module count on success. */
+ return comedi_device_attach(dev, &it);
+}
+
+/*
+ * COMEDI_BUFCONFIG ioctl
+ * buffer configuration
+ *
+ * arg:
+ * pointer to comedi_bufconfig structure
+ *
+ * reads:
+ * comedi_bufconfig structure
+ *
+ * writes:
+ * modified comedi_bufconfig structure
+ */
+static int do_bufconfig_ioctl(struct comedi_device *dev,
+ struct comedi_bufconfig __user *arg)
+{
+ struct comedi_bufconfig bc;
+ struct comedi_async *async;
+ struct comedi_subdevice *s;
+ int retval = 0;
+
+ if (copy_from_user(&bc, arg, sizeof(bc)))
+ return -EFAULT;
+
+ if (bc.subdevice >= dev->n_subdevices)
+ return -EINVAL;
+
+ s = &dev->subdevices[bc.subdevice];
+ async = s->async;
+
+ if (!async) {
+ dev_dbg(dev->class_dev,
+ "subdevice does not have async capability\n");
+ bc.size = 0;
+ bc.maximum_size = 0;
+ goto copyback;
+ }
+
+ if (bc.maximum_size) {
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+
+ async->max_bufsize = bc.maximum_size;
+ }
+
+ if (bc.size) {
+ retval = resize_async_buffer(dev, s, bc.size);
+ if (retval < 0)
+ return retval;
+ }
+
+ bc.size = async->prealloc_bufsz;
+ bc.maximum_size = async->max_bufsize;
+
+copyback:
+ if (copy_to_user(arg, &bc, sizeof(bc)))
+ return -EFAULT;
+
+ return 0;
+}
+
+/*
+ * COMEDI_DEVINFO ioctl
+ * device info
+ *
+ * arg:
+ * pointer to comedi_devinfo structure
+ *
+ * reads:
+ * nothing
+ *
+ * writes:
+ * comedi_devinfo structure
+ */
+static int do_devinfo_ioctl(struct comedi_device *dev,
+ struct comedi_devinfo __user *arg,
+ struct file *file)
+{
+ struct comedi_subdevice *s;
+ struct comedi_devinfo devinfo;
+
+ memset(&devinfo, 0, sizeof(devinfo));
+
+ /* fill devinfo structure */
+ devinfo.version_code = COMEDI_VERSION_CODE;
+ devinfo.n_subdevs = dev->n_subdevices;
+ strlcpy(devinfo.driver_name, dev->driver->driver_name, COMEDI_NAMELEN);
+ strlcpy(devinfo.board_name, dev->board_name, COMEDI_NAMELEN);
+
+ s = comedi_file_read_subdevice(file);
+ if (s)
+ devinfo.read_subdevice = s->index;
+ else
+ devinfo.read_subdevice = -1;
+
+ s = comedi_file_write_subdevice(file);
+ if (s)
+ devinfo.write_subdevice = s->index;
+ else
+ devinfo.write_subdevice = -1;
+
+ if (copy_to_user(arg, &devinfo, sizeof(devinfo)))
+ return -EFAULT;
+
+ return 0;
+}
+
+/*
+ * COMEDI_SUBDINFO ioctl
+ * subdevices info
+ *
+ * arg:
+ * pointer to array of comedi_subdinfo structures
+ *
+ * reads:
+ * nothing
+ *
+ * writes:
+ * array of comedi_subdinfo structures
+ */
+static int do_subdinfo_ioctl(struct comedi_device *dev,
+ struct comedi_subdinfo __user *arg, void *file)
+{
+ int ret, i;
+ struct comedi_subdinfo *tmp, *us;
+ struct comedi_subdevice *s;
+
+ tmp = kcalloc(dev->n_subdevices, sizeof(*tmp), GFP_KERNEL);
+ if (!tmp)
+ return -ENOMEM;
+
+ /* fill subdinfo structs */
+ for (i = 0; i < dev->n_subdevices; i++) {
+ s = &dev->subdevices[i];
+ us = tmp + i;
+
+ us->type = s->type;
+ us->n_chan = s->n_chan;
+ us->subd_flags = s->subdev_flags;
+ if (comedi_is_subdevice_running(s))
+ us->subd_flags |= SDF_RUNNING;
+#define TIMER_nanosec 5 /* backwards compatibility */
+ us->timer_type = TIMER_nanosec;
+ us->len_chanlist = s->len_chanlist;
+ us->maxdata = s->maxdata;
+ if (s->range_table) {
+ us->range_type =
+ (i << 24) | (0 << 16) | (s->range_table->length);
+ } else {
+ us->range_type = 0; /* XXX */
+ }
+
+ if (s->busy)
+ us->subd_flags |= SDF_BUSY;
+ if (s->busy == file)
+ us->subd_flags |= SDF_BUSY_OWNER;
+ if (s->lock)
+ us->subd_flags |= SDF_LOCKED;
+ if (s->lock == file)
+ us->subd_flags |= SDF_LOCK_OWNER;
+ if (!s->maxdata && s->maxdata_list)
+ us->subd_flags |= SDF_MAXDATA;
+ if (s->range_table_list)
+ us->subd_flags |= SDF_RANGETYPE;
+ if (s->do_cmd)
+ us->subd_flags |= SDF_CMD;
+
+ if (s->insn_bits != &insn_inval)
+ us->insn_bits_support = COMEDI_SUPPORTED;
+ else
+ us->insn_bits_support = COMEDI_UNSUPPORTED;
+ }
+
+ ret = copy_to_user(arg, tmp, dev->n_subdevices * sizeof(*tmp));
+
+ kfree(tmp);
+
+ return ret ? -EFAULT : 0;
+}
+
+/*
+ * COMEDI_CHANINFO ioctl
+ * subdevice channel info
+ *
+ * arg:
+ * pointer to comedi_chaninfo structure
+ *
+ * reads:
+ * comedi_chaninfo structure
+ *
+ * writes:
+ * array of maxdata values to chaninfo->maxdata_list if requested
+ * array of range table lengths to chaninfo->range_table_list if requested
+ */
+static int do_chaninfo_ioctl(struct comedi_device *dev,
+ struct comedi_chaninfo __user *arg)
+{
+ struct comedi_subdevice *s;
+ struct comedi_chaninfo it;
+
+ if (copy_from_user(&it, arg, sizeof(it)))
+ return -EFAULT;
+
+ if (it.subdev >= dev->n_subdevices)
+ return -EINVAL;
+ s = &dev->subdevices[it.subdev];
+
+ if (it.maxdata_list) {
+ if (s->maxdata || !s->maxdata_list)
+ return -EINVAL;
+ if (copy_to_user(it.maxdata_list, s->maxdata_list,
+ s->n_chan * sizeof(unsigned int)))
+ return -EFAULT;
+ }
+
+ if (it.flaglist)
+ return -EINVAL; /* flaglist not supported */
+
+ if (it.rangelist) {
+ int i;
+
+ if (!s->range_table_list)
+ return -EINVAL;
+ for (i = 0; i < s->n_chan; i++) {
+ int x;
+
+ x = (dev->minor << 28) | (it.subdev << 24) | (i << 16) |
+ (s->range_table_list[i]->length);
+ if (put_user(x, it.rangelist + i))
+ return -EFAULT;
+ }
+#if 0
+ if (copy_to_user(it.rangelist, s->range_type_list,
+ s->n_chan * sizeof(unsigned int)))
+ return -EFAULT;
+#endif
+ }
+
+ return 0;
+}
+
+/*
+ * COMEDI_BUFINFO ioctl
+ * buffer information
+ *
+ * arg:
+ * pointer to comedi_bufinfo structure
+ *
+ * reads:
+ * comedi_bufinfo structure
+ *
+ * writes:
+ * modified comedi_bufinfo structure
+ */
+static int do_bufinfo_ioctl(struct comedi_device *dev,
+ struct comedi_bufinfo __user *arg, void *file)
+{
+ struct comedi_bufinfo bi;
+ struct comedi_subdevice *s;
+ struct comedi_async *async;
+
+ if (copy_from_user(&bi, arg, sizeof(bi)))
+ return -EFAULT;
+
+ if (bi.subdevice >= dev->n_subdevices)
+ return -EINVAL;
+
+ s = &dev->subdevices[bi.subdevice];
+
+ async = s->async;
+
+ if (!async) {
+ dev_dbg(dev->class_dev,
+ "subdevice does not have async capability\n");
+ bi.buf_write_ptr = 0;
+ bi.buf_read_ptr = 0;
+ bi.buf_write_count = 0;
+ bi.buf_read_count = 0;
+ bi.bytes_read = 0;
+ bi.bytes_written = 0;
+ goto copyback;
+ }
+ if (!s->busy) {
+ bi.bytes_read = 0;
+ bi.bytes_written = 0;
+ goto copyback_position;
+ }
+ if (s->busy != file)
+ return -EACCES;
+
+ if (bi.bytes_read && !(async->cmd.flags & CMDF_WRITE)) {
+ bi.bytes_read = comedi_buf_read_alloc(s, bi.bytes_read);
+ comedi_buf_read_free(s, bi.bytes_read);
+
+ if (comedi_is_subdevice_idle(s) &&
+ comedi_buf_n_bytes_ready(s) == 0) {
+ do_become_nonbusy(dev, s);
+ }
+ }
+
+ if (bi.bytes_written && (async->cmd.flags & CMDF_WRITE)) {
+ bi.bytes_written =
+ comedi_buf_write_alloc(s, bi.bytes_written);
+ comedi_buf_write_free(s, bi.bytes_written);
+ }
+
+copyback_position:
+ bi.buf_write_count = async->buf_write_count;
+ bi.buf_write_ptr = async->buf_write_ptr;
+ bi.buf_read_count = async->buf_read_count;
+ bi.buf_read_ptr = async->buf_read_ptr;
+
+copyback:
+ if (copy_to_user(arg, &bi, sizeof(bi)))
+ return -EFAULT;
+
+ return 0;
+}
+
+static int check_insn_config_length(struct comedi_insn *insn,
+ unsigned int *data)
+{
+ if (insn->n < 1)
+ return -EINVAL;
+
+ switch (data[0]) {
+ case INSN_CONFIG_DIO_OUTPUT:
+ case INSN_CONFIG_DIO_INPUT:
+ case INSN_CONFIG_DISARM:
+ case INSN_CONFIG_RESET:
+ if (insn->n == 1)
+ return 0;
+ break;
+ case INSN_CONFIG_ARM:
+ case INSN_CONFIG_DIO_QUERY:
+ case INSN_CONFIG_BLOCK_SIZE:
+ case INSN_CONFIG_FILTER:
+ case INSN_CONFIG_SERIAL_CLOCK:
+ case INSN_CONFIG_BIDIRECTIONAL_DATA:
+ case INSN_CONFIG_ALT_SOURCE:
+ case INSN_CONFIG_SET_COUNTER_MODE:
+ case INSN_CONFIG_8254_READ_STATUS:
+ case INSN_CONFIG_SET_ROUTING:
+ case INSN_CONFIG_GET_ROUTING:
+ case INSN_CONFIG_GET_PWM_STATUS:
+ case INSN_CONFIG_PWM_SET_PERIOD:
+ case INSN_CONFIG_PWM_GET_PERIOD:
+ if (insn->n == 2)
+ return 0;
+ break;
+ case INSN_CONFIG_SET_GATE_SRC:
+ case INSN_CONFIG_GET_GATE_SRC:
+ case INSN_CONFIG_SET_CLOCK_SRC:
+ case INSN_CONFIG_GET_CLOCK_SRC:
+ case INSN_CONFIG_SET_OTHER_SRC:
+ case INSN_CONFIG_GET_COUNTER_STATUS:
+ case INSN_CONFIG_PWM_SET_H_BRIDGE:
+ case INSN_CONFIG_PWM_GET_H_BRIDGE:
+ case INSN_CONFIG_GET_HARDWARE_BUFFER_SIZE:
+ if (insn->n == 3)
+ return 0;
+ break;
+ case INSN_CONFIG_PWM_OUTPUT:
+ case INSN_CONFIG_ANALOG_TRIG:
+ if (insn->n == 5)
+ return 0;
+ break;
+ case INSN_CONFIG_DIGITAL_TRIG:
+ if (insn->n == 6)
+ return 0;
+ break;
+ /*
+ * by default we allow the insn since we don't have checks for
+ * all possible cases yet
+ */
+ default:
+ pr_warn("No check for data length of config insn id %i is implemented\n",
+ data[0]);
+ pr_warn("Add a check to %s in %s\n", __func__, __FILE__);
+ pr_warn("Assuming n=%i is correct\n", insn->n);
+ return 0;
+ }
+ return -EINVAL;
+}
+
+static int parse_insn(struct comedi_device *dev, struct comedi_insn *insn,
+ unsigned int *data, void *file)
+{
+ struct comedi_subdevice *s;
+ int ret = 0;
+ int i;
+
+ if (insn->insn & INSN_MASK_SPECIAL) {
+ /* a non-subdevice instruction */
+
+ switch (insn->insn) {
+ case INSN_GTOD:
+ {
+ struct timeval tv;
+
+ if (insn->n != 2) {
+ ret = -EINVAL;
+ break;
+ }
+
+ do_gettimeofday(&tv);
+ data[0] = tv.tv_sec;
+ data[1] = tv.tv_usec;
+ ret = 2;
+
+ break;
+ }
+ case INSN_WAIT:
+ if (insn->n != 1 || data[0] >= 100000) {
+ ret = -EINVAL;
+ break;
+ }
+ udelay(data[0] / 1000);
+ ret = 1;
+ break;
+ case INSN_INTTRIG:
+ if (insn->n != 1) {
+ ret = -EINVAL;
+ break;
+ }
+ if (insn->subdev >= dev->n_subdevices) {
+ dev_dbg(dev->class_dev,
+ "%d not usable subdevice\n",
+ insn->subdev);
+ ret = -EINVAL;
+ break;
+ }
+ s = &dev->subdevices[insn->subdev];
+ if (!s->async) {
+ dev_dbg(dev->class_dev, "no async\n");
+ ret = -EINVAL;
+ break;
+ }
+ if (!s->async->inttrig) {
+ dev_dbg(dev->class_dev, "no inttrig\n");
+ ret = -EAGAIN;
+ break;
+ }
+ ret = s->async->inttrig(dev, s, data[0]);
+ if (ret >= 0)
+ ret = 1;
+ break;
+ default:
+ dev_dbg(dev->class_dev, "invalid insn\n");
+ ret = -EINVAL;
+ break;
+ }
+ } else {
+ /* a subdevice instruction */
+ unsigned int maxdata;
+
+ if (insn->subdev >= dev->n_subdevices) {
+ dev_dbg(dev->class_dev, "subdevice %d out of range\n",
+ insn->subdev);
+ ret = -EINVAL;
+ goto out;
+ }
+ s = &dev->subdevices[insn->subdev];
+
+ if (s->type == COMEDI_SUBD_UNUSED) {
+ dev_dbg(dev->class_dev, "%d not usable subdevice\n",
+ insn->subdev);
+ ret = -EIO;
+ goto out;
+ }
+
+ /* are we locked? (ioctl lock) */
+ if (s->lock && s->lock != file) {
+ dev_dbg(dev->class_dev, "device locked\n");
+ ret = -EACCES;
+ goto out;
+ }
+
+ ret = comedi_check_chanlist(s, 1, &insn->chanspec);
+ if (ret < 0) {
+ ret = -EINVAL;
+ dev_dbg(dev->class_dev, "bad chanspec\n");
+ goto out;
+ }
+
+ if (s->busy) {
+ ret = -EBUSY;
+ goto out;
+ }
+ /* This looks arbitrary. It is. */
+ s->busy = &parse_insn;
+ switch (insn->insn) {
+ case INSN_READ:
+ ret = s->insn_read(dev, s, insn, data);
+ if (ret == -ETIMEDOUT) {
+ dev_dbg(dev->class_dev,
+ "subdevice %d read instruction timed out\n",
+ s->index);
+ }
+ break;
+ case INSN_WRITE:
+ maxdata = s->maxdata_list
+ ? s->maxdata_list[CR_CHAN(insn->chanspec)]
+ : s->maxdata;
+ for (i = 0; i < insn->n; ++i) {
+ if (data[i] > maxdata) {
+ ret = -EINVAL;
+ dev_dbg(dev->class_dev,
+ "bad data value(s)\n");
+ break;
+ }
+ }
+ if (ret == 0) {
+ ret = s->insn_write(dev, s, insn, data);
+ if (ret == -ETIMEDOUT) {
+ dev_dbg(dev->class_dev,
+ "subdevice %d write instruction timed out\n",
+ s->index);
+ }
+ }
+ break;
+ case INSN_BITS:
+ if (insn->n != 2) {
+ ret = -EINVAL;
+ } else {
+ /*
+ * Most drivers ignore the base channel in
+ * insn->chanspec. Fix this here if
+ * the subdevice has <= 32 channels.
+ */
+ unsigned int orig_mask = data[0];
+ unsigned int shift = 0;
+
+ if (s->n_chan <= 32) {
+ shift = CR_CHAN(insn->chanspec);
+ if (shift > 0) {
+ insn->chanspec = 0;
+ data[0] <<= shift;
+ data[1] <<= shift;
+ }
+ }
+ ret = s->insn_bits(dev, s, insn, data);
+ data[0] = orig_mask;
+ if (shift > 0)
+ data[1] >>= shift;
+ }
+ break;
+ case INSN_CONFIG:
+ ret = check_insn_config_length(insn, data);
+ if (ret)
+ break;
+ ret = s->insn_config(dev, s, insn, data);
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ s->busy = NULL;
+ }
+
+out:
+ return ret;
+}
+
+/*
+ * COMEDI_INSNLIST ioctl
+ * synchronous instruction list
+ *
+ * arg:
+ * pointer to comedi_insnlist structure
+ *
+ * reads:
+ * comedi_insnlist structure
+ * array of comedi_insn structures from insnlist->insns pointer
+ * data (for writes) from insns[].data pointers
+ *
+ * writes:
+ * data (for reads) to insns[].data pointers
+ */
+/* arbitrary limits */
+#define MAX_SAMPLES 256
+static int do_insnlist_ioctl(struct comedi_device *dev,
+ struct comedi_insnlist __user *arg, void *file)
+{
+ struct comedi_insnlist insnlist;
+ struct comedi_insn *insns = NULL;
+ unsigned int *data = NULL;
+ int i = 0;
+ int ret = 0;
+
+ if (copy_from_user(&insnlist, arg, sizeof(insnlist)))
+ return -EFAULT;
+
+ data = kmalloc_array(MAX_SAMPLES, sizeof(unsigned int), GFP_KERNEL);
+ if (!data) {
+ ret = -ENOMEM;
+ goto error;
+ }
+
+ insns = kcalloc(insnlist.n_insns, sizeof(*insns), GFP_KERNEL);
+ if (!insns) {
+ ret = -ENOMEM;
+ goto error;
+ }
+
+ if (copy_from_user(insns, insnlist.insns,
+ sizeof(*insns) * insnlist.n_insns)) {
+ dev_dbg(dev->class_dev, "copy_from_user failed\n");
+ ret = -EFAULT;
+ goto error;
+ }
+
+ for (i = 0; i < insnlist.n_insns; i++) {
+ if (insns[i].n > MAX_SAMPLES) {
+ dev_dbg(dev->class_dev,
+ "number of samples too large\n");
+ ret = -EINVAL;
+ goto error;
+ }
+ if (insns[i].insn & INSN_MASK_WRITE) {
+ if (copy_from_user(data, insns[i].data,
+ insns[i].n * sizeof(unsigned int))) {
+ dev_dbg(dev->class_dev,
+ "copy_from_user failed\n");
+ ret = -EFAULT;
+ goto error;
+ }
+ }
+ ret = parse_insn(dev, insns + i, data, file);
+ if (ret < 0)
+ goto error;
+ if (insns[i].insn & INSN_MASK_READ) {
+ if (copy_to_user(insns[i].data, data,
+ insns[i].n * sizeof(unsigned int))) {
+ dev_dbg(dev->class_dev,
+ "copy_to_user failed\n");
+ ret = -EFAULT;
+ goto error;
+ }
+ }
+ if (need_resched())
+ schedule();
+ }
+
+error:
+ kfree(insns);
+ kfree(data);
+
+ if (ret < 0)
+ return ret;
+ return i;
+}
+
+/*
+ * COMEDI_INSN ioctl
+ * synchronous instruction
+ *
+ * arg:
+ * pointer to comedi_insn structure
+ *
+ * reads:
+ * comedi_insn structure
+ * data (for writes) from insn->data pointer
+ *
+ * writes:
+ * data (for reads) to insn->data pointer
+ */
+static int do_insn_ioctl(struct comedi_device *dev,
+ struct comedi_insn __user *arg, void *file)
+{
+ struct comedi_insn insn;
+ unsigned int *data = NULL;
+ int ret = 0;
+
+ data = kmalloc_array(MAX_SAMPLES, sizeof(unsigned int), GFP_KERNEL);
+ if (!data) {
+ ret = -ENOMEM;
+ goto error;
+ }
+
+ if (copy_from_user(&insn, arg, sizeof(insn))) {
+ ret = -EFAULT;
+ goto error;
+ }
+
+ /* This is where the behavior of insn and insnlist deviate. */
+ if (insn.n > MAX_SAMPLES)
+ insn.n = MAX_SAMPLES;
+ if (insn.insn & INSN_MASK_WRITE) {
+ if (copy_from_user(data,
+ insn.data,
+ insn.n * sizeof(unsigned int))) {
+ ret = -EFAULT;
+ goto error;
+ }
+ }
+ ret = parse_insn(dev, &insn, data, file);
+ if (ret < 0)
+ goto error;
+ if (insn.insn & INSN_MASK_READ) {
+ if (copy_to_user(insn.data,
+ data,
+ insn.n * sizeof(unsigned int))) {
+ ret = -EFAULT;
+ goto error;
+ }
+ }
+ ret = insn.n;
+
+error:
+ kfree(data);
+
+ return ret;
+}
+
+static int __comedi_get_user_cmd(struct comedi_device *dev,
+ struct comedi_cmd __user *arg,
+ struct comedi_cmd *cmd)
+{
+ struct comedi_subdevice *s;
+
+ if (copy_from_user(cmd, arg, sizeof(*cmd))) {
+ dev_dbg(dev->class_dev, "bad cmd address\n");
+ return -EFAULT;
+ }
+
+ if (cmd->subdev >= dev->n_subdevices) {
+ dev_dbg(dev->class_dev, "%d no such subdevice\n", cmd->subdev);
+ return -ENODEV;
+ }
+
+ s = &dev->subdevices[cmd->subdev];
+
+ if (s->type == COMEDI_SUBD_UNUSED) {
+ dev_dbg(dev->class_dev, "%d not valid subdevice\n",
+ cmd->subdev);
+ return -EIO;
+ }
+
+ if (!s->do_cmd || !s->do_cmdtest || !s->async) {
+ dev_dbg(dev->class_dev,
+ "subdevice %d does not support commands\n",
+ cmd->subdev);
+ return -EIO;
+ }
+
+ /* make sure channel/gain list isn't too long */
+ if (cmd->chanlist_len > s->len_chanlist) {
+ dev_dbg(dev->class_dev, "channel/gain list too long %d > %d\n",
+ cmd->chanlist_len, s->len_chanlist);
+ return -EINVAL;
+ }
+
+ /*
+ * Set the CMDF_WRITE flag to the correct state if the subdevice
+ * supports only "read" commands or only "write" commands.
+ */
+ switch (s->subdev_flags & (SDF_CMD_READ | SDF_CMD_WRITE)) {
+ case SDF_CMD_READ:
+ cmd->flags &= ~CMDF_WRITE;
+ break;
+ case SDF_CMD_WRITE:
+ cmd->flags |= CMDF_WRITE;
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int __comedi_get_user_chanlist(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ unsigned int __user *user_chanlist,
+ struct comedi_cmd *cmd)
+{
+ unsigned int *chanlist;
+ int ret;
+
+ cmd->chanlist = NULL;
+ chanlist = memdup_user(user_chanlist,
+ cmd->chanlist_len * sizeof(unsigned int));
+ if (IS_ERR(chanlist))
+ return PTR_ERR(chanlist);
+
+ /* make sure each element in channel/gain list is valid */
+ ret = comedi_check_chanlist(s, cmd->chanlist_len, chanlist);
+ if (ret < 0) {
+ kfree(chanlist);
+ return ret;
+ }
+
+ cmd->chanlist = chanlist;
+
+ return 0;
+}
+
+/*
+ * COMEDI_CMD ioctl
+ * asynchronous acquisition command set-up
+ *
+ * arg:
+ * pointer to comedi_cmd structure
+ *
+ * reads:
+ * comedi_cmd structure
+ * channel/range list from cmd->chanlist pointer
+ *
+ * writes:
+ * possibly modified comedi_cmd structure (when -EAGAIN returned)
+ */
+static int do_cmd_ioctl(struct comedi_device *dev,
+ struct comedi_cmd __user *arg, void *file)
+{
+ struct comedi_cmd cmd;
+ struct comedi_subdevice *s;
+ struct comedi_async *async;
+ unsigned int __user *user_chanlist;
+ int ret;
+
+ /* get the user's cmd and do some simple validation */
+ ret = __comedi_get_user_cmd(dev, arg, &cmd);
+ if (ret)
+ return ret;
+
+ /* save user's chanlist pointer so it can be restored later */
+ user_chanlist = (unsigned int __user *)cmd.chanlist;
+
+ s = &dev->subdevices[cmd.subdev];
+ async = s->async;
+
+ /* are we locked? (ioctl lock) */
+ if (s->lock && s->lock != file) {
+ dev_dbg(dev->class_dev, "subdevice locked\n");
+ return -EACCES;
+ }
+
+ /* are we busy? */
+ if (s->busy) {
+ dev_dbg(dev->class_dev, "subdevice busy\n");
+ return -EBUSY;
+ }
+
+ /* make sure channel/gain list isn't too short */
+ if (cmd.chanlist_len < 1) {
+ dev_dbg(dev->class_dev, "channel/gain list too short %u < 1\n",
+ cmd.chanlist_len);
+ return -EINVAL;
+ }
+
+ async->cmd = cmd;
+ async->cmd.data = NULL;
+
+ /* load channel/gain list */
+ ret = __comedi_get_user_chanlist(dev, s, user_chanlist, &async->cmd);
+ if (ret)
+ goto cleanup;
+
+ ret = s->do_cmdtest(dev, s, &async->cmd);
+
+ if (async->cmd.flags & CMDF_BOGUS || ret) {
+ dev_dbg(dev->class_dev, "test returned %d\n", ret);
+ cmd = async->cmd;
+ /* restore chanlist pointer before copying back */
+ cmd.chanlist = (unsigned int __force *)user_chanlist;
+ cmd.data = NULL;
+ if (copy_to_user(arg, &cmd, sizeof(cmd))) {
+ dev_dbg(dev->class_dev, "fault writing cmd\n");
+ ret = -EFAULT;
+ goto cleanup;
+ }
+ ret = -EAGAIN;
+ goto cleanup;
+ }
+
+ if (!async->prealloc_bufsz) {
+ ret = -ENOMEM;
+ dev_dbg(dev->class_dev, "no buffer (?)\n");
+ goto cleanup;
+ }
+
+ comedi_buf_reset(s);
+
+ async->cb_mask = COMEDI_CB_BLOCK | COMEDI_CB_CANCEL_MASK;
+ if (async->cmd.flags & CMDF_WAKE_EOS)
+ async->cb_mask |= COMEDI_CB_EOS;
+
+ comedi_update_subdevice_runflags(s, COMEDI_SRF_BUSY_MASK,
+ COMEDI_SRF_RUNNING);
+
+ /*
+ * Set s->busy _after_ setting COMEDI_SRF_RUNNING flag to avoid
+ * race with comedi_read() or comedi_write().
+ */
+ s->busy = file;
+ ret = s->do_cmd(dev, s);
+ if (ret == 0)
+ return 0;
+
+cleanup:
+ do_become_nonbusy(dev, s);
+
+ return ret;
+}
+
+/*
+ * COMEDI_CMDTEST ioctl
+ * asynchronous aquisition command testing
+ *
+ * arg:
+ * pointer to comedi_cmd structure
+ *
+ * reads:
+ * comedi_cmd structure
+ * channel/range list from cmd->chanlist pointer
+ *
+ * writes:
+ * possibly modified comedi_cmd structure
+ */
+static int do_cmdtest_ioctl(struct comedi_device *dev,
+ struct comedi_cmd __user *arg, void *file)
+{
+ struct comedi_cmd cmd;
+ struct comedi_subdevice *s;
+ unsigned int __user *user_chanlist;
+ int ret;
+
+ /* get the user's cmd and do some simple validation */
+ ret = __comedi_get_user_cmd(dev, arg, &cmd);
+ if (ret)
+ return ret;
+
+ /* save user's chanlist pointer so it can be restored later */
+ user_chanlist = (unsigned int __user *)cmd.chanlist;
+
+ s = &dev->subdevices[cmd.subdev];
+
+ /* user_chanlist can be NULL for COMEDI_CMDTEST ioctl */
+ if (user_chanlist) {
+ /* load channel/gain list */
+ ret = __comedi_get_user_chanlist(dev, s, user_chanlist, &cmd);
+ if (ret)
+ return ret;
+ }
+
+ ret = s->do_cmdtest(dev, s, &cmd);
+
+ kfree(cmd.chanlist); /* free kernel copy of user chanlist */
+
+ /* restore chanlist pointer before copying back */
+ cmd.chanlist = (unsigned int __force *)user_chanlist;
+
+ if (copy_to_user(arg, &cmd, sizeof(cmd))) {
+ dev_dbg(dev->class_dev, "bad cmd address\n");
+ ret = -EFAULT;
+ }
+
+ return ret;
+}
+
+/*
+ * COMEDI_LOCK ioctl
+ * lock subdevice
+ *
+ * arg:
+ * subdevice number
+ *
+ * reads:
+ * nothing
+ *
+ * writes:
+ * nothing
+ */
+static int do_lock_ioctl(struct comedi_device *dev, unsigned long arg,
+ void *file)
+{
+ int ret = 0;
+ unsigned long flags;
+ struct comedi_subdevice *s;
+
+ if (arg >= dev->n_subdevices)
+ return -EINVAL;
+ s = &dev->subdevices[arg];
+
+ spin_lock_irqsave(&s->spin_lock, flags);
+ if (s->busy || s->lock)
+ ret = -EBUSY;
+ else
+ s->lock = file;
+ spin_unlock_irqrestore(&s->spin_lock, flags);
+
+ return ret;
+}
+
+/*
+ * COMEDI_UNLOCK ioctl
+ * unlock subdevice
+ *
+ * arg:
+ * subdevice number
+ *
+ * reads:
+ * nothing
+ *
+ * writes:
+ * nothing
+ */
+static int do_unlock_ioctl(struct comedi_device *dev, unsigned long arg,
+ void *file)
+{
+ struct comedi_subdevice *s;
+
+ if (arg >= dev->n_subdevices)
+ return -EINVAL;
+ s = &dev->subdevices[arg];
+
+ if (s->busy)
+ return -EBUSY;
+
+ if (s->lock && s->lock != file)
+ return -EACCES;
+
+ if (s->lock == file)
+ s->lock = NULL;
+
+ return 0;
+}
+
+/*
+ * COMEDI_CANCEL ioctl
+ * cancel asynchronous acquisition
+ *
+ * arg:
+ * subdevice number
+ *
+ * reads:
+ * nothing
+ *
+ * writes:
+ * nothing
+ */
+static int do_cancel_ioctl(struct comedi_device *dev, unsigned long arg,
+ void *file)
+{
+ struct comedi_subdevice *s;
+
+ if (arg >= dev->n_subdevices)
+ return -EINVAL;
+ s = &dev->subdevices[arg];
+ if (!s->async)
+ return -EINVAL;
+
+ if (!s->busy)
+ return 0;
+
+ if (s->busy != file)
+ return -EBUSY;
+
+ return do_cancel(dev, s);
+}
+
+/*
+ * COMEDI_POLL ioctl
+ * instructs driver to synchronize buffers
+ *
+ * arg:
+ * subdevice number
+ *
+ * reads:
+ * nothing
+ *
+ * writes:
+ * nothing
+ */
+static int do_poll_ioctl(struct comedi_device *dev, unsigned long arg,
+ void *file)
+{
+ struct comedi_subdevice *s;
+
+ if (arg >= dev->n_subdevices)
+ return -EINVAL;
+ s = &dev->subdevices[arg];
+
+ if (!s->busy)
+ return 0;
+
+ if (s->busy != file)
+ return -EBUSY;
+
+ if (s->poll)
+ return s->poll(dev, s);
+
+ return -EINVAL;
+}
+
+/*
+ * COMEDI_SETRSUBD ioctl
+ * sets the current "read" subdevice on a per-file basis
+ *
+ * arg:
+ * subdevice number
+ *
+ * reads:
+ * nothing
+ *
+ * writes:
+ * nothing
+ */
+static int do_setrsubd_ioctl(struct comedi_device *dev, unsigned long arg,
+ struct file *file)
+{
+ struct comedi_file *cfp = file->private_data;
+ struct comedi_subdevice *s_old, *s_new;
+
+ if (arg >= dev->n_subdevices)
+ return -EINVAL;
+
+ s_new = &dev->subdevices[arg];
+ s_old = comedi_file_read_subdevice(file);
+ if (s_old == s_new)
+ return 0; /* no change */
+
+ if (!(s_new->subdev_flags & SDF_CMD_READ))
+ return -EINVAL;
+
+ /*
+ * Check the file isn't still busy handling a "read" command on the
+ * old subdevice (if any).
+ */
+ if (s_old && s_old->busy == file && s_old->async &&
+ !(s_old->async->cmd.flags & CMDF_WRITE))
+ return -EBUSY;
+
+ ACCESS_ONCE(cfp->read_subdev) = s_new;
+ return 0;
+}
+
+/*
+ * COMEDI_SETWSUBD ioctl
+ * sets the current "write" subdevice on a per-file basis
+ *
+ * arg:
+ * subdevice number
+ *
+ * reads:
+ * nothing
+ *
+ * writes:
+ * nothing
+ */
+static int do_setwsubd_ioctl(struct comedi_device *dev, unsigned long arg,
+ struct file *file)
+{
+ struct comedi_file *cfp = file->private_data;
+ struct comedi_subdevice *s_old, *s_new;
+
+ if (arg >= dev->n_subdevices)
+ return -EINVAL;
+
+ s_new = &dev->subdevices[arg];
+ s_old = comedi_file_write_subdevice(file);
+ if (s_old == s_new)
+ return 0; /* no change */
+
+ if (!(s_new->subdev_flags & SDF_CMD_WRITE))
+ return -EINVAL;
+
+ /*
+ * Check the file isn't still busy handling a "write" command on the
+ * old subdevice (if any).
+ */
+ if (s_old && s_old->busy == file && s_old->async &&
+ (s_old->async->cmd.flags & CMDF_WRITE))
+ return -EBUSY;
+
+ ACCESS_ONCE(cfp->write_subdev) = s_new;
+ return 0;
+}
+
+static long comedi_unlocked_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ unsigned minor = iminor(file_inode(file));
+ struct comedi_file *cfp = file->private_data;
+ struct comedi_device *dev = cfp->dev;
+ int rc;
+
+ mutex_lock(&dev->mutex);
+
+ /*
+ * Device config is special, because it must work on
+ * an unconfigured device.
+ */
+ if (cmd == COMEDI_DEVCONFIG) {
+ if (minor >= COMEDI_NUM_BOARD_MINORS) {
+ /* Device config not appropriate on non-board minors. */
+ rc = -ENOTTY;
+ goto done;
+ }
+ rc = do_devconfig_ioctl(dev,
+ (struct comedi_devconfig __user *)arg);
+ if (rc == 0) {
+ if (arg == 0 &&
+ dev->minor >= comedi_num_legacy_minors) {
+ /*
+ * Successfully unconfigured a dynamically
+ * allocated device. Try and remove it.
+ */
+ if (comedi_clear_board_dev(dev)) {
+ mutex_unlock(&dev->mutex);
+ comedi_free_board_dev(dev);
+ return rc;
+ }
+ }
+ }
+ goto done;
+ }
+
+ if (!dev->attached) {
+ dev_dbg(dev->class_dev, "no driver attached\n");
+ rc = -ENODEV;
+ goto done;
+ }
+
+ switch (cmd) {
+ case COMEDI_BUFCONFIG:
+ rc = do_bufconfig_ioctl(dev,
+ (struct comedi_bufconfig __user *)arg);
+ break;
+ case COMEDI_DEVINFO:
+ rc = do_devinfo_ioctl(dev, (struct comedi_devinfo __user *)arg,
+ file);
+ break;
+ case COMEDI_SUBDINFO:
+ rc = do_subdinfo_ioctl(dev,
+ (struct comedi_subdinfo __user *)arg,
+ file);
+ break;
+ case COMEDI_CHANINFO:
+ rc = do_chaninfo_ioctl(dev, (void __user *)arg);
+ break;
+ case COMEDI_RANGEINFO:
+ rc = do_rangeinfo_ioctl(dev, (void __user *)arg);
+ break;
+ case COMEDI_BUFINFO:
+ rc = do_bufinfo_ioctl(dev,
+ (struct comedi_bufinfo __user *)arg,
+ file);
+ break;
+ case COMEDI_LOCK:
+ rc = do_lock_ioctl(dev, arg, file);
+ break;
+ case COMEDI_UNLOCK:
+ rc = do_unlock_ioctl(dev, arg, file);
+ break;
+ case COMEDI_CANCEL:
+ rc = do_cancel_ioctl(dev, arg, file);
+ break;
+ case COMEDI_CMD:
+ rc = do_cmd_ioctl(dev, (struct comedi_cmd __user *)arg, file);
+ break;
+ case COMEDI_CMDTEST:
+ rc = do_cmdtest_ioctl(dev, (struct comedi_cmd __user *)arg,
+ file);
+ break;
+ case COMEDI_INSNLIST:
+ rc = do_insnlist_ioctl(dev,
+ (struct comedi_insnlist __user *)arg,
+ file);
+ break;
+ case COMEDI_INSN:
+ rc = do_insn_ioctl(dev, (struct comedi_insn __user *)arg,
+ file);
+ break;
+ case COMEDI_POLL:
+ rc = do_poll_ioctl(dev, arg, file);
+ break;
+ case COMEDI_SETRSUBD:
+ rc = do_setrsubd_ioctl(dev, arg, file);
+ break;
+ case COMEDI_SETWSUBD:
+ rc = do_setwsubd_ioctl(dev, arg, file);
+ break;
+ default:
+ rc = -ENOTTY;
+ break;
+ }
+
+done:
+ mutex_unlock(&dev->mutex);
+ return rc;
+}
+
+static void comedi_vm_open(struct vm_area_struct *area)
+{
+ struct comedi_buf_map *bm;
+
+ bm = area->vm_private_data;
+ comedi_buf_map_get(bm);
+}
+
+static void comedi_vm_close(struct vm_area_struct *area)
+{
+ struct comedi_buf_map *bm;
+
+ bm = area->vm_private_data;
+ comedi_buf_map_put(bm);
+}
+
+static struct vm_operations_struct comedi_vm_ops = {
+ .open = comedi_vm_open,
+ .close = comedi_vm_close,
+};
+
+static int comedi_mmap(struct file *file, struct vm_area_struct *vma)
+{
+ struct comedi_file *cfp = file->private_data;
+ struct comedi_device *dev = cfp->dev;
+ struct comedi_subdevice *s;
+ struct comedi_async *async;
+ struct comedi_buf_map *bm = NULL;
+ unsigned long start = vma->vm_start;
+ unsigned long size;
+ int n_pages;
+ int i;
+ int retval;
+
+ /*
+ * 'trylock' avoids circular dependency with current->mm->mmap_sem
+ * and down-reading &dev->attach_lock should normally succeed without
+ * contention unless the device is in the process of being attached
+ * or detached.
+ */
+ if (!down_read_trylock(&dev->attach_lock))
+ return -EAGAIN;
+
+ if (!dev->attached) {
+ dev_dbg(dev->class_dev, "no driver attached\n");
+ retval = -ENODEV;
+ goto done;
+ }
+
+ if (vma->vm_flags & VM_WRITE)
+ s = comedi_file_write_subdevice(file);
+ else
+ s = comedi_file_read_subdevice(file);
+ if (!s) {
+ retval = -EINVAL;
+ goto done;
+ }
+
+ async = s->async;
+ if (!async) {
+ retval = -EINVAL;
+ goto done;
+ }
+
+ if (vma->vm_pgoff != 0) {
+ dev_dbg(dev->class_dev, "mmap() offset must be 0.\n");
+ retval = -EINVAL;
+ goto done;
+ }
+
+ size = vma->vm_end - vma->vm_start;
+ if (size > async->prealloc_bufsz) {
+ retval = -EFAULT;
+ goto done;
+ }
+ if (size & (~PAGE_MASK)) {
+ retval = -EFAULT;
+ goto done;
+ }
+
+ n_pages = size >> PAGE_SHIFT;
+
+ /* get reference to current buf map (if any) */
+ bm = comedi_buf_map_from_subdev_get(s);
+ if (!bm || n_pages > bm->n_pages) {
+ retval = -EINVAL;
+ goto done;
+ }
+ for (i = 0; i < n_pages; ++i) {
+ struct comedi_buf_page *buf = &bm->page_list[i];
+
+ if (remap_pfn_range(vma, start,
+ page_to_pfn(virt_to_page(buf->virt_addr)),
+ PAGE_SIZE, PAGE_SHARED)) {
+ retval = -EAGAIN;
+ goto done;
+ }
+ start += PAGE_SIZE;
+ }
+
+ vma->vm_ops = &comedi_vm_ops;
+ vma->vm_private_data = bm;
+
+ vma->vm_ops->open(vma);
+
+ retval = 0;
+done:
+ up_read(&dev->attach_lock);
+ comedi_buf_map_put(bm); /* put reference to buf map - okay if NULL */
+ return retval;
+}
+
+static unsigned int comedi_poll(struct file *file, poll_table *wait)
+{
+ unsigned int mask = 0;
+ struct comedi_file *cfp = file->private_data;
+ struct comedi_device *dev = cfp->dev;
+ struct comedi_subdevice *s;
+
+ mutex_lock(&dev->mutex);
+
+ if (!dev->attached) {
+ dev_dbg(dev->class_dev, "no driver attached\n");
+ goto done;
+ }
+
+ s = comedi_file_read_subdevice(file);
+ if (s && s->async) {
+ poll_wait(file, &s->async->wait_head, wait);
+ if (!s->busy || !comedi_is_subdevice_running(s) ||
+ (s->async->cmd.flags & CMDF_WRITE) ||
+ comedi_buf_read_n_available(s) > 0)
+ mask |= POLLIN | POLLRDNORM;
+ }
+
+ s = comedi_file_write_subdevice(file);
+ if (s && s->async) {
+ unsigned int bps = comedi_bytes_per_sample(s);
+
+ poll_wait(file, &s->async->wait_head, wait);
+ comedi_buf_write_alloc(s, s->async->prealloc_bufsz);
+ if (!s->busy || !comedi_is_subdevice_running(s) ||
+ !(s->async->cmd.flags & CMDF_WRITE) ||
+ comedi_buf_write_n_allocated(s) >= bps)
+ mask |= POLLOUT | POLLWRNORM;
+ }
+
+done:
+ mutex_unlock(&dev->mutex);
+ return mask;
+}
+
+static ssize_t comedi_write(struct file *file, const char __user *buf,
+ size_t nbytes, loff_t *offset)
+{
+ struct comedi_subdevice *s;
+ struct comedi_async *async;
+ int n, m, count = 0, retval = 0;
+ DECLARE_WAITQUEUE(wait, current);
+ struct comedi_file *cfp = file->private_data;
+ struct comedi_device *dev = cfp->dev;
+ bool on_wait_queue = false;
+ bool attach_locked;
+ unsigned int old_detach_count;
+
+ /* Protect against device detachment during operation. */
+ down_read(&dev->attach_lock);
+ attach_locked = true;
+ old_detach_count = dev->detach_count;
+
+ if (!dev->attached) {
+ dev_dbg(dev->class_dev, "no driver attached\n");
+ retval = -ENODEV;
+ goto out;
+ }
+
+ s = comedi_file_write_subdevice(file);
+ if (!s || !s->async) {
+ retval = -EIO;
+ goto out;
+ }
+
+ async = s->async;
+
+ if (!s->busy || !nbytes)
+ goto out;
+ if (s->busy != file) {
+ retval = -EACCES;
+ goto out;
+ }
+ if (!(async->cmd.flags & CMDF_WRITE)) {
+ retval = -EINVAL;
+ goto out;
+ }
+
+ add_wait_queue(&async->wait_head, &wait);
+ on_wait_queue = true;
+ while (nbytes > 0 && !retval) {
+ unsigned runflags;
+
+ set_current_state(TASK_INTERRUPTIBLE);
+
+ runflags = comedi_get_subdevice_runflags(s);
+ if (!comedi_is_runflags_running(runflags)) {
+ if (count == 0) {
+ struct comedi_subdevice *new_s;
+
+ if (comedi_is_runflags_in_error(runflags))
+ retval = -EPIPE;
+ else
+ retval = 0;
+ /*
+ * To avoid deadlock, cannot acquire dev->mutex
+ * while dev->attach_lock is held. Need to
+ * remove task from the async wait queue before
+ * releasing dev->attach_lock, as it might not
+ * be valid afterwards.
+ */
+ remove_wait_queue(&async->wait_head, &wait);
+ on_wait_queue = false;
+ up_read(&dev->attach_lock);
+ attach_locked = false;
+ mutex_lock(&dev->mutex);
+ /*
+ * Become non-busy unless things have changed
+ * behind our back. Checking dev->detach_count
+ * is unchanged ought to be sufficient (unless
+ * there have been 2**32 detaches in the
+ * meantime!), but check the subdevice pointer
+ * as well just in case.
+ */
+ new_s = comedi_file_write_subdevice(file);
+ if (dev->attached &&
+ old_detach_count == dev->detach_count &&
+ s == new_s && new_s->async == async)
+ do_become_nonbusy(dev, s);
+ mutex_unlock(&dev->mutex);
+ }
+ break;
+ }
+
+ n = nbytes;
+
+ m = n;
+ if (async->buf_write_ptr + m > async->prealloc_bufsz)
+ m = async->prealloc_bufsz - async->buf_write_ptr;
+ comedi_buf_write_alloc(s, async->prealloc_bufsz);
+ if (m > comedi_buf_write_n_allocated(s))
+ m = comedi_buf_write_n_allocated(s);
+ if (m < n)
+ n = m;
+
+ if (n == 0) {
+ if (file->f_flags & O_NONBLOCK) {
+ retval = -EAGAIN;
+ break;
+ }
+ schedule();
+ if (signal_pending(current)) {
+ retval = -ERESTARTSYS;
+ break;
+ }
+ if (!s->busy)
+ break;
+ if (s->busy != file) {
+ retval = -EACCES;
+ break;
+ }
+ if (!(async->cmd.flags & CMDF_WRITE)) {
+ retval = -EINVAL;
+ break;
+ }
+ continue;
+ }
+
+ m = copy_from_user(async->prealloc_buf + async->buf_write_ptr,
+ buf, n);
+ if (m) {
+ n -= m;
+ retval = -EFAULT;
+ }
+ comedi_buf_write_free(s, n);
+
+ count += n;
+ nbytes -= n;
+
+ buf += n;
+ break; /* makes device work like a pipe */
+ }
+out:
+ if (on_wait_queue)
+ remove_wait_queue(&async->wait_head, &wait);
+ set_current_state(TASK_RUNNING);
+ if (attach_locked)
+ up_read(&dev->attach_lock);
+
+ return count ? count : retval;
+}
+
+static ssize_t comedi_read(struct file *file, char __user *buf, size_t nbytes,
+ loff_t *offset)
+{
+ struct comedi_subdevice *s;
+ struct comedi_async *async;
+ int n, m, count = 0, retval = 0;
+ DECLARE_WAITQUEUE(wait, current);
+ struct comedi_file *cfp = file->private_data;
+ struct comedi_device *dev = cfp->dev;
+ unsigned int old_detach_count;
+ bool become_nonbusy = false;
+ bool attach_locked;
+
+ /* Protect against device detachment during operation. */
+ down_read(&dev->attach_lock);
+ attach_locked = true;
+ old_detach_count = dev->detach_count;
+
+ if (!dev->attached) {
+ dev_dbg(dev->class_dev, "no driver attached\n");
+ retval = -ENODEV;
+ goto out;
+ }
+
+ s = comedi_file_read_subdevice(file);
+ if (!s || !s->async) {
+ retval = -EIO;
+ goto out;
+ }
+
+ async = s->async;
+ if (!s->busy || !nbytes)
+ goto out;
+ if (s->busy != file) {
+ retval = -EACCES;
+ goto out;
+ }
+ if (async->cmd.flags & CMDF_WRITE) {
+ retval = -EINVAL;
+ goto out;
+ }
+
+ add_wait_queue(&async->wait_head, &wait);
+ while (nbytes > 0 && !retval) {
+ set_current_state(TASK_INTERRUPTIBLE);
+
+ n = nbytes;
+
+ m = comedi_buf_read_n_available(s);
+ if (async->buf_read_ptr + m > async->prealloc_bufsz)
+ m = async->prealloc_bufsz - async->buf_read_ptr;
+ if (m < n)
+ n = m;
+
+ if (n == 0) {
+ unsigned runflags = comedi_get_subdevice_runflags(s);
+
+ if (!comedi_is_runflags_running(runflags)) {
+ if (comedi_is_runflags_in_error(runflags))
+ retval = -EPIPE;
+ else
+ retval = 0;
+ become_nonbusy = true;
+ break;
+ }
+ if (file->f_flags & O_NONBLOCK) {
+ retval = -EAGAIN;
+ break;
+ }
+ schedule();
+ if (signal_pending(current)) {
+ retval = -ERESTARTSYS;
+ break;
+ }
+ if (!s->busy) {
+ retval = 0;
+ break;
+ }
+ if (s->busy != file) {
+ retval = -EACCES;
+ break;
+ }
+ if (async->cmd.flags & CMDF_WRITE) {
+ retval = -EINVAL;
+ break;
+ }
+ continue;
+ }
+ m = copy_to_user(buf, async->prealloc_buf +
+ async->buf_read_ptr, n);
+ if (m) {
+ n -= m;
+ retval = -EFAULT;
+ }
+
+ comedi_buf_read_alloc(s, n);
+ comedi_buf_read_free(s, n);
+
+ count += n;
+ nbytes -= n;
+
+ buf += n;
+ break; /* makes device work like a pipe */
+ }
+ remove_wait_queue(&async->wait_head, &wait);
+ set_current_state(TASK_RUNNING);
+ if (become_nonbusy || comedi_is_subdevice_idle(s)) {
+ struct comedi_subdevice *new_s;
+
+ /*
+ * To avoid deadlock, cannot acquire dev->mutex
+ * while dev->attach_lock is held.
+ */
+ up_read(&dev->attach_lock);
+ attach_locked = false;
+ mutex_lock(&dev->mutex);
+ /*
+ * Check device hasn't become detached behind our back.
+ * Checking dev->detach_count is unchanged ought to be
+ * sufficient (unless there have been 2**32 detaches in the
+ * meantime!), but check the subdevice pointer as well just in
+ * case.
+ */
+ new_s = comedi_file_read_subdevice(file);
+ if (dev->attached && old_detach_count == dev->detach_count &&
+ s == new_s && new_s->async == async) {
+ if (become_nonbusy || comedi_buf_n_bytes_ready(s) == 0)
+ do_become_nonbusy(dev, s);
+ }
+ mutex_unlock(&dev->mutex);
+ }
+out:
+ if (attach_locked)
+ up_read(&dev->attach_lock);
+
+ return count ? count : retval;
+}
+
+static int comedi_open(struct inode *inode, struct file *file)
+{
+ const unsigned minor = iminor(inode);
+ struct comedi_file *cfp;
+ struct comedi_device *dev = comedi_dev_get_from_minor(minor);
+ int rc;
+
+ if (!dev) {
+ pr_debug("invalid minor number\n");
+ return -ENODEV;
+ }
+
+ cfp = kzalloc(sizeof(*cfp), GFP_KERNEL);
+ if (!cfp)
+ return -ENOMEM;
+
+ cfp->dev = dev;
+
+ mutex_lock(&dev->mutex);
+ if (!dev->attached && !capable(CAP_NET_ADMIN)) {
+ dev_dbg(dev->class_dev, "not attached and not CAP_NET_ADMIN\n");
+ rc = -ENODEV;
+ goto out;
+ }
+ if (dev->attached && dev->use_count == 0) {
+ if (!try_module_get(dev->driver->module)) {
+ rc = -ENOSYS;
+ goto out;
+ }
+ if (dev->open) {
+ rc = dev->open(dev);
+ if (rc < 0) {
+ module_put(dev->driver->module);
+ goto out;
+ }
+ }
+ }
+
+ dev->use_count++;
+ file->private_data = cfp;
+ comedi_file_reset(file);
+ rc = 0;
+
+out:
+ mutex_unlock(&dev->mutex);
+ if (rc) {
+ comedi_dev_put(dev);
+ kfree(cfp);
+ }
+ return rc;
+}
+
+static int comedi_fasync(int fd, struct file *file, int on)
+{
+ struct comedi_file *cfp = file->private_data;
+ struct comedi_device *dev = cfp->dev;
+
+ return fasync_helper(fd, file, on, &dev->async_queue);
+}
+
+static int comedi_close(struct inode *inode, struct file *file)
+{
+ struct comedi_file *cfp = file->private_data;
+ struct comedi_device *dev = cfp->dev;
+ struct comedi_subdevice *s = NULL;
+ int i;
+
+ mutex_lock(&dev->mutex);
+
+ if (dev->subdevices) {
+ for (i = 0; i < dev->n_subdevices; i++) {
+ s = &dev->subdevices[i];
+
+ if (s->busy == file)
+ do_cancel(dev, s);
+ if (s->lock == file)
+ s->lock = NULL;
+ }
+ }
+ if (dev->attached && dev->use_count == 1) {
+ if (dev->close)
+ dev->close(dev);
+ module_put(dev->driver->module);
+ }
+
+ dev->use_count--;
+
+ mutex_unlock(&dev->mutex);
+ comedi_dev_put(dev);
+ kfree(cfp);
+
+ return 0;
+}
+
+static const struct file_operations comedi_fops = {
+ .owner = THIS_MODULE,
+ .unlocked_ioctl = comedi_unlocked_ioctl,
+ .compat_ioctl = comedi_compat_ioctl,
+ .open = comedi_open,
+ .release = comedi_close,
+ .read = comedi_read,
+ .write = comedi_write,
+ .mmap = comedi_mmap,
+ .poll = comedi_poll,
+ .fasync = comedi_fasync,
+ .llseek = noop_llseek,
+};
+
+/**
+ * comedi_event - handle events for asynchronous comedi command
+ * @dev: comedi_device struct
+ * @s: comedi_subdevice struct associated with dev
+ * Context: interrupt (usually), s->spin_lock spin-lock not held
+ *
+ * If an asynchronous comedi command is active on the subdevice, process
+ * any COMEDI_CB_... event flags that have been set, usually by an
+ * interrupt handler. These may change the run state of the asynchronous
+ * command, wake a task, and/or send a SIGIO signal.
+ */
+void comedi_event(struct comedi_device *dev, struct comedi_subdevice *s)
+{
+ struct comedi_async *async = s->async;
+ unsigned int events;
+ int si_code = 0;
+ unsigned long flags;
+
+ spin_lock_irqsave(&s->spin_lock, flags);
+
+ events = async->events;
+ async->events = 0;
+ if (!__comedi_is_subdevice_running(s)) {
+ spin_unlock_irqrestore(&s->spin_lock, flags);
+ return;
+ }
+
+ if (events & COMEDI_CB_CANCEL_MASK)
+ __comedi_clear_subdevice_runflags(s, COMEDI_SRF_RUNNING);
+
+ /*
+ * Remember if an error event has occurred, so an error can be
+ * returned the next time the user does a read() or write().
+ */
+ if (events & COMEDI_CB_ERROR_MASK)
+ __comedi_set_subdevice_runflags(s, COMEDI_SRF_ERROR);
+
+ if (async->cb_mask & events) {
+ wake_up_interruptible(&async->wait_head);
+ si_code = async->cmd.flags & CMDF_WRITE ? POLL_OUT : POLL_IN;
+ }
+
+ spin_unlock_irqrestore(&s->spin_lock, flags);
+
+ if (si_code)
+ kill_fasync(&dev->async_queue, SIGIO, si_code);
+}
+EXPORT_SYMBOL_GPL(comedi_event);
+
+/* Note: the ->mutex is pre-locked on successful return */
+struct comedi_device *comedi_alloc_board_minor(struct device *hardware_device)
+{
+ struct comedi_device *dev;
+ struct device *csdev;
+ unsigned i;
+
+ dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+ if (!dev)
+ return ERR_PTR(-ENOMEM);
+ comedi_device_init(dev);
+ comedi_set_hw_dev(dev, hardware_device);
+ mutex_lock(&dev->mutex);
+ mutex_lock(&comedi_board_minor_table_lock);
+ for (i = hardware_device ? comedi_num_legacy_minors : 0;
+ i < COMEDI_NUM_BOARD_MINORS; ++i) {
+ if (!comedi_board_minor_table[i]) {
+ comedi_board_minor_table[i] = dev;
+ break;
+ }
+ }
+ mutex_unlock(&comedi_board_minor_table_lock);
+ if (i == COMEDI_NUM_BOARD_MINORS) {
+ mutex_unlock(&dev->mutex);
+ comedi_device_cleanup(dev);
+ comedi_dev_put(dev);
+ dev_err(hardware_device,
+ "ran out of minor numbers for board device files\n");
+ return ERR_PTR(-EBUSY);
+ }
+ dev->minor = i;
+ csdev = device_create(comedi_class, hardware_device,
+ MKDEV(COMEDI_MAJOR, i), NULL, "comedi%i", i);
+ if (!IS_ERR(csdev))
+ dev->class_dev = get_device(csdev);
+
+ /* Note: dev->mutex needs to be unlocked by the caller. */
+ return dev;
+}
+
+static void comedi_free_board_minor(unsigned minor)
+{
+ BUG_ON(minor >= COMEDI_NUM_BOARD_MINORS);
+ comedi_free_board_dev(comedi_clear_board_minor(minor));
+}
+
+void comedi_release_hardware_device(struct device *hardware_device)
+{
+ int minor;
+ struct comedi_device *dev;
+
+ for (minor = comedi_num_legacy_minors; minor < COMEDI_NUM_BOARD_MINORS;
+ minor++) {
+ mutex_lock(&comedi_board_minor_table_lock);
+ dev = comedi_board_minor_table[minor];
+ if (dev && dev->hw_dev == hardware_device) {
+ comedi_board_minor_table[minor] = NULL;
+ mutex_unlock(&comedi_board_minor_table_lock);
+ comedi_free_board_dev(dev);
+ break;
+ }
+ mutex_unlock(&comedi_board_minor_table_lock);
+ }
+}
+
+int comedi_alloc_subdevice_minor(struct comedi_subdevice *s)
+{
+ struct comedi_device *dev = s->device;
+ struct device *csdev;
+ unsigned i;
+
+ mutex_lock(&comedi_subdevice_minor_table_lock);
+ for (i = 0; i < COMEDI_NUM_SUBDEVICE_MINORS; ++i) {
+ if (!comedi_subdevice_minor_table[i]) {
+ comedi_subdevice_minor_table[i] = s;
+ break;
+ }
+ }
+ mutex_unlock(&comedi_subdevice_minor_table_lock);
+ if (i == COMEDI_NUM_SUBDEVICE_MINORS) {
+ dev_err(dev->class_dev,
+ "ran out of minor numbers for subdevice files\n");
+ return -EBUSY;
+ }
+ i += COMEDI_NUM_BOARD_MINORS;
+ s->minor = i;
+ csdev = device_create(comedi_class, dev->class_dev,
+ MKDEV(COMEDI_MAJOR, i), NULL, "comedi%i_subd%i",
+ dev->minor, s->index);
+ if (!IS_ERR(csdev))
+ s->class_dev = csdev;
+
+ return 0;
+}
+
+void comedi_free_subdevice_minor(struct comedi_subdevice *s)
+{
+ unsigned int i;
+
+ if (!s)
+ return;
+ if (s->minor < 0)
+ return;
+
+ BUG_ON(s->minor >= COMEDI_NUM_MINORS);
+ BUG_ON(s->minor < COMEDI_NUM_BOARD_MINORS);
+
+ i = s->minor - COMEDI_NUM_BOARD_MINORS;
+ mutex_lock(&comedi_subdevice_minor_table_lock);
+ if (s == comedi_subdevice_minor_table[i])
+ comedi_subdevice_minor_table[i] = NULL;
+ mutex_unlock(&comedi_subdevice_minor_table_lock);
+ if (s->class_dev) {
+ device_destroy(comedi_class, MKDEV(COMEDI_MAJOR, s->minor));
+ s->class_dev = NULL;
+ }
+}
+
+static void comedi_cleanup_board_minors(void)
+{
+ unsigned i;
+
+ for (i = 0; i < COMEDI_NUM_BOARD_MINORS; i++)
+ comedi_free_board_minor(i);
+}
+
+static int __init comedi_init(void)
+{
+ int i;
+ int retval;
+
+ pr_info("version " COMEDI_RELEASE " - http://www.comedi.org\n");
+
+ if (comedi_num_legacy_minors < 0 ||
+ comedi_num_legacy_minors > COMEDI_NUM_BOARD_MINORS) {
+ pr_err("invalid value for module parameter \"comedi_num_legacy_minors\". Valid values are 0 through %i.\n",
+ COMEDI_NUM_BOARD_MINORS);
+ return -EINVAL;
+ }
+
+ retval = register_chrdev_region(MKDEV(COMEDI_MAJOR, 0),
+ COMEDI_NUM_MINORS, "comedi");
+ if (retval)
+ return -EIO;
+ cdev_init(&comedi_cdev, &comedi_fops);
+ comedi_cdev.owner = THIS_MODULE;
+
+ retval = kobject_set_name(&comedi_cdev.kobj, "comedi");
+ if (retval) {
+ unregister_chrdev_region(MKDEV(COMEDI_MAJOR, 0),
+ COMEDI_NUM_MINORS);
+ return retval;
+ }
+
+ if (cdev_add(&comedi_cdev, MKDEV(COMEDI_MAJOR, 0), COMEDI_NUM_MINORS)) {
+ unregister_chrdev_region(MKDEV(COMEDI_MAJOR, 0),
+ COMEDI_NUM_MINORS);
+ return -EIO;
+ }
+ comedi_class = class_create(THIS_MODULE, "comedi");
+ if (IS_ERR(comedi_class)) {
+ pr_err("failed to create class\n");
+ cdev_del(&comedi_cdev);
+ unregister_chrdev_region(MKDEV(COMEDI_MAJOR, 0),
+ COMEDI_NUM_MINORS);
+ return PTR_ERR(comedi_class);
+ }
+
+ comedi_class->dev_groups = comedi_dev_groups;
+
+ /* XXX requires /proc interface */
+ comedi_proc_init();
+
+ /* create devices files for legacy/manual use */
+ for (i = 0; i < comedi_num_legacy_minors; i++) {
+ struct comedi_device *dev;
+
+ dev = comedi_alloc_board_minor(NULL);
+ if (IS_ERR(dev)) {
+ comedi_cleanup_board_minors();
+ cdev_del(&comedi_cdev);
+ unregister_chrdev_region(MKDEV(COMEDI_MAJOR, 0),
+ COMEDI_NUM_MINORS);
+ return PTR_ERR(dev);
+ }
+ /* comedi_alloc_board_minor() locked the mutex */
+ mutex_unlock(&dev->mutex);
+ }
+
+ return 0;
+}
+module_init(comedi_init);
+
+static void __exit comedi_cleanup(void)
+{
+ int i;
+
+ comedi_cleanup_board_minors();
+ for (i = 0; i < COMEDI_NUM_BOARD_MINORS; ++i)
+ BUG_ON(comedi_board_minor_table[i]);
+ for (i = 0; i < COMEDI_NUM_SUBDEVICE_MINORS; ++i)
+ BUG_ON(comedi_subdevice_minor_table[i]);
+
+ class_destroy(comedi_class);
+ cdev_del(&comedi_cdev);
+ unregister_chrdev_region(MKDEV(COMEDI_MAJOR, 0), COMEDI_NUM_MINORS);
+
+ comedi_proc_cleanup();
+}
+module_exit(comedi_cleanup);
+
+MODULE_AUTHOR("http://www.comedi.org");
+MODULE_DESCRIPTION("Comedi core module");
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/comedi/comedi_internal.h b/drivers/staging/comedi/comedi_internal.h
new file mode 100644
index 000000000..3b9185388
--- /dev/null
+++ b/drivers/staging/comedi/comedi_internal.h
@@ -0,0 +1,67 @@
+#ifndef _COMEDI_INTERNAL_H
+#define _COMEDI_INTERNAL_H
+
+#include <linux/compiler.h>
+#include <linux/types.h>
+
+/*
+ * various internal comedi stuff
+ */
+
+struct comedi_buf_map;
+struct comedi_devconfig;
+struct comedi_device;
+struct comedi_insn;
+struct comedi_rangeinfo;
+struct comedi_subdevice;
+struct device;
+
+int do_rangeinfo_ioctl(struct comedi_device *dev,
+ struct comedi_rangeinfo __user *arg);
+struct comedi_device *comedi_alloc_board_minor(struct device *hardware_device);
+void comedi_release_hardware_device(struct device *hardware_device);
+int comedi_alloc_subdevice_minor(struct comedi_subdevice *s);
+void comedi_free_subdevice_minor(struct comedi_subdevice *s);
+
+int comedi_buf_alloc(struct comedi_device *dev, struct comedi_subdevice *s,
+ unsigned long new_size);
+void comedi_buf_reset(struct comedi_subdevice *s);
+bool comedi_buf_is_mmapped(struct comedi_subdevice *s);
+void comedi_buf_map_get(struct comedi_buf_map *bm);
+int comedi_buf_map_put(struct comedi_buf_map *bm);
+struct comedi_buf_map *comedi_buf_map_from_subdev_get(
+ struct comedi_subdevice *s);
+unsigned int comedi_buf_write_n_allocated(struct comedi_subdevice *s);
+void comedi_device_cancel_all(struct comedi_device *dev);
+
+extern unsigned int comedi_default_buf_size_kb;
+extern unsigned int comedi_default_buf_maxsize_kb;
+
+/* drivers.c */
+
+extern struct comedi_driver *comedi_drivers;
+extern struct mutex comedi_drivers_list_lock;
+
+int insn_inval(struct comedi_device *, struct comedi_subdevice *,
+ struct comedi_insn *, unsigned int *);
+
+void comedi_device_detach(struct comedi_device *);
+int comedi_device_attach(struct comedi_device *, struct comedi_devconfig *);
+
+#ifdef CONFIG_PROC_FS
+
+/* proc.c */
+
+void comedi_proc_init(void);
+void comedi_proc_cleanup(void);
+#else
+static inline void comedi_proc_init(void)
+{
+}
+
+static inline void comedi_proc_cleanup(void)
+{
+}
+#endif
+
+#endif /* _COMEDI_INTERNAL_H */
diff --git a/drivers/staging/comedi/comedi_pci.c b/drivers/staging/comedi/comedi_pci.c
new file mode 100644
index 000000000..027f0f4e5
--- /dev/null
+++ b/drivers/staging/comedi/comedi_pci.c
@@ -0,0 +1,185 @@
+/*
+ * comedi_pci.c
+ * Comedi PCI driver specific functions.
+ *
+ * COMEDI - Linux Control and Measurement Device Interface
+ * Copyright (C) 1997-2000 David A. Schleef <ds@schleef.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/interrupt.h>
+
+#include "comedi_pci.h"
+
+/**
+ * comedi_to_pci_dev() - comedi_device pointer to pci_dev pointer.
+ * @dev: comedi_device struct
+ */
+struct pci_dev *comedi_to_pci_dev(struct comedi_device *dev)
+{
+ return dev->hw_dev ? to_pci_dev(dev->hw_dev) : NULL;
+}
+EXPORT_SYMBOL_GPL(comedi_to_pci_dev);
+
+/**
+ * comedi_pci_enable() - Enable the PCI device and request the regions.
+ * @dev: comedi_device struct
+ */
+int comedi_pci_enable(struct comedi_device *dev)
+{
+ struct pci_dev *pcidev = comedi_to_pci_dev(dev);
+ int rc;
+
+ if (!pcidev)
+ return -ENODEV;
+
+ rc = pci_enable_device(pcidev);
+ if (rc < 0)
+ return rc;
+
+ rc = pci_request_regions(pcidev, dev->board_name);
+ if (rc < 0)
+ pci_disable_device(pcidev);
+ else
+ dev->ioenabled = true;
+
+ return rc;
+}
+EXPORT_SYMBOL_GPL(comedi_pci_enable);
+
+/**
+ * comedi_pci_disable() - Release the regions and disable the PCI device.
+ * @dev: comedi_device struct
+ */
+void comedi_pci_disable(struct comedi_device *dev)
+{
+ struct pci_dev *pcidev = comedi_to_pci_dev(dev);
+
+ if (pcidev && dev->ioenabled) {
+ pci_release_regions(pcidev);
+ pci_disable_device(pcidev);
+ }
+ dev->ioenabled = false;
+}
+EXPORT_SYMBOL_GPL(comedi_pci_disable);
+
+/**
+ * comedi_pci_detach() - A generic (*detach) function for PCI drivers.
+ * @dev: comedi_device struct
+ */
+void comedi_pci_detach(struct comedi_device *dev)
+{
+ struct pci_dev *pcidev = comedi_to_pci_dev(dev);
+
+ if (!pcidev || !dev->ioenabled)
+ return;
+
+ if (dev->irq) {
+ free_irq(dev->irq, dev);
+ dev->irq = 0;
+ }
+ if (dev->mmio) {
+ iounmap(dev->mmio);
+ dev->mmio = NULL;
+ }
+ comedi_pci_disable(dev);
+}
+EXPORT_SYMBOL_GPL(comedi_pci_detach);
+
+/**
+ * comedi_pci_auto_config() - Configure/probe a comedi PCI driver.
+ * @pcidev: pci_dev struct
+ * @driver: comedi_driver struct
+ * @context: driver specific data, passed to comedi_auto_config()
+ *
+ * Typically called from the pci_driver (*probe) function.
+ */
+int comedi_pci_auto_config(struct pci_dev *pcidev,
+ struct comedi_driver *driver,
+ unsigned long context)
+{
+ return comedi_auto_config(&pcidev->dev, driver, context);
+}
+EXPORT_SYMBOL_GPL(comedi_pci_auto_config);
+
+/**
+ * comedi_pci_auto_unconfig() - Unconfigure/remove a comedi PCI driver.
+ * @pcidev: pci_dev struct
+ *
+ * Typically called from the pci_driver (*remove) function.
+ */
+void comedi_pci_auto_unconfig(struct pci_dev *pcidev)
+{
+ comedi_auto_unconfig(&pcidev->dev);
+}
+EXPORT_SYMBOL_GPL(comedi_pci_auto_unconfig);
+
+/**
+ * comedi_pci_driver_register() - Register a comedi PCI driver.
+ * @comedi_driver: comedi_driver struct
+ * @pci_driver: pci_driver struct
+ *
+ * This function is used for the module_init() of comedi PCI drivers.
+ * Do not call it directly, use the module_comedi_pci_driver() helper
+ * macro instead.
+ */
+int comedi_pci_driver_register(struct comedi_driver *comedi_driver,
+ struct pci_driver *pci_driver)
+{
+ int ret;
+
+ ret = comedi_driver_register(comedi_driver);
+ if (ret < 0)
+ return ret;
+
+ ret = pci_register_driver(pci_driver);
+ if (ret < 0) {
+ comedi_driver_unregister(comedi_driver);
+ return ret;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(comedi_pci_driver_register);
+
+/**
+ * comedi_pci_driver_unregister() - Unregister a comedi PCI driver.
+ * @comedi_driver: comedi_driver struct
+ * @pci_driver: pci_driver struct
+ *
+ * This function is used for the module_exit() of comedi PCI drivers.
+ * Do not call it directly, use the module_comedi_pci_driver() helper
+ * macro instead.
+ */
+void comedi_pci_driver_unregister(struct comedi_driver *comedi_driver,
+ struct pci_driver *pci_driver)
+{
+ pci_unregister_driver(pci_driver);
+ comedi_driver_unregister(comedi_driver);
+}
+EXPORT_SYMBOL_GPL(comedi_pci_driver_unregister);
+
+static int __init comedi_pci_init(void)
+{
+ return 0;
+}
+module_init(comedi_pci_init);
+
+static void __exit comedi_pci_exit(void)
+{
+}
+module_exit(comedi_pci_exit);
+
+MODULE_AUTHOR("http://www.comedi.org");
+MODULE_DESCRIPTION("Comedi PCI interface module");
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/comedi/comedi_pci.h b/drivers/staging/comedi/comedi_pci.h
new file mode 100644
index 000000000..4005cc9cf
--- /dev/null
+++ b/drivers/staging/comedi/comedi_pci.h
@@ -0,0 +1,64 @@
+/*
+ * comedi_pci.h
+ * header file for Comedi PCI drivers
+ *
+ * COMEDI - Linux Control and Measurement Device Interface
+ * Copyright (C) 1997-2000 David A. Schleef <ds@schleef.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _COMEDI_PCI_H
+#define _COMEDI_PCI_H
+
+#include <linux/pci.h>
+
+#include "comedidev.h"
+
+/*
+ * PCI Vendor IDs not in <linux/pci_ids.h>
+ */
+#define PCI_VENDOR_ID_KOLTER 0x1001
+#define PCI_VENDOR_ID_ICP 0x104c
+#define PCI_VENDOR_ID_DT 0x1116
+#define PCI_VENDOR_ID_IOTECH 0x1616
+#define PCI_VENDOR_ID_CONTEC 0x1221
+#define PCI_VENDOR_ID_RTD 0x1435
+#define PCI_VENDOR_ID_HUMUSOFT 0x186c
+
+struct pci_dev *comedi_to_pci_dev(struct comedi_device *);
+
+int comedi_pci_enable(struct comedi_device *);
+void comedi_pci_disable(struct comedi_device *);
+void comedi_pci_detach(struct comedi_device *);
+
+int comedi_pci_auto_config(struct pci_dev *, struct comedi_driver *,
+ unsigned long context);
+void comedi_pci_auto_unconfig(struct pci_dev *);
+
+int comedi_pci_driver_register(struct comedi_driver *, struct pci_driver *);
+void comedi_pci_driver_unregister(struct comedi_driver *, struct pci_driver *);
+
+/**
+ * module_comedi_pci_driver() - Helper macro for registering a comedi PCI driver
+ * @__comedi_driver: comedi_driver struct
+ * @__pci_driver: pci_driver struct
+ *
+ * Helper macro for comedi PCI drivers which do not do anything special
+ * in module init/exit. This eliminates a lot of boilerplate. Each
+ * module may only use this macro once, and calling it replaces
+ * module_init() and module_exit()
+ */
+#define module_comedi_pci_driver(__comedi_driver, __pci_driver) \
+ module_driver(__comedi_driver, comedi_pci_driver_register, \
+ comedi_pci_driver_unregister, &(__pci_driver))
+
+#endif /* _COMEDI_PCI_H */
diff --git a/drivers/staging/comedi/comedi_pcmcia.c b/drivers/staging/comedi/comedi_pcmcia.c
new file mode 100644
index 000000000..7e784399a
--- /dev/null
+++ b/drivers/staging/comedi/comedi_pcmcia.c
@@ -0,0 +1,169 @@
+/*
+ * comedi_pcmcia.c
+ * Comedi PCMCIA driver specific functions.
+ *
+ * COMEDI - Linux Control and Measurement Device Interface
+ * Copyright (C) 1997-2000 David A. Schleef <ds@schleef.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+
+#include "comedi_pcmcia.h"
+
+/**
+ * comedi_to_pcmcia_dev() - comedi_device pointer to pcmcia_device pointer.
+ * @dev: comedi_device struct
+ */
+struct pcmcia_device *comedi_to_pcmcia_dev(struct comedi_device *dev)
+{
+ return dev->hw_dev ? to_pcmcia_dev(dev->hw_dev) : NULL;
+}
+EXPORT_SYMBOL_GPL(comedi_to_pcmcia_dev);
+
+static int comedi_pcmcia_conf_check(struct pcmcia_device *link,
+ void *priv_data)
+{
+ if (link->config_index == 0)
+ return -EINVAL;
+
+ return pcmcia_request_io(link);
+}
+
+/**
+ * comedi_pcmcia_enable() - Request the regions and enable the PCMCIA device.
+ * @dev: comedi_device struct
+ * @conf_check: optional callback to check the pcmcia_device configuration
+ *
+ * The comedi PCMCIA driver needs to set the link->config_flags, as
+ * appropriate for that driver, before calling this function in order
+ * to allow pcmcia_loop_config() to do its internal autoconfiguration.
+ */
+int comedi_pcmcia_enable(struct comedi_device *dev,
+ int (*conf_check)(struct pcmcia_device *, void *))
+{
+ struct pcmcia_device *link = comedi_to_pcmcia_dev(dev);
+ int ret;
+
+ if (!link)
+ return -ENODEV;
+
+ if (!conf_check)
+ conf_check = comedi_pcmcia_conf_check;
+
+ ret = pcmcia_loop_config(link, conf_check, NULL);
+ if (ret)
+ return ret;
+
+ return pcmcia_enable_device(link);
+}
+EXPORT_SYMBOL_GPL(comedi_pcmcia_enable);
+
+/**
+ * comedi_pcmcia_disable() - Disable the PCMCIA device and release the regions.
+ * @dev: comedi_device struct
+ */
+void comedi_pcmcia_disable(struct comedi_device *dev)
+{
+ struct pcmcia_device *link = comedi_to_pcmcia_dev(dev);
+
+ if (link)
+ pcmcia_disable_device(link);
+}
+EXPORT_SYMBOL_GPL(comedi_pcmcia_disable);
+
+/**
+ * comedi_pcmcia_auto_config() - Configure/probe a comedi PCMCIA driver.
+ * @link: pcmcia_device struct
+ * @driver: comedi_driver struct
+ *
+ * Typically called from the pcmcia_driver (*probe) function.
+ */
+int comedi_pcmcia_auto_config(struct pcmcia_device *link,
+ struct comedi_driver *driver)
+{
+ return comedi_auto_config(&link->dev, driver, 0);
+}
+EXPORT_SYMBOL_GPL(comedi_pcmcia_auto_config);
+
+/**
+ * comedi_pcmcia_auto_unconfig() - Unconfigure/remove a comedi PCMCIA driver.
+ * @link: pcmcia_device struct
+ *
+ * Typically called from the pcmcia_driver (*remove) function.
+ */
+void comedi_pcmcia_auto_unconfig(struct pcmcia_device *link)
+{
+ comedi_auto_unconfig(&link->dev);
+}
+EXPORT_SYMBOL_GPL(comedi_pcmcia_auto_unconfig);
+
+/**
+ * comedi_pcmcia_driver_register() - Register a comedi PCMCIA driver.
+ * @comedi_driver: comedi_driver struct
+ * @pcmcia_driver: pcmcia_driver struct
+ *
+ * This function is used for the module_init() of comedi USB drivers.
+ * Do not call it directly, use the module_comedi_pcmcia_driver() helper
+ * macro instead.
+ */
+int comedi_pcmcia_driver_register(struct comedi_driver *comedi_driver,
+ struct pcmcia_driver *pcmcia_driver)
+{
+ int ret;
+
+ ret = comedi_driver_register(comedi_driver);
+ if (ret < 0)
+ return ret;
+
+ ret = pcmcia_register_driver(pcmcia_driver);
+ if (ret < 0) {
+ comedi_driver_unregister(comedi_driver);
+ return ret;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(comedi_pcmcia_driver_register);
+
+/**
+ * comedi_pcmcia_driver_unregister() - Unregister a comedi PCMCIA driver.
+ * @comedi_driver: comedi_driver struct
+ * @pcmcia_driver: pcmcia_driver struct
+ *
+ * This function is used for the module_exit() of comedi PCMCIA drivers.
+ * Do not call it directly, use the module_comedi_pcmcia_driver() helper
+ * macro instead.
+ */
+void comedi_pcmcia_driver_unregister(struct comedi_driver *comedi_driver,
+ struct pcmcia_driver *pcmcia_driver)
+{
+ pcmcia_unregister_driver(pcmcia_driver);
+ comedi_driver_unregister(comedi_driver);
+}
+EXPORT_SYMBOL_GPL(comedi_pcmcia_driver_unregister);
+
+static int __init comedi_pcmcia_init(void)
+{
+ return 0;
+}
+module_init(comedi_pcmcia_init);
+
+static void __exit comedi_pcmcia_exit(void)
+{
+}
+module_exit(comedi_pcmcia_exit);
+
+MODULE_AUTHOR("http://www.comedi.org");
+MODULE_DESCRIPTION("Comedi PCMCIA interface module");
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/comedi/comedi_pcmcia.h b/drivers/staging/comedi/comedi_pcmcia.h
new file mode 100644
index 000000000..5d3db2b9b
--- /dev/null
+++ b/drivers/staging/comedi/comedi_pcmcia.h
@@ -0,0 +1,55 @@
+/*
+ * comedi_pcmcia.h
+ * header file for Comedi PCMCIA drivers
+ *
+ * COMEDI - Linux Control and Measurement Device Interface
+ * Copyright (C) 1997-2000 David A. Schleef <ds@schleef.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _COMEDI_PCMCIA_H
+#define _COMEDI_PCMCIA_H
+
+#include <pcmcia/cistpl.h>
+#include <pcmcia/ds.h>
+
+#include "comedidev.h"
+
+struct pcmcia_device *comedi_to_pcmcia_dev(struct comedi_device *);
+
+int comedi_pcmcia_enable(struct comedi_device *,
+ int (*conf_check)(struct pcmcia_device *, void *));
+void comedi_pcmcia_disable(struct comedi_device *);
+
+int comedi_pcmcia_auto_config(struct pcmcia_device *, struct comedi_driver *);
+void comedi_pcmcia_auto_unconfig(struct pcmcia_device *);
+
+int comedi_pcmcia_driver_register(struct comedi_driver *,
+ struct pcmcia_driver *);
+void comedi_pcmcia_driver_unregister(struct comedi_driver *,
+ struct pcmcia_driver *);
+
+/**
+ * module_comedi_pcmcia_driver() - Helper macro for registering a comedi PCMCIA driver
+ * @__comedi_driver: comedi_driver struct
+ * @__pcmcia_driver: pcmcia_driver struct
+ *
+ * Helper macro for comedi PCMCIA drivers which do not do anything special
+ * in module init/exit. This eliminates a lot of boilerplate. Each
+ * module may only use this macro once, and calling it replaces
+ * module_init() and module_exit()
+ */
+#define module_comedi_pcmcia_driver(__comedi_driver, __pcmcia_driver) \
+ module_driver(__comedi_driver, comedi_pcmcia_driver_register, \
+ comedi_pcmcia_driver_unregister, &(__pcmcia_driver))
+
+#endif /* _COMEDI_PCMCIA_H */
diff --git a/drivers/staging/comedi/comedi_usb.c b/drivers/staging/comedi/comedi_usb.c
new file mode 100644
index 000000000..68b75e8fe
--- /dev/null
+++ b/drivers/staging/comedi/comedi_usb.c
@@ -0,0 +1,131 @@
+/*
+ * comedi_usb.c
+ * Comedi USB driver specific functions.
+ *
+ * COMEDI - Linux Control and Measurement Device Interface
+ * Copyright (C) 1997-2000 David A. Schleef <ds@schleef.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+
+#include "comedi_usb.h"
+
+/**
+ * comedi_to_usb_interface() - comedi_device pointer to usb_interface pointer.
+ * @dev: comedi_device struct
+ */
+struct usb_interface *comedi_to_usb_interface(struct comedi_device *dev)
+{
+ return dev->hw_dev ? to_usb_interface(dev->hw_dev) : NULL;
+}
+EXPORT_SYMBOL_GPL(comedi_to_usb_interface);
+
+/**
+ * comedi_to_usb_dev() - comedi_device pointer to usb_device pointer.
+ * @dev: comedi_device struct
+ */
+struct usb_device *comedi_to_usb_dev(struct comedi_device *dev)
+{
+ struct usb_interface *intf = comedi_to_usb_interface(dev);
+
+ return intf ? interface_to_usbdev(intf) : NULL;
+}
+EXPORT_SYMBOL_GPL(comedi_to_usb_dev);
+
+/**
+ * comedi_usb_auto_config() - Configure/probe a comedi USB driver.
+ * @intf: usb_interface struct
+ * @driver: comedi_driver struct
+ * @context: driver specific data, passed to comedi_auto_config()
+ *
+ * Typically called from the usb_driver (*probe) function.
+ */
+int comedi_usb_auto_config(struct usb_interface *intf,
+ struct comedi_driver *driver,
+ unsigned long context)
+{
+ return comedi_auto_config(&intf->dev, driver, context);
+}
+EXPORT_SYMBOL_GPL(comedi_usb_auto_config);
+
+/**
+ * comedi_pci_auto_unconfig() - Unconfigure/disconnect a comedi USB driver.
+ * @intf: usb_interface struct
+ *
+ * Typically called from the usb_driver (*disconnect) function.
+ */
+void comedi_usb_auto_unconfig(struct usb_interface *intf)
+{
+ comedi_auto_unconfig(&intf->dev);
+}
+EXPORT_SYMBOL_GPL(comedi_usb_auto_unconfig);
+
+/**
+ * comedi_usb_driver_register() - Register a comedi USB driver.
+ * @comedi_driver: comedi_driver struct
+ * @usb_driver: usb_driver struct
+ *
+ * This function is used for the module_init() of comedi USB drivers.
+ * Do not call it directly, use the module_comedi_usb_driver() helper
+ * macro instead.
+ */
+int comedi_usb_driver_register(struct comedi_driver *comedi_driver,
+ struct usb_driver *usb_driver)
+{
+ int ret;
+
+ ret = comedi_driver_register(comedi_driver);
+ if (ret < 0)
+ return ret;
+
+ ret = usb_register(usb_driver);
+ if (ret < 0) {
+ comedi_driver_unregister(comedi_driver);
+ return ret;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(comedi_usb_driver_register);
+
+/**
+ * comedi_usb_driver_unregister() - Unregister a comedi USB driver.
+ * @comedi_driver: comedi_driver struct
+ * @usb_driver: usb_driver struct
+ *
+ * This function is used for the module_exit() of comedi USB drivers.
+ * Do not call it directly, use the module_comedi_usb_driver() helper
+ * macro instead.
+ */
+void comedi_usb_driver_unregister(struct comedi_driver *comedi_driver,
+ struct usb_driver *usb_driver)
+{
+ usb_deregister(usb_driver);
+ comedi_driver_unregister(comedi_driver);
+}
+EXPORT_SYMBOL_GPL(comedi_usb_driver_unregister);
+
+static int __init comedi_usb_init(void)
+{
+ return 0;
+}
+module_init(comedi_usb_init);
+
+static void __exit comedi_usb_exit(void)
+{
+}
+module_exit(comedi_usb_exit);
+
+MODULE_AUTHOR("http://www.comedi.org");
+MODULE_DESCRIPTION("Comedi USB interface module");
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/comedi/comedi_usb.h b/drivers/staging/comedi/comedi_usb.h
new file mode 100644
index 000000000..721128bec
--- /dev/null
+++ b/drivers/staging/comedi/comedi_usb.h
@@ -0,0 +1,50 @@
+/*
+ * comedi_usb.h
+ * header file for USB Comedi drivers
+ *
+ * COMEDI - Linux Control and Measurement Device Interface
+ * Copyright (C) 1997-2000 David A. Schleef <ds@schleef.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _COMEDI_USB_H
+#define _COMEDI_USB_H
+
+#include <linux/usb.h>
+
+#include "comedidev.h"
+
+struct usb_interface *comedi_to_usb_interface(struct comedi_device *);
+struct usb_device *comedi_to_usb_dev(struct comedi_device *);
+
+int comedi_usb_auto_config(struct usb_interface *, struct comedi_driver *,
+ unsigned long context);
+void comedi_usb_auto_unconfig(struct usb_interface *);
+
+int comedi_usb_driver_register(struct comedi_driver *, struct usb_driver *);
+void comedi_usb_driver_unregister(struct comedi_driver *, struct usb_driver *);
+
+/**
+ * module_comedi_usb_driver() - Helper macro for registering a comedi USB driver
+ * @__comedi_driver: comedi_driver struct
+ * @__usb_driver: usb_driver struct
+ *
+ * Helper macro for comedi USB drivers which do not do anything special
+ * in module init/exit. This eliminates a lot of boilerplate. Each
+ * module may only use this macro once, and calling it replaces
+ * module_init() and module_exit()
+ */
+#define module_comedi_usb_driver(__comedi_driver, __usb_driver) \
+ module_driver(__comedi_driver, comedi_usb_driver_register, \
+ comedi_usb_driver_unregister, &(__usb_driver))
+
+#endif /* _COMEDI_USB_H */
diff --git a/drivers/staging/comedi/comedidev.h b/drivers/staging/comedi/comedidev.h
new file mode 100644
index 000000000..dfab5a84b
--- /dev/null
+++ b/drivers/staging/comedi/comedidev.h
@@ -0,0 +1,635 @@
+/*
+ include/linux/comedidev.h
+ header file for kernel-only structures, variables, and constants
+
+ COMEDI - Linux Control and Measurement Device Interface
+ Copyright (C) 1997-2000 David A. Schleef <ds@schleef.org>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+*/
+
+#ifndef _COMEDIDEV_H
+#define _COMEDIDEV_H
+
+#include <linux/dma-mapping.h>
+#include <linux/mutex.h>
+#include <linux/spinlock_types.h>
+#include <linux/rwsem.h>
+#include <linux/kref.h>
+
+#include "comedi.h"
+
+#define COMEDI_VERSION(a, b, c) (((a) << 16) + ((b) << 8) + (c))
+#define COMEDI_VERSION_CODE COMEDI_VERSION(COMEDI_MAJORVERSION, \
+ COMEDI_MINORVERSION, COMEDI_MICROVERSION)
+#define COMEDI_RELEASE VERSION
+
+#define COMEDI_NUM_BOARD_MINORS 0x30
+
+struct comedi_subdevice {
+ struct comedi_device *device;
+ int index;
+ int type;
+ int n_chan;
+ int subdev_flags;
+ int len_chanlist; /* maximum length of channel/gain list */
+
+ void *private;
+
+ struct comedi_async *async;
+
+ void *lock;
+ void *busy;
+ unsigned runflags;
+ spinlock_t spin_lock;
+
+ unsigned int io_bits;
+
+ unsigned int maxdata; /* if maxdata==0, use list */
+ const unsigned int *maxdata_list; /* list is channel specific */
+
+ const struct comedi_lrange *range_table;
+ const struct comedi_lrange *const *range_table_list;
+
+ unsigned int *chanlist; /* driver-owned chanlist (not used) */
+
+ int (*insn_read)(struct comedi_device *, struct comedi_subdevice *,
+ struct comedi_insn *, unsigned int *);
+ int (*insn_write)(struct comedi_device *, struct comedi_subdevice *,
+ struct comedi_insn *, unsigned int *);
+ int (*insn_bits)(struct comedi_device *, struct comedi_subdevice *,
+ struct comedi_insn *, unsigned int *);
+ int (*insn_config)(struct comedi_device *, struct comedi_subdevice *,
+ struct comedi_insn *, unsigned int *);
+
+ int (*do_cmd)(struct comedi_device *, struct comedi_subdevice *);
+ int (*do_cmdtest)(struct comedi_device *, struct comedi_subdevice *,
+ struct comedi_cmd *);
+ int (*poll)(struct comedi_device *, struct comedi_subdevice *);
+ int (*cancel)(struct comedi_device *, struct comedi_subdevice *);
+
+ /* called when the buffer changes */
+ int (*buf_change)(struct comedi_device *, struct comedi_subdevice *);
+
+ void (*munge)(struct comedi_device *dev, struct comedi_subdevice *s,
+ void *data, unsigned int num_bytes,
+ unsigned int start_chan_index);
+ enum dma_data_direction async_dma_dir;
+
+ unsigned int state;
+
+ struct device *class_dev;
+ int minor;
+
+ unsigned int *readback;
+};
+
+struct comedi_buf_page {
+ void *virt_addr;
+ dma_addr_t dma_addr;
+};
+
+struct comedi_buf_map {
+ struct device *dma_hw_dev;
+ struct comedi_buf_page *page_list;
+ unsigned int n_pages;
+ enum dma_data_direction dma_dir;
+ struct kref refcount;
+};
+
+/**
+ * struct comedi_async - control data for asynchronous comedi commands
+ * @prealloc_buf: preallocated buffer
+ * @prealloc_bufsz: buffer size (in bytes)
+ * @buf_map: map of buffer pages
+ * @max_bufsize: maximum buffer size (in bytes)
+ * @buf_write_count: "write completed" count (in bytes, modulo 2**32)
+ * @buf_write_alloc_count: "allocated for writing" count (in bytes,
+ * modulo 2**32)
+ * @buf_read_count: "read completed" count (in bytes, modulo 2**32)
+ * @buf_read_alloc_count: "allocated for reading" count (in bytes,
+ * modulo 2**32)
+ * @buf_write_ptr: buffer position for writer
+ * @buf_read_ptr: buffer position for reader
+ * @cur_chan: current position in chanlist for scan (for those
+ * drivers that use it)
+ * @scans_done: the number of scans completed (COMEDI_CB_EOS)
+ * @scan_progress: amount received or sent for current scan (in bytes)
+ * @munge_chan: current position in chanlist for "munging"
+ * @munge_count: "munge" count (in bytes, modulo 2**32)
+ * @munge_ptr: buffer position for "munging"
+ * @events: bit-vector of events that have occurred
+ * @cmd: details of comedi command in progress
+ * @wait_head: task wait queue for file reader or writer
+ * @cb_mask: bit-vector of events that should wake waiting tasks
+ * @inttrig: software trigger function for command, or NULL
+ *
+ * Note about the ..._count and ..._ptr members:
+ *
+ * Think of the _Count values being integers of unlimited size, indexing
+ * into a buffer of infinite length (though only an advancing portion
+ * of the buffer of fixed length prealloc_bufsz is accessible at any time).
+ * Then:
+ *
+ * Buf_Read_Count <= Buf_Read_Alloc_Count <= Munge_Count <=
+ * Buf_Write_Count <= Buf_Write_Alloc_Count <=
+ * (Buf_Read_Count + prealloc_bufsz)
+ *
+ * (Those aren't the actual members, apart from prealloc_bufsz.) When
+ * the buffer is reset, those _Count values start at 0 and only increase
+ * in value, maintaining the above inequalities until the next time the
+ * buffer is reset. The buffer is divided into the following regions by
+ * the inequalities:
+ *
+ * [0, Buf_Read_Count):
+ * old region no longer accessible
+ * [Buf_Read_Count, Buf_Read_Alloc_Count):
+ * filled and munged region allocated for reading but not yet read
+ * [Buf_Read_Alloc_Count, Munge_Count):
+ * filled and munged region not yet allocated for reading
+ * [Munge_Count, Buf_Write_Count):
+ * filled region not yet munged
+ * [Buf_Write_Count, Buf_Write_Alloc_Count):
+ * unfilled region allocated for writing but not yet written
+ * [Buf_Write_Alloc_Count, Buf_Read_Count + prealloc_bufsz):
+ * unfilled region not yet allocated for writing
+ * [Buf_Read_Count + prealloc_bufsz, infinity):
+ * unfilled region not yet accessible
+ *
+ * Data needs to be written into the buffer before it can be read out,
+ * and may need to be converted (or "munged") between the two
+ * operations. Extra unfilled buffer space may need to allocated for
+ * writing (advancing Buf_Write_Alloc_Count) before new data is written.
+ * After writing new data, the newly filled space needs to be released
+ * (advancing Buf_Write_Count). This also results in the new data being
+ * "munged" (advancing Munge_Count). Before data is read out of the
+ * buffer, extra space may need to be allocated for reading (advancing
+ * Buf_Read_Alloc_Count). After the data has been read out, the space
+ * needs to be released (advancing Buf_Read_Count).
+ *
+ * The actual members, buf_read_count, buf_read_alloc_count,
+ * munge_count, buf_write_count, and buf_write_alloc_count take the
+ * value of the corresponding capitalized _Count values modulo 2^32
+ * (UINT_MAX+1). Subtracting a "higher" _count value from a "lower"
+ * _count value gives the same answer as subtracting a "higher" _Count
+ * value from a lower _Count value because prealloc_bufsz < UINT_MAX+1.
+ * The modulo operation is done implicitly.
+ *
+ * The buf_read_ptr, munge_ptr, and buf_write_ptr members take the value
+ * of the corresponding capitalized _Count values modulo prealloc_bufsz.
+ * These correspond to byte indices in the physical buffer. The modulo
+ * operation is done by subtracting prealloc_bufsz when the value
+ * exceeds prealloc_bufsz (assuming prealloc_bufsz plus the increment is
+ * less than or equal to UINT_MAX).
+ */
+struct comedi_async {
+ void *prealloc_buf;
+ unsigned int prealloc_bufsz;
+ struct comedi_buf_map *buf_map;
+ unsigned int max_bufsize;
+ unsigned int buf_write_count;
+ unsigned int buf_write_alloc_count;
+ unsigned int buf_read_count;
+ unsigned int buf_read_alloc_count;
+ unsigned int buf_write_ptr;
+ unsigned int buf_read_ptr;
+ unsigned int cur_chan;
+ unsigned int scans_done;
+ unsigned int scan_progress;
+ unsigned int munge_chan;
+ unsigned int munge_count;
+ unsigned int munge_ptr;
+ unsigned int events;
+ struct comedi_cmd cmd;
+ wait_queue_head_t wait_head;
+ unsigned int cb_mask;
+ int (*inttrig)(struct comedi_device *dev, struct comedi_subdevice *s,
+ unsigned int x);
+};
+
+/**
+ * comedi_async callback "events"
+ * @COMEDI_CB_EOS: end-of-scan
+ * @COMEDI_CB_EOA: end-of-acquisition/output
+ * @COMEDI_CB_BLOCK: data has arrived, wakes up read() / write()
+ * @COMEDI_CB_EOBUF: DEPRECATED: end of buffer
+ * @COMEDI_CB_ERROR: card error during acquisition
+ * @COMEDI_CB_OVERFLOW: buffer overflow/underflow
+ *
+ * @COMEDI_CB_ERROR_MASK: events that indicate an error has occurred
+ * @COMEDI_CB_CANCEL_MASK: events that will cancel an async command
+ */
+#define COMEDI_CB_EOS (1 << 0)
+#define COMEDI_CB_EOA (1 << 1)
+#define COMEDI_CB_BLOCK (1 << 2)
+#define COMEDI_CB_EOBUF (1 << 3)
+#define COMEDI_CB_ERROR (1 << 4)
+#define COMEDI_CB_OVERFLOW (1 << 5)
+
+#define COMEDI_CB_ERROR_MASK (COMEDI_CB_ERROR | COMEDI_CB_OVERFLOW)
+#define COMEDI_CB_CANCEL_MASK (COMEDI_CB_EOA | COMEDI_CB_ERROR_MASK)
+
+struct comedi_driver {
+ struct comedi_driver *next;
+
+ const char *driver_name;
+ struct module *module;
+ int (*attach)(struct comedi_device *, struct comedi_devconfig *);
+ void (*detach)(struct comedi_device *);
+ int (*auto_attach)(struct comedi_device *, unsigned long);
+
+ /* number of elements in board_name and board_id arrays */
+ unsigned int num_names;
+ const char *const *board_name;
+ /* offset in bytes from one board name pointer to the next */
+ int offset;
+};
+
+struct comedi_device {
+ int use_count;
+ struct comedi_driver *driver;
+ struct comedi_8254 *pacer;
+ void *private;
+
+ struct device *class_dev;
+ int minor;
+ unsigned int detach_count;
+ /* hw_dev is passed to dma_alloc_coherent when allocating async buffers
+ * for subdevices that have async_dma_dir set to something other than
+ * DMA_NONE */
+ struct device *hw_dev;
+
+ const char *board_name;
+ const void *board_ptr;
+ bool attached:1;
+ bool ioenabled:1;
+ spinlock_t spinlock;
+ struct mutex mutex;
+ struct rw_semaphore attach_lock;
+ struct kref refcount;
+
+ int n_subdevices;
+ struct comedi_subdevice *subdevices;
+
+ /* dumb */
+ void __iomem *mmio;
+ unsigned long iobase;
+ unsigned long iolen;
+ unsigned int irq;
+
+ struct comedi_subdevice *read_subdev;
+ struct comedi_subdevice *write_subdev;
+
+ struct fasync_struct *async_queue;
+
+ int (*open)(struct comedi_device *dev);
+ void (*close)(struct comedi_device *dev);
+};
+
+/*
+ * function prototypes
+ */
+
+void comedi_event(struct comedi_device *dev, struct comedi_subdevice *s);
+
+struct comedi_device *comedi_dev_get_from_minor(unsigned minor);
+int comedi_dev_put(struct comedi_device *dev);
+
+/**
+ * comedi_subdevice "runflags"
+ * @COMEDI_SRF_RT: DEPRECATED: command is running real-time
+ * @COMEDI_SRF_ERROR: indicates an COMEDI_CB_ERROR event has occurred
+ * since the last command was started
+ * @COMEDI_SRF_RUNNING: command is running
+ * @COMEDI_SRF_FREE_SPRIV: free s->private on detach
+ *
+ * @COMEDI_SRF_BUSY_MASK: runflags that indicate the subdevice is "busy"
+ */
+#define COMEDI_SRF_RT BIT(1)
+#define COMEDI_SRF_ERROR BIT(2)
+#define COMEDI_SRF_RUNNING BIT(27)
+#define COMEDI_SRF_FREE_SPRIV BIT(31)
+
+#define COMEDI_SRF_BUSY_MASK (COMEDI_SRF_ERROR | COMEDI_SRF_RUNNING)
+
+bool comedi_is_subdevice_running(struct comedi_subdevice *s);
+
+void *comedi_alloc_spriv(struct comedi_subdevice *s, size_t size);
+
+int comedi_check_chanlist(struct comedi_subdevice *s,
+ int n,
+ unsigned int *chanlist);
+
+/* range stuff */
+
+#define RANGE(a, b) {(a)*1e6, (b)*1e6, 0}
+#define RANGE_ext(a, b) {(a)*1e6, (b)*1e6, RF_EXTERNAL}
+#define RANGE_mA(a, b) {(a)*1e6, (b)*1e6, UNIT_mA}
+#define RANGE_unitless(a, b) {(a)*1e6, (b)*1e6, 0}
+#define BIP_RANGE(a) {-(a)*1e6, (a)*1e6, 0}
+#define UNI_RANGE(a) {0, (a)*1e6, 0}
+
+extern const struct comedi_lrange range_bipolar10;
+extern const struct comedi_lrange range_bipolar5;
+extern const struct comedi_lrange range_bipolar2_5;
+extern const struct comedi_lrange range_unipolar10;
+extern const struct comedi_lrange range_unipolar5;
+extern const struct comedi_lrange range_unipolar2_5;
+extern const struct comedi_lrange range_0_20mA;
+extern const struct comedi_lrange range_4_20mA;
+extern const struct comedi_lrange range_0_32mA;
+extern const struct comedi_lrange range_unknown;
+
+#define range_digital range_unipolar5
+
+#if __GNUC__ >= 3
+#define GCC_ZERO_LENGTH_ARRAY
+#else
+#define GCC_ZERO_LENGTH_ARRAY 0
+#endif
+
+struct comedi_lrange {
+ int length;
+ struct comedi_krange range[GCC_ZERO_LENGTH_ARRAY];
+};
+
+static inline bool comedi_range_is_bipolar(struct comedi_subdevice *s,
+ unsigned int range)
+{
+ return s->range_table->range[range].min < 0;
+}
+
+static inline bool comedi_range_is_unipolar(struct comedi_subdevice *s,
+ unsigned int range)
+{
+ return s->range_table->range[range].min >= 0;
+}
+
+static inline bool comedi_range_is_external(struct comedi_subdevice *s,
+ unsigned int range)
+{
+ return !!(s->range_table->range[range].flags & RF_EXTERNAL);
+}
+
+static inline bool comedi_chan_range_is_bipolar(struct comedi_subdevice *s,
+ unsigned int chan,
+ unsigned int range)
+{
+ return s->range_table_list[chan]->range[range].min < 0;
+}
+
+static inline bool comedi_chan_range_is_unipolar(struct comedi_subdevice *s,
+ unsigned int chan,
+ unsigned int range)
+{
+ return s->range_table_list[chan]->range[range].min >= 0;
+}
+
+static inline bool comedi_chan_range_is_external(struct comedi_subdevice *s,
+ unsigned int chan,
+ unsigned int range)
+{
+ return !!(s->range_table_list[chan]->range[range].flags & RF_EXTERNAL);
+}
+
+/* munge between offset binary and two's complement values */
+static inline unsigned int comedi_offset_munge(struct comedi_subdevice *s,
+ unsigned int val)
+{
+ return val ^ s->maxdata ^ (s->maxdata >> 1);
+}
+
+/**
+ * comedi_bytes_per_sample - determine subdevice sample size
+ * @s: comedi_subdevice struct
+ *
+ * The sample size will be 4 (sizeof int) or 2 (sizeof short) depending on
+ * whether the SDF_LSAMPL subdevice flag is set or not.
+ *
+ * Returns the subdevice sample size.
+ */
+static inline unsigned int comedi_bytes_per_sample(struct comedi_subdevice *s)
+{
+ return s->subdev_flags & SDF_LSAMPL ? sizeof(int) : sizeof(short);
+}
+
+/**
+ * comedi_sample_shift - determine log2 of subdevice sample size
+ * @s: comedi_subdevice struct
+ *
+ * The sample size will be 4 (sizeof int) or 2 (sizeof short) depending on
+ * whether the SDF_LSAMPL subdevice flag is set or not. The log2 of the
+ * sample size will be 2 or 1 and can be used as the right operand of a
+ * bit-shift operator to multiply or divide something by the sample size.
+ *
+ * Returns log2 of the subdevice sample size.
+ */
+static inline unsigned int comedi_sample_shift(struct comedi_subdevice *s)
+{
+ return s->subdev_flags & SDF_LSAMPL ? 2 : 1;
+}
+
+/**
+ * comedi_bytes_to_samples - converts a number of bytes to a number of samples
+ * @s: comedi_subdevice struct
+ * @nbytes: number of bytes
+ *
+ * Returns the number of bytes divided by the subdevice sample size.
+ */
+static inline unsigned int comedi_bytes_to_samples(struct comedi_subdevice *s,
+ unsigned int nbytes)
+{
+ return nbytes >> comedi_sample_shift(s);
+}
+
+/**
+ * comedi_samples_to_bytes - converts a number of samples to a number of bytes
+ * @s: comedi_subdevice struct
+ * @nsamples: number of samples
+ *
+ * Returns the number of samples multiplied by the subdevice sample size.
+ * Does not check for arithmetic overflow.
+ */
+static inline unsigned int comedi_samples_to_bytes(struct comedi_subdevice *s,
+ unsigned int nsamples)
+{
+ return nsamples << comedi_sample_shift(s);
+}
+
+/**
+ * comedi_check_trigger_src() - trivially validate a comedi_cmd trigger source
+ * @src: pointer to the trigger source to validate
+ * @flags: bitmask of valid TRIG_* for the trigger
+ *
+ * This is used in "step 1" of the do_cmdtest functions of comedi drivers
+ * to vaildate the comedi_cmd triggers. The mask of the @src against the
+ * @flags allows the userspace comedilib to pass all the comedi_cmd
+ * triggers as TRIG_ANY and get back a bitmask of the valid trigger sources.
+ */
+static inline int comedi_check_trigger_src(unsigned int *src,
+ unsigned int flags)
+{
+ unsigned int orig_src = *src;
+
+ *src = orig_src & flags;
+ if (*src == TRIG_INVALID || *src != orig_src)
+ return -EINVAL;
+ return 0;
+}
+
+/**
+ * comedi_check_trigger_is_unique() - make sure a trigger source is unique
+ * @src: the trigger source to check
+ */
+static inline int comedi_check_trigger_is_unique(unsigned int src)
+{
+ /* this test is true if more than one _src bit is set */
+ if ((src & (src - 1)) != 0)
+ return -EINVAL;
+ return 0;
+}
+
+/**
+ * comedi_check_trigger_arg_is() - trivially validate a trigger argument
+ * @arg: pointer to the trigger arg to validate
+ * @val: the value the argument should be
+ */
+static inline int comedi_check_trigger_arg_is(unsigned int *arg,
+ unsigned int val)
+{
+ if (*arg != val) {
+ *arg = val;
+ return -EINVAL;
+ }
+ return 0;
+}
+
+/**
+ * comedi_check_trigger_arg_min() - trivially validate a trigger argument
+ * @arg: pointer to the trigger arg to validate
+ * @val: the minimum value the argument should be
+ */
+static inline int comedi_check_trigger_arg_min(unsigned int *arg,
+ unsigned int val)
+{
+ if (*arg < val) {
+ *arg = val;
+ return -EINVAL;
+ }
+ return 0;
+}
+
+/**
+ * comedi_check_trigger_arg_max() - trivially validate a trigger argument
+ * @arg: pointer to the trigger arg to validate
+ * @val: the maximum value the argument should be
+ */
+static inline int comedi_check_trigger_arg_max(unsigned int *arg,
+ unsigned int val)
+{
+ if (*arg > val) {
+ *arg = val;
+ return -EINVAL;
+ }
+ return 0;
+}
+
+/*
+ * Must set dev->hw_dev if you wish to dma directly into comedi's buffer.
+ * Also useful for retrieving a previously configured hardware device of
+ * known bus type. Set automatically for auto-configured devices.
+ * Automatically set to NULL when detaching hardware device.
+ */
+int comedi_set_hw_dev(struct comedi_device *dev, struct device *hw_dev);
+
+static inline unsigned int comedi_buf_n_bytes_ready(struct comedi_subdevice *s)
+{
+ return s->async->buf_write_count - s->async->buf_read_count;
+}
+
+unsigned int comedi_buf_write_alloc(struct comedi_subdevice *s, unsigned int n);
+unsigned int comedi_buf_write_free(struct comedi_subdevice *s, unsigned int n);
+
+unsigned int comedi_buf_read_n_available(struct comedi_subdevice *s);
+unsigned int comedi_buf_read_alloc(struct comedi_subdevice *s, unsigned int n);
+unsigned int comedi_buf_read_free(struct comedi_subdevice *s, unsigned int n);
+
+unsigned int comedi_buf_write_samples(struct comedi_subdevice *s,
+ const void *data, unsigned int nsamples);
+unsigned int comedi_buf_read_samples(struct comedi_subdevice *s,
+ void *data, unsigned int nsamples);
+
+/* drivers.c - general comedi driver functions */
+
+#define COMEDI_TIMEOUT_MS 1000
+
+int comedi_timeout(struct comedi_device *, struct comedi_subdevice *,
+ struct comedi_insn *,
+ int (*cb)(struct comedi_device *, struct comedi_subdevice *,
+ struct comedi_insn *, unsigned long context),
+ unsigned long context);
+
+unsigned int comedi_handle_events(struct comedi_device *dev,
+ struct comedi_subdevice *s);
+
+int comedi_dio_insn_config(struct comedi_device *, struct comedi_subdevice *,
+ struct comedi_insn *, unsigned int *data,
+ unsigned int mask);
+unsigned int comedi_dio_update_state(struct comedi_subdevice *,
+ unsigned int *data);
+unsigned int comedi_bytes_per_scan(struct comedi_subdevice *s);
+unsigned int comedi_nscans_left(struct comedi_subdevice *s,
+ unsigned int nscans);
+unsigned int comedi_nsamples_left(struct comedi_subdevice *s,
+ unsigned int nsamples);
+void comedi_inc_scan_progress(struct comedi_subdevice *s,
+ unsigned int num_bytes);
+
+void *comedi_alloc_devpriv(struct comedi_device *, size_t);
+int comedi_alloc_subdevices(struct comedi_device *, int);
+int comedi_alloc_subdev_readback(struct comedi_subdevice *);
+
+int comedi_readback_insn_read(struct comedi_device *, struct comedi_subdevice *,
+ struct comedi_insn *, unsigned int *data);
+
+int comedi_load_firmware(struct comedi_device *, struct device *,
+ const char *name,
+ int (*cb)(struct comedi_device *,
+ const u8 *data, size_t size,
+ unsigned long context),
+ unsigned long context);
+
+int __comedi_request_region(struct comedi_device *,
+ unsigned long start, unsigned long len);
+int comedi_request_region(struct comedi_device *,
+ unsigned long start, unsigned long len);
+void comedi_legacy_detach(struct comedi_device *);
+
+int comedi_auto_config(struct device *, struct comedi_driver *,
+ unsigned long context);
+void comedi_auto_unconfig(struct device *);
+
+int comedi_driver_register(struct comedi_driver *);
+void comedi_driver_unregister(struct comedi_driver *);
+
+/**
+ * module_comedi_driver() - Helper macro for registering a comedi driver
+ * @__comedi_driver: comedi_driver struct
+ *
+ * Helper macro for comedi drivers which do not do anything special in module
+ * init/exit. This eliminates a lot of boilerplate. Each module may only use
+ * this macro once, and calling it replaces module_init() and module_exit().
+ */
+#define module_comedi_driver(__comedi_driver) \
+ module_driver(__comedi_driver, comedi_driver_register, \
+ comedi_driver_unregister)
+
+#endif /* _COMEDIDEV_H */
diff --git a/drivers/staging/comedi/comedilib.h b/drivers/staging/comedi/comedilib.h
new file mode 100644
index 000000000..56baf852e
--- /dev/null
+++ b/drivers/staging/comedi/comedilib.h
@@ -0,0 +1,35 @@
+/*
+ linux/include/comedilib.h
+ header file for kcomedilib
+
+ COMEDI - Linux Control and Measurement Device Interface
+ Copyright (C) 1998-2001 David A. Schleef <ds@schleef.org>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+*/
+
+#ifndef _LINUX_COMEDILIB_H
+#define _LINUX_COMEDILIB_H
+
+struct comedi_device *comedi_open(const char *path);
+int comedi_close(struct comedi_device *dev);
+int comedi_dio_get_config(struct comedi_device *dev, unsigned int subdev,
+ unsigned int chan, unsigned int *io);
+int comedi_dio_config(struct comedi_device *dev, unsigned int subdev,
+ unsigned int chan, unsigned int io);
+int comedi_dio_bitfield2(struct comedi_device *dev, unsigned int subdev,
+ unsigned int mask, unsigned int *bits,
+ unsigned int base_channel);
+int comedi_find_subdevice_by_type(struct comedi_device *dev, int type,
+ unsigned int subd);
+int comedi_get_n_channels(struct comedi_device *dev, unsigned int subdevice);
+
+#endif
diff --git a/drivers/staging/comedi/drivers.c b/drivers/staging/comedi/drivers.c
new file mode 100644
index 000000000..f72a3ac9a
--- /dev/null
+++ b/drivers/staging/comedi/drivers.c
@@ -0,0 +1,956 @@
+/*
+ module/drivers.c
+ functions for manipulating drivers
+
+ COMEDI - Linux Control and Measurement Device Interface
+ Copyright (C) 1997-2000 David A. Schleef <ds@schleef.org>
+ Copyright (C) 2002 Frank Mori Hess <fmhess@users.sourceforge.net>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+*/
+
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/kconfig.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/fcntl.h>
+#include <linux/ioport.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <linux/highmem.h> /* for SuSE brokenness */
+#include <linux/vmalloc.h>
+#include <linux/cdev.h>
+#include <linux/dma-mapping.h>
+#include <linux/io.h>
+#include <linux/interrupt.h>
+#include <linux/firmware.h>
+
+#include "comedidev.h"
+#include "comedi_internal.h"
+
+struct comedi_driver *comedi_drivers;
+/* protects access to comedi_drivers */
+DEFINE_MUTEX(comedi_drivers_list_lock);
+
+int comedi_set_hw_dev(struct comedi_device *dev, struct device *hw_dev)
+{
+ if (hw_dev == dev->hw_dev)
+ return 0;
+ if (dev->hw_dev)
+ return -EEXIST;
+ dev->hw_dev = get_device(hw_dev);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(comedi_set_hw_dev);
+
+static void comedi_clear_hw_dev(struct comedi_device *dev)
+{
+ put_device(dev->hw_dev);
+ dev->hw_dev = NULL;
+}
+
+/**
+ * comedi_alloc_devpriv() - Allocate memory for the device private data.
+ * @dev: comedi_device struct
+ * @size: size of the memory to allocate
+ */
+void *comedi_alloc_devpriv(struct comedi_device *dev, size_t size)
+{
+ dev->private = kzalloc(size, GFP_KERNEL);
+ return dev->private;
+}
+EXPORT_SYMBOL_GPL(comedi_alloc_devpriv);
+
+int comedi_alloc_subdevices(struct comedi_device *dev, int num_subdevices)
+{
+ struct comedi_subdevice *s;
+ int i;
+
+ if (num_subdevices < 1)
+ return -EINVAL;
+
+ s = kcalloc(num_subdevices, sizeof(*s), GFP_KERNEL);
+ if (!s)
+ return -ENOMEM;
+ dev->subdevices = s;
+ dev->n_subdevices = num_subdevices;
+
+ for (i = 0; i < num_subdevices; ++i) {
+ s = &dev->subdevices[i];
+ s->device = dev;
+ s->index = i;
+ s->async_dma_dir = DMA_NONE;
+ spin_lock_init(&s->spin_lock);
+ s->minor = -1;
+ }
+ return 0;
+}
+EXPORT_SYMBOL_GPL(comedi_alloc_subdevices);
+
+/**
+ * comedi_alloc_subdev_readback() - Allocate memory for the subdevice readback.
+ * @s: comedi_subdevice struct
+ */
+int comedi_alloc_subdev_readback(struct comedi_subdevice *s)
+{
+ if (!s->n_chan)
+ return -EINVAL;
+
+ s->readback = kcalloc(s->n_chan, sizeof(*s->readback), GFP_KERNEL);
+ if (!s->readback)
+ return -ENOMEM;
+
+ if (!s->insn_read)
+ s->insn_read = comedi_readback_insn_read;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(comedi_alloc_subdev_readback);
+
+static void comedi_device_detach_cleanup(struct comedi_device *dev)
+{
+ int i;
+ struct comedi_subdevice *s;
+
+ if (dev->subdevices) {
+ for (i = 0; i < dev->n_subdevices; i++) {
+ s = &dev->subdevices[i];
+ if (s->runflags & COMEDI_SRF_FREE_SPRIV)
+ kfree(s->private);
+ comedi_free_subdevice_minor(s);
+ if (s->async) {
+ comedi_buf_alloc(dev, s, 0);
+ kfree(s->async);
+ }
+ kfree(s->readback);
+ }
+ kfree(dev->subdevices);
+ dev->subdevices = NULL;
+ dev->n_subdevices = 0;
+ }
+ kfree(dev->private);
+ kfree(dev->pacer);
+ dev->private = NULL;
+ dev->pacer = NULL;
+ dev->driver = NULL;
+ dev->board_name = NULL;
+ dev->board_ptr = NULL;
+ dev->mmio = NULL;
+ dev->iobase = 0;
+ dev->iolen = 0;
+ dev->ioenabled = false;
+ dev->irq = 0;
+ dev->read_subdev = NULL;
+ dev->write_subdev = NULL;
+ dev->open = NULL;
+ dev->close = NULL;
+ comedi_clear_hw_dev(dev);
+}
+
+void comedi_device_detach(struct comedi_device *dev)
+{
+ comedi_device_cancel_all(dev);
+ down_write(&dev->attach_lock);
+ dev->attached = false;
+ dev->detach_count++;
+ if (dev->driver)
+ dev->driver->detach(dev);
+ comedi_device_detach_cleanup(dev);
+ up_write(&dev->attach_lock);
+}
+
+static int poll_invalid(struct comedi_device *dev, struct comedi_subdevice *s)
+{
+ return -EINVAL;
+}
+
+int insn_inval(struct comedi_device *dev, struct comedi_subdevice *s,
+ struct comedi_insn *insn, unsigned int *data)
+{
+ return -EINVAL;
+}
+
+/**
+ * comedi_readback_insn_read() - A generic (*insn_read) for subdevice readback.
+ * @dev: comedi_device struct
+ * @s: comedi_subdevice struct
+ * @insn: comedi_insn struct
+ * @data: pointer to return the readback data
+ */
+int comedi_readback_insn_read(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ unsigned int chan = CR_CHAN(insn->chanspec);
+ int i;
+
+ if (!s->readback)
+ return -EINVAL;
+
+ for (i = 0; i < insn->n; i++)
+ data[i] = s->readback[chan];
+
+ return insn->n;
+}
+EXPORT_SYMBOL_GPL(comedi_readback_insn_read);
+
+/**
+ * comedi_timeout() - busy-wait for a driver condition to occur.
+ * @dev: comedi_device struct
+ * @s: comedi_subdevice struct
+ * @insn: comedi_insn struct
+ * @cb: callback to check for the condition
+ * @context: private context from the driver
+ */
+int comedi_timeout(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ int (*cb)(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned long context),
+ unsigned long context)
+{
+ unsigned long timeout = jiffies + msecs_to_jiffies(COMEDI_TIMEOUT_MS);
+ int ret;
+
+ while (time_before(jiffies, timeout)) {
+ ret = cb(dev, s, insn, context);
+ if (ret != -EBUSY)
+ return ret; /* success (0) or non EBUSY errno */
+ cpu_relax();
+ }
+ return -ETIMEDOUT;
+}
+EXPORT_SYMBOL_GPL(comedi_timeout);
+
+/**
+ * comedi_dio_insn_config() - boilerplate (*insn_config) for DIO subdevices.
+ * @dev: comedi_device struct
+ * @s: comedi_subdevice struct
+ * @insn: comedi_insn struct
+ * @data: parameters for the @insn
+ * @mask: io_bits mask for grouped channels
+ */
+int comedi_dio_insn_config(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data,
+ unsigned int mask)
+{
+ unsigned int chan_mask = 1 << CR_CHAN(insn->chanspec);
+
+ if (!mask)
+ mask = chan_mask;
+
+ switch (data[0]) {
+ case INSN_CONFIG_DIO_INPUT:
+ s->io_bits &= ~mask;
+ break;
+
+ case INSN_CONFIG_DIO_OUTPUT:
+ s->io_bits |= mask;
+ break;
+
+ case INSN_CONFIG_DIO_QUERY:
+ data[1] = (s->io_bits & mask) ? COMEDI_OUTPUT : COMEDI_INPUT;
+ return insn->n;
+
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(comedi_dio_insn_config);
+
+/**
+ * comedi_dio_update_state() - update the internal state of DIO subdevices.
+ * @s: comedi_subdevice struct
+ * @data: the channel mask and bits to update
+ */
+unsigned int comedi_dio_update_state(struct comedi_subdevice *s,
+ unsigned int *data)
+{
+ unsigned int chanmask = (s->n_chan < 32) ? ((1 << s->n_chan) - 1)
+ : 0xffffffff;
+ unsigned int mask = data[0] & chanmask;
+ unsigned int bits = data[1];
+
+ if (mask) {
+ s->state &= ~mask;
+ s->state |= (bits & mask);
+ }
+
+ return mask;
+}
+EXPORT_SYMBOL_GPL(comedi_dio_update_state);
+
+/**
+ * comedi_bytes_per_scan - get length of asynchronous command "scan" in bytes
+ * @s: comedi_subdevice struct
+ *
+ * Determines the overall scan length according to the subdevice type and the
+ * number of channels in the scan.
+ *
+ * For digital input, output or input/output subdevices, samples for multiple
+ * channels are assumed to be packed into one or more unsigned short or
+ * unsigned int values according to the subdevice's SDF_LSAMPL flag. For other
+ * types of subdevice, samples are assumed to occupy a whole unsigned short or
+ * unsigned int according to the SDF_LSAMPL flag.
+ *
+ * Returns the overall scan length in bytes.
+ */
+unsigned int comedi_bytes_per_scan(struct comedi_subdevice *s)
+{
+ struct comedi_cmd *cmd = &s->async->cmd;
+ unsigned int num_samples;
+ unsigned int bits_per_sample;
+
+ switch (s->type) {
+ case COMEDI_SUBD_DI:
+ case COMEDI_SUBD_DO:
+ case COMEDI_SUBD_DIO:
+ bits_per_sample = 8 * comedi_bytes_per_sample(s);
+ num_samples = DIV_ROUND_UP(cmd->scan_end_arg, bits_per_sample);
+ break;
+ default:
+ num_samples = cmd->scan_end_arg;
+ break;
+ }
+ return comedi_samples_to_bytes(s, num_samples);
+}
+EXPORT_SYMBOL_GPL(comedi_bytes_per_scan);
+
+/**
+ * comedi_nscans_left - return the number of scans left in the command
+ * @s: comedi_subdevice struct
+ * @nscans: the expected number of scans
+ *
+ * If nscans is 0, the number of scans available in the async buffer will be
+ * used. Otherwise the expected number of scans will be used.
+ *
+ * If the async command has a stop_src of TRIG_COUNT, the nscans will be
+ * checked against the number of scans left in the command.
+ *
+ * The return value will then be either the expected number of scans or the
+ * number of scans remaining in the command.
+ */
+unsigned int comedi_nscans_left(struct comedi_subdevice *s,
+ unsigned int nscans)
+{
+ struct comedi_async *async = s->async;
+ struct comedi_cmd *cmd = &async->cmd;
+
+ if (nscans == 0) {
+ unsigned int nbytes = comedi_buf_read_n_available(s);
+
+ nscans = nbytes / comedi_bytes_per_scan(s);
+ }
+
+ if (cmd->stop_src == TRIG_COUNT) {
+ unsigned int scans_left = 0;
+
+ if (async->scans_done < cmd->stop_arg)
+ scans_left = cmd->stop_arg - async->scans_done;
+
+ if (nscans > scans_left)
+ nscans = scans_left;
+ }
+ return nscans;
+}
+EXPORT_SYMBOL_GPL(comedi_nscans_left);
+
+/**
+ * comedi_nsamples_left - return the number of samples left in the command
+ * @s: comedi_subdevice struct
+ * @nsamples: the expected number of samples
+ *
+ * Returns the expected number of samples of the number of samples remaining
+ * in the command.
+ */
+unsigned int comedi_nsamples_left(struct comedi_subdevice *s,
+ unsigned int nsamples)
+{
+ struct comedi_async *async = s->async;
+ struct comedi_cmd *cmd = &async->cmd;
+
+ if (cmd->stop_src == TRIG_COUNT) {
+ /* +1 to force comedi_nscans_left() to return the scans left */
+ unsigned int nscans = (nsamples / cmd->scan_end_arg) + 1;
+ unsigned int scans_left = comedi_nscans_left(s, nscans);
+ unsigned int scan_pos =
+ comedi_bytes_to_samples(s, async->scan_progress);
+ unsigned long long samples_left = 0;
+
+ if (scans_left) {
+ samples_left = ((unsigned long long)scans_left *
+ cmd->scan_end_arg) - scan_pos;
+ }
+
+ if (samples_left < nsamples)
+ nsamples = samples_left;
+ }
+ return nsamples;
+}
+EXPORT_SYMBOL_GPL(comedi_nsamples_left);
+
+/**
+ * comedi_inc_scan_progress - update scan progress in asynchronous command
+ * @s: comedi_subdevice struct
+ * @num_bytes: amount of data in bytes to increment scan progress
+ *
+ * Increments the scan progress by the number of bytes specified by num_bytes.
+ * If the scan progress reaches or exceeds the scan length in bytes, reduce
+ * it modulo the scan length in bytes and set the "end of scan" asynchronous
+ * event flag to be processed later.
+ */
+void comedi_inc_scan_progress(struct comedi_subdevice *s,
+ unsigned int num_bytes)
+{
+ struct comedi_async *async = s->async;
+ struct comedi_cmd *cmd = &async->cmd;
+ unsigned int scan_length = comedi_bytes_per_scan(s);
+
+ /* track the 'cur_chan' for non-SDF_PACKED subdevices */
+ if (!(s->subdev_flags & SDF_PACKED)) {
+ async->cur_chan += comedi_bytes_to_samples(s, num_bytes);
+ async->cur_chan %= cmd->chanlist_len;
+ }
+
+ async->scan_progress += num_bytes;
+ if (async->scan_progress >= scan_length) {
+ unsigned int nscans = async->scan_progress / scan_length;
+
+ if (async->scans_done < (UINT_MAX - nscans))
+ async->scans_done += nscans;
+ else
+ async->scans_done = UINT_MAX;
+
+ async->scan_progress %= scan_length;
+ async->events |= COMEDI_CB_EOS;
+ }
+}
+EXPORT_SYMBOL_GPL(comedi_inc_scan_progress);
+
+/**
+ * comedi_handle_events - handle events and possibly stop acquisition
+ * @dev: comedi_device struct
+ * @s: comedi_subdevice struct
+ *
+ * Handles outstanding asynchronous acquisition event flags associated
+ * with the subdevice. Call the subdevice's "->cancel()" handler if the
+ * "end of acquisition", "error" or "overflow" event flags are set in order
+ * to stop the acquisition at the driver level.
+ *
+ * Calls comedi_event() to further process the event flags, which may mark
+ * the asynchronous command as no longer running, possibly terminated with
+ * an error, and may wake up tasks.
+ *
+ * Return a bit-mask of the handled events.
+ */
+unsigned int comedi_handle_events(struct comedi_device *dev,
+ struct comedi_subdevice *s)
+{
+ unsigned int events = s->async->events;
+
+ if (events == 0)
+ return events;
+
+ if (events & COMEDI_CB_CANCEL_MASK)
+ s->cancel(dev, s);
+
+ comedi_event(dev, s);
+
+ return events;
+}
+EXPORT_SYMBOL_GPL(comedi_handle_events);
+
+static int insn_rw_emulate_bits(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn, unsigned int *data)
+{
+ struct comedi_insn new_insn;
+ int ret;
+ static const unsigned channels_per_bitfield = 32;
+
+ unsigned chan = CR_CHAN(insn->chanspec);
+ const unsigned base_bitfield_channel =
+ (chan < channels_per_bitfield) ? 0 : chan;
+ unsigned int new_data[2];
+
+ memset(new_data, 0, sizeof(new_data));
+ memset(&new_insn, 0, sizeof(new_insn));
+ new_insn.insn = INSN_BITS;
+ new_insn.chanspec = base_bitfield_channel;
+ new_insn.n = 2;
+ new_insn.subdev = insn->subdev;
+
+ if (insn->insn == INSN_WRITE) {
+ if (!(s->subdev_flags & SDF_WRITABLE))
+ return -EINVAL;
+ new_data[0] = 1 << (chan - base_bitfield_channel); /* mask */
+ new_data[1] = data[0] ? (1 << (chan - base_bitfield_channel))
+ : 0; /* bits */
+ }
+
+ ret = s->insn_bits(dev, s, &new_insn, new_data);
+ if (ret < 0)
+ return ret;
+
+ if (insn->insn == INSN_READ)
+ data[0] = (new_data[1] >> (chan - base_bitfield_channel)) & 1;
+
+ return 1;
+}
+
+static int __comedi_device_postconfig_async(struct comedi_device *dev,
+ struct comedi_subdevice *s)
+{
+ struct comedi_async *async;
+ unsigned int buf_size;
+ int ret;
+
+ if ((s->subdev_flags & (SDF_CMD_READ | SDF_CMD_WRITE)) == 0) {
+ dev_warn(dev->class_dev,
+ "async subdevices must support SDF_CMD_READ or SDF_CMD_WRITE\n");
+ return -EINVAL;
+ }
+ if (!s->do_cmdtest) {
+ dev_warn(dev->class_dev,
+ "async subdevices must have a do_cmdtest() function\n");
+ return -EINVAL;
+ }
+
+ async = kzalloc(sizeof(*async), GFP_KERNEL);
+ if (!async)
+ return -ENOMEM;
+
+ init_waitqueue_head(&async->wait_head);
+ s->async = async;
+
+ async->max_bufsize = comedi_default_buf_maxsize_kb * 1024;
+ buf_size = comedi_default_buf_size_kb * 1024;
+ if (buf_size > async->max_bufsize)
+ buf_size = async->max_bufsize;
+
+ if (comedi_buf_alloc(dev, s, buf_size) < 0) {
+ dev_warn(dev->class_dev, "Buffer allocation failed\n");
+ return -ENOMEM;
+ }
+ if (s->buf_change) {
+ ret = s->buf_change(dev, s);
+ if (ret < 0)
+ return ret;
+ }
+
+ comedi_alloc_subdevice_minor(s);
+
+ return 0;
+}
+
+static int __comedi_device_postconfig(struct comedi_device *dev)
+{
+ struct comedi_subdevice *s;
+ int ret;
+ int i;
+
+ for (i = 0; i < dev->n_subdevices; i++) {
+ s = &dev->subdevices[i];
+
+ if (s->type == COMEDI_SUBD_UNUSED)
+ continue;
+
+ if (s->type == COMEDI_SUBD_DO) {
+ if (s->n_chan < 32)
+ s->io_bits = (1 << s->n_chan) - 1;
+ else
+ s->io_bits = 0xffffffff;
+ }
+
+ if (s->len_chanlist == 0)
+ s->len_chanlist = 1;
+
+ if (s->do_cmd) {
+ ret = __comedi_device_postconfig_async(dev, s);
+ if (ret)
+ return ret;
+ }
+
+ if (!s->range_table && !s->range_table_list)
+ s->range_table = &range_unknown;
+
+ if (!s->insn_read && s->insn_bits)
+ s->insn_read = insn_rw_emulate_bits;
+ if (!s->insn_write && s->insn_bits)
+ s->insn_write = insn_rw_emulate_bits;
+
+ if (!s->insn_read)
+ s->insn_read = insn_inval;
+ if (!s->insn_write)
+ s->insn_write = insn_inval;
+ if (!s->insn_bits)
+ s->insn_bits = insn_inval;
+ if (!s->insn_config)
+ s->insn_config = insn_inval;
+
+ if (!s->poll)
+ s->poll = poll_invalid;
+ }
+
+ return 0;
+}
+
+/* do a little post-config cleanup */
+static int comedi_device_postconfig(struct comedi_device *dev)
+{
+ int ret;
+
+ ret = __comedi_device_postconfig(dev);
+ if (ret < 0)
+ return ret;
+ down_write(&dev->attach_lock);
+ dev->attached = true;
+ up_write(&dev->attach_lock);
+ return 0;
+}
+
+/*
+ * Generic recognize function for drivers that register their supported
+ * board names.
+ *
+ * 'driv->board_name' points to a 'const char *' member within the
+ * zeroth element of an array of some private board information
+ * structure, say 'struct foo_board' containing a member 'const char
+ * *board_name' that is initialized to point to a board name string that
+ * is one of the candidates matched against this function's 'name'
+ * parameter.
+ *
+ * 'driv->offset' is the size of the private board information
+ * structure, say 'sizeof(struct foo_board)', and 'driv->num_names' is
+ * the length of the array of private board information structures.
+ *
+ * If one of the board names in the array of private board information
+ * structures matches the name supplied to this function, the function
+ * returns a pointer to the pointer to the board name, otherwise it
+ * returns NULL. The return value ends up in the 'board_ptr' member of
+ * a 'struct comedi_device' that the low-level comedi driver's
+ * 'attach()' hook can convert to a point to a particular element of its
+ * array of private board information structures by subtracting the
+ * offset of the member that points to the board name. (No subtraction
+ * is required if the board name pointer is the first member of the
+ * private board information structure, which is generally the case.)
+ */
+static void *comedi_recognize(struct comedi_driver *driv, const char *name)
+{
+ char **name_ptr = (char **)driv->board_name;
+ int i;
+
+ for (i = 0; i < driv->num_names; i++) {
+ if (strcmp(*name_ptr, name) == 0)
+ return name_ptr;
+ name_ptr = (void *)name_ptr + driv->offset;
+ }
+
+ return NULL;
+}
+
+static void comedi_report_boards(struct comedi_driver *driv)
+{
+ unsigned int i;
+ const char *const *name_ptr;
+
+ pr_info("comedi: valid board names for %s driver are:\n",
+ driv->driver_name);
+
+ name_ptr = driv->board_name;
+ for (i = 0; i < driv->num_names; i++) {
+ pr_info(" %s\n", *name_ptr);
+ name_ptr = (const char **)((char *)name_ptr + driv->offset);
+ }
+
+ if (driv->num_names == 0)
+ pr_info(" %s\n", driv->driver_name);
+}
+
+/**
+ * comedi_load_firmware() - Request and load firmware for a device.
+ * @dev: comedi_device struct
+ * @hw_device: device struct for the comedi_device
+ * @name: the name of the firmware image
+ * @cb: callback to the upload the firmware image
+ * @context: private context from the driver
+ */
+int comedi_load_firmware(struct comedi_device *dev,
+ struct device *device,
+ const char *name,
+ int (*cb)(struct comedi_device *dev,
+ const u8 *data, size_t size,
+ unsigned long context),
+ unsigned long context)
+{
+ const struct firmware *fw;
+ int ret;
+
+ if (!cb)
+ return -EINVAL;
+
+ ret = maybe_reject_firmware(&fw, name, device);
+ if (ret == 0) {
+ ret = cb(dev, fw->data, fw->size, context);
+ release_firmware(fw);
+ }
+
+ return ret < 0 ? ret : 0;
+}
+EXPORT_SYMBOL_GPL(comedi_load_firmware);
+
+/**
+ * __comedi_request_region() - Request an I/O reqion for a legacy driver.
+ * @dev: comedi_device struct
+ * @start: base address of the I/O reqion
+ * @len: length of the I/O region
+ */
+int __comedi_request_region(struct comedi_device *dev,
+ unsigned long start, unsigned long len)
+{
+ if (!start) {
+ dev_warn(dev->class_dev,
+ "%s: a I/O base address must be specified\n",
+ dev->board_name);
+ return -EINVAL;
+ }
+
+ if (!request_region(start, len, dev->board_name)) {
+ dev_warn(dev->class_dev, "%s: I/O port conflict (%#lx,%lu)\n",
+ dev->board_name, start, len);
+ return -EIO;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(__comedi_request_region);
+
+/**
+ * comedi_request_region() - Request an I/O reqion for a legacy driver.
+ * @dev: comedi_device struct
+ * @start: base address of the I/O reqion
+ * @len: length of the I/O region
+ */
+int comedi_request_region(struct comedi_device *dev,
+ unsigned long start, unsigned long len)
+{
+ int ret;
+
+ ret = __comedi_request_region(dev, start, len);
+ if (ret == 0) {
+ dev->iobase = start;
+ dev->iolen = len;
+ }
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(comedi_request_region);
+
+/**
+ * comedi_legacy_detach() - A generic (*detach) function for legacy drivers.
+ * @dev: comedi_device struct
+ */
+void comedi_legacy_detach(struct comedi_device *dev)
+{
+ if (dev->irq) {
+ free_irq(dev->irq, dev);
+ dev->irq = 0;
+ }
+ if (dev->iobase && dev->iolen) {
+ release_region(dev->iobase, dev->iolen);
+ dev->iobase = 0;
+ dev->iolen = 0;
+ }
+}
+EXPORT_SYMBOL_GPL(comedi_legacy_detach);
+
+int comedi_device_attach(struct comedi_device *dev, struct comedi_devconfig *it)
+{
+ struct comedi_driver *driv;
+ int ret;
+
+ if (dev->attached)
+ return -EBUSY;
+
+ mutex_lock(&comedi_drivers_list_lock);
+ for (driv = comedi_drivers; driv; driv = driv->next) {
+ if (!try_module_get(driv->module))
+ continue;
+ if (driv->num_names) {
+ dev->board_ptr = comedi_recognize(driv, it->board_name);
+ if (dev->board_ptr)
+ break;
+ } else if (strcmp(driv->driver_name, it->board_name) == 0) {
+ break;
+ }
+ module_put(driv->module);
+ }
+ if (!driv) {
+ /* recognize has failed if we get here */
+ /* report valid board names before returning error */
+ for (driv = comedi_drivers; driv; driv = driv->next) {
+ if (!try_module_get(driv->module))
+ continue;
+ comedi_report_boards(driv);
+ module_put(driv->module);
+ }
+ ret = -EIO;
+ goto out;
+ }
+ if (!driv->attach) {
+ /* driver does not support manual configuration */
+ dev_warn(dev->class_dev,
+ "driver '%s' does not support attach using comedi_config\n",
+ driv->driver_name);
+ module_put(driv->module);
+ ret = -ENOSYS;
+ goto out;
+ }
+ dev->driver = driv;
+ dev->board_name = dev->board_ptr ? *(const char **)dev->board_ptr
+ : dev->driver->driver_name;
+ ret = driv->attach(dev, it);
+ if (ret >= 0)
+ ret = comedi_device_postconfig(dev);
+ if (ret < 0) {
+ comedi_device_detach(dev);
+ module_put(driv->module);
+ }
+ /* On success, the driver module count has been incremented. */
+out:
+ mutex_unlock(&comedi_drivers_list_lock);
+ return ret;
+}
+
+int comedi_auto_config(struct device *hardware_device,
+ struct comedi_driver *driver, unsigned long context)
+{
+ struct comedi_device *dev;
+ int ret;
+
+ if (!hardware_device) {
+ pr_warn("BUG! comedi_auto_config called with NULL hardware_device\n");
+ return -EINVAL;
+ }
+ if (!driver) {
+ dev_warn(hardware_device,
+ "BUG! comedi_auto_config called with NULL comedi driver\n");
+ return -EINVAL;
+ }
+
+ if (!driver->auto_attach) {
+ dev_warn(hardware_device,
+ "BUG! comedi driver '%s' has no auto_attach handler\n",
+ driver->driver_name);
+ return -EINVAL;
+ }
+
+ dev = comedi_alloc_board_minor(hardware_device);
+ if (IS_ERR(dev)) {
+ dev_warn(hardware_device,
+ "driver '%s' could not create device.\n",
+ driver->driver_name);
+ return PTR_ERR(dev);
+ }
+ /* Note: comedi_alloc_board_minor() locked dev->mutex. */
+
+ dev->driver = driver;
+ dev->board_name = dev->driver->driver_name;
+ ret = driver->auto_attach(dev, context);
+ if (ret >= 0)
+ ret = comedi_device_postconfig(dev);
+ mutex_unlock(&dev->mutex);
+
+ if (ret < 0) {
+ dev_warn(hardware_device,
+ "driver '%s' failed to auto-configure device.\n",
+ driver->driver_name);
+ comedi_release_hardware_device(hardware_device);
+ } else {
+ /*
+ * class_dev should be set properly here
+ * after a successful auto config
+ */
+ dev_info(dev->class_dev,
+ "driver '%s' has successfully auto-configured '%s'.\n",
+ driver->driver_name, dev->board_name);
+ }
+ return ret;
+}
+EXPORT_SYMBOL_GPL(comedi_auto_config);
+
+void comedi_auto_unconfig(struct device *hardware_device)
+{
+ if (!hardware_device)
+ return;
+ comedi_release_hardware_device(hardware_device);
+}
+EXPORT_SYMBOL_GPL(comedi_auto_unconfig);
+
+int comedi_driver_register(struct comedi_driver *driver)
+{
+ mutex_lock(&comedi_drivers_list_lock);
+ driver->next = comedi_drivers;
+ comedi_drivers = driver;
+ mutex_unlock(&comedi_drivers_list_lock);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(comedi_driver_register);
+
+void comedi_driver_unregister(struct comedi_driver *driver)
+{
+ struct comedi_driver *prev;
+ int i;
+
+ /* unlink the driver */
+ mutex_lock(&comedi_drivers_list_lock);
+ if (comedi_drivers == driver) {
+ comedi_drivers = driver->next;
+ } else {
+ for (prev = comedi_drivers; prev->next; prev = prev->next) {
+ if (prev->next == driver) {
+ prev->next = driver->next;
+ break;
+ }
+ }
+ }
+ mutex_unlock(&comedi_drivers_list_lock);
+
+ /* check for devices using this driver */
+ for (i = 0; i < COMEDI_NUM_BOARD_MINORS; i++) {
+ struct comedi_device *dev = comedi_dev_get_from_minor(i);
+
+ if (!dev)
+ continue;
+
+ mutex_lock(&dev->mutex);
+ if (dev->attached && dev->driver == driver) {
+ if (dev->use_count)
+ dev_warn(dev->class_dev,
+ "BUG! detaching device with use_count=%d\n",
+ dev->use_count);
+ comedi_device_detach(dev);
+ }
+ mutex_unlock(&dev->mutex);
+ comedi_dev_put(dev);
+ }
+}
+EXPORT_SYMBOL_GPL(comedi_driver_unregister);
diff --git a/drivers/staging/comedi/drivers/8255.c b/drivers/staging/comedi/drivers/8255.c
new file mode 100644
index 000000000..ba89321df
--- /dev/null
+++ b/drivers/staging/comedi/drivers/8255.c
@@ -0,0 +1,342 @@
+/*
+ * comedi/drivers/8255.c
+ * Driver for 8255
+ *
+ * COMEDI - Linux Control and Measurement Device Interface
+ * Copyright (C) 1998 David A. Schleef <ds@schleef.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+/*
+ * Driver: 8255
+ * Description: generic 8255 support
+ * Devices: [standard] 8255 (8255)
+ * Author: ds
+ * Status: works
+ * Updated: Fri, 7 Jun 2002 12:56:45 -0700
+ *
+ * The classic in digital I/O. The 8255 appears in Comedi as a single
+ * digital I/O subdevice with 24 channels. The channel 0 corresponds
+ * to the 8255's port A, bit 0; channel 23 corresponds to port C, bit
+ * 7. Direction configuration is done in blocks, with channels 0-7,
+ * 8-15, 16-19, and 20-23 making up the 4 blocks. The only 8255 mode
+ * supported is mode 0.
+ *
+ * You should enable compilation this driver if you plan to use a board
+ * that has an 8255 chip. For multifunction boards, the main driver will
+ * configure the 8255 subdevice automatically.
+ *
+ * This driver also works independently with ISA and PCI cards that
+ * directly map the 8255 registers to I/O ports, including cards with
+ * multiple 8255 chips. To configure the driver for such a card, the
+ * option list should be a list of the I/O port bases for each of the
+ * 8255 chips. For example,
+ *
+ * comedi_config /dev/comedi0 8255 0x200,0x204,0x208,0x20c
+ *
+ * Note that most PCI 8255 boards do NOT work with this driver, and
+ * need a separate driver as a wrapper. For those that do work, the
+ * I/O port base address can be found in the output of 'lspci -v'.
+ */
+
+#include <linux/module.h>
+#include "../comedidev.h"
+
+#include "8255.h"
+
+struct subdev_8255_private {
+ unsigned long regbase;
+ int (*io)(struct comedi_device *, int, int, int, unsigned long);
+};
+
+static int subdev_8255_io(struct comedi_device *dev,
+ int dir, int port, int data, unsigned long regbase)
+{
+ if (dir) {
+ outb(data, dev->iobase + regbase + port);
+ return 0;
+ }
+ return inb(dev->iobase + regbase + port);
+}
+
+static int subdev_8255_mmio(struct comedi_device *dev,
+ int dir, int port, int data, unsigned long regbase)
+{
+ if (dir) {
+ writeb(data, dev->mmio + regbase + port);
+ return 0;
+ }
+ return readb(dev->mmio + regbase + port);
+}
+
+static int subdev_8255_insn(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ struct subdev_8255_private *spriv = s->private;
+ unsigned long regbase = spriv->regbase;
+ unsigned int mask;
+ unsigned int v;
+
+ mask = comedi_dio_update_state(s, data);
+ if (mask) {
+ if (mask & 0xff)
+ spriv->io(dev, 1, I8255_DATA_A_REG,
+ s->state & 0xff, regbase);
+ if (mask & 0xff00)
+ spriv->io(dev, 1, I8255_DATA_B_REG,
+ (s->state >> 8) & 0xff, regbase);
+ if (mask & 0xff0000)
+ spriv->io(dev, 1, I8255_DATA_C_REG,
+ (s->state >> 16) & 0xff, regbase);
+ }
+
+ v = spriv->io(dev, 0, I8255_DATA_A_REG, 0, regbase);
+ v |= (spriv->io(dev, 0, I8255_DATA_B_REG, 0, regbase) << 8);
+ v |= (spriv->io(dev, 0, I8255_DATA_C_REG, 0, regbase) << 16);
+
+ data[1] = v;
+
+ return insn->n;
+}
+
+static void subdev_8255_do_config(struct comedi_device *dev,
+ struct comedi_subdevice *s)
+{
+ struct subdev_8255_private *spriv = s->private;
+ unsigned long regbase = spriv->regbase;
+ int config;
+
+ config = I8255_CTRL_CW;
+ /* 1 in io_bits indicates output, 1 in config indicates input */
+ if (!(s->io_bits & 0x0000ff))
+ config |= I8255_CTRL_A_IO;
+ if (!(s->io_bits & 0x00ff00))
+ config |= I8255_CTRL_B_IO;
+ if (!(s->io_bits & 0x0f0000))
+ config |= I8255_CTRL_C_LO_IO;
+ if (!(s->io_bits & 0xf00000))
+ config |= I8255_CTRL_C_HI_IO;
+
+ spriv->io(dev, 1, I8255_CTRL_REG, config, regbase);
+}
+
+static int subdev_8255_insn_config(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ unsigned int chan = CR_CHAN(insn->chanspec);
+ unsigned int mask;
+ int ret;
+
+ if (chan < 8)
+ mask = 0x0000ff;
+ else if (chan < 16)
+ mask = 0x00ff00;
+ else if (chan < 20)
+ mask = 0x0f0000;
+ else
+ mask = 0xf00000;
+
+ ret = comedi_dio_insn_config(dev, s, insn, data, mask);
+ if (ret)
+ return ret;
+
+ subdev_8255_do_config(dev, s);
+
+ return insn->n;
+}
+
+static int __subdev_8255_init(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ int (*io)(struct comedi_device *,
+ int, int, int, unsigned long),
+ unsigned long regbase,
+ bool is_mmio)
+{
+ struct subdev_8255_private *spriv;
+
+ spriv = comedi_alloc_spriv(s, sizeof(*spriv));
+ if (!spriv)
+ return -ENOMEM;
+
+ if (io)
+ spriv->io = io;
+ else if (is_mmio)
+ spriv->io = subdev_8255_mmio;
+ else
+ spriv->io = subdev_8255_io;
+ spriv->regbase = regbase;
+
+ s->type = COMEDI_SUBD_DIO;
+ s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
+ s->n_chan = 24;
+ s->range_table = &range_digital;
+ s->maxdata = 1;
+ s->insn_bits = subdev_8255_insn;
+ s->insn_config = subdev_8255_insn_config;
+
+ subdev_8255_do_config(dev, s);
+
+ return 0;
+}
+
+/**
+ * subdev_8255_init - initialize DIO subdevice for driving I/O mapped 8255
+ * @dev: comedi device owning subdevice
+ * @s: comedi subdevice to initialize
+ * @io: (optional) register I/O call-back function
+ * @regbase: offset of 8255 registers from dev->iobase, or call-back context
+ *
+ * Initializes a comedi subdevice as a DIO subdevice driving an 8255 chip.
+ *
+ * If the optional I/O call-back function is provided, its prototype is of
+ * the following form:
+ *
+ * int my_8255_callback(struct comedi_device *dev,
+ * struct comedi_subdevice *s, int dir, int port,
+ * int data, unsigned long regbase);
+ *
+ * where 'dev', 's', and 'regbase' match the values passed to this function,
+ * 'port' is the 8255 port number 0 to 3 (including the control port), 'dir'
+ * is the direction (0 for read, 1 for write) and 'data' is the value to be
+ * written. It should return 0 if writing or the value read if reading.
+ *
+ * If the optional I/O call-back function is not provided, an internal
+ * call-back function is used which uses consecutive I/O port addresses
+ * starting at dev->iobase + regbase.
+ *
+ * Return: -ENOMEM if failed to allocate memory, zero on success.
+ */
+int subdev_8255_init(struct comedi_device *dev, struct comedi_subdevice *s,
+ int (*io)(struct comedi_device *,
+ int, int, int, unsigned long),
+ unsigned long regbase)
+{
+ return __subdev_8255_init(dev, s, io, regbase, false);
+}
+EXPORT_SYMBOL_GPL(subdev_8255_init);
+
+/**
+ * subdev_8255_mm_init - initialize DIO subdevice for driving mmio-mapped 8255
+ * @dev: comedi device owning subdevice
+ * @s: comedi subdevice to initialize
+ * @io: (optional) register I/O call-back function
+ * @regbase: offset of 8255 registers from dev->mmio, or call-back context
+ *
+ * Initializes a comedi subdevice as a DIO subdevice driving an 8255 chip.
+ *
+ * If the optional I/O call-back function is provided, its prototype is of
+ * the following form:
+ *
+ * int my_8255_callback(struct comedi_device *dev,
+ * struct comedi_subdevice *s, int dir, int port,
+ * int data, unsigned long regbase);
+ *
+ * where 'dev', 's', and 'regbase' match the values passed to this function,
+ * 'port' is the 8255 port number 0 to 3 (including the control port), 'dir'
+ * is the direction (0 for read, 1 for write) and 'data' is the value to be
+ * written. It should return 0 if writing or the value read if reading.
+ *
+ * If the optional I/O call-back function is not provided, an internal
+ * call-back function is used which uses consecutive MMIO virtual addresses
+ * starting at dev->mmio + regbase.
+ *
+ * Return: -ENOMEM if failed to allocate memory, zero on success.
+ */
+int subdev_8255_mm_init(struct comedi_device *dev, struct comedi_subdevice *s,
+ int (*io)(struct comedi_device *,
+ int, int, int, unsigned long),
+ unsigned long regbase)
+{
+ return __subdev_8255_init(dev, s, io, regbase, true);
+}
+EXPORT_SYMBOL_GPL(subdev_8255_mm_init);
+
+/*
+ * Start of the 8255 standalone device
+ */
+
+static int dev_8255_attach(struct comedi_device *dev,
+ struct comedi_devconfig *it)
+{
+ struct comedi_subdevice *s;
+ unsigned long iobase;
+ int ret;
+ int i;
+
+ for (i = 0; i < COMEDI_NDEVCONFOPTS; i++) {
+ iobase = it->options[i];
+ if (!iobase)
+ break;
+ }
+ if (i == 0) {
+ dev_warn(dev->class_dev, "no devices specified\n");
+ return -EINVAL;
+ }
+
+ ret = comedi_alloc_subdevices(dev, i);
+ if (ret)
+ return ret;
+
+ for (i = 0; i < dev->n_subdevices; i++) {
+ s = &dev->subdevices[i];
+ iobase = it->options[i];
+
+ /*
+ * __comedi_request_region() does not set dev->iobase.
+ *
+ * For 8255 devices that are manually attached using
+ * comedi_config, the 'iobase' is the actual I/O port
+ * base address of the chip.
+ */
+ ret = __comedi_request_region(dev, iobase, I8255_SIZE);
+ if (ret) {
+ s->type = COMEDI_SUBD_UNUSED;
+ } else {
+ ret = subdev_8255_init(dev, s, NULL, iobase);
+ if (ret)
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static void dev_8255_detach(struct comedi_device *dev)
+{
+ struct comedi_subdevice *s;
+ struct subdev_8255_private *spriv;
+ int i;
+
+ for (i = 0; i < dev->n_subdevices; i++) {
+ s = &dev->subdevices[i];
+ if (s->type != COMEDI_SUBD_UNUSED) {
+ spriv = s->private;
+ release_region(spriv->regbase, I8255_SIZE);
+ }
+ }
+}
+
+static struct comedi_driver dev_8255_driver = {
+ .driver_name = "8255",
+ .module = THIS_MODULE,
+ .attach = dev_8255_attach,
+ .detach = dev_8255_detach,
+};
+module_comedi_driver(dev_8255_driver);
+
+MODULE_AUTHOR("Comedi http://www.comedi.org");
+MODULE_DESCRIPTION("Comedi low-level driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/comedi/drivers/8255.h b/drivers/staging/comedi/drivers/8255.h
new file mode 100644
index 000000000..934b940eb
--- /dev/null
+++ b/drivers/staging/comedi/drivers/8255.h
@@ -0,0 +1,48 @@
+/*
+ * module/8255.h
+ * Header file for 8255
+ *
+ * COMEDI - Linux Control and Measurement Device Interface
+ * Copyright (C) 1998 David A. Schleef <ds@schleef.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _8255_H
+#define _8255_H
+
+#include "../comedidev.h"
+
+#define I8255_SIZE 0x04
+
+#define I8255_DATA_A_REG 0x00
+#define I8255_DATA_B_REG 0x01
+#define I8255_DATA_C_REG 0x02
+#define I8255_CTRL_REG 0x03
+#define I8255_CTRL_C_LO_IO (1 << 0)
+#define I8255_CTRL_B_IO (1 << 1)
+#define I8255_CTRL_B_MODE (1 << 2)
+#define I8255_CTRL_C_HI_IO (1 << 3)
+#define I8255_CTRL_A_IO (1 << 4)
+#define I8255_CTRL_A_MODE(x) ((x) << 5)
+#define I8255_CTRL_CW (1 << 7)
+
+int subdev_8255_init(struct comedi_device *, struct comedi_subdevice *,
+ int (*io)(struct comedi_device *,
+ int, int, int, unsigned long),
+ unsigned long regbase);
+
+int subdev_8255_mm_init(struct comedi_device *, struct comedi_subdevice *,
+ int (*io)(struct comedi_device *,
+ int, int, int, unsigned long),
+ unsigned long regbase);
+
+#endif
diff --git a/drivers/staging/comedi/drivers/8255_pci.c b/drivers/staging/comedi/drivers/8255_pci.c
new file mode 100644
index 000000000..bb9854b56
--- /dev/null
+++ b/drivers/staging/comedi/drivers/8255_pci.c
@@ -0,0 +1,304 @@
+/*
+ * COMEDI driver for generic PCI based 8255 digital i/o boards
+ * Copyright (C) 2012 H Hartley Sweeten <hsweeten@visionengravers.com>
+ *
+ * Based on the tested adl_pci7296 driver written by:
+ * Jon Grierson <jd@renko.co.uk>
+ * and the experimental cb_pcidio driver written by:
+ * Yoshiya Matsuzaka
+ *
+ * COMEDI - Linux Control and Measurement Device Interface
+ * Copyright (C) 2000 David A. Schleef <ds@schleef.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+/*
+ * Driver: 8255_pci
+ * Description: Generic PCI based 8255 Digital I/O boards
+ * Devices: [ADLink] PCI-7224 (adl_pci-7224), PCI-7248 (adl_pci-7248),
+ * PCI-7296 (adl_pci-7296),
+ * [Measurement Computing] PCI-DIO24 (cb_pci-dio24),
+ * PCI-DIO24H (cb_pci-dio24h), PCI-DIO48H (cb_pci-dio48h),
+ * PCI-DIO96H (cb_pci-dio96h),
+ * [National Instruments] PCI-DIO-96 (ni_pci-dio-96),
+ * PCI-DIO-96B (ni_pci-dio-96b), PXI-6508 (ni_pxi-6508),
+ * PCI-6503 (ni_pci-6503), PCI-6503B (ni_pci-6503b),
+ * PCI-6503X (ni_pci-6503x), PXI-6503 (ni_pxi-6503)
+ * Author: H Hartley Sweeten <hsweeten@visionengravers.com>
+ * Updated: Wed, 12 Sep 2012 11:52:01 -0700
+ * Status: untested
+ *
+ * These boards have one or more 8255 digital I/O chips, each of which
+ * is supported as a separate 24-channel DIO subdevice.
+ *
+ * Boards with 24 DIO channels (1 DIO subdevice):
+ *
+ * PCI-7224, PCI-DIO24, PCI-DIO24H, PCI-6503, PCI-6503B, PCI-6503X,
+ * PXI-6503
+ *
+ * Boards with 48 DIO channels (2 DIO subdevices):
+ *
+ * PCI-7248, PCI-DIO48H
+ *
+ * Boards with 96 DIO channels (4 DIO subdevices):
+ *
+ * PCI-7296, PCI-DIO96H, PCI-DIO-96, PCI-DIO-96B, PXI-6508
+ *
+ * Some of these boards also have an 8254 programmable timer/counter
+ * chip. This chip is not currently supported by this driver.
+ *
+ * Interrupt support for these boards is also not currently supported.
+ *
+ * Configuration Options: not applicable, uses PCI auto config.
+ */
+
+#include <linux/module.h>
+
+#include "../comedi_pci.h"
+
+#include "8255.h"
+
+enum pci_8255_boardid {
+ BOARD_ADLINK_PCI7224,
+ BOARD_ADLINK_PCI7248,
+ BOARD_ADLINK_PCI7296,
+ BOARD_CB_PCIDIO24,
+ BOARD_CB_PCIDIO24H,
+ BOARD_CB_PCIDIO48H_OLD,
+ BOARD_CB_PCIDIO48H_NEW,
+ BOARD_CB_PCIDIO96H,
+ BOARD_NI_PCIDIO96,
+ BOARD_NI_PCIDIO96B,
+ BOARD_NI_PXI6508,
+ BOARD_NI_PCI6503,
+ BOARD_NI_PCI6503B,
+ BOARD_NI_PCI6503X,
+ BOARD_NI_PXI_6503,
+};
+
+struct pci_8255_boardinfo {
+ const char *name;
+ int dio_badr;
+ int n_8255;
+ unsigned int has_mite:1;
+};
+
+static const struct pci_8255_boardinfo pci_8255_boards[] = {
+ [BOARD_ADLINK_PCI7224] = {
+ .name = "adl_pci-7224",
+ .dio_badr = 2,
+ .n_8255 = 1,
+ },
+ [BOARD_ADLINK_PCI7248] = {
+ .name = "adl_pci-7248",
+ .dio_badr = 2,
+ .n_8255 = 2,
+ },
+ [BOARD_ADLINK_PCI7296] = {
+ .name = "adl_pci-7296",
+ .dio_badr = 2,
+ .n_8255 = 4,
+ },
+ [BOARD_CB_PCIDIO24] = {
+ .name = "cb_pci-dio24",
+ .dio_badr = 2,
+ .n_8255 = 1,
+ },
+ [BOARD_CB_PCIDIO24H] = {
+ .name = "cb_pci-dio24h",
+ .dio_badr = 2,
+ .n_8255 = 1,
+ },
+ [BOARD_CB_PCIDIO48H_OLD] = {
+ .name = "cb_pci-dio48h",
+ .dio_badr = 1,
+ .n_8255 = 2,
+ },
+ [BOARD_CB_PCIDIO48H_NEW] = {
+ .name = "cb_pci-dio48h",
+ .dio_badr = 2,
+ .n_8255 = 2,
+ },
+ [BOARD_CB_PCIDIO96H] = {
+ .name = "cb_pci-dio96h",
+ .dio_badr = 2,
+ .n_8255 = 4,
+ },
+ [BOARD_NI_PCIDIO96] = {
+ .name = "ni_pci-dio-96",
+ .dio_badr = 1,
+ .n_8255 = 4,
+ .has_mite = 1,
+ },
+ [BOARD_NI_PCIDIO96B] = {
+ .name = "ni_pci-dio-96b",
+ .dio_badr = 1,
+ .n_8255 = 4,
+ .has_mite = 1,
+ },
+ [BOARD_NI_PXI6508] = {
+ .name = "ni_pxi-6508",
+ .dio_badr = 1,
+ .n_8255 = 4,
+ .has_mite = 1,
+ },
+ [BOARD_NI_PCI6503] = {
+ .name = "ni_pci-6503",
+ .dio_badr = 1,
+ .n_8255 = 1,
+ .has_mite = 1,
+ },
+ [BOARD_NI_PCI6503B] = {
+ .name = "ni_pci-6503b",
+ .dio_badr = 1,
+ .n_8255 = 1,
+ .has_mite = 1,
+ },
+ [BOARD_NI_PCI6503X] = {
+ .name = "ni_pci-6503x",
+ .dio_badr = 1,
+ .n_8255 = 1,
+ .has_mite = 1,
+ },
+ [BOARD_NI_PXI_6503] = {
+ .name = "ni_pxi-6503",
+ .dio_badr = 1,
+ .n_8255 = 1,
+ .has_mite = 1,
+ },
+};
+
+/* ripped from mite.h and mite_setup2() to avoid mite dependency */
+#define MITE_IODWBSR 0xc0 /* IO Device Window Base Size Register */
+#define WENAB (1 << 7) /* window enable */
+
+static int pci_8255_mite_init(struct pci_dev *pcidev)
+{
+ void __iomem *mite_base;
+ u32 main_phys_addr;
+
+ /* ioremap the MITE registers (BAR 0) temporarily */
+ mite_base = pci_ioremap_bar(pcidev, 0);
+ if (!mite_base)
+ return -ENOMEM;
+
+ /* set data window to main registers (BAR 1) */
+ main_phys_addr = pci_resource_start(pcidev, 1);
+ writel(main_phys_addr | WENAB, mite_base + MITE_IODWBSR);
+
+ /* finished with MITE registers */
+ iounmap(mite_base);
+ return 0;
+}
+
+static int pci_8255_auto_attach(struct comedi_device *dev,
+ unsigned long context)
+{
+ struct pci_dev *pcidev = comedi_to_pci_dev(dev);
+ const struct pci_8255_boardinfo *board = NULL;
+ struct comedi_subdevice *s;
+ int ret;
+ int i;
+
+ if (context < ARRAY_SIZE(pci_8255_boards))
+ board = &pci_8255_boards[context];
+ if (!board)
+ return -ENODEV;
+ dev->board_ptr = board;
+ dev->board_name = board->name;
+
+ ret = comedi_pci_enable(dev);
+ if (ret)
+ return ret;
+
+ if (board->has_mite) {
+ ret = pci_8255_mite_init(pcidev);
+ if (ret)
+ return ret;
+ }
+
+ if ((pci_resource_flags(pcidev, board->dio_badr) & IORESOURCE_MEM)) {
+ dev->mmio = pci_ioremap_bar(pcidev, board->dio_badr);
+ if (!dev->mmio)
+ return -ENOMEM;
+ } else {
+ dev->iobase = pci_resource_start(pcidev, board->dio_badr);
+ }
+
+ /*
+ * One, two, or four subdevices are setup by this driver depending
+ * on the number of channels provided by the board. Each subdevice
+ * has 24 channels supported by the 8255 module.
+ */
+ ret = comedi_alloc_subdevices(dev, board->n_8255);
+ if (ret)
+ return ret;
+
+ for (i = 0; i < board->n_8255; i++) {
+ s = &dev->subdevices[i];
+ if (dev->mmio)
+ ret = subdev_8255_mm_init(dev, s, NULL, i * I8255_SIZE);
+ else
+ ret = subdev_8255_init(dev, s, NULL, i * I8255_SIZE);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static struct comedi_driver pci_8255_driver = {
+ .driver_name = "8255_pci",
+ .module = THIS_MODULE,
+ .auto_attach = pci_8255_auto_attach,
+ .detach = comedi_pci_detach,
+};
+
+static int pci_8255_pci_probe(struct pci_dev *dev,
+ const struct pci_device_id *id)
+{
+ return comedi_pci_auto_config(dev, &pci_8255_driver, id->driver_data);
+}
+
+static const struct pci_device_id pci_8255_pci_table[] = {
+ { PCI_VDEVICE(ADLINK, 0x7224), BOARD_ADLINK_PCI7224 },
+ { PCI_VDEVICE(ADLINK, 0x7248), BOARD_ADLINK_PCI7248 },
+ { PCI_VDEVICE(ADLINK, 0x7296), BOARD_ADLINK_PCI7296 },
+ { PCI_VDEVICE(CB, 0x0028), BOARD_CB_PCIDIO24 },
+ { PCI_VDEVICE(CB, 0x0014), BOARD_CB_PCIDIO24H },
+ { PCI_DEVICE_SUB(PCI_VENDOR_ID_CB, 0x000b, 0x0000, 0x0000),
+ .driver_data = BOARD_CB_PCIDIO48H_OLD },
+ { PCI_DEVICE_SUB(PCI_VENDOR_ID_CB, 0x000b, PCI_VENDOR_ID_CB, 0x000b),
+ .driver_data = BOARD_CB_PCIDIO48H_NEW },
+ { PCI_VDEVICE(CB, 0x0017), BOARD_CB_PCIDIO96H },
+ { PCI_VDEVICE(NI, 0x0160), BOARD_NI_PCIDIO96 },
+ { PCI_VDEVICE(NI, 0x1630), BOARD_NI_PCIDIO96B },
+ { PCI_VDEVICE(NI, 0x13c0), BOARD_NI_PXI6508 },
+ { PCI_VDEVICE(NI, 0x0400), BOARD_NI_PCI6503 },
+ { PCI_VDEVICE(NI, 0x1250), BOARD_NI_PCI6503B },
+ { PCI_VDEVICE(NI, 0x17d0), BOARD_NI_PCI6503X },
+ { PCI_VDEVICE(NI, 0x1800), BOARD_NI_PXI_6503 },
+ { 0 }
+};
+MODULE_DEVICE_TABLE(pci, pci_8255_pci_table);
+
+static struct pci_driver pci_8255_pci_driver = {
+ .name = "8255_pci",
+ .id_table = pci_8255_pci_table,
+ .probe = pci_8255_pci_probe,
+ .remove = comedi_pci_auto_unconfig,
+};
+module_comedi_pci_driver(pci_8255_driver, pci_8255_pci_driver);
+
+MODULE_DESCRIPTION("COMEDI - Generic PCI based 8255 Digital I/O boards");
+MODULE_AUTHOR("Comedi http://www.comedi.org");
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/comedi/drivers/Makefile b/drivers/staging/comedi/drivers/Makefile
new file mode 100644
index 000000000..d6d834006
--- /dev/null
+++ b/drivers/staging/comedi/drivers/Makefile
@@ -0,0 +1,145 @@
+# Makefile for individual comedi drivers
+#
+ccflags-$(CONFIG_COMEDI_DEBUG) := -DDEBUG
+
+# Comedi "helper" modules
+obj-$(CONFIG_COMEDI_8254) += comedi_8254.o
+obj-$(CONFIG_COMEDI_ISADMA) += comedi_isadma.o
+
+# Comedi misc drivers
+obj-$(CONFIG_COMEDI_BOND) += comedi_bond.o
+obj-$(CONFIG_COMEDI_TEST) += comedi_test.o
+obj-$(CONFIG_COMEDI_PARPORT) += comedi_parport.o
+obj-$(CONFIG_COMEDI_SERIAL2002) += serial2002.o
+
+# Comedi ISA drivers
+obj-$(CONFIG_COMEDI_AMPLC_DIO200_ISA) += amplc_dio200.o
+obj-$(CONFIG_COMEDI_AMPLC_PC236_ISA) += amplc_pc236.o
+obj-$(CONFIG_COMEDI_AMPLC_PC263_ISA) += amplc_pc263.o
+obj-$(CONFIG_COMEDI_PCL711) += pcl711.o
+obj-$(CONFIG_COMEDI_PCL724) += pcl724.o
+obj-$(CONFIG_COMEDI_PCL726) += pcl726.o
+obj-$(CONFIG_COMEDI_PCL730) += pcl730.o
+obj-$(CONFIG_COMEDI_PCL812) += pcl812.o
+obj-$(CONFIG_COMEDI_PCL816) += pcl816.o
+obj-$(CONFIG_COMEDI_PCL818) += pcl818.o
+obj-$(CONFIG_COMEDI_PCM3724) += pcm3724.o
+obj-$(CONFIG_COMEDI_RTI800) += rti800.o
+obj-$(CONFIG_COMEDI_RTI802) += rti802.o
+obj-$(CONFIG_COMEDI_DAC02) += dac02.o
+obj-$(CONFIG_COMEDI_DAS16M1) += das16m1.o
+obj-$(CONFIG_COMEDI_DAS08_ISA) += das08_isa.o
+obj-$(CONFIG_COMEDI_DAS16) += das16.o
+obj-$(CONFIG_COMEDI_DAS800) += das800.o
+obj-$(CONFIG_COMEDI_DAS1800) += das1800.o
+obj-$(CONFIG_COMEDI_DAS6402) += das6402.o
+obj-$(CONFIG_COMEDI_DT2801) += dt2801.o
+obj-$(CONFIG_COMEDI_DT2811) += dt2811.o
+obj-$(CONFIG_COMEDI_DT2814) += dt2814.o
+obj-$(CONFIG_COMEDI_DT2815) += dt2815.o
+obj-$(CONFIG_COMEDI_DT2817) += dt2817.o
+obj-$(CONFIG_COMEDI_DT282X) += dt282x.o
+obj-$(CONFIG_COMEDI_DMM32AT) += dmm32at.o
+obj-$(CONFIG_COMEDI_FL512) += fl512.o
+obj-$(CONFIG_COMEDI_AIO_AIO12_8) += aio_aio12_8.o
+obj-$(CONFIG_COMEDI_AIO_IIRO_16) += aio_iiro_16.o
+obj-$(CONFIG_COMEDI_II_PCI20KC) += ii_pci20kc.o
+obj-$(CONFIG_COMEDI_C6XDIGIO) += c6xdigio.o
+obj-$(CONFIG_COMEDI_MPC624) += mpc624.o
+obj-$(CONFIG_COMEDI_ADQ12B) += adq12b.o
+obj-$(CONFIG_COMEDI_NI_AT_A2150) += ni_at_a2150.o
+obj-$(CONFIG_COMEDI_NI_AT_AO) += ni_at_ao.o
+obj-$(CONFIG_COMEDI_NI_ATMIO) += ni_atmio.o
+obj-$(CONFIG_COMEDI_NI_ATMIO16D) += ni_atmio16d.o
+obj-$(CONFIG_COMEDI_NI_LABPC_ISA) += ni_labpc.o
+obj-$(CONFIG_COMEDI_PCMAD) += pcmad.o
+obj-$(CONFIG_COMEDI_PCMDA12) += pcmda12.o
+obj-$(CONFIG_COMEDI_PCMMIO) += pcmmio.o
+obj-$(CONFIG_COMEDI_PCMUIO) += pcmuio.o
+obj-$(CONFIG_COMEDI_MULTIQ3) += multiq3.o
+obj-$(CONFIG_COMEDI_S526) += s526.o
+
+# Comedi PCI drivers
+obj-$(CONFIG_COMEDI_8255_PCI) += 8255_pci.o
+obj-$(CONFIG_COMEDI_ADDI_WATCHDOG) += addi_watchdog.o
+obj-$(CONFIG_COMEDI_ADDI_APCI_1032) += addi_apci_1032.o
+obj-$(CONFIG_COMEDI_ADDI_APCI_1500) += addi_apci_1500.o
+obj-$(CONFIG_COMEDI_ADDI_APCI_1516) += addi_apci_1516.o
+obj-$(CONFIG_COMEDI_ADDI_APCI_1564) += addi_apci_1564.o
+obj-$(CONFIG_COMEDI_ADDI_APCI_16XX) += addi_apci_16xx.o
+obj-$(CONFIG_COMEDI_ADDI_APCI_2032) += addi_apci_2032.o
+obj-$(CONFIG_COMEDI_ADDI_APCI_2200) += addi_apci_2200.o
+obj-$(CONFIG_COMEDI_ADDI_APCI_3120) += addi_apci_3120.o
+obj-$(CONFIG_COMEDI_ADDI_APCI_3501) += addi_apci_3501.o
+obj-$(CONFIG_COMEDI_ADDI_APCI_3XXX) += addi_apci_3xxx.o
+obj-$(CONFIG_COMEDI_ADL_PCI6208) += adl_pci6208.o
+obj-$(CONFIG_COMEDI_ADL_PCI7X3X) += adl_pci7x3x.o
+obj-$(CONFIG_COMEDI_ADL_PCI8164) += adl_pci8164.o
+obj-$(CONFIG_COMEDI_ADL_PCI9111) += adl_pci9111.o
+obj-$(CONFIG_COMEDI_ADL_PCI9118) += adl_pci9118.o
+obj-$(CONFIG_COMEDI_ADV_PCI1710) += adv_pci1710.o
+obj-$(CONFIG_COMEDI_ADV_PCI1723) += adv_pci1723.o
+obj-$(CONFIG_COMEDI_ADV_PCI1724) += adv_pci1724.o
+obj-$(CONFIG_COMEDI_ADV_PCI_DIO) += adv_pci_dio.o
+obj-$(CONFIG_COMEDI_AMPLC_DIO200_PCI) += amplc_dio200_pci.o
+obj-$(CONFIG_COMEDI_AMPLC_PC236_PCI) += amplc_pci236.o
+obj-$(CONFIG_COMEDI_AMPLC_PC263_PCI) += amplc_pci263.o
+obj-$(CONFIG_COMEDI_AMPLC_PCI224) += amplc_pci224.o
+obj-$(CONFIG_COMEDI_AMPLC_PCI230) += amplc_pci230.o
+obj-$(CONFIG_COMEDI_CONTEC_PCI_DIO) += contec_pci_dio.o
+obj-$(CONFIG_COMEDI_DAS08_PCI) += das08_pci.o
+obj-$(CONFIG_COMEDI_DT3000) += dt3000.o
+obj-$(CONFIG_COMEDI_DYNA_PCI10XX) += dyna_pci10xx.o
+obj-$(CONFIG_COMEDI_UNIOXX5) += unioxx5.o
+obj-$(CONFIG_COMEDI_GSC_HPDI) += gsc_hpdi.o
+obj-$(CONFIG_COMEDI_ICP_MULTI) += icp_multi.o
+obj-$(CONFIG_COMEDI_DAQBOARD2000) += daqboard2000.o
+obj-$(CONFIG_COMEDI_JR3_PCI) += jr3_pci.o
+obj-$(CONFIG_COMEDI_KE_COUNTER) += ke_counter.o
+obj-$(CONFIG_COMEDI_CB_PCIDAS64) += cb_pcidas64.o
+obj-$(CONFIG_COMEDI_CB_PCIDAS) += cb_pcidas.o
+obj-$(CONFIG_COMEDI_CB_PCIDDA) += cb_pcidda.o
+obj-$(CONFIG_COMEDI_CB_PCIMDAS) += cb_pcimdas.o
+obj-$(CONFIG_COMEDI_CB_PCIMDDA) += cb_pcimdda.o
+obj-$(CONFIG_COMEDI_ME4000) += me4000.o
+obj-$(CONFIG_COMEDI_ME_DAQ) += me_daq.o
+obj-$(CONFIG_COMEDI_NI_6527) += ni_6527.o
+obj-$(CONFIG_COMEDI_NI_65XX) += ni_65xx.o
+obj-$(CONFIG_COMEDI_NI_660X) += ni_660x.o
+obj-$(CONFIG_COMEDI_NI_670X) += ni_670x.o
+obj-$(CONFIG_COMEDI_NI_LABPC_PCI) += ni_labpc_pci.o
+obj-$(CONFIG_COMEDI_NI_PCIDIO) += ni_pcidio.o
+obj-$(CONFIG_COMEDI_NI_PCIMIO) += ni_pcimio.o
+obj-$(CONFIG_COMEDI_RTD520) += rtd520.o
+obj-$(CONFIG_COMEDI_S626) += s626.o
+obj-$(CONFIG_COMEDI_SSV_DNP) += ssv_dnp.o
+obj-$(CONFIG_COMEDI_MF6X4) += mf6x4.o
+
+# Comedi PCMCIA drivers
+obj-$(CONFIG_COMEDI_CB_DAS16_CS) += cb_das16_cs.o
+obj-$(CONFIG_COMEDI_DAS08_CS) += das08_cs.o
+obj-$(CONFIG_COMEDI_NI_DAQ_700_CS) += ni_daq_700.o
+obj-$(CONFIG_COMEDI_NI_DAQ_DIO24_CS) += ni_daq_dio24.o
+obj-$(CONFIG_COMEDI_NI_LABPC_CS) += ni_labpc_cs.o
+obj-$(CONFIG_COMEDI_NI_MIO_CS) += ni_mio_cs.o
+obj-$(CONFIG_COMEDI_QUATECH_DAQP_CS) += quatech_daqp_cs.o
+
+# Comedi USB drivers
+obj-$(CONFIG_COMEDI_DT9812) += dt9812.o
+obj-$(CONFIG_COMEDI_NI_USB6501) += ni_usb6501.o
+obj-$(CONFIG_COMEDI_USBDUX) += usbdux.o
+obj-$(CONFIG_COMEDI_USBDUXFAST) += usbduxfast.o
+obj-$(CONFIG_COMEDI_USBDUXSIGMA) += usbduxsigma.o
+obj-$(CONFIG_COMEDI_VMK80XX) += vmk80xx.o
+
+# Comedi NI drivers
+obj-$(CONFIG_COMEDI_MITE) += mite.o
+obj-$(CONFIG_COMEDI_NI_TIO) += ni_tio.o
+obj-$(CONFIG_COMEDI_NI_TIOCMD) += ni_tiocmd.o
+obj-$(CONFIG_COMEDI_NI_LABPC) += ni_labpc_common.o
+obj-$(CONFIG_COMEDI_NI_LABPC_ISADMA) += ni_labpc_isadma.o
+
+obj-$(CONFIG_COMEDI_8255) += 8255.o
+obj-$(CONFIG_COMEDI_AMPLC_DIO200) += amplc_dio200_common.o
+obj-$(CONFIG_COMEDI_AMPLC_PC236) += amplc_pc236_common.o
+obj-$(CONFIG_COMEDI_DAS08) += das08.o
diff --git a/drivers/staging/comedi/drivers/addi-data/hwdrv_apci1564.c b/drivers/staging/comedi/drivers/addi-data/hwdrv_apci1564.c
new file mode 100644
index 000000000..fa99c8ca4
--- /dev/null
+++ b/drivers/staging/comedi/drivers/addi-data/hwdrv_apci1564.c
@@ -0,0 +1,204 @@
+/* Digital Input IRQ Function Selection */
+#define APCI1564_DI_INT_OR (0 << 1)
+#define APCI1564_DI_INT_AND (1 << 1)
+
+/* Digital Input Interrupt Enable Disable. */
+#define APCI1564_DI_INT_ENABLE 0x4
+#define APCI1564_DI_INT_DISABLE 0xfffffffb
+
+/* Digital Output Interrupt Enable Disable. */
+#define APCI1564_DO_VCC_INT_ENABLE 0x1
+#define APCI1564_DO_VCC_INT_DISABLE 0xfffffffe
+#define APCI1564_DO_CC_INT_ENABLE 0x2
+#define APCI1564_DO_CC_INT_DISABLE 0xfffffffd
+
+/* TIMER COUNTER WATCHDOG DEFINES */
+#define ADDIDATA_TIMER 0
+#define ADDIDATA_COUNTER 1
+#define ADDIDATA_WATCHDOG 2
+
+static int apci1564_timer_insn_config(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ struct apci1564_private *devpriv = dev->private;
+ unsigned int ctrl;
+
+ devpriv->tsk_current = current;
+
+ /* First Stop The Timer */
+ ctrl = inl(devpriv->timer + ADDI_TCW_CTRL_REG);
+ ctrl &= 0xfffff9fe;
+ /* Stop The Timer */
+ outl(ctrl, devpriv->timer + ADDI_TCW_CTRL_REG);
+
+ if (data[1] == 1) {
+ /* Enable timer int & disable all the other int sources */
+ outl(0x02, devpriv->timer + ADDI_TCW_CTRL_REG);
+ outl(0x0, dev->iobase + APCI1564_DI_IRQ_REG);
+ outl(0x0, dev->iobase + APCI1564_DO_IRQ_REG);
+ outl(0x0, dev->iobase + APCI1564_WDOG_IRQ_REG);
+ if (devpriv->counters) {
+ unsigned long iobase;
+
+ iobase = devpriv->counters + ADDI_TCW_IRQ_REG;
+ outl(0x0, iobase + APCI1564_COUNTER(0));
+ outl(0x0, iobase + APCI1564_COUNTER(1));
+ outl(0x0, iobase + APCI1564_COUNTER(2));
+ }
+ } else {
+ /* disable Timer interrupt */
+ outl(0x0, devpriv->timer + ADDI_TCW_CTRL_REG);
+ }
+
+ /* Loading Timebase */
+ outl(data[2], devpriv->timer + ADDI_TCW_TIMEBASE_REG);
+
+ /* Loading the Reload value */
+ outl(data[3], devpriv->timer + ADDI_TCW_RELOAD_REG);
+
+ ctrl = inl(devpriv->timer + ADDI_TCW_CTRL_REG);
+ ctrl &= 0xfff719e2;
+ ctrl |= (2 << 13) | 0x10;
+ /* mode 2 */
+ outl(ctrl, devpriv->timer + ADDI_TCW_CTRL_REG);
+
+ return insn->n;
+}
+
+static int apci1564_timer_insn_write(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ struct apci1564_private *devpriv = dev->private;
+ unsigned int ctrl;
+
+ ctrl = inl(devpriv->timer + ADDI_TCW_CTRL_REG);
+ switch (data[1]) {
+ case 0: /* Stop The Timer */
+ ctrl &= 0xfffff9fe;
+ break;
+ case 1: /* Enable the Timer */
+ ctrl &= 0xfffff9ff;
+ ctrl |= 0x1;
+ break;
+ }
+ outl(ctrl, devpriv->timer + ADDI_TCW_CTRL_REG);
+
+ return insn->n;
+}
+
+static int apci1564_timer_insn_read(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ struct apci1564_private *devpriv = dev->private;
+
+ /* Stores the status of the Timer */
+ data[0] = inl(devpriv->timer + ADDI_TCW_STATUS_REG) & 0x1;
+
+ /* Stores the Actual value of the Timer */
+ data[1] = inl(devpriv->timer + ADDI_TCW_VAL_REG);
+
+ return insn->n;
+}
+
+static int apci1564_counter_insn_config(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ struct apci1564_private *devpriv = dev->private;
+ unsigned int chan = CR_CHAN(insn->chanspec);
+ unsigned long iobase = devpriv->counters + APCI1564_COUNTER(chan);
+ unsigned int ctrl;
+
+ devpriv->tsk_current = current;
+
+ /* First Stop The Counter */
+ ctrl = inl(iobase + ADDI_TCW_CTRL_REG);
+ ctrl &= 0xfffff9fe;
+ /* Stop The Timer */
+ outl(ctrl, iobase + ADDI_TCW_CTRL_REG);
+
+ /* Set the reload value */
+ outl(data[3], iobase + ADDI_TCW_RELOAD_REG);
+
+ /* Set the mode : */
+ /* - Disable the hardware */
+ /* - Disable the counter mode */
+ /* - Disable the warning */
+ /* - Disable the reset */
+ /* - Disable the timer mode */
+ /* - Enable the counter mode */
+
+ ctrl &= 0xfffc19e2;
+ ctrl |= 0x80000 | (data[4] << 16);
+ outl(ctrl, iobase + ADDI_TCW_CTRL_REG);
+
+ /* Enable or Disable Interrupt */
+ ctrl &= 0xfffff9fd;
+ ctrl |= (data[1] << 1);
+ outl(ctrl, iobase + ADDI_TCW_CTRL_REG);
+
+ /* Set the Up/Down selection */
+ ctrl &= 0xfffbf9ff;
+ ctrl |= (data[6] << 18);
+ outl(ctrl, iobase + ADDI_TCW_CTRL_REG);
+
+ return insn->n;
+}
+
+static int apci1564_counter_insn_write(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ struct apci1564_private *devpriv = dev->private;
+ unsigned int chan = CR_CHAN(insn->chanspec);
+ unsigned long iobase = devpriv->counters + APCI1564_COUNTER(chan);
+ unsigned int ctrl;
+
+ ctrl = inl(iobase + ADDI_TCW_CTRL_REG);
+ switch (data[1]) {
+ case 0: /* Stops the Counter subdevice */
+ ctrl = 0;
+ break;
+ case 1: /* Start the Counter subdevice */
+ ctrl &= 0xfffff9ff;
+ ctrl |= 0x1;
+ break;
+ case 2: /* Clears the Counter subdevice */
+ ctrl &= 0xfffff9ff;
+ ctrl |= 0x400;
+ break;
+ }
+ outl(ctrl, iobase + ADDI_TCW_CTRL_REG);
+
+ return insn->n;
+}
+
+static int apci1564_counter_insn_read(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ struct apci1564_private *devpriv = dev->private;
+ unsigned int chan = CR_CHAN(insn->chanspec);
+ unsigned long iobase = devpriv->counters + APCI1564_COUNTER(chan);
+ unsigned int status;
+
+ /* Read the Counter Actual Value. */
+ data[0] = inl(iobase + ADDI_TCW_VAL_REG);
+
+ status = inl(iobase + ADDI_TCW_STATUS_REG);
+ data[1] = (status >> 1) & 1; /* software trigger status */
+ data[2] = (status >> 2) & 1; /* hardware trigger status */
+ data[3] = (status >> 3) & 1; /* software clear status */
+ data[4] = (status >> 0) & 1; /* overflow status */
+
+ return insn->n;
+}
diff --git a/drivers/staging/comedi/drivers/addi-data/hwdrv_apci3501.c b/drivers/staging/comedi/drivers/addi-data/hwdrv_apci3501.c
new file mode 100644
index 000000000..1f2f78186
--- /dev/null
+++ b/drivers/staging/comedi/drivers/addi-data/hwdrv_apci3501.c
@@ -0,0 +1,173 @@
+/* Watchdog Related Defines */
+
+#define ADDIDATA_TIMER 0
+#define ADDIDATA_WATCHDOG 2
+
+/*
+ * (*insn_config) for the timer subdevice
+ *
+ * Configures The Timer, Counter or Watchdog
+ * Data Pointer contains configuration parameters as below
+ * data[0] : 0 Configure As Timer
+ * 1 Configure As Counter
+ * 2 Configure As Watchdog
+ * data[1] : 1 Enable Interrupt
+ * 0 Disable Interrupt
+ * data[2] : Time Unit
+ * data[3] : Reload Value
+ */
+static int apci3501_config_insn_timer(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ struct apci3501_private *devpriv = dev->private;
+ unsigned int ul_Command1 = 0;
+
+ devpriv->tsk_Current = current;
+ if (data[0] == ADDIDATA_WATCHDOG) {
+
+ devpriv->b_TimerSelectMode = ADDIDATA_WATCHDOG;
+ /* Disable the watchdog */
+ outl(0x0, dev->iobase + APCI3501_TIMER_CTRL_REG);
+
+ if (data[1] == 1) {
+ /* Enable TIMER int & DISABLE ALL THE OTHER int SOURCES */
+ outl(0x02, dev->iobase + APCI3501_TIMER_CTRL_REG);
+ } else {
+ /* disable Timer interrupt */
+ outl(0x0, dev->iobase + APCI3501_TIMER_CTRL_REG);
+ }
+
+ outl(data[2], dev->iobase + APCI3501_TIMER_TIMEBASE_REG);
+ outl(data[3], dev->iobase + APCI3501_TIMER_RELOAD_REG);
+
+ /* Set the mode (e2->e0) */
+ ul_Command1 = inl(dev->iobase + APCI3501_TIMER_CTRL_REG) | 0xFFF819E0UL;
+ outl(ul_Command1, dev->iobase + APCI3501_TIMER_CTRL_REG);
+ }
+
+ else if (data[0] == ADDIDATA_TIMER) {
+ /* First Stop The Timer */
+ ul_Command1 = inl(dev->iobase + APCI3501_TIMER_CTRL_REG);
+ ul_Command1 = ul_Command1 & 0xFFFFF9FEUL;
+ outl(ul_Command1, dev->iobase + APCI3501_TIMER_CTRL_REG);
+ devpriv->b_TimerSelectMode = ADDIDATA_TIMER;
+ if (data[1] == 1) {
+ /* Enable TIMER int & DISABLE ALL THE OTHER int SOURCES */
+ outl(0x02, dev->iobase + APCI3501_TIMER_CTRL_REG);
+ } else {
+ /* disable Timer interrupt */
+ outl(0x0, dev->iobase + APCI3501_TIMER_CTRL_REG);
+ }
+
+ outl(data[2], dev->iobase + APCI3501_TIMER_TIMEBASE_REG);
+ outl(data[3], dev->iobase + APCI3501_TIMER_RELOAD_REG);
+
+ /* mode 2 */
+ ul_Command1 = inl(dev->iobase + APCI3501_TIMER_CTRL_REG);
+ ul_Command1 =
+ (ul_Command1 & 0xFFF719E2UL) | 2UL << 13UL | 0x10UL;
+ outl(ul_Command1, dev->iobase + APCI3501_TIMER_CTRL_REG);
+ }
+
+ return insn->n;
+}
+
+/*
+ * (*insn_write) for the timer subdevice
+ *
+ * Start / Stop The Selected Timer , Counter or Watchdog
+ * Data Pointer contains configuration parameters as below
+ * data[0] : 0 Timer
+ * 1 Counter
+ * 2 Watchdog
+ * data[1] : 1 Start
+ * 0 Stop
+ * 2 Trigger
+ */
+static int apci3501_write_insn_timer(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ struct apci3501_private *devpriv = dev->private;
+ unsigned int ul_Command1 = 0;
+
+ if (devpriv->b_TimerSelectMode == ADDIDATA_WATCHDOG) {
+
+ if (data[1] == 1) {
+ ul_Command1 = inl(dev->iobase + APCI3501_TIMER_CTRL_REG);
+ ul_Command1 = (ul_Command1 & 0xFFFFF9FFUL) | 0x1UL;
+ /* Enable the Watchdog */
+ outl(ul_Command1, dev->iobase + APCI3501_TIMER_CTRL_REG);
+ } else if (data[1] == 0) { /* Stop The Watchdog */
+ ul_Command1 = inl(dev->iobase + APCI3501_TIMER_CTRL_REG);
+ ul_Command1 = ul_Command1 & 0xFFFFF9FEUL;
+ outl(0x0, dev->iobase + APCI3501_TIMER_CTRL_REG);
+ } else if (data[1] == 2) {
+ ul_Command1 = inl(dev->iobase + APCI3501_TIMER_CTRL_REG);
+ ul_Command1 = (ul_Command1 & 0xFFFFF9FFUL) | 0x200UL;
+ outl(ul_Command1, dev->iobase + APCI3501_TIMER_CTRL_REG);
+ }
+ }
+
+ if (devpriv->b_TimerSelectMode == ADDIDATA_TIMER) {
+ if (data[1] == 1) {
+
+ ul_Command1 = inl(dev->iobase + APCI3501_TIMER_CTRL_REG);
+ ul_Command1 = (ul_Command1 & 0xFFFFF9FFUL) | 0x1UL;
+ /* Enable the Timer */
+ outl(ul_Command1, dev->iobase + APCI3501_TIMER_CTRL_REG);
+ } else if (data[1] == 0) {
+ /* Stop The Timer */
+ ul_Command1 = inl(dev->iobase + APCI3501_TIMER_CTRL_REG);
+ ul_Command1 = ul_Command1 & 0xFFFFF9FEUL;
+ outl(ul_Command1, dev->iobase + APCI3501_TIMER_CTRL_REG);
+ }
+
+ else if (data[1] == 2) {
+ /* Trigger the Timer */
+ ul_Command1 = inl(dev->iobase + APCI3501_TIMER_CTRL_REG);
+ ul_Command1 = (ul_Command1 & 0xFFFFF9FFUL) | 0x200UL;
+ outl(ul_Command1, dev->iobase + APCI3501_TIMER_CTRL_REG);
+ }
+ }
+
+ inl(dev->iobase + APCI3501_TIMER_STATUS_REG);
+ return insn->n;
+}
+
+/*
+ * (*insn_read) for the timer subdevice
+ *
+ * Read The Selected Timer, Counter or Watchdog
+ * Data Pointer contains configuration parameters as below
+ * data[0] : 0 Timer
+ * 1 Counter
+ * 2 Watchdog
+ * data[1] : Timer Counter Watchdog Number
+ */
+static int apci3501_read_insn_timer(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ struct apci3501_private *devpriv = dev->private;
+
+ if (devpriv->b_TimerSelectMode == ADDIDATA_WATCHDOG) {
+ data[0] = inl(dev->iobase + APCI3501_TIMER_STATUS_REG) & 0x1;
+ data[1] = inl(dev->iobase + APCI3501_TIMER_SYNC_REG);
+ }
+
+ else if (devpriv->b_TimerSelectMode == ADDIDATA_TIMER) {
+ data[0] = inl(dev->iobase + APCI3501_TIMER_STATUS_REG) & 0x1;
+ data[1] = inl(dev->iobase + APCI3501_TIMER_SYNC_REG);
+ }
+
+ else if ((devpriv->b_TimerSelectMode != ADDIDATA_TIMER)
+ && (devpriv->b_TimerSelectMode != ADDIDATA_WATCHDOG)) {
+ dev_err(dev->class_dev, "Invalid subdevice.\n");
+ }
+ return insn->n;
+}
diff --git a/drivers/staging/comedi/drivers/addi_apci_1032.c b/drivers/staging/comedi/drivers/addi_apci_1032.c
new file mode 100644
index 000000000..b37166d57
--- /dev/null
+++ b/drivers/staging/comedi/drivers/addi_apci_1032.c
@@ -0,0 +1,394 @@
+/*
+ * addi_apci_1032.c
+ * Copyright (C) 2004,2005 ADDI-DATA GmbH for the source code of this module.
+ * Project manager: Eric Stolz
+ *
+ * ADDI-DATA GmbH
+ * Dieselstrasse 3
+ * D-77833 Ottersweier
+ * Tel: +19(0)7223/9493-0
+ * Fax: +49(0)7223/9493-92
+ * http://www.addi-data.com
+ * info@addi-data.com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ */
+
+/*
+ * Driver: addi_apci_1032
+ * Description: ADDI-DATA APCI-1032 Digital Input Board
+ * Author: ADDI-DATA GmbH <info@addi-data.com>,
+ * H Hartley Sweeten <hsweeten@visionengravers.com>
+ * Status: untested
+ * Devices: [ADDI-DATA] APCI-1032 (addi_apci_1032)
+ *
+ * Configuration options:
+ * None; devices are configured automatically.
+ *
+ * This driver models the APCI-1032 as a 32-channel, digital input subdevice
+ * plus an additional digital input subdevice to handle change-of-state (COS)
+ * interrupts (if an interrupt handler can be set up successfully).
+ *
+ * The COS subdevice supports comedi asynchronous read commands.
+ *
+ * Change-Of-State (COS) interrupt configuration:
+ *
+ * Channels 0 to 15 are interruptible. These channels can be configured
+ * to generate interrupts based on AND/OR logic for the desired channels.
+ *
+ * OR logic:
+ * - reacts to rising or falling edges
+ * - interrupt is generated when any enabled channel meets the desired
+ * interrupt condition
+ *
+ * AND logic:
+ * - reacts to changes in level of the selected inputs
+ * - interrupt is generated when all enabled channels meet the desired
+ * interrupt condition
+ * - after an interrupt, a change in level must occur on the selected
+ * inputs to release the IRQ logic
+ *
+ * The COS subdevice must be configured before setting up a comedi
+ * asynchronous command:
+ *
+ * data[0] : INSN_CONFIG_DIGITAL_TRIG
+ * data[1] : trigger number (= 0)
+ * data[2] : configuration operation:
+ * - COMEDI_DIGITAL_TRIG_DISABLE = no interrupts
+ * - COMEDI_DIGITAL_TRIG_ENABLE_EDGES = OR (edge) interrupts
+ * - COMEDI_DIGITAL_TRIG_ENABLE_LEVELS = AND (level) interrupts
+ * data[3] : left-shift for data[4] and data[5]
+ * data[4] : rising-edge/high level channels
+ * data[5] : falling-edge/low level channels
+ */
+
+#include <linux/module.h>
+#include <linux/interrupt.h>
+
+#include "../comedi_pci.h"
+#include "amcc_s5933.h"
+
+/*
+ * I/O Register Map
+ */
+#define APCI1032_DI_REG 0x00
+#define APCI1032_MODE1_REG 0x04
+#define APCI1032_MODE2_REG 0x08
+#define APCI1032_STATUS_REG 0x0c
+#define APCI1032_CTRL_REG 0x10
+#define APCI1032_CTRL_INT_OR (0 << 1)
+#define APCI1032_CTRL_INT_AND (1 << 1)
+#define APCI1032_CTRL_INT_ENA (1 << 2)
+
+struct apci1032_private {
+ unsigned long amcc_iobase; /* base of AMCC I/O registers */
+ unsigned int mode1; /* rising-edge/high level channels */
+ unsigned int mode2; /* falling-edge/low level channels */
+ unsigned int ctrl; /* interrupt mode OR (edge) . AND (level) */
+};
+
+static int apci1032_reset(struct comedi_device *dev)
+{
+ /* disable the interrupts */
+ outl(0x0, dev->iobase + APCI1032_CTRL_REG);
+ /* Reset the interrupt status register */
+ inl(dev->iobase + APCI1032_STATUS_REG);
+ /* Disable the and/or interrupt */
+ outl(0x0, dev->iobase + APCI1032_MODE1_REG);
+ outl(0x0, dev->iobase + APCI1032_MODE2_REG);
+
+ return 0;
+}
+
+static int apci1032_cos_insn_config(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ struct apci1032_private *devpriv = dev->private;
+ unsigned int shift, oldmask;
+
+ switch (data[0]) {
+ case INSN_CONFIG_DIGITAL_TRIG:
+ if (data[1] != 0)
+ return -EINVAL;
+ shift = data[3];
+ oldmask = (1U << shift) - 1;
+ switch (data[2]) {
+ case COMEDI_DIGITAL_TRIG_DISABLE:
+ devpriv->ctrl = 0;
+ devpriv->mode1 = 0;
+ devpriv->mode2 = 0;
+ apci1032_reset(dev);
+ break;
+ case COMEDI_DIGITAL_TRIG_ENABLE_EDGES:
+ if (devpriv->ctrl != (APCI1032_CTRL_INT_ENA |
+ APCI1032_CTRL_INT_OR)) {
+ /* switching to 'OR' mode */
+ devpriv->ctrl = APCI1032_CTRL_INT_ENA |
+ APCI1032_CTRL_INT_OR;
+ /* wipe old channels */
+ devpriv->mode1 = 0;
+ devpriv->mode2 = 0;
+ } else {
+ /* preserve unspecified channels */
+ devpriv->mode1 &= oldmask;
+ devpriv->mode2 &= oldmask;
+ }
+ /* configure specified channels */
+ devpriv->mode1 |= data[4] << shift;
+ devpriv->mode2 |= data[5] << shift;
+ break;
+ case COMEDI_DIGITAL_TRIG_ENABLE_LEVELS:
+ if (devpriv->ctrl != (APCI1032_CTRL_INT_ENA |
+ APCI1032_CTRL_INT_AND)) {
+ /* switching to 'AND' mode */
+ devpriv->ctrl = APCI1032_CTRL_INT_ENA |
+ APCI1032_CTRL_INT_AND;
+ /* wipe old channels */
+ devpriv->mode1 = 0;
+ devpriv->mode2 = 0;
+ } else {
+ /* preserve unspecified channels */
+ devpriv->mode1 &= oldmask;
+ devpriv->mode2 &= oldmask;
+ }
+ /* configure specified channels */
+ devpriv->mode1 |= data[4] << shift;
+ devpriv->mode2 |= data[5] << shift;
+ break;
+ default:
+ return -EINVAL;
+ }
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return insn->n;
+}
+
+static int apci1032_cos_insn_bits(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ data[1] = s->state;
+
+ return 0;
+}
+
+static int apci1032_cos_cmdtest(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_cmd *cmd)
+{
+ int err = 0;
+
+ /* Step 1 : check if triggers are trivially valid */
+
+ err |= comedi_check_trigger_src(&cmd->start_src, TRIG_NOW);
+ err |= comedi_check_trigger_src(&cmd->scan_begin_src, TRIG_EXT);
+ err |= comedi_check_trigger_src(&cmd->convert_src, TRIG_FOLLOW);
+ err |= comedi_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
+ err |= comedi_check_trigger_src(&cmd->stop_src, TRIG_NONE);
+
+ if (err)
+ return 1;
+
+ /* Step 2a : make sure trigger sources are unique */
+ /* Step 2b : and mutually compatible */
+
+ /* Step 3: check if arguments are trivially valid */
+
+ err |= comedi_check_trigger_arg_is(&cmd->start_arg, 0);
+ err |= comedi_check_trigger_arg_is(&cmd->scan_begin_arg, 0);
+ err |= comedi_check_trigger_arg_is(&cmd->convert_arg, 0);
+ err |= comedi_check_trigger_arg_is(&cmd->scan_end_arg,
+ cmd->chanlist_len);
+ err |= comedi_check_trigger_arg_is(&cmd->stop_arg, 0);
+
+ if (err)
+ return 3;
+
+ /* Step 4: fix up any arguments */
+
+ /* Step 5: check channel list if it exists */
+
+ return 0;
+}
+
+/*
+ * Change-Of-State (COS) 'do_cmd' operation
+ *
+ * Enable the COS interrupt as configured by apci1032_cos_insn_config().
+ */
+static int apci1032_cos_cmd(struct comedi_device *dev,
+ struct comedi_subdevice *s)
+{
+ struct apci1032_private *devpriv = dev->private;
+
+ if (!devpriv->ctrl) {
+ dev_warn(dev->class_dev,
+ "Interrupts disabled due to mode configuration!\n");
+ return -EINVAL;
+ }
+
+ outl(devpriv->mode1, dev->iobase + APCI1032_MODE1_REG);
+ outl(devpriv->mode2, dev->iobase + APCI1032_MODE2_REG);
+ outl(devpriv->ctrl, dev->iobase + APCI1032_CTRL_REG);
+
+ return 0;
+}
+
+static int apci1032_cos_cancel(struct comedi_device *dev,
+ struct comedi_subdevice *s)
+{
+ return apci1032_reset(dev);
+}
+
+static irqreturn_t apci1032_interrupt(int irq, void *d)
+{
+ struct comedi_device *dev = d;
+ struct apci1032_private *devpriv = dev->private;
+ struct comedi_subdevice *s = dev->read_subdev;
+ unsigned int ctrl;
+
+ /* check interrupt is from this device */
+ if ((inl(devpriv->amcc_iobase + AMCC_OP_REG_INTCSR) &
+ INTCSR_INTR_ASSERTED) == 0)
+ return IRQ_NONE;
+
+ /* check interrupt is enabled */
+ ctrl = inl(dev->iobase + APCI1032_CTRL_REG);
+ if ((ctrl & APCI1032_CTRL_INT_ENA) == 0)
+ return IRQ_HANDLED;
+
+ /* disable the interrupt */
+ outl(ctrl & ~APCI1032_CTRL_INT_ENA, dev->iobase + APCI1032_CTRL_REG);
+
+ s->state = inl(dev->iobase + APCI1032_STATUS_REG) & 0xffff;
+ comedi_buf_write_samples(s, &s->state, 1);
+ comedi_handle_events(dev, s);
+
+ /* enable the interrupt */
+ outl(ctrl, dev->iobase + APCI1032_CTRL_REG);
+
+ return IRQ_HANDLED;
+}
+
+static int apci1032_di_insn_bits(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ data[1] = inl(dev->iobase + APCI1032_DI_REG);
+
+ return insn->n;
+}
+
+static int apci1032_auto_attach(struct comedi_device *dev,
+ unsigned long context_unused)
+{
+ struct pci_dev *pcidev = comedi_to_pci_dev(dev);
+ struct apci1032_private *devpriv;
+ struct comedi_subdevice *s;
+ int ret;
+
+ devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
+ if (!devpriv)
+ return -ENOMEM;
+
+ ret = comedi_pci_enable(dev);
+ if (ret)
+ return ret;
+
+ devpriv->amcc_iobase = pci_resource_start(pcidev, 0);
+ dev->iobase = pci_resource_start(pcidev, 1);
+ apci1032_reset(dev);
+ if (pcidev->irq > 0) {
+ ret = request_irq(pcidev->irq, apci1032_interrupt, IRQF_SHARED,
+ dev->board_name, dev);
+ if (ret == 0)
+ dev->irq = pcidev->irq;
+ }
+
+ ret = comedi_alloc_subdevices(dev, 2);
+ if (ret)
+ return ret;
+
+ /* Allocate and Initialise DI Subdevice Structures */
+ s = &dev->subdevices[0];
+ s->type = COMEDI_SUBD_DI;
+ s->subdev_flags = SDF_READABLE;
+ s->n_chan = 32;
+ s->maxdata = 1;
+ s->range_table = &range_digital;
+ s->insn_bits = apci1032_di_insn_bits;
+
+ /* Change-Of-State (COS) interrupt subdevice */
+ s = &dev->subdevices[1];
+ if (dev->irq) {
+ dev->read_subdev = s;
+ s->type = COMEDI_SUBD_DI;
+ s->subdev_flags = SDF_READABLE | SDF_CMD_READ;
+ s->n_chan = 1;
+ s->maxdata = 1;
+ s->range_table = &range_digital;
+ s->insn_config = apci1032_cos_insn_config;
+ s->insn_bits = apci1032_cos_insn_bits;
+ s->len_chanlist = 1;
+ s->do_cmdtest = apci1032_cos_cmdtest;
+ s->do_cmd = apci1032_cos_cmd;
+ s->cancel = apci1032_cos_cancel;
+ } else {
+ s->type = COMEDI_SUBD_UNUSED;
+ }
+
+ return 0;
+}
+
+static void apci1032_detach(struct comedi_device *dev)
+{
+ if (dev->iobase)
+ apci1032_reset(dev);
+ comedi_pci_detach(dev);
+}
+
+static struct comedi_driver apci1032_driver = {
+ .driver_name = "addi_apci_1032",
+ .module = THIS_MODULE,
+ .auto_attach = apci1032_auto_attach,
+ .detach = apci1032_detach,
+};
+
+static int apci1032_pci_probe(struct pci_dev *dev,
+ const struct pci_device_id *id)
+{
+ return comedi_pci_auto_config(dev, &apci1032_driver, id->driver_data);
+}
+
+static const struct pci_device_id apci1032_pci_table[] = {
+ { PCI_DEVICE(PCI_VENDOR_ID_ADDIDATA, 0x1003) },
+ { 0 }
+};
+MODULE_DEVICE_TABLE(pci, apci1032_pci_table);
+
+static struct pci_driver apci1032_pci_driver = {
+ .name = "addi_apci_1032",
+ .id_table = apci1032_pci_table,
+ .probe = apci1032_pci_probe,
+ .remove = comedi_pci_auto_unconfig,
+};
+module_comedi_pci_driver(apci1032_driver, apci1032_pci_driver);
+
+MODULE_AUTHOR("Comedi http://www.comedi.org");
+MODULE_DESCRIPTION("ADDI-DATA APCI-1032, 32 channel DI boards");
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/comedi/drivers/addi_apci_1500.c b/drivers/staging/comedi/drivers/addi_apci_1500.c
new file mode 100644
index 000000000..63991c49f
--- /dev/null
+++ b/drivers/staging/comedi/drivers/addi_apci_1500.c
@@ -0,0 +1,878 @@
+/*
+ * addi_apci_1500.c
+ * Copyright (C) 2004,2005 ADDI-DATA GmbH for the source code of this module.
+ *
+ * ADDI-DATA GmbH
+ * Dieselstrasse 3
+ * D-77833 Ottersweier
+ * Tel: +19(0)7223/9493-0
+ * Fax: +49(0)7223/9493-92
+ * http://www.addi-data.com
+ * info@addi-data.com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ */
+
+#include <linux/module.h>
+#include <linux/interrupt.h>
+
+#include "../comedi_pci.h"
+#include "amcc_s5933.h"
+#include "z8536.h"
+
+/*
+ * PCI Bar 0 Register map (devpriv->amcc)
+ * see amcc_s5933.h for register and bit defines
+ */
+
+/*
+ * PCI Bar 1 Register map (dev->iobase)
+ * see z8536.h for Z8536 internal registers and bit defines
+ */
+#define APCI1500_Z8536_PORTC_REG 0x00
+#define APCI1500_Z8536_PORTB_REG 0x01
+#define APCI1500_Z8536_PORTA_REG 0x02
+#define APCI1500_Z8536_CTRL_REG 0x03
+
+/*
+ * PCI Bar 2 Register map (devpriv->addon)
+ */
+#define APCI1500_CLK_SEL_REG 0x00
+#define APCI1500_DI_REG 0x00
+#define APCI1500_DO_REG 0x02
+
+struct apci1500_private {
+ unsigned long amcc;
+ unsigned long addon;
+
+ unsigned int clk_src;
+
+ /* Digital trigger configuration [0]=AND [1]=OR */
+ unsigned int pm[2]; /* Pattern Mask */
+ unsigned int pt[2]; /* Pattern Transition */
+ unsigned int pp[2]; /* Pattern Polarity */
+};
+
+static unsigned int z8536_read(struct comedi_device *dev, unsigned int reg)
+{
+ unsigned long flags;
+ unsigned int val;
+
+ spin_lock_irqsave(&dev->spinlock, flags);
+ outb(reg, dev->iobase + APCI1500_Z8536_CTRL_REG);
+ val = inb(dev->iobase + APCI1500_Z8536_CTRL_REG);
+ spin_unlock_irqrestore(&dev->spinlock, flags);
+
+ return val;
+}
+
+static void z8536_write(struct comedi_device *dev,
+ unsigned int val, unsigned int reg)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&dev->spinlock, flags);
+ outb(reg, dev->iobase + APCI1500_Z8536_CTRL_REG);
+ outb(val, dev->iobase + APCI1500_Z8536_CTRL_REG);
+ spin_unlock_irqrestore(&dev->spinlock, flags);
+}
+
+static void z8536_reset(struct comedi_device *dev)
+{
+ unsigned long flags;
+
+ /*
+ * Even if the state of the Z8536 is not known, the following
+ * sequence will reset it and put it in State 0.
+ */
+ spin_lock_irqsave(&dev->spinlock, flags);
+ inb(dev->iobase + APCI1500_Z8536_CTRL_REG);
+ outb(0, dev->iobase + APCI1500_Z8536_CTRL_REG);
+ inb(dev->iobase + APCI1500_Z8536_CTRL_REG);
+ outb(0, dev->iobase + APCI1500_Z8536_CTRL_REG);
+ outb(1, dev->iobase + APCI1500_Z8536_CTRL_REG);
+ outb(0, dev->iobase + APCI1500_Z8536_CTRL_REG);
+ spin_unlock_irqrestore(&dev->spinlock, flags);
+
+ /* Disable all Ports and Counter/Timers */
+ z8536_write(dev, 0x00, Z8536_CFG_CTRL_REG);
+
+ /*
+ * Port A is connected to Ditial Input channels 0-7.
+ * Configure the port to allow interrupt detection.
+ */
+ z8536_write(dev, Z8536_PAB_MODE_PTS_BIT |
+ Z8536_PAB_MODE_SB |
+ Z8536_PAB_MODE_PMS_DISABLE,
+ Z8536_PA_MODE_REG);
+ z8536_write(dev, 0xff, Z8536_PB_DPP_REG);
+ z8536_write(dev, 0xff, Z8536_PA_DD_REG);
+
+ /*
+ * Port B is connected to Ditial Input channels 8-13.
+ * Configure the port to allow interrupt detection.
+ *
+ * NOTE: Bits 7 and 6 of Port B are connected to internal
+ * diagnostic signals and bit 7 is inverted.
+ */
+ z8536_write(dev, Z8536_PAB_MODE_PTS_BIT |
+ Z8536_PAB_MODE_SB |
+ Z8536_PAB_MODE_PMS_DISABLE,
+ Z8536_PB_MODE_REG);
+ z8536_write(dev, 0x7f, Z8536_PB_DPP_REG);
+ z8536_write(dev, 0xff, Z8536_PB_DD_REG);
+
+ /*
+ * Not sure what Port C is connected to...
+ */
+ z8536_write(dev, 0x09, Z8536_PC_DPP_REG);
+ z8536_write(dev, 0x0e, Z8536_PC_DD_REG);
+
+ /*
+ * Clear and disable all interrupt sources.
+ *
+ * Just in case, the reset of the Z8536 should have already
+ * done this.
+ */
+ z8536_write(dev, Z8536_CMD_CLR_IP_IUS, Z8536_PA_CMDSTAT_REG);
+ z8536_write(dev, Z8536_CMD_CLR_IE, Z8536_PA_CMDSTAT_REG);
+
+ z8536_write(dev, Z8536_CMD_CLR_IP_IUS, Z8536_PB_CMDSTAT_REG);
+ z8536_write(dev, Z8536_CMD_CLR_IE, Z8536_PB_CMDSTAT_REG);
+
+ z8536_write(dev, Z8536_CMD_CLR_IP_IUS, Z8536_CT_CMDSTAT_REG(0));
+ z8536_write(dev, Z8536_CMD_CLR_IE, Z8536_CT_CMDSTAT_REG(0));
+
+ z8536_write(dev, Z8536_CMD_CLR_IP_IUS, Z8536_CT_CMDSTAT_REG(1));
+ z8536_write(dev, Z8536_CMD_CLR_IE, Z8536_CT_CMDSTAT_REG(1));
+
+ z8536_write(dev, Z8536_CMD_CLR_IP_IUS, Z8536_CT_CMDSTAT_REG(2));
+ z8536_write(dev, Z8536_CMD_CLR_IE, Z8536_CT_CMDSTAT_REG(2));
+
+ /* Disable all interrupts */
+ z8536_write(dev, 0x00, Z8536_INT_CTRL_REG);
+}
+
+static void apci1500_port_enable(struct comedi_device *dev, bool enable)
+{
+ unsigned int cfg;
+
+ cfg = z8536_read(dev, Z8536_CFG_CTRL_REG);
+ if (enable)
+ cfg |= (Z8536_CFG_CTRL_PAE | Z8536_CFG_CTRL_PBE);
+ else
+ cfg &= ~(Z8536_CFG_CTRL_PAE | Z8536_CFG_CTRL_PBE);
+ z8536_write(dev, cfg, Z8536_CFG_CTRL_REG);
+}
+
+static void apci1500_timer_enable(struct comedi_device *dev,
+ unsigned int chan, bool enable)
+{
+ unsigned int bit;
+ unsigned int cfg;
+
+ if (chan == 0)
+ bit = Z8536_CFG_CTRL_CT1E;
+ else if (chan == 1)
+ bit = Z8536_CFG_CTRL_CT2E;
+ else
+ bit = Z8536_CFG_CTRL_PCE_CT3E;
+
+ cfg = z8536_read(dev, Z8536_CFG_CTRL_REG);
+ if (enable) {
+ cfg |= bit;
+ } else {
+ cfg &= ~bit;
+ z8536_write(dev, 0x00, Z8536_CT_CMDSTAT_REG(chan));
+ }
+ z8536_write(dev, cfg, Z8536_CFG_CTRL_REG);
+}
+
+static bool apci1500_ack_irq(struct comedi_device *dev,
+ unsigned int reg)
+{
+ unsigned int val;
+
+ val = z8536_read(dev, reg);
+ if ((val & Z8536_STAT_IE_IP) == Z8536_STAT_IE_IP) {
+ val &= 0x0f; /* preserve any write bits */
+ val |= Z8536_CMD_CLR_IP_IUS;
+ z8536_write(dev, val, reg);
+
+ return true;
+ }
+ return false;
+}
+
+static irqreturn_t apci1500_interrupt(int irq, void *d)
+{
+ struct comedi_device *dev = d;
+ struct apci1500_private *devpriv = dev->private;
+ struct comedi_subdevice *s = dev->read_subdev;
+ unsigned int status = 0;
+ unsigned int val;
+
+ val = inl(devpriv->amcc + AMCC_OP_REG_INTCSR);
+ if (!(val & INTCSR_INTR_ASSERTED))
+ return IRQ_NONE;
+
+ if (apci1500_ack_irq(dev, Z8536_PA_CMDSTAT_REG))
+ status |= 0x01; /* port a event (inputs 0-7) */
+
+ if (apci1500_ack_irq(dev, Z8536_PB_CMDSTAT_REG)) {
+ /* Tests if this is an external error */
+ val = inb(dev->iobase + APCI1500_Z8536_PORTB_REG);
+ val &= 0xc0;
+ if (val) {
+ if (val & 0x80) /* voltage error */
+ status |= 0x40;
+ if (val & 0x40) /* short circuit error */
+ status |= 0x80;
+ } else {
+ status |= 0x02; /* port b event (inputs 8-13) */
+ }
+ }
+
+ /*
+ * NOTE: The 'status' returned by the sample matches the
+ * interrupt mask information from the APCI-1500 Users Manual.
+ *
+ * Mask Meaning
+ * ---------- ------------------------------------------
+ * 0x00000001 Event 1 has occurred
+ * 0x00000010 Event 2 has occurred
+ * 0x00000100 Counter/timer 1 has run down (not implemented)
+ * 0x00001000 Counter/timer 2 has run down (not implemented)
+ * 0x00010000 Counter 3 has run down (not implemented)
+ * 0x00100000 Watchdog has run down (not implemented)
+ * 0x01000000 Voltage error
+ * 0x10000000 Short-circuit error
+ */
+ comedi_buf_write_samples(s, &status, 1);
+ comedi_handle_events(dev, s);
+
+ return IRQ_HANDLED;
+}
+
+static int apci1500_di_cancel(struct comedi_device *dev,
+ struct comedi_subdevice *s)
+{
+ /* Disables the main interrupt on the board */
+ z8536_write(dev, 0x00, Z8536_INT_CTRL_REG);
+
+ /* Disable Ports A & B */
+ apci1500_port_enable(dev, false);
+
+ /* Ack any pending interrupts */
+ apci1500_ack_irq(dev, Z8536_PA_CMDSTAT_REG);
+ apci1500_ack_irq(dev, Z8536_PB_CMDSTAT_REG);
+
+ /* Disable pattern interrupts */
+ z8536_write(dev, Z8536_CMD_CLR_IE, Z8536_PA_CMDSTAT_REG);
+ z8536_write(dev, Z8536_CMD_CLR_IE, Z8536_PB_CMDSTAT_REG);
+
+ /* Enable Ports A & B */
+ apci1500_port_enable(dev, true);
+
+ return 0;
+}
+
+static int apci1500_di_inttrig_start(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ unsigned int trig_num)
+{
+ struct apci1500_private *devpriv = dev->private;
+ struct comedi_cmd *cmd = &s->async->cmd;
+ unsigned int pa_mode = Z8536_PAB_MODE_PMS_DISABLE;
+ unsigned int pb_mode = Z8536_PAB_MODE_PMS_DISABLE;
+ unsigned int pa_trig = trig_num & 0x01;
+ unsigned int pb_trig = (trig_num >> 1) & 0x01;
+ bool valid_trig = false;
+ unsigned int val;
+
+ if (trig_num != cmd->start_arg)
+ return -EINVAL;
+
+ /* Disable Ports A & B */
+ apci1500_port_enable(dev, false);
+
+ /* Set Port A for selected trigger pattern */
+ z8536_write(dev, devpriv->pm[pa_trig] & 0xff, Z8536_PA_PM_REG);
+ z8536_write(dev, devpriv->pt[pa_trig] & 0xff, Z8536_PA_PT_REG);
+ z8536_write(dev, devpriv->pp[pa_trig] & 0xff, Z8536_PA_PP_REG);
+
+ /* Set Port B for selected trigger pattern */
+ z8536_write(dev, (devpriv->pm[pb_trig] >> 8) & 0xff, Z8536_PB_PM_REG);
+ z8536_write(dev, (devpriv->pt[pb_trig] >> 8) & 0xff, Z8536_PB_PT_REG);
+ z8536_write(dev, (devpriv->pp[pb_trig] >> 8) & 0xff, Z8536_PB_PP_REG);
+
+ /* Set Port A trigger mode (if enabled) and enable interrupt */
+ if (devpriv->pm[pa_trig] & 0xff) {
+ pa_mode = pa_trig ? Z8536_PAB_MODE_PMS_AND
+ : Z8536_PAB_MODE_PMS_OR;
+
+ val = z8536_read(dev, Z8536_PA_MODE_REG);
+ val &= ~Z8536_PAB_MODE_PMS_MASK;
+ val |= (pa_mode | Z8536_PAB_MODE_IMO);
+ z8536_write(dev, val, Z8536_PA_MODE_REG);
+
+ z8536_write(dev, Z8536_CMD_SET_IE, Z8536_PA_CMDSTAT_REG);
+
+ valid_trig = true;
+
+ dev_dbg(dev->class_dev,
+ "Port A configured for %s mode pattern detection\n",
+ pa_trig ? "AND" : "OR");
+ }
+
+ /* Set Port B trigger mode (if enabled) and enable interrupt */
+ if (devpriv->pm[pb_trig] & 0xff00) {
+ pb_mode = pb_trig ? Z8536_PAB_MODE_PMS_AND
+ : Z8536_PAB_MODE_PMS_OR;
+
+ val = z8536_read(dev, Z8536_PB_MODE_REG);
+ val &= ~Z8536_PAB_MODE_PMS_MASK;
+ val |= (pb_mode | Z8536_PAB_MODE_IMO);
+ z8536_write(dev, val, Z8536_PB_MODE_REG);
+
+ z8536_write(dev, Z8536_CMD_SET_IE, Z8536_PB_CMDSTAT_REG);
+
+ valid_trig = true;
+
+ dev_dbg(dev->class_dev,
+ "Port B configured for %s mode pattern detection\n",
+ pb_trig ? "AND" : "OR");
+ }
+
+ /* Enable Ports A & B */
+ apci1500_port_enable(dev, true);
+
+ if (!valid_trig) {
+ dev_dbg(dev->class_dev,
+ "digital trigger %d is not configured\n", trig_num);
+ return -EINVAL;
+ }
+
+ /* Authorizes the main interrupt on the board */
+ z8536_write(dev, Z8536_INT_CTRL_MIE | Z8536_INT_CTRL_DLC,
+ Z8536_INT_CTRL_REG);
+
+ return 0;
+}
+
+static int apci1500_di_cmd(struct comedi_device *dev,
+ struct comedi_subdevice *s)
+{
+ s->async->inttrig = apci1500_di_inttrig_start;
+
+ return 0;
+}
+
+static int apci1500_di_cmdtest(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_cmd *cmd)
+{
+ int err = 0;
+
+ /* Step 1 : check if triggers are trivially valid */
+
+ err |= comedi_check_trigger_src(&cmd->start_src, TRIG_INT);
+ err |= comedi_check_trigger_src(&cmd->scan_begin_src, TRIG_EXT);
+ err |= comedi_check_trigger_src(&cmd->convert_src, TRIG_FOLLOW);
+ err |= comedi_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
+ err |= comedi_check_trigger_src(&cmd->stop_src, TRIG_NONE);
+
+ if (err)
+ return 1;
+
+ /* Step 2a : make sure trigger sources are unique */
+ /* Step 2b : and mutually compatible */
+
+ /* Step 3: check if arguments are trivially valid */
+
+ /*
+ * Internal start source triggers:
+ *
+ * 0 AND mode for Port A (digital inputs 0-7)
+ * AND mode for Port B (digital inputs 8-13 and internal signals)
+ *
+ * 1 OR mode for Port A (digital inputs 0-7)
+ * AND mode for Port B (digital inputs 8-13 and internal signals)
+ *
+ * 2 AND mode for Port A (digital inputs 0-7)
+ * OR mode for Port B (digital inputs 8-13 and internal signals)
+ *
+ * 3 OR mode for Port A (digital inputs 0-7)
+ * OR mode for Port B (digital inputs 8-13 and internal signals)
+ */
+ err |= comedi_check_trigger_arg_max(&cmd->start_arg, 3);
+
+ err |= comedi_check_trigger_arg_is(&cmd->scan_begin_arg, 0);
+ err |= comedi_check_trigger_arg_is(&cmd->convert_arg, 0);
+ err |= comedi_check_trigger_arg_is(&cmd->scan_end_arg,
+ cmd->chanlist_len);
+ err |= comedi_check_trigger_arg_is(&cmd->stop_arg, 0);
+
+ if (err)
+ return 3;
+
+ /* Step 4: fix up any arguments */
+
+ /* Step 5: check channel list if it exists */
+
+ return 0;
+}
+
+/*
+ * The pattern-recognition logic must be configured before the digital
+ * input async command is started.
+ *
+ * Digital input channels 0 to 13 can generate interrupts. Channels 14
+ * and 15 are connected to internal board status/diagnostic signals.
+ *
+ * Channel 14 - Voltage error (the external supply is < 5V)
+ * Channel 15 - Short-circuit/overtemperature error
+ *
+ * data[0] : INSN_CONFIG_DIGITAL_TRIG
+ * data[1] : trigger number
+ * 0 = AND mode
+ * 1 = OR mode
+ * data[2] : configuration operation:
+ * COMEDI_DIGITAL_TRIG_DISABLE = no interrupts
+ * COMEDI_DIGITAL_TRIG_ENABLE_EDGES = edge interrupts
+ * COMEDI_DIGITAL_TRIG_ENABLE_LEVELS = level interrupts
+ * data[3] : left-shift for data[4] and data[5]
+ * data[4] : rising-edge/high level channels
+ * data[5] : falling-edge/low level channels
+ */
+static int apci1500_di_cfg_trig(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ struct apci1500_private *devpriv = dev->private;
+ unsigned int trig = data[1];
+ unsigned int shift = data[3];
+ unsigned int hi_mask = data[4] << shift;
+ unsigned int lo_mask = data[5] << shift;
+ unsigned int chan_mask = hi_mask | lo_mask;
+ unsigned int old_mask = (1 << shift) - 1;
+ unsigned int pm = devpriv->pm[trig] & old_mask;
+ unsigned int pt = devpriv->pt[trig] & old_mask;
+ unsigned int pp = devpriv->pp[trig] & old_mask;
+
+ if (trig > 1) {
+ dev_dbg(dev->class_dev,
+ "invalid digital trigger number (0=AND, 1=OR)\n");
+ return -EINVAL;
+ }
+
+ if (chan_mask > 0xffff) {
+ dev_dbg(dev->class_dev, "invalid digital trigger channel\n");
+ return -EINVAL;
+ }
+
+ switch (data[2]) {
+ case COMEDI_DIGITAL_TRIG_DISABLE:
+ /* clear trigger configuration */
+ pm = 0;
+ pt = 0;
+ pp = 0;
+ break;
+ case COMEDI_DIGITAL_TRIG_ENABLE_EDGES:
+ pm |= chan_mask; /* enable channels */
+ pt |= chan_mask; /* enable edge detection */
+ pp |= hi_mask; /* rising-edge channels */
+ pp &= ~lo_mask; /* falling-edge channels */
+ break;
+ case COMEDI_DIGITAL_TRIG_ENABLE_LEVELS:
+ pm |= chan_mask; /* enable channels */
+ pt &= ~chan_mask; /* enable level detection */
+ pp |= hi_mask; /* high level channels */
+ pp &= ~lo_mask; /* low level channels */
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /*
+ * The AND mode trigger can only have one channel (max) enabled
+ * for edge detection.
+ */
+ if (trig == 0) {
+ int ret = 0;
+ unsigned int src;
+
+ src = pt & 0xff;
+ if (src)
+ ret |= comedi_check_trigger_is_unique(src);
+
+ src = (pt >> 8) & 0xff;
+ if (src)
+ ret |= comedi_check_trigger_is_unique(src);
+
+ if (ret) {
+ dev_dbg(dev->class_dev,
+ "invalid AND trigger configuration\n");
+ return ret;
+ }
+ }
+
+ /* save the trigger configuration */
+ devpriv->pm[trig] = pm;
+ devpriv->pt[trig] = pt;
+ devpriv->pp[trig] = pp;
+
+ return insn->n;
+}
+
+static int apci1500_di_insn_config(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ switch (data[0]) {
+ case INSN_CONFIG_DIGITAL_TRIG:
+ return apci1500_di_cfg_trig(dev, s, insn, data);
+ default:
+ return -EINVAL;
+ }
+}
+
+static int apci1500_di_insn_bits(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ struct apci1500_private *devpriv = dev->private;
+
+ data[1] = inw(devpriv->addon + APCI1500_DI_REG);
+
+ return insn->n;
+}
+
+static int apci1500_do_insn_bits(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ struct apci1500_private *devpriv = dev->private;
+
+ if (comedi_dio_update_state(s, data))
+ outw(s->state, devpriv->addon + APCI1500_DO_REG);
+
+ data[1] = s->state;
+
+ return insn->n;
+}
+
+static int apci1500_timer_insn_config(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ struct apci1500_private *devpriv = dev->private;
+ unsigned int chan = CR_CHAN(insn->chanspec);
+ unsigned int val;
+
+ switch (data[0]) {
+ case INSN_CONFIG_ARM:
+ val = data[1] & s->maxdata;
+ z8536_write(dev, val & 0xff, Z8536_CT_RELOAD_LSB_REG(chan));
+ z8536_write(dev, (val >> 8) & 0xff,
+ Z8536_CT_RELOAD_MSB_REG(chan));
+
+ apci1500_timer_enable(dev, chan, true);
+ z8536_write(dev, Z8536_CT_CMDSTAT_GCB,
+ Z8536_CT_CMDSTAT_REG(chan));
+ break;
+ case INSN_CONFIG_DISARM:
+ apci1500_timer_enable(dev, chan, false);
+ break;
+
+ case INSN_CONFIG_GET_COUNTER_STATUS:
+ data[1] = 0;
+ val = z8536_read(dev, Z8536_CT_CMDSTAT_REG(chan));
+ if (val & Z8536_CT_STAT_CIP)
+ data[1] |= COMEDI_COUNTER_COUNTING;
+ if (val & Z8536_CT_CMDSTAT_GCB)
+ data[1] |= COMEDI_COUNTER_ARMED;
+ if (val & Z8536_STAT_IP) {
+ data[1] |= COMEDI_COUNTER_TERMINAL_COUNT;
+ apci1500_ack_irq(dev, Z8536_CT_CMDSTAT_REG(chan));
+ }
+ data[2] = COMEDI_COUNTER_ARMED | COMEDI_COUNTER_COUNTING |
+ COMEDI_COUNTER_TERMINAL_COUNT;
+ break;
+
+ case INSN_CONFIG_SET_COUNTER_MODE:
+ /* Simulate the 8254 timer modes */
+ switch (data[1]) {
+ case I8254_MODE0:
+ /* Interrupt on Terminal Count */
+ val = Z8536_CT_MODE_ECE |
+ Z8536_CT_MODE_DCS_ONESHOT;
+ break;
+ case I8254_MODE1:
+ /* Hardware Retriggerable One-Shot */
+ val = Z8536_CT_MODE_ETE |
+ Z8536_CT_MODE_DCS_ONESHOT;
+ break;
+ case I8254_MODE2:
+ /* Rate Generator */
+ val = Z8536_CT_MODE_CSC |
+ Z8536_CT_MODE_DCS_PULSE;
+ break;
+ case I8254_MODE3:
+ /* Square Wave Mode */
+ val = Z8536_CT_MODE_CSC |
+ Z8536_CT_MODE_DCS_SQRWAVE;
+ break;
+ case I8254_MODE4:
+ /* Software Triggered Strobe */
+ val = Z8536_CT_MODE_REB |
+ Z8536_CT_MODE_DCS_PULSE;
+ break;
+ case I8254_MODE5:
+ /* Hardware Triggered Strobe (watchdog) */
+ val = Z8536_CT_MODE_EOE |
+ Z8536_CT_MODE_ETE |
+ Z8536_CT_MODE_REB |
+ Z8536_CT_MODE_DCS_PULSE;
+ break;
+ default:
+ return -EINVAL;
+ }
+ apci1500_timer_enable(dev, chan, false);
+ z8536_write(dev, val, Z8536_CT_MODE_REG(chan));
+ break;
+
+ case INSN_CONFIG_SET_CLOCK_SRC:
+ if (data[1] > 2)
+ return -EINVAL;
+ devpriv->clk_src = data[1];
+ if (devpriv->clk_src == 2)
+ devpriv->clk_src = 3;
+ outw(devpriv->clk_src, devpriv->addon + APCI1500_CLK_SEL_REG);
+ break;
+ case INSN_CONFIG_GET_CLOCK_SRC:
+ switch (devpriv->clk_src) {
+ case 0:
+ data[1] = 0; /* 111.86 kHz / 2 */
+ data[2] = 17879; /* 17879 ns (approx) */
+ break;
+ case 1:
+ data[1] = 1; /* 3.49 kHz / 2 */
+ data[2] = 573066; /* 573066 ns (approx) */
+ break;
+ case 3:
+ data[1] = 2; /* 1.747 kHz / 2 */
+ data[2] = 1164822; /* 1164822 ns (approx) */
+ break;
+ default:
+ return -EINVAL;
+ }
+ break;
+
+ case INSN_CONFIG_SET_GATE_SRC:
+ if (chan == 0)
+ return -EINVAL;
+
+ val = z8536_read(dev, Z8536_CT_MODE_REG(chan));
+ val &= Z8536_CT_MODE_EGE;
+ if (data[1] == 1)
+ val |= Z8536_CT_MODE_EGE;
+ else if (data[1] > 1)
+ return -EINVAL;
+ z8536_write(dev, val, Z8536_CT_MODE_REG(chan));
+ break;
+ case INSN_CONFIG_GET_GATE_SRC:
+ if (chan == 0)
+ return -EINVAL;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+ return insn->n;
+}
+
+static int apci1500_timer_insn_write(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ unsigned int chan = CR_CHAN(insn->chanspec);
+ unsigned int cmd;
+
+ cmd = z8536_read(dev, Z8536_CT_CMDSTAT_REG(chan));
+ cmd &= Z8536_CT_CMDSTAT_GCB; /* preserve gate */
+ cmd |= Z8536_CT_CMD_TCB; /* set trigger */
+
+ /* software trigger a timer, it only makes sense to do one write */
+ if (insn->n)
+ z8536_write(dev, cmd, Z8536_CT_CMDSTAT_REG(chan));
+
+ return insn->n;
+}
+
+static int apci1500_timer_insn_read(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ unsigned int chan = CR_CHAN(insn->chanspec);
+ unsigned int cmd;
+ unsigned int val;
+ int i;
+
+ cmd = z8536_read(dev, Z8536_CT_CMDSTAT_REG(chan));
+ cmd &= Z8536_CT_CMDSTAT_GCB; /* preserve gate */
+ cmd |= Z8536_CT_CMD_RCC; /* set RCC */
+
+ for (i = 0; i < insn->n; i++) {
+ z8536_write(dev, cmd, Z8536_CT_CMDSTAT_REG(chan));
+
+ val = z8536_read(dev, Z8536_CT_VAL_MSB_REG(chan)) << 8;
+ val |= z8536_read(dev, Z8536_CT_VAL_LSB_REG(chan));
+
+ data[i] = val;
+ }
+
+ return insn->n;
+}
+
+static int apci1500_auto_attach(struct comedi_device *dev,
+ unsigned long context)
+{
+ struct pci_dev *pcidev = comedi_to_pci_dev(dev);
+ struct apci1500_private *devpriv;
+ struct comedi_subdevice *s;
+ int ret;
+
+ devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
+ if (!devpriv)
+ return -ENOMEM;
+
+ ret = comedi_pci_enable(dev);
+ if (ret)
+ return ret;
+
+ dev->iobase = pci_resource_start(pcidev, 1);
+ devpriv->amcc = pci_resource_start(pcidev, 0);
+ devpriv->addon = pci_resource_start(pcidev, 2);
+
+ z8536_reset(dev);
+
+ if (pcidev->irq > 0) {
+ ret = request_irq(pcidev->irq, apci1500_interrupt, IRQF_SHARED,
+ dev->board_name, dev);
+ if (ret == 0)
+ dev->irq = pcidev->irq;
+ }
+
+ ret = comedi_alloc_subdevices(dev, 3);
+ if (ret)
+ return ret;
+
+ /* Digital Input subdevice */
+ s = &dev->subdevices[0];
+ s->type = COMEDI_SUBD_DI;
+ s->subdev_flags = SDF_READABLE;
+ s->n_chan = 16;
+ s->maxdata = 1;
+ s->range_table = &range_digital;
+ s->insn_bits = apci1500_di_insn_bits;
+ if (dev->irq) {
+ dev->read_subdev = s;
+ s->subdev_flags |= SDF_CMD_READ;
+ s->len_chanlist = 1;
+ s->insn_config = apci1500_di_insn_config;
+ s->do_cmdtest = apci1500_di_cmdtest;
+ s->do_cmd = apci1500_di_cmd;
+ s->cancel = apci1500_di_cancel;
+ }
+
+ /* Digital Output subdevice */
+ s = &dev->subdevices[1];
+ s->type = COMEDI_SUBD_DO;
+ s->subdev_flags = SDF_WRITABLE;
+ s->n_chan = 16;
+ s->maxdata = 1;
+ s->range_table = &range_digital;
+ s->insn_bits = apci1500_do_insn_bits;
+
+ /* reset all the digital outputs */
+ outw(0x0, devpriv->addon + APCI1500_DO_REG);
+
+ /* Counter/Timer(Watchdog) subdevice */
+ s = &dev->subdevices[2];
+ s->type = COMEDI_SUBD_TIMER;
+ s->subdev_flags = SDF_WRITABLE | SDF_READABLE;
+ s->n_chan = 3;
+ s->maxdata = 0xffff;
+ s->range_table = &range_unknown;
+ s->insn_config = apci1500_timer_insn_config;
+ s->insn_write = apci1500_timer_insn_write;
+ s->insn_read = apci1500_timer_insn_read;
+
+ /* Enable the PCI interrupt */
+ if (dev->irq) {
+ outl(0x2000 | INTCSR_INBOX_FULL_INT,
+ devpriv->amcc + AMCC_OP_REG_INTCSR);
+ inl(devpriv->amcc + AMCC_OP_REG_IMB1);
+ inl(devpriv->amcc + AMCC_OP_REG_INTCSR);
+ outl(INTCSR_INBOX_INTR_STATUS | 0x2000 | INTCSR_INBOX_FULL_INT,
+ devpriv->amcc + AMCC_OP_REG_INTCSR);
+ }
+
+ return 0;
+}
+
+static void apci1500_detach(struct comedi_device *dev)
+{
+ struct apci1500_private *devpriv = dev->private;
+
+ if (devpriv->amcc)
+ outl(0x0, devpriv->amcc + AMCC_OP_REG_INTCSR);
+ comedi_pci_detach(dev);
+}
+
+static struct comedi_driver apci1500_driver = {
+ .driver_name = "addi_apci_1500",
+ .module = THIS_MODULE,
+ .auto_attach = apci1500_auto_attach,
+ .detach = apci1500_detach,
+};
+
+static int apci1500_pci_probe(struct pci_dev *dev,
+ const struct pci_device_id *id)
+{
+ return comedi_pci_auto_config(dev, &apci1500_driver, id->driver_data);
+}
+
+static const struct pci_device_id apci1500_pci_table[] = {
+ { PCI_DEVICE(PCI_VENDOR_ID_AMCC, 0x80fc) },
+ { 0 }
+};
+MODULE_DEVICE_TABLE(pci, apci1500_pci_table);
+
+static struct pci_driver apci1500_pci_driver = {
+ .name = "addi_apci_1500",
+ .id_table = apci1500_pci_table,
+ .probe = apci1500_pci_probe,
+ .remove = comedi_pci_auto_unconfig,
+};
+module_comedi_pci_driver(apci1500_driver, apci1500_pci_driver);
+
+MODULE_AUTHOR("Comedi http://www.comedi.org");
+MODULE_DESCRIPTION("ADDI-DATA APCI-1500, 16 channel DI / 16 channel DO boards");
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/comedi/drivers/addi_apci_1516.c b/drivers/staging/comedi/drivers/addi_apci_1516.c
new file mode 100644
index 000000000..9c516d1fe
--- /dev/null
+++ b/drivers/staging/comedi/drivers/addi_apci_1516.c
@@ -0,0 +1,225 @@
+/*
+ * addi_apci_1516.c
+ * Copyright (C) 2004,2005 ADDI-DATA GmbH for the source code of this module.
+ * Project manager: Eric Stolz
+ *
+ * ADDI-DATA GmbH
+ * Dieselstrasse 3
+ * D-77833 Ottersweier
+ * Tel: +19(0)7223/9493-0
+ * Fax: +49(0)7223/9493-92
+ * http://www.addi-data.com
+ * info@addi-data.com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ */
+
+#include <linux/module.h>
+
+#include "../comedi_pci.h"
+#include "addi_watchdog.h"
+
+/*
+ * PCI bar 1 I/O Register map - Digital input/output
+ */
+#define APCI1516_DI_REG 0x00
+#define APCI1516_DO_REG 0x04
+
+/*
+ * PCI bar 2 I/O Register map - Watchdog (APCI-1516 and APCI-2016)
+ */
+#define APCI1516_WDOG_REG 0x00
+
+enum apci1516_boardid {
+ BOARD_APCI1016,
+ BOARD_APCI1516,
+ BOARD_APCI2016,
+};
+
+struct apci1516_boardinfo {
+ const char *name;
+ int di_nchan;
+ int do_nchan;
+ int has_wdog;
+};
+
+static const struct apci1516_boardinfo apci1516_boardtypes[] = {
+ [BOARD_APCI1016] = {
+ .name = "apci1016",
+ .di_nchan = 16,
+ },
+ [BOARD_APCI1516] = {
+ .name = "apci1516",
+ .di_nchan = 8,
+ .do_nchan = 8,
+ .has_wdog = 1,
+ },
+ [BOARD_APCI2016] = {
+ .name = "apci2016",
+ .do_nchan = 16,
+ .has_wdog = 1,
+ },
+};
+
+struct apci1516_private {
+ unsigned long wdog_iobase;
+};
+
+static int apci1516_di_insn_bits(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ data[1] = inw(dev->iobase + APCI1516_DI_REG);
+
+ return insn->n;
+}
+
+static int apci1516_do_insn_bits(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ s->state = inw(dev->iobase + APCI1516_DO_REG);
+
+ if (comedi_dio_update_state(s, data))
+ outw(s->state, dev->iobase + APCI1516_DO_REG);
+
+ data[1] = s->state;
+
+ return insn->n;
+}
+
+static int apci1516_reset(struct comedi_device *dev)
+{
+ const struct apci1516_boardinfo *this_board = dev->board_ptr;
+ struct apci1516_private *devpriv = dev->private;
+
+ if (!this_board->has_wdog)
+ return 0;
+
+ outw(0x0, dev->iobase + APCI1516_DO_REG);
+
+ addi_watchdog_reset(devpriv->wdog_iobase);
+
+ return 0;
+}
+
+static int apci1516_auto_attach(struct comedi_device *dev,
+ unsigned long context)
+{
+ struct pci_dev *pcidev = comedi_to_pci_dev(dev);
+ const struct apci1516_boardinfo *this_board = NULL;
+ struct apci1516_private *devpriv;
+ struct comedi_subdevice *s;
+ int ret;
+
+ if (context < ARRAY_SIZE(apci1516_boardtypes))
+ this_board = &apci1516_boardtypes[context];
+ if (!this_board)
+ return -ENODEV;
+ dev->board_ptr = this_board;
+ dev->board_name = this_board->name;
+
+ devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
+ if (!devpriv)
+ return -ENOMEM;
+
+ ret = comedi_pci_enable(dev);
+ if (ret)
+ return ret;
+
+ dev->iobase = pci_resource_start(pcidev, 1);
+ devpriv->wdog_iobase = pci_resource_start(pcidev, 2);
+
+ ret = comedi_alloc_subdevices(dev, 3);
+ if (ret)
+ return ret;
+
+ /* Initialize the digital input subdevice */
+ s = &dev->subdevices[0];
+ if (this_board->di_nchan) {
+ s->type = COMEDI_SUBD_DI;
+ s->subdev_flags = SDF_READABLE;
+ s->n_chan = this_board->di_nchan;
+ s->maxdata = 1;
+ s->range_table = &range_digital;
+ s->insn_bits = apci1516_di_insn_bits;
+ } else {
+ s->type = COMEDI_SUBD_UNUSED;
+ }
+
+ /* Initialize the digital output subdevice */
+ s = &dev->subdevices[1];
+ if (this_board->do_nchan) {
+ s->type = COMEDI_SUBD_DO;
+ s->subdev_flags = SDF_WRITABLE;
+ s->n_chan = this_board->do_nchan;
+ s->maxdata = 1;
+ s->range_table = &range_digital;
+ s->insn_bits = apci1516_do_insn_bits;
+ } else {
+ s->type = COMEDI_SUBD_UNUSED;
+ }
+
+ /* Initialize the watchdog subdevice */
+ s = &dev->subdevices[2];
+ if (this_board->has_wdog) {
+ ret = addi_watchdog_init(s, devpriv->wdog_iobase);
+ if (ret)
+ return ret;
+ } else {
+ s->type = COMEDI_SUBD_UNUSED;
+ }
+
+ apci1516_reset(dev);
+ return 0;
+}
+
+static void apci1516_detach(struct comedi_device *dev)
+{
+ if (dev->iobase)
+ apci1516_reset(dev);
+ comedi_pci_detach(dev);
+}
+
+static struct comedi_driver apci1516_driver = {
+ .driver_name = "addi_apci_1516",
+ .module = THIS_MODULE,
+ .auto_attach = apci1516_auto_attach,
+ .detach = apci1516_detach,
+};
+
+static int apci1516_pci_probe(struct pci_dev *dev,
+ const struct pci_device_id *id)
+{
+ return comedi_pci_auto_config(dev, &apci1516_driver, id->driver_data);
+}
+
+static const struct pci_device_id apci1516_pci_table[] = {
+ { PCI_VDEVICE(ADDIDATA, 0x1000), BOARD_APCI1016 },
+ { PCI_VDEVICE(ADDIDATA, 0x1001), BOARD_APCI1516 },
+ { PCI_VDEVICE(ADDIDATA, 0x1002), BOARD_APCI2016 },
+ { 0 }
+};
+MODULE_DEVICE_TABLE(pci, apci1516_pci_table);
+
+static struct pci_driver apci1516_pci_driver = {
+ .name = "addi_apci_1516",
+ .id_table = apci1516_pci_table,
+ .probe = apci1516_pci_probe,
+ .remove = comedi_pci_auto_unconfig,
+};
+module_comedi_pci_driver(apci1516_driver, apci1516_pci_driver);
+
+MODULE_DESCRIPTION("ADDI-DATA APCI-1016/1516/2016, 16 channel DIO boards");
+MODULE_AUTHOR("Comedi http://www.comedi.org");
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/comedi/drivers/addi_apci_1564.c b/drivers/staging/comedi/drivers/addi_apci_1564.c
new file mode 100644
index 000000000..33e58b9a2
--- /dev/null
+++ b/drivers/staging/comedi/drivers/addi_apci_1564.c
@@ -0,0 +1,593 @@
+/*
+ * addi_apci_1564.c
+ * Copyright (C) 2004,2005 ADDI-DATA GmbH for the source code of this module.
+ *
+ * ADDI-DATA GmbH
+ * Dieselstrasse 3
+ * D-77833 Ottersweier
+ * Tel: +19(0)7223/9493-0
+ * Fax: +49(0)7223/9493-92
+ * http://www.addi-data.com
+ * info@addi-data.com
+ *
+ * This program is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ */
+
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/sched.h>
+
+#include "../comedi_pci.h"
+#include "addi_tcw.h"
+#include "addi_watchdog.h"
+
+/*
+ * PCI BAR 0
+ *
+ * PLD Revision 1.0 I/O Mapping
+ * 0x00 93C76 EEPROM
+ * 0x04 - 0x18 Timer 12-Bit
+ *
+ * PLD Revision 2.x I/O Mapping
+ * 0x00 93C76 EEPROM
+ * 0x04 - 0x14 Digital Input
+ * 0x18 - 0x25 Digital Output
+ * 0x28 - 0x44 Watchdog 8-Bit
+ * 0x48 - 0x64 Timer 12-Bit
+ */
+#define APCI1564_EEPROM_REG 0x00
+#define APCI1564_EEPROM_VCC_STATUS (1 << 8)
+#define APCI1564_EEPROM_TO_REV(x) (((x) >> 4) & 0xf)
+#define APCI1564_EEPROM_DI (1 << 3)
+#define APCI1564_EEPROM_DO (1 << 2)
+#define APCI1564_EEPROM_CS (1 << 1)
+#define APCI1564_EEPROM_CLK (1 << 0)
+#define APCI1564_REV1_TIMER_IOBASE 0x04
+#define APCI1564_REV2_MAIN_IOBASE 0x04
+#define APCI1564_REV2_TIMER_IOBASE 0x48
+
+/*
+ * PCI BAR 1
+ *
+ * PLD Revision 1.0 I/O Mapping
+ * 0x00 - 0x10 Digital Input
+ * 0x14 - 0x20 Digital Output
+ * 0x24 - 0x3c Watchdog 8-Bit
+ *
+ * PLD Revision 2.x I/O Mapping
+ * 0x00 Counter_0
+ * 0x20 Counter_1
+ * 0x30 Counter_3
+ */
+#define APCI1564_REV1_MAIN_IOBASE 0x00
+
+/*
+ * dev->iobase Register Map
+ * PLD Revision 1.0 - PCI BAR 1 + 0x00
+ * PLD Revision 2.x - PCI BAR 0 + 0x04
+ */
+#define APCI1564_DI_REG 0x00
+#define APCI1564_DI_INT_MODE1_REG 0x04
+#define APCI1564_DI_INT_MODE2_REG 0x08
+#define APCI1564_DI_INT_STATUS_REG 0x0c
+#define APCI1564_DI_IRQ_REG 0x10
+#define APCI1564_DO_REG 0x14
+#define APCI1564_DO_INT_CTRL_REG 0x18
+#define APCI1564_DO_INT_STATUS_REG 0x1c
+#define APCI1564_DO_IRQ_REG 0x20
+#define APCI1564_WDOG_REG 0x24
+#define APCI1564_WDOG_RELOAD_REG 0x28
+#define APCI1564_WDOG_TIMEBASE_REG 0x2c
+#define APCI1564_WDOG_CTRL_REG 0x30
+#define APCI1564_WDOG_STATUS_REG 0x34
+#define APCI1564_WDOG_IRQ_REG 0x38
+#define APCI1564_WDOG_WARN_TIMEVAL_REG 0x3c
+#define APCI1564_WDOG_WARN_TIMEBASE_REG 0x40
+
+/*
+ * devpriv->timer Register Map (see addi_tcw.h for register/bit defines)
+ * PLD Revision 1.0 - PCI BAR 0 + 0x04
+ * PLD Revision 2.x - PCI BAR 0 + 0x48
+ */
+
+/*
+ * devpriv->counters Register Map (see addi_tcw.h for register/bit defines)
+ * PLD Revision 2.x - PCI BAR 1 + 0x00
+ */
+#define APCI1564_COUNTER(x) ((x) * 0x20)
+
+struct apci1564_private {
+ unsigned long eeprom; /* base address of EEPROM register */
+ unsigned long timer; /* base address of 12-bit timer */
+ unsigned long counters; /* base address of 32-bit counters */
+ unsigned int mode1; /* riding-edge/high level channels */
+ unsigned int mode2; /* falling-edge/low level channels */
+ unsigned int ctrl; /* interrupt mode OR (edge) . AND (level) */
+ struct task_struct *tsk_current;
+};
+
+#include "addi-data/hwdrv_apci1564.c"
+
+static int apci1564_reset(struct comedi_device *dev)
+{
+ struct apci1564_private *devpriv = dev->private;
+
+ /* Disable the input interrupts and reset status register */
+ outl(0x0, dev->iobase + APCI1564_DI_IRQ_REG);
+ inl(dev->iobase + APCI1564_DI_INT_STATUS_REG);
+ outl(0x0, dev->iobase + APCI1564_DI_INT_MODE1_REG);
+ outl(0x0, dev->iobase + APCI1564_DI_INT_MODE2_REG);
+
+ /* Reset the output channels and disable interrupts */
+ outl(0x0, dev->iobase + APCI1564_DO_REG);
+ outl(0x0, dev->iobase + APCI1564_DO_INT_CTRL_REG);
+
+ /* Reset the watchdog registers */
+ addi_watchdog_reset(dev->iobase + APCI1564_WDOG_REG);
+
+ /* Reset the timer registers */
+ outl(0x0, devpriv->timer + ADDI_TCW_CTRL_REG);
+ outl(0x0, devpriv->timer + ADDI_TCW_RELOAD_REG);
+
+ if (devpriv->counters) {
+ unsigned long iobase = devpriv->counters + ADDI_TCW_CTRL_REG;
+
+ /* Reset the counter registers */
+ outl(0x0, iobase + APCI1564_COUNTER(0));
+ outl(0x0, iobase + APCI1564_COUNTER(1));
+ outl(0x0, iobase + APCI1564_COUNTER(2));
+ }
+
+ return 0;
+}
+
+static irqreturn_t apci1564_interrupt(int irq, void *d)
+{
+ struct comedi_device *dev = d;
+ struct apci1564_private *devpriv = dev->private;
+ struct comedi_subdevice *s = dev->read_subdev;
+ unsigned int status;
+ unsigned int ctrl;
+ unsigned int chan;
+
+ status = inl(dev->iobase + APCI1564_DI_IRQ_REG);
+ if (status & APCI1564_DI_INT_ENABLE) {
+ /* disable the interrupt */
+ outl(status & APCI1564_DI_INT_DISABLE,
+ dev->iobase + APCI1564_DI_IRQ_REG);
+
+ s->state = inl(dev->iobase + APCI1564_DI_INT_STATUS_REG) &
+ 0xffff;
+ comedi_buf_write_samples(s, &s->state, 1);
+ comedi_handle_events(dev, s);
+
+ /* enable the interrupt */
+ outl(status, dev->iobase + APCI1564_DI_IRQ_REG);
+ }
+
+ status = inl(devpriv->timer + ADDI_TCW_IRQ_REG);
+ if (status & 0x01) {
+ /* Disable Timer Interrupt */
+ ctrl = inl(devpriv->timer + ADDI_TCW_CTRL_REG);
+ outl(0x0, devpriv->timer + ADDI_TCW_CTRL_REG);
+
+ /* Send a signal to from kernel to user space */
+ send_sig(SIGIO, devpriv->tsk_current, 0);
+
+ /* Enable Timer Interrupt */
+ outl(ctrl, devpriv->timer + ADDI_TCW_CTRL_REG);
+ }
+
+ if (devpriv->counters) {
+ for (chan = 0; chan < 4; chan++) {
+ unsigned long iobase;
+
+ iobase = devpriv->counters + APCI1564_COUNTER(chan);
+
+ status = inl(iobase + ADDI_TCW_IRQ_REG);
+ if (status & 0x01) {
+ /* Disable Counter Interrupt */
+ ctrl = inl(iobase + ADDI_TCW_CTRL_REG);
+ outl(0x0, iobase + ADDI_TCW_CTRL_REG);
+
+ /* Send a signal to from kernel to user space */
+ send_sig(SIGIO, devpriv->tsk_current, 0);
+
+ /* Enable Counter Interrupt */
+ outl(ctrl, iobase + ADDI_TCW_CTRL_REG);
+ }
+ }
+ }
+
+ return IRQ_HANDLED;
+}
+
+static int apci1564_di_insn_bits(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ data[1] = inl(dev->iobase + APCI1564_DI_REG);
+
+ return insn->n;
+}
+
+static int apci1564_do_insn_bits(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ s->state = inl(dev->iobase + APCI1564_DO_REG);
+
+ if (comedi_dio_update_state(s, data))
+ outl(s->state, dev->iobase + APCI1564_DO_REG);
+
+ data[1] = s->state;
+
+ return insn->n;
+}
+
+static int apci1564_diag_insn_bits(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ data[1] = inl(dev->iobase + APCI1564_DO_INT_STATUS_REG) & 3;
+
+ return insn->n;
+}
+
+/*
+ * Change-Of-State (COS) interrupt configuration
+ *
+ * Channels 0 to 15 are interruptible. These channels can be configured
+ * to generate interrupts based on AND/OR logic for the desired channels.
+ *
+ * OR logic
+ * - reacts to rising or falling edges
+ * - interrupt is generated when any enabled channel
+ * meet the desired interrupt condition
+ *
+ * AND logic
+ * - reacts to changes in level of the selected inputs
+ * - interrupt is generated when all enabled channels
+ * meet the desired interrupt condition
+ * - after an interrupt, a change in level must occur on
+ * the selected inputs to release the IRQ logic
+ *
+ * The COS interrupt must be configured before it can be enabled.
+ *
+ * data[0] : INSN_CONFIG_DIGITAL_TRIG
+ * data[1] : trigger number (= 0)
+ * data[2] : configuration operation:
+ * COMEDI_DIGITAL_TRIG_DISABLE = no interrupts
+ * COMEDI_DIGITAL_TRIG_ENABLE_EDGES = OR (edge) interrupts
+ * COMEDI_DIGITAL_TRIG_ENABLE_LEVELS = AND (level) interrupts
+ * data[3] : left-shift for data[4] and data[5]
+ * data[4] : rising-edge/high level channels
+ * data[5] : falling-edge/low level channels
+ */
+static int apci1564_cos_insn_config(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ struct apci1564_private *devpriv = dev->private;
+ unsigned int shift, oldmask;
+
+ switch (data[0]) {
+ case INSN_CONFIG_DIGITAL_TRIG:
+ if (data[1] != 0)
+ return -EINVAL;
+ shift = data[3];
+ oldmask = (1U << shift) - 1;
+ switch (data[2]) {
+ case COMEDI_DIGITAL_TRIG_DISABLE:
+ devpriv->ctrl = 0;
+ devpriv->mode1 = 0;
+ devpriv->mode2 = 0;
+ outl(0x0, dev->iobase + APCI1564_DI_IRQ_REG);
+ inl(dev->iobase + APCI1564_DI_INT_STATUS_REG);
+ outl(0x0, dev->iobase + APCI1564_DI_INT_MODE1_REG);
+ outl(0x0, dev->iobase + APCI1564_DI_INT_MODE2_REG);
+ break;
+ case COMEDI_DIGITAL_TRIG_ENABLE_EDGES:
+ if (devpriv->ctrl != (APCI1564_DI_INT_ENABLE |
+ APCI1564_DI_INT_OR)) {
+ /* switching to 'OR' mode */
+ devpriv->ctrl = APCI1564_DI_INT_ENABLE |
+ APCI1564_DI_INT_OR;
+ /* wipe old channels */
+ devpriv->mode1 = 0;
+ devpriv->mode2 = 0;
+ } else {
+ /* preserve unspecified channels */
+ devpriv->mode1 &= oldmask;
+ devpriv->mode2 &= oldmask;
+ }
+ /* configure specified channels */
+ devpriv->mode1 |= data[4] << shift;
+ devpriv->mode2 |= data[5] << shift;
+ break;
+ case COMEDI_DIGITAL_TRIG_ENABLE_LEVELS:
+ if (devpriv->ctrl != (APCI1564_DI_INT_ENABLE |
+ APCI1564_DI_INT_AND)) {
+ /* switching to 'AND' mode */
+ devpriv->ctrl = APCI1564_DI_INT_ENABLE |
+ APCI1564_DI_INT_AND;
+ /* wipe old channels */
+ devpriv->mode1 = 0;
+ devpriv->mode2 = 0;
+ } else {
+ /* preserve unspecified channels */
+ devpriv->mode1 &= oldmask;
+ devpriv->mode2 &= oldmask;
+ }
+ /* configure specified channels */
+ devpriv->mode1 |= data[4] << shift;
+ devpriv->mode2 |= data[5] << shift;
+ break;
+ default:
+ return -EINVAL;
+ }
+ break;
+ default:
+ return -EINVAL;
+ }
+ return insn->n;
+}
+
+static int apci1564_cos_insn_bits(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ data[1] = s->state;
+
+ return 0;
+}
+
+static int apci1564_cos_cmdtest(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_cmd *cmd)
+{
+ int err = 0;
+
+ /* Step 1 : check if triggers are trivially valid */
+
+ err |= comedi_check_trigger_src(&cmd->start_src, TRIG_NOW);
+ err |= comedi_check_trigger_src(&cmd->scan_begin_src, TRIG_EXT);
+ err |= comedi_check_trigger_src(&cmd->convert_src, TRIG_FOLLOW);
+ err |= comedi_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
+ err |= comedi_check_trigger_src(&cmd->stop_src, TRIG_NONE);
+
+ if (err)
+ return 1;
+
+ /* Step 2a : make sure trigger sources are unique */
+ /* Step 2b : and mutually compatible */
+
+ /* Step 3: check if arguments are trivially valid */
+
+ err |= comedi_check_trigger_arg_is(&cmd->start_arg, 0);
+ err |= comedi_check_trigger_arg_is(&cmd->scan_begin_arg, 0);
+ err |= comedi_check_trigger_arg_is(&cmd->convert_arg, 0);
+ err |= comedi_check_trigger_arg_is(&cmd->scan_end_arg,
+ cmd->chanlist_len);
+ err |= comedi_check_trigger_arg_is(&cmd->stop_arg, 0);
+
+ if (err)
+ return 3;
+
+ /* Step 4: fix up any arguments */
+
+ /* Step 5: check channel list if it exists */
+
+ return 0;
+}
+
+/*
+ * Change-Of-State (COS) 'do_cmd' operation
+ *
+ * Enable the COS interrupt as configured by apci1564_cos_insn_config().
+ */
+static int apci1564_cos_cmd(struct comedi_device *dev,
+ struct comedi_subdevice *s)
+{
+ struct apci1564_private *devpriv = dev->private;
+
+ if (!devpriv->ctrl) {
+ dev_warn(dev->class_dev,
+ "Interrupts disabled due to mode configuration!\n");
+ return -EINVAL;
+ }
+
+ outl(devpriv->mode1, dev->iobase + APCI1564_DI_INT_MODE1_REG);
+ outl(devpriv->mode2, dev->iobase + APCI1564_DI_INT_MODE2_REG);
+ outl(devpriv->ctrl, dev->iobase + APCI1564_DI_IRQ_REG);
+
+ return 0;
+}
+
+static int apci1564_cos_cancel(struct comedi_device *dev,
+ struct comedi_subdevice *s)
+{
+ outl(0x0, dev->iobase + APCI1564_DI_IRQ_REG);
+ inl(dev->iobase + APCI1564_DI_INT_STATUS_REG);
+ outl(0x0, dev->iobase + APCI1564_DI_INT_MODE1_REG);
+ outl(0x0, dev->iobase + APCI1564_DI_INT_MODE2_REG);
+
+ return 0;
+}
+
+static int apci1564_auto_attach(struct comedi_device *dev,
+ unsigned long context_unused)
+{
+ struct pci_dev *pcidev = comedi_to_pci_dev(dev);
+ struct apci1564_private *devpriv;
+ struct comedi_subdevice *s;
+ unsigned int val;
+ int ret;
+
+ devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
+ if (!devpriv)
+ return -ENOMEM;
+
+ ret = comedi_pci_enable(dev);
+ if (ret)
+ return ret;
+
+ /* read the EEPROM register and check the I/O map revision */
+ devpriv->eeprom = pci_resource_start(pcidev, 0);
+ val = inl(devpriv->eeprom + APCI1564_EEPROM_REG);
+ if (APCI1564_EEPROM_TO_REV(val) == 0) {
+ /* PLD Revision 1.0 I/O Mapping */
+ dev->iobase = pci_resource_start(pcidev, 1) +
+ APCI1564_REV1_MAIN_IOBASE;
+ devpriv->timer = devpriv->eeprom + APCI1564_REV1_TIMER_IOBASE;
+ } else {
+ /* PLD Revision 2.x I/O Mapping */
+ dev->iobase = devpriv->eeprom + APCI1564_REV2_MAIN_IOBASE;
+ devpriv->timer = devpriv->eeprom + APCI1564_REV2_TIMER_IOBASE;
+ devpriv->counters = pci_resource_start(pcidev, 1);
+ }
+
+ apci1564_reset(dev);
+
+ if (pcidev->irq > 0) {
+ ret = request_irq(pcidev->irq, apci1564_interrupt, IRQF_SHARED,
+ dev->board_name, dev);
+ if (ret == 0)
+ dev->irq = pcidev->irq;
+ }
+
+ ret = comedi_alloc_subdevices(dev, 7);
+ if (ret)
+ return ret;
+
+ /* Allocate and Initialise DI Subdevice Structures */
+ s = &dev->subdevices[0];
+ s->type = COMEDI_SUBD_DI;
+ s->subdev_flags = SDF_READABLE;
+ s->n_chan = 32;
+ s->maxdata = 1;
+ s->range_table = &range_digital;
+ s->insn_bits = apci1564_di_insn_bits;
+
+ /* Allocate and Initialise DO Subdevice Structures */
+ s = &dev->subdevices[1];
+ s->type = COMEDI_SUBD_DO;
+ s->subdev_flags = SDF_WRITABLE;
+ s->n_chan = 32;
+ s->maxdata = 1;
+ s->range_table = &range_digital;
+ s->insn_bits = apci1564_do_insn_bits;
+
+ /* Change-Of-State (COS) interrupt subdevice */
+ s = &dev->subdevices[2];
+ if (dev->irq) {
+ dev->read_subdev = s;
+ s->type = COMEDI_SUBD_DI;
+ s->subdev_flags = SDF_READABLE | SDF_CMD_READ;
+ s->n_chan = 1;
+ s->maxdata = 1;
+ s->range_table = &range_digital;
+ s->len_chanlist = 1;
+ s->insn_config = apci1564_cos_insn_config;
+ s->insn_bits = apci1564_cos_insn_bits;
+ s->do_cmdtest = apci1564_cos_cmdtest;
+ s->do_cmd = apci1564_cos_cmd;
+ s->cancel = apci1564_cos_cancel;
+ } else {
+ s->type = COMEDI_SUBD_UNUSED;
+ }
+
+ /* Timer subdevice */
+ s = &dev->subdevices[3];
+ s->type = COMEDI_SUBD_TIMER;
+ s->subdev_flags = SDF_WRITABLE | SDF_READABLE;
+ s->n_chan = 1;
+ s->maxdata = 0x0fff;
+ s->range_table = &range_digital;
+ s->insn_config = apci1564_timer_insn_config;
+ s->insn_write = apci1564_timer_insn_write;
+ s->insn_read = apci1564_timer_insn_read;
+
+ /* Counter subdevice */
+ s = &dev->subdevices[4];
+ if (devpriv->counters) {
+ s->type = COMEDI_SUBD_COUNTER;
+ s->subdev_flags = SDF_WRITABLE | SDF_READABLE | SDF_LSAMPL;
+ s->n_chan = 3;
+ s->maxdata = 0xffffffff;
+ s->range_table = &range_digital;
+ s->insn_config = apci1564_counter_insn_config;
+ s->insn_write = apci1564_counter_insn_write;
+ s->insn_read = apci1564_counter_insn_read;
+ } else {
+ s->type = COMEDI_SUBD_UNUSED;
+ }
+
+ /* Initialize the watchdog subdevice */
+ s = &dev->subdevices[5];
+ ret = addi_watchdog_init(s, dev->iobase + APCI1564_WDOG_REG);
+ if (ret)
+ return ret;
+
+ /* Initialize the diagnostic status subdevice */
+ s = &dev->subdevices[6];
+ s->type = COMEDI_SUBD_DI;
+ s->subdev_flags = SDF_READABLE;
+ s->n_chan = 2;
+ s->maxdata = 1;
+ s->range_table = &range_digital;
+ s->insn_bits = apci1564_diag_insn_bits;
+
+ return 0;
+}
+
+static void apci1564_detach(struct comedi_device *dev)
+{
+ if (dev->iobase)
+ apci1564_reset(dev);
+ comedi_pci_detach(dev);
+}
+
+static struct comedi_driver apci1564_driver = {
+ .driver_name = "addi_apci_1564",
+ .module = THIS_MODULE,
+ .auto_attach = apci1564_auto_attach,
+ .detach = apci1564_detach,
+};
+
+static int apci1564_pci_probe(struct pci_dev *dev,
+ const struct pci_device_id *id)
+{
+ return comedi_pci_auto_config(dev, &apci1564_driver, id->driver_data);
+}
+
+static const struct pci_device_id apci1564_pci_table[] = {
+ { PCI_DEVICE(PCI_VENDOR_ID_ADDIDATA, 0x1006) },
+ { 0 }
+};
+MODULE_DEVICE_TABLE(pci, apci1564_pci_table);
+
+static struct pci_driver apci1564_pci_driver = {
+ .name = "addi_apci_1564",
+ .id_table = apci1564_pci_table,
+ .probe = apci1564_pci_probe,
+ .remove = comedi_pci_auto_unconfig,
+};
+module_comedi_pci_driver(apci1564_driver, apci1564_pci_driver);
+
+MODULE_AUTHOR("Comedi http://www.comedi.org");
+MODULE_DESCRIPTION("ADDI-DATA APCI-1564, 32 channel DI / 32 channel DO boards");
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/comedi/drivers/addi_apci_16xx.c b/drivers/staging/comedi/drivers/addi_apci_16xx.c
new file mode 100644
index 000000000..c63133a12
--- /dev/null
+++ b/drivers/staging/comedi/drivers/addi_apci_16xx.c
@@ -0,0 +1,187 @@
+/*
+ * addi_apci_16xx.c
+ * Copyright (C) 2004,2005 ADDI-DATA GmbH for the source code of this module.
+ * Project manager: S. Weber
+ *
+ * ADDI-DATA GmbH
+ * Dieselstrasse 3
+ * D-77833 Ottersweier
+ * Tel: +19(0)7223/9493-0
+ * Fax: +49(0)7223/9493-92
+ * http://www.addi-data.com
+ * info@addi-data.com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ */
+
+#include <linux/module.h>
+
+#include "../comedi_pci.h"
+
+/*
+ * Register I/O map
+ */
+#define APCI16XX_IN_REG(x) (((x) * 4) + 0x08)
+#define APCI16XX_OUT_REG(x) (((x) * 4) + 0x14)
+#define APCI16XX_DIR_REG(x) (((x) * 4) + 0x20)
+
+enum apci16xx_boardid {
+ BOARD_APCI1648,
+ BOARD_APCI1696,
+};
+
+struct apci16xx_boardinfo {
+ const char *name;
+ int n_chan;
+};
+
+static const struct apci16xx_boardinfo apci16xx_boardtypes[] = {
+ [BOARD_APCI1648] = {
+ .name = "apci1648",
+ .n_chan = 48, /* 2 subdevices */
+ },
+ [BOARD_APCI1696] = {
+ .name = "apci1696",
+ .n_chan = 96, /* 3 subdevices */
+ },
+};
+
+static int apci16xx_insn_config(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ unsigned int chan = CR_CHAN(insn->chanspec);
+ unsigned int mask;
+ int ret;
+
+ if (chan < 8)
+ mask = 0x000000ff;
+ else if (chan < 16)
+ mask = 0x0000ff00;
+ else if (chan < 24)
+ mask = 0x00ff0000;
+ else
+ mask = 0xff000000;
+
+ ret = comedi_dio_insn_config(dev, s, insn, data, mask);
+ if (ret)
+ return ret;
+
+ outl(s->io_bits, dev->iobase + APCI16XX_DIR_REG(s->index));
+
+ return insn->n;
+}
+
+static int apci16xx_dio_insn_bits(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ if (comedi_dio_update_state(s, data))
+ outl(s->state, dev->iobase + APCI16XX_OUT_REG(s->index));
+
+ data[1] = inl(dev->iobase + APCI16XX_IN_REG(s->index));
+
+ return insn->n;
+}
+
+static int apci16xx_auto_attach(struct comedi_device *dev,
+ unsigned long context)
+{
+ struct pci_dev *pcidev = comedi_to_pci_dev(dev);
+ const struct apci16xx_boardinfo *board = NULL;
+ struct comedi_subdevice *s;
+ unsigned int n_subdevs;
+ unsigned int last;
+ int i;
+ int ret;
+
+ if (context < ARRAY_SIZE(apci16xx_boardtypes))
+ board = &apci16xx_boardtypes[context];
+ if (!board)
+ return -ENODEV;
+ dev->board_ptr = board;
+ dev->board_name = board->name;
+
+ ret = comedi_pci_enable(dev);
+ if (ret)
+ return ret;
+
+ dev->iobase = pci_resource_start(pcidev, 0);
+
+ /*
+ * Work out the nubmer of subdevices needed to support all the
+ * digital i/o channels on the board. Each subdevice supports
+ * up to 32 channels.
+ */
+ n_subdevs = board->n_chan / 32;
+ if ((n_subdevs * 32) < board->n_chan) {
+ last = board->n_chan - (n_subdevs * 32);
+ n_subdevs++;
+ } else {
+ last = 0;
+ }
+
+ ret = comedi_alloc_subdevices(dev, n_subdevs);
+ if (ret)
+ return ret;
+
+ /* Initialize the TTL digital i/o subdevices */
+ for (i = 0; i < n_subdevs; i++) {
+ s = &dev->subdevices[i];
+ s->type = COMEDI_SUBD_DIO;
+ s->subdev_flags = SDF_WRITABLE | SDF_READABLE;
+ s->n_chan = ((i * 32) < board->n_chan) ? 32 : last;
+ s->maxdata = 1;
+ s->range_table = &range_digital;
+ s->insn_config = apci16xx_insn_config;
+ s->insn_bits = apci16xx_dio_insn_bits;
+
+ /* Default all channels to inputs */
+ s->io_bits = 0;
+ outl(s->io_bits, dev->iobase + APCI16XX_DIR_REG(i));
+ }
+
+ return 0;
+}
+
+static struct comedi_driver apci16xx_driver = {
+ .driver_name = "addi_apci_16xx",
+ .module = THIS_MODULE,
+ .auto_attach = apci16xx_auto_attach,
+ .detach = comedi_pci_detach,
+};
+
+static int apci16xx_pci_probe(struct pci_dev *dev,
+ const struct pci_device_id *id)
+{
+ return comedi_pci_auto_config(dev, &apci16xx_driver, id->driver_data);
+}
+
+static const struct pci_device_id apci16xx_pci_table[] = {
+ { PCI_VDEVICE(ADDIDATA, 0x1009), BOARD_APCI1648 },
+ { PCI_VDEVICE(ADDIDATA, 0x100a), BOARD_APCI1696 },
+ { 0 }
+};
+MODULE_DEVICE_TABLE(pci, apci16xx_pci_table);
+
+static struct pci_driver apci16xx_pci_driver = {
+ .name = "addi_apci_16xx",
+ .id_table = apci16xx_pci_table,
+ .probe = apci16xx_pci_probe,
+ .remove = comedi_pci_auto_unconfig,
+};
+module_comedi_pci_driver(apci16xx_driver, apci16xx_pci_driver);
+
+MODULE_DESCRIPTION("ADDI-DATA APCI-1648/1696, TTL I/O boards");
+MODULE_AUTHOR("Comedi http://www.comedi.org");
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/comedi/drivers/addi_apci_2032.c b/drivers/staging/comedi/drivers/addi_apci_2032.c
new file mode 100644
index 000000000..ad715253b
--- /dev/null
+++ b/drivers/staging/comedi/drivers/addi_apci_2032.c
@@ -0,0 +1,339 @@
+/*
+ * addi_apci_2032.c
+ * Copyright (C) 2004,2005 ADDI-DATA GmbH for the source code of this module.
+ * Project manager: Eric Stolz
+ *
+ * ADDI-DATA GmbH
+ * Dieselstrasse 3
+ * D-77833 Ottersweier
+ * Tel: +19(0)7223/9493-0
+ * Fax: +49(0)7223/9493-92
+ * http://www.addi-data.com
+ * info@addi-data.com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ */
+
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/slab.h>
+
+#include "../comedi_pci.h"
+#include "addi_watchdog.h"
+
+/*
+ * PCI bar 1 I/O Register map
+ */
+#define APCI2032_DO_REG 0x00
+#define APCI2032_INT_CTRL_REG 0x04
+#define APCI2032_INT_CTRL_VCC_ENA (1 << 0)
+#define APCI2032_INT_CTRL_CC_ENA (1 << 1)
+#define APCI2032_INT_STATUS_REG 0x08
+#define APCI2032_INT_STATUS_VCC (1 << 0)
+#define APCI2032_INT_STATUS_CC (1 << 1)
+#define APCI2032_STATUS_REG 0x0c
+#define APCI2032_STATUS_IRQ (1 << 0)
+#define APCI2032_WDOG_REG 0x10
+
+struct apci2032_int_private {
+ spinlock_t spinlock;
+ bool active;
+ unsigned char enabled_isns;
+};
+
+static int apci2032_do_insn_bits(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ s->state = inl(dev->iobase + APCI2032_DO_REG);
+
+ if (comedi_dio_update_state(s, data))
+ outl(s->state, dev->iobase + APCI2032_DO_REG);
+
+ data[1] = s->state;
+
+ return insn->n;
+}
+
+static int apci2032_int_insn_bits(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ data[1] = inl(dev->iobase + APCI2032_INT_STATUS_REG) & 3;
+ return insn->n;
+}
+
+static void apci2032_int_stop(struct comedi_device *dev,
+ struct comedi_subdevice *s)
+{
+ struct apci2032_int_private *subpriv = s->private;
+
+ subpriv->active = false;
+ subpriv->enabled_isns = 0;
+ outl(0x0, dev->iobase + APCI2032_INT_CTRL_REG);
+}
+
+static int apci2032_int_cmdtest(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_cmd *cmd)
+{
+ int err = 0;
+
+ /* Step 1 : check if triggers are trivially valid */
+
+ err |= comedi_check_trigger_src(&cmd->start_src, TRIG_NOW);
+ err |= comedi_check_trigger_src(&cmd->scan_begin_src, TRIG_EXT);
+ err |= comedi_check_trigger_src(&cmd->convert_src, TRIG_NOW);
+ err |= comedi_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
+ err |= comedi_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE);
+
+ if (err)
+ return 1;
+
+ /* Step 2a : make sure trigger sources are unique */
+ err |= comedi_check_trigger_is_unique(cmd->stop_src);
+
+ /* Step 2b : and mutually compatible */
+
+ if (err)
+ return 2;
+
+ /* Step 3: check if arguments are trivially valid */
+
+ err |= comedi_check_trigger_arg_is(&cmd->start_arg, 0);
+ err |= comedi_check_trigger_arg_is(&cmd->scan_begin_arg, 0);
+ err |= comedi_check_trigger_arg_is(&cmd->convert_arg, 0);
+ err |= comedi_check_trigger_arg_is(&cmd->scan_end_arg,
+ cmd->chanlist_len);
+ if (cmd->stop_src == TRIG_COUNT)
+ err |= comedi_check_trigger_arg_min(&cmd->stop_arg, 1);
+ else /* TRIG_NONE */
+ err |= comedi_check_trigger_arg_is(&cmd->stop_arg, 0);
+
+ if (err)
+ return 3;
+
+ /* Step 4: fix up any arguments */
+
+ /* Step 5: check channel list if it exists */
+
+ return 0;
+}
+
+static int apci2032_int_cmd(struct comedi_device *dev,
+ struct comedi_subdevice *s)
+{
+ struct comedi_cmd *cmd = &s->async->cmd;
+ struct apci2032_int_private *subpriv = s->private;
+ unsigned char enabled_isns;
+ unsigned int n;
+ unsigned long flags;
+
+ enabled_isns = 0;
+ for (n = 0; n < cmd->chanlist_len; n++)
+ enabled_isns |= 1 << CR_CHAN(cmd->chanlist[n]);
+
+ spin_lock_irqsave(&subpriv->spinlock, flags);
+
+ subpriv->enabled_isns = enabled_isns;
+ subpriv->active = true;
+ outl(enabled_isns, dev->iobase + APCI2032_INT_CTRL_REG);
+
+ spin_unlock_irqrestore(&subpriv->spinlock, flags);
+
+ return 0;
+}
+
+static int apci2032_int_cancel(struct comedi_device *dev,
+ struct comedi_subdevice *s)
+{
+ struct apci2032_int_private *subpriv = s->private;
+ unsigned long flags;
+
+ spin_lock_irqsave(&subpriv->spinlock, flags);
+ if (subpriv->active)
+ apci2032_int_stop(dev, s);
+ spin_unlock_irqrestore(&subpriv->spinlock, flags);
+
+ return 0;
+}
+
+static irqreturn_t apci2032_interrupt(int irq, void *d)
+{
+ struct comedi_device *dev = d;
+ struct comedi_subdevice *s = dev->read_subdev;
+ struct comedi_cmd *cmd = &s->async->cmd;
+ struct apci2032_int_private *subpriv;
+ unsigned int val;
+
+ if (!dev->attached)
+ return IRQ_NONE;
+
+ /* Check if VCC OR CC interrupt has occurred */
+ val = inl(dev->iobase + APCI2032_STATUS_REG) & APCI2032_STATUS_IRQ;
+ if (!val)
+ return IRQ_NONE;
+
+ subpriv = s->private;
+ spin_lock(&subpriv->spinlock);
+
+ val = inl(dev->iobase + APCI2032_INT_STATUS_REG) & 3;
+ /* Disable triggered interrupt sources. */
+ outl(~val & 3, dev->iobase + APCI2032_INT_CTRL_REG);
+ /*
+ * Note: We don't reenable the triggered interrupt sources because they
+ * are level-sensitive, hardware error status interrupt sources and
+ * they'd keep triggering interrupts repeatedly.
+ */
+
+ if (subpriv->active && (val & subpriv->enabled_isns) != 0) {
+ unsigned short bits = 0;
+ int i;
+
+ /* Bits in scan data correspond to indices in channel list. */
+ for (i = 0; i < cmd->chanlist_len; i++) {
+ unsigned int chan = CR_CHAN(cmd->chanlist[i]);
+
+ if (val & (1 << chan))
+ bits |= (1 << i);
+ }
+
+ comedi_buf_write_samples(s, &bits, 1);
+
+ if (cmd->stop_src == TRIG_COUNT &&
+ s->async->scans_done >= cmd->stop_arg)
+ s->async->events |= COMEDI_CB_EOA;
+ }
+
+ spin_unlock(&subpriv->spinlock);
+
+ comedi_handle_events(dev, s);
+
+ return IRQ_HANDLED;
+}
+
+static int apci2032_reset(struct comedi_device *dev)
+{
+ outl(0x0, dev->iobase + APCI2032_DO_REG);
+ outl(0x0, dev->iobase + APCI2032_INT_CTRL_REG);
+
+ addi_watchdog_reset(dev->iobase + APCI2032_WDOG_REG);
+
+ return 0;
+}
+
+static int apci2032_auto_attach(struct comedi_device *dev,
+ unsigned long context_unused)
+{
+ struct pci_dev *pcidev = comedi_to_pci_dev(dev);
+ struct comedi_subdevice *s;
+ int ret;
+
+ ret = comedi_pci_enable(dev);
+ if (ret)
+ return ret;
+ dev->iobase = pci_resource_start(pcidev, 1);
+ apci2032_reset(dev);
+
+ if (pcidev->irq > 0) {
+ ret = request_irq(pcidev->irq, apci2032_interrupt,
+ IRQF_SHARED, dev->board_name, dev);
+ if (ret == 0)
+ dev->irq = pcidev->irq;
+ }
+
+ ret = comedi_alloc_subdevices(dev, 3);
+ if (ret)
+ return ret;
+
+ /* Initialize the digital output subdevice */
+ s = &dev->subdevices[0];
+ s->type = COMEDI_SUBD_DO;
+ s->subdev_flags = SDF_WRITABLE;
+ s->n_chan = 32;
+ s->maxdata = 1;
+ s->range_table = &range_digital;
+ s->insn_bits = apci2032_do_insn_bits;
+
+ /* Initialize the watchdog subdevice */
+ s = &dev->subdevices[1];
+ ret = addi_watchdog_init(s, dev->iobase + APCI2032_WDOG_REG);
+ if (ret)
+ return ret;
+
+ /* Initialize the interrupt subdevice */
+ s = &dev->subdevices[2];
+ s->type = COMEDI_SUBD_DI;
+ s->subdev_flags = SDF_READABLE;
+ s->n_chan = 2;
+ s->maxdata = 1;
+ s->range_table = &range_digital;
+ s->insn_bits = apci2032_int_insn_bits;
+ if (dev->irq) {
+ struct apci2032_int_private *subpriv;
+
+ dev->read_subdev = s;
+ subpriv = kzalloc(sizeof(*subpriv), GFP_KERNEL);
+ if (!subpriv)
+ return -ENOMEM;
+ spin_lock_init(&subpriv->spinlock);
+ s->private = subpriv;
+ s->subdev_flags = SDF_READABLE | SDF_CMD_READ | SDF_PACKED;
+ s->len_chanlist = 2;
+ s->do_cmdtest = apci2032_int_cmdtest;
+ s->do_cmd = apci2032_int_cmd;
+ s->cancel = apci2032_int_cancel;
+ }
+
+ return 0;
+}
+
+static void apci2032_detach(struct comedi_device *dev)
+{
+ if (dev->iobase)
+ apci2032_reset(dev);
+ comedi_pci_detach(dev);
+ if (dev->read_subdev)
+ kfree(dev->read_subdev->private);
+}
+
+static struct comedi_driver apci2032_driver = {
+ .driver_name = "addi_apci_2032",
+ .module = THIS_MODULE,
+ .auto_attach = apci2032_auto_attach,
+ .detach = apci2032_detach,
+};
+
+static int apci2032_pci_probe(struct pci_dev *dev,
+ const struct pci_device_id *id)
+{
+ return comedi_pci_auto_config(dev, &apci2032_driver, id->driver_data);
+}
+
+static const struct pci_device_id apci2032_pci_table[] = {
+ { PCI_DEVICE(PCI_VENDOR_ID_ADDIDATA, 0x1004) },
+ { 0 }
+};
+MODULE_DEVICE_TABLE(pci, apci2032_pci_table);
+
+static struct pci_driver apci2032_pci_driver = {
+ .name = "addi_apci_2032",
+ .id_table = apci2032_pci_table,
+ .probe = apci2032_pci_probe,
+ .remove = comedi_pci_auto_unconfig,
+};
+module_comedi_pci_driver(apci2032_driver, apci2032_pci_driver);
+
+MODULE_AUTHOR("Comedi http://www.comedi.org");
+MODULE_DESCRIPTION("ADDI-DATA APCI-2032, 32 channel DO boards");
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/comedi/drivers/addi_apci_2200.c b/drivers/staging/comedi/drivers/addi_apci_2200.c
new file mode 100644
index 000000000..2b382a52d
--- /dev/null
+++ b/drivers/staging/comedi/drivers/addi_apci_2200.c
@@ -0,0 +1,152 @@
+/*
+ * addi_apci_2200.c
+ * Copyright (C) 2004,2005 ADDI-DATA GmbH for the source code of this module.
+ * Project manager: Eric Stolz
+ *
+ * ADDI-DATA GmbH
+ * Dieselstrasse 3
+ * D-77833 Ottersweier
+ * Tel: +19(0)7223/9493-0
+ * Fax: +49(0)7223/9493-92
+ * http://www.addi-data.com
+ * info@addi-data.com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ */
+
+#include <linux/module.h>
+
+#include "../comedi_pci.h"
+#include "addi_watchdog.h"
+
+/*
+ * I/O Register Map
+ */
+#define APCI2200_DI_REG 0x00
+#define APCI2200_DO_REG 0x04
+#define APCI2200_WDOG_REG 0x08
+
+static int apci2200_di_insn_bits(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ data[1] = inw(dev->iobase + APCI2200_DI_REG);
+
+ return insn->n;
+}
+
+static int apci2200_do_insn_bits(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ s->state = inw(dev->iobase + APCI2200_DO_REG);
+
+ if (comedi_dio_update_state(s, data))
+ outw(s->state, dev->iobase + APCI2200_DO_REG);
+
+ data[1] = s->state;
+
+ return insn->n;
+}
+
+static int apci2200_reset(struct comedi_device *dev)
+{
+ outw(0x0, dev->iobase + APCI2200_DO_REG);
+
+ addi_watchdog_reset(dev->iobase + APCI2200_WDOG_REG);
+
+ return 0;
+}
+
+static int apci2200_auto_attach(struct comedi_device *dev,
+ unsigned long context_unused)
+{
+ struct pci_dev *pcidev = comedi_to_pci_dev(dev);
+ struct comedi_subdevice *s;
+ int ret;
+
+ ret = comedi_pci_enable(dev);
+ if (ret)
+ return ret;
+
+ dev->iobase = pci_resource_start(pcidev, 1);
+
+ ret = comedi_alloc_subdevices(dev, 3);
+ if (ret)
+ return ret;
+
+ /* Initialize the digital input subdevice */
+ s = &dev->subdevices[0];
+ s->type = COMEDI_SUBD_DI;
+ s->subdev_flags = SDF_READABLE;
+ s->n_chan = 8;
+ s->maxdata = 1;
+ s->range_table = &range_digital;
+ s->insn_bits = apci2200_di_insn_bits;
+
+ /* Initialize the digital output subdevice */
+ s = &dev->subdevices[1];
+ s->type = COMEDI_SUBD_DO;
+ s->subdev_flags = SDF_WRITABLE;
+ s->n_chan = 16;
+ s->maxdata = 1;
+ s->range_table = &range_digital;
+ s->insn_bits = apci2200_do_insn_bits;
+
+ /* Initialize the watchdog subdevice */
+ s = &dev->subdevices[2];
+ ret = addi_watchdog_init(s, dev->iobase + APCI2200_WDOG_REG);
+ if (ret)
+ return ret;
+
+ apci2200_reset(dev);
+ return 0;
+}
+
+static void apci2200_detach(struct comedi_device *dev)
+{
+ if (dev->iobase)
+ apci2200_reset(dev);
+ comedi_pci_detach(dev);
+}
+
+static struct comedi_driver apci2200_driver = {
+ .driver_name = "addi_apci_2200",
+ .module = THIS_MODULE,
+ .auto_attach = apci2200_auto_attach,
+ .detach = apci2200_detach,
+};
+
+static int apci2200_pci_probe(struct pci_dev *dev,
+ const struct pci_device_id *id)
+{
+ return comedi_pci_auto_config(dev, &apci2200_driver, id->driver_data);
+}
+
+static const struct pci_device_id apci2200_pci_table[] = {
+ { PCI_DEVICE(PCI_VENDOR_ID_ADDIDATA, 0x1005) },
+ { 0 }
+};
+MODULE_DEVICE_TABLE(pci, apci2200_pci_table);
+
+static struct pci_driver apci2200_pci_driver = {
+ .name = "addi_apci_2200",
+ .id_table = apci2200_pci_table,
+ .probe = apci2200_pci_probe,
+ .remove = comedi_pci_auto_unconfig,
+};
+module_comedi_pci_driver(apci2200_driver, apci2200_pci_driver);
+
+MODULE_DESCRIPTION("ADDI-DATA APCI-2200 Relay board, optically isolated");
+MODULE_AUTHOR("Comedi http://www.comedi.org");
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/comedi/drivers/addi_apci_3120.c b/drivers/staging/comedi/drivers/addi_apci_3120.c
new file mode 100644
index 000000000..95dc64bfe
--- /dev/null
+++ b/drivers/staging/comedi/drivers/addi_apci_3120.c
@@ -0,0 +1,1129 @@
+/*
+ * addi_apci_3120.c
+ * Copyright (C) 2004,2005 ADDI-DATA GmbH for the source code of this module.
+ *
+ * ADDI-DATA GmbH
+ * Dieselstrasse 3
+ * D-77833 Ottersweier
+ * Tel: +19(0)7223/9493-0
+ * Fax: +49(0)7223/9493-92
+ * http://www.addi-data.com
+ * info@addi-data.com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ */
+
+#include <linux/module.h>
+#include <linux/interrupt.h>
+
+#include "../comedi_pci.h"
+#include "amcc_s5933.h"
+
+/*
+ * PCI BAR 0 register map (devpriv->amcc)
+ * see amcc_s5933.h for register and bit defines
+ */
+#define APCI3120_FIFO_ADVANCE_ON_BYTE_2 (1 << 29)
+
+/*
+ * PCI BAR 1 register map (dev->iobase)
+ */
+#define APCI3120_AI_FIFO_REG 0x00
+#define APCI3120_CTRL_REG 0x00
+#define APCI3120_CTRL_EXT_TRIG (1 << 15)
+#define APCI3120_CTRL_GATE(x) (1 << (12 + (x)))
+#define APCI3120_CTRL_PR(x) (((x) & 0xf) << 8)
+#define APCI3120_CTRL_PA(x) (((x) & 0xf) << 0)
+#define APCI3120_AI_SOFTTRIG_REG 0x02
+#define APCI3120_STATUS_REG 0x02
+#define APCI3120_STATUS_EOC_INT (1 << 15)
+#define APCI3120_STATUS_AMCC_INT (1 << 14)
+#define APCI3120_STATUS_EOS_INT (1 << 13)
+#define APCI3120_STATUS_TIMER2_INT (1 << 12)
+#define APCI3120_STATUS_INT_MASK (0xf << 12)
+#define APCI3120_STATUS_TO_DI_BITS(x) (((x) >> 8) & 0xf)
+#define APCI3120_STATUS_TO_VERSION(x) (((x) >> 4) & 0xf)
+#define APCI3120_STATUS_FIFO_FULL (1 << 2)
+#define APCI3120_STATUS_FIFO_EMPTY (1 << 1)
+#define APCI3120_STATUS_DA_READY (1 << 0)
+#define APCI3120_TIMER_REG 0x04
+#define APCI3120_CHANLIST_REG 0x06
+#define APCI3120_CHANLIST_INDEX(x) (((x) & 0xf) << 8)
+#define APCI3120_CHANLIST_UNIPOLAR (1 << 7)
+#define APCI3120_CHANLIST_GAIN(x) (((x) & 0x3) << 4)
+#define APCI3120_CHANLIST_MUX(x) (((x) & 0xf) << 0)
+#define APCI3120_AO_REG(x) (0x08 + (((x) / 4) * 2))
+#define APCI3120_AO_MUX(x) (((x) & 0x3) << 14)
+#define APCI3120_AO_DATA(x) ((x) << 0)
+#define APCI3120_TIMER_MODE_REG 0x0c
+#define APCI3120_TIMER_MODE(_t, _m) ((_m) << ((_t) * 2))
+#define APCI3120_TIMER_MODE0 0 /* I8254_MODE0 */
+#define APCI3120_TIMER_MODE2 1 /* I8254_MODE2 */
+#define APCI3120_TIMER_MODE4 2 /* I8254_MODE4 */
+#define APCI3120_TIMER_MODE5 3 /* I8254_MODE5 */
+#define APCI3120_TIMER_MODE_MASK(_t) (3 << ((_t) * 2))
+#define APCI3120_CTR0_REG 0x0d
+#define APCI3120_CTR0_DO_BITS(x) ((x) << 4)
+#define APCI3120_CTR0_TIMER_SEL(x) ((x) << 0)
+#define APCI3120_MODE_REG 0x0e
+#define APCI3120_MODE_TIMER2_CLK_OSC (0 << 6)
+#define APCI3120_MODE_TIMER2_CLK_OUT1 (1 << 6)
+#define APCI3120_MODE_TIMER2_CLK_EOC (2 << 6)
+#define APCI3120_MODE_TIMER2_CLK_EOS (3 << 6)
+#define APCI3120_MODE_TIMER2_CLK_MASK (3 << 6)
+#define APCI3120_MODE_TIMER2_AS_TIMER (0 << 4)
+#define APCI3120_MODE_TIMER2_AS_COUNTER (1 << 4)
+#define APCI3120_MODE_TIMER2_AS_WDOG (2 << 4)
+#define APCI3120_MODE_TIMER2_AS_MASK (3 << 4) /* sets AS_TIMER */
+#define APCI3120_MODE_SCAN_ENA (1 << 3)
+#define APCI3120_MODE_TIMER2_IRQ_ENA (1 << 2)
+#define APCI3120_MODE_EOS_IRQ_ENA (1 << 1)
+#define APCI3120_MODE_EOC_IRQ_ENA (1 << 0)
+
+/*
+ * PCI BAR 2 register map (devpriv->addon)
+ */
+#define APCI3120_ADDON_ADDR_REG 0x00
+#define APCI3120_ADDON_DATA_REG 0x02
+#define APCI3120_ADDON_CTRL_REG 0x04
+#define APCI3120_ADDON_CTRL_AMWEN_ENA (1 << 1)
+#define APCI3120_ADDON_CTRL_A2P_FIFO_ENA (1 << 0)
+
+/*
+ * Board revisions
+ */
+#define APCI3120_REVA 0xa
+#define APCI3120_REVB 0xb
+#define APCI3120_REVA_OSC_BASE 70 /* 70ns = 14.29MHz */
+#define APCI3120_REVB_OSC_BASE 50 /* 50ns = 20MHz */
+
+static const struct comedi_lrange apci3120_ai_range = {
+ 8, {
+ BIP_RANGE(10),
+ BIP_RANGE(5),
+ BIP_RANGE(2),
+ BIP_RANGE(1),
+ UNI_RANGE(10),
+ UNI_RANGE(5),
+ UNI_RANGE(2),
+ UNI_RANGE(1)
+ }
+};
+
+enum apci3120_boardid {
+ BOARD_APCI3120,
+ BOARD_APCI3001,
+};
+
+struct apci3120_board {
+ const char *name;
+ unsigned int ai_is_16bit:1;
+ unsigned int has_ao:1;
+};
+
+static const struct apci3120_board apci3120_boardtypes[] = {
+ [BOARD_APCI3120] = {
+ .name = "apci3120",
+ .ai_is_16bit = 1,
+ .has_ao = 1,
+ },
+ [BOARD_APCI3001] = {
+ .name = "apci3001",
+ },
+};
+
+struct apci3120_dmabuf {
+ unsigned short *virt;
+ dma_addr_t hw;
+ unsigned int size;
+ unsigned int use_size;
+};
+
+struct apci3120_private {
+ unsigned long amcc;
+ unsigned long addon;
+ unsigned int osc_base;
+ unsigned int use_dma:1;
+ unsigned int use_double_buffer:1;
+ unsigned int cur_dmabuf:1;
+ struct apci3120_dmabuf dmabuf[2];
+ unsigned char do_bits;
+ unsigned char timer_mode;
+ unsigned char mode;
+ unsigned short ctrl;
+};
+
+static void apci3120_addon_write(struct comedi_device *dev,
+ unsigned int val, unsigned int reg)
+{
+ struct apci3120_private *devpriv = dev->private;
+
+ /* 16-bit interface for AMCC add-on registers */
+
+ outw(reg, devpriv->addon + APCI3120_ADDON_ADDR_REG);
+ outw(val & 0xffff, devpriv->addon + APCI3120_ADDON_DATA_REG);
+
+ outw(reg + 2, devpriv->addon + APCI3120_ADDON_ADDR_REG);
+ outw((val >> 16) & 0xffff, devpriv->addon + APCI3120_ADDON_DATA_REG);
+}
+
+static void apci3120_init_dma(struct comedi_device *dev,
+ struct apci3120_dmabuf *dmabuf)
+{
+ struct apci3120_private *devpriv = dev->private;
+
+ /* AMCC - enable transfer count and reset A2P FIFO */
+ outl(AGCSTS_TC_ENABLE | AGCSTS_RESET_A2P_FIFO,
+ devpriv->amcc + AMCC_OP_REG_AGCSTS);
+
+ /* Add-On - enable transfer count and reset A2P FIFO */
+ apci3120_addon_write(dev, AGCSTS_TC_ENABLE | AGCSTS_RESET_A2P_FIFO,
+ AMCC_OP_REG_AGCSTS);
+
+ /* AMCC - enable transfers and reset A2P flags */
+ outl(RESET_A2P_FLAGS | EN_A2P_TRANSFERS,
+ devpriv->amcc + AMCC_OP_REG_MCSR);
+
+ /* Add-On - DMA start address */
+ apci3120_addon_write(dev, dmabuf->hw, AMCC_OP_REG_AMWAR);
+
+ /* Add-On - Number of acquisitions */
+ apci3120_addon_write(dev, dmabuf->use_size, AMCC_OP_REG_AMWTC);
+
+ /* AMCC - enable write complete (DMA) and set FIFO advance */
+ outl(APCI3120_FIFO_ADVANCE_ON_BYTE_2 | AINT_WRITE_COMPL,
+ devpriv->amcc + AMCC_OP_REG_INTCSR);
+
+ /* Add-On - enable DMA */
+ outw(APCI3120_ADDON_CTRL_AMWEN_ENA | APCI3120_ADDON_CTRL_A2P_FIFO_ENA,
+ devpriv->addon + APCI3120_ADDON_CTRL_REG);
+}
+
+static void apci3120_setup_dma(struct comedi_device *dev,
+ struct comedi_subdevice *s)
+{
+ struct apci3120_private *devpriv = dev->private;
+ struct comedi_cmd *cmd = &s->async->cmd;
+ struct apci3120_dmabuf *dmabuf0 = &devpriv->dmabuf[0];
+ struct apci3120_dmabuf *dmabuf1 = &devpriv->dmabuf[1];
+ unsigned int dmalen0 = dmabuf0->size;
+ unsigned int dmalen1 = dmabuf1->size;
+ unsigned int scan_bytes;
+
+ scan_bytes = comedi_samples_to_bytes(s, cmd->scan_end_arg);
+
+ if (cmd->stop_src == TRIG_COUNT) {
+ /*
+ * Must we fill full first buffer? And must we fill
+ * full second buffer when first is once filled?
+ */
+ if (dmalen0 > (cmd->stop_arg * scan_bytes))
+ dmalen0 = cmd->stop_arg * scan_bytes;
+ else if (dmalen1 > (cmd->stop_arg * scan_bytes - dmalen0))
+ dmalen1 = cmd->stop_arg * scan_bytes - dmalen0;
+ }
+
+ if (cmd->flags & CMDF_WAKE_EOS) {
+ /* don't we want wake up every scan? */
+ if (dmalen0 > scan_bytes) {
+ dmalen0 = scan_bytes;
+ if (cmd->scan_end_arg & 1)
+ dmalen0 += 2;
+ }
+ if (dmalen1 > scan_bytes) {
+ dmalen1 = scan_bytes;
+ if (cmd->scan_end_arg & 1)
+ dmalen1 -= 2;
+ if (dmalen1 < 4)
+ dmalen1 = 4;
+ }
+ } else {
+ /* isn't output buff smaller that our DMA buff? */
+ if (dmalen0 > s->async->prealloc_bufsz)
+ dmalen0 = s->async->prealloc_bufsz;
+ if (dmalen1 > s->async->prealloc_bufsz)
+ dmalen1 = s->async->prealloc_bufsz;
+ }
+ dmabuf0->use_size = dmalen0;
+ dmabuf1->use_size = dmalen1;
+
+ apci3120_init_dma(dev, dmabuf0);
+}
+
+/*
+ * There are three timers on the board. They all use the same base
+ * clock with a fixed prescaler for each timer. The base clock used
+ * depends on the board version and type.
+ *
+ * APCI-3120 Rev A boards OSC = 14.29MHz base clock (~70ns)
+ * APCI-3120 Rev B boards OSC = 20MHz base clock (50ns)
+ * APCI-3001 boards OSC = 20MHz base clock (50ns)
+ *
+ * The prescalers for each timer are:
+ * Timer 0 CLK = OSC/10
+ * Timer 1 CLK = OSC/1000
+ * Timer 2 CLK = OSC/1000
+ */
+static unsigned int apci3120_ns_to_timer(struct comedi_device *dev,
+ unsigned int timer,
+ unsigned int ns,
+ unsigned int flags)
+{
+ struct apci3120_private *devpriv = dev->private;
+ unsigned int prescale = (timer == 0) ? 10 : 1000;
+ unsigned int timer_base = devpriv->osc_base * prescale;
+ unsigned int divisor;
+
+ switch (flags & CMDF_ROUND_MASK) {
+ case CMDF_ROUND_UP:
+ divisor = DIV_ROUND_UP(ns, timer_base);
+ break;
+ case CMDF_ROUND_DOWN:
+ divisor = ns / timer_base;
+ break;
+ case CMDF_ROUND_NEAREST:
+ default:
+ divisor = DIV_ROUND_CLOSEST(ns, timer_base);
+ break;
+ }
+
+ if (timer == 2) {
+ /* timer 2 is 24-bits */
+ if (divisor > 0x00ffffff)
+ divisor = 0x00ffffff;
+ } else {
+ /* timers 0 and 1 are 16-bits */
+ if (divisor > 0xffff)
+ divisor = 0xffff;
+ }
+ /* the timers require a minimum divisor of 2 */
+ if (divisor < 2)
+ divisor = 2;
+
+ return divisor;
+}
+
+static void apci3120_clr_timer2_interrupt(struct comedi_device *dev)
+{
+ /* a dummy read of APCI3120_CTR0_REG clears the timer 2 interrupt */
+ inb(dev->iobase + APCI3120_CTR0_REG);
+}
+
+static void apci3120_timer_write(struct comedi_device *dev,
+ unsigned int timer, unsigned int val)
+{
+ struct apci3120_private *devpriv = dev->private;
+
+ /* write 16-bit value to timer (lower 16-bits of timer 2) */
+ outb(APCI3120_CTR0_DO_BITS(devpriv->do_bits) |
+ APCI3120_CTR0_TIMER_SEL(timer),
+ dev->iobase + APCI3120_CTR0_REG);
+ outw(val & 0xffff, dev->iobase + APCI3120_TIMER_REG);
+
+ if (timer == 2) {
+ /* write upper 16-bits to timer 2 */
+ outb(APCI3120_CTR0_DO_BITS(devpriv->do_bits) |
+ APCI3120_CTR0_TIMER_SEL(timer + 1),
+ dev->iobase + APCI3120_CTR0_REG);
+ outw((val >> 16) & 0xffff, dev->iobase + APCI3120_TIMER_REG);
+ }
+}
+
+static unsigned int apci3120_timer_read(struct comedi_device *dev,
+ unsigned int timer)
+{
+ struct apci3120_private *devpriv = dev->private;
+ unsigned int val;
+
+ /* read 16-bit value from timer (lower 16-bits of timer 2) */
+ outb(APCI3120_CTR0_DO_BITS(devpriv->do_bits) |
+ APCI3120_CTR0_TIMER_SEL(timer),
+ dev->iobase + APCI3120_CTR0_REG);
+ val = inw(dev->iobase + APCI3120_TIMER_REG);
+
+ if (timer == 2) {
+ /* read upper 16-bits from timer 2 */
+ outb(APCI3120_CTR0_DO_BITS(devpriv->do_bits) |
+ APCI3120_CTR0_TIMER_SEL(timer + 1),
+ dev->iobase + APCI3120_CTR0_REG);
+ val |= (inw(dev->iobase + APCI3120_TIMER_REG) << 16);
+ }
+
+ return val;
+}
+
+static void apci3120_timer_set_mode(struct comedi_device *dev,
+ unsigned int timer, unsigned int mode)
+{
+ struct apci3120_private *devpriv = dev->private;
+
+ devpriv->timer_mode &= ~APCI3120_TIMER_MODE_MASK(timer);
+ devpriv->timer_mode |= APCI3120_TIMER_MODE(timer, mode);
+ outb(devpriv->timer_mode, dev->iobase + APCI3120_TIMER_MODE_REG);
+}
+
+static void apci3120_timer_enable(struct comedi_device *dev,
+ unsigned int timer, bool enable)
+{
+ struct apci3120_private *devpriv = dev->private;
+
+ if (enable)
+ devpriv->ctrl |= APCI3120_CTRL_GATE(timer);
+ else
+ devpriv->ctrl &= ~APCI3120_CTRL_GATE(timer);
+ outw(devpriv->ctrl, dev->iobase + APCI3120_CTRL_REG);
+}
+
+static void apci3120_exttrig_enable(struct comedi_device *dev, bool enable)
+{
+ struct apci3120_private *devpriv = dev->private;
+
+ if (enable)
+ devpriv->ctrl |= APCI3120_CTRL_EXT_TRIG;
+ else
+ devpriv->ctrl &= ~APCI3120_CTRL_EXT_TRIG;
+ outw(devpriv->ctrl, dev->iobase + APCI3120_CTRL_REG);
+}
+
+static void apci3120_set_chanlist(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ int n_chan, unsigned int *chanlist)
+{
+ struct apci3120_private *devpriv = dev->private;
+ int i;
+
+ /* set chanlist for scan */
+ for (i = 0; i < n_chan; i++) {
+ unsigned int chan = CR_CHAN(chanlist[i]);
+ unsigned int range = CR_RANGE(chanlist[i]);
+ unsigned int val;
+
+ val = APCI3120_CHANLIST_MUX(chan) |
+ APCI3120_CHANLIST_GAIN(range) |
+ APCI3120_CHANLIST_INDEX(i);
+
+ if (comedi_range_is_unipolar(s, range))
+ val |= APCI3120_CHANLIST_UNIPOLAR;
+
+ outw(val, dev->iobase + APCI3120_CHANLIST_REG);
+ }
+
+ /* a dummy read of APCI3120_TIMER_MODE_REG resets the ai FIFO */
+ inw(dev->iobase + APCI3120_TIMER_MODE_REG);
+
+ /* set scan length (PR) and scan start (PA) */
+ devpriv->ctrl = APCI3120_CTRL_PR(n_chan - 1) | APCI3120_CTRL_PA(0);
+ outw(devpriv->ctrl, dev->iobase + APCI3120_CTRL_REG);
+
+ /* enable chanlist scanning if necessary */
+ if (n_chan > 1)
+ devpriv->mode |= APCI3120_MODE_SCAN_ENA;
+}
+
+static void apci3120_interrupt_dma(struct comedi_device *dev,
+ struct comedi_subdevice *s)
+{
+ struct apci3120_private *devpriv = dev->private;
+ struct comedi_async *async = s->async;
+ struct comedi_cmd *cmd = &async->cmd;
+ struct apci3120_dmabuf *dmabuf;
+ unsigned int nbytes;
+ unsigned int nsamples;
+
+ dmabuf = &devpriv->dmabuf[devpriv->cur_dmabuf];
+
+ nbytes = dmabuf->use_size - inl(devpriv->amcc + AMCC_OP_REG_MWTC);
+
+ if (nbytes < dmabuf->use_size)
+ dev_err(dev->class_dev, "Interrupted DMA transfer!\n");
+ if (nbytes & 1) {
+ dev_err(dev->class_dev, "Odd count of bytes in DMA ring!\n");
+ async->events |= COMEDI_CB_ERROR;
+ return;
+ }
+
+ nsamples = comedi_bytes_to_samples(s, nbytes);
+ if (nsamples) {
+ comedi_buf_write_samples(s, dmabuf->virt, nsamples);
+
+ if (!(cmd->flags & CMDF_WAKE_EOS))
+ async->events |= COMEDI_CB_EOS;
+ }
+
+ if ((async->events & COMEDI_CB_CANCEL_MASK) ||
+ (cmd->stop_src == TRIG_COUNT && async->scans_done >= cmd->stop_arg))
+ return;
+
+ if (devpriv->use_double_buffer) {
+ /* switch DMA buffers for next interrupt */
+ devpriv->cur_dmabuf = !devpriv->cur_dmabuf;
+ dmabuf = &devpriv->dmabuf[devpriv->cur_dmabuf];
+ apci3120_init_dma(dev, dmabuf);
+ } else {
+ /* restart DMA if not using double buffering */
+ apci3120_init_dma(dev, dmabuf);
+ }
+}
+
+static irqreturn_t apci3120_interrupt(int irq, void *d)
+{
+ struct comedi_device *dev = d;
+ struct apci3120_private *devpriv = dev->private;
+ struct comedi_subdevice *s = dev->read_subdev;
+ struct comedi_async *async = s->async;
+ struct comedi_cmd *cmd = &async->cmd;
+ unsigned int status;
+ unsigned int int_amcc;
+
+ status = inw(dev->iobase + APCI3120_STATUS_REG);
+ int_amcc = inl(devpriv->amcc + AMCC_OP_REG_INTCSR);
+
+ if (!(status & APCI3120_STATUS_INT_MASK) &&
+ !(int_amcc & ANY_S593X_INT)) {
+ dev_err(dev->class_dev, "IRQ from unknown source\n");
+ return IRQ_NONE;
+ }
+
+ outl(int_amcc | AINT_INT_MASK, devpriv->amcc + AMCC_OP_REG_INTCSR);
+
+ if (devpriv->ctrl & APCI3120_CTRL_EXT_TRIG)
+ apci3120_exttrig_enable(dev, false);
+
+ if (int_amcc & MASTER_ABORT_INT)
+ dev_err(dev->class_dev, "AMCC IRQ - MASTER DMA ABORT!\n");
+ if (int_amcc & TARGET_ABORT_INT)
+ dev_err(dev->class_dev, "AMCC IRQ - TARGET DMA ABORT!\n");
+
+ if ((status & APCI3120_STATUS_EOC_INT) == 0 &&
+ (devpriv->mode & APCI3120_MODE_EOC_IRQ_ENA)) {
+ /* nothing to do... EOC mode is not currently used */
+ }
+
+ if ((status & APCI3120_STATUS_EOS_INT) &&
+ (devpriv->mode & APCI3120_MODE_EOS_IRQ_ENA)) {
+ unsigned short val;
+ int i;
+
+ for (i = 0; i < cmd->chanlist_len; i++) {
+ val = inw(dev->iobase + APCI3120_AI_FIFO_REG);
+ comedi_buf_write_samples(s, &val, 1);
+ }
+
+ devpriv->mode |= APCI3120_MODE_EOS_IRQ_ENA;
+ outb(devpriv->mode, dev->iobase + APCI3120_MODE_REG);
+ }
+
+ if (status & APCI3120_STATUS_TIMER2_INT) {
+ /*
+ * for safety...
+ * timer2 interrupts are not enabled in the driver
+ */
+ apci3120_clr_timer2_interrupt(dev);
+ }
+
+ if (status & APCI3120_STATUS_AMCC_INT) {
+ /* AMCC- Clear write complete interrupt (DMA) */
+ outl(AINT_WT_COMPLETE, devpriv->amcc + AMCC_OP_REG_INTCSR);
+
+ /* do some data transfer */
+ apci3120_interrupt_dma(dev, s);
+ }
+
+ if (cmd->stop_src == TRIG_COUNT && async->scans_done >= cmd->stop_arg)
+ async->events |= COMEDI_CB_EOA;
+
+ comedi_handle_events(dev, s);
+
+ return IRQ_HANDLED;
+}
+
+static int apci3120_ai_cmd(struct comedi_device *dev,
+ struct comedi_subdevice *s)
+{
+ struct apci3120_private *devpriv = dev->private;
+ struct comedi_cmd *cmd = &s->async->cmd;
+ unsigned int divisor;
+
+ /* set default mode bits */
+ devpriv->mode = APCI3120_MODE_TIMER2_CLK_OSC |
+ APCI3120_MODE_TIMER2_AS_TIMER;
+
+ /* AMCC- Clear write complete interrupt (DMA) */
+ outl(AINT_WT_COMPLETE, devpriv->amcc + AMCC_OP_REG_INTCSR);
+
+ devpriv->cur_dmabuf = 0;
+
+ /* load chanlist for command scan */
+ apci3120_set_chanlist(dev, s, cmd->chanlist_len, cmd->chanlist);
+
+ if (cmd->start_src == TRIG_EXT)
+ apci3120_exttrig_enable(dev, true);
+
+ if (cmd->scan_begin_src == TRIG_TIMER) {
+ /*
+ * Timer 1 is used in MODE2 (rate generator) to set the
+ * start time for each scan.
+ */
+ divisor = apci3120_ns_to_timer(dev, 1, cmd->scan_begin_arg,
+ cmd->flags);
+ apci3120_timer_set_mode(dev, 1, APCI3120_TIMER_MODE2);
+ apci3120_timer_write(dev, 1, divisor);
+ }
+
+ /*
+ * Timer 0 is used in MODE2 (rate generator) to set the conversion
+ * time for each acquisition.
+ */
+ divisor = apci3120_ns_to_timer(dev, 0, cmd->convert_arg, cmd->flags);
+ apci3120_timer_set_mode(dev, 0, APCI3120_TIMER_MODE2);
+ apci3120_timer_write(dev, 0, divisor);
+
+ if (devpriv->use_dma)
+ apci3120_setup_dma(dev, s);
+ else
+ devpriv->mode |= APCI3120_MODE_EOS_IRQ_ENA;
+
+ /* set mode to enable acquisition */
+ outb(devpriv->mode, dev->iobase + APCI3120_MODE_REG);
+
+ if (cmd->scan_begin_src == TRIG_TIMER)
+ apci3120_timer_enable(dev, 1, true);
+ apci3120_timer_enable(dev, 0, true);
+
+ return 0;
+}
+
+static int apci3120_ai_cmdtest(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_cmd *cmd)
+{
+ unsigned int arg;
+ int err = 0;
+
+ /* Step 1 : check if triggers are trivially valid */
+
+ err |= comedi_check_trigger_src(&cmd->start_src, TRIG_NOW | TRIG_EXT);
+ err |= comedi_check_trigger_src(&cmd->scan_begin_src,
+ TRIG_TIMER | TRIG_FOLLOW);
+ err |= comedi_check_trigger_src(&cmd->convert_src, TRIG_TIMER);
+ err |= comedi_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
+ err |= comedi_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE);
+
+ if (err)
+ return 1;
+
+ /* Step 2a : make sure trigger sources are unique */
+
+ err |= comedi_check_trigger_is_unique(cmd->start_src);
+ err |= comedi_check_trigger_is_unique(cmd->scan_begin_src);
+ err |= comedi_check_trigger_is_unique(cmd->stop_src);
+
+ /* Step 2b : and mutually compatible */
+
+ if (err)
+ return 2;
+
+ /* Step 3: check if arguments are trivially valid */
+
+ err |= comedi_check_trigger_arg_is(&cmd->start_arg, 0);
+
+ if (cmd->scan_begin_src == TRIG_TIMER) { /* Test Delay timing */
+ err |= comedi_check_trigger_arg_min(&cmd->scan_begin_arg,
+ 100000);
+ }
+
+ /* minimum conversion time per sample is 10us */
+ err |= comedi_check_trigger_arg_min(&cmd->convert_arg, 10000);
+
+ err |= comedi_check_trigger_arg_min(&cmd->chanlist_len, 1);
+ err |= comedi_check_trigger_arg_is(&cmd->scan_end_arg,
+ cmd->chanlist_len);
+
+ if (cmd->stop_src == TRIG_COUNT)
+ err |= comedi_check_trigger_arg_min(&cmd->stop_arg, 1);
+ else /* TRIG_NONE */
+ err |= comedi_check_trigger_arg_is(&cmd->stop_arg, 0);
+
+ if (err)
+ return 3;
+
+ /* Step 4: fix up any arguments */
+
+ if (cmd->scan_begin_src == TRIG_TIMER) {
+ /* scan begin must be larger than the scan time */
+ arg = cmd->convert_arg * cmd->scan_end_arg;
+ err |= comedi_check_trigger_arg_min(&cmd->scan_begin_arg, arg);
+ }
+
+ if (err)
+ return 4;
+
+ /* Step 5: check channel list if it exists */
+
+ return 0;
+}
+
+static int apci3120_cancel(struct comedi_device *dev,
+ struct comedi_subdevice *s)
+{
+ struct apci3120_private *devpriv = dev->private;
+
+ /* Add-On - disable DMA */
+ outw(0, devpriv->addon + 4);
+
+ /* Add-On - disable bus master */
+ apci3120_addon_write(dev, 0, AMCC_OP_REG_AGCSTS);
+
+ /* AMCC - disable bus master */
+ outl(0, devpriv->amcc + AMCC_OP_REG_MCSR);
+
+ /* disable all counters, ext trigger, and reset scan */
+ devpriv->ctrl = 0;
+ outw(devpriv->ctrl, dev->iobase + APCI3120_CTRL_REG);
+
+ /* DISABLE_ALL_INTERRUPT */
+ devpriv->mode = 0;
+ outb(devpriv->mode, dev->iobase + APCI3120_MODE_REG);
+
+ inw(dev->iobase + APCI3120_STATUS_REG);
+ devpriv->cur_dmabuf = 0;
+
+ return 0;
+}
+
+static int apci3120_ai_eoc(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned long context)
+{
+ unsigned int status;
+
+ status = inw(dev->iobase + APCI3120_STATUS_REG);
+ if ((status & APCI3120_STATUS_EOC_INT) == 0)
+ return 0;
+ return -EBUSY;
+}
+
+static int apci3120_ai_insn_read(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ struct apci3120_private *devpriv = dev->private;
+ unsigned int divisor;
+ int ret;
+ int i;
+
+ /* set mode for A/D conversions by software trigger with timer 0 */
+ devpriv->mode = APCI3120_MODE_TIMER2_CLK_OSC |
+ APCI3120_MODE_TIMER2_AS_TIMER;
+ outb(devpriv->mode, dev->iobase + APCI3120_MODE_REG);
+
+ /* load chanlist for single channel scan */
+ apci3120_set_chanlist(dev, s, 1, &insn->chanspec);
+
+ /*
+ * Timer 0 is used in MODE4 (software triggered strobe) to set the
+ * conversion time for each acquisition. Each conversion is triggered
+ * when the divisor is written to the timer, The conversion is done
+ * when the EOC bit in the status register is '0'.
+ */
+ apci3120_timer_set_mode(dev, 0, APCI3120_TIMER_MODE4);
+ apci3120_timer_enable(dev, 0, true);
+
+ /* fixed conversion time of 10 us */
+ divisor = apci3120_ns_to_timer(dev, 0, 10000, CMDF_ROUND_NEAREST);
+
+ for (i = 0; i < insn->n; i++) {
+ /* trigger conversion */
+ apci3120_timer_write(dev, 0, divisor);
+
+ ret = comedi_timeout(dev, s, insn, apci3120_ai_eoc, 0);
+ if (ret)
+ return ret;
+
+ data[i] = inw(dev->iobase + APCI3120_AI_FIFO_REG);
+ }
+
+ return insn->n;
+}
+
+static int apci3120_ao_ready(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned long context)
+{
+ unsigned int status;
+
+ status = inw(dev->iobase + APCI3120_STATUS_REG);
+ if (status & APCI3120_STATUS_DA_READY)
+ return 0;
+ return -EBUSY;
+}
+
+static int apci3120_ao_insn_write(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ unsigned int chan = CR_CHAN(insn->chanspec);
+ int i;
+
+ for (i = 0; i < insn->n; i++) {
+ unsigned int val = data[i];
+ int ret;
+
+ ret = comedi_timeout(dev, s, insn, apci3120_ao_ready, 0);
+ if (ret)
+ return ret;
+
+ outw(APCI3120_AO_MUX(chan) | APCI3120_AO_DATA(val),
+ dev->iobase + APCI3120_AO_REG(chan));
+
+ s->readback[chan] = val;
+ }
+
+ return insn->n;
+}
+
+static int apci3120_di_insn_bits(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ unsigned int status;
+
+ status = inw(dev->iobase + APCI3120_STATUS_REG);
+ data[1] = APCI3120_STATUS_TO_DI_BITS(status);
+
+ return insn->n;
+}
+
+static int apci3120_do_insn_bits(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ struct apci3120_private *devpriv = dev->private;
+
+ if (comedi_dio_update_state(s, data)) {
+ devpriv->do_bits = s->state;
+ outb(APCI3120_CTR0_DO_BITS(devpriv->do_bits),
+ dev->iobase + APCI3120_CTR0_REG);
+ }
+
+ data[1] = s->state;
+
+ return insn->n;
+}
+
+static int apci3120_timer_insn_config(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ struct apci3120_private *devpriv = dev->private;
+ unsigned int divisor;
+ unsigned int status;
+ unsigned int mode;
+ unsigned int timer_mode;
+
+ switch (data[0]) {
+ case INSN_CONFIG_ARM:
+ apci3120_clr_timer2_interrupt(dev);
+ divisor = apci3120_ns_to_timer(dev, 2, data[1],
+ CMDF_ROUND_DOWN);
+ apci3120_timer_write(dev, 2, divisor);
+ apci3120_timer_enable(dev, 2, true);
+ break;
+
+ case INSN_CONFIG_DISARM:
+ apci3120_timer_enable(dev, 2, false);
+ apci3120_clr_timer2_interrupt(dev);
+ break;
+
+ case INSN_CONFIG_GET_COUNTER_STATUS:
+ data[1] = 0;
+ data[2] = COMEDI_COUNTER_ARMED | COMEDI_COUNTER_COUNTING |
+ COMEDI_COUNTER_TERMINAL_COUNT;
+
+ if (devpriv->ctrl & APCI3120_CTRL_GATE(2)) {
+ data[1] |= COMEDI_COUNTER_ARMED;
+ data[1] |= COMEDI_COUNTER_COUNTING;
+ }
+ status = inw(dev->iobase + APCI3120_STATUS_REG);
+ if (status & APCI3120_STATUS_TIMER2_INT) {
+ data[1] &= ~COMEDI_COUNTER_COUNTING;
+ data[1] |= COMEDI_COUNTER_TERMINAL_COUNT;
+ }
+ break;
+
+ case INSN_CONFIG_SET_COUNTER_MODE:
+ switch (data[1]) {
+ case I8254_MODE0:
+ mode = APCI3120_MODE_TIMER2_AS_COUNTER;
+ timer_mode = APCI3120_TIMER_MODE0;
+ break;
+ case I8254_MODE2:
+ mode = APCI3120_MODE_TIMER2_AS_TIMER;
+ timer_mode = APCI3120_TIMER_MODE2;
+ break;
+ case I8254_MODE4:
+ mode = APCI3120_MODE_TIMER2_AS_TIMER;
+ timer_mode = APCI3120_TIMER_MODE4;
+ break;
+ case I8254_MODE5:
+ mode = APCI3120_MODE_TIMER2_AS_WDOG;
+ timer_mode = APCI3120_TIMER_MODE5;
+ break;
+ default:
+ return -EINVAL;
+ }
+ apci3120_timer_enable(dev, 2, false);
+ apci3120_clr_timer2_interrupt(dev);
+ apci3120_timer_set_mode(dev, 2, timer_mode);
+ devpriv->mode &= ~APCI3120_MODE_TIMER2_AS_MASK;
+ devpriv->mode |= mode;
+ outb(devpriv->mode, dev->iobase + APCI3120_MODE_REG);
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ return insn->n;
+}
+
+static int apci3120_timer_insn_read(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ int i;
+
+ for (i = 0; i < insn->n; i++)
+ data[i] = apci3120_timer_read(dev, 2);
+
+ return insn->n;
+}
+
+static void apci3120_dma_alloc(struct comedi_device *dev)
+{
+ struct apci3120_private *devpriv = dev->private;
+ struct apci3120_dmabuf *dmabuf;
+ int order;
+ int i;
+
+ for (i = 0; i < 2; i++) {
+ dmabuf = &devpriv->dmabuf[i];
+ for (order = 2; order >= 0; order--) {
+ dmabuf->virt = dma_alloc_coherent(dev->hw_dev,
+ PAGE_SIZE << order,
+ &dmabuf->hw,
+ GFP_KERNEL);
+ if (dmabuf->virt)
+ break;
+ }
+ if (!dmabuf->virt)
+ break;
+ dmabuf->size = PAGE_SIZE << order;
+
+ if (i == 0)
+ devpriv->use_dma = 1;
+ if (i == 1)
+ devpriv->use_double_buffer = 1;
+ }
+}
+
+static void apci3120_dma_free(struct comedi_device *dev)
+{
+ struct apci3120_private *devpriv = dev->private;
+ struct apci3120_dmabuf *dmabuf;
+ int i;
+
+ if (!devpriv)
+ return;
+
+ for (i = 0; i < 2; i++) {
+ dmabuf = &devpriv->dmabuf[i];
+ if (dmabuf->virt) {
+ dma_free_coherent(dev->hw_dev, dmabuf->size,
+ dmabuf->virt, dmabuf->hw);
+ }
+ }
+}
+
+static void apci3120_reset(struct comedi_device *dev)
+{
+ /* disable all interrupt sources */
+ outb(0, dev->iobase + APCI3120_MODE_REG);
+
+ /* disable all counters, ext trigger, and reset scan */
+ outw(0, dev->iobase + APCI3120_CTRL_REG);
+
+ /* clear interrupt status */
+ inw(dev->iobase + APCI3120_STATUS_REG);
+}
+
+static int apci3120_auto_attach(struct comedi_device *dev,
+ unsigned long context)
+{
+ struct pci_dev *pcidev = comedi_to_pci_dev(dev);
+ const struct apci3120_board *this_board = NULL;
+ struct apci3120_private *devpriv;
+ struct comedi_subdevice *s;
+ unsigned int status;
+ int ret;
+
+ if (context < ARRAY_SIZE(apci3120_boardtypes))
+ this_board = &apci3120_boardtypes[context];
+ if (!this_board)
+ return -ENODEV;
+ dev->board_ptr = this_board;
+ dev->board_name = this_board->name;
+
+ devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
+ if (!devpriv)
+ return -ENOMEM;
+
+ ret = comedi_pci_enable(dev);
+ if (ret)
+ return ret;
+ pci_set_master(pcidev);
+
+ dev->iobase = pci_resource_start(pcidev, 1);
+ devpriv->amcc = pci_resource_start(pcidev, 0);
+ devpriv->addon = pci_resource_start(pcidev, 2);
+
+ apci3120_reset(dev);
+
+ if (pcidev->irq > 0) {
+ ret = request_irq(pcidev->irq, apci3120_interrupt, IRQF_SHARED,
+ dev->board_name, dev);
+ if (ret == 0) {
+ dev->irq = pcidev->irq;
+
+ apci3120_dma_alloc(dev);
+ }
+ }
+
+ status = inw(dev->iobase + APCI3120_STATUS_REG);
+ if (APCI3120_STATUS_TO_VERSION(status) == APCI3120_REVB ||
+ context == BOARD_APCI3001)
+ devpriv->osc_base = APCI3120_REVB_OSC_BASE;
+ else
+ devpriv->osc_base = APCI3120_REVA_OSC_BASE;
+
+ ret = comedi_alloc_subdevices(dev, 5);
+ if (ret)
+ return ret;
+
+ /* Analog Input subdevice */
+ s = &dev->subdevices[0];
+ s->type = COMEDI_SUBD_AI;
+ s->subdev_flags = SDF_READABLE | SDF_COMMON | SDF_GROUND | SDF_DIFF;
+ s->n_chan = 16;
+ s->maxdata = this_board->ai_is_16bit ? 0xffff : 0x0fff;
+ s->range_table = &apci3120_ai_range;
+ s->insn_read = apci3120_ai_insn_read;
+ if (dev->irq) {
+ dev->read_subdev = s;
+ s->subdev_flags |= SDF_CMD_READ;
+ s->len_chanlist = s->n_chan;
+ s->do_cmdtest = apci3120_ai_cmdtest;
+ s->do_cmd = apci3120_ai_cmd;
+ s->cancel = apci3120_cancel;
+ }
+
+ /* Analog Output subdevice */
+ s = &dev->subdevices[1];
+ if (this_board->has_ao) {
+ s->type = COMEDI_SUBD_AO;
+ s->subdev_flags = SDF_WRITABLE | SDF_GROUND | SDF_COMMON;
+ s->n_chan = 8;
+ s->maxdata = 0x3fff;
+ s->range_table = &range_bipolar10;
+ s->insn_write = apci3120_ao_insn_write;
+
+ ret = comedi_alloc_subdev_readback(s);
+ if (ret)
+ return ret;
+ } else {
+ s->type = COMEDI_SUBD_UNUSED;
+ }
+
+ /* Digital Input subdevice */
+ s = &dev->subdevices[2];
+ s->type = COMEDI_SUBD_DI;
+ s->subdev_flags = SDF_READABLE;
+ s->n_chan = 4;
+ s->maxdata = 1;
+ s->range_table = &range_digital;
+ s->insn_bits = apci3120_di_insn_bits;
+
+ /* Digital Output subdevice */
+ s = &dev->subdevices[3];
+ s->type = COMEDI_SUBD_DO;
+ s->subdev_flags = SDF_WRITABLE;
+ s->n_chan = 4;
+ s->maxdata = 1;
+ s->range_table = &range_digital;
+ s->insn_bits = apci3120_do_insn_bits;
+
+ /* Timer subdevice */
+ s = &dev->subdevices[4];
+ s->type = COMEDI_SUBD_TIMER;
+ s->subdev_flags = SDF_READABLE;
+ s->n_chan = 1;
+ s->maxdata = 0x00ffffff;
+ s->insn_config = apci3120_timer_insn_config;
+ s->insn_read = apci3120_timer_insn_read;
+
+ return 0;
+}
+
+static void apci3120_detach(struct comedi_device *dev)
+{
+ comedi_pci_detach(dev);
+ apci3120_dma_free(dev);
+}
+
+static struct comedi_driver apci3120_driver = {
+ .driver_name = "addi_apci_3120",
+ .module = THIS_MODULE,
+ .auto_attach = apci3120_auto_attach,
+ .detach = apci3120_detach,
+};
+
+static int apci3120_pci_probe(struct pci_dev *dev,
+ const struct pci_device_id *id)
+{
+ return comedi_pci_auto_config(dev, &apci3120_driver, id->driver_data);
+}
+
+static const struct pci_device_id apci3120_pci_table[] = {
+ { PCI_VDEVICE(AMCC, 0x818d), BOARD_APCI3120 },
+ { PCI_VDEVICE(AMCC, 0x828d), BOARD_APCI3001 },
+ { 0 }
+};
+MODULE_DEVICE_TABLE(pci, apci3120_pci_table);
+
+static struct pci_driver apci3120_pci_driver = {
+ .name = "addi_apci_3120",
+ .id_table = apci3120_pci_table,
+ .probe = apci3120_pci_probe,
+ .remove = comedi_pci_auto_unconfig,
+};
+module_comedi_pci_driver(apci3120_driver, apci3120_pci_driver);
+
+MODULE_AUTHOR("Comedi http://www.comedi.org");
+MODULE_DESCRIPTION("ADDI-DATA APCI-3120, Analog input board");
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/comedi/drivers/addi_apci_3501.c b/drivers/staging/comedi/drivers/addi_apci_3501.c
new file mode 100644
index 000000000..73786a3f3
--- /dev/null
+++ b/drivers/staging/comedi/drivers/addi_apci_3501.c
@@ -0,0 +1,448 @@
+/*
+ * addi_apci_3501.c
+ * Copyright (C) 2004,2005 ADDI-DATA GmbH for the source code of this module.
+ * Project manager: Eric Stolz
+ *
+ * ADDI-DATA GmbH
+ * Dieselstrasse 3
+ * D-77833 Ottersweier
+ * Tel: +19(0)7223/9493-0
+ * Fax: +49(0)7223/9493-92
+ * http://www.addi-data.com
+ * info@addi-data.com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ */
+
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/sched.h>
+
+#include "../comedi_pci.h"
+#include "amcc_s5933.h"
+
+/*
+ * PCI bar 1 register I/O map
+ */
+#define APCI3501_AO_CTRL_STATUS_REG 0x00
+#define APCI3501_AO_CTRL_BIPOLAR (1 << 0)
+#define APCI3501_AO_STATUS_READY (1 << 8)
+#define APCI3501_AO_DATA_REG 0x04
+#define APCI3501_AO_DATA_CHAN(x) ((x) << 0)
+#define APCI3501_AO_DATA_VAL(x) ((x) << 8)
+#define APCI3501_AO_DATA_BIPOLAR (1 << 31)
+#define APCI3501_AO_TRIG_SCS_REG 0x08
+#define APCI3501_TIMER_SYNC_REG 0x20
+#define APCI3501_TIMER_RELOAD_REG 0x24
+#define APCI3501_TIMER_TIMEBASE_REG 0x28
+#define APCI3501_TIMER_CTRL_REG 0x2c
+#define APCI3501_TIMER_STATUS_REG 0x30
+#define APCI3501_TIMER_IRQ_REG 0x34
+#define APCI3501_TIMER_WARN_RELOAD_REG 0x38
+#define APCI3501_TIMER_WARN_TIMEBASE_REG 0x3c
+#define APCI3501_DO_REG 0x40
+#define APCI3501_DI_REG 0x50
+
+/*
+ * AMCC S5933 NVRAM
+ */
+#define NVRAM_USER_DATA_START 0x100
+
+#define NVCMD_BEGIN_READ (0x7 << 5)
+#define NVCMD_LOAD_LOW (0x4 << 5)
+#define NVCMD_LOAD_HIGH (0x5 << 5)
+
+/*
+ * Function types stored in the eeprom
+ */
+#define EEPROM_DIGITALINPUT 0
+#define EEPROM_DIGITALOUTPUT 1
+#define EEPROM_ANALOGINPUT 2
+#define EEPROM_ANALOGOUTPUT 3
+#define EEPROM_TIMER 4
+#define EEPROM_WATCHDOG 5
+#define EEPROM_TIMER_WATCHDOG_COUNTER 10
+
+struct apci3501_private {
+ int i_IobaseAmcc;
+ struct task_struct *tsk_Current;
+ unsigned char b_TimerSelectMode;
+};
+
+static struct comedi_lrange apci3501_ao_range = {
+ 2, {
+ BIP_RANGE(10),
+ UNI_RANGE(10)
+ }
+};
+
+static int apci3501_wait_for_dac(struct comedi_device *dev)
+{
+ unsigned int status;
+
+ do {
+ status = inl(dev->iobase + APCI3501_AO_CTRL_STATUS_REG);
+ } while (!(status & APCI3501_AO_STATUS_READY));
+
+ return 0;
+}
+
+static int apci3501_ao_insn_write(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ unsigned int chan = CR_CHAN(insn->chanspec);
+ unsigned int range = CR_RANGE(insn->chanspec);
+ unsigned int cfg = APCI3501_AO_DATA_CHAN(chan);
+ int ret;
+ int i;
+
+ /*
+ * All analog output channels have the same output range.
+ * 14-bit bipolar: 0-10V
+ * 13-bit unipolar: +/-10V
+ * Changing the range of one channel changes all of them!
+ */
+ if (range) {
+ outl(0, dev->iobase + APCI3501_AO_CTRL_STATUS_REG);
+ } else {
+ cfg |= APCI3501_AO_DATA_BIPOLAR;
+ outl(APCI3501_AO_CTRL_BIPOLAR,
+ dev->iobase + APCI3501_AO_CTRL_STATUS_REG);
+ }
+
+ for (i = 0; i < insn->n; i++) {
+ unsigned int val = data[i];
+
+ if (range == 1) {
+ if (data[i] > 0x1fff) {
+ dev_err(dev->class_dev,
+ "Unipolar resolution is only 13-bits\n");
+ return -EINVAL;
+ }
+ }
+
+ ret = apci3501_wait_for_dac(dev);
+ if (ret)
+ return ret;
+
+ outl(cfg | APCI3501_AO_DATA_VAL(val),
+ dev->iobase + APCI3501_AO_DATA_REG);
+
+ s->readback[chan] = val;
+ }
+
+ return insn->n;
+}
+
+#include "addi-data/hwdrv_apci3501.c"
+
+static int apci3501_di_insn_bits(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ data[1] = inl(dev->iobase + APCI3501_DI_REG) & 0x3;
+
+ return insn->n;
+}
+
+static int apci3501_do_insn_bits(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ s->state = inl(dev->iobase + APCI3501_DO_REG);
+
+ if (comedi_dio_update_state(s, data))
+ outl(s->state, dev->iobase + APCI3501_DO_REG);
+
+ data[1] = s->state;
+
+ return insn->n;
+}
+
+static void apci3501_eeprom_wait(unsigned long iobase)
+{
+ unsigned char val;
+
+ do {
+ val = inb(iobase + AMCC_OP_REG_MCSR_NVCMD);
+ } while (val & 0x80);
+}
+
+static unsigned short apci3501_eeprom_readw(unsigned long iobase,
+ unsigned short addr)
+{
+ unsigned short val = 0;
+ unsigned char tmp;
+ unsigned char i;
+
+ /* Add the offset to the start of the user data */
+ addr += NVRAM_USER_DATA_START;
+
+ for (i = 0; i < 2; i++) {
+ /* Load the low 8 bit address */
+ outb(NVCMD_LOAD_LOW, iobase + AMCC_OP_REG_MCSR_NVCMD);
+ apci3501_eeprom_wait(iobase);
+ outb((addr + i) & 0xff, iobase + AMCC_OP_REG_MCSR_NVDATA);
+ apci3501_eeprom_wait(iobase);
+
+ /* Load the high 8 bit address */
+ outb(NVCMD_LOAD_HIGH, iobase + AMCC_OP_REG_MCSR_NVCMD);
+ apci3501_eeprom_wait(iobase);
+ outb(((addr + i) >> 8) & 0xff,
+ iobase + AMCC_OP_REG_MCSR_NVDATA);
+ apci3501_eeprom_wait(iobase);
+
+ /* Read the eeprom data byte */
+ outb(NVCMD_BEGIN_READ, iobase + AMCC_OP_REG_MCSR_NVCMD);
+ apci3501_eeprom_wait(iobase);
+ tmp = inb(iobase + AMCC_OP_REG_MCSR_NVDATA);
+ apci3501_eeprom_wait(iobase);
+
+ if (i == 0)
+ val |= tmp;
+ else
+ val |= (tmp << 8);
+ }
+
+ return val;
+}
+
+static int apci3501_eeprom_get_ao_n_chan(struct comedi_device *dev)
+{
+ struct apci3501_private *devpriv = dev->private;
+ unsigned long iobase = devpriv->i_IobaseAmcc;
+ unsigned char nfuncs;
+ int i;
+
+ nfuncs = apci3501_eeprom_readw(iobase, 10) & 0xff;
+
+ /* Read functionality details */
+ for (i = 0; i < nfuncs; i++) {
+ unsigned short offset = i * 4;
+ unsigned short addr;
+ unsigned char func;
+ unsigned short val;
+
+ func = apci3501_eeprom_readw(iobase, 12 + offset) & 0x3f;
+ addr = apci3501_eeprom_readw(iobase, 14 + offset);
+
+ if (func == EEPROM_ANALOGOUTPUT) {
+ val = apci3501_eeprom_readw(iobase, addr + 10);
+ return (val >> 4) & 0x3ff;
+ }
+ }
+ return 0;
+}
+
+static int apci3501_eeprom_insn_read(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ struct apci3501_private *devpriv = dev->private;
+ unsigned short addr = CR_CHAN(insn->chanspec);
+
+ data[0] = apci3501_eeprom_readw(devpriv->i_IobaseAmcc, 2 * addr);
+
+ return insn->n;
+}
+
+static irqreturn_t apci3501_interrupt(int irq, void *d)
+{
+ struct comedi_device *dev = d;
+ struct apci3501_private *devpriv = dev->private;
+ unsigned int ui_Timer_AOWatchdog;
+ unsigned long ul_Command1;
+
+ /* Disable Interrupt */
+ ul_Command1 = inl(dev->iobase + APCI3501_TIMER_CTRL_REG);
+ ul_Command1 = ul_Command1 & 0xFFFFF9FDul;
+ outl(ul_Command1, dev->iobase + APCI3501_TIMER_CTRL_REG);
+
+ ui_Timer_AOWatchdog = inl(dev->iobase + APCI3501_TIMER_IRQ_REG) & 0x1;
+ if ((!ui_Timer_AOWatchdog)) {
+ dev_err(dev->class_dev, "IRQ from unknown source\n");
+ return IRQ_NONE;
+ }
+
+ /* Enable Interrupt Send a signal to from kernel to user space */
+ send_sig(SIGIO, devpriv->tsk_Current, 0);
+ ul_Command1 = inl(dev->iobase + APCI3501_TIMER_CTRL_REG);
+ ul_Command1 = (ul_Command1 & 0xFFFFF9FDul) | 1 << 1;
+ outl(ul_Command1, dev->iobase + APCI3501_TIMER_CTRL_REG);
+ inl(dev->iobase + APCI3501_TIMER_STATUS_REG);
+
+ return IRQ_HANDLED;
+}
+
+static int apci3501_reset(struct comedi_device *dev)
+{
+ unsigned int val;
+ int chan;
+ int ret;
+
+ /* Reset all digital outputs to "0" */
+ outl(0x0, dev->iobase + APCI3501_DO_REG);
+
+ /* Default all analog outputs to 0V (bipolar) */
+ outl(APCI3501_AO_CTRL_BIPOLAR,
+ dev->iobase + APCI3501_AO_CTRL_STATUS_REG);
+ val = APCI3501_AO_DATA_BIPOLAR | APCI3501_AO_DATA_VAL(0);
+
+ /* Set all analog output channels */
+ for (chan = 0; chan < 8; chan++) {
+ ret = apci3501_wait_for_dac(dev);
+ if (ret) {
+ dev_warn(dev->class_dev,
+ "%s: DAC not-ready for channel %i\n",
+ __func__, chan);
+ } else {
+ outl(val | APCI3501_AO_DATA_CHAN(chan),
+ dev->iobase + APCI3501_AO_DATA_REG);
+ }
+ }
+
+ return 0;
+}
+
+static int apci3501_auto_attach(struct comedi_device *dev,
+ unsigned long context_unused)
+{
+ struct pci_dev *pcidev = comedi_to_pci_dev(dev);
+ struct apci3501_private *devpriv;
+ struct comedi_subdevice *s;
+ int ao_n_chan;
+ int ret;
+
+ devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
+ if (!devpriv)
+ return -ENOMEM;
+
+ ret = comedi_pci_enable(dev);
+ if (ret)
+ return ret;
+
+ dev->iobase = pci_resource_start(pcidev, 1);
+ devpriv->i_IobaseAmcc = pci_resource_start(pcidev, 0);
+
+ ao_n_chan = apci3501_eeprom_get_ao_n_chan(dev);
+
+ if (pcidev->irq > 0) {
+ ret = request_irq(pcidev->irq, apci3501_interrupt, IRQF_SHARED,
+ dev->board_name, dev);
+ if (ret == 0)
+ dev->irq = pcidev->irq;
+ }
+
+ ret = comedi_alloc_subdevices(dev, 5);
+ if (ret)
+ return ret;
+
+ /* Initialize the analog output subdevice */
+ s = &dev->subdevices[0];
+ if (ao_n_chan) {
+ s->type = COMEDI_SUBD_AO;
+ s->subdev_flags = SDF_WRITABLE | SDF_GROUND | SDF_COMMON;
+ s->n_chan = ao_n_chan;
+ s->maxdata = 0x3fff;
+ s->range_table = &apci3501_ao_range;
+ s->insn_write = apci3501_ao_insn_write;
+
+ ret = comedi_alloc_subdev_readback(s);
+ if (ret)
+ return ret;
+ } else {
+ s->type = COMEDI_SUBD_UNUSED;
+ }
+
+ /* Initialize the digital input subdevice */
+ s = &dev->subdevices[1];
+ s->type = COMEDI_SUBD_DI;
+ s->subdev_flags = SDF_READABLE;
+ s->n_chan = 2;
+ s->maxdata = 1;
+ s->range_table = &range_digital;
+ s->insn_bits = apci3501_di_insn_bits;
+
+ /* Initialize the digital output subdevice */
+ s = &dev->subdevices[2];
+ s->type = COMEDI_SUBD_DO;
+ s->subdev_flags = SDF_WRITABLE;
+ s->n_chan = 2;
+ s->maxdata = 1;
+ s->range_table = &range_digital;
+ s->insn_bits = apci3501_do_insn_bits;
+
+ /* Initialize the timer/watchdog subdevice */
+ s = &dev->subdevices[3];
+ s->type = COMEDI_SUBD_TIMER;
+ s->subdev_flags = SDF_WRITABLE;
+ s->n_chan = 1;
+ s->maxdata = 0;
+ s->len_chanlist = 1;
+ s->range_table = &range_digital;
+ s->insn_write = apci3501_write_insn_timer;
+ s->insn_read = apci3501_read_insn_timer;
+ s->insn_config = apci3501_config_insn_timer;
+
+ /* Initialize the eeprom subdevice */
+ s = &dev->subdevices[4];
+ s->type = COMEDI_SUBD_MEMORY;
+ s->subdev_flags = SDF_READABLE | SDF_INTERNAL;
+ s->n_chan = 256;
+ s->maxdata = 0xffff;
+ s->insn_read = apci3501_eeprom_insn_read;
+
+ apci3501_reset(dev);
+ return 0;
+}
+
+static void apci3501_detach(struct comedi_device *dev)
+{
+ if (dev->iobase)
+ apci3501_reset(dev);
+ comedi_pci_detach(dev);
+}
+
+static struct comedi_driver apci3501_driver = {
+ .driver_name = "addi_apci_3501",
+ .module = THIS_MODULE,
+ .auto_attach = apci3501_auto_attach,
+ .detach = apci3501_detach,
+};
+
+static int apci3501_pci_probe(struct pci_dev *dev,
+ const struct pci_device_id *id)
+{
+ return comedi_pci_auto_config(dev, &apci3501_driver, id->driver_data);
+}
+
+static const struct pci_device_id apci3501_pci_table[] = {
+ { PCI_DEVICE(PCI_VENDOR_ID_ADDIDATA, 0x3001) },
+ { 0 }
+};
+MODULE_DEVICE_TABLE(pci, apci3501_pci_table);
+
+static struct pci_driver apci3501_pci_driver = {
+ .name = "addi_apci_3501",
+ .id_table = apci3501_pci_table,
+ .probe = apci3501_pci_probe,
+ .remove = comedi_pci_auto_unconfig,
+};
+module_comedi_pci_driver(apci3501_driver, apci3501_pci_driver);
+
+MODULE_DESCRIPTION("ADDI-DATA APCI-3501 Analog output board");
+MODULE_AUTHOR("Comedi http://www.comedi.org");
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/comedi/drivers/addi_apci_3xxx.c b/drivers/staging/comedi/drivers/addi_apci_3xxx.c
new file mode 100644
index 000000000..bef6efc84
--- /dev/null
+++ b/drivers/staging/comedi/drivers/addi_apci_3xxx.c
@@ -0,0 +1,968 @@
+/*
+ * addi_apci_3xxx.c
+ * Copyright (C) 2004,2005 ADDI-DATA GmbH for the source code of this module.
+ * Project manager: S. Weber
+ *
+ * ADDI-DATA GmbH
+ * Dieselstrasse 3
+ * D-77833 Ottersweier
+ * Tel: +19(0)7223/9493-0
+ * Fax: +49(0)7223/9493-92
+ * http://www.addi-data.com
+ * info@addi-data.com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ */
+
+#include <linux/module.h>
+#include <linux/interrupt.h>
+
+#include "../comedi_pci.h"
+
+#define CONV_UNIT_NS (1 << 0)
+#define CONV_UNIT_US (1 << 1)
+#define CONV_UNIT_MS (1 << 2)
+
+static const struct comedi_lrange apci3xxx_ai_range = {
+ 8, {
+ BIP_RANGE(10),
+ BIP_RANGE(5),
+ BIP_RANGE(2),
+ BIP_RANGE(1),
+ UNI_RANGE(10),
+ UNI_RANGE(5),
+ UNI_RANGE(2),
+ UNI_RANGE(1)
+ }
+};
+
+static const struct comedi_lrange apci3xxx_ao_range = {
+ 2, {
+ BIP_RANGE(10),
+ UNI_RANGE(10)
+ }
+};
+
+enum apci3xxx_boardid {
+ BOARD_APCI3000_16,
+ BOARD_APCI3000_8,
+ BOARD_APCI3000_4,
+ BOARD_APCI3006_16,
+ BOARD_APCI3006_8,
+ BOARD_APCI3006_4,
+ BOARD_APCI3010_16,
+ BOARD_APCI3010_8,
+ BOARD_APCI3010_4,
+ BOARD_APCI3016_16,
+ BOARD_APCI3016_8,
+ BOARD_APCI3016_4,
+ BOARD_APCI3100_16_4,
+ BOARD_APCI3100_8_4,
+ BOARD_APCI3106_16_4,
+ BOARD_APCI3106_8_4,
+ BOARD_APCI3110_16_4,
+ BOARD_APCI3110_8_4,
+ BOARD_APCI3116_16_4,
+ BOARD_APCI3116_8_4,
+ BOARD_APCI3003,
+ BOARD_APCI3002_16,
+ BOARD_APCI3002_8,
+ BOARD_APCI3002_4,
+ BOARD_APCI3500,
+};
+
+struct apci3xxx_boardinfo {
+ const char *name;
+ int ai_subdev_flags;
+ int ai_n_chan;
+ unsigned int ai_maxdata;
+ unsigned char ai_conv_units;
+ unsigned int ai_min_acq_ns;
+ unsigned int has_ao:1;
+ unsigned int has_dig_in:1;
+ unsigned int has_dig_out:1;
+ unsigned int has_ttl_io:1;
+};
+
+static const struct apci3xxx_boardinfo apci3xxx_boardtypes[] = {
+ [BOARD_APCI3000_16] = {
+ .name = "apci3000-16",
+ .ai_subdev_flags = SDF_COMMON | SDF_GROUND | SDF_DIFF,
+ .ai_n_chan = 16,
+ .ai_maxdata = 0x0fff,
+ .ai_conv_units = CONV_UNIT_MS | CONV_UNIT_US,
+ .ai_min_acq_ns = 10000,
+ .has_ttl_io = 1,
+ },
+ [BOARD_APCI3000_8] = {
+ .name = "apci3000-8",
+ .ai_subdev_flags = SDF_COMMON | SDF_GROUND | SDF_DIFF,
+ .ai_n_chan = 8,
+ .ai_maxdata = 0x0fff,
+ .ai_conv_units = CONV_UNIT_MS | CONV_UNIT_US,
+ .ai_min_acq_ns = 10000,
+ .has_ttl_io = 1,
+ },
+ [BOARD_APCI3000_4] = {
+ .name = "apci3000-4",
+ .ai_subdev_flags = SDF_COMMON | SDF_GROUND | SDF_DIFF,
+ .ai_n_chan = 4,
+ .ai_maxdata = 0x0fff,
+ .ai_conv_units = CONV_UNIT_MS | CONV_UNIT_US,
+ .ai_min_acq_ns = 10000,
+ .has_ttl_io = 1,
+ },
+ [BOARD_APCI3006_16] = {
+ .name = "apci3006-16",
+ .ai_subdev_flags = SDF_COMMON | SDF_GROUND | SDF_DIFF,
+ .ai_n_chan = 16,
+ .ai_maxdata = 0xffff,
+ .ai_conv_units = CONV_UNIT_MS | CONV_UNIT_US,
+ .ai_min_acq_ns = 10000,
+ .has_ttl_io = 1,
+ },
+ [BOARD_APCI3006_8] = {
+ .name = "apci3006-8",
+ .ai_subdev_flags = SDF_COMMON | SDF_GROUND | SDF_DIFF,
+ .ai_n_chan = 8,
+ .ai_maxdata = 0xffff,
+ .ai_conv_units = CONV_UNIT_MS | CONV_UNIT_US,
+ .ai_min_acq_ns = 10000,
+ .has_ttl_io = 1,
+ },
+ [BOARD_APCI3006_4] = {
+ .name = "apci3006-4",
+ .ai_subdev_flags = SDF_COMMON | SDF_GROUND | SDF_DIFF,
+ .ai_n_chan = 4,
+ .ai_maxdata = 0xffff,
+ .ai_conv_units = CONV_UNIT_MS | CONV_UNIT_US,
+ .ai_min_acq_ns = 10000,
+ .has_ttl_io = 1,
+ },
+ [BOARD_APCI3010_16] = {
+ .name = "apci3010-16",
+ .ai_subdev_flags = SDF_COMMON | SDF_GROUND | SDF_DIFF,
+ .ai_n_chan = 16,
+ .ai_maxdata = 0x0fff,
+ .ai_conv_units = CONV_UNIT_MS | CONV_UNIT_US,
+ .ai_min_acq_ns = 5000,
+ .has_dig_in = 1,
+ .has_dig_out = 1,
+ .has_ttl_io = 1,
+ },
+ [BOARD_APCI3010_8] = {
+ .name = "apci3010-8",
+ .ai_subdev_flags = SDF_COMMON | SDF_GROUND | SDF_DIFF,
+ .ai_n_chan = 8,
+ .ai_maxdata = 0x0fff,
+ .ai_conv_units = CONV_UNIT_MS | CONV_UNIT_US,
+ .ai_min_acq_ns = 5000,
+ .has_dig_in = 1,
+ .has_dig_out = 1,
+ .has_ttl_io = 1,
+ },
+ [BOARD_APCI3010_4] = {
+ .name = "apci3010-4",
+ .ai_subdev_flags = SDF_COMMON | SDF_GROUND | SDF_DIFF,
+ .ai_n_chan = 4,
+ .ai_maxdata = 0x0fff,
+ .ai_conv_units = CONV_UNIT_MS | CONV_UNIT_US,
+ .ai_min_acq_ns = 5000,
+ .has_dig_in = 1,
+ .has_dig_out = 1,
+ .has_ttl_io = 1,
+ },
+ [BOARD_APCI3016_16] = {
+ .name = "apci3016-16",
+ .ai_subdev_flags = SDF_COMMON | SDF_GROUND | SDF_DIFF,
+ .ai_n_chan = 16,
+ .ai_maxdata = 0xffff,
+ .ai_conv_units = CONV_UNIT_MS | CONV_UNIT_US,
+ .ai_min_acq_ns = 5000,
+ .has_dig_in = 1,
+ .has_dig_out = 1,
+ .has_ttl_io = 1,
+ },
+ [BOARD_APCI3016_8] = {
+ .name = "apci3016-8",
+ .ai_subdev_flags = SDF_COMMON | SDF_GROUND | SDF_DIFF,
+ .ai_n_chan = 8,
+ .ai_maxdata = 0xffff,
+ .ai_conv_units = CONV_UNIT_MS | CONV_UNIT_US,
+ .ai_min_acq_ns = 5000,
+ .has_dig_in = 1,
+ .has_dig_out = 1,
+ .has_ttl_io = 1,
+ },
+ [BOARD_APCI3016_4] = {
+ .name = "apci3016-4",
+ .ai_subdev_flags = SDF_COMMON | SDF_GROUND | SDF_DIFF,
+ .ai_n_chan = 4,
+ .ai_maxdata = 0xffff,
+ .ai_conv_units = CONV_UNIT_MS | CONV_UNIT_US,
+ .ai_min_acq_ns = 5000,
+ .has_dig_in = 1,
+ .has_dig_out = 1,
+ .has_ttl_io = 1,
+ },
+ [BOARD_APCI3100_16_4] = {
+ .name = "apci3100-16-4",
+ .ai_subdev_flags = SDF_COMMON | SDF_GROUND | SDF_DIFF,
+ .ai_n_chan = 16,
+ .ai_maxdata = 0x0fff,
+ .ai_conv_units = CONV_UNIT_MS | CONV_UNIT_US,
+ .ai_min_acq_ns = 10000,
+ .has_ao = 1,
+ .has_ttl_io = 1,
+ },
+ [BOARD_APCI3100_8_4] = {
+ .name = "apci3100-8-4",
+ .ai_subdev_flags = SDF_COMMON | SDF_GROUND | SDF_DIFF,
+ .ai_n_chan = 8,
+ .ai_maxdata = 0x0fff,
+ .ai_conv_units = CONV_UNIT_MS | CONV_UNIT_US,
+ .ai_min_acq_ns = 10000,
+ .has_ao = 1,
+ .has_ttl_io = 1,
+ },
+ [BOARD_APCI3106_16_4] = {
+ .name = "apci3106-16-4",
+ .ai_subdev_flags = SDF_COMMON | SDF_GROUND | SDF_DIFF,
+ .ai_n_chan = 16,
+ .ai_maxdata = 0xffff,
+ .ai_conv_units = CONV_UNIT_MS | CONV_UNIT_US,
+ .ai_min_acq_ns = 10000,
+ .has_ao = 1,
+ .has_ttl_io = 1,
+ },
+ [BOARD_APCI3106_8_4] = {
+ .name = "apci3106-8-4",
+ .ai_subdev_flags = SDF_COMMON | SDF_GROUND | SDF_DIFF,
+ .ai_n_chan = 8,
+ .ai_maxdata = 0xffff,
+ .ai_conv_units = CONV_UNIT_MS | CONV_UNIT_US,
+ .ai_min_acq_ns = 10000,
+ .has_ao = 1,
+ .has_ttl_io = 1,
+ },
+ [BOARD_APCI3110_16_4] = {
+ .name = "apci3110-16-4",
+ .ai_subdev_flags = SDF_COMMON | SDF_GROUND | SDF_DIFF,
+ .ai_n_chan = 16,
+ .ai_maxdata = 0x0fff,
+ .ai_conv_units = CONV_UNIT_MS | CONV_UNIT_US,
+ .ai_min_acq_ns = 5000,
+ .has_ao = 1,
+ .has_dig_in = 1,
+ .has_dig_out = 1,
+ .has_ttl_io = 1,
+ },
+ [BOARD_APCI3110_8_4] = {
+ .name = "apci3110-8-4",
+ .ai_subdev_flags = SDF_COMMON | SDF_GROUND | SDF_DIFF,
+ .ai_n_chan = 8,
+ .ai_maxdata = 0x0fff,
+ .ai_conv_units = CONV_UNIT_MS | CONV_UNIT_US,
+ .ai_min_acq_ns = 5000,
+ .has_ao = 1,
+ .has_dig_in = 1,
+ .has_dig_out = 1,
+ .has_ttl_io = 1,
+ },
+ [BOARD_APCI3116_16_4] = {
+ .name = "apci3116-16-4",
+ .ai_subdev_flags = SDF_COMMON | SDF_GROUND | SDF_DIFF,
+ .ai_n_chan = 16,
+ .ai_maxdata = 0xffff,
+ .ai_conv_units = CONV_UNIT_MS | CONV_UNIT_US,
+ .ai_min_acq_ns = 5000,
+ .has_ao = 1,
+ .has_dig_in = 1,
+ .has_dig_out = 1,
+ .has_ttl_io = 1,
+ },
+ [BOARD_APCI3116_8_4] = {
+ .name = "apci3116-8-4",
+ .ai_subdev_flags = SDF_COMMON | SDF_GROUND | SDF_DIFF,
+ .ai_n_chan = 8,
+ .ai_maxdata = 0xffff,
+ .ai_conv_units = CONV_UNIT_MS | CONV_UNIT_US,
+ .ai_min_acq_ns = 5000,
+ .has_ao = 1,
+ .has_dig_in = 1,
+ .has_dig_out = 1,
+ .has_ttl_io = 1,
+ },
+ [BOARD_APCI3003] = {
+ .name = "apci3003",
+ .ai_subdev_flags = SDF_DIFF,
+ .ai_n_chan = 4,
+ .ai_maxdata = 0xffff,
+ .ai_conv_units = CONV_UNIT_MS | CONV_UNIT_US |
+ CONV_UNIT_NS,
+ .ai_min_acq_ns = 2500,
+ .has_dig_in = 1,
+ .has_dig_out = 1,
+ },
+ [BOARD_APCI3002_16] = {
+ .name = "apci3002-16",
+ .ai_subdev_flags = SDF_DIFF,
+ .ai_n_chan = 16,
+ .ai_maxdata = 0xffff,
+ .ai_conv_units = CONV_UNIT_MS | CONV_UNIT_US,
+ .ai_min_acq_ns = 5000,
+ .has_dig_in = 1,
+ .has_dig_out = 1,
+ },
+ [BOARD_APCI3002_8] = {
+ .name = "apci3002-8",
+ .ai_subdev_flags = SDF_DIFF,
+ .ai_n_chan = 8,
+ .ai_maxdata = 0xffff,
+ .ai_conv_units = CONV_UNIT_MS | CONV_UNIT_US,
+ .ai_min_acq_ns = 5000,
+ .has_dig_in = 1,
+ .has_dig_out = 1,
+ },
+ [BOARD_APCI3002_4] = {
+ .name = "apci3002-4",
+ .ai_subdev_flags = SDF_DIFF,
+ .ai_n_chan = 4,
+ .ai_maxdata = 0xffff,
+ .ai_conv_units = CONV_UNIT_MS | CONV_UNIT_US,
+ .ai_min_acq_ns = 5000,
+ .has_dig_in = 1,
+ .has_dig_out = 1,
+ },
+ [BOARD_APCI3500] = {
+ .name = "apci3500",
+ .has_ao = 1,
+ .has_ttl_io = 1,
+ },
+};
+
+struct apci3xxx_private {
+ unsigned int ai_timer;
+ unsigned char ai_time_base;
+};
+
+static irqreturn_t apci3xxx_irq_handler(int irq, void *d)
+{
+ struct comedi_device *dev = d;
+ struct comedi_subdevice *s = dev->read_subdev;
+ unsigned int status;
+ unsigned int val;
+
+ /* Test if interrupt occur */
+ status = readl(dev->mmio + 16);
+ if ((status & 0x2) == 0x2) {
+ /* Reset the interrupt */
+ writel(status, dev->mmio + 16);
+
+ val = readl(dev->mmio + 28);
+ comedi_buf_write_samples(s, &val, 1);
+
+ s->async->events |= COMEDI_CB_EOA;
+ comedi_handle_events(dev, s);
+
+ return IRQ_HANDLED;
+ }
+ return IRQ_NONE;
+}
+
+static int apci3xxx_ai_started(struct comedi_device *dev)
+{
+ if ((readl(dev->mmio + 8) & 0x80000) == 0x80000)
+ return 1;
+
+ return 0;
+}
+
+static int apci3xxx_ai_setup(struct comedi_device *dev, unsigned int chanspec)
+{
+ unsigned int chan = CR_CHAN(chanspec);
+ unsigned int range = CR_RANGE(chanspec);
+ unsigned int aref = CR_AREF(chanspec);
+ unsigned int delay_mode;
+ unsigned int val;
+
+ if (apci3xxx_ai_started(dev))
+ return -EBUSY;
+
+ /* Clear the FIFO */
+ writel(0x10000, dev->mmio + 12);
+
+ /* Get and save the delay mode */
+ delay_mode = readl(dev->mmio + 4);
+ delay_mode &= 0xfffffef0;
+
+ /* Channel configuration selection */
+ writel(delay_mode, dev->mmio + 4);
+
+ /* Make the configuration */
+ val = (range & 3) | ((range >> 2) << 6) |
+ ((aref == AREF_DIFF) << 7);
+ writel(val, dev->mmio + 0);
+
+ /* Channel selection */
+ writel(delay_mode | 0x100, dev->mmio + 4);
+ writel(chan, dev->mmio + 0);
+
+ /* Restore delay mode */
+ writel(delay_mode, dev->mmio + 4);
+
+ /* Set the number of sequence to 1 */
+ writel(1, dev->mmio + 48);
+
+ return 0;
+}
+
+static int apci3xxx_ai_eoc(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned long context)
+{
+ unsigned int status;
+
+ status = readl(dev->mmio + 20);
+ if (status & 0x1)
+ return 0;
+ return -EBUSY;
+}
+
+static int apci3xxx_ai_insn_read(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ int ret;
+ int i;
+
+ ret = apci3xxx_ai_setup(dev, insn->chanspec);
+ if (ret)
+ return ret;
+
+ for (i = 0; i < insn->n; i++) {
+ /* Start the conversion */
+ writel(0x80000, dev->mmio + 8);
+
+ /* Wait the EOS */
+ ret = comedi_timeout(dev, s, insn, apci3xxx_ai_eoc, 0);
+ if (ret)
+ return ret;
+
+ /* Read the analog value */
+ data[i] = readl(dev->mmio + 28);
+ }
+
+ return insn->n;
+}
+
+static int apci3xxx_ai_ns_to_timer(struct comedi_device *dev,
+ unsigned int *ns, unsigned int flags)
+{
+ const struct apci3xxx_boardinfo *board = dev->board_ptr;
+ struct apci3xxx_private *devpriv = dev->private;
+ unsigned int base;
+ unsigned int timer;
+ int time_base;
+
+ /* time_base: 0 = ns, 1 = us, 2 = ms */
+ for (time_base = 0; time_base < 3; time_base++) {
+ /* skip unsupported time bases */
+ if (!(board->ai_conv_units & (1 << time_base)))
+ continue;
+
+ switch (time_base) {
+ case 0:
+ base = 1;
+ break;
+ case 1:
+ base = 1000;
+ break;
+ case 2:
+ base = 1000000;
+ break;
+ }
+
+ switch (flags & CMDF_ROUND_MASK) {
+ case CMDF_ROUND_NEAREST:
+ default:
+ timer = (*ns + base / 2) / base;
+ break;
+ case CMDF_ROUND_DOWN:
+ timer = *ns / base;
+ break;
+ case CMDF_ROUND_UP:
+ timer = (*ns + base - 1) / base;
+ break;
+ }
+
+ if (timer < 0x10000) {
+ devpriv->ai_time_base = time_base;
+ devpriv->ai_timer = timer;
+ *ns = timer * time_base;
+ return 0;
+ }
+ }
+ return -EINVAL;
+}
+
+static int apci3xxx_ai_cmdtest(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_cmd *cmd)
+{
+ const struct apci3xxx_boardinfo *board = dev->board_ptr;
+ int err = 0;
+ unsigned int arg;
+
+ /* Step 1 : check if triggers are trivially valid */
+
+ err |= comedi_check_trigger_src(&cmd->start_src, TRIG_NOW);
+ err |= comedi_check_trigger_src(&cmd->scan_begin_src, TRIG_FOLLOW);
+ err |= comedi_check_trigger_src(&cmd->convert_src, TRIG_TIMER);
+ err |= comedi_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
+ err |= comedi_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE);
+
+ if (err)
+ return 1;
+
+ /* Step 2a : make sure trigger sources are unique */
+
+ err |= comedi_check_trigger_is_unique(cmd->stop_src);
+
+ /* Step 2b : and mutually compatible */
+
+ if (err)
+ return 2;
+
+ /* Step 3: check if arguments are trivially valid */
+
+ err |= comedi_check_trigger_arg_is(&cmd->start_arg, 0);
+ err |= comedi_check_trigger_arg_is(&cmd->scan_begin_arg, 0);
+ err |= comedi_check_trigger_arg_min(&cmd->convert_arg,
+ board->ai_min_acq_ns);
+ err |= comedi_check_trigger_arg_is(&cmd->scan_end_arg,
+ cmd->chanlist_len);
+
+ if (cmd->stop_src == TRIG_COUNT)
+ err |= comedi_check_trigger_arg_min(&cmd->stop_arg, 1);
+ else /* TRIG_NONE */
+ err |= comedi_check_trigger_arg_is(&cmd->stop_arg, 0);
+
+ if (err)
+ return 3;
+
+ /* step 4: fix up any arguments */
+
+ arg = cmd->convert_arg;
+ err |= apci3xxx_ai_ns_to_timer(dev, &arg, cmd->flags);
+ err |= comedi_check_trigger_arg_is(&cmd->convert_arg, arg);
+
+ if (err)
+ return 4;
+
+ return 0;
+}
+
+static int apci3xxx_ai_cmd(struct comedi_device *dev,
+ struct comedi_subdevice *s)
+{
+ struct apci3xxx_private *devpriv = dev->private;
+ struct comedi_cmd *cmd = &s->async->cmd;
+ int ret;
+
+ ret = apci3xxx_ai_setup(dev, cmd->chanlist[0]);
+ if (ret)
+ return ret;
+
+ /* Set the convert timing unit */
+ writel(devpriv->ai_time_base, dev->mmio + 36);
+
+ /* Set the convert timing */
+ writel(devpriv->ai_timer, dev->mmio + 32);
+
+ /* Start the conversion */
+ writel(0x180000, dev->mmio + 8);
+
+ return 0;
+}
+
+static int apci3xxx_ai_cancel(struct comedi_device *dev,
+ struct comedi_subdevice *s)
+{
+ return 0;
+}
+
+static int apci3xxx_ao_eoc(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned long context)
+{
+ unsigned int status;
+
+ status = readl(dev->mmio + 96);
+ if (status & 0x100)
+ return 0;
+ return -EBUSY;
+}
+
+static int apci3xxx_ao_insn_write(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ unsigned int chan = CR_CHAN(insn->chanspec);
+ unsigned int range = CR_RANGE(insn->chanspec);
+ int ret;
+ int i;
+
+ for (i = 0; i < insn->n; i++) {
+ unsigned int val = data[i];
+
+ /* Set the range selection */
+ writel(range, dev->mmio + 96);
+
+ /* Write the analog value to the selected channel */
+ writel((val << 8) | chan, dev->mmio + 100);
+
+ /* Wait the end of transfer */
+ ret = comedi_timeout(dev, s, insn, apci3xxx_ao_eoc, 0);
+ if (ret)
+ return ret;
+
+ s->readback[chan] = val;
+ }
+
+ return insn->n;
+}
+
+static int apci3xxx_di_insn_bits(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ data[1] = inl(dev->iobase + 32) & 0xf;
+
+ return insn->n;
+}
+
+static int apci3xxx_do_insn_bits(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ s->state = inl(dev->iobase + 48) & 0xf;
+
+ if (comedi_dio_update_state(s, data))
+ outl(s->state, dev->iobase + 48);
+
+ data[1] = s->state;
+
+ return insn->n;
+}
+
+static int apci3xxx_dio_insn_config(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ unsigned int chan = CR_CHAN(insn->chanspec);
+ unsigned int mask = 0;
+ int ret;
+
+ /*
+ * Port 0 (channels 0-7) are always inputs
+ * Port 1 (channels 8-15) are always outputs
+ * Port 2 (channels 16-23) are programmable i/o
+ */
+ if (data[0] != INSN_CONFIG_DIO_QUERY) {
+ /* ignore all other instructions for ports 0 and 1 */
+ if (chan < 16)
+ return -EINVAL;
+
+ /* changing any channel in port 2 changes the entire port */
+ mask = 0xff0000;
+ }
+
+ ret = comedi_dio_insn_config(dev, s, insn, data, mask);
+ if (ret)
+ return ret;
+
+ /* update port 2 configuration */
+ outl((s->io_bits >> 24) & 0xff, dev->iobase + 224);
+
+ return insn->n;
+}
+
+static int apci3xxx_dio_insn_bits(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ unsigned int mask;
+ unsigned int val;
+
+ mask = comedi_dio_update_state(s, data);
+ if (mask) {
+ if (mask & 0xff)
+ outl(s->state & 0xff, dev->iobase + 80);
+ if (mask & 0xff0000)
+ outl((s->state >> 16) & 0xff, dev->iobase + 112);
+ }
+
+ val = inl(dev->iobase + 80);
+ val |= (inl(dev->iobase + 64) << 8);
+ if (s->io_bits & 0xff0000)
+ val |= (inl(dev->iobase + 112) << 16);
+ else
+ val |= (inl(dev->iobase + 96) << 16);
+
+ data[1] = val;
+
+ return insn->n;
+}
+
+static int apci3xxx_reset(struct comedi_device *dev)
+{
+ unsigned int val;
+ int i;
+
+ /* Disable the interrupt */
+ disable_irq(dev->irq);
+
+ /* Clear the start command */
+ writel(0, dev->mmio + 8);
+
+ /* Reset the interrupt flags */
+ val = readl(dev->mmio + 16);
+ writel(val, dev->mmio + 16);
+
+ /* clear the EOS */
+ readl(dev->mmio + 20);
+
+ /* Clear the FIFO */
+ for (i = 0; i < 16; i++)
+ val = readl(dev->mmio + 28);
+
+ /* Enable the interrupt */
+ enable_irq(dev->irq);
+
+ return 0;
+}
+
+static int apci3xxx_auto_attach(struct comedi_device *dev,
+ unsigned long context)
+{
+ struct pci_dev *pcidev = comedi_to_pci_dev(dev);
+ const struct apci3xxx_boardinfo *board = NULL;
+ struct apci3xxx_private *devpriv;
+ struct comedi_subdevice *s;
+ int n_subdevices;
+ int subdev;
+ int ret;
+
+ if (context < ARRAY_SIZE(apci3xxx_boardtypes))
+ board = &apci3xxx_boardtypes[context];
+ if (!board)
+ return -ENODEV;
+ dev->board_ptr = board;
+ dev->board_name = board->name;
+
+ devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
+ if (!devpriv)
+ return -ENOMEM;
+
+ ret = comedi_pci_enable(dev);
+ if (ret)
+ return ret;
+
+ dev->iobase = pci_resource_start(pcidev, 2);
+ dev->mmio = pci_ioremap_bar(pcidev, 3);
+
+ if (pcidev->irq > 0) {
+ ret = request_irq(pcidev->irq, apci3xxx_irq_handler,
+ IRQF_SHARED, dev->board_name, dev);
+ if (ret == 0)
+ dev->irq = pcidev->irq;
+ }
+
+ n_subdevices = (board->ai_n_chan ? 0 : 1) + board->has_ao +
+ board->has_dig_in + board->has_dig_out +
+ board->has_ttl_io;
+ ret = comedi_alloc_subdevices(dev, n_subdevices);
+ if (ret)
+ return ret;
+
+ subdev = 0;
+
+ /* Analog Input subdevice */
+ if (board->ai_n_chan) {
+ s = &dev->subdevices[subdev];
+ s->type = COMEDI_SUBD_AI;
+ s->subdev_flags = SDF_READABLE | board->ai_subdev_flags;
+ s->n_chan = board->ai_n_chan;
+ s->maxdata = board->ai_maxdata;
+ s->range_table = &apci3xxx_ai_range;
+ s->insn_read = apci3xxx_ai_insn_read;
+ if (dev->irq) {
+ /*
+ * FIXME: The hardware supports multiple scan modes
+ * but the original addi-data driver only supported
+ * reading a single channel with interrupts. Need a
+ * proper datasheet to fix this.
+ *
+ * The following scan modes are supported by the
+ * hardware:
+ * 1) Single software scan
+ * 2) Single hardware triggered scan
+ * 3) Continuous software scan
+ * 4) Continuous software scan with timer delay
+ * 5) Continuous hardware triggered scan
+ * 6) Continuous hardware triggered scan with timer
+ * delay
+ *
+ * For now, limit the chanlist to a single channel.
+ */
+ dev->read_subdev = s;
+ s->subdev_flags |= SDF_CMD_READ;
+ s->len_chanlist = 1;
+ s->do_cmdtest = apci3xxx_ai_cmdtest;
+ s->do_cmd = apci3xxx_ai_cmd;
+ s->cancel = apci3xxx_ai_cancel;
+ }
+
+ subdev++;
+ }
+
+ /* Analog Output subdevice */
+ if (board->has_ao) {
+ s = &dev->subdevices[subdev];
+ s->type = COMEDI_SUBD_AO;
+ s->subdev_flags = SDF_WRITABLE | SDF_GROUND | SDF_COMMON;
+ s->n_chan = 4;
+ s->maxdata = 0x0fff;
+ s->range_table = &apci3xxx_ao_range;
+ s->insn_write = apci3xxx_ao_insn_write;
+
+ ret = comedi_alloc_subdev_readback(s);
+ if (ret)
+ return ret;
+
+ subdev++;
+ }
+
+ /* Digital Input subdevice */
+ if (board->has_dig_in) {
+ s = &dev->subdevices[subdev];
+ s->type = COMEDI_SUBD_DI;
+ s->subdev_flags = SDF_READABLE;
+ s->n_chan = 4;
+ s->maxdata = 1;
+ s->range_table = &range_digital;
+ s->insn_bits = apci3xxx_di_insn_bits;
+
+ subdev++;
+ }
+
+ /* Digital Output subdevice */
+ if (board->has_dig_out) {
+ s = &dev->subdevices[subdev];
+ s->type = COMEDI_SUBD_DO;
+ s->subdev_flags = SDF_WRITABLE;
+ s->n_chan = 4;
+ s->maxdata = 1;
+ s->range_table = &range_digital;
+ s->insn_bits = apci3xxx_do_insn_bits;
+
+ subdev++;
+ }
+
+ /* TTL Digital I/O subdevice */
+ if (board->has_ttl_io) {
+ s = &dev->subdevices[subdev];
+ s->type = COMEDI_SUBD_DIO;
+ s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
+ s->n_chan = 24;
+ s->maxdata = 1;
+ s->io_bits = 0xff; /* channels 0-7 are always outputs */
+ s->range_table = &range_digital;
+ s->insn_config = apci3xxx_dio_insn_config;
+ s->insn_bits = apci3xxx_dio_insn_bits;
+
+ subdev++;
+ }
+
+ apci3xxx_reset(dev);
+ return 0;
+}
+
+static void apci3xxx_detach(struct comedi_device *dev)
+{
+ if (dev->iobase)
+ apci3xxx_reset(dev);
+ comedi_pci_detach(dev);
+}
+
+static struct comedi_driver apci3xxx_driver = {
+ .driver_name = "addi_apci_3xxx",
+ .module = THIS_MODULE,
+ .auto_attach = apci3xxx_auto_attach,
+ .detach = apci3xxx_detach,
+};
+
+static int apci3xxx_pci_probe(struct pci_dev *dev,
+ const struct pci_device_id *id)
+{
+ return comedi_pci_auto_config(dev, &apci3xxx_driver, id->driver_data);
+}
+
+static const struct pci_device_id apci3xxx_pci_table[] = {
+ { PCI_VDEVICE(ADDIDATA, 0x3010), BOARD_APCI3000_16 },
+ { PCI_VDEVICE(ADDIDATA, 0x300f), BOARD_APCI3000_8 },
+ { PCI_VDEVICE(ADDIDATA, 0x300e), BOARD_APCI3000_4 },
+ { PCI_VDEVICE(ADDIDATA, 0x3013), BOARD_APCI3006_16 },
+ { PCI_VDEVICE(ADDIDATA, 0x3014), BOARD_APCI3006_8 },
+ { PCI_VDEVICE(ADDIDATA, 0x3015), BOARD_APCI3006_4 },
+ { PCI_VDEVICE(ADDIDATA, 0x3016), BOARD_APCI3010_16 },
+ { PCI_VDEVICE(ADDIDATA, 0x3017), BOARD_APCI3010_8 },
+ { PCI_VDEVICE(ADDIDATA, 0x3018), BOARD_APCI3010_4 },
+ { PCI_VDEVICE(ADDIDATA, 0x3019), BOARD_APCI3016_16 },
+ { PCI_VDEVICE(ADDIDATA, 0x301a), BOARD_APCI3016_8 },
+ { PCI_VDEVICE(ADDIDATA, 0x301b), BOARD_APCI3016_4 },
+ { PCI_VDEVICE(ADDIDATA, 0x301c), BOARD_APCI3100_16_4 },
+ { PCI_VDEVICE(ADDIDATA, 0x301d), BOARD_APCI3100_8_4 },
+ { PCI_VDEVICE(ADDIDATA, 0x301e), BOARD_APCI3106_16_4 },
+ { PCI_VDEVICE(ADDIDATA, 0x301f), BOARD_APCI3106_8_4 },
+ { PCI_VDEVICE(ADDIDATA, 0x3020), BOARD_APCI3110_16_4 },
+ { PCI_VDEVICE(ADDIDATA, 0x3021), BOARD_APCI3110_8_4 },
+ { PCI_VDEVICE(ADDIDATA, 0x3022), BOARD_APCI3116_16_4 },
+ { PCI_VDEVICE(ADDIDATA, 0x3023), BOARD_APCI3116_8_4 },
+ { PCI_VDEVICE(ADDIDATA, 0x300B), BOARD_APCI3003 },
+ { PCI_VDEVICE(ADDIDATA, 0x3002), BOARD_APCI3002_16 },
+ { PCI_VDEVICE(ADDIDATA, 0x3003), BOARD_APCI3002_8 },
+ { PCI_VDEVICE(ADDIDATA, 0x3004), BOARD_APCI3002_4 },
+ { PCI_VDEVICE(ADDIDATA, 0x3024), BOARD_APCI3500 },
+ { 0 }
+};
+MODULE_DEVICE_TABLE(pci, apci3xxx_pci_table);
+
+static struct pci_driver apci3xxx_pci_driver = {
+ .name = "addi_apci_3xxx",
+ .id_table = apci3xxx_pci_table,
+ .probe = apci3xxx_pci_probe,
+ .remove = comedi_pci_auto_unconfig,
+};
+module_comedi_pci_driver(apci3xxx_driver, apci3xxx_pci_driver);
+
+MODULE_AUTHOR("Comedi http://www.comedi.org");
+MODULE_DESCRIPTION("Comedi low-level driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/comedi/drivers/addi_tcw.h b/drivers/staging/comedi/drivers/addi_tcw.h
new file mode 100644
index 000000000..8794d4cbb
--- /dev/null
+++ b/drivers/staging/comedi/drivers/addi_tcw.h
@@ -0,0 +1,56 @@
+#ifndef _ADDI_TCW_H
+#define _ADDI_TCW_H
+
+/*
+ * Following are the generic definitions for the ADDI-DATA timer/counter/
+ * watchdog (TCW) registers and bits. Some of the registers are not used
+ * depending on the use of the TCW.
+ */
+
+#define ADDI_TCW_VAL_REG 0x00
+
+#define ADDI_TCW_SYNC_REG 0x00
+#define ADDI_TCW_SYNC_CTR_TRIG (1 << 8)
+#define ADDI_TCW_SYNC_CTR_DIS (1 << 7)
+#define ADDI_TCW_SYNC_CTR_ENA (1 << 6)
+#define ADDI_TCW_SYNC_TIMER_TRIG (1 << 5)
+#define ADDI_TCW_SYNC_TIMER_DIS (1 << 4)
+#define ADDI_TCW_SYNC_TIMER_ENA (1 << 3)
+#define ADDI_TCW_SYNC_WDOG_TRIG (1 << 2)
+#define ADDI_TCW_SYNC_WDOG_DIS (1 << 1)
+#define ADDI_TCW_SYNC_WDOG_ENA (1 << 0)
+
+#define ADDI_TCW_RELOAD_REG 0x04
+
+#define ADDI_TCW_TIMEBASE_REG 0x08
+
+#define ADDI_TCW_CTRL_REG 0x0c
+#define ADDI_TCW_CTRL_EXT_CLK_STATUS (1 << 21)
+#define ADDI_TCW_CTRL_CASCADE (1 << 20)
+#define ADDI_TCW_CTRL_CNTR_ENA (1 << 19)
+#define ADDI_TCW_CTRL_CNT_UP (1 << 18)
+#define ADDI_TCW_CTRL_EXT_CLK(x) ((x) << 16)
+#define ADDI_TCW_CTRL_OUT(x) ((x) << 11)
+#define ADDI_TCW_CTRL_GATE (1 << 10)
+#define ADDI_TCW_CTRL_TRIG (1 << 9)
+#define ADDI_TCW_CTRL_EXT_GATE(x) ((x) << 7)
+#define ADDI_TCW_CTRL_EXT_TRIG(x) ((x) << 5)
+#define ADDI_TCW_CTRL_TIMER_ENA (1 << 4)
+#define ADDI_TCW_CTRL_RESET_ENA (1 << 3)
+#define ADDI_TCW_CTRL_WARN_ENA (1 << 2)
+#define ADDI_TCW_CTRL_IRQ_ENA (1 << 1)
+#define ADDI_TCW_CTRL_ENA (1 << 0)
+
+#define ADDI_TCW_STATUS_REG 0x10
+#define ADDI_TCW_STATUS_SOFT_CLR (1 << 3)
+#define ADDI_TCW_STATUS_SOFT_TRIG (1 << 1)
+#define ADDI_TCW_STATUS_OVERFLOW (1 << 0)
+
+#define ADDI_TCW_IRQ_REG 0x14
+#define ADDI_TCW_IRQ (1 << 0)
+
+#define ADDI_TCW_WARN_TIMEVAL_REG 0x18
+
+#define ADDI_TCW_WARN_TIMEBASE_REG 0x1c
+
+#endif
diff --git a/drivers/staging/comedi/drivers/addi_watchdog.c b/drivers/staging/comedi/drivers/addi_watchdog.c
new file mode 100644
index 000000000..9d9853fe5
--- /dev/null
+++ b/drivers/staging/comedi/drivers/addi_watchdog.c
@@ -0,0 +1,149 @@
+/*
+ * COMEDI driver for the watchdog subdevice found on some addi-data boards
+ * Copyright (c) 2013 H Hartley Sweeten <hsweeten@visionengravers.com>
+ *
+ * Based on implementations in various addi-data COMEDI drivers.
+ *
+ * COMEDI - Linux Control and Measurement Device Interface
+ * Copyright (C) 1998 David A. Schleef <ds@schleef.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include "../comedidev.h"
+#include "addi_tcw.h"
+#include "addi_watchdog.h"
+
+struct addi_watchdog_private {
+ unsigned long iobase;
+ unsigned int wdog_ctrl;
+};
+
+/*
+ * The watchdog subdevice is configured with two INSN_CONFIG instructions:
+ *
+ * Enable the watchdog and set the reload timeout:
+ * data[0] = INSN_CONFIG_ARM
+ * data[1] = timeout reload value
+ *
+ * Disable the watchdog:
+ * data[0] = INSN_CONFIG_DISARM
+ */
+static int addi_watchdog_insn_config(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ struct addi_watchdog_private *spriv = s->private;
+ unsigned int reload;
+
+ switch (data[0]) {
+ case INSN_CONFIG_ARM:
+ spriv->wdog_ctrl = ADDI_TCW_CTRL_ENA;
+ reload = data[1] & s->maxdata;
+ outl(reload, spriv->iobase + ADDI_TCW_RELOAD_REG);
+
+ /* Time base is 20ms, let the user know the timeout */
+ dev_info(dev->class_dev, "watchdog enabled, timeout:%dms\n",
+ 20 * reload + 20);
+ break;
+ case INSN_CONFIG_DISARM:
+ spriv->wdog_ctrl = 0;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ outl(spriv->wdog_ctrl, spriv->iobase + ADDI_TCW_CTRL_REG);
+
+ return insn->n;
+}
+
+static int addi_watchdog_insn_read(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ struct addi_watchdog_private *spriv = s->private;
+ int i;
+
+ for (i = 0; i < insn->n; i++)
+ data[i] = inl(spriv->iobase + ADDI_TCW_STATUS_REG);
+
+ return insn->n;
+}
+
+static int addi_watchdog_insn_write(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ struct addi_watchdog_private *spriv = s->private;
+ int i;
+
+ if (spriv->wdog_ctrl == 0) {
+ dev_warn(dev->class_dev, "watchdog is disabled\n");
+ return -EINVAL;
+ }
+
+ /* "ping" the watchdog */
+ for (i = 0; i < insn->n; i++) {
+ outl(spriv->wdog_ctrl | ADDI_TCW_CTRL_TRIG,
+ spriv->iobase + ADDI_TCW_CTRL_REG);
+ }
+
+ return insn->n;
+}
+
+void addi_watchdog_reset(unsigned long iobase)
+{
+ outl(0x0, iobase + ADDI_TCW_CTRL_REG);
+ outl(0x0, iobase + ADDI_TCW_RELOAD_REG);
+}
+EXPORT_SYMBOL_GPL(addi_watchdog_reset);
+
+int addi_watchdog_init(struct comedi_subdevice *s, unsigned long iobase)
+{
+ struct addi_watchdog_private *spriv;
+
+ spriv = comedi_alloc_spriv(s, sizeof(*spriv));
+ if (!spriv)
+ return -ENOMEM;
+
+ spriv->iobase = iobase;
+
+ s->type = COMEDI_SUBD_TIMER;
+ s->subdev_flags = SDF_WRITABLE;
+ s->n_chan = 1;
+ s->maxdata = 0xff;
+ s->insn_config = addi_watchdog_insn_config;
+ s->insn_read = addi_watchdog_insn_read;
+ s->insn_write = addi_watchdog_insn_write;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(addi_watchdog_init);
+
+static int __init addi_watchdog_module_init(void)
+{
+ return 0;
+}
+module_init(addi_watchdog_module_init);
+
+static void __exit addi_watchdog_module_exit(void)
+{
+}
+module_exit(addi_watchdog_module_exit);
+
+MODULE_DESCRIPTION("ADDI-DATA Watchdog subdevice");
+MODULE_AUTHOR("H Hartley Sweeten <hsweeten@visionengravers.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/comedi/drivers/addi_watchdog.h b/drivers/staging/comedi/drivers/addi_watchdog.h
new file mode 100644
index 000000000..83b47befa
--- /dev/null
+++ b/drivers/staging/comedi/drivers/addi_watchdog.h
@@ -0,0 +1,9 @@
+#ifndef _ADDI_WATCHDOG_H
+#define _ADDI_WATCHDOG_H
+
+#include "../comedidev.h"
+
+void addi_watchdog_reset(unsigned long iobase);
+int addi_watchdog_init(struct comedi_subdevice *, unsigned long iobase);
+
+#endif
diff --git a/drivers/staging/comedi/drivers/adl_pci6208.c b/drivers/staging/comedi/drivers/adl_pci6208.c
new file mode 100644
index 000000000..7ed3fd6fb
--- /dev/null
+++ b/drivers/staging/comedi/drivers/adl_pci6208.c
@@ -0,0 +1,211 @@
+/*
+ * adl_pci6208.c
+ * Comedi driver for ADLink 6208 series cards
+ *
+ * COMEDI - Linux Control and Measurement Device Interface
+ * Copyright (C) 2000 David A. Schleef <ds@schleef.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+/*
+ * Driver: adl_pci6208
+ * Description: ADLink PCI-6208/6216 Series Multi-channel Analog Output Cards
+ * Devices: [ADLink] PCI-6208 (adl_pci6208), PCI-6216
+ * Author: nsyeow <nsyeow@pd.jaring.my>
+ * Updated: Wed, 11 Feb 2015 11:37:18 +0000
+ * Status: untested
+ *
+ * Configuration Options: not applicable, uses PCI auto config
+ *
+ * All supported devices share the same PCI device ID and are treated as a
+ * PCI-6216 with 16 analog output channels. On a PCI-6208, the upper 8
+ * channels exist in registers, but don't go to DAC chips.
+ */
+
+#include <linux/module.h>
+#include <linux/delay.h>
+
+#include "../comedi_pci.h"
+
+/*
+ * PCI-6208/6216-GL register map
+ */
+#define PCI6208_AO_CONTROL(x) (0x00 + (2 * (x)))
+#define PCI6208_AO_STATUS 0x00
+#define PCI6208_AO_STATUS_DATA_SEND (1 << 0)
+#define PCI6208_DIO 0x40
+#define PCI6208_DIO_DO_MASK (0x0f)
+#define PCI6208_DIO_DO_SHIFT (0)
+#define PCI6208_DIO_DI_MASK (0xf0)
+#define PCI6208_DIO_DI_SHIFT (4)
+
+static int pci6208_ao_eoc(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned long context)
+{
+ unsigned int status;
+
+ status = inw(dev->iobase + PCI6208_AO_STATUS);
+ if ((status & PCI6208_AO_STATUS_DATA_SEND) == 0)
+ return 0;
+ return -EBUSY;
+}
+
+static int pci6208_ao_insn_write(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ unsigned int chan = CR_CHAN(insn->chanspec);
+ unsigned int val = s->readback[chan];
+ int ret;
+ int i;
+
+ for (i = 0; i < insn->n; i++) {
+ val = data[i];
+
+ /* D/A transfer rate is 2.2us */
+ ret = comedi_timeout(dev, s, insn, pci6208_ao_eoc, 0);
+ if (ret)
+ return ret;
+
+ /* the hardware expects two's complement values */
+ outw(comedi_offset_munge(s, val),
+ dev->iobase + PCI6208_AO_CONTROL(chan));
+
+ s->readback[chan] = val;
+ }
+
+ return insn->n;
+}
+
+static int pci6208_di_insn_bits(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ unsigned int val;
+
+ val = inw(dev->iobase + PCI6208_DIO);
+ val = (val & PCI6208_DIO_DI_MASK) >> PCI6208_DIO_DI_SHIFT;
+
+ data[1] = val;
+
+ return insn->n;
+}
+
+static int pci6208_do_insn_bits(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ if (comedi_dio_update_state(s, data))
+ outw(s->state, dev->iobase + PCI6208_DIO);
+
+ data[1] = s->state;
+
+ return insn->n;
+}
+
+static int pci6208_auto_attach(struct comedi_device *dev,
+ unsigned long context_unused)
+{
+ struct pci_dev *pcidev = comedi_to_pci_dev(dev);
+ struct comedi_subdevice *s;
+ unsigned int val;
+ int ret;
+
+ ret = comedi_pci_enable(dev);
+ if (ret)
+ return ret;
+ dev->iobase = pci_resource_start(pcidev, 2);
+
+ ret = comedi_alloc_subdevices(dev, 3);
+ if (ret)
+ return ret;
+
+ s = &dev->subdevices[0];
+ /* analog output subdevice */
+ s->type = COMEDI_SUBD_AO;
+ s->subdev_flags = SDF_WRITABLE;
+ s->n_chan = 16; /* Only 8 usable on PCI-6208 */
+ s->maxdata = 0xffff;
+ s->range_table = &range_bipolar10;
+ s->insn_write = pci6208_ao_insn_write;
+
+ ret = comedi_alloc_subdev_readback(s);
+ if (ret)
+ return ret;
+
+ s = &dev->subdevices[1];
+ /* digital input subdevice */
+ s->type = COMEDI_SUBD_DI;
+ s->subdev_flags = SDF_READABLE;
+ s->n_chan = 4;
+ s->maxdata = 1;
+ s->range_table = &range_digital;
+ s->insn_bits = pci6208_di_insn_bits;
+
+ s = &dev->subdevices[2];
+ /* digital output subdevice */
+ s->type = COMEDI_SUBD_DO;
+ s->subdev_flags = SDF_WRITABLE;
+ s->n_chan = 4;
+ s->maxdata = 1;
+ s->range_table = &range_digital;
+ s->insn_bits = pci6208_do_insn_bits;
+
+ /*
+ * Get the read back signals from the digital outputs
+ * and save it as the initial state for the subdevice.
+ */
+ val = inw(dev->iobase + PCI6208_DIO);
+ val = (val & PCI6208_DIO_DO_MASK) >> PCI6208_DIO_DO_SHIFT;
+ s->state = val;
+
+ return 0;
+}
+
+static struct comedi_driver adl_pci6208_driver = {
+ .driver_name = "adl_pci6208",
+ .module = THIS_MODULE,
+ .auto_attach = pci6208_auto_attach,
+ .detach = comedi_pci_detach,
+};
+
+static int adl_pci6208_pci_probe(struct pci_dev *dev,
+ const struct pci_device_id *id)
+{
+ return comedi_pci_auto_config(dev, &adl_pci6208_driver,
+ id->driver_data);
+}
+
+static const struct pci_device_id adl_pci6208_pci_table[] = {
+ { PCI_DEVICE(PCI_VENDOR_ID_ADLINK, 0x6208) },
+ { PCI_DEVICE_SUB(PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050,
+ 0x9999, 0x6208) },
+ { 0 }
+};
+MODULE_DEVICE_TABLE(pci, adl_pci6208_pci_table);
+
+static struct pci_driver adl_pci6208_pci_driver = {
+ .name = "adl_pci6208",
+ .id_table = adl_pci6208_pci_table,
+ .probe = adl_pci6208_pci_probe,
+ .remove = comedi_pci_auto_unconfig,
+};
+module_comedi_pci_driver(adl_pci6208_driver, adl_pci6208_pci_driver);
+
+MODULE_AUTHOR("Comedi http://www.comedi.org");
+MODULE_DESCRIPTION("Comedi driver for ADLink 6208 series cards");
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/comedi/drivers/adl_pci7x3x.c b/drivers/staging/comedi/drivers/adl_pci7x3x.c
new file mode 100644
index 000000000..934af3ff7
--- /dev/null
+++ b/drivers/staging/comedi/drivers/adl_pci7x3x.c
@@ -0,0 +1,275 @@
+/*
+ * COMEDI driver for the ADLINK PCI-723x/743x series boards.
+ * Copyright (C) 2012 H Hartley Sweeten <hsweeten@visionengravers.com>
+ *
+ * Based on the adl_pci7230 driver written by:
+ * David Fernandez <dfcastelao@gmail.com>
+ * and the adl_pci7432 driver written by:
+ * Michel Lachaine <mike@mikelachaine.ca>
+ *
+ * COMEDI - Linux Control and Measurement Device Interface
+ * Copyright (C) 2000 David A. Schleef <ds@schleef.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+/*
+ * Driver: adl_pci7x3x
+ * Description: 32/64-Channel Isolated Digital I/O Boards
+ * Devices: [ADLink] PCI-7230 (adl_pci7230), PCI-7233 (adl_pci7233),
+ * PCI-7234 (adl_pci7234), PCI-7432 (adl_pci7432), PCI-7433 (adl_pci7433),
+ * PCI-7434 (adl_pci7434)
+ * Author: H Hartley Sweeten <hsweeten@visionengravers.com>
+ * Updated: Thu, 02 Aug 2012 14:27:46 -0700
+ * Status: untested
+ *
+ * One or two subdevices are setup by this driver depending on
+ * the number of digital inputs and/or outputs provided by the
+ * board. Each subdevice has a maximum of 32 channels.
+ *
+ * PCI-7230 - 2 subdevices: 0 - 16 input, 1 - 16 output
+ * PCI-7233 - 1 subdevice: 0 - 32 input
+ * PCI-7234 - 1 subdevice: 0 - 32 output
+ * PCI-7432 - 2 subdevices: 0 - 32 input, 1 - 32 output
+ * PCI-7433 - 2 subdevices: 0 - 32 input, 1 - 32 input
+ * PCI-7434 - 2 subdevices: 0 - 32 output, 1 - 32 output
+ *
+ * The PCI-7230, PCI-7432 and PCI-7433 boards also support external
+ * interrupt signals on digital input channels 0 and 1. The PCI-7233
+ * has dual-interrupt sources for change-of-state (COS) on any 16
+ * digital input channels of LSB and for COS on any 16 digital input
+ * lines of MSB. Interrupts are not currently supported by this
+ * driver.
+ *
+ * Configuration Options: not applicable, uses comedi PCI auto config
+ */
+
+#include <linux/module.h>
+
+#include "../comedi_pci.h"
+
+/*
+ * Register I/O map (32-bit access only)
+ */
+#define PCI7X3X_DIO_REG 0x00
+#define PCI743X_DIO_REG 0x04
+
+enum apci1516_boardid {
+ BOARD_PCI7230,
+ BOARD_PCI7233,
+ BOARD_PCI7234,
+ BOARD_PCI7432,
+ BOARD_PCI7433,
+ BOARD_PCI7434,
+};
+
+struct adl_pci7x3x_boardinfo {
+ const char *name;
+ int nsubdevs;
+ int di_nchan;
+ int do_nchan;
+};
+
+static const struct adl_pci7x3x_boardinfo adl_pci7x3x_boards[] = {
+ [BOARD_PCI7230] = {
+ .name = "adl_pci7230",
+ .nsubdevs = 2,
+ .di_nchan = 16,
+ .do_nchan = 16,
+ },
+ [BOARD_PCI7233] = {
+ .name = "adl_pci7233",
+ .nsubdevs = 1,
+ .di_nchan = 32,
+ },
+ [BOARD_PCI7234] = {
+ .name = "adl_pci7234",
+ .nsubdevs = 1,
+ .do_nchan = 32,
+ },
+ [BOARD_PCI7432] = {
+ .name = "adl_pci7432",
+ .nsubdevs = 2,
+ .di_nchan = 32,
+ .do_nchan = 32,
+ },
+ [BOARD_PCI7433] = {
+ .name = "adl_pci7433",
+ .nsubdevs = 2,
+ .di_nchan = 64,
+ },
+ [BOARD_PCI7434] = {
+ .name = "adl_pci7434",
+ .nsubdevs = 2,
+ .do_nchan = 64,
+ }
+};
+
+static int adl_pci7x3x_do_insn_bits(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ unsigned long reg = (unsigned long)s->private;
+
+ if (comedi_dio_update_state(s, data))
+ outl(s->state, dev->iobase + reg);
+
+ data[1] = s->state;
+
+ return insn->n;
+}
+
+static int adl_pci7x3x_di_insn_bits(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ unsigned long reg = (unsigned long)s->private;
+
+ data[1] = inl(dev->iobase + reg);
+
+ return insn->n;
+}
+
+static int adl_pci7x3x_auto_attach(struct comedi_device *dev,
+ unsigned long context)
+{
+ struct pci_dev *pcidev = comedi_to_pci_dev(dev);
+ const struct adl_pci7x3x_boardinfo *board = NULL;
+ struct comedi_subdevice *s;
+ int subdev;
+ int nchan;
+ int ret;
+
+ if (context < ARRAY_SIZE(adl_pci7x3x_boards))
+ board = &adl_pci7x3x_boards[context];
+ if (!board)
+ return -ENODEV;
+ dev->board_ptr = board;
+ dev->board_name = board->name;
+
+ ret = comedi_pci_enable(dev);
+ if (ret)
+ return ret;
+ dev->iobase = pci_resource_start(pcidev, 2);
+
+ ret = comedi_alloc_subdevices(dev, board->nsubdevs);
+ if (ret)
+ return ret;
+
+ subdev = 0;
+
+ if (board->di_nchan) {
+ nchan = min(board->di_nchan, 32);
+
+ s = &dev->subdevices[subdev];
+ /* Isolated digital inputs 0 to 15/31 */
+ s->type = COMEDI_SUBD_DI;
+ s->subdev_flags = SDF_READABLE;
+ s->n_chan = nchan;
+ s->maxdata = 1;
+ s->insn_bits = adl_pci7x3x_di_insn_bits;
+ s->range_table = &range_digital;
+
+ s->private = (void *)PCI7X3X_DIO_REG;
+
+ subdev++;
+
+ nchan = board->di_nchan - nchan;
+ if (nchan) {
+ s = &dev->subdevices[subdev];
+ /* Isolated digital inputs 32 to 63 */
+ s->type = COMEDI_SUBD_DI;
+ s->subdev_flags = SDF_READABLE;
+ s->n_chan = nchan;
+ s->maxdata = 1;
+ s->insn_bits = adl_pci7x3x_di_insn_bits;
+ s->range_table = &range_digital;
+
+ s->private = (void *)PCI743X_DIO_REG;
+
+ subdev++;
+ }
+ }
+
+ if (board->do_nchan) {
+ nchan = min(board->do_nchan, 32);
+
+ s = &dev->subdevices[subdev];
+ /* Isolated digital outputs 0 to 15/31 */
+ s->type = COMEDI_SUBD_DO;
+ s->subdev_flags = SDF_WRITABLE;
+ s->n_chan = nchan;
+ s->maxdata = 1;
+ s->insn_bits = adl_pci7x3x_do_insn_bits;
+ s->range_table = &range_digital;
+
+ s->private = (void *)PCI7X3X_DIO_REG;
+
+ subdev++;
+
+ nchan = board->do_nchan - nchan;
+ if (nchan) {
+ s = &dev->subdevices[subdev];
+ /* Isolated digital outputs 32 to 63 */
+ s->type = COMEDI_SUBD_DO;
+ s->subdev_flags = SDF_WRITABLE;
+ s->n_chan = nchan;
+ s->maxdata = 1;
+ s->insn_bits = adl_pci7x3x_do_insn_bits;
+ s->range_table = &range_digital;
+
+ s->private = (void *)PCI743X_DIO_REG;
+
+ subdev++;
+ }
+ }
+
+ return 0;
+}
+
+static struct comedi_driver adl_pci7x3x_driver = {
+ .driver_name = "adl_pci7x3x",
+ .module = THIS_MODULE,
+ .auto_attach = adl_pci7x3x_auto_attach,
+ .detach = comedi_pci_detach,
+};
+
+static int adl_pci7x3x_pci_probe(struct pci_dev *dev,
+ const struct pci_device_id *id)
+{
+ return comedi_pci_auto_config(dev, &adl_pci7x3x_driver,
+ id->driver_data);
+}
+
+static const struct pci_device_id adl_pci7x3x_pci_table[] = {
+ { PCI_VDEVICE(ADLINK, 0x7230), BOARD_PCI7230 },
+ { PCI_VDEVICE(ADLINK, 0x7233), BOARD_PCI7233 },
+ { PCI_VDEVICE(ADLINK, 0x7234), BOARD_PCI7234 },
+ { PCI_VDEVICE(ADLINK, 0x7432), BOARD_PCI7432 },
+ { PCI_VDEVICE(ADLINK, 0x7433), BOARD_PCI7433 },
+ { PCI_VDEVICE(ADLINK, 0x7434), BOARD_PCI7434 },
+ { 0 }
+};
+MODULE_DEVICE_TABLE(pci, adl_pci7x3x_pci_table);
+
+static struct pci_driver adl_pci7x3x_pci_driver = {
+ .name = "adl_pci7x3x",
+ .id_table = adl_pci7x3x_pci_table,
+ .probe = adl_pci7x3x_pci_probe,
+ .remove = comedi_pci_auto_unconfig,
+};
+module_comedi_pci_driver(adl_pci7x3x_driver, adl_pci7x3x_pci_driver);
+
+MODULE_DESCRIPTION("ADLINK PCI-723x/743x Isolated Digital I/O boards");
+MODULE_AUTHOR("H Hartley Sweeten <hsweeten@visionengravers.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/comedi/drivers/adl_pci8164.c b/drivers/staging/comedi/drivers/adl_pci8164.c
new file mode 100644
index 000000000..da901c8de
--- /dev/null
+++ b/drivers/staging/comedi/drivers/adl_pci8164.c
@@ -0,0 +1,163 @@
+/*
+ * comedi/drivers/adl_pci8164.c
+ *
+ * Hardware comedi driver for PCI-8164 Adlink card
+ * Copyright (C) 2004 Michel Lachine <mike@mikelachaine.ca>
+ *
+ * This program is free software; you can redistribute it and/or 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.
+ */
+
+/*
+ * Driver: adl_pci8164
+ * Description: Driver for the Adlink PCI-8164 4 Axes Motion Control board
+ * Devices: [ADLink] PCI-8164 (adl_pci8164)
+ * Author: Michel Lachaine <mike@mikelachaine.ca>
+ * Status: experimental
+ * Updated: Mon, 14 Apr 2008 15:10:32 +0100
+ *
+ * Configuration Options: not applicable, uses PCI auto config
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+
+#include "../comedi_pci.h"
+
+#define PCI8164_AXIS(x) ((x) * 0x08)
+#define PCI8164_CMD_MSTS_REG 0x00
+#define PCI8164_OTP_SSTS_REG 0x02
+#define PCI8164_BUF0_REG 0x04
+#define PCI8164_BUF1_REG 0x06
+
+static int adl_pci8164_insn_read(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ unsigned long offset = (unsigned long)s->private;
+ unsigned int chan = CR_CHAN(insn->chanspec);
+ int i;
+
+ for (i = 0; i < insn->n; i++)
+ data[i] = inw(dev->iobase + PCI8164_AXIS(chan) + offset);
+
+ return insn->n;
+}
+
+static int adl_pci8164_insn_write(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ unsigned long offset = (unsigned long)s->private;
+ unsigned int chan = CR_CHAN(insn->chanspec);
+ int i;
+
+ for (i = 0; i < insn->n; i++)
+ outw(data[i], dev->iobase + PCI8164_AXIS(chan) + offset);
+
+ return insn->n;
+}
+
+static int adl_pci8164_auto_attach(struct comedi_device *dev,
+ unsigned long context_unused)
+{
+ struct pci_dev *pcidev = comedi_to_pci_dev(dev);
+ struct comedi_subdevice *s;
+ int ret;
+
+ ret = comedi_pci_enable(dev);
+ if (ret)
+ return ret;
+ dev->iobase = pci_resource_start(pcidev, 2);
+
+ ret = comedi_alloc_subdevices(dev, 4);
+ if (ret)
+ return ret;
+
+ /* read MSTS register / write CMD register for each axis (channel) */
+ s = &dev->subdevices[0];
+ s->type = COMEDI_SUBD_PROC;
+ s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
+ s->n_chan = 4;
+ s->maxdata = 0xffff;
+ s->len_chanlist = 4;
+ s->insn_read = adl_pci8164_insn_read;
+ s->insn_write = adl_pci8164_insn_write;
+ s->private = (void *)PCI8164_CMD_MSTS_REG;
+
+ /* read SSTS register / write OTP register for each axis (channel) */
+ s = &dev->subdevices[1];
+ s->type = COMEDI_SUBD_PROC;
+ s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
+ s->n_chan = 4;
+ s->maxdata = 0xffff;
+ s->len_chanlist = 4;
+ s->insn_read = adl_pci8164_insn_read;
+ s->insn_write = adl_pci8164_insn_write;
+ s->private = (void *)PCI8164_OTP_SSTS_REG;
+
+ /* read/write BUF0 register for each axis (channel) */
+ s = &dev->subdevices[2];
+ s->type = COMEDI_SUBD_PROC;
+ s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
+ s->n_chan = 4;
+ s->maxdata = 0xffff;
+ s->len_chanlist = 4;
+ s->insn_read = adl_pci8164_insn_read;
+ s->insn_write = adl_pci8164_insn_write;
+ s->private = (void *)PCI8164_BUF0_REG;
+
+ /* read/write BUF1 register for each axis (channel) */
+ s = &dev->subdevices[3];
+ s->type = COMEDI_SUBD_PROC;
+ s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
+ s->n_chan = 4;
+ s->maxdata = 0xffff;
+ s->len_chanlist = 4;
+ s->insn_read = adl_pci8164_insn_read;
+ s->insn_write = adl_pci8164_insn_write;
+ s->private = (void *)PCI8164_BUF1_REG;
+
+ return 0;
+}
+
+static struct comedi_driver adl_pci8164_driver = {
+ .driver_name = "adl_pci8164",
+ .module = THIS_MODULE,
+ .auto_attach = adl_pci8164_auto_attach,
+ .detach = comedi_pci_detach,
+};
+
+static int adl_pci8164_pci_probe(struct pci_dev *dev,
+ const struct pci_device_id *id)
+{
+ return comedi_pci_auto_config(dev, &adl_pci8164_driver,
+ id->driver_data);
+}
+
+static const struct pci_device_id adl_pci8164_pci_table[] = {
+ { PCI_DEVICE(PCI_VENDOR_ID_ADLINK, 0x8164) },
+ { 0 }
+};
+MODULE_DEVICE_TABLE(pci, adl_pci8164_pci_table);
+
+static struct pci_driver adl_pci8164_pci_driver = {
+ .name = "adl_pci8164",
+ .id_table = adl_pci8164_pci_table,
+ .probe = adl_pci8164_pci_probe,
+ .remove = comedi_pci_auto_unconfig,
+};
+module_comedi_pci_driver(adl_pci8164_driver, adl_pci8164_pci_driver);
+
+MODULE_AUTHOR("Comedi http://www.comedi.org");
+MODULE_DESCRIPTION("Comedi low-level driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/comedi/drivers/adl_pci9111.c b/drivers/staging/comedi/drivers/adl_pci9111.c
new file mode 100644
index 000000000..c9df3afe9
--- /dev/null
+++ b/drivers/staging/comedi/drivers/adl_pci9111.c
@@ -0,0 +1,775 @@
+/*
+
+comedi/drivers/adl_pci9111.c
+
+Hardware driver for PCI9111 ADLink cards:
+
+PCI-9111HR
+
+Copyright (C) 2002-2005 Emmanuel Pacaud <emmanuel.pacaud@univ-poitiers.fr>
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, 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.
+*/
+
+/*
+Driver: adl_pci9111
+Description: Adlink PCI-9111HR
+Author: Emmanuel Pacaud <emmanuel.pacaud@univ-poitiers.fr>
+Devices: [ADLink] PCI-9111HR (adl_pci9111)
+Status: experimental
+
+Supports:
+
+ - ai_insn read
+ - ao_insn read/write
+ - di_insn read
+ - do_insn read/write
+ - ai_do_cmd mode with the following sources:
+
+ - start_src TRIG_NOW
+ - scan_begin_src TRIG_FOLLOW TRIG_TIMER TRIG_EXT
+ - convert_src TRIG_TIMER TRIG_EXT
+ - scan_end_src TRIG_COUNT
+ - stop_src TRIG_COUNT TRIG_NONE
+
+The scanned channels must be consecutive and start from 0. They must
+all have the same range and aref.
+
+Configuration options: not applicable, uses PCI auto config
+*/
+
+/*
+CHANGELOG:
+
+2005/02/17 Extend AI streaming capabilities. Now, scan_begin_arg can be
+a multiple of chanlist_len*convert_arg.
+2002/02/19 Fixed the two's complement conversion in pci9111_(hr_)ai_get_data.
+2002/02/18 Added external trigger support for analog input.
+
+TODO:
+
+ - Really test implemented functionality.
+ - Add support for the PCI-9111DG with a probe routine to identify
+ the card type (perhaps with the help of the channel number readback
+ of the A/D Data register).
+ - Add external multiplexer support.
+
+*/
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+
+#include "../comedi_pci.h"
+
+#include "plx9052.h"
+#include "comedi_8254.h"
+
+#define PCI9111_FIFO_HALF_SIZE 512
+
+#define PCI9111_AI_ACQUISITION_PERIOD_MIN_NS 10000
+
+#define PCI9111_RANGE_SETTING_DELAY 10
+#define PCI9111_AI_INSTANT_READ_UDELAY_US 2
+
+/*
+ * IO address map and bit defines
+ */
+#define PCI9111_AI_FIFO_REG 0x00
+#define PCI9111_AO_REG 0x00
+#define PCI9111_DIO_REG 0x02
+#define PCI9111_EDIO_REG 0x04
+#define PCI9111_AI_CHANNEL_REG 0x06
+#define PCI9111_AI_RANGE_STAT_REG 0x08
+#define PCI9111_AI_STAT_AD_BUSY (1 << 7)
+#define PCI9111_AI_STAT_FF_FF (1 << 6)
+#define PCI9111_AI_STAT_FF_HF (1 << 5)
+#define PCI9111_AI_STAT_FF_EF (1 << 4)
+#define PCI9111_AI_RANGE_MASK (7 << 0)
+#define PCI9111_AI_TRIG_CTRL_REG 0x0a
+#define PCI9111_AI_TRIG_CTRL_TRGEVENT (1 << 5)
+#define PCI9111_AI_TRIG_CTRL_POTRG (1 << 4)
+#define PCI9111_AI_TRIG_CTRL_PTRG (1 << 3)
+#define PCI9111_AI_TRIG_CTRL_ETIS (1 << 2)
+#define PCI9111_AI_TRIG_CTRL_TPST (1 << 1)
+#define PCI9111_AI_TRIG_CTRL_ASCAN (1 << 0)
+#define PCI9111_INT_CTRL_REG 0x0c
+#define PCI9111_INT_CTRL_ISC2 (1 << 3)
+#define PCI9111_INT_CTRL_FFEN (1 << 2)
+#define PCI9111_INT_CTRL_ISC1 (1 << 1)
+#define PCI9111_INT_CTRL_ISC0 (1 << 0)
+#define PCI9111_SOFT_TRIG_REG 0x0e
+#define PCI9111_8254_BASE_REG 0x40
+#define PCI9111_INT_CLR_REG 0x48
+
+/* PLX 9052 Local Interrupt 1 enabled and active */
+#define PCI9111_LI1_ACTIVE (PLX9052_INTCSR_LI1ENAB | \
+ PLX9052_INTCSR_LI1STAT)
+
+/* PLX 9052 Local Interrupt 2 enabled and active */
+#define PCI9111_LI2_ACTIVE (PLX9052_INTCSR_LI2ENAB | \
+ PLX9052_INTCSR_LI2STAT)
+
+static const struct comedi_lrange pci9111_ai_range = {
+ 5, {
+ BIP_RANGE(10),
+ BIP_RANGE(5),
+ BIP_RANGE(2.5),
+ BIP_RANGE(1.25),
+ BIP_RANGE(0.625)
+ }
+};
+
+struct pci9111_private_data {
+ unsigned long lcr_io_base;
+
+ unsigned int scan_delay;
+ unsigned int chunk_counter;
+ unsigned int chunk_num_samples;
+
+ unsigned short ai_bounce_buffer[2 * PCI9111_FIFO_HALF_SIZE];
+};
+
+static void plx9050_interrupt_control(unsigned long io_base,
+ bool LINTi1_enable,
+ bool LINTi1_active_high,
+ bool LINTi2_enable,
+ bool LINTi2_active_high,
+ bool interrupt_enable)
+{
+ int flags = 0;
+
+ if (LINTi1_enable)
+ flags |= PLX9052_INTCSR_LI1ENAB;
+ if (LINTi1_active_high)
+ flags |= PLX9052_INTCSR_LI1POL;
+ if (LINTi2_enable)
+ flags |= PLX9052_INTCSR_LI2ENAB;
+ if (LINTi2_active_high)
+ flags |= PLX9052_INTCSR_LI2POL;
+
+ if (interrupt_enable)
+ flags |= PLX9052_INTCSR_PCIENAB;
+
+ outb(flags, io_base + PLX9052_INTCSR);
+}
+
+enum pci9111_ISC0_sources {
+ irq_on_eoc,
+ irq_on_fifo_half_full
+};
+
+enum pci9111_ISC1_sources {
+ irq_on_timer_tick,
+ irq_on_external_trigger
+};
+
+static void pci9111_interrupt_source_set(struct comedi_device *dev,
+ enum pci9111_ISC0_sources irq_0_source,
+ enum pci9111_ISC1_sources irq_1_source)
+{
+ int flags;
+
+ /* Read the current interrupt control bits */
+ flags = inb(dev->iobase + PCI9111_AI_TRIG_CTRL_REG);
+ /* Shift the bits so they are compatible with the write register */
+ flags >>= 4;
+ /* Mask off the ISCx bits */
+ flags &= 0xc0;
+
+ /* Now set the new ISCx bits */
+ if (irq_0_source == irq_on_fifo_half_full)
+ flags |= PCI9111_INT_CTRL_ISC0;
+
+ if (irq_1_source == irq_on_external_trigger)
+ flags |= PCI9111_INT_CTRL_ISC1;
+
+ outb(flags, dev->iobase + PCI9111_INT_CTRL_REG);
+}
+
+static void pci9111_fifo_reset(struct comedi_device *dev)
+{
+ unsigned long int_ctrl_reg = dev->iobase + PCI9111_INT_CTRL_REG;
+
+ /* To reset the FIFO, set FFEN sequence as 0 -> 1 -> 0 */
+ outb(0, int_ctrl_reg);
+ outb(PCI9111_INT_CTRL_FFEN, int_ctrl_reg);
+ outb(0, int_ctrl_reg);
+}
+
+static int pci9111_ai_cancel(struct comedi_device *dev,
+ struct comedi_subdevice *s)
+{
+ struct pci9111_private_data *dev_private = dev->private;
+
+ /* Disable interrupts */
+ plx9050_interrupt_control(dev_private->lcr_io_base, true, true, true,
+ true, false);
+
+ /* disable A/D triggers (software trigger mode) and auto scan off */
+ outb(0, dev->iobase + PCI9111_AI_TRIG_CTRL_REG);
+
+ pci9111_fifo_reset(dev);
+
+ return 0;
+}
+
+static int pci9111_ai_check_chanlist(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_cmd *cmd)
+{
+ unsigned int range0 = CR_RANGE(cmd->chanlist[0]);
+ unsigned int aref0 = CR_AREF(cmd->chanlist[0]);
+ int i;
+
+ for (i = 1; i < cmd->chanlist_len; i++) {
+ unsigned int chan = CR_CHAN(cmd->chanlist[i]);
+ unsigned int range = CR_RANGE(cmd->chanlist[i]);
+ unsigned int aref = CR_AREF(cmd->chanlist[i]);
+
+ if (chan != i) {
+ dev_dbg(dev->class_dev,
+ "entries in chanlist must be consecutive channels,counting upwards from 0\n");
+ return -EINVAL;
+ }
+
+ if (range != range0) {
+ dev_dbg(dev->class_dev,
+ "entries in chanlist must all have the same gain\n");
+ return -EINVAL;
+ }
+
+ if (aref != aref0) {
+ dev_dbg(dev->class_dev,
+ "entries in chanlist must all have the same reference\n");
+ return -EINVAL;
+ }
+ }
+
+ return 0;
+}
+
+static int pci9111_ai_do_cmd_test(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_cmd *cmd)
+{
+ int err = 0;
+ unsigned int arg;
+
+ /* Step 1 : check if triggers are trivially valid */
+
+ err |= comedi_check_trigger_src(&cmd->start_src, TRIG_NOW);
+ err |= comedi_check_trigger_src(&cmd->scan_begin_src,
+ TRIG_TIMER | TRIG_FOLLOW | TRIG_EXT);
+ err |= comedi_check_trigger_src(&cmd->convert_src,
+ TRIG_TIMER | TRIG_EXT);
+ err |= comedi_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
+ err |= comedi_check_trigger_src(&cmd->stop_src,
+ TRIG_COUNT | TRIG_NONE);
+
+ if (err)
+ return 1;
+
+ /* Step 2a : make sure trigger sources are unique */
+
+ err |= comedi_check_trigger_is_unique(cmd->scan_begin_src);
+ err |= comedi_check_trigger_is_unique(cmd->convert_src);
+ err |= comedi_check_trigger_is_unique(cmd->stop_src);
+
+ /* Step 2b : and mutually compatible */
+
+ if (cmd->scan_begin_src != TRIG_FOLLOW) {
+ if (cmd->scan_begin_src != cmd->convert_src)
+ err |= -EINVAL;
+ }
+
+ if (err)
+ return 2;
+
+ /* Step 3: check if arguments are trivially valid */
+
+ err |= comedi_check_trigger_arg_is(&cmd->start_arg, 0);
+
+ if (cmd->convert_src == TRIG_TIMER) {
+ err |= comedi_check_trigger_arg_min(&cmd->convert_arg,
+ PCI9111_AI_ACQUISITION_PERIOD_MIN_NS);
+ } else { /* TRIG_EXT */
+ err |= comedi_check_trigger_arg_is(&cmd->convert_arg, 0);
+ }
+
+ if (cmd->scan_begin_src == TRIG_TIMER) {
+ err |= comedi_check_trigger_arg_min(&cmd->scan_begin_arg,
+ PCI9111_AI_ACQUISITION_PERIOD_MIN_NS);
+ } else { /* TRIG_FOLLOW || TRIG_EXT */
+ err |= comedi_check_trigger_arg_is(&cmd->scan_begin_arg, 0);
+ }
+
+ err |= comedi_check_trigger_arg_is(&cmd->scan_end_arg,
+ cmd->chanlist_len);
+
+ if (cmd->stop_src == TRIG_COUNT)
+ err |= comedi_check_trigger_arg_min(&cmd->stop_arg, 1);
+ else /* TRIG_NONE */
+ err |= comedi_check_trigger_arg_is(&cmd->stop_arg, 0);
+
+ if (err)
+ return 3;
+
+ /* Step 4: fix up any arguments */
+
+ if (cmd->convert_src == TRIG_TIMER) {
+ arg = cmd->convert_arg;
+ comedi_8254_cascade_ns_to_timer(dev->pacer, &arg, cmd->flags);
+ err |= comedi_check_trigger_arg_is(&cmd->convert_arg, arg);
+ }
+
+ /*
+ * There's only one timer on this card, so the scan_begin timer
+ * must be a multiple of chanlist_len*convert_arg
+ */
+ if (cmd->scan_begin_src == TRIG_TIMER) {
+ arg = cmd->chanlist_len * cmd->convert_arg;
+
+ if (arg < cmd->scan_begin_arg)
+ arg *= (cmd->scan_begin_arg / arg);
+
+ err |= comedi_check_trigger_arg_is(&cmd->scan_begin_arg, arg);
+ }
+
+ if (err)
+ return 4;
+
+ /* Step 5: check channel list if it exists */
+ if (cmd->chanlist && cmd->chanlist_len > 0)
+ err |= pci9111_ai_check_chanlist(dev, s, cmd);
+
+ if (err)
+ return 5;
+
+ return 0;
+}
+
+static int pci9111_ai_do_cmd(struct comedi_device *dev,
+ struct comedi_subdevice *s)
+{
+ struct pci9111_private_data *dev_private = dev->private;
+ struct comedi_cmd *cmd = &s->async->cmd;
+ unsigned int last_chan = CR_CHAN(cmd->chanlist[cmd->chanlist_len - 1]);
+ unsigned int trig = 0;
+
+ /* Set channel scan limit */
+ /* PCI9111 allows only scanning from channel 0 to channel n */
+ /* TODO: handle the case of an external multiplexer */
+
+ if (cmd->chanlist_len > 1)
+ trig |= PCI9111_AI_TRIG_CTRL_ASCAN;
+
+ outb(last_chan, dev->iobase + PCI9111_AI_CHANNEL_REG);
+
+ /* Set gain */
+ /* This is the same gain on every channel */
+
+ outb(CR_RANGE(cmd->chanlist[0]) & PCI9111_AI_RANGE_MASK,
+ dev->iobase + PCI9111_AI_RANGE_STAT_REG);
+
+ /* Set timer pacer */
+ dev_private->scan_delay = 0;
+ if (cmd->convert_src == TRIG_TIMER) {
+ trig |= PCI9111_AI_TRIG_CTRL_TPST;
+ comedi_8254_update_divisors(dev->pacer);
+ comedi_8254_pacer_enable(dev->pacer, 1, 2, true);
+ pci9111_fifo_reset(dev);
+ pci9111_interrupt_source_set(dev, irq_on_fifo_half_full,
+ irq_on_timer_tick);
+ plx9050_interrupt_control(dev_private->lcr_io_base, true, true,
+ false, true, true);
+
+ if (cmd->scan_begin_src == TRIG_TIMER) {
+ dev_private->scan_delay = (cmd->scan_begin_arg /
+ (cmd->convert_arg * cmd->chanlist_len)) - 1;
+ }
+ } else { /* TRIG_EXT */
+ trig |= PCI9111_AI_TRIG_CTRL_ETIS;
+ pci9111_fifo_reset(dev);
+ pci9111_interrupt_source_set(dev, irq_on_fifo_half_full,
+ irq_on_timer_tick);
+ plx9050_interrupt_control(dev_private->lcr_io_base, true, true,
+ false, true, true);
+ }
+ outb(trig, dev->iobase + PCI9111_AI_TRIG_CTRL_REG);
+
+ dev_private->chunk_counter = 0;
+ dev_private->chunk_num_samples = cmd->chanlist_len *
+ (1 + dev_private->scan_delay);
+
+ return 0;
+}
+
+static void pci9111_ai_munge(struct comedi_device *dev,
+ struct comedi_subdevice *s, void *data,
+ unsigned int num_bytes,
+ unsigned int start_chan_index)
+{
+ unsigned short *array = data;
+ unsigned int maxdata = s->maxdata;
+ unsigned int invert = (maxdata + 1) >> 1;
+ unsigned int shift = (maxdata == 0xffff) ? 0 : 4;
+ unsigned int num_samples = comedi_bytes_to_samples(s, num_bytes);
+ unsigned int i;
+
+ for (i = 0; i < num_samples; i++)
+ array[i] = ((array[i] >> shift) & maxdata) ^ invert;
+}
+
+static void pci9111_handle_fifo_half_full(struct comedi_device *dev,
+ struct comedi_subdevice *s)
+{
+ struct pci9111_private_data *devpriv = dev->private;
+ struct comedi_cmd *cmd = &s->async->cmd;
+ unsigned int samples;
+
+ samples = comedi_nsamples_left(s, PCI9111_FIFO_HALF_SIZE);
+ insw(dev->iobase + PCI9111_AI_FIFO_REG,
+ devpriv->ai_bounce_buffer, samples);
+
+ if (devpriv->scan_delay < 1) {
+ comedi_buf_write_samples(s, devpriv->ai_bounce_buffer, samples);
+ } else {
+ unsigned int pos = 0;
+ unsigned int to_read;
+
+ while (pos < samples) {
+ if (devpriv->chunk_counter < cmd->chanlist_len) {
+ to_read = cmd->chanlist_len -
+ devpriv->chunk_counter;
+
+ if (to_read > samples - pos)
+ to_read = samples - pos;
+
+ comedi_buf_write_samples(s,
+ devpriv->ai_bounce_buffer + pos,
+ to_read);
+ } else {
+ to_read = devpriv->chunk_num_samples -
+ devpriv->chunk_counter;
+
+ if (to_read > samples - pos)
+ to_read = samples - pos;
+ }
+
+ pos += to_read;
+ devpriv->chunk_counter += to_read;
+
+ if (devpriv->chunk_counter >=
+ devpriv->chunk_num_samples)
+ devpriv->chunk_counter = 0;
+ }
+ }
+}
+
+static irqreturn_t pci9111_interrupt(int irq, void *p_device)
+{
+ struct comedi_device *dev = p_device;
+ struct pci9111_private_data *dev_private = dev->private;
+ struct comedi_subdevice *s = dev->read_subdev;
+ struct comedi_async *async;
+ struct comedi_cmd *cmd;
+ unsigned int status;
+ unsigned long irq_flags;
+ unsigned char intcsr;
+
+ if (!dev->attached) {
+ /* Ignore interrupt before device fully attached. */
+ /* Might not even have allocated subdevices yet! */
+ return IRQ_NONE;
+ }
+
+ async = s->async;
+ cmd = &async->cmd;
+
+ spin_lock_irqsave(&dev->spinlock, irq_flags);
+
+ /* Check if we are source of interrupt */
+ intcsr = inb(dev_private->lcr_io_base + PLX9052_INTCSR);
+ if (!(((intcsr & PLX9052_INTCSR_PCIENAB) != 0) &&
+ (((intcsr & PCI9111_LI1_ACTIVE) == PCI9111_LI1_ACTIVE) ||
+ ((intcsr & PCI9111_LI2_ACTIVE) == PCI9111_LI2_ACTIVE)))) {
+ /* Not the source of the interrupt. */
+ /* (N.B. not using PLX9052_INTCSR_SOFTINT) */
+ spin_unlock_irqrestore(&dev->spinlock, irq_flags);
+ return IRQ_NONE;
+ }
+
+ if ((intcsr & PCI9111_LI1_ACTIVE) == PCI9111_LI1_ACTIVE) {
+ /* Interrupt comes from fifo_half-full signal */
+
+ status = inb(dev->iobase + PCI9111_AI_RANGE_STAT_REG);
+
+ /* '0' means FIFO is full, data may have been lost */
+ if (!(status & PCI9111_AI_STAT_FF_FF)) {
+ spin_unlock_irqrestore(&dev->spinlock, irq_flags);
+ dev_dbg(dev->class_dev, "fifo overflow\n");
+ outb(0, dev->iobase + PCI9111_INT_CLR_REG);
+ async->events |= COMEDI_CB_ERROR;
+ comedi_handle_events(dev, s);
+
+ return IRQ_HANDLED;
+ }
+
+ /* '0' means FIFO is half-full */
+ if (!(status & PCI9111_AI_STAT_FF_HF))
+ pci9111_handle_fifo_half_full(dev, s);
+ }
+
+ if (cmd->stop_src == TRIG_COUNT && async->scans_done >= cmd->stop_arg)
+ async->events |= COMEDI_CB_EOA;
+
+ outb(0, dev->iobase + PCI9111_INT_CLR_REG);
+
+ spin_unlock_irqrestore(&dev->spinlock, irq_flags);
+
+ comedi_handle_events(dev, s);
+
+ return IRQ_HANDLED;
+}
+
+static int pci9111_ai_eoc(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned long context)
+{
+ unsigned int status;
+
+ status = inb(dev->iobase + PCI9111_AI_RANGE_STAT_REG);
+ if (status & PCI9111_AI_STAT_FF_EF)
+ return 0;
+ return -EBUSY;
+}
+
+static int pci9111_ai_insn_read(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn, unsigned int *data)
+{
+ unsigned int chan = CR_CHAN(insn->chanspec);
+ unsigned int range = CR_RANGE(insn->chanspec);
+ unsigned int maxdata = s->maxdata;
+ unsigned int invert = (maxdata + 1) >> 1;
+ unsigned int shift = (maxdata == 0xffff) ? 0 : 4;
+ unsigned int status;
+ int ret;
+ int i;
+
+ outb(chan, dev->iobase + PCI9111_AI_CHANNEL_REG);
+
+ status = inb(dev->iobase + PCI9111_AI_RANGE_STAT_REG);
+ if ((status & PCI9111_AI_RANGE_MASK) != range) {
+ outb(range & PCI9111_AI_RANGE_MASK,
+ dev->iobase + PCI9111_AI_RANGE_STAT_REG);
+ }
+
+ pci9111_fifo_reset(dev);
+
+ for (i = 0; i < insn->n; i++) {
+ /* Generate a software trigger */
+ outb(0, dev->iobase + PCI9111_SOFT_TRIG_REG);
+
+ ret = comedi_timeout(dev, s, insn, pci9111_ai_eoc, 0);
+ if (ret) {
+ pci9111_fifo_reset(dev);
+ return ret;
+ }
+
+ data[i] = inw(dev->iobase + PCI9111_AI_FIFO_REG);
+ data[i] = ((data[i] >> shift) & maxdata) ^ invert;
+ }
+
+ return i;
+}
+
+static int pci9111_ao_insn_write(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ unsigned int chan = CR_CHAN(insn->chanspec);
+ unsigned int val = s->readback[chan];
+ int i;
+
+ for (i = 0; i < insn->n; i++) {
+ val = data[i];
+ outw(val, dev->iobase + PCI9111_AO_REG);
+ }
+ s->readback[chan] = val;
+
+ return insn->n;
+}
+
+static int pci9111_di_insn_bits(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ data[1] = inw(dev->iobase + PCI9111_DIO_REG);
+
+ return insn->n;
+}
+
+static int pci9111_do_insn_bits(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ if (comedi_dio_update_state(s, data))
+ outw(s->state, dev->iobase + PCI9111_DIO_REG);
+
+ data[1] = s->state;
+
+ return insn->n;
+}
+
+static int pci9111_reset(struct comedi_device *dev)
+{
+ struct pci9111_private_data *dev_private = dev->private;
+
+ /* Set trigger source to software */
+ plx9050_interrupt_control(dev_private->lcr_io_base, true, true, true,
+ true, false);
+
+ /* disable A/D triggers (software trigger mode) and auto scan off */
+ outb(0, dev->iobase + PCI9111_AI_TRIG_CTRL_REG);
+
+ return 0;
+}
+
+static int pci9111_auto_attach(struct comedi_device *dev,
+ unsigned long context_unused)
+{
+ struct pci_dev *pcidev = comedi_to_pci_dev(dev);
+ struct pci9111_private_data *dev_private;
+ struct comedi_subdevice *s;
+ int ret;
+
+ dev_private = comedi_alloc_devpriv(dev, sizeof(*dev_private));
+ if (!dev_private)
+ return -ENOMEM;
+
+ ret = comedi_pci_enable(dev);
+ if (ret)
+ return ret;
+ dev_private->lcr_io_base = pci_resource_start(pcidev, 1);
+ dev->iobase = pci_resource_start(pcidev, 2);
+
+ pci9111_reset(dev);
+
+ if (pcidev->irq) {
+ ret = request_irq(pcidev->irq, pci9111_interrupt,
+ IRQF_SHARED, dev->board_name, dev);
+ if (ret == 0)
+ dev->irq = pcidev->irq;
+ }
+
+ dev->pacer = comedi_8254_init(dev->iobase + PCI9111_8254_BASE_REG,
+ I8254_OSC_BASE_2MHZ, I8254_IO16, 0);
+ if (!dev->pacer)
+ return -ENOMEM;
+
+ ret = comedi_alloc_subdevices(dev, 4);
+ if (ret)
+ return ret;
+
+ s = &dev->subdevices[0];
+ s->type = COMEDI_SUBD_AI;
+ s->subdev_flags = SDF_READABLE | SDF_COMMON;
+ s->n_chan = 16;
+ s->maxdata = 0xffff;
+ s->range_table = &pci9111_ai_range;
+ s->insn_read = pci9111_ai_insn_read;
+ if (dev->irq) {
+ dev->read_subdev = s;
+ s->subdev_flags |= SDF_CMD_READ;
+ s->len_chanlist = s->n_chan;
+ s->do_cmdtest = pci9111_ai_do_cmd_test;
+ s->do_cmd = pci9111_ai_do_cmd;
+ s->cancel = pci9111_ai_cancel;
+ s->munge = pci9111_ai_munge;
+ }
+
+ s = &dev->subdevices[1];
+ s->type = COMEDI_SUBD_AO;
+ s->subdev_flags = SDF_WRITABLE | SDF_COMMON;
+ s->n_chan = 1;
+ s->maxdata = 0x0fff;
+ s->len_chanlist = 1;
+ s->range_table = &range_bipolar10;
+ s->insn_write = pci9111_ao_insn_write;
+
+ ret = comedi_alloc_subdev_readback(s);
+ if (ret)
+ return ret;
+
+ s = &dev->subdevices[2];
+ s->type = COMEDI_SUBD_DI;
+ s->subdev_flags = SDF_READABLE;
+ s->n_chan = 16;
+ s->maxdata = 1;
+ s->range_table = &range_digital;
+ s->insn_bits = pci9111_di_insn_bits;
+
+ s = &dev->subdevices[3];
+ s->type = COMEDI_SUBD_DO;
+ s->subdev_flags = SDF_WRITABLE;
+ s->n_chan = 16;
+ s->maxdata = 1;
+ s->range_table = &range_digital;
+ s->insn_bits = pci9111_do_insn_bits;
+
+ return 0;
+}
+
+static void pci9111_detach(struct comedi_device *dev)
+{
+ if (dev->iobase)
+ pci9111_reset(dev);
+ comedi_pci_detach(dev);
+}
+
+static struct comedi_driver adl_pci9111_driver = {
+ .driver_name = "adl_pci9111",
+ .module = THIS_MODULE,
+ .auto_attach = pci9111_auto_attach,
+ .detach = pci9111_detach,
+};
+
+static int pci9111_pci_probe(struct pci_dev *dev,
+ const struct pci_device_id *id)
+{
+ return comedi_pci_auto_config(dev, &adl_pci9111_driver,
+ id->driver_data);
+}
+
+static const struct pci_device_id pci9111_pci_table[] = {
+ { PCI_DEVICE(PCI_VENDOR_ID_ADLINK, 0x9111) },
+ /* { PCI_DEVICE(PCI_VENDOR_ID_ADLINK, PCI9111_HG_DEVICE_ID) }, */
+ { 0 }
+};
+MODULE_DEVICE_TABLE(pci, pci9111_pci_table);
+
+static struct pci_driver adl_pci9111_pci_driver = {
+ .name = "adl_pci9111",
+ .id_table = pci9111_pci_table,
+ .probe = pci9111_pci_probe,
+ .remove = comedi_pci_auto_unconfig,
+};
+module_comedi_pci_driver(adl_pci9111_driver, adl_pci9111_pci_driver);
+
+MODULE_AUTHOR("Comedi http://www.comedi.org");
+MODULE_DESCRIPTION("Comedi low-level driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/comedi/drivers/adl_pci9118.c b/drivers/staging/comedi/drivers/adl_pci9118.c
new file mode 100644
index 000000000..fb3043dcf
--- /dev/null
+++ b/drivers/staging/comedi/drivers/adl_pci9118.c
@@ -0,0 +1,1761 @@
+/*
+ * comedi/drivers/adl_pci9118.c
+ *
+ * hardware driver for ADLink cards:
+ * card: PCI-9118DG, PCI-9118HG, PCI-9118HR
+ * driver: pci9118dg, pci9118hg, pci9118hr
+ *
+ * Author: Michal Dobes <dobes@tesnet.cz>
+ *
+ */
+
+/*
+ * Driver: adl_pci9118
+ * Description: Adlink PCI-9118DG, PCI-9118HG, PCI-9118HR
+ * Author: Michal Dobes <dobes@tesnet.cz>
+ * Devices: [ADLink] PCI-9118DG (pci9118dg), PCI-9118HG (pci9118hg),
+ * PCI-9118HR (pci9118hr)
+ * Status: works
+ *
+ * This driver supports AI, AO, DI and DO subdevices.
+ * AI subdevice supports cmd and insn interface,
+ * other subdevices support only insn interface.
+ * For AI:
+ * - If cmd->scan_begin_src=TRIG_EXT then trigger input is TGIN (pin 46).
+ * - If cmd->convert_src=TRIG_EXT then trigger input is EXTTRG (pin 44).
+ * - If cmd->start_src/stop_src=TRIG_EXT then trigger input is TGIN (pin 46).
+ * - It is not necessary to have cmd.scan_end_arg=cmd.chanlist_len but
+ * cmd.scan_end_arg modulo cmd.chanlist_len must by 0.
+ * - If return value of cmdtest is 5 then you've bad channel list
+ * (it isn't possible mixture S.E. and DIFF inputs or bipolar and unipolar
+ * ranges).
+ *
+ * There are some hardware limitations:
+ * a) You cann't use mixture of unipolar/bipoar ranges or differencial/single
+ * ended inputs.
+ * b) DMA transfers must have the length aligned to two samples (32 bit),
+ * so there is some problems if cmd->chanlist_len is odd. This driver tries
+ * bypass this with adding one sample to the end of the every scan and discard
+ * it on output but this can't be used if cmd->scan_begin_src=TRIG_FOLLOW
+ * and is used flag CMDF_WAKE_EOS, then driver switch to interrupt driven mode
+ * with interrupt after every sample.
+ * c) If isn't used DMA then you can use only mode where
+ * cmd->scan_begin_src=TRIG_FOLLOW.
+ *
+ * Configuration options:
+ * [0] - PCI bus of device (optional)
+ * [1] - PCI slot of device (optional)
+ * If bus/slot is not specified, then first available PCI
+ * card will be used.
+ * [2] - 0= standard 8 DIFF/16 SE channels configuration
+ * n = external multiplexer connected, 1 <= n <= 256
+ * [3] - ignored
+ * [4] - sample&hold signal - card can generate signal for external S&H board
+ * 0 = use SSHO(pin 45) signal is generated in onboard hardware S&H logic
+ * 0 != use ADCHN7(pin 23) signal is generated from driver, number say how
+ * long delay is requested in ns and sign polarity of the hold
+ * (in this case external multiplexor can serve only 128 channels)
+ * [5] - ignored
+ */
+
+/*
+ * FIXME
+ *
+ * All the supported boards have the same PCI vendor and device IDs, so
+ * auto-attachment of PCI devices will always find the first board type.
+ *
+ * Perhaps the boards have different subdevice IDs that we could use to
+ * distinguish them?
+ *
+ * Need some device attributes so the board type can be corrected after
+ * attachment if necessary, and possibly to set other options supported by
+ * manual attachment.
+ */
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/gfp.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+
+#include "../comedi_pci.h"
+
+#include "amcc_s5933.h"
+#include "comedi_8254.h"
+
+#define IORANGE_9118 64 /* I hope */
+#define PCI9118_CHANLEN 255 /*
+ * len of chanlist, some source say 256,
+ * but reality looks like 255 :-(
+ */
+
+/*
+ * PCI BAR2 Register map (dev->iobase)
+ */
+#define PCI9118_TIMER_BASE 0x00
+#define PCI9118_AI_FIFO_REG 0x10
+#define PCI9118_AO_REG(x) (0x10 + ((x) * 4))
+#define PCI9118_AI_STATUS_REG 0x18
+#define PCI9118_AI_STATUS_NFULL (1 << 8) /* 0=FIFO full (fatal) */
+#define PCI9118_AI_STATUS_NHFULL (1 << 7) /* 0=FIFO half full */
+#define PCI9118_AI_STATUS_NEPTY (1 << 6) /* 0=FIFO empty */
+#define PCI9118_AI_STATUS_ACMP (1 << 5) /* 1=about trigger complete */
+#define PCI9118_AI_STATUS_DTH (1 << 4) /* 1=ext. digital trigger */
+#define PCI9118_AI_STATUS_BOVER (1 << 3) /* 1=burst overrun (fatal) */
+#define PCI9118_AI_STATUS_ADOS (1 << 2) /* 1=A/D over speed (warn) */
+#define PCI9118_AI_STATUS_ADOR (1 << 1) /* 1=A/D overrun (fatal) */
+#define PCI9118_AI_STATUS_ADRDY (1 << 0) /* 1=A/D ready */
+#define PCI9118_AI_CTRL_REG 0x18
+#define PCI9118_AI_CTRL_UNIP (1 << 7) /* 1=unipolar */
+#define PCI9118_AI_CTRL_DIFF (1 << 6) /* 1=differential inputs */
+#define PCI9118_AI_CTRL_SOFTG (1 << 5) /* 1=8254 software gate */
+#define PCI9118_AI_CTRL_EXTG (1 << 4) /* 1=8254 TGIN(pin 46) gate */
+#define PCI9118_AI_CTRL_EXTM (1 << 3) /* 1=ext. trigger (pin 44) */
+#define PCI9118_AI_CTRL_TMRTR (1 << 2) /* 1=8254 is trigger source */
+#define PCI9118_AI_CTRL_INT (1 << 1) /* 1=enable interrupt */
+#define PCI9118_AI_CTRL_DMA (1 << 0) /* 1=enable DMA */
+#define PCI9118_DIO_REG 0x1c
+#define PCI9118_SOFTTRG_REG 0x20
+#define PCI9118_AI_CHANLIST_REG 0x24
+#define PCI9118_AI_CHANLIST_RANGE(x) (((x) & 0x3) << 8)
+#define PCI9118_AI_CHANLIST_CHAN(x) ((x) << 0)
+#define PCI9118_AI_BURST_NUM_REG 0x28
+#define PCI9118_AI_AUTOSCAN_MODE_REG 0x2c
+#define PCI9118_AI_CFG_REG 0x30
+#define PCI9118_AI_CFG_PDTRG (1 << 7) /* 1=positive trigger */
+#define PCI9118_AI_CFG_PETRG (1 << 6) /* 1=positive ext. trigger */
+#define PCI9118_AI_CFG_BSSH (1 << 5) /* 1=with sample & hold */
+#define PCI9118_AI_CFG_BM (1 << 4) /* 1=burst mode */
+#define PCI9118_AI_CFG_BS (1 << 3) /* 1=burst mode start */
+#define PCI9118_AI_CFG_PM (1 << 2) /* 1=post trigger */
+#define PCI9118_AI_CFG_AM (1 << 1) /* 1=about trigger */
+#define PCI9118_AI_CFG_START (1 << 0) /* 1=trigger start */
+#define PCI9118_FIFO_RESET_REG 0x34
+#define PCI9118_INT_CTRL_REG 0x38
+#define PCI9118_INT_CTRL_TIMER (1 << 3) /* timer interrupt */
+#define PCI9118_INT_CTRL_ABOUT (1 << 2) /* about trigger complete */
+#define PCI9118_INT_CTRL_HFULL (1 << 1) /* A/D FIFO half full */
+#define PCI9118_INT_CTRL_DTRG (1 << 0) /* ext. digital trigger */
+
+#define START_AI_EXT 0x01 /* start measure on external trigger */
+#define STOP_AI_EXT 0x02 /* stop measure on external trigger */
+#define STOP_AI_INT 0x08 /* stop measure on internal trigger */
+
+#define PCI9118_HALF_FIFO_SZ (1024 / 2)
+
+static const struct comedi_lrange pci9118_ai_range = {
+ 8, {
+ BIP_RANGE(5),
+ BIP_RANGE(2.5),
+ BIP_RANGE(1.25),
+ BIP_RANGE(0.625),
+ UNI_RANGE(10),
+ UNI_RANGE(5),
+ UNI_RANGE(2.5),
+ UNI_RANGE(1.25)
+ }
+};
+
+static const struct comedi_lrange pci9118hg_ai_range = {
+ 8, {
+ BIP_RANGE(5),
+ BIP_RANGE(0.5),
+ BIP_RANGE(0.05),
+ BIP_RANGE(0.005),
+ UNI_RANGE(10),
+ UNI_RANGE(1),
+ UNI_RANGE(0.1),
+ UNI_RANGE(0.01)
+ }
+};
+
+#define PCI9118_BIPOLAR_RANGES 4 /*
+ * used for test on mixture
+ * of BIP/UNI ranges
+ */
+
+enum pci9118_boardid {
+ BOARD_PCI9118DG,
+ BOARD_PCI9118HG,
+ BOARD_PCI9118HR,
+};
+
+struct pci9118_boardinfo {
+ const char *name;
+ unsigned int ai_is_16bit:1;
+ unsigned int is_hg:1;
+};
+
+static const struct pci9118_boardinfo pci9118_boards[] = {
+ [BOARD_PCI9118DG] = {
+ .name = "pci9118dg",
+ },
+ [BOARD_PCI9118HG] = {
+ .name = "pci9118hg",
+ .is_hg = 1,
+ },
+ [BOARD_PCI9118HR] = {
+ .name = "pci9118hr",
+ .ai_is_16bit = 1,
+ },
+};
+
+struct pci9118_dmabuf {
+ unsigned short *virt; /* virtual address of buffer */
+ dma_addr_t hw; /* hardware (bus) address of buffer */
+ unsigned int size; /* size of dma buffer in bytes */
+ unsigned int use_size; /* which size we may now use for transfer */
+};
+
+struct pci9118_private {
+ unsigned long iobase_a; /* base+size for AMCC chip */
+ unsigned int master:1;
+ unsigned int dma_doublebuf:1;
+ unsigned int ai_neverending:1;
+ unsigned int usedma:1;
+ unsigned int usemux:1;
+ unsigned char ai_ctrl;
+ unsigned char int_ctrl;
+ unsigned char ai_cfg;
+ unsigned int ai_do; /* what do AI? 0=nothing, 1 to 4 mode */
+ unsigned int ai_n_realscanlen; /*
+ * what we must transfer for one
+ * outgoing scan include front/back adds
+ */
+ unsigned int ai_act_dmapos; /* position in actual real stream */
+ unsigned int ai_add_front; /*
+ * how many channels we must add
+ * before scan to satisfy S&H?
+ */
+ unsigned int ai_add_back; /*
+ * how many channels we must add
+ * before scan to satisfy DMA?
+ */
+ unsigned int ai_flags;
+ char ai12_startstop; /*
+ * measure can start/stop
+ * on external trigger
+ */
+ unsigned int dma_actbuf; /* which buffer is used now */
+ struct pci9118_dmabuf dmabuf[2];
+ int softsshdelay; /*
+ * >0 use software S&H,
+ * numer is requested delay in ns
+ */
+ unsigned char softsshsample; /*
+ * polarity of S&H signal
+ * in sample state
+ */
+ unsigned char softsshhold; /*
+ * polarity of S&H signal
+ * in hold state
+ */
+ unsigned int ai_ns_min;
+};
+
+static void pci9118_amcc_setup_dma(struct comedi_device *dev, unsigned int buf)
+{
+ struct pci9118_private *devpriv = dev->private;
+ struct pci9118_dmabuf *dmabuf = &devpriv->dmabuf[buf];
+
+ /* set the master write address and transfer count */
+ outl(dmabuf->hw, devpriv->iobase_a + AMCC_OP_REG_MWAR);
+ outl(dmabuf->use_size, devpriv->iobase_a + AMCC_OP_REG_MWTC);
+}
+
+static void pci9118_amcc_dma_ena(struct comedi_device *dev, bool enable)
+{
+ struct pci9118_private *devpriv = dev->private;
+ unsigned int mcsr;
+
+ mcsr = inl(devpriv->iobase_a + AMCC_OP_REG_MCSR);
+ if (enable)
+ mcsr |= RESET_A2P_FLAGS | A2P_HI_PRIORITY | EN_A2P_TRANSFERS;
+ else
+ mcsr &= ~EN_A2P_TRANSFERS;
+ outl(mcsr, devpriv->iobase_a + AMCC_OP_REG_MCSR);
+}
+
+static void pci9118_amcc_int_ena(struct comedi_device *dev, bool enable)
+{
+ struct pci9118_private *devpriv = dev->private;
+ unsigned int intcsr;
+
+ /* enable/disable interrupt for AMCC Incoming Mailbox 4 (32-bit) */
+ intcsr = inl(devpriv->iobase_a + AMCC_OP_REG_INTCSR);
+ if (enable)
+ intcsr |= 0x1f00;
+ else
+ intcsr &= ~0x1f00;
+ outl(intcsr, devpriv->iobase_a + AMCC_OP_REG_INTCSR);
+}
+
+static void pci9118_ai_reset_fifo(struct comedi_device *dev)
+{
+ /* writing any value resets the A/D FIFO */
+ outl(0, dev->iobase + PCI9118_FIFO_RESET_REG);
+}
+
+static int check_channel_list(struct comedi_device *dev,
+ struct comedi_subdevice *s, int n_chan,
+ unsigned int *chanlist, int frontadd, int backadd)
+{
+ struct pci9118_private *devpriv = dev->private;
+ unsigned int i, differencial = 0, bipolar = 0;
+
+ /* correct channel and range number check itself comedi/range.c */
+ if (n_chan < 1) {
+ dev_err(dev->class_dev, "range/channel list is empty!\n");
+ return 0;
+ }
+ if ((frontadd + n_chan + backadd) > s->len_chanlist) {
+ dev_err(dev->class_dev,
+ "range/channel list is too long for actual configuration!\n");
+ return 0;
+ }
+
+ if (CR_AREF(chanlist[0]) == AREF_DIFF)
+ differencial = 1; /* all input must be diff */
+ if (CR_RANGE(chanlist[0]) < PCI9118_BIPOLAR_RANGES)
+ bipolar = 1; /* all input must be bipolar */
+ if (n_chan > 1)
+ for (i = 1; i < n_chan; i++) { /* check S.E/diff */
+ if ((CR_AREF(chanlist[i]) == AREF_DIFF) !=
+ (differencial)) {
+ dev_err(dev->class_dev,
+ "Differential and single ended inputs can't be mixed!\n");
+ return 0;
+ }
+ if ((CR_RANGE(chanlist[i]) < PCI9118_BIPOLAR_RANGES) !=
+ (bipolar)) {
+ dev_err(dev->class_dev,
+ "Bipolar and unipolar ranges can't be mixed!\n");
+ return 0;
+ }
+ if (!devpriv->usemux && differencial &&
+ (CR_CHAN(chanlist[i]) >= (s->n_chan / 2))) {
+ dev_err(dev->class_dev,
+ "AREF_DIFF is only available for the first 8 channels!\n");
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+static void pci9118_set_chanlist(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ int n_chan, unsigned int *chanlist,
+ int frontadd, int backadd)
+{
+ struct pci9118_private *devpriv = dev->private;
+ unsigned int chan0 = CR_CHAN(chanlist[0]);
+ unsigned int range0 = CR_RANGE(chanlist[0]);
+ unsigned int aref0 = CR_AREF(chanlist[0]);
+ unsigned int ssh = 0x00;
+ unsigned int val;
+ int i;
+
+ /*
+ * Configure analog input based on the first chanlist entry.
+ * All entries are either unipolar or bipolar and single-ended
+ * or differential.
+ */
+ devpriv->ai_ctrl = 0;
+ if (comedi_range_is_unipolar(s, range0))
+ devpriv->ai_ctrl |= PCI9118_AI_CTRL_UNIP;
+ if (aref0 == AREF_DIFF)
+ devpriv->ai_ctrl |= PCI9118_AI_CTRL_DIFF;
+ outl(devpriv->ai_ctrl, dev->iobase + PCI9118_AI_CTRL_REG);
+
+ /* gods know why this sequence! */
+ outl(2, dev->iobase + PCI9118_AI_AUTOSCAN_MODE_REG);
+ outl(0, dev->iobase + PCI9118_AI_AUTOSCAN_MODE_REG);
+ outl(1, dev->iobase + PCI9118_AI_AUTOSCAN_MODE_REG);
+
+ /* insert channels for S&H */
+ if (frontadd) {
+ val = PCI9118_AI_CHANLIST_CHAN(chan0) |
+ PCI9118_AI_CHANLIST_RANGE(range0);
+ ssh = devpriv->softsshsample;
+ for (i = 0; i < frontadd; i++) {
+ outl(val | ssh, dev->iobase + PCI9118_AI_CHANLIST_REG);
+ ssh = devpriv->softsshhold;
+ }
+ }
+
+ /* store chanlist */
+ for (i = 0; i < n_chan; i++) {
+ unsigned int chan = CR_CHAN(chanlist[i]);
+ unsigned int range = CR_RANGE(chanlist[i]);
+
+ val = PCI9118_AI_CHANLIST_CHAN(chan) |
+ PCI9118_AI_CHANLIST_RANGE(range);
+ outl(val | ssh, dev->iobase + PCI9118_AI_CHANLIST_REG);
+ }
+
+ /* insert channels to fit onto 32bit DMA */
+ if (backadd) {
+ val = PCI9118_AI_CHANLIST_CHAN(chan0) |
+ PCI9118_AI_CHANLIST_RANGE(range0);
+ for (i = 0; i < backadd; i++)
+ outl(val | ssh, dev->iobase + PCI9118_AI_CHANLIST_REG);
+ }
+ /* close scan queue */
+ outl(0, dev->iobase + PCI9118_AI_AUTOSCAN_MODE_REG);
+ /* udelay(100); important delay, or first sample will be crippled */
+}
+
+static void interrupt_pci9118_ai_mode4_switch(struct comedi_device *dev,
+ unsigned int next_buf)
+{
+ struct pci9118_private *devpriv = dev->private;
+ struct pci9118_dmabuf *dmabuf = &devpriv->dmabuf[next_buf];
+
+ devpriv->ai_cfg = PCI9118_AI_CFG_PDTRG | PCI9118_AI_CFG_PETRG |
+ PCI9118_AI_CFG_AM;
+ outl(devpriv->ai_cfg, dev->iobase + PCI9118_AI_CFG_REG);
+ comedi_8254_load(dev->pacer, 0, dmabuf->hw >> 1,
+ I8254_MODE0 | I8254_BINARY);
+ devpriv->ai_cfg |= PCI9118_AI_CFG_START;
+ outl(devpriv->ai_cfg, dev->iobase + PCI9118_AI_CFG_REG);
+}
+
+static unsigned int valid_samples_in_act_dma_buf(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ unsigned int n_raw_samples)
+{
+ struct pci9118_private *devpriv = dev->private;
+ struct comedi_cmd *cmd = &s->async->cmd;
+ unsigned int start_pos = devpriv->ai_add_front;
+ unsigned int stop_pos = start_pos + cmd->chanlist_len;
+ unsigned int span_len = stop_pos + devpriv->ai_add_back;
+ unsigned int dma_pos = devpriv->ai_act_dmapos;
+ unsigned int whole_spans, n_samples, x;
+
+ if (span_len == cmd->chanlist_len)
+ return n_raw_samples; /* use all samples */
+
+ /*
+ * Not all samples are to be used. Buffer contents consist of a
+ * possibly non-whole number of spans and a region of each span
+ * is to be used.
+ *
+ * Account for samples in whole number of spans.
+ */
+ whole_spans = n_raw_samples / span_len;
+ n_samples = whole_spans * cmd->chanlist_len;
+ n_raw_samples -= whole_spans * span_len;
+
+ /*
+ * Deal with remaining samples which could overlap up to two spans.
+ */
+ while (n_raw_samples) {
+ if (dma_pos < start_pos) {
+ /* Skip samples before start position. */
+ x = start_pos - dma_pos;
+ if (x > n_raw_samples)
+ x = n_raw_samples;
+ dma_pos += x;
+ n_raw_samples -= x;
+ if (!n_raw_samples)
+ break;
+ }
+ if (dma_pos < stop_pos) {
+ /* Include samples before stop position. */
+ x = stop_pos - dma_pos;
+ if (x > n_raw_samples)
+ x = n_raw_samples;
+ n_samples += x;
+ dma_pos += x;
+ n_raw_samples -= x;
+ }
+ /* Advance to next span. */
+ start_pos += span_len;
+ stop_pos += span_len;
+ }
+ return n_samples;
+}
+
+static void move_block_from_dma(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ unsigned short *dma_buffer,
+ unsigned int n_raw_samples)
+{
+ struct pci9118_private *devpriv = dev->private;
+ struct comedi_cmd *cmd = &s->async->cmd;
+ unsigned int start_pos = devpriv->ai_add_front;
+ unsigned int stop_pos = start_pos + cmd->chanlist_len;
+ unsigned int span_len = stop_pos + devpriv->ai_add_back;
+ unsigned int dma_pos = devpriv->ai_act_dmapos;
+ unsigned int x;
+
+ if (span_len == cmd->chanlist_len) {
+ /* All samples are to be copied. */
+ comedi_buf_write_samples(s, dma_buffer, n_raw_samples);
+ dma_pos += n_raw_samples;
+ } else {
+ /*
+ * Not all samples are to be copied. Buffer contents consist
+ * of a possibly non-whole number of spans and a region of
+ * each span is to be copied.
+ */
+ while (n_raw_samples) {
+ if (dma_pos < start_pos) {
+ /* Skip samples before start position. */
+ x = start_pos - dma_pos;
+ if (x > n_raw_samples)
+ x = n_raw_samples;
+ dma_pos += x;
+ n_raw_samples -= x;
+ if (!n_raw_samples)
+ break;
+ }
+ if (dma_pos < stop_pos) {
+ /* Copy samples before stop position. */
+ x = stop_pos - dma_pos;
+ if (x > n_raw_samples)
+ x = n_raw_samples;
+ comedi_buf_write_samples(s, dma_buffer, x);
+ dma_pos += x;
+ n_raw_samples -= x;
+ }
+ /* Advance to next span. */
+ start_pos += span_len;
+ stop_pos += span_len;
+ }
+ }
+ /* Update position in span for next time. */
+ devpriv->ai_act_dmapos = dma_pos % span_len;
+}
+
+static void pci9118_exttrg_enable(struct comedi_device *dev, bool enable)
+{
+ struct pci9118_private *devpriv = dev->private;
+
+ if (enable)
+ devpriv->int_ctrl |= PCI9118_INT_CTRL_DTRG;
+ else
+ devpriv->int_ctrl &= ~PCI9118_INT_CTRL_DTRG;
+ outl(devpriv->int_ctrl, dev->iobase + PCI9118_INT_CTRL_REG);
+
+ if (devpriv->int_ctrl)
+ pci9118_amcc_int_ena(dev, true);
+ else
+ pci9118_amcc_int_ena(dev, false);
+}
+
+static void pci9118_calc_divisors(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ unsigned int *tim1, unsigned int *tim2,
+ unsigned int flags, int chans,
+ unsigned int *div1, unsigned int *div2,
+ unsigned int chnsshfront)
+{
+ struct comedi_8254 *pacer = dev->pacer;
+ struct comedi_cmd *cmd = &s->async->cmd;
+
+ *div1 = *tim2 / pacer->osc_base; /* convert timer (burst) */
+ *div2 = *tim1 / pacer->osc_base; /* scan timer */
+ *div2 = *div2 / *div1; /* major timer is c1*c2 */
+ if (*div2 < chans)
+ *div2 = chans;
+
+ *tim2 = *div1 * pacer->osc_base; /* real convert timer */
+
+ if (cmd->convert_src == TRIG_NOW && !chnsshfront) {
+ /* use BSSH signal */
+ if (*div2 < (chans + 2))
+ *div2 = chans + 2;
+ }
+
+ *tim1 = *div1 * *div2 * pacer->osc_base;
+}
+
+static void pci9118_start_pacer(struct comedi_device *dev, int mode)
+{
+ if (mode == 1 || mode == 2 || mode == 4)
+ comedi_8254_pacer_enable(dev->pacer, 1, 2, true);
+}
+
+static int pci9118_ai_cancel(struct comedi_device *dev,
+ struct comedi_subdevice *s)
+{
+ struct pci9118_private *devpriv = dev->private;
+
+ if (devpriv->usedma)
+ pci9118_amcc_dma_ena(dev, false);
+ pci9118_exttrg_enable(dev, false);
+ comedi_8254_pacer_enable(dev->pacer, 1, 2, false);
+ /* set default config (disable burst and triggers) */
+ devpriv->ai_cfg = PCI9118_AI_CFG_PDTRG | PCI9118_AI_CFG_PETRG;
+ outl(devpriv->ai_cfg, dev->iobase + PCI9118_AI_CFG_REG);
+ /* reset acqusition control */
+ devpriv->ai_ctrl = 0;
+ outl(devpriv->ai_ctrl, dev->iobase + PCI9118_AI_CTRL_REG);
+ outl(0, dev->iobase + PCI9118_AI_BURST_NUM_REG);
+ /* reset scan queue */
+ outl(1, dev->iobase + PCI9118_AI_AUTOSCAN_MODE_REG);
+ outl(2, dev->iobase + PCI9118_AI_AUTOSCAN_MODE_REG);
+ pci9118_ai_reset_fifo(dev);
+
+ devpriv->int_ctrl = 0;
+ outl(devpriv->int_ctrl, dev->iobase + PCI9118_INT_CTRL_REG);
+ pci9118_amcc_int_ena(dev, false);
+
+ devpriv->ai_do = 0;
+ devpriv->usedma = 0;
+
+ devpriv->ai_act_dmapos = 0;
+ s->async->inttrig = NULL;
+ devpriv->ai_neverending = 0;
+ devpriv->dma_actbuf = 0;
+
+ return 0;
+}
+
+static void pci9118_ai_munge(struct comedi_device *dev,
+ struct comedi_subdevice *s, void *data,
+ unsigned int num_bytes,
+ unsigned int start_chan_index)
+{
+ struct pci9118_private *devpriv = dev->private;
+ unsigned short *array = data;
+ unsigned int num_samples = comedi_bytes_to_samples(s, num_bytes);
+ unsigned int i;
+
+ for (i = 0; i < num_samples; i++) {
+ if (devpriv->usedma)
+ array[i] = be16_to_cpu(array[i]);
+ if (s->maxdata == 0xffff)
+ array[i] ^= 0x8000;
+ else
+ array[i] = (array[i] >> 4) & 0x0fff;
+ }
+}
+
+static void interrupt_pci9118_ai_onesample(struct comedi_device *dev,
+ struct comedi_subdevice *s)
+{
+ struct pci9118_private *devpriv = dev->private;
+ struct comedi_cmd *cmd = &s->async->cmd;
+ unsigned short sampl;
+
+ sampl = inl(dev->iobase + PCI9118_AI_FIFO_REG);
+
+ comedi_buf_write_samples(s, &sampl, 1);
+
+ if (!devpriv->ai_neverending) {
+ if (s->async->scans_done >= cmd->stop_arg)
+ s->async->events |= COMEDI_CB_EOA;
+ }
+}
+
+static void interrupt_pci9118_ai_dma(struct comedi_device *dev,
+ struct comedi_subdevice *s)
+{
+ struct pci9118_private *devpriv = dev->private;
+ struct comedi_cmd *cmd = &s->async->cmd;
+ struct pci9118_dmabuf *dmabuf = &devpriv->dmabuf[devpriv->dma_actbuf];
+ unsigned int n_all = comedi_bytes_to_samples(s, dmabuf->use_size);
+ unsigned int n_valid;
+ bool more_dma;
+
+ /* determine whether more DMA buffers to do after this one */
+ n_valid = valid_samples_in_act_dma_buf(dev, s, n_all);
+ more_dma = n_valid < comedi_nsamples_left(s, n_valid + 1);
+
+ /* switch DMA buffers and restart DMA if double buffering */
+ if (more_dma && devpriv->dma_doublebuf) {
+ devpriv->dma_actbuf = 1 - devpriv->dma_actbuf;
+ pci9118_amcc_setup_dma(dev, devpriv->dma_actbuf);
+ if (devpriv->ai_do == 4) {
+ interrupt_pci9118_ai_mode4_switch(dev,
+ devpriv->dma_actbuf);
+ }
+ }
+
+ if (n_all)
+ move_block_from_dma(dev, s, dmabuf->virt, n_all);
+
+ if (!devpriv->ai_neverending) {
+ if (s->async->scans_done >= cmd->stop_arg)
+ s->async->events |= COMEDI_CB_EOA;
+ }
+
+ if (s->async->events & COMEDI_CB_CANCEL_MASK)
+ more_dma = false;
+
+ /* restart DMA if not double buffering */
+ if (more_dma && !devpriv->dma_doublebuf) {
+ pci9118_amcc_setup_dma(dev, 0);
+ if (devpriv->ai_do == 4)
+ interrupt_pci9118_ai_mode4_switch(dev, 0);
+ }
+}
+
+static irqreturn_t pci9118_interrupt(int irq, void *d)
+{
+ struct comedi_device *dev = d;
+ struct comedi_subdevice *s = dev->read_subdev;
+ struct pci9118_private *devpriv = dev->private;
+ unsigned int intsrc; /* IRQ reasons from card */
+ unsigned int intcsr; /* INT register from AMCC chip */
+ unsigned int adstat; /* STATUS register */
+
+ if (!dev->attached)
+ return IRQ_NONE;
+
+ intsrc = inl(dev->iobase + PCI9118_INT_CTRL_REG) & 0xf;
+ intcsr = inl(devpriv->iobase_a + AMCC_OP_REG_INTCSR);
+
+ if (!intsrc && !(intcsr & ANY_S593X_INT))
+ return IRQ_NONE;
+
+ outl(intcsr | 0x00ff0000, devpriv->iobase_a + AMCC_OP_REG_INTCSR);
+
+ if (intcsr & MASTER_ABORT_INT) {
+ dev_err(dev->class_dev, "AMCC IRQ - MASTER DMA ABORT!\n");
+ s->async->events |= COMEDI_CB_ERROR;
+ goto interrupt_exit;
+ }
+
+ if (intcsr & TARGET_ABORT_INT) {
+ dev_err(dev->class_dev, "AMCC IRQ - TARGET DMA ABORT!\n");
+ s->async->events |= COMEDI_CB_ERROR;
+ goto interrupt_exit;
+ }
+
+ adstat = inl(dev->iobase + PCI9118_AI_STATUS_REG);
+ if ((adstat & PCI9118_AI_STATUS_NFULL) == 0) {
+ dev_err(dev->class_dev,
+ "A/D FIFO Full status (Fatal Error!)\n");
+ s->async->events |= COMEDI_CB_ERROR | COMEDI_CB_OVERFLOW;
+ goto interrupt_exit;
+ }
+ if (adstat & PCI9118_AI_STATUS_BOVER) {
+ dev_err(dev->class_dev,
+ "A/D Burst Mode Overrun Status (Fatal Error!)\n");
+ s->async->events |= COMEDI_CB_ERROR | COMEDI_CB_OVERFLOW;
+ goto interrupt_exit;
+ }
+ if (adstat & PCI9118_AI_STATUS_ADOS) {
+ dev_err(dev->class_dev, "A/D Over Speed Status (Warning!)\n");
+ s->async->events |= COMEDI_CB_ERROR;
+ goto interrupt_exit;
+ }
+ if (adstat & PCI9118_AI_STATUS_ADOR) {
+ dev_err(dev->class_dev, "A/D Overrun Status (Fatal Error!)\n");
+ s->async->events |= COMEDI_CB_ERROR | COMEDI_CB_OVERFLOW;
+ goto interrupt_exit;
+ }
+
+ if (!devpriv->ai_do)
+ return IRQ_HANDLED;
+
+ if (devpriv->ai12_startstop) {
+ if ((adstat & PCI9118_AI_STATUS_DTH) &&
+ (intsrc & PCI9118_INT_CTRL_DTRG)) {
+ /* start/stop of measure */
+ if (devpriv->ai12_startstop & START_AI_EXT) {
+ /* deactivate EXT trigger */
+ devpriv->ai12_startstop &= ~START_AI_EXT;
+ if (!(devpriv->ai12_startstop & STOP_AI_EXT))
+ pci9118_exttrg_enable(dev, false);
+
+ /* start pacer */
+ pci9118_start_pacer(dev, devpriv->ai_do);
+ outl(devpriv->ai_ctrl,
+ dev->iobase + PCI9118_AI_CTRL_REG);
+ } else if (devpriv->ai12_startstop & STOP_AI_EXT) {
+ /* deactivate EXT trigger */
+ devpriv->ai12_startstop &= ~STOP_AI_EXT;
+ pci9118_exttrg_enable(dev, false);
+
+ /* on next interrupt measure will stop */
+ devpriv->ai_neverending = 0;
+ }
+ }
+ }
+
+ if (devpriv->usedma)
+ interrupt_pci9118_ai_dma(dev, s);
+ else
+ interrupt_pci9118_ai_onesample(dev, s);
+
+interrupt_exit:
+ comedi_handle_events(dev, s);
+ return IRQ_HANDLED;
+}
+
+static void pci9118_ai_cmd_start(struct comedi_device *dev)
+{
+ struct pci9118_private *devpriv = dev->private;
+
+ outl(devpriv->int_ctrl, dev->iobase + PCI9118_INT_CTRL_REG);
+ outl(devpriv->ai_cfg, dev->iobase + PCI9118_AI_CFG_REG);
+ if (devpriv->ai_do != 3) {
+ pci9118_start_pacer(dev, devpriv->ai_do);
+ devpriv->ai_ctrl |= PCI9118_AI_CTRL_SOFTG;
+ }
+ outl(devpriv->ai_ctrl, dev->iobase + PCI9118_AI_CTRL_REG);
+}
+
+static int pci9118_ai_inttrig(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ unsigned int trig_num)
+{
+ struct comedi_cmd *cmd = &s->async->cmd;
+
+ if (trig_num != cmd->start_arg)
+ return -EINVAL;
+
+ s->async->inttrig = NULL;
+ pci9118_ai_cmd_start(dev);
+
+ return 1;
+}
+
+static int Compute_and_setup_dma(struct comedi_device *dev,
+ struct comedi_subdevice *s)
+{
+ struct pci9118_private *devpriv = dev->private;
+ struct comedi_cmd *cmd = &s->async->cmd;
+ struct pci9118_dmabuf *dmabuf0 = &devpriv->dmabuf[0];
+ struct pci9118_dmabuf *dmabuf1 = &devpriv->dmabuf[1];
+ unsigned int dmalen0, dmalen1, i;
+
+ dmalen0 = dmabuf0->size;
+ dmalen1 = dmabuf1->size;
+ /* isn't output buff smaller that our DMA buff? */
+ if (dmalen0 > s->async->prealloc_bufsz) {
+ /* align to 32bit down */
+ dmalen0 = s->async->prealloc_bufsz & ~3L;
+ }
+ if (dmalen1 > s->async->prealloc_bufsz) {
+ /* align to 32bit down */
+ dmalen1 = s->async->prealloc_bufsz & ~3L;
+ }
+
+ /* we want wake up every scan? */
+ if (devpriv->ai_flags & CMDF_WAKE_EOS) {
+ if (dmalen0 < (devpriv->ai_n_realscanlen << 1)) {
+ /* uff, too short DMA buffer, disable EOS support! */
+ devpriv->ai_flags &= (~CMDF_WAKE_EOS);
+ dev_info(dev->class_dev,
+ "WAR: DMA0 buf too short, can't support CMDF_WAKE_EOS (%d<%d)\n",
+ dmalen0, devpriv->ai_n_realscanlen << 1);
+ } else {
+ /* short first DMA buffer to one scan */
+ dmalen0 = devpriv->ai_n_realscanlen << 1;
+ if (dmalen0 < 4) {
+ dev_info(dev->class_dev,
+ "ERR: DMA0 buf len bug? (%d<4)\n",
+ dmalen0);
+ dmalen0 = 4;
+ }
+ }
+ }
+ if (devpriv->ai_flags & CMDF_WAKE_EOS) {
+ if (dmalen1 < (devpriv->ai_n_realscanlen << 1)) {
+ /* uff, too short DMA buffer, disable EOS support! */
+ devpriv->ai_flags &= (~CMDF_WAKE_EOS);
+ dev_info(dev->class_dev,
+ "WAR: DMA1 buf too short, can't support CMDF_WAKE_EOS (%d<%d)\n",
+ dmalen1, devpriv->ai_n_realscanlen << 1);
+ } else {
+ /* short second DMA buffer to one scan */
+ dmalen1 = devpriv->ai_n_realscanlen << 1;
+ if (dmalen1 < 4) {
+ dev_info(dev->class_dev,
+ "ERR: DMA1 buf len bug? (%d<4)\n",
+ dmalen1);
+ dmalen1 = 4;
+ }
+ }
+ }
+
+ /* transfer without CMDF_WAKE_EOS */
+ if (!(devpriv->ai_flags & CMDF_WAKE_EOS)) {
+ /* if it's possible then align DMA buffers to length of scan */
+ i = dmalen0;
+ dmalen0 =
+ (dmalen0 / (devpriv->ai_n_realscanlen << 1)) *
+ (devpriv->ai_n_realscanlen << 1);
+ dmalen0 &= ~3L;
+ if (!dmalen0)
+ dmalen0 = i; /* uff. very long scan? */
+ i = dmalen1;
+ dmalen1 =
+ (dmalen1 / (devpriv->ai_n_realscanlen << 1)) *
+ (devpriv->ai_n_realscanlen << 1);
+ dmalen1 &= ~3L;
+ if (!dmalen1)
+ dmalen1 = i; /* uff. very long scan? */
+ /*
+ * if measure isn't neverending then test, if it fits whole
+ * into one or two DMA buffers
+ */
+ if (!devpriv->ai_neverending) {
+ /* fits whole measure into one DMA buffer? */
+ if (dmalen0 >
+ ((devpriv->ai_n_realscanlen << 1) *
+ cmd->stop_arg)) {
+ dmalen0 =
+ (devpriv->ai_n_realscanlen << 1) *
+ cmd->stop_arg;
+ dmalen0 &= ~3L;
+ } else { /*
+ * fits whole measure into
+ * two DMA buffer?
+ */
+ if (dmalen1 >
+ ((devpriv->ai_n_realscanlen << 1) *
+ cmd->stop_arg - dmalen0))
+ dmalen1 =
+ (devpriv->ai_n_realscanlen << 1) *
+ cmd->stop_arg - dmalen0;
+ dmalen1 &= ~3L;
+ }
+ }
+ }
+
+ /* these DMA buffer size will be used */
+ devpriv->dma_actbuf = 0;
+ dmabuf0->use_size = dmalen0;
+ dmabuf1->use_size = dmalen1;
+
+ pci9118_amcc_dma_ena(dev, false);
+ pci9118_amcc_setup_dma(dev, 0);
+ /* init DMA transfer */
+ outl(0x00000000 | AINT_WRITE_COMPL,
+ devpriv->iobase_a + AMCC_OP_REG_INTCSR);
+/* outl(0x02000000|AINT_WRITE_COMPL, devpriv->iobase_a+AMCC_OP_REG_INTCSR); */
+ pci9118_amcc_dma_ena(dev, true);
+ outl(inl(devpriv->iobase_a + AMCC_OP_REG_INTCSR) | EN_A2P_TRANSFERS,
+ devpriv->iobase_a + AMCC_OP_REG_INTCSR);
+ /* allow bus mastering */
+
+ return 0;
+}
+
+static int pci9118_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
+{
+ struct pci9118_private *devpriv = dev->private;
+ struct comedi_8254 *pacer = dev->pacer;
+ struct comedi_cmd *cmd = &s->async->cmd;
+ unsigned int addchans = 0;
+
+ devpriv->ai12_startstop = 0;
+ devpriv->ai_flags = cmd->flags;
+ devpriv->ai_add_front = 0;
+ devpriv->ai_add_back = 0;
+
+ /* prepare for start/stop conditions */
+ if (cmd->start_src == TRIG_EXT)
+ devpriv->ai12_startstop |= START_AI_EXT;
+ if (cmd->stop_src == TRIG_EXT) {
+ devpriv->ai_neverending = 1;
+ devpriv->ai12_startstop |= STOP_AI_EXT;
+ }
+ if (cmd->stop_src == TRIG_NONE)
+ devpriv->ai_neverending = 1;
+ if (cmd->stop_src == TRIG_COUNT)
+ devpriv->ai_neverending = 0;
+
+ /*
+ * use additional sample at end of every scan
+ * to satisty DMA 32 bit transfer?
+ */
+ devpriv->ai_add_front = 0;
+ devpriv->ai_add_back = 0;
+ if (devpriv->master) {
+ devpriv->usedma = 1;
+ if ((cmd->flags & CMDF_WAKE_EOS) &&
+ (cmd->scan_end_arg == 1)) {
+ if (cmd->convert_src == TRIG_NOW)
+ devpriv->ai_add_back = 1;
+ if (cmd->convert_src == TRIG_TIMER) {
+ devpriv->usedma = 0;
+ /*
+ * use INT transfer if scanlist
+ * have only one channel
+ */
+ }
+ }
+ if ((cmd->flags & CMDF_WAKE_EOS) &&
+ (cmd->scan_end_arg & 1) &&
+ (cmd->scan_end_arg > 1)) {
+ if (cmd->scan_begin_src == TRIG_FOLLOW) {
+ devpriv->usedma = 0;
+ /*
+ * XXX maybe can be corrected to use 16 bit DMA
+ */
+ } else { /*
+ * well, we must insert one sample
+ * to end of EOS to meet 32 bit transfer
+ */
+ devpriv->ai_add_back = 1;
+ }
+ }
+ } else { /* interrupt transfer don't need any correction */
+ devpriv->usedma = 0;
+ }
+
+ /*
+ * we need software S&H signal?
+ * It adds two samples before every scan as minimum
+ */
+ if (cmd->convert_src == TRIG_NOW && devpriv->softsshdelay) {
+ devpriv->ai_add_front = 2;
+ if ((devpriv->usedma == 1) && (devpriv->ai_add_back == 1)) {
+ /* move it to front */
+ devpriv->ai_add_front++;
+ devpriv->ai_add_back = 0;
+ }
+ if (cmd->convert_arg < devpriv->ai_ns_min)
+ cmd->convert_arg = devpriv->ai_ns_min;
+ addchans = devpriv->softsshdelay / cmd->convert_arg;
+ if (devpriv->softsshdelay % cmd->convert_arg)
+ addchans++;
+ if (addchans > (devpriv->ai_add_front - 1)) {
+ /* uff, still short */
+ devpriv->ai_add_front = addchans + 1;
+ if (devpriv->usedma == 1)
+ if ((devpriv->ai_add_front +
+ cmd->chanlist_len +
+ devpriv->ai_add_back) & 1)
+ devpriv->ai_add_front++;
+ /* round up to 32 bit */
+ }
+ }
+ /* well, we now know what must be all added */
+ devpriv->ai_n_realscanlen = /*
+ * what we must take from card in real
+ * to have cmd->scan_end_arg on output?
+ */
+ (devpriv->ai_add_front + cmd->chanlist_len +
+ devpriv->ai_add_back) * (cmd->scan_end_arg /
+ cmd->chanlist_len);
+
+ /* check and setup channel list */
+ if (!check_channel_list(dev, s, cmd->chanlist_len,
+ cmd->chanlist, devpriv->ai_add_front,
+ devpriv->ai_add_back))
+ return -EINVAL;
+
+ /*
+ * Configure analog input and load the chanlist.
+ * The acqusition control bits are enabled later.
+ */
+ pci9118_set_chanlist(dev, s, cmd->chanlist_len, cmd->chanlist,
+ devpriv->ai_add_front, devpriv->ai_add_back);
+
+ /* Determine acqusition mode and calculate timing */
+ devpriv->ai_do = 0;
+ if (cmd->scan_begin_src != TRIG_TIMER &&
+ cmd->convert_src == TRIG_TIMER) {
+ /* cascaded timers 1 and 2 are used for convert timing */
+ if (cmd->scan_begin_src == TRIG_EXT)
+ devpriv->ai_do = 4;
+ else
+ devpriv->ai_do = 1;
+
+ comedi_8254_cascade_ns_to_timer(pacer, &cmd->convert_arg,
+ devpriv->ai_flags &
+ CMDF_ROUND_NEAREST);
+ comedi_8254_update_divisors(pacer);
+
+ devpriv->ai_ctrl |= PCI9118_AI_CTRL_TMRTR;
+
+ if (!devpriv->usedma) {
+ devpriv->ai_ctrl |= PCI9118_AI_CTRL_INT;
+ devpriv->int_ctrl |= PCI9118_INT_CTRL_TIMER;
+ }
+
+ if (cmd->scan_begin_src == TRIG_EXT) {
+ struct pci9118_dmabuf *dmabuf = &devpriv->dmabuf[0];
+
+ devpriv->ai_cfg |= PCI9118_AI_CFG_AM;
+ outl(devpriv->ai_cfg, dev->iobase + PCI9118_AI_CFG_REG);
+ comedi_8254_load(pacer, 0, dmabuf->hw >> 1,
+ I8254_MODE0 | I8254_BINARY);
+ devpriv->ai_cfg |= PCI9118_AI_CFG_START;
+ }
+ }
+
+ if (cmd->scan_begin_src == TRIG_TIMER &&
+ cmd->convert_src != TRIG_EXT) {
+ if (!devpriv->usedma) {
+ dev_err(dev->class_dev,
+ "cmd->scan_begin_src=TRIG_TIMER works only with bus mastering!\n");
+ return -EIO;
+ }
+
+ /* double timed action */
+ devpriv->ai_do = 2;
+
+ pci9118_calc_divisors(dev, s,
+ &cmd->scan_begin_arg, &cmd->convert_arg,
+ devpriv->ai_flags,
+ devpriv->ai_n_realscanlen,
+ &pacer->divisor1,
+ &pacer->divisor2,
+ devpriv->ai_add_front);
+
+ devpriv->ai_ctrl |= PCI9118_AI_CTRL_TMRTR;
+ devpriv->ai_cfg |= PCI9118_AI_CFG_BM | PCI9118_AI_CFG_BS;
+ if (cmd->convert_src == TRIG_NOW && !devpriv->softsshdelay)
+ devpriv->ai_cfg |= PCI9118_AI_CFG_BSSH;
+ outl(devpriv->ai_n_realscanlen,
+ dev->iobase + PCI9118_AI_BURST_NUM_REG);
+ }
+
+ if (cmd->scan_begin_src == TRIG_FOLLOW &&
+ cmd->convert_src == TRIG_EXT) {
+ /* external trigger conversion */
+ devpriv->ai_do = 3;
+
+ devpriv->ai_ctrl |= PCI9118_AI_CTRL_EXTM;
+ }
+
+ if (devpriv->ai_do == 0) {
+ dev_err(dev->class_dev,
+ "Unable to determine acqusition mode! BUG in (*do_cmdtest)?\n");
+ return -EINVAL;
+ }
+
+ if (devpriv->usedma)
+ devpriv->ai_ctrl |= PCI9118_AI_CTRL_DMA;
+
+ /* set default config (disable burst and triggers) */
+ devpriv->ai_cfg = PCI9118_AI_CFG_PDTRG | PCI9118_AI_CFG_PETRG;
+ outl(devpriv->ai_cfg, dev->iobase + PCI9118_AI_CFG_REG);
+ udelay(1);
+ pci9118_ai_reset_fifo(dev);
+
+ /* clear A/D and INT status registers */
+ inl(dev->iobase + PCI9118_AI_STATUS_REG);
+ inl(dev->iobase + PCI9118_INT_CTRL_REG);
+
+ devpriv->ai_act_dmapos = 0;
+
+ if (devpriv->usedma) {
+ Compute_and_setup_dma(dev, s);
+
+ outl(0x02000000 | AINT_WRITE_COMPL,
+ devpriv->iobase_a + AMCC_OP_REG_INTCSR);
+ } else {
+ pci9118_amcc_int_ena(dev, true);
+ }
+
+ /* start async command now or wait for internal trigger */
+ if (cmd->start_src == TRIG_NOW)
+ pci9118_ai_cmd_start(dev);
+ else if (cmd->start_src == TRIG_INT)
+ s->async->inttrig = pci9118_ai_inttrig;
+
+ /* enable external trigger for command start/stop */
+ if (cmd->start_src == TRIG_EXT || cmd->stop_src == TRIG_EXT)
+ pci9118_exttrg_enable(dev, true);
+
+ return 0;
+}
+
+static int pci9118_ai_cmdtest(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_cmd *cmd)
+{
+ struct pci9118_private *devpriv = dev->private;
+ int err = 0;
+ unsigned int flags;
+ unsigned int arg;
+
+ /* Step 1 : check if triggers are trivially valid */
+
+ err |= comedi_check_trigger_src(&cmd->start_src,
+ TRIG_NOW | TRIG_EXT | TRIG_INT);
+
+ flags = TRIG_FOLLOW;
+ if (devpriv->master)
+ flags |= TRIG_TIMER | TRIG_EXT;
+ err |= comedi_check_trigger_src(&cmd->scan_begin_src, flags);
+
+ flags = TRIG_TIMER | TRIG_EXT;
+ if (devpriv->master)
+ flags |= TRIG_NOW;
+ err |= comedi_check_trigger_src(&cmd->convert_src, flags);
+
+ err |= comedi_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
+ err |= comedi_check_trigger_src(&cmd->stop_src,
+ TRIG_COUNT | TRIG_NONE | TRIG_EXT);
+
+ if (err)
+ return 1;
+
+ /* Step 2a : make sure trigger sources are unique */
+
+ err |= comedi_check_trigger_is_unique(cmd->start_src);
+ err |= comedi_check_trigger_is_unique(cmd->scan_begin_src);
+ err |= comedi_check_trigger_is_unique(cmd->convert_src);
+ err |= comedi_check_trigger_is_unique(cmd->stop_src);
+
+ /* Step 2b : and mutually compatible */
+
+ if (cmd->start_src == TRIG_EXT && cmd->scan_begin_src == TRIG_EXT)
+ err |= -EINVAL;
+
+ if (cmd->start_src == TRIG_INT && cmd->scan_begin_src == TRIG_INT)
+ err |= -EINVAL;
+
+ if ((cmd->scan_begin_src & (TRIG_TIMER | TRIG_EXT)) &&
+ (!(cmd->convert_src & (TRIG_TIMER | TRIG_NOW))))
+ err |= -EINVAL;
+
+ if ((cmd->scan_begin_src == TRIG_FOLLOW) &&
+ (!(cmd->convert_src & (TRIG_TIMER | TRIG_EXT))))
+ err |= -EINVAL;
+
+ if (cmd->stop_src == TRIG_EXT && cmd->scan_begin_src == TRIG_EXT)
+ err |= -EINVAL;
+
+ if (err)
+ return 2;
+
+ /* Step 3: check if arguments are trivially valid */
+
+ switch (cmd->start_src) {
+ case TRIG_NOW:
+ case TRIG_EXT:
+ err |= comedi_check_trigger_arg_is(&cmd->start_arg, 0);
+ break;
+ case TRIG_INT:
+ /* start_arg is the internal trigger (any value) */
+ break;
+ }
+
+ if (cmd->scan_begin_src & (TRIG_FOLLOW | TRIG_EXT))
+ err |= comedi_check_trigger_arg_is(&cmd->scan_begin_arg, 0);
+
+ if ((cmd->scan_begin_src == TRIG_TIMER) &&
+ (cmd->convert_src == TRIG_TIMER) && (cmd->scan_end_arg == 1)) {
+ cmd->scan_begin_src = TRIG_FOLLOW;
+ cmd->convert_arg = cmd->scan_begin_arg;
+ cmd->scan_begin_arg = 0;
+ }
+
+ if (cmd->scan_begin_src == TRIG_TIMER) {
+ err |= comedi_check_trigger_arg_min(&cmd->scan_begin_arg,
+ devpriv->ai_ns_min);
+ }
+
+ if (cmd->scan_begin_src == TRIG_EXT) {
+ if (cmd->scan_begin_arg) {
+ cmd->scan_begin_arg = 0;
+ err |= -EINVAL;
+ err |= comedi_check_trigger_arg_max(&cmd->scan_end_arg,
+ 65535);
+ }
+ }
+
+ if (cmd->convert_src & (TRIG_TIMER | TRIG_NOW)) {
+ err |= comedi_check_trigger_arg_min(&cmd->convert_arg,
+ devpriv->ai_ns_min);
+ }
+
+ if (cmd->convert_src == TRIG_EXT)
+ err |= comedi_check_trigger_arg_is(&cmd->convert_arg, 0);
+
+ if (cmd->stop_src == TRIG_COUNT)
+ err |= comedi_check_trigger_arg_min(&cmd->stop_arg, 1);
+ else /* TRIG_NONE */
+ err |= comedi_check_trigger_arg_is(&cmd->stop_arg, 0);
+
+ err |= comedi_check_trigger_arg_min(&cmd->chanlist_len, 1);
+
+ err |= comedi_check_trigger_arg_min(&cmd->scan_end_arg,
+ cmd->chanlist_len);
+
+ if ((cmd->scan_end_arg % cmd->chanlist_len)) {
+ cmd->scan_end_arg =
+ cmd->chanlist_len * (cmd->scan_end_arg / cmd->chanlist_len);
+ err |= -EINVAL;
+ }
+
+ if (err)
+ return 3;
+
+ /* step 4: fix up any arguments */
+
+ if (cmd->scan_begin_src == TRIG_TIMER) {
+ arg = cmd->scan_begin_arg;
+ comedi_8254_cascade_ns_to_timer(dev->pacer, &arg, cmd->flags);
+ err |= comedi_check_trigger_arg_is(&cmd->scan_begin_arg, arg);
+ }
+
+ if (cmd->convert_src & (TRIG_TIMER | TRIG_NOW)) {
+ arg = cmd->convert_arg;
+ comedi_8254_cascade_ns_to_timer(dev->pacer, &arg, cmd->flags);
+ err |= comedi_check_trigger_arg_is(&cmd->convert_arg, arg);
+
+ if (cmd->scan_begin_src == TRIG_TIMER &&
+ cmd->convert_src == TRIG_NOW) {
+ if (cmd->convert_arg == 0) {
+ arg = devpriv->ai_ns_min *
+ (cmd->scan_end_arg + 2);
+ } else {
+ arg = cmd->convert_arg * cmd->chanlist_len;
+ }
+ err |= comedi_check_trigger_arg_min(&cmd->
+ scan_begin_arg,
+ arg);
+ }
+ }
+
+ if (err)
+ return 4;
+
+ if (cmd->chanlist)
+ if (!check_channel_list(dev, s, cmd->chanlist_len,
+ cmd->chanlist, 0, 0))
+ return 5; /* incorrect channels list */
+
+ return 0;
+}
+
+static int pci9118_ai_eoc(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned long context)
+{
+ unsigned int status;
+
+ status = inl(dev->iobase + PCI9118_AI_STATUS_REG);
+ if (status & PCI9118_AI_STATUS_ADRDY)
+ return 0;
+ return -EBUSY;
+}
+
+static void pci9118_ai_start_conv(struct comedi_device *dev)
+{
+ /* writing any value triggers an A/D conversion */
+ outl(0, dev->iobase + PCI9118_SOFTTRG_REG);
+}
+
+static int pci9118_ai_insn_read(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ struct pci9118_private *devpriv = dev->private;
+ unsigned int val;
+ int ret;
+ int i;
+
+ /*
+ * Configure analog input based on the chanspec.
+ * Acqusition is software controlled without interrupts.
+ */
+ pci9118_set_chanlist(dev, s, 1, &insn->chanspec, 0, 0);
+
+ /* set default config (disable burst and triggers) */
+ devpriv->ai_cfg = PCI9118_AI_CFG_PDTRG | PCI9118_AI_CFG_PETRG;
+ outl(devpriv->ai_cfg, dev->iobase + PCI9118_AI_CFG_REG);
+
+ pci9118_ai_reset_fifo(dev);
+
+ for (i = 0; i < insn->n; i++) {
+ pci9118_ai_start_conv(dev);
+
+ ret = comedi_timeout(dev, s, insn, pci9118_ai_eoc, 0);
+ if (ret)
+ return ret;
+
+ val = inl(dev->iobase + PCI9118_AI_FIFO_REG);
+ if (s->maxdata == 0xffff)
+ data[i] = (val & 0xffff) ^ 0x8000;
+ else
+ data[i] = (val >> 4) & 0xfff;
+ }
+
+ return insn->n;
+}
+
+static int pci9118_ao_insn_write(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ unsigned int chan = CR_CHAN(insn->chanspec);
+ unsigned int val = s->readback[chan];
+ int i;
+
+ for (i = 0; i < insn->n; i++) {
+ val = data[i];
+ outl(val, dev->iobase + PCI9118_AO_REG(chan));
+ }
+ s->readback[chan] = val;
+
+ return insn->n;
+}
+
+static int pci9118_di_insn_bits(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ /*
+ * The digital inputs and outputs share the read register.
+ * bits [7:4] are the digital outputs
+ * bits [3:0] are the digital inputs
+ */
+ data[1] = inl(dev->iobase + PCI9118_DIO_REG) & 0xf;
+
+ return insn->n;
+}
+
+static int pci9118_do_insn_bits(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ /*
+ * The digital outputs are set with the same register that
+ * the digital inputs and outputs are read from. But the
+ * outputs are set with bits [3:0] so we can simply write
+ * the s->state to set them.
+ */
+ if (comedi_dio_update_state(s, data))
+ outl(s->state, dev->iobase + PCI9118_DIO_REG);
+
+ data[1] = s->state;
+
+ return insn->n;
+}
+
+static void pci9118_reset(struct comedi_device *dev)
+{
+ /* reset analog input subsystem */
+ outl(0, dev->iobase + PCI9118_INT_CTRL_REG);
+ outl(0, dev->iobase + PCI9118_AI_CTRL_REG);
+ outl(0, dev->iobase + PCI9118_AI_CFG_REG);
+ pci9118_ai_reset_fifo(dev);
+
+ /* clear any pending interrupts and status */
+ inl(dev->iobase + PCI9118_INT_CTRL_REG);
+ inl(dev->iobase + PCI9118_AI_STATUS_REG);
+
+ /* reset DMA and scan queue */
+ outl(0, dev->iobase + PCI9118_AI_BURST_NUM_REG);
+ outl(1, dev->iobase + PCI9118_AI_AUTOSCAN_MODE_REG);
+ outl(2, dev->iobase + PCI9118_AI_AUTOSCAN_MODE_REG);
+
+ /* reset analog outputs to 0V */
+ outl(2047, dev->iobase + PCI9118_AO_REG(0));
+ outl(2047, dev->iobase + PCI9118_AO_REG(1));
+}
+
+static struct pci_dev *pci9118_find_pci(struct comedi_device *dev,
+ struct comedi_devconfig *it)
+{
+ struct pci_dev *pcidev = NULL;
+ int bus = it->options[0];
+ int slot = it->options[1];
+
+ for_each_pci_dev(pcidev) {
+ if (pcidev->vendor != PCI_VENDOR_ID_AMCC)
+ continue;
+ if (pcidev->device != 0x80d9)
+ continue;
+ if (bus || slot) {
+ /* requested particular bus/slot */
+ if (pcidev->bus->number != bus ||
+ PCI_SLOT(pcidev->devfn) != slot)
+ continue;
+ }
+ return pcidev;
+ }
+ dev_err(dev->class_dev,
+ "no supported board found! (req. bus/slot : %d/%d)\n",
+ bus, slot);
+ return NULL;
+}
+
+static void pci9118_alloc_dma(struct comedi_device *dev)
+{
+ struct pci9118_private *devpriv = dev->private;
+ struct pci9118_dmabuf *dmabuf;
+ int order;
+ int i;
+
+ for (i = 0; i < 2; i++) {
+ dmabuf = &devpriv->dmabuf[i];
+ for (order = 2; order >= 0; order--) {
+ dmabuf->virt =
+ dma_alloc_coherent(dev->hw_dev, PAGE_SIZE << order,
+ &dmabuf->hw, GFP_KERNEL);
+ if (dmabuf->virt)
+ break;
+ }
+ if (!dmabuf->virt)
+ break;
+ dmabuf->size = PAGE_SIZE << order;
+
+ if (i == 0)
+ devpriv->master = 1;
+ if (i == 1)
+ devpriv->dma_doublebuf = 1;
+ }
+}
+
+static void pci9118_free_dma(struct comedi_device *dev)
+{
+ struct pci9118_private *devpriv = dev->private;
+ struct pci9118_dmabuf *dmabuf;
+ int i;
+
+ if (!devpriv)
+ return;
+
+ for (i = 0; i < 2; i++) {
+ dmabuf = &devpriv->dmabuf[i];
+ if (dmabuf->virt) {
+ dma_free_coherent(dev->hw_dev, dmabuf->size,
+ dmabuf->virt, dmabuf->hw);
+ }
+ }
+}
+
+static int pci9118_common_attach(struct comedi_device *dev,
+ int ext_mux, int softsshdelay)
+{
+ const struct pci9118_boardinfo *board = dev->board_ptr;
+ struct pci_dev *pcidev = comedi_to_pci_dev(dev);
+ struct pci9118_private *devpriv;
+ struct comedi_subdevice *s;
+ int ret;
+ int i;
+ u16 u16w;
+
+ devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
+ if (!devpriv)
+ return -ENOMEM;
+
+ ret = comedi_pci_enable(dev);
+ if (ret)
+ return ret;
+ pci_set_master(pcidev);
+
+ devpriv->iobase_a = pci_resource_start(pcidev, 0);
+ dev->iobase = pci_resource_start(pcidev, 2);
+
+ dev->pacer = comedi_8254_init(dev->iobase + PCI9118_TIMER_BASE,
+ I8254_OSC_BASE_4MHZ, I8254_IO32, 0);
+ if (!dev->pacer)
+ return -ENOMEM;
+
+ pci9118_reset(dev);
+
+ if (pcidev->irq) {
+ ret = request_irq(pcidev->irq, pci9118_interrupt, IRQF_SHARED,
+ dev->board_name, dev);
+ if (ret == 0) {
+ dev->irq = pcidev->irq;
+
+ pci9118_alloc_dma(dev);
+ }
+ }
+
+ if (ext_mux > 0) {
+ if (ext_mux > 256)
+ ext_mux = 256; /* max 256 channels! */
+ if (softsshdelay > 0)
+ if (ext_mux > 128)
+ ext_mux = 128;
+ devpriv->usemux = 1;
+ } else {
+ devpriv->usemux = 0;
+ }
+
+ if (softsshdelay < 0) {
+ /* select sample&hold signal polarity */
+ devpriv->softsshdelay = -softsshdelay;
+ devpriv->softsshsample = 0x80;
+ devpriv->softsshhold = 0x00;
+ } else {
+ devpriv->softsshdelay = softsshdelay;
+ devpriv->softsshsample = 0x00;
+ devpriv->softsshhold = 0x80;
+ }
+
+ pci_read_config_word(pcidev, PCI_COMMAND, &u16w);
+ pci_write_config_word(pcidev, PCI_COMMAND, u16w | 64);
+ /* Enable parity check for parity error */
+
+ ret = comedi_alloc_subdevices(dev, 4);
+ if (ret)
+ return ret;
+
+ /* Analog Input subdevice */
+ s = &dev->subdevices[0];
+ s->type = COMEDI_SUBD_AI;
+ s->subdev_flags = SDF_READABLE | SDF_COMMON | SDF_GROUND | SDF_DIFF;
+ s->n_chan = (devpriv->usemux) ? ext_mux : 16;
+ s->maxdata = board->ai_is_16bit ? 0xffff : 0x0fff;
+ s->range_table = board->is_hg ? &pci9118hg_ai_range
+ : &pci9118_ai_range;
+ s->insn_read = pci9118_ai_insn_read;
+ if (dev->irq) {
+ dev->read_subdev = s;
+ s->subdev_flags |= SDF_CMD_READ;
+ s->len_chanlist = PCI9118_CHANLEN;
+ s->do_cmdtest = pci9118_ai_cmdtest;
+ s->do_cmd = pci9118_ai_cmd;
+ s->cancel = pci9118_ai_cancel;
+ s->munge = pci9118_ai_munge;
+ }
+
+ if (s->maxdata == 0xffff) {
+ /*
+ * 16-bit samples are from an ADS7805 A/D converter.
+ * Minimum sampling rate is 10us.
+ */
+ devpriv->ai_ns_min = 10000;
+ } else {
+ /*
+ * 12-bit samples are from an ADS7800 A/D converter.
+ * Minimum sampling rate is 3us.
+ */
+ devpriv->ai_ns_min = 3000;
+ }
+
+ /* Analog Output subdevice */
+ s = &dev->subdevices[1];
+ s->type = COMEDI_SUBD_AO;
+ s->subdev_flags = SDF_WRITABLE | SDF_GROUND | SDF_COMMON;
+ s->n_chan = 2;
+ s->maxdata = 0x0fff;
+ s->range_table = &range_bipolar10;
+ s->insn_write = pci9118_ao_insn_write;
+
+ ret = comedi_alloc_subdev_readback(s);
+ if (ret)
+ return ret;
+
+ /* the analog outputs were reset to 0V, make the readback match */
+ for (i = 0; i < s->n_chan; i++)
+ s->readback[i] = 2047;
+
+ /* Digital Input subdevice */
+ s = &dev->subdevices[2];
+ s->type = COMEDI_SUBD_DI;
+ s->subdev_flags = SDF_READABLE;
+ s->n_chan = 4;
+ s->maxdata = 1;
+ s->range_table = &range_digital;
+ s->insn_bits = pci9118_di_insn_bits;
+
+ /* Digital Output subdevice */
+ s = &dev->subdevices[3];
+ s->type = COMEDI_SUBD_DO;
+ s->subdev_flags = SDF_WRITABLE;
+ s->n_chan = 4;
+ s->maxdata = 1;
+ s->range_table = &range_digital;
+ s->insn_bits = pci9118_do_insn_bits;
+
+ /* get the current state of the digital outputs */
+ s->state = inl(dev->iobase + PCI9118_DIO_REG) >> 4;
+
+ return 0;
+}
+
+static int pci9118_attach(struct comedi_device *dev,
+ struct comedi_devconfig *it)
+{
+ struct pci_dev *pcidev;
+ int ext_mux, softsshdelay;
+
+ ext_mux = it->options[2];
+ softsshdelay = it->options[4];
+
+ pcidev = pci9118_find_pci(dev, it);
+ if (!pcidev)
+ return -EIO;
+ comedi_set_hw_dev(dev, &pcidev->dev);
+
+ return pci9118_common_attach(dev, ext_mux, softsshdelay);
+}
+
+static int pci9118_auto_attach(struct comedi_device *dev,
+ unsigned long context)
+{
+ struct pci_dev *pcidev = comedi_to_pci_dev(dev);
+ const struct pci9118_boardinfo *board = NULL;
+
+ if (context < ARRAY_SIZE(pci9118_boards))
+ board = &pci9118_boards[context];
+ if (!board)
+ return -ENODEV;
+ dev->board_ptr = board;
+ dev->board_name = board->name;
+
+ /*
+ * Need to 'get' the PCI device to match the 'put' in pci9118_detach().
+ * (The 'put' also matches the implicit 'get' by pci9118_find_pci().)
+ */
+ pci_dev_get(pcidev);
+ /* no external mux, no sample-hold delay */
+ return pci9118_common_attach(dev, 0, 0);
+}
+
+static void pci9118_detach(struct comedi_device *dev)
+{
+ struct pci_dev *pcidev = comedi_to_pci_dev(dev);
+
+ if (dev->iobase)
+ pci9118_reset(dev);
+ comedi_pci_detach(dev);
+ pci9118_free_dma(dev);
+ if (pcidev)
+ pci_dev_put(pcidev);
+}
+
+static struct comedi_driver adl_pci9118_driver = {
+ .driver_name = "adl_pci9118",
+ .module = THIS_MODULE,
+ .attach = pci9118_attach,
+ .auto_attach = pci9118_auto_attach,
+ .detach = pci9118_detach,
+ .num_names = ARRAY_SIZE(pci9118_boards),
+ .board_name = &pci9118_boards[0].name,
+ .offset = sizeof(struct pci9118_boardinfo),
+};
+
+static int adl_pci9118_pci_probe(struct pci_dev *dev,
+ const struct pci_device_id *id)
+{
+ return comedi_pci_auto_config(dev, &adl_pci9118_driver,
+ id->driver_data);
+}
+
+/* FIXME: All the supported board types have the same device ID! */
+static const struct pci_device_id adl_pci9118_pci_table[] = {
+ { PCI_VDEVICE(AMCC, 0x80d9), BOARD_PCI9118DG },
+/* { PCI_VDEVICE(AMCC, 0x80d9), BOARD_PCI9118HG }, */
+/* { PCI_VDEVICE(AMCC, 0x80d9), BOARD_PCI9118HR }, */
+ { 0 }
+};
+MODULE_DEVICE_TABLE(pci, adl_pci9118_pci_table);
+
+static struct pci_driver adl_pci9118_pci_driver = {
+ .name = "adl_pci9118",
+ .id_table = adl_pci9118_pci_table,
+ .probe = adl_pci9118_pci_probe,
+ .remove = comedi_pci_auto_unconfig,
+};
+module_comedi_pci_driver(adl_pci9118_driver, adl_pci9118_pci_driver);
+
+MODULE_AUTHOR("Comedi http://www.comedi.org");
+MODULE_DESCRIPTION("Comedi low-level driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/comedi/drivers/adq12b.c b/drivers/staging/comedi/drivers/adq12b.c
new file mode 100644
index 000000000..bc5f97f50
--- /dev/null
+++ b/drivers/staging/comedi/drivers/adq12b.c
@@ -0,0 +1,268 @@
+/*
+ comedi/drivers/adq12b.c
+ driver for MicroAxial ADQ12-B data acquisition and control card
+
+ COMEDI - Linux Control and Measurement Device Interface
+ Copyright (C) 2000 David A. Schleef <ds@schleef.org>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+*/
+/*
+Driver: adq12b
+Description: driver for MicroAxial ADQ12-B data acquisition and control card
+Devices: [MicroAxial] ADQ12-B (adq12b)
+Author: jeremy theler <thelerg@ib.cnea.gov.ar>
+Updated: Thu, 21 Feb 2008 02:56:27 -0300
+Status: works
+
+Driver for the acquisition card ADQ12-B (without any add-on).
+
+ - Analog input is subdevice 0 (16 channels single-ended or 8 differential)
+ - Digital input is subdevice 1 (5 channels)
+ - Digital output is subdevice 1 (8 channels)
+ - The PACER is not supported in this version
+
+If you do not specify any options, they will default to
+
+ # comedi_config /dev/comedi0 adq12b 0x300,0,0
+
+ option 1: I/O base address. The following table is provided as a help
+ of the hardware jumpers.
+
+ address jumper JADR
+ 0x300 1 (factory default)
+ 0x320 2
+ 0x340 3
+ 0x360 4
+ 0x380 5
+ 0x3A0 6
+
+ option 2: unipolar/bipolar ADC selection: 0 -> bipolar, 1 -> unipolar
+
+ selection comedi_config option JUB
+ bipolar 0 2-3 (factory default)
+ unipolar 1 1-2
+
+ option 3: single-ended/differential AI selection: 0 -> SE, 1 -> differential
+
+ selection comedi_config option JCHA JCHB
+ single-ended 0 1-2 1-2 (factory default)
+ differential 1 2-3 2-3
+
+ written by jeremy theler <thelerg@ib.cnea.gov.ar>
+
+ instituto balseiro
+ commission nacional de energia atomica
+ universidad nacional de cuyo
+ argentina
+
+ 21-feb-2008
+ + changed supported devices string (missused the [] and ())
+
+ 13-oct-2007
+ + first try
+*/
+
+#include <linux/module.h>
+#include <linux/delay.h>
+
+#include "../comedidev.h"
+
+/* address scheme (page 2.17 of the manual) */
+#define ADQ12B_CTREG 0x00
+#define ADQ12B_CTREG_MSKP (1 << 7) /* enable pacer interrupt */
+#define ADQ12B_CTREG_GTP (1 << 6) /* enable pacer */
+#define ADQ12B_CTREG_RANGE(x) ((x) << 4)
+#define ADQ12B_CTREG_CHAN(x) ((x) << 0)
+#define ADQ12B_STINR 0x00
+#define ADQ12B_STINR_OUT2 (1 << 7) /* timer 2 output state */
+#define ADQ12B_STINR_OUTP (1 << 6) /* pacer output state */
+#define ADQ12B_STINR_EOC (1 << 5) /* A/D end-of-conversion */
+#define ADQ12B_STINR_IN_MASK (0x1f << 0)
+#define ADQ12B_OUTBR 0x04
+#define ADQ12B_ADLOW 0x08
+#define ADQ12B_ADHIG 0x09
+#define ADQ12B_TIMER_BASE 0x0c
+
+/* available ranges through the PGA gains */
+static const struct comedi_lrange range_adq12b_ai_bipolar = {
+ 4, {
+ BIP_RANGE(5),
+ BIP_RANGE(2),
+ BIP_RANGE(1),
+ BIP_RANGE(0.5)
+ }
+};
+
+static const struct comedi_lrange range_adq12b_ai_unipolar = {
+ 4, {
+ UNI_RANGE(5),
+ UNI_RANGE(2),
+ UNI_RANGE(1),
+ UNI_RANGE(0.5)
+ }
+};
+
+struct adq12b_private {
+ unsigned int last_ctreg;
+};
+
+static int adq12b_ai_eoc(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned long context)
+{
+ unsigned char status;
+
+ status = inb(dev->iobase + ADQ12B_STINR);
+ if (status & ADQ12B_STINR_EOC)
+ return 0;
+ return -EBUSY;
+}
+
+static int adq12b_ai_insn_read(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ struct adq12b_private *devpriv = dev->private;
+ unsigned int chan = CR_CHAN(insn->chanspec);
+ unsigned int range = CR_RANGE(insn->chanspec);
+ unsigned int val;
+ int ret;
+ int i;
+
+ /* change channel and range only if it is different from the previous */
+ val = ADQ12B_CTREG_RANGE(range) | ADQ12B_CTREG_CHAN(chan);
+ if (val != devpriv->last_ctreg) {
+ outb(val, dev->iobase + ADQ12B_CTREG);
+ devpriv->last_ctreg = val;
+ udelay(50); /* wait for the mux to settle */
+ }
+
+ val = inb(dev->iobase + ADQ12B_ADLOW); /* trigger A/D */
+
+ for (i = 0; i < insn->n; i++) {
+ ret = comedi_timeout(dev, s, insn, adq12b_ai_eoc, 0);
+ if (ret)
+ return ret;
+
+ val = inb(dev->iobase + ADQ12B_ADHIG) << 8;
+ val |= inb(dev->iobase + ADQ12B_ADLOW); /* retriggers A/D */
+
+ data[i] = val;
+ }
+
+ return insn->n;
+}
+
+static int adq12b_di_insn_bits(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn, unsigned int *data)
+{
+ /* only bits 0-4 have information about digital inputs */
+ data[1] = (inb(dev->iobase + ADQ12B_STINR) & ADQ12B_STINR_IN_MASK);
+
+ return insn->n;
+}
+
+static int adq12b_do_insn_bits(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ unsigned int mask;
+ unsigned int chan;
+ unsigned int val;
+
+ mask = comedi_dio_update_state(s, data);
+ if (mask) {
+ for (chan = 0; chan < 8; chan++) {
+ if ((mask >> chan) & 0x01) {
+ val = (s->state >> chan) & 0x01;
+ outb((val << 3) | chan,
+ dev->iobase + ADQ12B_OUTBR);
+ }
+ }
+ }
+
+ data[1] = s->state;
+
+ return insn->n;
+}
+
+static int adq12b_attach(struct comedi_device *dev, struct comedi_devconfig *it)
+{
+ struct adq12b_private *devpriv;
+ struct comedi_subdevice *s;
+ int ret;
+
+ ret = comedi_request_region(dev, it->options[0], 0x10);
+ if (ret)
+ return ret;
+
+ devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
+ if (!devpriv)
+ return -ENOMEM;
+
+ devpriv->last_ctreg = -1; /* force ctreg update */
+
+ ret = comedi_alloc_subdevices(dev, 3);
+ if (ret)
+ return ret;
+
+ /* Analog Input subdevice */
+ s = &dev->subdevices[0];
+ s->type = COMEDI_SUBD_AI;
+ if (it->options[2]) {
+ s->subdev_flags = SDF_READABLE | SDF_DIFF;
+ s->n_chan = 8;
+ } else {
+ s->subdev_flags = SDF_READABLE | SDF_GROUND;
+ s->n_chan = 16;
+ }
+ s->maxdata = 0xfff;
+ s->range_table = it->options[1] ? &range_adq12b_ai_unipolar
+ : &range_adq12b_ai_bipolar;
+ s->insn_read = adq12b_ai_insn_read;
+
+ /* Digital Input subdevice */
+ s = &dev->subdevices[1];
+ s->type = COMEDI_SUBD_DI;
+ s->subdev_flags = SDF_READABLE;
+ s->n_chan = 5;
+ s->maxdata = 1;
+ s->range_table = &range_digital;
+ s->insn_bits = adq12b_di_insn_bits;
+
+ /* Digital Output subdevice */
+ s = &dev->subdevices[2];
+ s->type = COMEDI_SUBD_DO;
+ s->subdev_flags = SDF_WRITABLE;
+ s->n_chan = 8;
+ s->maxdata = 1;
+ s->range_table = &range_digital;
+ s->insn_bits = adq12b_do_insn_bits;
+
+ return 0;
+}
+
+static struct comedi_driver adq12b_driver = {
+ .driver_name = "adq12b",
+ .module = THIS_MODULE,
+ .attach = adq12b_attach,
+ .detach = comedi_legacy_detach,
+};
+module_comedi_driver(adq12b_driver);
+
+MODULE_AUTHOR("Comedi http://www.comedi.org");
+MODULE_DESCRIPTION("Comedi low-level driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/comedi/drivers/adv_pci1710.c b/drivers/staging/comedi/drivers/adv_pci1710.c
new file mode 100644
index 000000000..0c6aa964c
--- /dev/null
+++ b/drivers/staging/comedi/drivers/adv_pci1710.c
@@ -0,0 +1,1100 @@
+/*
+ * comedi/drivers/adv_pci1710.c
+ *
+ * Author: Michal Dobes <dobes@tesnet.cz>
+ *
+ * Thanks to ZhenGang Shang <ZhenGang.Shang@Advantech.com.cn>
+ * for testing and information.
+ *
+ * hardware driver for Advantech cards:
+ * card: PCI-1710, PCI-1710HG, PCI-1711, PCI-1713, PCI-1720, PCI-1731
+ * driver: pci1710, pci1710hg, pci1711, pci1713, pci1720, pci1731
+ *
+ * Options:
+ * [0] - PCI bus number - if bus number and slot number are 0,
+ * then driver search for first unused card
+ * [1] - PCI slot number
+ *
+*/
+/*
+Driver: adv_pci1710
+Description: Advantech PCI-1710, PCI-1710HG, PCI-1711, PCI-1713,
+ Advantech PCI-1720, PCI-1731
+Author: Michal Dobes <dobes@tesnet.cz>
+Devices: [Advantech] PCI-1710 (adv_pci1710), PCI-1710HG (pci1710hg),
+ PCI-1711 (adv_pci1710), PCI-1713, PCI-1720,
+ PCI-1731
+Status: works
+
+This driver supports AI, AO, DI and DO subdevices.
+AI subdevice supports cmd and insn interface,
+other subdevices support only insn interface.
+
+The PCI-1710 and PCI-1710HG have the same PCI device ID, so the
+driver cannot distinguish between them, as would be normal for a
+PCI driver.
+
+Configuration options:
+ [0] - PCI bus of device (optional)
+ [1] - PCI slot of device (optional)
+ If bus/slot is not specified, the first available PCI
+ device will be used.
+*/
+
+#include <linux/module.h>
+#include <linux/interrupt.h>
+
+#include "../comedi_pci.h"
+
+#include "comedi_8254.h"
+#include "amcc_s5933.h"
+
+#define PCI171x_AD_DATA 0 /* R: A/D data */
+#define PCI171x_SOFTTRG 0 /* W: soft trigger for A/D */
+#define PCI171x_RANGE 2 /* W: A/D gain/range register */
+#define PCI171x_MUX 4 /* W: A/D multiplexor control */
+#define PCI171x_STATUS 6 /* R: status register */
+#define PCI171x_CONTROL 6 /* W: control register */
+#define PCI171x_CLRINT 8 /* W: clear interrupts request */
+#define PCI171x_CLRFIFO 9 /* W: clear FIFO */
+#define PCI171x_DA1 10 /* W: D/A register */
+#define PCI171x_DA2 12 /* W: D/A register */
+#define PCI171x_DAREF 14 /* W: D/A reference control */
+#define PCI171x_DI 16 /* R: digi inputs */
+#define PCI171x_DO 16 /* R: digi inputs */
+
+#define PCI171X_TIMER_BASE 0x18
+
+/* upper bits from status register (PCI171x_STATUS) (lower is same with control
+ * reg) */
+#define Status_FE 0x0100 /* 1=FIFO is empty */
+#define Status_FH 0x0200 /* 1=FIFO is half full */
+#define Status_FF 0x0400 /* 1=FIFO is full, fatal error */
+#define Status_IRQ 0x0800 /* 1=IRQ occurred */
+/* bits from control register (PCI171x_CONTROL) */
+#define Control_CNT0 0x0040 /* 1=CNT0 have external source,
+ * 0=have internal 100kHz source */
+#define Control_ONEFH 0x0020 /* 1=IRQ on FIFO is half full, 0=every sample */
+#define Control_IRQEN 0x0010 /* 1=enable IRQ */
+#define Control_GATE 0x0008 /* 1=enable external trigger GATE (8254?) */
+#define Control_EXT 0x0004 /* 1=external trigger source */
+#define Control_PACER 0x0002 /* 1=enable internal 8254 trigger source */
+#define Control_SW 0x0001 /* 1=enable software trigger source */
+
+#define PCI1720_DA0 0 /* W: D/A register 0 */
+#define PCI1720_DA1 2 /* W: D/A register 1 */
+#define PCI1720_DA2 4 /* W: D/A register 2 */
+#define PCI1720_DA3 6 /* W: D/A register 3 */
+#define PCI1720_RANGE 8 /* R/W: D/A range register */
+#define PCI1720_SYNCOUT 9 /* W: D/A synchronized output register */
+#define PCI1720_SYNCONT 15 /* R/W: D/A synchronized control */
+
+/* D/A synchronized control (PCI1720_SYNCONT) */
+#define Syncont_SC0 1 /* set synchronous output mode */
+
+static const struct comedi_lrange range_pci1710_3 = {
+ 9, {
+ BIP_RANGE(5),
+ BIP_RANGE(2.5),
+ BIP_RANGE(1.25),
+ BIP_RANGE(0.625),
+ BIP_RANGE(10),
+ UNI_RANGE(10),
+ UNI_RANGE(5),
+ UNI_RANGE(2.5),
+ UNI_RANGE(1.25)
+ }
+};
+
+static const char range_codes_pci1710_3[] = { 0x00, 0x01, 0x02, 0x03, 0x04,
+ 0x10, 0x11, 0x12, 0x13 };
+
+static const struct comedi_lrange range_pci1710hg = {
+ 12, {
+ BIP_RANGE(5),
+ BIP_RANGE(0.5),
+ BIP_RANGE(0.05),
+ BIP_RANGE(0.005),
+ BIP_RANGE(10),
+ BIP_RANGE(1),
+ BIP_RANGE(0.1),
+ BIP_RANGE(0.01),
+ UNI_RANGE(10),
+ UNI_RANGE(1),
+ UNI_RANGE(0.1),
+ UNI_RANGE(0.01)
+ }
+};
+
+static const char range_codes_pci1710hg[] = { 0x00, 0x01, 0x02, 0x03, 0x04,
+ 0x05, 0x06, 0x07, 0x10, 0x11,
+ 0x12, 0x13 };
+
+static const struct comedi_lrange range_pci17x1 = {
+ 5, {
+ BIP_RANGE(10),
+ BIP_RANGE(5),
+ BIP_RANGE(2.5),
+ BIP_RANGE(1.25),
+ BIP_RANGE(0.625)
+ }
+};
+
+static const char range_codes_pci17x1[] = { 0x00, 0x01, 0x02, 0x03, 0x04 };
+
+static const struct comedi_lrange pci1720_ao_range = {
+ 4, {
+ UNI_RANGE(5),
+ UNI_RANGE(10),
+ BIP_RANGE(5),
+ BIP_RANGE(10)
+ }
+};
+
+static const struct comedi_lrange pci171x_ao_range = {
+ 2, {
+ UNI_RANGE(5),
+ UNI_RANGE(10)
+ }
+};
+
+enum pci1710_boardid {
+ BOARD_PCI1710,
+ BOARD_PCI1710HG,
+ BOARD_PCI1711,
+ BOARD_PCI1713,
+ BOARD_PCI1720,
+ BOARD_PCI1731,
+};
+
+struct boardtype {
+ const char *name; /* board name */
+ int n_aichan; /* num of A/D chans */
+ const struct comedi_lrange *rangelist_ai; /* rangelist for A/D */
+ const char *rangecode_ai; /* range codes for programming */
+ unsigned int is_pci1713:1;
+ unsigned int is_pci1720:1;
+ unsigned int has_irq:1;
+ unsigned int has_large_fifo:1; /* 4K or 1K FIFO */
+ unsigned int has_diff_ai:1;
+ unsigned int has_ao:1;
+ unsigned int has_di_do:1;
+ unsigned int has_counter:1;
+};
+
+static const struct boardtype boardtypes[] = {
+ [BOARD_PCI1710] = {
+ .name = "pci1710",
+ .n_aichan = 16,
+ .rangelist_ai = &range_pci1710_3,
+ .rangecode_ai = range_codes_pci1710_3,
+ .has_irq = 1,
+ .has_large_fifo = 1,
+ .has_diff_ai = 1,
+ .has_ao = 1,
+ .has_di_do = 1,
+ .has_counter = 1,
+ },
+ [BOARD_PCI1710HG] = {
+ .name = "pci1710hg",
+ .n_aichan = 16,
+ .rangelist_ai = &range_pci1710hg,
+ .rangecode_ai = range_codes_pci1710hg,
+ .has_irq = 1,
+ .has_large_fifo = 1,
+ .has_diff_ai = 1,
+ .has_ao = 1,
+ .has_di_do = 1,
+ .has_counter = 1,
+ },
+ [BOARD_PCI1711] = {
+ .name = "pci1711",
+ .n_aichan = 16,
+ .rangelist_ai = &range_pci17x1,
+ .rangecode_ai = range_codes_pci17x1,
+ .has_irq = 1,
+ .has_ao = 1,
+ .has_di_do = 1,
+ .has_counter = 1,
+ },
+ [BOARD_PCI1713] = {
+ .name = "pci1713",
+ .n_aichan = 32,
+ .rangelist_ai = &range_pci1710_3,
+ .rangecode_ai = range_codes_pci1710_3,
+ .is_pci1713 = 1,
+ .has_irq = 1,
+ .has_large_fifo = 1,
+ .has_diff_ai = 1,
+ },
+ [BOARD_PCI1720] = {
+ .name = "pci1720",
+ .is_pci1720 = 1,
+ .has_ao = 1,
+ },
+ [BOARD_PCI1731] = {
+ .name = "pci1731",
+ .n_aichan = 16,
+ .rangelist_ai = &range_pci17x1,
+ .rangecode_ai = range_codes_pci17x1,
+ .has_irq = 1,
+ .has_di_do = 1,
+ },
+};
+
+struct pci1710_private {
+ unsigned int max_samples;
+ unsigned int CntrlReg; /* Control register */
+ unsigned char ai_et;
+ unsigned int ai_et_CntrlReg;
+ unsigned int ai_et_MuxVal;
+ unsigned int act_chanlist[32]; /* list of scanned channel */
+ unsigned char saved_seglen; /* len of the non-repeating chanlist */
+ unsigned char da_ranges; /* copy of D/A outpit range register */
+};
+
+static int pci171x_ai_check_chanlist(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_cmd *cmd)
+{
+ struct pci1710_private *devpriv = dev->private;
+ unsigned int chan0 = CR_CHAN(cmd->chanlist[0]);
+ unsigned int last_aref = CR_AREF(cmd->chanlist[0]);
+ unsigned int next_chan = (chan0 + 1) % s->n_chan;
+ unsigned int chansegment[32];
+ unsigned int seglen;
+ int i;
+
+ if (cmd->chanlist_len == 1) {
+ devpriv->saved_seglen = cmd->chanlist_len;
+ return 0;
+ }
+
+ /* first channel is always ok */
+ chansegment[0] = cmd->chanlist[0];
+
+ for (i = 1; i < cmd->chanlist_len; i++) {
+ unsigned int chan = CR_CHAN(cmd->chanlist[i]);
+ unsigned int aref = CR_AREF(cmd->chanlist[i]);
+
+ if (cmd->chanlist[0] == cmd->chanlist[i])
+ break; /* we detected a loop, stop */
+
+ if (aref == AREF_DIFF && (chan & 1)) {
+ dev_err(dev->class_dev,
+ "Odd channel cannot be differential input!\n");
+ return -EINVAL;
+ }
+
+ if (last_aref == AREF_DIFF)
+ next_chan = (next_chan + 1) % s->n_chan;
+ if (chan != next_chan) {
+ dev_err(dev->class_dev,
+ "channel list must be continuous! chanlist[%i]=%d but must be %d or %d!\n",
+ i, chan, next_chan, chan0);
+ return -EINVAL;
+ }
+
+ /* next correct channel in list */
+ chansegment[i] = cmd->chanlist[i];
+ last_aref = aref;
+ }
+ seglen = i;
+
+ for (i = 0; i < cmd->chanlist_len; i++) {
+ if (cmd->chanlist[i] != chansegment[i % seglen]) {
+ dev_err(dev->class_dev,
+ "bad channel, reference or range number! chanlist[%i]=%d,%d,%d and not %d,%d,%d!\n",
+ i, CR_CHAN(chansegment[i]),
+ CR_RANGE(chansegment[i]),
+ CR_AREF(chansegment[i]),
+ CR_CHAN(cmd->chanlist[i % seglen]),
+ CR_RANGE(cmd->chanlist[i % seglen]),
+ CR_AREF(chansegment[i % seglen]));
+ return -EINVAL;
+ }
+ }
+ devpriv->saved_seglen = seglen;
+
+ return 0;
+}
+
+static void pci171x_ai_setup_chanlist(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ unsigned int *chanlist,
+ unsigned int n_chan,
+ unsigned int seglen)
+{
+ const struct boardtype *board = dev->board_ptr;
+ struct pci1710_private *devpriv = dev->private;
+ unsigned int first_chan = CR_CHAN(chanlist[0]);
+ unsigned int last_chan = CR_CHAN(chanlist[seglen - 1]);
+ unsigned int i;
+
+ for (i = 0; i < seglen; i++) { /* store range list to card */
+ unsigned int chan = CR_CHAN(chanlist[i]);
+ unsigned int range = CR_RANGE(chanlist[i]);
+ unsigned int aref = CR_AREF(chanlist[i]);
+ unsigned int rangeval;
+
+ rangeval = board->rangecode_ai[range];
+ if (aref == AREF_DIFF)
+ rangeval |= 0x0020;
+
+ /* select channel and set range */
+ outw(chan | (chan << 8), dev->iobase + PCI171x_MUX);
+ outw(rangeval, dev->iobase + PCI171x_RANGE);
+
+ devpriv->act_chanlist[i] = chan;
+ }
+ for ( ; i < n_chan; i++) /* store remainder of channel list */
+ devpriv->act_chanlist[i] = CR_CHAN(chanlist[i]);
+
+ /* select channel interval to scan */
+ devpriv->ai_et_MuxVal = first_chan | (last_chan << 8);
+ outw(devpriv->ai_et_MuxVal, dev->iobase + PCI171x_MUX);
+}
+
+static int pci171x_ai_eoc(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned long context)
+{
+ unsigned int status;
+
+ status = inw(dev->iobase + PCI171x_STATUS);
+ if ((status & Status_FE) == 0)
+ return 0;
+ return -EBUSY;
+}
+
+static int pci171x_ai_read_sample(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ unsigned int cur_chan,
+ unsigned int *val)
+{
+ const struct boardtype *board = dev->board_ptr;
+ struct pci1710_private *devpriv = dev->private;
+ unsigned int sample;
+ unsigned int chan;
+
+ sample = inw(dev->iobase + PCI171x_AD_DATA);
+ if (!board->is_pci1713) {
+ /*
+ * The upper 4 bits of the 16-bit sample are the channel number
+ * that the sample was acquired from. Verify that this channel
+ * number matches the expected channel number.
+ */
+ chan = sample >> 12;
+ if (chan != devpriv->act_chanlist[cur_chan]) {
+ dev_err(dev->class_dev,
+ "A/D data droput: received from channel %d, expected %d\n",
+ chan, devpriv->act_chanlist[cur_chan]);
+ return -ENODATA;
+ }
+ }
+ *val = sample & s->maxdata;
+ return 0;
+}
+
+static int pci171x_ai_insn_read(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ struct pci1710_private *devpriv = dev->private;
+ int ret = 0;
+ int i;
+
+ devpriv->CntrlReg &= Control_CNT0;
+ devpriv->CntrlReg |= Control_SW; /* set software trigger */
+ outw(devpriv->CntrlReg, dev->iobase + PCI171x_CONTROL);
+ outb(0, dev->iobase + PCI171x_CLRFIFO);
+ outb(0, dev->iobase + PCI171x_CLRINT);
+
+ pci171x_ai_setup_chanlist(dev, s, &insn->chanspec, 1, 1);
+
+ for (i = 0; i < insn->n; i++) {
+ unsigned int val;
+
+ outw(0, dev->iobase + PCI171x_SOFTTRG); /* start conversion */
+
+ ret = comedi_timeout(dev, s, insn, pci171x_ai_eoc, 0);
+ if (ret)
+ break;
+
+ ret = pci171x_ai_read_sample(dev, s, 0, &val);
+ if (ret)
+ break;
+
+ data[i] = val;
+ }
+
+ outb(0, dev->iobase + PCI171x_CLRFIFO);
+ outb(0, dev->iobase + PCI171x_CLRINT);
+
+ return ret ? ret : insn->n;
+}
+
+static int pci171x_ao_insn_write(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ struct pci1710_private *devpriv = dev->private;
+ unsigned int chan = CR_CHAN(insn->chanspec);
+ unsigned int range = CR_RANGE(insn->chanspec);
+ unsigned int reg = chan ? PCI171x_DA2 : PCI171x_DA1;
+ unsigned int val = s->readback[chan];
+ int i;
+
+ devpriv->da_ranges &= ~(1 << (chan << 1));
+ devpriv->da_ranges |= (range << (chan << 1));
+ outw(devpriv->da_ranges, dev->iobase + PCI171x_DAREF);
+
+ for (i = 0; i < insn->n; i++) {
+ val = data[i];
+ outw(val, dev->iobase + reg);
+ }
+
+ s->readback[chan] = val;
+
+ return insn->n;
+}
+
+static int pci171x_di_insn_bits(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ data[1] = inw(dev->iobase + PCI171x_DI);
+
+ return insn->n;
+}
+
+static int pci171x_do_insn_bits(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ if (comedi_dio_update_state(s, data))
+ outw(s->state, dev->iobase + PCI171x_DO);
+
+ data[1] = s->state;
+
+ return insn->n;
+}
+
+static int pci1720_ao_insn_write(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ struct pci1710_private *devpriv = dev->private;
+ unsigned int chan = CR_CHAN(insn->chanspec);
+ unsigned int range = CR_RANGE(insn->chanspec);
+ unsigned int val;
+ int i;
+
+ val = devpriv->da_ranges & (~(0x03 << (chan << 1)));
+ val |= (range << (chan << 1));
+ if (val != devpriv->da_ranges) {
+ outb(val, dev->iobase + PCI1720_RANGE);
+ devpriv->da_ranges = val;
+ }
+
+ val = s->readback[chan];
+ for (i = 0; i < insn->n; i++) {
+ val = data[i];
+ outw(val, dev->iobase + PCI1720_DA0 + (chan << 1));
+ outb(0, dev->iobase + PCI1720_SYNCOUT); /* update outputs */
+ }
+
+ s->readback[chan] = val;
+
+ return insn->n;
+}
+
+static int pci171x_ai_cancel(struct comedi_device *dev,
+ struct comedi_subdevice *s)
+{
+ struct pci1710_private *devpriv = dev->private;
+
+ devpriv->CntrlReg &= Control_CNT0;
+ devpriv->CntrlReg |= Control_SW;
+ /* reset any operations */
+ outw(devpriv->CntrlReg, dev->iobase + PCI171x_CONTROL);
+ comedi_8254_pacer_enable(dev->pacer, 1, 2, false);
+ outb(0, dev->iobase + PCI171x_CLRFIFO);
+ outb(0, dev->iobase + PCI171x_CLRINT);
+
+ return 0;
+}
+
+static void pci1710_handle_every_sample(struct comedi_device *dev,
+ struct comedi_subdevice *s)
+{
+ struct comedi_cmd *cmd = &s->async->cmd;
+ unsigned int status;
+ unsigned int val;
+ int ret;
+
+ status = inw(dev->iobase + PCI171x_STATUS);
+ if (status & Status_FE) {
+ dev_dbg(dev->class_dev, "A/D FIFO empty (%4x)\n", status);
+ s->async->events |= COMEDI_CB_ERROR;
+ return;
+ }
+ if (status & Status_FF) {
+ dev_dbg(dev->class_dev,
+ "A/D FIFO Full status (Fatal Error!) (%4x)\n", status);
+ s->async->events |= COMEDI_CB_ERROR;
+ return;
+ }
+
+ outb(0, dev->iobase + PCI171x_CLRINT); /* clear our INT request */
+
+ for (; !(inw(dev->iobase + PCI171x_STATUS) & Status_FE);) {
+ ret = pci171x_ai_read_sample(dev, s, s->async->cur_chan, &val);
+ if (ret) {
+ s->async->events |= COMEDI_CB_ERROR;
+ break;
+ }
+
+ comedi_buf_write_samples(s, &val, 1);
+
+ if (cmd->stop_src == TRIG_COUNT &&
+ s->async->scans_done >= cmd->stop_arg) {
+ s->async->events |= COMEDI_CB_EOA;
+ break;
+ }
+ }
+
+ outb(0, dev->iobase + PCI171x_CLRINT); /* clear our INT request */
+}
+
+static void pci1710_handle_fifo(struct comedi_device *dev,
+ struct comedi_subdevice *s)
+{
+ struct pci1710_private *devpriv = dev->private;
+ struct comedi_async *async = s->async;
+ struct comedi_cmd *cmd = &async->cmd;
+ unsigned int status;
+ int i;
+
+ status = inw(dev->iobase + PCI171x_STATUS);
+ if (!(status & Status_FH)) {
+ dev_dbg(dev->class_dev, "A/D FIFO not half full!\n");
+ async->events |= COMEDI_CB_ERROR;
+ return;
+ }
+ if (status & Status_FF) {
+ dev_dbg(dev->class_dev,
+ "A/D FIFO Full status (Fatal Error!)\n");
+ async->events |= COMEDI_CB_ERROR;
+ return;
+ }
+
+ for (i = 0; i < devpriv->max_samples; i++) {
+ unsigned int val;
+ int ret;
+
+ ret = pci171x_ai_read_sample(dev, s, s->async->cur_chan, &val);
+ if (ret) {
+ s->async->events |= COMEDI_CB_ERROR;
+ break;
+ }
+
+ if (!comedi_buf_write_samples(s, &val, 1))
+ break;
+
+ if (cmd->stop_src == TRIG_COUNT &&
+ async->scans_done >= cmd->stop_arg) {
+ async->events |= COMEDI_CB_EOA;
+ break;
+ }
+ }
+
+ outb(0, dev->iobase + PCI171x_CLRINT); /* clear our INT request */
+}
+
+static irqreturn_t interrupt_service_pci1710(int irq, void *d)
+{
+ struct comedi_device *dev = d;
+ struct pci1710_private *devpriv = dev->private;
+ struct comedi_subdevice *s;
+ struct comedi_cmd *cmd;
+
+ if (!dev->attached) /* is device attached? */
+ return IRQ_NONE; /* no, exit */
+
+ s = dev->read_subdev;
+ cmd = &s->async->cmd;
+
+ /* is this interrupt from our board? */
+ if (!(inw(dev->iobase + PCI171x_STATUS) & Status_IRQ))
+ return IRQ_NONE; /* no, exit */
+
+ if (devpriv->ai_et) { /* Switch from initial TRIG_EXT to TRIG_xxx. */
+ devpriv->ai_et = 0;
+ devpriv->CntrlReg &= Control_CNT0;
+ devpriv->CntrlReg |= Control_SW; /* set software trigger */
+ outw(devpriv->CntrlReg, dev->iobase + PCI171x_CONTROL);
+ devpriv->CntrlReg = devpriv->ai_et_CntrlReg;
+ outb(0, dev->iobase + PCI171x_CLRFIFO);
+ outb(0, dev->iobase + PCI171x_CLRINT);
+ outw(devpriv->ai_et_MuxVal, dev->iobase + PCI171x_MUX);
+ outw(devpriv->CntrlReg, dev->iobase + PCI171x_CONTROL);
+ comedi_8254_pacer_enable(dev->pacer, 1, 2, true);
+ return IRQ_HANDLED;
+ }
+
+ if (cmd->flags & CMDF_WAKE_EOS)
+ pci1710_handle_every_sample(dev, s);
+ else
+ pci1710_handle_fifo(dev, s);
+
+ comedi_handle_events(dev, s);
+
+ return IRQ_HANDLED;
+}
+
+static int pci171x_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
+{
+ struct pci1710_private *devpriv = dev->private;
+ struct comedi_cmd *cmd = &s->async->cmd;
+
+ pci171x_ai_setup_chanlist(dev, s, cmd->chanlist, cmd->chanlist_len,
+ devpriv->saved_seglen);
+
+ outb(0, dev->iobase + PCI171x_CLRFIFO);
+ outb(0, dev->iobase + PCI171x_CLRINT);
+
+ devpriv->CntrlReg &= Control_CNT0;
+ if ((cmd->flags & CMDF_WAKE_EOS) == 0)
+ devpriv->CntrlReg |= Control_ONEFH;
+
+ if (cmd->convert_src == TRIG_TIMER) {
+ comedi_8254_update_divisors(dev->pacer);
+
+ devpriv->CntrlReg |= Control_PACER | Control_IRQEN;
+ if (cmd->start_src == TRIG_EXT) {
+ devpriv->ai_et_CntrlReg = devpriv->CntrlReg;
+ devpriv->CntrlReg &=
+ ~(Control_PACER | Control_ONEFH | Control_GATE);
+ devpriv->CntrlReg |= Control_EXT;
+ devpriv->ai_et = 1;
+ } else { /* TRIG_NOW */
+ devpriv->ai_et = 0;
+ }
+ outw(devpriv->CntrlReg, dev->iobase + PCI171x_CONTROL);
+
+ if (cmd->start_src == TRIG_NOW)
+ comedi_8254_pacer_enable(dev->pacer, 1, 2, true);
+ } else { /* TRIG_EXT */
+ devpriv->CntrlReg |= Control_EXT | Control_IRQEN;
+ outw(devpriv->CntrlReg, dev->iobase + PCI171x_CONTROL);
+ }
+
+ return 0;
+}
+
+static int pci171x_ai_cmdtest(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_cmd *cmd)
+{
+ int err = 0;
+
+ /* Step 1 : check if triggers are trivially valid */
+
+ err |= comedi_check_trigger_src(&cmd->start_src, TRIG_NOW | TRIG_EXT);
+ err |= comedi_check_trigger_src(&cmd->scan_begin_src, TRIG_FOLLOW);
+ err |= comedi_check_trigger_src(&cmd->convert_src,
+ TRIG_TIMER | TRIG_EXT);
+ err |= comedi_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
+ err |= comedi_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE);
+
+ if (err)
+ return 1;
+
+ /* step 2a: make sure trigger sources are unique */
+
+ err |= comedi_check_trigger_is_unique(cmd->start_src);
+ err |= comedi_check_trigger_is_unique(cmd->convert_src);
+ err |= comedi_check_trigger_is_unique(cmd->stop_src);
+
+ /* step 2b: and mutually compatible */
+
+ if (err)
+ return 2;
+
+ /* Step 3: check if arguments are trivially valid */
+
+ err |= comedi_check_trigger_arg_is(&cmd->start_arg, 0);
+ err |= comedi_check_trigger_arg_is(&cmd->scan_begin_arg, 0);
+
+ if (cmd->convert_src == TRIG_TIMER)
+ err |= comedi_check_trigger_arg_min(&cmd->convert_arg, 10000);
+ else /* TRIG_FOLLOW */
+ err |= comedi_check_trigger_arg_is(&cmd->convert_arg, 0);
+
+ err |= comedi_check_trigger_arg_is(&cmd->scan_end_arg,
+ cmd->chanlist_len);
+
+ if (cmd->stop_src == TRIG_COUNT)
+ err |= comedi_check_trigger_arg_min(&cmd->stop_arg, 1);
+ else /* TRIG_NONE */
+ err |= comedi_check_trigger_arg_is(&cmd->stop_arg, 0);
+
+ if (err)
+ return 3;
+
+ /* step 4: fix up any arguments */
+
+ if (cmd->convert_src == TRIG_TIMER) {
+ unsigned int arg = cmd->convert_arg;
+
+ comedi_8254_cascade_ns_to_timer(dev->pacer, &arg, cmd->flags);
+ err |= comedi_check_trigger_arg_is(&cmd->convert_arg, arg);
+ }
+
+ if (err)
+ return 4;
+
+ /* Step 5: check channel list */
+
+ err |= pci171x_ai_check_chanlist(dev, s, cmd);
+
+ if (err)
+ return 5;
+
+ return 0;
+}
+
+static int pci171x_insn_counter_config(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ struct pci1710_private *devpriv = dev->private;
+
+ switch (data[0]) {
+ case INSN_CONFIG_SET_CLOCK_SRC:
+ switch (data[1]) {
+ case 0: /* internal */
+ devpriv->ai_et_CntrlReg &= ~Control_CNT0;
+ break;
+ case 1: /* external */
+ devpriv->ai_et_CntrlReg |= Control_CNT0;
+ break;
+ default:
+ return -EINVAL;
+ }
+ outw(devpriv->ai_et_CntrlReg, dev->iobase + PCI171x_CONTROL);
+ break;
+ case INSN_CONFIG_GET_CLOCK_SRC:
+ if (devpriv->ai_et_CntrlReg & Control_CNT0) {
+ data[1] = 1;
+ data[2] = 0;
+ } else {
+ data[1] = 0;
+ data[2] = I8254_OSC_BASE_10MHZ;
+ }
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return insn->n;
+}
+
+static int pci171x_reset(struct comedi_device *dev)
+{
+ const struct boardtype *board = dev->board_ptr;
+ struct pci1710_private *devpriv = dev->private;
+
+ /* Software trigger, CNT0=external */
+ devpriv->CntrlReg = Control_SW | Control_CNT0;
+ /* reset any operations */
+ outw(devpriv->CntrlReg, dev->iobase + PCI171x_CONTROL);
+ outb(0, dev->iobase + PCI171x_CLRFIFO); /* clear FIFO */
+ outb(0, dev->iobase + PCI171x_CLRINT); /* clear INT request */
+ devpriv->da_ranges = 0;
+ if (board->has_ao) {
+ /* set DACs to 0..5V */
+ outb(devpriv->da_ranges, dev->iobase + PCI171x_DAREF);
+ outw(0, dev->iobase + PCI171x_DA1); /* set DA outputs to 0V */
+ outw(0, dev->iobase + PCI171x_DA2);
+ }
+ outw(0, dev->iobase + PCI171x_DO); /* digital outputs to 0 */
+ outb(0, dev->iobase + PCI171x_CLRFIFO); /* clear FIFO */
+ outb(0, dev->iobase + PCI171x_CLRINT); /* clear INT request */
+
+ return 0;
+}
+
+static int pci1720_reset(struct comedi_device *dev)
+{
+ struct pci1710_private *devpriv = dev->private;
+ /* set synchronous output mode */
+ outb(Syncont_SC0, dev->iobase + PCI1720_SYNCONT);
+ devpriv->da_ranges = 0xAA;
+ /* set all ranges to +/-5V */
+ outb(devpriv->da_ranges, dev->iobase + PCI1720_RANGE);
+ outw(0x0800, dev->iobase + PCI1720_DA0); /* set outputs to 0V */
+ outw(0x0800, dev->iobase + PCI1720_DA1);
+ outw(0x0800, dev->iobase + PCI1720_DA2);
+ outw(0x0800, dev->iobase + PCI1720_DA3);
+ outb(0, dev->iobase + PCI1720_SYNCOUT); /* update outputs */
+
+ return 0;
+}
+
+static int pci1710_reset(struct comedi_device *dev)
+{
+ const struct boardtype *board = dev->board_ptr;
+
+ if (board->is_pci1720)
+ return pci1720_reset(dev);
+
+ return pci171x_reset(dev);
+}
+
+static int pci1710_auto_attach(struct comedi_device *dev,
+ unsigned long context)
+{
+ struct pci_dev *pcidev = comedi_to_pci_dev(dev);
+ const struct boardtype *board = NULL;
+ struct pci1710_private *devpriv;
+ struct comedi_subdevice *s;
+ int ret, subdev, n_subdevices;
+
+ if (context < ARRAY_SIZE(boardtypes))
+ board = &boardtypes[context];
+ if (!board)
+ return -ENODEV;
+ dev->board_ptr = board;
+ dev->board_name = board->name;
+
+ devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
+ if (!devpriv)
+ return -ENOMEM;
+
+ ret = comedi_pci_enable(dev);
+ if (ret)
+ return ret;
+ dev->iobase = pci_resource_start(pcidev, 2);
+
+ dev->pacer = comedi_8254_init(dev->iobase + PCI171X_TIMER_BASE,
+ I8254_OSC_BASE_10MHZ, I8254_IO16, 0);
+ if (!dev->pacer)
+ return -ENOMEM;
+
+ n_subdevices = 0;
+ if (board->n_aichan)
+ n_subdevices++;
+ if (board->has_ao)
+ n_subdevices++;
+ if (board->has_di_do)
+ n_subdevices += 2;
+ if (board->has_counter)
+ n_subdevices++;
+
+ ret = comedi_alloc_subdevices(dev, n_subdevices);
+ if (ret)
+ return ret;
+
+ pci1710_reset(dev);
+
+ if (board->has_irq && pcidev->irq) {
+ ret = request_irq(pcidev->irq, interrupt_service_pci1710,
+ IRQF_SHARED, dev->board_name, dev);
+ if (ret == 0)
+ dev->irq = pcidev->irq;
+ }
+
+ subdev = 0;
+
+ if (board->n_aichan) {
+ s = &dev->subdevices[subdev];
+ s->type = COMEDI_SUBD_AI;
+ s->subdev_flags = SDF_READABLE | SDF_COMMON | SDF_GROUND;
+ if (board->has_diff_ai)
+ s->subdev_flags |= SDF_DIFF;
+ s->n_chan = board->n_aichan;
+ s->maxdata = 0x0fff;
+ s->range_table = board->rangelist_ai;
+ s->insn_read = pci171x_ai_insn_read;
+ if (dev->irq) {
+ dev->read_subdev = s;
+ s->subdev_flags |= SDF_CMD_READ;
+ s->len_chanlist = s->n_chan;
+ s->do_cmdtest = pci171x_ai_cmdtest;
+ s->do_cmd = pci171x_ai_cmd;
+ s->cancel = pci171x_ai_cancel;
+ }
+ subdev++;
+ }
+
+ if (board->has_ao) {
+ s = &dev->subdevices[subdev];
+ s->type = COMEDI_SUBD_AO;
+ s->subdev_flags = SDF_WRITABLE | SDF_GROUND | SDF_COMMON;
+ s->maxdata = 0x0fff;
+ if (board->is_pci1720) {
+ s->n_chan = 4;
+ s->range_table = &pci1720_ao_range;
+ s->insn_write = pci1720_ao_insn_write;
+ } else {
+ s->n_chan = 2;
+ s->range_table = &pci171x_ao_range;
+ s->insn_write = pci171x_ao_insn_write;
+ }
+
+ ret = comedi_alloc_subdev_readback(s);
+ if (ret)
+ return ret;
+
+ /* initialize the readback values to match the board reset */
+ if (board->is_pci1720) {
+ int i;
+
+ for (i = 0; i < s->n_chan; i++)
+ s->readback[i] = 0x0800;
+ }
+
+ subdev++;
+ }
+
+ if (board->has_di_do) {
+ s = &dev->subdevices[subdev];
+ s->type = COMEDI_SUBD_DI;
+ s->subdev_flags = SDF_READABLE;
+ s->n_chan = 16;
+ s->maxdata = 1;
+ s->range_table = &range_digital;
+ s->insn_bits = pci171x_di_insn_bits;
+ subdev++;
+
+ s = &dev->subdevices[subdev];
+ s->type = COMEDI_SUBD_DO;
+ s->subdev_flags = SDF_WRITABLE;
+ s->n_chan = 16;
+ s->maxdata = 1;
+ s->range_table = &range_digital;
+ s->insn_bits = pci171x_do_insn_bits;
+ subdev++;
+ }
+
+ /* Counter subdevice (8254) */
+ if (board->has_counter) {
+ s = &dev->subdevices[subdev];
+ comedi_8254_subdevice_init(s, dev->pacer);
+
+ dev->pacer->insn_config = pci171x_insn_counter_config;
+
+ /* counters 1 and 2 are used internally for the pacer */
+ comedi_8254_set_busy(dev->pacer, 1, true);
+ comedi_8254_set_busy(dev->pacer, 2, true);
+
+ subdev++;
+ }
+
+ /* max_samples is half the FIFO size (2 bytes/sample) */
+ devpriv->max_samples = (board->has_large_fifo) ? 2048 : 512;
+
+ return 0;
+}
+
+static void pci1710_detach(struct comedi_device *dev)
+{
+ if (dev->iobase)
+ pci1710_reset(dev);
+ comedi_pci_detach(dev);
+}
+
+static struct comedi_driver adv_pci1710_driver = {
+ .driver_name = "adv_pci1710",
+ .module = THIS_MODULE,
+ .auto_attach = pci1710_auto_attach,
+ .detach = pci1710_detach,
+};
+
+static int adv_pci1710_pci_probe(struct pci_dev *dev,
+ const struct pci_device_id *id)
+{
+ return comedi_pci_auto_config(dev, &adv_pci1710_driver,
+ id->driver_data);
+}
+
+static const struct pci_device_id adv_pci1710_pci_table[] = {
+ {
+ PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH, 0x1710,
+ PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050),
+ .driver_data = BOARD_PCI1710,
+ }, {
+ PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH, 0x1710,
+ PCI_VENDOR_ID_ADVANTECH, 0x0000),
+ .driver_data = BOARD_PCI1710,
+ }, {
+ PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH, 0x1710,
+ PCI_VENDOR_ID_ADVANTECH, 0xb100),
+ .driver_data = BOARD_PCI1710,
+ }, {
+ PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH, 0x1710,
+ PCI_VENDOR_ID_ADVANTECH, 0xb200),
+ .driver_data = BOARD_PCI1710,
+ }, {
+ PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH, 0x1710,
+ PCI_VENDOR_ID_ADVANTECH, 0xc100),
+ .driver_data = BOARD_PCI1710,
+ }, {
+ PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH, 0x1710,
+ PCI_VENDOR_ID_ADVANTECH, 0xc200),
+ .driver_data = BOARD_PCI1710,
+ }, {
+ PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH, 0x1710, 0x1000, 0xd100),
+ .driver_data = BOARD_PCI1710,
+ }, {
+ PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH, 0x1710,
+ PCI_VENDOR_ID_ADVANTECH, 0x0002),
+ .driver_data = BOARD_PCI1710HG,
+ }, {
+ PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH, 0x1710,
+ PCI_VENDOR_ID_ADVANTECH, 0xb102),
+ .driver_data = BOARD_PCI1710HG,
+ }, {
+ PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH, 0x1710,
+ PCI_VENDOR_ID_ADVANTECH, 0xb202),
+ .driver_data = BOARD_PCI1710HG,
+ }, {
+ PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH, 0x1710,
+ PCI_VENDOR_ID_ADVANTECH, 0xc102),
+ .driver_data = BOARD_PCI1710HG,
+ }, {
+ PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH, 0x1710,
+ PCI_VENDOR_ID_ADVANTECH, 0xc202),
+ .driver_data = BOARD_PCI1710HG,
+ }, {
+ PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH, 0x1710, 0x1000, 0xd102),
+ .driver_data = BOARD_PCI1710HG,
+ },
+ { PCI_VDEVICE(ADVANTECH, 0x1711), BOARD_PCI1711 },
+ { PCI_VDEVICE(ADVANTECH, 0x1713), BOARD_PCI1713 },
+ { PCI_VDEVICE(ADVANTECH, 0x1720), BOARD_PCI1720 },
+ { PCI_VDEVICE(ADVANTECH, 0x1731), BOARD_PCI1731 },
+ { 0 }
+};
+MODULE_DEVICE_TABLE(pci, adv_pci1710_pci_table);
+
+static struct pci_driver adv_pci1710_pci_driver = {
+ .name = "adv_pci1710",
+ .id_table = adv_pci1710_pci_table,
+ .probe = adv_pci1710_pci_probe,
+ .remove = comedi_pci_auto_unconfig,
+};
+module_comedi_pci_driver(adv_pci1710_driver, adv_pci1710_pci_driver);
+
+MODULE_AUTHOR("Comedi http://www.comedi.org");
+MODULE_DESCRIPTION("Comedi: Advantech PCI-1710 Series Multifunction DAS Cards");
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/comedi/drivers/adv_pci1723.c b/drivers/staging/comedi/drivers/adv_pci1723.c
new file mode 100644
index 000000000..1921a97cc
--- /dev/null
+++ b/drivers/staging/comedi/drivers/adv_pci1723.c
@@ -0,0 +1,233 @@
+/*
+ * adv_pci1723.c
+ * Comedi driver for the Advantech PCI-1723 card.
+ *
+ * COMEDI - Linux Control and Measurement Device Interface
+ * Copyright (C) 2000 David A. Schleef <ds@schleef.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+/*
+ * Driver: adv_pci1723
+ * Description: Advantech PCI-1723
+ * Author: yonggang <rsmgnu@gmail.com>, Ian Abbott <abbotti@mev.co.uk>
+ * Devices: [Advantech] PCI-1723 (adv_pci1723)
+ * Updated: Mon, 14 Apr 2008 15:12:56 +0100
+ * Status: works
+ *
+ * Configuration Options: not applicable, uses comedi PCI auto config
+ *
+ * Subdevice 0 is 8-channel AO, 16-bit, range +/- 10 V.
+ *
+ * Subdevice 1 is 16-channel DIO. The channels are configurable as
+ * input or output in 2 groups (0 to 7, 8 to 15). Configuring any
+ * channel implicitly configures all channels in the same group.
+ *
+ * TODO:
+ * 1. Add the two milliamp ranges to the AO subdevice (0 to 20 mA,
+ * 4 to 20 mA).
+ * 2. Read the initial ranges and values of the AO subdevice at
+ * start-up instead of reinitializing them.
+ * 3. Implement calibration.
+ */
+
+#include <linux/module.h>
+
+#include "../comedi_pci.h"
+
+/*
+ * PCI Bar 2 I/O Register map (dev->iobase)
+ */
+#define PCI1723_AO_REG(x) (0x00 + ((x) * 2))
+#define PCI1723_BOARD_ID_REG 0x10
+#define PCI1723_BOARD_ID_MASK (0xf << 0)
+#define PCI1723_SYNC_CTRL_REG 0x12
+#define PCI1723_SYNC_CTRL_ASYNC (0 << 0)
+#define PCI1723_SYNC_CTRL_SYNC (1 << 0)
+#define PCI1723_CTRL_REG 0x14
+#define PCI1723_CTRL_BUSY (1 << 15)
+#define PCI1723_CTRL_INIT (1 << 14)
+#define PCI1723_CTRL_SELF (1 << 8)
+#define PCI1723_CTRL_IDX(x) (((x) & 0x3) << 6)
+#define PCI1723_CTRL_RANGE(x) (((x) & 0x3) << 4)
+#define PCI1723_CTRL_GAIN (0 << 3)
+#define PCI1723_CTRL_OFFSET (1 << 3)
+#define PCI1723_CTRL_CHAN(x) (((x) & 0x7) << 0)
+#define PCI1723_CALIB_CTRL_REG 0x16
+#define PCI1723_CALIB_CTRL_CS (1 << 2)
+#define PCI1723_CALIB_CTRL_DAT (1 << 1)
+#define PCI1723_CALIB_CTRL_CLK (1 << 0)
+#define PCI1723_CALIB_STROBE_REG 0x18
+#define PCI1723_DIO_CTRL_REG 0x1a
+#define PCI1723_DIO_CTRL_HDIO (1 << 1)
+#define PCI1723_DIO_CTRL_LDIO (1 << 0)
+#define PCI1723_DIO_DATA_REG 0x1c
+#define PCI1723_CALIB_DATA_REG 0x1e
+#define PCI1723_SYNC_STROBE_REG 0x20
+#define PCI1723_RESET_AO_STROBE_REG 0x22
+#define PCI1723_RESET_CALIB_STROBE_REG 0x24
+#define PCI1723_RANGE_STROBE_REG 0x26
+#define PCI1723_VREF_REG 0x28
+#define PCI1723_VREF_NEG10V (0 << 0)
+#define PCI1723_VREF_0V (1 << 0)
+#define PCI1723_VREF_POS10V (3 << 0)
+
+static int pci1723_ao_insn_write(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ unsigned int chan = CR_CHAN(insn->chanspec);
+ int i;
+
+ for (i = 0; i < insn->n; i++) {
+ unsigned int val = data[i];
+
+ outw(val, dev->iobase + PCI1723_AO_REG(chan));
+ s->readback[chan] = val;
+ }
+
+ return insn->n;
+}
+
+static int pci1723_dio_insn_config(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ unsigned int chan = CR_CHAN(insn->chanspec);
+ unsigned int mask = (chan < 8) ? 0x00ff : 0xff00;
+ unsigned short mode = 0x0000; /* assume output */
+ int ret;
+
+ ret = comedi_dio_insn_config(dev, s, insn, data, mask);
+ if (ret)
+ return ret;
+
+ if (!(s->io_bits & 0x00ff))
+ mode |= PCI1723_DIO_CTRL_LDIO; /* low byte input */
+ if (!(s->io_bits & 0xff00))
+ mode |= PCI1723_DIO_CTRL_HDIO; /* high byte input */
+ outw(mode, dev->iobase + PCI1723_DIO_CTRL_REG);
+
+ return insn->n;
+}
+
+static int pci1723_dio_insn_bits(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ if (comedi_dio_update_state(s, data))
+ outw(s->state, dev->iobase + PCI1723_DIO_DATA_REG);
+
+ data[1] = inw(dev->iobase + PCI1723_DIO_DATA_REG);
+
+ return insn->n;
+}
+
+static int pci1723_auto_attach(struct comedi_device *dev,
+ unsigned long context_unused)
+{
+ struct pci_dev *pcidev = comedi_to_pci_dev(dev);
+ struct comedi_subdevice *s;
+ unsigned int val;
+ int ret;
+ int i;
+
+ ret = comedi_pci_enable(dev);
+ if (ret)
+ return ret;
+ dev->iobase = pci_resource_start(pcidev, 2);
+
+ ret = comedi_alloc_subdevices(dev, 2);
+ if (ret)
+ return ret;
+
+ s = &dev->subdevices[0];
+ s->type = COMEDI_SUBD_AO;
+ s->subdev_flags = SDF_WRITABLE | SDF_GROUND | SDF_COMMON;
+ s->n_chan = 8;
+ s->maxdata = 0xffff;
+ s->range_table = &range_bipolar10;
+ s->insn_write = pci1723_ao_insn_write;
+
+ ret = comedi_alloc_subdev_readback(s);
+ if (ret)
+ return ret;
+
+ /* synchronously reset all analog outputs to 0V, +/-10V range */
+ outw(PCI1723_SYNC_CTRL_SYNC, dev->iobase + PCI1723_SYNC_CTRL_REG);
+ for (i = 0; i < s->n_chan; i++) {
+ outw(PCI1723_CTRL_RANGE(0) | PCI1723_CTRL_CHAN(i),
+ PCI1723_CTRL_REG);
+ outw(0, dev->iobase + PCI1723_RANGE_STROBE_REG);
+
+ outw(0x8000, dev->iobase + PCI1723_AO_REG(i));
+ s->readback[i] = 0x8000;
+ }
+ outw(0, dev->iobase + PCI1723_SYNC_STROBE_REG);
+
+ /* disable syncronous control */
+ outw(PCI1723_SYNC_CTRL_ASYNC, dev->iobase + PCI1723_SYNC_CTRL_REG);
+
+ s = &dev->subdevices[1];
+ s->type = COMEDI_SUBD_DIO;
+ s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
+ s->n_chan = 16;
+ s->maxdata = 1;
+ s->range_table = &range_digital;
+ s->insn_config = pci1723_dio_insn_config;
+ s->insn_bits = pci1723_dio_insn_bits;
+
+ /* get initial DIO direction and state */
+ val = inw(dev->iobase + PCI1723_DIO_CTRL_REG);
+ if (!(val & PCI1723_DIO_CTRL_LDIO))
+ s->io_bits |= 0x00ff; /* low byte output */
+ if (!(val & PCI1723_DIO_CTRL_HDIO))
+ s->io_bits |= 0xff00; /* high byte output */
+ s->state = inw(dev->iobase + PCI1723_DIO_DATA_REG);
+
+ return 0;
+}
+
+static struct comedi_driver adv_pci1723_driver = {
+ .driver_name = "adv_pci1723",
+ .module = THIS_MODULE,
+ .auto_attach = pci1723_auto_attach,
+ .detach = comedi_pci_detach,
+};
+
+static int adv_pci1723_pci_probe(struct pci_dev *dev,
+ const struct pci_device_id *id)
+{
+ return comedi_pci_auto_config(dev, &adv_pci1723_driver,
+ id->driver_data);
+}
+
+static const struct pci_device_id adv_pci1723_pci_table[] = {
+ { PCI_DEVICE(PCI_VENDOR_ID_ADVANTECH, 0x1723) },
+ { 0 }
+};
+MODULE_DEVICE_TABLE(pci, adv_pci1723_pci_table);
+
+static struct pci_driver adv_pci1723_pci_driver = {
+ .name = "adv_pci1723",
+ .id_table = adv_pci1723_pci_table,
+ .probe = adv_pci1723_pci_probe,
+ .remove = comedi_pci_auto_unconfig,
+};
+module_comedi_pci_driver(adv_pci1723_driver, adv_pci1723_pci_driver);
+
+MODULE_AUTHOR("Comedi http://www.comedi.org");
+MODULE_DESCRIPTION("Advantech PCI-1723 Comedi driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/comedi/drivers/adv_pci1724.c b/drivers/staging/comedi/drivers/adv_pci1724.c
new file mode 100644
index 000000000..f7a7dab01
--- /dev/null
+++ b/drivers/staging/comedi/drivers/adv_pci1724.c
@@ -0,0 +1,220 @@
+/*
+ * adv_pci1724.c
+ * Comedi driver for the Advantech PCI-1724U card.
+ *
+ * Author: Frank Mori Hess <fmh6jj@gmail.com>
+ * Copyright (C) 2013 GnuBIO Inc
+ *
+ * COMEDI - Linux Control and Measurement Device Interface
+ * Copyright (C) 1997-8 David A. Schleef <ds@schleef.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+/*
+ * Driver: adv_pci1724
+ * Description: Advantech PCI-1724U
+ * Devices: [Advantech] PCI-1724U (adv_pci1724)
+ * Author: Frank Mori Hess <fmh6jj@gmail.com>
+ * Updated: 2013-02-09
+ * Status: works
+ *
+ * Configuration Options: not applicable, uses comedi PCI auto config
+ *
+ * Subdevice 0 is the analog output.
+ * Subdevice 1 is the offset calibration for the analog output.
+ * Subdevice 2 is the gain calibration for the analog output.
+ *
+ * The calibration offset and gains have quite a large effect on the
+ * analog output, so it is possible to adjust the analog output to
+ * have an output range significantly different from the board's
+ * nominal output ranges. For a calibrated +/-10V range, the analog
+ * output's offset will be set somewhere near mid-range (0x2000) and
+ * its gain will be near maximum (0x3fff).
+ *
+ * There is really no difference between the board's documented 0-20mA
+ * versus 4-20mA output ranges. To pick one or the other is simply a
+ * matter of adjusting the offset and gain calibration until the board
+ * outputs in the desired range.
+ */
+
+#include <linux/module.h>
+
+#include "../comedi_pci.h"
+
+/*
+ * PCI bar 2 Register I/O map (dev->iobase)
+ */
+#define PCI1724_DAC_CTRL_REG 0x00
+#define PCI1724_DAC_CTRL_GX(x) (1 << (20 + ((x) / 8)))
+#define PCI1724_DAC_CTRL_CX(x) (((x) % 8) << 16)
+#define PCI1724_DAC_CTRL_MODE_GAIN (1 << 14)
+#define PCI1724_DAC_CTRL_MODE_OFFSET (2 << 14)
+#define PCI1724_DAC_CTRL_MODE_NORMAL (3 << 14)
+#define PCI1724_DAC_CTRL_MODE_MASK (3 << 14)
+#define PCI1724_DAC_CTRL_DATA(x) (((x) & 0x3fff) << 0)
+#define PCI1724_SYNC_CTRL_REG 0x04
+#define PCI1724_SYNC_CTRL_DACSTAT (1 << 1)
+#define PCI1724_SYNC_CTRL_SYN (1 << 0)
+#define PCI1724_EEPROM_CTRL_REG 0x08
+#define PCI1724_SYNC_TRIG_REG 0x0c /* any value works */
+#define PCI1724_BOARD_ID_REG 0x10
+#define PCI1724_BOARD_ID_MASK (0xf << 0)
+
+static const struct comedi_lrange adv_pci1724_ao_ranges = {
+ 4, {
+ BIP_RANGE(10),
+ RANGE_mA(0, 20),
+ RANGE_mA(4, 20),
+ RANGE_unitless(0, 1)
+ }
+};
+
+static int adv_pci1724_dac_idle(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned long context)
+{
+ unsigned int status;
+
+ status = inl(dev->iobase + PCI1724_SYNC_CTRL_REG);
+ if ((status & PCI1724_SYNC_CTRL_DACSTAT) == 0)
+ return 0;
+ return -EBUSY;
+}
+
+static int adv_pci1724_insn_write(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ unsigned long mode = (unsigned long)s->private;
+ unsigned int chan = CR_CHAN(insn->chanspec);
+ unsigned int ctrl;
+ int ret;
+ int i;
+
+ ctrl = PCI1724_DAC_CTRL_GX(chan) | PCI1724_DAC_CTRL_CX(chan) | mode;
+
+ /* turn off synchronous mode */
+ outl(0, dev->iobase + PCI1724_SYNC_CTRL_REG);
+
+ for (i = 0; i < insn->n; ++i) {
+ unsigned int val = data[i];
+
+ ret = comedi_timeout(dev, s, insn, adv_pci1724_dac_idle, 0);
+ if (ret)
+ return ret;
+
+ outl(ctrl | PCI1724_DAC_CTRL_DATA(val),
+ dev->iobase + PCI1724_DAC_CTRL_REG);
+
+ s->readback[chan] = val;
+ }
+
+ return insn->n;
+}
+
+static int adv_pci1724_auto_attach(struct comedi_device *dev,
+ unsigned long context_unused)
+{
+ struct pci_dev *pcidev = comedi_to_pci_dev(dev);
+ struct comedi_subdevice *s;
+ unsigned int board_id;
+ int ret;
+
+ ret = comedi_pci_enable(dev);
+ if (ret)
+ return ret;
+
+ dev->iobase = pci_resource_start(pcidev, 2);
+ board_id = inl(dev->iobase + PCI1724_BOARD_ID_REG);
+ dev_info(dev->class_dev, "board id: %d\n",
+ board_id & PCI1724_BOARD_ID_MASK);
+
+ ret = comedi_alloc_subdevices(dev, 3);
+ if (ret)
+ return ret;
+
+ /* Analog Output subdevice */
+ s = &dev->subdevices[0];
+ s->type = COMEDI_SUBD_AO;
+ s->subdev_flags = SDF_READABLE | SDF_WRITABLE | SDF_GROUND;
+ s->n_chan = 32;
+ s->maxdata = 0x3fff;
+ s->range_table = &adv_pci1724_ao_ranges;
+ s->insn_write = adv_pci1724_insn_write;
+ s->private = (void *)PCI1724_DAC_CTRL_MODE_NORMAL;
+
+ ret = comedi_alloc_subdev_readback(s);
+ if (ret)
+ return ret;
+
+ /* Offset Calibration subdevice */
+ s = &dev->subdevices[1];
+ s->type = COMEDI_SUBD_CALIB;
+ s->subdev_flags = SDF_READABLE | SDF_WRITABLE | SDF_INTERNAL;
+ s->n_chan = 32;
+ s->maxdata = 0x3fff;
+ s->insn_write = adv_pci1724_insn_write;
+ s->private = (void *)PCI1724_DAC_CTRL_MODE_OFFSET;
+
+ ret = comedi_alloc_subdev_readback(s);
+ if (ret)
+ return ret;
+
+ /* Gain Calibration subdevice */
+ s = &dev->subdevices[2];
+ s->type = COMEDI_SUBD_CALIB;
+ s->subdev_flags = SDF_READABLE | SDF_WRITABLE | SDF_INTERNAL;
+ s->n_chan = 32;
+ s->maxdata = 0x3fff;
+ s->insn_write = adv_pci1724_insn_write;
+ s->private = (void *)PCI1724_DAC_CTRL_MODE_GAIN;
+
+ ret = comedi_alloc_subdev_readback(s);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static struct comedi_driver adv_pci1724_driver = {
+ .driver_name = "adv_pci1724",
+ .module = THIS_MODULE,
+ .auto_attach = adv_pci1724_auto_attach,
+ .detach = comedi_pci_detach,
+};
+
+static int adv_pci1724_pci_probe(struct pci_dev *dev,
+ const struct pci_device_id *id)
+{
+ return comedi_pci_auto_config(dev, &adv_pci1724_driver,
+ id->driver_data);
+}
+
+static const struct pci_device_id adv_pci1724_pci_table[] = {
+ { PCI_DEVICE(PCI_VENDOR_ID_ADVANTECH, 0x1724) },
+ { 0 }
+};
+MODULE_DEVICE_TABLE(pci, adv_pci1724_pci_table);
+
+static struct pci_driver adv_pci1724_pci_driver = {
+ .name = "adv_pci1724",
+ .id_table = adv_pci1724_pci_table,
+ .probe = adv_pci1724_pci_probe,
+ .remove = comedi_pci_auto_unconfig,
+};
+module_comedi_pci_driver(adv_pci1724_driver, adv_pci1724_pci_driver);
+
+MODULE_AUTHOR("Frank Mori Hess <fmh6jj@gmail.com>");
+MODULE_DESCRIPTION("Advantech PCI-1724U Comedi driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/comedi/drivers/adv_pci_dio.c b/drivers/staging/comedi/drivers/adv_pci_dio.c
new file mode 100644
index 000000000..456e87013
--- /dev/null
+++ b/drivers/staging/comedi/drivers/adv_pci_dio.c
@@ -0,0 +1,1113 @@
+/*
+ * comedi/drivers/adv_pci_dio.c
+ *
+ * Author: Michal Dobes <dobes@tesnet.cz>
+ *
+ * Hardware driver for Advantech PCI DIO cards.
+*/
+/*
+Driver: adv_pci_dio
+Description: Advantech PCI-1730, PCI-1733, PCI-1734, PCI-1735U,
+ PCI-1736UP, PCI-1739U, PCI-1750, PCI-1751, PCI-1752,
+ PCI-1753/E, PCI-1754, PCI-1756, PCI-1760, PCI-1762
+Author: Michal Dobes <dobes@tesnet.cz>
+Devices: [Advantech] PCI-1730 (adv_pci_dio), PCI-1733,
+ PCI-1734, PCI-1735U, PCI-1736UP, PCI-1739U, PCI-1750,
+ PCI-1751, PCI-1752, PCI-1753,
+ PCI-1753+PCI-1753E, PCI-1754, PCI-1756,
+ PCI-1760, PCI-1762
+Status: untested
+Updated: Mon, 09 Jan 2012 12:40:46 +0000
+
+This driver supports now only insn interface for DI/DO/DIO.
+
+Configuration options:
+ [0] - PCI bus of device (optional)
+ [1] - PCI slot of device (optional)
+ If bus/slot is not specified, the first available PCI
+ device will be used.
+
+*/
+
+#include <linux/module.h>
+#include <linux/delay.h>
+
+#include "../comedi_pci.h"
+
+#include "8255.h"
+#include "comedi_8254.h"
+
+/* hardware types of the cards */
+enum hw_cards_id {
+ TYPE_PCI1730, TYPE_PCI1733, TYPE_PCI1734, TYPE_PCI1735, TYPE_PCI1736,
+ TYPE_PCI1739,
+ TYPE_PCI1750,
+ TYPE_PCI1751,
+ TYPE_PCI1752,
+ TYPE_PCI1753, TYPE_PCI1753E,
+ TYPE_PCI1754, TYPE_PCI1756,
+ TYPE_PCI1760,
+ TYPE_PCI1762
+};
+
+/* which I/O instructions to use */
+enum hw_io_access {
+ IO_8b, IO_16b
+};
+
+#define MAX_DI_SUBDEVS 2 /* max number of DI subdevices per card */
+#define MAX_DO_SUBDEVS 2 /* max number of DO subdevices per card */
+#define MAX_DIO_SUBDEVG 2 /* max number of DIO subdevices group per
+ * card */
+
+#define PCIDIO_MAINREG 2 /* main I/O region for all Advantech cards? */
+
+/* Register offset definitions */
+/* Advantech PCI-1730/3/4 */
+#define PCI1730_IDI 0 /* R: Isolated digital input 0-15 */
+#define PCI1730_IDO 0 /* W: Isolated digital output 0-15 */
+#define PCI1730_DI 2 /* R: Digital input 0-15 */
+#define PCI1730_DO 2 /* W: Digital output 0-15 */
+#define PCI1733_IDI 0 /* R: Isolated digital input 0-31 */
+#define PCI1730_3_INT_EN 0x08 /* R/W: enable/disable interrupts */
+#define PCI1730_3_INT_RF 0x0c /* R/W: set falling/raising edge for
+ * interrupts */
+#define PCI1730_3_INT_CLR 0x10 /* R/W: clear interrupts */
+#define PCI1734_IDO 0 /* W: Isolated digital output 0-31 */
+#define PCI173x_BOARDID 4 /* R: Board I/D switch for 1730/3/4 */
+
+/* Advantech PCI-1735U */
+#define PCI1735_DI 0 /* R: Digital input 0-31 */
+#define PCI1735_DO 0 /* W: Digital output 0-31 */
+#define PCI1735_C8254 4 /* R/W: 8254 counter */
+#define PCI1735_BOARDID 8 /* R: Board I/D switch for 1735U */
+
+/* Advantech PCI-1736UP */
+#define PCI1736_IDI 0 /* R: Isolated digital input 0-15 */
+#define PCI1736_IDO 0 /* W: Isolated digital output 0-15 */
+#define PCI1736_3_INT_EN 0x08 /* R/W: enable/disable interrupts */
+#define PCI1736_3_INT_RF 0x0c /* R/W: set falling/raising edge for
+ * interrupts */
+#define PCI1736_3_INT_CLR 0x10 /* R/W: clear interrupts */
+#define PCI1736_BOARDID 4 /* R: Board I/D switch for 1736UP */
+#define PCI1736_MAINREG 0 /* Normal register (2) doesn't work */
+
+/* Advantech PCI-1739U */
+#define PCI1739_DIO 0 /* R/W: begin of 8255 registers block */
+#define PCI1739_ICR 32 /* W: Interrupt control register */
+#define PCI1739_ISR 32 /* R: Interrupt status register */
+#define PCI1739_BOARDID 8 /* R: Board I/D switch for 1739U */
+
+/* Advantech PCI-1750 */
+#define PCI1750_IDI 0 /* R: Isolated digital input 0-15 */
+#define PCI1750_IDO 0 /* W: Isolated digital output 0-15 */
+#define PCI1750_ICR 32 /* W: Interrupt control register */
+#define PCI1750_ISR 32 /* R: Interrupt status register */
+
+/* Advantech PCI-1751/3/3E */
+#define PCI1751_DIO 0 /* R/W: begin of 8255 registers block */
+#define PCI1751_CNT 24 /* R/W: begin of 8254 registers block */
+#define PCI1751_ICR 32 /* W: Interrupt control register */
+#define PCI1751_ISR 32 /* R: Interrupt status register */
+#define PCI1753_DIO 0 /* R/W: begin of 8255 registers block */
+#define PCI1753_ICR0 16 /* R/W: Interrupt control register group 0 */
+#define PCI1753_ICR1 17 /* R/W: Interrupt control register group 1 */
+#define PCI1753_ICR2 18 /* R/W: Interrupt control register group 2 */
+#define PCI1753_ICR3 19 /* R/W: Interrupt control register group 3 */
+#define PCI1753E_DIO 32 /* R/W: begin of 8255 registers block */
+#define PCI1753E_ICR0 48 /* R/W: Interrupt control register group 0 */
+#define PCI1753E_ICR1 49 /* R/W: Interrupt control register group 1 */
+#define PCI1753E_ICR2 50 /* R/W: Interrupt control register group 2 */
+#define PCI1753E_ICR3 51 /* R/W: Interrupt control register group 3 */
+
+/* Advantech PCI-1752/4/6 */
+#define PCI1752_IDO 0 /* R/W: Digital output 0-31 */
+#define PCI1752_IDO2 4 /* R/W: Digital output 32-63 */
+#define PCI1754_IDI 0 /* R: Digital input 0-31 */
+#define PCI1754_IDI2 4 /* R: Digital input 32-64 */
+#define PCI1756_IDI 0 /* R: Digital input 0-31 */
+#define PCI1756_IDO 4 /* R/W: Digital output 0-31 */
+#define PCI1754_6_ICR0 0x08 /* R/W: Interrupt control register group 0 */
+#define PCI1754_6_ICR1 0x0a /* R/W: Interrupt control register group 1 */
+#define PCI1754_ICR2 0x0c /* R/W: Interrupt control register group 2 */
+#define PCI1754_ICR3 0x0e /* R/W: Interrupt control register group 3 */
+#define PCI1752_6_CFC 0x12 /* R/W: set/read channel freeze function */
+#define PCI175x_BOARDID 0x10 /* R: Board I/D switch for 1752/4/6 */
+
+/* Advantech PCI-1762 registers */
+#define PCI1762_RO 0 /* R/W: Relays status/output */
+#define PCI1762_IDI 2 /* R: Isolated input status */
+#define PCI1762_BOARDID 4 /* R: Board I/D switch */
+#define PCI1762_ICR 6 /* W: Interrupt control register */
+#define PCI1762_ISR 6 /* R: Interrupt status register */
+
+/* Advantech PCI-1760 registers */
+#define OMB0 0x0c /* W: Mailbox outgoing registers */
+#define OMB1 0x0d
+#define OMB2 0x0e
+#define OMB3 0x0f
+#define IMB0 0x1c /* R: Mailbox incoming registers */
+#define IMB1 0x1d
+#define IMB2 0x1e
+#define IMB3 0x1f
+#define INTCSR0 0x38 /* R/W: Interrupt control registers */
+#define INTCSR1 0x39
+#define INTCSR2 0x3a
+#define INTCSR3 0x3b
+
+/* PCI-1760 mailbox commands */
+#define CMD_ClearIMB2 0x00 /* Clear IMB2 status and return actual
+ * DI status in IMB3 */
+#define CMD_SetRelaysOutput 0x01 /* Set relay output from OMB0 */
+#define CMD_GetRelaysStatus 0x02 /* Get relay status to IMB0 */
+#define CMD_ReadCurrentStatus 0x07 /* Read the current status of the
+ * register in OMB0, result in IMB0 */
+#define CMD_ReadFirmwareVersion 0x0e /* Read the firmware ver., result in
+ * IMB1.IMB0 */
+#define CMD_ReadHardwareVersion 0x0f /* Read the hardware ver., result in
+ * IMB1.IMB0 */
+#define CMD_EnableIDIFilters 0x20 /* Enable IDI filters based on bits in
+ * OMB0 */
+#define CMD_EnableIDIPatternMatch 0x21 /* Enable IDI pattern match based on
+ * bits in OMB0 */
+#define CMD_SetIDIPatternMatch 0x22 /* Enable IDI pattern match based on
+ * bits in OMB0 */
+#define CMD_EnableIDICounters 0x28 /* Enable IDI counters based on bits in
+ * OMB0 */
+#define CMD_ResetIDICounters 0x29 /* Reset IDI counters based on bits in
+ * OMB0 to its reset values */
+#define CMD_OverflowIDICounters 0x2a /* Enable IDI counters overflow
+ * interrupts based on bits in OMB0 */
+#define CMD_MatchIntIDICounters 0x2b /* Enable IDI counters match value
+ * interrupts based on bits in OMB0 */
+#define CMD_EdgeIDICounters 0x2c /* Set IDI up counters count edge (bit=0
+ * - rising, =1 - falling) */
+#define CMD_GetIDICntCurValue 0x2f /* Read IDI{OMB0} up counter current
+ * value */
+#define CMD_SetIDI0CntResetValue 0x40 /* Set IDI0 Counter Reset Value
+ * 256*OMB1+OMB0 */
+#define CMD_SetIDI1CntResetValue 0x41 /* Set IDI1 Counter Reset Value
+ * 256*OMB1+OMB0 */
+#define CMD_SetIDI2CntResetValue 0x42 /* Set IDI2 Counter Reset Value
+ * 256*OMB1+OMB0 */
+#define CMD_SetIDI3CntResetValue 0x43 /* Set IDI3 Counter Reset Value
+ * 256*OMB1+OMB0 */
+#define CMD_SetIDI4CntResetValue 0x44 /* Set IDI4 Counter Reset Value
+ * 256*OMB1+OMB0 */
+#define CMD_SetIDI5CntResetValue 0x45 /* Set IDI5 Counter Reset Value
+ * 256*OMB1+OMB0 */
+#define CMD_SetIDI6CntResetValue 0x46 /* Set IDI6 Counter Reset Value
+ * 256*OMB1+OMB0 */
+#define CMD_SetIDI7CntResetValue 0x47 /* Set IDI7 Counter Reset Value
+ * 256*OMB1+OMB0 */
+#define CMD_SetIDI0CntMatchValue 0x48 /* Set IDI0 Counter Match Value
+ * 256*OMB1+OMB0 */
+#define CMD_SetIDI1CntMatchValue 0x49 /* Set IDI1 Counter Match Value
+ * 256*OMB1+OMB0 */
+#define CMD_SetIDI2CntMatchValue 0x4a /* Set IDI2 Counter Match Value
+ * 256*OMB1+OMB0 */
+#define CMD_SetIDI3CntMatchValue 0x4b /* Set IDI3 Counter Match Value
+ * 256*OMB1+OMB0 */
+#define CMD_SetIDI4CntMatchValue 0x4c /* Set IDI4 Counter Match Value
+ * 256*OMB1+OMB0 */
+#define CMD_SetIDI5CntMatchValue 0x4d /* Set IDI5 Counter Match Value
+ * 256*OMB1+OMB0 */
+#define CMD_SetIDI6CntMatchValue 0x4e /* Set IDI6 Counter Match Value
+ * 256*OMB1+OMB0 */
+#define CMD_SetIDI7CntMatchValue 0x4f /* Set IDI7 Counter Match Value
+ * 256*OMB1+OMB0 */
+
+#define OMBCMD_RETRY 0x03 /* 3 times try request before error */
+
+struct diosubd_data {
+ int chans; /* num of chans */
+ int addr; /* PCI address ofset */
+ int regs; /* number of registers to read or 8255
+ subdevices */
+ unsigned int specflags; /* addon subdevice flags */
+};
+
+struct dio_boardtype {
+ const char *name; /* board name */
+ int main_pci_region; /* main I/O PCI region */
+ enum hw_cards_id cardtype;
+ int nsubdevs;
+ struct diosubd_data sdi[MAX_DI_SUBDEVS]; /* DI chans */
+ struct diosubd_data sdo[MAX_DO_SUBDEVS]; /* DO chans */
+ struct diosubd_data sdio[MAX_DIO_SUBDEVG]; /* DIO 8255 chans */
+ struct diosubd_data boardid; /* card supports board ID switch */
+ unsigned long timer_regbase;
+ enum hw_io_access io_access;
+};
+
+static const struct dio_boardtype boardtypes[] = {
+ [TYPE_PCI1730] = {
+ .name = "pci1730",
+ .main_pci_region = PCIDIO_MAINREG,
+ .cardtype = TYPE_PCI1730,
+ .nsubdevs = 5,
+ .sdi[0] = { 16, PCI1730_DI, 2, 0, },
+ .sdi[1] = { 16, PCI1730_IDI, 2, 0, },
+ .sdo[0] = { 16, PCI1730_DO, 2, 0, },
+ .sdo[1] = { 16, PCI1730_IDO, 2, 0, },
+ .boardid = { 4, PCI173x_BOARDID, 1, SDF_INTERNAL, },
+ .io_access = IO_8b,
+ },
+ [TYPE_PCI1733] = {
+ .name = "pci1733",
+ .main_pci_region = PCIDIO_MAINREG,
+ .cardtype = TYPE_PCI1733,
+ .nsubdevs = 2,
+ .sdi[1] = { 32, PCI1733_IDI, 4, 0, },
+ .boardid = { 4, PCI173x_BOARDID, 1, SDF_INTERNAL, },
+ .io_access = IO_8b,
+ },
+ [TYPE_PCI1734] = {
+ .name = "pci1734",
+ .main_pci_region = PCIDIO_MAINREG,
+ .cardtype = TYPE_PCI1734,
+ .nsubdevs = 2,
+ .sdo[1] = { 32, PCI1734_IDO, 4, 0, },
+ .boardid = { 4, PCI173x_BOARDID, 1, SDF_INTERNAL, },
+ .io_access = IO_8b,
+ },
+ [TYPE_PCI1735] = {
+ .name = "pci1735",
+ .main_pci_region = PCIDIO_MAINREG,
+ .cardtype = TYPE_PCI1735,
+ .nsubdevs = 4,
+ .sdi[0] = { 32, PCI1735_DI, 4, 0, },
+ .sdo[0] = { 32, PCI1735_DO, 4, 0, },
+ .boardid = { 4, PCI1735_BOARDID, 1, SDF_INTERNAL, },
+ .timer_regbase = PCI1735_C8254,
+ .io_access = IO_8b,
+ },
+ [TYPE_PCI1736] = {
+ .name = "pci1736",
+ .main_pci_region = PCI1736_MAINREG,
+ .cardtype = TYPE_PCI1736,
+ .nsubdevs = 3,
+ .sdi[1] = { 16, PCI1736_IDI, 2, 0, },
+ .sdo[1] = { 16, PCI1736_IDO, 2, 0, },
+ .boardid = { 4, PCI1736_BOARDID, 1, SDF_INTERNAL, },
+ .io_access = IO_8b,
+ },
+ [TYPE_PCI1739] = {
+ .name = "pci1739",
+ .main_pci_region = PCIDIO_MAINREG,
+ .cardtype = TYPE_PCI1739,
+ .nsubdevs = 2,
+ .sdio[0] = { 48, PCI1739_DIO, 2, 0, },
+ .io_access = IO_8b,
+ },
+ [TYPE_PCI1750] = {
+ .name = "pci1750",
+ .main_pci_region = PCIDIO_MAINREG,
+ .cardtype = TYPE_PCI1750,
+ .nsubdevs = 2,
+ .sdi[1] = { 16, PCI1750_IDI, 2, 0, },
+ .sdo[1] = { 16, PCI1750_IDO, 2, 0, },
+ .io_access = IO_8b,
+ },
+ [TYPE_PCI1751] = {
+ .name = "pci1751",
+ .main_pci_region = PCIDIO_MAINREG,
+ .cardtype = TYPE_PCI1751,
+ .nsubdevs = 3,
+ .sdio[0] = { 48, PCI1751_DIO, 2, 0, },
+ .timer_regbase = PCI1751_CNT,
+ .io_access = IO_8b,
+ },
+ [TYPE_PCI1752] = {
+ .name = "pci1752",
+ .main_pci_region = PCIDIO_MAINREG,
+ .cardtype = TYPE_PCI1752,
+ .nsubdevs = 3,
+ .sdo[0] = { 32, PCI1752_IDO, 2, 0, },
+ .sdo[1] = { 32, PCI1752_IDO2, 2, 0, },
+ .boardid = { 4, PCI175x_BOARDID, 1, SDF_INTERNAL, },
+ .io_access = IO_16b,
+ },
+ [TYPE_PCI1753] = {
+ .name = "pci1753",
+ .main_pci_region = PCIDIO_MAINREG,
+ .cardtype = TYPE_PCI1753,
+ .nsubdevs = 4,
+ .sdio[0] = { 96, PCI1753_DIO, 4, 0, },
+ .io_access = IO_8b,
+ },
+ [TYPE_PCI1753E] = {
+ .name = "pci1753e",
+ .main_pci_region = PCIDIO_MAINREG,
+ .cardtype = TYPE_PCI1753E,
+ .nsubdevs = 8,
+ .sdio[0] = { 96, PCI1753_DIO, 4, 0, },
+ .sdio[1] = { 96, PCI1753E_DIO, 4, 0, },
+ .io_access = IO_8b,
+ },
+ [TYPE_PCI1754] = {
+ .name = "pci1754",
+ .main_pci_region = PCIDIO_MAINREG,
+ .cardtype = TYPE_PCI1754,
+ .nsubdevs = 3,
+ .sdi[0] = { 32, PCI1754_IDI, 2, 0, },
+ .sdi[1] = { 32, PCI1754_IDI2, 2, 0, },
+ .boardid = { 4, PCI175x_BOARDID, 1, SDF_INTERNAL, },
+ .io_access = IO_16b,
+ },
+ [TYPE_PCI1756] = {
+ .name = "pci1756",
+ .main_pci_region = PCIDIO_MAINREG,
+ .cardtype = TYPE_PCI1756,
+ .nsubdevs = 3,
+ .sdi[1] = { 32, PCI1756_IDI, 2, 0, },
+ .sdo[1] = { 32, PCI1756_IDO, 2, 0, },
+ .boardid = { 4, PCI175x_BOARDID, 1, SDF_INTERNAL, },
+ .io_access = IO_16b,
+ },
+ [TYPE_PCI1760] = {
+ /* This card has its own 'attach' */
+ .name = "pci1760",
+ .main_pci_region = 0,
+ .cardtype = TYPE_PCI1760,
+ .nsubdevs = 4,
+ .io_access = IO_8b,
+ },
+ [TYPE_PCI1762] = {
+ .name = "pci1762",
+ .main_pci_region = PCIDIO_MAINREG,
+ .cardtype = TYPE_PCI1762,
+ .nsubdevs = 3,
+ .sdi[1] = { 16, PCI1762_IDI, 1, 0, },
+ .sdo[1] = { 16, PCI1762_RO, 1, 0, },
+ .boardid = { 4, PCI1762_BOARDID, 1, SDF_INTERNAL, },
+ .io_access = IO_16b,
+ },
+};
+
+struct pci_dio_private {
+ char GlobalIrqEnabled; /* 1= any IRQ source is enabled */
+ /* PCI-1760 specific data */
+ unsigned char IDICntEnable; /* counter's counting enable status */
+ unsigned char IDICntOverEnable; /* counter's overflow interrupts enable
+ * status */
+ unsigned char IDICntMatchEnable; /* counter's match interrupts
+ * enable status */
+ unsigned char IDICntEdge; /* counter's count edge value
+ * (bit=0 - rising, =1 - falling) */
+ unsigned short CntResValue[8]; /* counters' reset value */
+ unsigned short CntMatchValue[8]; /* counters' match interrupt value */
+ unsigned char IDIFiltersEn; /* IDI's digital filters enable status */
+ unsigned char IDIPatMatchEn; /* IDI's pattern match enable status */
+ unsigned char IDIPatMatchValue; /* IDI's pattern match value */
+ unsigned short IDIFiltrLow[8]; /* IDI's filter value low signal */
+ unsigned short IDIFiltrHigh[8]; /* IDI's filter value high signal */
+};
+
+/*
+==============================================================================
+*/
+static int pci_dio_insn_bits_di_b(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn, unsigned int *data)
+{
+ const struct diosubd_data *d = (const struct diosubd_data *)s->private;
+ int i;
+
+ data[1] = 0;
+ for (i = 0; i < d->regs; i++)
+ data[1] |= inb(dev->iobase + d->addr + i) << (8 * i);
+
+ return insn->n;
+}
+
+/*
+==============================================================================
+*/
+static int pci_dio_insn_bits_di_w(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn, unsigned int *data)
+{
+ const struct diosubd_data *d = (const struct diosubd_data *)s->private;
+ int i;
+
+ data[1] = 0;
+ for (i = 0; i < d->regs; i++)
+ data[1] |= inw(dev->iobase + d->addr + 2 * i) << (16 * i);
+
+ return insn->n;
+}
+
+static int pci_dio_insn_bits_do_b(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ const struct diosubd_data *d = (const struct diosubd_data *)s->private;
+ int i;
+
+ if (comedi_dio_update_state(s, data)) {
+ for (i = 0; i < d->regs; i++)
+ outb((s->state >> (8 * i)) & 0xff,
+ dev->iobase + d->addr + i);
+ }
+
+ data[1] = s->state;
+
+ return insn->n;
+}
+
+static int pci_dio_insn_bits_do_w(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ const struct diosubd_data *d = (const struct diosubd_data *)s->private;
+ int i;
+
+ if (comedi_dio_update_state(s, data)) {
+ for (i = 0; i < d->regs; i++)
+ outw((s->state >> (16 * i)) & 0xffff,
+ dev->iobase + d->addr + 2 * i);
+ }
+
+ data[1] = s->state;
+
+ return insn->n;
+}
+
+/*
+==============================================================================
+*/
+static int pci1760_unchecked_mbxrequest(struct comedi_device *dev,
+ unsigned char *omb, unsigned char *imb,
+ int repeats)
+{
+ int cnt, tout, ok = 0;
+
+ for (cnt = 0; cnt < repeats; cnt++) {
+ outb(omb[0], dev->iobase + OMB0);
+ outb(omb[1], dev->iobase + OMB1);
+ outb(omb[2], dev->iobase + OMB2);
+ outb(omb[3], dev->iobase + OMB3);
+ for (tout = 0; tout < 251; tout++) {
+ imb[2] = inb(dev->iobase + IMB2);
+ if (imb[2] == omb[2]) {
+ imb[0] = inb(dev->iobase + IMB0);
+ imb[1] = inb(dev->iobase + IMB1);
+ imb[3] = inb(dev->iobase + IMB3);
+ ok = 1;
+ break;
+ }
+ udelay(1);
+ }
+ if (ok)
+ return 0;
+ }
+
+ dev_err(dev->class_dev, "PCI-1760 mailbox request timeout!\n");
+ return -ETIME;
+}
+
+static int pci1760_clear_imb2(struct comedi_device *dev)
+{
+ unsigned char omb[4] = { 0x0, 0x0, CMD_ClearIMB2, 0x0 };
+ unsigned char imb[4];
+ /* check if imb2 is already clear */
+ if (inb(dev->iobase + IMB2) == CMD_ClearIMB2)
+ return 0;
+ return pci1760_unchecked_mbxrequest(dev, omb, imb, OMBCMD_RETRY);
+}
+
+static int pci1760_mbxrequest(struct comedi_device *dev,
+ unsigned char *omb, unsigned char *imb)
+{
+ if (omb[2] == CMD_ClearIMB2) {
+ dev_err(dev->class_dev,
+ "bug! this function should not be used for CMD_ClearIMB2 command\n");
+ return -EINVAL;
+ }
+ if (inb(dev->iobase + IMB2) == omb[2]) {
+ int retval;
+
+ retval = pci1760_clear_imb2(dev);
+ if (retval < 0)
+ return retval;
+ }
+ return pci1760_unchecked_mbxrequest(dev, omb, imb, OMBCMD_RETRY);
+}
+
+/*
+==============================================================================
+*/
+static int pci1760_insn_bits_di(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn, unsigned int *data)
+{
+ data[1] = inb(dev->iobase + IMB3);
+
+ return insn->n;
+}
+
+static int pci1760_insn_bits_do(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ int ret;
+ unsigned char omb[4] = {
+ 0x00,
+ 0x00,
+ CMD_SetRelaysOutput,
+ 0x00
+ };
+ unsigned char imb[4];
+
+ if (comedi_dio_update_state(s, data)) {
+ omb[0] = s->state;
+ ret = pci1760_mbxrequest(dev, omb, imb);
+ if (!ret)
+ return ret;
+ }
+
+ data[1] = s->state;
+
+ return insn->n;
+}
+
+/*
+==============================================================================
+*/
+static int pci1760_insn_cnt_read(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn, unsigned int *data)
+{
+ int ret, n;
+ unsigned char omb[4] = {
+ CR_CHAN(insn->chanspec) & 0x07,
+ 0x00,
+ CMD_GetIDICntCurValue,
+ 0x00
+ };
+ unsigned char imb[4];
+
+ for (n = 0; n < insn->n; n++) {
+ ret = pci1760_mbxrequest(dev, omb, imb);
+ if (!ret)
+ return ret;
+ data[n] = (imb[1] << 8) + imb[0];
+ }
+
+ return n;
+}
+
+/*
+==============================================================================
+*/
+static int pci1760_insn_cnt_write(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn, unsigned int *data)
+{
+ struct pci_dio_private *devpriv = dev->private;
+ int ret;
+ unsigned char chan = CR_CHAN(insn->chanspec) & 0x07;
+ unsigned char bitmask = 1 << chan;
+ unsigned char omb[4] = {
+ data[0] & 0xff,
+ (data[0] >> 8) & 0xff,
+ CMD_SetIDI0CntResetValue + chan,
+ 0x00
+ };
+ unsigned char imb[4];
+
+ /* Set reset value if different */
+ if (devpriv->CntResValue[chan] != (data[0] & 0xffff)) {
+ ret = pci1760_mbxrequest(dev, omb, imb);
+ if (!ret)
+ return ret;
+ devpriv->CntResValue[chan] = data[0] & 0xffff;
+ }
+
+ omb[0] = bitmask; /* reset counter to it reset value */
+ omb[2] = CMD_ResetIDICounters;
+ ret = pci1760_mbxrequest(dev, omb, imb);
+ if (!ret)
+ return ret;
+
+ /* start counter if it don't run */
+ if (!(bitmask & devpriv->IDICntEnable)) {
+ omb[0] = bitmask;
+ omb[2] = CMD_EnableIDICounters;
+ ret = pci1760_mbxrequest(dev, omb, imb);
+ if (!ret)
+ return ret;
+ devpriv->IDICntEnable |= bitmask;
+ }
+ return 1;
+}
+
+/*
+==============================================================================
+*/
+static int pci1760_reset(struct comedi_device *dev)
+{
+ struct pci_dio_private *devpriv = dev->private;
+ int i;
+ unsigned char omb[4] = { 0x00, 0x00, 0x00, 0x00 };
+ unsigned char imb[4];
+
+ outb(0, dev->iobase + INTCSR0); /* disable IRQ */
+ outb(0, dev->iobase + INTCSR1);
+ outb(0, dev->iobase + INTCSR2);
+ outb(0, dev->iobase + INTCSR3);
+ devpriv->GlobalIrqEnabled = 0;
+
+ omb[0] = 0x00;
+ omb[2] = CMD_SetRelaysOutput; /* reset relay outputs */
+ pci1760_mbxrequest(dev, omb, imb);
+
+ omb[0] = 0x00;
+ omb[2] = CMD_EnableIDICounters; /* disable IDI up counters */
+ pci1760_mbxrequest(dev, omb, imb);
+ devpriv->IDICntEnable = 0;
+
+ omb[0] = 0x00;
+ omb[2] = CMD_OverflowIDICounters; /* disable counters overflow
+ * interrupts */
+ pci1760_mbxrequest(dev, omb, imb);
+ devpriv->IDICntOverEnable = 0;
+
+ omb[0] = 0x00;
+ omb[2] = CMD_MatchIntIDICounters; /* disable counters match value
+ * interrupts */
+ pci1760_mbxrequest(dev, omb, imb);
+ devpriv->IDICntMatchEnable = 0;
+
+ omb[0] = 0x00;
+ omb[1] = 0x80;
+ for (i = 0; i < 8; i++) { /* set IDI up counters match value */
+ omb[2] = CMD_SetIDI0CntMatchValue + i;
+ pci1760_mbxrequest(dev, omb, imb);
+ devpriv->CntMatchValue[i] = 0x8000;
+ }
+
+ omb[0] = 0x00;
+ omb[1] = 0x00;
+ for (i = 0; i < 8; i++) { /* set IDI up counters reset value */
+ omb[2] = CMD_SetIDI0CntResetValue + i;
+ pci1760_mbxrequest(dev, omb, imb);
+ devpriv->CntResValue[i] = 0x0000;
+ }
+
+ omb[0] = 0xff;
+ omb[2] = CMD_ResetIDICounters; /* reset IDI up counters to reset
+ * values */
+ pci1760_mbxrequest(dev, omb, imb);
+
+ omb[0] = 0x00;
+ omb[2] = CMD_EdgeIDICounters; /* set IDI up counters count edge */
+ pci1760_mbxrequest(dev, omb, imb);
+ devpriv->IDICntEdge = 0x00;
+
+ omb[0] = 0x00;
+ omb[2] = CMD_EnableIDIFilters; /* disable all digital in filters */
+ pci1760_mbxrequest(dev, omb, imb);
+ devpriv->IDIFiltersEn = 0x00;
+
+ omb[0] = 0x00;
+ omb[2] = CMD_EnableIDIPatternMatch; /* disable pattern matching */
+ pci1760_mbxrequest(dev, omb, imb);
+ devpriv->IDIPatMatchEn = 0x00;
+
+ omb[0] = 0x00;
+ omb[2] = CMD_SetIDIPatternMatch; /* set pattern match value */
+ pci1760_mbxrequest(dev, omb, imb);
+ devpriv->IDIPatMatchValue = 0x00;
+
+ return 0;
+}
+
+/*
+==============================================================================
+*/
+static int pci_dio_reset(struct comedi_device *dev)
+{
+ const struct dio_boardtype *this_board = dev->board_ptr;
+
+ switch (this_board->cardtype) {
+ case TYPE_PCI1730:
+ outb(0, dev->iobase + PCI1730_DO); /* clear outputs */
+ outb(0, dev->iobase + PCI1730_DO + 1);
+ outb(0, dev->iobase + PCI1730_IDO);
+ outb(0, dev->iobase + PCI1730_IDO + 1);
+ /* fallthrough */
+ case TYPE_PCI1733:
+ /* disable interrupts */
+ outb(0, dev->iobase + PCI1730_3_INT_EN);
+ /* clear interrupts */
+ outb(0x0f, dev->iobase + PCI1730_3_INT_CLR);
+ /* set rising edge trigger */
+ outb(0, dev->iobase + PCI1730_3_INT_RF);
+ break;
+ case TYPE_PCI1734:
+ outb(0, dev->iobase + PCI1734_IDO); /* clear outputs */
+ outb(0, dev->iobase + PCI1734_IDO + 1);
+ outb(0, dev->iobase + PCI1734_IDO + 2);
+ outb(0, dev->iobase + PCI1734_IDO + 3);
+ break;
+ case TYPE_PCI1735:
+ outb(0, dev->iobase + PCI1735_DO); /* clear outputs */
+ outb(0, dev->iobase + PCI1735_DO + 1);
+ outb(0, dev->iobase + PCI1735_DO + 2);
+ outb(0, dev->iobase + PCI1735_DO + 3);
+ break;
+
+ case TYPE_PCI1736:
+ outb(0, dev->iobase + PCI1736_IDO);
+ outb(0, dev->iobase + PCI1736_IDO + 1);
+ /* disable interrupts */
+ outb(0, dev->iobase + PCI1736_3_INT_EN);
+ /* clear interrupts */
+ outb(0x0f, dev->iobase + PCI1736_3_INT_CLR);
+ /* set rising edge trigger */
+ outb(0, dev->iobase + PCI1736_3_INT_RF);
+ break;
+
+ case TYPE_PCI1739:
+ /* disable & clear interrupts */
+ outb(0x88, dev->iobase + PCI1739_ICR);
+ break;
+
+ case TYPE_PCI1750:
+ case TYPE_PCI1751:
+ /* disable & clear interrupts */
+ outb(0x88, dev->iobase + PCI1750_ICR);
+ break;
+ case TYPE_PCI1752:
+ outw(0, dev->iobase + PCI1752_6_CFC); /* disable channel freeze
+ * function */
+ outw(0, dev->iobase + PCI1752_IDO); /* clear outputs */
+ outw(0, dev->iobase + PCI1752_IDO + 2);
+ outw(0, dev->iobase + PCI1752_IDO2);
+ outw(0, dev->iobase + PCI1752_IDO2 + 2);
+ break;
+ case TYPE_PCI1753E:
+ outb(0x88, dev->iobase + PCI1753E_ICR0); /* disable & clear
+ * interrupts */
+ outb(0x80, dev->iobase + PCI1753E_ICR1);
+ outb(0x80, dev->iobase + PCI1753E_ICR2);
+ outb(0x80, dev->iobase + PCI1753E_ICR3);
+ /* fallthrough */
+ case TYPE_PCI1753:
+ outb(0x88, dev->iobase + PCI1753_ICR0); /* disable & clear
+ * interrupts */
+ outb(0x80, dev->iobase + PCI1753_ICR1);
+ outb(0x80, dev->iobase + PCI1753_ICR2);
+ outb(0x80, dev->iobase + PCI1753_ICR3);
+ break;
+ case TYPE_PCI1754:
+ outw(0x08, dev->iobase + PCI1754_6_ICR0); /* disable and clear
+ * interrupts */
+ outw(0x08, dev->iobase + PCI1754_6_ICR1);
+ outw(0x08, dev->iobase + PCI1754_ICR2);
+ outw(0x08, dev->iobase + PCI1754_ICR3);
+ break;
+ case TYPE_PCI1756:
+ outw(0, dev->iobase + PCI1752_6_CFC); /* disable channel freeze
+ * function */
+ outw(0x08, dev->iobase + PCI1754_6_ICR0); /* disable and clear
+ * interrupts */
+ outw(0x08, dev->iobase + PCI1754_6_ICR1);
+ outw(0, dev->iobase + PCI1756_IDO); /* clear outputs */
+ outw(0, dev->iobase + PCI1756_IDO + 2);
+ break;
+ case TYPE_PCI1760:
+ pci1760_reset(dev);
+ break;
+ case TYPE_PCI1762:
+ outw(0x0101, dev->iobase + PCI1762_ICR); /* disable & clear
+ * interrupts */
+ break;
+ }
+
+ return 0;
+}
+
+/*
+==============================================================================
+*/
+static int pci1760_attach(struct comedi_device *dev)
+{
+ struct comedi_subdevice *s;
+
+ s = &dev->subdevices[0];
+ s->type = COMEDI_SUBD_DI;
+ s->subdev_flags = SDF_READABLE;
+ s->n_chan = 8;
+ s->maxdata = 1;
+ s->len_chanlist = 8;
+ s->range_table = &range_digital;
+ s->insn_bits = pci1760_insn_bits_di;
+
+ s = &dev->subdevices[1];
+ s->type = COMEDI_SUBD_DO;
+ s->subdev_flags = SDF_WRITABLE;
+ s->n_chan = 8;
+ s->maxdata = 1;
+ s->len_chanlist = 8;
+ s->range_table = &range_digital;
+ s->state = 0;
+ s->insn_bits = pci1760_insn_bits_do;
+
+ s = &dev->subdevices[2];
+ s->type = COMEDI_SUBD_TIMER;
+ s->subdev_flags = SDF_WRITABLE | SDF_LSAMPL;
+ s->n_chan = 2;
+ s->maxdata = 0xffffffff;
+ s->len_chanlist = 2;
+/* s->insn_config=pci1760_insn_pwm_cfg; */
+
+ s = &dev->subdevices[3];
+ s->type = COMEDI_SUBD_COUNTER;
+ s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
+ s->n_chan = 8;
+ s->maxdata = 0xffff;
+ s->len_chanlist = 8;
+ s->insn_read = pci1760_insn_cnt_read;
+ s->insn_write = pci1760_insn_cnt_write;
+/* s->insn_config=pci1760_insn_cnt_cfg; */
+
+ return 0;
+}
+
+/*
+==============================================================================
+*/
+static int pci_dio_add_di(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ const struct diosubd_data *d)
+{
+ const struct dio_boardtype *this_board = dev->board_ptr;
+
+ s->type = COMEDI_SUBD_DI;
+ s->subdev_flags = SDF_READABLE | d->specflags;
+ if (d->chans > 16)
+ s->subdev_flags |= SDF_LSAMPL;
+ s->n_chan = d->chans;
+ s->maxdata = 1;
+ s->len_chanlist = d->chans;
+ s->range_table = &range_digital;
+ switch (this_board->io_access) {
+ case IO_8b:
+ s->insn_bits = pci_dio_insn_bits_di_b;
+ break;
+ case IO_16b:
+ s->insn_bits = pci_dio_insn_bits_di_w;
+ break;
+ }
+ s->private = (void *)d;
+
+ return 0;
+}
+
+/*
+==============================================================================
+*/
+static int pci_dio_add_do(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ const struct diosubd_data *d)
+{
+ const struct dio_boardtype *this_board = dev->board_ptr;
+
+ s->type = COMEDI_SUBD_DO;
+ s->subdev_flags = SDF_WRITABLE;
+ if (d->chans > 16)
+ s->subdev_flags |= SDF_LSAMPL;
+ s->n_chan = d->chans;
+ s->maxdata = 1;
+ s->len_chanlist = d->chans;
+ s->range_table = &range_digital;
+ s->state = 0;
+ switch (this_board->io_access) {
+ case IO_8b:
+ s->insn_bits = pci_dio_insn_bits_do_b;
+ break;
+ case IO_16b:
+ s->insn_bits = pci_dio_insn_bits_do_w;
+ break;
+ }
+ s->private = (void *)d;
+
+ return 0;
+}
+
+static unsigned long pci_dio_override_cardtype(struct pci_dev *pcidev,
+ unsigned long cardtype)
+{
+ /*
+ * Change cardtype from TYPE_PCI1753 to TYPE_PCI1753E if expansion
+ * board available. Need to enable PCI device and request the main
+ * registers PCI BAR temporarily to perform the test.
+ */
+ if (cardtype != TYPE_PCI1753)
+ return cardtype;
+ if (pci_enable_device(pcidev) < 0)
+ return cardtype;
+ if (pci_request_region(pcidev, PCIDIO_MAINREG, "adv_pci_dio") == 0) {
+ /*
+ * This test is based on Advantech's "advdaq" driver source
+ * (which declares its module licence as "GPL" although the
+ * driver source does not include a "COPYING" file).
+ */
+ unsigned long reg =
+ pci_resource_start(pcidev, PCIDIO_MAINREG) + 53;
+
+ outb(0x05, reg);
+ if ((inb(reg) & 0x07) == 0x02) {
+ outb(0x02, reg);
+ if ((inb(reg) & 0x07) == 0x05)
+ cardtype = TYPE_PCI1753E;
+ }
+ pci_release_region(pcidev, PCIDIO_MAINREG);
+ }
+ pci_disable_device(pcidev);
+ return cardtype;
+}
+
+static int pci_dio_auto_attach(struct comedi_device *dev,
+ unsigned long context)
+{
+ struct pci_dev *pcidev = comedi_to_pci_dev(dev);
+ const struct dio_boardtype *this_board = NULL;
+ struct pci_dio_private *devpriv;
+ struct comedi_subdevice *s;
+ int ret, subdev, i, j;
+
+ if (context < ARRAY_SIZE(boardtypes))
+ this_board = &boardtypes[context];
+ if (!this_board)
+ return -ENODEV;
+ dev->board_ptr = this_board;
+ dev->board_name = this_board->name;
+
+ devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
+ if (!devpriv)
+ return -ENOMEM;
+
+ ret = comedi_pci_enable(dev);
+ if (ret)
+ return ret;
+ dev->iobase = pci_resource_start(pcidev, this_board->main_pci_region);
+
+ ret = comedi_alloc_subdevices(dev, this_board->nsubdevs);
+ if (ret)
+ return ret;
+
+ subdev = 0;
+ for (i = 0; i < MAX_DI_SUBDEVS; i++)
+ if (this_board->sdi[i].chans) {
+ s = &dev->subdevices[subdev];
+ pci_dio_add_di(dev, s, &this_board->sdi[i]);
+ subdev++;
+ }
+
+ for (i = 0; i < MAX_DO_SUBDEVS; i++)
+ if (this_board->sdo[i].chans) {
+ s = &dev->subdevices[subdev];
+ pci_dio_add_do(dev, s, &this_board->sdo[i]);
+ subdev++;
+ }
+
+ for (i = 0; i < MAX_DIO_SUBDEVG; i++)
+ for (j = 0; j < this_board->sdio[i].regs; j++) {
+ s = &dev->subdevices[subdev];
+ ret = subdev_8255_init(dev, s, NULL,
+ this_board->sdio[i].addr +
+ j * I8255_SIZE);
+ if (ret)
+ return ret;
+ subdev++;
+ }
+
+ if (this_board->boardid.chans) {
+ s = &dev->subdevices[subdev];
+ s->type = COMEDI_SUBD_DI;
+ pci_dio_add_di(dev, s, &this_board->boardid);
+ subdev++;
+ }
+
+ if (this_board->timer_regbase) {
+ s = &dev->subdevices[subdev];
+
+ dev->pacer = comedi_8254_init(dev->iobase +
+ this_board->timer_regbase,
+ 0, I8254_IO8, 0);
+ if (!dev->pacer)
+ return -ENOMEM;
+
+ comedi_8254_subdevice_init(s, dev->pacer);
+
+ subdev++;
+ }
+
+ if (this_board->cardtype == TYPE_PCI1760)
+ pci1760_attach(dev);
+
+ pci_dio_reset(dev);
+
+ return 0;
+}
+
+static void pci_dio_detach(struct comedi_device *dev)
+{
+ if (dev->iobase)
+ pci_dio_reset(dev);
+ comedi_pci_detach(dev);
+}
+
+static struct comedi_driver adv_pci_dio_driver = {
+ .driver_name = "adv_pci_dio",
+ .module = THIS_MODULE,
+ .auto_attach = pci_dio_auto_attach,
+ .detach = pci_dio_detach,
+};
+
+static int adv_pci_dio_pci_probe(struct pci_dev *dev,
+ const struct pci_device_id *id)
+{
+ unsigned long cardtype;
+
+ cardtype = pci_dio_override_cardtype(dev, id->driver_data);
+ return comedi_pci_auto_config(dev, &adv_pci_dio_driver, cardtype);
+}
+
+static const struct pci_device_id adv_pci_dio_pci_table[] = {
+ { PCI_VDEVICE(ADVANTECH, 0x1730), TYPE_PCI1730 },
+ { PCI_VDEVICE(ADVANTECH, 0x1733), TYPE_PCI1733 },
+ { PCI_VDEVICE(ADVANTECH, 0x1734), TYPE_PCI1734 },
+ { PCI_VDEVICE(ADVANTECH, 0x1735), TYPE_PCI1735 },
+ { PCI_VDEVICE(ADVANTECH, 0x1736), TYPE_PCI1736 },
+ { PCI_VDEVICE(ADVANTECH, 0x1739), TYPE_PCI1739 },
+ { PCI_VDEVICE(ADVANTECH, 0x1750), TYPE_PCI1750 },
+ { PCI_VDEVICE(ADVANTECH, 0x1751), TYPE_PCI1751 },
+ { PCI_VDEVICE(ADVANTECH, 0x1752), TYPE_PCI1752 },
+ { PCI_VDEVICE(ADVANTECH, 0x1753), TYPE_PCI1753 },
+ { PCI_VDEVICE(ADVANTECH, 0x1754), TYPE_PCI1754 },
+ { PCI_VDEVICE(ADVANTECH, 0x1756), TYPE_PCI1756 },
+ { PCI_VDEVICE(ADVANTECH, 0x1760), TYPE_PCI1760 },
+ { PCI_VDEVICE(ADVANTECH, 0x1762), TYPE_PCI1762 },
+ { 0 }
+};
+MODULE_DEVICE_TABLE(pci, adv_pci_dio_pci_table);
+
+static struct pci_driver adv_pci_dio_pci_driver = {
+ .name = "adv_pci_dio",
+ .id_table = adv_pci_dio_pci_table,
+ .probe = adv_pci_dio_pci_probe,
+ .remove = comedi_pci_auto_unconfig,
+};
+module_comedi_pci_driver(adv_pci_dio_driver, adv_pci_dio_pci_driver);
+
+MODULE_AUTHOR("Comedi http://www.comedi.org");
+MODULE_DESCRIPTION("Comedi low-level driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/comedi/drivers/aio_aio12_8.c b/drivers/staging/comedi/drivers/aio_aio12_8.c
new file mode 100644
index 000000000..fbc3e5aa9
--- /dev/null
+++ b/drivers/staging/comedi/drivers/aio_aio12_8.c
@@ -0,0 +1,249 @@
+/*
+
+ comedi/drivers/aio_aio12_8.c
+
+ Driver for Access I/O Products PC-104 AIO12-8 Analog I/O Board
+ Copyright (C) 2006 C&C Technologies, 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 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.
+*/
+
+/*
+
+Driver: aio_aio12_8
+Description: Access I/O Products PC-104 AIO12-8 Analog I/O Board
+Author: Pablo Mejia <pablo.mejia@cctechnol.com>
+Devices: [Access I/O] PC-104 AIO12-8 (aio_aio12_8)
+ [Access I/O] PC-104 AI12-8 (aio_ai12_8)
+ [Access I/O] PC-104 AO12-8 (aio_ao12_8)
+Status: experimental
+
+Configuration Options:
+ [0] - I/O port base address
+
+Notes:
+
+ Only synchronous operations are supported.
+
+*/
+
+#include <linux/module.h>
+#include "../comedidev.h"
+#include "8255.h"
+
+/*
+ * Register map
+ */
+#define AIO12_8_STATUS_REG 0x00
+#define AIO12_8_STATUS_ADC_EOC (1 << 7)
+#define AIO12_8_STATUS_PORT_C_COS (1 << 6)
+#define AIO12_8_STATUS_IRQ_ENA (1 << 2)
+#define AIO12_8_INTERRUPT_REG 0x01
+#define AIO12_8_INTERRUPT_ADC (1 << 7)
+#define AIO12_8_INTERRUPT_COS (1 << 6)
+#define AIO12_8_INTERRUPT_COUNTER1 (1 << 5)
+#define AIO12_8_INTERRUPT_PORT_C3 (1 << 4)
+#define AIO12_8_INTERRUPT_PORT_C0 (1 << 3)
+#define AIO12_8_INTERRUPT_ENA (1 << 2)
+#define AIO12_8_ADC_REG 0x02
+#define AIO12_8_ADC_MODE_NORMAL (0 << 6)
+#define AIO12_8_ADC_MODE_INT_CLK (1 << 6)
+#define AIO12_8_ADC_MODE_STANDBY (2 << 6)
+#define AIO12_8_ADC_MODE_POWERDOWN (3 << 6)
+#define AIO12_8_ADC_ACQ_3USEC (0 << 5)
+#define AIO12_8_ADC_ACQ_PROGRAM (1 << 5)
+#define AIO12_8_ADC_RANGE(x) ((x) << 3)
+#define AIO12_8_ADC_CHAN(x) ((x) << 0)
+#define AIO12_8_DAC_REG(x) (0x04 + (x) * 2)
+#define AIO12_8_8254_BASE_REG 0x0c
+#define AIO12_8_8255_BASE_REG 0x10
+#define AIO12_8_DIO_CONTROL_REG 0x14
+#define AIO12_8_DIO_CONTROL_TST (1 << 0)
+#define AIO12_8_ADC_TRIGGER_REG 0x15
+#define AIO12_8_ADC_TRIGGER_RANGE(x) ((x) << 3)
+#define AIO12_8_ADC_TRIGGER_CHAN(x) ((x) << 0)
+#define AIO12_8_TRIGGER_REG 0x16
+#define AIO12_8_TRIGGER_ADTRIG (1 << 1)
+#define AIO12_8_TRIGGER_DACTRIG (1 << 0)
+#define AIO12_8_COS_REG 0x17
+#define AIO12_8_DAC_ENABLE_REG 0x18
+#define AIO12_8_DAC_ENABLE_REF_ENA (1 << 0)
+
+struct aio12_8_boardtype {
+ const char *name;
+ int ai_nchan;
+ int ao_nchan;
+};
+
+static const struct aio12_8_boardtype board_types[] = {
+ {
+ .name = "aio_aio12_8",
+ .ai_nchan = 8,
+ .ao_nchan = 4,
+ }, {
+ .name = "aio_ai12_8",
+ .ai_nchan = 8,
+ }, {
+ .name = "aio_ao12_8",
+ .ao_nchan = 4,
+ },
+};
+
+static int aio_aio12_8_ai_eoc(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned long context)
+{
+ unsigned int status;
+
+ status = inb(dev->iobase + AIO12_8_STATUS_REG);
+ if (status & AIO12_8_STATUS_ADC_EOC)
+ return 0;
+ return -EBUSY;
+}
+
+static int aio_aio12_8_ai_read(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn, unsigned int *data)
+{
+ unsigned int chan = CR_CHAN(insn->chanspec);
+ unsigned int range = CR_RANGE(insn->chanspec);
+ unsigned char control;
+ int ret;
+ int n;
+
+ /*
+ * Setup the control byte for internal 2MHz clock, 3uS conversion,
+ * at the desired range of the requested channel.
+ */
+ control = AIO12_8_ADC_MODE_NORMAL | AIO12_8_ADC_ACQ_3USEC |
+ AIO12_8_ADC_RANGE(range) | AIO12_8_ADC_CHAN(chan);
+
+ /* Read status to clear EOC latch */
+ inb(dev->iobase + AIO12_8_STATUS_REG);
+
+ for (n = 0; n < insn->n; n++) {
+ /* Setup and start conversion */
+ outb(control, dev->iobase + AIO12_8_ADC_REG);
+
+ /* Wait for conversion to complete */
+ ret = comedi_timeout(dev, s, insn, aio_aio12_8_ai_eoc, 0);
+ if (ret)
+ return ret;
+
+ data[n] = inw(dev->iobase + AIO12_8_ADC_REG) & s->maxdata;
+ }
+
+ return insn->n;
+}
+
+static int aio_aio12_8_ao_insn_write(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ unsigned int chan = CR_CHAN(insn->chanspec);
+ unsigned int val = s->readback[chan];
+ int i;
+
+ /* enable DACs */
+ outb(AIO12_8_DAC_ENABLE_REF_ENA, dev->iobase + AIO12_8_DAC_ENABLE_REG);
+
+ for (i = 0; i < insn->n; i++) {
+ val = data[i];
+ outw(val, dev->iobase + AIO12_8_DAC_REG(chan));
+ }
+ s->readback[chan] = val;
+
+ return insn->n;
+}
+
+static const struct comedi_lrange range_aio_aio12_8 = {
+ 4, {
+ UNI_RANGE(5),
+ BIP_RANGE(5),
+ UNI_RANGE(10),
+ BIP_RANGE(10)
+ }
+};
+
+static int aio_aio12_8_attach(struct comedi_device *dev,
+ struct comedi_devconfig *it)
+{
+ const struct aio12_8_boardtype *board = dev->board_ptr;
+ struct comedi_subdevice *s;
+ int ret;
+
+ ret = comedi_request_region(dev, it->options[0], 32);
+ if (ret)
+ return ret;
+
+ ret = comedi_alloc_subdevices(dev, 4);
+ if (ret)
+ return ret;
+
+ s = &dev->subdevices[0];
+ if (board->ai_nchan) {
+ /* Analog input subdevice */
+ s->type = COMEDI_SUBD_AI;
+ s->subdev_flags = SDF_READABLE | SDF_GROUND | SDF_DIFF;
+ s->n_chan = board->ai_nchan;
+ s->maxdata = 0x0fff;
+ s->range_table = &range_aio_aio12_8;
+ s->insn_read = aio_aio12_8_ai_read;
+ } else {
+ s->type = COMEDI_SUBD_UNUSED;
+ }
+
+ s = &dev->subdevices[1];
+ if (board->ao_nchan) {
+ /* Analog output subdevice */
+ s->type = COMEDI_SUBD_AO;
+ s->subdev_flags = SDF_WRITABLE | SDF_GROUND | SDF_DIFF;
+ s->n_chan = 4;
+ s->maxdata = 0x0fff;
+ s->range_table = &range_aio_aio12_8;
+ s->insn_write = aio_aio12_8_ao_insn_write;
+
+ ret = comedi_alloc_subdev_readback(s);
+ if (ret)
+ return ret;
+ } else {
+ s->type = COMEDI_SUBD_UNUSED;
+ }
+
+ s = &dev->subdevices[2];
+ /* 8255 Digital i/o subdevice */
+ ret = subdev_8255_init(dev, s, NULL, AIO12_8_8255_BASE_REG);
+ if (ret)
+ return ret;
+
+ s = &dev->subdevices[3];
+ /* 8254 counter/timer subdevice */
+ s->type = COMEDI_SUBD_UNUSED;
+
+ return 0;
+}
+
+static struct comedi_driver aio_aio12_8_driver = {
+ .driver_name = "aio_aio12_8",
+ .module = THIS_MODULE,
+ .attach = aio_aio12_8_attach,
+ .detach = comedi_legacy_detach,
+ .board_name = &board_types[0].name,
+ .num_names = ARRAY_SIZE(board_types),
+ .offset = sizeof(struct aio12_8_boardtype),
+};
+module_comedi_driver(aio_aio12_8_driver);
+
+MODULE_AUTHOR("Comedi http://www.comedi.org");
+MODULE_DESCRIPTION("Comedi low-level driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/comedi/drivers/aio_iiro_16.c b/drivers/staging/comedi/drivers/aio_iiro_16.c
new file mode 100644
index 000000000..35b2f98f0
--- /dev/null
+++ b/drivers/staging/comedi/drivers/aio_iiro_16.c
@@ -0,0 +1,244 @@
+/*
+ * aio_iiro_16.c
+ * Comedi driver for Access I/O Products 104-IIRO-16 board
+ * Copyright (C) 2006 C&C Technologies, 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 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.
+ */
+
+/*
+ * Driver: aio_iiro_16
+ * Description: Access I/O Products PC/104 Isolated Input/Relay Output Board
+ * Author: Zachary Ware <zach.ware@cctechnol.com>
+ * Devices: [Access I/O] 104-IIRO-16 (aio_iiro_16)
+ * Status: experimental
+ *
+ * Configuration Options:
+ * [0] - I/O port base address
+ * [1] - IRQ (optional)
+ *
+ * The board supports interrupts on change of state of the digital inputs.
+ * The sample data returned by the async command indicates which inputs
+ * changed state and the current state of the inputs:
+ *
+ * Bit 23 - IRQ Enable (1) / Disable (0)
+ * Bit 17 - Input 8-15 Changed State (1 = Changed, 0 = No Change)
+ * Bit 16 - Input 0-7 Changed State (1 = Changed, 0 = No Change)
+ * Bit 15 - Digital input 15
+ * ...
+ * Bit 0 - Digital input 0
+ */
+
+#include <linux/module.h>
+#include <linux/interrupt.h>
+
+#include "../comedidev.h"
+
+#define AIO_IIRO_16_RELAY_0_7 0x00
+#define AIO_IIRO_16_INPUT_0_7 0x01
+#define AIO_IIRO_16_IRQ 0x02
+#define AIO_IIRO_16_RELAY_8_15 0x04
+#define AIO_IIRO_16_INPUT_8_15 0x05
+#define AIO_IIRO_16_STATUS 0x07
+#define AIO_IIRO_16_STATUS_IRQE BIT(7)
+#define AIO_IIRO_16_STATUS_INPUT_8_15 BIT(1)
+#define AIO_IIRO_16_STATUS_INPUT_0_7 BIT(0)
+
+static unsigned int aio_iiro_16_read_inputs(struct comedi_device *dev)
+{
+ unsigned int val;
+
+ val = inb(dev->iobase + AIO_IIRO_16_INPUT_0_7);
+ val |= inb(dev->iobase + AIO_IIRO_16_INPUT_8_15) << 8;
+
+ return val;
+}
+
+static irqreturn_t aio_iiro_16_cos(int irq, void *d)
+{
+ struct comedi_device *dev = d;
+ struct comedi_subdevice *s = dev->read_subdev;
+ unsigned int status;
+ unsigned int val;
+
+ status = inb(dev->iobase + AIO_IIRO_16_STATUS);
+ if (!(status & AIO_IIRO_16_STATUS_IRQE))
+ return IRQ_NONE;
+
+ val = aio_iiro_16_read_inputs(dev);
+ val |= (status << 16);
+
+ comedi_buf_write_samples(s, &val, 1);
+ comedi_handle_events(dev, s);
+
+ return IRQ_HANDLED;
+}
+
+static void aio_iiro_enable_irq(struct comedi_device *dev, bool enable)
+{
+ if (enable)
+ inb(dev->iobase + AIO_IIRO_16_IRQ);
+ else
+ outb(0, dev->iobase + AIO_IIRO_16_IRQ);
+}
+
+static int aio_iiro_16_cos_cancel(struct comedi_device *dev,
+ struct comedi_subdevice *s)
+{
+ aio_iiro_enable_irq(dev, false);
+
+ return 0;
+}
+
+static int aio_iiro_16_cos_cmd(struct comedi_device *dev,
+ struct comedi_subdevice *s)
+{
+ aio_iiro_enable_irq(dev, true);
+
+ return 0;
+}
+
+static int aio_iiro_16_cos_cmdtest(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_cmd *cmd)
+{
+ int err = 0;
+
+ /* Step 1 : check if triggers are trivially valid */
+
+ err |= comedi_check_trigger_src(&cmd->start_src, TRIG_NOW);
+ err |= comedi_check_trigger_src(&cmd->scan_begin_src, TRIG_EXT);
+ err |= comedi_check_trigger_src(&cmd->convert_src, TRIG_FOLLOW);
+ err |= comedi_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
+ err |= comedi_check_trigger_src(&cmd->stop_src, TRIG_NONE);
+
+ if (err)
+ return 1;
+
+ /* Step 2a : make sure trigger sources are unique */
+ /* Step 2b : and mutually compatible */
+
+ /* Step 3: check if arguments are trivially valid */
+
+ err |= comedi_check_trigger_arg_is(&cmd->start_arg, 0);
+ err |= comedi_check_trigger_arg_is(&cmd->scan_begin_arg, 0);
+ err |= comedi_check_trigger_arg_is(&cmd->convert_arg, 0);
+ err |= comedi_check_trigger_arg_is(&cmd->scan_end_arg,
+ cmd->chanlist_len);
+ err |= comedi_check_trigger_arg_is(&cmd->stop_arg, 0);
+
+ if (err)
+ return 3;
+
+ /* Step 4: fix up any arguments */
+
+ /* Step 5: check channel list if it exists */
+
+ return 0;
+}
+
+static int aio_iiro_16_do_insn_bits(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ if (comedi_dio_update_state(s, data)) {
+ outb(s->state & 0xff, dev->iobase + AIO_IIRO_16_RELAY_0_7);
+ outb((s->state >> 8) & 0xff,
+ dev->iobase + AIO_IIRO_16_RELAY_8_15);
+ }
+
+ data[1] = s->state;
+
+ return insn->n;
+}
+
+static int aio_iiro_16_di_insn_bits(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ data[1] = aio_iiro_16_read_inputs(dev);
+
+ return insn->n;
+}
+
+static int aio_iiro_16_attach(struct comedi_device *dev,
+ struct comedi_devconfig *it)
+{
+ struct comedi_subdevice *s;
+ int ret;
+
+ ret = comedi_request_region(dev, it->options[0], 0x8);
+ if (ret)
+ return ret;
+
+ aio_iiro_enable_irq(dev, false);
+
+ /*
+ * Digital input change of state interrupts are optionally supported
+ * using IRQ 2-7, 10-12, 14, or 15.
+ */
+ if ((1 << it->options[1]) & 0xdcfc) {
+ ret = request_irq(it->options[1], aio_iiro_16_cos, 0,
+ dev->board_name, dev);
+ if (ret == 0)
+ dev->irq = it->options[1];
+ }
+
+ ret = comedi_alloc_subdevices(dev, 2);
+ if (ret)
+ return ret;
+
+ /* Digital Output subdevice */
+ s = &dev->subdevices[0];
+ s->type = COMEDI_SUBD_DO;
+ s->subdev_flags = SDF_WRITABLE;
+ s->n_chan = 16;
+ s->maxdata = 1;
+ s->range_table = &range_digital;
+ s->insn_bits = aio_iiro_16_do_insn_bits;
+
+ /* get the initial state of the relays */
+ s->state = inb(dev->iobase + AIO_IIRO_16_RELAY_0_7) |
+ (inb(dev->iobase + AIO_IIRO_16_RELAY_8_15) << 8);
+
+ /* Digital Input subdevice */
+ s = &dev->subdevices[1];
+ s->type = COMEDI_SUBD_DI;
+ s->subdev_flags = SDF_READABLE;
+ s->n_chan = 16;
+ s->maxdata = 1;
+ s->range_table = &range_digital;
+ s->insn_bits = aio_iiro_16_di_insn_bits;
+ if (dev->irq) {
+ dev->read_subdev = s;
+ s->subdev_flags |= SDF_CMD_READ | SDF_LSAMPL;
+ s->len_chanlist = 1;
+ s->do_cmdtest = aio_iiro_16_cos_cmdtest;
+ s->do_cmd = aio_iiro_16_cos_cmd;
+ s->cancel = aio_iiro_16_cos_cancel;
+ }
+
+ return 0;
+}
+
+static struct comedi_driver aio_iiro_16_driver = {
+ .driver_name = "aio_iiro_16",
+ .module = THIS_MODULE,
+ .attach = aio_iiro_16_attach,
+ .detach = comedi_legacy_detach,
+};
+module_comedi_driver(aio_iiro_16_driver);
+
+MODULE_AUTHOR("Comedi http://www.comedi.org");
+MODULE_DESCRIPTION("Comedi driver for Access I/O Products 104-IIRO-16 board");
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/comedi/drivers/amcc_s5933.h b/drivers/staging/comedi/drivers/amcc_s5933.h
new file mode 100644
index 000000000..d4b8c0195
--- /dev/null
+++ b/drivers/staging/comedi/drivers/amcc_s5933.h
@@ -0,0 +1,176 @@
+/*
+ comedi/drivers/amcc_s5933.h
+
+ Stuff for AMCC S5933 PCI Controller
+
+ Author: Michal Dobes <dobes@tesnet.cz>
+
+ Inspirated from general-purpose AMCC S5933 PCI Matchmaker driver
+ made by Andrea Cisternino <acister@pcape1.pi.infn.it>
+ and as result of espionage from MITE code made by David A. Schleef.
+ Thanks to AMCC for their on-line documentation and bus master DMA
+ example.
+*/
+
+#ifndef _AMCC_S5933_H_
+#define _AMCC_S5933_H_
+
+/****************************************************************************/
+/* AMCC Operation Register Offsets - PCI */
+/****************************************************************************/
+
+#define AMCC_OP_REG_OMB1 0x00
+#define AMCC_OP_REG_OMB2 0x04
+#define AMCC_OP_REG_OMB3 0x08
+#define AMCC_OP_REG_OMB4 0x0c
+#define AMCC_OP_REG_IMB1 0x10
+#define AMCC_OP_REG_IMB2 0x14
+#define AMCC_OP_REG_IMB3 0x18
+#define AMCC_OP_REG_IMB4 0x1c
+#define AMCC_OP_REG_FIFO 0x20
+#define AMCC_OP_REG_MWAR 0x24
+#define AMCC_OP_REG_MWTC 0x28
+#define AMCC_OP_REG_MRAR 0x2c
+#define AMCC_OP_REG_MRTC 0x30
+#define AMCC_OP_REG_MBEF 0x34
+#define AMCC_OP_REG_INTCSR 0x38
+#define AMCC_OP_REG_INTCSR_SRC (AMCC_OP_REG_INTCSR + 2) /* INT source */
+#define AMCC_OP_REG_INTCSR_FEC (AMCC_OP_REG_INTCSR + 3) /* FIFO ctrl */
+#define AMCC_OP_REG_MCSR 0x3c
+#define AMCC_OP_REG_MCSR_NVDATA (AMCC_OP_REG_MCSR + 2) /* Data in byte 2 */
+#define AMCC_OP_REG_MCSR_NVCMD (AMCC_OP_REG_MCSR + 3) /* Command in byte 3 */
+
+#define AMCC_FIFO_DEPTH_DWORD 8
+#define AMCC_FIFO_DEPTH_BYTES (8 * sizeof(u32))
+
+/****************************************************************************/
+/* AMCC - PCI Interrupt Control/Status Register */
+/****************************************************************************/
+#define INTCSR_OUTBOX_BYTE(x) ((x) & 0x3)
+#define INTCSR_OUTBOX_SELECT(x) (((x) & 0x3) << 2)
+#define INTCSR_OUTBOX_EMPTY_INT 0x10 /* enable outbox empty interrupt */
+#define INTCSR_INBOX_BYTE(x) (((x) & 0x3) << 8)
+#define INTCSR_INBOX_SELECT(x) (((x) & 0x3) << 10)
+#define INTCSR_INBOX_FULL_INT 0x1000 /* enable inbox full interrupt */
+/* read, or write clear inbox full interrupt */
+#define INTCSR_INBOX_INTR_STATUS 0x20000
+/* read only, interrupt asserted */
+#define INTCSR_INTR_ASSERTED 0x800000
+
+/****************************************************************************/
+/* AMCC - PCI non-volatile ram command register (byte 3 of master control/status register) */
+/****************************************************************************/
+#define MCSR_NV_LOAD_LOW_ADDR 0x0
+#define MCSR_NV_LOAD_HIGH_ADDR 0x20
+#define MCSR_NV_WRITE 0x40
+#define MCSR_NV_READ 0x60
+#define MCSR_NV_MASK 0x60
+#define MCSR_NV_ENABLE 0x80
+#define MCSR_NV_BUSY MCSR_NV_ENABLE
+
+/****************************************************************************/
+/* AMCC Operation Registers Size - PCI */
+/****************************************************************************/
+
+#define AMCC_OP_REG_SIZE 64 /* in bytes */
+
+/****************************************************************************/
+/* AMCC Operation Register Offsets - Add-on */
+/****************************************************************************/
+
+#define AMCC_OP_REG_AIMB1 0x00
+#define AMCC_OP_REG_AIMB2 0x04
+#define AMCC_OP_REG_AIMB3 0x08
+#define AMCC_OP_REG_AIMB4 0x0c
+#define AMCC_OP_REG_AOMB1 0x10
+#define AMCC_OP_REG_AOMB2 0x14
+#define AMCC_OP_REG_AOMB3 0x18
+#define AMCC_OP_REG_AOMB4 0x1c
+#define AMCC_OP_REG_AFIFO 0x20
+#define AMCC_OP_REG_AMWAR 0x24
+#define AMCC_OP_REG_APTA 0x28
+#define AMCC_OP_REG_APTD 0x2c
+#define AMCC_OP_REG_AMRAR 0x30
+#define AMCC_OP_REG_AMBEF 0x34
+#define AMCC_OP_REG_AINT 0x38
+#define AMCC_OP_REG_AGCSTS 0x3c
+#define AMCC_OP_REG_AMWTC 0x58
+#define AMCC_OP_REG_AMRTC 0x5c
+
+/****************************************************************************/
+/* AMCC - Add-on General Control/Status Register */
+/****************************************************************************/
+
+#define AGCSTS_CONTROL_MASK 0xfffff000
+#define AGCSTS_NV_ACC_MASK 0xe0000000
+#define AGCSTS_RESET_MASK 0x0e000000
+#define AGCSTS_NV_DA_MASK 0x00ff0000
+#define AGCSTS_BIST_MASK 0x0000f000
+#define AGCSTS_STATUS_MASK 0x000000ff
+#define AGCSTS_TCZERO_MASK 0x000000c0
+#define AGCSTS_FIFO_ST_MASK 0x0000003f
+
+#define AGCSTS_TC_ENABLE 0x10000000
+
+#define AGCSTS_RESET_MBFLAGS 0x08000000
+#define AGCSTS_RESET_P2A_FIFO 0x04000000
+#define AGCSTS_RESET_A2P_FIFO 0x02000000
+#define AGCSTS_RESET_FIFOS (AGCSTS_RESET_A2P_FIFO | AGCSTS_RESET_P2A_FIFO)
+
+#define AGCSTS_A2P_TCOUNT 0x00000080
+#define AGCSTS_P2A_TCOUNT 0x00000040
+
+#define AGCSTS_FS_P2A_EMPTY 0x00000020
+#define AGCSTS_FS_P2A_HALF 0x00000010
+#define AGCSTS_FS_P2A_FULL 0x00000008
+
+#define AGCSTS_FS_A2P_EMPTY 0x00000004
+#define AGCSTS_FS_A2P_HALF 0x00000002
+#define AGCSTS_FS_A2P_FULL 0x00000001
+
+/****************************************************************************/
+/* AMCC - Add-on Interrupt Control/Status Register */
+/****************************************************************************/
+
+#define AINT_INT_MASK 0x00ff0000
+#define AINT_SEL_MASK 0x0000ffff
+#define AINT_IS_ENSEL_MASK 0x00001f1f
+
+#define AINT_INT_ASSERTED 0x00800000
+#define AINT_BM_ERROR 0x00200000
+#define AINT_BIST_INT 0x00100000
+
+#define AINT_RT_COMPLETE 0x00080000
+#define AINT_WT_COMPLETE 0x00040000
+
+#define AINT_OUT_MB_INT 0x00020000
+#define AINT_IN_MB_INT 0x00010000
+
+#define AINT_READ_COMPL 0x00008000
+#define AINT_WRITE_COMPL 0x00004000
+
+#define AINT_OMB_ENABLE 0x00001000
+#define AINT_OMB_SELECT 0x00000c00
+#define AINT_OMB_BYTE 0x00000300
+
+#define AINT_IMB_ENABLE 0x00000010
+#define AINT_IMB_SELECT 0x0000000c
+#define AINT_IMB_BYTE 0x00000003
+
+/* these are bits from various different registers, needs cleanup XXX */
+/* Enable Bus Mastering */
+#define EN_A2P_TRANSFERS 0x00000400
+/* FIFO Flag Reset */
+#define RESET_A2P_FLAGS 0x04000000L
+/* FIFO Relative Priority */
+#define A2P_HI_PRIORITY 0x00000100L
+/* Identify Interrupt Sources */
+#define ANY_S593X_INT 0x00800000L
+#define READ_TC_INT 0x00080000L
+#define WRITE_TC_INT 0x00040000L
+#define IN_MB_INT 0x00020000L
+#define MASTER_ABORT_INT 0x00100000L
+#define TARGET_ABORT_INT 0x00200000L
+#define BUS_MASTER_INT 0x00200000L
+
+#endif
diff --git a/drivers/staging/comedi/drivers/amplc_dio200.c b/drivers/staging/comedi/drivers/amplc_dio200.c
new file mode 100644
index 000000000..4fe118380
--- /dev/null
+++ b/drivers/staging/comedi/drivers/amplc_dio200.c
@@ -0,0 +1,273 @@
+/*
+ comedi/drivers/amplc_dio200.c
+
+ Driver for Amplicon PC212E, PC214E, PC215E, PC218E, PC272E.
+
+ Copyright (C) 2005-2013 MEV Ltd. <http://www.mev.co.uk/>
+
+ COMEDI - Linux Control and Measurement Device Interface
+ Copyright (C) 1998,2000 David A. Schleef <ds@schleef.org>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+*/
+/*
+ * Driver: amplc_dio200
+ * Description: Amplicon 200 Series ISA Digital I/O
+ * Author: Ian Abbott <abbotti@mev.co.uk>
+ * Devices: [Amplicon] PC212E (pc212e), PC214E (pc214e), PC215E (pc215e),
+ * PC218E (pc218e), PC272E (pc272e)
+ * Updated: Mon, 18 Mar 2013 14:40:41 +0000
+ *
+ * Status: works
+ *
+ * Configuration options:
+ * [0] - I/O port base address
+ * [1] - IRQ (optional, but commands won't work without it)
+ *
+ * Passing a zero for an option is the same as leaving it unspecified.
+ *
+ * SUBDEVICES
+ *
+ * PC212E PC214E PC215E
+ * ------------- ------------- -------------
+ * Subdevices 6 4 5
+ * 0 PPI-X PPI-X PPI-X
+ * 1 CTR-Y1 PPI-Y PPI-Y
+ * 2 CTR-Y2 CTR-Z1* CTR-Z1
+ * 3 CTR-Z1 INTERRUPT* CTR-Z2
+ * 4 CTR-Z2 INTERRUPT
+ * 5 INTERRUPT
+ *
+ * PC218E PC272E
+ * ------------- -------------
+ * Subdevices 7 4
+ * 0 CTR-X1 PPI-X
+ * 1 CTR-X2 PPI-Y
+ * 2 CTR-Y1 PPI-Z
+ * 3 CTR-Y2 INTERRUPT
+ * 4 CTR-Z1
+ * 5 CTR-Z2
+ * 6 INTERRUPT
+ *
+ * Each PPI is a 8255 chip providing 24 DIO channels. The DIO channels
+ * are configurable as inputs or outputs in four groups:
+ *
+ * Port A - channels 0 to 7
+ * Port B - channels 8 to 15
+ * Port CL - channels 16 to 19
+ * Port CH - channels 20 to 23
+ *
+ * Only mode 0 of the 8255 chips is supported.
+ *
+ * Each CTR is a 8254 chip providing 3 16-bit counter channels. Each
+ * channel is configured individually with INSN_CONFIG instructions. The
+ * specific type of configuration instruction is specified in data[0].
+ * Some configuration instructions expect an additional parameter in
+ * data[1]; others return a value in data[1]. The following configuration
+ * instructions are supported:
+ *
+ * INSN_CONFIG_SET_COUNTER_MODE. Sets the counter channel's mode and
+ * BCD/binary setting specified in data[1].
+ *
+ * INSN_CONFIG_8254_READ_STATUS. Reads the status register value for the
+ * counter channel into data[1].
+ *
+ * INSN_CONFIG_SET_CLOCK_SRC. Sets the counter channel's clock source as
+ * specified in data[1] (this is a hardware-specific value). Not
+ * supported on PC214E. For the other boards, valid clock sources are
+ * 0 to 7 as follows:
+ *
+ * 0. CLK n, the counter channel's dedicated CLK input from the SK1
+ * connector. (N.B. for other values, the counter channel's CLKn
+ * pin on the SK1 connector is an output!)
+ * 1. Internal 10 MHz clock.
+ * 2. Internal 1 MHz clock.
+ * 3. Internal 100 kHz clock.
+ * 4. Internal 10 kHz clock.
+ * 5. Internal 1 kHz clock.
+ * 6. OUT n-1, the output of counter channel n-1 (see note 1 below).
+ * 7. Ext Clock, the counter chip's dedicated Ext Clock input from
+ * the SK1 connector. This pin is shared by all three counter
+ * channels on the chip.
+ *
+ * INSN_CONFIG_GET_CLOCK_SRC. Returns the counter channel's current
+ * clock source in data[1]. For internal clock sources, data[2] is set
+ * to the period in ns.
+ *
+ * INSN_CONFIG_SET_GATE_SRC. Sets the counter channel's gate source as
+ * specified in data[2] (this is a hardware-specific value). Not
+ * supported on PC214E. For the other boards, valid gate sources are 0
+ * to 7 as follows:
+ *
+ * 0. VCC (internal +5V d.c.), i.e. gate permanently enabled.
+ * 1. GND (internal 0V d.c.), i.e. gate permanently disabled.
+ * 2. GAT n, the counter channel's dedicated GAT input from the SK1
+ * connector. (N.B. for other values, the counter channel's GATn
+ * pin on the SK1 connector is an output!)
+ * 3. /OUT n-2, the inverted output of counter channel n-2 (see note
+ * 2 below).
+ * 4. Reserved.
+ * 5. Reserved.
+ * 6. Reserved.
+ * 7. Reserved.
+ *
+ * INSN_CONFIG_GET_GATE_SRC. Returns the counter channel's current gate
+ * source in data[2].
+ *
+ * Clock and gate interconnection notes:
+ *
+ * 1. Clock source OUT n-1 is the output of the preceding channel on the
+ * same counter subdevice if n > 0, or the output of channel 2 on the
+ * preceding counter subdevice (see note 3) if n = 0.
+ *
+ * 2. Gate source /OUT n-2 is the inverted output of channel 0 on the
+ * same counter subdevice if n = 2, or the inverted output of channel n+1
+ * on the preceding counter subdevice (see note 3) if n < 2.
+ *
+ * 3. The counter subdevices are connected in a ring, so the highest
+ * counter subdevice precedes the lowest.
+ *
+ * The 'INTERRUPT' subdevice pretends to be a digital input subdevice. The
+ * digital inputs come from the interrupt status register. The number of
+ * channels matches the number of interrupt sources. The PC214E does not
+ * have an interrupt status register; see notes on 'INTERRUPT SOURCES'
+ * below.
+ *
+ * INTERRUPT SOURCES
+ *
+ * PC212E PC214E PC215E
+ * ------------- ------------- -------------
+ * Sources 6 1 6
+ * 0 PPI-X-C0 JUMPER-J5 PPI-X-C0
+ * 1 PPI-X-C3 PPI-X-C3
+ * 2 CTR-Y1-OUT1 PPI-Y-C0
+ * 3 CTR-Y2-OUT1 PPI-Y-C3
+ * 4 CTR-Z1-OUT1 CTR-Z1-OUT1
+ * 5 CTR-Z2-OUT1 CTR-Z2-OUT1
+ *
+ * PC218E PC272E
+ * ------------- -------------
+ * Sources 6 6
+ * 0 CTR-X1-OUT1 PPI-X-C0
+ * 1 CTR-X2-OUT1 PPI-X-C3
+ * 2 CTR-Y1-OUT1 PPI-Y-C0
+ * 3 CTR-Y2-OUT1 PPI-Y-C3
+ * 4 CTR-Z1-OUT1 PPI-Z-C0
+ * 5 CTR-Z2-OUT1 PPI-Z-C3
+ *
+ * When an interrupt source is enabled in the interrupt source enable
+ * register, a rising edge on the source signal latches the corresponding
+ * bit to 1 in the interrupt status register.
+ *
+ * When the interrupt status register value as a whole (actually, just the
+ * 6 least significant bits) goes from zero to non-zero, the board will
+ * generate an interrupt. No further interrupts will occur until the
+ * interrupt status register is cleared to zero. To clear a bit to zero in
+ * the interrupt status register, the corresponding interrupt source must
+ * be disabled in the interrupt source enable register (there is no
+ * separate interrupt clear register).
+ *
+ * The PC214E does not have an interrupt source enable register or an
+ * interrupt status register; its 'INTERRUPT' subdevice has a single
+ * channel and its interrupt source is selected by the position of jumper
+ * J5.
+ *
+ * COMMANDS
+ *
+ * The driver supports a read streaming acquisition command on the
+ * 'INTERRUPT' subdevice. The channel list selects the interrupt sources
+ * to be enabled. All channels will be sampled together (convert_src ==
+ * TRIG_NOW). The scan begins a short time after the hardware interrupt
+ * occurs, subject to interrupt latencies (scan_begin_src == TRIG_EXT,
+ * scan_begin_arg == 0). The value read from the interrupt status register
+ * is packed into a short value, one bit per requested channel, in the
+ * order they appear in the channel list.
+ */
+
+#include <linux/module.h>
+#include "../comedidev.h"
+
+#include "amplc_dio200.h"
+
+/*
+ * Board descriptions.
+ */
+static const struct dio200_board dio200_isa_boards[] = {
+ {
+ .name = "pc212e",
+ .n_subdevs = 6,
+ .sdtype = {
+ sd_8255, sd_8254, sd_8254, sd_8254, sd_8254, sd_intr
+ },
+ .sdinfo = { 0x00, 0x08, 0x0c, 0x10, 0x14, 0x3f },
+ .has_int_sce = true,
+ .has_clk_gat_sce = true,
+ }, {
+ .name = "pc214e",
+ .n_subdevs = 4,
+ .sdtype = {
+ sd_8255, sd_8255, sd_8254, sd_intr
+ },
+ .sdinfo = { 0x00, 0x08, 0x10, 0x01 },
+ }, {
+ .name = "pc215e",
+ .n_subdevs = 5,
+ .sdtype = {
+ sd_8255, sd_8255, sd_8254, sd_8254, sd_intr
+ },
+ .sdinfo = { 0x00, 0x08, 0x10, 0x14, 0x3f },
+ .has_int_sce = true,
+ .has_clk_gat_sce = true,
+ }, {
+ .name = "pc218e",
+ .n_subdevs = 7,
+ .sdtype = {
+ sd_8254, sd_8254, sd_8255, sd_8254, sd_8254, sd_intr
+ },
+ .sdinfo = { 0x00, 0x04, 0x08, 0x0c, 0x10, 0x14, 0x3f },
+ .has_int_sce = true,
+ .has_clk_gat_sce = true,
+ }, {
+ .name = "pc272e",
+ .n_subdevs = 4,
+ .sdtype = {
+ sd_8255, sd_8255, sd_8255, sd_intr
+ },
+ .sdinfo = { 0x00, 0x08, 0x10, 0x3f },
+ .has_int_sce = true,
+ },
+};
+
+static int dio200_attach(struct comedi_device *dev, struct comedi_devconfig *it)
+{
+ int ret;
+
+ ret = comedi_request_region(dev, it->options[0], 0x20);
+ if (ret)
+ return ret;
+
+ return amplc_dio200_common_attach(dev, it->options[1], 0);
+}
+
+static struct comedi_driver amplc_dio200_driver = {
+ .driver_name = "amplc_dio200",
+ .module = THIS_MODULE,
+ .attach = dio200_attach,
+ .detach = comedi_legacy_detach,
+ .board_name = &dio200_isa_boards[0].name,
+ .offset = sizeof(struct dio200_board),
+ .num_names = ARRAY_SIZE(dio200_isa_boards),
+};
+module_comedi_driver(amplc_dio200_driver);
+
+MODULE_AUTHOR("Comedi http://www.comedi.org");
+MODULE_DESCRIPTION("Comedi driver for Amplicon 200 Series ISA DIO boards");
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/comedi/drivers/amplc_dio200.h b/drivers/staging/comedi/drivers/amplc_dio200.h
new file mode 100644
index 000000000..d6d6a265c
--- /dev/null
+++ b/drivers/staging/comedi/drivers/amplc_dio200.h
@@ -0,0 +1,51 @@
+/*
+ comedi/drivers/amplc_dio.h
+
+ Header for amplc_dio200.c, amplc_dio200_common.c and
+ amplc_dio200_pci.c.
+
+ Copyright (C) 2005-2013 MEV Ltd. <http://www.mev.co.uk/>
+
+ COMEDI - Linux Control and Measurement Device Interface
+ Copyright (C) 1998,2000 David A. Schleef <ds@schleef.org>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+*/
+
+#ifndef AMPLC_DIO200_H_INCLUDED
+#define AMPLC_DIO200_H_INCLUDED
+
+/*
+ * Subdevice types.
+ */
+enum dio200_sdtype { sd_none, sd_intr, sd_8255, sd_8254, sd_timer };
+
+#define DIO200_MAX_SUBDEVS 8
+#define DIO200_MAX_ISNS 6
+
+struct dio200_board {
+ const char *name;
+ unsigned char mainbar;
+ unsigned short n_subdevs; /* number of subdevices */
+ unsigned char sdtype[DIO200_MAX_SUBDEVS]; /* enum dio200_sdtype */
+ unsigned char sdinfo[DIO200_MAX_SUBDEVS]; /* depends on sdtype */
+ bool has_int_sce:1; /* has interrupt enable/status reg */
+ bool has_clk_gat_sce:1; /* has clock/gate selection registers */
+ bool is_pcie:1; /* has enhanced features */
+};
+
+int amplc_dio200_common_attach(struct comedi_device *dev, unsigned int irq,
+ unsigned long req_irq_flags);
+
+/* Used by initialization of PCIe boards. */
+void amplc_dio200_set_enhance(struct comedi_device *dev, unsigned char val);
+
+#endif
diff --git a/drivers/staging/comedi/drivers/amplc_dio200_common.c b/drivers/staging/comedi/drivers/amplc_dio200_common.c
new file mode 100644
index 000000000..d15a3dc12
--- /dev/null
+++ b/drivers/staging/comedi/drivers/amplc_dio200_common.c
@@ -0,0 +1,874 @@
+/*
+ comedi/drivers/amplc_dio200_common.c
+
+ Common support code for "amplc_dio200" and "amplc_dio200_pci".
+
+ Copyright (C) 2005-2013 MEV Ltd. <http://www.mev.co.uk/>
+
+ COMEDI - Linux Control and Measurement Device Interface
+ Copyright (C) 1998,2000 David A. Schleef <ds@schleef.org>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+*/
+
+#include <linux/module.h>
+#include <linux/interrupt.h>
+
+#include "../comedidev.h"
+
+#include "amplc_dio200.h"
+#include "comedi_8254.h"
+#include "8255.h" /* only for register defines */
+
+/* 200 series registers */
+#define DIO200_IO_SIZE 0x20
+#define DIO200_PCIE_IO_SIZE 0x4000
+#define DIO200_CLK_SCE(x) (0x18 + (x)) /* Group X/Y/Z clock sel reg */
+#define DIO200_GAT_SCE(x) (0x1b + (x)) /* Group X/Y/Z gate sel reg */
+#define DIO200_INT_SCE 0x1e /* Interrupt enable/status register */
+/* Extra registers for new PCIe boards */
+#define DIO200_ENHANCE 0x20 /* 1 to enable enhanced features */
+#define DIO200_VERSION 0x24 /* Hardware version register */
+#define DIO200_TS_CONFIG 0x600 /* Timestamp timer config register */
+#define DIO200_TS_COUNT 0x602 /* Timestamp timer count register */
+
+/*
+ * Functions for constructing value for DIO_200_?CLK_SCE and
+ * DIO_200_?GAT_SCE registers:
+ *
+ * 'which' is: 0 for CTR-X1, CTR-Y1, CTR-Z1; 1 for CTR-X2, CTR-Y2 or CTR-Z2.
+ * 'chan' is the channel: 0, 1 or 2.
+ * 'source' is the signal source: 0 to 7, or 0 to 31 for "enhanced" boards.
+ */
+static unsigned char clk_gat_sce(unsigned int which, unsigned int chan,
+ unsigned int source)
+{
+ return (which << 5) | (chan << 3) |
+ ((source & 030) << 3) | (source & 007);
+}
+
+static unsigned char clk_sce(unsigned int which, unsigned int chan,
+ unsigned int source)
+{
+ return clk_gat_sce(which, chan, source);
+}
+
+static unsigned char gat_sce(unsigned int which, unsigned int chan,
+ unsigned int source)
+{
+ return clk_gat_sce(which, chan, source);
+}
+
+/*
+ * Periods of the internal clock sources in nanoseconds.
+ */
+static const unsigned int clock_period[32] = {
+ [1] = 100, /* 10 MHz */
+ [2] = 1000, /* 1 MHz */
+ [3] = 10000, /* 100 kHz */
+ [4] = 100000, /* 10 kHz */
+ [5] = 1000000, /* 1 kHz */
+ [11] = 50, /* 20 MHz (enhanced boards) */
+ /* clock sources 12 and later reserved for enhanced boards */
+};
+
+/*
+ * Timestamp timer configuration register (for new PCIe boards).
+ */
+#define TS_CONFIG_RESET 0x100 /* Reset counter to zero. */
+#define TS_CONFIG_CLK_SRC_MASK 0x0FF /* Clock source. */
+#define TS_CONFIG_MAX_CLK_SRC 2 /* Maximum clock source value. */
+
+/*
+ * Periods of the timestamp timer clock sources in nanoseconds.
+ */
+static const unsigned int ts_clock_period[TS_CONFIG_MAX_CLK_SRC + 1] = {
+ 1, /* 1 nanosecond (but with 20 ns granularity). */
+ 1000, /* 1 microsecond. */
+ 1000000, /* 1 millisecond. */
+};
+
+struct dio200_subdev_8255 {
+ unsigned int ofs; /* DIO base offset */
+};
+
+struct dio200_subdev_intr {
+ spinlock_t spinlock;
+ unsigned int ofs;
+ unsigned int valid_isns;
+ unsigned int enabled_isns;
+ bool active:1;
+};
+
+static unsigned char dio200_read8(struct comedi_device *dev,
+ unsigned int offset)
+{
+ const struct dio200_board *board = dev->board_ptr;
+
+ if (board->is_pcie)
+ offset <<= 3;
+
+ if (dev->mmio)
+ return readb(dev->mmio + offset);
+ return inb(dev->iobase + offset);
+}
+
+static void dio200_write8(struct comedi_device *dev,
+ unsigned int offset, unsigned char val)
+{
+ const struct dio200_board *board = dev->board_ptr;
+
+ if (board->is_pcie)
+ offset <<= 3;
+
+ if (dev->mmio)
+ writeb(val, dev->mmio + offset);
+ else
+ outb(val, dev->iobase + offset);
+}
+
+static unsigned int dio200_read32(struct comedi_device *dev,
+ unsigned int offset)
+{
+ const struct dio200_board *board = dev->board_ptr;
+
+ if (board->is_pcie)
+ offset <<= 3;
+
+ if (dev->mmio)
+ return readl(dev->mmio + offset);
+ return inl(dev->iobase + offset);
+}
+
+static void dio200_write32(struct comedi_device *dev,
+ unsigned int offset, unsigned int val)
+{
+ const struct dio200_board *board = dev->board_ptr;
+
+ if (board->is_pcie)
+ offset <<= 3;
+
+ if (dev->mmio)
+ writel(val, dev->mmio + offset);
+ else
+ outl(val, dev->iobase + offset);
+}
+
+static unsigned int dio200_subdev_8254_offset(struct comedi_device *dev,
+ struct comedi_subdevice *s)
+{
+ const struct dio200_board *board = dev->board_ptr;
+ struct comedi_8254 *i8254 = s->private;
+ unsigned int offset;
+
+ /* get the offset that was passed to comedi_8254_*_init() */
+ if (dev->mmio)
+ offset = i8254->mmio - dev->mmio;
+ else
+ offset = i8254->iobase - dev->iobase;
+
+ /* remove the shift that was added for PCIe boards */
+ if (board->is_pcie)
+ offset >>= 3;
+
+ /* this offset now works for the dio200_{read,write} helpers */
+ return offset;
+}
+
+static int dio200_subdev_intr_insn_bits(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ const struct dio200_board *board = dev->board_ptr;
+ struct dio200_subdev_intr *subpriv = s->private;
+
+ if (board->has_int_sce) {
+ /* Just read the interrupt status register. */
+ data[1] = dio200_read8(dev, subpriv->ofs) & subpriv->valid_isns;
+ } else {
+ /* No interrupt status register. */
+ data[0] = 0;
+ }
+
+ return insn->n;
+}
+
+static void dio200_stop_intr(struct comedi_device *dev,
+ struct comedi_subdevice *s)
+{
+ const struct dio200_board *board = dev->board_ptr;
+ struct dio200_subdev_intr *subpriv = s->private;
+
+ subpriv->active = false;
+ subpriv->enabled_isns = 0;
+ if (board->has_int_sce)
+ dio200_write8(dev, subpriv->ofs, 0);
+}
+
+static void dio200_start_intr(struct comedi_device *dev,
+ struct comedi_subdevice *s)
+{
+ const struct dio200_board *board = dev->board_ptr;
+ struct dio200_subdev_intr *subpriv = s->private;
+ struct comedi_cmd *cmd = &s->async->cmd;
+ unsigned int n;
+ unsigned isn_bits;
+
+ /* Determine interrupt sources to enable. */
+ isn_bits = 0;
+ if (cmd->chanlist) {
+ for (n = 0; n < cmd->chanlist_len; n++)
+ isn_bits |= (1U << CR_CHAN(cmd->chanlist[n]));
+ }
+ isn_bits &= subpriv->valid_isns;
+ /* Enable interrupt sources. */
+ subpriv->enabled_isns = isn_bits;
+ if (board->has_int_sce)
+ dio200_write8(dev, subpriv->ofs, isn_bits);
+}
+
+static int dio200_inttrig_start_intr(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ unsigned int trig_num)
+{
+ struct dio200_subdev_intr *subpriv = s->private;
+ struct comedi_cmd *cmd = &s->async->cmd;
+ unsigned long flags;
+
+ if (trig_num != cmd->start_arg)
+ return -EINVAL;
+
+ spin_lock_irqsave(&subpriv->spinlock, flags);
+ s->async->inttrig = NULL;
+ if (subpriv->active)
+ dio200_start_intr(dev, s);
+
+ spin_unlock_irqrestore(&subpriv->spinlock, flags);
+
+ return 1;
+}
+
+static void dio200_read_scan_intr(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ unsigned int triggered)
+{
+ struct comedi_cmd *cmd = &s->async->cmd;
+ unsigned short val;
+ unsigned int n, ch;
+
+ val = 0;
+ for (n = 0; n < cmd->chanlist_len; n++) {
+ ch = CR_CHAN(cmd->chanlist[n]);
+ if (triggered & (1U << ch))
+ val |= (1U << n);
+ }
+
+ comedi_buf_write_samples(s, &val, 1);
+
+ if (cmd->stop_src == TRIG_COUNT &&
+ s->async->scans_done >= cmd->stop_arg)
+ s->async->events |= COMEDI_CB_EOA;
+}
+
+static int dio200_handle_read_intr(struct comedi_device *dev,
+ struct comedi_subdevice *s)
+{
+ const struct dio200_board *board = dev->board_ptr;
+ struct dio200_subdev_intr *subpriv = s->private;
+ unsigned triggered;
+ unsigned intstat;
+ unsigned cur_enabled;
+ unsigned long flags;
+
+ triggered = 0;
+
+ spin_lock_irqsave(&subpriv->spinlock, flags);
+ if (board->has_int_sce) {
+ /*
+ * Collect interrupt sources that have triggered and disable
+ * them temporarily. Loop around until no extra interrupt
+ * sources have triggered, at which point, the valid part of
+ * the interrupt status register will read zero, clearing the
+ * cause of the interrupt.
+ *
+ * Mask off interrupt sources already seen to avoid infinite
+ * loop in case of misconfiguration.
+ */
+ cur_enabled = subpriv->enabled_isns;
+ while ((intstat = (dio200_read8(dev, subpriv->ofs) &
+ subpriv->valid_isns & ~triggered)) != 0) {
+ triggered |= intstat;
+ cur_enabled &= ~triggered;
+ dio200_write8(dev, subpriv->ofs, cur_enabled);
+ }
+ } else {
+ /*
+ * No interrupt status register. Assume the single interrupt
+ * source has triggered.
+ */
+ triggered = subpriv->enabled_isns;
+ }
+
+ if (triggered) {
+ /*
+ * Some interrupt sources have triggered and have been
+ * temporarily disabled to clear the cause of the interrupt.
+ *
+ * Reenable them NOW to minimize the time they are disabled.
+ */
+ cur_enabled = subpriv->enabled_isns;
+ if (board->has_int_sce)
+ dio200_write8(dev, subpriv->ofs, cur_enabled);
+
+ if (subpriv->active) {
+ /*
+ * The command is still active.
+ *
+ * Ignore interrupt sources that the command isn't
+ * interested in (just in case there's a race
+ * condition).
+ */
+ if (triggered & subpriv->enabled_isns)
+ /* Collect scan data. */
+ dio200_read_scan_intr(dev, s, triggered);
+ }
+ }
+ spin_unlock_irqrestore(&subpriv->spinlock, flags);
+
+ comedi_handle_events(dev, s);
+
+ return (triggered != 0);
+}
+
+static int dio200_subdev_intr_cancel(struct comedi_device *dev,
+ struct comedi_subdevice *s)
+{
+ struct dio200_subdev_intr *subpriv = s->private;
+ unsigned long flags;
+
+ spin_lock_irqsave(&subpriv->spinlock, flags);
+ if (subpriv->active)
+ dio200_stop_intr(dev, s);
+
+ spin_unlock_irqrestore(&subpriv->spinlock, flags);
+
+ return 0;
+}
+
+static int dio200_subdev_intr_cmdtest(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_cmd *cmd)
+{
+ int err = 0;
+
+ /* Step 1 : check if triggers are trivially valid */
+
+ err |= comedi_check_trigger_src(&cmd->start_src, TRIG_NOW | TRIG_INT);
+ err |= comedi_check_trigger_src(&cmd->scan_begin_src, TRIG_EXT);
+ err |= comedi_check_trigger_src(&cmd->convert_src, TRIG_NOW);
+ err |= comedi_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
+ err |= comedi_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE);
+
+ if (err)
+ return 1;
+
+ /* Step 2a : make sure trigger sources are unique */
+
+ err |= comedi_check_trigger_is_unique(cmd->start_src);
+ err |= comedi_check_trigger_is_unique(cmd->stop_src);
+
+ /* Step 2b : and mutually compatible */
+
+ if (err)
+ return 2;
+
+ /* Step 3: check if arguments are trivially valid */
+
+ err |= comedi_check_trigger_arg_is(&cmd->start_arg, 0);
+ err |= comedi_check_trigger_arg_is(&cmd->scan_begin_arg, 0);
+ err |= comedi_check_trigger_arg_is(&cmd->convert_arg, 0);
+ err |= comedi_check_trigger_arg_is(&cmd->scan_end_arg,
+ cmd->chanlist_len);
+
+ if (cmd->stop_src == TRIG_COUNT)
+ err |= comedi_check_trigger_arg_min(&cmd->stop_arg, 1);
+ else /* TRIG_NONE */
+ err |= comedi_check_trigger_arg_is(&cmd->stop_arg, 0);
+
+ if (err)
+ return 3;
+
+ /* step 4: fix up any arguments */
+
+ /* if (err) return 4; */
+
+ return 0;
+}
+
+static int dio200_subdev_intr_cmd(struct comedi_device *dev,
+ struct comedi_subdevice *s)
+{
+ struct comedi_cmd *cmd = &s->async->cmd;
+ struct dio200_subdev_intr *subpriv = s->private;
+ unsigned long flags;
+
+ spin_lock_irqsave(&subpriv->spinlock, flags);
+
+ subpriv->active = true;
+
+ if (cmd->start_src == TRIG_INT)
+ s->async->inttrig = dio200_inttrig_start_intr;
+ else /* TRIG_NOW */
+ dio200_start_intr(dev, s);
+
+ spin_unlock_irqrestore(&subpriv->spinlock, flags);
+
+ return 0;
+}
+
+static int dio200_subdev_intr_init(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ unsigned int offset,
+ unsigned valid_isns)
+{
+ const struct dio200_board *board = dev->board_ptr;
+ struct dio200_subdev_intr *subpriv;
+
+ subpriv = comedi_alloc_spriv(s, sizeof(*subpriv));
+ if (!subpriv)
+ return -ENOMEM;
+
+ subpriv->ofs = offset;
+ subpriv->valid_isns = valid_isns;
+ spin_lock_init(&subpriv->spinlock);
+
+ if (board->has_int_sce)
+ /* Disable interrupt sources. */
+ dio200_write8(dev, subpriv->ofs, 0);
+
+ s->type = COMEDI_SUBD_DI;
+ s->subdev_flags = SDF_READABLE | SDF_CMD_READ | SDF_PACKED;
+ if (board->has_int_sce) {
+ s->n_chan = DIO200_MAX_ISNS;
+ s->len_chanlist = DIO200_MAX_ISNS;
+ } else {
+ /* No interrupt source register. Support single channel. */
+ s->n_chan = 1;
+ s->len_chanlist = 1;
+ }
+ s->range_table = &range_digital;
+ s->maxdata = 1;
+ s->insn_bits = dio200_subdev_intr_insn_bits;
+ s->do_cmdtest = dio200_subdev_intr_cmdtest;
+ s->do_cmd = dio200_subdev_intr_cmd;
+ s->cancel = dio200_subdev_intr_cancel;
+
+ return 0;
+}
+
+static irqreturn_t dio200_interrupt(int irq, void *d)
+{
+ struct comedi_device *dev = d;
+ struct comedi_subdevice *s = dev->read_subdev;
+ int handled;
+
+ if (!dev->attached)
+ return IRQ_NONE;
+
+ handled = dio200_handle_read_intr(dev, s);
+
+ return IRQ_RETVAL(handled);
+}
+
+static void dio200_subdev_8254_set_gate_src(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ unsigned int chan,
+ unsigned int src)
+{
+ unsigned int offset = dio200_subdev_8254_offset(dev, s);
+
+ dio200_write8(dev, DIO200_GAT_SCE(offset >> 3),
+ gat_sce((offset >> 2) & 1, chan, src));
+}
+
+static void dio200_subdev_8254_set_clock_src(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ unsigned int chan,
+ unsigned int src)
+{
+ unsigned int offset = dio200_subdev_8254_offset(dev, s);
+
+ dio200_write8(dev, DIO200_CLK_SCE(offset >> 3),
+ clk_sce((offset >> 2) & 1, chan, src));
+}
+
+static int dio200_subdev_8254_config(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ const struct dio200_board *board = dev->board_ptr;
+ struct comedi_8254 *i8254 = s->private;
+ unsigned int chan = CR_CHAN(insn->chanspec);
+ unsigned int max_src = board->is_pcie ? 31 : 7;
+ unsigned int src;
+
+ if (!board->has_clk_gat_sce)
+ return -EINVAL;
+
+ switch (data[0]) {
+ case INSN_CONFIG_SET_GATE_SRC:
+ src = data[2];
+ if (src > max_src)
+ return -EINVAL;
+
+ dio200_subdev_8254_set_gate_src(dev, s, chan, src);
+ i8254->gate_src[chan] = src;
+ break;
+ case INSN_CONFIG_GET_GATE_SRC:
+ data[2] = i8254->gate_src[chan];
+ break;
+ case INSN_CONFIG_SET_CLOCK_SRC:
+ src = data[1];
+ if (src > max_src)
+ return -EINVAL;
+
+ dio200_subdev_8254_set_clock_src(dev, s, chan, src);
+ i8254->clock_src[chan] = src;
+ break;
+ case INSN_CONFIG_GET_CLOCK_SRC:
+ data[1] = i8254->clock_src[chan];
+ data[2] = clock_period[i8254->clock_src[chan]];
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return insn->n;
+}
+
+static int dio200_subdev_8254_init(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ unsigned int offset)
+{
+ const struct dio200_board *board = dev->board_ptr;
+ struct comedi_8254 *i8254;
+ unsigned int regshift;
+ int chan;
+
+ /*
+ * PCIe boards need the offset shifted in order to get the
+ * correct base address of the timer.
+ */
+ if (board->is_pcie) {
+ offset <<= 3;
+ regshift = 3;
+ } else {
+ regshift = 0;
+ }
+
+ if (dev->mmio)
+ i8254 = comedi_8254_mm_init(dev->mmio + offset,
+ 0, I8254_IO8, regshift);
+ else
+ i8254 = comedi_8254_init(dev->iobase + offset,
+ 0, I8254_IO8, regshift);
+ if (!i8254)
+ return -ENOMEM;
+
+ comedi_8254_subdevice_init(s, i8254);
+
+ i8254->insn_config = dio200_subdev_8254_config;
+
+ /*
+ * There could be multiple timers so this driver does not
+ * use dev->pacer to save the i8254 pointer. Instead,
+ * comedi_8254_subdevice_init() saved the i8254 pointer in
+ * s->private. Set the runflag bit so that the core will
+ * automatically free it when the driver is detached.
+ */
+ s->runflags |= COMEDI_SRF_FREE_SPRIV;
+
+ /* Initialize channels. */
+ if (board->has_clk_gat_sce) {
+ for (chan = 0; chan < 3; chan++) {
+ /* Gate source 0 is VCC (logic 1). */
+ dio200_subdev_8254_set_gate_src(dev, s, chan, 0);
+ /* Clock source 0 is the dedicated clock input. */
+ dio200_subdev_8254_set_clock_src(dev, s, chan, 0);
+ }
+ }
+
+ return 0;
+}
+
+static void dio200_subdev_8255_set_dir(struct comedi_device *dev,
+ struct comedi_subdevice *s)
+{
+ struct dio200_subdev_8255 *subpriv = s->private;
+ int config;
+
+ config = I8255_CTRL_CW;
+ /* 1 in io_bits indicates output, 1 in config indicates input */
+ if (!(s->io_bits & 0x0000ff))
+ config |= I8255_CTRL_A_IO;
+ if (!(s->io_bits & 0x00ff00))
+ config |= I8255_CTRL_B_IO;
+ if (!(s->io_bits & 0x0f0000))
+ config |= I8255_CTRL_C_LO_IO;
+ if (!(s->io_bits & 0xf00000))
+ config |= I8255_CTRL_C_HI_IO;
+ dio200_write8(dev, subpriv->ofs + I8255_CTRL_REG, config);
+}
+
+static int dio200_subdev_8255_bits(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ struct dio200_subdev_8255 *subpriv = s->private;
+ unsigned int mask;
+ unsigned int val;
+
+ mask = comedi_dio_update_state(s, data);
+ if (mask) {
+ if (mask & 0xff)
+ dio200_write8(dev, subpriv->ofs + I8255_DATA_A_REG,
+ s->state & 0xff);
+ if (mask & 0xff00)
+ dio200_write8(dev, subpriv->ofs + I8255_DATA_B_REG,
+ (s->state >> 8) & 0xff);
+ if (mask & 0xff0000)
+ dio200_write8(dev, subpriv->ofs + I8255_DATA_C_REG,
+ (s->state >> 16) & 0xff);
+ }
+
+ val = dio200_read8(dev, subpriv->ofs + I8255_DATA_A_REG);
+ val |= dio200_read8(dev, subpriv->ofs + I8255_DATA_B_REG) << 8;
+ val |= dio200_read8(dev, subpriv->ofs + I8255_DATA_C_REG) << 16;
+
+ data[1] = val;
+
+ return insn->n;
+}
+
+static int dio200_subdev_8255_config(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ unsigned int chan = CR_CHAN(insn->chanspec);
+ unsigned int mask;
+ int ret;
+
+ if (chan < 8)
+ mask = 0x0000ff;
+ else if (chan < 16)
+ mask = 0x00ff00;
+ else if (chan < 20)
+ mask = 0x0f0000;
+ else
+ mask = 0xf00000;
+
+ ret = comedi_dio_insn_config(dev, s, insn, data, mask);
+ if (ret)
+ return ret;
+
+ dio200_subdev_8255_set_dir(dev, s);
+
+ return insn->n;
+}
+
+static int dio200_subdev_8255_init(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ unsigned int offset)
+{
+ struct dio200_subdev_8255 *subpriv;
+
+ subpriv = comedi_alloc_spriv(s, sizeof(*subpriv));
+ if (!subpriv)
+ return -ENOMEM;
+
+ subpriv->ofs = offset;
+
+ s->type = COMEDI_SUBD_DIO;
+ s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
+ s->n_chan = 24;
+ s->range_table = &range_digital;
+ s->maxdata = 1;
+ s->insn_bits = dio200_subdev_8255_bits;
+ s->insn_config = dio200_subdev_8255_config;
+ dio200_subdev_8255_set_dir(dev, s);
+ return 0;
+}
+
+static int dio200_subdev_timer_read(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ unsigned int n;
+
+ for (n = 0; n < insn->n; n++)
+ data[n] = dio200_read32(dev, DIO200_TS_COUNT);
+ return n;
+}
+
+static void dio200_subdev_timer_reset(struct comedi_device *dev,
+ struct comedi_subdevice *s)
+{
+ unsigned int clock;
+
+ clock = dio200_read32(dev, DIO200_TS_CONFIG) & TS_CONFIG_CLK_SRC_MASK;
+ dio200_write32(dev, DIO200_TS_CONFIG, clock | TS_CONFIG_RESET);
+ dio200_write32(dev, DIO200_TS_CONFIG, clock);
+}
+
+static void dio200_subdev_timer_get_clock_src(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ unsigned int *src,
+ unsigned int *period)
+{
+ unsigned int clk;
+
+ clk = dio200_read32(dev, DIO200_TS_CONFIG) & TS_CONFIG_CLK_SRC_MASK;
+ *src = clk;
+ *period = (clk < ARRAY_SIZE(ts_clock_period)) ?
+ ts_clock_period[clk] : 0;
+}
+
+static int dio200_subdev_timer_set_clock_src(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ unsigned int src)
+{
+ if (src > TS_CONFIG_MAX_CLK_SRC)
+ return -EINVAL;
+ dio200_write32(dev, DIO200_TS_CONFIG, src);
+ return 0;
+}
+
+static int dio200_subdev_timer_config(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ int ret = 0;
+
+ switch (data[0]) {
+ case INSN_CONFIG_RESET:
+ dio200_subdev_timer_reset(dev, s);
+ break;
+ case INSN_CONFIG_SET_CLOCK_SRC:
+ ret = dio200_subdev_timer_set_clock_src(dev, s, data[1]);
+ if (ret < 0)
+ ret = -EINVAL;
+ break;
+ case INSN_CONFIG_GET_CLOCK_SRC:
+ dio200_subdev_timer_get_clock_src(dev, s, &data[1], &data[2]);
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+ return ret < 0 ? ret : insn->n;
+}
+
+void amplc_dio200_set_enhance(struct comedi_device *dev, unsigned char val)
+{
+ dio200_write8(dev, DIO200_ENHANCE, val);
+}
+EXPORT_SYMBOL_GPL(amplc_dio200_set_enhance);
+
+int amplc_dio200_common_attach(struct comedi_device *dev, unsigned int irq,
+ unsigned long req_irq_flags)
+{
+ const struct dio200_board *board = dev->board_ptr;
+ struct comedi_subdevice *s;
+ unsigned int n;
+ int ret;
+
+ ret = comedi_alloc_subdevices(dev, board->n_subdevs);
+ if (ret)
+ return ret;
+
+ for (n = 0; n < dev->n_subdevices; n++) {
+ s = &dev->subdevices[n];
+ switch (board->sdtype[n]) {
+ case sd_8254:
+ /* counter subdevice (8254) */
+ ret = dio200_subdev_8254_init(dev, s,
+ board->sdinfo[n]);
+ if (ret < 0)
+ return ret;
+ break;
+ case sd_8255:
+ /* digital i/o subdevice (8255) */
+ ret = dio200_subdev_8255_init(dev, s,
+ board->sdinfo[n]);
+ if (ret < 0)
+ return ret;
+ break;
+ case sd_intr:
+ /* 'INTERRUPT' subdevice */
+ if (irq && !dev->read_subdev) {
+ ret = dio200_subdev_intr_init(dev, s,
+ DIO200_INT_SCE,
+ board->sdinfo[n]);
+ if (ret < 0)
+ return ret;
+ dev->read_subdev = s;
+ } else {
+ s->type = COMEDI_SUBD_UNUSED;
+ }
+ break;
+ case sd_timer:
+ s->type = COMEDI_SUBD_TIMER;
+ s->subdev_flags = SDF_READABLE | SDF_LSAMPL;
+ s->n_chan = 1;
+ s->maxdata = 0xffffffff;
+ s->insn_read = dio200_subdev_timer_read;
+ s->insn_config = dio200_subdev_timer_config;
+ break;
+ default:
+ s->type = COMEDI_SUBD_UNUSED;
+ break;
+ }
+ }
+
+ if (irq && dev->read_subdev) {
+ if (request_irq(irq, dio200_interrupt, req_irq_flags,
+ dev->board_name, dev) >= 0) {
+ dev->irq = irq;
+ } else {
+ dev_warn(dev->class_dev,
+ "warning! irq %u unavailable!\n", irq);
+ }
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(amplc_dio200_common_attach);
+
+static int __init amplc_dio200_common_init(void)
+{
+ return 0;
+}
+module_init(amplc_dio200_common_init);
+
+static void __exit amplc_dio200_common_exit(void)
+{
+}
+module_exit(amplc_dio200_common_exit);
+
+MODULE_AUTHOR("Comedi http://www.comedi.org");
+MODULE_DESCRIPTION("Comedi helper for amplc_dio200 and amplc_dio200_pci");
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/comedi/drivers/amplc_dio200_pci.c b/drivers/staging/comedi/drivers/amplc_dio200_pci.c
new file mode 100644
index 000000000..d9850c917
--- /dev/null
+++ b/drivers/staging/comedi/drivers/amplc_dio200_pci.c
@@ -0,0 +1,423 @@
+/* comedi/drivers/amplc_dio200_pci.c
+
+ Driver for Amplicon PCI215, PCI272, PCIe215, PCIe236, PCIe296.
+
+ Copyright (C) 2005-2013 MEV Ltd. <http://www.mev.co.uk/>
+
+ COMEDI - Linux Control and Measurement Device Interface
+ Copyright (C) 1998,2000 David A. Schleef <ds@schleef.org>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+*/
+/*
+ * Driver: amplc_dio200_pci
+ * Description: Amplicon 200 Series PCI Digital I/O
+ * Author: Ian Abbott <abbotti@mev.co.uk>
+ * Devices: [Amplicon] PCI215 (amplc_dio200_pci), PCIe215, PCIe236,
+ * PCI272, PCIe296
+ * Updated: Mon, 18 Mar 2013 15:03:50 +0000
+ * Status: works
+ *
+ * Configuration options:
+ * none
+ *
+ * Manual configuration of PCI(e) cards is not supported; they are configured
+ * automatically.
+ *
+ * SUBDEVICES
+ *
+ * PCI215 PCIe215 PCIe236
+ * ------------- ------------- -------------
+ * Subdevices 5 8 8
+ * 0 PPI-X PPI-X PPI-X
+ * 1 PPI-Y UNUSED UNUSED
+ * 2 CTR-Z1 PPI-Y UNUSED
+ * 3 CTR-Z2 UNUSED UNUSED
+ * 4 INTERRUPT CTR-Z1 CTR-Z1
+ * 5 CTR-Z2 CTR-Z2
+ * 6 TIMER TIMER
+ * 7 INTERRUPT INTERRUPT
+ *
+ *
+ * PCI272 PCIe296
+ * ------------- -------------
+ * Subdevices 4 8
+ * 0 PPI-X PPI-X1
+ * 1 PPI-Y PPI-X2
+ * 2 PPI-Z PPI-Y1
+ * 3 INTERRUPT PPI-Y2
+ * 4 CTR-Z1
+ * 5 CTR-Z2
+ * 6 TIMER
+ * 7 INTERRUPT
+ *
+ * Each PPI is a 8255 chip providing 24 DIO channels. The DIO channels
+ * are configurable as inputs or outputs in four groups:
+ *
+ * Port A - channels 0 to 7
+ * Port B - channels 8 to 15
+ * Port CL - channels 16 to 19
+ * Port CH - channels 20 to 23
+ *
+ * Only mode 0 of the 8255 chips is supported.
+ *
+ * Each CTR is a 8254 chip providing 3 16-bit counter channels. Each
+ * channel is configured individually with INSN_CONFIG instructions. The
+ * specific type of configuration instruction is specified in data[0].
+ * Some configuration instructions expect an additional parameter in
+ * data[1]; others return a value in data[1]. The following configuration
+ * instructions are supported:
+ *
+ * INSN_CONFIG_SET_COUNTER_MODE. Sets the counter channel's mode and
+ * BCD/binary setting specified in data[1].
+ *
+ * INSN_CONFIG_8254_READ_STATUS. Reads the status register value for the
+ * counter channel into data[1].
+ *
+ * INSN_CONFIG_SET_CLOCK_SRC. Sets the counter channel's clock source as
+ * specified in data[1] (this is a hardware-specific value). Not
+ * supported on PC214E. For the other boards, valid clock sources are
+ * 0 to 7 as follows:
+ *
+ * 0. CLK n, the counter channel's dedicated CLK input from the SK1
+ * connector. (N.B. for other values, the counter channel's CLKn
+ * pin on the SK1 connector is an output!)
+ * 1. Internal 10 MHz clock.
+ * 2. Internal 1 MHz clock.
+ * 3. Internal 100 kHz clock.
+ * 4. Internal 10 kHz clock.
+ * 5. Internal 1 kHz clock.
+ * 6. OUT n-1, the output of counter channel n-1 (see note 1 below).
+ * 7. Ext Clock, the counter chip's dedicated Ext Clock input from
+ * the SK1 connector. This pin is shared by all three counter
+ * channels on the chip.
+ *
+ * For the PCIe boards, clock sources in the range 0 to 31 are allowed
+ * and the following additional clock sources are defined:
+ *
+ * 8. HIGH logic level.
+ * 9. LOW logic level.
+ * 10. "Pattern present" signal.
+ * 11. Internal 20 MHz clock.
+ *
+ * INSN_CONFIG_GET_CLOCK_SRC. Returns the counter channel's current
+ * clock source in data[1]. For internal clock sources, data[2] is set
+ * to the period in ns.
+ *
+ * INSN_CONFIG_SET_GATE_SRC. Sets the counter channel's gate source as
+ * specified in data[2] (this is a hardware-specific value). Not
+ * supported on PC214E. For the other boards, valid gate sources are 0
+ * to 7 as follows:
+ *
+ * 0. VCC (internal +5V d.c.), i.e. gate permanently enabled.
+ * 1. GND (internal 0V d.c.), i.e. gate permanently disabled.
+ * 2. GAT n, the counter channel's dedicated GAT input from the SK1
+ * connector. (N.B. for other values, the counter channel's GATn
+ * pin on the SK1 connector is an output!)
+ * 3. /OUT n-2, the inverted output of counter channel n-2 (see note
+ * 2 below).
+ * 4. Reserved.
+ * 5. Reserved.
+ * 6. Reserved.
+ * 7. Reserved.
+ *
+ * For the PCIe boards, gate sources in the range 0 to 31 are allowed;
+ * the following additional clock sources and clock sources 6 and 7 are
+ * (re)defined:
+ *
+ * 6. /GAT n, negated version of the counter channel's dedicated
+ * GAT input (negated version of gate source 2).
+ * 7. OUT n-2, the non-inverted output of counter channel n-2
+ * (negated version of gate source 3).
+ * 8. "Pattern present" signal, HIGH while pattern present.
+ * 9. "Pattern occurred" latched signal, latches HIGH when pattern
+ * occurs.
+ * 10. "Pattern gone away" latched signal, latches LOW when pattern
+ * goes away after it occurred.
+ * 11. Negated "pattern present" signal, LOW while pattern present
+ * (negated version of gate source 8).
+ * 12. Negated "pattern occurred" latched signal, latches LOW when
+ * pattern occurs (negated version of gate source 9).
+ * 13. Negated "pattern gone away" latched signal, latches LOW when
+ * pattern goes away after it occurred (negated version of gate
+ * source 10).
+ *
+ * INSN_CONFIG_GET_GATE_SRC. Returns the counter channel's current gate
+ * source in data[2].
+ *
+ * Clock and gate interconnection notes:
+ *
+ * 1. Clock source OUT n-1 is the output of the preceding channel on the
+ * same counter subdevice if n > 0, or the output of channel 2 on the
+ * preceding counter subdevice (see note 3) if n = 0.
+ *
+ * 2. Gate source /OUT n-2 is the inverted output of channel 0 on the
+ * same counter subdevice if n = 2, or the inverted output of channel n+1
+ * on the preceding counter subdevice (see note 3) if n < 2.
+ *
+ * 3. The counter subdevices are connected in a ring, so the highest
+ * counter subdevice precedes the lowest.
+ *
+ * The 'TIMER' subdevice is a free-running 32-bit timer subdevice.
+ *
+ * The 'INTERRUPT' subdevice pretends to be a digital input subdevice. The
+ * digital inputs come from the interrupt status register. The number of
+ * channels matches the number of interrupt sources. The PC214E does not
+ * have an interrupt status register; see notes on 'INTERRUPT SOURCES'
+ * below.
+ *
+ * INTERRUPT SOURCES
+ *
+ * PCI215 PCIe215 PCIe236
+ * ------------- ------------- -------------
+ * Sources 6 6 6
+ * 0 PPI-X-C0 PPI-X-C0 PPI-X-C0
+ * 1 PPI-X-C3 PPI-X-C3 PPI-X-C3
+ * 2 PPI-Y-C0 PPI-Y-C0 unused
+ * 3 PPI-Y-C3 PPI-Y-C3 unused
+ * 4 CTR-Z1-OUT1 CTR-Z1-OUT1 CTR-Z1-OUT1
+ * 5 CTR-Z2-OUT1 CTR-Z2-OUT1 CTR-Z2-OUT1
+ *
+ * PCI272 PCIe296
+ * ------------- -------------
+ * Sources 6 6
+ * 0 PPI-X-C0 PPI-X1-C0
+ * 1 PPI-X-C3 PPI-X1-C3
+ * 2 PPI-Y-C0 PPI-Y1-C0
+ * 3 PPI-Y-C3 PPI-Y1-C3
+ * 4 PPI-Z-C0 CTR-Z1-OUT1
+ * 5 PPI-Z-C3 CTR-Z2-OUT1
+ *
+ * When an interrupt source is enabled in the interrupt source enable
+ * register, a rising edge on the source signal latches the corresponding
+ * bit to 1 in the interrupt status register.
+ *
+ * When the interrupt status register value as a whole (actually, just the
+ * 6 least significant bits) goes from zero to non-zero, the board will
+ * generate an interrupt. The interrupt will remain asserted until the
+ * interrupt status register is cleared to zero. To clear a bit to zero in
+ * the interrupt status register, the corresponding interrupt source must
+ * be disabled in the interrupt source enable register (there is no
+ * separate interrupt clear register).
+ *
+ * COMMANDS
+ *
+ * The driver supports a read streaming acquisition command on the
+ * 'INTERRUPT' subdevice. The channel list selects the interrupt sources
+ * to be enabled. All channels will be sampled together (convert_src ==
+ * TRIG_NOW). The scan begins a short time after the hardware interrupt
+ * occurs, subject to interrupt latencies (scan_begin_src == TRIG_EXT,
+ * scan_begin_arg == 0). The value read from the interrupt status register
+ * is packed into a short value, one bit per requested channel, in the
+ * order they appear in the channel list.
+ */
+
+#include <linux/module.h>
+#include <linux/interrupt.h>
+
+#include "../comedi_pci.h"
+
+#include "amplc_dio200.h"
+
+/*
+ * Board descriptions.
+ */
+
+enum dio200_pci_model {
+ pci215_model,
+ pci272_model,
+ pcie215_model,
+ pcie236_model,
+ pcie296_model
+};
+
+static const struct dio200_board dio200_pci_boards[] = {
+ [pci215_model] = {
+ .name = "pci215",
+ .mainbar = 2,
+ .n_subdevs = 5,
+ .sdtype = {
+ sd_8255, sd_8255, sd_8254, sd_8254, sd_intr
+ },
+ .sdinfo = { 0x00, 0x08, 0x10, 0x14, 0x3f },
+ .has_int_sce = true,
+ .has_clk_gat_sce = true,
+ },
+ [pci272_model] = {
+ .name = "pci272",
+ .mainbar = 2,
+ .n_subdevs = 4,
+ .sdtype = {
+ sd_8255, sd_8255, sd_8255, sd_intr
+ },
+ .sdinfo = { 0x00, 0x08, 0x10, 0x3f },
+ .has_int_sce = true,
+ },
+ [pcie215_model] = {
+ .name = "pcie215",
+ .mainbar = 1,
+ .n_subdevs = 8,
+ .sdtype = {
+ sd_8255, sd_none, sd_8255, sd_none,
+ sd_8254, sd_8254, sd_timer, sd_intr
+ },
+ .sdinfo = {
+ 0x00, 0x00, 0x08, 0x00, 0x10, 0x14, 0x00, 0x3f
+ },
+ .has_int_sce = true,
+ .has_clk_gat_sce = true,
+ .is_pcie = true,
+ },
+ [pcie236_model] = {
+ .name = "pcie236",
+ .mainbar = 1,
+ .n_subdevs = 8,
+ .sdtype = {
+ sd_8255, sd_none, sd_none, sd_none,
+ sd_8254, sd_8254, sd_timer, sd_intr
+ },
+ .sdinfo = {
+ 0x00, 0x00, 0x00, 0x00, 0x10, 0x14, 0x00, 0x3f
+ },
+ .has_int_sce = true,
+ .has_clk_gat_sce = true,
+ .is_pcie = true,
+ },
+ [pcie296_model] = {
+ .name = "pcie296",
+ .mainbar = 1,
+ .n_subdevs = 8,
+ .sdtype = {
+ sd_8255, sd_8255, sd_8255, sd_8255,
+ sd_8254, sd_8254, sd_timer, sd_intr
+ },
+ .sdinfo = {
+ 0x00, 0x04, 0x08, 0x0c, 0x10, 0x14, 0x00, 0x3f
+ },
+ .has_int_sce = true,
+ .has_clk_gat_sce = true,
+ .is_pcie = true,
+ },
+};
+
+/*
+ * This function does some special set-up for the PCIe boards
+ * PCIe215, PCIe236, PCIe296.
+ */
+static int dio200_pcie_board_setup(struct comedi_device *dev)
+{
+ struct pci_dev *pcidev = comedi_to_pci_dev(dev);
+ void __iomem *brbase;
+
+ /*
+ * The board uses Altera Cyclone IV with PCI-Express hard IP.
+ * The FPGA configuration has the PCI-Express Avalon-MM Bridge
+ * Control registers in PCI BAR 0, offset 0, and the length of
+ * these registers is 0x4000.
+ *
+ * We need to write 0x80 to the "Avalon-MM to PCI-Express Interrupt
+ * Enable" register at offset 0x50 to allow generation of PCIe
+ * interrupts when RXmlrq_i is asserted in the SOPC Builder system.
+ */
+ if (pci_resource_len(pcidev, 0) < 0x4000) {
+ dev_err(dev->class_dev, "error! bad PCI region!\n");
+ return -EINVAL;
+ }
+ brbase = pci_ioremap_bar(pcidev, 0);
+ if (!brbase) {
+ dev_err(dev->class_dev, "error! failed to map registers!\n");
+ return -ENOMEM;
+ }
+ writel(0x80, brbase + 0x50);
+ iounmap(brbase);
+ /* Enable "enhanced" features of board. */
+ amplc_dio200_set_enhance(dev, 1);
+ return 0;
+}
+
+static int dio200_pci_auto_attach(struct comedi_device *dev,
+ unsigned long context_model)
+{
+ struct pci_dev *pci_dev = comedi_to_pci_dev(dev);
+ const struct dio200_board *board = NULL;
+ unsigned int bar;
+ int ret;
+
+ if (context_model < ARRAY_SIZE(dio200_pci_boards))
+ board = &dio200_pci_boards[context_model];
+ if (!board)
+ return -EINVAL;
+ dev->board_ptr = board;
+ dev->board_name = board->name;
+
+ dev_info(dev->class_dev, "%s: attach pci %s (%s)\n",
+ dev->driver->driver_name, pci_name(pci_dev), dev->board_name);
+
+ ret = comedi_pci_enable(dev);
+ if (ret)
+ return ret;
+
+ bar = board->mainbar;
+ if (pci_resource_flags(pci_dev, bar) & IORESOURCE_MEM) {
+ dev->mmio = pci_ioremap_bar(pci_dev, bar);
+ if (!dev->mmio) {
+ dev_err(dev->class_dev,
+ "error! cannot remap registers\n");
+ return -ENOMEM;
+ }
+ } else {
+ dev->iobase = pci_resource_start(pci_dev, bar);
+ }
+
+ if (board->is_pcie) {
+ ret = dio200_pcie_board_setup(dev);
+ if (ret < 0)
+ return ret;
+ }
+
+ return amplc_dio200_common_attach(dev, pci_dev->irq, IRQF_SHARED);
+}
+
+static struct comedi_driver dio200_pci_comedi_driver = {
+ .driver_name = "amplc_dio200_pci",
+ .module = THIS_MODULE,
+ .auto_attach = dio200_pci_auto_attach,
+ .detach = comedi_pci_detach,
+};
+
+static const struct pci_device_id dio200_pci_table[] = {
+ { PCI_VDEVICE(AMPLICON, 0x000b), pci215_model },
+ { PCI_VDEVICE(AMPLICON, 0x000a), pci272_model },
+ { PCI_VDEVICE(AMPLICON, 0x0011), pcie236_model },
+ { PCI_VDEVICE(AMPLICON, 0x0012), pcie215_model },
+ { PCI_VDEVICE(AMPLICON, 0x0014), pcie296_model },
+ {0}
+};
+
+MODULE_DEVICE_TABLE(pci, dio200_pci_table);
+
+static int dio200_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
+{
+ return comedi_pci_auto_config(dev, &dio200_pci_comedi_driver,
+ id->driver_data);
+}
+
+static struct pci_driver dio200_pci_pci_driver = {
+ .name = "amplc_dio200_pci",
+ .id_table = dio200_pci_table,
+ .probe = dio200_pci_probe,
+ .remove = comedi_pci_auto_unconfig,
+};
+module_comedi_pci_driver(dio200_pci_comedi_driver, dio200_pci_pci_driver);
+
+MODULE_AUTHOR("Comedi http://www.comedi.org");
+MODULE_DESCRIPTION("Comedi driver for Amplicon 200 Series PCI(e) DIO boards");
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/comedi/drivers/amplc_pc236.c b/drivers/staging/comedi/drivers/amplc_pc236.c
new file mode 100644
index 000000000..875cc19cb
--- /dev/null
+++ b/drivers/staging/comedi/drivers/amplc_pc236.c
@@ -0,0 +1,85 @@
+/*
+ * comedi/drivers/amplc_pc236.c
+ * Driver for Amplicon PC36AT DIO boards.
+ *
+ * Copyright (C) 2002 MEV Ltd. <http://www.mev.co.uk/>
+ *
+ * COMEDI - Linux Control and Measurement Device Interface
+ * Copyright (C) 2000 David A. Schleef <ds@schleef.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+/*
+ * Driver: amplc_pc236
+ * Description: Amplicon PC36AT
+ * Author: Ian Abbott <abbotti@mev.co.uk>
+ * Devices: [Amplicon] PC36AT (pc36at)
+ * Updated: Fri, 25 Jul 2014 15:32:40 +0000
+ * Status: works
+ *
+ * Configuration options - PC36AT:
+ * [0] - I/O port base address
+ * [1] - IRQ (optional)
+ *
+ * The PC36AT board has a single 8255 appearing as subdevice 0.
+ *
+ * Subdevice 1 pretends to be a digital input device, but it always returns
+ * 0 when read. However, if you run a command with scan_begin_src=TRIG_EXT,
+ * a rising edge on port C bit 3 acts as an external trigger, which can be
+ * used to wake up tasks. This is like the comedi_parport device, but the
+ * only way to physically disable the interrupt on the PC36AT is to remove
+ * the IRQ jumper. If no interrupt is connected, then subdevice 1 is
+ * unused.
+ */
+
+#include <linux/module.h>
+
+#include "../comedidev.h"
+
+#include "amplc_pc236.h"
+
+static int pc236_attach(struct comedi_device *dev, struct comedi_devconfig *it)
+{
+ struct pc236_private *devpriv;
+ int ret;
+
+ devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
+ if (!devpriv)
+ return -ENOMEM;
+
+ ret = comedi_request_region(dev, it->options[0], 0x4);
+ if (ret)
+ return ret;
+
+ return amplc_pc236_common_attach(dev, dev->iobase, it->options[1], 0);
+}
+
+static const struct pc236_board pc236_boards[] = {
+ {
+ .name = "pc36at",
+ },
+};
+
+static struct comedi_driver amplc_pc236_driver = {
+ .driver_name = "amplc_pc236",
+ .module = THIS_MODULE,
+ .attach = pc236_attach,
+ .detach = comedi_legacy_detach,
+ .board_name = &pc236_boards[0].name,
+ .offset = sizeof(struct pc236_board),
+ .num_names = ARRAY_SIZE(pc236_boards),
+};
+
+module_comedi_driver(amplc_pc236_driver);
+
+MODULE_AUTHOR("Comedi http://www.comedi.org");
+MODULE_DESCRIPTION("Comedi driver for Amplicon PC36AT DIO boards");
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/comedi/drivers/amplc_pc236.h b/drivers/staging/comedi/drivers/amplc_pc236.h
new file mode 100644
index 000000000..91d6d9c06
--- /dev/null
+++ b/drivers/staging/comedi/drivers/amplc_pc236.h
@@ -0,0 +1,42 @@
+/*
+ * comedi/drivers/amplc_pc236.h
+ * Header for "amplc_pc236", "amplc_pci236" and "amplc_pc236_common".
+ *
+ * Copyright (C) 2002-2014 MEV Ltd. <http://www.mev.co.uk/>
+ *
+ * COMEDI - Linux Control and Measurement Device Interface
+ * Copyright (C) 2000 David A. Schleef <ds@schleef.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef AMPLC_PC236_H_INCLUDED
+#define AMPLC_PC236_H_INCLUDED
+
+#include <linux/types.h>
+
+struct comedi_device;
+
+struct pc236_board {
+ const char *name;
+ void (*intr_update_cb)(struct comedi_device *dev, bool enable);
+ bool (*intr_chk_clr_cb)(struct comedi_device *dev);
+};
+
+struct pc236_private {
+ unsigned long lcr_iobase; /* PLX PCI9052 config registers in PCIBAR1 */
+ bool enable_irq;
+};
+
+int amplc_pc236_common_attach(struct comedi_device *dev, unsigned long iobase,
+ unsigned int irq, unsigned long req_irq_flags);
+
+#endif
diff --git a/drivers/staging/comedi/drivers/amplc_pc236_common.c b/drivers/staging/comedi/drivers/amplc_pc236_common.c
new file mode 100644
index 000000000..245f932a7
--- /dev/null
+++ b/drivers/staging/comedi/drivers/amplc_pc236_common.c
@@ -0,0 +1,200 @@
+/*
+ * comedi/drivers/amplc_pc236_common.c
+ * Common support code for "amplc_pc236" and "amplc_pci236".
+ *
+ * Copyright (C) 2002-2014 MEV Ltd. <http://www.mev.co.uk/>
+ *
+ * COMEDI - Linux Control and Measurement Device Interface
+ * Copyright (C) 2000 David A. Schleef <ds@schleef.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/interrupt.h>
+
+#include "../comedidev.h"
+
+#include "amplc_pc236.h"
+#include "8255.h"
+
+static void pc236_intr_update(struct comedi_device *dev, bool enable)
+{
+ const struct pc236_board *thisboard = dev->board_ptr;
+ struct pc236_private *devpriv = dev->private;
+ unsigned long flags;
+
+ spin_lock_irqsave(&dev->spinlock, flags);
+ devpriv->enable_irq = enable;
+ if (thisboard->intr_update_cb)
+ thisboard->intr_update_cb(dev, enable);
+ spin_unlock_irqrestore(&dev->spinlock, flags);
+}
+
+/*
+ * This function is called when an interrupt occurs to check whether
+ * the interrupt has been marked as enabled and was generated by the
+ * board. If so, the function prepares the hardware for the next
+ * interrupt.
+ * Returns false if the interrupt should be ignored.
+ */
+static bool pc236_intr_check(struct comedi_device *dev)
+{
+ const struct pc236_board *thisboard = dev->board_ptr;
+ struct pc236_private *devpriv = dev->private;
+ bool retval = false;
+ unsigned long flags;
+
+ spin_lock_irqsave(&dev->spinlock, flags);
+ if (devpriv->enable_irq) {
+ if (thisboard->intr_chk_clr_cb)
+ retval = thisboard->intr_chk_clr_cb(dev);
+ else
+ retval = true;
+ }
+ spin_unlock_irqrestore(&dev->spinlock, flags);
+
+ return retval;
+}
+
+static int pc236_intr_insn(struct comedi_device *dev,
+ struct comedi_subdevice *s, struct comedi_insn *insn,
+ unsigned int *data)
+{
+ data[1] = 0;
+ return insn->n;
+}
+
+static int pc236_intr_cmdtest(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_cmd *cmd)
+{
+ int err = 0;
+
+ /* Step 1 : check if triggers are trivially valid */
+
+ err |= comedi_check_trigger_src(&cmd->start_src, TRIG_NOW);
+ err |= comedi_check_trigger_src(&cmd->scan_begin_src, TRIG_EXT);
+ err |= comedi_check_trigger_src(&cmd->convert_src, TRIG_FOLLOW);
+ err |= comedi_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
+ err |= comedi_check_trigger_src(&cmd->stop_src, TRIG_NONE);
+
+ if (err)
+ return 1;
+
+ /* Step 2a : make sure trigger sources are unique */
+ /* Step 2b : and mutually compatible */
+
+ /* Step 3: check it arguments are trivially valid */
+
+ err |= comedi_check_trigger_arg_is(&cmd->start_arg, 0);
+ err |= comedi_check_trigger_arg_is(&cmd->scan_begin_arg, 0);
+ err |= comedi_check_trigger_arg_is(&cmd->convert_arg, 0);
+ err |= comedi_check_trigger_arg_is(&cmd->scan_end_arg,
+ cmd->chanlist_len);
+ err |= comedi_check_trigger_arg_is(&cmd->stop_arg, 0);
+
+ if (err)
+ return 3;
+
+ /* Step 4: fix up any arguments */
+
+ /* Step 5: check channel list if it exists */
+
+ return 0;
+}
+
+static int pc236_intr_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
+{
+ pc236_intr_update(dev, true);
+
+ return 0;
+}
+
+static int pc236_intr_cancel(struct comedi_device *dev,
+ struct comedi_subdevice *s)
+{
+ pc236_intr_update(dev, false);
+
+ return 0;
+}
+
+static irqreturn_t pc236_interrupt(int irq, void *d)
+{
+ struct comedi_device *dev = d;
+ struct comedi_subdevice *s = dev->read_subdev;
+ bool handled;
+
+ handled = pc236_intr_check(dev);
+ if (dev->attached && handled) {
+ comedi_buf_write_samples(s, &s->state, 1);
+ comedi_handle_events(dev, s);
+ }
+ return IRQ_RETVAL(handled);
+}
+
+int amplc_pc236_common_attach(struct comedi_device *dev, unsigned long iobase,
+ unsigned int irq, unsigned long req_irq_flags)
+{
+ struct comedi_subdevice *s;
+ int ret;
+
+ dev->iobase = iobase;
+
+ ret = comedi_alloc_subdevices(dev, 2);
+ if (ret)
+ return ret;
+
+ s = &dev->subdevices[0];
+ /* digital i/o subdevice (8255) */
+ ret = subdev_8255_init(dev, s, NULL, 0x00);
+ if (ret)
+ return ret;
+
+ s = &dev->subdevices[1];
+ dev->read_subdev = s;
+ s->type = COMEDI_SUBD_UNUSED;
+ pc236_intr_update(dev, false);
+ if (irq) {
+ if (request_irq(irq, pc236_interrupt, req_irq_flags,
+ dev->board_name, dev) >= 0) {
+ dev->irq = irq;
+ s->type = COMEDI_SUBD_DI;
+ s->subdev_flags = SDF_READABLE | SDF_CMD_READ;
+ s->n_chan = 1;
+ s->maxdata = 1;
+ s->range_table = &range_digital;
+ s->insn_bits = pc236_intr_insn;
+ s->len_chanlist = 1;
+ s->do_cmdtest = pc236_intr_cmdtest;
+ s->do_cmd = pc236_intr_cmd;
+ s->cancel = pc236_intr_cancel;
+ }
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(amplc_pc236_common_attach);
+
+static int __init amplc_pc236_common_init(void)
+{
+ return 0;
+}
+module_init(amplc_pc236_common_init);
+
+static void __exit amplc_pc236_common_exit(void)
+{
+}
+module_exit(amplc_pc236_common_exit);
+
+MODULE_AUTHOR("Comedi http://www.comedi.org");
+MODULE_DESCRIPTION("Comedi helper for amplc_pc236 and amplc_pci236");
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/comedi/drivers/amplc_pc263.c b/drivers/staging/comedi/drivers/amplc_pc263.c
new file mode 100644
index 000000000..b1946ce6e
--- /dev/null
+++ b/drivers/staging/comedi/drivers/amplc_pc263.c
@@ -0,0 +1,111 @@
+/*
+ comedi/drivers/amplc_pc263.c
+ Driver for Amplicon PC263 and PCI263 relay boards.
+
+ Copyright (C) 2002 MEV Ltd. <http://www.mev.co.uk/>
+
+ COMEDI - Linux Control and Measurement Device Interface
+ Copyright (C) 2000 David A. Schleef <ds@schleef.org>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+*/
+/*
+Driver: amplc_pc263
+Description: Amplicon PC263
+Author: Ian Abbott <abbotti@mev.co.uk>
+Devices: [Amplicon] PC263 (pc263)
+Updated: Fri, 12 Apr 2013 15:19:36 +0100
+Status: works
+
+Configuration options:
+ [0] - I/O port base address
+
+The board appears as one subdevice, with 16 digital outputs, each
+connected to a reed-relay. Relay contacts are closed when output is 1.
+The state of the outputs can be read.
+*/
+
+#include <linux/module.h>
+#include "../comedidev.h"
+
+/* PC263 registers */
+
+/*
+ * Board descriptions for Amplicon PC263.
+ */
+
+struct pc263_board {
+ const char *name;
+};
+
+static const struct pc263_board pc263_boards[] = {
+ {
+ .name = "pc263",
+ },
+};
+
+static int pc263_do_insn_bits(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ if (comedi_dio_update_state(s, data)) {
+ outb(s->state & 0xff, dev->iobase);
+ outb((s->state >> 8) & 0xff, dev->iobase + 1);
+ }
+
+ data[1] = s->state;
+
+ return insn->n;
+}
+
+static int pc263_attach(struct comedi_device *dev, struct comedi_devconfig *it)
+{
+ struct comedi_subdevice *s;
+ int ret;
+
+ ret = comedi_request_region(dev, it->options[0], 0x2);
+ if (ret)
+ return ret;
+
+ ret = comedi_alloc_subdevices(dev, 1);
+ if (ret)
+ return ret;
+
+ s = &dev->subdevices[0];
+ /* digital output subdevice */
+ s->type = COMEDI_SUBD_DO;
+ s->subdev_flags = SDF_WRITABLE;
+ s->n_chan = 16;
+ s->maxdata = 1;
+ s->range_table = &range_digital;
+ s->insn_bits = pc263_do_insn_bits;
+ /* read initial relay state */
+ s->state = inb(dev->iobase) | (inb(dev->iobase + 1) << 8);
+
+ return 0;
+}
+
+static struct comedi_driver amplc_pc263_driver = {
+ .driver_name = "amplc_pc263",
+ .module = THIS_MODULE,
+ .attach = pc263_attach,
+ .detach = comedi_legacy_detach,
+ .board_name = &pc263_boards[0].name,
+ .offset = sizeof(struct pc263_board),
+ .num_names = ARRAY_SIZE(pc263_boards),
+};
+
+module_comedi_driver(amplc_pc263_driver);
+
+MODULE_AUTHOR("Comedi http://www.comedi.org");
+MODULE_DESCRIPTION("Comedi driver for Amplicon PC263 relay board");
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/comedi/drivers/amplc_pci224.c b/drivers/staging/comedi/drivers/amplc_pci224.c
new file mode 100644
index 000000000..08a918548
--- /dev/null
+++ b/drivers/staging/comedi/drivers/amplc_pci224.c
@@ -0,0 +1,1136 @@
+/*
+ * comedi/drivers/amplc_pci224.c
+ * Driver for Amplicon PCI224 and PCI234 AO boards.
+ *
+ * Copyright (C) 2005 MEV Ltd. <http://www.mev.co.uk/>
+ *
+ * COMEDI - Linux Control and Measurement Device Interface
+ * Copyright (C) 1998,2000 David A. Schleef <ds@schleef.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+/*
+ * Driver: amplc_pci224
+ * Description: Amplicon PCI224, PCI234
+ * Author: Ian Abbott <abbotti@mev.co.uk>
+ * Devices: [Amplicon] PCI224 (amplc_pci224), PCI234
+ * Updated: Thu, 31 Jul 2014 11:08:03 +0000
+ * Status: works, but see caveats
+ *
+ * Supports:
+ *
+ * - ao_insn read/write
+ * - ao_do_cmd mode with the following sources:
+ *
+ * - start_src TRIG_INT TRIG_EXT
+ * - scan_begin_src TRIG_TIMER TRIG_EXT
+ * - convert_src TRIG_NOW
+ * - scan_end_src TRIG_COUNT
+ * - stop_src TRIG_COUNT TRIG_EXT TRIG_NONE
+ *
+ * The channel list must contain at least one channel with no repeated
+ * channels. The scan end count must equal the number of channels in
+ * the channel list.
+ *
+ * There is only one external trigger source so only one of start_src,
+ * scan_begin_src or stop_src may use TRIG_EXT.
+ *
+ * Configuration options:
+ * none
+ *
+ * Manual configuration of PCI cards is not supported; they are configured
+ * automatically.
+ *
+ * Output range selection - PCI224:
+ *
+ * Output ranges on PCI224 are partly software-selectable and partly
+ * hardware-selectable according to jumper LK1. All channels are set
+ * to the same range:
+ *
+ * - LK1 position 1-2 (factory default) corresponds to the following
+ * comedi ranges:
+ *
+ * 0: [-10V,+10V]; 1: [-5V,+5V]; 2: [-2.5V,+2.5V], 3: [-1.25V,+1.25V],
+ * 4: [0,+10V], 5: [0,+5V], 6: [0,+2.5V], 7: [0,+1.25V]
+ *
+ * - LK1 position 2-3 corresponds to the following Comedi ranges, using
+ * an external voltage reference:
+ *
+ * 0: [-Vext,+Vext],
+ * 1: [0,+Vext]
+ *
+ * Output range selection - PCI234:
+ *
+ * Output ranges on PCI234 are hardware-selectable according to jumper
+ * LK1 which affects all channels, and jumpers LK2, LK3, LK4 and LK5
+ * which affect channels 0, 1, 2 and 3 individually. LK1 chooses between
+ * an internal 5V reference and an external voltage reference (Vext).
+ * LK2/3/4/5 choose (per channel) to double the reference or not according
+ * to the following table:
+ *
+ * LK1 position LK2/3/4/5 pos Comedi range
+ * ------------- ------------- --------------
+ * 2-3 (factory) 1-2 (factory) 0: [-10V,+10V]
+ * 2-3 (factory) 2-3 1: [-5V,+5V]
+ * 1-2 1-2 (factory) 2: [-2*Vext,+2*Vext]
+ * 1-2 2-3 3: [-Vext,+Vext]
+ *
+ * Caveats:
+ *
+ * 1) All channels on the PCI224 share the same range. Any change to the
+ * range as a result of insn_write or a streaming command will affect
+ * the output voltages of all channels, including those not specified
+ * by the instruction or command.
+ *
+ * 2) For the analog output command, the first scan may be triggered
+ * falsely at the start of acquisition. This occurs when the DAC scan
+ * trigger source is switched from 'none' to 'timer' (scan_begin_src =
+ * TRIG_TIMER) or 'external' (scan_begin_src == TRIG_EXT) at the start
+ * of acquisition and the trigger source is at logic level 1 at the
+ * time of the switch. This is very likely for TRIG_TIMER. For
+ * TRIG_EXT, it depends on the state of the external line and whether
+ * the CR_INVERT flag has been set. The remaining scans are triggered
+ * correctly.
+ */
+
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/slab.h>
+
+#include "../comedi_pci.h"
+
+#include "comedi_8254.h"
+
+/*
+ * PCI224/234 i/o space 1 (PCIBAR2) registers.
+ */
+#define PCI224_Z2_BASE 0x14 /* 82C54 counter/timer */
+#define PCI224_ZCLK_SCE 0x1A /* Group Z Clock Configuration Register */
+#define PCI224_ZGAT_SCE 0x1D /* Group Z Gate Configuration Register */
+#define PCI224_INT_SCE 0x1E /* ISR Interrupt source mask register */
+ /* /Interrupt status */
+
+/*
+ * PCI224/234 i/o space 2 (PCIBAR3) 16-bit registers.
+ */
+#define PCI224_DACDATA 0x00 /* (w-o) DAC FIFO data. */
+#define PCI224_SOFTTRIG 0x00 /* (r-o) DAC software scan trigger. */
+#define PCI224_DACCON 0x02 /* (r/w) DAC status/configuration. */
+#define PCI224_FIFOSIZ 0x04 /* (w-o) FIFO size for wraparound mode. */
+#define PCI224_DACCEN 0x06 /* (w-o) DAC channel enable register. */
+
+/*
+ * DACCON values.
+ */
+/* (r/w) Scan trigger. */
+#define PCI224_DACCON_TRIG_MASK (7 << 0)
+#define PCI224_DACCON_TRIG_NONE (0 << 0) /* none */
+#define PCI224_DACCON_TRIG_SW (1 << 0) /* software trig */
+#define PCI224_DACCON_TRIG_EXTP (2 << 0) /* ext +ve edge */
+#define PCI224_DACCON_TRIG_EXTN (3 << 0) /* ext -ve edge */
+#define PCI224_DACCON_TRIG_Z2CT0 (4 << 0) /* Z2 CT0 out */
+#define PCI224_DACCON_TRIG_Z2CT1 (5 << 0) /* Z2 CT1 out */
+#define PCI224_DACCON_TRIG_Z2CT2 (6 << 0) /* Z2 CT2 out */
+/* (r/w) Polarity (PCI224 only, PCI234 always bipolar!). */
+#define PCI224_DACCON_POLAR_MASK (1 << 3)
+#define PCI224_DACCON_POLAR_UNI (0 << 3) /* range [0,Vref] */
+#define PCI224_DACCON_POLAR_BI (1 << 3) /* range [-Vref,Vref] */
+/* (r/w) Internal Vref (PCI224 only, when LK1 in position 1-2). */
+#define PCI224_DACCON_VREF_MASK (3 << 4)
+#define PCI224_DACCON_VREF_1_25 (0 << 4) /* Vref = 1.25V */
+#define PCI224_DACCON_VREF_2_5 (1 << 4) /* Vref = 2.5V */
+#define PCI224_DACCON_VREF_5 (2 << 4) /* Vref = 5V */
+#define PCI224_DACCON_VREF_10 (3 << 4) /* Vref = 10V */
+/* (r/w) Wraparound mode enable (to play back stored waveform). */
+#define PCI224_DACCON_FIFOWRAP (1 << 7)
+/* (r/w) FIFO enable. It MUST be set! */
+#define PCI224_DACCON_FIFOENAB (1 << 8)
+/* (r/w) FIFO interrupt trigger level (most values are not very useful). */
+#define PCI224_DACCON_FIFOINTR_MASK (7 << 9)
+#define PCI224_DACCON_FIFOINTR_EMPTY (0 << 9) /* when empty */
+#define PCI224_DACCON_FIFOINTR_NEMPTY (1 << 9) /* when not empty */
+#define PCI224_DACCON_FIFOINTR_NHALF (2 << 9) /* when not half full */
+#define PCI224_DACCON_FIFOINTR_HALF (3 << 9) /* when half full */
+#define PCI224_DACCON_FIFOINTR_NFULL (4 << 9) /* when not full */
+#define PCI224_DACCON_FIFOINTR_FULL (5 << 9) /* when full */
+/* (r-o) FIFO fill level. */
+#define PCI224_DACCON_FIFOFL_MASK (7 << 12)
+#define PCI224_DACCON_FIFOFL_EMPTY (1 << 12) /* 0 */
+#define PCI224_DACCON_FIFOFL_ONETOHALF (0 << 12) /* [1,2048] */
+#define PCI224_DACCON_FIFOFL_HALFTOFULL (4 << 12) /* [2049,4095] */
+#define PCI224_DACCON_FIFOFL_FULL (6 << 12) /* 4096 */
+/* (r-o) DAC busy flag. */
+#define PCI224_DACCON_BUSY (1 << 15)
+/* (w-o) FIFO reset. */
+#define PCI224_DACCON_FIFORESET (1 << 12)
+/* (w-o) Global reset (not sure what it does). */
+#define PCI224_DACCON_GLOBALRESET (1 << 13)
+
+/*
+ * DAC FIFO size.
+ */
+#define PCI224_FIFO_SIZE 4096
+
+/*
+ * DAC FIFO guaranteed minimum room available, depending on reported fill level.
+ * The maximum room available depends on the reported fill level and how much
+ * has been written!
+ */
+#define PCI224_FIFO_ROOM_EMPTY PCI224_FIFO_SIZE
+#define PCI224_FIFO_ROOM_ONETOHALF (PCI224_FIFO_SIZE / 2)
+#define PCI224_FIFO_ROOM_HALFTOFULL 1
+#define PCI224_FIFO_ROOM_FULL 0
+
+/*
+ * Counter/timer clock input configuration sources.
+ */
+#define CLK_CLK 0 /* reserved (channel-specific clock) */
+#define CLK_10MHZ 1 /* internal 10 MHz clock */
+#define CLK_1MHZ 2 /* internal 1 MHz clock */
+#define CLK_100KHZ 3 /* internal 100 kHz clock */
+#define CLK_10KHZ 4 /* internal 10 kHz clock */
+#define CLK_1KHZ 5 /* internal 1 kHz clock */
+#define CLK_OUTNM1 6 /* output of channel-1 modulo total */
+#define CLK_EXT 7 /* external clock */
+/* Macro to construct clock input configuration register value. */
+#define CLK_CONFIG(chan, src) ((((chan) & 3) << 3) | ((src) & 7))
+
+/*
+ * Counter/timer gate input configuration sources.
+ */
+#define GAT_VCC 0 /* VCC (i.e. enabled) */
+#define GAT_GND 1 /* GND (i.e. disabled) */
+#define GAT_EXT 2 /* reserved (external gate input) */
+#define GAT_NOUTNM2 3 /* inverted output of channel-2 modulo total */
+/* Macro to construct gate input configuration register value. */
+#define GAT_CONFIG(chan, src) ((((chan) & 3) << 3) | ((src) & 7))
+
+/*
+ * Summary of CLK_OUTNM1 and GAT_NOUTNM2 connections for PCI224 and PCI234:
+ *
+ * Channel's Channel's
+ * clock input gate input
+ * Channel CLK_OUTNM1 GAT_NOUTNM2
+ * ------- ---------- -----------
+ * Z2-CT0 Z2-CT2-OUT /Z2-CT1-OUT
+ * Z2-CT1 Z2-CT0-OUT /Z2-CT2-OUT
+ * Z2-CT2 Z2-CT1-OUT /Z2-CT0-OUT
+ */
+
+/*
+ * Interrupt enable/status bits
+ */
+#define PCI224_INTR_EXT 0x01 /* rising edge on external input */
+#define PCI224_INTR_DAC 0x04 /* DAC (FIFO) interrupt */
+#define PCI224_INTR_Z2CT1 0x20 /* rising edge on Z2-CT1 output */
+
+#define PCI224_INTR_EDGE_BITS (PCI224_INTR_EXT | PCI224_INTR_Z2CT1)
+#define PCI224_INTR_LEVEL_BITS PCI224_INTR_DACFIFO
+
+/*
+ * Handy macros.
+ */
+
+/* Combine old and new bits. */
+#define COMBINE(old, new, mask) (((old) & ~(mask)) | ((new) & (mask)))
+
+/* Current CPU. XXX should this be hard_smp_processor_id()? */
+#define THISCPU smp_processor_id()
+
+/* State bits for use with atomic bit operations. */
+#define AO_CMD_STARTED 0
+
+/*
+ * Range tables.
+ */
+
+/*
+ * The ranges for PCI224.
+ *
+ * These are partly hardware-selectable by jumper LK1 and partly
+ * software-selectable.
+ *
+ * All channels share the same hardware range.
+ */
+static const struct comedi_lrange range_pci224 = {
+ 10, {
+ /* jumper LK1 in position 1-2 (factory default) */
+ BIP_RANGE(10),
+ BIP_RANGE(5),
+ BIP_RANGE(2.5),
+ BIP_RANGE(1.25),
+ UNI_RANGE(10),
+ UNI_RANGE(5),
+ UNI_RANGE(2.5),
+ UNI_RANGE(1.25),
+ /* jumper LK1 in position 2-3 */
+ RANGE_ext(-1, 1), /* bipolar [-Vext,+Vext] */
+ RANGE_ext(0, 1), /* unipolar [0,+Vext] */
+ }
+};
+
+static const unsigned short hwrange_pci224[10] = {
+ /* jumper LK1 in position 1-2 (factory default) */
+ PCI224_DACCON_POLAR_BI | PCI224_DACCON_VREF_10,
+ PCI224_DACCON_POLAR_BI | PCI224_DACCON_VREF_5,
+ PCI224_DACCON_POLAR_BI | PCI224_DACCON_VREF_2_5,
+ PCI224_DACCON_POLAR_BI | PCI224_DACCON_VREF_1_25,
+ PCI224_DACCON_POLAR_UNI | PCI224_DACCON_VREF_10,
+ PCI224_DACCON_POLAR_UNI | PCI224_DACCON_VREF_5,
+ PCI224_DACCON_POLAR_UNI | PCI224_DACCON_VREF_2_5,
+ PCI224_DACCON_POLAR_UNI | PCI224_DACCON_VREF_1_25,
+ /* jumper LK1 in position 2-3 */
+ PCI224_DACCON_POLAR_BI,
+ PCI224_DACCON_POLAR_UNI,
+};
+
+/* Used to check all channels set to the same range on PCI224. */
+static const unsigned char range_check_pci224[10] = {
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+};
+
+/*
+ * The ranges for PCI234.
+ *
+ * These are all hardware-selectable by jumper LK1 affecting all channels,
+ * and jumpers LK2, LK3, LK4 and LK5 affecting channels 0, 1, 2 and 3
+ * individually.
+ */
+static const struct comedi_lrange range_pci234 = {
+ 4, {
+ /* LK1: 1-2 (fact def), LK2/3/4/5: 2-3 (fac def) */
+ BIP_RANGE(10),
+ /* LK1: 1-2 (fact def), LK2/3/4/5: 1-2 */
+ BIP_RANGE(5),
+ /* LK1: 2-3, LK2/3/4/5: 2-3 (fac def) */
+ RANGE_ext(-2, 2), /* bipolar [-2*Vext,+2*Vext] */
+ /* LK1: 2-3, LK2/3/4/5: 1-2 */
+ RANGE_ext(-1, 1), /* bipolar [-Vext,+Vext] */
+ }
+};
+
+/* N.B. PCI234 ignores the polarity bit, but software uses it. */
+static const unsigned short hwrange_pci234[4] = {
+ PCI224_DACCON_POLAR_BI,
+ PCI224_DACCON_POLAR_BI,
+ PCI224_DACCON_POLAR_BI,
+ PCI224_DACCON_POLAR_BI,
+};
+
+/* Used to check all channels use same LK1 setting on PCI234. */
+static const unsigned char range_check_pci234[4] = {
+ 0, 0, 1, 1,
+};
+
+/*
+ * Board descriptions.
+ */
+
+enum pci224_model { pci224_model, pci234_model };
+
+struct pci224_board {
+ const char *name;
+ unsigned int ao_chans;
+ unsigned int ao_bits;
+ const struct comedi_lrange *ao_range;
+ const unsigned short *ao_hwrange;
+ const unsigned char *ao_range_check;
+};
+
+static const struct pci224_board pci224_boards[] = {
+ [pci224_model] = {
+ .name = "pci224",
+ .ao_chans = 16,
+ .ao_bits = 12,
+ .ao_range = &range_pci224,
+ .ao_hwrange = &hwrange_pci224[0],
+ .ao_range_check = &range_check_pci224[0],
+ },
+ [pci234_model] = {
+ .name = "pci234",
+ .ao_chans = 4,
+ .ao_bits = 16,
+ .ao_range = &range_pci234,
+ .ao_hwrange = &hwrange_pci234[0],
+ .ao_range_check = &range_check_pci234[0],
+ },
+};
+
+struct pci224_private {
+ unsigned long iobase1;
+ unsigned long state;
+ spinlock_t ao_spinlock; /* spinlock for AO command handling */
+ unsigned short *ao_scan_vals;
+ unsigned char *ao_scan_order;
+ int intr_cpuid;
+ short intr_running;
+ unsigned short daccon;
+ unsigned short ao_enab; /* max 16 channels so 'short' will do */
+ unsigned char intsce;
+};
+
+/*
+ * Called from the 'insn_write' function to perform a single write.
+ */
+static void
+pci224_ao_set_data(struct comedi_device *dev, int chan, int range,
+ unsigned int data)
+{
+ const struct pci224_board *thisboard = dev->board_ptr;
+ struct pci224_private *devpriv = dev->private;
+ unsigned short mangled;
+
+ /* Enable the channel. */
+ outw(1 << chan, dev->iobase + PCI224_DACCEN);
+ /* Set range and reset FIFO. */
+ devpriv->daccon = COMBINE(devpriv->daccon, thisboard->ao_hwrange[range],
+ PCI224_DACCON_POLAR_MASK |
+ PCI224_DACCON_VREF_MASK);
+ outw(devpriv->daccon | PCI224_DACCON_FIFORESET,
+ dev->iobase + PCI224_DACCON);
+ /*
+ * Mangle the data. The hardware expects:
+ * - bipolar: 16-bit 2's complement
+ * - unipolar: 16-bit unsigned
+ */
+ mangled = (unsigned short)data << (16 - thisboard->ao_bits);
+ if ((devpriv->daccon & PCI224_DACCON_POLAR_MASK) ==
+ PCI224_DACCON_POLAR_BI) {
+ mangled ^= 0x8000;
+ }
+ /* Write mangled data to the FIFO. */
+ outw(mangled, dev->iobase + PCI224_DACDATA);
+ /* Trigger the conversion. */
+ inw(dev->iobase + PCI224_SOFTTRIG);
+}
+
+static int pci224_ao_insn_write(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ unsigned int chan = CR_CHAN(insn->chanspec);
+ unsigned int range = CR_RANGE(insn->chanspec);
+ unsigned int val = s->readback[chan];
+ int i;
+
+ for (i = 0; i < insn->n; i++) {
+ val = data[i];
+ pci224_ao_set_data(dev, chan, range, val);
+ }
+ s->readback[chan] = val;
+
+ return insn->n;
+}
+
+/*
+ * Kills a command running on the AO subdevice.
+ */
+static void pci224_ao_stop(struct comedi_device *dev,
+ struct comedi_subdevice *s)
+{
+ struct pci224_private *devpriv = dev->private;
+ unsigned long flags;
+
+ if (!test_and_clear_bit(AO_CMD_STARTED, &devpriv->state))
+ return;
+
+ spin_lock_irqsave(&devpriv->ao_spinlock, flags);
+ /* Kill the interrupts. */
+ devpriv->intsce = 0;
+ outb(0, devpriv->iobase1 + PCI224_INT_SCE);
+ /*
+ * Interrupt routine may or may not be running. We may or may not
+ * have been called from the interrupt routine (directly or
+ * indirectly via a comedi_events() callback routine). It's highly
+ * unlikely that we've been called from some other interrupt routine
+ * but who knows what strange things coders get up to!
+ *
+ * If the interrupt routine is currently running, wait for it to
+ * finish, unless we appear to have been called via the interrupt
+ * routine.
+ */
+ while (devpriv->intr_running && devpriv->intr_cpuid != THISCPU) {
+ spin_unlock_irqrestore(&devpriv->ao_spinlock, flags);
+ spin_lock_irqsave(&devpriv->ao_spinlock, flags);
+ }
+ spin_unlock_irqrestore(&devpriv->ao_spinlock, flags);
+ /* Reconfigure DAC for insn_write usage. */
+ outw(0, dev->iobase + PCI224_DACCEN); /* Disable channels. */
+ devpriv->daccon =
+ COMBINE(devpriv->daccon,
+ PCI224_DACCON_TRIG_SW | PCI224_DACCON_FIFOINTR_EMPTY,
+ PCI224_DACCON_TRIG_MASK | PCI224_DACCON_FIFOINTR_MASK);
+ outw(devpriv->daccon | PCI224_DACCON_FIFORESET,
+ dev->iobase + PCI224_DACCON);
+}
+
+/*
+ * Handles start of acquisition for the AO subdevice.
+ */
+static void pci224_ao_start(struct comedi_device *dev,
+ struct comedi_subdevice *s)
+{
+ struct pci224_private *devpriv = dev->private;
+ struct comedi_cmd *cmd = &s->async->cmd;
+ unsigned long flags;
+
+ set_bit(AO_CMD_STARTED, &devpriv->state);
+
+ /* Enable interrupts. */
+ spin_lock_irqsave(&devpriv->ao_spinlock, flags);
+ if (cmd->stop_src == TRIG_EXT)
+ devpriv->intsce = PCI224_INTR_EXT | PCI224_INTR_DAC;
+ else
+ devpriv->intsce = PCI224_INTR_DAC;
+
+ outb(devpriv->intsce, devpriv->iobase1 + PCI224_INT_SCE);
+ spin_unlock_irqrestore(&devpriv->ao_spinlock, flags);
+}
+
+/*
+ * Handles interrupts from the DAC FIFO.
+ */
+static void pci224_ao_handle_fifo(struct comedi_device *dev,
+ struct comedi_subdevice *s)
+{
+ struct pci224_private *devpriv = dev->private;
+ struct comedi_cmd *cmd = &s->async->cmd;
+ unsigned int num_scans = comedi_nscans_left(s, 0);
+ unsigned int room;
+ unsigned short dacstat;
+ unsigned int i, n;
+
+ /* Determine how much room is in the FIFO (in samples). */
+ dacstat = inw(dev->iobase + PCI224_DACCON);
+ switch (dacstat & PCI224_DACCON_FIFOFL_MASK) {
+ case PCI224_DACCON_FIFOFL_EMPTY:
+ room = PCI224_FIFO_ROOM_EMPTY;
+ if (cmd->stop_src == TRIG_COUNT &&
+ s->async->scans_done >= cmd->stop_arg) {
+ /* FIFO empty at end of counted acquisition. */
+ s->async->events |= COMEDI_CB_EOA;
+ comedi_handle_events(dev, s);
+ return;
+ }
+ break;
+ case PCI224_DACCON_FIFOFL_ONETOHALF:
+ room = PCI224_FIFO_ROOM_ONETOHALF;
+ break;
+ case PCI224_DACCON_FIFOFL_HALFTOFULL:
+ room = PCI224_FIFO_ROOM_HALFTOFULL;
+ break;
+ default:
+ room = PCI224_FIFO_ROOM_FULL;
+ break;
+ }
+ if (room >= PCI224_FIFO_ROOM_ONETOHALF) {
+ /* FIFO is less than half-full. */
+ if (num_scans == 0) {
+ /* Nothing left to put in the FIFO. */
+ dev_err(dev->class_dev, "AO buffer underrun\n");
+ s->async->events |= COMEDI_CB_OVERFLOW;
+ }
+ }
+ /* Determine how many new scans can be put in the FIFO. */
+ room /= cmd->chanlist_len;
+
+ /* Determine how many scans to process. */
+ if (num_scans > room)
+ num_scans = room;
+
+ /* Process scans. */
+ for (n = 0; n < num_scans; n++) {
+ comedi_buf_read_samples(s, &devpriv->ao_scan_vals[0],
+ cmd->chanlist_len);
+ for (i = 0; i < cmd->chanlist_len; i++) {
+ outw(devpriv->ao_scan_vals[devpriv->ao_scan_order[i]],
+ dev->iobase + PCI224_DACDATA);
+ }
+ }
+ if (cmd->stop_src == TRIG_COUNT &&
+ s->async->scans_done >= cmd->stop_arg) {
+ /*
+ * Change FIFO interrupt trigger level to wait
+ * until FIFO is empty.
+ */
+ devpriv->daccon = COMBINE(devpriv->daccon,
+ PCI224_DACCON_FIFOINTR_EMPTY,
+ PCI224_DACCON_FIFOINTR_MASK);
+ outw(devpriv->daccon, dev->iobase + PCI224_DACCON);
+ }
+ if ((devpriv->daccon & PCI224_DACCON_TRIG_MASK) ==
+ PCI224_DACCON_TRIG_NONE) {
+ unsigned short trig;
+
+ /*
+ * This is the initial DAC FIFO interrupt at the
+ * start of the acquisition. The DAC's scan trigger
+ * has been set to 'none' up until now.
+ *
+ * Now that data has been written to the FIFO, the
+ * DAC's scan trigger source can be set to the
+ * correct value.
+ *
+ * BUG: The first scan will be triggered immediately
+ * if the scan trigger source is at logic level 1.
+ */
+ if (cmd->scan_begin_src == TRIG_TIMER) {
+ trig = PCI224_DACCON_TRIG_Z2CT0;
+ } else {
+ /* cmd->scan_begin_src == TRIG_EXT */
+ if (cmd->scan_begin_arg & CR_INVERT)
+ trig = PCI224_DACCON_TRIG_EXTN;
+ else
+ trig = PCI224_DACCON_TRIG_EXTP;
+ }
+ devpriv->daccon =
+ COMBINE(devpriv->daccon, trig, PCI224_DACCON_TRIG_MASK);
+ outw(devpriv->daccon, dev->iobase + PCI224_DACCON);
+ }
+
+ comedi_handle_events(dev, s);
+}
+
+static int pci224_ao_inttrig_start(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ unsigned int trig_num)
+{
+ struct comedi_cmd *cmd = &s->async->cmd;
+
+ if (trig_num != cmd->start_arg)
+ return -EINVAL;
+
+ s->async->inttrig = NULL;
+ pci224_ao_start(dev, s);
+
+ return 1;
+}
+
+static int pci224_ao_check_chanlist(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_cmd *cmd)
+{
+ const struct pci224_board *thisboard = dev->board_ptr;
+ unsigned int range_check_0;
+ unsigned int chan_mask = 0;
+ int i;
+
+ range_check_0 = thisboard->ao_range_check[CR_RANGE(cmd->chanlist[0])];
+ for (i = 0; i < cmd->chanlist_len; i++) {
+ unsigned int chan = CR_CHAN(cmd->chanlist[i]);
+
+ if (chan_mask & (1 << chan)) {
+ dev_dbg(dev->class_dev,
+ "%s: entries in chanlist must contain no duplicate channels\n",
+ __func__);
+ return -EINVAL;
+ }
+ chan_mask |= 1 << chan;
+
+ if (thisboard->ao_range_check[CR_RANGE(cmd->chanlist[i])] !=
+ range_check_0) {
+ dev_dbg(dev->class_dev,
+ "%s: entries in chanlist have incompatible ranges\n",
+ __func__);
+ return -EINVAL;
+ }
+ }
+
+ return 0;
+}
+
+#define MAX_SCAN_PERIOD 0xFFFFFFFFU
+#define MIN_SCAN_PERIOD 2500
+#define CONVERT_PERIOD 625
+
+/*
+ * 'do_cmdtest' function for AO subdevice.
+ */
+static int
+pci224_ao_cmdtest(struct comedi_device *dev, struct comedi_subdevice *s,
+ struct comedi_cmd *cmd)
+{
+ int err = 0;
+ unsigned int arg;
+
+ /* Step 1 : check if triggers are trivially valid */
+
+ err |= comedi_check_trigger_src(&cmd->start_src, TRIG_INT | TRIG_EXT);
+ err |= comedi_check_trigger_src(&cmd->scan_begin_src,
+ TRIG_EXT | TRIG_TIMER);
+ err |= comedi_check_trigger_src(&cmd->convert_src, TRIG_NOW);
+ err |= comedi_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
+ err |= comedi_check_trigger_src(&cmd->stop_src,
+ TRIG_COUNT | TRIG_EXT | TRIG_NONE);
+
+ if (err)
+ return 1;
+
+ /* Step 2a : make sure trigger sources are unique */
+
+ err |= comedi_check_trigger_is_unique(cmd->start_src);
+ err |= comedi_check_trigger_is_unique(cmd->scan_begin_src);
+ err |= comedi_check_trigger_is_unique(cmd->stop_src);
+
+ /* Step 2b : and mutually compatible */
+
+ /*
+ * There's only one external trigger signal (which makes these
+ * tests easier). Only one thing can use it.
+ */
+ arg = 0;
+ if (cmd->start_src & TRIG_EXT)
+ arg++;
+ if (cmd->scan_begin_src & TRIG_EXT)
+ arg++;
+ if (cmd->stop_src & TRIG_EXT)
+ arg++;
+ if (arg > 1)
+ err |= -EINVAL;
+
+ if (err)
+ return 2;
+
+ /* Step 3: check if arguments are trivially valid */
+
+ switch (cmd->start_src) {
+ case TRIG_INT:
+ err |= comedi_check_trigger_arg_is(&cmd->start_arg, 0);
+ break;
+ case TRIG_EXT:
+ /* Force to external trigger 0. */
+ if (cmd->start_arg & ~CR_FLAGS_MASK) {
+ cmd->start_arg =
+ COMBINE(cmd->start_arg, 0, ~CR_FLAGS_MASK);
+ err |= -EINVAL;
+ }
+ /* The only flag allowed is CR_EDGE, which is ignored. */
+ if (cmd->start_arg & CR_FLAGS_MASK & ~CR_EDGE) {
+ cmd->start_arg = COMBINE(cmd->start_arg, 0,
+ CR_FLAGS_MASK & ~CR_EDGE);
+ err |= -EINVAL;
+ }
+ break;
+ }
+
+ switch (cmd->scan_begin_src) {
+ case TRIG_TIMER:
+ err |= comedi_check_trigger_arg_max(&cmd->scan_begin_arg,
+ MAX_SCAN_PERIOD);
+
+ arg = cmd->chanlist_len * CONVERT_PERIOD;
+ if (arg < MIN_SCAN_PERIOD)
+ arg = MIN_SCAN_PERIOD;
+ err |= comedi_check_trigger_arg_min(&cmd->scan_begin_arg, arg);
+ break;
+ case TRIG_EXT:
+ /* Force to external trigger 0. */
+ if (cmd->scan_begin_arg & ~CR_FLAGS_MASK) {
+ cmd->scan_begin_arg =
+ COMBINE(cmd->scan_begin_arg, 0, ~CR_FLAGS_MASK);
+ err |= -EINVAL;
+ }
+ /* Only allow flags CR_EDGE and CR_INVERT. Ignore CR_EDGE. */
+ if (cmd->scan_begin_arg & CR_FLAGS_MASK &
+ ~(CR_EDGE | CR_INVERT)) {
+ cmd->scan_begin_arg =
+ COMBINE(cmd->scan_begin_arg, 0,
+ CR_FLAGS_MASK & ~(CR_EDGE | CR_INVERT));
+ err |= -EINVAL;
+ }
+ break;
+ }
+
+ err |= comedi_check_trigger_arg_is(&cmd->convert_arg, 0);
+ err |= comedi_check_trigger_arg_is(&cmd->scan_end_arg,
+ cmd->chanlist_len);
+
+ switch (cmd->stop_src) {
+ case TRIG_COUNT:
+ err |= comedi_check_trigger_arg_min(&cmd->stop_arg, 1);
+ break;
+ case TRIG_EXT:
+ /* Force to external trigger 0. */
+ if (cmd->stop_arg & ~CR_FLAGS_MASK) {
+ cmd->stop_arg =
+ COMBINE(cmd->stop_arg, 0, ~CR_FLAGS_MASK);
+ err |= -EINVAL;
+ }
+ /* The only flag allowed is CR_EDGE, which is ignored. */
+ if (cmd->stop_arg & CR_FLAGS_MASK & ~CR_EDGE) {
+ cmd->stop_arg =
+ COMBINE(cmd->stop_arg, 0, CR_FLAGS_MASK & ~CR_EDGE);
+ }
+ break;
+ case TRIG_NONE:
+ err |= comedi_check_trigger_arg_is(&cmd->stop_arg, 0);
+ break;
+ }
+
+ if (err)
+ return 3;
+
+ /* Step 4: fix up any arguments. */
+
+ if (cmd->scan_begin_src == TRIG_TIMER) {
+ arg = cmd->scan_begin_arg;
+ /* Use two timers. */
+ comedi_8254_cascade_ns_to_timer(dev->pacer, &arg, cmd->flags);
+ err |= comedi_check_trigger_arg_is(&cmd->scan_begin_arg, arg);
+ }
+
+ if (err)
+ return 4;
+
+ /* Step 5: check channel list if it exists */
+ if (cmd->chanlist && cmd->chanlist_len > 0)
+ err |= pci224_ao_check_chanlist(dev, s, cmd);
+
+ if (err)
+ return 5;
+
+ return 0;
+}
+
+static void pci224_ao_start_pacer(struct comedi_device *dev,
+ struct comedi_subdevice *s)
+{
+ struct pci224_private *devpriv = dev->private;
+
+ /*
+ * The output of timer Z2-0 will be used as the scan trigger
+ * source.
+ */
+ /* Make sure Z2-0 is gated on. */
+ outb(GAT_CONFIG(0, GAT_VCC), devpriv->iobase1 + PCI224_ZGAT_SCE);
+ /* Cascading with Z2-2. */
+ /* Make sure Z2-2 is gated on. */
+ outb(GAT_CONFIG(2, GAT_VCC), devpriv->iobase1 + PCI224_ZGAT_SCE);
+ /* Z2-2 needs 10 MHz clock. */
+ outb(CLK_CONFIG(2, CLK_10MHZ), devpriv->iobase1 + PCI224_ZCLK_SCE);
+ /* Z2-0 is clocked from Z2-2's output. */
+ outb(CLK_CONFIG(0, CLK_OUTNM1), devpriv->iobase1 + PCI224_ZCLK_SCE);
+
+ comedi_8254_pacer_enable(dev->pacer, 2, 0, false);
+}
+
+static int pci224_ao_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
+{
+ const struct pci224_board *thisboard = dev->board_ptr;
+ struct pci224_private *devpriv = dev->private;
+ struct comedi_cmd *cmd = &s->async->cmd;
+ int range;
+ unsigned int i, j;
+ unsigned int ch;
+ unsigned int rank;
+ unsigned long flags;
+
+ /* Cannot handle null/empty chanlist. */
+ if (!cmd->chanlist || cmd->chanlist_len == 0)
+ return -EINVAL;
+
+ /* Determine which channels are enabled and their load order. */
+ devpriv->ao_enab = 0;
+
+ for (i = 0; i < cmd->chanlist_len; i++) {
+ ch = CR_CHAN(cmd->chanlist[i]);
+ devpriv->ao_enab |= 1U << ch;
+ rank = 0;
+ for (j = 0; j < cmd->chanlist_len; j++) {
+ if (CR_CHAN(cmd->chanlist[j]) < ch)
+ rank++;
+ }
+ devpriv->ao_scan_order[rank] = i;
+ }
+
+ /* Set enabled channels. */
+ outw(devpriv->ao_enab, dev->iobase + PCI224_DACCEN);
+
+ /* Determine range and polarity. All channels the same. */
+ range = CR_RANGE(cmd->chanlist[0]);
+
+ /*
+ * Set DAC range and polarity.
+ * Set DAC scan trigger source to 'none'.
+ * Set DAC FIFO interrupt trigger level to 'not half full'.
+ * Reset DAC FIFO.
+ *
+ * N.B. DAC FIFO interrupts are currently disabled.
+ */
+ devpriv->daccon =
+ COMBINE(devpriv->daccon,
+ thisboard->ao_hwrange[range] | PCI224_DACCON_TRIG_NONE |
+ PCI224_DACCON_FIFOINTR_NHALF,
+ PCI224_DACCON_POLAR_MASK | PCI224_DACCON_VREF_MASK |
+ PCI224_DACCON_TRIG_MASK | PCI224_DACCON_FIFOINTR_MASK);
+ outw(devpriv->daccon | PCI224_DACCON_FIFORESET,
+ dev->iobase + PCI224_DACCON);
+
+ if (cmd->scan_begin_src == TRIG_TIMER) {
+ comedi_8254_update_divisors(dev->pacer);
+ pci224_ao_start_pacer(dev, s);
+ }
+
+ spin_lock_irqsave(&devpriv->ao_spinlock, flags);
+ if (cmd->start_src == TRIG_INT) {
+ s->async->inttrig = pci224_ao_inttrig_start;
+ } else { /* TRIG_EXT */
+ /* Enable external interrupt trigger to start acquisition. */
+ devpriv->intsce |= PCI224_INTR_EXT;
+ outb(devpriv->intsce, devpriv->iobase1 + PCI224_INT_SCE);
+ }
+ spin_unlock_irqrestore(&devpriv->ao_spinlock, flags);
+
+ return 0;
+}
+
+/*
+ * 'cancel' function for AO subdevice.
+ */
+static int pci224_ao_cancel(struct comedi_device *dev,
+ struct comedi_subdevice *s)
+{
+ pci224_ao_stop(dev, s);
+ return 0;
+}
+
+/*
+ * 'munge' data for AO command.
+ */
+static void
+pci224_ao_munge(struct comedi_device *dev, struct comedi_subdevice *s,
+ void *data, unsigned int num_bytes, unsigned int chan_index)
+{
+ const struct pci224_board *thisboard = dev->board_ptr;
+ struct comedi_cmd *cmd = &s->async->cmd;
+ unsigned short *array = data;
+ unsigned int length = num_bytes / sizeof(*array);
+ unsigned int offset;
+ unsigned int shift;
+ unsigned int i;
+
+ /* The hardware expects 16-bit numbers. */
+ shift = 16 - thisboard->ao_bits;
+ /* Channels will be all bipolar or all unipolar. */
+ if ((thisboard->ao_hwrange[CR_RANGE(cmd->chanlist[0])] &
+ PCI224_DACCON_POLAR_MASK) == PCI224_DACCON_POLAR_UNI) {
+ /* Unipolar */
+ offset = 0;
+ } else {
+ /* Bipolar */
+ offset = 32768;
+ }
+ /* Munge the data. */
+ for (i = 0; i < length; i++)
+ array[i] = (array[i] << shift) - offset;
+}
+
+/*
+ * Interrupt handler.
+ */
+static irqreturn_t pci224_interrupt(int irq, void *d)
+{
+ struct comedi_device *dev = d;
+ struct pci224_private *devpriv = dev->private;
+ struct comedi_subdevice *s = dev->write_subdev;
+ struct comedi_cmd *cmd;
+ unsigned char intstat, valid_intstat;
+ unsigned char curenab;
+ int retval = 0;
+ unsigned long flags;
+
+ intstat = inb(devpriv->iobase1 + PCI224_INT_SCE) & 0x3F;
+ if (intstat) {
+ retval = 1;
+ spin_lock_irqsave(&devpriv->ao_spinlock, flags);
+ valid_intstat = devpriv->intsce & intstat;
+ /* Temporarily disable interrupt sources. */
+ curenab = devpriv->intsce & ~intstat;
+ outb(curenab, devpriv->iobase1 + PCI224_INT_SCE);
+ devpriv->intr_running = 1;
+ devpriv->intr_cpuid = THISCPU;
+ spin_unlock_irqrestore(&devpriv->ao_spinlock, flags);
+ if (valid_intstat) {
+ cmd = &s->async->cmd;
+ if (valid_intstat & PCI224_INTR_EXT) {
+ devpriv->intsce &= ~PCI224_INTR_EXT;
+ if (cmd->start_src == TRIG_EXT)
+ pci224_ao_start(dev, s);
+ else if (cmd->stop_src == TRIG_EXT)
+ pci224_ao_stop(dev, s);
+ }
+ if (valid_intstat & PCI224_INTR_DAC)
+ pci224_ao_handle_fifo(dev, s);
+ }
+ /* Reenable interrupt sources. */
+ spin_lock_irqsave(&devpriv->ao_spinlock, flags);
+ if (curenab != devpriv->intsce) {
+ outb(devpriv->intsce,
+ devpriv->iobase1 + PCI224_INT_SCE);
+ }
+ devpriv->intr_running = 0;
+ spin_unlock_irqrestore(&devpriv->ao_spinlock, flags);
+ }
+ return IRQ_RETVAL(retval);
+}
+
+static int
+pci224_auto_attach(struct comedi_device *dev, unsigned long context_model)
+{
+ struct pci_dev *pci_dev = comedi_to_pci_dev(dev);
+ const struct pci224_board *thisboard = NULL;
+ struct pci224_private *devpriv;
+ struct comedi_subdevice *s;
+ unsigned int irq;
+ int ret;
+
+ if (context_model < ARRAY_SIZE(pci224_boards))
+ thisboard = &pci224_boards[context_model];
+ if (!thisboard || !thisboard->name) {
+ dev_err(dev->class_dev,
+ "amplc_pci224: BUG! cannot determine board type!\n");
+ return -EINVAL;
+ }
+ dev->board_ptr = thisboard;
+ dev->board_name = thisboard->name;
+
+ dev_info(dev->class_dev, "amplc_pci224: attach pci %s - %s\n",
+ pci_name(pci_dev), dev->board_name);
+
+ devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
+ if (!devpriv)
+ return -ENOMEM;
+
+ ret = comedi_pci_enable(dev);
+ if (ret)
+ return ret;
+
+ spin_lock_init(&devpriv->ao_spinlock);
+
+ devpriv->iobase1 = pci_resource_start(pci_dev, 2);
+ dev->iobase = pci_resource_start(pci_dev, 3);
+ irq = pci_dev->irq;
+
+ /* Allocate buffer to hold values for AO channel scan. */
+ devpriv->ao_scan_vals = kmalloc(sizeof(devpriv->ao_scan_vals[0]) *
+ thisboard->ao_chans, GFP_KERNEL);
+ if (!devpriv->ao_scan_vals)
+ return -ENOMEM;
+
+ /* Allocate buffer to hold AO channel scan order. */
+ devpriv->ao_scan_order = kmalloc(sizeof(devpriv->ao_scan_order[0]) *
+ thisboard->ao_chans, GFP_KERNEL);
+ if (!devpriv->ao_scan_order)
+ return -ENOMEM;
+
+ /* Disable interrupt sources. */
+ devpriv->intsce = 0;
+ outb(0, devpriv->iobase1 + PCI224_INT_SCE);
+
+ /* Initialize the DAC hardware. */
+ outw(PCI224_DACCON_GLOBALRESET, dev->iobase + PCI224_DACCON);
+ outw(0, dev->iobase + PCI224_DACCEN);
+ outw(0, dev->iobase + PCI224_FIFOSIZ);
+ devpriv->daccon = PCI224_DACCON_TRIG_SW | PCI224_DACCON_POLAR_BI |
+ PCI224_DACCON_FIFOENAB | PCI224_DACCON_FIFOINTR_EMPTY;
+ outw(devpriv->daccon | PCI224_DACCON_FIFORESET,
+ dev->iobase + PCI224_DACCON);
+
+ dev->pacer = comedi_8254_init(devpriv->iobase1 + PCI224_Z2_BASE,
+ I8254_OSC_BASE_10MHZ, I8254_IO8, 0);
+ if (!dev->pacer)
+ return -ENOMEM;
+
+ ret = comedi_alloc_subdevices(dev, 1);
+ if (ret)
+ return ret;
+
+ s = &dev->subdevices[0];
+ /* Analog output subdevice. */
+ s->type = COMEDI_SUBD_AO;
+ s->subdev_flags = SDF_WRITABLE | SDF_GROUND | SDF_CMD_WRITE;
+ s->n_chan = thisboard->ao_chans;
+ s->maxdata = (1 << thisboard->ao_bits) - 1;
+ s->range_table = thisboard->ao_range;
+ s->insn_write = pci224_ao_insn_write;
+ s->len_chanlist = s->n_chan;
+ dev->write_subdev = s;
+ s->do_cmd = pci224_ao_cmd;
+ s->do_cmdtest = pci224_ao_cmdtest;
+ s->cancel = pci224_ao_cancel;
+ s->munge = pci224_ao_munge;
+
+ ret = comedi_alloc_subdev_readback(s);
+ if (ret)
+ return ret;
+
+ if (irq) {
+ ret = request_irq(irq, pci224_interrupt, IRQF_SHARED,
+ dev->board_name, dev);
+ if (ret < 0) {
+ dev_err(dev->class_dev,
+ "error! unable to allocate irq %u\n", irq);
+ return ret;
+ }
+ dev->irq = irq;
+ }
+
+ return 0;
+}
+
+static void pci224_detach(struct comedi_device *dev)
+{
+ struct pci224_private *devpriv = dev->private;
+
+ comedi_pci_detach(dev);
+ if (devpriv) {
+ kfree(devpriv->ao_scan_vals);
+ kfree(devpriv->ao_scan_order);
+ }
+}
+
+static struct comedi_driver amplc_pci224_driver = {
+ .driver_name = "amplc_pci224",
+ .module = THIS_MODULE,
+ .detach = pci224_detach,
+ .auto_attach = pci224_auto_attach,
+ .board_name = &pci224_boards[0].name,
+ .offset = sizeof(struct pci224_board),
+ .num_names = ARRAY_SIZE(pci224_boards),
+};
+
+static int amplc_pci224_pci_probe(struct pci_dev *dev,
+ const struct pci_device_id *id)
+{
+ return comedi_pci_auto_config(dev, &amplc_pci224_driver,
+ id->driver_data);
+}
+
+static const struct pci_device_id amplc_pci224_pci_table[] = {
+ { PCI_VDEVICE(AMPLICON, 0x0007), pci224_model },
+ { PCI_VDEVICE(AMPLICON, 0x0008), pci234_model },
+ { 0 }
+};
+MODULE_DEVICE_TABLE(pci, amplc_pci224_pci_table);
+
+static struct pci_driver amplc_pci224_pci_driver = {
+ .name = "amplc_pci224",
+ .id_table = amplc_pci224_pci_table,
+ .probe = amplc_pci224_pci_probe,
+ .remove = comedi_pci_auto_unconfig,
+};
+module_comedi_pci_driver(amplc_pci224_driver, amplc_pci224_pci_driver);
+
+MODULE_AUTHOR("Comedi http://www.comedi.org");
+MODULE_DESCRIPTION("Comedi driver for Amplicon PCI224 and PCI234 AO boards");
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/comedi/drivers/amplc_pci230.c b/drivers/staging/comedi/drivers/amplc_pci230.c
new file mode 100644
index 000000000..20d592002
--- /dev/null
+++ b/drivers/staging/comedi/drivers/amplc_pci230.c
@@ -0,0 +1,2568 @@
+/*
+ * comedi/drivers/amplc_pci230.c
+ * Driver for Amplicon PCI230 and PCI260 Multifunction I/O boards.
+ *
+ * Copyright (C) 2001 Allan Willcox <allanwillcox@ozemail.com.au>
+ *
+ * COMEDI - Linux Control and Measurement Device Interface
+ * Copyright (C) 2000 David A. Schleef <ds@schleef.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+/*
+ * Driver: amplc_pci230
+ * Description: Amplicon PCI230, PCI260 Multifunction I/O boards
+ * Author: Allan Willcox <allanwillcox@ozemail.com.au>,
+ * Steve D Sharples <steve.sharples@nottingham.ac.uk>,
+ * Ian Abbott <abbotti@mev.co.uk>
+ * Updated: Mon, 01 Sep 2014 10:09:16 +0000
+ * Devices: [Amplicon] PCI230 (amplc_pci230), PCI230+, PCI260, PCI260+
+ * Status: works
+ *
+ * Configuration options:
+ * none
+ *
+ * Manual configuration of PCI cards is not supported; they are configured
+ * automatically.
+ *
+ * The PCI230+ and PCI260+ have the same PCI device IDs as the PCI230 and
+ * PCI260, but can be distinguished by the size of the PCI regions. A
+ * card will be configured as a "+" model if detected as such.
+ *
+ * Subdevices:
+ *
+ * PCI230(+) PCI260(+)
+ * --------- ---------
+ * Subdevices 3 1
+ * 0 AI AI
+ * 1 AO
+ * 2 DIO
+ *
+ * AI Subdevice:
+ *
+ * The AI subdevice has 16 single-ended channels or 8 differential
+ * channels.
+ *
+ * The PCI230 and PCI260 cards have 12-bit resolution. The PCI230+ and
+ * PCI260+ cards have 16-bit resolution.
+ *
+ * For differential mode, use inputs 2N and 2N+1 for channel N (e.g. use
+ * inputs 14 and 15 for channel 7). If the card is physically a PCI230
+ * or PCI260 then it actually uses a "pseudo-differential" mode where the
+ * inputs are sampled a few microseconds apart. The PCI230+ and PCI260+
+ * use true differential sampling. Another difference is that if the
+ * card is physically a PCI230 or PCI260, the inverting input is 2N,
+ * whereas for a PCI230+ or PCI260+ the inverting input is 2N+1. So if a
+ * PCI230 is physically replaced by a PCI230+ (or a PCI260 with a
+ * PCI260+) and differential mode is used, the differential inputs need
+ * to be physically swapped on the connector.
+ *
+ * The following input ranges are supported:
+ *
+ * 0 => [-10, +10] V
+ * 1 => [-5, +5] V
+ * 2 => [-2.5, +2.5] V
+ * 3 => [-1.25, +1.25] V
+ * 4 => [0, 10] V
+ * 5 => [0, 5] V
+ * 6 => [0, 2.5] V
+ *
+ * AI Commands:
+ *
+ * +=========+==============+===========+============+==========+
+ * |start_src|scan_begin_src|convert_src|scan_end_src| stop_src |
+ * +=========+==============+===========+============+==========+
+ * |TRIG_NOW | TRIG_FOLLOW |TRIG_TIMER | TRIG_COUNT |TRIG_NONE |
+ * |TRIG_INT | |TRIG_EXT(3)| |TRIG_COUNT|
+ * | | |TRIG_INT | | |
+ * | |--------------|-----------| | |
+ * | | TRIG_TIMER(1)|TRIG_TIMER | | |
+ * | | TRIG_EXT(2) | | | |
+ * | | TRIG_INT | | | |
+ * +---------+--------------+-----------+------------+----------+
+ *
+ * Note 1: If AI command and AO command are used simultaneously, only
+ * one may have scan_begin_src == TRIG_TIMER.
+ *
+ * Note 2: For PCI230 and PCI230+, scan_begin_src == TRIG_EXT uses
+ * DIO channel 16 (pin 49) which will need to be configured as
+ * a digital input. For PCI260+, the EXTTRIG/EXTCONVCLK input
+ * (pin 17) is used instead. For PCI230, scan_begin_src ==
+ * TRIG_EXT is not supported. The trigger is a rising edge
+ * on the input.
+ *
+ * Note 3: For convert_src == TRIG_EXT, the EXTTRIG/EXTCONVCLK input
+ * (pin 25 on PCI230(+), pin 17 on PCI260(+)) is used. The
+ * convert_arg value is interpreted as follows:
+ *
+ * convert_arg == (CR_EDGE | 0) => rising edge
+ * convert_arg == (CR_EDGE | CR_INVERT | 0) => falling edge
+ * convert_arg == 0 => falling edge (backwards compatibility)
+ * convert_arg == 1 => rising edge (backwards compatibility)
+ *
+ * All entries in the channel list must use the same analogue reference.
+ * If the analogue reference is not AREF_DIFF (not differential) each
+ * pair of channel numbers (0 and 1, 2 and 3, etc.) must use the same
+ * input range. The input ranges used in the sequence must be all
+ * bipolar (ranges 0 to 3) or all unipolar (ranges 4 to 6). The channel
+ * sequence must consist of 1 or more identical subsequences. Within the
+ * subsequence, channels must be in ascending order with no repeated
+ * channels. For example, the following sequences are valid: 0 1 2 3
+ * (single valid subsequence), 0 2 3 5 0 2 3 5 (repeated valid
+ * subsequence), 1 1 1 1 (repeated valid subsequence). The following
+ * sequences are invalid: 0 3 2 1 (invalid subsequence), 0 2 3 5 0 2 3
+ * (incompletely repeated subsequence). Some versions of the PCI230+ and
+ * PCI260+ have a bug that requires a subsequence longer than one entry
+ * long to include channel 0.
+ *
+ * AO Subdevice:
+ *
+ * The AO subdevice has 2 channels with 12-bit resolution.
+ * The following output ranges are supported:
+ * 0 => [0, 10] V
+ * 1 => [-10, +10] V
+ *
+ * AO Commands:
+ *
+ * +=========+==============+===========+============+==========+
+ * |start_src|scan_begin_src|convert_src|scan_end_src| stop_src |
+ * +=========+==============+===========+============+==========+
+ * |TRIG_INT | TRIG_TIMER(1)| TRIG_NOW | TRIG_COUNT |TRIG_NONE |
+ * | | TRIG_EXT(2) | | |TRIG_COUNT|
+ * | | TRIG_INT | | | |
+ * +---------+--------------+-----------+------------+----------+
+ *
+ * Note 1: If AI command and AO command are used simultaneously, only
+ * one may have scan_begin_src == TRIG_TIMER.
+ *
+ * Note 2: scan_begin_src == TRIG_EXT is only supported if the card is
+ * configured as a PCI230+ and is only supported on later
+ * versions of the card. As a card configured as a PCI230+ is
+ * not guaranteed to support external triggering, please consider
+ * this support to be a bonus. It uses the EXTTRIG/ EXTCONVCLK
+ * input (PCI230+ pin 25). Triggering will be on the rising edge
+ * unless the CR_INVERT flag is set in scan_begin_arg.
+ *
+ * The channels in the channel sequence must be in ascending order with
+ * no repeats. All entries in the channel sequence must use the same
+ * output range.
+ *
+ * DIO Subdevice:
+ *
+ * The DIO subdevice is a 8255 chip providing 24 DIO channels. The DIO
+ * channels are configurable as inputs or outputs in four groups:
+ *
+ * Port A - channels 0 to 7
+ * Port B - channels 8 to 15
+ * Port CL - channels 16 to 19
+ * Port CH - channels 20 to 23
+ *
+ * Only mode 0 of the 8255 chip is supported.
+ *
+ * Bit 0 of port C (DIO channel 16) is also used as an external scan
+ * trigger input for AI commands on PCI230 and PCI230+, so would need to
+ * be configured as an input to use it for that purpose.
+ */
+
+/*
+ * Extra triggered scan functionality, interrupt bug-fix added by Steve
+ * Sharples. Support for PCI230+/260+, more triggered scan functionality,
+ * and workarounds for (or detection of) various hardware problems added
+ * by Ian Abbott.
+ */
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+
+#include "../comedi_pci.h"
+
+#include "comedi_8254.h"
+#include "8255.h"
+
+/*
+ * PCI230 PCI configuration register information
+ */
+#define PCI_DEVICE_ID_PCI230 0x0000
+#define PCI_DEVICE_ID_PCI260 0x0006
+
+/*
+ * PCI230 i/o space 1 registers.
+ */
+#define PCI230_PPI_X_BASE 0x00 /* User PPI (82C55) base */
+#define PCI230_PPI_X_A 0x00 /* User PPI (82C55) port A */
+#define PCI230_PPI_X_B 0x01 /* User PPI (82C55) port B */
+#define PCI230_PPI_X_C 0x02 /* User PPI (82C55) port C */
+#define PCI230_PPI_X_CMD 0x03 /* User PPI (82C55) control word */
+#define PCI230_Z2_CT_BASE 0x14 /* 82C54 counter/timer base */
+#define PCI230_ZCLK_SCE 0x1A /* Group Z Clock Configuration */
+#define PCI230_ZGAT_SCE 0x1D /* Group Z Gate Configuration */
+#define PCI230_INT_SCE 0x1E /* Interrupt source mask (w) */
+#define PCI230_INT_STAT 0x1E /* Interrupt status (r) */
+
+/*
+ * PCI230 i/o space 2 registers.
+ */
+#define PCI230_DACCON 0x00 /* DAC control */
+#define PCI230_DACOUT1 0x02 /* DAC channel 0 (w) */
+#define PCI230_DACOUT2 0x04 /* DAC channel 1 (w) (not FIFO mode) */
+#define PCI230_ADCDATA 0x08 /* ADC data (r) */
+#define PCI230_ADCSWTRIG 0x08 /* ADC software trigger (w) */
+#define PCI230_ADCCON 0x0A /* ADC control */
+#define PCI230_ADCEN 0x0C /* ADC channel enable bits */
+#define PCI230_ADCG 0x0E /* ADC gain control bits */
+/* PCI230+ i/o space 2 additional registers. */
+#define PCI230P_ADCTRIG 0x10 /* ADC start acquisition trigger */
+#define PCI230P_ADCTH 0x12 /* ADC analog trigger threshold */
+#define PCI230P_ADCFFTH 0x14 /* ADC FIFO interrupt threshold */
+#define PCI230P_ADCFFLEV 0x16 /* ADC FIFO level (r) */
+#define PCI230P_ADCPTSC 0x18 /* ADC pre-trigger sample count (r) */
+#define PCI230P_ADCHYST 0x1A /* ADC analog trigger hysteresys */
+#define PCI230P_EXTFUNC 0x1C /* Extended functions */
+#define PCI230P_HWVER 0x1E /* Hardware version (r) */
+/* PCI230+ hardware version 2 onwards. */
+#define PCI230P2_DACDATA 0x02 /* DAC data (FIFO mode) (w) */
+#define PCI230P2_DACSWTRIG 0x02 /* DAC soft trigger (FIFO mode) (r) */
+#define PCI230P2_DACEN 0x06 /* DAC channel enable (FIFO mode) */
+
+/*
+ * DACCON read-write values.
+ */
+#define PCI230_DAC_OR_UNI (0 << 0) /* Output range unipolar */
+#define PCI230_DAC_OR_BIP (1 << 0) /* Output range bipolar */
+#define PCI230_DAC_OR_MASK (1 << 0)
+/*
+ * The following applies only if DAC FIFO support is enabled in the EXTFUNC
+ * register (and only for PCI230+ hardware version 2 onwards).
+ */
+#define PCI230P2_DAC_FIFO_EN (1 << 8) /* FIFO enable */
+/*
+ * The following apply only if the DAC FIFO is enabled (and only for PCI230+
+ * hardware version 2 onwards).
+ */
+#define PCI230P2_DAC_TRIG_NONE (0 << 2) /* No trigger */
+#define PCI230P2_DAC_TRIG_SW (1 << 2) /* Software trigger trigger */
+#define PCI230P2_DAC_TRIG_EXTP (2 << 2) /* EXTTRIG +ve edge trigger */
+#define PCI230P2_DAC_TRIG_EXTN (3 << 2) /* EXTTRIG -ve edge trigger */
+#define PCI230P2_DAC_TRIG_Z2CT0 (4 << 2) /* CT0-OUT +ve edge trigger */
+#define PCI230P2_DAC_TRIG_Z2CT1 (5 << 2) /* CT1-OUT +ve edge trigger */
+#define PCI230P2_DAC_TRIG_Z2CT2 (6 << 2) /* CT2-OUT +ve edge trigger */
+#define PCI230P2_DAC_TRIG_MASK (7 << 2)
+#define PCI230P2_DAC_FIFO_WRAP (1 << 7) /* FIFO wraparound mode */
+#define PCI230P2_DAC_INT_FIFO_EMPTY (0 << 9) /* FIFO interrupt empty */
+#define PCI230P2_DAC_INT_FIFO_NEMPTY (1 << 9)
+#define PCI230P2_DAC_INT_FIFO_NHALF (2 << 9) /* FIFO intr not half full */
+#define PCI230P2_DAC_INT_FIFO_HALF (3 << 9)
+#define PCI230P2_DAC_INT_FIFO_NFULL (4 << 9) /* FIFO interrupt not full */
+#define PCI230P2_DAC_INT_FIFO_FULL (5 << 9)
+#define PCI230P2_DAC_INT_FIFO_MASK (7 << 9)
+
+/*
+ * DACCON read-only values.
+ */
+#define PCI230_DAC_BUSY (1 << 1) /* DAC busy. */
+/*
+ * The following apply only if the DAC FIFO is enabled (and only for PCI230+
+ * hardware version 2 onwards).
+ */
+#define PCI230P2_DAC_FIFO_UNDERRUN_LATCHED (1 << 5) /* Underrun error */
+#define PCI230P2_DAC_FIFO_EMPTY (1 << 13) /* FIFO empty */
+#define PCI230P2_DAC_FIFO_FULL (1 << 14) /* FIFO full */
+#define PCI230P2_DAC_FIFO_HALF (1 << 15) /* FIFO half full */
+
+/*
+ * DACCON write-only, transient values.
+ */
+/*
+ * The following apply only if the DAC FIFO is enabled (and only for PCI230+
+ * hardware version 2 onwards).
+ */
+#define PCI230P2_DAC_FIFO_UNDERRUN_CLEAR (1 << 5) /* Clear underrun */
+#define PCI230P2_DAC_FIFO_RESET (1 << 12) /* FIFO reset */
+
+/*
+ * PCI230+ hardware version 2 DAC FIFO levels.
+ */
+#define PCI230P2_DAC_FIFOLEVEL_HALF 512
+#define PCI230P2_DAC_FIFOLEVEL_FULL 1024
+/* Free space in DAC FIFO. */
+#define PCI230P2_DAC_FIFOROOM_EMPTY PCI230P2_DAC_FIFOLEVEL_FULL
+#define PCI230P2_DAC_FIFOROOM_ONETOHALF \
+ (PCI230P2_DAC_FIFOLEVEL_FULL - PCI230P2_DAC_FIFOLEVEL_HALF)
+#define PCI230P2_DAC_FIFOROOM_HALFTOFULL 1
+#define PCI230P2_DAC_FIFOROOM_FULL 0
+
+/*
+ * ADCCON read/write values.
+ */
+#define PCI230_ADC_TRIG_NONE (0 << 0) /* No trigger */
+#define PCI230_ADC_TRIG_SW (1 << 0) /* Software trigger trigger */
+#define PCI230_ADC_TRIG_EXTP (2 << 0) /* EXTTRIG +ve edge trigger */
+#define PCI230_ADC_TRIG_EXTN (3 << 0) /* EXTTRIG -ve edge trigger */
+#define PCI230_ADC_TRIG_Z2CT0 (4 << 0) /* CT0-OUT +ve edge trigger */
+#define PCI230_ADC_TRIG_Z2CT1 (5 << 0) /* CT1-OUT +ve edge trigger */
+#define PCI230_ADC_TRIG_Z2CT2 (6 << 0) /* CT2-OUT +ve edge trigger */
+#define PCI230_ADC_TRIG_MASK (7 << 0)
+#define PCI230_ADC_IR_UNI (0 << 3) /* Input range unipolar */
+#define PCI230_ADC_IR_BIP (1 << 3) /* Input range bipolar */
+#define PCI230_ADC_IR_MASK (1 << 3)
+#define PCI230_ADC_IM_SE (0 << 4) /* Input mode single ended */
+#define PCI230_ADC_IM_DIF (1 << 4) /* Input mode differential */
+#define PCI230_ADC_IM_MASK (1 << 4)
+#define PCI230_ADC_FIFO_EN (1 << 8) /* FIFO enable */
+#define PCI230_ADC_INT_FIFO_EMPTY (0 << 9)
+#define PCI230_ADC_INT_FIFO_NEMPTY (1 << 9) /* FIFO interrupt not empty */
+#define PCI230_ADC_INT_FIFO_NHALF (2 << 9)
+#define PCI230_ADC_INT_FIFO_HALF (3 << 9) /* FIFO interrupt half full */
+#define PCI230_ADC_INT_FIFO_NFULL (4 << 9)
+#define PCI230_ADC_INT_FIFO_FULL (5 << 9) /* FIFO interrupt full */
+#define PCI230P_ADC_INT_FIFO_THRESH (7 << 9) /* FIFO interrupt threshold */
+#define PCI230_ADC_INT_FIFO_MASK (7 << 9)
+
+/*
+ * ADCCON write-only, transient values.
+ */
+#define PCI230_ADC_FIFO_RESET (1 << 12) /* FIFO reset */
+#define PCI230_ADC_GLOB_RESET (1 << 13) /* Global reset */
+
+/*
+ * ADCCON read-only values.
+ */
+#define PCI230_ADC_BUSY (1 << 15) /* ADC busy */
+#define PCI230_ADC_FIFO_EMPTY (1 << 12) /* FIFO empty */
+#define PCI230_ADC_FIFO_FULL (1 << 13) /* FIFO full */
+#define PCI230_ADC_FIFO_HALF (1 << 14) /* FIFO half full */
+#define PCI230_ADC_FIFO_FULL_LATCHED (1 << 5) /* FIFO overrun occurred */
+
+/*
+ * PCI230 ADC FIFO levels.
+ */
+#define PCI230_ADC_FIFOLEVEL_HALFFULL 2049 /* Value for FIFO half full */
+#define PCI230_ADC_FIFOLEVEL_FULL 4096 /* FIFO size */
+
+/*
+ * PCI230+ EXTFUNC values.
+ */
+/* Route EXTTRIG pin to external gate inputs. */
+#define PCI230P_EXTFUNC_GAT_EXTTRIG (1 << 0)
+/* PCI230+ hardware version 2 values. */
+/* Allow DAC FIFO to be enabled. */
+#define PCI230P2_EXTFUNC_DACFIFO (1 << 1)
+
+/*
+ * Counter/timer clock input configuration sources.
+ */
+#define CLK_CLK 0 /* reserved (channel-specific clock) */
+#define CLK_10MHZ 1 /* internal 10 MHz clock */
+#define CLK_1MHZ 2 /* internal 1 MHz clock */
+#define CLK_100KHZ 3 /* internal 100 kHz clock */
+#define CLK_10KHZ 4 /* internal 10 kHz clock */
+#define CLK_1KHZ 5 /* internal 1 kHz clock */
+#define CLK_OUTNM1 6 /* output of channel-1 modulo total */
+#define CLK_EXT 7 /* external clock */
+/* Macro to construct clock input configuration register value. */
+#define CLK_CONFIG(chan, src) ((((chan) & 3) << 3) | ((src) & 7))
+
+/*
+ * Counter/timer gate input configuration sources.
+ */
+#define GAT_VCC 0 /* VCC (i.e. enabled) */
+#define GAT_GND 1 /* GND (i.e. disabled) */
+#define GAT_EXT 2 /* external gate input (PPCn on PCI230) */
+#define GAT_NOUTNM2 3 /* inverted output of channel-2 modulo total */
+/* Macro to construct gate input configuration register value. */
+#define GAT_CONFIG(chan, src) ((((chan) & 3) << 3) | ((src) & 7))
+
+/*
+ * Summary of CLK_OUTNM1 and GAT_NOUTNM2 connections for PCI230 and PCI260:
+ *
+ * Channel's Channel's
+ * clock input gate input
+ * Channel CLK_OUTNM1 GAT_NOUTNM2
+ * ------- ---------- -----------
+ * Z2-CT0 Z2-CT2-OUT /Z2-CT1-OUT
+ * Z2-CT1 Z2-CT0-OUT /Z2-CT2-OUT
+ * Z2-CT2 Z2-CT1-OUT /Z2-CT0-OUT
+ */
+
+/*
+ * Interrupt enables/status register values.
+ */
+#define PCI230_INT_DISABLE 0
+#define PCI230_INT_PPI_C0 (1 << 0)
+#define PCI230_INT_PPI_C3 (1 << 1)
+#define PCI230_INT_ADC (1 << 2)
+#define PCI230_INT_ZCLK_CT1 (1 << 5)
+/* For PCI230+ hardware version 2 when DAC FIFO enabled. */
+#define PCI230P2_INT_DAC (1 << 4)
+
+/*
+ * (Potentially) shared resources and their owners
+ */
+enum {
+ RES_Z2CT0 = (1U << 0), /* Z2-CT0 */
+ RES_Z2CT1 = (1U << 1), /* Z2-CT1 */
+ RES_Z2CT2 = (1U << 2) /* Z2-CT2 */
+};
+
+enum {
+ OWNER_AICMD, /* Owned by AI command */
+ OWNER_AOCMD, /* Owned by AO command */
+ NUM_OWNERS /* Number of owners */
+};
+
+/*
+ * Handy macros.
+ */
+
+/* Combine old and new bits. */
+#define COMBINE(old, new, mask) (((old) & ~(mask)) | ((new) & (mask)))
+
+/* Current CPU. XXX should this be hard_smp_processor_id()? */
+#define THISCPU smp_processor_id()
+
+/*
+ * Board descriptions for the two boards supported.
+ */
+
+struct pci230_board {
+ const char *name;
+ unsigned short id;
+ unsigned char ai_bits;
+ unsigned char ao_bits;
+ unsigned char min_hwver; /* Minimum hardware version supported. */
+ bool have_dio:1;
+};
+
+static const struct pci230_board pci230_boards[] = {
+ {
+ .name = "pci230+",
+ .id = PCI_DEVICE_ID_PCI230,
+ .ai_bits = 16,
+ .ao_bits = 12,
+ .have_dio = true,
+ .min_hwver = 1,
+ },
+ {
+ .name = "pci260+",
+ .id = PCI_DEVICE_ID_PCI260,
+ .ai_bits = 16,
+ .min_hwver = 1,
+ },
+ {
+ .name = "pci230",
+ .id = PCI_DEVICE_ID_PCI230,
+ .ai_bits = 12,
+ .ao_bits = 12,
+ .have_dio = true,
+ },
+ {
+ .name = "pci260",
+ .id = PCI_DEVICE_ID_PCI260,
+ .ai_bits = 12,
+ },
+};
+
+struct pci230_private {
+ spinlock_t isr_spinlock; /* Interrupt spin lock */
+ spinlock_t res_spinlock; /* Shared resources spin lock */
+ spinlock_t ai_stop_spinlock; /* Spin lock for stopping AI command */
+ spinlock_t ao_stop_spinlock; /* Spin lock for stopping AO command */
+ unsigned long daqio; /* PCI230's DAQ I/O space */
+ int intr_cpuid; /* ID of CPU running ISR */
+ unsigned short hwver; /* Hardware version (for '+' models) */
+ unsigned short adccon; /* ADCCON register value */
+ unsigned short daccon; /* DACCON register value */
+ unsigned short adcfifothresh; /* ADC FIFO threshold (PCI230+/260+) */
+ unsigned short adcg; /* ADCG register value */
+ unsigned char ier; /* Interrupt enable bits */
+ unsigned char res_owned[NUM_OWNERS]; /* Owned resources */
+ bool intr_running:1; /* Flag set in interrupt routine */
+ bool ai_bipolar:1; /* Flag AI range is bipolar */
+ bool ao_bipolar:1; /* Flag AO range is bipolar */
+ bool ai_cmd_started:1; /* Flag AI command started */
+ bool ao_cmd_started:1; /* Flag AO command started */
+};
+
+/* PCI230 clock source periods in ns */
+static const unsigned int pci230_timebase[8] = {
+ [CLK_10MHZ] = I8254_OSC_BASE_10MHZ,
+ [CLK_1MHZ] = I8254_OSC_BASE_1MHZ,
+ [CLK_100KHZ] = I8254_OSC_BASE_100KHZ,
+ [CLK_10KHZ] = I8254_OSC_BASE_10KHZ,
+ [CLK_1KHZ] = I8254_OSC_BASE_1KHZ,
+};
+
+/* PCI230 analogue input range table */
+static const struct comedi_lrange pci230_ai_range = {
+ 7, {
+ BIP_RANGE(10),
+ BIP_RANGE(5),
+ BIP_RANGE(2.5),
+ BIP_RANGE(1.25),
+ UNI_RANGE(10),
+ UNI_RANGE(5),
+ UNI_RANGE(2.5)
+ }
+};
+
+/* PCI230 analogue gain bits for each input range. */
+static const unsigned char pci230_ai_gain[7] = { 0, 1, 2, 3, 1, 2, 3 };
+
+/* PCI230 analogue output range table */
+static const struct comedi_lrange pci230_ao_range = {
+ 2, {
+ UNI_RANGE(10),
+ BIP_RANGE(10)
+ }
+};
+
+static unsigned short pci230_ai_read(struct comedi_device *dev)
+{
+ const struct pci230_board *thisboard = dev->board_ptr;
+ struct pci230_private *devpriv = dev->private;
+ unsigned short data;
+
+ /* Read sample. */
+ data = inw(devpriv->daqio + PCI230_ADCDATA);
+ /*
+ * PCI230 is 12 bit - stored in upper bits of 16 bit register
+ * (lower four bits reserved for expansion). PCI230+ is 16 bit AI.
+ *
+ * If a bipolar range was specified, mangle it
+ * (twos complement->straight binary).
+ */
+ if (devpriv->ai_bipolar)
+ data ^= 0x8000;
+ data >>= (16 - thisboard->ai_bits);
+ return data;
+}
+
+static unsigned short pci230_ao_mangle_datum(struct comedi_device *dev,
+ unsigned short datum)
+{
+ const struct pci230_board *thisboard = dev->board_ptr;
+ struct pci230_private *devpriv = dev->private;
+
+ /*
+ * PCI230 is 12 bit - stored in upper bits of 16 bit register (lower
+ * four bits reserved for expansion). PCI230+ is also 12 bit AO.
+ */
+ datum <<= (16 - thisboard->ao_bits);
+ /*
+ * If a bipolar range was specified, mangle it
+ * (straight binary->twos complement).
+ */
+ if (devpriv->ao_bipolar)
+ datum ^= 0x8000;
+ return datum;
+}
+
+static void pci230_ao_write_nofifo(struct comedi_device *dev,
+ unsigned short datum, unsigned int chan)
+{
+ struct pci230_private *devpriv = dev->private;
+
+ /* Write mangled datum to appropriate DACOUT register. */
+ outw(pci230_ao_mangle_datum(dev, datum),
+ devpriv->daqio + ((chan == 0) ? PCI230_DACOUT1 : PCI230_DACOUT2));
+}
+
+static void pci230_ao_write_fifo(struct comedi_device *dev,
+ unsigned short datum, unsigned int chan)
+{
+ struct pci230_private *devpriv = dev->private;
+
+ /* Write mangled datum to appropriate DACDATA register. */
+ outw(pci230_ao_mangle_datum(dev, datum),
+ devpriv->daqio + PCI230P2_DACDATA);
+}
+
+static bool pci230_claim_shared(struct comedi_device *dev,
+ unsigned char res_mask, unsigned int owner)
+{
+ struct pci230_private *devpriv = dev->private;
+ unsigned int o;
+ unsigned long irqflags;
+
+ spin_lock_irqsave(&devpriv->res_spinlock, irqflags);
+ for (o = 0; o < NUM_OWNERS; o++) {
+ if (o == owner)
+ continue;
+ if (devpriv->res_owned[o] & res_mask) {
+ spin_unlock_irqrestore(&devpriv->res_spinlock,
+ irqflags);
+ return false;
+ }
+ }
+ devpriv->res_owned[owner] |= res_mask;
+ spin_unlock_irqrestore(&devpriv->res_spinlock, irqflags);
+ return true;
+}
+
+static void pci230_release_shared(struct comedi_device *dev,
+ unsigned char res_mask, unsigned int owner)
+{
+ struct pci230_private *devpriv = dev->private;
+ unsigned long irqflags;
+
+ spin_lock_irqsave(&devpriv->res_spinlock, irqflags);
+ devpriv->res_owned[owner] &= ~res_mask;
+ spin_unlock_irqrestore(&devpriv->res_spinlock, irqflags);
+}
+
+static void pci230_release_all_resources(struct comedi_device *dev,
+ unsigned int owner)
+{
+ pci230_release_shared(dev, (unsigned char)~0, owner);
+}
+
+static unsigned int pci230_divide_ns(uint64_t ns, unsigned int timebase,
+ unsigned int flags)
+{
+ uint64_t div;
+ unsigned int rem;
+
+ div = ns;
+ rem = do_div(div, timebase);
+ switch (flags & CMDF_ROUND_MASK) {
+ default:
+ case CMDF_ROUND_NEAREST:
+ div += (rem + (timebase / 2)) / timebase;
+ break;
+ case CMDF_ROUND_DOWN:
+ break;
+ case CMDF_ROUND_UP:
+ div += (rem + timebase - 1) / timebase;
+ break;
+ }
+ return div > UINT_MAX ? UINT_MAX : (unsigned int)div;
+}
+
+/*
+ * Given desired period in ns, returns the required internal clock source
+ * and gets the initial count.
+ */
+static unsigned int pci230_choose_clk_count(uint64_t ns, unsigned int *count,
+ unsigned int flags)
+{
+ unsigned int clk_src, cnt;
+
+ for (clk_src = CLK_10MHZ;; clk_src++) {
+ cnt = pci230_divide_ns(ns, pci230_timebase[clk_src], flags);
+ if (cnt <= 65536 || clk_src == CLK_1KHZ)
+ break;
+ }
+ *count = cnt;
+ return clk_src;
+}
+
+static void pci230_ns_to_single_timer(unsigned int *ns, unsigned int flags)
+{
+ unsigned int count;
+ unsigned int clk_src;
+
+ clk_src = pci230_choose_clk_count(*ns, &count, flags);
+ *ns = count * pci230_timebase[clk_src];
+}
+
+static void pci230_ct_setup_ns_mode(struct comedi_device *dev, unsigned int ct,
+ unsigned int mode, uint64_t ns,
+ unsigned int flags)
+{
+ unsigned int clk_src;
+ unsigned int count;
+
+ /* Set mode. */
+ comedi_8254_set_mode(dev->pacer, ct, mode);
+ /* Determine clock source and count. */
+ clk_src = pci230_choose_clk_count(ns, &count, flags);
+ /* Program clock source. */
+ outb(CLK_CONFIG(ct, clk_src), dev->iobase + PCI230_ZCLK_SCE);
+ /* Set initial count. */
+ if (count >= 65536)
+ count = 0;
+
+ comedi_8254_write(dev->pacer, ct, count);
+}
+
+static void pci230_cancel_ct(struct comedi_device *dev, unsigned int ct)
+{
+ /* Counter ct, 8254 mode 1, initial count not written. */
+ comedi_8254_set_mode(dev->pacer, ct, I8254_MODE1);
+}
+
+static int pci230_ai_eoc(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned long context)
+{
+ struct pci230_private *devpriv = dev->private;
+ unsigned int status;
+
+ status = inw(devpriv->daqio + PCI230_ADCCON);
+ if ((status & PCI230_ADC_FIFO_EMPTY) == 0)
+ return 0;
+ return -EBUSY;
+}
+
+static int pci230_ai_insn_read(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn, unsigned int *data)
+{
+ struct pci230_private *devpriv = dev->private;
+ unsigned int n;
+ unsigned int chan, range, aref;
+ unsigned int gainshift;
+ unsigned short adccon, adcen;
+ int ret;
+
+ /* Unpack channel and range. */
+ chan = CR_CHAN(insn->chanspec);
+ range = CR_RANGE(insn->chanspec);
+ aref = CR_AREF(insn->chanspec);
+ if (aref == AREF_DIFF) {
+ /* Differential. */
+ if (chan >= s->n_chan / 2) {
+ dev_dbg(dev->class_dev,
+ "%s: differential channel number out of range 0 to %u\n",
+ __func__, (s->n_chan / 2) - 1);
+ return -EINVAL;
+ }
+ }
+
+ /*
+ * Use Z2-CT2 as a conversion trigger instead of the built-in
+ * software trigger, as otherwise triggering of differential channels
+ * doesn't work properly for some versions of PCI230/260. Also set
+ * FIFO mode because the ADC busy bit only works for software triggers.
+ */
+ adccon = PCI230_ADC_TRIG_Z2CT2 | PCI230_ADC_FIFO_EN;
+ /* Set Z2-CT2 output low to avoid any false triggers. */
+ comedi_8254_set_mode(dev->pacer, 2, I8254_MODE0);
+ devpriv->ai_bipolar = comedi_range_is_bipolar(s, range);
+ if (aref == AREF_DIFF) {
+ /* Differential. */
+ gainshift = chan * 2;
+ if (devpriv->hwver == 0) {
+ /*
+ * Original PCI230/260 expects both inputs of the
+ * differential channel to be enabled.
+ */
+ adcen = 3 << gainshift;
+ } else {
+ /*
+ * PCI230+/260+ expects only one input of the
+ * differential channel to be enabled.
+ */
+ adcen = 1 << gainshift;
+ }
+ adccon |= PCI230_ADC_IM_DIF;
+ } else {
+ /* Single ended. */
+ adcen = 1 << chan;
+ gainshift = chan & ~1;
+ adccon |= PCI230_ADC_IM_SE;
+ }
+ devpriv->adcg = (devpriv->adcg & ~(3 << gainshift)) |
+ (pci230_ai_gain[range] << gainshift);
+ if (devpriv->ai_bipolar)
+ adccon |= PCI230_ADC_IR_BIP;
+ else
+ adccon |= PCI230_ADC_IR_UNI;
+
+ /*
+ * Enable only this channel in the scan list - otherwise by default
+ * we'll get one sample from each channel.
+ */
+ outw(adcen, devpriv->daqio + PCI230_ADCEN);
+
+ /* Set gain for channel. */
+ outw(devpriv->adcg, devpriv->daqio + PCI230_ADCG);
+
+ /* Specify uni/bip, se/diff, conversion source, and reset FIFO. */
+ devpriv->adccon = adccon;
+ outw(adccon | PCI230_ADC_FIFO_RESET, devpriv->daqio + PCI230_ADCCON);
+
+ /* Convert n samples */
+ for (n = 0; n < insn->n; n++) {
+ /*
+ * Trigger conversion by toggling Z2-CT2 output
+ * (finish with output high).
+ */
+ comedi_8254_set_mode(dev->pacer, 2, I8254_MODE0);
+ comedi_8254_set_mode(dev->pacer, 2, I8254_MODE1);
+
+ /* wait for conversion to end */
+ ret = comedi_timeout(dev, s, insn, pci230_ai_eoc, 0);
+ if (ret)
+ return ret;
+
+ /* read data */
+ data[n] = pci230_ai_read(dev);
+ }
+
+ /* return the number of samples read/written */
+ return n;
+}
+
+static int pci230_ao_insn_write(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ struct pci230_private *devpriv = dev->private;
+ unsigned int chan = CR_CHAN(insn->chanspec);
+ unsigned int range = CR_RANGE(insn->chanspec);
+ unsigned int val = s->readback[chan];
+ int i;
+
+ /*
+ * Set range - see analogue output range table; 0 => unipolar 10V,
+ * 1 => bipolar +/-10V range scale
+ */
+ devpriv->ao_bipolar = comedi_range_is_bipolar(s, range);
+ outw(range, devpriv->daqio + PCI230_DACCON);
+
+ for (i = 0; i < insn->n; i++) {
+ val = data[i];
+ pci230_ao_write_nofifo(dev, val, chan);
+ }
+ s->readback[chan] = val;
+
+ return insn->n;
+}
+
+static int pci230_ao_check_chanlist(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_cmd *cmd)
+{
+ unsigned int prev_chan = CR_CHAN(cmd->chanlist[0]);
+ unsigned int range0 = CR_RANGE(cmd->chanlist[0]);
+ int i;
+
+ for (i = 1; i < cmd->chanlist_len; i++) {
+ unsigned int chan = CR_CHAN(cmd->chanlist[i]);
+ unsigned int range = CR_RANGE(cmd->chanlist[i]);
+
+ if (chan < prev_chan) {
+ dev_dbg(dev->class_dev,
+ "%s: channel numbers must increase\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ if (range != range0) {
+ dev_dbg(dev->class_dev,
+ "%s: channels must have the same range\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ prev_chan = chan;
+ }
+
+ return 0;
+}
+
+static int pci230_ao_cmdtest(struct comedi_device *dev,
+ struct comedi_subdevice *s, struct comedi_cmd *cmd)
+{
+ const struct pci230_board *thisboard = dev->board_ptr;
+ struct pci230_private *devpriv = dev->private;
+ int err = 0;
+ unsigned int tmp;
+
+ /* Step 1 : check if triggers are trivially valid */
+
+ err |= comedi_check_trigger_src(&cmd->start_src, TRIG_INT);
+
+ tmp = TRIG_TIMER | TRIG_INT;
+ if (thisboard->min_hwver > 0 && devpriv->hwver >= 2) {
+ /*
+ * For PCI230+ hardware version 2 onwards, allow external
+ * trigger from EXTTRIG/EXTCONVCLK input (PCI230+ pin 25).
+ *
+ * FIXME: The permitted scan_begin_src values shouldn't depend
+ * on devpriv->hwver (the detected card's actual hardware
+ * version). They should only depend on thisboard->min_hwver
+ * (the static capabilities of the configured card). To fix
+ * it, a new card model, e.g. "pci230+2" would have to be
+ * defined with min_hwver set to 2. It doesn't seem worth it
+ * for this alone. At the moment, please consider
+ * scan_begin_src==TRIG_EXT support to be a bonus rather than a
+ * guarantee!
+ */
+ tmp |= TRIG_EXT;
+ }
+ err |= comedi_check_trigger_src(&cmd->scan_begin_src, tmp);
+
+ err |= comedi_check_trigger_src(&cmd->convert_src, TRIG_NOW);
+ err |= comedi_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
+ err |= comedi_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE);
+
+ if (err)
+ return 1;
+
+ /* Step 2a : make sure trigger sources are unique */
+
+ err |= comedi_check_trigger_is_unique(cmd->scan_begin_src);
+ err |= comedi_check_trigger_is_unique(cmd->stop_src);
+
+ /* Step 2b : and mutually compatible */
+
+ if (err)
+ return 2;
+
+ /* Step 3: check if arguments are trivially valid */
+
+ err |= comedi_check_trigger_arg_is(&cmd->start_arg, 0);
+
+#define MAX_SPEED_AO 8000 /* 8000 ns => 125 kHz */
+/*
+ * Comedi limit due to unsigned int cmd. Driver limit =
+ * 2^16 (16bit * counter) * 1000000ns (1kHz onboard clock) = 65.536s
+ */
+#define MIN_SPEED_AO 4294967295u /* 4294967295ns = 4.29s */
+
+ switch (cmd->scan_begin_src) {
+ case TRIG_TIMER:
+ err |= comedi_check_trigger_arg_min(&cmd->scan_begin_arg,
+ MAX_SPEED_AO);
+ err |= comedi_check_trigger_arg_max(&cmd->scan_begin_arg,
+ MIN_SPEED_AO);
+ break;
+ case TRIG_EXT:
+ /*
+ * External trigger - for PCI230+ hardware version 2 onwards.
+ */
+ /* Trigger number must be 0. */
+ if (cmd->scan_begin_arg & ~CR_FLAGS_MASK) {
+ cmd->scan_begin_arg = COMBINE(cmd->scan_begin_arg, 0,
+ ~CR_FLAGS_MASK);
+ err |= -EINVAL;
+ }
+ /*
+ * The only flags allowed are CR_EDGE and CR_INVERT.
+ * The CR_EDGE flag is ignored.
+ */
+ if (cmd->scan_begin_arg & CR_FLAGS_MASK &
+ ~(CR_EDGE | CR_INVERT)) {
+ cmd->scan_begin_arg =
+ COMBINE(cmd->scan_begin_arg, 0,
+ CR_FLAGS_MASK & ~(CR_EDGE | CR_INVERT));
+ err |= -EINVAL;
+ }
+ break;
+ default:
+ err |= comedi_check_trigger_arg_is(&cmd->scan_begin_arg, 0);
+ break;
+ }
+
+ err |= comedi_check_trigger_arg_is(&cmd->scan_end_arg,
+ cmd->chanlist_len);
+
+ if (cmd->stop_src == TRIG_COUNT)
+ err |= comedi_check_trigger_arg_min(&cmd->stop_arg, 1);
+ else /* TRIG_NONE */
+ err |= comedi_check_trigger_arg_is(&cmd->stop_arg, 0);
+
+ if (err)
+ return 3;
+
+ /* Step 4: fix up any arguments */
+
+ if (cmd->scan_begin_src == TRIG_TIMER) {
+ tmp = cmd->scan_begin_arg;
+ pci230_ns_to_single_timer(&cmd->scan_begin_arg, cmd->flags);
+ if (tmp != cmd->scan_begin_arg)
+ err++;
+ }
+
+ if (err)
+ return 4;
+
+ /* Step 5: check channel list if it exists */
+ if (cmd->chanlist && cmd->chanlist_len > 0)
+ err |= pci230_ao_check_chanlist(dev, s, cmd);
+
+ if (err)
+ return 5;
+
+ return 0;
+}
+
+static void pci230_ao_stop(struct comedi_device *dev,
+ struct comedi_subdevice *s)
+{
+ struct pci230_private *devpriv = dev->private;
+ unsigned long irqflags;
+ unsigned char intsrc;
+ bool started;
+ struct comedi_cmd *cmd;
+
+ spin_lock_irqsave(&devpriv->ao_stop_spinlock, irqflags);
+ started = devpriv->ao_cmd_started;
+ devpriv->ao_cmd_started = false;
+ spin_unlock_irqrestore(&devpriv->ao_stop_spinlock, irqflags);
+ if (!started)
+ return;
+ cmd = &s->async->cmd;
+ if (cmd->scan_begin_src == TRIG_TIMER) {
+ /* Stop scan rate generator. */
+ pci230_cancel_ct(dev, 1);
+ }
+ /* Determine interrupt source. */
+ if (devpriv->hwver < 2) {
+ /* Not using DAC FIFO. Using CT1 interrupt. */
+ intsrc = PCI230_INT_ZCLK_CT1;
+ } else {
+ /* Using DAC FIFO interrupt. */
+ intsrc = PCI230P2_INT_DAC;
+ }
+ /*
+ * Disable interrupt and wait for interrupt routine to finish running
+ * unless we are called from the interrupt routine.
+ */
+ spin_lock_irqsave(&devpriv->isr_spinlock, irqflags);
+ devpriv->ier &= ~intsrc;
+ while (devpriv->intr_running && devpriv->intr_cpuid != THISCPU) {
+ spin_unlock_irqrestore(&devpriv->isr_spinlock, irqflags);
+ spin_lock_irqsave(&devpriv->isr_spinlock, irqflags);
+ }
+ outb(devpriv->ier, dev->iobase + PCI230_INT_SCE);
+ spin_unlock_irqrestore(&devpriv->isr_spinlock, irqflags);
+ if (devpriv->hwver >= 2) {
+ /*
+ * Using DAC FIFO. Reset FIFO, clear underrun error,
+ * disable FIFO.
+ */
+ devpriv->daccon &= PCI230_DAC_OR_MASK;
+ outw(devpriv->daccon | PCI230P2_DAC_FIFO_RESET |
+ PCI230P2_DAC_FIFO_UNDERRUN_CLEAR,
+ devpriv->daqio + PCI230_DACCON);
+ }
+ /* Release resources. */
+ pci230_release_all_resources(dev, OWNER_AOCMD);
+}
+
+static void pci230_handle_ao_nofifo(struct comedi_device *dev,
+ struct comedi_subdevice *s)
+{
+ struct comedi_async *async = s->async;
+ struct comedi_cmd *cmd = &async->cmd;
+ unsigned short data;
+ int i;
+
+ if (cmd->stop_src == TRIG_COUNT && async->scans_done >= cmd->stop_arg)
+ return;
+
+ for (i = 0; i < cmd->chanlist_len; i++) {
+ unsigned int chan = CR_CHAN(cmd->chanlist[i]);
+
+ if (!comedi_buf_read_samples(s, &data, 1)) {
+ async->events |= COMEDI_CB_OVERFLOW;
+ return;
+ }
+ pci230_ao_write_nofifo(dev, data, chan);
+ s->readback[chan] = data;
+ }
+
+ if (cmd->stop_src == TRIG_COUNT && async->scans_done >= cmd->stop_arg)
+ async->events |= COMEDI_CB_EOA;
+}
+
+/*
+ * Loads DAC FIFO (if using it) from buffer.
+ * Returns false if AO finished due to completion or error, true if still going.
+ */
+static bool pci230_handle_ao_fifo(struct comedi_device *dev,
+ struct comedi_subdevice *s)
+{
+ struct pci230_private *devpriv = dev->private;
+ struct comedi_async *async = s->async;
+ struct comedi_cmd *cmd = &async->cmd;
+ unsigned int num_scans = comedi_nscans_left(s, 0);
+ unsigned int room;
+ unsigned short dacstat;
+ unsigned int i, n;
+ unsigned int events = 0;
+
+ /* Get DAC FIFO status. */
+ dacstat = inw(devpriv->daqio + PCI230_DACCON);
+
+ if (cmd->stop_src == TRIG_COUNT && num_scans == 0)
+ events |= COMEDI_CB_EOA;
+
+ if (events == 0) {
+ /* Check for FIFO underrun. */
+ if (dacstat & PCI230P2_DAC_FIFO_UNDERRUN_LATCHED) {
+ dev_err(dev->class_dev, "AO FIFO underrun\n");
+ events |= COMEDI_CB_OVERFLOW | COMEDI_CB_ERROR;
+ }
+ /*
+ * Check for buffer underrun if FIFO less than half full
+ * (otherwise there will be loads of "DAC FIFO not half full"
+ * interrupts).
+ */
+ if (num_scans == 0 &&
+ (dacstat & PCI230P2_DAC_FIFO_HALF) == 0) {
+ dev_err(dev->class_dev, "AO buffer underrun\n");
+ events |= COMEDI_CB_OVERFLOW | COMEDI_CB_ERROR;
+ }
+ }
+ if (events == 0) {
+ /* Determine how much room is in the FIFO (in samples). */
+ if (dacstat & PCI230P2_DAC_FIFO_FULL)
+ room = PCI230P2_DAC_FIFOROOM_FULL;
+ else if (dacstat & PCI230P2_DAC_FIFO_HALF)
+ room = PCI230P2_DAC_FIFOROOM_HALFTOFULL;
+ else if (dacstat & PCI230P2_DAC_FIFO_EMPTY)
+ room = PCI230P2_DAC_FIFOROOM_EMPTY;
+ else
+ room = PCI230P2_DAC_FIFOROOM_ONETOHALF;
+ /* Convert room to number of scans that can be added. */
+ room /= cmd->chanlist_len;
+ /* Determine number of scans to process. */
+ if (num_scans > room)
+ num_scans = room;
+ /* Process scans. */
+ for (n = 0; n < num_scans; n++) {
+ for (i = 0; i < cmd->chanlist_len; i++) {
+ unsigned int chan = CR_CHAN(cmd->chanlist[i]);
+ unsigned short datum;
+
+ comedi_buf_read_samples(s, &datum, 1);
+ pci230_ao_write_fifo(dev, datum, chan);
+ s->readback[chan] = datum;
+ }
+ }
+
+ if (cmd->stop_src == TRIG_COUNT &&
+ async->scans_done >= cmd->stop_arg) {
+ /*
+ * All data for the command has been written
+ * to FIFO. Set FIFO interrupt trigger level
+ * to 'empty'.
+ */
+ devpriv->daccon &= ~PCI230P2_DAC_INT_FIFO_MASK;
+ devpriv->daccon |= PCI230P2_DAC_INT_FIFO_EMPTY;
+ outw(devpriv->daccon, devpriv->daqio + PCI230_DACCON);
+ }
+ /* Check if FIFO underrun occurred while writing to FIFO. */
+ dacstat = inw(devpriv->daqio + PCI230_DACCON);
+ if (dacstat & PCI230P2_DAC_FIFO_UNDERRUN_LATCHED) {
+ dev_err(dev->class_dev, "AO FIFO underrun\n");
+ events |= COMEDI_CB_OVERFLOW | COMEDI_CB_ERROR;
+ }
+ }
+ async->events |= events;
+ return !(async->events & COMEDI_CB_CANCEL_MASK);
+}
+
+static int pci230_ao_inttrig_scan_begin(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ unsigned int trig_num)
+{
+ struct pci230_private *devpriv = dev->private;
+ unsigned long irqflags;
+
+ if (trig_num)
+ return -EINVAL;
+
+ spin_lock_irqsave(&devpriv->ao_stop_spinlock, irqflags);
+ if (!devpriv->ao_cmd_started) {
+ spin_unlock_irqrestore(&devpriv->ao_stop_spinlock, irqflags);
+ return 1;
+ }
+ /* Perform scan. */
+ if (devpriv->hwver < 2) {
+ /* Not using DAC FIFO. */
+ spin_unlock_irqrestore(&devpriv->ao_stop_spinlock, irqflags);
+ pci230_handle_ao_nofifo(dev, s);
+ comedi_handle_events(dev, s);
+ } else {
+ /* Using DAC FIFO. */
+ /* Read DACSWTRIG register to trigger conversion. */
+ inw(devpriv->daqio + PCI230P2_DACSWTRIG);
+ spin_unlock_irqrestore(&devpriv->ao_stop_spinlock, irqflags);
+ }
+ /* Delay. Should driver be responsible for this? */
+ /* XXX TODO: See if DAC busy bit can be used. */
+ udelay(8);
+ return 1;
+}
+
+static void pci230_ao_start(struct comedi_device *dev,
+ struct comedi_subdevice *s)
+{
+ struct pci230_private *devpriv = dev->private;
+ struct comedi_async *async = s->async;
+ struct comedi_cmd *cmd = &async->cmd;
+ unsigned long irqflags;
+
+ devpriv->ao_cmd_started = true;
+
+ if (devpriv->hwver >= 2) {
+ /* Using DAC FIFO. */
+ unsigned short scantrig;
+ bool run;
+
+ /* Preload FIFO data. */
+ run = pci230_handle_ao_fifo(dev, s);
+ comedi_handle_events(dev, s);
+ if (!run) {
+ /* Stopped. */
+ return;
+ }
+ /* Set scan trigger source. */
+ switch (cmd->scan_begin_src) {
+ case TRIG_TIMER:
+ scantrig = PCI230P2_DAC_TRIG_Z2CT1;
+ break;
+ case TRIG_EXT:
+ /* Trigger on EXTTRIG/EXTCONVCLK pin. */
+ if ((cmd->scan_begin_arg & CR_INVERT) == 0) {
+ /* +ve edge */
+ scantrig = PCI230P2_DAC_TRIG_EXTP;
+ } else {
+ /* -ve edge */
+ scantrig = PCI230P2_DAC_TRIG_EXTN;
+ }
+ break;
+ case TRIG_INT:
+ scantrig = PCI230P2_DAC_TRIG_SW;
+ break;
+ default:
+ /* Shouldn't get here. */
+ scantrig = PCI230P2_DAC_TRIG_NONE;
+ break;
+ }
+ devpriv->daccon =
+ (devpriv->daccon & ~PCI230P2_DAC_TRIG_MASK) | scantrig;
+ outw(devpriv->daccon, devpriv->daqio + PCI230_DACCON);
+ }
+ switch (cmd->scan_begin_src) {
+ case TRIG_TIMER:
+ if (devpriv->hwver < 2) {
+ /* Not using DAC FIFO. */
+ /* Enable CT1 timer interrupt. */
+ spin_lock_irqsave(&devpriv->isr_spinlock, irqflags);
+ devpriv->ier |= PCI230_INT_ZCLK_CT1;
+ outb(devpriv->ier, dev->iobase + PCI230_INT_SCE);
+ spin_unlock_irqrestore(&devpriv->isr_spinlock,
+ irqflags);
+ }
+ /* Set CT1 gate high to start counting. */
+ outb(GAT_CONFIG(1, GAT_VCC), dev->iobase + PCI230_ZGAT_SCE);
+ break;
+ case TRIG_INT:
+ async->inttrig = pci230_ao_inttrig_scan_begin;
+ break;
+ }
+ if (devpriv->hwver >= 2) {
+ /* Using DAC FIFO. Enable DAC FIFO interrupt. */
+ spin_lock_irqsave(&devpriv->isr_spinlock, irqflags);
+ devpriv->ier |= PCI230P2_INT_DAC;
+ outb(devpriv->ier, dev->iobase + PCI230_INT_SCE);
+ spin_unlock_irqrestore(&devpriv->isr_spinlock, irqflags);
+ }
+}
+
+static int pci230_ao_inttrig_start(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ unsigned int trig_num)
+{
+ struct comedi_cmd *cmd = &s->async->cmd;
+
+ if (trig_num != cmd->start_src)
+ return -EINVAL;
+
+ s->async->inttrig = NULL;
+ pci230_ao_start(dev, s);
+
+ return 1;
+}
+
+static int pci230_ao_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
+{
+ struct pci230_private *devpriv = dev->private;
+ unsigned short daccon;
+ unsigned int range;
+
+ /* Get the command. */
+ struct comedi_cmd *cmd = &s->async->cmd;
+
+ if (cmd->scan_begin_src == TRIG_TIMER) {
+ /* Claim Z2-CT1. */
+ if (!pci230_claim_shared(dev, RES_Z2CT1, OWNER_AOCMD))
+ return -EBUSY;
+ }
+
+ /*
+ * Set range - see analogue output range table; 0 => unipolar 10V,
+ * 1 => bipolar +/-10V range scale
+ */
+ range = CR_RANGE(cmd->chanlist[0]);
+ devpriv->ao_bipolar = comedi_range_is_bipolar(s, range);
+ daccon = devpriv->ao_bipolar ? PCI230_DAC_OR_BIP : PCI230_DAC_OR_UNI;
+ /* Use DAC FIFO for hardware version 2 onwards. */
+ if (devpriv->hwver >= 2) {
+ unsigned short dacen;
+ unsigned int i;
+
+ dacen = 0;
+ for (i = 0; i < cmd->chanlist_len; i++)
+ dacen |= 1 << CR_CHAN(cmd->chanlist[i]);
+
+ /* Set channel scan list. */
+ outw(dacen, devpriv->daqio + PCI230P2_DACEN);
+ /*
+ * Enable DAC FIFO.
+ * Set DAC scan source to 'none'.
+ * Set DAC FIFO interrupt trigger level to 'not half full'.
+ * Reset DAC FIFO and clear underrun.
+ *
+ * N.B. DAC FIFO interrupts are currently disabled.
+ */
+ daccon |= PCI230P2_DAC_FIFO_EN | PCI230P2_DAC_FIFO_RESET |
+ PCI230P2_DAC_FIFO_UNDERRUN_CLEAR |
+ PCI230P2_DAC_TRIG_NONE | PCI230P2_DAC_INT_FIFO_NHALF;
+ }
+
+ /* Set DACCON. */
+ outw(daccon, devpriv->daqio + PCI230_DACCON);
+ /* Preserve most of DACCON apart from write-only, transient bits. */
+ devpriv->daccon = daccon & ~(PCI230P2_DAC_FIFO_RESET |
+ PCI230P2_DAC_FIFO_UNDERRUN_CLEAR);
+
+ if (cmd->scan_begin_src == TRIG_TIMER) {
+ /*
+ * Set the counter timer 1 to the specified scan frequency.
+ * cmd->scan_begin_arg is sampling period in ns.
+ * Gate it off for now.
+ */
+ outb(GAT_CONFIG(1, GAT_GND), dev->iobase + PCI230_ZGAT_SCE);
+ pci230_ct_setup_ns_mode(dev, 1, I8254_MODE3,
+ cmd->scan_begin_arg,
+ cmd->flags);
+ }
+
+ /* N.B. cmd->start_src == TRIG_INT */
+ s->async->inttrig = pci230_ao_inttrig_start;
+
+ return 0;
+}
+
+static int pci230_ao_cancel(struct comedi_device *dev,
+ struct comedi_subdevice *s)
+{
+ pci230_ao_stop(dev, s);
+ return 0;
+}
+
+static int pci230_ai_check_scan_period(struct comedi_cmd *cmd)
+{
+ unsigned int min_scan_period, chanlist_len;
+ int err = 0;
+
+ chanlist_len = cmd->chanlist_len;
+ if (cmd->chanlist_len == 0)
+ chanlist_len = 1;
+
+ min_scan_period = chanlist_len * cmd->convert_arg;
+ if (min_scan_period < chanlist_len ||
+ min_scan_period < cmd->convert_arg) {
+ /* Arithmetic overflow. */
+ min_scan_period = UINT_MAX;
+ err++;
+ }
+ if (cmd->scan_begin_arg < min_scan_period) {
+ cmd->scan_begin_arg = min_scan_period;
+ err++;
+ }
+
+ return !err;
+}
+
+static int pci230_ai_check_chanlist(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_cmd *cmd)
+{
+ struct pci230_private *devpriv = dev->private;
+ unsigned int max_diff_chan = (s->n_chan / 2) - 1;
+ unsigned int prev_chan = 0;
+ unsigned int prev_range = 0;
+ unsigned int prev_aref = 0;
+ bool prev_bipolar = false;
+ unsigned int subseq_len = 0;
+ int i;
+
+ for (i = 0; i < cmd->chanlist_len; i++) {
+ unsigned int chanspec = cmd->chanlist[i];
+ unsigned int chan = CR_CHAN(chanspec);
+ unsigned int range = CR_RANGE(chanspec);
+ unsigned int aref = CR_AREF(chanspec);
+ bool bipolar = comedi_range_is_bipolar(s, range);
+
+ if (aref == AREF_DIFF && chan >= max_diff_chan) {
+ dev_dbg(dev->class_dev,
+ "%s: differential channel number out of range 0 to %u\n",
+ __func__, max_diff_chan);
+ return -EINVAL;
+ }
+
+ if (i > 0) {
+ /*
+ * Channel numbers must strictly increase or
+ * subsequence must repeat exactly.
+ */
+ if (chan <= prev_chan && subseq_len == 0)
+ subseq_len = i;
+
+ if (subseq_len > 0 &&
+ cmd->chanlist[i % subseq_len] != chanspec) {
+ dev_dbg(dev->class_dev,
+ "%s: channel numbers must increase or sequence must repeat exactly\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ if (aref != prev_aref) {
+ dev_dbg(dev->class_dev,
+ "%s: channel sequence analogue references must be all the same (single-ended or differential)\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ if (bipolar != prev_bipolar) {
+ dev_dbg(dev->class_dev,
+ "%s: channel sequence ranges must be all bipolar or all unipolar\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ if (aref != AREF_DIFF && range != prev_range &&
+ ((chan ^ prev_chan) & ~1) == 0) {
+ dev_dbg(dev->class_dev,
+ "%s: single-ended channel pairs must have the same range\n",
+ __func__);
+ return -EINVAL;
+ }
+ }
+ prev_chan = chan;
+ prev_range = range;
+ prev_aref = aref;
+ prev_bipolar = bipolar;
+ }
+
+ if (subseq_len == 0)
+ subseq_len = cmd->chanlist_len;
+
+ if (cmd->chanlist_len % subseq_len) {
+ dev_dbg(dev->class_dev,
+ "%s: sequence must repeat exactly\n", __func__);
+ return -EINVAL;
+ }
+
+ /*
+ * Buggy PCI230+ or PCI260+ requires channel 0 to be (first) in the
+ * sequence if the sequence contains more than one channel. Hardware
+ * versions 1 and 2 have the bug. There is no hardware version 3.
+ *
+ * Actually, there are two firmwares that report themselves as
+ * hardware version 1 (the boards have different ADC chips with
+ * slightly different timing requirements, which was supposed to
+ * be invisible to software). The first one doesn't seem to have
+ * the bug, but the second one does, and we can't tell them apart!
+ */
+ if (devpriv->hwver > 0 && devpriv->hwver < 4) {
+ if (subseq_len > 1 && CR_CHAN(cmd->chanlist[0])) {
+ dev_info(dev->class_dev,
+ "amplc_pci230: ai_cmdtest: Buggy PCI230+/260+ h/w version %u requires first channel of multi-channel sequence to be 0 (corrected in h/w version 4)\n",
+ devpriv->hwver);
+ return -EINVAL;
+ }
+ }
+
+ return 0;
+}
+
+static int pci230_ai_cmdtest(struct comedi_device *dev,
+ struct comedi_subdevice *s, struct comedi_cmd *cmd)
+{
+ const struct pci230_board *thisboard = dev->board_ptr;
+ struct pci230_private *devpriv = dev->private;
+ int err = 0;
+ unsigned int tmp;
+
+ /* Step 1 : check if triggers are trivially valid */
+
+ err |= comedi_check_trigger_src(&cmd->start_src, TRIG_NOW | TRIG_INT);
+
+ tmp = TRIG_FOLLOW | TRIG_TIMER | TRIG_INT;
+ if (thisboard->have_dio || thisboard->min_hwver > 0) {
+ /*
+ * Unfortunately, we cannot trigger a scan off an external
+ * source on the PCI260 board, since it uses the PPIC0 (DIO)
+ * input, which isn't present on the PCI260. For PCI260+
+ * we can use the EXTTRIG/EXTCONVCLK input on pin 17 instead.
+ */
+ tmp |= TRIG_EXT;
+ }
+ err |= comedi_check_trigger_src(&cmd->scan_begin_src, tmp);
+ err |= comedi_check_trigger_src(&cmd->convert_src,
+ TRIG_TIMER | TRIG_INT | TRIG_EXT);
+ err |= comedi_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
+ err |= comedi_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE);
+
+ if (err)
+ return 1;
+
+ /* Step 2a : make sure trigger sources are unique */
+
+ err |= comedi_check_trigger_is_unique(cmd->start_src);
+ err |= comedi_check_trigger_is_unique(cmd->scan_begin_src);
+ err |= comedi_check_trigger_is_unique(cmd->convert_src);
+ err |= comedi_check_trigger_is_unique(cmd->stop_src);
+
+ /* Step 2b : and mutually compatible */
+
+ /*
+ * If scan_begin_src is not TRIG_FOLLOW, then a monostable will be
+ * set up to generate a fixed number of timed conversion pulses.
+ */
+ if (cmd->scan_begin_src != TRIG_FOLLOW &&
+ cmd->convert_src != TRIG_TIMER)
+ err |= -EINVAL;
+
+ if (err)
+ return 2;
+
+ /* Step 3: check if arguments are trivially valid */
+
+ err |= comedi_check_trigger_arg_is(&cmd->start_arg, 0);
+
+#define MAX_SPEED_AI_SE 3200 /* PCI230 SE: 3200 ns => 312.5 kHz */
+#define MAX_SPEED_AI_DIFF 8000 /* PCI230 DIFF: 8000 ns => 125 kHz */
+#define MAX_SPEED_AI_PLUS 4000 /* PCI230+: 4000 ns => 250 kHz */
+/*
+ * Comedi limit due to unsigned int cmd. Driver limit =
+ * 2^16 (16bit * counter) * 1000000ns (1kHz onboard clock) = 65.536s
+ */
+#define MIN_SPEED_AI 4294967295u /* 4294967295ns = 4.29s */
+
+ if (cmd->convert_src == TRIG_TIMER) {
+ unsigned int max_speed_ai;
+
+ if (devpriv->hwver == 0) {
+ /*
+ * PCI230 or PCI260. Max speed depends whether
+ * single-ended or pseudo-differential.
+ */
+ if (cmd->chanlist && cmd->chanlist_len > 0) {
+ /* Peek analogue reference of first channel. */
+ if (CR_AREF(cmd->chanlist[0]) == AREF_DIFF)
+ max_speed_ai = MAX_SPEED_AI_DIFF;
+ else
+ max_speed_ai = MAX_SPEED_AI_SE;
+
+ } else {
+ /* No channel list. Assume single-ended. */
+ max_speed_ai = MAX_SPEED_AI_SE;
+ }
+ } else {
+ /* PCI230+ or PCI260+. */
+ max_speed_ai = MAX_SPEED_AI_PLUS;
+ }
+
+ err |= comedi_check_trigger_arg_min(&cmd->convert_arg,
+ max_speed_ai);
+ err |= comedi_check_trigger_arg_max(&cmd->convert_arg,
+ MIN_SPEED_AI);
+ } else if (cmd->convert_src == TRIG_EXT) {
+ /*
+ * external trigger
+ *
+ * convert_arg == (CR_EDGE | 0)
+ * => trigger on +ve edge.
+ * convert_arg == (CR_EDGE | CR_INVERT | 0)
+ * => trigger on -ve edge.
+ */
+ if (cmd->convert_arg & CR_FLAGS_MASK) {
+ /* Trigger number must be 0. */
+ if (cmd->convert_arg & ~CR_FLAGS_MASK) {
+ cmd->convert_arg = COMBINE(cmd->convert_arg, 0,
+ ~CR_FLAGS_MASK);
+ err |= -EINVAL;
+ }
+ /*
+ * The only flags allowed are CR_INVERT and CR_EDGE.
+ * CR_EDGE is required.
+ */
+ if ((cmd->convert_arg & CR_FLAGS_MASK & ~CR_INVERT) !=
+ CR_EDGE) {
+ /* Set CR_EDGE, preserve CR_INVERT. */
+ cmd->convert_arg =
+ COMBINE(cmd->start_arg, CR_EDGE | 0,
+ CR_FLAGS_MASK & ~CR_INVERT);
+ err |= -EINVAL;
+ }
+ } else {
+ /*
+ * Backwards compatibility with previous versions:
+ * convert_arg == 0 => trigger on -ve edge.
+ * convert_arg == 1 => trigger on +ve edge.
+ */
+ err |= comedi_check_trigger_arg_max(&cmd->convert_arg,
+ 1);
+ }
+ } else {
+ err |= comedi_check_trigger_arg_is(&cmd->convert_arg, 0);
+ }
+
+ err |= comedi_check_trigger_arg_is(&cmd->scan_end_arg,
+ cmd->chanlist_len);
+
+ if (cmd->stop_src == TRIG_COUNT)
+ err |= comedi_check_trigger_arg_min(&cmd->stop_arg, 1);
+ else /* TRIG_NONE */
+ err |= comedi_check_trigger_arg_is(&cmd->stop_arg, 0);
+
+ if (cmd->scan_begin_src == TRIG_EXT) {
+ /*
+ * external "trigger" to begin each scan:
+ * scan_begin_arg==0 => use PPC0 input -> gate of CT0 -> gate
+ * of CT2 (sample convert trigger is CT2)
+ */
+ if (cmd->scan_begin_arg & ~CR_FLAGS_MASK) {
+ cmd->scan_begin_arg = COMBINE(cmd->scan_begin_arg, 0,
+ ~CR_FLAGS_MASK);
+ err |= -EINVAL;
+ }
+ /* The only flag allowed is CR_EDGE, which is ignored. */
+ if (cmd->scan_begin_arg & CR_FLAGS_MASK & ~CR_EDGE) {
+ cmd->scan_begin_arg = COMBINE(cmd->scan_begin_arg, 0,
+ CR_FLAGS_MASK & ~CR_EDGE);
+ err |= -EINVAL;
+ }
+ } else if (cmd->scan_begin_src == TRIG_TIMER) {
+ /* N.B. cmd->convert_arg is also TRIG_TIMER */
+ if (!pci230_ai_check_scan_period(cmd))
+ err |= -EINVAL;
+
+ } else {
+ err |= comedi_check_trigger_arg_is(&cmd->scan_begin_arg, 0);
+ }
+
+ if (err)
+ return 3;
+
+ /* Step 4: fix up any arguments */
+
+ if (cmd->convert_src == TRIG_TIMER) {
+ tmp = cmd->convert_arg;
+ pci230_ns_to_single_timer(&cmd->convert_arg, cmd->flags);
+ if (tmp != cmd->convert_arg)
+ err++;
+ }
+
+ if (cmd->scan_begin_src == TRIG_TIMER) {
+ /* N.B. cmd->convert_arg is also TRIG_TIMER */
+ tmp = cmd->scan_begin_arg;
+ pci230_ns_to_single_timer(&cmd->scan_begin_arg, cmd->flags);
+ if (!pci230_ai_check_scan_period(cmd)) {
+ /* Was below minimum required. Round up. */
+ pci230_ns_to_single_timer(&cmd->scan_begin_arg,
+ CMDF_ROUND_UP);
+ pci230_ai_check_scan_period(cmd);
+ }
+ if (tmp != cmd->scan_begin_arg)
+ err++;
+ }
+
+ if (err)
+ return 4;
+
+ /* Step 5: check channel list if it exists */
+ if (cmd->chanlist && cmd->chanlist_len > 0)
+ err |= pci230_ai_check_chanlist(dev, s, cmd);
+
+ if (err)
+ return 5;
+
+ return 0;
+}
+
+static void pci230_ai_update_fifo_trigger_level(struct comedi_device *dev,
+ struct comedi_subdevice *s)
+{
+ struct pci230_private *devpriv = dev->private;
+ struct comedi_cmd *cmd = &s->async->cmd;
+ unsigned int wake;
+ unsigned short triglev;
+ unsigned short adccon;
+
+ if (cmd->flags & CMDF_WAKE_EOS)
+ wake = cmd->scan_end_arg - s->async->cur_chan;
+ else
+ wake = comedi_nsamples_left(s, PCI230_ADC_FIFOLEVEL_HALFFULL);
+
+ if (wake >= PCI230_ADC_FIFOLEVEL_HALFFULL) {
+ triglev = PCI230_ADC_INT_FIFO_HALF;
+ } else if (wake > 1 && devpriv->hwver > 0) {
+ /* PCI230+/260+ programmable FIFO interrupt level. */
+ if (devpriv->adcfifothresh != wake) {
+ devpriv->adcfifothresh = wake;
+ outw(wake, devpriv->daqio + PCI230P_ADCFFTH);
+ }
+ triglev = PCI230P_ADC_INT_FIFO_THRESH;
+ } else {
+ triglev = PCI230_ADC_INT_FIFO_NEMPTY;
+ }
+ adccon = (devpriv->adccon & ~PCI230_ADC_INT_FIFO_MASK) | triglev;
+ if (adccon != devpriv->adccon) {
+ devpriv->adccon = adccon;
+ outw(adccon, devpriv->daqio + PCI230_ADCCON);
+ }
+}
+
+static int pci230_ai_inttrig_convert(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ unsigned int trig_num)
+{
+ struct pci230_private *devpriv = dev->private;
+ unsigned long irqflags;
+ unsigned int delayus;
+
+ if (trig_num)
+ return -EINVAL;
+
+ spin_lock_irqsave(&devpriv->ai_stop_spinlock, irqflags);
+ if (!devpriv->ai_cmd_started) {
+ spin_unlock_irqrestore(&devpriv->ai_stop_spinlock, irqflags);
+ return 1;
+ }
+ /*
+ * Trigger conversion by toggling Z2-CT2 output.
+ * Finish with output high.
+ */
+ comedi_8254_set_mode(dev->pacer, 2, I8254_MODE0);
+ comedi_8254_set_mode(dev->pacer, 2, I8254_MODE1);
+ /*
+ * Delay. Should driver be responsible for this? An
+ * alternative would be to wait until conversion is complete,
+ * but we can't tell when it's complete because the ADC busy
+ * bit has a different meaning when FIFO enabled (and when
+ * FIFO not enabled, it only works for software triggers).
+ */
+ if ((devpriv->adccon & PCI230_ADC_IM_MASK) == PCI230_ADC_IM_DIF &&
+ devpriv->hwver == 0) {
+ /* PCI230/260 in differential mode */
+ delayus = 8;
+ } else {
+ /* single-ended or PCI230+/260+ */
+ delayus = 4;
+ }
+ spin_unlock_irqrestore(&devpriv->ai_stop_spinlock, irqflags);
+ udelay(delayus);
+ return 1;
+}
+
+static int pci230_ai_inttrig_scan_begin(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ unsigned int trig_num)
+{
+ struct pci230_private *devpriv = dev->private;
+ unsigned long irqflags;
+ unsigned char zgat;
+
+ if (trig_num)
+ return -EINVAL;
+
+ spin_lock_irqsave(&devpriv->ai_stop_spinlock, irqflags);
+ if (devpriv->ai_cmd_started) {
+ /* Trigger scan by waggling CT0 gate source. */
+ zgat = GAT_CONFIG(0, GAT_GND);
+ outb(zgat, dev->iobase + PCI230_ZGAT_SCE);
+ zgat = GAT_CONFIG(0, GAT_VCC);
+ outb(zgat, dev->iobase + PCI230_ZGAT_SCE);
+ }
+ spin_unlock_irqrestore(&devpriv->ai_stop_spinlock, irqflags);
+
+ return 1;
+}
+
+static void pci230_ai_stop(struct comedi_device *dev,
+ struct comedi_subdevice *s)
+{
+ struct pci230_private *devpriv = dev->private;
+ unsigned long irqflags;
+ struct comedi_cmd *cmd;
+ bool started;
+
+ spin_lock_irqsave(&devpriv->ai_stop_spinlock, irqflags);
+ started = devpriv->ai_cmd_started;
+ devpriv->ai_cmd_started = false;
+ spin_unlock_irqrestore(&devpriv->ai_stop_spinlock, irqflags);
+ if (!started)
+ return;
+ cmd = &s->async->cmd;
+ if (cmd->convert_src == TRIG_TIMER) {
+ /* Stop conversion rate generator. */
+ pci230_cancel_ct(dev, 2);
+ }
+ if (cmd->scan_begin_src != TRIG_FOLLOW) {
+ /* Stop scan period monostable. */
+ pci230_cancel_ct(dev, 0);
+ }
+ spin_lock_irqsave(&devpriv->isr_spinlock, irqflags);
+ /*
+ * Disable ADC interrupt and wait for interrupt routine to finish
+ * running unless we are called from the interrupt routine.
+ */
+ devpriv->ier &= ~PCI230_INT_ADC;
+ while (devpriv->intr_running && devpriv->intr_cpuid != THISCPU) {
+ spin_unlock_irqrestore(&devpriv->isr_spinlock, irqflags);
+ spin_lock_irqsave(&devpriv->isr_spinlock, irqflags);
+ }
+ outb(devpriv->ier, dev->iobase + PCI230_INT_SCE);
+ spin_unlock_irqrestore(&devpriv->isr_spinlock, irqflags);
+ /*
+ * Reset FIFO, disable FIFO and set start conversion source to none.
+ * Keep se/diff and bip/uni settings.
+ */
+ devpriv->adccon =
+ (devpriv->adccon & (PCI230_ADC_IR_MASK | PCI230_ADC_IM_MASK)) |
+ PCI230_ADC_TRIG_NONE;
+ outw(devpriv->adccon | PCI230_ADC_FIFO_RESET,
+ devpriv->daqio + PCI230_ADCCON);
+ /* Release resources. */
+ pci230_release_all_resources(dev, OWNER_AICMD);
+}
+
+static void pci230_ai_start(struct comedi_device *dev,
+ struct comedi_subdevice *s)
+{
+ struct pci230_private *devpriv = dev->private;
+ unsigned long irqflags;
+ unsigned short conv;
+ struct comedi_async *async = s->async;
+ struct comedi_cmd *cmd = &async->cmd;
+
+ devpriv->ai_cmd_started = true;
+
+ /* Enable ADC FIFO trigger level interrupt. */
+ spin_lock_irqsave(&devpriv->isr_spinlock, irqflags);
+ devpriv->ier |= PCI230_INT_ADC;
+ outb(devpriv->ier, dev->iobase + PCI230_INT_SCE);
+ spin_unlock_irqrestore(&devpriv->isr_spinlock, irqflags);
+
+ /*
+ * Update conversion trigger source which is currently set
+ * to CT2 output, which is currently stuck high.
+ */
+ switch (cmd->convert_src) {
+ default:
+ conv = PCI230_ADC_TRIG_NONE;
+ break;
+ case TRIG_TIMER:
+ /* Using CT2 output. */
+ conv = PCI230_ADC_TRIG_Z2CT2;
+ break;
+ case TRIG_EXT:
+ if (cmd->convert_arg & CR_EDGE) {
+ if ((cmd->convert_arg & CR_INVERT) == 0) {
+ /* Trigger on +ve edge. */
+ conv = PCI230_ADC_TRIG_EXTP;
+ } else {
+ /* Trigger on -ve edge. */
+ conv = PCI230_ADC_TRIG_EXTN;
+ }
+ } else {
+ /* Backwards compatibility. */
+ if (cmd->convert_arg) {
+ /* Trigger on +ve edge. */
+ conv = PCI230_ADC_TRIG_EXTP;
+ } else {
+ /* Trigger on -ve edge. */
+ conv = PCI230_ADC_TRIG_EXTN;
+ }
+ }
+ break;
+ case TRIG_INT:
+ /*
+ * Use CT2 output for software trigger due to problems
+ * in differential mode on PCI230/260.
+ */
+ conv = PCI230_ADC_TRIG_Z2CT2;
+ break;
+ }
+ devpriv->adccon = (devpriv->adccon & ~PCI230_ADC_TRIG_MASK) | conv;
+ outw(devpriv->adccon, devpriv->daqio + PCI230_ADCCON);
+ if (cmd->convert_src == TRIG_INT)
+ async->inttrig = pci230_ai_inttrig_convert;
+
+ /*
+ * Update FIFO interrupt trigger level, which is currently
+ * set to "full".
+ */
+ pci230_ai_update_fifo_trigger_level(dev, s);
+ if (cmd->convert_src == TRIG_TIMER) {
+ /* Update timer gates. */
+ unsigned char zgat;
+
+ if (cmd->scan_begin_src != TRIG_FOLLOW) {
+ /*
+ * Conversion timer CT2 needs to be gated by
+ * inverted output of monostable CT2.
+ */
+ zgat = GAT_CONFIG(2, GAT_NOUTNM2);
+ } else {
+ /*
+ * Conversion timer CT2 needs to be gated on
+ * continuously.
+ */
+ zgat = GAT_CONFIG(2, GAT_VCC);
+ }
+ outb(zgat, dev->iobase + PCI230_ZGAT_SCE);
+ if (cmd->scan_begin_src != TRIG_FOLLOW) {
+ /* Set monostable CT0 trigger source. */
+ switch (cmd->scan_begin_src) {
+ default:
+ zgat = GAT_CONFIG(0, GAT_VCC);
+ break;
+ case TRIG_EXT:
+ /*
+ * For CT0 on PCI230, the external trigger
+ * (gate) signal comes from PPC0, which is
+ * channel 16 of the DIO subdevice. The
+ * application needs to configure this as an
+ * input in order to use it as an external scan
+ * trigger.
+ */
+ zgat = GAT_CONFIG(0, GAT_EXT);
+ break;
+ case TRIG_TIMER:
+ /*
+ * Monostable CT0 triggered by rising edge on
+ * inverted output of CT1 (falling edge on CT1).
+ */
+ zgat = GAT_CONFIG(0, GAT_NOUTNM2);
+ break;
+ case TRIG_INT:
+ /*
+ * Monostable CT0 is triggered by inttrig
+ * function waggling the CT0 gate source.
+ */
+ zgat = GAT_CONFIG(0, GAT_VCC);
+ break;
+ }
+ outb(zgat, dev->iobase + PCI230_ZGAT_SCE);
+ switch (cmd->scan_begin_src) {
+ case TRIG_TIMER:
+ /*
+ * Scan period timer CT1 needs to be
+ * gated on to start counting.
+ */
+ zgat = GAT_CONFIG(1, GAT_VCC);
+ outb(zgat, dev->iobase + PCI230_ZGAT_SCE);
+ break;
+ case TRIG_INT:
+ async->inttrig = pci230_ai_inttrig_scan_begin;
+ break;
+ }
+ }
+ } else if (cmd->convert_src != TRIG_INT) {
+ /* No longer need Z2-CT2. */
+ pci230_release_shared(dev, RES_Z2CT2, OWNER_AICMD);
+ }
+}
+
+static int pci230_ai_inttrig_start(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ unsigned int trig_num)
+{
+ struct comedi_cmd *cmd = &s->async->cmd;
+
+ if (trig_num != cmd->start_arg)
+ return -EINVAL;
+
+ s->async->inttrig = NULL;
+ pci230_ai_start(dev, s);
+
+ return 1;
+}
+
+static void pci230_handle_ai(struct comedi_device *dev,
+ struct comedi_subdevice *s)
+{
+ struct pci230_private *devpriv = dev->private;
+ struct comedi_async *async = s->async;
+ struct comedi_cmd *cmd = &async->cmd;
+ unsigned int status_fifo;
+ unsigned int i;
+ unsigned int todo;
+ unsigned int fifoamount;
+ unsigned short val;
+
+ /* Determine number of samples to read. */
+ todo = comedi_nsamples_left(s, PCI230_ADC_FIFOLEVEL_HALFFULL);
+ if (todo == 0)
+ return;
+
+ fifoamount = 0;
+ for (i = 0; i < todo; i++) {
+ if (fifoamount == 0) {
+ /* Read FIFO state. */
+ status_fifo = inw(devpriv->daqio + PCI230_ADCCON);
+ if (status_fifo & PCI230_ADC_FIFO_FULL_LATCHED) {
+ /*
+ * Report error otherwise FIFO overruns will go
+ * unnoticed by the caller.
+ */
+ dev_err(dev->class_dev, "AI FIFO overrun\n");
+ async->events |= COMEDI_CB_ERROR;
+ break;
+ } else if (status_fifo & PCI230_ADC_FIFO_EMPTY) {
+ /* FIFO empty. */
+ break;
+ } else if (status_fifo & PCI230_ADC_FIFO_HALF) {
+ /* FIFO half full. */
+ fifoamount = PCI230_ADC_FIFOLEVEL_HALFFULL;
+ } else if (devpriv->hwver > 0) {
+ /* Read PCI230+/260+ ADC FIFO level. */
+ fifoamount = inw(devpriv->daqio +
+ PCI230P_ADCFFLEV);
+ if (fifoamount == 0)
+ break; /* Shouldn't happen. */
+ } else {
+ /* FIFO not empty. */
+ fifoamount = 1;
+ }
+ }
+
+ val = pci230_ai_read(dev);
+ if (!comedi_buf_write_samples(s, &val, 1))
+ break;
+
+ fifoamount--;
+
+ if (cmd->stop_src == TRIG_COUNT &&
+ async->scans_done >= cmd->stop_arg) {
+ async->events |= COMEDI_CB_EOA;
+ break;
+ }
+ }
+
+ /* update FIFO interrupt trigger level if still running */
+ if (!(async->events & COMEDI_CB_CANCEL_MASK))
+ pci230_ai_update_fifo_trigger_level(dev, s);
+}
+
+static int pci230_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
+{
+ struct pci230_private *devpriv = dev->private;
+ unsigned int i, chan, range, diff;
+ unsigned int res_mask;
+ unsigned short adccon, adcen;
+ unsigned char zgat;
+
+ /* Get the command. */
+ struct comedi_async *async = s->async;
+ struct comedi_cmd *cmd = &async->cmd;
+
+ /*
+ * Determine which shared resources are needed.
+ */
+ res_mask = 0;
+ /*
+ * Need Z2-CT2 to supply a conversion trigger source at a high
+ * logic level, even if not doing timed conversions.
+ */
+ res_mask |= RES_Z2CT2;
+ if (cmd->scan_begin_src != TRIG_FOLLOW) {
+ /* Using Z2-CT0 monostable to gate Z2-CT2 conversion timer */
+ res_mask |= RES_Z2CT0;
+ if (cmd->scan_begin_src == TRIG_TIMER) {
+ /* Using Z2-CT1 for scan frequency */
+ res_mask |= RES_Z2CT1;
+ }
+ }
+ /* Claim resources. */
+ if (!pci230_claim_shared(dev, res_mask, OWNER_AICMD))
+ return -EBUSY;
+
+ /*
+ * Steps:
+ * - Set channel scan list.
+ * - Set channel gains.
+ * - Enable and reset FIFO, specify uni/bip, se/diff, and set
+ * start conversion source to point to something at a high logic
+ * level (we use the output of counter/timer 2 for this purpose.
+ * - PAUSE to allow things to settle down.
+ * - Reset the FIFO again because it needs resetting twice and there
+ * may have been a false conversion trigger on some versions of
+ * PCI230/260 due to the start conversion source being set to a
+ * high logic level.
+ * - Enable ADC FIFO level interrupt.
+ * - Set actual conversion trigger source and FIFO interrupt trigger
+ * level.
+ * - If convert_src is TRIG_TIMER, set up the timers.
+ */
+
+ adccon = PCI230_ADC_FIFO_EN;
+ adcen = 0;
+
+ if (CR_AREF(cmd->chanlist[0]) == AREF_DIFF) {
+ /* Differential - all channels must be differential. */
+ diff = 1;
+ adccon |= PCI230_ADC_IM_DIF;
+ } else {
+ /* Single ended - all channels must be single-ended. */
+ diff = 0;
+ adccon |= PCI230_ADC_IM_SE;
+ }
+
+ range = CR_RANGE(cmd->chanlist[0]);
+ devpriv->ai_bipolar = comedi_range_is_bipolar(s, range);
+ if (devpriv->ai_bipolar)
+ adccon |= PCI230_ADC_IR_BIP;
+ else
+ adccon |= PCI230_ADC_IR_UNI;
+
+ for (i = 0; i < cmd->chanlist_len; i++) {
+ unsigned int gainshift;
+
+ chan = CR_CHAN(cmd->chanlist[i]);
+ range = CR_RANGE(cmd->chanlist[i]);
+ if (diff) {
+ gainshift = 2 * chan;
+ if (devpriv->hwver == 0) {
+ /*
+ * Original PCI230/260 expects both inputs of
+ * the differential channel to be enabled.
+ */
+ adcen |= 3 << gainshift;
+ } else {
+ /*
+ * PCI230+/260+ expects only one input of the
+ * differential channel to be enabled.
+ */
+ adcen |= 1 << gainshift;
+ }
+ } else {
+ gainshift = chan & ~1;
+ adcen |= 1 << chan;
+ }
+ devpriv->adcg = (devpriv->adcg & ~(3 << gainshift)) |
+ (pci230_ai_gain[range] << gainshift);
+ }
+
+ /* Set channel scan list. */
+ outw(adcen, devpriv->daqio + PCI230_ADCEN);
+
+ /* Set channel gains. */
+ outw(devpriv->adcg, devpriv->daqio + PCI230_ADCG);
+
+ /*
+ * Set counter/timer 2 output high for use as the initial start
+ * conversion source.
+ */
+ comedi_8254_set_mode(dev->pacer, 2, I8254_MODE1);
+
+ /*
+ * Temporarily use CT2 output as conversion trigger source and
+ * temporarily set FIFO interrupt trigger level to 'full'.
+ */
+ adccon |= PCI230_ADC_INT_FIFO_FULL | PCI230_ADC_TRIG_Z2CT2;
+
+ /*
+ * Enable and reset FIFO, specify FIFO trigger level full, specify
+ * uni/bip, se/diff, and temporarily set the start conversion source
+ * to CT2 output. Note that CT2 output is currently high, and this
+ * will produce a false conversion trigger on some versions of the
+ * PCI230/260, but that will be dealt with later.
+ */
+ devpriv->adccon = adccon;
+ outw(adccon | PCI230_ADC_FIFO_RESET, devpriv->daqio + PCI230_ADCCON);
+
+ /*
+ * Delay -
+ * Failure to include this will result in the first few channels'-worth
+ * of data being corrupt, normally manifesting itself by large negative
+ * voltages. It seems the board needs time to settle between the first
+ * FIFO reset (above) and the second FIFO reset (below). Setting the
+ * channel gains and scan list _before_ the first FIFO reset also
+ * helps, though only slightly.
+ */
+ usleep_range(25, 100);
+
+ /* Reset FIFO again. */
+ outw(adccon | PCI230_ADC_FIFO_RESET, devpriv->daqio + PCI230_ADCCON);
+
+ if (cmd->convert_src == TRIG_TIMER) {
+ /*
+ * Set up CT2 as conversion timer, but gate it off for now.
+ * Note, counter/timer output 2 can be monitored on the
+ * connector: PCI230 pin 21, PCI260 pin 18.
+ */
+ zgat = GAT_CONFIG(2, GAT_GND);
+ outb(zgat, dev->iobase + PCI230_ZGAT_SCE);
+ /* Set counter/timer 2 to the specified conversion period. */
+ pci230_ct_setup_ns_mode(dev, 2, I8254_MODE3, cmd->convert_arg,
+ cmd->flags);
+ if (cmd->scan_begin_src != TRIG_FOLLOW) {
+ /*
+ * Set up monostable on CT0 output for scan timing. A
+ * rising edge on the trigger (gate) input of CT0 will
+ * trigger the monostable, causing its output to go low
+ * for the configured period. The period depends on
+ * the conversion period and the number of conversions
+ * in the scan.
+ *
+ * Set the trigger high before setting up the
+ * monostable to stop it triggering. The trigger
+ * source will be changed later.
+ */
+ zgat = GAT_CONFIG(0, GAT_VCC);
+ outb(zgat, dev->iobase + PCI230_ZGAT_SCE);
+ pci230_ct_setup_ns_mode(dev, 0, I8254_MODE1,
+ ((uint64_t)cmd->convert_arg *
+ cmd->scan_end_arg),
+ CMDF_ROUND_UP);
+ if (cmd->scan_begin_src == TRIG_TIMER) {
+ /*
+ * Monostable on CT0 will be triggered by
+ * output of CT1 at configured scan frequency.
+ *
+ * Set up CT1 but gate it off for now.
+ */
+ zgat = GAT_CONFIG(1, GAT_GND);
+ outb(zgat, dev->iobase + PCI230_ZGAT_SCE);
+ pci230_ct_setup_ns_mode(dev, 1, I8254_MODE3,
+ cmd->scan_begin_arg,
+ cmd->flags);
+ }
+ }
+ }
+
+ if (cmd->start_src == TRIG_INT)
+ s->async->inttrig = pci230_ai_inttrig_start;
+ else /* TRIG_NOW */
+ pci230_ai_start(dev, s);
+
+ return 0;
+}
+
+static int pci230_ai_cancel(struct comedi_device *dev,
+ struct comedi_subdevice *s)
+{
+ pci230_ai_stop(dev, s);
+ return 0;
+}
+
+/* Interrupt handler */
+static irqreturn_t pci230_interrupt(int irq, void *d)
+{
+ unsigned char status_int, valid_status_int, temp_ier;
+ struct comedi_device *dev = (struct comedi_device *)d;
+ struct pci230_private *devpriv = dev->private;
+ struct comedi_subdevice *s_ao = dev->write_subdev;
+ struct comedi_subdevice *s_ai = dev->read_subdev;
+ unsigned long irqflags;
+
+ /* Read interrupt status/enable register. */
+ status_int = inb(dev->iobase + PCI230_INT_STAT);
+
+ if (status_int == PCI230_INT_DISABLE)
+ return IRQ_NONE;
+
+ spin_lock_irqsave(&devpriv->isr_spinlock, irqflags);
+ valid_status_int = devpriv->ier & status_int;
+ /*
+ * Disable triggered interrupts.
+ * (Only those interrupts that need re-enabling, are, later in the
+ * handler).
+ */
+ temp_ier = devpriv->ier & ~status_int;
+ outb(temp_ier, dev->iobase + PCI230_INT_SCE);
+ devpriv->intr_running = true;
+ devpriv->intr_cpuid = THISCPU;
+ spin_unlock_irqrestore(&devpriv->isr_spinlock, irqflags);
+
+ /*
+ * Check the source of interrupt and handle it.
+ * The PCI230 can cope with concurrent ADC, DAC, PPI C0 and C3
+ * interrupts. However, at present (Comedi-0.7.60) does not allow
+ * concurrent execution of commands, instructions or a mixture of the
+ * two.
+ */
+
+ if (valid_status_int & PCI230_INT_ZCLK_CT1)
+ pci230_handle_ao_nofifo(dev, s_ao);
+
+ if (valid_status_int & PCI230P2_INT_DAC)
+ pci230_handle_ao_fifo(dev, s_ao);
+
+ if (valid_status_int & PCI230_INT_ADC)
+ pci230_handle_ai(dev, s_ai);
+
+ /* Reenable interrupts. */
+ spin_lock_irqsave(&devpriv->isr_spinlock, irqflags);
+ if (devpriv->ier != temp_ier)
+ outb(devpriv->ier, dev->iobase + PCI230_INT_SCE);
+ devpriv->intr_running = false;
+ spin_unlock_irqrestore(&devpriv->isr_spinlock, irqflags);
+
+ comedi_handle_events(dev, s_ao);
+ comedi_handle_events(dev, s_ai);
+
+ return IRQ_HANDLED;
+}
+
+/* Check if PCI device matches a specific board. */
+static bool pci230_match_pci_board(const struct pci230_board *board,
+ struct pci_dev *pci_dev)
+{
+ /* assume pci_dev->device != PCI_DEVICE_ID_INVALID */
+ if (board->id != pci_dev->device)
+ return false;
+ if (board->min_hwver == 0)
+ return true;
+ /* Looking for a '+' model. First check length of registers. */
+ if (pci_resource_len(pci_dev, 3) < 32)
+ return false; /* Not a '+' model. */
+ /*
+ * TODO: temporarily enable PCI device and read the hardware version
+ * register. For now, assume it's okay.
+ */
+ return true;
+}
+
+/* Look for board matching PCI device. */
+static const struct pci230_board *pci230_find_pci_board(struct pci_dev *pci_dev)
+{
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(pci230_boards); i++)
+ if (pci230_match_pci_board(&pci230_boards[i], pci_dev))
+ return &pci230_boards[i];
+ return NULL;
+}
+
+static int pci230_auto_attach(struct comedi_device *dev,
+ unsigned long context_unused)
+{
+ struct pci_dev *pci_dev = comedi_to_pci_dev(dev);
+ const struct pci230_board *thisboard;
+ struct pci230_private *devpriv;
+ struct comedi_subdevice *s;
+ int rc;
+
+ dev_info(dev->class_dev, "amplc_pci230: attach pci %s\n",
+ pci_name(pci_dev));
+
+ devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
+ if (!devpriv)
+ return -ENOMEM;
+
+ spin_lock_init(&devpriv->isr_spinlock);
+ spin_lock_init(&devpriv->res_spinlock);
+ spin_lock_init(&devpriv->ai_stop_spinlock);
+ spin_lock_init(&devpriv->ao_stop_spinlock);
+
+ dev->board_ptr = pci230_find_pci_board(pci_dev);
+ if (!dev->board_ptr) {
+ dev_err(dev->class_dev,
+ "amplc_pci230: BUG! cannot determine board type!\n");
+ return -EINVAL;
+ }
+ thisboard = dev->board_ptr;
+ dev->board_name = thisboard->name;
+
+ rc = comedi_pci_enable(dev);
+ if (rc)
+ return rc;
+
+ /*
+ * Read base addresses of the PCI230's two I/O regions from PCI
+ * configuration register.
+ */
+ dev->iobase = pci_resource_start(pci_dev, 2);
+ devpriv->daqio = pci_resource_start(pci_dev, 3);
+ dev_dbg(dev->class_dev,
+ "%s I/O region 1 0x%04lx I/O region 2 0x%04lx\n",
+ dev->board_name, dev->iobase, devpriv->daqio);
+ /* Read bits of DACCON register - only the output range. */
+ devpriv->daccon = inw(devpriv->daqio + PCI230_DACCON) &
+ PCI230_DAC_OR_MASK;
+ /*
+ * Read hardware version register and set extended function register
+ * if they exist.
+ */
+ if (pci_resource_len(pci_dev, 3) >= 32) {
+ unsigned short extfunc = 0;
+
+ devpriv->hwver = inw(devpriv->daqio + PCI230P_HWVER);
+ if (devpriv->hwver < thisboard->min_hwver) {
+ dev_err(dev->class_dev,
+ "%s - bad hardware version - got %u, need %u\n",
+ dev->board_name, devpriv->hwver,
+ thisboard->min_hwver);
+ return -EIO;
+ }
+ if (devpriv->hwver > 0) {
+ if (!thisboard->have_dio) {
+ /*
+ * No DIO ports. Route counters' external gates
+ * to the EXTTRIG signal (PCI260+ pin 17).
+ * (Otherwise, they would be routed to DIO
+ * inputs PC0, PC1 and PC2 which don't exist
+ * on PCI260[+].)
+ */
+ extfunc |= PCI230P_EXTFUNC_GAT_EXTTRIG;
+ }
+ if (thisboard->ao_bits && devpriv->hwver >= 2) {
+ /* Enable DAC FIFO functionality. */
+ extfunc |= PCI230P2_EXTFUNC_DACFIFO;
+ }
+ }
+ outw(extfunc, devpriv->daqio + PCI230P_EXTFUNC);
+ if (extfunc & PCI230P2_EXTFUNC_DACFIFO) {
+ /*
+ * Temporarily enable DAC FIFO, reset it and disable
+ * FIFO wraparound.
+ */
+ outw(devpriv->daccon | PCI230P2_DAC_FIFO_EN |
+ PCI230P2_DAC_FIFO_RESET,
+ devpriv->daqio + PCI230_DACCON);
+ /* Clear DAC FIFO channel enable register. */
+ outw(0, devpriv->daqio + PCI230P2_DACEN);
+ /* Disable DAC FIFO. */
+ outw(devpriv->daccon, devpriv->daqio + PCI230_DACCON);
+ }
+ }
+ /* Disable board's interrupts. */
+ outb(0, dev->iobase + PCI230_INT_SCE);
+ /* Set ADC to a reasonable state. */
+ devpriv->adcg = 0;
+ devpriv->adccon = PCI230_ADC_TRIG_NONE | PCI230_ADC_IM_SE |
+ PCI230_ADC_IR_BIP;
+ outw(1 << 0, devpriv->daqio + PCI230_ADCEN);
+ outw(devpriv->adcg, devpriv->daqio + PCI230_ADCG);
+ outw(devpriv->adccon | PCI230_ADC_FIFO_RESET,
+ devpriv->daqio + PCI230_ADCCON);
+
+ if (pci_dev->irq) {
+ rc = request_irq(pci_dev->irq, pci230_interrupt, IRQF_SHARED,
+ dev->board_name, dev);
+ if (rc == 0)
+ dev->irq = pci_dev->irq;
+ }
+
+ dev->pacer = comedi_8254_init(dev->iobase + PCI230_Z2_CT_BASE,
+ 0, I8254_IO8, 0);
+ if (!dev->pacer)
+ return -ENOMEM;
+
+ rc = comedi_alloc_subdevices(dev, 3);
+ if (rc)
+ return rc;
+
+ s = &dev->subdevices[0];
+ /* analog input subdevice */
+ s->type = COMEDI_SUBD_AI;
+ s->subdev_flags = SDF_READABLE | SDF_DIFF | SDF_GROUND;
+ s->n_chan = 16;
+ s->maxdata = (1 << thisboard->ai_bits) - 1;
+ s->range_table = &pci230_ai_range;
+ s->insn_read = pci230_ai_insn_read;
+ s->len_chanlist = 256; /* but there are restrictions. */
+ if (dev->irq) {
+ dev->read_subdev = s;
+ s->subdev_flags |= SDF_CMD_READ;
+ s->do_cmd = pci230_ai_cmd;
+ s->do_cmdtest = pci230_ai_cmdtest;
+ s->cancel = pci230_ai_cancel;
+ }
+
+ s = &dev->subdevices[1];
+ /* analog output subdevice */
+ if (thisboard->ao_bits) {
+ s->type = COMEDI_SUBD_AO;
+ s->subdev_flags = SDF_WRITABLE | SDF_GROUND;
+ s->n_chan = 2;
+ s->maxdata = (1 << thisboard->ao_bits) - 1;
+ s->range_table = &pci230_ao_range;
+ s->insn_write = pci230_ao_insn_write;
+ s->len_chanlist = 2;
+ if (dev->irq) {
+ dev->write_subdev = s;
+ s->subdev_flags |= SDF_CMD_WRITE;
+ s->do_cmd = pci230_ao_cmd;
+ s->do_cmdtest = pci230_ao_cmdtest;
+ s->cancel = pci230_ao_cancel;
+ }
+
+ rc = comedi_alloc_subdev_readback(s);
+ if (rc)
+ return rc;
+ } else {
+ s->type = COMEDI_SUBD_UNUSED;
+ }
+
+ s = &dev->subdevices[2];
+ /* digital i/o subdevice */
+ if (thisboard->have_dio) {
+ rc = subdev_8255_init(dev, s, NULL, PCI230_PPI_X_BASE);
+ if (rc)
+ return rc;
+ } else {
+ s->type = COMEDI_SUBD_UNUSED;
+ }
+
+ return 0;
+}
+
+static struct comedi_driver amplc_pci230_driver = {
+ .driver_name = "amplc_pci230",
+ .module = THIS_MODULE,
+ .auto_attach = pci230_auto_attach,
+ .detach = comedi_pci_detach,
+};
+
+static int amplc_pci230_pci_probe(struct pci_dev *dev,
+ const struct pci_device_id *id)
+{
+ return comedi_pci_auto_config(dev, &amplc_pci230_driver,
+ id->driver_data);
+}
+
+static const struct pci_device_id amplc_pci230_pci_table[] = {
+ { PCI_DEVICE(PCI_VENDOR_ID_AMPLICON, PCI_DEVICE_ID_PCI230) },
+ { PCI_DEVICE(PCI_VENDOR_ID_AMPLICON, PCI_DEVICE_ID_PCI260) },
+ { 0 }
+};
+MODULE_DEVICE_TABLE(pci, amplc_pci230_pci_table);
+
+static struct pci_driver amplc_pci230_pci_driver = {
+ .name = "amplc_pci230",
+ .id_table = amplc_pci230_pci_table,
+ .probe = amplc_pci230_pci_probe,
+ .remove = comedi_pci_auto_unconfig,
+};
+module_comedi_pci_driver(amplc_pci230_driver, amplc_pci230_pci_driver);
+
+MODULE_AUTHOR("Comedi http://www.comedi.org");
+MODULE_DESCRIPTION("Comedi driver for Amplicon PCI230(+) and PCI260(+)");
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/comedi/drivers/amplc_pci236.c b/drivers/staging/comedi/drivers/amplc_pci236.c
new file mode 100644
index 000000000..31cc38b4b
--- /dev/null
+++ b/drivers/staging/comedi/drivers/amplc_pci236.c
@@ -0,0 +1,153 @@
+/*
+ * comedi/drivers/amplc_pci236.c
+ * Driver for Amplicon PCI236 DIO boards.
+ *
+ * Copyright (C) 2002-2014 MEV Ltd. <http://www.mev.co.uk/>
+ *
+ * COMEDI - Linux Control and Measurement Device Interface
+ * Copyright (C) 2000 David A. Schleef <ds@schleef.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+/*
+ * Driver: amplc_pci236
+ * Description: Amplicon PCI236
+ * Author: Ian Abbott <abbotti@mev.co.uk>
+ * Devices: [Amplicon] PCI236 (amplc_pci236)
+ * Updated: Fri, 25 Jul 2014 15:32:40 +0000
+ * Status: works
+ *
+ * Configuration options:
+ * none
+ *
+ * Manual configuration of PCI board (PCI236) is not supported; it is
+ * configured automatically.
+ *
+ * The PCI236 board has a single 8255 appearing as subdevice 0.
+ *
+ * Subdevice 1 pretends to be a digital input device, but it always
+ * returns 0 when read. However, if you run a command with
+ * scan_begin_src=TRIG_EXT, a rising edge on port C bit 3 acts as an
+ * external trigger, which can be used to wake up tasks. This is like
+ * the comedi_parport device. If no interrupt is connected, then
+ * subdevice 1 is unused.
+ */
+
+#include <linux/module.h>
+#include <linux/interrupt.h>
+
+#include "../comedi_pci.h"
+
+#include "amplc_pc236.h"
+#include "plx9052.h"
+
+/* Disable, and clear, interrupts */
+#define PCI236_INTR_DISABLE (PLX9052_INTCSR_LI1POL | \
+ PLX9052_INTCSR_LI2POL | \
+ PLX9052_INTCSR_LI1SEL | \
+ PLX9052_INTCSR_LI1CLRINT)
+
+/* Enable, and clear, interrupts */
+#define PCI236_INTR_ENABLE (PLX9052_INTCSR_LI1ENAB | \
+ PLX9052_INTCSR_LI1POL | \
+ PLX9052_INTCSR_LI2POL | \
+ PLX9052_INTCSR_PCIENAB | \
+ PLX9052_INTCSR_LI1SEL | \
+ PLX9052_INTCSR_LI1CLRINT)
+
+static void pci236_intr_update_cb(struct comedi_device *dev, bool enable)
+{
+ struct pc236_private *devpriv = dev->private;
+
+ /* this will also clear the "local interrupt 1" latch */
+ outl(enable ? PCI236_INTR_ENABLE : PCI236_INTR_DISABLE,
+ devpriv->lcr_iobase + PLX9052_INTCSR);
+}
+
+static bool pci236_intr_chk_clr_cb(struct comedi_device *dev)
+{
+ struct pc236_private *devpriv = dev->private;
+
+ /* check if interrupt occurred */
+ if (!(inl(devpriv->lcr_iobase + PLX9052_INTCSR) &
+ PLX9052_INTCSR_LI1STAT))
+ return false;
+ /* clear the interrupt */
+ pci236_intr_update_cb(dev, devpriv->enable_irq);
+ return true;
+}
+
+static const struct pc236_board pc236_pci_board = {
+ .name = "pci236",
+ .intr_update_cb = pci236_intr_update_cb,
+ .intr_chk_clr_cb = pci236_intr_chk_clr_cb,
+};
+
+static int pci236_auto_attach(struct comedi_device *dev,
+ unsigned long context_unused)
+{
+ struct pci_dev *pci_dev = comedi_to_pci_dev(dev);
+ struct pc236_private *devpriv;
+ unsigned long iobase;
+ int ret;
+
+ dev_info(dev->class_dev, "amplc_pci236: attach pci %s\n",
+ pci_name(pci_dev));
+
+ devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
+ if (!devpriv)
+ return -ENOMEM;
+
+ dev->board_ptr = &pc236_pci_board;
+ dev->board_name = pc236_pci_board.name;
+ ret = comedi_pci_enable(dev);
+ if (ret)
+ return ret;
+
+ devpriv->lcr_iobase = pci_resource_start(pci_dev, 1);
+ iobase = pci_resource_start(pci_dev, 2);
+ return amplc_pc236_common_attach(dev, iobase, pci_dev->irq,
+ IRQF_SHARED);
+}
+
+static struct comedi_driver amplc_pci236_driver = {
+ .driver_name = "amplc_pci236",
+ .module = THIS_MODULE,
+ .auto_attach = pci236_auto_attach,
+ .detach = comedi_pci_detach,
+};
+
+static const struct pci_device_id pci236_pci_table[] = {
+ { PCI_DEVICE(PCI_VENDOR_ID_AMPLICON, 0x0009) },
+ { 0 }
+};
+
+MODULE_DEVICE_TABLE(pci, pci236_pci_table);
+
+static int amplc_pci236_pci_probe(struct pci_dev *dev,
+ const struct pci_device_id *id)
+{
+ return comedi_pci_auto_config(dev, &amplc_pci236_driver,
+ id->driver_data);
+}
+
+static struct pci_driver amplc_pci236_pci_driver = {
+ .name = "amplc_pci236",
+ .id_table = pci236_pci_table,
+ .probe = &amplc_pci236_pci_probe,
+ .remove = comedi_pci_auto_unconfig,
+};
+
+module_comedi_pci_driver(amplc_pci236_driver, amplc_pci236_pci_driver);
+
+MODULE_AUTHOR("Comedi http://www.comedi.org");
+MODULE_DESCRIPTION("Comedi driver for Amplicon PCI236 DIO boards");
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/comedi/drivers/amplc_pci263.c b/drivers/staging/comedi/drivers/amplc_pci263.c
new file mode 100644
index 000000000..b6768aa90
--- /dev/null
+++ b/drivers/staging/comedi/drivers/amplc_pci263.c
@@ -0,0 +1,114 @@
+/*
+ comedi/drivers/amplc_pci263.c
+ Driver for Amplicon PCI263 relay board.
+
+ Copyright (C) 2002 MEV Ltd. <http://www.mev.co.uk/>
+
+ COMEDI - Linux Control and Measurement Device Interface
+ Copyright (C) 2000 David A. Schleef <ds@schleef.org>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+*/
+/*
+Driver: amplc_pci263
+Description: Amplicon PCI263
+Author: Ian Abbott <abbotti@mev.co.uk>
+Devices: [Amplicon] PCI263 (amplc_pci263)
+Updated: Fri, 12 Apr 2013 15:19:36 +0100
+Status: works
+
+Configuration options: not applicable, uses PCI auto config
+
+The board appears as one subdevice, with 16 digital outputs, each
+connected to a reed-relay. Relay contacts are closed when output is 1.
+The state of the outputs can be read.
+*/
+
+#include <linux/module.h>
+
+#include "../comedi_pci.h"
+
+static int pci263_do_insn_bits(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ if (comedi_dio_update_state(s, data)) {
+ outb(s->state & 0xff, dev->iobase);
+ outb((s->state >> 8) & 0xff, dev->iobase + 1);
+ }
+
+ data[1] = s->state;
+
+ return insn->n;
+}
+
+static int pci263_auto_attach(struct comedi_device *dev,
+ unsigned long context_unused)
+{
+ struct pci_dev *pci_dev = comedi_to_pci_dev(dev);
+ struct comedi_subdevice *s;
+ int ret;
+
+ ret = comedi_pci_enable(dev);
+ if (ret)
+ return ret;
+
+ dev->iobase = pci_resource_start(pci_dev, 2);
+ ret = comedi_alloc_subdevices(dev, 1);
+ if (ret)
+ return ret;
+
+ s = &dev->subdevices[0];
+ /* digital output subdevice */
+ s->type = COMEDI_SUBD_DO;
+ s->subdev_flags = SDF_WRITABLE;
+ s->n_chan = 16;
+ s->maxdata = 1;
+ s->range_table = &range_digital;
+ s->insn_bits = pci263_do_insn_bits;
+ /* read initial relay state */
+ s->state = inb(dev->iobase) | (inb(dev->iobase + 1) << 8);
+
+ return 0;
+}
+
+static struct comedi_driver amplc_pci263_driver = {
+ .driver_name = "amplc_pci263",
+ .module = THIS_MODULE,
+ .auto_attach = pci263_auto_attach,
+ .detach = comedi_pci_detach,
+};
+
+static const struct pci_device_id pci263_pci_table[] = {
+ { PCI_DEVICE(PCI_VENDOR_ID_AMPLICON, 0x000c) },
+ {0}
+};
+MODULE_DEVICE_TABLE(pci, pci263_pci_table);
+
+static int amplc_pci263_pci_probe(struct pci_dev *dev,
+ const struct pci_device_id *id)
+{
+ return comedi_pci_auto_config(dev, &amplc_pci263_driver,
+ id->driver_data);
+}
+
+static struct pci_driver amplc_pci263_pci_driver = {
+ .name = "amplc_pci263",
+ .id_table = pci263_pci_table,
+ .probe = &amplc_pci263_pci_probe,
+ .remove = comedi_pci_auto_unconfig,
+};
+module_comedi_pci_driver(amplc_pci263_driver, amplc_pci263_pci_driver);
+
+MODULE_AUTHOR("Comedi http://www.comedi.org");
+MODULE_DESCRIPTION("Comedi driver for Amplicon PCI263 relay board");
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/comedi/drivers/c6xdigio.c b/drivers/staging/comedi/drivers/c6xdigio.c
new file mode 100644
index 000000000..1a109e30d
--- /dev/null
+++ b/drivers/staging/comedi/drivers/c6xdigio.c
@@ -0,0 +1,307 @@
+/*
+ * c6xdigio.c
+ * Hardware driver for Mechatronic Systems Inc. C6x_DIGIO DSP daughter card.
+ * http://web.archive.org/web/%2A/http://robot0.ge.uiuc.edu/~spong/mecha/
+ *
+ * COMEDI - Linux Control and Measurement Device Interface
+ * Copyright (C) 1999 Dan Block
+ *
+ * This program is free software; you can redistribute it and/or 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.
+ */
+
+/*
+ * Driver: c6xdigio
+ * Description: Mechatronic Systems Inc. C6x_DIGIO DSP daughter card
+ * Author: Dan Block
+ * Status: unknown
+ * Devices: [Mechatronic Systems Inc.] C6x_DIGIO DSP daughter card (c6xdigio)
+ * Updated: Sun Nov 20 20:18:34 EST 2005
+ *
+ * Configuration Options:
+ * [0] - base address
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/errno.h>
+#include <linux/interrupt.h>
+#include <linux/timex.h>
+#include <linux/timer.h>
+#include <linux/io.h>
+#include <linux/pnp.h>
+
+#include "../comedidev.h"
+
+/*
+ * Register I/O map
+ */
+#define C6XDIGIO_DATA_REG 0x00
+#define C6XDIGIO_DATA_CHAN(x) (((x) + 1) << 4)
+#define C6XDIGIO_DATA_PWM (1 << 5)
+#define C6XDIGIO_DATA_ENCODER (1 << 6)
+#define C6XDIGIO_STATUS_REG 0x01
+#define C6XDIGIO_CTRL_REG 0x02
+
+#define C6XDIGIO_TIME_OUT 20
+
+static int c6xdigio_chk_status(struct comedi_device *dev, unsigned long context)
+{
+ unsigned int status;
+ int timeout = 0;
+
+ do {
+ status = inb(dev->iobase + C6XDIGIO_STATUS_REG);
+ if ((status & 0x80) != context)
+ return 0;
+ timeout++;
+ } while (timeout < C6XDIGIO_TIME_OUT);
+
+ return -EBUSY;
+}
+
+static int c6xdigio_write_data(struct comedi_device *dev,
+ unsigned int val, unsigned int status)
+{
+ outb_p(val, dev->iobase + C6XDIGIO_DATA_REG);
+ return c6xdigio_chk_status(dev, status);
+}
+
+static int c6xdigio_get_encoder_bits(struct comedi_device *dev,
+ unsigned int *bits,
+ unsigned int cmd,
+ unsigned int status)
+{
+ unsigned int val;
+
+ val = inb(dev->iobase + C6XDIGIO_STATUS_REG);
+ val >>= 3;
+ val &= 0x07;
+
+ *bits = val;
+
+ return c6xdigio_write_data(dev, cmd, status);
+}
+
+static void c6xdigio_pwm_write(struct comedi_device *dev,
+ unsigned int chan, unsigned int val)
+{
+ unsigned int cmd = C6XDIGIO_DATA_PWM | C6XDIGIO_DATA_CHAN(chan);
+ unsigned int bits;
+
+ if (val > 498)
+ val = 498;
+ if (val < 2)
+ val = 2;
+
+ bits = (val >> 0) & 0x03;
+ c6xdigio_write_data(dev, cmd | bits | (0 << 2), 0x00);
+ bits = (val >> 2) & 0x03;
+ c6xdigio_write_data(dev, cmd | bits | (1 << 2), 0x80);
+ bits = (val >> 4) & 0x03;
+ c6xdigio_write_data(dev, cmd | bits | (0 << 2), 0x00);
+ bits = (val >> 6) & 0x03;
+ c6xdigio_write_data(dev, cmd | bits | (1 << 2), 0x80);
+ bits = (val >> 8) & 0x03;
+ c6xdigio_write_data(dev, cmd | bits | (0 << 2), 0x00);
+
+ c6xdigio_write_data(dev, 0x00, 0x80);
+}
+
+static int c6xdigio_encoder_read(struct comedi_device *dev,
+ unsigned int chan)
+{
+ unsigned int cmd = C6XDIGIO_DATA_ENCODER | C6XDIGIO_DATA_CHAN(chan);
+ unsigned int val = 0;
+ unsigned int bits;
+
+ c6xdigio_write_data(dev, cmd, 0x00);
+
+ c6xdigio_get_encoder_bits(dev, &bits, cmd | (1 << 2), 0x80);
+ val |= (bits << 0);
+
+ c6xdigio_get_encoder_bits(dev, &bits, cmd | (0 << 2), 0x00);
+ val |= (bits << 3);
+
+ c6xdigio_get_encoder_bits(dev, &bits, cmd | (1 << 2), 0x80);
+ val |= (bits << 6);
+
+ c6xdigio_get_encoder_bits(dev, &bits, cmd | (0 << 2), 0x00);
+ val |= (bits << 9);
+
+ c6xdigio_get_encoder_bits(dev, &bits, cmd | (1 << 2), 0x80);
+ val |= (bits << 12);
+
+ c6xdigio_get_encoder_bits(dev, &bits, cmd | (0 << 2), 0x00);
+ val |= (bits << 15);
+
+ c6xdigio_get_encoder_bits(dev, &bits, cmd | (1 << 2), 0x80);
+ val |= (bits << 18);
+
+ c6xdigio_get_encoder_bits(dev, &bits, cmd | (0 << 2), 0x00);
+ val |= (bits << 21);
+
+ c6xdigio_write_data(dev, 0x00, 0x80);
+
+ return val;
+}
+
+static int c6xdigio_pwm_insn_write(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ unsigned int chan = CR_CHAN(insn->chanspec);
+ unsigned int val = (s->state >> (16 * chan)) & 0xffff;
+ int i;
+
+ for (i = 0; i < insn->n; i++) {
+ val = data[i];
+ c6xdigio_pwm_write(dev, chan, val);
+ }
+
+ /*
+ * There are only 2 PWM channels and they have a maxdata of 500.
+ * Instead of allocating private data to save the values in for
+ * readback this driver just packs the values for the two channels
+ * in the s->state.
+ */
+ s->state &= (0xffff << (16 * chan));
+ s->state |= (val << (16 * chan));
+
+ return insn->n;
+}
+
+static int c6xdigio_pwm_insn_read(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ unsigned int chan = CR_CHAN(insn->chanspec);
+ unsigned int val;
+ int i;
+
+ val = (s->state >> (16 * chan)) & 0xffff;
+
+ for (i = 0; i < insn->n; i++)
+ data[i] = val;
+
+ return insn->n;
+}
+
+static int c6xdigio_encoder_insn_read(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ unsigned int chan = CR_CHAN(insn->chanspec);
+ unsigned int val;
+ int i;
+
+ for (i = 0; i < insn->n; i++) {
+ val = c6xdigio_encoder_read(dev, chan);
+
+ /* munge two's complement value to offset binary */
+ data[i] = comedi_offset_munge(s, val);
+ }
+
+ return insn->n;
+}
+
+static void c6xdigio_init(struct comedi_device *dev)
+{
+ /* Initialize the PWM */
+ c6xdigio_write_data(dev, 0x70, 0x00);
+ c6xdigio_write_data(dev, 0x74, 0x80);
+ c6xdigio_write_data(dev, 0x70, 0x00);
+ c6xdigio_write_data(dev, 0x00, 0x80);
+
+ /* Reset the encoders */
+ c6xdigio_write_data(dev, 0x68, 0x00);
+ c6xdigio_write_data(dev, 0x6c, 0x80);
+ c6xdigio_write_data(dev, 0x68, 0x00);
+ c6xdigio_write_data(dev, 0x00, 0x80);
+}
+
+static const struct pnp_device_id c6xdigio_pnp_tbl[] = {
+ /* Standard LPT Printer Port */
+ {.id = "PNP0400", .driver_data = 0},
+ /* ECP Printer Port */
+ {.id = "PNP0401", .driver_data = 0},
+ {}
+};
+
+static struct pnp_driver c6xdigio_pnp_driver = {
+ .name = "c6xdigio",
+ .id_table = c6xdigio_pnp_tbl,
+};
+
+static int c6xdigio_attach(struct comedi_device *dev,
+ struct comedi_devconfig *it)
+{
+ struct comedi_subdevice *s;
+ int ret;
+
+ ret = comedi_request_region(dev, it->options[0], 0x03);
+ if (ret)
+ return ret;
+
+ ret = comedi_alloc_subdevices(dev, 2);
+ if (ret)
+ return ret;
+
+ /* Make sure that PnP ports get activated */
+ pnp_register_driver(&c6xdigio_pnp_driver);
+
+ s = &dev->subdevices[0];
+ /* pwm output subdevice */
+ s->type = COMEDI_SUBD_PWM;
+ s->subdev_flags = SDF_WRITABLE;
+ s->n_chan = 2;
+ s->maxdata = 500;
+ s->range_table = &range_unknown;
+ s->insn_write = c6xdigio_pwm_insn_write;
+ s->insn_read = c6xdigio_pwm_insn_read;
+
+ s = &dev->subdevices[1];
+ /* encoder (counter) subdevice */
+ s->type = COMEDI_SUBD_COUNTER;
+ s->subdev_flags = SDF_READABLE | SDF_LSAMPL;
+ s->n_chan = 2;
+ s->maxdata = 0xffffff;
+ s->range_table = &range_unknown;
+ s->insn_read = c6xdigio_encoder_insn_read;
+
+ /* I will call this init anyway but more than likely the DSP board */
+ /* will not be connected when device driver is loaded. */
+ c6xdigio_init(dev);
+
+ return 0;
+}
+
+static void c6xdigio_detach(struct comedi_device *dev)
+{
+ comedi_legacy_detach(dev);
+ pnp_unregister_driver(&c6xdigio_pnp_driver);
+}
+
+static struct comedi_driver c6xdigio_driver = {
+ .driver_name = "c6xdigio",
+ .module = THIS_MODULE,
+ .attach = c6xdigio_attach,
+ .detach = c6xdigio_detach,
+};
+module_comedi_driver(c6xdigio_driver);
+
+MODULE_AUTHOR("Comedi http://www.comedi.org");
+MODULE_DESCRIPTION("Comedi driver for the C6x_DIGIO DSP daughter card");
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/comedi/drivers/cb_das16_cs.c b/drivers/staging/comedi/drivers/cb_das16_cs.c
new file mode 100644
index 000000000..ae84f2c0c
--- /dev/null
+++ b/drivers/staging/comedi/drivers/cb_das16_cs.c
@@ -0,0 +1,355 @@
+/*
+ comedi/drivers/das16cs.c
+ Driver for Computer Boards PC-CARD DAS16/16.
+
+ COMEDI - Linux Control and Measurement Device Interface
+ Copyright (C) 2000, 2001, 2002 David A. Schleef <ds@schleef.org>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ PCMCIA support code for this driver is adapted from the dummy_cs.c
+ driver of the Linux PCMCIA Card Services package.
+
+ The initial developer of the original code is David A. Hinds
+ <dahinds@users.sourceforge.net>. Portions created by David A. Hinds
+ are Copyright (C) 1999 David A. Hinds. All Rights Reserved.
+
+*/
+/*
+Driver: cb_das16_cs
+Description: Computer Boards PC-CARD DAS16/16
+Devices: [ComputerBoards] PC-CARD DAS16/16 (cb_das16_cs), PC-CARD DAS16/16-AO
+Author: ds
+Updated: Mon, 04 Nov 2002 20:04:21 -0800
+Status: experimental
+*/
+
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+
+#include "../comedi_pcmcia.h"
+
+#include "comedi_8254.h"
+
+#define DAS16CS_ADC_DATA 0
+#define DAS16CS_DIO_MUX 2
+#define DAS16CS_MISC1 4
+#define DAS16CS_MISC2 6
+#define DAS16CS_TIMER_BASE 8
+#define DAS16CS_DIO 16
+
+struct das16cs_board {
+ const char *name;
+ int device_id;
+ int n_ao_chans;
+};
+
+static const struct das16cs_board das16cs_boards[] = {
+ {
+ .name = "PC-CARD DAS16/16-AO",
+ .device_id = 0x0039,
+ .n_ao_chans = 2,
+ }, {
+ .name = "PCM-DAS16s/16",
+ .device_id = 0x4009,
+ .n_ao_chans = 0,
+ }, {
+ .name = "PC-CARD DAS16/16",
+ .device_id = 0x0000, /* unknown */
+ .n_ao_chans = 0,
+ },
+};
+
+struct das16cs_private {
+ unsigned short status1;
+ unsigned short status2;
+};
+
+static const struct comedi_lrange das16cs_ai_range = {
+ 4, {
+ BIP_RANGE(10),
+ BIP_RANGE(5),
+ BIP_RANGE(2.5),
+ BIP_RANGE(1.25),
+ }
+};
+
+static int das16cs_ai_eoc(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned long context)
+{
+ unsigned int status;
+
+ status = inw(dev->iobase + DAS16CS_MISC1);
+ if (status & 0x0080)
+ return 0;
+ return -EBUSY;
+}
+
+static int das16cs_ai_rinsn(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn, unsigned int *data)
+{
+ struct das16cs_private *devpriv = dev->private;
+ int chan = CR_CHAN(insn->chanspec);
+ int range = CR_RANGE(insn->chanspec);
+ int aref = CR_AREF(insn->chanspec);
+ int ret;
+ int i;
+
+ outw(chan, dev->iobase + DAS16CS_DIO_MUX);
+
+ devpriv->status1 &= ~0xf320;
+ devpriv->status1 |= (aref == AREF_DIFF) ? 0 : 0x0020;
+ outw(devpriv->status1, dev->iobase + DAS16CS_MISC1);
+
+ devpriv->status2 &= ~0xff00;
+ switch (range) {
+ case 0:
+ devpriv->status2 |= 0x800;
+ break;
+ case 1:
+ devpriv->status2 |= 0x000;
+ break;
+ case 2:
+ devpriv->status2 |= 0x100;
+ break;
+ case 3:
+ devpriv->status2 |= 0x200;
+ break;
+ }
+ outw(devpriv->status2, dev->iobase + DAS16CS_MISC2);
+
+ for (i = 0; i < insn->n; i++) {
+ outw(0, dev->iobase + DAS16CS_ADC_DATA);
+
+ ret = comedi_timeout(dev, s, insn, das16cs_ai_eoc, 0);
+ if (ret)
+ return ret;
+
+ data[i] = inw(dev->iobase + DAS16CS_ADC_DATA);
+ }
+
+ return i;
+}
+
+static int das16cs_ao_insn_write(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ struct das16cs_private *devpriv = dev->private;
+ unsigned int chan = CR_CHAN(insn->chanspec);
+ unsigned int val = s->readback[chan];
+ unsigned short status1;
+ int bit;
+ int i;
+
+ for (i = 0; i < insn->n; i++) {
+ val = data[i];
+
+ outw(devpriv->status1, dev->iobase + DAS16CS_MISC1);
+ udelay(1);
+
+ status1 = devpriv->status1 & ~0xf;
+ if (chan)
+ status1 |= 0x0001;
+ else
+ status1 |= 0x0008;
+
+ outw(status1, dev->iobase + DAS16CS_MISC1);
+ udelay(1);
+
+ for (bit = 15; bit >= 0; bit--) {
+ int b = (val >> bit) & 0x1;
+
+ b <<= 1;
+ outw(status1 | b | 0x0000, dev->iobase + DAS16CS_MISC1);
+ udelay(1);
+ outw(status1 | b | 0x0004, dev->iobase + DAS16CS_MISC1);
+ udelay(1);
+ }
+ /*
+ * Make both DAC0CS and DAC1CS high to load
+ * the new data and update analog the output
+ */
+ outw(status1 | 0x9, dev->iobase + DAS16CS_MISC1);
+ }
+ s->readback[chan] = val;
+
+ return insn->n;
+}
+
+static int das16cs_dio_insn_bits(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ if (comedi_dio_update_state(s, data))
+ outw(s->state, dev->iobase + DAS16CS_DIO);
+
+ data[1] = inw(dev->iobase + DAS16CS_DIO);
+
+ return insn->n;
+}
+
+static int das16cs_dio_insn_config(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ struct das16cs_private *devpriv = dev->private;
+ unsigned int chan = CR_CHAN(insn->chanspec);
+ unsigned int mask;
+ int ret;
+
+ if (chan < 4)
+ mask = 0x0f;
+ else
+ mask = 0xf0;
+
+ ret = comedi_dio_insn_config(dev, s, insn, data, mask);
+ if (ret)
+ return ret;
+
+ devpriv->status2 &= ~0x00c0;
+ devpriv->status2 |= (s->io_bits & 0xf0) ? 0x0080 : 0;
+ devpriv->status2 |= (s->io_bits & 0x0f) ? 0x0040 : 0;
+
+ outw(devpriv->status2, dev->iobase + DAS16CS_MISC2);
+
+ return insn->n;
+}
+
+static const void *das16cs_find_boardinfo(struct comedi_device *dev,
+ struct pcmcia_device *link)
+{
+ const struct das16cs_board *board;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(das16cs_boards); i++) {
+ board = &das16cs_boards[i];
+ if (board->device_id == link->card_id)
+ return board;
+ }
+
+ return NULL;
+}
+
+static int das16cs_auto_attach(struct comedi_device *dev,
+ unsigned long context)
+{
+ struct pcmcia_device *link = comedi_to_pcmcia_dev(dev);
+ const struct das16cs_board *board;
+ struct das16cs_private *devpriv;
+ struct comedi_subdevice *s;
+ int ret;
+
+ board = das16cs_find_boardinfo(dev, link);
+ if (!board)
+ return -ENODEV;
+ dev->board_ptr = board;
+ dev->board_name = board->name;
+
+ link->config_flags |= CONF_AUTO_SET_IO | CONF_ENABLE_IRQ;
+ ret = comedi_pcmcia_enable(dev, NULL);
+ if (ret)
+ return ret;
+ dev->iobase = link->resource[0]->start;
+
+ link->priv = dev;
+
+ devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
+ if (!devpriv)
+ return -ENOMEM;
+
+ dev->pacer = comedi_8254_init(dev->iobase + DAS16CS_TIMER_BASE,
+ I8254_OSC_BASE_10MHZ, I8254_IO16, 0);
+ if (!dev->pacer)
+ return -ENOMEM;
+
+ ret = comedi_alloc_subdevices(dev, 3);
+ if (ret)
+ return ret;
+
+ s = &dev->subdevices[0];
+ /* analog input subdevice */
+ s->type = COMEDI_SUBD_AI;
+ s->subdev_flags = SDF_READABLE | SDF_GROUND | SDF_DIFF | SDF_CMD_READ;
+ s->n_chan = 16;
+ s->maxdata = 0xffff;
+ s->range_table = &das16cs_ai_range;
+ s->len_chanlist = 16;
+ s->insn_read = das16cs_ai_rinsn;
+
+ s = &dev->subdevices[1];
+ /* analog output subdevice */
+ if (board->n_ao_chans) {
+ s->type = COMEDI_SUBD_AO;
+ s->subdev_flags = SDF_WRITABLE;
+ s->n_chan = board->n_ao_chans;
+ s->maxdata = 0xffff;
+ s->range_table = &range_bipolar10;
+ s->insn_write = &das16cs_ao_insn_write;
+
+ ret = comedi_alloc_subdev_readback(s);
+ if (ret)
+ return ret;
+ } else {
+ s->type = COMEDI_SUBD_UNUSED;
+ }
+
+ s = &dev->subdevices[2];
+ /* digital i/o subdevice */
+ s->type = COMEDI_SUBD_DIO;
+ s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
+ s->n_chan = 8;
+ s->maxdata = 1;
+ s->range_table = &range_digital;
+ s->insn_bits = das16cs_dio_insn_bits;
+ s->insn_config = das16cs_dio_insn_config;
+
+ return 0;
+}
+
+static struct comedi_driver driver_das16cs = {
+ .driver_name = "cb_das16_cs",
+ .module = THIS_MODULE,
+ .auto_attach = das16cs_auto_attach,
+ .detach = comedi_pcmcia_disable,
+};
+
+static int das16cs_pcmcia_attach(struct pcmcia_device *link)
+{
+ return comedi_pcmcia_auto_config(link, &driver_das16cs);
+}
+
+static const struct pcmcia_device_id das16cs_id_table[] = {
+ PCMCIA_DEVICE_MANF_CARD(0x01c5, 0x0039),
+ PCMCIA_DEVICE_MANF_CARD(0x01c5, 0x4009),
+ PCMCIA_DEVICE_NULL
+};
+MODULE_DEVICE_TABLE(pcmcia, das16cs_id_table);
+
+static struct pcmcia_driver das16cs_driver = {
+ .name = "cb_das16_cs",
+ .owner = THIS_MODULE,
+ .id_table = das16cs_id_table,
+ .probe = das16cs_pcmcia_attach,
+ .remove = comedi_pcmcia_auto_unconfig,
+};
+module_comedi_pcmcia_driver(driver_das16cs, das16cs_driver);
+
+MODULE_AUTHOR("David A. Schleef <ds@schleef.org>");
+MODULE_DESCRIPTION("Comedi driver for Computer Boards PC-CARD DAS16/16");
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/comedi/drivers/cb_pcidas.c b/drivers/staging/comedi/drivers/cb_pcidas.c
new file mode 100644
index 000000000..e3591a582
--- /dev/null
+++ b/drivers/staging/comedi/drivers/cb_pcidas.c
@@ -0,0 +1,1582 @@
+/*
+ comedi/drivers/cb_pcidas.c
+
+ Developed by Ivan Martinez and Frank Mori Hess, with valuable help from
+ David Schleef and the rest of the Comedi developers comunity.
+
+ Copyright (C) 2001-2003 Ivan Martinez <imr@oersted.dtu.dk>
+ Copyright (C) 2001,2002 Frank Mori Hess <fmhess@users.sourceforge.net>
+
+ COMEDI - Linux Control and Measurement Device Interface
+ Copyright (C) 1997-8 David A. Schleef <ds@schleef.org>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+*/
+/*
+Driver: cb_pcidas
+Description: MeasurementComputing PCI-DAS series
+ with the AMCC S5933 PCI controller
+Author: Ivan Martinez <imr@oersted.dtu.dk>,
+ Frank Mori Hess <fmhess@users.sourceforge.net>
+Updated: 2003-3-11
+Devices: [Measurement Computing] PCI-DAS1602/16 (cb_pcidas),
+ PCI-DAS1602/16jr, PCI-DAS1602/12, PCI-DAS1200, PCI-DAS1200jr,
+ PCI-DAS1000, PCI-DAS1001, PCI_DAS1002
+
+Status:
+ There are many reports of the driver being used with most of the
+ supported cards. Despite no detailed log is maintained, it can
+ be said that the driver is quite tested and stable.
+
+ The boards may be autocalibrated using the comedi_calibrate
+ utility.
+
+Configuration options: not applicable, uses PCI auto config
+
+For commands, the scanned channels must be consecutive
+(i.e. 4-5-6-7, 2-3-4,...), and must all have the same
+range and aref.
+
+AI Triggering:
+ For start_src == TRIG_EXT, the A/D EXTERNAL TRIGGER IN (pin 45) is used.
+ For 1602 series, the start_arg is interpreted as follows:
+ start_arg == 0 => gated trigger (level high)
+ start_arg == CR_INVERT => gated trigger (level low)
+ start_arg == CR_EDGE => Rising edge
+ start_arg == CR_EDGE | CR_INVERT => Falling edge
+ For the other boards the trigger will be done on rising edge
+*/
+/*
+
+TODO:
+
+analog triggering on 1602 series
+*/
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+
+#include "../comedi_pci.h"
+
+#include "comedi_8254.h"
+#include "8255.h"
+#include "amcc_s5933.h"
+
+#define AI_BUFFER_SIZE 1024 /* max ai fifo size */
+#define AO_BUFFER_SIZE 1024 /* max ao fifo size */
+#define NUM_CHANNELS_8800 8
+#define NUM_CHANNELS_7376 1
+#define NUM_CHANNELS_8402 2
+#define NUM_CHANNELS_DAC08 1
+
+/* Control/Status registers */
+#define INT_ADCFIFO 0 /* INTERRUPT / ADC FIFO register */
+#define INT_EOS 0x1 /* int end of scan */
+#define INT_FHF 0x2 /* int fifo half full */
+#define INT_FNE 0x3 /* int fifo not empty */
+#define INT_MASK 0x3 /* mask of int select bits */
+#define INTE 0x4 /* int enable */
+#define DAHFIE 0x8 /* dac half full int enable */
+#define EOAIE 0x10 /* end of acq. int enable */
+#define DAHFI 0x20 /* dac half full status / clear */
+#define EOAI 0x40 /* end of acq. int status / clear */
+#define INT 0x80 /* int status / clear */
+#define EOBI 0x200 /* end of burst int status */
+#define ADHFI 0x400 /* half-full int status */
+#define ADNEI 0x800 /* fifo not empty int status (latch) */
+#define ADNE 0x1000 /* fifo not empty status (realtime) */
+#define DAEMIE 0x1000 /* dac empty int enable */
+#define LADFUL 0x2000 /* fifo overflow / clear */
+#define DAEMI 0x4000 /* dac fifo empty int status / clear */
+
+#define ADCMUX_CONT 2 /* ADC CHANNEL MUX AND CONTROL reg */
+#define BEGIN_SCAN(x) ((x) & 0xf)
+#define END_SCAN(x) (((x) & 0xf) << 4)
+#define GAIN_BITS(x) (((x) & 0x3) << 8)
+#define UNIP 0x800 /* Analog front-end unipolar mode */
+#define SE 0x400 /* Inputs in single-ended mode */
+#define PACER_MASK 0x3000 /* pacer source bits */
+#define PACER_INT 0x1000 /* int. pacer */
+#define PACER_EXT_FALL 0x2000 /* ext. falling edge */
+#define PACER_EXT_RISE 0x3000 /* ext. rising edge */
+#define EOC 0x4000 /* adc not busy */
+
+#define TRIG_CONTSTAT 4 /* TRIGGER CONTROL/STATUS register */
+#define SW_TRIGGER 0x1 /* software start trigger */
+#define EXT_TRIGGER 0x2 /* ext. start trigger */
+#define ANALOG_TRIGGER 0x3 /* ext. analog trigger */
+#define TRIGGER_MASK 0x3 /* start trigger mask */
+#define TGPOL 0x04 /* invert trigger (1602 only) */
+#define TGSEL 0x08 /* edge/level trigerred (1602 only) */
+#define TGEN 0x10 /* enable external start trigger */
+#define BURSTE 0x20 /* burst mode enable */
+#define XTRCL 0x80 /* clear external trigger */
+
+#define CALIBRATION_REG 6 /* CALIBRATION register */
+#define SELECT_8800_BIT 0x100 /* select 8800 caldac */
+#define SELECT_TRIMPOT_BIT 0x200 /* select ad7376 trim pot */
+#define SELECT_DAC08_BIT 0x400 /* select dac08 caldac */
+#define CAL_SRC_BITS(x) (((x) & 0x7) << 11)
+#define CAL_EN_BIT 0x4000 /* calibration source enable */
+#define SERIAL_DATA_IN_BIT 0x8000 /* serial data bit going to caldac */
+
+#define DAC_CSR 0x8 /* dac control and status register */
+#define DACEN 0x02 /* dac enable */
+#define DAC_MODE_UPDATE_BOTH 0x80 /* update both dacs */
+
+static inline unsigned int DAC_RANGE(unsigned int channel, unsigned int range)
+{
+ return (range & 0x3) << (8 + 2 * (channel & 0x1));
+}
+
+static inline unsigned int DAC_RANGE_MASK(unsigned int channel)
+{
+ return 0x3 << (8 + 2 * (channel & 0x1));
+};
+
+/* bits for 1602 series only */
+#define DAC_EMPTY 0x1 /* fifo empty, read, write clear */
+#define DAC_START 0x4 /* start/arm fifo operations */
+#define DAC_PACER_MASK 0x18 /* bits that set pacer source */
+#define DAC_PACER_INT 0x8 /* int. pacing */
+#define DAC_PACER_EXT_FALL 0x10 /* ext. pacing, falling edge */
+#define DAC_PACER_EXT_RISE 0x18 /* ext. pacing, rising edge */
+
+static inline unsigned int DAC_CHAN_EN(unsigned int channel)
+{
+ return 1 << (5 + (channel & 0x1)); /* enable channel 0 or 1 */
+};
+
+/* analog input fifo */
+#define ADCDATA 0 /* ADC DATA register */
+#define ADCFIFOCLR 2 /* ADC FIFO CLEAR */
+
+/* pacer, counter, dio registers */
+#define ADC8254 0
+#define DIO_8255 4
+#define DAC8254 8
+
+/* analog output registers for 100x, 1200 series */
+static inline unsigned int DAC_DATA_REG(unsigned int channel)
+{
+ return 2 * (channel & 0x1);
+}
+
+/* analog output registers for 1602 series*/
+#define DACDATA 0 /* DAC DATA register */
+#define DACFIFOCLR 2 /* DAC FIFO CLEAR */
+
+#define IS_UNIPOLAR 0x4 /* unipolar range mask */
+
+/* analog input ranges for most boards */
+static const struct comedi_lrange cb_pcidas_ranges = {
+ 8, {
+ BIP_RANGE(10),
+ BIP_RANGE(5),
+ BIP_RANGE(2.5),
+ BIP_RANGE(1.25),
+ UNI_RANGE(10),
+ UNI_RANGE(5),
+ UNI_RANGE(2.5),
+ UNI_RANGE(1.25)
+ }
+};
+
+/* pci-das1001 input ranges */
+static const struct comedi_lrange cb_pcidas_alt_ranges = {
+ 8, {
+ BIP_RANGE(10),
+ BIP_RANGE(1),
+ BIP_RANGE(0.1),
+ BIP_RANGE(0.01),
+ UNI_RANGE(10),
+ UNI_RANGE(1),
+ UNI_RANGE(0.1),
+ UNI_RANGE(0.01)
+ }
+};
+
+/* analog output ranges */
+static const struct comedi_lrange cb_pcidas_ao_ranges = {
+ 4, {
+ BIP_RANGE(5),
+ BIP_RANGE(10),
+ UNI_RANGE(5),
+ UNI_RANGE(10)
+ }
+};
+
+enum trimpot_model {
+ AD7376,
+ AD8402,
+};
+
+enum cb_pcidas_boardid {
+ BOARD_PCIDAS1602_16,
+ BOARD_PCIDAS1200,
+ BOARD_PCIDAS1602_12,
+ BOARD_PCIDAS1200_JR,
+ BOARD_PCIDAS1602_16_JR,
+ BOARD_PCIDAS1000,
+ BOARD_PCIDAS1001,
+ BOARD_PCIDAS1002,
+};
+
+struct cb_pcidas_board {
+ const char *name;
+ int ai_nchan; /* Inputs in single-ended mode */
+ int ai_bits; /* analog input resolution */
+ int ai_speed; /* fastest conversion period in ns */
+ int ao_nchan; /* number of analog out channels */
+ int has_ao_fifo; /* analog output has fifo */
+ int ao_scan_speed; /* analog output scan speed for 1602 series */
+ int fifo_size; /* number of samples fifo can hold */
+ const struct comedi_lrange *ranges;
+ enum trimpot_model trimpot;
+ unsigned has_dac08:1;
+ unsigned is_1602:1;
+};
+
+static const struct cb_pcidas_board cb_pcidas_boards[] = {
+ [BOARD_PCIDAS1602_16] = {
+ .name = "pci-das1602/16",
+ .ai_nchan = 16,
+ .ai_bits = 16,
+ .ai_speed = 5000,
+ .ao_nchan = 2,
+ .has_ao_fifo = 1,
+ .ao_scan_speed = 10000,
+ .fifo_size = 512,
+ .ranges = &cb_pcidas_ranges,
+ .trimpot = AD8402,
+ .has_dac08 = 1,
+ .is_1602 = 1,
+ },
+ [BOARD_PCIDAS1200] = {
+ .name = "pci-das1200",
+ .ai_nchan = 16,
+ .ai_bits = 12,
+ .ai_speed = 3200,
+ .ao_nchan = 2,
+ .fifo_size = 1024,
+ .ranges = &cb_pcidas_ranges,
+ .trimpot = AD7376,
+ },
+ [BOARD_PCIDAS1602_12] = {
+ .name = "pci-das1602/12",
+ .ai_nchan = 16,
+ .ai_bits = 12,
+ .ai_speed = 3200,
+ .ao_nchan = 2,
+ .has_ao_fifo = 1,
+ .ao_scan_speed = 4000,
+ .fifo_size = 1024,
+ .ranges = &cb_pcidas_ranges,
+ .trimpot = AD7376,
+ .is_1602 = 1,
+ },
+ [BOARD_PCIDAS1200_JR] = {
+ .name = "pci-das1200/jr",
+ .ai_nchan = 16,
+ .ai_bits = 12,
+ .ai_speed = 3200,
+ .fifo_size = 1024,
+ .ranges = &cb_pcidas_ranges,
+ .trimpot = AD7376,
+ },
+ [BOARD_PCIDAS1602_16_JR] = {
+ .name = "pci-das1602/16/jr",
+ .ai_nchan = 16,
+ .ai_bits = 16,
+ .ai_speed = 5000,
+ .fifo_size = 512,
+ .ranges = &cb_pcidas_ranges,
+ .trimpot = AD8402,
+ .has_dac08 = 1,
+ .is_1602 = 1,
+ },
+ [BOARD_PCIDAS1000] = {
+ .name = "pci-das1000",
+ .ai_nchan = 16,
+ .ai_bits = 12,
+ .ai_speed = 4000,
+ .fifo_size = 1024,
+ .ranges = &cb_pcidas_ranges,
+ .trimpot = AD7376,
+ },
+ [BOARD_PCIDAS1001] = {
+ .name = "pci-das1001",
+ .ai_nchan = 16,
+ .ai_bits = 12,
+ .ai_speed = 6800,
+ .ao_nchan = 2,
+ .fifo_size = 1024,
+ .ranges = &cb_pcidas_alt_ranges,
+ .trimpot = AD7376,
+ },
+ [BOARD_PCIDAS1002] = {
+ .name = "pci-das1002",
+ .ai_nchan = 16,
+ .ai_bits = 12,
+ .ai_speed = 6800,
+ .ao_nchan = 2,
+ .fifo_size = 1024,
+ .ranges = &cb_pcidas_ranges,
+ .trimpot = AD7376,
+ },
+};
+
+struct cb_pcidas_private {
+ struct comedi_8254 *ao_pacer;
+ /* base addresses */
+ unsigned long s5933_config;
+ unsigned long control_status;
+ unsigned long adc_fifo;
+ unsigned long ao_registers;
+ /* bits to write to registers */
+ unsigned int adc_fifo_bits;
+ unsigned int s5933_intcsr_bits;
+ unsigned int ao_control_bits;
+ /* fifo buffers */
+ unsigned short ai_buffer[AI_BUFFER_SIZE];
+ unsigned short ao_buffer[AO_BUFFER_SIZE];
+ unsigned int calibration_source;
+};
+
+static inline unsigned int cal_enable_bits(struct comedi_device *dev)
+{
+ struct cb_pcidas_private *devpriv = dev->private;
+
+ return CAL_EN_BIT | CAL_SRC_BITS(devpriv->calibration_source);
+}
+
+static int cb_pcidas_ai_eoc(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned long context)
+{
+ struct cb_pcidas_private *devpriv = dev->private;
+ unsigned int status;
+
+ status = inw(devpriv->control_status + ADCMUX_CONT);
+ if (status & EOC)
+ return 0;
+ return -EBUSY;
+}
+
+static int cb_pcidas_ai_rinsn(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn, unsigned int *data)
+{
+ struct cb_pcidas_private *devpriv = dev->private;
+ unsigned int chan = CR_CHAN(insn->chanspec);
+ unsigned int range = CR_RANGE(insn->chanspec);
+ unsigned int aref = CR_AREF(insn->chanspec);
+ unsigned int bits;
+ int ret;
+ int n;
+
+ /* enable calibration input if appropriate */
+ if (insn->chanspec & CR_ALT_SOURCE) {
+ outw(cal_enable_bits(dev),
+ devpriv->control_status + CALIBRATION_REG);
+ chan = 0;
+ } else {
+ outw(0, devpriv->control_status + CALIBRATION_REG);
+ }
+
+ /* set mux limits and gain */
+ bits = BEGIN_SCAN(chan) | END_SCAN(chan) | GAIN_BITS(range);
+ /* set unipolar/bipolar */
+ if (range & IS_UNIPOLAR)
+ bits |= UNIP;
+ /* set single-ended/differential */
+ if (aref != AREF_DIFF)
+ bits |= SE;
+ outw(bits, devpriv->control_status + ADCMUX_CONT);
+
+ /* clear fifo */
+ outw(0, devpriv->adc_fifo + ADCFIFOCLR);
+
+ /* convert n samples */
+ for (n = 0; n < insn->n; n++) {
+ /* trigger conversion */
+ outw(0, devpriv->adc_fifo + ADCDATA);
+
+ /* wait for conversion to end */
+ ret = comedi_timeout(dev, s, insn, cb_pcidas_ai_eoc, 0);
+ if (ret)
+ return ret;
+
+ /* read data */
+ data[n] = inw(devpriv->adc_fifo + ADCDATA);
+ }
+
+ /* return the number of samples read/written */
+ return n;
+}
+
+static int ai_config_insn(struct comedi_device *dev, struct comedi_subdevice *s,
+ struct comedi_insn *insn, unsigned int *data)
+{
+ struct cb_pcidas_private *devpriv = dev->private;
+ int id = data[0];
+ unsigned int source = data[1];
+
+ switch (id) {
+ case INSN_CONFIG_ALT_SOURCE:
+ if (source >= 8) {
+ dev_err(dev->class_dev,
+ "invalid calibration source: %i\n",
+ source);
+ return -EINVAL;
+ }
+ devpriv->calibration_source = source;
+ break;
+ default:
+ return -EINVAL;
+ }
+ return insn->n;
+}
+
+/* analog output insn for pcidas-1000 and 1200 series */
+static int cb_pcidas_ao_nofifo_winsn(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ struct cb_pcidas_private *devpriv = dev->private;
+ unsigned int chan = CR_CHAN(insn->chanspec);
+ unsigned int range = CR_RANGE(insn->chanspec);
+ unsigned long flags;
+
+ /* set channel and range */
+ spin_lock_irqsave(&dev->spinlock, flags);
+ devpriv->ao_control_bits &= (~DAC_MODE_UPDATE_BOTH &
+ ~DAC_RANGE_MASK(chan));
+ devpriv->ao_control_bits |= (DACEN | DAC_RANGE(chan, range));
+ outw(devpriv->ao_control_bits, devpriv->control_status + DAC_CSR);
+ spin_unlock_irqrestore(&dev->spinlock, flags);
+
+ /* remember value for readback */
+ s->readback[chan] = data[0];
+
+ /* send data */
+ outw(data[0], devpriv->ao_registers + DAC_DATA_REG(chan));
+
+ return insn->n;
+}
+
+/* analog output insn for pcidas-1602 series */
+static int cb_pcidas_ao_fifo_winsn(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn, unsigned int *data)
+{
+ struct cb_pcidas_private *devpriv = dev->private;
+ unsigned int chan = CR_CHAN(insn->chanspec);
+ unsigned int range = CR_RANGE(insn->chanspec);
+ unsigned long flags;
+
+ /* clear dac fifo */
+ outw(0, devpriv->ao_registers + DACFIFOCLR);
+
+ /* set channel and range */
+ spin_lock_irqsave(&dev->spinlock, flags);
+ devpriv->ao_control_bits &= (~DAC_CHAN_EN(0) & ~DAC_CHAN_EN(1) &
+ ~DAC_RANGE_MASK(chan) & ~DAC_PACER_MASK);
+ devpriv->ao_control_bits |= (DACEN | DAC_RANGE(chan, range) |
+ DAC_CHAN_EN(chan) | DAC_START);
+ outw(devpriv->ao_control_bits, devpriv->control_status + DAC_CSR);
+ spin_unlock_irqrestore(&dev->spinlock, flags);
+
+ /* remember value for readback */
+ s->readback[chan] = data[0];
+
+ /* send data */
+ outw(data[0], devpriv->ao_registers + DACDATA);
+
+ return insn->n;
+}
+
+static int wait_for_nvram_ready(unsigned long s5933_base_addr)
+{
+ static const int timeout = 1000;
+ unsigned int i;
+
+ for (i = 0; i < timeout; i++) {
+ if ((inb(s5933_base_addr +
+ AMCC_OP_REG_MCSR_NVCMD) & MCSR_NV_BUSY)
+ == 0)
+ return 0;
+ udelay(1);
+ }
+ return -1;
+}
+
+static int nvram_read(struct comedi_device *dev, unsigned int address,
+ uint8_t *data)
+{
+ struct cb_pcidas_private *devpriv = dev->private;
+ unsigned long iobase = devpriv->s5933_config;
+
+ if (wait_for_nvram_ready(iobase) < 0)
+ return -ETIMEDOUT;
+
+ outb(MCSR_NV_ENABLE | MCSR_NV_LOAD_LOW_ADDR,
+ iobase + AMCC_OP_REG_MCSR_NVCMD);
+ outb(address & 0xff, iobase + AMCC_OP_REG_MCSR_NVDATA);
+ outb(MCSR_NV_ENABLE | MCSR_NV_LOAD_HIGH_ADDR,
+ iobase + AMCC_OP_REG_MCSR_NVCMD);
+ outb((address >> 8) & 0xff, iobase + AMCC_OP_REG_MCSR_NVDATA);
+ outb(MCSR_NV_ENABLE | MCSR_NV_READ, iobase + AMCC_OP_REG_MCSR_NVCMD);
+
+ if (wait_for_nvram_ready(iobase) < 0)
+ return -ETIMEDOUT;
+
+ *data = inb(iobase + AMCC_OP_REG_MCSR_NVDATA);
+
+ return 0;
+}
+
+static int eeprom_read_insn(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn, unsigned int *data)
+{
+ uint8_t nvram_data;
+ int retval;
+
+ retval = nvram_read(dev, CR_CHAN(insn->chanspec), &nvram_data);
+ if (retval < 0)
+ return retval;
+
+ data[0] = nvram_data;
+
+ return 1;
+}
+
+static void write_calibration_bitstream(struct comedi_device *dev,
+ unsigned int register_bits,
+ unsigned int bitstream,
+ unsigned int bitstream_length)
+{
+ struct cb_pcidas_private *devpriv = dev->private;
+ static const int write_delay = 1;
+ unsigned int bit;
+
+ for (bit = 1 << (bitstream_length - 1); bit; bit >>= 1) {
+ if (bitstream & bit)
+ register_bits |= SERIAL_DATA_IN_BIT;
+ else
+ register_bits &= ~SERIAL_DATA_IN_BIT;
+ udelay(write_delay);
+ outw(register_bits, devpriv->control_status + CALIBRATION_REG);
+ }
+}
+
+static void caldac_8800_write(struct comedi_device *dev,
+ unsigned int chan, uint8_t val)
+{
+ struct cb_pcidas_private *devpriv = dev->private;
+ static const int bitstream_length = 11;
+ unsigned int bitstream = ((chan & 0x7) << 8) | val;
+ static const int caldac_8800_udelay = 1;
+
+ write_calibration_bitstream(dev, cal_enable_bits(dev), bitstream,
+ bitstream_length);
+
+ udelay(caldac_8800_udelay);
+ outw(cal_enable_bits(dev) | SELECT_8800_BIT,
+ devpriv->control_status + CALIBRATION_REG);
+ udelay(caldac_8800_udelay);
+ outw(cal_enable_bits(dev), devpriv->control_status + CALIBRATION_REG);
+}
+
+static int cb_pcidas_caldac_insn_write(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ unsigned int chan = CR_CHAN(insn->chanspec);
+
+ if (insn->n) {
+ unsigned int val = data[insn->n - 1];
+
+ if (s->readback[chan] != val) {
+ caldac_8800_write(dev, chan, val);
+ s->readback[chan] = val;
+ }
+ }
+
+ return insn->n;
+}
+
+/* 1602/16 pregain offset */
+static void dac08_write(struct comedi_device *dev, unsigned int value)
+{
+ struct cb_pcidas_private *devpriv = dev->private;
+
+ value &= 0xff;
+ value |= cal_enable_bits(dev);
+
+ /* latch the new value into the caldac */
+ outw(value, devpriv->control_status + CALIBRATION_REG);
+ udelay(1);
+ outw(value | SELECT_DAC08_BIT,
+ devpriv->control_status + CALIBRATION_REG);
+ udelay(1);
+ outw(value, devpriv->control_status + CALIBRATION_REG);
+ udelay(1);
+}
+
+static int cb_pcidas_dac08_insn_write(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ unsigned int chan = CR_CHAN(insn->chanspec);
+
+ if (insn->n) {
+ unsigned int val = data[insn->n - 1];
+
+ if (s->readback[chan] != val) {
+ dac08_write(dev, val);
+ s->readback[chan] = val;
+ }
+ }
+
+ return insn->n;
+}
+
+static int trimpot_7376_write(struct comedi_device *dev, uint8_t value)
+{
+ struct cb_pcidas_private *devpriv = dev->private;
+ static const int bitstream_length = 7;
+ unsigned int bitstream = value & 0x7f;
+ unsigned int register_bits;
+ static const int ad7376_udelay = 1;
+
+ register_bits = cal_enable_bits(dev) | SELECT_TRIMPOT_BIT;
+ udelay(ad7376_udelay);
+ outw(register_bits, devpriv->control_status + CALIBRATION_REG);
+
+ write_calibration_bitstream(dev, register_bits, bitstream,
+ bitstream_length);
+
+ udelay(ad7376_udelay);
+ outw(cal_enable_bits(dev), devpriv->control_status + CALIBRATION_REG);
+
+ return 0;
+}
+
+/* For 1602/16 only
+ * ch 0 : adc gain
+ * ch 1 : adc postgain offset */
+static int trimpot_8402_write(struct comedi_device *dev, unsigned int channel,
+ uint8_t value)
+{
+ struct cb_pcidas_private *devpriv = dev->private;
+ static const int bitstream_length = 10;
+ unsigned int bitstream = ((channel & 0x3) << 8) | (value & 0xff);
+ unsigned int register_bits;
+ static const int ad8402_udelay = 1;
+
+ register_bits = cal_enable_bits(dev) | SELECT_TRIMPOT_BIT;
+ udelay(ad8402_udelay);
+ outw(register_bits, devpriv->control_status + CALIBRATION_REG);
+
+ write_calibration_bitstream(dev, register_bits, bitstream,
+ bitstream_length);
+
+ udelay(ad8402_udelay);
+ outw(cal_enable_bits(dev), devpriv->control_status + CALIBRATION_REG);
+
+ return 0;
+}
+
+static void cb_pcidas_trimpot_write(struct comedi_device *dev,
+ unsigned int chan, unsigned int val)
+{
+ const struct cb_pcidas_board *thisboard = dev->board_ptr;
+
+ switch (thisboard->trimpot) {
+ case AD7376:
+ trimpot_7376_write(dev, val);
+ break;
+ case AD8402:
+ trimpot_8402_write(dev, chan, val);
+ break;
+ default:
+ dev_err(dev->class_dev, "driver bug?\n");
+ break;
+ }
+}
+
+static int cb_pcidas_trimpot_insn_write(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ unsigned int chan = CR_CHAN(insn->chanspec);
+
+ if (insn->n) {
+ unsigned int val = data[insn->n - 1];
+
+ if (s->readback[chan] != val) {
+ cb_pcidas_trimpot_write(dev, chan, val);
+ s->readback[chan] = val;
+ }
+ }
+
+ return insn->n;
+}
+
+static int cb_pcidas_ai_check_chanlist(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_cmd *cmd)
+{
+ unsigned int chan0 = CR_CHAN(cmd->chanlist[0]);
+ unsigned int range0 = CR_RANGE(cmd->chanlist[0]);
+ int i;
+
+ for (i = 1; i < cmd->chanlist_len; i++) {
+ unsigned int chan = CR_CHAN(cmd->chanlist[i]);
+ unsigned int range = CR_RANGE(cmd->chanlist[i]);
+
+ if (chan != (chan0 + i) % s->n_chan) {
+ dev_dbg(dev->class_dev,
+ "entries in chanlist must be consecutive channels, counting upwards\n");
+ return -EINVAL;
+ }
+
+ if (range != range0) {
+ dev_dbg(dev->class_dev,
+ "entries in chanlist must all have the same gain\n");
+ return -EINVAL;
+ }
+ }
+ return 0;
+}
+
+static int cb_pcidas_ai_cmdtest(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_cmd *cmd)
+{
+ const struct cb_pcidas_board *thisboard = dev->board_ptr;
+ int err = 0;
+ unsigned int arg;
+
+ /* Step 1 : check if triggers are trivially valid */
+
+ err |= comedi_check_trigger_src(&cmd->start_src, TRIG_NOW | TRIG_EXT);
+ err |= comedi_check_trigger_src(&cmd->scan_begin_src,
+ TRIG_FOLLOW | TRIG_TIMER | TRIG_EXT);
+ err |= comedi_check_trigger_src(&cmd->convert_src,
+ TRIG_TIMER | TRIG_NOW | TRIG_EXT);
+ err |= comedi_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
+ err |= comedi_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE);
+
+ if (err)
+ return 1;
+
+ /* Step 2a : make sure trigger sources are unique */
+
+ err |= comedi_check_trigger_is_unique(cmd->start_src);
+ err |= comedi_check_trigger_is_unique(cmd->scan_begin_src);
+ err |= comedi_check_trigger_is_unique(cmd->convert_src);
+ err |= comedi_check_trigger_is_unique(cmd->stop_src);
+
+ /* Step 2b : and mutually compatible */
+
+ if (cmd->scan_begin_src == TRIG_FOLLOW && cmd->convert_src == TRIG_NOW)
+ err |= -EINVAL;
+ if (cmd->scan_begin_src != TRIG_FOLLOW && cmd->convert_src != TRIG_NOW)
+ err |= -EINVAL;
+ if (cmd->start_src == TRIG_EXT &&
+ (cmd->convert_src == TRIG_EXT || cmd->scan_begin_src == TRIG_EXT))
+ err |= -EINVAL;
+
+ if (err)
+ return 2;
+
+ /* Step 3: check if arguments are trivially valid */
+
+ switch (cmd->start_src) {
+ case TRIG_NOW:
+ err |= comedi_check_trigger_arg_is(&cmd->start_arg, 0);
+ break;
+ case TRIG_EXT:
+ /* External trigger, only CR_EDGE and CR_INVERT flags allowed */
+ if ((cmd->start_arg
+ & (CR_FLAGS_MASK & ~(CR_EDGE | CR_INVERT))) != 0) {
+ cmd->start_arg &= ~(CR_FLAGS_MASK &
+ ~(CR_EDGE | CR_INVERT));
+ err |= -EINVAL;
+ }
+ if (!thisboard->is_1602 && (cmd->start_arg & CR_INVERT)) {
+ cmd->start_arg &= (CR_FLAGS_MASK & ~CR_INVERT);
+ err |= -EINVAL;
+ }
+ break;
+ }
+
+ if (cmd->scan_begin_src == TRIG_TIMER) {
+ err |= comedi_check_trigger_arg_min(&cmd->scan_begin_arg,
+ thisboard->ai_speed *
+ cmd->chanlist_len);
+ }
+
+ if (cmd->convert_src == TRIG_TIMER) {
+ err |= comedi_check_trigger_arg_min(&cmd->convert_arg,
+ thisboard->ai_speed);
+ }
+
+ err |= comedi_check_trigger_arg_is(&cmd->scan_end_arg,
+ cmd->chanlist_len);
+
+ if (cmd->stop_src == TRIG_COUNT)
+ err |= comedi_check_trigger_arg_min(&cmd->stop_arg, 1);
+ else /* TRIG_NONE */
+ err |= comedi_check_trigger_arg_is(&cmd->stop_arg, 0);
+
+ if (err)
+ return 3;
+
+ /* step 4: fix up any arguments */
+
+ if (cmd->scan_begin_src == TRIG_TIMER) {
+ arg = cmd->scan_begin_arg;
+ comedi_8254_cascade_ns_to_timer(dev->pacer, &arg, cmd->flags);
+ err |= comedi_check_trigger_arg_is(&cmd->scan_begin_arg, arg);
+ }
+ if (cmd->convert_src == TRIG_TIMER) {
+ arg = cmd->convert_arg;
+ comedi_8254_cascade_ns_to_timer(dev->pacer, &arg, cmd->flags);
+ err |= comedi_check_trigger_arg_is(&cmd->convert_arg, arg);
+ }
+
+ if (err)
+ return 4;
+
+ /* Step 5: check channel list if it exists */
+ if (cmd->chanlist && cmd->chanlist_len > 0)
+ err |= cb_pcidas_ai_check_chanlist(dev, s, cmd);
+
+ if (err)
+ return 5;
+
+ return 0;
+}
+
+static int cb_pcidas_ai_cmd(struct comedi_device *dev,
+ struct comedi_subdevice *s)
+{
+ const struct cb_pcidas_board *thisboard = dev->board_ptr;
+ struct cb_pcidas_private *devpriv = dev->private;
+ struct comedi_async *async = s->async;
+ struct comedi_cmd *cmd = &async->cmd;
+ unsigned int bits;
+ unsigned long flags;
+
+ /* make sure CAL_EN_BIT is disabled */
+ outw(0, devpriv->control_status + CALIBRATION_REG);
+ /* initialize before settings pacer source and count values */
+ outw(0, devpriv->control_status + TRIG_CONTSTAT);
+ /* clear fifo */
+ outw(0, devpriv->adc_fifo + ADCFIFOCLR);
+
+ /* set mux limits, gain and pacer source */
+ bits = BEGIN_SCAN(CR_CHAN(cmd->chanlist[0])) |
+ END_SCAN(CR_CHAN(cmd->chanlist[cmd->chanlist_len - 1])) |
+ GAIN_BITS(CR_RANGE(cmd->chanlist[0]));
+ /* set unipolar/bipolar */
+ if (CR_RANGE(cmd->chanlist[0]) & IS_UNIPOLAR)
+ bits |= UNIP;
+ /* set singleended/differential */
+ if (CR_AREF(cmd->chanlist[0]) != AREF_DIFF)
+ bits |= SE;
+ /* set pacer source */
+ if (cmd->convert_src == TRIG_EXT || cmd->scan_begin_src == TRIG_EXT)
+ bits |= PACER_EXT_RISE;
+ else
+ bits |= PACER_INT;
+ outw(bits, devpriv->control_status + ADCMUX_CONT);
+
+ /* load counters */
+ if (cmd->scan_begin_src == TRIG_TIMER ||
+ cmd->convert_src == TRIG_TIMER) {
+ comedi_8254_update_divisors(dev->pacer);
+ comedi_8254_pacer_enable(dev->pacer, 1, 2, true);
+ }
+
+ /* enable interrupts */
+ spin_lock_irqsave(&dev->spinlock, flags);
+ devpriv->adc_fifo_bits |= INTE;
+ devpriv->adc_fifo_bits &= ~INT_MASK;
+ if (cmd->flags & CMDF_WAKE_EOS) {
+ if (cmd->convert_src == TRIG_NOW && cmd->chanlist_len > 1) {
+ /* interrupt end of burst */
+ devpriv->adc_fifo_bits |= INT_EOS;
+ } else {
+ /* interrupt fifo not empty */
+ devpriv->adc_fifo_bits |= INT_FNE;
+ }
+ } else {
+ /* interrupt fifo half full */
+ devpriv->adc_fifo_bits |= INT_FHF;
+ }
+
+ /* enable (and clear) interrupts */
+ outw(devpriv->adc_fifo_bits | EOAI | INT | LADFUL,
+ devpriv->control_status + INT_ADCFIFO);
+ spin_unlock_irqrestore(&dev->spinlock, flags);
+
+ /* set start trigger and burst mode */
+ bits = 0;
+ if (cmd->start_src == TRIG_NOW) {
+ bits |= SW_TRIGGER;
+ } else { /* TRIG_EXT */
+ bits |= EXT_TRIGGER | TGEN | XTRCL;
+ if (thisboard->is_1602) {
+ if (cmd->start_arg & CR_INVERT)
+ bits |= TGPOL;
+ if (cmd->start_arg & CR_EDGE)
+ bits |= TGSEL;
+ }
+ }
+ if (cmd->convert_src == TRIG_NOW && cmd->chanlist_len > 1)
+ bits |= BURSTE;
+ outw(bits, devpriv->control_status + TRIG_CONTSTAT);
+
+ return 0;
+}
+
+static int cb_pcidas_ao_check_chanlist(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_cmd *cmd)
+{
+ unsigned int chan0 = CR_CHAN(cmd->chanlist[0]);
+
+ if (cmd->chanlist_len > 1) {
+ unsigned int chan1 = CR_CHAN(cmd->chanlist[1]);
+
+ if (chan0 != 0 || chan1 != 1) {
+ dev_dbg(dev->class_dev,
+ "channels must be ordered channel 0, channel 1 in chanlist\n");
+ return -EINVAL;
+ }
+ }
+
+ return 0;
+}
+
+static int cb_pcidas_ao_cmdtest(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_cmd *cmd)
+{
+ const struct cb_pcidas_board *thisboard = dev->board_ptr;
+ struct cb_pcidas_private *devpriv = dev->private;
+ int err = 0;
+
+ /* Step 1 : check if triggers are trivially valid */
+
+ err |= comedi_check_trigger_src(&cmd->start_src, TRIG_INT);
+ err |= comedi_check_trigger_src(&cmd->scan_begin_src,
+ TRIG_TIMER | TRIG_EXT);
+ err |= comedi_check_trigger_src(&cmd->convert_src, TRIG_NOW);
+ err |= comedi_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
+ err |= comedi_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE);
+
+ if (err)
+ return 1;
+
+ /* Step 2a : make sure trigger sources are unique */
+
+ err |= comedi_check_trigger_is_unique(cmd->scan_begin_src);
+ err |= comedi_check_trigger_is_unique(cmd->stop_src);
+
+ /* Step 2b : and mutually compatible */
+
+ if (err)
+ return 2;
+
+ /* Step 3: check if arguments are trivially valid */
+
+ err |= comedi_check_trigger_arg_is(&cmd->start_arg, 0);
+
+ if (cmd->scan_begin_src == TRIG_TIMER) {
+ err |= comedi_check_trigger_arg_min(&cmd->scan_begin_arg,
+ thisboard->ao_scan_speed);
+ }
+
+ err |= comedi_check_trigger_arg_is(&cmd->scan_end_arg,
+ cmd->chanlist_len);
+
+ if (cmd->stop_src == TRIG_COUNT)
+ err |= comedi_check_trigger_arg_min(&cmd->stop_arg, 1);
+ else /* TRIG_NONE */
+ err |= comedi_check_trigger_arg_is(&cmd->stop_arg, 0);
+
+ if (err)
+ return 3;
+
+ /* step 4: fix up any arguments */
+
+ if (cmd->scan_begin_src == TRIG_TIMER) {
+ unsigned int arg = cmd->scan_begin_arg;
+
+ comedi_8254_cascade_ns_to_timer(devpriv->ao_pacer,
+ &arg, cmd->flags);
+ err |= comedi_check_trigger_arg_is(&cmd->scan_begin_arg, arg);
+ }
+
+ if (err)
+ return 4;
+
+ /* Step 5: check channel list if it exists */
+ if (cmd->chanlist && cmd->chanlist_len > 0)
+ err |= cb_pcidas_ao_check_chanlist(dev, s, cmd);
+
+ if (err)
+ return 5;
+
+ return 0;
+}
+
+/* cancel analog input command */
+static int cb_pcidas_cancel(struct comedi_device *dev,
+ struct comedi_subdevice *s)
+{
+ struct cb_pcidas_private *devpriv = dev->private;
+ unsigned long flags;
+
+ spin_lock_irqsave(&dev->spinlock, flags);
+ /* disable interrupts */
+ devpriv->adc_fifo_bits &= ~INTE & ~EOAIE;
+ outw(devpriv->adc_fifo_bits, devpriv->control_status + INT_ADCFIFO);
+ spin_unlock_irqrestore(&dev->spinlock, flags);
+
+ /* disable start trigger source and burst mode */
+ outw(0, devpriv->control_status + TRIG_CONTSTAT);
+ /* software pacer source */
+ outw(0, devpriv->control_status + ADCMUX_CONT);
+
+ return 0;
+}
+
+static void cb_pcidas_ao_load_fifo(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ unsigned int nsamples)
+{
+ struct cb_pcidas_private *devpriv = dev->private;
+ unsigned int nbytes;
+
+ nsamples = comedi_nsamples_left(s, nsamples);
+ nbytes = comedi_buf_read_samples(s, devpriv->ao_buffer, nsamples);
+
+ nsamples = comedi_bytes_to_samples(s, nbytes);
+ outsw(devpriv->ao_registers + DACDATA, devpriv->ao_buffer, nsamples);
+}
+
+static int cb_pcidas_ao_inttrig(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ unsigned int trig_num)
+{
+ const struct cb_pcidas_board *thisboard = dev->board_ptr;
+ struct cb_pcidas_private *devpriv = dev->private;
+ struct comedi_async *async = s->async;
+ struct comedi_cmd *cmd = &async->cmd;
+ unsigned long flags;
+
+ if (trig_num != cmd->start_arg)
+ return -EINVAL;
+
+ cb_pcidas_ao_load_fifo(dev, s, thisboard->fifo_size);
+
+ /* enable dac half-full and empty interrupts */
+ spin_lock_irqsave(&dev->spinlock, flags);
+ devpriv->adc_fifo_bits |= DAEMIE | DAHFIE;
+
+ /* enable and clear interrupts */
+ outw(devpriv->adc_fifo_bits | DAEMI | DAHFI,
+ devpriv->control_status + INT_ADCFIFO);
+
+ /* start dac */
+ devpriv->ao_control_bits |= DAC_START | DACEN | DAC_EMPTY;
+ outw(devpriv->ao_control_bits, devpriv->control_status + DAC_CSR);
+
+ spin_unlock_irqrestore(&dev->spinlock, flags);
+
+ async->inttrig = NULL;
+
+ return 0;
+}
+
+static int cb_pcidas_ao_cmd(struct comedi_device *dev,
+ struct comedi_subdevice *s)
+{
+ struct cb_pcidas_private *devpriv = dev->private;
+ struct comedi_async *async = s->async;
+ struct comedi_cmd *cmd = &async->cmd;
+ unsigned int i;
+ unsigned long flags;
+
+ /* set channel limits, gain */
+ spin_lock_irqsave(&dev->spinlock, flags);
+ for (i = 0; i < cmd->chanlist_len; i++) {
+ /* enable channel */
+ devpriv->ao_control_bits |=
+ DAC_CHAN_EN(CR_CHAN(cmd->chanlist[i]));
+ /* set range */
+ devpriv->ao_control_bits |= DAC_RANGE(CR_CHAN(cmd->chanlist[i]),
+ CR_RANGE(cmd->
+ chanlist[i]));
+ }
+
+ /* disable analog out before settings pacer source and count values */
+ outw(devpriv->ao_control_bits, devpriv->control_status + DAC_CSR);
+ spin_unlock_irqrestore(&dev->spinlock, flags);
+
+ /* clear fifo */
+ outw(0, devpriv->ao_registers + DACFIFOCLR);
+
+ /* load counters */
+ if (cmd->scan_begin_src == TRIG_TIMER) {
+ comedi_8254_update_divisors(devpriv->ao_pacer);
+ comedi_8254_pacer_enable(devpriv->ao_pacer, 1, 2, true);
+ }
+
+ /* set pacer source */
+ spin_lock_irqsave(&dev->spinlock, flags);
+ switch (cmd->scan_begin_src) {
+ case TRIG_TIMER:
+ devpriv->ao_control_bits |= DAC_PACER_INT;
+ break;
+ case TRIG_EXT:
+ devpriv->ao_control_bits |= DAC_PACER_EXT_RISE;
+ break;
+ default:
+ spin_unlock_irqrestore(&dev->spinlock, flags);
+ dev_err(dev->class_dev, "error setting dac pacer source\n");
+ return -1;
+ }
+ spin_unlock_irqrestore(&dev->spinlock, flags);
+
+ async->inttrig = cb_pcidas_ao_inttrig;
+
+ return 0;
+}
+
+/* cancel analog output command */
+static int cb_pcidas_ao_cancel(struct comedi_device *dev,
+ struct comedi_subdevice *s)
+{
+ struct cb_pcidas_private *devpriv = dev->private;
+ unsigned long flags;
+
+ spin_lock_irqsave(&dev->spinlock, flags);
+ /* disable interrupts */
+ devpriv->adc_fifo_bits &= ~DAHFIE & ~DAEMIE;
+ outw(devpriv->adc_fifo_bits, devpriv->control_status + INT_ADCFIFO);
+
+ /* disable output */
+ devpriv->ao_control_bits &= ~DACEN & ~DAC_PACER_MASK;
+ outw(devpriv->ao_control_bits, devpriv->control_status + DAC_CSR);
+ spin_unlock_irqrestore(&dev->spinlock, flags);
+
+ return 0;
+}
+
+static void handle_ao_interrupt(struct comedi_device *dev, unsigned int status)
+{
+ const struct cb_pcidas_board *thisboard = dev->board_ptr;
+ struct cb_pcidas_private *devpriv = dev->private;
+ struct comedi_subdevice *s = dev->write_subdev;
+ struct comedi_async *async = s->async;
+ struct comedi_cmd *cmd = &async->cmd;
+ unsigned long flags;
+
+ if (status & DAEMI) {
+ /* clear dac empty interrupt latch */
+ spin_lock_irqsave(&dev->spinlock, flags);
+ outw(devpriv->adc_fifo_bits | DAEMI,
+ devpriv->control_status + INT_ADCFIFO);
+ spin_unlock_irqrestore(&dev->spinlock, flags);
+ if (inw(devpriv->ao_registers + DAC_CSR) & DAC_EMPTY) {
+ if (cmd->stop_src == TRIG_COUNT &&
+ async->scans_done >= cmd->stop_arg) {
+ async->events |= COMEDI_CB_EOA;
+ } else {
+ dev_err(dev->class_dev, "dac fifo underflow\n");
+ async->events |= COMEDI_CB_ERROR;
+ }
+ }
+ } else if (status & DAHFI) {
+ cb_pcidas_ao_load_fifo(dev, s, thisboard->fifo_size / 2);
+
+ /* clear half-full interrupt latch */
+ spin_lock_irqsave(&dev->spinlock, flags);
+ outw(devpriv->adc_fifo_bits | DAHFI,
+ devpriv->control_status + INT_ADCFIFO);
+ spin_unlock_irqrestore(&dev->spinlock, flags);
+ }
+
+ comedi_handle_events(dev, s);
+}
+
+static irqreturn_t cb_pcidas_interrupt(int irq, void *d)
+{
+ struct comedi_device *dev = (struct comedi_device *)d;
+ const struct cb_pcidas_board *thisboard = dev->board_ptr;
+ struct cb_pcidas_private *devpriv = dev->private;
+ struct comedi_subdevice *s = dev->read_subdev;
+ struct comedi_async *async;
+ struct comedi_cmd *cmd;
+ int status, s5933_status;
+ int half_fifo = thisboard->fifo_size / 2;
+ unsigned int num_samples, i;
+ static const int timeout = 10000;
+ unsigned long flags;
+
+ if (!dev->attached)
+ return IRQ_NONE;
+
+ async = s->async;
+ cmd = &async->cmd;
+
+ s5933_status = inl(devpriv->s5933_config + AMCC_OP_REG_INTCSR);
+
+ if ((INTCSR_INTR_ASSERTED & s5933_status) == 0)
+ return IRQ_NONE;
+
+ /* make sure mailbox 4 is empty */
+ inl_p(devpriv->s5933_config + AMCC_OP_REG_IMB4);
+ /* clear interrupt on amcc s5933 */
+ outl(devpriv->s5933_intcsr_bits | INTCSR_INBOX_INTR_STATUS,
+ devpriv->s5933_config + AMCC_OP_REG_INTCSR);
+
+ status = inw(devpriv->control_status + INT_ADCFIFO);
+
+ /* check for analog output interrupt */
+ if (status & (DAHFI | DAEMI))
+ handle_ao_interrupt(dev, status);
+ /* check for analog input interrupts */
+ /* if fifo half-full */
+ if (status & ADHFI) {
+ /* read data */
+ num_samples = comedi_nsamples_left(s, half_fifo);
+ insw(devpriv->adc_fifo + ADCDATA, devpriv->ai_buffer,
+ num_samples);
+ comedi_buf_write_samples(s, devpriv->ai_buffer, num_samples);
+
+ if (cmd->stop_src == TRIG_COUNT &&
+ async->scans_done >= cmd->stop_arg)
+ async->events |= COMEDI_CB_EOA;
+
+ /* clear half-full interrupt latch */
+ spin_lock_irqsave(&dev->spinlock, flags);
+ outw(devpriv->adc_fifo_bits | INT,
+ devpriv->control_status + INT_ADCFIFO);
+ spin_unlock_irqrestore(&dev->spinlock, flags);
+ /* else if fifo not empty */
+ } else if (status & (ADNEI | EOBI)) {
+ for (i = 0; i < timeout; i++) {
+ unsigned short val;
+
+ /* break if fifo is empty */
+ if ((ADNE & inw(devpriv->control_status +
+ INT_ADCFIFO)) == 0)
+ break;
+ val = inw(devpriv->adc_fifo);
+ comedi_buf_write_samples(s, &val, 1);
+
+ if (cmd->stop_src == TRIG_COUNT &&
+ async->scans_done >= cmd->stop_arg) {
+ async->events |= COMEDI_CB_EOA;
+ break;
+ }
+ }
+ /* clear not-empty interrupt latch */
+ spin_lock_irqsave(&dev->spinlock, flags);
+ outw(devpriv->adc_fifo_bits | INT,
+ devpriv->control_status + INT_ADCFIFO);
+ spin_unlock_irqrestore(&dev->spinlock, flags);
+ } else if (status & EOAI) {
+ dev_err(dev->class_dev,
+ "bug! encountered end of acquisition interrupt?\n");
+ /* clear EOA interrupt latch */
+ spin_lock_irqsave(&dev->spinlock, flags);
+ outw(devpriv->adc_fifo_bits | EOAI,
+ devpriv->control_status + INT_ADCFIFO);
+ spin_unlock_irqrestore(&dev->spinlock, flags);
+ }
+ /* check for fifo overflow */
+ if (status & LADFUL) {
+ dev_err(dev->class_dev, "fifo overflow\n");
+ /* clear overflow interrupt latch */
+ spin_lock_irqsave(&dev->spinlock, flags);
+ outw(devpriv->adc_fifo_bits | LADFUL,
+ devpriv->control_status + INT_ADCFIFO);
+ spin_unlock_irqrestore(&dev->spinlock, flags);
+ async->events |= COMEDI_CB_ERROR;
+ }
+
+ comedi_handle_events(dev, s);
+
+ return IRQ_HANDLED;
+}
+
+static int cb_pcidas_auto_attach(struct comedi_device *dev,
+ unsigned long context)
+{
+ struct pci_dev *pcidev = comedi_to_pci_dev(dev);
+ const struct cb_pcidas_board *thisboard = NULL;
+ struct cb_pcidas_private *devpriv;
+ struct comedi_subdevice *s;
+ int i;
+ int ret;
+
+ if (context < ARRAY_SIZE(cb_pcidas_boards))
+ thisboard = &cb_pcidas_boards[context];
+ if (!thisboard)
+ return -ENODEV;
+ dev->board_ptr = thisboard;
+ dev->board_name = thisboard->name;
+
+ devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
+ if (!devpriv)
+ return -ENOMEM;
+
+ ret = comedi_pci_enable(dev);
+ if (ret)
+ return ret;
+
+ devpriv->s5933_config = pci_resource_start(pcidev, 0);
+ devpriv->control_status = pci_resource_start(pcidev, 1);
+ devpriv->adc_fifo = pci_resource_start(pcidev, 2);
+ dev->iobase = pci_resource_start(pcidev, 3);
+ if (thisboard->ao_nchan)
+ devpriv->ao_registers = pci_resource_start(pcidev, 4);
+
+ /* disable and clear interrupts on amcc s5933 */
+ outl(INTCSR_INBOX_INTR_STATUS,
+ devpriv->s5933_config + AMCC_OP_REG_INTCSR);
+
+ ret = request_irq(pcidev->irq, cb_pcidas_interrupt, IRQF_SHARED,
+ dev->board_name, dev);
+ if (ret) {
+ dev_dbg(dev->class_dev, "unable to allocate irq %d\n",
+ pcidev->irq);
+ return ret;
+ }
+ dev->irq = pcidev->irq;
+
+ dev->pacer = comedi_8254_init(dev->iobase + ADC8254,
+ I8254_OSC_BASE_10MHZ, I8254_IO8, 0);
+ if (!dev->pacer)
+ return -ENOMEM;
+
+ devpriv->ao_pacer = comedi_8254_init(dev->iobase + DAC8254,
+ I8254_OSC_BASE_10MHZ,
+ I8254_IO8, 0);
+ if (!devpriv->ao_pacer)
+ return -ENOMEM;
+
+ ret = comedi_alloc_subdevices(dev, 7);
+ if (ret)
+ return ret;
+
+ s = &dev->subdevices[0];
+ /* analog input subdevice */
+ dev->read_subdev = s;
+ s->type = COMEDI_SUBD_AI;
+ s->subdev_flags = SDF_READABLE | SDF_GROUND | SDF_DIFF | SDF_CMD_READ;
+ /* WARNING: Number of inputs in differential mode is ignored */
+ s->n_chan = thisboard->ai_nchan;
+ s->len_chanlist = thisboard->ai_nchan;
+ s->maxdata = (1 << thisboard->ai_bits) - 1;
+ s->range_table = thisboard->ranges;
+ s->insn_read = cb_pcidas_ai_rinsn;
+ s->insn_config = ai_config_insn;
+ s->do_cmd = cb_pcidas_ai_cmd;
+ s->do_cmdtest = cb_pcidas_ai_cmdtest;
+ s->cancel = cb_pcidas_cancel;
+
+ /* analog output subdevice */
+ s = &dev->subdevices[1];
+ if (thisboard->ao_nchan) {
+ s->type = COMEDI_SUBD_AO;
+ s->subdev_flags = SDF_READABLE | SDF_WRITABLE | SDF_GROUND;
+ s->n_chan = thisboard->ao_nchan;
+ /*
+ * analog out resolution is the same as
+ * analog input resolution, so use ai_bits
+ */
+ s->maxdata = (1 << thisboard->ai_bits) - 1;
+ s->range_table = &cb_pcidas_ao_ranges;
+ /* default to no fifo (*insn_write) */
+ s->insn_write = cb_pcidas_ao_nofifo_winsn;
+
+ ret = comedi_alloc_subdev_readback(s);
+ if (ret)
+ return ret;
+
+ if (thisboard->has_ao_fifo) {
+ dev->write_subdev = s;
+ s->subdev_flags |= SDF_CMD_WRITE;
+ /* use fifo (*insn_write) instead */
+ s->insn_write = cb_pcidas_ao_fifo_winsn;
+ s->do_cmdtest = cb_pcidas_ao_cmdtest;
+ s->do_cmd = cb_pcidas_ao_cmd;
+ s->cancel = cb_pcidas_ao_cancel;
+ }
+ } else {
+ s->type = COMEDI_SUBD_UNUSED;
+ }
+
+ /* 8255 */
+ s = &dev->subdevices[2];
+ ret = subdev_8255_init(dev, s, NULL, DIO_8255);
+ if (ret)
+ return ret;
+
+ /* serial EEPROM, */
+ s = &dev->subdevices[3];
+ s->type = COMEDI_SUBD_MEMORY;
+ s->subdev_flags = SDF_READABLE | SDF_INTERNAL;
+ s->n_chan = 256;
+ s->maxdata = 0xff;
+ s->insn_read = eeprom_read_insn;
+
+ /* 8800 caldac */
+ s = &dev->subdevices[4];
+ s->type = COMEDI_SUBD_CALIB;
+ s->subdev_flags = SDF_READABLE | SDF_WRITABLE | SDF_INTERNAL;
+ s->n_chan = NUM_CHANNELS_8800;
+ s->maxdata = 0xff;
+ s->insn_write = cb_pcidas_caldac_insn_write;
+
+ ret = comedi_alloc_subdev_readback(s);
+ if (ret)
+ return ret;
+
+ for (i = 0; i < s->n_chan; i++) {
+ caldac_8800_write(dev, i, s->maxdata / 2);
+ s->readback[i] = s->maxdata / 2;
+ }
+
+ /* trim potentiometer */
+ s = &dev->subdevices[5];
+ s->type = COMEDI_SUBD_CALIB;
+ s->subdev_flags = SDF_READABLE | SDF_WRITABLE | SDF_INTERNAL;
+ if (thisboard->trimpot == AD7376) {
+ s->n_chan = NUM_CHANNELS_7376;
+ s->maxdata = 0x7f;
+ } else {
+ s->n_chan = NUM_CHANNELS_8402;
+ s->maxdata = 0xff;
+ }
+ s->insn_write = cb_pcidas_trimpot_insn_write;
+
+ ret = comedi_alloc_subdev_readback(s);
+ if (ret)
+ return ret;
+
+ for (i = 0; i < s->n_chan; i++) {
+ cb_pcidas_trimpot_write(dev, i, s->maxdata / 2);
+ s->readback[i] = s->maxdata / 2;
+ }
+
+ /* dac08 caldac */
+ s = &dev->subdevices[6];
+ if (thisboard->has_dac08) {
+ s->type = COMEDI_SUBD_CALIB;
+ s->subdev_flags = SDF_READABLE | SDF_WRITABLE | SDF_INTERNAL;
+ s->n_chan = NUM_CHANNELS_DAC08;
+ s->maxdata = 0xff;
+ s->insn_write = cb_pcidas_dac08_insn_write;
+
+ ret = comedi_alloc_subdev_readback(s);
+ if (ret)
+ return ret;
+
+ for (i = 0; i < s->n_chan; i++) {
+ dac08_write(dev, s->maxdata / 2);
+ s->readback[i] = s->maxdata / 2;
+ }
+ } else {
+ s->type = COMEDI_SUBD_UNUSED;
+ }
+
+ /* make sure mailbox 4 is empty */
+ inl(devpriv->s5933_config + AMCC_OP_REG_IMB4);
+ /* Set bits to enable incoming mailbox interrupts on amcc s5933. */
+ devpriv->s5933_intcsr_bits =
+ INTCSR_INBOX_BYTE(3) | INTCSR_INBOX_SELECT(3) |
+ INTCSR_INBOX_FULL_INT;
+ /* clear and enable interrupt on amcc s5933 */
+ outl(devpriv->s5933_intcsr_bits | INTCSR_INBOX_INTR_STATUS,
+ devpriv->s5933_config + AMCC_OP_REG_INTCSR);
+
+ return 0;
+}
+
+static void cb_pcidas_detach(struct comedi_device *dev)
+{
+ struct cb_pcidas_private *devpriv = dev->private;
+
+ if (devpriv) {
+ if (devpriv->s5933_config)
+ outl(INTCSR_INBOX_INTR_STATUS,
+ devpriv->s5933_config + AMCC_OP_REG_INTCSR);
+ kfree(devpriv->ao_pacer);
+ }
+ comedi_pci_detach(dev);
+}
+
+static struct comedi_driver cb_pcidas_driver = {
+ .driver_name = "cb_pcidas",
+ .module = THIS_MODULE,
+ .auto_attach = cb_pcidas_auto_attach,
+ .detach = cb_pcidas_detach,
+};
+
+static int cb_pcidas_pci_probe(struct pci_dev *dev,
+ const struct pci_device_id *id)
+{
+ return comedi_pci_auto_config(dev, &cb_pcidas_driver,
+ id->driver_data);
+}
+
+static const struct pci_device_id cb_pcidas_pci_table[] = {
+ { PCI_VDEVICE(CB, 0x0001), BOARD_PCIDAS1602_16 },
+ { PCI_VDEVICE(CB, 0x000f), BOARD_PCIDAS1200 },
+ { PCI_VDEVICE(CB, 0x0010), BOARD_PCIDAS1602_12 },
+ { PCI_VDEVICE(CB, 0x0019), BOARD_PCIDAS1200_JR },
+ { PCI_VDEVICE(CB, 0x001c), BOARD_PCIDAS1602_16_JR },
+ { PCI_VDEVICE(CB, 0x004c), BOARD_PCIDAS1000 },
+ { PCI_VDEVICE(CB, 0x001a), BOARD_PCIDAS1001 },
+ { PCI_VDEVICE(CB, 0x001b), BOARD_PCIDAS1002 },
+ { 0 }
+};
+MODULE_DEVICE_TABLE(pci, cb_pcidas_pci_table);
+
+static struct pci_driver cb_pcidas_pci_driver = {
+ .name = "cb_pcidas",
+ .id_table = cb_pcidas_pci_table,
+ .probe = cb_pcidas_pci_probe,
+ .remove = comedi_pci_auto_unconfig,
+};
+module_comedi_pci_driver(cb_pcidas_driver, cb_pcidas_pci_driver);
+
+MODULE_AUTHOR("Comedi http://www.comedi.org");
+MODULE_DESCRIPTION("Comedi low-level driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/comedi/drivers/cb_pcidas64.c b/drivers/staging/comedi/drivers/cb_pcidas64.c
new file mode 100644
index 000000000..a94c33c3d
--- /dev/null
+++ b/drivers/staging/comedi/drivers/cb_pcidas64.c
@@ -0,0 +1,4079 @@
+/*
+ comedi/drivers/cb_pcidas64.c
+ This is a driver for the ComputerBoards/MeasurementComputing PCI-DAS
+ 64xx, 60xx, and 4020 cards.
+
+ Author: Frank Mori Hess <fmhess@users.sourceforge.net>
+ Copyright (C) 2001, 2002 Frank Mori Hess
+
+ Thanks also go to the following people:
+
+ Steve Rosenbluth, for providing the source code for
+ his pci-das6402 driver, and source code for working QNX pci-6402
+ drivers by Greg Laird and Mariusz Bogacz. None of the code was
+ used directly here, but it was useful as an additional source of
+ documentation on how to program the boards.
+
+ John Sims, for much testing and feedback on pcidas-4020 support.
+
+ COMEDI - Linux Control and Measurement Device Interface
+ Copyright (C) 1997-8 David A. Schleef <ds@schleef.org>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+*/
+
+/*
+ * Driver: cb_pcidas64
+ * Description: MeasurementComputing PCI-DAS64xx, 60XX, and 4020 series
+ * with the PLX 9080 PCI controller
+ * Author: Frank Mori Hess <fmhess@users.sourceforge.net>
+ * Status: works
+ * Updated: Fri, 02 Nov 2012 18:58:55 +0000
+ * Devices: [Measurement Computing] PCI-DAS6402/16 (cb_pcidas64),
+ * PCI-DAS6402/12, PCI-DAS64/M1/16, PCI-DAS64/M2/16,
+ * PCI-DAS64/M3/16, PCI-DAS6402/16/JR, PCI-DAS64/M1/16/JR,
+ * PCI-DAS64/M2/16/JR, PCI-DAS64/M3/16/JR, PCI-DAS64/M1/14,
+ * PCI-DAS64/M2/14, PCI-DAS64/M3/14, PCI-DAS6013, PCI-DAS6014,
+ * PCI-DAS6023, PCI-DAS6025, PCI-DAS6030,
+ * PCI-DAS6031, PCI-DAS6032, PCI-DAS6033, PCI-DAS6034,
+ * PCI-DAS6035, PCI-DAS6036, PCI-DAS6040, PCI-DAS6052,
+ * PCI-DAS6070, PCI-DAS6071, PCI-DAS4020/12
+ *
+ * Configuration options:
+ * None.
+ *
+ * Manual attachment of PCI cards with the comedi_config utility is not
+ * supported by this driver; they are attached automatically.
+ *
+ * These boards may be autocalibrated with the comedi_calibrate utility.
+ *
+ * To select the bnc trigger input on the 4020 (instead of the dio input),
+ * specify a nonzero channel in the chanspec. If you wish to use an external
+ * master clock on the 4020, you may do so by setting the scan_begin_src
+ * to TRIG_OTHER, and using an INSN_CONFIG_TIMER_1 configuration insn
+ * to configure the divisor to use for the external clock.
+ *
+ * Some devices are not identified because the PCI device IDs are not yet
+ * known. If you have such a board, please let the maintainers know.
+ */
+
+/*
+
+TODO:
+ make it return error if user attempts an ai command that uses the
+ external queue, and an ao command simultaneously user counter subdevice
+ there are a number of boards this driver will support when they are
+ fully released, but does not yet since the pci device id numbers
+ are not yet available.
+
+ support prescaled 100khz clock for slow pacing (not available on 6000
+ series?)
+
+ make ao fifo size adjustable like ai fifo
+*/
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+
+#include "../comedi_pci.h"
+
+#include "8255.h"
+#include "plx9080.h"
+
+#define TIMER_BASE 25 /* 40MHz master clock */
+/* 100kHz 'prescaled' clock for slow acquisition,
+ * maybe I'll support this someday */
+#define PRESCALED_TIMER_BASE 10000
+#define DMA_BUFFER_SIZE 0x1000
+
+/* maximum value that can be loaded into board's 24-bit counters*/
+static const int max_counter_value = 0xffffff;
+
+/* PCI-DAS64xxx base addresses */
+
+/* devpriv->main_iobase registers */
+enum write_only_registers {
+ INTR_ENABLE_REG = 0x0, /* interrupt enable register */
+ HW_CONFIG_REG = 0x2, /* hardware config register */
+ DAQ_SYNC_REG = 0xc,
+ DAQ_ATRIG_LOW_4020_REG = 0xc,
+ ADC_CONTROL0_REG = 0x10, /* adc control register 0 */
+ ADC_CONTROL1_REG = 0x12, /* adc control register 1 */
+ CALIBRATION_REG = 0x14,
+ /* lower 16 bits of adc sample interval counter */
+ ADC_SAMPLE_INTERVAL_LOWER_REG = 0x16,
+ /* upper 8 bits of adc sample interval counter */
+ ADC_SAMPLE_INTERVAL_UPPER_REG = 0x18,
+ /* lower 16 bits of delay interval counter */
+ ADC_DELAY_INTERVAL_LOWER_REG = 0x1a,
+ /* upper 8 bits of delay interval counter */
+ ADC_DELAY_INTERVAL_UPPER_REG = 0x1c,
+ /* lower 16 bits of hardware conversion/scan counter */
+ ADC_COUNT_LOWER_REG = 0x1e,
+ /* upper 8 bits of hardware conversion/scan counter */
+ ADC_COUNT_UPPER_REG = 0x20,
+ ADC_START_REG = 0x22, /* software trigger to start acquisition */
+ ADC_CONVERT_REG = 0x24, /* initiates single conversion */
+ ADC_QUEUE_CLEAR_REG = 0x26, /* clears adc queue */
+ ADC_QUEUE_LOAD_REG = 0x28, /* loads adc queue */
+ ADC_BUFFER_CLEAR_REG = 0x2a,
+ /* high channel for internal queue, use adc_chan_bits() inline above */
+ ADC_QUEUE_HIGH_REG = 0x2c,
+ DAC_CONTROL0_REG = 0x50, /* dac control register 0 */
+ DAC_CONTROL1_REG = 0x52, /* dac control register 0 */
+ /* lower 16 bits of dac sample interval counter */
+ DAC_SAMPLE_INTERVAL_LOWER_REG = 0x54,
+ /* upper 8 bits of dac sample interval counter */
+ DAC_SAMPLE_INTERVAL_UPPER_REG = 0x56,
+ DAC_SELECT_REG = 0x60,
+ DAC_START_REG = 0x64,
+ DAC_BUFFER_CLEAR_REG = 0x66, /* clear dac buffer */
+};
+
+static inline unsigned int dac_convert_reg(unsigned int channel)
+{
+ return 0x70 + (2 * (channel & 0x1));
+}
+
+static inline unsigned int dac_lsb_4020_reg(unsigned int channel)
+{
+ return 0x70 + (4 * (channel & 0x1));
+}
+
+static inline unsigned int dac_msb_4020_reg(unsigned int channel)
+{
+ return 0x72 + (4 * (channel & 0x1));
+}
+
+enum read_only_registers {
+ /* hardware status register,
+ * reading this apparently clears pending interrupts as well */
+ HW_STATUS_REG = 0x0,
+ PIPE1_READ_REG = 0x4,
+ ADC_READ_PNTR_REG = 0x8,
+ LOWER_XFER_REG = 0x10,
+ ADC_WRITE_PNTR_REG = 0xc,
+ PREPOST_REG = 0x14,
+};
+
+enum read_write_registers {
+ I8255_4020_REG = 0x48, /* 8255 offset, for 4020 only */
+ /* external channel/gain queue, uses same bits as ADC_QUEUE_LOAD_REG */
+ ADC_QUEUE_FIFO_REG = 0x100,
+ ADC_FIFO_REG = 0x200, /* adc data fifo */
+ /* dac data fifo, has weird interactions with external channel queue */
+ DAC_FIFO_REG = 0x300,
+};
+
+/* dev->mmio registers */
+enum dio_counter_registers {
+ DIO_8255_OFFSET = 0x0,
+ DO_REG = 0x20,
+ DI_REG = 0x28,
+ DIO_DIRECTION_60XX_REG = 0x40,
+ DIO_DATA_60XX_REG = 0x48,
+};
+
+/* bit definitions for write-only registers */
+
+enum intr_enable_contents {
+ ADC_INTR_SRC_MASK = 0x3, /* adc interrupt source mask */
+ ADC_INTR_QFULL_BITS = 0x0, /* interrupt fifo quarter full */
+ ADC_INTR_EOC_BITS = 0x1, /* interrupt end of conversion */
+ ADC_INTR_EOSCAN_BITS = 0x2, /* interrupt end of scan */
+ ADC_INTR_EOSEQ_BITS = 0x3, /* interrupt end of sequence mask */
+ EN_ADC_INTR_SRC_BIT = 0x4, /* enable adc interrupt source */
+ EN_ADC_DONE_INTR_BIT = 0x8, /* enable adc acquisition done intr */
+ DAC_INTR_SRC_MASK = 0x30,
+ DAC_INTR_QEMPTY_BITS = 0x0,
+ DAC_INTR_HIGH_CHAN_BITS = 0x10,
+ EN_DAC_INTR_SRC_BIT = 0x40, /* enable dac interrupt source */
+ EN_DAC_DONE_INTR_BIT = 0x80,
+ EN_ADC_ACTIVE_INTR_BIT = 0x200, /* enable adc active interrupt */
+ EN_ADC_STOP_INTR_BIT = 0x400, /* enable adc stop trigger interrupt */
+ EN_DAC_ACTIVE_INTR_BIT = 0x800, /* enable dac active interrupt */
+ EN_DAC_UNDERRUN_BIT = 0x4000, /* enable dac underrun status bit */
+ EN_ADC_OVERRUN_BIT = 0x8000, /* enable adc overrun status bit */
+};
+
+enum hw_config_contents {
+ MASTER_CLOCK_4020_MASK = 0x3, /* master clock source mask for 4020 */
+ INTERNAL_CLOCK_4020_BITS = 0x1, /* use 40 MHz internal master clock */
+ BNC_CLOCK_4020_BITS = 0x2, /* use BNC input for master clock */
+ EXT_CLOCK_4020_BITS = 0x3, /* use dio input for master clock */
+ EXT_QUEUE_BIT = 0x200, /* use external channel/gain queue */
+ /* use 225 nanosec strobe when loading dac instead of 50 nanosec */
+ SLOW_DAC_BIT = 0x400,
+ /* bit with unknown function yet given as default value in pci-das64
+ * manual */
+ HW_CONFIG_DUMMY_BITS = 0x2000,
+ /* bit selects channels 1/0 for analog input/output, otherwise 0/1 */
+ DMA_CH_SELECT_BIT = 0x8000,
+ FIFO_SIZE_REG = 0x4, /* allows adjustment of fifo sizes */
+ DAC_FIFO_SIZE_MASK = 0xff00, /* bits that set dac fifo size */
+ DAC_FIFO_BITS = 0xf800, /* 8k sample ao fifo */
+};
+#define DAC_FIFO_SIZE 0x2000
+
+enum daq_atrig_low_4020_contents {
+ /* use trig/ext clk bnc input for analog gate signal */
+ EXT_AGATE_BNC_BIT = 0x8000,
+ /* use trig/ext clk bnc input for external stop trigger signal */
+ EXT_STOP_TRIG_BNC_BIT = 0x4000,
+ /* use trig/ext clk bnc input for external start trigger signal */
+ EXT_START_TRIG_BNC_BIT = 0x2000,
+};
+
+static inline uint16_t analog_trig_low_threshold_bits(uint16_t threshold)
+{
+ return threshold & 0xfff;
+}
+
+enum adc_control0_contents {
+ ADC_GATE_SRC_MASK = 0x3, /* bits that select gate */
+ ADC_SOFT_GATE_BITS = 0x1, /* software gate */
+ ADC_EXT_GATE_BITS = 0x2, /* external digital gate */
+ ADC_ANALOG_GATE_BITS = 0x3, /* analog level gate */
+ ADC_GATE_LEVEL_BIT = 0x4, /* level-sensitive gate (for digital) */
+ ADC_GATE_POLARITY_BIT = 0x8, /* gate active low */
+ ADC_START_TRIG_SOFT_BITS = 0x10,
+ ADC_START_TRIG_EXT_BITS = 0x20,
+ ADC_START_TRIG_ANALOG_BITS = 0x30,
+ ADC_START_TRIG_MASK = 0x30,
+ ADC_START_TRIG_FALLING_BIT = 0x40, /* trig 1 uses falling edge */
+ /* external pacing uses falling edge */
+ ADC_EXT_CONV_FALLING_BIT = 0x800,
+ /* enable hardware scan counter */
+ ADC_SAMPLE_COUNTER_EN_BIT = 0x1000,
+ ADC_DMA_DISABLE_BIT = 0x4000, /* disables dma */
+ ADC_ENABLE_BIT = 0x8000, /* master adc enable */
+};
+
+enum adc_control1_contents {
+ /* should be set for boards with > 16 channels */
+ ADC_QUEUE_CONFIG_BIT = 0x1,
+ CONVERT_POLARITY_BIT = 0x10,
+ EOC_POLARITY_BIT = 0x20,
+ ADC_SW_GATE_BIT = 0x40, /* software gate of adc */
+ ADC_DITHER_BIT = 0x200, /* turn on extra noise for dithering */
+ RETRIGGER_BIT = 0x800,
+ ADC_LO_CHANNEL_4020_MASK = 0x300,
+ ADC_HI_CHANNEL_4020_MASK = 0xc00,
+ TWO_CHANNEL_4020_BITS = 0x1000, /* two channel mode for 4020 */
+ FOUR_CHANNEL_4020_BITS = 0x2000, /* four channel mode for 4020 */
+ CHANNEL_MODE_4020_MASK = 0x3000,
+ ADC_MODE_MASK = 0xf000,
+};
+
+static inline uint16_t adc_lo_chan_4020_bits(unsigned int channel)
+{
+ return (channel & 0x3) << 8;
+};
+
+static inline uint16_t adc_hi_chan_4020_bits(unsigned int channel)
+{
+ return (channel & 0x3) << 10;
+};
+
+static inline uint16_t adc_mode_bits(unsigned int mode)
+{
+ return (mode & 0xf) << 12;
+};
+
+enum calibration_contents {
+ SELECT_8800_BIT = 0x1,
+ SELECT_8402_64XX_BIT = 0x2,
+ SELECT_1590_60XX_BIT = 0x2,
+ CAL_EN_64XX_BIT = 0x40, /* calibration enable for 64xx series */
+ SERIAL_DATA_IN_BIT = 0x80,
+ SERIAL_CLOCK_BIT = 0x100,
+ CAL_EN_60XX_BIT = 0x200, /* calibration enable for 60xx series */
+ CAL_GAIN_BIT = 0x800,
+};
+
+/* calibration sources for 6025 are:
+ * 0 : ground
+ * 1 : 10V
+ * 2 : 5V
+ * 3 : 0.5V
+ * 4 : 0.05V
+ * 5 : ground
+ * 6 : dac channel 0
+ * 7 : dac channel 1
+ */
+
+static inline uint16_t adc_src_bits(unsigned int source)
+{
+ return (source & 0xf) << 3;
+};
+
+static inline uint16_t adc_convert_chan_4020_bits(unsigned int channel)
+{
+ return (channel & 0x3) << 8;
+};
+
+enum adc_queue_load_contents {
+ UNIP_BIT = 0x800, /* unipolar/bipolar bit */
+ ADC_SE_DIFF_BIT = 0x1000, /* single-ended/ differential bit */
+ /* non-referenced single-ended (common-mode input) */
+ ADC_COMMON_BIT = 0x2000,
+ QUEUE_EOSEQ_BIT = 0x4000, /* queue end of sequence */
+ QUEUE_EOSCAN_BIT = 0x8000, /* queue end of scan */
+};
+
+static inline uint16_t adc_chan_bits(unsigned int channel)
+{
+ return channel & 0x3f;
+};
+
+enum dac_control0_contents {
+ DAC_ENABLE_BIT = 0x8000, /* dac controller enable bit */
+ DAC_CYCLIC_STOP_BIT = 0x4000,
+ DAC_WAVEFORM_MODE_BIT = 0x100,
+ DAC_EXT_UPDATE_FALLING_BIT = 0x80,
+ DAC_EXT_UPDATE_ENABLE_BIT = 0x40,
+ WAVEFORM_TRIG_MASK = 0x30,
+ WAVEFORM_TRIG_DISABLED_BITS = 0x0,
+ WAVEFORM_TRIG_SOFT_BITS = 0x10,
+ WAVEFORM_TRIG_EXT_BITS = 0x20,
+ WAVEFORM_TRIG_ADC1_BITS = 0x30,
+ WAVEFORM_TRIG_FALLING_BIT = 0x8,
+ WAVEFORM_GATE_LEVEL_BIT = 0x4,
+ WAVEFORM_GATE_ENABLE_BIT = 0x2,
+ WAVEFORM_GATE_SELECT_BIT = 0x1,
+};
+
+enum dac_control1_contents {
+ DAC_WRITE_POLARITY_BIT = 0x800, /* board-dependent setting */
+ DAC1_EXT_REF_BIT = 0x200,
+ DAC0_EXT_REF_BIT = 0x100,
+ DAC_OUTPUT_ENABLE_BIT = 0x80, /* dac output enable bit */
+ DAC_UPDATE_POLARITY_BIT = 0x40, /* board-dependent setting */
+ DAC_SW_GATE_BIT = 0x20,
+ DAC1_UNIPOLAR_BIT = 0x8,
+ DAC0_UNIPOLAR_BIT = 0x2,
+};
+
+/* bit definitions for read-only registers */
+enum hw_status_contents {
+ DAC_UNDERRUN_BIT = 0x1,
+ ADC_OVERRUN_BIT = 0x2,
+ DAC_ACTIVE_BIT = 0x4,
+ ADC_ACTIVE_BIT = 0x8,
+ DAC_INTR_PENDING_BIT = 0x10,
+ ADC_INTR_PENDING_BIT = 0x20,
+ DAC_DONE_BIT = 0x40,
+ ADC_DONE_BIT = 0x80,
+ EXT_INTR_PENDING_BIT = 0x100,
+ ADC_STOP_BIT = 0x200,
+};
+
+static inline uint16_t pipe_full_bits(uint16_t hw_status_bits)
+{
+ return (hw_status_bits >> 10) & 0x3;
+};
+
+static inline unsigned int dma_chain_flag_bits(uint16_t prepost_bits)
+{
+ return (prepost_bits >> 6) & 0x3;
+}
+
+static inline unsigned int adc_upper_read_ptr_code(uint16_t prepost_bits)
+{
+ return (prepost_bits >> 12) & 0x3;
+}
+
+static inline unsigned int adc_upper_write_ptr_code(uint16_t prepost_bits)
+{
+ return (prepost_bits >> 14) & 0x3;
+}
+
+/* I2C addresses for 4020 */
+enum i2c_addresses {
+ RANGE_CAL_I2C_ADDR = 0x20,
+ CALDAC0_I2C_ADDR = 0xc,
+ CALDAC1_I2C_ADDR = 0xd,
+};
+
+enum range_cal_i2c_contents {
+ /* bits that set what source the adc converter measures */
+ ADC_SRC_4020_MASK = 0x70,
+ /* make bnc trig/ext clock threshold 0V instead of 2.5V */
+ BNC_TRIG_THRESHOLD_0V_BIT = 0x80,
+};
+
+static inline uint8_t adc_src_4020_bits(unsigned int source)
+{
+ return (source << 4) & ADC_SRC_4020_MASK;
+};
+
+static inline uint8_t attenuate_bit(unsigned int channel)
+{
+ /* attenuate channel (+-5V input range) */
+ return 1 << (channel & 0x3);
+};
+
+/* analog input ranges for 64xx boards */
+static const struct comedi_lrange ai_ranges_64xx = {
+ 8, {
+ BIP_RANGE(10),
+ BIP_RANGE(5),
+ BIP_RANGE(2.5),
+ BIP_RANGE(1.25),
+ UNI_RANGE(10),
+ UNI_RANGE(5),
+ UNI_RANGE(2.5),
+ UNI_RANGE(1.25)
+ }
+};
+
+static const uint8_t ai_range_code_64xx[8] = {
+ 0x0, 0x1, 0x2, 0x3, /* bipolar 10, 5, 2,5, 1.25 */
+ 0x8, 0x9, 0xa, 0xb /* unipolar 10, 5, 2.5, 1.25 */
+};
+
+/* analog input ranges for 64-Mx boards */
+static const struct comedi_lrange ai_ranges_64_mx = {
+ 7, {
+ BIP_RANGE(5),
+ BIP_RANGE(2.5),
+ BIP_RANGE(1.25),
+ BIP_RANGE(0.625),
+ UNI_RANGE(5),
+ UNI_RANGE(2.5),
+ UNI_RANGE(1.25)
+ }
+};
+
+static const uint8_t ai_range_code_64_mx[7] = {
+ 0x0, 0x1, 0x2, 0x3, /* bipolar 5, 2.5, 1.25, 0.625 */
+ 0x9, 0xa, 0xb /* unipolar 5, 2.5, 1.25 */
+};
+
+/* analog input ranges for 60xx boards */
+static const struct comedi_lrange ai_ranges_60xx = {
+ 4, {
+ BIP_RANGE(10),
+ BIP_RANGE(5),
+ BIP_RANGE(0.5),
+ BIP_RANGE(0.05)
+ }
+};
+
+static const uint8_t ai_range_code_60xx[4] = {
+ 0x0, 0x1, 0x4, 0x7 /* bipolar 10, 5, 0.5, 0.05 */
+};
+
+/* analog input ranges for 6030, etc boards */
+static const struct comedi_lrange ai_ranges_6030 = {
+ 14, {
+ BIP_RANGE(10),
+ BIP_RANGE(5),
+ BIP_RANGE(2),
+ BIP_RANGE(1),
+ BIP_RANGE(0.5),
+ BIP_RANGE(0.2),
+ BIP_RANGE(0.1),
+ UNI_RANGE(10),
+ UNI_RANGE(5),
+ UNI_RANGE(2),
+ UNI_RANGE(1),
+ UNI_RANGE(0.5),
+ UNI_RANGE(0.2),
+ UNI_RANGE(0.1)
+ }
+};
+
+static const uint8_t ai_range_code_6030[14] = {
+ 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, /* bip 10, 5, 2, 1, 0.5, 0.2, 0.1 */
+ 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf /* uni 10, 5, 2, 1, 0.5, 0.2, 0.1 */
+};
+
+/* analog input ranges for 6052, etc boards */
+static const struct comedi_lrange ai_ranges_6052 = {
+ 15, {
+ BIP_RANGE(10),
+ BIP_RANGE(5),
+ BIP_RANGE(2.5),
+ BIP_RANGE(1),
+ BIP_RANGE(0.5),
+ BIP_RANGE(0.25),
+ BIP_RANGE(0.1),
+ BIP_RANGE(0.05),
+ UNI_RANGE(10),
+ UNI_RANGE(5),
+ UNI_RANGE(2),
+ UNI_RANGE(1),
+ UNI_RANGE(0.5),
+ UNI_RANGE(0.2),
+ UNI_RANGE(0.1)
+ }
+};
+
+static const uint8_t ai_range_code_6052[15] = {
+ 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, /* bipolar 10 ... 0.05 */
+ 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf /* unipolar 10 ... 0.1 */
+};
+
+/* analog input ranges for 4020 board */
+static const struct comedi_lrange ai_ranges_4020 = {
+ 2, {
+ BIP_RANGE(5),
+ BIP_RANGE(1)
+ }
+};
+
+/* analog output ranges */
+static const struct comedi_lrange ao_ranges_64xx = {
+ 4, {
+ BIP_RANGE(5),
+ BIP_RANGE(10),
+ UNI_RANGE(5),
+ UNI_RANGE(10)
+ }
+};
+
+static const int ao_range_code_64xx[] = {
+ 0x0,
+ 0x1,
+ 0x2,
+ 0x3,
+};
+
+static const int ao_range_code_60xx[] = {
+ 0x0,
+};
+
+static const struct comedi_lrange ao_ranges_6030 = {
+ 2, {
+ BIP_RANGE(10),
+ UNI_RANGE(10)
+ }
+};
+
+static const int ao_range_code_6030[] = {
+ 0x0,
+ 0x2,
+};
+
+static const struct comedi_lrange ao_ranges_4020 = {
+ 2, {
+ BIP_RANGE(5),
+ BIP_RANGE(10)
+ }
+};
+
+static const int ao_range_code_4020[] = {
+ 0x1,
+ 0x0,
+};
+
+enum register_layout {
+ LAYOUT_60XX,
+ LAYOUT_64XX,
+ LAYOUT_4020,
+};
+
+struct hw_fifo_info {
+ unsigned int num_segments;
+ unsigned int max_segment_length;
+ unsigned int sample_packing_ratio;
+ uint16_t fifo_size_reg_mask;
+};
+
+enum pcidas64_boardid {
+ BOARD_PCIDAS6402_16,
+ BOARD_PCIDAS6402_12,
+ BOARD_PCIDAS64_M1_16,
+ BOARD_PCIDAS64_M2_16,
+ BOARD_PCIDAS64_M3_16,
+ BOARD_PCIDAS6013,
+ BOARD_PCIDAS6014,
+ BOARD_PCIDAS6023,
+ BOARD_PCIDAS6025,
+ BOARD_PCIDAS6030,
+ BOARD_PCIDAS6031,
+ BOARD_PCIDAS6032,
+ BOARD_PCIDAS6033,
+ BOARD_PCIDAS6034,
+ BOARD_PCIDAS6035,
+ BOARD_PCIDAS6036,
+ BOARD_PCIDAS6040,
+ BOARD_PCIDAS6052,
+ BOARD_PCIDAS6070,
+ BOARD_PCIDAS6071,
+ BOARD_PCIDAS4020_12,
+ BOARD_PCIDAS6402_16_JR,
+ BOARD_PCIDAS64_M1_16_JR,
+ BOARD_PCIDAS64_M2_16_JR,
+ BOARD_PCIDAS64_M3_16_JR,
+ BOARD_PCIDAS64_M1_14,
+ BOARD_PCIDAS64_M2_14,
+ BOARD_PCIDAS64_M3_14,
+};
+
+struct pcidas64_board {
+ const char *name;
+ int ai_se_chans; /* number of ai inputs in single-ended mode */
+ int ai_bits; /* analog input resolution */
+ int ai_speed; /* fastest conversion period in ns */
+ const struct comedi_lrange *ai_range_table;
+ const uint8_t *ai_range_code;
+ int ao_nchan; /* number of analog out channels */
+ int ao_bits; /* analog output resolution */
+ int ao_scan_speed; /* analog output scan speed */
+ const struct comedi_lrange *ao_range_table;
+ const int *ao_range_code;
+ const struct hw_fifo_info *const ai_fifo;
+ /* different board families have slightly different registers */
+ enum register_layout layout;
+ unsigned has_8255:1;
+};
+
+static const struct hw_fifo_info ai_fifo_4020 = {
+ .num_segments = 2,
+ .max_segment_length = 0x8000,
+ .sample_packing_ratio = 2,
+ .fifo_size_reg_mask = 0x7f,
+};
+
+static const struct hw_fifo_info ai_fifo_64xx = {
+ .num_segments = 4,
+ .max_segment_length = 0x800,
+ .sample_packing_ratio = 1,
+ .fifo_size_reg_mask = 0x3f,
+};
+
+static const struct hw_fifo_info ai_fifo_60xx = {
+ .num_segments = 4,
+ .max_segment_length = 0x800,
+ .sample_packing_ratio = 1,
+ .fifo_size_reg_mask = 0x7f,
+};
+
+/* maximum number of dma transfers we will chain together into a ring
+ * (and the maximum number of dma buffers we maintain) */
+#define MAX_AI_DMA_RING_COUNT (0x80000 / DMA_BUFFER_SIZE)
+#define MIN_AI_DMA_RING_COUNT (0x10000 / DMA_BUFFER_SIZE)
+#define AO_DMA_RING_COUNT (0x10000 / DMA_BUFFER_SIZE)
+static inline unsigned int ai_dma_ring_count(const struct pcidas64_board *board)
+{
+ if (board->layout == LAYOUT_4020)
+ return MAX_AI_DMA_RING_COUNT;
+
+ return MIN_AI_DMA_RING_COUNT;
+}
+
+static const int bytes_in_sample = 2;
+
+static const struct pcidas64_board pcidas64_boards[] = {
+ [BOARD_PCIDAS6402_16] = {
+ .name = "pci-das6402/16",
+ .ai_se_chans = 64,
+ .ai_bits = 16,
+ .ai_speed = 5000,
+ .ao_nchan = 2,
+ .ao_bits = 16,
+ .ao_scan_speed = 10000,
+ .layout = LAYOUT_64XX,
+ .ai_range_table = &ai_ranges_64xx,
+ .ai_range_code = ai_range_code_64xx,
+ .ao_range_table = &ao_ranges_64xx,
+ .ao_range_code = ao_range_code_64xx,
+ .ai_fifo = &ai_fifo_64xx,
+ .has_8255 = 1,
+ },
+ [BOARD_PCIDAS6402_12] = {
+ .name = "pci-das6402/12", /* XXX check */
+ .ai_se_chans = 64,
+ .ai_bits = 12,
+ .ai_speed = 5000,
+ .ao_nchan = 2,
+ .ao_bits = 12,
+ .ao_scan_speed = 10000,
+ .layout = LAYOUT_64XX,
+ .ai_range_table = &ai_ranges_64xx,
+ .ai_range_code = ai_range_code_64xx,
+ .ao_range_table = &ao_ranges_64xx,
+ .ao_range_code = ao_range_code_64xx,
+ .ai_fifo = &ai_fifo_64xx,
+ .has_8255 = 1,
+ },
+ [BOARD_PCIDAS64_M1_16] = {
+ .name = "pci-das64/m1/16",
+ .ai_se_chans = 64,
+ .ai_bits = 16,
+ .ai_speed = 1000,
+ .ao_nchan = 2,
+ .ao_bits = 16,
+ .ao_scan_speed = 10000,
+ .layout = LAYOUT_64XX,
+ .ai_range_table = &ai_ranges_64_mx,
+ .ai_range_code = ai_range_code_64_mx,
+ .ao_range_table = &ao_ranges_64xx,
+ .ao_range_code = ao_range_code_64xx,
+ .ai_fifo = &ai_fifo_64xx,
+ .has_8255 = 1,
+ },
+ [BOARD_PCIDAS64_M2_16] = {
+ .name = "pci-das64/m2/16",
+ .ai_se_chans = 64,
+ .ai_bits = 16,
+ .ai_speed = 500,
+ .ao_nchan = 2,
+ .ao_bits = 16,
+ .ao_scan_speed = 10000,
+ .layout = LAYOUT_64XX,
+ .ai_range_table = &ai_ranges_64_mx,
+ .ai_range_code = ai_range_code_64_mx,
+ .ao_range_table = &ao_ranges_64xx,
+ .ao_range_code = ao_range_code_64xx,
+ .ai_fifo = &ai_fifo_64xx,
+ .has_8255 = 1,
+ },
+ [BOARD_PCIDAS64_M3_16] = {
+ .name = "pci-das64/m3/16",
+ .ai_se_chans = 64,
+ .ai_bits = 16,
+ .ai_speed = 333,
+ .ao_nchan = 2,
+ .ao_bits = 16,
+ .ao_scan_speed = 10000,
+ .layout = LAYOUT_64XX,
+ .ai_range_table = &ai_ranges_64_mx,
+ .ai_range_code = ai_range_code_64_mx,
+ .ao_range_table = &ao_ranges_64xx,
+ .ao_range_code = ao_range_code_64xx,
+ .ai_fifo = &ai_fifo_64xx,
+ .has_8255 = 1,
+ },
+ [BOARD_PCIDAS6013] = {
+ .name = "pci-das6013",
+ .ai_se_chans = 16,
+ .ai_bits = 16,
+ .ai_speed = 5000,
+ .ao_nchan = 0,
+ .ao_bits = 16,
+ .layout = LAYOUT_60XX,
+ .ai_range_table = &ai_ranges_60xx,
+ .ai_range_code = ai_range_code_60xx,
+ .ao_range_table = &range_bipolar10,
+ .ao_range_code = ao_range_code_60xx,
+ .ai_fifo = &ai_fifo_60xx,
+ .has_8255 = 0,
+ },
+ [BOARD_PCIDAS6014] = {
+ .name = "pci-das6014",
+ .ai_se_chans = 16,
+ .ai_bits = 16,
+ .ai_speed = 5000,
+ .ao_nchan = 2,
+ .ao_bits = 16,
+ .ao_scan_speed = 100000,
+ .layout = LAYOUT_60XX,
+ .ai_range_table = &ai_ranges_60xx,
+ .ai_range_code = ai_range_code_60xx,
+ .ao_range_table = &range_bipolar10,
+ .ao_range_code = ao_range_code_60xx,
+ .ai_fifo = &ai_fifo_60xx,
+ .has_8255 = 0,
+ },
+ [BOARD_PCIDAS6023] = {
+ .name = "pci-das6023",
+ .ai_se_chans = 16,
+ .ai_bits = 12,
+ .ai_speed = 5000,
+ .ao_nchan = 0,
+ .ao_scan_speed = 100000,
+ .layout = LAYOUT_60XX,
+ .ai_range_table = &ai_ranges_60xx,
+ .ai_range_code = ai_range_code_60xx,
+ .ao_range_table = &range_bipolar10,
+ .ao_range_code = ao_range_code_60xx,
+ .ai_fifo = &ai_fifo_60xx,
+ .has_8255 = 1,
+ },
+ [BOARD_PCIDAS6025] = {
+ .name = "pci-das6025",
+ .ai_se_chans = 16,
+ .ai_bits = 12,
+ .ai_speed = 5000,
+ .ao_nchan = 2,
+ .ao_bits = 12,
+ .ao_scan_speed = 100000,
+ .layout = LAYOUT_60XX,
+ .ai_range_table = &ai_ranges_60xx,
+ .ai_range_code = ai_range_code_60xx,
+ .ao_range_table = &range_bipolar10,
+ .ao_range_code = ao_range_code_60xx,
+ .ai_fifo = &ai_fifo_60xx,
+ .has_8255 = 1,
+ },
+ [BOARD_PCIDAS6030] = {
+ .name = "pci-das6030",
+ .ai_se_chans = 16,
+ .ai_bits = 16,
+ .ai_speed = 10000,
+ .ao_nchan = 2,
+ .ao_bits = 16,
+ .ao_scan_speed = 10000,
+ .layout = LAYOUT_60XX,
+ .ai_range_table = &ai_ranges_6030,
+ .ai_range_code = ai_range_code_6030,
+ .ao_range_table = &ao_ranges_6030,
+ .ao_range_code = ao_range_code_6030,
+ .ai_fifo = &ai_fifo_60xx,
+ .has_8255 = 0,
+ },
+ [BOARD_PCIDAS6031] = {
+ .name = "pci-das6031",
+ .ai_se_chans = 64,
+ .ai_bits = 16,
+ .ai_speed = 10000,
+ .ao_nchan = 2,
+ .ao_bits = 16,
+ .ao_scan_speed = 10000,
+ .layout = LAYOUT_60XX,
+ .ai_range_table = &ai_ranges_6030,
+ .ai_range_code = ai_range_code_6030,
+ .ao_range_table = &ao_ranges_6030,
+ .ao_range_code = ao_range_code_6030,
+ .ai_fifo = &ai_fifo_60xx,
+ .has_8255 = 0,
+ },
+ [BOARD_PCIDAS6032] = {
+ .name = "pci-das6032",
+ .ai_se_chans = 16,
+ .ai_bits = 16,
+ .ai_speed = 10000,
+ .ao_nchan = 0,
+ .layout = LAYOUT_60XX,
+ .ai_range_table = &ai_ranges_6030,
+ .ai_range_code = ai_range_code_6030,
+ .ai_fifo = &ai_fifo_60xx,
+ .has_8255 = 0,
+ },
+ [BOARD_PCIDAS6033] = {
+ .name = "pci-das6033",
+ .ai_se_chans = 64,
+ .ai_bits = 16,
+ .ai_speed = 10000,
+ .ao_nchan = 0,
+ .layout = LAYOUT_60XX,
+ .ai_range_table = &ai_ranges_6030,
+ .ai_range_code = ai_range_code_6030,
+ .ai_fifo = &ai_fifo_60xx,
+ .has_8255 = 0,
+ },
+ [BOARD_PCIDAS6034] = {
+ .name = "pci-das6034",
+ .ai_se_chans = 16,
+ .ai_bits = 16,
+ .ai_speed = 5000,
+ .ao_nchan = 0,
+ .ao_scan_speed = 0,
+ .layout = LAYOUT_60XX,
+ .ai_range_table = &ai_ranges_60xx,
+ .ai_range_code = ai_range_code_60xx,
+ .ai_fifo = &ai_fifo_60xx,
+ .has_8255 = 0,
+ },
+ [BOARD_PCIDAS6035] = {
+ .name = "pci-das6035",
+ .ai_se_chans = 16,
+ .ai_bits = 16,
+ .ai_speed = 5000,
+ .ao_nchan = 2,
+ .ao_bits = 12,
+ .ao_scan_speed = 100000,
+ .layout = LAYOUT_60XX,
+ .ai_range_table = &ai_ranges_60xx,
+ .ai_range_code = ai_range_code_60xx,
+ .ao_range_table = &range_bipolar10,
+ .ao_range_code = ao_range_code_60xx,
+ .ai_fifo = &ai_fifo_60xx,
+ .has_8255 = 0,
+ },
+ [BOARD_PCIDAS6036] = {
+ .name = "pci-das6036",
+ .ai_se_chans = 16,
+ .ai_bits = 16,
+ .ai_speed = 5000,
+ .ao_nchan = 2,
+ .ao_bits = 16,
+ .ao_scan_speed = 100000,
+ .layout = LAYOUT_60XX,
+ .ai_range_table = &ai_ranges_60xx,
+ .ai_range_code = ai_range_code_60xx,
+ .ao_range_table = &range_bipolar10,
+ .ao_range_code = ao_range_code_60xx,
+ .ai_fifo = &ai_fifo_60xx,
+ .has_8255 = 0,
+ },
+ [BOARD_PCIDAS6040] = {
+ .name = "pci-das6040",
+ .ai_se_chans = 16,
+ .ai_bits = 12,
+ .ai_speed = 2000,
+ .ao_nchan = 2,
+ .ao_bits = 12,
+ .ao_scan_speed = 1000,
+ .layout = LAYOUT_60XX,
+ .ai_range_table = &ai_ranges_6052,
+ .ai_range_code = ai_range_code_6052,
+ .ao_range_table = &ao_ranges_6030,
+ .ao_range_code = ao_range_code_6030,
+ .ai_fifo = &ai_fifo_60xx,
+ .has_8255 = 0,
+ },
+ [BOARD_PCIDAS6052] = {
+ .name = "pci-das6052",
+ .ai_se_chans = 16,
+ .ai_bits = 16,
+ .ai_speed = 3333,
+ .ao_nchan = 2,
+ .ao_bits = 16,
+ .ao_scan_speed = 3333,
+ .layout = LAYOUT_60XX,
+ .ai_range_table = &ai_ranges_6052,
+ .ai_range_code = ai_range_code_6052,
+ .ao_range_table = &ao_ranges_6030,
+ .ao_range_code = ao_range_code_6030,
+ .ai_fifo = &ai_fifo_60xx,
+ .has_8255 = 0,
+ },
+ [BOARD_PCIDAS6070] = {
+ .name = "pci-das6070",
+ .ai_se_chans = 16,
+ .ai_bits = 12,
+ .ai_speed = 800,
+ .ao_nchan = 2,
+ .ao_bits = 12,
+ .ao_scan_speed = 1000,
+ .layout = LAYOUT_60XX,
+ .ai_range_table = &ai_ranges_6052,
+ .ai_range_code = ai_range_code_6052,
+ .ao_range_table = &ao_ranges_6030,
+ .ao_range_code = ao_range_code_6030,
+ .ai_fifo = &ai_fifo_60xx,
+ .has_8255 = 0,
+ },
+ [BOARD_PCIDAS6071] = {
+ .name = "pci-das6071",
+ .ai_se_chans = 64,
+ .ai_bits = 12,
+ .ai_speed = 800,
+ .ao_nchan = 2,
+ .ao_bits = 12,
+ .ao_scan_speed = 1000,
+ .layout = LAYOUT_60XX,
+ .ai_range_table = &ai_ranges_6052,
+ .ai_range_code = ai_range_code_6052,
+ .ao_range_table = &ao_ranges_6030,
+ .ao_range_code = ao_range_code_6030,
+ .ai_fifo = &ai_fifo_60xx,
+ .has_8255 = 0,
+ },
+ [BOARD_PCIDAS4020_12] = {
+ .name = "pci-das4020/12",
+ .ai_se_chans = 4,
+ .ai_bits = 12,
+ .ai_speed = 50,
+ .ao_bits = 12,
+ .ao_nchan = 2,
+ .ao_scan_speed = 0, /* no hardware pacing on ao */
+ .layout = LAYOUT_4020,
+ .ai_range_table = &ai_ranges_4020,
+ .ao_range_table = &ao_ranges_4020,
+ .ao_range_code = ao_range_code_4020,
+ .ai_fifo = &ai_fifo_4020,
+ .has_8255 = 1,
+ },
+#if 0
+ /*
+ * The device id for these boards is unknown
+ */
+
+ [BOARD_PCIDAS6402_16_JR] = {
+ .name = "pci-das6402/16/jr",
+ .ai_se_chans = 64,
+ .ai_bits = 16,
+ .ai_speed = 5000,
+ .ao_nchan = 0,
+ .ao_scan_speed = 10000,
+ .layout = LAYOUT_64XX,
+ .ai_range_table = &ai_ranges_64xx,
+ .ai_range_code = ai_range_code_64xx,
+ .ai_fifo = ai_fifo_64xx,
+ .has_8255 = 1,
+ },
+ [BOARD_PCIDAS64_M1_16_JR] = {
+ .name = "pci-das64/m1/16/jr",
+ .ai_se_chans = 64,
+ .ai_bits = 16,
+ .ai_speed = 1000,
+ .ao_nchan = 0,
+ .ao_scan_speed = 10000,
+ .layout = LAYOUT_64XX,
+ .ai_range_table = &ai_ranges_64_mx,
+ .ai_range_code = ai_range_code_64_mx,
+ .ai_fifo = ai_fifo_64xx,
+ .has_8255 = 1,
+ },
+ [BOARD_PCIDAS64_M2_16_JR] = {
+ .name = "pci-das64/m2/16/jr",
+ .ai_se_chans = 64,
+ .ai_bits = 16,
+ .ai_speed = 500,
+ .ao_nchan = 0,
+ .ao_scan_speed = 10000,
+ .layout = LAYOUT_64XX,
+ .ai_range_table = &ai_ranges_64_mx,
+ .ai_range_code = ai_range_code_64_mx,
+ .ai_fifo = ai_fifo_64xx,
+ .has_8255 = 1,
+ },
+ [BOARD_PCIDAS64_M3_16_JR] = {
+ .name = "pci-das64/m3/16/jr",
+ .ai_se_chans = 64,
+ .ai_bits = 16,
+ .ai_speed = 333,
+ .ao_nchan = 0,
+ .ao_scan_speed = 10000,
+ .layout = LAYOUT_64XX,
+ .ai_range_table = &ai_ranges_64_mx,
+ .ai_range_code = ai_range_code_64_mx,
+ .ai_fifo = ai_fifo_64xx,
+ .has_8255 = 1,
+ },
+ [BOARD_PCIDAS64_M1_14] = {
+ .name = "pci-das64/m1/14",
+ .ai_se_chans = 64,
+ .ai_bits = 14,
+ .ai_speed = 1000,
+ .ao_nchan = 2,
+ .ao_scan_speed = 10000,
+ .layout = LAYOUT_64XX,
+ .ai_range_table = &ai_ranges_64_mx,
+ .ai_range_code = ai_range_code_64_mx,
+ .ai_fifo = ai_fifo_64xx,
+ .has_8255 = 1,
+ },
+ [BOARD_PCIDAS64_M2_14] = {
+ .name = "pci-das64/m2/14",
+ .ai_se_chans = 64,
+ .ai_bits = 14,
+ .ai_speed = 500,
+ .ao_nchan = 2,
+ .ao_scan_speed = 10000,
+ .layout = LAYOUT_64XX,
+ .ai_range_table = &ai_ranges_64_mx,
+ .ai_range_code = ai_range_code_64_mx,
+ .ai_fifo = ai_fifo_64xx,
+ .has_8255 = 1,
+ },
+ [BOARD_PCIDAS64_M3_14] = {
+ .name = "pci-das64/m3/14",
+ .ai_se_chans = 64,
+ .ai_bits = 14,
+ .ai_speed = 333,
+ .ao_nchan = 2,
+ .ao_scan_speed = 10000,
+ .layout = LAYOUT_64XX,
+ .ai_range_table = &ai_ranges_64_mx,
+ .ai_range_code = ai_range_code_64_mx,
+ .ai_fifo = ai_fifo_64xx,
+ .has_8255 = 1,
+ },
+#endif
+};
+
+static inline unsigned short se_diff_bit_6xxx(struct comedi_device *dev,
+ int use_differential)
+{
+ const struct pcidas64_board *thisboard = dev->board_ptr;
+
+ if ((thisboard->layout == LAYOUT_64XX && !use_differential) ||
+ (thisboard->layout == LAYOUT_60XX && use_differential))
+ return ADC_SE_DIFF_BIT;
+
+ return 0;
+}
+
+struct ext_clock_info {
+ /* master clock divisor to use for scans with external master clock */
+ unsigned int divisor;
+ /* chanspec for master clock input when used as scan begin src */
+ unsigned int chanspec;
+};
+
+/* this structure is for data unique to this hardware driver. */
+struct pcidas64_private {
+ /* base addresses (physical) */
+ resource_size_t main_phys_iobase;
+ resource_size_t dio_counter_phys_iobase;
+ /* base addresses (ioremapped) */
+ void __iomem *plx9080_iobase;
+ void __iomem *main_iobase;
+ /* local address (used by dma controller) */
+ uint32_t local0_iobase;
+ uint32_t local1_iobase;
+ /* dma buffers for analog input */
+ uint16_t *ai_buffer[MAX_AI_DMA_RING_COUNT];
+ /* physical addresses of ai dma buffers */
+ dma_addr_t ai_buffer_bus_addr[MAX_AI_DMA_RING_COUNT];
+ /* array of ai dma descriptors read by plx9080,
+ * allocated to get proper alignment */
+ struct plx_dma_desc *ai_dma_desc;
+ /* physical address of ai dma descriptor array */
+ dma_addr_t ai_dma_desc_bus_addr;
+ /* index of the ai dma descriptor/buffer
+ * that is currently being used */
+ unsigned int ai_dma_index;
+ /* dma buffers for analog output */
+ uint16_t *ao_buffer[AO_DMA_RING_COUNT];
+ /* physical addresses of ao dma buffers */
+ dma_addr_t ao_buffer_bus_addr[AO_DMA_RING_COUNT];
+ struct plx_dma_desc *ao_dma_desc;
+ dma_addr_t ao_dma_desc_bus_addr;
+ /* keeps track of buffer where the next ao sample should go */
+ unsigned int ao_dma_index;
+ unsigned int hw_revision; /* stc chip hardware revision number */
+ /* last bits sent to INTR_ENABLE_REG register */
+ unsigned int intr_enable_bits;
+ /* last bits sent to ADC_CONTROL1_REG register */
+ uint16_t adc_control1_bits;
+ /* last bits sent to FIFO_SIZE_REG register */
+ uint16_t fifo_size_bits;
+ /* last bits sent to HW_CONFIG_REG register */
+ uint16_t hw_config_bits;
+ uint16_t dac_control1_bits;
+ /* last bits written to plx9080 control register */
+ uint32_t plx_control_bits;
+ /* last bits written to plx interrupt control and status register */
+ uint32_t plx_intcsr_bits;
+ /* index of calibration source readable through ai ch0 */
+ int calibration_source;
+ /* bits written to i2c calibration/range register */
+ uint8_t i2c_cal_range_bits;
+ /* configure digital triggers to trigger on falling edge */
+ unsigned int ext_trig_falling;
+ short ai_cmd_running;
+ unsigned int ai_fifo_segment_length;
+ struct ext_clock_info ext_clock;
+ unsigned short ao_bounce_buffer[DAC_FIFO_SIZE];
+};
+
+static unsigned int ai_range_bits_6xxx(const struct comedi_device *dev,
+ unsigned int range_index)
+{
+ const struct pcidas64_board *thisboard = dev->board_ptr;
+
+ return thisboard->ai_range_code[range_index] << 8;
+}
+
+static unsigned int hw_revision(const struct comedi_device *dev,
+ uint16_t hw_status_bits)
+{
+ const struct pcidas64_board *thisboard = dev->board_ptr;
+
+ if (thisboard->layout == LAYOUT_4020)
+ return (hw_status_bits >> 13) & 0x7;
+
+ return (hw_status_bits >> 12) & 0xf;
+}
+
+static void set_dac_range_bits(struct comedi_device *dev,
+ uint16_t *bits, unsigned int channel,
+ unsigned int range)
+{
+ const struct pcidas64_board *thisboard = dev->board_ptr;
+ unsigned int code = thisboard->ao_range_code[range];
+
+ if (channel > 1)
+ dev_err(dev->class_dev, "bug! bad channel?\n");
+ if (code & ~0x3)
+ dev_err(dev->class_dev, "bug! bad range code?\n");
+
+ *bits &= ~(0x3 << (2 * channel));
+ *bits |= code << (2 * channel);
+};
+
+static inline int ao_cmd_is_supported(const struct pcidas64_board *board)
+{
+ return board->ao_nchan && board->layout != LAYOUT_4020;
+}
+
+static void abort_dma(struct comedi_device *dev, unsigned int channel)
+{
+ struct pcidas64_private *devpriv = dev->private;
+ unsigned long flags;
+
+ /* spinlock for plx dma control/status reg */
+ spin_lock_irqsave(&dev->spinlock, flags);
+
+ plx9080_abort_dma(devpriv->plx9080_iobase, channel);
+
+ spin_unlock_irqrestore(&dev->spinlock, flags);
+}
+
+static void disable_plx_interrupts(struct comedi_device *dev)
+{
+ struct pcidas64_private *devpriv = dev->private;
+
+ devpriv->plx_intcsr_bits = 0;
+ writel(devpriv->plx_intcsr_bits,
+ devpriv->plx9080_iobase + PLX_INTRCS_REG);
+}
+
+static void disable_ai_interrupts(struct comedi_device *dev)
+{
+ struct pcidas64_private *devpriv = dev->private;
+ unsigned long flags;
+
+ spin_lock_irqsave(&dev->spinlock, flags);
+ devpriv->intr_enable_bits &=
+ ~EN_ADC_INTR_SRC_BIT & ~EN_ADC_DONE_INTR_BIT &
+ ~EN_ADC_ACTIVE_INTR_BIT & ~EN_ADC_STOP_INTR_BIT &
+ ~EN_ADC_OVERRUN_BIT & ~ADC_INTR_SRC_MASK;
+ writew(devpriv->intr_enable_bits,
+ devpriv->main_iobase + INTR_ENABLE_REG);
+ spin_unlock_irqrestore(&dev->spinlock, flags);
+}
+
+static void enable_ai_interrupts(struct comedi_device *dev,
+ const struct comedi_cmd *cmd)
+{
+ const struct pcidas64_board *thisboard = dev->board_ptr;
+ struct pcidas64_private *devpriv = dev->private;
+ uint32_t bits;
+ unsigned long flags;
+
+ bits = EN_ADC_OVERRUN_BIT | EN_ADC_DONE_INTR_BIT |
+ EN_ADC_ACTIVE_INTR_BIT | EN_ADC_STOP_INTR_BIT;
+ /* Use pio transfer and interrupt on end of conversion
+ * if CMDF_WAKE_EOS flag is set. */
+ if (cmd->flags & CMDF_WAKE_EOS) {
+ /* 4020 doesn't support pio transfers except for fifo dregs */
+ if (thisboard->layout != LAYOUT_4020)
+ bits |= ADC_INTR_EOSCAN_BITS | EN_ADC_INTR_SRC_BIT;
+ }
+ spin_lock_irqsave(&dev->spinlock, flags);
+ devpriv->intr_enable_bits |= bits;
+ writew(devpriv->intr_enable_bits,
+ devpriv->main_iobase + INTR_ENABLE_REG);
+ spin_unlock_irqrestore(&dev->spinlock, flags);
+}
+
+/* initialize plx9080 chip */
+static void init_plx9080(struct comedi_device *dev)
+{
+ const struct pcidas64_board *thisboard = dev->board_ptr;
+ struct pcidas64_private *devpriv = dev->private;
+ uint32_t bits;
+ void __iomem *plx_iobase = devpriv->plx9080_iobase;
+
+ devpriv->plx_control_bits =
+ readl(devpriv->plx9080_iobase + PLX_CONTROL_REG);
+
+#ifdef __BIG_ENDIAN
+ bits = BIGEND_DMA0 | BIGEND_DMA1;
+#else
+ bits = 0;
+#endif
+ writel(bits, devpriv->plx9080_iobase + PLX_BIGEND_REG);
+
+ disable_plx_interrupts(dev);
+
+ abort_dma(dev, 0);
+ abort_dma(dev, 1);
+
+ /* configure dma0 mode */
+ bits = 0;
+ /* enable ready input, not sure if this is necessary */
+ bits |= PLX_DMA_EN_READYIN_BIT;
+ /* enable bterm, not sure if this is necessary */
+ bits |= PLX_EN_BTERM_BIT;
+ /* enable dma chaining */
+ bits |= PLX_EN_CHAIN_BIT;
+ /* enable interrupt on dma done
+ * (probably don't need this, since chain never finishes) */
+ bits |= PLX_EN_DMA_DONE_INTR_BIT;
+ /* don't increment local address during transfers
+ * (we are transferring from a fixed fifo register) */
+ bits |= PLX_LOCAL_ADDR_CONST_BIT;
+ /* route dma interrupt to pci bus */
+ bits |= PLX_DMA_INTR_PCI_BIT;
+ /* enable demand mode */
+ bits |= PLX_DEMAND_MODE_BIT;
+ /* enable local burst mode */
+ bits |= PLX_DMA_LOCAL_BURST_EN_BIT;
+ /* 4020 uses 32 bit dma */
+ if (thisboard->layout == LAYOUT_4020)
+ bits |= PLX_LOCAL_BUS_32_WIDE_BITS;
+ else /* localspace0 bus is 16 bits wide */
+ bits |= PLX_LOCAL_BUS_16_WIDE_BITS;
+ writel(bits, plx_iobase + PLX_DMA1_MODE_REG);
+ if (ao_cmd_is_supported(thisboard))
+ writel(bits, plx_iobase + PLX_DMA0_MODE_REG);
+
+ /* enable interrupts on plx 9080 */
+ devpriv->plx_intcsr_bits |=
+ ICS_AERR | ICS_PERR | ICS_PIE | ICS_PLIE | ICS_PAIE | ICS_LIE |
+ ICS_DMA0_E | ICS_DMA1_E;
+ writel(devpriv->plx_intcsr_bits,
+ devpriv->plx9080_iobase + PLX_INTRCS_REG);
+}
+
+static void disable_ai_pacing(struct comedi_device *dev)
+{
+ struct pcidas64_private *devpriv = dev->private;
+ unsigned long flags;
+
+ disable_ai_interrupts(dev);
+
+ spin_lock_irqsave(&dev->spinlock, flags);
+ devpriv->adc_control1_bits &= ~ADC_SW_GATE_BIT;
+ writew(devpriv->adc_control1_bits,
+ devpriv->main_iobase + ADC_CONTROL1_REG);
+ spin_unlock_irqrestore(&dev->spinlock, flags);
+
+ /* disable pacing, triggering, etc */
+ writew(ADC_DMA_DISABLE_BIT | ADC_SOFT_GATE_BITS | ADC_GATE_LEVEL_BIT,
+ devpriv->main_iobase + ADC_CONTROL0_REG);
+}
+
+static int set_ai_fifo_segment_length(struct comedi_device *dev,
+ unsigned int num_entries)
+{
+ const struct pcidas64_board *thisboard = dev->board_ptr;
+ struct pcidas64_private *devpriv = dev->private;
+ static const int increment_size = 0x100;
+ const struct hw_fifo_info *const fifo = thisboard->ai_fifo;
+ unsigned int num_increments;
+ uint16_t bits;
+
+ if (num_entries < increment_size)
+ num_entries = increment_size;
+ if (num_entries > fifo->max_segment_length)
+ num_entries = fifo->max_segment_length;
+
+ /* 1 == 256 entries, 2 == 512 entries, etc */
+ num_increments = (num_entries + increment_size / 2) / increment_size;
+
+ bits = (~(num_increments - 1)) & fifo->fifo_size_reg_mask;
+ devpriv->fifo_size_bits &= ~fifo->fifo_size_reg_mask;
+ devpriv->fifo_size_bits |= bits;
+ writew(devpriv->fifo_size_bits,
+ devpriv->main_iobase + FIFO_SIZE_REG);
+
+ devpriv->ai_fifo_segment_length = num_increments * increment_size;
+
+ return devpriv->ai_fifo_segment_length;
+}
+
+/* adjusts the size of hardware fifo (which determines block size for dma xfers) */
+static int set_ai_fifo_size(struct comedi_device *dev, unsigned int num_samples)
+{
+ const struct pcidas64_board *thisboard = dev->board_ptr;
+ unsigned int num_fifo_entries;
+ int retval;
+ const struct hw_fifo_info *const fifo = thisboard->ai_fifo;
+
+ num_fifo_entries = num_samples / fifo->sample_packing_ratio;
+
+ retval = set_ai_fifo_segment_length(dev,
+ num_fifo_entries /
+ fifo->num_segments);
+ if (retval < 0)
+ return retval;
+
+ num_samples = retval * fifo->num_segments * fifo->sample_packing_ratio;
+
+ return num_samples;
+}
+
+/* query length of fifo */
+static unsigned int ai_fifo_size(struct comedi_device *dev)
+{
+ const struct pcidas64_board *thisboard = dev->board_ptr;
+ struct pcidas64_private *devpriv = dev->private;
+
+ return devpriv->ai_fifo_segment_length *
+ thisboard->ai_fifo->num_segments *
+ thisboard->ai_fifo->sample_packing_ratio;
+}
+
+static void init_stc_registers(struct comedi_device *dev)
+{
+ const struct pcidas64_board *thisboard = dev->board_ptr;
+ struct pcidas64_private *devpriv = dev->private;
+ uint16_t bits;
+ unsigned long flags;
+
+ spin_lock_irqsave(&dev->spinlock, flags);
+
+ /* bit should be set for 6025,
+ * although docs say boards with <= 16 chans should be cleared XXX */
+ if (1)
+ devpriv->adc_control1_bits |= ADC_QUEUE_CONFIG_BIT;
+ writew(devpriv->adc_control1_bits,
+ devpriv->main_iobase + ADC_CONTROL1_REG);
+
+ /* 6402/16 manual says this register must be initialized to 0xff? */
+ writew(0xff, devpriv->main_iobase + ADC_SAMPLE_INTERVAL_UPPER_REG);
+
+ bits = SLOW_DAC_BIT | DMA_CH_SELECT_BIT;
+ if (thisboard->layout == LAYOUT_4020)
+ bits |= INTERNAL_CLOCK_4020_BITS;
+ devpriv->hw_config_bits |= bits;
+ writew(devpriv->hw_config_bits,
+ devpriv->main_iobase + HW_CONFIG_REG);
+
+ writew(0, devpriv->main_iobase + DAQ_SYNC_REG);
+ writew(0, devpriv->main_iobase + CALIBRATION_REG);
+
+ spin_unlock_irqrestore(&dev->spinlock, flags);
+
+ /* set fifos to maximum size */
+ devpriv->fifo_size_bits |= DAC_FIFO_BITS;
+ set_ai_fifo_segment_length(dev,
+ thisboard->ai_fifo->max_segment_length);
+
+ devpriv->dac_control1_bits = DAC_OUTPUT_ENABLE_BIT;
+ devpriv->intr_enable_bits =
+ /* EN_DAC_INTR_SRC_BIT | DAC_INTR_QEMPTY_BITS | */
+ EN_DAC_DONE_INTR_BIT | EN_DAC_UNDERRUN_BIT;
+ writew(devpriv->intr_enable_bits,
+ devpriv->main_iobase + INTR_ENABLE_REG);
+
+ disable_ai_pacing(dev);
+};
+
+static int alloc_and_init_dma_members(struct comedi_device *dev)
+{
+ const struct pcidas64_board *thisboard = dev->board_ptr;
+ struct pci_dev *pcidev = comedi_to_pci_dev(dev);
+ struct pcidas64_private *devpriv = dev->private;
+ int i;
+
+ /* allocate pci dma buffers */
+ for (i = 0; i < ai_dma_ring_count(thisboard); i++) {
+ devpriv->ai_buffer[i] =
+ pci_alloc_consistent(pcidev, DMA_BUFFER_SIZE,
+ &devpriv->ai_buffer_bus_addr[i]);
+ if (!devpriv->ai_buffer[i])
+ return -ENOMEM;
+ }
+ for (i = 0; i < AO_DMA_RING_COUNT; i++) {
+ if (ao_cmd_is_supported(thisboard)) {
+ devpriv->ao_buffer[i] =
+ pci_alloc_consistent(pcidev, DMA_BUFFER_SIZE,
+ &devpriv->
+ ao_buffer_bus_addr[i]);
+ if (!devpriv->ao_buffer[i])
+ return -ENOMEM;
+ }
+ }
+ /* allocate dma descriptors */
+ devpriv->ai_dma_desc =
+ pci_alloc_consistent(pcidev, sizeof(struct plx_dma_desc) *
+ ai_dma_ring_count(thisboard),
+ &devpriv->ai_dma_desc_bus_addr);
+ if (!devpriv->ai_dma_desc)
+ return -ENOMEM;
+
+ if (ao_cmd_is_supported(thisboard)) {
+ devpriv->ao_dma_desc =
+ pci_alloc_consistent(pcidev,
+ sizeof(struct plx_dma_desc) *
+ AO_DMA_RING_COUNT,
+ &devpriv->ao_dma_desc_bus_addr);
+ if (!devpriv->ao_dma_desc)
+ return -ENOMEM;
+ }
+ /* initialize dma descriptors */
+ for (i = 0; i < ai_dma_ring_count(thisboard); i++) {
+ devpriv->ai_dma_desc[i].pci_start_addr =
+ cpu_to_le32(devpriv->ai_buffer_bus_addr[i]);
+ if (thisboard->layout == LAYOUT_4020)
+ devpriv->ai_dma_desc[i].local_start_addr =
+ cpu_to_le32(devpriv->local1_iobase +
+ ADC_FIFO_REG);
+ else
+ devpriv->ai_dma_desc[i].local_start_addr =
+ cpu_to_le32(devpriv->local0_iobase +
+ ADC_FIFO_REG);
+ devpriv->ai_dma_desc[i].transfer_size = cpu_to_le32(0);
+ devpriv->ai_dma_desc[i].next =
+ cpu_to_le32((devpriv->ai_dma_desc_bus_addr +
+ ((i + 1) % ai_dma_ring_count(thisboard)) *
+ sizeof(devpriv->ai_dma_desc[0])) |
+ PLX_DESC_IN_PCI_BIT | PLX_INTR_TERM_COUNT |
+ PLX_XFER_LOCAL_TO_PCI);
+ }
+ if (ao_cmd_is_supported(thisboard)) {
+ for (i = 0; i < AO_DMA_RING_COUNT; i++) {
+ devpriv->ao_dma_desc[i].pci_start_addr =
+ cpu_to_le32(devpriv->ao_buffer_bus_addr[i]);
+ devpriv->ao_dma_desc[i].local_start_addr =
+ cpu_to_le32(devpriv->local0_iobase +
+ DAC_FIFO_REG);
+ devpriv->ao_dma_desc[i].transfer_size = cpu_to_le32(0);
+ devpriv->ao_dma_desc[i].next =
+ cpu_to_le32((devpriv->ao_dma_desc_bus_addr +
+ ((i + 1) % (AO_DMA_RING_COUNT)) *
+ sizeof(devpriv->ao_dma_desc[0])) |
+ PLX_DESC_IN_PCI_BIT |
+ PLX_INTR_TERM_COUNT);
+ }
+ }
+ return 0;
+}
+
+static void cb_pcidas64_free_dma(struct comedi_device *dev)
+{
+ const struct pcidas64_board *thisboard = dev->board_ptr;
+ struct pci_dev *pcidev = comedi_to_pci_dev(dev);
+ struct pcidas64_private *devpriv = dev->private;
+ int i;
+
+ if (!devpriv)
+ return;
+
+ /* free pci dma buffers */
+ for (i = 0; i < ai_dma_ring_count(thisboard); i++) {
+ if (devpriv->ai_buffer[i])
+ pci_free_consistent(pcidev,
+ DMA_BUFFER_SIZE,
+ devpriv->ai_buffer[i],
+ devpriv->ai_buffer_bus_addr[i]);
+ }
+ for (i = 0; i < AO_DMA_RING_COUNT; i++) {
+ if (devpriv->ao_buffer[i])
+ pci_free_consistent(pcidev,
+ DMA_BUFFER_SIZE,
+ devpriv->ao_buffer[i],
+ devpriv->ao_buffer_bus_addr[i]);
+ }
+ /* free dma descriptors */
+ if (devpriv->ai_dma_desc)
+ pci_free_consistent(pcidev,
+ sizeof(struct plx_dma_desc) *
+ ai_dma_ring_count(thisboard),
+ devpriv->ai_dma_desc,
+ devpriv->ai_dma_desc_bus_addr);
+ if (devpriv->ao_dma_desc)
+ pci_free_consistent(pcidev,
+ sizeof(struct plx_dma_desc) *
+ AO_DMA_RING_COUNT,
+ devpriv->ao_dma_desc,
+ devpriv->ao_dma_desc_bus_addr);
+}
+
+static inline void warn_external_queue(struct comedi_device *dev)
+{
+ dev_err(dev->class_dev,
+ "AO command and AI external channel queue cannot be used simultaneously\n");
+ dev_err(dev->class_dev,
+ "Use internal AI channel queue (channels must be consecutive and use same range/aref)\n");
+}
+
+/* Their i2c requires a huge delay on setting clock or data high for some reason */
+static const int i2c_high_udelay = 1000;
+static const int i2c_low_udelay = 10;
+
+/* set i2c data line high or low */
+static void i2c_set_sda(struct comedi_device *dev, int state)
+{
+ struct pcidas64_private *devpriv = dev->private;
+ static const int data_bit = CTL_EE_W;
+ void __iomem *plx_control_addr = devpriv->plx9080_iobase +
+ PLX_CONTROL_REG;
+
+ if (state) {
+ /* set data line high */
+ devpriv->plx_control_bits &= ~data_bit;
+ writel(devpriv->plx_control_bits, plx_control_addr);
+ udelay(i2c_high_udelay);
+ } else { /* set data line low */
+
+ devpriv->plx_control_bits |= data_bit;
+ writel(devpriv->plx_control_bits, plx_control_addr);
+ udelay(i2c_low_udelay);
+ }
+}
+
+/* set i2c clock line high or low */
+static void i2c_set_scl(struct comedi_device *dev, int state)
+{
+ struct pcidas64_private *devpriv = dev->private;
+ static const int clock_bit = CTL_USERO;
+ void __iomem *plx_control_addr = devpriv->plx9080_iobase +
+ PLX_CONTROL_REG;
+
+ if (state) {
+ /* set clock line high */
+ devpriv->plx_control_bits &= ~clock_bit;
+ writel(devpriv->plx_control_bits, plx_control_addr);
+ udelay(i2c_high_udelay);
+ } else { /* set clock line low */
+
+ devpriv->plx_control_bits |= clock_bit;
+ writel(devpriv->plx_control_bits, plx_control_addr);
+ udelay(i2c_low_udelay);
+ }
+}
+
+static void i2c_write_byte(struct comedi_device *dev, uint8_t byte)
+{
+ uint8_t bit;
+ unsigned int num_bits = 8;
+
+ for (bit = 1 << (num_bits - 1); bit; bit >>= 1) {
+ i2c_set_scl(dev, 0);
+ if ((byte & bit))
+ i2c_set_sda(dev, 1);
+ else
+ i2c_set_sda(dev, 0);
+ i2c_set_scl(dev, 1);
+ }
+}
+
+/* we can't really read the lines, so fake it */
+static int i2c_read_ack(struct comedi_device *dev)
+{
+ i2c_set_scl(dev, 0);
+ i2c_set_sda(dev, 1);
+ i2c_set_scl(dev, 1);
+
+ return 0; /* return fake acknowledge bit */
+}
+
+/* send start bit */
+static void i2c_start(struct comedi_device *dev)
+{
+ i2c_set_scl(dev, 1);
+ i2c_set_sda(dev, 1);
+ i2c_set_sda(dev, 0);
+}
+
+/* send stop bit */
+static void i2c_stop(struct comedi_device *dev)
+{
+ i2c_set_scl(dev, 0);
+ i2c_set_sda(dev, 0);
+ i2c_set_scl(dev, 1);
+ i2c_set_sda(dev, 1);
+}
+
+static void i2c_write(struct comedi_device *dev, unsigned int address,
+ const uint8_t *data, unsigned int length)
+{
+ struct pcidas64_private *devpriv = dev->private;
+ unsigned int i;
+ uint8_t bitstream;
+ static const int read_bit = 0x1;
+
+ /* XXX need mutex to prevent simultaneous attempts to access
+ * eeprom and i2c bus */
+
+ /* make sure we dont send anything to eeprom */
+ devpriv->plx_control_bits &= ~CTL_EE_CS;
+
+ i2c_stop(dev);
+ i2c_start(dev);
+
+ /* send address and write bit */
+ bitstream = (address << 1) & ~read_bit;
+ i2c_write_byte(dev, bitstream);
+
+ /* get acknowledge */
+ if (i2c_read_ack(dev) != 0) {
+ dev_err(dev->class_dev, "failed: no acknowledge\n");
+ i2c_stop(dev);
+ return;
+ }
+ /* write data bytes */
+ for (i = 0; i < length; i++) {
+ i2c_write_byte(dev, data[i]);
+ if (i2c_read_ack(dev) != 0) {
+ dev_err(dev->class_dev, "failed: no acknowledge\n");
+ i2c_stop(dev);
+ return;
+ }
+ }
+ i2c_stop(dev);
+}
+
+static int cb_pcidas64_ai_eoc(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned long context)
+{
+ const struct pcidas64_board *thisboard = dev->board_ptr;
+ struct pcidas64_private *devpriv = dev->private;
+ unsigned int status;
+
+ status = readw(devpriv->main_iobase + HW_STATUS_REG);
+ if (thisboard->layout == LAYOUT_4020) {
+ status = readw(devpriv->main_iobase + ADC_WRITE_PNTR_REG);
+ if (status)
+ return 0;
+ } else {
+ if (pipe_full_bits(status))
+ return 0;
+ }
+ return -EBUSY;
+}
+
+static int ai_rinsn(struct comedi_device *dev, struct comedi_subdevice *s,
+ struct comedi_insn *insn, unsigned int *data)
+{
+ const struct pcidas64_board *thisboard = dev->board_ptr;
+ struct pcidas64_private *devpriv = dev->private;
+ unsigned int bits = 0, n;
+ unsigned int channel, range, aref;
+ unsigned long flags;
+ int ret;
+
+ channel = CR_CHAN(insn->chanspec);
+ range = CR_RANGE(insn->chanspec);
+ aref = CR_AREF(insn->chanspec);
+
+ /* disable card's analog input interrupt sources and pacing */
+ /* 4020 generates dac done interrupts even though they are disabled */
+ disable_ai_pacing(dev);
+
+ spin_lock_irqsave(&dev->spinlock, flags);
+ if (insn->chanspec & CR_ALT_FILTER)
+ devpriv->adc_control1_bits |= ADC_DITHER_BIT;
+ else
+ devpriv->adc_control1_bits &= ~ADC_DITHER_BIT;
+ writew(devpriv->adc_control1_bits,
+ devpriv->main_iobase + ADC_CONTROL1_REG);
+ spin_unlock_irqrestore(&dev->spinlock, flags);
+
+ if (thisboard->layout != LAYOUT_4020) {
+ /* use internal queue */
+ devpriv->hw_config_bits &= ~EXT_QUEUE_BIT;
+ writew(devpriv->hw_config_bits,
+ devpriv->main_iobase + HW_CONFIG_REG);
+
+ /* ALT_SOURCE is internal calibration reference */
+ if (insn->chanspec & CR_ALT_SOURCE) {
+ unsigned int cal_en_bit;
+
+ if (thisboard->layout == LAYOUT_60XX)
+ cal_en_bit = CAL_EN_60XX_BIT;
+ else
+ cal_en_bit = CAL_EN_64XX_BIT;
+ /* select internal reference source to connect
+ * to channel 0 */
+ writew(cal_en_bit |
+ adc_src_bits(devpriv->calibration_source),
+ devpriv->main_iobase + CALIBRATION_REG);
+ } else {
+ /* make sure internal calibration source
+ * is turned off */
+ writew(0, devpriv->main_iobase + CALIBRATION_REG);
+ }
+ /* load internal queue */
+ bits = 0;
+ /* set gain */
+ bits |= ai_range_bits_6xxx(dev, CR_RANGE(insn->chanspec));
+ /* set single-ended / differential */
+ bits |= se_diff_bit_6xxx(dev, aref == AREF_DIFF);
+ if (aref == AREF_COMMON)
+ bits |= ADC_COMMON_BIT;
+ bits |= adc_chan_bits(channel);
+ /* set stop channel */
+ writew(adc_chan_bits(channel),
+ devpriv->main_iobase + ADC_QUEUE_HIGH_REG);
+ /* set start channel, and rest of settings */
+ writew(bits, devpriv->main_iobase + ADC_QUEUE_LOAD_REG);
+ } else {
+ uint8_t old_cal_range_bits = devpriv->i2c_cal_range_bits;
+
+ devpriv->i2c_cal_range_bits &= ~ADC_SRC_4020_MASK;
+ if (insn->chanspec & CR_ALT_SOURCE) {
+ devpriv->i2c_cal_range_bits |=
+ adc_src_4020_bits(devpriv->calibration_source);
+ } else { /* select BNC inputs */
+ devpriv->i2c_cal_range_bits |= adc_src_4020_bits(4);
+ }
+ /* select range */
+ if (range == 0)
+ devpriv->i2c_cal_range_bits |= attenuate_bit(channel);
+ else
+ devpriv->i2c_cal_range_bits &= ~attenuate_bit(channel);
+ /* update calibration/range i2c register only if necessary,
+ * as it is very slow */
+ if (old_cal_range_bits != devpriv->i2c_cal_range_bits) {
+ uint8_t i2c_data = devpriv->i2c_cal_range_bits;
+
+ i2c_write(dev, RANGE_CAL_I2C_ADDR, &i2c_data,
+ sizeof(i2c_data));
+ }
+
+ /* 4020 manual asks that sample interval register to be set
+ * before writing to convert register.
+ * Using somewhat arbitrary setting of 4 master clock ticks
+ * = 0.1 usec */
+ writew(0, devpriv->main_iobase + ADC_SAMPLE_INTERVAL_UPPER_REG);
+ writew(2, devpriv->main_iobase + ADC_SAMPLE_INTERVAL_LOWER_REG);
+ }
+
+ for (n = 0; n < insn->n; n++) {
+ /* clear adc buffer (inside loop for 4020 sake) */
+ writew(0, devpriv->main_iobase + ADC_BUFFER_CLEAR_REG);
+
+ /* trigger conversion, bits sent only matter for 4020 */
+ writew(adc_convert_chan_4020_bits(CR_CHAN(insn->chanspec)),
+ devpriv->main_iobase + ADC_CONVERT_REG);
+
+ /* wait for data */
+ ret = comedi_timeout(dev, s, insn, cb_pcidas64_ai_eoc, 0);
+ if (ret)
+ return ret;
+
+ if (thisboard->layout == LAYOUT_4020)
+ data[n] = readl(dev->mmio + ADC_FIFO_REG) & 0xffff;
+ else
+ data[n] = readw(devpriv->main_iobase + PIPE1_READ_REG);
+ }
+
+ return n;
+}
+
+static int ai_config_calibration_source(struct comedi_device *dev,
+ unsigned int *data)
+{
+ const struct pcidas64_board *thisboard = dev->board_ptr;
+ struct pcidas64_private *devpriv = dev->private;
+ unsigned int source = data[1];
+ int num_calibration_sources;
+
+ if (thisboard->layout == LAYOUT_60XX)
+ num_calibration_sources = 16;
+ else
+ num_calibration_sources = 8;
+ if (source >= num_calibration_sources) {
+ dev_dbg(dev->class_dev, "invalid calibration source: %i\n",
+ source);
+ return -EINVAL;
+ }
+
+ devpriv->calibration_source = source;
+
+ return 2;
+}
+
+static int ai_config_block_size(struct comedi_device *dev, unsigned int *data)
+{
+ const struct pcidas64_board *thisboard = dev->board_ptr;
+ int fifo_size;
+ const struct hw_fifo_info *const fifo = thisboard->ai_fifo;
+ unsigned int block_size, requested_block_size;
+ int retval;
+
+ requested_block_size = data[1];
+
+ if (requested_block_size) {
+ fifo_size = requested_block_size * fifo->num_segments /
+ bytes_in_sample;
+
+ retval = set_ai_fifo_size(dev, fifo_size);
+ if (retval < 0)
+ return retval;
+ }
+
+ block_size = ai_fifo_size(dev) / fifo->num_segments * bytes_in_sample;
+
+ data[1] = block_size;
+
+ return 2;
+}
+
+static int ai_config_master_clock_4020(struct comedi_device *dev,
+ unsigned int *data)
+{
+ struct pcidas64_private *devpriv = dev->private;
+ unsigned int divisor = data[4];
+ int retval = 0;
+
+ if (divisor < 2) {
+ divisor = 2;
+ retval = -EAGAIN;
+ }
+
+ switch (data[1]) {
+ case COMEDI_EV_SCAN_BEGIN:
+ devpriv->ext_clock.divisor = divisor;
+ devpriv->ext_clock.chanspec = data[2];
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ data[4] = divisor;
+
+ return retval ? retval : 5;
+}
+
+/* XXX could add support for 60xx series */
+static int ai_config_master_clock(struct comedi_device *dev, unsigned int *data)
+{
+ const struct pcidas64_board *thisboard = dev->board_ptr;
+
+ switch (thisboard->layout) {
+ case LAYOUT_4020:
+ return ai_config_master_clock_4020(dev, data);
+ default:
+ return -EINVAL;
+ }
+
+ return -EINVAL;
+}
+
+static int ai_config_insn(struct comedi_device *dev, struct comedi_subdevice *s,
+ struct comedi_insn *insn, unsigned int *data)
+{
+ int id = data[0];
+
+ switch (id) {
+ case INSN_CONFIG_ALT_SOURCE:
+ return ai_config_calibration_source(dev, data);
+ case INSN_CONFIG_BLOCK_SIZE:
+ return ai_config_block_size(dev, data);
+ case INSN_CONFIG_TIMER_1:
+ return ai_config_master_clock(dev, data);
+ default:
+ return -EINVAL;
+ }
+ return -EINVAL;
+}
+
+/* Gets nearest achievable timing given master clock speed, does not
+ * take into account possible minimum/maximum divisor values. Used
+ * by other timing checking functions. */
+static unsigned int get_divisor(unsigned int ns, unsigned int flags)
+{
+ unsigned int divisor;
+
+ switch (flags & CMDF_ROUND_MASK) {
+ case CMDF_ROUND_UP:
+ divisor = (ns + TIMER_BASE - 1) / TIMER_BASE;
+ break;
+ case CMDF_ROUND_DOWN:
+ divisor = ns / TIMER_BASE;
+ break;
+ case CMDF_ROUND_NEAREST:
+ default:
+ divisor = (ns + TIMER_BASE / 2) / TIMER_BASE;
+ break;
+ }
+ return divisor;
+}
+
+/* utility function that rounds desired timing to an achievable time, and
+ * sets cmd members appropriately.
+ * adc paces conversions from master clock by dividing by (x + 3) where x is 24 bit number
+ */
+static void check_adc_timing(struct comedi_device *dev, struct comedi_cmd *cmd)
+{
+ const struct pcidas64_board *thisboard = dev->board_ptr;
+ unsigned long long convert_divisor = 0;
+ unsigned int scan_divisor;
+ static const int min_convert_divisor = 3;
+ static const int max_convert_divisor =
+ max_counter_value + min_convert_divisor;
+ static const int min_scan_divisor_4020 = 2;
+ unsigned long long max_scan_divisor, min_scan_divisor;
+
+ if (cmd->convert_src == TRIG_TIMER) {
+ if (thisboard->layout == LAYOUT_4020) {
+ cmd->convert_arg = 0;
+ } else {
+ convert_divisor = get_divisor(cmd->convert_arg,
+ cmd->flags);
+ if (convert_divisor > max_convert_divisor)
+ convert_divisor = max_convert_divisor;
+ if (convert_divisor < min_convert_divisor)
+ convert_divisor = min_convert_divisor;
+ cmd->convert_arg = convert_divisor * TIMER_BASE;
+ }
+ } else if (cmd->convert_src == TRIG_NOW) {
+ cmd->convert_arg = 0;
+ }
+
+ if (cmd->scan_begin_src == TRIG_TIMER) {
+ scan_divisor = get_divisor(cmd->scan_begin_arg, cmd->flags);
+ if (cmd->convert_src == TRIG_TIMER) {
+ min_scan_divisor = convert_divisor * cmd->chanlist_len;
+ max_scan_divisor =
+ (convert_divisor * cmd->chanlist_len - 1) +
+ max_counter_value;
+ } else {
+ min_scan_divisor = min_scan_divisor_4020;
+ max_scan_divisor = max_counter_value + min_scan_divisor;
+ }
+ if (scan_divisor > max_scan_divisor)
+ scan_divisor = max_scan_divisor;
+ if (scan_divisor < min_scan_divisor)
+ scan_divisor = min_scan_divisor;
+ cmd->scan_begin_arg = scan_divisor * TIMER_BASE;
+ }
+}
+
+static int cb_pcidas64_ai_check_chanlist(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_cmd *cmd)
+{
+ const struct pcidas64_board *board = dev->board_ptr;
+ unsigned int aref0 = CR_AREF(cmd->chanlist[0]);
+ int i;
+
+ for (i = 1; i < cmd->chanlist_len; i++) {
+ unsigned int aref = CR_AREF(cmd->chanlist[i]);
+
+ if (aref != aref0) {
+ dev_dbg(dev->class_dev,
+ "all elements in chanlist must use the same analog reference\n");
+ return -EINVAL;
+ }
+ }
+
+ if (board->layout == LAYOUT_4020) {
+ unsigned int chan0 = CR_CHAN(cmd->chanlist[0]);
+
+ for (i = 1; i < cmd->chanlist_len; i++) {
+ unsigned int chan = CR_CHAN(cmd->chanlist[i]);
+
+ if (chan != (chan0 + i)) {
+ dev_dbg(dev->class_dev,
+ "chanlist must use consecutive channels\n");
+ return -EINVAL;
+ }
+ }
+ if (cmd->chanlist_len == 3) {
+ dev_dbg(dev->class_dev,
+ "chanlist cannot be 3 channels long, use 1, 2, or 4 channels\n");
+ return -EINVAL;
+ }
+ }
+
+ return 0;
+}
+
+static int ai_cmdtest(struct comedi_device *dev, struct comedi_subdevice *s,
+ struct comedi_cmd *cmd)
+{
+ const struct pcidas64_board *thisboard = dev->board_ptr;
+ int err = 0;
+ unsigned int tmp_arg, tmp_arg2;
+ unsigned int triggers;
+
+ /* Step 1 : check if triggers are trivially valid */
+
+ err |= comedi_check_trigger_src(&cmd->start_src, TRIG_NOW | TRIG_EXT);
+
+ triggers = TRIG_TIMER;
+ if (thisboard->layout == LAYOUT_4020)
+ triggers |= TRIG_OTHER;
+ else
+ triggers |= TRIG_FOLLOW;
+ err |= comedi_check_trigger_src(&cmd->scan_begin_src, triggers);
+
+ triggers = TRIG_TIMER;
+ if (thisboard->layout == LAYOUT_4020)
+ triggers |= TRIG_NOW;
+ else
+ triggers |= TRIG_EXT;
+ err |= comedi_check_trigger_src(&cmd->convert_src, triggers);
+ err |= comedi_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
+ err |= comedi_check_trigger_src(&cmd->stop_src,
+ TRIG_COUNT | TRIG_EXT | TRIG_NONE);
+
+ if (err)
+ return 1;
+
+ /* Step 2a : make sure trigger sources are unique */
+
+ err |= comedi_check_trigger_is_unique(cmd->start_src);
+ err |= comedi_check_trigger_is_unique(cmd->scan_begin_src);
+ err |= comedi_check_trigger_is_unique(cmd->convert_src);
+ err |= comedi_check_trigger_is_unique(cmd->stop_src);
+
+ /* Step 2b : and mutually compatible */
+
+ if (cmd->convert_src == TRIG_EXT && cmd->scan_begin_src == TRIG_TIMER)
+ err |= -EINVAL;
+
+ if (err)
+ return 2;
+
+ /* Step 3: check if arguments are trivially valid */
+
+ switch (cmd->start_src) {
+ case TRIG_NOW:
+ err |= comedi_check_trigger_arg_is(&cmd->start_arg, 0);
+ break;
+ case TRIG_EXT:
+ /*
+ * start_arg is the CR_CHAN | CR_INVERT of the
+ * external trigger.
+ */
+ break;
+ }
+
+ if (cmd->convert_src == TRIG_TIMER) {
+ if (thisboard->layout == LAYOUT_4020) {
+ err |= comedi_check_trigger_arg_is(&cmd->convert_arg,
+ 0);
+ } else {
+ err |= comedi_check_trigger_arg_min(&cmd->convert_arg,
+ thisboard->
+ ai_speed);
+ /*
+ * if scans are timed faster than conversion rate
+ * allows
+ */
+ if (cmd->scan_begin_src == TRIG_TIMER) {
+ err |= comedi_check_trigger_arg_min(
+ &cmd->scan_begin_arg,
+ cmd->convert_arg *
+ cmd->chanlist_len);
+ }
+ }
+ }
+
+ err |= comedi_check_trigger_arg_min(&cmd->chanlist_len, 1);
+ err |= comedi_check_trigger_arg_is(&cmd->scan_end_arg,
+ cmd->chanlist_len);
+
+ switch (cmd->stop_src) {
+ case TRIG_EXT:
+ break;
+ case TRIG_COUNT:
+ err |= comedi_check_trigger_arg_min(&cmd->stop_arg, 1);
+ break;
+ case TRIG_NONE:
+ err |= comedi_check_trigger_arg_is(&cmd->stop_arg, 0);
+ break;
+ default:
+ break;
+ }
+
+ if (err)
+ return 3;
+
+ /* step 4: fix up any arguments */
+
+ if (cmd->convert_src == TRIG_TIMER) {
+ tmp_arg = cmd->convert_arg;
+ tmp_arg2 = cmd->scan_begin_arg;
+ check_adc_timing(dev, cmd);
+ if (tmp_arg != cmd->convert_arg)
+ err++;
+ if (tmp_arg2 != cmd->scan_begin_arg)
+ err++;
+ }
+
+ if (err)
+ return 4;
+
+ /* Step 5: check channel list if it exists */
+ if (cmd->chanlist && cmd->chanlist_len > 0)
+ err |= cb_pcidas64_ai_check_chanlist(dev, s, cmd);
+
+ if (err)
+ return 5;
+
+ return 0;
+}
+
+static int use_hw_sample_counter(struct comedi_cmd *cmd)
+{
+/* disable for now until I work out a race */
+ return 0;
+
+ if (cmd->stop_src == TRIG_COUNT && cmd->stop_arg <= max_counter_value)
+ return 1;
+
+ return 0;
+}
+
+static void setup_sample_counters(struct comedi_device *dev,
+ struct comedi_cmd *cmd)
+{
+ struct pcidas64_private *devpriv = dev->private;
+
+ /* load hardware conversion counter */
+ if (use_hw_sample_counter(cmd)) {
+ writew(cmd->stop_arg & 0xffff,
+ devpriv->main_iobase + ADC_COUNT_LOWER_REG);
+ writew((cmd->stop_arg >> 16) & 0xff,
+ devpriv->main_iobase + ADC_COUNT_UPPER_REG);
+ } else {
+ writew(1, devpriv->main_iobase + ADC_COUNT_LOWER_REG);
+ }
+}
+
+static inline unsigned int dma_transfer_size(struct comedi_device *dev)
+{
+ const struct pcidas64_board *thisboard = dev->board_ptr;
+ struct pcidas64_private *devpriv = dev->private;
+ unsigned int num_samples;
+
+ num_samples = devpriv->ai_fifo_segment_length *
+ thisboard->ai_fifo->sample_packing_ratio;
+ if (num_samples > DMA_BUFFER_SIZE / sizeof(uint16_t))
+ num_samples = DMA_BUFFER_SIZE / sizeof(uint16_t);
+
+ return num_samples;
+}
+
+static uint32_t ai_convert_counter_6xxx(const struct comedi_device *dev,
+ const struct comedi_cmd *cmd)
+{
+ /* supposed to load counter with desired divisor minus 3 */
+ return cmd->convert_arg / TIMER_BASE - 3;
+}
+
+static uint32_t ai_scan_counter_6xxx(struct comedi_device *dev,
+ struct comedi_cmd *cmd)
+{
+ uint32_t count;
+
+ /* figure out how long we need to delay at end of scan */
+ switch (cmd->scan_begin_src) {
+ case TRIG_TIMER:
+ count = (cmd->scan_begin_arg -
+ (cmd->convert_arg * (cmd->chanlist_len - 1))) /
+ TIMER_BASE;
+ break;
+ case TRIG_FOLLOW:
+ count = cmd->convert_arg / TIMER_BASE;
+ break;
+ default:
+ return 0;
+ }
+ return count - 3;
+}
+
+static uint32_t ai_convert_counter_4020(struct comedi_device *dev,
+ struct comedi_cmd *cmd)
+{
+ struct pcidas64_private *devpriv = dev->private;
+ unsigned int divisor;
+
+ switch (cmd->scan_begin_src) {
+ case TRIG_TIMER:
+ divisor = cmd->scan_begin_arg / TIMER_BASE;
+ break;
+ case TRIG_OTHER:
+ divisor = devpriv->ext_clock.divisor;
+ break;
+ default: /* should never happen */
+ dev_err(dev->class_dev, "bug! failed to set ai pacing!\n");
+ divisor = 1000;
+ break;
+ }
+
+ /* supposed to load counter with desired divisor minus 2 for 4020 */
+ return divisor - 2;
+}
+
+static void select_master_clock_4020(struct comedi_device *dev,
+ const struct comedi_cmd *cmd)
+{
+ struct pcidas64_private *devpriv = dev->private;
+
+ /* select internal/external master clock */
+ devpriv->hw_config_bits &= ~MASTER_CLOCK_4020_MASK;
+ if (cmd->scan_begin_src == TRIG_OTHER) {
+ int chanspec = devpriv->ext_clock.chanspec;
+
+ if (CR_CHAN(chanspec))
+ devpriv->hw_config_bits |= BNC_CLOCK_4020_BITS;
+ else
+ devpriv->hw_config_bits |= EXT_CLOCK_4020_BITS;
+ } else {
+ devpriv->hw_config_bits |= INTERNAL_CLOCK_4020_BITS;
+ }
+ writew(devpriv->hw_config_bits,
+ devpriv->main_iobase + HW_CONFIG_REG);
+}
+
+static void select_master_clock(struct comedi_device *dev,
+ const struct comedi_cmd *cmd)
+{
+ const struct pcidas64_board *thisboard = dev->board_ptr;
+
+ switch (thisboard->layout) {
+ case LAYOUT_4020:
+ select_master_clock_4020(dev, cmd);
+ break;
+ default:
+ break;
+ }
+}
+
+static inline void dma_start_sync(struct comedi_device *dev,
+ unsigned int channel)
+{
+ struct pcidas64_private *devpriv = dev->private;
+ unsigned long flags;
+
+ /* spinlock for plx dma control/status reg */
+ spin_lock_irqsave(&dev->spinlock, flags);
+ if (channel)
+ writeb(PLX_DMA_EN_BIT | PLX_DMA_START_BIT |
+ PLX_CLEAR_DMA_INTR_BIT,
+ devpriv->plx9080_iobase + PLX_DMA1_CS_REG);
+ else
+ writeb(PLX_DMA_EN_BIT | PLX_DMA_START_BIT |
+ PLX_CLEAR_DMA_INTR_BIT,
+ devpriv->plx9080_iobase + PLX_DMA0_CS_REG);
+ spin_unlock_irqrestore(&dev->spinlock, flags);
+}
+
+static void set_ai_pacing(struct comedi_device *dev, struct comedi_cmd *cmd)
+{
+ const struct pcidas64_board *thisboard = dev->board_ptr;
+ struct pcidas64_private *devpriv = dev->private;
+ uint32_t convert_counter = 0, scan_counter = 0;
+
+ check_adc_timing(dev, cmd);
+
+ select_master_clock(dev, cmd);
+
+ if (thisboard->layout == LAYOUT_4020) {
+ convert_counter = ai_convert_counter_4020(dev, cmd);
+ } else {
+ convert_counter = ai_convert_counter_6xxx(dev, cmd);
+ scan_counter = ai_scan_counter_6xxx(dev, cmd);
+ }
+
+ /* load lower 16 bits of convert interval */
+ writew(convert_counter & 0xffff,
+ devpriv->main_iobase + ADC_SAMPLE_INTERVAL_LOWER_REG);
+ /* load upper 8 bits of convert interval */
+ writew((convert_counter >> 16) & 0xff,
+ devpriv->main_iobase + ADC_SAMPLE_INTERVAL_UPPER_REG);
+ /* load lower 16 bits of scan delay */
+ writew(scan_counter & 0xffff,
+ devpriv->main_iobase + ADC_DELAY_INTERVAL_LOWER_REG);
+ /* load upper 8 bits of scan delay */
+ writew((scan_counter >> 16) & 0xff,
+ devpriv->main_iobase + ADC_DELAY_INTERVAL_UPPER_REG);
+}
+
+static int use_internal_queue_6xxx(const struct comedi_cmd *cmd)
+{
+ int i;
+
+ for (i = 0; i + 1 < cmd->chanlist_len; i++) {
+ if (CR_CHAN(cmd->chanlist[i + 1]) !=
+ CR_CHAN(cmd->chanlist[i]) + 1)
+ return 0;
+ if (CR_RANGE(cmd->chanlist[i + 1]) !=
+ CR_RANGE(cmd->chanlist[i]))
+ return 0;
+ if (CR_AREF(cmd->chanlist[i + 1]) != CR_AREF(cmd->chanlist[i]))
+ return 0;
+ }
+ return 1;
+}
+
+static int setup_channel_queue(struct comedi_device *dev,
+ const struct comedi_cmd *cmd)
+{
+ const struct pcidas64_board *thisboard = dev->board_ptr;
+ struct pcidas64_private *devpriv = dev->private;
+ unsigned short bits;
+ int i;
+
+ if (thisboard->layout != LAYOUT_4020) {
+ if (use_internal_queue_6xxx(cmd)) {
+ devpriv->hw_config_bits &= ~EXT_QUEUE_BIT;
+ writew(devpriv->hw_config_bits,
+ devpriv->main_iobase + HW_CONFIG_REG);
+ bits = 0;
+ /* set channel */
+ bits |= adc_chan_bits(CR_CHAN(cmd->chanlist[0]));
+ /* set gain */
+ bits |= ai_range_bits_6xxx(dev,
+ CR_RANGE(cmd->chanlist[0]));
+ /* set single-ended / differential */
+ bits |= se_diff_bit_6xxx(dev,
+ CR_AREF(cmd->chanlist[0]) ==
+ AREF_DIFF);
+ if (CR_AREF(cmd->chanlist[0]) == AREF_COMMON)
+ bits |= ADC_COMMON_BIT;
+ /* set stop channel */
+ writew(adc_chan_bits
+ (CR_CHAN(cmd->chanlist[cmd->chanlist_len - 1])),
+ devpriv->main_iobase + ADC_QUEUE_HIGH_REG);
+ /* set start channel, and rest of settings */
+ writew(bits,
+ devpriv->main_iobase + ADC_QUEUE_LOAD_REG);
+ } else {
+ /* use external queue */
+ if (dev->write_subdev && dev->write_subdev->busy) {
+ warn_external_queue(dev);
+ return -EBUSY;
+ }
+ devpriv->hw_config_bits |= EXT_QUEUE_BIT;
+ writew(devpriv->hw_config_bits,
+ devpriv->main_iobase + HW_CONFIG_REG);
+ /* clear DAC buffer to prevent weird interactions */
+ writew(0,
+ devpriv->main_iobase + DAC_BUFFER_CLEAR_REG);
+ /* clear queue pointer */
+ writew(0, devpriv->main_iobase + ADC_QUEUE_CLEAR_REG);
+ /* load external queue */
+ for (i = 0; i < cmd->chanlist_len; i++) {
+ bits = 0;
+ /* set channel */
+ bits |= adc_chan_bits(CR_CHAN(cmd->
+ chanlist[i]));
+ /* set gain */
+ bits |= ai_range_bits_6xxx(dev,
+ CR_RANGE(cmd->
+ chanlist
+ [i]));
+ /* set single-ended / differential */
+ bits |= se_diff_bit_6xxx(dev,
+ CR_AREF(cmd->
+ chanlist[i]) ==
+ AREF_DIFF);
+ if (CR_AREF(cmd->chanlist[i]) == AREF_COMMON)
+ bits |= ADC_COMMON_BIT;
+ /* mark end of queue */
+ if (i == cmd->chanlist_len - 1)
+ bits |= QUEUE_EOSCAN_BIT |
+ QUEUE_EOSEQ_BIT;
+ writew(bits,
+ devpriv->main_iobase +
+ ADC_QUEUE_FIFO_REG);
+ }
+ /* doing a queue clear is not specified in board docs,
+ * but required for reliable operation */
+ writew(0, devpriv->main_iobase + ADC_QUEUE_CLEAR_REG);
+ /* prime queue holding register */
+ writew(0, devpriv->main_iobase + ADC_QUEUE_LOAD_REG);
+ }
+ } else {
+ unsigned short old_cal_range_bits = devpriv->i2c_cal_range_bits;
+
+ devpriv->i2c_cal_range_bits &= ~ADC_SRC_4020_MASK;
+ /* select BNC inputs */
+ devpriv->i2c_cal_range_bits |= adc_src_4020_bits(4);
+ /* select ranges */
+ for (i = 0; i < cmd->chanlist_len; i++) {
+ unsigned int channel = CR_CHAN(cmd->chanlist[i]);
+ unsigned int range = CR_RANGE(cmd->chanlist[i]);
+
+ if (range == 0)
+ devpriv->i2c_cal_range_bits |=
+ attenuate_bit(channel);
+ else
+ devpriv->i2c_cal_range_bits &=
+ ~attenuate_bit(channel);
+ }
+ /* update calibration/range i2c register only if necessary,
+ * as it is very slow */
+ if (old_cal_range_bits != devpriv->i2c_cal_range_bits) {
+ uint8_t i2c_data = devpriv->i2c_cal_range_bits;
+
+ i2c_write(dev, RANGE_CAL_I2C_ADDR, &i2c_data,
+ sizeof(i2c_data));
+ }
+ }
+ return 0;
+}
+
+static inline void load_first_dma_descriptor(struct comedi_device *dev,
+ unsigned int dma_channel,
+ unsigned int descriptor_bits)
+{
+ struct pcidas64_private *devpriv = dev->private;
+
+ /* The transfer size, pci address, and local address registers
+ * are supposedly unused during chained dma,
+ * but I have found that left over values from last operation
+ * occasionally cause problems with transfer of first dma
+ * block. Initializing them to zero seems to fix the problem. */
+ if (dma_channel) {
+ writel(0,
+ devpriv->plx9080_iobase + PLX_DMA1_TRANSFER_SIZE_REG);
+ writel(0, devpriv->plx9080_iobase + PLX_DMA1_PCI_ADDRESS_REG);
+ writel(0,
+ devpriv->plx9080_iobase + PLX_DMA1_LOCAL_ADDRESS_REG);
+ writel(descriptor_bits,
+ devpriv->plx9080_iobase + PLX_DMA1_DESCRIPTOR_REG);
+ } else {
+ writel(0,
+ devpriv->plx9080_iobase + PLX_DMA0_TRANSFER_SIZE_REG);
+ writel(0, devpriv->plx9080_iobase + PLX_DMA0_PCI_ADDRESS_REG);
+ writel(0,
+ devpriv->plx9080_iobase + PLX_DMA0_LOCAL_ADDRESS_REG);
+ writel(descriptor_bits,
+ devpriv->plx9080_iobase + PLX_DMA0_DESCRIPTOR_REG);
+ }
+}
+
+static int ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
+{
+ const struct pcidas64_board *thisboard = dev->board_ptr;
+ struct pcidas64_private *devpriv = dev->private;
+ struct comedi_async *async = s->async;
+ struct comedi_cmd *cmd = &async->cmd;
+ uint32_t bits;
+ unsigned int i;
+ unsigned long flags;
+ int retval;
+
+ disable_ai_pacing(dev);
+ abort_dma(dev, 1);
+
+ retval = setup_channel_queue(dev, cmd);
+ if (retval < 0)
+ return retval;
+
+ /* make sure internal calibration source is turned off */
+ writew(0, devpriv->main_iobase + CALIBRATION_REG);
+
+ set_ai_pacing(dev, cmd);
+
+ setup_sample_counters(dev, cmd);
+
+ enable_ai_interrupts(dev, cmd);
+
+ spin_lock_irqsave(&dev->spinlock, flags);
+ /* set mode, allow conversions through software gate */
+ devpriv->adc_control1_bits |= ADC_SW_GATE_BIT;
+ devpriv->adc_control1_bits &= ~ADC_DITHER_BIT;
+ if (thisboard->layout != LAYOUT_4020) {
+ devpriv->adc_control1_bits &= ~ADC_MODE_MASK;
+ if (cmd->convert_src == TRIG_EXT)
+ /* good old mode 13 */
+ devpriv->adc_control1_bits |= adc_mode_bits(13);
+ else
+ /* mode 8. What else could you need? */
+ devpriv->adc_control1_bits |= adc_mode_bits(8);
+ } else {
+ devpriv->adc_control1_bits &= ~CHANNEL_MODE_4020_MASK;
+ if (cmd->chanlist_len == 4)
+ devpriv->adc_control1_bits |= FOUR_CHANNEL_4020_BITS;
+ else if (cmd->chanlist_len == 2)
+ devpriv->adc_control1_bits |= TWO_CHANNEL_4020_BITS;
+ devpriv->adc_control1_bits &= ~ADC_LO_CHANNEL_4020_MASK;
+ devpriv->adc_control1_bits |=
+ adc_lo_chan_4020_bits(CR_CHAN(cmd->chanlist[0]));
+ devpriv->adc_control1_bits &= ~ADC_HI_CHANNEL_4020_MASK;
+ devpriv->adc_control1_bits |=
+ adc_hi_chan_4020_bits(CR_CHAN(cmd->chanlist
+ [cmd->chanlist_len - 1]));
+ }
+ writew(devpriv->adc_control1_bits,
+ devpriv->main_iobase + ADC_CONTROL1_REG);
+ spin_unlock_irqrestore(&dev->spinlock, flags);
+
+ /* clear adc buffer */
+ writew(0, devpriv->main_iobase + ADC_BUFFER_CLEAR_REG);
+
+ if ((cmd->flags & CMDF_WAKE_EOS) == 0 ||
+ thisboard->layout == LAYOUT_4020) {
+ devpriv->ai_dma_index = 0;
+
+ /* set dma transfer size */
+ for (i = 0; i < ai_dma_ring_count(thisboard); i++)
+ devpriv->ai_dma_desc[i].transfer_size =
+ cpu_to_le32(dma_transfer_size(dev) *
+ sizeof(uint16_t));
+
+ /* give location of first dma descriptor */
+ load_first_dma_descriptor(dev, 1,
+ devpriv->ai_dma_desc_bus_addr |
+ PLX_DESC_IN_PCI_BIT |
+ PLX_INTR_TERM_COUNT |
+ PLX_XFER_LOCAL_TO_PCI);
+
+ dma_start_sync(dev, 1);
+ }
+
+ if (thisboard->layout == LAYOUT_4020) {
+ /* set source for external triggers */
+ bits = 0;
+ if (cmd->start_src == TRIG_EXT && CR_CHAN(cmd->start_arg))
+ bits |= EXT_START_TRIG_BNC_BIT;
+ if (cmd->stop_src == TRIG_EXT && CR_CHAN(cmd->stop_arg))
+ bits |= EXT_STOP_TRIG_BNC_BIT;
+ writew(bits, devpriv->main_iobase + DAQ_ATRIG_LOW_4020_REG);
+ }
+
+ spin_lock_irqsave(&dev->spinlock, flags);
+
+ /* enable pacing, triggering, etc */
+ bits = ADC_ENABLE_BIT | ADC_SOFT_GATE_BITS | ADC_GATE_LEVEL_BIT;
+ if (cmd->flags & CMDF_WAKE_EOS)
+ bits |= ADC_DMA_DISABLE_BIT;
+ /* set start trigger */
+ if (cmd->start_src == TRIG_EXT) {
+ bits |= ADC_START_TRIG_EXT_BITS;
+ if (cmd->start_arg & CR_INVERT)
+ bits |= ADC_START_TRIG_FALLING_BIT;
+ } else if (cmd->start_src == TRIG_NOW) {
+ bits |= ADC_START_TRIG_SOFT_BITS;
+ }
+ if (use_hw_sample_counter(cmd))
+ bits |= ADC_SAMPLE_COUNTER_EN_BIT;
+ writew(bits, devpriv->main_iobase + ADC_CONTROL0_REG);
+
+ devpriv->ai_cmd_running = 1;
+
+ spin_unlock_irqrestore(&dev->spinlock, flags);
+
+ /* start acquisition */
+ if (cmd->start_src == TRIG_NOW)
+ writew(0, devpriv->main_iobase + ADC_START_REG);
+
+ return 0;
+}
+
+/* read num_samples from 16 bit wide ai fifo */
+static void pio_drain_ai_fifo_16(struct comedi_device *dev)
+{
+ struct pcidas64_private *devpriv = dev->private;
+ struct comedi_subdevice *s = dev->read_subdev;
+ unsigned int i;
+ uint16_t prepost_bits;
+ int read_segment, read_index, write_segment, write_index;
+ int num_samples;
+
+ do {
+ /* get least significant 15 bits */
+ read_index = readw(devpriv->main_iobase + ADC_READ_PNTR_REG) &
+ 0x7fff;
+ write_index = readw(devpriv->main_iobase + ADC_WRITE_PNTR_REG) &
+ 0x7fff;
+ /* Get most significant bits (grey code).
+ * Different boards use different code so use a scheme
+ * that doesn't depend on encoding. This read must
+ * occur after reading least significant 15 bits to avoid race
+ * with fifo switching to next segment. */
+ prepost_bits = readw(devpriv->main_iobase + PREPOST_REG);
+
+ /* if read and write pointers are not on the same fifo segment,
+ * read to the end of the read segment */
+ read_segment = adc_upper_read_ptr_code(prepost_bits);
+ write_segment = adc_upper_write_ptr_code(prepost_bits);
+
+ if (read_segment != write_segment)
+ num_samples =
+ devpriv->ai_fifo_segment_length - read_index;
+ else
+ num_samples = write_index - read_index;
+ if (num_samples < 0) {
+ dev_err(dev->class_dev,
+ "cb_pcidas64: bug! num_samples < 0\n");
+ break;
+ }
+
+ num_samples = comedi_nsamples_left(s, num_samples);
+ if (num_samples == 0)
+ break;
+
+ for (i = 0; i < num_samples; i++) {
+ unsigned short val;
+
+ val = readw(devpriv->main_iobase + ADC_FIFO_REG);
+ comedi_buf_write_samples(s, &val, 1);
+ }
+
+ } while (read_segment != write_segment);
+}
+
+/* Read from 32 bit wide ai fifo of 4020 - deal with insane grey coding of
+ * pointers. The pci-4020 hardware only supports dma transfers (it only
+ * supports the use of pio for draining the last remaining points from the
+ * fifo when a data acquisition operation has completed).
+ */
+static void pio_drain_ai_fifo_32(struct comedi_device *dev)
+{
+ struct pcidas64_private *devpriv = dev->private;
+ struct comedi_subdevice *s = dev->read_subdev;
+ unsigned int nsamples;
+ unsigned int i;
+ uint32_t fifo_data;
+ int write_code =
+ readw(devpriv->main_iobase + ADC_WRITE_PNTR_REG) & 0x7fff;
+ int read_code =
+ readw(devpriv->main_iobase + ADC_READ_PNTR_REG) & 0x7fff;
+
+ nsamples = comedi_nsamples_left(s, 100000);
+ for (i = 0; read_code != write_code && i < nsamples;) {
+ unsigned short val;
+
+ fifo_data = readl(dev->mmio + ADC_FIFO_REG);
+ val = fifo_data & 0xffff;
+ comedi_buf_write_samples(s, &val, 1);
+ i++;
+ if (i < nsamples) {
+ val = (fifo_data >> 16) & 0xffff;
+ comedi_buf_write_samples(s, &val, 1);
+ i++;
+ }
+ read_code = readw(devpriv->main_iobase + ADC_READ_PNTR_REG) &
+ 0x7fff;
+ }
+}
+
+/* empty fifo */
+static void pio_drain_ai_fifo(struct comedi_device *dev)
+{
+ const struct pcidas64_board *thisboard = dev->board_ptr;
+
+ if (thisboard->layout == LAYOUT_4020)
+ pio_drain_ai_fifo_32(dev);
+ else
+ pio_drain_ai_fifo_16(dev);
+}
+
+static void drain_dma_buffers(struct comedi_device *dev, unsigned int channel)
+{
+ const struct pcidas64_board *thisboard = dev->board_ptr;
+ struct pcidas64_private *devpriv = dev->private;
+ struct comedi_subdevice *s = dev->read_subdev;
+ uint32_t next_transfer_addr;
+ int j;
+ int num_samples = 0;
+ void __iomem *pci_addr_reg;
+
+ if (channel)
+ pci_addr_reg =
+ devpriv->plx9080_iobase + PLX_DMA1_PCI_ADDRESS_REG;
+ else
+ pci_addr_reg =
+ devpriv->plx9080_iobase + PLX_DMA0_PCI_ADDRESS_REG;
+
+ /* loop until we have read all the full buffers */
+ for (j = 0, next_transfer_addr = readl(pci_addr_reg);
+ (next_transfer_addr <
+ devpriv->ai_buffer_bus_addr[devpriv->ai_dma_index] ||
+ next_transfer_addr >=
+ devpriv->ai_buffer_bus_addr[devpriv->ai_dma_index] +
+ DMA_BUFFER_SIZE) && j < ai_dma_ring_count(thisboard); j++) {
+ /* transfer data from dma buffer to comedi buffer */
+ num_samples = comedi_nsamples_left(s, dma_transfer_size(dev));
+ comedi_buf_write_samples(s,
+ devpriv->ai_buffer[devpriv->ai_dma_index],
+ num_samples);
+ devpriv->ai_dma_index = (devpriv->ai_dma_index + 1) %
+ ai_dma_ring_count(thisboard);
+ }
+ /* XXX check for dma ring buffer overrun
+ * (use end-of-chain bit to mark last unused buffer) */
+}
+
+static void handle_ai_interrupt(struct comedi_device *dev,
+ unsigned short status,
+ unsigned int plx_status)
+{
+ const struct pcidas64_board *thisboard = dev->board_ptr;
+ struct pcidas64_private *devpriv = dev->private;
+ struct comedi_subdevice *s = dev->read_subdev;
+ struct comedi_async *async = s->async;
+ struct comedi_cmd *cmd = &async->cmd;
+ uint8_t dma1_status;
+ unsigned long flags;
+
+ /* check for fifo overrun */
+ if (status & ADC_OVERRUN_BIT) {
+ dev_err(dev->class_dev, "fifo overrun\n");
+ async->events |= COMEDI_CB_ERROR;
+ }
+ /* spin lock makes sure no one else changes plx dma control reg */
+ spin_lock_irqsave(&dev->spinlock, flags);
+ dma1_status = readb(devpriv->plx9080_iobase + PLX_DMA1_CS_REG);
+ if (plx_status & ICS_DMA1_A) { /* dma chan 1 interrupt */
+ writeb((dma1_status & PLX_DMA_EN_BIT) | PLX_CLEAR_DMA_INTR_BIT,
+ devpriv->plx9080_iobase + PLX_DMA1_CS_REG);
+
+ if (dma1_status & PLX_DMA_EN_BIT)
+ drain_dma_buffers(dev, 1);
+ }
+ spin_unlock_irqrestore(&dev->spinlock, flags);
+
+ /* drain fifo with pio */
+ if ((status & ADC_DONE_BIT) ||
+ ((cmd->flags & CMDF_WAKE_EOS) &&
+ (status & ADC_INTR_PENDING_BIT) &&
+ (thisboard->layout != LAYOUT_4020))) {
+ spin_lock_irqsave(&dev->spinlock, flags);
+ if (devpriv->ai_cmd_running) {
+ spin_unlock_irqrestore(&dev->spinlock, flags);
+ pio_drain_ai_fifo(dev);
+ } else {
+ spin_unlock_irqrestore(&dev->spinlock, flags);
+ }
+ }
+ /* if we are have all the data, then quit */
+ if ((cmd->stop_src == TRIG_COUNT &&
+ async->scans_done >= cmd->stop_arg) ||
+ (cmd->stop_src == TRIG_EXT && (status & ADC_STOP_BIT)))
+ async->events |= COMEDI_CB_EOA;
+
+ comedi_handle_events(dev, s);
+}
+
+static inline unsigned int prev_ao_dma_index(struct comedi_device *dev)
+{
+ struct pcidas64_private *devpriv = dev->private;
+ unsigned int buffer_index;
+
+ if (devpriv->ao_dma_index == 0)
+ buffer_index = AO_DMA_RING_COUNT - 1;
+ else
+ buffer_index = devpriv->ao_dma_index - 1;
+ return buffer_index;
+}
+
+static int last_ao_dma_load_completed(struct comedi_device *dev)
+{
+ struct pcidas64_private *devpriv = dev->private;
+ unsigned int buffer_index;
+ unsigned int transfer_address;
+ unsigned short dma_status;
+
+ buffer_index = prev_ao_dma_index(dev);
+ dma_status = readb(devpriv->plx9080_iobase + PLX_DMA0_CS_REG);
+ if ((dma_status & PLX_DMA_DONE_BIT) == 0)
+ return 0;
+
+ transfer_address =
+ readl(devpriv->plx9080_iobase + PLX_DMA0_PCI_ADDRESS_REG);
+ if (transfer_address != devpriv->ao_buffer_bus_addr[buffer_index])
+ return 0;
+
+ return 1;
+}
+
+static inline int ao_dma_needs_restart(struct comedi_device *dev,
+ unsigned short dma_status)
+{
+ if ((dma_status & PLX_DMA_DONE_BIT) == 0 ||
+ (dma_status & PLX_DMA_EN_BIT) == 0)
+ return 0;
+ if (last_ao_dma_load_completed(dev))
+ return 0;
+
+ return 1;
+}
+
+static void restart_ao_dma(struct comedi_device *dev)
+{
+ struct pcidas64_private *devpriv = dev->private;
+ unsigned int dma_desc_bits;
+
+ dma_desc_bits =
+ readl(devpriv->plx9080_iobase + PLX_DMA0_DESCRIPTOR_REG);
+ dma_desc_bits &= ~PLX_END_OF_CHAIN_BIT;
+ load_first_dma_descriptor(dev, 0, dma_desc_bits);
+
+ dma_start_sync(dev, 0);
+}
+
+static unsigned int cb_pcidas64_ao_fill_buffer(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ unsigned short *dest,
+ unsigned int max_bytes)
+{
+ unsigned int nsamples = comedi_bytes_to_samples(s, max_bytes);
+ unsigned int actual_bytes;
+
+ nsamples = comedi_nsamples_left(s, nsamples);
+ actual_bytes = comedi_buf_read_samples(s, dest, nsamples);
+
+ return comedi_bytes_to_samples(s, actual_bytes);
+}
+
+static unsigned int load_ao_dma_buffer(struct comedi_device *dev,
+ const struct comedi_cmd *cmd)
+{
+ struct pcidas64_private *devpriv = dev->private;
+ struct comedi_subdevice *s = dev->write_subdev;
+ unsigned int buffer_index = devpriv->ao_dma_index;
+ unsigned int prev_buffer_index = prev_ao_dma_index(dev);
+ unsigned int nsamples;
+ unsigned int nbytes;
+ unsigned int next_bits;
+
+ nsamples = cb_pcidas64_ao_fill_buffer(dev, s,
+ devpriv->ao_buffer[buffer_index],
+ DMA_BUFFER_SIZE);
+ if (nsamples == 0)
+ return 0;
+
+ nbytes = comedi_samples_to_bytes(s, nsamples);
+ devpriv->ao_dma_desc[buffer_index].transfer_size = cpu_to_le32(nbytes);
+ /* set end of chain bit so we catch underruns */
+ next_bits = le32_to_cpu(devpriv->ao_dma_desc[buffer_index].next);
+ next_bits |= PLX_END_OF_CHAIN_BIT;
+ devpriv->ao_dma_desc[buffer_index].next = cpu_to_le32(next_bits);
+ /* clear end of chain bit on previous buffer now that we have set it
+ * for the last buffer */
+ next_bits = le32_to_cpu(devpriv->ao_dma_desc[prev_buffer_index].next);
+ next_bits &= ~PLX_END_OF_CHAIN_BIT;
+ devpriv->ao_dma_desc[prev_buffer_index].next = cpu_to_le32(next_bits);
+
+ devpriv->ao_dma_index = (buffer_index + 1) % AO_DMA_RING_COUNT;
+
+ return nbytes;
+}
+
+static void load_ao_dma(struct comedi_device *dev, const struct comedi_cmd *cmd)
+{
+ struct pcidas64_private *devpriv = dev->private;
+ unsigned int num_bytes;
+ unsigned int next_transfer_addr;
+ void __iomem *pci_addr_reg =
+ devpriv->plx9080_iobase + PLX_DMA0_PCI_ADDRESS_REG;
+ unsigned int buffer_index;
+
+ do {
+ buffer_index = devpriv->ao_dma_index;
+ /* don't overwrite data that hasn't been transferred yet */
+ next_transfer_addr = readl(pci_addr_reg);
+ if (next_transfer_addr >=
+ devpriv->ao_buffer_bus_addr[buffer_index] &&
+ next_transfer_addr <
+ devpriv->ao_buffer_bus_addr[buffer_index] +
+ DMA_BUFFER_SIZE)
+ return;
+ num_bytes = load_ao_dma_buffer(dev, cmd);
+ } while (num_bytes >= DMA_BUFFER_SIZE);
+}
+
+static void handle_ao_interrupt(struct comedi_device *dev,
+ unsigned short status, unsigned int plx_status)
+{
+ struct pcidas64_private *devpriv = dev->private;
+ struct comedi_subdevice *s = dev->write_subdev;
+ struct comedi_async *async;
+ struct comedi_cmd *cmd;
+ uint8_t dma0_status;
+ unsigned long flags;
+
+ /* board might not support ao, in which case write_subdev is NULL */
+ if (!s)
+ return;
+ async = s->async;
+ cmd = &async->cmd;
+
+ /* spin lock makes sure no one else changes plx dma control reg */
+ spin_lock_irqsave(&dev->spinlock, flags);
+ dma0_status = readb(devpriv->plx9080_iobase + PLX_DMA0_CS_REG);
+ if (plx_status & ICS_DMA0_A) { /* dma chan 0 interrupt */
+ if ((dma0_status & PLX_DMA_EN_BIT) &&
+ !(dma0_status & PLX_DMA_DONE_BIT))
+ writeb(PLX_DMA_EN_BIT | PLX_CLEAR_DMA_INTR_BIT,
+ devpriv->plx9080_iobase + PLX_DMA0_CS_REG);
+ else
+ writeb(PLX_CLEAR_DMA_INTR_BIT,
+ devpriv->plx9080_iobase + PLX_DMA0_CS_REG);
+ spin_unlock_irqrestore(&dev->spinlock, flags);
+ if (dma0_status & PLX_DMA_EN_BIT) {
+ load_ao_dma(dev, cmd);
+ /* try to recover from dma end-of-chain event */
+ if (ao_dma_needs_restart(dev, dma0_status))
+ restart_ao_dma(dev);
+ }
+ } else {
+ spin_unlock_irqrestore(&dev->spinlock, flags);
+ }
+
+ if ((status & DAC_DONE_BIT)) {
+ if ((cmd->stop_src == TRIG_COUNT &&
+ async->scans_done >= cmd->stop_arg) ||
+ last_ao_dma_load_completed(dev))
+ async->events |= COMEDI_CB_EOA;
+ else
+ async->events |= COMEDI_CB_ERROR;
+ }
+ comedi_handle_events(dev, s);
+}
+
+static irqreturn_t handle_interrupt(int irq, void *d)
+{
+ struct comedi_device *dev = d;
+ struct pcidas64_private *devpriv = dev->private;
+ unsigned short status;
+ uint32_t plx_status;
+ uint32_t plx_bits;
+
+ plx_status = readl(devpriv->plx9080_iobase + PLX_INTRCS_REG);
+ status = readw(devpriv->main_iobase + HW_STATUS_REG);
+
+ /* an interrupt before all the postconfig stuff gets done could
+ * cause a NULL dereference if we continue through the
+ * interrupt handler */
+ if (!dev->attached)
+ return IRQ_HANDLED;
+
+ handle_ai_interrupt(dev, status, plx_status);
+ handle_ao_interrupt(dev, status, plx_status);
+
+ /* clear possible plx9080 interrupt sources */
+ if (plx_status & ICS_LDIA) { /* clear local doorbell interrupt */
+ plx_bits = readl(devpriv->plx9080_iobase + PLX_DBR_OUT_REG);
+ writel(plx_bits, devpriv->plx9080_iobase + PLX_DBR_OUT_REG);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static int ai_cancel(struct comedi_device *dev, struct comedi_subdevice *s)
+{
+ struct pcidas64_private *devpriv = dev->private;
+ unsigned long flags;
+
+ spin_lock_irqsave(&dev->spinlock, flags);
+ if (devpriv->ai_cmd_running == 0) {
+ spin_unlock_irqrestore(&dev->spinlock, flags);
+ return 0;
+ }
+ devpriv->ai_cmd_running = 0;
+ spin_unlock_irqrestore(&dev->spinlock, flags);
+
+ disable_ai_pacing(dev);
+
+ abort_dma(dev, 1);
+
+ return 0;
+}
+
+static int ao_winsn(struct comedi_device *dev, struct comedi_subdevice *s,
+ struct comedi_insn *insn, unsigned int *data)
+{
+ const struct pcidas64_board *thisboard = dev->board_ptr;
+ struct pcidas64_private *devpriv = dev->private;
+ int chan = CR_CHAN(insn->chanspec);
+ int range = CR_RANGE(insn->chanspec);
+
+ /* do some initializing */
+ writew(0, devpriv->main_iobase + DAC_CONTROL0_REG);
+
+ /* set range */
+ set_dac_range_bits(dev, &devpriv->dac_control1_bits, chan, range);
+ writew(devpriv->dac_control1_bits,
+ devpriv->main_iobase + DAC_CONTROL1_REG);
+
+ /* write to channel */
+ if (thisboard->layout == LAYOUT_4020) {
+ writew(data[0] & 0xff,
+ devpriv->main_iobase + dac_lsb_4020_reg(chan));
+ writew((data[0] >> 8) & 0xf,
+ devpriv->main_iobase + dac_msb_4020_reg(chan));
+ } else {
+ writew(data[0], devpriv->main_iobase + dac_convert_reg(chan));
+ }
+
+ /* remember output value */
+ s->readback[chan] = data[0];
+
+ return 1;
+}
+
+static void set_dac_control0_reg(struct comedi_device *dev,
+ const struct comedi_cmd *cmd)
+{
+ struct pcidas64_private *devpriv = dev->private;
+ unsigned int bits = DAC_ENABLE_BIT | WAVEFORM_GATE_LEVEL_BIT |
+ WAVEFORM_GATE_ENABLE_BIT | WAVEFORM_GATE_SELECT_BIT;
+
+ if (cmd->start_src == TRIG_EXT) {
+ bits |= WAVEFORM_TRIG_EXT_BITS;
+ if (cmd->start_arg & CR_INVERT)
+ bits |= WAVEFORM_TRIG_FALLING_BIT;
+ } else {
+ bits |= WAVEFORM_TRIG_SOFT_BITS;
+ }
+ if (cmd->scan_begin_src == TRIG_EXT) {
+ bits |= DAC_EXT_UPDATE_ENABLE_BIT;
+ if (cmd->scan_begin_arg & CR_INVERT)
+ bits |= DAC_EXT_UPDATE_FALLING_BIT;
+ }
+ writew(bits, devpriv->main_iobase + DAC_CONTROL0_REG);
+}
+
+static void set_dac_control1_reg(struct comedi_device *dev,
+ const struct comedi_cmd *cmd)
+{
+ struct pcidas64_private *devpriv = dev->private;
+ int i;
+
+ for (i = 0; i < cmd->chanlist_len; i++) {
+ int channel, range;
+
+ channel = CR_CHAN(cmd->chanlist[i]);
+ range = CR_RANGE(cmd->chanlist[i]);
+ set_dac_range_bits(dev, &devpriv->dac_control1_bits, channel,
+ range);
+ }
+ devpriv->dac_control1_bits |= DAC_SW_GATE_BIT;
+ writew(devpriv->dac_control1_bits,
+ devpriv->main_iobase + DAC_CONTROL1_REG);
+}
+
+static void set_dac_select_reg(struct comedi_device *dev,
+ const struct comedi_cmd *cmd)
+{
+ struct pcidas64_private *devpriv = dev->private;
+ uint16_t bits;
+ unsigned int first_channel, last_channel;
+
+ first_channel = CR_CHAN(cmd->chanlist[0]);
+ last_channel = CR_CHAN(cmd->chanlist[cmd->chanlist_len - 1]);
+ if (last_channel < first_channel)
+ dev_err(dev->class_dev,
+ "bug! last ao channel < first ao channel\n");
+
+ bits = (first_channel & 0x7) | (last_channel & 0x7) << 3;
+
+ writew(bits, devpriv->main_iobase + DAC_SELECT_REG);
+}
+
+static unsigned int get_ao_divisor(unsigned int ns, unsigned int flags)
+{
+ return get_divisor(ns, flags) - 2;
+}
+
+static void set_dac_interval_regs(struct comedi_device *dev,
+ const struct comedi_cmd *cmd)
+{
+ struct pcidas64_private *devpriv = dev->private;
+ unsigned int divisor;
+
+ if (cmd->scan_begin_src != TRIG_TIMER)
+ return;
+
+ divisor = get_ao_divisor(cmd->scan_begin_arg, cmd->flags);
+ if (divisor > max_counter_value) {
+ dev_err(dev->class_dev, "bug! ao divisor too big\n");
+ divisor = max_counter_value;
+ }
+ writew(divisor & 0xffff,
+ devpriv->main_iobase + DAC_SAMPLE_INTERVAL_LOWER_REG);
+ writew((divisor >> 16) & 0xff,
+ devpriv->main_iobase + DAC_SAMPLE_INTERVAL_UPPER_REG);
+}
+
+static int prep_ao_dma(struct comedi_device *dev, const struct comedi_cmd *cmd)
+{
+ struct pcidas64_private *devpriv = dev->private;
+ struct comedi_subdevice *s = dev->write_subdev;
+ unsigned int nsamples;
+ unsigned int nbytes;
+ int i;
+
+ /* clear queue pointer too, since external queue has
+ * weird interactions with ao fifo */
+ writew(0, devpriv->main_iobase + ADC_QUEUE_CLEAR_REG);
+ writew(0, devpriv->main_iobase + DAC_BUFFER_CLEAR_REG);
+
+ nsamples = cb_pcidas64_ao_fill_buffer(dev, s,
+ devpriv->ao_bounce_buffer,
+ DAC_FIFO_SIZE);
+ if (nsamples == 0)
+ return -1;
+
+ for (i = 0; i < nsamples; i++) {
+ writew(devpriv->ao_bounce_buffer[i],
+ devpriv->main_iobase + DAC_FIFO_REG);
+ }
+
+ if (cmd->stop_src == TRIG_COUNT &&
+ s->async->scans_done >= cmd->stop_arg)
+ return 0;
+
+ nbytes = load_ao_dma_buffer(dev, cmd);
+ if (nbytes == 0)
+ return -1;
+ load_ao_dma(dev, cmd);
+
+ dma_start_sync(dev, 0);
+
+ return 0;
+}
+
+static inline int external_ai_queue_in_use(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_cmd *cmd)
+{
+ const struct pcidas64_board *thisboard = dev->board_ptr;
+
+ if (s->busy)
+ return 0;
+ if (thisboard->layout == LAYOUT_4020)
+ return 0;
+ else if (use_internal_queue_6xxx(cmd))
+ return 0;
+ return 1;
+}
+
+static int ao_inttrig(struct comedi_device *dev, struct comedi_subdevice *s,
+ unsigned int trig_num)
+{
+ struct pcidas64_private *devpriv = dev->private;
+ struct comedi_cmd *cmd = &s->async->cmd;
+ int retval;
+
+ if (trig_num != cmd->start_arg)
+ return -EINVAL;
+
+ retval = prep_ao_dma(dev, cmd);
+ if (retval < 0)
+ return -EPIPE;
+
+ set_dac_control0_reg(dev, cmd);
+
+ if (cmd->start_src == TRIG_INT)
+ writew(0, devpriv->main_iobase + DAC_START_REG);
+
+ s->async->inttrig = NULL;
+
+ return 0;
+}
+
+static int ao_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
+{
+ struct pcidas64_private *devpriv = dev->private;
+ struct comedi_cmd *cmd = &s->async->cmd;
+
+ if (external_ai_queue_in_use(dev, s, cmd)) {
+ warn_external_queue(dev);
+ return -EBUSY;
+ }
+ /* disable analog output system during setup */
+ writew(0x0, devpriv->main_iobase + DAC_CONTROL0_REG);
+
+ devpriv->ao_dma_index = 0;
+
+ set_dac_select_reg(dev, cmd);
+ set_dac_interval_regs(dev, cmd);
+ load_first_dma_descriptor(dev, 0, devpriv->ao_dma_desc_bus_addr |
+ PLX_DESC_IN_PCI_BIT | PLX_INTR_TERM_COUNT);
+
+ set_dac_control1_reg(dev, cmd);
+ s->async->inttrig = ao_inttrig;
+
+ return 0;
+}
+
+static int cb_pcidas64_ao_check_chanlist(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_cmd *cmd)
+{
+ unsigned int chan0 = CR_CHAN(cmd->chanlist[0]);
+ int i;
+
+ for (i = 1; i < cmd->chanlist_len; i++) {
+ unsigned int chan = CR_CHAN(cmd->chanlist[i]);
+
+ if (chan != (chan0 + i)) {
+ dev_dbg(dev->class_dev,
+ "chanlist must use consecutive channels\n");
+ return -EINVAL;
+ }
+ }
+
+ return 0;
+}
+
+static int ao_cmdtest(struct comedi_device *dev, struct comedi_subdevice *s,
+ struct comedi_cmd *cmd)
+{
+ const struct pcidas64_board *thisboard = dev->board_ptr;
+ int err = 0;
+ unsigned int tmp_arg;
+
+ /* Step 1 : check if triggers are trivially valid */
+
+ err |= comedi_check_trigger_src(&cmd->start_src, TRIG_INT | TRIG_EXT);
+ err |= comedi_check_trigger_src(&cmd->scan_begin_src,
+ TRIG_TIMER | TRIG_EXT);
+ err |= comedi_check_trigger_src(&cmd->convert_src, TRIG_NOW);
+ err |= comedi_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
+ err |= comedi_check_trigger_src(&cmd->stop_src, TRIG_NONE);
+
+ if (err)
+ return 1;
+
+ /* Step 2a : make sure trigger sources are unique */
+
+ err |= comedi_check_trigger_is_unique(cmd->start_src);
+ err |= comedi_check_trigger_is_unique(cmd->scan_begin_src);
+
+ /* Step 2b : and mutually compatible */
+
+ if (cmd->convert_src == TRIG_EXT && cmd->scan_begin_src == TRIG_TIMER)
+ err |= -EINVAL;
+ if (cmd->stop_src != TRIG_COUNT &&
+ cmd->stop_src != TRIG_NONE && cmd->stop_src != TRIG_EXT)
+ err |= -EINVAL;
+
+ if (err)
+ return 2;
+
+ /* Step 3: check if arguments are trivially valid */
+
+ err |= comedi_check_trigger_arg_is(&cmd->start_arg, 0);
+
+ if (cmd->scan_begin_src == TRIG_TIMER) {
+ err |= comedi_check_trigger_arg_min(&cmd->scan_begin_arg,
+ thisboard->ao_scan_speed);
+ if (get_ao_divisor(cmd->scan_begin_arg, cmd->flags) >
+ max_counter_value) {
+ cmd->scan_begin_arg = (max_counter_value + 2) *
+ TIMER_BASE;
+ err |= -EINVAL;
+ }
+ }
+
+ err |= comedi_check_trigger_arg_min(&cmd->chanlist_len, 1);
+ err |= comedi_check_trigger_arg_is(&cmd->scan_end_arg,
+ cmd->chanlist_len);
+
+ if (err)
+ return 3;
+
+ /* step 4: fix up any arguments */
+
+ if (cmd->scan_begin_src == TRIG_TIMER) {
+ tmp_arg = cmd->scan_begin_arg;
+ cmd->scan_begin_arg = get_divisor(cmd->scan_begin_arg,
+ cmd->flags) * TIMER_BASE;
+ if (tmp_arg != cmd->scan_begin_arg)
+ err++;
+ }
+
+ if (err)
+ return 4;
+
+ /* Step 5: check channel list if it exists */
+ if (cmd->chanlist && cmd->chanlist_len > 0)
+ err |= cb_pcidas64_ao_check_chanlist(dev, s, cmd);
+
+ if (err)
+ return 5;
+
+ return 0;
+}
+
+static int ao_cancel(struct comedi_device *dev, struct comedi_subdevice *s)
+{
+ struct pcidas64_private *devpriv = dev->private;
+
+ writew(0x0, devpriv->main_iobase + DAC_CONTROL0_REG);
+ abort_dma(dev, 0);
+ return 0;
+}
+
+static int dio_callback_4020(struct comedi_device *dev,
+ int dir, int port, int data, unsigned long iobase)
+{
+ struct pcidas64_private *devpriv = dev->private;
+
+ if (dir) {
+ writew(data, devpriv->main_iobase + iobase + 2 * port);
+ return 0;
+ }
+ return readw(devpriv->main_iobase + iobase + 2 * port);
+}
+
+static int di_rbits(struct comedi_device *dev, struct comedi_subdevice *s,
+ struct comedi_insn *insn, unsigned int *data)
+{
+ unsigned int bits;
+
+ bits = readb(dev->mmio + DI_REG);
+ bits &= 0xf;
+ data[1] = bits;
+ data[0] = 0;
+
+ return insn->n;
+}
+
+static int do_wbits(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ if (comedi_dio_update_state(s, data))
+ writeb(s->state, dev->mmio + DO_REG);
+
+ data[1] = s->state;
+
+ return insn->n;
+}
+
+static int dio_60xx_config_insn(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ int ret;
+
+ ret = comedi_dio_insn_config(dev, s, insn, data, 0);
+ if (ret)
+ return ret;
+
+ writeb(s->io_bits, dev->mmio + DIO_DIRECTION_60XX_REG);
+
+ return insn->n;
+}
+
+static int dio_60xx_wbits(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ if (comedi_dio_update_state(s, data))
+ writeb(s->state, dev->mmio + DIO_DATA_60XX_REG);
+
+ data[1] = readb(dev->mmio + DIO_DATA_60XX_REG);
+
+ return insn->n;
+}
+
+/* pci-6025 8800 caldac:
+ * address 0 == dac channel 0 offset
+ * address 1 == dac channel 0 gain
+ * address 2 == dac channel 1 offset
+ * address 3 == dac channel 1 gain
+ * address 4 == fine adc offset
+ * address 5 == coarse adc offset
+ * address 6 == coarse adc gain
+ * address 7 == fine adc gain
+ */
+/* pci-6402/16 uses all 8 channels for dac:
+ * address 0 == dac channel 0 fine gain
+ * address 1 == dac channel 0 coarse gain
+ * address 2 == dac channel 0 coarse offset
+ * address 3 == dac channel 1 coarse offset
+ * address 4 == dac channel 1 fine gain
+ * address 5 == dac channel 1 coarse gain
+ * address 6 == dac channel 0 fine offset
+ * address 7 == dac channel 1 fine offset
+*/
+
+static int caldac_8800_write(struct comedi_device *dev, unsigned int address,
+ uint8_t value)
+{
+ struct pcidas64_private *devpriv = dev->private;
+ static const int num_caldac_channels = 8;
+ static const int bitstream_length = 11;
+ unsigned int bitstream = ((address & 0x7) << 8) | value;
+ unsigned int bit, register_bits;
+ static const int caldac_8800_udelay = 1;
+
+ if (address >= num_caldac_channels) {
+ dev_err(dev->class_dev, "illegal caldac channel\n");
+ return -1;
+ }
+ for (bit = 1 << (bitstream_length - 1); bit; bit >>= 1) {
+ register_bits = 0;
+ if (bitstream & bit)
+ register_bits |= SERIAL_DATA_IN_BIT;
+ udelay(caldac_8800_udelay);
+ writew(register_bits, devpriv->main_iobase + CALIBRATION_REG);
+ register_bits |= SERIAL_CLOCK_BIT;
+ udelay(caldac_8800_udelay);
+ writew(register_bits, devpriv->main_iobase + CALIBRATION_REG);
+ }
+ udelay(caldac_8800_udelay);
+ writew(SELECT_8800_BIT, devpriv->main_iobase + CALIBRATION_REG);
+ udelay(caldac_8800_udelay);
+ writew(0, devpriv->main_iobase + CALIBRATION_REG);
+ udelay(caldac_8800_udelay);
+ return 0;
+}
+
+/* 4020 caldacs */
+static int caldac_i2c_write(struct comedi_device *dev,
+ unsigned int caldac_channel, unsigned int value)
+{
+ uint8_t serial_bytes[3];
+ uint8_t i2c_addr;
+ enum pointer_bits {
+ /* manual has gain and offset bits switched */
+ OFFSET_0_2 = 0x1,
+ GAIN_0_2 = 0x2,
+ OFFSET_1_3 = 0x4,
+ GAIN_1_3 = 0x8,
+ };
+ enum data_bits {
+ NOT_CLEAR_REGISTERS = 0x20,
+ };
+
+ switch (caldac_channel) {
+ case 0: /* chan 0 offset */
+ i2c_addr = CALDAC0_I2C_ADDR;
+ serial_bytes[0] = OFFSET_0_2;
+ break;
+ case 1: /* chan 1 offset */
+ i2c_addr = CALDAC0_I2C_ADDR;
+ serial_bytes[0] = OFFSET_1_3;
+ break;
+ case 2: /* chan 2 offset */
+ i2c_addr = CALDAC1_I2C_ADDR;
+ serial_bytes[0] = OFFSET_0_2;
+ break;
+ case 3: /* chan 3 offset */
+ i2c_addr = CALDAC1_I2C_ADDR;
+ serial_bytes[0] = OFFSET_1_3;
+ break;
+ case 4: /* chan 0 gain */
+ i2c_addr = CALDAC0_I2C_ADDR;
+ serial_bytes[0] = GAIN_0_2;
+ break;
+ case 5: /* chan 1 gain */
+ i2c_addr = CALDAC0_I2C_ADDR;
+ serial_bytes[0] = GAIN_1_3;
+ break;
+ case 6: /* chan 2 gain */
+ i2c_addr = CALDAC1_I2C_ADDR;
+ serial_bytes[0] = GAIN_0_2;
+ break;
+ case 7: /* chan 3 gain */
+ i2c_addr = CALDAC1_I2C_ADDR;
+ serial_bytes[0] = GAIN_1_3;
+ break;
+ default:
+ dev_err(dev->class_dev, "invalid caldac channel\n");
+ return -1;
+ }
+ serial_bytes[1] = NOT_CLEAR_REGISTERS | ((value >> 8) & 0xf);
+ serial_bytes[2] = value & 0xff;
+ i2c_write(dev, i2c_addr, serial_bytes, 3);
+ return 0;
+}
+
+static void caldac_write(struct comedi_device *dev, unsigned int channel,
+ unsigned int value)
+{
+ const struct pcidas64_board *thisboard = dev->board_ptr;
+
+ switch (thisboard->layout) {
+ case LAYOUT_60XX:
+ case LAYOUT_64XX:
+ caldac_8800_write(dev, channel, value);
+ break;
+ case LAYOUT_4020:
+ caldac_i2c_write(dev, channel, value);
+ break;
+ default:
+ break;
+ }
+}
+
+static int cb_pcidas64_calib_insn_write(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ unsigned int chan = CR_CHAN(insn->chanspec);
+
+ /*
+ * Programming the calib device is slow. Only write the
+ * last data value if the value has changed.
+ */
+ if (insn->n) {
+ unsigned int val = data[insn->n - 1];
+
+ if (s->readback[chan] != val) {
+ caldac_write(dev, chan, val);
+ s->readback[chan] = val;
+ }
+ }
+
+ return insn->n;
+}
+
+static void ad8402_write(struct comedi_device *dev, unsigned int channel,
+ unsigned int value)
+{
+ struct pcidas64_private *devpriv = dev->private;
+ static const int bitstream_length = 10;
+ unsigned int bit, register_bits;
+ unsigned int bitstream = ((channel & 0x3) << 8) | (value & 0xff);
+ static const int ad8402_udelay = 1;
+
+ register_bits = SELECT_8402_64XX_BIT;
+ udelay(ad8402_udelay);
+ writew(register_bits, devpriv->main_iobase + CALIBRATION_REG);
+
+ for (bit = 1 << (bitstream_length - 1); bit; bit >>= 1) {
+ if (bitstream & bit)
+ register_bits |= SERIAL_DATA_IN_BIT;
+ else
+ register_bits &= ~SERIAL_DATA_IN_BIT;
+ udelay(ad8402_udelay);
+ writew(register_bits, devpriv->main_iobase + CALIBRATION_REG);
+ udelay(ad8402_udelay);
+ writew(register_bits | SERIAL_CLOCK_BIT,
+ devpriv->main_iobase + CALIBRATION_REG);
+ }
+
+ udelay(ad8402_udelay);
+ writew(0, devpriv->main_iobase + CALIBRATION_REG);
+}
+
+/* for pci-das6402/16, channel 0 is analog input gain and channel 1 is offset */
+static int cb_pcidas64_ad8402_insn_write(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ unsigned int chan = CR_CHAN(insn->chanspec);
+
+ /*
+ * Programming the calib device is slow. Only write the
+ * last data value if the value has changed.
+ */
+ if (insn->n) {
+ unsigned int val = data[insn->n - 1];
+
+ if (s->readback[chan] != val) {
+ ad8402_write(dev, chan, val);
+ s->readback[chan] = val;
+ }
+ }
+
+ return insn->n;
+}
+
+static uint16_t read_eeprom(struct comedi_device *dev, uint8_t address)
+{
+ struct pcidas64_private *devpriv = dev->private;
+ static const int bitstream_length = 11;
+ static const int read_command = 0x6;
+ unsigned int bitstream = (read_command << 8) | address;
+ unsigned int bit;
+ void __iomem * const plx_control_addr =
+ devpriv->plx9080_iobase + PLX_CONTROL_REG;
+ uint16_t value;
+ static const int value_length = 16;
+ static const int eeprom_udelay = 1;
+
+ udelay(eeprom_udelay);
+ devpriv->plx_control_bits &= ~CTL_EE_CLK & ~CTL_EE_CS;
+ /* make sure we don't send anything to the i2c bus on 4020 */
+ devpriv->plx_control_bits |= CTL_USERO;
+ writel(devpriv->plx_control_bits, plx_control_addr);
+ /* activate serial eeprom */
+ udelay(eeprom_udelay);
+ devpriv->plx_control_bits |= CTL_EE_CS;
+ writel(devpriv->plx_control_bits, plx_control_addr);
+
+ /* write read command and desired memory address */
+ for (bit = 1 << (bitstream_length - 1); bit; bit >>= 1) {
+ /* set bit to be written */
+ udelay(eeprom_udelay);
+ if (bitstream & bit)
+ devpriv->plx_control_bits |= CTL_EE_W;
+ else
+ devpriv->plx_control_bits &= ~CTL_EE_W;
+ writel(devpriv->plx_control_bits, plx_control_addr);
+ /* clock in bit */
+ udelay(eeprom_udelay);
+ devpriv->plx_control_bits |= CTL_EE_CLK;
+ writel(devpriv->plx_control_bits, plx_control_addr);
+ udelay(eeprom_udelay);
+ devpriv->plx_control_bits &= ~CTL_EE_CLK;
+ writel(devpriv->plx_control_bits, plx_control_addr);
+ }
+ /* read back value from eeprom memory location */
+ value = 0;
+ for (bit = 1 << (value_length - 1); bit; bit >>= 1) {
+ /* clock out bit */
+ udelay(eeprom_udelay);
+ devpriv->plx_control_bits |= CTL_EE_CLK;
+ writel(devpriv->plx_control_bits, plx_control_addr);
+ udelay(eeprom_udelay);
+ devpriv->plx_control_bits &= ~CTL_EE_CLK;
+ writel(devpriv->plx_control_bits, plx_control_addr);
+ udelay(eeprom_udelay);
+ if (readl(plx_control_addr) & CTL_EE_R)
+ value |= bit;
+ }
+
+ /* deactivate eeprom serial input */
+ udelay(eeprom_udelay);
+ devpriv->plx_control_bits &= ~CTL_EE_CS;
+ writel(devpriv->plx_control_bits, plx_control_addr);
+
+ return value;
+}
+
+static int eeprom_read_insn(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn, unsigned int *data)
+{
+ data[0] = read_eeprom(dev, CR_CHAN(insn->chanspec));
+
+ return 1;
+}
+
+/* Allocate and initialize the subdevice structures.
+ */
+static int setup_subdevices(struct comedi_device *dev)
+{
+ const struct pcidas64_board *thisboard = dev->board_ptr;
+ struct pcidas64_private *devpriv = dev->private;
+ struct comedi_subdevice *s;
+ int i;
+ int ret;
+
+ ret = comedi_alloc_subdevices(dev, 10);
+ if (ret)
+ return ret;
+
+ s = &dev->subdevices[0];
+ /* analog input subdevice */
+ dev->read_subdev = s;
+ s->type = COMEDI_SUBD_AI;
+ s->subdev_flags = SDF_READABLE | SDF_GROUND | SDF_DITHER | SDF_CMD_READ;
+ if (thisboard->layout == LAYOUT_60XX)
+ s->subdev_flags |= SDF_COMMON | SDF_DIFF;
+ else if (thisboard->layout == LAYOUT_64XX)
+ s->subdev_flags |= SDF_DIFF;
+ /* XXX Number of inputs in differential mode is ignored */
+ s->n_chan = thisboard->ai_se_chans;
+ s->len_chanlist = 0x2000;
+ s->maxdata = (1 << thisboard->ai_bits) - 1;
+ s->range_table = thisboard->ai_range_table;
+ s->insn_read = ai_rinsn;
+ s->insn_config = ai_config_insn;
+ s->do_cmd = ai_cmd;
+ s->do_cmdtest = ai_cmdtest;
+ s->cancel = ai_cancel;
+ if (thisboard->layout == LAYOUT_4020) {
+ uint8_t data;
+ /* set adc to read from inputs
+ * (not internal calibration sources) */
+ devpriv->i2c_cal_range_bits = adc_src_4020_bits(4);
+ /* set channels to +-5 volt input ranges */
+ for (i = 0; i < s->n_chan; i++)
+ devpriv->i2c_cal_range_bits |= attenuate_bit(i);
+ data = devpriv->i2c_cal_range_bits;
+ i2c_write(dev, RANGE_CAL_I2C_ADDR, &data, sizeof(data));
+ }
+
+ /* analog output subdevice */
+ s = &dev->subdevices[1];
+ if (thisboard->ao_nchan) {
+ s->type = COMEDI_SUBD_AO;
+ s->subdev_flags = SDF_READABLE | SDF_WRITABLE |
+ SDF_GROUND | SDF_CMD_WRITE;
+ s->n_chan = thisboard->ao_nchan;
+ s->maxdata = (1 << thisboard->ao_bits) - 1;
+ s->range_table = thisboard->ao_range_table;
+ s->insn_write = ao_winsn;
+
+ ret = comedi_alloc_subdev_readback(s);
+ if (ret)
+ return ret;
+
+ if (ao_cmd_is_supported(thisboard)) {
+ dev->write_subdev = s;
+ s->do_cmdtest = ao_cmdtest;
+ s->do_cmd = ao_cmd;
+ s->len_chanlist = thisboard->ao_nchan;
+ s->cancel = ao_cancel;
+ }
+ } else {
+ s->type = COMEDI_SUBD_UNUSED;
+ }
+
+ /* digital input */
+ s = &dev->subdevices[2];
+ if (thisboard->layout == LAYOUT_64XX) {
+ s->type = COMEDI_SUBD_DI;
+ s->subdev_flags = SDF_READABLE;
+ s->n_chan = 4;
+ s->maxdata = 1;
+ s->range_table = &range_digital;
+ s->insn_bits = di_rbits;
+ } else {
+ s->type = COMEDI_SUBD_UNUSED;
+ }
+
+ /* digital output */
+ if (thisboard->layout == LAYOUT_64XX) {
+ s = &dev->subdevices[3];
+ s->type = COMEDI_SUBD_DO;
+ s->subdev_flags = SDF_WRITABLE;
+ s->n_chan = 4;
+ s->maxdata = 1;
+ s->range_table = &range_digital;
+ s->insn_bits = do_wbits;
+ } else {
+ s->type = COMEDI_SUBD_UNUSED;
+ }
+
+ /* 8255 */
+ s = &dev->subdevices[4];
+ if (thisboard->has_8255) {
+ if (thisboard->layout == LAYOUT_4020) {
+ ret = subdev_8255_init(dev, s, dio_callback_4020,
+ I8255_4020_REG);
+ } else {
+ ret = subdev_8255_mm_init(dev, s, NULL,
+ DIO_8255_OFFSET);
+ }
+ if (ret)
+ return ret;
+ } else {
+ s->type = COMEDI_SUBD_UNUSED;
+ }
+
+ /* 8 channel dio for 60xx */
+ s = &dev->subdevices[5];
+ if (thisboard->layout == LAYOUT_60XX) {
+ s->type = COMEDI_SUBD_DIO;
+ s->subdev_flags = SDF_WRITABLE | SDF_READABLE;
+ s->n_chan = 8;
+ s->maxdata = 1;
+ s->range_table = &range_digital;
+ s->insn_config = dio_60xx_config_insn;
+ s->insn_bits = dio_60xx_wbits;
+ } else {
+ s->type = COMEDI_SUBD_UNUSED;
+ }
+
+ /* caldac */
+ s = &dev->subdevices[6];
+ s->type = COMEDI_SUBD_CALIB;
+ s->subdev_flags = SDF_READABLE | SDF_WRITABLE | SDF_INTERNAL;
+ s->n_chan = 8;
+ if (thisboard->layout == LAYOUT_4020)
+ s->maxdata = 0xfff;
+ else
+ s->maxdata = 0xff;
+ s->insn_write = cb_pcidas64_calib_insn_write;
+
+ ret = comedi_alloc_subdev_readback(s);
+ if (ret)
+ return ret;
+
+ for (i = 0; i < s->n_chan; i++) {
+ caldac_write(dev, i, s->maxdata / 2);
+ s->readback[i] = s->maxdata / 2;
+ }
+
+ /* 2 channel ad8402 potentiometer */
+ s = &dev->subdevices[7];
+ if (thisboard->layout == LAYOUT_64XX) {
+ s->type = COMEDI_SUBD_CALIB;
+ s->subdev_flags = SDF_READABLE | SDF_WRITABLE | SDF_INTERNAL;
+ s->n_chan = 2;
+ s->maxdata = 0xff;
+ s->insn_write = cb_pcidas64_ad8402_insn_write;
+
+ ret = comedi_alloc_subdev_readback(s);
+ if (ret)
+ return ret;
+
+ for (i = 0; i < s->n_chan; i++) {
+ ad8402_write(dev, i, s->maxdata / 2);
+ s->readback[i] = s->maxdata / 2;
+ }
+ } else {
+ s->type = COMEDI_SUBD_UNUSED;
+ }
+
+ /* serial EEPROM, if present */
+ s = &dev->subdevices[8];
+ if (readl(devpriv->plx9080_iobase + PLX_CONTROL_REG) & CTL_EECHK) {
+ s->type = COMEDI_SUBD_MEMORY;
+ s->subdev_flags = SDF_READABLE | SDF_INTERNAL;
+ s->n_chan = 128;
+ s->maxdata = 0xffff;
+ s->insn_read = eeprom_read_insn;
+ } else {
+ s->type = COMEDI_SUBD_UNUSED;
+ }
+
+ /* user counter subd XXX */
+ s = &dev->subdevices[9];
+ s->type = COMEDI_SUBD_UNUSED;
+
+ return 0;
+}
+
+static int auto_attach(struct comedi_device *dev,
+ unsigned long context)
+{
+ struct pci_dev *pcidev = comedi_to_pci_dev(dev);
+ const struct pcidas64_board *thisboard = NULL;
+ struct pcidas64_private *devpriv;
+ uint32_t local_range, local_decode;
+ int retval;
+
+ if (context < ARRAY_SIZE(pcidas64_boards))
+ thisboard = &pcidas64_boards[context];
+ if (!thisboard)
+ return -ENODEV;
+ dev->board_ptr = thisboard;
+
+ devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
+ if (!devpriv)
+ return -ENOMEM;
+
+ retval = comedi_pci_enable(dev);
+ if (retval)
+ return retval;
+ pci_set_master(pcidev);
+
+ /* Initialize dev->board_name */
+ dev->board_name = thisboard->name;
+
+ devpriv->main_phys_iobase = pci_resource_start(pcidev, 2);
+ devpriv->dio_counter_phys_iobase = pci_resource_start(pcidev, 3);
+
+ devpriv->plx9080_iobase = pci_ioremap_bar(pcidev, 0);
+ devpriv->main_iobase = pci_ioremap_bar(pcidev, 2);
+ dev->mmio = pci_ioremap_bar(pcidev, 3);
+
+ if (!devpriv->plx9080_iobase || !devpriv->main_iobase || !dev->mmio) {
+ dev_warn(dev->class_dev, "failed to remap io memory\n");
+ return -ENOMEM;
+ }
+
+ /* figure out what local addresses are */
+ local_range = readl(devpriv->plx9080_iobase + PLX_LAS0RNG_REG) &
+ LRNG_MEM_MASK;
+ local_decode = readl(devpriv->plx9080_iobase + PLX_LAS0MAP_REG) &
+ local_range & LMAP_MEM_MASK;
+ devpriv->local0_iobase = ((uint32_t)devpriv->main_phys_iobase &
+ ~local_range) | local_decode;
+ local_range = readl(devpriv->plx9080_iobase + PLX_LAS1RNG_REG) &
+ LRNG_MEM_MASK;
+ local_decode = readl(devpriv->plx9080_iobase + PLX_LAS1MAP_REG) &
+ local_range & LMAP_MEM_MASK;
+ devpriv->local1_iobase = ((uint32_t)devpriv->dio_counter_phys_iobase &
+ ~local_range) | local_decode;
+
+ retval = alloc_and_init_dma_members(dev);
+ if (retval < 0)
+ return retval;
+
+ devpriv->hw_revision =
+ hw_revision(dev, readw(devpriv->main_iobase + HW_STATUS_REG));
+ dev_dbg(dev->class_dev, "stc hardware revision %i\n",
+ devpriv->hw_revision);
+ init_plx9080(dev);
+ init_stc_registers(dev);
+
+ retval = request_irq(pcidev->irq, handle_interrupt, IRQF_SHARED,
+ dev->board_name, dev);
+ if (retval) {
+ dev_dbg(dev->class_dev, "unable to allocate irq %u\n",
+ pcidev->irq);
+ return retval;
+ }
+ dev->irq = pcidev->irq;
+ dev_dbg(dev->class_dev, "irq %u\n", dev->irq);
+
+ retval = setup_subdevices(dev);
+ if (retval < 0)
+ return retval;
+
+ return 0;
+}
+
+static void detach(struct comedi_device *dev)
+{
+ struct pcidas64_private *devpriv = dev->private;
+
+ if (dev->irq)
+ free_irq(dev->irq, dev);
+ if (devpriv) {
+ if (devpriv->plx9080_iobase) {
+ disable_plx_interrupts(dev);
+ iounmap(devpriv->plx9080_iobase);
+ }
+ if (devpriv->main_iobase)
+ iounmap(devpriv->main_iobase);
+ if (dev->mmio)
+ iounmap(dev->mmio);
+ }
+ comedi_pci_disable(dev);
+ cb_pcidas64_free_dma(dev);
+}
+
+static struct comedi_driver cb_pcidas64_driver = {
+ .driver_name = "cb_pcidas64",
+ .module = THIS_MODULE,
+ .auto_attach = auto_attach,
+ .detach = detach,
+};
+
+static int cb_pcidas64_pci_probe(struct pci_dev *dev,
+ const struct pci_device_id *id)
+{
+ return comedi_pci_auto_config(dev, &cb_pcidas64_driver,
+ id->driver_data);
+}
+
+static const struct pci_device_id cb_pcidas64_pci_table[] = {
+ { PCI_VDEVICE(CB, 0x001d), BOARD_PCIDAS6402_16 },
+ { PCI_VDEVICE(CB, 0x001e), BOARD_PCIDAS6402_12 },
+ { PCI_VDEVICE(CB, 0x0035), BOARD_PCIDAS64_M1_16 },
+ { PCI_VDEVICE(CB, 0x0036), BOARD_PCIDAS64_M2_16 },
+ { PCI_VDEVICE(CB, 0x0037), BOARD_PCIDAS64_M3_16 },
+ { PCI_VDEVICE(CB, 0x0052), BOARD_PCIDAS4020_12 },
+ { PCI_VDEVICE(CB, 0x005d), BOARD_PCIDAS6023 },
+ { PCI_VDEVICE(CB, 0x005e), BOARD_PCIDAS6025 },
+ { PCI_VDEVICE(CB, 0x005f), BOARD_PCIDAS6030 },
+ { PCI_VDEVICE(CB, 0x0060), BOARD_PCIDAS6031 },
+ { PCI_VDEVICE(CB, 0x0061), BOARD_PCIDAS6032 },
+ { PCI_VDEVICE(CB, 0x0062), BOARD_PCIDAS6033 },
+ { PCI_VDEVICE(CB, 0x0063), BOARD_PCIDAS6034 },
+ { PCI_VDEVICE(CB, 0x0064), BOARD_PCIDAS6035 },
+ { PCI_VDEVICE(CB, 0x0065), BOARD_PCIDAS6040 },
+ { PCI_VDEVICE(CB, 0x0066), BOARD_PCIDAS6052 },
+ { PCI_VDEVICE(CB, 0x0067), BOARD_PCIDAS6070 },
+ { PCI_VDEVICE(CB, 0x0068), BOARD_PCIDAS6071 },
+ { PCI_VDEVICE(CB, 0x006f), BOARD_PCIDAS6036 },
+ { PCI_VDEVICE(CB, 0x0078), BOARD_PCIDAS6013 },
+ { PCI_VDEVICE(CB, 0x0079), BOARD_PCIDAS6014 },
+ { 0 }
+};
+MODULE_DEVICE_TABLE(pci, cb_pcidas64_pci_table);
+
+static struct pci_driver cb_pcidas64_pci_driver = {
+ .name = "cb_pcidas64",
+ .id_table = cb_pcidas64_pci_table,
+ .probe = cb_pcidas64_pci_probe,
+ .remove = comedi_pci_auto_unconfig,
+};
+module_comedi_pci_driver(cb_pcidas64_driver, cb_pcidas64_pci_driver);
+
+MODULE_AUTHOR("Comedi http://www.comedi.org");
+MODULE_DESCRIPTION("Comedi low-level driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/comedi/drivers/cb_pcidda.c b/drivers/staging/comedi/drivers/cb_pcidda.c
new file mode 100644
index 000000000..30c9e27d1
--- /dev/null
+++ b/drivers/staging/comedi/drivers/cb_pcidda.c
@@ -0,0 +1,428 @@
+/*
+ * comedi/drivers/cb_pcidda.c
+ * Driver for the ComputerBoards / MeasurementComputing PCI-DDA series.
+ *
+ * Copyright (C) 2001 Ivan Martinez <ivanmr@altavista.com>
+ * Copyright (C) 2001 Frank Mori Hess <fmhess@users.sourceforge.net>
+ *
+ * COMEDI - Linux Control and Measurement Device Interface
+ * Copyright (C) 1997-8 David A. Schleef <ds@schleef.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+/*
+ * Driver: cb_pcidda
+ * Description: MeasurementComputing PCI-DDA series
+ * Devices: [Measurement Computing] PCI-DDA08/12 (pci-dda08/12),
+ * PCI-DDA04/12 (pci-dda04/12), PCI-DDA02/12 (pci-dda02/12),
+ * PCI-DDA08/16 (pci-dda08/16), PCI-DDA04/16 (pci-dda04/16),
+ * PCI-DDA02/16 (pci-dda02/16)
+ * Author: Ivan Martinez <ivanmr@altavista.com>
+ * Frank Mori Hess <fmhess@users.sourceforge.net>
+ * Status: works
+ *
+ * Configuration options: not applicable, uses PCI auto config
+ *
+ * Only simple analog output writing is supported.
+ */
+
+#include <linux/module.h>
+
+#include "../comedi_pci.h"
+
+#include "8255.h"
+
+#define EEPROM_SIZE 128 /* number of entries in eeprom */
+/* maximum number of ao channels for supported boards */
+#define MAX_AO_CHANNELS 8
+
+/* Digital I/O registers */
+#define CB_DDA_DIO0_8255_BASE 0x00
+#define CB_DDA_DIO1_8255_BASE 0x04
+
+/* DAC registers */
+#define CB_DDA_DA_CTRL_REG 0x00 /* D/A Control Register */
+#define CB_DDA_DA_CTRL_SU (1 << 0) /* Simultaneous update */
+#define CB_DDA_DA_CTRL_EN (1 << 1) /* Enable specified DAC */
+#define CB_DDA_DA_CTRL_DAC(x) ((x) << 2) /* Specify DAC channel */
+#define CB_DDA_DA_CTRL_RANGE2V5 (0 << 6) /* 2.5V range */
+#define CB_DDA_DA_CTRL_RANGE5V (2 << 6) /* 5V range */
+#define CB_DDA_DA_CTRL_RANGE10V (3 << 6) /* 10V range */
+#define CB_DDA_DA_CTRL_UNIP (1 << 8) /* Unipolar range */
+
+#define DACALIBRATION1 4 /* D/A CALIBRATION REGISTER 1 */
+/* write bits */
+/* serial data input for eeprom, caldacs, reference dac */
+#define SERIAL_IN_BIT 0x1
+#define CAL_CHANNEL_MASK (0x7 << 1)
+#define CAL_CHANNEL_BITS(channel) (((channel) << 1) & CAL_CHANNEL_MASK)
+/* read bits */
+#define CAL_COUNTER_MASK 0x1f
+/* calibration counter overflow status bit */
+#define CAL_COUNTER_OVERFLOW_BIT 0x20
+/* analog output is less than reference dac voltage */
+#define AO_BELOW_REF_BIT 0x40
+#define SERIAL_OUT_BIT 0x80 /* serial data out, for reading from eeprom */
+
+#define DACALIBRATION2 6 /* D/A CALIBRATION REGISTER 2 */
+#define SELECT_EEPROM_BIT 0x1 /* send serial data in to eeprom */
+/* don't send serial data to MAX542 reference dac */
+#define DESELECT_REF_DAC_BIT 0x2
+/* don't send serial data to caldac n */
+#define DESELECT_CALDAC_BIT(n) (0x4 << (n))
+/* manual says to set this bit with no explanation */
+#define DUMMY_BIT 0x40
+
+#define CB_DDA_DA_DATA_REG(x) (0x08 + ((x) * 2))
+
+/* Offsets for the caldac channels */
+#define CB_DDA_CALDAC_FINE_GAIN 0
+#define CB_DDA_CALDAC_COURSE_GAIN 1
+#define CB_DDA_CALDAC_COURSE_OFFSET 2
+#define CB_DDA_CALDAC_FINE_OFFSET 3
+
+static const struct comedi_lrange cb_pcidda_ranges = {
+ 6, {
+ BIP_RANGE(10),
+ BIP_RANGE(5),
+ BIP_RANGE(2.5),
+ UNI_RANGE(10),
+ UNI_RANGE(5),
+ UNI_RANGE(2.5)
+ }
+};
+
+enum cb_pcidda_boardid {
+ BOARD_DDA02_12,
+ BOARD_DDA04_12,
+ BOARD_DDA08_12,
+ BOARD_DDA02_16,
+ BOARD_DDA04_16,
+ BOARD_DDA08_16,
+};
+
+struct cb_pcidda_board {
+ const char *name;
+ int ao_chans;
+ int ao_bits;
+};
+
+static const struct cb_pcidda_board cb_pcidda_boards[] = {
+ [BOARD_DDA02_12] = {
+ .name = "pci-dda02/12",
+ .ao_chans = 2,
+ .ao_bits = 12,
+ },
+ [BOARD_DDA04_12] = {
+ .name = "pci-dda04/12",
+ .ao_chans = 4,
+ .ao_bits = 12,
+ },
+ [BOARD_DDA08_12] = {
+ .name = "pci-dda08/12",
+ .ao_chans = 8,
+ .ao_bits = 12,
+ },
+ [BOARD_DDA02_16] = {
+ .name = "pci-dda02/16",
+ .ao_chans = 2,
+ .ao_bits = 16,
+ },
+ [BOARD_DDA04_16] = {
+ .name = "pci-dda04/16",
+ .ao_chans = 4,
+ .ao_bits = 16,
+ },
+ [BOARD_DDA08_16] = {
+ .name = "pci-dda08/16",
+ .ao_chans = 8,
+ .ao_bits = 16,
+ },
+};
+
+struct cb_pcidda_private {
+ unsigned long daqio;
+ /* bits last written to da calibration register 1 */
+ unsigned int dac_cal1_bits;
+ /* current range settings for output channels */
+ unsigned int ao_range[MAX_AO_CHANNELS];
+ u16 eeprom_data[EEPROM_SIZE]; /* software copy of board's eeprom */
+};
+
+/* lowlevel read from eeprom */
+static unsigned int cb_pcidda_serial_in(struct comedi_device *dev)
+{
+ struct cb_pcidda_private *devpriv = dev->private;
+ unsigned int value = 0;
+ int i;
+ const int value_width = 16; /* number of bits wide values are */
+
+ for (i = 1; i <= value_width; i++) {
+ /* read bits most significant bit first */
+ if (inw_p(devpriv->daqio + DACALIBRATION1) & SERIAL_OUT_BIT)
+ value |= 1 << (value_width - i);
+ }
+
+ return value;
+}
+
+/* lowlevel write to eeprom/dac */
+static void cb_pcidda_serial_out(struct comedi_device *dev, unsigned int value,
+ unsigned int num_bits)
+{
+ struct cb_pcidda_private *devpriv = dev->private;
+ int i;
+
+ for (i = 1; i <= num_bits; i++) {
+ /* send bits most significant bit first */
+ if (value & (1 << (num_bits - i)))
+ devpriv->dac_cal1_bits |= SERIAL_IN_BIT;
+ else
+ devpriv->dac_cal1_bits &= ~SERIAL_IN_BIT;
+ outw_p(devpriv->dac_cal1_bits, devpriv->daqio + DACALIBRATION1);
+ }
+}
+
+/* reads a 16 bit value from board's eeprom */
+static unsigned int cb_pcidda_read_eeprom(struct comedi_device *dev,
+ unsigned int address)
+{
+ struct cb_pcidda_private *devpriv = dev->private;
+ unsigned int i;
+ unsigned int cal2_bits;
+ unsigned int value;
+ /* one caldac for every two dac channels */
+ const int max_num_caldacs = 4;
+ /* bits to send to tell eeprom we want to read */
+ const int read_instruction = 0x6;
+ const int instruction_length = 3;
+ const int address_length = 8;
+
+ /* send serial output stream to eeprom */
+ cal2_bits = SELECT_EEPROM_BIT | DESELECT_REF_DAC_BIT | DUMMY_BIT;
+ /* deactivate caldacs (one caldac for every two channels) */
+ for (i = 0; i < max_num_caldacs; i++)
+ cal2_bits |= DESELECT_CALDAC_BIT(i);
+ outw_p(cal2_bits, devpriv->daqio + DACALIBRATION2);
+
+ /* tell eeprom we want to read */
+ cb_pcidda_serial_out(dev, read_instruction, instruction_length);
+ /* send address we want to read from */
+ cb_pcidda_serial_out(dev, address, address_length);
+
+ value = cb_pcidda_serial_in(dev);
+
+ /* deactivate eeprom */
+ cal2_bits &= ~SELECT_EEPROM_BIT;
+ outw_p(cal2_bits, devpriv->daqio + DACALIBRATION2);
+
+ return value;
+}
+
+/* writes to 8 bit calibration dacs */
+static void cb_pcidda_write_caldac(struct comedi_device *dev,
+ unsigned int caldac, unsigned int channel,
+ unsigned int value)
+{
+ struct cb_pcidda_private *devpriv = dev->private;
+ unsigned int cal2_bits;
+ unsigned int i;
+ /* caldacs use 3 bit channel specification */
+ const int num_channel_bits = 3;
+ const int num_caldac_bits = 8; /* 8 bit calibration dacs */
+ /* one caldac for every two dac channels */
+ const int max_num_caldacs = 4;
+
+ /* write 3 bit channel */
+ cb_pcidda_serial_out(dev, channel, num_channel_bits);
+ /* write 8 bit caldac value */
+ cb_pcidda_serial_out(dev, value, num_caldac_bits);
+
+/*
+* latch stream into appropriate caldac deselect reference dac
+*/
+ cal2_bits = DESELECT_REF_DAC_BIT | DUMMY_BIT;
+ /* deactivate caldacs (one caldac for every two channels) */
+ for (i = 0; i < max_num_caldacs; i++)
+ cal2_bits |= DESELECT_CALDAC_BIT(i);
+ /* activate the caldac we want */
+ cal2_bits &= ~DESELECT_CALDAC_BIT(caldac);
+ outw_p(cal2_bits, devpriv->daqio + DACALIBRATION2);
+ /* deactivate caldac */
+ cal2_bits |= DESELECT_CALDAC_BIT(caldac);
+ outw_p(cal2_bits, devpriv->daqio + DACALIBRATION2);
+}
+
+/* set caldacs to eeprom values for given channel and range */
+static void cb_pcidda_calibrate(struct comedi_device *dev, unsigned int channel,
+ unsigned int range)
+{
+ struct cb_pcidda_private *devpriv = dev->private;
+ unsigned int caldac = channel / 2; /* two caldacs per channel */
+ unsigned int chan = 4 * (channel % 2); /* caldac channel base */
+ unsigned int index = 2 * range + 12 * channel;
+ unsigned int offset;
+ unsigned int gain;
+
+ /* save range so we can tell when we need to readjust calibration */
+ devpriv->ao_range[channel] = range;
+
+ /* get values from eeprom data */
+ offset = devpriv->eeprom_data[0x7 + index];
+ gain = devpriv->eeprom_data[0x8 + index];
+
+ /* set caldacs */
+ cb_pcidda_write_caldac(dev, caldac, chan + CB_DDA_CALDAC_COURSE_OFFSET,
+ (offset >> 8) & 0xff);
+ cb_pcidda_write_caldac(dev, caldac, chan + CB_DDA_CALDAC_FINE_OFFSET,
+ offset & 0xff);
+ cb_pcidda_write_caldac(dev, caldac, chan + CB_DDA_CALDAC_COURSE_GAIN,
+ (gain >> 8) & 0xff);
+ cb_pcidda_write_caldac(dev, caldac, chan + CB_DDA_CALDAC_FINE_GAIN,
+ gain & 0xff);
+}
+
+static int cb_pcidda_ao_insn_write(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ struct cb_pcidda_private *devpriv = dev->private;
+ unsigned int channel = CR_CHAN(insn->chanspec);
+ unsigned int range = CR_RANGE(insn->chanspec);
+ unsigned int ctrl;
+
+ if (range != devpriv->ao_range[channel])
+ cb_pcidda_calibrate(dev, channel, range);
+
+ ctrl = CB_DDA_DA_CTRL_EN | CB_DDA_DA_CTRL_DAC(channel);
+
+ switch (range) {
+ case 0:
+ case 3:
+ ctrl |= CB_DDA_DA_CTRL_RANGE10V;
+ break;
+ case 1:
+ case 4:
+ ctrl |= CB_DDA_DA_CTRL_RANGE5V;
+ break;
+ case 2:
+ case 5:
+ ctrl |= CB_DDA_DA_CTRL_RANGE2V5;
+ break;
+ }
+
+ if (range > 2)
+ ctrl |= CB_DDA_DA_CTRL_UNIP;
+
+ outw(ctrl, devpriv->daqio + CB_DDA_DA_CTRL_REG);
+
+ outw(data[0], devpriv->daqio + CB_DDA_DA_DATA_REG(channel));
+
+ return insn->n;
+}
+
+static int cb_pcidda_auto_attach(struct comedi_device *dev,
+ unsigned long context)
+{
+ struct pci_dev *pcidev = comedi_to_pci_dev(dev);
+ const struct cb_pcidda_board *thisboard = NULL;
+ struct cb_pcidda_private *devpriv;
+ struct comedi_subdevice *s;
+ int i;
+ int ret;
+
+ if (context < ARRAY_SIZE(cb_pcidda_boards))
+ thisboard = &cb_pcidda_boards[context];
+ if (!thisboard)
+ return -ENODEV;
+ dev->board_ptr = thisboard;
+ dev->board_name = thisboard->name;
+
+ devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
+ if (!devpriv)
+ return -ENOMEM;
+
+ ret = comedi_pci_enable(dev);
+ if (ret)
+ return ret;
+ dev->iobase = pci_resource_start(pcidev, 2);
+ devpriv->daqio = pci_resource_start(pcidev, 3);
+
+ ret = comedi_alloc_subdevices(dev, 3);
+ if (ret)
+ return ret;
+
+ s = &dev->subdevices[0];
+ /* analog output subdevice */
+ s->type = COMEDI_SUBD_AO;
+ s->subdev_flags = SDF_WRITABLE;
+ s->n_chan = thisboard->ao_chans;
+ s->maxdata = (1 << thisboard->ao_bits) - 1;
+ s->range_table = &cb_pcidda_ranges;
+ s->insn_write = cb_pcidda_ao_insn_write;
+
+ /* two 8255 digital io subdevices */
+ for (i = 0; i < 2; i++) {
+ s = &dev->subdevices[1 + i];
+ ret = subdev_8255_init(dev, s, NULL, i * I8255_SIZE);
+ if (ret)
+ return ret;
+ }
+
+ /* Read the caldac eeprom data */
+ for (i = 0; i < EEPROM_SIZE; i++)
+ devpriv->eeprom_data[i] = cb_pcidda_read_eeprom(dev, i);
+
+ /* set calibrations dacs */
+ for (i = 0; i < thisboard->ao_chans; i++)
+ cb_pcidda_calibrate(dev, i, devpriv->ao_range[i]);
+
+ return 0;
+}
+
+static struct comedi_driver cb_pcidda_driver = {
+ .driver_name = "cb_pcidda",
+ .module = THIS_MODULE,
+ .auto_attach = cb_pcidda_auto_attach,
+ .detach = comedi_pci_detach,
+};
+
+static int cb_pcidda_pci_probe(struct pci_dev *dev,
+ const struct pci_device_id *id)
+{
+ return comedi_pci_auto_config(dev, &cb_pcidda_driver,
+ id->driver_data);
+}
+
+static const struct pci_device_id cb_pcidda_pci_table[] = {
+ { PCI_VDEVICE(CB, 0x0020), BOARD_DDA02_12 },
+ { PCI_VDEVICE(CB, 0x0021), BOARD_DDA04_12 },
+ { PCI_VDEVICE(CB, 0x0022), BOARD_DDA08_12 },
+ { PCI_VDEVICE(CB, 0x0023), BOARD_DDA02_16 },
+ { PCI_VDEVICE(CB, 0x0024), BOARD_DDA04_16 },
+ { PCI_VDEVICE(CB, 0x0025), BOARD_DDA08_16 },
+ { 0 }
+};
+MODULE_DEVICE_TABLE(pci, cb_pcidda_pci_table);
+
+static struct pci_driver cb_pcidda_pci_driver = {
+ .name = "cb_pcidda",
+ .id_table = cb_pcidda_pci_table,
+ .probe = cb_pcidda_pci_probe,
+ .remove = comedi_pci_auto_unconfig,
+};
+module_comedi_pci_driver(cb_pcidda_driver, cb_pcidda_pci_driver);
+
+MODULE_AUTHOR("Comedi http://www.comedi.org");
+MODULE_DESCRIPTION("Comedi low-level driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/comedi/drivers/cb_pcimdas.c b/drivers/staging/comedi/drivers/cb_pcimdas.c
new file mode 100644
index 000000000..4ebf5aae5
--- /dev/null
+++ b/drivers/staging/comedi/drivers/cb_pcimdas.c
@@ -0,0 +1,482 @@
+/*
+ * comedi/drivers/cb_pcimdas.c
+ * Comedi driver for Computer Boards PCIM-DAS1602/16 and PCIe-DAS1602/16
+ *
+ * COMEDI - Linux Control and Measurement Device Interface
+ * Copyright (C) 2000 David A. Schleef <ds@schleef.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+/*
+ * Driver: cb_pcimdas
+ * Description: Measurement Computing PCI Migration series boards
+ * Devices: [ComputerBoards] PCIM-DAS1602/16 (cb_pcimdas), PCIe-DAS1602/16
+ * Author: Richard Bytheway
+ * Updated: Mon, 13 Oct 2014 11:57:39 +0000
+ * Status: experimental
+ *
+ * Written to support the PCIM-DAS1602/16 and PCIe-DAS1602/16.
+ *
+ * Configuration Options:
+ * none
+ *
+ * Manual configuration of PCI(e) cards is not supported; they are configured
+ * automatically.
+ *
+ * Developed from cb_pcidas and skel by Richard Bytheway (mocelet@sucs.org).
+ * Only supports DIO, AO and simple AI in it's present form.
+ * No interrupts, multi channel or FIFO AI,
+ * although the card looks like it could support this.
+ *
+ * http://www.mccdaq.com/PDFs/Manuals/pcim-das1602-16.pdf
+ * http://www.mccdaq.com/PDFs/Manuals/pcie-das1602-16.pdf
+ */
+
+#include <linux/module.h>
+#include <linux/interrupt.h>
+
+#include "../comedi_pci.h"
+
+#include "comedi_8254.h"
+#include "plx9052.h"
+#include "8255.h"
+
+/*
+ * PCI Bar 1 Register map
+ * see plx9052.h for register and bit defines
+ */
+
+/*
+ * PCI Bar 2 Register map (devpriv->daqio)
+ */
+#define PCIMDAS_AI_REG 0x00
+#define PCIMDAS_AI_SOFTTRIG_REG 0x00
+#define PCIMDAS_AO_REG(x) (0x02 + ((x) * 2))
+
+/*
+ * PCI Bar 3 Register map (devpriv->BADR3)
+ */
+#define PCIMDAS_MUX_REG 0x00
+#define PCIMDAS_MUX(_lo, _hi) ((_lo) | ((_hi) << 4))
+#define PCIMDAS_DI_DO_REG 0x01
+#define PCIMDAS_STATUS_REG 0x02
+#define PCIMDAS_STATUS_EOC BIT(7)
+#define PCIMDAS_STATUS_UB BIT(6)
+#define PCIMDAS_STATUS_MUX BIT(5)
+#define PCIMDAS_STATUS_CLK BIT(4)
+#define PCIMDAS_STATUS_TO_CURR_MUX(x) ((x) & 0xf)
+#define PCIMDAS_CONV_STATUS_REG 0x03
+#define PCIMDAS_CONV_STATUS_EOC BIT(7)
+#define PCIMDAS_CONV_STATUS_EOB BIT(6)
+#define PCIMDAS_CONV_STATUS_EOA BIT(5)
+#define PCIMDAS_CONV_STATUS_FNE BIT(4)
+#define PCIMDAS_CONV_STATUS_FHF BIT(3)
+#define PCIMDAS_CONV_STATUS_OVERRUN BIT(2)
+#define PCIMDAS_IRQ_REG 0x04
+#define PCIMDAS_IRQ_INTE BIT(7)
+#define PCIMDAS_IRQ_INT BIT(6)
+#define PCIMDAS_IRQ_OVERRUN BIT(4)
+#define PCIMDAS_IRQ_EOA BIT(3)
+#define PCIMDAS_IRQ_EOA_INT_SEL BIT(2)
+#define PCIMDAS_IRQ_INTSEL(x) ((x) << 0)
+#define PCIMDAS_IRQ_INTSEL_EOC PCIMDAS_IRQ_INTSEL(0)
+#define PCIMDAS_IRQ_INTSEL_FNE PCIMDAS_IRQ_INTSEL(1)
+#define PCIMDAS_IRQ_INTSEL_EOB PCIMDAS_IRQ_INTSEL(2)
+#define PCIMDAS_IRQ_INTSEL_FHF_EOA PCIMDAS_IRQ_INTSEL(3)
+#define PCIMDAS_PACER_REG 0x05
+#define PCIMDAS_PACER_GATE_STATUS BIT(6)
+#define PCIMDAS_PACER_GATE_POL BIT(5)
+#define PCIMDAS_PACER_GATE_LATCH BIT(4)
+#define PCIMDAS_PACER_GATE_EN BIT(3)
+#define PCIMDAS_PACER_EXT_PACER_POL BIT(2)
+#define PCIMDAS_PACER_SRC(x) ((x) << 0)
+#define PCIMDAS_PACER_SRC_POLLED PCIMDAS_PACER_SRC(0)
+#define PCIMDAS_PACER_SRC_EXT PCIMDAS_PACER_SRC(2)
+#define PCIMDAS_PACER_SRC_INT PCIMDAS_PACER_SRC(3)
+#define PCIMDAS_PACER_SRC_MASK (3 << 0)
+#define PCIMDAS_BURST_REG 0x06
+#define PCIMDAS_BURST_BME BIT(1)
+#define PCIMDAS_BURST_CONV_EN BIT(0)
+#define PCIMDAS_GAIN_REG 0x07
+#define PCIMDAS_8254_BASE 0x08
+#define PCIMDAS_USER_CNTR_REG 0x0c
+#define PCIMDAS_USER_CNTR_CTR1_CLK_SEL BIT(0)
+#define PCIMDAS_RESIDUE_MSB_REG 0x0d
+#define PCIMDAS_RESIDUE_LSB_REG 0x0e
+
+/*
+ * PCI Bar 4 Register map (dev->iobase)
+ */
+#define PCIMDAS_8255_BASE 0x00
+
+static const struct comedi_lrange cb_pcimdas_ai_bip_range = {
+ 4, {
+ BIP_RANGE(10),
+ BIP_RANGE(5),
+ BIP_RANGE(2.5),
+ BIP_RANGE(1.25)
+ }
+};
+
+static const struct comedi_lrange cb_pcimdas_ai_uni_range = {
+ 4, {
+ UNI_RANGE(10),
+ UNI_RANGE(5),
+ UNI_RANGE(2.5),
+ UNI_RANGE(1.25)
+ }
+};
+
+/*
+ * The Analog Output range is not programmable. The DAC ranges are
+ * jumper-settable on the board. The settings are not software-readable.
+ */
+static const struct comedi_lrange cb_pcimdas_ao_range = {
+ 4, {
+ BIP_RANGE(10),
+ BIP_RANGE(5),
+ UNI_RANGE(10),
+ UNI_RANGE(5)
+ }
+};
+
+/*
+ * this structure is for data unique to this hardware driver. If
+ * several hardware drivers keep similar information in this structure,
+ * feel free to suggest moving the variable to the struct comedi_device
+ * struct.
+ */
+struct cb_pcimdas_private {
+ /* base addresses */
+ unsigned long daqio;
+ unsigned long BADR3;
+};
+
+static int cb_pcimdas_ai_eoc(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned long context)
+{
+ struct cb_pcimdas_private *devpriv = dev->private;
+ unsigned int status;
+
+ status = inb(devpriv->BADR3 + PCIMDAS_STATUS_REG);
+ if ((status & PCIMDAS_STATUS_EOC) == 0)
+ return 0;
+ return -EBUSY;
+}
+
+static int cb_pcimdas_ai_insn_read(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ struct cb_pcimdas_private *devpriv = dev->private;
+ unsigned int chan = CR_CHAN(insn->chanspec);
+ unsigned int range = CR_RANGE(insn->chanspec);
+ int n;
+ unsigned int d;
+ int ret;
+
+ /* only support sw initiated reads from a single channel */
+
+ /* configure for sw initiated read */
+ d = inb(devpriv->BADR3 + PCIMDAS_PACER_REG);
+ if ((d & PCIMDAS_PACER_SRC_MASK) != PCIMDAS_PACER_SRC_POLLED) {
+ d &= ~PCIMDAS_PACER_SRC_MASK;
+ d |= PCIMDAS_PACER_SRC_POLLED;
+ outb(d, devpriv->BADR3 + PCIMDAS_PACER_REG);
+ }
+
+ /* set bursting off, conversions on */
+ outb(PCIMDAS_BURST_CONV_EN, devpriv->BADR3 + PCIMDAS_BURST_REG);
+
+ /* set range */
+ outb(range, devpriv->BADR3 + PCIMDAS_GAIN_REG);
+
+ /* set mux for single channel scan */
+ outb(PCIMDAS_MUX(chan, chan), devpriv->BADR3 + PCIMDAS_MUX_REG);
+
+ /* convert n samples */
+ for (n = 0; n < insn->n; n++) {
+ /* trigger conversion */
+ outw(0, devpriv->daqio + PCIMDAS_AI_SOFTTRIG_REG);
+
+ /* wait for conversion to end */
+ ret = comedi_timeout(dev, s, insn, cb_pcimdas_ai_eoc, 0);
+ if (ret)
+ return ret;
+
+ /* read data */
+ data[n] = inw(devpriv->daqio + PCIMDAS_AI_REG);
+ }
+
+ /* return the number of samples read/written */
+ return n;
+}
+
+static int cb_pcimdas_ao_insn_write(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ struct cb_pcimdas_private *devpriv = dev->private;
+ unsigned int chan = CR_CHAN(insn->chanspec);
+ unsigned int val = s->readback[chan];
+ int i;
+
+ for (i = 0; i < insn->n; i++) {
+ val = data[i];
+ outw(val, devpriv->daqio + PCIMDAS_AO_REG(chan));
+ }
+ s->readback[chan] = val;
+
+ return insn->n;
+}
+
+static int cb_pcimdas_di_insn_bits(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ struct cb_pcimdas_private *devpriv = dev->private;
+ unsigned int val;
+
+ val = inb(devpriv->BADR3 + PCIMDAS_DI_DO_REG);
+
+ data[1] = val & 0x0f;
+
+ return insn->n;
+}
+
+static int cb_pcimdas_do_insn_bits(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ struct cb_pcimdas_private *devpriv = dev->private;
+
+ if (comedi_dio_update_state(s, data))
+ outb(s->state, devpriv->BADR3 + PCIMDAS_DI_DO_REG);
+
+ data[1] = s->state;
+
+ return insn->n;
+}
+
+static int cb_pcimdas_counter_insn_config(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ struct cb_pcimdas_private *devpriv = dev->private;
+ unsigned int ctrl;
+
+ switch (data[0]) {
+ case INSN_CONFIG_SET_CLOCK_SRC:
+ switch (data[1]) {
+ case 0: /* internal 100 kHz clock */
+ ctrl = PCIMDAS_USER_CNTR_CTR1_CLK_SEL;
+ break;
+ case 1: /* external clk on pin 21 */
+ ctrl = 0;
+ break;
+ default:
+ return -EINVAL;
+ }
+ outb(ctrl, devpriv->BADR3 + PCIMDAS_USER_CNTR_REG);
+ break;
+ case INSN_CONFIG_GET_CLOCK_SRC:
+ ctrl = inb(devpriv->BADR3 + PCIMDAS_USER_CNTR_REG);
+ if (ctrl & PCIMDAS_USER_CNTR_CTR1_CLK_SEL) {
+ data[1] = 0;
+ data[2] = I8254_OSC_BASE_100KHZ;
+ } else {
+ data[1] = 1;
+ data[2] = 0;
+ }
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return insn->n;
+}
+
+static unsigned int cb_pcimdas_pacer_clk(struct comedi_device *dev)
+{
+ struct cb_pcimdas_private *devpriv = dev->private;
+ unsigned int status;
+
+ /* The Pacer Clock jumper selects a 10 MHz or 1 MHz clock */
+ status = inb(devpriv->BADR3 + PCIMDAS_STATUS_REG);
+ if (status & PCIMDAS_STATUS_CLK)
+ return I8254_OSC_BASE_10MHZ;
+ return I8254_OSC_BASE_1MHZ;
+}
+
+static bool cb_pcimdas_is_ai_se(struct comedi_device *dev)
+{
+ struct cb_pcimdas_private *devpriv = dev->private;
+ unsigned int status;
+
+ /*
+ * The number of Analog Input channels is set with the
+ * Analog Input Mode Switch on the board. The board can
+ * have 16 single-ended or 8 differential channels.
+ */
+ status = inb(devpriv->BADR3 + PCIMDAS_STATUS_REG);
+ return status & PCIMDAS_STATUS_MUX;
+}
+
+static bool cb_pcimdas_is_ai_uni(struct comedi_device *dev)
+{
+ struct cb_pcimdas_private *devpriv = dev->private;
+ unsigned int status;
+
+ /*
+ * The Analog Input range polarity is set with the
+ * Analog Input Polarity Switch on the board. The
+ * inputs can be set to Unipolar or Bipolar ranges.
+ */
+ status = inb(devpriv->BADR3 + PCIMDAS_STATUS_REG);
+ return status & PCIMDAS_STATUS_UB;
+}
+
+static int cb_pcimdas_auto_attach(struct comedi_device *dev,
+ unsigned long context_unused)
+{
+ struct pci_dev *pcidev = comedi_to_pci_dev(dev);
+ struct cb_pcimdas_private *devpriv;
+ struct comedi_subdevice *s;
+ int ret;
+
+ devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
+ if (!devpriv)
+ return -ENOMEM;
+
+ ret = comedi_pci_enable(dev);
+ if (ret)
+ return ret;
+
+ devpriv->daqio = pci_resource_start(pcidev, 2);
+ devpriv->BADR3 = pci_resource_start(pcidev, 3);
+ dev->iobase = pci_resource_start(pcidev, 4);
+
+ dev->pacer = comedi_8254_init(devpriv->BADR3 + PCIMDAS_8254_BASE,
+ cb_pcimdas_pacer_clk(dev),
+ I8254_IO8, 0);
+ if (!dev->pacer)
+ return -ENOMEM;
+
+ ret = comedi_alloc_subdevices(dev, 6);
+ if (ret)
+ return ret;
+
+ /* Analog Input subdevice */
+ s = &dev->subdevices[0];
+ s->type = COMEDI_SUBD_AI;
+ s->subdev_flags = SDF_READABLE;
+ if (cb_pcimdas_is_ai_se(dev)) {
+ s->subdev_flags |= SDF_GROUND;
+ s->n_chan = 16;
+ } else {
+ s->subdev_flags |= SDF_DIFF;
+ s->n_chan = 8;
+ }
+ s->maxdata = 0xffff;
+ s->range_table = cb_pcimdas_is_ai_uni(dev) ? &cb_pcimdas_ai_uni_range
+ : &cb_pcimdas_ai_bip_range;
+ s->insn_read = cb_pcimdas_ai_insn_read;
+
+ /* Analog Output subdevice */
+ s = &dev->subdevices[1];
+ s->type = COMEDI_SUBD_AO;
+ s->subdev_flags = SDF_WRITABLE;
+ s->n_chan = 2;
+ s->maxdata = 0xfff;
+ s->range_table = &cb_pcimdas_ao_range;
+ s->insn_write = cb_pcimdas_ao_insn_write;
+
+ ret = comedi_alloc_subdev_readback(s);
+ if (ret)
+ return ret;
+
+ /* Digital I/O subdevice */
+ s = &dev->subdevices[2];
+ ret = subdev_8255_init(dev, s, NULL, PCIMDAS_8255_BASE);
+ if (ret)
+ return ret;
+
+ /* Digital Input subdevice (main connector) */
+ s = &dev->subdevices[3];
+ s->type = COMEDI_SUBD_DI;
+ s->subdev_flags = SDF_READABLE;
+ s->n_chan = 4;
+ s->maxdata = 1;
+ s->range_table = &range_digital;
+ s->insn_bits = cb_pcimdas_di_insn_bits;
+
+ /* Digital Output subdevice (main connector) */
+ s = &dev->subdevices[4];
+ s->type = COMEDI_SUBD_DO;
+ s->subdev_flags = SDF_WRITABLE;
+ s->n_chan = 4;
+ s->maxdata = 1;
+ s->range_table = &range_digital;
+ s->insn_bits = cb_pcimdas_do_insn_bits;
+
+ /* Counter subdevice (8254) */
+ s = &dev->subdevices[5];
+ comedi_8254_subdevice_init(s, dev->pacer);
+
+ dev->pacer->insn_config = cb_pcimdas_counter_insn_config;
+
+ /* counters 1 and 2 are used internally for the pacer */
+ comedi_8254_set_busy(dev->pacer, 1, true);
+ comedi_8254_set_busy(dev->pacer, 2, true);
+
+ return 0;
+}
+
+static struct comedi_driver cb_pcimdas_driver = {
+ .driver_name = "cb_pcimdas",
+ .module = THIS_MODULE,
+ .auto_attach = cb_pcimdas_auto_attach,
+ .detach = comedi_pci_detach,
+};
+
+static int cb_pcimdas_pci_probe(struct pci_dev *dev,
+ const struct pci_device_id *id)
+{
+ return comedi_pci_auto_config(dev, &cb_pcimdas_driver,
+ id->driver_data);
+}
+
+static const struct pci_device_id cb_pcimdas_pci_table[] = {
+ { PCI_DEVICE(PCI_VENDOR_ID_CB, 0x0056) }, /* PCIM-DAS1602/16 */
+ { PCI_DEVICE(PCI_VENDOR_ID_CB, 0x0115) }, /* PCIe-DAS1602/16 */
+ { 0 }
+};
+MODULE_DEVICE_TABLE(pci, cb_pcimdas_pci_table);
+
+static struct pci_driver cb_pcimdas_pci_driver = {
+ .name = "cb_pcimdas",
+ .id_table = cb_pcimdas_pci_table,
+ .probe = cb_pcimdas_pci_probe,
+ .remove = comedi_pci_auto_unconfig,
+};
+module_comedi_pci_driver(cb_pcimdas_driver, cb_pcimdas_pci_driver);
+
+MODULE_AUTHOR("Comedi http://www.comedi.org");
+MODULE_DESCRIPTION("Comedi driver for PCIM-DAS1602/16 and PCIe-DAS1602/16");
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/comedi/drivers/cb_pcimdda.c b/drivers/staging/comedi/drivers/cb_pcimdda.c
new file mode 100644
index 000000000..a4781dbbd
--- /dev/null
+++ b/drivers/staging/comedi/drivers/cb_pcimdda.c
@@ -0,0 +1,206 @@
+/*
+ comedi/drivers/cb_pcimdda.c
+ Computer Boards PCIM-DDA06-16 Comedi driver
+ Author: Calin Culianu <calin@ajvar.org>
+
+ COMEDI - Linux Control and Measurement Device Interface
+ Copyright (C) 2000 David A. Schleef <ds@schleef.org>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+*/
+/*
+Driver: cb_pcimdda
+Description: Measurement Computing PCIM-DDA06-16
+Devices: [Measurement Computing] PCIM-DDA06-16 (cb_pcimdda)
+Author: Calin Culianu <calin@ajvar.org>
+Updated: Mon, 14 Apr 2008 15:15:51 +0100
+Status: works
+
+All features of the PCIM-DDA06-16 board are supported. This board
+has 6 16-bit AO channels, and the usual 8255 DIO setup. (24 channels,
+configurable in banks of 8 and 4, etc.). This board does not support commands.
+
+The board has a peculiar way of specifying AO gain/range settings -- You have
+1 jumper bank on the card, which either makes all 6 AO channels either
+5 Volt unipolar, 5V bipolar, 10 Volt unipolar or 10V bipolar.
+
+Since there is absolutely _no_ way to tell in software how this jumper is set
+(well, at least according to the rather thin spec. from Measurement Computing
+ that comes with the board), the driver assumes the jumper is at its factory
+default setting of +/-5V.
+
+Also of note is the fact that this board features another jumper, whose
+state is also completely invisible to software. It toggles two possible AO
+output modes on the board:
+
+ - Update Mode: Writing to an AO channel instantaneously updates the actual
+ signal output by the DAC on the board (this is the factory default).
+ - Simultaneous XFER Mode: Writing to an AO channel has no effect until
+ you read from any one of the AO channels. This is useful for loading
+ all 6 AO values, and then reading from any one of the AO channels on the
+ device to instantly update all 6 AO values in unison. Useful for some
+ control apps, I would assume? If your jumper is in this setting, then you
+ need to issue your comedi_data_write()s to load all the values you want,
+ then issue one comedi_data_read() on any channel on the AO subdevice
+ to initiate the simultaneous XFER.
+
+Configuration Options: not applicable, uses PCI auto config
+*/
+
+/*
+ This is a driver for the Computer Boards PCIM-DDA06-16 Analog Output
+ card. This board has a unique register layout and as such probably
+ deserves its own driver file.
+
+ It is theoretically possible to integrate this board into the cb_pcidda
+ file, but since that isn't my code, I didn't want to significantly
+ modify that file to support this board (I thought it impolite to do so).
+
+ At any rate, if you feel ambitious, please feel free to take
+ the code out of this file and combine it with a more unified driver
+ file.
+
+ I would like to thank Timothy Curry <Timothy.Curry@rdec.redstone.army.mil>
+ for lending me a board so that I could write this driver.
+
+ -Calin Culianu <calin@ajvar.org>
+ */
+
+#include <linux/module.h>
+
+#include "../comedi_pci.h"
+
+#include "8255.h"
+
+/* device ids of the cards we support -- currently only 1 card supported */
+#define PCI_ID_PCIM_DDA06_16 0x0053
+
+/*
+ * Register map, 8-bit access only
+ */
+#define PCIMDDA_DA_CHAN(x) (0x00 + (x) * 2)
+#define PCIMDDA_8255_BASE_REG 0x0c
+
+static int cb_pcimdda_ao_insn_write(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ unsigned int chan = CR_CHAN(insn->chanspec);
+ unsigned long offset = dev->iobase + PCIMDDA_DA_CHAN(chan);
+ unsigned int val = s->readback[chan];
+ int i;
+
+ for (i = 0; i < insn->n; i++) {
+ val = data[i];
+
+ /*
+ * Write the LSB then MSB.
+ *
+ * If the simultaneous xfer mode is selected by the
+ * jumper on the card, a read instruction is needed
+ * in order to initiate the simultaneous transfer.
+ * Otherwise, the DAC will be updated when the MSB
+ * is written.
+ */
+ outb(val & 0x00ff, offset);
+ outb((val >> 8) & 0x00ff, offset + 1);
+ }
+ s->readback[chan] = val;
+
+ return insn->n;
+}
+
+static int cb_pcimdda_ao_insn_read(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ unsigned int chan = CR_CHAN(insn->chanspec);
+
+ /* Initiate the simultaneous transfer */
+ inw(dev->iobase + PCIMDDA_DA_CHAN(chan));
+
+ return comedi_readback_insn_read(dev, s, insn, data);
+}
+
+static int cb_pcimdda_auto_attach(struct comedi_device *dev,
+ unsigned long context_unused)
+{
+ struct pci_dev *pcidev = comedi_to_pci_dev(dev);
+ struct comedi_subdevice *s;
+ int ret;
+
+ ret = comedi_pci_enable(dev);
+ if (ret)
+ return ret;
+ dev->iobase = pci_resource_start(pcidev, 3);
+
+ ret = comedi_alloc_subdevices(dev, 2);
+ if (ret)
+ return ret;
+
+ s = &dev->subdevices[0];
+ /* analog output subdevice */
+ s->type = COMEDI_SUBD_AO;
+ s->subdev_flags = SDF_WRITABLE | SDF_READABLE;
+ s->n_chan = 6;
+ s->maxdata = 0xffff;
+ s->range_table = &range_bipolar5;
+ s->insn_write = cb_pcimdda_ao_insn_write;
+ s->insn_read = cb_pcimdda_ao_insn_read;
+
+ ret = comedi_alloc_subdev_readback(s);
+ if (ret)
+ return ret;
+
+ s = &dev->subdevices[1];
+ /* digital i/o subdevice */
+ ret = subdev_8255_init(dev, s, NULL, PCIMDDA_8255_BASE_REG);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static struct comedi_driver cb_pcimdda_driver = {
+ .driver_name = "cb_pcimdda",
+ .module = THIS_MODULE,
+ .auto_attach = cb_pcimdda_auto_attach,
+ .detach = comedi_pci_detach,
+};
+
+static int cb_pcimdda_pci_probe(struct pci_dev *dev,
+ const struct pci_device_id *id)
+{
+ return comedi_pci_auto_config(dev, &cb_pcimdda_driver,
+ id->driver_data);
+}
+
+static const struct pci_device_id cb_pcimdda_pci_table[] = {
+ { PCI_DEVICE(PCI_VENDOR_ID_CB, PCI_ID_PCIM_DDA06_16) },
+ { 0 }
+};
+MODULE_DEVICE_TABLE(pci, cb_pcimdda_pci_table);
+
+static struct pci_driver cb_pcimdda_driver_pci_driver = {
+ .name = "cb_pcimdda",
+ .id_table = cb_pcimdda_pci_table,
+ .probe = cb_pcimdda_pci_probe,
+ .remove = comedi_pci_auto_unconfig,
+};
+module_comedi_pci_driver(cb_pcimdda_driver, cb_pcimdda_driver_pci_driver);
+
+MODULE_AUTHOR("Calin A. Culianu <calin@rtlab.org>");
+MODULE_DESCRIPTION("Comedi low-level driver for the Computerboards PCIM-DDA "
+ "series. Currently only supports PCIM-DDA06-16 (which "
+ "also happens to be the only board in this series. :) ) ");
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/comedi/drivers/comedi_8254.c b/drivers/staging/comedi/drivers/comedi_8254.c
new file mode 100644
index 000000000..0d5d56b61
--- /dev/null
+++ b/drivers/staging/comedi/drivers/comedi_8254.c
@@ -0,0 +1,664 @@
+/*
+ * comedi_8254.c
+ * Generic 8254 timer/counter support
+ * Copyright (C) 2014 H Hartley Sweeten <hsweeten@visionengravers.com>
+ *
+ * Based on 8253.h and various subdevice implementations in comedi drivers.
+ *
+ * COMEDI - Linux Control and Measurement Device Interface
+ * Copyright (C) 2000 David A. Schleef <ds@schleef.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+/*
+ * Module: comedi_8254
+ * Description: Generic 8254 timer/counter support
+ * Author: H Hartley Sweeten <hsweeten@visionengravers.com>
+ * Updated: Thu Jan 8 16:45:45 MST 2015
+ * Status: works
+ *
+ * This module is not used directly by end-users. Rather, it is used by other
+ * drivers to provide support for an 8254 Programmable Interval Timer. These
+ * counters are typically used to generate the pacer clock used for data
+ * acquisition. Some drivers also expose the counters for general purpose use.
+ *
+ * This module provides the following basic functions:
+ *
+ * comedi_8254_init() / comedi_8254_mm_init()
+ * Initializes this module to access the 8254 registers. The _mm version
+ * sets up the module for MMIO register access the other for PIO access.
+ * The pointer returned from these functions is normally stored in the
+ * comedi_device dev->pacer and will be freed by the comedi core during
+ * the driver (*detach). If a driver has multiple 8254 devices, they need
+ * to be stored in the drivers private data and freed when the driver is
+ * detached.
+ *
+ * NOTE: The counters are reset by setting them to I8254_MODE0 as part of
+ * this initialization.
+ *
+ * comedi_8254_set_mode()
+ * Sets a counters operation mode:
+ * I8254_MODE0 Interrupt on terminal count
+ * I8254_MODE1 Hardware retriggerable one-shot
+ * I8254_MODE2 Rate generator
+ * I8254_MODE3 Square wave mode
+ * I8254_MODE4 Software triggered strobe
+ * I8254_MODE5 Hardware triggered strobe (retriggerable)
+ *
+ * In addition I8254_BCD and I8254_BINARY specify the counting mode:
+ * I8254_BCD BCD counting
+ * I8254_BINARY Binary counting
+ *
+ * comedi_8254_write()
+ * Writes an initial value to a counter.
+ *
+ * The largest possible initial count is 0; this is equivalent to 2^16
+ * for binary counting and 10^4 for BCD counting.
+ *
+ * NOTE: The counter does not stop when it reaches zero. In Mode 0, 1, 4,
+ * and 5 the counter "wraps around" to the highest count, either 0xffff
+ * for binary counting or 9999 for BCD counting, and continues counting.
+ * Modes 2 and 3 are periodic; the counter reloads itself with the initial
+ * count and continues counting from there.
+ *
+ * comedi_8254_read()
+ * Reads the current value from a counter.
+ *
+ * comedi_8254_status()
+ * Reads the status of a counter.
+ *
+ * comedi_8254_load()
+ * Sets a counters operation mode and writes the initial value.
+ *
+ * Typically the pacer clock is created by cascading two of the 16-bit counters
+ * to create a 32-bit rate generator (I8254_MODE2). These functions are
+ * provided to handle the cascaded counters:
+ *
+ * comedi_8254_ns_to_timer()
+ * Calculates the divisor value needed for a single counter to generate
+ * ns timing.
+ *
+ * comedi_8254_cascade_ns_to_timer()
+ * Calculates the two divisor values needed to the generate the pacer
+ * clock (in ns).
+ *
+ * comedi_8254_update_divisors()
+ * Transfers the intermediate divisor values to the current divisors.
+ *
+ * comedi_8254_pacer_enable()
+ * Programs the mode of the cascaded counters and writes the current
+ * divisor values.
+ *
+ * To expose the counters as a subdevice for general purpose use the following
+ * functions a provided:
+ *
+ * comedi_8254_subdevice_init()
+ * Initializes a comedi_subdevice to use the 8254 timer.
+ *
+ * comedi_8254_set_busy()
+ * Internally flags a counter as "busy". This is done to protect the
+ * counters that are used for the cascaded 32-bit pacer.
+ *
+ * The subdevice provides (*insn_read) and (*insn_write) operations to read
+ * the current value and write an initial value to a counter. A (*insn_config)
+ * operation is also provided to handle the following comedi instructions:
+ *
+ * INSN_CONFIG_SET_COUNTER_MODE calls comedi_8254_set_mode()
+ * INSN_CONFIG_8254_READ_STATUS calls comedi_8254_status()
+ *
+ * The (*insn_config) member of comedi_8254 can be initialized by the external
+ * driver to handle any additional instructions.
+ *
+ * NOTE: Gate control, clock routing, and any interrupt handling for the
+ * counters is not handled by this module. These features are driver dependent.
+ */
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/io.h>
+
+#include "../comedidev.h"
+
+#include "comedi_8254.h"
+
+static unsigned int __i8254_read(struct comedi_8254 *i8254, unsigned int reg)
+{
+ unsigned int reg_offset = (reg * i8254->iosize) << i8254->regshift;
+ unsigned int val;
+
+ switch (i8254->iosize) {
+ default:
+ case I8254_IO8:
+ if (i8254->mmio)
+ val = readb(i8254->mmio + reg_offset);
+ else
+ val = inb(i8254->iobase + reg_offset);
+ break;
+ case I8254_IO16:
+ if (i8254->mmio)
+ val = readw(i8254->mmio + reg_offset);
+ else
+ val = inw(i8254->iobase + reg_offset);
+ break;
+ case I8254_IO32:
+ if (i8254->mmio)
+ val = readl(i8254->mmio + reg_offset);
+ else
+ val = inl(i8254->iobase + reg_offset);
+ break;
+ }
+ return val & 0xff;
+}
+
+static void __i8254_write(struct comedi_8254 *i8254,
+ unsigned int val, unsigned int reg)
+{
+ unsigned int reg_offset = (reg * i8254->iosize) << i8254->regshift;
+
+ switch (i8254->iosize) {
+ default:
+ case I8254_IO8:
+ if (i8254->mmio)
+ writeb(val, i8254->mmio + reg_offset);
+ else
+ outb(val, i8254->iobase + reg_offset);
+ break;
+ case I8254_IO16:
+ if (i8254->mmio)
+ writew(val, i8254->mmio + reg_offset);
+ else
+ outw(val, i8254->iobase + reg_offset);
+ break;
+ case I8254_IO32:
+ if (i8254->mmio)
+ writel(val, i8254->mmio + reg_offset);
+ else
+ outl(val, i8254->iobase + reg_offset);
+ break;
+ }
+}
+
+/**
+ * comedi_8254_status - return the status of a counter
+ * @i8254: comedi_8254 struct for the timer
+ * @counter: the counter number
+ */
+unsigned int comedi_8254_status(struct comedi_8254 *i8254, unsigned int counter)
+{
+ unsigned int cmd;
+
+ if (counter > 2)
+ return 0;
+
+ cmd = I8254_CTRL_READBACK_STATUS | I8254_CTRL_READBACK_SEL_CTR(counter);
+ __i8254_write(i8254, cmd, I8254_CTRL_REG);
+
+ return __i8254_read(i8254, counter);
+}
+EXPORT_SYMBOL_GPL(comedi_8254_status);
+
+/**
+ * comedi_8254_read - read the current counter value
+ * @i8254: comedi_8254 struct for the timer
+ * @counter: the counter number
+ */
+unsigned int comedi_8254_read(struct comedi_8254 *i8254, unsigned int counter)
+{
+ unsigned int val;
+
+ if (counter > 2)
+ return 0;
+
+ /* latch counter */
+ __i8254_write(i8254, I8254_CTRL_SEL_CTR(counter) | I8254_CTRL_LATCH,
+ I8254_CTRL_REG);
+
+ /* read LSB then MSB */
+ val = __i8254_read(i8254, counter);
+ val |= (__i8254_read(i8254, counter) << 8);
+
+ return val;
+}
+EXPORT_SYMBOL_GPL(comedi_8254_read);
+
+/**
+ * comedi_8254_write - load a 16-bit initial counter value
+ * @i8254: comedi_8254 struct for the timer
+ * @counter: the counter number
+ * @val: the initial value
+ */
+void comedi_8254_write(struct comedi_8254 *i8254,
+ unsigned int counter, unsigned int val)
+{
+ unsigned int byte;
+
+ if (counter > 2)
+ return;
+ if (val > 0xffff)
+ return;
+
+ /* load LSB then MSB */
+ byte = val & 0xff;
+ __i8254_write(i8254, byte, counter);
+ byte = (val >> 8) & 0xff;
+ __i8254_write(i8254, byte, counter);
+}
+EXPORT_SYMBOL_GPL(comedi_8254_write);
+
+/**
+ * comedi_8254_set_mode - set the mode of a counter
+ * @i8254: comedi_8254 struct for the timer
+ * @counter: the counter number
+ * @mode: the I8254_MODEx and I8254_BCD|I8254_BINARY
+ */
+int comedi_8254_set_mode(struct comedi_8254 *i8254, unsigned int counter,
+ unsigned int mode)
+{
+ unsigned int byte;
+
+ if (counter > 2)
+ return -EINVAL;
+ if (mode > (I8254_MODE5 | I8254_BCD))
+ return -EINVAL;
+
+ byte = I8254_CTRL_SEL_CTR(counter) | /* select counter */
+ I8254_CTRL_LSB_MSB | /* load LSB then MSB */
+ mode; /* mode and BCD|binary */
+ __i8254_write(i8254, byte, I8254_CTRL_REG);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(comedi_8254_set_mode);
+
+/**
+ * comedi_8254_load - program the mode and initial count of a counter
+ * @i8254: comedi_8254 struct for the timer
+ * @counter: the counter number
+ * @mode: the I8254_MODEx and I8254_BCD|I8254_BINARY
+ * @val: the initial value
+ */
+int comedi_8254_load(struct comedi_8254 *i8254, unsigned int counter,
+ unsigned int val, unsigned int mode)
+{
+ if (counter > 2)
+ return -EINVAL;
+ if (val > 0xffff)
+ return -EINVAL;
+ if (mode > (I8254_MODE5 | I8254_BCD))
+ return -EINVAL;
+
+ comedi_8254_set_mode(i8254, counter, mode);
+ comedi_8254_write(i8254, counter, val);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(comedi_8254_load);
+
+/**
+ * comedi_8254_pacer_enable - set the mode and load the cascaded counters
+ * @i8254: comedi_8254 struct for the timer
+ * @counter1: the counter number for the first divisor
+ * @counter2: the counter number for the second divisor
+ * @enable: flag to enable (load) the counters
+ */
+void comedi_8254_pacer_enable(struct comedi_8254 *i8254,
+ unsigned int counter1,
+ unsigned int counter2,
+ bool enable)
+{
+ unsigned int mode;
+
+ if (counter1 > 2 || counter2 > 2 || counter1 == counter2)
+ return;
+
+ if (enable)
+ mode = I8254_MODE2 | I8254_BINARY;
+ else
+ mode = I8254_MODE0 | I8254_BINARY;
+
+ comedi_8254_set_mode(i8254, counter1, mode);
+ comedi_8254_set_mode(i8254, counter2, mode);
+
+ if (enable) {
+ /*
+ * Divisors are loaded second counter then first counter to
+ * avoid possible issues with the first counter expiring
+ * before the second counter is loaded.
+ */
+ comedi_8254_write(i8254, counter2, i8254->divisor2);
+ comedi_8254_write(i8254, counter1, i8254->divisor1);
+ }
+}
+EXPORT_SYMBOL_GPL(comedi_8254_pacer_enable);
+
+/**
+ * comedi_8254_update_divisors - update the divisors for the cascaded counters
+ * @i8254: comedi_8254 struct for the timer
+ */
+void comedi_8254_update_divisors(struct comedi_8254 *i8254)
+{
+ /* masking is done since counter maps zero to 0x10000 */
+ i8254->divisor = i8254->next_div & 0xffff;
+ i8254->divisor1 = i8254->next_div1 & 0xffff;
+ i8254->divisor2 = i8254->next_div2 & 0xffff;
+}
+EXPORT_SYMBOL_GPL(comedi_8254_update_divisors);
+
+/**
+ * comedi_8254_cascade_ns_to_timer - calculate the cascaded divisor values
+ * @i8254: comedi_8254 struct for the timer
+ * @nanosec: the desired ns time
+ * @flags: comedi_cmd flags
+ */
+void comedi_8254_cascade_ns_to_timer(struct comedi_8254 *i8254,
+ unsigned int *nanosec,
+ unsigned int flags)
+{
+ unsigned int d1 = i8254->next_div1 ? i8254->next_div1 : I8254_MAX_COUNT;
+ unsigned int d2 = i8254->next_div2 ? i8254->next_div2 : I8254_MAX_COUNT;
+ unsigned int div = d1 * d2;
+ unsigned int ns_lub = 0xffffffff;
+ unsigned int ns_glb = 0;
+ unsigned int d1_lub = 0;
+ unsigned int d1_glb = 0;
+ unsigned int d2_lub = 0;
+ unsigned int d2_glb = 0;
+ unsigned int start;
+ unsigned int ns;
+ unsigned int ns_low;
+ unsigned int ns_high;
+
+ /* exit early if everything is already correct */
+ if (div * i8254->osc_base == *nanosec &&
+ d1 > 1 && d1 <= I8254_MAX_COUNT &&
+ d2 > 1 && d2 <= I8254_MAX_COUNT &&
+ /* check for overflow */
+ div > d1 && div > d2 &&
+ div * i8254->osc_base > div &&
+ div * i8254->osc_base > i8254->osc_base)
+ return;
+
+ div = *nanosec / i8254->osc_base;
+ d2 = I8254_MAX_COUNT;
+ start = div / d2;
+ if (start < 2)
+ start = 2;
+ for (d1 = start; d1 <= div / d1 + 1 && d1 <= I8254_MAX_COUNT; d1++) {
+ for (d2 = div / d1;
+ d1 * d2 <= div + d1 + 1 && d2 <= I8254_MAX_COUNT; d2++) {
+ ns = i8254->osc_base * d1 * d2;
+ if (ns <= *nanosec && ns > ns_glb) {
+ ns_glb = ns;
+ d1_glb = d1;
+ d2_glb = d2;
+ }
+ if (ns >= *nanosec && ns < ns_lub) {
+ ns_lub = ns;
+ d1_lub = d1;
+ d2_lub = d2;
+ }
+ }
+ }
+
+ switch (flags & CMDF_ROUND_MASK) {
+ case CMDF_ROUND_NEAREST:
+ default:
+ ns_high = d1_lub * d2_lub * i8254->osc_base;
+ ns_low = d1_glb * d2_glb * i8254->osc_base;
+ if (ns_high - *nanosec < *nanosec - ns_low) {
+ d1 = d1_lub;
+ d2 = d2_lub;
+ } else {
+ d1 = d1_glb;
+ d2 = d2_glb;
+ }
+ break;
+ case CMDF_ROUND_UP:
+ d1 = d1_lub;
+ d2 = d2_lub;
+ break;
+ case CMDF_ROUND_DOWN:
+ d1 = d1_glb;
+ d2 = d2_glb;
+ break;
+ }
+
+ *nanosec = d1 * d2 * i8254->osc_base;
+ i8254->next_div1 = d1;
+ i8254->next_div2 = d2;
+}
+EXPORT_SYMBOL_GPL(comedi_8254_cascade_ns_to_timer);
+
+/**
+ * comedi_8254_ns_to_timer - calculate the divisor value for nanosec timing
+ * @i8254: comedi_8254 struct for the timer
+ * @nanosec: the desired ns time
+ * @flags: comedi_cmd flags
+ */
+void comedi_8254_ns_to_timer(struct comedi_8254 *i8254,
+ unsigned int *nanosec, unsigned int flags)
+{
+ unsigned int divisor;
+
+ switch (flags & CMDF_ROUND_MASK) {
+ default:
+ case CMDF_ROUND_NEAREST:
+ divisor = DIV_ROUND_CLOSEST(*nanosec, i8254->osc_base);
+ break;
+ case CMDF_ROUND_UP:
+ divisor = DIV_ROUND_UP(*nanosec, i8254->osc_base);
+ break;
+ case CMDF_ROUND_DOWN:
+ divisor = *nanosec / i8254->osc_base;
+ break;
+ }
+ if (divisor < 2)
+ divisor = 2;
+ if (divisor > I8254_MAX_COUNT)
+ divisor = I8254_MAX_COUNT;
+
+ *nanosec = divisor * i8254->osc_base;
+ i8254->next_div = divisor;
+}
+EXPORT_SYMBOL_GPL(comedi_8254_ns_to_timer);
+
+/**
+ * comedi_8254_set_busy - set/clear the "busy" flag for a given counter
+ * @i8254: comedi_8254 struct for the timer
+ * @counter: the counter number
+ * @busy: set/clear flag
+ */
+void comedi_8254_set_busy(struct comedi_8254 *i8254,
+ unsigned int counter, bool busy)
+{
+ if (counter < 3)
+ i8254->busy[counter] = busy;
+}
+EXPORT_SYMBOL_GPL(comedi_8254_set_busy);
+
+static int comedi_8254_insn_read(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ struct comedi_8254 *i8254 = s->private;
+ unsigned int chan = CR_CHAN(insn->chanspec);
+ int i;
+
+ if (i8254->busy[chan])
+ return -EBUSY;
+
+ for (i = 0; i < insn->n; i++)
+ data[i] = comedi_8254_read(i8254, chan);
+
+ return insn->n;
+}
+
+static int comedi_8254_insn_write(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ struct comedi_8254 *i8254 = s->private;
+ unsigned int chan = CR_CHAN(insn->chanspec);
+
+ if (i8254->busy[chan])
+ return -EBUSY;
+
+ if (insn->n)
+ comedi_8254_write(i8254, chan, data[insn->n - 1]);
+
+ return insn->n;
+}
+
+static int comedi_8254_insn_config(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ struct comedi_8254 *i8254 = s->private;
+ unsigned int chan = CR_CHAN(insn->chanspec);
+ int ret;
+
+ if (i8254->busy[chan])
+ return -EBUSY;
+
+ switch (data[0]) {
+ case INSN_CONFIG_RESET:
+ ret = comedi_8254_set_mode(i8254, chan,
+ I8254_MODE0 | I8254_BINARY);
+ if (ret)
+ return ret;
+ break;
+ case INSN_CONFIG_SET_COUNTER_MODE:
+ ret = comedi_8254_set_mode(i8254, chan, data[1]);
+ if (ret)
+ return ret;
+ break;
+ case INSN_CONFIG_8254_READ_STATUS:
+ data[1] = comedi_8254_status(i8254, chan);
+ break;
+ default:
+ /*
+ * If available, call the driver provided (*insn_config)
+ * to handle any driver implemented instructions.
+ */
+ if (i8254->insn_config)
+ return i8254->insn_config(dev, s, insn, data);
+
+ return -EINVAL;
+ }
+
+ return insn->n;
+}
+
+/**
+ * comedi_8254_subdevice_init - initialize a comedi_subdevice for the 8254 timer
+ * @s: comedi_subdevice struct
+ */
+void comedi_8254_subdevice_init(struct comedi_subdevice *s,
+ struct comedi_8254 *i8254)
+{
+ s->type = COMEDI_SUBD_COUNTER;
+ s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
+ s->n_chan = 3;
+ s->maxdata = 0xffff;
+ s->range_table = &range_unknown;
+ s->insn_read = comedi_8254_insn_read;
+ s->insn_write = comedi_8254_insn_write;
+ s->insn_config = comedi_8254_insn_config;
+
+ s->private = i8254;
+}
+EXPORT_SYMBOL_GPL(comedi_8254_subdevice_init);
+
+static struct comedi_8254 *__i8254_init(unsigned long iobase,
+ void __iomem *mmio,
+ unsigned int osc_base,
+ unsigned int iosize,
+ unsigned int regshift)
+{
+ struct comedi_8254 *i8254;
+ int i;
+
+ /* sanity check that the iosize is valid */
+ if (!(iosize == I8254_IO8 || iosize == I8254_IO16 ||
+ iosize == I8254_IO32))
+ return NULL;
+
+ i8254 = kzalloc(sizeof(*i8254), GFP_KERNEL);
+ if (!i8254)
+ return NULL;
+
+ i8254->iobase = iobase;
+ i8254->mmio = mmio;
+ i8254->iosize = iosize;
+ i8254->regshift = regshift;
+
+ /* default osc_base to the max speed of a generic 8254 timer */
+ i8254->osc_base = osc_base ? osc_base : I8254_OSC_BASE_10MHZ;
+
+ /* reset all the counters by setting them to I8254_MODE0 */
+ for (i = 0; i < 3; i++)
+ comedi_8254_set_mode(i8254, i, I8254_MODE0 | I8254_BINARY);
+
+ return i8254;
+}
+
+/**
+ * comedi_8254_init - allocate and initialize the 8254 device for pio access
+ * @mmio: port I/O base address
+ * @osc_base: base time of the counter in ns
+ * OPTIONAL - only used by comedi_8254_cascade_ns_to_timer()
+ * @iosize: I/O register size
+ * @regshift: register gap shift
+ */
+struct comedi_8254 *comedi_8254_init(unsigned long iobase,
+ unsigned int osc_base,
+ unsigned int iosize,
+ unsigned int regshift)
+{
+ return __i8254_init(iobase, NULL, osc_base, iosize, regshift);
+}
+EXPORT_SYMBOL_GPL(comedi_8254_init);
+
+/**
+ * comedi_8254_mm_init - allocate and initialize the 8254 device for mmio access
+ * @mmio: memory mapped I/O base address
+ * @osc_base: base time of the counter in ns
+ * OPTIONAL - only used by comedi_8254_cascade_ns_to_timer()
+ * @iosize: I/O register size
+ * @regshift: register gap shift
+ */
+struct comedi_8254 *comedi_8254_mm_init(void __iomem *mmio,
+ unsigned int osc_base,
+ unsigned int iosize,
+ unsigned int regshift)
+{
+ return __i8254_init(0, mmio, osc_base, iosize, regshift);
+}
+EXPORT_SYMBOL_GPL(comedi_8254_mm_init);
+
+static int __init comedi_8254_module_init(void)
+{
+ return 0;
+}
+module_init(comedi_8254_module_init);
+
+static void __exit comedi_8254_module_exit(void)
+{
+}
+module_exit(comedi_8254_module_exit);
+
+MODULE_AUTHOR("H Hartley Sweeten <hsweeten@visionengravers.com>");
+MODULE_DESCRIPTION("Comedi: Generic 8254 timer/counter support");
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/comedi/drivers/comedi_8254.h b/drivers/staging/comedi/drivers/comedi_8254.h
new file mode 100644
index 000000000..d89f6d94f
--- /dev/null
+++ b/drivers/staging/comedi/drivers/comedi_8254.h
@@ -0,0 +1,133 @@
+/*
+ * comedi_8254.h
+ * Generic 8254 timer/counter support
+ * Copyright (C) 2014 H Hartley Sweeten <hsweeten@visionengravers.com>
+ *
+ * COMEDI - Linux Control and Measurement Device Interface
+ * Copyright (C) 2000 David A. Schleef <ds@schleef.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _COMEDI_8254_H
+#define _COMEDI_8254_H
+
+/*
+ * Common oscillator base values in nanoseconds
+ */
+#define I8254_OSC_BASE_10MHZ 100
+#define I8254_OSC_BASE_5MHZ 200
+#define I8254_OSC_BASE_4MHZ 250
+#define I8254_OSC_BASE_2MHZ 500
+#define I8254_OSC_BASE_1MHZ 1000
+#define I8254_OSC_BASE_100KHZ 10000
+#define I8254_OSC_BASE_10KHZ 100000
+#define I8254_OSC_BASE_1KHZ 1000000
+
+/*
+ * I/O access size used to read/write registers
+ */
+#define I8254_IO8 1
+#define I8254_IO16 2
+#define I8254_IO32 4
+
+/*
+ * Register map for generic 8254 timer (I8254_IO8 with 0 regshift)
+ */
+#define I8254_COUNTER0_REG 0x00
+#define I8254_COUNTER1_REG 0x01
+#define I8254_COUNTER2_REG 0x02
+#define I8254_CTRL_REG 0x03
+#define I8254_CTRL_SEL_CTR(x) ((x) << 6)
+#define I8254_CTRL_READBACK_COUNT ((3 << 6) | (1 << 4))
+#define I8254_CTRL_READBACK_STATUS ((3 << 6) | (1 << 5))
+#define I8254_CTRL_READBACK_SEL_CTR(x) (2 << (x))
+#define I8254_CTRL_LATCH (0 << 4)
+#define I8254_CTRL_LSB_ONLY (1 << 4)
+#define I8254_CTRL_MSB_ONLY (2 << 4)
+#define I8254_CTRL_LSB_MSB (3 << 4)
+
+/* counter maps zero to 0x10000 */
+#define I8254_MAX_COUNT 0x10000
+
+/**
+ * struct comedi_8254 - private data used by this module
+ * @iobase: PIO base address of the registers (in/out)
+ * @mmio: MMIO base address of the registers (read/write)
+ * @iosize: I/O size used to access the registers (b/w/l)
+ * @regshift: register gap shift
+ * @osc_base: cascaded oscillator speed in ns
+ * @divisor: divisor for single counter
+ * @divisor1: divisor loaded into first cascaded counter
+ * @divisor2: divisor loaded into second cascaded counter
+ * #next_div: next divisor for single counter
+ * @next_div1: next divisor to use for first cascaded counter
+ * @next_div2: next divisor to use for second cascaded counter
+ * @clock_src; current clock source for each counter (driver specific)
+ * @gate_src; current gate source for each counter (driver specific)
+ * @busy: flags used to indicate that a counter is "busy"
+ * @insn_config: driver specific (*insn_config) callback
+ */
+struct comedi_8254 {
+ unsigned long iobase;
+ void __iomem *mmio;
+ unsigned int iosize;
+ unsigned int regshift;
+ unsigned int osc_base;
+ unsigned int divisor;
+ unsigned int divisor1;
+ unsigned int divisor2;
+ unsigned int next_div;
+ unsigned int next_div1;
+ unsigned int next_div2;
+ unsigned int clock_src[3];
+ unsigned int gate_src[3];
+ bool busy[3];
+
+ int (*insn_config)(struct comedi_device *, struct comedi_subdevice *s,
+ struct comedi_insn *, unsigned int *data);
+};
+
+unsigned int comedi_8254_status(struct comedi_8254 *, unsigned int counter);
+unsigned int comedi_8254_read(struct comedi_8254 *, unsigned int counter);
+void comedi_8254_write(struct comedi_8254 *,
+ unsigned int counter, unsigned int val);
+
+int comedi_8254_set_mode(struct comedi_8254 *,
+ unsigned int counter, unsigned int mode);
+int comedi_8254_load(struct comedi_8254 *,
+ unsigned int counter, unsigned int val, unsigned int mode);
+
+void comedi_8254_pacer_enable(struct comedi_8254 *,
+ unsigned int counter1, unsigned int counter2,
+ bool enable);
+void comedi_8254_update_divisors(struct comedi_8254 *);
+void comedi_8254_cascade_ns_to_timer(struct comedi_8254 *,
+ unsigned int *nanosec, unsigned int flags);
+void comedi_8254_ns_to_timer(struct comedi_8254 *,
+ unsigned int *nanosec, unsigned int flags);
+
+void comedi_8254_set_busy(struct comedi_8254 *,
+ unsigned int counter, bool busy);
+
+void comedi_8254_subdevice_init(struct comedi_subdevice *,
+ struct comedi_8254 *);
+
+struct comedi_8254 *comedi_8254_init(unsigned long iobase,
+ unsigned int osc_base,
+ unsigned int iosize,
+ unsigned int regshift);
+struct comedi_8254 *comedi_8254_mm_init(void __iomem *mmio,
+ unsigned int osc_base,
+ unsigned int iosize,
+ unsigned int regshift);
+
+#endif /* _COMEDI_8254_H */
diff --git a/drivers/staging/comedi/drivers/comedi_bond.c b/drivers/staging/comedi/drivers/comedi_bond.c
new file mode 100644
index 000000000..96db0c268
--- /dev/null
+++ b/drivers/staging/comedi/drivers/comedi_bond.c
@@ -0,0 +1,355 @@
+/*
+ * comedi_bond.c
+ * A Comedi driver to 'bond' or merge multiple drivers and devices as one.
+ *
+ * COMEDI - Linux Control and Measurement Device Interface
+ * Copyright (C) 2000 David A. Schleef <ds@schleef.org>
+ * Copyright (C) 2005 Calin A. Culianu <calin@ajvar.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+/*
+ * Driver: comedi_bond
+ * Description: A driver to 'bond' (merge) multiple subdevices from multiple
+ * devices together as one.
+ * Devices:
+ * Author: ds
+ * Updated: Mon, 10 Oct 00:18:25 -0500
+ * Status: works
+ *
+ * This driver allows you to 'bond' (merge) multiple comedi subdevices
+ * (coming from possibly difference boards and/or drivers) together. For
+ * example, if you had a board with 2 different DIO subdevices, and
+ * another with 1 DIO subdevice, you could 'bond' them with this driver
+ * so that they look like one big fat DIO subdevice. This makes writing
+ * applications slightly easier as you don't have to worry about managing
+ * different subdevices in the application -- you just worry about
+ * indexing one linear array of channel id's.
+ *
+ * Right now only DIO subdevices are supported as that's the personal itch
+ * I am scratching with this driver. If you want to add support for AI and AO
+ * subdevs, go right on ahead and do so!
+ *
+ * Commands aren't supported -- although it would be cool if they were.
+ *
+ * Configuration Options:
+ * List of comedi-minors to bond. All subdevices of the same type
+ * within each minor will be concatenated together in the order given here.
+ */
+
+#include <linux/module.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+#include "../comedi.h"
+#include "../comedilib.h"
+#include "../comedidev.h"
+
+struct bonded_device {
+ struct comedi_device *dev;
+ unsigned minor;
+ unsigned subdev;
+ unsigned nchans;
+};
+
+struct comedi_bond_private {
+ char name[256];
+ struct bonded_device **devs;
+ unsigned ndevs;
+ unsigned nchans;
+};
+
+static int bonding_dio_insn_bits(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn, unsigned int *data)
+{
+ struct comedi_bond_private *devpriv = dev->private;
+ unsigned int n_left, n_done, base_chan;
+ unsigned int write_mask, data_bits;
+ struct bonded_device **devs;
+
+ write_mask = data[0];
+ data_bits = data[1];
+ base_chan = CR_CHAN(insn->chanspec);
+ /* do a maximum of 32 channels, starting from base_chan. */
+ n_left = devpriv->nchans - base_chan;
+ if (n_left > 32)
+ n_left = 32;
+
+ n_done = 0;
+ devs = devpriv->devs;
+ do {
+ struct bonded_device *bdev = *devs++;
+
+ if (base_chan < bdev->nchans) {
+ /* base channel falls within bonded device */
+ unsigned int b_chans, b_mask, b_write_mask, b_data_bits;
+ int ret;
+
+ /*
+ * Get num channels to do for bonded device and set
+ * up mask and data bits for bonded device.
+ */
+ b_chans = bdev->nchans - base_chan;
+ if (b_chans > n_left)
+ b_chans = n_left;
+ b_mask = (1U << b_chans) - 1;
+ b_write_mask = (write_mask >> n_done) & b_mask;
+ b_data_bits = (data_bits >> n_done) & b_mask;
+ /* Read/Write the new digital lines. */
+ ret = comedi_dio_bitfield2(bdev->dev, bdev->subdev,
+ b_write_mask, &b_data_bits,
+ base_chan);
+ if (ret < 0)
+ return ret;
+ /* Place read bits into data[1]. */
+ data[1] &= ~(b_mask << n_done);
+ data[1] |= (b_data_bits & b_mask) << n_done;
+ /*
+ * Set up for following bonded device (if still have
+ * channels to read/write).
+ */
+ base_chan = 0;
+ n_done += b_chans;
+ n_left -= b_chans;
+ } else {
+ /* Skip bonded devices before base channel. */
+ base_chan -= bdev->nchans;
+ }
+ } while (n_left);
+
+ return insn->n;
+}
+
+static int bonding_dio_insn_config(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn, unsigned int *data)
+{
+ struct comedi_bond_private *devpriv = dev->private;
+ unsigned int chan = CR_CHAN(insn->chanspec);
+ int ret;
+ struct bonded_device *bdev;
+ struct bonded_device **devs;
+
+ /*
+ * Locate bonded subdevice and adjust channel.
+ */
+ devs = devpriv->devs;
+ for (bdev = *devs++; chan >= bdev->nchans; bdev = *devs++)
+ chan -= bdev->nchans;
+
+ /*
+ * The input or output configuration of each digital line is
+ * configured by a special insn_config instruction. chanspec
+ * contains the channel to be changed, and data[0] contains the
+ * configuration instruction INSN_CONFIG_DIO_OUTPUT,
+ * INSN_CONFIG_DIO_INPUT or INSN_CONFIG_DIO_QUERY.
+ *
+ * Note that INSN_CONFIG_DIO_OUTPUT == COMEDI_OUTPUT,
+ * and INSN_CONFIG_DIO_INPUT == COMEDI_INPUT. This is deliberate ;)
+ */
+ switch (data[0]) {
+ case INSN_CONFIG_DIO_OUTPUT:
+ case INSN_CONFIG_DIO_INPUT:
+ ret = comedi_dio_config(bdev->dev, bdev->subdev, chan, data[0]);
+ break;
+ case INSN_CONFIG_DIO_QUERY:
+ ret = comedi_dio_get_config(bdev->dev, bdev->subdev, chan,
+ &data[1]);
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+ if (ret >= 0)
+ ret = insn->n;
+ return ret;
+}
+
+static int do_dev_config(struct comedi_device *dev, struct comedi_devconfig *it)
+{
+ struct comedi_bond_private *devpriv = dev->private;
+ DECLARE_BITMAP(devs_opened, COMEDI_NUM_BOARD_MINORS);
+ int i;
+
+ memset(&devs_opened, 0, sizeof(devs_opened));
+ devpriv->name[0] = 0;
+ /*
+ * Loop through all comedi devices specified on the command-line,
+ * building our device list.
+ */
+ for (i = 0; i < COMEDI_NDEVCONFOPTS && (!i || it->options[i]); ++i) {
+ char file[sizeof("/dev/comediXXXXXX")];
+ int minor = it->options[i];
+ struct comedi_device *d;
+ int sdev = -1, nchans;
+ struct bonded_device *bdev;
+ struct bonded_device **devs;
+
+ if (minor < 0 || minor >= COMEDI_NUM_BOARD_MINORS) {
+ dev_err(dev->class_dev,
+ "Minor %d is invalid!\n", minor);
+ return -EINVAL;
+ }
+ if (minor == dev->minor) {
+ dev_err(dev->class_dev,
+ "Cannot bond this driver to itself!\n");
+ return -EINVAL;
+ }
+ if (test_and_set_bit(minor, devs_opened)) {
+ dev_err(dev->class_dev,
+ "Minor %d specified more than once!\n", minor);
+ return -EINVAL;
+ }
+
+ snprintf(file, sizeof(file), "/dev/comedi%d", minor);
+ file[sizeof(file) - 1] = 0;
+
+ d = comedi_open(file);
+
+ if (!d) {
+ dev_err(dev->class_dev,
+ "Minor %u could not be opened\n", minor);
+ return -ENODEV;
+ }
+
+ /* Do DIO, as that's all we support now.. */
+ while ((sdev = comedi_find_subdevice_by_type(d, COMEDI_SUBD_DIO,
+ sdev + 1)) > -1) {
+ nchans = comedi_get_n_channels(d, sdev);
+ if (nchans <= 0) {
+ dev_err(dev->class_dev,
+ "comedi_get_n_channels() returned %d on minor %u subdev %d!\n",
+ nchans, minor, sdev);
+ return -EINVAL;
+ }
+ bdev = kmalloc(sizeof(*bdev), GFP_KERNEL);
+ if (!bdev)
+ return -ENOMEM;
+
+ bdev->dev = d;
+ bdev->minor = minor;
+ bdev->subdev = sdev;
+ bdev->nchans = nchans;
+ devpriv->nchans += nchans;
+
+ /*
+ * Now put bdev pointer at end of devpriv->devs array
+ * list..
+ */
+
+ /* ergh.. ugly.. we need to realloc :( */
+ devs = krealloc(devpriv->devs,
+ (devpriv->ndevs + 1) * sizeof(*devs),
+ GFP_KERNEL);
+ if (!devs) {
+ dev_err(dev->class_dev,
+ "Could not allocate memory. Out of memory?\n");
+ kfree(bdev);
+ return -ENOMEM;
+ }
+ devpriv->devs = devs;
+ devpriv->devs[devpriv->ndevs++] = bdev;
+ {
+ /* Append dev:subdev to devpriv->name */
+ char buf[20];
+
+ snprintf(buf, sizeof(buf), "%u:%u ",
+ bdev->minor, bdev->subdev);
+ strlcat(devpriv->name, buf,
+ sizeof(devpriv->name));
+ }
+ }
+ }
+
+ if (!devpriv->nchans) {
+ dev_err(dev->class_dev, "No channels found!\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int bonding_attach(struct comedi_device *dev,
+ struct comedi_devconfig *it)
+{
+ struct comedi_bond_private *devpriv;
+ struct comedi_subdevice *s;
+ int ret;
+
+ devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
+ if (!devpriv)
+ return -ENOMEM;
+
+ /*
+ * Setup our bonding from config params.. sets up our private struct..
+ */
+ ret = do_dev_config(dev, it);
+ if (ret)
+ return ret;
+
+ dev->board_name = devpriv->name;
+
+ ret = comedi_alloc_subdevices(dev, 1);
+ if (ret)
+ return ret;
+
+ s = &dev->subdevices[0];
+ s->type = COMEDI_SUBD_DIO;
+ s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
+ s->n_chan = devpriv->nchans;
+ s->maxdata = 1;
+ s->range_table = &range_digital;
+ s->insn_bits = bonding_dio_insn_bits;
+ s->insn_config = bonding_dio_insn_config;
+
+ dev_info(dev->class_dev,
+ "%s: %s attached, %u channels from %u devices\n",
+ dev->driver->driver_name, dev->board_name,
+ devpriv->nchans, devpriv->ndevs);
+
+ return 0;
+}
+
+static void bonding_detach(struct comedi_device *dev)
+{
+ struct comedi_bond_private *devpriv = dev->private;
+
+ if (devpriv && devpriv->devs) {
+ DECLARE_BITMAP(devs_closed, COMEDI_NUM_BOARD_MINORS);
+
+ memset(&devs_closed, 0, sizeof(devs_closed));
+ while (devpriv->ndevs--) {
+ struct bonded_device *bdev;
+
+ bdev = devpriv->devs[devpriv->ndevs];
+ if (!bdev)
+ continue;
+ if (!test_and_set_bit(bdev->minor, devs_closed))
+ comedi_close(bdev->dev);
+ kfree(bdev);
+ }
+ kfree(devpriv->devs);
+ devpriv->devs = NULL;
+ }
+}
+
+static struct comedi_driver bonding_driver = {
+ .driver_name = "comedi_bond",
+ .module = THIS_MODULE,
+ .attach = bonding_attach,
+ .detach = bonding_detach,
+};
+module_comedi_driver(bonding_driver);
+
+MODULE_AUTHOR("Calin A. Culianu");
+MODULE_DESCRIPTION("comedi_bond: A driver for COMEDI to bond multiple COMEDI devices together as one.");
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/comedi/drivers/comedi_isadma.c b/drivers/staging/comedi/drivers/comedi_isadma.c
new file mode 100644
index 000000000..6ba71d114
--- /dev/null
+++ b/drivers/staging/comedi/drivers/comedi_isadma.c
@@ -0,0 +1,264 @@
+/*
+ * COMEDI ISA DMA support functions
+ * Copyright (c) 2014 H Hartley Sweeten <hsweeten@visionengravers.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/dma-mapping.h>
+#include <asm/dma.h>
+
+#include "../comedidev.h"
+
+#include "comedi_isadma.h"
+
+/**
+ * comedi_isadma_program - program and enable an ISA DMA transfer
+ * @desc: the ISA DMA cookie to program and enable
+ */
+void comedi_isadma_program(struct comedi_isadma_desc *desc)
+{
+ unsigned long flags;
+
+ flags = claim_dma_lock();
+ clear_dma_ff(desc->chan);
+ set_dma_mode(desc->chan, desc->mode);
+ set_dma_addr(desc->chan, desc->hw_addr);
+ set_dma_count(desc->chan, desc->size);
+ enable_dma(desc->chan);
+ release_dma_lock(flags);
+}
+EXPORT_SYMBOL_GPL(comedi_isadma_program);
+
+/**
+ * comedi_isadma_disable - disable the ISA DMA channel
+ * @dma_chan: the DMA channel to disable
+ *
+ * Returns the residue (remaining bytes) left in the DMA transfer.
+ */
+unsigned int comedi_isadma_disable(unsigned int dma_chan)
+{
+ unsigned long flags;
+ unsigned int residue;
+
+ flags = claim_dma_lock();
+ disable_dma(dma_chan);
+ residue = get_dma_residue(dma_chan);
+ release_dma_lock(flags);
+
+ return residue;
+}
+EXPORT_SYMBOL_GPL(comedi_isadma_disable);
+
+/**
+ * comedi_isadma_disable_on_sample - disable the ISA DMA channel
+ * @dma_chan: the DMA channel to disable
+ * @size: the sample size (in bytes)
+ *
+ * Returns the residue (remaining bytes) left in the DMA transfer.
+ */
+unsigned int comedi_isadma_disable_on_sample(unsigned int dma_chan,
+ unsigned int size)
+{
+ int stalled = 0;
+ unsigned long flags;
+ unsigned int residue;
+ unsigned int new_residue;
+
+ residue = comedi_isadma_disable(dma_chan);
+ while (residue % size) {
+ /* residue is a partial sample, enable DMA to allow more data */
+ flags = claim_dma_lock();
+ enable_dma(dma_chan);
+ release_dma_lock(flags);
+
+ udelay(2);
+ new_residue = comedi_isadma_disable(dma_chan);
+
+ /* is DMA stalled? */
+ if (new_residue == residue) {
+ stalled++;
+ if (stalled > 10)
+ break;
+ } else {
+ residue = new_residue;
+ stalled = 0;
+ }
+ }
+ return residue;
+}
+EXPORT_SYMBOL_GPL(comedi_isadma_disable_on_sample);
+
+/**
+ * comedi_isadma_poll - poll the current DMA transfer
+ * @dma: the ISA DMA to poll
+ *
+ * Returns the position (in bytes) of the current DMA transfer.
+ */
+unsigned int comedi_isadma_poll(struct comedi_isadma *dma)
+{
+ struct comedi_isadma_desc *desc = &dma->desc[dma->cur_dma];
+ unsigned long flags;
+ unsigned int result;
+ unsigned int result1;
+
+ flags = claim_dma_lock();
+ clear_dma_ff(desc->chan);
+ if (!isa_dma_bridge_buggy)
+ disable_dma(desc->chan);
+ result = get_dma_residue(desc->chan);
+ /*
+ * Read the counter again and choose higher value in order to
+ * avoid reading during counter lower byte roll over if the
+ * isa_dma_bridge_buggy is set.
+ */
+ result1 = get_dma_residue(desc->chan);
+ if (!isa_dma_bridge_buggy)
+ enable_dma(desc->chan);
+ release_dma_lock(flags);
+
+ if (result < result1)
+ result = result1;
+ if (result >= desc->size || result == 0)
+ return 0;
+ else
+ return desc->size - result;
+}
+EXPORT_SYMBOL_GPL(comedi_isadma_poll);
+
+/**
+ * comedi_isadma_set_mode - set the ISA DMA transfer direction
+ * @desc: the ISA DMA cookie to set
+ * @dma_dir: the DMA direction
+ */
+void comedi_isadma_set_mode(struct comedi_isadma_desc *desc, char dma_dir)
+{
+ desc->mode = (dma_dir == COMEDI_ISADMA_READ) ? DMA_MODE_READ
+ : DMA_MODE_WRITE;
+}
+EXPORT_SYMBOL_GPL(comedi_isadma_set_mode);
+
+/**
+ * comedi_isadma_alloc - allocate and initialize the ISA DMA
+ * @dev: comedi_device struct
+ * @n_desc: the number of cookies to allocate
+ * @dma_chan: DMA channel for the first cookie
+ * @dma_chan2: DMA channel for the second cookie
+ * @maxsize: the size of the buffer to allocate for each cookie
+ * @dma_dir: the DMA direction
+ *
+ * Returns the allocated and initialized ISA DMA or NULL if anything fails.
+ */
+struct comedi_isadma *comedi_isadma_alloc(struct comedi_device *dev,
+ int n_desc, unsigned int dma_chan1,
+ unsigned int dma_chan2,
+ unsigned int maxsize, char dma_dir)
+{
+ struct comedi_isadma *dma = NULL;
+ struct comedi_isadma_desc *desc;
+ unsigned int dma_chans[2];
+ int i;
+
+ if (n_desc < 1 || n_desc > 2)
+ goto no_dma;
+
+ dma = kzalloc(sizeof(*dma), GFP_KERNEL);
+ if (!dma)
+ goto no_dma;
+
+ desc = kcalloc(n_desc, sizeof(*desc), GFP_KERNEL);
+ if (!desc)
+ goto no_dma;
+ dma->desc = desc;
+ dma->n_desc = n_desc;
+
+ dma_chans[0] = dma_chan1;
+ if (dma_chan2 == 0 || dma_chan2 == dma_chan1)
+ dma_chans[1] = dma_chan1;
+ else
+ dma_chans[1] = dma_chan2;
+
+ if (request_dma(dma_chans[0], dev->board_name))
+ goto no_dma;
+ dma->chan = dma_chans[0];
+ if (dma_chans[1] != dma_chans[0]) {
+ if (request_dma(dma_chans[1], dev->board_name))
+ goto no_dma;
+ }
+ dma->chan2 = dma_chans[1];
+
+ for (i = 0; i < n_desc; i++) {
+ desc = &dma->desc[i];
+ desc->chan = dma_chans[i];
+ desc->maxsize = maxsize;
+ desc->virt_addr = dma_alloc_coherent(NULL, desc->maxsize,
+ &desc->hw_addr,
+ GFP_KERNEL);
+ if (!desc->virt_addr)
+ goto no_dma;
+ comedi_isadma_set_mode(desc, dma_dir);
+ }
+
+ return dma;
+
+no_dma:
+ comedi_isadma_free(dma);
+ return NULL;
+}
+EXPORT_SYMBOL_GPL(comedi_isadma_alloc);
+
+/**
+ * comedi_isadma_free - free the ISA DMA
+ * @dma: the ISA DMA to free
+ */
+void comedi_isadma_free(struct comedi_isadma *dma)
+{
+ struct comedi_isadma_desc *desc;
+ int i;
+
+ if (!dma)
+ return;
+
+ if (dma->desc) {
+ for (i = 0; i < dma->n_desc; i++) {
+ desc = &dma->desc[i];
+ if (desc->virt_addr)
+ dma_free_coherent(NULL, desc->maxsize,
+ desc->virt_addr,
+ desc->hw_addr);
+ }
+ kfree(dma->desc);
+ }
+ if (dma->chan2 && dma->chan2 != dma->chan)
+ free_dma(dma->chan2);
+ if (dma->chan)
+ free_dma(dma->chan);
+ kfree(dma);
+}
+EXPORT_SYMBOL_GPL(comedi_isadma_free);
+
+static int __init comedi_isadma_init(void)
+{
+ return 0;
+}
+module_init(comedi_isadma_init);
+
+static void __exit comedi_isadma_exit(void)
+{
+}
+module_exit(comedi_isadma_exit);
+
+MODULE_AUTHOR("H Hartley Sweeten <hsweeten@visionengravers.com>");
+MODULE_DESCRIPTION("Comedi ISA DMA support");
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/comedi/drivers/comedi_isadma.h b/drivers/staging/comedi/drivers/comedi_isadma.h
new file mode 100644
index 000000000..c7c524faf
--- /dev/null
+++ b/drivers/staging/comedi/drivers/comedi_isadma.h
@@ -0,0 +1,116 @@
+/*
+ * COMEDI ISA DMA support functions
+ * Copyright (c) 2014 H Hartley Sweeten <hsweeten@visionengravers.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _COMEDI_ISADMA_H
+#define _COMEDI_ISADMA_H
+
+/*
+ * These are used to avoid issues when <asm/dma.h> and the DMA_MODE_
+ * defines are not available.
+ */
+#define COMEDI_ISADMA_READ 0
+#define COMEDI_ISADMA_WRITE 1
+
+/**
+ * struct comedi_isadma_desc - cookie for ISA DMA
+ * @virt_addr: virtual address of buffer
+ * @hw_addr: hardware (bus) address of buffer
+ * @chan: DMA channel
+ * @maxsize: allocated size of buffer (in bytes)
+ * @size: transfer size (in bytes)
+ * @mode: DMA_MODE_READ or DMA_MODE_WRITE
+ */
+struct comedi_isadma_desc {
+ void *virt_addr;
+ dma_addr_t hw_addr;
+ unsigned int chan;
+ unsigned int maxsize;
+ unsigned int size;
+ char mode;
+};
+
+/**
+ * struct comedi_isadma - ISA DMA data
+ * @desc: cookie for each DMA buffer
+ * @n_desc: the number of cookies
+ * @cur_dma: the current cookie in use
+ * @chan: the first DMA channel requested
+ * @chan2: the second DMA channel requested
+ */
+struct comedi_isadma {
+ struct comedi_isadma_desc *desc;
+ int n_desc;
+ int cur_dma;
+ unsigned int chan;
+ unsigned int chan2;
+};
+
+#if IS_ENABLED(CONFIG_ISA_DMA_API)
+
+void comedi_isadma_program(struct comedi_isadma_desc *);
+unsigned int comedi_isadma_disable(unsigned int dma_chan);
+unsigned int comedi_isadma_disable_on_sample(unsigned int dma_chan,
+ unsigned int size);
+unsigned int comedi_isadma_poll(struct comedi_isadma *);
+void comedi_isadma_set_mode(struct comedi_isadma_desc *, char dma_dir);
+
+struct comedi_isadma *comedi_isadma_alloc(struct comedi_device *,
+ int n_desc, unsigned int dma_chan1,
+ unsigned int dma_chan2,
+ unsigned int maxsize, char dma_dir);
+void comedi_isadma_free(struct comedi_isadma *);
+
+#else /* !IS_ENABLED(CONFIG_ISA_DMA_API) */
+
+static inline void comedi_isadma_program(struct comedi_isadma_desc *desc)
+{
+}
+
+static inline unsigned int comedi_isadma_disable(unsigned int dma_chan)
+{
+ return 0;
+}
+
+static inline unsigned int
+comedi_isadma_disable_on_sample(unsigned int dma_chan, unsigned int size)
+{
+ return 0;
+}
+
+static inline unsigned int comedi_isadma_poll(struct comedi_isadma *dma)
+{
+ return 0;
+}
+
+static inline void comedi_isadma_set_mode(struct comedi_isadma_desc *desc,
+ char dma_dir)
+{
+}
+
+static inline struct comedi_isadma *
+comedi_isadma_alloc(struct comedi_device *dev, int n_desc,
+ unsigned int dma_chan1, unsigned int dma_chan2,
+ unsigned int maxsize, char dma_dir)
+{
+ return NULL;
+}
+
+static inline void comedi_isadma_free(struct comedi_isadma *dma)
+{
+}
+
+#endif /* !IS_ENABLED(CONFIG_ISA_DMA_API) */
+
+#endif /* #ifndef _COMEDI_ISADMA_H */
diff --git a/drivers/staging/comedi/drivers/comedi_parport.c b/drivers/staging/comedi/drivers/comedi_parport.c
new file mode 100644
index 000000000..15a4093ef
--- /dev/null
+++ b/drivers/staging/comedi/drivers/comedi_parport.c
@@ -0,0 +1,314 @@
+/*
+ * comedi_parport.c
+ * Comedi driver for standard parallel port
+ *
+ * For more information see:
+ * http://retired.beyondlogic.org/spp/parallel.htm
+ *
+ * COMEDI - Linux Control and Measurement Device Interface
+ * Copyright (C) 1998,2001 David A. Schleef <ds@schleef.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+/*
+ * Driver: comedi_parport
+ * Description: Standard PC parallel port
+ * Author: ds
+ * Status: works in immediate mode
+ * Devices: [standard] parallel port (comedi_parport)
+ * Updated: Tue, 30 Apr 2002 21:11:45 -0700
+ *
+ * A cheap and easy way to get a few more digital I/O lines. Steal
+ * additional parallel ports from old computers or your neighbors'
+ * computers.
+ *
+ * Option list:
+ * 0: I/O port base for the parallel port.
+ * 1: IRQ (optional)
+ *
+ * Parallel Port Lines:
+ *
+ * pin subdev chan type name
+ * ----- ------ ---- ---- --------------
+ * 1 2 0 DO strobe
+ * 2 0 0 DIO data 0
+ * 3 0 1 DIO data 1
+ * 4 0 2 DIO data 2
+ * 5 0 3 DIO data 3
+ * 6 0 4 DIO data 4
+ * 7 0 5 DIO data 5
+ * 8 0 6 DIO data 6
+ * 9 0 7 DIO data 7
+ * 10 1 3 DI ack
+ * 11 1 4 DI busy
+ * 12 1 2 DI paper out
+ * 13 1 1 DI select in
+ * 14 2 1 DO auto LF
+ * 15 1 0 DI error
+ * 16 2 2 DO init
+ * 17 2 3 DO select printer
+ * 18-25 ground
+ *
+ * When an IRQ is configured subdevice 3 pretends to be a digital
+ * input subdevice, but it always returns 0 when read. However, if
+ * you run a command with scan_begin_src=TRIG_EXT, it uses pin 10
+ * as a external trigger, which can be used to wake up tasks.
+ */
+
+#include <linux/module.h>
+#include <linux/interrupt.h>
+
+#include "../comedidev.h"
+
+/*
+ * Register map
+ */
+#define PARPORT_DATA_REG 0x00
+#define PARPORT_STATUS_REG 0x01
+#define PARPORT_CTRL_REG 0x02
+#define PARPORT_CTRL_IRQ_ENA (1 << 4)
+#define PARPORT_CTRL_BIDIR_ENA (1 << 5)
+
+static int parport_data_reg_insn_bits(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ if (comedi_dio_update_state(s, data))
+ outb(s->state, dev->iobase + PARPORT_DATA_REG);
+
+ data[1] = inb(dev->iobase + PARPORT_DATA_REG);
+
+ return insn->n;
+}
+
+static int parport_data_reg_insn_config(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ unsigned int ctrl;
+ int ret;
+
+ ret = comedi_dio_insn_config(dev, s, insn, data, 0xff);
+ if (ret)
+ return ret;
+
+ ctrl = inb(dev->iobase + PARPORT_CTRL_REG);
+ if (s->io_bits)
+ ctrl &= ~PARPORT_CTRL_BIDIR_ENA;
+ else
+ ctrl |= PARPORT_CTRL_BIDIR_ENA;
+ outb(ctrl, dev->iobase + PARPORT_CTRL_REG);
+
+ return insn->n;
+}
+
+static int parport_status_reg_insn_bits(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ data[1] = inb(dev->iobase + PARPORT_STATUS_REG) >> 3;
+
+ return insn->n;
+}
+
+static int parport_ctrl_reg_insn_bits(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ unsigned int ctrl;
+
+ if (comedi_dio_update_state(s, data)) {
+ ctrl = inb(dev->iobase + PARPORT_CTRL_REG);
+ ctrl &= (PARPORT_CTRL_IRQ_ENA | PARPORT_CTRL_BIDIR_ENA);
+ ctrl |= s->state;
+ outb(ctrl, dev->iobase + PARPORT_CTRL_REG);
+ }
+
+ data[1] = s->state;
+
+ return insn->n;
+}
+
+static int parport_intr_insn_bits(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ data[1] = 0;
+ return insn->n;
+}
+
+static int parport_intr_cmdtest(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_cmd *cmd)
+{
+ int err = 0;
+
+ /* Step 1 : check if triggers are trivially valid */
+
+ err |= comedi_check_trigger_src(&cmd->start_src, TRIG_NOW);
+ err |= comedi_check_trigger_src(&cmd->scan_begin_src, TRIG_EXT);
+ err |= comedi_check_trigger_src(&cmd->convert_src, TRIG_FOLLOW);
+ err |= comedi_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
+ err |= comedi_check_trigger_src(&cmd->stop_src, TRIG_NONE);
+
+ if (err)
+ return 1;
+
+ /* Step 2a : make sure trigger sources are unique */
+ /* Step 2b : and mutually compatible */
+
+ /* Step 3: check if arguments are trivially valid */
+
+ err |= comedi_check_trigger_arg_is(&cmd->start_arg, 0);
+ err |= comedi_check_trigger_arg_is(&cmd->scan_begin_arg, 0);
+ err |= comedi_check_trigger_arg_is(&cmd->convert_arg, 0);
+ err |= comedi_check_trigger_arg_is(&cmd->scan_end_arg,
+ cmd->chanlist_len);
+ err |= comedi_check_trigger_arg_is(&cmd->stop_arg, 0);
+
+ if (err)
+ return 3;
+
+ /* Step 4: fix up any arguments */
+
+ /* Step 5: check channel list if it exists */
+
+ return 0;
+}
+
+static int parport_intr_cmd(struct comedi_device *dev,
+ struct comedi_subdevice *s)
+{
+ unsigned int ctrl;
+
+ ctrl = inb(dev->iobase + PARPORT_CTRL_REG);
+ ctrl |= PARPORT_CTRL_IRQ_ENA;
+ outb(ctrl, dev->iobase + PARPORT_CTRL_REG);
+
+ return 0;
+}
+
+static int parport_intr_cancel(struct comedi_device *dev,
+ struct comedi_subdevice *s)
+{
+ unsigned int ctrl;
+
+ ctrl = inb(dev->iobase + PARPORT_CTRL_REG);
+ ctrl &= ~PARPORT_CTRL_IRQ_ENA;
+ outb(ctrl, dev->iobase + PARPORT_CTRL_REG);
+
+ return 0;
+}
+
+static irqreturn_t parport_interrupt(int irq, void *d)
+{
+ struct comedi_device *dev = d;
+ struct comedi_subdevice *s = dev->read_subdev;
+ unsigned int ctrl;
+
+ ctrl = inb(dev->iobase + PARPORT_CTRL_REG);
+ if (!(ctrl & PARPORT_CTRL_IRQ_ENA))
+ return IRQ_NONE;
+
+ comedi_buf_write_samples(s, &s->state, 1);
+ comedi_handle_events(dev, s);
+
+ return IRQ_HANDLED;
+}
+
+static int parport_attach(struct comedi_device *dev,
+ struct comedi_devconfig *it)
+{
+ struct comedi_subdevice *s;
+ int ret;
+
+ ret = comedi_request_region(dev, it->options[0], 0x03);
+ if (ret)
+ return ret;
+
+ if (it->options[1]) {
+ ret = request_irq(it->options[1], parport_interrupt, 0,
+ dev->board_name, dev);
+ if (ret == 0)
+ dev->irq = it->options[1];
+ }
+
+ ret = comedi_alloc_subdevices(dev, dev->irq ? 4 : 3);
+ if (ret)
+ return ret;
+
+ /* Digial I/O subdevice - Parallel port DATA register */
+ s = &dev->subdevices[0];
+ s->type = COMEDI_SUBD_DIO;
+ s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
+ s->n_chan = 8;
+ s->maxdata = 1;
+ s->range_table = &range_digital;
+ s->insn_bits = parport_data_reg_insn_bits;
+ s->insn_config = parport_data_reg_insn_config;
+
+ /* Digial Input subdevice - Parallel port STATUS register */
+ s = &dev->subdevices[1];
+ s->type = COMEDI_SUBD_DI;
+ s->subdev_flags = SDF_READABLE;
+ s->n_chan = 5;
+ s->maxdata = 1;
+ s->range_table = &range_digital;
+ s->insn_bits = parport_status_reg_insn_bits;
+
+ /* Digial Output subdevice - Parallel port CONTROL register */
+ s = &dev->subdevices[2];
+ s->type = COMEDI_SUBD_DO;
+ s->subdev_flags = SDF_WRITABLE;
+ s->n_chan = 4;
+ s->maxdata = 1;
+ s->range_table = &range_digital;
+ s->insn_bits = parport_ctrl_reg_insn_bits;
+
+ if (dev->irq) {
+ /* Digial Input subdevice - Interrupt support */
+ s = &dev->subdevices[3];
+ dev->read_subdev = s;
+ s->type = COMEDI_SUBD_DI;
+ s->subdev_flags = SDF_READABLE | SDF_CMD_READ;
+ s->n_chan = 1;
+ s->maxdata = 1;
+ s->range_table = &range_digital;
+ s->insn_bits = parport_intr_insn_bits;
+ s->len_chanlist = 1;
+ s->do_cmdtest = parport_intr_cmdtest;
+ s->do_cmd = parport_intr_cmd;
+ s->cancel = parport_intr_cancel;
+ }
+
+ outb(0, dev->iobase + PARPORT_DATA_REG);
+ outb(0, dev->iobase + PARPORT_CTRL_REG);
+
+ return 0;
+}
+
+static struct comedi_driver parport_driver = {
+ .driver_name = "comedi_parport",
+ .module = THIS_MODULE,
+ .attach = parport_attach,
+ .detach = comedi_legacy_detach,
+};
+module_comedi_driver(parport_driver);
+
+MODULE_AUTHOR("Comedi http://www.comedi.org");
+MODULE_DESCRIPTION("Comedi: Standard parallel port driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/comedi/drivers/comedi_test.c b/drivers/staging/comedi/drivers/comedi_test.c
new file mode 100644
index 000000000..80d613c0f
--- /dev/null
+++ b/drivers/staging/comedi/drivers/comedi_test.c
@@ -0,0 +1,456 @@
+/*
+ comedi/drivers/comedi_test.c
+
+ Generates fake waveform signals that can be read through
+ the command interface. It does _not_ read from any board;
+ it just generates deterministic waveforms.
+ Useful for various testing purposes.
+
+ Copyright (C) 2002 Joachim Wuttke <Joachim.Wuttke@icn.siemens.de>
+ Copyright (C) 2002 Frank Mori Hess <fmhess@users.sourceforge.net>
+
+ COMEDI - Linux Control and Measurement Device Interface
+ Copyright (C) 2000 David A. Schleef <ds@schleef.org>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+*/
+/*
+Driver: comedi_test
+Description: generates fake waveforms
+Author: Joachim Wuttke <Joachim.Wuttke@icn.siemens.de>, Frank Mori Hess
+ <fmhess@users.sourceforge.net>, ds
+Devices:
+Status: works
+Updated: Sat, 16 Mar 2002 17:34:48 -0800
+
+This driver is mainly for testing purposes, but can also be used to
+generate sample waveforms on systems that don't have data acquisition
+hardware.
+
+Configuration options:
+ [0] - Amplitude in microvolts for fake waveforms (default 1 volt)
+ [1] - Period in microseconds for fake waveforms (default 0.1 sec)
+
+Generates a sawtooth wave on channel 0, square wave on channel 1, additional
+waveforms could be added to other channels (currently they return flatline
+zero volts).
+
+*/
+
+#include <linux/module.h>
+#include "../comedidev.h"
+
+#include <asm/div64.h>
+
+#include <linux/timer.h>
+#include <linux/ktime.h>
+
+#define N_CHANS 8
+
+enum waveform_state_bits {
+ WAVEFORM_AI_RUNNING = 0
+};
+
+/* Data unique to this driver */
+struct waveform_private {
+ struct timer_list timer;
+ ktime_t last; /* time last timer interrupt occurred */
+ unsigned int uvolt_amplitude; /* waveform amplitude in microvolts */
+ unsigned long usec_period; /* waveform period in microseconds */
+ unsigned long usec_current; /* current time (mod waveform period) */
+ unsigned long usec_remainder; /* usec since last scan */
+ unsigned long state_bits;
+ unsigned int scan_period; /* scan period in usec */
+ unsigned int convert_period; /* conversion period in usec */
+ unsigned int ao_loopbacks[N_CHANS];
+};
+
+/* 1000 nanosec in a microsec */
+static const int nano_per_micro = 1000;
+
+/* fake analog input ranges */
+static const struct comedi_lrange waveform_ai_ranges = {
+ 2, {
+ BIP_RANGE(10),
+ BIP_RANGE(5)
+ }
+};
+
+static unsigned short fake_sawtooth(struct comedi_device *dev,
+ unsigned int range_index,
+ unsigned long current_time)
+{
+ struct waveform_private *devpriv = dev->private;
+ struct comedi_subdevice *s = dev->read_subdev;
+ unsigned int offset = s->maxdata / 2;
+ u64 value;
+ const struct comedi_krange *krange =
+ &s->range_table->range[range_index];
+ u64 binary_amplitude;
+
+ binary_amplitude = s->maxdata;
+ binary_amplitude *= devpriv->uvolt_amplitude;
+ do_div(binary_amplitude, krange->max - krange->min);
+
+ current_time %= devpriv->usec_period;
+ value = current_time;
+ value *= binary_amplitude * 2;
+ do_div(value, devpriv->usec_period);
+ value -= binary_amplitude; /* get rid of sawtooth's dc offset */
+
+ return offset + value;
+}
+
+static unsigned short fake_squarewave(struct comedi_device *dev,
+ unsigned int range_index,
+ unsigned long current_time)
+{
+ struct waveform_private *devpriv = dev->private;
+ struct comedi_subdevice *s = dev->read_subdev;
+ unsigned int offset = s->maxdata / 2;
+ u64 value;
+ const struct comedi_krange *krange =
+ &s->range_table->range[range_index];
+ current_time %= devpriv->usec_period;
+
+ value = s->maxdata;
+ value *= devpriv->uvolt_amplitude;
+ do_div(value, krange->max - krange->min);
+
+ if (current_time < devpriv->usec_period / 2)
+ value *= -1;
+
+ return offset + value;
+}
+
+static unsigned short fake_flatline(struct comedi_device *dev,
+ unsigned int range_index,
+ unsigned long current_time)
+{
+ return dev->read_subdev->maxdata / 2;
+}
+
+/* generates a different waveform depending on what channel is read */
+static unsigned short fake_waveform(struct comedi_device *dev,
+ unsigned int channel, unsigned int range,
+ unsigned long current_time)
+{
+ enum {
+ SAWTOOTH_CHAN,
+ SQUARE_CHAN,
+ };
+ switch (channel) {
+ case SAWTOOTH_CHAN:
+ return fake_sawtooth(dev, range, current_time);
+ case SQUARE_CHAN:
+ return fake_squarewave(dev, range, current_time);
+ default:
+ break;
+ }
+
+ return fake_flatline(dev, range, current_time);
+}
+
+/*
+ This is the background routine used to generate arbitrary data.
+ It should run in the background; therefore it is scheduled by
+ a timer mechanism.
+*/
+static void waveform_ai_interrupt(unsigned long arg)
+{
+ struct comedi_device *dev = (struct comedi_device *)arg;
+ struct waveform_private *devpriv = dev->private;
+ struct comedi_subdevice *s = dev->read_subdev;
+ struct comedi_async *async = s->async;
+ struct comedi_cmd *cmd = &async->cmd;
+ unsigned int i, j;
+ /* all times in microsec */
+ unsigned long elapsed_time;
+ unsigned int num_scans;
+ ktime_t now;
+
+ /* check command is still active */
+ if (!test_bit(WAVEFORM_AI_RUNNING, &devpriv->state_bits))
+ return;
+
+ now = ktime_get();
+
+ elapsed_time = ktime_to_us(ktime_sub(now, devpriv->last));
+ devpriv->last = now;
+ num_scans =
+ (devpriv->usec_remainder + elapsed_time) / devpriv->scan_period;
+ devpriv->usec_remainder =
+ (devpriv->usec_remainder + elapsed_time) % devpriv->scan_period;
+
+ num_scans = comedi_nscans_left(s, num_scans);
+ for (i = 0; i < num_scans; i++) {
+ for (j = 0; j < cmd->chanlist_len; j++) {
+ unsigned short sample;
+
+ sample = fake_waveform(dev, CR_CHAN(cmd->chanlist[j]),
+ CR_RANGE(cmd->chanlist[j]),
+ devpriv->usec_current +
+ i * devpriv->scan_period +
+ j * devpriv->convert_period);
+ comedi_buf_write_samples(s, &sample, 1);
+ }
+ }
+
+ devpriv->usec_current += elapsed_time;
+ devpriv->usec_current %= devpriv->usec_period;
+
+ if (cmd->stop_src == TRIG_COUNT && async->scans_done >= cmd->stop_arg)
+ async->events |= COMEDI_CB_EOA;
+ else
+ mod_timer(&devpriv->timer, jiffies + 1);
+
+ comedi_handle_events(dev, s);
+}
+
+static int waveform_ai_cmdtest(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_cmd *cmd)
+{
+ int err = 0;
+ unsigned int arg;
+
+ /* Step 1 : check if triggers are trivially valid */
+
+ err |= comedi_check_trigger_src(&cmd->start_src, TRIG_NOW);
+ err |= comedi_check_trigger_src(&cmd->scan_begin_src, TRIG_TIMER);
+ err |= comedi_check_trigger_src(&cmd->convert_src,
+ TRIG_NOW | TRIG_TIMER);
+ err |= comedi_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
+ err |= comedi_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE);
+
+ if (err)
+ return 1;
+
+ /* Step 2a : make sure trigger sources are unique */
+
+ err |= comedi_check_trigger_is_unique(cmd->convert_src);
+ err |= comedi_check_trigger_is_unique(cmd->stop_src);
+
+ /* Step 2b : and mutually compatible */
+
+ if (err)
+ return 2;
+
+ /* Step 3: check if arguments are trivially valid */
+
+ err |= comedi_check_trigger_arg_is(&cmd->start_arg, 0);
+
+ if (cmd->convert_src == TRIG_NOW)
+ err |= comedi_check_trigger_arg_is(&cmd->convert_arg, 0);
+
+ if (cmd->scan_begin_src == TRIG_TIMER) {
+ err |= comedi_check_trigger_arg_min(&cmd->scan_begin_arg,
+ nano_per_micro);
+ if (cmd->convert_src == TRIG_TIMER) {
+ err |= comedi_check_trigger_arg_min(&cmd->
+ scan_begin_arg,
+ cmd->convert_arg *
+ cmd->chanlist_len);
+ }
+ }
+
+ err |= comedi_check_trigger_arg_min(&cmd->chanlist_len, 1);
+ err |= comedi_check_trigger_arg_is(&cmd->scan_end_arg,
+ cmd->chanlist_len);
+
+ if (cmd->stop_src == TRIG_COUNT)
+ err |= comedi_check_trigger_arg_min(&cmd->stop_arg, 1);
+ else /* TRIG_NONE */
+ err |= comedi_check_trigger_arg_is(&cmd->stop_arg, 0);
+
+ if (err)
+ return 3;
+
+ /* step 4: fix up any arguments */
+
+ if (cmd->scan_begin_src == TRIG_TIMER) {
+ arg = cmd->scan_begin_arg;
+ /* round to nearest microsec */
+ arg = nano_per_micro *
+ ((arg + (nano_per_micro / 2)) / nano_per_micro);
+ err |= comedi_check_trigger_arg_is(&cmd->scan_begin_arg, arg);
+ }
+ if (cmd->convert_src == TRIG_TIMER) {
+ arg = cmd->convert_arg;
+ /* round to nearest microsec */
+ arg = nano_per_micro *
+ ((arg + (nano_per_micro / 2)) / nano_per_micro);
+ err |= comedi_check_trigger_arg_is(&cmd->convert_arg, arg);
+ }
+
+ if (err)
+ return 4;
+
+ return 0;
+}
+
+static int waveform_ai_cmd(struct comedi_device *dev,
+ struct comedi_subdevice *s)
+{
+ struct waveform_private *devpriv = dev->private;
+ struct comedi_cmd *cmd = &s->async->cmd;
+
+ if (cmd->flags & CMDF_PRIORITY) {
+ dev_err(dev->class_dev,
+ "commands at RT priority not supported in this driver\n");
+ return -1;
+ }
+
+ devpriv->scan_period = cmd->scan_begin_arg / nano_per_micro;
+
+ if (cmd->convert_src == TRIG_NOW)
+ devpriv->convert_period = 0;
+ else /* TRIG_TIMER */
+ devpriv->convert_period = cmd->convert_arg / nano_per_micro;
+
+ devpriv->last = ktime_get();
+ devpriv->usec_current =
+ ((u32)ktime_to_us(devpriv->last)) % devpriv->usec_period;
+ devpriv->usec_remainder = 0;
+
+ devpriv->timer.expires = jiffies + 1;
+ /* mark command as active */
+ smp_mb__before_atomic();
+ set_bit(WAVEFORM_AI_RUNNING, &devpriv->state_bits);
+ smp_mb__after_atomic();
+ add_timer(&devpriv->timer);
+ return 0;
+}
+
+static int waveform_ai_cancel(struct comedi_device *dev,
+ struct comedi_subdevice *s)
+{
+ struct waveform_private *devpriv = dev->private;
+
+ /* mark command as no longer active */
+ clear_bit(WAVEFORM_AI_RUNNING, &devpriv->state_bits);
+ smp_mb__after_atomic();
+ /* cannot call del_timer_sync() as may be called from timer routine */
+ del_timer(&devpriv->timer);
+ return 0;
+}
+
+static int waveform_ai_insn_read(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn, unsigned int *data)
+{
+ struct waveform_private *devpriv = dev->private;
+ int i, chan = CR_CHAN(insn->chanspec);
+
+ for (i = 0; i < insn->n; i++)
+ data[i] = devpriv->ao_loopbacks[chan];
+
+ return insn->n;
+}
+
+static int waveform_ao_insn_write(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn, unsigned int *data)
+{
+ struct waveform_private *devpriv = dev->private;
+ int i, chan = CR_CHAN(insn->chanspec);
+
+ for (i = 0; i < insn->n; i++)
+ devpriv->ao_loopbacks[chan] = data[i];
+
+ return insn->n;
+}
+
+static int waveform_attach(struct comedi_device *dev,
+ struct comedi_devconfig *it)
+{
+ struct waveform_private *devpriv;
+ struct comedi_subdevice *s;
+ int amplitude = it->options[0];
+ int period = it->options[1];
+ int i;
+ int ret;
+
+ devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
+ if (!devpriv)
+ return -ENOMEM;
+
+ /* set default amplitude and period */
+ if (amplitude <= 0)
+ amplitude = 1000000; /* 1 volt */
+ if (period <= 0)
+ period = 100000; /* 0.1 sec */
+
+ devpriv->uvolt_amplitude = amplitude;
+ devpriv->usec_period = period;
+
+ ret = comedi_alloc_subdevices(dev, 2);
+ if (ret)
+ return ret;
+
+ s = &dev->subdevices[0];
+ dev->read_subdev = s;
+ /* analog input subdevice */
+ s->type = COMEDI_SUBD_AI;
+ s->subdev_flags = SDF_READABLE | SDF_GROUND | SDF_CMD_READ;
+ s->n_chan = N_CHANS;
+ s->maxdata = 0xffff;
+ s->range_table = &waveform_ai_ranges;
+ s->len_chanlist = s->n_chan * 2;
+ s->insn_read = waveform_ai_insn_read;
+ s->do_cmd = waveform_ai_cmd;
+ s->do_cmdtest = waveform_ai_cmdtest;
+ s->cancel = waveform_ai_cancel;
+
+ s = &dev->subdevices[1];
+ dev->write_subdev = s;
+ /* analog output subdevice (loopback) */
+ s->type = COMEDI_SUBD_AO;
+ s->subdev_flags = SDF_WRITABLE | SDF_GROUND;
+ s->n_chan = N_CHANS;
+ s->maxdata = 0xffff;
+ s->range_table = &waveform_ai_ranges;
+ s->insn_write = waveform_ao_insn_write;
+
+ /* Our default loopback value is just a 0V flatline */
+ for (i = 0; i < s->n_chan; i++)
+ devpriv->ao_loopbacks[i] = s->maxdata / 2;
+
+ setup_timer(&devpriv->timer, waveform_ai_interrupt,
+ (unsigned long)dev);
+
+ dev_info(dev->class_dev,
+ "%s: %i microvolt, %li microsecond waveform attached\n",
+ dev->board_name,
+ devpriv->uvolt_amplitude, devpriv->usec_period);
+
+ return 0;
+}
+
+static void waveform_detach(struct comedi_device *dev)
+{
+ struct waveform_private *devpriv = dev->private;
+
+ if (devpriv)
+ del_timer_sync(&devpriv->timer);
+}
+
+static struct comedi_driver waveform_driver = {
+ .driver_name = "comedi_test",
+ .module = THIS_MODULE,
+ .attach = waveform_attach,
+ .detach = waveform_detach,
+};
+module_comedi_driver(waveform_driver);
+
+MODULE_AUTHOR("Comedi http://www.comedi.org");
+MODULE_DESCRIPTION("Comedi low-level driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/comedi/drivers/contec_pci_dio.c b/drivers/staging/comedi/drivers/contec_pci_dio.c
new file mode 100644
index 000000000..4956a49a6
--- /dev/null
+++ b/drivers/staging/comedi/drivers/contec_pci_dio.c
@@ -0,0 +1,125 @@
+/*
+ comedi/drivers/contec_pci_dio.c
+
+ COMEDI - Linux Control and Measurement Device Interface
+ Copyright (C) 2000 David A. Schleef <ds@schleef.org>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+*/
+/*
+Driver: contec_pci_dio
+Description: Contec PIO1616L digital I/O board
+Devices: [Contec] PIO1616L (contec_pci_dio)
+Author: Stefano Rivoir <s.rivoir@gts.it>
+Updated: Wed, 27 Jun 2007 13:00:06 +0100
+Status: works
+
+Configuration Options: not applicable, uses comedi PCI auto config
+*/
+
+#include <linux/module.h>
+
+#include "../comedi_pci.h"
+
+/*
+ * Register map
+ */
+#define PIO1616L_DI_REG 0x00
+#define PIO1616L_DO_REG 0x02
+
+static int contec_do_insn_bits(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ if (comedi_dio_update_state(s, data))
+ outw(s->state, dev->iobase + PIO1616L_DO_REG);
+
+ data[1] = s->state;
+
+ return insn->n;
+}
+
+static int contec_di_insn_bits(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn, unsigned int *data)
+{
+ data[1] = inw(dev->iobase + PIO1616L_DI_REG);
+
+ return insn->n;
+}
+
+static int contec_auto_attach(struct comedi_device *dev,
+ unsigned long context_unused)
+{
+ struct pci_dev *pcidev = comedi_to_pci_dev(dev);
+ struct comedi_subdevice *s;
+ int ret;
+
+ ret = comedi_pci_enable(dev);
+ if (ret)
+ return ret;
+ dev->iobase = pci_resource_start(pcidev, 0);
+
+ ret = comedi_alloc_subdevices(dev, 2);
+ if (ret)
+ return ret;
+
+ s = &dev->subdevices[0];
+ s->type = COMEDI_SUBD_DI;
+ s->subdev_flags = SDF_READABLE;
+ s->n_chan = 16;
+ s->maxdata = 1;
+ s->range_table = &range_digital;
+ s->insn_bits = contec_di_insn_bits;
+
+ s = &dev->subdevices[1];
+ s->type = COMEDI_SUBD_DO;
+ s->subdev_flags = SDF_WRITABLE;
+ s->n_chan = 16;
+ s->maxdata = 1;
+ s->range_table = &range_digital;
+ s->insn_bits = contec_do_insn_bits;
+
+ return 0;
+}
+
+static struct comedi_driver contec_pci_dio_driver = {
+ .driver_name = "contec_pci_dio",
+ .module = THIS_MODULE,
+ .auto_attach = contec_auto_attach,
+ .detach = comedi_pci_detach,
+};
+
+static int contec_pci_dio_pci_probe(struct pci_dev *dev,
+ const struct pci_device_id *id)
+{
+ return comedi_pci_auto_config(dev, &contec_pci_dio_driver,
+ id->driver_data);
+}
+
+static const struct pci_device_id contec_pci_dio_pci_table[] = {
+ { PCI_DEVICE(PCI_VENDOR_ID_CONTEC, 0x8172) },
+ { 0 }
+};
+MODULE_DEVICE_TABLE(pci, contec_pci_dio_pci_table);
+
+static struct pci_driver contec_pci_dio_pci_driver = {
+ .name = "contec_pci_dio",
+ .id_table = contec_pci_dio_pci_table,
+ .probe = contec_pci_dio_pci_probe,
+ .remove = comedi_pci_auto_unconfig,
+};
+module_comedi_pci_driver(contec_pci_dio_driver, contec_pci_dio_pci_driver);
+
+MODULE_AUTHOR("Comedi http://www.comedi.org");
+MODULE_DESCRIPTION("Comedi low-level driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/comedi/drivers/dac02.c b/drivers/staging/comedi/drivers/dac02.c
new file mode 100644
index 000000000..a6798ad8f
--- /dev/null
+++ b/drivers/staging/comedi/drivers/dac02.c
@@ -0,0 +1,150 @@
+/*
+ * dac02.c
+ * Comedi driver for DAC02 compatible boards
+ * Copyright (C) 2014 H Hartley Sweeten <hsweeten@visionengravers.com>
+ *
+ * Based on the poc driver
+ * Copyright (C) 2000 Frank Mori Hess <fmhess@users.sourceforge.net>
+ * Copyright (C) 2001 David A. Schleef <ds@schleef.org>
+ *
+ * COMEDI - Linux Control and Measurement Device Interface
+ * Copyright (C) 1998 David A. Schleef <ds@schleef.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+/*
+ * Driver: dac02
+ * Description: Comedi driver for DAC02 compatible boards
+ * Devices: [Keithley Metrabyte] DAC-02 (dac02)
+ * Author: H Hartley Sweeten <hsweeten@visionengravers.com>
+ * Updated: Tue, 11 Mar 2014 11:27:19 -0700
+ * Status: unknown
+ *
+ * Configuration options:
+ * [0] - I/O port base
+ */
+
+#include <linux/module.h>
+
+#include "../comedidev.h"
+
+/*
+ * The output range is selected by jumpering pins on the I/O connector.
+ *
+ * Range Chan # Jumper pins Output
+ * ------------- ------ ------------- -----------------
+ * 0 to 5V 0 21 to 22 24
+ * 1 15 to 16 18
+ * 0 to 10V 0 20 to 22 24
+ * 1 14 to 16 18
+ * +/-5V 0 21 to 22 23
+ * 1 15 to 16 17
+ * +/-10V 0 20 to 22 23
+ * 1 14 to 16 17
+ * 4 to 20mA 0 21 to 22 25
+ * 1 15 to 16 19
+ * AC reference 0 In on pin 22 24 (2-quadrant)
+ * In on pin 22 23 (4-quadrant)
+ * 1 In on pin 16 18 (2-quadrant)
+ * In on pin 16 17 (4-quadrant)
+ */
+static const struct comedi_lrange das02_ao_ranges = {
+ 6, {
+ UNI_RANGE(5),
+ UNI_RANGE(10),
+ BIP_RANGE(5),
+ BIP_RANGE(10),
+ RANGE_mA(4, 20),
+ RANGE_ext(0, 1)
+ }
+};
+
+/*
+ * Register I/O map
+ */
+#define DAC02_AO_LSB(x) (0x00 + ((x) * 2))
+#define DAC02_AO_MSB(x) (0x01 + ((x) * 2))
+
+static int dac02_ao_insn_write(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ unsigned int chan = CR_CHAN(insn->chanspec);
+ unsigned int range = CR_RANGE(insn->chanspec);
+ unsigned int val;
+ int i;
+
+ for (i = 0; i < insn->n; i++) {
+ val = data[i];
+
+ s->readback[chan] = val;
+
+ /*
+ * Unipolar outputs are true binary encoding.
+ * Bipolar outputs are complementary offset binary
+ * (that is, 0 = +full scale, maxdata = -full scale).
+ */
+ if (comedi_range_is_bipolar(s, range))
+ val = s->maxdata - val;
+
+ /*
+ * DACs are double-buffered.
+ * Write LSB then MSB to latch output.
+ */
+ outb((val << 4) & 0xf0, dev->iobase + DAC02_AO_LSB(chan));
+ outb((val >> 4) & 0xff, dev->iobase + DAC02_AO_MSB(chan));
+ }
+
+ return insn->n;
+}
+
+static int dac02_attach(struct comedi_device *dev, struct comedi_devconfig *it)
+{
+ struct comedi_subdevice *s;
+ int ret;
+
+ ret = comedi_request_region(dev, it->options[0], 0x08);
+ if (ret)
+ return ret;
+
+ ret = comedi_alloc_subdevices(dev, 1);
+ if (ret)
+ return ret;
+
+ /* Analog Output subdevice */
+ s = &dev->subdevices[0];
+ s->type = COMEDI_SUBD_AO;
+ s->subdev_flags = SDF_WRITABLE;
+ s->n_chan = 2;
+ s->maxdata = 0x0fff;
+ s->range_table = &das02_ao_ranges;
+ s->insn_write = dac02_ao_insn_write;
+
+ ret = comedi_alloc_subdev_readback(s);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static struct comedi_driver dac02_driver = {
+ .driver_name = "dac02",
+ .module = THIS_MODULE,
+ .attach = dac02_attach,
+ .detach = comedi_legacy_detach,
+};
+module_comedi_driver(dac02_driver);
+
+MODULE_AUTHOR("H Hartley Sweeten <hsweeten@visionengravers.com>");
+MODULE_DESCRIPTION("Comedi driver for DAC02 compatible boards");
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/comedi/drivers/daqboard2000.c b/drivers/staging/comedi/drivers/daqboard2000.c
new file mode 100644
index 000000000..0bba7a9c0
--- /dev/null
+++ b/drivers/staging/comedi/drivers/daqboard2000.c
@@ -0,0 +1,764 @@
+/*
+ comedi/drivers/daqboard2000.c
+ hardware driver for IOtech DAQboard/2000
+
+ COMEDI - Linux Control and Measurement Device Interface
+ Copyright (C) 1999 Anders Blomdell <anders.blomdell@control.lth.se>
+
+ This program is free software; you can redistribute it and/or 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.
+ */
+/*
+Driver: daqboard2000
+Description: IOTech DAQBoard/2000
+Author: Anders Blomdell <anders.blomdell@control.lth.se>
+Status: works
+Updated: Mon, 14 Apr 2008 15:28:52 +0100
+Devices: [IOTech] DAQBoard/2000 (daqboard2000)
+
+Much of the functionality of this driver was determined from reading
+the source code for the Windows driver.
+
+The FPGA on the board requires fimware, which is available from
+http://www.comedi.org in the comedi_nonfree_firmware tarball.
+
+Configuration options: not applicable, uses PCI auto config
+*/
+/*
+ This card was obviously never intended to leave the Windows world,
+ since it lacked all kind of hardware documentation (except for cable
+ pinouts, plug and pray has something to catch up with yet).
+
+ With some help from our swedish distributor, we got the Windows sourcecode
+ for the card, and here are the findings so far.
+
+ 1. A good document that describes the PCI interface chip is 9080db-106.pdf
+ available from http://www.plxtech.com/products/io/pci9080
+
+ 2. The initialization done so far is:
+ a. program the FPGA (windows code sans a lot of error messages)
+ b.
+
+ 3. Analog out seems to work OK with DAC's disabled, if DAC's are enabled,
+ you have to output values to all enabled DAC's until result appears, I
+ guess that it has something to do with pacer clocks, but the source
+ gives me no clues. I'll keep it simple so far.
+
+ 4. Analog in.
+ Each channel in the scanlist seems to be controlled by four
+ control words:
+
+ Word0:
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ ! | | | ! | | | ! | | | ! | | | !
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+ Word1:
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ ! | | | ! | | | ! | | | ! | | | !
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | | | | | | |
+ +------+------+ | | | | +-- Digital input (??)
+ | | | | +---- 10 us settling time
+ | | | +------ Suspend acquisition (last to scan)
+ | | +-------- Simultaneous sample and hold
+ | +---------- Signed data format
+ +------------------------- Correction offset low
+
+ Word2:
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ ! | | | ! | | | ! | | | ! | | | !
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | | | | | | | | | |
+ +-----+ +--+--+ +++ +++ +--+--+
+ | | | | +----- Expansion channel
+ | | | +----------- Expansion gain
+ | | +--------------- Channel (low)
+ | +--------------------- Correction offset high
+ +----------------------------- Correction gain low
+ Word3:
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ ! | | | ! | | | ! | | | ! | | | !
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | | | | | | | | |
+ +------+------+ | | +-+-+ | | +-- Low bank enable
+ | | | | | +---- High bank enable
+ | | | | +------ Hi/low select
+ | | | +---------- Gain (1,?,2,4,8,16,32,64)
+ | | +-------------- differential/single ended
+ | +---------------- Unipolar
+ +------------------------- Correction gain high
+
+ 999. The card seems to have an incredible amount of capabilities, but
+ trying to reverse engineer them from the Windows source is beyond my
+ patience.
+
+ */
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+
+#include "../comedi_pci.h"
+
+#include "8255.h"
+
+#define DAQBOARD2000_FIRMWARE "/*(DEBLOBBED)*/"
+
+#define DAQBOARD2000_SUBSYSTEM_IDS2 0x0002 /* Daqboard/2000 - 2 Dacs */
+#define DAQBOARD2000_SUBSYSTEM_IDS4 0x0004 /* Daqboard/2000 - 4 Dacs */
+
+/* Initialization bits for the Serial EEPROM Control Register */
+#define DAQBOARD2000_SECRProgPinHi 0x8001767e
+#define DAQBOARD2000_SECRProgPinLo 0x8000767e
+#define DAQBOARD2000_SECRLocalBusHi 0xc000767e
+#define DAQBOARD2000_SECRLocalBusLo 0x8000767e
+#define DAQBOARD2000_SECRReloadHi 0xa000767e
+#define DAQBOARD2000_SECRReloadLo 0x8000767e
+
+/* SECR status bits */
+#define DAQBOARD2000_EEPROM_PRESENT 0x10000000
+
+/* CPLD status bits */
+#define DAQBOARD2000_CPLD_INIT 0x0002
+#define DAQBOARD2000_CPLD_DONE 0x0004
+
+static const struct comedi_lrange range_daqboard2000_ai = {
+ 13, {
+ BIP_RANGE(10),
+ BIP_RANGE(5),
+ BIP_RANGE(2.5),
+ BIP_RANGE(1.25),
+ BIP_RANGE(0.625),
+ BIP_RANGE(0.3125),
+ BIP_RANGE(0.156),
+ UNI_RANGE(10),
+ UNI_RANGE(5),
+ UNI_RANGE(2.5),
+ UNI_RANGE(1.25),
+ UNI_RANGE(0.625),
+ UNI_RANGE(0.3125)
+ }
+};
+
+/*
+ * Register Memory Map
+ */
+#define acqControl 0x00 /* u16 */
+#define acqScanListFIFO 0x02 /* u16 */
+#define acqPacerClockDivLow 0x04 /* u32 */
+#define acqScanCounter 0x08 /* u16 */
+#define acqPacerClockDivHigh 0x0a /* u16 */
+#define acqTriggerCount 0x0c /* u16 */
+#define acqResultsFIFO 0x10 /* u16 */
+#define acqResultsShadow 0x14 /* u16 */
+#define acqAdcResult 0x18 /* u16 */
+#define dacScanCounter 0x1c /* u16 */
+#define dacControl 0x20 /* u16 */
+#define dacFIFO 0x24 /* s16 */
+#define dacPacerClockDiv 0x2a /* u16 */
+#define refDacs 0x2c /* u16 */
+#define dioControl 0x30 /* u16 */
+#define dioP3hsioData 0x32 /* s16 */
+#define dioP3Control 0x34 /* u16 */
+#define calEepromControl 0x36 /* u16 */
+#define dacSetting(x) (0x38 + (x)*2) /* s16 */
+#define dioP2ExpansionIO8Bit 0x40 /* s16 */
+#define ctrTmrControl 0x80 /* u16 */
+#define ctrInput(x) (0x88 + (x)*2) /* s16 */
+#define timerDivisor(x) (0xa0 + (x)*2) /* u16 */
+#define dmaControl 0xb0 /* u16 */
+#define trigControl 0xb2 /* u16 */
+#define calEeprom 0xb8 /* u16 */
+#define acqDigitalMark 0xba /* u16 */
+#define trigDacs 0xbc /* u16 */
+#define dioP2ExpansionIO16Bit(x) (0xc0 + (x)*2) /* s16 */
+
+/* Scan Sequencer programming */
+#define DAQBOARD2000_SeqStartScanList 0x0011
+#define DAQBOARD2000_SeqStopScanList 0x0010
+
+/* Prepare for acquisition */
+#define DAQBOARD2000_AcqResetScanListFifo 0x0004
+#define DAQBOARD2000_AcqResetResultsFifo 0x0002
+#define DAQBOARD2000_AcqResetConfigPipe 0x0001
+
+/* Acqusition status bits */
+#define DAQBOARD2000_AcqResultsFIFOMore1Sample 0x0001
+#define DAQBOARD2000_AcqResultsFIFOHasValidData 0x0002
+#define DAQBOARD2000_AcqResultsFIFOOverrun 0x0004
+#define DAQBOARD2000_AcqLogicScanning 0x0008
+#define DAQBOARD2000_AcqConfigPipeFull 0x0010
+#define DAQBOARD2000_AcqScanListFIFOEmpty 0x0020
+#define DAQBOARD2000_AcqAdcNotReady 0x0040
+#define DAQBOARD2000_ArbitrationFailure 0x0080
+#define DAQBOARD2000_AcqPacerOverrun 0x0100
+#define DAQBOARD2000_DacPacerOverrun 0x0200
+#define DAQBOARD2000_AcqHardwareError 0x01c0
+
+/* Scan Sequencer programming */
+#define DAQBOARD2000_SeqStartScanList 0x0011
+#define DAQBOARD2000_SeqStopScanList 0x0010
+
+/* Pacer Clock Control */
+#define DAQBOARD2000_AdcPacerInternal 0x0030
+#define DAQBOARD2000_AdcPacerExternal 0x0032
+#define DAQBOARD2000_AdcPacerEnable 0x0031
+#define DAQBOARD2000_AdcPacerEnableDacPacer 0x0034
+#define DAQBOARD2000_AdcPacerDisable 0x0030
+#define DAQBOARD2000_AdcPacerNormalMode 0x0060
+#define DAQBOARD2000_AdcPacerCompatibilityMode 0x0061
+#define DAQBOARD2000_AdcPacerInternalOutEnable 0x0008
+#define DAQBOARD2000_AdcPacerExternalRising 0x0100
+
+/* DAC status */
+#define DAQBOARD2000_DacFull 0x0001
+#define DAQBOARD2000_RefBusy 0x0002
+#define DAQBOARD2000_TrgBusy 0x0004
+#define DAQBOARD2000_CalBusy 0x0008
+#define DAQBOARD2000_Dac0Busy 0x0010
+#define DAQBOARD2000_Dac1Busy 0x0020
+#define DAQBOARD2000_Dac2Busy 0x0040
+#define DAQBOARD2000_Dac3Busy 0x0080
+
+/* DAC control */
+#define DAQBOARD2000_Dac0Enable 0x0021
+#define DAQBOARD2000_Dac1Enable 0x0031
+#define DAQBOARD2000_Dac2Enable 0x0041
+#define DAQBOARD2000_Dac3Enable 0x0051
+#define DAQBOARD2000_DacEnableBit 0x0001
+#define DAQBOARD2000_Dac0Disable 0x0020
+#define DAQBOARD2000_Dac1Disable 0x0030
+#define DAQBOARD2000_Dac2Disable 0x0040
+#define DAQBOARD2000_Dac3Disable 0x0050
+#define DAQBOARD2000_DacResetFifo 0x0004
+#define DAQBOARD2000_DacPatternDisable 0x0060
+#define DAQBOARD2000_DacPatternEnable 0x0061
+#define DAQBOARD2000_DacSelectSignedData 0x0002
+#define DAQBOARD2000_DacSelectUnsignedData 0x0000
+
+/* Trigger Control */
+#define DAQBOARD2000_TrigAnalog 0x0000
+#define DAQBOARD2000_TrigTTL 0x0010
+#define DAQBOARD2000_TrigTransHiLo 0x0004
+#define DAQBOARD2000_TrigTransLoHi 0x0000
+#define DAQBOARD2000_TrigAbove 0x0000
+#define DAQBOARD2000_TrigBelow 0x0004
+#define DAQBOARD2000_TrigLevelSense 0x0002
+#define DAQBOARD2000_TrigEdgeSense 0x0000
+#define DAQBOARD2000_TrigEnable 0x0001
+#define DAQBOARD2000_TrigDisable 0x0000
+
+/* Reference Dac Selection */
+#define DAQBOARD2000_PosRefDacSelect 0x0100
+#define DAQBOARD2000_NegRefDacSelect 0x0000
+
+struct daq200_boardtype {
+ const char *name;
+ int id;
+};
+static const struct daq200_boardtype boardtypes[] = {
+ {"ids2", DAQBOARD2000_SUBSYSTEM_IDS2},
+ {"ids4", DAQBOARD2000_SUBSYSTEM_IDS4},
+};
+
+struct daqboard2000_private {
+ enum {
+ card_daqboard_2000
+ } card;
+ void __iomem *plx;
+};
+
+static void writeAcqScanListEntry(struct comedi_device *dev, u16 entry)
+{
+ /* udelay(4); */
+ writew(entry & 0x00ff, dev->mmio + acqScanListFIFO);
+ /* udelay(4); */
+ writew((entry >> 8) & 0x00ff, dev->mmio + acqScanListFIFO);
+}
+
+static void setup_sampling(struct comedi_device *dev, int chan, int gain)
+{
+ u16 word0, word1, word2, word3;
+
+ /* Channel 0-7 diff, channel 8-23 single ended */
+ word0 = 0;
+ word1 = 0x0004; /* Last scan */
+ word2 = (chan << 6) & 0x00c0;
+ switch (chan / 4) {
+ case 0:
+ word3 = 0x0001;
+ break;
+ case 1:
+ word3 = 0x0002;
+ break;
+ case 2:
+ word3 = 0x0005;
+ break;
+ case 3:
+ word3 = 0x0006;
+ break;
+ case 4:
+ word3 = 0x0041;
+ break;
+ case 5:
+ word3 = 0x0042;
+ break;
+ default:
+ word3 = 0;
+ break;
+ }
+/*
+ dev->eeprom.correctionDACSE[i][j][k].offset = 0x800;
+ dev->eeprom.correctionDACSE[i][j][k].gain = 0xc00;
+*/
+ /* These should be read from EEPROM */
+ word2 |= 0x0800;
+ word3 |= 0xc000;
+ writeAcqScanListEntry(dev, word0);
+ writeAcqScanListEntry(dev, word1);
+ writeAcqScanListEntry(dev, word2);
+ writeAcqScanListEntry(dev, word3);
+}
+
+static int daqboard2000_ai_status(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned long context)
+{
+ unsigned int status;
+
+ status = readw(dev->mmio + acqControl);
+ if (status & context)
+ return 0;
+ return -EBUSY;
+}
+
+static int daqboard2000_ai_insn_read(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ int gain, chan;
+ int ret;
+ int i;
+
+ writew(DAQBOARD2000_AcqResetScanListFifo |
+ DAQBOARD2000_AcqResetResultsFifo |
+ DAQBOARD2000_AcqResetConfigPipe, dev->mmio + acqControl);
+
+ /*
+ * If pacer clock is not set to some high value (> 10 us), we
+ * risk multiple samples to be put into the result FIFO.
+ */
+ /* 1 second, should be long enough */
+ writel(1000000, dev->mmio + acqPacerClockDivLow);
+ writew(0, dev->mmio + acqPacerClockDivHigh);
+
+ gain = CR_RANGE(insn->chanspec);
+ chan = CR_CHAN(insn->chanspec);
+
+ /* This doesn't look efficient. I decided to take the conservative
+ * approach when I did the insn conversion. Perhaps it would be
+ * better to have broken it completely, then someone would have been
+ * forced to fix it. --ds */
+ for (i = 0; i < insn->n; i++) {
+ setup_sampling(dev, chan, gain);
+ /* Enable reading from the scanlist FIFO */
+ writew(DAQBOARD2000_SeqStartScanList, dev->mmio + acqControl);
+
+ ret = comedi_timeout(dev, s, insn, daqboard2000_ai_status,
+ DAQBOARD2000_AcqConfigPipeFull);
+ if (ret)
+ return ret;
+
+ writew(DAQBOARD2000_AdcPacerEnable, dev->mmio + acqControl);
+
+ ret = comedi_timeout(dev, s, insn, daqboard2000_ai_status,
+ DAQBOARD2000_AcqLogicScanning);
+ if (ret)
+ return ret;
+
+ ret = comedi_timeout(dev, s, insn, daqboard2000_ai_status,
+ DAQBOARD2000_AcqResultsFIFOHasValidData);
+ if (ret)
+ return ret;
+
+ data[i] = readw(dev->mmio + acqResultsFIFO);
+ writew(DAQBOARD2000_AdcPacerDisable, dev->mmio + acqControl);
+ writew(DAQBOARD2000_SeqStopScanList, dev->mmio + acqControl);
+ }
+
+ return i;
+}
+
+static int daqboard2000_ao_eoc(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned long context)
+{
+ unsigned int chan = CR_CHAN(insn->chanspec);
+ unsigned int status;
+
+ status = readw(dev->mmio + dacControl);
+ if ((status & ((chan + 1) * 0x0010)) == 0)
+ return 0;
+ return -EBUSY;
+}
+
+static int daqboard2000_ao_insn_write(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ unsigned int chan = CR_CHAN(insn->chanspec);
+ int i;
+
+ for (i = 0; i < insn->n; i++) {
+ unsigned int val = data[i];
+ int ret;
+
+ writew(val, dev->mmio + dacSetting(chan));
+
+ ret = comedi_timeout(dev, s, insn, daqboard2000_ao_eoc, 0);
+ if (ret)
+ return ret;
+
+ s->readback[chan] = val;
+ }
+
+ return insn->n;
+}
+
+static void daqboard2000_resetLocalBus(struct comedi_device *dev)
+{
+ struct daqboard2000_private *devpriv = dev->private;
+
+ writel(DAQBOARD2000_SECRLocalBusHi, devpriv->plx + 0x6c);
+ mdelay(10);
+ writel(DAQBOARD2000_SECRLocalBusLo, devpriv->plx + 0x6c);
+ mdelay(10);
+}
+
+static void daqboard2000_reloadPLX(struct comedi_device *dev)
+{
+ struct daqboard2000_private *devpriv = dev->private;
+
+ writel(DAQBOARD2000_SECRReloadLo, devpriv->plx + 0x6c);
+ mdelay(10);
+ writel(DAQBOARD2000_SECRReloadHi, devpriv->plx + 0x6c);
+ mdelay(10);
+ writel(DAQBOARD2000_SECRReloadLo, devpriv->plx + 0x6c);
+ mdelay(10);
+}
+
+static void daqboard2000_pulseProgPin(struct comedi_device *dev)
+{
+ struct daqboard2000_private *devpriv = dev->private;
+
+ writel(DAQBOARD2000_SECRProgPinHi, devpriv->plx + 0x6c);
+ mdelay(10);
+ writel(DAQBOARD2000_SECRProgPinLo, devpriv->plx + 0x6c);
+ mdelay(10); /* Not in the original code, but I like symmetry... */
+}
+
+static int daqboard2000_pollCPLD(struct comedi_device *dev, int mask)
+{
+ int result = 0;
+ int i;
+ int cpld;
+
+ /* timeout after 50 tries -> 5ms */
+ for (i = 0; i < 50; i++) {
+ cpld = readw(dev->mmio + 0x1000);
+ if ((cpld & mask) == mask) {
+ result = 1;
+ break;
+ }
+ udelay(100);
+ }
+ udelay(5);
+ return result;
+}
+
+static int daqboard2000_writeCPLD(struct comedi_device *dev, int data)
+{
+ int result = 0;
+
+ udelay(10);
+ writew(data, dev->mmio + 0x1000);
+ if ((readw(dev->mmio + 0x1000) & DAQBOARD2000_CPLD_INIT) ==
+ DAQBOARD2000_CPLD_INIT) {
+ result = 1;
+ }
+ return result;
+}
+
+static int initialize_daqboard2000(struct comedi_device *dev,
+ const u8 *cpld_array, size_t len,
+ unsigned long context)
+{
+ struct daqboard2000_private *devpriv = dev->private;
+ int result = -EIO;
+ /* Read the serial EEPROM control register */
+ int secr;
+ int retry;
+ size_t i;
+
+ /* Check to make sure the serial eeprom is present on the board */
+ secr = readl(devpriv->plx + 0x6c);
+ if (!(secr & DAQBOARD2000_EEPROM_PRESENT))
+ return -EIO;
+
+ for (retry = 0; retry < 3; retry++) {
+ daqboard2000_resetLocalBus(dev);
+ daqboard2000_reloadPLX(dev);
+ daqboard2000_pulseProgPin(dev);
+ if (daqboard2000_pollCPLD(dev, DAQBOARD2000_CPLD_INIT)) {
+ for (i = 0; i < len; i++) {
+ if (cpld_array[i] == 0xff &&
+ cpld_array[i + 1] == 0x20)
+ break;
+ }
+ for (; i < len; i += 2) {
+ int data =
+ (cpld_array[i] << 8) + cpld_array[i + 1];
+ if (!daqboard2000_writeCPLD(dev, data))
+ break;
+ }
+ if (i >= len) {
+ daqboard2000_resetLocalBus(dev);
+ daqboard2000_reloadPLX(dev);
+ result = 0;
+ break;
+ }
+ }
+ }
+ return result;
+}
+
+static void daqboard2000_adcStopDmaTransfer(struct comedi_device *dev)
+{
+}
+
+static void daqboard2000_adcDisarm(struct comedi_device *dev)
+{
+ /* Disable hardware triggers */
+ udelay(2);
+ writew(DAQBOARD2000_TrigAnalog | DAQBOARD2000_TrigDisable,
+ dev->mmio + trigControl);
+ udelay(2);
+ writew(DAQBOARD2000_TrigTTL | DAQBOARD2000_TrigDisable,
+ dev->mmio + trigControl);
+
+ /* Stop the scan list FIFO from loading the configuration pipe */
+ udelay(2);
+ writew(DAQBOARD2000_SeqStopScanList, dev->mmio + acqControl);
+
+ /* Stop the pacer clock */
+ udelay(2);
+ writew(DAQBOARD2000_AdcPacerDisable, dev->mmio + acqControl);
+
+ /* Stop the input dma (abort channel 1) */
+ daqboard2000_adcStopDmaTransfer(dev);
+}
+
+static void daqboard2000_activateReferenceDacs(struct comedi_device *dev)
+{
+ unsigned int val;
+ int timeout;
+
+ /* Set the + reference dac value in the FPGA */
+ writew(0x80 | DAQBOARD2000_PosRefDacSelect, dev->mmio + refDacs);
+ for (timeout = 0; timeout < 20; timeout++) {
+ val = readw(dev->mmio + dacControl);
+ if ((val & DAQBOARD2000_RefBusy) == 0)
+ break;
+ udelay(2);
+ }
+
+ /* Set the - reference dac value in the FPGA */
+ writew(0x80 | DAQBOARD2000_NegRefDacSelect, dev->mmio + refDacs);
+ for (timeout = 0; timeout < 20; timeout++) {
+ val = readw(dev->mmio + dacControl);
+ if ((val & DAQBOARD2000_RefBusy) == 0)
+ break;
+ udelay(2);
+ }
+}
+
+static void daqboard2000_initializeCtrs(struct comedi_device *dev)
+{
+}
+
+static void daqboard2000_initializeTmrs(struct comedi_device *dev)
+{
+}
+
+static void daqboard2000_dacDisarm(struct comedi_device *dev)
+{
+}
+
+static void daqboard2000_initializeAdc(struct comedi_device *dev)
+{
+ daqboard2000_adcDisarm(dev);
+ daqboard2000_activateReferenceDacs(dev);
+ daqboard2000_initializeCtrs(dev);
+ daqboard2000_initializeTmrs(dev);
+}
+
+static void daqboard2000_initializeDac(struct comedi_device *dev)
+{
+ daqboard2000_dacDisarm(dev);
+}
+
+static int daqboard2000_8255_cb(struct comedi_device *dev,
+ int dir, int port, int data,
+ unsigned long iobase)
+{
+ if (dir) {
+ writew(data, dev->mmio + iobase + port * 2);
+ return 0;
+ }
+ return readw(dev->mmio + iobase + port * 2);
+}
+
+static const void *daqboard2000_find_boardinfo(struct comedi_device *dev,
+ struct pci_dev *pcidev)
+{
+ const struct daq200_boardtype *board;
+ int i;
+
+ if (pcidev->subsystem_device != PCI_VENDOR_ID_IOTECH)
+ return NULL;
+
+ for (i = 0; i < ARRAY_SIZE(boardtypes); i++) {
+ board = &boardtypes[i];
+ if (pcidev->subsystem_device == board->id)
+ return board;
+ }
+ return NULL;
+}
+
+static int daqboard2000_auto_attach(struct comedi_device *dev,
+ unsigned long context_unused)
+{
+ struct pci_dev *pcidev = comedi_to_pci_dev(dev);
+ const struct daq200_boardtype *board;
+ struct daqboard2000_private *devpriv;
+ struct comedi_subdevice *s;
+ int result;
+
+ board = daqboard2000_find_boardinfo(dev, pcidev);
+ if (!board)
+ return -ENODEV;
+ dev->board_ptr = board;
+ dev->board_name = board->name;
+
+ devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
+ if (!devpriv)
+ return -ENOMEM;
+
+ result = comedi_pci_enable(dev);
+ if (result)
+ return result;
+
+ devpriv->plx = pci_ioremap_bar(pcidev, 0);
+ dev->mmio = pci_ioremap_bar(pcidev, 2);
+ if (!devpriv->plx || !dev->mmio)
+ return -ENOMEM;
+
+ result = comedi_alloc_subdevices(dev, 3);
+ if (result)
+ return result;
+
+ readl(devpriv->plx + 0x6c);
+
+ result = comedi_load_firmware(dev, &comedi_to_pci_dev(dev)->dev,
+ DAQBOARD2000_FIRMWARE,
+ initialize_daqboard2000, 0);
+ if (result < 0)
+ return result;
+
+ daqboard2000_initializeAdc(dev);
+ daqboard2000_initializeDac(dev);
+
+ s = &dev->subdevices[0];
+ /* ai subdevice */
+ s->type = COMEDI_SUBD_AI;
+ s->subdev_flags = SDF_READABLE | SDF_GROUND;
+ s->n_chan = 24;
+ s->maxdata = 0xffff;
+ s->insn_read = daqboard2000_ai_insn_read;
+ s->range_table = &range_daqboard2000_ai;
+
+ s = &dev->subdevices[1];
+ /* ao subdevice */
+ s->type = COMEDI_SUBD_AO;
+ s->subdev_flags = SDF_WRITABLE;
+ s->n_chan = 2;
+ s->maxdata = 0xffff;
+ s->insn_write = daqboard2000_ao_insn_write;
+ s->range_table = &range_bipolar10;
+
+ result = comedi_alloc_subdev_readback(s);
+ if (result)
+ return result;
+
+ s = &dev->subdevices[2];
+ result = subdev_8255_init(dev, s, daqboard2000_8255_cb,
+ dioP2ExpansionIO8Bit);
+ if (result)
+ return result;
+
+ return 0;
+}
+
+static void daqboard2000_detach(struct comedi_device *dev)
+{
+ struct daqboard2000_private *devpriv = dev->private;
+
+ if (devpriv && devpriv->plx)
+ iounmap(devpriv->plx);
+ comedi_pci_detach(dev);
+}
+
+static struct comedi_driver daqboard2000_driver = {
+ .driver_name = "daqboard2000",
+ .module = THIS_MODULE,
+ .auto_attach = daqboard2000_auto_attach,
+ .detach = daqboard2000_detach,
+};
+
+static int daqboard2000_pci_probe(struct pci_dev *dev,
+ const struct pci_device_id *id)
+{
+ return comedi_pci_auto_config(dev, &daqboard2000_driver,
+ id->driver_data);
+}
+
+static const struct pci_device_id daqboard2000_pci_table[] = {
+ { PCI_DEVICE(PCI_VENDOR_ID_IOTECH, 0x0409) },
+ { 0 }
+};
+MODULE_DEVICE_TABLE(pci, daqboard2000_pci_table);
+
+static struct pci_driver daqboard2000_pci_driver = {
+ .name = "daqboard2000",
+ .id_table = daqboard2000_pci_table,
+ .probe = daqboard2000_pci_probe,
+ .remove = comedi_pci_auto_unconfig,
+};
+module_comedi_pci_driver(daqboard2000_driver, daqboard2000_pci_driver);
+
+MODULE_AUTHOR("Comedi http://www.comedi.org");
+MODULE_DESCRIPTION("Comedi low-level driver");
+MODULE_LICENSE("GPL");
+/*(DEBLOBBED)*/
diff --git a/drivers/staging/comedi/drivers/das08.c b/drivers/staging/comedi/drivers/das08.c
new file mode 100644
index 000000000..73f4c8dbb
--- /dev/null
+++ b/drivers/staging/comedi/drivers/das08.c
@@ -0,0 +1,489 @@
+/*
+ * comedi/drivers/das08.c
+ * comedi module for common DAS08 support (used by ISA/PCI/PCMCIA drivers)
+ *
+ * COMEDI - Linux Control and Measurement Device Interface
+ * Copyright (C) 2000 David A. Schleef <ds@schleef.org>
+ * Copyright (C) 2001,2002,2003 Frank Mori Hess <fmhess@users.sourceforge.net>
+ * Copyright (C) 2004 Salvador E. Tropea <set@users.sf.net> <set@ieee.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+
+#include "../comedidev.h"
+
+#include "8255.h"
+#include "comedi_8254.h"
+#include "das08.h"
+
+/*
+ cio-das08.pdf
+
+ "isa-das08"
+
+ 0 a/d bits 0-3 start 8 bit
+ 1 a/d bits 4-11 start 12 bit
+ 2 eoc, ip1-3, irq, mux op1-4, inte, mux
+ 3 unused unused
+ 4567 8254
+ 89ab 8255
+
+ requires hard-wiring for async ai
+
+*/
+
+#define DAS08_LSB 0
+#define DAS08_MSB 1
+#define DAS08_TRIG_12BIT 1
+#define DAS08_STATUS 2
+#define DAS08_EOC (1<<7)
+#define DAS08_IRQ (1<<3)
+#define DAS08_IP(x) (((x)>>4)&0x7)
+#define DAS08_CONTROL 2
+#define DAS08_MUX_MASK 0x7
+#define DAS08_MUX(x) ((x) & DAS08_MUX_MASK)
+#define DAS08_INTE (1<<3)
+#define DAS08_DO_MASK 0xf0
+#define DAS08_OP(x) (((x) << 4) & DAS08_DO_MASK)
+
+/*
+ cio-das08jr.pdf
+
+ "das08/jr-ao"
+
+ 0 a/d bits 0-3 unused
+ 1 a/d bits 4-11 start 12 bit
+ 2 eoc, mux mux
+ 3 di do
+ 4 unused ao0_lsb
+ 5 unused ao0_msb
+ 6 unused ao1_lsb
+ 7 unused ao1_msb
+
+*/
+
+#define DAS08JR_DIO 3
+#define DAS08JR_AO_LSB(x) ((x) ? 6 : 4)
+#define DAS08JR_AO_MSB(x) ((x) ? 7 : 5)
+
+/*
+ cio-das08_aox.pdf
+
+ "das08-aoh"
+ "das08-aol"
+ "das08-aom"
+
+ 0 a/d bits 0-3 start 8 bit
+ 1 a/d bits 4-11 start 12 bit
+ 2 eoc, ip1-3, irq, mux op1-4, inte, mux
+ 3 mux, gain status gain control
+ 4567 8254
+ 8 unused ao0_lsb
+ 9 unused ao0_msb
+ a unused ao1_lsb
+ b unused ao1_msb
+ 89ab
+ cdef 8255
+*/
+
+#define DAS08AO_GAIN_CONTROL 3
+#define DAS08AO_GAIN_STATUS 3
+
+#define DAS08AO_AO_LSB(x) ((x) ? 0xa : 8)
+#define DAS08AO_AO_MSB(x) ((x) ? 0xb : 9)
+#define DAS08AO_AO_UPDATE 8
+
+/* gainlist same as _pgx_ below */
+
+static const struct comedi_lrange range_das08_pgl = {
+ 9, {
+ BIP_RANGE(10),
+ BIP_RANGE(5),
+ BIP_RANGE(2.5),
+ BIP_RANGE(1.25),
+ BIP_RANGE(0.625),
+ UNI_RANGE(10),
+ UNI_RANGE(5),
+ UNI_RANGE(2.5),
+ UNI_RANGE(1.25)
+ }
+};
+
+static const struct comedi_lrange range_das08_pgh = {
+ 12, {
+ BIP_RANGE(10),
+ BIP_RANGE(5),
+ BIP_RANGE(1),
+ BIP_RANGE(0.5),
+ BIP_RANGE(0.1),
+ BIP_RANGE(0.05),
+ BIP_RANGE(0.01),
+ BIP_RANGE(0.005),
+ UNI_RANGE(10),
+ UNI_RANGE(1),
+ UNI_RANGE(0.1),
+ UNI_RANGE(0.01)
+ }
+};
+
+static const struct comedi_lrange range_das08_pgm = {
+ 9, {
+ BIP_RANGE(10),
+ BIP_RANGE(5),
+ BIP_RANGE(0.5),
+ BIP_RANGE(0.05),
+ BIP_RANGE(0.01),
+ UNI_RANGE(10),
+ UNI_RANGE(1),
+ UNI_RANGE(0.1),
+ UNI_RANGE(0.01)
+ }
+}; /*
+ cio-das08jr.pdf
+
+ "das08/jr-ao"
+
+ 0 a/d bits 0-3 unused
+ 1 a/d bits 4-11 start 12 bit
+ 2 eoc, mux mux
+ 3 di do
+ 4 unused ao0_lsb
+ 5 unused ao0_msb
+ 6 unused ao1_lsb
+ 7 unused ao1_msb
+
+ */
+
+static const struct comedi_lrange *const das08_ai_lranges[] = {
+ &range_unknown,
+ &range_bipolar5,
+ &range_das08_pgh,
+ &range_das08_pgl,
+ &range_das08_pgm,
+};
+
+static const int das08_pgh_gainlist[] = {
+ 8, 0, 10, 2, 12, 4, 14, 6, 1, 3, 5, 7
+};
+static const int das08_pgl_gainlist[] = { 8, 0, 2, 4, 6, 1, 3, 5, 7 };
+static const int das08_pgm_gainlist[] = { 8, 0, 10, 12, 14, 9, 11, 13, 15 };
+
+static const int *const das08_gainlists[] = {
+ NULL,
+ NULL,
+ das08_pgh_gainlist,
+ das08_pgl_gainlist,
+ das08_pgm_gainlist,
+};
+
+static int das08_ai_eoc(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned long context)
+{
+ unsigned int status;
+
+ status = inb(dev->iobase + DAS08_STATUS);
+ if ((status & DAS08_EOC) == 0)
+ return 0;
+ return -EBUSY;
+}
+
+static int das08_ai_rinsn(struct comedi_device *dev, struct comedi_subdevice *s,
+ struct comedi_insn *insn, unsigned int *data)
+{
+ const struct das08_board_struct *thisboard = dev->board_ptr;
+ struct das08_private_struct *devpriv = dev->private;
+ int n;
+ int chan;
+ int range;
+ int lsb, msb;
+ int ret;
+
+ chan = CR_CHAN(insn->chanspec);
+ range = CR_RANGE(insn->chanspec);
+
+ /* clear crap */
+ inb(dev->iobase + DAS08_LSB);
+ inb(dev->iobase + DAS08_MSB);
+
+ /* set multiplexer */
+ /* lock to prevent race with digital output */
+ spin_lock(&dev->spinlock);
+ devpriv->do_mux_bits &= ~DAS08_MUX_MASK;
+ devpriv->do_mux_bits |= DAS08_MUX(chan);
+ outb(devpriv->do_mux_bits, dev->iobase + DAS08_CONTROL);
+ spin_unlock(&dev->spinlock);
+
+ if (s->range_table->length > 1) {
+ /* set gain/range */
+ range = CR_RANGE(insn->chanspec);
+ outb(devpriv->pg_gainlist[range],
+ dev->iobase + DAS08AO_GAIN_CONTROL);
+ }
+
+ for (n = 0; n < insn->n; n++) {
+ /* clear over-range bits for 16-bit boards */
+ if (thisboard->ai_nbits == 16)
+ if (inb(dev->iobase + DAS08_MSB) & 0x80)
+ dev_info(dev->class_dev, "over-range\n");
+
+ /* trigger conversion */
+ outb_p(0, dev->iobase + DAS08_TRIG_12BIT);
+
+ ret = comedi_timeout(dev, s, insn, das08_ai_eoc, 0);
+ if (ret)
+ return ret;
+
+ msb = inb(dev->iobase + DAS08_MSB);
+ lsb = inb(dev->iobase + DAS08_LSB);
+ if (thisboard->ai_encoding == das08_encode12) {
+ data[n] = (lsb >> 4) | (msb << 4);
+ } else if (thisboard->ai_encoding == das08_pcm_encode12) {
+ data[n] = (msb << 8) + lsb;
+ } else if (thisboard->ai_encoding == das08_encode16) {
+ /* FPOS 16-bit boards are sign-magnitude */
+ if (msb & 0x80)
+ data[n] = (1 << 15) | lsb | ((msb & 0x7f) << 8);
+ else
+ data[n] = (1 << 15) - (lsb | (msb & 0x7f) << 8);
+ } else {
+ dev_err(dev->class_dev, "bug! unknown ai encoding\n");
+ return -1;
+ }
+ }
+
+ return n;
+}
+
+static int das08_di_rbits(struct comedi_device *dev, struct comedi_subdevice *s,
+ struct comedi_insn *insn, unsigned int *data)
+{
+ data[0] = 0;
+ data[1] = DAS08_IP(inb(dev->iobase + DAS08_STATUS));
+
+ return insn->n;
+}
+
+static int das08_do_wbits(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ struct das08_private_struct *devpriv = dev->private;
+
+ if (comedi_dio_update_state(s, data)) {
+ /* prevent race with setting of analog input mux */
+ spin_lock(&dev->spinlock);
+ devpriv->do_mux_bits &= ~DAS08_DO_MASK;
+ devpriv->do_mux_bits |= DAS08_OP(s->state);
+ outb(devpriv->do_mux_bits, dev->iobase + DAS08_CONTROL);
+ spin_unlock(&dev->spinlock);
+ }
+
+ data[1] = s->state;
+
+ return insn->n;
+}
+
+static int das08jr_di_rbits(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn, unsigned int *data)
+{
+ data[0] = 0;
+ data[1] = inb(dev->iobase + DAS08JR_DIO);
+
+ return insn->n;
+}
+
+static int das08jr_do_wbits(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ if (comedi_dio_update_state(s, data))
+ outb(s->state, dev->iobase + DAS08JR_DIO);
+
+ data[1] = s->state;
+
+ return insn->n;
+}
+
+static void das08_ao_set_data(struct comedi_device *dev,
+ unsigned int chan, unsigned int data)
+{
+ const struct das08_board_struct *thisboard = dev->board_ptr;
+ unsigned char lsb;
+ unsigned char msb;
+
+ lsb = data & 0xff;
+ msb = (data >> 8) & 0xff;
+ if (thisboard->is_jr) {
+ outb(lsb, dev->iobase + DAS08JR_AO_LSB(chan));
+ outb(msb, dev->iobase + DAS08JR_AO_MSB(chan));
+ /* load DACs */
+ inb(dev->iobase + DAS08JR_DIO);
+ } else {
+ outb(lsb, dev->iobase + DAS08AO_AO_LSB(chan));
+ outb(msb, dev->iobase + DAS08AO_AO_MSB(chan));
+ /* load DACs */
+ inb(dev->iobase + DAS08AO_AO_UPDATE);
+ }
+}
+
+static int das08_ao_insn_write(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ unsigned int chan = CR_CHAN(insn->chanspec);
+ unsigned int val = s->readback[chan];
+ int i;
+
+ for (i = 0; i < insn->n; i++) {
+ val = data[i];
+ das08_ao_set_data(dev, chan, val);
+ }
+ s->readback[chan] = val;
+
+ return insn->n;
+}
+
+int das08_common_attach(struct comedi_device *dev, unsigned long iobase)
+{
+ const struct das08_board_struct *thisboard = dev->board_ptr;
+ struct das08_private_struct *devpriv = dev->private;
+ struct comedi_subdevice *s;
+ int ret;
+ int i;
+
+ dev->iobase = iobase;
+
+ dev->board_name = thisboard->name;
+
+ ret = comedi_alloc_subdevices(dev, 6);
+ if (ret)
+ return ret;
+
+ s = &dev->subdevices[0];
+ /* ai */
+ if (thisboard->ai_nbits) {
+ s->type = COMEDI_SUBD_AI;
+ /* XXX some boards actually have differential
+ * inputs instead of single ended.
+ * The driver does nothing with arefs though,
+ * so it's no big deal.
+ */
+ s->subdev_flags = SDF_READABLE | SDF_GROUND;
+ s->n_chan = 8;
+ s->maxdata = (1 << thisboard->ai_nbits) - 1;
+ s->range_table = das08_ai_lranges[thisboard->ai_pg];
+ s->insn_read = das08_ai_rinsn;
+ devpriv->pg_gainlist = das08_gainlists[thisboard->ai_pg];
+ } else {
+ s->type = COMEDI_SUBD_UNUSED;
+ }
+
+ s = &dev->subdevices[1];
+ /* ao */
+ if (thisboard->ao_nbits) {
+ s->type = COMEDI_SUBD_AO;
+ s->subdev_flags = SDF_WRITABLE;
+ s->n_chan = 2;
+ s->maxdata = (1 << thisboard->ao_nbits) - 1;
+ s->range_table = &range_bipolar5;
+ s->insn_write = das08_ao_insn_write;
+
+ ret = comedi_alloc_subdev_readback(s);
+ if (ret)
+ return ret;
+
+ /* initialize all channels to 0V */
+ for (i = 0; i < s->n_chan; i++) {
+ s->readback[i] = s->maxdata / 2;
+ das08_ao_set_data(dev, i, s->readback[i]);
+ }
+ } else {
+ s->type = COMEDI_SUBD_UNUSED;
+ }
+
+ s = &dev->subdevices[2];
+ /* di */
+ if (thisboard->di_nchan) {
+ s->type = COMEDI_SUBD_DI;
+ s->subdev_flags = SDF_READABLE;
+ s->n_chan = thisboard->di_nchan;
+ s->maxdata = 1;
+ s->range_table = &range_digital;
+ s->insn_bits =
+ thisboard->is_jr ? das08jr_di_rbits : das08_di_rbits;
+ } else {
+ s->type = COMEDI_SUBD_UNUSED;
+ }
+
+ s = &dev->subdevices[3];
+ /* do */
+ if (thisboard->do_nchan) {
+ s->type = COMEDI_SUBD_DO;
+ s->subdev_flags = SDF_WRITABLE;
+ s->n_chan = thisboard->do_nchan;
+ s->maxdata = 1;
+ s->range_table = &range_digital;
+ s->insn_bits =
+ thisboard->is_jr ? das08jr_do_wbits : das08_do_wbits;
+ } else {
+ s->type = COMEDI_SUBD_UNUSED;
+ }
+
+ s = &dev->subdevices[4];
+ /* 8255 */
+ if (thisboard->i8255_offset != 0) {
+ ret = subdev_8255_init(dev, s, NULL, thisboard->i8255_offset);
+ if (ret)
+ return ret;
+ } else {
+ s->type = COMEDI_SUBD_UNUSED;
+ }
+
+ /* Counter subdevice (8254) */
+ s = &dev->subdevices[5];
+ if (thisboard->i8254_offset) {
+ dev->pacer = comedi_8254_init(dev->iobase +
+ thisboard->i8254_offset,
+ 0, I8254_IO8, 0);
+ if (!dev->pacer)
+ return -ENOMEM;
+
+ comedi_8254_subdevice_init(s, dev->pacer);
+ } else {
+ s->type = COMEDI_SUBD_UNUSED;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(das08_common_attach);
+
+static int __init das08_init(void)
+{
+ return 0;
+}
+module_init(das08_init);
+
+static void __exit das08_exit(void)
+{
+}
+module_exit(das08_exit);
+
+MODULE_AUTHOR("Comedi http://www.comedi.org");
+MODULE_DESCRIPTION("Comedi low-level driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/comedi/drivers/das08.h b/drivers/staging/comedi/drivers/das08.h
new file mode 100644
index 000000000..f86167da5
--- /dev/null
+++ b/drivers/staging/comedi/drivers/das08.h
@@ -0,0 +1,51 @@
+/*
+ das08.h
+
+ Header for das08.c and das08_cs.c
+
+ Copyright (C) 2003 Frank Mori Hess <fmhess@users.sourceforge.net>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+*/
+
+#ifndef _DAS08_H
+#define _DAS08_H
+
+/* different ways ai data is encoded in first two registers */
+enum das08_ai_encoding { das08_encode12, das08_encode16, das08_pcm_encode12 };
+enum das08_lrange { das08_pg_none, das08_bipolar5, das08_pgh, das08_pgl,
+ das08_pgm
+};
+
+struct das08_board_struct {
+ const char *name;
+ bool is_jr; /* true for 'JR' boards */
+ unsigned int ai_nbits;
+ enum das08_lrange ai_pg;
+ enum das08_ai_encoding ai_encoding;
+ unsigned int ao_nbits;
+ unsigned int di_nchan;
+ unsigned int do_nchan;
+ unsigned int i8255_offset;
+ unsigned int i8254_offset;
+ unsigned int iosize; /* number of ioports used */
+};
+
+struct das08_private_struct {
+ unsigned int do_mux_bits; /* bits for do/mux register on boards
+ * without separate do register
+ */
+ const unsigned int *pg_gainlist;
+};
+
+int das08_common_attach(struct comedi_device *dev, unsigned long iobase);
+
+#endif /* _DAS08_H */
diff --git a/drivers/staging/comedi/drivers/das08_cs.c b/drivers/staging/comedi/drivers/das08_cs.c
new file mode 100644
index 000000000..93fab6890
--- /dev/null
+++ b/drivers/staging/comedi/drivers/das08_cs.c
@@ -0,0 +1,114 @@
+/*
+ comedi/drivers/das08_cs.c
+ DAS08 driver
+
+ COMEDI - Linux Control and Measurement Device Interface
+ Copyright (C) 2000 David A. Schleef <ds@schleef.org>
+ Copyright (C) 2001,2002,2003 Frank Mori Hess <fmhess@users.sourceforge.net>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ PCMCIA support code for this driver is adapted from the dummy_cs.c
+ driver of the Linux PCMCIA Card Services package.
+
+ The initial developer of the original code is David A. Hinds
+ <dahinds@users.sourceforge.net>. Portions created by David A. Hinds
+ are Copyright (C) 1999 David A. Hinds. All Rights Reserved.
+*/
+/*
+Driver: das08_cs
+Description: DAS-08 PCMCIA boards
+Author: Warren Jasper, ds, Frank Hess
+Devices: [ComputerBoards] PCM-DAS08 (pcm-das08)
+Status: works
+
+This is the PCMCIA-specific support split off from the
+das08 driver.
+
+Options (for pcm-das08):
+ NONE
+
+Command support does not exist, but could be added for this board.
+*/
+
+#include <linux/module.h>
+
+#include "../comedi_pcmcia.h"
+
+#include "das08.h"
+
+static const struct das08_board_struct das08_cs_boards[] = {
+ {
+ .name = "pcm-das08",
+ .ai_nbits = 12,
+ .ai_pg = das08_bipolar5,
+ .ai_encoding = das08_pcm_encode12,
+ .di_nchan = 3,
+ .do_nchan = 3,
+ .iosize = 16,
+ },
+};
+
+static int das08_cs_auto_attach(struct comedi_device *dev,
+ unsigned long context)
+{
+ struct pcmcia_device *link = comedi_to_pcmcia_dev(dev);
+ struct das08_private_struct *devpriv;
+ unsigned long iobase;
+ int ret;
+
+ /* The das08 driver needs the board_ptr */
+ dev->board_ptr = &das08_cs_boards[0];
+
+ link->config_flags |= CONF_AUTO_SET_IO;
+ ret = comedi_pcmcia_enable(dev, NULL);
+ if (ret)
+ return ret;
+ iobase = link->resource[0]->start;
+
+ devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
+ if (!devpriv)
+ return -ENOMEM;
+
+ return das08_common_attach(dev, iobase);
+}
+
+static struct comedi_driver driver_das08_cs = {
+ .driver_name = "das08_cs",
+ .module = THIS_MODULE,
+ .auto_attach = das08_cs_auto_attach,
+ .detach = comedi_pcmcia_disable,
+};
+
+static int das08_pcmcia_attach(struct pcmcia_device *link)
+{
+ return comedi_pcmcia_auto_config(link, &driver_das08_cs);
+}
+
+static const struct pcmcia_device_id das08_cs_id_table[] = {
+ PCMCIA_DEVICE_MANF_CARD(0x01c5, 0x4001),
+ PCMCIA_DEVICE_NULL
+};
+MODULE_DEVICE_TABLE(pcmcia, das08_cs_id_table);
+
+static struct pcmcia_driver das08_cs_driver = {
+ .name = "pcm-das08",
+ .owner = THIS_MODULE,
+ .id_table = das08_cs_id_table,
+ .probe = das08_pcmcia_attach,
+ .remove = comedi_pcmcia_auto_unconfig,
+};
+module_comedi_pcmcia_driver(driver_das08_cs, das08_cs_driver);
+
+MODULE_AUTHOR("David A. Schleef <ds@schleef.org>, "
+ "Frank Mori Hess <fmhess@users.sourceforge.net>");
+MODULE_DESCRIPTION("Comedi driver for ComputerBoards DAS-08 PCMCIA boards");
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/comedi/drivers/das08_isa.c b/drivers/staging/comedi/drivers/das08_isa.c
new file mode 100644
index 000000000..2d9a31dab
--- /dev/null
+++ b/drivers/staging/comedi/drivers/das08_isa.c
@@ -0,0 +1,199 @@
+/*
+ * das08_isa.c
+ * comedi driver for DAS08 ISA/PC-104 boards
+ *
+ * COMEDI - Linux Control and Measurement Device Interface
+ * Copyright (C) 2000 David A. Schleef <ds@schleef.org>
+ * Copyright (C) 2001,2002,2003 Frank Mori Hess <fmhess@users.sourceforge.net>
+ * Copyright (C) 2004 Salvador E. Tropea <set@users.sf.net> <set@ieee.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+/*
+ * Driver: das08_isa
+ * Description: DAS-08 ISA/PC-104 compatible boards
+ * Devices: [Keithley Metrabyte] DAS08 (isa-das08),
+ * [ComputerBoards] DAS08 (isa-das08), DAS08-PGM (das08-pgm),
+ * DAS08-PGH (das08-pgh), DAS08-PGL (das08-pgl), DAS08-AOH (das08-aoh),
+ * DAS08-AOL (das08-aol), DAS08-AOM (das08-aom), DAS08/JR-AO (das08/jr-ao),
+ * DAS08/JR-16-AO (das08jr-16-ao), PC104-DAS08 (pc104-das08),
+ * DAS08/JR/16 (das08jr/16)
+ * Author: Warren Jasper, ds, Frank Hess
+ * Updated: Fri, 31 Aug 2012 19:19:06 +0100
+ * Status: works
+ *
+ * This is the ISA/PC-104-specific support split off from the das08 driver.
+ *
+ * Configuration Options:
+ * [0] - base io address
+ */
+
+#include <linux/module.h>
+#include "../comedidev.h"
+
+#include "das08.h"
+
+static const struct das08_board_struct das08_isa_boards[] = {
+ {
+ /* cio-das08.pdf */
+ .name = "isa-das08",
+ .ai_nbits = 12,
+ .ai_pg = das08_pg_none,
+ .ai_encoding = das08_encode12,
+ .di_nchan = 3,
+ .do_nchan = 4,
+ .i8255_offset = 8,
+ .i8254_offset = 4,
+ .iosize = 16, /* unchecked */
+ }, {
+ /* cio-das08pgx.pdf */
+ .name = "das08-pgm",
+ .ai_nbits = 12,
+ .ai_pg = das08_pgm,
+ .ai_encoding = das08_encode12,
+ .di_nchan = 3,
+ .do_nchan = 4,
+ .i8255_offset = 0,
+ .i8254_offset = 0x04,
+ .iosize = 16, /* unchecked */
+ }, {
+ /* cio-das08pgx.pdf */
+ .name = "das08-pgh",
+ .ai_nbits = 12,
+ .ai_pg = das08_pgh,
+ .ai_encoding = das08_encode12,
+ .di_nchan = 3,
+ .do_nchan = 4,
+ .i8254_offset = 0x04,
+ .iosize = 16, /* unchecked */
+ }, {
+ /* cio-das08pgx.pdf */
+ .name = "das08-pgl",
+ .ai_nbits = 12,
+ .ai_pg = das08_pgl,
+ .ai_encoding = das08_encode12,
+ .di_nchan = 3,
+ .do_nchan = 4,
+ .i8254_offset = 0x04,
+ .iosize = 16, /* unchecked */
+ }, {
+ /* cio-das08_aox.pdf */
+ .name = "das08-aoh",
+ .ai_nbits = 12,
+ .ai_pg = das08_pgh,
+ .ai_encoding = das08_encode12,
+ .ao_nbits = 12,
+ .di_nchan = 3,
+ .do_nchan = 4,
+ .i8255_offset = 0x0c,
+ .i8254_offset = 0x04,
+ .iosize = 16, /* unchecked */
+ }, {
+ /* cio-das08_aox.pdf */
+ .name = "das08-aol",
+ .ai_nbits = 12,
+ .ai_pg = das08_pgl,
+ .ai_encoding = das08_encode12,
+ .ao_nbits = 12,
+ .di_nchan = 3,
+ .do_nchan = 4,
+ .i8255_offset = 0x0c,
+ .i8254_offset = 0x04,
+ .iosize = 16, /* unchecked */
+ }, {
+ /* cio-das08_aox.pdf */
+ .name = "das08-aom",
+ .ai_nbits = 12,
+ .ai_pg = das08_pgm,
+ .ai_encoding = das08_encode12,
+ .ao_nbits = 12,
+ .di_nchan = 3,
+ .do_nchan = 4,
+ .i8255_offset = 0x0c,
+ .i8254_offset = 0x04,
+ .iosize = 16, /* unchecked */
+ }, {
+ /* cio-das08-jr-ao.pdf */
+ .name = "das08/jr-ao",
+ .is_jr = true,
+ .ai_nbits = 12,
+ .ai_pg = das08_pg_none,
+ .ai_encoding = das08_encode12,
+ .ao_nbits = 12,
+ .di_nchan = 8,
+ .do_nchan = 8,
+ .iosize = 16, /* unchecked */
+ }, {
+ /* cio-das08jr-16-ao.pdf */
+ .name = "das08jr-16-ao",
+ .is_jr = true,
+ .ai_nbits = 16,
+ .ai_pg = das08_pg_none,
+ .ai_encoding = das08_encode16,
+ .ao_nbits = 16,
+ .di_nchan = 8,
+ .do_nchan = 8,
+ .i8254_offset = 0x04,
+ .iosize = 16, /* unchecked */
+ }, {
+ .name = "pc104-das08",
+ .ai_nbits = 12,
+ .ai_pg = das08_pg_none,
+ .ai_encoding = das08_encode12,
+ .di_nchan = 3,
+ .do_nchan = 4,
+ .i8254_offset = 4,
+ .iosize = 16, /* unchecked */
+ }, {
+ .name = "das08jr/16",
+ .is_jr = true,
+ .ai_nbits = 16,
+ .ai_pg = das08_pg_none,
+ .ai_encoding = das08_encode16,
+ .di_nchan = 8,
+ .do_nchan = 8,
+ .iosize = 16, /* unchecked */
+ },
+};
+
+static int das08_isa_attach(struct comedi_device *dev,
+ struct comedi_devconfig *it)
+{
+ const struct das08_board_struct *thisboard = dev->board_ptr;
+ struct das08_private_struct *devpriv;
+ int ret;
+
+ devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
+ if (!devpriv)
+ return -ENOMEM;
+
+ ret = comedi_request_region(dev, it->options[0], thisboard->iosize);
+ if (ret)
+ return ret;
+
+ return das08_common_attach(dev, dev->iobase);
+}
+
+static struct comedi_driver das08_isa_driver = {
+ .driver_name = "isa-das08",
+ .module = THIS_MODULE,
+ .attach = das08_isa_attach,
+ .detach = comedi_legacy_detach,
+ .board_name = &das08_isa_boards[0].name,
+ .num_names = ARRAY_SIZE(das08_isa_boards),
+ .offset = sizeof(das08_isa_boards[0]),
+};
+module_comedi_driver(das08_isa_driver);
+
+MODULE_AUTHOR("Comedi http://www.comedi.org");
+MODULE_DESCRIPTION("Comedi low-level driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/comedi/drivers/das08_pci.c b/drivers/staging/comedi/drivers/das08_pci.c
new file mode 100644
index 000000000..d8d27fa44
--- /dev/null
+++ b/drivers/staging/comedi/drivers/das08_pci.c
@@ -0,0 +1,105 @@
+/*
+ * das08_pci.c
+ * comedi driver for DAS08 PCI boards
+ *
+ * COMEDI - Linux Control and Measurement Device Interface
+ * Copyright (C) 2000 David A. Schleef <ds@schleef.org>
+ * Copyright (C) 2001,2002,2003 Frank Mori Hess <fmhess@users.sourceforge.net>
+ * Copyright (C) 2004 Salvador E. Tropea <set@users.sf.net> <set@ieee.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+/*
+ * Driver: das08_pci
+ * Description: DAS-08 PCI compatible boards
+ * Devices: [ComputerBoards] PCI-DAS08 (pci-das08)
+ * Author: Warren Jasper, ds, Frank Hess
+ * Updated: Fri, 31 Aug 2012 19:19:06 +0100
+ * Status: works
+ *
+ * This is the PCI-specific support split off from the das08 driver.
+ *
+ * Configuration Options: not applicable, uses PCI auto config
+ */
+
+#include <linux/module.h>
+
+#include "../comedi_pci.h"
+
+#include "das08.h"
+
+static const struct das08_board_struct das08_pci_boards[] = {
+ {
+ .name = "pci-das08",
+ .ai_nbits = 12,
+ .ai_pg = das08_bipolar5,
+ .ai_encoding = das08_encode12,
+ .di_nchan = 3,
+ .do_nchan = 4,
+ .i8254_offset = 4,
+ .iosize = 8,
+ },
+};
+
+static int das08_pci_auto_attach(struct comedi_device *dev,
+ unsigned long context_unused)
+{
+ struct pci_dev *pdev = comedi_to_pci_dev(dev);
+ struct das08_private_struct *devpriv;
+ int ret;
+
+ devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
+ if (!devpriv)
+ return -ENOMEM;
+
+ /* The das08 driver needs the board_ptr */
+ dev->board_ptr = &das08_pci_boards[0];
+
+ ret = comedi_pci_enable(dev);
+ if (ret)
+ return ret;
+ dev->iobase = pci_resource_start(pdev, 2);
+
+ return das08_common_attach(dev, dev->iobase);
+}
+
+static struct comedi_driver das08_pci_comedi_driver = {
+ .driver_name = "pci-das08",
+ .module = THIS_MODULE,
+ .auto_attach = das08_pci_auto_attach,
+ .detach = comedi_pci_detach,
+};
+
+static int das08_pci_probe(struct pci_dev *dev,
+ const struct pci_device_id *id)
+{
+ return comedi_pci_auto_config(dev, &das08_pci_comedi_driver,
+ id->driver_data);
+}
+
+static const struct pci_device_id das08_pci_table[] = {
+ { PCI_DEVICE(PCI_VENDOR_ID_CB, 0x0029) },
+ { 0 }
+};
+MODULE_DEVICE_TABLE(pci, das08_pci_table);
+
+static struct pci_driver das08_pci_driver = {
+ .name = "pci-das08",
+ .id_table = das08_pci_table,
+ .probe = das08_pci_probe,
+ .remove = comedi_pci_auto_unconfig,
+};
+module_comedi_pci_driver(das08_pci_comedi_driver, das08_pci_driver);
+
+MODULE_AUTHOR("Comedi http://www.comedi.org");
+MODULE_DESCRIPTION("Comedi low-level driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/comedi/drivers/das16.c b/drivers/staging/comedi/drivers/das16.c
new file mode 100644
index 000000000..d7cf4b153
--- /dev/null
+++ b/drivers/staging/comedi/drivers/das16.c
@@ -0,0 +1,1207 @@
+/*
+ * das16.c
+ * DAS16 driver
+ *
+ * COMEDI - Linux Control and Measurement Device Interface
+ * Copyright (C) 2000 David A. Schleef <ds@schleef.org>
+ * Copyright (C) 2000 Chris R. Baugher <baugher@enteract.com>
+ * Copyright (C) 2001,2002 Frank Mori Hess <fmhess@users.sourceforge.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+/*
+ * Driver: das16
+ * Description: DAS16 compatible boards
+ * Author: Sam Moore, Warren Jasper, ds, Chris Baugher, Frank Hess, Roman Fietze
+ * Devices: [Keithley Metrabyte] DAS-16 (das-16), DAS-16G (das-16g),
+ * DAS-16F (das-16f), DAS-1201 (das-1201), DAS-1202 (das-1202),
+ * DAS-1401 (das-1401), DAS-1402 (das-1402), DAS-1601 (das-1601),
+ * DAS-1602 (das-1602),
+ * [ComputerBoards] PC104-DAS16/JR (pc104-das16jr),
+ * PC104-DAS16JR/16 (pc104-das16jr/16), CIO-DAS16 (cio-das16),
+ * CIO-DAS16F (cio-das16/f), CIO-DAS16/JR (cio-das16/jr),
+ * CIO-DAS16JR/16 (cio-das16jr/16), CIO-DAS1401/12 (cio-das1401/12),
+ * CIO-DAS1402/12 (cio-das1402/12), CIO-DAS1402/16 (cio-das1402/16),
+ * CIO-DAS1601/12 (cio-das1601/12), CIO-DAS1602/12 (cio-das1602/12),
+ * CIO-DAS1602/16 (cio-das1602/16), CIO-DAS16/330 (cio-das16/330)
+ * Status: works
+ * Updated: 2003-10-12
+ *
+ * A rewrite of the das16 and das1600 drivers.
+ *
+ * Options:
+ * [0] - base io address
+ * [1] - irq (does nothing, irq is not used anymore)
+ * [2] - dma channel (optional, required for comedi_command support)
+ * [3] - master clock speed in MHz (optional, 1 or 10, ignored if
+ * board can probe clock, defaults to 1)
+ * [4] - analog input range lowest voltage in microvolts (optional,
+ * only useful if your board does not have software
+ * programmable gain)
+ * [5] - analog input range highest voltage in microvolts (optional,
+ * only useful if board does not have software programmable
+ * gain)
+ * [6] - analog output range lowest voltage in microvolts (optional)
+ * [7] - analog output range highest voltage in microvolts (optional)
+ *
+ * Passing a zero for an option is the same as leaving it unspecified.
+ */
+
+/*
+ * Testing and debugging help provided by Daniel Koch.
+ *
+ * Keithley Manuals:
+ * 2309.PDF (das16)
+ * 4919.PDF (das1400, 1600)
+ * 4922.PDF (das-1400)
+ * 4923.PDF (das1200, 1400, 1600)
+ *
+ * Computer boards manuals also available from their website
+ * www.measurementcomputing.com
+ */
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+
+#include "../comedidev.h"
+
+#include "comedi_isadma.h"
+#include "comedi_8254.h"
+#include "8255.h"
+
+#define DAS16_DMA_SIZE 0xff00 /* size in bytes of allocated dma buffer */
+
+/*
+ * Register I/O map
+ */
+#define DAS16_TRIG_REG 0x00
+#define DAS16_AI_LSB_REG 0x00
+#define DAS16_AI_MSB_REG 0x01
+#define DAS16_MUX_REG 0x02
+#define DAS16_DIO_REG 0x03
+#define DAS16_AO_LSB_REG(x) ((x) ? 0x06 : 0x04)
+#define DAS16_AO_MSB_REG(x) ((x) ? 0x07 : 0x05)
+#define DAS16_STATUS_REG 0x08
+#define DAS16_STATUS_BUSY (1 << 7)
+#define DAS16_STATUS_UNIPOLAR (1 << 6)
+#define DAS16_STATUS_MUXBIT (1 << 5)
+#define DAS16_STATUS_INT (1 << 4)
+#define DAS16_CTRL_REG 0x09
+#define DAS16_CTRL_INTE (1 << 7)
+#define DAS16_CTRL_IRQ(x) (((x) & 0x7) << 4)
+#define DAS16_CTRL_DMAE (1 << 2)
+#define DAS16_CTRL_PACING_MASK (3 << 0)
+#define DAS16_CTRL_INT_PACER (3 << 0)
+#define DAS16_CTRL_EXT_PACER (2 << 0)
+#define DAS16_CTRL_SOFT_PACER (0 << 0)
+#define DAS16_PACER_REG 0x0a
+#define DAS16_PACER_BURST_LEN(x) (((x) & 0xf) << 4)
+#define DAS16_PACER_CTR0 (1 << 1)
+#define DAS16_PACER_TRIG0 (1 << 0)
+#define DAS16_GAIN_REG 0x0b
+#define DAS16_TIMER_BASE_REG 0x0c /* to 0x0f */
+
+#define DAS1600_CONV_REG 0x404
+#define DAS1600_CONV_DISABLE (1 << 6)
+#define DAS1600_BURST_REG 0x405
+#define DAS1600_BURST_VAL (1 << 6)
+#define DAS1600_ENABLE_REG 0x406
+#define DAS1600_ENABLE_VAL (1 << 6)
+#define DAS1600_STATUS_REG 0x407
+#define DAS1600_STATUS_BME (1 << 6)
+#define DAS1600_STATUS_ME (1 << 5)
+#define DAS1600_STATUS_CD (1 << 4)
+#define DAS1600_STATUS_WS (1 << 1)
+#define DAS1600_STATUS_CLK_10MHZ (1 << 0)
+
+static const struct comedi_lrange range_das1x01_bip = {
+ 4, {
+ BIP_RANGE(10),
+ BIP_RANGE(1),
+ BIP_RANGE(0.1),
+ BIP_RANGE(0.01)
+ }
+};
+
+static const struct comedi_lrange range_das1x01_unip = {
+ 4, {
+ UNI_RANGE(10),
+ UNI_RANGE(1),
+ UNI_RANGE(0.1),
+ UNI_RANGE(0.01)
+ }
+};
+
+static const struct comedi_lrange range_das1x02_bip = {
+ 4, {
+ BIP_RANGE(10),
+ BIP_RANGE(5),
+ BIP_RANGE(2.5),
+ BIP_RANGE(1.25)
+ }
+};
+
+static const struct comedi_lrange range_das1x02_unip = {
+ 4, {
+ UNI_RANGE(10),
+ UNI_RANGE(5),
+ UNI_RANGE(2.5),
+ UNI_RANGE(1.25)
+ }
+};
+
+static const struct comedi_lrange range_das16jr = {
+ 9, {
+ BIP_RANGE(10),
+ BIP_RANGE(5),
+ BIP_RANGE(2.5),
+ BIP_RANGE(1.25),
+ BIP_RANGE(0.625),
+ UNI_RANGE(10),
+ UNI_RANGE(5),
+ UNI_RANGE(2.5),
+ UNI_RANGE(1.25)
+ }
+};
+
+static const struct comedi_lrange range_das16jr_16 = {
+ 8, {
+ BIP_RANGE(10),
+ BIP_RANGE(5),
+ BIP_RANGE(2.5),
+ BIP_RANGE(1.25),
+ UNI_RANGE(10),
+ UNI_RANGE(5),
+ UNI_RANGE(2.5),
+ UNI_RANGE(1.25)
+ }
+};
+
+static const int das16jr_gainlist[] = { 8, 0, 1, 2, 3, 4, 5, 6, 7 };
+static const int das16jr_16_gainlist[] = { 0, 1, 2, 3, 4, 5, 6, 7 };
+static const int das1600_gainlist[] = { 0, 1, 2, 3 };
+
+enum {
+ das16_pg_none = 0,
+ das16_pg_16jr,
+ das16_pg_16jr_16,
+ das16_pg_1601,
+ das16_pg_1602,
+};
+static const int *const das16_gainlists[] = {
+ NULL,
+ das16jr_gainlist,
+ das16jr_16_gainlist,
+ das1600_gainlist,
+ das1600_gainlist,
+};
+
+static const struct comedi_lrange *const das16_ai_uni_lranges[] = {
+ &range_unknown,
+ &range_das16jr,
+ &range_das16jr_16,
+ &range_das1x01_unip,
+ &range_das1x02_unip,
+};
+
+static const struct comedi_lrange *const das16_ai_bip_lranges[] = {
+ &range_unknown,
+ &range_das16jr,
+ &range_das16jr_16,
+ &range_das1x01_bip,
+ &range_das1x02_bip,
+};
+
+struct das16_board {
+ const char *name;
+ unsigned int ai_maxdata;
+ unsigned int ai_speed; /* max conversion speed in nanosec */
+ unsigned int ai_pg;
+ unsigned int has_ao:1;
+ unsigned int has_8255:1;
+
+ unsigned int i8255_offset;
+
+ unsigned int size;
+ unsigned int id;
+};
+
+static const struct das16_board das16_boards[] = {
+ {
+ .name = "das-16",
+ .ai_maxdata = 0x0fff,
+ .ai_speed = 15000,
+ .ai_pg = das16_pg_none,
+ .has_ao = 1,
+ .has_8255 = 1,
+ .i8255_offset = 0x10,
+ .size = 0x14,
+ .id = 0x00,
+ }, {
+ .name = "das-16g",
+ .ai_maxdata = 0x0fff,
+ .ai_speed = 15000,
+ .ai_pg = das16_pg_none,
+ .has_ao = 1,
+ .has_8255 = 1,
+ .i8255_offset = 0x10,
+ .size = 0x14,
+ .id = 0x00,
+ }, {
+ .name = "das-16f",
+ .ai_maxdata = 0x0fff,
+ .ai_speed = 8500,
+ .ai_pg = das16_pg_none,
+ .has_ao = 1,
+ .has_8255 = 1,
+ .i8255_offset = 0x10,
+ .size = 0x14,
+ .id = 0x00,
+ }, {
+ .name = "cio-das16",
+ .ai_maxdata = 0x0fff,
+ .ai_speed = 20000,
+ .ai_pg = das16_pg_none,
+ .has_ao = 1,
+ .has_8255 = 1,
+ .i8255_offset = 0x10,
+ .size = 0x14,
+ .id = 0x80,
+ }, {
+ .name = "cio-das16/f",
+ .ai_maxdata = 0x0fff,
+ .ai_speed = 10000,
+ .ai_pg = das16_pg_none,
+ .has_ao = 1,
+ .has_8255 = 1,
+ .i8255_offset = 0x10,
+ .size = 0x14,
+ .id = 0x80,
+ }, {
+ .name = "cio-das16/jr",
+ .ai_maxdata = 0x0fff,
+ .ai_speed = 7692,
+ .ai_pg = das16_pg_16jr,
+ .size = 0x10,
+ .id = 0x00,
+ }, {
+ .name = "pc104-das16jr",
+ .ai_maxdata = 0x0fff,
+ .ai_speed = 3300,
+ .ai_pg = das16_pg_16jr,
+ .size = 0x10,
+ .id = 0x00,
+ }, {
+ .name = "cio-das16jr/16",
+ .ai_maxdata = 0xffff,
+ .ai_speed = 10000,
+ .ai_pg = das16_pg_16jr_16,
+ .size = 0x10,
+ .id = 0x00,
+ }, {
+ .name = "pc104-das16jr/16",
+ .ai_maxdata = 0xffff,
+ .ai_speed = 10000,
+ .ai_pg = das16_pg_16jr_16,
+ .size = 0x10,
+ .id = 0x00,
+ }, {
+ .name = "das-1201",
+ .ai_maxdata = 0x0fff,
+ .ai_speed = 20000,
+ .ai_pg = das16_pg_none,
+ .has_8255 = 1,
+ .i8255_offset = 0x400,
+ .size = 0x408,
+ .id = 0x20,
+ }, {
+ .name = "das-1202",
+ .ai_maxdata = 0x0fff,
+ .ai_speed = 10000,
+ .ai_pg = das16_pg_none,
+ .has_8255 = 1,
+ .i8255_offset = 0x400,
+ .size = 0x408,
+ .id = 0x20,
+ }, {
+ .name = "das-1401",
+ .ai_maxdata = 0x0fff,
+ .ai_speed = 10000,
+ .ai_pg = das16_pg_1601,
+ .size = 0x408,
+ .id = 0xc0,
+ }, {
+ .name = "das-1402",
+ .ai_maxdata = 0x0fff,
+ .ai_speed = 10000,
+ .ai_pg = das16_pg_1602,
+ .size = 0x408,
+ .id = 0xc0,
+ }, {
+ .name = "das-1601",
+ .ai_maxdata = 0x0fff,
+ .ai_speed = 10000,
+ .ai_pg = das16_pg_1601,
+ .has_ao = 1,
+ .has_8255 = 1,
+ .i8255_offset = 0x400,
+ .size = 0x408,
+ .id = 0xc0,
+ }, {
+ .name = "das-1602",
+ .ai_maxdata = 0x0fff,
+ .ai_speed = 10000,
+ .ai_pg = das16_pg_1602,
+ .has_ao = 1,
+ .has_8255 = 1,
+ .i8255_offset = 0x400,
+ .size = 0x408,
+ .id = 0xc0,
+ }, {
+ .name = "cio-das1401/12",
+ .ai_maxdata = 0x0fff,
+ .ai_speed = 6250,
+ .ai_pg = das16_pg_1601,
+ .size = 0x408,
+ .id = 0xc0,
+ }, {
+ .name = "cio-das1402/12",
+ .ai_maxdata = 0x0fff,
+ .ai_speed = 6250,
+ .ai_pg = das16_pg_1602,
+ .size = 0x408,
+ .id = 0xc0,
+ }, {
+ .name = "cio-das1402/16",
+ .ai_maxdata = 0xffff,
+ .ai_speed = 10000,
+ .ai_pg = das16_pg_1602,
+ .size = 0x408,
+ .id = 0xc0,
+ }, {
+ .name = "cio-das1601/12",
+ .ai_maxdata = 0x0fff,
+ .ai_speed = 6250,
+ .ai_pg = das16_pg_1601,
+ .has_ao = 1,
+ .has_8255 = 1,
+ .i8255_offset = 0x400,
+ .size = 0x408,
+ .id = 0xc0,
+ }, {
+ .name = "cio-das1602/12",
+ .ai_maxdata = 0x0fff,
+ .ai_speed = 10000,
+ .ai_pg = das16_pg_1602,
+ .has_ao = 1,
+ .has_8255 = 1,
+ .i8255_offset = 0x400,
+ .size = 0x408,
+ .id = 0xc0,
+ }, {
+ .name = "cio-das1602/16",
+ .ai_maxdata = 0xffff,
+ .ai_speed = 10000,
+ .ai_pg = das16_pg_1602,
+ .has_ao = 1,
+ .has_8255 = 1,
+ .i8255_offset = 0x400,
+ .size = 0x408,
+ .id = 0xc0,
+ }, {
+ .name = "cio-das16/330",
+ .ai_maxdata = 0x0fff,
+ .ai_speed = 3030,
+ .ai_pg = das16_pg_16jr,
+ .size = 0x14,
+ .id = 0xf0,
+ },
+};
+
+/* Period for timer interrupt in jiffies. It's a function
+ * to deal with possibility of dynamic HZ patches */
+static inline int timer_period(void)
+{
+ return HZ / 20;
+}
+
+struct das16_private_struct {
+ struct comedi_isadma *dma;
+ unsigned int clockbase;
+ unsigned int ctrl_reg;
+ unsigned int divisor1;
+ unsigned int divisor2;
+ struct timer_list timer;
+ unsigned long extra_iobase;
+ unsigned int can_burst:1;
+ unsigned int timer_running:1;
+};
+
+static void das16_ai_setup_dma(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ unsigned int unread_samples)
+{
+ struct das16_private_struct *devpriv = dev->private;
+ struct comedi_isadma *dma = devpriv->dma;
+ struct comedi_isadma_desc *desc = &dma->desc[dma->cur_dma];
+ unsigned int max_samples = comedi_bytes_to_samples(s, desc->maxsize);
+ unsigned int nsamples;
+
+ /*
+ * Determine dma size based on the buffer size plus the number of
+ * unread samples and the number of samples remaining in the command.
+ */
+ nsamples = comedi_nsamples_left(s, max_samples + unread_samples);
+ if (nsamples > unread_samples) {
+ nsamples -= unread_samples;
+ desc->size = comedi_samples_to_bytes(s, nsamples);
+ comedi_isadma_program(desc);
+ }
+}
+
+static void das16_interrupt(struct comedi_device *dev)
+{
+ struct das16_private_struct *devpriv = dev->private;
+ struct comedi_subdevice *s = dev->read_subdev;
+ struct comedi_async *async = s->async;
+ struct comedi_cmd *cmd = &async->cmd;
+ struct comedi_isadma *dma = devpriv->dma;
+ struct comedi_isadma_desc *desc = &dma->desc[dma->cur_dma];
+ unsigned long spin_flags;
+ unsigned int residue;
+ unsigned int nbytes;
+ unsigned int nsamples;
+
+ spin_lock_irqsave(&dev->spinlock, spin_flags);
+ if (!(devpriv->ctrl_reg & DAS16_CTRL_DMAE)) {
+ spin_unlock_irqrestore(&dev->spinlock, spin_flags);
+ return;
+ }
+
+ /*
+ * The pc104-das16jr (at least) has problems if the dma
+ * transfer is interrupted in the middle of transferring
+ * a 16 bit sample.
+ */
+ residue = comedi_isadma_disable_on_sample(desc->chan,
+ comedi_bytes_per_sample(s));
+
+ /* figure out how many samples to read */
+ if (residue > desc->size) {
+ dev_err(dev->class_dev, "residue > transfer size!\n");
+ async->events |= COMEDI_CB_ERROR;
+ nbytes = 0;
+ } else {
+ nbytes = desc->size - residue;
+ }
+ nsamples = comedi_bytes_to_samples(s, nbytes);
+
+ /* restart DMA if more samples are needed */
+ if (nsamples) {
+ dma->cur_dma = 1 - dma->cur_dma;
+ das16_ai_setup_dma(dev, s, nsamples);
+ }
+
+ spin_unlock_irqrestore(&dev->spinlock, spin_flags);
+
+ comedi_buf_write_samples(s, desc->virt_addr, nsamples);
+
+ if (cmd->stop_src == TRIG_COUNT && async->scans_done >= cmd->stop_arg)
+ async->events |= COMEDI_CB_EOA;
+
+ comedi_handle_events(dev, s);
+}
+
+static void das16_timer_interrupt(unsigned long arg)
+{
+ struct comedi_device *dev = (struct comedi_device *)arg;
+ struct das16_private_struct *devpriv = dev->private;
+ unsigned long flags;
+
+ das16_interrupt(dev);
+
+ spin_lock_irqsave(&dev->spinlock, flags);
+ if (devpriv->timer_running)
+ mod_timer(&devpriv->timer, jiffies + timer_period());
+ spin_unlock_irqrestore(&dev->spinlock, flags);
+}
+
+static void das16_ai_set_mux_range(struct comedi_device *dev,
+ unsigned int first_chan,
+ unsigned int last_chan,
+ unsigned int range)
+{
+ const struct das16_board *board = dev->board_ptr;
+
+ /* set multiplexer */
+ outb(first_chan | (last_chan << 4), dev->iobase + DAS16_MUX_REG);
+
+ /* some boards do not have programmable gain */
+ if (board->ai_pg == das16_pg_none)
+ return;
+
+ /*
+ * Set gain (this is also burst rate register but according to
+ * computer boards manual, burst rate does nothing, even on
+ * keithley cards).
+ */
+ outb((das16_gainlists[board->ai_pg])[range],
+ dev->iobase + DAS16_GAIN_REG);
+}
+
+static int das16_ai_check_chanlist(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_cmd *cmd)
+{
+ unsigned int chan0 = CR_CHAN(cmd->chanlist[0]);
+ unsigned int range0 = CR_RANGE(cmd->chanlist[0]);
+ int i;
+
+ for (i = 1; i < cmd->chanlist_len; i++) {
+ unsigned int chan = CR_CHAN(cmd->chanlist[i]);
+ unsigned int range = CR_RANGE(cmd->chanlist[i]);
+
+ if (chan != ((chan0 + i) % s->n_chan)) {
+ dev_dbg(dev->class_dev,
+ "entries in chanlist must be consecutive channels, counting upwards\n");
+ return -EINVAL;
+ }
+
+ if (range != range0) {
+ dev_dbg(dev->class_dev,
+ "entries in chanlist must all have the same gain\n");
+ return -EINVAL;
+ }
+ }
+
+ return 0;
+}
+
+static int das16_cmd_test(struct comedi_device *dev, struct comedi_subdevice *s,
+ struct comedi_cmd *cmd)
+{
+ const struct das16_board *board = dev->board_ptr;
+ struct das16_private_struct *devpriv = dev->private;
+ int err = 0;
+ unsigned int trig_mask;
+ unsigned int arg;
+
+ /* Step 1 : check if triggers are trivially valid */
+
+ err |= comedi_check_trigger_src(&cmd->start_src, TRIG_NOW);
+
+ trig_mask = TRIG_FOLLOW;
+ if (devpriv->can_burst)
+ trig_mask |= TRIG_TIMER | TRIG_EXT;
+ err |= comedi_check_trigger_src(&cmd->scan_begin_src, trig_mask);
+
+ trig_mask = TRIG_TIMER | TRIG_EXT;
+ if (devpriv->can_burst)
+ trig_mask |= TRIG_NOW;
+ err |= comedi_check_trigger_src(&cmd->convert_src, trig_mask);
+
+ err |= comedi_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
+ err |= comedi_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE);
+
+ if (err)
+ return 1;
+
+ /* Step 2a : make sure trigger sources are unique */
+
+ err |= comedi_check_trigger_is_unique(cmd->scan_begin_src);
+ err |= comedi_check_trigger_is_unique(cmd->convert_src);
+ err |= comedi_check_trigger_is_unique(cmd->stop_src);
+
+ /* Step 2b : and mutually compatible */
+
+ /* make sure scan_begin_src and convert_src dont conflict */
+ if (cmd->scan_begin_src == TRIG_FOLLOW && cmd->convert_src == TRIG_NOW)
+ err |= -EINVAL;
+ if (cmd->scan_begin_src != TRIG_FOLLOW && cmd->convert_src != TRIG_NOW)
+ err |= -EINVAL;
+
+ if (err)
+ return 2;
+
+ /* Step 3: check if arguments are trivially valid */
+
+ err |= comedi_check_trigger_arg_is(&cmd->start_arg, 0);
+
+ if (cmd->scan_begin_src == TRIG_FOLLOW) /* internal trigger */
+ err |= comedi_check_trigger_arg_is(&cmd->scan_begin_arg, 0);
+
+ err |= comedi_check_trigger_arg_is(&cmd->scan_end_arg,
+ cmd->chanlist_len);
+
+ /* check against maximum frequency */
+ if (cmd->scan_begin_src == TRIG_TIMER) {
+ err |= comedi_check_trigger_arg_min(&cmd->scan_begin_arg,
+ board->ai_speed *
+ cmd->chanlist_len);
+ }
+
+ if (cmd->convert_src == TRIG_TIMER) {
+ err |= comedi_check_trigger_arg_min(&cmd->convert_arg,
+ board->ai_speed);
+ }
+
+ if (cmd->stop_src == TRIG_COUNT)
+ err |= comedi_check_trigger_arg_min(&cmd->stop_arg, 1);
+ else /* TRIG_NONE */
+ err |= comedi_check_trigger_arg_is(&cmd->stop_arg, 0);
+
+ if (err)
+ return 3;
+
+ /* step 4: fix up arguments */
+ if (cmd->scan_begin_src == TRIG_TIMER) {
+ arg = cmd->scan_begin_arg;
+ comedi_8254_cascade_ns_to_timer(dev->pacer, &arg, cmd->flags);
+ err |= comedi_check_trigger_arg_is(&cmd->scan_begin_arg, arg);
+ }
+ if (cmd->convert_src == TRIG_TIMER) {
+ arg = cmd->convert_arg;
+ comedi_8254_cascade_ns_to_timer(dev->pacer, &arg, cmd->flags);
+ err |= comedi_check_trigger_arg_is(&cmd->convert_arg, arg);
+ }
+ if (err)
+ return 4;
+
+ /* Step 5: check channel list if it exists */
+ if (cmd->chanlist && cmd->chanlist_len > 0)
+ err |= das16_ai_check_chanlist(dev, s, cmd);
+
+ if (err)
+ return 5;
+
+ return 0;
+}
+
+static unsigned int das16_set_pacer(struct comedi_device *dev, unsigned int ns,
+ unsigned int flags)
+{
+ comedi_8254_cascade_ns_to_timer(dev->pacer, &ns, flags);
+ comedi_8254_update_divisors(dev->pacer);
+ comedi_8254_pacer_enable(dev->pacer, 1, 2, true);
+
+ return ns;
+}
+
+static int das16_cmd_exec(struct comedi_device *dev, struct comedi_subdevice *s)
+{
+ struct das16_private_struct *devpriv = dev->private;
+ struct comedi_isadma *dma = devpriv->dma;
+ struct comedi_async *async = s->async;
+ struct comedi_cmd *cmd = &async->cmd;
+ unsigned int first_chan = CR_CHAN(cmd->chanlist[0]);
+ unsigned int last_chan = CR_CHAN(cmd->chanlist[cmd->chanlist_len - 1]);
+ unsigned int range = CR_RANGE(cmd->chanlist[0]);
+ unsigned int byte;
+ unsigned long flags;
+
+ if (cmd->flags & CMDF_PRIORITY) {
+ dev_err(dev->class_dev,
+ "isa dma transfers cannot be performed with CMDF_PRIORITY, aborting\n");
+ return -1;
+ }
+
+ if (devpriv->can_burst)
+ outb(DAS1600_CONV_DISABLE, dev->iobase + DAS1600_CONV_REG);
+
+ /* set mux and range for chanlist scan */
+ das16_ai_set_mux_range(dev, first_chan, last_chan, range);
+
+ /* set counter mode and counts */
+ cmd->convert_arg = das16_set_pacer(dev, cmd->convert_arg, cmd->flags);
+
+ /* enable counters */
+ byte = 0;
+ if (devpriv->can_burst) {
+ if (cmd->convert_src == TRIG_NOW) {
+ outb(DAS1600_BURST_VAL,
+ dev->iobase + DAS1600_BURST_REG);
+ /* set burst length */
+ byte |= DAS16_PACER_BURST_LEN(cmd->chanlist_len - 1);
+ } else {
+ outb(0, dev->iobase + DAS1600_BURST_REG);
+ }
+ }
+ outb(byte, dev->iobase + DAS16_PACER_REG);
+
+ /* set up dma transfer */
+ dma->cur_dma = 0;
+ das16_ai_setup_dma(dev, s, 0);
+
+ /* set up timer */
+ spin_lock_irqsave(&dev->spinlock, flags);
+ devpriv->timer_running = 1;
+ devpriv->timer.expires = jiffies + timer_period();
+ add_timer(&devpriv->timer);
+
+ /* enable DMA interrupt with external or internal pacing */
+ devpriv->ctrl_reg &= ~(DAS16_CTRL_INTE | DAS16_CTRL_PACING_MASK);
+ devpriv->ctrl_reg |= DAS16_CTRL_DMAE;
+ if (cmd->convert_src == TRIG_EXT)
+ devpriv->ctrl_reg |= DAS16_CTRL_EXT_PACER;
+ else
+ devpriv->ctrl_reg |= DAS16_CTRL_INT_PACER;
+ outb(devpriv->ctrl_reg, dev->iobase + DAS16_CTRL_REG);
+
+ if (devpriv->can_burst)
+ outb(0, dev->iobase + DAS1600_CONV_REG);
+ spin_unlock_irqrestore(&dev->spinlock, flags);
+
+ return 0;
+}
+
+static int das16_cancel(struct comedi_device *dev, struct comedi_subdevice *s)
+{
+ struct das16_private_struct *devpriv = dev->private;
+ struct comedi_isadma *dma = devpriv->dma;
+ unsigned long flags;
+
+ spin_lock_irqsave(&dev->spinlock, flags);
+
+ /* disable interrupts, dma and pacer clocked conversions */
+ devpriv->ctrl_reg &= ~(DAS16_CTRL_INTE | DAS16_CTRL_DMAE |
+ DAS16_CTRL_PACING_MASK);
+ outb(devpriv->ctrl_reg, dev->iobase + DAS16_CTRL_REG);
+
+ comedi_isadma_disable(dma->chan);
+
+ /* disable SW timer */
+ if (devpriv->timer_running) {
+ devpriv->timer_running = 0;
+ del_timer(&devpriv->timer);
+ }
+
+ if (devpriv->can_burst)
+ outb(0, dev->iobase + DAS1600_BURST_REG);
+
+ spin_unlock_irqrestore(&dev->spinlock, flags);
+
+ return 0;
+}
+
+static void das16_ai_munge(struct comedi_device *dev,
+ struct comedi_subdevice *s, void *array,
+ unsigned int num_bytes,
+ unsigned int start_chan_index)
+{
+ unsigned short *data = array;
+ unsigned int num_samples = comedi_bytes_to_samples(s, num_bytes);
+ unsigned int i;
+
+ for (i = 0; i < num_samples; i++) {
+ data[i] = le16_to_cpu(data[i]);
+ if (s->maxdata == 0x0fff)
+ data[i] >>= 4;
+ data[i] &= s->maxdata;
+ }
+}
+
+static int das16_ai_eoc(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned long context)
+{
+ unsigned int status;
+
+ status = inb(dev->iobase + DAS16_STATUS_REG);
+ if ((status & DAS16_STATUS_BUSY) == 0)
+ return 0;
+ return -EBUSY;
+}
+
+static int das16_ai_insn_read(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ unsigned int chan = CR_CHAN(insn->chanspec);
+ unsigned int range = CR_RANGE(insn->chanspec);
+ unsigned int val;
+ int ret;
+ int i;
+
+ /* set mux and range for single channel */
+ das16_ai_set_mux_range(dev, chan, chan, range);
+
+ for (i = 0; i < insn->n; i++) {
+ /* trigger conversion */
+ outb_p(0, dev->iobase + DAS16_TRIG_REG);
+
+ ret = comedi_timeout(dev, s, insn, das16_ai_eoc, 0);
+ if (ret)
+ return ret;
+
+ val = inb(dev->iobase + DAS16_AI_MSB_REG) << 8;
+ val |= inb(dev->iobase + DAS16_AI_LSB_REG);
+ if (s->maxdata == 0x0fff)
+ val >>= 4;
+ val &= s->maxdata;
+
+ data[i] = val;
+ }
+
+ return insn->n;
+}
+
+static int das16_ao_insn_write(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ unsigned int chan = CR_CHAN(insn->chanspec);
+ int i;
+
+ for (i = 0; i < insn->n; i++) {
+ unsigned int val = data[i];
+
+ s->readback[chan] = val;
+
+ val <<= 4;
+
+ outb(val & 0xff, dev->iobase + DAS16_AO_LSB_REG(chan));
+ outb((val >> 8) & 0xff, dev->iobase + DAS16_AO_MSB_REG(chan));
+ }
+
+ return insn->n;
+}
+
+static int das16_di_insn_bits(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ data[1] = inb(dev->iobase + DAS16_DIO_REG) & 0xf;
+
+ return insn->n;
+}
+
+static int das16_do_insn_bits(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ if (comedi_dio_update_state(s, data))
+ outb(s->state, dev->iobase + DAS16_DIO_REG);
+
+ data[1] = s->state;
+
+ return insn->n;
+}
+
+static int das16_probe(struct comedi_device *dev, struct comedi_devconfig *it)
+{
+ const struct das16_board *board = dev->board_ptr;
+ int diobits;
+
+ /* diobits indicates boards */
+ diobits = inb(dev->iobase + DAS16_DIO_REG) & 0xf0;
+ if (board->id != diobits) {
+ dev_err(dev->class_dev,
+ "requested board's id bits are incorrect (0x%x != 0x%x)\n",
+ board->id, diobits);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static void das16_reset(struct comedi_device *dev)
+{
+ outb(0, dev->iobase + DAS16_STATUS_REG);
+ outb(0, dev->iobase + DAS16_CTRL_REG);
+ outb(0, dev->iobase + DAS16_PACER_REG);
+}
+
+static void das16_alloc_dma(struct comedi_device *dev, unsigned int dma_chan)
+{
+ struct das16_private_struct *devpriv = dev->private;
+
+ /* only DMA channels 3 and 1 are valid */
+ if (!(dma_chan == 1 || dma_chan == 3))
+ return;
+
+ /* DMA uses two buffers */
+ devpriv->dma = comedi_isadma_alloc(dev, 2, dma_chan, dma_chan,
+ DAS16_DMA_SIZE, COMEDI_ISADMA_READ);
+ if (devpriv->dma) {
+ setup_timer(&devpriv->timer, das16_timer_interrupt,
+ (unsigned long)dev);
+ }
+}
+
+static void das16_free_dma(struct comedi_device *dev)
+{
+ struct das16_private_struct *devpriv = dev->private;
+
+ if (devpriv) {
+ if (devpriv->timer.data)
+ del_timer_sync(&devpriv->timer);
+ comedi_isadma_free(devpriv->dma);
+ }
+}
+
+static const struct comedi_lrange *das16_ai_range(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_devconfig *it,
+ unsigned int pg_type,
+ unsigned int status)
+{
+ unsigned int min = it->options[4];
+ unsigned int max = it->options[5];
+
+ /* get any user-defined input range */
+ if (pg_type == das16_pg_none && (min || max)) {
+ struct comedi_lrange *lrange;
+ struct comedi_krange *krange;
+
+ /* allocate single-range range table */
+ lrange = comedi_alloc_spriv(s,
+ sizeof(*lrange) + sizeof(*krange));
+ if (!lrange)
+ return &range_unknown;
+
+ /* initialize ai range */
+ lrange->length = 1;
+ krange = lrange->range;
+ krange->min = min;
+ krange->max = max;
+ krange->flags = UNIT_volt;
+
+ return lrange;
+ }
+
+ /* use software programmable range */
+ if (status & DAS16_STATUS_UNIPOLAR)
+ return das16_ai_uni_lranges[pg_type];
+ return das16_ai_bip_lranges[pg_type];
+}
+
+static const struct comedi_lrange *das16_ao_range(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_devconfig *it)
+{
+ unsigned int min = it->options[6];
+ unsigned int max = it->options[7];
+
+ /* get any user-defined output range */
+ if (min || max) {
+ struct comedi_lrange *lrange;
+ struct comedi_krange *krange;
+
+ /* allocate single-range range table */
+ lrange = comedi_alloc_spriv(s,
+ sizeof(*lrange) + sizeof(*krange));
+ if (!lrange)
+ return &range_unknown;
+
+ /* initialize ao range */
+ lrange->length = 1;
+ krange = lrange->range;
+ krange->min = min;
+ krange->max = max;
+ krange->flags = UNIT_volt;
+
+ return lrange;
+ }
+
+ return &range_unknown;
+}
+
+static int das16_attach(struct comedi_device *dev, struct comedi_devconfig *it)
+{
+ const struct das16_board *board = dev->board_ptr;
+ struct das16_private_struct *devpriv;
+ struct comedi_subdevice *s;
+ unsigned int osc_base;
+ unsigned int status;
+ int ret;
+
+ /* check that clock setting is valid */
+ if (it->options[3]) {
+ if (it->options[3] != 0 &&
+ it->options[3] != 1 && it->options[3] != 10) {
+ dev_err(dev->class_dev,
+ "Invalid option. Master clock must be set to 1 or 10 (MHz)\n");
+ return -EINVAL;
+ }
+ }
+
+ devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
+ if (!devpriv)
+ return -ENOMEM;
+
+ if (board->size < 0x400) {
+ ret = comedi_request_region(dev, it->options[0], board->size);
+ if (ret)
+ return ret;
+ } else {
+ ret = comedi_request_region(dev, it->options[0], 0x10);
+ if (ret)
+ return ret;
+ /* Request an additional region for the 8255 */
+ ret = __comedi_request_region(dev, dev->iobase + 0x400,
+ board->size & 0x3ff);
+ if (ret)
+ return ret;
+ devpriv->extra_iobase = dev->iobase + 0x400;
+ devpriv->can_burst = 1;
+ }
+
+ /* probe id bits to make sure they are consistent */
+ if (das16_probe(dev, it))
+ return -EINVAL;
+
+ /* get master clock speed */
+ osc_base = I8254_OSC_BASE_1MHZ;
+ if (devpriv->can_burst) {
+ status = inb(dev->iobase + DAS1600_STATUS_REG);
+ if (status & DAS1600_STATUS_CLK_10MHZ)
+ osc_base = I8254_OSC_BASE_10MHZ;
+ } else {
+ if (it->options[3])
+ osc_base = I8254_OSC_BASE_1MHZ / it->options[3];
+ }
+
+ dev->pacer = comedi_8254_init(dev->iobase + DAS16_TIMER_BASE_REG,
+ osc_base, I8254_IO8, 0);
+ if (!dev->pacer)
+ return -ENOMEM;
+
+ das16_alloc_dma(dev, it->options[2]);
+
+ ret = comedi_alloc_subdevices(dev, 4 + board->has_8255);
+ if (ret)
+ return ret;
+
+ status = inb(dev->iobase + DAS16_STATUS_REG);
+
+ /* Analog Input subdevice */
+ s = &dev->subdevices[0];
+ s->type = COMEDI_SUBD_AI;
+ s->subdev_flags = SDF_READABLE;
+ if (status & DAS16_STATUS_MUXBIT) {
+ s->subdev_flags |= SDF_GROUND;
+ s->n_chan = 16;
+ } else {
+ s->subdev_flags |= SDF_DIFF;
+ s->n_chan = 8;
+ }
+ s->len_chanlist = s->n_chan;
+ s->maxdata = board->ai_maxdata;
+ s->range_table = das16_ai_range(dev, s, it, board->ai_pg, status);
+ s->insn_read = das16_ai_insn_read;
+ if (devpriv->dma) {
+ dev->read_subdev = s;
+ s->subdev_flags |= SDF_CMD_READ;
+ s->do_cmdtest = das16_cmd_test;
+ s->do_cmd = das16_cmd_exec;
+ s->cancel = das16_cancel;
+ s->munge = das16_ai_munge;
+ }
+
+ /* Analog Output subdevice */
+ s = &dev->subdevices[1];
+ if (board->has_ao) {
+ s->type = COMEDI_SUBD_AO;
+ s->subdev_flags = SDF_WRITABLE;
+ s->n_chan = 2;
+ s->maxdata = 0x0fff;
+ s->range_table = das16_ao_range(dev, s, it);
+ s->insn_write = das16_ao_insn_write;
+
+ ret = comedi_alloc_subdev_readback(s);
+ if (ret)
+ return ret;
+ } else {
+ s->type = COMEDI_SUBD_UNUSED;
+ }
+
+ /* Digital Input subdevice */
+ s = &dev->subdevices[2];
+ s->type = COMEDI_SUBD_DI;
+ s->subdev_flags = SDF_READABLE;
+ s->n_chan = 4;
+ s->maxdata = 1;
+ s->range_table = &range_digital;
+ s->insn_bits = das16_di_insn_bits;
+
+ /* Digital Output subdevice */
+ s = &dev->subdevices[3];
+ s->type = COMEDI_SUBD_DO;
+ s->subdev_flags = SDF_WRITABLE;
+ s->n_chan = 4;
+ s->maxdata = 1;
+ s->range_table = &range_digital;
+ s->insn_bits = das16_do_insn_bits;
+
+ /* initialize digital output lines */
+ outb(s->state, dev->iobase + DAS16_DIO_REG);
+
+ /* 8255 Digital I/O subdevice */
+ if (board->has_8255) {
+ s = &dev->subdevices[4];
+ ret = subdev_8255_init(dev, s, NULL, board->i8255_offset);
+ if (ret)
+ return ret;
+ }
+
+ das16_reset(dev);
+ /* set the interrupt level */
+ devpriv->ctrl_reg = DAS16_CTRL_IRQ(dev->irq);
+ outb(devpriv->ctrl_reg, dev->iobase + DAS16_CTRL_REG);
+
+ if (devpriv->can_burst) {
+ outb(DAS1600_ENABLE_VAL, dev->iobase + DAS1600_ENABLE_REG);
+ outb(0, dev->iobase + DAS1600_CONV_REG);
+ outb(0, dev->iobase + DAS1600_BURST_REG);
+ }
+
+ return 0;
+}
+
+static void das16_detach(struct comedi_device *dev)
+{
+ const struct das16_board *board = dev->board_ptr;
+ struct das16_private_struct *devpriv = dev->private;
+
+ if (devpriv) {
+ if (dev->iobase)
+ das16_reset(dev);
+ das16_free_dma(dev);
+
+ if (devpriv->extra_iobase)
+ release_region(devpriv->extra_iobase,
+ board->size & 0x3ff);
+ }
+
+ comedi_legacy_detach(dev);
+}
+
+static struct comedi_driver das16_driver = {
+ .driver_name = "das16",
+ .module = THIS_MODULE,
+ .attach = das16_attach,
+ .detach = das16_detach,
+ .board_name = &das16_boards[0].name,
+ .num_names = ARRAY_SIZE(das16_boards),
+ .offset = sizeof(das16_boards[0]),
+};
+module_comedi_driver(das16_driver);
+
+MODULE_AUTHOR("Comedi http://www.comedi.org");
+MODULE_DESCRIPTION("Comedi driver for DAS16 compatible boards");
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/comedi/drivers/das16m1.c b/drivers/staging/comedi/drivers/das16m1.c
new file mode 100644
index 000000000..1adf6a71a
--- /dev/null
+++ b/drivers/staging/comedi/drivers/das16m1.c
@@ -0,0 +1,644 @@
+/*
+ comedi/drivers/das16m1.c
+ CIO-DAS16/M1 driver
+ Author: Frank Mori Hess, based on code from the das16
+ driver.
+ Copyright (C) 2001 Frank Mori Hess <fmhess@users.sourceforge.net>
+
+ COMEDI - Linux Control and Measurement Device Interface
+ Copyright (C) 2000 David A. Schleef <ds@schleef.org>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+*/
+/*
+Driver: das16m1
+Description: CIO-DAS16/M1
+Author: Frank Mori Hess <fmhess@users.sourceforge.net>
+Devices: [Measurement Computing] CIO-DAS16/M1 (das16m1)
+Status: works
+
+This driver supports a single board - the CIO-DAS16/M1.
+As far as I know, there are no other boards that have
+the same register layout. Even the CIO-DAS16/M1/16 is
+significantly different.
+
+I was _barely_ able to reach the full 1 MHz capability
+of this board, using a hard real-time interrupt
+(set the TRIG_RT flag in your struct comedi_cmd and use
+rtlinux or RTAI). The board can't do dma, so the bottleneck is
+pulling the data across the ISA bus. I timed the interrupt
+handler, and it took my computer ~470 microseconds to pull 512
+samples from the board. So at 1 Mhz sampling rate,
+expect your CPU to be spending almost all of its
+time in the interrupt handler.
+
+This board has some unusual restrictions for its channel/gain list. If the
+list has 2 or more channels in it, then two conditions must be satisfied:
+(1) - even/odd channels must appear at even/odd indices in the list
+(2) - the list must have an even number of entries.
+
+Options:
+ [0] - base io address
+ [1] - irq (optional, but you probably want it)
+
+irq can be omitted, although the cmd interface will not work without it.
+*/
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include "../comedidev.h"
+
+#include "8255.h"
+#include "comedi_8254.h"
+
+#define DAS16M1_SIZE2 8
+
+#define FIFO_SIZE 1024 /* 1024 sample fifo */
+
+/*
+ CIO-DAS16_M1.pdf
+
+ "cio-das16/m1"
+
+ 0 a/d bits 0-3, mux start 12 bit
+ 1 a/d bits 4-11 unused
+ 2 status control
+ 3 di 4 bit do 4 bit
+ 4 unused clear interrupt
+ 5 interrupt, pacer
+ 6 channel/gain queue address
+ 7 channel/gain queue data
+ 89ab 8254
+ cdef 8254
+ 400 8255
+ 404-407 8254
+
+*/
+
+#define DAS16M1_AI 0 /* 16-bit wide register */
+#define AI_CHAN(x) ((x) & 0xf)
+#define DAS16M1_CS 2
+#define EXT_TRIG_BIT 0x1
+#define OVRUN 0x20
+#define IRQDATA 0x80
+#define DAS16M1_DIO 3
+#define DAS16M1_CLEAR_INTR 4
+#define DAS16M1_INTR_CONTROL 5
+#define EXT_PACER 0x2
+#define INT_PACER 0x3
+#define PACER_MASK 0x3
+#define INTE 0x80
+#define DAS16M1_QUEUE_ADDR 6
+#define DAS16M1_QUEUE_DATA 7
+#define Q_CHAN(x) ((x) & 0x7)
+#define Q_RANGE(x) (((x) & 0xf) << 4)
+#define UNIPOLAR 0x40
+#define DAS16M1_8254_FIRST 0x8
+#define DAS16M1_8254_SECOND 0xc
+#define DAS16M1_82C55 0x400
+#define DAS16M1_8254_THIRD 0x404
+
+static const struct comedi_lrange range_das16m1 = {
+ 9, {
+ BIP_RANGE(5),
+ BIP_RANGE(2.5),
+ BIP_RANGE(1.25),
+ BIP_RANGE(0.625),
+ UNI_RANGE(10),
+ UNI_RANGE(5),
+ UNI_RANGE(2.5),
+ UNI_RANGE(1.25),
+ BIP_RANGE(10)
+ }
+};
+
+struct das16m1_private_struct {
+ struct comedi_8254 *counter;
+ unsigned int control_state;
+ unsigned int adc_count; /* number of samples completed */
+ /* initial value in lower half of hardware conversion counter,
+ * needed to keep track of whether new count has been loaded into
+ * counter yet (loaded by first sample conversion) */
+ u16 initial_hw_count;
+ unsigned short ai_buffer[FIFO_SIZE];
+ unsigned long extra_iobase;
+};
+
+static inline unsigned short munge_sample(unsigned short data)
+{
+ return (data >> 4) & 0xfff;
+}
+
+static void munge_sample_array(unsigned short *array, unsigned int num_elements)
+{
+ unsigned int i;
+
+ for (i = 0; i < num_elements; i++)
+ array[i] = munge_sample(array[i]);
+}
+
+static int das16m1_ai_check_chanlist(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_cmd *cmd)
+{
+ int i;
+
+ if (cmd->chanlist_len == 1)
+ return 0;
+
+ if ((cmd->chanlist_len % 2) != 0) {
+ dev_dbg(dev->class_dev,
+ "chanlist must be of even length or length 1\n");
+ return -EINVAL;
+ }
+
+ for (i = 0; i < cmd->chanlist_len; i++) {
+ unsigned int chan = CR_CHAN(cmd->chanlist[i]);
+
+ if ((i % 2) != (chan % 2)) {
+ dev_dbg(dev->class_dev,
+ "even/odd channels must go have even/odd chanlist indices\n");
+ return -EINVAL;
+ }
+ }
+
+ return 0;
+}
+
+static int das16m1_cmd_test(struct comedi_device *dev,
+ struct comedi_subdevice *s, struct comedi_cmd *cmd)
+{
+ int err = 0;
+
+ /* Step 1 : check if triggers are trivially valid */
+
+ err |= comedi_check_trigger_src(&cmd->start_src, TRIG_NOW | TRIG_EXT);
+ err |= comedi_check_trigger_src(&cmd->scan_begin_src, TRIG_FOLLOW);
+ err |= comedi_check_trigger_src(&cmd->convert_src,
+ TRIG_TIMER | TRIG_EXT);
+ err |= comedi_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
+ err |= comedi_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE);
+
+ if (err)
+ return 1;
+
+ /* Step 2a : make sure trigger sources are unique */
+
+ err |= comedi_check_trigger_is_unique(cmd->start_src);
+ err |= comedi_check_trigger_is_unique(cmd->convert_src);
+ err |= comedi_check_trigger_is_unique(cmd->stop_src);
+
+ /* Step 2b : and mutually compatible */
+
+ if (err)
+ return 2;
+
+ /* Step 3: check if arguments are trivially valid */
+
+ err |= comedi_check_trigger_arg_is(&cmd->start_arg, 0);
+
+ if (cmd->scan_begin_src == TRIG_FOLLOW) /* internal trigger */
+ err |= comedi_check_trigger_arg_is(&cmd->scan_begin_arg, 0);
+
+ if (cmd->convert_src == TRIG_TIMER)
+ err |= comedi_check_trigger_arg_min(&cmd->convert_arg, 1000);
+
+ err |= comedi_check_trigger_arg_is(&cmd->scan_end_arg,
+ cmd->chanlist_len);
+
+ if (cmd->stop_src == TRIG_COUNT)
+ err |= comedi_check_trigger_arg_min(&cmd->stop_arg, 1);
+ else /* TRIG_NONE */
+ err |= comedi_check_trigger_arg_is(&cmd->stop_arg, 0);
+
+ if (err)
+ return 3;
+
+ /* step 4: fix up arguments */
+
+ if (cmd->convert_src == TRIG_TIMER) {
+ unsigned int arg = cmd->convert_arg;
+
+ comedi_8254_cascade_ns_to_timer(dev->pacer, &arg, cmd->flags);
+ err |= comedi_check_trigger_arg_is(&cmd->convert_arg, arg);
+ }
+
+ if (err)
+ return 4;
+
+ /* Step 5: check channel list if it exists */
+ if (cmd->chanlist && cmd->chanlist_len > 0)
+ err |= das16m1_ai_check_chanlist(dev, s, cmd);
+
+ if (err)
+ return 5;
+
+ return 0;
+}
+
+static int das16m1_cmd_exec(struct comedi_device *dev,
+ struct comedi_subdevice *s)
+{
+ struct das16m1_private_struct *devpriv = dev->private;
+ struct comedi_async *async = s->async;
+ struct comedi_cmd *cmd = &async->cmd;
+ unsigned int byte, i;
+
+ /* disable interrupts and internal pacer */
+ devpriv->control_state &= ~INTE & ~PACER_MASK;
+ outb(devpriv->control_state, dev->iobase + DAS16M1_INTR_CONTROL);
+
+ /* set software count */
+ devpriv->adc_count = 0;
+
+ /*
+ * Initialize lower half of hardware counter, used to determine how
+ * many samples are in fifo. Value doesn't actually load into counter
+ * until counter's next clock (the next a/d conversion).
+ */
+ comedi_8254_set_mode(devpriv->counter, 1, I8254_MODE2 | I8254_BINARY);
+ comedi_8254_write(devpriv->counter, 1, 0);
+
+ /*
+ * Remember current reading of counter so we know when counter has
+ * actually been loaded.
+ */
+ devpriv->initial_hw_count = comedi_8254_read(devpriv->counter, 1);
+
+ /* setup channel/gain queue */
+ for (i = 0; i < cmd->chanlist_len; i++) {
+ outb(i, dev->iobase + DAS16M1_QUEUE_ADDR);
+ byte =
+ Q_CHAN(CR_CHAN(cmd->chanlist[i])) |
+ Q_RANGE(CR_RANGE(cmd->chanlist[i]));
+ outb(byte, dev->iobase + DAS16M1_QUEUE_DATA);
+ }
+
+ /* enable interrupts and set internal pacer counter mode and counts */
+ devpriv->control_state &= ~PACER_MASK;
+ if (cmd->convert_src == TRIG_TIMER) {
+ comedi_8254_update_divisors(dev->pacer);
+ comedi_8254_pacer_enable(dev->pacer, 1, 2, true);
+ devpriv->control_state |= INT_PACER;
+ } else { /* TRIG_EXT */
+ devpriv->control_state |= EXT_PACER;
+ }
+
+ /* set control & status register */
+ byte = 0;
+ /* if we are using external start trigger (also board dislikes having
+ * both start and conversion triggers external simultaneously) */
+ if (cmd->start_src == TRIG_EXT && cmd->convert_src != TRIG_EXT)
+ byte |= EXT_TRIG_BIT;
+
+ outb(byte, dev->iobase + DAS16M1_CS);
+ /* clear interrupt bit */
+ outb(0, dev->iobase + DAS16M1_CLEAR_INTR);
+
+ devpriv->control_state |= INTE;
+ outb(devpriv->control_state, dev->iobase + DAS16M1_INTR_CONTROL);
+
+ return 0;
+}
+
+static int das16m1_cancel(struct comedi_device *dev, struct comedi_subdevice *s)
+{
+ struct das16m1_private_struct *devpriv = dev->private;
+
+ devpriv->control_state &= ~INTE & ~PACER_MASK;
+ outb(devpriv->control_state, dev->iobase + DAS16M1_INTR_CONTROL);
+
+ return 0;
+}
+
+static int das16m1_ai_eoc(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned long context)
+{
+ unsigned int status;
+
+ status = inb(dev->iobase + DAS16M1_CS);
+ if (status & IRQDATA)
+ return 0;
+ return -EBUSY;
+}
+
+static int das16m1_ai_rinsn(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn, unsigned int *data)
+{
+ struct das16m1_private_struct *devpriv = dev->private;
+ int ret;
+ int n;
+ int byte;
+
+ /* disable interrupts and internal pacer */
+ devpriv->control_state &= ~INTE & ~PACER_MASK;
+ outb(devpriv->control_state, dev->iobase + DAS16M1_INTR_CONTROL);
+
+ /* setup channel/gain queue */
+ outb(0, dev->iobase + DAS16M1_QUEUE_ADDR);
+ byte =
+ Q_CHAN(CR_CHAN(insn->chanspec)) | Q_RANGE(CR_RANGE(insn->chanspec));
+ outb(byte, dev->iobase + DAS16M1_QUEUE_DATA);
+
+ for (n = 0; n < insn->n; n++) {
+ /* clear IRQDATA bit */
+ outb(0, dev->iobase + DAS16M1_CLEAR_INTR);
+ /* trigger conversion */
+ outb(0, dev->iobase);
+
+ ret = comedi_timeout(dev, s, insn, das16m1_ai_eoc, 0);
+ if (ret)
+ return ret;
+
+ data[n] = munge_sample(inw(dev->iobase));
+ }
+
+ return n;
+}
+
+static int das16m1_di_rbits(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn, unsigned int *data)
+{
+ unsigned int bits;
+
+ bits = inb(dev->iobase + DAS16M1_DIO) & 0xf;
+ data[1] = bits;
+ data[0] = 0;
+
+ return insn->n;
+}
+
+static int das16m1_do_wbits(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ if (comedi_dio_update_state(s, data))
+ outb(s->state, dev->iobase + DAS16M1_DIO);
+
+ data[1] = s->state;
+
+ return insn->n;
+}
+
+static void das16m1_handler(struct comedi_device *dev, unsigned int status)
+{
+ struct das16m1_private_struct *devpriv = dev->private;
+ struct comedi_subdevice *s;
+ struct comedi_async *async;
+ struct comedi_cmd *cmd;
+ u16 num_samples;
+ u16 hw_counter;
+
+ s = dev->read_subdev;
+ async = s->async;
+ cmd = &async->cmd;
+
+ /* figure out how many samples are in fifo */
+ hw_counter = comedi_8254_read(devpriv->counter, 1);
+ /* make sure hardware counter reading is not bogus due to initial value
+ * not having been loaded yet */
+ if (devpriv->adc_count == 0 && hw_counter == devpriv->initial_hw_count) {
+ num_samples = 0;
+ } else {
+ /* The calculation of num_samples looks odd, but it uses the following facts.
+ * 16 bit hardware counter is initialized with value of zero (which really
+ * means 0x1000). The counter decrements by one on each conversion
+ * (when the counter decrements from zero it goes to 0xffff). num_samples
+ * is a 16 bit variable, so it will roll over in a similar fashion to the
+ * hardware counter. Work it out, and this is what you get. */
+ num_samples = -hw_counter - devpriv->adc_count;
+ }
+ /* check if we only need some of the points */
+ if (cmd->stop_src == TRIG_COUNT) {
+ if (num_samples > cmd->stop_arg * cmd->chanlist_len)
+ num_samples = cmd->stop_arg * cmd->chanlist_len;
+ }
+ /* make sure we dont try to get too many points if fifo has overrun */
+ if (num_samples > FIFO_SIZE)
+ num_samples = FIFO_SIZE;
+ insw(dev->iobase, devpriv->ai_buffer, num_samples);
+ munge_sample_array(devpriv->ai_buffer, num_samples);
+ comedi_buf_write_samples(s, devpriv->ai_buffer, num_samples);
+ devpriv->adc_count += num_samples;
+
+ if (cmd->stop_src == TRIG_COUNT) {
+ if (devpriv->adc_count >= cmd->stop_arg * cmd->chanlist_len) {
+ /* end of acquisition */
+ async->events |= COMEDI_CB_EOA;
+ }
+ }
+
+ /* this probably won't catch overruns since the card doesn't generate
+ * overrun interrupts, but we might as well try */
+ if (status & OVRUN) {
+ async->events |= COMEDI_CB_ERROR;
+ dev_err(dev->class_dev, "fifo overflow\n");
+ }
+
+ comedi_handle_events(dev, s);
+}
+
+static int das16m1_poll(struct comedi_device *dev, struct comedi_subdevice *s)
+{
+ unsigned long flags;
+ unsigned int status;
+
+ /* prevent race with interrupt handler */
+ spin_lock_irqsave(&dev->spinlock, flags);
+ status = inb(dev->iobase + DAS16M1_CS);
+ das16m1_handler(dev, status);
+ spin_unlock_irqrestore(&dev->spinlock, flags);
+
+ return comedi_buf_n_bytes_ready(s);
+}
+
+static irqreturn_t das16m1_interrupt(int irq, void *d)
+{
+ int status;
+ struct comedi_device *dev = d;
+
+ if (!dev->attached) {
+ dev_err(dev->class_dev, "premature interrupt\n");
+ return IRQ_HANDLED;
+ }
+ /* prevent race with comedi_poll() */
+ spin_lock(&dev->spinlock);
+
+ status = inb(dev->iobase + DAS16M1_CS);
+
+ if ((status & (IRQDATA | OVRUN)) == 0) {
+ dev_err(dev->class_dev, "spurious interrupt\n");
+ spin_unlock(&dev->spinlock);
+ return IRQ_NONE;
+ }
+
+ das16m1_handler(dev, status);
+
+ /* clear interrupt */
+ outb(0, dev->iobase + DAS16M1_CLEAR_INTR);
+
+ spin_unlock(&dev->spinlock);
+ return IRQ_HANDLED;
+}
+
+static int das16m1_irq_bits(unsigned int irq)
+{
+ switch (irq) {
+ case 10:
+ return 0x0;
+ case 11:
+ return 0x1;
+ case 12:
+ return 0x2;
+ case 15:
+ return 0x3;
+ case 2:
+ return 0x4;
+ case 3:
+ return 0x5;
+ case 5:
+ return 0x6;
+ case 7:
+ return 0x7;
+ default:
+ return 0x0;
+ }
+}
+
+/*
+ * Options list:
+ * 0 I/O base
+ * 1 IRQ
+ */
+static int das16m1_attach(struct comedi_device *dev,
+ struct comedi_devconfig *it)
+{
+ struct das16m1_private_struct *devpriv;
+ struct comedi_subdevice *s;
+ int ret;
+
+ devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
+ if (!devpriv)
+ return -ENOMEM;
+
+ ret = comedi_request_region(dev, it->options[0], 0x10);
+ if (ret)
+ return ret;
+ /* Request an additional region for the 8255 */
+ ret = __comedi_request_region(dev, dev->iobase + DAS16M1_82C55,
+ DAS16M1_SIZE2);
+ if (ret)
+ return ret;
+ devpriv->extra_iobase = dev->iobase + DAS16M1_82C55;
+
+ /* only irqs 2, 3, 4, 5, 6, 7, 10, 11, 12, 14, and 15 are valid */
+ if ((1 << it->options[1]) & 0xdcfc) {
+ ret = request_irq(it->options[1], das16m1_interrupt, 0,
+ dev->board_name, dev);
+ if (ret == 0)
+ dev->irq = it->options[1];
+ }
+
+ dev->pacer = comedi_8254_init(dev->iobase + DAS16M1_8254_SECOND,
+ I8254_OSC_BASE_10MHZ, I8254_IO8, 0);
+ if (!dev->pacer)
+ return -ENOMEM;
+
+ devpriv->counter = comedi_8254_init(dev->iobase + DAS16M1_8254_FIRST,
+ 0, I8254_IO8, 0);
+ if (!devpriv->counter)
+ return -ENOMEM;
+
+ ret = comedi_alloc_subdevices(dev, 4);
+ if (ret)
+ return ret;
+
+ s = &dev->subdevices[0];
+ /* ai */
+ s->type = COMEDI_SUBD_AI;
+ s->subdev_flags = SDF_READABLE | SDF_DIFF;
+ s->n_chan = 8;
+ s->maxdata = (1 << 12) - 1;
+ s->range_table = &range_das16m1;
+ s->insn_read = das16m1_ai_rinsn;
+ if (dev->irq) {
+ dev->read_subdev = s;
+ s->subdev_flags |= SDF_CMD_READ;
+ s->len_chanlist = 256;
+ s->do_cmdtest = das16m1_cmd_test;
+ s->do_cmd = das16m1_cmd_exec;
+ s->cancel = das16m1_cancel;
+ s->poll = das16m1_poll;
+ }
+
+ s = &dev->subdevices[1];
+ /* di */
+ s->type = COMEDI_SUBD_DI;
+ s->subdev_flags = SDF_READABLE;
+ s->n_chan = 4;
+ s->maxdata = 1;
+ s->range_table = &range_digital;
+ s->insn_bits = das16m1_di_rbits;
+
+ s = &dev->subdevices[2];
+ /* do */
+ s->type = COMEDI_SUBD_DO;
+ s->subdev_flags = SDF_WRITABLE;
+ s->n_chan = 4;
+ s->maxdata = 1;
+ s->range_table = &range_digital;
+ s->insn_bits = das16m1_do_wbits;
+
+ s = &dev->subdevices[3];
+ /* 8255 */
+ ret = subdev_8255_init(dev, s, NULL, DAS16M1_82C55);
+ if (ret)
+ return ret;
+
+ /* initialize digital output lines */
+ outb(0, dev->iobase + DAS16M1_DIO);
+
+ /* set the interrupt level */
+ devpriv->control_state = das16m1_irq_bits(dev->irq) << 4;
+ outb(devpriv->control_state, dev->iobase + DAS16M1_INTR_CONTROL);
+
+ return 0;
+}
+
+static void das16m1_detach(struct comedi_device *dev)
+{
+ struct das16m1_private_struct *devpriv = dev->private;
+
+ if (devpriv) {
+ if (devpriv->extra_iobase)
+ release_region(devpriv->extra_iobase, DAS16M1_SIZE2);
+ kfree(devpriv->counter);
+ }
+ comedi_legacy_detach(dev);
+}
+
+static struct comedi_driver das16m1_driver = {
+ .driver_name = "das16m1",
+ .module = THIS_MODULE,
+ .attach = das16m1_attach,
+ .detach = das16m1_detach,
+};
+module_comedi_driver(das16m1_driver);
+
+MODULE_AUTHOR("Comedi http://www.comedi.org");
+MODULE_DESCRIPTION("Comedi low-level driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/comedi/drivers/das1800.c b/drivers/staging/comedi/drivers/das1800.c
new file mode 100644
index 000000000..53baf37cd
--- /dev/null
+++ b/drivers/staging/comedi/drivers/das1800.c
@@ -0,0 +1,1455 @@
+/*
+ comedi/drivers/das1800.c
+ Driver for Keitley das1700/das1800 series boards
+ Copyright (C) 2000 Frank Mori Hess <fmhess@users.sourceforge.net>
+
+ COMEDI - Linux Control and Measurement Device Interface
+ Copyright (C) 2000 David A. Schleef <ds@schleef.org>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+*/
+/*
+Driver: das1800
+Description: Keithley Metrabyte DAS1800 (& compatibles)
+Author: Frank Mori Hess <fmhess@users.sourceforge.net>
+Devices: [Keithley Metrabyte] DAS-1701ST (das-1701st),
+ DAS-1701ST-DA (das-1701st-da), DAS-1701/AO (das-1701ao),
+ DAS-1702ST (das-1702st), DAS-1702ST-DA (das-1702st-da),
+ DAS-1702HR (das-1702hr), DAS-1702HR-DA (das-1702hr-da),
+ DAS-1702/AO (das-1702ao), DAS-1801ST (das-1801st),
+ DAS-1801ST-DA (das-1801st-da), DAS-1801HC (das-1801hc),
+ DAS-1801AO (das-1801ao), DAS-1802ST (das-1802st),
+ DAS-1802ST-DA (das-1802st-da), DAS-1802HR (das-1802hr),
+ DAS-1802HR-DA (das-1802hr-da), DAS-1802HC (das-1802hc),
+ DAS-1802AO (das-1802ao)
+Status: works
+
+The waveform analog output on the 'ao' cards is not supported.
+If you need it, send me (Frank Hess) an email.
+
+Configuration options:
+ [0] - I/O port base address
+ [1] - IRQ (optional, required for timed or externally triggered conversions)
+ [2] - DMA0 (optional, requires irq)
+ [3] - DMA1 (optional, requires irq and dma0)
+*/
+/*
+
+This driver supports the following Keithley boards:
+
+das-1701st
+das-1701st-da
+das-1701ao
+das-1702st
+das-1702st-da
+das-1702hr
+das-1702hr-da
+das-1702ao
+das-1801st
+das-1801st-da
+das-1801hc
+das-1801ao
+das-1802st
+das-1802st-da
+das-1802hr
+das-1802hr-da
+das-1802hc
+das-1802ao
+
+Options:
+ [0] - base io address
+ [1] - irq (optional, required for timed or externally triggered conversions)
+ [2] - dma0 (optional, requires irq)
+ [3] - dma1 (optional, requires irq and dma0)
+
+irq can be omitted, although the cmd interface will not work without it.
+
+analog input cmd triggers supported:
+ start_src: TRIG_NOW | TRIG_EXT
+ scan_begin_src: TRIG_FOLLOW | TRIG_TIMER | TRIG_EXT
+ scan_end_src: TRIG_COUNT
+ convert_src: TRIG_TIMER | TRIG_EXT (TRIG_EXT requires scan_begin_src == TRIG_FOLLOW)
+ stop_src: TRIG_COUNT | TRIG_EXT | TRIG_NONE
+
+scan_begin_src triggers TRIG_TIMER and TRIG_EXT use the card's
+'burst mode' which limits the valid conversion time to 64 microseconds
+(convert_arg <= 64000). This limitation does not apply if scan_begin_src
+is TRIG_FOLLOW.
+
+NOTES:
+Only the DAS-1801ST has been tested by me.
+Unipolar and bipolar ranges cannot be mixed in the channel/gain list.
+
+TODO:
+ Make it automatically allocate irq and dma channels if they are not specified
+ Add support for analog out on 'ao' cards
+ read insn for analog out
+*/
+
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/slab.h>
+#include <linux/io.h>
+
+#include "../comedidev.h"
+
+#include "comedi_isadma.h"
+#include "comedi_8254.h"
+
+/* misc. defines */
+#define DAS1800_SIZE 16 /* uses 16 io addresses */
+#define FIFO_SIZE 1024 /* 1024 sample fifo */
+#define UNIPOLAR 0x4 /* bit that determines whether input range is uni/bipolar */
+#define DMA_BUF_SIZE 0x1ff00 /* size in bytes of dma buffers */
+
+/* Registers for the das1800 */
+#define DAS1800_FIFO 0x0
+#define DAS1800_QRAM 0x0
+#define DAS1800_DAC 0x0
+#define DAS1800_SELECT 0x2
+#define ADC 0x0
+#define QRAM 0x1
+#define DAC(a) (0x2 + a)
+#define DAS1800_DIGITAL 0x3
+#define DAS1800_CONTROL_A 0x4
+#define FFEN 0x1
+#define CGEN 0x4
+#define CGSL 0x8
+#define TGEN 0x10
+#define TGSL 0x20
+#define ATEN 0x80
+#define DAS1800_CONTROL_B 0x5
+#define DMA_CH5 0x1
+#define DMA_CH6 0x2
+#define DMA_CH7 0x3
+#define DMA_CH5_CH6 0x5
+#define DMA_CH6_CH7 0x6
+#define DMA_CH7_CH5 0x7
+#define DMA_ENABLED 0x3 /* mask used to determine if dma is enabled */
+#define DMA_DUAL 0x4
+#define IRQ3 0x8
+#define IRQ5 0x10
+#define IRQ7 0x18
+#define IRQ10 0x28
+#define IRQ11 0x30
+#define IRQ15 0x38
+#define FIMD 0x40
+#define DAS1800_CONTROL_C 0X6
+#define IPCLK 0x1
+#define XPCLK 0x3
+#define BMDE 0x4
+#define CMEN 0x8
+#define UQEN 0x10
+#define SD 0x40
+#define UB 0x80
+#define DAS1800_STATUS 0x7
+/* bits that prevent interrupt status bits (and CVEN) from being cleared on write */
+#define CLEAR_INTR_MASK (CVEN_MASK | 0x1f)
+#define INT 0x1
+#define DMATC 0x2
+#define CT0TC 0x8
+#define OVF 0x10
+#define FHF 0x20
+#define FNE 0x40
+#define CVEN_MASK 0x40 /* masks CVEN on write */
+#define CVEN 0x80
+#define DAS1800_BURST_LENGTH 0x8
+#define DAS1800_BURST_RATE 0x9
+#define DAS1800_QRAM_ADDRESS 0xa
+#define DAS1800_COUNTER 0xc
+
+#define IOBASE2 0x400 /* offset of additional ioports used on 'ao' cards */
+
+enum {
+ das1701st, das1701st_da, das1702st, das1702st_da, das1702hr,
+ das1702hr_da,
+ das1701ao, das1702ao, das1801st, das1801st_da, das1802st, das1802st_da,
+ das1802hr, das1802hr_da, das1801hc, das1802hc, das1801ao, das1802ao
+};
+
+/* analog input ranges */
+static const struct comedi_lrange range_ai_das1801 = {
+ 8, {
+ BIP_RANGE(5),
+ BIP_RANGE(1),
+ BIP_RANGE(0.1),
+ BIP_RANGE(0.02),
+ UNI_RANGE(5),
+ UNI_RANGE(1),
+ UNI_RANGE(0.1),
+ UNI_RANGE(0.02)
+ }
+};
+
+static const struct comedi_lrange range_ai_das1802 = {
+ 8, {
+ BIP_RANGE(10),
+ BIP_RANGE(5),
+ BIP_RANGE(2.5),
+ BIP_RANGE(1.25),
+ UNI_RANGE(10),
+ UNI_RANGE(5),
+ UNI_RANGE(2.5),
+ UNI_RANGE(1.25)
+ }
+};
+
+struct das1800_board {
+ const char *name;
+ int ai_speed; /* max conversion period in nanoseconds */
+ int resolution; /* bits of ai resolution */
+ int qram_len; /* length of card's channel / gain queue */
+ int common; /* supports AREF_COMMON flag */
+ int do_n_chan; /* number of digital output channels */
+ int ao_ability; /* 0 == no analog out, 1 == basic analog out, 2 == waveform analog out */
+ int ao_n_chan; /* number of analog out channels */
+ const struct comedi_lrange *range_ai; /* available input ranges */
+};
+
+/* Warning: the maximum conversion speeds listed below are
+ * not always achievable depending on board setup (see
+ * user manual.)
+ */
+static const struct das1800_board das1800_boards[] = {
+ {
+ .name = "das-1701st",
+ .ai_speed = 6250,
+ .resolution = 12,
+ .qram_len = 256,
+ .common = 1,
+ .do_n_chan = 4,
+ .ao_ability = 0,
+ .ao_n_chan = 0,
+ .range_ai = &range_ai_das1801,
+ },
+ {
+ .name = "das-1701st-da",
+ .ai_speed = 6250,
+ .resolution = 12,
+ .qram_len = 256,
+ .common = 1,
+ .do_n_chan = 4,
+ .ao_ability = 1,
+ .ao_n_chan = 4,
+ .range_ai = &range_ai_das1801,
+ },
+ {
+ .name = "das-1702st",
+ .ai_speed = 6250,
+ .resolution = 12,
+ .qram_len = 256,
+ .common = 1,
+ .do_n_chan = 4,
+ .ao_ability = 0,
+ .ao_n_chan = 0,
+ .range_ai = &range_ai_das1802,
+ },
+ {
+ .name = "das-1702st-da",
+ .ai_speed = 6250,
+ .resolution = 12,
+ .qram_len = 256,
+ .common = 1,
+ .do_n_chan = 4,
+ .ao_ability = 1,
+ .ao_n_chan = 4,
+ .range_ai = &range_ai_das1802,
+ },
+ {
+ .name = "das-1702hr",
+ .ai_speed = 20000,
+ .resolution = 16,
+ .qram_len = 256,
+ .common = 1,
+ .do_n_chan = 4,
+ .ao_ability = 0,
+ .ao_n_chan = 0,
+ .range_ai = &range_ai_das1802,
+ },
+ {
+ .name = "das-1702hr-da",
+ .ai_speed = 20000,
+ .resolution = 16,
+ .qram_len = 256,
+ .common = 1,
+ .do_n_chan = 4,
+ .ao_ability = 1,
+ .ao_n_chan = 2,
+ .range_ai = &range_ai_das1802,
+ },
+ {
+ .name = "das-1701ao",
+ .ai_speed = 6250,
+ .resolution = 12,
+ .qram_len = 256,
+ .common = 1,
+ .do_n_chan = 4,
+ .ao_ability = 2,
+ .ao_n_chan = 2,
+ .range_ai = &range_ai_das1801,
+ },
+ {
+ .name = "das-1702ao",
+ .ai_speed = 6250,
+ .resolution = 12,
+ .qram_len = 256,
+ .common = 1,
+ .do_n_chan = 4,
+ .ao_ability = 2,
+ .ao_n_chan = 2,
+ .range_ai = &range_ai_das1802,
+ },
+ {
+ .name = "das-1801st",
+ .ai_speed = 3000,
+ .resolution = 12,
+ .qram_len = 256,
+ .common = 1,
+ .do_n_chan = 4,
+ .ao_ability = 0,
+ .ao_n_chan = 0,
+ .range_ai = &range_ai_das1801,
+ },
+ {
+ .name = "das-1801st-da",
+ .ai_speed = 3000,
+ .resolution = 12,
+ .qram_len = 256,
+ .common = 1,
+ .do_n_chan = 4,
+ .ao_ability = 0,
+ .ao_n_chan = 4,
+ .range_ai = &range_ai_das1801,
+ },
+ {
+ .name = "das-1802st",
+ .ai_speed = 3000,
+ .resolution = 12,
+ .qram_len = 256,
+ .common = 1,
+ .do_n_chan = 4,
+ .ao_ability = 0,
+ .ao_n_chan = 0,
+ .range_ai = &range_ai_das1802,
+ },
+ {
+ .name = "das-1802st-da",
+ .ai_speed = 3000,
+ .resolution = 12,
+ .qram_len = 256,
+ .common = 1,
+ .do_n_chan = 4,
+ .ao_ability = 1,
+ .ao_n_chan = 4,
+ .range_ai = &range_ai_das1802,
+ },
+ {
+ .name = "das-1802hr",
+ .ai_speed = 10000,
+ .resolution = 16,
+ .qram_len = 256,
+ .common = 1,
+ .do_n_chan = 4,
+ .ao_ability = 0,
+ .ao_n_chan = 0,
+ .range_ai = &range_ai_das1802,
+ },
+ {
+ .name = "das-1802hr-da",
+ .ai_speed = 10000,
+ .resolution = 16,
+ .qram_len = 256,
+ .common = 1,
+ .do_n_chan = 4,
+ .ao_ability = 1,
+ .ao_n_chan = 2,
+ .range_ai = &range_ai_das1802,
+ },
+ {
+ .name = "das-1801hc",
+ .ai_speed = 3000,
+ .resolution = 12,
+ .qram_len = 64,
+ .common = 0,
+ .do_n_chan = 8,
+ .ao_ability = 1,
+ .ao_n_chan = 2,
+ .range_ai = &range_ai_das1801,
+ },
+ {
+ .name = "das-1802hc",
+ .ai_speed = 3000,
+ .resolution = 12,
+ .qram_len = 64,
+ .common = 0,
+ .do_n_chan = 8,
+ .ao_ability = 1,
+ .ao_n_chan = 2,
+ .range_ai = &range_ai_das1802,
+ },
+ {
+ .name = "das-1801ao",
+ .ai_speed = 3000,
+ .resolution = 12,
+ .qram_len = 256,
+ .common = 1,
+ .do_n_chan = 4,
+ .ao_ability = 2,
+ .ao_n_chan = 2,
+ .range_ai = &range_ai_das1801,
+ },
+ {
+ .name = "das-1802ao",
+ .ai_speed = 3000,
+ .resolution = 12,
+ .qram_len = 256,
+ .common = 1,
+ .do_n_chan = 4,
+ .ao_ability = 2,
+ .ao_n_chan = 2,
+ .range_ai = &range_ai_das1802,
+ },
+};
+
+struct das1800_private {
+ struct comedi_isadma *dma;
+ int irq_dma_bits; /* bits for control register b */
+ /* dma bits for control register b, stored so that dma can be
+ * turned on and off */
+ int dma_bits;
+ uint16_t *fifo_buf; /* bounce buffer for analog input FIFO */
+ unsigned long iobase2; /* secondary io address used for analog out on 'ao' boards */
+ unsigned short ao_update_bits; /* remembers the last write to the
+ * 'update' dac */
+};
+
+/* analog out range for 'ao' boards */
+/*
+static const struct comedi_lrange range_ao_2 = {
+ 2, {
+ BIP_RANGE(10),
+ BIP_RANGE(5)
+ }
+};
+*/
+
+static inline uint16_t munge_bipolar_sample(const struct comedi_device *dev,
+ uint16_t sample)
+{
+ const struct das1800_board *thisboard = dev->board_ptr;
+
+ sample += 1 << (thisboard->resolution - 1);
+ return sample;
+}
+
+static void munge_data(struct comedi_device *dev, uint16_t *array,
+ unsigned int num_elements)
+{
+ unsigned int i;
+ int unipolar;
+
+ /* see if card is using a unipolar or bipolar range so we can munge data correctly */
+ unipolar = inb(dev->iobase + DAS1800_CONTROL_C) & UB;
+
+ /* convert to unsigned type if we are in a bipolar mode */
+ if (!unipolar) {
+ for (i = 0; i < num_elements; i++)
+ array[i] = munge_bipolar_sample(dev, array[i]);
+ }
+}
+
+static void das1800_handle_fifo_half_full(struct comedi_device *dev,
+ struct comedi_subdevice *s)
+{
+ struct das1800_private *devpriv = dev->private;
+ unsigned int nsamples = comedi_nsamples_left(s, FIFO_SIZE / 2);
+
+ insw(dev->iobase + DAS1800_FIFO, devpriv->fifo_buf, nsamples);
+ munge_data(dev, devpriv->fifo_buf, nsamples);
+ comedi_buf_write_samples(s, devpriv->fifo_buf, nsamples);
+}
+
+static void das1800_handle_fifo_not_empty(struct comedi_device *dev,
+ struct comedi_subdevice *s)
+{
+ struct comedi_cmd *cmd = &s->async->cmd;
+ unsigned short dpnt;
+ int unipolar;
+
+ unipolar = inb(dev->iobase + DAS1800_CONTROL_C) & UB;
+
+ while (inb(dev->iobase + DAS1800_STATUS) & FNE) {
+ dpnt = inw(dev->iobase + DAS1800_FIFO);
+ /* convert to unsigned type */
+ dpnt = munge_bipolar_sample(dev, dpnt);
+ comedi_buf_write_samples(s, &dpnt, 1);
+
+ if (cmd->stop_src == TRIG_COUNT &&
+ s->async->scans_done >= cmd->stop_arg)
+ break;
+ }
+}
+
+/* Utility function used by das1800_flush_dma() and das1800_handle_dma() */
+static void das1800_flush_dma_channel(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_isadma_desc *desc)
+{
+ unsigned int residue = comedi_isadma_disable(desc->chan);
+ unsigned int nbytes = desc->size - residue;
+ unsigned int nsamples;
+
+ /* figure out how many points to read */
+ nsamples = comedi_bytes_to_samples(s, nbytes);
+ nsamples = comedi_nsamples_left(s, nsamples);
+
+ munge_data(dev, desc->virt_addr, nsamples);
+ comedi_buf_write_samples(s, desc->virt_addr, nsamples);
+}
+
+/* flushes remaining data from board when external trigger has stopped acquisition
+ * and we are using dma transfers */
+static void das1800_flush_dma(struct comedi_device *dev,
+ struct comedi_subdevice *s)
+{
+ struct das1800_private *devpriv = dev->private;
+ struct comedi_isadma *dma = devpriv->dma;
+ struct comedi_isadma_desc *desc = &dma->desc[dma->cur_dma];
+ const int dual_dma = devpriv->irq_dma_bits & DMA_DUAL;
+
+ das1800_flush_dma_channel(dev, s, desc);
+
+ if (dual_dma) {
+ /* switch to other channel and flush it */
+ dma->cur_dma = 1 - dma->cur_dma;
+ desc = &dma->desc[dma->cur_dma];
+ das1800_flush_dma_channel(dev, s, desc);
+ }
+
+ /* get any remaining samples in fifo */
+ das1800_handle_fifo_not_empty(dev, s);
+}
+
+static void das1800_handle_dma(struct comedi_device *dev,
+ struct comedi_subdevice *s, unsigned int status)
+{
+ struct das1800_private *devpriv = dev->private;
+ struct comedi_isadma *dma = devpriv->dma;
+ struct comedi_isadma_desc *desc = &dma->desc[dma->cur_dma];
+ const int dual_dma = devpriv->irq_dma_bits & DMA_DUAL;
+
+ das1800_flush_dma_channel(dev, s, desc);
+
+ /* re-enable dma channel */
+ comedi_isadma_program(desc);
+
+ if (status & DMATC) {
+ /* clear DMATC interrupt bit */
+ outb(CLEAR_INTR_MASK & ~DMATC, dev->iobase + DAS1800_STATUS);
+ /* switch dma channels for next time, if appropriate */
+ if (dual_dma)
+ dma->cur_dma = 1 - dma->cur_dma;
+ }
+}
+
+static int das1800_cancel(struct comedi_device *dev, struct comedi_subdevice *s)
+{
+ struct das1800_private *devpriv = dev->private;
+ struct comedi_isadma *dma = devpriv->dma;
+ struct comedi_isadma_desc *desc;
+ int i;
+
+ outb(0x0, dev->iobase + DAS1800_STATUS); /* disable conversions */
+ outb(0x0, dev->iobase + DAS1800_CONTROL_B); /* disable interrupts and dma */
+ outb(0x0, dev->iobase + DAS1800_CONTROL_A); /* disable and clear fifo and stop triggering */
+
+ for (i = 0; i < 2; i++) {
+ desc = &dma->desc[i];
+ if (desc->chan)
+ comedi_isadma_disable(desc->chan);
+ }
+
+ return 0;
+}
+
+/* the guts of the interrupt handler, that is shared with das1800_ai_poll */
+static void das1800_ai_handler(struct comedi_device *dev)
+{
+ struct das1800_private *devpriv = dev->private;
+ struct comedi_subdevice *s = dev->read_subdev;
+ struct comedi_async *async = s->async;
+ struct comedi_cmd *cmd = &async->cmd;
+ unsigned int status = inb(dev->iobase + DAS1800_STATUS);
+
+ /* select adc for base address + 0 */
+ outb(ADC, dev->iobase + DAS1800_SELECT);
+ /* dma buffer full */
+ if (devpriv->irq_dma_bits & DMA_ENABLED) {
+ /* look for data from dma transfer even if dma terminal count hasn't happened yet */
+ das1800_handle_dma(dev, s, status);
+ } else if (status & FHF) { /* if fifo half full */
+ das1800_handle_fifo_half_full(dev, s);
+ } else if (status & FNE) { /* if fifo not empty */
+ das1800_handle_fifo_not_empty(dev, s);
+ }
+
+ /* if the card's fifo has overflowed */
+ if (status & OVF) {
+ /* clear OVF interrupt bit */
+ outb(CLEAR_INTR_MASK & ~OVF, dev->iobase + DAS1800_STATUS);
+ dev_err(dev->class_dev, "FIFO overflow\n");
+ async->events |= COMEDI_CB_ERROR;
+ comedi_handle_events(dev, s);
+ return;
+ }
+ /* stop taking data if appropriate */
+ /* stop_src TRIG_EXT */
+ if (status & CT0TC) {
+ /* clear CT0TC interrupt bit */
+ outb(CLEAR_INTR_MASK & ~CT0TC, dev->iobase + DAS1800_STATUS);
+ /* make sure we get all remaining data from board before quitting */
+ if (devpriv->irq_dma_bits & DMA_ENABLED)
+ das1800_flush_dma(dev, s);
+ else
+ das1800_handle_fifo_not_empty(dev, s);
+ async->events |= COMEDI_CB_EOA;
+ } else if (cmd->stop_src == TRIG_COUNT &&
+ async->scans_done >= cmd->stop_arg) {
+ async->events |= COMEDI_CB_EOA;
+ }
+
+ comedi_handle_events(dev, s);
+}
+
+static int das1800_ai_poll(struct comedi_device *dev,
+ struct comedi_subdevice *s)
+{
+ unsigned long flags;
+
+ /* prevent race with interrupt handler */
+ spin_lock_irqsave(&dev->spinlock, flags);
+ das1800_ai_handler(dev);
+ spin_unlock_irqrestore(&dev->spinlock, flags);
+
+ return comedi_buf_n_bytes_ready(s);
+}
+
+static irqreturn_t das1800_interrupt(int irq, void *d)
+{
+ struct comedi_device *dev = d;
+ unsigned int status;
+
+ if (!dev->attached) {
+ dev_err(dev->class_dev, "premature interrupt\n");
+ return IRQ_HANDLED;
+ }
+
+ /* Prevent race with das1800_ai_poll() on multi processor systems.
+ * Also protects indirect addressing in das1800_ai_handler */
+ spin_lock(&dev->spinlock);
+ status = inb(dev->iobase + DAS1800_STATUS);
+
+ /* if interrupt was not caused by das-1800 */
+ if (!(status & INT)) {
+ spin_unlock(&dev->spinlock);
+ return IRQ_NONE;
+ }
+ /* clear the interrupt status bit INT */
+ outb(CLEAR_INTR_MASK & ~INT, dev->iobase + DAS1800_STATUS);
+ /* handle interrupt */
+ das1800_ai_handler(dev);
+
+ spin_unlock(&dev->spinlock);
+ return IRQ_HANDLED;
+}
+
+/* converts requested conversion timing to timing compatible with
+ * hardware, used only when card is in 'burst mode'
+ */
+static unsigned int burst_convert_arg(unsigned int convert_arg, int flags)
+{
+ unsigned int micro_sec;
+
+ /* in burst mode, the maximum conversion time is 64 microseconds */
+ if (convert_arg > 64000)
+ convert_arg = 64000;
+
+ /* the conversion time must be an integral number of microseconds */
+ switch (flags & CMDF_ROUND_MASK) {
+ case CMDF_ROUND_NEAREST:
+ default:
+ micro_sec = (convert_arg + 500) / 1000;
+ break;
+ case CMDF_ROUND_DOWN:
+ micro_sec = convert_arg / 1000;
+ break;
+ case CMDF_ROUND_UP:
+ micro_sec = (convert_arg - 1) / 1000 + 1;
+ break;
+ }
+
+ /* return number of nanoseconds */
+ return micro_sec * 1000;
+}
+
+static int das1800_ai_check_chanlist(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_cmd *cmd)
+{
+ unsigned int unipolar0 = CR_RANGE(cmd->chanlist[0]) & UNIPOLAR;
+ int i;
+
+ for (i = 1; i < cmd->chanlist_len; i++) {
+ unsigned int unipolar = CR_RANGE(cmd->chanlist[i]) & UNIPOLAR;
+
+ if (unipolar != unipolar0) {
+ dev_dbg(dev->class_dev,
+ "unipolar and bipolar ranges cannot be mixed in the chanlist\n");
+ return -EINVAL;
+ }
+ }
+
+ return 0;
+}
+
+/* test analog input cmd */
+static int das1800_ai_do_cmdtest(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_cmd *cmd)
+{
+ const struct das1800_board *thisboard = dev->board_ptr;
+ int err = 0;
+ unsigned int arg;
+
+ /* Step 1 : check if triggers are trivially valid */
+
+ err |= comedi_check_trigger_src(&cmd->start_src, TRIG_NOW | TRIG_EXT);
+ err |= comedi_check_trigger_src(&cmd->scan_begin_src,
+ TRIG_FOLLOW | TRIG_TIMER | TRIG_EXT);
+ err |= comedi_check_trigger_src(&cmd->convert_src,
+ TRIG_TIMER | TRIG_EXT);
+ err |= comedi_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
+ err |= comedi_check_trigger_src(&cmd->stop_src,
+ TRIG_COUNT | TRIG_EXT | TRIG_NONE);
+
+ if (err)
+ return 1;
+
+ /* Step 2a : make sure trigger sources are unique */
+
+ err |= comedi_check_trigger_is_unique(cmd->start_src);
+ err |= comedi_check_trigger_is_unique(cmd->scan_begin_src);
+ err |= comedi_check_trigger_is_unique(cmd->convert_src);
+ err |= comedi_check_trigger_is_unique(cmd->stop_src);
+
+ /* Step 2b : and mutually compatible */
+
+ if (cmd->scan_begin_src != TRIG_FOLLOW &&
+ cmd->convert_src != TRIG_TIMER)
+ err |= -EINVAL;
+
+ if (err)
+ return 2;
+
+ /* Step 3: check if arguments are trivially valid */
+
+ err |= comedi_check_trigger_arg_is(&cmd->start_arg, 0);
+
+ if (cmd->convert_src == TRIG_TIMER) {
+ err |= comedi_check_trigger_arg_min(&cmd->convert_arg,
+ thisboard->ai_speed);
+ }
+
+ err |= comedi_check_trigger_arg_min(&cmd->chanlist_len, 1);
+ err |= comedi_check_trigger_arg_is(&cmd->scan_end_arg,
+ cmd->chanlist_len);
+
+ switch (cmd->stop_src) {
+ case TRIG_COUNT:
+ err |= comedi_check_trigger_arg_min(&cmd->stop_arg, 1);
+ break;
+ case TRIG_NONE:
+ err |= comedi_check_trigger_arg_is(&cmd->stop_arg, 0);
+ break;
+ default:
+ break;
+ }
+
+ if (err)
+ return 3;
+
+ /* step 4: fix up any arguments */
+
+ if (cmd->scan_begin_src == TRIG_FOLLOW &&
+ cmd->convert_src == TRIG_TIMER) {
+ /* we are not in burst mode */
+ arg = cmd->convert_arg;
+ comedi_8254_cascade_ns_to_timer(dev->pacer, &arg, cmd->flags);
+ err |= comedi_check_trigger_arg_is(&cmd->convert_arg, arg);
+ } else if (cmd->convert_src == TRIG_TIMER) {
+ /* we are in burst mode */
+ arg = burst_convert_arg(cmd->convert_arg, cmd->flags);
+ err |= comedi_check_trigger_arg_is(&cmd->convert_arg, arg);
+
+ if (cmd->scan_begin_src == TRIG_TIMER) {
+ arg = cmd->convert_arg * cmd->chanlist_len;
+ err |= comedi_check_trigger_arg_max(&cmd->
+ scan_begin_arg,
+ arg);
+
+ arg = cmd->scan_begin_arg;
+ comedi_8254_cascade_ns_to_timer(dev->pacer, &arg,
+ cmd->flags);
+ err |= comedi_check_trigger_arg_is(&cmd->scan_begin_arg,
+ arg);
+ }
+ }
+
+ if (err)
+ return 4;
+
+ /* Step 5: check channel list if it exists */
+ if (cmd->chanlist && cmd->chanlist_len > 0)
+ err |= das1800_ai_check_chanlist(dev, s, cmd);
+
+ if (err)
+ return 5;
+
+ return 0;
+}
+
+/* returns appropriate bits for control register a, depending on command */
+static int control_a_bits(const struct comedi_cmd *cmd)
+{
+ int control_a;
+
+ control_a = FFEN; /* enable fifo */
+ if (cmd->stop_src == TRIG_EXT)
+ control_a |= ATEN;
+ switch (cmd->start_src) {
+ case TRIG_EXT:
+ control_a |= TGEN | CGSL;
+ break;
+ case TRIG_NOW:
+ control_a |= CGEN;
+ break;
+ default:
+ break;
+ }
+
+ return control_a;
+}
+
+/* returns appropriate bits for control register c, depending on command */
+static int control_c_bits(const struct comedi_cmd *cmd)
+{
+ int control_c;
+ int aref;
+
+ /* set clock source to internal or external, select analog reference,
+ * select unipolar / bipolar
+ */
+ aref = CR_AREF(cmd->chanlist[0]);
+ control_c = UQEN; /* enable upper qram addresses */
+ if (aref != AREF_DIFF)
+ control_c |= SD;
+ if (aref == AREF_COMMON)
+ control_c |= CMEN;
+ /* if a unipolar range was selected */
+ if (CR_RANGE(cmd->chanlist[0]) & UNIPOLAR)
+ control_c |= UB;
+ switch (cmd->scan_begin_src) {
+ case TRIG_FOLLOW: /* not in burst mode */
+ switch (cmd->convert_src) {
+ case TRIG_TIMER:
+ /* trig on cascaded counters */
+ control_c |= IPCLK;
+ break;
+ case TRIG_EXT:
+ /* trig on falling edge of external trigger */
+ control_c |= XPCLK;
+ break;
+ default:
+ break;
+ }
+ break;
+ case TRIG_TIMER:
+ /* burst mode with internal pacer clock */
+ control_c |= BMDE | IPCLK;
+ break;
+ case TRIG_EXT:
+ /* burst mode with external trigger */
+ control_c |= BMDE | XPCLK;
+ break;
+ default:
+ break;
+ }
+
+ return control_c;
+}
+
+static unsigned int das1800_ai_transfer_size(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ unsigned int maxbytes,
+ unsigned int ns)
+{
+ struct comedi_cmd *cmd = &s->async->cmd;
+ unsigned int max_samples = comedi_bytes_to_samples(s, maxbytes);
+ unsigned int samples;
+
+ samples = max_samples;
+
+ /* for timed modes, make dma buffer fill in 'ns' time */
+ switch (cmd->scan_begin_src) {
+ case TRIG_FOLLOW: /* not in burst mode */
+ if (cmd->convert_src == TRIG_TIMER)
+ samples = ns / cmd->convert_arg;
+ break;
+ case TRIG_TIMER:
+ samples = ns / (cmd->scan_begin_arg * cmd->chanlist_len);
+ break;
+ }
+
+ /* limit samples to what is remaining in the command */
+ samples = comedi_nsamples_left(s, samples);
+
+ if (samples > max_samples)
+ samples = max_samples;
+ if (samples < 1)
+ samples = 1;
+
+ return comedi_samples_to_bytes(s, samples);
+}
+
+static void das1800_ai_setup_dma(struct comedi_device *dev,
+ struct comedi_subdevice *s)
+{
+ struct das1800_private *devpriv = dev->private;
+ struct comedi_isadma *dma = devpriv->dma;
+ struct comedi_isadma_desc *desc = &dma->desc[0];
+ unsigned int bytes;
+
+ if ((devpriv->irq_dma_bits & DMA_ENABLED) == 0)
+ return;
+
+ dma->cur_dma = 0;
+
+ /* determine a dma transfer size to fill buffer in 0.3 sec */
+ bytes = das1800_ai_transfer_size(dev, s, desc->maxsize, 300000000);
+
+ desc->size = bytes;
+ comedi_isadma_program(desc);
+
+ /* set up dual dma if appropriate */
+ if (devpriv->irq_dma_bits & DMA_DUAL) {
+ desc = &dma->desc[1];
+ desc->size = bytes;
+ comedi_isadma_program(desc);
+ }
+}
+
+/* programs channel/gain list into card */
+static void program_chanlist(struct comedi_device *dev,
+ const struct comedi_cmd *cmd)
+{
+ int i, n, chan_range;
+ unsigned long irq_flags;
+ const int range_mask = 0x3; /* masks unipolar/bipolar bit off range */
+ const int range_bitshift = 8;
+
+ n = cmd->chanlist_len;
+ /* spinlock protects indirect addressing */
+ spin_lock_irqsave(&dev->spinlock, irq_flags);
+ outb(QRAM, dev->iobase + DAS1800_SELECT); /* select QRAM for baseAddress + 0x0 */
+ outb(n - 1, dev->iobase + DAS1800_QRAM_ADDRESS); /*set QRAM address start */
+ /* make channel / gain list */
+ for (i = 0; i < n; i++) {
+ chan_range =
+ CR_CHAN(cmd->chanlist[i]) |
+ ((CR_RANGE(cmd->chanlist[i]) & range_mask) <<
+ range_bitshift);
+ outw(chan_range, dev->iobase + DAS1800_QRAM);
+ }
+ outb(n - 1, dev->iobase + DAS1800_QRAM_ADDRESS); /*finish write to QRAM */
+ spin_unlock_irqrestore(&dev->spinlock, irq_flags);
+}
+
+/* analog input do_cmd */
+static int das1800_ai_do_cmd(struct comedi_device *dev,
+ struct comedi_subdevice *s)
+{
+ struct das1800_private *devpriv = dev->private;
+ int control_a, control_c;
+ struct comedi_async *async = s->async;
+ const struct comedi_cmd *cmd = &async->cmd;
+
+ /* disable dma on CMDF_WAKE_EOS, or CMDF_PRIORITY
+ * (because dma in handler is unsafe at hard real-time priority) */
+ if (cmd->flags & (CMDF_WAKE_EOS | CMDF_PRIORITY))
+ devpriv->irq_dma_bits &= ~DMA_ENABLED;
+ else
+ devpriv->irq_dma_bits |= devpriv->dma_bits;
+ /* interrupt on end of conversion for CMDF_WAKE_EOS */
+ if (cmd->flags & CMDF_WAKE_EOS) {
+ /* interrupt fifo not empty */
+ devpriv->irq_dma_bits &= ~FIMD;
+ } else {
+ /* interrupt fifo half full */
+ devpriv->irq_dma_bits |= FIMD;
+ }
+
+ das1800_cancel(dev, s);
+
+ /* determine proper bits for control registers */
+ control_a = control_a_bits(cmd);
+ control_c = control_c_bits(cmd);
+
+ /* setup card and start */
+ program_chanlist(dev, cmd);
+
+ /* setup cascaded counters for conversion/scan frequency */
+ if ((cmd->scan_begin_src == TRIG_FOLLOW ||
+ cmd->scan_begin_src == TRIG_TIMER) &&
+ cmd->convert_src == TRIG_TIMER) {
+ comedi_8254_update_divisors(dev->pacer);
+ comedi_8254_pacer_enable(dev->pacer, 1, 2, true);
+ }
+
+ /* setup counter 0 for 'about triggering' */
+ if (cmd->stop_src == TRIG_EXT)
+ comedi_8254_load(dev->pacer, 0, 1, I8254_MODE0 | I8254_BINARY);
+
+ das1800_ai_setup_dma(dev, s);
+ outb(control_c, dev->iobase + DAS1800_CONTROL_C);
+ /* set conversion rate and length for burst mode */
+ if (control_c & BMDE) {
+ /* program conversion period with number of microseconds minus 1 */
+ outb(cmd->convert_arg / 1000 - 1,
+ dev->iobase + DAS1800_BURST_RATE);
+ outb(cmd->chanlist_len - 1, dev->iobase + DAS1800_BURST_LENGTH);
+ }
+ outb(devpriv->irq_dma_bits, dev->iobase + DAS1800_CONTROL_B); /* enable irq/dma */
+ outb(control_a, dev->iobase + DAS1800_CONTROL_A); /* enable fifo and triggering */
+ outb(CVEN, dev->iobase + DAS1800_STATUS); /* enable conversions */
+
+ return 0;
+}
+
+/* read analog input */
+static int das1800_ai_rinsn(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn, unsigned int *data)
+{
+ const struct das1800_board *thisboard = dev->board_ptr;
+ int i, n;
+ int chan, range, aref, chan_range;
+ int timeout = 1000;
+ unsigned short dpnt;
+ int conv_flags = 0;
+ unsigned long irq_flags;
+
+ /* set up analog reference and unipolar / bipolar mode */
+ aref = CR_AREF(insn->chanspec);
+ conv_flags |= UQEN;
+ if (aref != AREF_DIFF)
+ conv_flags |= SD;
+ if (aref == AREF_COMMON)
+ conv_flags |= CMEN;
+ /* if a unipolar range was selected */
+ if (CR_RANGE(insn->chanspec) & UNIPOLAR)
+ conv_flags |= UB;
+
+ outb(conv_flags, dev->iobase + DAS1800_CONTROL_C); /* software conversion enabled */
+ outb(CVEN, dev->iobase + DAS1800_STATUS); /* enable conversions */
+ outb(0x0, dev->iobase + DAS1800_CONTROL_A); /* reset fifo */
+ outb(FFEN, dev->iobase + DAS1800_CONTROL_A);
+
+ chan = CR_CHAN(insn->chanspec);
+ /* mask of unipolar/bipolar bit from range */
+ range = CR_RANGE(insn->chanspec) & 0x3;
+ chan_range = chan | (range << 8);
+ spin_lock_irqsave(&dev->spinlock, irq_flags);
+ outb(QRAM, dev->iobase + DAS1800_SELECT); /* select QRAM for baseAddress + 0x0 */
+ outb(0x0, dev->iobase + DAS1800_QRAM_ADDRESS); /* set QRAM address start */
+ outw(chan_range, dev->iobase + DAS1800_QRAM);
+ outb(0x0, dev->iobase + DAS1800_QRAM_ADDRESS); /*finish write to QRAM */
+ outb(ADC, dev->iobase + DAS1800_SELECT); /* select ADC for baseAddress + 0x0 */
+
+ for (n = 0; n < insn->n; n++) {
+ /* trigger conversion */
+ outb(0, dev->iobase + DAS1800_FIFO);
+ for (i = 0; i < timeout; i++) {
+ if (inb(dev->iobase + DAS1800_STATUS) & FNE)
+ break;
+ }
+ if (i == timeout) {
+ dev_err(dev->class_dev, "timeout\n");
+ n = -ETIME;
+ goto exit;
+ }
+ dpnt = inw(dev->iobase + DAS1800_FIFO);
+ /* shift data to offset binary for bipolar ranges */
+ if ((conv_flags & UB) == 0)
+ dpnt += 1 << (thisboard->resolution - 1);
+ data[n] = dpnt;
+ }
+exit:
+ spin_unlock_irqrestore(&dev->spinlock, irq_flags);
+
+ return n;
+}
+
+/* writes to an analog output channel */
+static int das1800_ao_winsn(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn, unsigned int *data)
+{
+ const struct das1800_board *thisboard = dev->board_ptr;
+ struct das1800_private *devpriv = dev->private;
+ int chan = CR_CHAN(insn->chanspec);
+/* int range = CR_RANGE(insn->chanspec); */
+ int update_chan = thisboard->ao_n_chan - 1;
+ unsigned short output;
+ unsigned long irq_flags;
+
+ /* card expects two's complement data */
+ output = data[0] - (1 << (thisboard->resolution - 1));
+ /* if the write is to the 'update' channel, we need to remember its value */
+ if (chan == update_chan)
+ devpriv->ao_update_bits = output;
+ /* write to channel */
+ spin_lock_irqsave(&dev->spinlock, irq_flags);
+ outb(DAC(chan), dev->iobase + DAS1800_SELECT); /* select dac channel for baseAddress + 0x0 */
+ outw(output, dev->iobase + DAS1800_DAC);
+ /* now we need to write to 'update' channel to update all dac channels */
+ if (chan != update_chan) {
+ outb(DAC(update_chan), dev->iobase + DAS1800_SELECT); /* select 'update' channel for baseAddress + 0x0 */
+ outw(devpriv->ao_update_bits, dev->iobase + DAS1800_DAC);
+ }
+ spin_unlock_irqrestore(&dev->spinlock, irq_flags);
+
+ return 1;
+}
+
+/* reads from digital input channels */
+static int das1800_di_rbits(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn, unsigned int *data)
+{
+ data[1] = inb(dev->iobase + DAS1800_DIGITAL) & 0xf;
+ data[0] = 0;
+
+ return insn->n;
+}
+
+static int das1800_do_wbits(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ if (comedi_dio_update_state(s, data))
+ outb(s->state, dev->iobase + DAS1800_DIGITAL);
+
+ data[1] = s->state;
+
+ return insn->n;
+}
+
+static void das1800_init_dma(struct comedi_device *dev,
+ struct comedi_devconfig *it)
+{
+ struct das1800_private *devpriv = dev->private;
+ unsigned int *dma_chan;
+
+ /*
+ * it->options[2] is DMA channel 0
+ * it->options[3] is DMA channel 1
+ *
+ * Encode the DMA channels into 2 digit hexadecimal for switch.
+ */
+ dma_chan = &it->options[2];
+
+ switch ((dma_chan[0] & 0x7) | (dma_chan[1] << 4)) {
+ case 0x5: /* dma0 == 5 */
+ devpriv->dma_bits = DMA_CH5;
+ break;
+ case 0x6: /* dma0 == 6 */
+ devpriv->dma_bits = DMA_CH6;
+ break;
+ case 0x7: /* dma0 == 7 */
+ devpriv->dma_bits = DMA_CH7;
+ break;
+ case 0x65: /* dma0 == 5, dma1 == 6 */
+ devpriv->dma_bits = DMA_CH5_CH6;
+ break;
+ case 0x76: /* dma0 == 6, dma1 == 7 */
+ devpriv->dma_bits = DMA_CH6_CH7;
+ break;
+ case 0x57: /* dma0 == 7, dma1 == 5 */
+ devpriv->dma_bits = DMA_CH7_CH5;
+ break;
+ default:
+ return;
+ }
+
+ /* DMA can use 1 or 2 buffers, each with a separate channel */
+ devpriv->dma = comedi_isadma_alloc(dev, dma_chan[1] ? 2 : 1,
+ dma_chan[0], dma_chan[1],
+ DMA_BUF_SIZE, COMEDI_ISADMA_READ);
+ if (!devpriv->dma)
+ devpriv->dma_bits = 0;
+}
+
+static void das1800_free_dma(struct comedi_device *dev)
+{
+ struct das1800_private *devpriv = dev->private;
+
+ if (devpriv)
+ comedi_isadma_free(devpriv->dma);
+}
+
+static int das1800_probe(struct comedi_device *dev)
+{
+ const struct das1800_board *board = dev->board_ptr;
+ int index;
+ int id;
+
+ /* calc the offset to the boardinfo that was found by the core */
+ index = board - das1800_boards;
+
+ /* verify that the board id matches the boardinfo */
+ id = (inb(dev->iobase + DAS1800_DIGITAL) >> 4) & 0xf;
+ switch (id) {
+ case 0x3:
+ if (index == das1801st_da || index == das1802st_da ||
+ index == das1701st_da || index == das1702st_da)
+ return index;
+ index = das1801st;
+ break;
+ case 0x4:
+ if (index == das1802hr_da || index == das1702hr_da)
+ return index;
+ index = das1802hr;
+ break;
+ case 0x5:
+ if (index == das1801ao || index == das1802ao ||
+ index == das1701ao || index == das1702ao)
+ return index;
+ index = das1801ao;
+ break;
+ case 0x6:
+ if (index == das1802hr || index == das1702hr)
+ return index;
+ index = das1802hr;
+ break;
+ case 0x7:
+ if (index == das1801st || index == das1802st ||
+ index == das1701st || index == das1702st)
+ return index;
+ index = das1801st;
+ break;
+ case 0x8:
+ if (index == das1801hc || index == das1802hc)
+ return index;
+ index = das1801hc;
+ break;
+ default:
+ dev_err(dev->class_dev,
+ "Board model: probe returned 0x%x (unknown, please report)\n",
+ id);
+ break;
+ }
+ dev_err(dev->class_dev,
+ "Board model (probed, not recommended): %s series\n",
+ das1800_boards[index].name);
+
+ return index;
+}
+
+static int das1800_attach(struct comedi_device *dev,
+ struct comedi_devconfig *it)
+{
+ const struct das1800_board *thisboard;
+ struct das1800_private *devpriv;
+ struct comedi_subdevice *s;
+ unsigned int irq = it->options[1];
+ int board;
+ int ret;
+
+ devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
+ if (!devpriv)
+ return -ENOMEM;
+
+ ret = comedi_request_region(dev, it->options[0], DAS1800_SIZE);
+ if (ret)
+ return ret;
+
+ board = das1800_probe(dev);
+ if (board < 0) {
+ dev_err(dev->class_dev, "unable to determine board type\n");
+ return -ENODEV;
+ }
+
+ dev->board_ptr = das1800_boards + board;
+ thisboard = dev->board_ptr;
+ dev->board_name = thisboard->name;
+
+ /* if it is an 'ao' board with fancy analog out then we need extra io ports */
+ if (thisboard->ao_ability == 2) {
+ unsigned long iobase2 = dev->iobase + IOBASE2;
+
+ ret = __comedi_request_region(dev, iobase2, DAS1800_SIZE);
+ if (ret)
+ return ret;
+ devpriv->iobase2 = iobase2;
+ }
+
+ if (irq == 3 || irq == 5 || irq == 7 || irq == 10 || irq == 11 ||
+ irq == 15) {
+ ret = request_irq(irq, das1800_interrupt, 0,
+ dev->board_name, dev);
+ if (ret == 0) {
+ dev->irq = irq;
+
+ switch (irq) {
+ case 3:
+ devpriv->irq_dma_bits |= 0x8;
+ break;
+ case 5:
+ devpriv->irq_dma_bits |= 0x10;
+ break;
+ case 7:
+ devpriv->irq_dma_bits |= 0x18;
+ break;
+ case 10:
+ devpriv->irq_dma_bits |= 0x28;
+ break;
+ case 11:
+ devpriv->irq_dma_bits |= 0x30;
+ break;
+ case 15:
+ devpriv->irq_dma_bits |= 0x38;
+ break;
+ }
+ }
+ }
+
+ /* an irq and one dma channel is required to use dma */
+ if (dev->irq & it->options[2])
+ das1800_init_dma(dev, it);
+
+ devpriv->fifo_buf = kmalloc_array(FIFO_SIZE, sizeof(uint16_t), GFP_KERNEL);
+ if (!devpriv->fifo_buf)
+ return -ENOMEM;
+
+ dev->pacer = comedi_8254_init(dev->iobase + DAS1800_COUNTER,
+ I8254_OSC_BASE_5MHZ, I8254_IO8, 0);
+ if (!dev->pacer)
+ return -ENOMEM;
+
+ ret = comedi_alloc_subdevices(dev, 4);
+ if (ret)
+ return ret;
+
+ /* analog input subdevice */
+ s = &dev->subdevices[0];
+ s->type = COMEDI_SUBD_AI;
+ s->subdev_flags = SDF_READABLE | SDF_DIFF | SDF_GROUND;
+ if (thisboard->common)
+ s->subdev_flags |= SDF_COMMON;
+ s->n_chan = thisboard->qram_len;
+ s->maxdata = (1 << thisboard->resolution) - 1;
+ s->range_table = thisboard->range_ai;
+ s->insn_read = das1800_ai_rinsn;
+ if (dev->irq) {
+ dev->read_subdev = s;
+ s->subdev_flags |= SDF_CMD_READ;
+ s->len_chanlist = s->n_chan;
+ s->do_cmd = das1800_ai_do_cmd;
+ s->do_cmdtest = das1800_ai_do_cmdtest;
+ s->poll = das1800_ai_poll;
+ s->cancel = das1800_cancel;
+ }
+
+ /* analog out */
+ s = &dev->subdevices[1];
+ if (thisboard->ao_ability == 1) {
+ s->type = COMEDI_SUBD_AO;
+ s->subdev_flags = SDF_WRITABLE;
+ s->n_chan = thisboard->ao_n_chan;
+ s->maxdata = (1 << thisboard->resolution) - 1;
+ s->range_table = &range_bipolar10;
+ s->insn_write = das1800_ao_winsn;
+ } else {
+ s->type = COMEDI_SUBD_UNUSED;
+ }
+
+ /* di */
+ s = &dev->subdevices[2];
+ s->type = COMEDI_SUBD_DI;
+ s->subdev_flags = SDF_READABLE;
+ s->n_chan = 4;
+ s->maxdata = 1;
+ s->range_table = &range_digital;
+ s->insn_bits = das1800_di_rbits;
+
+ /* do */
+ s = &dev->subdevices[3];
+ s->type = COMEDI_SUBD_DO;
+ s->subdev_flags = SDF_WRITABLE;
+ s->n_chan = thisboard->do_n_chan;
+ s->maxdata = 1;
+ s->range_table = &range_digital;
+ s->insn_bits = das1800_do_wbits;
+
+ das1800_cancel(dev, dev->read_subdev);
+
+ /* initialize digital out channels */
+ outb(0, dev->iobase + DAS1800_DIGITAL);
+
+ /* initialize analog out channels */
+ if (thisboard->ao_ability == 1) {
+ /* select 'update' dac channel for baseAddress + 0x0 */
+ outb(DAC(thisboard->ao_n_chan - 1),
+ dev->iobase + DAS1800_SELECT);
+ outw(devpriv->ao_update_bits, dev->iobase + DAS1800_DAC);
+ }
+
+ return 0;
+};
+
+static void das1800_detach(struct comedi_device *dev)
+{
+ struct das1800_private *devpriv = dev->private;
+
+ das1800_free_dma(dev);
+ if (devpriv) {
+ kfree(devpriv->fifo_buf);
+ if (devpriv->iobase2)
+ release_region(devpriv->iobase2, DAS1800_SIZE);
+ }
+ comedi_legacy_detach(dev);
+}
+
+static struct comedi_driver das1800_driver = {
+ .driver_name = "das1800",
+ .module = THIS_MODULE,
+ .attach = das1800_attach,
+ .detach = das1800_detach,
+ .num_names = ARRAY_SIZE(das1800_boards),
+ .board_name = &das1800_boards[0].name,
+ .offset = sizeof(struct das1800_board),
+};
+module_comedi_driver(das1800_driver);
+
+MODULE_AUTHOR("Comedi http://www.comedi.org");
+MODULE_DESCRIPTION("Comedi low-level driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/comedi/drivers/das6402.c b/drivers/staging/comedi/drivers/das6402.c
new file mode 100644
index 000000000..1701294b7
--- /dev/null
+++ b/drivers/staging/comedi/drivers/das6402.c
@@ -0,0 +1,676 @@
+/*
+ * das6402.c
+ * Comedi driver for DAS6402 compatible boards
+ * Copyright(c) 2014 H Hartley Sweeten <hsweeten@visionengravers.com>
+ *
+ * Rewrite of an experimental driver by:
+ * Copyright (C) 1999 Oystein Svendsen <svendsen@pvv.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+/*
+ * Driver: das6402
+ * Description: Keithley Metrabyte DAS6402 (& compatibles)
+ * Devices: [Keithley Metrabyte] DAS6402-12 (das6402-12),
+ * DAS6402-16 (das6402-16)
+ * Author: H Hartley Sweeten <hsweeten@visionengravers.com>
+ * Updated: Fri, 14 Mar 2014 10:18:43 -0700
+ * Status: unknown
+ *
+ * Configuration Options:
+ * [0] - I/O base address
+ * [1] - IRQ (optional, needed for async command support)
+ */
+
+#include <linux/module.h>
+#include <linux/interrupt.h>
+
+#include "../comedidev.h"
+
+#include "comedi_8254.h"
+
+/*
+ * Register I/O map
+ */
+#define DAS6402_AI_DATA_REG 0x00
+#define DAS6402_AI_MUX_REG 0x02
+#define DAS6402_AI_MUX_LO(x) (((x) & 0x3f) << 0)
+#define DAS6402_AI_MUX_HI(x) (((x) & 0x3f) << 8)
+#define DAS6402_DI_DO_REG 0x03
+#define DAS6402_AO_DATA_REG(x) (0x04 + ((x) * 2))
+#define DAS6402_AO_LSB_REG(x) (0x04 + ((x) * 2))
+#define DAS6402_AO_MSB_REG(x) (0x05 + ((x) * 2))
+#define DAS6402_STATUS_REG 0x08
+#define DAS6402_STATUS_FFNE (1 << 0)
+#define DAS6402_STATUS_FHALF (1 << 1)
+#define DAS6402_STATUS_FFULL (1 << 2)
+#define DAS6402_STATUS_XINT (1 << 3)
+#define DAS6402_STATUS_INT (1 << 4)
+#define DAS6402_STATUS_XTRIG (1 << 5)
+#define DAS6402_STATUS_INDGT (1 << 6)
+#define DAS6402_STATUS_10MHZ (1 << 7)
+#define DAS6402_STATUS_W_CLRINT (1 << 0)
+#define DAS6402_STATUS_W_CLRXTR (1 << 1)
+#define DAS6402_STATUS_W_CLRXIN (1 << 2)
+#define DAS6402_STATUS_W_EXTEND (1 << 4)
+#define DAS6402_STATUS_W_ARMED (1 << 5)
+#define DAS6402_STATUS_W_POSTMODE (1 << 6)
+#define DAS6402_STATUS_W_10MHZ (1 << 7)
+#define DAS6402_CTRL_REG 0x09
+#define DAS6402_CTRL_SOFT_TRIG (0 << 0)
+#define DAS6402_CTRL_EXT_FALL_TRIG (1 << 0)
+#define DAS6402_CTRL_EXT_RISE_TRIG (2 << 0)
+#define DAS6402_CTRL_PACER_TRIG (3 << 0)
+#define DAS6402_CTRL_BURSTEN (1 << 2)
+#define DAS6402_CTRL_XINTE (1 << 3)
+#define DAS6402_CTRL_IRQ(x) ((x) << 4)
+#define DAS6402_CTRL_INTE (1 << 7)
+#define DAS6402_TRIG_REG 0x0a
+#define DAS6402_TRIG_TGEN (1 << 0)
+#define DAS6402_TRIG_TGSEL (1 << 1)
+#define DAS6402_TRIG_TGPOL (1 << 2)
+#define DAS6402_TRIG_PRETRIG (1 << 3)
+#define DAS6402_AO_RANGE(_chan, _range) ((_range) << ((_chan) ? 6 : 4))
+#define DAS6402_AO_RANGE_MASK(_chan) (3 << ((_chan) ? 6 : 4))
+#define DAS6402_MODE_REG 0x0b
+#define DAS6402_MODE_RANGE(x) ((x) << 0)
+#define DAS6402_MODE_POLLED (0 << 2)
+#define DAS6402_MODE_FIFONEPTY (1 << 2)
+#define DAS6402_MODE_FIFOHFULL (2 << 2)
+#define DAS6402_MODE_EOB (3 << 2)
+#define DAS6402_MODE_ENHANCED (1 << 4)
+#define DAS6402_MODE_SE (1 << 5)
+#define DAS6402_MODE_UNI (1 << 6)
+#define DAS6402_MODE_DMA1 (0 << 7)
+#define DAS6402_MODE_DMA3 (1 << 7)
+#define DAS6402_TIMER_BASE 0x0c
+
+static const struct comedi_lrange das6402_ai_ranges = {
+ 8, {
+ BIP_RANGE(10),
+ BIP_RANGE(5),
+ BIP_RANGE(2.5),
+ BIP_RANGE(1.25),
+ UNI_RANGE(10),
+ UNI_RANGE(5),
+ UNI_RANGE(2.5),
+ UNI_RANGE(1.25)
+ }
+};
+
+/*
+ * Analog output ranges are programmable on the DAS6402/12.
+ * For the DAS6402/16 the range bits have no function, the
+ * DAC ranges are selected by switches on the board.
+ */
+static const struct comedi_lrange das6402_ao_ranges = {
+ 4, {
+ BIP_RANGE(5),
+ BIP_RANGE(10),
+ UNI_RANGE(5),
+ UNI_RANGE(10)
+ }
+};
+
+struct das6402_boardinfo {
+ const char *name;
+ unsigned int maxdata;
+};
+
+static struct das6402_boardinfo das6402_boards[] = {
+ {
+ .name = "das6402-12",
+ .maxdata = 0x0fff,
+ }, {
+ .name = "das6402-16",
+ .maxdata = 0xffff,
+ },
+};
+
+struct das6402_private {
+ unsigned int irq;
+ unsigned int ao_range;
+};
+
+static void das6402_set_mode(struct comedi_device *dev,
+ unsigned int mode)
+{
+ outb(DAS6402_MODE_ENHANCED | mode, dev->iobase + DAS6402_MODE_REG);
+}
+
+static void das6402_set_extended(struct comedi_device *dev,
+ unsigned int val)
+{
+ outb(DAS6402_STATUS_W_EXTEND, dev->iobase + DAS6402_STATUS_REG);
+ outb(DAS6402_STATUS_W_EXTEND | val, dev->iobase + DAS6402_STATUS_REG);
+ outb(val, dev->iobase + DAS6402_STATUS_REG);
+}
+
+static void das6402_clear_all_interrupts(struct comedi_device *dev)
+{
+ outb(DAS6402_STATUS_W_CLRINT |
+ DAS6402_STATUS_W_CLRXTR |
+ DAS6402_STATUS_W_CLRXIN, dev->iobase + DAS6402_STATUS_REG);
+}
+
+static void das6402_ai_clear_eoc(struct comedi_device *dev)
+{
+ outb(DAS6402_STATUS_W_CLRINT, dev->iobase + DAS6402_STATUS_REG);
+}
+
+static unsigned int das6402_ai_read_sample(struct comedi_device *dev,
+ struct comedi_subdevice *s)
+{
+ unsigned int val;
+
+ val = inw(dev->iobase + DAS6402_AI_DATA_REG);
+ if (s->maxdata == 0x0fff)
+ val >>= 4;
+ return val;
+}
+
+static irqreturn_t das6402_interrupt(int irq, void *d)
+{
+ struct comedi_device *dev = d;
+ struct comedi_subdevice *s = dev->read_subdev;
+ struct comedi_async *async = s->async;
+ struct comedi_cmd *cmd = &async->cmd;
+ unsigned int status;
+
+ status = inb(dev->iobase + DAS6402_STATUS_REG);
+ if ((status & DAS6402_STATUS_INT) == 0)
+ return IRQ_NONE;
+
+ if (status & DAS6402_STATUS_FFULL) {
+ async->events |= COMEDI_CB_OVERFLOW;
+ } else if (status & DAS6402_STATUS_FFNE) {
+ unsigned int val;
+
+ val = das6402_ai_read_sample(dev, s);
+ comedi_buf_write_samples(s, &val, 1);
+
+ if (cmd->stop_src == TRIG_COUNT &&
+ async->scans_done >= cmd->stop_arg)
+ async->events |= COMEDI_CB_EOA;
+ }
+
+ das6402_clear_all_interrupts(dev);
+
+ comedi_handle_events(dev, s);
+
+ return IRQ_HANDLED;
+}
+
+static void das6402_ai_set_mode(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ unsigned int chanspec,
+ unsigned int mode)
+{
+ unsigned int range = CR_RANGE(chanspec);
+ unsigned int aref = CR_AREF(chanspec);
+
+ mode |= DAS6402_MODE_RANGE(range);
+ if (aref == AREF_GROUND)
+ mode |= DAS6402_MODE_SE;
+ if (comedi_range_is_unipolar(s, range))
+ mode |= DAS6402_MODE_UNI;
+
+ das6402_set_mode(dev, mode);
+}
+
+static int das6402_ai_cmd(struct comedi_device *dev,
+ struct comedi_subdevice *s)
+{
+ struct das6402_private *devpriv = dev->private;
+ struct comedi_cmd *cmd = &s->async->cmd;
+ unsigned int chan_lo = CR_CHAN(cmd->chanlist[0]);
+ unsigned int chan_hi = CR_CHAN(cmd->chanlist[cmd->chanlist_len - 1]);
+
+ das6402_ai_set_mode(dev, s, cmd->chanlist[0], DAS6402_MODE_FIFONEPTY);
+
+ /* load the mux for chanlist conversion */
+ outw(DAS6402_AI_MUX_HI(chan_hi) | DAS6402_AI_MUX_LO(chan_lo),
+ dev->iobase + DAS6402_AI_MUX_REG);
+
+ comedi_8254_update_divisors(dev->pacer);
+ comedi_8254_pacer_enable(dev->pacer, 1, 2, true);
+
+ /* enable interrupt and pacer trigger */
+ outb(DAS6402_CTRL_INTE |
+ DAS6402_CTRL_IRQ(devpriv->irq) |
+ DAS6402_CTRL_PACER_TRIG, dev->iobase + DAS6402_CTRL_REG);
+
+ return 0;
+}
+
+static int das6402_ai_check_chanlist(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_cmd *cmd)
+{
+ unsigned int chan0 = CR_CHAN(cmd->chanlist[0]);
+ unsigned int range0 = CR_RANGE(cmd->chanlist[0]);
+ unsigned int aref0 = CR_AREF(cmd->chanlist[0]);
+ int i;
+
+ for (i = 1; i < cmd->chanlist_len; i++) {
+ unsigned int chan = CR_CHAN(cmd->chanlist[i]);
+ unsigned int range = CR_RANGE(cmd->chanlist[i]);
+ unsigned int aref = CR_AREF(cmd->chanlist[i]);
+
+ if (chan != chan0 + i) {
+ dev_dbg(dev->class_dev,
+ "chanlist must be consecutive\n");
+ return -EINVAL;
+ }
+
+ if (range != range0) {
+ dev_dbg(dev->class_dev,
+ "chanlist must have the same range\n");
+ return -EINVAL;
+ }
+
+ if (aref != aref0) {
+ dev_dbg(dev->class_dev,
+ "chanlist must have the same reference\n");
+ return -EINVAL;
+ }
+
+ if (aref0 == AREF_DIFF && chan > (s->n_chan / 2)) {
+ dev_dbg(dev->class_dev,
+ "chanlist differential channel to large\n");
+ return -EINVAL;
+ }
+ }
+ return 0;
+}
+
+static int das6402_ai_cmdtest(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_cmd *cmd)
+{
+ int err = 0;
+ unsigned int arg;
+
+ /* Step 1 : check if triggers are trivially valid */
+
+ err |= comedi_check_trigger_src(&cmd->start_src, TRIG_NOW);
+ err |= comedi_check_trigger_src(&cmd->scan_begin_src, TRIG_FOLLOW);
+ err |= comedi_check_trigger_src(&cmd->convert_src, TRIG_TIMER);
+ err |= comedi_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
+ err |= comedi_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE);
+
+ if (err)
+ return 1;
+
+ /* Step 2a : make sure trigger sources are unique */
+
+ err |= comedi_check_trigger_is_unique(cmd->stop_src);
+
+ /* Step 2b : and mutually compatible */
+
+ if (err)
+ return 2;
+
+ /* Step 3: check if arguments are trivially valid */
+
+ err |= comedi_check_trigger_arg_is(&cmd->start_arg, 0);
+ err |= comedi_check_trigger_arg_is(&cmd->scan_begin_arg, 0);
+ err |= comedi_check_trigger_arg_min(&cmd->convert_arg, 10000);
+ err |= comedi_check_trigger_arg_min(&cmd->chanlist_len, 1);
+ err |= comedi_check_trigger_arg_is(&cmd->scan_end_arg,
+ cmd->chanlist_len);
+
+ if (cmd->stop_src == TRIG_COUNT)
+ err |= comedi_check_trigger_arg_min(&cmd->stop_arg, 1);
+ else /* TRIG_NONE */
+ err |= comedi_check_trigger_arg_is(&cmd->stop_arg, 0);
+
+ if (err)
+ return 3;
+
+ /* step 4: fix up any arguments */
+
+ arg = cmd->convert_arg;
+ comedi_8254_cascade_ns_to_timer(dev->pacer, &arg, cmd->flags);
+ err |= comedi_check_trigger_arg_is(&cmd->convert_arg, arg);
+
+ if (err)
+ return 4;
+
+ /* Step 5: check channel list if it exists */
+ if (cmd->chanlist && cmd->chanlist_len > 0)
+ err |= das6402_ai_check_chanlist(dev, s, cmd);
+
+ if (err)
+ return 5;
+
+ return 0;
+}
+
+static int das6402_ai_cancel(struct comedi_device *dev,
+ struct comedi_subdevice *s)
+{
+ outb(DAS6402_CTRL_SOFT_TRIG, dev->iobase + DAS6402_CTRL_REG);
+
+ return 0;
+}
+
+static void das6402_ai_soft_trig(struct comedi_device *dev)
+{
+ outw(0, dev->iobase + DAS6402_AI_DATA_REG);
+}
+
+static int das6402_ai_eoc(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned long context)
+{
+ unsigned int status;
+
+ status = inb(dev->iobase + DAS6402_STATUS_REG);
+ if (status & DAS6402_STATUS_FFNE)
+ return 0;
+ return -EBUSY;
+}
+
+static int das6402_ai_insn_read(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ unsigned int chan = CR_CHAN(insn->chanspec);
+ unsigned int aref = CR_AREF(insn->chanspec);
+ int ret;
+ int i;
+
+ if (aref == AREF_DIFF && chan > (s->n_chan / 2))
+ return -EINVAL;
+
+ /* enable software conversion trigger */
+ outb(DAS6402_CTRL_SOFT_TRIG, dev->iobase + DAS6402_CTRL_REG);
+
+ das6402_ai_set_mode(dev, s, insn->chanspec, DAS6402_MODE_POLLED);
+
+ /* load the mux for single channel conversion */
+ outw(DAS6402_AI_MUX_HI(chan) | DAS6402_AI_MUX_LO(chan),
+ dev->iobase + DAS6402_AI_MUX_REG);
+
+ for (i = 0; i < insn->n; i++) {
+ das6402_ai_clear_eoc(dev);
+ das6402_ai_soft_trig(dev);
+
+ ret = comedi_timeout(dev, s, insn, das6402_ai_eoc, 0);
+ if (ret)
+ break;
+
+ data[i] = das6402_ai_read_sample(dev, s);
+ }
+
+ das6402_ai_clear_eoc(dev);
+
+ return insn->n;
+}
+
+static int das6402_ao_insn_write(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ struct das6402_private *devpriv = dev->private;
+ unsigned int chan = CR_CHAN(insn->chanspec);
+ unsigned int range = CR_RANGE(insn->chanspec);
+ unsigned int val;
+ int i;
+
+ /* set the range for this channel */
+ val = devpriv->ao_range;
+ val &= ~DAS6402_AO_RANGE_MASK(chan);
+ val |= DAS6402_AO_RANGE(chan, range);
+ if (val != devpriv->ao_range) {
+ devpriv->ao_range = val;
+ outb(val, dev->iobase + DAS6402_TRIG_REG);
+ }
+
+ /*
+ * The DAS6402/16 has a jumper to select either individual
+ * update (UPDATE) or simultaneous updating (XFER) of both
+ * DAC's. In UPDATE mode, when the MSB is written, that DAC
+ * is updated. In XFER mode, after both DAC's are loaded,
+ * a read cycle of any DAC register will update both DAC's
+ * simultaneously.
+ *
+ * If you have XFER mode enabled a (*insn_read) will need
+ * to be performed in order to update the DAC's with the
+ * last value written.
+ */
+ for (i = 0; i < insn->n; i++) {
+ val = data[i];
+
+ s->readback[chan] = val;
+
+ if (s->maxdata == 0x0fff) {
+ /*
+ * DAS6402/12 has the two 8-bit DAC registers, left
+ * justified (the 4 LSB bits are don't care). Data
+ * can be written as one word.
+ */
+ val <<= 4;
+ outw(val, dev->iobase + DAS6402_AO_DATA_REG(chan));
+ } else {
+ /*
+ * DAS6402/16 uses both 8-bit DAC registers and needs
+ * to be written LSB then MSB.
+ */
+ outb(val & 0xff,
+ dev->iobase + DAS6402_AO_LSB_REG(chan));
+ outb((val >> 8) & 0xff,
+ dev->iobase + DAS6402_AO_LSB_REG(chan));
+ }
+ }
+
+ return insn->n;
+}
+
+static int das6402_ao_insn_read(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ unsigned int chan = CR_CHAN(insn->chanspec);
+
+ /*
+ * If XFER mode is enabled, reading any DAC register
+ * will update both DAC's simultaneously.
+ */
+ inw(dev->iobase + DAS6402_AO_LSB_REG(chan));
+
+ return comedi_readback_insn_read(dev, s, insn, data);
+}
+
+static int das6402_di_insn_bits(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ data[1] = inb(dev->iobase + DAS6402_DI_DO_REG);
+
+ return insn->n;
+}
+
+static int das6402_do_insn_bits(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ if (comedi_dio_update_state(s, data))
+ outb(s->state, dev->iobase + DAS6402_DI_DO_REG);
+
+ data[1] = s->state;
+
+ return insn->n;
+}
+
+static void das6402_reset(struct comedi_device *dev)
+{
+ struct das6402_private *devpriv = dev->private;
+
+ /* enable "Enhanced" mode */
+ outb(DAS6402_MODE_ENHANCED, dev->iobase + DAS6402_MODE_REG);
+
+ /* enable 10MHz pacer clock */
+ das6402_set_extended(dev, DAS6402_STATUS_W_10MHZ);
+
+ /* enable software conversion trigger */
+ outb(DAS6402_CTRL_SOFT_TRIG, dev->iobase + DAS6402_CTRL_REG);
+
+ /* default ADC to single-ended unipolar 10V inputs */
+ das6402_set_mode(dev, DAS6402_MODE_RANGE(0) |
+ DAS6402_MODE_POLLED |
+ DAS6402_MODE_SE |
+ DAS6402_MODE_UNI);
+
+ /* default mux for single channel conversion (channel 0) */
+ outw(DAS6402_AI_MUX_HI(0) | DAS6402_AI_MUX_LO(0),
+ dev->iobase + DAS6402_AI_MUX_REG);
+
+ /* set both DAC's for unipolar 5V output range */
+ devpriv->ao_range = DAS6402_AO_RANGE(0, 2) | DAS6402_AO_RANGE(1, 2);
+ outb(devpriv->ao_range, dev->iobase + DAS6402_TRIG_REG);
+
+ /* set both DAC's to 0V */
+ outw(0, dev->iobase + DAS6402_AO_DATA_REG(0));
+ outw(0, dev->iobase + DAS6402_AO_DATA_REG(0));
+ inw(dev->iobase + DAS6402_AO_LSB_REG(0));
+
+ /* set all digital outputs low */
+ outb(0, dev->iobase + DAS6402_DI_DO_REG);
+
+ das6402_clear_all_interrupts(dev);
+}
+
+static int das6402_attach(struct comedi_device *dev,
+ struct comedi_devconfig *it)
+{
+ const struct das6402_boardinfo *board = dev->board_ptr;
+ struct das6402_private *devpriv;
+ struct comedi_subdevice *s;
+ int ret;
+
+ devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
+ if (!devpriv)
+ return -ENOMEM;
+
+ ret = comedi_request_region(dev, it->options[0], 0x10);
+ if (ret)
+ return ret;
+
+ das6402_reset(dev);
+
+ /* IRQs 2,3,5,6,7, 10,11,15 are valid for "enhanced" mode */
+ if ((1 << it->options[1]) & 0x8cec) {
+ ret = request_irq(it->options[1], das6402_interrupt, 0,
+ dev->board_name, dev);
+ if (ret == 0) {
+ dev->irq = it->options[1];
+
+ switch (dev->irq) {
+ case 10:
+ devpriv->irq = 4;
+ break;
+ case 11:
+ devpriv->irq = 1;
+ break;
+ case 15:
+ devpriv->irq = 6;
+ break;
+ default:
+ devpriv->irq = dev->irq;
+ break;
+ }
+ }
+ }
+
+ dev->pacer = comedi_8254_init(dev->iobase + DAS6402_TIMER_BASE,
+ I8254_OSC_BASE_10MHZ, I8254_IO8, 0);
+ if (!dev->pacer)
+ return -ENOMEM;
+
+ ret = comedi_alloc_subdevices(dev, 4);
+ if (ret)
+ return ret;
+
+ /* Analog Input subdevice */
+ s = &dev->subdevices[0];
+ s->type = COMEDI_SUBD_AI;
+ s->subdev_flags = SDF_READABLE | SDF_GROUND | SDF_DIFF;
+ s->n_chan = 64;
+ s->maxdata = board->maxdata;
+ s->range_table = &das6402_ai_ranges;
+ s->insn_read = das6402_ai_insn_read;
+ if (dev->irq) {
+ dev->read_subdev = s;
+ s->subdev_flags |= SDF_CMD_READ;
+ s->len_chanlist = s->n_chan;
+ s->do_cmdtest = das6402_ai_cmdtest;
+ s->do_cmd = das6402_ai_cmd;
+ s->cancel = das6402_ai_cancel;
+ }
+
+ /* Analog Output subdevice */
+ s = &dev->subdevices[1];
+ s->type = COMEDI_SUBD_AO;
+ s->subdev_flags = SDF_WRITABLE;
+ s->n_chan = 2;
+ s->maxdata = board->maxdata;
+ s->range_table = &das6402_ao_ranges;
+ s->insn_write = das6402_ao_insn_write;
+ s->insn_read = das6402_ao_insn_read;
+
+ ret = comedi_alloc_subdev_readback(s);
+ if (ret)
+ return ret;
+
+ /* Digital Input subdevice */
+ s = &dev->subdevices[2];
+ s->type = COMEDI_SUBD_DI;
+ s->subdev_flags = SDF_READABLE;
+ s->n_chan = 8;
+ s->maxdata = 1;
+ s->range_table = &range_digital;
+ s->insn_bits = das6402_di_insn_bits;
+
+ /* Digital Input subdevice */
+ s = &dev->subdevices[3];
+ s->type = COMEDI_SUBD_DO;
+ s->subdev_flags = SDF_WRITABLE;
+ s->n_chan = 8;
+ s->maxdata = 1;
+ s->range_table = &range_digital;
+ s->insn_bits = das6402_do_insn_bits;
+
+ return 0;
+}
+
+static struct comedi_driver das6402_driver = {
+ .driver_name = "das6402",
+ .module = THIS_MODULE,
+ .attach = das6402_attach,
+ .detach = comedi_legacy_detach,
+ .board_name = &das6402_boards[0].name,
+ .num_names = ARRAY_SIZE(das6402_boards),
+ .offset = sizeof(struct das6402_boardinfo),
+};
+module_comedi_driver(das6402_driver)
+
+MODULE_AUTHOR("H Hartley Sweeten <hsweeten@visionengravers.com>");
+MODULE_DESCRIPTION("Comedi driver for DAS6402 compatible boards");
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/comedi/drivers/das800.c b/drivers/staging/comedi/drivers/das800.c
new file mode 100644
index 000000000..39d304a12
--- /dev/null
+++ b/drivers/staging/comedi/drivers/das800.c
@@ -0,0 +1,748 @@
+/*
+ comedi/drivers/das800.c
+ Driver for Keitley das800 series boards and compatibles
+ Copyright (C) 2000 Frank Mori Hess <fmhess@users.sourceforge.net>
+
+ COMEDI - Linux Control and Measurement Device Interface
+ Copyright (C) 2000 David A. Schleef <ds@schleef.org>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+*/
+/*
+Driver: das800
+Description: Keithley Metrabyte DAS800 (& compatibles)
+Author: Frank Mori Hess <fmhess@users.sourceforge.net>
+Devices: [Keithley Metrabyte] DAS-800 (das-800), DAS-801 (das-801),
+ DAS-802 (das-802),
+ [Measurement Computing] CIO-DAS800 (cio-das800),
+ CIO-DAS801 (cio-das801), CIO-DAS802 (cio-das802),
+ CIO-DAS802/16 (cio-das802/16)
+Status: works, cio-das802/16 untested - email me if you have tested it
+
+Configuration options:
+ [0] - I/O port base address
+ [1] - IRQ (optional, required for timed or externally triggered conversions)
+
+Notes:
+ IRQ can be omitted, although the cmd interface will not work without it.
+
+ All entries in the channel/gain list must use the same gain and be
+ consecutive channels counting upwards in channel number (these are
+ hardware limitations.)
+
+ I've never tested the gain setting stuff since I only have a
+ DAS-800 board with fixed gain.
+
+ The cio-das802/16 does not have a fifo-empty status bit! Therefore
+ only fifo-half-full transfers are possible with this card.
+
+cmd triggers supported:
+ start_src: TRIG_NOW | TRIG_EXT
+ scan_begin_src: TRIG_FOLLOW
+ scan_end_src: TRIG_COUNT
+ convert_src: TRIG_TIMER | TRIG_EXT
+ stop_src: TRIG_NONE | TRIG_COUNT
+*/
+
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+
+#include "../comedidev.h"
+
+#include "comedi_8254.h"
+
+#define N_CHAN_AI 8 /* number of analog input channels */
+
+/* Registers for the das800 */
+
+#define DAS800_LSB 0
+#define FIFO_EMPTY 0x1
+#define FIFO_OVF 0x2
+#define DAS800_MSB 1
+#define DAS800_CONTROL1 2
+#define CONTROL1_INTE 0x8
+#define DAS800_CONV_CONTROL 2
+#define ITE 0x1
+#define CASC 0x2
+#define DTEN 0x4
+#define IEOC 0x8
+#define EACS 0x10
+#define CONV_HCEN 0x80
+#define DAS800_SCAN_LIMITS 2
+#define DAS800_STATUS 2
+#define IRQ 0x8
+#define BUSY 0x80
+#define DAS800_GAIN 3
+#define CIO_FFOV 0x8 /* cio-das802/16 fifo overflow */
+#define CIO_ENHF 0x90 /* cio-das802/16 fifo half full int ena */
+#define CONTROL1 0x80
+#define CONV_CONTROL 0xa0
+#define SCAN_LIMITS 0xc0
+#define ID 0xe0
+#define DAS800_8254 4
+#define DAS800_STATUS2 7
+#define STATUS2_HCEN 0x80
+#define STATUS2_INTE 0X20
+#define DAS800_ID 7
+
+#define DAS802_16_HALF_FIFO_SZ 128
+
+struct das800_board {
+ const char *name;
+ int ai_speed;
+ const struct comedi_lrange *ai_range;
+ int resolution;
+};
+
+static const struct comedi_lrange range_das801_ai = {
+ 9, {
+ BIP_RANGE(5),
+ BIP_RANGE(10),
+ UNI_RANGE(10),
+ BIP_RANGE(0.5),
+ UNI_RANGE(1),
+ BIP_RANGE(0.05),
+ UNI_RANGE(0.1),
+ BIP_RANGE(0.01),
+ UNI_RANGE(0.02)
+ }
+};
+
+static const struct comedi_lrange range_cio_das801_ai = {
+ 9, {
+ BIP_RANGE(5),
+ BIP_RANGE(10),
+ UNI_RANGE(10),
+ BIP_RANGE(0.5),
+ UNI_RANGE(1),
+ BIP_RANGE(0.05),
+ UNI_RANGE(0.1),
+ BIP_RANGE(0.005),
+ UNI_RANGE(0.01)
+ }
+};
+
+static const struct comedi_lrange range_das802_ai = {
+ 9, {
+ BIP_RANGE(5),
+ BIP_RANGE(10),
+ UNI_RANGE(10),
+ BIP_RANGE(2.5),
+ UNI_RANGE(5),
+ BIP_RANGE(1.25),
+ UNI_RANGE(2.5),
+ BIP_RANGE(0.625),
+ UNI_RANGE(1.25)
+ }
+};
+
+static const struct comedi_lrange range_das80216_ai = {
+ 8, {
+ BIP_RANGE(10),
+ UNI_RANGE(10),
+ BIP_RANGE(5),
+ UNI_RANGE(5),
+ BIP_RANGE(2.5),
+ UNI_RANGE(2.5),
+ BIP_RANGE(1.25),
+ UNI_RANGE(1.25)
+ }
+};
+
+enum das800_boardinfo {
+ BOARD_DAS800,
+ BOARD_CIODAS800,
+ BOARD_DAS801,
+ BOARD_CIODAS801,
+ BOARD_DAS802,
+ BOARD_CIODAS802,
+ BOARD_CIODAS80216,
+};
+
+static const struct das800_board das800_boards[] = {
+ [BOARD_DAS800] = {
+ .name = "das-800",
+ .ai_speed = 25000,
+ .ai_range = &range_bipolar5,
+ .resolution = 12,
+ },
+ [BOARD_CIODAS800] = {
+ .name = "cio-das800",
+ .ai_speed = 20000,
+ .ai_range = &range_bipolar5,
+ .resolution = 12,
+ },
+ [BOARD_DAS801] = {
+ .name = "das-801",
+ .ai_speed = 25000,
+ .ai_range = &range_das801_ai,
+ .resolution = 12,
+ },
+ [BOARD_CIODAS801] = {
+ .name = "cio-das801",
+ .ai_speed = 20000,
+ .ai_range = &range_cio_das801_ai,
+ .resolution = 12,
+ },
+ [BOARD_DAS802] = {
+ .name = "das-802",
+ .ai_speed = 25000,
+ .ai_range = &range_das802_ai,
+ .resolution = 12,
+ },
+ [BOARD_CIODAS802] = {
+ .name = "cio-das802",
+ .ai_speed = 20000,
+ .ai_range = &range_das802_ai,
+ .resolution = 12,
+ },
+ [BOARD_CIODAS80216] = {
+ .name = "cio-das802/16",
+ .ai_speed = 10000,
+ .ai_range = &range_das80216_ai,
+ .resolution = 16,
+ },
+};
+
+struct das800_private {
+ unsigned int do_bits; /* digital output bits */
+};
+
+static void das800_ind_write(struct comedi_device *dev,
+ unsigned val, unsigned reg)
+{
+ /*
+ * Select dev->iobase + 2 to be desired register
+ * then write to that register.
+ */
+ outb(reg, dev->iobase + DAS800_GAIN);
+ outb(val, dev->iobase + 2);
+}
+
+static unsigned das800_ind_read(struct comedi_device *dev, unsigned reg)
+{
+ /*
+ * Select dev->iobase + 7 to be desired register
+ * then read from that register.
+ */
+ outb(reg, dev->iobase + DAS800_GAIN);
+ return inb(dev->iobase + 7);
+}
+
+static void das800_enable(struct comedi_device *dev)
+{
+ const struct das800_board *thisboard = dev->board_ptr;
+ struct das800_private *devpriv = dev->private;
+ unsigned long irq_flags;
+
+ spin_lock_irqsave(&dev->spinlock, irq_flags);
+ /* enable fifo-half full interrupts for cio-das802/16 */
+ if (thisboard->resolution == 16)
+ outb(CIO_ENHF, dev->iobase + DAS800_GAIN);
+ /* enable hardware triggering */
+ das800_ind_write(dev, CONV_HCEN, CONV_CONTROL);
+ /* enable card's interrupt */
+ das800_ind_write(dev, CONTROL1_INTE | devpriv->do_bits, CONTROL1);
+ spin_unlock_irqrestore(&dev->spinlock, irq_flags);
+}
+
+static void das800_disable(struct comedi_device *dev)
+{
+ unsigned long irq_flags;
+
+ spin_lock_irqsave(&dev->spinlock, irq_flags);
+ /* disable hardware triggering of conversions */
+ das800_ind_write(dev, 0x0, CONV_CONTROL);
+ spin_unlock_irqrestore(&dev->spinlock, irq_flags);
+}
+
+static int das800_cancel(struct comedi_device *dev, struct comedi_subdevice *s)
+{
+ das800_disable(dev);
+ return 0;
+}
+
+static int das800_ai_check_chanlist(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_cmd *cmd)
+{
+ unsigned int chan0 = CR_CHAN(cmd->chanlist[0]);
+ unsigned int range0 = CR_RANGE(cmd->chanlist[0]);
+ int i;
+
+ for (i = 1; i < cmd->chanlist_len; i++) {
+ unsigned int chan = CR_CHAN(cmd->chanlist[i]);
+ unsigned int range = CR_RANGE(cmd->chanlist[i]);
+
+ if (chan != (chan0 + i) % s->n_chan) {
+ dev_dbg(dev->class_dev,
+ "chanlist must be consecutive, counting upwards\n");
+ return -EINVAL;
+ }
+
+ if (range != range0) {
+ dev_dbg(dev->class_dev,
+ "chanlist must all have the same gain\n");
+ return -EINVAL;
+ }
+ }
+
+ return 0;
+}
+
+static int das800_ai_do_cmdtest(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_cmd *cmd)
+{
+ const struct das800_board *thisboard = dev->board_ptr;
+ int err = 0;
+
+ /* Step 1 : check if triggers are trivially valid */
+
+ err |= comedi_check_trigger_src(&cmd->start_src, TRIG_NOW | TRIG_EXT);
+ err |= comedi_check_trigger_src(&cmd->scan_begin_src, TRIG_FOLLOW);
+ err |= comedi_check_trigger_src(&cmd->convert_src,
+ TRIG_TIMER | TRIG_EXT);
+ err |= comedi_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
+ err |= comedi_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE);
+
+ if (err)
+ return 1;
+
+ /* Step 2a : make sure trigger sources are unique */
+
+ err |= comedi_check_trigger_is_unique(cmd->start_src);
+ err |= comedi_check_trigger_is_unique(cmd->convert_src);
+ err |= comedi_check_trigger_is_unique(cmd->stop_src);
+
+ /* Step 2b : and mutually compatible */
+
+ if (err)
+ return 2;
+
+ /* Step 3: check if arguments are trivially valid */
+
+ err |= comedi_check_trigger_arg_is(&cmd->start_arg, 0);
+
+ if (cmd->convert_src == TRIG_TIMER) {
+ err |= comedi_check_trigger_arg_min(&cmd->convert_arg,
+ thisboard->ai_speed);
+ }
+
+ err |= comedi_check_trigger_arg_min(&cmd->chanlist_len, 1);
+ err |= comedi_check_trigger_arg_is(&cmd->scan_end_arg,
+ cmd->chanlist_len);
+
+ if (cmd->stop_src == TRIG_COUNT)
+ err |= comedi_check_trigger_arg_min(&cmd->stop_arg, 1);
+ else /* TRIG_NONE */
+ err |= comedi_check_trigger_arg_is(&cmd->stop_arg, 0);
+
+ if (err)
+ return 3;
+
+ /* step 4: fix up any arguments */
+
+ if (cmd->convert_src == TRIG_TIMER) {
+ unsigned int arg = cmd->convert_arg;
+
+ comedi_8254_cascade_ns_to_timer(dev->pacer, &arg, cmd->flags);
+ err |= comedi_check_trigger_arg_is(&cmd->convert_arg, arg);
+ }
+
+ if (err)
+ return 4;
+
+ /* Step 5: check channel list if it exists */
+ if (cmd->chanlist && cmd->chanlist_len > 0)
+ err |= das800_ai_check_chanlist(dev, s, cmd);
+
+ if (err)
+ return 5;
+
+ return 0;
+}
+
+static int das800_ai_do_cmd(struct comedi_device *dev,
+ struct comedi_subdevice *s)
+{
+ const struct das800_board *thisboard = dev->board_ptr;
+ struct comedi_async *async = s->async;
+ struct comedi_cmd *cmd = &async->cmd;
+ unsigned int gain = CR_RANGE(cmd->chanlist[0]);
+ unsigned int start_chan = CR_CHAN(cmd->chanlist[0]);
+ unsigned int end_chan = (start_chan + cmd->chanlist_len - 1) % 8;
+ unsigned int scan_chans = (end_chan << 3) | start_chan;
+ int conv_bits;
+ unsigned long irq_flags;
+
+ das800_disable(dev);
+
+ spin_lock_irqsave(&dev->spinlock, irq_flags);
+ /* set scan limits */
+ das800_ind_write(dev, scan_chans, SCAN_LIMITS);
+ spin_unlock_irqrestore(&dev->spinlock, irq_flags);
+
+ /* set gain */
+ if (thisboard->resolution == 12 && gain > 0)
+ gain += 0x7;
+ gain &= 0xf;
+ outb(gain, dev->iobase + DAS800_GAIN);
+
+ /* enable auto channel scan, send interrupts on end of conversion
+ * and set clock source to internal or external
+ */
+ conv_bits = 0;
+ conv_bits |= EACS | IEOC;
+ if (cmd->start_src == TRIG_EXT)
+ conv_bits |= DTEN;
+ if (cmd->convert_src == TRIG_TIMER) {
+ conv_bits |= CASC | ITE;
+ comedi_8254_update_divisors(dev->pacer);
+ comedi_8254_pacer_enable(dev->pacer, 1, 2, true);
+ }
+
+ spin_lock_irqsave(&dev->spinlock, irq_flags);
+ das800_ind_write(dev, conv_bits, CONV_CONTROL);
+ spin_unlock_irqrestore(&dev->spinlock, irq_flags);
+
+ das800_enable(dev);
+ return 0;
+}
+
+static unsigned int das800_ai_get_sample(struct comedi_device *dev)
+{
+ unsigned int lsb = inb(dev->iobase + DAS800_LSB);
+ unsigned int msb = inb(dev->iobase + DAS800_MSB);
+
+ return (msb << 8) | lsb;
+}
+
+static irqreturn_t das800_interrupt(int irq, void *d)
+{
+ struct comedi_device *dev = d;
+ struct das800_private *devpriv = dev->private;
+ struct comedi_subdevice *s = dev->read_subdev;
+ struct comedi_async *async;
+ struct comedi_cmd *cmd;
+ unsigned long irq_flags;
+ unsigned int status;
+ unsigned int val;
+ bool fifo_empty;
+ bool fifo_overflow;
+ int i;
+
+ status = inb(dev->iobase + DAS800_STATUS);
+ if (!(status & IRQ))
+ return IRQ_NONE;
+ if (!dev->attached)
+ return IRQ_HANDLED;
+
+ async = s->async;
+ cmd = &async->cmd;
+
+ spin_lock_irqsave(&dev->spinlock, irq_flags);
+ status = das800_ind_read(dev, CONTROL1) & STATUS2_HCEN;
+ /*
+ * Don't release spinlock yet since we want to make sure
+ * no one else disables hardware conversions.
+ */
+
+ /* if hardware conversions are not enabled, then quit */
+ if (status == 0) {
+ spin_unlock_irqrestore(&dev->spinlock, irq_flags);
+ return IRQ_HANDLED;
+ }
+
+ for (i = 0; i < DAS802_16_HALF_FIFO_SZ; i++) {
+ val = das800_ai_get_sample(dev);
+ if (s->maxdata == 0x0fff) {
+ fifo_empty = !!(val & FIFO_EMPTY);
+ fifo_overflow = !!(val & FIFO_OVF);
+ } else {
+ /* cio-das802/16 has no fifo empty status bit */
+ fifo_empty = false;
+ fifo_overflow = !!(inb(dev->iobase + DAS800_GAIN) &
+ CIO_FFOV);
+ }
+ if (fifo_empty || fifo_overflow)
+ break;
+
+ if (s->maxdata == 0x0fff)
+ val >>= 4; /* 12-bit sample */
+
+ val &= s->maxdata;
+ comedi_buf_write_samples(s, &val, 1);
+
+ if (cmd->stop_src == TRIG_COUNT &&
+ async->scans_done >= cmd->stop_arg) {
+ async->events |= COMEDI_CB_EOA;
+ break;
+ }
+ }
+
+ if (fifo_overflow) {
+ spin_unlock_irqrestore(&dev->spinlock, irq_flags);
+ async->events |= COMEDI_CB_ERROR;
+ comedi_handle_events(dev, s);
+ return IRQ_HANDLED;
+ }
+
+ if (!(async->events & COMEDI_CB_CANCEL_MASK)) {
+ /*
+ * Re-enable card's interrupt.
+ * We already have spinlock, so indirect addressing is safe
+ */
+ das800_ind_write(dev, CONTROL1_INTE | devpriv->do_bits,
+ CONTROL1);
+ spin_unlock_irqrestore(&dev->spinlock, irq_flags);
+ } else {
+ /* otherwise, stop taking data */
+ spin_unlock_irqrestore(&dev->spinlock, irq_flags);
+ das800_disable(dev);
+ }
+ comedi_handle_events(dev, s);
+ return IRQ_HANDLED;
+}
+
+static int das800_ai_eoc(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned long context)
+{
+ unsigned int status;
+
+ status = inb(dev->iobase + DAS800_STATUS);
+ if ((status & BUSY) == 0)
+ return 0;
+ return -EBUSY;
+}
+
+static int das800_ai_insn_read(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ struct das800_private *devpriv = dev->private;
+ unsigned int chan = CR_CHAN(insn->chanspec);
+ unsigned int range = CR_RANGE(insn->chanspec);
+ unsigned long irq_flags;
+ unsigned int val;
+ int ret;
+ int i;
+
+ das800_disable(dev);
+
+ /* set multiplexer */
+ spin_lock_irqsave(&dev->spinlock, irq_flags);
+ das800_ind_write(dev, chan | devpriv->do_bits, CONTROL1);
+ spin_unlock_irqrestore(&dev->spinlock, irq_flags);
+
+ /* set gain / range */
+ if (s->maxdata == 0x0fff && range)
+ range += 0x7;
+ range &= 0xf;
+ outb(range, dev->iobase + DAS800_GAIN);
+
+ udelay(5);
+
+ for (i = 0; i < insn->n; i++) {
+ /* trigger conversion */
+ outb_p(0, dev->iobase + DAS800_MSB);
+
+ ret = comedi_timeout(dev, s, insn, das800_ai_eoc, 0);
+ if (ret)
+ return ret;
+
+ val = das800_ai_get_sample(dev);
+ if (s->maxdata == 0x0fff)
+ val >>= 4; /* 12-bit sample */
+ data[i] = val & s->maxdata;
+ }
+
+ return insn->n;
+}
+
+static int das800_di_insn_bits(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ data[1] = (inb(dev->iobase + DAS800_STATUS) >> 4) & 0x7;
+
+ return insn->n;
+}
+
+static int das800_do_insn_bits(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ struct das800_private *devpriv = dev->private;
+ unsigned long irq_flags;
+
+ if (comedi_dio_update_state(s, data)) {
+ devpriv->do_bits = s->state << 4;
+
+ spin_lock_irqsave(&dev->spinlock, irq_flags);
+ das800_ind_write(dev, CONTROL1_INTE | devpriv->do_bits,
+ CONTROL1);
+ spin_unlock_irqrestore(&dev->spinlock, irq_flags);
+ }
+
+ data[1] = s->state;
+
+ return insn->n;
+}
+
+static int das800_probe(struct comedi_device *dev)
+{
+ const struct das800_board *thisboard = dev->board_ptr;
+ int board = thisboard ? thisboard - das800_boards : -EINVAL;
+ int id_bits;
+ unsigned long irq_flags;
+
+ spin_lock_irqsave(&dev->spinlock, irq_flags);
+ id_bits = das800_ind_read(dev, ID) & 0x3;
+ spin_unlock_irqrestore(&dev->spinlock, irq_flags);
+
+ switch (id_bits) {
+ case 0x0:
+ if (board == BOARD_DAS800 || board == BOARD_CIODAS800)
+ break;
+ dev_dbg(dev->class_dev, "Board model (probed): DAS-800\n");
+ board = BOARD_DAS800;
+ break;
+ case 0x2:
+ if (board == BOARD_DAS801 || board == BOARD_CIODAS801)
+ break;
+ dev_dbg(dev->class_dev, "Board model (probed): DAS-801\n");
+ board = BOARD_DAS801;
+ break;
+ case 0x3:
+ if (board == BOARD_DAS802 || board == BOARD_CIODAS802 ||
+ board == BOARD_CIODAS80216)
+ break;
+ dev_dbg(dev->class_dev, "Board model (probed): DAS-802\n");
+ board = BOARD_DAS802;
+ break;
+ default:
+ dev_dbg(dev->class_dev, "Board model: 0x%x (unknown)\n",
+ id_bits);
+ board = -EINVAL;
+ break;
+ }
+ return board;
+}
+
+static int das800_attach(struct comedi_device *dev, struct comedi_devconfig *it)
+{
+ const struct das800_board *thisboard;
+ struct das800_private *devpriv;
+ struct comedi_subdevice *s;
+ unsigned int irq = it->options[1];
+ unsigned long irq_flags;
+ int board;
+ int ret;
+
+ devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
+ if (!devpriv)
+ return -ENOMEM;
+
+ ret = comedi_request_region(dev, it->options[0], 0x8);
+ if (ret)
+ return ret;
+
+ board = das800_probe(dev);
+ if (board < 0) {
+ dev_dbg(dev->class_dev, "unable to determine board type\n");
+ return -ENODEV;
+ }
+ dev->board_ptr = das800_boards + board;
+ thisboard = dev->board_ptr;
+ dev->board_name = thisboard->name;
+
+ if (irq > 1 && irq <= 7) {
+ ret = request_irq(irq, das800_interrupt, 0, dev->board_name,
+ dev);
+ if (ret == 0)
+ dev->irq = irq;
+ }
+
+ dev->pacer = comedi_8254_init(dev->iobase + DAS800_8254,
+ I8254_OSC_BASE_1MHZ, I8254_IO8, 0);
+ if (!dev->pacer)
+ return -ENOMEM;
+
+ ret = comedi_alloc_subdevices(dev, 3);
+ if (ret)
+ return ret;
+
+ /* Analog Input subdevice */
+ s = &dev->subdevices[0];
+ dev->read_subdev = s;
+ s->type = COMEDI_SUBD_AI;
+ s->subdev_flags = SDF_READABLE | SDF_GROUND;
+ s->n_chan = 8;
+ s->maxdata = (1 << thisboard->resolution) - 1;
+ s->range_table = thisboard->ai_range;
+ s->insn_read = das800_ai_insn_read;
+ if (dev->irq) {
+ s->subdev_flags |= SDF_CMD_READ;
+ s->len_chanlist = 8;
+ s->do_cmdtest = das800_ai_do_cmdtest;
+ s->do_cmd = das800_ai_do_cmd;
+ s->cancel = das800_cancel;
+ }
+
+ /* Digital Input subdevice */
+ s = &dev->subdevices[1];
+ s->type = COMEDI_SUBD_DI;
+ s->subdev_flags = SDF_READABLE;
+ s->n_chan = 3;
+ s->maxdata = 1;
+ s->range_table = &range_digital;
+ s->insn_bits = das800_di_insn_bits;
+
+ /* Digital Output subdevice */
+ s = &dev->subdevices[2];
+ s->type = COMEDI_SUBD_DO;
+ s->subdev_flags = SDF_WRITABLE;
+ s->n_chan = 4;
+ s->maxdata = 1;
+ s->range_table = &range_digital;
+ s->insn_bits = das800_do_insn_bits;
+
+ das800_disable(dev);
+
+ /* initialize digital out channels */
+ spin_lock_irqsave(&dev->spinlock, irq_flags);
+ das800_ind_write(dev, CONTROL1_INTE | devpriv->do_bits, CONTROL1);
+ spin_unlock_irqrestore(&dev->spinlock, irq_flags);
+
+ return 0;
+};
+
+static struct comedi_driver driver_das800 = {
+ .driver_name = "das800",
+ .module = THIS_MODULE,
+ .attach = das800_attach,
+ .detach = comedi_legacy_detach,
+ .num_names = ARRAY_SIZE(das800_boards),
+ .board_name = &das800_boards[0].name,
+ .offset = sizeof(struct das800_board),
+};
+module_comedi_driver(driver_das800);
+
+MODULE_AUTHOR("Comedi http://www.comedi.org");
+MODULE_DESCRIPTION("Comedi low-level driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/comedi/drivers/dmm32at.c b/drivers/staging/comedi/drivers/dmm32at.c
new file mode 100644
index 000000000..bb2883c83
--- /dev/null
+++ b/drivers/staging/comedi/drivers/dmm32at.c
@@ -0,0 +1,627 @@
+/*
+ * dmm32at.c
+ * Diamond Systems Diamond-MM-32-AT Comedi driver
+ *
+ * COMEDI - Linux Control and Measurement Device Interface
+ * Copyright (C) 2000 David A. Schleef <ds@schleef.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+/*
+ * Driver: dmm32at
+ * Description: Diamond Systems Diamond-MM-32-AT
+ * Devices: [Diamond Systems] Diamond-MM-32-AT (dmm32at)
+ * Author: Perry J. Piplani <perry.j.piplani@nasa.gov>
+ * Updated: Fri Jun 4 09:13:24 CDT 2004
+ * Status: experimental
+ *
+ * Configuration Options:
+ * comedi_config /dev/comedi0 dmm32at baseaddr,irq
+ *
+ * This driver is for the Diamond Systems MM-32-AT board
+ * http://www.diamondsystems.com/products/diamondmm32at
+ *
+ * It is being used on several projects inside NASA, without
+ * problems so far. For analog input commands, TRIG_EXT is not
+ * yet supported.
+ */
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include "../comedidev.h"
+
+#include "8255.h"
+
+/* Board register addresses */
+#define DMM32AT_AI_START_CONV_REG 0x00
+#define DMM32AT_AI_LSB_REG 0x00
+#define DMM32AT_AUX_DOUT_REG 0x01
+#define DMM32AT_AUX_DOUT2 (1 << 2) /* J3.42 - OUT2 (OUT2EN) */
+#define DMM32AT_AUX_DOUT1 (1 << 1) /* J3.43 */
+#define DMM32AT_AUX_DOUT0 (1 << 0) /* J3.44 - OUT0 (OUT0EN) */
+#define DMM32AT_AI_MSB_REG 0x01
+#define DMM32AT_AI_LO_CHAN_REG 0x02
+#define DMM32AT_AI_HI_CHAN_REG 0x03
+#define DMM32AT_AUX_DI_REG 0x04
+#define DMM32AT_AUX_DI_DACBUSY (1 << 7)
+#define DMM32AT_AUX_DI_CALBUSY (1 << 6)
+#define DMM32AT_AUX_DI3 (1 << 3) /* J3.45 - ADCLK (CLKSEL) */
+#define DMM32AT_AUX_DI2 (1 << 2) /* J3.46 - GATE12 (GT12EN) */
+#define DMM32AT_AUX_DI1 (1 << 1) /* J3.47 - GATE0 (GT0EN) */
+#define DMM32AT_AUX_DI0 (1 << 0) /* J3.48 - CLK0 (SRC0) */
+#define DMM32AT_AO_LSB_REG 0x04
+#define DMM32AT_AO_MSB_REG 0x05
+#define DMM32AT_AO_MSB_DACH(x) ((x) << 6)
+#define DMM32AT_FIFO_DEPTH_REG 0x06
+#define DMM32AT_FIFO_CTRL_REG 0x07
+#define DMM32AT_FIFO_CTRL_FIFOEN (1 << 3)
+#define DMM32AT_FIFO_CTRL_SCANEN (1 << 2)
+#define DMM32AT_FIFO_CTRL_FIFORST (1 << 1)
+#define DMM32AT_FIFO_STATUS_REG 0x07
+#define DMM32AT_FIFO_STATUS_EF (1 << 7)
+#define DMM32AT_FIFO_STATUS_HF (1 << 6)
+#define DMM32AT_FIFO_STATUS_FF (1 << 5)
+#define DMM32AT_FIFO_STATUS_OVF (1 << 4)
+#define DMM32AT_FIFO_STATUS_FIFOEN (1 << 3)
+#define DMM32AT_FIFO_STATUS_SCANEN (1 << 2)
+#define DMM32AT_FIFO_STATUS_PAGE_MASK (3 << 0)
+#define DMM32AT_CTRL_REG 0x08
+#define DMM32AT_CTRL_RESETA (1 << 5)
+#define DMM32AT_CTRL_RESETD (1 << 4)
+#define DMM32AT_CTRL_INTRST (1 << 3)
+#define DMM32AT_CTRL_PAGE_8254 (0 << 0)
+#define DMM32AT_CTRL_PAGE_8255 (1 << 0)
+#define DMM32AT_CTRL_PAGE_CALIB (3 << 0)
+#define DMM32AT_AI_STATUS_REG 0x08
+#define DMM32AT_AI_STATUS_STS (1 << 7)
+#define DMM32AT_AI_STATUS_SD1 (1 << 6)
+#define DMM32AT_AI_STATUS_SD0 (1 << 5)
+#define DMM32AT_AI_STATUS_ADCH_MASK (0x1f << 0)
+#define DMM32AT_INTCLK_REG 0x09
+#define DMM32AT_INTCLK_ADINT (1 << 7)
+#define DMM32AT_INTCLK_DINT (1 << 6)
+#define DMM32AT_INTCLK_TINT (1 << 5)
+#define DMM32AT_INTCLK_CLKEN (1 << 1) /* 1=see below 0=software */
+#define DMM32AT_INTCLK_CLKSEL (1 << 0) /* 1=OUT2 0=EXTCLK */
+#define DMM32AT_CTRDIO_CFG_REG 0x0a
+#define DMM32AT_CTRDIO_CFG_FREQ12 (1 << 7) /* CLK12 1=100KHz 0=10MHz */
+#define DMM32AT_CTRDIO_CFG_FREQ0 (1 << 6) /* CLK0 1=10KHz 0=10MHz */
+#define DMM32AT_CTRDIO_CFG_OUT2EN (1 << 5) /* J3.42 1=OUT2 is DOUT2 */
+#define DMM32AT_CTRDIO_CFG_OUT0EN (1 << 4) /* J3,44 1=OUT0 is DOUT0 */
+#define DMM32AT_CTRDIO_CFG_GT0EN (1 << 2) /* J3.47 1=DIN1 is GATE0 */
+#define DMM32AT_CTRDIO_CFG_SRC0 (1 << 1) /* CLK0 is 0=FREQ0 1=J3.48 */
+#define DMM32AT_CTRDIO_CFG_GT12EN (1 << 0) /* J3.46 1=DIN2 is GATE12 */
+#define DMM32AT_AI_CFG_REG 0x0b
+#define DMM32AT_AI_CFG_SCINT_20US (0 << 4)
+#define DMM32AT_AI_CFG_SCINT_15US (1 << 4)
+#define DMM32AT_AI_CFG_SCINT_10US (2 << 4)
+#define DMM32AT_AI_CFG_SCINT_5US (3 << 4)
+#define DMM32AT_AI_CFG_RANGE (1 << 3) /* 0=5V 1=10V */
+#define DMM32AT_AI_CFG_ADBU (1 << 2) /* 0=bipolar 1=unipolar */
+#define DMM32AT_AI_CFG_GAIN(x) ((x) << 0)
+#define DMM32AT_AI_READBACK_REG 0x0b
+#define DMM32AT_AI_READBACK_WAIT (1 << 7) /* DMM32AT_AI_STATUS_STS */
+#define DMM32AT_AI_READBACK_RANGE (1 << 3)
+#define DMM32AT_AI_READBACK_ADBU (1 << 2)
+#define DMM32AT_AI_READBACK_GAIN_MASK (3 << 0)
+
+#define DMM32AT_CLK1 0x0d
+#define DMM32AT_CLK2 0x0e
+#define DMM32AT_CLKCT 0x0f
+
+#define DMM32AT_8255_IOBASE 0x0c /* Page 1 registers */
+
+/* Board register values. */
+
+/* DMM32AT_AI_CFG_REG 0x0b */
+#define DMM32AT_RANGE_U10 0x0c
+#define DMM32AT_RANGE_U5 0x0d
+#define DMM32AT_RANGE_B10 0x08
+#define DMM32AT_RANGE_B5 0x00
+
+/* DMM32AT_CLKCT 0x0f */
+#define DMM32AT_CLKCT1 0x56 /* mode3 counter 1 - write low byte only */
+#define DMM32AT_CLKCT2 0xb6 /* mode3 counter 2 - write high and low byte */
+
+/* board AI ranges in comedi structure */
+static const struct comedi_lrange dmm32at_airanges = {
+ 4, {
+ UNI_RANGE(10),
+ UNI_RANGE(5),
+ BIP_RANGE(10),
+ BIP_RANGE(5)
+ }
+};
+
+/* register values for above ranges */
+static const unsigned char dmm32at_rangebits[] = {
+ DMM32AT_RANGE_U10,
+ DMM32AT_RANGE_U5,
+ DMM32AT_RANGE_B10,
+ DMM32AT_RANGE_B5,
+};
+
+/* only one of these ranges is valid, as set by a jumper on the
+ * board. The application should only use the range set by the jumper
+ */
+static const struct comedi_lrange dmm32at_aoranges = {
+ 4, {
+ UNI_RANGE(10),
+ UNI_RANGE(5),
+ BIP_RANGE(10),
+ BIP_RANGE(5)
+ }
+};
+
+static void dmm32at_ai_set_chanspec(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ unsigned int chanspec, int nchan)
+{
+ unsigned int chan = CR_CHAN(chanspec);
+ unsigned int range = CR_RANGE(chanspec);
+ unsigned int last_chan = (chan + nchan - 1) % s->n_chan;
+
+ outb(DMM32AT_FIFO_CTRL_FIFORST, dev->iobase + DMM32AT_FIFO_CTRL_REG);
+
+ if (nchan > 1)
+ outb(DMM32AT_FIFO_CTRL_SCANEN,
+ dev->iobase + DMM32AT_FIFO_CTRL_REG);
+
+ outb(chan, dev->iobase + DMM32AT_AI_LO_CHAN_REG);
+ outb(last_chan, dev->iobase + DMM32AT_AI_HI_CHAN_REG);
+ outb(dmm32at_rangebits[range], dev->iobase + DMM32AT_AI_CFG_REG);
+}
+
+static unsigned int dmm32at_ai_get_sample(struct comedi_device *dev,
+ struct comedi_subdevice *s)
+{
+ unsigned int val;
+
+ val = inb(dev->iobase + DMM32AT_AI_LSB_REG);
+ val |= (inb(dev->iobase + DMM32AT_AI_MSB_REG) << 8);
+
+ /* munge two's complement value to offset binary */
+ return comedi_offset_munge(s, val);
+}
+
+static int dmm32at_ai_status(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned long context)
+{
+ unsigned char status;
+
+ status = inb(dev->iobase + context);
+ if ((status & DMM32AT_AI_STATUS_STS) == 0)
+ return 0;
+ return -EBUSY;
+}
+
+static int dmm32at_ai_insn_read(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ int ret;
+ int i;
+
+ dmm32at_ai_set_chanspec(dev, s, insn->chanspec, 1);
+
+ /* wait for circuit to settle */
+ ret = comedi_timeout(dev, s, insn, dmm32at_ai_status,
+ DMM32AT_AI_READBACK_REG);
+ if (ret)
+ return ret;
+
+ for (i = 0; i < insn->n; i++) {
+ outb(0xff, dev->iobase + DMM32AT_AI_START_CONV_REG);
+
+ ret = comedi_timeout(dev, s, insn, dmm32at_ai_status,
+ DMM32AT_AI_STATUS_REG);
+ if (ret)
+ return ret;
+
+ data[i] = dmm32at_ai_get_sample(dev, s);
+ }
+
+ return insn->n;
+}
+
+static int dmm32at_ai_check_chanlist(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_cmd *cmd)
+{
+ unsigned int chan0 = CR_CHAN(cmd->chanlist[0]);
+ unsigned int range0 = CR_RANGE(cmd->chanlist[0]);
+ int i;
+
+ for (i = 1; i < cmd->chanlist_len; i++) {
+ unsigned int chan = CR_CHAN(cmd->chanlist[i]);
+ unsigned int range = CR_RANGE(cmd->chanlist[i]);
+
+ if (chan != (chan0 + i) % s->n_chan) {
+ dev_dbg(dev->class_dev,
+ "entries in chanlist must be consecutive channels, counting upwards\n");
+ return -EINVAL;
+ }
+ if (range != range0) {
+ dev_dbg(dev->class_dev,
+ "entries in chanlist must all have the same gain\n");
+ return -EINVAL;
+ }
+ }
+
+ return 0;
+}
+
+static int dmm32at_ai_cmdtest(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_cmd *cmd)
+{
+ int err = 0;
+ unsigned int arg;
+
+ /* Step 1 : check if triggers are trivially valid */
+
+ err |= comedi_check_trigger_src(&cmd->start_src, TRIG_NOW);
+ err |= comedi_check_trigger_src(&cmd->scan_begin_src, TRIG_TIMER);
+ err |= comedi_check_trigger_src(&cmd->convert_src, TRIG_TIMER);
+ err |= comedi_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
+ err |= comedi_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE);
+
+ if (err)
+ return 1;
+
+ /* Step 2a : make sure trigger sources are unique */
+
+ err |= comedi_check_trigger_is_unique(cmd->stop_src);
+
+ /* Step 2b : and mutually compatible */
+
+ if (err)
+ return 2;
+
+ /* Step 3: check if arguments are trivially valid */
+
+ err |= comedi_check_trigger_arg_is(&cmd->start_arg, 0);
+
+ err |= comedi_check_trigger_arg_min(&cmd->scan_begin_arg, 1000000);
+ err |= comedi_check_trigger_arg_max(&cmd->scan_begin_arg, 1000000000);
+
+ if (cmd->convert_arg >= 17500)
+ cmd->convert_arg = 20000;
+ else if (cmd->convert_arg >= 12500)
+ cmd->convert_arg = 15000;
+ else if (cmd->convert_arg >= 7500)
+ cmd->convert_arg = 10000;
+ else
+ cmd->convert_arg = 5000;
+
+ err |= comedi_check_trigger_arg_is(&cmd->scan_end_arg,
+ cmd->chanlist_len);
+
+ if (cmd->stop_src == TRIG_COUNT)
+ err |= comedi_check_trigger_arg_min(&cmd->stop_arg, 1);
+ else /* TRIG_NONE */
+ err |= comedi_check_trigger_arg_is(&cmd->stop_arg, 0);
+
+ if (err)
+ return 3;
+
+ /* Step 4: fix up any arguments */
+
+ arg = cmd->convert_arg * cmd->scan_end_arg;
+ err |= comedi_check_trigger_arg_min(&cmd->scan_begin_arg, arg);
+
+ if (err)
+ return 4;
+
+ /* Step 5: check channel list if it exists */
+ if (cmd->chanlist && cmd->chanlist_len > 0)
+ err |= dmm32at_ai_check_chanlist(dev, s, cmd);
+
+ if (err)
+ return 5;
+
+ return 0;
+}
+
+static void dmm32at_setaitimer(struct comedi_device *dev, unsigned int nansec)
+{
+ unsigned char lo1, lo2, hi2;
+ unsigned short both2;
+
+ /* based on 10mhz clock */
+ lo1 = 200;
+ both2 = nansec / 20000;
+ hi2 = (both2 & 0xff00) >> 8;
+ lo2 = both2 & 0x00ff;
+
+ /* set counter clocks to 10MHz, disable all aux dio */
+ outb(0, dev->iobase + DMM32AT_CTRDIO_CFG_REG);
+
+ /* get access to the clock regs */
+ outb(DMM32AT_CTRL_PAGE_8254, dev->iobase + DMM32AT_CTRL_REG);
+
+ /* write the counter 1 control word and low byte to counter */
+ outb(DMM32AT_CLKCT1, dev->iobase + DMM32AT_CLKCT);
+ outb(lo1, dev->iobase + DMM32AT_CLK1);
+
+ /* write the counter 2 control word and low byte then to counter */
+ outb(DMM32AT_CLKCT2, dev->iobase + DMM32AT_CLKCT);
+ outb(lo2, dev->iobase + DMM32AT_CLK2);
+ outb(hi2, dev->iobase + DMM32AT_CLK2);
+
+ /* enable the ai conversion interrupt and the clock to start scans */
+ outb(DMM32AT_INTCLK_ADINT |
+ DMM32AT_INTCLK_CLKEN | DMM32AT_INTCLK_CLKSEL,
+ dev->iobase + DMM32AT_INTCLK_REG);
+}
+
+static int dmm32at_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
+{
+ struct comedi_cmd *cmd = &s->async->cmd;
+ int ret;
+
+ dmm32at_ai_set_chanspec(dev, s, cmd->chanlist[0], cmd->chanlist_len);
+
+ /* reset the interrupt just in case */
+ outb(DMM32AT_CTRL_INTRST, dev->iobase + DMM32AT_CTRL_REG);
+
+ /*
+ * wait for circuit to settle
+ * we don't have the 'insn' here but it's not needed
+ */
+ ret = comedi_timeout(dev, s, NULL, dmm32at_ai_status,
+ DMM32AT_AI_READBACK_REG);
+ if (ret)
+ return ret;
+
+ if (cmd->stop_src == TRIG_NONE || cmd->stop_arg > 1) {
+ /* start the clock and enable the interrupts */
+ dmm32at_setaitimer(dev, cmd->scan_begin_arg);
+ } else {
+ /* start the interrupts and initiate a single scan */
+ outb(DMM32AT_INTCLK_ADINT, dev->iobase + DMM32AT_INTCLK_REG);
+ outb(0xff, dev->iobase + DMM32AT_AI_START_CONV_REG);
+ }
+
+ return 0;
+}
+
+static int dmm32at_ai_cancel(struct comedi_device *dev,
+ struct comedi_subdevice *s)
+{
+ /* disable further interrupts and clocks */
+ outb(0x0, dev->iobase + DMM32AT_INTCLK_REG);
+ return 0;
+}
+
+static irqreturn_t dmm32at_isr(int irq, void *d)
+{
+ struct comedi_device *dev = d;
+ unsigned char intstat;
+ unsigned int val;
+ int i;
+
+ if (!dev->attached) {
+ dev_err(dev->class_dev, "spurious interrupt\n");
+ return IRQ_HANDLED;
+ }
+
+ intstat = inb(dev->iobase + DMM32AT_INTCLK_REG);
+
+ if (intstat & DMM32AT_INTCLK_ADINT) {
+ struct comedi_subdevice *s = dev->read_subdev;
+ struct comedi_cmd *cmd = &s->async->cmd;
+
+ for (i = 0; i < cmd->chanlist_len; i++) {
+ val = dmm32at_ai_get_sample(dev, s);
+ comedi_buf_write_samples(s, &val, 1);
+ }
+
+ if (cmd->stop_src == TRIG_COUNT &&
+ s->async->scans_done >= cmd->stop_arg)
+ s->async->events |= COMEDI_CB_EOA;
+
+ comedi_handle_events(dev, s);
+ }
+
+ /* reset the interrupt */
+ outb(DMM32AT_CTRL_INTRST, dev->iobase + DMM32AT_CTRL_REG);
+ return IRQ_HANDLED;
+}
+
+static int dmm32at_ao_eoc(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned long context)
+{
+ unsigned char status;
+
+ status = inb(dev->iobase + DMM32AT_AUX_DI_REG);
+ if ((status & DMM32AT_AUX_DI_DACBUSY) == 0)
+ return 0;
+ return -EBUSY;
+}
+
+static int dmm32at_ao_insn_write(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ unsigned int chan = CR_CHAN(insn->chanspec);
+ int i;
+
+ for (i = 0; i < insn->n; i++) {
+ unsigned int val = data[i];
+ int ret;
+
+ /* write LSB then MSB + chan to load DAC */
+ outb(val & 0xff, dev->iobase + DMM32AT_AO_LSB_REG);
+ outb((val >> 8) | DMM32AT_AO_MSB_DACH(chan),
+ dev->iobase + DMM32AT_AO_MSB_REG);
+
+ /* wait for circuit to settle */
+ ret = comedi_timeout(dev, s, insn, dmm32at_ao_eoc, 0);
+ if (ret)
+ return ret;
+
+ /* dummy read to update DAC */
+ inb(dev->iobase + DMM32AT_AO_MSB_REG);
+
+ s->readback[chan] = val;
+ }
+
+ return insn->n;
+}
+
+static int dmm32at_8255_io(struct comedi_device *dev,
+ int dir, int port, int data, unsigned long regbase)
+{
+ /* get access to the DIO regs */
+ outb(DMM32AT_CTRL_PAGE_8255, dev->iobase + DMM32AT_CTRL_REG);
+
+ if (dir) {
+ outb(data, dev->iobase + regbase + port);
+ return 0;
+ }
+ return inb(dev->iobase + regbase + port);
+}
+
+/* Make sure the board is there and put it to a known state */
+static int dmm32at_reset(struct comedi_device *dev)
+{
+ unsigned char aihi, ailo, fifostat, aistat, intstat, airback;
+
+ /* reset the board */
+ outb(DMM32AT_CTRL_RESETA, dev->iobase + DMM32AT_CTRL_REG);
+
+ /* allow a millisecond to reset */
+ udelay(1000);
+
+ /* zero scan and fifo control */
+ outb(0x0, dev->iobase + DMM32AT_FIFO_CTRL_REG);
+
+ /* zero interrupt and clock control */
+ outb(0x0, dev->iobase + DMM32AT_INTCLK_REG);
+
+ /* write a test channel range, the high 3 bits should drop */
+ outb(0x80, dev->iobase + DMM32AT_AI_LO_CHAN_REG);
+ outb(0xff, dev->iobase + DMM32AT_AI_HI_CHAN_REG);
+
+ /* set the range at 10v unipolar */
+ outb(DMM32AT_RANGE_U10, dev->iobase + DMM32AT_AI_CFG_REG);
+
+ /* should take 10 us to settle, here's a hundred */
+ udelay(100);
+
+ /* read back the values */
+ ailo = inb(dev->iobase + DMM32AT_AI_LO_CHAN_REG);
+ aihi = inb(dev->iobase + DMM32AT_AI_HI_CHAN_REG);
+ fifostat = inb(dev->iobase + DMM32AT_FIFO_STATUS_REG);
+ aistat = inb(dev->iobase + DMM32AT_AI_STATUS_REG);
+ intstat = inb(dev->iobase + DMM32AT_INTCLK_REG);
+ airback = inb(dev->iobase + DMM32AT_AI_READBACK_REG);
+
+ /*
+ * NOTE: The (DMM32AT_AI_STATUS_SD1 | DMM32AT_AI_STATUS_SD0)
+ * test makes this driver only work if the board is configured
+ * with all A/D channels set for single-ended operation.
+ */
+ if (ailo != 0x00 || aihi != 0x1f ||
+ fifostat != DMM32AT_FIFO_STATUS_EF ||
+ aistat != (DMM32AT_AI_STATUS_SD1 | DMM32AT_AI_STATUS_SD0) ||
+ intstat != 0x00 || airback != 0x0c)
+ return -EIO;
+
+ return 0;
+}
+
+static int dmm32at_attach(struct comedi_device *dev,
+ struct comedi_devconfig *it)
+{
+ struct comedi_subdevice *s;
+ int ret;
+
+ ret = comedi_request_region(dev, it->options[0], 0x10);
+ if (ret)
+ return ret;
+
+ ret = dmm32at_reset(dev);
+ if (ret) {
+ dev_err(dev->class_dev, "board detection failed\n");
+ return ret;
+ }
+
+ if (it->options[1]) {
+ ret = request_irq(it->options[1], dmm32at_isr, 0,
+ dev->board_name, dev);
+ if (ret == 0)
+ dev->irq = it->options[1];
+ }
+
+ ret = comedi_alloc_subdevices(dev, 3);
+ if (ret)
+ return ret;
+
+ /* Analog Input subdevice */
+ s = &dev->subdevices[0];
+ s->type = COMEDI_SUBD_AI;
+ s->subdev_flags = SDF_READABLE | SDF_GROUND | SDF_DIFF;
+ s->n_chan = 32;
+ s->maxdata = 0xffff;
+ s->range_table = &dmm32at_airanges;
+ s->insn_read = dmm32at_ai_insn_read;
+ if (dev->irq) {
+ dev->read_subdev = s;
+ s->subdev_flags |= SDF_CMD_READ;
+ s->len_chanlist = s->n_chan;
+ s->do_cmd = dmm32at_ai_cmd;
+ s->do_cmdtest = dmm32at_ai_cmdtest;
+ s->cancel = dmm32at_ai_cancel;
+ }
+
+ /* Analog Output subdevice */
+ s = &dev->subdevices[1];
+ s->type = COMEDI_SUBD_AO;
+ s->subdev_flags = SDF_WRITABLE;
+ s->n_chan = 4;
+ s->maxdata = 0x0fff;
+ s->range_table = &dmm32at_aoranges;
+ s->insn_write = dmm32at_ao_insn_write;
+
+ ret = comedi_alloc_subdev_readback(s);
+ if (ret)
+ return ret;
+
+ /* Digital I/O subdevice */
+ s = &dev->subdevices[2];
+ ret = subdev_8255_init(dev, s, dmm32at_8255_io, DMM32AT_8255_IOBASE);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static struct comedi_driver dmm32at_driver = {
+ .driver_name = "dmm32at",
+ .module = THIS_MODULE,
+ .attach = dmm32at_attach,
+ .detach = comedi_legacy_detach,
+};
+module_comedi_driver(dmm32at_driver);
+
+MODULE_AUTHOR("Comedi http://www.comedi.org");
+MODULE_DESCRIPTION("Comedi: Diamond Systems Diamond-MM-32-AT");
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/comedi/drivers/dt2801.c b/drivers/staging/comedi/drivers/dt2801.c
new file mode 100644
index 000000000..80e38dedd
--- /dev/null
+++ b/drivers/staging/comedi/drivers/dt2801.c
@@ -0,0 +1,639 @@
+/*
+ * comedi/drivers/dt2801.c
+ * Device Driver for DataTranslation DT2801
+ *
+ */
+/*
+Driver: dt2801
+Description: Data Translation DT2801 series and DT01-EZ
+Author: ds
+Status: works
+Devices: [Data Translation] DT2801 (dt2801), DT2801-A, DT2801/5716A,
+ DT2805, DT2805/5716A, DT2808, DT2818, DT2809, DT01-EZ
+
+This driver can autoprobe the type of board.
+
+Configuration options:
+ [0] - I/O port base address
+ [1] - unused
+ [2] - A/D reference 0=differential, 1=single-ended
+ [3] - A/D range
+ 0 = [-10, 10]
+ 1 = [0,10]
+ [4] - D/A 0 range
+ 0 = [-10, 10]
+ 1 = [-5,5]
+ 2 = [-2.5,2.5]
+ 3 = [0,10]
+ 4 = [0,5]
+ [5] - D/A 1 range (same choices)
+*/
+
+#include <linux/module.h>
+#include "../comedidev.h"
+#include <linux/delay.h>
+
+#define DT2801_TIMEOUT 1000
+
+/* Hardware Configuration */
+/* ====================== */
+
+#define DT2801_MAX_DMA_SIZE (64 * 1024)
+
+/* define's */
+/* ====================== */
+
+/* Commands */
+#define DT_C_RESET 0x0
+#define DT_C_CLEAR_ERR 0x1
+#define DT_C_READ_ERRREG 0x2
+#define DT_C_SET_CLOCK 0x3
+
+#define DT_C_TEST 0xb
+#define DT_C_STOP 0xf
+
+#define DT_C_SET_DIGIN 0x4
+#define DT_C_SET_DIGOUT 0x5
+#define DT_C_READ_DIG 0x6
+#define DT_C_WRITE_DIG 0x7
+
+#define DT_C_WRITE_DAIM 0x8
+#define DT_C_SET_DA 0x9
+#define DT_C_WRITE_DA 0xa
+
+#define DT_C_READ_ADIM 0xc
+#define DT_C_SET_AD 0xd
+#define DT_C_READ_AD 0xe
+
+/* Command modifiers (only used with read/write), EXTTRIG can be
+ used with some other commands.
+*/
+#define DT_MOD_DMA (1<<4)
+#define DT_MOD_CONT (1<<5)
+#define DT_MOD_EXTCLK (1<<6)
+#define DT_MOD_EXTTRIG (1<<7)
+
+/* Bits in status register */
+#define DT_S_DATA_OUT_READY (1<<0)
+#define DT_S_DATA_IN_FULL (1<<1)
+#define DT_S_READY (1<<2)
+#define DT_S_COMMAND (1<<3)
+#define DT_S_COMPOSITE_ERROR (1<<7)
+
+/* registers */
+#define DT2801_DATA 0
+#define DT2801_STATUS 1
+#define DT2801_CMD 1
+
+#if 0
+/* ignore 'defined but not used' warning */
+static const struct comedi_lrange range_dt2801_ai_pgh_bipolar = {
+ 4, {
+ BIP_RANGE(10),
+ BIP_RANGE(5),
+ BIP_RANGE(2.5),
+ BIP_RANGE(1.25)
+ }
+};
+#endif
+static const struct comedi_lrange range_dt2801_ai_pgl_bipolar = {
+ 4, {
+ BIP_RANGE(10),
+ BIP_RANGE(1),
+ BIP_RANGE(0.1),
+ BIP_RANGE(0.02)
+ }
+};
+
+#if 0
+/* ignore 'defined but not used' warning */
+static const struct comedi_lrange range_dt2801_ai_pgh_unipolar = {
+ 4, {
+ UNI_RANGE(10),
+ UNI_RANGE(5),
+ UNI_RANGE(2.5),
+ UNI_RANGE(1.25)
+ }
+};
+#endif
+static const struct comedi_lrange range_dt2801_ai_pgl_unipolar = {
+ 4, {
+ UNI_RANGE(10),
+ UNI_RANGE(1),
+ UNI_RANGE(0.1),
+ UNI_RANGE(0.02)
+ }
+};
+
+struct dt2801_board {
+ const char *name;
+ int boardcode;
+ int ad_diff;
+ int ad_chan;
+ int adbits;
+ int adrangetype;
+ int dabits;
+};
+
+/* Typeid's for the different boards of the DT2801-series
+ (taken from the test-software, that comes with the board)
+ */
+static const struct dt2801_board boardtypes[] = {
+ {
+ .name = "dt2801",
+ .boardcode = 0x09,
+ .ad_diff = 2,
+ .ad_chan = 16,
+ .adbits = 12,
+ .adrangetype = 0,
+ .dabits = 12},
+ {
+ .name = "dt2801-a",
+ .boardcode = 0x52,
+ .ad_diff = 2,
+ .ad_chan = 16,
+ .adbits = 12,
+ .adrangetype = 0,
+ .dabits = 12},
+ {
+ .name = "dt2801/5716a",
+ .boardcode = 0x82,
+ .ad_diff = 1,
+ .ad_chan = 16,
+ .adbits = 16,
+ .adrangetype = 1,
+ .dabits = 12},
+ {
+ .name = "dt2805",
+ .boardcode = 0x12,
+ .ad_diff = 1,
+ .ad_chan = 16,
+ .adbits = 12,
+ .adrangetype = 0,
+ .dabits = 12},
+ {
+ .name = "dt2805/5716a",
+ .boardcode = 0x92,
+ .ad_diff = 1,
+ .ad_chan = 16,
+ .adbits = 16,
+ .adrangetype = 1,
+ .dabits = 12},
+ {
+ .name = "dt2808",
+ .boardcode = 0x20,
+ .ad_diff = 0,
+ .ad_chan = 16,
+ .adbits = 12,
+ .adrangetype = 2,
+ .dabits = 8},
+ {
+ .name = "dt2818",
+ .boardcode = 0xa2,
+ .ad_diff = 0,
+ .ad_chan = 4,
+ .adbits = 12,
+ .adrangetype = 0,
+ .dabits = 12},
+ {
+ .name = "dt2809",
+ .boardcode = 0xb0,
+ .ad_diff = 0,
+ .ad_chan = 8,
+ .adbits = 12,
+ .adrangetype = 1,
+ .dabits = 12},
+};
+
+struct dt2801_private {
+ const struct comedi_lrange *dac_range_types[2];
+};
+
+/* These are the low-level routines:
+ writecommand: write a command to the board
+ writedata: write data byte
+ readdata: read data byte
+ */
+
+/* Only checks DataOutReady-flag, not the Ready-flag as it is done
+ in the examples of the manual. I don't see why this should be
+ necessary. */
+static int dt2801_readdata(struct comedi_device *dev, int *data)
+{
+ int stat = 0;
+ int timeout = DT2801_TIMEOUT;
+
+ do {
+ stat = inb_p(dev->iobase + DT2801_STATUS);
+ if (stat & (DT_S_COMPOSITE_ERROR | DT_S_READY))
+ return stat;
+ if (stat & DT_S_DATA_OUT_READY) {
+ *data = inb_p(dev->iobase + DT2801_DATA);
+ return 0;
+ }
+ } while (--timeout > 0);
+
+ return -ETIME;
+}
+
+static int dt2801_readdata2(struct comedi_device *dev, int *data)
+{
+ int lb = 0;
+ int hb = 0;
+ int ret;
+
+ ret = dt2801_readdata(dev, &lb);
+ if (ret)
+ return ret;
+ ret = dt2801_readdata(dev, &hb);
+ if (ret)
+ return ret;
+
+ *data = (hb << 8) + lb;
+ return 0;
+}
+
+static int dt2801_writedata(struct comedi_device *dev, unsigned int data)
+{
+ int stat = 0;
+ int timeout = DT2801_TIMEOUT;
+
+ do {
+ stat = inb_p(dev->iobase + DT2801_STATUS);
+
+ if (stat & DT_S_COMPOSITE_ERROR)
+ return stat;
+ if (!(stat & DT_S_DATA_IN_FULL)) {
+ outb_p(data & 0xff, dev->iobase + DT2801_DATA);
+ return 0;
+ }
+ } while (--timeout > 0);
+
+ return -ETIME;
+}
+
+static int dt2801_writedata2(struct comedi_device *dev, unsigned int data)
+{
+ int ret;
+
+ ret = dt2801_writedata(dev, data & 0xff);
+ if (ret < 0)
+ return ret;
+ ret = dt2801_writedata(dev, data >> 8);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static int dt2801_wait_for_ready(struct comedi_device *dev)
+{
+ int timeout = DT2801_TIMEOUT;
+ int stat;
+
+ stat = inb_p(dev->iobase + DT2801_STATUS);
+ if (stat & DT_S_READY)
+ return 0;
+ do {
+ stat = inb_p(dev->iobase + DT2801_STATUS);
+
+ if (stat & DT_S_COMPOSITE_ERROR)
+ return stat;
+ if (stat & DT_S_READY)
+ return 0;
+ } while (--timeout > 0);
+
+ return -ETIME;
+}
+
+static void dt2801_writecmd(struct comedi_device *dev, int command)
+{
+ int stat;
+
+ dt2801_wait_for_ready(dev);
+
+ stat = inb_p(dev->iobase + DT2801_STATUS);
+ if (stat & DT_S_COMPOSITE_ERROR) {
+ dev_dbg(dev->class_dev,
+ "composite-error in %s, ignoring\n", __func__);
+ }
+ if (!(stat & DT_S_READY))
+ dev_dbg(dev->class_dev, "!ready in %s, ignoring\n", __func__);
+ outb_p(command, dev->iobase + DT2801_CMD);
+}
+
+static int dt2801_reset(struct comedi_device *dev)
+{
+ int board_code = 0;
+ unsigned int stat;
+ int timeout;
+
+ /* pull random data from data port */
+ inb_p(dev->iobase + DT2801_DATA);
+ inb_p(dev->iobase + DT2801_DATA);
+ inb_p(dev->iobase + DT2801_DATA);
+ inb_p(dev->iobase + DT2801_DATA);
+
+ /* dt2801_writecmd(dev,DT_C_STOP); */
+ outb_p(DT_C_STOP, dev->iobase + DT2801_CMD);
+
+ /* dt2801_wait_for_ready(dev); */
+ udelay(100);
+ timeout = 10000;
+ do {
+ stat = inb_p(dev->iobase + DT2801_STATUS);
+ if (stat & DT_S_READY)
+ break;
+ } while (timeout--);
+ if (!timeout)
+ dev_dbg(dev->class_dev, "timeout 1 status=0x%02x\n", stat);
+
+ /* dt2801_readdata(dev,&board_code); */
+
+ outb_p(DT_C_RESET, dev->iobase + DT2801_CMD);
+ /* dt2801_writecmd(dev,DT_C_RESET); */
+
+ udelay(100);
+ timeout = 10000;
+ do {
+ stat = inb_p(dev->iobase + DT2801_STATUS);
+ if (stat & DT_S_READY)
+ break;
+ } while (timeout--);
+ if (!timeout)
+ dev_dbg(dev->class_dev, "timeout 2 status=0x%02x\n", stat);
+
+ dt2801_readdata(dev, &board_code);
+
+ return board_code;
+}
+
+static int probe_number_of_ai_chans(struct comedi_device *dev)
+{
+ int n_chans;
+ int stat;
+ int data;
+
+ for (n_chans = 0; n_chans < 16; n_chans++) {
+ dt2801_writecmd(dev, DT_C_READ_ADIM);
+ dt2801_writedata(dev, 0);
+ dt2801_writedata(dev, n_chans);
+ stat = dt2801_readdata2(dev, &data);
+
+ if (stat)
+ break;
+ }
+
+ dt2801_reset(dev);
+ dt2801_reset(dev);
+
+ return n_chans;
+}
+
+static const struct comedi_lrange *dac_range_table[] = {
+ &range_bipolar10,
+ &range_bipolar5,
+ &range_bipolar2_5,
+ &range_unipolar10,
+ &range_unipolar5
+};
+
+static const struct comedi_lrange *dac_range_lkup(int opt)
+{
+ if (opt < 0 || opt >= 5)
+ return &range_unknown;
+ return dac_range_table[opt];
+}
+
+static const struct comedi_lrange *ai_range_lkup(int type, int opt)
+{
+ switch (type) {
+ case 0:
+ return (opt) ?
+ &range_dt2801_ai_pgl_unipolar :
+ &range_dt2801_ai_pgl_bipolar;
+ case 1:
+ return (opt) ? &range_unipolar10 : &range_bipolar10;
+ case 2:
+ return &range_unipolar5;
+ }
+ return &range_unknown;
+}
+
+static int dt2801_error(struct comedi_device *dev, int stat)
+{
+ if (stat < 0) {
+ if (stat == -ETIME)
+ dev_dbg(dev->class_dev, "timeout\n");
+ else
+ dev_dbg(dev->class_dev, "error %d\n", stat);
+ return stat;
+ }
+ dev_dbg(dev->class_dev, "error status 0x%02x, resetting...\n", stat);
+
+ dt2801_reset(dev);
+ dt2801_reset(dev);
+
+ return -EIO;
+}
+
+static int dt2801_ai_insn_read(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn, unsigned int *data)
+{
+ int d;
+ int stat;
+ int i;
+
+ for (i = 0; i < insn->n; i++) {
+ dt2801_writecmd(dev, DT_C_READ_ADIM);
+ dt2801_writedata(dev, CR_RANGE(insn->chanspec));
+ dt2801_writedata(dev, CR_CHAN(insn->chanspec));
+ stat = dt2801_readdata2(dev, &d);
+
+ if (stat != 0)
+ return dt2801_error(dev, stat);
+
+ data[i] = d;
+ }
+
+ return i;
+}
+
+static int dt2801_ao_insn_write(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ unsigned int chan = CR_CHAN(insn->chanspec);
+
+ dt2801_writecmd(dev, DT_C_WRITE_DAIM);
+ dt2801_writedata(dev, chan);
+ dt2801_writedata2(dev, data[0]);
+
+ s->readback[chan] = data[0];
+
+ return 1;
+}
+
+static int dt2801_dio_insn_bits(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ int which = (s == &dev->subdevices[3]) ? 1 : 0;
+ unsigned int val = 0;
+
+ if (comedi_dio_update_state(s, data)) {
+ dt2801_writecmd(dev, DT_C_WRITE_DIG);
+ dt2801_writedata(dev, which);
+ dt2801_writedata(dev, s->state);
+ }
+
+ dt2801_writecmd(dev, DT_C_READ_DIG);
+ dt2801_writedata(dev, which);
+ dt2801_readdata(dev, &val);
+
+ data[1] = val;
+
+ return insn->n;
+}
+
+static int dt2801_dio_insn_config(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ int ret;
+
+ ret = comedi_dio_insn_config(dev, s, insn, data, 0xff);
+ if (ret)
+ return ret;
+
+ dt2801_writecmd(dev, s->io_bits ? DT_C_SET_DIGOUT : DT_C_SET_DIGIN);
+ dt2801_writedata(dev, (s == &dev->subdevices[3]) ? 1 : 0);
+
+ return insn->n;
+}
+
+/*
+ options:
+ [0] - i/o base
+ [1] - unused
+ [2] - a/d 0=differential, 1=single-ended
+ [3] - a/d range 0=[-10,10], 1=[0,10]
+ [4] - dac0 range 0=[-10,10], 1=[-5,5], 2=[-2.5,2.5] 3=[0,10], 4=[0,5]
+ [5] - dac1 range 0=[-10,10], 1=[-5,5], 2=[-2.5,2.5] 3=[0,10], 4=[0,5]
+*/
+static int dt2801_attach(struct comedi_device *dev, struct comedi_devconfig *it)
+{
+ const struct dt2801_board *board;
+ struct dt2801_private *devpriv;
+ struct comedi_subdevice *s;
+ int board_code, type;
+ int ret = 0;
+ int n_ai_chans;
+
+ ret = comedi_request_region(dev, it->options[0], 0x2);
+ if (ret)
+ return ret;
+
+ /* do some checking */
+
+ board_code = dt2801_reset(dev);
+
+ /* heh. if it didn't work, try it again. */
+ if (!board_code)
+ board_code = dt2801_reset(dev);
+
+ for (type = 0; type < ARRAY_SIZE(boardtypes); type++) {
+ if (boardtypes[type].boardcode == board_code)
+ goto havetype;
+ }
+ dev_dbg(dev->class_dev,
+ "unrecognized board code=0x%02x, contact author\n", board_code);
+ type = 0;
+
+havetype:
+ dev->board_ptr = boardtypes + type;
+ board = dev->board_ptr;
+
+ n_ai_chans = probe_number_of_ai_chans(dev);
+
+ ret = comedi_alloc_subdevices(dev, 4);
+ if (ret)
+ goto out;
+
+ devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
+ if (!devpriv)
+ return -ENOMEM;
+
+ dev->board_name = board->name;
+
+ s = &dev->subdevices[0];
+ /* ai subdevice */
+ s->type = COMEDI_SUBD_AI;
+ s->subdev_flags = SDF_READABLE | SDF_GROUND;
+#if 1
+ s->n_chan = n_ai_chans;
+#else
+ if (it->options[2])
+ s->n_chan = board->ad_chan;
+ else
+ s->n_chan = board->ad_chan / 2;
+#endif
+ s->maxdata = (1 << board->adbits) - 1;
+ s->range_table = ai_range_lkup(board->adrangetype, it->options[3]);
+ s->insn_read = dt2801_ai_insn_read;
+
+ s = &dev->subdevices[1];
+ /* ao subdevice */
+ s->type = COMEDI_SUBD_AO;
+ s->subdev_flags = SDF_WRITABLE;
+ s->n_chan = 2;
+ s->maxdata = (1 << board->dabits) - 1;
+ s->range_table_list = devpriv->dac_range_types;
+ devpriv->dac_range_types[0] = dac_range_lkup(it->options[4]);
+ devpriv->dac_range_types[1] = dac_range_lkup(it->options[5]);
+ s->insn_write = dt2801_ao_insn_write;
+
+ ret = comedi_alloc_subdev_readback(s);
+ if (ret)
+ return ret;
+
+ s = &dev->subdevices[2];
+ /* 1st digital subdevice */
+ s->type = COMEDI_SUBD_DIO;
+ s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
+ s->n_chan = 8;
+ s->maxdata = 1;
+ s->range_table = &range_digital;
+ s->insn_bits = dt2801_dio_insn_bits;
+ s->insn_config = dt2801_dio_insn_config;
+
+ s = &dev->subdevices[3];
+ /* 2nd digital subdevice */
+ s->type = COMEDI_SUBD_DIO;
+ s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
+ s->n_chan = 8;
+ s->maxdata = 1;
+ s->range_table = &range_digital;
+ s->insn_bits = dt2801_dio_insn_bits;
+ s->insn_config = dt2801_dio_insn_config;
+
+ ret = 0;
+out:
+ return ret;
+}
+
+static struct comedi_driver dt2801_driver = {
+ .driver_name = "dt2801",
+ .module = THIS_MODULE,
+ .attach = dt2801_attach,
+ .detach = comedi_legacy_detach,
+};
+module_comedi_driver(dt2801_driver);
+
+MODULE_AUTHOR("Comedi http://www.comedi.org");
+MODULE_DESCRIPTION("Comedi low-level driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/comedi/drivers/dt2811.c b/drivers/staging/comedi/drivers/dt2811.c
new file mode 100644
index 000000000..a80773291
--- /dev/null
+++ b/drivers/staging/comedi/drivers/dt2811.c
@@ -0,0 +1,474 @@
+/*
+ comedi/drivers/dt2811.c
+ Hardware driver for Data Translation DT2811
+
+ COMEDI - Linux Control and Measurement Device Interface
+ History:
+ Base Version - David A. Schleef <ds@schleef.org>
+ December 1998 - Updated to work. David does not have a DT2811
+ board any longer so this was suffering from bitrot.
+ Updated performed by ...
+
+ This program is free software; you can redistribute it and/or 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.
+ */
+/*
+Driver: dt2811
+Description: Data Translation DT2811
+Author: ds
+Devices: [Data Translation] DT2811-PGL (dt2811-pgl), DT2811-PGH (dt2811-pgh)
+Status: works
+
+Configuration options:
+ [0] - I/O port base address
+ [1] - IRQ, although this is currently unused
+ [2] - A/D reference
+ 0 = signle-ended
+ 1 = differential
+ 2 = pseudo-differential (common reference)
+ [3] - A/D range
+ 0 = [-5, 5]
+ 1 = [-2.5, 2.5]
+ 2 = [0, 5]
+ [4] - D/A 0 range (same choices)
+ [4] - D/A 1 range (same choices)
+*/
+
+#include <linux/module.h>
+#include "../comedidev.h"
+
+static const struct comedi_lrange range_dt2811_pgh_ai_5_unipolar = {
+ 4, {
+ UNI_RANGE(5),
+ UNI_RANGE(2.5),
+ UNI_RANGE(1.25),
+ UNI_RANGE(0.625)
+ }
+};
+
+static const struct comedi_lrange range_dt2811_pgh_ai_2_5_bipolar = {
+ 4, {
+ BIP_RANGE(2.5),
+ BIP_RANGE(1.25),
+ BIP_RANGE(0.625),
+ BIP_RANGE(0.3125)
+ }
+};
+
+static const struct comedi_lrange range_dt2811_pgh_ai_5_bipolar = {
+ 4, {
+ BIP_RANGE(5),
+ BIP_RANGE(2.5),
+ BIP_RANGE(1.25),
+ BIP_RANGE(0.625)
+ }
+};
+
+static const struct comedi_lrange range_dt2811_pgl_ai_5_unipolar = {
+ 4, {
+ UNI_RANGE(5),
+ UNI_RANGE(0.5),
+ UNI_RANGE(0.05),
+ UNI_RANGE(0.01)
+ }
+};
+
+static const struct comedi_lrange range_dt2811_pgl_ai_2_5_bipolar = {
+ 4, {
+ BIP_RANGE(2.5),
+ BIP_RANGE(0.25),
+ BIP_RANGE(0.025),
+ BIP_RANGE(0.005)
+ }
+};
+
+static const struct comedi_lrange range_dt2811_pgl_ai_5_bipolar = {
+ 4, {
+ BIP_RANGE(5),
+ BIP_RANGE(0.5),
+ BIP_RANGE(0.05),
+ BIP_RANGE(0.01)
+ }
+};
+
+/*
+
+ 0x00 ADCSR R/W A/D Control/Status Register
+ bit 7 - (R) 1 indicates A/D conversion done
+ reading ADDAT clears bit
+ (W) ignored
+ bit 6 - (R) 1 indicates A/D error
+ (W) ignored
+ bit 5 - (R) 1 indicates A/D busy, cleared at end
+ of conversion
+ (W) ignored
+ bit 4 - (R) 0
+ (W)
+ bit 3 - (R) 0
+ bit 2 - (R/W) 1 indicates interrupts enabled
+ bits 1,0 - (R/W) mode bits
+ 00 single conversion on ADGCR load
+ 01 continuous conversion, internal clock,
+ (clock enabled on ADGCR load)
+ 10 continuous conversion, internal clock,
+ external trigger
+ 11 continuous conversion, external clock,
+ external trigger
+
+ 0x01 ADGCR R/W A/D Gain/Channel Register
+ bit 6,7 - (R/W) gain select
+ 00 gain=1, both PGH, PGL models
+ 01 gain=2 PGH, 10 PGL
+ 10 gain=4 PGH, 100 PGL
+ 11 gain=8 PGH, 500 PGL
+ bit 4,5 - reserved
+ bit 3-0 - (R/W) channel select
+ channel number from 0-15
+
+ 0x02,0x03 (R) ADDAT A/D Data Register
+ (W) DADAT0 D/A Data Register 0
+ 0x02 low byte
+ 0x03 high byte
+
+ 0x04,0x05 (W) DADAT0 D/A Data Register 1
+
+ 0x06 (R) DIO0 Digital Input Port 0
+ (W) DIO1 Digital Output Port 1
+
+ 0x07 TMRCTR (R/W) Timer/Counter Register
+ bits 6,7 - reserved
+ bits 5-3 - Timer frequency control (mantissa)
+ 543 divisor freqency (kHz)
+ 000 1 600
+ 001 10 60
+ 010 2 300
+ 011 3 200
+ 100 4 150
+ 101 5 120
+ 110 6 100
+ 111 12 50
+ bits 2-0 - Timer frequency control (exponent)
+ 210 multiply divisor/divide frequency by
+ 000 1
+ 001 10
+ 010 100
+ 011 1000
+ 100 10000
+ 101 100000
+ 110 1000000
+ 111 10000000
+
+ */
+
+#define TIMEOUT 10000
+
+#define DT2811_ADCSR 0
+#define DT2811_ADGCR 1
+#define DT2811_ADDATLO 2
+#define DT2811_ADDATHI 3
+#define DT2811_DADAT0LO 2
+#define DT2811_DADAT0HI 3
+#define DT2811_DADAT1LO 4
+#define DT2811_DADAT1HI 5
+#define DT2811_DIO 6
+#define DT2811_TMRCTR 7
+
+/*
+ * flags
+ */
+
+/* ADCSR */
+
+#define DT2811_ADDONE 0x80
+#define DT2811_ADERROR 0x40
+#define DT2811_ADBUSY 0x20
+#define DT2811_CLRERROR 0x10
+#define DT2811_INTENB 0x04
+#define DT2811_ADMODE 0x03
+
+struct dt2811_board {
+ const char *name;
+ const struct comedi_lrange *bip_5;
+ const struct comedi_lrange *bip_2_5;
+ const struct comedi_lrange *unip_5;
+};
+
+enum { card_2811_pgh, card_2811_pgl };
+
+struct dt2811_private {
+ int ntrig;
+ int curadchan;
+ enum {
+ adc_singleended, adc_diff, adc_pseudo_diff
+ } adc_mux;
+ enum {
+ dac_bipolar_5, dac_bipolar_2_5, dac_unipolar_5
+ } dac_range[2];
+ const struct comedi_lrange *range_type_list[2];
+};
+
+static const struct comedi_lrange *dac_range_types[] = {
+ &range_bipolar5,
+ &range_bipolar2_5,
+ &range_unipolar5
+};
+
+static int dt2811_ai_eoc(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned long context)
+{
+ unsigned int status;
+
+ status = inb(dev->iobase + DT2811_ADCSR);
+ if ((status & DT2811_ADBUSY) == 0)
+ return 0;
+ return -EBUSY;
+}
+
+static int dt2811_ai_insn(struct comedi_device *dev, struct comedi_subdevice *s,
+ struct comedi_insn *insn, unsigned int *data)
+{
+ int chan = CR_CHAN(insn->chanspec);
+ int ret;
+ int i;
+
+ for (i = 0; i < insn->n; i++) {
+ outb(chan, dev->iobase + DT2811_ADGCR);
+
+ ret = comedi_timeout(dev, s, insn, dt2811_ai_eoc, 0);
+ if (ret)
+ return ret;
+
+ data[i] = inb(dev->iobase + DT2811_ADDATLO);
+ data[i] |= inb(dev->iobase + DT2811_ADDATHI) << 8;
+ data[i] &= 0xfff;
+ }
+
+ return i;
+}
+
+static int dt2811_ao_insn_write(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ unsigned int chan = CR_CHAN(insn->chanspec);
+ unsigned int val = s->readback[chan];
+ int i;
+
+ for (i = 0; i < insn->n; i++) {
+ val = data[i];
+ outb(val & 0xff, dev->iobase + DT2811_DADAT0LO + 2 * chan);
+ outb((val >> 8) & 0xff,
+ dev->iobase + DT2811_DADAT0HI + 2 * chan);
+ }
+ s->readback[chan] = val;
+
+ return insn->n;
+}
+
+static int dt2811_di_insn_bits(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn, unsigned int *data)
+{
+ data[1] = inb(dev->iobase + DT2811_DIO);
+
+ return insn->n;
+}
+
+static int dt2811_do_insn_bits(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ if (comedi_dio_update_state(s, data))
+ outb(s->state, dev->iobase + DT2811_DIO);
+
+ data[1] = s->state;
+
+ return insn->n;
+}
+
+/*
+ options[0] Board base address
+ options[1] IRQ
+ options[2] Input configuration
+ 0 == single-ended
+ 1 == differential
+ 2 == pseudo-differential
+ options[3] Analog input range configuration
+ 0 == bipolar 5 (-5V -- +5V)
+ 1 == bipolar 2.5V (-2.5V -- +2.5V)
+ 2 == unipolar 5V (0V -- +5V)
+ options[4] Analog output 0 range configuration
+ 0 == bipolar 5 (-5V -- +5V)
+ 1 == bipolar 2.5V (-2.5V -- +2.5V)
+ 2 == unipolar 5V (0V -- +5V)
+ options[5] Analog output 1 range configuration
+ 0 == bipolar 5 (-5V -- +5V)
+ 1 == bipolar 2.5V (-2.5V -- +2.5V)
+ 2 == unipolar 5V (0V -- +5V)
+*/
+static int dt2811_attach(struct comedi_device *dev, struct comedi_devconfig *it)
+{
+ /* int i; */
+ const struct dt2811_board *board = dev->board_ptr;
+ struct dt2811_private *devpriv;
+ int ret;
+ struct comedi_subdevice *s;
+
+ ret = comedi_request_region(dev, it->options[0], 0x8);
+ if (ret)
+ return ret;
+
+#if 0
+ outb(0, dev->iobase + DT2811_ADCSR);
+ udelay(100);
+ i = inb(dev->iobase + DT2811_ADDATLO);
+ i = inb(dev->iobase + DT2811_ADDATHI);
+#endif
+
+ ret = comedi_alloc_subdevices(dev, 4);
+ if (ret)
+ return ret;
+
+ devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
+ if (!devpriv)
+ return -ENOMEM;
+
+ switch (it->options[2]) {
+ case 0:
+ devpriv->adc_mux = adc_singleended;
+ break;
+ case 1:
+ devpriv->adc_mux = adc_diff;
+ break;
+ case 2:
+ devpriv->adc_mux = adc_pseudo_diff;
+ break;
+ default:
+ devpriv->adc_mux = adc_singleended;
+ break;
+ }
+ switch (it->options[4]) {
+ case 0:
+ devpriv->dac_range[0] = dac_bipolar_5;
+ break;
+ case 1:
+ devpriv->dac_range[0] = dac_bipolar_2_5;
+ break;
+ case 2:
+ devpriv->dac_range[0] = dac_unipolar_5;
+ break;
+ default:
+ devpriv->dac_range[0] = dac_bipolar_5;
+ break;
+ }
+ switch (it->options[5]) {
+ case 0:
+ devpriv->dac_range[1] = dac_bipolar_5;
+ break;
+ case 1:
+ devpriv->dac_range[1] = dac_bipolar_2_5;
+ break;
+ case 2:
+ devpriv->dac_range[1] = dac_unipolar_5;
+ break;
+ default:
+ devpriv->dac_range[1] = dac_bipolar_5;
+ break;
+ }
+
+ s = &dev->subdevices[0];
+ /* initialize the ADC subdevice */
+ s->type = COMEDI_SUBD_AI;
+ s->subdev_flags = SDF_READABLE | SDF_GROUND;
+ s->n_chan = devpriv->adc_mux == adc_diff ? 8 : 16;
+ s->insn_read = dt2811_ai_insn;
+ s->maxdata = 0xfff;
+ switch (it->options[3]) {
+ case 0:
+ default:
+ s->range_table = board->bip_5;
+ break;
+ case 1:
+ s->range_table = board->bip_2_5;
+ break;
+ case 2:
+ s->range_table = board->unip_5;
+ break;
+ }
+
+ s = &dev->subdevices[1];
+ /* ao subdevice */
+ s->type = COMEDI_SUBD_AO;
+ s->subdev_flags = SDF_WRITABLE;
+ s->n_chan = 2;
+ s->maxdata = 0xfff;
+ s->range_table_list = devpriv->range_type_list;
+ devpriv->range_type_list[0] = dac_range_types[devpriv->dac_range[0]];
+ devpriv->range_type_list[1] = dac_range_types[devpriv->dac_range[1]];
+ s->insn_write = dt2811_ao_insn_write;
+
+ ret = comedi_alloc_subdev_readback(s);
+ if (ret)
+ return ret;
+
+ s = &dev->subdevices[2];
+ /* di subdevice */
+ s->type = COMEDI_SUBD_DI;
+ s->subdev_flags = SDF_READABLE;
+ s->n_chan = 8;
+ s->insn_bits = dt2811_di_insn_bits;
+ s->maxdata = 1;
+ s->range_table = &range_digital;
+
+ s = &dev->subdevices[3];
+ /* do subdevice */
+ s->type = COMEDI_SUBD_DO;
+ s->subdev_flags = SDF_WRITABLE;
+ s->n_chan = 8;
+ s->insn_bits = dt2811_do_insn_bits;
+ s->maxdata = 1;
+ s->state = 0;
+ s->range_table = &range_digital;
+
+ return 0;
+}
+
+static const struct dt2811_board boardtypes[] = {
+ {
+ .name = "dt2811-pgh",
+ .bip_5 = &range_dt2811_pgh_ai_5_bipolar,
+ .bip_2_5 = &range_dt2811_pgh_ai_2_5_bipolar,
+ .unip_5 = &range_dt2811_pgh_ai_5_unipolar,
+ }, {
+ .name = "dt2811-pgl",
+ .bip_5 = &range_dt2811_pgl_ai_5_bipolar,
+ .bip_2_5 = &range_dt2811_pgl_ai_2_5_bipolar,
+ .unip_5 = &range_dt2811_pgl_ai_5_unipolar,
+ },
+};
+
+static struct comedi_driver dt2811_driver = {
+ .driver_name = "dt2811",
+ .module = THIS_MODULE,
+ .attach = dt2811_attach,
+ .detach = comedi_legacy_detach,
+ .board_name = &boardtypes[0].name,
+ .num_names = ARRAY_SIZE(boardtypes),
+ .offset = sizeof(struct dt2811_board),
+};
+module_comedi_driver(dt2811_driver);
+
+MODULE_AUTHOR("Comedi http://www.comedi.org");
+MODULE_DESCRIPTION("Comedi low-level driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/comedi/drivers/dt2814.c b/drivers/staging/comedi/drivers/dt2814.c
new file mode 100644
index 000000000..66705f9a0
--- /dev/null
+++ b/drivers/staging/comedi/drivers/dt2814.c
@@ -0,0 +1,297 @@
+/*
+ comedi/drivers/dt2814.c
+ Hardware driver for Data Translation DT2814
+
+ COMEDI - Linux Control and Measurement Device Interface
+ Copyright (C) 1998 David A. Schleef <ds@schleef.org>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+*/
+/*
+Driver: dt2814
+Description: Data Translation DT2814
+Author: ds
+Status: complete
+Devices: [Data Translation] DT2814 (dt2814)
+
+Configuration options:
+ [0] - I/O port base address
+ [1] - IRQ
+
+This card has 16 analog inputs multiplexed onto a 12 bit ADC. There
+is a minimally useful onboard clock. The base frequency for the
+clock is selected by jumpers, and the clock divider can be selected
+via programmed I/O. Unfortunately, the clock divider can only be
+a power of 10, from 1 to 10^7, of which only 3 or 4 are useful. In
+addition, the clock does not seem to be very accurate.
+*/
+
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include "../comedidev.h"
+
+#include <linux/delay.h>
+
+#define DT2814_CSR 0
+#define DT2814_DATA 1
+
+/*
+ * flags
+ */
+
+#define DT2814_FINISH 0x80
+#define DT2814_ERR 0x40
+#define DT2814_BUSY 0x20
+#define DT2814_ENB 0x10
+#define DT2814_CHANMASK 0x0f
+
+struct dt2814_private {
+ int ntrig;
+ int curadchan;
+};
+
+#define DT2814_TIMEOUT 10
+#define DT2814_MAX_SPEED 100000 /* Arbitrary 10 khz limit */
+
+static int dt2814_ai_eoc(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned long context)
+{
+ unsigned int status;
+
+ status = inb(dev->iobase + DT2814_CSR);
+ if (status & DT2814_FINISH)
+ return 0;
+ return -EBUSY;
+}
+
+static int dt2814_ai_insn_read(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn, unsigned int *data)
+{
+ int n, hi, lo;
+ int chan;
+ int ret;
+
+ for (n = 0; n < insn->n; n++) {
+ chan = CR_CHAN(insn->chanspec);
+
+ outb(chan, dev->iobase + DT2814_CSR);
+
+ ret = comedi_timeout(dev, s, insn, dt2814_ai_eoc, 0);
+ if (ret)
+ return ret;
+
+ hi = inb(dev->iobase + DT2814_DATA);
+ lo = inb(dev->iobase + DT2814_DATA);
+
+ data[n] = (hi << 4) | (lo >> 4);
+ }
+
+ return n;
+}
+
+static int dt2814_ns_to_timer(unsigned int *ns, unsigned int flags)
+{
+ int i;
+ unsigned int f;
+
+ /* XXX ignores flags */
+
+ f = 10000; /* ns */
+ for (i = 0; i < 8; i++) {
+ if ((2 * (*ns)) < (f * 11))
+ break;
+ f *= 10;
+ }
+
+ *ns = f;
+
+ return i;
+}
+
+static int dt2814_ai_cmdtest(struct comedi_device *dev,
+ struct comedi_subdevice *s, struct comedi_cmd *cmd)
+{
+ int err = 0;
+ unsigned int arg;
+
+ /* Step 1 : check if triggers are trivially valid */
+
+ err |= comedi_check_trigger_src(&cmd->start_src, TRIG_NOW);
+ err |= comedi_check_trigger_src(&cmd->scan_begin_src, TRIG_TIMER);
+ err |= comedi_check_trigger_src(&cmd->convert_src, TRIG_NOW);
+ err |= comedi_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
+ err |= comedi_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE);
+
+ if (err)
+ return 1;
+
+ /* Step 2a : make sure trigger sources are unique */
+
+ err |= comedi_check_trigger_is_unique(cmd->stop_src);
+
+ /* Step 2b : and mutually compatible */
+
+ if (err)
+ return 2;
+
+ /* Step 3: check if arguments are trivially valid */
+
+ err |= comedi_check_trigger_arg_is(&cmd->start_arg, 0);
+
+ err |= comedi_check_trigger_arg_max(&cmd->scan_begin_arg, 1000000000);
+ err |= comedi_check_trigger_arg_min(&cmd->scan_begin_arg,
+ DT2814_MAX_SPEED);
+
+ err |= comedi_check_trigger_arg_is(&cmd->scan_end_arg,
+ cmd->chanlist_len);
+
+ if (cmd->stop_src == TRIG_COUNT)
+ err |= comedi_check_trigger_arg_min(&cmd->stop_arg, 2);
+ else /* TRIG_NONE */
+ err |= comedi_check_trigger_arg_is(&cmd->stop_arg, 0);
+
+ if (err)
+ return 3;
+
+ /* step 4: fix up any arguments */
+
+ arg = cmd->scan_begin_arg;
+ dt2814_ns_to_timer(&arg, cmd->flags);
+ err |= comedi_check_trigger_arg_is(&cmd->scan_begin_arg, arg);
+
+ if (err)
+ return 4;
+
+ return 0;
+}
+
+static int dt2814_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
+{
+ struct dt2814_private *devpriv = dev->private;
+ struct comedi_cmd *cmd = &s->async->cmd;
+ int chan;
+ int trigvar;
+
+ trigvar = dt2814_ns_to_timer(&cmd->scan_begin_arg, cmd->flags);
+
+ chan = CR_CHAN(cmd->chanlist[0]);
+
+ devpriv->ntrig = cmd->stop_arg;
+ outb(chan | DT2814_ENB | (trigvar << 5), dev->iobase + DT2814_CSR);
+
+ return 0;
+}
+
+static irqreturn_t dt2814_interrupt(int irq, void *d)
+{
+ int lo, hi;
+ struct comedi_device *dev = d;
+ struct dt2814_private *devpriv = dev->private;
+ struct comedi_subdevice *s = dev->read_subdev;
+ int data;
+
+ if (!dev->attached) {
+ dev_err(dev->class_dev, "spurious interrupt\n");
+ return IRQ_HANDLED;
+ }
+
+ hi = inb(dev->iobase + DT2814_DATA);
+ lo = inb(dev->iobase + DT2814_DATA);
+
+ data = (hi << 4) | (lo >> 4);
+
+ if (!(--devpriv->ntrig)) {
+ int i;
+
+ outb(0, dev->iobase + DT2814_CSR);
+ /* note: turning off timed mode triggers another
+ sample. */
+
+ for (i = 0; i < DT2814_TIMEOUT; i++) {
+ if (inb(dev->iobase + DT2814_CSR) & DT2814_FINISH)
+ break;
+ }
+ inb(dev->iobase + DT2814_DATA);
+ inb(dev->iobase + DT2814_DATA);
+
+ s->async->events |= COMEDI_CB_EOA;
+ }
+ comedi_handle_events(dev, s);
+ return IRQ_HANDLED;
+}
+
+static int dt2814_attach(struct comedi_device *dev, struct comedi_devconfig *it)
+{
+ struct dt2814_private *devpriv;
+ struct comedi_subdevice *s;
+ int ret;
+ int i;
+
+ ret = comedi_request_region(dev, it->options[0], 0x2);
+ if (ret)
+ return ret;
+
+ outb(0, dev->iobase + DT2814_CSR);
+ udelay(100);
+ if (inb(dev->iobase + DT2814_CSR) & DT2814_ERR) {
+ dev_err(dev->class_dev, "reset error (fatal)\n");
+ return -EIO;
+ }
+ i = inb(dev->iobase + DT2814_DATA);
+ i = inb(dev->iobase + DT2814_DATA);
+
+ if (it->options[1]) {
+ ret = request_irq(it->options[1], dt2814_interrupt, 0,
+ dev->board_name, dev);
+ if (ret == 0)
+ dev->irq = it->options[1];
+ }
+
+ ret = comedi_alloc_subdevices(dev, 1);
+ if (ret)
+ return ret;
+
+ devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
+ if (!devpriv)
+ return -ENOMEM;
+
+ s = &dev->subdevices[0];
+ s->type = COMEDI_SUBD_AI;
+ s->subdev_flags = SDF_READABLE | SDF_GROUND;
+ s->n_chan = 16; /* XXX */
+ s->insn_read = dt2814_ai_insn_read;
+ s->maxdata = 0xfff;
+ s->range_table = &range_unknown; /* XXX */
+ if (dev->irq) {
+ dev->read_subdev = s;
+ s->subdev_flags |= SDF_CMD_READ;
+ s->len_chanlist = 1;
+ s->do_cmd = dt2814_ai_cmd;
+ s->do_cmdtest = dt2814_ai_cmdtest;
+ }
+
+ return 0;
+}
+
+static struct comedi_driver dt2814_driver = {
+ .driver_name = "dt2814",
+ .module = THIS_MODULE,
+ .attach = dt2814_attach,
+ .detach = comedi_legacy_detach,
+};
+module_comedi_driver(dt2814_driver);
+
+MODULE_AUTHOR("Comedi http://www.comedi.org");
+MODULE_DESCRIPTION("Comedi low-level driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/comedi/drivers/dt2815.c b/drivers/staging/comedi/drivers/dt2815.c
new file mode 100644
index 000000000..fb08569c1
--- /dev/null
+++ b/drivers/staging/comedi/drivers/dt2815.c
@@ -0,0 +1,223 @@
+/*
+ comedi/drivers/dt2815.c
+ Hardware driver for Data Translation DT2815
+
+ COMEDI - Linux Control and Measurement Device Interface
+ Copyright (C) 1999 Anders Blomdell <anders.blomdell@control.lth.se>
+
+ This program is free software; you can redistribute it and/or 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.
+ */
+/*
+Driver: dt2815
+Description: Data Translation DT2815
+Author: ds
+Status: mostly complete, untested
+Devices: [Data Translation] DT2815 (dt2815)
+
+I'm not sure anyone has ever tested this board. If you have information
+contrary, please update.
+
+Configuration options:
+ [0] - I/O port base base address
+ [1] - IRQ (unused)
+ [2] - Voltage unipolar/bipolar configuration
+ 0 == unipolar 5V (0V -- +5V)
+ 1 == bipolar 5V (-5V -- +5V)
+ [3] - Current offset configuration
+ 0 == disabled (0mA -- +32mAV)
+ 1 == enabled (+4mA -- +20mAV)
+ [4] - Firmware program configuration
+ 0 == program 1 (see manual table 5-4)
+ 1 == program 2 (see manual table 5-4)
+ 2 == program 3 (see manual table 5-4)
+ 3 == program 4 (see manual table 5-4)
+ [5] - Analog output 0 range configuration
+ 0 == voltage
+ 1 == current
+ [6] - Analog output 1 range configuration (same options)
+ [7] - Analog output 2 range configuration (same options)
+ [8] - Analog output 3 range configuration (same options)
+ [9] - Analog output 4 range configuration (same options)
+ [10] - Analog output 5 range configuration (same options)
+ [11] - Analog output 6 range configuration (same options)
+ [12] - Analog output 7 range configuration (same options)
+*/
+
+#include <linux/module.h>
+#include "../comedidev.h"
+
+#include <linux/delay.h>
+
+#define DT2815_DATA 0
+#define DT2815_STATUS 1
+
+struct dt2815_private {
+ const struct comedi_lrange *range_type_list[8];
+ unsigned int ao_readback[8];
+};
+
+static int dt2815_ao_status(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned long context)
+{
+ unsigned int status;
+
+ status = inb(dev->iobase + DT2815_STATUS);
+ if (status == context)
+ return 0;
+ return -EBUSY;
+}
+
+static int dt2815_ao_insn_read(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn, unsigned int *data)
+{
+ struct dt2815_private *devpriv = dev->private;
+ int i;
+ int chan = CR_CHAN(insn->chanspec);
+
+ for (i = 0; i < insn->n; i++)
+ data[i] = devpriv->ao_readback[chan];
+
+ return i;
+}
+
+static int dt2815_ao_insn(struct comedi_device *dev, struct comedi_subdevice *s,
+ struct comedi_insn *insn, unsigned int *data)
+{
+ struct dt2815_private *devpriv = dev->private;
+ int i;
+ int chan = CR_CHAN(insn->chanspec);
+ unsigned int lo, hi;
+ int ret;
+
+ for (i = 0; i < insn->n; i++) {
+ lo = ((data[i] & 0x0f) << 4) | (chan << 1) | 0x01;
+ hi = (data[i] & 0xff0) >> 4;
+
+ ret = comedi_timeout(dev, s, insn, dt2815_ao_status, 0x00);
+ if (ret)
+ return ret;
+
+ outb(lo, dev->iobase + DT2815_DATA);
+
+ ret = comedi_timeout(dev, s, insn, dt2815_ao_status, 0x10);
+ if (ret)
+ return ret;
+
+ devpriv->ao_readback[chan] = data[i];
+ }
+ return i;
+}
+
+/*
+ options[0] Board base address
+ options[1] IRQ (not applicable)
+ options[2] Voltage unipolar/bipolar configuration
+ 0 == unipolar 5V (0V -- +5V)
+ 1 == bipolar 5V (-5V -- +5V)
+ options[3] Current offset configuration
+ 0 == disabled (0mA -- +32mAV)
+ 1 == enabled (+4mA -- +20mAV)
+ options[4] Firmware program configuration
+ 0 == program 1 (see manual table 5-4)
+ 1 == program 2 (see manual table 5-4)
+ 2 == program 3 (see manual table 5-4)
+ 3 == program 4 (see manual table 5-4)
+ options[5] Analog output 0 range configuration
+ 0 == voltage
+ 1 == current
+ options[6] Analog output 1 range configuration
+ ...
+ options[12] Analog output 7 range configuration
+ 0 == voltage
+ 1 == current
+ */
+
+static int dt2815_attach(struct comedi_device *dev, struct comedi_devconfig *it)
+{
+ struct dt2815_private *devpriv;
+ struct comedi_subdevice *s;
+ int i;
+ const struct comedi_lrange *current_range_type, *voltage_range_type;
+ int ret;
+
+ ret = comedi_request_region(dev, it->options[0], 0x2);
+ if (ret)
+ return ret;
+
+ ret = comedi_alloc_subdevices(dev, 1);
+ if (ret)
+ return ret;
+
+ devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
+ if (!devpriv)
+ return -ENOMEM;
+
+ s = &dev->subdevices[0];
+ /* ao subdevice */
+ s->type = COMEDI_SUBD_AO;
+ s->subdev_flags = SDF_WRITABLE;
+ s->maxdata = 0xfff;
+ s->n_chan = 8;
+ s->insn_write = dt2815_ao_insn;
+ s->insn_read = dt2815_ao_insn_read;
+ s->range_table_list = devpriv->range_type_list;
+
+ current_range_type = (it->options[3])
+ ? &range_4_20mA : &range_0_32mA;
+ voltage_range_type = (it->options[2])
+ ? &range_bipolar5 : &range_unipolar5;
+ for (i = 0; i < 8; i++) {
+ devpriv->range_type_list[i] = (it->options[5 + i])
+ ? current_range_type : voltage_range_type;
+ }
+
+ /* Init the 2815 */
+ outb(0x00, dev->iobase + DT2815_STATUS);
+ for (i = 0; i < 100; i++) {
+ /* This is incredibly slow (approx 20 ms) */
+ unsigned int status;
+
+ udelay(1000);
+ status = inb(dev->iobase + DT2815_STATUS);
+ if (status == 4) {
+ unsigned int program;
+
+ program = (it->options[4] & 0x3) << 3 | 0x7;
+ outb(program, dev->iobase + DT2815_DATA);
+ dev_dbg(dev->class_dev, "program: 0x%x (@t=%d)\n",
+ program, i);
+ break;
+ } else if (status != 0x00) {
+ dev_dbg(dev->class_dev,
+ "unexpected status 0x%x (@t=%d)\n",
+ status, i);
+ if (status & 0x60)
+ outb(0x00, dev->iobase + DT2815_STATUS);
+ }
+ }
+
+ return 0;
+}
+
+static struct comedi_driver dt2815_driver = {
+ .driver_name = "dt2815",
+ .module = THIS_MODULE,
+ .attach = dt2815_attach,
+ .detach = comedi_legacy_detach,
+};
+module_comedi_driver(dt2815_driver);
+
+MODULE_AUTHOR("Comedi http://www.comedi.org");
+MODULE_DESCRIPTION("Comedi low-level driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/comedi/drivers/dt2817.c b/drivers/staging/comedi/drivers/dt2817.c
new file mode 100644
index 000000000..5131deebf
--- /dev/null
+++ b/drivers/staging/comedi/drivers/dt2817.c
@@ -0,0 +1,149 @@
+/*
+ comedi/drivers/dt2817.c
+ Hardware driver for Data Translation DT2817
+
+ COMEDI - Linux Control and Measurement Device Interface
+ Copyright (C) 1998 David A. Schleef <ds@schleef.org>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+*/
+/*
+Driver: dt2817
+Description: Data Translation DT2817
+Author: ds
+Status: complete
+Devices: [Data Translation] DT2817 (dt2817)
+
+A very simple digital I/O card. Four banks of 8 lines, each bank
+is configurable for input or output. One wonders why it takes a
+50 page manual to describe this thing.
+
+The driver (which, btw, is much less than 50 pages) has 1 subdevice
+with 32 channels, configurable in groups of 8.
+
+Configuration options:
+ [0] - I/O port base base address
+*/
+
+#include <linux/module.h>
+#include "../comedidev.h"
+
+#define DT2817_CR 0
+#define DT2817_DATA 1
+
+static int dt2817_dio_insn_config(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ unsigned int chan = CR_CHAN(insn->chanspec);
+ unsigned int oe = 0;
+ unsigned int mask;
+ int ret;
+
+ if (chan < 8)
+ mask = 0x000000ff;
+ else if (chan < 16)
+ mask = 0x0000ff00;
+ else if (chan < 24)
+ mask = 0x00ff0000;
+ else
+ mask = 0xff000000;
+
+ ret = comedi_dio_insn_config(dev, s, insn, data, mask);
+ if (ret)
+ return ret;
+
+ if (s->io_bits & 0x000000ff)
+ oe |= 0x1;
+ if (s->io_bits & 0x0000ff00)
+ oe |= 0x2;
+ if (s->io_bits & 0x00ff0000)
+ oe |= 0x4;
+ if (s->io_bits & 0xff000000)
+ oe |= 0x8;
+
+ outb(oe, dev->iobase + DT2817_CR);
+
+ return insn->n;
+}
+
+static int dt2817_dio_insn_bits(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ unsigned long iobase = dev->iobase + DT2817_DATA;
+ unsigned int mask;
+ unsigned int val;
+
+ mask = comedi_dio_update_state(s, data);
+ if (mask) {
+ if (mask & 0x000000ff)
+ outb(s->state & 0xff, iobase + 0);
+ if (mask & 0x0000ff00)
+ outb((s->state >> 8) & 0xff, iobase + 1);
+ if (mask & 0x00ff0000)
+ outb((s->state >> 16) & 0xff, iobase + 2);
+ if (mask & 0xff000000)
+ outb((s->state >> 24) & 0xff, iobase + 3);
+ }
+
+ val = inb(iobase + 0);
+ val |= (inb(iobase + 1) << 8);
+ val |= (inb(iobase + 2) << 16);
+ val |= (inb(iobase + 3) << 24);
+
+ data[1] = val;
+
+ return insn->n;
+}
+
+static int dt2817_attach(struct comedi_device *dev, struct comedi_devconfig *it)
+{
+ int ret;
+ struct comedi_subdevice *s;
+
+ ret = comedi_request_region(dev, it->options[0], 0x5);
+ if (ret)
+ return ret;
+
+ ret = comedi_alloc_subdevices(dev, 1);
+ if (ret)
+ return ret;
+
+ s = &dev->subdevices[0];
+
+ s->n_chan = 32;
+ s->type = COMEDI_SUBD_DIO;
+ s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
+ s->range_table = &range_digital;
+ s->maxdata = 1;
+ s->insn_bits = dt2817_dio_insn_bits;
+ s->insn_config = dt2817_dio_insn_config;
+
+ s->state = 0;
+ outb(0, dev->iobase + DT2817_CR);
+
+ return 0;
+}
+
+static struct comedi_driver dt2817_driver = {
+ .driver_name = "dt2817",
+ .module = THIS_MODULE,
+ .attach = dt2817_attach,
+ .detach = comedi_legacy_detach,
+};
+module_comedi_driver(dt2817_driver);
+
+MODULE_AUTHOR("Comedi http://www.comedi.org");
+MODULE_DESCRIPTION("Comedi low-level driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/comedi/drivers/dt282x.c b/drivers/staging/comedi/drivers/dt282x.c
new file mode 100644
index 000000000..5a536a000
--- /dev/null
+++ b/drivers/staging/comedi/drivers/dt282x.c
@@ -0,0 +1,1211 @@
+/*
+ * dt282x.c
+ * Comedi driver for Data Translation DT2821 series
+ *
+ * COMEDI - Linux Control and Measurement Device Interface
+ * Copyright (C) 1997-8 David A. Schleef <ds@schleef.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+/*
+ * Driver: dt282x
+ * Description: Data Translation DT2821 series (including DT-EZ)
+ * Author: ds
+ * Devices: [Data Translation] DT2821 (dt2821), DT2821-F-16SE (dt2821-f),
+ * DT2821-F-8DI (dt2821-f), DT2821-G-16SE (dt2821-g),
+ * DT2821-G-8DI (dt2821-g), DT2823 (dt2823), DT2824-PGH (dt2824-pgh),
+ * DT2824-PGL (dt2824-pgl), DT2825 (dt2825), DT2827 (dt2827),
+ * DT2828 (dt2828), DT2928 (dt2829), DT21-EZ (dt21-ez), DT23-EZ (dt23-ez),
+ * DT24-EZ (dt24-ez), DT24-EZ-PGL (dt24-ez-pgl)
+ * Status: complete
+ * Updated: Wed, 22 Aug 2001 17:11:34 -0700
+ *
+ * Configuration options:
+ * [0] - I/O port base address
+ * [1] - IRQ (optional, required for async command support)
+ * [2] - DMA 1 (optional, required for async command support)
+ * [3] - DMA 2 (optional, required for async command support)
+ * [4] - AI jumpered for 0=single ended, 1=differential
+ * [5] - AI jumpered for 0=straight binary, 1=2's complement
+ * [6] - AO 0 data format (deprecated, see below)
+ * [7] - AO 1 data format (deprecated, see below)
+ * [8] - AI jumpered for 0=[-10,10]V, 1=[0,10], 2=[-5,5], 3=[0,5]
+ * [9] - AO channel 0 range (deprecated, see below)
+ * [10]- AO channel 1 range (deprecated, see below)
+ *
+ * Notes:
+ * - AO commands might be broken.
+ * - If you try to run a command on both the AI and AO subdevices
+ * simultaneously, bad things will happen. The driver needs to
+ * be fixed to check for this situation and return an error.
+ * - AO range is not programmable. The AO subdevice has a range_table
+ * containing all the possible analog output ranges. Use the range
+ * that matches your board configuration to convert between data
+ * values and physical units. The format of the data written to the
+ * board is handled automatically based on the unipolar/bipolar
+ * range that is selected.
+ */
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/gfp.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+
+#include "../comedidev.h"
+
+#include "comedi_isadma.h"
+
+/*
+ * Register map
+ */
+#define DT2821_ADCSR_REG 0x00
+#define DT2821_ADCSR_ADERR (1 << 15)
+#define DT2821_ADCSR_ADCLK (1 << 9)
+#define DT2821_ADCSR_MUXBUSY (1 << 8)
+#define DT2821_ADCSR_ADDONE (1 << 7)
+#define DT2821_ADCSR_IADDONE (1 << 6)
+#define DT2821_ADCSR_GS(x) (((x) & 0x3) << 4)
+#define DT2821_ADCSR_CHAN(x) (((x) & 0xf) << 0)
+#define DT2821_CHANCSR_REG 0x02
+#define DT2821_CHANCSR_LLE (1 << 15)
+#define DT2821_CHANCSR_PRESLA(x) (((x) & 0xf) >> 8)
+#define DT2821_CHANCSR_NUMB(x) ((((x) - 1) & 0xf) << 0)
+#define DT2821_ADDAT_REG 0x04
+#define DT2821_DACSR_REG 0x06
+#define DT2821_DACSR_DAERR (1 << 15)
+#define DT2821_DACSR_YSEL(x) ((x) << 9)
+#define DT2821_DACSR_SSEL (1 << 8)
+#define DT2821_DACSR_DACRDY (1 << 7)
+#define DT2821_DACSR_IDARDY (1 << 6)
+#define DT2821_DACSR_DACLK (1 << 5)
+#define DT2821_DACSR_HBOE (1 << 1)
+#define DT2821_DACSR_LBOE (1 << 0)
+#define DT2821_DADAT_REG 0x08
+#define DT2821_DIODAT_REG 0x0a
+#define DT2821_SUPCSR_REG 0x0c
+#define DT2821_SUPCSR_DMAD (1 << 15)
+#define DT2821_SUPCSR_ERRINTEN (1 << 14)
+#define DT2821_SUPCSR_CLRDMADNE (1 << 13)
+#define DT2821_SUPCSR_DDMA (1 << 12)
+#define DT2821_SUPCSR_DS_PIO (0 << 10)
+#define DT2821_SUPCSR_DS_AD_CLK (1 << 10)
+#define DT2821_SUPCSR_DS_DA_CLK (2 << 10)
+#define DT2821_SUPCSR_DS_AD_TRIG (3 << 10)
+#define DT2821_SUPCSR_BUFFB (1 << 9)
+#define DT2821_SUPCSR_SCDN (1 << 8)
+#define DT2821_SUPCSR_DACON (1 << 7)
+#define DT2821_SUPCSR_ADCINIT (1 << 6)
+#define DT2821_SUPCSR_DACINIT (1 << 5)
+#define DT2821_SUPCSR_PRLD (1 << 4)
+#define DT2821_SUPCSR_STRIG (1 << 3)
+#define DT2821_SUPCSR_XTRIG (1 << 2)
+#define DT2821_SUPCSR_XCLK (1 << 1)
+#define DT2821_SUPCSR_BDINIT (1 << 0)
+#define DT2821_TMRCTR_REG 0x0e
+
+static const struct comedi_lrange range_dt282x_ai_lo_bipolar = {
+ 4, {
+ BIP_RANGE(10),
+ BIP_RANGE(5),
+ BIP_RANGE(2.5),
+ BIP_RANGE(1.25)
+ }
+};
+
+static const struct comedi_lrange range_dt282x_ai_lo_unipolar = {
+ 4, {
+ UNI_RANGE(10),
+ UNI_RANGE(5),
+ UNI_RANGE(2.5),
+ UNI_RANGE(1.25)
+ }
+};
+
+static const struct comedi_lrange range_dt282x_ai_5_bipolar = {
+ 4, {
+ BIP_RANGE(5),
+ BIP_RANGE(2.5),
+ BIP_RANGE(1.25),
+ BIP_RANGE(0.625)
+ }
+};
+
+static const struct comedi_lrange range_dt282x_ai_5_unipolar = {
+ 4, {
+ UNI_RANGE(5),
+ UNI_RANGE(2.5),
+ UNI_RANGE(1.25),
+ UNI_RANGE(0.625)
+ }
+};
+
+static const struct comedi_lrange range_dt282x_ai_hi_bipolar = {
+ 4, {
+ BIP_RANGE(10),
+ BIP_RANGE(1),
+ BIP_RANGE(0.1),
+ BIP_RANGE(0.02)
+ }
+};
+
+static const struct comedi_lrange range_dt282x_ai_hi_unipolar = {
+ 4, {
+ UNI_RANGE(10),
+ UNI_RANGE(1),
+ UNI_RANGE(0.1),
+ UNI_RANGE(0.02)
+ }
+};
+
+/*
+ * The Analog Output range is set per-channel using jumpers on the board.
+ * All of these ranges may not be available on some DT2821 series boards.
+ * The default jumper setting has both channels set for +/-10V output.
+ */
+static const struct comedi_lrange dt282x_ao_range = {
+ 5, {
+ BIP_RANGE(10),
+ BIP_RANGE(5),
+ BIP_RANGE(2.5),
+ UNI_RANGE(10),
+ UNI_RANGE(5),
+ }
+};
+
+struct dt282x_board {
+ const char *name;
+ unsigned int ai_maxdata;
+ int adchan_se;
+ int adchan_di;
+ int ai_speed;
+ int ispgl;
+ int dachan;
+ unsigned int ao_maxdata;
+};
+
+static const struct dt282x_board boardtypes[] = {
+ {
+ .name = "dt2821",
+ .ai_maxdata = 0x0fff,
+ .adchan_se = 16,
+ .adchan_di = 8,
+ .ai_speed = 20000,
+ .dachan = 2,
+ .ao_maxdata = 0x0fff,
+ }, {
+ .name = "dt2821-f",
+ .ai_maxdata = 0x0fff,
+ .adchan_se = 16,
+ .adchan_di = 8,
+ .ai_speed = 6500,
+ .dachan = 2,
+ .ao_maxdata = 0x0fff,
+ }, {
+ .name = "dt2821-g",
+ .ai_maxdata = 0x0fff,
+ .adchan_se = 16,
+ .adchan_di = 8,
+ .ai_speed = 4000,
+ .dachan = 2,
+ .ao_maxdata = 0x0fff,
+ }, {
+ .name = "dt2823",
+ .ai_maxdata = 0xffff,
+ .adchan_di = 4,
+ .ai_speed = 10000,
+ .dachan = 2,
+ .ao_maxdata = 0xffff,
+ }, {
+ .name = "dt2824-pgh",
+ .ai_maxdata = 0x0fff,
+ .adchan_se = 16,
+ .adchan_di = 8,
+ .ai_speed = 20000,
+ }, {
+ .name = "dt2824-pgl",
+ .ai_maxdata = 0x0fff,
+ .adchan_se = 16,
+ .adchan_di = 8,
+ .ai_speed = 20000,
+ .ispgl = 1,
+ }, {
+ .name = "dt2825",
+ .ai_maxdata = 0x0fff,
+ .adchan_se = 16,
+ .adchan_di = 8,
+ .ai_speed = 20000,
+ .ispgl = 1,
+ .dachan = 2,
+ .ao_maxdata = 0x0fff,
+ }, {
+ .name = "dt2827",
+ .ai_maxdata = 0xffff,
+ .adchan_di = 4,
+ .ai_speed = 10000,
+ .dachan = 2,
+ .ao_maxdata = 0x0fff,
+ }, {
+ .name = "dt2828",
+ .ai_maxdata = 0x0fff,
+ .adchan_se = 4,
+ .ai_speed = 10000,
+ .dachan = 2,
+ .ao_maxdata = 0x0fff,
+ }, {
+ .name = "dt2829",
+ .ai_maxdata = 0xffff,
+ .adchan_se = 8,
+ .ai_speed = 33250,
+ .dachan = 2,
+ .ao_maxdata = 0xffff,
+ }, {
+ .name = "dt21-ez",
+ .ai_maxdata = 0x0fff,
+ .adchan_se = 16,
+ .adchan_di = 8,
+ .ai_speed = 10000,
+ .dachan = 2,
+ .ao_maxdata = 0x0fff,
+ }, {
+ .name = "dt23-ez",
+ .ai_maxdata = 0xffff,
+ .adchan_se = 16,
+ .adchan_di = 8,
+ .ai_speed = 10000,
+ }, {
+ .name = "dt24-ez",
+ .ai_maxdata = 0x0fff,
+ .adchan_se = 16,
+ .adchan_di = 8,
+ .ai_speed = 10000,
+ }, {
+ .name = "dt24-ez-pgl",
+ .ai_maxdata = 0x0fff,
+ .adchan_se = 16,
+ .adchan_di = 8,
+ .ai_speed = 10000,
+ .ispgl = 1,
+ },
+};
+
+struct dt282x_private {
+ struct comedi_isadma *dma;
+ unsigned int ad_2scomp:1;
+ unsigned int divisor;
+ int dacsr; /* software copies of registers */
+ int adcsr;
+ int supcsr;
+ int ntrig;
+ int nread;
+ int dma_dir;
+};
+
+static int dt282x_prep_ai_dma(struct comedi_device *dev, int dma_index, int n)
+{
+ struct dt282x_private *devpriv = dev->private;
+ struct comedi_isadma *dma = devpriv->dma;
+ struct comedi_isadma_desc *desc = &dma->desc[dma_index];
+
+ if (!devpriv->ntrig)
+ return 0;
+
+ if (n == 0)
+ n = desc->maxsize;
+ if (n > devpriv->ntrig * 2)
+ n = devpriv->ntrig * 2;
+ devpriv->ntrig -= n / 2;
+
+ desc->size = n;
+ comedi_isadma_set_mode(desc, devpriv->dma_dir);
+
+ comedi_isadma_program(desc);
+
+ return n;
+}
+
+static int dt282x_prep_ao_dma(struct comedi_device *dev, int dma_index, int n)
+{
+ struct dt282x_private *devpriv = dev->private;
+ struct comedi_isadma *dma = devpriv->dma;
+ struct comedi_isadma_desc *desc = &dma->desc[dma_index];
+
+ desc->size = n;
+ comedi_isadma_set_mode(desc, devpriv->dma_dir);
+
+ comedi_isadma_program(desc);
+
+ return n;
+}
+
+static void dt282x_disable_dma(struct comedi_device *dev)
+{
+ struct dt282x_private *devpriv = dev->private;
+ struct comedi_isadma *dma = devpriv->dma;
+ struct comedi_isadma_desc *desc;
+ int i;
+
+ for (i = 0; i < 2; i++) {
+ desc = &dma->desc[i];
+ comedi_isadma_disable(desc->chan);
+ }
+}
+
+static unsigned int dt282x_ns_to_timer(unsigned int *ns, unsigned int flags)
+{
+ unsigned int prescale, base, divider;
+
+ for (prescale = 0; prescale < 16; prescale++) {
+ if (prescale == 1)
+ continue;
+ base = 250 * (1 << prescale);
+ switch (flags & CMDF_ROUND_MASK) {
+ case CMDF_ROUND_NEAREST:
+ default:
+ divider = (*ns + base / 2) / base;
+ break;
+ case CMDF_ROUND_DOWN:
+ divider = (*ns) / base;
+ break;
+ case CMDF_ROUND_UP:
+ divider = (*ns + base - 1) / base;
+ break;
+ }
+ if (divider < 256) {
+ *ns = divider * base;
+ return (prescale << 8) | (255 - divider);
+ }
+ }
+ base = 250 * (1 << 15);
+ divider = 255;
+ *ns = divider * base;
+ return (15 << 8) | (255 - divider);
+}
+
+static void dt282x_munge(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ unsigned short *buf,
+ unsigned int nbytes)
+{
+ struct dt282x_private *devpriv = dev->private;
+ unsigned int val;
+ int i;
+
+ if (nbytes % 2)
+ dev_err(dev->class_dev,
+ "bug! odd number of bytes from dma xfer\n");
+
+ for (i = 0; i < nbytes / 2; i++) {
+ val = buf[i];
+ val &= s->maxdata;
+ if (devpriv->ad_2scomp)
+ val = comedi_offset_munge(s, val);
+
+ buf[i] = val;
+ }
+}
+
+static unsigned int dt282x_ao_setup_dma(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ int cur_dma)
+{
+ struct dt282x_private *devpriv = dev->private;
+ struct comedi_isadma *dma = devpriv->dma;
+ struct comedi_isadma_desc *desc = &dma->desc[cur_dma];
+ unsigned int nsamples = comedi_bytes_to_samples(s, desc->maxsize);
+ unsigned int nbytes;
+
+ nbytes = comedi_buf_read_samples(s, desc->virt_addr, nsamples);
+ if (nbytes)
+ dt282x_prep_ao_dma(dev, cur_dma, nbytes);
+ else
+ dev_err(dev->class_dev, "AO underrun\n");
+
+ return nbytes;
+}
+
+static void dt282x_ao_dma_interrupt(struct comedi_device *dev,
+ struct comedi_subdevice *s)
+{
+ struct dt282x_private *devpriv = dev->private;
+ struct comedi_isadma *dma = devpriv->dma;
+ struct comedi_isadma_desc *desc = &dma->desc[dma->cur_dma];
+
+ outw(devpriv->supcsr | DT2821_SUPCSR_CLRDMADNE,
+ dev->iobase + DT2821_SUPCSR_REG);
+
+ comedi_isadma_disable(desc->chan);
+
+ if (!dt282x_ao_setup_dma(dev, s, dma->cur_dma))
+ s->async->events |= COMEDI_CB_OVERFLOW;
+
+ dma->cur_dma = 1 - dma->cur_dma;
+}
+
+static void dt282x_ai_dma_interrupt(struct comedi_device *dev,
+ struct comedi_subdevice *s)
+{
+ struct dt282x_private *devpriv = dev->private;
+ struct comedi_isadma *dma = devpriv->dma;
+ struct comedi_isadma_desc *desc = &dma->desc[dma->cur_dma];
+ unsigned int nsamples = comedi_bytes_to_samples(s, desc->size);
+ int ret;
+
+ outw(devpriv->supcsr | DT2821_SUPCSR_CLRDMADNE,
+ dev->iobase + DT2821_SUPCSR_REG);
+
+ comedi_isadma_disable(desc->chan);
+
+ dt282x_munge(dev, s, desc->virt_addr, desc->size);
+ ret = comedi_buf_write_samples(s, desc->virt_addr, nsamples);
+ if (ret != desc->size)
+ return;
+
+ devpriv->nread -= nsamples;
+ if (devpriv->nread < 0) {
+ dev_info(dev->class_dev, "nread off by one\n");
+ devpriv->nread = 0;
+ }
+ if (!devpriv->nread) {
+ s->async->events |= COMEDI_CB_EOA;
+ return;
+ }
+#if 0
+ /* clear the dual dma flag, making this the last dma segment */
+ /* XXX probably wrong */
+ if (!devpriv->ntrig) {
+ devpriv->supcsr &= ~DT2821_SUPCSR_DDMA;
+ outw(devpriv->supcsr, dev->iobase + DT2821_SUPCSR_REG);
+ }
+#endif
+ /* restart the channel */
+ dt282x_prep_ai_dma(dev, dma->cur_dma, 0);
+
+ dma->cur_dma = 1 - dma->cur_dma;
+}
+
+static irqreturn_t dt282x_interrupt(int irq, void *d)
+{
+ struct comedi_device *dev = d;
+ struct dt282x_private *devpriv = dev->private;
+ struct comedi_subdevice *s = dev->read_subdev;
+ struct comedi_subdevice *s_ao = dev->write_subdev;
+ unsigned int supcsr, adcsr, dacsr;
+ int handled = 0;
+
+ if (!dev->attached) {
+ dev_err(dev->class_dev, "spurious interrupt\n");
+ return IRQ_HANDLED;
+ }
+
+ adcsr = inw(dev->iobase + DT2821_ADCSR_REG);
+ dacsr = inw(dev->iobase + DT2821_DACSR_REG);
+ supcsr = inw(dev->iobase + DT2821_SUPCSR_REG);
+ if (supcsr & DT2821_SUPCSR_DMAD) {
+ if (devpriv->dma_dir == COMEDI_ISADMA_READ)
+ dt282x_ai_dma_interrupt(dev, s);
+ else
+ dt282x_ao_dma_interrupt(dev, s_ao);
+ handled = 1;
+ }
+ if (adcsr & DT2821_ADCSR_ADERR) {
+ if (devpriv->nread != 0) {
+ dev_err(dev->class_dev, "A/D error\n");
+ s->async->events |= COMEDI_CB_ERROR;
+ }
+ handled = 1;
+ }
+ if (dacsr & DT2821_DACSR_DAERR) {
+ dev_err(dev->class_dev, "D/A error\n");
+ s_ao->async->events |= COMEDI_CB_ERROR;
+ handled = 1;
+ }
+#if 0
+ if (adcsr & DT2821_ADCSR_ADDONE) {
+ unsigned short data;
+
+ data = inw(dev->iobase + DT2821_ADDAT_REG);
+ data &= s->maxdata;
+ if (devpriv->ad_2scomp)
+ data = comedi_offset_munge(s, data);
+
+ comedi_buf_write_samples(s, &data, 1);
+
+ devpriv->nread--;
+ if (!devpriv->nread) {
+ s->async->events |= COMEDI_CB_EOA;
+ } else {
+ if (supcsr & DT2821_SUPCSR_SCDN)
+ outw(devpriv->supcsr | DT2821_SUPCSR_STRIG,
+ dev->iobase + DT2821_SUPCSR_REG);
+ }
+ handled = 1;
+ }
+#endif
+ comedi_handle_events(dev, s);
+ comedi_handle_events(dev, s_ao);
+
+ return IRQ_RETVAL(handled);
+}
+
+static void dt282x_load_changain(struct comedi_device *dev, int n,
+ unsigned int *chanlist)
+{
+ struct dt282x_private *devpriv = dev->private;
+ int i;
+
+ outw(DT2821_CHANCSR_LLE | DT2821_CHANCSR_NUMB(n),
+ dev->iobase + DT2821_CHANCSR_REG);
+ for (i = 0; i < n; i++) {
+ unsigned int chan = CR_CHAN(chanlist[i]);
+ unsigned int range = CR_RANGE(chanlist[i]);
+
+ outw(devpriv->adcsr |
+ DT2821_ADCSR_GS(range) |
+ DT2821_ADCSR_CHAN(chan),
+ dev->iobase + DT2821_ADCSR_REG);
+ }
+ outw(DT2821_CHANCSR_NUMB(n), dev->iobase + DT2821_CHANCSR_REG);
+}
+
+static int dt282x_ai_timeout(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned long context)
+{
+ unsigned int status;
+
+ status = inw(dev->iobase + DT2821_ADCSR_REG);
+ switch (context) {
+ case DT2821_ADCSR_MUXBUSY:
+ if ((status & DT2821_ADCSR_MUXBUSY) == 0)
+ return 0;
+ break;
+ case DT2821_ADCSR_ADDONE:
+ if (status & DT2821_ADCSR_ADDONE)
+ return 0;
+ break;
+ default:
+ return -EINVAL;
+ }
+ return -EBUSY;
+}
+
+/*
+ * Performs a single A/D conversion.
+ * - Put channel/gain into channel-gain list
+ * - preload multiplexer
+ * - trigger conversion and wait for it to finish
+ */
+static int dt282x_ai_insn_read(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ struct dt282x_private *devpriv = dev->private;
+ unsigned int val;
+ int ret;
+ int i;
+
+ /* XXX should we really be enabling the ad clock here? */
+ devpriv->adcsr = DT2821_ADCSR_ADCLK;
+ outw(devpriv->adcsr, dev->iobase + DT2821_ADCSR_REG);
+
+ dt282x_load_changain(dev, 1, &insn->chanspec);
+
+ outw(devpriv->supcsr | DT2821_SUPCSR_PRLD,
+ dev->iobase + DT2821_SUPCSR_REG);
+ ret = comedi_timeout(dev, s, insn,
+ dt282x_ai_timeout, DT2821_ADCSR_MUXBUSY);
+ if (ret)
+ return ret;
+
+ for (i = 0; i < insn->n; i++) {
+ outw(devpriv->supcsr | DT2821_SUPCSR_STRIG,
+ dev->iobase + DT2821_SUPCSR_REG);
+
+ ret = comedi_timeout(dev, s, insn,
+ dt282x_ai_timeout, DT2821_ADCSR_ADDONE);
+ if (ret)
+ return ret;
+
+ val = inw(dev->iobase + DT2821_ADDAT_REG);
+ val &= s->maxdata;
+ if (devpriv->ad_2scomp)
+ val = comedi_offset_munge(s, val);
+
+ data[i] = val;
+ }
+
+ return i;
+}
+
+static int dt282x_ai_cmdtest(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_cmd *cmd)
+{
+ const struct dt282x_board *board = dev->board_ptr;
+ struct dt282x_private *devpriv = dev->private;
+ int err = 0;
+ unsigned int arg;
+
+ /* Step 1 : check if triggers are trivially valid */
+
+ err |= comedi_check_trigger_src(&cmd->start_src, TRIG_NOW);
+ err |= comedi_check_trigger_src(&cmd->scan_begin_src,
+ TRIG_FOLLOW | TRIG_EXT);
+ err |= comedi_check_trigger_src(&cmd->convert_src, TRIG_TIMER);
+ err |= comedi_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
+ err |= comedi_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE);
+
+ if (err)
+ return 1;
+
+ /* Step 2a : make sure trigger sources are unique */
+
+ err |= comedi_check_trigger_is_unique(cmd->scan_begin_src);
+ err |= comedi_check_trigger_is_unique(cmd->stop_src);
+
+ /* Step 2b : and mutually compatible */
+
+ if (err)
+ return 2;
+
+ /* Step 3: check if arguments are trivially valid */
+
+ err |= comedi_check_trigger_arg_is(&cmd->start_arg, 0);
+
+ err |= comedi_check_trigger_arg_is(&cmd->scan_begin_arg, 0);
+
+ err |= comedi_check_trigger_arg_min(&cmd->convert_arg, 4000);
+
+#define SLOWEST_TIMER (250*(1<<15)*255)
+ err |= comedi_check_trigger_arg_max(&cmd->convert_arg, SLOWEST_TIMER);
+ err |= comedi_check_trigger_arg_min(&cmd->convert_arg, board->ai_speed);
+ err |= comedi_check_trigger_arg_is(&cmd->scan_end_arg,
+ cmd->chanlist_len);
+
+ if (cmd->stop_src == TRIG_COUNT)
+ err |= comedi_check_trigger_arg_min(&cmd->stop_arg, 1);
+ else /* TRIG_EXT | TRIG_NONE */
+ err |= comedi_check_trigger_arg_is(&cmd->stop_arg, 0);
+
+ if (err)
+ return 3;
+
+ /* step 4: fix up any arguments */
+
+ arg = cmd->convert_arg;
+ devpriv->divisor = dt282x_ns_to_timer(&arg, cmd->flags);
+ err |= comedi_check_trigger_arg_is(&cmd->convert_arg, arg);
+
+ if (err)
+ return 4;
+
+ return 0;
+}
+
+static int dt282x_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
+{
+ struct dt282x_private *devpriv = dev->private;
+ struct comedi_isadma *dma = devpriv->dma;
+ struct comedi_cmd *cmd = &s->async->cmd;
+ int ret;
+
+ dt282x_disable_dma(dev);
+
+ outw(devpriv->divisor, dev->iobase + DT2821_TMRCTR_REG);
+
+ devpriv->supcsr = DT2821_SUPCSR_ERRINTEN;
+ if (cmd->scan_begin_src == TRIG_FOLLOW)
+ devpriv->supcsr = DT2821_SUPCSR_DS_AD_CLK;
+ else
+ devpriv->supcsr = DT2821_SUPCSR_DS_AD_TRIG;
+ outw(devpriv->supcsr |
+ DT2821_SUPCSR_CLRDMADNE |
+ DT2821_SUPCSR_BUFFB |
+ DT2821_SUPCSR_ADCINIT,
+ dev->iobase + DT2821_SUPCSR_REG);
+
+ devpriv->ntrig = cmd->stop_arg * cmd->scan_end_arg;
+ devpriv->nread = devpriv->ntrig;
+
+ devpriv->dma_dir = COMEDI_ISADMA_READ;
+ dma->cur_dma = 0;
+ dt282x_prep_ai_dma(dev, 0, 0);
+ if (devpriv->ntrig) {
+ dt282x_prep_ai_dma(dev, 1, 0);
+ devpriv->supcsr |= DT2821_SUPCSR_DDMA;
+ outw(devpriv->supcsr, dev->iobase + DT2821_SUPCSR_REG);
+ }
+
+ devpriv->adcsr = 0;
+
+ dt282x_load_changain(dev, cmd->chanlist_len, cmd->chanlist);
+
+ devpriv->adcsr = DT2821_ADCSR_ADCLK | DT2821_ADCSR_IADDONE;
+ outw(devpriv->adcsr, dev->iobase + DT2821_ADCSR_REG);
+
+ outw(devpriv->supcsr | DT2821_SUPCSR_PRLD,
+ dev->iobase + DT2821_SUPCSR_REG);
+ ret = comedi_timeout(dev, s, NULL,
+ dt282x_ai_timeout, DT2821_ADCSR_MUXBUSY);
+ if (ret)
+ return ret;
+
+ if (cmd->scan_begin_src == TRIG_FOLLOW) {
+ outw(devpriv->supcsr | DT2821_SUPCSR_STRIG,
+ dev->iobase + DT2821_SUPCSR_REG);
+ } else {
+ devpriv->supcsr |= DT2821_SUPCSR_XTRIG;
+ outw(devpriv->supcsr, dev->iobase + DT2821_SUPCSR_REG);
+ }
+
+ return 0;
+}
+
+static int dt282x_ai_cancel(struct comedi_device *dev,
+ struct comedi_subdevice *s)
+{
+ struct dt282x_private *devpriv = dev->private;
+
+ dt282x_disable_dma(dev);
+
+ devpriv->adcsr = 0;
+ outw(devpriv->adcsr, dev->iobase + DT2821_ADCSR_REG);
+
+ devpriv->supcsr = 0;
+ outw(devpriv->supcsr | DT2821_SUPCSR_ADCINIT,
+ dev->iobase + DT2821_SUPCSR_REG);
+
+ return 0;
+}
+
+static int dt282x_ao_insn_write(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ struct dt282x_private *devpriv = dev->private;
+ unsigned int chan = CR_CHAN(insn->chanspec);
+ unsigned int range = CR_RANGE(insn->chanspec);
+ int i;
+
+ devpriv->dacsr |= DT2821_DACSR_SSEL | DT2821_DACSR_YSEL(chan);
+
+ for (i = 0; i < insn->n; i++) {
+ unsigned int val = data[i];
+
+ s->readback[chan] = val;
+
+ if (comedi_range_is_bipolar(s, range))
+ val = comedi_offset_munge(s, val);
+
+ outw(devpriv->dacsr, dev->iobase + DT2821_DACSR_REG);
+
+ outw(val, dev->iobase + DT2821_DADAT_REG);
+
+ outw(devpriv->supcsr | DT2821_SUPCSR_DACON,
+ dev->iobase + DT2821_SUPCSR_REG);
+ }
+
+ return insn->n;
+}
+
+static int dt282x_ao_cmdtest(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_cmd *cmd)
+{
+ struct dt282x_private *devpriv = dev->private;
+ int err = 0;
+ unsigned int arg;
+
+ /* Step 1 : check if triggers are trivially valid */
+
+ err |= comedi_check_trigger_src(&cmd->start_src, TRIG_INT);
+ err |= comedi_check_trigger_src(&cmd->scan_begin_src, TRIG_TIMER);
+ err |= comedi_check_trigger_src(&cmd->convert_src, TRIG_NOW);
+ err |= comedi_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
+ err |= comedi_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE);
+
+ if (err)
+ return 1;
+
+ /* Step 2a : make sure trigger sources are unique */
+
+ err |= comedi_check_trigger_is_unique(cmd->stop_src);
+
+ /* Step 2b : and mutually compatible */
+
+ if (err)
+ return 2;
+
+ /* Step 3: check if arguments are trivially valid */
+
+ err |= comedi_check_trigger_arg_is(&cmd->start_arg, 0);
+ err |= comedi_check_trigger_arg_min(&cmd->scan_begin_arg, 5000);
+ err |= comedi_check_trigger_arg_is(&cmd->convert_arg, 0);
+ err |= comedi_check_trigger_arg_is(&cmd->scan_end_arg,
+ cmd->chanlist_len);
+
+ if (cmd->stop_src == TRIG_COUNT)
+ err |= comedi_check_trigger_arg_min(&cmd->stop_arg, 1);
+ else /* TRIG_EXT | TRIG_NONE */
+ err |= comedi_check_trigger_arg_is(&cmd->stop_arg, 0);
+
+ if (err)
+ return 3;
+
+ /* step 4: fix up any arguments */
+
+ arg = cmd->scan_begin_arg;
+ devpriv->divisor = dt282x_ns_to_timer(&arg, cmd->flags);
+ err |= comedi_check_trigger_arg_is(&cmd->scan_begin_arg, arg);
+
+ if (err)
+ return 4;
+
+ return 0;
+}
+
+static int dt282x_ao_inttrig(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ unsigned int trig_num)
+{
+ struct dt282x_private *devpriv = dev->private;
+ struct comedi_cmd *cmd = &s->async->cmd;
+
+ if (trig_num != cmd->start_src)
+ return -EINVAL;
+
+ if (!dt282x_ao_setup_dma(dev, s, 0))
+ return -EPIPE;
+
+ if (!dt282x_ao_setup_dma(dev, s, 1))
+ return -EPIPE;
+
+ outw(devpriv->supcsr | DT2821_SUPCSR_STRIG,
+ dev->iobase + DT2821_SUPCSR_REG);
+ s->async->inttrig = NULL;
+
+ return 1;
+}
+
+static int dt282x_ao_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
+{
+ struct dt282x_private *devpriv = dev->private;
+ struct comedi_isadma *dma = devpriv->dma;
+ struct comedi_cmd *cmd = &s->async->cmd;
+
+ dt282x_disable_dma(dev);
+
+ devpriv->supcsr = DT2821_SUPCSR_ERRINTEN |
+ DT2821_SUPCSR_DS_DA_CLK |
+ DT2821_SUPCSR_DDMA;
+ outw(devpriv->supcsr |
+ DT2821_SUPCSR_CLRDMADNE |
+ DT2821_SUPCSR_BUFFB |
+ DT2821_SUPCSR_DACINIT,
+ dev->iobase + DT2821_SUPCSR_REG);
+
+ devpriv->ntrig = cmd->stop_arg * cmd->chanlist_len;
+ devpriv->nread = devpriv->ntrig;
+
+ devpriv->dma_dir = COMEDI_ISADMA_WRITE;
+ dma->cur_dma = 0;
+
+ outw(devpriv->divisor, dev->iobase + DT2821_TMRCTR_REG);
+
+ /* clear all bits but the DIO direction bits */
+ devpriv->dacsr &= (DT2821_DACSR_LBOE | DT2821_DACSR_HBOE);
+
+ devpriv->dacsr |= (DT2821_DACSR_SSEL |
+ DT2821_DACSR_DACLK |
+ DT2821_DACSR_IDARDY);
+ outw(devpriv->dacsr, dev->iobase + DT2821_DACSR_REG);
+
+ s->async->inttrig = dt282x_ao_inttrig;
+
+ return 0;
+}
+
+static int dt282x_ao_cancel(struct comedi_device *dev,
+ struct comedi_subdevice *s)
+{
+ struct dt282x_private *devpriv = dev->private;
+
+ dt282x_disable_dma(dev);
+
+ /* clear all bits but the DIO direction bits */
+ devpriv->dacsr &= (DT2821_DACSR_LBOE | DT2821_DACSR_HBOE);
+
+ outw(devpriv->dacsr, dev->iobase + DT2821_DACSR_REG);
+
+ devpriv->supcsr = 0;
+ outw(devpriv->supcsr | DT2821_SUPCSR_DACINIT,
+ dev->iobase + DT2821_SUPCSR_REG);
+
+ return 0;
+}
+
+static int dt282x_dio_insn_bits(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ if (comedi_dio_update_state(s, data))
+ outw(s->state, dev->iobase + DT2821_DIODAT_REG);
+
+ data[1] = inw(dev->iobase + DT2821_DIODAT_REG);
+
+ return insn->n;
+}
+
+static int dt282x_dio_insn_config(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ struct dt282x_private *devpriv = dev->private;
+ unsigned int chan = CR_CHAN(insn->chanspec);
+ unsigned int mask;
+ int ret;
+
+ if (chan < 8)
+ mask = 0x00ff;
+ else
+ mask = 0xff00;
+
+ ret = comedi_dio_insn_config(dev, s, insn, data, mask);
+ if (ret)
+ return ret;
+
+ devpriv->dacsr &= ~(DT2821_DACSR_LBOE | DT2821_DACSR_HBOE);
+ if (s->io_bits & 0x00ff)
+ devpriv->dacsr |= DT2821_DACSR_LBOE;
+ if (s->io_bits & 0xff00)
+ devpriv->dacsr |= DT2821_DACSR_HBOE;
+
+ outw(devpriv->dacsr, dev->iobase + DT2821_DACSR_REG);
+
+ return insn->n;
+}
+
+static const struct comedi_lrange *const ai_range_table[] = {
+ &range_dt282x_ai_lo_bipolar,
+ &range_dt282x_ai_lo_unipolar,
+ &range_dt282x_ai_5_bipolar,
+ &range_dt282x_ai_5_unipolar
+};
+
+static const struct comedi_lrange *const ai_range_pgl_table[] = {
+ &range_dt282x_ai_hi_bipolar,
+ &range_dt282x_ai_hi_unipolar
+};
+
+static const struct comedi_lrange *opt_ai_range_lkup(int ispgl, int x)
+{
+ if (ispgl) {
+ if (x < 0 || x >= 2)
+ x = 0;
+ return ai_range_pgl_table[x];
+ }
+
+ if (x < 0 || x >= 4)
+ x = 0;
+ return ai_range_table[x];
+}
+
+static void dt282x_alloc_dma(struct comedi_device *dev,
+ struct comedi_devconfig *it)
+{
+ struct dt282x_private *devpriv = dev->private;
+ unsigned int irq_num = it->options[1];
+ unsigned int dma_chan[2];
+
+ if (it->options[2] < it->options[3]) {
+ dma_chan[0] = it->options[2];
+ dma_chan[1] = it->options[3];
+ } else {
+ dma_chan[0] = it->options[3];
+ dma_chan[1] = it->options[2];
+ }
+
+ if (!irq_num || dma_chan[0] == dma_chan[1] ||
+ dma_chan[0] < 5 || dma_chan[0] > 7 ||
+ dma_chan[1] < 5 || dma_chan[1] > 7)
+ return;
+
+ if (request_irq(irq_num, dt282x_interrupt, 0, dev->board_name, dev))
+ return;
+
+ /* DMA uses two 4K buffers with separate DMA channels */
+ devpriv->dma = comedi_isadma_alloc(dev, 2, dma_chan[0], dma_chan[1],
+ PAGE_SIZE, 0);
+ if (!devpriv->dma)
+ free_irq(irq_num, dev);
+}
+
+static void dt282x_free_dma(struct comedi_device *dev)
+{
+ struct dt282x_private *devpriv = dev->private;
+
+ if (devpriv)
+ comedi_isadma_free(devpriv->dma);
+}
+
+static int dt282x_initialize(struct comedi_device *dev)
+{
+ /* Initialize board */
+ outw(DT2821_SUPCSR_BDINIT, dev->iobase + DT2821_SUPCSR_REG);
+ inw(dev->iobase + DT2821_ADCSR_REG);
+
+ /*
+ * At power up, some registers are in a well-known state.
+ * Check them to see if a DT2821 series board is present.
+ */
+ if (((inw(dev->iobase + DT2821_ADCSR_REG) & 0xfff0) != 0x7c00) ||
+ ((inw(dev->iobase + DT2821_CHANCSR_REG) & 0xf0f0) != 0x70f0) ||
+ ((inw(dev->iobase + DT2821_DACSR_REG) & 0x7c93) != 0x7c90) ||
+ ((inw(dev->iobase + DT2821_SUPCSR_REG) & 0xf8ff) != 0x0000) ||
+ ((inw(dev->iobase + DT2821_TMRCTR_REG) & 0xff00) != 0xf000)) {
+ dev_err(dev->class_dev, "board not found\n");
+ return -EIO;
+ }
+ return 0;
+}
+
+/*
+ options:
+ 0 i/o base
+ 1 irq
+ 2 dma1
+ 3 dma2
+ 4 0=single ended, 1=differential
+ 5 ai 0=straight binary, 1=2's comp
+ 6 ao0 0=straight binary, 1=2's comp
+ 7 ao1 0=straight binary, 1=2's comp
+ 8 ai 0=±10 V, 1=0-10 V, 2=±5 V, 3=0-5 V
+ 9 ao0 0=±10 V, 1=0-10 V, 2=±5 V, 3=0-5 V, 4=±2.5 V
+ 10 ao1 0=±10 V, 1=0-10 V, 2=±5 V, 3=0-5 V, 4=±2.5 V
+ */
+static int dt282x_attach(struct comedi_device *dev, struct comedi_devconfig *it)
+{
+ const struct dt282x_board *board = dev->board_ptr;
+ struct dt282x_private *devpriv;
+ struct comedi_subdevice *s;
+ int ret;
+
+ ret = comedi_request_region(dev, it->options[0], 0x10);
+ if (ret)
+ return ret;
+
+ ret = dt282x_initialize(dev);
+ if (ret)
+ return ret;
+
+ devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
+ if (!devpriv)
+ return -ENOMEM;
+
+ /* an IRQ and 2 DMA channels are required for async command support */
+ dt282x_alloc_dma(dev, it);
+
+ ret = comedi_alloc_subdevices(dev, 3);
+ if (ret)
+ return ret;
+
+ /* Analog Input subdevice */
+ s = &dev->subdevices[0];
+ s->type = COMEDI_SUBD_AI;
+ s->subdev_flags = SDF_READABLE;
+ if ((it->options[4] && board->adchan_di) || board->adchan_se == 0) {
+ s->subdev_flags |= SDF_DIFF;
+ s->n_chan = board->adchan_di;
+ } else {
+ s->subdev_flags |= SDF_COMMON;
+ s->n_chan = board->adchan_se;
+ }
+ s->maxdata = board->ai_maxdata;
+
+ s->range_table = opt_ai_range_lkup(board->ispgl, it->options[8]);
+ devpriv->ad_2scomp = it->options[5] ? 1 : 0;
+
+ s->insn_read = dt282x_ai_insn_read;
+ if (dev->irq) {
+ dev->read_subdev = s;
+ s->subdev_flags |= SDF_CMD_READ;
+ s->len_chanlist = s->n_chan;
+ s->do_cmdtest = dt282x_ai_cmdtest;
+ s->do_cmd = dt282x_ai_cmd;
+ s->cancel = dt282x_ai_cancel;
+ }
+
+ /* Analog Output subdevice */
+ s = &dev->subdevices[1];
+ if (board->dachan) {
+ s->type = COMEDI_SUBD_AO;
+ s->subdev_flags = SDF_WRITABLE;
+ s->n_chan = board->dachan;
+ s->maxdata = board->ao_maxdata;
+ /* ranges are per-channel, set by jumpers on the board */
+ s->range_table = &dt282x_ao_range;
+ s->insn_write = dt282x_ao_insn_write;
+ if (dev->irq) {
+ dev->write_subdev = s;
+ s->subdev_flags |= SDF_CMD_WRITE;
+ s->len_chanlist = s->n_chan;
+ s->do_cmdtest = dt282x_ao_cmdtest;
+ s->do_cmd = dt282x_ao_cmd;
+ s->cancel = dt282x_ao_cancel;
+ }
+
+ ret = comedi_alloc_subdev_readback(s);
+ if (ret)
+ return ret;
+ } else {
+ s->type = COMEDI_SUBD_UNUSED;
+ }
+
+ /* Digital I/O subdevice */
+ s = &dev->subdevices[2];
+ s->type = COMEDI_SUBD_DIO;
+ s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
+ s->n_chan = 16;
+ s->maxdata = 1;
+ s->range_table = &range_digital;
+ s->insn_bits = dt282x_dio_insn_bits;
+ s->insn_config = dt282x_dio_insn_config;
+
+ return 0;
+}
+
+static void dt282x_detach(struct comedi_device *dev)
+{
+ dt282x_free_dma(dev);
+ comedi_legacy_detach(dev);
+}
+
+static struct comedi_driver dt282x_driver = {
+ .driver_name = "dt282x",
+ .module = THIS_MODULE,
+ .attach = dt282x_attach,
+ .detach = dt282x_detach,
+ .board_name = &boardtypes[0].name,
+ .num_names = ARRAY_SIZE(boardtypes),
+ .offset = sizeof(struct dt282x_board),
+};
+module_comedi_driver(dt282x_driver);
+
+MODULE_AUTHOR("Comedi http://www.comedi.org");
+MODULE_DESCRIPTION("Comedi driver for Data Translation DT2821 series");
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/comedi/drivers/dt3000.c b/drivers/staging/comedi/drivers/dt3000.c
new file mode 100644
index 000000000..031282c82
--- /dev/null
+++ b/drivers/staging/comedi/drivers/dt3000.c
@@ -0,0 +1,769 @@
+/*
+ comedi/drivers/dt3000.c
+ Data Translation DT3000 series driver
+
+ COMEDI - Linux Control and Measurement Device Interface
+ Copyright (C) 1999 David A. Schleef <ds@schleef.org>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+*/
+/*
+Driver: dt3000
+Description: Data Translation DT3000 series
+Author: ds
+Devices: [Data Translation] DT3001 (dt3000), DT3001-PGL, DT3002, DT3003,
+ DT3003-PGL, DT3004, DT3005, DT3004-200
+Updated: Mon, 14 Apr 2008 15:41:24 +0100
+Status: works
+
+Configuration Options: not applicable, uses PCI auto config
+
+There is code to support AI commands, but it may not work.
+
+AO commands are not supported.
+*/
+
+/*
+ The DT3000 series is Data Translation's attempt to make a PCI
+ data acquisition board. The design of this series is very nice,
+ since each board has an on-board DSP (Texas Instruments TMS320C52).
+ However, a few details are a little annoying. The boards lack
+ bus-mastering DMA, which eliminates them from serious work.
+ They also are not capable of autocalibration, which is a common
+ feature in modern hardware. The default firmware is pretty bad,
+ making it nearly impossible to write an RT compatible driver.
+ It would make an interesting project to write a decent firmware
+ for these boards.
+
+ Data Translation originally wanted an NDA for the documentation
+ for the 3k series. However, if you ask nicely, they might send
+ you the docs without one, also.
+*/
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+
+#include "../comedi_pci.h"
+
+static const struct comedi_lrange range_dt3000_ai = {
+ 4, {
+ BIP_RANGE(10),
+ BIP_RANGE(5),
+ BIP_RANGE(2.5),
+ BIP_RANGE(1.25)
+ }
+};
+
+static const struct comedi_lrange range_dt3000_ai_pgl = {
+ 4, {
+ BIP_RANGE(10),
+ BIP_RANGE(1),
+ BIP_RANGE(0.1),
+ BIP_RANGE(0.02)
+ }
+};
+
+enum dt3k_boardid {
+ BOARD_DT3001,
+ BOARD_DT3001_PGL,
+ BOARD_DT3002,
+ BOARD_DT3003,
+ BOARD_DT3003_PGL,
+ BOARD_DT3004,
+ BOARD_DT3005,
+};
+
+struct dt3k_boardtype {
+ const char *name;
+ int adchan;
+ int adbits;
+ int ai_speed;
+ const struct comedi_lrange *adrange;
+ int dachan;
+ int dabits;
+};
+
+static const struct dt3k_boardtype dt3k_boardtypes[] = {
+ [BOARD_DT3001] = {
+ .name = "dt3001",
+ .adchan = 16,
+ .adbits = 12,
+ .adrange = &range_dt3000_ai,
+ .ai_speed = 3000,
+ .dachan = 2,
+ .dabits = 12,
+ },
+ [BOARD_DT3001_PGL] = {
+ .name = "dt3001-pgl",
+ .adchan = 16,
+ .adbits = 12,
+ .adrange = &range_dt3000_ai_pgl,
+ .ai_speed = 3000,
+ .dachan = 2,
+ .dabits = 12,
+ },
+ [BOARD_DT3002] = {
+ .name = "dt3002",
+ .adchan = 32,
+ .adbits = 12,
+ .adrange = &range_dt3000_ai,
+ .ai_speed = 3000,
+ },
+ [BOARD_DT3003] = {
+ .name = "dt3003",
+ .adchan = 64,
+ .adbits = 12,
+ .adrange = &range_dt3000_ai,
+ .ai_speed = 3000,
+ .dachan = 2,
+ .dabits = 12,
+ },
+ [BOARD_DT3003_PGL] = {
+ .name = "dt3003-pgl",
+ .adchan = 64,
+ .adbits = 12,
+ .adrange = &range_dt3000_ai_pgl,
+ .ai_speed = 3000,
+ .dachan = 2,
+ .dabits = 12,
+ },
+ [BOARD_DT3004] = {
+ .name = "dt3004",
+ .adchan = 16,
+ .adbits = 16,
+ .adrange = &range_dt3000_ai,
+ .ai_speed = 10000,
+ .dachan = 2,
+ .dabits = 12,
+ },
+ [BOARD_DT3005] = {
+ .name = "dt3005", /* a.k.a. 3004-200 */
+ .adchan = 16,
+ .adbits = 16,
+ .adrange = &range_dt3000_ai,
+ .ai_speed = 5000,
+ .dachan = 2,
+ .dabits = 12,
+ },
+};
+
+/* dual-ported RAM location definitions */
+
+#define DPR_DAC_buffer (4*0x000)
+#define DPR_ADC_buffer (4*0x800)
+#define DPR_Command (4*0xfd3)
+#define DPR_SubSys (4*0xfd3)
+#define DPR_Encode (4*0xfd4)
+#define DPR_Params(a) (4*(0xfd5+(a)))
+#define DPR_Tick_Reg_Lo (4*0xff5)
+#define DPR_Tick_Reg_Hi (4*0xff6)
+#define DPR_DA_Buf_Front (4*0xff7)
+#define DPR_DA_Buf_Rear (4*0xff8)
+#define DPR_AD_Buf_Front (4*0xff9)
+#define DPR_AD_Buf_Rear (4*0xffa)
+#define DPR_Int_Mask (4*0xffb)
+#define DPR_Intr_Flag (4*0xffc)
+#define DPR_Response_Mbx (4*0xffe)
+#define DPR_Command_Mbx (4*0xfff)
+
+#define AI_FIFO_DEPTH 2003
+#define AO_FIFO_DEPTH 2048
+
+/* command list */
+
+#define CMD_GETBRDINFO 0
+#define CMD_CONFIG 1
+#define CMD_GETCONFIG 2
+#define CMD_START 3
+#define CMD_STOP 4
+#define CMD_READSINGLE 5
+#define CMD_WRITESINGLE 6
+#define CMD_CALCCLOCK 7
+#define CMD_READEVENTS 8
+#define CMD_WRITECTCTRL 16
+#define CMD_READCTCTRL 17
+#define CMD_WRITECT 18
+#define CMD_READCT 19
+#define CMD_WRITEDATA 32
+#define CMD_READDATA 33
+#define CMD_WRITEIO 34
+#define CMD_READIO 35
+#define CMD_WRITECODE 36
+#define CMD_READCODE 37
+#define CMD_EXECUTE 38
+#define CMD_HALT 48
+
+#define SUBS_AI 0
+#define SUBS_AO 1
+#define SUBS_DIN 2
+#define SUBS_DOUT 3
+#define SUBS_MEM 4
+#define SUBS_CT 5
+
+/* interrupt flags */
+#define DT3000_CMDONE 0x80
+#define DT3000_CTDONE 0x40
+#define DT3000_DAHWERR 0x20
+#define DT3000_DASWERR 0x10
+#define DT3000_DAEMPTY 0x08
+#define DT3000_ADHWERR 0x04
+#define DT3000_ADSWERR 0x02
+#define DT3000_ADFULL 0x01
+
+#define DT3000_COMPLETION_MASK 0xff00
+#define DT3000_COMMAND_MASK 0x00ff
+#define DT3000_NOTPROCESSED 0x0000
+#define DT3000_NOERROR 0x5500
+#define DT3000_ERROR 0xaa00
+#define DT3000_NOTSUPPORTED 0xff00
+
+#define DT3000_EXTERNAL_CLOCK 1
+#define DT3000_RISING_EDGE 2
+
+#define TMODE_MASK 0x1c
+
+#define DT3000_AD_TRIG_INTERNAL (0<<2)
+#define DT3000_AD_TRIG_EXTERNAL (1<<2)
+#define DT3000_AD_RETRIG_INTERNAL (2<<2)
+#define DT3000_AD_RETRIG_EXTERNAL (3<<2)
+#define DT3000_AD_EXTRETRIG (4<<2)
+
+#define DT3000_CHANNEL_MODE_SE 0
+#define DT3000_CHANNEL_MODE_DI 1
+
+struct dt3k_private {
+ unsigned int lock;
+ unsigned int ai_front;
+ unsigned int ai_rear;
+};
+
+#define TIMEOUT 100
+
+static void dt3k_send_cmd(struct comedi_device *dev, unsigned int cmd)
+{
+ int i;
+ unsigned int status = 0;
+
+ writew(cmd, dev->mmio + DPR_Command_Mbx);
+
+ for (i = 0; i < TIMEOUT; i++) {
+ status = readw(dev->mmio + DPR_Command_Mbx);
+ if ((status & DT3000_COMPLETION_MASK) != DT3000_NOTPROCESSED)
+ break;
+ udelay(1);
+ }
+
+ if ((status & DT3000_COMPLETION_MASK) != DT3000_NOERROR)
+ dev_dbg(dev->class_dev, "%s: timeout/error status=0x%04x\n",
+ __func__, status);
+}
+
+static unsigned int dt3k_readsingle(struct comedi_device *dev,
+ unsigned int subsys, unsigned int chan,
+ unsigned int gain)
+{
+ writew(subsys, dev->mmio + DPR_SubSys);
+
+ writew(chan, dev->mmio + DPR_Params(0));
+ writew(gain, dev->mmio + DPR_Params(1));
+
+ dt3k_send_cmd(dev, CMD_READSINGLE);
+
+ return readw(dev->mmio + DPR_Params(2));
+}
+
+static void dt3k_writesingle(struct comedi_device *dev, unsigned int subsys,
+ unsigned int chan, unsigned int data)
+{
+ writew(subsys, dev->mmio + DPR_SubSys);
+
+ writew(chan, dev->mmio + DPR_Params(0));
+ writew(0, dev->mmio + DPR_Params(1));
+ writew(data, dev->mmio + DPR_Params(2));
+
+ dt3k_send_cmd(dev, CMD_WRITESINGLE);
+}
+
+static void dt3k_ai_empty_fifo(struct comedi_device *dev,
+ struct comedi_subdevice *s)
+{
+ struct dt3k_private *devpriv = dev->private;
+ int front;
+ int rear;
+ int count;
+ int i;
+ unsigned short data;
+
+ front = readw(dev->mmio + DPR_AD_Buf_Front);
+ count = front - devpriv->ai_front;
+ if (count < 0)
+ count += AI_FIFO_DEPTH;
+
+ rear = devpriv->ai_rear;
+
+ for (i = 0; i < count; i++) {
+ data = readw(dev->mmio + DPR_ADC_buffer + rear);
+ comedi_buf_write_samples(s, &data, 1);
+ rear++;
+ if (rear >= AI_FIFO_DEPTH)
+ rear = 0;
+ }
+
+ devpriv->ai_rear = rear;
+ writew(rear, dev->mmio + DPR_AD_Buf_Rear);
+}
+
+static int dt3k_ai_cancel(struct comedi_device *dev,
+ struct comedi_subdevice *s)
+{
+ writew(SUBS_AI, dev->mmio + DPR_SubSys);
+ dt3k_send_cmd(dev, CMD_STOP);
+
+ writew(0, dev->mmio + DPR_Int_Mask);
+
+ return 0;
+}
+
+static int debug_n_ints;
+
+/* FIXME! Assumes shared interrupt is for this card. */
+/* What's this debug_n_ints stuff? Obviously needs some work... */
+static irqreturn_t dt3k_interrupt(int irq, void *d)
+{
+ struct comedi_device *dev = d;
+ struct comedi_subdevice *s = dev->read_subdev;
+ unsigned int status;
+
+ if (!dev->attached)
+ return IRQ_NONE;
+
+ status = readw(dev->mmio + DPR_Intr_Flag);
+
+ if (status & DT3000_ADFULL)
+ dt3k_ai_empty_fifo(dev, s);
+
+ if (status & (DT3000_ADSWERR | DT3000_ADHWERR))
+ s->async->events |= COMEDI_CB_ERROR;
+
+ debug_n_ints++;
+ if (debug_n_ints >= 10)
+ s->async->events |= COMEDI_CB_EOA;
+
+ comedi_handle_events(dev, s);
+ return IRQ_HANDLED;
+}
+
+static int dt3k_ns_to_timer(unsigned int timer_base, unsigned int *nanosec,
+ unsigned int flags)
+{
+ int divider, base, prescale;
+
+ /* This function needs improvment */
+ /* Don't know if divider==0 works. */
+
+ for (prescale = 0; prescale < 16; prescale++) {
+ base = timer_base * (prescale + 1);
+ switch (flags & CMDF_ROUND_MASK) {
+ case CMDF_ROUND_NEAREST:
+ default:
+ divider = (*nanosec + base / 2) / base;
+ break;
+ case CMDF_ROUND_DOWN:
+ divider = (*nanosec) / base;
+ break;
+ case CMDF_ROUND_UP:
+ divider = (*nanosec) / base;
+ break;
+ }
+ if (divider < 65536) {
+ *nanosec = divider * base;
+ return (prescale << 16) | (divider);
+ }
+ }
+
+ prescale = 15;
+ base = timer_base * (1 << prescale);
+ divider = 65535;
+ *nanosec = divider * base;
+ return (prescale << 16) | (divider);
+}
+
+static int dt3k_ai_cmdtest(struct comedi_device *dev,
+ struct comedi_subdevice *s, struct comedi_cmd *cmd)
+{
+ const struct dt3k_boardtype *this_board = dev->board_ptr;
+ int err = 0;
+ unsigned int arg;
+
+ /* Step 1 : check if triggers are trivially valid */
+
+ err |= comedi_check_trigger_src(&cmd->start_src, TRIG_NOW);
+ err |= comedi_check_trigger_src(&cmd->scan_begin_src, TRIG_TIMER);
+ err |= comedi_check_trigger_src(&cmd->convert_src, TRIG_TIMER);
+ err |= comedi_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
+ err |= comedi_check_trigger_src(&cmd->stop_src, TRIG_COUNT);
+
+ if (err)
+ return 1;
+
+ /* Step 2a : make sure trigger sources are unique */
+ /* Step 2b : and mutually compatible */
+
+ /* Step 3: check if arguments are trivially valid */
+
+ err |= comedi_check_trigger_arg_is(&cmd->start_arg, 0);
+
+ if (cmd->scan_begin_src == TRIG_TIMER) {
+ err |= comedi_check_trigger_arg_min(&cmd->scan_begin_arg,
+ this_board->ai_speed);
+ err |= comedi_check_trigger_arg_max(&cmd->scan_begin_arg,
+ 100 * 16 * 65535);
+ }
+
+ if (cmd->convert_src == TRIG_TIMER) {
+ err |= comedi_check_trigger_arg_min(&cmd->convert_arg,
+ this_board->ai_speed);
+ err |= comedi_check_trigger_arg_max(&cmd->convert_arg,
+ 50 * 16 * 65535);
+ }
+
+ err |= comedi_check_trigger_arg_is(&cmd->scan_end_arg,
+ cmd->chanlist_len);
+
+ if (cmd->stop_src == TRIG_COUNT)
+ err |= comedi_check_trigger_arg_max(&cmd->stop_arg, 0x00ffffff);
+ else /* TRIG_NONE */
+ err |= comedi_check_trigger_arg_is(&cmd->stop_arg, 0);
+
+ if (err)
+ return 3;
+
+ /* step 4: fix up any arguments */
+
+ if (cmd->scan_begin_src == TRIG_TIMER) {
+ arg = cmd->scan_begin_arg;
+ dt3k_ns_to_timer(100, &arg, cmd->flags);
+ err |= comedi_check_trigger_arg_is(&cmd->scan_begin_arg, arg);
+ }
+
+ if (cmd->convert_src == TRIG_TIMER) {
+ arg = cmd->convert_arg;
+ dt3k_ns_to_timer(50, &arg, cmd->flags);
+ err |= comedi_check_trigger_arg_is(&cmd->convert_arg, arg);
+
+ if (cmd->scan_begin_src == TRIG_TIMER) {
+ arg = cmd->convert_arg * cmd->scan_end_arg;
+ err |= comedi_check_trigger_arg_min(&cmd->
+ scan_begin_arg,
+ arg);
+ }
+ }
+
+ if (err)
+ return 4;
+
+ return 0;
+}
+
+static int dt3k_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
+{
+ struct comedi_cmd *cmd = &s->async->cmd;
+ int i;
+ unsigned int chan, range, aref;
+ unsigned int divider;
+ unsigned int tscandiv;
+
+ for (i = 0; i < cmd->chanlist_len; i++) {
+ chan = CR_CHAN(cmd->chanlist[i]);
+ range = CR_RANGE(cmd->chanlist[i]);
+
+ writew((range << 6) | chan, dev->mmio + DPR_ADC_buffer + i);
+ }
+ aref = CR_AREF(cmd->chanlist[0]);
+
+ writew(cmd->scan_end_arg, dev->mmio + DPR_Params(0));
+
+ if (cmd->convert_src == TRIG_TIMER) {
+ divider = dt3k_ns_to_timer(50, &cmd->convert_arg, cmd->flags);
+ writew((divider >> 16), dev->mmio + DPR_Params(1));
+ writew((divider & 0xffff), dev->mmio + DPR_Params(2));
+ }
+
+ if (cmd->scan_begin_src == TRIG_TIMER) {
+ tscandiv = dt3k_ns_to_timer(100, &cmd->scan_begin_arg,
+ cmd->flags);
+ writew((tscandiv >> 16), dev->mmio + DPR_Params(3));
+ writew((tscandiv & 0xffff), dev->mmio + DPR_Params(4));
+ }
+
+ writew(DT3000_AD_RETRIG_INTERNAL, dev->mmio + DPR_Params(5));
+ writew(aref == AREF_DIFF, dev->mmio + DPR_Params(6));
+
+ writew(AI_FIFO_DEPTH / 2, dev->mmio + DPR_Params(7));
+
+ writew(SUBS_AI, dev->mmio + DPR_SubSys);
+ dt3k_send_cmd(dev, CMD_CONFIG);
+
+ writew(DT3000_ADFULL | DT3000_ADSWERR | DT3000_ADHWERR,
+ dev->mmio + DPR_Int_Mask);
+
+ debug_n_ints = 0;
+
+ writew(SUBS_AI, dev->mmio + DPR_SubSys);
+ dt3k_send_cmd(dev, CMD_START);
+
+ return 0;
+}
+
+static int dt3k_ai_insn(struct comedi_device *dev, struct comedi_subdevice *s,
+ struct comedi_insn *insn, unsigned int *data)
+{
+ int i;
+ unsigned int chan, gain, aref;
+
+ chan = CR_CHAN(insn->chanspec);
+ gain = CR_RANGE(insn->chanspec);
+ /* XXX docs don't explain how to select aref */
+ aref = CR_AREF(insn->chanspec);
+
+ for (i = 0; i < insn->n; i++)
+ data[i] = dt3k_readsingle(dev, SUBS_AI, chan, gain);
+
+ return i;
+}
+
+static int dt3k_ao_insn_write(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ unsigned int chan = CR_CHAN(insn->chanspec);
+ unsigned int val = s->readback[chan];
+ int i;
+
+ for (i = 0; i < insn->n; i++) {
+ val = data[i];
+ dt3k_writesingle(dev, SUBS_AO, chan, val);
+ }
+ s->readback[chan] = val;
+
+ return insn->n;
+}
+
+static void dt3k_dio_config(struct comedi_device *dev, int bits)
+{
+ /* XXX */
+ writew(SUBS_DOUT, dev->mmio + DPR_SubSys);
+
+ writew(bits, dev->mmio + DPR_Params(0));
+#if 0
+ /* don't know */
+ writew(0, dev->mmio + DPR_Params(1));
+ writew(0, dev->mmio + DPR_Params(2));
+#endif
+
+ dt3k_send_cmd(dev, CMD_CONFIG);
+}
+
+static int dt3k_dio_insn_config(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ unsigned int chan = CR_CHAN(insn->chanspec);
+ unsigned int mask;
+ int ret;
+
+ if (chan < 4)
+ mask = 0x0f;
+ else
+ mask = 0xf0;
+
+ ret = comedi_dio_insn_config(dev, s, insn, data, mask);
+ if (ret)
+ return ret;
+
+ dt3k_dio_config(dev, (s->io_bits & 0x01) | ((s->io_bits & 0x10) >> 3));
+
+ return insn->n;
+}
+
+static int dt3k_dio_insn_bits(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ if (comedi_dio_update_state(s, data))
+ dt3k_writesingle(dev, SUBS_DOUT, 0, s->state);
+
+ data[1] = dt3k_readsingle(dev, SUBS_DIN, 0, 0);
+
+ return insn->n;
+}
+
+static int dt3k_mem_insn_read(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ unsigned int addr = CR_CHAN(insn->chanspec);
+ int i;
+
+ for (i = 0; i < insn->n; i++) {
+ writew(SUBS_MEM, dev->mmio + DPR_SubSys);
+ writew(addr, dev->mmio + DPR_Params(0));
+ writew(1, dev->mmio + DPR_Params(1));
+
+ dt3k_send_cmd(dev, CMD_READCODE);
+
+ data[i] = readw(dev->mmio + DPR_Params(2));
+ }
+
+ return i;
+}
+
+static int dt3000_auto_attach(struct comedi_device *dev,
+ unsigned long context)
+{
+ struct pci_dev *pcidev = comedi_to_pci_dev(dev);
+ const struct dt3k_boardtype *this_board = NULL;
+ struct dt3k_private *devpriv;
+ struct comedi_subdevice *s;
+ int ret = 0;
+
+ if (context < ARRAY_SIZE(dt3k_boardtypes))
+ this_board = &dt3k_boardtypes[context];
+ if (!this_board)
+ return -ENODEV;
+ dev->board_ptr = this_board;
+ dev->board_name = this_board->name;
+
+ devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
+ if (!devpriv)
+ return -ENOMEM;
+
+ ret = comedi_pci_enable(dev);
+ if (ret < 0)
+ return ret;
+
+ dev->mmio = pci_ioremap_bar(pcidev, 0);
+ if (!dev->mmio)
+ return -ENOMEM;
+
+ if (pcidev->irq) {
+ ret = request_irq(pcidev->irq, dt3k_interrupt, IRQF_SHARED,
+ dev->board_name, dev);
+ if (ret == 0)
+ dev->irq = pcidev->irq;
+ }
+
+ ret = comedi_alloc_subdevices(dev, 4);
+ if (ret)
+ return ret;
+
+ s = &dev->subdevices[0];
+ /* ai subdevice */
+ s->type = COMEDI_SUBD_AI;
+ s->subdev_flags = SDF_READABLE | SDF_GROUND | SDF_DIFF;
+ s->n_chan = this_board->adchan;
+ s->insn_read = dt3k_ai_insn;
+ s->maxdata = (1 << this_board->adbits) - 1;
+ s->range_table = &range_dt3000_ai; /* XXX */
+ if (dev->irq) {
+ dev->read_subdev = s;
+ s->subdev_flags |= SDF_CMD_READ;
+ s->len_chanlist = 512;
+ s->do_cmd = dt3k_ai_cmd;
+ s->do_cmdtest = dt3k_ai_cmdtest;
+ s->cancel = dt3k_ai_cancel;
+ }
+
+ s = &dev->subdevices[1];
+ /* ao subsystem */
+ s->type = COMEDI_SUBD_AO;
+ s->subdev_flags = SDF_WRITABLE;
+ s->n_chan = 2;
+ s->maxdata = (1 << this_board->dabits) - 1;
+ s->len_chanlist = 1;
+ s->range_table = &range_bipolar10;
+ s->insn_write = dt3k_ao_insn_write;
+
+ ret = comedi_alloc_subdev_readback(s);
+ if (ret)
+ return ret;
+
+ s = &dev->subdevices[2];
+ /* dio subsystem */
+ s->type = COMEDI_SUBD_DIO;
+ s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
+ s->n_chan = 8;
+ s->insn_config = dt3k_dio_insn_config;
+ s->insn_bits = dt3k_dio_insn_bits;
+ s->maxdata = 1;
+ s->len_chanlist = 8;
+ s->range_table = &range_digital;
+
+ s = &dev->subdevices[3];
+ /* mem subsystem */
+ s->type = COMEDI_SUBD_MEMORY;
+ s->subdev_flags = SDF_READABLE;
+ s->n_chan = 0x1000;
+ s->insn_read = dt3k_mem_insn_read;
+ s->maxdata = 0xff;
+ s->len_chanlist = 1;
+ s->range_table = &range_unknown;
+
+#if 0
+ s = &dev->subdevices[4];
+ /* proc subsystem */
+ s->type = COMEDI_SUBD_PROC;
+#endif
+
+ return 0;
+}
+
+static struct comedi_driver dt3000_driver = {
+ .driver_name = "dt3000",
+ .module = THIS_MODULE,
+ .auto_attach = dt3000_auto_attach,
+ .detach = comedi_pci_detach,
+};
+
+static int dt3000_pci_probe(struct pci_dev *dev,
+ const struct pci_device_id *id)
+{
+ return comedi_pci_auto_config(dev, &dt3000_driver, id->driver_data);
+}
+
+static const struct pci_device_id dt3000_pci_table[] = {
+ { PCI_VDEVICE(DT, 0x0022), BOARD_DT3001 },
+ { PCI_VDEVICE(DT, 0x0023), BOARD_DT3002 },
+ { PCI_VDEVICE(DT, 0x0024), BOARD_DT3003 },
+ { PCI_VDEVICE(DT, 0x0025), BOARD_DT3004 },
+ { PCI_VDEVICE(DT, 0x0026), BOARD_DT3005 },
+ { PCI_VDEVICE(DT, 0x0027), BOARD_DT3001_PGL },
+ { PCI_VDEVICE(DT, 0x0028), BOARD_DT3003_PGL },
+ { 0 }
+};
+MODULE_DEVICE_TABLE(pci, dt3000_pci_table);
+
+static struct pci_driver dt3000_pci_driver = {
+ .name = "dt3000",
+ .id_table = dt3000_pci_table,
+ .probe = dt3000_pci_probe,
+ .remove = comedi_pci_auto_unconfig,
+};
+module_comedi_pci_driver(dt3000_driver, dt3000_pci_driver);
+
+MODULE_AUTHOR("Comedi http://www.comedi.org");
+MODULE_DESCRIPTION("Comedi low-level driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/comedi/drivers/dt9812.c b/drivers/staging/comedi/drivers/dt9812.c
new file mode 100644
index 000000000..e11c216a4
--- /dev/null
+++ b/drivers/staging/comedi/drivers/dt9812.c
@@ -0,0 +1,885 @@
+/*
+ * comedi/drivers/dt9812.c
+ * COMEDI driver for DataTranslation DT9812 USB module
+ *
+ * Copyright (C) 2005 Anders Blomdell <anders.blomdell@control.lth.se>
+ *
+ * COMEDI - Linux Control and Measurement Device Interface
+ *
+ * This program is free software; you can redistribute it and/or 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.
+ */
+
+/*
+Driver: dt9812
+Description: Data Translation DT9812 USB module
+Author: anders.blomdell@control.lth.se (Anders Blomdell)
+Status: in development
+Devices: [Data Translation] DT9812 (dt9812)
+Updated: Sun Nov 20 20:18:34 EST 2005
+
+This driver works, but bulk transfers not implemented. Might be a starting point
+for someone else. I found out too late that USB has too high latencies (>1 ms)
+for my needs.
+*/
+
+/*
+ * Nota Bene:
+ * 1. All writes to command pipe has to be 32 bytes (ISP1181B SHRTP=0 ?)
+ * 2. The DDK source (as of sep 2005) is in error regarding the
+ * input MUX bits (example code says P4, but firmware schematics
+ * says P1).
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/uaccess.h>
+
+#include "../comedi_usb.h"
+
+#define DT9812_DIAGS_BOARD_INFO_ADDR 0xFBFF
+#define DT9812_MAX_WRITE_CMD_PIPE_SIZE 32
+#define DT9812_MAX_READ_CMD_PIPE_SIZE 32
+
+/* usb_bulk_msg() timout in milliseconds */
+#define DT9812_USB_TIMEOUT 1000
+
+/*
+ * See Silican Laboratories C8051F020/1/2/3 manual
+ */
+#define F020_SFR_P4 0x84
+#define F020_SFR_P1 0x90
+#define F020_SFR_P2 0xa0
+#define F020_SFR_P3 0xb0
+#define F020_SFR_AMX0CF 0xba
+#define F020_SFR_AMX0SL 0xbb
+#define F020_SFR_ADC0CF 0xbc
+#define F020_SFR_ADC0L 0xbe
+#define F020_SFR_ADC0H 0xbf
+#define F020_SFR_DAC0L 0xd2
+#define F020_SFR_DAC0H 0xd3
+#define F020_SFR_DAC0CN 0xd4
+#define F020_SFR_DAC1L 0xd5
+#define F020_SFR_DAC1H 0xd6
+#define F020_SFR_DAC1CN 0xd7
+#define F020_SFR_ADC0CN 0xe8
+
+#define F020_MASK_ADC0CF_AMP0GN0 0x01
+#define F020_MASK_ADC0CF_AMP0GN1 0x02
+#define F020_MASK_ADC0CF_AMP0GN2 0x04
+
+#define F020_MASK_ADC0CN_AD0EN 0x80
+#define F020_MASK_ADC0CN_AD0INT 0x20
+#define F020_MASK_ADC0CN_AD0BUSY 0x10
+
+#define F020_MASK_DACxCN_DACxEN 0x80
+
+enum {
+ /* A/D D/A DI DO CT */
+ DT9812_DEVID_DT9812_10, /* 8 2 8 8 1 +/- 10V */
+ DT9812_DEVID_DT9812_2PT5, /* 8 2 8 8 1 0-2.44V */
+};
+
+enum dt9812_gain {
+ DT9812_GAIN_0PT25 = 1,
+ DT9812_GAIN_0PT5 = 2,
+ DT9812_GAIN_1 = 4,
+ DT9812_GAIN_2 = 8,
+ DT9812_GAIN_4 = 16,
+ DT9812_GAIN_8 = 32,
+ DT9812_GAIN_16 = 64,
+};
+
+enum {
+ DT9812_LEAST_USB_FIRMWARE_CMD_CODE = 0,
+ /* Write Flash memory */
+ DT9812_W_FLASH_DATA = 0,
+ /* Read Flash memory misc config info */
+ DT9812_R_FLASH_DATA = 1,
+
+ /*
+ * Register read/write commands for processor
+ */
+
+ /* Read a single byte of USB memory */
+ DT9812_R_SINGLE_BYTE_REG = 2,
+ /* Write a single byte of USB memory */
+ DT9812_W_SINGLE_BYTE_REG = 3,
+ /* Multiple Reads of USB memory */
+ DT9812_R_MULTI_BYTE_REG = 4,
+ /* Multiple Writes of USB memory */
+ DT9812_W_MULTI_BYTE_REG = 5,
+ /* Read, (AND) with mask, OR value, then write (single) */
+ DT9812_RMW_SINGLE_BYTE_REG = 6,
+ /* Read, (AND) with mask, OR value, then write (multiple) */
+ DT9812_RMW_MULTI_BYTE_REG = 7,
+
+ /*
+ * Register read/write commands for SMBus
+ */
+
+ /* Read a single byte of SMBus */
+ DT9812_R_SINGLE_BYTE_SMBUS = 8,
+ /* Write a single byte of SMBus */
+ DT9812_W_SINGLE_BYTE_SMBUS = 9,
+ /* Multiple Reads of SMBus */
+ DT9812_R_MULTI_BYTE_SMBUS = 10,
+ /* Multiple Writes of SMBus */
+ DT9812_W_MULTI_BYTE_SMBUS = 11,
+
+ /*
+ * Register read/write commands for a device
+ */
+
+ /* Read a single byte of a device */
+ DT9812_R_SINGLE_BYTE_DEV = 12,
+ /* Write a single byte of a device */
+ DT9812_W_SINGLE_BYTE_DEV = 13,
+ /* Multiple Reads of a device */
+ DT9812_R_MULTI_BYTE_DEV = 14,
+ /* Multiple Writes of a device */
+ DT9812_W_MULTI_BYTE_DEV = 15,
+
+ /* Not sure if we'll need this */
+ DT9812_W_DAC_THRESHOLD = 16,
+
+ /* Set interrupt on change mask */
+ DT9812_W_INT_ON_CHANGE_MASK = 17,
+
+ /* Write (or Clear) the CGL for the ADC */
+ DT9812_W_CGL = 18,
+ /* Multiple Reads of USB memory */
+ DT9812_R_MULTI_BYTE_USBMEM = 19,
+ /* Multiple Writes to USB memory */
+ DT9812_W_MULTI_BYTE_USBMEM = 20,
+
+ /* Issue a start command to a given subsystem */
+ DT9812_START_SUBSYSTEM = 21,
+ /* Issue a stop command to a given subsystem */
+ DT9812_STOP_SUBSYSTEM = 22,
+
+ /* calibrate the board using CAL_POT_CMD */
+ DT9812_CALIBRATE_POT = 23,
+ /* set the DAC FIFO size */
+ DT9812_W_DAC_FIFO_SIZE = 24,
+ /* Write or Clear the CGL for the DAC */
+ DT9812_W_CGL_DAC = 25,
+ /* Read a single value from a subsystem */
+ DT9812_R_SINGLE_VALUE_CMD = 26,
+ /* Write a single value to a subsystem */
+ DT9812_W_SINGLE_VALUE_CMD = 27,
+ /* Valid DT9812_USB_FIRMWARE_CMD_CODE's will be less than this number */
+ DT9812_MAX_USB_FIRMWARE_CMD_CODE,
+};
+
+struct dt9812_flash_data {
+ __le16 numbytes;
+ __le16 address;
+};
+
+#define DT9812_MAX_NUM_MULTI_BYTE_RDS \
+ ((DT9812_MAX_WRITE_CMD_PIPE_SIZE - 4 - 1) / sizeof(u8))
+
+struct dt9812_read_multi {
+ u8 count;
+ u8 address[DT9812_MAX_NUM_MULTI_BYTE_RDS];
+};
+
+struct dt9812_write_byte {
+ u8 address;
+ u8 value;
+};
+
+#define DT9812_MAX_NUM_MULTI_BYTE_WRTS \
+ ((DT9812_MAX_WRITE_CMD_PIPE_SIZE - 4 - 1) / \
+ sizeof(struct dt9812_write_byte))
+
+struct dt9812_write_multi {
+ u8 count;
+ struct dt9812_write_byte write[DT9812_MAX_NUM_MULTI_BYTE_WRTS];
+};
+
+struct dt9812_rmw_byte {
+ u8 address;
+ u8 and_mask;
+ u8 or_value;
+};
+
+#define DT9812_MAX_NUM_MULTI_BYTE_RMWS \
+ ((DT9812_MAX_WRITE_CMD_PIPE_SIZE - 4 - 1) / \
+ sizeof(struct dt9812_rmw_byte))
+
+struct dt9812_rmw_multi {
+ u8 count;
+ struct dt9812_rmw_byte rmw[DT9812_MAX_NUM_MULTI_BYTE_RMWS];
+};
+
+struct dt9812_usb_cmd {
+ __le32 cmd;
+ union {
+ struct dt9812_flash_data flash_data_info;
+ struct dt9812_read_multi read_multi_info;
+ struct dt9812_write_multi write_multi_info;
+ struct dt9812_rmw_multi rmw_multi_info;
+ } u;
+};
+
+struct dt9812_private {
+ struct semaphore sem;
+ struct {
+ __u8 addr;
+ size_t size;
+ } cmd_wr, cmd_rd;
+ u16 device;
+};
+
+static int dt9812_read_info(struct comedi_device *dev,
+ int offset, void *buf, size_t buf_size)
+{
+ struct usb_device *usb = comedi_to_usb_dev(dev);
+ struct dt9812_private *devpriv = dev->private;
+ struct dt9812_usb_cmd cmd;
+ int count, ret;
+
+ cmd.cmd = cpu_to_le32(DT9812_R_FLASH_DATA);
+ cmd.u.flash_data_info.address =
+ cpu_to_le16(DT9812_DIAGS_BOARD_INFO_ADDR + offset);
+ cmd.u.flash_data_info.numbytes = cpu_to_le16(buf_size);
+
+ /* DT9812 only responds to 32 byte writes!! */
+ ret = usb_bulk_msg(usb, usb_sndbulkpipe(usb, devpriv->cmd_wr.addr),
+ &cmd, 32, &count, DT9812_USB_TIMEOUT);
+ if (ret)
+ return ret;
+
+ return usb_bulk_msg(usb, usb_rcvbulkpipe(usb, devpriv->cmd_rd.addr),
+ buf, buf_size, &count, DT9812_USB_TIMEOUT);
+}
+
+static int dt9812_read_multiple_registers(struct comedi_device *dev,
+ int reg_count, u8 *address,
+ u8 *value)
+{
+ struct usb_device *usb = comedi_to_usb_dev(dev);
+ struct dt9812_private *devpriv = dev->private;
+ struct dt9812_usb_cmd cmd;
+ int i, count, ret;
+
+ cmd.cmd = cpu_to_le32(DT9812_R_MULTI_BYTE_REG);
+ cmd.u.read_multi_info.count = reg_count;
+ for (i = 0; i < reg_count; i++)
+ cmd.u.read_multi_info.address[i] = address[i];
+
+ /* DT9812 only responds to 32 byte writes!! */
+ ret = usb_bulk_msg(usb, usb_sndbulkpipe(usb, devpriv->cmd_wr.addr),
+ &cmd, 32, &count, DT9812_USB_TIMEOUT);
+ if (ret)
+ return ret;
+
+ return usb_bulk_msg(usb, usb_rcvbulkpipe(usb, devpriv->cmd_rd.addr),
+ value, reg_count, &count, DT9812_USB_TIMEOUT);
+}
+
+static int dt9812_write_multiple_registers(struct comedi_device *dev,
+ int reg_count, u8 *address,
+ u8 *value)
+{
+ struct usb_device *usb = comedi_to_usb_dev(dev);
+ struct dt9812_private *devpriv = dev->private;
+ struct dt9812_usb_cmd cmd;
+ int i, count;
+
+ cmd.cmd = cpu_to_le32(DT9812_W_MULTI_BYTE_REG);
+ cmd.u.read_multi_info.count = reg_count;
+ for (i = 0; i < reg_count; i++) {
+ cmd.u.write_multi_info.write[i].address = address[i];
+ cmd.u.write_multi_info.write[i].value = value[i];
+ }
+
+ /* DT9812 only responds to 32 byte writes!! */
+ return usb_bulk_msg(usb, usb_sndbulkpipe(usb, devpriv->cmd_wr.addr),
+ &cmd, 32, &count, DT9812_USB_TIMEOUT);
+}
+
+static int dt9812_rmw_multiple_registers(struct comedi_device *dev,
+ int reg_count,
+ struct dt9812_rmw_byte *rmw)
+{
+ struct usb_device *usb = comedi_to_usb_dev(dev);
+ struct dt9812_private *devpriv = dev->private;
+ struct dt9812_usb_cmd cmd;
+ int i, count;
+
+ cmd.cmd = cpu_to_le32(DT9812_RMW_MULTI_BYTE_REG);
+ cmd.u.rmw_multi_info.count = reg_count;
+ for (i = 0; i < reg_count; i++)
+ cmd.u.rmw_multi_info.rmw[i] = rmw[i];
+
+ /* DT9812 only responds to 32 byte writes!! */
+ return usb_bulk_msg(usb, usb_sndbulkpipe(usb, devpriv->cmd_wr.addr),
+ &cmd, 32, &count, DT9812_USB_TIMEOUT);
+}
+
+static int dt9812_digital_in(struct comedi_device *dev, u8 *bits)
+{
+ struct dt9812_private *devpriv = dev->private;
+ u8 reg[2] = { F020_SFR_P3, F020_SFR_P1 };
+ u8 value[2];
+ int ret;
+
+ down(&devpriv->sem);
+ ret = dt9812_read_multiple_registers(dev, 2, reg, value);
+ if (ret == 0) {
+ /*
+ * bits 0-6 in F020_SFR_P3 are bits 0-6 in the digital
+ * input port bit 3 in F020_SFR_P1 is bit 7 in the
+ * digital input port
+ */
+ *bits = (value[0] & 0x7f) | ((value[1] & 0x08) << 4);
+ }
+ up(&devpriv->sem);
+
+ return ret;
+}
+
+static int dt9812_digital_out(struct comedi_device *dev, u8 bits)
+{
+ struct dt9812_private *devpriv = dev->private;
+ u8 reg[1] = { F020_SFR_P2 };
+ u8 value[1] = { bits };
+ int ret;
+
+ down(&devpriv->sem);
+ ret = dt9812_write_multiple_registers(dev, 1, reg, value);
+ up(&devpriv->sem);
+
+ return ret;
+}
+
+static void dt9812_configure_mux(struct comedi_device *dev,
+ struct dt9812_rmw_byte *rmw, int channel)
+{
+ struct dt9812_private *devpriv = dev->private;
+
+ if (devpriv->device == DT9812_DEVID_DT9812_10) {
+ /* In the DT9812/10V MUX is selected by P1.5-7 */
+ rmw->address = F020_SFR_P1;
+ rmw->and_mask = 0xe0;
+ rmw->or_value = channel << 5;
+ } else {
+ /* In the DT9812/2.5V, internal mux is selected by bits 0:2 */
+ rmw->address = F020_SFR_AMX0SL;
+ rmw->and_mask = 0xff;
+ rmw->or_value = channel & 0x07;
+ }
+}
+
+static void dt9812_configure_gain(struct comedi_device *dev,
+ struct dt9812_rmw_byte *rmw,
+ enum dt9812_gain gain)
+{
+ struct dt9812_private *devpriv = dev->private;
+
+ /* In the DT9812/10V, there is an external gain of 0.5 */
+ if (devpriv->device == DT9812_DEVID_DT9812_10)
+ gain <<= 1;
+
+ rmw->address = F020_SFR_ADC0CF;
+ rmw->and_mask = F020_MASK_ADC0CF_AMP0GN2 |
+ F020_MASK_ADC0CF_AMP0GN1 |
+ F020_MASK_ADC0CF_AMP0GN0;
+
+ switch (gain) {
+ /*
+ * 000 -> Gain = 1
+ * 001 -> Gain = 2
+ * 010 -> Gain = 4
+ * 011 -> Gain = 8
+ * 10x -> Gain = 16
+ * 11x -> Gain = 0.5
+ */
+ case DT9812_GAIN_0PT5:
+ rmw->or_value = F020_MASK_ADC0CF_AMP0GN2 |
+ F020_MASK_ADC0CF_AMP0GN1;
+ break;
+ default:
+ /* this should never happen, just use a gain of 1 */
+ case DT9812_GAIN_1:
+ rmw->or_value = 0x00;
+ break;
+ case DT9812_GAIN_2:
+ rmw->or_value = F020_MASK_ADC0CF_AMP0GN0;
+ break;
+ case DT9812_GAIN_4:
+ rmw->or_value = F020_MASK_ADC0CF_AMP0GN1;
+ break;
+ case DT9812_GAIN_8:
+ rmw->or_value = F020_MASK_ADC0CF_AMP0GN1 |
+ F020_MASK_ADC0CF_AMP0GN0;
+ break;
+ case DT9812_GAIN_16:
+ rmw->or_value = F020_MASK_ADC0CF_AMP0GN2;
+ break;
+ }
+}
+
+static int dt9812_analog_in(struct comedi_device *dev,
+ int channel, u16 *value, enum dt9812_gain gain)
+{
+ struct dt9812_private *devpriv = dev->private;
+ struct dt9812_rmw_byte rmw[3];
+ u8 reg[3] = {
+ F020_SFR_ADC0CN,
+ F020_SFR_ADC0H,
+ F020_SFR_ADC0L
+ };
+ u8 val[3];
+ int ret;
+
+ down(&devpriv->sem);
+
+ /* 1 select the gain */
+ dt9812_configure_gain(dev, &rmw[0], gain);
+
+ /* 2 set the MUX to select the channel */
+ dt9812_configure_mux(dev, &rmw[1], channel);
+
+ /* 3 start conversion */
+ rmw[2].address = F020_SFR_ADC0CN;
+ rmw[2].and_mask = 0xff;
+ rmw[2].or_value = F020_MASK_ADC0CN_AD0EN | F020_MASK_ADC0CN_AD0BUSY;
+
+ ret = dt9812_rmw_multiple_registers(dev, 3, rmw);
+ if (ret)
+ goto exit;
+
+ /* read the status and ADC */
+ ret = dt9812_read_multiple_registers(dev, 3, reg, val);
+ if (ret)
+ goto exit;
+
+ /*
+ * An ADC conversion takes 16 SAR clocks cycles, i.e. about 9us.
+ * Therefore, between the instant that AD0BUSY was set via
+ * dt9812_rmw_multiple_registers and the read of AD0BUSY via
+ * dt9812_read_multiple_registers, the conversion should be complete
+ * since these two operations require two USB transactions each taking
+ * at least a millisecond to complete. However, lets make sure that
+ * conversion is finished.
+ */
+ if ((val[0] & (F020_MASK_ADC0CN_AD0INT | F020_MASK_ADC0CN_AD0BUSY)) ==
+ F020_MASK_ADC0CN_AD0INT) {
+ switch (devpriv->device) {
+ case DT9812_DEVID_DT9812_10:
+ /*
+ * For DT9812-10V the personality module set the
+ * encoding to 2's complement. Hence, convert it before
+ * returning it
+ */
+ *value = ((val[1] << 8) | val[2]) + 0x800;
+ break;
+ case DT9812_DEVID_DT9812_2PT5:
+ *value = (val[1] << 8) | val[2];
+ break;
+ }
+ }
+
+exit:
+ up(&devpriv->sem);
+
+ return ret;
+}
+
+static int dt9812_analog_out(struct comedi_device *dev, int channel, u16 value)
+{
+ struct dt9812_private *devpriv = dev->private;
+ struct dt9812_rmw_byte rmw[3];
+ int ret;
+
+ down(&devpriv->sem);
+
+ switch (channel) {
+ case 0:
+ /* 1. Set DAC mode */
+ rmw[0].address = F020_SFR_DAC0CN;
+ rmw[0].and_mask = 0xff;
+ rmw[0].or_value = F020_MASK_DACxCN_DACxEN;
+
+ /* 2 load low byte of DAC value first */
+ rmw[1].address = F020_SFR_DAC0L;
+ rmw[1].and_mask = 0xff;
+ rmw[1].or_value = value & 0xff;
+
+ /* 3 load high byte of DAC value next to latch the
+ 12-bit value */
+ rmw[2].address = F020_SFR_DAC0H;
+ rmw[2].and_mask = 0xff;
+ rmw[2].or_value = (value >> 8) & 0xf;
+ break;
+
+ case 1:
+ /* 1. Set DAC mode */
+ rmw[0].address = F020_SFR_DAC1CN;
+ rmw[0].and_mask = 0xff;
+ rmw[0].or_value = F020_MASK_DACxCN_DACxEN;
+
+ /* 2 load low byte of DAC value first */
+ rmw[1].address = F020_SFR_DAC1L;
+ rmw[1].and_mask = 0xff;
+ rmw[1].or_value = value & 0xff;
+
+ /* 3 load high byte of DAC value next to latch the
+ 12-bit value */
+ rmw[2].address = F020_SFR_DAC1H;
+ rmw[2].and_mask = 0xff;
+ rmw[2].or_value = (value >> 8) & 0xf;
+ break;
+ }
+ ret = dt9812_rmw_multiple_registers(dev, 3, rmw);
+
+ up(&devpriv->sem);
+
+ return ret;
+}
+
+static int dt9812_di_insn_bits(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ u8 bits = 0;
+ int ret;
+
+ ret = dt9812_digital_in(dev, &bits);
+ if (ret)
+ return ret;
+
+ data[1] = bits;
+
+ return insn->n;
+}
+
+static int dt9812_do_insn_bits(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ if (comedi_dio_update_state(s, data))
+ dt9812_digital_out(dev, s->state);
+
+ data[1] = s->state;
+
+ return insn->n;
+}
+
+static int dt9812_ai_insn_read(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ unsigned int chan = CR_CHAN(insn->chanspec);
+ u16 val = 0;
+ int ret;
+ int i;
+
+ for (i = 0; i < insn->n; i++) {
+ ret = dt9812_analog_in(dev, chan, &val, DT9812_GAIN_1);
+ if (ret)
+ return ret;
+ data[i] = val;
+ }
+
+ return insn->n;
+}
+
+static int dt9812_ao_insn_read(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ struct dt9812_private *devpriv = dev->private;
+ int ret;
+
+ down(&devpriv->sem);
+ ret = comedi_readback_insn_read(dev, s, insn, data);
+ up(&devpriv->sem);
+
+ return ret;
+}
+
+static int dt9812_ao_insn_write(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ unsigned int chan = CR_CHAN(insn->chanspec);
+ int i;
+
+ for (i = 0; i < insn->n; i++) {
+ unsigned int val = data[i];
+ int ret;
+
+ ret = dt9812_analog_out(dev, chan, val);
+ if (ret)
+ return ret;
+
+ s->readback[chan] = val;
+ }
+
+ return insn->n;
+}
+
+static int dt9812_find_endpoints(struct comedi_device *dev)
+{
+ struct usb_interface *intf = comedi_to_usb_interface(dev);
+ struct usb_host_interface *host = intf->cur_altsetting;
+ struct dt9812_private *devpriv = dev->private;
+ struct usb_endpoint_descriptor *ep;
+ int i;
+
+ if (host->desc.bNumEndpoints != 5) {
+ dev_err(dev->class_dev, "Wrong number of endpoints\n");
+ return -ENODEV;
+ }
+
+ for (i = 0; i < host->desc.bNumEndpoints; ++i) {
+ int dir = -1;
+
+ ep = &host->endpoint[i].desc;
+ switch (i) {
+ case 0:
+ /* unused message pipe */
+ dir = USB_DIR_IN;
+ break;
+ case 1:
+ dir = USB_DIR_OUT;
+ devpriv->cmd_wr.addr = ep->bEndpointAddress;
+ devpriv->cmd_wr.size = le16_to_cpu(ep->wMaxPacketSize);
+ break;
+ case 2:
+ dir = USB_DIR_IN;
+ devpriv->cmd_rd.addr = ep->bEndpointAddress;
+ devpriv->cmd_rd.size = le16_to_cpu(ep->wMaxPacketSize);
+ break;
+ case 3:
+ /* unused write stream */
+ dir = USB_DIR_OUT;
+ break;
+ case 4:
+ /* unused read stream */
+ dir = USB_DIR_IN;
+ break;
+ }
+ if ((ep->bEndpointAddress & USB_DIR_IN) != dir) {
+ dev_err(dev->class_dev,
+ "Endpoint has wrong direction\n");
+ return -ENODEV;
+ }
+ }
+ return 0;
+}
+
+static int dt9812_reset_device(struct comedi_device *dev)
+{
+ struct usb_device *usb = comedi_to_usb_dev(dev);
+ struct dt9812_private *devpriv = dev->private;
+ u32 serial;
+ u16 vendor;
+ u16 product;
+ u8 tmp8;
+ __le16 tmp16;
+ __le32 tmp32;
+ int ret;
+ int i;
+
+ ret = dt9812_read_info(dev, 0, &tmp8, sizeof(tmp8));
+ if (ret) {
+ /*
+ * Seems like a configuration reset is necessary if driver is
+ * reloaded while device is attached
+ */
+ usb_reset_configuration(usb);
+ for (i = 0; i < 10; i++) {
+ ret = dt9812_read_info(dev, 1, &tmp8, sizeof(tmp8));
+ if (ret == 0)
+ break;
+ }
+ if (ret) {
+ dev_err(dev->class_dev,
+ "unable to reset configuration\n");
+ return ret;
+ }
+ }
+
+ ret = dt9812_read_info(dev, 1, &tmp16, sizeof(tmp16));
+ if (ret) {
+ dev_err(dev->class_dev, "failed to read vendor id\n");
+ return ret;
+ }
+ vendor = le16_to_cpu(tmp16);
+
+ ret = dt9812_read_info(dev, 3, &tmp16, sizeof(tmp16));
+ if (ret) {
+ dev_err(dev->class_dev, "failed to read product id\n");
+ return ret;
+ }
+ product = le16_to_cpu(tmp16);
+
+ ret = dt9812_read_info(dev, 5, &tmp16, sizeof(tmp16));
+ if (ret) {
+ dev_err(dev->class_dev, "failed to read device id\n");
+ return ret;
+ }
+ devpriv->device = le16_to_cpu(tmp16);
+
+ ret = dt9812_read_info(dev, 7, &tmp32, sizeof(tmp32));
+ if (ret) {
+ dev_err(dev->class_dev, "failed to read serial number\n");
+ return ret;
+ }
+ serial = le32_to_cpu(tmp32);
+
+ /* let the user know what node this device is now attached to */
+ dev_info(dev->class_dev, "USB DT9812 (%4.4x.%4.4x.%4.4x) #0x%8.8x\n",
+ vendor, product, devpriv->device, serial);
+
+ if (devpriv->device != DT9812_DEVID_DT9812_10 &&
+ devpriv->device != DT9812_DEVID_DT9812_2PT5) {
+ dev_err(dev->class_dev, "Unsupported device!\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int dt9812_auto_attach(struct comedi_device *dev,
+ unsigned long context)
+{
+ struct usb_interface *intf = comedi_to_usb_interface(dev);
+ struct dt9812_private *devpriv;
+ struct comedi_subdevice *s;
+ bool is_unipolar;
+ int ret;
+ int i;
+
+ devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
+ if (!devpriv)
+ return -ENOMEM;
+
+ sema_init(&devpriv->sem, 1);
+ usb_set_intfdata(intf, devpriv);
+
+ ret = dt9812_find_endpoints(dev);
+ if (ret)
+ return ret;
+
+ ret = dt9812_reset_device(dev);
+ if (ret)
+ return ret;
+
+ is_unipolar = (devpriv->device == DT9812_DEVID_DT9812_2PT5);
+
+ ret = comedi_alloc_subdevices(dev, 4);
+ if (ret)
+ return ret;
+
+ /* Digital Input subdevice */
+ s = &dev->subdevices[0];
+ s->type = COMEDI_SUBD_DI;
+ s->subdev_flags = SDF_READABLE;
+ s->n_chan = 8;
+ s->maxdata = 1;
+ s->range_table = &range_digital;
+ s->insn_bits = dt9812_di_insn_bits;
+
+ /* Digital Output subdevice */
+ s = &dev->subdevices[1];
+ s->type = COMEDI_SUBD_DO;
+ s->subdev_flags = SDF_WRITABLE;
+ s->n_chan = 8;
+ s->maxdata = 1;
+ s->range_table = &range_digital;
+ s->insn_bits = dt9812_do_insn_bits;
+
+ /* Analog Input subdevice */
+ s = &dev->subdevices[2];
+ s->type = COMEDI_SUBD_AI;
+ s->subdev_flags = SDF_READABLE | SDF_GROUND;
+ s->n_chan = 8;
+ s->maxdata = 0x0fff;
+ s->range_table = is_unipolar ? &range_unipolar2_5 : &range_bipolar10;
+ s->insn_read = dt9812_ai_insn_read;
+
+ /* Analog Output subdevice */
+ s = &dev->subdevices[3];
+ s->type = COMEDI_SUBD_AO;
+ s->subdev_flags = SDF_WRITABLE;
+ s->n_chan = 2;
+ s->maxdata = 0x0fff;
+ s->range_table = is_unipolar ? &range_unipolar2_5 : &range_bipolar10;
+ s->insn_write = dt9812_ao_insn_write;
+ s->insn_read = dt9812_ao_insn_read;
+
+ ret = comedi_alloc_subdev_readback(s);
+ if (ret)
+ return ret;
+
+ for (i = 0; i < s->n_chan; i++)
+ s->readback[i] = is_unipolar ? 0x0000 : 0x0800;
+
+ return 0;
+}
+
+static void dt9812_detach(struct comedi_device *dev)
+{
+ struct usb_interface *intf = comedi_to_usb_interface(dev);
+ struct dt9812_private *devpriv = dev->private;
+
+ if (!devpriv)
+ return;
+
+ down(&devpriv->sem);
+
+ usb_set_intfdata(intf, NULL);
+
+ up(&devpriv->sem);
+}
+
+static struct comedi_driver dt9812_driver = {
+ .driver_name = "dt9812",
+ .module = THIS_MODULE,
+ .auto_attach = dt9812_auto_attach,
+ .detach = dt9812_detach,
+};
+
+static int dt9812_usb_probe(struct usb_interface *intf,
+ const struct usb_device_id *id)
+{
+ return comedi_usb_auto_config(intf, &dt9812_driver, id->driver_info);
+}
+
+static const struct usb_device_id dt9812_usb_table[] = {
+ { USB_DEVICE(0x0867, 0x9812) },
+ { }
+};
+MODULE_DEVICE_TABLE(usb, dt9812_usb_table);
+
+static struct usb_driver dt9812_usb_driver = {
+ .name = "dt9812",
+ .id_table = dt9812_usb_table,
+ .probe = dt9812_usb_probe,
+ .disconnect = comedi_usb_auto_unconfig,
+};
+module_comedi_usb_driver(dt9812_driver, dt9812_usb_driver);
+
+MODULE_AUTHOR("Anders Blomdell <anders.blomdell@control.lth.se>");
+MODULE_DESCRIPTION("Comedi DT9812 driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/comedi/drivers/dyna_pci10xx.c b/drivers/staging/comedi/drivers/dyna_pci10xx.c
new file mode 100644
index 000000000..c9eb26fab
--- /dev/null
+++ b/drivers/staging/comedi/drivers/dyna_pci10xx.c
@@ -0,0 +1,282 @@
+/*
+ * comedi/drivers/dyna_pci10xx.c
+ * Copyright (C) 2011 Prashant Shah, pshah.mumbai@gmail.com
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+/*
+ * Driver: dyna_pci10xx
+ * Description: Dynalog India PCI DAQ Cards, http://www.dynalogindia.com/
+ * Devices: [Dynalog] PCI-1050 (dyna_pci1050)
+ * Author: Prashant Shah <pshah.mumbai@gmail.com>
+ * Status: Stable
+ *
+ * Developed at Automation Labs, Chemical Dept., IIT Bombay, India.
+ * Prof. Kannan Moudgalya <kannan@iitb.ac.in>
+ * http://www.iitb.ac.in
+ *
+ * Notes :
+ * - Dynalog India Pvt. Ltd. does not have a registered PCI Vendor ID and
+ * they are using the PLX Technlogies Vendor ID since that is the PCI Chip
+ * used in the card.
+ * - Dynalog India Pvt. Ltd. has provided the internal register specification
+ * for their cards in their manuals.
+ */
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/mutex.h>
+
+#include "../comedi_pci.h"
+
+#define READ_TIMEOUT 50
+
+static const struct comedi_lrange range_pci1050_ai = {
+ 3, {
+ BIP_RANGE(10),
+ BIP_RANGE(5),
+ UNI_RANGE(10)
+ }
+};
+
+static const char range_codes_pci1050_ai[] = { 0x00, 0x10, 0x30 };
+
+struct dyna_pci10xx_private {
+ struct mutex mutex;
+ unsigned long BADR3;
+};
+
+static int dyna_pci10xx_ai_eoc(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned long context)
+{
+ unsigned int status;
+
+ status = inw_p(dev->iobase);
+ if (status & (1 << 15))
+ return 0;
+ return -EBUSY;
+}
+
+static int dyna_pci10xx_insn_read_ai(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ struct dyna_pci10xx_private *devpriv = dev->private;
+ int n;
+ u16 d = 0;
+ int ret = 0;
+ unsigned int chan, range;
+
+ /* get the channel number and range */
+ chan = CR_CHAN(insn->chanspec);
+ range = range_codes_pci1050_ai[CR_RANGE((insn->chanspec))];
+
+ mutex_lock(&devpriv->mutex);
+ /* convert n samples */
+ for (n = 0; n < insn->n; n++) {
+ /* trigger conversion */
+ smp_mb();
+ outw_p(0x0000 + range + chan, dev->iobase + 2);
+ udelay(10);
+
+ ret = comedi_timeout(dev, s, insn, dyna_pci10xx_ai_eoc, 0);
+ if (ret)
+ break;
+
+ /* read data */
+ d = inw_p(dev->iobase);
+ /* mask the first 4 bits - EOC bits */
+ d &= 0x0FFF;
+ data[n] = d;
+ }
+ mutex_unlock(&devpriv->mutex);
+
+ /* return the number of samples read/written */
+ return ret ? ret : n;
+}
+
+/* analog output callback */
+static int dyna_pci10xx_insn_write_ao(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ struct dyna_pci10xx_private *devpriv = dev->private;
+ int n;
+ unsigned int chan, range;
+
+ chan = CR_CHAN(insn->chanspec);
+ range = range_codes_pci1050_ai[CR_RANGE((insn->chanspec))];
+
+ mutex_lock(&devpriv->mutex);
+ for (n = 0; n < insn->n; n++) {
+ smp_mb();
+ /* trigger conversion and write data */
+ outw_p(data[n], dev->iobase);
+ udelay(10);
+ }
+ mutex_unlock(&devpriv->mutex);
+ return n;
+}
+
+/* digital input bit interface */
+static int dyna_pci10xx_di_insn_bits(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ struct dyna_pci10xx_private *devpriv = dev->private;
+ u16 d = 0;
+
+ mutex_lock(&devpriv->mutex);
+ smp_mb();
+ d = inw_p(devpriv->BADR3);
+ udelay(10);
+
+ /* on return the data[0] contains output and data[1] contains input */
+ data[1] = d;
+ data[0] = s->state;
+ mutex_unlock(&devpriv->mutex);
+ return insn->n;
+}
+
+static int dyna_pci10xx_do_insn_bits(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ struct dyna_pci10xx_private *devpriv = dev->private;
+
+ mutex_lock(&devpriv->mutex);
+ if (comedi_dio_update_state(s, data)) {
+ smp_mb();
+ outw_p(s->state, devpriv->BADR3);
+ udelay(10);
+ }
+
+ data[1] = s->state;
+ mutex_unlock(&devpriv->mutex);
+
+ return insn->n;
+}
+
+static int dyna_pci10xx_auto_attach(struct comedi_device *dev,
+ unsigned long context_unused)
+{
+ struct pci_dev *pcidev = comedi_to_pci_dev(dev);
+ struct dyna_pci10xx_private *devpriv;
+ struct comedi_subdevice *s;
+ int ret;
+
+ devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
+ if (!devpriv)
+ return -ENOMEM;
+
+ ret = comedi_pci_enable(dev);
+ if (ret)
+ return ret;
+ dev->iobase = pci_resource_start(pcidev, 2);
+ devpriv->BADR3 = pci_resource_start(pcidev, 3);
+
+ mutex_init(&devpriv->mutex);
+
+ ret = comedi_alloc_subdevices(dev, 4);
+ if (ret)
+ return ret;
+
+ /* analog input */
+ s = &dev->subdevices[0];
+ s->type = COMEDI_SUBD_AI;
+ s->subdev_flags = SDF_READABLE | SDF_GROUND | SDF_DIFF;
+ s->n_chan = 16;
+ s->maxdata = 0x0FFF;
+ s->range_table = &range_pci1050_ai;
+ s->len_chanlist = 16;
+ s->insn_read = dyna_pci10xx_insn_read_ai;
+
+ /* analog output */
+ s = &dev->subdevices[1];
+ s->type = COMEDI_SUBD_AO;
+ s->subdev_flags = SDF_WRITABLE;
+ s->n_chan = 16;
+ s->maxdata = 0x0FFF;
+ s->range_table = &range_unipolar10;
+ s->len_chanlist = 16;
+ s->insn_write = dyna_pci10xx_insn_write_ao;
+
+ /* digital input */
+ s = &dev->subdevices[2];
+ s->type = COMEDI_SUBD_DI;
+ s->subdev_flags = SDF_READABLE;
+ s->n_chan = 16;
+ s->maxdata = 1;
+ s->range_table = &range_digital;
+ s->len_chanlist = 16;
+ s->insn_bits = dyna_pci10xx_di_insn_bits;
+
+ /* digital output */
+ s = &dev->subdevices[3];
+ s->type = COMEDI_SUBD_DO;
+ s->subdev_flags = SDF_WRITABLE;
+ s->n_chan = 16;
+ s->maxdata = 1;
+ s->range_table = &range_digital;
+ s->len_chanlist = 16;
+ s->state = 0;
+ s->insn_bits = dyna_pci10xx_do_insn_bits;
+
+ return 0;
+}
+
+static void dyna_pci10xx_detach(struct comedi_device *dev)
+{
+ struct dyna_pci10xx_private *devpriv = dev->private;
+
+ comedi_pci_detach(dev);
+ if (devpriv)
+ mutex_destroy(&devpriv->mutex);
+}
+
+static struct comedi_driver dyna_pci10xx_driver = {
+ .driver_name = "dyna_pci10xx",
+ .module = THIS_MODULE,
+ .auto_attach = dyna_pci10xx_auto_attach,
+ .detach = dyna_pci10xx_detach,
+};
+
+static int dyna_pci10xx_pci_probe(struct pci_dev *dev,
+ const struct pci_device_id *id)
+{
+ return comedi_pci_auto_config(dev, &dyna_pci10xx_driver,
+ id->driver_data);
+}
+
+static const struct pci_device_id dyna_pci10xx_pci_table[] = {
+ { PCI_DEVICE(PCI_VENDOR_ID_PLX, 0x1050) },
+ { 0 }
+};
+MODULE_DEVICE_TABLE(pci, dyna_pci10xx_pci_table);
+
+static struct pci_driver dyna_pci10xx_pci_driver = {
+ .name = "dyna_pci10xx",
+ .id_table = dyna_pci10xx_pci_table,
+ .probe = dyna_pci10xx_pci_probe,
+ .remove = comedi_pci_auto_unconfig,
+};
+module_comedi_pci_driver(dyna_pci10xx_driver, dyna_pci10xx_pci_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Prashant Shah <pshah.mumbai@gmail.com>");
+MODULE_DESCRIPTION("Comedi based drivers for Dynalog PCI DAQ cards");
diff --git a/drivers/staging/comedi/drivers/fl512.c b/drivers/staging/comedi/drivers/fl512.c
new file mode 100644
index 000000000..e1f493241
--- /dev/null
+++ b/drivers/staging/comedi/drivers/fl512.c
@@ -0,0 +1,156 @@
+/*
+ * fl512.c
+ * Anders Gnistrup <ex18@kalman.iau.dtu.dk>
+ *
+ * COMEDI - Linux Control and Measurement Device Interface
+ * Copyright (C) 2000 David A. Schleef <ds@schleef.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+/*
+ * Driver: fl512
+ * Description: unknown
+ * Author: Anders Gnistrup <ex18@kalman.iau.dtu.dk>
+ * Devices: [unknown] FL512 (fl512)
+ * Status: unknown
+ *
+ * Digital I/O is not supported.
+ *
+ * Configuration options:
+ * [0] - I/O port base address
+ */
+
+#include <linux/module.h>
+#include "../comedidev.h"
+
+#include <linux/delay.h>
+
+/*
+ * Register I/O map
+ */
+#define FL512_AI_LSB_REG 0x02
+#define FL512_AI_MSB_REG 0x03
+#define FL512_AI_MUX_REG 0x02
+#define FL512_AI_START_CONV_REG 0x03
+#define FL512_AO_DATA_REG(x) (0x04 + ((x) * 2))
+#define FL512_AO_TRIG_REG(x) (0x04 + ((x) * 2))
+
+static const struct comedi_lrange range_fl512 = {
+ 4, {
+ BIP_RANGE(0.5),
+ BIP_RANGE(1),
+ BIP_RANGE(5),
+ BIP_RANGE(10),
+ UNI_RANGE(1),
+ UNI_RANGE(5),
+ UNI_RANGE(10)
+ }
+};
+
+static int fl512_ai_insn_read(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ unsigned int chan = CR_CHAN(insn->chanspec);
+ unsigned int val;
+ int i;
+
+ outb(chan, dev->iobase + FL512_AI_MUX_REG);
+
+ for (i = 0; i < insn->n; i++) {
+ outb(0, dev->iobase + FL512_AI_START_CONV_REG);
+
+ /* XXX should test "done" flag instead of delay */
+ udelay(30);
+
+ val = inb(dev->iobase + FL512_AI_LSB_REG);
+ val |= (inb(dev->iobase + FL512_AI_MSB_REG) << 8);
+ val &= s->maxdata;
+
+ data[i] = val;
+ }
+
+ return insn->n;
+}
+
+static int fl512_ao_insn_write(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ unsigned int chan = CR_CHAN(insn->chanspec);
+ unsigned int val = s->readback[chan];
+ int i;
+
+ for (i = 0; i < insn->n; i++) {
+ val = data[i];
+
+ /* write LSB, MSB then trigger conversion */
+ outb(val & 0x0ff, dev->iobase + FL512_AO_DATA_REG(chan));
+ outb((val >> 8) & 0xf, dev->iobase + FL512_AO_DATA_REG(chan));
+ inb(dev->iobase + FL512_AO_TRIG_REG(chan));
+ }
+ s->readback[chan] = val;
+
+ return insn->n;
+}
+
+static int fl512_attach(struct comedi_device *dev, struct comedi_devconfig *it)
+{
+ struct comedi_subdevice *s;
+ int ret;
+
+ ret = comedi_request_region(dev, it->options[0], 0x10);
+ if (ret)
+ return ret;
+
+ ret = comedi_alloc_subdevices(dev, 2);
+ if (ret)
+ return ret;
+
+ /* Analog Input subdevice */
+ s = &dev->subdevices[0];
+ s->type = COMEDI_SUBD_AI;
+ s->subdev_flags = SDF_READABLE | SDF_GROUND;
+ s->n_chan = 16;
+ s->maxdata = 0x0fff;
+ s->range_table = &range_fl512;
+ s->insn_read = fl512_ai_insn_read;
+
+ /* Analog Output subdevice */
+ s = &dev->subdevices[1];
+ s->type = COMEDI_SUBD_AO;
+ s->subdev_flags = SDF_WRITABLE;
+ s->n_chan = 2;
+ s->maxdata = 0x0fff;
+ s->range_table = &range_fl512;
+ s->insn_write = fl512_ao_insn_write;
+
+ ret = comedi_alloc_subdev_readback(s);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static struct comedi_driver fl512_driver = {
+ .driver_name = "fl512",
+ .module = THIS_MODULE,
+ .attach = fl512_attach,
+ .detach = comedi_legacy_detach,
+};
+module_comedi_driver(fl512_driver);
+
+MODULE_AUTHOR("Comedi http://www.comedi.org");
+MODULE_DESCRIPTION("Comedi low-level driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/comedi/drivers/gsc_hpdi.c b/drivers/staging/comedi/drivers/gsc_hpdi.c
new file mode 100644
index 000000000..3cb6409c4
--- /dev/null
+++ b/drivers/staging/comedi/drivers/gsc_hpdi.c
@@ -0,0 +1,752 @@
+/*
+ * gsc_hpdi.c
+ * Comedi driver the General Standards Corporation
+ * High Speed Parallel Digital Interface rs485 boards.
+ *
+ * Author: Frank Mori Hess <fmhess@users.sourceforge.net>
+ * Copyright (C) 2003 Coherent Imaging Systems
+ *
+ * COMEDI - Linux Control and Measurement Device Interface
+ * Copyright (C) 1997-8 David A. Schleef <ds@schleef.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+/*
+ * Driver: gsc_hpdi
+ * Description: General Standards Corporation High
+ * Speed Parallel Digital Interface rs485 boards
+ * Author: Frank Mori Hess <fmhess@users.sourceforge.net>
+ * Status: only receive mode works, transmit not supported
+ * Updated: Thu, 01 Nov 2012 16:17:38 +0000
+ * Devices: [General Standards Corporation] PCI-HPDI32 (gsc_hpdi),
+ * PMC-HPDI32
+ *
+ * Configuration options:
+ * None.
+ *
+ * Manual configuration of supported devices is not supported; they are
+ * configured automatically.
+ *
+ * There are some additional hpdi models available from GSC for which
+ * support could be added to this driver.
+ */
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+
+#include "../comedi_pci.h"
+
+#include "plx9080.h"
+
+/*
+ * PCI BAR2 Register map (dev->mmio)
+ */
+#define FIRMWARE_REV_REG 0x00
+#define FEATURES_REG_PRESENT_BIT (1 << 15)
+#define BOARD_CONTROL_REG 0x04
+#define BOARD_RESET_BIT (1 << 0)
+#define TX_FIFO_RESET_BIT (1 << 1)
+#define RX_FIFO_RESET_BIT (1 << 2)
+#define TX_ENABLE_BIT (1 << 4)
+#define RX_ENABLE_BIT (1 << 5)
+#define DEMAND_DMA_DIRECTION_TX_BIT (1 << 6) /* ch 0 only */
+#define LINE_VALID_ON_STATUS_VALID_BIT (1 << 7)
+#define START_TX_BIT (1 << 8)
+#define CABLE_THROTTLE_ENABLE_BIT (1 << 9)
+#define TEST_MODE_ENABLE_BIT (1 << 31)
+#define BOARD_STATUS_REG 0x08
+#define COMMAND_LINE_STATUS_MASK (0x7f << 0)
+#define TX_IN_PROGRESS_BIT (1 << 7)
+#define TX_NOT_EMPTY_BIT (1 << 8)
+#define TX_NOT_ALMOST_EMPTY_BIT (1 << 9)
+#define TX_NOT_ALMOST_FULL_BIT (1 << 10)
+#define TX_NOT_FULL_BIT (1 << 11)
+#define RX_NOT_EMPTY_BIT (1 << 12)
+#define RX_NOT_ALMOST_EMPTY_BIT (1 << 13)
+#define RX_NOT_ALMOST_FULL_BIT (1 << 14)
+#define RX_NOT_FULL_BIT (1 << 15)
+#define BOARD_JUMPER0_INSTALLED_BIT (1 << 16)
+#define BOARD_JUMPER1_INSTALLED_BIT (1 << 17)
+#define TX_OVERRUN_BIT (1 << 21)
+#define RX_UNDERRUN_BIT (1 << 22)
+#define RX_OVERRUN_BIT (1 << 23)
+#define TX_PROG_ALMOST_REG 0x0c
+#define RX_PROG_ALMOST_REG 0x10
+#define ALMOST_EMPTY_BITS(x) (((x) & 0xffff) << 0)
+#define ALMOST_FULL_BITS(x) (((x) & 0xff) << 16)
+#define FEATURES_REG 0x14
+#define FIFO_SIZE_PRESENT_BIT (1 << 0)
+#define FIFO_WORDS_PRESENT_BIT (1 << 1)
+#define LEVEL_EDGE_INTERRUPTS_PRESENT_BIT (1 << 2)
+#define GPIO_SUPPORTED_BIT (1 << 3)
+#define PLX_DMA_CH1_SUPPORTED_BIT (1 << 4)
+#define OVERRUN_UNDERRUN_SUPPORTED_BIT (1 << 5)
+#define FIFO_REG 0x18
+#define TX_STATUS_COUNT_REG 0x1c
+#define TX_LINE_VALID_COUNT_REG 0x20,
+#define TX_LINE_INVALID_COUNT_REG 0x24
+#define RX_STATUS_COUNT_REG 0x28
+#define RX_LINE_COUNT_REG 0x2c
+#define INTERRUPT_CONTROL_REG 0x30
+#define FRAME_VALID_START_INTR (1 << 0)
+#define FRAME_VALID_END_INTR (1 << 1)
+#define TX_FIFO_EMPTY_INTR (1 << 8)
+#define TX_FIFO_ALMOST_EMPTY_INTR (1 << 9)
+#define TX_FIFO_ALMOST_FULL_INTR (1 << 10)
+#define TX_FIFO_FULL_INTR (1 << 11)
+#define RX_EMPTY_INTR (1 << 12)
+#define RX_ALMOST_EMPTY_INTR (1 << 13)
+#define RX_ALMOST_FULL_INTR (1 << 14)
+#define RX_FULL_INTR (1 << 15)
+#define INTERRUPT_STATUS_REG 0x34
+#define TX_CLOCK_DIVIDER_REG 0x38
+#define TX_FIFO_SIZE_REG 0x40
+#define RX_FIFO_SIZE_REG 0x44
+#define FIFO_SIZE_MASK (0xfffff << 0)
+#define TX_FIFO_WORDS_REG 0x48
+#define RX_FIFO_WORDS_REG 0x4c
+#define INTERRUPT_EDGE_LEVEL_REG 0x50
+#define INTERRUPT_POLARITY_REG 0x54
+
+#define TIMER_BASE 50 /* 20MHz master clock */
+#define DMA_BUFFER_SIZE 0x10000
+#define NUM_DMA_BUFFERS 4
+#define NUM_DMA_DESCRIPTORS 256
+
+struct hpdi_board {
+ const char *name;
+ int device_id;
+ int subdevice_id;
+};
+
+static const struct hpdi_board hpdi_boards[] = {
+ {
+ .name = "pci-hpdi32",
+ .device_id = PCI_DEVICE_ID_PLX_9080,
+ .subdevice_id = 0x2400,
+ },
+#if 0
+ {
+ .name = "pxi-hpdi32",
+ .device_id = 0x9656,
+ .subdevice_id = 0x2705,
+ },
+#endif
+};
+
+struct hpdi_private {
+ void __iomem *plx9080_mmio;
+ uint32_t *dio_buffer[NUM_DMA_BUFFERS]; /* dma buffers */
+ /* physical addresses of dma buffers */
+ dma_addr_t dio_buffer_phys_addr[NUM_DMA_BUFFERS];
+ /* array of dma descriptors read by plx9080, allocated to get proper
+ * alignment */
+ struct plx_dma_desc *dma_desc;
+ /* physical address of dma descriptor array */
+ dma_addr_t dma_desc_phys_addr;
+ unsigned int num_dma_descriptors;
+ /* pointer to start of buffers indexed by descriptor */
+ uint32_t *desc_dio_buffer[NUM_DMA_DESCRIPTORS];
+ /* index of the dma descriptor that is currently being used */
+ unsigned int dma_desc_index;
+ unsigned int tx_fifo_size;
+ unsigned int rx_fifo_size;
+ unsigned long dio_count;
+ /* number of bytes at which to generate COMEDI_CB_BLOCK events */
+ unsigned int block_size;
+};
+
+static void gsc_hpdi_drain_dma(struct comedi_device *dev, unsigned int channel)
+{
+ struct hpdi_private *devpriv = dev->private;
+ struct comedi_subdevice *s = dev->read_subdev;
+ struct comedi_cmd *cmd = &s->async->cmd;
+ unsigned int idx;
+ unsigned int start;
+ unsigned int desc;
+ unsigned int size;
+ unsigned int next;
+
+ if (channel)
+ next = readl(devpriv->plx9080_mmio + PLX_DMA1_PCI_ADDRESS_REG);
+ else
+ next = readl(devpriv->plx9080_mmio + PLX_DMA0_PCI_ADDRESS_REG);
+
+ idx = devpriv->dma_desc_index;
+ start = le32_to_cpu(devpriv->dma_desc[idx].pci_start_addr);
+ /* loop until we have read all the full buffers */
+ for (desc = 0; (next < start || next >= start + devpriv->block_size) &&
+ desc < devpriv->num_dma_descriptors; desc++) {
+ /* transfer data from dma buffer to comedi buffer */
+ size = devpriv->block_size / sizeof(uint32_t);
+ if (cmd->stop_src == TRIG_COUNT) {
+ if (size > devpriv->dio_count)
+ size = devpriv->dio_count;
+ devpriv->dio_count -= size;
+ }
+ comedi_buf_write_samples(s, devpriv->desc_dio_buffer[idx],
+ size);
+ idx++;
+ idx %= devpriv->num_dma_descriptors;
+ start = le32_to_cpu(devpriv->dma_desc[idx].pci_start_addr);
+
+ devpriv->dma_desc_index = idx;
+ }
+ /* XXX check for buffer overrun somehow */
+}
+
+static irqreturn_t gsc_hpdi_interrupt(int irq, void *d)
+{
+ struct comedi_device *dev = d;
+ struct hpdi_private *devpriv = dev->private;
+ struct comedi_subdevice *s = dev->read_subdev;
+ struct comedi_async *async = s->async;
+ uint32_t hpdi_intr_status, hpdi_board_status;
+ uint32_t plx_status;
+ uint32_t plx_bits;
+ uint8_t dma0_status, dma1_status;
+ unsigned long flags;
+
+ if (!dev->attached)
+ return IRQ_NONE;
+
+ plx_status = readl(devpriv->plx9080_mmio + PLX_INTRCS_REG);
+ if ((plx_status & (ICS_DMA0_A | ICS_DMA1_A | ICS_LIA)) == 0)
+ return IRQ_NONE;
+
+ hpdi_intr_status = readl(dev->mmio + INTERRUPT_STATUS_REG);
+ hpdi_board_status = readl(dev->mmio + BOARD_STATUS_REG);
+
+ if (hpdi_intr_status)
+ writel(hpdi_intr_status, dev->mmio + INTERRUPT_STATUS_REG);
+
+ /* spin lock makes sure no one else changes plx dma control reg */
+ spin_lock_irqsave(&dev->spinlock, flags);
+ dma0_status = readb(devpriv->plx9080_mmio + PLX_DMA0_CS_REG);
+ if (plx_status & ICS_DMA0_A) { /* dma chan 0 interrupt */
+ writeb((dma0_status & PLX_DMA_EN_BIT) | PLX_CLEAR_DMA_INTR_BIT,
+ devpriv->plx9080_mmio + PLX_DMA0_CS_REG);
+
+ if (dma0_status & PLX_DMA_EN_BIT)
+ gsc_hpdi_drain_dma(dev, 0);
+ }
+ spin_unlock_irqrestore(&dev->spinlock, flags);
+
+ /* spin lock makes sure no one else changes plx dma control reg */
+ spin_lock_irqsave(&dev->spinlock, flags);
+ dma1_status = readb(devpriv->plx9080_mmio + PLX_DMA1_CS_REG);
+ if (plx_status & ICS_DMA1_A) { /* XXX *//* dma chan 1 interrupt */
+ writeb((dma1_status & PLX_DMA_EN_BIT) | PLX_CLEAR_DMA_INTR_BIT,
+ devpriv->plx9080_mmio + PLX_DMA1_CS_REG);
+ }
+ spin_unlock_irqrestore(&dev->spinlock, flags);
+
+ /* clear possible plx9080 interrupt sources */
+ if (plx_status & ICS_LDIA) { /* clear local doorbell interrupt */
+ plx_bits = readl(devpriv->plx9080_mmio + PLX_DBR_OUT_REG);
+ writel(plx_bits, devpriv->plx9080_mmio + PLX_DBR_OUT_REG);
+ }
+
+ if (hpdi_board_status & RX_OVERRUN_BIT) {
+ dev_err(dev->class_dev, "rx fifo overrun\n");
+ async->events |= COMEDI_CB_ERROR;
+ }
+
+ if (hpdi_board_status & RX_UNDERRUN_BIT) {
+ dev_err(dev->class_dev, "rx fifo underrun\n");
+ async->events |= COMEDI_CB_ERROR;
+ }
+
+ if (devpriv->dio_count == 0)
+ async->events |= COMEDI_CB_EOA;
+
+ comedi_handle_events(dev, s);
+
+ return IRQ_HANDLED;
+}
+
+static void gsc_hpdi_abort_dma(struct comedi_device *dev, unsigned int channel)
+{
+ struct hpdi_private *devpriv = dev->private;
+ unsigned long flags;
+
+ /* spinlock for plx dma control/status reg */
+ spin_lock_irqsave(&dev->spinlock, flags);
+
+ plx9080_abort_dma(devpriv->plx9080_mmio, channel);
+
+ spin_unlock_irqrestore(&dev->spinlock, flags);
+}
+
+static int gsc_hpdi_cancel(struct comedi_device *dev,
+ struct comedi_subdevice *s)
+{
+ writel(0, dev->mmio + BOARD_CONTROL_REG);
+ writel(0, dev->mmio + INTERRUPT_CONTROL_REG);
+
+ gsc_hpdi_abort_dma(dev, 0);
+
+ return 0;
+}
+
+static int gsc_hpdi_cmd(struct comedi_device *dev,
+ struct comedi_subdevice *s)
+{
+ struct hpdi_private *devpriv = dev->private;
+ struct comedi_async *async = s->async;
+ struct comedi_cmd *cmd = &async->cmd;
+ unsigned long flags;
+ uint32_t bits;
+
+ if (s->io_bits)
+ return -EINVAL;
+
+ writel(RX_FIFO_RESET_BIT, dev->mmio + BOARD_CONTROL_REG);
+
+ gsc_hpdi_abort_dma(dev, 0);
+
+ devpriv->dma_desc_index = 0;
+
+ /*
+ * These register are supposedly unused during chained dma,
+ * but I have found that left over values from last operation
+ * occasionally cause problems with transfer of first dma
+ * block. Initializing them to zero seems to fix the problem.
+ */
+ writel(0, devpriv->plx9080_mmio + PLX_DMA0_TRANSFER_SIZE_REG);
+ writel(0, devpriv->plx9080_mmio + PLX_DMA0_PCI_ADDRESS_REG);
+ writel(0, devpriv->plx9080_mmio + PLX_DMA0_LOCAL_ADDRESS_REG);
+
+ /* give location of first dma descriptor */
+ bits = devpriv->dma_desc_phys_addr | PLX_DESC_IN_PCI_BIT |
+ PLX_INTR_TERM_COUNT | PLX_XFER_LOCAL_TO_PCI;
+ writel(bits, devpriv->plx9080_mmio + PLX_DMA0_DESCRIPTOR_REG);
+
+ /* enable dma transfer */
+ spin_lock_irqsave(&dev->spinlock, flags);
+ writeb(PLX_DMA_EN_BIT | PLX_DMA_START_BIT | PLX_CLEAR_DMA_INTR_BIT,
+ devpriv->plx9080_mmio + PLX_DMA0_CS_REG);
+ spin_unlock_irqrestore(&dev->spinlock, flags);
+
+ if (cmd->stop_src == TRIG_COUNT)
+ devpriv->dio_count = cmd->stop_arg;
+ else
+ devpriv->dio_count = 1;
+
+ /* clear over/under run status flags */
+ writel(RX_UNDERRUN_BIT | RX_OVERRUN_BIT, dev->mmio + BOARD_STATUS_REG);
+
+ /* enable interrupts */
+ writel(RX_FULL_INTR, dev->mmio + INTERRUPT_CONTROL_REG);
+
+ writel(RX_ENABLE_BIT, dev->mmio + BOARD_CONTROL_REG);
+
+ return 0;
+}
+
+static int gsc_hpdi_check_chanlist(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_cmd *cmd)
+{
+ int i;
+
+ for (i = 0; i < cmd->chanlist_len; i++) {
+ unsigned int chan = CR_CHAN(cmd->chanlist[i]);
+
+ if (chan != i) {
+ dev_dbg(dev->class_dev,
+ "chanlist must be ch 0 to 31 in order\n");
+ return -EINVAL;
+ }
+ }
+
+ return 0;
+}
+
+static int gsc_hpdi_cmd_test(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_cmd *cmd)
+{
+ int err = 0;
+
+ if (s->io_bits)
+ return -EINVAL;
+
+ /* Step 1 : check if triggers are trivially valid */
+
+ err |= comedi_check_trigger_src(&cmd->start_src, TRIG_NOW);
+ err |= comedi_check_trigger_src(&cmd->scan_begin_src, TRIG_EXT);
+ err |= comedi_check_trigger_src(&cmd->convert_src, TRIG_NOW);
+ err |= comedi_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
+ err |= comedi_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE);
+
+ if (err)
+ return 1;
+
+ /* Step 2a : make sure trigger sources are unique */
+
+ err |= comedi_check_trigger_is_unique(cmd->stop_src);
+
+ /* Step 2b : and mutually compatible */
+
+ if (err)
+ return 2;
+
+ /* Step 3: check if arguments are trivially valid */
+
+ err |= comedi_check_trigger_arg_is(&cmd->start_arg, 0);
+
+ if (!cmd->chanlist_len || !cmd->chanlist) {
+ cmd->chanlist_len = 32;
+ err |= -EINVAL;
+ }
+ err |= comedi_check_trigger_arg_is(&cmd->scan_end_arg,
+ cmd->chanlist_len);
+
+ if (cmd->stop_src == TRIG_COUNT)
+ err |= comedi_check_trigger_arg_min(&cmd->stop_arg, 1);
+ else /* TRIG_NONE */
+ err |= comedi_check_trigger_arg_is(&cmd->stop_arg, 0);
+
+ if (err)
+ return 3;
+
+ /* Step 4: fix up any arguments */
+
+ /* Step 5: check channel list if it exists */
+
+ if (cmd->chanlist && cmd->chanlist_len > 0)
+ err |= gsc_hpdi_check_chanlist(dev, s, cmd);
+
+ if (err)
+ return 5;
+
+ return 0;
+}
+
+/* setup dma descriptors so a link completes every 'len' bytes */
+static int gsc_hpdi_setup_dma_descriptors(struct comedi_device *dev,
+ unsigned int len)
+{
+ struct hpdi_private *devpriv = dev->private;
+ dma_addr_t phys_addr = devpriv->dma_desc_phys_addr;
+ uint32_t next_bits = PLX_DESC_IN_PCI_BIT | PLX_INTR_TERM_COUNT |
+ PLX_XFER_LOCAL_TO_PCI;
+ unsigned int offset = 0;
+ unsigned int idx = 0;
+ unsigned int i;
+
+ if (len > DMA_BUFFER_SIZE)
+ len = DMA_BUFFER_SIZE;
+ len -= len % sizeof(uint32_t);
+ if (len == 0)
+ return -EINVAL;
+
+ for (i = 0; i < NUM_DMA_DESCRIPTORS && idx < NUM_DMA_BUFFERS; i++) {
+ devpriv->dma_desc[i].pci_start_addr =
+ cpu_to_le32(devpriv->dio_buffer_phys_addr[idx] + offset);
+ devpriv->dma_desc[i].local_start_addr = cpu_to_le32(FIFO_REG);
+ devpriv->dma_desc[i].transfer_size = cpu_to_le32(len);
+ devpriv->dma_desc[i].next = cpu_to_le32((phys_addr +
+ (i + 1) * sizeof(devpriv->dma_desc[0])) | next_bits);
+
+ devpriv->desc_dio_buffer[i] = devpriv->dio_buffer[idx] +
+ (offset / sizeof(uint32_t));
+
+ offset += len;
+ if (len + offset > DMA_BUFFER_SIZE) {
+ offset = 0;
+ idx++;
+ }
+ }
+ devpriv->num_dma_descriptors = i;
+ /* fix last descriptor to point back to first */
+ devpriv->dma_desc[i - 1].next = cpu_to_le32(phys_addr | next_bits);
+
+ devpriv->block_size = len;
+
+ return len;
+}
+
+static int gsc_hpdi_dio_insn_config(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ int ret;
+
+ switch (data[0]) {
+ case INSN_CONFIG_BLOCK_SIZE:
+ ret = gsc_hpdi_setup_dma_descriptors(dev, data[1]);
+ if (ret)
+ return ret;
+
+ data[1] = ret;
+ break;
+ default:
+ ret = comedi_dio_insn_config(dev, s, insn, data, 0xffffffff);
+ if (ret)
+ return ret;
+ break;
+ }
+
+ return insn->n;
+}
+
+static void gsc_hpdi_free_dma(struct comedi_device *dev)
+{
+ struct pci_dev *pcidev = comedi_to_pci_dev(dev);
+ struct hpdi_private *devpriv = dev->private;
+ int i;
+
+ if (!devpriv)
+ return;
+
+ /* free pci dma buffers */
+ for (i = 0; i < NUM_DMA_BUFFERS; i++) {
+ if (devpriv->dio_buffer[i])
+ pci_free_consistent(pcidev,
+ DMA_BUFFER_SIZE,
+ devpriv->dio_buffer[i],
+ devpriv->dio_buffer_phys_addr[i]);
+ }
+ /* free dma descriptors */
+ if (devpriv->dma_desc)
+ pci_free_consistent(pcidev,
+ sizeof(struct plx_dma_desc) *
+ NUM_DMA_DESCRIPTORS,
+ devpriv->dma_desc,
+ devpriv->dma_desc_phys_addr);
+}
+
+static int gsc_hpdi_init(struct comedi_device *dev)
+{
+ struct hpdi_private *devpriv = dev->private;
+ uint32_t plx_intcsr_bits;
+
+ /* wait 10usec after reset before accessing fifos */
+ writel(BOARD_RESET_BIT, dev->mmio + BOARD_CONTROL_REG);
+ udelay(10);
+
+ writel(ALMOST_EMPTY_BITS(32) | ALMOST_FULL_BITS(32),
+ dev->mmio + RX_PROG_ALMOST_REG);
+ writel(ALMOST_EMPTY_BITS(32) | ALMOST_FULL_BITS(32),
+ dev->mmio + TX_PROG_ALMOST_REG);
+
+ devpriv->tx_fifo_size = readl(dev->mmio + TX_FIFO_SIZE_REG) &
+ FIFO_SIZE_MASK;
+ devpriv->rx_fifo_size = readl(dev->mmio + RX_FIFO_SIZE_REG) &
+ FIFO_SIZE_MASK;
+
+ writel(0, dev->mmio + INTERRUPT_CONTROL_REG);
+
+ /* enable interrupts */
+ plx_intcsr_bits =
+ ICS_AERR | ICS_PERR | ICS_PIE | ICS_PLIE | ICS_PAIE | ICS_LIE |
+ ICS_DMA0_E;
+ writel(plx_intcsr_bits, devpriv->plx9080_mmio + PLX_INTRCS_REG);
+
+ return 0;
+}
+
+static void gsc_hpdi_init_plx9080(struct comedi_device *dev)
+{
+ struct hpdi_private *devpriv = dev->private;
+ uint32_t bits;
+ void __iomem *plx_iobase = devpriv->plx9080_mmio;
+
+#ifdef __BIG_ENDIAN
+ bits = BIGEND_DMA0 | BIGEND_DMA1;
+#else
+ bits = 0;
+#endif
+ writel(bits, devpriv->plx9080_mmio + PLX_BIGEND_REG);
+
+ writel(0, devpriv->plx9080_mmio + PLX_INTRCS_REG);
+
+ gsc_hpdi_abort_dma(dev, 0);
+ gsc_hpdi_abort_dma(dev, 1);
+
+ /* configure dma0 mode */
+ bits = 0;
+ /* enable ready input */
+ bits |= PLX_DMA_EN_READYIN_BIT;
+ /* enable dma chaining */
+ bits |= PLX_EN_CHAIN_BIT;
+ /* enable interrupt on dma done
+ * (probably don't need this, since chain never finishes) */
+ bits |= PLX_EN_DMA_DONE_INTR_BIT;
+ /* don't increment local address during transfers
+ * (we are transferring from a fixed fifo register) */
+ bits |= PLX_LOCAL_ADDR_CONST_BIT;
+ /* route dma interrupt to pci bus */
+ bits |= PLX_DMA_INTR_PCI_BIT;
+ /* enable demand mode */
+ bits |= PLX_DEMAND_MODE_BIT;
+ /* enable local burst mode */
+ bits |= PLX_DMA_LOCAL_BURST_EN_BIT;
+ bits |= PLX_LOCAL_BUS_32_WIDE_BITS;
+ writel(bits, plx_iobase + PLX_DMA0_MODE_REG);
+}
+
+static const struct hpdi_board *gsc_hpdi_find_board(struct pci_dev *pcidev)
+{
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(hpdi_boards); i++)
+ if (pcidev->device == hpdi_boards[i].device_id &&
+ pcidev->subsystem_device == hpdi_boards[i].subdevice_id)
+ return &hpdi_boards[i];
+ return NULL;
+}
+
+static int gsc_hpdi_auto_attach(struct comedi_device *dev,
+ unsigned long context_unused)
+{
+ struct pci_dev *pcidev = comedi_to_pci_dev(dev);
+ const struct hpdi_board *thisboard;
+ struct hpdi_private *devpriv;
+ struct comedi_subdevice *s;
+ int i;
+ int retval;
+
+ thisboard = gsc_hpdi_find_board(pcidev);
+ if (!thisboard) {
+ dev_err(dev->class_dev, "gsc_hpdi: pci %s not supported\n",
+ pci_name(pcidev));
+ return -EINVAL;
+ }
+ dev->board_ptr = thisboard;
+ dev->board_name = thisboard->name;
+
+ devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
+ if (!devpriv)
+ return -ENOMEM;
+
+ retval = comedi_pci_enable(dev);
+ if (retval)
+ return retval;
+ pci_set_master(pcidev);
+
+ devpriv->plx9080_mmio = pci_ioremap_bar(pcidev, 0);
+ dev->mmio = pci_ioremap_bar(pcidev, 2);
+ if (!devpriv->plx9080_mmio || !dev->mmio) {
+ dev_warn(dev->class_dev, "failed to remap io memory\n");
+ return -ENOMEM;
+ }
+
+ gsc_hpdi_init_plx9080(dev);
+
+ /* get irq */
+ if (request_irq(pcidev->irq, gsc_hpdi_interrupt, IRQF_SHARED,
+ dev->board_name, dev)) {
+ dev_warn(dev->class_dev,
+ "unable to allocate irq %u\n", pcidev->irq);
+ return -EINVAL;
+ }
+ dev->irq = pcidev->irq;
+
+ dev_dbg(dev->class_dev, " irq %u\n", dev->irq);
+
+ /* allocate pci dma buffers */
+ for (i = 0; i < NUM_DMA_BUFFERS; i++) {
+ devpriv->dio_buffer[i] =
+ pci_alloc_consistent(pcidev, DMA_BUFFER_SIZE,
+ &devpriv->dio_buffer_phys_addr[i]);
+ }
+ /* allocate dma descriptors */
+ devpriv->dma_desc = pci_alloc_consistent(pcidev,
+ sizeof(struct plx_dma_desc) *
+ NUM_DMA_DESCRIPTORS,
+ &devpriv->dma_desc_phys_addr);
+ if (devpriv->dma_desc_phys_addr & 0xf) {
+ dev_warn(dev->class_dev,
+ " dma descriptors not quad-word aligned (bug)\n");
+ return -EIO;
+ }
+
+ retval = gsc_hpdi_setup_dma_descriptors(dev, 0x1000);
+ if (retval < 0)
+ return retval;
+
+ retval = comedi_alloc_subdevices(dev, 1);
+ if (retval)
+ return retval;
+
+ /* Digital I/O subdevice */
+ s = &dev->subdevices[0];
+ dev->read_subdev = s;
+ s->type = COMEDI_SUBD_DIO;
+ s->subdev_flags = SDF_READABLE | SDF_WRITABLE | SDF_LSAMPL |
+ SDF_CMD_READ;
+ s->n_chan = 32;
+ s->len_chanlist = 32;
+ s->maxdata = 1;
+ s->range_table = &range_digital;
+ s->insn_config = gsc_hpdi_dio_insn_config;
+ s->do_cmd = gsc_hpdi_cmd;
+ s->do_cmdtest = gsc_hpdi_cmd_test;
+ s->cancel = gsc_hpdi_cancel;
+
+ return gsc_hpdi_init(dev);
+}
+
+static void gsc_hpdi_detach(struct comedi_device *dev)
+{
+ struct hpdi_private *devpriv = dev->private;
+
+ if (dev->irq)
+ free_irq(dev->irq, dev);
+ if (devpriv) {
+ if (devpriv->plx9080_mmio) {
+ writel(0, devpriv->plx9080_mmio + PLX_INTRCS_REG);
+ iounmap(devpriv->plx9080_mmio);
+ }
+ if (dev->mmio)
+ iounmap(dev->mmio);
+ }
+ comedi_pci_disable(dev);
+ gsc_hpdi_free_dma(dev);
+}
+
+static struct comedi_driver gsc_hpdi_driver = {
+ .driver_name = "gsc_hpdi",
+ .module = THIS_MODULE,
+ .auto_attach = gsc_hpdi_auto_attach,
+ .detach = gsc_hpdi_detach,
+};
+
+static int gsc_hpdi_pci_probe(struct pci_dev *dev,
+ const struct pci_device_id *id)
+{
+ return comedi_pci_auto_config(dev, &gsc_hpdi_driver, id->driver_data);
+}
+
+static const struct pci_device_id gsc_hpdi_pci_table[] = {
+ { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9080, PCI_VENDOR_ID_PLX,
+ 0x2400, 0, 0, 0},
+ { 0 }
+};
+MODULE_DEVICE_TABLE(pci, gsc_hpdi_pci_table);
+
+static struct pci_driver gsc_hpdi_pci_driver = {
+ .name = "gsc_hpdi",
+ .id_table = gsc_hpdi_pci_table,
+ .probe = gsc_hpdi_pci_probe,
+ .remove = comedi_pci_auto_unconfig,
+};
+module_comedi_pci_driver(gsc_hpdi_driver, gsc_hpdi_pci_driver);
+
+MODULE_AUTHOR("Comedi http://www.comedi.org");
+MODULE_DESCRIPTION("Comedi low-level driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/comedi/drivers/icp_multi.c b/drivers/staging/comedi/drivers/icp_multi.c
new file mode 100644
index 000000000..1e104ebf8
--- /dev/null
+++ b/drivers/staging/comedi/drivers/icp_multi.c
@@ -0,0 +1,568 @@
+/*
+ comedi/drivers/icp_multi.c
+
+ COMEDI - Linux Control and Measurement Device Interface
+ Copyright (C) 1997-2002 David A. Schleef <ds@schleef.org>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+*/
+
+/*
+Driver: icp_multi
+Description: Inova ICP_MULTI
+Author: Anne Smorthit <anne.smorthit@sfwte.ch>
+Devices: [Inova] ICP_MULTI (icp_multi)
+Status: works
+
+The driver works for analog input and output and digital input and output.
+It does not work with interrupts or with the counters. Currently no support
+for DMA.
+
+It has 16 single-ended or 8 differential Analogue Input channels with 12-bit
+resolution. Ranges : 5V, 10V, +/-5V, +/-10V, 0..20mA and 4..20mA. Input
+ranges can be individually programmed for each channel. Voltage or current
+measurement is selected by jumper.
+
+There are 4 x 12-bit Analogue Outputs. Ranges : 5V, 10V, +/-5V, +/-10V
+
+16 x Digital Inputs, 24V
+
+8 x Digital Outputs, 24V, 1A
+
+4 x 16-bit counters
+
+Configuration options: not applicable, uses PCI auto config
+*/
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+
+#include "../comedi_pci.h"
+
+#define ICP_MULTI_ADC_CSR 0 /* R/W: ADC command/status register */
+#define ICP_MULTI_AI 2 /* R: Analogue input data */
+#define ICP_MULTI_DAC_CSR 4 /* R/W: DAC command/status register */
+#define ICP_MULTI_AO 6 /* R/W: Analogue output data */
+#define ICP_MULTI_DI 8 /* R/W: Digital inputs */
+#define ICP_MULTI_DO 0x0A /* R/W: Digital outputs */
+#define ICP_MULTI_INT_EN 0x0C /* R/W: Interrupt enable register */
+#define ICP_MULTI_INT_STAT 0x0E /* R/W: Interrupt status register */
+#define ICP_MULTI_CNTR0 0x10 /* R/W: Counter 0 */
+#define ICP_MULTI_CNTR1 0x12 /* R/W: counter 1 */
+#define ICP_MULTI_CNTR2 0x14 /* R/W: Counter 2 */
+#define ICP_MULTI_CNTR3 0x16 /* R/W: Counter 3 */
+
+/* Define bits from ADC command/status register */
+#define ADC_ST 0x0001 /* Start ADC */
+#define ADC_BSY 0x0001 /* ADC busy */
+#define ADC_BI 0x0010 /* Bipolar input range 1 = bipolar */
+#define ADC_RA 0x0020 /* Input range 0 = 5V, 1 = 10V */
+#define ADC_DI 0x0040 /* Differential input mode 1 = differential */
+
+/* Define bits from DAC command/status register */
+#define DAC_ST 0x0001 /* Start DAC */
+#define DAC_BSY 0x0001 /* DAC busy */
+#define DAC_BI 0x0010 /* Bipolar input range 1 = bipolar */
+#define DAC_RA 0x0020 /* Input range 0 = 5V, 1 = 10V */
+
+/* Define bits from interrupt enable/status registers */
+#define ADC_READY 0x0001 /* A/d conversion ready interrupt */
+#define DAC_READY 0x0002 /* D/a conversion ready interrupt */
+#define DOUT_ERROR 0x0004 /* Digital output error interrupt */
+#define DIN_STATUS 0x0008 /* Digital input status change interrupt */
+#define CIE0 0x0010 /* Counter 0 overrun interrupt */
+#define CIE1 0x0020 /* Counter 1 overrun interrupt */
+#define CIE2 0x0040 /* Counter 2 overrun interrupt */
+#define CIE3 0x0080 /* Counter 3 overrun interrupt */
+
+/* Useful definitions */
+#define Status_IRQ 0x00ff /* All interrupts */
+
+/* Define analogue range */
+static const struct comedi_lrange range_analog = {
+ 4, {
+ UNI_RANGE(5),
+ UNI_RANGE(10),
+ BIP_RANGE(5),
+ BIP_RANGE(10)
+ }
+};
+
+static const char range_codes_analog[] = { 0x00, 0x20, 0x10, 0x30 };
+
+/*
+==============================================================================
+ Data & Structure declarations
+==============================================================================
+*/
+
+struct icp_multi_private {
+ unsigned int AdcCmdStatus; /* ADC Command/Status register */
+ unsigned int DacCmdStatus; /* DAC Command/Status register */
+ unsigned int IntEnable; /* Interrupt Enable register */
+ unsigned int IntStatus; /* Interrupt Status register */
+ unsigned int act_chanlist[32]; /* list of scanned channel */
+ unsigned char act_chanlist_len; /* len of scanlist */
+ unsigned char act_chanlist_pos; /* actual position in MUX list */
+ unsigned int *ai_chanlist; /* actaul chanlist */
+ unsigned int do_data; /* Remember digital output data */
+};
+
+static void setup_channel_list(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ unsigned int *chanlist, unsigned int n_chan)
+{
+ struct icp_multi_private *devpriv = dev->private;
+ unsigned int i, range, chanprog;
+ unsigned int diff;
+
+ devpriv->act_chanlist_len = n_chan;
+ devpriv->act_chanlist_pos = 0;
+
+ for (i = 0; i < n_chan; i++) {
+ /* Get channel */
+ chanprog = CR_CHAN(chanlist[i]);
+
+ /* Determine if it is a differential channel (Bit 15 = 1) */
+ if (CR_AREF(chanlist[i]) == AREF_DIFF) {
+ diff = 1;
+ chanprog &= 0x0007;
+ } else {
+ diff = 0;
+ chanprog &= 0x000f;
+ }
+
+ /* Clear channel, range and input mode bits
+ * in A/D command/status register */
+ devpriv->AdcCmdStatus &= 0xf00f;
+
+ /* Set channel number and differential mode status bit */
+ if (diff) {
+ /* Set channel number, bits 9-11 & mode, bit 6 */
+ devpriv->AdcCmdStatus |= (chanprog << 9);
+ devpriv->AdcCmdStatus |= ADC_DI;
+ } else
+ /* Set channel number, bits 8-11 */
+ devpriv->AdcCmdStatus |= (chanprog << 8);
+
+ /* Get range for current channel */
+ range = range_codes_analog[CR_RANGE(chanlist[i])];
+ /* Set range. bits 4-5 */
+ devpriv->AdcCmdStatus |= range;
+
+ /* Output channel, range, mode to ICP Multi */
+ writew(devpriv->AdcCmdStatus, dev->mmio + ICP_MULTI_ADC_CSR);
+ }
+}
+
+static int icp_multi_ai_eoc(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned long context)
+{
+ unsigned int status;
+
+ status = readw(dev->mmio + ICP_MULTI_ADC_CSR);
+ if ((status & ADC_BSY) == 0)
+ return 0;
+ return -EBUSY;
+}
+
+static int icp_multi_insn_read_ai(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ struct icp_multi_private *devpriv = dev->private;
+ int ret = 0;
+ int n;
+
+ /* Disable A/D conversion ready interrupt */
+ devpriv->IntEnable &= ~ADC_READY;
+ writew(devpriv->IntEnable, dev->mmio + ICP_MULTI_INT_EN);
+
+ /* Clear interrupt status */
+ devpriv->IntStatus |= ADC_READY;
+ writew(devpriv->IntStatus, dev->mmio + ICP_MULTI_INT_STAT);
+
+ /* Set up appropriate channel, mode and range data, for specified ch */
+ setup_channel_list(dev, s, &insn->chanspec, 1);
+
+ for (n = 0; n < insn->n; n++) {
+ /* Set start ADC bit */
+ devpriv->AdcCmdStatus |= ADC_ST;
+ writew(devpriv->AdcCmdStatus, dev->mmio + ICP_MULTI_ADC_CSR);
+ devpriv->AdcCmdStatus &= ~ADC_ST;
+
+ udelay(1);
+
+ /* Wait for conversion to complete, or get fed up waiting */
+ ret = comedi_timeout(dev, s, insn, icp_multi_ai_eoc, 0);
+ if (ret)
+ break;
+
+ data[n] = (readw(dev->mmio + ICP_MULTI_AI) >> 4) & 0x0fff;
+ }
+
+ /* Disable interrupt */
+ devpriv->IntEnable &= ~ADC_READY;
+ writew(devpriv->IntEnable, dev->mmio + ICP_MULTI_INT_EN);
+
+ /* Clear interrupt status */
+ devpriv->IntStatus |= ADC_READY;
+ writew(devpriv->IntStatus, dev->mmio + ICP_MULTI_INT_STAT);
+
+ return ret ? ret : n;
+}
+
+static int icp_multi_ao_eoc(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned long context)
+{
+ unsigned int status;
+
+ status = readw(dev->mmio + ICP_MULTI_DAC_CSR);
+ if ((status & DAC_BSY) == 0)
+ return 0;
+ return -EBUSY;
+}
+
+static int icp_multi_ao_insn_write(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ struct icp_multi_private *devpriv = dev->private;
+ unsigned int chan = CR_CHAN(insn->chanspec);
+ unsigned int range = CR_RANGE(insn->chanspec);
+ int i;
+
+ /* Disable D/A conversion ready interrupt */
+ devpriv->IntEnable &= ~DAC_READY;
+ writew(devpriv->IntEnable, dev->mmio + ICP_MULTI_INT_EN);
+
+ /* Clear interrupt status */
+ devpriv->IntStatus |= DAC_READY;
+ writew(devpriv->IntStatus, dev->mmio + ICP_MULTI_INT_STAT);
+
+ /* Set up range and channel data */
+ /* Bit 4 = 1 : Bipolar */
+ /* Bit 5 = 0 : 5V */
+ /* Bit 5 = 1 : 10V */
+ /* Bits 8-9 : Channel number */
+ devpriv->DacCmdStatus &= 0xfccf;
+ devpriv->DacCmdStatus |= range_codes_analog[range];
+ devpriv->DacCmdStatus |= (chan << 8);
+
+ writew(devpriv->DacCmdStatus, dev->mmio + ICP_MULTI_DAC_CSR);
+
+ for (i = 0; i < insn->n; i++) {
+ unsigned int val = data[i];
+ int ret;
+
+ /* Wait for analogue output data register to be
+ * ready for new data, or get fed up waiting */
+ ret = comedi_timeout(dev, s, insn, icp_multi_ao_eoc, 0);
+ if (ret) {
+ /* Disable interrupt */
+ devpriv->IntEnable &= ~DAC_READY;
+ writew(devpriv->IntEnable,
+ dev->mmio + ICP_MULTI_INT_EN);
+
+ /* Clear interrupt status */
+ devpriv->IntStatus |= DAC_READY;
+ writew(devpriv->IntStatus,
+ dev->mmio + ICP_MULTI_INT_STAT);
+
+ return ret;
+ }
+
+ writew(val, dev->mmio + ICP_MULTI_AO);
+
+ /* Set DAC_ST bit to write the data to selected channel */
+ devpriv->DacCmdStatus |= DAC_ST;
+ writew(devpriv->DacCmdStatus, dev->mmio + ICP_MULTI_DAC_CSR);
+ devpriv->DacCmdStatus &= ~DAC_ST;
+
+ s->readback[chan] = val;
+ }
+
+ return insn->n;
+}
+
+static int icp_multi_insn_bits_di(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ data[1] = readw(dev->mmio + ICP_MULTI_DI);
+
+ return insn->n;
+}
+
+static int icp_multi_insn_bits_do(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ if (comedi_dio_update_state(s, data))
+ writew(s->state, dev->mmio + ICP_MULTI_DO);
+
+ data[1] = s->state;
+
+ return insn->n;
+}
+
+static int icp_multi_insn_read_ctr(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn, unsigned int *data)
+{
+ return 0;
+}
+
+static int icp_multi_insn_write_ctr(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ return 0;
+}
+
+static irqreturn_t interrupt_service_icp_multi(int irq, void *d)
+{
+ struct comedi_device *dev = d;
+ int int_no;
+
+ /* Is this interrupt from our board? */
+ int_no = readw(dev->mmio + ICP_MULTI_INT_STAT) & Status_IRQ;
+ if (!int_no)
+ /* No, exit */
+ return IRQ_NONE;
+
+ /* Determine which interrupt is active & handle it */
+ switch (int_no) {
+ case ADC_READY:
+ break;
+ case DAC_READY:
+ break;
+ case DOUT_ERROR:
+ break;
+ case DIN_STATUS:
+ break;
+ case CIE0:
+ break;
+ case CIE1:
+ break;
+ case CIE2:
+ break;
+ case CIE3:
+ break;
+ default:
+ break;
+ }
+
+ return IRQ_HANDLED;
+}
+
+#if 0
+static int check_channel_list(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ unsigned int *chanlist, unsigned int n_chan)
+{
+ unsigned int i;
+
+ /* Check that we at least have one channel to check */
+ if (n_chan < 1) {
+ dev_err(dev->class_dev, "range/channel list is empty!\n");
+ return 0;
+ }
+ /* Check all channels */
+ for (i = 0; i < n_chan; i++) {
+ /* Check that channel number is < maximum */
+ if (CR_AREF(chanlist[i]) == AREF_DIFF) {
+ if (CR_CHAN(chanlist[i]) > (s->nchan / 2)) {
+ dev_err(dev->class_dev,
+ "Incorrect differential ai ch-nr\n");
+ return 0;
+ }
+ } else {
+ if (CR_CHAN(chanlist[i]) > s->n_chan) {
+ dev_err(dev->class_dev,
+ "Incorrect ai channel number\n");
+ return 0;
+ }
+ }
+ }
+ return 1;
+}
+#endif
+
+static int icp_multi_reset(struct comedi_device *dev)
+{
+ struct icp_multi_private *devpriv = dev->private;
+ unsigned int i;
+
+ /* Clear INT enables and requests */
+ writew(0, dev->mmio + ICP_MULTI_INT_EN);
+ writew(0x00ff, dev->mmio + ICP_MULTI_INT_STAT);
+
+ /* Set DACs to 0..5V range and 0V output */
+ for (i = 0; i < 4; i++) {
+ devpriv->DacCmdStatus &= 0xfcce;
+
+ /* Set channel number */
+ devpriv->DacCmdStatus |= (i << 8);
+
+ /* Output 0V */
+ writew(0, dev->mmio + ICP_MULTI_AO);
+
+ /* Set start conversion bit */
+ devpriv->DacCmdStatus |= DAC_ST;
+
+ /* Output to command / status register */
+ writew(devpriv->DacCmdStatus, dev->mmio + ICP_MULTI_DAC_CSR);
+
+ /* Delay to allow DAC time to recover */
+ udelay(1);
+ }
+
+ /* Digital outputs to 0 */
+ writew(0, dev->mmio + ICP_MULTI_DO);
+
+ return 0;
+}
+
+static int icp_multi_auto_attach(struct comedi_device *dev,
+ unsigned long context_unused)
+{
+ struct pci_dev *pcidev = comedi_to_pci_dev(dev);
+ struct icp_multi_private *devpriv;
+ struct comedi_subdevice *s;
+ int ret;
+
+ devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
+ if (!devpriv)
+ return -ENOMEM;
+
+ ret = comedi_pci_enable(dev);
+ if (ret)
+ return ret;
+
+ dev->mmio = pci_ioremap_bar(pcidev, 2);
+ if (!dev->mmio)
+ return -ENOMEM;
+
+ ret = comedi_alloc_subdevices(dev, 5);
+ if (ret)
+ return ret;
+
+ icp_multi_reset(dev);
+
+ if (pcidev->irq) {
+ ret = request_irq(pcidev->irq, interrupt_service_icp_multi,
+ IRQF_SHARED, dev->board_name, dev);
+ if (ret == 0)
+ dev->irq = pcidev->irq;
+ }
+
+ s = &dev->subdevices[0];
+ dev->read_subdev = s;
+ s->type = COMEDI_SUBD_AI;
+ s->subdev_flags = SDF_READABLE | SDF_COMMON | SDF_GROUND | SDF_DIFF;
+ s->n_chan = 16;
+ s->maxdata = 0x0fff;
+ s->len_chanlist = 16;
+ s->range_table = &range_analog;
+ s->insn_read = icp_multi_insn_read_ai;
+
+ s = &dev->subdevices[1];
+ s->type = COMEDI_SUBD_AO;
+ s->subdev_flags = SDF_WRITABLE | SDF_GROUND | SDF_COMMON;
+ s->n_chan = 4;
+ s->maxdata = 0x0fff;
+ s->len_chanlist = 4;
+ s->range_table = &range_analog;
+ s->insn_write = icp_multi_ao_insn_write;
+
+ ret = comedi_alloc_subdev_readback(s);
+ if (ret)
+ return ret;
+
+ s = &dev->subdevices[2];
+ s->type = COMEDI_SUBD_DI;
+ s->subdev_flags = SDF_READABLE;
+ s->n_chan = 16;
+ s->maxdata = 1;
+ s->len_chanlist = 16;
+ s->range_table = &range_digital;
+ s->insn_bits = icp_multi_insn_bits_di;
+
+ s = &dev->subdevices[3];
+ s->type = COMEDI_SUBD_DO;
+ s->subdev_flags = SDF_WRITABLE;
+ s->n_chan = 8;
+ s->maxdata = 1;
+ s->len_chanlist = 8;
+ s->range_table = &range_digital;
+ s->insn_bits = icp_multi_insn_bits_do;
+
+ s = &dev->subdevices[4];
+ s->type = COMEDI_SUBD_COUNTER;
+ s->subdev_flags = SDF_WRITABLE;
+ s->n_chan = 4;
+ s->maxdata = 0xffff;
+ s->len_chanlist = 4;
+ s->state = 0;
+ s->insn_read = icp_multi_insn_read_ctr;
+ s->insn_write = icp_multi_insn_write_ctr;
+
+ return 0;
+}
+
+static void icp_multi_detach(struct comedi_device *dev)
+{
+ if (dev->mmio)
+ icp_multi_reset(dev);
+ comedi_pci_detach(dev);
+}
+
+static struct comedi_driver icp_multi_driver = {
+ .driver_name = "icp_multi",
+ .module = THIS_MODULE,
+ .auto_attach = icp_multi_auto_attach,
+ .detach = icp_multi_detach,
+};
+
+static int icp_multi_pci_probe(struct pci_dev *dev,
+ const struct pci_device_id *id)
+{
+ return comedi_pci_auto_config(dev, &icp_multi_driver, id->driver_data);
+}
+
+static const struct pci_device_id icp_multi_pci_table[] = {
+ { PCI_DEVICE(PCI_VENDOR_ID_ICP, 0x8000) },
+ { 0 }
+};
+MODULE_DEVICE_TABLE(pci, icp_multi_pci_table);
+
+static struct pci_driver icp_multi_pci_driver = {
+ .name = "icp_multi",
+ .id_table = icp_multi_pci_table,
+ .probe = icp_multi_pci_probe,
+ .remove = comedi_pci_auto_unconfig,
+};
+module_comedi_pci_driver(icp_multi_driver, icp_multi_pci_driver);
+
+MODULE_AUTHOR("Comedi http://www.comedi.org");
+MODULE_DESCRIPTION("Comedi low-level driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/comedi/drivers/ii_pci20kc.c b/drivers/staging/comedi/drivers/ii_pci20kc.c
new file mode 100644
index 000000000..0768bc42a
--- /dev/null
+++ b/drivers/staging/comedi/drivers/ii_pci20kc.c
@@ -0,0 +1,526 @@
+/*
+ * ii_pci20kc.c
+ * Driver for Intelligent Instruments PCI-20001C carrier board and modules.
+ *
+ * Copyright (C) 2000 Markus Kempf <kempf@matsci.uni-sb.de>
+ * with suggestions from David Schleef 16.06.2000
+ */
+
+/*
+ * Driver: ii_pci20kc
+ * Description: Intelligent Instruments PCI-20001C carrier board
+ * Devices: [Intelligent Instrumentation] PCI-20001C (ii_pci20kc)
+ * Author: Markus Kempf <kempf@matsci.uni-sb.de>
+ * Status: works
+ *
+ * Supports the PCI-20001C-1a and PCI-20001C-2a carrier boards. The
+ * -2a version has 32 on-board DIO channels. Three add-on modules
+ * can be added to the carrier board for additional functionality.
+ *
+ * Supported add-on modules:
+ * PCI-20006M-1 1 channel, 16-bit analog output module
+ * PCI-20006M-2 2 channel, 16-bit analog output module
+ * PCI-20341M-1A 4 channel, 16-bit analog input module
+ *
+ * Options:
+ * 0 Board base address
+ * 1 IRQ (not-used)
+ */
+
+#include <linux/module.h>
+#include "../comedidev.h"
+
+/*
+ * Register I/O map
+ */
+#define II20K_SIZE 0x400
+#define II20K_MOD_OFFSET 0x100
+#define II20K_ID_REG 0x00
+#define II20K_ID_MOD1_EMPTY (1 << 7)
+#define II20K_ID_MOD2_EMPTY (1 << 6)
+#define II20K_ID_MOD3_EMPTY (1 << 5)
+#define II20K_ID_MASK 0x1f
+#define II20K_ID_PCI20001C_1A 0x1b /* no on-board DIO */
+#define II20K_ID_PCI20001C_2A 0x1d /* on-board DIO */
+#define II20K_MOD_STATUS_REG 0x40
+#define II20K_MOD_STATUS_IRQ_MOD1 (1 << 7)
+#define II20K_MOD_STATUS_IRQ_MOD2 (1 << 6)
+#define II20K_MOD_STATUS_IRQ_MOD3 (1 << 5)
+#define II20K_DIO0_REG 0x80
+#define II20K_DIO1_REG 0x81
+#define II20K_DIR_ENA_REG 0x82
+#define II20K_DIR_DIO3_OUT (1 << 7)
+#define II20K_DIR_DIO2_OUT (1 << 6)
+#define II20K_BUF_DISAB_DIO3 (1 << 5)
+#define II20K_BUF_DISAB_DIO2 (1 << 4)
+#define II20K_DIR_DIO1_OUT (1 << 3)
+#define II20K_DIR_DIO0_OUT (1 << 2)
+#define II20K_BUF_DISAB_DIO1 (1 << 1)
+#define II20K_BUF_DISAB_DIO0 (1 << 0)
+#define II20K_CTRL01_REG 0x83
+#define II20K_CTRL01_SET (1 << 7)
+#define II20K_CTRL01_DIO0_IN (1 << 4)
+#define II20K_CTRL01_DIO1_IN (1 << 1)
+#define II20K_DIO2_REG 0xc0
+#define II20K_DIO3_REG 0xc1
+#define II20K_CTRL23_REG 0xc3
+#define II20K_CTRL23_SET (1 << 7)
+#define II20K_CTRL23_DIO2_IN (1 << 4)
+#define II20K_CTRL23_DIO3_IN (1 << 1)
+
+#define II20K_ID_PCI20006M_1 0xe2 /* 1 AO channels */
+#define II20K_ID_PCI20006M_2 0xe3 /* 2 AO channels */
+#define II20K_AO_STRB_REG(x) (0x0b + ((x) * 0x08))
+#define II20K_AO_LSB_REG(x) (0x0d + ((x) * 0x08))
+#define II20K_AO_MSB_REG(x) (0x0e + ((x) * 0x08))
+#define II20K_AO_STRB_BOTH_REG 0x1b
+
+#define II20K_ID_PCI20341M_1 0x77 /* 4 AI channels */
+#define II20K_AI_STATUS_CMD_REG 0x01
+#define II20K_AI_STATUS_CMD_BUSY (1 << 7)
+#define II20K_AI_STATUS_CMD_HW_ENA (1 << 1)
+#define II20K_AI_STATUS_CMD_EXT_START (1 << 0)
+#define II20K_AI_LSB_REG 0x02
+#define II20K_AI_MSB_REG 0x03
+#define II20K_AI_PACER_RESET_REG 0x04
+#define II20K_AI_16BIT_DATA_REG 0x06
+#define II20K_AI_CONF_REG 0x10
+#define II20K_AI_CONF_ENA (1 << 2)
+#define II20K_AI_OPT_REG 0x11
+#define II20K_AI_OPT_TRIG_ENA (1 << 5)
+#define II20K_AI_OPT_TRIG_INV (1 << 4)
+#define II20K_AI_OPT_TIMEBASE(x) (((x) & 0x3) << 1)
+#define II20K_AI_OPT_BURST_MODE (1 << 0)
+#define II20K_AI_STATUS_REG 0x12
+#define II20K_AI_STATUS_INT (1 << 7)
+#define II20K_AI_STATUS_TRIG (1 << 6)
+#define II20K_AI_STATUS_TRIG_ENA (1 << 5)
+#define II20K_AI_STATUS_PACER_ERR (1 << 2)
+#define II20K_AI_STATUS_DATA_ERR (1 << 1)
+#define II20K_AI_STATUS_SET_TIME_ERR (1 << 0)
+#define II20K_AI_LAST_CHAN_ADDR_REG 0x13
+#define II20K_AI_CUR_ADDR_REG 0x14
+#define II20K_AI_SET_TIME_REG 0x15
+#define II20K_AI_DELAY_LSB_REG 0x16
+#define II20K_AI_DELAY_MSB_REG 0x17
+#define II20K_AI_CHAN_ADV_REG 0x18
+#define II20K_AI_CHAN_RESET_REG 0x19
+#define II20K_AI_START_TRIG_REG 0x1a
+#define II20K_AI_COUNT_RESET_REG 0x1b
+#define II20K_AI_CHANLIST_REG 0x80
+#define II20K_AI_CHANLIST_ONBOARD_ONLY (1 << 5)
+#define II20K_AI_CHANLIST_GAIN(x) (((x) & 0x3) << 3)
+#define II20K_AI_CHANLIST_MUX_ENA (1 << 2)
+#define II20K_AI_CHANLIST_CHAN(x) (((x) & 0x3) << 0)
+#define II20K_AI_CHANLIST_LEN 0x80
+
+/* the AO range is set by jumpers on the 20006M module */
+static const struct comedi_lrange ii20k_ao_ranges = {
+ 3, {
+ BIP_RANGE(5), /* Chan 0 - W1/W3 in Chan 1 - W2/W4 in */
+ UNI_RANGE(10), /* Chan 0 - W1/W3 out Chan 1 - W2/W4 in */
+ BIP_RANGE(10) /* Chan 0 - W1/W3 in Chan 1 - W2/W4 out */
+ }
+};
+
+static const struct comedi_lrange ii20k_ai_ranges = {
+ 4, {
+ BIP_RANGE(5), /* gain 1 */
+ BIP_RANGE(0.5), /* gain 10 */
+ BIP_RANGE(0.05), /* gain 100 */
+ BIP_RANGE(0.025) /* gain 200 */
+ },
+};
+
+static void __iomem *ii20k_module_iobase(struct comedi_device *dev,
+ struct comedi_subdevice *s)
+{
+ return dev->mmio + (s->index + 1) * II20K_MOD_OFFSET;
+}
+
+static int ii20k_ao_insn_write(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ void __iomem *iobase = ii20k_module_iobase(dev, s);
+ unsigned int chan = CR_CHAN(insn->chanspec);
+ int i;
+
+ for (i = 0; i < insn->n; i++) {
+ unsigned int val = data[i];
+
+ s->readback[chan] = val;
+
+ /* munge data */
+ val += ((s->maxdata + 1) >> 1);
+ val &= s->maxdata;
+
+ writeb(val & 0xff, iobase + II20K_AO_LSB_REG(chan));
+ writeb((val >> 8) & 0xff, iobase + II20K_AO_MSB_REG(chan));
+ writeb(0x00, iobase + II20K_AO_STRB_REG(chan));
+ }
+
+ return insn->n;
+}
+
+static int ii20k_ai_eoc(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned long context)
+{
+ void __iomem *iobase = ii20k_module_iobase(dev, s);
+ unsigned char status;
+
+ status = readb(iobase + II20K_AI_STATUS_REG);
+ if ((status & II20K_AI_STATUS_INT) == 0)
+ return 0;
+ return -EBUSY;
+}
+
+static void ii20k_ai_setup(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ unsigned int chanspec)
+{
+ void __iomem *iobase = ii20k_module_iobase(dev, s);
+ unsigned int chan = CR_CHAN(chanspec);
+ unsigned int range = CR_RANGE(chanspec);
+ unsigned char val;
+
+ /* initialize module */
+ writeb(II20K_AI_CONF_ENA, iobase + II20K_AI_CONF_REG);
+
+ /* software conversion */
+ writeb(0, iobase + II20K_AI_STATUS_CMD_REG);
+
+ /* set the time base for the settling time counter based on the gain */
+ val = (range < 3) ? II20K_AI_OPT_TIMEBASE(0) : II20K_AI_OPT_TIMEBASE(2);
+ writeb(val, iobase + II20K_AI_OPT_REG);
+
+ /* set the settling time counter based on the gain */
+ val = (range < 2) ? 0x58 : (range < 3) ? 0x93 : 0x99;
+ writeb(val, iobase + II20K_AI_SET_TIME_REG);
+
+ /* set number of input channels */
+ writeb(1, iobase + II20K_AI_LAST_CHAN_ADDR_REG);
+
+ /* set the channel list byte */
+ val = II20K_AI_CHANLIST_ONBOARD_ONLY |
+ II20K_AI_CHANLIST_MUX_ENA |
+ II20K_AI_CHANLIST_GAIN(range) |
+ II20K_AI_CHANLIST_CHAN(chan);
+ writeb(val, iobase + II20K_AI_CHANLIST_REG);
+
+ /* reset settling time counter and trigger delay counter */
+ writeb(0, iobase + II20K_AI_COUNT_RESET_REG);
+
+ /* reset channel scanner */
+ writeb(0, iobase + II20K_AI_CHAN_RESET_REG);
+}
+
+static int ii20k_ai_insn_read(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ void __iomem *iobase = ii20k_module_iobase(dev, s);
+ int ret;
+ int i;
+
+ ii20k_ai_setup(dev, s, insn->chanspec);
+
+ for (i = 0; i < insn->n; i++) {
+ unsigned int val;
+
+ /* generate a software start convert signal */
+ readb(iobase + II20K_AI_PACER_RESET_REG);
+
+ ret = comedi_timeout(dev, s, insn, ii20k_ai_eoc, 0);
+ if (ret)
+ return ret;
+
+ val = readb(iobase + II20K_AI_LSB_REG);
+ val |= (readb(iobase + II20K_AI_MSB_REG) << 8);
+
+ /* munge two's complement data */
+ val += ((s->maxdata + 1) >> 1);
+ val &= s->maxdata;
+
+ data[i] = val;
+ }
+
+ return insn->n;
+}
+
+static void ii20k_dio_config(struct comedi_device *dev,
+ struct comedi_subdevice *s)
+{
+ unsigned char ctrl01 = 0;
+ unsigned char ctrl23 = 0;
+ unsigned char dir_ena = 0;
+
+ /* port 0 - channels 0-7 */
+ if (s->io_bits & 0x000000ff) {
+ /* output port */
+ ctrl01 &= ~II20K_CTRL01_DIO0_IN;
+ dir_ena &= ~II20K_BUF_DISAB_DIO0;
+ dir_ena |= II20K_DIR_DIO0_OUT;
+ } else {
+ /* input port */
+ ctrl01 |= II20K_CTRL01_DIO0_IN;
+ dir_ena &= ~II20K_DIR_DIO0_OUT;
+ }
+
+ /* port 1 - channels 8-15 */
+ if (s->io_bits & 0x0000ff00) {
+ /* output port */
+ ctrl01 &= ~II20K_CTRL01_DIO1_IN;
+ dir_ena &= ~II20K_BUF_DISAB_DIO1;
+ dir_ena |= II20K_DIR_DIO1_OUT;
+ } else {
+ /* input port */
+ ctrl01 |= II20K_CTRL01_DIO1_IN;
+ dir_ena &= ~II20K_DIR_DIO1_OUT;
+ }
+
+ /* port 2 - channels 16-23 */
+ if (s->io_bits & 0x00ff0000) {
+ /* output port */
+ ctrl23 &= ~II20K_CTRL23_DIO2_IN;
+ dir_ena &= ~II20K_BUF_DISAB_DIO2;
+ dir_ena |= II20K_DIR_DIO2_OUT;
+ } else {
+ /* input port */
+ ctrl23 |= II20K_CTRL23_DIO2_IN;
+ dir_ena &= ~II20K_DIR_DIO2_OUT;
+ }
+
+ /* port 3 - channels 24-31 */
+ if (s->io_bits & 0xff000000) {
+ /* output port */
+ ctrl23 &= ~II20K_CTRL23_DIO3_IN;
+ dir_ena &= ~II20K_BUF_DISAB_DIO3;
+ dir_ena |= II20K_DIR_DIO3_OUT;
+ } else {
+ /* input port */
+ ctrl23 |= II20K_CTRL23_DIO3_IN;
+ dir_ena &= ~II20K_DIR_DIO3_OUT;
+ }
+
+ ctrl23 |= II20K_CTRL01_SET;
+ ctrl23 |= II20K_CTRL23_SET;
+
+ /* order is important */
+ writeb(ctrl01, dev->mmio + II20K_CTRL01_REG);
+ writeb(ctrl23, dev->mmio + II20K_CTRL23_REG);
+ writeb(dir_ena, dev->mmio + II20K_DIR_ENA_REG);
+}
+
+static int ii20k_dio_insn_config(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ unsigned int chan = CR_CHAN(insn->chanspec);
+ unsigned int mask;
+ int ret;
+
+ if (chan < 8)
+ mask = 0x000000ff;
+ else if (chan < 16)
+ mask = 0x0000ff00;
+ else if (chan < 24)
+ mask = 0x00ff0000;
+ else
+ mask = 0xff000000;
+
+ ret = comedi_dio_insn_config(dev, s, insn, data, mask);
+ if (ret)
+ return ret;
+
+ ii20k_dio_config(dev, s);
+
+ return insn->n;
+}
+
+static int ii20k_dio_insn_bits(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ unsigned int mask;
+
+ mask = comedi_dio_update_state(s, data);
+ if (mask) {
+ if (mask & 0x000000ff)
+ writeb((s->state >> 0) & 0xff,
+ dev->mmio + II20K_DIO0_REG);
+ if (mask & 0x0000ff00)
+ writeb((s->state >> 8) & 0xff,
+ dev->mmio + II20K_DIO1_REG);
+ if (mask & 0x00ff0000)
+ writeb((s->state >> 16) & 0xff,
+ dev->mmio + II20K_DIO2_REG);
+ if (mask & 0xff000000)
+ writeb((s->state >> 24) & 0xff,
+ dev->mmio + II20K_DIO3_REG);
+ }
+
+ data[1] = readb(dev->mmio + II20K_DIO0_REG);
+ data[1] |= readb(dev->mmio + II20K_DIO1_REG) << 8;
+ data[1] |= readb(dev->mmio + II20K_DIO2_REG) << 16;
+ data[1] |= readb(dev->mmio + II20K_DIO3_REG) << 24;
+
+ return insn->n;
+}
+
+static int ii20k_init_module(struct comedi_device *dev,
+ struct comedi_subdevice *s)
+{
+ void __iomem *iobase = ii20k_module_iobase(dev, s);
+ unsigned char id;
+ int ret;
+
+ id = readb(iobase + II20K_ID_REG);
+ switch (id) {
+ case II20K_ID_PCI20006M_1:
+ case II20K_ID_PCI20006M_2:
+ /* Analog Output subdevice */
+ s->type = COMEDI_SUBD_AO;
+ s->subdev_flags = SDF_WRITABLE;
+ s->n_chan = (id == II20K_ID_PCI20006M_2) ? 2 : 1;
+ s->maxdata = 0xffff;
+ s->range_table = &ii20k_ao_ranges;
+ s->insn_write = ii20k_ao_insn_write;
+
+ ret = comedi_alloc_subdev_readback(s);
+ if (ret)
+ return ret;
+ break;
+ case II20K_ID_PCI20341M_1:
+ /* Analog Input subdevice */
+ s->type = COMEDI_SUBD_AI;
+ s->subdev_flags = SDF_READABLE | SDF_DIFF;
+ s->n_chan = 4;
+ s->maxdata = 0xffff;
+ s->range_table = &ii20k_ai_ranges;
+ s->insn_read = ii20k_ai_insn_read;
+ break;
+ default:
+ s->type = COMEDI_SUBD_UNUSED;
+ break;
+ }
+
+ return 0;
+}
+
+static int ii20k_attach(struct comedi_device *dev,
+ struct comedi_devconfig *it)
+{
+ struct comedi_subdevice *s;
+ unsigned int membase;
+ unsigned char id;
+ bool has_dio;
+ int ret;
+
+ membase = it->options[0];
+ if (!membase || (membase & ~(0x100000 - II20K_SIZE))) {
+ dev_warn(dev->class_dev,
+ "%s: invalid memory address specified\n",
+ dev->board_name);
+ return -EINVAL;
+ }
+
+ if (!request_mem_region(membase, II20K_SIZE, dev->board_name)) {
+ dev_warn(dev->class_dev, "%s: I/O mem conflict (%#x,%u)\n",
+ dev->board_name, membase, II20K_SIZE);
+ return -EIO;
+ }
+ dev->iobase = membase; /* actually, a memory address */
+
+ dev->mmio = ioremap(membase, II20K_SIZE);
+ if (!dev->mmio)
+ return -ENOMEM;
+
+ id = readb(dev->mmio + II20K_ID_REG);
+ switch (id & II20K_ID_MASK) {
+ case II20K_ID_PCI20001C_1A:
+ has_dio = false;
+ break;
+ case II20K_ID_PCI20001C_2A:
+ has_dio = true;
+ break;
+ default:
+ return -ENODEV;
+ }
+
+ ret = comedi_alloc_subdevices(dev, 4);
+ if (ret)
+ return ret;
+
+ s = &dev->subdevices[0];
+ if (id & II20K_ID_MOD1_EMPTY) {
+ s->type = COMEDI_SUBD_UNUSED;
+ } else {
+ ret = ii20k_init_module(dev, s);
+ if (ret)
+ return ret;
+ }
+
+ s = &dev->subdevices[1];
+ if (id & II20K_ID_MOD2_EMPTY) {
+ s->type = COMEDI_SUBD_UNUSED;
+ } else {
+ ret = ii20k_init_module(dev, s);
+ if (ret)
+ return ret;
+ }
+
+ s = &dev->subdevices[2];
+ if (id & II20K_ID_MOD3_EMPTY) {
+ s->type = COMEDI_SUBD_UNUSED;
+ } else {
+ ret = ii20k_init_module(dev, s);
+ if (ret)
+ return ret;
+ }
+
+ /* Digital I/O subdevice */
+ s = &dev->subdevices[3];
+ if (has_dio) {
+ s->type = COMEDI_SUBD_DIO;
+ s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
+ s->n_chan = 32;
+ s->maxdata = 1;
+ s->range_table = &range_digital;
+ s->insn_bits = ii20k_dio_insn_bits;
+ s->insn_config = ii20k_dio_insn_config;
+
+ /* default all channels to input */
+ ii20k_dio_config(dev, s);
+ } else {
+ s->type = COMEDI_SUBD_UNUSED;
+ }
+
+ return 0;
+}
+
+static void ii20k_detach(struct comedi_device *dev)
+{
+ if (dev->mmio)
+ iounmap(dev->mmio);
+ if (dev->iobase) /* actually, a memory address */
+ release_mem_region(dev->iobase, II20K_SIZE);
+}
+
+static struct comedi_driver ii20k_driver = {
+ .driver_name = "ii_pci20kc",
+ .module = THIS_MODULE,
+ .attach = ii20k_attach,
+ .detach = ii20k_detach,
+};
+module_comedi_driver(ii20k_driver);
+
+MODULE_AUTHOR("Comedi http://www.comedi.org");
+MODULE_DESCRIPTION("Comedi low-level driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/comedi/drivers/jr3_pci.c b/drivers/staging/comedi/drivers/jr3_pci.c
new file mode 100644
index 000000000..40dd2b219
--- /dev/null
+++ b/drivers/staging/comedi/drivers/jr3_pci.c
@@ -0,0 +1,828 @@
+/*
+ comedi/drivers/jr3_pci.c
+ hardware driver for JR3/PCI force sensor board
+
+ COMEDI - Linux Control and Measurement Device Interface
+ Copyright (C) 2007 Anders Blomdell <anders.blomdell@control.lth.se>
+
+ This program is free software; you can redistribute it and/or 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.
+*/
+/*
+ * Driver: jr3_pci
+ * Description: JR3/PCI force sensor board
+ * Author: Anders Blomdell <anders.blomdell@control.lth.se>
+ * Updated: Thu, 01 Nov 2012 17:34:55 +0000
+ * Status: works
+ * Devices: [JR3] PCI force sensor board (jr3_pci)
+ *
+ * Configuration options:
+ * None
+ *
+ * Manual configuration of comedi devices is not supported by this
+ * driver; supported PCI devices are configured as comedi devices
+ * automatically.
+ *
+ * The DSP on the board requires initialization code, which can be
+ * loaded by placing it in /lib/firmware/comedi. The initialization
+ * code should be somewhere on the media you got with your card. One
+ * version is available from http://www.comedi.org in the
+ * comedi_nonfree_firmware tarball. The file is called "/*(DEBLOBBED)*/
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/ctype.h>
+#include <linux/jiffies.h>
+#include <linux/slab.h>
+#include <linux/timer.h>
+
+#include "../comedi_pci.h"
+
+#include "jr3_pci.h"
+
+#define PCI_VENDOR_ID_JR3 0x1762
+
+enum jr3_pci_boardid {
+ BOARD_JR3_1,
+ BOARD_JR3_2,
+ BOARD_JR3_3,
+ BOARD_JR3_4,
+};
+
+struct jr3_pci_board {
+ const char *name;
+ int n_subdevs;
+};
+
+static const struct jr3_pci_board jr3_pci_boards[] = {
+ [BOARD_JR3_1] = {
+ .name = "jr3_pci_1",
+ .n_subdevs = 1,
+ },
+ [BOARD_JR3_2] = {
+ .name = "jr3_pci_2",
+ .n_subdevs = 2,
+ },
+ [BOARD_JR3_3] = {
+ .name = "jr3_pci_3",
+ .n_subdevs = 3,
+ },
+ [BOARD_JR3_4] = {
+ .name = "jr3_pci_4",
+ .n_subdevs = 4,
+ },
+};
+
+struct jr3_pci_transform {
+ struct {
+ u16 link_type;
+ s16 link_amount;
+ } link[8];
+};
+
+struct jr3_pci_poll_delay {
+ int min;
+ int max;
+};
+
+struct jr3_pci_dev_private {
+ struct jr3_t __iomem *iobase;
+ struct timer_list timer;
+};
+
+struct jr3_pci_subdev_private {
+ struct jr3_channel __iomem *channel;
+ unsigned long next_time_min;
+ unsigned long next_time_max;
+ enum { state_jr3_poll,
+ state_jr3_init_wait_for_offset,
+ state_jr3_init_transform_complete,
+ state_jr3_init_set_full_scale_complete,
+ state_jr3_init_use_offset_complete,
+ state_jr3_done
+ } state;
+ int serial_no;
+ int model_no;
+ struct {
+ int length;
+ struct comedi_krange range;
+ } range[9];
+ const struct comedi_lrange *range_table_list[8 * 7 + 2];
+ unsigned int maxdata_list[8 * 7 + 2];
+ u16 errors;
+ int retries;
+};
+
+static struct jr3_pci_poll_delay poll_delay_min_max(int min, int max)
+{
+ struct jr3_pci_poll_delay result;
+
+ result.min = min;
+ result.max = max;
+ return result;
+}
+
+static int is_complete(struct jr3_channel __iomem *channel)
+{
+ return get_s16(&channel->command_word0) == 0;
+}
+
+static void set_transforms(struct jr3_channel __iomem *channel,
+ struct jr3_pci_transform transf, short num)
+{
+ int i;
+
+ num &= 0x000f; /* Make sure that 0 <= num <= 15 */
+ for (i = 0; i < 8; i++) {
+ set_u16(&channel->transforms[num].link[i].link_type,
+ transf.link[i].link_type);
+ udelay(1);
+ set_s16(&channel->transforms[num].link[i].link_amount,
+ transf.link[i].link_amount);
+ udelay(1);
+ if (transf.link[i].link_type == end_x_form)
+ break;
+ }
+}
+
+static void use_transform(struct jr3_channel __iomem *channel,
+ short transf_num)
+{
+ set_s16(&channel->command_word0, 0x0500 + (transf_num & 0x000f));
+}
+
+static void use_offset(struct jr3_channel __iomem *channel, short offset_num)
+{
+ set_s16(&channel->command_word0, 0x0600 + (offset_num & 0x000f));
+}
+
+static void set_offset(struct jr3_channel __iomem *channel)
+{
+ set_s16(&channel->command_word0, 0x0700);
+}
+
+struct six_axis_t {
+ s16 fx;
+ s16 fy;
+ s16 fz;
+ s16 mx;
+ s16 my;
+ s16 mz;
+};
+
+static void set_full_scales(struct jr3_channel __iomem *channel,
+ struct six_axis_t full_scale)
+{
+ set_s16(&channel->full_scale.fx, full_scale.fx);
+ set_s16(&channel->full_scale.fy, full_scale.fy);
+ set_s16(&channel->full_scale.fz, full_scale.fz);
+ set_s16(&channel->full_scale.mx, full_scale.mx);
+ set_s16(&channel->full_scale.my, full_scale.my);
+ set_s16(&channel->full_scale.mz, full_scale.mz);
+ set_s16(&channel->command_word0, 0x0a00);
+}
+
+static struct six_axis_t get_min_full_scales(struct jr3_channel __iomem
+ *channel)
+{
+ struct six_axis_t result;
+
+ result.fx = get_s16(&channel->min_full_scale.fx);
+ result.fy = get_s16(&channel->min_full_scale.fy);
+ result.fz = get_s16(&channel->min_full_scale.fz);
+ result.mx = get_s16(&channel->min_full_scale.mx);
+ result.my = get_s16(&channel->min_full_scale.my);
+ result.mz = get_s16(&channel->min_full_scale.mz);
+ return result;
+}
+
+static struct six_axis_t get_max_full_scales(struct jr3_channel __iomem
+ *channel)
+{
+ struct six_axis_t result;
+
+ result.fx = get_s16(&channel->max_full_scale.fx);
+ result.fy = get_s16(&channel->max_full_scale.fy);
+ result.fz = get_s16(&channel->max_full_scale.fz);
+ result.mx = get_s16(&channel->max_full_scale.mx);
+ result.my = get_s16(&channel->max_full_scale.my);
+ result.mz = get_s16(&channel->max_full_scale.mz);
+ return result;
+}
+
+static unsigned int jr3_pci_ai_read_chan(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ unsigned int chan)
+{
+ struct jr3_pci_subdev_private *spriv = s->private;
+ unsigned int val = 0;
+
+ if (spriv->state != state_jr3_done)
+ return 0;
+
+ if (chan < 56) {
+ unsigned int axis = chan % 8;
+ unsigned filter = chan / 8;
+
+ switch (axis) {
+ case 0:
+ val = get_s16(&spriv->channel->filter[filter].fx);
+ break;
+ case 1:
+ val = get_s16(&spriv->channel->filter[filter].fy);
+ break;
+ case 2:
+ val = get_s16(&spriv->channel->filter[filter].fz);
+ break;
+ case 3:
+ val = get_s16(&spriv->channel->filter[filter].mx);
+ break;
+ case 4:
+ val = get_s16(&spriv->channel->filter[filter].my);
+ break;
+ case 5:
+ val = get_s16(&spriv->channel->filter[filter].mz);
+ break;
+ case 6:
+ val = get_s16(&spriv->channel->filter[filter].v1);
+ break;
+ case 7:
+ val = get_s16(&spriv->channel->filter[filter].v2);
+ break;
+ }
+ val += 0x4000;
+ } else if (chan == 56) {
+ val = get_u16(&spriv->channel->model_no);
+ } else if (chan == 57) {
+ val = get_u16(&spriv->channel->serial_no);
+ }
+
+ return val;
+}
+
+static int jr3_pci_ai_insn_read(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ struct jr3_pci_subdev_private *spriv = s->private;
+ unsigned int chan = CR_CHAN(insn->chanspec);
+ u16 errors;
+ int i;
+
+ if (!spriv)
+ return -EINVAL;
+
+ errors = get_u16(&spriv->channel->errors);
+ if (spriv->state != state_jr3_done ||
+ (errors & (watch_dog | watch_dog2 | sensor_change))) {
+ /* No sensor or sensor changed */
+ if (spriv->state == state_jr3_done) {
+ /* Restart polling */
+ spriv->state = state_jr3_poll;
+ }
+ return -EAGAIN;
+ }
+
+ for (i = 0; i < insn->n; i++)
+ data[i] = jr3_pci_ai_read_chan(dev, s, chan);
+
+ return insn->n;
+}
+
+static int jr3_pci_open(struct comedi_device *dev)
+{
+ struct jr3_pci_subdev_private *spriv;
+ struct comedi_subdevice *s;
+ int i;
+
+ dev_dbg(dev->class_dev, "jr3_pci_open\n");
+ for (i = 0; i < dev->n_subdevices; i++) {
+ s = &dev->subdevices[i];
+ spriv = s->private;
+ if (spriv)
+ dev_dbg(dev->class_dev, "serial: %p %d (%d)\n",
+ spriv, spriv->serial_no, s->index);
+ }
+ return 0;
+}
+
+static int read_idm_word(const u8 *data, size_t size, int *pos,
+ unsigned int *val)
+{
+ int result = 0;
+ int value;
+
+ if (pos && val) {
+ /* Skip over non hex */
+ for (; *pos < size && !isxdigit(data[*pos]); (*pos)++)
+ ;
+ /* Collect value */
+ *val = 0;
+ for (; *pos < size; (*pos)++) {
+ value = hex_to_bin(data[*pos]);
+ if (value >= 0) {
+ result = 1;
+ *val = (*val << 4) + value;
+ } else {
+ break;
+ }
+ }
+ }
+ return result;
+}
+
+static int jr3_check_firmware(struct comedi_device *dev,
+ const u8 *data, size_t size)
+{
+ int more = 1;
+ int pos = 0;
+
+ /*
+ * IDM file format is:
+ * { count, address, data <count> } *
+ * ffff
+ */
+ while (more) {
+ unsigned int count = 0;
+ unsigned int addr = 0;
+
+ more = more && read_idm_word(data, size, &pos, &count);
+ if (more && count == 0xffff)
+ return 0;
+
+ more = more && read_idm_word(data, size, &pos, &addr);
+ while (more && count > 0) {
+ unsigned int dummy = 0;
+
+ more = more && read_idm_word(data, size, &pos, &dummy);
+ count--;
+ }
+ }
+
+ return -ENODATA;
+}
+
+static void jr3_write_firmware(struct comedi_device *dev,
+ int subdev, const u8 *data, size_t size)
+{
+ struct jr3_pci_dev_private *devpriv = dev->private;
+ struct jr3_t __iomem *iobase = devpriv->iobase;
+ u32 __iomem *lo;
+ u32 __iomem *hi;
+ int more = 1;
+ int pos = 0;
+
+ while (more) {
+ unsigned int count = 0;
+ unsigned int addr = 0;
+
+ more = more && read_idm_word(data, size, &pos, &count);
+ if (more && count == 0xffff)
+ return;
+
+ more = more && read_idm_word(data, size, &pos, &addr);
+
+ dev_dbg(dev->class_dev, "Loading#%d %4.4x bytes at %4.4x\n",
+ subdev, count, addr);
+
+ while (more && count > 0) {
+ if (addr & 0x4000) {
+ /* 16 bit data, never seen in real life!! */
+ unsigned int data1 = 0;
+
+ more = more &&
+ read_idm_word(data, size, &pos, &data1);
+ count--;
+ /* jr3[addr + 0x20000 * pnum] = data1; */
+ } else {
+ /* Download 24 bit program */
+ unsigned int data1 = 0;
+ unsigned int data2 = 0;
+
+ lo = &iobase->channel[subdev].program_lo[addr];
+ hi = &iobase->channel[subdev].program_hi[addr];
+
+ more = more &&
+ read_idm_word(data, size, &pos, &data1);
+ more = more &&
+ read_idm_word(data, size, &pos, &data2);
+ count -= 2;
+ if (more) {
+ set_u16(lo, data1);
+ udelay(1);
+ set_u16(hi, data2);
+ udelay(1);
+ }
+ }
+ addr++;
+ }
+ }
+}
+
+static int jr3_download_firmware(struct comedi_device *dev,
+ const u8 *data, size_t size,
+ unsigned long context)
+{
+ int subdev;
+ int ret;
+
+ /* verify IDM file format */
+ ret = jr3_check_firmware(dev, data, size);
+ if (ret)
+ return ret;
+
+ /* write firmware to each subdevice */
+ for (subdev = 0; subdev < dev->n_subdevices; subdev++)
+ jr3_write_firmware(dev, subdev, data, size);
+
+ return 0;
+}
+
+static struct jr3_pci_poll_delay jr3_pci_poll_subdevice(struct comedi_subdevice *s)
+{
+ struct jr3_pci_subdev_private *spriv = s->private;
+ struct jr3_pci_poll_delay result = poll_delay_min_max(1000, 2000);
+ struct jr3_channel __iomem *channel;
+ u16 model_no;
+ u16 serial_no;
+ int errors;
+ int i;
+
+ if (!spriv)
+ return result;
+
+ channel = spriv->channel;
+ errors = get_u16(&channel->errors);
+
+ if (errors != spriv->errors)
+ spriv->errors = errors;
+
+ /* Sensor communication lost? force poll mode */
+ if (errors & (watch_dog | watch_dog2 | sensor_change))
+ spriv->state = state_jr3_poll;
+
+ switch (spriv->state) {
+ case state_jr3_poll:
+ model_no = get_u16(&channel->model_no);
+ serial_no = get_u16(&channel->serial_no);
+
+ if ((errors & (watch_dog | watch_dog2)) ||
+ model_no == 0 || serial_no == 0) {
+ /*
+ * Still no sensor, keep on polling.
+ * Since it takes up to 10 seconds for offsets to
+ * stabilize, polling each second should suffice.
+ */
+ } else {
+ spriv->retries = 0;
+ spriv->state = state_jr3_init_wait_for_offset;
+ }
+ break;
+ case state_jr3_init_wait_for_offset:
+ spriv->retries++;
+ if (spriv->retries < 10) {
+ /*
+ * Wait for offeset to stabilize
+ * (< 10 s according to manual)
+ */
+ } else {
+ struct jr3_pci_transform transf;
+
+ spriv->model_no = get_u16(&channel->model_no);
+ spriv->serial_no = get_u16(&channel->serial_no);
+
+ /* Transformation all zeros */
+ for (i = 0; i < ARRAY_SIZE(transf.link); i++) {
+ transf.link[i].link_type = (enum link_types)0;
+ transf.link[i].link_amount = 0;
+ }
+
+ set_transforms(channel, transf, 0);
+ use_transform(channel, 0);
+ spriv->state = state_jr3_init_transform_complete;
+ /* Allow 20 ms for completion */
+ result = poll_delay_min_max(20, 100);
+ }
+ break;
+ case state_jr3_init_transform_complete:
+ if (!is_complete(channel)) {
+ result = poll_delay_min_max(20, 100);
+ } else {
+ /* Set full scale */
+ struct six_axis_t min_full_scale;
+ struct six_axis_t max_full_scale;
+
+ min_full_scale = get_min_full_scales(channel);
+ max_full_scale = get_max_full_scales(channel);
+ set_full_scales(channel, max_full_scale);
+
+ spriv->state = state_jr3_init_set_full_scale_complete;
+ /* Allow 20 ms for completion */
+ result = poll_delay_min_max(20, 100);
+ }
+ break;
+ case state_jr3_init_set_full_scale_complete:
+ if (!is_complete(channel)) {
+ result = poll_delay_min_max(20, 100);
+ } else {
+ struct force_array __iomem *fs = &channel->full_scale;
+
+ /* Use ranges in kN or we will overflow around 2000N! */
+ spriv->range[0].range.min = -get_s16(&fs->fx) * 1000;
+ spriv->range[0].range.max = get_s16(&fs->fx) * 1000;
+ spriv->range[1].range.min = -get_s16(&fs->fy) * 1000;
+ spriv->range[1].range.max = get_s16(&fs->fy) * 1000;
+ spriv->range[2].range.min = -get_s16(&fs->fz) * 1000;
+ spriv->range[2].range.max = get_s16(&fs->fz) * 1000;
+ spriv->range[3].range.min = -get_s16(&fs->mx) * 100;
+ spriv->range[3].range.max = get_s16(&fs->mx) * 100;
+ spriv->range[4].range.min = -get_s16(&fs->my) * 100;
+ spriv->range[4].range.max = get_s16(&fs->my) * 100;
+ spriv->range[5].range.min = -get_s16(&fs->mz) * 100;
+ /* the next five are questionable */
+ spriv->range[5].range.max = get_s16(&fs->mz) * 100;
+ spriv->range[6].range.min = -get_s16(&fs->v1) * 100;
+ spriv->range[6].range.max = get_s16(&fs->v1) * 100;
+ spriv->range[7].range.min = -get_s16(&fs->v2) * 100;
+ spriv->range[7].range.max = get_s16(&fs->v2) * 100;
+ spriv->range[8].range.min = 0;
+ spriv->range[8].range.max = 65535;
+
+ use_offset(channel, 0);
+ spriv->state = state_jr3_init_use_offset_complete;
+ /* Allow 40 ms for completion */
+ result = poll_delay_min_max(40, 100);
+ }
+ break;
+ case state_jr3_init_use_offset_complete:
+ if (!is_complete(channel)) {
+ result = poll_delay_min_max(20, 100);
+ } else {
+ set_s16(&channel->offsets.fx, 0);
+ set_s16(&channel->offsets.fy, 0);
+ set_s16(&channel->offsets.fz, 0);
+ set_s16(&channel->offsets.mx, 0);
+ set_s16(&channel->offsets.my, 0);
+ set_s16(&channel->offsets.mz, 0);
+
+ set_offset(channel);
+
+ spriv->state = state_jr3_done;
+ }
+ break;
+ case state_jr3_done:
+ result = poll_delay_min_max(10000, 20000);
+ break;
+ default:
+ break;
+ }
+
+ return result;
+}
+
+static void jr3_pci_poll_dev(unsigned long data)
+{
+ struct comedi_device *dev = (struct comedi_device *)data;
+ struct jr3_pci_dev_private *devpriv = dev->private;
+ struct jr3_pci_subdev_private *spriv;
+ struct comedi_subdevice *s;
+ unsigned long flags;
+ unsigned long now;
+ int delay;
+ int i;
+
+ spin_lock_irqsave(&dev->spinlock, flags);
+ delay = 1000;
+ now = jiffies;
+
+ /* Poll all channels that are ready to be polled */
+ for (i = 0; i < dev->n_subdevices; i++) {
+ s = &dev->subdevices[i];
+ spriv = s->private;
+
+ if (now > spriv->next_time_min) {
+ struct jr3_pci_poll_delay sub_delay;
+
+ sub_delay = jr3_pci_poll_subdevice(s);
+
+ spriv->next_time_min = jiffies +
+ msecs_to_jiffies(sub_delay.min);
+ spriv->next_time_max = jiffies +
+ msecs_to_jiffies(sub_delay.max);
+
+ if (sub_delay.max && sub_delay.max < delay)
+ /*
+ * Wake up as late as possible ->
+ * poll as many channels as possible at once.
+ */
+ delay = sub_delay.max;
+ }
+ }
+ spin_unlock_irqrestore(&dev->spinlock, flags);
+
+ devpriv->timer.expires = jiffies + msecs_to_jiffies(delay);
+ add_timer(&devpriv->timer);
+}
+
+static struct jr3_pci_subdev_private *
+jr3_pci_alloc_spriv(struct comedi_device *dev, struct comedi_subdevice *s)
+{
+ struct jr3_pci_dev_private *devpriv = dev->private;
+ struct jr3_pci_subdev_private *spriv;
+ int j;
+ int k;
+
+ spriv = comedi_alloc_spriv(s, sizeof(*spriv));
+ if (!spriv)
+ return NULL;
+
+ spriv->channel = &devpriv->iobase->channel[s->index].data;
+
+ for (j = 0; j < 8; j++) {
+ spriv->range[j].length = 1;
+ spriv->range[j].range.min = -1000000;
+ spriv->range[j].range.max = 1000000;
+
+ for (k = 0; k < 7; k++) {
+ spriv->range_table_list[j + k * 8] =
+ (struct comedi_lrange *)&spriv->range[j];
+ spriv->maxdata_list[j + k * 8] = 0x7fff;
+ }
+ }
+ spriv->range[8].length = 1;
+ spriv->range[8].range.min = 0;
+ spriv->range[8].range.max = 65536;
+
+ spriv->range_table_list[56] = (struct comedi_lrange *)&spriv->range[8];
+ spriv->range_table_list[57] = (struct comedi_lrange *)&spriv->range[8];
+ spriv->maxdata_list[56] = 0xffff;
+ spriv->maxdata_list[57] = 0xffff;
+
+ dev_dbg(dev->class_dev, "p->channel %p %p (%tx)\n",
+ spriv->channel, devpriv->iobase,
+ ((char __iomem *)spriv->channel -
+ (char __iomem *)devpriv->iobase));
+
+ return spriv;
+}
+
+static int jr3_pci_auto_attach(struct comedi_device *dev,
+ unsigned long context)
+{
+ struct pci_dev *pcidev = comedi_to_pci_dev(dev);
+ static const struct jr3_pci_board *board;
+ struct jr3_pci_dev_private *devpriv;
+ struct jr3_pci_subdev_private *spriv;
+ struct comedi_subdevice *s;
+ int ret;
+ int i;
+
+ if (sizeof(struct jr3_channel) != 0xc00) {
+ dev_err(dev->class_dev,
+ "sizeof(struct jr3_channel) = %x [expected %x]\n",
+ (unsigned)sizeof(struct jr3_channel), 0xc00);
+ return -EINVAL;
+ }
+
+ if (context < ARRAY_SIZE(jr3_pci_boards))
+ board = &jr3_pci_boards[context];
+ if (!board)
+ return -ENODEV;
+ dev->board_ptr = board;
+ dev->board_name = board->name;
+
+ devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
+ if (!devpriv)
+ return -ENOMEM;
+
+ ret = comedi_pci_enable(dev);
+ if (ret)
+ return ret;
+
+ devpriv->iobase = pci_ioremap_bar(pcidev, 0);
+ if (!devpriv->iobase)
+ return -ENOMEM;
+
+ ret = comedi_alloc_subdevices(dev, board->n_subdevs);
+ if (ret)
+ return ret;
+
+ dev->open = jr3_pci_open;
+ for (i = 0; i < dev->n_subdevices; i++) {
+ s = &dev->subdevices[i];
+ s->type = COMEDI_SUBD_AI;
+ s->subdev_flags = SDF_READABLE | SDF_GROUND;
+ s->n_chan = 8 * 7 + 2;
+ s->insn_read = jr3_pci_ai_insn_read;
+
+ spriv = jr3_pci_alloc_spriv(dev, s);
+ if (spriv) {
+ /* Channel specific range and maxdata */
+ s->range_table_list = spriv->range_table_list;
+ s->maxdata_list = spriv->maxdata_list;
+ }
+ }
+
+ /* Reset DSP card */
+ writel(0, &devpriv->iobase->channel[0].reset);
+
+ ret = comedi_load_firmware(dev, &comedi_to_pci_dev(dev)->dev,
+ "/*(DEBLOBBED)*/",
+ jr3_download_firmware, 0);
+ dev_dbg(dev->class_dev, "Firmare load %d\n", ret);
+ if (ret < 0)
+ return ret;
+ /*
+ * TODO: use firmware to load preferred offset tables. Suggested
+ * format:
+ * model serial Fx Fy Fz Mx My Mz\n
+ *
+ * comedi_load_firmware(dev, &comedi_to_pci_dev(dev)->dev,
+ * "comedi/jr3_offsets_table",
+ * jr3_download_firmware, 1);
+ */
+
+ /*
+ * It takes a few milliseconds for software to settle as much as we
+ * can read firmware version
+ */
+ msleep_interruptible(25);
+ for (i = 0; i < 0x18; i++) {
+ dev_dbg(dev->class_dev, "%c\n",
+ get_u16(&devpriv->iobase->channel[0].
+ data.copyright[i]) >> 8);
+ }
+
+ /* Start card timer */
+ for (i = 0; i < dev->n_subdevices; i++) {
+ s = &dev->subdevices[i];
+ spriv = s->private;
+
+ spriv->next_time_min = jiffies + msecs_to_jiffies(500);
+ spriv->next_time_max = jiffies + msecs_to_jiffies(2000);
+ }
+
+ setup_timer(&devpriv->timer, jr3_pci_poll_dev, (unsigned long)dev);
+ devpriv->timer.expires = jiffies + msecs_to_jiffies(1000);
+ add_timer(&devpriv->timer);
+
+ return 0;
+}
+
+static void jr3_pci_detach(struct comedi_device *dev)
+{
+ struct jr3_pci_dev_private *devpriv = dev->private;
+
+ if (devpriv) {
+ del_timer_sync(&devpriv->timer);
+
+ if (devpriv->iobase)
+ iounmap(devpriv->iobase);
+ }
+ comedi_pci_disable(dev);
+}
+
+static struct comedi_driver jr3_pci_driver = {
+ .driver_name = "jr3_pci",
+ .module = THIS_MODULE,
+ .auto_attach = jr3_pci_auto_attach,
+ .detach = jr3_pci_detach,
+};
+
+static int jr3_pci_pci_probe(struct pci_dev *dev,
+ const struct pci_device_id *id)
+{
+ return comedi_pci_auto_config(dev, &jr3_pci_driver, id->driver_data);
+}
+
+static const struct pci_device_id jr3_pci_pci_table[] = {
+ { PCI_VDEVICE(JR3, 0x1111), BOARD_JR3_1 },
+ { PCI_VDEVICE(JR3, 0x3111), BOARD_JR3_1 },
+ { PCI_VDEVICE(JR3, 0x3112), BOARD_JR3_2 },
+ { PCI_VDEVICE(JR3, 0x3113), BOARD_JR3_3 },
+ { PCI_VDEVICE(JR3, 0x3114), BOARD_JR3_4 },
+ { 0 }
+};
+MODULE_DEVICE_TABLE(pci, jr3_pci_pci_table);
+
+static struct pci_driver jr3_pci_pci_driver = {
+ .name = "jr3_pci",
+ .id_table = jr3_pci_pci_table,
+ .probe = jr3_pci_pci_probe,
+ .remove = comedi_pci_auto_unconfig,
+};
+module_comedi_pci_driver(jr3_pci_driver, jr3_pci_pci_driver);
+
+MODULE_AUTHOR("Comedi http://www.comedi.org");
+MODULE_DESCRIPTION("Comedi low-level driver");
+MODULE_LICENSE("GPL");
+/*(DEBLOBBED)*/
diff --git a/drivers/staging/comedi/drivers/jr3_pci.h b/drivers/staging/comedi/drivers/jr3_pci.h
new file mode 100644
index 000000000..356811def
--- /dev/null
+++ b/drivers/staging/comedi/drivers/jr3_pci.h
@@ -0,0 +1,682 @@
+/* Helper types to take care of the fact that the DSP card memory
+ * is 16 bits, but aligned on a 32 bit PCI boundary
+ */
+
+static inline u16 get_u16(const u32 __iomem *p)
+{
+ return (u16)readl(p);
+}
+
+static inline void set_u16(u32 __iomem *p, u16 val)
+{
+ writel(val, p);
+}
+
+static inline s16 get_s16(const s32 __iomem *p)
+{
+ return (s16)readl(p);
+}
+
+static inline void set_s16(s32 __iomem *p, s16 val)
+{
+ writel(val, p);
+}
+
+/* The raw data is stored in a format which facilitates rapid
+ * processing by the JR3 DSP chip. The raw_channel structure shows the
+ * format for a single channel of data. Each channel takes four,
+ * two-byte words.
+ *
+ * Raw_time is an unsigned integer which shows the value of the JR3
+ * DSP's internal clock at the time the sample was received. The clock
+ * runs at 1/10 the JR3 DSP cycle time. JR3's slowest DSP runs at 10
+ * Mhz. At 10 Mhz raw_time would therefore clock at 1 Mhz.
+ *
+ * Raw_data is the raw data received directly from the sensor. The
+ * sensor data stream is capable of representing 16 different
+ * channels. Channel 0 shows the excitation voltage at the sensor. It
+ * is used to regulate the voltage over various cable lengths.
+ * Channels 1-6 contain the coupled force data Fx through Mz. Channel
+ * 7 contains the sensor's calibration data. The use of channels 8-15
+ * varies with different sensors.
+ */
+
+struct raw_channel {
+ u32 raw_time;
+ s32 raw_data;
+ s32 reserved[2];
+};
+
+/* The force_array structure shows the layout for the decoupled and
+ * filtered force data.
+ */
+struct force_array {
+ s32 fx;
+ s32 fy;
+ s32 fz;
+ s32 mx;
+ s32 my;
+ s32 mz;
+ s32 v1;
+ s32 v2;
+};
+
+/* The six_axis_array structure shows the layout for the offsets and
+ * the full scales.
+ */
+struct six_axis_array {
+ s32 fx;
+ s32 fy;
+ s32 fz;
+ s32 mx;
+ s32 my;
+ s32 mz;
+};
+
+/* VECT_BITS */
+/* The vect_bits structure shows the layout for indicating
+ * which axes to use in computing the vectors. Each bit signifies
+ * selection of a single axis. The V1x axis bit corresponds to a hex
+ * value of 0x0001 and the V2z bit corresponds to a hex value of
+ * 0x0020. Example: to specify the axes V1x, V1y, V2x, and V2z the
+ * pattern would be 0x002b. Vector 1 defaults to a force vector and
+ * vector 2 defaults to a moment vector. It is possible to change one
+ * or the other so that two force vectors or two moment vectors are
+ * calculated. Setting the changeV1 bit or the changeV2 bit will
+ * change that vector to be the opposite of its default. Therefore to
+ * have two force vectors, set changeV1 to 1.
+ */
+
+/* vect_bits appears to be unused at this time */
+enum {
+ fx = 0x0001,
+ fy = 0x0002,
+ fz = 0x0004,
+ mx = 0x0008,
+ my = 0x0010,
+ mz = 0x0020,
+ changeV2 = 0x0040,
+ changeV1 = 0x0080
+};
+
+/* WARNING_BITS */
+/* The warning_bits structure shows the bit pattern for the warning
+ * word. The bit fields are shown from bit 0 (lsb) to bit 15 (msb).
+ */
+
+/* XX_NEAR_SET */
+/* The xx_near_sat bits signify that the indicated axis has reached or
+ * exceeded the near saturation value.
+ */
+
+enum {
+ fx_near_sat = 0x0001,
+ fy_near_sat = 0x0002,
+ fz_near_sat = 0x0004,
+ mx_near_sat = 0x0008,
+ my_near_sat = 0x0010,
+ mz_near_sat = 0x0020
+};
+
+/* ERROR_BITS */
+/* XX_SAT */
+/* MEMORY_ERROR */
+/* SENSOR_CHANGE */
+
+/* The error_bits structure shows the bit pattern for the error word.
+ * The bit fields are shown from bit 0 (lsb) to bit 15 (msb). The
+ * xx_sat bits signify that the indicated axis has reached or exceeded
+ * the saturation value. The memory_error bit indicates that a problem
+ * was detected in the on-board RAM during the power-up
+ * initialization. The sensor_change bit indicates that a sensor other
+ * than the one originally plugged in has passed its CRC check. This
+ * bit latches, and must be reset by the user.
+ *
+ */
+
+/* SYSTEM_BUSY */
+
+/* The system_busy bit indicates that the JR3 DSP is currently busy
+ * and is not calculating force data. This occurs when a new
+ * coordinate transformation, or new sensor full scale is set by the
+ * user. A very fast system using the force data for feedback might
+ * become unstable during the approximately 4 ms needed to accomplish
+ * these calculations. This bit will also become active when a new
+ * sensor is plugged in and the system needs to recalculate the
+ * calibration CRC.
+ */
+
+/* CAL_CRC_BAD */
+
+/* The cal_crc_bad bit indicates that the calibration CRC has not
+ * calculated to zero. CRC is short for cyclic redundancy code. It is
+ * a method for determining the integrity of messages in data
+ * communication. The calibration data stored inside the sensor is
+ * transmitted to the JR3 DSP along with the sensor data. The
+ * calibration data has a CRC attached to the end of it, to assist in
+ * determining the completeness and integrity of the calibration data
+ * received from the sensor. There are two reasons the CRC may not
+ * have calculated to zero. The first is that all the calibration data
+ * has not yet been received, the second is that the calibration data
+ * has been corrupted. A typical sensor transmits the entire contents
+ * of its calibration matrix over 30 times a second. Therefore, if
+ * this bit is not zero within a couple of seconds after the sensor
+ * has been plugged in, there is a problem with the sensor's
+ * calibration data.
+ */
+
+/* WATCH_DOG */
+/* WATCH_DOG2 */
+
+/* The watch_dog and watch_dog2 bits are sensor, not processor, watch
+ * dog bits. Watch_dog indicates that the sensor data line seems to be
+ * acting correctly, while watch_dog2 indicates that sensor data and
+ * clock are being received. It is possible for watch_dog2 to go off
+ * while watch_dog does not. This would indicate an improper clock
+ * signal, while data is acting correctly. If either watch dog barks,
+ * the sensor data is not being received correctly.
+ */
+
+enum error_bits_t {
+ fx_sat = 0x0001,
+ fy_sat = 0x0002,
+ fz_sat = 0x0004,
+ mx_sat = 0x0008,
+ my_sat = 0x0010,
+ mz_sat = 0x0020,
+ memory_error = 0x0400,
+ sensor_change = 0x0800,
+ system_busy = 0x1000,
+ cal_crc_bad = 0x2000,
+ watch_dog2 = 0x4000,
+ watch_dog = 0x8000
+};
+
+/* THRESH_STRUCT */
+
+/* This structure shows the layout for a single threshold packet inside of a
+ * load envelope. Each load envelope can contain several threshold structures.
+ * 1. data_address contains the address of the data for that threshold. This
+ * includes filtered, unfiltered, raw, rate, counters, error and warning data
+ * 2. threshold is the is the value at which, if data is above or below, the
+ * bits will be set ... (pag.24).
+ * 3. bit_pattern contains the bits that will be set if the threshold value is
+ * met or exceeded.
+ */
+
+struct thresh_struct {
+ s32 data_address;
+ s32 threshold;
+ s32 bit_pattern;
+};
+
+/* LE_STRUCT */
+
+/* Layout of a load enveloped packet. Four thresholds are showed ... for more
+ * see manual (pag.25)
+ * 1. latch_bits is a bit pattern that show which bits the user wants to latch.
+ * The latched bits will not be reset once the threshold which set them is
+ * no longer true. In that case the user must reset them using the reset_bit
+ * command.
+ * 2. number_of_xx_thresholds specify how many GE/LE threshold there are.
+ */
+struct le_struct {
+ s32 latch_bits;
+ s32 number_of_ge_thresholds;
+ s32 number_of_le_thresholds;
+ struct thresh_struct thresholds[4];
+ s32 reserved;
+};
+
+/* LINK_TYPES */
+/* Link types is an enumerated value showing the different possible transform
+ * link types.
+ * 0 - end transform packet
+ * 1 - translate along X axis (TX)
+ * 2 - translate along Y axis (TY)
+ * 3 - translate along Z axis (TZ)
+ * 4 - rotate about X axis (RX)
+ * 5 - rotate about Y axis (RY)
+ * 6 - rotate about Z axis (RZ)
+ * 7 - negate all axes (NEG)
+ */
+
+enum link_types {
+ end_x_form,
+ tx,
+ ty,
+ tz,
+ rx,
+ ry,
+ rz,
+ neg
+};
+
+/* TRANSFORM */
+/* Structure used to describe a transform. */
+struct intern_transform {
+ struct {
+ u32 link_type;
+ s32 link_amount;
+ } link[8];
+};
+
+/* JR3 force/torque sensor data definition. For more information see sensor
+ * and hardware manuals.
+ */
+
+struct jr3_channel {
+ /* Raw_channels is the area used to store the raw data coming from */
+ /* the sensor. */
+
+ struct raw_channel raw_channels[16]; /* offset 0x0000 */
+
+ /* Copyright is a null terminated ASCII string containing the JR3 */
+ /* copyright notice. */
+
+ u32 copyright[0x0018]; /* offset 0x0040 */
+ s32 reserved1[0x0008]; /* offset 0x0058 */
+
+ /* Shunts contains the sensor shunt readings. Some JR3 sensors have
+ * the ability to have their gains adjusted. This allows the
+ * hardware full scales to be adjusted to potentially allow
+ * better resolution or dynamic range. For sensors that have
+ * this ability, the gain of each sensor channel is measured at
+ * the time of calibration using a shunt resistor. The shunt
+ * resistor is placed across one arm of the resistor bridge, and
+ * the resulting change in the output of that channel is
+ * measured. This measurement is called the shunt reading, and
+ * is recorded here. If the user has changed the gain of the //
+ * sensor, and made new shunt measurements, those shunt
+ * measurements can be placed here. The JR3 DSP will then scale
+ * the calibration matrix such so that the gains are again
+ * proper for the indicated shunt readings. If shunts is 0, then
+ * the sensor cannot have its gain changed. For details on
+ * changing the sensor gain, and making shunts readings, please
+ * see the sensor manual. To make these values take effect the
+ * user must call either command (5) use transform # (pg. 33) or
+ * command (10) set new full scales (pg. 38).
+ */
+
+ struct six_axis_array shunts; /* offset 0x0060 */
+ s32 reserved2[2]; /* offset 0x0066 */
+
+ /* Default_FS contains the full scale that is used if the user does */
+ /* not set a full scale. */
+
+ struct six_axis_array default_FS; /* offset 0x0068 */
+ s32 reserved3; /* offset 0x006e */
+
+ /* Load_envelope_num is the load envelope number that is currently
+ * in use. This value is set by the user after one of the load
+ * envelopes has been initialized.
+ */
+
+ s32 load_envelope_num; /* offset 0x006f */
+
+ /* Min_full_scale is the recommend minimum full scale. */
+
+ /* These values in conjunction with max_full_scale (pg. 9) helps
+ * determine the appropriate value for setting the full scales. The
+ * software allows the user to set the sensor full scale to an
+ * arbitrary value. But setting the full scales has some hazards. If
+ * the full scale is set too low, the data will saturate
+ * prematurely, and dynamic range will be lost. If the full scale is
+ * set too high, then resolution is lost as the data is shifted to
+ * the right and the least significant bits are lost. Therefore the
+ * maximum full scale is the maximum value at which no resolution is
+ * lost, and the minimum full scale is the value at which the data
+ * will not saturate prematurely. These values are calculated
+ * whenever a new coordinate transformation is calculated. It is
+ * possible for the recommended maximum to be less than the
+ * recommended minimum. This comes about primarily when using
+ * coordinate translations. If this is the case, it means that any
+ * full scale selection will be a compromise between dynamic range
+ * and resolution. It is usually recommended to compromise in favor
+ * of resolution which means that the recommend maximum full scale
+ * should be chosen.
+ *
+ * WARNING: Be sure that the full scale is no less than 0.4% of the
+ * recommended minimum full scale. Full scales below this value will
+ * cause erroneous results.
+ */
+
+ struct six_axis_array min_full_scale; /* offset 0x0070 */
+ s32 reserved4; /* offset 0x0076 */
+
+ /* Transform_num is the transform number that is currently in use.
+ * This value is set by the JR3 DSP after the user has used command
+ * (5) use transform # (pg. 33).
+ */
+
+ s32 transform_num; /* offset 0x0077 */
+
+ /* Max_full_scale is the recommended maximum full scale. See */
+ /* min_full_scale (pg. 9) for more details. */
+
+ struct six_axis_array max_full_scale; /* offset 0x0078 */
+ s32 reserved5; /* offset 0x007e */
+
+ /* Peak_address is the address of the data which will be monitored
+ * by the peak routine. This value is set by the user. The peak
+ * routine will monitor any 8 contiguous addresses for peak values.
+ * (ex. to watch filter3 data for peaks, set this value to 0x00a8).
+ */
+
+ s32 peak_address; /* offset 0x007f */
+
+ /* Full_scale is the sensor full scales which are currently in use.
+ * Decoupled and filtered data is scaled so that +/- 16384 is equal
+ * to the full scales. The engineering units used are indicated by
+ * the units value discussed on page 16. The full scales for Fx, Fy,
+ * Fz, Mx, My and Mz can be written by the user prior to calling
+ * command (10) set new full scales (pg. 38). The full scales for V1
+ * and V2 are set whenever the full scales are changed or when the
+ * axes used to calculate the vectors are changed. The full scale of
+ * V1 and V2 will always be equal to the largest full scale of the
+ * axes used for each vector respectively.
+ */
+
+ struct force_array full_scale; /* offset 0x0080 */
+
+ /* Offsets contains the sensor offsets. These values are subtracted from
+ * the sensor data to obtain the decoupled data. The offsets are set a
+ * few seconds (< 10) after the calibration data has been received.
+ * They are set so that the output data will be zero. These values
+ * can be written as well as read. The JR3 DSP will use the values
+ * written here within 2 ms of being written. To set future
+ * decoupled data to zero, add these values to the current decoupled
+ * data values and place the sum here. The JR3 DSP will change these
+ * values when a new transform is applied. So if the offsets are
+ * such that FX is 5 and all other values are zero, after rotating
+ * about Z by 90 degrees, FY would be 5 and all others would be zero.
+ */
+
+ struct six_axis_array offsets; /* offset 0x0088 */
+
+ /* Offset_num is the number of the offset currently in use. This
+ * value is set by the JR3 DSP after the user has executed the use
+ * offset # command (pg. 34). It can vary between 0 and 15.
+ */
+
+ s32 offset_num; /* offset 0x008e */
+
+ /* Vect_axes is a bit map showing which of the axes are being used
+ * in the vector calculations. This value is set by the JR3 DSP
+ * after the user has executed the set vector axes command (pg. 37).
+ */
+
+ u32 vect_axes; /* offset 0x008f */
+
+ /* Filter0 is the decoupled, unfiltered data from the JR3 sensor.
+ * This data has had the offsets removed.
+ *
+ * These force_arrays hold the filtered data. The decoupled data is
+ * passed through cascaded low pass filters. Each succeeding filter
+ * has a cutoff frequency of 1/4 of the preceding filter. The cutoff
+ * frequency of filter1 is 1/16 of the sample rate from the sensor.
+ * For a typical sensor with a sample rate of 8 kHz, the cutoff
+ * frequency of filter1 would be 500 Hz. The following filters would
+ * cutoff at 125 Hz, 31.25 Hz, 7.813 Hz, 1.953 Hz and 0.4883 Hz.
+ */
+
+ struct force_array filter[7]; /* offset 0x0090,
+ offset 0x0098,
+ offset 0x00a0,
+ offset 0x00a8,
+ offset 0x00b0,
+ offset 0x00b8 ,
+ offset 0x00c0 */
+
+ /* Rate_data is the calculated rate data. It is a first derivative
+ * calculation. It is calculated at a frequency specified by the
+ * variable rate_divisor (pg. 12). The data on which the rate is
+ * calculated is specified by the variable rate_address (pg. 12).
+ */
+
+ struct force_array rate_data; /* offset 0x00c8 */
+
+ /* Minimum_data & maximum_data are the minimum and maximum (peak)
+ * data values. The JR3 DSP can monitor any 8 contiguous data items
+ * for minimums and maximums at full sensor bandwidth. This area is
+ * only updated at user request. This is done so that the user does
+ * not miss any peaks. To read the data, use either the read peaks
+ * command (pg. 40), or the read and reset peaks command (pg. 39).
+ * The address of the data to watch for peaks is stored in the
+ * variable peak_address (pg. 10). Peak data is lost when executing
+ * a coordinate transformation or a full scale change. Peak data is
+ * also lost when plugging in a new sensor.
+ */
+
+ struct force_array minimum_data; /* offset 0x00d0 */
+ struct force_array maximum_data; /* offset 0x00d8 */
+
+ /* Near_sat_value & sat_value contain the value used to determine if
+ * the raw sensor is saturated. Because of decoupling and offset
+ * removal, it is difficult to tell from the processed data if the
+ * sensor is saturated. These values, in conjunction with the error
+ * and warning words (pg. 14), provide this critical information.
+ * These two values may be set by the host processor. These values
+ * are positive signed values, since the saturation logic uses the
+ * absolute values of the raw data. The near_sat_value defaults to
+ * approximately 80% of the ADC's full scale, which is 26214, while
+ * sat_value defaults to the ADC's full scale:
+ *
+ * sat_value = 32768 - 2^(16 - ADC bits)
+ */
+
+ s32 near_sat_value; /* offset 0x00e0 */
+ s32 sat_value; /* offset 0x00e1 */
+
+ /* Rate_address, rate_divisor & rate_count contain the data used to
+ * control the calculations of the rates. Rate_address is the
+ * address of the data used for the rate calculation. The JR3 DSP
+ * will calculate rates for any 8 contiguous values (ex. to
+ * calculate rates for filter3 data set rate_address to 0x00a8).
+ * Rate_divisor is how often the rate is calculated. If rate_divisor
+ * is 1, the rates are calculated at full sensor bandwidth. If
+ * rate_divisor is 200, rates are calculated every 200 samples.
+ * Rate_divisor can be any value between 1 and 65536. Set
+ * rate_divisor to 0 to calculate rates every 65536 samples.
+ * Rate_count starts at zero and counts until it equals
+ * rate_divisor, at which point the rates are calculated, and
+ * rate_count is reset to 0. When setting a new rate divisor, it is
+ * a good idea to set rate_count to one less than rate divisor. This
+ * will minimize the time necessary to start the rate calculations.
+ */
+
+ s32 rate_address; /* offset 0x00e2 */
+ u32 rate_divisor; /* offset 0x00e3 */
+ u32 rate_count; /* offset 0x00e4 */
+
+ /* Command_word2 through command_word0 are the locations used to
+ * send commands to the JR3 DSP. Their usage varies with the command
+ * and is detailed later in the Command Definitions section (pg.
+ * 29). In general the user places values into various memory
+ * locations, and then places the command word into command_word0.
+ * The JR3 DSP will process the command and place a 0 into
+ * command_word0 to indicate successful completion. Alternatively
+ * the JR3 DSP will place a negative number into command_word0 to
+ * indicate an error condition. Please note the command locations
+ * are numbered backwards. (I.E. command_word2 comes before
+ * command_word1).
+ */
+
+ s32 command_word2; /* offset 0x00e5 */
+ s32 command_word1; /* offset 0x00e6 */
+ s32 command_word0; /* offset 0x00e7 */
+
+ /* Count1 through count6 are unsigned counters which are incremented
+ * every time the matching filters are calculated. Filter1 is
+ * calculated at the sensor data bandwidth. So this counter would
+ * increment at 8 kHz for a typical sensor. The rest of the counters
+ * are incremented at 1/4 the interval of the counter immediately
+ * preceding it, so they would count at 2 kHz, 500 Hz, 125 Hz etc.
+ * These counters can be used to wait for data. Each time the
+ * counter changes, the corresponding data set can be sampled, and
+ * this will insure that the user gets each sample, once, and only
+ * once.
+ */
+
+ u32 count1; /* offset 0x00e8 */
+ u32 count2; /* offset 0x00e9 */
+ u32 count3; /* offset 0x00ea */
+ u32 count4; /* offset 0x00eb */
+ u32 count5; /* offset 0x00ec */
+ u32 count6; /* offset 0x00ed */
+
+ /* Error_count is a running count of data reception errors. If this
+ * counter is changing rapidly, it probably indicates a bad sensor
+ * cable connection or other hardware problem. In most installations
+ * error_count should not change at all. But it is possible in an
+ * extremely noisy environment to experience occasional errors even
+ * without a hardware problem. If the sensor is well grounded, this
+ * is probably unavoidable in these environments. On the occasions
+ * where this counter counts a bad sample, that sample is ignored.
+ */
+
+ u32 error_count; /* offset 0x00ee */
+
+ /* Count_x is a counter which is incremented every time the JR3 DSP
+ * searches its job queues and finds nothing to do. It indicates the
+ * amount of idle time the JR3 DSP has available. It can also be
+ * used to determine if the JR3 DSP is alive. See the Performance
+ * Issues section on pg. 49 for more details.
+ */
+
+ u32 count_x; /* offset 0x00ef */
+
+ /* Warnings & errors contain the warning and error bits
+ * respectively. The format of these two words is discussed on page
+ * 21 under the headings warnings_bits and error_bits.
+ */
+
+ u32 warnings; /* offset 0x00f0 */
+ u32 errors; /* offset 0x00f1 */
+
+ /* Threshold_bits is a word containing the bits that are set by the
+ * load envelopes. See load_envelopes (pg. 17) and thresh_struct
+ * (pg. 23) for more details.
+ */
+
+ s32 threshold_bits; /* offset 0x00f2 */
+
+ /* Last_crc is the value that shows the actual calculated CRC. CRC
+ * is short for cyclic redundancy code. It should be zero. See the
+ * description for cal_crc_bad (pg. 21) for more information.
+ */
+
+ s32 last_CRC; /* offset 0x00f3 */
+
+ /* EEProm_ver_no contains the version number of the sensor EEProm.
+ * EEProm version numbers can vary between 0 and 255.
+ * Software_ver_no contains the software version number. Version
+ * 3.02 would be stored as 302.
+ */
+
+ s32 eeprom_ver_no; /* offset 0x00f4 */
+ s32 software_ver_no; /* offset 0x00f5 */
+
+ /* Software_day & software_year are the release date of the software
+ * the JR3 DSP is currently running. Day is the day of the year,
+ * with January 1 being 1, and December 31, being 365 for non leap
+ * years.
+ */
+
+ s32 software_day; /* offset 0x00f6 */
+ s32 software_year; /* offset 0x00f7 */
+
+ /* Serial_no & model_no are the two values which uniquely identify a
+ * sensor. This model number does not directly correspond to the JR3
+ * model number, but it will provide a unique identifier for
+ * different sensor configurations.
+ */
+
+ u32 serial_no; /* offset 0x00f8 */
+ u32 model_no; /* offset 0x00f9 */
+
+ /* Cal_day & cal_year are the sensor calibration date. Day is the
+ * day of the year, with January 1 being 1, and December 31, being
+ * 366 for leap years.
+ */
+
+ s32 cal_day; /* offset 0x00fa */
+ s32 cal_year; /* offset 0x00fb */
+
+ /* Units is an enumerated read only value defining the engineering
+ * units used in the sensor full scale. The meanings of particular
+ * values are discussed in the section detailing the force_units
+ * structure on page 22. The engineering units are setto customer
+ * specifications during sensor manufacture and cannot be changed by
+ * writing to Units.
+ *
+ * Bits contains the number of bits of resolution of the ADC
+ * currently in use.
+ *
+ * Channels is a bit field showing which channels the current sensor
+ * is capable of sending. If bit 0 is active, this sensor can send
+ * channel 0, if bit 13 is active, this sensor can send channel 13,
+ * etc. This bit can be active, even if the sensor is not currently
+ * sending this channel. Some sensors are configurable as to which
+ * channels to send, and this field only contains information on the
+ * channels available to send, not on the current configuration. To
+ * find which channels are currently being sent, monitor the
+ * Raw_time fields (pg. 19) in the raw_channels array (pg. 7). If
+ * the time is changing periodically, then that channel is being
+ * received.
+ */
+
+ u32 units; /* offset 0x00fc */
+ s32 bits; /* offset 0x00fd */
+ s32 channels; /* offset 0x00fe */
+
+ /* Thickness specifies the overall thickness of the sensor from
+ * flange to flange. The engineering units for this value are
+ * contained in units (pg. 16). The sensor calibration is relative
+ * to the center of the sensor. This value allows easy coordinate
+ * transformation from the center of the sensor to either flange.
+ */
+
+ s32 thickness; /* offset 0x00ff */
+
+ /* Load_envelopes is a table containing the load envelope
+ * descriptions. There are 16 possible load envelope slots in the
+ * table. The slots are on 16 word boundaries and are numbered 0-15.
+ * Each load envelope needs to start at the beginning of a slot but
+ * need not be fully contained in that slot. That is to say that a
+ * single load envelope can be larger than a single slot. The
+ * software has been tested and ran satisfactorily with 50
+ * thresholds active. A single load envelope this large would take
+ * up 5 of the 16 slots. The load envelope data is laid out in an
+ * order that is most efficient for the JR3 DSP. The structure is
+ * detailed later in the section showing the definition of the
+ * le_struct structure (pg. 23).
+ */
+
+ struct le_struct load_envelopes[0x10]; /* offset 0x0100 */
+
+ /* Transforms is a table containing the transform descriptions.
+ * There are 16 possible transform slots in the table. The slots are
+ * on 16 word boundaries and are numbered 0-15. Each transform needs
+ * to start at the beginning of a slot but need not be fully
+ * contained in that slot. That is to say that a single transform
+ * can be larger than a single slot. A transform is 2 * no of links
+ * + 1 words in length. So a single slot can contain a transform
+ * with 7 links. Two slots can contain a transform that is 15 links.
+ * The layout is detailed later in the section showing the
+ * definition of the transform structure (pg. 26).
+ */
+
+ struct intern_transform transforms[0x10]; /* offset 0x0200 */
+};
+
+struct jr3_t {
+ struct {
+ u32 program_lo[0x4000]; /* 0x00000 - 0x10000 */
+ struct jr3_channel data; /* 0x10000 - 0x10c00 */
+ char pad2[0x30000 - 0x00c00]; /* 0x10c00 - 0x40000 */
+ u32 program_hi[0x8000]; /* 0x40000 - 0x60000 */
+ u32 reset; /* 0x60000 - 0x60004 */
+ char pad3[0x20000 - 0x00004]; /* 0x60004 - 0x80000 */
+ } channel[4];
+};
diff --git a/drivers/staging/comedi/drivers/ke_counter.c b/drivers/staging/comedi/drivers/ke_counter.c
new file mode 100644
index 000000000..dc642edf4
--- /dev/null
+++ b/drivers/staging/comedi/drivers/ke_counter.c
@@ -0,0 +1,240 @@
+/*
+ * ke_counter.c
+ * Comedi driver for Kolter-Electronic PCI Counter 1 Card
+ *
+ * COMEDI - Linux Control and Measurement Device Interface
+ * Copyright (C) 2000 David A. Schleef <ds@schleef.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+/*
+ * Driver: ke_counter
+ * Description: Driver for Kolter Electronic Counter Card
+ * Devices: [Kolter Electronic] PCI Counter Card (ke_counter)
+ * Author: Michael Hillmann
+ * Updated: Mon, 14 Apr 2008 15:42:42 +0100
+ * Status: tested
+ *
+ * Configuration Options: not applicable, uses PCI auto config
+ */
+
+#include <linux/module.h>
+
+#include "../comedi_pci.h"
+
+/*
+ * PCI BAR 0 Register I/O map
+ */
+#define KE_RESET_REG(x) (0x00 + ((x) * 0x20))
+#define KE_LATCH_REG(x) (0x00 + ((x) * 0x20))
+#define KE_LSB_REG(x) (0x04 + ((x) * 0x20))
+#define KE_MID_REG(x) (0x08 + ((x) * 0x20))
+#define KE_MSB_REG(x) (0x0c + ((x) * 0x20))
+#define KE_SIGN_REG(x) (0x10 + ((x) * 0x20))
+#define KE_OSC_SEL_REG 0xf8
+#define KE_OSC_SEL_EXT (1 << 0)
+#define KE_OSC_SEL_4MHZ (2 << 0)
+#define KE_OSC_SEL_20MHZ (3 << 0)
+#define KE_DO_REG 0xfc
+
+static int ke_counter_insn_write(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ unsigned int chan = CR_CHAN(insn->chanspec);
+ unsigned int val;
+ int i;
+
+ for (i = 0; i < insn->n; i++) {
+ val = data[0];
+
+ /* Order matters */
+ outb((val >> 24) & 0xff, dev->iobase + KE_SIGN_REG(chan));
+ outb((val >> 16) & 0xff, dev->iobase + KE_MSB_REG(chan));
+ outb((val >> 8) & 0xff, dev->iobase + KE_MID_REG(chan));
+ outb((val >> 0) & 0xff, dev->iobase + KE_LSB_REG(chan));
+ }
+
+ return insn->n;
+}
+
+static int ke_counter_insn_read(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ unsigned int chan = CR_CHAN(insn->chanspec);
+ unsigned int val;
+ int i;
+
+ for (i = 0; i < insn->n; i++) {
+ /* Order matters */
+ inb(dev->iobase + KE_LATCH_REG(chan));
+
+ val = inb(dev->iobase + KE_LSB_REG(chan));
+ val |= (inb(dev->iobase + KE_MID_REG(chan)) << 8);
+ val |= (inb(dev->iobase + KE_MSB_REG(chan)) << 16);
+ val |= (inb(dev->iobase + KE_SIGN_REG(chan)) << 24);
+
+ data[i] = val;
+ }
+
+ return insn->n;
+}
+
+static void ke_counter_reset(struct comedi_device *dev)
+{
+ unsigned int chan;
+
+ for (chan = 0; chan < 3; chan++)
+ outb(0, dev->iobase + KE_RESET_REG(chan));
+}
+
+static int ke_counter_insn_config(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ unsigned char src;
+
+ switch (data[0]) {
+ case INSN_CONFIG_SET_CLOCK_SRC:
+ switch (data[1]) {
+ case KE_CLK_20MHZ: /* default */
+ src = KE_OSC_SEL_20MHZ;
+ break;
+ case KE_CLK_4MHZ: /* option */
+ src = KE_OSC_SEL_4MHZ;
+ break;
+ case KE_CLK_EXT: /* Pin 21 on D-sub */
+ src = KE_OSC_SEL_EXT;
+ break;
+ default:
+ return -EINVAL;
+ }
+ outb(src, dev->iobase + KE_OSC_SEL_REG);
+ break;
+ case INSN_CONFIG_GET_CLOCK_SRC:
+ src = inb(dev->iobase + KE_OSC_SEL_REG);
+ switch (src) {
+ case KE_OSC_SEL_20MHZ:
+ data[1] = KE_CLK_20MHZ;
+ data[2] = 50; /* 50ns */
+ break;
+ case KE_OSC_SEL_4MHZ:
+ data[1] = KE_CLK_4MHZ;
+ data[2] = 250; /* 250ns */
+ break;
+ case KE_OSC_SEL_EXT:
+ data[1] = KE_CLK_EXT;
+ data[2] = 0; /* Unknown */
+ break;
+ default:
+ return -EINVAL;
+ }
+ break;
+ case INSN_CONFIG_RESET:
+ ke_counter_reset(dev);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return insn->n;
+}
+
+static int ke_counter_do_insn_bits(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ if (comedi_dio_update_state(s, data))
+ outb(s->state, dev->iobase + KE_DO_REG);
+
+ data[1] = s->state;
+
+ return insn->n;
+}
+
+static int ke_counter_auto_attach(struct comedi_device *dev,
+ unsigned long context_unused)
+{
+ struct pci_dev *pcidev = comedi_to_pci_dev(dev);
+ struct comedi_subdevice *s;
+ int ret;
+
+ ret = comedi_pci_enable(dev);
+ if (ret)
+ return ret;
+ dev->iobase = pci_resource_start(pcidev, 0);
+
+ ret = comedi_alloc_subdevices(dev, 2);
+ if (ret)
+ return ret;
+
+ s = &dev->subdevices[0];
+ s->type = COMEDI_SUBD_COUNTER;
+ s->subdev_flags = SDF_READABLE;
+ s->n_chan = 3;
+ s->maxdata = 0x01ffffff;
+ s->range_table = &range_unknown;
+ s->insn_read = ke_counter_insn_read;
+ s->insn_write = ke_counter_insn_write;
+ s->insn_config = ke_counter_insn_config;
+
+ s = &dev->subdevices[1];
+ s->type = COMEDI_SUBD_DO;
+ s->subdev_flags = SDF_WRITABLE;
+ s->n_chan = 3;
+ s->maxdata = 1;
+ s->range_table = &range_digital;
+ s->insn_bits = ke_counter_do_insn_bits;
+
+ outb(KE_OSC_SEL_20MHZ, dev->iobase + KE_OSC_SEL_REG);
+
+ ke_counter_reset(dev);
+
+ return 0;
+}
+
+static struct comedi_driver ke_counter_driver = {
+ .driver_name = "ke_counter",
+ .module = THIS_MODULE,
+ .auto_attach = ke_counter_auto_attach,
+ .detach = comedi_pci_detach,
+};
+
+static int ke_counter_pci_probe(struct pci_dev *dev,
+ const struct pci_device_id *id)
+{
+ return comedi_pci_auto_config(dev, &ke_counter_driver,
+ id->driver_data);
+}
+
+static const struct pci_device_id ke_counter_pci_table[] = {
+ { PCI_DEVICE(PCI_VENDOR_ID_KOLTER, 0x0014) },
+ { 0 }
+};
+MODULE_DEVICE_TABLE(pci, ke_counter_pci_table);
+
+static struct pci_driver ke_counter_pci_driver = {
+ .name = "ke_counter",
+ .id_table = ke_counter_pci_table,
+ .probe = ke_counter_pci_probe,
+ .remove = comedi_pci_auto_unconfig,
+};
+module_comedi_pci_driver(ke_counter_driver, ke_counter_pci_driver);
+
+MODULE_AUTHOR("Comedi http://www.comedi.org");
+MODULE_DESCRIPTION("Comedi driver for Kolter Electronic Counter Card");
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/comedi/drivers/me4000.c b/drivers/staging/comedi/drivers/me4000.c
new file mode 100644
index 000000000..0423cf875
--- /dev/null
+++ b/drivers/staging/comedi/drivers/me4000.c
@@ -0,0 +1,1443 @@
+/*
+ comedi/drivers/me4000.c
+ Source code for the Meilhaus ME-4000 board family.
+
+ COMEDI - Linux Control and Measurement Device Interface
+ Copyright (C) 2000 David A. Schleef <ds@schleef.org>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+ */
+/*
+Driver: me4000
+Description: Meilhaus ME-4000 series boards
+Devices: [Meilhaus] ME-4650 (me4000), ME-4670i, ME-4680, ME-4680i, ME-4680is
+Author: gg (Guenter Gebhardt <g.gebhardt@meilhaus.com>)
+Updated: Mon, 18 Mar 2002 15:34:01 -0800
+Status: broken (no support for loading firmware)
+
+Supports:
+
+ - Analog Input
+ - Analog Output
+ - Digital I/O
+ - Counter
+
+Configuration Options: not applicable, uses PCI auto config
+
+The firmware required by these boards is available in the
+comedi_nonfree_firmware tarball available from
+http://www.comedi.org. However, the driver's support for
+loading the firmware through comedi_config is currently
+broken.
+
+ */
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+
+#include "../comedi_pci.h"
+
+#include "comedi_8254.h"
+#include "plx9052.h"
+
+#define ME4000_FIRMWARE "/*(DEBLOBBED)*/"
+
+/*
+ * ME4000 Register map and bit defines
+ */
+#define ME4000_AO_CHAN(x) ((x) * 0x18)
+
+#define ME4000_AO_CTRL_REG(x) (0x00 + ME4000_AO_CHAN(x))
+#define ME4000_AO_CTRL_BIT_MODE_0 (1 << 0)
+#define ME4000_AO_CTRL_BIT_MODE_1 (1 << 1)
+#define ME4000_AO_CTRL_MASK_MODE (3 << 0)
+#define ME4000_AO_CTRL_BIT_STOP (1 << 2)
+#define ME4000_AO_CTRL_BIT_ENABLE_FIFO (1 << 3)
+#define ME4000_AO_CTRL_BIT_ENABLE_EX_TRIG (1 << 4)
+#define ME4000_AO_CTRL_BIT_EX_TRIG_EDGE (1 << 5)
+#define ME4000_AO_CTRL_BIT_IMMEDIATE_STOP (1 << 7)
+#define ME4000_AO_CTRL_BIT_ENABLE_DO (1 << 8)
+#define ME4000_AO_CTRL_BIT_ENABLE_IRQ (1 << 9)
+#define ME4000_AO_CTRL_BIT_RESET_IRQ (1 << 10)
+#define ME4000_AO_STATUS_REG(x) (0x04 + ME4000_AO_CHAN(x))
+#define ME4000_AO_STATUS_BIT_FSM (1 << 0)
+#define ME4000_AO_STATUS_BIT_FF (1 << 1)
+#define ME4000_AO_STATUS_BIT_HF (1 << 2)
+#define ME4000_AO_STATUS_BIT_EF (1 << 3)
+#define ME4000_AO_FIFO_REG(x) (0x08 + ME4000_AO_CHAN(x))
+#define ME4000_AO_SINGLE_REG(x) (0x0c + ME4000_AO_CHAN(x))
+#define ME4000_AO_TIMER_REG(x) (0x10 + ME4000_AO_CHAN(x))
+#define ME4000_AI_CTRL_REG 0x74
+#define ME4000_AI_STATUS_REG 0x74
+#define ME4000_AI_CTRL_BIT_MODE_0 (1 << 0)
+#define ME4000_AI_CTRL_BIT_MODE_1 (1 << 1)
+#define ME4000_AI_CTRL_BIT_MODE_2 (1 << 2)
+#define ME4000_AI_CTRL_BIT_SAMPLE_HOLD (1 << 3)
+#define ME4000_AI_CTRL_BIT_IMMEDIATE_STOP (1 << 4)
+#define ME4000_AI_CTRL_BIT_STOP (1 << 5)
+#define ME4000_AI_CTRL_BIT_CHANNEL_FIFO (1 << 6)
+#define ME4000_AI_CTRL_BIT_DATA_FIFO (1 << 7)
+#define ME4000_AI_CTRL_BIT_FULLSCALE (1 << 8)
+#define ME4000_AI_CTRL_BIT_OFFSET (1 << 9)
+#define ME4000_AI_CTRL_BIT_EX_TRIG_ANALOG (1 << 10)
+#define ME4000_AI_CTRL_BIT_EX_TRIG (1 << 11)
+#define ME4000_AI_CTRL_BIT_EX_TRIG_FALLING (1 << 12)
+#define ME4000_AI_CTRL_BIT_EX_IRQ (1 << 13)
+#define ME4000_AI_CTRL_BIT_EX_IRQ_RESET (1 << 14)
+#define ME4000_AI_CTRL_BIT_LE_IRQ (1 << 15)
+#define ME4000_AI_CTRL_BIT_LE_IRQ_RESET (1 << 16)
+#define ME4000_AI_CTRL_BIT_HF_IRQ (1 << 17)
+#define ME4000_AI_CTRL_BIT_HF_IRQ_RESET (1 << 18)
+#define ME4000_AI_CTRL_BIT_SC_IRQ (1 << 19)
+#define ME4000_AI_CTRL_BIT_SC_IRQ_RESET (1 << 20)
+#define ME4000_AI_CTRL_BIT_SC_RELOAD (1 << 21)
+#define ME4000_AI_STATUS_BIT_EF_CHANNEL (1 << 22)
+#define ME4000_AI_STATUS_BIT_HF_CHANNEL (1 << 23)
+#define ME4000_AI_STATUS_BIT_FF_CHANNEL (1 << 24)
+#define ME4000_AI_STATUS_BIT_EF_DATA (1 << 25)
+#define ME4000_AI_STATUS_BIT_HF_DATA (1 << 26)
+#define ME4000_AI_STATUS_BIT_FF_DATA (1 << 27)
+#define ME4000_AI_STATUS_BIT_LE (1 << 28)
+#define ME4000_AI_STATUS_BIT_FSM (1 << 29)
+#define ME4000_AI_CTRL_BIT_EX_TRIG_BOTH (1 << 31)
+#define ME4000_AI_CHANNEL_LIST_REG 0x78
+#define ME4000_AI_LIST_INPUT_SINGLE_ENDED (0 << 5)
+#define ME4000_AI_LIST_INPUT_DIFFERENTIAL (1 << 5)
+#define ME4000_AI_LIST_RANGE_BIPOLAR_10 (0 << 6)
+#define ME4000_AI_LIST_RANGE_BIPOLAR_2_5 (1 << 6)
+#define ME4000_AI_LIST_RANGE_UNIPOLAR_10 (2 << 6)
+#define ME4000_AI_LIST_RANGE_UNIPOLAR_2_5 (3 << 6)
+#define ME4000_AI_LIST_LAST_ENTRY (1 << 8)
+#define ME4000_AI_DATA_REG 0x7c
+#define ME4000_AI_CHAN_TIMER_REG 0x80
+#define ME4000_AI_CHAN_PRE_TIMER_REG 0x84
+#define ME4000_AI_SCAN_TIMER_LOW_REG 0x88
+#define ME4000_AI_SCAN_TIMER_HIGH_REG 0x8c
+#define ME4000_AI_SCAN_PRE_TIMER_LOW_REG 0x90
+#define ME4000_AI_SCAN_PRE_TIMER_HIGH_REG 0x94
+#define ME4000_AI_START_REG 0x98
+#define ME4000_IRQ_STATUS_REG 0x9c
+#define ME4000_IRQ_STATUS_BIT_EX (1 << 0)
+#define ME4000_IRQ_STATUS_BIT_LE (1 << 1)
+#define ME4000_IRQ_STATUS_BIT_AI_HF (1 << 2)
+#define ME4000_IRQ_STATUS_BIT_AO_0_HF (1 << 3)
+#define ME4000_IRQ_STATUS_BIT_AO_1_HF (1 << 4)
+#define ME4000_IRQ_STATUS_BIT_AO_2_HF (1 << 5)
+#define ME4000_IRQ_STATUS_BIT_AO_3_HF (1 << 6)
+#define ME4000_IRQ_STATUS_BIT_SC (1 << 7)
+#define ME4000_DIO_PORT_0_REG 0xa0
+#define ME4000_DIO_PORT_1_REG 0xa4
+#define ME4000_DIO_PORT_2_REG 0xa8
+#define ME4000_DIO_PORT_3_REG 0xac
+#define ME4000_DIO_DIR_REG 0xb0
+#define ME4000_AO_LOADSETREG_XX 0xb4
+#define ME4000_DIO_CTRL_REG 0xb8
+#define ME4000_DIO_CTRL_BIT_MODE_0 (1 << 0)
+#define ME4000_DIO_CTRL_BIT_MODE_1 (1 << 1)
+#define ME4000_DIO_CTRL_BIT_MODE_2 (1 << 2)
+#define ME4000_DIO_CTRL_BIT_MODE_3 (1 << 3)
+#define ME4000_DIO_CTRL_BIT_MODE_4 (1 << 4)
+#define ME4000_DIO_CTRL_BIT_MODE_5 (1 << 5)
+#define ME4000_DIO_CTRL_BIT_MODE_6 (1 << 6)
+#define ME4000_DIO_CTRL_BIT_MODE_7 (1 << 7)
+#define ME4000_DIO_CTRL_BIT_FUNCTION_0 (1 << 8)
+#define ME4000_DIO_CTRL_BIT_FUNCTION_1 (1 << 9)
+#define ME4000_DIO_CTRL_BIT_FIFO_HIGH_0 (1 << 10)
+#define ME4000_DIO_CTRL_BIT_FIFO_HIGH_1 (1 << 11)
+#define ME4000_DIO_CTRL_BIT_FIFO_HIGH_2 (1 << 12)
+#define ME4000_DIO_CTRL_BIT_FIFO_HIGH_3 (1 << 13)
+#define ME4000_AO_DEMUX_ADJUST_REG 0xbc
+#define ME4000_AO_DEMUX_ADJUST_VALUE 0x4c
+#define ME4000_AI_SAMPLE_COUNTER_REG 0xc0
+
+#define ME4000_AI_FIFO_COUNT 2048
+
+#define ME4000_AI_MIN_TICKS 66
+#define ME4000_AI_MIN_SAMPLE_TIME 2000
+
+#define ME4000_AI_CHANNEL_LIST_COUNT 1024
+
+struct me4000_info {
+ unsigned long plx_regbase;
+};
+
+enum me4000_boardid {
+ BOARD_ME4650,
+ BOARD_ME4660,
+ BOARD_ME4660I,
+ BOARD_ME4660S,
+ BOARD_ME4660IS,
+ BOARD_ME4670,
+ BOARD_ME4670I,
+ BOARD_ME4670S,
+ BOARD_ME4670IS,
+ BOARD_ME4680,
+ BOARD_ME4680I,
+ BOARD_ME4680S,
+ BOARD_ME4680IS,
+};
+
+struct me4000_board {
+ const char *name;
+ int ao_nchan;
+ int ao_fifo;
+ int ai_nchan;
+ int ai_diff_nchan;
+ int ai_sh_nchan;
+ int ex_trig_analog;
+ int dio_nchan;
+ int has_counter;
+};
+
+static const struct me4000_board me4000_boards[] = {
+ [BOARD_ME4650] = {
+ .name = "ME-4650",
+ .ai_nchan = 16,
+ .dio_nchan = 32,
+ },
+ [BOARD_ME4660] = {
+ .name = "ME-4660",
+ .ai_nchan = 32,
+ .ai_diff_nchan = 16,
+ .dio_nchan = 32,
+ .has_counter = 1,
+ },
+ [BOARD_ME4660I] = {
+ .name = "ME-4660i",
+ .ai_nchan = 32,
+ .ai_diff_nchan = 16,
+ .dio_nchan = 32,
+ .has_counter = 1,
+ },
+ [BOARD_ME4660S] = {
+ .name = "ME-4660s",
+ .ai_nchan = 32,
+ .ai_diff_nchan = 16,
+ .ai_sh_nchan = 8,
+ .dio_nchan = 32,
+ .has_counter = 1,
+ },
+ [BOARD_ME4660IS] = {
+ .name = "ME-4660is",
+ .ai_nchan = 32,
+ .ai_diff_nchan = 16,
+ .ai_sh_nchan = 8,
+ .dio_nchan = 32,
+ .has_counter = 1,
+ },
+ [BOARD_ME4670] = {
+ .name = "ME-4670",
+ .ao_nchan = 4,
+ .ai_nchan = 32,
+ .ai_diff_nchan = 16,
+ .ex_trig_analog = 1,
+ .dio_nchan = 32,
+ .has_counter = 1,
+ },
+ [BOARD_ME4670I] = {
+ .name = "ME-4670i",
+ .ao_nchan = 4,
+ .ai_nchan = 32,
+ .ai_diff_nchan = 16,
+ .ex_trig_analog = 1,
+ .dio_nchan = 32,
+ .has_counter = 1,
+ },
+ [BOARD_ME4670S] = {
+ .name = "ME-4670s",
+ .ao_nchan = 4,
+ .ai_nchan = 32,
+ .ai_diff_nchan = 16,
+ .ai_sh_nchan = 8,
+ .ex_trig_analog = 1,
+ .dio_nchan = 32,
+ .has_counter = 1,
+ },
+ [BOARD_ME4670IS] = {
+ .name = "ME-4670is",
+ .ao_nchan = 4,
+ .ai_nchan = 32,
+ .ai_diff_nchan = 16,
+ .ai_sh_nchan = 8,
+ .ex_trig_analog = 1,
+ .dio_nchan = 32,
+ .has_counter = 1,
+ },
+ [BOARD_ME4680] = {
+ .name = "ME-4680",
+ .ao_nchan = 4,
+ .ao_fifo = 4,
+ .ai_nchan = 32,
+ .ai_diff_nchan = 16,
+ .ex_trig_analog = 1,
+ .dio_nchan = 32,
+ .has_counter = 1,
+ },
+ [BOARD_ME4680I] = {
+ .name = "ME-4680i",
+ .ao_nchan = 4,
+ .ao_fifo = 4,
+ .ai_nchan = 32,
+ .ai_diff_nchan = 16,
+ .ex_trig_analog = 1,
+ .dio_nchan = 32,
+ .has_counter = 1,
+ },
+ [BOARD_ME4680S] = {
+ .name = "ME-4680s",
+ .ao_nchan = 4,
+ .ao_fifo = 4,
+ .ai_nchan = 32,
+ .ai_diff_nchan = 16,
+ .ai_sh_nchan = 8,
+ .ex_trig_analog = 1,
+ .dio_nchan = 32,
+ .has_counter = 1,
+ },
+ [BOARD_ME4680IS] = {
+ .name = "ME-4680is",
+ .ao_nchan = 4,
+ .ao_fifo = 4,
+ .ai_nchan = 32,
+ .ai_diff_nchan = 16,
+ .ai_sh_nchan = 8,
+ .ex_trig_analog = 1,
+ .dio_nchan = 32,
+ .has_counter = 1,
+ },
+};
+
+static const struct comedi_lrange me4000_ai_range = {
+ 4, {
+ UNI_RANGE(2.5),
+ UNI_RANGE(10),
+ BIP_RANGE(2.5),
+ BIP_RANGE(10)
+ }
+};
+
+static int me4000_xilinx_download(struct comedi_device *dev,
+ const u8 *data, size_t size,
+ unsigned long context)
+{
+ struct pci_dev *pcidev = comedi_to_pci_dev(dev);
+ struct me4000_info *info = dev->private;
+ unsigned long xilinx_iobase = pci_resource_start(pcidev, 5);
+ unsigned int file_length;
+ unsigned int val;
+ unsigned int i;
+
+ if (!xilinx_iobase)
+ return -ENODEV;
+
+ /*
+ * Set PLX local interrupt 2 polarity to high.
+ * Interrupt is thrown by init pin of xilinx.
+ */
+ outl(PLX9052_INTCSR_LI2POL, info->plx_regbase + PLX9052_INTCSR);
+
+ /* Set /CS and /WRITE of the Xilinx */
+ val = inl(info->plx_regbase + PLX9052_CNTRL);
+ val |= PLX9052_CNTRL_UIO2_DATA;
+ outl(val, info->plx_regbase + PLX9052_CNTRL);
+
+ /* Init Xilinx with CS1 */
+ inb(xilinx_iobase + 0xC8);
+
+ /* Wait until /INIT pin is set */
+ udelay(20);
+ val = inl(info->plx_regbase + PLX9052_INTCSR);
+ if (!(val & PLX9052_INTCSR_LI2STAT)) {
+ dev_err(dev->class_dev, "Can't init Xilinx\n");
+ return -EIO;
+ }
+
+ /* Reset /CS and /WRITE of the Xilinx */
+ val = inl(info->plx_regbase + PLX9052_CNTRL);
+ val &= ~PLX9052_CNTRL_UIO2_DATA;
+ outl(val, info->plx_regbase + PLX9052_CNTRL);
+
+ /* Download Xilinx firmware */
+ file_length = (((unsigned int)data[0] & 0xff) << 24) +
+ (((unsigned int)data[1] & 0xff) << 16) +
+ (((unsigned int)data[2] & 0xff) << 8) +
+ ((unsigned int)data[3] & 0xff);
+ udelay(10);
+
+ for (i = 0; i < file_length; i++) {
+ outb(data[16 + i], xilinx_iobase);
+ udelay(10);
+
+ /* Check if BUSY flag is low */
+ val = inl(info->plx_regbase + PLX9052_CNTRL);
+ if (val & PLX9052_CNTRL_UIO1_DATA) {
+ dev_err(dev->class_dev,
+ "Xilinx is still busy (i = %d)\n", i);
+ return -EIO;
+ }
+ }
+
+ /* If done flag is high download was successful */
+ val = inl(info->plx_regbase + PLX9052_CNTRL);
+ if (!(val & PLX9052_CNTRL_UIO0_DATA)) {
+ dev_err(dev->class_dev, "DONE flag is not set\n");
+ dev_err(dev->class_dev, "Download not successful\n");
+ return -EIO;
+ }
+
+ /* Set /CS and /WRITE */
+ val = inl(info->plx_regbase + PLX9052_CNTRL);
+ val |= PLX9052_CNTRL_UIO2_DATA;
+ outl(val, info->plx_regbase + PLX9052_CNTRL);
+
+ return 0;
+}
+
+static void me4000_reset(struct comedi_device *dev)
+{
+ struct me4000_info *info = dev->private;
+ unsigned int val;
+ int chan;
+
+ /* Make a hardware reset */
+ val = inl(info->plx_regbase + PLX9052_CNTRL);
+ val |= PLX9052_CNTRL_PCI_RESET;
+ outl(val, info->plx_regbase + PLX9052_CNTRL);
+ val &= ~PLX9052_CNTRL_PCI_RESET;
+ outl(val, info->plx_regbase + PLX9052_CNTRL);
+
+ /* 0x8000 to the DACs means an output voltage of 0V */
+ for (chan = 0; chan < 4; chan++)
+ outl(0x8000, dev->iobase + ME4000_AO_SINGLE_REG(chan));
+
+ /* Set both stop bits in the analog input control register */
+ outl(ME4000_AI_CTRL_BIT_IMMEDIATE_STOP | ME4000_AI_CTRL_BIT_STOP,
+ dev->iobase + ME4000_AI_CTRL_REG);
+
+ /* Set both stop bits in the analog output control register */
+ val = ME4000_AO_CTRL_BIT_IMMEDIATE_STOP | ME4000_AO_CTRL_BIT_STOP;
+ for (chan = 0; chan < 4; chan++)
+ outl(val, dev->iobase + ME4000_AO_CTRL_REG(chan));
+
+ /* Enable interrupts on the PLX */
+ outl(PLX9052_INTCSR_LI1ENAB |
+ PLX9052_INTCSR_LI1POL |
+ PLX9052_INTCSR_PCIENAB, info->plx_regbase + PLX9052_INTCSR);
+
+ /* Set the adustment register for AO demux */
+ outl(ME4000_AO_DEMUX_ADJUST_VALUE,
+ dev->iobase + ME4000_AO_DEMUX_ADJUST_REG);
+
+ /*
+ * Set digital I/O direction for port 0
+ * to output on isolated versions
+ */
+ if (!(inl(dev->iobase + ME4000_DIO_DIR_REG) & 0x1))
+ outl(0x1, dev->iobase + ME4000_DIO_CTRL_REG);
+}
+
+/*=============================================================================
+ Analog input section
+ ===========================================================================*/
+
+static int me4000_ai_insn_read(struct comedi_device *dev,
+ struct comedi_subdevice *subdevice,
+ struct comedi_insn *insn, unsigned int *data)
+{
+ const struct me4000_board *thisboard = dev->board_ptr;
+ int chan = CR_CHAN(insn->chanspec);
+ int rang = CR_RANGE(insn->chanspec);
+ int aref = CR_AREF(insn->chanspec);
+
+ unsigned int entry = 0;
+ unsigned int tmp;
+ unsigned int lval;
+
+ if (insn->n == 0) {
+ return 0;
+ } else if (insn->n > 1) {
+ dev_err(dev->class_dev, "Invalid instruction length %d\n",
+ insn->n);
+ return -EINVAL;
+ }
+
+ switch (rang) {
+ case 0:
+ entry |= ME4000_AI_LIST_RANGE_UNIPOLAR_2_5;
+ break;
+ case 1:
+ entry |= ME4000_AI_LIST_RANGE_UNIPOLAR_10;
+ break;
+ case 2:
+ entry |= ME4000_AI_LIST_RANGE_BIPOLAR_2_5;
+ break;
+ case 3:
+ entry |= ME4000_AI_LIST_RANGE_BIPOLAR_10;
+ break;
+ default:
+ dev_err(dev->class_dev, "Invalid range specified\n");
+ return -EINVAL;
+ }
+
+ switch (aref) {
+ case AREF_GROUND:
+ case AREF_COMMON:
+ if (chan >= thisboard->ai_nchan) {
+ dev_err(dev->class_dev,
+ "Analog input is not available\n");
+ return -EINVAL;
+ }
+ entry |= ME4000_AI_LIST_INPUT_SINGLE_ENDED | chan;
+ break;
+
+ case AREF_DIFF:
+ if (rang == 0 || rang == 1) {
+ dev_err(dev->class_dev,
+ "Range must be bipolar when aref = diff\n");
+ return -EINVAL;
+ }
+
+ if (chan >= thisboard->ai_diff_nchan) {
+ dev_err(dev->class_dev,
+ "Analog input is not available\n");
+ return -EINVAL;
+ }
+ entry |= ME4000_AI_LIST_INPUT_DIFFERENTIAL | chan;
+ break;
+ default:
+ dev_err(dev->class_dev, "Invalid aref specified\n");
+ return -EINVAL;
+ }
+
+ entry |= ME4000_AI_LIST_LAST_ENTRY;
+
+ /* Clear channel list, data fifo and both stop bits */
+ tmp = inl(dev->iobase + ME4000_AI_CTRL_REG);
+ tmp &= ~(ME4000_AI_CTRL_BIT_CHANNEL_FIFO |
+ ME4000_AI_CTRL_BIT_DATA_FIFO |
+ ME4000_AI_CTRL_BIT_STOP | ME4000_AI_CTRL_BIT_IMMEDIATE_STOP);
+ outl(tmp, dev->iobase + ME4000_AI_CTRL_REG);
+
+ /* Set the acquisition mode to single */
+ tmp &= ~(ME4000_AI_CTRL_BIT_MODE_0 | ME4000_AI_CTRL_BIT_MODE_1 |
+ ME4000_AI_CTRL_BIT_MODE_2);
+ outl(tmp, dev->iobase + ME4000_AI_CTRL_REG);
+
+ /* Enable channel list and data fifo */
+ tmp |= ME4000_AI_CTRL_BIT_CHANNEL_FIFO | ME4000_AI_CTRL_BIT_DATA_FIFO;
+ outl(tmp, dev->iobase + ME4000_AI_CTRL_REG);
+
+ /* Generate channel list entry */
+ outl(entry, dev->iobase + ME4000_AI_CHANNEL_LIST_REG);
+
+ /* Set the timer to maximum sample rate */
+ outl(ME4000_AI_MIN_TICKS, dev->iobase + ME4000_AI_CHAN_TIMER_REG);
+ outl(ME4000_AI_MIN_TICKS, dev->iobase + ME4000_AI_CHAN_PRE_TIMER_REG);
+
+ /* Start conversion by dummy read */
+ inl(dev->iobase + ME4000_AI_START_REG);
+
+ /* Wait until ready */
+ udelay(10);
+ if (!(inl(dev->iobase + ME4000_AI_STATUS_REG) &
+ ME4000_AI_STATUS_BIT_EF_DATA)) {
+ dev_err(dev->class_dev, "Value not available after wait\n");
+ return -EIO;
+ }
+
+ /* Read value from data fifo */
+ lval = inl(dev->iobase + ME4000_AI_DATA_REG) & 0xFFFF;
+ data[0] = lval ^ 0x8000;
+
+ return 1;
+}
+
+static int me4000_ai_cancel(struct comedi_device *dev,
+ struct comedi_subdevice *s)
+{
+ unsigned int tmp;
+
+ /* Stop any running conversion */
+ tmp = inl(dev->iobase + ME4000_AI_CTRL_REG);
+ tmp &= ~(ME4000_AI_CTRL_BIT_STOP | ME4000_AI_CTRL_BIT_IMMEDIATE_STOP);
+ outl(tmp, dev->iobase + ME4000_AI_CTRL_REG);
+
+ /* Clear the control register */
+ outl(0x0, dev->iobase + ME4000_AI_CTRL_REG);
+
+ return 0;
+}
+
+static int me4000_ai_check_chanlist(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_cmd *cmd)
+{
+ const struct me4000_board *board = dev->board_ptr;
+ unsigned int max_diff_chan = board->ai_diff_nchan;
+ unsigned int aref0 = CR_AREF(cmd->chanlist[0]);
+ int i;
+
+ for (i = 0; i < cmd->chanlist_len; i++) {
+ unsigned int chan = CR_CHAN(cmd->chanlist[i]);
+ unsigned int range = CR_RANGE(cmd->chanlist[i]);
+ unsigned int aref = CR_AREF(cmd->chanlist[i]);
+
+ if (aref != aref0) {
+ dev_dbg(dev->class_dev,
+ "Mode is not equal for all entries\n");
+ return -EINVAL;
+ }
+
+ if (aref == AREF_DIFF) {
+ if (chan >= max_diff_chan) {
+ dev_dbg(dev->class_dev,
+ "Channel number to high\n");
+ return -EINVAL;
+ }
+
+ if (!comedi_range_is_bipolar(s, range)) {
+ dev_dbg(dev->class_dev,
+ "Bipolar is not selected in differential mode\n");
+ return -EINVAL;
+ }
+ }
+ }
+
+ return 0;
+}
+
+static int ai_round_cmd_args(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_cmd *cmd,
+ unsigned int *init_ticks,
+ unsigned int *scan_ticks, unsigned int *chan_ticks)
+{
+ int rest;
+
+ *init_ticks = 0;
+ *scan_ticks = 0;
+ *chan_ticks = 0;
+
+ if (cmd->start_arg) {
+ *init_ticks = (cmd->start_arg * 33) / 1000;
+ rest = (cmd->start_arg * 33) % 1000;
+
+ if ((cmd->flags & CMDF_ROUND_MASK) == CMDF_ROUND_NEAREST) {
+ if (rest > 33)
+ (*init_ticks)++;
+ } else if ((cmd->flags & CMDF_ROUND_MASK) == CMDF_ROUND_UP) {
+ if (rest)
+ (*init_ticks)++;
+ }
+ }
+
+ if (cmd->scan_begin_arg) {
+ *scan_ticks = (cmd->scan_begin_arg * 33) / 1000;
+ rest = (cmd->scan_begin_arg * 33) % 1000;
+
+ if ((cmd->flags & CMDF_ROUND_MASK) == CMDF_ROUND_NEAREST) {
+ if (rest > 33)
+ (*scan_ticks)++;
+ } else if ((cmd->flags & CMDF_ROUND_MASK) == CMDF_ROUND_UP) {
+ if (rest)
+ (*scan_ticks)++;
+ }
+ }
+
+ if (cmd->convert_arg) {
+ *chan_ticks = (cmd->convert_arg * 33) / 1000;
+ rest = (cmd->convert_arg * 33) % 1000;
+
+ if ((cmd->flags & CMDF_ROUND_MASK) == CMDF_ROUND_NEAREST) {
+ if (rest > 33)
+ (*chan_ticks)++;
+ } else if ((cmd->flags & CMDF_ROUND_MASK) == CMDF_ROUND_UP) {
+ if (rest)
+ (*chan_ticks)++;
+ }
+ }
+
+ return 0;
+}
+
+static void ai_write_timer(struct comedi_device *dev,
+ unsigned int init_ticks,
+ unsigned int scan_ticks, unsigned int chan_ticks)
+{
+ outl(init_ticks - 1, dev->iobase + ME4000_AI_SCAN_PRE_TIMER_LOW_REG);
+ outl(0x0, dev->iobase + ME4000_AI_SCAN_PRE_TIMER_HIGH_REG);
+
+ if (scan_ticks) {
+ outl(scan_ticks - 1, dev->iobase + ME4000_AI_SCAN_TIMER_LOW_REG);
+ outl(0x0, dev->iobase + ME4000_AI_SCAN_TIMER_HIGH_REG);
+ }
+
+ outl(chan_ticks - 1, dev->iobase + ME4000_AI_CHAN_PRE_TIMER_REG);
+ outl(chan_ticks - 1, dev->iobase + ME4000_AI_CHAN_TIMER_REG);
+}
+
+static int ai_write_chanlist(struct comedi_device *dev,
+ struct comedi_subdevice *s, struct comedi_cmd *cmd)
+{
+ unsigned int entry;
+ unsigned int chan;
+ unsigned int rang;
+ unsigned int aref;
+ int i;
+
+ for (i = 0; i < cmd->chanlist_len; i++) {
+ chan = CR_CHAN(cmd->chanlist[i]);
+ rang = CR_RANGE(cmd->chanlist[i]);
+ aref = CR_AREF(cmd->chanlist[i]);
+
+ entry = chan;
+
+ if (rang == 0)
+ entry |= ME4000_AI_LIST_RANGE_UNIPOLAR_2_5;
+ else if (rang == 1)
+ entry |= ME4000_AI_LIST_RANGE_UNIPOLAR_10;
+ else if (rang == 2)
+ entry |= ME4000_AI_LIST_RANGE_BIPOLAR_2_5;
+ else
+ entry |= ME4000_AI_LIST_RANGE_BIPOLAR_10;
+
+ if (aref == AREF_DIFF)
+ entry |= ME4000_AI_LIST_INPUT_DIFFERENTIAL;
+ else
+ entry |= ME4000_AI_LIST_INPUT_SINGLE_ENDED;
+
+ outl(entry, dev->iobase + ME4000_AI_CHANNEL_LIST_REG);
+ }
+
+ return 0;
+}
+
+static int ai_prepare(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_cmd *cmd,
+ unsigned int init_ticks,
+ unsigned int scan_ticks, unsigned int chan_ticks)
+{
+ unsigned int tmp = 0;
+
+ /* Write timer arguments */
+ ai_write_timer(dev, init_ticks, scan_ticks, chan_ticks);
+
+ /* Reset control register */
+ outl(tmp, dev->iobase + ME4000_AI_CTRL_REG);
+
+ /* Start sources */
+ if ((cmd->start_src == TRIG_EXT &&
+ cmd->scan_begin_src == TRIG_TIMER &&
+ cmd->convert_src == TRIG_TIMER) ||
+ (cmd->start_src == TRIG_EXT &&
+ cmd->scan_begin_src == TRIG_FOLLOW &&
+ cmd->convert_src == TRIG_TIMER)) {
+ tmp = ME4000_AI_CTRL_BIT_MODE_1 |
+ ME4000_AI_CTRL_BIT_CHANNEL_FIFO |
+ ME4000_AI_CTRL_BIT_DATA_FIFO;
+ } else if (cmd->start_src == TRIG_EXT &&
+ cmd->scan_begin_src == TRIG_EXT &&
+ cmd->convert_src == TRIG_TIMER) {
+ tmp = ME4000_AI_CTRL_BIT_MODE_2 |
+ ME4000_AI_CTRL_BIT_CHANNEL_FIFO |
+ ME4000_AI_CTRL_BIT_DATA_FIFO;
+ } else if (cmd->start_src == TRIG_EXT &&
+ cmd->scan_begin_src == TRIG_EXT &&
+ cmd->convert_src == TRIG_EXT) {
+ tmp = ME4000_AI_CTRL_BIT_MODE_0 |
+ ME4000_AI_CTRL_BIT_MODE_1 |
+ ME4000_AI_CTRL_BIT_CHANNEL_FIFO |
+ ME4000_AI_CTRL_BIT_DATA_FIFO;
+ } else {
+ tmp = ME4000_AI_CTRL_BIT_MODE_0 |
+ ME4000_AI_CTRL_BIT_CHANNEL_FIFO |
+ ME4000_AI_CTRL_BIT_DATA_FIFO;
+ }
+
+ /* Stop triggers */
+ if (cmd->stop_src == TRIG_COUNT) {
+ outl(cmd->chanlist_len * cmd->stop_arg,
+ dev->iobase + ME4000_AI_SAMPLE_COUNTER_REG);
+ tmp |= ME4000_AI_CTRL_BIT_HF_IRQ | ME4000_AI_CTRL_BIT_SC_IRQ;
+ } else if (cmd->stop_src == TRIG_NONE &&
+ cmd->scan_end_src == TRIG_COUNT) {
+ outl(cmd->scan_end_arg,
+ dev->iobase + ME4000_AI_SAMPLE_COUNTER_REG);
+ tmp |= ME4000_AI_CTRL_BIT_HF_IRQ | ME4000_AI_CTRL_BIT_SC_IRQ;
+ } else {
+ tmp |= ME4000_AI_CTRL_BIT_HF_IRQ;
+ }
+
+ /* Write the setup to the control register */
+ outl(tmp, dev->iobase + ME4000_AI_CTRL_REG);
+
+ /* Write the channel list */
+ ai_write_chanlist(dev, s, cmd);
+
+ return 0;
+}
+
+static int me4000_ai_do_cmd(struct comedi_device *dev,
+ struct comedi_subdevice *s)
+{
+ int err;
+ unsigned int init_ticks = 0;
+ unsigned int scan_ticks = 0;
+ unsigned int chan_ticks = 0;
+ struct comedi_cmd *cmd = &s->async->cmd;
+
+ /* Reset the analog input */
+ err = me4000_ai_cancel(dev, s);
+ if (err)
+ return err;
+
+ /* Round the timer arguments */
+ err = ai_round_cmd_args(dev,
+ s, cmd, &init_ticks, &scan_ticks, &chan_ticks);
+ if (err)
+ return err;
+
+ /* Prepare the AI for acquisition */
+ err = ai_prepare(dev, s, cmd, init_ticks, scan_ticks, chan_ticks);
+ if (err)
+ return err;
+
+ /* Start acquistion by dummy read */
+ inl(dev->iobase + ME4000_AI_START_REG);
+
+ return 0;
+}
+
+static int me4000_ai_do_cmd_test(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_cmd *cmd)
+{
+ unsigned int init_ticks;
+ unsigned int chan_ticks;
+ unsigned int scan_ticks;
+ int err = 0;
+
+ /* Round the timer arguments */
+ ai_round_cmd_args(dev, s, cmd, &init_ticks, &scan_ticks, &chan_ticks);
+
+ /* Step 1 : check if triggers are trivially valid */
+
+ err |= comedi_check_trigger_src(&cmd->start_src, TRIG_NOW | TRIG_EXT);
+ err |= comedi_check_trigger_src(&cmd->scan_begin_src,
+ TRIG_FOLLOW | TRIG_TIMER | TRIG_EXT);
+ err |= comedi_check_trigger_src(&cmd->convert_src,
+ TRIG_TIMER | TRIG_EXT);
+ err |= comedi_check_trigger_src(&cmd->scan_end_src,
+ TRIG_NONE | TRIG_COUNT);
+ err |= comedi_check_trigger_src(&cmd->stop_src, TRIG_NONE | TRIG_COUNT);
+
+ if (err)
+ return 1;
+
+ /* Step 2a : make sure trigger sources are unique */
+
+ err |= comedi_check_trigger_is_unique(cmd->start_src);
+ err |= comedi_check_trigger_is_unique(cmd->scan_begin_src);
+ err |= comedi_check_trigger_is_unique(cmd->convert_src);
+ err |= comedi_check_trigger_is_unique(cmd->scan_end_src);
+ err |= comedi_check_trigger_is_unique(cmd->stop_src);
+
+ /* Step 2b : and mutually compatible */
+
+ if (cmd->start_src == TRIG_NOW &&
+ cmd->scan_begin_src == TRIG_TIMER &&
+ cmd->convert_src == TRIG_TIMER) {
+ } else if (cmd->start_src == TRIG_NOW &&
+ cmd->scan_begin_src == TRIG_FOLLOW &&
+ cmd->convert_src == TRIG_TIMER) {
+ } else if (cmd->start_src == TRIG_EXT &&
+ cmd->scan_begin_src == TRIG_TIMER &&
+ cmd->convert_src == TRIG_TIMER) {
+ } else if (cmd->start_src == TRIG_EXT &&
+ cmd->scan_begin_src == TRIG_FOLLOW &&
+ cmd->convert_src == TRIG_TIMER) {
+ } else if (cmd->start_src == TRIG_EXT &&
+ cmd->scan_begin_src == TRIG_EXT &&
+ cmd->convert_src == TRIG_TIMER) {
+ } else if (cmd->start_src == TRIG_EXT &&
+ cmd->scan_begin_src == TRIG_EXT &&
+ cmd->convert_src == TRIG_EXT) {
+ } else {
+ err |= -EINVAL;
+ }
+
+ if (err)
+ return 2;
+
+ /* Step 3: check if arguments are trivially valid */
+
+ err |= comedi_check_trigger_arg_is(&cmd->start_arg, 0);
+
+ if (cmd->chanlist_len < 1) {
+ cmd->chanlist_len = 1;
+ err |= -EINVAL;
+ }
+ if (init_ticks < 66) {
+ cmd->start_arg = 2000;
+ err |= -EINVAL;
+ }
+ if (scan_ticks && scan_ticks < 67) {
+ cmd->scan_begin_arg = 2031;
+ err |= -EINVAL;
+ }
+ if (chan_ticks < 66) {
+ cmd->convert_arg = 2000;
+ err |= -EINVAL;
+ }
+
+ if (cmd->stop_src == TRIG_COUNT)
+ err |= comedi_check_trigger_arg_min(&cmd->stop_arg, 1);
+ else /* TRIG_NONE */
+ err |= comedi_check_trigger_arg_is(&cmd->stop_arg, 0);
+
+ if (err)
+ return 3;
+
+ /*
+ * Stage 4. Check for argument conflicts.
+ */
+ if (cmd->start_src == TRIG_NOW &&
+ cmd->scan_begin_src == TRIG_TIMER &&
+ cmd->convert_src == TRIG_TIMER) {
+ /* Check timer arguments */
+ if (init_ticks < ME4000_AI_MIN_TICKS) {
+ dev_err(dev->class_dev, "Invalid start arg\n");
+ cmd->start_arg = 2000; /* 66 ticks at least */
+ err++;
+ }
+ if (chan_ticks < ME4000_AI_MIN_TICKS) {
+ dev_err(dev->class_dev, "Invalid convert arg\n");
+ cmd->convert_arg = 2000; /* 66 ticks at least */
+ err++;
+ }
+ if (scan_ticks <= cmd->chanlist_len * chan_ticks) {
+ dev_err(dev->class_dev, "Invalid scan end arg\n");
+
+ /* At least one tick more */
+ cmd->scan_end_arg = 2000 * cmd->chanlist_len + 31;
+ err++;
+ }
+ } else if (cmd->start_src == TRIG_NOW &&
+ cmd->scan_begin_src == TRIG_FOLLOW &&
+ cmd->convert_src == TRIG_TIMER) {
+ /* Check timer arguments */
+ if (init_ticks < ME4000_AI_MIN_TICKS) {
+ dev_err(dev->class_dev, "Invalid start arg\n");
+ cmd->start_arg = 2000; /* 66 ticks at least */
+ err++;
+ }
+ if (chan_ticks < ME4000_AI_MIN_TICKS) {
+ dev_err(dev->class_dev, "Invalid convert arg\n");
+ cmd->convert_arg = 2000; /* 66 ticks at least */
+ err++;
+ }
+ } else if (cmd->start_src == TRIG_EXT &&
+ cmd->scan_begin_src == TRIG_TIMER &&
+ cmd->convert_src == TRIG_TIMER) {
+ /* Check timer arguments */
+ if (init_ticks < ME4000_AI_MIN_TICKS) {
+ dev_err(dev->class_dev, "Invalid start arg\n");
+ cmd->start_arg = 2000; /* 66 ticks at least */
+ err++;
+ }
+ if (chan_ticks < ME4000_AI_MIN_TICKS) {
+ dev_err(dev->class_dev, "Invalid convert arg\n");
+ cmd->convert_arg = 2000; /* 66 ticks at least */
+ err++;
+ }
+ if (scan_ticks <= cmd->chanlist_len * chan_ticks) {
+ dev_err(dev->class_dev, "Invalid scan end arg\n");
+
+ /* At least one tick more */
+ cmd->scan_end_arg = 2000 * cmd->chanlist_len + 31;
+ err++;
+ }
+ } else if (cmd->start_src == TRIG_EXT &&
+ cmd->scan_begin_src == TRIG_FOLLOW &&
+ cmd->convert_src == TRIG_TIMER) {
+ /* Check timer arguments */
+ if (init_ticks < ME4000_AI_MIN_TICKS) {
+ dev_err(dev->class_dev, "Invalid start arg\n");
+ cmd->start_arg = 2000; /* 66 ticks at least */
+ err++;
+ }
+ if (chan_ticks < ME4000_AI_MIN_TICKS) {
+ dev_err(dev->class_dev, "Invalid convert arg\n");
+ cmd->convert_arg = 2000; /* 66 ticks at least */
+ err++;
+ }
+ } else if (cmd->start_src == TRIG_EXT &&
+ cmd->scan_begin_src == TRIG_EXT &&
+ cmd->convert_src == TRIG_TIMER) {
+ /* Check timer arguments */
+ if (init_ticks < ME4000_AI_MIN_TICKS) {
+ dev_err(dev->class_dev, "Invalid start arg\n");
+ cmd->start_arg = 2000; /* 66 ticks at least */
+ err++;
+ }
+ if (chan_ticks < ME4000_AI_MIN_TICKS) {
+ dev_err(dev->class_dev, "Invalid convert arg\n");
+ cmd->convert_arg = 2000; /* 66 ticks at least */
+ err++;
+ }
+ } else if (cmd->start_src == TRIG_EXT &&
+ cmd->scan_begin_src == TRIG_EXT &&
+ cmd->convert_src == TRIG_EXT) {
+ /* Check timer arguments */
+ if (init_ticks < ME4000_AI_MIN_TICKS) {
+ dev_err(dev->class_dev, "Invalid start arg\n");
+ cmd->start_arg = 2000; /* 66 ticks at least */
+ err++;
+ }
+ }
+ if (cmd->scan_end_src == TRIG_COUNT) {
+ if (cmd->scan_end_arg == 0) {
+ dev_err(dev->class_dev, "Invalid scan end arg\n");
+ cmd->scan_end_arg = 1;
+ err++;
+ }
+ }
+
+ if (err)
+ return 4;
+
+ /* Step 5: check channel list if it exists */
+ if (cmd->chanlist && cmd->chanlist_len > 0)
+ err |= me4000_ai_check_chanlist(dev, s, cmd);
+
+ if (err)
+ return 5;
+
+ return 0;
+}
+
+static irqreturn_t me4000_ai_isr(int irq, void *dev_id)
+{
+ unsigned int tmp;
+ struct comedi_device *dev = dev_id;
+ struct comedi_subdevice *s = dev->read_subdev;
+ int i;
+ int c = 0;
+ unsigned int lval;
+
+ if (!dev->attached)
+ return IRQ_NONE;
+
+ if (inl(dev->iobase + ME4000_IRQ_STATUS_REG) &
+ ME4000_IRQ_STATUS_BIT_AI_HF) {
+ /* Read status register to find out what happened */
+ tmp = inl(dev->iobase + ME4000_AI_CTRL_REG);
+
+ if (!(tmp & ME4000_AI_STATUS_BIT_FF_DATA) &&
+ !(tmp & ME4000_AI_STATUS_BIT_HF_DATA) &&
+ (tmp & ME4000_AI_STATUS_BIT_EF_DATA)) {
+ c = ME4000_AI_FIFO_COUNT;
+
+ /*
+ * FIFO overflow, so stop conversion
+ * and disable all interrupts
+ */
+ tmp |= ME4000_AI_CTRL_BIT_IMMEDIATE_STOP;
+ tmp &= ~(ME4000_AI_CTRL_BIT_HF_IRQ |
+ ME4000_AI_CTRL_BIT_SC_IRQ);
+ outl(tmp, dev->iobase + ME4000_AI_CTRL_REG);
+
+ s->async->events |= COMEDI_CB_ERROR;
+
+ dev_err(dev->class_dev, "FIFO overflow\n");
+ } else if ((tmp & ME4000_AI_STATUS_BIT_FF_DATA)
+ && !(tmp & ME4000_AI_STATUS_BIT_HF_DATA)
+ && (tmp & ME4000_AI_STATUS_BIT_EF_DATA)) {
+ c = ME4000_AI_FIFO_COUNT / 2;
+ } else {
+ dev_err(dev->class_dev,
+ "Can't determine state of fifo\n");
+ c = 0;
+
+ /*
+ * Undefined state, so stop conversion
+ * and disable all interrupts
+ */
+ tmp |= ME4000_AI_CTRL_BIT_IMMEDIATE_STOP;
+ tmp &= ~(ME4000_AI_CTRL_BIT_HF_IRQ |
+ ME4000_AI_CTRL_BIT_SC_IRQ);
+ outl(tmp, dev->iobase + ME4000_AI_CTRL_REG);
+
+ s->async->events |= COMEDI_CB_ERROR;
+
+ dev_err(dev->class_dev, "Undefined FIFO state\n");
+ }
+
+ for (i = 0; i < c; i++) {
+ /* Read value from data fifo */
+ lval = inl(dev->iobase + ME4000_AI_DATA_REG) & 0xFFFF;
+ lval ^= 0x8000;
+
+ if (!comedi_buf_write_samples(s, &lval, 1)) {
+ /*
+ * Buffer overflow, so stop conversion
+ * and disable all interrupts
+ */
+ tmp |= ME4000_AI_CTRL_BIT_IMMEDIATE_STOP;
+ tmp &= ~(ME4000_AI_CTRL_BIT_HF_IRQ |
+ ME4000_AI_CTRL_BIT_SC_IRQ);
+ outl(tmp, dev->iobase + ME4000_AI_CTRL_REG);
+ break;
+ }
+ }
+
+ /* Work is done, so reset the interrupt */
+ tmp |= ME4000_AI_CTRL_BIT_HF_IRQ_RESET;
+ outl(tmp, dev->iobase + ME4000_AI_CTRL_REG);
+ tmp &= ~ME4000_AI_CTRL_BIT_HF_IRQ_RESET;
+ outl(tmp, dev->iobase + ME4000_AI_CTRL_REG);
+ }
+
+ if (inl(dev->iobase + ME4000_IRQ_STATUS_REG) &
+ ME4000_IRQ_STATUS_BIT_SC) {
+ s->async->events |= COMEDI_CB_EOA;
+
+ /*
+ * Acquisition is complete, so stop
+ * conversion and disable all interrupts
+ */
+ tmp = inl(dev->iobase + ME4000_AI_CTRL_REG);
+ tmp |= ME4000_AI_CTRL_BIT_IMMEDIATE_STOP;
+ tmp &= ~(ME4000_AI_CTRL_BIT_HF_IRQ | ME4000_AI_CTRL_BIT_SC_IRQ);
+ outl(tmp, dev->iobase + ME4000_AI_CTRL_REG);
+
+ /* Poll data until fifo empty */
+ while (inl(dev->iobase + ME4000_AI_CTRL_REG) &
+ ME4000_AI_STATUS_BIT_EF_DATA) {
+ /* Read value from data fifo */
+ lval = inl(dev->iobase + ME4000_AI_DATA_REG) & 0xFFFF;
+ lval ^= 0x8000;
+
+ if (!comedi_buf_write_samples(s, &lval, 1))
+ break;
+ }
+
+ /* Work is done, so reset the interrupt */
+ tmp |= ME4000_AI_CTRL_BIT_SC_IRQ_RESET;
+ outl(tmp, dev->iobase + ME4000_AI_CTRL_REG);
+ tmp &= ~ME4000_AI_CTRL_BIT_SC_IRQ_RESET;
+ outl(tmp, dev->iobase + ME4000_AI_CTRL_REG);
+ }
+
+ comedi_handle_events(dev, s);
+
+ return IRQ_HANDLED;
+}
+
+static int me4000_ao_insn_write(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ int chan = CR_CHAN(insn->chanspec);
+ unsigned int tmp;
+
+ /* Stop any running conversion */
+ tmp = inl(dev->iobase + ME4000_AO_CTRL_REG(chan));
+ tmp |= ME4000_AO_CTRL_BIT_IMMEDIATE_STOP;
+ outl(tmp, dev->iobase + ME4000_AO_CTRL_REG(chan));
+
+ /* Clear control register and set to single mode */
+ outl(0x0, dev->iobase + ME4000_AO_CTRL_REG(chan));
+
+ /* Write data value */
+ outl(data[0], dev->iobase + ME4000_AO_SINGLE_REG(chan));
+
+ /* Store in the mirror */
+ s->readback[chan] = data[0];
+
+ return 1;
+}
+
+static int me4000_dio_insn_bits(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ if (comedi_dio_update_state(s, data)) {
+ outl((s->state >> 0) & 0xFF,
+ dev->iobase + ME4000_DIO_PORT_0_REG);
+ outl((s->state >> 8) & 0xFF,
+ dev->iobase + ME4000_DIO_PORT_1_REG);
+ outl((s->state >> 16) & 0xFF,
+ dev->iobase + ME4000_DIO_PORT_2_REG);
+ outl((s->state >> 24) & 0xFF,
+ dev->iobase + ME4000_DIO_PORT_3_REG);
+ }
+
+ data[1] = ((inl(dev->iobase + ME4000_DIO_PORT_0_REG) & 0xFF) << 0) |
+ ((inl(dev->iobase + ME4000_DIO_PORT_1_REG) & 0xFF) << 8) |
+ ((inl(dev->iobase + ME4000_DIO_PORT_2_REG) & 0xFF) << 16) |
+ ((inl(dev->iobase + ME4000_DIO_PORT_3_REG) & 0xFF) << 24);
+
+ return insn->n;
+}
+
+static int me4000_dio_insn_config(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ unsigned int chan = CR_CHAN(insn->chanspec);
+ unsigned int mask;
+ unsigned int tmp;
+ int ret;
+
+ if (chan < 8)
+ mask = 0x000000ff;
+ else if (chan < 16)
+ mask = 0x0000ff00;
+ else if (chan < 24)
+ mask = 0x00ff0000;
+ else
+ mask = 0xff000000;
+
+ ret = comedi_dio_insn_config(dev, s, insn, data, mask);
+ if (ret)
+ return ret;
+
+ tmp = inl(dev->iobase + ME4000_DIO_CTRL_REG);
+ tmp &= ~(ME4000_DIO_CTRL_BIT_MODE_0 | ME4000_DIO_CTRL_BIT_MODE_1 |
+ ME4000_DIO_CTRL_BIT_MODE_2 | ME4000_DIO_CTRL_BIT_MODE_3 |
+ ME4000_DIO_CTRL_BIT_MODE_4 | ME4000_DIO_CTRL_BIT_MODE_5 |
+ ME4000_DIO_CTRL_BIT_MODE_6 | ME4000_DIO_CTRL_BIT_MODE_7);
+ if (s->io_bits & 0x000000ff)
+ tmp |= ME4000_DIO_CTRL_BIT_MODE_0;
+ if (s->io_bits & 0x0000ff00)
+ tmp |= ME4000_DIO_CTRL_BIT_MODE_2;
+ if (s->io_bits & 0x00ff0000)
+ tmp |= ME4000_DIO_CTRL_BIT_MODE_4;
+ if (s->io_bits & 0xff000000)
+ tmp |= ME4000_DIO_CTRL_BIT_MODE_6;
+
+ /*
+ * Check for optoisolated ME-4000 version.
+ * If one the first port is a fixed output
+ * port and the second is a fixed input port.
+ */
+ if (inl(dev->iobase + ME4000_DIO_DIR_REG)) {
+ s->io_bits |= 0x000000ff;
+ s->io_bits &= ~0x0000ff00;
+ tmp |= ME4000_DIO_CTRL_BIT_MODE_0;
+ tmp &= ~(ME4000_DIO_CTRL_BIT_MODE_2 |
+ ME4000_DIO_CTRL_BIT_MODE_3);
+ }
+
+ outl(tmp, dev->iobase + ME4000_DIO_CTRL_REG);
+
+ return insn->n;
+}
+
+static int me4000_auto_attach(struct comedi_device *dev,
+ unsigned long context)
+{
+ struct pci_dev *pcidev = comedi_to_pci_dev(dev);
+ const struct me4000_board *thisboard = NULL;
+ struct me4000_info *info;
+ struct comedi_subdevice *s;
+ int result;
+
+ if (context < ARRAY_SIZE(me4000_boards))
+ thisboard = &me4000_boards[context];
+ if (!thisboard)
+ return -ENODEV;
+ dev->board_ptr = thisboard;
+ dev->board_name = thisboard->name;
+
+ info = comedi_alloc_devpriv(dev, sizeof(*info));
+ if (!info)
+ return -ENOMEM;
+
+ result = comedi_pci_enable(dev);
+ if (result)
+ return result;
+
+ info->plx_regbase = pci_resource_start(pcidev, 1);
+ dev->iobase = pci_resource_start(pcidev, 2);
+ if (!info->plx_regbase || !dev->iobase)
+ return -ENODEV;
+
+ result = comedi_load_firmware(dev, &pcidev->dev, ME4000_FIRMWARE,
+ me4000_xilinx_download, 0);
+ if (result < 0)
+ return result;
+
+ me4000_reset(dev);
+
+ if (pcidev->irq > 0) {
+ result = request_irq(pcidev->irq, me4000_ai_isr, IRQF_SHARED,
+ dev->board_name, dev);
+ if (result == 0)
+ dev->irq = pcidev->irq;
+ }
+
+ result = comedi_alloc_subdevices(dev, 4);
+ if (result)
+ return result;
+
+ /*=========================================================================
+ Analog input subdevice
+ ========================================================================*/
+
+ s = &dev->subdevices[0];
+
+ if (thisboard->ai_nchan) {
+ s->type = COMEDI_SUBD_AI;
+ s->subdev_flags =
+ SDF_READABLE | SDF_COMMON | SDF_GROUND | SDF_DIFF;
+ s->n_chan = thisboard->ai_nchan;
+ s->maxdata = 0xFFFF; /* 16 bit ADC */
+ s->len_chanlist = ME4000_AI_CHANNEL_LIST_COUNT;
+ s->range_table = &me4000_ai_range;
+ s->insn_read = me4000_ai_insn_read;
+
+ if (dev->irq) {
+ dev->read_subdev = s;
+ s->subdev_flags |= SDF_CMD_READ;
+ s->cancel = me4000_ai_cancel;
+ s->do_cmdtest = me4000_ai_do_cmd_test;
+ s->do_cmd = me4000_ai_do_cmd;
+ }
+ } else {
+ s->type = COMEDI_SUBD_UNUSED;
+ }
+
+ /*=========================================================================
+ Analog output subdevice
+ ========================================================================*/
+
+ s = &dev->subdevices[1];
+
+ if (thisboard->ao_nchan) {
+ s->type = COMEDI_SUBD_AO;
+ s->subdev_flags = SDF_WRITABLE | SDF_COMMON | SDF_GROUND;
+ s->n_chan = thisboard->ao_nchan;
+ s->maxdata = 0xFFFF; /* 16 bit DAC */
+ s->range_table = &range_bipolar10;
+ s->insn_write = me4000_ao_insn_write;
+
+ result = comedi_alloc_subdev_readback(s);
+ if (result)
+ return result;
+ } else {
+ s->type = COMEDI_SUBD_UNUSED;
+ }
+
+ /*=========================================================================
+ Digital I/O subdevice
+ ========================================================================*/
+
+ s = &dev->subdevices[2];
+
+ if (thisboard->dio_nchan) {
+ s->type = COMEDI_SUBD_DIO;
+ s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
+ s->n_chan = thisboard->dio_nchan;
+ s->maxdata = 1;
+ s->range_table = &range_digital;
+ s->insn_bits = me4000_dio_insn_bits;
+ s->insn_config = me4000_dio_insn_config;
+ } else {
+ s->type = COMEDI_SUBD_UNUSED;
+ }
+
+ /*
+ * Check for optoisolated ME-4000 version. If one the first
+ * port is a fixed output port and the second is a fixed input port.
+ */
+ if (!inl(dev->iobase + ME4000_DIO_DIR_REG)) {
+ s->io_bits |= 0xFF;
+ outl(ME4000_DIO_CTRL_BIT_MODE_0,
+ dev->iobase + ME4000_DIO_DIR_REG);
+ }
+
+ /* Counter subdevice (8254) */
+ s = &dev->subdevices[3];
+ if (thisboard->has_counter) {
+ unsigned long timer_base = pci_resource_start(pcidev, 3);
+
+ if (!timer_base)
+ return -ENODEV;
+
+ dev->pacer = comedi_8254_init(timer_base, 0, I8254_IO8, 0);
+ if (!dev->pacer)
+ return -ENOMEM;
+
+ comedi_8254_subdevice_init(s, dev->pacer);
+ } else {
+ s->type = COMEDI_SUBD_UNUSED;
+ }
+
+ return 0;
+}
+
+static void me4000_detach(struct comedi_device *dev)
+{
+ if (dev->iobase)
+ me4000_reset(dev);
+ comedi_pci_detach(dev);
+}
+
+static struct comedi_driver me4000_driver = {
+ .driver_name = "me4000",
+ .module = THIS_MODULE,
+ .auto_attach = me4000_auto_attach,
+ .detach = me4000_detach,
+};
+
+static int me4000_pci_probe(struct pci_dev *dev,
+ const struct pci_device_id *id)
+{
+ return comedi_pci_auto_config(dev, &me4000_driver, id->driver_data);
+}
+
+static const struct pci_device_id me4000_pci_table[] = {
+ { PCI_VDEVICE(MEILHAUS, 0x4650), BOARD_ME4650 },
+ { PCI_VDEVICE(MEILHAUS, 0x4660), BOARD_ME4660 },
+ { PCI_VDEVICE(MEILHAUS, 0x4661), BOARD_ME4660I },
+ { PCI_VDEVICE(MEILHAUS, 0x4662), BOARD_ME4660S },
+ { PCI_VDEVICE(MEILHAUS, 0x4663), BOARD_ME4660IS },
+ { PCI_VDEVICE(MEILHAUS, 0x4670), BOARD_ME4670 },
+ { PCI_VDEVICE(MEILHAUS, 0x4671), BOARD_ME4670I },
+ { PCI_VDEVICE(MEILHAUS, 0x4672), BOARD_ME4670S },
+ { PCI_VDEVICE(MEILHAUS, 0x4673), BOARD_ME4670IS },
+ { PCI_VDEVICE(MEILHAUS, 0x4680), BOARD_ME4680 },
+ { PCI_VDEVICE(MEILHAUS, 0x4681), BOARD_ME4680I },
+ { PCI_VDEVICE(MEILHAUS, 0x4682), BOARD_ME4680S },
+ { PCI_VDEVICE(MEILHAUS, 0x4683), BOARD_ME4680IS },
+ { 0 }
+};
+MODULE_DEVICE_TABLE(pci, me4000_pci_table);
+
+static struct pci_driver me4000_pci_driver = {
+ .name = "me4000",
+ .id_table = me4000_pci_table,
+ .probe = me4000_pci_probe,
+ .remove = comedi_pci_auto_unconfig,
+};
+module_comedi_pci_driver(me4000_driver, me4000_pci_driver);
+
+MODULE_AUTHOR("Comedi http://www.comedi.org");
+MODULE_DESCRIPTION("Comedi low-level driver");
+MODULE_LICENSE("GPL");
+/*(DEBLOBBED)*/
diff --git a/drivers/staging/comedi/drivers/me_daq.c b/drivers/staging/comedi/drivers/me_daq.c
new file mode 100644
index 000000000..87e9f46b4
--- /dev/null
+++ b/drivers/staging/comedi/drivers/me_daq.c
@@ -0,0 +1,583 @@
+/*
+ * comedi/drivers/me_daq.c
+ * Hardware driver for Meilhaus data acquisition cards:
+ * ME-2000i, ME-2600i, ME-3000vm1
+ *
+ * Copyright (C) 2002 Michael Hillmann <hillmann@syscongroup.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.
+ */
+
+/*
+ * Driver: me_daq
+ * Description: Meilhaus PCI data acquisition cards
+ * Devices: [Meilhaus] ME-2600i (me-2600i), ME-2000i (me-2000i)
+ * Author: Michael Hillmann <hillmann@syscongroup.de>
+ * Status: experimental
+ *
+ * Configuration options: not applicable, uses PCI auto config
+ *
+ * Supports:
+ * Analog Input, Analog Output, Digital I/O
+ */
+
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/sched.h>
+
+#include "../comedi_pci.h"
+
+#include "plx9052.h"
+
+#define ME2600_FIRMWARE "/*(DEBLOBBED)*/"
+
+#define XILINX_DOWNLOAD_RESET 0x42 /* Xilinx registers */
+
+#define ME_CONTROL_1 0x0000 /* - | W */
+#define INTERRUPT_ENABLE (1<<15)
+#define COUNTER_B_IRQ (1<<12)
+#define COUNTER_A_IRQ (1<<11)
+#define CHANLIST_READY_IRQ (1<<10)
+#define EXT_IRQ (1<<9)
+#define ADFIFO_HALFFULL_IRQ (1<<8)
+#define SCAN_COUNT_ENABLE (1<<5)
+#define SIMULTANEOUS_ENABLE (1<<4)
+#define TRIGGER_FALLING_EDGE (1<<3)
+#define CONTINUOUS_MODE (1<<2)
+#define DISABLE_ADC (0<<0)
+#define SOFTWARE_TRIGGERED_ADC (1<<0)
+#define SCAN_TRIGGERED_ADC (2<<0)
+#define EXT_TRIGGERED_ADC (3<<0)
+#define ME_ADC_START 0x0000 /* R | - */
+#define ME_CONTROL_2 0x0002 /* - | W */
+#define ENABLE_ADFIFO (1<<10)
+#define ENABLE_CHANLIST (1<<9)
+#define ENABLE_PORT_B (1<<7)
+#define ENABLE_PORT_A (1<<6)
+#define ENABLE_COUNTER_B (1<<4)
+#define ENABLE_COUNTER_A (1<<3)
+#define ENABLE_DAC (1<<1)
+#define BUFFERED_DAC (1<<0)
+#define ME_DAC_UPDATE 0x0002 /* R | - */
+#define ME_STATUS 0x0004 /* R | - */
+#define COUNTER_B_IRQ_PENDING (1<<12)
+#define COUNTER_A_IRQ_PENDING (1<<11)
+#define CHANLIST_READY_IRQ_PENDING (1<<10)
+#define EXT_IRQ_PENDING (1<<9)
+#define ADFIFO_HALFFULL_IRQ_PENDING (1<<8)
+#define ADFIFO_FULL (1<<4)
+#define ADFIFO_HALFFULL (1<<3)
+#define ADFIFO_EMPTY (1<<2)
+#define CHANLIST_FULL (1<<1)
+#define FST_ACTIVE (1<<0)
+#define ME_RESET_INTERRUPT 0x0004 /* - | W */
+#define ME_DIO_PORT_A 0x0006 /* R | W */
+#define ME_DIO_PORT_B 0x0008 /* R | W */
+#define ME_TIMER_DATA_0 0x000A /* - | W */
+#define ME_TIMER_DATA_1 0x000C /* - | W */
+#define ME_TIMER_DATA_2 0x000E /* - | W */
+#define ME_CHANNEL_LIST 0x0010 /* - | W */
+#define ADC_UNIPOLAR (1<<6)
+#define ADC_GAIN_0 (0<<4)
+#define ADC_GAIN_1 (1<<4)
+#define ADC_GAIN_2 (2<<4)
+#define ADC_GAIN_3 (3<<4)
+#define ME_READ_AD_FIFO 0x0010 /* R | - */
+#define ME_DAC_CONTROL 0x0012 /* - | W */
+#define DAC_UNIPOLAR_D (0<<4)
+#define DAC_BIPOLAR_D (1<<4)
+#define DAC_UNIPOLAR_C (0<<5)
+#define DAC_BIPOLAR_C (1<<5)
+#define DAC_UNIPOLAR_B (0<<6)
+#define DAC_BIPOLAR_B (1<<6)
+#define DAC_UNIPOLAR_A (0<<7)
+#define DAC_BIPOLAR_A (1<<7)
+#define DAC_GAIN_0_D (0<<8)
+#define DAC_GAIN_1_D (1<<8)
+#define DAC_GAIN_0_C (0<<9)
+#define DAC_GAIN_1_C (1<<9)
+#define DAC_GAIN_0_B (0<<10)
+#define DAC_GAIN_1_B (1<<10)
+#define DAC_GAIN_0_A (0<<11)
+#define DAC_GAIN_1_A (1<<11)
+#define ME_DAC_CONTROL_UPDATE 0x0012 /* R | - */
+#define ME_DAC_DATA_A 0x0014 /* - | W */
+#define ME_DAC_DATA_B 0x0016 /* - | W */
+#define ME_DAC_DATA_C 0x0018 /* - | W */
+#define ME_DAC_DATA_D 0x001A /* - | W */
+#define ME_COUNTER_ENDDATA_A 0x001C /* - | W */
+#define ME_COUNTER_ENDDATA_B 0x001E /* - | W */
+#define ME_COUNTER_STARTDATA_A 0x0020 /* - | W */
+#define ME_COUNTER_VALUE_A 0x0020 /* R | - */
+#define ME_COUNTER_STARTDATA_B 0x0022 /* - | W */
+#define ME_COUNTER_VALUE_B 0x0022 /* R | - */
+
+static const struct comedi_lrange me_ai_range = {
+ 8, {
+ BIP_RANGE(10),
+ BIP_RANGE(5),
+ BIP_RANGE(2.5),
+ BIP_RANGE(1.25),
+ UNI_RANGE(10),
+ UNI_RANGE(5),
+ UNI_RANGE(2.5),
+ UNI_RANGE(1.25)
+ }
+};
+
+static const struct comedi_lrange me_ao_range = {
+ 3, {
+ BIP_RANGE(10),
+ BIP_RANGE(5),
+ UNI_RANGE(10)
+ }
+};
+
+enum me_boardid {
+ BOARD_ME2600,
+ BOARD_ME2000,
+};
+
+struct me_board {
+ const char *name;
+ int needs_firmware;
+ int has_ao;
+};
+
+static const struct me_board me_boards[] = {
+ [BOARD_ME2600] = {
+ .name = "me-2600i",
+ .needs_firmware = 1,
+ .has_ao = 1,
+ },
+ [BOARD_ME2000] = {
+ .name = "me-2000i",
+ },
+};
+
+struct me_private_data {
+ void __iomem *plx_regbase; /* PLX configuration base address */
+
+ unsigned short control_1; /* Mirror of CONTROL_1 register */
+ unsigned short control_2; /* Mirror of CONTROL_2 register */
+ unsigned short dac_control; /* Mirror of the DAC_CONTROL register */
+};
+
+static inline void sleep(unsigned sec)
+{
+ __set_current_state(TASK_INTERRUPTIBLE);
+ schedule_timeout(sec * HZ);
+}
+
+static int me_dio_insn_config(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ struct me_private_data *devpriv = dev->private;
+ unsigned int chan = CR_CHAN(insn->chanspec);
+ unsigned int mask;
+ int ret;
+
+ if (chan < 16)
+ mask = 0x0000ffff;
+ else
+ mask = 0xffff0000;
+
+ ret = comedi_dio_insn_config(dev, s, insn, data, mask);
+ if (ret)
+ return ret;
+
+ if (s->io_bits & 0x0000ffff)
+ devpriv->control_2 |= ENABLE_PORT_A;
+ else
+ devpriv->control_2 &= ~ENABLE_PORT_A;
+ if (s->io_bits & 0xffff0000)
+ devpriv->control_2 |= ENABLE_PORT_B;
+ else
+ devpriv->control_2 &= ~ENABLE_PORT_B;
+
+ writew(devpriv->control_2, dev->mmio + ME_CONTROL_2);
+
+ return insn->n;
+}
+
+static int me_dio_insn_bits(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ void __iomem *mmio_porta = dev->mmio + ME_DIO_PORT_A;
+ void __iomem *mmio_portb = dev->mmio + ME_DIO_PORT_B;
+ unsigned int mask;
+ unsigned int val;
+
+ mask = comedi_dio_update_state(s, data);
+ if (mask) {
+ if (mask & 0x0000ffff)
+ writew((s->state & 0xffff), mmio_porta);
+ if (mask & 0xffff0000)
+ writew(((s->state >> 16) & 0xffff), mmio_portb);
+ }
+
+ if (s->io_bits & 0x0000ffff)
+ val = s->state & 0xffff;
+ else
+ val = readw(mmio_porta);
+
+ if (s->io_bits & 0xffff0000)
+ val |= (s->state & 0xffff0000);
+ else
+ val |= (readw(mmio_portb) << 16);
+
+ data[1] = val;
+
+ return insn->n;
+}
+
+static int me_ai_eoc(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned long context)
+{
+ unsigned int status;
+
+ status = readw(dev->mmio + ME_STATUS);
+ if ((status & 0x0004) == 0)
+ return 0;
+ return -EBUSY;
+}
+
+static int me_ai_insn_read(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ struct me_private_data *dev_private = dev->private;
+ unsigned int chan = CR_CHAN(insn->chanspec);
+ unsigned int rang = CR_RANGE(insn->chanspec);
+ unsigned int aref = CR_AREF(insn->chanspec);
+ unsigned short val;
+ int ret;
+
+ /* stop any running conversion */
+ dev_private->control_1 &= 0xFFFC;
+ writew(dev_private->control_1, dev->mmio + ME_CONTROL_1);
+
+ /* clear chanlist and ad fifo */
+ dev_private->control_2 &= ~(ENABLE_ADFIFO | ENABLE_CHANLIST);
+ writew(dev_private->control_2, dev->mmio + ME_CONTROL_2);
+
+ /* reset any pending interrupt */
+ writew(0x00, dev->mmio + ME_RESET_INTERRUPT);
+
+ /* enable the chanlist and ADC fifo */
+ dev_private->control_2 |= (ENABLE_ADFIFO | ENABLE_CHANLIST);
+ writew(dev_private->control_2, dev->mmio + ME_CONTROL_2);
+
+ /* write to channel list fifo */
+ val = chan & 0x0f; /* b3:b0 channel */
+ val |= (rang & 0x03) << 4; /* b5:b4 gain */
+ val |= (rang & 0x04) << 4; /* b6 polarity */
+ val |= ((aref & AREF_DIFF) ? 0x80 : 0); /* b7 differential */
+ writew(val & 0xff, dev->mmio + ME_CHANNEL_LIST);
+
+ /* set ADC mode to software trigger */
+ dev_private->control_1 |= SOFTWARE_TRIGGERED_ADC;
+ writew(dev_private->control_1, dev->mmio + ME_CONTROL_1);
+
+ /* start conversion by reading from ADC_START */
+ readw(dev->mmio + ME_ADC_START);
+
+ /* wait for ADC fifo not empty flag */
+ ret = comedi_timeout(dev, s, insn, me_ai_eoc, 0);
+ if (ret)
+ return ret;
+
+ /* get value from ADC fifo */
+ val = readw(dev->mmio + ME_READ_AD_FIFO);
+ val = (val ^ 0x800) & 0x0fff;
+ data[0] = val;
+
+ /* stop any running conversion */
+ dev_private->control_1 &= 0xFFFC;
+ writew(dev_private->control_1, dev->mmio + ME_CONTROL_1);
+
+ return 1;
+}
+
+static int me_ao_insn_write(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ struct me_private_data *dev_private = dev->private;
+ unsigned int chan = CR_CHAN(insn->chanspec);
+ unsigned int rang = CR_RANGE(insn->chanspec);
+ unsigned int val = s->readback[chan];
+ int i;
+
+ /* Enable all DAC */
+ dev_private->control_2 |= ENABLE_DAC;
+ writew(dev_private->control_2, dev->mmio + ME_CONTROL_2);
+
+ /* and set DAC to "buffered" mode */
+ dev_private->control_2 |= BUFFERED_DAC;
+ writew(dev_private->control_2, dev->mmio + ME_CONTROL_2);
+
+ /* Set dac-control register */
+ for (i = 0; i < insn->n; i++) {
+ /* clear bits for this channel */
+ dev_private->dac_control &= ~(0x0880 >> chan);
+ if (rang == 0)
+ dev_private->dac_control |=
+ ((DAC_BIPOLAR_A | DAC_GAIN_1_A) >> chan);
+ else if (rang == 1)
+ dev_private->dac_control |=
+ ((DAC_BIPOLAR_A | DAC_GAIN_0_A) >> chan);
+ }
+ writew(dev_private->dac_control, dev->mmio + ME_DAC_CONTROL);
+
+ /* Update dac-control register */
+ readw(dev->mmio + ME_DAC_CONTROL_UPDATE);
+
+ /* Set data register */
+ for (i = 0; i < insn->n; i++) {
+ val = data[i];
+
+ writew(val, dev->mmio + ME_DAC_DATA_A + (chan << 1));
+ }
+ s->readback[chan] = val;
+
+ /* Update dac with data registers */
+ readw(dev->mmio + ME_DAC_UPDATE);
+
+ return insn->n;
+}
+
+static int me2600_xilinx_download(struct comedi_device *dev,
+ const u8 *data, size_t size,
+ unsigned long context)
+{
+ struct me_private_data *dev_private = dev->private;
+ unsigned int value;
+ unsigned int file_length;
+ unsigned int i;
+
+ /* disable irq's on PLX */
+ writel(0x00, dev_private->plx_regbase + PLX9052_INTCSR);
+
+ /* First, make a dummy read to reset xilinx */
+ value = readw(dev->mmio + XILINX_DOWNLOAD_RESET);
+
+ /* Wait until reset is over */
+ sleep(1);
+
+ /* Write a dummy value to Xilinx */
+ writeb(0x00, dev->mmio + 0x0);
+ sleep(1);
+
+ /*
+ * Format of the firmware
+ * Build longs from the byte-wise coded header
+ * Byte 1-3: length of the array
+ * Byte 4-7: version
+ * Byte 8-11: date
+ * Byte 12-15: reserved
+ */
+ if (size < 16)
+ return -EINVAL;
+
+ file_length = (((unsigned int)data[0] & 0xff) << 24) +
+ (((unsigned int)data[1] & 0xff) << 16) +
+ (((unsigned int)data[2] & 0xff) << 8) +
+ ((unsigned int)data[3] & 0xff);
+
+ /*
+ * Loop for writing firmware byte by byte to xilinx
+ * Firmware data start at offset 16
+ */
+ for (i = 0; i < file_length; i++)
+ writeb((data[16 + i] & 0xff), dev->mmio + 0x0);
+
+ /* Write 5 dummy values to xilinx */
+ for (i = 0; i < 5; i++)
+ writeb(0x00, dev->mmio + 0x0);
+
+ /* Test if there was an error during download -> INTB was thrown */
+ value = readl(dev_private->plx_regbase + PLX9052_INTCSR);
+ if (value & PLX9052_INTCSR_LI2STAT) {
+ /* Disable interrupt */
+ writel(0x00, dev_private->plx_regbase + PLX9052_INTCSR);
+ dev_err(dev->class_dev, "Xilinx download failed\n");
+ return -EIO;
+ }
+
+ /* Wait until the Xilinx is ready for real work */
+ sleep(1);
+
+ /* Enable PLX-Interrupts */
+ writel(PLX9052_INTCSR_LI1ENAB |
+ PLX9052_INTCSR_LI1POL |
+ PLX9052_INTCSR_PCIENAB,
+ dev_private->plx_regbase + PLX9052_INTCSR);
+
+ return 0;
+}
+
+static int me_reset(struct comedi_device *dev)
+{
+ struct me_private_data *dev_private = dev->private;
+
+ /* Reset board */
+ writew(0x00, dev->mmio + ME_CONTROL_1);
+ writew(0x00, dev->mmio + ME_CONTROL_2);
+ writew(0x00, dev->mmio + ME_RESET_INTERRUPT);
+ writew(0x00, dev->mmio + ME_DAC_CONTROL);
+
+ /* Save values in the board context */
+ dev_private->dac_control = 0;
+ dev_private->control_1 = 0;
+ dev_private->control_2 = 0;
+
+ return 0;
+}
+
+static int me_auto_attach(struct comedi_device *dev,
+ unsigned long context)
+{
+ struct pci_dev *pcidev = comedi_to_pci_dev(dev);
+ const struct me_board *board = NULL;
+ struct me_private_data *dev_private;
+ struct comedi_subdevice *s;
+ int ret;
+
+ if (context < ARRAY_SIZE(me_boards))
+ board = &me_boards[context];
+ if (!board)
+ return -ENODEV;
+ dev->board_ptr = board;
+ dev->board_name = board->name;
+
+ dev_private = comedi_alloc_devpriv(dev, sizeof(*dev_private));
+ if (!dev_private)
+ return -ENOMEM;
+
+ ret = comedi_pci_enable(dev);
+ if (ret)
+ return ret;
+
+ dev_private->plx_regbase = pci_ioremap_bar(pcidev, 0);
+ if (!dev_private->plx_regbase)
+ return -ENOMEM;
+
+ dev->mmio = pci_ioremap_bar(pcidev, 2);
+ if (!dev->mmio)
+ return -ENOMEM;
+
+ /* Download firmware and reset card */
+ if (board->needs_firmware) {
+ ret = comedi_load_firmware(dev, &comedi_to_pci_dev(dev)->dev,
+ ME2600_FIRMWARE,
+ me2600_xilinx_download, 0);
+ if (ret < 0)
+ return ret;
+ }
+ me_reset(dev);
+
+ ret = comedi_alloc_subdevices(dev, 3);
+ if (ret)
+ return ret;
+
+ s = &dev->subdevices[0];
+ s->type = COMEDI_SUBD_AI;
+ s->subdev_flags = SDF_READABLE | SDF_COMMON;
+ s->n_chan = 16;
+ s->maxdata = 0x0fff;
+ s->len_chanlist = 16;
+ s->range_table = &me_ai_range;
+ s->insn_read = me_ai_insn_read;
+
+ s = &dev->subdevices[1];
+ if (board->has_ao) {
+ s->type = COMEDI_SUBD_AO;
+ s->subdev_flags = SDF_WRITABLE | SDF_COMMON;
+ s->n_chan = 4;
+ s->maxdata = 0x0fff;
+ s->len_chanlist = 4;
+ s->range_table = &me_ao_range;
+ s->insn_write = me_ao_insn_write;
+
+ ret = comedi_alloc_subdev_readback(s);
+ if (ret)
+ return ret;
+ } else {
+ s->type = COMEDI_SUBD_UNUSED;
+ }
+
+ s = &dev->subdevices[2];
+ s->type = COMEDI_SUBD_DIO;
+ s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
+ s->n_chan = 32;
+ s->maxdata = 1;
+ s->len_chanlist = 32;
+ s->range_table = &range_digital;
+ s->insn_bits = me_dio_insn_bits;
+ s->insn_config = me_dio_insn_config;
+
+ return 0;
+}
+
+static void me_detach(struct comedi_device *dev)
+{
+ struct me_private_data *dev_private = dev->private;
+
+ if (dev_private) {
+ if (dev->mmio)
+ me_reset(dev);
+ if (dev_private->plx_regbase)
+ iounmap(dev_private->plx_regbase);
+ }
+ comedi_pci_detach(dev);
+}
+
+static struct comedi_driver me_daq_driver = {
+ .driver_name = "me_daq",
+ .module = THIS_MODULE,
+ .auto_attach = me_auto_attach,
+ .detach = me_detach,
+};
+
+static int me_daq_pci_probe(struct pci_dev *dev,
+ const struct pci_device_id *id)
+{
+ return comedi_pci_auto_config(dev, &me_daq_driver, id->driver_data);
+}
+
+static const struct pci_device_id me_daq_pci_table[] = {
+ { PCI_VDEVICE(MEILHAUS, 0x2600), BOARD_ME2600 },
+ { PCI_VDEVICE(MEILHAUS, 0x2000), BOARD_ME2000 },
+ { 0 }
+};
+MODULE_DEVICE_TABLE(pci, me_daq_pci_table);
+
+static struct pci_driver me_daq_pci_driver = {
+ .name = "me_daq",
+ .id_table = me_daq_pci_table,
+ .probe = me_daq_pci_probe,
+ .remove = comedi_pci_auto_unconfig,
+};
+module_comedi_pci_driver(me_daq_driver, me_daq_pci_driver);
+
+MODULE_AUTHOR("Comedi http://www.comedi.org");
+MODULE_DESCRIPTION("Comedi low-level driver");
+MODULE_LICENSE("GPL");
+/*(DEBLOBBED)*/
diff --git a/drivers/staging/comedi/drivers/mf6x4.c b/drivers/staging/comedi/drivers/mf6x4.c
new file mode 100644
index 000000000..a675e2ef9
--- /dev/null
+++ b/drivers/staging/comedi/drivers/mf6x4.c
@@ -0,0 +1,330 @@
+/*
+ * comedi/drivers/mf6x4.c
+ * Driver for Humusoft MF634 and MF624 Data acquisition cards
+ *
+ * COMEDI - Linux Control and Measurement Device Interface
+ * Copyright (C) 2000 David A. Schleef <ds@schleef.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+/*
+ * Driver: mf6x4
+ * Description: Humusoft MF634 and MF624 Data acquisition card driver
+ * Devices: [Humusoft] MF634 (mf634), MF624 (mf624)
+ * Author: Rostislav Lisovy <lisovy@gmail.com>
+ * Status: works
+ * Updated:
+ * Configuration Options: none
+ */
+
+#include <linux/module.h>
+#include <linux/delay.h>
+
+#include "../comedi_pci.h"
+
+/* Registers present in BAR0 memory region */
+#define MF624_GPIOC_R 0x54
+
+#define MF6X4_GPIOC_EOLC /* End Of Last Conversion */ (1 << 17)
+#define MF6X4_GPIOC_LDAC /* Load DACs */ (1 << 23)
+#define MF6X4_GPIOC_DACEN (1 << 26)
+
+/* BAR1 registers */
+#define MF6X4_DIN_R 0x10
+#define MF6X4_DIN_M 0xff
+#define MF6X4_DOUT_R 0x10
+#define MF6X4_DOUT_M 0xff
+
+#define MF6X4_ADSTART_R 0x20
+#define MF6X4_ADDATA_R 0x00
+#define MF6X4_ADCTRL_R 0x00
+#define MF6X4_ADCTRL_M 0xff
+
+#define MF6X4_DA0_R 0x20
+#define MF6X4_DA1_R 0x22
+#define MF6X4_DA2_R 0x24
+#define MF6X4_DA3_R 0x26
+#define MF6X4_DA4_R 0x28
+#define MF6X4_DA5_R 0x2a
+#define MF6X4_DA6_R 0x2c
+#define MF6X4_DA7_R 0x2e
+/* Map DAC cahnnel id to real HW-dependent offset value */
+#define MF6X4_DAC_R(x) (0x20 + ((x) * 2))
+
+/* BAR2 registers */
+#define MF634_GPIOC_R 0x68
+
+enum mf6x4_boardid {
+ BOARD_MF634,
+ BOARD_MF624,
+};
+
+struct mf6x4_board {
+ const char *name;
+ unsigned int bar_nums[3]; /* We need to keep track of the
+ order of BARs used by the cards */
+};
+
+static const struct mf6x4_board mf6x4_boards[] = {
+ [BOARD_MF634] = {
+ .name = "mf634",
+ .bar_nums = {0, 2, 3},
+ },
+ [BOARD_MF624] = {
+ .name = "mf624",
+ .bar_nums = {0, 2, 4},
+ },
+};
+
+struct mf6x4_private {
+ /*
+ * Documentation for both MF634 and MF624 describes registers
+ * present in BAR0, 1 and 2 regions.
+ * The real (i.e. in HW) BAR numbers are different for MF624
+ * and MF634 yet we will call them 0, 1, 2
+ */
+ void __iomem *bar0_mem;
+ void __iomem *bar2_mem;
+
+ /*
+ * This configuration register has the same function and fields
+ * for both cards however it lies in different BARs on different
+ * offsets -- this variable makes the access easier
+ */
+ void __iomem *gpioc_R;
+};
+
+static int mf6x4_di_insn_bits(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ data[1] = ioread16(dev->mmio + MF6X4_DIN_R) & MF6X4_DIN_M;
+
+ return insn->n;
+}
+
+static int mf6x4_do_insn_bits(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ if (comedi_dio_update_state(s, data))
+ iowrite16(s->state & MF6X4_DOUT_M, dev->mmio + MF6X4_DOUT_R);
+
+ data[1] = s->state;
+
+ return insn->n;
+}
+
+static int mf6x4_ai_eoc(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned long context)
+{
+ struct mf6x4_private *devpriv = dev->private;
+ unsigned int status;
+
+ status = ioread32(devpriv->gpioc_R);
+ if (status & MF6X4_GPIOC_EOLC)
+ return 0;
+ return -EBUSY;
+}
+
+static int mf6x4_ai_insn_read(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ int chan = CR_CHAN(insn->chanspec);
+ int ret;
+ int i;
+ int d;
+
+ /* Set the ADC channel number in the scan list */
+ iowrite16((1 << chan) & MF6X4_ADCTRL_M, dev->mmio + MF6X4_ADCTRL_R);
+
+ for (i = 0; i < insn->n; i++) {
+ /* Trigger ADC conversion by reading ADSTART */
+ ioread16(dev->mmio + MF6X4_ADSTART_R);
+
+ ret = comedi_timeout(dev, s, insn, mf6x4_ai_eoc, 0);
+ if (ret)
+ return ret;
+
+ /* Read the actual value */
+ d = ioread16(dev->mmio + MF6X4_ADDATA_R);
+ d &= s->maxdata;
+ data[i] = d;
+ }
+
+ iowrite16(0x0, dev->mmio + MF6X4_ADCTRL_R);
+
+ return insn->n;
+}
+
+static int mf6x4_ao_insn_write(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ struct mf6x4_private *devpriv = dev->private;
+ unsigned int chan = CR_CHAN(insn->chanspec);
+ unsigned int val = s->readback[chan];
+ uint32_t gpioc;
+ int i;
+
+ /* Enable instantaneous update of converters outputs + Enable DACs */
+ gpioc = ioread32(devpriv->gpioc_R);
+ iowrite32((gpioc & ~MF6X4_GPIOC_LDAC) | MF6X4_GPIOC_DACEN,
+ devpriv->gpioc_R);
+
+ for (i = 0; i < insn->n; i++) {
+ val = data[i];
+ iowrite16(val, dev->mmio + MF6X4_DAC_R(chan));
+ }
+ s->readback[chan] = val;
+
+ return insn->n;
+}
+
+static int mf6x4_auto_attach(struct comedi_device *dev, unsigned long context)
+{
+ struct pci_dev *pcidev = comedi_to_pci_dev(dev);
+ const struct mf6x4_board *board = NULL;
+ struct mf6x4_private *devpriv;
+ struct comedi_subdevice *s;
+ int ret;
+
+ if (context < ARRAY_SIZE(mf6x4_boards))
+ board = &mf6x4_boards[context];
+ else
+ return -ENODEV;
+
+ dev->board_ptr = board;
+ dev->board_name = board->name;
+
+ ret = comedi_pci_enable(dev);
+ if (ret)
+ return ret;
+
+ devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
+ if (!devpriv)
+ return -ENOMEM;
+
+ devpriv->bar0_mem = pci_ioremap_bar(pcidev, board->bar_nums[0]);
+ if (!devpriv->bar0_mem)
+ return -ENODEV;
+
+ dev->mmio = pci_ioremap_bar(pcidev, board->bar_nums[1]);
+ if (!dev->mmio)
+ return -ENODEV;
+
+ devpriv->bar2_mem = pci_ioremap_bar(pcidev, board->bar_nums[2]);
+ if (!devpriv->bar2_mem)
+ return -ENODEV;
+
+ if (board == &mf6x4_boards[BOARD_MF634])
+ devpriv->gpioc_R = devpriv->bar2_mem + MF634_GPIOC_R;
+ else
+ devpriv->gpioc_R = devpriv->bar0_mem + MF624_GPIOC_R;
+
+ ret = comedi_alloc_subdevices(dev, 4);
+ if (ret)
+ return ret;
+
+ /* ADC */
+ s = &dev->subdevices[0];
+ s->type = COMEDI_SUBD_AI;
+ s->subdev_flags = SDF_READABLE | SDF_GROUND;
+ s->n_chan = 8;
+ s->maxdata = 0x3fff; /* 14 bits ADC */
+ s->range_table = &range_bipolar10;
+ s->insn_read = mf6x4_ai_insn_read;
+
+ /* DAC */
+ s = &dev->subdevices[1];
+ s->type = COMEDI_SUBD_AO;
+ s->subdev_flags = SDF_WRITABLE;
+ s->n_chan = 8;
+ s->maxdata = 0x3fff; /* 14 bits DAC */
+ s->range_table = &range_bipolar10;
+ s->insn_write = mf6x4_ao_insn_write;
+
+ ret = comedi_alloc_subdev_readback(s);
+ if (ret)
+ return ret;
+
+ /* DIN */
+ s = &dev->subdevices[2];
+ s->type = COMEDI_SUBD_DI;
+ s->subdev_flags = SDF_READABLE;
+ s->n_chan = 8;
+ s->maxdata = 1;
+ s->range_table = &range_digital;
+ s->insn_bits = mf6x4_di_insn_bits;
+
+ /* DOUT */
+ s = &dev->subdevices[3];
+ s->type = COMEDI_SUBD_DO;
+ s->subdev_flags = SDF_WRITABLE;
+ s->n_chan = 8;
+ s->maxdata = 1;
+ s->range_table = &range_digital;
+ s->insn_bits = mf6x4_do_insn_bits;
+
+ return 0;
+}
+
+static void mf6x4_detach(struct comedi_device *dev)
+{
+ struct mf6x4_private *devpriv = dev->private;
+
+ if (devpriv) {
+ if (devpriv->bar0_mem)
+ iounmap(devpriv->bar0_mem);
+ if (devpriv->bar2_mem)
+ iounmap(devpriv->bar2_mem);
+ }
+ comedi_pci_detach(dev);
+}
+
+static struct comedi_driver mf6x4_driver = {
+ .driver_name = "mf6x4",
+ .module = THIS_MODULE,
+ .auto_attach = mf6x4_auto_attach,
+ .detach = mf6x4_detach,
+};
+
+static int mf6x4_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
+{
+ return comedi_pci_auto_config(dev, &mf6x4_driver, id->driver_data);
+}
+
+static const struct pci_device_id mf6x4_pci_table[] = {
+ { PCI_VDEVICE(HUMUSOFT, 0x0634), BOARD_MF634 },
+ { PCI_VDEVICE(HUMUSOFT, 0x0624), BOARD_MF624 },
+ { 0 }
+};
+MODULE_DEVICE_TABLE(pci, mf6x4_pci_table);
+
+static struct pci_driver mf6x4_pci_driver = {
+ .name = "mf6x4",
+ .id_table = mf6x4_pci_table,
+ .probe = mf6x4_pci_probe,
+ .remove = comedi_pci_auto_unconfig,
+};
+
+module_comedi_pci_driver(mf6x4_driver, mf6x4_pci_driver);
+
+MODULE_AUTHOR("Rostislav Lisovy <lisovy@gmail.com>");
+MODULE_DESCRIPTION("Comedi MF634 and MF624 DAQ cards driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/comedi/drivers/mite.c b/drivers/staging/comedi/drivers/mite.c
new file mode 100644
index 000000000..e43a0c832
--- /dev/null
+++ b/drivers/staging/comedi/drivers/mite.c
@@ -0,0 +1,626 @@
+/*
+ comedi/drivers/mite.c
+ Hardware driver for NI Mite PCI interface chip
+
+ COMEDI - Linux Control and Measurement Device Interface
+ Copyright (C) 1997-2002 David A. Schleef <ds@schleef.org>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+*/
+
+/*
+ The PCI-MIO E series driver was originally written by
+ Tomasz Motylewski <...>, and ported to comedi by ds.
+
+ References for specifications:
+
+ 321747b.pdf Register Level Programmer Manual (obsolete)
+ 321747c.pdf Register Level Programmer Manual (new)
+ DAQ-STC reference manual
+
+ Other possibly relevant info:
+
+ 320517c.pdf User manual (obsolete)
+ 320517f.pdf User manual (new)
+ 320889a.pdf delete
+ 320906c.pdf maximum signal ratings
+ 321066a.pdf about 16x
+ 321791a.pdf discontinuation of at-mio-16e-10 rev. c
+ 321808a.pdf about at-mio-16e-10 rev P
+ 321837a.pdf discontinuation of at-mio-16de-10 rev d
+ 321838a.pdf about at-mio-16de-10 rev N
+
+ ISSUES:
+
+*/
+
+/* #define USE_KMALLOC */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/module.h>
+
+#include "../comedi_pci.h"
+
+#include "mite.h"
+
+#define TOP_OF_PAGE(x) ((x)|(~(PAGE_MASK)))
+
+struct mite_struct *mite_alloc(struct pci_dev *pcidev)
+{
+ struct mite_struct *mite;
+ unsigned int i;
+
+ mite = kzalloc(sizeof(*mite), GFP_KERNEL);
+ if (mite) {
+ spin_lock_init(&mite->lock);
+ mite->pcidev = pcidev;
+ for (i = 0; i < MAX_MITE_DMA_CHANNELS; ++i) {
+ mite->channels[i].mite = mite;
+ mite->channels[i].channel = i;
+ mite->channels[i].done = 1;
+ }
+ }
+ return mite;
+}
+EXPORT_SYMBOL_GPL(mite_alloc);
+
+static void dump_chip_signature(u32 csigr_bits)
+{
+ pr_info("version = %i, type = %i, mite mode = %i, interface mode = %i\n",
+ mite_csigr_version(csigr_bits), mite_csigr_type(csigr_bits),
+ mite_csigr_mmode(csigr_bits), mite_csigr_imode(csigr_bits));
+ pr_info("num channels = %i, write post fifo depth = %i, wins = %i, iowins = %i\n",
+ mite_csigr_dmac(csigr_bits), mite_csigr_wpdep(csigr_bits),
+ mite_csigr_wins(csigr_bits), mite_csigr_iowins(csigr_bits));
+}
+
+static unsigned mite_fifo_size(struct mite_struct *mite, unsigned channel)
+{
+ unsigned fcr_bits = readl(mite->mite_io_addr + MITE_FCR(channel));
+ unsigned empty_count = (fcr_bits >> 16) & 0xff;
+ unsigned full_count = fcr_bits & 0xff;
+
+ return empty_count + full_count;
+}
+
+int mite_setup2(struct comedi_device *dev,
+ struct mite_struct *mite, bool use_win1)
+{
+ unsigned long length;
+ int i;
+ u32 csigr_bits;
+ unsigned unknown_dma_burst_bits;
+
+ pci_set_master(mite->pcidev);
+
+ mite->mite_io_addr = pci_ioremap_bar(mite->pcidev, 0);
+ if (!mite->mite_io_addr) {
+ dev_err(dev->class_dev,
+ "Failed to remap mite io memory address\n");
+ return -ENOMEM;
+ }
+ mite->mite_phys_addr = pci_resource_start(mite->pcidev, 0);
+
+ dev->mmio = pci_ioremap_bar(mite->pcidev, 1);
+ if (!dev->mmio) {
+ dev_err(dev->class_dev,
+ "Failed to remap daq io memory address\n");
+ return -ENOMEM;
+ }
+ mite->daq_phys_addr = pci_resource_start(mite->pcidev, 1);
+ length = pci_resource_len(mite->pcidev, 1);
+
+ if (use_win1) {
+ writel(0, mite->mite_io_addr + MITE_IODWBSR);
+ dev_info(dev->class_dev,
+ "using I/O Window Base Size register 1\n");
+ writel(mite->daq_phys_addr | WENAB |
+ MITE_IODWBSR_1_WSIZE_bits(length),
+ mite->mite_io_addr + MITE_IODWBSR_1);
+ writel(0, mite->mite_io_addr + MITE_IODWCR_1);
+ } else {
+ writel(mite->daq_phys_addr | WENAB,
+ mite->mite_io_addr + MITE_IODWBSR);
+ }
+ /*
+ * make sure dma bursts work. I got this from running a bus analyzer
+ * on a pxi-6281 and a pxi-6713. 6713 powered up with register value
+ * of 0x61f and bursts worked. 6281 powered up with register value of
+ * 0x1f and bursts didn't work. The NI windows driver reads the
+ * register, then does a bitwise-or of 0x600 with it and writes it back.
+ */
+ unknown_dma_burst_bits =
+ readl(mite->mite_io_addr + MITE_UNKNOWN_DMA_BURST_REG);
+ unknown_dma_burst_bits |= UNKNOWN_DMA_BURST_ENABLE_BITS;
+ writel(unknown_dma_burst_bits,
+ mite->mite_io_addr + MITE_UNKNOWN_DMA_BURST_REG);
+
+ csigr_bits = readl(mite->mite_io_addr + MITE_CSIGR);
+ mite->num_channels = mite_csigr_dmac(csigr_bits);
+ if (mite->num_channels > MAX_MITE_DMA_CHANNELS) {
+ dev_warn(dev->class_dev,
+ "mite: bug? chip claims to have %i dma channels. Setting to %i.\n",
+ mite->num_channels, MAX_MITE_DMA_CHANNELS);
+ mite->num_channels = MAX_MITE_DMA_CHANNELS;
+ }
+ dump_chip_signature(csigr_bits);
+ for (i = 0; i < mite->num_channels; i++) {
+ writel(CHOR_DMARESET, mite->mite_io_addr + MITE_CHOR(i));
+ /* disable interrupts */
+ writel(CHCR_CLR_DMA_IE | CHCR_CLR_LINKP_IE | CHCR_CLR_SAR_IE |
+ CHCR_CLR_DONE_IE | CHCR_CLR_MRDY_IE | CHCR_CLR_DRDY_IE |
+ CHCR_CLR_LC_IE | CHCR_CLR_CONT_RB_IE,
+ mite->mite_io_addr + MITE_CHCR(i));
+ }
+ mite->fifo_size = mite_fifo_size(mite, 0);
+ dev_info(dev->class_dev, "fifo size is %i.\n", mite->fifo_size);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(mite_setup2);
+
+void mite_detach(struct mite_struct *mite)
+{
+ if (!mite)
+ return;
+
+ if (mite->mite_io_addr)
+ iounmap(mite->mite_io_addr);
+
+ kfree(mite);
+}
+EXPORT_SYMBOL_GPL(mite_detach);
+
+struct mite_dma_descriptor_ring *mite_alloc_ring(struct mite_struct *mite)
+{
+ struct mite_dma_descriptor_ring *ring =
+ kmalloc(sizeof(struct mite_dma_descriptor_ring), GFP_KERNEL);
+
+ if (!ring)
+ return NULL;
+ ring->hw_dev = get_device(&mite->pcidev->dev);
+ if (!ring->hw_dev) {
+ kfree(ring);
+ return NULL;
+ }
+ ring->n_links = 0;
+ ring->descriptors = NULL;
+ ring->descriptors_dma_addr = 0;
+ return ring;
+};
+EXPORT_SYMBOL_GPL(mite_alloc_ring);
+
+void mite_free_ring(struct mite_dma_descriptor_ring *ring)
+{
+ if (ring) {
+ if (ring->descriptors) {
+ dma_free_coherent(ring->hw_dev,
+ ring->n_links *
+ sizeof(struct mite_dma_descriptor),
+ ring->descriptors,
+ ring->descriptors_dma_addr);
+ }
+ put_device(ring->hw_dev);
+ kfree(ring);
+ }
+};
+EXPORT_SYMBOL_GPL(mite_free_ring);
+
+struct mite_channel *mite_request_channel_in_range(struct mite_struct *mite,
+ struct
+ mite_dma_descriptor_ring
+ *ring, unsigned min_channel,
+ unsigned max_channel)
+{
+ int i;
+ unsigned long flags;
+ struct mite_channel *channel = NULL;
+
+ /* spin lock so mite_release_channel can be called safely
+ * from interrupts
+ */
+ spin_lock_irqsave(&mite->lock, flags);
+ for (i = min_channel; i <= max_channel; ++i) {
+ if (mite->channel_allocated[i] == 0) {
+ mite->channel_allocated[i] = 1;
+ channel = &mite->channels[i];
+ channel->ring = ring;
+ break;
+ }
+ }
+ spin_unlock_irqrestore(&mite->lock, flags);
+ return channel;
+}
+EXPORT_SYMBOL_GPL(mite_request_channel_in_range);
+
+void mite_release_channel(struct mite_channel *mite_chan)
+{
+ struct mite_struct *mite = mite_chan->mite;
+ unsigned long flags;
+
+ /* spin lock to prevent races with mite_request_channel */
+ spin_lock_irqsave(&mite->lock, flags);
+ if (mite->channel_allocated[mite_chan->channel]) {
+ mite_dma_disarm(mite_chan);
+ mite_dma_reset(mite_chan);
+ /*
+ * disable all channel's interrupts (do it after disarm/reset so
+ * MITE_CHCR reg isn't changed while dma is still active!)
+ */
+ writel(CHCR_CLR_DMA_IE | CHCR_CLR_LINKP_IE |
+ CHCR_CLR_SAR_IE | CHCR_CLR_DONE_IE |
+ CHCR_CLR_MRDY_IE | CHCR_CLR_DRDY_IE |
+ CHCR_CLR_LC_IE | CHCR_CLR_CONT_RB_IE,
+ mite->mite_io_addr + MITE_CHCR(mite_chan->channel));
+ mite->channel_allocated[mite_chan->channel] = 0;
+ mite_chan->ring = NULL;
+ mmiowb();
+ }
+ spin_unlock_irqrestore(&mite->lock, flags);
+}
+EXPORT_SYMBOL_GPL(mite_release_channel);
+
+void mite_dma_arm(struct mite_channel *mite_chan)
+{
+ struct mite_struct *mite = mite_chan->mite;
+ int chor;
+ unsigned long flags;
+
+ /*
+ * memory barrier is intended to insure any twiddling with the buffer
+ * is done before writing to the mite to arm dma transfer
+ */
+ smp_mb();
+ /* arm */
+ chor = CHOR_START;
+ spin_lock_irqsave(&mite->lock, flags);
+ mite_chan->done = 0;
+ writel(chor, mite->mite_io_addr + MITE_CHOR(mite_chan->channel));
+ mmiowb();
+ spin_unlock_irqrestore(&mite->lock, flags);
+/* mite_dma_tcr(mite, channel); */
+}
+EXPORT_SYMBOL_GPL(mite_dma_arm);
+
+/**************************************/
+
+int mite_buf_change(struct mite_dma_descriptor_ring *ring,
+ struct comedi_subdevice *s)
+{
+ struct comedi_async *async = s->async;
+ unsigned int n_links;
+ int i;
+
+ if (ring->descriptors) {
+ dma_free_coherent(ring->hw_dev,
+ ring->n_links *
+ sizeof(struct mite_dma_descriptor),
+ ring->descriptors,
+ ring->descriptors_dma_addr);
+ }
+ ring->descriptors = NULL;
+ ring->descriptors_dma_addr = 0;
+ ring->n_links = 0;
+
+ if (async->prealloc_bufsz == 0)
+ return 0;
+
+ n_links = async->prealloc_bufsz >> PAGE_SHIFT;
+
+ ring->descriptors =
+ dma_alloc_coherent(ring->hw_dev,
+ n_links * sizeof(struct mite_dma_descriptor),
+ &ring->descriptors_dma_addr, GFP_KERNEL);
+ if (!ring->descriptors) {
+ dev_err(s->device->class_dev,
+ "mite: ring buffer allocation failed\n");
+ return -ENOMEM;
+ }
+ ring->n_links = n_links;
+
+ for (i = 0; i < n_links; i++) {
+ ring->descriptors[i].count = cpu_to_le32(PAGE_SIZE);
+ ring->descriptors[i].addr =
+ cpu_to_le32(async->buf_map->page_list[i].dma_addr);
+ ring->descriptors[i].next =
+ cpu_to_le32(ring->descriptors_dma_addr + (i +
+ 1) *
+ sizeof(struct mite_dma_descriptor));
+ }
+ ring->descriptors[n_links - 1].next =
+ cpu_to_le32(ring->descriptors_dma_addr);
+ /*
+ * barrier is meant to insure that all the writes to the dma descriptors
+ * have completed before the dma controller is commanded to read them
+ */
+ smp_wmb();
+ return 0;
+}
+EXPORT_SYMBOL_GPL(mite_buf_change);
+
+void mite_prep_dma(struct mite_channel *mite_chan,
+ unsigned int num_device_bits, unsigned int num_memory_bits)
+{
+ unsigned int chor, chcr, mcr, dcr, lkcr;
+ struct mite_struct *mite = mite_chan->mite;
+
+ /* reset DMA and FIFO */
+ chor = CHOR_DMARESET | CHOR_FRESET;
+ writel(chor, mite->mite_io_addr + MITE_CHOR(mite_chan->channel));
+
+ /* short link chaining mode */
+ chcr = CHCR_SET_DMA_IE | CHCR_LINKSHORT | CHCR_SET_DONE_IE |
+ CHCR_BURSTEN;
+ /*
+ * Link Complete Interrupt: interrupt every time a link
+ * in MITE_RING is completed. This can generate a lot of
+ * extra interrupts, but right now we update the values
+ * of buf_int_ptr and buf_int_count at each interrupt. A
+ * better method is to poll the MITE before each user
+ * "read()" to calculate the number of bytes available.
+ */
+ chcr |= CHCR_SET_LC_IE;
+ if (num_memory_bits == 32 && num_device_bits == 16) {
+ /*
+ * Doing a combined 32 and 16 bit byteswap gets the 16 bit
+ * samples into the fifo in the right order. Tested doing 32 bit
+ * memory to 16 bit device transfers to the analog out of a
+ * pxi-6281, which has mite version = 1, type = 4. This also
+ * works for dma reads from the counters on e-series boards.
+ */
+ chcr |= CHCR_BYTE_SWAP_DEVICE | CHCR_BYTE_SWAP_MEMORY;
+ }
+ if (mite_chan->dir == COMEDI_INPUT)
+ chcr |= CHCR_DEV_TO_MEM;
+
+ writel(chcr, mite->mite_io_addr + MITE_CHCR(mite_chan->channel));
+
+ /* to/from memory */
+ mcr = CR_RL(64) | CR_ASEQUP;
+ switch (num_memory_bits) {
+ case 8:
+ mcr |= CR_PSIZE8;
+ break;
+ case 16:
+ mcr |= CR_PSIZE16;
+ break;
+ case 32:
+ mcr |= CR_PSIZE32;
+ break;
+ default:
+ pr_warn("bug! invalid mem bit width for dma transfer\n");
+ break;
+ }
+ writel(mcr, mite->mite_io_addr + MITE_MCR(mite_chan->channel));
+
+ /* from/to device */
+ dcr = CR_RL(64) | CR_ASEQUP;
+ dcr |= CR_PORTIO | CR_AMDEVICE | CR_REQSDRQ(mite_chan->channel);
+ switch (num_device_bits) {
+ case 8:
+ dcr |= CR_PSIZE8;
+ break;
+ case 16:
+ dcr |= CR_PSIZE16;
+ break;
+ case 32:
+ dcr |= CR_PSIZE32;
+ break;
+ default:
+ pr_warn("bug! invalid dev bit width for dma transfer\n");
+ break;
+ }
+ writel(dcr, mite->mite_io_addr + MITE_DCR(mite_chan->channel));
+
+ /* reset the DAR */
+ writel(0, mite->mite_io_addr + MITE_DAR(mite_chan->channel));
+
+ /* the link is 32bits */
+ lkcr = CR_RL(64) | CR_ASEQUP | CR_PSIZE32;
+ writel(lkcr, mite->mite_io_addr + MITE_LKCR(mite_chan->channel));
+
+ /* starting address for link chaining */
+ writel(mite_chan->ring->descriptors_dma_addr,
+ mite->mite_io_addr + MITE_LKAR(mite_chan->channel));
+}
+EXPORT_SYMBOL_GPL(mite_prep_dma);
+
+static u32 mite_device_bytes_transferred(struct mite_channel *mite_chan)
+{
+ struct mite_struct *mite = mite_chan->mite;
+
+ return readl(mite->mite_io_addr + MITE_DAR(mite_chan->channel));
+}
+
+u32 mite_bytes_in_transit(struct mite_channel *mite_chan)
+{
+ struct mite_struct *mite = mite_chan->mite;
+
+ return readl(mite->mite_io_addr +
+ MITE_FCR(mite_chan->channel)) & 0x000000FF;
+}
+EXPORT_SYMBOL_GPL(mite_bytes_in_transit);
+
+/* returns lower bound for number of bytes transferred from device to memory */
+u32 mite_bytes_written_to_memory_lb(struct mite_channel *mite_chan)
+{
+ u32 device_byte_count;
+
+ device_byte_count = mite_device_bytes_transferred(mite_chan);
+ return device_byte_count - mite_bytes_in_transit(mite_chan);
+}
+EXPORT_SYMBOL_GPL(mite_bytes_written_to_memory_lb);
+
+/* returns upper bound for number of bytes transferred from device to memory */
+u32 mite_bytes_written_to_memory_ub(struct mite_channel *mite_chan)
+{
+ u32 in_transit_count;
+
+ in_transit_count = mite_bytes_in_transit(mite_chan);
+ return mite_device_bytes_transferred(mite_chan) - in_transit_count;
+}
+EXPORT_SYMBOL_GPL(mite_bytes_written_to_memory_ub);
+
+/* returns lower bound for number of bytes read from memory to device */
+u32 mite_bytes_read_from_memory_lb(struct mite_channel *mite_chan)
+{
+ u32 device_byte_count;
+
+ device_byte_count = mite_device_bytes_transferred(mite_chan);
+ return device_byte_count + mite_bytes_in_transit(mite_chan);
+}
+EXPORT_SYMBOL_GPL(mite_bytes_read_from_memory_lb);
+
+/* returns upper bound for number of bytes read from memory to device */
+u32 mite_bytes_read_from_memory_ub(struct mite_channel *mite_chan)
+{
+ u32 in_transit_count;
+
+ in_transit_count = mite_bytes_in_transit(mite_chan);
+ return mite_device_bytes_transferred(mite_chan) + in_transit_count;
+}
+EXPORT_SYMBOL_GPL(mite_bytes_read_from_memory_ub);
+
+unsigned mite_dma_tcr(struct mite_channel *mite_chan)
+{
+ struct mite_struct *mite = mite_chan->mite;
+
+ return readl(mite->mite_io_addr + MITE_TCR(mite_chan->channel));
+}
+EXPORT_SYMBOL_GPL(mite_dma_tcr);
+
+void mite_dma_disarm(struct mite_channel *mite_chan)
+{
+ struct mite_struct *mite = mite_chan->mite;
+ unsigned chor;
+
+ /* disarm */
+ chor = CHOR_ABORT;
+ writel(chor, mite->mite_io_addr + MITE_CHOR(mite_chan->channel));
+}
+EXPORT_SYMBOL_GPL(mite_dma_disarm);
+
+int mite_sync_input_dma(struct mite_channel *mite_chan,
+ struct comedi_subdevice *s)
+{
+ struct comedi_async *async = s->async;
+ int count;
+ unsigned int nbytes, old_alloc_count;
+
+ old_alloc_count = async->buf_write_alloc_count;
+ /* write alloc as much as we can */
+ comedi_buf_write_alloc(s, async->prealloc_bufsz);
+
+ nbytes = mite_bytes_written_to_memory_lb(mite_chan);
+ if ((int)(mite_bytes_written_to_memory_ub(mite_chan) -
+ old_alloc_count) > 0) {
+ dev_warn(s->device->class_dev,
+ "mite: DMA overwrite of free area\n");
+ async->events |= COMEDI_CB_OVERFLOW;
+ return -1;
+ }
+
+ count = nbytes - async->buf_write_count;
+ /* it's possible count will be negative due to
+ * conservative value returned by mite_bytes_written_to_memory_lb */
+ if (count <= 0)
+ return 0;
+
+ comedi_buf_write_free(s, count);
+ comedi_inc_scan_progress(s, count);
+ async->events |= COMEDI_CB_BLOCK;
+ return 0;
+}
+EXPORT_SYMBOL_GPL(mite_sync_input_dma);
+
+int mite_sync_output_dma(struct mite_channel *mite_chan,
+ struct comedi_subdevice *s)
+{
+ struct comedi_async *async = s->async;
+ struct comedi_cmd *cmd = &async->cmd;
+ u32 stop_count = cmd->stop_arg * comedi_bytes_per_scan(s);
+ unsigned int old_alloc_count = async->buf_read_alloc_count;
+ u32 nbytes_ub, nbytes_lb;
+ int count;
+
+ /* read alloc as much as we can */
+ comedi_buf_read_alloc(s, async->prealloc_bufsz);
+ nbytes_lb = mite_bytes_read_from_memory_lb(mite_chan);
+ if (cmd->stop_src == TRIG_COUNT && (int)(nbytes_lb - stop_count) > 0)
+ nbytes_lb = stop_count;
+ nbytes_ub = mite_bytes_read_from_memory_ub(mite_chan);
+ if (cmd->stop_src == TRIG_COUNT && (int)(nbytes_ub - stop_count) > 0)
+ nbytes_ub = stop_count;
+ if ((int)(nbytes_ub - old_alloc_count) > 0) {
+ dev_warn(s->device->class_dev, "mite: DMA underrun\n");
+ async->events |= COMEDI_CB_OVERFLOW;
+ return -1;
+ }
+ count = nbytes_lb - async->buf_read_count;
+ if (count <= 0)
+ return 0;
+
+ if (count) {
+ comedi_buf_read_free(s, count);
+ async->events |= COMEDI_CB_BLOCK;
+ }
+ return 0;
+}
+EXPORT_SYMBOL_GPL(mite_sync_output_dma);
+
+unsigned mite_get_status(struct mite_channel *mite_chan)
+{
+ struct mite_struct *mite = mite_chan->mite;
+ unsigned status;
+ unsigned long flags;
+
+ spin_lock_irqsave(&mite->lock, flags);
+ status = readl(mite->mite_io_addr + MITE_CHSR(mite_chan->channel));
+ if (status & CHSR_DONE) {
+ mite_chan->done = 1;
+ writel(CHOR_CLRDONE,
+ mite->mite_io_addr + MITE_CHOR(mite_chan->channel));
+ }
+ mmiowb();
+ spin_unlock_irqrestore(&mite->lock, flags);
+ return status;
+}
+EXPORT_SYMBOL_GPL(mite_get_status);
+
+int mite_done(struct mite_channel *mite_chan)
+{
+ struct mite_struct *mite = mite_chan->mite;
+ unsigned long flags;
+ int done;
+
+ mite_get_status(mite_chan);
+ spin_lock_irqsave(&mite->lock, flags);
+ done = mite_chan->done;
+ spin_unlock_irqrestore(&mite->lock, flags);
+ return done;
+}
+EXPORT_SYMBOL_GPL(mite_done);
+
+static int __init mite_module_init(void)
+{
+ return 0;
+}
+
+static void __exit mite_module_exit(void)
+{
+}
+
+module_init(mite_module_init);
+module_exit(mite_module_exit);
+
+MODULE_AUTHOR("Comedi http://www.comedi.org");
+MODULE_DESCRIPTION("Comedi low-level driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/comedi/drivers/mite.h b/drivers/staging/comedi/drivers/mite.h
new file mode 100644
index 000000000..b3ca7fc3a
--- /dev/null
+++ b/drivers/staging/comedi/drivers/mite.h
@@ -0,0 +1,347 @@
+/*
+ module/mite.h
+ Hardware driver for NI Mite PCI interface chip
+
+ COMEDI - Linux Control and Measurement Device Interface
+ Copyright (C) 1999 David A. Schleef <ds@schleef.org>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+*/
+
+#ifndef _MITE_H_
+#define _MITE_H_
+
+#include <linux/log2.h>
+#include <linux/slab.h>
+#include "../comedi_pci.h"
+
+#define PCIMIO_COMPAT
+
+#define MAX_MITE_DMA_CHANNELS 8
+
+struct mite_dma_descriptor {
+ __le32 count;
+ __le32 addr;
+ __le32 next;
+ u32 dar;
+};
+
+struct mite_dma_descriptor_ring {
+ struct device *hw_dev;
+ unsigned int n_links;
+ struct mite_dma_descriptor *descriptors;
+ dma_addr_t descriptors_dma_addr;
+};
+
+struct mite_channel {
+ struct mite_struct *mite;
+ unsigned channel;
+ int dir;
+ int done;
+ struct mite_dma_descriptor_ring *ring;
+};
+
+struct mite_struct {
+ struct pci_dev *pcidev;
+ resource_size_t mite_phys_addr;
+ void __iomem *mite_io_addr;
+ resource_size_t daq_phys_addr;
+ struct mite_channel channels[MAX_MITE_DMA_CHANNELS];
+ short channel_allocated[MAX_MITE_DMA_CHANNELS];
+ int num_channels;
+ unsigned fifo_size;
+ spinlock_t lock;
+};
+
+struct mite_struct *mite_alloc(struct pci_dev *pcidev);
+
+int mite_setup2(struct comedi_device *, struct mite_struct *, bool use_win1);
+
+static inline int mite_setup(struct comedi_device *dev,
+ struct mite_struct *mite)
+{
+ return mite_setup2(dev, mite, false);
+}
+
+void mite_detach(struct mite_struct *mite);
+struct mite_dma_descriptor_ring *mite_alloc_ring(struct mite_struct *mite);
+void mite_free_ring(struct mite_dma_descriptor_ring *ring);
+struct mite_channel *mite_request_channel_in_range(struct mite_struct *mite,
+ struct
+ mite_dma_descriptor_ring
+ *ring, unsigned min_channel,
+ unsigned max_channel);
+static inline struct mite_channel *mite_request_channel(struct mite_struct
+ *mite,
+ struct
+ mite_dma_descriptor_ring
+ *ring)
+{
+ return mite_request_channel_in_range(mite, ring, 0,
+ mite->num_channels - 1);
+}
+
+void mite_release_channel(struct mite_channel *mite_chan);
+
+unsigned mite_dma_tcr(struct mite_channel *mite_chan);
+void mite_dma_arm(struct mite_channel *mite_chan);
+void mite_dma_disarm(struct mite_channel *mite_chan);
+int mite_sync_input_dma(struct mite_channel *mite_chan,
+ struct comedi_subdevice *s);
+int mite_sync_output_dma(struct mite_channel *mite_chan,
+ struct comedi_subdevice *s);
+u32 mite_bytes_written_to_memory_lb(struct mite_channel *mite_chan);
+u32 mite_bytes_written_to_memory_ub(struct mite_channel *mite_chan);
+u32 mite_bytes_read_from_memory_lb(struct mite_channel *mite_chan);
+u32 mite_bytes_read_from_memory_ub(struct mite_channel *mite_chan);
+u32 mite_bytes_in_transit(struct mite_channel *mite_chan);
+unsigned mite_get_status(struct mite_channel *mite_chan);
+int mite_done(struct mite_channel *mite_chan);
+
+void mite_prep_dma(struct mite_channel *mite_chan,
+ unsigned int num_device_bits, unsigned int num_memory_bits);
+int mite_buf_change(struct mite_dma_descriptor_ring *ring,
+ struct comedi_subdevice *s);
+
+enum mite_registers {
+ /* The bits 0x90180700 in MITE_UNKNOWN_DMA_BURST_REG can be
+ written and read back. The bits 0x1f always read as 1.
+ The rest always read as zero. */
+ MITE_UNKNOWN_DMA_BURST_REG = 0x28,
+ MITE_IODWBSR = 0xc0, /* IO Device Window Base Size Register */
+ MITE_IODWBSR_1 = 0xc4, /* IO Device Window Base Size Register 1 */
+ MITE_IODWCR_1 = 0xf4,
+ MITE_PCI_CONFIG_OFFSET = 0x300,
+ MITE_CSIGR = 0x460 /* chip signature */
+};
+
+#define MITE_CHAN(x) (0x500 + 0x100 * (x))
+#define MITE_CHOR(x) (0x00 + MITE_CHAN(x)) /* channel operation */
+#define MITE_CHCR(x) (0x04 + MITE_CHAN(x)) /* channel control */
+#define MITE_TCR(x) (0x08 + MITE_CHAN(x)) /* transfer count */
+#define MITE_MCR(x) (0x0c + MITE_CHAN(x)) /* memory configuration */
+#define MITE_MAR(x) (0x10 + MITE_CHAN(x)) /* memory address */
+#define MITE_DCR(x) (0x14 + MITE_CHAN(x)) /* device configuration */
+#define MITE_DAR(x) (0x18 + MITE_CHAN(x)) /* device address */
+#define MITE_LKCR(x) (0x1c + MITE_CHAN(x)) /* link configuration */
+#define MITE_LKAR(x) (0x20 + MITE_CHAN(x)) /* link address */
+#define MITE_LLKAR(x) (0x24 + MITE_CHAN(x)) /* see tnt5002 manual */
+#define MITE_BAR(x) (0x28 + MITE_CHAN(x)) /* base address */
+#define MITE_BCR(x) (0x2c + MITE_CHAN(x)) /* base count */
+#define MITE_SAR(x) (0x30 + MITE_CHAN(x)) /* ? address */
+#define MITE_WSCR(x) (0x34 + MITE_CHAN(x)) /* ? */
+#define MITE_WSER(x) (0x38 + MITE_CHAN(x)) /* ? */
+#define MITE_CHSR(x) (0x3c + MITE_CHAN(x)) /* channel status */
+#define MITE_FCR(x) (0x40 + MITE_CHAN(x)) /* fifo count */
+
+enum MITE_IODWBSR_bits {
+ WENAB = 0x80, /* window enable */
+};
+
+static inline unsigned MITE_IODWBSR_1_WSIZE_bits(unsigned size)
+{
+ unsigned order = 0;
+
+ BUG_ON(size == 0);
+ order = ilog2(size);
+ BUG_ON(order < 1);
+ return (order - 1) & 0x1f;
+}
+
+enum MITE_UNKNOWN_DMA_BURST_bits {
+ UNKNOWN_DMA_BURST_ENABLE_BITS = 0x600
+};
+
+static inline int mite_csigr_version(u32 csigr_bits)
+{
+ return csigr_bits & 0xf;
+};
+
+static inline int mite_csigr_type(u32 csigr_bits)
+{ /* original mite = 0, minimite = 1 */
+ return (csigr_bits >> 4) & 0xf;
+};
+
+static inline int mite_csigr_mmode(u32 csigr_bits)
+{ /* mite mode, minimite = 1 */
+ return (csigr_bits >> 8) & 0x3;
+};
+
+static inline int mite_csigr_imode(u32 csigr_bits)
+{ /* cpu port interface mode, pci = 0x3 */
+ return (csigr_bits >> 12) & 0x3;
+};
+
+static inline int mite_csigr_dmac(u32 csigr_bits)
+{ /* number of dma channels */
+ return (csigr_bits >> 16) & 0xf;
+};
+
+static inline int mite_csigr_wpdep(u32 csigr_bits)
+{ /* write post fifo depth */
+ unsigned int wpdep_bits = (csigr_bits >> 20) & 0x7;
+
+ return (wpdep_bits) ? (1 << (wpdep_bits - 1)) : 0;
+}
+
+static inline int mite_csigr_wins(u32 csigr_bits)
+{
+ return (csigr_bits >> 24) & 0x1f;
+};
+
+static inline int mite_csigr_iowins(u32 csigr_bits)
+{ /* number of io windows */
+ return (csigr_bits >> 29) & 0x7;
+};
+
+enum MITE_MCR_bits {
+ MCRPON = 0,
+};
+
+enum MITE_DCR_bits {
+ DCR_NORMAL = (1 << 29),
+ DCRPON = 0,
+};
+
+enum MITE_CHOR_bits {
+ CHOR_DMARESET = (1 << 31),
+ CHOR_SET_SEND_TC = (1 << 11),
+ CHOR_CLR_SEND_TC = (1 << 10),
+ CHOR_SET_LPAUSE = (1 << 9),
+ CHOR_CLR_LPAUSE = (1 << 8),
+ CHOR_CLRDONE = (1 << 7),
+ CHOR_CLRRB = (1 << 6),
+ CHOR_CLRLC = (1 << 5),
+ CHOR_FRESET = (1 << 4),
+ CHOR_ABORT = (1 << 3), /* stop without emptying fifo */
+ CHOR_STOP = (1 << 2), /* stop after emptying fifo */
+ CHOR_CONT = (1 << 1),
+ CHOR_START = (1 << 0),
+ CHOR_PON = (CHOR_CLR_SEND_TC | CHOR_CLR_LPAUSE),
+};
+
+enum MITE_CHCR_bits {
+ CHCR_SET_DMA_IE = (1 << 31),
+ CHCR_CLR_DMA_IE = (1 << 30),
+ CHCR_SET_LINKP_IE = (1 << 29),
+ CHCR_CLR_LINKP_IE = (1 << 28),
+ CHCR_SET_SAR_IE = (1 << 27),
+ CHCR_CLR_SAR_IE = (1 << 26),
+ CHCR_SET_DONE_IE = (1 << 25),
+ CHCR_CLR_DONE_IE = (1 << 24),
+ CHCR_SET_MRDY_IE = (1 << 23),
+ CHCR_CLR_MRDY_IE = (1 << 22),
+ CHCR_SET_DRDY_IE = (1 << 21),
+ CHCR_CLR_DRDY_IE = (1 << 20),
+ CHCR_SET_LC_IE = (1 << 19),
+ CHCR_CLR_LC_IE = (1 << 18),
+ CHCR_SET_CONT_RB_IE = (1 << 17),
+ CHCR_CLR_CONT_RB_IE = (1 << 16),
+ CHCR_FIFODIS = (1 << 15),
+ CHCR_FIFO_ON = 0,
+ CHCR_BURSTEN = (1 << 14),
+ CHCR_NO_BURSTEN = 0,
+ CHCR_BYTE_SWAP_DEVICE = (1 << 6),
+ CHCR_BYTE_SWAP_MEMORY = (1 << 4),
+ CHCR_DIR = (1 << 3),
+ CHCR_DEV_TO_MEM = CHCR_DIR,
+ CHCR_MEM_TO_DEV = 0,
+ CHCR_NORMAL = (0 << 0),
+ CHCR_CONTINUE = (1 << 0),
+ CHCR_RINGBUFF = (2 << 0),
+ CHCR_LINKSHORT = (4 << 0),
+ CHCR_LINKLONG = (5 << 0),
+ CHCRPON =
+ (CHCR_CLR_DMA_IE | CHCR_CLR_LINKP_IE | CHCR_CLR_SAR_IE |
+ CHCR_CLR_DONE_IE | CHCR_CLR_MRDY_IE | CHCR_CLR_DRDY_IE |
+ CHCR_CLR_LC_IE | CHCR_CLR_CONT_RB_IE),
+};
+
+enum ConfigRegister_bits {
+ CR_REQS_MASK = 0x7 << 16,
+ CR_ASEQDONT = 0x0 << 10,
+ CR_ASEQUP = 0x1 << 10,
+ CR_ASEQDOWN = 0x2 << 10,
+ CR_ASEQ_MASK = 0x3 << 10,
+ CR_PSIZE8 = (1 << 8),
+ CR_PSIZE16 = (2 << 8),
+ CR_PSIZE32 = (3 << 8),
+ CR_PORTCPU = (0 << 6),
+ CR_PORTIO = (1 << 6),
+ CR_PORTVXI = (2 << 6),
+ CR_PORTMXI = (3 << 6),
+ CR_AMDEVICE = (1 << 0),
+};
+static inline int CR_REQS(int source)
+{
+ return (source & 0x7) << 16;
+};
+
+static inline int CR_REQSDRQ(unsigned drq_line)
+{
+ /* This also works on m-series when
+ using channels (drq_line) 4 or 5. */
+ return CR_REQS((drq_line & 0x3) | 0x4);
+}
+
+static inline int CR_RL(unsigned int retry_limit)
+{
+ int value = 0;
+
+ if (retry_limit)
+ value = 1 + ilog2(retry_limit);
+ if (value > 0x7)
+ value = 0x7;
+ return (value & 0x7) << 21;
+}
+
+enum CHSR_bits {
+ CHSR_INT = (1 << 31),
+ CHSR_LPAUSES = (1 << 29),
+ CHSR_SARS = (1 << 27),
+ CHSR_DONE = (1 << 25),
+ CHSR_MRDY = (1 << 23),
+ CHSR_DRDY = (1 << 21),
+ CHSR_LINKC = (1 << 19),
+ CHSR_CONTS_RB = (1 << 17),
+ CHSR_ERROR = (1 << 15),
+ CHSR_SABORT = (1 << 14),
+ CHSR_HABORT = (1 << 13),
+ CHSR_STOPS = (1 << 12),
+ CHSR_OPERR_mask = (3 << 10),
+ CHSR_OPERR_NOERROR = (0 << 10),
+ CHSR_OPERR_FIFOERROR = (1 << 10),
+ CHSR_OPERR_LINKERROR = (1 << 10), /* ??? */
+ CHSR_XFERR = (1 << 9),
+ CHSR_END = (1 << 8),
+ CHSR_DRQ1 = (1 << 7),
+ CHSR_DRQ0 = (1 << 6),
+ CHSR_LxERR_mask = (3 << 4),
+ CHSR_LBERR = (1 << 4),
+ CHSR_LRERR = (2 << 4),
+ CHSR_LOERR = (3 << 4),
+ CHSR_MxERR_mask = (3 << 2),
+ CHSR_MBERR = (1 << 2),
+ CHSR_MRERR = (2 << 2),
+ CHSR_MOERR = (3 << 2),
+ CHSR_DxERR_mask = (3 << 0),
+ CHSR_DBERR = (1 << 0),
+ CHSR_DRERR = (2 << 0),
+ CHSR_DOERR = (3 << 0),
+};
+
+static inline void mite_dma_reset(struct mite_channel *mite_chan)
+{
+ writel(CHOR_DMARESET | CHOR_FRESET,
+ mite_chan->mite->mite_io_addr + MITE_CHOR(mite_chan->channel));
+};
+
+#endif
diff --git a/drivers/staging/comedi/drivers/mpc624.c b/drivers/staging/comedi/drivers/mpc624.c
new file mode 100644
index 000000000..0207b8edf
--- /dev/null
+++ b/drivers/staging/comedi/drivers/mpc624.c
@@ -0,0 +1,357 @@
+/*
+ comedi/drivers/mpc624.c
+ Hardware driver for a Micro/sys inc. MPC-624 PC/104 board
+
+ COMEDI - Linux Control and Measurement Device Interface
+ Copyright (C) 2000 David A. Schleef <ds@schleef.org>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+*/
+/*
+Driver: mpc624
+Description: Micro/sys MPC-624 PC/104 board
+Devices: [Micro/sys] MPC-624 (mpc624)
+Author: Stanislaw Raczynski <sraczynski@op.pl>
+Updated: Thu, 15 Sep 2005 12:01:18 +0200
+Status: working
+
+ The Micro/sys MPC-624 board is based on the LTC2440 24-bit sigma-delta
+ ADC chip.
+
+ Subdevices supported by the driver:
+ - Analog In: supported
+ - Digital I/O: not supported
+ - LEDs: not supported
+ - EEPROM: not supported
+
+Configuration Options:
+ [0] - I/O base address
+ [1] - conversion rate
+ Conversion rate RMS noise Effective Number Of Bits
+ 0 3.52kHz 23uV 17
+ 1 1.76kHz 3.5uV 20
+ 2 880Hz 2uV 21.3
+ 3 440Hz 1.4uV 21.8
+ 4 220Hz 1uV 22.4
+ 5 110Hz 750uV 22.9
+ 6 55Hz 510nV 23.4
+ 7 27.5Hz 375nV 24
+ 8 13.75Hz 250nV 24.4
+ 9 6.875Hz 200nV 24.6
+ [2] - voltage range
+ 0 -1.01V .. +1.01V
+ 1 -10.1V .. +10.1V
+*/
+
+#include <linux/module.h>
+#include "../comedidev.h"
+
+#include <linux/delay.h>
+
+/* Offsets of different ports */
+#define MPC624_MASTER_CONTROL 0 /* not used */
+#define MPC624_GNMUXCH 1 /* Gain, Mux, Channel of ADC */
+#define MPC624_ADC 2 /* read/write to/from ADC */
+#define MPC624_EE 3 /* read/write to/from serial EEPROM via I2C */
+#define MPC624_LEDS 4 /* write to LEDs */
+#define MPC624_DIO 5 /* read/write to/from digital I/O ports */
+#define MPC624_IRQ_MASK 6 /* IRQ masking enable/disable */
+
+/* Register bits' names */
+#define MPC624_ADBUSY (1<<5)
+#define MPC624_ADSDO (1<<4)
+#define MPC624_ADFO (1<<3)
+#define MPC624_ADCS (1<<2)
+#define MPC624_ADSCK (1<<1)
+#define MPC624_ADSDI (1<<0)
+
+/* SDI Speed/Resolution Programming bits */
+#define MPC624_OSR4 (1<<31)
+#define MPC624_OSR3 (1<<30)
+#define MPC624_OSR2 (1<<29)
+#define MPC624_OSR1 (1<<28)
+#define MPC624_OSR0 (1<<27)
+
+/* 32-bit output value bits' names */
+#define MPC624_EOC_BIT (1<<31)
+#define MPC624_DMY_BIT (1<<30)
+#define MPC624_SGN_BIT (1<<29)
+
+/* Conversion speeds */
+/* OSR4 OSR3 OSR2 OSR1 OSR0 Conversion rate RMS noise ENOB^
+ * X 0 0 0 1 3.52kHz 23uV 17
+ * X 0 0 1 0 1.76kHz 3.5uV 20
+ * X 0 0 1 1 880Hz 2uV 21.3
+ * X 0 1 0 0 440Hz 1.4uV 21.8
+ * X 0 1 0 1 220Hz 1uV 22.4
+ * X 0 1 1 0 110Hz 750uV 22.9
+ * X 0 1 1 1 55Hz 510nV 23.4
+ * X 1 0 0 0 27.5Hz 375nV 24
+ * X 1 0 0 1 13.75Hz 250nV 24.4
+ * X 1 1 1 1 6.875Hz 200nV 24.6
+ *
+ * ^ - Effective Number Of Bits
+ */
+
+#define MPC624_SPEED_3_52_kHz (MPC624_OSR4 | MPC624_OSR0)
+#define MPC624_SPEED_1_76_kHz (MPC624_OSR4 | MPC624_OSR1)
+#define MPC624_SPEED_880_Hz (MPC624_OSR4 | MPC624_OSR1 | MPC624_OSR0)
+#define MPC624_SPEED_440_Hz (MPC624_OSR4 | MPC624_OSR2)
+#define MPC624_SPEED_220_Hz (MPC624_OSR4 | MPC624_OSR2 | MPC624_OSR0)
+#define MPC624_SPEED_110_Hz (MPC624_OSR4 | MPC624_OSR2 | MPC624_OSR1)
+#define MPC624_SPEED_55_Hz \
+ (MPC624_OSR4 | MPC624_OSR2 | MPC624_OSR1 | MPC624_OSR0)
+#define MPC624_SPEED_27_5_Hz (MPC624_OSR4 | MPC624_OSR3)
+#define MPC624_SPEED_13_75_Hz (MPC624_OSR4 | MPC624_OSR3 | MPC624_OSR0)
+#define MPC624_SPEED_6_875_Hz \
+ (MPC624_OSR4 | MPC624_OSR3 | MPC624_OSR2 | MPC624_OSR1 | MPC624_OSR0)
+/* -------------------------------------------------------------------------- */
+struct mpc624_private {
+ /* set by mpc624_attach() from driver's parameters */
+ unsigned long int ulConvertionRate;
+};
+
+/* -------------------------------------------------------------------------- */
+static const struct comedi_lrange range_mpc624_bipolar1 = {
+ 1,
+ {
+/* BIP_RANGE(1.01) this is correct, */
+ /* but my MPC-624 actually seems to have a range of 2.02 */
+ BIP_RANGE(2.02)
+ }
+};
+
+static const struct comedi_lrange range_mpc624_bipolar10 = {
+ 1,
+ {
+/* BIP_RANGE(10.1) this is correct, */
+ /* but my MPC-624 actually seems to have a range of 20.2 */
+ BIP_RANGE(20.2)
+ }
+};
+
+static int mpc624_ai_eoc(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned long context)
+{
+ unsigned char status;
+
+ status = inb(dev->iobase + MPC624_ADC);
+ if ((status & MPC624_ADBUSY) == 0)
+ return 0;
+ return -EBUSY;
+}
+
+static int mpc624_ai_rinsn(struct comedi_device *dev,
+ struct comedi_subdevice *s, struct comedi_insn *insn,
+ unsigned int *data)
+{
+ struct mpc624_private *devpriv = dev->private;
+ int n, i;
+ unsigned long int data_in, data_out;
+ int ret;
+
+ /*
+ * WARNING:
+ * We always write 0 to GNSWA bit, so the channel range is +-/10.1Vdc
+ */
+ outb(insn->chanspec, dev->iobase + MPC624_GNMUXCH);
+
+ for (n = 0; n < insn->n; n++) {
+ /* Trigger the conversion */
+ outb(MPC624_ADSCK, dev->iobase + MPC624_ADC);
+ udelay(1);
+ outb(MPC624_ADCS | MPC624_ADSCK, dev->iobase + MPC624_ADC);
+ udelay(1);
+ outb(0, dev->iobase + MPC624_ADC);
+ udelay(1);
+
+ /* Wait for the conversion to end */
+ ret = comedi_timeout(dev, s, insn, mpc624_ai_eoc, 0);
+ if (ret)
+ return ret;
+
+ /* Start reading data */
+ data_in = 0;
+ data_out = devpriv->ulConvertionRate;
+ udelay(1);
+ for (i = 0; i < 32; i++) {
+ /* Set the clock low */
+ outb(0, dev->iobase + MPC624_ADC);
+ udelay(1);
+
+ if (data_out & (1 << 31)) { /* the next bit is a 1 */
+ /* Set the ADSDI line (send to MPC624) */
+ outb(MPC624_ADSDI, dev->iobase + MPC624_ADC);
+ udelay(1);
+ /* Set the clock high */
+ outb(MPC624_ADSCK | MPC624_ADSDI,
+ dev->iobase + MPC624_ADC);
+ } else { /* the next bit is a 0 */
+
+ /* Set the ADSDI line (send to MPC624) */
+ outb(0, dev->iobase + MPC624_ADC);
+ udelay(1);
+ /* Set the clock high */
+ outb(MPC624_ADSCK, dev->iobase + MPC624_ADC);
+ }
+ /* Read ADSDO on high clock (receive from MPC624) */
+ udelay(1);
+ data_in <<= 1;
+ data_in |=
+ (inb(dev->iobase + MPC624_ADC) & MPC624_ADSDO) >> 4;
+ udelay(1);
+
+ data_out <<= 1;
+ }
+
+ /*
+ * Received 32-bit long value consist of:
+ * 31: EOC -
+ * (End Of Transmission) bit - should be 0
+ * 30: DMY
+ * (Dummy) bit - should be 0
+ * 29: SIG
+ * (Sign) bit- 1 if the voltage is positive,
+ * 0 if negative
+ * 28: MSB
+ * (Most Significant Bit) - the first bit of
+ * the conversion result
+ * ....
+ * 05: LSB
+ * (Least Significant Bit)- the last bit of the
+ * conversion result
+ * 04-00: sub-LSB
+ * - sub-LSBs are basically noise, but when
+ * averaged properly, they can increase conversion
+ * precision up to 29 bits; they can be discarded
+ * without loss of resolution.
+ */
+
+ if (data_in & MPC624_EOC_BIT)
+ dev_dbg(dev->class_dev,
+ "EOC bit is set (data_in=%lu)!", data_in);
+ if (data_in & MPC624_DMY_BIT)
+ dev_dbg(dev->class_dev,
+ "DMY bit is set (data_in=%lu)!", data_in);
+ if (data_in & MPC624_SGN_BIT) { /* Volatge is positive */
+ /*
+ * comedi operates on unsigned numbers, so mask off EOC
+ * and DMY and don't clear the SGN bit
+ */
+ data_in &= 0x3FFFFFFF;
+ data[n] = data_in;
+ } else { /* The voltage is negative */
+ /*
+ * data_in contains a number in 30-bit two's complement
+ * code and we must deal with it
+ */
+ data_in |= MPC624_SGN_BIT;
+ data_in = ~data_in;
+ data_in += 1;
+ data_in &= ~(MPC624_EOC_BIT | MPC624_DMY_BIT);
+ /* clear EOC and DMY bits */
+ data_in = 0x20000000 - data_in;
+ data[n] = data_in;
+ }
+ }
+
+ /* Return the number of samples read/written */
+ return n;
+}
+
+static int mpc624_attach(struct comedi_device *dev, struct comedi_devconfig *it)
+{
+ struct mpc624_private *devpriv;
+ struct comedi_subdevice *s;
+ int ret;
+
+ ret = comedi_request_region(dev, it->options[0], 0x10);
+ if (ret)
+ return ret;
+
+ devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
+ if (!devpriv)
+ return -ENOMEM;
+
+ switch (it->options[1]) {
+ case 0:
+ devpriv->ulConvertionRate = MPC624_SPEED_3_52_kHz;
+ break;
+ case 1:
+ devpriv->ulConvertionRate = MPC624_SPEED_1_76_kHz;
+ break;
+ case 2:
+ devpriv->ulConvertionRate = MPC624_SPEED_880_Hz;
+ break;
+ case 3:
+ devpriv->ulConvertionRate = MPC624_SPEED_440_Hz;
+ break;
+ case 4:
+ devpriv->ulConvertionRate = MPC624_SPEED_220_Hz;
+ break;
+ case 5:
+ devpriv->ulConvertionRate = MPC624_SPEED_110_Hz;
+ break;
+ case 6:
+ devpriv->ulConvertionRate = MPC624_SPEED_55_Hz;
+ break;
+ case 7:
+ devpriv->ulConvertionRate = MPC624_SPEED_27_5_Hz;
+ break;
+ case 8:
+ devpriv->ulConvertionRate = MPC624_SPEED_13_75_Hz;
+ break;
+ case 9:
+ devpriv->ulConvertionRate = MPC624_SPEED_6_875_Hz;
+ break;
+ default:
+ devpriv->ulConvertionRate = MPC624_SPEED_3_52_kHz;
+ }
+
+ ret = comedi_alloc_subdevices(dev, 1);
+ if (ret)
+ return ret;
+
+ s = &dev->subdevices[0];
+ s->type = COMEDI_SUBD_AI;
+ s->subdev_flags = SDF_READABLE | SDF_DIFF;
+ s->n_chan = 8;
+ switch (it->options[1]) {
+ default:
+ s->maxdata = 0x3FFFFFFF;
+ }
+
+ switch (it->options[1]) {
+ case 0:
+ s->range_table = &range_mpc624_bipolar1;
+ break;
+ default:
+ s->range_table = &range_mpc624_bipolar10;
+ }
+ s->len_chanlist = 1;
+ s->insn_read = mpc624_ai_rinsn;
+
+ return 0;
+}
+
+static struct comedi_driver mpc624_driver = {
+ .driver_name = "mpc624",
+ .module = THIS_MODULE,
+ .attach = mpc624_attach,
+ .detach = comedi_legacy_detach,
+};
+module_comedi_driver(mpc624_driver);
+
+MODULE_AUTHOR("Comedi http://www.comedi.org");
+MODULE_DESCRIPTION("Comedi low-level driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/comedi/drivers/multiq3.c b/drivers/staging/comedi/drivers/multiq3.c
new file mode 100644
index 000000000..847121921
--- /dev/null
+++ b/drivers/staging/comedi/drivers/multiq3.c
@@ -0,0 +1,289 @@
+/*
+ comedi/drivers/multiq3.c
+ Hardware driver for Quanser Consulting MultiQ-3 board
+
+ COMEDI - Linux Control and Measurement Device Interface
+ Copyright (C) 1999 Anders Blomdell <anders.blomdell@control.lth.se>
+
+ This program is free software; you can redistribute it and/or 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.
+ */
+/*
+Driver: multiq3
+Description: Quanser Consulting MultiQ-3
+Author: Anders Blomdell <anders.blomdell@control.lth.se>
+Status: works
+Devices: [Quanser Consulting] MultiQ-3 (multiq3)
+
+*/
+
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include "../comedidev.h"
+
+/*
+ * MULTIQ-3 port offsets
+ */
+#define MULTIQ3_DIGIN_PORT 0
+#define MULTIQ3_DIGOUT_PORT 0
+#define MULTIQ3_DAC_DATA 2
+#define MULTIQ3_AD_DATA 4
+#define MULTIQ3_AD_CS 4
+#define MULTIQ3_STATUS 6
+#define MULTIQ3_CONTROL 6
+#define MULTIQ3_CLK_DATA 8
+#define MULTIQ3_ENC_DATA 12
+#define MULTIQ3_ENC_CONTROL 14
+
+/*
+ * flags for CONTROL register
+ */
+#define MULTIQ3_AD_MUX_EN 0x0040
+#define MULTIQ3_AD_AUTOZ 0x0080
+#define MULTIQ3_AD_AUTOCAL 0x0100
+#define MULTIQ3_AD_SH 0x0200
+#define MULTIQ3_AD_CLOCK_4M 0x0400
+#define MULTIQ3_DA_LOAD 0x1800
+
+#define MULTIQ3_CONTROL_MUST 0x0600
+
+/*
+ * flags for STATUS register
+ */
+#define MULTIQ3_STATUS_EOC 0x008
+#define MULTIQ3_STATUS_EOC_I 0x010
+
+/*
+ * flags for encoder control
+ */
+#define MULTIQ3_CLOCK_DATA 0x00
+#define MULTIQ3_CLOCK_SETUP 0x18
+#define MULTIQ3_INPUT_SETUP 0x41
+#define MULTIQ3_QUAD_X4 0x38
+#define MULTIQ3_BP_RESET 0x01
+#define MULTIQ3_CNTR_RESET 0x02
+#define MULTIQ3_TRSFRPR_CTR 0x08
+#define MULTIQ3_TRSFRCNTR_OL 0x10
+#define MULTIQ3_EFLAG_RESET 0x06
+
+#define MULTIQ3_TIMEOUT 30
+
+static int multiq3_ai_status(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned long context)
+{
+ unsigned int status;
+
+ status = inw(dev->iobase + MULTIQ3_STATUS);
+ if (status & context)
+ return 0;
+ return -EBUSY;
+}
+
+static int multiq3_ai_insn_read(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn, unsigned int *data)
+{
+ int n;
+ int chan;
+ unsigned int hi, lo;
+ int ret;
+
+ chan = CR_CHAN(insn->chanspec);
+ outw(MULTIQ3_CONTROL_MUST | MULTIQ3_AD_MUX_EN | (chan << 3),
+ dev->iobase + MULTIQ3_CONTROL);
+
+ ret = comedi_timeout(dev, s, insn, multiq3_ai_status,
+ MULTIQ3_STATUS_EOC);
+ if (ret)
+ return ret;
+
+ for (n = 0; n < insn->n; n++) {
+ outw(0, dev->iobase + MULTIQ3_AD_CS);
+
+ ret = comedi_timeout(dev, s, insn, multiq3_ai_status,
+ MULTIQ3_STATUS_EOC_I);
+ if (ret)
+ return ret;
+
+ hi = inb(dev->iobase + MULTIQ3_AD_CS);
+ lo = inb(dev->iobase + MULTIQ3_AD_CS);
+ data[n] = (((hi << 8) | lo) + 0x1000) & 0x1fff;
+ }
+
+ return n;
+}
+
+static int multiq3_ao_insn_write(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ unsigned int chan = CR_CHAN(insn->chanspec);
+ unsigned int val = s->readback[chan];
+ int i;
+
+ for (i = 0; i < insn->n; i++) {
+ val = data[i];
+ outw(MULTIQ3_CONTROL_MUST | MULTIQ3_DA_LOAD | chan,
+ dev->iobase + MULTIQ3_CONTROL);
+ outw(val, dev->iobase + MULTIQ3_DAC_DATA);
+ outw(MULTIQ3_CONTROL_MUST, dev->iobase + MULTIQ3_CONTROL);
+ }
+ s->readback[chan] = val;
+
+ return insn->n;
+}
+
+static int multiq3_di_insn_bits(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn, unsigned int *data)
+{
+ data[1] = inw(dev->iobase + MULTIQ3_DIGIN_PORT);
+
+ return insn->n;
+}
+
+static int multiq3_do_insn_bits(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ if (comedi_dio_update_state(s, data))
+ outw(s->state, dev->iobase + MULTIQ3_DIGOUT_PORT);
+
+ data[1] = s->state;
+
+ return insn->n;
+}
+
+static int multiq3_encoder_insn_read(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ int chan = CR_CHAN(insn->chanspec);
+ int control = MULTIQ3_CONTROL_MUST | MULTIQ3_AD_MUX_EN | (chan << 3);
+ int value;
+ int n;
+
+ for (n = 0; n < insn->n; n++) {
+ outw(control, dev->iobase + MULTIQ3_CONTROL);
+ outb(MULTIQ3_BP_RESET, dev->iobase + MULTIQ3_ENC_CONTROL);
+ outb(MULTIQ3_TRSFRCNTR_OL, dev->iobase + MULTIQ3_ENC_CONTROL);
+ value = inb(dev->iobase + MULTIQ3_ENC_DATA);
+ value |= (inb(dev->iobase + MULTIQ3_ENC_DATA) << 8);
+ value |= (inb(dev->iobase + MULTIQ3_ENC_DATA) << 16);
+ data[n] = (value + 0x800000) & 0xffffff;
+ }
+
+ return n;
+}
+
+static void encoder_reset(struct comedi_device *dev)
+{
+ struct comedi_subdevice *s = &dev->subdevices[4];
+ int chan;
+
+ for (chan = 0; chan < s->n_chan; chan++) {
+ int control =
+ MULTIQ3_CONTROL_MUST | MULTIQ3_AD_MUX_EN | (chan << 3);
+ outw(control, dev->iobase + MULTIQ3_CONTROL);
+ outb(MULTIQ3_EFLAG_RESET, dev->iobase + MULTIQ3_ENC_CONTROL);
+ outb(MULTIQ3_BP_RESET, dev->iobase + MULTIQ3_ENC_CONTROL);
+ outb(MULTIQ3_CLOCK_DATA, dev->iobase + MULTIQ3_ENC_DATA);
+ outb(MULTIQ3_CLOCK_SETUP, dev->iobase + MULTIQ3_ENC_CONTROL);
+ outb(MULTIQ3_INPUT_SETUP, dev->iobase + MULTIQ3_ENC_CONTROL);
+ outb(MULTIQ3_QUAD_X4, dev->iobase + MULTIQ3_ENC_CONTROL);
+ outb(MULTIQ3_CNTR_RESET, dev->iobase + MULTIQ3_ENC_CONTROL);
+ }
+}
+
+static int multiq3_attach(struct comedi_device *dev,
+ struct comedi_devconfig *it)
+{
+ struct comedi_subdevice *s;
+ int ret;
+
+ ret = comedi_request_region(dev, it->options[0], 0x10);
+ if (ret)
+ return ret;
+
+ ret = comedi_alloc_subdevices(dev, 5);
+ if (ret)
+ return ret;
+
+ s = &dev->subdevices[0];
+ /* ai subdevice */
+ s->type = COMEDI_SUBD_AI;
+ s->subdev_flags = SDF_READABLE | SDF_GROUND;
+ s->n_chan = 8;
+ s->insn_read = multiq3_ai_insn_read;
+ s->maxdata = 0x1fff;
+ s->range_table = &range_bipolar5;
+
+ s = &dev->subdevices[1];
+ /* ao subdevice */
+ s->type = COMEDI_SUBD_AO;
+ s->subdev_flags = SDF_WRITABLE;
+ s->n_chan = 8;
+ s->maxdata = 0xfff;
+ s->range_table = &range_bipolar5;
+ s->insn_write = multiq3_ao_insn_write;
+
+ ret = comedi_alloc_subdev_readback(s);
+ if (ret)
+ return ret;
+
+ s = &dev->subdevices[2];
+ /* di subdevice */
+ s->type = COMEDI_SUBD_DI;
+ s->subdev_flags = SDF_READABLE;
+ s->n_chan = 16;
+ s->insn_bits = multiq3_di_insn_bits;
+ s->maxdata = 1;
+ s->range_table = &range_digital;
+
+ s = &dev->subdevices[3];
+ /* do subdevice */
+ s->type = COMEDI_SUBD_DO;
+ s->subdev_flags = SDF_WRITABLE;
+ s->n_chan = 16;
+ s->insn_bits = multiq3_do_insn_bits;
+ s->maxdata = 1;
+ s->range_table = &range_digital;
+ s->state = 0;
+
+ s = &dev->subdevices[4];
+ /* encoder (counter) subdevice */
+ s->type = COMEDI_SUBD_COUNTER;
+ s->subdev_flags = SDF_READABLE | SDF_LSAMPL;
+ s->n_chan = it->options[2] * 2;
+ s->insn_read = multiq3_encoder_insn_read;
+ s->maxdata = 0xffffff;
+ s->range_table = &range_unknown;
+
+ encoder_reset(dev);
+
+ return 0;
+}
+
+static struct comedi_driver multiq3_driver = {
+ .driver_name = "multiq3",
+ .module = THIS_MODULE,
+ .attach = multiq3_attach,
+ .detach = comedi_legacy_detach,
+};
+module_comedi_driver(multiq3_driver);
+
+MODULE_AUTHOR("Comedi http://www.comedi.org");
+MODULE_DESCRIPTION("Comedi low-level driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/comedi/drivers/ni_6527.c b/drivers/staging/comedi/drivers/ni_6527.c
new file mode 100644
index 000000000..62a817e4c
--- /dev/null
+++ b/drivers/staging/comedi/drivers/ni_6527.c
@@ -0,0 +1,500 @@
+/*
+ * ni_6527.c
+ * Comedi driver for National Instruments PCI-6527
+ *
+ * COMEDI - Linux Control and Measurement Device Interface
+ * Copyright (C) 1999,2002,2003 David A. Schleef <ds@schleef.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+/*
+ * Driver: ni_6527
+ * Description: National Instruments 6527
+ * Devices: [National Instruments] PCI-6527 (pci-6527), PXI-6527 (pxi-6527)
+ * Author: David A. Schleef <ds@schleef.org>
+ * Updated: Sat, 25 Jan 2003 13:24:40 -0800
+ * Status: works
+ *
+ * Configuration Options: not applicable, uses PCI auto config
+ */
+
+#include <linux/module.h>
+#include <linux/interrupt.h>
+
+#include "../comedi_pci.h"
+
+/*
+ * PCI BAR1 - Register memory map
+ *
+ * Manuals (available from ftp://ftp.natinst.com/support/manuals)
+ * 370106b.pdf 6527 Register Level Programmer Manual
+ */
+#define NI6527_DI_REG(x) (0x00 + (x))
+#define NI6527_DO_REG(x) (0x03 + (x))
+#define NI6527_ID_REG 0x06
+#define NI6527_CLR_REG 0x07
+#define NI6527_CLR_EDGE (1 << 3)
+#define NI6527_CLR_OVERFLOW (1 << 2)
+#define NI6527_CLR_FILT (1 << 1)
+#define NI6527_CLR_INTERVAL (1 << 0)
+#define NI6527_CLR_IRQS (NI6527_CLR_EDGE | NI6527_CLR_OVERFLOW)
+#define NI6527_CLR_RESET_FILT (NI6527_CLR_FILT | NI6527_CLR_INTERVAL)
+#define NI6527_FILT_INTERVAL_REG(x) (0x08 + (x))
+#define NI6527_FILT_ENA_REG(x) (0x0c + (x))
+#define NI6527_STATUS_REG 0x14
+#define NI6527_STATUS_IRQ (1 << 2)
+#define NI6527_STATUS_OVERFLOW (1 << 1)
+#define NI6527_STATUS_EDGE (1 << 0)
+#define NI6527_CTRL_REG 0x15
+#define NI6527_CTRL_FALLING (1 << 4)
+#define NI6527_CTRL_RISING (1 << 3)
+#define NI6527_CTRL_IRQ (1 << 2)
+#define NI6527_CTRL_OVERFLOW (1 << 1)
+#define NI6527_CTRL_EDGE (1 << 0)
+#define NI6527_CTRL_DISABLE_IRQS 0
+#define NI6527_CTRL_ENABLE_IRQS (NI6527_CTRL_FALLING | \
+ NI6527_CTRL_RISING | \
+ NI6527_CTRL_IRQ | NI6527_CTRL_EDGE)
+#define NI6527_RISING_EDGE_REG(x) (0x18 + (x))
+#define NI6527_FALLING_EDGE_REG(x) (0x20 + (x))
+
+enum ni6527_boardid {
+ BOARD_PCI6527,
+ BOARD_PXI6527,
+};
+
+struct ni6527_board {
+ const char *name;
+};
+
+static const struct ni6527_board ni6527_boards[] = {
+ [BOARD_PCI6527] = {
+ .name = "pci-6527",
+ },
+ [BOARD_PXI6527] = {
+ .name = "pxi-6527",
+ },
+};
+
+struct ni6527_private {
+ unsigned int filter_interval;
+ unsigned int filter_enable;
+};
+
+static void ni6527_set_filter_interval(struct comedi_device *dev,
+ unsigned int val)
+{
+ struct ni6527_private *devpriv = dev->private;
+
+ if (val != devpriv->filter_interval) {
+ writeb(val & 0xff, dev->mmio + NI6527_FILT_INTERVAL_REG(0));
+ writeb((val >> 8) & 0xff,
+ dev->mmio + NI6527_FILT_INTERVAL_REG(1));
+ writeb((val >> 16) & 0x0f,
+ dev->mmio + NI6527_FILT_INTERVAL_REG(2));
+
+ writeb(NI6527_CLR_INTERVAL, dev->mmio + NI6527_CLR_REG);
+
+ devpriv->filter_interval = val;
+ }
+}
+
+static void ni6527_set_filter_enable(struct comedi_device *dev,
+ unsigned int val)
+{
+ writeb(val & 0xff, dev->mmio + NI6527_FILT_ENA_REG(0));
+ writeb((val >> 8) & 0xff, dev->mmio + NI6527_FILT_ENA_REG(1));
+ writeb((val >> 16) & 0xff, dev->mmio + NI6527_FILT_ENA_REG(2));
+}
+
+static int ni6527_di_insn_config(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ struct ni6527_private *devpriv = dev->private;
+ unsigned int chan = CR_CHAN(insn->chanspec);
+ unsigned int interval;
+
+ switch (data[0]) {
+ case INSN_CONFIG_FILTER:
+ /*
+ * The deglitch filter interval is specified in nanoseconds.
+ * The hardware supports intervals in 200ns increments. Round
+ * the user values up and return the actual interval.
+ */
+ interval = (data[1] + 100) / 200;
+ data[1] = interval * 200;
+
+ if (interval) {
+ ni6527_set_filter_interval(dev, interval);
+ devpriv->filter_enable |= 1 << chan;
+ } else {
+ devpriv->filter_enable &= ~(1 << chan);
+ }
+ ni6527_set_filter_enable(dev, devpriv->filter_enable);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return insn->n;
+}
+
+static int ni6527_di_insn_bits(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ unsigned int val;
+
+ val = readb(dev->mmio + NI6527_DI_REG(0));
+ val |= (readb(dev->mmio + NI6527_DI_REG(1)) << 8);
+ val |= (readb(dev->mmio + NI6527_DI_REG(2)) << 16);
+
+ data[1] = val;
+
+ return insn->n;
+}
+
+static int ni6527_do_insn_bits(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ unsigned int mask;
+
+ mask = comedi_dio_update_state(s, data);
+ if (mask) {
+ /* Outputs are inverted */
+ unsigned int val = s->state ^ 0xffffff;
+
+ if (mask & 0x0000ff)
+ writeb(val & 0xff, dev->mmio + NI6527_DO_REG(0));
+ if (mask & 0x00ff00)
+ writeb((val >> 8) & 0xff,
+ dev->mmio + NI6527_DO_REG(1));
+ if (mask & 0xff0000)
+ writeb((val >> 16) & 0xff,
+ dev->mmio + NI6527_DO_REG(2));
+ }
+
+ data[1] = s->state;
+
+ return insn->n;
+}
+
+static irqreturn_t ni6527_interrupt(int irq, void *d)
+{
+ struct comedi_device *dev = d;
+ struct comedi_subdevice *s = dev->read_subdev;
+ unsigned int status;
+
+ status = readb(dev->mmio + NI6527_STATUS_REG);
+ if (!(status & NI6527_STATUS_IRQ))
+ return IRQ_NONE;
+
+ if (status & NI6527_STATUS_EDGE) {
+ comedi_buf_write_samples(s, &s->state, 1);
+ comedi_handle_events(dev, s);
+ }
+
+ writeb(NI6527_CLR_IRQS, dev->mmio + NI6527_CLR_REG);
+
+ return IRQ_HANDLED;
+}
+
+static int ni6527_intr_cmdtest(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_cmd *cmd)
+{
+ int err = 0;
+
+ /* Step 1 : check if triggers are trivially valid */
+
+ err |= comedi_check_trigger_src(&cmd->start_src, TRIG_NOW);
+ err |= comedi_check_trigger_src(&cmd->scan_begin_src, TRIG_OTHER);
+ err |= comedi_check_trigger_src(&cmd->convert_src, TRIG_FOLLOW);
+ err |= comedi_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
+ err |= comedi_check_trigger_src(&cmd->stop_src, TRIG_COUNT);
+
+ if (err)
+ return 1;
+
+ /* Step 2a : make sure trigger sources are unique */
+ /* Step 2b : and mutually compatible */
+
+ /* Step 3: check if arguments are trivially valid */
+
+ err |= comedi_check_trigger_arg_is(&cmd->start_arg, 0);
+ err |= comedi_check_trigger_arg_is(&cmd->scan_begin_arg, 0);
+ err |= comedi_check_trigger_arg_is(&cmd->convert_arg, 0);
+ err |= comedi_check_trigger_arg_is(&cmd->scan_end_arg,
+ cmd->chanlist_len);
+ err |= comedi_check_trigger_arg_is(&cmd->stop_arg, 0);
+
+ if (err)
+ return 3;
+
+ /* Step 4: fix up any arguments */
+
+ /* Step 5: check channel list if it exists */
+
+ return 0;
+}
+
+static int ni6527_intr_cmd(struct comedi_device *dev,
+ struct comedi_subdevice *s)
+{
+ writeb(NI6527_CLR_IRQS, dev->mmio + NI6527_CLR_REG);
+ writeb(NI6527_CTRL_ENABLE_IRQS, dev->mmio + NI6527_CTRL_REG);
+
+ return 0;
+}
+
+static int ni6527_intr_cancel(struct comedi_device *dev,
+ struct comedi_subdevice *s)
+{
+ writeb(NI6527_CTRL_DISABLE_IRQS, dev->mmio + NI6527_CTRL_REG);
+
+ return 0;
+}
+
+static int ni6527_intr_insn_bits(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn, unsigned int *data)
+{
+ data[1] = 0;
+ return insn->n;
+}
+
+static void ni6527_set_edge_detection(struct comedi_device *dev,
+ unsigned int mask,
+ unsigned int rising,
+ unsigned int falling)
+{
+ unsigned int i;
+
+ rising &= mask;
+ falling &= mask;
+ for (i = 0; i < 2; i++) {
+ if (mask & 0xff) {
+ if (~mask & 0xff) {
+ /* preserve rising-edge detection channels */
+ rising |= readb(dev->mmio +
+ NI6527_RISING_EDGE_REG(i)) &
+ (~mask & 0xff);
+ /* preserve falling-edge detection channels */
+ falling |= readb(dev->mmio +
+ NI6527_FALLING_EDGE_REG(i)) &
+ (~mask & 0xff);
+ }
+ /* update rising-edge detection channels */
+ writeb(rising & 0xff,
+ dev->mmio + NI6527_RISING_EDGE_REG(i));
+ /* update falling-edge detection channels */
+ writeb(falling & 0xff,
+ dev->mmio + NI6527_FALLING_EDGE_REG(i));
+ }
+ rising >>= 8;
+ falling >>= 8;
+ mask >>= 8;
+ }
+}
+
+static int ni6527_intr_insn_config(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ unsigned int mask = 0xffffffff;
+ unsigned int rising, falling, shift;
+
+ switch (data[0]) {
+ case INSN_CONFIG_CHANGE_NOTIFY:
+ /* check_insn_config_length() does not check this instruction */
+ if (insn->n != 3)
+ return -EINVAL;
+ rising = data[1];
+ falling = data[2];
+ ni6527_set_edge_detection(dev, mask, rising, falling);
+ break;
+ case INSN_CONFIG_DIGITAL_TRIG:
+ /* check trigger number */
+ if (data[1] != 0)
+ return -EINVAL;
+ /* check digital trigger operation */
+ switch (data[2]) {
+ case COMEDI_DIGITAL_TRIG_DISABLE:
+ rising = 0;
+ falling = 0;
+ break;
+ case COMEDI_DIGITAL_TRIG_ENABLE_EDGES:
+ /* check shift amount */
+ shift = data[3];
+ if (shift >= s->n_chan) {
+ mask = 0;
+ rising = 0;
+ falling = 0;
+ } else {
+ mask <<= shift;
+ rising = data[4] << shift;
+ falling = data[5] << shift;
+ }
+ break;
+ default:
+ return -EINVAL;
+ }
+ ni6527_set_edge_detection(dev, mask, rising, falling);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return insn->n;
+}
+
+static void ni6527_reset(struct comedi_device *dev)
+{
+ /* disable deglitch filters on all channels */
+ ni6527_set_filter_enable(dev, 0);
+
+ /* disable edge detection */
+ ni6527_set_edge_detection(dev, 0xffffffff, 0, 0);
+
+ writeb(NI6527_CLR_IRQS | NI6527_CLR_RESET_FILT,
+ dev->mmio + NI6527_CLR_REG);
+ writeb(NI6527_CTRL_DISABLE_IRQS, dev->mmio + NI6527_CTRL_REG);
+}
+
+static int ni6527_auto_attach(struct comedi_device *dev,
+ unsigned long context)
+{
+ struct pci_dev *pcidev = comedi_to_pci_dev(dev);
+ const struct ni6527_board *board = NULL;
+ struct ni6527_private *devpriv;
+ struct comedi_subdevice *s;
+ int ret;
+
+ if (context < ARRAY_SIZE(ni6527_boards))
+ board = &ni6527_boards[context];
+ if (!board)
+ return -ENODEV;
+ dev->board_ptr = board;
+ dev->board_name = board->name;
+
+ devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
+ if (!devpriv)
+ return -ENOMEM;
+
+ ret = comedi_pci_enable(dev);
+ if (ret)
+ return ret;
+
+ dev->mmio = pci_ioremap_bar(pcidev, 1);
+ if (!dev->mmio)
+ return -ENOMEM;
+
+ /* make sure this is actually a 6527 device */
+ if (readb(dev->mmio + NI6527_ID_REG) != 0x27)
+ return -ENODEV;
+
+ ni6527_reset(dev);
+
+ ret = request_irq(pcidev->irq, ni6527_interrupt, IRQF_SHARED,
+ dev->board_name, dev);
+ if (ret == 0)
+ dev->irq = pcidev->irq;
+
+ ret = comedi_alloc_subdevices(dev, 3);
+ if (ret)
+ return ret;
+
+ /* Digital Input subdevice */
+ s = &dev->subdevices[0];
+ s->type = COMEDI_SUBD_DI;
+ s->subdev_flags = SDF_READABLE;
+ s->n_chan = 24;
+ s->maxdata = 1;
+ s->range_table = &range_digital;
+ s->insn_config = ni6527_di_insn_config;
+ s->insn_bits = ni6527_di_insn_bits;
+
+ /* Digital Output subdevice */
+ s = &dev->subdevices[1];
+ s->type = COMEDI_SUBD_DO;
+ s->subdev_flags = SDF_WRITABLE;
+ s->n_chan = 24;
+ s->maxdata = 1;
+ s->range_table = &range_digital;
+ s->insn_bits = ni6527_do_insn_bits;
+
+ /* Edge detection interrupt subdevice */
+ s = &dev->subdevices[2];
+ if (dev->irq) {
+ dev->read_subdev = s;
+ s->type = COMEDI_SUBD_DI;
+ s->subdev_flags = SDF_READABLE | SDF_CMD_READ;
+ s->n_chan = 1;
+ s->maxdata = 1;
+ s->range_table = &range_digital;
+ s->insn_config = ni6527_intr_insn_config;
+ s->insn_bits = ni6527_intr_insn_bits;
+ s->len_chanlist = 1;
+ s->do_cmdtest = ni6527_intr_cmdtest;
+ s->do_cmd = ni6527_intr_cmd;
+ s->cancel = ni6527_intr_cancel;
+ } else {
+ s->type = COMEDI_SUBD_UNUSED;
+ }
+
+ return 0;
+}
+
+static void ni6527_detach(struct comedi_device *dev)
+{
+ if (dev->mmio)
+ ni6527_reset(dev);
+ comedi_pci_detach(dev);
+}
+
+static struct comedi_driver ni6527_driver = {
+ .driver_name = "ni_6527",
+ .module = THIS_MODULE,
+ .auto_attach = ni6527_auto_attach,
+ .detach = ni6527_detach,
+};
+
+static int ni6527_pci_probe(struct pci_dev *dev,
+ const struct pci_device_id *id)
+{
+ return comedi_pci_auto_config(dev, &ni6527_driver, id->driver_data);
+}
+
+static const struct pci_device_id ni6527_pci_table[] = {
+ { PCI_VDEVICE(NI, 0x2b10), BOARD_PXI6527 },
+ { PCI_VDEVICE(NI, 0x2b20), BOARD_PCI6527 },
+ { 0 }
+};
+MODULE_DEVICE_TABLE(pci, ni6527_pci_table);
+
+static struct pci_driver ni6527_pci_driver = {
+ .name = "ni_6527",
+ .id_table = ni6527_pci_table,
+ .probe = ni6527_pci_probe,
+ .remove = comedi_pci_auto_unconfig,
+};
+module_comedi_pci_driver(ni6527_driver, ni6527_pci_driver);
+
+MODULE_AUTHOR("Comedi http://www.comedi.org");
+MODULE_DESCRIPTION("Comedi driver for National Instruments PCI-6527");
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/comedi/drivers/ni_65xx.c b/drivers/staging/comedi/drivers/ni_65xx.c
new file mode 100644
index 000000000..800d57426
--- /dev/null
+++ b/drivers/staging/comedi/drivers/ni_65xx.c
@@ -0,0 +1,831 @@
+/*
+ * ni_65xx.c
+ * Comedi driver for National Instruments PCI-65xx static dio boards
+ *
+ * Copyright (C) 2006 Jon Grierson <jd@renko.co.uk>
+ * Copyright (C) 2006 Frank Mori Hess <fmhess@users.sourceforge.net>
+ *
+ * COMEDI - Linux Control and Measurement Device Interface
+ * Copyright (C) 1999,2002,2003 David A. Schleef <ds@schleef.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+/*
+ * Driver: ni_65xx
+ * Description: National Instruments 65xx static dio boards
+ * Author: Jon Grierson <jd@renko.co.uk>,
+ * Frank Mori Hess <fmhess@users.sourceforge.net>
+ * Status: testing
+ * Devices: [National Instruments] PCI-6509 (pci-6509), PXI-6509 (pxi-6509),
+ * PCI-6510 (pci-6510), PCI-6511 (pci-6511), PXI-6511 (pxi-6511),
+ * PCI-6512 (pci-6512), PXI-6512 (pxi-6512), PCI-6513 (pci-6513),
+ * PXI-6513 (pxi-6513), PCI-6514 (pci-6514), PXI-6514 (pxi-6514),
+ * PCI-6515 (pxi-6515), PXI-6515 (pxi-6515), PCI-6516 (pci-6516),
+ * PCI-6517 (pci-6517), PCI-6518 (pci-6518), PCI-6519 (pci-6519),
+ * PCI-6520 (pci-6520), PCI-6521 (pci-6521), PXI-6521 (pxi-6521),
+ * PCI-6528 (pci-6528), PXI-6528 (pxi-6528)
+ * Updated: Mon, 21 Jul 2014 12:49:58 +0000
+ *
+ * Configuration Options: not applicable, uses PCI auto config
+ *
+ * Based on the PCI-6527 driver by ds.
+ * The interrupt subdevice (subdevice 3) is probably broken for all
+ * boards except maybe the 6514.
+ *
+ * This driver previously inverted the outputs on PCI-6513 through to
+ * PCI-6519 and on PXI-6513 through to PXI-6515. It no longer inverts
+ * outputs on those cards by default as it didn't make much sense. If
+ * you require the outputs to be inverted on those cards for legacy
+ * reasons, set the module parameter "legacy_invert_outputs=true" when
+ * loading the module, or set "ni_65xx.legacy_invert_outputs=true" on
+ * the kernel command line if the driver is built in to the kernel.
+ */
+
+/*
+ * Manuals (available from ftp://ftp.natinst.com/support/manuals)
+ *
+ * 370106b.pdf 6514 Register Level Programmer Manual
+ */
+
+#include <linux/module.h>
+#include <linux/interrupt.h>
+
+#include "../comedi_pci.h"
+
+/*
+ * PCI BAR1 Register Map
+ */
+
+/* Non-recurring Registers (8-bit except where noted) */
+#define NI_65XX_ID_REG 0x00
+#define NI_65XX_CLR_REG 0x01
+#define NI_65XX_CLR_WDOG_INT (1 << 6)
+#define NI_65XX_CLR_WDOG_PING (1 << 5)
+#define NI_65XX_CLR_WDOG_EXP (1 << 4)
+#define NI_65XX_CLR_EDGE_INT (1 << 3)
+#define NI_65XX_CLR_OVERFLOW_INT (1 << 2)
+#define NI_65XX_STATUS_REG 0x02
+#define NI_65XX_STATUS_WDOG_INT (1 << 5)
+#define NI_65XX_STATUS_FALL_EDGE (1 << 4)
+#define NI_65XX_STATUS_RISE_EDGE (1 << 3)
+#define NI_65XX_STATUS_INT (1 << 2)
+#define NI_65XX_STATUS_OVERFLOW_INT (1 << 1)
+#define NI_65XX_STATUS_EDGE_INT (1 << 0)
+#define NI_65XX_CTRL_REG 0x03
+#define NI_65XX_CTRL_WDOG_ENA (1 << 5)
+#define NI_65XX_CTRL_FALL_EDGE_ENA (1 << 4)
+#define NI_65XX_CTRL_RISE_EDGE_ENA (1 << 3)
+#define NI_65XX_CTRL_INT_ENA (1 << 2)
+#define NI_65XX_CTRL_OVERFLOW_ENA (1 << 1)
+#define NI_65XX_CTRL_EDGE_ENA (1 << 0)
+#define NI_65XX_REV_REG 0x04 /* 32-bit */
+#define NI_65XX_FILTER_REG 0x08 /* 32-bit */
+#define NI_65XX_RTSI_ROUTE_REG 0x0c /* 16-bit */
+#define NI_65XX_RTSI_EDGE_REG 0x0e /* 16-bit */
+#define NI_65XX_RTSI_WDOG_REG 0x10 /* 16-bit */
+#define NI_65XX_RTSI_TRIG_REG 0x12 /* 16-bit */
+#define NI_65XX_AUTO_CLK_SEL_REG 0x14 /* PXI-6528 only */
+#define NI_65XX_AUTO_CLK_SEL_STATUS (1 << 1)
+#define NI_65XX_AUTO_CLK_SEL_DISABLE (1 << 0)
+#define NI_65XX_WDOG_CTRL_REG 0x15
+#define NI_65XX_WDOG_CTRL_ENA (1 << 0)
+#define NI_65XX_RTSI_CFG_REG 0x16
+#define NI_65XX_RTSI_CFG_RISE_SENSE (1 << 2)
+#define NI_65XX_RTSI_CFG_FALL_SENSE (1 << 1)
+#define NI_65XX_RTSI_CFG_SYNC_DETECT (1 << 0)
+#define NI_65XX_WDOG_STATUS_REG 0x17
+#define NI_65XX_WDOG_STATUS_EXP (1 << 0)
+#define NI_65XX_WDOG_INTERVAL_REG 0x18 /* 32-bit */
+
+/* Recurring port registers (8-bit) */
+#define NI_65XX_PORT(x) ((x) * 0x10)
+#define NI_65XX_IO_DATA_REG(x) (0x40 + NI_65XX_PORT(x))
+#define NI_65XX_IO_SEL_REG(x) (0x41 + NI_65XX_PORT(x))
+#define NI_65XX_IO_SEL_OUTPUT (0 << 0)
+#define NI_65XX_IO_SEL_INPUT (1 << 0)
+#define NI_65XX_RISE_EDGE_ENA_REG(x) (0x42 + NI_65XX_PORT(x))
+#define NI_65XX_FALL_EDGE_ENA_REG(x) (0x43 + NI_65XX_PORT(x))
+#define NI_65XX_FILTER_ENA(x) (0x44 + NI_65XX_PORT(x))
+#define NI_65XX_WDOG_HIZ_REG(x) (0x46 + NI_65XX_PORT(x))
+#define NI_65XX_WDOG_ENA(x) (0x47 + NI_65XX_PORT(x))
+#define NI_65XX_WDOG_HI_LO_REG(x) (0x48 + NI_65XX_PORT(x))
+#define NI_65XX_RTSI_ENA(x) (0x49 + NI_65XX_PORT(x))
+
+#define NI_65XX_PORT_TO_CHAN(x) ((x) * 8)
+#define NI_65XX_CHAN_TO_PORT(x) ((x) / 8)
+#define NI_65XX_CHAN_TO_MASK(x) (1 << ((x) % 8))
+
+enum ni_65xx_boardid {
+ BOARD_PCI6509,
+ BOARD_PXI6509,
+ BOARD_PCI6510,
+ BOARD_PCI6511,
+ BOARD_PXI6511,
+ BOARD_PCI6512,
+ BOARD_PXI6512,
+ BOARD_PCI6513,
+ BOARD_PXI6513,
+ BOARD_PCI6514,
+ BOARD_PXI6514,
+ BOARD_PCI6515,
+ BOARD_PXI6515,
+ BOARD_PCI6516,
+ BOARD_PCI6517,
+ BOARD_PCI6518,
+ BOARD_PCI6519,
+ BOARD_PCI6520,
+ BOARD_PCI6521,
+ BOARD_PXI6521,
+ BOARD_PCI6528,
+ BOARD_PXI6528,
+};
+
+struct ni_65xx_board {
+ const char *name;
+ unsigned num_dio_ports;
+ unsigned num_di_ports;
+ unsigned num_do_ports;
+ unsigned legacy_invert:1;
+};
+
+static const struct ni_65xx_board ni_65xx_boards[] = {
+ [BOARD_PCI6509] = {
+ .name = "pci-6509",
+ .num_dio_ports = 12,
+ },
+ [BOARD_PXI6509] = {
+ .name = "pxi-6509",
+ .num_dio_ports = 12,
+ },
+ [BOARD_PCI6510] = {
+ .name = "pci-6510",
+ .num_di_ports = 4,
+ },
+ [BOARD_PCI6511] = {
+ .name = "pci-6511",
+ .num_di_ports = 8,
+ },
+ [BOARD_PXI6511] = {
+ .name = "pxi-6511",
+ .num_di_ports = 8,
+ },
+ [BOARD_PCI6512] = {
+ .name = "pci-6512",
+ .num_do_ports = 8,
+ },
+ [BOARD_PXI6512] = {
+ .name = "pxi-6512",
+ .num_do_ports = 8,
+ },
+ [BOARD_PCI6513] = {
+ .name = "pci-6513",
+ .num_do_ports = 8,
+ .legacy_invert = 1,
+ },
+ [BOARD_PXI6513] = {
+ .name = "pxi-6513",
+ .num_do_ports = 8,
+ .legacy_invert = 1,
+ },
+ [BOARD_PCI6514] = {
+ .name = "pci-6514",
+ .num_di_ports = 4,
+ .num_do_ports = 4,
+ .legacy_invert = 1,
+ },
+ [BOARD_PXI6514] = {
+ .name = "pxi-6514",
+ .num_di_ports = 4,
+ .num_do_ports = 4,
+ .legacy_invert = 1,
+ },
+ [BOARD_PCI6515] = {
+ .name = "pci-6515",
+ .num_di_ports = 4,
+ .num_do_ports = 4,
+ .legacy_invert = 1,
+ },
+ [BOARD_PXI6515] = {
+ .name = "pxi-6515",
+ .num_di_ports = 4,
+ .num_do_ports = 4,
+ .legacy_invert = 1,
+ },
+ [BOARD_PCI6516] = {
+ .name = "pci-6516",
+ .num_do_ports = 4,
+ .legacy_invert = 1,
+ },
+ [BOARD_PCI6517] = {
+ .name = "pci-6517",
+ .num_do_ports = 4,
+ .legacy_invert = 1,
+ },
+ [BOARD_PCI6518] = {
+ .name = "pci-6518",
+ .num_di_ports = 2,
+ .num_do_ports = 2,
+ .legacy_invert = 1,
+ },
+ [BOARD_PCI6519] = {
+ .name = "pci-6519",
+ .num_di_ports = 2,
+ .num_do_ports = 2,
+ .legacy_invert = 1,
+ },
+ [BOARD_PCI6520] = {
+ .name = "pci-6520",
+ .num_di_ports = 1,
+ .num_do_ports = 1,
+ },
+ [BOARD_PCI6521] = {
+ .name = "pci-6521",
+ .num_di_ports = 1,
+ .num_do_ports = 1,
+ },
+ [BOARD_PXI6521] = {
+ .name = "pxi-6521",
+ .num_di_ports = 1,
+ .num_do_ports = 1,
+ },
+ [BOARD_PCI6528] = {
+ .name = "pci-6528",
+ .num_di_ports = 3,
+ .num_do_ports = 3,
+ },
+ [BOARD_PXI6528] = {
+ .name = "pxi-6528",
+ .num_di_ports = 3,
+ .num_do_ports = 3,
+ },
+};
+
+static bool ni_65xx_legacy_invert_outputs;
+module_param_named(legacy_invert_outputs, ni_65xx_legacy_invert_outputs,
+ bool, 0444);
+MODULE_PARM_DESC(legacy_invert_outputs,
+ "invert outputs of PCI/PXI-6513/6514/6515/6516/6517/6518/6519 for compatibility with old user code");
+
+static unsigned int ni_65xx_num_ports(struct comedi_device *dev)
+{
+ const struct ni_65xx_board *board = dev->board_ptr;
+
+ return board->num_dio_ports + board->num_di_ports + board->num_do_ports;
+}
+
+static void ni_65xx_disable_input_filters(struct comedi_device *dev)
+{
+ unsigned int num_ports = ni_65xx_num_ports(dev);
+ int i;
+
+ /* disable input filtering on all ports */
+ for (i = 0; i < num_ports; ++i)
+ writeb(0x00, dev->mmio + NI_65XX_FILTER_ENA(i));
+
+ /* set filter interval to 0 (32bit reg) */
+ writel(0x00000000, dev->mmio + NI_65XX_FILTER_REG);
+}
+
+/* updates edge detection for base_chan to base_chan+31 */
+static void ni_65xx_update_edge_detection(struct comedi_device *dev,
+ unsigned int base_chan,
+ unsigned int rising,
+ unsigned int falling)
+{
+ unsigned int num_ports = ni_65xx_num_ports(dev);
+ unsigned int port;
+
+ if (base_chan >= NI_65XX_PORT_TO_CHAN(num_ports))
+ return;
+
+ for (port = NI_65XX_CHAN_TO_PORT(base_chan); port < num_ports; port++) {
+ int bitshift = (int)(NI_65XX_PORT_TO_CHAN(port) - base_chan);
+ unsigned int port_mask, port_rising, port_falling;
+
+ if (bitshift >= 32)
+ break;
+
+ if (bitshift >= 0) {
+ port_mask = ~0U >> bitshift;
+ port_rising = rising >> bitshift;
+ port_falling = falling >> bitshift;
+ } else {
+ port_mask = ~0U << -bitshift;
+ port_rising = rising << -bitshift;
+ port_falling = falling << -bitshift;
+ }
+ if (port_mask & 0xff) {
+ if (~port_mask & 0xff) {
+ port_rising |=
+ readb(dev->mmio +
+ NI_65XX_RISE_EDGE_ENA_REG(port)) &
+ ~port_mask;
+ port_falling |=
+ readb(dev->mmio +
+ NI_65XX_FALL_EDGE_ENA_REG(port)) &
+ ~port_mask;
+ }
+ writeb(port_rising & 0xff,
+ dev->mmio + NI_65XX_RISE_EDGE_ENA_REG(port));
+ writeb(port_falling & 0xff,
+ dev->mmio + NI_65XX_FALL_EDGE_ENA_REG(port));
+ }
+ }
+}
+
+static void ni_65xx_disable_edge_detection(struct comedi_device *dev)
+{
+ /* clear edge detection for channels 0 to 31 */
+ ni_65xx_update_edge_detection(dev, 0, 0, 0);
+ /* clear edge detection for channels 32 to 63 */
+ ni_65xx_update_edge_detection(dev, 32, 0, 0);
+ /* clear edge detection for channels 64 to 95 */
+ ni_65xx_update_edge_detection(dev, 64, 0, 0);
+}
+
+static int ni_65xx_dio_insn_config(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ unsigned long base_port = (unsigned long)s->private;
+ unsigned int chan = CR_CHAN(insn->chanspec);
+ unsigned int chan_mask = NI_65XX_CHAN_TO_MASK(chan);
+ unsigned port = base_port + NI_65XX_CHAN_TO_PORT(chan);
+ unsigned int interval;
+ unsigned int val;
+
+ switch (data[0]) {
+ case INSN_CONFIG_FILTER:
+ /*
+ * The deglitch filter interval is specified in nanoseconds.
+ * The hardware supports intervals in 200ns increments. Round
+ * the user values up and return the actual interval.
+ */
+ interval = (data[1] + 100) / 200;
+ if (interval > 0xfffff)
+ interval = 0xfffff;
+ data[1] = interval * 200;
+
+ /*
+ * Enable/disable the channel for deglitch filtering. Note
+ * that the filter interval is never set to '0'. This is done
+ * because other channels might still be enabled for filtering.
+ */
+ val = readb(dev->mmio + NI_65XX_FILTER_ENA(port));
+ if (interval) {
+ writel(interval, dev->mmio + NI_65XX_FILTER_REG);
+ val |= chan_mask;
+ } else {
+ val &= ~chan_mask;
+ }
+ writeb(val, dev->mmio + NI_65XX_FILTER_ENA(port));
+ break;
+
+ case INSN_CONFIG_DIO_OUTPUT:
+ if (s->type != COMEDI_SUBD_DIO)
+ return -EINVAL;
+ writeb(NI_65XX_IO_SEL_OUTPUT,
+ dev->mmio + NI_65XX_IO_SEL_REG(port));
+ break;
+
+ case INSN_CONFIG_DIO_INPUT:
+ if (s->type != COMEDI_SUBD_DIO)
+ return -EINVAL;
+ writeb(NI_65XX_IO_SEL_INPUT,
+ dev->mmio + NI_65XX_IO_SEL_REG(port));
+ break;
+
+ case INSN_CONFIG_DIO_QUERY:
+ if (s->type != COMEDI_SUBD_DIO)
+ return -EINVAL;
+ val = readb(dev->mmio + NI_65XX_IO_SEL_REG(port));
+ data[1] = (val == NI_65XX_IO_SEL_INPUT) ? COMEDI_INPUT
+ : COMEDI_OUTPUT;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ return insn->n;
+}
+
+static int ni_65xx_dio_insn_bits(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ unsigned long base_port = (unsigned long)s->private;
+ unsigned int base_chan = CR_CHAN(insn->chanspec);
+ int last_port_offset = NI_65XX_CHAN_TO_PORT(s->n_chan - 1);
+ unsigned read_bits = 0;
+ int port_offset;
+
+ for (port_offset = NI_65XX_CHAN_TO_PORT(base_chan);
+ port_offset <= last_port_offset; port_offset++) {
+ unsigned port = base_port + port_offset;
+ int base_port_channel = NI_65XX_PORT_TO_CHAN(port_offset);
+ unsigned port_mask, port_data, bits;
+ int bitshift = base_port_channel - base_chan;
+
+ if (bitshift >= 32)
+ break;
+ port_mask = data[0];
+ port_data = data[1];
+ if (bitshift > 0) {
+ port_mask >>= bitshift;
+ port_data >>= bitshift;
+ } else {
+ port_mask <<= -bitshift;
+ port_data <<= -bitshift;
+ }
+ port_mask &= 0xff;
+ port_data &= 0xff;
+
+ /* update the outputs */
+ if (port_mask) {
+ bits = readb(dev->mmio + NI_65XX_IO_DATA_REG(port));
+ bits ^= s->io_bits; /* invert if necessary */
+ bits &= ~port_mask;
+ bits |= (port_data & port_mask);
+ bits ^= s->io_bits; /* invert back */
+ writeb(bits, dev->mmio + NI_65XX_IO_DATA_REG(port));
+ }
+
+ /* read back the actual state */
+ bits = readb(dev->mmio + NI_65XX_IO_DATA_REG(port));
+ bits ^= s->io_bits; /* invert if necessary */
+ if (bitshift > 0)
+ bits <<= bitshift;
+ else
+ bits >>= -bitshift;
+
+ read_bits |= bits;
+ }
+ data[1] = read_bits;
+ return insn->n;
+}
+
+static irqreturn_t ni_65xx_interrupt(int irq, void *d)
+{
+ struct comedi_device *dev = d;
+ struct comedi_subdevice *s = dev->read_subdev;
+ unsigned int status;
+
+ status = readb(dev->mmio + NI_65XX_STATUS_REG);
+ if ((status & NI_65XX_STATUS_INT) == 0)
+ return IRQ_NONE;
+ if ((status & NI_65XX_STATUS_EDGE_INT) == 0)
+ return IRQ_NONE;
+
+ writeb(NI_65XX_CLR_EDGE_INT | NI_65XX_CLR_OVERFLOW_INT,
+ dev->mmio + NI_65XX_CLR_REG);
+
+ comedi_buf_write_samples(s, &s->state, 1);
+ comedi_handle_events(dev, s);
+
+ return IRQ_HANDLED;
+}
+
+static int ni_65xx_intr_cmdtest(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_cmd *cmd)
+{
+ int err = 0;
+
+ /* Step 1 : check if triggers are trivially valid */
+
+ err |= comedi_check_trigger_src(&cmd->start_src, TRIG_NOW);
+ err |= comedi_check_trigger_src(&cmd->scan_begin_src, TRIG_OTHER);
+ err |= comedi_check_trigger_src(&cmd->convert_src, TRIG_FOLLOW);
+ err |= comedi_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
+ err |= comedi_check_trigger_src(&cmd->stop_src, TRIG_COUNT);
+
+ if (err)
+ return 1;
+
+ /* Step 2a : make sure trigger sources are unique */
+ /* Step 2b : and mutually compatible */
+
+ /* Step 3: check if arguments are trivially valid */
+
+ err |= comedi_check_trigger_arg_is(&cmd->start_arg, 0);
+ err |= comedi_check_trigger_arg_is(&cmd->scan_begin_arg, 0);
+ err |= comedi_check_trigger_arg_is(&cmd->convert_arg, 0);
+ err |= comedi_check_trigger_arg_is(&cmd->scan_end_arg,
+ cmd->chanlist_len);
+ err |= comedi_check_trigger_arg_is(&cmd->stop_arg, 0);
+
+ if (err)
+ return 3;
+
+ /* Step 4: fix up any arguments */
+
+ /* Step 5: check channel list if it exists */
+
+ return 0;
+}
+
+static int ni_65xx_intr_cmd(struct comedi_device *dev,
+ struct comedi_subdevice *s)
+{
+ writeb(NI_65XX_CLR_EDGE_INT | NI_65XX_CLR_OVERFLOW_INT,
+ dev->mmio + NI_65XX_CLR_REG);
+ writeb(NI_65XX_CTRL_FALL_EDGE_ENA | NI_65XX_CTRL_RISE_EDGE_ENA |
+ NI_65XX_CTRL_INT_ENA | NI_65XX_CTRL_EDGE_ENA,
+ dev->mmio + NI_65XX_CTRL_REG);
+
+ return 0;
+}
+
+static int ni_65xx_intr_cancel(struct comedi_device *dev,
+ struct comedi_subdevice *s)
+{
+ writeb(0x00, dev->mmio + NI_65XX_CTRL_REG);
+
+ return 0;
+}
+
+static int ni_65xx_intr_insn_bits(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ data[1] = 0;
+ return insn->n;
+}
+
+static int ni_65xx_intr_insn_config(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ switch (data[0]) {
+ case INSN_CONFIG_CHANGE_NOTIFY:
+ /* add instruction to check_insn_config_length() */
+ if (insn->n != 3)
+ return -EINVAL;
+
+ /* update edge detection for channels 0 to 31 */
+ ni_65xx_update_edge_detection(dev, 0, data[1], data[2]);
+ /* clear edge detection for channels 32 to 63 */
+ ni_65xx_update_edge_detection(dev, 32, 0, 0);
+ /* clear edge detection for channels 64 to 95 */
+ ni_65xx_update_edge_detection(dev, 64, 0, 0);
+ break;
+ case INSN_CONFIG_DIGITAL_TRIG:
+ /* check trigger number */
+ if (data[1] != 0)
+ return -EINVAL;
+ /* check digital trigger operation */
+ switch (data[2]) {
+ case COMEDI_DIGITAL_TRIG_DISABLE:
+ ni_65xx_disable_edge_detection(dev);
+ break;
+ case COMEDI_DIGITAL_TRIG_ENABLE_EDGES:
+ /*
+ * update edge detection for channels data[3]
+ * to (data[3] + 31)
+ */
+ ni_65xx_update_edge_detection(dev, data[3],
+ data[4], data[5]);
+ break;
+ default:
+ return -EINVAL;
+ }
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return insn->n;
+}
+
+/* ripped from mite.h and mite_setup2() to avoid mite dependency */
+#define MITE_IODWBSR 0xc0 /* IO Device Window Base Size Register */
+#define WENAB (1 << 7) /* window enable */
+
+static int ni_65xx_mite_init(struct pci_dev *pcidev)
+{
+ void __iomem *mite_base;
+ u32 main_phys_addr;
+
+ /* ioremap the MITE registers (BAR 0) temporarily */
+ mite_base = pci_ioremap_bar(pcidev, 0);
+ if (!mite_base)
+ return -ENOMEM;
+
+ /* set data window to main registers (BAR 1) */
+ main_phys_addr = pci_resource_start(pcidev, 1);
+ writel(main_phys_addr | WENAB, mite_base + MITE_IODWBSR);
+
+ /* finished with MITE registers */
+ iounmap(mite_base);
+ return 0;
+}
+
+static int ni_65xx_auto_attach(struct comedi_device *dev,
+ unsigned long context)
+{
+ struct pci_dev *pcidev = comedi_to_pci_dev(dev);
+ const struct ni_65xx_board *board = NULL;
+ struct comedi_subdevice *s;
+ unsigned i;
+ int ret;
+
+ if (context < ARRAY_SIZE(ni_65xx_boards))
+ board = &ni_65xx_boards[context];
+ if (!board)
+ return -ENODEV;
+ dev->board_ptr = board;
+ dev->board_name = board->name;
+
+ ret = comedi_pci_enable(dev);
+ if (ret)
+ return ret;
+
+ ret = ni_65xx_mite_init(pcidev);
+ if (ret)
+ return ret;
+
+ dev->mmio = pci_ioremap_bar(pcidev, 1);
+ if (!dev->mmio)
+ return -ENOMEM;
+
+ writeb(NI_65XX_CLR_EDGE_INT | NI_65XX_CLR_OVERFLOW_INT,
+ dev->mmio + NI_65XX_CLR_REG);
+ writeb(0x00, dev->mmio + NI_65XX_CTRL_REG);
+
+ if (pcidev->irq) {
+ ret = request_irq(pcidev->irq, ni_65xx_interrupt, IRQF_SHARED,
+ dev->board_name, dev);
+ if (ret == 0)
+ dev->irq = pcidev->irq;
+ }
+
+ dev_info(dev->class_dev, "board: %s, ID=0x%02x", dev->board_name,
+ readb(dev->mmio + NI_65XX_ID_REG));
+
+ ret = comedi_alloc_subdevices(dev, 4);
+ if (ret)
+ return ret;
+
+ s = &dev->subdevices[0];
+ if (board->num_di_ports) {
+ s->type = COMEDI_SUBD_DI;
+ s->subdev_flags = SDF_READABLE;
+ s->n_chan = NI_65XX_PORT_TO_CHAN(board->num_di_ports);
+ s->maxdata = 1;
+ s->range_table = &range_digital;
+ s->insn_bits = ni_65xx_dio_insn_bits;
+ s->insn_config = ni_65xx_dio_insn_config;
+
+ /* the input ports always start at port 0 */
+ s->private = (void *)0;
+ } else {
+ s->type = COMEDI_SUBD_UNUSED;
+ }
+
+ s = &dev->subdevices[1];
+ if (board->num_do_ports) {
+ s->type = COMEDI_SUBD_DO;
+ s->subdev_flags = SDF_WRITABLE;
+ s->n_chan = NI_65XX_PORT_TO_CHAN(board->num_do_ports);
+ s->maxdata = 1;
+ s->range_table = &range_digital;
+ s->insn_bits = ni_65xx_dio_insn_bits;
+
+ /* the output ports always start after the input ports */
+ s->private = (void *)(unsigned long)board->num_di_ports;
+
+ /*
+ * Use the io_bits to handle the inverted outputs. Inverted
+ * outputs are only supported if the "legacy_invert_outputs"
+ * module parameter is set to "true".
+ */
+ if (ni_65xx_legacy_invert_outputs && board->legacy_invert)
+ s->io_bits = 0xff;
+
+ /* reset all output ports to comedi '0' */
+ for (i = 0; i < board->num_do_ports; ++i) {
+ writeb(s->io_bits, /* inverted if necessary */
+ dev->mmio +
+ NI_65XX_IO_DATA_REG(board->num_di_ports + i));
+ }
+ } else {
+ s->type = COMEDI_SUBD_UNUSED;
+ }
+
+ s = &dev->subdevices[2];
+ if (board->num_dio_ports) {
+ s->type = COMEDI_SUBD_DIO;
+ s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
+ s->n_chan = NI_65XX_PORT_TO_CHAN(board->num_dio_ports);
+ s->maxdata = 1;
+ s->range_table = &range_digital;
+ s->insn_bits = ni_65xx_dio_insn_bits;
+ s->insn_config = ni_65xx_dio_insn_config;
+
+ /* the input/output ports always start at port 0 */
+ s->private = (void *)0;
+
+ /* configure all ports for input */
+ for (i = 0; i < board->num_dio_ports; ++i) {
+ writeb(NI_65XX_IO_SEL_INPUT,
+ dev->mmio + NI_65XX_IO_SEL_REG(i));
+ }
+ } else {
+ s->type = COMEDI_SUBD_UNUSED;
+ }
+
+ s = &dev->subdevices[3];
+ s->type = COMEDI_SUBD_DI;
+ s->subdev_flags = SDF_READABLE;
+ s->n_chan = 1;
+ s->maxdata = 1;
+ s->range_table = &range_digital;
+ s->insn_bits = ni_65xx_intr_insn_bits;
+ if (dev->irq) {
+ dev->read_subdev = s;
+ s->subdev_flags |= SDF_CMD_READ;
+ s->len_chanlist = 1;
+ s->insn_config = ni_65xx_intr_insn_config;
+ s->do_cmdtest = ni_65xx_intr_cmdtest;
+ s->do_cmd = ni_65xx_intr_cmd;
+ s->cancel = ni_65xx_intr_cancel;
+ }
+
+ ni_65xx_disable_input_filters(dev);
+ ni_65xx_disable_edge_detection(dev);
+
+ return 0;
+}
+
+static void ni_65xx_detach(struct comedi_device *dev)
+{
+ if (dev->mmio)
+ writeb(0x00, dev->mmio + NI_65XX_CTRL_REG);
+ comedi_pci_detach(dev);
+}
+
+static struct comedi_driver ni_65xx_driver = {
+ .driver_name = "ni_65xx",
+ .module = THIS_MODULE,
+ .auto_attach = ni_65xx_auto_attach,
+ .detach = ni_65xx_detach,
+};
+
+static int ni_65xx_pci_probe(struct pci_dev *dev,
+ const struct pci_device_id *id)
+{
+ return comedi_pci_auto_config(dev, &ni_65xx_driver, id->driver_data);
+}
+
+static const struct pci_device_id ni_65xx_pci_table[] = {
+ { PCI_VDEVICE(NI, 0x1710), BOARD_PXI6509 },
+ { PCI_VDEVICE(NI, 0x7085), BOARD_PCI6509 },
+ { PCI_VDEVICE(NI, 0x7086), BOARD_PXI6528 },
+ { PCI_VDEVICE(NI, 0x7087), BOARD_PCI6515 },
+ { PCI_VDEVICE(NI, 0x7088), BOARD_PCI6514 },
+ { PCI_VDEVICE(NI, 0x70a9), BOARD_PCI6528 },
+ { PCI_VDEVICE(NI, 0x70c3), BOARD_PCI6511 },
+ { PCI_VDEVICE(NI, 0x70c8), BOARD_PCI6513 },
+ { PCI_VDEVICE(NI, 0x70c9), BOARD_PXI6515 },
+ { PCI_VDEVICE(NI, 0x70cc), BOARD_PCI6512 },
+ { PCI_VDEVICE(NI, 0x70cd), BOARD_PXI6514 },
+ { PCI_VDEVICE(NI, 0x70d1), BOARD_PXI6513 },
+ { PCI_VDEVICE(NI, 0x70d2), BOARD_PXI6512 },
+ { PCI_VDEVICE(NI, 0x70d3), BOARD_PXI6511 },
+ { PCI_VDEVICE(NI, 0x7124), BOARD_PCI6510 },
+ { PCI_VDEVICE(NI, 0x7125), BOARD_PCI6516 },
+ { PCI_VDEVICE(NI, 0x7126), BOARD_PCI6517 },
+ { PCI_VDEVICE(NI, 0x7127), BOARD_PCI6518 },
+ { PCI_VDEVICE(NI, 0x7128), BOARD_PCI6519 },
+ { PCI_VDEVICE(NI, 0x718b), BOARD_PCI6521 },
+ { PCI_VDEVICE(NI, 0x718c), BOARD_PXI6521 },
+ { PCI_VDEVICE(NI, 0x71c5), BOARD_PCI6520 },
+ { 0 }
+};
+MODULE_DEVICE_TABLE(pci, ni_65xx_pci_table);
+
+static struct pci_driver ni_65xx_pci_driver = {
+ .name = "ni_65xx",
+ .id_table = ni_65xx_pci_table,
+ .probe = ni_65xx_pci_probe,
+ .remove = comedi_pci_auto_unconfig,
+};
+module_comedi_pci_driver(ni_65xx_driver, ni_65xx_pci_driver);
+
+MODULE_AUTHOR("Comedi http://www.comedi.org");
+MODULE_DESCRIPTION("Comedi driver for NI PCI-65xx static dio boards");
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/comedi/drivers/ni_660x.c b/drivers/staging/comedi/drivers/ni_660x.c
new file mode 100644
index 000000000..46647c64f
--- /dev/null
+++ b/drivers/staging/comedi/drivers/ni_660x.c
@@ -0,0 +1,1222 @@
+/*
+ comedi/drivers/ni_660x.c
+ Hardware driver for NI 660x devices
+
+ This program is free software; you can redistribute it and/or 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.
+*/
+
+/*
+ * Driver: ni_660x
+ * Description: National Instruments 660x counter/timer boards
+ * Devices: [National Instruments] PCI-6601 (ni_660x), PCI-6602, PXI-6602,
+ * PXI-6608, PXI-6624
+ * Author: J.P. Mellor <jpmellor@rose-hulman.edu>,
+ * Herman.Bruyninckx@mech.kuleuven.ac.be,
+ * Wim.Meeussen@mech.kuleuven.ac.be,
+ * Klaas.Gadeyne@mech.kuleuven.ac.be,
+ * Frank Mori Hess <fmhess@users.sourceforge.net>
+ * Updated: Fri, 15 Mar 2013 10:47:56 +0000
+ * Status: experimental
+ *
+ * Encoders work. PulseGeneration (both single pulse and pulse train)
+ * works. Buffered commands work for input but not output.
+ *
+ * References:
+ * DAQ 660x Register-Level Programmer Manual (NI 370505A-01)
+ * DAQ 6601/6602 User Manual (NI 322137B-01)
+ */
+
+#include <linux/module.h>
+#include <linux/interrupt.h>
+
+#include "../comedi_pci.h"
+
+#include "mite.h"
+#include "ni_tio.h"
+
+enum ni_660x_constants {
+ min_counter_pfi_chan = 8,
+ max_dio_pfi_chan = 31,
+ counters_per_chip = 4
+};
+
+#define NUM_PFI_CHANNELS 40
+/* really there are only up to 3 dma channels, but the register layout allows
+for 4 */
+#define MAX_DMA_CHANNEL 4
+
+/* See Register-Level Programmer Manual page 3.1 */
+enum ni_660x_register {
+ NI660X_G0_INT_ACK,
+ NI660X_G0_STATUS,
+ NI660X_G1_INT_ACK,
+ NI660X_G1_STATUS,
+ NI660X_G01_STATUS,
+ NI660X_G0_CMD,
+ NI660X_STC_DIO_PARALLEL_INPUT,
+ NI660X_G1_CMD,
+ NI660X_G0_HW_SAVE,
+ NI660X_G1_HW_SAVE,
+ NI660X_STC_DIO_OUTPUT,
+ NI660X_STC_DIO_CONTROL,
+ NI660X_G0_SW_SAVE,
+ NI660X_G1_SW_SAVE,
+ NI660X_G0_MODE,
+ NI660X_G01_STATUS1,
+ NI660X_G1_MODE,
+ NI660X_STC_DIO_SERIAL_INPUT,
+ NI660X_G0_LOADA,
+ NI660X_G01_STATUS2,
+ NI660X_G0_LOADB,
+ NI660X_G1_LOADA,
+ NI660X_G1_LOADB,
+ NI660X_G0_INPUT_SEL,
+ NI660X_G1_INPUT_SEL,
+ NI660X_G0_AUTO_INC,
+ NI660X_G1_AUTO_INC,
+ NI660X_G01_RESET,
+ NI660X_G0_INT_ENA,
+ NI660X_G1_INT_ENA,
+ NI660X_G0_CNT_MODE,
+ NI660X_G1_CNT_MODE,
+ NI660X_G0_GATE2,
+ NI660X_G1_GATE2,
+ NI660X_G0_DMA_CFG,
+ NI660X_G0_DMA_STATUS,
+ NI660X_G1_DMA_CFG,
+ NI660X_G1_DMA_STATUS,
+ NI660X_G2_INT_ACK,
+ NI660X_G2_STATUS,
+ NI660X_G3_INT_ACK,
+ NI660X_G3_STATUS,
+ NI660X_G23_STATUS,
+ NI660X_G2_CMD,
+ NI660X_G3_CMD,
+ NI660X_G2_HW_SAVE,
+ NI660X_G3_HW_SAVE,
+ NI660X_G2_SW_SAVE,
+ NI660X_G3_SW_SAVE,
+ NI660X_G2_MODE,
+ NI660X_G23_STATUS1,
+ NI660X_G3_MODE,
+ NI660X_G2_LOADA,
+ NI660X_G23_STATUS2,
+ NI660X_G2_LOADB,
+ NI660X_G3_LOADA,
+ NI660X_G3_LOADB,
+ NI660X_G2_INPUT_SEL,
+ NI660X_G3_INPUT_SEL,
+ NI660X_G2_AUTO_INC,
+ NI660X_G3_AUTO_INC,
+ NI660X_G23_RESET,
+ NI660X_G2_INT_ENA,
+ NI660X_G3_INT_ENA,
+ NI660X_G2_CNT_MODE,
+ NI660X_G3_CNT_MODE,
+ NI660X_G3_GATE2,
+ NI660X_G2_GATE2,
+ NI660X_G2_DMA_CFG,
+ NI660X_G2_DMA_STATUS,
+ NI660X_G3_DMA_CFG,
+ NI660X_G3_DMA_STATUS,
+ NI660X_DIO32_INPUT,
+ NI660X_DIO32_OUTPUT,
+ NI660X_CLK_CFG,
+ NI660X_GLOBAL_INT_STATUS,
+ NI660X_DMA_CFG,
+ NI660X_GLOBAL_INT_CFG,
+ NI660X_IO_CFG_0_1,
+ NI660X_IO_CFG_2_3,
+ NI660X_IO_CFG_4_5,
+ NI660X_IO_CFG_6_7,
+ NI660X_IO_CFG_8_9,
+ NI660X_IO_CFG_10_11,
+ NI660X_IO_CFG_12_13,
+ NI660X_IO_CFG_14_15,
+ NI660X_IO_CFG_16_17,
+ NI660X_IO_CFG_18_19,
+ NI660X_IO_CFG_20_21,
+ NI660X_IO_CFG_22_23,
+ NI660X_IO_CFG_24_25,
+ NI660X_IO_CFG_26_27,
+ NI660X_IO_CFG_28_29,
+ NI660X_IO_CFG_30_31,
+ NI660X_IO_CFG_32_33,
+ NI660X_IO_CFG_34_35,
+ NI660X_IO_CFG_36_37,
+ NI660X_IO_CFG_38_39,
+ NI660X_NUM_REGS,
+};
+
+static inline unsigned IOConfigReg(unsigned pfi_channel)
+{
+ unsigned reg = NI660X_IO_CFG_0_1 + pfi_channel / 2;
+
+ BUG_ON(reg > NI660X_IO_CFG_38_39);
+ return reg;
+}
+
+enum ni_660x_register_width {
+ DATA_1B,
+ DATA_2B,
+ DATA_4B
+};
+
+enum ni_660x_register_direction {
+ NI_660x_READ,
+ NI_660x_WRITE,
+ NI_660x_READ_WRITE
+};
+
+enum ni_660x_pfi_output_select {
+ pfi_output_select_high_Z = 0,
+ pfi_output_select_counter = 1,
+ pfi_output_select_do = 2,
+ num_pfi_output_selects
+};
+
+enum ni_660x_subdevices {
+ NI_660X_DIO_SUBDEV = 1,
+ NI_660X_GPCT_SUBDEV_0 = 2
+};
+static inline unsigned NI_660X_GPCT_SUBDEV(unsigned index)
+{
+ return NI_660X_GPCT_SUBDEV_0 + index;
+}
+
+struct NI_660xRegisterData {
+ const char *name; /* Register Name */
+ int offset; /* Offset from base address from GPCT chip */
+ enum ni_660x_register_direction direction;
+ enum ni_660x_register_width size; /* 1 byte, 2 bytes, or 4 bytes */
+};
+
+static const struct NI_660xRegisterData registerData[NI660X_NUM_REGS] = {
+ {"G0 Interrupt Acknowledge", 0x004, NI_660x_WRITE, DATA_2B},
+ {"G0 Status Register", 0x004, NI_660x_READ, DATA_2B},
+ {"G1 Interrupt Acknowledge", 0x006, NI_660x_WRITE, DATA_2B},
+ {"G1 Status Register", 0x006, NI_660x_READ, DATA_2B},
+ {"G01 Status Register ", 0x008, NI_660x_READ, DATA_2B},
+ {"G0 Command Register", 0x00C, NI_660x_WRITE, DATA_2B},
+ {"STC DIO Parallel Input", 0x00E, NI_660x_READ, DATA_2B},
+ {"G1 Command Register", 0x00E, NI_660x_WRITE, DATA_2B},
+ {"G0 HW Save Register", 0x010, NI_660x_READ, DATA_4B},
+ {"G1 HW Save Register", 0x014, NI_660x_READ, DATA_4B},
+ {"STC DIO Output", 0x014, NI_660x_WRITE, DATA_2B},
+ {"STC DIO Control", 0x016, NI_660x_WRITE, DATA_2B},
+ {"G0 SW Save Register", 0x018, NI_660x_READ, DATA_4B},
+ {"G1 SW Save Register", 0x01C, NI_660x_READ, DATA_4B},
+ {"G0 Mode Register", 0x034, NI_660x_WRITE, DATA_2B},
+ {"G01 Joint Status 1 Register", 0x036, NI_660x_READ, DATA_2B},
+ {"G1 Mode Register", 0x036, NI_660x_WRITE, DATA_2B},
+ {"STC DIO Serial Input", 0x038, NI_660x_READ, DATA_2B},
+ {"G0 Load A Register", 0x038, NI_660x_WRITE, DATA_4B},
+ {"G01 Joint Status 2 Register", 0x03A, NI_660x_READ, DATA_2B},
+ {"G0 Load B Register", 0x03C, NI_660x_WRITE, DATA_4B},
+ {"G1 Load A Register", 0x040, NI_660x_WRITE, DATA_4B},
+ {"G1 Load B Register", 0x044, NI_660x_WRITE, DATA_4B},
+ {"G0 Input Select Register", 0x048, NI_660x_WRITE, DATA_2B},
+ {"G1 Input Select Register", 0x04A, NI_660x_WRITE, DATA_2B},
+ {"G0 Autoincrement Register", 0x088, NI_660x_WRITE, DATA_2B},
+ {"G1 Autoincrement Register", 0x08A, NI_660x_WRITE, DATA_2B},
+ {"G01 Joint Reset Register", 0x090, NI_660x_WRITE, DATA_2B},
+ {"G0 Interrupt Enable", 0x092, NI_660x_WRITE, DATA_2B},
+ {"G1 Interrupt Enable", 0x096, NI_660x_WRITE, DATA_2B},
+ {"G0 Counting Mode Register", 0x0B0, NI_660x_WRITE, DATA_2B},
+ {"G1 Counting Mode Register", 0x0B2, NI_660x_WRITE, DATA_2B},
+ {"G0 Second Gate Register", 0x0B4, NI_660x_WRITE, DATA_2B},
+ {"G1 Second Gate Register", 0x0B6, NI_660x_WRITE, DATA_2B},
+ {"G0 DMA Config Register", 0x0B8, NI_660x_WRITE, DATA_2B},
+ {"G0 DMA Status Register", 0x0B8, NI_660x_READ, DATA_2B},
+ {"G1 DMA Config Register", 0x0BA, NI_660x_WRITE, DATA_2B},
+ {"G1 DMA Status Register", 0x0BA, NI_660x_READ, DATA_2B},
+ {"G2 Interrupt Acknowledge", 0x104, NI_660x_WRITE, DATA_2B},
+ {"G2 Status Register", 0x104, NI_660x_READ, DATA_2B},
+ {"G3 Interrupt Acknowledge", 0x106, NI_660x_WRITE, DATA_2B},
+ {"G3 Status Register", 0x106, NI_660x_READ, DATA_2B},
+ {"G23 Status Register", 0x108, NI_660x_READ, DATA_2B},
+ {"G2 Command Register", 0x10C, NI_660x_WRITE, DATA_2B},
+ {"G3 Command Register", 0x10E, NI_660x_WRITE, DATA_2B},
+ {"G2 HW Save Register", 0x110, NI_660x_READ, DATA_4B},
+ {"G3 HW Save Register", 0x114, NI_660x_READ, DATA_4B},
+ {"G2 SW Save Register", 0x118, NI_660x_READ, DATA_4B},
+ {"G3 SW Save Register", 0x11C, NI_660x_READ, DATA_4B},
+ {"G2 Mode Register", 0x134, NI_660x_WRITE, DATA_2B},
+ {"G23 Joint Status 1 Register", 0x136, NI_660x_READ, DATA_2B},
+ {"G3 Mode Register", 0x136, NI_660x_WRITE, DATA_2B},
+ {"G2 Load A Register", 0x138, NI_660x_WRITE, DATA_4B},
+ {"G23 Joint Status 2 Register", 0x13A, NI_660x_READ, DATA_2B},
+ {"G2 Load B Register", 0x13C, NI_660x_WRITE, DATA_4B},
+ {"G3 Load A Register", 0x140, NI_660x_WRITE, DATA_4B},
+ {"G3 Load B Register", 0x144, NI_660x_WRITE, DATA_4B},
+ {"G2 Input Select Register", 0x148, NI_660x_WRITE, DATA_2B},
+ {"G3 Input Select Register", 0x14A, NI_660x_WRITE, DATA_2B},
+ {"G2 Autoincrement Register", 0x188, NI_660x_WRITE, DATA_2B},
+ {"G3 Autoincrement Register", 0x18A, NI_660x_WRITE, DATA_2B},
+ {"G23 Joint Reset Register", 0x190, NI_660x_WRITE, DATA_2B},
+ {"G2 Interrupt Enable", 0x192, NI_660x_WRITE, DATA_2B},
+ {"G3 Interrupt Enable", 0x196, NI_660x_WRITE, DATA_2B},
+ {"G2 Counting Mode Register", 0x1B0, NI_660x_WRITE, DATA_2B},
+ {"G3 Counting Mode Register", 0x1B2, NI_660x_WRITE, DATA_2B},
+ {"G3 Second Gate Register", 0x1B6, NI_660x_WRITE, DATA_2B},
+ {"G2 Second Gate Register", 0x1B4, NI_660x_WRITE, DATA_2B},
+ {"G2 DMA Config Register", 0x1B8, NI_660x_WRITE, DATA_2B},
+ {"G2 DMA Status Register", 0x1B8, NI_660x_READ, DATA_2B},
+ {"G3 DMA Config Register", 0x1BA, NI_660x_WRITE, DATA_2B},
+ {"G3 DMA Status Register", 0x1BA, NI_660x_READ, DATA_2B},
+ {"32 bit Digital Input", 0x414, NI_660x_READ, DATA_4B},
+ {"32 bit Digital Output", 0x510, NI_660x_WRITE, DATA_4B},
+ {"Clock Config Register", 0x73C, NI_660x_WRITE, DATA_4B},
+ {"Global Interrupt Status Register", 0x754, NI_660x_READ, DATA_4B},
+ {"DMA Configuration Register", 0x76C, NI_660x_WRITE, DATA_4B},
+ {"Global Interrupt Config Register", 0x770, NI_660x_WRITE, DATA_4B},
+ {"IO Config Register 0-1", 0x77C, NI_660x_READ_WRITE, DATA_2B},
+ {"IO Config Register 2-3", 0x77E, NI_660x_READ_WRITE, DATA_2B},
+ {"IO Config Register 4-5", 0x780, NI_660x_READ_WRITE, DATA_2B},
+ {"IO Config Register 6-7", 0x782, NI_660x_READ_WRITE, DATA_2B},
+ {"IO Config Register 8-9", 0x784, NI_660x_READ_WRITE, DATA_2B},
+ {"IO Config Register 10-11", 0x786, NI_660x_READ_WRITE, DATA_2B},
+ {"IO Config Register 12-13", 0x788, NI_660x_READ_WRITE, DATA_2B},
+ {"IO Config Register 14-15", 0x78A, NI_660x_READ_WRITE, DATA_2B},
+ {"IO Config Register 16-17", 0x78C, NI_660x_READ_WRITE, DATA_2B},
+ {"IO Config Register 18-19", 0x78E, NI_660x_READ_WRITE, DATA_2B},
+ {"IO Config Register 20-21", 0x790, NI_660x_READ_WRITE, DATA_2B},
+ {"IO Config Register 22-23", 0x792, NI_660x_READ_WRITE, DATA_2B},
+ {"IO Config Register 24-25", 0x794, NI_660x_READ_WRITE, DATA_2B},
+ {"IO Config Register 26-27", 0x796, NI_660x_READ_WRITE, DATA_2B},
+ {"IO Config Register 28-29", 0x798, NI_660x_READ_WRITE, DATA_2B},
+ {"IO Config Register 30-31", 0x79A, NI_660x_READ_WRITE, DATA_2B},
+ {"IO Config Register 32-33", 0x79C, NI_660x_READ_WRITE, DATA_2B},
+ {"IO Config Register 34-35", 0x79E, NI_660x_READ_WRITE, DATA_2B},
+ {"IO Config Register 36-37", 0x7A0, NI_660x_READ_WRITE, DATA_2B},
+ {"IO Config Register 38-39", 0x7A2, NI_660x_READ_WRITE, DATA_2B}
+};
+
+/* kind of ENABLE for the second counter */
+enum clock_config_register_bits {
+ CounterSwap = 0x1 << 21
+};
+
+/* ioconfigreg */
+static inline unsigned ioconfig_bitshift(unsigned pfi_channel)
+{
+ return (pfi_channel % 2) ? 0 : 8;
+}
+
+static inline unsigned pfi_output_select_mask(unsigned pfi_channel)
+{
+ return 0x3 << ioconfig_bitshift(pfi_channel);
+}
+
+static inline unsigned pfi_output_select_bits(unsigned pfi_channel,
+ unsigned output_select)
+{
+ return (output_select & 0x3) << ioconfig_bitshift(pfi_channel);
+}
+
+static inline unsigned pfi_input_select_mask(unsigned pfi_channel)
+{
+ return 0x7 << (4 + ioconfig_bitshift(pfi_channel));
+}
+
+static inline unsigned pfi_input_select_bits(unsigned pfi_channel,
+ unsigned input_select)
+{
+ return (input_select & 0x7) << (4 + ioconfig_bitshift(pfi_channel));
+}
+
+/* dma configuration register bits */
+static inline unsigned dma_select_mask(unsigned dma_channel)
+{
+ BUG_ON(dma_channel >= MAX_DMA_CHANNEL);
+ return 0x1f << (8 * dma_channel);
+}
+
+enum dma_selection {
+ dma_selection_none = 0x1f,
+};
+
+static inline unsigned dma_select_bits(unsigned dma_channel, unsigned selection)
+{
+ BUG_ON(dma_channel >= MAX_DMA_CHANNEL);
+ return (selection << (8 * dma_channel)) & dma_select_mask(dma_channel);
+}
+
+static inline unsigned dma_reset_bit(unsigned dma_channel)
+{
+ BUG_ON(dma_channel >= MAX_DMA_CHANNEL);
+ return 0x80 << (8 * dma_channel);
+}
+
+enum global_interrupt_status_register_bits {
+ Counter_0_Int_Bit = 0x100,
+ Counter_1_Int_Bit = 0x200,
+ Counter_2_Int_Bit = 0x400,
+ Counter_3_Int_Bit = 0x800,
+ Cascade_Int_Bit = 0x20000000,
+ Global_Int_Bit = 0x80000000
+};
+
+enum global_interrupt_config_register_bits {
+ Cascade_Int_Enable_Bit = 0x20000000,
+ Global_Int_Polarity_Bit = 0x40000000,
+ Global_Int_Enable_Bit = 0x80000000
+};
+
+/* Offset of the GPCT chips from the base-address of the card */
+/* First chip is at base-address + 0x00, etc. */
+static const unsigned GPCT_OFFSET[2] = { 0x0, 0x800 };
+
+enum ni_660x_boardid {
+ BOARD_PCI6601,
+ BOARD_PCI6602,
+ BOARD_PXI6602,
+ BOARD_PXI6608,
+ BOARD_PXI6624
+};
+
+struct ni_660x_board {
+ const char *name;
+ unsigned n_chips; /* total number of TIO chips */
+};
+
+static const struct ni_660x_board ni_660x_boards[] = {
+ [BOARD_PCI6601] = {
+ .name = "PCI-6601",
+ .n_chips = 1,
+ },
+ [BOARD_PCI6602] = {
+ .name = "PCI-6602",
+ .n_chips = 2,
+ },
+ [BOARD_PXI6602] = {
+ .name = "PXI-6602",
+ .n_chips = 2,
+ },
+ [BOARD_PXI6608] = {
+ .name = "PXI-6608",
+ .n_chips = 2,
+ },
+ [BOARD_PXI6624] = {
+ .name = "PXI-6624",
+ .n_chips = 2,
+ },
+};
+
+#define NI_660X_MAX_NUM_CHIPS 2
+#define NI_660X_MAX_NUM_COUNTERS (NI_660X_MAX_NUM_CHIPS * counters_per_chip)
+
+struct ni_660x_private {
+ struct mite_struct *mite;
+ struct ni_gpct_device *counter_dev;
+ uint64_t pfi_direction_bits;
+ struct mite_dma_descriptor_ring
+ *mite_rings[NI_660X_MAX_NUM_CHIPS][counters_per_chip];
+ spinlock_t mite_channel_lock;
+ /* interrupt_lock prevents races between interrupt and comedi_poll */
+ spinlock_t interrupt_lock;
+ unsigned dma_configuration_soft_copies[NI_660X_MAX_NUM_CHIPS];
+ spinlock_t soft_reg_copy_lock;
+ unsigned short pfi_output_selects[NUM_PFI_CHANNELS];
+};
+
+static inline unsigned ni_660x_num_counters(struct comedi_device *dev)
+{
+ const struct ni_660x_board *board = dev->board_ptr;
+
+ return board->n_chips * counters_per_chip;
+}
+
+static enum ni_660x_register ni_gpct_to_660x_register(enum ni_gpct_register reg)
+{
+ switch (reg) {
+ case NITIO_G0_AUTO_INC:
+ return NI660X_G0_AUTO_INC;
+ case NITIO_G1_AUTO_INC:
+ return NI660X_G1_AUTO_INC;
+ case NITIO_G2_AUTO_INC:
+ return NI660X_G2_AUTO_INC;
+ case NITIO_G3_AUTO_INC:
+ return NI660X_G3_AUTO_INC;
+ case NITIO_G0_CMD:
+ return NI660X_G0_CMD;
+ case NITIO_G1_CMD:
+ return NI660X_G1_CMD;
+ case NITIO_G2_CMD:
+ return NI660X_G2_CMD;
+ case NITIO_G3_CMD:
+ return NI660X_G3_CMD;
+ case NITIO_G0_HW_SAVE:
+ return NI660X_G0_HW_SAVE;
+ case NITIO_G1_HW_SAVE:
+ return NI660X_G1_HW_SAVE;
+ case NITIO_G2_HW_SAVE:
+ return NI660X_G2_HW_SAVE;
+ case NITIO_G3_HW_SAVE:
+ return NI660X_G3_HW_SAVE;
+ case NITIO_G0_SW_SAVE:
+ return NI660X_G0_SW_SAVE;
+ case NITIO_G1_SW_SAVE:
+ return NI660X_G1_SW_SAVE;
+ case NITIO_G2_SW_SAVE:
+ return NI660X_G2_SW_SAVE;
+ case NITIO_G3_SW_SAVE:
+ return NI660X_G3_SW_SAVE;
+ case NITIO_G0_MODE:
+ return NI660X_G0_MODE;
+ case NITIO_G1_MODE:
+ return NI660X_G1_MODE;
+ case NITIO_G2_MODE:
+ return NI660X_G2_MODE;
+ case NITIO_G3_MODE:
+ return NI660X_G3_MODE;
+ case NITIO_G0_LOADA:
+ return NI660X_G0_LOADA;
+ case NITIO_G1_LOADA:
+ return NI660X_G1_LOADA;
+ case NITIO_G2_LOADA:
+ return NI660X_G2_LOADA;
+ case NITIO_G3_LOADA:
+ return NI660X_G3_LOADA;
+ case NITIO_G0_LOADB:
+ return NI660X_G0_LOADB;
+ case NITIO_G1_LOADB:
+ return NI660X_G1_LOADB;
+ case NITIO_G2_LOADB:
+ return NI660X_G2_LOADB;
+ case NITIO_G3_LOADB:
+ return NI660X_G3_LOADB;
+ case NITIO_G0_INPUT_SEL:
+ return NI660X_G0_INPUT_SEL;
+ case NITIO_G1_INPUT_SEL:
+ return NI660X_G1_INPUT_SEL;
+ case NITIO_G2_INPUT_SEL:
+ return NI660X_G2_INPUT_SEL;
+ case NITIO_G3_INPUT_SEL:
+ return NI660X_G3_INPUT_SEL;
+ case NITIO_G01_STATUS:
+ return NI660X_G01_STATUS;
+ case NITIO_G23_STATUS:
+ return NI660X_G23_STATUS;
+ case NITIO_G01_RESET:
+ return NI660X_G01_RESET;
+ case NITIO_G23_RESET:
+ return NI660X_G23_RESET;
+ case NITIO_G01_STATUS1:
+ return NI660X_G01_STATUS1;
+ case NITIO_G23_STATUS1:
+ return NI660X_G23_STATUS1;
+ case NITIO_G01_STATUS2:
+ return NI660X_G01_STATUS2;
+ case NITIO_G23_STATUS2:
+ return NI660X_G23_STATUS2;
+ case NITIO_G0_CNT_MODE:
+ return NI660X_G0_CNT_MODE;
+ case NITIO_G1_CNT_MODE:
+ return NI660X_G1_CNT_MODE;
+ case NITIO_G2_CNT_MODE:
+ return NI660X_G2_CNT_MODE;
+ case NITIO_G3_CNT_MODE:
+ return NI660X_G3_CNT_MODE;
+ case NITIO_G0_GATE2:
+ return NI660X_G0_GATE2;
+ case NITIO_G1_GATE2:
+ return NI660X_G1_GATE2;
+ case NITIO_G2_GATE2:
+ return NI660X_G2_GATE2;
+ case NITIO_G3_GATE2:
+ return NI660X_G3_GATE2;
+ case NITIO_G0_DMA_CFG:
+ return NI660X_G0_DMA_CFG;
+ case NITIO_G0_DMA_STATUS:
+ return NI660X_G0_DMA_STATUS;
+ case NITIO_G1_DMA_CFG:
+ return NI660X_G1_DMA_CFG;
+ case NITIO_G1_DMA_STATUS:
+ return NI660X_G1_DMA_STATUS;
+ case NITIO_G2_DMA_CFG:
+ return NI660X_G2_DMA_CFG;
+ case NITIO_G2_DMA_STATUS:
+ return NI660X_G2_DMA_STATUS;
+ case NITIO_G3_DMA_CFG:
+ return NI660X_G3_DMA_CFG;
+ case NITIO_G3_DMA_STATUS:
+ return NI660X_G3_DMA_STATUS;
+ case NITIO_G0_INT_ACK:
+ return NI660X_G0_INT_ACK;
+ case NITIO_G1_INT_ACK:
+ return NI660X_G1_INT_ACK;
+ case NITIO_G2_INT_ACK:
+ return NI660X_G2_INT_ACK;
+ case NITIO_G3_INT_ACK:
+ return NI660X_G3_INT_ACK;
+ case NITIO_G0_STATUS:
+ return NI660X_G0_STATUS;
+ case NITIO_G1_STATUS:
+ return NI660X_G1_STATUS;
+ case NITIO_G2_STATUS:
+ return NI660X_G2_STATUS;
+ case NITIO_G3_STATUS:
+ return NI660X_G3_STATUS;
+ case NITIO_G0_INT_ENA:
+ return NI660X_G0_INT_ENA;
+ case NITIO_G1_INT_ENA:
+ return NI660X_G1_INT_ENA;
+ case NITIO_G2_INT_ENA:
+ return NI660X_G2_INT_ENA;
+ case NITIO_G3_INT_ENA:
+ return NI660X_G3_INT_ENA;
+ default:
+ BUG();
+ return 0;
+ }
+}
+
+static inline void ni_660x_write_register(struct comedi_device *dev,
+ unsigned chip, unsigned bits,
+ enum ni_660x_register reg)
+{
+ unsigned int addr = GPCT_OFFSET[chip] + registerData[reg].offset;
+
+ switch (registerData[reg].size) {
+ case DATA_2B:
+ writew(bits, dev->mmio + addr);
+ break;
+ case DATA_4B:
+ writel(bits, dev->mmio + addr);
+ break;
+ default:
+ BUG();
+ break;
+ }
+}
+
+static inline unsigned ni_660x_read_register(struct comedi_device *dev,
+ unsigned chip,
+ enum ni_660x_register reg)
+{
+ unsigned int addr = GPCT_OFFSET[chip] + registerData[reg].offset;
+
+ switch (registerData[reg].size) {
+ case DATA_2B:
+ return readw(dev->mmio + addr);
+ case DATA_4B:
+ return readl(dev->mmio + addr);
+ default:
+ BUG();
+ break;
+ }
+ return 0;
+}
+
+static void ni_gpct_write_register(struct ni_gpct *counter, unsigned bits,
+ enum ni_gpct_register reg)
+{
+ struct comedi_device *dev = counter->counter_dev->dev;
+ enum ni_660x_register ni_660x_register = ni_gpct_to_660x_register(reg);
+ unsigned chip = counter->chip_index;
+
+ ni_660x_write_register(dev, chip, bits, ni_660x_register);
+}
+
+static unsigned ni_gpct_read_register(struct ni_gpct *counter,
+ enum ni_gpct_register reg)
+{
+ struct comedi_device *dev = counter->counter_dev->dev;
+ enum ni_660x_register ni_660x_register = ni_gpct_to_660x_register(reg);
+ unsigned chip = counter->chip_index;
+
+ return ni_660x_read_register(dev, chip, ni_660x_register);
+}
+
+static inline struct mite_dma_descriptor_ring *mite_ring(struct ni_660x_private
+ *priv,
+ struct ni_gpct
+ *counter)
+{
+ unsigned chip = counter->chip_index;
+
+ return priv->mite_rings[chip][counter->counter_index];
+}
+
+static inline void ni_660x_set_dma_channel(struct comedi_device *dev,
+ unsigned mite_channel,
+ struct ni_gpct *counter)
+{
+ struct ni_660x_private *devpriv = dev->private;
+ unsigned chip = counter->chip_index;
+ unsigned long flags;
+
+ spin_lock_irqsave(&devpriv->soft_reg_copy_lock, flags);
+ devpriv->dma_configuration_soft_copies[chip] &=
+ ~dma_select_mask(mite_channel);
+ devpriv->dma_configuration_soft_copies[chip] |=
+ dma_select_bits(mite_channel, counter->counter_index);
+ ni_660x_write_register(dev, chip,
+ devpriv->dma_configuration_soft_copies[chip] |
+ dma_reset_bit(mite_channel), NI660X_DMA_CFG);
+ mmiowb();
+ spin_unlock_irqrestore(&devpriv->soft_reg_copy_lock, flags);
+}
+
+static inline void ni_660x_unset_dma_channel(struct comedi_device *dev,
+ unsigned mite_channel,
+ struct ni_gpct *counter)
+{
+ struct ni_660x_private *devpriv = dev->private;
+ unsigned chip = counter->chip_index;
+ unsigned long flags;
+
+ spin_lock_irqsave(&devpriv->soft_reg_copy_lock, flags);
+ devpriv->dma_configuration_soft_copies[chip] &=
+ ~dma_select_mask(mite_channel);
+ devpriv->dma_configuration_soft_copies[chip] |=
+ dma_select_bits(mite_channel, dma_selection_none);
+ ni_660x_write_register(dev, chip,
+ devpriv->dma_configuration_soft_copies[chip],
+ NI660X_DMA_CFG);
+ mmiowb();
+ spin_unlock_irqrestore(&devpriv->soft_reg_copy_lock, flags);
+}
+
+static int ni_660x_request_mite_channel(struct comedi_device *dev,
+ struct ni_gpct *counter,
+ enum comedi_io_direction direction)
+{
+ struct ni_660x_private *devpriv = dev->private;
+ unsigned long flags;
+ struct mite_channel *mite_chan;
+
+ spin_lock_irqsave(&devpriv->mite_channel_lock, flags);
+ BUG_ON(counter->mite_chan);
+ mite_chan = mite_request_channel(devpriv->mite,
+ mite_ring(devpriv, counter));
+ if (!mite_chan) {
+ spin_unlock_irqrestore(&devpriv->mite_channel_lock, flags);
+ dev_err(dev->class_dev,
+ "failed to reserve mite dma channel for counter\n");
+ return -EBUSY;
+ }
+ mite_chan->dir = direction;
+ ni_tio_set_mite_channel(counter, mite_chan);
+ ni_660x_set_dma_channel(dev, mite_chan->channel, counter);
+ spin_unlock_irqrestore(&devpriv->mite_channel_lock, flags);
+ return 0;
+}
+
+static void ni_660x_release_mite_channel(struct comedi_device *dev,
+ struct ni_gpct *counter)
+{
+ struct ni_660x_private *devpriv = dev->private;
+ unsigned long flags;
+
+ spin_lock_irqsave(&devpriv->mite_channel_lock, flags);
+ if (counter->mite_chan) {
+ struct mite_channel *mite_chan = counter->mite_chan;
+
+ ni_660x_unset_dma_channel(dev, mite_chan->channel, counter);
+ ni_tio_set_mite_channel(counter, NULL);
+ mite_release_channel(mite_chan);
+ }
+ spin_unlock_irqrestore(&devpriv->mite_channel_lock, flags);
+}
+
+static int ni_660x_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
+{
+ struct ni_gpct *counter = s->private;
+ int retval;
+
+ retval = ni_660x_request_mite_channel(dev, counter, COMEDI_INPUT);
+ if (retval) {
+ dev_err(dev->class_dev,
+ "no dma channel available for use by counter\n");
+ return retval;
+ }
+ ni_tio_acknowledge(counter);
+
+ return ni_tio_cmd(dev, s);
+}
+
+static int ni_660x_cancel(struct comedi_device *dev, struct comedi_subdevice *s)
+{
+ struct ni_gpct *counter = s->private;
+ int retval;
+
+ retval = ni_tio_cancel(counter);
+ ni_660x_release_mite_channel(dev, counter);
+ return retval;
+}
+
+static void set_tio_counterswap(struct comedi_device *dev, int chip)
+{
+ unsigned bits = 0;
+
+ /*
+ * See P. 3.5 of the Register-Level Programming manual.
+ * The CounterSwap bit has to be set on the second chip,
+ * otherwise it will try to use the same pins as the
+ * first chip.
+ */
+ if (chip)
+ bits = CounterSwap;
+
+ ni_660x_write_register(dev, chip, bits, NI660X_CLK_CFG);
+}
+
+static void ni_660x_handle_gpct_interrupt(struct comedi_device *dev,
+ struct comedi_subdevice *s)
+{
+ struct ni_gpct *counter = s->private;
+
+ ni_tio_handle_interrupt(counter, s);
+ comedi_handle_events(dev, s);
+}
+
+static irqreturn_t ni_660x_interrupt(int irq, void *d)
+{
+ struct comedi_device *dev = d;
+ struct ni_660x_private *devpriv = dev->private;
+ struct comedi_subdevice *s;
+ unsigned i;
+ unsigned long flags;
+
+ if (!dev->attached)
+ return IRQ_NONE;
+ /* lock to avoid race with comedi_poll */
+ spin_lock_irqsave(&devpriv->interrupt_lock, flags);
+ smp_mb();
+ for (i = 0; i < ni_660x_num_counters(dev); ++i) {
+ s = &dev->subdevices[NI_660X_GPCT_SUBDEV(i)];
+ ni_660x_handle_gpct_interrupt(dev, s);
+ }
+ spin_unlock_irqrestore(&devpriv->interrupt_lock, flags);
+ return IRQ_HANDLED;
+}
+
+static int ni_660x_input_poll(struct comedi_device *dev,
+ struct comedi_subdevice *s)
+{
+ struct ni_660x_private *devpriv = dev->private;
+ struct ni_gpct *counter = s->private;
+ unsigned long flags;
+
+ /* lock to avoid race with comedi_poll */
+ spin_lock_irqsave(&devpriv->interrupt_lock, flags);
+ mite_sync_input_dma(counter->mite_chan, s);
+ spin_unlock_irqrestore(&devpriv->interrupt_lock, flags);
+ return comedi_buf_read_n_available(s);
+}
+
+static int ni_660x_buf_change(struct comedi_device *dev,
+ struct comedi_subdevice *s)
+{
+ struct ni_660x_private *devpriv = dev->private;
+ struct ni_gpct *counter = s->private;
+ int ret;
+
+ ret = mite_buf_change(mite_ring(devpriv, counter), s);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static int ni_660x_allocate_private(struct comedi_device *dev)
+{
+ struct ni_660x_private *devpriv;
+ unsigned i;
+
+ devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
+ if (!devpriv)
+ return -ENOMEM;
+
+ spin_lock_init(&devpriv->mite_channel_lock);
+ spin_lock_init(&devpriv->interrupt_lock);
+ spin_lock_init(&devpriv->soft_reg_copy_lock);
+ for (i = 0; i < NUM_PFI_CHANNELS; ++i)
+ devpriv->pfi_output_selects[i] = pfi_output_select_counter;
+
+ return 0;
+}
+
+static int ni_660x_alloc_mite_rings(struct comedi_device *dev)
+{
+ const struct ni_660x_board *board = dev->board_ptr;
+ struct ni_660x_private *devpriv = dev->private;
+ unsigned i;
+ unsigned j;
+
+ for (i = 0; i < board->n_chips; ++i) {
+ for (j = 0; j < counters_per_chip; ++j) {
+ devpriv->mite_rings[i][j] =
+ mite_alloc_ring(devpriv->mite);
+ if (!devpriv->mite_rings[i][j])
+ return -ENOMEM;
+ }
+ }
+ return 0;
+}
+
+static void ni_660x_free_mite_rings(struct comedi_device *dev)
+{
+ const struct ni_660x_board *board = dev->board_ptr;
+ struct ni_660x_private *devpriv = dev->private;
+ unsigned i;
+ unsigned j;
+
+ for (i = 0; i < board->n_chips; ++i) {
+ for (j = 0; j < counters_per_chip; ++j)
+ mite_free_ring(devpriv->mite_rings[i][j]);
+ }
+}
+
+static void init_tio_chip(struct comedi_device *dev, int chipset)
+{
+ struct ni_660x_private *devpriv = dev->private;
+ unsigned i;
+
+ /* init dma configuration register */
+ devpriv->dma_configuration_soft_copies[chipset] = 0;
+ for (i = 0; i < MAX_DMA_CHANNEL; ++i) {
+ devpriv->dma_configuration_soft_copies[chipset] |=
+ dma_select_bits(i, dma_selection_none) & dma_select_mask(i);
+ }
+ ni_660x_write_register(dev, chipset,
+ devpriv->dma_configuration_soft_copies[chipset],
+ NI660X_DMA_CFG);
+ for (i = 0; i < NUM_PFI_CHANNELS; ++i)
+ ni_660x_write_register(dev, chipset, 0, IOConfigReg(i));
+}
+
+static int ni_660x_dio_insn_bits(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn, unsigned int *data)
+{
+ unsigned base_bitfield_channel = CR_CHAN(insn->chanspec);
+
+ /* Check if we have to write some bits */
+ if (data[0]) {
+ s->state &= ~(data[0] << base_bitfield_channel);
+ s->state |= (data[0] & data[1]) << base_bitfield_channel;
+ /* Write out the new digital output lines */
+ ni_660x_write_register(dev, 0, s->state, NI660X_DIO32_OUTPUT);
+ }
+ /* on return, data[1] contains the value of the digital
+ * input and output lines. */
+ data[1] = (ni_660x_read_register(dev, 0, NI660X_DIO32_INPUT) >>
+ base_bitfield_channel);
+
+ return insn->n;
+}
+
+static void ni_660x_select_pfi_output(struct comedi_device *dev,
+ unsigned pfi_channel,
+ unsigned output_select)
+{
+ const struct ni_660x_board *board = dev->board_ptr;
+ static const unsigned counter_4_7_first_pfi = 8;
+ static const unsigned counter_4_7_last_pfi = 23;
+ unsigned active_chipset = 0;
+ unsigned idle_chipset = 0;
+ unsigned active_bits;
+ unsigned idle_bits;
+
+ if (board->n_chips > 1) {
+ if (output_select == pfi_output_select_counter &&
+ pfi_channel >= counter_4_7_first_pfi &&
+ pfi_channel <= counter_4_7_last_pfi) {
+ active_chipset = 1;
+ idle_chipset = 0;
+ } else {
+ active_chipset = 0;
+ idle_chipset = 1;
+ }
+ }
+
+ if (idle_chipset != active_chipset) {
+ idle_bits =
+ ni_660x_read_register(dev, idle_chipset,
+ IOConfigReg(pfi_channel));
+ idle_bits &= ~pfi_output_select_mask(pfi_channel);
+ idle_bits |=
+ pfi_output_select_bits(pfi_channel,
+ pfi_output_select_high_Z);
+ ni_660x_write_register(dev, idle_chipset, idle_bits,
+ IOConfigReg(pfi_channel));
+ }
+
+ active_bits =
+ ni_660x_read_register(dev, active_chipset,
+ IOConfigReg(pfi_channel));
+ active_bits &= ~pfi_output_select_mask(pfi_channel);
+ active_bits |= pfi_output_select_bits(pfi_channel, output_select);
+ ni_660x_write_register(dev, active_chipset, active_bits,
+ IOConfigReg(pfi_channel));
+}
+
+static int ni_660x_set_pfi_routing(struct comedi_device *dev, unsigned chan,
+ unsigned source)
+{
+ struct ni_660x_private *devpriv = dev->private;
+
+ if (source > num_pfi_output_selects)
+ return -EINVAL;
+ if (source == pfi_output_select_high_Z)
+ return -EINVAL;
+ if (chan < min_counter_pfi_chan) {
+ if (source == pfi_output_select_counter)
+ return -EINVAL;
+ } else if (chan > max_dio_pfi_chan) {
+ if (source == pfi_output_select_do)
+ return -EINVAL;
+ }
+
+ devpriv->pfi_output_selects[chan] = source;
+ if (devpriv->pfi_direction_bits & (((uint64_t) 1) << chan))
+ ni_660x_select_pfi_output(dev, chan,
+ devpriv->pfi_output_selects[chan]);
+ return 0;
+}
+
+static int ni_660x_dio_insn_config(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ struct ni_660x_private *devpriv = dev->private;
+ unsigned int chan = CR_CHAN(insn->chanspec);
+ uint64_t bit = 1ULL << chan;
+ unsigned int val;
+ int ret;
+
+ switch (data[0]) {
+ case INSN_CONFIG_DIO_OUTPUT:
+ devpriv->pfi_direction_bits |= bit;
+ ni_660x_select_pfi_output(dev, chan,
+ devpriv->pfi_output_selects[chan]);
+ break;
+
+ case INSN_CONFIG_DIO_INPUT:
+ devpriv->pfi_direction_bits &= ~bit;
+ ni_660x_select_pfi_output(dev, chan, pfi_output_select_high_Z);
+ break;
+
+ case INSN_CONFIG_DIO_QUERY:
+ data[1] = (devpriv->pfi_direction_bits & bit) ? COMEDI_OUTPUT
+ : COMEDI_INPUT;
+ break;
+
+ case INSN_CONFIG_SET_ROUTING:
+ ret = ni_660x_set_pfi_routing(dev, chan, data[1]);
+ if (ret)
+ return ret;
+ break;
+
+ case INSN_CONFIG_GET_ROUTING:
+ data[1] = devpriv->pfi_output_selects[chan];
+ break;
+
+ case INSN_CONFIG_FILTER:
+ val = ni_660x_read_register(dev, 0, IOConfigReg(chan));
+ val &= ~pfi_input_select_mask(chan);
+ val |= pfi_input_select_bits(chan, data[1]);
+ ni_660x_write_register(dev, 0, val, IOConfigReg(chan));
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ return insn->n;
+}
+
+static int ni_660x_auto_attach(struct comedi_device *dev,
+ unsigned long context)
+{
+ struct pci_dev *pcidev = comedi_to_pci_dev(dev);
+ const struct ni_660x_board *board = NULL;
+ struct ni_660x_private *devpriv;
+ struct comedi_subdevice *s;
+ int ret;
+ unsigned i;
+ unsigned global_interrupt_config_bits;
+
+ if (context < ARRAY_SIZE(ni_660x_boards))
+ board = &ni_660x_boards[context];
+ if (!board)
+ return -ENODEV;
+ dev->board_ptr = board;
+ dev->board_name = board->name;
+
+ ret = comedi_pci_enable(dev);
+ if (ret)
+ return ret;
+
+ ret = ni_660x_allocate_private(dev);
+ if (ret < 0)
+ return ret;
+ devpriv = dev->private;
+
+ devpriv->mite = mite_alloc(pcidev);
+ if (!devpriv->mite)
+ return -ENOMEM;
+
+ ret = mite_setup2(dev, devpriv->mite, true);
+ if (ret < 0)
+ return ret;
+
+ ret = ni_660x_alloc_mite_rings(dev);
+ if (ret < 0)
+ return ret;
+
+ ret = comedi_alloc_subdevices(dev, 2 + NI_660X_MAX_NUM_COUNTERS);
+ if (ret)
+ return ret;
+
+ s = &dev->subdevices[0];
+ /* Old GENERAL-PURPOSE COUNTER/TIME (GPCT) subdevice, no longer used */
+ s->type = COMEDI_SUBD_UNUSED;
+
+ s = &dev->subdevices[NI_660X_DIO_SUBDEV];
+ /* DIGITAL I/O SUBDEVICE */
+ s->type = COMEDI_SUBD_DIO;
+ s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
+ s->n_chan = NUM_PFI_CHANNELS;
+ s->maxdata = 1;
+ s->range_table = &range_digital;
+ s->insn_bits = ni_660x_dio_insn_bits;
+ s->insn_config = ni_660x_dio_insn_config;
+ /* we use the ioconfig registers to control dio direction, so zero
+ output enables in stc dio control reg */
+ ni_660x_write_register(dev, 0, 0, NI660X_STC_DIO_CONTROL);
+
+ devpriv->counter_dev = ni_gpct_device_construct(dev,
+ &ni_gpct_write_register,
+ &ni_gpct_read_register,
+ ni_gpct_variant_660x,
+ ni_660x_num_counters
+ (dev));
+ if (!devpriv->counter_dev)
+ return -ENOMEM;
+ for (i = 0; i < NI_660X_MAX_NUM_COUNTERS; ++i) {
+ s = &dev->subdevices[NI_660X_GPCT_SUBDEV(i)];
+ if (i < ni_660x_num_counters(dev)) {
+ s->type = COMEDI_SUBD_COUNTER;
+ s->subdev_flags = SDF_READABLE | SDF_WRITABLE |
+ SDF_LSAMPL | SDF_CMD_READ;
+ s->n_chan = 3;
+ s->maxdata = 0xffffffff;
+ s->insn_read = ni_tio_insn_read;
+ s->insn_write = ni_tio_insn_write;
+ s->insn_config = ni_tio_insn_config;
+ s->do_cmd = &ni_660x_cmd;
+ s->len_chanlist = 1;
+ s->do_cmdtest = ni_tio_cmdtest;
+ s->cancel = &ni_660x_cancel;
+ s->poll = &ni_660x_input_poll;
+ s->async_dma_dir = DMA_BIDIRECTIONAL;
+ s->buf_change = &ni_660x_buf_change;
+ s->private = &devpriv->counter_dev->counters[i];
+
+ devpriv->counter_dev->counters[i].chip_index =
+ i / counters_per_chip;
+ devpriv->counter_dev->counters[i].counter_index =
+ i % counters_per_chip;
+ } else {
+ s->type = COMEDI_SUBD_UNUSED;
+ }
+ }
+ for (i = 0; i < board->n_chips; ++i)
+ init_tio_chip(dev, i);
+
+ for (i = 0; i < ni_660x_num_counters(dev); ++i)
+ ni_tio_init_counter(&devpriv->counter_dev->counters[i]);
+
+ for (i = 0; i < NUM_PFI_CHANNELS; ++i) {
+ if (i < min_counter_pfi_chan)
+ ni_660x_set_pfi_routing(dev, i, pfi_output_select_do);
+ else
+ ni_660x_set_pfi_routing(dev, i,
+ pfi_output_select_counter);
+ ni_660x_select_pfi_output(dev, i, pfi_output_select_high_Z);
+ }
+ /* to be safe, set counterswap bits on tio chips after all the counter
+ outputs have been set to high impedance mode */
+ for (i = 0; i < board->n_chips; ++i)
+ set_tio_counterswap(dev, i);
+
+ ret = request_irq(pcidev->irq, ni_660x_interrupt, IRQF_SHARED,
+ dev->board_name, dev);
+ if (ret < 0) {
+ dev_warn(dev->class_dev, " irq not available\n");
+ return ret;
+ }
+ dev->irq = pcidev->irq;
+ global_interrupt_config_bits = Global_Int_Enable_Bit;
+ if (board->n_chips > 1)
+ global_interrupt_config_bits |= Cascade_Int_Enable_Bit;
+ ni_660x_write_register(dev, 0, global_interrupt_config_bits,
+ NI660X_GLOBAL_INT_CFG);
+
+ return 0;
+}
+
+static void ni_660x_detach(struct comedi_device *dev)
+{
+ struct ni_660x_private *devpriv = dev->private;
+
+ if (dev->irq)
+ free_irq(dev->irq, dev);
+ if (devpriv) {
+ if (devpriv->counter_dev)
+ ni_gpct_device_destroy(devpriv->counter_dev);
+ ni_660x_free_mite_rings(dev);
+ mite_detach(devpriv->mite);
+ }
+ if (dev->mmio)
+ iounmap(dev->mmio);
+ comedi_pci_disable(dev);
+}
+
+static struct comedi_driver ni_660x_driver = {
+ .driver_name = "ni_660x",
+ .module = THIS_MODULE,
+ .auto_attach = ni_660x_auto_attach,
+ .detach = ni_660x_detach,
+};
+
+static int ni_660x_pci_probe(struct pci_dev *dev,
+ const struct pci_device_id *id)
+{
+ return comedi_pci_auto_config(dev, &ni_660x_driver, id->driver_data);
+}
+
+static const struct pci_device_id ni_660x_pci_table[] = {
+ { PCI_VDEVICE(NI, 0x1310), BOARD_PCI6602 },
+ { PCI_VDEVICE(NI, 0x1360), BOARD_PXI6602 },
+ { PCI_VDEVICE(NI, 0x2c60), BOARD_PCI6601 },
+ { PCI_VDEVICE(NI, 0x2cc0), BOARD_PXI6608 },
+ { PCI_VDEVICE(NI, 0x1e40), BOARD_PXI6624 },
+ { 0 }
+};
+MODULE_DEVICE_TABLE(pci, ni_660x_pci_table);
+
+static struct pci_driver ni_660x_pci_driver = {
+ .name = "ni_660x",
+ .id_table = ni_660x_pci_table,
+ .probe = ni_660x_pci_probe,
+ .remove = comedi_pci_auto_unconfig,
+};
+module_comedi_pci_driver(ni_660x_driver, ni_660x_pci_driver);
+
+MODULE_AUTHOR("Comedi http://www.comedi.org");
+MODULE_DESCRIPTION("Comedi low-level driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/comedi/drivers/ni_670x.c b/drivers/staging/comedi/drivers/ni_670x.c
new file mode 100644
index 000000000..13c6ccb1f
--- /dev/null
+++ b/drivers/staging/comedi/drivers/ni_670x.c
@@ -0,0 +1,296 @@
+/*
+ comedi/drivers/ni_670x.c
+ Hardware driver for NI 670x devices
+
+ COMEDI - Linux Control and Measurement Device Interface
+ Copyright (C) 1997-2001 David A. Schleef <ds@schleef.org>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+*/
+/*
+Driver: ni_670x
+Description: National Instruments 670x
+Author: Bart Joris <bjoris@advalvas.be>
+Updated: Wed, 11 Dec 2002 18:25:35 -0800
+Devices: [National Instruments] PCI-6703 (ni_670x), PCI-6704
+Status: unknown
+
+Commands are not supported.
+*/
+
+/*
+ Bart Joris <bjoris@advalvas.be> Last updated on 20/08/2001
+
+ Manuals:
+
+ 322110a.pdf PCI/PXI-6704 User Manual
+ 322110b.pdf PCI/PXI-6703/6704 User Manual
+
+*/
+
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/slab.h>
+
+#include "../comedi_pci.h"
+
+#define AO_VALUE_OFFSET 0x00
+#define AO_CHAN_OFFSET 0x0c
+#define AO_STATUS_OFFSET 0x10
+#define AO_CONTROL_OFFSET 0x10
+#define DIO_PORT0_DIR_OFFSET 0x20
+#define DIO_PORT0_DATA_OFFSET 0x24
+#define DIO_PORT1_DIR_OFFSET 0x28
+#define DIO_PORT1_DATA_OFFSET 0x2c
+#define MISC_STATUS_OFFSET 0x14
+#define MISC_CONTROL_OFFSET 0x14
+
+enum ni_670x_boardid {
+ BOARD_PCI6703,
+ BOARD_PXI6704,
+ BOARD_PCI6704,
+};
+
+struct ni_670x_board {
+ const char *name;
+ unsigned short ao_chans;
+};
+
+static const struct ni_670x_board ni_670x_boards[] = {
+ [BOARD_PCI6703] = {
+ .name = "PCI-6703",
+ .ao_chans = 16,
+ },
+ [BOARD_PXI6704] = {
+ .name = "PXI-6704",
+ .ao_chans = 32,
+ },
+ [BOARD_PCI6704] = {
+ .name = "PCI-6704",
+ .ao_chans = 32,
+ },
+};
+
+struct ni_670x_private {
+ int boardtype;
+ int dio;
+};
+
+static int ni_670x_ao_insn_write(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ unsigned int chan = CR_CHAN(insn->chanspec);
+ unsigned int val = s->readback[chan];
+ int i;
+
+ /*
+ * Channel number mapping:
+ *
+ * NI 6703/ NI 6704 | NI 6704 Only
+ * -------------------------------
+ * vch(0) : 0 | ich(16) : 1
+ * vch(1) : 2 | ich(17) : 3
+ * ... | ...
+ * vch(15) : 30 | ich(31) : 31
+ */
+ for (i = 0; i < insn->n; i++) {
+ val = data[i];
+ /* First write in channel register which channel to use */
+ writel(((chan & 15) << 1) | ((chan & 16) >> 4),
+ dev->mmio + AO_CHAN_OFFSET);
+ /* write channel value */
+ writel(val, dev->mmio + AO_VALUE_OFFSET);
+ }
+ s->readback[chan] = val;
+
+ return insn->n;
+}
+
+static int ni_670x_dio_insn_bits(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ if (comedi_dio_update_state(s, data))
+ writel(s->state, dev->mmio + DIO_PORT0_DATA_OFFSET);
+
+ data[1] = readl(dev->mmio + DIO_PORT0_DATA_OFFSET);
+
+ return insn->n;
+}
+
+static int ni_670x_dio_insn_config(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ int ret;
+
+ ret = comedi_dio_insn_config(dev, s, insn, data, 0);
+ if (ret)
+ return ret;
+
+ writel(s->io_bits, dev->mmio + DIO_PORT0_DIR_OFFSET);
+
+ return insn->n;
+}
+
+/* ripped from mite.h and mite_setup2() to avoid mite dependency */
+#define MITE_IODWBSR 0xc0 /* IO Device Window Base Size Register */
+#define WENAB (1 << 7) /* window enable */
+
+static int ni_670x_mite_init(struct pci_dev *pcidev)
+{
+ void __iomem *mite_base;
+ u32 main_phys_addr;
+
+ /* ioremap the MITE registers (BAR 0) temporarily */
+ mite_base = pci_ioremap_bar(pcidev, 0);
+ if (!mite_base)
+ return -ENOMEM;
+
+ /* set data window to main registers (BAR 1) */
+ main_phys_addr = pci_resource_start(pcidev, 1);
+ writel(main_phys_addr | WENAB, mite_base + MITE_IODWBSR);
+
+ /* finished with MITE registers */
+ iounmap(mite_base);
+ return 0;
+}
+
+static int ni_670x_auto_attach(struct comedi_device *dev,
+ unsigned long context)
+{
+ struct pci_dev *pcidev = comedi_to_pci_dev(dev);
+ const struct ni_670x_board *thisboard = NULL;
+ struct ni_670x_private *devpriv;
+ struct comedi_subdevice *s;
+ int ret;
+ int i;
+
+ if (context < ARRAY_SIZE(ni_670x_boards))
+ thisboard = &ni_670x_boards[context];
+ if (!thisboard)
+ return -ENODEV;
+ dev->board_ptr = thisboard;
+ dev->board_name = thisboard->name;
+
+ ret = comedi_pci_enable(dev);
+ if (ret)
+ return ret;
+
+ devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
+ if (!devpriv)
+ return -ENOMEM;
+
+ ret = ni_670x_mite_init(pcidev);
+ if (ret)
+ return ret;
+
+ dev->mmio = pci_ioremap_bar(pcidev, 1);
+ if (!dev->mmio)
+ return -ENOMEM;
+
+ ret = comedi_alloc_subdevices(dev, 2);
+ if (ret)
+ return ret;
+
+ s = &dev->subdevices[0];
+ /* analog output subdevice */
+ s->type = COMEDI_SUBD_AO;
+ s->subdev_flags = SDF_WRITABLE;
+ s->n_chan = thisboard->ao_chans;
+ s->maxdata = 0xffff;
+ if (s->n_chan == 32) {
+ const struct comedi_lrange **range_table_list;
+
+ range_table_list = kmalloc(sizeof(struct comedi_lrange *) * 32,
+ GFP_KERNEL);
+ if (!range_table_list)
+ return -ENOMEM;
+ s->range_table_list = range_table_list;
+ for (i = 0; i < 16; i++) {
+ range_table_list[i] = &range_bipolar10;
+ range_table_list[16 + i] = &range_0_20mA;
+ }
+ } else {
+ s->range_table = &range_bipolar10;
+ }
+ s->insn_write = ni_670x_ao_insn_write;
+
+ ret = comedi_alloc_subdev_readback(s);
+ if (ret)
+ return ret;
+
+ s = &dev->subdevices[1];
+ /* digital i/o subdevice */
+ s->type = COMEDI_SUBD_DIO;
+ s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
+ s->n_chan = 8;
+ s->maxdata = 1;
+ s->range_table = &range_digital;
+ s->insn_bits = ni_670x_dio_insn_bits;
+ s->insn_config = ni_670x_dio_insn_config;
+
+ /* Config of misc registers */
+ writel(0x10, dev->mmio + MISC_CONTROL_OFFSET);
+ /* Config of ao registers */
+ writel(0x00, dev->mmio + AO_CONTROL_OFFSET);
+
+ return 0;
+}
+
+static void ni_670x_detach(struct comedi_device *dev)
+{
+ struct comedi_subdevice *s;
+
+ comedi_pci_detach(dev);
+ if (dev->n_subdevices) {
+ s = &dev->subdevices[0];
+ if (s)
+ kfree(s->range_table_list);
+ }
+}
+
+static struct comedi_driver ni_670x_driver = {
+ .driver_name = "ni_670x",
+ .module = THIS_MODULE,
+ .auto_attach = ni_670x_auto_attach,
+ .detach = ni_670x_detach,
+};
+
+static int ni_670x_pci_probe(struct pci_dev *dev,
+ const struct pci_device_id *id)
+{
+ return comedi_pci_auto_config(dev, &ni_670x_driver, id->driver_data);
+}
+
+static const struct pci_device_id ni_670x_pci_table[] = {
+ { PCI_VDEVICE(NI, 0x1290), BOARD_PCI6704 },
+ { PCI_VDEVICE(NI, 0x1920), BOARD_PXI6704 },
+ { PCI_VDEVICE(NI, 0x2c90), BOARD_PCI6703 },
+ { 0 }
+};
+MODULE_DEVICE_TABLE(pci, ni_670x_pci_table);
+
+static struct pci_driver ni_670x_pci_driver = {
+ .name = "ni_670x",
+ .id_table = ni_670x_pci_table,
+ .probe = ni_670x_pci_probe,
+ .remove = comedi_pci_auto_unconfig,
+};
+module_comedi_pci_driver(ni_670x_driver, ni_670x_pci_driver);
+
+MODULE_AUTHOR("Comedi http://www.comedi.org");
+MODULE_DESCRIPTION("Comedi low-level driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/comedi/drivers/ni_at_a2150.c b/drivers/staging/comedi/drivers/ni_at_a2150.c
new file mode 100644
index 000000000..3a972d153
--- /dev/null
+++ b/drivers/staging/comedi/drivers/ni_at_a2150.c
@@ -0,0 +1,796 @@
+/*
+ comedi/drivers/ni_at_a2150.c
+ Driver for National Instruments AT-A2150 boards
+ Copyright (C) 2001, 2002 Frank Mori Hess <fmhess@users.sourceforge.net>
+
+ COMEDI - Linux Control and Measurement Device Interface
+ Copyright (C) 2000 David A. Schleef <ds@schleef.org>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+*/
+/*
+Driver: ni_at_a2150
+Description: National Instruments AT-A2150
+Author: Frank Mori Hess
+Status: works
+Devices: [National Instruments] AT-A2150C (at_a2150c), AT-2150S (at_a2150s)
+
+If you want to ac couple the board's inputs, use AREF_OTHER.
+
+Configuration options:
+ [0] - I/O port base address
+ [1] - IRQ (optional, required for timed conversions)
+ [2] - DMA (optional, required for timed conversions)
+
+*/
+/*
+Yet another driver for obsolete hardware brought to you by Frank Hess.
+Testing and debugging help provided by Dave Andruczyk.
+
+This driver supports the boards:
+
+AT-A2150C
+AT-A2150S
+
+The only difference is their master clock frequencies.
+
+Options:
+ [0] - base io address
+ [1] - irq
+ [2] - dma channel
+
+References (from ftp://ftp.natinst.com/support/manuals):
+
+ 320360.pdf AT-A2150 User Manual
+
+TODO:
+
+analog level triggering
+TRIG_WAKE_EOS
+
+*/
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/slab.h>
+#include <linux/io.h>
+
+#include "../comedidev.h"
+
+#include "comedi_isadma.h"
+#include "comedi_8254.h"
+
+#define A2150_DMA_BUFFER_SIZE 0xff00 /* size in bytes of dma buffer */
+
+/* Registers and bits */
+#define CONFIG_REG 0x0
+#define CHANNEL_BITS(x) ((x) & 0x7)
+#define CHANNEL_MASK 0x7
+#define CLOCK_SELECT_BITS(x) (((x) & 0x3) << 3)
+#define CLOCK_DIVISOR_BITS(x) (((x) & 0x3) << 5)
+#define CLOCK_MASK (0xf << 3)
+#define ENABLE0_BIT 0x80 /* enable (don't internally ground) channels 0 and 1 */
+#define ENABLE1_BIT 0x100 /* enable (don't internally ground) channels 2 and 3 */
+#define AC0_BIT 0x200 /* ac couple channels 0,1 */
+#define AC1_BIT 0x400 /* ac couple channels 2,3 */
+#define APD_BIT 0x800 /* analog power down */
+#define DPD_BIT 0x1000 /* digital power down */
+#define TRIGGER_REG 0x2 /* trigger config register */
+#define POST_TRIGGER_BITS 0x2
+#define DELAY_TRIGGER_BITS 0x3
+#define HW_TRIG_EN 0x10 /* enable hardware trigger */
+#define FIFO_START_REG 0x6 /* software start aquistion trigger */
+#define FIFO_RESET_REG 0x8 /* clears fifo + fifo flags */
+#define FIFO_DATA_REG 0xa /* read data */
+#define DMA_TC_CLEAR_REG 0xe /* clear dma terminal count interrupt */
+#define STATUS_REG 0x12 /* read only */
+#define FNE_BIT 0x1 /* fifo not empty */
+#define OVFL_BIT 0x8 /* fifo overflow */
+#define EDAQ_BIT 0x10 /* end of acquisition interrupt */
+#define DCAL_BIT 0x20 /* offset calibration in progress */
+#define INTR_BIT 0x40 /* interrupt has occurred */
+#define DMA_TC_BIT 0x80 /* dma terminal count interrupt has occurred */
+#define ID_BITS(x) (((x) >> 8) & 0x3)
+#define IRQ_DMA_CNTRL_REG 0x12 /* write only */
+#define DMA_CHAN_BITS(x) ((x) & 0x7) /* sets dma channel */
+#define DMA_EN_BIT 0x8 /* enables dma */
+#define IRQ_LVL_BITS(x) (((x) & 0xf) << 4) /* sets irq level */
+#define FIFO_INTR_EN_BIT 0x100 /* enable fifo interrupts */
+#define FIFO_INTR_FHF_BIT 0x200 /* interrupt fifo half full */
+#define DMA_INTR_EN_BIT 0x800 /* enable interrupt on dma terminal count */
+#define DMA_DEM_EN_BIT 0x1000 /* enables demand mode dma */
+#define I8253_BASE_REG 0x14
+
+struct a2150_board {
+ const char *name;
+ int clock[4]; /* master clock periods, in nanoseconds */
+ int num_clocks; /* number of available master clock speeds */
+ int ai_speed; /* maximum conversion rate in nanoseconds */
+};
+
+/* analog input range */
+static const struct comedi_lrange range_a2150 = {
+ 1, {
+ BIP_RANGE(2.828)
+ }
+};
+
+/* enum must match board indices */
+enum { a2150_c, a2150_s };
+static const struct a2150_board a2150_boards[] = {
+ {
+ .name = "at-a2150c",
+ .clock = {31250, 22676, 20833, 19531},
+ .num_clocks = 4,
+ .ai_speed = 19531,
+ },
+ {
+ .name = "at-a2150s",
+ .clock = {62500, 50000, 41667, 0},
+ .num_clocks = 3,
+ .ai_speed = 41667,
+ },
+};
+
+struct a2150_private {
+ struct comedi_isadma *dma;
+ unsigned int count; /* number of data points left to be taken */
+ int irq_dma_bits; /* irq/dma register bits */
+ int config_bits; /* config register bits */
+};
+
+/* interrupt service routine */
+static irqreturn_t a2150_interrupt(int irq, void *d)
+{
+ struct comedi_device *dev = d;
+ struct a2150_private *devpriv = dev->private;
+ struct comedi_isadma *dma = devpriv->dma;
+ struct comedi_isadma_desc *desc = &dma->desc[0];
+ struct comedi_subdevice *s = dev->read_subdev;
+ struct comedi_async *async = s->async;
+ struct comedi_cmd *cmd = &async->cmd;
+ unsigned short *buf = desc->virt_addr;
+ unsigned int max_points, num_points, residue, leftover;
+ unsigned short dpnt;
+ int status;
+ int i;
+
+ if (!dev->attached)
+ return IRQ_HANDLED;
+
+ status = inw(dev->iobase + STATUS_REG);
+ if ((status & INTR_BIT) == 0)
+ return IRQ_NONE;
+
+ if (status & OVFL_BIT) {
+ async->events |= COMEDI_CB_ERROR;
+ comedi_handle_events(dev, s);
+ }
+
+ if ((status & DMA_TC_BIT) == 0) {
+ async->events |= COMEDI_CB_ERROR;
+ comedi_handle_events(dev, s);
+ return IRQ_HANDLED;
+ }
+
+ /*
+ * residue is the number of bytes left to be done on the dma
+ * transfer. It should always be zero at this point unless
+ * the stop_src is set to external triggering.
+ */
+ residue = comedi_isadma_disable(desc->chan);
+
+ /* figure out how many points to read */
+ max_points = comedi_bytes_to_samples(s, desc->size);
+ num_points = max_points - comedi_bytes_to_samples(s, residue);
+ if (devpriv->count < num_points && cmd->stop_src == TRIG_COUNT)
+ num_points = devpriv->count;
+
+ /* figure out how many points will be stored next time */
+ leftover = 0;
+ if (cmd->stop_src == TRIG_NONE) {
+ leftover = comedi_bytes_to_samples(s, desc->size);
+ } else if (devpriv->count > max_points) {
+ leftover = devpriv->count - max_points;
+ if (leftover > max_points)
+ leftover = max_points;
+ }
+ /* there should only be a residue if collection was stopped by having
+ * the stop_src set to an external trigger, in which case there
+ * will be no more data
+ */
+ if (residue)
+ leftover = 0;
+
+ for (i = 0; i < num_points; i++) {
+ /* write data point to comedi buffer */
+ dpnt = buf[i];
+ /* convert from 2's complement to unsigned coding */
+ dpnt ^= 0x8000;
+ comedi_buf_write_samples(s, &dpnt, 1);
+ if (cmd->stop_src == TRIG_COUNT) {
+ if (--devpriv->count == 0) { /* end of acquisition */
+ async->events |= COMEDI_CB_EOA;
+ break;
+ }
+ }
+ }
+ /* re-enable dma */
+ if (leftover) {
+ desc->size = comedi_samples_to_bytes(s, leftover);
+ comedi_isadma_program(desc);
+ }
+
+ comedi_handle_events(dev, s);
+
+ /* clear interrupt */
+ outw(0x00, dev->iobase + DMA_TC_CLEAR_REG);
+
+ return IRQ_HANDLED;
+}
+
+static int a2150_cancel(struct comedi_device *dev, struct comedi_subdevice *s)
+{
+ struct a2150_private *devpriv = dev->private;
+ struct comedi_isadma *dma = devpriv->dma;
+ struct comedi_isadma_desc *desc = &dma->desc[0];
+
+ /* disable dma on card */
+ devpriv->irq_dma_bits &= ~DMA_INTR_EN_BIT & ~DMA_EN_BIT;
+ outw(devpriv->irq_dma_bits, dev->iobase + IRQ_DMA_CNTRL_REG);
+
+ /* disable computer's dma */
+ comedi_isadma_disable(desc->chan);
+
+ /* clear fifo and reset triggering circuitry */
+ outw(0, dev->iobase + FIFO_RESET_REG);
+
+ return 0;
+}
+
+/*
+ * sets bits in devpriv->clock_bits to nearest approximation of requested
+ * period, adjusts requested period to actual timing.
+ */
+static int a2150_get_timing(struct comedi_device *dev, unsigned int *period,
+ unsigned int flags)
+{
+ const struct a2150_board *thisboard = dev->board_ptr;
+ struct a2150_private *devpriv = dev->private;
+ int lub, glb, temp;
+ int lub_divisor_shift, lub_index, glb_divisor_shift, glb_index;
+ int i, j;
+
+ /* initialize greatest lower and least upper bounds */
+ lub_divisor_shift = 3;
+ lub_index = 0;
+ lub = thisboard->clock[lub_index] * (1 << lub_divisor_shift);
+ glb_divisor_shift = 0;
+ glb_index = thisboard->num_clocks - 1;
+ glb = thisboard->clock[glb_index] * (1 << glb_divisor_shift);
+
+ /* make sure period is in available range */
+ if (*period < glb)
+ *period = glb;
+ if (*period > lub)
+ *period = lub;
+
+ /* we can multiply period by 1, 2, 4, or 8, using (1 << i) */
+ for (i = 0; i < 4; i++) {
+ /* there are a maximum of 4 master clocks */
+ for (j = 0; j < thisboard->num_clocks; j++) {
+ /* temp is the period in nanosec we are evaluating */
+ temp = thisboard->clock[j] * (1 << i);
+ /* if it is the best match yet */
+ if (temp < lub && temp >= *period) {
+ lub_divisor_shift = i;
+ lub_index = j;
+ lub = temp;
+ }
+ if (temp > glb && temp <= *period) {
+ glb_divisor_shift = i;
+ glb_index = j;
+ glb = temp;
+ }
+ }
+ }
+ switch (flags & CMDF_ROUND_MASK) {
+ case CMDF_ROUND_NEAREST:
+ default:
+ /* if least upper bound is better approximation */
+ if (lub - *period < *period - glb)
+ *period = lub;
+ else
+ *period = glb;
+ break;
+ case CMDF_ROUND_UP:
+ *period = lub;
+ break;
+ case CMDF_ROUND_DOWN:
+ *period = glb;
+ break;
+ }
+
+ /* set clock bits for config register appropriately */
+ devpriv->config_bits &= ~CLOCK_MASK;
+ if (*period == lub) {
+ devpriv->config_bits |=
+ CLOCK_SELECT_BITS(lub_index) |
+ CLOCK_DIVISOR_BITS(lub_divisor_shift);
+ } else {
+ devpriv->config_bits |=
+ CLOCK_SELECT_BITS(glb_index) |
+ CLOCK_DIVISOR_BITS(glb_divisor_shift);
+ }
+
+ return 0;
+}
+
+static int a2150_set_chanlist(struct comedi_device *dev,
+ unsigned int start_channel,
+ unsigned int num_channels)
+{
+ struct a2150_private *devpriv = dev->private;
+
+ if (start_channel + num_channels > 4)
+ return -1;
+
+ devpriv->config_bits &= ~CHANNEL_MASK;
+
+ switch (num_channels) {
+ case 1:
+ devpriv->config_bits |= CHANNEL_BITS(0x4 | start_channel);
+ break;
+ case 2:
+ if (start_channel == 0)
+ devpriv->config_bits |= CHANNEL_BITS(0x2);
+ else if (start_channel == 2)
+ devpriv->config_bits |= CHANNEL_BITS(0x3);
+ else
+ return -1;
+ break;
+ case 4:
+ devpriv->config_bits |= CHANNEL_BITS(0x1);
+ break;
+ default:
+ return -1;
+ }
+
+ return 0;
+}
+
+static int a2150_ai_check_chanlist(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_cmd *cmd)
+{
+ unsigned int chan0 = CR_CHAN(cmd->chanlist[0]);
+ unsigned int aref0 = CR_AREF(cmd->chanlist[0]);
+ int i;
+
+ if (cmd->chanlist_len == 2 && (chan0 == 1 || chan0 == 3)) {
+ dev_dbg(dev->class_dev,
+ "length 2 chanlist must be channels 0,1 or channels 2,3\n");
+ return -EINVAL;
+ }
+
+ if (cmd->chanlist_len == 3) {
+ dev_dbg(dev->class_dev,
+ "chanlist must have 1,2 or 4 channels\n");
+ return -EINVAL;
+ }
+
+ for (i = 1; i < cmd->chanlist_len; i++) {
+ unsigned int chan = CR_CHAN(cmd->chanlist[i]);
+ unsigned int aref = CR_AREF(cmd->chanlist[i]);
+
+ if (chan != (chan0 + i)) {
+ dev_dbg(dev->class_dev,
+ "entries in chanlist must be consecutive channels, counting upwards\n");
+ return -EINVAL;
+ }
+
+ if (chan == 2)
+ aref0 = aref;
+ if (aref != aref0) {
+ dev_dbg(dev->class_dev,
+ "channels 0/1 and 2/3 must have the same analog reference\n");
+ return -EINVAL;
+ }
+ }
+
+ return 0;
+}
+
+static int a2150_ai_cmdtest(struct comedi_device *dev,
+ struct comedi_subdevice *s, struct comedi_cmd *cmd)
+{
+ const struct a2150_board *thisboard = dev->board_ptr;
+ int err = 0;
+ unsigned int arg;
+
+ /* Step 1 : check if triggers are trivially valid */
+
+ err |= comedi_check_trigger_src(&cmd->start_src, TRIG_NOW | TRIG_EXT);
+ err |= comedi_check_trigger_src(&cmd->scan_begin_src, TRIG_TIMER);
+ err |= comedi_check_trigger_src(&cmd->convert_src, TRIG_NOW);
+ err |= comedi_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
+ err |= comedi_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE);
+
+ if (err)
+ return 1;
+
+ /* Step 2a : make sure trigger sources are unique */
+
+ err |= comedi_check_trigger_is_unique(cmd->start_src);
+ err |= comedi_check_trigger_is_unique(cmd->stop_src);
+
+ /* Step 2b : and mutually compatible */
+
+ if (err)
+ return 2;
+
+ /* Step 3: check if arguments are trivially valid */
+
+ err |= comedi_check_trigger_arg_is(&cmd->start_arg, 0);
+
+ if (cmd->convert_src == TRIG_TIMER) {
+ err |= comedi_check_trigger_arg_min(&cmd->convert_arg,
+ thisboard->ai_speed);
+ }
+
+ err |= comedi_check_trigger_arg_min(&cmd->chanlist_len, 1);
+ err |= comedi_check_trigger_arg_is(&cmd->scan_end_arg,
+ cmd->chanlist_len);
+
+ if (cmd->stop_src == TRIG_COUNT)
+ err |= comedi_check_trigger_arg_min(&cmd->stop_arg, 1);
+ else /* TRIG_NONE */
+ err |= comedi_check_trigger_arg_is(&cmd->stop_arg, 0);
+
+ if (err)
+ return 3;
+
+ /* step 4: fix up any arguments */
+
+ if (cmd->scan_begin_src == TRIG_TIMER) {
+ arg = cmd->scan_begin_arg;
+ a2150_get_timing(dev, &arg, cmd->flags);
+ err |= comedi_check_trigger_arg_is(&cmd->scan_begin_arg, arg);
+ }
+
+ if (err)
+ return 4;
+
+ /* Step 5: check channel list if it exists */
+ if (cmd->chanlist && cmd->chanlist_len > 0)
+ err |= a2150_ai_check_chanlist(dev, s, cmd);
+
+ if (err)
+ return 5;
+
+ return 0;
+}
+
+static int a2150_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
+{
+ struct a2150_private *devpriv = dev->private;
+ struct comedi_isadma *dma = devpriv->dma;
+ struct comedi_isadma_desc *desc = &dma->desc[0];
+ struct comedi_async *async = s->async;
+ struct comedi_cmd *cmd = &async->cmd;
+ unsigned int old_config_bits = devpriv->config_bits;
+ unsigned int trigger_bits;
+
+ if (cmd->flags & CMDF_PRIORITY) {
+ dev_err(dev->class_dev,
+ "dma incompatible with hard real-time interrupt (CMDF_PRIORITY), aborting\n");
+ return -1;
+ }
+ /* clear fifo and reset triggering circuitry */
+ outw(0, dev->iobase + FIFO_RESET_REG);
+
+ /* setup chanlist */
+ if (a2150_set_chanlist(dev, CR_CHAN(cmd->chanlist[0]),
+ cmd->chanlist_len) < 0)
+ return -1;
+
+ /* setup ac/dc coupling */
+ if (CR_AREF(cmd->chanlist[0]) == AREF_OTHER)
+ devpriv->config_bits |= AC0_BIT;
+ else
+ devpriv->config_bits &= ~AC0_BIT;
+ if (CR_AREF(cmd->chanlist[2]) == AREF_OTHER)
+ devpriv->config_bits |= AC1_BIT;
+ else
+ devpriv->config_bits &= ~AC1_BIT;
+
+ /* setup timing */
+ a2150_get_timing(dev, &cmd->scan_begin_arg, cmd->flags);
+
+ /* send timing, channel, config bits */
+ outw(devpriv->config_bits, dev->iobase + CONFIG_REG);
+
+ /* initialize number of samples remaining */
+ devpriv->count = cmd->stop_arg * cmd->chanlist_len;
+
+ comedi_isadma_disable(desc->chan);
+
+ /* set size of transfer to fill in 1/3 second */
+#define ONE_THIRD_SECOND 333333333
+ desc->size = comedi_bytes_per_sample(s) * cmd->chanlist_len *
+ ONE_THIRD_SECOND / cmd->scan_begin_arg;
+ if (desc->size > desc->maxsize)
+ desc->size = desc->maxsize;
+ if (desc->size < comedi_bytes_per_sample(s))
+ desc->size = comedi_bytes_per_sample(s);
+ desc->size -= desc->size % comedi_bytes_per_sample(s);
+
+ comedi_isadma_program(desc);
+
+ /* clear dma interrupt before enabling it, to try and get rid of that
+ * one spurious interrupt that has been happening */
+ outw(0x00, dev->iobase + DMA_TC_CLEAR_REG);
+
+ /* enable dma on card */
+ devpriv->irq_dma_bits |= DMA_INTR_EN_BIT | DMA_EN_BIT;
+ outw(devpriv->irq_dma_bits, dev->iobase + IRQ_DMA_CNTRL_REG);
+
+ /* may need to wait 72 sampling periods if timing was changed */
+ comedi_8254_load(dev->pacer, 2, 72, I8254_MODE0 | I8254_BINARY);
+
+ /* setup start triggering */
+ trigger_bits = 0;
+ /* decide if we need to wait 72 periods for valid data */
+ if (cmd->start_src == TRIG_NOW &&
+ (old_config_bits & CLOCK_MASK) !=
+ (devpriv->config_bits & CLOCK_MASK)) {
+ /* set trigger source to delay trigger */
+ trigger_bits |= DELAY_TRIGGER_BITS;
+ } else {
+ /* otherwise no delay */
+ trigger_bits |= POST_TRIGGER_BITS;
+ }
+ /* enable external hardware trigger */
+ if (cmd->start_src == TRIG_EXT) {
+ trigger_bits |= HW_TRIG_EN;
+ } else if (cmd->start_src == TRIG_OTHER) {
+ /* XXX add support for level/slope start trigger using TRIG_OTHER */
+ dev_err(dev->class_dev, "you shouldn't see this?\n");
+ }
+ /* send trigger config bits */
+ outw(trigger_bits, dev->iobase + TRIGGER_REG);
+
+ /* start acquisition for soft trigger */
+ if (cmd->start_src == TRIG_NOW)
+ outw(0, dev->iobase + FIFO_START_REG);
+
+ return 0;
+}
+
+static int a2150_ai_eoc(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned long context)
+{
+ unsigned int status;
+
+ status = inw(dev->iobase + STATUS_REG);
+ if (status & FNE_BIT)
+ return 0;
+ return -EBUSY;
+}
+
+static int a2150_ai_rinsn(struct comedi_device *dev, struct comedi_subdevice *s,
+ struct comedi_insn *insn, unsigned int *data)
+{
+ struct a2150_private *devpriv = dev->private;
+ unsigned int n;
+ int ret;
+
+ /* clear fifo and reset triggering circuitry */
+ outw(0, dev->iobase + FIFO_RESET_REG);
+
+ /* setup chanlist */
+ if (a2150_set_chanlist(dev, CR_CHAN(insn->chanspec), 1) < 0)
+ return -1;
+
+ /* set dc coupling */
+ devpriv->config_bits &= ~AC0_BIT;
+ devpriv->config_bits &= ~AC1_BIT;
+
+ /* send timing, channel, config bits */
+ outw(devpriv->config_bits, dev->iobase + CONFIG_REG);
+
+ /* disable dma on card */
+ devpriv->irq_dma_bits &= ~DMA_INTR_EN_BIT & ~DMA_EN_BIT;
+ outw(devpriv->irq_dma_bits, dev->iobase + IRQ_DMA_CNTRL_REG);
+
+ /* setup start triggering */
+ outw(0, dev->iobase + TRIGGER_REG);
+
+ /* start acquisition for soft trigger */
+ outw(0, dev->iobase + FIFO_START_REG);
+
+ /*
+ * there is a 35.6 sample delay for data to get through the
+ * antialias filter
+ */
+ for (n = 0; n < 36; n++) {
+ ret = comedi_timeout(dev, s, insn, a2150_ai_eoc, 0);
+ if (ret)
+ return ret;
+
+ inw(dev->iobase + FIFO_DATA_REG);
+ }
+
+ /* read data */
+ for (n = 0; n < insn->n; n++) {
+ ret = comedi_timeout(dev, s, insn, a2150_ai_eoc, 0);
+ if (ret)
+ return ret;
+
+ data[n] = inw(dev->iobase + FIFO_DATA_REG);
+ data[n] ^= 0x8000;
+ }
+
+ /* clear fifo and reset triggering circuitry */
+ outw(0, dev->iobase + FIFO_RESET_REG);
+
+ return n;
+}
+
+static void a2150_alloc_irq_and_dma(struct comedi_device *dev,
+ struct comedi_devconfig *it)
+{
+ struct a2150_private *devpriv = dev->private;
+ unsigned int irq_num = it->options[1];
+ unsigned int dma_chan = it->options[2];
+
+ /*
+ * Only IRQs 15, 14, 12-9, and 7-3 are valid.
+ * Only DMA channels 7-5 and 3-0 are valid.
+ */
+ if (irq_num > 15 || dma_chan > 7 ||
+ !((1 << irq_num) & 0xdef8) || !((1 << dma_chan) & 0xef))
+ return;
+
+ if (request_irq(irq_num, a2150_interrupt, 0, dev->board_name, dev))
+ return;
+
+ /* DMA uses 1 buffer */
+ devpriv->dma = comedi_isadma_alloc(dev, 1, dma_chan, dma_chan,
+ A2150_DMA_BUFFER_SIZE,
+ COMEDI_ISADMA_READ);
+ if (!devpriv->dma) {
+ free_irq(irq_num, dev);
+ } else {
+ dev->irq = irq_num;
+ devpriv->irq_dma_bits = IRQ_LVL_BITS(irq_num) |
+ DMA_CHAN_BITS(dma_chan);
+ }
+}
+
+static void a2150_free_dma(struct comedi_device *dev)
+{
+ struct a2150_private *devpriv = dev->private;
+
+ if (devpriv)
+ comedi_isadma_free(devpriv->dma);
+}
+
+/* probes board type, returns offset */
+static int a2150_probe(struct comedi_device *dev)
+{
+ int status = inw(dev->iobase + STATUS_REG);
+
+ return ID_BITS(status);
+}
+
+static int a2150_attach(struct comedi_device *dev, struct comedi_devconfig *it)
+{
+ const struct a2150_board *thisboard;
+ struct a2150_private *devpriv;
+ struct comedi_subdevice *s;
+ static const int timeout = 2000;
+ int i;
+ int ret;
+
+ devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
+ if (!devpriv)
+ return -ENOMEM;
+
+ ret = comedi_request_region(dev, it->options[0], 0x1c);
+ if (ret)
+ return ret;
+
+ i = a2150_probe(dev);
+ if (i >= ARRAY_SIZE(a2150_boards))
+ return -ENODEV;
+
+ dev->board_ptr = a2150_boards + i;
+ thisboard = dev->board_ptr;
+ dev->board_name = thisboard->name;
+
+ /* an IRQ and DMA are required to support async commands */
+ a2150_alloc_irq_and_dma(dev, it);
+
+ dev->pacer = comedi_8254_init(dev->iobase + I8253_BASE_REG,
+ 0, I8254_IO8, 0);
+ if (!dev->pacer)
+ return -ENOMEM;
+
+ ret = comedi_alloc_subdevices(dev, 1);
+ if (ret)
+ return ret;
+
+ /* analog input subdevice */
+ s = &dev->subdevices[0];
+ s->type = COMEDI_SUBD_AI;
+ s->subdev_flags = SDF_READABLE | SDF_GROUND | SDF_OTHER;
+ s->n_chan = 4;
+ s->maxdata = 0xffff;
+ s->range_table = &range_a2150;
+ s->insn_read = a2150_ai_rinsn;
+ if (dev->irq) {
+ dev->read_subdev = s;
+ s->subdev_flags |= SDF_CMD_READ;
+ s->len_chanlist = s->n_chan;
+ s->do_cmd = a2150_ai_cmd;
+ s->do_cmdtest = a2150_ai_cmdtest;
+ s->cancel = a2150_cancel;
+ }
+
+ /* set card's irq and dma levels */
+ outw(devpriv->irq_dma_bits, dev->iobase + IRQ_DMA_CNTRL_REG);
+
+ /* reset and sync adc clock circuitry */
+ outw_p(DPD_BIT | APD_BIT, dev->iobase + CONFIG_REG);
+ outw_p(DPD_BIT, dev->iobase + CONFIG_REG);
+ /* initialize configuration register */
+ devpriv->config_bits = 0;
+ outw(devpriv->config_bits, dev->iobase + CONFIG_REG);
+ /* wait until offset calibration is done, then enable analog inputs */
+ for (i = 0; i < timeout; i++) {
+ if ((DCAL_BIT & inw(dev->iobase + STATUS_REG)) == 0)
+ break;
+ udelay(1000);
+ }
+ if (i == timeout) {
+ dev_err(dev->class_dev,
+ "timed out waiting for offset calibration to complete\n");
+ return -ETIME;
+ }
+ devpriv->config_bits |= ENABLE0_BIT | ENABLE1_BIT;
+ outw(devpriv->config_bits, dev->iobase + CONFIG_REG);
+
+ return 0;
+};
+
+static void a2150_detach(struct comedi_device *dev)
+{
+ if (dev->iobase)
+ outw(APD_BIT | DPD_BIT, dev->iobase + CONFIG_REG);
+ a2150_free_dma(dev);
+ comedi_legacy_detach(dev);
+};
+
+static struct comedi_driver ni_at_a2150_driver = {
+ .driver_name = "ni_at_a2150",
+ .module = THIS_MODULE,
+ .attach = a2150_attach,
+ .detach = a2150_detach,
+};
+module_comedi_driver(ni_at_a2150_driver);
+
+MODULE_AUTHOR("Comedi http://www.comedi.org");
+MODULE_DESCRIPTION("Comedi low-level driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/comedi/drivers/ni_at_ao.c b/drivers/staging/comedi/drivers/ni_at_ao.c
new file mode 100644
index 000000000..f27aa0e82
--- /dev/null
+++ b/drivers/staging/comedi/drivers/ni_at_ao.c
@@ -0,0 +1,383 @@
+/*
+ * ni_at_ao.c
+ * Driver for NI AT-AO-6/10 boards
+ *
+ * COMEDI - Linux Control and Measurement Device Interface
+ * Copyright (C) 2000,2002 David A. Schleef <ds@schleef.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+/*
+ * Driver: ni_at_ao
+ * Description: National Instruments AT-AO-6/10
+ * Devices: [National Instruments] AT-AO-6 (at-ao-6), AT-AO-10 (at-ao-10)
+ * Status: should work
+ * Author: David A. Schleef <ds@schleef.org>
+ * Updated: Sun Dec 26 12:26:28 EST 2004
+ *
+ * Configuration options:
+ * [0] - I/O port base address
+ * [1] - IRQ (unused)
+ * [2] - DMA (unused)
+ * [3] - analog output range, set by jumpers on hardware
+ * 0 for -10 to 10V bipolar
+ * 1 for 0V to 10V unipolar
+ */
+
+#include <linux/module.h>
+
+#include "../comedidev.h"
+
+#include "comedi_8254.h"
+
+/*
+ * Register map
+ *
+ * Register-level programming information can be found in NI
+ * document 320379.pdf.
+ */
+#define ATAO_DIO_REG 0x00
+#define ATAO_CFG2_REG 0x02
+#define ATAO_CFG2_CALLD_NOP (0 << 14)
+#define ATAO_CFG2_CALLD(x) ((((x) >> 3) + 1) << 14)
+#define ATAO_CFG2_FFRTEN (1 << 13)
+#define ATAO_CFG2_DACS(x) (1 << (((x) / 2) + 8))
+#define ATAO_CFG2_LDAC(x) (1 << (((x) / 2) + 3))
+#define ATAO_CFG2_PROMEN (1 << 2)
+#define ATAO_CFG2_SCLK (1 << 1)
+#define ATAO_CFG2_SDATA (1 << 0)
+#define ATAO_CFG3_REG 0x04
+#define ATAO_CFG3_DMAMODE (1 << 6)
+#define ATAO_CFG3_CLKOUT (1 << 5)
+#define ATAO_CFG3_RCLKEN (1 << 4)
+#define ATAO_CFG3_DOUTEN2 (1 << 3)
+#define ATAO_CFG3_DOUTEN1 (1 << 2)
+#define ATAO_CFG3_EN2_5V (1 << 1)
+#define ATAO_CFG3_SCANEN (1 << 0)
+#define ATAO_82C53_BASE 0x06
+#define ATAO_CFG1_REG 0x0a
+#define ATAO_CFG1_EXTINT2EN (1 << 15)
+#define ATAO_CFG1_EXTINT1EN (1 << 14)
+#define ATAO_CFG1_CNTINT2EN (1 << 13)
+#define ATAO_CFG1_CNTINT1EN (1 << 12)
+#define ATAO_CFG1_TCINTEN (1 << 11)
+#define ATAO_CFG1_CNT1SRC (1 << 10)
+#define ATAO_CFG1_CNT2SRC (1 << 9)
+#define ATAO_CFG1_FIFOEN (1 << 8)
+#define ATAO_CFG1_GRP2WR (1 << 7)
+#define ATAO_CFG1_EXTUPDEN (1 << 6)
+#define ATAO_CFG1_DMARQ (1 << 5)
+#define ATAO_CFG1_DMAEN (1 << 4)
+#define ATAO_CFG1_CH(x) (((x) & 0xf) << 0)
+#define ATAO_STATUS_REG 0x0a
+#define ATAO_STATUS_FH (1 << 6)
+#define ATAO_STATUS_FE (1 << 5)
+#define ATAO_STATUS_FF (1 << 4)
+#define ATAO_STATUS_INT2 (1 << 3)
+#define ATAO_STATUS_INT1 (1 << 2)
+#define ATAO_STATUS_TCINT (1 << 1)
+#define ATAO_STATUS_PROMOUT (1 << 0)
+#define ATAO_FIFO_WRITE_REG 0x0c
+#define ATAO_FIFO_CLEAR_REG 0x0c
+#define ATAO_AO_REG(x) (0x0c + ((x) * 2))
+
+/* registers with _2_ are accessed when GRP2WR is set in CFG1 */
+#define ATAO_2_DMATCCLR_REG 0x00
+#define ATAO_2_INT1CLR_REG 0x02
+#define ATAO_2_INT2CLR_REG 0x04
+#define ATAO_2_RTSISHFT_REG 0x06
+#define ATAO_2_RTSISHFT_RSI (1 << 0)
+#define ATAO_2_RTSISTRB_REG 0x07
+
+struct atao_board {
+ const char *name;
+ int n_ao_chans;
+};
+
+static const struct atao_board atao_boards[] = {
+ {
+ .name = "at-ao-6",
+ .n_ao_chans = 6,
+ }, {
+ .name = "at-ao-10",
+ .n_ao_chans = 10,
+ },
+};
+
+struct atao_private {
+ unsigned short cfg1;
+ unsigned short cfg3;
+
+ /* Used for caldac readback */
+ unsigned char caldac[21];
+};
+
+static void atao_select_reg_group(struct comedi_device *dev, int group)
+{
+ struct atao_private *devpriv = dev->private;
+
+ if (group)
+ devpriv->cfg1 |= ATAO_CFG1_GRP2WR;
+ else
+ devpriv->cfg1 &= ~ATAO_CFG1_GRP2WR;
+ outw(devpriv->cfg1, dev->iobase + ATAO_CFG1_REG);
+}
+
+static int atao_ao_insn_write(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ unsigned int chan = CR_CHAN(insn->chanspec);
+ unsigned int val = s->readback[chan];
+ int i;
+
+ if (chan == 0)
+ atao_select_reg_group(dev, 1);
+
+ for (i = 0; i < insn->n; i++) {
+ val = data[i];
+
+ /* the hardware expects two's complement values */
+ outw(comedi_offset_munge(s, val),
+ dev->iobase + ATAO_AO_REG(chan));
+ }
+ s->readback[chan] = val;
+
+ if (chan == 0)
+ atao_select_reg_group(dev, 0);
+
+ return insn->n;
+}
+
+static int atao_dio_insn_bits(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ if (comedi_dio_update_state(s, data))
+ outw(s->state, dev->iobase + ATAO_DIO_REG);
+
+ data[1] = inw(dev->iobase + ATAO_DIO_REG);
+
+ return insn->n;
+}
+
+static int atao_dio_insn_config(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ struct atao_private *devpriv = dev->private;
+ unsigned int chan = CR_CHAN(insn->chanspec);
+ unsigned int mask;
+ int ret;
+
+ if (chan < 4)
+ mask = 0x0f;
+ else
+ mask = 0xf0;
+
+ ret = comedi_dio_insn_config(dev, s, insn, data, mask);
+ if (ret)
+ return ret;
+
+ if (s->io_bits & 0x0f)
+ devpriv->cfg3 |= ATAO_CFG3_DOUTEN1;
+ else
+ devpriv->cfg3 &= ~ATAO_CFG3_DOUTEN1;
+ if (s->io_bits & 0xf0)
+ devpriv->cfg3 |= ATAO_CFG3_DOUTEN2;
+ else
+ devpriv->cfg3 &= ~ATAO_CFG3_DOUTEN2;
+
+ outw(devpriv->cfg3, dev->iobase + ATAO_CFG3_REG);
+
+ return insn->n;
+}
+
+/*
+ * There are three DAC8800 TrimDACs on the board. These are 8-channel,
+ * 8-bit DACs that are used to calibrate the Analog Output channels.
+ * The factory default calibration values are stored in the EEPROM.
+ * The TrimDACs, and EEPROM addresses, are mapped as:
+ *
+ * Channel EEPROM Description
+ * ----------------- ------ -----------------------------------
+ * 0 - DAC0 Chan 0 0x30 AO Channel 0 Offset
+ * 1 - DAC0 Chan 1 0x31 AO Channel 0 Gain
+ * 2 - DAC0 Chan 2 0x32 AO Channel 1 Offset
+ * 3 - DAC0 Chan 3 0x33 AO Channel 1 Gain
+ * 4 - DAC0 Chan 4 0x34 AO Channel 2 Offset
+ * 5 - DAC0 Chan 5 0x35 AO Channel 2 Gain
+ * 6 - DAC0 Chan 6 0x36 AO Channel 3 Offset
+ * 7 - DAC0 Chan 7 0x37 AO Channel 3 Gain
+ * 8 - DAC1 Chan 0 0x38 AO Channel 4 Offset
+ * 9 - DAC1 Chan 1 0x39 AO Channel 4 Gain
+ * 10 - DAC1 Chan 2 0x3a AO Channel 5 Offset
+ * 11 - DAC1 Chan 3 0x3b AO Channel 5 Gain
+ * 12 - DAC1 Chan 4 0x3c 2.5V Offset
+ * 13 - DAC1 Chan 5 0x3d AO Channel 6 Offset (at-ao-10 only)
+ * 14 - DAC1 Chan 6 0x3e AO Channel 6 Gain (at-ao-10 only)
+ * 15 - DAC1 Chan 7 0x3f AO Channel 7 Offset (at-ao-10 only)
+ * 16 - DAC2 Chan 0 0x40 AO Channel 7 Gain (at-ao-10 only)
+ * 17 - DAC2 Chan 1 0x41 AO Channel 8 Offset (at-ao-10 only)
+ * 18 - DAC2 Chan 2 0x42 AO Channel 8 Gain (at-ao-10 only)
+ * 19 - DAC2 Chan 3 0x43 AO Channel 9 Offset (at-ao-10 only)
+ * 20 - DAC2 Chan 4 0x44 AO Channel 9 Gain (at-ao-10 only)
+ * DAC2 Chan 5 0x45 Reserved
+ * DAC2 Chan 6 0x46 Reserved
+ * DAC2 Chan 7 0x47 Reserved
+ */
+static int atao_calib_insn_write(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ unsigned int chan = CR_CHAN(insn->chanspec);
+
+ if (insn->n) {
+ unsigned int val = data[insn->n - 1];
+ unsigned int bitstring = ((chan & 0x7) << 8) | val;
+ unsigned int bits;
+ int bit;
+
+ /* write the channel and last data value to the caldac */
+ /* clock the bitstring to the caldac; MSB -> LSB */
+ for (bit = 1 << 10; bit; bit >>= 1) {
+ bits = (bit & bitstring) ? ATAO_CFG2_SDATA : 0;
+
+ outw(bits, dev->iobase + ATAO_CFG2_REG);
+ outw(bits | ATAO_CFG2_SCLK,
+ dev->iobase + ATAO_CFG2_REG);
+ }
+
+ /* strobe the caldac to load the value */
+ outw(ATAO_CFG2_CALLD(chan), dev->iobase + ATAO_CFG2_REG);
+ outw(ATAO_CFG2_CALLD_NOP, dev->iobase + ATAO_CFG2_REG);
+
+ s->readback[chan] = val;
+ }
+
+ return insn->n;
+}
+
+static void atao_reset(struct comedi_device *dev)
+{
+ struct atao_private *devpriv = dev->private;
+
+ /* This is the reset sequence described in the manual */
+
+ devpriv->cfg1 = 0;
+ outw(devpriv->cfg1, dev->iobase + ATAO_CFG1_REG);
+
+ /* Put outputs of counter 1 and counter 2 in a high state */
+ comedi_8254_set_mode(dev->pacer, 0, I8254_MODE4 | I8254_BINARY);
+ comedi_8254_set_mode(dev->pacer, 1, I8254_MODE4 | I8254_BINARY);
+ comedi_8254_write(dev->pacer, 0, 0x0003);
+
+ outw(ATAO_CFG2_CALLD_NOP, dev->iobase + ATAO_CFG2_REG);
+
+ devpriv->cfg3 = 0;
+ outw(devpriv->cfg3, dev->iobase + ATAO_CFG3_REG);
+
+ inw(dev->iobase + ATAO_FIFO_CLEAR_REG);
+
+ atao_select_reg_group(dev, 1);
+ outw(0, dev->iobase + ATAO_2_INT1CLR_REG);
+ outw(0, dev->iobase + ATAO_2_INT2CLR_REG);
+ outw(0, dev->iobase + ATAO_2_DMATCCLR_REG);
+ atao_select_reg_group(dev, 0);
+}
+
+static int atao_attach(struct comedi_device *dev, struct comedi_devconfig *it)
+{
+ const struct atao_board *board = dev->board_ptr;
+ struct atao_private *devpriv;
+ struct comedi_subdevice *s;
+ int ret;
+
+ ret = comedi_request_region(dev, it->options[0], 0x20);
+ if (ret)
+ return ret;
+
+ devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
+ if (!devpriv)
+ return -ENOMEM;
+
+ dev->pacer = comedi_8254_init(dev->iobase + ATAO_82C53_BASE,
+ 0, I8254_IO8, 0);
+ if (!dev->pacer)
+ return -ENOMEM;
+
+ ret = comedi_alloc_subdevices(dev, 4);
+ if (ret)
+ return ret;
+
+ /* Analog Output subdevice */
+ s = &dev->subdevices[0];
+ s->type = COMEDI_SUBD_AO;
+ s->subdev_flags = SDF_WRITABLE;
+ s->n_chan = board->n_ao_chans;
+ s->maxdata = 0x0fff;
+ s->range_table = it->options[3] ? &range_unipolar10 : &range_bipolar10;
+ s->insn_write = atao_ao_insn_write;
+
+ ret = comedi_alloc_subdev_readback(s);
+ if (ret)
+ return ret;
+
+ /* Digital I/O subdevice */
+ s = &dev->subdevices[1];
+ s->type = COMEDI_SUBD_DIO;
+ s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
+ s->n_chan = 8;
+ s->maxdata = 1;
+ s->range_table = &range_digital;
+ s->insn_bits = atao_dio_insn_bits;
+ s->insn_config = atao_dio_insn_config;
+
+ /* caldac subdevice */
+ s = &dev->subdevices[2];
+ s->type = COMEDI_SUBD_CALIB;
+ s->subdev_flags = SDF_WRITABLE | SDF_INTERNAL;
+ s->n_chan = (board->n_ao_chans * 2) + 1;
+ s->maxdata = 0xff;
+ s->insn_write = atao_calib_insn_write;
+
+ ret = comedi_alloc_subdev_readback(s);
+ if (ret)
+ return ret;
+
+ /* EEPROM subdevice */
+ s = &dev->subdevices[3];
+ s->type = COMEDI_SUBD_UNUSED;
+
+ atao_reset(dev);
+
+ return 0;
+}
+
+static struct comedi_driver ni_at_ao_driver = {
+ .driver_name = "ni_at_ao",
+ .module = THIS_MODULE,
+ .attach = atao_attach,
+ .detach = comedi_legacy_detach,
+ .board_name = &atao_boards[0].name,
+ .offset = sizeof(struct atao_board),
+ .num_names = ARRAY_SIZE(atao_boards),
+};
+module_comedi_driver(ni_at_ao_driver);
+
+MODULE_AUTHOR("Comedi http://www.comedi.org");
+MODULE_DESCRIPTION("Comedi driver for NI AT-AO-6/10 boards");
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/comedi/drivers/ni_atmio.c b/drivers/staging/comedi/drivers/ni_atmio.c
new file mode 100644
index 000000000..1304b0698
--- /dev/null
+++ b/drivers/staging/comedi/drivers/ni_atmio.c
@@ -0,0 +1,378 @@
+/*
+ comedi/drivers/ni_atmio.c
+ Hardware driver for NI AT-MIO E series cards
+
+ COMEDI - Linux Control and Measurement Device Interface
+ Copyright (C) 1997-2001 David A. Schleef <ds@schleef.org>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+*/
+/*
+Driver: ni_atmio
+Description: National Instruments AT-MIO-E series
+Author: ds
+Devices: [National Instruments] AT-MIO-16E-1 (ni_atmio),
+ AT-MIO-16E-2, AT-MIO-16E-10, AT-MIO-16DE-10, AT-MIO-64E-3,
+ AT-MIO-16XE-50, AT-MIO-16XE-10, AT-AI-16XE-10
+Status: works
+Updated: Thu May 1 20:03:02 CDT 2003
+
+The driver has 2.6 kernel isapnp support, and
+will automatically probe for a supported board if the
+I/O base is left unspecified with comedi_config.
+However, many of
+the isapnp id numbers are unknown. If your board is not
+recognized, please send the output of 'cat /proc/isapnp'
+(you may need to modprobe the isa-pnp module for
+/proc/isapnp to exist) so the
+id numbers for your board can be added to the driver.
+
+Otherwise, you can use the isapnptools package to configure
+your board. Use isapnp to
+configure the I/O base and IRQ for the board, and then pass
+the same values as
+parameters in comedi_config. A sample isapnp.conf file is included
+in the etc/ directory of Comedilib.
+
+Comedilib includes a utility to autocalibrate these boards. The
+boards seem to boot into a state where the all calibration DACs
+are at one extreme of their range, thus the default calibration
+is terrible. Calibration at boot is strongly encouraged.
+
+To use the extended digital I/O on some of the boards, enable the
+8255 driver when configuring the Comedi source tree.
+
+External triggering is supported for some events. The channel index
+(scan_begin_arg, etc.) maps to PFI0 - PFI9.
+
+Some of the more esoteric triggering possibilities of these boards
+are not supported.
+*/
+/*
+ The real guts of the driver is in ni_mio_common.c, which is included
+ both here and in ni_pcimio.c
+
+ Interrupt support added by Truxton Fulton <trux@truxton.com>
+
+ References for specifications:
+
+ 340747b.pdf Register Level Programmer Manual (obsolete)
+ 340747c.pdf Register Level Programmer Manual (new)
+ DAQ-STC reference manual
+
+ Other possibly relevant info:
+
+ 320517c.pdf User manual (obsolete)
+ 320517f.pdf User manual (new)
+ 320889a.pdf delete
+ 320906c.pdf maximum signal ratings
+ 321066a.pdf about 16x
+ 321791a.pdf discontinuation of at-mio-16e-10 rev. c
+ 321808a.pdf about at-mio-16e-10 rev P
+ 321837a.pdf discontinuation of at-mio-16de-10 rev d
+ 321838a.pdf about at-mio-16de-10 rev N
+
+ ISSUES:
+
+ need to deal with external reference for DAC, and other DAC
+ properties in board properties
+
+ deal with at-mio-16de-10 revision D to N changes, etc.
+
+*/
+
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include "../comedidev.h"
+
+#include <linux/isapnp.h>
+
+#include "ni_stc.h"
+#include "8255.h"
+
+/*
+ * AT specific setup
+ */
+
+static const struct ni_board_struct ni_boards[] = {
+ {
+ .name = "at-mio-16e-1",
+ .device_id = 44,
+ .isapnp_id = 0x0000, /* XXX unknown */
+ .n_adchan = 16,
+ .ai_maxdata = 0x0fff,
+ .ai_fifo_depth = 8192,
+ .gainlkup = ai_gain_16,
+ .ai_speed = 800,
+ .n_aochan = 2,
+ .ao_maxdata = 0x0fff,
+ .ao_fifo_depth = 2048,
+ .ao_range_table = &range_ni_E_ao_ext,
+ .ao_speed = 1000,
+ .caldac = { mb88341 },
+ }, {
+ .name = "at-mio-16e-2",
+ .device_id = 25,
+ .isapnp_id = 0x1900,
+ .n_adchan = 16,
+ .ai_maxdata = 0x0fff,
+ .ai_fifo_depth = 2048,
+ .gainlkup = ai_gain_16,
+ .ai_speed = 2000,
+ .n_aochan = 2,
+ .ao_maxdata = 0x0fff,
+ .ao_fifo_depth = 2048,
+ .ao_range_table = &range_ni_E_ao_ext,
+ .ao_speed = 1000,
+ .caldac = { mb88341 },
+ }, {
+ .name = "at-mio-16e-10",
+ .device_id = 36,
+ .isapnp_id = 0x2400,
+ .n_adchan = 16,
+ .ai_maxdata = 0x0fff,
+ .ai_fifo_depth = 512,
+ .gainlkup = ai_gain_16,
+ .ai_speed = 10000,
+ .n_aochan = 2,
+ .ao_maxdata = 0x0fff,
+ .ao_range_table = &range_ni_E_ao_ext,
+ .ao_speed = 10000,
+ .caldac = { ad8804_debug },
+ }, {
+ .name = "at-mio-16de-10",
+ .device_id = 37,
+ .isapnp_id = 0x2500,
+ .n_adchan = 16,
+ .ai_maxdata = 0x0fff,
+ .ai_fifo_depth = 512,
+ .gainlkup = ai_gain_16,
+ .ai_speed = 10000,
+ .n_aochan = 2,
+ .ao_maxdata = 0x0fff,
+ .ao_range_table = &range_ni_E_ao_ext,
+ .ao_speed = 10000,
+ .caldac = { ad8804_debug },
+ .has_8255 = 1,
+ }, {
+ .name = "at-mio-64e-3",
+ .device_id = 38,
+ .isapnp_id = 0x2600,
+ .n_adchan = 64,
+ .ai_maxdata = 0x0fff,
+ .ai_fifo_depth = 2048,
+ .gainlkup = ai_gain_16,
+ .ai_speed = 2000,
+ .n_aochan = 2,
+ .ao_maxdata = 0x0fff,
+ .ao_fifo_depth = 2048,
+ .ao_range_table = &range_ni_E_ao_ext,
+ .ao_speed = 1000,
+ .caldac = { ad8804_debug },
+ }, {
+ .name = "at-mio-16xe-50",
+ .device_id = 39,
+ .isapnp_id = 0x2700,
+ .n_adchan = 16,
+ .ai_maxdata = 0xffff,
+ .ai_fifo_depth = 512,
+ .alwaysdither = 1,
+ .gainlkup = ai_gain_8,
+ .ai_speed = 50000,
+ .n_aochan = 2,
+ .ao_maxdata = 0x0fff,
+ .ao_range_table = &range_bipolar10,
+ .ao_speed = 50000,
+ .caldac = { dac8800, dac8043 },
+ }, {
+ .name = "at-mio-16xe-10",
+ .device_id = 50,
+ .isapnp_id = 0x0000, /* XXX unknown */
+ .n_adchan = 16,
+ .ai_maxdata = 0xffff,
+ .ai_fifo_depth = 512,
+ .alwaysdither = 1,
+ .gainlkup = ai_gain_14,
+ .ai_speed = 10000,
+ .n_aochan = 2,
+ .ao_maxdata = 0xffff,
+ .ao_fifo_depth = 2048,
+ .ao_range_table = &range_ni_E_ao_ext,
+ .ao_speed = 1000,
+ .caldac = { dac8800, dac8043, ad8522 },
+ }, {
+ .name = "at-ai-16xe-10",
+ .device_id = 51,
+ .isapnp_id = 0x0000, /* XXX unknown */
+ .n_adchan = 16,
+ .ai_maxdata = 0xffff,
+ .ai_fifo_depth = 512,
+ .alwaysdither = 1, /* unknown */
+ .gainlkup = ai_gain_14,
+ .ai_speed = 10000,
+ .caldac = { dac8800, dac8043, ad8522 },
+ },
+};
+
+static const int ni_irqpin[] = {
+ -1, -1, -1, 0, 1, 2, -1, 3, -1, -1, 4, 5, 6, -1, -1, 7
+};
+
+#include "ni_mio_common.c"
+
+static struct pnp_device_id device_ids[] = {
+ {.id = "NIC1900", .driver_data = 0},
+ {.id = "NIC2400", .driver_data = 0},
+ {.id = "NIC2500", .driver_data = 0},
+ {.id = "NIC2600", .driver_data = 0},
+ {.id = "NIC2700", .driver_data = 0},
+ {.id = ""}
+};
+
+MODULE_DEVICE_TABLE(pnp, device_ids);
+
+static int ni_isapnp_find_board(struct pnp_dev **dev)
+{
+ struct pnp_dev *isapnp_dev = NULL;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(ni_boards); i++) {
+ isapnp_dev = pnp_find_dev(NULL,
+ ISAPNP_VENDOR('N', 'I', 'C'),
+ ISAPNP_FUNCTION(ni_boards[i].
+ isapnp_id), NULL);
+
+ if (!isapnp_dev || !isapnp_dev->card)
+ continue;
+
+ if (pnp_device_attach(isapnp_dev) < 0)
+ continue;
+
+ if (pnp_activate_dev(isapnp_dev) < 0) {
+ pnp_device_detach(isapnp_dev);
+ return -EAGAIN;
+ }
+
+ if (!pnp_port_valid(isapnp_dev, 0) ||
+ !pnp_irq_valid(isapnp_dev, 0)) {
+ pnp_device_detach(isapnp_dev);
+ return -ENOMEM;
+ }
+ break;
+ }
+ if (i == ARRAY_SIZE(ni_boards))
+ return -ENODEV;
+ *dev = isapnp_dev;
+ return 0;
+}
+
+static int ni_getboardtype(struct comedi_device *dev)
+{
+ int device_id = ni_read_eeprom(dev, 511);
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(ni_boards); i++) {
+ if (ni_boards[i].device_id == device_id)
+ return i;
+ }
+ if (device_id == 255)
+ dev_err(dev->class_dev, "can't find board\n");
+ else if (device_id == 0)
+ dev_err(dev->class_dev,
+ "EEPROM read error (?) or device not found\n");
+ else
+ dev_err(dev->class_dev,
+ "unknown device ID %d -- contact author\n", device_id);
+
+ return -1;
+}
+
+static int ni_atmio_attach(struct comedi_device *dev,
+ struct comedi_devconfig *it)
+{
+ const struct ni_board_struct *boardtype;
+ struct pnp_dev *isapnp_dev;
+ int ret;
+ unsigned long iobase;
+ int board;
+ unsigned int irq;
+
+ ret = ni_alloc_private(dev);
+ if (ret)
+ return ret;
+
+ iobase = it->options[0];
+ irq = it->options[1];
+ isapnp_dev = NULL;
+ if (iobase == 0) {
+ ret = ni_isapnp_find_board(&isapnp_dev);
+ if (ret < 0)
+ return ret;
+
+ iobase = pnp_port_start(isapnp_dev, 0);
+ irq = pnp_irq(isapnp_dev, 0);
+ comedi_set_hw_dev(dev, &isapnp_dev->dev);
+ }
+
+ ret = comedi_request_region(dev, iobase, 0x20);
+ if (ret)
+ return ret;
+
+ /* get board type */
+
+ board = ni_getboardtype(dev);
+ if (board < 0)
+ return -EIO;
+
+ dev->board_ptr = ni_boards + board;
+ boardtype = dev->board_ptr;
+ dev->board_name = boardtype->name;
+
+ /* irq stuff */
+
+ if (irq != 0) {
+ if (irq > 15 || ni_irqpin[irq] == -1)
+ return -EINVAL;
+ ret = request_irq(irq, ni_E_interrupt, 0,
+ dev->board_name, dev);
+ if (ret < 0)
+ return -EINVAL;
+ dev->irq = irq;
+ }
+
+ /* generic E series stuff in ni_mio_common.c */
+
+ ret = ni_E_init(dev, ni_irqpin[dev->irq], 0);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static void ni_atmio_detach(struct comedi_device *dev)
+{
+ struct pnp_dev *isapnp_dev;
+
+ mio_common_detach(dev);
+ comedi_legacy_detach(dev);
+
+ isapnp_dev = dev->hw_dev ? to_pnp_dev(dev->hw_dev) : NULL;
+ if (isapnp_dev)
+ pnp_device_detach(isapnp_dev);
+}
+
+static struct comedi_driver ni_atmio_driver = {
+ .driver_name = "ni_atmio",
+ .module = THIS_MODULE,
+ .attach = ni_atmio_attach,
+ .detach = ni_atmio_detach,
+};
+module_comedi_driver(ni_atmio_driver);
diff --git a/drivers/staging/comedi/drivers/ni_atmio16d.c b/drivers/staging/comedi/drivers/ni_atmio16d.c
new file mode 100644
index 000000000..c3eb54622
--- /dev/null
+++ b/drivers/staging/comedi/drivers/ni_atmio16d.c
@@ -0,0 +1,760 @@
+/*
+ comedi/drivers/ni_atmio16d.c
+ Hardware driver for National Instruments AT-MIO16D board
+ Copyright (C) 2000 Chris R. Baugher <baugher@enteract.com>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+ */
+/*
+Driver: ni_atmio16d
+Description: National Instruments AT-MIO-16D
+Author: Chris R. Baugher <baugher@enteract.com>
+Status: unknown
+Devices: [National Instruments] AT-MIO-16 (atmio16), AT-MIO-16D (atmio16d)
+*/
+/*
+ * I must give credit here to Michal Dobes <dobes@tesnet.cz> who
+ * wrote the driver for Advantec's pcl812 boards. I used the interrupt
+ * handling code from his driver as an example for this one.
+ *
+ * Chris Baugher
+ * 5/1/2000
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include "../comedidev.h"
+
+#include "8255.h"
+
+/* Configuration and Status Registers */
+#define COM_REG_1 0x00 /* wo 16 */
+#define STAT_REG 0x00 /* ro 16 */
+#define COM_REG_2 0x02 /* wo 16 */
+/* Event Strobe Registers */
+#define START_CONVERT_REG 0x08 /* wo 16 */
+#define START_DAQ_REG 0x0A /* wo 16 */
+#define AD_CLEAR_REG 0x0C /* wo 16 */
+#define EXT_STROBE_REG 0x0E /* wo 16 */
+/* Analog Output Registers */
+#define DAC0_REG 0x10 /* wo 16 */
+#define DAC1_REG 0x12 /* wo 16 */
+#define INT2CLR_REG 0x14 /* wo 16 */
+/* Analog Input Registers */
+#define MUX_CNTR_REG 0x04 /* wo 16 */
+#define MUX_GAIN_REG 0x06 /* wo 16 */
+#define AD_FIFO_REG 0x16 /* ro 16 */
+#define DMA_TC_INT_CLR_REG 0x16 /* wo 16 */
+/* AM9513A Counter/Timer Registers */
+#define AM9513A_DATA_REG 0x18 /* rw 16 */
+#define AM9513A_COM_REG 0x1A /* wo 16 */
+#define AM9513A_STAT_REG 0x1A /* ro 16 */
+/* MIO-16 Digital I/O Registers */
+#define MIO_16_DIG_IN_REG 0x1C /* ro 16 */
+#define MIO_16_DIG_OUT_REG 0x1C /* wo 16 */
+/* RTSI Switch Registers */
+#define RTSI_SW_SHIFT_REG 0x1E /* wo 8 */
+#define RTSI_SW_STROBE_REG 0x1F /* wo 8 */
+/* DIO-24 Registers */
+#define DIO_24_PORTA_REG 0x00 /* rw 8 */
+#define DIO_24_PORTB_REG 0x01 /* rw 8 */
+#define DIO_24_PORTC_REG 0x02 /* rw 8 */
+#define DIO_24_CNFG_REG 0x03 /* wo 8 */
+
+/* Command Register bits */
+#define COMREG1_2SCADC 0x0001
+#define COMREG1_1632CNT 0x0002
+#define COMREG1_SCANEN 0x0008
+#define COMREG1_DAQEN 0x0010
+#define COMREG1_DMAEN 0x0020
+#define COMREG1_CONVINTEN 0x0080
+#define COMREG2_SCN2 0x0010
+#define COMREG2_INTEN 0x0080
+#define COMREG2_DOUTEN0 0x0100
+#define COMREG2_DOUTEN1 0x0200
+/* Status Register bits */
+#define STAT_AD_OVERRUN 0x0100
+#define STAT_AD_OVERFLOW 0x0200
+#define STAT_AD_DAQPROG 0x0800
+#define STAT_AD_CONVAVAIL 0x2000
+#define STAT_AD_DAQSTOPINT 0x4000
+/* AM9513A Counter/Timer defines */
+#define CLOCK_1_MHZ 0x8B25
+#define CLOCK_100_KHZ 0x8C25
+#define CLOCK_10_KHZ 0x8D25
+#define CLOCK_1_KHZ 0x8E25
+#define CLOCK_100_HZ 0x8F25
+
+struct atmio16_board_t {
+ const char *name;
+ int has_8255;
+};
+
+/* range structs */
+static const struct comedi_lrange range_atmio16d_ai_10_bipolar = {
+ 4, {
+ BIP_RANGE(10),
+ BIP_RANGE(1),
+ BIP_RANGE(0.1),
+ BIP_RANGE(0.02)
+ }
+};
+
+static const struct comedi_lrange range_atmio16d_ai_5_bipolar = {
+ 4, {
+ BIP_RANGE(5),
+ BIP_RANGE(0.5),
+ BIP_RANGE(0.05),
+ BIP_RANGE(0.01)
+ }
+};
+
+static const struct comedi_lrange range_atmio16d_ai_unipolar = {
+ 4, {
+ UNI_RANGE(10),
+ UNI_RANGE(1),
+ UNI_RANGE(0.1),
+ UNI_RANGE(0.02)
+ }
+};
+
+/* private data struct */
+struct atmio16d_private {
+ enum { adc_diff, adc_singleended } adc_mux;
+ enum { adc_bipolar10, adc_bipolar5, adc_unipolar10 } adc_range;
+ enum { adc_2comp, adc_straight } adc_coding;
+ enum { dac_bipolar, dac_unipolar } dac0_range, dac1_range;
+ enum { dac_internal, dac_external } dac0_reference, dac1_reference;
+ enum { dac_2comp, dac_straight } dac0_coding, dac1_coding;
+ const struct comedi_lrange *ao_range_type_list[2];
+ unsigned int com_reg_1_state; /* current state of command register 1 */
+ unsigned int com_reg_2_state; /* current state of command register 2 */
+};
+
+static void reset_counters(struct comedi_device *dev)
+{
+ /* Counter 2 */
+ outw(0xFFC2, dev->iobase + AM9513A_COM_REG);
+ outw(0xFF02, dev->iobase + AM9513A_COM_REG);
+ outw(0x4, dev->iobase + AM9513A_DATA_REG);
+ outw(0xFF0A, dev->iobase + AM9513A_COM_REG);
+ outw(0x3, dev->iobase + AM9513A_DATA_REG);
+ outw(0xFF42, dev->iobase + AM9513A_COM_REG);
+ outw(0xFF42, dev->iobase + AM9513A_COM_REG);
+ /* Counter 3 */
+ outw(0xFFC4, dev->iobase + AM9513A_COM_REG);
+ outw(0xFF03, dev->iobase + AM9513A_COM_REG);
+ outw(0x4, dev->iobase + AM9513A_DATA_REG);
+ outw(0xFF0B, dev->iobase + AM9513A_COM_REG);
+ outw(0x3, dev->iobase + AM9513A_DATA_REG);
+ outw(0xFF44, dev->iobase + AM9513A_COM_REG);
+ outw(0xFF44, dev->iobase + AM9513A_COM_REG);
+ /* Counter 4 */
+ outw(0xFFC8, dev->iobase + AM9513A_COM_REG);
+ outw(0xFF04, dev->iobase + AM9513A_COM_REG);
+ outw(0x4, dev->iobase + AM9513A_DATA_REG);
+ outw(0xFF0C, dev->iobase + AM9513A_COM_REG);
+ outw(0x3, dev->iobase + AM9513A_DATA_REG);
+ outw(0xFF48, dev->iobase + AM9513A_COM_REG);
+ outw(0xFF48, dev->iobase + AM9513A_COM_REG);
+ /* Counter 5 */
+ outw(0xFFD0, dev->iobase + AM9513A_COM_REG);
+ outw(0xFF05, dev->iobase + AM9513A_COM_REG);
+ outw(0x4, dev->iobase + AM9513A_DATA_REG);
+ outw(0xFF0D, dev->iobase + AM9513A_COM_REG);
+ outw(0x3, dev->iobase + AM9513A_DATA_REG);
+ outw(0xFF50, dev->iobase + AM9513A_COM_REG);
+ outw(0xFF50, dev->iobase + AM9513A_COM_REG);
+
+ outw(0, dev->iobase + AD_CLEAR_REG);
+}
+
+static void reset_atmio16d(struct comedi_device *dev)
+{
+ struct atmio16d_private *devpriv = dev->private;
+ int i;
+
+ /* now we need to initialize the board */
+ outw(0, dev->iobase + COM_REG_1);
+ outw(0, dev->iobase + COM_REG_2);
+ outw(0, dev->iobase + MUX_GAIN_REG);
+ /* init AM9513A timer */
+ outw(0xFFFF, dev->iobase + AM9513A_COM_REG);
+ outw(0xFFEF, dev->iobase + AM9513A_COM_REG);
+ outw(0xFF17, dev->iobase + AM9513A_COM_REG);
+ outw(0xF000, dev->iobase + AM9513A_DATA_REG);
+ for (i = 1; i <= 5; ++i) {
+ outw(0xFF00 + i, dev->iobase + AM9513A_COM_REG);
+ outw(0x0004, dev->iobase + AM9513A_DATA_REG);
+ outw(0xFF08 + i, dev->iobase + AM9513A_COM_REG);
+ outw(0x3, dev->iobase + AM9513A_DATA_REG);
+ }
+ outw(0xFF5F, dev->iobase + AM9513A_COM_REG);
+ /* timer init done */
+ outw(0, dev->iobase + AD_CLEAR_REG);
+ outw(0, dev->iobase + INT2CLR_REG);
+ /* select straight binary mode for Analog Input */
+ devpriv->com_reg_1_state |= 1;
+ outw(devpriv->com_reg_1_state, dev->iobase + COM_REG_1);
+ devpriv->adc_coding = adc_straight;
+ /* zero the analog outputs */
+ outw(2048, dev->iobase + DAC0_REG);
+ outw(2048, dev->iobase + DAC1_REG);
+}
+
+static irqreturn_t atmio16d_interrupt(int irq, void *d)
+{
+ struct comedi_device *dev = d;
+ struct comedi_subdevice *s = dev->read_subdev;
+ unsigned short val;
+
+ val = inw(dev->iobase + AD_FIFO_REG);
+ comedi_buf_write_samples(s, &val, 1);
+ comedi_handle_events(dev, s);
+
+ return IRQ_HANDLED;
+}
+
+static int atmio16d_ai_cmdtest(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_cmd *cmd)
+{
+ int err = 0;
+
+ /* Step 1 : check if triggers are trivially valid */
+
+ err |= comedi_check_trigger_src(&cmd->start_src, TRIG_NOW);
+ err |= comedi_check_trigger_src(&cmd->scan_begin_src,
+ TRIG_FOLLOW | TRIG_TIMER);
+ err |= comedi_check_trigger_src(&cmd->convert_src, TRIG_TIMER);
+ err |= comedi_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
+ err |= comedi_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE);
+
+ if (err)
+ return 1;
+
+ /* Step 2a : make sure trigger sources are unique */
+
+ err |= comedi_check_trigger_is_unique(cmd->scan_begin_src);
+ err |= comedi_check_trigger_is_unique(cmd->stop_src);
+
+ /* Step 2b : and mutually compatible */
+
+ if (err)
+ return 2;
+
+ /* Step 3: check if arguments are trivially valid */
+
+ err |= comedi_check_trigger_arg_is(&cmd->start_arg, 0);
+
+ if (cmd->scan_begin_src == TRIG_FOLLOW) {
+ /* internal trigger */
+ err |= comedi_check_trigger_arg_is(&cmd->scan_begin_arg, 0);
+ } else {
+#if 0
+ /* external trigger */
+ /* should be level/edge, hi/lo specification here */
+ err |= comedi_check_trigger_arg_is(&cmd->scan_begin_arg, 0);
+#endif
+ }
+
+ err |= comedi_check_trigger_arg_min(&cmd->convert_arg, 10000);
+#if 0
+ err |= comedi_check_trigger_arg_max(&cmd->convert_arg, SLOWEST_TIMER);
+#endif
+
+ err |= comedi_check_trigger_arg_is(&cmd->scan_end_arg,
+ cmd->chanlist_len);
+
+ if (cmd->stop_src == TRIG_COUNT)
+ err |= comedi_check_trigger_arg_min(&cmd->stop_arg, 1);
+ else /* TRIG_NONE */
+ err |= comedi_check_trigger_arg_is(&cmd->stop_arg, 0);
+
+ if (err)
+ return 3;
+
+ return 0;
+}
+
+static int atmio16d_ai_cmd(struct comedi_device *dev,
+ struct comedi_subdevice *s)
+{
+ struct atmio16d_private *devpriv = dev->private;
+ struct comedi_cmd *cmd = &s->async->cmd;
+ unsigned int timer, base_clock;
+ unsigned int sample_count, tmp, chan, gain;
+ int i;
+
+ /* This is slowly becoming a working command interface. *
+ * It is still uber-experimental */
+
+ reset_counters(dev);
+
+ /* check if scanning multiple channels */
+ if (cmd->chanlist_len < 2) {
+ devpriv->com_reg_1_state &= ~COMREG1_SCANEN;
+ outw(devpriv->com_reg_1_state, dev->iobase + COM_REG_1);
+ } else {
+ devpriv->com_reg_1_state |= COMREG1_SCANEN;
+ devpriv->com_reg_2_state |= COMREG2_SCN2;
+ outw(devpriv->com_reg_1_state, dev->iobase + COM_REG_1);
+ outw(devpriv->com_reg_2_state, dev->iobase + COM_REG_2);
+ }
+
+ /* Setup the Mux-Gain Counter */
+ for (i = 0; i < cmd->chanlist_len; ++i) {
+ chan = CR_CHAN(cmd->chanlist[i]);
+ gain = CR_RANGE(cmd->chanlist[i]);
+ outw(i, dev->iobase + MUX_CNTR_REG);
+ tmp = chan | (gain << 6);
+ if (i == cmd->scan_end_arg - 1)
+ tmp |= 0x0010; /* set LASTONE bit */
+ outw(tmp, dev->iobase + MUX_GAIN_REG);
+ }
+
+ /* Now program the sample interval timer */
+ /* Figure out which clock to use then get an
+ * appropriate timer value */
+ if (cmd->convert_arg < 65536000) {
+ base_clock = CLOCK_1_MHZ;
+ timer = cmd->convert_arg / 1000;
+ } else if (cmd->convert_arg < 655360000) {
+ base_clock = CLOCK_100_KHZ;
+ timer = cmd->convert_arg / 10000;
+ } else /* cmd->convert_arg < 6553600000 */ {
+ base_clock = CLOCK_10_KHZ;
+ timer = cmd->convert_arg / 100000;
+ }
+ outw(0xFF03, dev->iobase + AM9513A_COM_REG);
+ outw(base_clock, dev->iobase + AM9513A_DATA_REG);
+ outw(0xFF0B, dev->iobase + AM9513A_COM_REG);
+ outw(0x2, dev->iobase + AM9513A_DATA_REG);
+ outw(0xFF44, dev->iobase + AM9513A_COM_REG);
+ outw(0xFFF3, dev->iobase + AM9513A_COM_REG);
+ outw(timer, dev->iobase + AM9513A_DATA_REG);
+ outw(0xFF24, dev->iobase + AM9513A_COM_REG);
+
+ /* Now figure out how many samples to get */
+ /* and program the sample counter */
+ sample_count = cmd->stop_arg * cmd->scan_end_arg;
+ outw(0xFF04, dev->iobase + AM9513A_COM_REG);
+ outw(0x1025, dev->iobase + AM9513A_DATA_REG);
+ outw(0xFF0C, dev->iobase + AM9513A_COM_REG);
+ if (sample_count < 65536) {
+ /* use only Counter 4 */
+ outw(sample_count, dev->iobase + AM9513A_DATA_REG);
+ outw(0xFF48, dev->iobase + AM9513A_COM_REG);
+ outw(0xFFF4, dev->iobase + AM9513A_COM_REG);
+ outw(0xFF28, dev->iobase + AM9513A_COM_REG);
+ devpriv->com_reg_1_state &= ~COMREG1_1632CNT;
+ outw(devpriv->com_reg_1_state, dev->iobase + COM_REG_1);
+ } else {
+ /* Counter 4 and 5 are needed */
+
+ tmp = sample_count & 0xFFFF;
+ if (tmp)
+ outw(tmp - 1, dev->iobase + AM9513A_DATA_REG);
+ else
+ outw(0xFFFF, dev->iobase + AM9513A_DATA_REG);
+
+ outw(0xFF48, dev->iobase + AM9513A_COM_REG);
+ outw(0, dev->iobase + AM9513A_DATA_REG);
+ outw(0xFF28, dev->iobase + AM9513A_COM_REG);
+ outw(0xFF05, dev->iobase + AM9513A_COM_REG);
+ outw(0x25, dev->iobase + AM9513A_DATA_REG);
+ outw(0xFF0D, dev->iobase + AM9513A_COM_REG);
+ tmp = sample_count & 0xFFFF;
+ if ((tmp == 0) || (tmp == 1)) {
+ outw((sample_count >> 16) & 0xFFFF,
+ dev->iobase + AM9513A_DATA_REG);
+ } else {
+ outw(((sample_count >> 16) & 0xFFFF) + 1,
+ dev->iobase + AM9513A_DATA_REG);
+ }
+ outw(0xFF70, dev->iobase + AM9513A_COM_REG);
+ devpriv->com_reg_1_state |= COMREG1_1632CNT;
+ outw(devpriv->com_reg_1_state, dev->iobase + COM_REG_1);
+ }
+
+ /* Program the scan interval timer ONLY IF SCANNING IS ENABLED */
+ /* Figure out which clock to use then get an
+ * appropriate timer value */
+ if (cmd->chanlist_len > 1) {
+ if (cmd->scan_begin_arg < 65536000) {
+ base_clock = CLOCK_1_MHZ;
+ timer = cmd->scan_begin_arg / 1000;
+ } else if (cmd->scan_begin_arg < 655360000) {
+ base_clock = CLOCK_100_KHZ;
+ timer = cmd->scan_begin_arg / 10000;
+ } else /* cmd->scan_begin_arg < 6553600000 */ {
+ base_clock = CLOCK_10_KHZ;
+ timer = cmd->scan_begin_arg / 100000;
+ }
+ outw(0xFF02, dev->iobase + AM9513A_COM_REG);
+ outw(base_clock, dev->iobase + AM9513A_DATA_REG);
+ outw(0xFF0A, dev->iobase + AM9513A_COM_REG);
+ outw(0x2, dev->iobase + AM9513A_DATA_REG);
+ outw(0xFF42, dev->iobase + AM9513A_COM_REG);
+ outw(0xFFF2, dev->iobase + AM9513A_COM_REG);
+ outw(timer, dev->iobase + AM9513A_DATA_REG);
+ outw(0xFF22, dev->iobase + AM9513A_COM_REG);
+ }
+
+ /* Clear the A/D FIFO and reset the MUX counter */
+ outw(0, dev->iobase + AD_CLEAR_REG);
+ outw(0, dev->iobase + MUX_CNTR_REG);
+ outw(0, dev->iobase + INT2CLR_REG);
+ /* enable this acquisition operation */
+ devpriv->com_reg_1_state |= COMREG1_DAQEN;
+ outw(devpriv->com_reg_1_state, dev->iobase + COM_REG_1);
+ /* enable interrupts for conversion completion */
+ devpriv->com_reg_1_state |= COMREG1_CONVINTEN;
+ devpriv->com_reg_2_state |= COMREG2_INTEN;
+ outw(devpriv->com_reg_1_state, dev->iobase + COM_REG_1);
+ outw(devpriv->com_reg_2_state, dev->iobase + COM_REG_2);
+ /* apply a trigger. this starts the counters! */
+ outw(0, dev->iobase + START_DAQ_REG);
+
+ return 0;
+}
+
+/* This will cancel a running acquisition operation */
+static int atmio16d_ai_cancel(struct comedi_device *dev,
+ struct comedi_subdevice *s)
+{
+ reset_atmio16d(dev);
+
+ return 0;
+}
+
+static int atmio16d_ai_eoc(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned long context)
+{
+ unsigned int status;
+
+ status = inw(dev->iobase + STAT_REG);
+ if (status & STAT_AD_CONVAVAIL)
+ return 0;
+ if (status & STAT_AD_OVERFLOW) {
+ outw(0, dev->iobase + AD_CLEAR_REG);
+ return -EOVERFLOW;
+ }
+ return -EBUSY;
+}
+
+static int atmio16d_ai_insn_read(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn, unsigned int *data)
+{
+ struct atmio16d_private *devpriv = dev->private;
+ int i;
+ int chan;
+ int gain;
+ int ret;
+
+ chan = CR_CHAN(insn->chanspec);
+ gain = CR_RANGE(insn->chanspec);
+
+ /* reset the Analog input circuitry */
+ /* outw( 0, dev->iobase+AD_CLEAR_REG ); */
+ /* reset the Analog Input MUX Counter to 0 */
+ /* outw( 0, dev->iobase+MUX_CNTR_REG ); */
+
+ /* set the Input MUX gain */
+ outw(chan | (gain << 6), dev->iobase + MUX_GAIN_REG);
+
+ for (i = 0; i < insn->n; i++) {
+ /* start the conversion */
+ outw(0, dev->iobase + START_CONVERT_REG);
+
+ /* wait for it to finish */
+ ret = comedi_timeout(dev, s, insn, atmio16d_ai_eoc, 0);
+ if (ret)
+ return ret;
+
+ /* read the data now */
+ data[i] = inw(dev->iobase + AD_FIFO_REG);
+ /* change to two's complement if need be */
+ if (devpriv->adc_coding == adc_2comp)
+ data[i] ^= 0x800;
+ }
+
+ return i;
+}
+
+static int atmio16d_ao_insn_write(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ struct atmio16d_private *devpriv = dev->private;
+ unsigned int chan = CR_CHAN(insn->chanspec);
+ unsigned int reg = (chan) ? DAC1_REG : DAC0_REG;
+ bool munge = false;
+ int i;
+
+ if (chan == 0 && devpriv->dac0_coding == dac_2comp)
+ munge = true;
+ if (chan == 1 && devpriv->dac1_coding == dac_2comp)
+ munge = true;
+
+ for (i = 0; i < insn->n; i++) {
+ unsigned int val = data[i];
+
+ s->readback[chan] = val;
+
+ if (munge)
+ val ^= 0x800;
+
+ outw(val, dev->iobase + reg);
+ }
+
+ return insn->n;
+}
+
+static int atmio16d_dio_insn_bits(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ if (comedi_dio_update_state(s, data))
+ outw(s->state, dev->iobase + MIO_16_DIG_OUT_REG);
+
+ data[1] = inw(dev->iobase + MIO_16_DIG_IN_REG);
+
+ return insn->n;
+}
+
+static int atmio16d_dio_insn_config(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ struct atmio16d_private *devpriv = dev->private;
+ unsigned int chan = CR_CHAN(insn->chanspec);
+ unsigned int mask;
+ int ret;
+
+ if (chan < 4)
+ mask = 0x0f;
+ else
+ mask = 0xf0;
+
+ ret = comedi_dio_insn_config(dev, s, insn, data, mask);
+ if (ret)
+ return ret;
+
+ devpriv->com_reg_2_state &= ~(COMREG2_DOUTEN0 | COMREG2_DOUTEN1);
+ if (s->io_bits & 0x0f)
+ devpriv->com_reg_2_state |= COMREG2_DOUTEN0;
+ if (s->io_bits & 0xf0)
+ devpriv->com_reg_2_state |= COMREG2_DOUTEN1;
+ outw(devpriv->com_reg_2_state, dev->iobase + COM_REG_2);
+
+ return insn->n;
+}
+
+/*
+ options[0] - I/O port
+ options[1] - MIO irq
+ 0 == no irq
+ N == irq N {3,4,5,6,7,9,10,11,12,14,15}
+ options[2] - DIO irq
+ 0 == no irq
+ N == irq N {3,4,5,6,7,9}
+ options[3] - DMA1 channel
+ 0 == no DMA
+ N == DMA N {5,6,7}
+ options[4] - DMA2 channel
+ 0 == no DMA
+ N == DMA N {5,6,7}
+
+ options[5] - a/d mux
+ 0=differential, 1=single
+ options[6] - a/d range
+ 0=bipolar10, 1=bipolar5, 2=unipolar10
+
+ options[7] - dac0 range
+ 0=bipolar, 1=unipolar
+ options[8] - dac0 reference
+ 0=internal, 1=external
+ options[9] - dac0 coding
+ 0=2's comp, 1=straight binary
+
+ options[10] - dac1 range
+ options[11] - dac1 reference
+ options[12] - dac1 coding
+ */
+
+static int atmio16d_attach(struct comedi_device *dev,
+ struct comedi_devconfig *it)
+{
+ const struct atmio16_board_t *board = dev->board_ptr;
+ struct atmio16d_private *devpriv;
+ struct comedi_subdevice *s;
+ int ret;
+
+ ret = comedi_request_region(dev, it->options[0], 0x20);
+ if (ret)
+ return ret;
+
+ ret = comedi_alloc_subdevices(dev, 4);
+ if (ret)
+ return ret;
+
+ devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
+ if (!devpriv)
+ return -ENOMEM;
+
+ /* reset the atmio16d hardware */
+ reset_atmio16d(dev);
+
+ if (it->options[1]) {
+ ret = request_irq(it->options[1], atmio16d_interrupt, 0,
+ dev->board_name, dev);
+ if (ret == 0)
+ dev->irq = it->options[1];
+ }
+
+ /* set device options */
+ devpriv->adc_mux = it->options[5];
+ devpriv->adc_range = it->options[6];
+
+ devpriv->dac0_range = it->options[7];
+ devpriv->dac0_reference = it->options[8];
+ devpriv->dac0_coding = it->options[9];
+ devpriv->dac1_range = it->options[10];
+ devpriv->dac1_reference = it->options[11];
+ devpriv->dac1_coding = it->options[12];
+
+ /* setup sub-devices */
+ s = &dev->subdevices[0];
+ /* ai subdevice */
+ s->type = COMEDI_SUBD_AI;
+ s->subdev_flags = SDF_READABLE | SDF_GROUND;
+ s->n_chan = (devpriv->adc_mux ? 16 : 8);
+ s->insn_read = atmio16d_ai_insn_read;
+ s->maxdata = 0xfff; /* 4095 decimal */
+ switch (devpriv->adc_range) {
+ case adc_bipolar10:
+ s->range_table = &range_atmio16d_ai_10_bipolar;
+ break;
+ case adc_bipolar5:
+ s->range_table = &range_atmio16d_ai_5_bipolar;
+ break;
+ case adc_unipolar10:
+ s->range_table = &range_atmio16d_ai_unipolar;
+ break;
+ }
+ if (dev->irq) {
+ dev->read_subdev = s;
+ s->subdev_flags |= SDF_CMD_READ;
+ s->len_chanlist = 16;
+ s->do_cmdtest = atmio16d_ai_cmdtest;
+ s->do_cmd = atmio16d_ai_cmd;
+ s->cancel = atmio16d_ai_cancel;
+ }
+
+ /* ao subdevice */
+ s = &dev->subdevices[1];
+ s->type = COMEDI_SUBD_AO;
+ s->subdev_flags = SDF_WRITABLE;
+ s->n_chan = 2;
+ s->maxdata = 0xfff; /* 4095 decimal */
+ s->range_table_list = devpriv->ao_range_type_list;
+ switch (devpriv->dac0_range) {
+ case dac_bipolar:
+ devpriv->ao_range_type_list[0] = &range_bipolar10;
+ break;
+ case dac_unipolar:
+ devpriv->ao_range_type_list[0] = &range_unipolar10;
+ break;
+ }
+ switch (devpriv->dac1_range) {
+ case dac_bipolar:
+ devpriv->ao_range_type_list[1] = &range_bipolar10;
+ break;
+ case dac_unipolar:
+ devpriv->ao_range_type_list[1] = &range_unipolar10;
+ break;
+ }
+ s->insn_write = atmio16d_ao_insn_write;
+
+ ret = comedi_alloc_subdev_readback(s);
+ if (ret)
+ return ret;
+
+ /* Digital I/O */
+ s = &dev->subdevices[2];
+ s->type = COMEDI_SUBD_DIO;
+ s->subdev_flags = SDF_WRITABLE | SDF_READABLE;
+ s->n_chan = 8;
+ s->insn_bits = atmio16d_dio_insn_bits;
+ s->insn_config = atmio16d_dio_insn_config;
+ s->maxdata = 1;
+ s->range_table = &range_digital;
+
+ /* 8255 subdevice */
+ s = &dev->subdevices[3];
+ if (board->has_8255) {
+ ret = subdev_8255_init(dev, s, NULL, 0x00);
+ if (ret)
+ return ret;
+ } else {
+ s->type = COMEDI_SUBD_UNUSED;
+ }
+
+/* don't yet know how to deal with counter/timers */
+#if 0
+ s = &dev->subdevices[4];
+ /* do */
+ s->type = COMEDI_SUBD_TIMER;
+ s->n_chan = 0;
+ s->maxdata = 0
+#endif
+
+ return 0;
+}
+
+static void atmio16d_detach(struct comedi_device *dev)
+{
+ reset_atmio16d(dev);
+ comedi_legacy_detach(dev);
+}
+
+static const struct atmio16_board_t atmio16_boards[] = {
+ {
+ .name = "atmio16",
+ .has_8255 = 0,
+ }, {
+ .name = "atmio16d",
+ .has_8255 = 1,
+ },
+};
+
+static struct comedi_driver atmio16d_driver = {
+ .driver_name = "atmio16",
+ .module = THIS_MODULE,
+ .attach = atmio16d_attach,
+ .detach = atmio16d_detach,
+ .board_name = &atmio16_boards[0].name,
+ .num_names = ARRAY_SIZE(atmio16_boards),
+ .offset = sizeof(struct atmio16_board_t),
+};
+module_comedi_driver(atmio16d_driver);
+
+MODULE_AUTHOR("Comedi http://www.comedi.org");
+MODULE_DESCRIPTION("Comedi low-level driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/comedi/drivers/ni_daq_700.c b/drivers/staging/comedi/drivers/ni_daq_700.c
new file mode 100644
index 000000000..8f6396edd
--- /dev/null
+++ b/drivers/staging/comedi/drivers/ni_daq_700.c
@@ -0,0 +1,289 @@
+/*
+ * comedi/drivers/ni_daq_700.c
+ * Driver for DAQCard-700 DIO/AI
+ * copied from 8255
+ *
+ * COMEDI - Linux Control and Measurement Device Interface
+ * Copyright (C) 1998 David A. Schleef <ds@schleef.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+/*
+ * Driver: ni_daq_700
+ * Description: National Instruments PCMCIA DAQCard-700
+ * Author: Fred Brooks <nsaspook@nsaspook.com>,
+ * based on ni_daq_dio24 by Daniel Vecino Castel <dvecino@able.es>
+ * Devices: [National Instruments] PCMCIA DAQ-Card-700 (ni_daq_700)
+ * Status: works
+ * Updated: Wed, 21 May 2014 12:07:20 +0000
+ *
+ * The daqcard-700 appears in Comedi as a digital I/O subdevice (0) with
+ * 16 channels and a analog input subdevice (1) with 16 single-ended channels
+ * or 8 differential channels, and three input ranges.
+ *
+ * Digital: The channel 0 corresponds to the daqcard-700's output
+ * port, bit 0; channel 8 corresponds to the input port, bit 0.
+ *
+ * Digital direction configuration: channels 0-7 output, 8-15 input.
+ *
+ * Analog: The input range is 0 to 4095 with a default of -10 to +10 volts.
+ * Valid ranges:
+ * 0 for -10 to 10V bipolar
+ * 1 for -5 to 5V bipolar
+ * 2 for -2.5 to 2.5V bipolar
+ *
+ * IRQ is assigned but not used.
+ *
+ * Manuals: Register level: http://www.ni.com/pdf/manuals/340698.pdf
+ * User Manual: http://www.ni.com/pdf/manuals/320676d.pdf
+ */
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+
+#include "../comedi_pcmcia.h"
+
+/* daqcard700 registers */
+#define DIO_W 0x04 /* WO 8bit */
+#define DIO_R 0x05 /* RO 8bit */
+#define CMD_R1 0x00 /* WO 8bit */
+#define CMD_R2 0x07 /* RW 8bit */
+#define CMD_R3 0x05 /* W0 8bit */
+#define STA_R1 0x00 /* RO 8bit */
+#define STA_R2 0x01 /* RO 8bit */
+#define ADFIFO_R 0x02 /* RO 16bit */
+#define ADCLEAR_R 0x01 /* WO 8bit */
+#define CDA_R0 0x08 /* RW 8bit */
+#define CDA_R1 0x09 /* RW 8bit */
+#define CDA_R2 0x0A /* RW 8bit */
+#define CMO_R 0x0B /* RO 8bit */
+#define TIC_R 0x06 /* WO 8bit */
+/* daqcard700 modes */
+#define CMD_R3_DIFF 0x04 /* diff mode */
+
+static const struct comedi_lrange range_daq700_ai = {
+ 3,
+ {
+ BIP_RANGE(10),
+ BIP_RANGE(5),
+ BIP_RANGE(2.5)
+ }
+};
+
+static int daq700_dio_insn_bits(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ unsigned int mask;
+ unsigned int val;
+
+ mask = comedi_dio_update_state(s, data);
+ if (mask) {
+ if (mask & 0xff)
+ outb(s->state & 0xff, dev->iobase + DIO_W);
+ }
+
+ val = s->state & 0xff;
+ val |= inb(dev->iobase + DIO_R) << 8;
+
+ data[1] = val;
+
+ return insn->n;
+}
+
+static int daq700_dio_insn_config(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ int ret;
+
+ ret = comedi_dio_insn_config(dev, s, insn, data, 0);
+ if (ret)
+ return ret;
+
+ /* The DIO channels are not configurable, fix the io_bits */
+ s->io_bits = 0x00ff;
+
+ return insn->n;
+}
+
+static int daq700_ai_eoc(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned long context)
+{
+ unsigned int status;
+
+ status = inb(dev->iobase + STA_R2);
+ if ((status & 0x03))
+ return -EOVERFLOW;
+ status = inb(dev->iobase + STA_R1);
+ if ((status & 0x02))
+ return -ENODATA;
+ if ((status & 0x11) == 0x01)
+ return 0;
+ return -EBUSY;
+}
+
+static int daq700_ai_rinsn(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn, unsigned int *data)
+{
+ int n;
+ int d;
+ int ret;
+ unsigned int chan = CR_CHAN(insn->chanspec);
+ unsigned int aref = CR_AREF(insn->chanspec);
+ unsigned int range = CR_RANGE(insn->chanspec);
+ unsigned int r3_bits = 0;
+
+ /* set channel input modes */
+ if (aref == AREF_DIFF)
+ r3_bits |= CMD_R3_DIFF;
+ /* write channel mode/range */
+ if (range >= 1)
+ range++; /* convert range to hardware value */
+ outb(r3_bits | (range & 0x03), dev->iobase + CMD_R3);
+
+ /* write channel to multiplexer */
+ /* set mask scan bit high to disable scanning */
+ outb(chan | 0x80, dev->iobase + CMD_R1);
+ /* mux needs 2us to really settle [Fred Brooks]. */
+ udelay(2);
+
+ /* convert n samples */
+ for (n = 0; n < insn->n; n++) {
+ /* trigger conversion with out0 L to H */
+ outb(0x00, dev->iobase + CMD_R2); /* enable ADC conversions */
+ outb(0x30, dev->iobase + CMO_R); /* mode 0 out0 L, from H */
+ outb(0x00, dev->iobase + ADCLEAR_R); /* clear the ADC FIFO */
+ /* read 16bit junk from FIFO to clear */
+ inw(dev->iobase + ADFIFO_R);
+ /* mode 1 out0 H, L to H, start conversion */
+ outb(0x32, dev->iobase + CMO_R);
+
+ /* wait for conversion to end */
+ ret = comedi_timeout(dev, s, insn, daq700_ai_eoc, 0);
+ if (ret)
+ return ret;
+
+ /* read data */
+ d = inw(dev->iobase + ADFIFO_R);
+ /* mangle the data as necessary */
+ /* Bipolar Offset Binary: 0 to 4095 for -10 to +10 */
+ d &= 0x0fff;
+ d ^= 0x0800;
+ data[n] = d;
+ }
+ return n;
+}
+
+/*
+ * Data acquisition is enabled.
+ * The counter 0 output is high.
+ * The I/O connector pin CLK1 drives counter 1 source.
+ * Multiple-channel scanning is disabled.
+ * All interrupts are disabled.
+ * The analog input range is set to +-10 V
+ * The analog input mode is single-ended.
+ * The analog input circuitry is initialized to channel 0.
+ * The A/D FIFO is cleared.
+ */
+static void daq700_ai_config(struct comedi_device *dev,
+ struct comedi_subdevice *s)
+{
+ unsigned long iobase = dev->iobase;
+
+ outb(0x80, iobase + CMD_R1); /* disable scanning, ADC to chan 0 */
+ outb(0x00, iobase + CMD_R2); /* clear all bits */
+ outb(0x00, iobase + CMD_R3); /* set +-10 range */
+ outb(0x32, iobase + CMO_R); /* config counter mode1, out0 to H */
+ outb(0x00, iobase + TIC_R); /* clear counter interrupt */
+ outb(0x00, iobase + ADCLEAR_R); /* clear the ADC FIFO */
+ inw(iobase + ADFIFO_R); /* read 16bit junk from FIFO to clear */
+}
+
+static int daq700_auto_attach(struct comedi_device *dev,
+ unsigned long context)
+{
+ struct pcmcia_device *link = comedi_to_pcmcia_dev(dev);
+ struct comedi_subdevice *s;
+ int ret;
+
+ link->config_flags |= CONF_AUTO_SET_IO;
+ ret = comedi_pcmcia_enable(dev, NULL);
+ if (ret)
+ return ret;
+ dev->iobase = link->resource[0]->start;
+
+ ret = comedi_alloc_subdevices(dev, 2);
+ if (ret)
+ return ret;
+
+ /* DAQCard-700 dio */
+ s = &dev->subdevices[0];
+ s->type = COMEDI_SUBD_DIO;
+ s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
+ s->n_chan = 16;
+ s->range_table = &range_digital;
+ s->maxdata = 1;
+ s->insn_bits = daq700_dio_insn_bits;
+ s->insn_config = daq700_dio_insn_config;
+ s->io_bits = 0x00ff;
+
+ /* DAQCard-700 ai */
+ s = &dev->subdevices[1];
+ s->type = COMEDI_SUBD_AI;
+ s->subdev_flags = SDF_READABLE | SDF_GROUND | SDF_DIFF;
+ s->n_chan = 16;
+ s->maxdata = (1 << 12) - 1;
+ s->range_table = &range_daq700_ai;
+ s->insn_read = daq700_ai_rinsn;
+ daq700_ai_config(dev, s);
+
+ return 0;
+}
+
+static struct comedi_driver daq700_driver = {
+ .driver_name = "ni_daq_700",
+ .module = THIS_MODULE,
+ .auto_attach = daq700_auto_attach,
+ .detach = comedi_pcmcia_disable,
+};
+
+static int daq700_cs_attach(struct pcmcia_device *link)
+{
+ return comedi_pcmcia_auto_config(link, &daq700_driver);
+}
+
+static const struct pcmcia_device_id daq700_cs_ids[] = {
+ PCMCIA_DEVICE_MANF_CARD(0x010b, 0x4743),
+ PCMCIA_DEVICE_NULL
+};
+MODULE_DEVICE_TABLE(pcmcia, daq700_cs_ids);
+
+static struct pcmcia_driver daq700_cs_driver = {
+ .name = "ni_daq_700",
+ .owner = THIS_MODULE,
+ .id_table = daq700_cs_ids,
+ .probe = daq700_cs_attach,
+ .remove = comedi_pcmcia_auto_unconfig,
+};
+module_comedi_pcmcia_driver(daq700_driver, daq700_cs_driver);
+
+MODULE_AUTHOR("Fred Brooks <nsaspook@nsaspook.com>");
+MODULE_DESCRIPTION(
+ "Comedi driver for National Instruments PCMCIA DAQCard-700 DIO/AI");
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/comedi/drivers/ni_daq_dio24.c b/drivers/staging/comedi/drivers/ni_daq_dio24.c
new file mode 100644
index 000000000..a208cb348
--- /dev/null
+++ b/drivers/staging/comedi/drivers/ni_daq_dio24.c
@@ -0,0 +1,95 @@
+/*
+ comedi/drivers/ni_daq_dio24.c
+ Driver for National Instruments PCMCIA DAQ-Card DIO-24
+ Copyright (C) 2002 Daniel Vecino Castel <dvecino@able.es>
+
+ PCMCIA crap at end of file is adapted from dummy_cs.c 1.31
+ 2001/08/24 12:13:13 from the pcmcia package.
+ The initial developer of the pcmcia dummy_cs.c code is David A. Hinds
+ <dahinds@users.sourceforge.net>. Portions created by David A. Hinds
+ are Copyright (C) 1999 David A. Hinds. All Rights Reserved.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+*/
+/*
+Driver: ni_daq_dio24
+Description: National Instruments PCMCIA DAQ-Card DIO-24
+Author: Daniel Vecino Castel <dvecino@able.es>
+Devices: [National Instruments] PCMCIA DAQ-Card DIO-24 (ni_daq_dio24)
+Status: ?
+Updated: Thu, 07 Nov 2002 21:53:06 -0800
+
+This is just a wrapper around the 8255.o driver to properly handle
+the PCMCIA interface.
+*/
+
+#include <linux/module.h>
+#include "../comedi_pcmcia.h"
+
+#include "8255.h"
+
+static int dio24_auto_attach(struct comedi_device *dev,
+ unsigned long context)
+{
+ struct pcmcia_device *link = comedi_to_pcmcia_dev(dev);
+ struct comedi_subdevice *s;
+ int ret;
+
+ link->config_flags |= CONF_AUTO_SET_IO;
+ ret = comedi_pcmcia_enable(dev, NULL);
+ if (ret)
+ return ret;
+ dev->iobase = link->resource[0]->start;
+
+ ret = comedi_alloc_subdevices(dev, 1);
+ if (ret)
+ return ret;
+
+ /* 8255 dio */
+ s = &dev->subdevices[0];
+ ret = subdev_8255_init(dev, s, NULL, 0x00);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static struct comedi_driver driver_dio24 = {
+ .driver_name = "ni_daq_dio24",
+ .module = THIS_MODULE,
+ .auto_attach = dio24_auto_attach,
+ .detach = comedi_pcmcia_disable,
+};
+
+static int dio24_cs_attach(struct pcmcia_device *link)
+{
+ return comedi_pcmcia_auto_config(link, &driver_dio24);
+}
+
+static const struct pcmcia_device_id dio24_cs_ids[] = {
+ PCMCIA_DEVICE_MANF_CARD(0x010b, 0x475c), /* daqcard-dio24 */
+ PCMCIA_DEVICE_NULL
+};
+MODULE_DEVICE_TABLE(pcmcia, dio24_cs_ids);
+
+static struct pcmcia_driver dio24_cs_driver = {
+ .name = "ni_daq_dio24",
+ .owner = THIS_MODULE,
+ .id_table = dio24_cs_ids,
+ .probe = dio24_cs_attach,
+ .remove = comedi_pcmcia_auto_unconfig,
+};
+module_comedi_pcmcia_driver(driver_dio24, dio24_cs_driver);
+
+MODULE_AUTHOR("Daniel Vecino Castel <dvecino@able.es>");
+MODULE_DESCRIPTION(
+ "Comedi driver for National Instruments PCMCIA DAQ-Card DIO-24");
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/comedi/drivers/ni_labpc.c b/drivers/staging/comedi/drivers/ni_labpc.c
new file mode 100644
index 000000000..51e5e942b
--- /dev/null
+++ b/drivers/staging/comedi/drivers/ni_labpc.c
@@ -0,0 +1,125 @@
+/*
+ * comedi/drivers/ni_labpc.c
+ * Driver for National Instruments Lab-PC series boards and compatibles
+ * Copyright (C) 2001-2003 Frank Mori Hess <fmhess@users.sourceforge.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+/*
+ * Driver: ni_labpc
+ * Description: National Instruments Lab-PC (& compatibles)
+ * Devices: [National Instruments] Lab-PC-1200 (lab-pc-1200),
+ * Lab-PC-1200AI (lab-pc-1200ai), Lab-PC+ (lab-pc+)
+ * Author: Frank Mori Hess <fmhess@users.sourceforge.net>
+ * Status: works
+ *
+ * Configuration options - ISA boards:
+ * [0] - I/O port base address
+ * [1] - IRQ (optional, required for timed or externally triggered
+ * conversions)
+ * [2] - DMA channel (optional)
+ *
+ * Tested with lab-pc-1200. For the older Lab-PC+, not all input
+ * ranges and analog references will work, the available ranges/arefs
+ * will depend on how you have configured the jumpers on your board
+ * (see your owner's manual).
+ *
+ * Kernel-level ISA plug-and-play support for the lab-pc-1200 boards
+ * has not yet been added to the driver, mainly due to the fact that
+ * I don't know the device id numbers. If you have one of these boards,
+ * please file a bug report at http://comedi.org/ so I can get the
+ * necessary information from you.
+ *
+ * The 1200 series boards have onboard calibration dacs for correcting
+ * analog input/output offsets and gains. The proper settings for these
+ * caldacs are stored on the board's eeprom. To read the caldac values
+ * from the eeprom and store them into a file that can be then be used
+ * by comedilib, use the comedi_calibrate program.
+ *
+ * The Lab-pc+ has quirky chanlist requirements when scanning multiple
+ * channels. Multiple channel scan sequence must start at highest channel,
+ * then decrement down to channel 0. The rest of the cards can scan down
+ * like lab-pc+ or scan up from channel zero. Chanlists consisting of all
+ * one channel are also legal, and allow you to pace conversions in bursts.
+ *
+ * NI manuals:
+ * 341309a (labpc-1200 register manual)
+ * 320502b (lab-pc+)
+ */
+
+#include <linux/module.h>
+
+#include "../comedidev.h"
+
+#include "ni_labpc.h"
+#include "ni_labpc_isadma.h"
+
+static const struct labpc_boardinfo labpc_boards[] = {
+ {
+ .name = "lab-pc-1200",
+ .ai_speed = 10000,
+ .ai_scan_up = 1,
+ .has_ao = 1,
+ .is_labpc1200 = 1,
+ }, {
+ .name = "lab-pc-1200ai",
+ .ai_speed = 10000,
+ .ai_scan_up = 1,
+ .is_labpc1200 = 1,
+ }, {
+ .name = "lab-pc+",
+ .ai_speed = 12000,
+ .has_ao = 1,
+ },
+};
+
+static int labpc_attach(struct comedi_device *dev, struct comedi_devconfig *it)
+{
+ unsigned int irq = it->options[1];
+ unsigned int dma_chan = it->options[2];
+ int ret;
+
+ ret = comedi_request_region(dev, it->options[0], 0x20);
+ if (ret)
+ return ret;
+
+ ret = labpc_common_attach(dev, irq, 0);
+ if (ret)
+ return ret;
+
+ if (dev->irq)
+ labpc_init_dma_chan(dev, dma_chan);
+
+ return 0;
+}
+
+static void labpc_detach(struct comedi_device *dev)
+{
+ labpc_free_dma_chan(dev);
+ labpc_common_detach(dev);
+ comedi_legacy_detach(dev);
+}
+
+static struct comedi_driver labpc_driver = {
+ .driver_name = "ni_labpc",
+ .module = THIS_MODULE,
+ .attach = labpc_attach,
+ .detach = labpc_detach,
+ .num_names = ARRAY_SIZE(labpc_boards),
+ .board_name = &labpc_boards[0].name,
+ .offset = sizeof(struct labpc_boardinfo),
+};
+module_comedi_driver(labpc_driver);
+
+MODULE_AUTHOR("Comedi http://www.comedi.org");
+MODULE_DESCRIPTION("Comedi driver for NI Lab-PC ISA boards");
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/comedi/drivers/ni_labpc.h b/drivers/staging/comedi/drivers/ni_labpc.h
new file mode 100644
index 000000000..83f878adb
--- /dev/null
+++ b/drivers/staging/comedi/drivers/ni_labpc.h
@@ -0,0 +1,69 @@
+/*
+ ni_labpc.h
+
+ Header for ni_labpc.c and ni_labpc_cs.c
+
+ Copyright (C) 2003 Frank Mori Hess <fmhess@users.sourceforge.net>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+*/
+
+#ifndef _NI_LABPC_H
+#define _NI_LABPC_H
+
+#define EEPROM_SIZE 256 /* 256 byte eeprom */
+#define NUM_AO_CHAN 2 /* boards have two analog output channels */
+
+enum transfer_type { fifo_not_empty_transfer, fifo_half_full_transfer,
+ isa_dma_transfer
+};
+
+struct labpc_boardinfo {
+ const char *name;
+ int ai_speed; /* maximum input speed in ns */
+ unsigned ai_scan_up:1; /* can auto scan up in ai channels */
+ unsigned has_ao:1; /* has analog outputs */
+ unsigned is_labpc1200:1; /* has extra regs compared to pc+ */
+};
+
+struct labpc_private {
+ struct comedi_isadma *dma;
+ struct comedi_8254 *counter;
+
+ /* number of data points left to be taken */
+ unsigned long long count;
+ /* software copys of bits written to command registers */
+ unsigned int cmd1;
+ unsigned int cmd2;
+ unsigned int cmd3;
+ unsigned int cmd4;
+ unsigned int cmd5;
+ unsigned int cmd6;
+ /* store last read of board status registers */
+ unsigned int stat1;
+ unsigned int stat2;
+
+ /* we are using dma/fifo-half-full/etc. */
+ enum transfer_type current_transfer;
+ /*
+ * function pointers so we can use inb/outb or readb/writeb as
+ * appropriate
+ */
+ unsigned int (*read_byte)(struct comedi_device *, unsigned long reg);
+ void (*write_byte)(struct comedi_device *,
+ unsigned int byte, unsigned long reg);
+};
+
+int labpc_common_attach(struct comedi_device *dev,
+ unsigned int irq, unsigned long isr_flags);
+void labpc_common_detach(struct comedi_device *dev);
+
+#endif /* _NI_LABPC_H */
diff --git a/drivers/staging/comedi/drivers/ni_labpc_common.c b/drivers/staging/comedi/drivers/ni_labpc_common.c
new file mode 100644
index 000000000..863afb28e
--- /dev/null
+++ b/drivers/staging/comedi/drivers/ni_labpc_common.c
@@ -0,0 +1,1355 @@
+/*
+ * comedi/drivers/ni_labpc_common.c
+ *
+ * Common support code for "ni_labpc", "ni_labpc_pci" and "ni_labpc_cs".
+ *
+ * Copyright (C) 2001-2003 Frank Mori Hess <fmhess@users.sourceforge.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+
+#include "../comedidev.h"
+
+#include "comedi_8254.h"
+#include "8255.h"
+#include "ni_labpc.h"
+#include "ni_labpc_regs.h"
+#include "ni_labpc_isadma.h"
+
+enum scan_mode {
+ MODE_SINGLE_CHAN,
+ MODE_SINGLE_CHAN_INTERVAL,
+ MODE_MULT_CHAN_UP,
+ MODE_MULT_CHAN_DOWN,
+};
+
+static const struct comedi_lrange range_labpc_plus_ai = {
+ 16, {
+ BIP_RANGE(5),
+ BIP_RANGE(4),
+ BIP_RANGE(2.5),
+ BIP_RANGE(1),
+ BIP_RANGE(0.5),
+ BIP_RANGE(0.25),
+ BIP_RANGE(0.1),
+ BIP_RANGE(0.05),
+ UNI_RANGE(10),
+ UNI_RANGE(8),
+ UNI_RANGE(5),
+ UNI_RANGE(2),
+ UNI_RANGE(1),
+ UNI_RANGE(0.5),
+ UNI_RANGE(0.2),
+ UNI_RANGE(0.1)
+ }
+};
+
+static const struct comedi_lrange range_labpc_1200_ai = {
+ 14, {
+ BIP_RANGE(5),
+ BIP_RANGE(2.5),
+ BIP_RANGE(1),
+ BIP_RANGE(0.5),
+ BIP_RANGE(0.25),
+ BIP_RANGE(0.1),
+ BIP_RANGE(0.05),
+ UNI_RANGE(10),
+ UNI_RANGE(5),
+ UNI_RANGE(2),
+ UNI_RANGE(1),
+ UNI_RANGE(0.5),
+ UNI_RANGE(0.2),
+ UNI_RANGE(0.1)
+ }
+};
+
+static const struct comedi_lrange range_labpc_ao = {
+ 2, {
+ BIP_RANGE(5),
+ UNI_RANGE(10)
+ }
+};
+
+/* functions that do inb/outb and readb/writeb so we can use
+ * function pointers to decide which to use */
+static unsigned int labpc_inb(struct comedi_device *dev, unsigned long reg)
+{
+ return inb(dev->iobase + reg);
+}
+
+static void labpc_outb(struct comedi_device *dev,
+ unsigned int byte, unsigned long reg)
+{
+ outb(byte, dev->iobase + reg);
+}
+
+static unsigned int labpc_readb(struct comedi_device *dev, unsigned long reg)
+{
+ return readb(dev->mmio + reg);
+}
+
+static void labpc_writeb(struct comedi_device *dev,
+ unsigned int byte, unsigned long reg)
+{
+ writeb(byte, dev->mmio + reg);
+}
+
+static int labpc_cancel(struct comedi_device *dev, struct comedi_subdevice *s)
+{
+ struct labpc_private *devpriv = dev->private;
+ unsigned long flags;
+
+ spin_lock_irqsave(&dev->spinlock, flags);
+ devpriv->cmd2 &= ~(CMD2_SWTRIG | CMD2_HWTRIG | CMD2_PRETRIG);
+ devpriv->write_byte(dev, devpriv->cmd2, CMD2_REG);
+ spin_unlock_irqrestore(&dev->spinlock, flags);
+
+ devpriv->cmd3 = 0;
+ devpriv->write_byte(dev, devpriv->cmd3, CMD3_REG);
+
+ return 0;
+}
+
+static void labpc_ai_set_chan_and_gain(struct comedi_device *dev,
+ enum scan_mode mode,
+ unsigned int chan,
+ unsigned int range,
+ unsigned int aref)
+{
+ const struct labpc_boardinfo *board = dev->board_ptr;
+ struct labpc_private *devpriv = dev->private;
+
+ if (board->is_labpc1200) {
+ /*
+ * The LabPC-1200 boards do not have a gain
+ * of '0x10'. Skip the range values that would
+ * result in this gain.
+ */
+ range += (range > 0) + (range > 7);
+ }
+
+ /* munge channel bits for differential/scan disabled mode */
+ if ((mode == MODE_SINGLE_CHAN || mode == MODE_SINGLE_CHAN_INTERVAL) &&
+ aref == AREF_DIFF)
+ chan *= 2;
+ devpriv->cmd1 = CMD1_MA(chan);
+ devpriv->cmd1 |= CMD1_GAIN(range);
+
+ devpriv->write_byte(dev, devpriv->cmd1, CMD1_REG);
+}
+
+static void labpc_setup_cmd6_reg(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ enum scan_mode mode,
+ enum transfer_type xfer,
+ unsigned int range,
+ unsigned int aref,
+ bool ena_intr)
+{
+ const struct labpc_boardinfo *board = dev->board_ptr;
+ struct labpc_private *devpriv = dev->private;
+
+ if (!board->is_labpc1200)
+ return;
+
+ /* reference inputs to ground or common? */
+ if (aref != AREF_GROUND)
+ devpriv->cmd6 |= CMD6_NRSE;
+ else
+ devpriv->cmd6 &= ~CMD6_NRSE;
+
+ /* bipolar or unipolar range? */
+ if (comedi_range_is_unipolar(s, range))
+ devpriv->cmd6 |= CMD6_ADCUNI;
+ else
+ devpriv->cmd6 &= ~CMD6_ADCUNI;
+
+ /* interrupt on fifo half full? */
+ if (xfer == fifo_half_full_transfer)
+ devpriv->cmd6 |= CMD6_HFINTEN;
+ else
+ devpriv->cmd6 &= ~CMD6_HFINTEN;
+
+ /* enable interrupt on counter a1 terminal count? */
+ if (ena_intr)
+ devpriv->cmd6 |= CMD6_DQINTEN;
+ else
+ devpriv->cmd6 &= ~CMD6_DQINTEN;
+
+ /* are we scanning up or down through channels? */
+ if (mode == MODE_MULT_CHAN_UP)
+ devpriv->cmd6 |= CMD6_SCANUP;
+ else
+ devpriv->cmd6 &= ~CMD6_SCANUP;
+
+ devpriv->write_byte(dev, devpriv->cmd6, CMD6_REG);
+}
+
+static unsigned int labpc_read_adc_fifo(struct comedi_device *dev)
+{
+ struct labpc_private *devpriv = dev->private;
+ unsigned int lsb = devpriv->read_byte(dev, ADC_FIFO_REG);
+ unsigned int msb = devpriv->read_byte(dev, ADC_FIFO_REG);
+
+ return (msb << 8) | lsb;
+}
+
+static void labpc_clear_adc_fifo(struct comedi_device *dev)
+{
+ struct labpc_private *devpriv = dev->private;
+
+ devpriv->write_byte(dev, 0x1, ADC_FIFO_CLEAR_REG);
+ labpc_read_adc_fifo(dev);
+}
+
+static int labpc_ai_eoc(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned long context)
+{
+ struct labpc_private *devpriv = dev->private;
+
+ devpriv->stat1 = devpriv->read_byte(dev, STAT1_REG);
+ if (devpriv->stat1 & STAT1_DAVAIL)
+ return 0;
+ return -EBUSY;
+}
+
+static int labpc_ai_insn_read(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ struct labpc_private *devpriv = dev->private;
+ unsigned int chan = CR_CHAN(insn->chanspec);
+ unsigned int range = CR_RANGE(insn->chanspec);
+ unsigned int aref = CR_AREF(insn->chanspec);
+ int ret;
+ int i;
+
+ /* disable timed conversions, interrupt generation and dma */
+ labpc_cancel(dev, s);
+
+ labpc_ai_set_chan_and_gain(dev, MODE_SINGLE_CHAN, chan, range, aref);
+
+ labpc_setup_cmd6_reg(dev, s, MODE_SINGLE_CHAN, fifo_not_empty_transfer,
+ range, aref, false);
+
+ /* setup cmd4 register */
+ devpriv->cmd4 = 0;
+ devpriv->cmd4 |= CMD4_ECLKRCV;
+ /* single-ended/differential */
+ if (aref == AREF_DIFF)
+ devpriv->cmd4 |= CMD4_SEDIFF;
+ devpriv->write_byte(dev, devpriv->cmd4, CMD4_REG);
+
+ /* initialize pacer counter to prevent any problems */
+ comedi_8254_set_mode(devpriv->counter, 0, I8254_MODE2 | I8254_BINARY);
+
+ labpc_clear_adc_fifo(dev);
+
+ for (i = 0; i < insn->n; i++) {
+ /* trigger conversion */
+ devpriv->write_byte(dev, 0x1, ADC_START_CONVERT_REG);
+
+ ret = comedi_timeout(dev, s, insn, labpc_ai_eoc, 0);
+ if (ret)
+ return ret;
+
+ data[i] = labpc_read_adc_fifo(dev);
+ }
+
+ return insn->n;
+}
+
+static bool labpc_use_continuous_mode(const struct comedi_cmd *cmd,
+ enum scan_mode mode)
+{
+ if (mode == MODE_SINGLE_CHAN || cmd->scan_begin_src == TRIG_FOLLOW)
+ return true;
+
+ return false;
+}
+
+static unsigned int labpc_ai_convert_period(const struct comedi_cmd *cmd,
+ enum scan_mode mode)
+{
+ if (cmd->convert_src != TRIG_TIMER)
+ return 0;
+
+ if (mode == MODE_SINGLE_CHAN && cmd->scan_begin_src == TRIG_TIMER)
+ return cmd->scan_begin_arg;
+
+ return cmd->convert_arg;
+}
+
+static void labpc_set_ai_convert_period(struct comedi_cmd *cmd,
+ enum scan_mode mode, unsigned int ns)
+{
+ if (cmd->convert_src != TRIG_TIMER)
+ return;
+
+ if (mode == MODE_SINGLE_CHAN &&
+ cmd->scan_begin_src == TRIG_TIMER) {
+ cmd->scan_begin_arg = ns;
+ if (cmd->convert_arg > cmd->scan_begin_arg)
+ cmd->convert_arg = cmd->scan_begin_arg;
+ } else {
+ cmd->convert_arg = ns;
+ }
+}
+
+static unsigned int labpc_ai_scan_period(const struct comedi_cmd *cmd,
+ enum scan_mode mode)
+{
+ if (cmd->scan_begin_src != TRIG_TIMER)
+ return 0;
+
+ if (mode == MODE_SINGLE_CHAN && cmd->convert_src == TRIG_TIMER)
+ return 0;
+
+ return cmd->scan_begin_arg;
+}
+
+static void labpc_set_ai_scan_period(struct comedi_cmd *cmd,
+ enum scan_mode mode, unsigned int ns)
+{
+ if (cmd->scan_begin_src != TRIG_TIMER)
+ return;
+
+ if (mode == MODE_SINGLE_CHAN && cmd->convert_src == TRIG_TIMER)
+ return;
+
+ cmd->scan_begin_arg = ns;
+}
+
+/* figures out what counter values to use based on command */
+static void labpc_adc_timing(struct comedi_device *dev, struct comedi_cmd *cmd,
+ enum scan_mode mode)
+{
+ struct comedi_8254 *pacer = dev->pacer;
+ unsigned int convert_period = labpc_ai_convert_period(cmd, mode);
+ unsigned int scan_period = labpc_ai_scan_period(cmd, mode);
+ unsigned int base_period;
+
+ /*
+ * If both convert and scan triggers are TRIG_TIMER, then they
+ * both rely on counter b0. If only one TRIG_TIMER is used, we
+ * can use the generic cascaded timing functions.
+ */
+ if (convert_period && scan_period) {
+ /*
+ * pick the lowest divisor value we can (for maximum input
+ * clock speed on convert and scan counters)
+ */
+ pacer->next_div1 = (scan_period - 1) /
+ (pacer->osc_base * I8254_MAX_COUNT) + 1;
+
+ comedi_check_trigger_arg_min(&pacer->next_div1, 2);
+ comedi_check_trigger_arg_max(&pacer->next_div1,
+ I8254_MAX_COUNT);
+
+ base_period = pacer->osc_base * pacer->next_div1;
+
+ /* set a0 for conversion frequency and b1 for scan frequency */
+ switch (cmd->flags & CMDF_ROUND_MASK) {
+ default:
+ case CMDF_ROUND_NEAREST:
+ pacer->next_div = DIV_ROUND_CLOSEST(convert_period,
+ base_period);
+ pacer->next_div2 = DIV_ROUND_CLOSEST(scan_period,
+ base_period);
+ break;
+ case CMDF_ROUND_UP:
+ pacer->next_div = DIV_ROUND_UP(convert_period,
+ base_period);
+ pacer->next_div2 = DIV_ROUND_UP(scan_period,
+ base_period);
+ break;
+ case CMDF_ROUND_DOWN:
+ pacer->next_div = convert_period / base_period;
+ pacer->next_div2 = scan_period / base_period;
+ break;
+ }
+ /* make sure a0 and b1 values are acceptable */
+ comedi_check_trigger_arg_min(&pacer->next_div, 2);
+ comedi_check_trigger_arg_max(&pacer->next_div, I8254_MAX_COUNT);
+ comedi_check_trigger_arg_min(&pacer->next_div2, 2);
+ comedi_check_trigger_arg_max(&pacer->next_div2,
+ I8254_MAX_COUNT);
+
+ /* write corrected timings to command */
+ labpc_set_ai_convert_period(cmd, mode,
+ base_period * pacer->next_div);
+ labpc_set_ai_scan_period(cmd, mode,
+ base_period * pacer->next_div2);
+ } else if (scan_period) {
+ /*
+ * calculate cascaded counter values
+ * that give desired scan timing
+ * (pacer->next_div2 / pacer->next_div1)
+ */
+ comedi_8254_cascade_ns_to_timer(pacer, &scan_period,
+ cmd->flags);
+ labpc_set_ai_scan_period(cmd, mode, scan_period);
+ } else if (convert_period) {
+ /*
+ * calculate cascaded counter values
+ * that give desired conversion timing
+ * (pacer->next_div / pacer->next_div1)
+ */
+ comedi_8254_cascade_ns_to_timer(pacer, &convert_period,
+ cmd->flags);
+ /* transfer div2 value so correct timer gets updated */
+ pacer->next_div = pacer->next_div2;
+ labpc_set_ai_convert_period(cmd, mode, convert_period);
+ }
+}
+
+static enum scan_mode labpc_ai_scan_mode(const struct comedi_cmd *cmd)
+{
+ unsigned int chan0;
+ unsigned int chan1;
+
+ if (cmd->chanlist_len == 1)
+ return MODE_SINGLE_CHAN;
+
+ /* chanlist may be NULL during cmdtest */
+ if (!cmd->chanlist)
+ return MODE_MULT_CHAN_UP;
+
+ chan0 = CR_CHAN(cmd->chanlist[0]);
+ chan1 = CR_CHAN(cmd->chanlist[1]);
+
+ if (chan0 < chan1)
+ return MODE_MULT_CHAN_UP;
+
+ if (chan0 > chan1)
+ return MODE_MULT_CHAN_DOWN;
+
+ return MODE_SINGLE_CHAN_INTERVAL;
+}
+
+static int labpc_ai_check_chanlist(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_cmd *cmd)
+{
+ enum scan_mode mode = labpc_ai_scan_mode(cmd);
+ unsigned int chan0 = CR_CHAN(cmd->chanlist[0]);
+ unsigned int range0 = CR_RANGE(cmd->chanlist[0]);
+ unsigned int aref0 = CR_AREF(cmd->chanlist[0]);
+ int i;
+
+ for (i = 0; i < cmd->chanlist_len; i++) {
+ unsigned int chan = CR_CHAN(cmd->chanlist[i]);
+ unsigned int range = CR_RANGE(cmd->chanlist[i]);
+ unsigned int aref = CR_AREF(cmd->chanlist[i]);
+
+ switch (mode) {
+ case MODE_SINGLE_CHAN:
+ break;
+ case MODE_SINGLE_CHAN_INTERVAL:
+ if (chan != chan0) {
+ dev_dbg(dev->class_dev,
+ "channel scanning order specified in chanlist is not supported by hardware\n");
+ return -EINVAL;
+ }
+ break;
+ case MODE_MULT_CHAN_UP:
+ if (chan != i) {
+ dev_dbg(dev->class_dev,
+ "channel scanning order specified in chanlist is not supported by hardware\n");
+ return -EINVAL;
+ }
+ break;
+ case MODE_MULT_CHAN_DOWN:
+ if (chan != (cmd->chanlist_len - i - 1)) {
+ dev_dbg(dev->class_dev,
+ "channel scanning order specified in chanlist is not supported by hardware\n");
+ return -EINVAL;
+ }
+ break;
+ }
+
+ if (range != range0) {
+ dev_dbg(dev->class_dev,
+ "entries in chanlist must all have the same range\n");
+ return -EINVAL;
+ }
+
+ if (aref != aref0) {
+ dev_dbg(dev->class_dev,
+ "entries in chanlist must all have the same reference\n");
+ return -EINVAL;
+ }
+ }
+
+ return 0;
+}
+
+static int labpc_ai_cmdtest(struct comedi_device *dev,
+ struct comedi_subdevice *s, struct comedi_cmd *cmd)
+{
+ const struct labpc_boardinfo *board = dev->board_ptr;
+ int err = 0;
+ int tmp, tmp2;
+ unsigned int stop_mask;
+ enum scan_mode mode;
+
+ /* Step 1 : check if triggers are trivially valid */
+
+ err |= comedi_check_trigger_src(&cmd->start_src, TRIG_NOW | TRIG_EXT);
+ err |= comedi_check_trigger_src(&cmd->scan_begin_src,
+ TRIG_TIMER | TRIG_FOLLOW | TRIG_EXT);
+ err |= comedi_check_trigger_src(&cmd->convert_src,
+ TRIG_TIMER | TRIG_EXT);
+ err |= comedi_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
+
+ stop_mask = TRIG_COUNT | TRIG_NONE;
+ if (board->is_labpc1200)
+ stop_mask |= TRIG_EXT;
+ err |= comedi_check_trigger_src(&cmd->stop_src, stop_mask);
+
+ if (err)
+ return 1;
+
+ /* Step 2a : make sure trigger sources are unique */
+
+ err |= comedi_check_trigger_is_unique(cmd->start_src);
+ err |= comedi_check_trigger_is_unique(cmd->scan_begin_src);
+ err |= comedi_check_trigger_is_unique(cmd->convert_src);
+ err |= comedi_check_trigger_is_unique(cmd->stop_src);
+
+ /* Step 2b : and mutually compatible */
+
+ /* can't have external stop and start triggers at once */
+ if (cmd->start_src == TRIG_EXT && cmd->stop_src == TRIG_EXT)
+ err++;
+
+ if (err)
+ return 2;
+
+ /* Step 3: check if arguments are trivially valid */
+
+ switch (cmd->start_src) {
+ case TRIG_NOW:
+ err |= comedi_check_trigger_arg_is(&cmd->start_arg, 0);
+ break;
+ case TRIG_EXT:
+ /* start_arg value is ignored */
+ break;
+ }
+
+ if (!cmd->chanlist_len)
+ err |= -EINVAL;
+ err |= comedi_check_trigger_arg_is(&cmd->scan_end_arg,
+ cmd->chanlist_len);
+
+ if (cmd->convert_src == TRIG_TIMER) {
+ err |= comedi_check_trigger_arg_min(&cmd->convert_arg,
+ board->ai_speed);
+ }
+
+ /* make sure scan timing is not too fast */
+ if (cmd->scan_begin_src == TRIG_TIMER) {
+ if (cmd->convert_src == TRIG_TIMER) {
+ err |= comedi_check_trigger_arg_min(&cmd->
+ scan_begin_arg,
+ cmd->convert_arg *
+ cmd->chanlist_len);
+ }
+ err |= comedi_check_trigger_arg_min(&cmd->scan_begin_arg,
+ board->ai_speed *
+ cmd->chanlist_len);
+ }
+
+ switch (cmd->stop_src) {
+ case TRIG_COUNT:
+ err |= comedi_check_trigger_arg_min(&cmd->stop_arg, 1);
+ break;
+ case TRIG_NONE:
+ err |= comedi_check_trigger_arg_is(&cmd->stop_arg, 0);
+ break;
+ /*
+ * TRIG_EXT doesn't care since it doesn't
+ * trigger off a numbered channel
+ */
+ default:
+ break;
+ }
+
+ if (err)
+ return 3;
+
+ /* step 4: fix up any arguments */
+
+ tmp = cmd->convert_arg;
+ tmp2 = cmd->scan_begin_arg;
+ mode = labpc_ai_scan_mode(cmd);
+ labpc_adc_timing(dev, cmd, mode);
+ if (tmp != cmd->convert_arg || tmp2 != cmd->scan_begin_arg)
+ err++;
+
+ if (err)
+ return 4;
+
+ /* Step 5: check channel list if it exists */
+ if (cmd->chanlist && cmd->chanlist_len > 0)
+ err |= labpc_ai_check_chanlist(dev, s, cmd);
+
+ if (err)
+ return 5;
+
+ return 0;
+}
+
+static int labpc_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
+{
+ const struct labpc_boardinfo *board = dev->board_ptr;
+ struct labpc_private *devpriv = dev->private;
+ struct comedi_async *async = s->async;
+ struct comedi_cmd *cmd = &async->cmd;
+ enum scan_mode mode = labpc_ai_scan_mode(cmd);
+ unsigned int chanspec = (mode == MODE_MULT_CHAN_UP) ?
+ cmd->chanlist[cmd->chanlist_len - 1] :
+ cmd->chanlist[0];
+ unsigned int chan = CR_CHAN(chanspec);
+ unsigned int range = CR_RANGE(chanspec);
+ unsigned int aref = CR_AREF(chanspec);
+ enum transfer_type xfer;
+ unsigned long flags;
+
+ /* make sure board is disabled before setting up acquisition */
+ labpc_cancel(dev, s);
+
+ /* initialize software conversion count */
+ if (cmd->stop_src == TRIG_COUNT)
+ devpriv->count = cmd->stop_arg * cmd->chanlist_len;
+
+ /* setup hardware conversion counter */
+ if (cmd->stop_src == TRIG_EXT) {
+ /*
+ * load counter a1 with count of 3
+ * (pc+ manual says this is minimum allowed) using mode 0
+ */
+ comedi_8254_load(devpriv->counter, 1,
+ 3, I8254_MODE0 | I8254_BINARY);
+ } else {
+ /* just put counter a1 in mode 0 to set its output low */
+ comedi_8254_set_mode(devpriv->counter, 1,
+ I8254_MODE0 | I8254_BINARY);
+ }
+
+ /* figure out what method we will use to transfer data */
+ if (devpriv->dma &&
+ /* dma unsafe at RT priority,
+ * and too much setup time for CMDF_WAKE_EOS */
+ (cmd->flags & (CMDF_WAKE_EOS | CMDF_PRIORITY)) == 0)
+ xfer = isa_dma_transfer;
+ else if (/* pc-plus has no fifo-half full interrupt */
+ board->is_labpc1200 &&
+ /* wake-end-of-scan should interrupt on fifo not empty */
+ (cmd->flags & CMDF_WAKE_EOS) == 0 &&
+ /* make sure we are taking more than just a few points */
+ (cmd->stop_src != TRIG_COUNT || devpriv->count > 256))
+ xfer = fifo_half_full_transfer;
+ else
+ xfer = fifo_not_empty_transfer;
+ devpriv->current_transfer = xfer;
+
+ labpc_ai_set_chan_and_gain(dev, mode, chan, range, aref);
+
+ labpc_setup_cmd6_reg(dev, s, mode, xfer, range, aref,
+ (cmd->stop_src == TRIG_EXT));
+
+ /* manual says to set scan enable bit on second pass */
+ if (mode == MODE_MULT_CHAN_UP || mode == MODE_MULT_CHAN_DOWN) {
+ devpriv->cmd1 |= CMD1_SCANEN;
+ /* need a brief delay before enabling scan, or scan
+ * list will get screwed when you switch
+ * between scan up to scan down mode - dunno why */
+ udelay(1);
+ devpriv->write_byte(dev, devpriv->cmd1, CMD1_REG);
+ }
+
+ devpriv->write_byte(dev, cmd->chanlist_len, INTERVAL_COUNT_REG);
+ /* load count */
+ devpriv->write_byte(dev, 0x1, INTERVAL_STROBE_REG);
+
+ if (cmd->convert_src == TRIG_TIMER ||
+ cmd->scan_begin_src == TRIG_TIMER) {
+ struct comedi_8254 *pacer = dev->pacer;
+ struct comedi_8254 *counter = devpriv->counter;
+
+ comedi_8254_update_divisors(pacer);
+
+ /* set up pacing */
+ comedi_8254_load(pacer, 0, pacer->divisor1,
+ I8254_MODE3 | I8254_BINARY);
+
+ /* set up conversion pacing */
+ comedi_8254_set_mode(counter, 0, I8254_MODE2 | I8254_BINARY);
+ if (labpc_ai_convert_period(cmd, mode))
+ comedi_8254_write(counter, 0, pacer->divisor);
+
+ /* set up scan pacing */
+ if (labpc_ai_scan_period(cmd, mode))
+ comedi_8254_load(pacer, 1, pacer->divisor2,
+ I8254_MODE2 | I8254_BINARY);
+ }
+
+ labpc_clear_adc_fifo(dev);
+
+ if (xfer == isa_dma_transfer)
+ labpc_setup_dma(dev, s);
+
+ /* enable error interrupts */
+ devpriv->cmd3 |= CMD3_ERRINTEN;
+ /* enable fifo not empty interrupt? */
+ if (xfer == fifo_not_empty_transfer)
+ devpriv->cmd3 |= CMD3_FIFOINTEN;
+ devpriv->write_byte(dev, devpriv->cmd3, CMD3_REG);
+
+ /* setup any external triggering/pacing (cmd4 register) */
+ devpriv->cmd4 = 0;
+ if (cmd->convert_src != TRIG_EXT)
+ devpriv->cmd4 |= CMD4_ECLKRCV;
+ /* XXX should discard first scan when using interval scanning
+ * since manual says it is not synced with scan clock */
+ if (!labpc_use_continuous_mode(cmd, mode)) {
+ devpriv->cmd4 |= CMD4_INTSCAN;
+ if (cmd->scan_begin_src == TRIG_EXT)
+ devpriv->cmd4 |= CMD4_EOIRCV;
+ }
+ /* single-ended/differential */
+ if (aref == AREF_DIFF)
+ devpriv->cmd4 |= CMD4_SEDIFF;
+ devpriv->write_byte(dev, devpriv->cmd4, CMD4_REG);
+
+ /* startup acquisition */
+
+ spin_lock_irqsave(&dev->spinlock, flags);
+
+ /* use 2 cascaded counters for pacing */
+ devpriv->cmd2 |= CMD2_TBSEL;
+
+ devpriv->cmd2 &= ~(CMD2_SWTRIG | CMD2_HWTRIG | CMD2_PRETRIG);
+ if (cmd->start_src == TRIG_EXT)
+ devpriv->cmd2 |= CMD2_HWTRIG;
+ else
+ devpriv->cmd2 |= CMD2_SWTRIG;
+ if (cmd->stop_src == TRIG_EXT)
+ devpriv->cmd2 |= (CMD2_HWTRIG | CMD2_PRETRIG);
+
+ devpriv->write_byte(dev, devpriv->cmd2, CMD2_REG);
+
+ spin_unlock_irqrestore(&dev->spinlock, flags);
+
+ return 0;
+}
+
+/* read all available samples from ai fifo */
+static int labpc_drain_fifo(struct comedi_device *dev)
+{
+ struct labpc_private *devpriv = dev->private;
+ struct comedi_async *async = dev->read_subdev->async;
+ struct comedi_cmd *cmd = &async->cmd;
+ unsigned short data;
+ const int timeout = 10000;
+ unsigned int i;
+
+ devpriv->stat1 = devpriv->read_byte(dev, STAT1_REG);
+
+ for (i = 0; (devpriv->stat1 & STAT1_DAVAIL) && i < timeout;
+ i++) {
+ /* quit if we have all the data we want */
+ if (cmd->stop_src == TRIG_COUNT) {
+ if (devpriv->count == 0)
+ break;
+ devpriv->count--;
+ }
+ data = labpc_read_adc_fifo(dev);
+ comedi_buf_write_samples(dev->read_subdev, &data, 1);
+ devpriv->stat1 = devpriv->read_byte(dev, STAT1_REG);
+ }
+ if (i == timeout) {
+ dev_err(dev->class_dev, "ai timeout, fifo never empties\n");
+ async->events |= COMEDI_CB_ERROR;
+ return -1;
+ }
+
+ return 0;
+}
+
+/* makes sure all data acquired by board is transferred to comedi (used
+ * when acquisition is terminated by stop_src == TRIG_EXT). */
+static void labpc_drain_dregs(struct comedi_device *dev)
+{
+ struct labpc_private *devpriv = dev->private;
+
+ if (devpriv->current_transfer == isa_dma_transfer)
+ labpc_drain_dma(dev);
+
+ labpc_drain_fifo(dev);
+}
+
+/* interrupt service routine */
+static irqreturn_t labpc_interrupt(int irq, void *d)
+{
+ struct comedi_device *dev = d;
+ const struct labpc_boardinfo *board = dev->board_ptr;
+ struct labpc_private *devpriv = dev->private;
+ struct comedi_subdevice *s = dev->read_subdev;
+ struct comedi_async *async;
+ struct comedi_cmd *cmd;
+
+ if (!dev->attached) {
+ dev_err(dev->class_dev, "premature interrupt\n");
+ return IRQ_HANDLED;
+ }
+
+ async = s->async;
+ cmd = &async->cmd;
+
+ /* read board status */
+ devpriv->stat1 = devpriv->read_byte(dev, STAT1_REG);
+ if (board->is_labpc1200)
+ devpriv->stat2 = devpriv->read_byte(dev, STAT2_REG);
+
+ if ((devpriv->stat1 & (STAT1_GATA0 | STAT1_CNTINT | STAT1_OVERFLOW |
+ STAT1_OVERRUN | STAT1_DAVAIL)) == 0 &&
+ (devpriv->stat2 & STAT2_OUTA1) == 0 &&
+ (devpriv->stat2 & STAT2_FIFONHF)) {
+ return IRQ_NONE;
+ }
+
+ if (devpriv->stat1 & STAT1_OVERRUN) {
+ /* clear error interrupt */
+ devpriv->write_byte(dev, 0x1, ADC_FIFO_CLEAR_REG);
+ async->events |= COMEDI_CB_ERROR;
+ comedi_handle_events(dev, s);
+ dev_err(dev->class_dev, "overrun\n");
+ return IRQ_HANDLED;
+ }
+
+ if (devpriv->current_transfer == isa_dma_transfer)
+ labpc_handle_dma_status(dev);
+ else
+ labpc_drain_fifo(dev);
+
+ if (devpriv->stat1 & STAT1_CNTINT) {
+ dev_err(dev->class_dev, "handled timer interrupt?\n");
+ /* clear it */
+ devpriv->write_byte(dev, 0x1, TIMER_CLEAR_REG);
+ }
+
+ if (devpriv->stat1 & STAT1_OVERFLOW) {
+ /* clear error interrupt */
+ devpriv->write_byte(dev, 0x1, ADC_FIFO_CLEAR_REG);
+ async->events |= COMEDI_CB_ERROR;
+ comedi_handle_events(dev, s);
+ dev_err(dev->class_dev, "overflow\n");
+ return IRQ_HANDLED;
+ }
+ /* handle external stop trigger */
+ if (cmd->stop_src == TRIG_EXT) {
+ if (devpriv->stat2 & STAT2_OUTA1) {
+ labpc_drain_dregs(dev);
+ async->events |= COMEDI_CB_EOA;
+ }
+ }
+
+ /* TRIG_COUNT end of acquisition */
+ if (cmd->stop_src == TRIG_COUNT) {
+ if (devpriv->count == 0)
+ async->events |= COMEDI_CB_EOA;
+ }
+
+ comedi_handle_events(dev, s);
+ return IRQ_HANDLED;
+}
+
+static void labpc_ao_write(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ unsigned int chan, unsigned int val)
+{
+ struct labpc_private *devpriv = dev->private;
+
+ devpriv->write_byte(dev, val & 0xff, DAC_LSB_REG(chan));
+ devpriv->write_byte(dev, (val >> 8) & 0xff, DAC_MSB_REG(chan));
+
+ s->readback[chan] = val;
+}
+
+static int labpc_ao_insn_write(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ const struct labpc_boardinfo *board = dev->board_ptr;
+ struct labpc_private *devpriv = dev->private;
+ int channel, range;
+ unsigned long flags;
+
+ channel = CR_CHAN(insn->chanspec);
+
+ /* turn off pacing of analog output channel */
+ /* note: hardware bug in daqcard-1200 means pacing cannot
+ * be independently enabled/disabled for its the two channels */
+ spin_lock_irqsave(&dev->spinlock, flags);
+ devpriv->cmd2 &= ~CMD2_LDAC(channel);
+ devpriv->write_byte(dev, devpriv->cmd2, CMD2_REG);
+ spin_unlock_irqrestore(&dev->spinlock, flags);
+
+ /* set range */
+ if (board->is_labpc1200) {
+ range = CR_RANGE(insn->chanspec);
+ if (comedi_range_is_unipolar(s, range))
+ devpriv->cmd6 |= CMD6_DACUNI(channel);
+ else
+ devpriv->cmd6 &= ~CMD6_DACUNI(channel);
+ /* write to register */
+ devpriv->write_byte(dev, devpriv->cmd6, CMD6_REG);
+ }
+ /* send data */
+ labpc_ao_write(dev, s, channel, data[0]);
+
+ return 1;
+}
+
+/* lowlevel write to eeprom/dac */
+static void labpc_serial_out(struct comedi_device *dev, unsigned int value,
+ unsigned int value_width)
+{
+ struct labpc_private *devpriv = dev->private;
+ int i;
+
+ for (i = 1; i <= value_width; i++) {
+ /* clear serial clock */
+ devpriv->cmd5 &= ~CMD5_SCLK;
+ /* send bits most significant bit first */
+ if (value & (1 << (value_width - i)))
+ devpriv->cmd5 |= CMD5_SDATA;
+ else
+ devpriv->cmd5 &= ~CMD5_SDATA;
+ udelay(1);
+ devpriv->write_byte(dev, devpriv->cmd5, CMD5_REG);
+ /* set clock to load bit */
+ devpriv->cmd5 |= CMD5_SCLK;
+ udelay(1);
+ devpriv->write_byte(dev, devpriv->cmd5, CMD5_REG);
+ }
+}
+
+/* lowlevel read from eeprom */
+static unsigned int labpc_serial_in(struct comedi_device *dev)
+{
+ struct labpc_private *devpriv = dev->private;
+ unsigned int value = 0;
+ int i;
+ const int value_width = 8; /* number of bits wide values are */
+
+ for (i = 1; i <= value_width; i++) {
+ /* set serial clock */
+ devpriv->cmd5 |= CMD5_SCLK;
+ udelay(1);
+ devpriv->write_byte(dev, devpriv->cmd5, CMD5_REG);
+ /* clear clock bit */
+ devpriv->cmd5 &= ~CMD5_SCLK;
+ udelay(1);
+ devpriv->write_byte(dev, devpriv->cmd5, CMD5_REG);
+ /* read bits most significant bit first */
+ udelay(1);
+ devpriv->stat2 = devpriv->read_byte(dev, STAT2_REG);
+ if (devpriv->stat2 & STAT2_PROMOUT)
+ value |= 1 << (value_width - i);
+ }
+
+ return value;
+}
+
+static unsigned int labpc_eeprom_read(struct comedi_device *dev,
+ unsigned int address)
+{
+ struct labpc_private *devpriv = dev->private;
+ unsigned int value;
+ /* bits to tell eeprom to expect a read */
+ const int read_instruction = 0x3;
+ /* 8 bit write lengths to eeprom */
+ const int write_length = 8;
+
+ /* enable read/write to eeprom */
+ devpriv->cmd5 &= ~CMD5_EEPROMCS;
+ udelay(1);
+ devpriv->write_byte(dev, devpriv->cmd5, CMD5_REG);
+ devpriv->cmd5 |= (CMD5_EEPROMCS | CMD5_WRTPRT);
+ udelay(1);
+ devpriv->write_byte(dev, devpriv->cmd5, CMD5_REG);
+
+ /* send read instruction */
+ labpc_serial_out(dev, read_instruction, write_length);
+ /* send 8 bit address to read from */
+ labpc_serial_out(dev, address, write_length);
+ /* read result */
+ value = labpc_serial_in(dev);
+
+ /* disable read/write to eeprom */
+ devpriv->cmd5 &= ~(CMD5_EEPROMCS | CMD5_WRTPRT);
+ udelay(1);
+ devpriv->write_byte(dev, devpriv->cmd5, CMD5_REG);
+
+ return value;
+}
+
+static unsigned int labpc_eeprom_read_status(struct comedi_device *dev)
+{
+ struct labpc_private *devpriv = dev->private;
+ unsigned int value;
+ const int read_status_instruction = 0x5;
+ const int write_length = 8; /* 8 bit write lengths to eeprom */
+
+ /* enable read/write to eeprom */
+ devpriv->cmd5 &= ~CMD5_EEPROMCS;
+ udelay(1);
+ devpriv->write_byte(dev, devpriv->cmd5, CMD5_REG);
+ devpriv->cmd5 |= (CMD5_EEPROMCS | CMD5_WRTPRT);
+ udelay(1);
+ devpriv->write_byte(dev, devpriv->cmd5, CMD5_REG);
+
+ /* send read status instruction */
+ labpc_serial_out(dev, read_status_instruction, write_length);
+ /* read result */
+ value = labpc_serial_in(dev);
+
+ /* disable read/write to eeprom */
+ devpriv->cmd5 &= ~(CMD5_EEPROMCS | CMD5_WRTPRT);
+ udelay(1);
+ devpriv->write_byte(dev, devpriv->cmd5, CMD5_REG);
+
+ return value;
+}
+
+static void labpc_eeprom_write(struct comedi_device *dev,
+ unsigned int address, unsigned int value)
+{
+ struct labpc_private *devpriv = dev->private;
+ const int write_enable_instruction = 0x6;
+ const int write_instruction = 0x2;
+ const int write_length = 8; /* 8 bit write lengths to eeprom */
+
+ /* enable read/write to eeprom */
+ devpriv->cmd5 &= ~CMD5_EEPROMCS;
+ udelay(1);
+ devpriv->write_byte(dev, devpriv->cmd5, CMD5_REG);
+ devpriv->cmd5 |= (CMD5_EEPROMCS | CMD5_WRTPRT);
+ udelay(1);
+ devpriv->write_byte(dev, devpriv->cmd5, CMD5_REG);
+
+ /* send write_enable instruction */
+ labpc_serial_out(dev, write_enable_instruction, write_length);
+ devpriv->cmd5 &= ~CMD5_EEPROMCS;
+ udelay(1);
+ devpriv->write_byte(dev, devpriv->cmd5, CMD5_REG);
+
+ /* send write instruction */
+ devpriv->cmd5 |= CMD5_EEPROMCS;
+ udelay(1);
+ devpriv->write_byte(dev, devpriv->cmd5, CMD5_REG);
+ labpc_serial_out(dev, write_instruction, write_length);
+ /* send 8 bit address to write to */
+ labpc_serial_out(dev, address, write_length);
+ /* write value */
+ labpc_serial_out(dev, value, write_length);
+ devpriv->cmd5 &= ~CMD5_EEPROMCS;
+ udelay(1);
+ devpriv->write_byte(dev, devpriv->cmd5, CMD5_REG);
+
+ /* disable read/write to eeprom */
+ devpriv->cmd5 &= ~(CMD5_EEPROMCS | CMD5_WRTPRT);
+ udelay(1);
+ devpriv->write_byte(dev, devpriv->cmd5, CMD5_REG);
+}
+
+/* writes to 8 bit calibration dacs */
+static void write_caldac(struct comedi_device *dev, unsigned int channel,
+ unsigned int value)
+{
+ struct labpc_private *devpriv = dev->private;
+
+ /* clear caldac load bit and make sure we don't write to eeprom */
+ devpriv->cmd5 &= ~(CMD5_CALDACLD | CMD5_EEPROMCS | CMD5_WRTPRT);
+ udelay(1);
+ devpriv->write_byte(dev, devpriv->cmd5, CMD5_REG);
+
+ /* write 4 bit channel */
+ labpc_serial_out(dev, channel, 4);
+ /* write 8 bit caldac value */
+ labpc_serial_out(dev, value, 8);
+
+ /* set and clear caldac bit to load caldac value */
+ devpriv->cmd5 |= CMD5_CALDACLD;
+ udelay(1);
+ devpriv->write_byte(dev, devpriv->cmd5, CMD5_REG);
+ devpriv->cmd5 &= ~CMD5_CALDACLD;
+ udelay(1);
+ devpriv->write_byte(dev, devpriv->cmd5, CMD5_REG);
+}
+
+static int labpc_calib_insn_write(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ unsigned int chan = CR_CHAN(insn->chanspec);
+
+ /*
+ * Only write the last data value to the caldac. Preceding
+ * data would be overwritten anyway.
+ */
+ if (insn->n > 0) {
+ unsigned int val = data[insn->n - 1];
+
+ if (s->readback[chan] != val) {
+ write_caldac(dev, chan, val);
+ s->readback[chan] = val;
+ }
+ }
+
+ return insn->n;
+}
+
+static int labpc_eeprom_ready(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned long context)
+{
+ unsigned int status;
+
+ /* make sure there isn't already a write in progress */
+ status = labpc_eeprom_read_status(dev);
+ if ((status & 0x1) == 0)
+ return 0;
+ return -EBUSY;
+}
+
+static int labpc_eeprom_insn_write(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ unsigned int chan = CR_CHAN(insn->chanspec);
+ int ret;
+
+ /* only allow writes to user area of eeprom */
+ if (chan < 16 || chan > 127)
+ return -EINVAL;
+
+ /*
+ * Only write the last data value to the eeprom. Preceding
+ * data would be overwritten anyway.
+ */
+ if (insn->n > 0) {
+ unsigned int val = data[insn->n - 1];
+
+ ret = comedi_timeout(dev, s, insn, labpc_eeprom_ready, 0);
+ if (ret)
+ return ret;
+
+ labpc_eeprom_write(dev, chan, val);
+ s->readback[chan] = val;
+ }
+
+ return insn->n;
+}
+
+int labpc_common_attach(struct comedi_device *dev,
+ unsigned int irq, unsigned long isr_flags)
+{
+ const struct labpc_boardinfo *board = dev->board_ptr;
+ struct labpc_private *devpriv;
+ struct comedi_subdevice *s;
+ int ret;
+ int i;
+
+ devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
+ if (!devpriv)
+ return -ENOMEM;
+
+ if (dev->mmio) {
+ devpriv->read_byte = labpc_readb;
+ devpriv->write_byte = labpc_writeb;
+ } else {
+ devpriv->read_byte = labpc_inb;
+ devpriv->write_byte = labpc_outb;
+ }
+
+ /* initialize board's command registers */
+ devpriv->write_byte(dev, devpriv->cmd1, CMD1_REG);
+ devpriv->write_byte(dev, devpriv->cmd2, CMD2_REG);
+ devpriv->write_byte(dev, devpriv->cmd3, CMD3_REG);
+ devpriv->write_byte(dev, devpriv->cmd4, CMD4_REG);
+ if (board->is_labpc1200) {
+ devpriv->write_byte(dev, devpriv->cmd5, CMD5_REG);
+ devpriv->write_byte(dev, devpriv->cmd6, CMD6_REG);
+ }
+
+ if (irq) {
+ ret = request_irq(irq, labpc_interrupt, isr_flags,
+ dev->board_name, dev);
+ if (ret == 0)
+ dev->irq = irq;
+ }
+
+ if (dev->mmio) {
+ dev->pacer = comedi_8254_mm_init(dev->mmio + COUNTER_B_BASE_REG,
+ I8254_OSC_BASE_2MHZ,
+ I8254_IO8, 0);
+ devpriv->counter = comedi_8254_mm_init(dev->mmio +
+ COUNTER_A_BASE_REG,
+ I8254_OSC_BASE_2MHZ,
+ I8254_IO8, 0);
+ } else {
+ dev->pacer = comedi_8254_init(dev->iobase + COUNTER_B_BASE_REG,
+ I8254_OSC_BASE_2MHZ,
+ I8254_IO8, 0);
+ devpriv->counter = comedi_8254_init(dev->iobase +
+ COUNTER_A_BASE_REG,
+ I8254_OSC_BASE_2MHZ,
+ I8254_IO8, 0);
+ }
+ if (!dev->pacer || !devpriv->counter)
+ return -ENOMEM;
+
+ ret = comedi_alloc_subdevices(dev, 5);
+ if (ret)
+ return ret;
+
+ /* analog input subdevice */
+ s = &dev->subdevices[0];
+ s->type = COMEDI_SUBD_AI;
+ s->subdev_flags = SDF_READABLE | SDF_GROUND | SDF_COMMON | SDF_DIFF;
+ s->n_chan = 8;
+ s->len_chanlist = 8;
+ s->maxdata = 0x0fff;
+ s->range_table = board->is_labpc1200 ?
+ &range_labpc_1200_ai : &range_labpc_plus_ai;
+ s->insn_read = labpc_ai_insn_read;
+ if (dev->irq) {
+ dev->read_subdev = s;
+ s->subdev_flags |= SDF_CMD_READ;
+ s->do_cmd = labpc_ai_cmd;
+ s->do_cmdtest = labpc_ai_cmdtest;
+ s->cancel = labpc_cancel;
+ }
+
+ /* analog output */
+ s = &dev->subdevices[1];
+ if (board->has_ao) {
+ s->type = COMEDI_SUBD_AO;
+ s->subdev_flags = SDF_READABLE | SDF_WRITABLE | SDF_GROUND;
+ s->n_chan = NUM_AO_CHAN;
+ s->maxdata = 0x0fff;
+ s->range_table = &range_labpc_ao;
+ s->insn_write = labpc_ao_insn_write;
+
+ ret = comedi_alloc_subdev_readback(s);
+ if (ret)
+ return ret;
+
+ /* initialize analog outputs to a known value */
+ for (i = 0; i < s->n_chan; i++)
+ labpc_ao_write(dev, s, i, s->maxdata / 2);
+ } else {
+ s->type = COMEDI_SUBD_UNUSED;
+ }
+
+ /* 8255 dio */
+ s = &dev->subdevices[2];
+ if (dev->mmio)
+ ret = subdev_8255_mm_init(dev, s, NULL, DIO_BASE_REG);
+ else
+ ret = subdev_8255_init(dev, s, NULL, DIO_BASE_REG);
+ if (ret)
+ return ret;
+
+ /* calibration subdevices for boards that have one */
+ s = &dev->subdevices[3];
+ if (board->is_labpc1200) {
+ s->type = COMEDI_SUBD_CALIB;
+ s->subdev_flags = SDF_READABLE | SDF_WRITABLE | SDF_INTERNAL;
+ s->n_chan = 16;
+ s->maxdata = 0xff;
+ s->insn_write = labpc_calib_insn_write;
+
+ ret = comedi_alloc_subdev_readback(s);
+ if (ret)
+ return ret;
+
+ for (i = 0; i < s->n_chan; i++) {
+ write_caldac(dev, i, s->maxdata / 2);
+ s->readback[i] = s->maxdata / 2;
+ }
+ } else {
+ s->type = COMEDI_SUBD_UNUSED;
+ }
+
+ /* EEPROM */
+ s = &dev->subdevices[4];
+ if (board->is_labpc1200) {
+ s->type = COMEDI_SUBD_MEMORY;
+ s->subdev_flags = SDF_READABLE | SDF_WRITABLE | SDF_INTERNAL;
+ s->n_chan = EEPROM_SIZE;
+ s->maxdata = 0xff;
+ s->insn_write = labpc_eeprom_insn_write;
+
+ ret = comedi_alloc_subdev_readback(s);
+ if (ret)
+ return ret;
+
+ for (i = 0; i < s->n_chan; i++)
+ s->readback[i] = labpc_eeprom_read(dev, i);
+ } else {
+ s->type = COMEDI_SUBD_UNUSED;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(labpc_common_attach);
+
+void labpc_common_detach(struct comedi_device *dev)
+{
+ struct labpc_private *devpriv = dev->private;
+
+ if (devpriv)
+ kfree(devpriv->counter);
+}
+EXPORT_SYMBOL_GPL(labpc_common_detach);
+
+static int __init labpc_common_init(void)
+{
+ return 0;
+}
+module_init(labpc_common_init);
+
+static void __exit labpc_common_exit(void)
+{
+}
+module_exit(labpc_common_exit);
+
+MODULE_AUTHOR("Comedi http://www.comedi.org");
+MODULE_DESCRIPTION("Comedi helper for ni_labpc, ni_labpc_pci, ni_labpc_cs");
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/comedi/drivers/ni_labpc_cs.c b/drivers/staging/comedi/drivers/ni_labpc_cs.c
new file mode 100644
index 000000000..a1c69ac07
--- /dev/null
+++ b/drivers/staging/comedi/drivers/ni_labpc_cs.c
@@ -0,0 +1,128 @@
+/*
+ comedi/drivers/ni_labpc_cs.c
+ Driver for National Instruments daqcard-1200 boards
+ Copyright (C) 2001, 2002, 2003 Frank Mori Hess <fmhess@users.sourceforge.net>
+
+ PCMCIA crap is adapted from dummy_cs.c 1.31 2001/08/24 12:13:13
+ from the pcmcia package.
+ The initial developer of the pcmcia dummy_cs.c code is David A. Hinds
+ <dahinds@users.sourceforge.net>. Portions created by David A. Hinds
+ are Copyright (C) 1999 David A. Hinds.
+
+ This program is free software; you can redistribute it and/or 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.
+*/
+/*
+Driver: ni_labpc_cs
+Description: National Instruments Lab-PC (& compatibles)
+Author: Frank Mori Hess <fmhess@users.sourceforge.net>
+Devices: [National Instruments] DAQCard-1200 (daqcard-1200)
+Status: works
+
+Thanks go to Fredrik Lingvall for much testing and perseverance in
+helping to debug daqcard-1200 support.
+
+The 1200 series boards have onboard calibration dacs for correcting
+analog input/output offsets and gains. The proper settings for these
+caldacs are stored on the board's eeprom. To read the caldac values
+from the eeprom and store them into a file that can be then be used by
+comedilib, use the comedi_calibrate program.
+
+Configuration options:
+ none
+
+The daqcard-1200 has quirky chanlist requirements
+when scanning multiple channels. Multiple channel scan
+sequence must start at highest channel, then decrement down to
+channel 0. Chanlists consisting of all one channel
+are also legal, and allow you to pace conversions in bursts.
+
+*/
+
+/*
+
+NI manuals:
+340988a (daqcard-1200)
+
+*/
+
+#include <linux/module.h>
+
+#include "../comedi_pcmcia.h"
+
+#include "ni_labpc.h"
+
+static const struct labpc_boardinfo labpc_cs_boards[] = {
+ {
+ .name = "daqcard-1200",
+ .ai_speed = 10000,
+ .has_ao = 1,
+ .is_labpc1200 = 1,
+ },
+};
+
+static int labpc_cs_auto_attach(struct comedi_device *dev,
+ unsigned long context)
+{
+ struct pcmcia_device *link = comedi_to_pcmcia_dev(dev);
+ int ret;
+
+ /* The ni_labpc driver needs the board_ptr */
+ dev->board_ptr = &labpc_cs_boards[0];
+
+ link->config_flags |= CONF_AUTO_SET_IO |
+ CONF_ENABLE_IRQ | CONF_ENABLE_PULSE_IRQ;
+ ret = comedi_pcmcia_enable(dev, NULL);
+ if (ret)
+ return ret;
+ dev->iobase = link->resource[0]->start;
+
+ if (!link->irq)
+ return -EINVAL;
+
+ return labpc_common_attach(dev, link->irq, IRQF_SHARED);
+}
+
+static void labpc_cs_detach(struct comedi_device *dev)
+{
+ labpc_common_detach(dev);
+ comedi_pcmcia_disable(dev);
+}
+
+static struct comedi_driver driver_labpc_cs = {
+ .driver_name = "ni_labpc_cs",
+ .module = THIS_MODULE,
+ .auto_attach = labpc_cs_auto_attach,
+ .detach = labpc_cs_detach,
+};
+
+static int labpc_cs_attach(struct pcmcia_device *link)
+{
+ return comedi_pcmcia_auto_config(link, &driver_labpc_cs);
+}
+
+static const struct pcmcia_device_id labpc_cs_ids[] = {
+ PCMCIA_DEVICE_MANF_CARD(0x010b, 0x0103), /* daqcard-1200 */
+ PCMCIA_DEVICE_NULL
+};
+MODULE_DEVICE_TABLE(pcmcia, labpc_cs_ids);
+
+static struct pcmcia_driver labpc_cs_driver = {
+ .name = "daqcard-1200",
+ .owner = THIS_MODULE,
+ .id_table = labpc_cs_ids,
+ .probe = labpc_cs_attach,
+ .remove = comedi_pcmcia_auto_unconfig,
+};
+module_comedi_pcmcia_driver(driver_labpc_cs, labpc_cs_driver);
+
+MODULE_DESCRIPTION("Comedi driver for National Instruments Lab-PC");
+MODULE_AUTHOR("Frank Mori Hess <fmhess@users.sourceforge.net>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/comedi/drivers/ni_labpc_isadma.c b/drivers/staging/comedi/drivers/ni_labpc_isadma.c
new file mode 100644
index 000000000..29dbdf5ec
--- /dev/null
+++ b/drivers/staging/comedi/drivers/ni_labpc_isadma.c
@@ -0,0 +1,190 @@
+/*
+ * comedi/drivers/ni_labpc_isadma.c
+ * ISA DMA support for National Instruments Lab-PC series boards and
+ * compatibles.
+ *
+ * Extracted from ni_labpc.c:
+ * Copyright (C) 2001-2003 Frank Mori Hess <fmhess@users.sourceforge.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/slab.h>
+
+#include "../comedidev.h"
+
+#include "comedi_isadma.h"
+#include "ni_labpc.h"
+#include "ni_labpc_regs.h"
+#include "ni_labpc_isadma.h"
+
+/* size in bytes of dma buffer */
+#define LABPC_ISADMA_BUFFER_SIZE 0xff00
+
+/* utility function that suggests a dma transfer size in bytes */
+static unsigned int labpc_suggest_transfer_size(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ unsigned int maxbytes)
+{
+ struct comedi_cmd *cmd = &s->async->cmd;
+ unsigned int sample_size = comedi_bytes_per_sample(s);
+ unsigned int size;
+ unsigned int freq;
+
+ if (cmd->convert_src == TRIG_TIMER)
+ freq = 1000000000 / cmd->convert_arg;
+ else
+ /* return some default value */
+ freq = 0xffffffff;
+
+ /* make buffer fill in no more than 1/3 second */
+ size = (freq / 3) * sample_size;
+
+ /* set a minimum and maximum size allowed */
+ if (size > maxbytes)
+ size = maxbytes;
+ else if (size < sample_size)
+ size = sample_size;
+
+ return size;
+}
+
+void labpc_setup_dma(struct comedi_device *dev, struct comedi_subdevice *s)
+{
+ struct labpc_private *devpriv = dev->private;
+ struct comedi_isadma_desc *desc = &devpriv->dma->desc[0];
+ struct comedi_cmd *cmd = &s->async->cmd;
+ unsigned int sample_size = comedi_bytes_per_sample(s);
+
+ /* set appropriate size of transfer */
+ desc->size = labpc_suggest_transfer_size(dev, s, desc->maxsize);
+ if (cmd->stop_src == TRIG_COUNT &&
+ devpriv->count * sample_size < desc->size)
+ desc->size = devpriv->count * sample_size;
+
+ comedi_isadma_program(desc);
+
+ /* set CMD3 bits for caller to enable DMA and interrupt */
+ devpriv->cmd3 |= (CMD3_DMAEN | CMD3_DMATCINTEN);
+}
+EXPORT_SYMBOL_GPL(labpc_setup_dma);
+
+void labpc_drain_dma(struct comedi_device *dev)
+{
+ struct labpc_private *devpriv = dev->private;
+ struct comedi_isadma_desc *desc = &devpriv->dma->desc[0];
+ struct comedi_subdevice *s = dev->read_subdev;
+ struct comedi_async *async = s->async;
+ struct comedi_cmd *cmd = &async->cmd;
+ unsigned int max_samples = comedi_bytes_to_samples(s, desc->size);
+ unsigned int residue;
+ unsigned int nsamples;
+ unsigned int leftover;
+
+ /*
+ * residue is the number of bytes left to be done on the dma
+ * transfer. It should always be zero at this point unless
+ * the stop_src is set to external triggering.
+ */
+ residue = comedi_isadma_disable(desc->chan);
+
+ /*
+ * Figure out how many samples to read for this transfer and
+ * how many will be stored for next time.
+ */
+ nsamples = max_samples - comedi_bytes_to_samples(s, residue);
+ if (cmd->stop_src == TRIG_COUNT) {
+ if (devpriv->count <= nsamples) {
+ nsamples = devpriv->count;
+ leftover = 0;
+ } else {
+ leftover = devpriv->count - nsamples;
+ if (leftover > max_samples)
+ leftover = max_samples;
+ }
+ devpriv->count -= nsamples;
+ } else {
+ leftover = max_samples;
+ }
+ desc->size = comedi_samples_to_bytes(s, leftover);
+
+ comedi_buf_write_samples(s, desc->virt_addr, nsamples);
+}
+EXPORT_SYMBOL_GPL(labpc_drain_dma);
+
+static void handle_isa_dma(struct comedi_device *dev)
+{
+ struct labpc_private *devpriv = dev->private;
+ struct comedi_isadma_desc *desc = &devpriv->dma->desc[0];
+
+ labpc_drain_dma(dev);
+
+ if (desc->size)
+ comedi_isadma_program(desc);
+
+ /* clear dma tc interrupt */
+ devpriv->write_byte(dev, 0x1, DMATC_CLEAR_REG);
+}
+
+void labpc_handle_dma_status(struct comedi_device *dev)
+{
+ const struct labpc_boardinfo *board = dev->board_ptr;
+ struct labpc_private *devpriv = dev->private;
+
+ /*
+ * if a dma terminal count of external stop trigger
+ * has occurred
+ */
+ if (devpriv->stat1 & STAT1_GATA0 ||
+ (board->is_labpc1200 && devpriv->stat2 & STAT2_OUTA1))
+ handle_isa_dma(dev);
+}
+EXPORT_SYMBOL_GPL(labpc_handle_dma_status);
+
+void labpc_init_dma_chan(struct comedi_device *dev, unsigned int dma_chan)
+{
+ struct labpc_private *devpriv = dev->private;
+
+ /* only DMA channels 3 and 1 are valid */
+ if (dma_chan != 1 && dma_chan != 3)
+ return;
+
+ /* DMA uses 1 buffer */
+ devpriv->dma = comedi_isadma_alloc(dev, 1, dma_chan, dma_chan,
+ LABPC_ISADMA_BUFFER_SIZE,
+ COMEDI_ISADMA_READ);
+}
+EXPORT_SYMBOL_GPL(labpc_init_dma_chan);
+
+void labpc_free_dma_chan(struct comedi_device *dev)
+{
+ struct labpc_private *devpriv = dev->private;
+
+ if (devpriv)
+ comedi_isadma_free(devpriv->dma);
+}
+EXPORT_SYMBOL_GPL(labpc_free_dma_chan);
+
+static int __init ni_labpc_isadma_init_module(void)
+{
+ return 0;
+}
+module_init(ni_labpc_isadma_init_module);
+
+static void __exit ni_labpc_isadma_cleanup_module(void)
+{
+}
+module_exit(ni_labpc_isadma_cleanup_module);
+
+MODULE_AUTHOR("Comedi http://www.comedi.org");
+MODULE_DESCRIPTION("Comedi NI Lab-PC ISA DMA support");
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/comedi/drivers/ni_labpc_isadma.h b/drivers/staging/comedi/drivers/ni_labpc_isadma.h
new file mode 100644
index 000000000..b8a1b0ee6
--- /dev/null
+++ b/drivers/staging/comedi/drivers/ni_labpc_isadma.h
@@ -0,0 +1,42 @@
+/*
+ * ni_labpc ISA DMA support.
+*/
+
+#ifndef _NI_LABPC_ISADMA_H
+#define _NI_LABPC_ISADMA_H
+
+#if IS_ENABLED(CONFIG_COMEDI_NI_LABPC_ISADMA)
+
+void labpc_init_dma_chan(struct comedi_device *dev, unsigned int dma_chan);
+void labpc_free_dma_chan(struct comedi_device *dev);
+void labpc_setup_dma(struct comedi_device *dev, struct comedi_subdevice *s);
+void labpc_drain_dma(struct comedi_device *dev);
+void labpc_handle_dma_status(struct comedi_device *dev);
+
+#else
+
+static inline void labpc_init_dma_chan(struct comedi_device *dev,
+ unsigned int dma_chan)
+{
+}
+
+static inline void labpc_free_dma_chan(struct comedi_device *dev)
+{
+}
+
+static inline void labpc_setup_dma(struct comedi_device *dev,
+ struct comedi_subdevice *s)
+{
+}
+
+static inline void labpc_drain_dma(struct comedi_device *dev)
+{
+}
+
+static inline void labpc_handle_dma_status(struct comedi_device *dev)
+{
+}
+
+#endif
+
+#endif /* _NI_LABPC_ISADMA_H */
diff --git a/drivers/staging/comedi/drivers/ni_labpc_pci.c b/drivers/staging/comedi/drivers/ni_labpc_pci.c
new file mode 100644
index 000000000..77d403801
--- /dev/null
+++ b/drivers/staging/comedi/drivers/ni_labpc_pci.c
@@ -0,0 +1,141 @@
+/*
+ * comedi/drivers/ni_labpc_pci.c
+ * Driver for National Instruments Lab-PC PCI-1200
+ * Copyright (C) 2001, 2002, 2003 Frank Mori Hess <fmhess@users.sourceforge.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+/*
+ * Driver: ni_labpc_pci
+ * Description: National Instruments Lab-PC PCI-1200
+ * Devices: [National Instruments] PCI-1200 (ni_pci-1200)
+ * Author: Frank Mori Hess <fmhess@users.sourceforge.net>
+ * Status: works
+ *
+ * This is the PCI-specific support split off from the ni_labpc driver.
+ *
+ * Configuration Options: not applicable, uses PCI auto config
+ *
+ * NI manuals:
+ * 340914a (pci-1200)
+ */
+
+#include <linux/module.h>
+#include <linux/interrupt.h>
+
+#include "../comedi_pci.h"
+
+#include "ni_labpc.h"
+
+enum labpc_pci_boardid {
+ BOARD_NI_PCI1200,
+};
+
+static const struct labpc_boardinfo labpc_pci_boards[] = {
+ [BOARD_NI_PCI1200] = {
+ .name = "ni_pci-1200",
+ .ai_speed = 10000,
+ .ai_scan_up = 1,
+ .has_ao = 1,
+ .is_labpc1200 = 1,
+ },
+};
+
+/* ripped from mite.h and mite_setup2() to avoid mite dependency */
+#define MITE_IODWBSR 0xc0 /* IO Device Window Base Size Register */
+#define WENAB (1 << 7) /* window enable */
+
+static int labpc_pci_mite_init(struct pci_dev *pcidev)
+{
+ void __iomem *mite_base;
+ u32 main_phys_addr;
+
+ /* ioremap the MITE registers (BAR 0) temporarily */
+ mite_base = pci_ioremap_bar(pcidev, 0);
+ if (!mite_base)
+ return -ENOMEM;
+
+ /* set data window to main registers (BAR 1) */
+ main_phys_addr = pci_resource_start(pcidev, 1);
+ writel(main_phys_addr | WENAB, mite_base + MITE_IODWBSR);
+
+ /* finished with MITE registers */
+ iounmap(mite_base);
+ return 0;
+}
+
+static int labpc_pci_auto_attach(struct comedi_device *dev,
+ unsigned long context)
+{
+ struct pci_dev *pcidev = comedi_to_pci_dev(dev);
+ const struct labpc_boardinfo *board = NULL;
+ int ret;
+
+ if (context < ARRAY_SIZE(labpc_pci_boards))
+ board = &labpc_pci_boards[context];
+ if (!board)
+ return -ENODEV;
+ dev->board_ptr = board;
+ dev->board_name = board->name;
+
+ ret = comedi_pci_enable(dev);
+ if (ret)
+ return ret;
+
+ ret = labpc_pci_mite_init(pcidev);
+ if (ret)
+ return ret;
+
+ dev->mmio = pci_ioremap_bar(pcidev, 1);
+ if (!dev->mmio)
+ return -ENOMEM;
+
+ return labpc_common_attach(dev, pcidev->irq, IRQF_SHARED);
+}
+
+static void labpc_pci_detach(struct comedi_device *dev)
+{
+ labpc_common_detach(dev);
+ comedi_pci_detach(dev);
+}
+
+static struct comedi_driver labpc_pci_comedi_driver = {
+ .driver_name = "labpc_pci",
+ .module = THIS_MODULE,
+ .auto_attach = labpc_pci_auto_attach,
+ .detach = labpc_pci_detach,
+};
+
+static const struct pci_device_id labpc_pci_table[] = {
+ { PCI_VDEVICE(NI, 0x161), BOARD_NI_PCI1200 },
+ { 0 }
+};
+MODULE_DEVICE_TABLE(pci, labpc_pci_table);
+
+static int labpc_pci_probe(struct pci_dev *dev,
+ const struct pci_device_id *id)
+{
+ return comedi_pci_auto_config(dev, &labpc_pci_comedi_driver,
+ id->driver_data);
+}
+
+static struct pci_driver labpc_pci_driver = {
+ .name = "labpc_pci",
+ .id_table = labpc_pci_table,
+ .probe = labpc_pci_probe,
+ .remove = comedi_pci_auto_unconfig,
+};
+module_comedi_pci_driver(labpc_pci_comedi_driver, labpc_pci_driver);
+
+MODULE_DESCRIPTION("Comedi: National Instruments Lab-PC PCI-1200 driver");
+MODULE_AUTHOR("Comedi http://www.comedi.org");
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/comedi/drivers/ni_labpc_regs.h b/drivers/staging/comedi/drivers/ni_labpc_regs.h
new file mode 100644
index 000000000..2a274a3e4
--- /dev/null
+++ b/drivers/staging/comedi/drivers/ni_labpc_regs.h
@@ -0,0 +1,75 @@
+/*
+ * ni_labpc register definitions.
+*/
+
+#ifndef _NI_LABPC_REGS_H
+#define _NI_LABPC_REGS_H
+
+/*
+ * Register map (all registers are 8-bit)
+ */
+#define STAT1_REG 0x00 /* R: Status 1 reg */
+#define STAT1_DAVAIL (1 << 0)
+#define STAT1_OVERRUN (1 << 1)
+#define STAT1_OVERFLOW (1 << 2)
+#define STAT1_CNTINT (1 << 3)
+#define STAT1_GATA0 (1 << 5)
+#define STAT1_EXTGATA0 (1 << 6)
+#define CMD1_REG 0x00 /* W: Command 1 reg */
+#define CMD1_MA(x) (((x) & 0x7) << 0)
+#define CMD1_TWOSCMP (1 << 3)
+#define CMD1_GAIN(x) (((x) & 0x7) << 4)
+#define CMD1_SCANEN (1 << 7)
+#define CMD2_REG 0x01 /* W: Command 2 reg */
+#define CMD2_PRETRIG (1 << 0)
+#define CMD2_HWTRIG (1 << 1)
+#define CMD2_SWTRIG (1 << 2)
+#define CMD2_TBSEL (1 << 3)
+#define CMD2_2SDAC0 (1 << 4)
+#define CMD2_2SDAC1 (1 << 5)
+#define CMD2_LDAC(x) (1 << (6 + (x)))
+#define CMD3_REG 0x02 /* W: Command 3 reg */
+#define CMD3_DMAEN (1 << 0)
+#define CMD3_DIOINTEN (1 << 1)
+#define CMD3_DMATCINTEN (1 << 2)
+#define CMD3_CNTINTEN (1 << 3)
+#define CMD3_ERRINTEN (1 << 4)
+#define CMD3_FIFOINTEN (1 << 5)
+#define ADC_START_CONVERT_REG 0x03 /* W: Start Convert reg */
+#define DAC_LSB_REG(x) (0x04 + 2 * (x)) /* W: DAC0/1 LSB reg */
+#define DAC_MSB_REG(x) (0x05 + 2 * (x)) /* W: DAC0/1 MSB reg */
+#define ADC_FIFO_CLEAR_REG 0x08 /* W: A/D FIFO Clear reg */
+#define ADC_FIFO_REG 0x0a /* R: A/D FIFO reg */
+#define DMATC_CLEAR_REG 0x0a /* W: DMA Interrupt Clear reg */
+#define TIMER_CLEAR_REG 0x0c /* W: Timer Interrupt Clear reg */
+#define CMD6_REG 0x0e /* W: Command 6 reg */
+#define CMD6_NRSE (1 << 0)
+#define CMD6_ADCUNI (1 << 1)
+#define CMD6_DACUNI(x) (1 << (2 + (x)))
+#define CMD6_HFINTEN (1 << 5)
+#define CMD6_DQINTEN (1 << 6)
+#define CMD6_SCANUP (1 << 7)
+#define CMD4_REG 0x0f /* W: Command 3 reg */
+#define CMD4_INTSCAN (1 << 0)
+#define CMD4_EOIRCV (1 << 1)
+#define CMD4_ECLKDRV (1 << 2)
+#define CMD4_SEDIFF (1 << 3)
+#define CMD4_ECLKRCV (1 << 4)
+#define DIO_BASE_REG 0x10 /* R/W: 8255 DIO base reg */
+#define COUNTER_A_BASE_REG 0x14 /* R/W: 8253 Counter A base reg */
+#define COUNTER_B_BASE_REG 0x18 /* R/W: 8253 Counter B base reg */
+#define CMD5_REG 0x1c /* W: Command 5 reg */
+#define CMD5_WRTPRT (1 << 2)
+#define CMD5_DITHEREN (1 << 3)
+#define CMD5_CALDACLD (1 << 4)
+#define CMD5_SCLK (1 << 5)
+#define CMD5_SDATA (1 << 6)
+#define CMD5_EEPROMCS (1 << 7)
+#define STAT2_REG 0x1d /* R: Status 2 reg */
+#define STAT2_PROMOUT (1 << 0)
+#define STAT2_OUTA1 (1 << 1)
+#define STAT2_FIFONHF (1 << 2)
+#define INTERVAL_COUNT_REG 0x1e /* W: Interval Counter Data reg */
+#define INTERVAL_STROBE_REG 0x1f /* W: Interval Counter Strobe reg */
+
+#endif /* _NI_LABPC_REGS_H */
diff --git a/drivers/staging/comedi/drivers/ni_mio_common.c b/drivers/staging/comedi/drivers/ni_mio_common.c
new file mode 100644
index 000000000..c66affd99
--- /dev/null
+++ b/drivers/staging/comedi/drivers/ni_mio_common.c
@@ -0,0 +1,5703 @@
+/*
+ comedi/drivers/ni_mio_common.c
+ Hardware driver for DAQ-STC based boards
+
+ COMEDI - Linux Control and Measurement Device Interface
+ Copyright (C) 1997-2001 David A. Schleef <ds@schleef.org>
+ Copyright (C) 2002-2006 Frank Mori Hess <fmhess@users.sourceforge.net>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+*/
+
+/*
+ This file is meant to be included by another file, e.g.,
+ ni_atmio.c or ni_pcimio.c.
+
+ Interrupt support originally added by Truxton Fulton
+ <trux@truxton.com>
+
+ References (from ftp://ftp.natinst.com/support/manuals):
+
+ 340747b.pdf AT-MIO E series Register Level Programmer Manual
+ 341079b.pdf PCI E Series RLPM
+ 340934b.pdf DAQ-STC reference manual
+ 67xx and 611x registers (from ftp://ftp.ni.com/support/daq/mhddk/documentation/)
+ release_ni611x.pdf
+ release_ni67xx.pdf
+ Other possibly relevant info:
+
+ 320517c.pdf User manual (obsolete)
+ 320517f.pdf User manual (new)
+ 320889a.pdf delete
+ 320906c.pdf maximum signal ratings
+ 321066a.pdf about 16x
+ 321791a.pdf discontinuation of at-mio-16e-10 rev. c
+ 321808a.pdf about at-mio-16e-10 rev P
+ 321837a.pdf discontinuation of at-mio-16de-10 rev d
+ 321838a.pdf about at-mio-16de-10 rev N
+
+ ISSUES:
+
+ - the interrupt routine needs to be cleaned up
+
+ 2006-02-07: S-Series PCI-6143: Support has been added but is not
+ fully tested as yet. Terry Barnaby, BEAM Ltd.
+*/
+
+#include <linux/interrupt.h>
+#include <linux/sched.h>
+#include <linux/delay.h>
+#include "8255.h"
+#include "mite.h"
+
+/* A timeout count */
+#define NI_TIMEOUT 1000
+static const unsigned old_RTSI_clock_channel = 7;
+
+/* Note: this table must match the ai_gain_* definitions */
+static const short ni_gainlkup[][16] = {
+ [ai_gain_16] = {0, 1, 2, 3, 4, 5, 6, 7,
+ 0x100, 0x101, 0x102, 0x103, 0x104, 0x105, 0x106, 0x107},
+ [ai_gain_8] = {1, 2, 4, 7, 0x101, 0x102, 0x104, 0x107},
+ [ai_gain_14] = {1, 2, 3, 4, 5, 6, 7,
+ 0x101, 0x102, 0x103, 0x104, 0x105, 0x106, 0x107},
+ [ai_gain_4] = {0, 1, 4, 7},
+ [ai_gain_611x] = {0x00a, 0x00b, 0x001, 0x002,
+ 0x003, 0x004, 0x005, 0x006},
+ [ai_gain_622x] = {0, 1, 4, 5},
+ [ai_gain_628x] = {1, 2, 3, 4, 5, 6, 7},
+ [ai_gain_6143] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+};
+
+static const struct comedi_lrange range_ni_E_ai = {
+ 16, {
+ BIP_RANGE(10),
+ BIP_RANGE(5),
+ BIP_RANGE(2.5),
+ BIP_RANGE(1),
+ BIP_RANGE(0.5),
+ BIP_RANGE(0.25),
+ BIP_RANGE(0.1),
+ BIP_RANGE(0.05),
+ UNI_RANGE(20),
+ UNI_RANGE(10),
+ UNI_RANGE(5),
+ UNI_RANGE(2),
+ UNI_RANGE(1),
+ UNI_RANGE(0.5),
+ UNI_RANGE(0.2),
+ UNI_RANGE(0.1)
+ }
+};
+
+static const struct comedi_lrange range_ni_E_ai_limited = {
+ 8, {
+ BIP_RANGE(10),
+ BIP_RANGE(5),
+ BIP_RANGE(1),
+ BIP_RANGE(0.1),
+ UNI_RANGE(10),
+ UNI_RANGE(5),
+ UNI_RANGE(1),
+ UNI_RANGE(0.1)
+ }
+};
+
+static const struct comedi_lrange range_ni_E_ai_limited14 = {
+ 14, {
+ BIP_RANGE(10),
+ BIP_RANGE(5),
+ BIP_RANGE(2),
+ BIP_RANGE(1),
+ BIP_RANGE(0.5),
+ BIP_RANGE(0.2),
+ BIP_RANGE(0.1),
+ UNI_RANGE(10),
+ UNI_RANGE(5),
+ UNI_RANGE(2),
+ UNI_RANGE(1),
+ UNI_RANGE(0.5),
+ UNI_RANGE(0.2),
+ UNI_RANGE(0.1)
+ }
+};
+
+static const struct comedi_lrange range_ni_E_ai_bipolar4 = {
+ 4, {
+ BIP_RANGE(10),
+ BIP_RANGE(5),
+ BIP_RANGE(0.5),
+ BIP_RANGE(0.05)
+ }
+};
+
+static const struct comedi_lrange range_ni_E_ai_611x = {
+ 8, {
+ BIP_RANGE(50),
+ BIP_RANGE(20),
+ BIP_RANGE(10),
+ BIP_RANGE(5),
+ BIP_RANGE(2),
+ BIP_RANGE(1),
+ BIP_RANGE(0.5),
+ BIP_RANGE(0.2)
+ }
+};
+
+static const struct comedi_lrange range_ni_M_ai_622x = {
+ 4, {
+ BIP_RANGE(10),
+ BIP_RANGE(5),
+ BIP_RANGE(1),
+ BIP_RANGE(0.2)
+ }
+};
+
+static const struct comedi_lrange range_ni_M_ai_628x = {
+ 7, {
+ BIP_RANGE(10),
+ BIP_RANGE(5),
+ BIP_RANGE(2),
+ BIP_RANGE(1),
+ BIP_RANGE(0.5),
+ BIP_RANGE(0.2),
+ BIP_RANGE(0.1)
+ }
+};
+
+static const struct comedi_lrange range_ni_E_ao_ext = {
+ 4, {
+ BIP_RANGE(10),
+ UNI_RANGE(10),
+ RANGE_ext(-1, 1),
+ RANGE_ext(0, 1)
+ }
+};
+
+static const struct comedi_lrange *const ni_range_lkup[] = {
+ [ai_gain_16] = &range_ni_E_ai,
+ [ai_gain_8] = &range_ni_E_ai_limited,
+ [ai_gain_14] = &range_ni_E_ai_limited14,
+ [ai_gain_4] = &range_ni_E_ai_bipolar4,
+ [ai_gain_611x] = &range_ni_E_ai_611x,
+ [ai_gain_622x] = &range_ni_M_ai_622x,
+ [ai_gain_628x] = &range_ni_M_ai_628x,
+ [ai_gain_6143] = &range_bipolar5
+};
+
+enum aimodes {
+ AIMODE_NONE = 0,
+ AIMODE_HALF_FULL = 1,
+ AIMODE_SCAN = 2,
+ AIMODE_SAMPLE = 3,
+};
+
+enum ni_common_subdevices {
+ NI_AI_SUBDEV,
+ NI_AO_SUBDEV,
+ NI_DIO_SUBDEV,
+ NI_8255_DIO_SUBDEV,
+ NI_UNUSED_SUBDEV,
+ NI_CALIBRATION_SUBDEV,
+ NI_EEPROM_SUBDEV,
+ NI_PFI_DIO_SUBDEV,
+ NI_CS5529_CALIBRATION_SUBDEV,
+ NI_SERIAL_SUBDEV,
+ NI_RTSI_SUBDEV,
+ NI_GPCT0_SUBDEV,
+ NI_GPCT1_SUBDEV,
+ NI_FREQ_OUT_SUBDEV,
+ NI_NUM_SUBDEVICES
+};
+static inline unsigned NI_GPCT_SUBDEV(unsigned counter_index)
+{
+ switch (counter_index) {
+ case 0:
+ return NI_GPCT0_SUBDEV;
+ case 1:
+ return NI_GPCT1_SUBDEV;
+ default:
+ break;
+ }
+ BUG();
+ return NI_GPCT0_SUBDEV;
+}
+
+enum timebase_nanoseconds {
+ TIMEBASE_1_NS = 50,
+ TIMEBASE_2_NS = 10000
+};
+
+#define SERIAL_DISABLED 0
+#define SERIAL_600NS 600
+#define SERIAL_1_2US 1200
+#define SERIAL_10US 10000
+
+static const int num_adc_stages_611x = 3;
+
+static void ni_writel(struct comedi_device *dev, uint32_t data, int reg)
+{
+ if (dev->mmio)
+ writel(data, dev->mmio + reg);
+
+ outl(data, dev->iobase + reg);
+}
+
+static void ni_writew(struct comedi_device *dev, uint16_t data, int reg)
+{
+ if (dev->mmio)
+ writew(data, dev->mmio + reg);
+
+ outw(data, dev->iobase + reg);
+}
+
+static void ni_writeb(struct comedi_device *dev, uint8_t data, int reg)
+{
+ if (dev->mmio)
+ writeb(data, dev->mmio + reg);
+
+ outb(data, dev->iobase + reg);
+}
+
+static uint32_t ni_readl(struct comedi_device *dev, int reg)
+{
+ if (dev->mmio)
+ return readl(dev->mmio + reg);
+
+ return inl(dev->iobase + reg);
+}
+
+static uint16_t ni_readw(struct comedi_device *dev, int reg)
+{
+ if (dev->mmio)
+ return readw(dev->mmio + reg);
+
+ return inw(dev->iobase + reg);
+}
+
+static uint8_t ni_readb(struct comedi_device *dev, int reg)
+{
+ if (dev->mmio)
+ return readb(dev->mmio + reg);
+
+ return inb(dev->iobase + reg);
+}
+
+/*
+ * We automatically take advantage of STC registers that can be
+ * read/written directly in the I/O space of the board.
+ *
+ * The AT-MIO and DAQCard devices map the low 8 STC registers to
+ * iobase+reg*2.
+ *
+ * Most PCIMIO devices also map the low 8 STC registers but the
+ * 611x devices map the read registers to iobase+(addr-1)*2.
+ * For now non-windowed STC access is disabled if a PCIMIO device
+ * is detected (devpriv->mite has been initialized).
+ *
+ * The M series devices do not used windowed registers for the
+ * STC registers. The functions below handle the mapping of the
+ * windowed STC registers to the m series register offsets.
+ */
+
+static void m_series_stc_writel(struct comedi_device *dev,
+ uint32_t data, int reg)
+{
+ unsigned offset;
+
+ switch (reg) {
+ case AI_SC_Load_A_Registers:
+ offset = M_Offset_AI_SC_Load_A;
+ break;
+ case AI_SI_Load_A_Registers:
+ offset = M_Offset_AI_SI_Load_A;
+ break;
+ case AO_BC_Load_A_Register:
+ offset = M_Offset_AO_BC_Load_A;
+ break;
+ case AO_UC_Load_A_Register:
+ offset = M_Offset_AO_UC_Load_A;
+ break;
+ case AO_UI_Load_A_Register:
+ offset = M_Offset_AO_UI_Load_A;
+ break;
+ case G_Load_A_Register(0):
+ offset = M_Offset_G0_Load_A;
+ break;
+ case G_Load_A_Register(1):
+ offset = M_Offset_G1_Load_A;
+ break;
+ case G_Load_B_Register(0):
+ offset = M_Offset_G0_Load_B;
+ break;
+ case G_Load_B_Register(1):
+ offset = M_Offset_G1_Load_B;
+ break;
+ default:
+ dev_warn(dev->class_dev,
+ "%s: bug! unhandled register=0x%x in switch\n",
+ __func__, reg);
+ return;
+ }
+ ni_writel(dev, data, offset);
+}
+
+static void m_series_stc_writew(struct comedi_device *dev,
+ uint16_t data, int reg)
+{
+ unsigned offset;
+
+ switch (reg) {
+ case ADC_FIFO_Clear:
+ offset = M_Offset_AI_FIFO_Clear;
+ break;
+ case AI_Command_1_Register:
+ offset = M_Offset_AI_Command_1;
+ break;
+ case AI_Command_2_Register:
+ offset = M_Offset_AI_Command_2;
+ break;
+ case AI_Mode_1_Register:
+ offset = M_Offset_AI_Mode_1;
+ break;
+ case AI_Mode_2_Register:
+ offset = M_Offset_AI_Mode_2;
+ break;
+ case AI_Mode_3_Register:
+ offset = M_Offset_AI_Mode_3;
+ break;
+ case AI_Output_Control_Register:
+ offset = M_Offset_AI_Output_Control;
+ break;
+ case AI_Personal_Register:
+ offset = M_Offset_AI_Personal;
+ break;
+ case AI_SI2_Load_A_Register:
+ /* this is a 32 bit register on m series boards */
+ ni_writel(dev, data, M_Offset_AI_SI2_Load_A);
+ return;
+ case AI_SI2_Load_B_Register:
+ /* this is a 32 bit register on m series boards */
+ ni_writel(dev, data, M_Offset_AI_SI2_Load_B);
+ return;
+ case AI_START_STOP_Select_Register:
+ offset = M_Offset_AI_START_STOP_Select;
+ break;
+ case AI_Trigger_Select_Register:
+ offset = M_Offset_AI_Trigger_Select;
+ break;
+ case Analog_Trigger_Etc_Register:
+ offset = M_Offset_Analog_Trigger_Etc;
+ break;
+ case AO_Command_1_Register:
+ offset = M_Offset_AO_Command_1;
+ break;
+ case AO_Command_2_Register:
+ offset = M_Offset_AO_Command_2;
+ break;
+ case AO_Mode_1_Register:
+ offset = M_Offset_AO_Mode_1;
+ break;
+ case AO_Mode_2_Register:
+ offset = M_Offset_AO_Mode_2;
+ break;
+ case AO_Mode_3_Register:
+ offset = M_Offset_AO_Mode_3;
+ break;
+ case AO_Output_Control_Register:
+ offset = M_Offset_AO_Output_Control;
+ break;
+ case AO_Personal_Register:
+ offset = M_Offset_AO_Personal;
+ break;
+ case AO_Start_Select_Register:
+ offset = M_Offset_AO_Start_Select;
+ break;
+ case AO_Trigger_Select_Register:
+ offset = M_Offset_AO_Trigger_Select;
+ break;
+ case Clock_and_FOUT_Register:
+ offset = M_Offset_Clock_and_FOUT;
+ break;
+ case Configuration_Memory_Clear:
+ offset = M_Offset_Configuration_Memory_Clear;
+ break;
+ case DAC_FIFO_Clear:
+ offset = M_Offset_AO_FIFO_Clear;
+ break;
+ case DIO_Control_Register:
+ dev_dbg(dev->class_dev,
+ "%s: FIXME: register 0x%x does not map cleanly on to m-series boards\n",
+ __func__, reg);
+ return;
+ case G_Autoincrement_Register(0):
+ offset = M_Offset_G0_Autoincrement;
+ break;
+ case G_Autoincrement_Register(1):
+ offset = M_Offset_G1_Autoincrement;
+ break;
+ case G_Command_Register(0):
+ offset = M_Offset_G0_Command;
+ break;
+ case G_Command_Register(1):
+ offset = M_Offset_G1_Command;
+ break;
+ case G_Input_Select_Register(0):
+ offset = M_Offset_G0_Input_Select;
+ break;
+ case G_Input_Select_Register(1):
+ offset = M_Offset_G1_Input_Select;
+ break;
+ case G_Mode_Register(0):
+ offset = M_Offset_G0_Mode;
+ break;
+ case G_Mode_Register(1):
+ offset = M_Offset_G1_Mode;
+ break;
+ case Interrupt_A_Ack_Register:
+ offset = M_Offset_Interrupt_A_Ack;
+ break;
+ case Interrupt_A_Enable_Register:
+ offset = M_Offset_Interrupt_A_Enable;
+ break;
+ case Interrupt_B_Ack_Register:
+ offset = M_Offset_Interrupt_B_Ack;
+ break;
+ case Interrupt_B_Enable_Register:
+ offset = M_Offset_Interrupt_B_Enable;
+ break;
+ case Interrupt_Control_Register:
+ offset = M_Offset_Interrupt_Control;
+ break;
+ case IO_Bidirection_Pin_Register:
+ offset = M_Offset_IO_Bidirection_Pin;
+ break;
+ case Joint_Reset_Register:
+ offset = M_Offset_Joint_Reset;
+ break;
+ case RTSI_Trig_A_Output_Register:
+ offset = M_Offset_RTSI_Trig_A_Output;
+ break;
+ case RTSI_Trig_B_Output_Register:
+ offset = M_Offset_RTSI_Trig_B_Output;
+ break;
+ case RTSI_Trig_Direction_Register:
+ offset = M_Offset_RTSI_Trig_Direction;
+ break;
+ /*
+ * FIXME: DIO_Output_Register (16 bit reg) is replaced by
+ * M_Offset_Static_Digital_Output (32 bit) and
+ * M_Offset_SCXI_Serial_Data_Out (8 bit)
+ */
+ default:
+ dev_warn(dev->class_dev,
+ "%s: bug! unhandled register=0x%x in switch\n",
+ __func__, reg);
+ return;
+ }
+ ni_writew(dev, data, offset);
+}
+
+static uint32_t m_series_stc_readl(struct comedi_device *dev, int reg)
+{
+ unsigned offset;
+
+ switch (reg) {
+ case G_HW_Save_Register(0):
+ offset = M_Offset_G0_HW_Save;
+ break;
+ case G_HW_Save_Register(1):
+ offset = M_Offset_G1_HW_Save;
+ break;
+ case G_Save_Register(0):
+ offset = M_Offset_G0_Save;
+ break;
+ case G_Save_Register(1):
+ offset = M_Offset_G1_Save;
+ break;
+ default:
+ dev_warn(dev->class_dev,
+ "%s: bug! unhandled register=0x%x in switch\n",
+ __func__, reg);
+ return 0;
+ }
+ return ni_readl(dev, offset);
+}
+
+static uint16_t m_series_stc_readw(struct comedi_device *dev, int reg)
+{
+ unsigned offset;
+
+ switch (reg) {
+ case AI_Status_1_Register:
+ offset = M_Offset_AI_Status_1;
+ break;
+ case AO_Status_1_Register:
+ offset = M_Offset_AO_Status_1;
+ break;
+ case AO_Status_2_Register:
+ offset = M_Offset_AO_Status_2;
+ break;
+ case DIO_Serial_Input_Register:
+ return ni_readb(dev, M_Offset_SCXI_Serial_Data_In);
+ case Joint_Status_1_Register:
+ offset = M_Offset_Joint_Status_1;
+ break;
+ case Joint_Status_2_Register:
+ offset = M_Offset_Joint_Status_2;
+ break;
+ case G_Status_Register:
+ offset = M_Offset_G01_Status;
+ break;
+ default:
+ dev_warn(dev->class_dev,
+ "%s: bug! unhandled register=0x%x in switch\n",
+ __func__, reg);
+ return 0;
+ }
+ return ni_readw(dev, offset);
+}
+
+static void ni_stc_writew(struct comedi_device *dev, uint16_t data, int reg)
+{
+ struct ni_private *devpriv = dev->private;
+ unsigned long flags;
+
+ if (devpriv->is_m_series) {
+ m_series_stc_writew(dev, data, reg);
+ } else {
+ spin_lock_irqsave(&devpriv->window_lock, flags);
+ if (!devpriv->mite && reg < 8) {
+ ni_writew(dev, data, reg * 2);
+ } else {
+ ni_writew(dev, reg, Window_Address);
+ ni_writew(dev, data, Window_Data);
+ }
+ spin_unlock_irqrestore(&devpriv->window_lock, flags);
+ }
+}
+
+static void ni_stc_writel(struct comedi_device *dev, uint32_t data, int reg)
+{
+ struct ni_private *devpriv = dev->private;
+
+ if (devpriv->is_m_series) {
+ m_series_stc_writel(dev, data, reg);
+ } else {
+ ni_stc_writew(dev, data >> 16, reg);
+ ni_stc_writew(dev, data & 0xffff, reg + 1);
+ }
+}
+
+static uint16_t ni_stc_readw(struct comedi_device *dev, int reg)
+{
+ struct ni_private *devpriv = dev->private;
+ unsigned long flags;
+ uint16_t val;
+
+ if (devpriv->is_m_series) {
+ val = m_series_stc_readw(dev, reg);
+ } else {
+ spin_lock_irqsave(&devpriv->window_lock, flags);
+ if (!devpriv->mite && reg < 8) {
+ val = ni_readw(dev, reg * 2);
+ } else {
+ ni_writew(dev, reg, Window_Address);
+ val = ni_readw(dev, Window_Data);
+ }
+ spin_unlock_irqrestore(&devpriv->window_lock, flags);
+ }
+ return val;
+}
+
+static uint32_t ni_stc_readl(struct comedi_device *dev, int reg)
+{
+ struct ni_private *devpriv = dev->private;
+ uint32_t val;
+
+ if (devpriv->is_m_series) {
+ val = m_series_stc_readl(dev, reg);
+ } else {
+ val = ni_stc_readw(dev, reg) << 16;
+ val |= ni_stc_readw(dev, reg + 1);
+ }
+ return val;
+}
+
+static inline void ni_set_bitfield(struct comedi_device *dev, int reg,
+ unsigned bit_mask, unsigned bit_values)
+{
+ struct ni_private *devpriv = dev->private;
+ unsigned long flags;
+
+ spin_lock_irqsave(&devpriv->soft_reg_copy_lock, flags);
+ switch (reg) {
+ case Interrupt_A_Enable_Register:
+ devpriv->int_a_enable_reg &= ~bit_mask;
+ devpriv->int_a_enable_reg |= bit_values & bit_mask;
+ ni_stc_writew(dev, devpriv->int_a_enable_reg,
+ Interrupt_A_Enable_Register);
+ break;
+ case Interrupt_B_Enable_Register:
+ devpriv->int_b_enable_reg &= ~bit_mask;
+ devpriv->int_b_enable_reg |= bit_values & bit_mask;
+ ni_stc_writew(dev, devpriv->int_b_enable_reg,
+ Interrupt_B_Enable_Register);
+ break;
+ case IO_Bidirection_Pin_Register:
+ devpriv->io_bidirection_pin_reg &= ~bit_mask;
+ devpriv->io_bidirection_pin_reg |= bit_values & bit_mask;
+ ni_stc_writew(dev, devpriv->io_bidirection_pin_reg,
+ IO_Bidirection_Pin_Register);
+ break;
+ case AI_AO_Select:
+ devpriv->ai_ao_select_reg &= ~bit_mask;
+ devpriv->ai_ao_select_reg |= bit_values & bit_mask;
+ ni_writeb(dev, devpriv->ai_ao_select_reg, AI_AO_Select);
+ break;
+ case G0_G1_Select:
+ devpriv->g0_g1_select_reg &= ~bit_mask;
+ devpriv->g0_g1_select_reg |= bit_values & bit_mask;
+ ni_writeb(dev, devpriv->g0_g1_select_reg, G0_G1_Select);
+ break;
+ default:
+ dev_err(dev->class_dev, "called with invalid register %d\n",
+ reg);
+ break;
+ }
+ mmiowb();
+ spin_unlock_irqrestore(&devpriv->soft_reg_copy_lock, flags);
+}
+
+#ifdef PCIDMA
+/* DMA channel setup */
+
+/* negative channel means no channel */
+static inline void ni_set_ai_dma_channel(struct comedi_device *dev, int channel)
+{
+ unsigned bitfield;
+
+ if (channel >= 0)
+ bitfield =
+ (ni_stc_dma_channel_select_bitfield(channel) <<
+ AI_DMA_Select_Shift) & AI_DMA_Select_Mask;
+ else
+ bitfield = 0;
+ ni_set_bitfield(dev, AI_AO_Select, AI_DMA_Select_Mask, bitfield);
+}
+
+/* negative channel means no channel */
+static inline void ni_set_ao_dma_channel(struct comedi_device *dev, int channel)
+{
+ unsigned bitfield;
+
+ if (channel >= 0)
+ bitfield =
+ (ni_stc_dma_channel_select_bitfield(channel) <<
+ AO_DMA_Select_Shift) & AO_DMA_Select_Mask;
+ else
+ bitfield = 0;
+ ni_set_bitfield(dev, AI_AO_Select, AO_DMA_Select_Mask, bitfield);
+}
+
+/* negative mite_channel means no channel */
+static inline void ni_set_gpct_dma_channel(struct comedi_device *dev,
+ unsigned gpct_index,
+ int mite_channel)
+{
+ unsigned bitfield;
+
+ if (mite_channel >= 0)
+ bitfield = GPCT_DMA_Select_Bits(gpct_index, mite_channel);
+ else
+ bitfield = 0;
+ ni_set_bitfield(dev, G0_G1_Select, GPCT_DMA_Select_Mask(gpct_index),
+ bitfield);
+}
+
+/* negative mite_channel means no channel */
+static inline void ni_set_cdo_dma_channel(struct comedi_device *dev,
+ int mite_channel)
+{
+ struct ni_private *devpriv = dev->private;
+ unsigned long flags;
+
+ spin_lock_irqsave(&devpriv->soft_reg_copy_lock, flags);
+ devpriv->cdio_dma_select_reg &= ~CDO_DMA_Select_Mask;
+ if (mite_channel >= 0) {
+ /*XXX just guessing ni_stc_dma_channel_select_bitfield() returns the right bits,
+ under the assumption the cdio dma selection works just like ai/ao/gpct.
+ Definitely works for dma channels 0 and 1. */
+ devpriv->cdio_dma_select_reg |=
+ (ni_stc_dma_channel_select_bitfield(mite_channel) <<
+ CDO_DMA_Select_Shift) & CDO_DMA_Select_Mask;
+ }
+ ni_writeb(dev, devpriv->cdio_dma_select_reg, M_Offset_CDIO_DMA_Select);
+ mmiowb();
+ spin_unlock_irqrestore(&devpriv->soft_reg_copy_lock, flags);
+}
+
+static int ni_request_ai_mite_channel(struct comedi_device *dev)
+{
+ struct ni_private *devpriv = dev->private;
+ unsigned long flags;
+
+ spin_lock_irqsave(&devpriv->mite_channel_lock, flags);
+ BUG_ON(devpriv->ai_mite_chan);
+ devpriv->ai_mite_chan =
+ mite_request_channel(devpriv->mite, devpriv->ai_mite_ring);
+ if (!devpriv->ai_mite_chan) {
+ spin_unlock_irqrestore(&devpriv->mite_channel_lock, flags);
+ dev_err(dev->class_dev,
+ "failed to reserve mite dma channel for analog input\n");
+ return -EBUSY;
+ }
+ devpriv->ai_mite_chan->dir = COMEDI_INPUT;
+ ni_set_ai_dma_channel(dev, devpriv->ai_mite_chan->channel);
+ spin_unlock_irqrestore(&devpriv->mite_channel_lock, flags);
+ return 0;
+}
+
+static int ni_request_ao_mite_channel(struct comedi_device *dev)
+{
+ struct ni_private *devpriv = dev->private;
+ unsigned long flags;
+
+ spin_lock_irqsave(&devpriv->mite_channel_lock, flags);
+ BUG_ON(devpriv->ao_mite_chan);
+ devpriv->ao_mite_chan =
+ mite_request_channel(devpriv->mite, devpriv->ao_mite_ring);
+ if (!devpriv->ao_mite_chan) {
+ spin_unlock_irqrestore(&devpriv->mite_channel_lock, flags);
+ dev_err(dev->class_dev,
+ "failed to reserve mite dma channel for analog outut\n");
+ return -EBUSY;
+ }
+ devpriv->ao_mite_chan->dir = COMEDI_OUTPUT;
+ ni_set_ao_dma_channel(dev, devpriv->ao_mite_chan->channel);
+ spin_unlock_irqrestore(&devpriv->mite_channel_lock, flags);
+ return 0;
+}
+
+static int ni_request_gpct_mite_channel(struct comedi_device *dev,
+ unsigned gpct_index,
+ enum comedi_io_direction direction)
+{
+ struct ni_private *devpriv = dev->private;
+ unsigned long flags;
+ struct mite_channel *mite_chan;
+
+ BUG_ON(gpct_index >= NUM_GPCT);
+ spin_lock_irqsave(&devpriv->mite_channel_lock, flags);
+ BUG_ON(devpriv->counter_dev->counters[gpct_index].mite_chan);
+ mite_chan =
+ mite_request_channel(devpriv->mite,
+ devpriv->gpct_mite_ring[gpct_index]);
+ if (!mite_chan) {
+ spin_unlock_irqrestore(&devpriv->mite_channel_lock, flags);
+ dev_err(dev->class_dev,
+ "failed to reserve mite dma channel for counter\n");
+ return -EBUSY;
+ }
+ mite_chan->dir = direction;
+ ni_tio_set_mite_channel(&devpriv->counter_dev->counters[gpct_index],
+ mite_chan);
+ ni_set_gpct_dma_channel(dev, gpct_index, mite_chan->channel);
+ spin_unlock_irqrestore(&devpriv->mite_channel_lock, flags);
+ return 0;
+}
+
+#endif /* PCIDMA */
+
+static int ni_request_cdo_mite_channel(struct comedi_device *dev)
+{
+#ifdef PCIDMA
+ struct ni_private *devpriv = dev->private;
+ unsigned long flags;
+
+ spin_lock_irqsave(&devpriv->mite_channel_lock, flags);
+ BUG_ON(devpriv->cdo_mite_chan);
+ devpriv->cdo_mite_chan =
+ mite_request_channel(devpriv->mite, devpriv->cdo_mite_ring);
+ if (!devpriv->cdo_mite_chan) {
+ spin_unlock_irqrestore(&devpriv->mite_channel_lock, flags);
+ dev_err(dev->class_dev,
+ "failed to reserve mite dma channel for correlated digital output\n");
+ return -EBUSY;
+ }
+ devpriv->cdo_mite_chan->dir = COMEDI_OUTPUT;
+ ni_set_cdo_dma_channel(dev, devpriv->cdo_mite_chan->channel);
+ spin_unlock_irqrestore(&devpriv->mite_channel_lock, flags);
+#endif /* PCIDMA */
+ return 0;
+}
+
+static void ni_release_ai_mite_channel(struct comedi_device *dev)
+{
+#ifdef PCIDMA
+ struct ni_private *devpriv = dev->private;
+ unsigned long flags;
+
+ spin_lock_irqsave(&devpriv->mite_channel_lock, flags);
+ if (devpriv->ai_mite_chan) {
+ ni_set_ai_dma_channel(dev, -1);
+ mite_release_channel(devpriv->ai_mite_chan);
+ devpriv->ai_mite_chan = NULL;
+ }
+ spin_unlock_irqrestore(&devpriv->mite_channel_lock, flags);
+#endif /* PCIDMA */
+}
+
+static void ni_release_ao_mite_channel(struct comedi_device *dev)
+{
+#ifdef PCIDMA
+ struct ni_private *devpriv = dev->private;
+ unsigned long flags;
+
+ spin_lock_irqsave(&devpriv->mite_channel_lock, flags);
+ if (devpriv->ao_mite_chan) {
+ ni_set_ao_dma_channel(dev, -1);
+ mite_release_channel(devpriv->ao_mite_chan);
+ devpriv->ao_mite_chan = NULL;
+ }
+ spin_unlock_irqrestore(&devpriv->mite_channel_lock, flags);
+#endif /* PCIDMA */
+}
+
+#ifdef PCIDMA
+static void ni_release_gpct_mite_channel(struct comedi_device *dev,
+ unsigned gpct_index)
+{
+ struct ni_private *devpriv = dev->private;
+ unsigned long flags;
+
+ BUG_ON(gpct_index >= NUM_GPCT);
+ spin_lock_irqsave(&devpriv->mite_channel_lock, flags);
+ if (devpriv->counter_dev->counters[gpct_index].mite_chan) {
+ struct mite_channel *mite_chan =
+ devpriv->counter_dev->counters[gpct_index].mite_chan;
+
+ ni_set_gpct_dma_channel(dev, gpct_index, -1);
+ ni_tio_set_mite_channel(&devpriv->
+ counter_dev->counters[gpct_index],
+ NULL);
+ mite_release_channel(mite_chan);
+ }
+ spin_unlock_irqrestore(&devpriv->mite_channel_lock, flags);
+}
+#endif /* PCIDMA */
+
+static void ni_release_cdo_mite_channel(struct comedi_device *dev)
+{
+#ifdef PCIDMA
+ struct ni_private *devpriv = dev->private;
+ unsigned long flags;
+
+ spin_lock_irqsave(&devpriv->mite_channel_lock, flags);
+ if (devpriv->cdo_mite_chan) {
+ ni_set_cdo_dma_channel(dev, -1);
+ mite_release_channel(devpriv->cdo_mite_chan);
+ devpriv->cdo_mite_chan = NULL;
+ }
+ spin_unlock_irqrestore(&devpriv->mite_channel_lock, flags);
+#endif /* PCIDMA */
+}
+
+#ifdef PCIDMA
+static void ni_e_series_enable_second_irq(struct comedi_device *dev,
+ unsigned gpct_index, short enable)
+{
+ struct ni_private *devpriv = dev->private;
+ uint16_t val = 0;
+ int reg;
+
+ if (devpriv->is_m_series || gpct_index > 1)
+ return;
+
+ /*
+ * e-series boards use the second irq signals to generate
+ * dma requests for their counters
+ */
+ if (gpct_index == 0) {
+ reg = Second_IRQ_A_Enable_Register;
+ if (enable)
+ val = G0_Gate_Second_Irq_Enable;
+ } else {
+ reg = Second_IRQ_B_Enable_Register;
+ if (enable)
+ val = G1_Gate_Second_Irq_Enable;
+ }
+ ni_stc_writew(dev, val, reg);
+}
+#endif /* PCIDMA */
+
+static void ni_clear_ai_fifo(struct comedi_device *dev)
+{
+ struct ni_private *devpriv = dev->private;
+ static const int timeout = 10000;
+ int i;
+
+ if (devpriv->is_6143) {
+ /* Flush the 6143 data FIFO */
+ ni_writel(dev, 0x10, AIFIFO_Control_6143);
+ ni_writel(dev, 0x00, AIFIFO_Control_6143);
+ /* Wait for complete */
+ for (i = 0; i < timeout; i++) {
+ if (!(ni_readl(dev, AIFIFO_Status_6143) & 0x10))
+ break;
+ udelay(1);
+ }
+ if (i == timeout)
+ dev_err(dev->class_dev, "FIFO flush timeout\n");
+ } else {
+ ni_stc_writew(dev, 1, ADC_FIFO_Clear);
+ if (devpriv->is_625x) {
+ ni_writeb(dev, 0, M_Offset_Static_AI_Control(0));
+ ni_writeb(dev, 1, M_Offset_Static_AI_Control(0));
+#if 0
+ /* the NI example code does 3 convert pulses for 625x boards,
+ but that appears to be wrong in practice. */
+ ni_stc_writew(dev, AI_CONVERT_Pulse,
+ AI_Command_1_Register);
+ ni_stc_writew(dev, AI_CONVERT_Pulse,
+ AI_Command_1_Register);
+ ni_stc_writew(dev, AI_CONVERT_Pulse,
+ AI_Command_1_Register);
+#endif
+ }
+ }
+}
+
+static inline void ni_ao_win_outw(struct comedi_device *dev, uint16_t data,
+ int addr)
+{
+ struct ni_private *devpriv = dev->private;
+ unsigned long flags;
+
+ spin_lock_irqsave(&devpriv->window_lock, flags);
+ ni_writew(dev, addr, AO_Window_Address_611x);
+ ni_writew(dev, data, AO_Window_Data_611x);
+ spin_unlock_irqrestore(&devpriv->window_lock, flags);
+}
+
+static inline void ni_ao_win_outl(struct comedi_device *dev, uint32_t data,
+ int addr)
+{
+ struct ni_private *devpriv = dev->private;
+ unsigned long flags;
+
+ spin_lock_irqsave(&devpriv->window_lock, flags);
+ ni_writew(dev, addr, AO_Window_Address_611x);
+ ni_writel(dev, data, AO_Window_Data_611x);
+ spin_unlock_irqrestore(&devpriv->window_lock, flags);
+}
+
+static inline unsigned short ni_ao_win_inw(struct comedi_device *dev, int addr)
+{
+ struct ni_private *devpriv = dev->private;
+ unsigned long flags;
+ unsigned short data;
+
+ spin_lock_irqsave(&devpriv->window_lock, flags);
+ ni_writew(dev, addr, AO_Window_Address_611x);
+ data = ni_readw(dev, AO_Window_Data_611x);
+ spin_unlock_irqrestore(&devpriv->window_lock, flags);
+ return data;
+}
+
+/* ni_set_bits( ) allows different parts of the ni_mio_common driver to
+* share registers (such as Interrupt_A_Register) without interfering with
+* each other.
+*
+* NOTE: the switch/case statements are optimized out for a constant argument
+* so this is actually quite fast--- If you must wrap another function around this
+* make it inline to avoid a large speed penalty.
+*
+* value should only be 1 or 0.
+*/
+static inline void ni_set_bits(struct comedi_device *dev, int reg,
+ unsigned bits, unsigned value)
+{
+ unsigned bit_values;
+
+ if (value)
+ bit_values = bits;
+ else
+ bit_values = 0;
+ ni_set_bitfield(dev, reg, bits, bit_values);
+}
+
+#ifdef PCIDMA
+static void ni_sync_ai_dma(struct comedi_device *dev)
+{
+ struct ni_private *devpriv = dev->private;
+ struct comedi_subdevice *s = dev->read_subdev;
+ unsigned long flags;
+
+ spin_lock_irqsave(&devpriv->mite_channel_lock, flags);
+ if (devpriv->ai_mite_chan)
+ mite_sync_input_dma(devpriv->ai_mite_chan, s);
+ spin_unlock_irqrestore(&devpriv->mite_channel_lock, flags);
+}
+
+static int ni_ai_drain_dma(struct comedi_device *dev)
+{
+ struct ni_private *devpriv = dev->private;
+ int i;
+ static const int timeout = 10000;
+ unsigned long flags;
+ int retval = 0;
+
+ spin_lock_irqsave(&devpriv->mite_channel_lock, flags);
+ if (devpriv->ai_mite_chan) {
+ for (i = 0; i < timeout; i++) {
+ if ((ni_stc_readw(dev, AI_Status_1_Register) &
+ AI_FIFO_Empty_St)
+ && mite_bytes_in_transit(devpriv->ai_mite_chan) ==
+ 0)
+ break;
+ udelay(5);
+ }
+ if (i == timeout) {
+ dev_err(dev->class_dev, "timed out\n");
+ dev_err(dev->class_dev,
+ "mite_bytes_in_transit=%i, AI_Status1_Register=0x%x\n",
+ mite_bytes_in_transit(devpriv->ai_mite_chan),
+ ni_stc_readw(dev, AI_Status_1_Register));
+ retval = -1;
+ }
+ }
+ spin_unlock_irqrestore(&devpriv->mite_channel_lock, flags);
+
+ ni_sync_ai_dma(dev);
+
+ return retval;
+}
+
+static void mite_handle_b_linkc(struct mite_struct *mite,
+ struct comedi_device *dev)
+{
+ struct ni_private *devpriv = dev->private;
+ struct comedi_subdevice *s = dev->write_subdev;
+ unsigned long flags;
+
+ spin_lock_irqsave(&devpriv->mite_channel_lock, flags);
+ if (devpriv->ao_mite_chan)
+ mite_sync_output_dma(devpriv->ao_mite_chan, s);
+ spin_unlock_irqrestore(&devpriv->mite_channel_lock, flags);
+}
+
+static int ni_ao_wait_for_dma_load(struct comedi_device *dev)
+{
+ static const int timeout = 10000;
+ int i;
+
+ for (i = 0; i < timeout; i++) {
+ unsigned short b_status;
+
+ b_status = ni_stc_readw(dev, AO_Status_1_Register);
+ if (b_status & AO_FIFO_Half_Full_St)
+ break;
+ /* if we poll too often, the pci bus activity seems
+ to slow the dma transfer down */
+ udelay(10);
+ }
+ if (i == timeout) {
+ dev_err(dev->class_dev, "timed out waiting for dma load\n");
+ return -EPIPE;
+ }
+ return 0;
+}
+#endif /* PCIDMA */
+
+#ifndef PCIDMA
+
+static void ni_ao_fifo_load(struct comedi_device *dev,
+ struct comedi_subdevice *s, int n)
+{
+ struct ni_private *devpriv = dev->private;
+ int i;
+ unsigned short d;
+ u32 packed_data;
+
+ for (i = 0; i < n; i++) {
+ comedi_buf_read_samples(s, &d, 1);
+
+ if (devpriv->is_6xxx) {
+ packed_data = d & 0xffff;
+ /* 6711 only has 16 bit wide ao fifo */
+ if (!devpriv->is_6711) {
+ comedi_buf_read_samples(s, &d, 1);
+ i++;
+ packed_data |= (d << 16) & 0xffff0000;
+ }
+ ni_writel(dev, packed_data, DAC_FIFO_Data_611x);
+ } else {
+ ni_writew(dev, d, DAC_FIFO_Data);
+ }
+ }
+}
+
+/*
+ * There's a small problem if the FIFO gets really low and we
+ * don't have the data to fill it. Basically, if after we fill
+ * the FIFO with all the data available, the FIFO is _still_
+ * less than half full, we never clear the interrupt. If the
+ * IRQ is in edge mode, we never get another interrupt, because
+ * this one wasn't cleared. If in level mode, we get flooded
+ * with interrupts that we can't fulfill, because nothing ever
+ * gets put into the buffer.
+ *
+ * This kind of situation is recoverable, but it is easier to
+ * just pretend we had a FIFO underrun, since there is a good
+ * chance it will happen anyway. This is _not_ the case for
+ * RT code, as RT code might purposely be running close to the
+ * metal. Needs to be fixed eventually.
+ */
+static int ni_ao_fifo_half_empty(struct comedi_device *dev,
+ struct comedi_subdevice *s)
+{
+ const struct ni_board_struct *board = dev->board_ptr;
+ unsigned int nbytes;
+ unsigned int nsamples;
+
+ nbytes = comedi_buf_read_n_available(s);
+ if (nbytes == 0) {
+ s->async->events |= COMEDI_CB_OVERFLOW;
+ return 0;
+ }
+
+ nsamples = comedi_bytes_to_samples(s, nbytes);
+ if (nsamples > board->ao_fifo_depth / 2)
+ nsamples = board->ao_fifo_depth / 2;
+
+ ni_ao_fifo_load(dev, s, nsamples);
+
+ return 1;
+}
+
+static int ni_ao_prep_fifo(struct comedi_device *dev,
+ struct comedi_subdevice *s)
+{
+ const struct ni_board_struct *board = dev->board_ptr;
+ struct ni_private *devpriv = dev->private;
+ unsigned int nbytes;
+ unsigned int nsamples;
+
+ /* reset fifo */
+ ni_stc_writew(dev, 1, DAC_FIFO_Clear);
+ if (devpriv->is_6xxx)
+ ni_ao_win_outl(dev, 0x6, AO_FIFO_Offset_Load_611x);
+
+ /* load some data */
+ nbytes = comedi_buf_read_n_available(s);
+ if (nbytes == 0)
+ return 0;
+
+ nsamples = comedi_bytes_to_samples(s, nbytes);
+ if (nsamples > board->ao_fifo_depth)
+ nsamples = board->ao_fifo_depth;
+
+ ni_ao_fifo_load(dev, s, nsamples);
+
+ return nsamples;
+}
+
+static void ni_ai_fifo_read(struct comedi_device *dev,
+ struct comedi_subdevice *s, int n)
+{
+ struct ni_private *devpriv = dev->private;
+ struct comedi_async *async = s->async;
+ u32 dl;
+ unsigned short data;
+ int i;
+
+ if (devpriv->is_611x) {
+ for (i = 0; i < n / 2; i++) {
+ dl = ni_readl(dev, ADC_FIFO_Data_611x);
+ /* This may get the hi/lo data in the wrong order */
+ data = (dl >> 16) & 0xffff;
+ comedi_buf_write_samples(s, &data, 1);
+ data = dl & 0xffff;
+ comedi_buf_write_samples(s, &data, 1);
+ }
+ /* Check if there's a single sample stuck in the FIFO */
+ if (n % 2) {
+ dl = ni_readl(dev, ADC_FIFO_Data_611x);
+ data = dl & 0xffff;
+ comedi_buf_write_samples(s, &data, 1);
+ }
+ } else if (devpriv->is_6143) {
+ /* This just reads the FIFO assuming the data is present, no checks on the FIFO status are performed */
+ for (i = 0; i < n / 2; i++) {
+ dl = ni_readl(dev, AIFIFO_Data_6143);
+
+ data = (dl >> 16) & 0xffff;
+ comedi_buf_write_samples(s, &data, 1);
+ data = dl & 0xffff;
+ comedi_buf_write_samples(s, &data, 1);
+ }
+ if (n % 2) {
+ /* Assume there is a single sample stuck in the FIFO */
+ /* Get stranded sample into FIFO */
+ ni_writel(dev, 0x01, AIFIFO_Control_6143);
+ dl = ni_readl(dev, AIFIFO_Data_6143);
+ data = (dl >> 16) & 0xffff;
+ comedi_buf_write_samples(s, &data, 1);
+ }
+ } else {
+ if (n > sizeof(devpriv->ai_fifo_buffer) /
+ sizeof(devpriv->ai_fifo_buffer[0])) {
+ dev_err(dev->class_dev,
+ "bug! ai_fifo_buffer too small\n");
+ async->events |= COMEDI_CB_ERROR;
+ return;
+ }
+ for (i = 0; i < n; i++) {
+ devpriv->ai_fifo_buffer[i] =
+ ni_readw(dev, ADC_FIFO_Data_Register);
+ }
+ comedi_buf_write_samples(s, devpriv->ai_fifo_buffer, n);
+ }
+}
+
+static void ni_handle_fifo_half_full(struct comedi_device *dev)
+{
+ const struct ni_board_struct *board = dev->board_ptr;
+ struct comedi_subdevice *s = dev->read_subdev;
+ int n;
+
+ n = board->ai_fifo_depth / 2;
+
+ ni_ai_fifo_read(dev, s, n);
+}
+#endif
+
+/*
+ Empties the AI fifo
+*/
+static void ni_handle_fifo_dregs(struct comedi_device *dev)
+{
+ struct ni_private *devpriv = dev->private;
+ struct comedi_subdevice *s = dev->read_subdev;
+ u32 dl;
+ unsigned short data;
+ unsigned short fifo_empty;
+ int i;
+
+ if (devpriv->is_611x) {
+ while ((ni_stc_readw(dev, AI_Status_1_Register) &
+ AI_FIFO_Empty_St) == 0) {
+ dl = ni_readl(dev, ADC_FIFO_Data_611x);
+
+ /* This may get the hi/lo data in the wrong order */
+ data = dl >> 16;
+ comedi_buf_write_samples(s, &data, 1);
+ data = dl & 0xffff;
+ comedi_buf_write_samples(s, &data, 1);
+ }
+ } else if (devpriv->is_6143) {
+ i = 0;
+ while (ni_readl(dev, AIFIFO_Status_6143) & 0x04) {
+ dl = ni_readl(dev, AIFIFO_Data_6143);
+
+ /* This may get the hi/lo data in the wrong order */
+ data = dl >> 16;
+ comedi_buf_write_samples(s, &data, 1);
+ data = dl & 0xffff;
+ comedi_buf_write_samples(s, &data, 1);
+ i += 2;
+ }
+ /* Check if stranded sample is present */
+ if (ni_readl(dev, AIFIFO_Status_6143) & 0x01) {
+ /* Get stranded sample into FIFO */
+ ni_writel(dev, 0x01, AIFIFO_Control_6143);
+ dl = ni_readl(dev, AIFIFO_Data_6143);
+ data = (dl >> 16) & 0xffff;
+ comedi_buf_write_samples(s, &data, 1);
+ }
+
+ } else {
+ fifo_empty = ni_stc_readw(dev, AI_Status_1_Register) &
+ AI_FIFO_Empty_St;
+ while (fifo_empty == 0) {
+ for (i = 0;
+ i <
+ sizeof(devpriv->ai_fifo_buffer) /
+ sizeof(devpriv->ai_fifo_buffer[0]); i++) {
+ fifo_empty = ni_stc_readw(dev,
+ AI_Status_1_Register) &
+ AI_FIFO_Empty_St;
+ if (fifo_empty)
+ break;
+ devpriv->ai_fifo_buffer[i] =
+ ni_readw(dev, ADC_FIFO_Data_Register);
+ }
+ comedi_buf_write_samples(s, devpriv->ai_fifo_buffer, i);
+ }
+ }
+}
+
+static void get_last_sample_611x(struct comedi_device *dev)
+{
+ struct ni_private *devpriv = dev->private;
+ struct comedi_subdevice *s = dev->read_subdev;
+ unsigned short data;
+ u32 dl;
+
+ if (!devpriv->is_611x)
+ return;
+
+ /* Check if there's a single sample stuck in the FIFO */
+ if (ni_readb(dev, XXX_Status) & 0x80) {
+ dl = ni_readl(dev, ADC_FIFO_Data_611x);
+ data = dl & 0xffff;
+ comedi_buf_write_samples(s, &data, 1);
+ }
+}
+
+static void get_last_sample_6143(struct comedi_device *dev)
+{
+ struct ni_private *devpriv = dev->private;
+ struct comedi_subdevice *s = dev->read_subdev;
+ unsigned short data;
+ u32 dl;
+
+ if (!devpriv->is_6143)
+ return;
+
+ /* Check if there's a single sample stuck in the FIFO */
+ if (ni_readl(dev, AIFIFO_Status_6143) & 0x01) {
+ /* Get stranded sample into FIFO */
+ ni_writel(dev, 0x01, AIFIFO_Control_6143);
+ dl = ni_readl(dev, AIFIFO_Data_6143);
+
+ /* This may get the hi/lo data in the wrong order */
+ data = (dl >> 16) & 0xffff;
+ comedi_buf_write_samples(s, &data, 1);
+ }
+}
+
+static void shutdown_ai_command(struct comedi_device *dev)
+{
+ struct comedi_subdevice *s = dev->read_subdev;
+
+#ifdef PCIDMA
+ ni_ai_drain_dma(dev);
+#endif
+ ni_handle_fifo_dregs(dev);
+ get_last_sample_611x(dev);
+ get_last_sample_6143(dev);
+
+ s->async->events |= COMEDI_CB_EOA;
+}
+
+static void ni_handle_eos(struct comedi_device *dev, struct comedi_subdevice *s)
+{
+ struct ni_private *devpriv = dev->private;
+
+ if (devpriv->aimode == AIMODE_SCAN) {
+#ifdef PCIDMA
+ static const int timeout = 10;
+ int i;
+
+ for (i = 0; i < timeout; i++) {
+ ni_sync_ai_dma(dev);
+ if ((s->async->events & COMEDI_CB_EOS))
+ break;
+ udelay(1);
+ }
+#else
+ ni_handle_fifo_dregs(dev);
+ s->async->events |= COMEDI_CB_EOS;
+#endif
+ }
+ /* handle special case of single scan using AI_End_On_End_Of_Scan */
+ if ((devpriv->ai_cmd2 & AI_End_On_End_Of_Scan))
+ shutdown_ai_command(dev);
+}
+
+static void handle_gpct_interrupt(struct comedi_device *dev,
+ unsigned short counter_index)
+{
+#ifdef PCIDMA
+ struct ni_private *devpriv = dev->private;
+ struct comedi_subdevice *s;
+
+ s = &dev->subdevices[NI_GPCT_SUBDEV(counter_index)];
+
+ ni_tio_handle_interrupt(&devpriv->counter_dev->counters[counter_index],
+ s);
+ comedi_handle_events(dev, s);
+#endif
+}
+
+static void ack_a_interrupt(struct comedi_device *dev, unsigned short a_status)
+{
+ unsigned short ack = 0;
+
+ if (a_status & AI_SC_TC_St)
+ ack |= AI_SC_TC_Interrupt_Ack;
+ if (a_status & AI_START1_St)
+ ack |= AI_START1_Interrupt_Ack;
+ if (a_status & AI_START_St)
+ ack |= AI_START_Interrupt_Ack;
+ if (a_status & AI_STOP_St)
+ /* not sure why we used to ack the START here also, instead of doing it independently. Frank Hess 2007-07-06 */
+ ack |= AI_STOP_Interrupt_Ack /*| AI_START_Interrupt_Ack */;
+ if (ack)
+ ni_stc_writew(dev, ack, Interrupt_A_Ack_Register);
+}
+
+static void handle_a_interrupt(struct comedi_device *dev, unsigned short status,
+ unsigned ai_mite_status)
+{
+ struct comedi_subdevice *s = dev->read_subdev;
+ struct comedi_cmd *cmd = &s->async->cmd;
+
+ /* 67xx boards don't have ai subdevice, but their gpct0 might generate an a interrupt */
+ if (s->type == COMEDI_SUBD_UNUSED)
+ return;
+
+#ifdef PCIDMA
+ if (ai_mite_status & CHSR_LINKC)
+ ni_sync_ai_dma(dev);
+
+ if (ai_mite_status & ~(CHSR_INT | CHSR_LINKC | CHSR_DONE | CHSR_MRDY |
+ CHSR_DRDY | CHSR_DRQ1 | CHSR_DRQ0 | CHSR_ERROR |
+ CHSR_SABORT | CHSR_XFERR | CHSR_LxERR_mask)) {
+ dev_err(dev->class_dev,
+ "unknown mite interrupt (ai_mite_status=%08x)\n",
+ ai_mite_status);
+ s->async->events |= COMEDI_CB_ERROR;
+ /* disable_irq(dev->irq); */
+ }
+#endif
+
+ /* test for all uncommon interrupt events at the same time */
+ if (status & (AI_Overrun_St | AI_Overflow_St | AI_SC_TC_Error_St |
+ AI_SC_TC_St | AI_START1_St)) {
+ if (status == 0xffff) {
+ dev_err(dev->class_dev, "Card removed?\n");
+ /* we probably aren't even running a command now,
+ * so it's a good idea to be careful. */
+ if (comedi_is_subdevice_running(s)) {
+ s->async->events |= COMEDI_CB_ERROR;
+ comedi_handle_events(dev, s);
+ }
+ return;
+ }
+ if (status & (AI_Overrun_St | AI_Overflow_St |
+ AI_SC_TC_Error_St)) {
+ dev_err(dev->class_dev, "ai error a_status=%04x\n",
+ status);
+
+ shutdown_ai_command(dev);
+
+ s->async->events |= COMEDI_CB_ERROR;
+ if (status & (AI_Overrun_St | AI_Overflow_St))
+ s->async->events |= COMEDI_CB_OVERFLOW;
+
+ comedi_handle_events(dev, s);
+ return;
+ }
+ if (status & AI_SC_TC_St) {
+ if (cmd->stop_src == TRIG_COUNT)
+ shutdown_ai_command(dev);
+ }
+ }
+#ifndef PCIDMA
+ if (status & AI_FIFO_Half_Full_St) {
+ int i;
+ static const int timeout = 10;
+ /* pcmcia cards (at least 6036) seem to stop producing interrupts if we
+ *fail to get the fifo less than half full, so loop to be sure.*/
+ for (i = 0; i < timeout; ++i) {
+ ni_handle_fifo_half_full(dev);
+ if ((ni_stc_readw(dev, AI_Status_1_Register) &
+ AI_FIFO_Half_Full_St) == 0)
+ break;
+ }
+ }
+#endif /* !PCIDMA */
+
+ if ((status & AI_STOP_St))
+ ni_handle_eos(dev, s);
+
+ comedi_handle_events(dev, s);
+}
+
+static void ack_b_interrupt(struct comedi_device *dev, unsigned short b_status)
+{
+ unsigned short ack = 0;
+
+ if (b_status & AO_BC_TC_St)
+ ack |= AO_BC_TC_Interrupt_Ack;
+ if (b_status & AO_Overrun_St)
+ ack |= AO_Error_Interrupt_Ack;
+ if (b_status & AO_START_St)
+ ack |= AO_START_Interrupt_Ack;
+ if (b_status & AO_START1_St)
+ ack |= AO_START1_Interrupt_Ack;
+ if (b_status & AO_UC_TC_St)
+ ack |= AO_UC_TC_Interrupt_Ack;
+ if (b_status & AO_UI2_TC_St)
+ ack |= AO_UI2_TC_Interrupt_Ack;
+ if (b_status & AO_UPDATE_St)
+ ack |= AO_UPDATE_Interrupt_Ack;
+ if (ack)
+ ni_stc_writew(dev, ack, Interrupt_B_Ack_Register);
+}
+
+static void handle_b_interrupt(struct comedi_device *dev,
+ unsigned short b_status, unsigned ao_mite_status)
+{
+ struct comedi_subdevice *s = dev->write_subdev;
+ /* unsigned short ack=0; */
+
+#ifdef PCIDMA
+ /* Currently, mite.c requires us to handle LINKC */
+ if (ao_mite_status & CHSR_LINKC) {
+ struct ni_private *devpriv = dev->private;
+
+ mite_handle_b_linkc(devpriv->mite, dev);
+ }
+
+ if (ao_mite_status & ~(CHSR_INT | CHSR_LINKC | CHSR_DONE | CHSR_MRDY |
+ CHSR_DRDY | CHSR_DRQ1 | CHSR_DRQ0 | CHSR_ERROR |
+ CHSR_SABORT | CHSR_XFERR | CHSR_LxERR_mask)) {
+ dev_err(dev->class_dev,
+ "unknown mite interrupt (ao_mite_status=%08x)\n",
+ ao_mite_status);
+ s->async->events |= COMEDI_CB_ERROR;
+ }
+#endif
+
+ if (b_status == 0xffff)
+ return;
+ if (b_status & AO_Overrun_St) {
+ dev_err(dev->class_dev,
+ "AO FIFO underrun status=0x%04x status2=0x%04x\n",
+ b_status, ni_stc_readw(dev, AO_Status_2_Register));
+ s->async->events |= COMEDI_CB_OVERFLOW;
+ }
+
+ if (b_status & AO_BC_TC_St)
+ s->async->events |= COMEDI_CB_EOA;
+
+#ifndef PCIDMA
+ if (b_status & AO_FIFO_Request_St) {
+ int ret;
+
+ ret = ni_ao_fifo_half_empty(dev, s);
+ if (!ret) {
+ dev_err(dev->class_dev, "AO buffer underrun\n");
+ ni_set_bits(dev, Interrupt_B_Enable_Register,
+ AO_FIFO_Interrupt_Enable |
+ AO_Error_Interrupt_Enable, 0);
+ s->async->events |= COMEDI_CB_OVERFLOW;
+ }
+ }
+#endif
+
+ comedi_handle_events(dev, s);
+}
+
+static void ni_ai_munge(struct comedi_device *dev, struct comedi_subdevice *s,
+ void *data, unsigned int num_bytes,
+ unsigned int chan_index)
+{
+ struct ni_private *devpriv = dev->private;
+ struct comedi_async *async = s->async;
+ struct comedi_cmd *cmd = &async->cmd;
+ unsigned int nsamples = comedi_bytes_to_samples(s, num_bytes);
+ unsigned short *array = data;
+ unsigned int *larray = data;
+ unsigned int i;
+
+ for (i = 0; i < nsamples; i++) {
+#ifdef PCIDMA
+ if (s->subdev_flags & SDF_LSAMPL)
+ larray[i] = le32_to_cpu(larray[i]);
+ else
+ array[i] = le16_to_cpu(array[i]);
+#endif
+ if (s->subdev_flags & SDF_LSAMPL)
+ larray[i] += devpriv->ai_offset[chan_index];
+ else
+ array[i] += devpriv->ai_offset[chan_index];
+ chan_index++;
+ chan_index %= cmd->chanlist_len;
+ }
+}
+
+#ifdef PCIDMA
+
+static int ni_ai_setup_MITE_dma(struct comedi_device *dev)
+{
+ struct ni_private *devpriv = dev->private;
+ struct comedi_subdevice *s = dev->read_subdev;
+ int retval;
+ unsigned long flags;
+
+ retval = ni_request_ai_mite_channel(dev);
+ if (retval)
+ return retval;
+
+ /* write alloc the entire buffer */
+ comedi_buf_write_alloc(s, s->async->prealloc_bufsz);
+
+ spin_lock_irqsave(&devpriv->mite_channel_lock, flags);
+ if (!devpriv->ai_mite_chan) {
+ spin_unlock_irqrestore(&devpriv->mite_channel_lock, flags);
+ return -EIO;
+ }
+
+ if (devpriv->is_611x || devpriv->is_6143)
+ mite_prep_dma(devpriv->ai_mite_chan, 32, 16);
+ else if (devpriv->is_628x)
+ mite_prep_dma(devpriv->ai_mite_chan, 32, 32);
+ else
+ mite_prep_dma(devpriv->ai_mite_chan, 16, 16);
+
+ /*start the MITE */
+ mite_dma_arm(devpriv->ai_mite_chan);
+ spin_unlock_irqrestore(&devpriv->mite_channel_lock, flags);
+
+ return 0;
+}
+
+static int ni_ao_setup_MITE_dma(struct comedi_device *dev)
+{
+ struct ni_private *devpriv = dev->private;
+ struct comedi_subdevice *s = dev->write_subdev;
+ int retval;
+ unsigned long flags;
+
+ retval = ni_request_ao_mite_channel(dev);
+ if (retval)
+ return retval;
+
+ /* read alloc the entire buffer */
+ comedi_buf_read_alloc(s, s->async->prealloc_bufsz);
+
+ spin_lock_irqsave(&devpriv->mite_channel_lock, flags);
+ if (devpriv->ao_mite_chan) {
+ if (devpriv->is_611x || devpriv->is_6713) {
+ mite_prep_dma(devpriv->ao_mite_chan, 32, 32);
+ } else {
+ /* doing 32 instead of 16 bit wide transfers from memory
+ makes the mite do 32 bit pci transfers, doubling pci bandwidth. */
+ mite_prep_dma(devpriv->ao_mite_chan, 16, 32);
+ }
+ mite_dma_arm(devpriv->ao_mite_chan);
+ } else {
+ retval = -EIO;
+ }
+ spin_unlock_irqrestore(&devpriv->mite_channel_lock, flags);
+
+ return retval;
+}
+
+#endif /* PCIDMA */
+
+/*
+ used for both cancel ioctl and board initialization
+
+ this is pretty harsh for a cancel, but it works...
+ */
+
+static int ni_ai_reset(struct comedi_device *dev, struct comedi_subdevice *s)
+{
+ struct ni_private *devpriv = dev->private;
+
+ ni_release_ai_mite_channel(dev);
+ /* ai configuration */
+ ni_stc_writew(dev, AI_Configuration_Start | AI_Reset,
+ Joint_Reset_Register);
+
+ ni_set_bits(dev, Interrupt_A_Enable_Register,
+ AI_SC_TC_Interrupt_Enable | AI_START1_Interrupt_Enable |
+ AI_START2_Interrupt_Enable | AI_START_Interrupt_Enable |
+ AI_STOP_Interrupt_Enable | AI_Error_Interrupt_Enable |
+ AI_FIFO_Interrupt_Enable, 0);
+
+ ni_clear_ai_fifo(dev);
+
+ if (!devpriv->is_6143)
+ ni_writeb(dev, 0, Misc_Command);
+
+ ni_stc_writew(dev, AI_Disarm, AI_Command_1_Register); /* reset pulses */
+ ni_stc_writew(dev, AI_Start_Stop | AI_Mode_1_Reserved
+ /*| AI_Trigger_Once */,
+ AI_Mode_1_Register);
+ ni_stc_writew(dev, 0x0000, AI_Mode_2_Register);
+ /* generate FIFO interrupts on non-empty */
+ ni_stc_writew(dev, (0 << 6) | 0x0000, AI_Mode_3_Register);
+ if (devpriv->is_611x) {
+ ni_stc_writew(dev,
+ AI_SHIFTIN_Pulse_Width |
+ AI_SOC_Polarity |
+ AI_LOCALMUX_CLK_Pulse_Width,
+ AI_Personal_Register);
+ ni_stc_writew(dev,
+ AI_SCAN_IN_PROG_Output_Select(3) |
+ AI_EXTMUX_CLK_Output_Select(0) |
+ AI_LOCALMUX_CLK_Output_Select(2) |
+ AI_SC_TC_Output_Select(3) |
+ AI_CONVERT_Output_Select
+ (AI_CONVERT_Output_Enable_High),
+ AI_Output_Control_Register);
+ } else if (devpriv->is_6143) {
+ ni_stc_writew(dev, AI_SHIFTIN_Pulse_Width |
+ AI_SOC_Polarity |
+ AI_LOCALMUX_CLK_Pulse_Width,
+ AI_Personal_Register);
+ ni_stc_writew(dev,
+ AI_SCAN_IN_PROG_Output_Select(3) |
+ AI_EXTMUX_CLK_Output_Select(0) |
+ AI_LOCALMUX_CLK_Output_Select(2) |
+ AI_SC_TC_Output_Select(3) |
+ AI_CONVERT_Output_Select
+ (AI_CONVERT_Output_Enable_Low),
+ AI_Output_Control_Register);
+ } else {
+ unsigned ai_output_control_bits;
+
+ ni_stc_writew(dev,
+ AI_SHIFTIN_Pulse_Width |
+ AI_SOC_Polarity |
+ AI_CONVERT_Pulse_Width |
+ AI_LOCALMUX_CLK_Pulse_Width,
+ AI_Personal_Register);
+ ai_output_control_bits =
+ AI_SCAN_IN_PROG_Output_Select(3) |
+ AI_EXTMUX_CLK_Output_Select(0) |
+ AI_LOCALMUX_CLK_Output_Select(2) |
+ AI_SC_TC_Output_Select(3);
+ if (devpriv->is_622x)
+ ai_output_control_bits |=
+ AI_CONVERT_Output_Select
+ (AI_CONVERT_Output_Enable_High);
+ else
+ ai_output_control_bits |=
+ AI_CONVERT_Output_Select
+ (AI_CONVERT_Output_Enable_Low);
+ ni_stc_writew(dev, ai_output_control_bits,
+ AI_Output_Control_Register);
+ }
+ /* the following registers should not be changed, because there
+ * are no backup registers in devpriv. If you want to change
+ * any of these, add a backup register and other appropriate code:
+ * AI_Mode_1_Register
+ * AI_Mode_3_Register
+ * AI_Personal_Register
+ * AI_Output_Control_Register
+ */
+ ni_stc_writew(dev,
+ AI_SC_TC_Error_Confirm |
+ AI_START_Interrupt_Ack |
+ AI_START2_Interrupt_Ack |
+ AI_START1_Interrupt_Ack |
+ AI_SC_TC_Interrupt_Ack |
+ AI_Error_Interrupt_Ack |
+ AI_STOP_Interrupt_Ack,
+ Interrupt_A_Ack_Register); /* clear interrupts */
+
+ ni_stc_writew(dev, AI_Configuration_End, Joint_Reset_Register);
+
+ return 0;
+}
+
+static int ni_ai_poll(struct comedi_device *dev, struct comedi_subdevice *s)
+{
+ unsigned long flags;
+ int count;
+
+ /* lock to avoid race with interrupt handler */
+ spin_lock_irqsave(&dev->spinlock, flags);
+#ifndef PCIDMA
+ ni_handle_fifo_dregs(dev);
+#else
+ ni_sync_ai_dma(dev);
+#endif
+ count = comedi_buf_n_bytes_ready(s);
+ spin_unlock_irqrestore(&dev->spinlock, flags);
+
+ return count;
+}
+
+static void ni_prime_channelgain_list(struct comedi_device *dev)
+{
+ int i;
+
+ ni_stc_writew(dev, AI_CONVERT_Pulse, AI_Command_1_Register);
+ for (i = 0; i < NI_TIMEOUT; ++i) {
+ if (!(ni_stc_readw(dev, AI_Status_1_Register) &
+ AI_FIFO_Empty_St)) {
+ ni_stc_writew(dev, 1, ADC_FIFO_Clear);
+ return;
+ }
+ udelay(1);
+ }
+ dev_err(dev->class_dev, "timeout loading channel/gain list\n");
+}
+
+static void ni_m_series_load_channelgain_list(struct comedi_device *dev,
+ unsigned int n_chan,
+ unsigned int *list)
+{
+ const struct ni_board_struct *board = dev->board_ptr;
+ struct ni_private *devpriv = dev->private;
+ unsigned int chan, range, aref;
+ unsigned int i;
+ unsigned int dither;
+ unsigned range_code;
+
+ ni_stc_writew(dev, 1, Configuration_Memory_Clear);
+
+ if ((list[0] & CR_ALT_SOURCE)) {
+ unsigned bypass_bits;
+
+ chan = CR_CHAN(list[0]);
+ range = CR_RANGE(list[0]);
+ range_code = ni_gainlkup[board->gainlkup][range];
+ dither = (list[0] & CR_ALT_FILTER) != 0;
+ bypass_bits = MSeries_AI_Bypass_Config_FIFO_Bit;
+ bypass_bits |= chan;
+ bypass_bits |=
+ (devpriv->ai_calib_source) &
+ (MSeries_AI_Bypass_Cal_Sel_Pos_Mask |
+ MSeries_AI_Bypass_Cal_Sel_Neg_Mask |
+ MSeries_AI_Bypass_Mode_Mux_Mask |
+ MSeries_AO_Bypass_AO_Cal_Sel_Mask);
+ bypass_bits |= MSeries_AI_Bypass_Gain_Bits(range_code);
+ if (dither)
+ bypass_bits |= MSeries_AI_Bypass_Dither_Bit;
+ /* don't use 2's complement encoding */
+ bypass_bits |= MSeries_AI_Bypass_Polarity_Bit;
+ ni_writel(dev, bypass_bits, M_Offset_AI_Config_FIFO_Bypass);
+ } else {
+ ni_writel(dev, 0, M_Offset_AI_Config_FIFO_Bypass);
+ }
+ for (i = 0; i < n_chan; i++) {
+ unsigned config_bits = 0;
+
+ chan = CR_CHAN(list[i]);
+ aref = CR_AREF(list[i]);
+ range = CR_RANGE(list[i]);
+ dither = (list[i] & CR_ALT_FILTER) != 0;
+
+ range_code = ni_gainlkup[board->gainlkup][range];
+ devpriv->ai_offset[i] = 0;
+ switch (aref) {
+ case AREF_DIFF:
+ config_bits |=
+ MSeries_AI_Config_Channel_Type_Differential_Bits;
+ break;
+ case AREF_COMMON:
+ config_bits |=
+ MSeries_AI_Config_Channel_Type_Common_Ref_Bits;
+ break;
+ case AREF_GROUND:
+ config_bits |=
+ MSeries_AI_Config_Channel_Type_Ground_Ref_Bits;
+ break;
+ case AREF_OTHER:
+ break;
+ }
+ config_bits |= MSeries_AI_Config_Channel_Bits(chan);
+ config_bits |=
+ MSeries_AI_Config_Bank_Bits(board->reg_type, chan);
+ config_bits |= MSeries_AI_Config_Gain_Bits(range_code);
+ if (i == n_chan - 1)
+ config_bits |= MSeries_AI_Config_Last_Channel_Bit;
+ if (dither)
+ config_bits |= MSeries_AI_Config_Dither_Bit;
+ /* don't use 2's complement encoding */
+ config_bits |= MSeries_AI_Config_Polarity_Bit;
+ ni_writew(dev, config_bits, M_Offset_AI_Config_FIFO_Data);
+ }
+ ni_prime_channelgain_list(dev);
+}
+
+/*
+ * Notes on the 6110 and 6111:
+ * These boards a slightly different than the rest of the series, since
+ * they have multiple A/D converters.
+ * From the driver side, the configuration memory is a
+ * little different.
+ * Configuration Memory Low:
+ * bits 15-9: same
+ * bit 8: unipolar/bipolar (should be 0 for bipolar)
+ * bits 0-3: gain. This is 4 bits instead of 3 for the other boards
+ * 1001 gain=0.1 (+/- 50)
+ * 1010 0.2
+ * 1011 0.1
+ * 0001 1
+ * 0010 2
+ * 0011 5
+ * 0100 10
+ * 0101 20
+ * 0110 50
+ * Configuration Memory High:
+ * bits 12-14: Channel Type
+ * 001 for differential
+ * 000 for calibration
+ * bit 11: coupling (this is not currently handled)
+ * 1 AC coupling
+ * 0 DC coupling
+ * bits 0-2: channel
+ * valid channels are 0-3
+ */
+static void ni_load_channelgain_list(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ unsigned int n_chan, unsigned int *list)
+{
+ const struct ni_board_struct *board = dev->board_ptr;
+ struct ni_private *devpriv = dev->private;
+ unsigned int offset = (s->maxdata + 1) >> 1;
+ unsigned int chan, range, aref;
+ unsigned int i;
+ unsigned int hi, lo;
+ unsigned int dither;
+
+ if (devpriv->is_m_series) {
+ ni_m_series_load_channelgain_list(dev, n_chan, list);
+ return;
+ }
+ if (n_chan == 1 && !devpriv->is_611x && !devpriv->is_6143) {
+ if (devpriv->changain_state
+ && devpriv->changain_spec == list[0]) {
+ /* ready to go. */
+ return;
+ }
+ devpriv->changain_state = 1;
+ devpriv->changain_spec = list[0];
+ } else {
+ devpriv->changain_state = 0;
+ }
+
+ ni_stc_writew(dev, 1, Configuration_Memory_Clear);
+
+ /* Set up Calibration mode if required */
+ if (devpriv->is_6143) {
+ if ((list[0] & CR_ALT_SOURCE)
+ && !devpriv->ai_calib_source_enabled) {
+ /* Strobe Relay enable bit */
+ ni_writew(dev, devpriv->ai_calib_source |
+ Calibration_Channel_6143_RelayOn,
+ Calibration_Channel_6143);
+ ni_writew(dev, devpriv->ai_calib_source,
+ Calibration_Channel_6143);
+ devpriv->ai_calib_source_enabled = 1;
+ msleep_interruptible(100); /* Allow relays to change */
+ } else if (!(list[0] & CR_ALT_SOURCE)
+ && devpriv->ai_calib_source_enabled) {
+ /* Strobe Relay disable bit */
+ ni_writew(dev, devpriv->ai_calib_source |
+ Calibration_Channel_6143_RelayOff,
+ Calibration_Channel_6143);
+ ni_writew(dev, devpriv->ai_calib_source,
+ Calibration_Channel_6143);
+ devpriv->ai_calib_source_enabled = 0;
+ msleep_interruptible(100); /* Allow relays to change */
+ }
+ }
+
+ for (i = 0; i < n_chan; i++) {
+ if (!devpriv->is_6143 && (list[i] & CR_ALT_SOURCE))
+ chan = devpriv->ai_calib_source;
+ else
+ chan = CR_CHAN(list[i]);
+ aref = CR_AREF(list[i]);
+ range = CR_RANGE(list[i]);
+ dither = (list[i] & CR_ALT_FILTER) != 0;
+
+ /* fix the external/internal range differences */
+ range = ni_gainlkup[board->gainlkup][range];
+ if (devpriv->is_611x)
+ devpriv->ai_offset[i] = offset;
+ else
+ devpriv->ai_offset[i] = (range & 0x100) ? 0 : offset;
+
+ hi = 0;
+ if ((list[i] & CR_ALT_SOURCE)) {
+ if (devpriv->is_611x)
+ ni_writew(dev, CR_CHAN(list[i]) & 0x0003,
+ Calibration_Channel_Select_611x);
+ } else {
+ if (devpriv->is_611x)
+ aref = AREF_DIFF;
+ else if (devpriv->is_6143)
+ aref = AREF_OTHER;
+ switch (aref) {
+ case AREF_DIFF:
+ hi |= AI_DIFFERENTIAL;
+ break;
+ case AREF_COMMON:
+ hi |= AI_COMMON;
+ break;
+ case AREF_GROUND:
+ hi |= AI_GROUND;
+ break;
+ case AREF_OTHER:
+ break;
+ }
+ }
+ hi |= AI_CONFIG_CHANNEL(chan);
+
+ ni_writew(dev, hi, Configuration_Memory_High);
+
+ if (!devpriv->is_6143) {
+ lo = range;
+ if (i == n_chan - 1)
+ lo |= AI_LAST_CHANNEL;
+ if (dither)
+ lo |= AI_DITHER;
+
+ ni_writew(dev, lo, Configuration_Memory_Low);
+ }
+ }
+
+ /* prime the channel/gain list */
+ if (!devpriv->is_611x && !devpriv->is_6143)
+ ni_prime_channelgain_list(dev);
+}
+
+static int ni_ai_insn_read(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ struct ni_private *devpriv = dev->private;
+ unsigned int mask = (s->maxdata + 1) >> 1;
+ int i, n;
+ unsigned signbits;
+ unsigned int d;
+ unsigned long dl;
+
+ ni_load_channelgain_list(dev, s, 1, &insn->chanspec);
+
+ ni_clear_ai_fifo(dev);
+
+ signbits = devpriv->ai_offset[0];
+ if (devpriv->is_611x) {
+ for (n = 0; n < num_adc_stages_611x; n++) {
+ ni_stc_writew(dev, AI_CONVERT_Pulse,
+ AI_Command_1_Register);
+ udelay(1);
+ }
+ for (n = 0; n < insn->n; n++) {
+ ni_stc_writew(dev, AI_CONVERT_Pulse,
+ AI_Command_1_Register);
+ /* The 611x has screwy 32-bit FIFOs. */
+ d = 0;
+ for (i = 0; i < NI_TIMEOUT; i++) {
+ if (ni_readb(dev, XXX_Status) & 0x80) {
+ d = ni_readl(dev, ADC_FIFO_Data_611x);
+ d >>= 16;
+ d &= 0xffff;
+ break;
+ }
+ if (!(ni_stc_readw(dev, AI_Status_1_Register) &
+ AI_FIFO_Empty_St)) {
+ d = ni_readl(dev, ADC_FIFO_Data_611x);
+ d &= 0xffff;
+ break;
+ }
+ }
+ if (i == NI_TIMEOUT) {
+ dev_err(dev->class_dev, "timeout\n");
+ return -ETIME;
+ }
+ d += signbits;
+ data[n] = d;
+ }
+ } else if (devpriv->is_6143) {
+ for (n = 0; n < insn->n; n++) {
+ ni_stc_writew(dev, AI_CONVERT_Pulse,
+ AI_Command_1_Register);
+
+ /* The 6143 has 32-bit FIFOs. You need to strobe a bit to move a single 16bit stranded sample into the FIFO */
+ dl = 0;
+ for (i = 0; i < NI_TIMEOUT; i++) {
+ if (ni_readl(dev, AIFIFO_Status_6143) & 0x01) {
+ /* Get stranded sample into FIFO */
+ ni_writel(dev, 0x01,
+ AIFIFO_Control_6143);
+ dl = ni_readl(dev, AIFIFO_Data_6143);
+ break;
+ }
+ }
+ if (i == NI_TIMEOUT) {
+ dev_err(dev->class_dev, "timeout\n");
+ return -ETIME;
+ }
+ data[n] = (((dl >> 16) & 0xFFFF) + signbits) & 0xFFFF;
+ }
+ } else {
+ for (n = 0; n < insn->n; n++) {
+ ni_stc_writew(dev, AI_CONVERT_Pulse,
+ AI_Command_1_Register);
+ for (i = 0; i < NI_TIMEOUT; i++) {
+ if (!(ni_stc_readw(dev, AI_Status_1_Register) &
+ AI_FIFO_Empty_St))
+ break;
+ }
+ if (i == NI_TIMEOUT) {
+ dev_err(dev->class_dev, "timeout\n");
+ return -ETIME;
+ }
+ if (devpriv->is_m_series) {
+ dl = ni_readl(dev, M_Offset_AI_FIFO_Data);
+ dl &= mask;
+ data[n] = dl;
+ } else {
+ d = ni_readw(dev, ADC_FIFO_Data_Register);
+ d += signbits; /* subtle: needs to be short addition */
+ data[n] = d;
+ }
+ }
+ }
+ return insn->n;
+}
+
+static int ni_ns_to_timer(const struct comedi_device *dev, unsigned nanosec,
+ unsigned int flags)
+{
+ struct ni_private *devpriv = dev->private;
+ int divider;
+
+ switch (flags & CMDF_ROUND_MASK) {
+ case CMDF_ROUND_NEAREST:
+ default:
+ divider = (nanosec + devpriv->clock_ns / 2) / devpriv->clock_ns;
+ break;
+ case CMDF_ROUND_DOWN:
+ divider = (nanosec) / devpriv->clock_ns;
+ break;
+ case CMDF_ROUND_UP:
+ divider = (nanosec + devpriv->clock_ns - 1) / devpriv->clock_ns;
+ break;
+ }
+ return divider - 1;
+}
+
+static unsigned ni_timer_to_ns(const struct comedi_device *dev, int timer)
+{
+ struct ni_private *devpriv = dev->private;
+
+ return devpriv->clock_ns * (timer + 1);
+}
+
+static unsigned ni_min_ai_scan_period_ns(struct comedi_device *dev,
+ unsigned num_channels)
+{
+ const struct ni_board_struct *board = dev->board_ptr;
+ struct ni_private *devpriv = dev->private;
+
+ /* simultaneously-sampled inputs */
+ if (devpriv->is_611x || devpriv->is_6143)
+ return board->ai_speed;
+
+ /* multiplexed inputs */
+ return board->ai_speed * num_channels;
+}
+
+static int ni_ai_cmdtest(struct comedi_device *dev, struct comedi_subdevice *s,
+ struct comedi_cmd *cmd)
+{
+ const struct ni_board_struct *board = dev->board_ptr;
+ struct ni_private *devpriv = dev->private;
+ int err = 0;
+ unsigned int tmp;
+ unsigned int sources;
+
+ /* Step 1 : check if triggers are trivially valid */
+
+ err |= comedi_check_trigger_src(&cmd->start_src,
+ TRIG_NOW | TRIG_INT | TRIG_EXT);
+ err |= comedi_check_trigger_src(&cmd->scan_begin_src,
+ TRIG_TIMER | TRIG_EXT);
+
+ sources = TRIG_TIMER | TRIG_EXT;
+ if (devpriv->is_611x || devpriv->is_6143)
+ sources |= TRIG_NOW;
+ err |= comedi_check_trigger_src(&cmd->convert_src, sources);
+
+ err |= comedi_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
+ err |= comedi_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE);
+
+ if (err)
+ return 1;
+
+ /* Step 2a : make sure trigger sources are unique */
+
+ err |= comedi_check_trigger_is_unique(cmd->start_src);
+ err |= comedi_check_trigger_is_unique(cmd->scan_begin_src);
+ err |= comedi_check_trigger_is_unique(cmd->convert_src);
+ err |= comedi_check_trigger_is_unique(cmd->stop_src);
+
+ /* Step 2b : and mutually compatible */
+
+ if (err)
+ return 2;
+
+ /* Step 3: check if arguments are trivially valid */
+
+ switch (cmd->start_src) {
+ case TRIG_NOW:
+ case TRIG_INT:
+ err |= comedi_check_trigger_arg_is(&cmd->start_arg, 0);
+ break;
+ case TRIG_EXT:
+ tmp = CR_CHAN(cmd->start_arg);
+
+ if (tmp > 16)
+ tmp = 16;
+ tmp |= (cmd->start_arg & (CR_INVERT | CR_EDGE));
+ err |= comedi_check_trigger_arg_is(&cmd->start_arg, tmp);
+ break;
+ }
+
+ if (cmd->scan_begin_src == TRIG_TIMER) {
+ err |= comedi_check_trigger_arg_min(&cmd->scan_begin_arg,
+ ni_min_ai_scan_period_ns(dev, cmd->chanlist_len));
+ err |= comedi_check_trigger_arg_max(&cmd->scan_begin_arg,
+ devpriv->clock_ns *
+ 0xffffff);
+ } else if (cmd->scan_begin_src == TRIG_EXT) {
+ /* external trigger */
+ unsigned int tmp = CR_CHAN(cmd->scan_begin_arg);
+
+ if (tmp > 16)
+ tmp = 16;
+ tmp |= (cmd->scan_begin_arg & (CR_INVERT | CR_EDGE));
+ err |= comedi_check_trigger_arg_is(&cmd->scan_begin_arg, tmp);
+ } else { /* TRIG_OTHER */
+ err |= comedi_check_trigger_arg_is(&cmd->scan_begin_arg, 0);
+ }
+
+ if (cmd->convert_src == TRIG_TIMER) {
+ if (devpriv->is_611x || devpriv->is_6143) {
+ err |= comedi_check_trigger_arg_is(&cmd->convert_arg,
+ 0);
+ } else {
+ err |= comedi_check_trigger_arg_min(&cmd->convert_arg,
+ board->ai_speed);
+ err |= comedi_check_trigger_arg_max(&cmd->convert_arg,
+ devpriv->clock_ns *
+ 0xffff);
+ }
+ } else if (cmd->convert_src == TRIG_EXT) {
+ /* external trigger */
+ unsigned int tmp = CR_CHAN(cmd->convert_arg);
+
+ if (tmp > 16)
+ tmp = 16;
+ tmp |= (cmd->convert_arg & (CR_ALT_FILTER | CR_INVERT));
+ err |= comedi_check_trigger_arg_is(&cmd->convert_arg, tmp);
+ } else if (cmd->convert_src == TRIG_NOW) {
+ err |= comedi_check_trigger_arg_is(&cmd->convert_arg, 0);
+ }
+
+ err |= comedi_check_trigger_arg_is(&cmd->scan_end_arg,
+ cmd->chanlist_len);
+
+ if (cmd->stop_src == TRIG_COUNT) {
+ unsigned int max_count = 0x01000000;
+
+ if (devpriv->is_611x)
+ max_count -= num_adc_stages_611x;
+ err |= comedi_check_trigger_arg_max(&cmd->stop_arg, max_count);
+ err |= comedi_check_trigger_arg_min(&cmd->stop_arg, 1);
+ } else {
+ /* TRIG_NONE */
+ err |= comedi_check_trigger_arg_is(&cmd->stop_arg, 0);
+ }
+
+ if (err)
+ return 3;
+
+ /* step 4: fix up any arguments */
+
+ if (cmd->scan_begin_src == TRIG_TIMER) {
+ tmp = cmd->scan_begin_arg;
+ cmd->scan_begin_arg =
+ ni_timer_to_ns(dev, ni_ns_to_timer(dev,
+ cmd->scan_begin_arg,
+ cmd->flags));
+ if (tmp != cmd->scan_begin_arg)
+ err++;
+ }
+ if (cmd->convert_src == TRIG_TIMER) {
+ if (!devpriv->is_611x && !devpriv->is_6143) {
+ tmp = cmd->convert_arg;
+ cmd->convert_arg =
+ ni_timer_to_ns(dev, ni_ns_to_timer(dev,
+ cmd->convert_arg,
+ cmd->flags));
+ if (tmp != cmd->convert_arg)
+ err++;
+ if (cmd->scan_begin_src == TRIG_TIMER &&
+ cmd->scan_begin_arg <
+ cmd->convert_arg * cmd->scan_end_arg) {
+ cmd->scan_begin_arg =
+ cmd->convert_arg * cmd->scan_end_arg;
+ err++;
+ }
+ }
+ }
+
+ if (err)
+ return 4;
+
+ return 0;
+}
+
+static int ni_ai_inttrig(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ unsigned int trig_num)
+{
+ struct ni_private *devpriv = dev->private;
+ struct comedi_cmd *cmd = &s->async->cmd;
+
+ if (trig_num != cmd->start_arg)
+ return -EINVAL;
+
+ ni_stc_writew(dev, AI_START1_Pulse | devpriv->ai_cmd2,
+ AI_Command_2_Register);
+ s->async->inttrig = NULL;
+
+ return 1;
+}
+
+static int ni_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
+{
+ struct ni_private *devpriv = dev->private;
+ const struct comedi_cmd *cmd = &s->async->cmd;
+ int timer;
+ int mode1 = 0; /* mode1 is needed for both stop and convert */
+ int mode2 = 0;
+ int start_stop_select = 0;
+ unsigned int stop_count;
+ int interrupt_a_enable = 0;
+
+ if (dev->irq == 0) {
+ dev_err(dev->class_dev, "cannot run command without an irq\n");
+ return -EIO;
+ }
+ ni_clear_ai_fifo(dev);
+
+ ni_load_channelgain_list(dev, s, cmd->chanlist_len, cmd->chanlist);
+
+ /* start configuration */
+ ni_stc_writew(dev, AI_Configuration_Start, Joint_Reset_Register);
+
+ /* disable analog triggering for now, since it
+ * interferes with the use of pfi0 */
+ devpriv->an_trig_etc_reg &= ~Analog_Trigger_Enable;
+ ni_stc_writew(dev, devpriv->an_trig_etc_reg,
+ Analog_Trigger_Etc_Register);
+
+ switch (cmd->start_src) {
+ case TRIG_INT:
+ case TRIG_NOW:
+ ni_stc_writew(dev,
+ AI_START2_Select(0) |
+ AI_START1_Sync | AI_START1_Edge |
+ AI_START1_Select(0),
+ AI_Trigger_Select_Register);
+ break;
+ case TRIG_EXT:
+ {
+ int chan = CR_CHAN(cmd->start_arg);
+ unsigned int bits = AI_START2_Select(0) |
+ AI_START1_Sync | AI_START1_Select(chan + 1);
+
+ if (cmd->start_arg & CR_INVERT)
+ bits |= AI_START1_Polarity;
+ if (cmd->start_arg & CR_EDGE)
+ bits |= AI_START1_Edge;
+ ni_stc_writew(dev, bits, AI_Trigger_Select_Register);
+ break;
+ }
+ }
+
+ mode2 &= ~AI_Pre_Trigger;
+ mode2 &= ~AI_SC_Initial_Load_Source;
+ mode2 &= ~AI_SC_Reload_Mode;
+ ni_stc_writew(dev, mode2, AI_Mode_2_Register);
+
+ if (cmd->chanlist_len == 1 || devpriv->is_611x || devpriv->is_6143) {
+ start_stop_select |= AI_STOP_Polarity;
+ start_stop_select |= AI_STOP_Select(31); /* logic low */
+ start_stop_select |= AI_STOP_Sync;
+ } else {
+ start_stop_select |= AI_STOP_Select(19); /* ai configuration memory */
+ }
+ ni_stc_writew(dev, start_stop_select, AI_START_STOP_Select_Register);
+
+ devpriv->ai_cmd2 = 0;
+ switch (cmd->stop_src) {
+ case TRIG_COUNT:
+ stop_count = cmd->stop_arg - 1;
+
+ if (devpriv->is_611x) {
+ /* have to take 3 stage adc pipeline into account */
+ stop_count += num_adc_stages_611x;
+ }
+ /* stage number of scans */
+ ni_stc_writel(dev, stop_count, AI_SC_Load_A_Registers);
+
+ mode1 |= AI_Start_Stop | AI_Mode_1_Reserved | AI_Trigger_Once;
+ ni_stc_writew(dev, mode1, AI_Mode_1_Register);
+ /* load SC (Scan Count) */
+ ni_stc_writew(dev, AI_SC_Load, AI_Command_1_Register);
+
+ if (stop_count == 0) {
+ devpriv->ai_cmd2 |= AI_End_On_End_Of_Scan;
+ interrupt_a_enable |= AI_STOP_Interrupt_Enable;
+ /* this is required to get the last sample for chanlist_len > 1, not sure why */
+ if (cmd->chanlist_len > 1)
+ start_stop_select |=
+ AI_STOP_Polarity | AI_STOP_Edge;
+ }
+ break;
+ case TRIG_NONE:
+ /* stage number of scans */
+ ni_stc_writel(dev, 0, AI_SC_Load_A_Registers);
+
+ mode1 |= AI_Start_Stop | AI_Mode_1_Reserved | AI_Continuous;
+ ni_stc_writew(dev, mode1, AI_Mode_1_Register);
+
+ /* load SC (Scan Count) */
+ ni_stc_writew(dev, AI_SC_Load, AI_Command_1_Register);
+ break;
+ }
+
+ switch (cmd->scan_begin_src) {
+ case TRIG_TIMER:
+ /*
+ stop bits for non 611x boards
+ AI_SI_Special_Trigger_Delay=0
+ AI_Pre_Trigger=0
+ AI_START_STOP_Select_Register:
+ AI_START_Polarity=0 (?) rising edge
+ AI_START_Edge=1 edge triggered
+ AI_START_Sync=1 (?)
+ AI_START_Select=0 SI_TC
+ AI_STOP_Polarity=0 rising edge
+ AI_STOP_Edge=0 level
+ AI_STOP_Sync=1
+ AI_STOP_Select=19 external pin (configuration mem)
+ */
+ start_stop_select |= AI_START_Edge | AI_START_Sync;
+ ni_stc_writew(dev, start_stop_select,
+ AI_START_STOP_Select_Register);
+
+ mode2 |= AI_SI_Reload_Mode(0);
+ /* AI_SI_Initial_Load_Source=A */
+ mode2 &= ~AI_SI_Initial_Load_Source;
+ /* mode2 |= AI_SC_Reload_Mode; */
+ ni_stc_writew(dev, mode2, AI_Mode_2_Register);
+
+ /* load SI */
+ timer = ni_ns_to_timer(dev, cmd->scan_begin_arg,
+ CMDF_ROUND_NEAREST);
+ ni_stc_writel(dev, timer, AI_SI_Load_A_Registers);
+ ni_stc_writew(dev, AI_SI_Load, AI_Command_1_Register);
+ break;
+ case TRIG_EXT:
+ if (cmd->scan_begin_arg & CR_EDGE)
+ start_stop_select |= AI_START_Edge;
+ /* AI_START_Polarity==1 is falling edge */
+ if (cmd->scan_begin_arg & CR_INVERT)
+ start_stop_select |= AI_START_Polarity;
+ if (cmd->scan_begin_src != cmd->convert_src ||
+ (cmd->scan_begin_arg & ~CR_EDGE) !=
+ (cmd->convert_arg & ~CR_EDGE))
+ start_stop_select |= AI_START_Sync;
+ start_stop_select |=
+ AI_START_Select(1 + CR_CHAN(cmd->scan_begin_arg));
+ ni_stc_writew(dev, start_stop_select,
+ AI_START_STOP_Select_Register);
+ break;
+ }
+
+ switch (cmd->convert_src) {
+ case TRIG_TIMER:
+ case TRIG_NOW:
+ if (cmd->convert_arg == 0 || cmd->convert_src == TRIG_NOW)
+ timer = 1;
+ else
+ timer = ni_ns_to_timer(dev, cmd->convert_arg,
+ CMDF_ROUND_NEAREST);
+ /* 0,0 does not work */
+ ni_stc_writew(dev, 1, AI_SI2_Load_A_Register);
+ ni_stc_writew(dev, timer, AI_SI2_Load_B_Register);
+
+ /* AI_SI2_Reload_Mode = alternate */
+ /* AI_SI2_Initial_Load_Source = A */
+ mode2 &= ~AI_SI2_Initial_Load_Source;
+ mode2 |= AI_SI2_Reload_Mode;
+ ni_stc_writew(dev, mode2, AI_Mode_2_Register);
+
+ /* AI_SI2_Load */
+ ni_stc_writew(dev, AI_SI2_Load, AI_Command_1_Register);
+
+ mode2 |= AI_SI2_Reload_Mode; /* alternate */
+ mode2 |= AI_SI2_Initial_Load_Source; /* B */
+
+ ni_stc_writew(dev, mode2, AI_Mode_2_Register);
+ break;
+ case TRIG_EXT:
+ mode1 |= AI_CONVERT_Source_Select(1 + cmd->convert_arg);
+ if ((cmd->convert_arg & CR_INVERT) == 0)
+ mode1 |= AI_CONVERT_Source_Polarity;
+ ni_stc_writew(dev, mode1, AI_Mode_1_Register);
+
+ mode2 |= AI_Start_Stop_Gate_Enable | AI_SC_Gate_Enable;
+ ni_stc_writew(dev, mode2, AI_Mode_2_Register);
+
+ break;
+ }
+
+ if (dev->irq) {
+ /* interrupt on FIFO, errors, SC_TC */
+ interrupt_a_enable |= AI_Error_Interrupt_Enable |
+ AI_SC_TC_Interrupt_Enable;
+
+#ifndef PCIDMA
+ interrupt_a_enable |= AI_FIFO_Interrupt_Enable;
+#endif
+
+ if (cmd->flags & CMDF_WAKE_EOS
+ || (devpriv->ai_cmd2 & AI_End_On_End_Of_Scan)) {
+ /* wake on end-of-scan */
+ devpriv->aimode = AIMODE_SCAN;
+ } else {
+ devpriv->aimode = AIMODE_HALF_FULL;
+ }
+
+ switch (devpriv->aimode) {
+ case AIMODE_HALF_FULL:
+ /*generate FIFO interrupts and DMA requests on half-full */
+#ifdef PCIDMA
+ ni_stc_writew(dev, AI_FIFO_Mode_HF_to_E,
+ AI_Mode_3_Register);
+#else
+ ni_stc_writew(dev, AI_FIFO_Mode_HF,
+ AI_Mode_3_Register);
+#endif
+ break;
+ case AIMODE_SAMPLE:
+ /*generate FIFO interrupts on non-empty */
+ ni_stc_writew(dev, AI_FIFO_Mode_NE,
+ AI_Mode_3_Register);
+ break;
+ case AIMODE_SCAN:
+#ifdef PCIDMA
+ ni_stc_writew(dev, AI_FIFO_Mode_NE,
+ AI_Mode_3_Register);
+#else
+ ni_stc_writew(dev, AI_FIFO_Mode_HF,
+ AI_Mode_3_Register);
+#endif
+ interrupt_a_enable |= AI_STOP_Interrupt_Enable;
+ break;
+ default:
+ break;
+ }
+
+ /* clear interrupts */
+ ni_stc_writew(dev,
+ AI_Error_Interrupt_Ack |
+ AI_STOP_Interrupt_Ack |
+ AI_START_Interrupt_Ack |
+ AI_START2_Interrupt_Ack |
+ AI_START1_Interrupt_Ack |
+ AI_SC_TC_Interrupt_Ack |
+ AI_SC_TC_Error_Confirm,
+ Interrupt_A_Ack_Register);
+
+ ni_set_bits(dev, Interrupt_A_Enable_Register,
+ interrupt_a_enable, 1);
+ } else {
+ /* interrupt on nothing */
+ ni_set_bits(dev, Interrupt_A_Enable_Register, ~0, 0);
+
+ /* XXX start polling if necessary */
+ }
+
+ /* end configuration */
+ ni_stc_writew(dev, AI_Configuration_End, Joint_Reset_Register);
+
+ switch (cmd->scan_begin_src) {
+ case TRIG_TIMER:
+ ni_stc_writew(dev,
+ AI_SI2_Arm | AI_SI_Arm | AI_DIV_Arm | AI_SC_Arm,
+ AI_Command_1_Register);
+ break;
+ case TRIG_EXT:
+ /* XXX AI_SI_Arm? */
+ ni_stc_writew(dev,
+ AI_SI2_Arm | AI_SI_Arm | AI_DIV_Arm | AI_SC_Arm,
+ AI_Command_1_Register);
+ break;
+ }
+
+#ifdef PCIDMA
+ {
+ int retval = ni_ai_setup_MITE_dma(dev);
+
+ if (retval)
+ return retval;
+ }
+#endif
+
+ if (cmd->start_src == TRIG_NOW) {
+ /* AI_START1_Pulse */
+ ni_stc_writew(dev, AI_START1_Pulse | devpriv->ai_cmd2,
+ AI_Command_2_Register);
+ s->async->inttrig = NULL;
+ } else if (cmd->start_src == TRIG_EXT) {
+ s->async->inttrig = NULL;
+ } else { /* TRIG_INT */
+ s->async->inttrig = ni_ai_inttrig;
+ }
+
+ return 0;
+}
+
+static int ni_ai_insn_config(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn, unsigned int *data)
+{
+ struct ni_private *devpriv = dev->private;
+
+ if (insn->n < 1)
+ return -EINVAL;
+
+ switch (data[0]) {
+ case INSN_CONFIG_ALT_SOURCE:
+ if (devpriv->is_m_series) {
+ if (data[1] & ~(MSeries_AI_Bypass_Cal_Sel_Pos_Mask |
+ MSeries_AI_Bypass_Cal_Sel_Neg_Mask |
+ MSeries_AI_Bypass_Mode_Mux_Mask |
+ MSeries_AO_Bypass_AO_Cal_Sel_Mask)) {
+ return -EINVAL;
+ }
+ devpriv->ai_calib_source = data[1];
+ } else if (devpriv->is_6143) {
+ unsigned int calib_source;
+
+ calib_source = data[1] & 0xf;
+
+ devpriv->ai_calib_source = calib_source;
+ ni_writew(dev, calib_source, Calibration_Channel_6143);
+ } else {
+ unsigned int calib_source;
+ unsigned int calib_source_adjust;
+
+ calib_source = data[1] & 0xf;
+ calib_source_adjust = (data[1] >> 4) & 0xff;
+
+ if (calib_source >= 8)
+ return -EINVAL;
+ devpriv->ai_calib_source = calib_source;
+ if (devpriv->is_611x) {
+ ni_writeb(dev, calib_source_adjust,
+ Cal_Gain_Select_611x);
+ }
+ }
+ return 2;
+ default:
+ break;
+ }
+
+ return -EINVAL;
+}
+
+static void ni_ao_munge(struct comedi_device *dev, struct comedi_subdevice *s,
+ void *data, unsigned int num_bytes,
+ unsigned int chan_index)
+{
+ struct comedi_cmd *cmd = &s->async->cmd;
+ unsigned int nsamples = comedi_bytes_to_samples(s, num_bytes);
+ unsigned short *array = data;
+ unsigned int i;
+
+ for (i = 0; i < nsamples; i++) {
+ unsigned int range = CR_RANGE(cmd->chanlist[chan_index]);
+ unsigned short val = array[i];
+
+ /*
+ * Munge data from unsigned to two's complement for
+ * bipolar ranges.
+ */
+ if (comedi_range_is_bipolar(s, range))
+ val = comedi_offset_munge(s, val);
+#ifdef PCIDMA
+ val = cpu_to_le16(val);
+#endif
+ array[i] = val;
+
+ chan_index++;
+ chan_index %= cmd->chanlist_len;
+ }
+}
+
+static int ni_m_series_ao_config_chanlist(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ unsigned int chanspec[],
+ unsigned int n_chans, int timed)
+{
+ struct ni_private *devpriv = dev->private;
+ unsigned int range;
+ unsigned int chan;
+ unsigned int conf;
+ int i;
+ int invert = 0;
+
+ if (timed) {
+ for (i = 0; i < s->n_chan; ++i) {
+ devpriv->ao_conf[i] &= ~MSeries_AO_Update_Timed_Bit;
+ ni_writeb(dev, devpriv->ao_conf[i],
+ M_Offset_AO_Config_Bank(i));
+ ni_writeb(dev, 0xf, M_Offset_AO_Waveform_Order(i));
+ }
+ }
+ for (i = 0; i < n_chans; i++) {
+ const struct comedi_krange *krange;
+
+ chan = CR_CHAN(chanspec[i]);
+ range = CR_RANGE(chanspec[i]);
+ krange = s->range_table->range + range;
+ invert = 0;
+ conf = 0;
+ switch (krange->max - krange->min) {
+ case 20000000:
+ conf |= MSeries_AO_DAC_Reference_10V_Internal_Bits;
+ ni_writeb(dev, 0,
+ M_Offset_AO_Reference_Attenuation(chan));
+ break;
+ case 10000000:
+ conf |= MSeries_AO_DAC_Reference_5V_Internal_Bits;
+ ni_writeb(dev, 0,
+ M_Offset_AO_Reference_Attenuation(chan));
+ break;
+ case 4000000:
+ conf |= MSeries_AO_DAC_Reference_10V_Internal_Bits;
+ ni_writeb(dev, MSeries_Attenuate_x5_Bit,
+ M_Offset_AO_Reference_Attenuation(chan));
+ break;
+ case 2000000:
+ conf |= MSeries_AO_DAC_Reference_5V_Internal_Bits;
+ ni_writeb(dev, MSeries_Attenuate_x5_Bit,
+ M_Offset_AO_Reference_Attenuation(chan));
+ break;
+ default:
+ dev_err(dev->class_dev,
+ "bug! unhandled ao reference voltage\n");
+ break;
+ }
+ switch (krange->max + krange->min) {
+ case 0:
+ conf |= MSeries_AO_DAC_Offset_0V_Bits;
+ break;
+ case 10000000:
+ conf |= MSeries_AO_DAC_Offset_5V_Bits;
+ break;
+ default:
+ dev_err(dev->class_dev,
+ "bug! unhandled ao offset voltage\n");
+ break;
+ }
+ if (timed)
+ conf |= MSeries_AO_Update_Timed_Bit;
+ ni_writeb(dev, conf, M_Offset_AO_Config_Bank(chan));
+ devpriv->ao_conf[chan] = conf;
+ ni_writeb(dev, i, M_Offset_AO_Waveform_Order(chan));
+ }
+ return invert;
+}
+
+static int ni_old_ao_config_chanlist(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ unsigned int chanspec[],
+ unsigned int n_chans)
+{
+ struct ni_private *devpriv = dev->private;
+ unsigned int range;
+ unsigned int chan;
+ unsigned int conf;
+ int i;
+ int invert = 0;
+
+ for (i = 0; i < n_chans; i++) {
+ chan = CR_CHAN(chanspec[i]);
+ range = CR_RANGE(chanspec[i]);
+ conf = AO_Channel(chan);
+
+ if (comedi_range_is_bipolar(s, range)) {
+ conf |= AO_Bipolar;
+ invert = (s->maxdata + 1) >> 1;
+ } else {
+ invert = 0;
+ }
+ if (comedi_range_is_external(s, range))
+ conf |= AO_Ext_Ref;
+
+ /* not all boards can deglitch, but this shouldn't hurt */
+ if (chanspec[i] & CR_DEGLITCH)
+ conf |= AO_Deglitch;
+
+ /* analog reference */
+ /* AREF_OTHER connects AO ground to AI ground, i think */
+ conf |= (CR_AREF(chanspec[i]) ==
+ AREF_OTHER) ? AO_Ground_Ref : 0;
+
+ ni_writew(dev, conf, AO_Configuration);
+ devpriv->ao_conf[chan] = conf;
+ }
+ return invert;
+}
+
+static int ni_ao_config_chanlist(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ unsigned int chanspec[], unsigned int n_chans,
+ int timed)
+{
+ struct ni_private *devpriv = dev->private;
+
+ if (devpriv->is_m_series)
+ return ni_m_series_ao_config_chanlist(dev, s, chanspec, n_chans,
+ timed);
+ else
+ return ni_old_ao_config_chanlist(dev, s, chanspec, n_chans);
+}
+
+static int ni_ao_insn_write(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ struct ni_private *devpriv = dev->private;
+ unsigned int chan = CR_CHAN(insn->chanspec);
+ unsigned int range = CR_RANGE(insn->chanspec);
+ int reg;
+ int i;
+
+ if (devpriv->is_6xxx) {
+ ni_ao_win_outw(dev, 1 << chan, AO_Immediate_671x);
+
+ reg = DACx_Direct_Data_671x(chan);
+ } else if (devpriv->is_m_series) {
+ reg = M_Offset_DAC_Direct_Data(chan);
+ } else {
+ reg = (chan) ? DAC1_Direct_Data : DAC0_Direct_Data;
+ }
+
+ ni_ao_config_chanlist(dev, s, &insn->chanspec, 1, 0);
+
+ for (i = 0; i < insn->n; i++) {
+ unsigned int val = data[i];
+
+ s->readback[chan] = val;
+
+ if (devpriv->is_6xxx) {
+ /*
+ * 6xxx boards have bipolar outputs, munge the
+ * unsigned comedi values to 2's complement
+ */
+ val = comedi_offset_munge(s, val);
+
+ ni_ao_win_outw(dev, val, reg);
+ } else if (devpriv->is_m_series) {
+ /*
+ * M-series boards use offset binary values for
+ * bipolar and uinpolar outputs
+ */
+ ni_writew(dev, val, reg);
+ } else {
+ /*
+ * Non-M series boards need two's complement values
+ * for bipolar ranges.
+ */
+ if (comedi_range_is_bipolar(s, range))
+ val = comedi_offset_munge(s, val);
+
+ ni_writew(dev, val, reg);
+ }
+ }
+
+ return insn->n;
+}
+
+static int ni_ao_insn_config(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn, unsigned int *data)
+{
+ const struct ni_board_struct *board = dev->board_ptr;
+ struct ni_private *devpriv = dev->private;
+ unsigned int nbytes;
+
+ switch (data[0]) {
+ case INSN_CONFIG_GET_HARDWARE_BUFFER_SIZE:
+ switch (data[1]) {
+ case COMEDI_OUTPUT:
+ nbytes = comedi_samples_to_bytes(s,
+ board->ao_fifo_depth);
+ data[2] = 1 + nbytes;
+ if (devpriv->mite)
+ data[2] += devpriv->mite->fifo_size;
+ break;
+ case COMEDI_INPUT:
+ data[2] = 0;
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+ default:
+ break;
+ }
+
+ return -EINVAL;
+}
+
+static int ni_ao_inttrig(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ unsigned int trig_num)
+{
+ struct ni_private *devpriv = dev->private;
+ struct comedi_cmd *cmd = &s->async->cmd;
+ int ret;
+ int interrupt_b_bits;
+ int i;
+ static const int timeout = 1000;
+
+ if (trig_num != cmd->start_arg)
+ return -EINVAL;
+
+ /* Null trig at beginning prevent ao start trigger from executing more than
+ once per command (and doing things like trying to allocate the ao dma channel
+ multiple times) */
+ s->async->inttrig = NULL;
+
+ ni_set_bits(dev, Interrupt_B_Enable_Register,
+ AO_FIFO_Interrupt_Enable | AO_Error_Interrupt_Enable, 0);
+ interrupt_b_bits = AO_Error_Interrupt_Enable;
+#ifdef PCIDMA
+ ni_stc_writew(dev, 1, DAC_FIFO_Clear);
+ if (devpriv->is_6xxx)
+ ni_ao_win_outl(dev, 0x6, AO_FIFO_Offset_Load_611x);
+ ret = ni_ao_setup_MITE_dma(dev);
+ if (ret)
+ return ret;
+ ret = ni_ao_wait_for_dma_load(dev);
+ if (ret < 0)
+ return ret;
+#else
+ ret = ni_ao_prep_fifo(dev, s);
+ if (ret == 0)
+ return -EPIPE;
+
+ interrupt_b_bits |= AO_FIFO_Interrupt_Enable;
+#endif
+
+ ni_stc_writew(dev, devpriv->ao_mode3 | AO_Not_An_UPDATE,
+ AO_Mode_3_Register);
+ ni_stc_writew(dev, devpriv->ao_mode3, AO_Mode_3_Register);
+ /* wait for DACs to be loaded */
+ for (i = 0; i < timeout; i++) {
+ udelay(1);
+ if ((ni_stc_readw(dev, Joint_Status_2_Register) &
+ AO_TMRDACWRs_In_Progress_St) == 0)
+ break;
+ }
+ if (i == timeout) {
+ dev_err(dev->class_dev,
+ "timed out waiting for AO_TMRDACWRs_In_Progress_St to clear\n");
+ return -EIO;
+ }
+ /*
+ * stc manual says we are need to clear error interrupt after
+ * AO_TMRDACWRs_In_Progress_St clears
+ */
+ ni_stc_writew(dev, AO_Error_Interrupt_Ack, Interrupt_B_Ack_Register);
+
+ ni_set_bits(dev, Interrupt_B_Enable_Register, interrupt_b_bits, 1);
+
+ ni_stc_writew(dev, devpriv->ao_cmd1 |
+ AO_UI_Arm | AO_UC_Arm | AO_BC_Arm |
+ AO_DAC1_Update_Mode | AO_DAC0_Update_Mode,
+ AO_Command_1_Register);
+
+ ni_stc_writew(dev, devpriv->ao_cmd2 | AO_START1_Pulse,
+ AO_Command_2_Register);
+
+ return 0;
+}
+
+static int ni_ao_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
+{
+ const struct ni_board_struct *board = dev->board_ptr;
+ struct ni_private *devpriv = dev->private;
+ const struct comedi_cmd *cmd = &s->async->cmd;
+ int bits;
+ int i;
+ unsigned trigvar;
+
+ if (dev->irq == 0) {
+ dev_err(dev->class_dev, "cannot run command without an irq\n");
+ return -EIO;
+ }
+
+ ni_stc_writew(dev, AO_Configuration_Start, Joint_Reset_Register);
+
+ ni_stc_writew(dev, AO_Disarm, AO_Command_1_Register);
+
+ if (devpriv->is_6xxx) {
+ ni_ao_win_outw(dev, CLEAR_WG, AO_Misc_611x);
+
+ bits = 0;
+ for (i = 0; i < cmd->chanlist_len; i++) {
+ int chan;
+
+ chan = CR_CHAN(cmd->chanlist[i]);
+ bits |= 1 << chan;
+ ni_ao_win_outw(dev, chan, AO_Waveform_Generation_611x);
+ }
+ ni_ao_win_outw(dev, bits, AO_Timed_611x);
+ }
+
+ ni_ao_config_chanlist(dev, s, cmd->chanlist, cmd->chanlist_len, 1);
+
+ if (cmd->stop_src == TRIG_NONE) {
+ devpriv->ao_mode1 |= AO_Continuous;
+ devpriv->ao_mode1 &= ~AO_Trigger_Once;
+ } else {
+ devpriv->ao_mode1 &= ~AO_Continuous;
+ devpriv->ao_mode1 |= AO_Trigger_Once;
+ }
+ ni_stc_writew(dev, devpriv->ao_mode1, AO_Mode_1_Register);
+ switch (cmd->start_src) {
+ case TRIG_INT:
+ case TRIG_NOW:
+ devpriv->ao_trigger_select &=
+ ~(AO_START1_Polarity | AO_START1_Select(-1));
+ devpriv->ao_trigger_select |= AO_START1_Edge | AO_START1_Sync;
+ ni_stc_writew(dev, devpriv->ao_trigger_select,
+ AO_Trigger_Select_Register);
+ break;
+ case TRIG_EXT:
+ devpriv->ao_trigger_select =
+ AO_START1_Select(CR_CHAN(cmd->start_arg) + 1);
+ if (cmd->start_arg & CR_INVERT)
+ devpriv->ao_trigger_select |= AO_START1_Polarity; /* 0=active high, 1=active low. see daq-stc 3-24 (p186) */
+ if (cmd->start_arg & CR_EDGE)
+ devpriv->ao_trigger_select |= AO_START1_Edge; /* 0=edge detection disabled, 1=enabled */
+ ni_stc_writew(dev, devpriv->ao_trigger_select,
+ AO_Trigger_Select_Register);
+ break;
+ default:
+ BUG();
+ break;
+ }
+ devpriv->ao_mode3 &= ~AO_Trigger_Length;
+ ni_stc_writew(dev, devpriv->ao_mode3, AO_Mode_3_Register);
+
+ ni_stc_writew(dev, devpriv->ao_mode1, AO_Mode_1_Register);
+ devpriv->ao_mode2 &= ~AO_BC_Initial_Load_Source;
+ ni_stc_writew(dev, devpriv->ao_mode2, AO_Mode_2_Register);
+ if (cmd->stop_src == TRIG_NONE)
+ ni_stc_writel(dev, 0xffffff, AO_BC_Load_A_Register);
+ else
+ ni_stc_writel(dev, 0, AO_BC_Load_A_Register);
+ ni_stc_writew(dev, AO_BC_Load, AO_Command_1_Register);
+ devpriv->ao_mode2 &= ~AO_UC_Initial_Load_Source;
+ ni_stc_writew(dev, devpriv->ao_mode2, AO_Mode_2_Register);
+ switch (cmd->stop_src) {
+ case TRIG_COUNT:
+ if (devpriv->is_m_series) {
+ /* this is how the NI example code does it for m-series boards, verified correct with 6259 */
+ ni_stc_writel(dev, cmd->stop_arg - 1,
+ AO_UC_Load_A_Register);
+ ni_stc_writew(dev, AO_UC_Load, AO_Command_1_Register);
+ } else {
+ ni_stc_writel(dev, cmd->stop_arg,
+ AO_UC_Load_A_Register);
+ ni_stc_writew(dev, AO_UC_Load, AO_Command_1_Register);
+ ni_stc_writel(dev, cmd->stop_arg - 1,
+ AO_UC_Load_A_Register);
+ }
+ break;
+ case TRIG_NONE:
+ ni_stc_writel(dev, 0xffffff, AO_UC_Load_A_Register);
+ ni_stc_writew(dev, AO_UC_Load, AO_Command_1_Register);
+ ni_stc_writel(dev, 0xffffff, AO_UC_Load_A_Register);
+ break;
+ default:
+ ni_stc_writel(dev, 0, AO_UC_Load_A_Register);
+ ni_stc_writew(dev, AO_UC_Load, AO_Command_1_Register);
+ ni_stc_writel(dev, cmd->stop_arg, AO_UC_Load_A_Register);
+ }
+
+ devpriv->ao_mode1 &=
+ ~(AO_UI_Source_Select(0x1f) | AO_UI_Source_Polarity |
+ AO_UPDATE_Source_Select(0x1f) | AO_UPDATE_Source_Polarity);
+ switch (cmd->scan_begin_src) {
+ case TRIG_TIMER:
+ devpriv->ao_cmd2 &= ~AO_BC_Gate_Enable;
+ trigvar =
+ ni_ns_to_timer(dev, cmd->scan_begin_arg,
+ CMDF_ROUND_NEAREST);
+ ni_stc_writel(dev, 1, AO_UI_Load_A_Register);
+ ni_stc_writew(dev, AO_UI_Load, AO_Command_1_Register);
+ ni_stc_writel(dev, trigvar, AO_UI_Load_A_Register);
+ break;
+ case TRIG_EXT:
+ devpriv->ao_mode1 |=
+ AO_UPDATE_Source_Select(cmd->scan_begin_arg);
+ if (cmd->scan_begin_arg & CR_INVERT)
+ devpriv->ao_mode1 |= AO_UPDATE_Source_Polarity;
+ devpriv->ao_cmd2 |= AO_BC_Gate_Enable;
+ break;
+ default:
+ BUG();
+ break;
+ }
+ ni_stc_writew(dev, devpriv->ao_cmd2, AO_Command_2_Register);
+ ni_stc_writew(dev, devpriv->ao_mode1, AO_Mode_1_Register);
+ devpriv->ao_mode2 &=
+ ~(AO_UI_Reload_Mode(3) | AO_UI_Initial_Load_Source);
+ ni_stc_writew(dev, devpriv->ao_mode2, AO_Mode_2_Register);
+
+ if (cmd->scan_end_arg > 1) {
+ devpriv->ao_mode1 |= AO_Multiple_Channels;
+ ni_stc_writew(dev,
+ AO_Number_Of_Channels(cmd->scan_end_arg - 1) |
+ AO_UPDATE_Output_Select(AO_Update_Output_High_Z),
+ AO_Output_Control_Register);
+ } else {
+ unsigned bits;
+
+ devpriv->ao_mode1 &= ~AO_Multiple_Channels;
+ bits = AO_UPDATE_Output_Select(AO_Update_Output_High_Z);
+ if (devpriv->is_m_series || devpriv->is_6xxx) {
+ bits |= AO_Number_Of_Channels(0);
+ } else {
+ bits |=
+ AO_Number_Of_Channels(CR_CHAN(cmd->chanlist[0]));
+ }
+ ni_stc_writew(dev, bits, AO_Output_Control_Register);
+ }
+ ni_stc_writew(dev, devpriv->ao_mode1, AO_Mode_1_Register);
+
+ ni_stc_writew(dev, AO_DAC0_Update_Mode | AO_DAC1_Update_Mode,
+ AO_Command_1_Register);
+
+ devpriv->ao_mode3 |= AO_Stop_On_Overrun_Error;
+ ni_stc_writew(dev, devpriv->ao_mode3, AO_Mode_3_Register);
+
+ devpriv->ao_mode2 &= ~AO_FIFO_Mode_Mask;
+#ifdef PCIDMA
+ devpriv->ao_mode2 |= AO_FIFO_Mode_HF_to_F;
+#else
+ devpriv->ao_mode2 |= AO_FIFO_Mode_HF;
+#endif
+ devpriv->ao_mode2 &= ~AO_FIFO_Retransmit_Enable;
+ ni_stc_writew(dev, devpriv->ao_mode2, AO_Mode_2_Register);
+
+ bits = AO_BC_Source_Select | AO_UPDATE_Pulse_Width |
+ AO_TMRDACWR_Pulse_Width;
+ if (board->ao_fifo_depth)
+ bits |= AO_FIFO_Enable;
+ else
+ bits |= AO_DMA_PIO_Control;
+#if 0
+ /* F Hess: windows driver does not set AO_Number_Of_DAC_Packages bit for 6281,
+ verified with bus analyzer. */
+ if (devpriv->is_m_series)
+ bits |= AO_Number_Of_DAC_Packages;
+#endif
+ ni_stc_writew(dev, bits, AO_Personal_Register);
+ /* enable sending of ao dma requests */
+ ni_stc_writew(dev, AO_AOFREQ_Enable, AO_Start_Select_Register);
+
+ ni_stc_writew(dev, AO_Configuration_End, Joint_Reset_Register);
+
+ if (cmd->stop_src == TRIG_COUNT) {
+ ni_stc_writew(dev, AO_BC_TC_Interrupt_Ack,
+ Interrupt_B_Ack_Register);
+ ni_set_bits(dev, Interrupt_B_Enable_Register,
+ AO_BC_TC_Interrupt_Enable, 1);
+ }
+
+ s->async->inttrig = ni_ao_inttrig;
+
+ return 0;
+}
+
+static int ni_ao_cmdtest(struct comedi_device *dev, struct comedi_subdevice *s,
+ struct comedi_cmd *cmd)
+{
+ const struct ni_board_struct *board = dev->board_ptr;
+ struct ni_private *devpriv = dev->private;
+ int err = 0;
+ unsigned int tmp;
+
+ /* Step 1 : check if triggers are trivially valid */
+
+ err |= comedi_check_trigger_src(&cmd->start_src, TRIG_INT | TRIG_EXT);
+ err |= comedi_check_trigger_src(&cmd->scan_begin_src,
+ TRIG_TIMER | TRIG_EXT);
+ err |= comedi_check_trigger_src(&cmd->convert_src, TRIG_NOW);
+ err |= comedi_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
+ err |= comedi_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE);
+
+ if (err)
+ return 1;
+
+ /* Step 2a : make sure trigger sources are unique */
+
+ err |= comedi_check_trigger_is_unique(cmd->start_src);
+ err |= comedi_check_trigger_is_unique(cmd->scan_begin_src);
+ err |= comedi_check_trigger_is_unique(cmd->stop_src);
+
+ /* Step 2b : and mutually compatible */
+
+ if (err)
+ return 2;
+
+ /* Step 3: check if arguments are trivially valid */
+
+ switch (cmd->start_src) {
+ case TRIG_INT:
+ err |= comedi_check_trigger_arg_is(&cmd->start_arg, 0);
+ break;
+ case TRIG_EXT:
+ tmp = CR_CHAN(cmd->start_arg);
+
+ if (tmp > 18)
+ tmp = 18;
+ tmp |= (cmd->start_arg & (CR_INVERT | CR_EDGE));
+ err |= comedi_check_trigger_arg_is(&cmd->start_arg, tmp);
+ break;
+ }
+
+ if (cmd->scan_begin_src == TRIG_TIMER) {
+ err |= comedi_check_trigger_arg_min(&cmd->scan_begin_arg,
+ board->ao_speed);
+ err |= comedi_check_trigger_arg_max(&cmd->scan_begin_arg,
+ devpriv->clock_ns *
+ 0xffffff);
+ }
+
+ err |= comedi_check_trigger_arg_is(&cmd->convert_arg, 0);
+ err |= comedi_check_trigger_arg_is(&cmd->scan_end_arg,
+ cmd->chanlist_len);
+
+ if (cmd->stop_src == TRIG_COUNT)
+ err |= comedi_check_trigger_arg_max(&cmd->stop_arg, 0x00ffffff);
+ else /* TRIG_NONE */
+ err |= comedi_check_trigger_arg_is(&cmd->stop_arg, 0);
+
+ if (err)
+ return 3;
+
+ /* step 4: fix up any arguments */
+ if (cmd->scan_begin_src == TRIG_TIMER) {
+ tmp = cmd->scan_begin_arg;
+ cmd->scan_begin_arg =
+ ni_timer_to_ns(dev, ni_ns_to_timer(dev,
+ cmd->scan_begin_arg,
+ cmd->flags));
+ if (tmp != cmd->scan_begin_arg)
+ err++;
+ }
+ if (err)
+ return 4;
+
+ return 0;
+}
+
+static int ni_ao_reset(struct comedi_device *dev, struct comedi_subdevice *s)
+{
+ struct ni_private *devpriv = dev->private;
+
+ ni_release_ao_mite_channel(dev);
+
+ ni_stc_writew(dev, AO_Configuration_Start, Joint_Reset_Register);
+ ni_stc_writew(dev, AO_Disarm, AO_Command_1_Register);
+ ni_set_bits(dev, Interrupt_B_Enable_Register, ~0, 0);
+ ni_stc_writew(dev, AO_BC_Source_Select, AO_Personal_Register);
+ ni_stc_writew(dev, 0x3f98, Interrupt_B_Ack_Register);
+ ni_stc_writew(dev, AO_BC_Source_Select | AO_UPDATE_Pulse_Width |
+ AO_TMRDACWR_Pulse_Width, AO_Personal_Register);
+ ni_stc_writew(dev, 0, AO_Output_Control_Register);
+ ni_stc_writew(dev, 0, AO_Start_Select_Register);
+ devpriv->ao_cmd1 = 0;
+ ni_stc_writew(dev, devpriv->ao_cmd1, AO_Command_1_Register);
+ devpriv->ao_cmd2 = 0;
+ ni_stc_writew(dev, devpriv->ao_cmd2, AO_Command_2_Register);
+ devpriv->ao_mode1 = 0;
+ ni_stc_writew(dev, devpriv->ao_mode1, AO_Mode_1_Register);
+ devpriv->ao_mode2 = 0;
+ ni_stc_writew(dev, devpriv->ao_mode2, AO_Mode_2_Register);
+ if (devpriv->is_m_series)
+ devpriv->ao_mode3 = AO_Last_Gate_Disable;
+ else
+ devpriv->ao_mode3 = 0;
+ ni_stc_writew(dev, devpriv->ao_mode3, AO_Mode_3_Register);
+ devpriv->ao_trigger_select = 0;
+ ni_stc_writew(dev, devpriv->ao_trigger_select,
+ AO_Trigger_Select_Register);
+ if (devpriv->is_6xxx) {
+ unsigned immediate_bits = 0;
+ unsigned i;
+
+ for (i = 0; i < s->n_chan; ++i)
+ immediate_bits |= 1 << i;
+ ni_ao_win_outw(dev, immediate_bits, AO_Immediate_671x);
+ ni_ao_win_outw(dev, CLEAR_WG, AO_Misc_611x);
+ }
+ ni_stc_writew(dev, AO_Configuration_End, Joint_Reset_Register);
+
+ return 0;
+}
+
+/* digital io */
+
+static int ni_dio_insn_config(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ struct ni_private *devpriv = dev->private;
+ int ret;
+
+ ret = comedi_dio_insn_config(dev, s, insn, data, 0);
+ if (ret)
+ return ret;
+
+ devpriv->dio_control &= ~DIO_Pins_Dir_Mask;
+ devpriv->dio_control |= DIO_Pins_Dir(s->io_bits);
+ ni_stc_writew(dev, devpriv->dio_control, DIO_Control_Register);
+
+ return insn->n;
+}
+
+static int ni_dio_insn_bits(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ struct ni_private *devpriv = dev->private;
+
+ /* Make sure we're not using the serial part of the dio */
+ if ((data[0] & (DIO_SDIN | DIO_SDOUT)) && devpriv->serial_interval_ns)
+ return -EBUSY;
+
+ if (comedi_dio_update_state(s, data)) {
+ devpriv->dio_output &= ~DIO_Parallel_Data_Mask;
+ devpriv->dio_output |= DIO_Parallel_Data_Out(s->state);
+ ni_stc_writew(dev, devpriv->dio_output, DIO_Output_Register);
+ }
+
+ data[1] = ni_stc_readw(dev, DIO_Parallel_Input_Register);
+
+ return insn->n;
+}
+
+static int ni_m_series_dio_insn_config(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ int ret;
+
+ ret = comedi_dio_insn_config(dev, s, insn, data, 0);
+ if (ret)
+ return ret;
+
+ ni_writel(dev, s->io_bits, M_Offset_DIO_Direction);
+
+ return insn->n;
+}
+
+static int ni_m_series_dio_insn_bits(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ if (comedi_dio_update_state(s, data))
+ ni_writel(dev, s->state, M_Offset_Static_Digital_Output);
+
+ data[1] = ni_readl(dev, M_Offset_Static_Digital_Input);
+
+ return insn->n;
+}
+
+static int ni_cdio_check_chanlist(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_cmd *cmd)
+{
+ int i;
+
+ for (i = 0; i < cmd->chanlist_len; ++i) {
+ unsigned int chan = CR_CHAN(cmd->chanlist[i]);
+
+ if (chan != i)
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int ni_cdio_cmdtest(struct comedi_device *dev,
+ struct comedi_subdevice *s, struct comedi_cmd *cmd)
+{
+ int err = 0;
+ int tmp;
+
+ /* Step 1 : check if triggers are trivially valid */
+
+ err |= comedi_check_trigger_src(&cmd->start_src, TRIG_INT);
+ err |= comedi_check_trigger_src(&cmd->scan_begin_src, TRIG_EXT);
+ err |= comedi_check_trigger_src(&cmd->convert_src, TRIG_NOW);
+ err |= comedi_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
+ err |= comedi_check_trigger_src(&cmd->stop_src, TRIG_NONE);
+
+ if (err)
+ return 1;
+
+ /* Step 2a : make sure trigger sources are unique */
+ /* Step 2b : and mutually compatible */
+
+ /* Step 3: check if arguments are trivially valid */
+
+ err |= comedi_check_trigger_arg_is(&cmd->start_arg, 0);
+
+ tmp = cmd->scan_begin_arg;
+ tmp &= CR_PACK_FLAGS(CDO_Sample_Source_Select_Mask, 0, 0, CR_INVERT);
+ if (tmp != cmd->scan_begin_arg)
+ err |= -EINVAL;
+
+ err |= comedi_check_trigger_arg_is(&cmd->convert_arg, 0);
+ err |= comedi_check_trigger_arg_is(&cmd->scan_end_arg,
+ cmd->chanlist_len);
+ err |= comedi_check_trigger_arg_is(&cmd->stop_arg, 0);
+
+ if (err)
+ return 3;
+
+ /* Step 4: fix up any arguments */
+
+ /* Step 5: check channel list if it exists */
+
+ if (cmd->chanlist && cmd->chanlist_len > 0)
+ err |= ni_cdio_check_chanlist(dev, s, cmd);
+
+ if (err)
+ return 5;
+
+ return 0;
+}
+
+static int ni_cdo_inttrig(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ unsigned int trig_num)
+{
+ struct comedi_cmd *cmd = &s->async->cmd;
+ const unsigned timeout = 1000;
+ int retval = 0;
+ unsigned i;
+#ifdef PCIDMA
+ struct ni_private *devpriv = dev->private;
+ unsigned long flags;
+#endif
+
+ if (trig_num != cmd->start_arg)
+ return -EINVAL;
+
+ s->async->inttrig = NULL;
+
+ /* read alloc the entire buffer */
+ comedi_buf_read_alloc(s, s->async->prealloc_bufsz);
+
+#ifdef PCIDMA
+ spin_lock_irqsave(&devpriv->mite_channel_lock, flags);
+ if (devpriv->cdo_mite_chan) {
+ mite_prep_dma(devpriv->cdo_mite_chan, 32, 32);
+ mite_dma_arm(devpriv->cdo_mite_chan);
+ } else {
+ dev_err(dev->class_dev, "BUG: no cdo mite channel?\n");
+ retval = -EIO;
+ }
+ spin_unlock_irqrestore(&devpriv->mite_channel_lock, flags);
+ if (retval < 0)
+ return retval;
+#endif
+/*
+* XXX not sure what interrupt C group does
+* ni_writeb(dev, Interrupt_Group_C_Enable_Bit,
+* M_Offset_Interrupt_C_Enable); wait for dma to fill output fifo
+*/
+ for (i = 0; i < timeout; ++i) {
+ if (ni_readl(dev, M_Offset_CDIO_Status) & CDO_FIFO_Full_Bit)
+ break;
+ udelay(10);
+ }
+ if (i == timeout) {
+ dev_err(dev->class_dev, "dma failed to fill cdo fifo!\n");
+ s->cancel(dev, s);
+ return -EIO;
+ }
+ ni_writel(dev, CDO_Arm_Bit | CDO_Error_Interrupt_Enable_Set_Bit |
+ CDO_Empty_FIFO_Interrupt_Enable_Set_Bit,
+ M_Offset_CDIO_Command);
+ return retval;
+}
+
+static int ni_cdio_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
+{
+ const struct comedi_cmd *cmd = &s->async->cmd;
+ unsigned cdo_mode_bits = CDO_FIFO_Mode_Bit | CDO_Halt_On_Error_Bit;
+ int retval;
+
+ ni_writel(dev, CDO_Reset_Bit, M_Offset_CDIO_Command);
+ switch (cmd->scan_begin_src) {
+ case TRIG_EXT:
+ cdo_mode_bits |=
+ CR_CHAN(cmd->scan_begin_arg) &
+ CDO_Sample_Source_Select_Mask;
+ break;
+ default:
+ BUG();
+ break;
+ }
+ if (cmd->scan_begin_arg & CR_INVERT)
+ cdo_mode_bits |= CDO_Polarity_Bit;
+ ni_writel(dev, cdo_mode_bits, M_Offset_CDO_Mode);
+ if (s->io_bits) {
+ ni_writel(dev, s->state, M_Offset_CDO_FIFO_Data);
+ ni_writel(dev, CDO_SW_Update_Bit, M_Offset_CDIO_Command);
+ ni_writel(dev, s->io_bits, M_Offset_CDO_Mask_Enable);
+ } else {
+ dev_err(dev->class_dev,
+ "attempted to run digital output command with no lines configured as outputs\n");
+ return -EIO;
+ }
+ retval = ni_request_cdo_mite_channel(dev);
+ if (retval < 0)
+ return retval;
+
+ s->async->inttrig = ni_cdo_inttrig;
+
+ return 0;
+}
+
+static int ni_cdio_cancel(struct comedi_device *dev, struct comedi_subdevice *s)
+{
+ ni_writel(dev, CDO_Disarm_Bit | CDO_Error_Interrupt_Enable_Clear_Bit |
+ CDO_Empty_FIFO_Interrupt_Enable_Clear_Bit |
+ CDO_FIFO_Request_Interrupt_Enable_Clear_Bit,
+ M_Offset_CDIO_Command);
+/*
+* XXX not sure what interrupt C group does ni_writeb(dev, 0,
+* M_Offset_Interrupt_C_Enable);
+*/
+ ni_writel(dev, 0, M_Offset_CDO_Mask_Enable);
+ ni_release_cdo_mite_channel(dev);
+ return 0;
+}
+
+static void handle_cdio_interrupt(struct comedi_device *dev)
+{
+ struct ni_private *devpriv = dev->private;
+ unsigned cdio_status;
+ struct comedi_subdevice *s = &dev->subdevices[NI_DIO_SUBDEV];
+#ifdef PCIDMA
+ unsigned long flags;
+#endif
+
+ if (!devpriv->is_m_series)
+ return;
+#ifdef PCIDMA
+ spin_lock_irqsave(&devpriv->mite_channel_lock, flags);
+ if (devpriv->cdo_mite_chan) {
+ unsigned cdo_mite_status =
+ mite_get_status(devpriv->cdo_mite_chan);
+ if (cdo_mite_status & CHSR_LINKC) {
+ writel(CHOR_CLRLC,
+ devpriv->mite->mite_io_addr +
+ MITE_CHOR(devpriv->cdo_mite_chan->channel));
+ }
+ mite_sync_output_dma(devpriv->cdo_mite_chan, s);
+ }
+ spin_unlock_irqrestore(&devpriv->mite_channel_lock, flags);
+#endif
+
+ cdio_status = ni_readl(dev, M_Offset_CDIO_Status);
+ if (cdio_status & (CDO_Overrun_Bit | CDO_Underflow_Bit)) {
+ /* XXX just guessing this is needed and does something useful */
+ ni_writel(dev, CDO_Error_Interrupt_Confirm_Bit,
+ M_Offset_CDIO_Command);
+ s->async->events |= COMEDI_CB_OVERFLOW;
+ }
+ if (cdio_status & CDO_FIFO_Empty_Bit) {
+ ni_writel(dev, CDO_Empty_FIFO_Interrupt_Enable_Clear_Bit,
+ M_Offset_CDIO_Command);
+ /* s->async->events |= COMEDI_CB_EOA; */
+ }
+ comedi_handle_events(dev, s);
+}
+
+static int ni_serial_hw_readwrite8(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ unsigned char data_out,
+ unsigned char *data_in)
+{
+ struct ni_private *devpriv = dev->private;
+ unsigned int status1;
+ int err = 0, count = 20;
+
+ devpriv->dio_output &= ~DIO_Serial_Data_Mask;
+ devpriv->dio_output |= DIO_Serial_Data_Out(data_out);
+ ni_stc_writew(dev, devpriv->dio_output, DIO_Output_Register);
+
+ status1 = ni_stc_readw(dev, Joint_Status_1_Register);
+ if (status1 & DIO_Serial_IO_In_Progress_St) {
+ err = -EBUSY;
+ goto Error;
+ }
+
+ devpriv->dio_control |= DIO_HW_Serial_Start;
+ ni_stc_writew(dev, devpriv->dio_control, DIO_Control_Register);
+ devpriv->dio_control &= ~DIO_HW_Serial_Start;
+
+ /* Wait until STC says we're done, but don't loop infinitely. */
+ while ((status1 = ni_stc_readw(dev, Joint_Status_1_Register)) &
+ DIO_Serial_IO_In_Progress_St) {
+ /* Delay one bit per loop */
+ udelay((devpriv->serial_interval_ns + 999) / 1000);
+ if (--count < 0) {
+ dev_err(dev->class_dev,
+ "SPI serial I/O didn't finish in time!\n");
+ err = -ETIME;
+ goto Error;
+ }
+ }
+
+ /* Delay for last bit. This delay is absolutely necessary, because
+ DIO_Serial_IO_In_Progress_St goes high one bit too early. */
+ udelay((devpriv->serial_interval_ns + 999) / 1000);
+
+ if (data_in)
+ *data_in = ni_stc_readw(dev, DIO_Serial_Input_Register);
+
+Error:
+ ni_stc_writew(dev, devpriv->dio_control, DIO_Control_Register);
+
+ return err;
+}
+
+static int ni_serial_sw_readwrite8(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ unsigned char data_out,
+ unsigned char *data_in)
+{
+ struct ni_private *devpriv = dev->private;
+ unsigned char mask, input = 0;
+
+ /* Wait for one bit before transfer */
+ udelay((devpriv->serial_interval_ns + 999) / 1000);
+
+ for (mask = 0x80; mask; mask >>= 1) {
+ /* Output current bit; note that we cannot touch s->state
+ because it is a per-subdevice field, and serial is
+ a separate subdevice from DIO. */
+ devpriv->dio_output &= ~DIO_SDOUT;
+ if (data_out & mask)
+ devpriv->dio_output |= DIO_SDOUT;
+ ni_stc_writew(dev, devpriv->dio_output, DIO_Output_Register);
+
+ /* Assert SDCLK (active low, inverted), wait for half of
+ the delay, deassert SDCLK, and wait for the other half. */
+ devpriv->dio_control |= DIO_Software_Serial_Control;
+ ni_stc_writew(dev, devpriv->dio_control, DIO_Control_Register);
+
+ udelay((devpriv->serial_interval_ns + 999) / 2000);
+
+ devpriv->dio_control &= ~DIO_Software_Serial_Control;
+ ni_stc_writew(dev, devpriv->dio_control, DIO_Control_Register);
+
+ udelay((devpriv->serial_interval_ns + 999) / 2000);
+
+ /* Input current bit */
+ if (ni_stc_readw(dev, DIO_Parallel_Input_Register) & DIO_SDIN)
+ input |= mask;
+ }
+
+ if (data_in)
+ *data_in = input;
+
+ return 0;
+}
+
+static int ni_serial_insn_config(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ struct ni_private *devpriv = dev->private;
+ int err = insn->n;
+ unsigned char byte_out, byte_in = 0;
+
+ if (insn->n != 2)
+ return -EINVAL;
+
+ switch (data[0]) {
+ case INSN_CONFIG_SERIAL_CLOCK:
+ devpriv->serial_hw_mode = 1;
+ devpriv->dio_control |= DIO_HW_Serial_Enable;
+
+ if (data[1] == SERIAL_DISABLED) {
+ devpriv->serial_hw_mode = 0;
+ devpriv->dio_control &= ~(DIO_HW_Serial_Enable |
+ DIO_Software_Serial_Control);
+ data[1] = SERIAL_DISABLED;
+ devpriv->serial_interval_ns = data[1];
+ } else if (data[1] <= SERIAL_600NS) {
+ /* Warning: this clock speed is too fast to reliably
+ control SCXI. */
+ devpriv->dio_control &= ~DIO_HW_Serial_Timebase;
+ devpriv->clock_and_fout |= Slow_Internal_Timebase;
+ devpriv->clock_and_fout &= ~DIO_Serial_Out_Divide_By_2;
+ data[1] = SERIAL_600NS;
+ devpriv->serial_interval_ns = data[1];
+ } else if (data[1] <= SERIAL_1_2US) {
+ devpriv->dio_control &= ~DIO_HW_Serial_Timebase;
+ devpriv->clock_and_fout |= Slow_Internal_Timebase |
+ DIO_Serial_Out_Divide_By_2;
+ data[1] = SERIAL_1_2US;
+ devpriv->serial_interval_ns = data[1];
+ } else if (data[1] <= SERIAL_10US) {
+ devpriv->dio_control |= DIO_HW_Serial_Timebase;
+ devpriv->clock_and_fout |= Slow_Internal_Timebase |
+ DIO_Serial_Out_Divide_By_2;
+ /* Note: DIO_Serial_Out_Divide_By_2 only affects
+ 600ns/1.2us. If you turn divide_by_2 off with the
+ slow clock, you will still get 10us, except then
+ all your delays are wrong. */
+ data[1] = SERIAL_10US;
+ devpriv->serial_interval_ns = data[1];
+ } else {
+ devpriv->dio_control &= ~(DIO_HW_Serial_Enable |
+ DIO_Software_Serial_Control);
+ devpriv->serial_hw_mode = 0;
+ data[1] = (data[1] / 1000) * 1000;
+ devpriv->serial_interval_ns = data[1];
+ }
+
+ ni_stc_writew(dev, devpriv->dio_control, DIO_Control_Register);
+ ni_stc_writew(dev, devpriv->clock_and_fout,
+ Clock_and_FOUT_Register);
+ return 1;
+
+ case INSN_CONFIG_BIDIRECTIONAL_DATA:
+
+ if (devpriv->serial_interval_ns == 0)
+ return -EINVAL;
+
+ byte_out = data[1] & 0xFF;
+
+ if (devpriv->serial_hw_mode) {
+ err = ni_serial_hw_readwrite8(dev, s, byte_out,
+ &byte_in);
+ } else if (devpriv->serial_interval_ns > 0) {
+ err = ni_serial_sw_readwrite8(dev, s, byte_out,
+ &byte_in);
+ } else {
+ dev_err(dev->class_dev, "serial disabled!\n");
+ return -EINVAL;
+ }
+ if (err < 0)
+ return err;
+ data[1] = byte_in & 0xFF;
+ return insn->n;
+
+ break;
+ default:
+ return -EINVAL;
+ }
+}
+
+static void init_ao_67xx(struct comedi_device *dev, struct comedi_subdevice *s)
+{
+ int i;
+
+ for (i = 0; i < s->n_chan; i++) {
+ ni_ao_win_outw(dev, AO_Channel(i) | 0x0,
+ AO_Configuration_2_67xx);
+ }
+ ni_ao_win_outw(dev, 0x0, AO_Later_Single_Point_Updates);
+}
+
+static unsigned ni_gpct_to_stc_register(enum ni_gpct_register reg)
+{
+ unsigned stc_register;
+
+ switch (reg) {
+ case NITIO_G0_AUTO_INC:
+ stc_register = G_Autoincrement_Register(0);
+ break;
+ case NITIO_G1_AUTO_INC:
+ stc_register = G_Autoincrement_Register(1);
+ break;
+ case NITIO_G0_CMD:
+ stc_register = G_Command_Register(0);
+ break;
+ case NITIO_G1_CMD:
+ stc_register = G_Command_Register(1);
+ break;
+ case NITIO_G0_HW_SAVE:
+ stc_register = G_HW_Save_Register(0);
+ break;
+ case NITIO_G1_HW_SAVE:
+ stc_register = G_HW_Save_Register(1);
+ break;
+ case NITIO_G0_SW_SAVE:
+ stc_register = G_Save_Register(0);
+ break;
+ case NITIO_G1_SW_SAVE:
+ stc_register = G_Save_Register(1);
+ break;
+ case NITIO_G0_MODE:
+ stc_register = G_Mode_Register(0);
+ break;
+ case NITIO_G1_MODE:
+ stc_register = G_Mode_Register(1);
+ break;
+ case NITIO_G0_LOADA:
+ stc_register = G_Load_A_Register(0);
+ break;
+ case NITIO_G1_LOADA:
+ stc_register = G_Load_A_Register(1);
+ break;
+ case NITIO_G0_LOADB:
+ stc_register = G_Load_B_Register(0);
+ break;
+ case NITIO_G1_LOADB:
+ stc_register = G_Load_B_Register(1);
+ break;
+ case NITIO_G0_INPUT_SEL:
+ stc_register = G_Input_Select_Register(0);
+ break;
+ case NITIO_G1_INPUT_SEL:
+ stc_register = G_Input_Select_Register(1);
+ break;
+ case NITIO_G01_STATUS:
+ stc_register = G_Status_Register;
+ break;
+ case NITIO_G01_RESET:
+ stc_register = Joint_Reset_Register;
+ break;
+ case NITIO_G01_STATUS1:
+ stc_register = Joint_Status_1_Register;
+ break;
+ case NITIO_G01_STATUS2:
+ stc_register = Joint_Status_2_Register;
+ break;
+ case NITIO_G0_INT_ACK:
+ stc_register = Interrupt_A_Ack_Register;
+ break;
+ case NITIO_G1_INT_ACK:
+ stc_register = Interrupt_B_Ack_Register;
+ break;
+ case NITIO_G0_STATUS:
+ stc_register = AI_Status_1_Register;
+ break;
+ case NITIO_G1_STATUS:
+ stc_register = AO_Status_1_Register;
+ break;
+ case NITIO_G0_INT_ENA:
+ stc_register = Interrupt_A_Enable_Register;
+ break;
+ case NITIO_G1_INT_ENA:
+ stc_register = Interrupt_B_Enable_Register;
+ break;
+ default:
+ pr_err("%s: unhandled register 0x%x in switch.\n",
+ __func__, reg);
+ BUG();
+ return 0;
+ }
+ return stc_register;
+}
+
+static void ni_gpct_write_register(struct ni_gpct *counter, unsigned bits,
+ enum ni_gpct_register reg)
+{
+ struct comedi_device *dev = counter->counter_dev->dev;
+ unsigned stc_register;
+ /* bits in the join reset register which are relevant to counters */
+ static const unsigned gpct_joint_reset_mask = G0_Reset | G1_Reset;
+ static const unsigned gpct_interrupt_a_enable_mask =
+ G0_Gate_Interrupt_Enable | G0_TC_Interrupt_Enable;
+ static const unsigned gpct_interrupt_b_enable_mask =
+ G1_Gate_Interrupt_Enable | G1_TC_Interrupt_Enable;
+
+ switch (reg) {
+ /* m-series-only registers */
+ case NITIO_G0_CNT_MODE:
+ ni_writew(dev, bits, M_Offset_G0_Counting_Mode);
+ break;
+ case NITIO_G1_CNT_MODE:
+ ni_writew(dev, bits, M_Offset_G1_Counting_Mode);
+ break;
+ case NITIO_G0_GATE2:
+ ni_writew(dev, bits, M_Offset_G0_Second_Gate);
+ break;
+ case NITIO_G1_GATE2:
+ ni_writew(dev, bits, M_Offset_G1_Second_Gate);
+ break;
+ case NITIO_G0_DMA_CFG:
+ ni_writew(dev, bits, M_Offset_G0_DMA_Config);
+ break;
+ case NITIO_G1_DMA_CFG:
+ ni_writew(dev, bits, M_Offset_G1_DMA_Config);
+ break;
+ case NITIO_G0_ABZ:
+ ni_writew(dev, bits, M_Offset_G0_MSeries_ABZ);
+ break;
+ case NITIO_G1_ABZ:
+ ni_writew(dev, bits, M_Offset_G1_MSeries_ABZ);
+ break;
+
+ /* 32 bit registers */
+ case NITIO_G0_LOADA:
+ case NITIO_G1_LOADA:
+ case NITIO_G0_LOADB:
+ case NITIO_G1_LOADB:
+ stc_register = ni_gpct_to_stc_register(reg);
+ ni_stc_writel(dev, bits, stc_register);
+ break;
+
+ /* 16 bit registers */
+ case NITIO_G0_INT_ENA:
+ BUG_ON(bits & ~gpct_interrupt_a_enable_mask);
+ ni_set_bitfield(dev, Interrupt_A_Enable_Register,
+ gpct_interrupt_a_enable_mask, bits);
+ break;
+ case NITIO_G1_INT_ENA:
+ BUG_ON(bits & ~gpct_interrupt_b_enable_mask);
+ ni_set_bitfield(dev, Interrupt_B_Enable_Register,
+ gpct_interrupt_b_enable_mask, bits);
+ break;
+ case NITIO_G01_RESET:
+ BUG_ON(bits & ~gpct_joint_reset_mask);
+ /* fall-through */
+ default:
+ stc_register = ni_gpct_to_stc_register(reg);
+ ni_stc_writew(dev, bits, stc_register);
+ }
+}
+
+static unsigned ni_gpct_read_register(struct ni_gpct *counter,
+ enum ni_gpct_register reg)
+{
+ struct comedi_device *dev = counter->counter_dev->dev;
+ unsigned stc_register;
+
+ switch (reg) {
+ /* m-series only registers */
+ case NITIO_G0_DMA_STATUS:
+ return ni_readw(dev, M_Offset_G0_DMA_Status);
+ case NITIO_G1_DMA_STATUS:
+ return ni_readw(dev, M_Offset_G1_DMA_Status);
+
+ /* 32 bit registers */
+ case NITIO_G0_HW_SAVE:
+ case NITIO_G1_HW_SAVE:
+ case NITIO_G0_SW_SAVE:
+ case NITIO_G1_SW_SAVE:
+ stc_register = ni_gpct_to_stc_register(reg);
+ return ni_stc_readl(dev, stc_register);
+
+ /* 16 bit registers */
+ default:
+ stc_register = ni_gpct_to_stc_register(reg);
+ return ni_stc_readw(dev, stc_register);
+ }
+ return 0;
+}
+
+static int ni_freq_out_insn_read(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ struct ni_private *devpriv = dev->private;
+ unsigned int val = devpriv->clock_and_fout & FOUT_Divider_mask;
+ int i;
+
+ for (i = 0; i < insn->n; i++)
+ data[i] = val;
+
+ return insn->n;
+}
+
+static int ni_freq_out_insn_write(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ struct ni_private *devpriv = dev->private;
+
+ if (insn->n) {
+ devpriv->clock_and_fout &= ~FOUT_Enable;
+ ni_stc_writew(dev, devpriv->clock_and_fout,
+ Clock_and_FOUT_Register);
+ devpriv->clock_and_fout &= ~FOUT_Divider_mask;
+
+ /* use the last data value to set the fout divider */
+ devpriv->clock_and_fout |= FOUT_Divider(data[insn->n - 1]);
+
+ devpriv->clock_and_fout |= FOUT_Enable;
+ ni_stc_writew(dev, devpriv->clock_and_fout,
+ Clock_and_FOUT_Register);
+ }
+ return insn->n;
+}
+
+static int ni_freq_out_insn_config(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ struct ni_private *devpriv = dev->private;
+
+ switch (data[0]) {
+ case INSN_CONFIG_SET_CLOCK_SRC:
+ switch (data[1]) {
+ case NI_FREQ_OUT_TIMEBASE_1_DIV_2_CLOCK_SRC:
+ devpriv->clock_and_fout &= ~FOUT_Timebase_Select;
+ break;
+ case NI_FREQ_OUT_TIMEBASE_2_CLOCK_SRC:
+ devpriv->clock_and_fout |= FOUT_Timebase_Select;
+ break;
+ default:
+ return -EINVAL;
+ }
+ ni_stc_writew(dev, devpriv->clock_and_fout,
+ Clock_and_FOUT_Register);
+ break;
+ case INSN_CONFIG_GET_CLOCK_SRC:
+ if (devpriv->clock_and_fout & FOUT_Timebase_Select) {
+ data[1] = NI_FREQ_OUT_TIMEBASE_2_CLOCK_SRC;
+ data[2] = TIMEBASE_2_NS;
+ } else {
+ data[1] = NI_FREQ_OUT_TIMEBASE_1_DIV_2_CLOCK_SRC;
+ data[2] = TIMEBASE_1_NS * 2;
+ }
+ break;
+ default:
+ return -EINVAL;
+ }
+ return insn->n;
+}
+
+static int ni_8255_callback(struct comedi_device *dev,
+ int dir, int port, int data, unsigned long iobase)
+{
+ if (dir) {
+ ni_writeb(dev, data, iobase + 2 * port);
+ return 0;
+ }
+
+ return ni_readb(dev, iobase + 2 * port);
+}
+
+static int ni_get_pwm_config(struct comedi_device *dev, unsigned int *data)
+{
+ struct ni_private *devpriv = dev->private;
+
+ data[1] = devpriv->pwm_up_count * devpriv->clock_ns;
+ data[2] = devpriv->pwm_down_count * devpriv->clock_ns;
+ return 3;
+}
+
+static int ni_m_series_pwm_config(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ struct ni_private *devpriv = dev->private;
+ unsigned up_count, down_count;
+
+ switch (data[0]) {
+ case INSN_CONFIG_PWM_OUTPUT:
+ switch (data[1]) {
+ case CMDF_ROUND_NEAREST:
+ up_count =
+ (data[2] +
+ devpriv->clock_ns / 2) / devpriv->clock_ns;
+ break;
+ case CMDF_ROUND_DOWN:
+ up_count = data[2] / devpriv->clock_ns;
+ break;
+ case CMDF_ROUND_UP:
+ up_count =
+ (data[2] + devpriv->clock_ns -
+ 1) / devpriv->clock_ns;
+ break;
+ default:
+ return -EINVAL;
+ }
+ switch (data[3]) {
+ case CMDF_ROUND_NEAREST:
+ down_count =
+ (data[4] +
+ devpriv->clock_ns / 2) / devpriv->clock_ns;
+ break;
+ case CMDF_ROUND_DOWN:
+ down_count = data[4] / devpriv->clock_ns;
+ break;
+ case CMDF_ROUND_UP:
+ down_count =
+ (data[4] + devpriv->clock_ns -
+ 1) / devpriv->clock_ns;
+ break;
+ default:
+ return -EINVAL;
+ }
+ if (up_count * devpriv->clock_ns != data[2] ||
+ down_count * devpriv->clock_ns != data[4]) {
+ data[2] = up_count * devpriv->clock_ns;
+ data[4] = down_count * devpriv->clock_ns;
+ return -EAGAIN;
+ }
+ ni_writel(dev, MSeries_Cal_PWM_High_Time_Bits(up_count) |
+ MSeries_Cal_PWM_Low_Time_Bits(down_count),
+ M_Offset_Cal_PWM);
+ devpriv->pwm_up_count = up_count;
+ devpriv->pwm_down_count = down_count;
+ return 5;
+ case INSN_CONFIG_GET_PWM_OUTPUT:
+ return ni_get_pwm_config(dev, data);
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int ni_6143_pwm_config(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ struct ni_private *devpriv = dev->private;
+ unsigned up_count, down_count;
+
+ switch (data[0]) {
+ case INSN_CONFIG_PWM_OUTPUT:
+ switch (data[1]) {
+ case CMDF_ROUND_NEAREST:
+ up_count =
+ (data[2] +
+ devpriv->clock_ns / 2) / devpriv->clock_ns;
+ break;
+ case CMDF_ROUND_DOWN:
+ up_count = data[2] / devpriv->clock_ns;
+ break;
+ case CMDF_ROUND_UP:
+ up_count =
+ (data[2] + devpriv->clock_ns -
+ 1) / devpriv->clock_ns;
+ break;
+ default:
+ return -EINVAL;
+ }
+ switch (data[3]) {
+ case CMDF_ROUND_NEAREST:
+ down_count =
+ (data[4] +
+ devpriv->clock_ns / 2) / devpriv->clock_ns;
+ break;
+ case CMDF_ROUND_DOWN:
+ down_count = data[4] / devpriv->clock_ns;
+ break;
+ case CMDF_ROUND_UP:
+ down_count =
+ (data[4] + devpriv->clock_ns -
+ 1) / devpriv->clock_ns;
+ break;
+ default:
+ return -EINVAL;
+ }
+ if (up_count * devpriv->clock_ns != data[2] ||
+ down_count * devpriv->clock_ns != data[4]) {
+ data[2] = up_count * devpriv->clock_ns;
+ data[4] = down_count * devpriv->clock_ns;
+ return -EAGAIN;
+ }
+ ni_writel(dev, up_count, Calibration_HighTime_6143);
+ devpriv->pwm_up_count = up_count;
+ ni_writel(dev, down_count, Calibration_LowTime_6143);
+ devpriv->pwm_down_count = down_count;
+ return 5;
+ case INSN_CONFIG_GET_PWM_OUTPUT:
+ return ni_get_pwm_config(dev, data);
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int pack_mb88341(int addr, int val, int *bitstring)
+{
+ /*
+ Fujitsu MB 88341
+ Note that address bits are reversed. Thanks to
+ Ingo Keen for noticing this.
+
+ Note also that the 88341 expects address values from
+ 1-12, whereas we use channel numbers 0-11. The NI
+ docs use 1-12, also, so be careful here.
+ */
+ addr++;
+ *bitstring = ((addr & 0x1) << 11) |
+ ((addr & 0x2) << 9) |
+ ((addr & 0x4) << 7) | ((addr & 0x8) << 5) | (val & 0xff);
+ return 12;
+}
+
+static int pack_dac8800(int addr, int val, int *bitstring)
+{
+ *bitstring = ((addr & 0x7) << 8) | (val & 0xff);
+ return 11;
+}
+
+static int pack_dac8043(int addr, int val, int *bitstring)
+{
+ *bitstring = val & 0xfff;
+ return 12;
+}
+
+static int pack_ad8522(int addr, int val, int *bitstring)
+{
+ *bitstring = (val & 0xfff) | (addr ? 0xc000 : 0xa000);
+ return 16;
+}
+
+static int pack_ad8804(int addr, int val, int *bitstring)
+{
+ *bitstring = ((addr & 0xf) << 8) | (val & 0xff);
+ return 12;
+}
+
+static int pack_ad8842(int addr, int val, int *bitstring)
+{
+ *bitstring = ((addr + 1) << 8) | (val & 0xff);
+ return 12;
+}
+
+struct caldac_struct {
+ int n_chans;
+ int n_bits;
+ int (*packbits)(int, int, int *);
+};
+
+static struct caldac_struct caldacs[] = {
+ [mb88341] = {12, 8, pack_mb88341},
+ [dac8800] = {8, 8, pack_dac8800},
+ [dac8043] = {1, 12, pack_dac8043},
+ [ad8522] = {2, 12, pack_ad8522},
+ [ad8804] = {12, 8, pack_ad8804},
+ [ad8842] = {8, 8, pack_ad8842},
+ [ad8804_debug] = {16, 8, pack_ad8804},
+};
+
+static void ni_write_caldac(struct comedi_device *dev, int addr, int val)
+{
+ const struct ni_board_struct *board = dev->board_ptr;
+ struct ni_private *devpriv = dev->private;
+ unsigned int loadbit = 0, bits = 0, bit, bitstring = 0;
+ int i;
+ int type;
+
+ if (devpriv->caldacs[addr] == val)
+ return;
+ devpriv->caldacs[addr] = val;
+
+ for (i = 0; i < 3; i++) {
+ type = board->caldac[i];
+ if (type == caldac_none)
+ break;
+ if (addr < caldacs[type].n_chans) {
+ bits = caldacs[type].packbits(addr, val, &bitstring);
+ loadbit = SerDacLd(i);
+ break;
+ }
+ addr -= caldacs[type].n_chans;
+ }
+
+ for (bit = 1 << (bits - 1); bit; bit >>= 1) {
+ ni_writeb(dev, ((bit & bitstring) ? 0x02 : 0), Serial_Command);
+ udelay(1);
+ ni_writeb(dev, 1 | ((bit & bitstring) ? 0x02 : 0),
+ Serial_Command);
+ udelay(1);
+ }
+ ni_writeb(dev, loadbit, Serial_Command);
+ udelay(1);
+ ni_writeb(dev, 0, Serial_Command);
+}
+
+static int ni_calib_insn_write(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ ni_write_caldac(dev, CR_CHAN(insn->chanspec), data[0]);
+
+ return 1;
+}
+
+static int ni_calib_insn_read(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ struct ni_private *devpriv = dev->private;
+
+ data[0] = devpriv->caldacs[CR_CHAN(insn->chanspec)];
+
+ return 1;
+}
+
+static void caldac_setup(struct comedi_device *dev, struct comedi_subdevice *s)
+{
+ const struct ni_board_struct *board = dev->board_ptr;
+ struct ni_private *devpriv = dev->private;
+ int i, j;
+ int n_dacs;
+ int n_chans = 0;
+ int n_bits;
+ int diffbits = 0;
+ int type;
+ int chan;
+
+ type = board->caldac[0];
+ if (type == caldac_none)
+ return;
+ n_bits = caldacs[type].n_bits;
+ for (i = 0; i < 3; i++) {
+ type = board->caldac[i];
+ if (type == caldac_none)
+ break;
+ if (caldacs[type].n_bits != n_bits)
+ diffbits = 1;
+ n_chans += caldacs[type].n_chans;
+ }
+ n_dacs = i;
+ s->n_chan = n_chans;
+
+ if (diffbits) {
+ unsigned int *maxdata_list;
+
+ if (n_chans > MAX_N_CALDACS)
+ dev_err(dev->class_dev,
+ "BUG! MAX_N_CALDACS too small\n");
+ s->maxdata_list = maxdata_list = devpriv->caldac_maxdata_list;
+ chan = 0;
+ for (i = 0; i < n_dacs; i++) {
+ type = board->caldac[i];
+ for (j = 0; j < caldacs[type].n_chans; j++) {
+ maxdata_list[chan] =
+ (1 << caldacs[type].n_bits) - 1;
+ chan++;
+ }
+ }
+
+ for (chan = 0; chan < s->n_chan; chan++)
+ ni_write_caldac(dev, i, s->maxdata_list[i] / 2);
+ } else {
+ type = board->caldac[0];
+ s->maxdata = (1 << caldacs[type].n_bits) - 1;
+
+ for (chan = 0; chan < s->n_chan; chan++)
+ ni_write_caldac(dev, i, s->maxdata / 2);
+ }
+}
+
+static int ni_read_eeprom(struct comedi_device *dev, int addr)
+{
+ int bit;
+ int bitstring;
+
+ bitstring = 0x0300 | ((addr & 0x100) << 3) | (addr & 0xff);
+ ni_writeb(dev, 0x04, Serial_Command);
+ for (bit = 0x8000; bit; bit >>= 1) {
+ ni_writeb(dev, 0x04 | ((bit & bitstring) ? 0x02 : 0),
+ Serial_Command);
+ ni_writeb(dev, 0x05 | ((bit & bitstring) ? 0x02 : 0),
+ Serial_Command);
+ }
+ bitstring = 0;
+ for (bit = 0x80; bit; bit >>= 1) {
+ ni_writeb(dev, 0x04, Serial_Command);
+ ni_writeb(dev, 0x05, Serial_Command);
+ bitstring |= ((ni_readb(dev, XXX_Status) & PROMOUT) ? bit : 0);
+ }
+ ni_writeb(dev, 0x00, Serial_Command);
+
+ return bitstring;
+}
+
+static int ni_eeprom_insn_read(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ data[0] = ni_read_eeprom(dev, CR_CHAN(insn->chanspec));
+
+ return 1;
+}
+
+static int ni_m_series_eeprom_insn_read(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ struct ni_private *devpriv = dev->private;
+
+ data[0] = devpriv->eeprom_buffer[CR_CHAN(insn->chanspec)];
+
+ return 1;
+}
+
+static unsigned ni_old_get_pfi_routing(struct comedi_device *dev,
+ unsigned chan)
+{
+ /* pre-m-series boards have fixed signals on pfi pins */
+ switch (chan) {
+ case 0:
+ return NI_PFI_OUTPUT_AI_START1;
+ case 1:
+ return NI_PFI_OUTPUT_AI_START2;
+ case 2:
+ return NI_PFI_OUTPUT_AI_CONVERT;
+ case 3:
+ return NI_PFI_OUTPUT_G_SRC1;
+ case 4:
+ return NI_PFI_OUTPUT_G_GATE1;
+ case 5:
+ return NI_PFI_OUTPUT_AO_UPDATE_N;
+ case 6:
+ return NI_PFI_OUTPUT_AO_START1;
+ case 7:
+ return NI_PFI_OUTPUT_AI_START_PULSE;
+ case 8:
+ return NI_PFI_OUTPUT_G_SRC0;
+ case 9:
+ return NI_PFI_OUTPUT_G_GATE0;
+ default:
+ dev_err(dev->class_dev, "bug, unhandled case in switch.\n");
+ break;
+ }
+ return 0;
+}
+
+static int ni_old_set_pfi_routing(struct comedi_device *dev,
+ unsigned chan, unsigned source)
+{
+ /* pre-m-series boards have fixed signals on pfi pins */
+ if (source != ni_old_get_pfi_routing(dev, chan))
+ return -EINVAL;
+ return 2;
+}
+
+static unsigned ni_m_series_get_pfi_routing(struct comedi_device *dev,
+ unsigned chan)
+{
+ struct ni_private *devpriv = dev->private;
+ const unsigned array_offset = chan / 3;
+
+ return MSeries_PFI_Output_Select_Source(chan,
+ devpriv->pfi_output_select_reg[array_offset]);
+}
+
+static int ni_m_series_set_pfi_routing(struct comedi_device *dev,
+ unsigned chan, unsigned source)
+{
+ struct ni_private *devpriv = dev->private;
+ unsigned pfi_reg_index;
+ unsigned array_offset;
+
+ if ((source & 0x1f) != source)
+ return -EINVAL;
+ pfi_reg_index = 1 + chan / 3;
+ array_offset = pfi_reg_index - 1;
+ devpriv->pfi_output_select_reg[array_offset] &=
+ ~MSeries_PFI_Output_Select_Mask(chan);
+ devpriv->pfi_output_select_reg[array_offset] |=
+ MSeries_PFI_Output_Select_Bits(chan, source);
+ ni_writew(dev, devpriv->pfi_output_select_reg[array_offset],
+ M_Offset_PFI_Output_Select(pfi_reg_index));
+ return 2;
+}
+
+static unsigned ni_get_pfi_routing(struct comedi_device *dev, unsigned chan)
+{
+ struct ni_private *devpriv = dev->private;
+
+ return (devpriv->is_m_series)
+ ? ni_m_series_get_pfi_routing(dev, chan)
+ : ni_old_get_pfi_routing(dev, chan);
+}
+
+static int ni_set_pfi_routing(struct comedi_device *dev, unsigned chan,
+ unsigned source)
+{
+ struct ni_private *devpriv = dev->private;
+
+ return (devpriv->is_m_series)
+ ? ni_m_series_set_pfi_routing(dev, chan, source)
+ : ni_old_set_pfi_routing(dev, chan, source);
+}
+
+static int ni_config_filter(struct comedi_device *dev,
+ unsigned pfi_channel,
+ enum ni_pfi_filter_select filter)
+{
+ struct ni_private *devpriv = dev->private;
+ unsigned bits;
+
+ if (!devpriv->is_m_series)
+ return -ENOTSUPP;
+
+ bits = ni_readl(dev, M_Offset_PFI_Filter);
+ bits &= ~MSeries_PFI_Filter_Select_Mask(pfi_channel);
+ bits |= MSeries_PFI_Filter_Select_Bits(pfi_channel, filter);
+ ni_writel(dev, bits, M_Offset_PFI_Filter);
+ return 0;
+}
+
+static int ni_pfi_insn_config(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ struct ni_private *devpriv = dev->private;
+ unsigned int chan;
+
+ if (insn->n < 1)
+ return -EINVAL;
+
+ chan = CR_CHAN(insn->chanspec);
+
+ switch (data[0]) {
+ case COMEDI_OUTPUT:
+ ni_set_bits(dev, IO_Bidirection_Pin_Register, 1 << chan, 1);
+ break;
+ case COMEDI_INPUT:
+ ni_set_bits(dev, IO_Bidirection_Pin_Register, 1 << chan, 0);
+ break;
+ case INSN_CONFIG_DIO_QUERY:
+ data[1] =
+ (devpriv->io_bidirection_pin_reg & (1 << chan)) ?
+ COMEDI_OUTPUT : COMEDI_INPUT;
+ return 0;
+ case INSN_CONFIG_SET_ROUTING:
+ return ni_set_pfi_routing(dev, chan, data[1]);
+ case INSN_CONFIG_GET_ROUTING:
+ data[1] = ni_get_pfi_routing(dev, chan);
+ break;
+ case INSN_CONFIG_FILTER:
+ return ni_config_filter(dev, chan, data[1]);
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int ni_pfi_insn_bits(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ struct ni_private *devpriv = dev->private;
+
+ if (!devpriv->is_m_series)
+ return -ENOTSUPP;
+
+ if (comedi_dio_update_state(s, data))
+ ni_writew(dev, s->state, M_Offset_PFI_DO);
+
+ data[1] = ni_readw(dev, M_Offset_PFI_DI);
+
+ return insn->n;
+}
+
+static int cs5529_wait_for_idle(struct comedi_device *dev)
+{
+ unsigned short status;
+ const int timeout = HZ;
+ int i;
+
+ for (i = 0; i < timeout; i++) {
+ status = ni_ao_win_inw(dev, CAL_ADC_Status_67xx);
+ if ((status & CSS_ADC_BUSY) == 0)
+ break;
+ set_current_state(TASK_INTERRUPTIBLE);
+ if (schedule_timeout(1))
+ return -EIO;
+ }
+ if (i == timeout) {
+ dev_err(dev->class_dev, "timeout\n");
+ return -ETIME;
+ }
+ return 0;
+}
+
+static void cs5529_command(struct comedi_device *dev, unsigned short value)
+{
+ static const int timeout = 100;
+ int i;
+
+ ni_ao_win_outw(dev, value, CAL_ADC_Command_67xx);
+ /* give time for command to start being serially clocked into cs5529.
+ * this insures that the CSS_ADC_BUSY bit will get properly
+ * set before we exit this function.
+ */
+ for (i = 0; i < timeout; i++) {
+ if ((ni_ao_win_inw(dev, CAL_ADC_Status_67xx) & CSS_ADC_BUSY))
+ break;
+ udelay(1);
+ }
+ if (i == timeout)
+ dev_err(dev->class_dev,
+ "possible problem - never saw adc go busy?\n");
+}
+
+static int cs5529_do_conversion(struct comedi_device *dev,
+ unsigned short *data)
+{
+ int retval;
+ unsigned short status;
+
+ cs5529_command(dev, CSCMD_COMMAND | CSCMD_SINGLE_CONVERSION);
+ retval = cs5529_wait_for_idle(dev);
+ if (retval) {
+ dev_err(dev->class_dev,
+ "timeout or signal in cs5529_do_conversion()\n");
+ return -ETIME;
+ }
+ status = ni_ao_win_inw(dev, CAL_ADC_Status_67xx);
+ if (status & CSS_OSC_DETECT) {
+ dev_err(dev->class_dev,
+ "cs5529 conversion error, status CSS_OSC_DETECT\n");
+ return -EIO;
+ }
+ if (status & CSS_OVERRANGE) {
+ dev_err(dev->class_dev,
+ "cs5529 conversion error, overrange (ignoring)\n");
+ }
+ if (data) {
+ *data = ni_ao_win_inw(dev, CAL_ADC_Data_67xx);
+ /* cs5529 returns 16 bit signed data in bipolar mode */
+ *data ^= (1 << 15);
+ }
+ return 0;
+}
+
+static int cs5529_ai_insn_read(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ int n, retval;
+ unsigned short sample;
+ unsigned int channel_select;
+ const unsigned int INTERNAL_REF = 0x1000;
+
+ /* Set calibration adc source. Docs lie, reference select bits 8 to 11
+ * do nothing. bit 12 seems to chooses internal reference voltage, bit
+ * 13 causes the adc input to go overrange (maybe reads external reference?) */
+ if (insn->chanspec & CR_ALT_SOURCE)
+ channel_select = INTERNAL_REF;
+ else
+ channel_select = CR_CHAN(insn->chanspec);
+ ni_ao_win_outw(dev, channel_select, AO_Calibration_Channel_Select_67xx);
+
+ for (n = 0; n < insn->n; n++) {
+ retval = cs5529_do_conversion(dev, &sample);
+ if (retval < 0)
+ return retval;
+ data[n] = sample;
+ }
+ return insn->n;
+}
+
+static void cs5529_config_write(struct comedi_device *dev, unsigned int value,
+ unsigned int reg_select_bits)
+{
+ ni_ao_win_outw(dev, ((value >> 16) & 0xff),
+ CAL_ADC_Config_Data_High_Word_67xx);
+ ni_ao_win_outw(dev, (value & 0xffff),
+ CAL_ADC_Config_Data_Low_Word_67xx);
+ reg_select_bits &= CSCMD_REGISTER_SELECT_MASK;
+ cs5529_command(dev, CSCMD_COMMAND | reg_select_bits);
+ if (cs5529_wait_for_idle(dev))
+ dev_err(dev->class_dev,
+ "timeout or signal in %s\n", __func__);
+}
+
+static int init_cs5529(struct comedi_device *dev)
+{
+ unsigned int config_bits =
+ CSCFG_PORT_MODE | CSCFG_WORD_RATE_2180_CYCLES;
+
+#if 1
+ /* do self-calibration */
+ cs5529_config_write(dev, config_bits | CSCFG_SELF_CAL_OFFSET_GAIN,
+ CSCMD_CONFIG_REGISTER);
+ /* need to force a conversion for calibration to run */
+ cs5529_do_conversion(dev, NULL);
+#else
+ /* force gain calibration to 1 */
+ cs5529_config_write(dev, 0x400000, CSCMD_GAIN_REGISTER);
+ cs5529_config_write(dev, config_bits | CSCFG_SELF_CAL_OFFSET,
+ CSCMD_CONFIG_REGISTER);
+ if (cs5529_wait_for_idle(dev))
+ dev_err(dev->class_dev,
+ "timeout or signal in %s\n", __func__);
+#endif
+ return 0;
+}
+
+/*
+ * Find best multiplier/divider to try and get the PLL running at 80 MHz
+ * given an arbitrary frequency input clock.
+ */
+static int ni_mseries_get_pll_parameters(unsigned reference_period_ns,
+ unsigned *freq_divider,
+ unsigned *freq_multiplier,
+ unsigned *actual_period_ns)
+{
+ unsigned div;
+ unsigned best_div = 1;
+ static const unsigned max_div = 0x10;
+ unsigned mult;
+ unsigned best_mult = 1;
+ static const unsigned max_mult = 0x100;
+ static const unsigned pico_per_nano = 1000;
+
+ const unsigned reference_picosec = reference_period_ns * pico_per_nano;
+ /* m-series wants the phased-locked loop to output 80MHz, which is divided by 4 to
+ * 20 MHz for most timing clocks */
+ static const unsigned target_picosec = 12500;
+ static const unsigned fudge_factor_80_to_20Mhz = 4;
+ int best_period_picosec = 0;
+
+ for (div = 1; div <= max_div; ++div) {
+ for (mult = 1; mult <= max_mult; ++mult) {
+ unsigned new_period_ps =
+ (reference_picosec * div) / mult;
+ if (abs(new_period_ps - target_picosec) <
+ abs(best_period_picosec - target_picosec)) {
+ best_period_picosec = new_period_ps;
+ best_div = div;
+ best_mult = mult;
+ }
+ }
+ }
+ if (best_period_picosec == 0)
+ return -EIO;
+
+ *freq_divider = best_div;
+ *freq_multiplier = best_mult;
+ *actual_period_ns =
+ (best_period_picosec * fudge_factor_80_to_20Mhz +
+ (pico_per_nano / 2)) / pico_per_nano;
+ return 0;
+}
+
+static int ni_mseries_set_pll_master_clock(struct comedi_device *dev,
+ unsigned source, unsigned period_ns)
+{
+ struct ni_private *devpriv = dev->private;
+ static const unsigned min_period_ns = 50;
+ static const unsigned max_period_ns = 1000;
+ static const unsigned timeout = 1000;
+ unsigned pll_control_bits;
+ unsigned freq_divider;
+ unsigned freq_multiplier;
+ unsigned i;
+ int retval;
+
+ if (source == NI_MIO_PLL_PXI10_CLOCK)
+ period_ns = 100;
+ /* these limits are somewhat arbitrary, but NI advertises 1 to 20MHz range so we'll use that */
+ if (period_ns < min_period_ns || period_ns > max_period_ns) {
+ dev_err(dev->class_dev,
+ "%s: you must specify an input clock frequency between %i and %i nanosec for the phased-lock loop\n",
+ __func__, min_period_ns, max_period_ns);
+ return -EINVAL;
+ }
+ devpriv->rtsi_trig_direction_reg &= ~Use_RTSI_Clock_Bit;
+ ni_stc_writew(dev, devpriv->rtsi_trig_direction_reg,
+ RTSI_Trig_Direction_Register);
+ pll_control_bits =
+ MSeries_PLL_Enable_Bit | MSeries_PLL_VCO_Mode_75_150MHz_Bits;
+ devpriv->clock_and_fout2 |=
+ MSeries_Timebase1_Select_Bit | MSeries_Timebase3_Select_Bit;
+ devpriv->clock_and_fout2 &= ~MSeries_PLL_In_Source_Select_Mask;
+ switch (source) {
+ case NI_MIO_PLL_PXI_STAR_TRIGGER_CLOCK:
+ devpriv->clock_and_fout2 |=
+ MSeries_PLL_In_Source_Select_Star_Trigger_Bits;
+ break;
+ case NI_MIO_PLL_PXI10_CLOCK:
+ /* pxi clock is 10MHz */
+ devpriv->clock_and_fout2 |=
+ MSeries_PLL_In_Source_Select_PXI_Clock10;
+ break;
+ default:
+ {
+ unsigned rtsi_channel;
+ static const unsigned max_rtsi_channel = 7;
+
+ for (rtsi_channel = 0; rtsi_channel <= max_rtsi_channel;
+ ++rtsi_channel) {
+ if (source ==
+ NI_MIO_PLL_RTSI_CLOCK(rtsi_channel)) {
+ devpriv->clock_and_fout2 |=
+ MSeries_PLL_In_Source_Select_RTSI_Bits
+ (rtsi_channel);
+ break;
+ }
+ }
+ if (rtsi_channel > max_rtsi_channel)
+ return -EINVAL;
+ }
+ break;
+ }
+ retval = ni_mseries_get_pll_parameters(period_ns,
+ &freq_divider,
+ &freq_multiplier,
+ &devpriv->clock_ns);
+ if (retval < 0) {
+ dev_err(dev->class_dev,
+ "bug, failed to find pll parameters\n");
+ return retval;
+ }
+
+ ni_writew(dev, devpriv->clock_and_fout2, M_Offset_Clock_and_Fout2);
+ pll_control_bits |=
+ MSeries_PLL_Divisor_Bits(freq_divider) |
+ MSeries_PLL_Multiplier_Bits(freq_multiplier);
+
+ ni_writew(dev, pll_control_bits, M_Offset_PLL_Control);
+ devpriv->clock_source = source;
+ /* it seems to typically take a few hundred microseconds for PLL to lock */
+ for (i = 0; i < timeout; ++i) {
+ if (ni_readw(dev, M_Offset_PLL_Status) & MSeries_PLL_Locked_Bit)
+ break;
+ udelay(1);
+ }
+ if (i == timeout) {
+ dev_err(dev->class_dev,
+ "%s: timed out waiting for PLL to lock to reference clock source %i with period %i ns\n",
+ __func__, source, period_ns);
+ return -ETIMEDOUT;
+ }
+ return 3;
+}
+
+static int ni_set_master_clock(struct comedi_device *dev,
+ unsigned source, unsigned period_ns)
+{
+ struct ni_private *devpriv = dev->private;
+
+ if (source == NI_MIO_INTERNAL_CLOCK) {
+ devpriv->rtsi_trig_direction_reg &= ~Use_RTSI_Clock_Bit;
+ ni_stc_writew(dev, devpriv->rtsi_trig_direction_reg,
+ RTSI_Trig_Direction_Register);
+ devpriv->clock_ns = TIMEBASE_1_NS;
+ if (devpriv->is_m_series) {
+ devpriv->clock_and_fout2 &=
+ ~(MSeries_Timebase1_Select_Bit |
+ MSeries_Timebase3_Select_Bit);
+ ni_writew(dev, devpriv->clock_and_fout2,
+ M_Offset_Clock_and_Fout2);
+ ni_writew(dev, 0, M_Offset_PLL_Control);
+ }
+ devpriv->clock_source = source;
+ } else {
+ if (devpriv->is_m_series) {
+ return ni_mseries_set_pll_master_clock(dev, source,
+ period_ns);
+ } else {
+ if (source == NI_MIO_RTSI_CLOCK) {
+ devpriv->rtsi_trig_direction_reg |=
+ Use_RTSI_Clock_Bit;
+ ni_stc_writew(dev,
+ devpriv->rtsi_trig_direction_reg,
+ RTSI_Trig_Direction_Register);
+ if (period_ns == 0) {
+ dev_err(dev->class_dev,
+ "we don't handle an unspecified clock period correctly yet, returning error\n");
+ return -EINVAL;
+ }
+ devpriv->clock_ns = period_ns;
+ devpriv->clock_source = source;
+ } else {
+ return -EINVAL;
+ }
+ }
+ }
+ return 3;
+}
+
+static unsigned num_configurable_rtsi_channels(struct comedi_device *dev)
+{
+ struct ni_private *devpriv = dev->private;
+
+ return (devpriv->is_m_series) ? 8 : 7;
+}
+
+static int ni_valid_rtsi_output_source(struct comedi_device *dev,
+ unsigned chan, unsigned source)
+{
+ struct ni_private *devpriv = dev->private;
+
+ if (chan >= num_configurable_rtsi_channels(dev)) {
+ if (chan == old_RTSI_clock_channel) {
+ if (source == NI_RTSI_OUTPUT_RTSI_OSC)
+ return 1;
+
+ dev_err(dev->class_dev,
+ "%s: invalid source for channel=%i, channel %i is always the RTSI clock for pre-m-series boards\n",
+ __func__, chan, old_RTSI_clock_channel);
+ return 0;
+ }
+ return 0;
+ }
+ switch (source) {
+ case NI_RTSI_OUTPUT_ADR_START1:
+ case NI_RTSI_OUTPUT_ADR_START2:
+ case NI_RTSI_OUTPUT_SCLKG:
+ case NI_RTSI_OUTPUT_DACUPDN:
+ case NI_RTSI_OUTPUT_DA_START1:
+ case NI_RTSI_OUTPUT_G_SRC0:
+ case NI_RTSI_OUTPUT_G_GATE0:
+ case NI_RTSI_OUTPUT_RGOUT0:
+ case NI_RTSI_OUTPUT_RTSI_BRD_0:
+ return 1;
+ case NI_RTSI_OUTPUT_RTSI_OSC:
+ return (devpriv->is_m_series) ? 1 : 0;
+ default:
+ return 0;
+ }
+}
+
+static int ni_set_rtsi_routing(struct comedi_device *dev,
+ unsigned chan, unsigned source)
+{
+ struct ni_private *devpriv = dev->private;
+
+ if (ni_valid_rtsi_output_source(dev, chan, source) == 0)
+ return -EINVAL;
+ if (chan < 4) {
+ devpriv->rtsi_trig_a_output_reg &= ~RTSI_Trig_Output_Mask(chan);
+ devpriv->rtsi_trig_a_output_reg |=
+ RTSI_Trig_Output_Bits(chan, source);
+ ni_stc_writew(dev, devpriv->rtsi_trig_a_output_reg,
+ RTSI_Trig_A_Output_Register);
+ } else if (chan < 8) {
+ devpriv->rtsi_trig_b_output_reg &= ~RTSI_Trig_Output_Mask(chan);
+ devpriv->rtsi_trig_b_output_reg |=
+ RTSI_Trig_Output_Bits(chan, source);
+ ni_stc_writew(dev, devpriv->rtsi_trig_b_output_reg,
+ RTSI_Trig_B_Output_Register);
+ }
+ return 2;
+}
+
+static unsigned ni_get_rtsi_routing(struct comedi_device *dev, unsigned chan)
+{
+ struct ni_private *devpriv = dev->private;
+
+ if (chan < 4) {
+ return RTSI_Trig_Output_Source(chan,
+ devpriv->rtsi_trig_a_output_reg);
+ } else if (chan < num_configurable_rtsi_channels(dev)) {
+ return RTSI_Trig_Output_Source(chan,
+ devpriv->rtsi_trig_b_output_reg);
+ } else {
+ if (chan == old_RTSI_clock_channel)
+ return NI_RTSI_OUTPUT_RTSI_OSC;
+ dev_err(dev->class_dev, "bug! should never get here?\n");
+ return 0;
+ }
+}
+
+static int ni_rtsi_insn_config(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ struct ni_private *devpriv = dev->private;
+ unsigned int chan = CR_CHAN(insn->chanspec);
+
+ switch (data[0]) {
+ case INSN_CONFIG_DIO_OUTPUT:
+ if (chan < num_configurable_rtsi_channels(dev)) {
+ devpriv->rtsi_trig_direction_reg |=
+ RTSI_Output_Bit(chan, devpriv->is_m_series);
+ } else if (chan == old_RTSI_clock_channel) {
+ devpriv->rtsi_trig_direction_reg |=
+ Drive_RTSI_Clock_Bit;
+ }
+ ni_stc_writew(dev, devpriv->rtsi_trig_direction_reg,
+ RTSI_Trig_Direction_Register);
+ break;
+ case INSN_CONFIG_DIO_INPUT:
+ if (chan < num_configurable_rtsi_channels(dev)) {
+ devpriv->rtsi_trig_direction_reg &=
+ ~RTSI_Output_Bit(chan, devpriv->is_m_series);
+ } else if (chan == old_RTSI_clock_channel) {
+ devpriv->rtsi_trig_direction_reg &=
+ ~Drive_RTSI_Clock_Bit;
+ }
+ ni_stc_writew(dev, devpriv->rtsi_trig_direction_reg,
+ RTSI_Trig_Direction_Register);
+ break;
+ case INSN_CONFIG_DIO_QUERY:
+ if (chan < num_configurable_rtsi_channels(dev)) {
+ data[1] =
+ (devpriv->rtsi_trig_direction_reg &
+ RTSI_Output_Bit(chan, devpriv->is_m_series))
+ ? INSN_CONFIG_DIO_OUTPUT
+ : INSN_CONFIG_DIO_INPUT;
+ } else if (chan == old_RTSI_clock_channel) {
+ data[1] =
+ (devpriv->rtsi_trig_direction_reg &
+ Drive_RTSI_Clock_Bit)
+ ? INSN_CONFIG_DIO_OUTPUT : INSN_CONFIG_DIO_INPUT;
+ }
+ return 2;
+ case INSN_CONFIG_SET_CLOCK_SRC:
+ return ni_set_master_clock(dev, data[1], data[2]);
+ case INSN_CONFIG_GET_CLOCK_SRC:
+ data[1] = devpriv->clock_source;
+ data[2] = devpriv->clock_ns;
+ return 3;
+ case INSN_CONFIG_SET_ROUTING:
+ return ni_set_rtsi_routing(dev, chan, data[1]);
+ case INSN_CONFIG_GET_ROUTING:
+ data[1] = ni_get_rtsi_routing(dev, chan);
+ return 2;
+ default:
+ return -EINVAL;
+ }
+ return 1;
+}
+
+static int ni_rtsi_insn_bits(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ data[1] = 0;
+
+ return insn->n;
+}
+
+static void ni_rtsi_init(struct comedi_device *dev)
+{
+ struct ni_private *devpriv = dev->private;
+
+ /* Initialises the RTSI bus signal switch to a default state */
+
+ /* Set clock mode to internal */
+ devpriv->clock_and_fout2 = MSeries_RTSI_10MHz_Bit;
+ if (ni_set_master_clock(dev, NI_MIO_INTERNAL_CLOCK, 0) < 0)
+ dev_err(dev->class_dev, "ni_set_master_clock failed, bug?\n");
+ /* default internal lines routing to RTSI bus lines */
+ devpriv->rtsi_trig_a_output_reg =
+ RTSI_Trig_Output_Bits(0,
+ NI_RTSI_OUTPUT_ADR_START1) |
+ RTSI_Trig_Output_Bits(1,
+ NI_RTSI_OUTPUT_ADR_START2) |
+ RTSI_Trig_Output_Bits(2,
+ NI_RTSI_OUTPUT_SCLKG) |
+ RTSI_Trig_Output_Bits(3, NI_RTSI_OUTPUT_DACUPDN);
+ ni_stc_writew(dev, devpriv->rtsi_trig_a_output_reg,
+ RTSI_Trig_A_Output_Register);
+ devpriv->rtsi_trig_b_output_reg =
+ RTSI_Trig_Output_Bits(4,
+ NI_RTSI_OUTPUT_DA_START1) |
+ RTSI_Trig_Output_Bits(5,
+ NI_RTSI_OUTPUT_G_SRC0) |
+ RTSI_Trig_Output_Bits(6, NI_RTSI_OUTPUT_G_GATE0);
+ if (devpriv->is_m_series)
+ devpriv->rtsi_trig_b_output_reg |=
+ RTSI_Trig_Output_Bits(7, NI_RTSI_OUTPUT_RTSI_OSC);
+ ni_stc_writew(dev, devpriv->rtsi_trig_b_output_reg,
+ RTSI_Trig_B_Output_Register);
+
+/*
+* Sets the source and direction of the 4 on board lines
+* ni_stc_writew(dev, 0x0000, RTSI_Board_Register);
+*/
+}
+
+#ifdef PCIDMA
+static int ni_gpct_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
+{
+ struct ni_gpct *counter = s->private;
+ int retval;
+
+ retval = ni_request_gpct_mite_channel(dev, counter->counter_index,
+ COMEDI_INPUT);
+ if (retval) {
+ dev_err(dev->class_dev,
+ "no dma channel available for use by counter\n");
+ return retval;
+ }
+ ni_tio_acknowledge(counter);
+ ni_e_series_enable_second_irq(dev, counter->counter_index, 1);
+
+ return ni_tio_cmd(dev, s);
+}
+
+static int ni_gpct_cancel(struct comedi_device *dev, struct comedi_subdevice *s)
+{
+ struct ni_gpct *counter = s->private;
+ int retval;
+
+ retval = ni_tio_cancel(counter);
+ ni_e_series_enable_second_irq(dev, counter->counter_index, 0);
+ ni_release_gpct_mite_channel(dev, counter->counter_index);
+ return retval;
+}
+#endif
+
+#if 0
+/*
+ * Read the GPCTs current value.
+ */
+static int GPCT_G_Watch(struct comedi_device *dev, int chan)
+{
+ unsigned int hi1, hi2, lo;
+
+ devpriv->gpct_command[chan] &= ~G_Save_Trace;
+ ni_stc_writew(dev, devpriv->gpct_command[chan],
+ G_Command_Register(chan));
+
+ devpriv->gpct_command[chan] |= G_Save_Trace;
+ ni_stc_writew(dev, devpriv->gpct_command[chan],
+ G_Command_Register(chan));
+
+ /* This procedure is used because the two registers cannot
+ * be read atomically. */
+ do {
+ hi1 = ni_stc_readw(dev, G_Save_Register_High(chan));
+ lo = ni_stc_readw(dev, G_Save_Register_Low(chan));
+ hi2 = ni_stc_readw(dev, G_Save_Register_High(chan));
+ } while (hi1 != hi2);
+
+ return (hi1 << 16) | lo;
+}
+
+static void GPCT_Reset(struct comedi_device *dev, int chan)
+{
+ int temp_ack_reg = 0;
+
+ devpriv->gpct_cur_operation[chan] = GPCT_RESET;
+
+ switch (chan) {
+ case 0:
+ ni_stc_writew(dev, G0_Reset, Joint_Reset_Register);
+ ni_set_bits(dev, Interrupt_A_Enable_Register,
+ G0_TC_Interrupt_Enable, 0);
+ ni_set_bits(dev, Interrupt_A_Enable_Register,
+ G0_Gate_Interrupt_Enable, 0);
+ temp_ack_reg |= G0_Gate_Error_Confirm;
+ temp_ack_reg |= G0_TC_Error_Confirm;
+ temp_ack_reg |= G0_TC_Interrupt_Ack;
+ temp_ack_reg |= G0_Gate_Interrupt_Ack;
+ ni_stc_writew(dev, temp_ack_reg, Interrupt_A_Ack_Register);
+
+ /* problem...this interferes with the other ctr... */
+ devpriv->an_trig_etc_reg |= GPFO_0_Output_Enable;
+ ni_stc_writew(dev, devpriv->an_trig_etc_reg,
+ Analog_Trigger_Etc_Register);
+ break;
+ case 1:
+ ni_stc_writew(dev, G1_Reset, Joint_Reset_Register);
+ ni_set_bits(dev, Interrupt_B_Enable_Register,
+ G1_TC_Interrupt_Enable, 0);
+ ni_set_bits(dev, Interrupt_B_Enable_Register,
+ G0_Gate_Interrupt_Enable, 0);
+ temp_ack_reg |= G1_Gate_Error_Confirm;
+ temp_ack_reg |= G1_TC_Error_Confirm;
+ temp_ack_reg |= G1_TC_Interrupt_Ack;
+ temp_ack_reg |= G1_Gate_Interrupt_Ack;
+ ni_stc_writew(dev, temp_ack_reg, Interrupt_B_Ack_Register);
+
+ devpriv->an_trig_etc_reg |= GPFO_1_Output_Enable;
+ ni_stc_writew(dev, devpriv->an_trig_etc_reg,
+ Analog_Trigger_Etc_Register);
+ break;
+ }
+
+ devpriv->gpct_mode[chan] = 0;
+ devpriv->gpct_input_select[chan] = 0;
+ devpriv->gpct_command[chan] = 0;
+
+ devpriv->gpct_command[chan] |= G_Synchronized_Gate;
+
+ ni_stc_writew(dev, devpriv->gpct_mode[chan], G_Mode_Register(chan));
+ ni_stc_writew(dev, devpriv->gpct_input_select[chan],
+ G_Input_Select_Register(chan));
+ ni_stc_writew(dev, 0, G_Autoincrement_Register(chan));
+}
+#endif
+
+static irqreturn_t ni_E_interrupt(int irq, void *d)
+{
+ struct comedi_device *dev = d;
+ unsigned short a_status;
+ unsigned short b_status;
+ unsigned int ai_mite_status = 0;
+ unsigned int ao_mite_status = 0;
+ unsigned long flags;
+#ifdef PCIDMA
+ struct ni_private *devpriv = dev->private;
+ struct mite_struct *mite = devpriv->mite;
+#endif
+
+ if (!dev->attached)
+ return IRQ_NONE;
+ smp_mb(); /* make sure dev->attached is checked before handler does anything else. */
+
+ /* lock to avoid race with comedi_poll */
+ spin_lock_irqsave(&dev->spinlock, flags);
+ a_status = ni_stc_readw(dev, AI_Status_1_Register);
+ b_status = ni_stc_readw(dev, AO_Status_1_Register);
+#ifdef PCIDMA
+ if (mite) {
+ struct ni_private *devpriv = dev->private;
+ unsigned long flags_too;
+
+ spin_lock_irqsave(&devpriv->mite_channel_lock, flags_too);
+ if (devpriv->ai_mite_chan) {
+ ai_mite_status = mite_get_status(devpriv->ai_mite_chan);
+ if (ai_mite_status & CHSR_LINKC)
+ writel(CHOR_CLRLC,
+ devpriv->mite->mite_io_addr +
+ MITE_CHOR(devpriv->
+ ai_mite_chan->channel));
+ }
+ if (devpriv->ao_mite_chan) {
+ ao_mite_status = mite_get_status(devpriv->ao_mite_chan);
+ if (ao_mite_status & CHSR_LINKC)
+ writel(CHOR_CLRLC,
+ mite->mite_io_addr +
+ MITE_CHOR(devpriv->
+ ao_mite_chan->channel));
+ }
+ spin_unlock_irqrestore(&devpriv->mite_channel_lock, flags_too);
+ }
+#endif
+ ack_a_interrupt(dev, a_status);
+ ack_b_interrupt(dev, b_status);
+ if ((a_status & Interrupt_A_St) || (ai_mite_status & CHSR_INT))
+ handle_a_interrupt(dev, a_status, ai_mite_status);
+ if ((b_status & Interrupt_B_St) || (ao_mite_status & CHSR_INT))
+ handle_b_interrupt(dev, b_status, ao_mite_status);
+ handle_gpct_interrupt(dev, 0);
+ handle_gpct_interrupt(dev, 1);
+ handle_cdio_interrupt(dev);
+
+ spin_unlock_irqrestore(&dev->spinlock, flags);
+ return IRQ_HANDLED;
+}
+
+static int ni_alloc_private(struct comedi_device *dev)
+{
+ struct ni_private *devpriv;
+
+ devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
+ if (!devpriv)
+ return -ENOMEM;
+
+ spin_lock_init(&devpriv->window_lock);
+ spin_lock_init(&devpriv->soft_reg_copy_lock);
+ spin_lock_init(&devpriv->mite_channel_lock);
+
+ return 0;
+}
+
+static int ni_E_init(struct comedi_device *dev,
+ unsigned interrupt_pin, unsigned irq_polarity)
+{
+ const struct ni_board_struct *board = dev->board_ptr;
+ struct ni_private *devpriv = dev->private;
+ struct comedi_subdevice *s;
+ int ret;
+ int i;
+
+ if (board->n_aochan > MAX_N_AO_CHAN) {
+ dev_err(dev->class_dev, "bug! n_aochan > MAX_N_AO_CHAN\n");
+ return -EINVAL;
+ }
+
+ /* initialize clock dividers */
+ devpriv->clock_and_fout = Slow_Internal_Time_Divide_By_2 |
+ Slow_Internal_Timebase |
+ Clock_To_Board_Divide_By_2 |
+ Clock_To_Board;
+ if (!devpriv->is_6xxx) {
+ /* BEAM is this needed for PCI-6143 ?? */
+ devpriv->clock_and_fout |= (AI_Output_Divide_By_2 |
+ AO_Output_Divide_By_2);
+ }
+ ni_stc_writew(dev, devpriv->clock_and_fout, Clock_and_FOUT_Register);
+
+ ret = comedi_alloc_subdevices(dev, NI_NUM_SUBDEVICES);
+ if (ret)
+ return ret;
+
+ /* Analog Input subdevice */
+ s = &dev->subdevices[NI_AI_SUBDEV];
+ if (board->n_adchan) {
+ s->type = COMEDI_SUBD_AI;
+ s->subdev_flags = SDF_READABLE | SDF_DIFF | SDF_DITHER;
+ if (!devpriv->is_611x)
+ s->subdev_flags |= SDF_GROUND | SDF_COMMON | SDF_OTHER;
+ if (board->ai_maxdata > 0xffff)
+ s->subdev_flags |= SDF_LSAMPL;
+ if (devpriv->is_m_series)
+ s->subdev_flags |= SDF_SOFT_CALIBRATED;
+ s->n_chan = board->n_adchan;
+ s->maxdata = board->ai_maxdata;
+ s->range_table = ni_range_lkup[board->gainlkup];
+ s->insn_read = ni_ai_insn_read;
+ s->insn_config = ni_ai_insn_config;
+ if (dev->irq) {
+ dev->read_subdev = s;
+ s->subdev_flags |= SDF_CMD_READ;
+ s->len_chanlist = 512;
+ s->do_cmdtest = ni_ai_cmdtest;
+ s->do_cmd = ni_ai_cmd;
+ s->cancel = ni_ai_reset;
+ s->poll = ni_ai_poll;
+ s->munge = ni_ai_munge;
+
+ if (devpriv->mite)
+ s->async_dma_dir = DMA_FROM_DEVICE;
+ }
+
+ /* reset the analog input configuration */
+ ni_ai_reset(dev, s);
+ } else {
+ s->type = COMEDI_SUBD_UNUSED;
+ }
+
+ /* Analog Output subdevice */
+ s = &dev->subdevices[NI_AO_SUBDEV];
+ if (board->n_aochan) {
+ s->type = COMEDI_SUBD_AO;
+ s->subdev_flags = SDF_WRITABLE | SDF_DEGLITCH | SDF_GROUND;
+ if (devpriv->is_m_series)
+ s->subdev_flags |= SDF_SOFT_CALIBRATED;
+ s->n_chan = board->n_aochan;
+ s->maxdata = board->ao_maxdata;
+ s->range_table = board->ao_range_table;
+ s->insn_config = ni_ao_insn_config;
+ s->insn_write = ni_ao_insn_write;
+
+ ret = comedi_alloc_subdev_readback(s);
+ if (ret)
+ return ret;
+
+ /*
+ * Along with the IRQ we need either a FIFO or DMA for
+ * async command support.
+ */
+ if (dev->irq && (board->ao_fifo_depth || devpriv->mite)) {
+ dev->write_subdev = s;
+ s->subdev_flags |= SDF_CMD_WRITE;
+ s->len_chanlist = s->n_chan;
+ s->do_cmdtest = ni_ao_cmdtest;
+ s->do_cmd = ni_ao_cmd;
+ s->cancel = ni_ao_reset;
+ if (!devpriv->is_m_series)
+ s->munge = ni_ao_munge;
+
+ if (devpriv->mite)
+ s->async_dma_dir = DMA_TO_DEVICE;
+ }
+
+ if (devpriv->is_67xx)
+ init_ao_67xx(dev, s);
+
+ /* reset the analog output configuration */
+ ni_ao_reset(dev, s);
+ } else {
+ s->type = COMEDI_SUBD_UNUSED;
+ }
+
+ /* Digital I/O subdevice */
+ s = &dev->subdevices[NI_DIO_SUBDEV];
+ s->type = COMEDI_SUBD_DIO;
+ s->subdev_flags = SDF_WRITABLE | SDF_READABLE;
+ s->n_chan = board->has_32dio_chan ? 32 : 8;
+ s->maxdata = 1;
+ s->range_table = &range_digital;
+ if (devpriv->is_m_series) {
+ s->subdev_flags |= SDF_LSAMPL;
+ s->insn_bits = ni_m_series_dio_insn_bits;
+ s->insn_config = ni_m_series_dio_insn_config;
+ if (dev->irq) {
+ s->subdev_flags |= SDF_CMD_WRITE /* | SDF_CMD_READ */;
+ s->len_chanlist = s->n_chan;
+ s->do_cmdtest = ni_cdio_cmdtest;
+ s->do_cmd = ni_cdio_cmd;
+ s->cancel = ni_cdio_cancel;
+
+ /* M-series boards use DMA */
+ s->async_dma_dir = DMA_BIDIRECTIONAL;
+ }
+
+ /* reset DIO and set all channels to inputs */
+ ni_writel(dev, CDO_Reset_Bit | CDI_Reset_Bit,
+ M_Offset_CDIO_Command);
+ ni_writel(dev, s->io_bits, M_Offset_DIO_Direction);
+ } else {
+ s->insn_bits = ni_dio_insn_bits;
+ s->insn_config = ni_dio_insn_config;
+
+ /* set all channels to inputs */
+ devpriv->dio_control = DIO_Pins_Dir(s->io_bits);
+ ni_writew(dev, devpriv->dio_control, DIO_Control_Register);
+ }
+
+ /* 8255 device */
+ s = &dev->subdevices[NI_8255_DIO_SUBDEV];
+ if (board->has_8255) {
+ ret = subdev_8255_init(dev, s, ni_8255_callback, Port_A);
+ if (ret)
+ return ret;
+ } else {
+ s->type = COMEDI_SUBD_UNUSED;
+ }
+
+ /* formerly general purpose counter/timer device, but no longer used */
+ s = &dev->subdevices[NI_UNUSED_SUBDEV];
+ s->type = COMEDI_SUBD_UNUSED;
+
+ /* Calibration subdevice */
+ s = &dev->subdevices[NI_CALIBRATION_SUBDEV];
+ s->type = COMEDI_SUBD_CALIB;
+ s->subdev_flags = SDF_INTERNAL;
+ s->n_chan = 1;
+ s->maxdata = 0;
+ if (devpriv->is_m_series) {
+ /* internal PWM output used for AI nonlinearity calibration */
+ s->insn_config = ni_m_series_pwm_config;
+
+ ni_writel(dev, 0x0, M_Offset_Cal_PWM);
+ } else if (devpriv->is_6143) {
+ /* internal PWM output used for AI nonlinearity calibration */
+ s->insn_config = ni_6143_pwm_config;
+ } else {
+ s->subdev_flags |= SDF_WRITABLE;
+ s->insn_read = ni_calib_insn_read;
+ s->insn_write = ni_calib_insn_write;
+
+ /* setup the caldacs and find the real n_chan and maxdata */
+ caldac_setup(dev, s);
+ }
+
+ /* EEPROM subdevice */
+ s = &dev->subdevices[NI_EEPROM_SUBDEV];
+ s->type = COMEDI_SUBD_MEMORY;
+ s->subdev_flags = SDF_READABLE | SDF_INTERNAL;
+ s->maxdata = 0xff;
+ if (devpriv->is_m_series) {
+ s->n_chan = M_SERIES_EEPROM_SIZE;
+ s->insn_read = ni_m_series_eeprom_insn_read;
+ } else {
+ s->n_chan = 512;
+ s->insn_read = ni_eeprom_insn_read;
+ }
+
+ /* Digital I/O (PFI) subdevice */
+ s = &dev->subdevices[NI_PFI_DIO_SUBDEV];
+ s->type = COMEDI_SUBD_DIO;
+ s->subdev_flags = SDF_READABLE | SDF_WRITABLE | SDF_INTERNAL;
+ s->maxdata = 1;
+ if (devpriv->is_m_series) {
+ s->n_chan = 16;
+ s->insn_bits = ni_pfi_insn_bits;
+
+ ni_writew(dev, s->state, M_Offset_PFI_DO);
+ for (i = 0; i < NUM_PFI_OUTPUT_SELECT_REGS; ++i) {
+ ni_writew(dev, devpriv->pfi_output_select_reg[i],
+ M_Offset_PFI_Output_Select(i + 1));
+ }
+ } else {
+ s->n_chan = 10;
+ }
+ s->insn_config = ni_pfi_insn_config;
+
+ ni_set_bits(dev, IO_Bidirection_Pin_Register, ~0, 0);
+
+ /* cs5529 calibration adc */
+ s = &dev->subdevices[NI_CS5529_CALIBRATION_SUBDEV];
+ if (devpriv->is_67xx) {
+ s->type = COMEDI_SUBD_AI;
+ s->subdev_flags = SDF_READABLE | SDF_DIFF | SDF_INTERNAL;
+ /* one channel for each analog output channel */
+ s->n_chan = board->n_aochan;
+ s->maxdata = (1 << 16) - 1;
+ s->range_table = &range_unknown; /* XXX */
+ s->insn_read = cs5529_ai_insn_read;
+ s->insn_config = NULL;
+ init_cs5529(dev);
+ } else {
+ s->type = COMEDI_SUBD_UNUSED;
+ }
+
+ /* Serial */
+ s = &dev->subdevices[NI_SERIAL_SUBDEV];
+ s->type = COMEDI_SUBD_SERIAL;
+ s->subdev_flags = SDF_READABLE | SDF_WRITABLE | SDF_INTERNAL;
+ s->n_chan = 1;
+ s->maxdata = 0xff;
+ s->insn_config = ni_serial_insn_config;
+ devpriv->serial_interval_ns = 0;
+ devpriv->serial_hw_mode = 0;
+
+ /* RTSI */
+ s = &dev->subdevices[NI_RTSI_SUBDEV];
+ s->type = COMEDI_SUBD_DIO;
+ s->subdev_flags = SDF_READABLE | SDF_WRITABLE | SDF_INTERNAL;
+ s->n_chan = 8;
+ s->maxdata = 1;
+ s->insn_bits = ni_rtsi_insn_bits;
+ s->insn_config = ni_rtsi_insn_config;
+ ni_rtsi_init(dev);
+
+ /* allocate and initialize the gpct counter device */
+ devpriv->counter_dev = ni_gpct_device_construct(dev,
+ ni_gpct_write_register,
+ ni_gpct_read_register,
+ (devpriv->is_m_series)
+ ? ni_gpct_variant_m_series
+ : ni_gpct_variant_e_series,
+ NUM_GPCT);
+ if (!devpriv->counter_dev)
+ return -ENOMEM;
+
+ /* Counter (gpct) subdevices */
+ for (i = 0; i < NUM_GPCT; ++i) {
+ struct ni_gpct *gpct = &devpriv->counter_dev->counters[i];
+
+ /* setup and initialize the counter */
+ gpct->chip_index = 0;
+ gpct->counter_index = i;
+ ni_tio_init_counter(gpct);
+
+ s = &dev->subdevices[NI_GPCT_SUBDEV(i)];
+ s->type = COMEDI_SUBD_COUNTER;
+ s->subdev_flags = SDF_READABLE | SDF_WRITABLE | SDF_LSAMPL;
+ s->n_chan = 3;
+ s->maxdata = (devpriv->is_m_series) ? 0xffffffff
+ : 0x00ffffff;
+ s->insn_read = ni_tio_insn_read;
+ s->insn_write = ni_tio_insn_read;
+ s->insn_config = ni_tio_insn_config;
+#ifdef PCIDMA
+ if (dev->irq && devpriv->mite) {
+ s->subdev_flags |= SDF_CMD_READ /* | SDF_CMD_WRITE */;
+ s->len_chanlist = 1;
+ s->do_cmdtest = ni_tio_cmdtest;
+ s->do_cmd = ni_gpct_cmd;
+ s->cancel = ni_gpct_cancel;
+
+ s->async_dma_dir = DMA_BIDIRECTIONAL;
+ }
+#endif
+ s->private = gpct;
+ }
+
+ /* Frequency output subdevice */
+ s = &dev->subdevices[NI_FREQ_OUT_SUBDEV];
+ s->type = COMEDI_SUBD_COUNTER;
+ s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
+ s->n_chan = 1;
+ s->maxdata = 0xf;
+ s->insn_read = ni_freq_out_insn_read;
+ s->insn_write = ni_freq_out_insn_write;
+ s->insn_config = ni_freq_out_insn_config;
+
+ if (dev->irq) {
+ ni_stc_writew(dev,
+ (irq_polarity ? Interrupt_Output_Polarity : 0) |
+ (Interrupt_Output_On_3_Pins & 0) |
+ Interrupt_A_Enable | Interrupt_B_Enable |
+ Interrupt_A_Output_Select(interrupt_pin) |
+ Interrupt_B_Output_Select(interrupt_pin),
+ Interrupt_Control_Register);
+ }
+
+ /* DMA setup */
+ ni_writeb(dev, devpriv->ai_ao_select_reg, AI_AO_Select);
+ ni_writeb(dev, devpriv->g0_g1_select_reg, G0_G1_Select);
+
+ if (devpriv->is_6xxx) {
+ ni_writeb(dev, 0, Magic_611x);
+ } else if (devpriv->is_m_series) {
+ int channel;
+
+ for (channel = 0; channel < board->n_aochan; ++channel) {
+ ni_writeb(dev, 0xf,
+ M_Offset_AO_Waveform_Order(channel));
+ ni_writeb(dev, 0x0,
+ M_Offset_AO_Reference_Attenuation(channel));
+ }
+ ni_writeb(dev, 0x0, M_Offset_AO_Calibration);
+ }
+
+ return 0;
+}
+
+static void mio_common_detach(struct comedi_device *dev)
+{
+ struct ni_private *devpriv = dev->private;
+
+ if (devpriv) {
+ if (devpriv->counter_dev)
+ ni_gpct_device_destroy(devpriv->counter_dev);
+ }
+}
diff --git a/drivers/staging/comedi/drivers/ni_mio_cs.c b/drivers/staging/comedi/drivers/ni_mio_cs.c
new file mode 100644
index 000000000..e3d821bf2
--- /dev/null
+++ b/drivers/staging/comedi/drivers/ni_mio_cs.c
@@ -0,0 +1,228 @@
+/*
+ comedi/drivers/ni_mio_cs.c
+ Hardware driver for NI PCMCIA MIO E series cards
+
+ COMEDI - Linux Control and Measurement Device Interface
+ Copyright (C) 1997-2000 David A. Schleef <ds@schleef.org>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+*/
+/*
+Driver: ni_mio_cs
+Description: National Instruments DAQCard E series
+Author: ds
+Status: works
+Devices: [National Instruments] DAQCard-AI-16XE-50 (ni_mio_cs),
+ DAQCard-AI-16E-4, DAQCard-6062E, DAQCard-6024E, DAQCard-6036E
+Updated: Thu Oct 23 19:43:17 CDT 2003
+
+See the notes in the ni_atmio.o driver.
+*/
+/*
+ The real guts of the driver is in ni_mio_common.c, which is
+ included by all the E series drivers.
+
+ References for specifications:
+
+ 341080a.pdf DAQCard E Series Register Level Programmer Manual
+
+*/
+
+#include <linux/module.h>
+#include <linux/delay.h>
+
+#include "../comedi_pcmcia.h"
+#include "ni_stc.h"
+#include "8255.h"
+
+/*
+ * AT specific setup
+ */
+
+static const struct ni_board_struct ni_boards[] = {
+ {
+ .name = "DAQCard-ai-16xe-50",
+ .device_id = 0x010d,
+ .n_adchan = 16,
+ .ai_maxdata = 0xffff,
+ .ai_fifo_depth = 1024,
+ .gainlkup = ai_gain_8,
+ .ai_speed = 5000,
+ .caldac = { dac8800, dac8043 },
+ }, {
+ .name = "DAQCard-ai-16e-4",
+ .device_id = 0x010c,
+ .n_adchan = 16,
+ .ai_maxdata = 0x0fff,
+ .ai_fifo_depth = 1024,
+ .gainlkup = ai_gain_16,
+ .ai_speed = 4000,
+ .caldac = { mb88341 }, /* verified */
+ }, {
+ .name = "DAQCard-6062E",
+ .device_id = 0x02c4,
+ .n_adchan = 16,
+ .ai_maxdata = 0x0fff,
+ .ai_fifo_depth = 8192,
+ .gainlkup = ai_gain_16,
+ .ai_speed = 2000,
+ .n_aochan = 2,
+ .ao_maxdata = 0x0fff,
+ .ao_fifo_depth = 2048,
+ .ao_range_table = &range_bipolar10,
+ .ao_speed = 1176,
+ .caldac = { ad8804_debug }, /* verified */
+ }, {
+ /* specs incorrect! */
+ .name = "DAQCard-6024E",
+ .device_id = 0x075e,
+ .n_adchan = 16,
+ .ai_maxdata = 0x0fff,
+ .ai_fifo_depth = 1024,
+ .gainlkup = ai_gain_4,
+ .ai_speed = 5000,
+ .n_aochan = 2,
+ .ao_maxdata = 0x0fff,
+ .ao_range_table = &range_bipolar10,
+ .ao_speed = 1000000,
+ .caldac = { ad8804_debug },
+ }, {
+ /* specs incorrect! */
+ .name = "DAQCard-6036E",
+ .device_id = 0x0245,
+ .n_adchan = 16,
+ .ai_maxdata = 0xffff,
+ .ai_fifo_depth = 1024,
+ .alwaysdither = 1,
+ .gainlkup = ai_gain_4,
+ .ai_speed = 5000,
+ .n_aochan = 2,
+ .ao_maxdata = 0xffff,
+ .ao_range_table = &range_bipolar10,
+ .ao_speed = 1000000,
+ .caldac = { ad8804_debug },
+ },
+#if 0
+ {
+ .name = "DAQCard-6715",
+ .device_id = 0x0000, /* unknown */
+ .n_aochan = 8,
+ .ao_maxdata = 0x0fff,
+ .ao_671x = 8192,
+ .caldac = { mb88341, mb88341 },
+ },
+#endif
+};
+
+#include "ni_mio_common.c"
+
+static const void *ni_getboardtype(struct comedi_device *dev,
+ struct pcmcia_device *link)
+{
+ static const struct ni_board_struct *board;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(ni_boards); i++) {
+ board = &ni_boards[i];
+ if (board->device_id == link->card_id)
+ return board;
+ }
+ return NULL;
+}
+
+static int mio_pcmcia_config_loop(struct pcmcia_device *p_dev, void *priv_data)
+{
+ int base, ret;
+
+ p_dev->resource[0]->flags &= ~IO_DATA_PATH_WIDTH;
+ p_dev->resource[0]->flags |= IO_DATA_PATH_WIDTH_16;
+
+ for (base = 0x000; base < 0x400; base += 0x20) {
+ p_dev->resource[0]->start = base;
+ ret = pcmcia_request_io(p_dev);
+ if (!ret)
+ return 0;
+ }
+ return -ENODEV;
+}
+
+static int mio_cs_auto_attach(struct comedi_device *dev,
+ unsigned long context)
+{
+ struct pcmcia_device *link = comedi_to_pcmcia_dev(dev);
+ static const struct ni_board_struct *board;
+ int ret;
+
+ board = ni_getboardtype(dev, link);
+ if (!board)
+ return -ENODEV;
+ dev->board_ptr = board;
+ dev->board_name = board->name;
+
+ link->config_flags |= CONF_AUTO_SET_IO | CONF_ENABLE_IRQ;
+ ret = comedi_pcmcia_enable(dev, mio_pcmcia_config_loop);
+ if (ret)
+ return ret;
+ dev->iobase = link->resource[0]->start;
+
+ link->priv = dev;
+ ret = pcmcia_request_irq(link, ni_E_interrupt);
+ if (ret)
+ return ret;
+ dev->irq = link->irq;
+
+ ret = ni_alloc_private(dev);
+ if (ret)
+ return ret;
+
+ return ni_E_init(dev, 0, 1);
+}
+
+static void mio_cs_detach(struct comedi_device *dev)
+{
+ mio_common_detach(dev);
+ comedi_pcmcia_disable(dev);
+}
+
+static struct comedi_driver driver_ni_mio_cs = {
+ .driver_name = "ni_mio_cs",
+ .module = THIS_MODULE,
+ .auto_attach = mio_cs_auto_attach,
+ .detach = mio_cs_detach,
+};
+
+static int cs_attach(struct pcmcia_device *link)
+{
+ return comedi_pcmcia_auto_config(link, &driver_ni_mio_cs);
+}
+
+static const struct pcmcia_device_id ni_mio_cs_ids[] = {
+ PCMCIA_DEVICE_MANF_CARD(0x010b, 0x010d), /* DAQCard-ai-16xe-50 */
+ PCMCIA_DEVICE_MANF_CARD(0x010b, 0x010c), /* DAQCard-ai-16e-4 */
+ PCMCIA_DEVICE_MANF_CARD(0x010b, 0x02c4), /* DAQCard-6062E */
+ PCMCIA_DEVICE_MANF_CARD(0x010b, 0x075e), /* DAQCard-6024E */
+ PCMCIA_DEVICE_MANF_CARD(0x010b, 0x0245), /* DAQCard-6036E */
+ PCMCIA_DEVICE_NULL
+};
+MODULE_DEVICE_TABLE(pcmcia, ni_mio_cs_ids);
+
+static struct pcmcia_driver ni_mio_cs_driver = {
+ .name = "ni_mio_cs",
+ .owner = THIS_MODULE,
+ .id_table = ni_mio_cs_ids,
+ .probe = cs_attach,
+ .remove = comedi_pcmcia_auto_unconfig,
+};
+module_comedi_pcmcia_driver(driver_ni_mio_cs, ni_mio_cs_driver);
+
+MODULE_DESCRIPTION("Comedi driver for National Instruments DAQCard E series");
+MODULE_AUTHOR("David A. Schleef <ds@schleef.org>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/comedi/drivers/ni_pcidio.c b/drivers/staging/comedi/drivers/ni_pcidio.c
new file mode 100644
index 000000000..6453b7870
--- /dev/null
+++ b/drivers/staging/comedi/drivers/ni_pcidio.c
@@ -0,0 +1,1024 @@
+/*
+ comedi/drivers/ni_pcidio.c
+ driver for National Instruments PCI-DIO-32HS
+
+ COMEDI - Linux Control and Measurement Device Interface
+ Copyright (C) 1999,2002 David A. Schleef <ds@schleef.org>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+*/
+/*
+Driver: ni_pcidio
+Description: National Instruments PCI-DIO32HS, PCI-6533
+Author: ds
+Status: works
+Devices: [National Instruments] PCI-DIO-32HS (ni_pcidio)
+ [National Instruments] PXI-6533, PCI-6533 (pxi-6533)
+ [National Instruments] PCI-6534 (pci-6534)
+Updated: Mon, 09 Jan 2012 14:27:23 +0000
+
+The DIO32HS board appears as one subdevice, with 32 channels.
+Each channel is individually I/O configurable. The channel order
+is 0=A0, 1=A1, 2=A2, ... 8=B0, 16=C0, 24=D0. The driver only
+supports simple digital I/O; no handshaking is supported.
+
+DMA mostly works for the PCI-DIO32HS, but only in timed input mode.
+
+The PCI-DIO-32HS/PCI-6533 has a configurable external trigger. Setting
+scan_begin_arg to 0 or CR_EDGE triggers on the leading edge. Setting
+scan_begin_arg to CR_INVERT or (CR_EDGE | CR_INVERT) triggers on the
+trailing edge.
+
+This driver could be easily modified to support AT-MIO32HS and
+AT-MIO96.
+
+The PCI-6534 requires a firmware upload after power-up to work, the
+firmware data and instructions for loading it with comedi_config
+it are contained in the
+comedi_nonfree_firmware tarball available from http://www.comedi.org
+*/
+
+#define USE_DMA
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/sched.h>
+
+#include "../comedi_pci.h"
+
+#include "mite.h"
+
+/* defines for the PCI-DIO-32HS */
+
+#define Window_Address 4 /* W */
+#define Interrupt_And_Window_Status 4 /* R */
+#define IntStatus1 (1<<0)
+#define IntStatus2 (1<<1)
+#define WindowAddressStatus_mask 0x7c
+
+#define Master_DMA_And_Interrupt_Control 5 /* W */
+#define InterruptLine(x) ((x)&3)
+#define OpenInt (1<<2)
+#define Group_Status 5 /* R */
+#define DataLeft (1<<0)
+#define Req (1<<2)
+#define StopTrig (1<<3)
+
+#define Group_1_Flags 6 /* R */
+#define Group_2_Flags 7 /* R */
+#define TransferReady (1<<0)
+#define CountExpired (1<<1)
+#define Waited (1<<5)
+#define PrimaryTC (1<<6)
+#define SecondaryTC (1<<7)
+ /* #define SerialRose */
+ /* #define ReqRose */
+ /* #define Paused */
+
+#define Group_1_First_Clear 6 /* W */
+#define Group_2_First_Clear 7 /* W */
+#define ClearWaited (1<<3)
+#define ClearPrimaryTC (1<<4)
+#define ClearSecondaryTC (1<<5)
+#define DMAReset (1<<6)
+#define FIFOReset (1<<7)
+#define ClearAll 0xf8
+
+#define Group_1_FIFO 8 /* W */
+#define Group_2_FIFO 12 /* W */
+
+#define Transfer_Count 20
+#define Chip_ID_D 24
+#define Chip_ID_I 25
+#define Chip_ID_O 26
+#define Chip_Version 27
+#define Port_IO(x) (28+(x))
+#define Port_Pin_Directions(x) (32+(x))
+#define Port_Pin_Mask(x) (36+(x))
+#define Port_Pin_Polarities(x) (40+(x))
+
+#define Master_Clock_Routing 45
+#define RTSIClocking(x) (((x)&3)<<4)
+
+#define Group_1_Second_Clear 46 /* W */
+#define Group_2_Second_Clear 47 /* W */
+#define ClearExpired (1<<0)
+
+#define Port_Pattern(x) (48+(x))
+
+#define Data_Path 64
+#define FIFOEnableA (1<<0)
+#define FIFOEnableB (1<<1)
+#define FIFOEnableC (1<<2)
+#define FIFOEnableD (1<<3)
+#define Funneling(x) (((x)&3)<<4)
+#define GroupDirection (1<<7)
+
+#define Protocol_Register_1 65
+#define OpMode Protocol_Register_1
+#define RunMode(x) ((x)&7)
+#define Numbered (1<<3)
+
+#define Protocol_Register_2 66
+#define ClockReg Protocol_Register_2
+#define ClockLine(x) (((x)&3)<<5)
+#define InvertStopTrig (1<<7)
+#define DataLatching(x) (((x)&3)<<5)
+
+#define Protocol_Register_3 67
+#define Sequence Protocol_Register_3
+
+#define Protocol_Register_14 68 /* 16 bit */
+#define ClockSpeed Protocol_Register_14
+
+#define Protocol_Register_4 70
+#define ReqReg Protocol_Register_4
+#define ReqConditioning(x) (((x)&7)<<3)
+
+#define Protocol_Register_5 71
+#define BlockMode Protocol_Register_5
+
+#define FIFO_Control 72
+#define ReadyLevel(x) ((x)&7)
+
+#define Protocol_Register_6 73
+#define LinePolarities Protocol_Register_6
+#define InvertAck (1<<0)
+#define InvertReq (1<<1)
+#define InvertClock (1<<2)
+#define InvertSerial (1<<3)
+#define OpenAck (1<<4)
+#define OpenClock (1<<5)
+
+#define Protocol_Register_7 74
+#define AckSer Protocol_Register_7
+#define AckLine(x) (((x)&3)<<2)
+#define ExchangePins (1<<7)
+
+#define Interrupt_Control 75
+ /* bits same as flags */
+
+#define DMA_Line_Control_Group1 76
+#define DMA_Line_Control_Group2 108
+/* channel zero is none */
+static inline unsigned primary_DMAChannel_bits(unsigned channel)
+{
+ return channel & 0x3;
+}
+
+static inline unsigned secondary_DMAChannel_bits(unsigned channel)
+{
+ return (channel << 2) & 0xc;
+}
+
+#define Transfer_Size_Control 77
+#define TransferWidth(x) ((x)&3)
+#define TransferLength(x) (((x)&3)<<3)
+#define RequireRLevel (1<<5)
+
+#define Protocol_Register_15 79
+#define DAQOptions Protocol_Register_15
+#define StartSource(x) ((x)&0x3)
+#define InvertStart (1<<2)
+#define StopSource(x) (((x)&0x3)<<3)
+#define ReqStart (1<<6)
+#define PreStart (1<<7)
+
+#define Pattern_Detection 81
+#define DetectionMethod (1<<0)
+#define InvertMatch (1<<1)
+#define IE_Pattern_Detection (1<<2)
+
+#define Protocol_Register_9 82
+#define ReqDelay Protocol_Register_9
+
+#define Protocol_Register_10 83
+#define ReqNotDelay Protocol_Register_10
+
+#define Protocol_Register_11 84
+#define AckDelay Protocol_Register_11
+
+#define Protocol_Register_12 85
+#define AckNotDelay Protocol_Register_12
+
+#define Protocol_Register_13 86
+#define Data1Delay Protocol_Register_13
+
+#define Protocol_Register_8 88 /* 32 bit */
+#define StartDelay Protocol_Register_8
+
+/* Firmware files for PCI-6524 */
+#define FW_PCI_6534_MAIN "/*(DEBLOBBED)*/"
+#define FW_PCI_6534_SCARAB_DI "/*(DEBLOBBED)*/"
+#define FW_PCI_6534_SCARAB_DO "/*(DEBLOBBED)*/"
+/*(DEBLOBBED)*/
+
+enum pci_6534_firmware_registers { /* 16 bit */
+ Firmware_Control_Register = 0x100,
+ Firmware_Status_Register = 0x104,
+ Firmware_Data_Register = 0x108,
+ Firmware_Mask_Register = 0x10c,
+ Firmware_Debug_Register = 0x110,
+};
+/* main fpga registers (32 bit)*/
+enum pci_6534_fpga_registers {
+ FPGA_Control1_Register = 0x200,
+ FPGA_Control2_Register = 0x204,
+ FPGA_Irq_Mask_Register = 0x208,
+ FPGA_Status_Register = 0x20c,
+ FPGA_Signature_Register = 0x210,
+ FPGA_SCALS_Counter_Register = 0x280, /*write-clear */
+ FPGA_SCAMS_Counter_Register = 0x284, /*write-clear */
+ FPGA_SCBLS_Counter_Register = 0x288, /*write-clear */
+ FPGA_SCBMS_Counter_Register = 0x28c, /*write-clear */
+ FPGA_Temp_Control_Register = 0x2a0,
+ FPGA_DAR_Register = 0x2a8,
+ FPGA_ELC_Read_Register = 0x2b8,
+ FPGA_ELC_Write_Register = 0x2bc,
+};
+enum FPGA_Control_Bits {
+ FPGA_Enable_Bit = 0x8000,
+};
+
+#define TIMER_BASE 50 /* nanoseconds */
+
+#ifdef USE_DMA
+#define IntEn (CountExpired|Waited|PrimaryTC|SecondaryTC)
+#else
+#define IntEn (TransferReady|CountExpired|Waited|PrimaryTC|SecondaryTC)
+#endif
+
+enum nidio_boardid {
+ BOARD_PCIDIO_32HS,
+ BOARD_PXI6533,
+ BOARD_PCI6534,
+};
+
+struct nidio_board {
+ const char *name;
+ unsigned int uses_firmware:1;
+};
+
+static const struct nidio_board nidio_boards[] = {
+ [BOARD_PCIDIO_32HS] = {
+ .name = "pci-dio-32hs",
+ },
+ [BOARD_PXI6533] = {
+ .name = "pxi-6533",
+ },
+ [BOARD_PCI6534] = {
+ .name = "pci-6534",
+ .uses_firmware = 1,
+ },
+};
+
+struct nidio96_private {
+ struct mite_struct *mite;
+ int boardtype;
+ int dio;
+ unsigned short OpModeBits;
+ struct mite_channel *di_mite_chan;
+ struct mite_dma_descriptor_ring *di_mite_ring;
+ spinlock_t mite_channel_lock;
+};
+
+static int ni_pcidio_request_di_mite_channel(struct comedi_device *dev)
+{
+ struct nidio96_private *devpriv = dev->private;
+ unsigned long flags;
+
+ spin_lock_irqsave(&devpriv->mite_channel_lock, flags);
+ BUG_ON(devpriv->di_mite_chan);
+ devpriv->di_mite_chan =
+ mite_request_channel_in_range(devpriv->mite,
+ devpriv->di_mite_ring, 1, 2);
+ if (!devpriv->di_mite_chan) {
+ spin_unlock_irqrestore(&devpriv->mite_channel_lock, flags);
+ dev_err(dev->class_dev, "failed to reserve mite dma channel\n");
+ return -EBUSY;
+ }
+ devpriv->di_mite_chan->dir = COMEDI_INPUT;
+ writeb(primary_DMAChannel_bits(devpriv->di_mite_chan->channel) |
+ secondary_DMAChannel_bits(devpriv->di_mite_chan->channel),
+ dev->mmio + DMA_Line_Control_Group1);
+ mmiowb();
+ spin_unlock_irqrestore(&devpriv->mite_channel_lock, flags);
+ return 0;
+}
+
+static void ni_pcidio_release_di_mite_channel(struct comedi_device *dev)
+{
+ struct nidio96_private *devpriv = dev->private;
+ unsigned long flags;
+
+ spin_lock_irqsave(&devpriv->mite_channel_lock, flags);
+ if (devpriv->di_mite_chan) {
+ mite_dma_disarm(devpriv->di_mite_chan);
+ mite_dma_reset(devpriv->di_mite_chan);
+ mite_release_channel(devpriv->di_mite_chan);
+ devpriv->di_mite_chan = NULL;
+ writeb(primary_DMAChannel_bits(0) |
+ secondary_DMAChannel_bits(0),
+ dev->mmio + DMA_Line_Control_Group1);
+ mmiowb();
+ }
+ spin_unlock_irqrestore(&devpriv->mite_channel_lock, flags);
+}
+
+static int setup_mite_dma(struct comedi_device *dev, struct comedi_subdevice *s)
+{
+ struct nidio96_private *devpriv = dev->private;
+ int retval;
+ unsigned long flags;
+
+ retval = ni_pcidio_request_di_mite_channel(dev);
+ if (retval)
+ return retval;
+
+ /* write alloc the entire buffer */
+ comedi_buf_write_alloc(s, s->async->prealloc_bufsz);
+
+ spin_lock_irqsave(&devpriv->mite_channel_lock, flags);
+ if (devpriv->di_mite_chan) {
+ mite_prep_dma(devpriv->di_mite_chan, 32, 32);
+ mite_dma_arm(devpriv->di_mite_chan);
+ } else {
+ retval = -EIO;
+ }
+ spin_unlock_irqrestore(&devpriv->mite_channel_lock, flags);
+
+ return retval;
+}
+
+static int ni_pcidio_poll(struct comedi_device *dev, struct comedi_subdevice *s)
+{
+ struct nidio96_private *devpriv = dev->private;
+ unsigned long irq_flags;
+ int count;
+
+ spin_lock_irqsave(&dev->spinlock, irq_flags);
+ spin_lock(&devpriv->mite_channel_lock);
+ if (devpriv->di_mite_chan)
+ mite_sync_input_dma(devpriv->di_mite_chan, s);
+ spin_unlock(&devpriv->mite_channel_lock);
+ count = comedi_buf_n_bytes_ready(s);
+ spin_unlock_irqrestore(&dev->spinlock, irq_flags);
+ return count;
+}
+
+static irqreturn_t nidio_interrupt(int irq, void *d)
+{
+ struct comedi_device *dev = d;
+ struct nidio96_private *devpriv = dev->private;
+ struct comedi_subdevice *s = dev->read_subdev;
+ struct comedi_async *async = s->async;
+ struct mite_struct *mite = devpriv->mite;
+ unsigned int auxdata;
+ int flags;
+ int status;
+ int work = 0;
+ unsigned int m_status = 0;
+
+ /* interrupcions parasites */
+ if (!dev->attached) {
+ /* assume it's from another card */
+ return IRQ_NONE;
+ }
+
+ /* Lock to avoid race with comedi_poll */
+ spin_lock(&dev->spinlock);
+
+ status = readb(dev->mmio + Interrupt_And_Window_Status);
+ flags = readb(dev->mmio + Group_1_Flags);
+
+ spin_lock(&devpriv->mite_channel_lock);
+ if (devpriv->di_mite_chan)
+ m_status = mite_get_status(devpriv->di_mite_chan);
+
+ if (m_status & CHSR_INT) {
+ if (m_status & CHSR_LINKC) {
+ writel(CHOR_CLRLC,
+ mite->mite_io_addr +
+ MITE_CHOR(devpriv->di_mite_chan->channel));
+ mite_sync_input_dma(devpriv->di_mite_chan, s);
+ /* XXX need to byteswap */
+ }
+ if (m_status & ~(CHSR_INT | CHSR_LINKC | CHSR_DONE | CHSR_DRDY |
+ CHSR_DRQ1 | CHSR_MRDY)) {
+ dev_dbg(dev->class_dev,
+ "unknown mite interrupt, disabling IRQ\n");
+ async->events |= COMEDI_CB_ERROR;
+ disable_irq(dev->irq);
+ }
+ }
+ spin_unlock(&devpriv->mite_channel_lock);
+
+ while (status & DataLeft) {
+ work++;
+ if (work > 20) {
+ dev_dbg(dev->class_dev, "too much work in interrupt\n");
+ writeb(0x00,
+ dev->mmio + Master_DMA_And_Interrupt_Control);
+ break;
+ }
+
+ flags &= IntEn;
+
+ if (flags & TransferReady) {
+ while (flags & TransferReady) {
+ work++;
+ if (work > 100) {
+ dev_dbg(dev->class_dev,
+ "too much work in interrupt\n");
+ writeb(0x00, dev->mmio +
+ Master_DMA_And_Interrupt_Control
+ );
+ goto out;
+ }
+ auxdata = readl(dev->mmio + Group_1_FIFO);
+ comedi_buf_write_samples(s, &auxdata, 1);
+ flags = readb(dev->mmio + Group_1_Flags);
+ }
+ }
+
+ if (flags & CountExpired) {
+ writeb(ClearExpired, dev->mmio + Group_1_Second_Clear);
+ async->events |= COMEDI_CB_EOA;
+
+ writeb(0x00, dev->mmio + OpMode);
+ break;
+ } else if (flags & Waited) {
+ writeb(ClearWaited, dev->mmio + Group_1_First_Clear);
+ async->events |= COMEDI_CB_ERROR;
+ break;
+ } else if (flags & PrimaryTC) {
+ writeb(ClearPrimaryTC,
+ dev->mmio + Group_1_First_Clear);
+ async->events |= COMEDI_CB_EOA;
+ } else if (flags & SecondaryTC) {
+ writeb(ClearSecondaryTC,
+ dev->mmio + Group_1_First_Clear);
+ async->events |= COMEDI_CB_EOA;
+ }
+
+ flags = readb(dev->mmio + Group_1_Flags);
+ status = readb(dev->mmio + Interrupt_And_Window_Status);
+ }
+
+out:
+ comedi_handle_events(dev, s);
+#if 0
+ if (!tag)
+ writeb(0x03, dev->mmio + Master_DMA_And_Interrupt_Control);
+#endif
+
+ spin_unlock(&dev->spinlock);
+ return IRQ_HANDLED;
+}
+
+static int ni_pcidio_insn_config(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ int ret;
+
+ ret = comedi_dio_insn_config(dev, s, insn, data, 0);
+ if (ret)
+ return ret;
+
+ writel(s->io_bits, dev->mmio + Port_Pin_Directions(0));
+
+ return insn->n;
+}
+
+static int ni_pcidio_insn_bits(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ if (comedi_dio_update_state(s, data))
+ writel(s->state, dev->mmio + Port_IO(0));
+
+ data[1] = readl(dev->mmio + Port_IO(0));
+
+ return insn->n;
+}
+
+static int ni_pcidio_ns_to_timer(int *nanosec, unsigned int flags)
+{
+ int divider, base;
+
+ base = TIMER_BASE;
+
+ switch (flags & CMDF_ROUND_MASK) {
+ case CMDF_ROUND_NEAREST:
+ default:
+ divider = (*nanosec + base / 2) / base;
+ break;
+ case CMDF_ROUND_DOWN:
+ divider = (*nanosec) / base;
+ break;
+ case CMDF_ROUND_UP:
+ divider = (*nanosec + base - 1) / base;
+ break;
+ }
+
+ *nanosec = base * divider;
+ return divider;
+}
+
+static int ni_pcidio_cmdtest(struct comedi_device *dev,
+ struct comedi_subdevice *s, struct comedi_cmd *cmd)
+{
+ int err = 0;
+ unsigned int arg;
+
+ /* Step 1 : check if triggers are trivially valid */
+
+ err |= comedi_check_trigger_src(&cmd->start_src, TRIG_NOW | TRIG_INT);
+ err |= comedi_check_trigger_src(&cmd->scan_begin_src,
+ TRIG_TIMER | TRIG_EXT);
+ err |= comedi_check_trigger_src(&cmd->convert_src, TRIG_NOW);
+ err |= comedi_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
+ err |= comedi_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE);
+
+ if (err)
+ return 1;
+
+ /* Step 2a : make sure trigger sources are unique */
+
+ err |= comedi_check_trigger_is_unique(cmd->start_src);
+ err |= comedi_check_trigger_is_unique(cmd->scan_begin_src);
+ err |= comedi_check_trigger_is_unique(cmd->stop_src);
+
+ /* Step 2b : and mutually compatible */
+
+ if (err)
+ return 2;
+
+ /* Step 3: check if arguments are trivially valid */
+
+ err |= comedi_check_trigger_arg_is(&cmd->start_arg, 0);
+
+#define MAX_SPEED (TIMER_BASE) /* in nanoseconds */
+
+ if (cmd->scan_begin_src == TRIG_TIMER) {
+ err |= comedi_check_trigger_arg_min(&cmd->scan_begin_arg,
+ MAX_SPEED);
+ /* no minimum speed */
+ } else {
+ /* TRIG_EXT */
+ /* should be level/edge, hi/lo specification here */
+ if ((cmd->scan_begin_arg & ~(CR_EDGE | CR_INVERT)) != 0) {
+ cmd->scan_begin_arg &= (CR_EDGE | CR_INVERT);
+ err |= -EINVAL;
+ }
+ }
+
+ err |= comedi_check_trigger_arg_is(&cmd->convert_arg, 0);
+ err |= comedi_check_trigger_arg_is(&cmd->scan_end_arg,
+ cmd->chanlist_len);
+
+ if (cmd->stop_src == TRIG_COUNT)
+ err |= comedi_check_trigger_arg_min(&cmd->stop_arg, 1);
+ else /* TRIG_NONE */
+ err |= comedi_check_trigger_arg_is(&cmd->stop_arg, 0);
+
+ if (err)
+ return 3;
+
+ /* step 4: fix up any arguments */
+
+ if (cmd->scan_begin_src == TRIG_TIMER) {
+ arg = cmd->scan_begin_arg;
+ ni_pcidio_ns_to_timer(&arg, cmd->flags);
+ err |= comedi_check_trigger_arg_is(&cmd->scan_begin_arg, arg);
+ }
+
+ if (err)
+ return 4;
+
+ return 0;
+}
+
+static int ni_pcidio_inttrig(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ unsigned int trig_num)
+{
+ struct nidio96_private *devpriv = dev->private;
+ struct comedi_cmd *cmd = &s->async->cmd;
+
+ if (trig_num != cmd->start_arg)
+ return -EINVAL;
+
+ writeb(devpriv->OpModeBits, dev->mmio + OpMode);
+ s->async->inttrig = NULL;
+
+ return 1;
+}
+
+static int ni_pcidio_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
+{
+ struct nidio96_private *devpriv = dev->private;
+ struct comedi_cmd *cmd = &s->async->cmd;
+
+ /* XXX configure ports for input */
+ writel(0x0000, dev->mmio + Port_Pin_Directions(0));
+
+ if (1) {
+ /* enable fifos A B C D */
+ writeb(0x0f, dev->mmio + Data_Path);
+
+ /* set transfer width a 32 bits */
+ writeb(TransferWidth(0) | TransferLength(0),
+ dev->mmio + Transfer_Size_Control);
+ } else {
+ writeb(0x03, dev->mmio + Data_Path);
+ writeb(TransferWidth(3) | TransferLength(0),
+ dev->mmio + Transfer_Size_Control);
+ }
+
+ /* protocol configuration */
+ if (cmd->scan_begin_src == TRIG_TIMER) {
+ /* page 4-5, "input with internal REQs" */
+ writeb(0, dev->mmio + OpMode);
+ writeb(0x00, dev->mmio + ClockReg);
+ writeb(1, dev->mmio + Sequence);
+ writeb(0x04, dev->mmio + ReqReg);
+ writeb(4, dev->mmio + BlockMode);
+ writeb(3, dev->mmio + LinePolarities);
+ writeb(0xc0, dev->mmio + AckSer);
+ writel(ni_pcidio_ns_to_timer(&cmd->scan_begin_arg,
+ CMDF_ROUND_NEAREST),
+ dev->mmio + StartDelay);
+ writeb(1, dev->mmio + ReqDelay);
+ writeb(1, dev->mmio + ReqNotDelay);
+ writeb(1, dev->mmio + AckDelay);
+ writeb(0x0b, dev->mmio + AckNotDelay);
+ writeb(0x01, dev->mmio + Data1Delay);
+ /* manual, page 4-5: ClockSpeed comment is incorrectly listed
+ * on DAQOptions */
+ writew(0, dev->mmio + ClockSpeed);
+ writeb(0, dev->mmio + DAQOptions);
+ } else {
+ /* TRIG_EXT */
+ /* page 4-5, "input with external REQs" */
+ writeb(0, dev->mmio + OpMode);
+ writeb(0x00, dev->mmio + ClockReg);
+ writeb(0, dev->mmio + Sequence);
+ writeb(0x00, dev->mmio + ReqReg);
+ writeb(4, dev->mmio + BlockMode);
+ if (!(cmd->scan_begin_arg & CR_INVERT)) /* Leading Edge */
+ writeb(0, dev->mmio + LinePolarities);
+ else /* Trailing Edge */
+ writeb(2, dev->mmio + LinePolarities);
+ writeb(0x00, dev->mmio + AckSer);
+ writel(1, dev->mmio + StartDelay);
+ writeb(1, dev->mmio + ReqDelay);
+ writeb(1, dev->mmio + ReqNotDelay);
+ writeb(1, dev->mmio + AckDelay);
+ writeb(0x0C, dev->mmio + AckNotDelay);
+ writeb(0x10, dev->mmio + Data1Delay);
+ writew(0, dev->mmio + ClockSpeed);
+ writeb(0x60, dev->mmio + DAQOptions);
+ }
+
+ if (cmd->stop_src == TRIG_COUNT) {
+ writel(cmd->stop_arg,
+ dev->mmio + Transfer_Count);
+ } else {
+ /* XXX */
+ }
+
+#ifdef USE_DMA
+ writeb(ClearPrimaryTC | ClearSecondaryTC,
+ dev->mmio + Group_1_First_Clear);
+
+ {
+ int retval = setup_mite_dma(dev, s);
+
+ if (retval)
+ return retval;
+ }
+#else
+ writeb(0x00, dev->mmio + DMA_Line_Control_Group1);
+#endif
+ writeb(0x00, dev->mmio + DMA_Line_Control_Group2);
+
+ /* clear and enable interrupts */
+ writeb(0xff, dev->mmio + Group_1_First_Clear);
+ /* writeb(ClearExpired, dev->mmio+Group_1_Second_Clear); */
+
+ writeb(IntEn, dev->mmio + Interrupt_Control);
+ writeb(0x03, dev->mmio + Master_DMA_And_Interrupt_Control);
+
+ if (cmd->stop_src == TRIG_NONE) {
+ devpriv->OpModeBits = DataLatching(0) | RunMode(7);
+ } else { /* TRIG_TIMER */
+ devpriv->OpModeBits = Numbered | RunMode(7);
+ }
+ if (cmd->start_src == TRIG_NOW) {
+ /* start */
+ writeb(devpriv->OpModeBits, dev->mmio + OpMode);
+ s->async->inttrig = NULL;
+ } else {
+ /* TRIG_INT */
+ s->async->inttrig = ni_pcidio_inttrig;
+ }
+
+ return 0;
+}
+
+static int ni_pcidio_cancel(struct comedi_device *dev,
+ struct comedi_subdevice *s)
+{
+ writeb(0x00, dev->mmio + Master_DMA_And_Interrupt_Control);
+ ni_pcidio_release_di_mite_channel(dev);
+
+ return 0;
+}
+
+static int ni_pcidio_change(struct comedi_device *dev,
+ struct comedi_subdevice *s)
+{
+ struct nidio96_private *devpriv = dev->private;
+ int ret;
+
+ ret = mite_buf_change(devpriv->di_mite_ring, s);
+ if (ret < 0)
+ return ret;
+
+ memset(s->async->prealloc_buf, 0xaa, s->async->prealloc_bufsz);
+
+ return 0;
+}
+
+static int pci_6534_load_fpga(struct comedi_device *dev,
+ const u8 *data, size_t data_len,
+ unsigned long context)
+{
+ static const int timeout = 1000;
+ int fpga_index = context;
+ int i;
+ size_t j;
+
+ writew(0x80 | fpga_index, dev->mmio + Firmware_Control_Register);
+ writew(0xc0 | fpga_index, dev->mmio + Firmware_Control_Register);
+ for (i = 0;
+ (readw(dev->mmio + Firmware_Status_Register) & 0x2) == 0 &&
+ i < timeout; ++i) {
+ udelay(1);
+ }
+ if (i == timeout) {
+ dev_warn(dev->class_dev,
+ "ni_pcidio: failed to load fpga %i, waiting for status 0x2\n",
+ fpga_index);
+ return -EIO;
+ }
+ writew(0x80 | fpga_index, dev->mmio + Firmware_Control_Register);
+ for (i = 0;
+ readw(dev->mmio + Firmware_Status_Register) != 0x3 &&
+ i < timeout; ++i) {
+ udelay(1);
+ }
+ if (i == timeout) {
+ dev_warn(dev->class_dev,
+ "ni_pcidio: failed to load fpga %i, waiting for status 0x3\n",
+ fpga_index);
+ return -EIO;
+ }
+ for (j = 0; j + 1 < data_len;) {
+ unsigned int value = data[j++];
+
+ value |= data[j++] << 8;
+ writew(value, dev->mmio + Firmware_Data_Register);
+ for (i = 0;
+ (readw(dev->mmio + Firmware_Status_Register) & 0x2) == 0
+ && i < timeout; ++i) {
+ udelay(1);
+ }
+ if (i == timeout) {
+ dev_warn(dev->class_dev,
+ "ni_pcidio: failed to load word into fpga %i\n",
+ fpga_index);
+ return -EIO;
+ }
+ if (need_resched())
+ schedule();
+ }
+ writew(0x0, dev->mmio + Firmware_Control_Register);
+ return 0;
+}
+
+static int pci_6534_reset_fpga(struct comedi_device *dev, int fpga_index)
+{
+ return pci_6534_load_fpga(dev, NULL, 0, fpga_index);
+}
+
+static int pci_6534_reset_fpgas(struct comedi_device *dev)
+{
+ int ret;
+ int i;
+
+ writew(0x0, dev->mmio + Firmware_Control_Register);
+ for (i = 0; i < 3; ++i) {
+ ret = pci_6534_reset_fpga(dev, i);
+ if (ret < 0)
+ break;
+ }
+ writew(0x0, dev->mmio + Firmware_Mask_Register);
+ return ret;
+}
+
+static void pci_6534_init_main_fpga(struct comedi_device *dev)
+{
+ writel(0, dev->mmio + FPGA_Control1_Register);
+ writel(0, dev->mmio + FPGA_Control2_Register);
+ writel(0, dev->mmio + FPGA_SCALS_Counter_Register);
+ writel(0, dev->mmio + FPGA_SCAMS_Counter_Register);
+ writel(0, dev->mmio + FPGA_SCBLS_Counter_Register);
+ writel(0, dev->mmio + FPGA_SCBMS_Counter_Register);
+}
+
+static int pci_6534_upload_firmware(struct comedi_device *dev)
+{
+ struct nidio96_private *devpriv = dev->private;
+ static const char *const fw_file[3] = {
+ FW_PCI_6534_SCARAB_DI, /* loaded into scarab A for DI */
+ FW_PCI_6534_SCARAB_DO, /* loaded into scarab B for DO */
+ FW_PCI_6534_MAIN, /* loaded into main FPGA */
+ };
+ int ret;
+ int n;
+
+ ret = pci_6534_reset_fpgas(dev);
+ if (ret < 0)
+ return ret;
+ /* load main FPGA first, then the two scarabs */
+ for (n = 2; n >= 0; n--) {
+ ret = comedi_load_firmware(dev, &devpriv->mite->pcidev->dev,
+ fw_file[n],
+ pci_6534_load_fpga, n);
+ if (ret == 0 && n == 2)
+ pci_6534_init_main_fpga(dev);
+ if (ret < 0)
+ break;
+ }
+ return ret;
+}
+
+static void nidio_reset_board(struct comedi_device *dev)
+{
+ writel(0, dev->mmio + Port_IO(0));
+ writel(0, dev->mmio + Port_Pin_Directions(0));
+ writel(0, dev->mmio + Port_Pin_Mask(0));
+
+ /* disable interrupts on board */
+ writeb(0, dev->mmio + Master_DMA_And_Interrupt_Control);
+}
+
+static int nidio_auto_attach(struct comedi_device *dev,
+ unsigned long context)
+{
+ struct pci_dev *pcidev = comedi_to_pci_dev(dev);
+ const struct nidio_board *board = NULL;
+ struct nidio96_private *devpriv;
+ struct comedi_subdevice *s;
+ int ret;
+ unsigned int irq;
+
+ if (context < ARRAY_SIZE(nidio_boards))
+ board = &nidio_boards[context];
+ if (!board)
+ return -ENODEV;
+ dev->board_ptr = board;
+ dev->board_name = board->name;
+
+ ret = comedi_pci_enable(dev);
+ if (ret)
+ return ret;
+
+ devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
+ if (!devpriv)
+ return -ENOMEM;
+
+ spin_lock_init(&devpriv->mite_channel_lock);
+
+ devpriv->mite = mite_alloc(pcidev);
+ if (!devpriv->mite)
+ return -ENOMEM;
+
+ ret = mite_setup(dev, devpriv->mite);
+ if (ret < 0)
+ return ret;
+
+ devpriv->di_mite_ring = mite_alloc_ring(devpriv->mite);
+ if (!devpriv->di_mite_ring)
+ return -ENOMEM;
+
+ if (board->uses_firmware) {
+ ret = pci_6534_upload_firmware(dev);
+ if (ret < 0)
+ return ret;
+ }
+
+ nidio_reset_board(dev);
+
+ ret = comedi_alloc_subdevices(dev, 1);
+ if (ret)
+ return ret;
+
+ dev_info(dev->class_dev, "%s rev=%d\n", dev->board_name,
+ readb(dev->mmio + Chip_Version));
+
+ s = &dev->subdevices[0];
+
+ dev->read_subdev = s;
+ s->type = COMEDI_SUBD_DIO;
+ s->subdev_flags =
+ SDF_READABLE | SDF_WRITABLE | SDF_LSAMPL | SDF_PACKED |
+ SDF_CMD_READ;
+ s->n_chan = 32;
+ s->range_table = &range_digital;
+ s->maxdata = 1;
+ s->insn_config = &ni_pcidio_insn_config;
+ s->insn_bits = &ni_pcidio_insn_bits;
+ s->do_cmd = &ni_pcidio_cmd;
+ s->do_cmdtest = &ni_pcidio_cmdtest;
+ s->cancel = &ni_pcidio_cancel;
+ s->len_chanlist = 32; /* XXX */
+ s->buf_change = &ni_pcidio_change;
+ s->async_dma_dir = DMA_BIDIRECTIONAL;
+ s->poll = &ni_pcidio_poll;
+
+ irq = pcidev->irq;
+ if (irq) {
+ ret = request_irq(irq, nidio_interrupt, IRQF_SHARED,
+ dev->board_name, dev);
+ if (ret == 0)
+ dev->irq = irq;
+ }
+
+ return 0;
+}
+
+static void nidio_detach(struct comedi_device *dev)
+{
+ struct nidio96_private *devpriv = dev->private;
+
+ if (dev->irq)
+ free_irq(dev->irq, dev);
+ if (devpriv) {
+ if (devpriv->di_mite_ring) {
+ mite_free_ring(devpriv->di_mite_ring);
+ devpriv->di_mite_ring = NULL;
+ }
+ mite_detach(devpriv->mite);
+ }
+ if (dev->mmio)
+ iounmap(dev->mmio);
+ comedi_pci_disable(dev);
+}
+
+static struct comedi_driver ni_pcidio_driver = {
+ .driver_name = "ni_pcidio",
+ .module = THIS_MODULE,
+ .auto_attach = nidio_auto_attach,
+ .detach = nidio_detach,
+};
+
+static int ni_pcidio_pci_probe(struct pci_dev *dev,
+ const struct pci_device_id *id)
+{
+ return comedi_pci_auto_config(dev, &ni_pcidio_driver, id->driver_data);
+}
+
+static const struct pci_device_id ni_pcidio_pci_table[] = {
+ { PCI_VDEVICE(NI, 0x1150), BOARD_PCIDIO_32HS },
+ { PCI_VDEVICE(NI, 0x12b0), BOARD_PCI6534 },
+ { PCI_VDEVICE(NI, 0x1320), BOARD_PXI6533 },
+ { 0 }
+};
+MODULE_DEVICE_TABLE(pci, ni_pcidio_pci_table);
+
+static struct pci_driver ni_pcidio_pci_driver = {
+ .name = "ni_pcidio",
+ .id_table = ni_pcidio_pci_table,
+ .probe = ni_pcidio_pci_probe,
+ .remove = comedi_pci_auto_unconfig,
+};
+module_comedi_pci_driver(ni_pcidio_driver, ni_pcidio_pci_driver);
+
+MODULE_AUTHOR("Comedi http://www.comedi.org");
+MODULE_DESCRIPTION("Comedi low-level driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/comedi/drivers/ni_pcimio.c b/drivers/staging/comedi/drivers/ni_pcimio.c
new file mode 100644
index 000000000..1481f71a3
--- /dev/null
+++ b/drivers/staging/comedi/drivers/ni_pcimio.c
@@ -0,0 +1,1308 @@
+/*
+ comedi/drivers/ni_pcimio.c
+ Hardware driver for NI PCI-MIO E series cards
+
+ COMEDI - Linux Control and Measurement Device Interface
+ Copyright (C) 1997-8 David A. Schleef <ds@schleef.org>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+*/
+/*
+Driver: ni_pcimio
+Description: National Instruments PCI-MIO-E series and M series (all boards)
+Author: ds, John Hallen, Frank Mori Hess, Rolf Mueller, Herbert Peremans,
+ Herman Bruyninckx, Terry Barnaby
+Status: works
+Devices: [National Instruments] PCI-MIO-16XE-50 (ni_pcimio),
+ PCI-MIO-16XE-10, PXI-6030E, PCI-MIO-16E-1, PCI-MIO-16E-4, PCI-6014, PCI-6040E,
+ PXI-6040E, PCI-6030E, PCI-6031E, PCI-6032E, PCI-6033E, PCI-6071E, PCI-6023E,
+ PCI-6024E, PCI-6025E, PXI-6025E, PCI-6034E, PCI-6035E, PCI-6052E,
+ PCI-6110, PCI-6111, PCI-6220, PCI-6221, PCI-6224, PXI-6224,
+ PCI-6225, PXI-6225, PCI-6229, PCI-6250, PCI-6251, PCIe-6251, PXIe-6251,
+ PCI-6254, PCI-6259, PCIe-6259,
+ PCI-6280, PCI-6281, PXI-6281, PCI-6284, PCI-6289,
+ PCI-6711, PXI-6711, PCI-6713, PXI-6713,
+ PXI-6071E, PCI-6070E, PXI-6070E,
+ PXI-6052E, PCI-6036E, PCI-6731, PCI-6733, PXI-6733,
+ PCI-6143, PXI-6143
+Updated: Mon, 09 Jan 2012 14:52:48 +0000
+
+These boards are almost identical to the AT-MIO E series, except that
+they use the PCI bus instead of ISA (i.e., AT). See the notes for
+the ni_atmio.o driver for additional information about these boards.
+
+Autocalibration is supported on many of the devices, using the
+comedi_calibrate (or comedi_soft_calibrate for m-series) utility.
+M-Series boards do analog input and analog output calibration entirely
+in software. The software calibration corrects
+the analog input for offset, gain and
+nonlinearity. The analog outputs are corrected for offset and gain.
+See the comedilib documentation on comedi_get_softcal_converter() for
+more information.
+
+By default, the driver uses DMA to transfer analog input data to
+memory. When DMA is enabled, not all triggering features are
+supported.
+
+Digital I/O may not work on 673x.
+
+Note that the PCI-6143 is a simultaineous sampling device with 8 convertors.
+With this board all of the convertors perform one simultaineous sample during
+a scan interval. The period for a scan is used for the convert time in a
+Comedi cmd. The convert trigger source is normally set to TRIG_NOW by default.
+
+The RTSI trigger bus is supported on these cards on
+subdevice 10. See the comedilib documentation for details.
+
+Information (number of channels, bits, etc.) for some devices may be
+incorrect. Please check this and submit a bug if there are problems
+for your device.
+
+SCXI is probably broken for m-series boards.
+
+Bugs:
+ - When DMA is enabled, COMEDI_EV_CONVERT does
+ not work correctly.
+
+*/
+/*
+ The PCI-MIO E series driver was originally written by
+ Tomasz Motylewski <...>, and ported to comedi by ds.
+
+ References:
+
+ 341079b.pdf PCI E Series Register-Level Programmer Manual
+ 340934b.pdf DAQ-STC reference manual
+
+ 322080b.pdf 6711/6713/6715 User Manual
+
+ 320945c.pdf PCI E Series User Manual
+ 322138a.pdf PCI-6052E and DAQPad-6052E User Manual
+
+ ISSUES:
+
+ need to deal with external reference for DAC, and other DAC
+ properties in board properties
+
+ deal with at-mio-16de-10 revision D to N changes, etc.
+
+ need to add other CALDAC type
+
+ need to slow down DAC loading. I don't trust NI's claim that
+ two writes to the PCI bus slows IO enough. I would prefer to
+ use udelay(). Timing specs: (clock)
+ AD8522 30ns
+ DAC8043 120ns
+ DAC8800 60ns
+ MB88341 ?
+
+*/
+
+#include <linux/module.h>
+#include <linux/delay.h>
+
+#include "../comedi_pci.h"
+
+#include <asm/byteorder.h>
+
+#include "ni_stc.h"
+#include "mite.h"
+
+#define PCIDMA
+
+/* These are not all the possible ao ranges for 628x boards.
+ They can do OFFSET +- REFERENCE where OFFSET can be
+ 0V, 5V, APFI<0,1>, or AO<0...3> and RANGE can
+ be 10V, 5V, 2V, 1V, APFI<0,1>, AO<0...3>. That's
+ 63 different possibilities. An AO channel
+ can not act as it's own OFFSET or REFERENCE.
+*/
+static const struct comedi_lrange range_ni_M_628x_ao = {
+ 8, {
+ BIP_RANGE(10),
+ BIP_RANGE(5),
+ BIP_RANGE(2),
+ BIP_RANGE(1),
+ RANGE(-5, 15),
+ UNI_RANGE(10),
+ RANGE(3, 7),
+ RANGE(4, 6),
+ RANGE_ext(-1, 1)
+ }
+};
+
+static const struct comedi_lrange range_ni_M_625x_ao = {
+ 3, {
+ BIP_RANGE(10),
+ BIP_RANGE(5),
+ RANGE_ext(-1, 1)
+ }
+};
+
+enum ni_pcimio_boardid {
+ BOARD_PCIMIO_16XE_50,
+ BOARD_PCIMIO_16XE_10,
+ BOARD_PCI6014,
+ BOARD_PXI6030E,
+ BOARD_PCIMIO_16E_1,
+ BOARD_PCIMIO_16E_4,
+ BOARD_PXI6040E,
+ BOARD_PCI6031E,
+ BOARD_PCI6032E,
+ BOARD_PCI6033E,
+ BOARD_PCI6071E,
+ BOARD_PCI6023E,
+ BOARD_PCI6024E,
+ BOARD_PCI6025E,
+ BOARD_PXI6025E,
+ BOARD_PCI6034E,
+ BOARD_PCI6035E,
+ BOARD_PCI6052E,
+ BOARD_PCI6110,
+ BOARD_PCI6111,
+ /* BOARD_PCI6115, */
+ /* BOARD_PXI6115, */
+ BOARD_PCI6711,
+ BOARD_PXI6711,
+ BOARD_PCI6713,
+ BOARD_PXI6713,
+ BOARD_PCI6731,
+ /* BOARD_PXI6731, */
+ BOARD_PCI6733,
+ BOARD_PXI6733,
+ BOARD_PXI6071E,
+ BOARD_PXI6070E,
+ BOARD_PXI6052E,
+ BOARD_PXI6031E,
+ BOARD_PCI6036E,
+ BOARD_PCI6220,
+ BOARD_PCI6221,
+ BOARD_PCI6221_37PIN,
+ BOARD_PCI6224,
+ BOARD_PXI6224,
+ BOARD_PCI6225,
+ BOARD_PXI6225,
+ BOARD_PCI6229,
+ BOARD_PCI6250,
+ BOARD_PCI6251,
+ BOARD_PCIE6251,
+ BOARD_PXIE6251,
+ BOARD_PCI6254,
+ BOARD_PCI6259,
+ BOARD_PCIE6259,
+ BOARD_PCI6280,
+ BOARD_PCI6281,
+ BOARD_PXI6281,
+ BOARD_PCI6284,
+ BOARD_PCI6289,
+ BOARD_PCI6143,
+ BOARD_PXI6143,
+};
+
+static const struct ni_board_struct ni_boards[] = {
+ [BOARD_PCIMIO_16XE_50] = {
+ .name = "pci-mio-16xe-50",
+ .n_adchan = 16,
+ .ai_maxdata = 0xffff,
+ .ai_fifo_depth = 2048,
+ .alwaysdither = 1,
+ .gainlkup = ai_gain_8,
+ .ai_speed = 50000,
+ .n_aochan = 2,
+ .ao_maxdata = 0x0fff,
+ .ao_range_table = &range_bipolar10,
+ .ao_speed = 50000,
+ .caldac = { dac8800, dac8043 },
+ },
+ [BOARD_PCIMIO_16XE_10] = {
+ .name = "pci-mio-16xe-10", /* aka pci-6030E */
+ .n_adchan = 16,
+ .ai_maxdata = 0xffff,
+ .ai_fifo_depth = 512,
+ .alwaysdither = 1,
+ .gainlkup = ai_gain_14,
+ .ai_speed = 10000,
+ .n_aochan = 2,
+ .ao_maxdata = 0xffff,
+ .ao_fifo_depth = 2048,
+ .ao_range_table = &range_ni_E_ao_ext,
+ .ao_speed = 10000,
+ .caldac = { dac8800, dac8043, ad8522 },
+ },
+ [BOARD_PCI6014] = {
+ .name = "pci-6014",
+ .n_adchan = 16,
+ .ai_maxdata = 0xffff,
+ .ai_fifo_depth = 512,
+ .alwaysdither = 1,
+ .gainlkup = ai_gain_4,
+ .ai_speed = 5000,
+ .n_aochan = 2,
+ .ao_maxdata = 0xffff,
+ .ao_range_table = &range_bipolar10,
+ .ao_speed = 100000,
+ .caldac = { ad8804_debug },
+ },
+ [BOARD_PXI6030E] = {
+ .name = "pxi-6030e",
+ .n_adchan = 16,
+ .ai_maxdata = 0xffff,
+ .ai_fifo_depth = 512,
+ .alwaysdither = 1,
+ .gainlkup = ai_gain_14,
+ .ai_speed = 10000,
+ .n_aochan = 2,
+ .ao_maxdata = 0xffff,
+ .ao_fifo_depth = 2048,
+ .ao_range_table = &range_ni_E_ao_ext,
+ .ao_speed = 10000,
+ .caldac = { dac8800, dac8043, ad8522 },
+ },
+ [BOARD_PCIMIO_16E_1] = {
+ .name = "pci-mio-16e-1", /* aka pci-6070e */
+ .n_adchan = 16,
+ .ai_maxdata = 0x0fff,
+ .ai_fifo_depth = 512,
+ .gainlkup = ai_gain_16,
+ .ai_speed = 800,
+ .n_aochan = 2,
+ .ao_maxdata = 0x0fff,
+ .ao_fifo_depth = 2048,
+ .ao_range_table = &range_ni_E_ao_ext,
+ .ao_speed = 1000,
+ .caldac = { mb88341 },
+ },
+ [BOARD_PCIMIO_16E_4] = {
+ .name = "pci-mio-16e-4", /* aka pci-6040e */
+ .n_adchan = 16,
+ .ai_maxdata = 0x0fff,
+ .ai_fifo_depth = 512,
+ .gainlkup = ai_gain_16,
+ /*
+ * there have been reported problems with
+ * full speed on this board
+ */
+ .ai_speed = 2000,
+ .n_aochan = 2,
+ .ao_maxdata = 0x0fff,
+ .ao_fifo_depth = 512,
+ .ao_range_table = &range_ni_E_ao_ext,
+ .ao_speed = 1000,
+ .caldac = { ad8804_debug }, /* doc says mb88341 */
+ },
+ [BOARD_PXI6040E] = {
+ .name = "pxi-6040e",
+ .n_adchan = 16,
+ .ai_maxdata = 0x0fff,
+ .ai_fifo_depth = 512,
+ .gainlkup = ai_gain_16,
+ .ai_speed = 2000,
+ .n_aochan = 2,
+ .ao_maxdata = 0x0fff,
+ .ao_fifo_depth = 512,
+ .ao_range_table = &range_ni_E_ao_ext,
+ .ao_speed = 1000,
+ .caldac = { mb88341 },
+ },
+ [BOARD_PCI6031E] = {
+ .name = "pci-6031e",
+ .n_adchan = 64,
+ .ai_maxdata = 0xffff,
+ .ai_fifo_depth = 512,
+ .alwaysdither = 1,
+ .gainlkup = ai_gain_14,
+ .ai_speed = 10000,
+ .n_aochan = 2,
+ .ao_maxdata = 0xffff,
+ .ao_fifo_depth = 2048,
+ .ao_range_table = &range_ni_E_ao_ext,
+ .ao_speed = 10000,
+ .caldac = { dac8800, dac8043, ad8522 },
+ },
+ [BOARD_PCI6032E] = {
+ .name = "pci-6032e",
+ .n_adchan = 16,
+ .ai_maxdata = 0xffff,
+ .ai_fifo_depth = 512,
+ .alwaysdither = 1,
+ .gainlkup = ai_gain_14,
+ .ai_speed = 10000,
+ .caldac = { dac8800, dac8043, ad8522 },
+ },
+ [BOARD_PCI6033E] = {
+ .name = "pci-6033e",
+ .n_adchan = 64,
+ .ai_maxdata = 0xffff,
+ .ai_fifo_depth = 512,
+ .alwaysdither = 1,
+ .gainlkup = ai_gain_14,
+ .ai_speed = 10000,
+ .caldac = { dac8800, dac8043, ad8522 },
+ },
+ [BOARD_PCI6071E] = {
+ .name = "pci-6071e",
+ .n_adchan = 64,
+ .ai_maxdata = 0x0fff,
+ .ai_fifo_depth = 512,
+ .alwaysdither = 1,
+ .gainlkup = ai_gain_16,
+ .ai_speed = 800,
+ .n_aochan = 2,
+ .ao_maxdata = 0x0fff,
+ .ao_fifo_depth = 2048,
+ .ao_range_table = &range_ni_E_ao_ext,
+ .ao_speed = 1000,
+ .caldac = { ad8804_debug },
+ },
+ [BOARD_PCI6023E] = {
+ .name = "pci-6023e",
+ .n_adchan = 16,
+ .ai_maxdata = 0x0fff,
+ .ai_fifo_depth = 512,
+ .gainlkup = ai_gain_4,
+ .ai_speed = 5000,
+ .caldac = { ad8804_debug }, /* manual is wrong */
+ },
+ [BOARD_PCI6024E] = {
+ .name = "pci-6024e",
+ .n_adchan = 16,
+ .ai_maxdata = 0x0fff,
+ .ai_fifo_depth = 512,
+ .gainlkup = ai_gain_4,
+ .ai_speed = 5000,
+ .n_aochan = 2,
+ .ao_maxdata = 0x0fff,
+ .ao_range_table = &range_bipolar10,
+ .ao_speed = 100000,
+ .caldac = { ad8804_debug }, /* manual is wrong */
+ },
+ [BOARD_PCI6025E] = {
+ .name = "pci-6025e",
+ .n_adchan = 16,
+ .ai_maxdata = 0x0fff,
+ .ai_fifo_depth = 512,
+ .gainlkup = ai_gain_4,
+ .ai_speed = 5000,
+ .n_aochan = 2,
+ .ao_maxdata = 0x0fff,
+ .ao_range_table = &range_bipolar10,
+ .ao_speed = 100000,
+ .caldac = { ad8804_debug }, /* manual is wrong */
+ .has_8255 = 1,
+ },
+ [BOARD_PXI6025E] = {
+ .name = "pxi-6025e",
+ .n_adchan = 16,
+ .ai_maxdata = 0x0fff,
+ .ai_fifo_depth = 512,
+ .gainlkup = ai_gain_4,
+ .ai_speed = 5000,
+ .n_aochan = 2,
+ .ao_maxdata = 0x0fff,
+ .ao_range_table = &range_ni_E_ao_ext,
+ .ao_speed = 100000,
+ .caldac = { ad8804_debug }, /* manual is wrong */
+ .has_8255 = 1,
+ },
+ [BOARD_PCI6034E] = {
+ .name = "pci-6034e",
+ .n_adchan = 16,
+ .ai_maxdata = 0xffff,
+ .ai_fifo_depth = 512,
+ .alwaysdither = 1,
+ .gainlkup = ai_gain_4,
+ .ai_speed = 5000,
+ .caldac = { ad8804_debug },
+ },
+ [BOARD_PCI6035E] = {
+ .name = "pci-6035e",
+ .n_adchan = 16,
+ .ai_maxdata = 0xffff,
+ .ai_fifo_depth = 512,
+ .alwaysdither = 1,
+ .gainlkup = ai_gain_4,
+ .ai_speed = 5000,
+ .n_aochan = 2,
+ .ao_maxdata = 0x0fff,
+ .ao_range_table = &range_bipolar10,
+ .ao_speed = 100000,
+ .caldac = { ad8804_debug },
+ },
+ [BOARD_PCI6052E] = {
+ .name = "pci-6052e",
+ .n_adchan = 16,
+ .ai_maxdata = 0xffff,
+ .ai_fifo_depth = 512,
+ .alwaysdither = 1,
+ .gainlkup = ai_gain_16,
+ .ai_speed = 3000,
+ .n_aochan = 2,
+ .ao_maxdata = 0xffff,
+ .ao_fifo_depth = 2048,
+ .ao_range_table = &range_ni_E_ao_ext,
+ .ao_speed = 3000,
+ /* manual is wrong */
+ .caldac = { ad8804_debug, ad8804_debug, ad8522 },
+ },
+ [BOARD_PCI6110] = {
+ .name = "pci-6110",
+ .n_adchan = 4,
+ .ai_maxdata = 0x0fff,
+ .ai_fifo_depth = 8192,
+ .alwaysdither = 0,
+ .gainlkup = ai_gain_611x,
+ .ai_speed = 200,
+ .n_aochan = 2,
+ .ao_maxdata = 0xffff,
+ .reg_type = ni_reg_611x,
+ .ao_range_table = &range_bipolar10,
+ .ao_fifo_depth = 2048,
+ .ao_speed = 250,
+ .caldac = { ad8804, ad8804 },
+ },
+ [BOARD_PCI6111] = {
+ .name = "pci-6111",
+ .n_adchan = 2,
+ .ai_maxdata = 0x0fff,
+ .ai_fifo_depth = 8192,
+ .gainlkup = ai_gain_611x,
+ .ai_speed = 200,
+ .n_aochan = 2,
+ .ao_maxdata = 0xffff,
+ .reg_type = ni_reg_611x,
+ .ao_range_table = &range_bipolar10,
+ .ao_fifo_depth = 2048,
+ .ao_speed = 250,
+ .caldac = { ad8804, ad8804 },
+ },
+#if 0
+ /* The 6115 boards probably need their own driver */
+ [BOARD_PCI6115] = { /* .device_id = 0x2ed0, */
+ .name = "pci-6115",
+ .n_adchan = 4,
+ .ai_maxdata = 0x0fff,
+ .ai_fifo_depth = 8192,
+ .gainlkup = ai_gain_611x,
+ .ai_speed = 100,
+ .n_aochan = 2,
+ .ao_maxdata = 0xffff,
+ .ao_671x = 1,
+ .ao_fifo_depth = 2048,
+ .ao_speed = 250,
+ .reg_611x = 1,
+ /* XXX */
+ .caldac = { ad8804_debug, ad8804_debug, ad8804_debug },
+ },
+#endif
+#if 0
+ [BOARD_PXI6115] = { /* .device_id = ????, */
+ .name = "pxi-6115",
+ .n_adchan = 4,
+ .ai_maxdata = 0x0fff,
+ .ai_fifo_depth = 8192,
+ .gainlkup = ai_gain_611x,
+ .ai_speed = 100,
+ .n_aochan = 2,
+ .ao_maxdata = 0xffff,
+ .ao_671x = 1,
+ .ao_fifo_depth = 2048,
+ .ao_speed = 250,
+ .reg_611x = 1,
+ /* XXX */
+ .caldac = { ad8804_debug, ad8804_debug, ad8804_debug },
+ },
+#endif
+ [BOARD_PCI6711] = {
+ .name = "pci-6711",
+ .n_aochan = 4,
+ .ao_maxdata = 0x0fff,
+ /* data sheet says 8192, but fifo really holds 16384 samples */
+ .ao_fifo_depth = 16384,
+ .ao_range_table = &range_bipolar10,
+ .ao_speed = 1000,
+ .reg_type = ni_reg_6711,
+ .caldac = { ad8804_debug },
+ },
+ [BOARD_PXI6711] = {
+ .name = "pxi-6711",
+ .n_aochan = 4,
+ .ao_maxdata = 0x0fff,
+ .ao_fifo_depth = 16384,
+ .ao_range_table = &range_bipolar10,
+ .ao_speed = 1000,
+ .reg_type = ni_reg_6711,
+ .caldac = { ad8804_debug },
+ },
+ [BOARD_PCI6713] = {
+ .name = "pci-6713",
+ .n_aochan = 8,
+ .ao_maxdata = 0x0fff,
+ .ao_fifo_depth = 16384,
+ .ao_range_table = &range_bipolar10,
+ .ao_speed = 1000,
+ .reg_type = ni_reg_6713,
+ .caldac = { ad8804_debug, ad8804_debug },
+ },
+ [BOARD_PXI6713] = {
+ .name = "pxi-6713",
+ .n_aochan = 8,
+ .ao_maxdata = 0x0fff,
+ .ao_fifo_depth = 16384,
+ .ao_range_table = &range_bipolar10,
+ .ao_speed = 1000,
+ .reg_type = ni_reg_6713,
+ .caldac = { ad8804_debug, ad8804_debug },
+ },
+ [BOARD_PCI6731] = {
+ .name = "pci-6731",
+ .n_aochan = 4,
+ .ao_maxdata = 0xffff,
+ .ao_fifo_depth = 8192,
+ .ao_range_table = &range_bipolar10,
+ .ao_speed = 1000,
+ .reg_type = ni_reg_6711,
+ .caldac = { ad8804_debug },
+ },
+#if 0
+ [BOARD_PXI6731] = { /* .device_id = ????, */
+ .name = "pxi-6731",
+ .n_aochan = 4,
+ .ao_maxdata = 0xffff,
+ .ao_fifo_depth = 8192,
+ .ao_range_table = &range_bipolar10,
+ .reg_type = ni_reg_6711,
+ .caldac = { ad8804_debug },
+ },
+#endif
+ [BOARD_PCI6733] = {
+ .name = "pci-6733",
+ .n_aochan = 8,
+ .ao_maxdata = 0xffff,
+ .ao_fifo_depth = 16384,
+ .ao_range_table = &range_bipolar10,
+ .ao_speed = 1000,
+ .reg_type = ni_reg_6713,
+ .caldac = { ad8804_debug, ad8804_debug },
+ },
+ [BOARD_PXI6733] = {
+ .name = "pxi-6733",
+ .n_aochan = 8,
+ .ao_maxdata = 0xffff,
+ .ao_fifo_depth = 16384,
+ .ao_range_table = &range_bipolar10,
+ .ao_speed = 1000,
+ .reg_type = ni_reg_6713,
+ .caldac = { ad8804_debug, ad8804_debug },
+ },
+ [BOARD_PXI6071E] = {
+ .name = "pxi-6071e",
+ .n_adchan = 64,
+ .ai_maxdata = 0x0fff,
+ .ai_fifo_depth = 512,
+ .alwaysdither = 1,
+ .gainlkup = ai_gain_16,
+ .ai_speed = 800,
+ .n_aochan = 2,
+ .ao_maxdata = 0x0fff,
+ .ao_fifo_depth = 2048,
+ .ao_range_table = &range_ni_E_ao_ext,
+ .ao_speed = 1000,
+ .caldac = { ad8804_debug },
+ },
+ [BOARD_PXI6070E] = {
+ .name = "pxi-6070e",
+ .n_adchan = 16,
+ .ai_maxdata = 0x0fff,
+ .ai_fifo_depth = 512,
+ .alwaysdither = 1,
+ .gainlkup = ai_gain_16,
+ .ai_speed = 800,
+ .n_aochan = 2,
+ .ao_maxdata = 0x0fff,
+ .ao_fifo_depth = 2048,
+ .ao_range_table = &range_ni_E_ao_ext,
+ .ao_speed = 1000,
+ .caldac = { ad8804_debug },
+ },
+ [BOARD_PXI6052E] = {
+ .name = "pxi-6052e",
+ .n_adchan = 16,
+ .ai_maxdata = 0xffff,
+ .ai_fifo_depth = 512,
+ .alwaysdither = 1,
+ .gainlkup = ai_gain_16,
+ .ai_speed = 3000,
+ .n_aochan = 2,
+ .ao_maxdata = 0xffff,
+ .ao_fifo_depth = 2048,
+ .ao_range_table = &range_ni_E_ao_ext,
+ .ao_speed = 3000,
+ .caldac = { mb88341, mb88341, ad8522 },
+ },
+ [BOARD_PXI6031E] = {
+ .name = "pxi-6031e",
+ .n_adchan = 64,
+ .ai_maxdata = 0xffff,
+ .ai_fifo_depth = 512,
+ .alwaysdither = 1,
+ .gainlkup = ai_gain_14,
+ .ai_speed = 10000,
+ .n_aochan = 2,
+ .ao_maxdata = 0xffff,
+ .ao_fifo_depth = 2048,
+ .ao_range_table = &range_ni_E_ao_ext,
+ .ao_speed = 10000,
+ .caldac = { dac8800, dac8043, ad8522 },
+ },
+ [BOARD_PCI6036E] = {
+ .name = "pci-6036e",
+ .n_adchan = 16,
+ .ai_maxdata = 0xffff,
+ .ai_fifo_depth = 512,
+ .alwaysdither = 1,
+ .gainlkup = ai_gain_4,
+ .ai_speed = 5000,
+ .n_aochan = 2,
+ .ao_maxdata = 0xffff,
+ .ao_range_table = &range_bipolar10,
+ .ao_speed = 100000,
+ .caldac = { ad8804_debug },
+ },
+ [BOARD_PCI6220] = {
+ .name = "pci-6220",
+ .n_adchan = 16,
+ .ai_maxdata = 0xffff,
+ .ai_fifo_depth = 512, /* FIXME: guess */
+ .gainlkup = ai_gain_622x,
+ .ai_speed = 4000,
+ .reg_type = ni_reg_622x,
+ .caldac = { caldac_none },
+ },
+ [BOARD_PCI6221] = {
+ .name = "pci-6221",
+ .n_adchan = 16,
+ .ai_maxdata = 0xffff,
+ .ai_fifo_depth = 4095,
+ .gainlkup = ai_gain_622x,
+ .ai_speed = 4000,
+ .n_aochan = 2,
+ .ao_maxdata = 0xffff,
+ .ao_fifo_depth = 8191,
+ .ao_range_table = &range_bipolar10,
+ .reg_type = ni_reg_622x,
+ .ao_speed = 1200,
+ .caldac = { caldac_none },
+ },
+ [BOARD_PCI6221_37PIN] = {
+ .name = "pci-6221_37pin",
+ .n_adchan = 16,
+ .ai_maxdata = 0xffff,
+ .ai_fifo_depth = 4095,
+ .gainlkup = ai_gain_622x,
+ .ai_speed = 4000,
+ .n_aochan = 2,
+ .ao_maxdata = 0xffff,
+ .ao_fifo_depth = 8191,
+ .ao_range_table = &range_bipolar10,
+ .reg_type = ni_reg_622x,
+ .ao_speed = 1200,
+ .caldac = { caldac_none },
+ },
+ [BOARD_PCI6224] = {
+ .name = "pci-6224",
+ .n_adchan = 32,
+ .ai_maxdata = 0xffff,
+ .ai_fifo_depth = 4095,
+ .gainlkup = ai_gain_622x,
+ .ai_speed = 4000,
+ .reg_type = ni_reg_622x,
+ .has_32dio_chan = 1,
+ .caldac = { caldac_none },
+ },
+ [BOARD_PXI6224] = {
+ .name = "pxi-6224",
+ .n_adchan = 32,
+ .ai_maxdata = 0xffff,
+ .ai_fifo_depth = 4095,
+ .gainlkup = ai_gain_622x,
+ .ai_speed = 4000,
+ .reg_type = ni_reg_622x,
+ .has_32dio_chan = 1,
+ .caldac = { caldac_none },
+ },
+ [BOARD_PCI6225] = {
+ .name = "pci-6225",
+ .n_adchan = 80,
+ .ai_maxdata = 0xffff,
+ .ai_fifo_depth = 4095,
+ .gainlkup = ai_gain_622x,
+ .ai_speed = 4000,
+ .n_aochan = 2,
+ .ao_maxdata = 0xffff,
+ .ao_fifo_depth = 8191,
+ .ao_range_table = &range_bipolar10,
+ .reg_type = ni_reg_622x,
+ .ao_speed = 1200,
+ .has_32dio_chan = 1,
+ .caldac = { caldac_none },
+ },
+ [BOARD_PXI6225] = {
+ .name = "pxi-6225",
+ .n_adchan = 80,
+ .ai_maxdata = 0xffff,
+ .ai_fifo_depth = 4095,
+ .gainlkup = ai_gain_622x,
+ .ai_speed = 4000,
+ .n_aochan = 2,
+ .ao_maxdata = 0xffff,
+ .ao_fifo_depth = 8191,
+ .ao_range_table = &range_bipolar10,
+ .reg_type = ni_reg_622x,
+ .ao_speed = 1200,
+ .has_32dio_chan = 1,
+ .caldac = { caldac_none },
+ },
+ [BOARD_PCI6229] = {
+ .name = "pci-6229",
+ .n_adchan = 32,
+ .ai_maxdata = 0xffff,
+ .ai_fifo_depth = 4095,
+ .gainlkup = ai_gain_622x,
+ .ai_speed = 4000,
+ .n_aochan = 4,
+ .ao_maxdata = 0xffff,
+ .ao_fifo_depth = 8191,
+ .ao_range_table = &range_bipolar10,
+ .reg_type = ni_reg_622x,
+ .ao_speed = 1200,
+ .has_32dio_chan = 1,
+ .caldac = { caldac_none },
+ },
+ [BOARD_PCI6250] = {
+ .name = "pci-6250",
+ .n_adchan = 16,
+ .ai_maxdata = 0xffff,
+ .ai_fifo_depth = 4095,
+ .gainlkup = ai_gain_628x,
+ .ai_speed = 800,
+ .reg_type = ni_reg_625x,
+ .caldac = { caldac_none },
+ },
+ [BOARD_PCI6251] = {
+ .name = "pci-6251",
+ .n_adchan = 16,
+ .ai_maxdata = 0xffff,
+ .ai_fifo_depth = 4095,
+ .gainlkup = ai_gain_628x,
+ .ai_speed = 800,
+ .n_aochan = 2,
+ .ao_maxdata = 0xffff,
+ .ao_fifo_depth = 8191,
+ .ao_range_table = &range_ni_M_625x_ao,
+ .reg_type = ni_reg_625x,
+ .ao_speed = 350,
+ .caldac = { caldac_none },
+ },
+ [BOARD_PCIE6251] = {
+ .name = "pcie-6251",
+ .n_adchan = 16,
+ .ai_maxdata = 0xffff,
+ .ai_fifo_depth = 4095,
+ .gainlkup = ai_gain_628x,
+ .ai_speed = 800,
+ .n_aochan = 2,
+ .ao_maxdata = 0xffff,
+ .ao_fifo_depth = 8191,
+ .ao_range_table = &range_ni_M_625x_ao,
+ .reg_type = ni_reg_625x,
+ .ao_speed = 350,
+ .caldac = { caldac_none },
+ },
+ [BOARD_PXIE6251] = {
+ .name = "pxie-6251",
+ .n_adchan = 16,
+ .ai_maxdata = 0xffff,
+ .ai_fifo_depth = 4095,
+ .gainlkup = ai_gain_628x,
+ .ai_speed = 800,
+ .n_aochan = 2,
+ .ao_maxdata = 0xffff,
+ .ao_fifo_depth = 8191,
+ .ao_range_table = &range_ni_M_625x_ao,
+ .reg_type = ni_reg_625x,
+ .ao_speed = 350,
+ .caldac = { caldac_none },
+ },
+ [BOARD_PCI6254] = {
+ .name = "pci-6254",
+ .n_adchan = 32,
+ .ai_maxdata = 0xffff,
+ .ai_fifo_depth = 4095,
+ .gainlkup = ai_gain_628x,
+ .ai_speed = 800,
+ .reg_type = ni_reg_625x,
+ .has_32dio_chan = 1,
+ .caldac = { caldac_none },
+ },
+ [BOARD_PCI6259] = {
+ .name = "pci-6259",
+ .n_adchan = 32,
+ .ai_maxdata = 0xffff,
+ .ai_fifo_depth = 4095,
+ .gainlkup = ai_gain_628x,
+ .ai_speed = 800,
+ .n_aochan = 4,
+ .ao_maxdata = 0xffff,
+ .ao_fifo_depth = 8191,
+ .ao_range_table = &range_ni_M_625x_ao,
+ .reg_type = ni_reg_625x,
+ .ao_speed = 350,
+ .has_32dio_chan = 1,
+ .caldac = { caldac_none },
+ },
+ [BOARD_PCIE6259] = {
+ .name = "pcie-6259",
+ .n_adchan = 32,
+ .ai_maxdata = 0xffff,
+ .ai_fifo_depth = 4095,
+ .gainlkup = ai_gain_628x,
+ .ai_speed = 800,
+ .n_aochan = 4,
+ .ao_maxdata = 0xffff,
+ .ao_fifo_depth = 8191,
+ .ao_range_table = &range_ni_M_625x_ao,
+ .reg_type = ni_reg_625x,
+ .ao_speed = 350,
+ .has_32dio_chan = 1,
+ .caldac = { caldac_none },
+ },
+ [BOARD_PCI6280] = {
+ .name = "pci-6280",
+ .n_adchan = 16,
+ .ai_maxdata = 0x3ffff,
+ .ai_fifo_depth = 2047,
+ .gainlkup = ai_gain_628x,
+ .ai_speed = 1600,
+ .ao_fifo_depth = 8191,
+ .reg_type = ni_reg_628x,
+ .caldac = { caldac_none },
+ },
+ [BOARD_PCI6281] = {
+ .name = "pci-6281",
+ .n_adchan = 16,
+ .ai_maxdata = 0x3ffff,
+ .ai_fifo_depth = 2047,
+ .gainlkup = ai_gain_628x,
+ .ai_speed = 1600,
+ .n_aochan = 2,
+ .ao_maxdata = 0xffff,
+ .ao_fifo_depth = 8191,
+ .ao_range_table = &range_ni_M_628x_ao,
+ .reg_type = ni_reg_628x,
+ .ao_speed = 350,
+ .caldac = { caldac_none },
+ },
+ [BOARD_PXI6281] = {
+ .name = "pxi-6281",
+ .n_adchan = 16,
+ .ai_maxdata = 0x3ffff,
+ .ai_fifo_depth = 2047,
+ .gainlkup = ai_gain_628x,
+ .ai_speed = 1600,
+ .n_aochan = 2,
+ .ao_maxdata = 0xffff,
+ .ao_fifo_depth = 8191,
+ .ao_range_table = &range_ni_M_628x_ao,
+ .reg_type = ni_reg_628x,
+ .ao_speed = 350,
+ .caldac = { caldac_none },
+ },
+ [BOARD_PCI6284] = {
+ .name = "pci-6284",
+ .n_adchan = 32,
+ .ai_maxdata = 0x3ffff,
+ .ai_fifo_depth = 2047,
+ .gainlkup = ai_gain_628x,
+ .ai_speed = 1600,
+ .reg_type = ni_reg_628x,
+ .has_32dio_chan = 1,
+ .caldac = { caldac_none },
+ },
+ [BOARD_PCI6289] = {
+ .name = "pci-6289",
+ .n_adchan = 32,
+ .ai_maxdata = 0x3ffff,
+ .ai_fifo_depth = 2047,
+ .gainlkup = ai_gain_628x,
+ .ai_speed = 1600,
+ .n_aochan = 4,
+ .ao_maxdata = 0xffff,
+ .ao_fifo_depth = 8191,
+ .ao_range_table = &range_ni_M_628x_ao,
+ .reg_type = ni_reg_628x,
+ .ao_speed = 350,
+ .has_32dio_chan = 1,
+ .caldac = { caldac_none },
+ },
+ [BOARD_PCI6143] = {
+ .name = "pci-6143",
+ .n_adchan = 8,
+ .ai_maxdata = 0xffff,
+ .ai_fifo_depth = 1024,
+ .gainlkup = ai_gain_6143,
+ .ai_speed = 4000,
+ .reg_type = ni_reg_6143,
+ .caldac = { ad8804_debug, ad8804_debug },
+ },
+ [BOARD_PXI6143] = {
+ .name = "pxi-6143",
+ .n_adchan = 8,
+ .ai_maxdata = 0xffff,
+ .ai_fifo_depth = 1024,
+ .gainlkup = ai_gain_6143,
+ .ai_speed = 4000,
+ .reg_type = ni_reg_6143,
+ .caldac = { ad8804_debug, ad8804_debug },
+ },
+};
+
+#include "ni_mio_common.c"
+
+static int pcimio_ai_change(struct comedi_device *dev,
+ struct comedi_subdevice *s)
+{
+ struct ni_private *devpriv = dev->private;
+ int ret;
+
+ ret = mite_buf_change(devpriv->ai_mite_ring, s);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static int pcimio_ao_change(struct comedi_device *dev,
+ struct comedi_subdevice *s)
+{
+ struct ni_private *devpriv = dev->private;
+ int ret;
+
+ ret = mite_buf_change(devpriv->ao_mite_ring, s);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static int pcimio_gpct0_change(struct comedi_device *dev,
+ struct comedi_subdevice *s)
+{
+ struct ni_private *devpriv = dev->private;
+ int ret;
+
+ ret = mite_buf_change(devpriv->gpct_mite_ring[0], s);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static int pcimio_gpct1_change(struct comedi_device *dev,
+ struct comedi_subdevice *s)
+{
+ struct ni_private *devpriv = dev->private;
+ int ret;
+
+ ret = mite_buf_change(devpriv->gpct_mite_ring[1], s);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static int pcimio_dio_change(struct comedi_device *dev,
+ struct comedi_subdevice *s)
+{
+ struct ni_private *devpriv = dev->private;
+ int ret;
+
+ ret = mite_buf_change(devpriv->cdo_mite_ring, s);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static void m_series_init_eeprom_buffer(struct comedi_device *dev)
+{
+ struct ni_private *devpriv = dev->private;
+ static const int Start_Cal_EEPROM = 0x400;
+ static const unsigned window_size = 10;
+ static const int serial_number_eeprom_offset = 0x4;
+ static const int serial_number_eeprom_length = 0x4;
+ unsigned old_iodwbsr_bits;
+ unsigned old_iodwbsr1_bits;
+ unsigned old_iodwcr1_bits;
+ int i;
+
+ old_iodwbsr_bits = readl(devpriv->mite->mite_io_addr + MITE_IODWBSR);
+ old_iodwbsr1_bits = readl(devpriv->mite->mite_io_addr + MITE_IODWBSR_1);
+ old_iodwcr1_bits = readl(devpriv->mite->mite_io_addr + MITE_IODWCR_1);
+ writel(0x0, devpriv->mite->mite_io_addr + MITE_IODWBSR);
+ writel(((0x80 | window_size) | devpriv->mite->daq_phys_addr),
+ devpriv->mite->mite_io_addr + MITE_IODWBSR_1);
+ writel(0x1 | old_iodwcr1_bits,
+ devpriv->mite->mite_io_addr + MITE_IODWCR_1);
+ writel(0xf, devpriv->mite->mite_io_addr + 0x30);
+
+ BUG_ON(serial_number_eeprom_length > sizeof(devpriv->serial_number));
+ for (i = 0; i < serial_number_eeprom_length; ++i) {
+ char *byte_ptr = (char *)&devpriv->serial_number + i;
+ *byte_ptr = ni_readb(dev, serial_number_eeprom_offset + i);
+ }
+ devpriv->serial_number = be32_to_cpu(devpriv->serial_number);
+
+ for (i = 0; i < M_SERIES_EEPROM_SIZE; ++i)
+ devpriv->eeprom_buffer[i] = ni_readb(dev, Start_Cal_EEPROM + i);
+
+ writel(old_iodwbsr1_bits, devpriv->mite->mite_io_addr + MITE_IODWBSR_1);
+ writel(old_iodwbsr_bits, devpriv->mite->mite_io_addr + MITE_IODWBSR);
+ writel(old_iodwcr1_bits, devpriv->mite->mite_io_addr + MITE_IODWCR_1);
+ writel(0x0, devpriv->mite->mite_io_addr + 0x30);
+}
+
+static void init_6143(struct comedi_device *dev)
+{
+ const struct ni_board_struct *board = dev->board_ptr;
+ struct ni_private *devpriv = dev->private;
+
+ /* Disable interrupts */
+ ni_stc_writew(dev, 0, Interrupt_Control_Register);
+
+ /* Initialise 6143 AI specific bits */
+
+ /* Set G0,G1 DMA mode to E series version */
+ ni_writeb(dev, 0x00, Magic_6143);
+ /* Set EOCMode, ADCMode and pipelinedelay */
+ ni_writeb(dev, 0x80, PipelineDelay_6143);
+ /* Set EOC Delay */
+ ni_writeb(dev, 0x00, EOC_Set_6143);
+
+ /* Set the FIFO half full level */
+ ni_writel(dev, board->ai_fifo_depth / 2, AIFIFO_Flag_6143);
+
+ /* Strobe Relay disable bit */
+ devpriv->ai_calib_source_enabled = 0;
+ ni_writew(dev, devpriv->ai_calib_source |
+ Calibration_Channel_6143_RelayOff,
+ Calibration_Channel_6143);
+ ni_writew(dev, devpriv->ai_calib_source, Calibration_Channel_6143);
+}
+
+static void pcimio_detach(struct comedi_device *dev)
+{
+ struct ni_private *devpriv = dev->private;
+
+ mio_common_detach(dev);
+ if (dev->irq)
+ free_irq(dev->irq, dev);
+ if (devpriv) {
+ mite_free_ring(devpriv->ai_mite_ring);
+ mite_free_ring(devpriv->ao_mite_ring);
+ mite_free_ring(devpriv->cdo_mite_ring);
+ mite_free_ring(devpriv->gpct_mite_ring[0]);
+ mite_free_ring(devpriv->gpct_mite_ring[1]);
+ mite_detach(devpriv->mite);
+ }
+ if (dev->mmio)
+ iounmap(dev->mmio);
+ comedi_pci_disable(dev);
+}
+
+static int pcimio_auto_attach(struct comedi_device *dev,
+ unsigned long context)
+{
+ struct pci_dev *pcidev = comedi_to_pci_dev(dev);
+ const struct ni_board_struct *board = NULL;
+ struct ni_private *devpriv;
+ unsigned int irq;
+ int ret;
+
+ if (context < ARRAY_SIZE(ni_boards))
+ board = &ni_boards[context];
+ if (!board)
+ return -ENODEV;
+ dev->board_ptr = board;
+ dev->board_name = board->name;
+
+ ret = comedi_pci_enable(dev);
+ if (ret)
+ return ret;
+
+ ret = ni_alloc_private(dev);
+ if (ret)
+ return ret;
+ devpriv = dev->private;
+
+ devpriv->mite = mite_alloc(pcidev);
+ if (!devpriv->mite)
+ return -ENOMEM;
+
+ if (board->reg_type & ni_reg_m_series_mask)
+ devpriv->is_m_series = 1;
+ if (board->reg_type & ni_reg_6xxx_mask)
+ devpriv->is_6xxx = 1;
+ if (board->reg_type == ni_reg_611x)
+ devpriv->is_611x = 1;
+ if (board->reg_type == ni_reg_6143)
+ devpriv->is_6143 = 1;
+ if (board->reg_type == ni_reg_622x)
+ devpriv->is_622x = 1;
+ if (board->reg_type == ni_reg_625x)
+ devpriv->is_625x = 1;
+ if (board->reg_type == ni_reg_628x)
+ devpriv->is_628x = 1;
+ if (board->reg_type & ni_reg_67xx_mask)
+ devpriv->is_67xx = 1;
+ if (board->reg_type == ni_reg_6711)
+ devpriv->is_6711 = 1;
+ if (board->reg_type == ni_reg_6713)
+ devpriv->is_6713 = 1;
+
+ ret = mite_setup(dev, devpriv->mite);
+ if (ret < 0)
+ return ret;
+
+ devpriv->ai_mite_ring = mite_alloc_ring(devpriv->mite);
+ if (!devpriv->ai_mite_ring)
+ return -ENOMEM;
+ devpriv->ao_mite_ring = mite_alloc_ring(devpriv->mite);
+ if (!devpriv->ao_mite_ring)
+ return -ENOMEM;
+ devpriv->cdo_mite_ring = mite_alloc_ring(devpriv->mite);
+ if (!devpriv->cdo_mite_ring)
+ return -ENOMEM;
+ devpriv->gpct_mite_ring[0] = mite_alloc_ring(devpriv->mite);
+ if (!devpriv->gpct_mite_ring[0])
+ return -ENOMEM;
+ devpriv->gpct_mite_ring[1] = mite_alloc_ring(devpriv->mite);
+ if (!devpriv->gpct_mite_ring[1])
+ return -ENOMEM;
+
+ if (devpriv->is_m_series)
+ m_series_init_eeprom_buffer(dev);
+ if (devpriv->is_6143)
+ init_6143(dev);
+
+ irq = pcidev->irq;
+ if (irq) {
+ ret = request_irq(irq, ni_E_interrupt, IRQF_SHARED,
+ dev->board_name, dev);
+ if (ret == 0)
+ dev->irq = irq;
+ }
+
+ ret = ni_E_init(dev, 0, 1);
+ if (ret < 0)
+ return ret;
+
+ dev->subdevices[NI_AI_SUBDEV].buf_change = &pcimio_ai_change;
+ dev->subdevices[NI_AO_SUBDEV].buf_change = &pcimio_ao_change;
+ dev->subdevices[NI_GPCT_SUBDEV(0)].buf_change = &pcimio_gpct0_change;
+ dev->subdevices[NI_GPCT_SUBDEV(1)].buf_change = &pcimio_gpct1_change;
+ dev->subdevices[NI_DIO_SUBDEV].buf_change = &pcimio_dio_change;
+
+ return 0;
+}
+
+static struct comedi_driver ni_pcimio_driver = {
+ .driver_name = "ni_pcimio",
+ .module = THIS_MODULE,
+ .auto_attach = pcimio_auto_attach,
+ .detach = pcimio_detach,
+};
+
+static int ni_pcimio_pci_probe(struct pci_dev *dev,
+ const struct pci_device_id *id)
+{
+ return comedi_pci_auto_config(dev, &ni_pcimio_driver, id->driver_data);
+}
+
+static const struct pci_device_id ni_pcimio_pci_table[] = {
+ { PCI_VDEVICE(NI, 0x0162), BOARD_PCIMIO_16XE_50 }, /* 0x1620? */
+ { PCI_VDEVICE(NI, 0x1170), BOARD_PCIMIO_16XE_10 },
+ { PCI_VDEVICE(NI, 0x1180), BOARD_PCIMIO_16E_1 },
+ { PCI_VDEVICE(NI, 0x1190), BOARD_PCIMIO_16E_4 },
+ { PCI_VDEVICE(NI, 0x11b0), BOARD_PXI6070E },
+ { PCI_VDEVICE(NI, 0x11c0), BOARD_PXI6040E },
+ { PCI_VDEVICE(NI, 0x11d0), BOARD_PXI6030E },
+ { PCI_VDEVICE(NI, 0x1270), BOARD_PCI6032E },
+ { PCI_VDEVICE(NI, 0x1330), BOARD_PCI6031E },
+ { PCI_VDEVICE(NI, 0x1340), BOARD_PCI6033E },
+ { PCI_VDEVICE(NI, 0x1350), BOARD_PCI6071E },
+ { PCI_VDEVICE(NI, 0x14e0), BOARD_PCI6110 },
+ { PCI_VDEVICE(NI, 0x14f0), BOARD_PCI6111 },
+ { PCI_VDEVICE(NI, 0x1580), BOARD_PXI6031E },
+ { PCI_VDEVICE(NI, 0x15b0), BOARD_PXI6071E },
+ { PCI_VDEVICE(NI, 0x1880), BOARD_PCI6711 },
+ { PCI_VDEVICE(NI, 0x1870), BOARD_PCI6713 },
+ { PCI_VDEVICE(NI, 0x18b0), BOARD_PCI6052E },
+ { PCI_VDEVICE(NI, 0x18c0), BOARD_PXI6052E },
+ { PCI_VDEVICE(NI, 0x2410), BOARD_PCI6733 },
+ { PCI_VDEVICE(NI, 0x2420), BOARD_PXI6733 },
+ { PCI_VDEVICE(NI, 0x2430), BOARD_PCI6731 },
+ { PCI_VDEVICE(NI, 0x2890), BOARD_PCI6036E },
+ { PCI_VDEVICE(NI, 0x28c0), BOARD_PCI6014 },
+ { PCI_VDEVICE(NI, 0x2a60), BOARD_PCI6023E },
+ { PCI_VDEVICE(NI, 0x2a70), BOARD_PCI6024E },
+ { PCI_VDEVICE(NI, 0x2a80), BOARD_PCI6025E },
+ { PCI_VDEVICE(NI, 0x2ab0), BOARD_PXI6025E },
+ { PCI_VDEVICE(NI, 0x2b80), BOARD_PXI6713 },
+ { PCI_VDEVICE(NI, 0x2b90), BOARD_PXI6711 },
+ { PCI_VDEVICE(NI, 0x2c80), BOARD_PCI6035E },
+ { PCI_VDEVICE(NI, 0x2ca0), BOARD_PCI6034E },
+ { PCI_VDEVICE(NI, 0x70aa), BOARD_PCI6229 },
+ { PCI_VDEVICE(NI, 0x70ab), BOARD_PCI6259 },
+ { PCI_VDEVICE(NI, 0x70ac), BOARD_PCI6289 },
+ { PCI_VDEVICE(NI, 0x70af), BOARD_PCI6221 },
+ { PCI_VDEVICE(NI, 0x70b0), BOARD_PCI6220 },
+ { PCI_VDEVICE(NI, 0x70b4), BOARD_PCI6250 },
+ { PCI_VDEVICE(NI, 0x70b6), BOARD_PCI6280 },
+ { PCI_VDEVICE(NI, 0x70b7), BOARD_PCI6254 },
+ { PCI_VDEVICE(NI, 0x70b8), BOARD_PCI6251 },
+ { PCI_VDEVICE(NI, 0x70bc), BOARD_PCI6284 },
+ { PCI_VDEVICE(NI, 0x70bd), BOARD_PCI6281 },
+ { PCI_VDEVICE(NI, 0x70bf), BOARD_PXI6281 },
+ { PCI_VDEVICE(NI, 0x70c0), BOARD_PCI6143 },
+ { PCI_VDEVICE(NI, 0x70f2), BOARD_PCI6224 },
+ { PCI_VDEVICE(NI, 0x70f3), BOARD_PXI6224 },
+ { PCI_VDEVICE(NI, 0x710d), BOARD_PXI6143 },
+ { PCI_VDEVICE(NI, 0x716c), BOARD_PCI6225 },
+ { PCI_VDEVICE(NI, 0x716d), BOARD_PXI6225 },
+ { PCI_VDEVICE(NI, 0x717f), BOARD_PCIE6259 },
+ { PCI_VDEVICE(NI, 0x71bc), BOARD_PCI6221_37PIN },
+ { PCI_VDEVICE(NI, 0x717d), BOARD_PCIE6251 },
+ { PCI_VDEVICE(NI, 0x72e8), BOARD_PXIE6251 },
+ { 0 }
+};
+MODULE_DEVICE_TABLE(pci, ni_pcimio_pci_table);
+
+static struct pci_driver ni_pcimio_pci_driver = {
+ .name = "ni_pcimio",
+ .id_table = ni_pcimio_pci_table,
+ .probe = ni_pcimio_pci_probe,
+ .remove = comedi_pci_auto_unconfig,
+};
+module_comedi_pci_driver(ni_pcimio_driver, ni_pcimio_pci_driver);
+
+MODULE_AUTHOR("Comedi http://www.comedi.org");
+MODULE_DESCRIPTION("Comedi low-level driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/comedi/drivers/ni_stc.h b/drivers/staging/comedi/drivers/ni_stc.h
new file mode 100644
index 000000000..bd69c3f0a
--- /dev/null
+++ b/drivers/staging/comedi/drivers/ni_stc.h
@@ -0,0 +1,1491 @@
+/*
+ module/ni_stc.h
+ Register descriptions for NI DAQ-STC chip
+
+ COMEDI - Linux Control and Measurement Device Interface
+ Copyright (C) 1998-9 David A. Schleef <ds@schleef.org>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+*/
+
+/*
+ References:
+ DAQ-STC Technical Reference Manual
+*/
+
+#ifndef _COMEDI_NI_STC_H
+#define _COMEDI_NI_STC_H
+
+#include "ni_tio.h"
+
+#define _bit15 0x8000
+#define _bit14 0x4000
+#define _bit13 0x2000
+#define _bit12 0x1000
+#define _bit11 0x0800
+#define _bit10 0x0400
+#define _bit9 0x0200
+#define _bit8 0x0100
+#define _bit7 0x0080
+#define _bit6 0x0040
+#define _bit5 0x0020
+#define _bit4 0x0010
+#define _bit3 0x0008
+#define _bit2 0x0004
+#define _bit1 0x0002
+#define _bit0 0x0001
+
+#define NUM_PFI_OUTPUT_SELECT_REGS 6
+
+/* Registers in the National Instruments DAQ-STC chip */
+
+#define Interrupt_A_Ack_Register 2
+#define G0_Gate_Interrupt_Ack _bit15
+#define G0_TC_Interrupt_Ack _bit14
+#define AI_Error_Interrupt_Ack _bit13
+#define AI_STOP_Interrupt_Ack _bit12
+#define AI_START_Interrupt_Ack _bit11
+#define AI_START2_Interrupt_Ack _bit10
+#define AI_START1_Interrupt_Ack _bit9
+#define AI_SC_TC_Interrupt_Ack _bit8
+#define AI_SC_TC_Error_Confirm _bit7
+#define G0_TC_Error_Confirm _bit6
+#define G0_Gate_Error_Confirm _bit5
+
+#define AI_Status_1_Register 2
+#define Interrupt_A_St 0x8000
+#define AI_FIFO_Full_St 0x4000
+#define AI_FIFO_Half_Full_St 0x2000
+#define AI_FIFO_Empty_St 0x1000
+#define AI_Overrun_St 0x0800
+#define AI_Overflow_St 0x0400
+#define AI_SC_TC_Error_St 0x0200
+#define AI_START2_St 0x0100
+#define AI_START1_St 0x0080
+#define AI_SC_TC_St 0x0040
+#define AI_START_St 0x0020
+#define AI_STOP_St 0x0010
+#define G0_TC_St 0x0008
+#define G0_Gate_Interrupt_St 0x0004
+#define AI_FIFO_Request_St 0x0002
+#define Pass_Thru_0_Interrupt_St 0x0001
+
+#define AI_Status_2_Register 5
+
+#define Interrupt_B_Ack_Register 3
+enum Interrupt_B_Ack_Bits {
+ G1_Gate_Error_Confirm = _bit1,
+ G1_TC_Error_Confirm = _bit2,
+ AO_BC_TC_Trigger_Error_Confirm = _bit3,
+ AO_BC_TC_Error_Confirm = _bit4,
+ AO_UI2_TC_Error_Confrim = _bit5,
+ AO_UI2_TC_Interrupt_Ack = _bit6,
+ AO_UC_TC_Interrupt_Ack = _bit7,
+ AO_BC_TC_Interrupt_Ack = _bit8,
+ AO_START1_Interrupt_Ack = _bit9,
+ AO_UPDATE_Interrupt_Ack = _bit10,
+ AO_START_Interrupt_Ack = _bit11,
+ AO_STOP_Interrupt_Ack = _bit12,
+ AO_Error_Interrupt_Ack = _bit13,
+ G1_TC_Interrupt_Ack = _bit14,
+ G1_Gate_Interrupt_Ack = _bit15
+};
+
+#define AO_Status_1_Register 3
+#define Interrupt_B_St _bit15
+#define AO_FIFO_Full_St _bit14
+#define AO_FIFO_Half_Full_St _bit13
+#define AO_FIFO_Empty_St _bit12
+#define AO_BC_TC_Error_St _bit11
+#define AO_START_St _bit10
+#define AO_Overrun_St _bit9
+#define AO_START1_St _bit8
+#define AO_BC_TC_St _bit7
+#define AO_UC_TC_St _bit6
+#define AO_UPDATE_St _bit5
+#define AO_UI2_TC_St _bit4
+#define G1_TC_St _bit3
+#define G1_Gate_Interrupt_St _bit2
+#define AO_FIFO_Request_St _bit1
+#define Pass_Thru_1_Interrupt_St _bit0
+
+#define AI_Command_2_Register 4
+#define AI_End_On_SC_TC _bit15
+#define AI_End_On_End_Of_Scan _bit14
+#define AI_START1_Disable _bit11
+#define AI_SC_Save_Trace _bit10
+#define AI_SI_Switch_Load_On_SC_TC _bit9
+#define AI_SI_Switch_Load_On_STOP _bit8
+#define AI_SI_Switch_Load_On_TC _bit7
+#define AI_SC_Switch_Load_On_TC _bit4
+#define AI_STOP_Pulse _bit3
+#define AI_START_Pulse _bit2
+#define AI_START2_Pulse _bit1
+#define AI_START1_Pulse _bit0
+
+#define AO_Command_2_Register 5
+#define AO_End_On_BC_TC(x) (((x) & 0x3) << 14)
+#define AO_Start_Stop_Gate_Enable _bit13
+#define AO_UC_Save_Trace _bit12
+#define AO_BC_Gate_Enable _bit11
+#define AO_BC_Save_Trace _bit10
+#define AO_UI_Switch_Load_On_BC_TC _bit9
+#define AO_UI_Switch_Load_On_Stop _bit8
+#define AO_UI_Switch_Load_On_TC _bit7
+#define AO_UC_Switch_Load_On_BC_TC _bit6
+#define AO_UC_Switch_Load_On_TC _bit5
+#define AO_BC_Switch_Load_On_TC _bit4
+#define AO_Mute_B _bit3
+#define AO_Mute_A _bit2
+#define AO_UPDATE2_Pulse _bit1
+#define AO_START1_Pulse _bit0
+
+#define AO_Status_2_Register 6
+
+#define DIO_Parallel_Input_Register 7
+
+#define AI_Command_1_Register 8
+#define AI_Analog_Trigger_Reset _bit14
+#define AI_Disarm _bit13
+#define AI_SI2_Arm _bit12
+#define AI_SI2_Load _bit11
+#define AI_SI_Arm _bit10
+#define AI_SI_Load _bit9
+#define AI_DIV_Arm _bit8
+#define AI_DIV_Load _bit7
+#define AI_SC_Arm _bit6
+#define AI_SC_Load _bit5
+#define AI_SCAN_IN_PROG_Pulse _bit4
+#define AI_EXTMUX_CLK_Pulse _bit3
+#define AI_LOCALMUX_CLK_Pulse _bit2
+#define AI_SC_TC_Pulse _bit1
+#define AI_CONVERT_Pulse _bit0
+
+#define AO_Command_1_Register 9
+#define AO_Analog_Trigger_Reset _bit15
+#define AO_START_Pulse _bit14
+#define AO_Disarm _bit13
+#define AO_UI2_Arm_Disarm _bit12
+#define AO_UI2_Load _bit11
+#define AO_UI_Arm _bit10
+#define AO_UI_Load _bit9
+#define AO_UC_Arm _bit8
+#define AO_UC_Load _bit7
+#define AO_BC_Arm _bit6
+#define AO_BC_Load _bit5
+#define AO_DAC1_Update_Mode _bit4
+#define AO_LDAC1_Source_Select _bit3
+#define AO_DAC0_Update_Mode _bit2
+#define AO_LDAC0_Source_Select _bit1
+#define AO_UPDATE_Pulse _bit0
+
+#define DIO_Output_Register 10
+#define DIO_Parallel_Data_Out(a) ((a)&0xff)
+#define DIO_Parallel_Data_Mask 0xff
+#define DIO_SDOUT _bit0
+#define DIO_SDIN _bit4
+#define DIO_Serial_Data_Out(a) (((a)&0xff)<<8)
+#define DIO_Serial_Data_Mask 0xff00
+
+#define DIO_Control_Register 11
+#define DIO_Software_Serial_Control _bit11
+#define DIO_HW_Serial_Timebase _bit10
+#define DIO_HW_Serial_Enable _bit9
+#define DIO_HW_Serial_Start _bit8
+#define DIO_Pins_Dir(a) ((a)&0xff)
+#define DIO_Pins_Dir_Mask 0xff
+
+#define AI_Mode_1_Register 12
+#define AI_CONVERT_Source_Select(a) (((a) & 0x1f) << 11)
+#define AI_SI_Source_select(a) (((a) & 0x1f) << 6)
+#define AI_CONVERT_Source_Polarity _bit5
+#define AI_SI_Source_Polarity _bit4
+#define AI_Start_Stop _bit3
+#define AI_Mode_1_Reserved _bit2
+#define AI_Continuous _bit1
+#define AI_Trigger_Once _bit0
+
+#define AI_Mode_2_Register 13
+#define AI_SC_Gate_Enable _bit15
+#define AI_Start_Stop_Gate_Enable _bit14
+#define AI_Pre_Trigger _bit13
+#define AI_External_MUX_Present _bit12
+#define AI_SI2_Initial_Load_Source _bit9
+#define AI_SI2_Reload_Mode _bit8
+#define AI_SI_Initial_Load_Source _bit7
+#define AI_SI_Reload_Mode(a) (((a) & 0x7)<<4)
+#define AI_SI_Write_Switch _bit3
+#define AI_SC_Initial_Load_Source _bit2
+#define AI_SC_Reload_Mode _bit1
+#define AI_SC_Write_Switch _bit0
+
+#define AI_SI_Load_A_Registers 14
+#define AI_SI_Load_B_Registers 16
+#define AI_SC_Load_A_Registers 18
+#define AI_SC_Load_B_Registers 20
+#define AI_SI_Save_Registers 64
+#define AI_SC_Save_Registers 66
+
+#define AI_SI2_Load_A_Register 23
+#define AI_SI2_Load_B_Register 25
+
+#define Joint_Status_1_Register 27
+#define DIO_Serial_IO_In_Progress_St _bit12
+
+#define DIO_Serial_Input_Register 28
+#define Joint_Status_2_Register 29
+enum Joint_Status_2_Bits {
+ AO_TMRDACWRs_In_Progress_St = 0x20,
+};
+
+#define AO_Mode_1_Register 38
+#define AO_UPDATE_Source_Select(x) (((x)&0x1f)<<11)
+#define AO_UI_Source_Select(x) (((x)&0x1f)<<6)
+#define AO_Multiple_Channels _bit5
+#define AO_UPDATE_Source_Polarity _bit4
+#define AO_UI_Source_Polarity _bit3
+#define AO_UC_Switch_Load_Every_TC _bit2
+#define AO_Continuous _bit1
+#define AO_Trigger_Once _bit0
+
+#define AO_Mode_2_Register 39
+#define AO_FIFO_Mode_Mask (0x3 << 14)
+enum AO_FIFO_Mode_Bits {
+ AO_FIFO_Mode_HF_to_F = (3 << 14),
+ AO_FIFO_Mode_F = (2 << 14),
+ AO_FIFO_Mode_HF = (1 << 14),
+ AO_FIFO_Mode_E = (0 << 14),
+};
+#define AO_FIFO_Retransmit_Enable _bit13
+#define AO_START1_Disable _bit12
+#define AO_UC_Initial_Load_Source _bit11
+#define AO_UC_Write_Switch _bit10
+#define AO_UI2_Initial_Load_Source _bit9
+#define AO_UI2_Reload_Mode _bit8
+#define AO_UI_Initial_Load_Source _bit7
+#define AO_UI_Reload_Mode(x) (((x) & 0x7) << 4)
+#define AO_UI_Write_Switch _bit3
+#define AO_BC_Initial_Load_Source _bit2
+#define AO_BC_Reload_Mode _bit1
+#define AO_BC_Write_Switch _bit0
+
+#define AO_UI_Load_A_Register 40
+#define AO_UI_Load_A_Register_High 40
+#define AO_UI_Load_A_Register_Low 41
+#define AO_UI_Load_B_Register 42
+#define AO_UI_Save_Registers 16
+#define AO_BC_Load_A_Register 44
+#define AO_BC_Load_A_Register_High 44
+#define AO_BC_Load_A_Register_Low 45
+#define AO_BC_Load_B_Register 46
+#define AO_BC_Load_B_Register_High 46
+#define AO_BC_Load_B_Register_Low 47
+#define AO_BC_Save_Registers 18
+#define AO_UC_Load_A_Register 48
+#define AO_UC_Load_A_Register_High 48
+#define AO_UC_Load_A_Register_Low 49
+#define AO_UC_Load_B_Register 50
+#define AO_UC_Save_Registers 20
+
+#define Clock_and_FOUT_Register 56
+enum Clock_and_FOUT_bits {
+ FOUT_Enable = _bit15,
+ FOUT_Timebase_Select = _bit14,
+ DIO_Serial_Out_Divide_By_2 = _bit13,
+ Slow_Internal_Time_Divide_By_2 = _bit12,
+ Slow_Internal_Timebase = _bit11,
+ G_Source_Divide_By_2 = _bit10,
+ Clock_To_Board_Divide_By_2 = _bit9,
+ Clock_To_Board = _bit8,
+ AI_Output_Divide_By_2 = _bit7,
+ AI_Source_Divide_By_2 = _bit6,
+ AO_Output_Divide_By_2 = _bit5,
+ AO_Source_Divide_By_2 = _bit4,
+ FOUT_Divider_mask = 0xf
+};
+static inline unsigned FOUT_Divider(unsigned divider)
+{
+ return divider & FOUT_Divider_mask;
+}
+
+#define IO_Bidirection_Pin_Register 57
+#define RTSI_Trig_Direction_Register 58
+enum RTSI_Trig_Direction_Bits {
+ Drive_RTSI_Clock_Bit = 0x1,
+ Use_RTSI_Clock_Bit = 0x2,
+};
+static inline unsigned RTSI_Output_Bit(unsigned channel, int is_mseries)
+{
+ unsigned max_channel;
+ unsigned base_bit_shift;
+ if (is_mseries) {
+ base_bit_shift = 8;
+ max_channel = 7;
+ } else {
+ base_bit_shift = 9;
+ max_channel = 6;
+ }
+ if (channel > max_channel) {
+ pr_err("%s: bug, invalid RTSI_channel=%i\n", __func__, channel);
+ return 0;
+ }
+ return 1 << (base_bit_shift + channel);
+}
+
+#define Interrupt_Control_Register 59
+#define Interrupt_B_Enable _bit15
+#define Interrupt_B_Output_Select(x) ((x)<<12)
+#define Interrupt_A_Enable _bit11
+#define Interrupt_A_Output_Select(x) ((x)<<8)
+#define Pass_Thru_0_Interrupt_Polarity _bit3
+#define Pass_Thru_1_Interrupt_Polarity _bit2
+#define Interrupt_Output_On_3_Pins _bit1
+#define Interrupt_Output_Polarity _bit0
+
+#define AI_Output_Control_Register 60
+#define AI_START_Output_Select _bit10
+#define AI_SCAN_IN_PROG_Output_Select(x) (((x) & 0x3) << 8)
+#define AI_EXTMUX_CLK_Output_Select(x) (((x) & 0x3) << 6)
+#define AI_LOCALMUX_CLK_Output_Select(x) ((x)<<4)
+#define AI_SC_TC_Output_Select(x) ((x)<<2)
+enum ai_convert_output_selection {
+ AI_CONVERT_Output_High_Z = 0,
+ AI_CONVERT_Output_Ground = 1,
+ AI_CONVERT_Output_Enable_Low = 2,
+ AI_CONVERT_Output_Enable_High = 3
+};
+static unsigned AI_CONVERT_Output_Select(enum ai_convert_output_selection
+ selection)
+{
+ return selection & 0x3;
+}
+
+#define AI_START_STOP_Select_Register 62
+#define AI_START_Polarity _bit15
+#define AI_STOP_Polarity _bit14
+#define AI_STOP_Sync _bit13
+#define AI_STOP_Edge _bit12
+#define AI_STOP_Select(a) (((a) & 0x1f)<<7)
+#define AI_START_Sync _bit6
+#define AI_START_Edge _bit5
+#define AI_START_Select(a) ((a) & 0x1f)
+
+#define AI_Trigger_Select_Register 63
+#define AI_START1_Polarity _bit15
+#define AI_START2_Polarity _bit14
+#define AI_START2_Sync _bit13
+#define AI_START2_Edge _bit12
+#define AI_START2_Select(a) (((a) & 0x1f) << 7)
+#define AI_START1_Sync _bit6
+#define AI_START1_Edge _bit5
+#define AI_START1_Select(a) ((a) & 0x1f)
+
+#define AI_DIV_Load_A_Register 64
+
+#define AO_Start_Select_Register 66
+#define AO_UI2_Software_Gate _bit15
+#define AO_UI2_External_Gate_Polarity _bit14
+#define AO_START_Polarity _bit13
+#define AO_AOFREQ_Enable _bit12
+#define AO_UI2_External_Gate_Select(a) (((a) & 0x1f) << 7)
+#define AO_START_Sync _bit6
+#define AO_START_Edge _bit5
+#define AO_START_Select(a) ((a) & 0x1f)
+
+#define AO_Trigger_Select_Register 67
+#define AO_UI2_External_Gate_Enable _bit15
+#define AO_Delayed_START1 _bit14
+#define AO_START1_Polarity _bit13
+#define AO_UI2_Source_Polarity _bit12
+#define AO_UI2_Source_Select(x) (((x)&0x1f)<<7)
+#define AO_START1_Sync _bit6
+#define AO_START1_Edge _bit5
+#define AO_START1_Select(x) (((x)&0x1f)<<0)
+
+#define AO_Mode_3_Register 70
+#define AO_UI2_Switch_Load_Next_TC _bit13
+#define AO_UC_Switch_Load_Every_BC_TC _bit12
+#define AO_Trigger_Length _bit11
+#define AO_Stop_On_Overrun_Error _bit5
+#define AO_Stop_On_BC_TC_Trigger_Error _bit4
+#define AO_Stop_On_BC_TC_Error _bit3
+#define AO_Not_An_UPDATE _bit2
+#define AO_Software_Gate _bit1
+#define AO_Last_Gate_Disable _bit0 /* M Series only */
+
+#define Joint_Reset_Register 72
+#define Software_Reset _bit11
+#define AO_Configuration_End _bit9
+#define AI_Configuration_End _bit8
+#define AO_Configuration_Start _bit5
+#define AI_Configuration_Start _bit4
+#define G1_Reset _bit3
+#define G0_Reset _bit2
+#define AO_Reset _bit1
+#define AI_Reset _bit0
+
+#define Interrupt_A_Enable_Register 73
+#define Pass_Thru_0_Interrupt_Enable _bit9
+#define G0_Gate_Interrupt_Enable _bit8
+#define AI_FIFO_Interrupt_Enable _bit7
+#define G0_TC_Interrupt_Enable _bit6
+#define AI_Error_Interrupt_Enable _bit5
+#define AI_STOP_Interrupt_Enable _bit4
+#define AI_START_Interrupt_Enable _bit3
+#define AI_START2_Interrupt_Enable _bit2
+#define AI_START1_Interrupt_Enable _bit1
+#define AI_SC_TC_Interrupt_Enable _bit0
+
+#define Interrupt_B_Enable_Register 75
+#define Pass_Thru_1_Interrupt_Enable _bit11
+#define G1_Gate_Interrupt_Enable _bit10
+#define G1_TC_Interrupt_Enable _bit9
+#define AO_FIFO_Interrupt_Enable _bit8
+#define AO_UI2_TC_Interrupt_Enable _bit7
+#define AO_UC_TC_Interrupt_Enable _bit6
+#define AO_Error_Interrupt_Enable _bit5
+#define AO_STOP_Interrupt_Enable _bit4
+#define AO_START_Interrupt_Enable _bit3
+#define AO_UPDATE_Interrupt_Enable _bit2
+#define AO_START1_Interrupt_Enable _bit1
+#define AO_BC_TC_Interrupt_Enable _bit0
+
+#define Second_IRQ_A_Enable_Register 74
+enum Second_IRQ_A_Enable_Bits {
+ AI_SC_TC_Second_Irq_Enable = _bit0,
+ AI_START1_Second_Irq_Enable = _bit1,
+ AI_START2_Second_Irq_Enable = _bit2,
+ AI_START_Second_Irq_Enable = _bit3,
+ AI_STOP_Second_Irq_Enable = _bit4,
+ AI_Error_Second_Irq_Enable = _bit5,
+ G0_TC_Second_Irq_Enable = _bit6,
+ AI_FIFO_Second_Irq_Enable = _bit7,
+ G0_Gate_Second_Irq_Enable = _bit8,
+ Pass_Thru_0_Second_Irq_Enable = _bit9
+};
+
+#define Second_IRQ_B_Enable_Register 76
+enum Second_IRQ_B_Enable_Bits {
+ AO_BC_TC_Second_Irq_Enable = _bit0,
+ AO_START1_Second_Irq_Enable = _bit1,
+ AO_UPDATE_Second_Irq_Enable = _bit2,
+ AO_START_Second_Irq_Enable = _bit3,
+ AO_STOP_Second_Irq_Enable = _bit4,
+ AO_Error_Second_Irq_Enable = _bit5,
+ AO_UC_TC_Second_Irq_Enable = _bit6,
+ AO_UI2_TC_Second_Irq_Enable = _bit7,
+ AO_FIFO_Second_Irq_Enable = _bit8,
+ G1_TC_Second_Irq_Enable = _bit9,
+ G1_Gate_Second_Irq_Enable = _bit10,
+ Pass_Thru_1_Second_Irq_Enable = _bit11
+};
+
+#define AI_Personal_Register 77
+#define AI_SHIFTIN_Pulse_Width _bit15
+#define AI_EOC_Polarity _bit14
+#define AI_SOC_Polarity _bit13
+#define AI_SHIFTIN_Polarity _bit12
+#define AI_CONVERT_Pulse_Timebase _bit11
+#define AI_CONVERT_Pulse_Width _bit10
+#define AI_CONVERT_Original_Pulse _bit9
+#define AI_FIFO_Flags_Polarity _bit8
+#define AI_Overrun_Mode _bit7
+#define AI_EXTMUX_CLK_Pulse_Width _bit6
+#define AI_LOCALMUX_CLK_Pulse_Width _bit5
+#define AI_AIFREQ_Polarity _bit4
+
+#define AO_Personal_Register 78
+enum AO_Personal_Bits {
+ AO_Interval_Buffer_Mode = 1 << 3,
+ AO_BC_Source_Select = 1 << 4,
+ AO_UPDATE_Pulse_Width = 1 << 5,
+ AO_UPDATE_Pulse_Timebase = 1 << 6,
+ AO_UPDATE_Original_Pulse = 1 << 7,
+ AO_DMA_PIO_Control = 1 << 8, /* M Series: reserved */
+ AO_AOFREQ_Polarity = 1 << 9, /* M Series: reserved */
+ AO_FIFO_Enable = 1 << 10,
+ AO_FIFO_Flags_Polarity = 1 << 11, /* M Series: reserved */
+ AO_TMRDACWR_Pulse_Width = 1 << 12,
+ AO_Fast_CPU = 1 << 13, /* M Series: reserved */
+ AO_Number_Of_DAC_Packages = 1 << 14, /* 1 for "single" mode, 0 for "dual" */
+ AO_Multiple_DACS_Per_Package = 1 << 15 /* m-series only */
+};
+#define RTSI_Trig_A_Output_Register 79
+#define RTSI_Trig_B_Output_Register 80
+enum RTSI_Trig_B_Output_Bits {
+ RTSI_Sub_Selection_1_Bit = 0x8000 /* not for m-series */
+};
+static inline unsigned RTSI_Trig_Output_Bits(unsigned rtsi_channel,
+ unsigned source)
+{
+ return (source & 0xf) << ((rtsi_channel % 4) * 4);
+};
+
+static inline unsigned RTSI_Trig_Output_Mask(unsigned rtsi_channel)
+{
+ return 0xf << ((rtsi_channel % 4) * 4);
+};
+
+/* inverse to RTSI_Trig_Output_Bits() */
+static inline unsigned RTSI_Trig_Output_Source(unsigned rtsi_channel,
+ unsigned bits)
+{
+ return (bits >> ((rtsi_channel % 4) * 4)) & 0xf;
+};
+
+#define RTSI_Board_Register 81
+#define Write_Strobe_0_Register 82
+#define Write_Strobe_1_Register 83
+#define Write_Strobe_2_Register 84
+#define Write_Strobe_3_Register 85
+
+#define AO_Output_Control_Register 86
+#define AO_External_Gate_Enable _bit15
+#define AO_External_Gate_Select(x) (((x)&0x1f)<<10)
+#define AO_Number_Of_Channels(x) (((x)&0xf)<<6)
+#define AO_UPDATE2_Output_Select(x) (((x)&0x3)<<4)
+#define AO_External_Gate_Polarity _bit3
+#define AO_UPDATE2_Output_Toggle _bit2
+enum ao_update_output_selection {
+ AO_Update_Output_High_Z = 0,
+ AO_Update_Output_Ground = 1,
+ AO_Update_Output_Enable_Low = 2,
+ AO_Update_Output_Enable_High = 3
+};
+static unsigned AO_UPDATE_Output_Select(enum ao_update_output_selection
+ selection)
+{
+ return selection & 0x3;
+}
+
+#define AI_Mode_3_Register 87
+#define AI_Trigger_Length _bit15
+#define AI_Delay_START _bit14
+#define AI_Software_Gate _bit13
+#define AI_SI_Special_Trigger_Delay _bit12
+#define AI_SI2_Source_Select _bit11
+#define AI_Delayed_START2 _bit10
+#define AI_Delayed_START1 _bit9
+#define AI_External_Gate_Mode _bit8
+#define AI_FIFO_Mode_HF_to_E (3<<6)
+#define AI_FIFO_Mode_F (2<<6)
+#define AI_FIFO_Mode_HF (1<<6)
+#define AI_FIFO_Mode_NE (0<<6)
+#define AI_External_Gate_Polarity _bit5
+#define AI_External_Gate_Select(a) ((a) & 0x1f)
+
+#define G_Autoincrement_Register(a) (68+(a))
+#define G_Command_Register(a) (6+(a))
+#define G_HW_Save_Register(a) (8+(a)*2)
+#define G_HW_Save_Register_High(a) (8+(a)*2)
+#define G_HW_Save_Register_Low(a) (9+(a)*2)
+#define G_Input_Select_Register(a) (36+(a))
+#define G_Load_A_Register(a) (28+(a)*4)
+#define G_Load_A_Register_High(a) (28+(a)*4)
+#define G_Load_A_Register_Low(a) (29+(a)*4)
+#define G_Load_B_Register(a) (30+(a)*4)
+#define G_Load_B_Register_High(a) (30+(a)*4)
+#define G_Load_B_Register_Low(a) (31+(a)*4)
+#define G_Mode_Register(a) (26+(a))
+#define G_Save_Register(a) (12+(a)*2)
+#define G_Save_Register_High(a) (12+(a)*2)
+#define G_Save_Register_Low(a) (13+(a)*2)
+#define G_Status_Register 4
+#define Analog_Trigger_Etc_Register 61
+
+/* command register */
+#define G_Disarm_Copy _bit15 /* strobe */
+#define G_Save_Trace_Copy _bit14
+#define G_Arm_Copy _bit13 /* strobe */
+#define G_Bank_Switch_Start _bit10 /* strobe */
+#define G_Little_Big_Endian _bit9
+#define G_Synchronized_Gate _bit8
+#define G_Write_Switch _bit7
+#define G_Up_Down(a) (((a)&0x03)<<5)
+#define G_Disarm _bit4 /* strobe */
+#define G_Analog_Trigger_Reset _bit3 /* strobe */
+#define G_Save_Trace _bit1
+#define G_Arm _bit0 /* strobe */
+
+/*channel agnostic names for the command register #defines */
+#define G_Bank_Switch_Enable _bit12
+#define G_Bank_Switch_Mode _bit11
+#define G_Load _bit2 /* strobe */
+
+/* input select register */
+#define G_Gate_Select(a) (((a)&0x1f)<<7)
+#define G_Source_Select(a) (((a)&0x1f)<<2)
+#define G_Write_Acknowledges_Irq _bit1
+#define G_Read_Acknowledges_Irq _bit0
+
+/* same input select register, but with channel agnostic names */
+#define G_Source_Polarity _bit15
+#define G_Output_Polarity _bit14
+#define G_OR_Gate _bit13
+#define G_Gate_Select_Load_Source _bit12
+
+/* mode register */
+#define G_Loading_On_TC _bit12
+#define G_Output_Mode(a) (((a)&0x03)<<8)
+#define G_Trigger_Mode_For_Edge_Gate(a) (((a)&0x03)<<3)
+#define G_Gating_Mode(a) (((a)&0x03)<<0)
+
+/* same input mode register, but with channel agnostic names */
+#define G_Load_Source_Select _bit7
+#define G_Reload_Source_Switching _bit15
+#define G_Loading_On_Gate _bit14
+#define G_Gate_Polarity _bit13
+
+#define G_Counting_Once(a) (((a)&0x03)<<10)
+#define G_Stop_Mode(a) (((a)&0x03)<<5)
+#define G_Gate_On_Both_Edges _bit2
+
+/* G_Status_Register */
+#define G1_Gate_Error_St _bit15
+#define G0_Gate_Error_St _bit14
+#define G1_TC_Error_St _bit13
+#define G0_TC_Error_St _bit12
+#define G1_No_Load_Between_Gates_St _bit11
+#define G0_No_Load_Between_Gates_St _bit10
+#define G1_Armed_St _bit9
+#define G0_Armed_St _bit8
+#define G1_Stale_Data_St _bit7
+#define G0_Stale_Data_St _bit6
+#define G1_Next_Load_Source_St _bit5
+#define G0_Next_Load_Source_St _bit4
+#define G1_Counting_St _bit3
+#define G0_Counting_St _bit2
+#define G1_Save_St _bit1
+#define G0_Save_St _bit0
+
+/* general purpose counter timer */
+#define G_Autoincrement(a) ((a)<<0)
+
+/*Analog_Trigger_Etc_Register*/
+#define Analog_Trigger_Mode(x) ((x) & 0x7)
+#define Analog_Trigger_Enable _bit3
+#define Analog_Trigger_Drive _bit4
+#define GPFO_1_Output_Select _bit7
+#define GPFO_0_Output_Select(a) ((a)<<11)
+#define GPFO_0_Output_Enable _bit14
+#define GPFO_1_Output_Enable _bit15
+
+/* Additional windowed registers unique to E series */
+
+/* 16 bit registers shadowed from DAQ-STC */
+#define Window_Address 0x00
+#define Window_Data 0x02
+
+#define Configuration_Memory_Clear 82
+#define ADC_FIFO_Clear 83
+#define DAC_FIFO_Clear 84
+
+/* i/o port offsets */
+
+/* 8 bit registers */
+#define XXX_Status 0x01
+enum XXX_Status_Bits {
+ PROMOUT = 0x1,
+ AI_FIFO_LOWER_NOT_EMPTY = 0x8,
+};
+#define Serial_Command 0x0d
+#define Misc_Command 0x0f
+#define Port_A 0x19
+#define Port_B 0x1b
+#define Port_C 0x1d
+#define Configuration 0x1f
+#define Strobes 0x01
+#define Channel_A_Mode 0x03
+#define Channel_B_Mode 0x05
+#define Channel_C_Mode 0x07
+#define AI_AO_Select 0x09
+enum AI_AO_Select_Bits {
+ AI_DMA_Select_Shift = 0,
+ AI_DMA_Select_Mask = 0xf,
+ AO_DMA_Select_Shift = 4,
+ AO_DMA_Select_Mask = 0xf << AO_DMA_Select_Shift
+};
+#define G0_G1_Select 0x0b
+static inline unsigned ni_stc_dma_channel_select_bitfield(unsigned channel)
+{
+ if (channel < 4)
+ return 1 << channel;
+ if (channel == 4)
+ return 0x3;
+ if (channel == 5)
+ return 0x5;
+ BUG();
+ return 0;
+}
+
+static inline unsigned GPCT_DMA_Select_Bits(unsigned gpct_index,
+ unsigned mite_channel)
+{
+ BUG_ON(gpct_index > 1);
+ return ni_stc_dma_channel_select_bitfield(mite_channel) << (4 *
+ gpct_index);
+}
+
+static inline unsigned GPCT_DMA_Select_Mask(unsigned gpct_index)
+{
+ BUG_ON(gpct_index > 1);
+ return 0xf << (4 * gpct_index);
+}
+
+/* 16 bit registers */
+
+#define Configuration_Memory_Low 0x10
+enum Configuration_Memory_Low_Bits {
+ AI_DITHER = 0x200,
+ AI_LAST_CHANNEL = 0x8000,
+};
+#define Configuration_Memory_High 0x12
+enum Configuration_Memory_High_Bits {
+ AI_AC_COUPLE = 0x800,
+ AI_DIFFERENTIAL = 0x1000,
+ AI_COMMON = 0x2000,
+ AI_GROUND = 0x3000,
+};
+static inline unsigned int AI_CONFIG_CHANNEL(unsigned int channel)
+{
+ return channel & 0x3f;
+}
+
+#define ADC_FIFO_Data_Register 0x1c
+
+#define AO_Configuration 0x16
+#define AO_Bipolar _bit0
+#define AO_Deglitch _bit1
+#define AO_Ext_Ref _bit2
+#define AO_Ground_Ref _bit3
+#define AO_Channel(x) ((x) << 8)
+
+#define DAC_FIFO_Data 0x1e
+#define DAC0_Direct_Data 0x18
+#define DAC1_Direct_Data 0x1a
+
+/* 611x registers (these boards differ from the e-series) */
+
+#define Magic_611x 0x19 /* w8 (new) */
+#define Calibration_Channel_Select_611x 0x1a /* w16 (new) */
+#define ADC_FIFO_Data_611x 0x1c /* r32 (incompatible) */
+#define AI_FIFO_Offset_Load_611x 0x05 /* r8 (new) */
+#define DAC_FIFO_Data_611x 0x14 /* w32 (incompatible) */
+#define Cal_Gain_Select_611x 0x05 /* w8 (new) */
+
+#define AO_Window_Address_611x 0x18
+#define AO_Window_Data_611x 0x1e
+
+/* 6143 registers */
+#define Magic_6143 0x19 /* w8 */
+#define G0G1_DMA_Select_6143 0x0B /* w8 */
+#define PipelineDelay_6143 0x1f /* w8 */
+#define EOC_Set_6143 0x1D /* w8 */
+#define AIDMA_Select_6143 0x09 /* w8 */
+#define AIFIFO_Data_6143 0x8C /* w32 */
+#define AIFIFO_Flag_6143 0x84 /* w32 */
+#define AIFIFO_Control_6143 0x88 /* w32 */
+#define AIFIFO_Status_6143 0x88 /* w32 */
+#define AIFIFO_DMAThreshold_6143 0x90 /* w32 */
+#define AIFIFO_Words_Available_6143 0x94 /* w32 */
+
+#define Calibration_Channel_6143 0x42 /* w16 */
+#define Calibration_LowTime_6143 0x20 /* w16 */
+#define Calibration_HighTime_6143 0x22 /* w16 */
+#define Relay_Counter_Load_Val__6143 0x4C /* w32 */
+#define Signature_6143 0x50 /* w32 */
+#define Release_Date_6143 0x54 /* w32 */
+#define Release_Oldest_Date_6143 0x58 /* w32 */
+
+#define Calibration_Channel_6143_RelayOn 0x8000 /* Calibration relay switch On */
+#define Calibration_Channel_6143_RelayOff 0x4000 /* Calibration relay switch Off */
+#define Calibration_Channel_Gnd_Gnd 0x00 /* Offset Calibration */
+#define Calibration_Channel_2v5_Gnd 0x02 /* 2.5V Reference */
+#define Calibration_Channel_Pwm_Gnd 0x05 /* +/- 5V Self Cal */
+#define Calibration_Channel_2v5_Pwm 0x0a /* PWM Calibration */
+#define Calibration_Channel_Pwm_Pwm 0x0d /* CMRR */
+#define Calibration_Channel_Gnd_Pwm 0x0e /* PWM Calibration */
+
+/* 671x, 611x registers */
+
+/* 671xi, 611x windowed ao registers */
+enum windowed_regs_67xx_61xx {
+ AO_Immediate_671x = 0x11, /* W 16 */
+ AO_Timed_611x = 0x10, /* W 16 */
+ AO_FIFO_Offset_Load_611x = 0x13, /* W32 */
+ AO_Later_Single_Point_Updates = 0x14, /* W 16 */
+ AO_Waveform_Generation_611x = 0x15, /* W 16 */
+ AO_Misc_611x = 0x16, /* W 16 */
+ AO_Calibration_Channel_Select_67xx = 0x17, /* W 16 */
+ AO_Configuration_2_67xx = 0x18, /* W 16 */
+ CAL_ADC_Command_67xx = 0x19, /* W 8 */
+ CAL_ADC_Status_67xx = 0x1a, /* R 8 */
+ CAL_ADC_Data_67xx = 0x1b, /* R 16 */
+ CAL_ADC_Config_Data_High_Word_67xx = 0x1c, /* RW 16 */
+ CAL_ADC_Config_Data_Low_Word_67xx = 0x1d, /* RW 16 */
+};
+static inline unsigned int DACx_Direct_Data_671x(int channel)
+{
+ return channel;
+}
+
+enum AO_Misc_611x_Bits {
+ CLEAR_WG = 1,
+};
+enum cs5529_configuration_bits {
+ CSCFG_CAL_CONTROL_MASK = 0x7,
+ CSCFG_SELF_CAL_OFFSET = 0x1,
+ CSCFG_SELF_CAL_GAIN = 0x2,
+ CSCFG_SELF_CAL_OFFSET_GAIN = 0x3,
+ CSCFG_SYSTEM_CAL_OFFSET = 0x5,
+ CSCFG_SYSTEM_CAL_GAIN = 0x6,
+ CSCFG_DONE = 1 << 3,
+ CSCFG_POWER_SAVE_SELECT = 1 << 4,
+ CSCFG_PORT_MODE = 1 << 5,
+ CSCFG_RESET_VALID = 1 << 6,
+ CSCFG_RESET = 1 << 7,
+ CSCFG_UNIPOLAR = 1 << 12,
+ CSCFG_WORD_RATE_2180_CYCLES = 0x0 << 13,
+ CSCFG_WORD_RATE_1092_CYCLES = 0x1 << 13,
+ CSCFG_WORD_RATE_532_CYCLES = 0x2 << 13,
+ CSCFG_WORD_RATE_388_CYCLES = 0x3 << 13,
+ CSCFG_WORD_RATE_324_CYCLES = 0x4 << 13,
+ CSCFG_WORD_RATE_17444_CYCLES = 0x5 << 13,
+ CSCFG_WORD_RATE_8724_CYCLES = 0x6 << 13,
+ CSCFG_WORD_RATE_4364_CYCLES = 0x7 << 13,
+ CSCFG_WORD_RATE_MASK = 0x7 << 13,
+ CSCFG_LOW_POWER = 1 << 16,
+};
+static inline unsigned int CS5529_CONFIG_DOUT(int output)
+{
+ return 1 << (18 + output);
+}
+
+static inline unsigned int CS5529_CONFIG_AOUT(int output)
+{
+ return 1 << (22 + output);
+}
+
+enum cs5529_command_bits {
+ CSCMD_POWER_SAVE = 0x1,
+ CSCMD_REGISTER_SELECT_MASK = 0xe,
+ CSCMD_OFFSET_REGISTER = 0x0,
+ CSCMD_GAIN_REGISTER = 0x2,
+ CSCMD_CONFIG_REGISTER = 0x4,
+ CSCMD_READ = 0x10,
+ CSCMD_CONTINUOUS_CONVERSIONS = 0x20,
+ CSCMD_SINGLE_CONVERSION = 0x40,
+ CSCMD_COMMAND = 0x80,
+};
+enum cs5529_status_bits {
+ CSS_ADC_BUSY = 0x1,
+ CSS_OSC_DETECT = 0x2, /* indicates adc error */
+ CSS_OVERRANGE = 0x4,
+};
+#define SerDacLd(x) (0x08<<(x))
+
+/*
+ This is stuff unique to the NI E series drivers,
+ but I thought I'd put it here anyway.
+*/
+
+enum { ai_gain_16 =
+ 0, ai_gain_8, ai_gain_14, ai_gain_4, ai_gain_611x, ai_gain_622x,
+ ai_gain_628x, ai_gain_6143
+};
+enum caldac_enum { caldac_none = 0, mb88341, dac8800, dac8043, ad8522,
+ ad8804, ad8842, ad8804_debug
+};
+enum ni_reg_type {
+ ni_reg_normal = 0x0,
+ ni_reg_611x = 0x1,
+ ni_reg_6711 = 0x2,
+ ni_reg_6713 = 0x4,
+ ni_reg_67xx_mask = 0x6,
+ ni_reg_6xxx_mask = 0x7,
+ ni_reg_622x = 0x8,
+ ni_reg_625x = 0x10,
+ ni_reg_628x = 0x18,
+ ni_reg_m_series_mask = 0x18,
+ ni_reg_6143 = 0x20
+};
+
+static const struct comedi_lrange range_ni_E_ao_ext;
+
+enum m_series_register_offsets {
+ M_Offset_CDIO_DMA_Select = 0x7, /* write */
+ M_Offset_SCXI_Status = 0x7, /* read */
+ M_Offset_AI_AO_Select = 0x9, /* write, same offset as e-series */
+ M_Offset_SCXI_Serial_Data_In = 0x9, /* read */
+ M_Offset_G0_G1_Select = 0xb, /* write, same offset as e-series */
+ M_Offset_Misc_Command = 0xf,
+ M_Offset_SCXI_Serial_Data_Out = 0x11,
+ M_Offset_SCXI_Control = 0x13,
+ M_Offset_SCXI_Output_Enable = 0x15,
+ M_Offset_AI_FIFO_Data = 0x1c,
+ M_Offset_Static_Digital_Output = 0x24, /* write */
+ M_Offset_Static_Digital_Input = 0x24, /* read */
+ M_Offset_DIO_Direction = 0x28,
+ M_Offset_Cal_PWM = 0x40,
+ M_Offset_AI_Config_FIFO_Data = 0x5e,
+ M_Offset_Interrupt_C_Enable = 0x88, /* write */
+ M_Offset_Interrupt_C_Status = 0x88, /* read */
+ M_Offset_Analog_Trigger_Control = 0x8c,
+ M_Offset_AO_Serial_Interrupt_Enable = 0xa0,
+ M_Offset_AO_Serial_Interrupt_Ack = 0xa1, /* write */
+ M_Offset_AO_Serial_Interrupt_Status = 0xa1, /* read */
+ M_Offset_AO_Calibration = 0xa3,
+ M_Offset_AO_FIFO_Data = 0xa4,
+ M_Offset_PFI_Filter = 0xb0,
+ M_Offset_RTSI_Filter = 0xb4,
+ M_Offset_SCXI_Legacy_Compatibility = 0xbc,
+ M_Offset_Interrupt_A_Ack = 0x104, /* write */
+ M_Offset_AI_Status_1 = 0x104, /* read */
+ M_Offset_Interrupt_B_Ack = 0x106, /* write */
+ M_Offset_AO_Status_1 = 0x106, /* read */
+ M_Offset_AI_Command_2 = 0x108, /* write */
+ M_Offset_G01_Status = 0x108, /* read */
+ M_Offset_AO_Command_2 = 0x10a,
+ M_Offset_AO_Status_2 = 0x10c, /* read */
+ M_Offset_G0_Command = 0x10c, /* write */
+ M_Offset_G1_Command = 0x10e, /* write */
+ M_Offset_G0_HW_Save = 0x110,
+ M_Offset_G0_HW_Save_High = 0x110,
+ M_Offset_AI_Command_1 = 0x110,
+ M_Offset_G0_HW_Save_Low = 0x112,
+ M_Offset_AO_Command_1 = 0x112,
+ M_Offset_G1_HW_Save = 0x114,
+ M_Offset_G1_HW_Save_High = 0x114,
+ M_Offset_G1_HW_Save_Low = 0x116,
+ M_Offset_AI_Mode_1 = 0x118,
+ M_Offset_G0_Save = 0x118,
+ M_Offset_G0_Save_High = 0x118,
+ M_Offset_AI_Mode_2 = 0x11a,
+ M_Offset_G0_Save_Low = 0x11a,
+ M_Offset_AI_SI_Load_A = 0x11c,
+ M_Offset_G1_Save = 0x11c,
+ M_Offset_G1_Save_High = 0x11c,
+ M_Offset_G1_Save_Low = 0x11e,
+ M_Offset_AI_SI_Load_B = 0x120, /* write */
+ M_Offset_AO_UI_Save = 0x120, /* read */
+ M_Offset_AI_SC_Load_A = 0x124, /* write */
+ M_Offset_AO_BC_Save = 0x124, /* read */
+ M_Offset_AI_SC_Load_B = 0x128, /* write */
+ M_Offset_AO_UC_Save = 0x128, /* read */
+ M_Offset_AI_SI2_Load_A = 0x12c,
+ M_Offset_AI_SI2_Load_B = 0x130,
+ M_Offset_G0_Mode = 0x134,
+ M_Offset_G1_Mode = 0x136, /* write */
+ M_Offset_Joint_Status_1 = 0x136, /* read */
+ M_Offset_G0_Load_A = 0x138,
+ M_Offset_Joint_Status_2 = 0x13a,
+ M_Offset_G0_Load_B = 0x13c,
+ M_Offset_G1_Load_A = 0x140,
+ M_Offset_G1_Load_B = 0x144,
+ M_Offset_G0_Input_Select = 0x148,
+ M_Offset_G1_Input_Select = 0x14a,
+ M_Offset_AO_Mode_1 = 0x14c,
+ M_Offset_AO_Mode_2 = 0x14e,
+ M_Offset_AO_UI_Load_A = 0x150,
+ M_Offset_AO_UI_Load_B = 0x154,
+ M_Offset_AO_BC_Load_A = 0x158,
+ M_Offset_AO_BC_Load_B = 0x15c,
+ M_Offset_AO_UC_Load_A = 0x160,
+ M_Offset_AO_UC_Load_B = 0x164,
+ M_Offset_Clock_and_FOUT = 0x170,
+ M_Offset_IO_Bidirection_Pin = 0x172,
+ M_Offset_RTSI_Trig_Direction = 0x174,
+ M_Offset_Interrupt_Control = 0x176,
+ M_Offset_AI_Output_Control = 0x178,
+ M_Offset_Analog_Trigger_Etc = 0x17a,
+ M_Offset_AI_START_STOP_Select = 0x17c,
+ M_Offset_AI_Trigger_Select = 0x17e,
+ M_Offset_AI_SI_Save = 0x180, /* read */
+ M_Offset_AI_DIV_Load_A = 0x180, /* write */
+ M_Offset_AI_SC_Save = 0x184, /* read */
+ M_Offset_AO_Start_Select = 0x184, /* write */
+ M_Offset_AO_Trigger_Select = 0x186,
+ M_Offset_AO_Mode_3 = 0x18c,
+ M_Offset_G0_Autoincrement = 0x188,
+ M_Offset_G1_Autoincrement = 0x18a,
+ M_Offset_Joint_Reset = 0x190,
+ M_Offset_Interrupt_A_Enable = 0x192,
+ M_Offset_Interrupt_B_Enable = 0x196,
+ M_Offset_AI_Personal = 0x19a,
+ M_Offset_AO_Personal = 0x19c,
+ M_Offset_RTSI_Trig_A_Output = 0x19e,
+ M_Offset_RTSI_Trig_B_Output = 0x1a0,
+ M_Offset_RTSI_Shared_MUX = 0x1a2,
+ M_Offset_AO_Output_Control = 0x1ac,
+ M_Offset_AI_Mode_3 = 0x1ae,
+ M_Offset_Configuration_Memory_Clear = 0x1a4,
+ M_Offset_AI_FIFO_Clear = 0x1a6,
+ M_Offset_AO_FIFO_Clear = 0x1a8,
+ M_Offset_G0_Counting_Mode = 0x1b0,
+ M_Offset_G1_Counting_Mode = 0x1b2,
+ M_Offset_G0_Second_Gate = 0x1b4,
+ M_Offset_G1_Second_Gate = 0x1b6,
+ M_Offset_G0_DMA_Config = 0x1b8, /* write */
+ M_Offset_G0_DMA_Status = 0x1b8, /* read */
+ M_Offset_G1_DMA_Config = 0x1ba, /* write */
+ M_Offset_G1_DMA_Status = 0x1ba, /* read */
+ M_Offset_G0_MSeries_ABZ = 0x1c0,
+ M_Offset_G1_MSeries_ABZ = 0x1c2,
+ M_Offset_Clock_and_Fout2 = 0x1c4,
+ M_Offset_PLL_Control = 0x1c6,
+ M_Offset_PLL_Status = 0x1c8,
+ M_Offset_PFI_Output_Select_1 = 0x1d0,
+ M_Offset_PFI_Output_Select_2 = 0x1d2,
+ M_Offset_PFI_Output_Select_3 = 0x1d4,
+ M_Offset_PFI_Output_Select_4 = 0x1d6,
+ M_Offset_PFI_Output_Select_5 = 0x1d8,
+ M_Offset_PFI_Output_Select_6 = 0x1da,
+ M_Offset_PFI_DI = 0x1dc,
+ M_Offset_PFI_DO = 0x1de,
+ M_Offset_AI_Config_FIFO_Bypass = 0x218,
+ M_Offset_SCXI_DIO_Enable = 0x21c,
+ M_Offset_CDI_FIFO_Data = 0x220, /* read */
+ M_Offset_CDO_FIFO_Data = 0x220, /* write */
+ M_Offset_CDIO_Status = 0x224, /* read */
+ M_Offset_CDIO_Command = 0x224, /* write */
+ M_Offset_CDI_Mode = 0x228,
+ M_Offset_CDO_Mode = 0x22c,
+ M_Offset_CDI_Mask_Enable = 0x230,
+ M_Offset_CDO_Mask_Enable = 0x234,
+};
+static inline int M_Offset_AO_Waveform_Order(int channel)
+{
+ return 0xc2 + 0x4 * channel;
+};
+
+static inline int M_Offset_AO_Config_Bank(int channel)
+{
+ return 0xc3 + 0x4 * channel;
+};
+
+static inline int M_Offset_DAC_Direct_Data(int channel)
+{
+ return 0xc0 + 0x4 * channel;
+}
+
+static inline int M_Offset_Gen_PWM(int channel)
+{
+ return 0x44 + 0x2 * channel;
+}
+
+static inline int M_Offset_Static_AI_Control(int i)
+{
+ int offset[] = {
+ 0x64,
+ 0x261,
+ 0x262,
+ 0x263,
+ };
+ if (((unsigned)i) >= ARRAY_SIZE(offset)) {
+ pr_err("%s: invalid channel=%i\n", __func__, i);
+ return offset[0];
+ }
+ return offset[i];
+};
+
+static inline int M_Offset_AO_Reference_Attenuation(int channel)
+{
+ int offset[] = {
+ 0x264,
+ 0x265,
+ 0x266,
+ 0x267
+ };
+ if (((unsigned)channel) >= ARRAY_SIZE(offset)) {
+ pr_err("%s: invalid channel=%i\n", __func__, channel);
+ return offset[0];
+ }
+ return offset[channel];
+};
+
+static inline unsigned M_Offset_PFI_Output_Select(unsigned n)
+{
+ if (n < 1 || n > NUM_PFI_OUTPUT_SELECT_REGS) {
+ pr_err("%s: invalid pfi output select register=%i\n",
+ __func__, n);
+ return M_Offset_PFI_Output_Select_1;
+ }
+ return M_Offset_PFI_Output_Select_1 + (n - 1) * 2;
+}
+
+enum MSeries_AI_Config_FIFO_Data_Bits {
+ MSeries_AI_Config_Channel_Type_Mask = 0x7 << 6,
+ MSeries_AI_Config_Channel_Type_Calibration_Bits = 0x0,
+ MSeries_AI_Config_Channel_Type_Differential_Bits = 0x1 << 6,
+ MSeries_AI_Config_Channel_Type_Common_Ref_Bits = 0x2 << 6,
+ MSeries_AI_Config_Channel_Type_Ground_Ref_Bits = 0x3 << 6,
+ MSeries_AI_Config_Channel_Type_Aux_Bits = 0x5 << 6,
+ MSeries_AI_Config_Channel_Type_Ghost_Bits = 0x7 << 6,
+ MSeries_AI_Config_Polarity_Bit = 0x1000, /* 0 for 2's complement encoding */
+ MSeries_AI_Config_Dither_Bit = 0x2000,
+ MSeries_AI_Config_Last_Channel_Bit = 0x4000,
+};
+static inline unsigned MSeries_AI_Config_Channel_Bits(unsigned channel)
+{
+ return channel & 0xf;
+}
+
+static inline unsigned MSeries_AI_Config_Bank_Bits(enum ni_reg_type reg_type,
+ unsigned channel)
+{
+ unsigned bits = channel & 0x30;
+ if (reg_type == ni_reg_622x) {
+ if (channel & 0x40)
+ bits |= 0x400;
+ }
+ return bits;
+}
+
+static inline unsigned MSeries_AI_Config_Gain_Bits(unsigned range)
+{
+ return (range & 0x7) << 9;
+}
+
+enum MSeries_Clock_and_Fout2_Bits {
+ MSeries_PLL_In_Source_Select_RTSI0_Bits = 0xb,
+ MSeries_PLL_In_Source_Select_Star_Trigger_Bits = 0x14,
+ MSeries_PLL_In_Source_Select_RTSI7_Bits = 0x1b,
+ MSeries_PLL_In_Source_Select_PXI_Clock10 = 0x1d,
+ MSeries_PLL_In_Source_Select_Mask = 0x1f,
+ MSeries_Timebase1_Select_Bit = 0x20, /* use PLL for timebase 1 */
+ MSeries_Timebase3_Select_Bit = 0x40, /* use PLL for timebase 3 */
+ /* use 10MHz instead of 20MHz for RTSI clock frequency. Appears
+ to have no effect, at least on pxi-6281, which always uses
+ 20MHz rtsi clock frequency */
+ MSeries_RTSI_10MHz_Bit = 0x80
+};
+static inline unsigned MSeries_PLL_In_Source_Select_RTSI_Bits(unsigned
+ RTSI_channel)
+{
+ if (RTSI_channel > 7) {
+ pr_err("%s: bug, invalid RTSI_channel=%i\n", __func__,
+ RTSI_channel);
+ return 0;
+ }
+ if (RTSI_channel == 7)
+ return MSeries_PLL_In_Source_Select_RTSI7_Bits;
+ else
+ return MSeries_PLL_In_Source_Select_RTSI0_Bits + RTSI_channel;
+}
+
+enum MSeries_PLL_Control_Bits {
+ MSeries_PLL_Enable_Bit = 0x1000,
+ MSeries_PLL_VCO_Mode_200_325MHz_Bits = 0x0,
+ MSeries_PLL_VCO_Mode_175_225MHz_Bits = 0x2000,
+ MSeries_PLL_VCO_Mode_100_225MHz_Bits = 0x4000,
+ MSeries_PLL_VCO_Mode_75_150MHz_Bits = 0x6000,
+};
+static inline unsigned MSeries_PLL_Divisor_Bits(unsigned divisor)
+{
+ static const unsigned max_divisor = 0x10;
+ if (divisor < 1 || divisor > max_divisor) {
+ pr_err("%s: bug, invalid divisor=%i\n", __func__, divisor);
+ return 0;
+ }
+ return (divisor & 0xf) << 8;
+}
+
+static inline unsigned MSeries_PLL_Multiplier_Bits(unsigned multiplier)
+{
+ static const unsigned max_multiplier = 0x100;
+ if (multiplier < 1 || multiplier > max_multiplier) {
+ pr_err("%s: bug, invalid multiplier=%i\n", __func__,
+ multiplier);
+ return 0;
+ }
+ return multiplier & 0xff;
+}
+
+enum MSeries_PLL_Status {
+ MSeries_PLL_Locked_Bit = 0x1
+};
+
+enum MSeries_AI_Config_FIFO_Bypass_Bits {
+ MSeries_AI_Bypass_Channel_Mask = 0x7,
+ MSeries_AI_Bypass_Bank_Mask = 0x78,
+ MSeries_AI_Bypass_Cal_Sel_Pos_Mask = 0x380,
+ MSeries_AI_Bypass_Cal_Sel_Neg_Mask = 0x1c00,
+ MSeries_AI_Bypass_Mode_Mux_Mask = 0x6000,
+ MSeries_AO_Bypass_AO_Cal_Sel_Mask = 0x38000,
+ MSeries_AI_Bypass_Gain_Mask = 0x1c0000,
+ MSeries_AI_Bypass_Dither_Bit = 0x200000,
+ MSeries_AI_Bypass_Polarity_Bit = 0x400000, /* 0 for 2's complement encoding */
+ MSeries_AI_Bypass_Config_FIFO_Bit = 0x80000000
+};
+static inline unsigned MSeries_AI_Bypass_Cal_Sel_Pos_Bits(int
+ calibration_source)
+{
+ return (calibration_source << 7) & MSeries_AI_Bypass_Cal_Sel_Pos_Mask;
+}
+
+static inline unsigned MSeries_AI_Bypass_Cal_Sel_Neg_Bits(int
+ calibration_source)
+{
+ return (calibration_source << 10) & MSeries_AI_Bypass_Cal_Sel_Pos_Mask;
+}
+
+static inline unsigned MSeries_AI_Bypass_Gain_Bits(int gain)
+{
+ return (gain << 18) & MSeries_AI_Bypass_Gain_Mask;
+}
+
+enum MSeries_AO_Config_Bank_Bits {
+ MSeries_AO_DAC_Offset_Select_Mask = 0x7,
+ MSeries_AO_DAC_Offset_0V_Bits = 0x0,
+ MSeries_AO_DAC_Offset_5V_Bits = 0x1,
+ MSeries_AO_DAC_Reference_Mask = 0x38,
+ MSeries_AO_DAC_Reference_10V_Internal_Bits = 0x0,
+ MSeries_AO_DAC_Reference_5V_Internal_Bits = 0x8,
+ MSeries_AO_Update_Timed_Bit = 0x40,
+ MSeries_AO_Bipolar_Bit = 0x80 /* turns on 2's complement encoding */
+};
+
+enum MSeries_AO_Reference_Attenuation_Bits {
+ MSeries_Attenuate_x5_Bit = 0x1
+};
+
+static inline unsigned MSeries_Cal_PWM_High_Time_Bits(unsigned count)
+{
+ return (count << 16) & 0xffff0000;
+}
+
+static inline unsigned MSeries_Cal_PWM_Low_Time_Bits(unsigned count)
+{
+ return count & 0xffff;
+}
+
+static inline unsigned MSeries_PFI_Output_Select_Mask(unsigned channel)
+{
+ return 0x1f << (channel % 3) * 5;
+};
+
+static inline unsigned MSeries_PFI_Output_Select_Bits(unsigned channel,
+ unsigned source)
+{
+ return (source & 0x1f) << ((channel % 3) * 5);
+};
+
+/* inverse to MSeries_PFI_Output_Select_Bits */
+static inline unsigned MSeries_PFI_Output_Select_Source(unsigned channel,
+ unsigned bits)
+{
+ return (bits >> ((channel % 3) * 5)) & 0x1f;
+};
+
+static inline unsigned MSeries_PFI_Filter_Select_Mask(unsigned channel)
+{
+ return 0x3 << (channel * 2);
+}
+
+static inline unsigned MSeries_PFI_Filter_Select_Bits(unsigned channel,
+ unsigned filter)
+{
+ return (filter << (channel *
+ 2)) & MSeries_PFI_Filter_Select_Mask(channel);
+}
+
+enum CDIO_DMA_Select_Bits {
+ CDI_DMA_Select_Shift = 0,
+ CDI_DMA_Select_Mask = 0xf,
+ CDO_DMA_Select_Shift = 4,
+ CDO_DMA_Select_Mask = 0xf << CDO_DMA_Select_Shift
+};
+
+enum CDIO_Status_Bits {
+ CDO_FIFO_Empty_Bit = 0x1,
+ CDO_FIFO_Full_Bit = 0x2,
+ CDO_FIFO_Request_Bit = 0x4,
+ CDO_Overrun_Bit = 0x8,
+ CDO_Underflow_Bit = 0x10,
+ CDI_FIFO_Empty_Bit = 0x10000,
+ CDI_FIFO_Full_Bit = 0x20000,
+ CDI_FIFO_Request_Bit = 0x40000,
+ CDI_Overrun_Bit = 0x80000,
+ CDI_Overflow_Bit = 0x100000
+};
+
+enum CDIO_Command_Bits {
+ CDO_Disarm_Bit = 0x1,
+ CDO_Arm_Bit = 0x2,
+ CDI_Disarm_Bit = 0x4,
+ CDI_Arm_Bit = 0x8,
+ CDO_Reset_Bit = 0x10,
+ CDI_Reset_Bit = 0x20,
+ CDO_Error_Interrupt_Enable_Set_Bit = 0x40,
+ CDO_Error_Interrupt_Enable_Clear_Bit = 0x80,
+ CDI_Error_Interrupt_Enable_Set_Bit = 0x100,
+ CDI_Error_Interrupt_Enable_Clear_Bit = 0x200,
+ CDO_FIFO_Request_Interrupt_Enable_Set_Bit = 0x400,
+ CDO_FIFO_Request_Interrupt_Enable_Clear_Bit = 0x800,
+ CDI_FIFO_Request_Interrupt_Enable_Set_Bit = 0x1000,
+ CDI_FIFO_Request_Interrupt_Enable_Clear_Bit = 0x2000,
+ CDO_Error_Interrupt_Confirm_Bit = 0x4000,
+ CDI_Error_Interrupt_Confirm_Bit = 0x8000,
+ CDO_Empty_FIFO_Interrupt_Enable_Set_Bit = 0x10000,
+ CDO_Empty_FIFO_Interrupt_Enable_Clear_Bit = 0x20000,
+ CDO_SW_Update_Bit = 0x80000,
+ CDI_SW_Update_Bit = 0x100000
+};
+
+enum CDI_Mode_Bits {
+ CDI_Sample_Source_Select_Mask = 0x3f,
+ CDI_Halt_On_Error_Bit = 0x200,
+ CDI_Polarity_Bit = 0x400, /* sample clock on falling edge */
+ CDI_FIFO_Mode_Bit = 0x800, /* set for half full mode, clear for not empty mode */
+ CDI_Data_Lane_Mask = 0x3000, /* data lanes specify which dio channels map to byte or word accesses to the dio fifos */
+ CDI_Data_Lane_0_15_Bits = 0x0,
+ CDI_Data_Lane_16_31_Bits = 0x1000,
+ CDI_Data_Lane_0_7_Bits = 0x0,
+ CDI_Data_Lane_8_15_Bits = 0x1000,
+ CDI_Data_Lane_16_23_Bits = 0x2000,
+ CDI_Data_Lane_24_31_Bits = 0x3000
+};
+
+enum CDO_Mode_Bits {
+ CDO_Sample_Source_Select_Mask = 0x3f,
+ CDO_Retransmit_Bit = 0x100,
+ CDO_Halt_On_Error_Bit = 0x200,
+ CDO_Polarity_Bit = 0x400, /* sample clock on falling edge */
+ CDO_FIFO_Mode_Bit = 0x800, /* set for half full mode, clear for not full mode */
+ CDO_Data_Lane_Mask = 0x3000, /* data lanes specify which dio channels map to byte or word accesses to the dio fifos */
+ CDO_Data_Lane_0_15_Bits = 0x0,
+ CDO_Data_Lane_16_31_Bits = 0x1000,
+ CDO_Data_Lane_0_7_Bits = 0x0,
+ CDO_Data_Lane_8_15_Bits = 0x1000,
+ CDO_Data_Lane_16_23_Bits = 0x2000,
+ CDO_Data_Lane_24_31_Bits = 0x3000
+};
+
+enum Interrupt_C_Enable_Bits {
+ Interrupt_Group_C_Enable_Bit = 0x1
+};
+
+enum Interrupt_C_Status_Bits {
+ Interrupt_Group_C_Status_Bit = 0x1
+};
+
+#define M_SERIES_EEPROM_SIZE 1024
+
+struct ni_board_struct {
+ const char *name;
+ int device_id;
+ int isapnp_id;
+
+ int n_adchan;
+ unsigned int ai_maxdata;
+
+ int ai_fifo_depth;
+ unsigned int alwaysdither:1;
+ int gainlkup;
+ int ai_speed;
+
+ int n_aochan;
+ unsigned int ao_maxdata;
+ int ao_fifo_depth;
+ const struct comedi_lrange *ao_range_table;
+ unsigned ao_speed;
+
+ int reg_type;
+ unsigned int has_8255:1;
+ unsigned int has_32dio_chan:1;
+
+ enum caldac_enum caldac[3];
+};
+
+#define MAX_N_CALDACS 34
+#define MAX_N_AO_CHAN 8
+#define NUM_GPCT 2
+
+struct ni_private {
+ unsigned short dio_output;
+ unsigned short dio_control;
+ int aimode;
+ unsigned int ai_calib_source;
+ unsigned int ai_calib_source_enabled;
+ spinlock_t window_lock;
+ spinlock_t soft_reg_copy_lock;
+ spinlock_t mite_channel_lock;
+
+ int changain_state;
+ unsigned int changain_spec;
+
+ unsigned int caldac_maxdata_list[MAX_N_CALDACS];
+ unsigned short caldacs[MAX_N_CALDACS];
+
+ unsigned short ai_cmd2;
+
+ unsigned short ao_conf[MAX_N_AO_CHAN];
+ unsigned short ao_mode1;
+ unsigned short ao_mode2;
+ unsigned short ao_mode3;
+ unsigned short ao_cmd1;
+ unsigned short ao_cmd2;
+ unsigned short ao_trigger_select;
+
+ struct ni_gpct_device *counter_dev;
+ unsigned short an_trig_etc_reg;
+
+ unsigned ai_offset[512];
+
+ unsigned long serial_interval_ns;
+ unsigned char serial_hw_mode;
+ unsigned short clock_and_fout;
+ unsigned short clock_and_fout2;
+
+ unsigned short int_a_enable_reg;
+ unsigned short int_b_enable_reg;
+ unsigned short io_bidirection_pin_reg;
+ unsigned short rtsi_trig_direction_reg;
+ unsigned short rtsi_trig_a_output_reg;
+ unsigned short rtsi_trig_b_output_reg;
+ unsigned short pfi_output_select_reg[NUM_PFI_OUTPUT_SELECT_REGS];
+ unsigned short ai_ao_select_reg;
+ unsigned short g0_g1_select_reg;
+ unsigned short cdio_dma_select_reg;
+
+ unsigned clock_ns;
+ unsigned clock_source;
+
+ unsigned short pwm_up_count;
+ unsigned short pwm_down_count;
+
+ unsigned short ai_fifo_buffer[0x2000];
+ uint8_t eeprom_buffer[M_SERIES_EEPROM_SIZE];
+ __be32 serial_number;
+
+ struct mite_struct *mite;
+ struct mite_channel *ai_mite_chan;
+ struct mite_channel *ao_mite_chan;
+ struct mite_channel *cdo_mite_chan;
+ struct mite_dma_descriptor_ring *ai_mite_ring;
+ struct mite_dma_descriptor_ring *ao_mite_ring;
+ struct mite_dma_descriptor_ring *cdo_mite_ring;
+ struct mite_dma_descriptor_ring *gpct_mite_ring[NUM_GPCT];
+
+ /* ni_pcimio board type flags (based on the boardinfo reg_type) */
+ unsigned int is_m_series:1;
+ unsigned int is_6xxx:1;
+ unsigned int is_611x:1;
+ unsigned int is_6143:1;
+ unsigned int is_622x:1;
+ unsigned int is_625x:1;
+ unsigned int is_628x:1;
+ unsigned int is_67xx:1;
+ unsigned int is_6711:1;
+ unsigned int is_6713:1;
+};
+
+#endif /* _COMEDI_NI_STC_H */
diff --git a/drivers/staging/comedi/drivers/ni_tio.c b/drivers/staging/comedi/drivers/ni_tio.c
new file mode 100644
index 000000000..c20c51bef
--- /dev/null
+++ b/drivers/staging/comedi/drivers/ni_tio.c
@@ -0,0 +1,1436 @@
+/*
+ comedi/drivers/ni_tio.c
+ Support for NI general purpose counters
+
+ Copyright (C) 2006 Frank Mori Hess <fmhess@users.sourceforge.net>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+*/
+
+/*
+ * Module: ni_tio
+ * Description: National Instruments general purpose counters
+ * Author: J.P. Mellor <jpmellor@rose-hulman.edu>,
+ * Herman.Bruyninckx@mech.kuleuven.ac.be,
+ * Wim.Meeussen@mech.kuleuven.ac.be,
+ * Klaas.Gadeyne@mech.kuleuven.ac.be,
+ * Frank Mori Hess <fmhess@users.sourceforge.net>
+ * Updated: Thu Nov 16 09:50:32 EST 2006
+ * Status: works
+ *
+ * This module is not used directly by end-users. Rather, it
+ * is used by other drivers (for example ni_660x and ni_pcimio)
+ * to provide support for NI's general purpose counters. It was
+ * originally based on the counter code from ni_660x.c and
+ * ni_mio_common.c.
+ *
+ * References:
+ * DAQ 660x Register-Level Programmer Manual (NI 370505A-01)
+ * DAQ 6601/6602 User Manual (NI 322137B-01)
+ * 340934b.pdf DAQ-STC reference manual
+ */
+
+/*
+TODO:
+ Support use of both banks X and Y
+*/
+
+#include <linux/module.h>
+#include <linux/slab.h>
+
+#include "ni_tio_internal.h"
+
+/*
+ * clock sources for ni e and m series boards,
+ * get bits with GI_SRC_SEL()
+ */
+#define NI_M_TIMEBASE_1_CLK 0x0 /* 20MHz */
+#define NI_M_PFI_CLK(x) (((x) < 10) ? (1 + (x)) : (0xb + (x)))
+#define NI_M_RTSI_CLK(x) (((x) == 7) ? 0x1b : (0xb + (x)))
+#define NI_M_TIMEBASE_2_CLK 0x12 /* 100KHz */
+#define NI_M_NEXT_TC_CLK 0x13
+#define NI_M_NEXT_GATE_CLK 0x14 /* Gi_Src_SubSelect=0 */
+#define NI_M_PXI_STAR_TRIGGER_CLK 0x14 /* Gi_Src_SubSelect=1 */
+#define NI_M_PXI10_CLK 0x1d
+#define NI_M_TIMEBASE_3_CLK 0x1e /* 80MHz, Gi_Src_SubSelect=0 */
+#define NI_M_ANALOG_TRIGGER_OUT_CLK 0x1e /* Gi_Src_SubSelect=1 */
+#define NI_M_LOGIC_LOW_CLK 0x1f
+#define NI_M_MAX_PFI_CHAN 15
+#define NI_M_MAX_RTSI_CHAN 7
+
+/*
+ * clock sources for ni_660x boards,
+ * get bits with GI_SRC_SEL()
+ */
+#define NI_660X_TIMEBASE_1_CLK 0x0 /* 20MHz */
+#define NI_660X_SRC_PIN_I_CLK 0x1
+#define NI_660X_SRC_PIN_CLK(x) (0x2 + (x))
+#define NI_660X_NEXT_GATE_CLK 0xa
+#define NI_660X_RTSI_CLK(x) (0xb + (x))
+#define NI_660X_TIMEBASE_2_CLK 0x12 /* 100KHz */
+#define NI_660X_NEXT_TC_CLK 0x13
+#define NI_660X_TIMEBASE_3_CLK 0x1e /* 80MHz */
+#define NI_660X_LOGIC_LOW_CLK 0x1f
+#define NI_660X_MAX_SRC_PIN 7
+#define NI_660X_MAX_RTSI_CHAN 6
+
+/* ni m series gate_select */
+#define NI_M_TIMESTAMP_MUX_GATE_SEL 0x0
+#define NI_M_PFI_GATE_SEL(x) (((x) < 10) ? (1 + (x)) : (0xb + (x)))
+#define NI_M_RTSI_GATE_SEL(x) (((x) == 7) ? 0x1b : (0xb + (x)))
+#define NI_M_AI_START2_GATE_SEL 0x12
+#define NI_M_PXI_STAR_TRIGGER_GATE_SEL 0x13
+#define NI_M_NEXT_OUT_GATE_SEL 0x14
+#define NI_M_AI_START1_GATE_SEL 0x1c
+#define NI_M_NEXT_SRC_GATE_SEL 0x1d
+#define NI_M_ANALOG_TRIG_OUT_GATE_SEL 0x1e
+#define NI_M_LOGIC_LOW_GATE_SEL 0x1f
+
+/* ni_660x gate select */
+#define NI_660X_SRC_PIN_I_GATE_SEL 0x0
+#define NI_660X_GATE_PIN_I_GATE_SEL 0x1
+#define NI_660X_PIN_GATE_SEL(x) (0x2 + (x))
+#define NI_660X_NEXT_SRC_GATE_SEL 0xa
+#define NI_660X_RTSI_GATE_SEL(x) (0xb + (x))
+#define NI_660X_NEXT_OUT_GATE_SEL 0x14
+#define NI_660X_LOGIC_LOW_GATE_SEL 0x1f
+#define NI_660X_MAX_GATE_PIN 7
+
+/* ni_660x second gate select */
+#define NI_660X_SRC_PIN_I_GATE2_SEL 0x0
+#define NI_660X_UD_PIN_I_GATE2_SEL 0x1
+#define NI_660X_UD_PIN_GATE2_SEL(x) (0x2 + (x))
+#define NI_660X_NEXT_SRC_GATE2_SEL 0xa
+#define NI_660X_RTSI_GATE2_SEL(x) (0xb + (x))
+#define NI_660X_NEXT_OUT_GATE2_SEL 0x14
+#define NI_660X_SELECTED_GATE2_SEL 0x1e
+#define NI_660X_LOGIC_LOW_GATE2_SEL 0x1f
+#define NI_660X_MAX_UP_DOWN_PIN 7
+
+static inline unsigned GI_ALT_SYNC(enum ni_gpct_variant variant)
+{
+ switch (variant) {
+ case ni_gpct_variant_e_series:
+ default:
+ return 0;
+ case ni_gpct_variant_m_series:
+ return GI_M_ALT_SYNC;
+ case ni_gpct_variant_660x:
+ return GI_660X_ALT_SYNC;
+ }
+}
+
+static inline unsigned GI_PRESCALE_X2(enum ni_gpct_variant variant)
+{
+ switch (variant) {
+ case ni_gpct_variant_e_series:
+ default:
+ return 0;
+ case ni_gpct_variant_m_series:
+ return GI_M_PRESCALE_X2;
+ case ni_gpct_variant_660x:
+ return GI_660X_PRESCALE_X2;
+ }
+}
+
+static inline unsigned GI_PRESCALE_X8(enum ni_gpct_variant variant)
+{
+ switch (variant) {
+ case ni_gpct_variant_e_series:
+ default:
+ return 0;
+ case ni_gpct_variant_m_series:
+ return GI_M_PRESCALE_X8;
+ case ni_gpct_variant_660x:
+ return GI_660X_PRESCALE_X8;
+ }
+}
+
+static inline unsigned GI_HW_ARM_SEL_MASK(enum ni_gpct_variant variant)
+{
+ switch (variant) {
+ case ni_gpct_variant_e_series:
+ default:
+ return 0;
+ case ni_gpct_variant_m_series:
+ return GI_M_HW_ARM_SEL_MASK;
+ case ni_gpct_variant_660x:
+ return GI_660X_HW_ARM_SEL_MASK;
+ }
+}
+
+static int ni_tio_has_gate2_registers(const struct ni_gpct_device *counter_dev)
+{
+ switch (counter_dev->variant) {
+ case ni_gpct_variant_e_series:
+ default:
+ return 0;
+ case ni_gpct_variant_m_series:
+ case ni_gpct_variant_660x:
+ return 1;
+ }
+}
+
+static void ni_tio_reset_count_and_disarm(struct ni_gpct *counter)
+{
+ unsigned cidx = counter->counter_index;
+
+ write_register(counter, GI_RESET(cidx), NITIO_RESET_REG(cidx));
+}
+
+static uint64_t ni_tio_clock_period_ps(const struct ni_gpct *counter,
+ unsigned generic_clock_source)
+{
+ uint64_t clock_period_ps;
+
+ switch (generic_clock_source & NI_GPCT_CLOCK_SRC_SELECT_MASK) {
+ case NI_GPCT_TIMEBASE_1_CLOCK_SRC_BITS:
+ clock_period_ps = 50000;
+ break;
+ case NI_GPCT_TIMEBASE_2_CLOCK_SRC_BITS:
+ clock_period_ps = 10000000;
+ break;
+ case NI_GPCT_TIMEBASE_3_CLOCK_SRC_BITS:
+ clock_period_ps = 12500;
+ break;
+ case NI_GPCT_PXI10_CLOCK_SRC_BITS:
+ clock_period_ps = 100000;
+ break;
+ default:
+ /*
+ * clock period is specified by user with prescaling
+ * already taken into account.
+ */
+ return counter->clock_period_ps;
+ }
+
+ switch (generic_clock_source & NI_GPCT_PRESCALE_MODE_CLOCK_SRC_MASK) {
+ case NI_GPCT_NO_PRESCALE_CLOCK_SRC_BITS:
+ break;
+ case NI_GPCT_PRESCALE_X2_CLOCK_SRC_BITS:
+ clock_period_ps *= 2;
+ break;
+ case NI_GPCT_PRESCALE_X8_CLOCK_SRC_BITS:
+ clock_period_ps *= 8;
+ break;
+ default:
+ BUG();
+ break;
+ }
+ return clock_period_ps;
+}
+
+static unsigned ni_tio_clock_src_modifiers(const struct ni_gpct *counter)
+{
+ struct ni_gpct_device *counter_dev = counter->counter_dev;
+ unsigned cidx = counter->counter_index;
+ const unsigned counting_mode_bits =
+ ni_tio_get_soft_copy(counter, NITIO_CNT_MODE_REG(cidx));
+ unsigned bits = 0;
+
+ if (ni_tio_get_soft_copy(counter, NITIO_INPUT_SEL_REG(cidx)) &
+ GI_SRC_POL_INVERT)
+ bits |= NI_GPCT_INVERT_CLOCK_SRC_BIT;
+ if (counting_mode_bits & GI_PRESCALE_X2(counter_dev->variant))
+ bits |= NI_GPCT_PRESCALE_X2_CLOCK_SRC_BITS;
+ if (counting_mode_bits & GI_PRESCALE_X8(counter_dev->variant))
+ bits |= NI_GPCT_PRESCALE_X8_CLOCK_SRC_BITS;
+ return bits;
+}
+
+static unsigned ni_m_series_clock_src_select(const struct ni_gpct *counter)
+{
+ struct ni_gpct_device *counter_dev = counter->counter_dev;
+ unsigned cidx = counter->counter_index;
+ const unsigned second_gate_reg = NITIO_GATE2_REG(cidx);
+ unsigned clock_source = 0;
+ unsigned src;
+ unsigned i;
+
+ src = GI_BITS_TO_SRC(ni_tio_get_soft_copy(counter,
+ NITIO_INPUT_SEL_REG(cidx)));
+
+ switch (src) {
+ case NI_M_TIMEBASE_1_CLK:
+ clock_source = NI_GPCT_TIMEBASE_1_CLOCK_SRC_BITS;
+ break;
+ case NI_M_TIMEBASE_2_CLK:
+ clock_source = NI_GPCT_TIMEBASE_2_CLOCK_SRC_BITS;
+ break;
+ case NI_M_TIMEBASE_3_CLK:
+ if (counter_dev->regs[second_gate_reg] & GI_SRC_SUBSEL)
+ clock_source =
+ NI_GPCT_ANALOG_TRIGGER_OUT_CLOCK_SRC_BITS;
+ else
+ clock_source = NI_GPCT_TIMEBASE_3_CLOCK_SRC_BITS;
+ break;
+ case NI_M_LOGIC_LOW_CLK:
+ clock_source = NI_GPCT_LOGIC_LOW_CLOCK_SRC_BITS;
+ break;
+ case NI_M_NEXT_GATE_CLK:
+ if (counter_dev->regs[second_gate_reg] & GI_SRC_SUBSEL)
+ clock_source = NI_GPCT_PXI_STAR_TRIGGER_CLOCK_SRC_BITS;
+ else
+ clock_source = NI_GPCT_NEXT_GATE_CLOCK_SRC_BITS;
+ break;
+ case NI_M_PXI10_CLK:
+ clock_source = NI_GPCT_PXI10_CLOCK_SRC_BITS;
+ break;
+ case NI_M_NEXT_TC_CLK:
+ clock_source = NI_GPCT_NEXT_TC_CLOCK_SRC_BITS;
+ break;
+ default:
+ for (i = 0; i <= NI_M_MAX_RTSI_CHAN; ++i) {
+ if (src == NI_M_RTSI_CLK(i)) {
+ clock_source = NI_GPCT_RTSI_CLOCK_SRC_BITS(i);
+ break;
+ }
+ }
+ if (i <= NI_M_MAX_RTSI_CHAN)
+ break;
+ for (i = 0; i <= NI_M_MAX_PFI_CHAN; ++i) {
+ if (src == NI_M_PFI_CLK(i)) {
+ clock_source = NI_GPCT_PFI_CLOCK_SRC_BITS(i);
+ break;
+ }
+ }
+ if (i <= NI_M_MAX_PFI_CHAN)
+ break;
+ BUG();
+ break;
+ }
+ clock_source |= ni_tio_clock_src_modifiers(counter);
+ return clock_source;
+}
+
+static unsigned ni_660x_clock_src_select(const struct ni_gpct *counter)
+{
+ unsigned clock_source = 0;
+ unsigned cidx = counter->counter_index;
+ unsigned src;
+ unsigned i;
+
+ src = GI_BITS_TO_SRC(ni_tio_get_soft_copy(counter,
+ NITIO_INPUT_SEL_REG(cidx)));
+
+ switch (src) {
+ case NI_660X_TIMEBASE_1_CLK:
+ clock_source = NI_GPCT_TIMEBASE_1_CLOCK_SRC_BITS;
+ break;
+ case NI_660X_TIMEBASE_2_CLK:
+ clock_source = NI_GPCT_TIMEBASE_2_CLOCK_SRC_BITS;
+ break;
+ case NI_660X_TIMEBASE_3_CLK:
+ clock_source = NI_GPCT_TIMEBASE_3_CLOCK_SRC_BITS;
+ break;
+ case NI_660X_LOGIC_LOW_CLK:
+ clock_source = NI_GPCT_LOGIC_LOW_CLOCK_SRC_BITS;
+ break;
+ case NI_660X_SRC_PIN_I_CLK:
+ clock_source = NI_GPCT_SOURCE_PIN_i_CLOCK_SRC_BITS;
+ break;
+ case NI_660X_NEXT_GATE_CLK:
+ clock_source = NI_GPCT_NEXT_GATE_CLOCK_SRC_BITS;
+ break;
+ case NI_660X_NEXT_TC_CLK:
+ clock_source = NI_GPCT_NEXT_TC_CLOCK_SRC_BITS;
+ break;
+ default:
+ for (i = 0; i <= NI_660X_MAX_RTSI_CHAN; ++i) {
+ if (src == NI_660X_RTSI_CLK(i)) {
+ clock_source = NI_GPCT_RTSI_CLOCK_SRC_BITS(i);
+ break;
+ }
+ }
+ if (i <= NI_660X_MAX_RTSI_CHAN)
+ break;
+ for (i = 0; i <= NI_660X_MAX_SRC_PIN; ++i) {
+ if (src == NI_660X_SRC_PIN_CLK(i)) {
+ clock_source =
+ NI_GPCT_SOURCE_PIN_CLOCK_SRC_BITS(i);
+ break;
+ }
+ }
+ if (i <= NI_660X_MAX_SRC_PIN)
+ break;
+ BUG();
+ break;
+ }
+ clock_source |= ni_tio_clock_src_modifiers(counter);
+ return clock_source;
+}
+
+static unsigned ni_tio_generic_clock_src_select(const struct ni_gpct *counter)
+{
+ switch (counter->counter_dev->variant) {
+ case ni_gpct_variant_e_series:
+ case ni_gpct_variant_m_series:
+ default:
+ return ni_m_series_clock_src_select(counter);
+ case ni_gpct_variant_660x:
+ return ni_660x_clock_src_select(counter);
+ }
+}
+
+static void ni_tio_set_sync_mode(struct ni_gpct *counter, int force_alt_sync)
+{
+ struct ni_gpct_device *counter_dev = counter->counter_dev;
+ unsigned cidx = counter->counter_index;
+ const unsigned counting_mode_reg = NITIO_CNT_MODE_REG(cidx);
+ static const uint64_t min_normal_sync_period_ps = 25000;
+ unsigned mode;
+ uint64_t clock_period_ps;
+
+ if (ni_tio_counting_mode_registers_present(counter_dev) == 0)
+ return;
+
+ mode = ni_tio_get_soft_copy(counter, counting_mode_reg);
+ switch (mode & GI_CNT_MODE_MASK) {
+ case GI_CNT_MODE_QUADX1:
+ case GI_CNT_MODE_QUADX2:
+ case GI_CNT_MODE_QUADX4:
+ case GI_CNT_MODE_SYNC_SRC:
+ force_alt_sync = 1;
+ break;
+ default:
+ break;
+ }
+
+ clock_period_ps = ni_tio_clock_period_ps(counter,
+ ni_tio_generic_clock_src_select(counter));
+
+ /*
+ * It's not clear what we should do if clock_period is unknown, so we
+ * are not using the alt sync bit in that case, but allow the caller
+ * to decide by using the force_alt_sync parameter.
+ */
+ if (force_alt_sync ||
+ (clock_period_ps && clock_period_ps < min_normal_sync_period_ps)) {
+ ni_tio_set_bits(counter, counting_mode_reg,
+ GI_ALT_SYNC(counter_dev->variant),
+ GI_ALT_SYNC(counter_dev->variant));
+ } else {
+ ni_tio_set_bits(counter, counting_mode_reg,
+ GI_ALT_SYNC(counter_dev->variant),
+ 0x0);
+ }
+}
+
+static int ni_tio_set_counter_mode(struct ni_gpct *counter, unsigned mode)
+{
+ struct ni_gpct_device *counter_dev = counter->counter_dev;
+ unsigned cidx = counter->counter_index;
+ unsigned mode_reg_mask;
+ unsigned mode_reg_values;
+ unsigned input_select_bits = 0;
+ /* these bits map directly on to the mode register */
+ static const unsigned mode_reg_direct_mask =
+ NI_GPCT_GATE_ON_BOTH_EDGES_BIT | NI_GPCT_EDGE_GATE_MODE_MASK |
+ NI_GPCT_STOP_MODE_MASK | NI_GPCT_OUTPUT_MODE_MASK |
+ NI_GPCT_HARDWARE_DISARM_MASK | NI_GPCT_LOADING_ON_TC_BIT |
+ NI_GPCT_LOADING_ON_GATE_BIT | NI_GPCT_LOAD_B_SELECT_BIT;
+
+ mode_reg_mask = mode_reg_direct_mask | GI_RELOAD_SRC_SWITCHING;
+ mode_reg_values = mode & mode_reg_direct_mask;
+ switch (mode & NI_GPCT_RELOAD_SOURCE_MASK) {
+ case NI_GPCT_RELOAD_SOURCE_FIXED_BITS:
+ break;
+ case NI_GPCT_RELOAD_SOURCE_SWITCHING_BITS:
+ mode_reg_values |= GI_RELOAD_SRC_SWITCHING;
+ break;
+ case NI_GPCT_RELOAD_SOURCE_GATE_SELECT_BITS:
+ input_select_bits |= GI_GATE_SEL_LOAD_SRC;
+ mode_reg_mask |= GI_GATING_MODE_MASK;
+ mode_reg_values |= GI_LEVEL_GATING;
+ break;
+ default:
+ break;
+ }
+ ni_tio_set_bits(counter, NITIO_MODE_REG(cidx),
+ mode_reg_mask, mode_reg_values);
+
+ if (ni_tio_counting_mode_registers_present(counter_dev)) {
+ unsigned bits = 0;
+
+ bits |= GI_CNT_MODE(mode >> NI_GPCT_COUNTING_MODE_SHIFT);
+ bits |= GI_INDEX_PHASE((mode >> NI_GPCT_INDEX_PHASE_BITSHIFT));
+ if (mode & NI_GPCT_INDEX_ENABLE_BIT)
+ bits |= GI_INDEX_MODE;
+ ni_tio_set_bits(counter, NITIO_CNT_MODE_REG(cidx),
+ GI_CNT_MODE_MASK | GI_INDEX_PHASE_MASK |
+ GI_INDEX_MODE, bits);
+ ni_tio_set_sync_mode(counter, 0);
+ }
+
+ ni_tio_set_bits(counter, NITIO_CMD_REG(cidx), GI_CNT_DIR_MASK,
+ GI_CNT_DIR(mode >> NI_GPCT_COUNTING_DIRECTION_SHIFT));
+
+ if (mode & NI_GPCT_OR_GATE_BIT)
+ input_select_bits |= GI_OR_GATE;
+ if (mode & NI_GPCT_INVERT_OUTPUT_BIT)
+ input_select_bits |= GI_OUTPUT_POL_INVERT;
+ ni_tio_set_bits(counter, NITIO_INPUT_SEL_REG(cidx),
+ GI_GATE_SEL_LOAD_SRC | GI_OR_GATE |
+ GI_OUTPUT_POL_INVERT, input_select_bits);
+
+ return 0;
+}
+
+int ni_tio_arm(struct ni_gpct *counter, int arm, unsigned start_trigger)
+{
+ struct ni_gpct_device *counter_dev = counter->counter_dev;
+ unsigned cidx = counter->counter_index;
+ unsigned command_transient_bits = 0;
+
+ if (arm) {
+ switch (start_trigger) {
+ case NI_GPCT_ARM_IMMEDIATE:
+ command_transient_bits |= GI_ARM;
+ break;
+ case NI_GPCT_ARM_PAIRED_IMMEDIATE:
+ command_transient_bits |= GI_ARM | GI_ARM_COPY;
+ break;
+ default:
+ break;
+ }
+ if (ni_tio_counting_mode_registers_present(counter_dev)) {
+ unsigned bits = 0;
+ unsigned sel_mask;
+
+ sel_mask = GI_HW_ARM_SEL_MASK(counter_dev->variant);
+
+ switch (start_trigger) {
+ case NI_GPCT_ARM_IMMEDIATE:
+ case NI_GPCT_ARM_PAIRED_IMMEDIATE:
+ break;
+ default:
+ if (start_trigger & NI_GPCT_ARM_UNKNOWN) {
+ /*
+ * pass-through the least significant
+ * bits so we can figure out what
+ * select later
+ */
+ bits |= GI_HW_ARM_ENA |
+ (GI_HW_ARM_SEL(start_trigger) &
+ sel_mask);
+ } else {
+ return -EINVAL;
+ }
+ break;
+ }
+ ni_tio_set_bits(counter, NITIO_CNT_MODE_REG(cidx),
+ GI_HW_ARM_ENA | sel_mask, bits);
+ }
+ } else {
+ command_transient_bits |= GI_DISARM;
+ }
+ ni_tio_set_bits_transient(counter, NITIO_CMD_REG(cidx),
+ 0, 0, command_transient_bits);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(ni_tio_arm);
+
+static unsigned ni_660x_clk_src(unsigned int clock_source)
+{
+ unsigned clk_src = clock_source & NI_GPCT_CLOCK_SRC_SELECT_MASK;
+ unsigned ni_660x_clock;
+ unsigned i;
+
+ switch (clk_src) {
+ case NI_GPCT_TIMEBASE_1_CLOCK_SRC_BITS:
+ ni_660x_clock = NI_660X_TIMEBASE_1_CLK;
+ break;
+ case NI_GPCT_TIMEBASE_2_CLOCK_SRC_BITS:
+ ni_660x_clock = NI_660X_TIMEBASE_2_CLK;
+ break;
+ case NI_GPCT_TIMEBASE_3_CLOCK_SRC_BITS:
+ ni_660x_clock = NI_660X_TIMEBASE_3_CLK;
+ break;
+ case NI_GPCT_LOGIC_LOW_CLOCK_SRC_BITS:
+ ni_660x_clock = NI_660X_LOGIC_LOW_CLK;
+ break;
+ case NI_GPCT_SOURCE_PIN_i_CLOCK_SRC_BITS:
+ ni_660x_clock = NI_660X_SRC_PIN_I_CLK;
+ break;
+ case NI_GPCT_NEXT_GATE_CLOCK_SRC_BITS:
+ ni_660x_clock = NI_660X_NEXT_GATE_CLK;
+ break;
+ case NI_GPCT_NEXT_TC_CLOCK_SRC_BITS:
+ ni_660x_clock = NI_660X_NEXT_TC_CLK;
+ break;
+ default:
+ for (i = 0; i <= NI_660X_MAX_RTSI_CHAN; ++i) {
+ if (clk_src == NI_GPCT_RTSI_CLOCK_SRC_BITS(i)) {
+ ni_660x_clock = NI_660X_RTSI_CLK(i);
+ break;
+ }
+ }
+ if (i <= NI_660X_MAX_RTSI_CHAN)
+ break;
+ for (i = 0; i <= NI_660X_MAX_SRC_PIN; ++i) {
+ if (clk_src == NI_GPCT_SOURCE_PIN_CLOCK_SRC_BITS(i)) {
+ ni_660x_clock = NI_660X_SRC_PIN_CLK(i);
+ break;
+ }
+ }
+ if (i <= NI_660X_MAX_SRC_PIN)
+ break;
+ ni_660x_clock = 0;
+ BUG();
+ break;
+ }
+ return GI_SRC_SEL(ni_660x_clock);
+}
+
+static unsigned ni_m_clk_src(unsigned int clock_source)
+{
+ unsigned clk_src = clock_source & NI_GPCT_CLOCK_SRC_SELECT_MASK;
+ unsigned ni_m_series_clock;
+ unsigned i;
+
+ switch (clk_src) {
+ case NI_GPCT_TIMEBASE_1_CLOCK_SRC_BITS:
+ ni_m_series_clock = NI_M_TIMEBASE_1_CLK;
+ break;
+ case NI_GPCT_TIMEBASE_2_CLOCK_SRC_BITS:
+ ni_m_series_clock = NI_M_TIMEBASE_2_CLK;
+ break;
+ case NI_GPCT_TIMEBASE_3_CLOCK_SRC_BITS:
+ ni_m_series_clock = NI_M_TIMEBASE_3_CLK;
+ break;
+ case NI_GPCT_LOGIC_LOW_CLOCK_SRC_BITS:
+ ni_m_series_clock = NI_M_LOGIC_LOW_CLK;
+ break;
+ case NI_GPCT_NEXT_GATE_CLOCK_SRC_BITS:
+ ni_m_series_clock = NI_M_NEXT_GATE_CLK;
+ break;
+ case NI_GPCT_NEXT_TC_CLOCK_SRC_BITS:
+ ni_m_series_clock = NI_M_NEXT_TC_CLK;
+ break;
+ case NI_GPCT_PXI10_CLOCK_SRC_BITS:
+ ni_m_series_clock = NI_M_PXI10_CLK;
+ break;
+ case NI_GPCT_PXI_STAR_TRIGGER_CLOCK_SRC_BITS:
+ ni_m_series_clock = NI_M_PXI_STAR_TRIGGER_CLK;
+ break;
+ case NI_GPCT_ANALOG_TRIGGER_OUT_CLOCK_SRC_BITS:
+ ni_m_series_clock = NI_M_ANALOG_TRIGGER_OUT_CLK;
+ break;
+ default:
+ for (i = 0; i <= NI_M_MAX_RTSI_CHAN; ++i) {
+ if (clk_src == NI_GPCT_RTSI_CLOCK_SRC_BITS(i)) {
+ ni_m_series_clock = NI_M_RTSI_CLK(i);
+ break;
+ }
+ }
+ if (i <= NI_M_MAX_RTSI_CHAN)
+ break;
+ for (i = 0; i <= NI_M_MAX_PFI_CHAN; ++i) {
+ if (clk_src == NI_GPCT_PFI_CLOCK_SRC_BITS(i)) {
+ ni_m_series_clock = NI_M_PFI_CLK(i);
+ break;
+ }
+ }
+ if (i <= NI_M_MAX_PFI_CHAN)
+ break;
+ pr_err("invalid clock source 0x%lx\n",
+ (unsigned long)clock_source);
+ BUG();
+ ni_m_series_clock = 0;
+ break;
+ }
+ return GI_SRC_SEL(ni_m_series_clock);
+};
+
+static void ni_tio_set_source_subselect(struct ni_gpct *counter,
+ unsigned int clock_source)
+{
+ struct ni_gpct_device *counter_dev = counter->counter_dev;
+ unsigned cidx = counter->counter_index;
+ const unsigned second_gate_reg = NITIO_GATE2_REG(cidx);
+
+ if (counter_dev->variant != ni_gpct_variant_m_series)
+ return;
+ switch (clock_source & NI_GPCT_CLOCK_SRC_SELECT_MASK) {
+ /* Gi_Source_Subselect is zero */
+ case NI_GPCT_NEXT_GATE_CLOCK_SRC_BITS:
+ case NI_GPCT_TIMEBASE_3_CLOCK_SRC_BITS:
+ counter_dev->regs[second_gate_reg] &= ~GI_SRC_SUBSEL;
+ break;
+ /* Gi_Source_Subselect is one */
+ case NI_GPCT_ANALOG_TRIGGER_OUT_CLOCK_SRC_BITS:
+ case NI_GPCT_PXI_STAR_TRIGGER_CLOCK_SRC_BITS:
+ counter_dev->regs[second_gate_reg] |= GI_SRC_SUBSEL;
+ break;
+ /* Gi_Source_Subselect doesn't matter */
+ default:
+ return;
+ }
+ write_register(counter, counter_dev->regs[second_gate_reg],
+ second_gate_reg);
+}
+
+static int ni_tio_set_clock_src(struct ni_gpct *counter,
+ unsigned int clock_source,
+ unsigned int period_ns)
+{
+ struct ni_gpct_device *counter_dev = counter->counter_dev;
+ unsigned cidx = counter->counter_index;
+ unsigned bits = 0;
+
+ /* FIXME: validate clock source */
+ switch (counter_dev->variant) {
+ case ni_gpct_variant_660x:
+ bits |= ni_660x_clk_src(clock_source);
+ break;
+ case ni_gpct_variant_e_series:
+ case ni_gpct_variant_m_series:
+ default:
+ bits |= ni_m_clk_src(clock_source);
+ break;
+ }
+ if (clock_source & NI_GPCT_INVERT_CLOCK_SRC_BIT)
+ bits |= GI_SRC_POL_INVERT;
+ ni_tio_set_bits(counter, NITIO_INPUT_SEL_REG(cidx),
+ GI_SRC_SEL_MASK | GI_SRC_POL_INVERT, bits);
+ ni_tio_set_source_subselect(counter, clock_source);
+
+ if (ni_tio_counting_mode_registers_present(counter_dev)) {
+ bits = 0;
+ switch (clock_source & NI_GPCT_PRESCALE_MODE_CLOCK_SRC_MASK) {
+ case NI_GPCT_NO_PRESCALE_CLOCK_SRC_BITS:
+ break;
+ case NI_GPCT_PRESCALE_X2_CLOCK_SRC_BITS:
+ bits |= GI_PRESCALE_X2(counter_dev->variant);
+ break;
+ case NI_GPCT_PRESCALE_X8_CLOCK_SRC_BITS:
+ bits |= GI_PRESCALE_X8(counter_dev->variant);
+ break;
+ default:
+ return -EINVAL;
+ }
+ ni_tio_set_bits(counter, NITIO_CNT_MODE_REG(cidx),
+ GI_PRESCALE_X2(counter_dev->variant) |
+ GI_PRESCALE_X8(counter_dev->variant), bits);
+ }
+ counter->clock_period_ps = period_ns * 1000;
+ ni_tio_set_sync_mode(counter, 0);
+ return 0;
+}
+
+static void ni_tio_get_clock_src(struct ni_gpct *counter,
+ unsigned int *clock_source,
+ unsigned int *period_ns)
+{
+ uint64_t temp64;
+
+ *clock_source = ni_tio_generic_clock_src_select(counter);
+ temp64 = ni_tio_clock_period_ps(counter, *clock_source);
+ do_div(temp64, 1000); /* ps to ns */
+ *period_ns = temp64;
+}
+
+static int ni_660x_set_gate(struct ni_gpct *counter, unsigned int gate_source)
+{
+ unsigned int chan = CR_CHAN(gate_source);
+ unsigned cidx = counter->counter_index;
+ unsigned gate_sel;
+ unsigned i;
+
+ switch (chan) {
+ case NI_GPCT_NEXT_SOURCE_GATE_SELECT:
+ gate_sel = NI_660X_NEXT_SRC_GATE_SEL;
+ break;
+ case NI_GPCT_NEXT_OUT_GATE_SELECT:
+ case NI_GPCT_LOGIC_LOW_GATE_SELECT:
+ case NI_GPCT_SOURCE_PIN_i_GATE_SELECT:
+ case NI_GPCT_GATE_PIN_i_GATE_SELECT:
+ gate_sel = chan & 0x1f;
+ break;
+ default:
+ for (i = 0; i <= NI_660X_MAX_RTSI_CHAN; ++i) {
+ if (chan == NI_GPCT_RTSI_GATE_SELECT(i)) {
+ gate_sel = chan & 0x1f;
+ break;
+ }
+ }
+ if (i <= NI_660X_MAX_RTSI_CHAN)
+ break;
+ for (i = 0; i <= NI_660X_MAX_GATE_PIN; ++i) {
+ if (chan == NI_GPCT_GATE_PIN_GATE_SELECT(i)) {
+ gate_sel = chan & 0x1f;
+ break;
+ }
+ }
+ if (i <= NI_660X_MAX_GATE_PIN)
+ break;
+ return -EINVAL;
+ }
+ ni_tio_set_bits(counter, NITIO_INPUT_SEL_REG(cidx),
+ GI_GATE_SEL_MASK, GI_GATE_SEL(gate_sel));
+ return 0;
+}
+
+static int ni_m_set_gate(struct ni_gpct *counter, unsigned int gate_source)
+{
+ unsigned int chan = CR_CHAN(gate_source);
+ unsigned cidx = counter->counter_index;
+ unsigned gate_sel;
+ unsigned i;
+
+ switch (chan) {
+ case NI_GPCT_TIMESTAMP_MUX_GATE_SELECT:
+ case NI_GPCT_AI_START2_GATE_SELECT:
+ case NI_GPCT_PXI_STAR_TRIGGER_GATE_SELECT:
+ case NI_GPCT_NEXT_OUT_GATE_SELECT:
+ case NI_GPCT_AI_START1_GATE_SELECT:
+ case NI_GPCT_NEXT_SOURCE_GATE_SELECT:
+ case NI_GPCT_ANALOG_TRIGGER_OUT_GATE_SELECT:
+ case NI_GPCT_LOGIC_LOW_GATE_SELECT:
+ gate_sel = chan & 0x1f;
+ break;
+ default:
+ for (i = 0; i <= NI_M_MAX_RTSI_CHAN; ++i) {
+ if (chan == NI_GPCT_RTSI_GATE_SELECT(i)) {
+ gate_sel = chan & 0x1f;
+ break;
+ }
+ }
+ if (i <= NI_M_MAX_RTSI_CHAN)
+ break;
+ for (i = 0; i <= NI_M_MAX_PFI_CHAN; ++i) {
+ if (chan == NI_GPCT_PFI_GATE_SELECT(i)) {
+ gate_sel = chan & 0x1f;
+ break;
+ }
+ }
+ if (i <= NI_M_MAX_PFI_CHAN)
+ break;
+ return -EINVAL;
+ }
+ ni_tio_set_bits(counter, NITIO_INPUT_SEL_REG(cidx),
+ GI_GATE_SEL_MASK, GI_GATE_SEL(gate_sel));
+ return 0;
+}
+
+static int ni_660x_set_gate2(struct ni_gpct *counter, unsigned int gate_source)
+{
+ struct ni_gpct_device *counter_dev = counter->counter_dev;
+ unsigned cidx = counter->counter_index;
+ unsigned int chan = CR_CHAN(gate_source);
+ unsigned gate2_reg = NITIO_GATE2_REG(cidx);
+ unsigned gate2_sel;
+ unsigned i;
+
+ switch (chan) {
+ case NI_GPCT_SOURCE_PIN_i_GATE_SELECT:
+ case NI_GPCT_UP_DOWN_PIN_i_GATE_SELECT:
+ case NI_GPCT_SELECTED_GATE_GATE_SELECT:
+ case NI_GPCT_NEXT_OUT_GATE_SELECT:
+ case NI_GPCT_LOGIC_LOW_GATE_SELECT:
+ gate2_sel = chan & 0x1f;
+ break;
+ case NI_GPCT_NEXT_SOURCE_GATE_SELECT:
+ gate2_sel = NI_660X_NEXT_SRC_GATE2_SEL;
+ break;
+ default:
+ for (i = 0; i <= NI_660X_MAX_RTSI_CHAN; ++i) {
+ if (chan == NI_GPCT_RTSI_GATE_SELECT(i)) {
+ gate2_sel = chan & 0x1f;
+ break;
+ }
+ }
+ if (i <= NI_660X_MAX_RTSI_CHAN)
+ break;
+ for (i = 0; i <= NI_660X_MAX_UP_DOWN_PIN; ++i) {
+ if (chan == NI_GPCT_UP_DOWN_PIN_GATE_SELECT(i)) {
+ gate2_sel = chan & 0x1f;
+ break;
+ }
+ }
+ if (i <= NI_660X_MAX_UP_DOWN_PIN)
+ break;
+ return -EINVAL;
+ }
+ counter_dev->regs[gate2_reg] |= GI_GATE2_MODE;
+ counter_dev->regs[gate2_reg] &= ~GI_GATE2_SEL_MASK;
+ counter_dev->regs[gate2_reg] |= GI_GATE2_SEL(gate2_sel);
+ write_register(counter, counter_dev->regs[gate2_reg], gate2_reg);
+ return 0;
+}
+
+static int ni_m_set_gate2(struct ni_gpct *counter, unsigned int gate_source)
+{
+ struct ni_gpct_device *counter_dev = counter->counter_dev;
+ unsigned cidx = counter->counter_index;
+ unsigned int chan = CR_CHAN(gate_source);
+ unsigned gate2_reg = NITIO_GATE2_REG(cidx);
+ unsigned gate2_sel;
+
+ /*
+ * FIXME: We don't know what the m-series second gate codes are,
+ * so we'll just pass the bits through for now.
+ */
+ switch (chan) {
+ default:
+ gate2_sel = chan & 0x1f;
+ break;
+ }
+ counter_dev->regs[gate2_reg] |= GI_GATE2_MODE;
+ counter_dev->regs[gate2_reg] &= ~GI_GATE2_SEL_MASK;
+ counter_dev->regs[gate2_reg] |= GI_GATE2_SEL(gate2_sel);
+ write_register(counter, counter_dev->regs[gate2_reg], gate2_reg);
+ return 0;
+}
+
+int ni_tio_set_gate_src(struct ni_gpct *counter, unsigned gate_index,
+ unsigned int gate_source)
+{
+ struct ni_gpct_device *counter_dev = counter->counter_dev;
+ unsigned cidx = counter->counter_index;
+ unsigned int chan = CR_CHAN(gate_source);
+ unsigned gate2_reg = NITIO_GATE2_REG(cidx);
+ unsigned mode = 0;
+
+ switch (gate_index) {
+ case 0:
+ if (chan == NI_GPCT_DISABLED_GATE_SELECT) {
+ ni_tio_set_bits(counter, NITIO_MODE_REG(cidx),
+ GI_GATING_MODE_MASK,
+ GI_GATING_DISABLED);
+ return 0;
+ }
+ if (gate_source & CR_INVERT)
+ mode |= GI_GATE_POL_INVERT;
+ if (gate_source & CR_EDGE)
+ mode |= GI_RISING_EDGE_GATING;
+ else
+ mode |= GI_LEVEL_GATING;
+ ni_tio_set_bits(counter, NITIO_MODE_REG(cidx),
+ GI_GATE_POL_INVERT | GI_GATING_MODE_MASK,
+ mode);
+ switch (counter_dev->variant) {
+ case ni_gpct_variant_e_series:
+ case ni_gpct_variant_m_series:
+ default:
+ return ni_m_set_gate(counter, gate_source);
+ case ni_gpct_variant_660x:
+ return ni_660x_set_gate(counter, gate_source);
+ }
+ break;
+ case 1:
+ if (!ni_tio_has_gate2_registers(counter_dev))
+ return -EINVAL;
+
+ if (chan == NI_GPCT_DISABLED_GATE_SELECT) {
+ counter_dev->regs[gate2_reg] &= ~GI_GATE2_MODE;
+ write_register(counter, counter_dev->regs[gate2_reg],
+ gate2_reg);
+ return 0;
+ }
+ if (gate_source & CR_INVERT)
+ counter_dev->regs[gate2_reg] |= GI_GATE2_POL_INVERT;
+ else
+ counter_dev->regs[gate2_reg] &= ~GI_GATE2_POL_INVERT;
+ switch (counter_dev->variant) {
+ case ni_gpct_variant_m_series:
+ return ni_m_set_gate2(counter, gate_source);
+ case ni_gpct_variant_660x:
+ return ni_660x_set_gate2(counter, gate_source);
+ default:
+ BUG();
+ break;
+ }
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+EXPORT_SYMBOL_GPL(ni_tio_set_gate_src);
+
+static int ni_tio_set_other_src(struct ni_gpct *counter, unsigned index,
+ unsigned int source)
+{
+ struct ni_gpct_device *counter_dev = counter->counter_dev;
+ unsigned cidx = counter->counter_index;
+ unsigned int abz_reg, shift, mask;
+
+ if (counter_dev->variant != ni_gpct_variant_m_series)
+ return -EINVAL;
+
+ abz_reg = NITIO_ABZ_REG(cidx);
+ switch (index) {
+ case NI_GPCT_SOURCE_ENCODER_A:
+ shift = 10;
+ break;
+ case NI_GPCT_SOURCE_ENCODER_B:
+ shift = 5;
+ break;
+ case NI_GPCT_SOURCE_ENCODER_Z:
+ shift = 0;
+ break;
+ default:
+ return -EINVAL;
+ }
+ mask = 0x1f << shift;
+ if (source > 0x1f)
+ source = 0x1f; /* Disable gate */
+
+ counter_dev->regs[abz_reg] &= ~mask;
+ counter_dev->regs[abz_reg] |= (source << shift) & mask;
+ write_register(counter, counter_dev->regs[abz_reg], abz_reg);
+ return 0;
+}
+
+static unsigned ni_660x_gate_to_generic_gate(unsigned gate)
+{
+ unsigned i;
+
+ switch (gate) {
+ case NI_660X_SRC_PIN_I_GATE_SEL:
+ return NI_GPCT_SOURCE_PIN_i_GATE_SELECT;
+ case NI_660X_GATE_PIN_I_GATE_SEL:
+ return NI_GPCT_GATE_PIN_i_GATE_SELECT;
+ case NI_660X_NEXT_SRC_GATE_SEL:
+ return NI_GPCT_NEXT_SOURCE_GATE_SELECT;
+ case NI_660X_NEXT_OUT_GATE_SEL:
+ return NI_GPCT_NEXT_OUT_GATE_SELECT;
+ case NI_660X_LOGIC_LOW_GATE_SEL:
+ return NI_GPCT_LOGIC_LOW_GATE_SELECT;
+ default:
+ for (i = 0; i <= NI_660X_MAX_RTSI_CHAN; ++i) {
+ if (gate == NI_660X_RTSI_GATE_SEL(i))
+ return NI_GPCT_RTSI_GATE_SELECT(i);
+ }
+ for (i = 0; i <= NI_660X_MAX_GATE_PIN; ++i) {
+ if (gate == NI_660X_PIN_GATE_SEL(i))
+ return NI_GPCT_GATE_PIN_GATE_SELECT(i);
+ }
+ BUG();
+ break;
+ }
+ return 0;
+};
+
+static unsigned ni_m_gate_to_generic_gate(unsigned gate)
+{
+ unsigned i;
+
+ switch (gate) {
+ case NI_M_TIMESTAMP_MUX_GATE_SEL:
+ return NI_GPCT_TIMESTAMP_MUX_GATE_SELECT;
+ case NI_M_AI_START2_GATE_SEL:
+ return NI_GPCT_AI_START2_GATE_SELECT;
+ case NI_M_PXI_STAR_TRIGGER_GATE_SEL:
+ return NI_GPCT_PXI_STAR_TRIGGER_GATE_SELECT;
+ case NI_M_NEXT_OUT_GATE_SEL:
+ return NI_GPCT_NEXT_OUT_GATE_SELECT;
+ case NI_M_AI_START1_GATE_SEL:
+ return NI_GPCT_AI_START1_GATE_SELECT;
+ case NI_M_NEXT_SRC_GATE_SEL:
+ return NI_GPCT_NEXT_SOURCE_GATE_SELECT;
+ case NI_M_ANALOG_TRIG_OUT_GATE_SEL:
+ return NI_GPCT_ANALOG_TRIGGER_OUT_GATE_SELECT;
+ case NI_M_LOGIC_LOW_GATE_SEL:
+ return NI_GPCT_LOGIC_LOW_GATE_SELECT;
+ default:
+ for (i = 0; i <= NI_M_MAX_RTSI_CHAN; ++i) {
+ if (gate == NI_M_RTSI_GATE_SEL(i))
+ return NI_GPCT_RTSI_GATE_SELECT(i);
+ }
+ for (i = 0; i <= NI_M_MAX_PFI_CHAN; ++i) {
+ if (gate == NI_M_PFI_GATE_SEL(i))
+ return NI_GPCT_PFI_GATE_SELECT(i);
+ }
+ BUG();
+ break;
+ }
+ return 0;
+};
+
+static unsigned ni_660x_gate2_to_generic_gate(unsigned gate)
+{
+ unsigned i;
+
+ switch (gate) {
+ case NI_660X_SRC_PIN_I_GATE2_SEL:
+ return NI_GPCT_SOURCE_PIN_i_GATE_SELECT;
+ case NI_660X_UD_PIN_I_GATE2_SEL:
+ return NI_GPCT_UP_DOWN_PIN_i_GATE_SELECT;
+ case NI_660X_NEXT_SRC_GATE2_SEL:
+ return NI_GPCT_NEXT_SOURCE_GATE_SELECT;
+ case NI_660X_NEXT_OUT_GATE2_SEL:
+ return NI_GPCT_NEXT_OUT_GATE_SELECT;
+ case NI_660X_SELECTED_GATE2_SEL:
+ return NI_GPCT_SELECTED_GATE_GATE_SELECT;
+ case NI_660X_LOGIC_LOW_GATE2_SEL:
+ return NI_GPCT_LOGIC_LOW_GATE_SELECT;
+ default:
+ for (i = 0; i <= NI_660X_MAX_RTSI_CHAN; ++i) {
+ if (gate == NI_660X_RTSI_GATE2_SEL(i))
+ return NI_GPCT_RTSI_GATE_SELECT(i);
+ }
+ for (i = 0; i <= NI_660X_MAX_UP_DOWN_PIN; ++i) {
+ if (gate == NI_660X_UD_PIN_GATE2_SEL(i))
+ return NI_GPCT_UP_DOWN_PIN_GATE_SELECT(i);
+ }
+ BUG();
+ break;
+ }
+ return 0;
+};
+
+static unsigned ni_m_gate2_to_generic_gate(unsigned gate)
+{
+ /*
+ * FIXME: the second gate sources for the m series are undocumented,
+ * so we just return the raw bits for now.
+ */
+ switch (gate) {
+ default:
+ return gate;
+ }
+ return 0;
+};
+
+static int ni_tio_get_gate_src(struct ni_gpct *counter, unsigned gate_index,
+ unsigned int *gate_source)
+{
+ struct ni_gpct_device *counter_dev = counter->counter_dev;
+ unsigned cidx = counter->counter_index;
+ unsigned mode = ni_tio_get_soft_copy(counter, NITIO_MODE_REG(cidx));
+ unsigned gate2_reg = NITIO_GATE2_REG(cidx);
+ unsigned gate;
+
+ switch (gate_index) {
+ case 0:
+ if ((mode & GI_GATING_MODE_MASK) == GI_GATING_DISABLED) {
+ *gate_source = NI_GPCT_DISABLED_GATE_SELECT;
+ return 0;
+ }
+
+ gate = GI_BITS_TO_GATE(ni_tio_get_soft_copy(counter,
+ NITIO_INPUT_SEL_REG(cidx)));
+
+ switch (counter_dev->variant) {
+ case ni_gpct_variant_e_series:
+ case ni_gpct_variant_m_series:
+ default:
+ *gate_source = ni_m_gate_to_generic_gate(gate);
+ break;
+ case ni_gpct_variant_660x:
+ *gate_source = ni_660x_gate_to_generic_gate(gate);
+ break;
+ }
+ if (mode & GI_GATE_POL_INVERT)
+ *gate_source |= CR_INVERT;
+ if ((mode & GI_GATING_MODE_MASK) != GI_LEVEL_GATING)
+ *gate_source |= CR_EDGE;
+ break;
+ case 1:
+ if ((mode & GI_GATING_MODE_MASK) == GI_GATING_DISABLED ||
+ !(counter_dev->regs[gate2_reg] & GI_GATE2_MODE)) {
+ *gate_source = NI_GPCT_DISABLED_GATE_SELECT;
+ return 0;
+ }
+
+ gate = GI_BITS_TO_GATE2(counter_dev->regs[gate2_reg]);
+
+ switch (counter_dev->variant) {
+ case ni_gpct_variant_e_series:
+ case ni_gpct_variant_m_series:
+ default:
+ *gate_source = ni_m_gate2_to_generic_gate(gate);
+ break;
+ case ni_gpct_variant_660x:
+ *gate_source = ni_660x_gate2_to_generic_gate(gate);
+ break;
+ }
+ if (counter_dev->regs[gate2_reg] & GI_GATE2_POL_INVERT)
+ *gate_source |= CR_INVERT;
+ /* second gate can't have edge/level mode set independently */
+ if ((mode & GI_GATING_MODE_MASK) != GI_LEVEL_GATING)
+ *gate_source |= CR_EDGE;
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+int ni_tio_insn_config(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ struct ni_gpct *counter = s->private;
+ unsigned cidx = counter->counter_index;
+ unsigned status;
+
+ switch (data[0]) {
+ case INSN_CONFIG_SET_COUNTER_MODE:
+ return ni_tio_set_counter_mode(counter, data[1]);
+ case INSN_CONFIG_ARM:
+ return ni_tio_arm(counter, 1, data[1]);
+ case INSN_CONFIG_DISARM:
+ ni_tio_arm(counter, 0, 0);
+ return 0;
+ case INSN_CONFIG_GET_COUNTER_STATUS:
+ data[1] = 0;
+ status = read_register(counter, NITIO_SHARED_STATUS_REG(cidx));
+ if (status & GI_ARMED(cidx)) {
+ data[1] |= COMEDI_COUNTER_ARMED;
+ if (status & GI_COUNTING(cidx))
+ data[1] |= COMEDI_COUNTER_COUNTING;
+ }
+ data[2] = COMEDI_COUNTER_ARMED | COMEDI_COUNTER_COUNTING;
+ return 0;
+ case INSN_CONFIG_SET_CLOCK_SRC:
+ return ni_tio_set_clock_src(counter, data[1], data[2]);
+ case INSN_CONFIG_GET_CLOCK_SRC:
+ ni_tio_get_clock_src(counter, &data[1], &data[2]);
+ return 0;
+ case INSN_CONFIG_SET_GATE_SRC:
+ return ni_tio_set_gate_src(counter, data[1], data[2]);
+ case INSN_CONFIG_GET_GATE_SRC:
+ return ni_tio_get_gate_src(counter, data[1], &data[2]);
+ case INSN_CONFIG_SET_OTHER_SRC:
+ return ni_tio_set_other_src(counter, data[1], data[2]);
+ case INSN_CONFIG_RESET:
+ ni_tio_reset_count_and_disarm(counter);
+ return 0;
+ default:
+ break;
+ }
+ return -EINVAL;
+}
+EXPORT_SYMBOL_GPL(ni_tio_insn_config);
+
+static unsigned int ni_tio_read_sw_save_reg(struct comedi_device *dev,
+ struct comedi_subdevice *s)
+{
+ struct ni_gpct *counter = s->private;
+ unsigned cidx = counter->counter_index;
+ unsigned int val;
+
+ ni_tio_set_bits(counter, NITIO_CMD_REG(cidx), GI_SAVE_TRACE, 0);
+ ni_tio_set_bits(counter, NITIO_CMD_REG(cidx),
+ GI_SAVE_TRACE, GI_SAVE_TRACE);
+
+ /*
+ * The count doesn't get latched until the next clock edge, so it is
+ * possible the count may change (once) while we are reading. Since
+ * the read of the SW_Save_Reg isn't atomic (apparently even when it's
+ * a 32 bit register according to 660x docs), we need to read twice
+ * and make sure the reading hasn't changed. If it has, a third read
+ * will be correct since the count value will definitely have latched
+ * by then.
+ */
+ val = read_register(counter, NITIO_SW_SAVE_REG(cidx));
+ if (val != read_register(counter, NITIO_SW_SAVE_REG(cidx)))
+ val = read_register(counter, NITIO_SW_SAVE_REG(cidx));
+
+ return val;
+}
+
+int ni_tio_insn_read(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ struct ni_gpct *counter = s->private;
+ struct ni_gpct_device *counter_dev = counter->counter_dev;
+ unsigned int channel = CR_CHAN(insn->chanspec);
+ unsigned cidx = counter->counter_index;
+ int i;
+
+ for (i = 0; i < insn->n; i++) {
+ switch (channel) {
+ case 0:
+ data[i] = ni_tio_read_sw_save_reg(dev, s);
+ break;
+ case 1:
+ data[i] = counter_dev->regs[NITIO_LOADA_REG(cidx)];
+ break;
+ case 2:
+ data[i] = counter_dev->regs[NITIO_LOADB_REG(cidx)];
+ break;
+ }
+ }
+ return insn->n;
+}
+EXPORT_SYMBOL_GPL(ni_tio_insn_read);
+
+static unsigned ni_tio_next_load_register(struct ni_gpct *counter)
+{
+ unsigned cidx = counter->counter_index;
+ const unsigned bits =
+ read_register(counter, NITIO_SHARED_STATUS_REG(cidx));
+
+ return (bits & GI_NEXT_LOAD_SRC(cidx))
+ ? NITIO_LOADB_REG(cidx)
+ : NITIO_LOADA_REG(cidx);
+}
+
+int ni_tio_insn_write(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ struct ni_gpct *counter = s->private;
+ struct ni_gpct_device *counter_dev = counter->counter_dev;
+ const unsigned channel = CR_CHAN(insn->chanspec);
+ unsigned cidx = counter->counter_index;
+ unsigned load_reg;
+
+ if (insn->n < 1)
+ return 0;
+ switch (channel) {
+ case 0:
+ /*
+ * Unsafe if counter is armed.
+ * Should probably check status and return -EBUSY if armed.
+ */
+
+ /*
+ * Don't disturb load source select, just use whichever
+ * load register is already selected.
+ */
+ load_reg = ni_tio_next_load_register(counter);
+ write_register(counter, data[0], load_reg);
+ ni_tio_set_bits_transient(counter, NITIO_CMD_REG(cidx),
+ 0, 0, GI_LOAD);
+ /* restore load reg */
+ write_register(counter, counter_dev->regs[load_reg], load_reg);
+ break;
+ case 1:
+ counter_dev->regs[NITIO_LOADA_REG(cidx)] = data[0];
+ write_register(counter, data[0], NITIO_LOADA_REG(cidx));
+ break;
+ case 2:
+ counter_dev->regs[NITIO_LOADB_REG(cidx)] = data[0];
+ write_register(counter, data[0], NITIO_LOADB_REG(cidx));
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+EXPORT_SYMBOL_GPL(ni_tio_insn_write);
+
+void ni_tio_init_counter(struct ni_gpct *counter)
+{
+ struct ni_gpct_device *counter_dev = counter->counter_dev;
+ unsigned cidx = counter->counter_index;
+
+ ni_tio_reset_count_and_disarm(counter);
+
+ /* initialize counter registers */
+ counter_dev->regs[NITIO_AUTO_INC_REG(cidx)] = 0x0;
+ write_register(counter, 0x0, NITIO_AUTO_INC_REG(cidx));
+
+ ni_tio_set_bits(counter, NITIO_CMD_REG(cidx),
+ ~0, GI_SYNC_GATE);
+
+ ni_tio_set_bits(counter, NITIO_MODE_REG(cidx), ~0, 0);
+
+ counter_dev->regs[NITIO_LOADA_REG(cidx)] = 0x0;
+ write_register(counter, 0x0, NITIO_LOADA_REG(cidx));
+
+ counter_dev->regs[NITIO_LOADB_REG(cidx)] = 0x0;
+ write_register(counter, 0x0, NITIO_LOADB_REG(cidx));
+
+ ni_tio_set_bits(counter, NITIO_INPUT_SEL_REG(cidx), ~0, 0);
+
+ if (ni_tio_counting_mode_registers_present(counter_dev))
+ ni_tio_set_bits(counter, NITIO_CNT_MODE_REG(cidx), ~0, 0);
+
+ if (ni_tio_has_gate2_registers(counter_dev)) {
+ counter_dev->regs[NITIO_GATE2_REG(cidx)] = 0x0;
+ write_register(counter, 0x0, NITIO_GATE2_REG(cidx));
+ }
+
+ ni_tio_set_bits(counter, NITIO_DMA_CFG_REG(cidx), ~0, 0x0);
+
+ ni_tio_set_bits(counter, NITIO_INT_ENA_REG(cidx), ~0, 0x0);
+}
+EXPORT_SYMBOL_GPL(ni_tio_init_counter);
+
+struct ni_gpct_device *
+ni_gpct_device_construct(struct comedi_device *dev,
+ void (*write_register)(struct ni_gpct *counter,
+ unsigned bits,
+ enum ni_gpct_register reg),
+ unsigned (*read_register)(struct ni_gpct *counter,
+ enum ni_gpct_register reg),
+ enum ni_gpct_variant variant,
+ unsigned num_counters)
+{
+ struct ni_gpct_device *counter_dev;
+ struct ni_gpct *counter;
+ unsigned i;
+
+ if (num_counters == 0)
+ return NULL;
+
+ counter_dev = kzalloc(sizeof(*counter_dev), GFP_KERNEL);
+ if (!counter_dev)
+ return NULL;
+
+ counter_dev->dev = dev;
+ counter_dev->write_register = write_register;
+ counter_dev->read_register = read_register;
+ counter_dev->variant = variant;
+
+ spin_lock_init(&counter_dev->regs_lock);
+
+ counter_dev->counters = kcalloc(num_counters, sizeof(*counter),
+ GFP_KERNEL);
+ if (!counter_dev->counters) {
+ kfree(counter_dev);
+ return NULL;
+ }
+
+ for (i = 0; i < num_counters; ++i) {
+ counter = &counter_dev->counters[i];
+ counter->counter_dev = counter_dev;
+ spin_lock_init(&counter->lock);
+ }
+ counter_dev->num_counters = num_counters;
+
+ return counter_dev;
+}
+EXPORT_SYMBOL_GPL(ni_gpct_device_construct);
+
+void ni_gpct_device_destroy(struct ni_gpct_device *counter_dev)
+{
+ if (!counter_dev->counters)
+ return;
+ kfree(counter_dev->counters);
+ kfree(counter_dev);
+}
+EXPORT_SYMBOL_GPL(ni_gpct_device_destroy);
+
+static int __init ni_tio_init_module(void)
+{
+ return 0;
+}
+module_init(ni_tio_init_module);
+
+static void __exit ni_tio_cleanup_module(void)
+{
+}
+module_exit(ni_tio_cleanup_module);
+
+MODULE_AUTHOR("Comedi <comedi@comedi.org>");
+MODULE_DESCRIPTION("Comedi support for NI general-purpose counters");
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/comedi/drivers/ni_tio.h b/drivers/staging/comedi/drivers/ni_tio.h
new file mode 100644
index 000000000..25aedd0e5
--- /dev/null
+++ b/drivers/staging/comedi/drivers/ni_tio.h
@@ -0,0 +1,154 @@
+/*
+ drivers/ni_tio.h
+ Header file for NI general purpose counter support code (ni_tio.c)
+
+ COMEDI - Linux Control and Measurement Device Interface
+
+ This program is free software; you can redistribute it and/or 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.
+*/
+
+#ifndef _COMEDI_NI_TIO_H
+#define _COMEDI_NI_TIO_H
+
+#include "../comedidev.h"
+
+/* forward declarations */
+struct mite_struct;
+struct ni_gpct_device;
+
+enum ni_gpct_register {
+ NITIO_G0_AUTO_INC,
+ NITIO_G1_AUTO_INC,
+ NITIO_G2_AUTO_INC,
+ NITIO_G3_AUTO_INC,
+ NITIO_G0_CMD,
+ NITIO_G1_CMD,
+ NITIO_G2_CMD,
+ NITIO_G3_CMD,
+ NITIO_G0_HW_SAVE,
+ NITIO_G1_HW_SAVE,
+ NITIO_G2_HW_SAVE,
+ NITIO_G3_HW_SAVE,
+ NITIO_G0_SW_SAVE,
+ NITIO_G1_SW_SAVE,
+ NITIO_G2_SW_SAVE,
+ NITIO_G3_SW_SAVE,
+ NITIO_G0_MODE,
+ NITIO_G1_MODE,
+ NITIO_G2_MODE,
+ NITIO_G3_MODE,
+ NITIO_G0_LOADA,
+ NITIO_G1_LOADA,
+ NITIO_G2_LOADA,
+ NITIO_G3_LOADA,
+ NITIO_G0_LOADB,
+ NITIO_G1_LOADB,
+ NITIO_G2_LOADB,
+ NITIO_G3_LOADB,
+ NITIO_G0_INPUT_SEL,
+ NITIO_G1_INPUT_SEL,
+ NITIO_G2_INPUT_SEL,
+ NITIO_G3_INPUT_SEL,
+ NITIO_G0_CNT_MODE,
+ NITIO_G1_CNT_MODE,
+ NITIO_G2_CNT_MODE,
+ NITIO_G3_CNT_MODE,
+ NITIO_G0_GATE2,
+ NITIO_G1_GATE2,
+ NITIO_G2_GATE2,
+ NITIO_G3_GATE2,
+ NITIO_G01_STATUS,
+ NITIO_G23_STATUS,
+ NITIO_G01_RESET,
+ NITIO_G23_RESET,
+ NITIO_G01_STATUS1,
+ NITIO_G23_STATUS1,
+ NITIO_G01_STATUS2,
+ NITIO_G23_STATUS2,
+ NITIO_G0_DMA_CFG,
+ NITIO_G1_DMA_CFG,
+ NITIO_G2_DMA_CFG,
+ NITIO_G3_DMA_CFG,
+ NITIO_G0_DMA_STATUS,
+ NITIO_G1_DMA_STATUS,
+ NITIO_G2_DMA_STATUS,
+ NITIO_G3_DMA_STATUS,
+ NITIO_G0_ABZ,
+ NITIO_G1_ABZ,
+ NITIO_G0_INT_ACK,
+ NITIO_G1_INT_ACK,
+ NITIO_G2_INT_ACK,
+ NITIO_G3_INT_ACK,
+ NITIO_G0_STATUS,
+ NITIO_G1_STATUS,
+ NITIO_G2_STATUS,
+ NITIO_G3_STATUS,
+ NITIO_G0_INT_ENA,
+ NITIO_G1_INT_ENA,
+ NITIO_G2_INT_ENA,
+ NITIO_G3_INT_ENA,
+ NITIO_NUM_REGS,
+};
+
+enum ni_gpct_variant {
+ ni_gpct_variant_e_series,
+ ni_gpct_variant_m_series,
+ ni_gpct_variant_660x
+};
+
+struct ni_gpct {
+ struct ni_gpct_device *counter_dev;
+ unsigned counter_index;
+ unsigned chip_index;
+ uint64_t clock_period_ps; /* clock period in picoseconds */
+ struct mite_channel *mite_chan;
+ spinlock_t lock;
+};
+
+struct ni_gpct_device {
+ struct comedi_device *dev;
+ void (*write_register)(struct ni_gpct *counter, unsigned bits,
+ enum ni_gpct_register reg);
+ unsigned (*read_register)(struct ni_gpct *counter,
+ enum ni_gpct_register reg);
+ enum ni_gpct_variant variant;
+ struct ni_gpct *counters;
+ unsigned num_counters;
+ unsigned regs[NITIO_NUM_REGS];
+ spinlock_t regs_lock;
+};
+
+struct ni_gpct_device *
+ni_gpct_device_construct(struct comedi_device *,
+ void (*write_register)(struct ni_gpct *,
+ unsigned bits,
+ enum ni_gpct_register),
+ unsigned (*read_register)(struct ni_gpct *,
+ enum ni_gpct_register),
+ enum ni_gpct_variant,
+ unsigned num_counters);
+void ni_gpct_device_destroy(struct ni_gpct_device *);
+void ni_tio_init_counter(struct ni_gpct *);
+int ni_tio_insn_read(struct comedi_device *, struct comedi_subdevice *,
+ struct comedi_insn *, unsigned int *data);
+int ni_tio_insn_config(struct comedi_device *, struct comedi_subdevice *,
+ struct comedi_insn *, unsigned int *data);
+int ni_tio_insn_write(struct comedi_device *, struct comedi_subdevice *,
+ struct comedi_insn *, unsigned int *data);
+int ni_tio_cmd(struct comedi_device *, struct comedi_subdevice *);
+int ni_tio_cmdtest(struct comedi_device *, struct comedi_subdevice *,
+ struct comedi_cmd *);
+int ni_tio_cancel(struct ni_gpct *);
+void ni_tio_handle_interrupt(struct ni_gpct *, struct comedi_subdevice *);
+void ni_tio_set_mite_channel(struct ni_gpct *, struct mite_channel *);
+void ni_tio_acknowledge(struct ni_gpct *);
+
+#endif /* _COMEDI_NI_TIO_H */
diff --git a/drivers/staging/comedi/drivers/ni_tio_internal.h b/drivers/staging/comedi/drivers/ni_tio_internal.h
new file mode 100644
index 000000000..2bceae493
--- /dev/null
+++ b/drivers/staging/comedi/drivers/ni_tio_internal.h
@@ -0,0 +1,245 @@
+/*
+ drivers/ni_tio_internal.h
+ Header file for NI general purpose counter support code (ni_tio.c and
+ ni_tiocmd.c)
+
+ COMEDI - Linux Control and Measurement Device Interface
+
+ This program is free software; you can redistribute it and/or 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.
+*/
+
+#ifndef _COMEDI_NI_TIO_INTERNAL_H
+#define _COMEDI_NI_TIO_INTERNAL_H
+
+#include "ni_tio.h"
+
+#define NITIO_AUTO_INC_REG(x) (NITIO_G0_AUTO_INC + (x))
+#define GI_AUTO_INC_MASK 0xff
+#define NITIO_CMD_REG(x) (NITIO_G0_CMD + (x))
+#define GI_ARM (1 << 0)
+#define GI_SAVE_TRACE (1 << 1)
+#define GI_LOAD (1 << 2)
+#define GI_DISARM (1 << 4)
+#define GI_CNT_DIR(x) (((x) & 0x3) << 5)
+#define GI_CNT_DIR_MASK (3 << 5)
+#define GI_WRITE_SWITCH (1 << 7)
+#define GI_SYNC_GATE (1 << 8)
+#define GI_LITTLE_BIG_ENDIAN (1 << 9)
+#define GI_BANK_SWITCH_START (1 << 10)
+#define GI_BANK_SWITCH_MODE (1 << 11)
+#define GI_BANK_SWITCH_ENABLE (1 << 12)
+#define GI_ARM_COPY (1 << 13)
+#define GI_SAVE_TRACE_COPY (1 << 14)
+#define GI_DISARM_COPY (1 << 15)
+#define NITIO_HW_SAVE_REG(x) (NITIO_G0_HW_SAVE + (x))
+#define NITIO_SW_SAVE_REG(x) (NITIO_G0_SW_SAVE + (x))
+#define NITIO_MODE_REG(x) (NITIO_G0_MODE + (x))
+#define GI_GATING_DISABLED (0 << 0)
+#define GI_LEVEL_GATING (1 << 0)
+#define GI_RISING_EDGE_GATING (2 << 0)
+#define GI_FALLING_EDGE_GATING (3 << 0)
+#define GI_GATING_MODE_MASK (3 << 0)
+#define GI_GATE_ON_BOTH_EDGES (1 << 2)
+#define GI_EDGE_GATE_STARTS_STOPS (0 << 3)
+#define GI_EDGE_GATE_STOPS_STARTS (1 << 3)
+#define GI_EDGE_GATE_STARTS (2 << 3)
+#define GI_EDGE_GATE_NO_STARTS_OR_STOPS (3 << 3)
+#define GI_EDGE_GATE_MODE_MASK (3 << 3)
+#define GI_STOP_ON_GATE (0 << 5)
+#define GI_STOP_ON_GATE_OR_TC (1 << 5)
+#define GI_STOP_ON_GATE_OR_SECOND_TC (2 << 5)
+#define GI_STOP_MODE_MASK (3 << 5)
+#define GI_LOAD_SRC_SEL (1 << 7)
+#define GI_OUTPUT_TC_PULSE (1 << 8)
+#define GI_OUTPUT_TC_TOGGLE (2 << 8)
+#define GI_OUTPUT_TC_OR_GATE_TOGGLE (3 << 8)
+#define GI_OUTPUT_MODE_MASK (3 << 8)
+#define GI_NO_HARDWARE_DISARM (0 << 10)
+#define GI_DISARM_AT_TC (1 << 10)
+#define GI_DISARM_AT_GATE (2 << 10)
+#define GI_DISARM_AT_TC_OR_GATE (3 << 10)
+#define GI_COUNTING_ONCE_MASK (3 << 10)
+#define GI_LOADING_ON_TC (1 << 12)
+#define GI_GATE_POL_INVERT (1 << 13)
+#define GI_LOADING_ON_GATE (1 << 14)
+#define GI_RELOAD_SRC_SWITCHING (1 << 15)
+#define NITIO_LOADA_REG(x) (NITIO_G0_LOADA + (x))
+#define NITIO_LOADB_REG(x) (NITIO_G0_LOADB + (x))
+#define NITIO_INPUT_SEL_REG(x) (NITIO_G0_INPUT_SEL + (x))
+#define GI_READ_ACKS_IRQ (1 << 0)
+#define GI_WRITE_ACKS_IRQ (1 << 1)
+#define GI_BITS_TO_SRC(x) (((x) >> 2) & 0x1f)
+#define GI_SRC_SEL(x) (((x) & 0x1f) << 2)
+#define GI_SRC_SEL_MASK (0x1f << 2)
+#define GI_BITS_TO_GATE(x) (((x) >> 7) & 0x1f)
+#define GI_GATE_SEL(x) (((x) & 0x1f) << 7)
+#define GI_GATE_SEL_MASK (0x1f << 7)
+#define GI_GATE_SEL_LOAD_SRC (1 << 12)
+#define GI_OR_GATE (1 << 13)
+#define GI_OUTPUT_POL_INVERT (1 << 14)
+#define GI_SRC_POL_INVERT (1 << 15)
+#define NITIO_CNT_MODE_REG(x) (NITIO_G0_CNT_MODE + (x))
+#define GI_CNT_MODE(x) (((x) & 0x7) << 0)
+#define GI_CNT_MODE_NORMAL GI_CNT_MODE(0)
+#define GI_CNT_MODE_QUADX1 GI_CNT_MODE(1)
+#define GI_CNT_MODE_QUADX2 GI_CNT_MODE(2)
+#define GI_CNT_MODE_QUADX4 GI_CNT_MODE(3)
+#define GI_CNT_MODE_TWO_PULSE GI_CNT_MODE(4)
+#define GI_CNT_MODE_SYNC_SRC GI_CNT_MODE(6)
+#define GI_CNT_MODE_MASK (7 << 0)
+#define GI_INDEX_MODE (1 << 4)
+#define GI_INDEX_PHASE(x) (((x) & 0x3) << 5)
+#define GI_INDEX_PHASE_MASK (3 << 5)
+#define GI_HW_ARM_ENA (1 << 7)
+#define GI_HW_ARM_SEL(x) ((x) << 8)
+#define GI_660X_HW_ARM_SEL_MASK (0x7 << 8)
+#define GI_M_HW_ARM_SEL_MASK (0x1f << 8)
+#define GI_660X_PRESCALE_X8 (1 << 12)
+#define GI_M_PRESCALE_X8 (1 << 13)
+#define GI_660X_ALT_SYNC (1 << 13)
+#define GI_M_ALT_SYNC (1 << 14)
+#define GI_660X_PRESCALE_X2 (1 << 14)
+#define GI_M_PRESCALE_X2 (1 << 15)
+#define NITIO_GATE2_REG(x) (NITIO_G0_GATE2 + (x))
+#define GI_GATE2_MODE (1 << 0)
+#define GI_BITS_TO_GATE2(x) (((x) >> 7) & 0x1f)
+#define GI_GATE2_SEL(x) (((x) & 0x1f) << 7)
+#define GI_GATE2_SEL_MASK (0x1f << 7)
+#define GI_GATE2_POL_INVERT (1 << 13)
+#define GI_GATE2_SUBSEL (1 << 14)
+#define GI_SRC_SUBSEL (1 << 15)
+#define NITIO_SHARED_STATUS_REG(x) (NITIO_G01_STATUS + ((x) / 2))
+#define GI_SAVE(x) (((x) % 2) ? (1 << 1) : (1 << 0))
+#define GI_COUNTING(x) (((x) % 2) ? (1 << 3) : (1 << 2))
+#define GI_NEXT_LOAD_SRC(x) (((x) % 2) ? (1 << 5) : (1 << 4))
+#define GI_STALE_DATA(x) (((x) % 2) ? (1 << 7) : (1 << 6))
+#define GI_ARMED(x) (((x) % 2) ? (1 << 9) : (1 << 8))
+#define GI_NO_LOAD_BETWEEN_GATES(x) (((x) % 2) ? (1 << 11) : (1 << 10))
+#define GI_TC_ERROR(x) (((x) % 2) ? (1 << 13) : (1 << 12))
+#define GI_GATE_ERROR(x) (((x) % 2) ? (1 << 15) : (1 << 14))
+#define NITIO_RESET_REG(x) (NITIO_G01_RESET + ((x) / 2))
+#define GI_RESET(x) (1 << (2 + ((x) % 2)))
+#define NITIO_STATUS1_REG(x) (NITIO_G01_STATUS1 + ((x) / 2))
+#define NITIO_STATUS2_REG(x) (NITIO_G01_STATUS2 + ((x) / 2))
+#define GI_OUTPUT(x) (((x) % 2) ? (1 << 1) : (1 << 0))
+#define GI_HW_SAVE(x) (((x) % 2) ? (1 << 13) : (1 << 12))
+#define GI_PERMANENT_STALE(x) (((x) % 2) ? (1 << 15) : (1 << 14))
+#define NITIO_DMA_CFG_REG(x) (NITIO_G0_DMA_CFG + (x))
+#define GI_DMA_ENABLE (1 << 0)
+#define GI_DMA_WRITE (1 << 1)
+#define GI_DMA_INT_ENA (1 << 2)
+#define GI_DMA_RESET (1 << 3)
+#define GI_DMA_BANKSW_ERROR (1 << 4)
+#define NITIO_DMA_STATUS_REG(x) (NITIO_G0_DMA_STATUS + (x))
+#define GI_DMA_READBANK (1 << 13)
+#define GI_DRQ_ERROR (1 << 14)
+#define GI_DRQ_STATUS (1 << 15)
+#define NITIO_ABZ_REG(x) (NITIO_G0_ABZ + (x))
+#define NITIO_INT_ACK_REG(x) (NITIO_G0_INT_ACK + (x))
+#define GI_GATE_ERROR_CONFIRM(x) (((x) % 2) ? (1 << 1) : (1 << 5))
+#define GI_TC_ERROR_CONFIRM(x) (((x) % 2) ? (1 << 2) : (1 << 6))
+#define GI_TC_INTERRUPT_ACK (1 << 14)
+#define GI_GATE_INTERRUPT_ACK (1 << 15)
+#define NITIO_STATUS_REG(x) (NITIO_G0_STATUS + (x))
+#define GI_GATE_INTERRUPT (1 << 2)
+#define GI_TC (1 << 3)
+#define GI_INTERRUPT (1 << 15)
+#define NITIO_INT_ENA_REG(x) (NITIO_G0_INT_ENA + (x))
+#define GI_TC_INTERRUPT_ENABLE(x) (((x) % 2) ? (1 << 9) : (1 << 6))
+#define GI_GATE_INTERRUPT_ENABLE(x) (((x) % 2) ? (1 << 10) : (1 << 8))
+
+static inline void write_register(struct ni_gpct *counter, unsigned bits,
+ enum ni_gpct_register reg)
+{
+ BUG_ON(reg >= NITIO_NUM_REGS);
+ counter->counter_dev->write_register(counter, bits, reg);
+}
+
+static inline unsigned read_register(struct ni_gpct *counter,
+ enum ni_gpct_register reg)
+{
+ BUG_ON(reg >= NITIO_NUM_REGS);
+ return counter->counter_dev->read_register(counter, reg);
+}
+
+static inline int ni_tio_counting_mode_registers_present(const struct
+ ni_gpct_device
+ *counter_dev)
+{
+ switch (counter_dev->variant) {
+ case ni_gpct_variant_e_series:
+ return 0;
+ case ni_gpct_variant_m_series:
+ case ni_gpct_variant_660x:
+ return 1;
+ default:
+ BUG();
+ break;
+ }
+ return 0;
+}
+
+static inline void ni_tio_set_bits_transient(struct ni_gpct *counter,
+ enum ni_gpct_register
+ register_index, unsigned bit_mask,
+ unsigned bit_values,
+ unsigned transient_bit_values)
+{
+ struct ni_gpct_device *counter_dev = counter->counter_dev;
+ unsigned long flags;
+
+ BUG_ON(register_index >= NITIO_NUM_REGS);
+ spin_lock_irqsave(&counter_dev->regs_lock, flags);
+ counter_dev->regs[register_index] &= ~bit_mask;
+ counter_dev->regs[register_index] |= (bit_values & bit_mask);
+ write_register(counter,
+ counter_dev->regs[register_index] | transient_bit_values,
+ register_index);
+ mmiowb();
+ spin_unlock_irqrestore(&counter_dev->regs_lock, flags);
+}
+
+/* ni_tio_set_bits( ) is for safely writing to registers whose bits may be
+ * twiddled in interrupt context, or whose software copy may be read in
+ * interrupt context.
+ */
+static inline void ni_tio_set_bits(struct ni_gpct *counter,
+ enum ni_gpct_register register_index,
+ unsigned bit_mask, unsigned bit_values)
+{
+ ni_tio_set_bits_transient(counter, register_index, bit_mask, bit_values,
+ 0x0);
+}
+
+/* ni_tio_get_soft_copy( ) is for safely reading the software copy of a register
+whose bits might be modified in interrupt context, or whose software copy
+might need to be read in interrupt context.
+*/
+static inline unsigned ni_tio_get_soft_copy(const struct ni_gpct *counter,
+ enum ni_gpct_register
+ register_index)
+{
+ struct ni_gpct_device *counter_dev = counter->counter_dev;
+ unsigned long flags;
+ unsigned value;
+
+ BUG_ON(register_index >= NITIO_NUM_REGS);
+ spin_lock_irqsave(&counter_dev->regs_lock, flags);
+ value = counter_dev->regs[register_index];
+ spin_unlock_irqrestore(&counter_dev->regs_lock, flags);
+ return value;
+}
+
+int ni_tio_arm(struct ni_gpct *counter, int arm, unsigned start_trigger);
+int ni_tio_set_gate_src(struct ni_gpct *counter, unsigned gate_index,
+ unsigned int gate_source);
+
+#endif /* _COMEDI_NI_TIO_INTERNAL_H */
diff --git a/drivers/staging/comedi/drivers/ni_tiocmd.c b/drivers/staging/comedi/drivers/ni_tiocmd.c
new file mode 100644
index 000000000..9b124b09e
--- /dev/null
+++ b/drivers/staging/comedi/drivers/ni_tiocmd.c
@@ -0,0 +1,484 @@
+/*
+ comedi/drivers/ni_tiocmd.c
+ Command support for NI general purpose counters
+
+ Copyright (C) 2006 Frank Mori Hess <fmhess@users.sourceforge.net>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+*/
+
+/*
+ * Module: ni_tiocmd
+ * Description: National Instruments general purpose counters command support
+ * Author: J.P. Mellor <jpmellor@rose-hulman.edu>,
+ * Herman.Bruyninckx@mech.kuleuven.ac.be,
+ * Wim.Meeussen@mech.kuleuven.ac.be,
+ * Klaas.Gadeyne@mech.kuleuven.ac.be,
+ * Frank Mori Hess <fmhess@users.sourceforge.net>
+ * Updated: Fri, 11 Apr 2008 12:32:35 +0100
+ * Status: works
+ *
+ * This module is not used directly by end-users. Rather, it
+ * is used by other drivers (for example ni_660x and ni_pcimio)
+ * to provide command support for NI's general purpose counters.
+ * It was originally split out of ni_tio.c to stop the 'ni_tio'
+ * module depending on the 'mite' module.
+ *
+ * References:
+ * DAQ 660x Register-Level Programmer Manual (NI 370505A-01)
+ * DAQ 6601/6602 User Manual (NI 322137B-01)
+ * 340934b.pdf DAQ-STC reference manual
+ */
+
+/*
+TODO:
+ Support use of both banks X and Y
+*/
+
+#include <linux/module.h>
+#include "ni_tio_internal.h"
+#include "mite.h"
+
+static void ni_tio_configure_dma(struct ni_gpct *counter,
+ bool enable, bool read)
+{
+ struct ni_gpct_device *counter_dev = counter->counter_dev;
+ unsigned cidx = counter->counter_index;
+ unsigned mask;
+ unsigned bits;
+
+ mask = GI_READ_ACKS_IRQ | GI_WRITE_ACKS_IRQ;
+ bits = 0;
+
+ if (enable) {
+ if (read)
+ bits |= GI_READ_ACKS_IRQ;
+ else
+ bits |= GI_WRITE_ACKS_IRQ;
+ }
+ ni_tio_set_bits(counter, NITIO_INPUT_SEL_REG(cidx), mask, bits);
+
+ switch (counter_dev->variant) {
+ case ni_gpct_variant_e_series:
+ break;
+ case ni_gpct_variant_m_series:
+ case ni_gpct_variant_660x:
+ mask = GI_DMA_ENABLE | GI_DMA_INT_ENA | GI_DMA_WRITE;
+ bits = 0;
+
+ if (enable)
+ bits |= GI_DMA_ENABLE | GI_DMA_INT_ENA;
+ if (!read)
+ bits |= GI_DMA_WRITE;
+ ni_tio_set_bits(counter, NITIO_DMA_CFG_REG(cidx), mask, bits);
+ break;
+ }
+}
+
+static int ni_tio_input_inttrig(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ unsigned int trig_num)
+{
+ struct ni_gpct *counter = s->private;
+ struct comedi_cmd *cmd = &s->async->cmd;
+ unsigned long flags;
+ int ret = 0;
+
+ if (trig_num != cmd->start_src)
+ return -EINVAL;
+
+ spin_lock_irqsave(&counter->lock, flags);
+ if (counter->mite_chan)
+ mite_dma_arm(counter->mite_chan);
+ else
+ ret = -EIO;
+ spin_unlock_irqrestore(&counter->lock, flags);
+ if (ret < 0)
+ return ret;
+ ret = ni_tio_arm(counter, 1, NI_GPCT_ARM_IMMEDIATE);
+ s->async->inttrig = NULL;
+
+ return ret;
+}
+
+static int ni_tio_input_cmd(struct comedi_subdevice *s)
+{
+ struct ni_gpct *counter = s->private;
+ struct ni_gpct_device *counter_dev = counter->counter_dev;
+ unsigned cidx = counter->counter_index;
+ struct comedi_async *async = s->async;
+ struct comedi_cmd *cmd = &async->cmd;
+ int ret = 0;
+
+ /* write alloc the entire buffer */
+ comedi_buf_write_alloc(s, async->prealloc_bufsz);
+ counter->mite_chan->dir = COMEDI_INPUT;
+ switch (counter_dev->variant) {
+ case ni_gpct_variant_m_series:
+ case ni_gpct_variant_660x:
+ mite_prep_dma(counter->mite_chan, 32, 32);
+ break;
+ case ni_gpct_variant_e_series:
+ mite_prep_dma(counter->mite_chan, 16, 32);
+ break;
+ default:
+ BUG();
+ break;
+ }
+ ni_tio_set_bits(counter, NITIO_CMD_REG(cidx), GI_SAVE_TRACE, 0);
+ ni_tio_configure_dma(counter, true, true);
+
+ if (cmd->start_src == TRIG_INT) {
+ async->inttrig = &ni_tio_input_inttrig;
+ } else { /* TRIG_NOW || TRIG_EXT || TRIG_OTHER */
+ async->inttrig = NULL;
+ mite_dma_arm(counter->mite_chan);
+
+ if (cmd->start_src == TRIG_NOW)
+ ret = ni_tio_arm(counter, 1, NI_GPCT_ARM_IMMEDIATE);
+ else if (cmd->start_src == TRIG_EXT)
+ ret = ni_tio_arm(counter, 1, cmd->start_arg);
+ }
+ return ret;
+}
+
+static int ni_tio_output_cmd(struct comedi_subdevice *s)
+{
+ struct ni_gpct *counter = s->private;
+
+ dev_err(counter->counter_dev->dev->class_dev,
+ "output commands not yet implemented.\n");
+ return -ENOTSUPP;
+
+ counter->mite_chan->dir = COMEDI_OUTPUT;
+ mite_prep_dma(counter->mite_chan, 32, 32);
+ ni_tio_configure_dma(counter, true, false);
+ mite_dma_arm(counter->mite_chan);
+ return ni_tio_arm(counter, 1, NI_GPCT_ARM_IMMEDIATE);
+}
+
+static int ni_tio_cmd_setup(struct comedi_subdevice *s)
+{
+ struct comedi_cmd *cmd = &s->async->cmd;
+ struct ni_gpct *counter = s->private;
+ unsigned cidx = counter->counter_index;
+ int set_gate_source = 0;
+ unsigned gate_source;
+ int retval = 0;
+
+ if (cmd->scan_begin_src == TRIG_EXT) {
+ set_gate_source = 1;
+ gate_source = cmd->scan_begin_arg;
+ } else if (cmd->convert_src == TRIG_EXT) {
+ set_gate_source = 1;
+ gate_source = cmd->convert_arg;
+ }
+ if (set_gate_source)
+ retval = ni_tio_set_gate_src(counter, 0, gate_source);
+ if (cmd->flags & CMDF_WAKE_EOS) {
+ ni_tio_set_bits(counter, NITIO_INT_ENA_REG(cidx),
+ GI_GATE_INTERRUPT_ENABLE(cidx),
+ GI_GATE_INTERRUPT_ENABLE(cidx));
+ }
+ return retval;
+}
+
+int ni_tio_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
+{
+ struct ni_gpct *counter = s->private;
+ struct comedi_async *async = s->async;
+ struct comedi_cmd *cmd = &async->cmd;
+ int retval = 0;
+ unsigned long flags;
+
+ spin_lock_irqsave(&counter->lock, flags);
+ if (!counter->mite_chan) {
+ dev_err(counter->counter_dev->dev->class_dev,
+ "commands only supported with DMA. ");
+ dev_err(counter->counter_dev->dev->class_dev,
+ "Interrupt-driven commands not yet implemented.\n");
+ retval = -EIO;
+ } else {
+ retval = ni_tio_cmd_setup(s);
+ if (retval == 0) {
+ if (cmd->flags & CMDF_WRITE)
+ retval = ni_tio_output_cmd(s);
+ else
+ retval = ni_tio_input_cmd(s);
+ }
+ }
+ spin_unlock_irqrestore(&counter->lock, flags);
+ return retval;
+}
+EXPORT_SYMBOL_GPL(ni_tio_cmd);
+
+int ni_tio_cmdtest(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_cmd *cmd)
+{
+ struct ni_gpct *counter = s->private;
+ int err = 0;
+ unsigned int sources;
+
+ /* Step 1 : check if triggers are trivially valid */
+
+ sources = TRIG_NOW | TRIG_INT | TRIG_OTHER;
+ if (ni_tio_counting_mode_registers_present(counter->counter_dev))
+ sources |= TRIG_EXT;
+ err |= comedi_check_trigger_src(&cmd->start_src, sources);
+
+ err |= comedi_check_trigger_src(&cmd->scan_begin_src,
+ TRIG_FOLLOW | TRIG_EXT | TRIG_OTHER);
+ err |= comedi_check_trigger_src(&cmd->convert_src,
+ TRIG_NOW | TRIG_EXT | TRIG_OTHER);
+ err |= comedi_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
+ err |= comedi_check_trigger_src(&cmd->stop_src, TRIG_NONE);
+
+ if (err)
+ return 1;
+
+ /* Step 2a : make sure trigger sources are unique */
+
+ err |= comedi_check_trigger_is_unique(cmd->start_src);
+ err |= comedi_check_trigger_is_unique(cmd->scan_begin_src);
+ err |= comedi_check_trigger_is_unique(cmd->convert_src);
+
+ /* Step 2b : and mutually compatible */
+
+ if (cmd->convert_src != TRIG_NOW && cmd->scan_begin_src != TRIG_FOLLOW)
+ err |= -EINVAL;
+
+ if (err)
+ return 2;
+
+ /* Step 3: check if arguments are trivially valid */
+
+ switch (cmd->start_src) {
+ case TRIG_NOW:
+ case TRIG_INT:
+ case TRIG_OTHER:
+ err |= comedi_check_trigger_arg_is(&cmd->start_arg, 0);
+ break;
+ case TRIG_EXT:
+ /* start_arg is the start_trigger passed to ni_tio_arm() */
+ break;
+ }
+
+ if (cmd->scan_begin_src != TRIG_EXT)
+ err |= comedi_check_trigger_arg_is(&cmd->scan_begin_arg, 0);
+
+ if (cmd->convert_src != TRIG_EXT)
+ err |= comedi_check_trigger_arg_is(&cmd->convert_arg, 0);
+
+ err |= comedi_check_trigger_arg_is(&cmd->scan_end_arg,
+ cmd->chanlist_len);
+ err |= comedi_check_trigger_arg_is(&cmd->stop_arg, 0);
+
+ if (err)
+ return 3;
+
+ /* Step 4: fix up any arguments */
+
+ /* Step 5: check channel list if it exists */
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(ni_tio_cmdtest);
+
+int ni_tio_cancel(struct ni_gpct *counter)
+{
+ unsigned cidx = counter->counter_index;
+ unsigned long flags;
+
+ ni_tio_arm(counter, 0, 0);
+ spin_lock_irqsave(&counter->lock, flags);
+ if (counter->mite_chan)
+ mite_dma_disarm(counter->mite_chan);
+ spin_unlock_irqrestore(&counter->lock, flags);
+ ni_tio_configure_dma(counter, false, false);
+
+ ni_tio_set_bits(counter, NITIO_INT_ENA_REG(cidx),
+ GI_GATE_INTERRUPT_ENABLE(cidx), 0x0);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(ni_tio_cancel);
+
+ /* During buffered input counter operation for e-series, the gate
+ interrupt is acked automatically by the dma controller, due to the
+ Gi_Read/Write_Acknowledges_IRQ bits in the input select register. */
+static int should_ack_gate(struct ni_gpct *counter)
+{
+ unsigned long flags;
+ int retval = 0;
+
+ switch (counter->counter_dev->variant) {
+ case ni_gpct_variant_m_series:
+ /* not sure if 660x really supports gate
+ interrupts (the bits are not listed
+ in register-level manual) */
+ case ni_gpct_variant_660x:
+ return 1;
+ case ni_gpct_variant_e_series:
+ spin_lock_irqsave(&counter->lock, flags);
+ {
+ if (!counter->mite_chan ||
+ counter->mite_chan->dir != COMEDI_INPUT ||
+ (mite_done(counter->mite_chan))) {
+ retval = 1;
+ }
+ }
+ spin_unlock_irqrestore(&counter->lock, flags);
+ break;
+ }
+ return retval;
+}
+
+static void ni_tio_acknowledge_and_confirm(struct ni_gpct *counter,
+ int *gate_error,
+ int *tc_error,
+ int *perm_stale_data,
+ int *stale_data)
+{
+ unsigned cidx = counter->counter_index;
+ const unsigned short gxx_status = read_register(counter,
+ NITIO_SHARED_STATUS_REG(cidx));
+ const unsigned short gi_status = read_register(counter,
+ NITIO_STATUS_REG(cidx));
+ unsigned ack = 0;
+
+ if (gate_error)
+ *gate_error = 0;
+ if (tc_error)
+ *tc_error = 0;
+ if (perm_stale_data)
+ *perm_stale_data = 0;
+ if (stale_data)
+ *stale_data = 0;
+
+ if (gxx_status & GI_GATE_ERROR(cidx)) {
+ ack |= GI_GATE_ERROR_CONFIRM(cidx);
+ if (gate_error) {
+ /*660x don't support automatic acknowledgment
+ of gate interrupt via dma read/write
+ and report bogus gate errors */
+ if (counter->counter_dev->variant !=
+ ni_gpct_variant_660x)
+ *gate_error = 1;
+ }
+ }
+ if (gxx_status & GI_TC_ERROR(cidx)) {
+ ack |= GI_TC_ERROR_CONFIRM(cidx);
+ if (tc_error)
+ *tc_error = 1;
+ }
+ if (gi_status & GI_TC)
+ ack |= GI_TC_INTERRUPT_ACK;
+ if (gi_status & GI_GATE_INTERRUPT) {
+ if (should_ack_gate(counter))
+ ack |= GI_GATE_INTERRUPT_ACK;
+ }
+ if (ack)
+ write_register(counter, ack, NITIO_INT_ACK_REG(cidx));
+ if (ni_tio_get_soft_copy(counter, NITIO_MODE_REG(cidx)) &
+ GI_LOADING_ON_GATE) {
+ if (gxx_status & GI_STALE_DATA(cidx)) {
+ if (stale_data)
+ *stale_data = 1;
+ }
+ if (read_register(counter, NITIO_STATUS2_REG(cidx)) &
+ GI_PERMANENT_STALE(cidx)) {
+ dev_info(counter->counter_dev->dev->class_dev,
+ "%s: Gi_Permanent_Stale_Data detected.\n",
+ __func__);
+ if (perm_stale_data)
+ *perm_stale_data = 1;
+ }
+ }
+}
+
+void ni_tio_acknowledge(struct ni_gpct *counter)
+{
+ ni_tio_acknowledge_and_confirm(counter, NULL, NULL, NULL, NULL);
+}
+EXPORT_SYMBOL_GPL(ni_tio_acknowledge);
+
+void ni_tio_handle_interrupt(struct ni_gpct *counter,
+ struct comedi_subdevice *s)
+{
+ unsigned cidx = counter->counter_index;
+ unsigned gpct_mite_status;
+ unsigned long flags;
+ int gate_error;
+ int tc_error;
+ int perm_stale_data;
+
+ ni_tio_acknowledge_and_confirm(counter, &gate_error, &tc_error,
+ &perm_stale_data, NULL);
+ if (gate_error) {
+ dev_notice(counter->counter_dev->dev->class_dev,
+ "%s: Gi_Gate_Error detected.\n", __func__);
+ s->async->events |= COMEDI_CB_OVERFLOW;
+ }
+ if (perm_stale_data)
+ s->async->events |= COMEDI_CB_ERROR;
+ switch (counter->counter_dev->variant) {
+ case ni_gpct_variant_m_series:
+ case ni_gpct_variant_660x:
+ if (read_register(counter, NITIO_DMA_STATUS_REG(cidx)) &
+ GI_DRQ_ERROR) {
+ dev_notice(counter->counter_dev->dev->class_dev,
+ "%s: Gi_DRQ_Error detected.\n", __func__);
+ s->async->events |= COMEDI_CB_OVERFLOW;
+ }
+ break;
+ case ni_gpct_variant_e_series:
+ break;
+ }
+ spin_lock_irqsave(&counter->lock, flags);
+ if (!counter->mite_chan) {
+ spin_unlock_irqrestore(&counter->lock, flags);
+ return;
+ }
+ gpct_mite_status = mite_get_status(counter->mite_chan);
+ if (gpct_mite_status & CHSR_LINKC)
+ writel(CHOR_CLRLC,
+ counter->mite_chan->mite->mite_io_addr +
+ MITE_CHOR(counter->mite_chan->channel));
+ mite_sync_input_dma(counter->mite_chan, s);
+ spin_unlock_irqrestore(&counter->lock, flags);
+}
+EXPORT_SYMBOL_GPL(ni_tio_handle_interrupt);
+
+void ni_tio_set_mite_channel(struct ni_gpct *counter,
+ struct mite_channel *mite_chan)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&counter->lock, flags);
+ counter->mite_chan = mite_chan;
+ spin_unlock_irqrestore(&counter->lock, flags);
+}
+EXPORT_SYMBOL_GPL(ni_tio_set_mite_channel);
+
+static int __init ni_tiocmd_init_module(void)
+{
+ return 0;
+}
+module_init(ni_tiocmd_init_module);
+
+static void __exit ni_tiocmd_cleanup_module(void)
+{
+}
+module_exit(ni_tiocmd_cleanup_module);
+
+MODULE_AUTHOR("Comedi <comedi@comedi.org>");
+MODULE_DESCRIPTION("Comedi command support for NI general-purpose counters");
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/comedi/drivers/ni_usb6501.c b/drivers/staging/comedi/drivers/ni_usb6501.c
new file mode 100644
index 000000000..5f649f88d
--- /dev/null
+++ b/drivers/staging/comedi/drivers/ni_usb6501.c
@@ -0,0 +1,620 @@
+/*
+ * comedi/drivers/ni_usb6501.c
+ * Comedi driver for National Instruments USB-6501
+ *
+ * COMEDI - Linux Control and Measurement Device Interface
+ * Copyright (C) 2014 Luca Ellero <luca.ellero@brickedbrain.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+/*
+ * Driver: ni_usb6501
+ * Description: National Instruments USB-6501 module
+ * Devices: [National Instruments] USB-6501 (ni_usb6501)
+ * Author: Luca Ellero <luca.ellero@brickedbrain.com>
+ * Updated: 8 Sep 2014
+ * Status: works
+ *
+ *
+ * Configuration Options:
+ * none
+ */
+
+/*
+ * NI-6501 - USB PROTOCOL DESCRIPTION
+ *
+ * Every command is composed by two USB packets:
+ * - request (out)
+ * - response (in)
+ *
+ * Every packet is at least 12 bytes long, here is the meaning of
+ * every field (all values are hex):
+ *
+ * byte 0 is always 00
+ * byte 1 is always 01
+ * byte 2 is always 00
+ * byte 3 is the total packet length
+ *
+ * byte 4 is always 00
+ * byte 5 is is the total packet length - 4
+ * byte 6 is always 01
+ * byte 7 is the command
+ *
+ * byte 8 is 02 (request) or 00 (response)
+ * byte 9 is 00 (response) or 10 (port request) or 20 (counter request)
+ * byte 10 is always 00
+ * byte 11 is 00 (request) or 02 (response)
+ *
+ * PORT PACKETS
+ *
+ * CMD: 0xE READ_PORT
+ * REQ: 00 01 00 10 00 0C 01 0E 02 10 00 00 00 03 <PORT> 00
+ * RES: 00 01 00 10 00 0C 01 00 00 00 00 02 00 03 <BMAP> 00
+ *
+ * CMD: 0xF WRITE_PORT
+ * REQ: 00 01 00 14 00 10 01 0F 02 10 00 00 00 03 <PORT> 00 03 <BMAP> 00 00
+ * RES: 00 01 00 0C 00 08 01 00 00 00 00 02
+ *
+ * CMD: 0x12 SET_PORT_DIR (0 = input, 1 = output)
+ * REQ: 00 01 00 18 00 14 01 12 02 10 00 00
+ * 00 05 <PORT 0> <PORT 1> <PORT 2> 00 05 00 00 00 00 00
+ * RES: 00 01 00 0C 00 08 01 00 00 00 00 02
+ *
+ * COUNTER PACKETS
+ *
+ * CMD 0x9: START_COUNTER
+ * REQ: 00 01 00 0C 00 08 01 09 02 20 00 00
+ * RES: 00 01 00 0C 00 08 01 00 00 00 00 02
+ *
+ * CMD 0xC: STOP_COUNTER
+ * REQ: 00 01 00 0C 00 08 01 0C 02 20 00 00
+ * RES: 00 01 00 0C 00 08 01 00 00 00 00 02
+ *
+ * CMD 0xE: READ_COUNTER
+ * REQ: 00 01 00 0C 00 08 01 0E 02 20 00 00
+ * RES: 00 01 00 10 00 0C 01 00 00 00 00 02 <u32 counter value, Big Endian>
+ *
+ * CMD 0xF: WRITE_COUNTER
+ * REQ: 00 01 00 10 00 0C 01 0F 02 20 00 00 <u32 counter value, Big Endian>
+ * RES: 00 01 00 0C 00 08 01 00 00 00 00 02
+ *
+ *
+ * Please visit http://www.brickedbrain.com if you need
+ * additional information or have any questions.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+
+#include "../comedi_usb.h"
+
+#define NI6501_TIMEOUT 1000
+
+/* Port request packets */
+static const u8 READ_PORT_REQUEST[] = {0x00, 0x01, 0x00, 0x10,
+ 0x00, 0x0C, 0x01, 0x0E,
+ 0x02, 0x10, 0x00, 0x00,
+ 0x00, 0x03, 0x00, 0x00};
+
+static const u8 WRITE_PORT_REQUEST[] = {0x00, 0x01, 0x00, 0x14,
+ 0x00, 0x10, 0x01, 0x0F,
+ 0x02, 0x10, 0x00, 0x00,
+ 0x00, 0x03, 0x00, 0x00,
+ 0x03, 0x00, 0x00, 0x00};
+
+static const u8 SET_PORT_DIR_REQUEST[] = {0x00, 0x01, 0x00, 0x18,
+ 0x00, 0x14, 0x01, 0x12,
+ 0x02, 0x10, 0x00, 0x00,
+ 0x00, 0x05, 0x00, 0x00,
+ 0x00, 0x00, 0x05, 0x00,
+ 0x00, 0x00, 0x00, 0x00};
+
+/* Counter request packets */
+static const u8 START_COUNTER_REQUEST[] = {0x00, 0x01, 0x00, 0x0C,
+ 0x00, 0x08, 0x01, 0x09,
+ 0x02, 0x20, 0x00, 0x00};
+
+static const u8 STOP_COUNTER_REQUEST[] = {0x00, 0x01, 0x00, 0x0C,
+ 0x00, 0x08, 0x01, 0x0C,
+ 0x02, 0x20, 0x00, 0x00};
+
+static const u8 READ_COUNTER_REQUEST[] = {0x00, 0x01, 0x00, 0x0C,
+ 0x00, 0x08, 0x01, 0x0E,
+ 0x02, 0x20, 0x00, 0x00};
+
+static const u8 WRITE_COUNTER_REQUEST[] = {0x00, 0x01, 0x00, 0x10,
+ 0x00, 0x0C, 0x01, 0x0F,
+ 0x02, 0x20, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00};
+
+/* Response packets */
+static const u8 GENERIC_RESPONSE[] = {0x00, 0x01, 0x00, 0x0C,
+ 0x00, 0x08, 0x01, 0x00,
+ 0x00, 0x00, 0x00, 0x02};
+
+static const u8 READ_PORT_RESPONSE[] = {0x00, 0x01, 0x00, 0x10,
+ 0x00, 0x0C, 0x01, 0x00,
+ 0x00, 0x00, 0x00, 0x02,
+ 0x00, 0x03, 0x00, 0x00};
+
+static const u8 READ_COUNTER_RESPONSE[] = {0x00, 0x01, 0x00, 0x10,
+ 0x00, 0x0C, 0x01, 0x00,
+ 0x00, 0x00, 0x00, 0x02,
+ 0x00, 0x00, 0x00, 0x00};
+
+enum commands {
+ READ_PORT,
+ WRITE_PORT,
+ SET_PORT_DIR,
+ START_COUNTER,
+ STOP_COUNTER,
+ READ_COUNTER,
+ WRITE_COUNTER
+};
+
+struct ni6501_private {
+ struct usb_endpoint_descriptor *ep_rx;
+ struct usb_endpoint_descriptor *ep_tx;
+ struct semaphore sem;
+ u8 *usb_rx_buf;
+ u8 *usb_tx_buf;
+};
+
+static int ni6501_port_command(struct comedi_device *dev, int command,
+ const u8 *port, u8 *bitmap)
+{
+ struct usb_device *usb = comedi_to_usb_dev(dev);
+ struct ni6501_private *devpriv = dev->private;
+ int request_size, response_size;
+ u8 *tx = devpriv->usb_tx_buf;
+ int ret;
+
+ if (command != SET_PORT_DIR && !bitmap)
+ return -EINVAL;
+
+ down(&devpriv->sem);
+
+ switch (command) {
+ case READ_PORT:
+ request_size = sizeof(READ_PORT_REQUEST);
+ response_size = sizeof(READ_PORT_RESPONSE);
+ memcpy(tx, READ_PORT_REQUEST, request_size);
+ tx[14] = port[0];
+ break;
+ case WRITE_PORT:
+ request_size = sizeof(WRITE_PORT_REQUEST);
+ response_size = sizeof(GENERIC_RESPONSE);
+ memcpy(tx, WRITE_PORT_REQUEST, request_size);
+ tx[14] = port[0];
+ tx[17] = bitmap[0];
+ break;
+ case SET_PORT_DIR:
+ request_size = sizeof(SET_PORT_DIR_REQUEST);
+ response_size = sizeof(GENERIC_RESPONSE);
+ memcpy(tx, SET_PORT_DIR_REQUEST, request_size);
+ tx[14] = port[0];
+ tx[15] = port[1];
+ tx[16] = port[2];
+ break;
+ default:
+ ret = -EINVAL;
+ goto end;
+ }
+
+ ret = usb_bulk_msg(usb,
+ usb_sndbulkpipe(usb,
+ devpriv->ep_tx->bEndpointAddress),
+ devpriv->usb_tx_buf,
+ request_size,
+ NULL,
+ NI6501_TIMEOUT);
+ if (ret)
+ goto end;
+
+ ret = usb_bulk_msg(usb,
+ usb_rcvbulkpipe(usb,
+ devpriv->ep_rx->bEndpointAddress),
+ devpriv->usb_rx_buf,
+ response_size,
+ NULL,
+ NI6501_TIMEOUT);
+ if (ret)
+ goto end;
+
+ /* Check if results are valid */
+
+ if (command == READ_PORT) {
+ bitmap[0] = devpriv->usb_rx_buf[14];
+ /* mask bitmap for comparing */
+ devpriv->usb_rx_buf[14] = 0x00;
+
+ if (memcmp(devpriv->usb_rx_buf, READ_PORT_RESPONSE,
+ sizeof(READ_PORT_RESPONSE))) {
+ ret = -EINVAL;
+ }
+ } else if (memcmp(devpriv->usb_rx_buf, GENERIC_RESPONSE,
+ sizeof(GENERIC_RESPONSE))) {
+ ret = -EINVAL;
+ }
+end:
+ up(&devpriv->sem);
+
+ return ret;
+}
+
+static int ni6501_counter_command(struct comedi_device *dev, int command,
+ u32 *val)
+{
+ struct usb_device *usb = comedi_to_usb_dev(dev);
+ struct ni6501_private *devpriv = dev->private;
+ int request_size, response_size;
+ u8 *tx = devpriv->usb_tx_buf;
+ int ret;
+
+ if ((command == READ_COUNTER || command == WRITE_COUNTER) && !val)
+ return -EINVAL;
+
+ down(&devpriv->sem);
+
+ switch (command) {
+ case START_COUNTER:
+ request_size = sizeof(START_COUNTER_REQUEST);
+ response_size = sizeof(GENERIC_RESPONSE);
+ memcpy(tx, START_COUNTER_REQUEST, request_size);
+ break;
+ case STOP_COUNTER:
+ request_size = sizeof(STOP_COUNTER_REQUEST);
+ response_size = sizeof(GENERIC_RESPONSE);
+ memcpy(tx, STOP_COUNTER_REQUEST, request_size);
+ break;
+ case READ_COUNTER:
+ request_size = sizeof(READ_COUNTER_REQUEST);
+ response_size = sizeof(READ_COUNTER_RESPONSE);
+ memcpy(tx, READ_COUNTER_REQUEST, request_size);
+ break;
+ case WRITE_COUNTER:
+ request_size = sizeof(WRITE_COUNTER_REQUEST);
+ response_size = sizeof(GENERIC_RESPONSE);
+ memcpy(tx, WRITE_COUNTER_REQUEST, request_size);
+ /* Setup tx packet: bytes 12,13,14,15 hold the */
+ /* u32 counter value (Big Endian) */
+ *((__be32 *)&tx[12]) = cpu_to_be32(*val);
+ break;
+ default:
+ ret = -EINVAL;
+ goto end;
+ }
+
+ ret = usb_bulk_msg(usb,
+ usb_sndbulkpipe(usb,
+ devpriv->ep_tx->bEndpointAddress),
+ devpriv->usb_tx_buf,
+ request_size,
+ NULL,
+ NI6501_TIMEOUT);
+ if (ret)
+ goto end;
+
+ ret = usb_bulk_msg(usb,
+ usb_rcvbulkpipe(usb,
+ devpriv->ep_rx->bEndpointAddress),
+ devpriv->usb_rx_buf,
+ response_size,
+ NULL,
+ NI6501_TIMEOUT);
+ if (ret)
+ goto end;
+
+ /* Check if results are valid */
+
+ if (command == READ_COUNTER) {
+ int i;
+
+ /* Read counter value: bytes 12,13,14,15 of rx packet */
+ /* hold the u32 counter value (Big Endian) */
+ *val = be32_to_cpu(*((__be32 *)&devpriv->usb_rx_buf[12]));
+
+ /* mask counter value for comparing */
+ for (i = 12; i < sizeof(READ_COUNTER_RESPONSE); ++i)
+ devpriv->usb_rx_buf[i] = 0x00;
+
+ if (memcmp(devpriv->usb_rx_buf, READ_COUNTER_RESPONSE,
+ sizeof(READ_COUNTER_RESPONSE))) {
+ ret = -EINVAL;
+ }
+ } else if (memcmp(devpriv->usb_rx_buf, GENERIC_RESPONSE,
+ sizeof(GENERIC_RESPONSE))) {
+ ret = -EINVAL;
+ }
+end:
+ up(&devpriv->sem);
+
+ return ret;
+}
+
+static int ni6501_dio_insn_config(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ int ret;
+ u8 port[3];
+
+ ret = comedi_dio_insn_config(dev, s, insn, data, 0);
+ if (ret)
+ return ret;
+
+ port[0] = (s->io_bits) & 0xff;
+ port[1] = (s->io_bits >> 8) & 0xff;
+ port[2] = (s->io_bits >> 16) & 0xff;
+
+ ret = ni6501_port_command(dev, SET_PORT_DIR, port, NULL);
+ if (ret)
+ return ret;
+
+ return insn->n;
+}
+
+static int ni6501_dio_insn_bits(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ unsigned int mask;
+ int ret;
+ u8 port;
+ u8 bitmap;
+
+ mask = comedi_dio_update_state(s, data);
+
+ for (port = 0; port < 3; port++) {
+ if (mask & (0xFF << port * 8)) {
+ bitmap = (s->state >> port * 8) & 0xFF;
+ ret = ni6501_port_command(dev, WRITE_PORT,
+ &port, &bitmap);
+ if (ret)
+ return ret;
+ }
+ }
+
+ data[1] = 0;
+
+ for (port = 0; port < 3; port++) {
+ ret = ni6501_port_command(dev, READ_PORT, &port, &bitmap);
+ if (ret)
+ return ret;
+ data[1] |= bitmap << port * 8;
+ }
+
+ return insn->n;
+}
+
+static int ni6501_cnt_insn_config(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ int ret;
+ u32 val = 0;
+
+ switch (data[0]) {
+ case INSN_CONFIG_ARM:
+ ret = ni6501_counter_command(dev, START_COUNTER, NULL);
+ break;
+ case INSN_CONFIG_DISARM:
+ ret = ni6501_counter_command(dev, STOP_COUNTER, NULL);
+ break;
+ case INSN_CONFIG_RESET:
+ ret = ni6501_counter_command(dev, STOP_COUNTER, NULL);
+ if (ret)
+ break;
+ ret = ni6501_counter_command(dev, WRITE_COUNTER, &val);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return ret ? ret : insn->n;
+}
+
+static int ni6501_cnt_insn_read(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ int ret;
+ u32 val;
+ unsigned int i;
+
+ for (i = 0; i < insn->n; i++) {
+ ret = ni6501_counter_command(dev, READ_COUNTER, &val);
+ if (ret)
+ return ret;
+ data[i] = val;
+ }
+
+ return insn->n;
+}
+
+static int ni6501_cnt_insn_write(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ int ret;
+
+ if (insn->n) {
+ u32 val = data[insn->n - 1];
+
+ ret = ni6501_counter_command(dev, WRITE_COUNTER, &val);
+ if (ret)
+ return ret;
+ }
+
+ return insn->n;
+}
+
+static int ni6501_alloc_usb_buffers(struct comedi_device *dev)
+{
+ struct ni6501_private *devpriv = dev->private;
+ size_t size;
+
+ size = le16_to_cpu(devpriv->ep_rx->wMaxPacketSize);
+ devpriv->usb_rx_buf = kzalloc(size, GFP_KERNEL);
+ if (!devpriv->usb_rx_buf)
+ return -ENOMEM;
+
+ size = le16_to_cpu(devpriv->ep_tx->wMaxPacketSize);
+ devpriv->usb_tx_buf = kzalloc(size, GFP_KERNEL);
+ if (!devpriv->usb_tx_buf) {
+ kfree(devpriv->usb_rx_buf);
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+static int ni6501_find_endpoints(struct comedi_device *dev)
+{
+ struct usb_interface *intf = comedi_to_usb_interface(dev);
+ struct ni6501_private *devpriv = dev->private;
+ struct usb_host_interface *iface_desc = intf->cur_altsetting;
+ struct usb_endpoint_descriptor *ep_desc;
+ int i;
+
+ if (iface_desc->desc.bNumEndpoints != 2) {
+ dev_err(dev->class_dev, "Wrong number of endpoints\n");
+ return -ENODEV;
+ }
+
+ for (i = 0; i < iface_desc->desc.bNumEndpoints; i++) {
+ ep_desc = &iface_desc->endpoint[i].desc;
+
+ if (usb_endpoint_is_bulk_in(ep_desc)) {
+ if (!devpriv->ep_rx)
+ devpriv->ep_rx = ep_desc;
+ continue;
+ }
+
+ if (usb_endpoint_is_bulk_out(ep_desc)) {
+ if (!devpriv->ep_tx)
+ devpriv->ep_tx = ep_desc;
+ continue;
+ }
+ }
+
+ if (!devpriv->ep_rx || !devpriv->ep_tx)
+ return -ENODEV;
+
+ return 0;
+}
+
+static int ni6501_auto_attach(struct comedi_device *dev,
+ unsigned long context)
+{
+ struct usb_interface *intf = comedi_to_usb_interface(dev);
+ struct ni6501_private *devpriv;
+ struct comedi_subdevice *s;
+ int ret;
+
+ devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
+ if (!devpriv)
+ return -ENOMEM;
+
+ ret = ni6501_find_endpoints(dev);
+ if (ret)
+ return ret;
+
+ ret = ni6501_alloc_usb_buffers(dev);
+ if (ret)
+ return ret;
+
+ sema_init(&devpriv->sem, 1);
+ usb_set_intfdata(intf, devpriv);
+
+ ret = comedi_alloc_subdevices(dev, 2);
+ if (ret)
+ return ret;
+
+ /* Digital Input/Output subdevice */
+ s = &dev->subdevices[0];
+ s->type = COMEDI_SUBD_DIO;
+ s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
+ s->n_chan = 24;
+ s->maxdata = 1;
+ s->range_table = &range_digital;
+ s->insn_bits = ni6501_dio_insn_bits;
+ s->insn_config = ni6501_dio_insn_config;
+
+ /* Counter subdevice */
+ s = &dev->subdevices[1];
+ s->type = COMEDI_SUBD_COUNTER;
+ s->subdev_flags = SDF_READABLE | SDF_WRITABLE | SDF_LSAMPL;
+ s->n_chan = 1;
+ s->maxdata = 0xffffffff;
+ s->insn_read = ni6501_cnt_insn_read;
+ s->insn_write = ni6501_cnt_insn_write;
+ s->insn_config = ni6501_cnt_insn_config;
+
+ return 0;
+}
+
+static void ni6501_detach(struct comedi_device *dev)
+{
+ struct usb_interface *intf = comedi_to_usb_interface(dev);
+ struct ni6501_private *devpriv = dev->private;
+
+ if (!devpriv)
+ return;
+
+ down(&devpriv->sem);
+
+ usb_set_intfdata(intf, NULL);
+
+ kfree(devpriv->usb_rx_buf);
+ kfree(devpriv->usb_tx_buf);
+
+ up(&devpriv->sem);
+}
+
+static struct comedi_driver ni6501_driver = {
+ .module = THIS_MODULE,
+ .driver_name = "ni6501",
+ .auto_attach = ni6501_auto_attach,
+ .detach = ni6501_detach,
+};
+
+static int ni6501_usb_probe(struct usb_interface *intf,
+ const struct usb_device_id *id)
+{
+ return comedi_usb_auto_config(intf, &ni6501_driver, id->driver_info);
+}
+
+static const struct usb_device_id ni6501_usb_table[] = {
+ { USB_DEVICE(0x3923, 0x718a) },
+ { }
+};
+MODULE_DEVICE_TABLE(usb, ni6501_usb_table);
+
+static struct usb_driver ni6501_usb_driver = {
+ .name = "ni6501",
+ .id_table = ni6501_usb_table,
+ .probe = ni6501_usb_probe,
+ .disconnect = comedi_usb_auto_unconfig,
+};
+module_comedi_usb_driver(ni6501_driver, ni6501_usb_driver);
+
+MODULE_AUTHOR("Luca Ellero");
+MODULE_DESCRIPTION("Comedi driver for National Instruments USB-6501");
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/comedi/drivers/pcl711.c b/drivers/staging/comedi/drivers/pcl711.c
new file mode 100644
index 000000000..cfc3a627d
--- /dev/null
+++ b/drivers/staging/comedi/drivers/pcl711.c
@@ -0,0 +1,521 @@
+/*
+ * pcl711.c
+ * Comedi driver for PC-LabCard PCL-711 and AdSys ACL-8112 and compatibles
+ * Copyright (C) 1998 David A. Schleef <ds@schleef.org>
+ * Janne Jalkanen <jalkanen@cs.hut.fi>
+ * Eric Bunn <ebu@cs.hut.fi>
+ *
+ * COMEDI - Linux Control and Measurement Device Interface
+ * Copyright (C) 1998 David A. Schleef <ds@schleef.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+/*
+ * Driver: pcl711
+ * Description: Advantech PCL-711 and 711b, ADLink ACL-8112
+ * Devices: [Advantech] PCL-711 (pcl711), PCL-711B (pcl711b),
+ * [ADLink] ACL-8112HG (acl8112hg), ACL-8112DG (acl8112dg)
+ * Author: David A. Schleef <ds@schleef.org>
+ * Janne Jalkanen <jalkanen@cs.hut.fi>
+ * Eric Bunn <ebu@cs.hut.fi>
+ * Updated:
+ * Status: mostly complete
+ *
+ * Configuration Options:
+ * [0] - I/O port base
+ * [1] - IRQ, optional
+ */
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+
+#include "../comedidev.h"
+
+#include "comedi_8254.h"
+
+/*
+ * I/O port register map
+ */
+#define PCL711_TIMER_BASE 0x00
+#define PCL711_AI_LSB_REG 0x04
+#define PCL711_AI_MSB_REG 0x05
+#define PCL711_AI_MSB_DRDY (1 << 4)
+#define PCL711_AO_LSB_REG(x) (0x04 + ((x) * 2))
+#define PCL711_AO_MSB_REG(x) (0x05 + ((x) * 2))
+#define PCL711_DI_LSB_REG 0x06
+#define PCL711_DI_MSB_REG 0x07
+#define PCL711_INT_STAT_REG 0x08
+#define PCL711_INT_STAT_CLR (0 << 0) /* any value will work */
+#define PCL711_AI_GAIN_REG 0x09
+#define PCL711_AI_GAIN(x) (((x) & 0xf) << 0)
+#define PCL711_MUX_REG 0x0a
+#define PCL711_MUX_CHAN(x) (((x) & 0xf) << 0)
+#define PCL711_MUX_CS0 (1 << 4)
+#define PCL711_MUX_CS1 (1 << 5)
+#define PCL711_MUX_DIFF (PCL711_MUX_CS0 | PCL711_MUX_CS1)
+#define PCL711_MODE_REG 0x0b
+#define PCL711_MODE_DEFAULT (0 << 0)
+#define PCL711_MODE_SOFTTRIG (1 << 0)
+#define PCL711_MODE_EXT (2 << 0)
+#define PCL711_MODE_EXT_IRQ (3 << 0)
+#define PCL711_MODE_PACER (4 << 0)
+#define PCL711_MODE_PACER_IRQ (6 << 0)
+#define PCL711_MODE_IRQ(x) (((x) & 0x7) << 4)
+#define PCL711_SOFTTRIG_REG 0x0c
+#define PCL711_SOFTTRIG (0 << 0) /* any value will work */
+#define PCL711_DO_LSB_REG 0x0d
+#define PCL711_DO_MSB_REG 0x0e
+
+static const struct comedi_lrange range_pcl711b_ai = {
+ 5, {
+ BIP_RANGE(5),
+ BIP_RANGE(2.5),
+ BIP_RANGE(1.25),
+ BIP_RANGE(0.625),
+ BIP_RANGE(0.3125)
+ }
+};
+
+static const struct comedi_lrange range_acl8112hg_ai = {
+ 12, {
+ BIP_RANGE(5),
+ BIP_RANGE(0.5),
+ BIP_RANGE(0.05),
+ BIP_RANGE(0.005),
+ UNI_RANGE(10),
+ UNI_RANGE(1),
+ UNI_RANGE(0.1),
+ UNI_RANGE(0.01),
+ BIP_RANGE(10),
+ BIP_RANGE(1),
+ BIP_RANGE(0.1),
+ BIP_RANGE(0.01)
+ }
+};
+
+static const struct comedi_lrange range_acl8112dg_ai = {
+ 9, {
+ BIP_RANGE(5),
+ BIP_RANGE(2.5),
+ BIP_RANGE(1.25),
+ BIP_RANGE(0.625),
+ UNI_RANGE(10),
+ UNI_RANGE(5),
+ UNI_RANGE(2.5),
+ UNI_RANGE(1.25),
+ BIP_RANGE(10)
+ }
+};
+
+struct pcl711_board {
+ const char *name;
+ int n_aichan;
+ int n_aochan;
+ int maxirq;
+ const struct comedi_lrange *ai_range_type;
+};
+
+static const struct pcl711_board boardtypes[] = {
+ {
+ .name = "pcl711",
+ .n_aichan = 8,
+ .n_aochan = 1,
+ .ai_range_type = &range_bipolar5,
+ }, {
+ .name = "pcl711b",
+ .n_aichan = 8,
+ .n_aochan = 1,
+ .maxirq = 7,
+ .ai_range_type = &range_pcl711b_ai,
+ }, {
+ .name = "acl8112hg",
+ .n_aichan = 16,
+ .n_aochan = 2,
+ .maxirq = 15,
+ .ai_range_type = &range_acl8112hg_ai,
+ }, {
+ .name = "acl8112dg",
+ .n_aichan = 16,
+ .n_aochan = 2,
+ .maxirq = 15,
+ .ai_range_type = &range_acl8112dg_ai,
+ },
+};
+
+static void pcl711_ai_set_mode(struct comedi_device *dev, unsigned int mode)
+{
+ /*
+ * The pcl711b board uses bits in the mode register to select the
+ * interrupt. The other boards supported by this driver all use
+ * jumpers on the board.
+ *
+ * Enables the interrupt when needed on the pcl711b board. These
+ * bits do nothing on the other boards.
+ */
+ if (mode == PCL711_MODE_EXT_IRQ || mode == PCL711_MODE_PACER_IRQ)
+ mode |= PCL711_MODE_IRQ(dev->irq);
+
+ outb(mode, dev->iobase + PCL711_MODE_REG);
+}
+
+static unsigned int pcl711_ai_get_sample(struct comedi_device *dev,
+ struct comedi_subdevice *s)
+{
+ unsigned int val;
+
+ val = inb(dev->iobase + PCL711_AI_MSB_REG) << 8;
+ val |= inb(dev->iobase + PCL711_AI_LSB_REG);
+
+ return val & s->maxdata;
+}
+
+static int pcl711_ai_cancel(struct comedi_device *dev,
+ struct comedi_subdevice *s)
+{
+ outb(PCL711_INT_STAT_CLR, dev->iobase + PCL711_INT_STAT_REG);
+ pcl711_ai_set_mode(dev, PCL711_MODE_SOFTTRIG);
+ return 0;
+}
+
+static irqreturn_t pcl711_interrupt(int irq, void *d)
+{
+ struct comedi_device *dev = d;
+ struct comedi_subdevice *s = dev->read_subdev;
+ struct comedi_cmd *cmd = &s->async->cmd;
+ unsigned int data;
+
+ if (!dev->attached) {
+ dev_err(dev->class_dev, "spurious interrupt\n");
+ return IRQ_HANDLED;
+ }
+
+ data = pcl711_ai_get_sample(dev, s);
+
+ outb(PCL711_INT_STAT_CLR, dev->iobase + PCL711_INT_STAT_REG);
+
+ comedi_buf_write_samples(s, &data, 1);
+
+ if (cmd->stop_src == TRIG_COUNT &&
+ s->async->scans_done >= cmd->stop_arg)
+ s->async->events |= COMEDI_CB_EOA;
+
+ comedi_handle_events(dev, s);
+
+ return IRQ_HANDLED;
+}
+
+static void pcl711_set_changain(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ unsigned int chanspec)
+{
+ unsigned int chan = CR_CHAN(chanspec);
+ unsigned int range = CR_RANGE(chanspec);
+ unsigned int aref = CR_AREF(chanspec);
+ unsigned int mux = 0;
+
+ outb(PCL711_AI_GAIN(range), dev->iobase + PCL711_AI_GAIN_REG);
+
+ if (s->n_chan > 8) {
+ /* Select the correct MPC508A chip */
+ if (aref == AREF_DIFF) {
+ chan &= 0x7;
+ mux |= PCL711_MUX_DIFF;
+ } else {
+ if (chan < 8)
+ mux |= PCL711_MUX_CS0;
+ else
+ mux |= PCL711_MUX_CS1;
+ }
+ }
+ outb(mux | PCL711_MUX_CHAN(chan), dev->iobase + PCL711_MUX_REG);
+}
+
+static int pcl711_ai_eoc(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned long context)
+{
+ unsigned int status;
+
+ status = inb(dev->iobase + PCL711_AI_MSB_REG);
+ if ((status & PCL711_AI_MSB_DRDY) == 0)
+ return 0;
+ return -EBUSY;
+}
+
+static int pcl711_ai_insn_read(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ int ret;
+ int i;
+
+ pcl711_set_changain(dev, s, insn->chanspec);
+
+ pcl711_ai_set_mode(dev, PCL711_MODE_SOFTTRIG);
+
+ for (i = 0; i < insn->n; i++) {
+ outb(PCL711_SOFTTRIG, dev->iobase + PCL711_SOFTTRIG_REG);
+
+ ret = comedi_timeout(dev, s, insn, pcl711_ai_eoc, 0);
+ if (ret)
+ return ret;
+
+ data[i] = pcl711_ai_get_sample(dev, s);
+ }
+
+ return insn->n;
+}
+
+static int pcl711_ai_cmdtest(struct comedi_device *dev,
+ struct comedi_subdevice *s, struct comedi_cmd *cmd)
+{
+ int err = 0;
+
+ /* Step 1 : check if triggers are trivially valid */
+
+ err |= comedi_check_trigger_src(&cmd->start_src, TRIG_NOW);
+ err |= comedi_check_trigger_src(&cmd->scan_begin_src,
+ TRIG_TIMER | TRIG_EXT);
+ err |= comedi_check_trigger_src(&cmd->convert_src, TRIG_NOW);
+ err |= comedi_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
+ err |= comedi_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE);
+
+ if (err)
+ return 1;
+
+ /* Step 2a : make sure trigger sources are unique */
+
+ err |= comedi_check_trigger_is_unique(cmd->scan_begin_src);
+ err |= comedi_check_trigger_is_unique(cmd->stop_src);
+
+ /* Step 2b : and mutually compatible */
+
+ if (err)
+ return 2;
+
+ /* Step 3: check if arguments are trivially valid */
+
+ err |= comedi_check_trigger_arg_is(&cmd->start_arg, 0);
+
+ if (cmd->scan_begin_src == TRIG_EXT) {
+ err |= comedi_check_trigger_arg_is(&cmd->scan_begin_arg, 0);
+ } else {
+#define MAX_SPEED 1000
+ err |= comedi_check_trigger_arg_min(&cmd->scan_begin_arg,
+ MAX_SPEED);
+ }
+
+ err |= comedi_check_trigger_arg_is(&cmd->convert_arg, 0);
+ err |= comedi_check_trigger_arg_is(&cmd->scan_end_arg,
+ cmd->chanlist_len);
+
+ if (cmd->stop_src == TRIG_COUNT)
+ err |= comedi_check_trigger_arg_min(&cmd->stop_arg, 1);
+ else /* TRIG_NONE */
+ err |= comedi_check_trigger_arg_is(&cmd->stop_arg, 0);
+
+ if (err)
+ return 3;
+
+ /* step 4 */
+
+ if (cmd->scan_begin_src == TRIG_TIMER) {
+ unsigned int arg = cmd->scan_begin_arg;
+
+ comedi_8254_cascade_ns_to_timer(dev->pacer, &arg, cmd->flags);
+ err |= comedi_check_trigger_arg_is(&cmd->scan_begin_arg, arg);
+ }
+
+ if (err)
+ return 4;
+
+ return 0;
+}
+
+static int pcl711_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
+{
+ struct comedi_cmd *cmd = &s->async->cmd;
+
+ pcl711_set_changain(dev, s, cmd->chanlist[0]);
+
+ if (cmd->scan_begin_src == TRIG_TIMER) {
+ comedi_8254_update_divisors(dev->pacer);
+ comedi_8254_pacer_enable(dev->pacer, 1, 2, true);
+ outb(PCL711_INT_STAT_CLR, dev->iobase + PCL711_INT_STAT_REG);
+ pcl711_ai_set_mode(dev, PCL711_MODE_PACER_IRQ);
+ } else {
+ pcl711_ai_set_mode(dev, PCL711_MODE_EXT_IRQ);
+ }
+
+ return 0;
+}
+
+static void pcl711_ao_write(struct comedi_device *dev,
+ unsigned int chan, unsigned int val)
+{
+ outb(val & 0xff, dev->iobase + PCL711_AO_LSB_REG(chan));
+ outb((val >> 8) & 0xff, dev->iobase + PCL711_AO_MSB_REG(chan));
+}
+
+static int pcl711_ao_insn_write(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ unsigned int chan = CR_CHAN(insn->chanspec);
+ unsigned int val = s->readback[chan];
+ int i;
+
+ for (i = 0; i < insn->n; i++) {
+ val = data[i];
+ pcl711_ao_write(dev, chan, val);
+ }
+ s->readback[chan] = val;
+
+ return insn->n;
+}
+
+static int pcl711_di_insn_bits(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ unsigned int val;
+
+ val = inb(dev->iobase + PCL711_DI_LSB_REG);
+ val |= (inb(dev->iobase + PCL711_DI_MSB_REG) << 8);
+
+ data[1] = val;
+
+ return insn->n;
+}
+
+static int pcl711_do_insn_bits(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ unsigned int mask;
+
+ mask = comedi_dio_update_state(s, data);
+ if (mask) {
+ if (mask & 0x00ff)
+ outb(s->state & 0xff, dev->iobase + PCL711_DO_LSB_REG);
+ if (mask & 0xff00)
+ outb((s->state >> 8), dev->iobase + PCL711_DO_MSB_REG);
+ }
+
+ data[1] = s->state;
+
+ return insn->n;
+}
+
+static int pcl711_attach(struct comedi_device *dev, struct comedi_devconfig *it)
+{
+ const struct pcl711_board *board = dev->board_ptr;
+ struct comedi_subdevice *s;
+ int ret;
+
+ ret = comedi_request_region(dev, it->options[0], 0x10);
+ if (ret)
+ return ret;
+
+ if (it->options[1] && it->options[1] <= board->maxirq) {
+ ret = request_irq(it->options[1], pcl711_interrupt, 0,
+ dev->board_name, dev);
+ if (ret == 0)
+ dev->irq = it->options[1];
+ }
+
+ dev->pacer = comedi_8254_init(dev->iobase + PCL711_TIMER_BASE,
+ I8254_OSC_BASE_2MHZ, I8254_IO8, 0);
+ if (!dev->pacer)
+ return -ENOMEM;
+
+ ret = comedi_alloc_subdevices(dev, 4);
+ if (ret)
+ return ret;
+
+ /* Analog Input subdevice */
+ s = &dev->subdevices[0];
+ s->type = COMEDI_SUBD_AI;
+ s->subdev_flags = SDF_READABLE | SDF_GROUND;
+ if (board->n_aichan > 8)
+ s->subdev_flags |= SDF_DIFF;
+ s->n_chan = board->n_aichan;
+ s->maxdata = 0xfff;
+ s->range_table = board->ai_range_type;
+ s->insn_read = pcl711_ai_insn_read;
+ if (dev->irq) {
+ dev->read_subdev = s;
+ s->subdev_flags |= SDF_CMD_READ;
+ s->len_chanlist = 1;
+ s->do_cmdtest = pcl711_ai_cmdtest;
+ s->do_cmd = pcl711_ai_cmd;
+ s->cancel = pcl711_ai_cancel;
+ }
+
+ /* Analog Output subdevice */
+ s = &dev->subdevices[1];
+ s->type = COMEDI_SUBD_AO;
+ s->subdev_flags = SDF_WRITABLE;
+ s->n_chan = board->n_aochan;
+ s->maxdata = 0xfff;
+ s->range_table = &range_bipolar5;
+ s->insn_write = pcl711_ao_insn_write;
+
+ ret = comedi_alloc_subdev_readback(s);
+ if (ret)
+ return ret;
+
+ /* Digital Input subdevice */
+ s = &dev->subdevices[2];
+ s->type = COMEDI_SUBD_DI;
+ s->subdev_flags = SDF_READABLE;
+ s->n_chan = 16;
+ s->maxdata = 1;
+ s->range_table = &range_digital;
+ s->insn_bits = pcl711_di_insn_bits;
+
+ /* Digital Output subdevice */
+ s = &dev->subdevices[3];
+ s->type = COMEDI_SUBD_DO;
+ s->subdev_flags = SDF_WRITABLE;
+ s->n_chan = 16;
+ s->maxdata = 1;
+ s->range_table = &range_digital;
+ s->insn_bits = pcl711_do_insn_bits;
+
+ /* clear DAC */
+ pcl711_ao_write(dev, 0, 0x0);
+ pcl711_ao_write(dev, 1, 0x0);
+
+ return 0;
+}
+
+static struct comedi_driver pcl711_driver = {
+ .driver_name = "pcl711",
+ .module = THIS_MODULE,
+ .attach = pcl711_attach,
+ .detach = comedi_legacy_detach,
+ .board_name = &boardtypes[0].name,
+ .num_names = ARRAY_SIZE(boardtypes),
+ .offset = sizeof(struct pcl711_board),
+};
+module_comedi_driver(pcl711_driver);
+
+MODULE_AUTHOR("Comedi http://www.comedi.org");
+MODULE_DESCRIPTION("Comedi driver for PCL-711 compatible boards");
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/comedi/drivers/pcl724.c b/drivers/staging/comedi/drivers/pcl724.c
new file mode 100644
index 000000000..74b07e174
--- /dev/null
+++ b/drivers/staging/comedi/drivers/pcl724.c
@@ -0,0 +1,152 @@
+/*
+ * pcl724.c
+ * Comedi driver for 8255 based ISA and PC/104 DIO boards
+ *
+ * Michal Dobes <dobes@tesnet.cz>
+ */
+
+/*
+ * Driver: pcl724
+ * Description: Comedi driver for 8255 based ISA DIO boards
+ * Devices: [Advantech] PCL-724 (pcl724), PCL-722 (pcl722), PCL-731 (pcl731),
+ * [ADLink] ACL-7122 (acl7122), ACL-7124 (acl7124), PET-48DIO (pet48dio),
+ * [WinSystems] PCM-IO48 (pcmio48),
+ * [Diamond Systems] ONYX-MM-DIO (onyx-mm-dio)
+ * Author: Michal Dobes <dobes@tesnet.cz>
+ * Status: untested
+ *
+ * Configuration options:
+ * [0] - IO Base
+ * [1] - IRQ (not supported)
+ * [2] - number of DIO (pcl722 and acl7122 boards)
+ * 0, 144: 144 DIO configuration
+ * 1, 96: 96 DIO configuration
+ */
+
+#include <linux/module.h>
+#include "../comedidev.h"
+
+#include "8255.h"
+
+struct pcl724_board {
+ const char *name;
+ unsigned int io_range;
+ unsigned int can_have96:1;
+ unsigned int is_pet48:1;
+ int numofports;
+};
+
+static const struct pcl724_board boardtypes[] = {
+ {
+ .name = "pcl724",
+ .io_range = 0x04,
+ .numofports = 1, /* 24 DIO channels */
+ }, {
+ .name = "pcl722",
+ .io_range = 0x20,
+ .can_have96 = 1,
+ .numofports = 6, /* 144 (or 96) DIO channels */
+ }, {
+ .name = "pcl731",
+ .io_range = 0x08,
+ .numofports = 2, /* 48 DIO channels */
+ }, {
+ .name = "acl7122",
+ .io_range = 0x20,
+ .can_have96 = 1,
+ .numofports = 6, /* 144 (or 96) DIO channels */
+ }, {
+ .name = "acl7124",
+ .io_range = 0x04,
+ .numofports = 1, /* 24 DIO channels */
+ }, {
+ .name = "pet48dio",
+ .io_range = 0x02,
+ .is_pet48 = 1,
+ .numofports = 2, /* 48 DIO channels */
+ }, {
+ .name = "pcmio48",
+ .io_range = 0x08,
+ .numofports = 2, /* 48 DIO channels */
+ }, {
+ .name = "onyx-mm-dio",
+ .io_range = 0x10,
+ .numofports = 2, /* 48 DIO channels */
+ },
+};
+
+static int pcl724_8255mapped_io(struct comedi_device *dev,
+ int dir, int port, int data,
+ unsigned long iobase)
+{
+ int movport = I8255_SIZE * (iobase >> 12);
+
+ iobase &= 0x0fff;
+
+ outb(port + movport, iobase);
+ if (dir) {
+ outb(data, iobase + 1);
+ return 0;
+ }
+ return inb(iobase + 1);
+}
+
+static int pcl724_attach(struct comedi_device *dev,
+ struct comedi_devconfig *it)
+{
+ const struct pcl724_board *board = dev->board_ptr;
+ struct comedi_subdevice *s;
+ unsigned long iobase;
+ unsigned int iorange;
+ int n_subdevices;
+ int ret;
+ int i;
+
+ iorange = board->io_range;
+ n_subdevices = board->numofports;
+
+ /* Handle PCL-724 in 96 DIO configuration */
+ if (board->can_have96 &&
+ (it->options[2] == 1 || it->options[2] == 96)) {
+ iorange = 0x10;
+ n_subdevices = 4;
+ }
+
+ ret = comedi_request_region(dev, it->options[0], iorange);
+ if (ret)
+ return ret;
+
+ ret = comedi_alloc_subdevices(dev, n_subdevices);
+ if (ret)
+ return ret;
+
+ for (i = 0; i < dev->n_subdevices; i++) {
+ s = &dev->subdevices[i];
+ if (board->is_pet48) {
+ iobase = dev->iobase + (i * 0x1000);
+ ret = subdev_8255_init(dev, s, pcl724_8255mapped_io,
+ iobase);
+ } else {
+ ret = subdev_8255_init(dev, s, NULL, i * I8255_SIZE);
+ }
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static struct comedi_driver pcl724_driver = {
+ .driver_name = "pcl724",
+ .module = THIS_MODULE,
+ .attach = pcl724_attach,
+ .detach = comedi_legacy_detach,
+ .board_name = &boardtypes[0].name,
+ .num_names = ARRAY_SIZE(boardtypes),
+ .offset = sizeof(struct pcl724_board),
+};
+module_comedi_driver(pcl724_driver);
+
+MODULE_AUTHOR("Comedi http://www.comedi.org");
+MODULE_DESCRIPTION("Comedi driver for 8255 based ISA and PC/104 DIO boards");
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/comedi/drivers/pcl726.c b/drivers/staging/comedi/drivers/pcl726.c
new file mode 100644
index 000000000..256850ccb
--- /dev/null
+++ b/drivers/staging/comedi/drivers/pcl726.c
@@ -0,0 +1,432 @@
+/*
+ * pcl726.c
+ * Comedi driver for 6/12-Channel D/A Output and DIO cards
+ *
+ * COMEDI - Linux Control and Measurement Device Interface
+ * Copyright (C) 1998 David A. Schleef <ds@schleef.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+/*
+ * Driver: pcl726
+ * Description: Advantech PCL-726 & compatibles
+ * Author: David A. Schleef <ds@schleef.org>
+ * Status: untested
+ * Devices: [Advantech] PCL-726 (pcl726), PCL-727 (pcl727), PCL-728 (pcl728),
+ * [ADLink] ACL-6126 (acl6126), ACL-6128 (acl6128)
+ *
+ * Configuration Options:
+ * [0] - IO Base
+ * [1] - IRQ (ACL-6126 only)
+ * [2] - D/A output range for channel 0
+ * [3] - D/A output range for channel 1
+ *
+ * Boards with > 2 analog output channels:
+ * [4] - D/A output range for channel 2
+ * [5] - D/A output range for channel 3
+ * [6] - D/A output range for channel 4
+ * [7] - D/A output range for channel 5
+ *
+ * Boards with > 6 analog output channels:
+ * [8] - D/A output range for channel 6
+ * [9] - D/A output range for channel 7
+ * [10] - D/A output range for channel 8
+ * [11] - D/A output range for channel 9
+ * [12] - D/A output range for channel 10
+ * [13] - D/A output range for channel 11
+ *
+ * For PCL-726 the D/A output ranges are:
+ * 0: 0-5V, 1: 0-10V, 2: +/-5V, 3: +/-10V, 4: 4-20mA, 5: unknown
+ *
+ * For PCL-727:
+ * 0: 0-5V, 1: 0-10V, 2: +/-5V, 3: 4-20mA
+ *
+ * For PCL-728 and ACL-6128:
+ * 0: 0-5V, 1: 0-10V, 2: +/-5V, 3: +/-10V, 4: 4-20mA, 5: 0-20mA
+ *
+ * For ACL-6126:
+ * 0: 0-5V, 1: 0-10V, 2: +/-5V, 3: +/-10V, 4: 4-20mA
+ */
+
+#include <linux/module.h>
+#include <linux/interrupt.h>
+
+#include "../comedidev.h"
+
+#define PCL726_AO_MSB_REG(x) (0x00 + ((x) * 2))
+#define PCL726_AO_LSB_REG(x) (0x01 + ((x) * 2))
+#define PCL726_DO_MSB_REG 0x0c
+#define PCL726_DO_LSB_REG 0x0d
+#define PCL726_DI_MSB_REG 0x0e
+#define PCL726_DI_LSB_REG 0x0f
+
+#define PCL727_DI_MSB_REG 0x00
+#define PCL727_DI_LSB_REG 0x01
+#define PCL727_DO_MSB_REG 0x18
+#define PCL727_DO_LSB_REG 0x19
+
+static const struct comedi_lrange *const rangelist_726[] = {
+ &range_unipolar5,
+ &range_unipolar10,
+ &range_bipolar5,
+ &range_bipolar10,
+ &range_4_20mA,
+ &range_unknown
+};
+
+static const struct comedi_lrange *const rangelist_727[] = {
+ &range_unipolar5,
+ &range_unipolar10,
+ &range_bipolar5,
+ &range_4_20mA
+};
+
+static const struct comedi_lrange *const rangelist_728[] = {
+ &range_unipolar5,
+ &range_unipolar10,
+ &range_bipolar5,
+ &range_bipolar10,
+ &range_4_20mA,
+ &range_0_20mA
+};
+
+struct pcl726_board {
+ const char *name;
+ unsigned long io_len;
+ unsigned int irq_mask;
+ const struct comedi_lrange *const *ao_ranges;
+ int ao_num_ranges;
+ int ao_nchan;
+ unsigned int have_dio:1;
+ unsigned int is_pcl727:1;
+};
+
+static const struct pcl726_board pcl726_boards[] = {
+ {
+ .name = "pcl726",
+ .io_len = 0x10,
+ .ao_ranges = &rangelist_726[0],
+ .ao_num_ranges = ARRAY_SIZE(rangelist_726),
+ .ao_nchan = 6,
+ .have_dio = 1,
+ }, {
+ .name = "pcl727",
+ .io_len = 0x20,
+ .ao_ranges = &rangelist_727[0],
+ .ao_num_ranges = ARRAY_SIZE(rangelist_727),
+ .ao_nchan = 12,
+ .have_dio = 1,
+ .is_pcl727 = 1,
+ }, {
+ .name = "pcl728",
+ .io_len = 0x08,
+ .ao_num_ranges = ARRAY_SIZE(rangelist_728),
+ .ao_ranges = &rangelist_728[0],
+ .ao_nchan = 2,
+ }, {
+ .name = "acl6126",
+ .io_len = 0x10,
+ .irq_mask = 0x96e8,
+ .ao_num_ranges = ARRAY_SIZE(rangelist_726),
+ .ao_ranges = &rangelist_726[0],
+ .ao_nchan = 6,
+ .have_dio = 1,
+ }, {
+ .name = "acl6128",
+ .io_len = 0x08,
+ .ao_num_ranges = ARRAY_SIZE(rangelist_728),
+ .ao_ranges = &rangelist_728[0],
+ .ao_nchan = 2,
+ },
+};
+
+struct pcl726_private {
+ const struct comedi_lrange *rangelist[12];
+ unsigned int cmd_running:1;
+};
+
+static int pcl726_intr_insn_bits(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ data[1] = 0;
+ return insn->n;
+}
+
+static int pcl726_intr_cmdtest(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_cmd *cmd)
+{
+ int err = 0;
+
+ /* Step 1 : check if triggers are trivially valid */
+
+ err |= comedi_check_trigger_src(&cmd->start_src, TRIG_NOW);
+ err |= comedi_check_trigger_src(&cmd->scan_begin_src, TRIG_EXT);
+ err |= comedi_check_trigger_src(&cmd->convert_src, TRIG_FOLLOW);
+ err |= comedi_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
+ err |= comedi_check_trigger_src(&cmd->stop_src, TRIG_NONE);
+
+ if (err)
+ return 1;
+
+ /* Step 2a : make sure trigger sources are unique */
+ /* Step 2b : and mutually compatible */
+
+ /* Step 3: check if arguments are trivially valid */
+
+ err |= comedi_check_trigger_arg_is(&cmd->start_arg, 0);
+ err |= comedi_check_trigger_arg_is(&cmd->scan_begin_arg, 0);
+ err |= comedi_check_trigger_arg_is(&cmd->convert_arg, 0);
+ err |= comedi_check_trigger_arg_is(&cmd->scan_end_arg,
+ cmd->chanlist_len);
+ err |= comedi_check_trigger_arg_is(&cmd->stop_arg, 0);
+
+ if (err)
+ return 3;
+
+ /* Step 4: fix up any arguments */
+
+ /* Step 5: check channel list if it exists */
+
+ return 0;
+}
+
+static int pcl726_intr_cmd(struct comedi_device *dev,
+ struct comedi_subdevice *s)
+{
+ struct pcl726_private *devpriv = dev->private;
+
+ devpriv->cmd_running = 1;
+
+ return 0;
+}
+
+static int pcl726_intr_cancel(struct comedi_device *dev,
+ struct comedi_subdevice *s)
+{
+ struct pcl726_private *devpriv = dev->private;
+
+ devpriv->cmd_running = 0;
+
+ return 0;
+}
+
+static irqreturn_t pcl726_interrupt(int irq, void *d)
+{
+ struct comedi_device *dev = d;
+ struct comedi_subdevice *s = dev->read_subdev;
+ struct pcl726_private *devpriv = dev->private;
+
+ if (devpriv->cmd_running) {
+ pcl726_intr_cancel(dev, s);
+
+ comedi_buf_write_samples(s, &s->state, 1);
+ comedi_handle_events(dev, s);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static int pcl726_ao_insn_write(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ unsigned int chan = CR_CHAN(insn->chanspec);
+ unsigned int range = CR_RANGE(insn->chanspec);
+ int i;
+
+ for (i = 0; i < insn->n; i++) {
+ unsigned int val = data[i];
+
+ s->readback[chan] = val;
+
+ /* bipolar data to the DAC is two's complement */
+ if (comedi_chan_range_is_bipolar(s, chan, range))
+ val = comedi_offset_munge(s, val);
+
+ /* order is important, MSB then LSB */
+ outb((val >> 8) & 0xff, dev->iobase + PCL726_AO_MSB_REG(chan));
+ outb(val & 0xff, dev->iobase + PCL726_AO_LSB_REG(chan));
+ }
+
+ return insn->n;
+}
+
+static int pcl726_di_insn_bits(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ const struct pcl726_board *board = dev->board_ptr;
+ unsigned int val;
+
+ if (board->is_pcl727) {
+ val = inb(dev->iobase + PCL727_DI_LSB_REG);
+ val |= (inb(dev->iobase + PCL727_DI_MSB_REG) << 8);
+ } else {
+ val = inb(dev->iobase + PCL726_DI_LSB_REG);
+ val |= (inb(dev->iobase + PCL726_DI_MSB_REG) << 8);
+ }
+
+ data[1] = val;
+
+ return insn->n;
+}
+
+static int pcl726_do_insn_bits(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ const struct pcl726_board *board = dev->board_ptr;
+ unsigned long io = dev->iobase;
+ unsigned int mask;
+
+ mask = comedi_dio_update_state(s, data);
+ if (mask) {
+ if (board->is_pcl727) {
+ if (mask & 0x00ff)
+ outb(s->state & 0xff, io + PCL727_DO_LSB_REG);
+ if (mask & 0xff00)
+ outb((s->state >> 8), io + PCL727_DO_MSB_REG);
+ } else {
+ if (mask & 0x00ff)
+ outb(s->state & 0xff, io + PCL726_DO_LSB_REG);
+ if (mask & 0xff00)
+ outb((s->state >> 8), io + PCL726_DO_MSB_REG);
+ }
+ }
+
+ data[1] = s->state;
+
+ return insn->n;
+}
+
+static int pcl726_attach(struct comedi_device *dev,
+ struct comedi_devconfig *it)
+{
+ const struct pcl726_board *board = dev->board_ptr;
+ struct pcl726_private *devpriv;
+ struct comedi_subdevice *s;
+ int subdev;
+ int ret;
+ int i;
+
+ ret = comedi_request_region(dev, it->options[0], board->io_len);
+ if (ret)
+ return ret;
+
+ devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
+ if (!devpriv)
+ return -ENOMEM;
+
+ /*
+ * Hook up the external trigger source interrupt only if the
+ * user config option is valid and the board supports interrupts.
+ */
+ if (it->options[1] && (board->irq_mask & (1 << it->options[1]))) {
+ ret = request_irq(it->options[1], pcl726_interrupt, 0,
+ dev->board_name, dev);
+ if (ret == 0) {
+ /* External trigger source is from Pin-17 of CN3 */
+ dev->irq = it->options[1];
+ }
+ }
+
+ /* setup the per-channel analog output range_table_list */
+ for (i = 0; i < 12; i++) {
+ unsigned int opt = it->options[2 + i];
+
+ if (opt < board->ao_num_ranges && i < board->ao_nchan)
+ devpriv->rangelist[i] = board->ao_ranges[opt];
+ else
+ devpriv->rangelist[i] = &range_unknown;
+ }
+
+ subdev = board->have_dio ? 3 : 1;
+ if (dev->irq)
+ subdev++;
+ ret = comedi_alloc_subdevices(dev, subdev);
+ if (ret)
+ return ret;
+
+ subdev = 0;
+
+ /* Analog Output subdevice */
+ s = &dev->subdevices[subdev++];
+ s->type = COMEDI_SUBD_AO;
+ s->subdev_flags = SDF_WRITABLE | SDF_GROUND;
+ s->n_chan = board->ao_nchan;
+ s->maxdata = 0x0fff;
+ s->range_table_list = devpriv->rangelist;
+ s->insn_write = pcl726_ao_insn_write;
+
+ ret = comedi_alloc_subdev_readback(s);
+ if (ret)
+ return ret;
+
+ if (board->have_dio) {
+ /* Digital Input subdevice */
+ s = &dev->subdevices[subdev++];
+ s->type = COMEDI_SUBD_DI;
+ s->subdev_flags = SDF_READABLE;
+ s->n_chan = 16;
+ s->maxdata = 1;
+ s->insn_bits = pcl726_di_insn_bits;
+ s->range_table = &range_digital;
+
+ /* Digital Output subdevice */
+ s = &dev->subdevices[subdev++];
+ s->type = COMEDI_SUBD_DO;
+ s->subdev_flags = SDF_WRITABLE;
+ s->n_chan = 16;
+ s->maxdata = 1;
+ s->insn_bits = pcl726_do_insn_bits;
+ s->range_table = &range_digital;
+ }
+
+ if (dev->irq) {
+ /* Digial Input subdevice - Interrupt support */
+ s = &dev->subdevices[subdev++];
+ dev->read_subdev = s;
+ s->type = COMEDI_SUBD_DI;
+ s->subdev_flags = SDF_READABLE | SDF_CMD_READ;
+ s->n_chan = 1;
+ s->maxdata = 1;
+ s->range_table = &range_digital;
+ s->insn_bits = pcl726_intr_insn_bits;
+ s->len_chanlist = 1;
+ s->do_cmdtest = pcl726_intr_cmdtest;
+ s->do_cmd = pcl726_intr_cmd;
+ s->cancel = pcl726_intr_cancel;
+ }
+
+ return 0;
+}
+
+static struct comedi_driver pcl726_driver = {
+ .driver_name = "pcl726",
+ .module = THIS_MODULE,
+ .attach = pcl726_attach,
+ .detach = comedi_legacy_detach,
+ .board_name = &pcl726_boards[0].name,
+ .num_names = ARRAY_SIZE(pcl726_boards),
+ .offset = sizeof(struct pcl726_board),
+};
+module_comedi_driver(pcl726_driver);
+
+MODULE_AUTHOR("Comedi http://www.comedi.org");
+MODULE_DESCRIPTION("Comedi driver for Advantech PCL-726 & compatibles");
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/comedi/drivers/pcl730.c b/drivers/staging/comedi/drivers/pcl730.c
new file mode 100644
index 000000000..ce958eef2
--- /dev/null
+++ b/drivers/staging/comedi/drivers/pcl730.c
@@ -0,0 +1,349 @@
+/*
+ * comedi/drivers/pcl730.c
+ * Driver for Advantech PCL-730 and clones
+ * JosĂ© Luis SĂ¡nchez
+ */
+
+/*
+ * Driver: pcl730
+ * Description: Advantech PCL-730 (& compatibles)
+ * Devices: [Advantech] PCL-730 (pcl730), PCM-3730 (pcm3730), PCL-725 (pcl725),
+ * PCL-733 (pcl733), PCL-734 (pcl734),
+ * [ADLink] ACL-7130 (acl7130), ACL-7225b (acl7225b),
+ * [ICP] ISO-730 (iso730), P8R8-DIO (p8r8dio), P16R16-DIO (p16r16dio),
+ * [Diamond Systems] OPMM-1616-XT (opmm-1616-xt), PEARL-MM-P (pearl-mm-p),
+ * IR104-PBF (ir104-pbf),
+ * Author: JosĂ© Luis SĂ¡nchez (jsanchezv@teleline.es)
+ * Status: untested
+ *
+ * Configuration options:
+ * [0] - I/O port base
+ *
+ * Interrupts are not supported.
+ * The ACL-7130 card has an 8254 timer/counter not supported by this driver.
+ */
+
+#include <linux/module.h>
+#include "../comedidev.h"
+
+/*
+ * Register map
+ *
+ * The register map varies slightly depending on the board type but
+ * all registers are 8-bit.
+ *
+ * The boardinfo 'io_range' is used to allow comedi to request the
+ * proper range required by the board.
+ *
+ * The comedi_subdevice 'private' data is used to pass the register
+ * offset to the (*insn_bits) functions to read/write the correct
+ * registers.
+ *
+ * The basic register mapping looks like this:
+ *
+ * BASE+0 Isolated outputs 0-7 (write) / inputs 0-7 (read)
+ * BASE+1 Isolated outputs 8-15 (write) / inputs 8-15 (read)
+ * BASE+2 TTL outputs 0-7 (write) / inputs 0-7 (read)
+ * BASE+3 TTL outputs 8-15 (write) / inputs 8-15 (read)
+ *
+ * The pcm3730 board does not have register BASE+1.
+ *
+ * The pcl725 and p8r8dio only have registers BASE+0 and BASE+1:
+ *
+ * BASE+0 Isolated outputs 0-7 (write) (read back on p8r8dio)
+ * BASE+1 Isolated inputs 0-7 (read)
+ *
+ * The acl7225b and p16r16dio boards have this register mapping:
+ *
+ * BASE+0 Isolated outputs 0-7 (write) (read back)
+ * BASE+1 Isolated outputs 8-15 (write) (read back)
+ * BASE+2 Isolated inputs 0-7 (read)
+ * BASE+3 Isolated inputs 8-15 (read)
+ *
+ * The pcl733 and pcl733 boards have this register mapping:
+ *
+ * BASE+0 Isolated outputs 0-7 (write) or inputs 0-7 (read)
+ * BASE+1 Isolated outputs 8-15 (write) or inputs 8-15 (read)
+ * BASE+2 Isolated outputs 16-23 (write) or inputs 16-23 (read)
+ * BASE+3 Isolated outputs 24-31 (write) or inputs 24-31 (read)
+ *
+ * The opmm-1616-xt board has this register mapping:
+ *
+ * BASE+0 Isolated outputs 0-7 (write) (read back)
+ * BASE+1 Isolated outputs 8-15 (write) (read back)
+ * BASE+2 Isolated inputs 0-7 (read)
+ * BASE+3 Isolated inputs 8-15 (read)
+ *
+ * These registers are not currently supported:
+ *
+ * BASE+2 Relay select register (write)
+ * BASE+3 Board reset control register (write)
+ * BASE+4 Interrupt control register (write)
+ * BASE+4 Change detect 7-0 status register (read)
+ * BASE+5 LED control register (write)
+ * BASE+5 Change detect 15-8 status register (read)
+ *
+ * The pearl-mm-p board has this register mapping:
+ *
+ * BASE+0 Isolated outputs 0-7 (write)
+ * BASE+1 Isolated outputs 8-15 (write)
+ *
+ * The ir104-pbf board has this register mapping:
+ *
+ * BASE+0 Isolated outputs 0-7 (write) (read back)
+ * BASE+1 Isolated outputs 8-15 (write) (read back)
+ * BASE+2 Isolated outputs 16-19 (write) (read back)
+ * BASE+4 Isolated inputs 0-7 (read)
+ * BASE+5 Isolated inputs 8-15 (read)
+ * BASE+6 Isolated inputs 16-19 (read)
+ */
+
+struct pcl730_board {
+ const char *name;
+ unsigned int io_range;
+ unsigned is_pcl725:1;
+ unsigned is_acl7225b:1;
+ unsigned is_ir104:1;
+ unsigned has_readback:1;
+ unsigned has_ttl_io:1;
+ int n_subdevs;
+ int n_iso_out_chan;
+ int n_iso_in_chan;
+ int n_ttl_chan;
+};
+
+static const struct pcl730_board pcl730_boards[] = {
+ {
+ .name = "pcl730",
+ .io_range = 0x04,
+ .has_ttl_io = 1,
+ .n_subdevs = 4,
+ .n_iso_out_chan = 16,
+ .n_iso_in_chan = 16,
+ .n_ttl_chan = 16,
+ }, {
+ .name = "iso730",
+ .io_range = 0x04,
+ .n_subdevs = 4,
+ .n_iso_out_chan = 16,
+ .n_iso_in_chan = 16,
+ .n_ttl_chan = 16,
+ }, {
+ .name = "acl7130",
+ .io_range = 0x08,
+ .has_ttl_io = 1,
+ .n_subdevs = 4,
+ .n_iso_out_chan = 16,
+ .n_iso_in_chan = 16,
+ .n_ttl_chan = 16,
+ }, {
+ .name = "pcm3730",
+ .io_range = 0x04,
+ .has_ttl_io = 1,
+ .n_subdevs = 4,
+ .n_iso_out_chan = 8,
+ .n_iso_in_chan = 8,
+ .n_ttl_chan = 16,
+ }, {
+ .name = "pcl725",
+ .io_range = 0x02,
+ .is_pcl725 = 1,
+ .n_subdevs = 2,
+ .n_iso_out_chan = 8,
+ .n_iso_in_chan = 8,
+ }, {
+ .name = "p8r8dio",
+ .io_range = 0x02,
+ .is_pcl725 = 1,
+ .has_readback = 1,
+ .n_subdevs = 2,
+ .n_iso_out_chan = 8,
+ .n_iso_in_chan = 8,
+ }, {
+ .name = "acl7225b",
+ .io_range = 0x08, /* only 4 are used */
+ .is_acl7225b = 1,
+ .has_readback = 1,
+ .n_subdevs = 2,
+ .n_iso_out_chan = 16,
+ .n_iso_in_chan = 16,
+ }, {
+ .name = "p16r16dio",
+ .io_range = 0x04,
+ .is_acl7225b = 1,
+ .has_readback = 1,
+ .n_subdevs = 2,
+ .n_iso_out_chan = 16,
+ .n_iso_in_chan = 16,
+ }, {
+ .name = "pcl733",
+ .io_range = 0x04,
+ .n_subdevs = 1,
+ .n_iso_in_chan = 32,
+ }, {
+ .name = "pcl734",
+ .io_range = 0x04,
+ .n_subdevs = 1,
+ .n_iso_out_chan = 32,
+ }, {
+ .name = "opmm-1616-xt",
+ .io_range = 0x10,
+ .is_acl7225b = 1,
+ .has_readback = 1,
+ .n_subdevs = 2,
+ .n_iso_out_chan = 16,
+ .n_iso_in_chan = 16,
+ }, {
+ .name = "pearl-mm-p",
+ .io_range = 0x02,
+ .n_subdevs = 1,
+ .n_iso_out_chan = 16,
+ }, {
+ .name = "ir104-pbf",
+ .io_range = 0x08,
+ .is_ir104 = 1,
+ .has_readback = 1,
+ .n_iso_out_chan = 20,
+ .n_iso_in_chan = 20,
+ },
+};
+
+static int pcl730_do_insn_bits(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ unsigned long reg = (unsigned long)s->private;
+ unsigned int mask;
+
+ mask = comedi_dio_update_state(s, data);
+ if (mask) {
+ if (mask & 0x00ff)
+ outb(s->state & 0xff, dev->iobase + reg);
+ if ((mask & 0xff00) && (s->n_chan > 8))
+ outb((s->state >> 8) & 0xff, dev->iobase + reg + 1);
+ if ((mask & 0xff0000) && (s->n_chan > 16))
+ outb((s->state >> 16) & 0xff, dev->iobase + reg + 2);
+ if ((mask & 0xff000000) && (s->n_chan > 24))
+ outb((s->state >> 24) & 0xff, dev->iobase + reg + 3);
+ }
+
+ data[1] = s->state;
+
+ return insn->n;
+}
+
+static unsigned int pcl730_get_bits(struct comedi_device *dev,
+ struct comedi_subdevice *s)
+{
+ unsigned long reg = (unsigned long)s->private;
+ unsigned int val;
+
+ val = inb(dev->iobase + reg);
+ if (s->n_chan > 8)
+ val |= (inb(dev->iobase + reg + 1) << 8);
+ if (s->n_chan > 16)
+ val |= (inb(dev->iobase + reg + 2) << 16);
+ if (s->n_chan > 24)
+ val |= (inb(dev->iobase + reg + 3) << 24);
+
+ return val;
+}
+
+static int pcl730_di_insn_bits(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ data[1] = pcl730_get_bits(dev, s);
+
+ return insn->n;
+}
+
+static int pcl730_attach(struct comedi_device *dev,
+ struct comedi_devconfig *it)
+{
+ const struct pcl730_board *board = dev->board_ptr;
+ struct comedi_subdevice *s;
+ int subdev;
+ int ret;
+
+ ret = comedi_request_region(dev, it->options[0], board->io_range);
+ if (ret)
+ return ret;
+
+ ret = comedi_alloc_subdevices(dev, board->n_subdevs);
+ if (ret)
+ return ret;
+
+ subdev = 0;
+
+ if (board->n_iso_out_chan) {
+ /* Isolated Digital Outputs */
+ s = &dev->subdevices[subdev++];
+ s->type = COMEDI_SUBD_DO;
+ s->subdev_flags = SDF_WRITABLE;
+ s->n_chan = board->n_iso_out_chan;
+ s->maxdata = 1;
+ s->range_table = &range_digital;
+ s->insn_bits = pcl730_do_insn_bits;
+ s->private = (void *)0;
+
+ /* get the initial state if supported */
+ if (board->has_readback)
+ s->state = pcl730_get_bits(dev, s);
+ }
+
+ if (board->n_iso_in_chan) {
+ /* Isolated Digital Inputs */
+ s = &dev->subdevices[subdev++];
+ s->type = COMEDI_SUBD_DI;
+ s->subdev_flags = SDF_READABLE;
+ s->n_chan = board->n_iso_in_chan;
+ s->maxdata = 1;
+ s->range_table = &range_digital;
+ s->insn_bits = pcl730_di_insn_bits;
+ s->private = board->is_ir104 ? (void *)4 :
+ board->is_acl7225b ? (void *)2 :
+ board->is_pcl725 ? (void *)1 : (void *)0;
+ }
+
+ if (board->has_ttl_io) {
+ /* TTL Digital Outputs */
+ s = &dev->subdevices[subdev++];
+ s->type = COMEDI_SUBD_DO;
+ s->subdev_flags = SDF_WRITABLE;
+ s->n_chan = board->n_ttl_chan;
+ s->maxdata = 1;
+ s->range_table = &range_digital;
+ s->insn_bits = pcl730_do_insn_bits;
+ s->private = (void *)2;
+
+ /* TTL Digital Inputs */
+ s = &dev->subdevices[subdev++];
+ s->type = COMEDI_SUBD_DI;
+ s->subdev_flags = SDF_READABLE;
+ s->n_chan = board->n_ttl_chan;
+ s->maxdata = 1;
+ s->range_table = &range_digital;
+ s->insn_bits = pcl730_di_insn_bits;
+ s->private = (void *)2;
+ }
+
+ return 0;
+}
+
+static struct comedi_driver pcl730_driver = {
+ .driver_name = "pcl730",
+ .module = THIS_MODULE,
+ .attach = pcl730_attach,
+ .detach = comedi_legacy_detach,
+ .board_name = &pcl730_boards[0].name,
+ .num_names = ARRAY_SIZE(pcl730_boards),
+ .offset = sizeof(struct pcl730_board),
+};
+module_comedi_driver(pcl730_driver);
+
+MODULE_AUTHOR("Comedi http://www.comedi.org");
+MODULE_DESCRIPTION("Comedi low-level driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/comedi/drivers/pcl812.c b/drivers/staging/comedi/drivers/pcl812.c
new file mode 100644
index 000000000..03a3fd6cd
--- /dev/null
+++ b/drivers/staging/comedi/drivers/pcl812.c
@@ -0,0 +1,1317 @@
+/*
+ * comedi/drivers/pcl812.c
+ *
+ * Author: Michal Dobes <dobes@tesnet.cz>
+ *
+ * hardware driver for Advantech cards
+ * card: PCL-812, PCL-812PG, PCL-813, PCL-813B
+ * driver: pcl812, pcl812pg, pcl813, pcl813b
+ * and for ADlink cards
+ * card: ACL-8112DG, ACL-8112HG, ACL-8112PG, ACL-8113, ACL-8216
+ * driver: acl8112dg, acl8112hg, acl8112pg, acl8113, acl8216
+ * and for ICP DAS cards
+ * card: ISO-813, A-821PGH, A-821PGL, A-821PGL-NDA, A-822PGH, A-822PGL,
+ * driver: iso813, a821pgh, a-821pgl, a-821pglnda, a822pgh, a822pgl,
+ * card: A-823PGH, A-823PGL, A-826PG
+ * driver: a823pgh, a823pgl, a826pg
+ */
+
+/*
+ * Driver: pcl812
+ * Description: Advantech PCL-812/PG, PCL-813/B,
+ * ADLink ACL-8112DG/HG/PG, ACL-8113, ACL-8216,
+ * ICP DAS A-821PGH/PGL/PGL-NDA, A-822PGH/PGL, A-823PGH/PGL, A-826PG,
+ * ICP DAS ISO-813
+ * Author: Michal Dobes <dobes@tesnet.cz>
+ * Devices: [Advantech] PCL-812 (pcl812), PCL-812PG (pcl812pg),
+ * PCL-813 (pcl813), PCL-813B (pcl813b), [ADLink] ACL-8112DG (acl8112dg),
+ * ACL-8112HG (acl8112hg), ACL-8113 (acl-8113), ACL-8216 (acl8216),
+ * [ICP] ISO-813 (iso813), A-821PGH (a821pgh), A-821PGL (a821pgl),
+ * A-821PGL-NDA (a821pclnda), A-822PGH (a822pgh), A-822PGL (a822pgl),
+ * A-823PGH (a823pgh), A-823PGL (a823pgl), A-826PG (a826pg)
+ * Updated: Mon, 06 Aug 2007 12:03:15 +0100
+ * Status: works (I hope. My board fire up under my hands
+ * and I cann't test all features.)
+ *
+ * This driver supports insn and cmd interfaces. Some boards support only insn
+ * because their hardware don't allow more (PCL-813/B, ACL-8113, ISO-813).
+ * Data transfer over DMA is supported only when you measure only one
+ * channel, this is too hardware limitation of these boards.
+ *
+ * Options for PCL-812:
+ * [0] - IO Base
+ * [1] - IRQ (0=disable, 2, 3, 4, 5, 6, 7; 10, 11, 12, 14, 15)
+ * [2] - DMA (0=disable, 1, 3)
+ * [3] - 0=trigger source is internal 8253 with 2MHz clock
+ * 1=trigger source is external
+ * [4] - 0=A/D input range is +/-10V
+ * 1=A/D input range is +/-5V
+ * 2=A/D input range is +/-2.5V
+ * 3=A/D input range is +/-1.25V
+ * 4=A/D input range is +/-0.625V
+ * 5=A/D input range is +/-0.3125V
+ * [5] - 0=D/A outputs 0-5V (internal reference -5V)
+ * 1=D/A outputs 0-10V (internal reference -10V)
+ * 2=D/A outputs unknown (external reference)
+ *
+ * Options for PCL-812PG, ACL-8112PG:
+ * [0] - IO Base
+ * [1] - IRQ (0=disable, 2, 3, 4, 5, 6, 7; 10, 11, 12, 14, 15)
+ * [2] - DMA (0=disable, 1, 3)
+ * [3] - 0=trigger source is internal 8253 with 2MHz clock
+ * 1=trigger source is external
+ * [4] - 0=A/D have max +/-5V input
+ * 1=A/D have max +/-10V input
+ * [5] - 0=D/A outputs 0-5V (internal reference -5V)
+ * 1=D/A outputs 0-10V (internal reference -10V)
+ * 2=D/A outputs unknown (external reference)
+ *
+ * Options for ACL-8112DG/HG, A-822PGL/PGH, A-823PGL/PGH, ACL-8216, A-826PG:
+ * [0] - IO Base
+ * [1] - IRQ (0=disable, 2, 3, 4, 5, 6, 7; 10, 11, 12, 14, 15)
+ * [2] - DMA (0=disable, 1, 3)
+ * [3] - 0=trigger source is internal 8253 with 2MHz clock
+ * 1=trigger source is external
+ * [4] - 0=A/D channels are S.E.
+ * 1=A/D channels are DIFF
+ * [5] - 0=D/A outputs 0-5V (internal reference -5V)
+ * 1=D/A outputs 0-10V (internal reference -10V)
+ * 2=D/A outputs unknown (external reference)
+ *
+ * Options for A-821PGL/PGH:
+ * [0] - IO Base
+ * [1] - IRQ (0=disable, 2, 3, 4, 5, 6, 7)
+ * [2] - 0=A/D channels are S.E.
+ * 1=A/D channels are DIFF
+ * [3] - 0=D/A output 0-5V (internal reference -5V)
+ * 1=D/A output 0-10V (internal reference -10V)
+ *
+ * Options for A-821PGL-NDA:
+ * [0] - IO Base
+ * [1] - IRQ (0=disable, 2, 3, 4, 5, 6, 7)
+ * [2] - 0=A/D channels are S.E.
+ * 1=A/D channels are DIFF
+ *
+ * Options for PCL-813:
+ * [0] - IO Base
+ *
+ * Options for PCL-813B:
+ * [0] - IO Base
+ * [1] - 0= bipolar inputs
+ * 1= unipolar inputs
+ *
+ * Options for ACL-8113, ISO-813:
+ * [0] - IO Base
+ * [1] - 0= 10V bipolar inputs
+ * 1= 10V unipolar inputs
+ * 2= 20V bipolar inputs
+ * 3= 20V unipolar inputs
+ */
+
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/gfp.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+
+#include "../comedidev.h"
+
+#include "comedi_isadma.h"
+#include "comedi_8254.h"
+
+/* hardware types of the cards */
+#define boardPCL812PG 0 /* and ACL-8112PG */
+#define boardPCL813B 1
+#define boardPCL812 2
+#define boardPCL813 3
+#define boardISO813 5
+#define boardACL8113 6
+#define boardACL8112 7 /* ACL-8112DG/HG, A-822PGL/PGH, A-823PGL/PGH */
+#define boardACL8216 8 /* and ICP DAS A-826PG */
+#define boardA821 9 /* PGH, PGL, PGL/NDA versions */
+
+/*
+ * Register I/O map
+ */
+#define PCL812_TIMER_BASE 0x00
+#define PCL812_AI_LSB_REG 0x04
+#define PCL812_AI_MSB_REG 0x05
+#define PCL812_AI_MSB_DRDY (1 << 4)
+#define PCL812_AO_LSB_REG(x) (0x04 + ((x) * 2))
+#define PCL812_AO_MSB_REG(x) (0x05 + ((x) * 2))
+#define PCL812_DI_LSB_REG 0x06
+#define PCL812_DI_MSB_REG 0x07
+#define PCL812_STATUS_REG 0x08
+#define PCL812_STATUS_DRDY (1 << 5)
+#define PCL812_RANGE_REG 0x09
+#define PCL812_MUX_REG 0x0a
+#define PCL812_MUX_CHAN(x) ((x) << 0)
+#define PCL812_MUX_CS0 (1 << 4)
+#define PCL812_MUX_CS1 (1 << 5)
+#define PCL812_CTRL_REG 0x0b
+#define PCL812_CTRL_DISABLE_TRIG (0 << 0)
+#define PCL812_CTRL_SOFT_TRIG (1 << 0)
+#define PCL812_CTRL_PACER_DMA_TRIG (2 << 0)
+#define PCL812_CTRL_PACER_EOC_TRIG (6 << 0)
+#define PCL812_SOFTTRIG_REG 0x0c
+#define PCL812_DO_LSB_REG 0x0d
+#define PCL812_DO_MSB_REG 0x0e
+
+#define MAX_CHANLIST_LEN 256 /* length of scan list */
+
+static const struct comedi_lrange range_pcl812pg_ai = {
+ 5, {
+ BIP_RANGE(5),
+ BIP_RANGE(2.5),
+ BIP_RANGE(1.25),
+ BIP_RANGE(0.625),
+ BIP_RANGE(0.3125)
+ }
+};
+
+static const struct comedi_lrange range_pcl812pg2_ai = {
+ 5, {
+ BIP_RANGE(10),
+ BIP_RANGE(5),
+ BIP_RANGE(2.5),
+ BIP_RANGE(1.25),
+ BIP_RANGE(0.625)
+ }
+};
+
+static const struct comedi_lrange range812_bipolar1_25 = {
+ 1, {
+ BIP_RANGE(1.25)
+ }
+};
+
+static const struct comedi_lrange range812_bipolar0_625 = {
+ 1, {
+ BIP_RANGE(0.625)
+ }
+};
+
+static const struct comedi_lrange range812_bipolar0_3125 = {
+ 1, {
+ BIP_RANGE(0.3125)
+ }
+};
+
+static const struct comedi_lrange range_pcl813b_ai = {
+ 4, {
+ BIP_RANGE(5),
+ BIP_RANGE(2.5),
+ BIP_RANGE(1.25),
+ BIP_RANGE(0.625)
+ }
+};
+
+static const struct comedi_lrange range_pcl813b2_ai = {
+ 4, {
+ UNI_RANGE(10),
+ UNI_RANGE(5),
+ UNI_RANGE(2.5),
+ UNI_RANGE(1.25)
+ }
+};
+
+static const struct comedi_lrange range_iso813_1_ai = {
+ 5, {
+ BIP_RANGE(5),
+ BIP_RANGE(2.5),
+ BIP_RANGE(1.25),
+ BIP_RANGE(0.625),
+ BIP_RANGE(0.3125)
+ }
+};
+
+static const struct comedi_lrange range_iso813_1_2_ai = {
+ 5, {
+ UNI_RANGE(10),
+ UNI_RANGE(5),
+ UNI_RANGE(2.5),
+ UNI_RANGE(1.25),
+ UNI_RANGE(0.625)
+ }
+};
+
+static const struct comedi_lrange range_iso813_2_ai = {
+ 4, {
+ BIP_RANGE(5),
+ BIP_RANGE(2.5),
+ BIP_RANGE(1.25),
+ BIP_RANGE(0.625)
+ }
+};
+
+static const struct comedi_lrange range_iso813_2_2_ai = {
+ 4, {
+ UNI_RANGE(10),
+ UNI_RANGE(5),
+ UNI_RANGE(2.5),
+ UNI_RANGE(1.25)
+ }
+};
+
+static const struct comedi_lrange range_acl8113_1_ai = {
+ 4, {
+ BIP_RANGE(5),
+ BIP_RANGE(2.5),
+ BIP_RANGE(1.25),
+ BIP_RANGE(0.625)
+ }
+};
+
+static const struct comedi_lrange range_acl8113_1_2_ai = {
+ 4, {
+ UNI_RANGE(10),
+ UNI_RANGE(5),
+ UNI_RANGE(2.5),
+ UNI_RANGE(1.25)
+ }
+};
+
+static const struct comedi_lrange range_acl8113_2_ai = {
+ 3, {
+ BIP_RANGE(5),
+ BIP_RANGE(2.5),
+ BIP_RANGE(1.25)
+ }
+};
+
+static const struct comedi_lrange range_acl8113_2_2_ai = {
+ 3, {
+ UNI_RANGE(10),
+ UNI_RANGE(5),
+ UNI_RANGE(2.5)
+ }
+};
+
+static const struct comedi_lrange range_acl8112dg_ai = {
+ 9, {
+ BIP_RANGE(5),
+ BIP_RANGE(2.5),
+ BIP_RANGE(1.25),
+ BIP_RANGE(0.625),
+ UNI_RANGE(10),
+ UNI_RANGE(5),
+ UNI_RANGE(2.5),
+ UNI_RANGE(1.25),
+ BIP_RANGE(10)
+ }
+};
+
+static const struct comedi_lrange range_acl8112hg_ai = {
+ 12, {
+ BIP_RANGE(5),
+ BIP_RANGE(0.5),
+ BIP_RANGE(0.05),
+ BIP_RANGE(0.005),
+ UNI_RANGE(10),
+ UNI_RANGE(1),
+ UNI_RANGE(0.1),
+ UNI_RANGE(0.01),
+ BIP_RANGE(10),
+ BIP_RANGE(1),
+ BIP_RANGE(0.1),
+ BIP_RANGE(0.01)
+ }
+};
+
+static const struct comedi_lrange range_a821pgh_ai = {
+ 4, {
+ BIP_RANGE(5),
+ BIP_RANGE(0.5),
+ BIP_RANGE(0.05),
+ BIP_RANGE(0.005)
+ }
+};
+
+struct pcl812_board {
+ const char *name;
+ int board_type;
+ int n_aichan;
+ int n_aochan;
+ unsigned int ai_ns_min;
+ const struct comedi_lrange *rangelist_ai;
+ unsigned int IRQbits;
+ unsigned int has_dma:1;
+ unsigned int has_16bit_ai:1;
+ unsigned int has_mpc508_mux:1;
+ unsigned int has_dio:1;
+};
+
+static const struct pcl812_board boardtypes[] = {
+ {
+ .name = "pcl812",
+ .board_type = boardPCL812,
+ .n_aichan = 16,
+ .n_aochan = 2,
+ .ai_ns_min = 33000,
+ .rangelist_ai = &range_bipolar10,
+ .IRQbits = 0xdcfc,
+ .has_dma = 1,
+ .has_dio = 1,
+ }, {
+ .name = "pcl812pg",
+ .board_type = boardPCL812PG,
+ .n_aichan = 16,
+ .n_aochan = 2,
+ .ai_ns_min = 33000,
+ .rangelist_ai = &range_pcl812pg_ai,
+ .IRQbits = 0xdcfc,
+ .has_dma = 1,
+ .has_dio = 1,
+ }, {
+ .name = "acl8112pg",
+ .board_type = boardPCL812PG,
+ .n_aichan = 16,
+ .n_aochan = 2,
+ .ai_ns_min = 10000,
+ .rangelist_ai = &range_pcl812pg_ai,
+ .IRQbits = 0xdcfc,
+ .has_dma = 1,
+ .has_dio = 1,
+ }, {
+ .name = "acl8112dg",
+ .board_type = boardACL8112,
+ .n_aichan = 16, /* 8 differential */
+ .n_aochan = 2,
+ .ai_ns_min = 10000,
+ .rangelist_ai = &range_acl8112dg_ai,
+ .IRQbits = 0xdcfc,
+ .has_dma = 1,
+ .has_mpc508_mux = 1,
+ .has_dio = 1,
+ }, {
+ .name = "acl8112hg",
+ .board_type = boardACL8112,
+ .n_aichan = 16, /* 8 differential */
+ .n_aochan = 2,
+ .ai_ns_min = 10000,
+ .rangelist_ai = &range_acl8112hg_ai,
+ .IRQbits = 0xdcfc,
+ .has_dma = 1,
+ .has_mpc508_mux = 1,
+ .has_dio = 1,
+ }, {
+ .name = "a821pgl",
+ .board_type = boardA821,
+ .n_aichan = 16, /* 8 differential */
+ .n_aochan = 1,
+ .ai_ns_min = 10000,
+ .rangelist_ai = &range_pcl813b_ai,
+ .IRQbits = 0x000c,
+ .has_dio = 1,
+ }, {
+ .name = "a821pglnda",
+ .board_type = boardA821,
+ .n_aichan = 16, /* 8 differential */
+ .ai_ns_min = 10000,
+ .rangelist_ai = &range_pcl813b_ai,
+ .IRQbits = 0x000c,
+ }, {
+ .name = "a821pgh",
+ .board_type = boardA821,
+ .n_aichan = 16, /* 8 differential */
+ .n_aochan = 1,
+ .ai_ns_min = 10000,
+ .rangelist_ai = &range_a821pgh_ai,
+ .IRQbits = 0x000c,
+ .has_dio = 1,
+ }, {
+ .name = "a822pgl",
+ .board_type = boardACL8112,
+ .n_aichan = 16, /* 8 differential */
+ .n_aochan = 2,
+ .ai_ns_min = 10000,
+ .rangelist_ai = &range_acl8112dg_ai,
+ .IRQbits = 0xdcfc,
+ .has_dma = 1,
+ .has_dio = 1,
+ }, {
+ .name = "a822pgh",
+ .board_type = boardACL8112,
+ .n_aichan = 16, /* 8 differential */
+ .n_aochan = 2,
+ .ai_ns_min = 10000,
+ .rangelist_ai = &range_acl8112hg_ai,
+ .IRQbits = 0xdcfc,
+ .has_dma = 1,
+ .has_dio = 1,
+ }, {
+ .name = "a823pgl",
+ .board_type = boardACL8112,
+ .n_aichan = 16, /* 8 differential */
+ .n_aochan = 2,
+ .ai_ns_min = 8000,
+ .rangelist_ai = &range_acl8112dg_ai,
+ .IRQbits = 0xdcfc,
+ .has_dma = 1,
+ .has_dio = 1,
+ }, {
+ .name = "a823pgh",
+ .board_type = boardACL8112,
+ .n_aichan = 16, /* 8 differential */
+ .n_aochan = 2,
+ .ai_ns_min = 8000,
+ .rangelist_ai = &range_acl8112hg_ai,
+ .IRQbits = 0xdcfc,
+ .has_dma = 1,
+ .has_dio = 1,
+ }, {
+ .name = "pcl813",
+ .board_type = boardPCL813,
+ .n_aichan = 32,
+ .rangelist_ai = &range_pcl813b_ai,
+ }, {
+ .name = "pcl813b",
+ .board_type = boardPCL813B,
+ .n_aichan = 32,
+ .rangelist_ai = &range_pcl813b_ai,
+ }, {
+ .name = "acl8113",
+ .board_type = boardACL8113,
+ .n_aichan = 32,
+ .rangelist_ai = &range_acl8113_1_ai,
+ }, {
+ .name = "iso813",
+ .board_type = boardISO813,
+ .n_aichan = 32,
+ .rangelist_ai = &range_iso813_1_ai,
+ }, {
+ .name = "acl8216",
+ .board_type = boardACL8216,
+ .n_aichan = 16, /* 8 differential */
+ .n_aochan = 2,
+ .ai_ns_min = 10000,
+ .rangelist_ai = &range_pcl813b2_ai,
+ .IRQbits = 0xdcfc,
+ .has_dma = 1,
+ .has_16bit_ai = 1,
+ .has_mpc508_mux = 1,
+ .has_dio = 1,
+ }, {
+ .name = "a826pg",
+ .board_type = boardACL8216,
+ .n_aichan = 16, /* 8 differential */
+ .n_aochan = 2,
+ .ai_ns_min = 10000,
+ .rangelist_ai = &range_pcl813b2_ai,
+ .IRQbits = 0xdcfc,
+ .has_dma = 1,
+ .has_16bit_ai = 1,
+ .has_dio = 1,
+ },
+};
+
+struct pcl812_private {
+ struct comedi_isadma *dma;
+ unsigned char range_correction; /* =1 we must add 1 to range number */
+ unsigned int last_ai_chanspec;
+ unsigned char mode_reg_int; /* there is stored INT number for some card */
+ unsigned int ai_poll_ptr; /* how many sampes transfer poll */
+ unsigned int max_812_ai_mode0_rangewait; /* setling time for gain */
+ unsigned int use_diff:1;
+ unsigned int use_mpc508:1;
+ unsigned int use_ext_trg:1;
+ unsigned int ai_dma:1;
+ unsigned int ai_eos:1;
+};
+
+static void pcl812_ai_setup_dma(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ unsigned int unread_samples)
+{
+ struct pcl812_private *devpriv = dev->private;
+ struct comedi_isadma *dma = devpriv->dma;
+ struct comedi_isadma_desc *desc = &dma->desc[dma->cur_dma];
+ unsigned int bytes;
+ unsigned int max_samples;
+ unsigned int nsamples;
+
+ comedi_isadma_disable(dma->chan);
+
+ /* if using EOS, adapt DMA buffer to one scan */
+ bytes = devpriv->ai_eos ? comedi_bytes_per_scan(s) : desc->maxsize;
+ max_samples = comedi_bytes_to_samples(s, bytes);
+
+ /*
+ * Determine dma size based on the buffer size plus the number of
+ * unread samples and the number of samples remaining in the command.
+ */
+ nsamples = comedi_nsamples_left(s, max_samples + unread_samples);
+ if (nsamples > unread_samples) {
+ nsamples -= unread_samples;
+ desc->size = comedi_samples_to_bytes(s, nsamples);
+ comedi_isadma_program(desc);
+ }
+}
+
+static void pcl812_ai_set_chan_range(struct comedi_device *dev,
+ unsigned int chanspec, char wait)
+{
+ struct pcl812_private *devpriv = dev->private;
+ unsigned int chan = CR_CHAN(chanspec);
+ unsigned int range = CR_RANGE(chanspec);
+ unsigned int mux = 0;
+
+ if (chanspec == devpriv->last_ai_chanspec)
+ return;
+
+ devpriv->last_ai_chanspec = chanspec;
+
+ if (devpriv->use_mpc508) {
+ if (devpriv->use_diff) {
+ mux |= PCL812_MUX_CS0 | PCL812_MUX_CS1;
+ } else {
+ if (chan < 8)
+ mux |= PCL812_MUX_CS0;
+ else
+ mux |= PCL812_MUX_CS1;
+ }
+ }
+
+ outb(mux | PCL812_MUX_CHAN(chan), dev->iobase + PCL812_MUX_REG);
+ outb(range + devpriv->range_correction, dev->iobase + PCL812_RANGE_REG);
+
+ if (wait)
+ /*
+ * XXX this depends on selected range and can be very long for
+ * some high gain ranges!
+ */
+ udelay(devpriv->max_812_ai_mode0_rangewait);
+}
+
+static void pcl812_ai_clear_eoc(struct comedi_device *dev)
+{
+ /* writing any value clears the interrupt request */
+ outb(0, dev->iobase + PCL812_STATUS_REG);
+}
+
+static void pcl812_ai_soft_trig(struct comedi_device *dev)
+{
+ /* writing any value triggers a software conversion */
+ outb(255, dev->iobase + PCL812_SOFTTRIG_REG);
+}
+
+static unsigned int pcl812_ai_get_sample(struct comedi_device *dev,
+ struct comedi_subdevice *s)
+{
+ unsigned int val;
+
+ val = inb(dev->iobase + PCL812_AI_MSB_REG) << 8;
+ val |= inb(dev->iobase + PCL812_AI_LSB_REG);
+
+ return val & s->maxdata;
+}
+
+static int pcl812_ai_eoc(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned long context)
+{
+ unsigned int status;
+
+ if (s->maxdata > 0x0fff) {
+ status = inb(dev->iobase + PCL812_STATUS_REG);
+ if ((status & PCL812_STATUS_DRDY) == 0)
+ return 0;
+ } else {
+ status = inb(dev->iobase + PCL812_AI_MSB_REG);
+ if ((status & PCL812_AI_MSB_DRDY) == 0)
+ return 0;
+ }
+ return -EBUSY;
+}
+
+static int pcl812_ai_cmdtest(struct comedi_device *dev,
+ struct comedi_subdevice *s, struct comedi_cmd *cmd)
+{
+ const struct pcl812_board *board = dev->board_ptr;
+ struct pcl812_private *devpriv = dev->private;
+ int err = 0;
+ unsigned int flags;
+
+ /* Step 1 : check if triggers are trivially valid */
+
+ err |= comedi_check_trigger_src(&cmd->start_src, TRIG_NOW);
+ err |= comedi_check_trigger_src(&cmd->scan_begin_src, TRIG_FOLLOW);
+
+ if (devpriv->use_ext_trg)
+ flags = TRIG_EXT;
+ else
+ flags = TRIG_TIMER;
+ err |= comedi_check_trigger_src(&cmd->convert_src, flags);
+
+ err |= comedi_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
+ err |= comedi_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE);
+
+ if (err)
+ return 1;
+
+ /* Step 2a : make sure trigger sources are unique */
+
+ err |= comedi_check_trigger_is_unique(cmd->stop_src);
+
+ /* Step 2b : and mutually compatible */
+
+ if (err)
+ return 2;
+
+ /* Step 3: check if arguments are trivially valid */
+
+ err |= comedi_check_trigger_arg_is(&cmd->start_arg, 0);
+ err |= comedi_check_trigger_arg_is(&cmd->scan_begin_arg, 0);
+
+ if (cmd->convert_src == TRIG_TIMER) {
+ err |= comedi_check_trigger_arg_min(&cmd->convert_arg,
+ board->ai_ns_min);
+ } else { /* TRIG_EXT */
+ err |= comedi_check_trigger_arg_is(&cmd->convert_arg, 0);
+ }
+
+ err |= comedi_check_trigger_arg_min(&cmd->chanlist_len, 1);
+ err |= comedi_check_trigger_arg_is(&cmd->scan_end_arg,
+ cmd->chanlist_len);
+
+ if (cmd->stop_src == TRIG_COUNT)
+ err |= comedi_check_trigger_arg_min(&cmd->stop_arg, 1);
+ else /* TRIG_NONE */
+ err |= comedi_check_trigger_arg_is(&cmd->stop_arg, 0);
+
+ if (err)
+ return 3;
+
+ /* step 4: fix up any arguments */
+
+ if (cmd->convert_src == TRIG_TIMER) {
+ unsigned int arg = cmd->convert_arg;
+
+ comedi_8254_cascade_ns_to_timer(dev->pacer, &arg, cmd->flags);
+ err |= comedi_check_trigger_arg_is(&cmd->convert_arg, arg);
+ }
+
+ if (err)
+ return 4;
+
+ return 0;
+}
+
+static int pcl812_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
+{
+ struct pcl812_private *devpriv = dev->private;
+ struct comedi_isadma *dma = devpriv->dma;
+ struct comedi_cmd *cmd = &s->async->cmd;
+ unsigned int ctrl = 0;
+ unsigned int i;
+
+ pcl812_ai_set_chan_range(dev, cmd->chanlist[0], 1);
+
+ if (dma) { /* check if we can use DMA transfer */
+ devpriv->ai_dma = 1;
+ for (i = 1; i < cmd->chanlist_len; i++)
+ if (cmd->chanlist[0] != cmd->chanlist[i]) {
+ /* we cann't use DMA :-( */
+ devpriv->ai_dma = 0;
+ break;
+ }
+ } else {
+ devpriv->ai_dma = 0;
+ }
+
+ devpriv->ai_poll_ptr = 0;
+
+ /* don't we want wake up every scan? */
+ if (cmd->flags & CMDF_WAKE_EOS) {
+ devpriv->ai_eos = 1;
+
+ /* DMA is useless for this situation */
+ if (cmd->chanlist_len == 1)
+ devpriv->ai_dma = 0;
+ }
+
+ if (devpriv->ai_dma) {
+ /* setup and enable dma for the first buffer */
+ dma->cur_dma = 0;
+ pcl812_ai_setup_dma(dev, s, 0);
+ }
+
+ switch (cmd->convert_src) {
+ case TRIG_TIMER:
+ comedi_8254_update_divisors(dev->pacer);
+ comedi_8254_pacer_enable(dev->pacer, 1, 2, true);
+ break;
+ }
+
+ if (devpriv->ai_dma)
+ ctrl |= PCL812_CTRL_PACER_DMA_TRIG;
+ else
+ ctrl |= PCL812_CTRL_PACER_EOC_TRIG;
+ outb(devpriv->mode_reg_int | ctrl, dev->iobase + PCL812_CTRL_REG);
+
+ return 0;
+}
+
+static bool pcl812_ai_next_chan(struct comedi_device *dev,
+ struct comedi_subdevice *s)
+{
+ struct comedi_cmd *cmd = &s->async->cmd;
+
+ if (cmd->stop_src == TRIG_COUNT &&
+ s->async->scans_done >= cmd->stop_arg) {
+ s->async->events |= COMEDI_CB_EOA;
+ return false;
+ }
+
+ return true;
+}
+
+static void pcl812_handle_eoc(struct comedi_device *dev,
+ struct comedi_subdevice *s)
+{
+ struct comedi_cmd *cmd = &s->async->cmd;
+ unsigned int chan = s->async->cur_chan;
+ unsigned int next_chan;
+ unsigned short val;
+
+ if (pcl812_ai_eoc(dev, s, NULL, 0)) {
+ dev_dbg(dev->class_dev, "A/D cmd IRQ without DRDY!\n");
+ s->async->events |= COMEDI_CB_ERROR;
+ return;
+ }
+
+ val = pcl812_ai_get_sample(dev, s);
+ comedi_buf_write_samples(s, &val, 1);
+
+ /* Set up next channel. Added by abbotti 2010-01-20, but untested. */
+ next_chan = s->async->cur_chan;
+ if (cmd->chanlist[chan] != cmd->chanlist[next_chan])
+ pcl812_ai_set_chan_range(dev, cmd->chanlist[next_chan], 0);
+
+ pcl812_ai_next_chan(dev, s);
+}
+
+static void transfer_from_dma_buf(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ unsigned short *ptr,
+ unsigned int bufptr, unsigned int len)
+{
+ unsigned int i;
+ unsigned short val;
+
+ for (i = len; i; i--) {
+ val = ptr[bufptr++];
+ comedi_buf_write_samples(s, &val, 1);
+
+ if (!pcl812_ai_next_chan(dev, s))
+ break;
+ }
+}
+
+static void pcl812_handle_dma(struct comedi_device *dev,
+ struct comedi_subdevice *s)
+{
+ struct pcl812_private *devpriv = dev->private;
+ struct comedi_isadma *dma = devpriv->dma;
+ struct comedi_isadma_desc *desc = &dma->desc[dma->cur_dma];
+ unsigned int nsamples;
+ int bufptr;
+
+ nsamples = comedi_bytes_to_samples(s, desc->size) -
+ devpriv->ai_poll_ptr;
+ bufptr = devpriv->ai_poll_ptr;
+ devpriv->ai_poll_ptr = 0;
+
+ /* restart dma with the next buffer */
+ dma->cur_dma = 1 - dma->cur_dma;
+ pcl812_ai_setup_dma(dev, s, nsamples);
+
+ transfer_from_dma_buf(dev, s, desc->virt_addr, bufptr, nsamples);
+}
+
+static irqreturn_t pcl812_interrupt(int irq, void *d)
+{
+ struct comedi_device *dev = d;
+ struct comedi_subdevice *s = dev->read_subdev;
+ struct pcl812_private *devpriv = dev->private;
+
+ if (!dev->attached) {
+ pcl812_ai_clear_eoc(dev);
+ return IRQ_HANDLED;
+ }
+
+ if (devpriv->ai_dma)
+ pcl812_handle_dma(dev, s);
+ else
+ pcl812_handle_eoc(dev, s);
+
+ pcl812_ai_clear_eoc(dev);
+
+ comedi_handle_events(dev, s);
+ return IRQ_HANDLED;
+}
+
+static int pcl812_ai_poll(struct comedi_device *dev, struct comedi_subdevice *s)
+{
+ struct pcl812_private *devpriv = dev->private;
+ struct comedi_isadma *dma = devpriv->dma;
+ struct comedi_isadma_desc *desc;
+ unsigned long flags;
+ unsigned int poll;
+ int ret;
+
+ /* poll is valid only for DMA transfer */
+ if (!devpriv->ai_dma)
+ return 0;
+
+ spin_lock_irqsave(&dev->spinlock, flags);
+
+ poll = comedi_isadma_poll(dma);
+ poll = comedi_bytes_to_samples(s, poll);
+ if (poll > devpriv->ai_poll_ptr) {
+ desc = &dma->desc[dma->cur_dma];
+ transfer_from_dma_buf(dev, s, desc->virt_addr,
+ devpriv->ai_poll_ptr,
+ poll - devpriv->ai_poll_ptr);
+ /* new buffer position */
+ devpriv->ai_poll_ptr = poll;
+
+ ret = comedi_buf_n_bytes_ready(s);
+ } else {
+ /* no new samples */
+ ret = 0;
+ }
+
+ spin_unlock_irqrestore(&dev->spinlock, flags);
+
+ return ret;
+}
+
+static int pcl812_ai_cancel(struct comedi_device *dev,
+ struct comedi_subdevice *s)
+{
+ struct pcl812_private *devpriv = dev->private;
+
+ if (devpriv->ai_dma)
+ comedi_isadma_disable(devpriv->dma->chan);
+
+ outb(devpriv->mode_reg_int | PCL812_CTRL_DISABLE_TRIG,
+ dev->iobase + PCL812_CTRL_REG);
+ comedi_8254_pacer_enable(dev->pacer, 1, 2, false);
+ pcl812_ai_clear_eoc(dev);
+ return 0;
+}
+
+static int pcl812_ai_insn_read(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ struct pcl812_private *devpriv = dev->private;
+ int ret = 0;
+ int i;
+
+ outb(devpriv->mode_reg_int | PCL812_CTRL_SOFT_TRIG,
+ dev->iobase + PCL812_CTRL_REG);
+
+ pcl812_ai_set_chan_range(dev, insn->chanspec, 1);
+
+ for (i = 0; i < insn->n; i++) {
+ pcl812_ai_clear_eoc(dev);
+ pcl812_ai_soft_trig(dev);
+
+ ret = comedi_timeout(dev, s, insn, pcl812_ai_eoc, 0);
+ if (ret)
+ break;
+
+ data[i] = pcl812_ai_get_sample(dev, s);
+ }
+ outb(devpriv->mode_reg_int | PCL812_CTRL_DISABLE_TRIG,
+ dev->iobase + PCL812_CTRL_REG);
+ pcl812_ai_clear_eoc(dev);
+
+ return ret ? ret : insn->n;
+}
+
+static int pcl812_ao_insn_write(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ unsigned int chan = CR_CHAN(insn->chanspec);
+ unsigned int val = s->readback[chan];
+ int i;
+
+ for (i = 0; i < insn->n; i++) {
+ val = data[i];
+ outb(val & 0xff, dev->iobase + PCL812_AO_LSB_REG(chan));
+ outb((val >> 8) & 0x0f, dev->iobase + PCL812_AO_MSB_REG(chan));
+ }
+ s->readback[chan] = val;
+
+ return insn->n;
+}
+
+static int pcl812_di_insn_bits(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ data[1] = inb(dev->iobase + PCL812_DI_LSB_REG) |
+ (inb(dev->iobase + PCL812_DI_MSB_REG) << 8);
+
+ return insn->n;
+}
+
+static int pcl812_do_insn_bits(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ if (comedi_dio_update_state(s, data)) {
+ outb(s->state & 0xff, dev->iobase + PCL812_DO_LSB_REG);
+ outb((s->state >> 8), dev->iobase + PCL812_DO_MSB_REG);
+ }
+
+ data[1] = s->state;
+
+ return insn->n;
+}
+
+static void pcl812_reset(struct comedi_device *dev)
+{
+ const struct pcl812_board *board = dev->board_ptr;
+ struct pcl812_private *devpriv = dev->private;
+ unsigned int chan;
+
+ /* disable analog input trigger */
+ outb(devpriv->mode_reg_int | PCL812_CTRL_DISABLE_TRIG,
+ dev->iobase + PCL812_CTRL_REG);
+ pcl812_ai_clear_eoc(dev);
+
+ /*
+ * Invalidate last_ai_chanspec then set analog input to
+ * known channel/range.
+ */
+ devpriv->last_ai_chanspec = CR_PACK(16, 0, 0);
+ pcl812_ai_set_chan_range(dev, CR_PACK(0, 0, 0), 0);
+
+ /* set analog output channels to 0V */
+ for (chan = 0; chan < board->n_aochan; chan++) {
+ outb(0, dev->iobase + PCL812_AO_LSB_REG(chan));
+ outb(0, dev->iobase + PCL812_AO_MSB_REG(chan));
+ }
+
+ /* set all digital outputs low */
+ if (board->has_dio) {
+ outb(0, dev->iobase + PCL812_DO_MSB_REG);
+ outb(0, dev->iobase + PCL812_DO_LSB_REG);
+ }
+}
+
+static void pcl812_set_ai_range_table(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_devconfig *it)
+{
+ const struct pcl812_board *board = dev->board_ptr;
+ struct pcl812_private *devpriv = dev->private;
+
+ /* default to the range table from the boardinfo */
+ s->range_table = board->rangelist_ai;
+
+ /* now check the user config option based on the boardtype */
+ switch (board->board_type) {
+ case boardPCL812PG:
+ if (it->options[4] == 1)
+ s->range_table = &range_pcl812pg2_ai;
+ break;
+ case boardPCL812:
+ switch (it->options[4]) {
+ case 0:
+ s->range_table = &range_bipolar10;
+ break;
+ case 1:
+ s->range_table = &range_bipolar5;
+ break;
+ case 2:
+ s->range_table = &range_bipolar2_5;
+ break;
+ case 3:
+ s->range_table = &range812_bipolar1_25;
+ break;
+ case 4:
+ s->range_table = &range812_bipolar0_625;
+ break;
+ case 5:
+ s->range_table = &range812_bipolar0_3125;
+ break;
+ default:
+ s->range_table = &range_bipolar10;
+ break;
+ }
+ break;
+ case boardPCL813B:
+ if (it->options[1] == 1)
+ s->range_table = &range_pcl813b2_ai;
+ break;
+ case boardISO813:
+ switch (it->options[1]) {
+ case 0:
+ s->range_table = &range_iso813_1_ai;
+ break;
+ case 1:
+ s->range_table = &range_iso813_1_2_ai;
+ break;
+ case 2:
+ s->range_table = &range_iso813_2_ai;
+ devpriv->range_correction = 1;
+ break;
+ case 3:
+ s->range_table = &range_iso813_2_2_ai;
+ devpriv->range_correction = 1;
+ break;
+ default:
+ s->range_table = &range_iso813_1_ai;
+ break;
+ }
+ break;
+ case boardACL8113:
+ switch (it->options[1]) {
+ case 0:
+ s->range_table = &range_acl8113_1_ai;
+ break;
+ case 1:
+ s->range_table = &range_acl8113_1_2_ai;
+ break;
+ case 2:
+ s->range_table = &range_acl8113_2_ai;
+ devpriv->range_correction = 1;
+ break;
+ case 3:
+ s->range_table = &range_acl8113_2_2_ai;
+ devpriv->range_correction = 1;
+ break;
+ default:
+ s->range_table = &range_acl8113_1_ai;
+ break;
+ }
+ break;
+ }
+}
+
+static void pcl812_alloc_dma(struct comedi_device *dev, unsigned int dma_chan)
+{
+ struct pcl812_private *devpriv = dev->private;
+
+ /* only DMA channels 3 and 1 are valid */
+ if (!(dma_chan == 3 || dma_chan == 1))
+ return;
+
+ /* DMA uses two 8K buffers */
+ devpriv->dma = comedi_isadma_alloc(dev, 2, dma_chan, dma_chan,
+ PAGE_SIZE * 2, COMEDI_ISADMA_READ);
+}
+
+static void pcl812_free_dma(struct comedi_device *dev)
+{
+ struct pcl812_private *devpriv = dev->private;
+
+ if (devpriv)
+ comedi_isadma_free(devpriv->dma);
+}
+
+static int pcl812_attach(struct comedi_device *dev, struct comedi_devconfig *it)
+{
+ const struct pcl812_board *board = dev->board_ptr;
+ struct pcl812_private *devpriv;
+ struct comedi_subdevice *s;
+ int n_subdevices;
+ int subdev;
+ int ret;
+
+ devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
+ if (!devpriv)
+ return -ENOMEM;
+
+ ret = comedi_request_region(dev, it->options[0], 0x10);
+ if (ret)
+ return ret;
+
+ if (board->IRQbits) {
+ dev->pacer = comedi_8254_init(dev->iobase + PCL812_TIMER_BASE,
+ I8254_OSC_BASE_2MHZ,
+ I8254_IO8, 0);
+ if (!dev->pacer)
+ return -ENOMEM;
+
+ if ((1 << it->options[1]) & board->IRQbits) {
+ ret = request_irq(it->options[1], pcl812_interrupt, 0,
+ dev->board_name, dev);
+ if (ret == 0)
+ dev->irq = it->options[1];
+ }
+ }
+
+ /* we need an IRQ to do DMA on channel 3 or 1 */
+ if (dev->irq && board->has_dma)
+ pcl812_alloc_dma(dev, it->options[2]);
+
+ /* differential analog inputs? */
+ switch (board->board_type) {
+ case boardA821:
+ if (it->options[2] == 1)
+ devpriv->use_diff = 1;
+ break;
+ case boardACL8112:
+ case boardACL8216:
+ if (it->options[4] == 1)
+ devpriv->use_diff = 1;
+ break;
+ }
+
+ n_subdevices = 1; /* all boardtypes have analog inputs */
+ if (board->n_aochan > 0)
+ n_subdevices++;
+ if (board->has_dio)
+ n_subdevices += 2;
+
+ ret = comedi_alloc_subdevices(dev, n_subdevices);
+ if (ret)
+ return ret;
+
+ subdev = 0;
+
+ /* Analog Input subdevice */
+ s = &dev->subdevices[subdev];
+ s->type = COMEDI_SUBD_AI;
+ s->subdev_flags = SDF_READABLE;
+ if (devpriv->use_diff) {
+ s->subdev_flags |= SDF_DIFF;
+ s->n_chan = board->n_aichan / 2;
+ } else {
+ s->subdev_flags |= SDF_GROUND;
+ s->n_chan = board->n_aichan;
+ }
+ s->maxdata = board->has_16bit_ai ? 0xffff : 0x0fff;
+
+ pcl812_set_ai_range_table(dev, s, it);
+
+ s->insn_read = pcl812_ai_insn_read;
+
+ if (dev->irq) {
+ dev->read_subdev = s;
+ s->subdev_flags |= SDF_CMD_READ;
+ s->len_chanlist = MAX_CHANLIST_LEN;
+ s->do_cmdtest = pcl812_ai_cmdtest;
+ s->do_cmd = pcl812_ai_cmd;
+ s->poll = pcl812_ai_poll;
+ s->cancel = pcl812_ai_cancel;
+ }
+
+ devpriv->use_mpc508 = board->has_mpc508_mux;
+
+ subdev++;
+
+ /* analog output */
+ if (board->n_aochan > 0) {
+ s = &dev->subdevices[subdev];
+ s->type = COMEDI_SUBD_AO;
+ s->subdev_flags = SDF_WRITABLE | SDF_GROUND;
+ s->n_chan = board->n_aochan;
+ s->maxdata = 0xfff;
+ s->range_table = &range_unipolar5;
+ switch (board->board_type) {
+ case boardA821:
+ if (it->options[3] == 1)
+ s->range_table = &range_unipolar10;
+ break;
+ case boardPCL812:
+ case boardACL8112:
+ case boardPCL812PG:
+ case boardACL8216:
+ if (it->options[5] == 1)
+ s->range_table = &range_unipolar10;
+ if (it->options[5] == 2)
+ s->range_table = &range_unknown;
+ break;
+ }
+ s->insn_write = pcl812_ao_insn_write;
+
+ ret = comedi_alloc_subdev_readback(s);
+ if (ret)
+ return ret;
+
+ subdev++;
+ }
+
+ if (board->has_dio) {
+ /* Digital Input subdevice */
+ s = &dev->subdevices[subdev];
+ s->type = COMEDI_SUBD_DI;
+ s->subdev_flags = SDF_READABLE;
+ s->n_chan = 16;
+ s->maxdata = 1;
+ s->range_table = &range_digital;
+ s->insn_bits = pcl812_di_insn_bits;
+ subdev++;
+
+ /* Digital Output subdevice */
+ s = &dev->subdevices[subdev];
+ s->type = COMEDI_SUBD_DO;
+ s->subdev_flags = SDF_WRITABLE;
+ s->n_chan = 16;
+ s->maxdata = 1;
+ s->range_table = &range_digital;
+ s->insn_bits = pcl812_do_insn_bits;
+ subdev++;
+ }
+
+ switch (board->board_type) {
+ case boardACL8216:
+ case boardPCL812PG:
+ case boardPCL812:
+ case boardACL8112:
+ devpriv->max_812_ai_mode0_rangewait = 1;
+ if (it->options[3] > 0)
+ /* we use external trigger */
+ devpriv->use_ext_trg = 1;
+ break;
+ case boardA821:
+ devpriv->max_812_ai_mode0_rangewait = 1;
+ devpriv->mode_reg_int = (dev->irq << 4) & 0xf0;
+ break;
+ case boardPCL813B:
+ case boardPCL813:
+ case boardISO813:
+ case boardACL8113:
+ /* maybe there must by greatest timeout */
+ devpriv->max_812_ai_mode0_rangewait = 5;
+ break;
+ }
+
+ pcl812_reset(dev);
+
+ return 0;
+}
+
+static void pcl812_detach(struct comedi_device *dev)
+{
+ pcl812_free_dma(dev);
+ comedi_legacy_detach(dev);
+}
+
+static struct comedi_driver pcl812_driver = {
+ .driver_name = "pcl812",
+ .module = THIS_MODULE,
+ .attach = pcl812_attach,
+ .detach = pcl812_detach,
+ .board_name = &boardtypes[0].name,
+ .num_names = ARRAY_SIZE(boardtypes),
+ .offset = sizeof(struct pcl812_board),
+};
+module_comedi_driver(pcl812_driver);
+
+MODULE_AUTHOR("Comedi http://www.comedi.org");
+MODULE_DESCRIPTION("Comedi low-level driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/comedi/drivers/pcl816.c b/drivers/staging/comedi/drivers/pcl816.c
new file mode 100644
index 000000000..1ccb2f19f
--- /dev/null
+++ b/drivers/staging/comedi/drivers/pcl816.c
@@ -0,0 +1,712 @@
+/*
+ comedi/drivers/pcl816.c
+
+ Author: Juan Grigera <juan@grigera.com.ar>
+ based on pcl818 by Michal Dobes <dobes@tesnet.cz> and bits of pcl812
+
+ hardware driver for Advantech cards:
+ card: PCL-816, PCL814B
+ driver: pcl816
+*/
+/*
+Driver: pcl816
+Description: Advantech PCL-816 cards, PCL-814
+Author: Juan Grigera <juan@grigera.com.ar>
+Devices: [Advantech] PCL-816 (pcl816), PCL-814B (pcl814b)
+Status: works
+Updated: Tue, 2 Apr 2002 23:15:21 -0800
+
+PCL 816 and 814B have 16 SE/DIFF ADCs, 16 DACs, 16 DI and 16 DO.
+Differences are at resolution (16 vs 12 bits).
+
+The driver support AI command mode, other subdevices not written.
+
+Analog output and digital input and output are not supported.
+
+Configuration Options:
+ [0] - IO Base
+ [1] - IRQ (0=disable, 2, 3, 4, 5, 6, 7)
+ [2] - DMA (0=disable, 1, 3)
+ [3] - 0, 10=10MHz clock for 8254
+ 1= 1MHz clock for 8254
+
+*/
+
+#include <linux/module.h>
+#include <linux/gfp.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/interrupt.h>
+
+#include "../comedidev.h"
+
+#include "comedi_isadma.h"
+#include "comedi_8254.h"
+
+/*
+ * Register I/O map
+ */
+#define PCL816_DO_DI_LSB_REG 0x00
+#define PCL816_DO_DI_MSB_REG 0x01
+#define PCL816_TIMER_BASE 0x04
+#define PCL816_AI_LSB_REG 0x08
+#define PCL816_AI_MSB_REG 0x09
+#define PCL816_RANGE_REG 0x09
+#define PCL816_CLRINT_REG 0x0a
+#define PCL816_MUX_REG 0x0b
+#define PCL816_MUX_SCAN(_first, _last) (((_last) << 4) | (_first))
+#define PCL816_CTRL_REG 0x0c
+#define PCL816_CTRL_DISABLE_TRIG (0 << 0)
+#define PCL816_CTRL_SOFT_TRIG (1 << 0)
+#define PCL816_CTRL_PACER_TRIG (1 << 1)
+#define PCL816_CTRL_EXT_TRIG (1 << 2)
+#define PCL816_CTRL_POE (1 << 3)
+#define PCL816_CTRL_DMAEN (1 << 4)
+#define PCL816_CTRL_INTEN (1 << 5)
+#define PCL816_CTRL_DMASRC_SLOT0 (0 << 6)
+#define PCL816_CTRL_DMASRC_SLOT1 (1 << 6)
+#define PCL816_CTRL_DMASRC_SLOT2 (2 << 6)
+#define PCL816_STATUS_REG 0x0d
+#define PCL816_STATUS_NEXT_CHAN_MASK (0xf << 0)
+#define PCL816_STATUS_INTSRC_MASK (3 << 4)
+#define PCL816_STATUS_INTSRC_SLOT0 (0 << 4)
+#define PCL816_STATUS_INTSRC_SLOT1 (1 << 4)
+#define PCL816_STATUS_INTSRC_SLOT2 (2 << 4)
+#define PCL816_STATUS_INTSRC_DMA (3 << 4)
+#define PCL816_STATUS_INTACT (1 << 6)
+#define PCL816_STATUS_DRDY (1 << 7)
+
+#define MAGIC_DMA_WORD 0x5a5a
+
+static const struct comedi_lrange range_pcl816 = {
+ 8, {
+ BIP_RANGE(10),
+ BIP_RANGE(5),
+ BIP_RANGE(2.5),
+ BIP_RANGE(1.25),
+ UNI_RANGE(10),
+ UNI_RANGE(5),
+ UNI_RANGE(2.5),
+ UNI_RANGE(1.25)
+ }
+};
+
+struct pcl816_board {
+ const char *name;
+ int ai_maxdata;
+ int ao_maxdata;
+ int ai_chanlist;
+};
+
+static const struct pcl816_board boardtypes[] = {
+ {
+ .name = "pcl816",
+ .ai_maxdata = 0xffff,
+ .ao_maxdata = 0xffff,
+ .ai_chanlist = 1024,
+ }, {
+ .name = "pcl814b",
+ .ai_maxdata = 0x3fff,
+ .ao_maxdata = 0x3fff,
+ .ai_chanlist = 1024,
+ },
+};
+
+struct pcl816_private {
+ struct comedi_isadma *dma;
+ unsigned int ai_poll_ptr; /* how many sampes transfer poll */
+ unsigned int ai_cmd_running:1;
+ unsigned int ai_cmd_canceled:1;
+};
+
+static void pcl816_ai_setup_dma(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ unsigned int unread_samples)
+{
+ struct pcl816_private *devpriv = dev->private;
+ struct comedi_isadma *dma = devpriv->dma;
+ struct comedi_isadma_desc *desc = &dma->desc[dma->cur_dma];
+ unsigned int max_samples = comedi_bytes_to_samples(s, desc->maxsize);
+ unsigned int nsamples;
+
+ comedi_isadma_disable(dma->chan);
+
+ /*
+ * Determine dma size based on the buffer maxsize plus the number of
+ * unread samples and the number of samples remaining in the command.
+ */
+ nsamples = comedi_nsamples_left(s, max_samples + unread_samples);
+ if (nsamples > unread_samples) {
+ nsamples -= unread_samples;
+ desc->size = comedi_samples_to_bytes(s, nsamples);
+ comedi_isadma_program(desc);
+ }
+}
+
+static void pcl816_ai_set_chan_range(struct comedi_device *dev,
+ unsigned int chan,
+ unsigned int range)
+{
+ outb(chan, dev->iobase + PCL816_MUX_REG);
+ outb(range, dev->iobase + PCL816_RANGE_REG);
+}
+
+static void pcl816_ai_set_chan_scan(struct comedi_device *dev,
+ unsigned int first_chan,
+ unsigned int last_chan)
+{
+ outb(PCL816_MUX_SCAN(first_chan, last_chan),
+ dev->iobase + PCL816_MUX_REG);
+}
+
+static void pcl816_ai_setup_chanlist(struct comedi_device *dev,
+ unsigned int *chanlist,
+ unsigned int seglen)
+{
+ unsigned int first_chan = CR_CHAN(chanlist[0]);
+ unsigned int last_chan;
+ unsigned int range;
+ unsigned int i;
+
+ /* store range list to card */
+ for (i = 0; i < seglen; i++) {
+ last_chan = CR_CHAN(chanlist[i]);
+ range = CR_RANGE(chanlist[i]);
+
+ pcl816_ai_set_chan_range(dev, last_chan, range);
+ }
+
+ udelay(1);
+
+ pcl816_ai_set_chan_scan(dev, first_chan, last_chan);
+}
+
+static void pcl816_ai_clear_eoc(struct comedi_device *dev)
+{
+ /* writing any value clears the interrupt request */
+ outb(0, dev->iobase + PCL816_CLRINT_REG);
+}
+
+static void pcl816_ai_soft_trig(struct comedi_device *dev)
+{
+ /* writing any value triggers a software conversion */
+ outb(0, dev->iobase + PCL816_AI_LSB_REG);
+}
+
+static unsigned int pcl816_ai_get_sample(struct comedi_device *dev,
+ struct comedi_subdevice *s)
+{
+ unsigned int val;
+
+ val = inb(dev->iobase + PCL816_AI_MSB_REG) << 8;
+ val |= inb(dev->iobase + PCL816_AI_LSB_REG);
+
+ return val & s->maxdata;
+}
+
+static int pcl816_ai_eoc(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned long context)
+{
+ unsigned int status;
+
+ status = inb(dev->iobase + PCL816_STATUS_REG);
+ if ((status & PCL816_STATUS_DRDY) == 0)
+ return 0;
+ return -EBUSY;
+}
+
+static bool pcl816_ai_next_chan(struct comedi_device *dev,
+ struct comedi_subdevice *s)
+{
+ struct comedi_cmd *cmd = &s->async->cmd;
+
+ if (cmd->stop_src == TRIG_COUNT &&
+ s->async->scans_done >= cmd->stop_arg) {
+ s->async->events |= COMEDI_CB_EOA;
+ return false;
+ }
+
+ return true;
+}
+
+static void transfer_from_dma_buf(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ unsigned short *ptr,
+ unsigned int bufptr, unsigned int len)
+{
+ unsigned short val;
+ int i;
+
+ for (i = 0; i < len; i++) {
+ val = ptr[bufptr++];
+ comedi_buf_write_samples(s, &val, 1);
+
+ if (!pcl816_ai_next_chan(dev, s))
+ return;
+ }
+}
+
+static irqreturn_t pcl816_interrupt(int irq, void *d)
+{
+ struct comedi_device *dev = d;
+ struct comedi_subdevice *s = dev->read_subdev;
+ struct pcl816_private *devpriv = dev->private;
+ struct comedi_isadma *dma = devpriv->dma;
+ struct comedi_isadma_desc *desc = &dma->desc[dma->cur_dma];
+ unsigned int nsamples;
+ unsigned int bufptr;
+
+ if (!dev->attached || !devpriv->ai_cmd_running) {
+ pcl816_ai_clear_eoc(dev);
+ return IRQ_HANDLED;
+ }
+
+ if (devpriv->ai_cmd_canceled) {
+ devpriv->ai_cmd_canceled = 0;
+ pcl816_ai_clear_eoc(dev);
+ return IRQ_HANDLED;
+ }
+
+ nsamples = comedi_bytes_to_samples(s, desc->size) -
+ devpriv->ai_poll_ptr;
+ bufptr = devpriv->ai_poll_ptr;
+ devpriv->ai_poll_ptr = 0;
+
+ /* restart dma with the next buffer */
+ dma->cur_dma = 1 - dma->cur_dma;
+ pcl816_ai_setup_dma(dev, s, nsamples);
+
+ transfer_from_dma_buf(dev, s, desc->virt_addr, bufptr, nsamples);
+
+ pcl816_ai_clear_eoc(dev);
+
+ comedi_handle_events(dev, s);
+ return IRQ_HANDLED;
+}
+
+static int check_channel_list(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ unsigned int *chanlist,
+ unsigned int chanlen)
+{
+ unsigned int chansegment[16];
+ unsigned int i, nowmustbechan, seglen, segpos;
+
+ /* correct channel and range number check itself comedi/range.c */
+ if (chanlen < 1) {
+ dev_err(dev->class_dev, "range/channel list is empty!\n");
+ return 0;
+ }
+
+ if (chanlen > 1) {
+ /* first channel is every time ok */
+ chansegment[0] = chanlist[0];
+ for (i = 1, seglen = 1; i < chanlen; i++, seglen++) {
+ /* we detect loop, this must by finish */
+ if (chanlist[0] == chanlist[i])
+ break;
+ nowmustbechan =
+ (CR_CHAN(chansegment[i - 1]) + 1) % chanlen;
+ if (nowmustbechan != CR_CHAN(chanlist[i])) {
+ /* channel list isn't continuous :-( */
+ dev_dbg(dev->class_dev,
+ "channel list must be continuous! chanlist[%i]=%d but must be %d or %d!\n",
+ i, CR_CHAN(chanlist[i]), nowmustbechan,
+ CR_CHAN(chanlist[0]));
+ return 0;
+ }
+ /* well, this is next correct channel in list */
+ chansegment[i] = chanlist[i];
+ }
+
+ /* check whole chanlist */
+ for (i = 0, segpos = 0; i < chanlen; i++) {
+ if (chanlist[i] != chansegment[i % seglen]) {
+ dev_dbg(dev->class_dev,
+ "bad channel or range number! chanlist[%i]=%d,%d,%d and not %d,%d,%d!\n",
+ i, CR_CHAN(chansegment[i]),
+ CR_RANGE(chansegment[i]),
+ CR_AREF(chansegment[i]),
+ CR_CHAN(chanlist[i % seglen]),
+ CR_RANGE(chanlist[i % seglen]),
+ CR_AREF(chansegment[i % seglen]));
+ return 0; /* chan/gain list is strange */
+ }
+ }
+ } else {
+ seglen = 1;
+ }
+
+ return seglen; /* we can serve this with MUX logic */
+}
+
+static int pcl816_ai_cmdtest(struct comedi_device *dev,
+ struct comedi_subdevice *s, struct comedi_cmd *cmd)
+{
+ int err = 0;
+
+ /* Step 1 : check if triggers are trivially valid */
+
+ err |= comedi_check_trigger_src(&cmd->start_src, TRIG_NOW);
+ err |= comedi_check_trigger_src(&cmd->scan_begin_src, TRIG_FOLLOW);
+ err |= comedi_check_trigger_src(&cmd->convert_src,
+ TRIG_EXT | TRIG_TIMER);
+ err |= comedi_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
+ err |= comedi_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE);
+
+ if (err)
+ return 1;
+
+ /* Step 2a : make sure trigger sources are unique */
+
+ err |= comedi_check_trigger_is_unique(cmd->convert_src);
+ err |= comedi_check_trigger_is_unique(cmd->stop_src);
+
+ /* Step 2b : and mutually compatible */
+
+ if (err)
+ return 2;
+
+ /* Step 3: check if arguments are trivially valid */
+
+ err |= comedi_check_trigger_arg_is(&cmd->start_arg, 0);
+ err |= comedi_check_trigger_arg_is(&cmd->scan_begin_arg, 0);
+
+ if (cmd->convert_src == TRIG_TIMER)
+ err |= comedi_check_trigger_arg_min(&cmd->convert_arg, 10000);
+ else /* TRIG_EXT */
+ err |= comedi_check_trigger_arg_is(&cmd->convert_arg, 0);
+
+ err |= comedi_check_trigger_arg_is(&cmd->scan_end_arg,
+ cmd->chanlist_len);
+
+ if (cmd->stop_src == TRIG_COUNT)
+ err |= comedi_check_trigger_arg_min(&cmd->stop_arg, 1);
+ else /* TRIG_NONE */
+ err |= comedi_check_trigger_arg_is(&cmd->stop_arg, 0);
+
+ if (err)
+ return 3;
+
+ /* step 4: fix up any arguments */
+ if (cmd->convert_src == TRIG_TIMER) {
+ unsigned int arg = cmd->convert_arg;
+
+ comedi_8254_cascade_ns_to_timer(dev->pacer, &arg, cmd->flags);
+ err |= comedi_check_trigger_arg_is(&cmd->convert_arg, arg);
+ }
+
+ if (err)
+ return 4;
+
+ /* step 5: complain about special chanlist considerations */
+
+ if (cmd->chanlist) {
+ if (!check_channel_list(dev, s, cmd->chanlist,
+ cmd->chanlist_len))
+ return 5; /* incorrect channels list */
+ }
+
+ return 0;
+}
+
+static int pcl816_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
+{
+ struct pcl816_private *devpriv = dev->private;
+ struct comedi_isadma *dma = devpriv->dma;
+ struct comedi_cmd *cmd = &s->async->cmd;
+ unsigned int ctrl;
+ unsigned int seglen;
+
+ if (devpriv->ai_cmd_running)
+ return -EBUSY;
+
+ seglen = check_channel_list(dev, s, cmd->chanlist, cmd->chanlist_len);
+ if (seglen < 1)
+ return -EINVAL;
+ pcl816_ai_setup_chanlist(dev, cmd->chanlist, seglen);
+ udelay(1);
+
+ devpriv->ai_cmd_running = 1;
+ devpriv->ai_poll_ptr = 0;
+ devpriv->ai_cmd_canceled = 0;
+
+ /* setup and enable dma for the first buffer */
+ dma->cur_dma = 0;
+ pcl816_ai_setup_dma(dev, s, 0);
+
+ comedi_8254_set_mode(dev->pacer, 0, I8254_MODE1 | I8254_BINARY);
+ comedi_8254_write(dev->pacer, 0, 0x0ff);
+ udelay(1);
+ comedi_8254_update_divisors(dev->pacer);
+ comedi_8254_pacer_enable(dev->pacer, 1, 2, true);
+
+ ctrl = PCL816_CTRL_INTEN | PCL816_CTRL_DMAEN | PCL816_CTRL_DMASRC_SLOT0;
+ if (cmd->convert_src == TRIG_TIMER)
+ ctrl |= PCL816_CTRL_PACER_TRIG;
+ else /* TRIG_EXT */
+ ctrl |= PCL816_CTRL_EXT_TRIG;
+
+ outb(ctrl, dev->iobase + PCL816_CTRL_REG);
+ outb((dma->chan << 4) | dev->irq,
+ dev->iobase + PCL816_STATUS_REG);
+
+ return 0;
+}
+
+static int pcl816_ai_poll(struct comedi_device *dev, struct comedi_subdevice *s)
+{
+ struct pcl816_private *devpriv = dev->private;
+ struct comedi_isadma *dma = devpriv->dma;
+ struct comedi_isadma_desc *desc;
+ unsigned long flags;
+ unsigned int poll;
+ int ret;
+
+ spin_lock_irqsave(&dev->spinlock, flags);
+
+ poll = comedi_isadma_poll(dma);
+ poll = comedi_bytes_to_samples(s, poll);
+ if (poll > devpriv->ai_poll_ptr) {
+ desc = &dma->desc[dma->cur_dma];
+ transfer_from_dma_buf(dev, s, desc->virt_addr,
+ devpriv->ai_poll_ptr,
+ poll - devpriv->ai_poll_ptr);
+ /* new buffer position */
+ devpriv->ai_poll_ptr = poll;
+
+ comedi_handle_events(dev, s);
+
+ ret = comedi_buf_n_bytes_ready(s);
+ } else {
+ /* no new samples */
+ ret = 0;
+ }
+ spin_unlock_irqrestore(&dev->spinlock, flags);
+
+ return ret;
+}
+
+static int pcl816_ai_cancel(struct comedi_device *dev,
+ struct comedi_subdevice *s)
+{
+ struct pcl816_private *devpriv = dev->private;
+
+ if (!devpriv->ai_cmd_running)
+ return 0;
+
+ outb(PCL816_CTRL_DISABLE_TRIG, dev->iobase + PCL816_CTRL_REG);
+ pcl816_ai_clear_eoc(dev);
+
+ comedi_8254_pacer_enable(dev->pacer, 1, 2, false);
+
+ devpriv->ai_cmd_running = 0;
+ devpriv->ai_cmd_canceled = 1;
+
+ return 0;
+}
+
+static int pcl816_ai_insn_read(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ unsigned int chan = CR_CHAN(insn->chanspec);
+ unsigned int range = CR_RANGE(insn->chanspec);
+ int ret = 0;
+ int i;
+
+ outb(PCL816_CTRL_SOFT_TRIG, dev->iobase + PCL816_CTRL_REG);
+
+ pcl816_ai_set_chan_range(dev, chan, range);
+ pcl816_ai_set_chan_scan(dev, chan, chan);
+
+ for (i = 0; i < insn->n; i++) {
+ pcl816_ai_clear_eoc(dev);
+ pcl816_ai_soft_trig(dev);
+
+ ret = comedi_timeout(dev, s, insn, pcl816_ai_eoc, 0);
+ if (ret)
+ break;
+
+ data[i] = pcl816_ai_get_sample(dev, s);
+ }
+ outb(PCL816_CTRL_DISABLE_TRIG, dev->iobase + PCL816_CTRL_REG);
+ pcl816_ai_clear_eoc(dev);
+
+ return ret ? ret : insn->n;
+}
+
+static int pcl816_di_insn_bits(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ data[1] = inb(dev->iobase + PCL816_DO_DI_LSB_REG) |
+ (inb(dev->iobase + PCL816_DO_DI_MSB_REG) << 8);
+
+ return insn->n;
+}
+
+static int pcl816_do_insn_bits(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ if (comedi_dio_update_state(s, data)) {
+ outb(s->state & 0xff, dev->iobase + PCL816_DO_DI_LSB_REG);
+ outb((s->state >> 8), dev->iobase + PCL816_DO_DI_MSB_REG);
+ }
+
+ data[1] = s->state;
+
+ return insn->n;
+}
+
+static void pcl816_reset(struct comedi_device *dev)
+{
+ outb(PCL816_CTRL_DISABLE_TRIG, dev->iobase + PCL816_CTRL_REG);
+ pcl816_ai_set_chan_range(dev, 0, 0);
+ pcl816_ai_clear_eoc(dev);
+
+ /* set all digital outputs low */
+ outb(0, dev->iobase + PCL816_DO_DI_LSB_REG);
+ outb(0, dev->iobase + PCL816_DO_DI_MSB_REG);
+}
+
+static void pcl816_alloc_irq_and_dma(struct comedi_device *dev,
+ struct comedi_devconfig *it)
+{
+ struct pcl816_private *devpriv = dev->private;
+ unsigned int irq_num = it->options[1];
+ unsigned int dma_chan = it->options[2];
+
+ /* only IRQs 2-7 and DMA channels 3 and 1 are valid */
+ if (!(irq_num >= 2 && irq_num <= 7) ||
+ !(dma_chan == 3 || dma_chan == 1))
+ return;
+
+ if (request_irq(irq_num, pcl816_interrupt, 0, dev->board_name, dev))
+ return;
+
+ /* DMA uses two 16K buffers */
+ devpriv->dma = comedi_isadma_alloc(dev, 2, dma_chan, dma_chan,
+ PAGE_SIZE * 4, COMEDI_ISADMA_READ);
+ if (!devpriv->dma)
+ free_irq(irq_num, dev);
+ else
+ dev->irq = irq_num;
+}
+
+static void pcl816_free_dma(struct comedi_device *dev)
+{
+ struct pcl816_private *devpriv = dev->private;
+
+ if (devpriv)
+ comedi_isadma_free(devpriv->dma);
+}
+
+static int pcl816_attach(struct comedi_device *dev, struct comedi_devconfig *it)
+{
+ const struct pcl816_board *board = dev->board_ptr;
+ struct pcl816_private *devpriv;
+ struct comedi_subdevice *s;
+ int ret;
+
+ devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
+ if (!devpriv)
+ return -ENOMEM;
+
+ ret = comedi_request_region(dev, it->options[0], 0x10);
+ if (ret)
+ return ret;
+
+ /* an IRQ and DMA are required to support async commands */
+ pcl816_alloc_irq_and_dma(dev, it);
+
+ dev->pacer = comedi_8254_init(dev->iobase + PCL816_TIMER_BASE,
+ I8254_OSC_BASE_10MHZ, I8254_IO8, 0);
+ if (!dev->pacer)
+ return -ENOMEM;
+
+ ret = comedi_alloc_subdevices(dev, 4);
+ if (ret)
+ return ret;
+
+ s = &dev->subdevices[0];
+ s->type = COMEDI_SUBD_AI;
+ s->subdev_flags = SDF_CMD_READ | SDF_DIFF;
+ s->n_chan = 16;
+ s->maxdata = board->ai_maxdata;
+ s->range_table = &range_pcl816;
+ s->insn_read = pcl816_ai_insn_read;
+ if (dev->irq) {
+ dev->read_subdev = s;
+ s->subdev_flags |= SDF_CMD_READ;
+ s->len_chanlist = board->ai_chanlist;
+ s->do_cmdtest = pcl816_ai_cmdtest;
+ s->do_cmd = pcl816_ai_cmd;
+ s->poll = pcl816_ai_poll;
+ s->cancel = pcl816_ai_cancel;
+ }
+
+ /* Analog OUtput subdevice */
+ s = &dev->subdevices[2];
+ s->type = COMEDI_SUBD_UNUSED;
+#if 0
+ subdevs[1] = COMEDI_SUBD_AO;
+ s->subdev_flags = SDF_WRITABLE | SDF_GROUND;
+ s->n_chan = 1;
+ s->maxdata = board->ao_maxdata;
+ s->range_table = &range_pcl816;
+#endif
+
+ /* Digital Input subdevice */
+ s = &dev->subdevices[2];
+ s->type = COMEDI_SUBD_DI;
+ s->subdev_flags = SDF_READABLE;
+ s->n_chan = 16;
+ s->maxdata = 1;
+ s->range_table = &range_digital;
+ s->insn_bits = pcl816_di_insn_bits;
+
+ /* Digital Output subdevice */
+ s = &dev->subdevices[3];
+ s->type = COMEDI_SUBD_DO;
+ s->subdev_flags = SDF_WRITABLE;
+ s->n_chan = 16;
+ s->maxdata = 1;
+ s->range_table = &range_digital;
+ s->insn_bits = pcl816_do_insn_bits;
+
+ pcl816_reset(dev);
+
+ return 0;
+}
+
+static void pcl816_detach(struct comedi_device *dev)
+{
+ if (dev->private) {
+ pcl816_ai_cancel(dev, dev->read_subdev);
+ pcl816_reset(dev);
+ }
+ pcl816_free_dma(dev);
+ comedi_legacy_detach(dev);
+}
+
+static struct comedi_driver pcl816_driver = {
+ .driver_name = "pcl816",
+ .module = THIS_MODULE,
+ .attach = pcl816_attach,
+ .detach = pcl816_detach,
+ .board_name = &boardtypes[0].name,
+ .num_names = ARRAY_SIZE(boardtypes),
+ .offset = sizeof(struct pcl816_board),
+};
+module_comedi_driver(pcl816_driver);
+
+MODULE_AUTHOR("Comedi http://www.comedi.org");
+MODULE_DESCRIPTION("Comedi low-level driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/comedi/drivers/pcl818.c b/drivers/staging/comedi/drivers/pcl818.c
new file mode 100644
index 000000000..e1bdde977
--- /dev/null
+++ b/drivers/staging/comedi/drivers/pcl818.c
@@ -0,0 +1,1146 @@
+/*
+ * comedi/drivers/pcl818.c
+ *
+ * Driver: pcl818
+ * Description: Advantech PCL-818 cards, PCL-718
+ * Author: Michal Dobes <dobes@tesnet.cz>
+ * Devices: [Advantech] PCL-818L (pcl818l), PCL-818H (pcl818h),
+ * PCL-818HD (pcl818hd), PCL-818HG (pcl818hg), PCL-818 (pcl818),
+ * PCL-718 (pcl718)
+ * Status: works
+ *
+ * All cards have 16 SE/8 DIFF ADCs, one or two DACs, 16 DI and 16 DO.
+ * Differences are only at maximal sample speed, range list and FIFO
+ * support.
+ * The driver support AI mode 0, 1, 3 other subdevices (AO, DI, DO) support
+ * only mode 0. If DMA/FIFO/INT are disabled then AI support only mode 0.
+ * PCL-818HD and PCL-818HG support 1kword FIFO. Driver support this FIFO
+ * but this code is untested.
+ * A word or two about DMA. Driver support DMA operations at two ways:
+ * 1) DMA uses two buffers and after one is filled then is generated
+ * INT and DMA restart with second buffer. With this mode I'm unable run
+ * more that 80Ksamples/secs without data dropouts on K6/233.
+ * 2) DMA uses one buffer and run in autoinit mode and the data are
+ * from DMA buffer moved on the fly with 2kHz interrupts from RTC.
+ * This mode is used if the interrupt 8 is available for allocation.
+ * If not, then first DMA mode is used. With this I can run at
+ * full speed one card (100ksamples/secs) or two cards with
+ * 60ksamples/secs each (more is problem on account of ISA limitations).
+ * To use this mode you must have compiled kernel with disabled
+ * "Enhanced Real Time Clock Support".
+ * Maybe you can have problems if you use xntpd or similar.
+ * If you've data dropouts with DMA mode 2 then:
+ * a) disable IDE DMA
+ * b) switch text mode console to fb.
+ *
+ * Options for PCL-818L:
+ * [0] - IO Base
+ * [1] - IRQ (0=disable, 2, 3, 4, 5, 6, 7)
+ * [2] - DMA (0=disable, 1, 3)
+ * [3] - 0, 10=10MHz clock for 8254
+ * 1= 1MHz clock for 8254
+ * [4] - 0, 5=A/D input -5V.. +5V
+ * 1, 10=A/D input -10V..+10V
+ * [5] - 0, 5=D/A output 0-5V (internal reference -5V)
+ * 1, 10=D/A output 0-10V (internal reference -10V)
+ * 2 =D/A output unknown (external reference)
+ *
+ * Options for PCL-818, PCL-818H:
+ * [0] - IO Base
+ * [1] - IRQ (0=disable, 2, 3, 4, 5, 6, 7)
+ * [2] - DMA (0=disable, 1, 3)
+ * [3] - 0, 10=10MHz clock for 8254
+ * 1= 1MHz clock for 8254
+ * [4] - 0, 5=D/A output 0-5V (internal reference -5V)
+ * 1, 10=D/A output 0-10V (internal reference -10V)
+ * 2 =D/A output unknown (external reference)
+ *
+ * Options for PCL-818HD, PCL-818HG:
+ * [0] - IO Base
+ * [1] - IRQ (0=disable, 2, 3, 4, 5, 6, 7)
+ * [2] - DMA/FIFO (-1=use FIFO, 0=disable both FIFO and DMA,
+ * 1=use DMA ch 1, 3=use DMA ch 3)
+ * [3] - 0, 10=10MHz clock for 8254
+ * 1= 1MHz clock for 8254
+ * [4] - 0, 5=D/A output 0-5V (internal reference -5V)
+ * 1, 10=D/A output 0-10V (internal reference -10V)
+ * 2 =D/A output unknown (external reference)
+ *
+ * Options for PCL-718:
+ * [0] - IO Base
+ * [1] - IRQ (0=disable, 2, 3, 4, 5, 6, 7)
+ * [2] - DMA (0=disable, 1, 3)
+ * [3] - 0, 10=10MHz clock for 8254
+ * 1= 1MHz clock for 8254
+ * [4] - 0=A/D Range is +/-10V
+ * 1= +/-5V
+ * 2= +/-2.5V
+ * 3= +/-1V
+ * 4= +/-0.5V
+ * 5= user defined bipolar
+ * 6= 0-10V
+ * 7= 0-5V
+ * 8= 0-2V
+ * 9= 0-1V
+ * 10= user defined unipolar
+ * [5] - 0, 5=D/A outputs 0-5V (internal reference -5V)
+ * 1, 10=D/A outputs 0-10V (internal reference -10V)
+ * 2=D/A outputs unknown (external reference)
+ * [6] - 0, 60=max 60kHz A/D sampling
+ * 1,100=max 100kHz A/D sampling (PCL-718 with Option 001 installed)
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/gfp.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/interrupt.h>
+
+#include "../comedidev.h"
+
+#include "comedi_isadma.h"
+#include "comedi_8254.h"
+
+/* boards constants */
+
+#define boardPCL818L 0
+#define boardPCL818H 1
+#define boardPCL818HD 2
+#define boardPCL818HG 3
+#define boardPCL818 4
+#define boardPCL718 5
+
+/*
+ * Register I/O map
+ */
+#define PCL818_AI_LSB_REG 0x00
+#define PCL818_AI_MSB_REG 0x01
+#define PCL818_RANGE_REG 0x01
+#define PCL818_MUX_REG 0x02
+#define PCL818_MUX_SCAN(_first, _last) (((_last) << 4) | (_first))
+#define PCL818_DO_DI_LSB_REG 0x03
+#define PCL818_AO_LSB_REG(x) (0x04 + ((x) * 2))
+#define PCL818_AO_MSB_REG(x) (0x05 + ((x) * 2))
+#define PCL818_STATUS_REG 0x08
+#define PCL818_STATUS_NEXT_CHAN_MASK (0xf << 0)
+#define PCL818_STATUS_INT (1 << 4)
+#define PCL818_STATUS_MUX (1 << 5)
+#define PCL818_STATUS_UNI (1 << 6)
+#define PCL818_STATUS_EOC (1 << 7)
+#define PCL818_CTRL_REG 0x09
+#define PCL818_CTRL_DISABLE_TRIG (0 << 0)
+#define PCL818_CTRL_SOFT_TRIG (1 << 0)
+#define PCL818_CTRL_EXT_TRIG (2 << 0)
+#define PCL818_CTRL_PACER_TRIG (3 << 0)
+#define PCL818_CTRL_DMAE (1 << 2)
+#define PCL818_CTRL_IRQ(x) ((x) << 4)
+#define PCL818_CTRL_INTE (1 << 7)
+#define PCL818_CNTENABLE_REG 0x0a
+#define PCL818_CNTENABLE_PACER_ENA (0 << 0)
+#define PCL818_CNTENABLE_PACER_TRIG0 (1 << 0)
+#define PCL818_CNTENABLE_CNT0_EXT_CLK (0 << 1)
+#define PCL818_CNTENABLE_CNT0_INT_CLK (1 << 1)
+#define PCL818_DO_DI_MSB_REG 0x0b
+#define PCL818_TIMER_BASE 0x0c
+
+/* W: fifo enable/disable */
+#define PCL818_FI_ENABLE 6
+/* W: fifo interrupt clear */
+#define PCL818_FI_INTCLR 20
+/* W: fifo interrupt clear */
+#define PCL818_FI_FLUSH 25
+/* R: fifo status */
+#define PCL818_FI_STATUS 25
+/* R: one record from FIFO */
+#define PCL818_FI_DATALO 23
+#define PCL818_FI_DATAHI 24
+
+#define MAGIC_DMA_WORD 0x5a5a
+
+static const struct comedi_lrange range_pcl818h_ai = {
+ 9, {
+ BIP_RANGE(5),
+ BIP_RANGE(2.5),
+ BIP_RANGE(1.25),
+ BIP_RANGE(0.625),
+ UNI_RANGE(10),
+ UNI_RANGE(5),
+ UNI_RANGE(2.5),
+ UNI_RANGE(1.25),
+ BIP_RANGE(10)
+ }
+};
+
+static const struct comedi_lrange range_pcl818hg_ai = {
+ 10, {
+ BIP_RANGE(5),
+ BIP_RANGE(0.5),
+ BIP_RANGE(0.05),
+ BIP_RANGE(0.005),
+ UNI_RANGE(10),
+ UNI_RANGE(1),
+ UNI_RANGE(0.1),
+ UNI_RANGE(0.01),
+ BIP_RANGE(10),
+ BIP_RANGE(1),
+ BIP_RANGE(0.1),
+ BIP_RANGE(0.01)
+ }
+};
+
+static const struct comedi_lrange range_pcl818l_l_ai = {
+ 4, {
+ BIP_RANGE(5),
+ BIP_RANGE(2.5),
+ BIP_RANGE(1.25),
+ BIP_RANGE(0.625)
+ }
+};
+
+static const struct comedi_lrange range_pcl818l_h_ai = {
+ 4, {
+ BIP_RANGE(10),
+ BIP_RANGE(5),
+ BIP_RANGE(2.5),
+ BIP_RANGE(1.25)
+ }
+};
+
+static const struct comedi_lrange range718_bipolar1 = {
+ 1, {
+ BIP_RANGE(1)
+ }
+};
+
+static const struct comedi_lrange range718_bipolar0_5 = {
+ 1, {
+ BIP_RANGE(0.5)
+ }
+};
+
+static const struct comedi_lrange range718_unipolar2 = {
+ 1, {
+ UNI_RANGE(2)
+ }
+};
+
+static const struct comedi_lrange range718_unipolar1 = {
+ 1, {
+ BIP_RANGE(1)
+ }
+};
+
+struct pcl818_board {
+ const char *name;
+ unsigned int ns_min;
+ int n_aochan;
+ const struct comedi_lrange *ai_range_type;
+ unsigned int has_dma:1;
+ unsigned int has_fifo:1;
+ unsigned int is_818:1;
+};
+
+static const struct pcl818_board boardtypes[] = {
+ {
+ .name = "pcl818l",
+ .ns_min = 25000,
+ .n_aochan = 1,
+ .ai_range_type = &range_pcl818l_l_ai,
+ .has_dma = 1,
+ .is_818 = 1,
+ }, {
+ .name = "pcl818h",
+ .ns_min = 10000,
+ .n_aochan = 1,
+ .ai_range_type = &range_pcl818h_ai,
+ .has_dma = 1,
+ .is_818 = 1,
+ }, {
+ .name = "pcl818hd",
+ .ns_min = 10000,
+ .n_aochan = 1,
+ .ai_range_type = &range_pcl818h_ai,
+ .has_dma = 1,
+ .has_fifo = 1,
+ .is_818 = 1,
+ }, {
+ .name = "pcl818hg",
+ .ns_min = 10000,
+ .n_aochan = 1,
+ .ai_range_type = &range_pcl818hg_ai,
+ .has_dma = 1,
+ .has_fifo = 1,
+ .is_818 = 1,
+ }, {
+ .name = "pcl818",
+ .ns_min = 10000,
+ .n_aochan = 2,
+ .ai_range_type = &range_pcl818h_ai,
+ .has_dma = 1,
+ .is_818 = 1,
+ }, {
+ .name = "pcl718",
+ .ns_min = 16000,
+ .n_aochan = 2,
+ .ai_range_type = &range_unipolar5,
+ .has_dma = 1,
+ }, {
+ .name = "pcm3718",
+ .ns_min = 10000,
+ .ai_range_type = &range_pcl818h_ai,
+ .has_dma = 1,
+ .is_818 = 1,
+ },
+};
+
+struct pcl818_private {
+ struct comedi_isadma *dma;
+ /* manimal allowed delay between samples (in us) for actual card */
+ unsigned int ns_min;
+ /* MUX setting for actual AI operations */
+ unsigned int act_chanlist[16];
+ unsigned int act_chanlist_len; /* how long is actual MUX list */
+ unsigned int act_chanlist_pos; /* actual position in MUX list */
+ unsigned int usefifo:1;
+ unsigned int ai_cmd_running:1;
+ unsigned int ai_cmd_canceled:1;
+};
+
+static void pcl818_ai_setup_dma(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ unsigned int unread_samples)
+{
+ struct pcl818_private *devpriv = dev->private;
+ struct comedi_isadma *dma = devpriv->dma;
+ struct comedi_isadma_desc *desc = &dma->desc[dma->cur_dma];
+ unsigned int max_samples = comedi_bytes_to_samples(s, desc->maxsize);
+ unsigned int nsamples;
+
+ comedi_isadma_disable(dma->chan);
+
+ /*
+ * Determine dma size based on the buffer maxsize plus the number of
+ * unread samples and the number of samples remaining in the command.
+ */
+ nsamples = comedi_nsamples_left(s, max_samples + unread_samples);
+ if (nsamples > unread_samples) {
+ nsamples -= unread_samples;
+ desc->size = comedi_samples_to_bytes(s, nsamples);
+ comedi_isadma_program(desc);
+ }
+}
+
+static void pcl818_ai_set_chan_range(struct comedi_device *dev,
+ unsigned int chan,
+ unsigned int range)
+{
+ outb(chan, dev->iobase + PCL818_MUX_REG);
+ outb(range, dev->iobase + PCL818_RANGE_REG);
+}
+
+static void pcl818_ai_set_chan_scan(struct comedi_device *dev,
+ unsigned int first_chan,
+ unsigned int last_chan)
+{
+ outb(PCL818_MUX_SCAN(first_chan, last_chan),
+ dev->iobase + PCL818_MUX_REG);
+}
+
+static void pcl818_ai_setup_chanlist(struct comedi_device *dev,
+ unsigned int *chanlist,
+ unsigned int seglen)
+{
+ struct pcl818_private *devpriv = dev->private;
+ unsigned int first_chan = CR_CHAN(chanlist[0]);
+ unsigned int last_chan;
+ unsigned int range;
+ int i;
+
+ devpriv->act_chanlist_len = seglen;
+ devpriv->act_chanlist_pos = 0;
+
+ /* store range list to card */
+ for (i = 0; i < seglen; i++) {
+ last_chan = CR_CHAN(chanlist[i]);
+ range = CR_RANGE(chanlist[i]);
+
+ devpriv->act_chanlist[i] = last_chan;
+
+ pcl818_ai_set_chan_range(dev, last_chan, range);
+ }
+
+ udelay(1);
+
+ pcl818_ai_set_chan_scan(dev, first_chan, last_chan);
+}
+
+static void pcl818_ai_clear_eoc(struct comedi_device *dev)
+{
+ /* writing any value clears the interrupt request */
+ outb(0, dev->iobase + PCL818_STATUS_REG);
+}
+
+static void pcl818_ai_soft_trig(struct comedi_device *dev)
+{
+ /* writing any value triggers a software conversion */
+ outb(0, dev->iobase + PCL818_AI_LSB_REG);
+}
+
+static unsigned int pcl818_ai_get_fifo_sample(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ unsigned int *chan)
+{
+ unsigned int val;
+
+ val = inb(dev->iobase + PCL818_FI_DATALO);
+ val |= (inb(dev->iobase + PCL818_FI_DATAHI) << 8);
+
+ if (chan)
+ *chan = val & 0xf;
+
+ return (val >> 4) & s->maxdata;
+}
+
+static unsigned int pcl818_ai_get_sample(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ unsigned int *chan)
+{
+ unsigned int val;
+
+ val = inb(dev->iobase + PCL818_AI_MSB_REG) << 8;
+ val |= inb(dev->iobase + PCL818_AI_LSB_REG);
+
+ if (chan)
+ *chan = val & 0xf;
+
+ return (val >> 4) & s->maxdata;
+}
+
+static int pcl818_ai_eoc(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned long context)
+{
+ unsigned int status;
+
+ status = inb(dev->iobase + PCL818_STATUS_REG);
+ if (status & PCL818_STATUS_INT)
+ return 0;
+ return -EBUSY;
+}
+
+static bool pcl818_ai_write_sample(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ unsigned int chan, unsigned int val)
+{
+ struct pcl818_private *devpriv = dev->private;
+ struct comedi_cmd *cmd = &s->async->cmd;
+ unsigned int expected_chan;
+
+ expected_chan = devpriv->act_chanlist[devpriv->act_chanlist_pos];
+ if (chan != expected_chan) {
+ dev_dbg(dev->class_dev,
+ "A/D mode1/3 %s - channel dropout %d!=%d !\n",
+ (devpriv->dma) ? "DMA" :
+ (devpriv->usefifo) ? "FIFO" : "IRQ",
+ chan, expected_chan);
+ s->async->events |= COMEDI_CB_ERROR;
+ return false;
+ }
+
+ comedi_buf_write_samples(s, &val, 1);
+
+ devpriv->act_chanlist_pos++;
+ if (devpriv->act_chanlist_pos >= devpriv->act_chanlist_len)
+ devpriv->act_chanlist_pos = 0;
+
+ if (cmd->stop_src == TRIG_COUNT &&
+ s->async->scans_done >= cmd->stop_arg) {
+ s->async->events |= COMEDI_CB_EOA;
+ return false;
+ }
+
+ return true;
+}
+
+static void pcl818_handle_eoc(struct comedi_device *dev,
+ struct comedi_subdevice *s)
+{
+ unsigned int chan;
+ unsigned int val;
+
+ if (pcl818_ai_eoc(dev, s, NULL, 0)) {
+ dev_err(dev->class_dev, "A/D mode1/3 IRQ without DRDY!\n");
+ s->async->events |= COMEDI_CB_ERROR;
+ return;
+ }
+
+ val = pcl818_ai_get_sample(dev, s, &chan);
+ pcl818_ai_write_sample(dev, s, chan, val);
+}
+
+static void pcl818_handle_dma(struct comedi_device *dev,
+ struct comedi_subdevice *s)
+{
+ struct pcl818_private *devpriv = dev->private;
+ struct comedi_isadma *dma = devpriv->dma;
+ struct comedi_isadma_desc *desc = &dma->desc[dma->cur_dma];
+ unsigned short *ptr = desc->virt_addr;
+ unsigned int nsamples = comedi_bytes_to_samples(s, desc->size);
+ unsigned int chan;
+ unsigned int val;
+ int i;
+
+ /* restart dma with the next buffer */
+ dma->cur_dma = 1 - dma->cur_dma;
+ pcl818_ai_setup_dma(dev, s, nsamples);
+
+ for (i = 0; i < nsamples; i++) {
+ val = ptr[i];
+ chan = val & 0xf;
+ val = (val >> 4) & s->maxdata;
+ if (!pcl818_ai_write_sample(dev, s, chan, val))
+ break;
+ }
+}
+
+static void pcl818_handle_fifo(struct comedi_device *dev,
+ struct comedi_subdevice *s)
+{
+ unsigned int status;
+ unsigned int chan;
+ unsigned int val;
+ int i, len;
+
+ status = inb(dev->iobase + PCL818_FI_STATUS);
+
+ if (status & 4) {
+ dev_err(dev->class_dev, "A/D mode1/3 FIFO overflow!\n");
+ s->async->events |= COMEDI_CB_ERROR;
+ return;
+ }
+
+ if (status & 1) {
+ dev_err(dev->class_dev,
+ "A/D mode1/3 FIFO interrupt without data!\n");
+ s->async->events |= COMEDI_CB_ERROR;
+ return;
+ }
+
+ if (status & 2)
+ len = 512;
+ else
+ len = 0;
+
+ for (i = 0; i < len; i++) {
+ val = pcl818_ai_get_fifo_sample(dev, s, &chan);
+ if (!pcl818_ai_write_sample(dev, s, chan, val))
+ break;
+ }
+}
+
+static irqreturn_t pcl818_interrupt(int irq, void *d)
+{
+ struct comedi_device *dev = d;
+ struct pcl818_private *devpriv = dev->private;
+ struct comedi_subdevice *s = dev->read_subdev;
+ struct comedi_cmd *cmd = &s->async->cmd;
+
+ if (!dev->attached || !devpriv->ai_cmd_running) {
+ pcl818_ai_clear_eoc(dev);
+ return IRQ_HANDLED;
+ }
+
+ if (devpriv->ai_cmd_canceled) {
+ /*
+ * The cleanup from ai_cancel() has been delayed
+ * until now because the card doesn't seem to like
+ * being reprogrammed while a DMA transfer is in
+ * progress.
+ */
+ s->async->scans_done = cmd->stop_arg;
+ s->cancel(dev, s);
+ return IRQ_HANDLED;
+ }
+
+ if (devpriv->dma)
+ pcl818_handle_dma(dev, s);
+ else if (devpriv->usefifo)
+ pcl818_handle_fifo(dev, s);
+ else
+ pcl818_handle_eoc(dev, s);
+
+ pcl818_ai_clear_eoc(dev);
+
+ comedi_handle_events(dev, s);
+ return IRQ_HANDLED;
+}
+
+static int check_channel_list(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ unsigned int *chanlist, unsigned int n_chan)
+{
+ unsigned int chansegment[16];
+ unsigned int i, nowmustbechan, seglen, segpos;
+
+ /* correct channel and range number check itself comedi/range.c */
+ if (n_chan < 1) {
+ dev_err(dev->class_dev, "range/channel list is empty!\n");
+ return 0;
+ }
+
+ if (n_chan > 1) {
+ /* first channel is every time ok */
+ chansegment[0] = chanlist[0];
+ /* build part of chanlist */
+ for (i = 1, seglen = 1; i < n_chan; i++, seglen++) {
+ /* we detect loop, this must by finish */
+
+ if (chanlist[0] == chanlist[i])
+ break;
+ nowmustbechan =
+ (CR_CHAN(chansegment[i - 1]) + 1) % s->n_chan;
+ if (nowmustbechan != CR_CHAN(chanlist[i])) {
+ /* channel list isn't continuous :-( */
+ dev_dbg(dev->class_dev,
+ "channel list must be continuous! chanlist[%i]=%d but must be %d or %d!\n",
+ i, CR_CHAN(chanlist[i]), nowmustbechan,
+ CR_CHAN(chanlist[0]));
+ return 0;
+ }
+ /* well, this is next correct channel in list */
+ chansegment[i] = chanlist[i];
+ }
+
+ /* check whole chanlist */
+ for (i = 0, segpos = 0; i < n_chan; i++) {
+ if (chanlist[i] != chansegment[i % seglen]) {
+ dev_dbg(dev->class_dev,
+ "bad channel or range number! chanlist[%i]=%d,%d,%d and not %d,%d,%d!\n",
+ i, CR_CHAN(chansegment[i]),
+ CR_RANGE(chansegment[i]),
+ CR_AREF(chansegment[i]),
+ CR_CHAN(chanlist[i % seglen]),
+ CR_RANGE(chanlist[i % seglen]),
+ CR_AREF(chansegment[i % seglen]));
+ return 0; /* chan/gain list is strange */
+ }
+ }
+ } else {
+ seglen = 1;
+ }
+ return seglen;
+}
+
+static int check_single_ended(unsigned int port)
+{
+ if (inb(port + PCL818_STATUS_REG) & PCL818_STATUS_MUX)
+ return 1;
+ return 0;
+}
+
+static int ai_cmdtest(struct comedi_device *dev, struct comedi_subdevice *s,
+ struct comedi_cmd *cmd)
+{
+ const struct pcl818_board *board = dev->board_ptr;
+ int err = 0;
+
+ /* Step 1 : check if triggers are trivially valid */
+
+ err |= comedi_check_trigger_src(&cmd->start_src, TRIG_NOW);
+ err |= comedi_check_trigger_src(&cmd->scan_begin_src, TRIG_FOLLOW);
+ err |= comedi_check_trigger_src(&cmd->convert_src,
+ TRIG_TIMER | TRIG_EXT);
+ err |= comedi_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
+ err |= comedi_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE);
+
+ if (err)
+ return 1;
+
+ /* Step 2a : make sure trigger sources are unique */
+
+ err |= comedi_check_trigger_is_unique(cmd->convert_src);
+ err |= comedi_check_trigger_is_unique(cmd->stop_src);
+
+ /* Step 2b : and mutually compatible */
+
+ if (err)
+ return 2;
+
+ /* Step 3: check if arguments are trivially valid */
+
+ err |= comedi_check_trigger_arg_is(&cmd->start_arg, 0);
+ err |= comedi_check_trigger_arg_is(&cmd->scan_begin_arg, 0);
+
+ if (cmd->convert_src == TRIG_TIMER) {
+ err |= comedi_check_trigger_arg_min(&cmd->convert_arg,
+ board->ns_min);
+ } else { /* TRIG_EXT */
+ err |= comedi_check_trigger_arg_is(&cmd->convert_arg, 0);
+ }
+
+ err |= comedi_check_trigger_arg_is(&cmd->scan_end_arg,
+ cmd->chanlist_len);
+
+ if (cmd->stop_src == TRIG_COUNT)
+ err |= comedi_check_trigger_arg_min(&cmd->stop_arg, 1);
+ else /* TRIG_NONE */
+ err |= comedi_check_trigger_arg_is(&cmd->stop_arg, 0);
+
+ if (err)
+ return 3;
+
+ /* step 4: fix up any arguments */
+
+ if (cmd->convert_src == TRIG_TIMER) {
+ unsigned int arg = cmd->convert_arg;
+
+ comedi_8254_cascade_ns_to_timer(dev->pacer, &arg, cmd->flags);
+ err |= comedi_check_trigger_arg_is(&cmd->convert_arg, arg);
+ }
+
+ if (err)
+ return 4;
+
+ /* step 5: complain about special chanlist considerations */
+
+ if (cmd->chanlist) {
+ if (!check_channel_list(dev, s, cmd->chanlist,
+ cmd->chanlist_len))
+ return 5; /* incorrect channels list */
+ }
+
+ return 0;
+}
+
+static int pcl818_ai_cmd(struct comedi_device *dev,
+ struct comedi_subdevice *s)
+{
+ struct pcl818_private *devpriv = dev->private;
+ struct comedi_isadma *dma = devpriv->dma;
+ struct comedi_cmd *cmd = &s->async->cmd;
+ unsigned int ctrl = 0;
+ unsigned int seglen;
+
+ if (devpriv->ai_cmd_running)
+ return -EBUSY;
+
+ seglen = check_channel_list(dev, s, cmd->chanlist, cmd->chanlist_len);
+ if (seglen < 1)
+ return -EINVAL;
+ pcl818_ai_setup_chanlist(dev, cmd->chanlist, seglen);
+
+ devpriv->ai_cmd_running = 1;
+ devpriv->ai_cmd_canceled = 0;
+ devpriv->act_chanlist_pos = 0;
+
+ if (cmd->convert_src == TRIG_TIMER)
+ ctrl |= PCL818_CTRL_PACER_TRIG;
+ else
+ ctrl |= PCL818_CTRL_EXT_TRIG;
+
+ outb(PCL818_CNTENABLE_PACER_ENA, dev->iobase + PCL818_CNTENABLE_REG);
+
+ if (dma) {
+ /* setup and enable dma for the first buffer */
+ dma->cur_dma = 0;
+ pcl818_ai_setup_dma(dev, s, 0);
+
+ ctrl |= PCL818_CTRL_INTE | PCL818_CTRL_IRQ(dev->irq) |
+ PCL818_CTRL_DMAE;
+ } else if (devpriv->usefifo) {
+ /* enable FIFO */
+ outb(1, dev->iobase + PCL818_FI_ENABLE);
+ } else {
+ ctrl |= PCL818_CTRL_INTE | PCL818_CTRL_IRQ(dev->irq);
+ }
+ outb(ctrl, dev->iobase + PCL818_CTRL_REG);
+
+ if (cmd->convert_src == TRIG_TIMER) {
+ comedi_8254_update_divisors(dev->pacer);
+ comedi_8254_pacer_enable(dev->pacer, 1, 2, true);
+ }
+
+ return 0;
+}
+
+static int pcl818_ai_cancel(struct comedi_device *dev,
+ struct comedi_subdevice *s)
+{
+ struct pcl818_private *devpriv = dev->private;
+ struct comedi_isadma *dma = devpriv->dma;
+ struct comedi_cmd *cmd = &s->async->cmd;
+
+ if (!devpriv->ai_cmd_running)
+ return 0;
+
+ if (dma) {
+ if (cmd->stop_src == TRIG_NONE ||
+ (cmd->stop_src == TRIG_COUNT &&
+ s->async->scans_done < cmd->stop_arg)) {
+ if (!devpriv->ai_cmd_canceled) {
+ /*
+ * Wait for running dma transfer to end,
+ * do cleanup in interrupt.
+ */
+ devpriv->ai_cmd_canceled = 1;
+ return 0;
+ }
+ }
+ comedi_isadma_disable(dma->chan);
+ }
+
+ outb(PCL818_CTRL_DISABLE_TRIG, dev->iobase + PCL818_CTRL_REG);
+ comedi_8254_pacer_enable(dev->pacer, 1, 2, false);
+ pcl818_ai_clear_eoc(dev);
+
+ if (devpriv->usefifo) { /* FIFO shutdown */
+ outb(0, dev->iobase + PCL818_FI_INTCLR);
+ outb(0, dev->iobase + PCL818_FI_FLUSH);
+ outb(0, dev->iobase + PCL818_FI_ENABLE);
+ }
+ devpriv->ai_cmd_running = 0;
+ devpriv->ai_cmd_canceled = 0;
+
+ return 0;
+}
+
+static int pcl818_ai_insn_read(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ unsigned int chan = CR_CHAN(insn->chanspec);
+ unsigned int range = CR_RANGE(insn->chanspec);
+ int ret = 0;
+ int i;
+
+ outb(PCL818_CTRL_SOFT_TRIG, dev->iobase + PCL818_CTRL_REG);
+
+ pcl818_ai_set_chan_range(dev, chan, range);
+ pcl818_ai_set_chan_scan(dev, chan, chan);
+
+ for (i = 0; i < insn->n; i++) {
+ pcl818_ai_clear_eoc(dev);
+ pcl818_ai_soft_trig(dev);
+
+ ret = comedi_timeout(dev, s, insn, pcl818_ai_eoc, 0);
+ if (ret)
+ break;
+
+ data[i] = pcl818_ai_get_sample(dev, s, NULL);
+ }
+ pcl818_ai_clear_eoc(dev);
+
+ return ret ? ret : insn->n;
+}
+
+static int pcl818_ao_insn_write(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ unsigned int chan = CR_CHAN(insn->chanspec);
+ unsigned int val = s->readback[chan];
+ int i;
+
+ for (i = 0; i < insn->n; i++) {
+ val = data[i];
+ outb((val & 0x000f) << 4,
+ dev->iobase + PCL818_AO_LSB_REG(chan));
+ outb((val & 0x0ff0) >> 4,
+ dev->iobase + PCL818_AO_MSB_REG(chan));
+ }
+ s->readback[chan] = val;
+
+ return insn->n;
+}
+
+static int pcl818_di_insn_bits(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ data[1] = inb(dev->iobase + PCL818_DO_DI_LSB_REG) |
+ (inb(dev->iobase + PCL818_DO_DI_MSB_REG) << 8);
+
+ return insn->n;
+}
+
+static int pcl818_do_insn_bits(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ if (comedi_dio_update_state(s, data)) {
+ outb(s->state & 0xff, dev->iobase + PCL818_DO_DI_LSB_REG);
+ outb((s->state >> 8), dev->iobase + PCL818_DO_DI_MSB_REG);
+ }
+
+ data[1] = s->state;
+
+ return insn->n;
+}
+
+static void pcl818_reset(struct comedi_device *dev)
+{
+ const struct pcl818_board *board = dev->board_ptr;
+ unsigned int chan;
+
+ /* flush and disable the FIFO */
+ if (board->has_fifo) {
+ outb(0, dev->iobase + PCL818_FI_INTCLR);
+ outb(0, dev->iobase + PCL818_FI_FLUSH);
+ outb(0, dev->iobase + PCL818_FI_ENABLE);
+ }
+
+ /* disable analog input trigger */
+ outb(PCL818_CTRL_DISABLE_TRIG, dev->iobase + PCL818_CTRL_REG);
+ pcl818_ai_clear_eoc(dev);
+
+ pcl818_ai_set_chan_range(dev, 0, 0);
+
+ /* stop pacer */
+ outb(PCL818_CNTENABLE_PACER_ENA, dev->iobase + PCL818_CNTENABLE_REG);
+
+ /* set analog output channels to 0V */
+ for (chan = 0; chan < board->n_aochan; chan++) {
+ outb(0, dev->iobase + PCL818_AO_LSB_REG(chan));
+ outb(0, dev->iobase + PCL818_AO_MSB_REG(chan));
+ }
+
+ /* set all digital outputs low */
+ outb(0, dev->iobase + PCL818_DO_DI_MSB_REG);
+ outb(0, dev->iobase + PCL818_DO_DI_LSB_REG);
+}
+
+static void pcl818_set_ai_range_table(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_devconfig *it)
+{
+ const struct pcl818_board *board = dev->board_ptr;
+
+ /* default to the range table from the boardinfo */
+ s->range_table = board->ai_range_type;
+
+ /* now check the user config option based on the boardtype */
+ if (board->is_818) {
+ if (it->options[4] == 1 || it->options[4] == 10) {
+ /* secondary range list jumper selectable */
+ s->range_table = &range_pcl818l_h_ai;
+ }
+ } else {
+ switch (it->options[4]) {
+ case 0:
+ s->range_table = &range_bipolar10;
+ break;
+ case 1:
+ s->range_table = &range_bipolar5;
+ break;
+ case 2:
+ s->range_table = &range_bipolar2_5;
+ break;
+ case 3:
+ s->range_table = &range718_bipolar1;
+ break;
+ case 4:
+ s->range_table = &range718_bipolar0_5;
+ break;
+ case 6:
+ s->range_table = &range_unipolar10;
+ break;
+ case 7:
+ s->range_table = &range_unipolar5;
+ break;
+ case 8:
+ s->range_table = &range718_unipolar2;
+ break;
+ case 9:
+ s->range_table = &range718_unipolar1;
+ break;
+ default:
+ s->range_table = &range_unknown;
+ break;
+ }
+ }
+}
+
+static void pcl818_alloc_dma(struct comedi_device *dev, unsigned int dma_chan)
+{
+ struct pcl818_private *devpriv = dev->private;
+
+ /* only DMA channels 3 and 1 are valid */
+ if (!(dma_chan == 3 || dma_chan == 1))
+ return;
+
+ /* DMA uses two 16K buffers */
+ devpriv->dma = comedi_isadma_alloc(dev, 2, dma_chan, dma_chan,
+ PAGE_SIZE * 4, COMEDI_ISADMA_READ);
+}
+
+static void pcl818_free_dma(struct comedi_device *dev)
+{
+ struct pcl818_private *devpriv = dev->private;
+
+ if (devpriv)
+ comedi_isadma_free(devpriv->dma);
+}
+
+static int pcl818_attach(struct comedi_device *dev, struct comedi_devconfig *it)
+{
+ const struct pcl818_board *board = dev->board_ptr;
+ struct pcl818_private *devpriv;
+ struct comedi_subdevice *s;
+ unsigned int osc_base;
+ int ret;
+
+ devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
+ if (!devpriv)
+ return -ENOMEM;
+
+ ret = comedi_request_region(dev, it->options[0],
+ board->has_fifo ? 0x20 : 0x10);
+ if (ret)
+ return ret;
+
+ /* we can use IRQ 2-7 for async command support */
+ if (it->options[1] >= 2 && it->options[1] <= 7) {
+ ret = request_irq(it->options[1], pcl818_interrupt, 0,
+ dev->board_name, dev);
+ if (ret == 0)
+ dev->irq = it->options[1];
+ }
+
+ /* should we use the FIFO? */
+ if (dev->irq && board->has_fifo && it->options[2] == -1)
+ devpriv->usefifo = 1;
+
+ /* we need an IRQ to do DMA on channel 3 or 1 */
+ if (dev->irq && board->has_dma)
+ pcl818_alloc_dma(dev, it->options[2]);
+
+ /* use 1MHz or 10MHz oscilator */
+ if ((it->options[3] == 0) || (it->options[3] == 10))
+ osc_base = I8254_OSC_BASE_10MHZ;
+ else
+ osc_base = I8254_OSC_BASE_1MHZ;
+
+ dev->pacer = comedi_8254_init(dev->iobase + PCL818_TIMER_BASE,
+ osc_base, I8254_IO8, 0);
+ if (!dev->pacer)
+ return -ENOMEM;
+
+ /* max sampling speed */
+ devpriv->ns_min = board->ns_min;
+ if (!board->is_818) {
+ /* extended PCL718 to 100kHz DAC */
+ if ((it->options[6] == 1) || (it->options[6] == 100))
+ devpriv->ns_min = 10000;
+ }
+
+ ret = comedi_alloc_subdevices(dev, 4);
+ if (ret)
+ return ret;
+
+ s = &dev->subdevices[0];
+ s->type = COMEDI_SUBD_AI;
+ s->subdev_flags = SDF_READABLE;
+ if (check_single_ended(dev->iobase)) {
+ s->n_chan = 16;
+ s->subdev_flags |= SDF_COMMON | SDF_GROUND;
+ } else {
+ s->n_chan = 8;
+ s->subdev_flags |= SDF_DIFF;
+ }
+ s->maxdata = 0x0fff;
+
+ pcl818_set_ai_range_table(dev, s, it);
+
+ s->insn_read = pcl818_ai_insn_read;
+ if (dev->irq) {
+ dev->read_subdev = s;
+ s->subdev_flags |= SDF_CMD_READ;
+ s->len_chanlist = s->n_chan;
+ s->do_cmdtest = ai_cmdtest;
+ s->do_cmd = pcl818_ai_cmd;
+ s->cancel = pcl818_ai_cancel;
+ }
+
+ /* Analog Output subdevice */
+ s = &dev->subdevices[1];
+ if (board->n_aochan) {
+ s->type = COMEDI_SUBD_AO;
+ s->subdev_flags = SDF_WRITABLE | SDF_GROUND;
+ s->n_chan = board->n_aochan;
+ s->maxdata = 0x0fff;
+ s->range_table = &range_unipolar5;
+ if (board->is_818) {
+ if ((it->options[4] == 1) || (it->options[4] == 10))
+ s->range_table = &range_unipolar10;
+ if (it->options[4] == 2)
+ s->range_table = &range_unknown;
+ } else {
+ if ((it->options[5] == 1) || (it->options[5] == 10))
+ s->range_table = &range_unipolar10;
+ if (it->options[5] == 2)
+ s->range_table = &range_unknown;
+ }
+ s->insn_write = pcl818_ao_insn_write;
+
+ ret = comedi_alloc_subdev_readback(s);
+ if (ret)
+ return ret;
+ } else {
+ s->type = COMEDI_SUBD_UNUSED;
+ }
+
+ /* Digital Input subdevice */
+ s = &dev->subdevices[2];
+ s->type = COMEDI_SUBD_DI;
+ s->subdev_flags = SDF_READABLE;
+ s->n_chan = 16;
+ s->maxdata = 1;
+ s->range_table = &range_digital;
+ s->insn_bits = pcl818_di_insn_bits;
+
+ /* Digital Output subdevice */
+ s = &dev->subdevices[3];
+ s->type = COMEDI_SUBD_DO;
+ s->subdev_flags = SDF_WRITABLE;
+ s->n_chan = 16;
+ s->maxdata = 1;
+ s->range_table = &range_digital;
+ s->insn_bits = pcl818_do_insn_bits;
+
+ pcl818_reset(dev);
+
+ return 0;
+}
+
+static void pcl818_detach(struct comedi_device *dev)
+{
+ struct pcl818_private *devpriv = dev->private;
+
+ if (devpriv) {
+ pcl818_ai_cancel(dev, dev->read_subdev);
+ pcl818_reset(dev);
+ }
+ pcl818_free_dma(dev);
+ comedi_legacy_detach(dev);
+}
+
+static struct comedi_driver pcl818_driver = {
+ .driver_name = "pcl818",
+ .module = THIS_MODULE,
+ .attach = pcl818_attach,
+ .detach = pcl818_detach,
+ .board_name = &boardtypes[0].name,
+ .num_names = ARRAY_SIZE(boardtypes),
+ .offset = sizeof(struct pcl818_board),
+};
+module_comedi_driver(pcl818_driver);
+
+MODULE_AUTHOR("Comedi http://www.comedi.org");
+MODULE_DESCRIPTION("Comedi low-level driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/comedi/drivers/pcm3724.c b/drivers/staging/comedi/drivers/pcm3724.c
new file mode 100644
index 000000000..6176dfa24
--- /dev/null
+++ b/drivers/staging/comedi/drivers/pcm3724.c
@@ -0,0 +1,220 @@
+/*
+ comedi/drivers/pcm724.c
+
+ Drew Csillag <drew_csillag@yahoo.com>
+
+ hardware driver for Advantech card:
+ card: PCM-3724
+ driver: pcm3724
+
+ Options for PCM-3724
+ [0] - IO Base
+*/
+/*
+Driver: pcm3724
+Description: Advantech PCM-3724
+Author: Drew Csillag <drew_csillag@yahoo.com>
+Devices: [Advantech] PCM-3724 (pcm724)
+Status: tested
+
+This is driver for digital I/O boards PCM-3724 with 48 DIO.
+It needs 8255.o for operations and only immediate mode is supported.
+See the source for configuration details.
+
+Copy/pasted/hacked from pcm724.c
+*/
+/*
+ * check_driver overrides:
+ * struct comedi_insn
+ */
+
+#include <linux/module.h>
+#include "../comedidev.h"
+
+#include "8255.h"
+
+#define BUF_C0 0x1
+#define BUF_B0 0x2
+#define BUF_A0 0x4
+#define BUF_C1 0x8
+#define BUF_B1 0x10
+#define BUF_A1 0x20
+
+#define GATE_A0 0x4
+#define GATE_B0 0x2
+#define GATE_C0 0x1
+#define GATE_A1 0x20
+#define GATE_B1 0x10
+#define GATE_C1 0x8
+
+/* used to track configured dios */
+struct priv_pcm3724 {
+ int dio_1;
+ int dio_2;
+};
+
+static int compute_buffer(int config, int devno, struct comedi_subdevice *s)
+{
+ /* 1 in io_bits indicates output */
+ if (s->io_bits & 0x0000ff) {
+ if (devno == 0)
+ config |= BUF_A0;
+ else
+ config |= BUF_A1;
+ }
+ if (s->io_bits & 0x00ff00) {
+ if (devno == 0)
+ config |= BUF_B0;
+ else
+ config |= BUF_B1;
+ }
+ if (s->io_bits & 0xff0000) {
+ if (devno == 0)
+ config |= BUF_C0;
+ else
+ config |= BUF_C1;
+ }
+ return config;
+}
+
+static void do_3724_config(struct comedi_device *dev,
+ struct comedi_subdevice *s, int chanspec)
+{
+ struct comedi_subdevice *s_dio1 = &dev->subdevices[0];
+ struct comedi_subdevice *s_dio2 = &dev->subdevices[1];
+ int config;
+ int buffer_config;
+ unsigned long port_8255_cfg;
+
+ config = I8255_CTRL_CW;
+ buffer_config = 0;
+
+ /* 1 in io_bits indicates output, 1 in config indicates input */
+ if (!(s->io_bits & 0x0000ff))
+ config |= I8255_CTRL_A_IO;
+
+ if (!(s->io_bits & 0x00ff00))
+ config |= I8255_CTRL_B_IO;
+
+ if (!(s->io_bits & 0xff0000))
+ config |= I8255_CTRL_C_HI_IO | I8255_CTRL_C_LO_IO;
+
+ buffer_config = compute_buffer(0, 0, s_dio1);
+ buffer_config = compute_buffer(buffer_config, 1, s_dio2);
+
+ if (s == s_dio1)
+ port_8255_cfg = dev->iobase + I8255_CTRL_REG;
+ else
+ port_8255_cfg = dev->iobase + I8255_SIZE + I8255_CTRL_REG;
+
+ outb(buffer_config, dev->iobase + 8); /* update buffer register */
+
+ outb(config, port_8255_cfg);
+}
+
+static void enable_chan(struct comedi_device *dev, struct comedi_subdevice *s,
+ int chanspec)
+{
+ struct priv_pcm3724 *priv = dev->private;
+ struct comedi_subdevice *s_dio1 = &dev->subdevices[0];
+ unsigned int mask;
+ int gatecfg;
+
+ gatecfg = 0;
+
+ mask = 1 << CR_CHAN(chanspec);
+ if (s == s_dio1)
+ priv->dio_1 |= mask;
+ else
+ priv->dio_2 |= mask;
+
+ if (priv->dio_1 & 0xff0000)
+ gatecfg |= GATE_C0;
+
+ if (priv->dio_1 & 0xff00)
+ gatecfg |= GATE_B0;
+
+ if (priv->dio_1 & 0xff)
+ gatecfg |= GATE_A0;
+
+ if (priv->dio_2 & 0xff0000)
+ gatecfg |= GATE_C1;
+
+ if (priv->dio_2 & 0xff00)
+ gatecfg |= GATE_B1;
+
+ if (priv->dio_2 & 0xff)
+ gatecfg |= GATE_A1;
+
+ outb(gatecfg, dev->iobase + 9);
+}
+
+/* overriding the 8255 insn config */
+static int subdev_3724_insn_config(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ unsigned int chan = CR_CHAN(insn->chanspec);
+ unsigned int mask;
+ int ret;
+
+ if (chan < 8)
+ mask = 0x0000ff;
+ else if (chan < 16)
+ mask = 0x00ff00;
+ else if (chan < 20)
+ mask = 0x0f0000;
+ else
+ mask = 0xf00000;
+
+ ret = comedi_dio_insn_config(dev, s, insn, data, mask);
+ if (ret)
+ return ret;
+
+ do_3724_config(dev, s, insn->chanspec);
+ enable_chan(dev, s, insn->chanspec);
+
+ return insn->n;
+}
+
+static int pcm3724_attach(struct comedi_device *dev,
+ struct comedi_devconfig *it)
+{
+ struct priv_pcm3724 *priv;
+ struct comedi_subdevice *s;
+ int ret, i;
+
+ priv = comedi_alloc_devpriv(dev, sizeof(*priv));
+ if (!priv)
+ return -ENOMEM;
+
+ ret = comedi_request_region(dev, it->options[0], 0x10);
+ if (ret)
+ return ret;
+
+ ret = comedi_alloc_subdevices(dev, 2);
+ if (ret)
+ return ret;
+
+ for (i = 0; i < dev->n_subdevices; i++) {
+ s = &dev->subdevices[i];
+ ret = subdev_8255_init(dev, s, NULL, i * I8255_SIZE);
+ if (ret)
+ return ret;
+ s->insn_config = subdev_3724_insn_config;
+ }
+ return 0;
+}
+
+static struct comedi_driver pcm3724_driver = {
+ .driver_name = "pcm3724",
+ .module = THIS_MODULE,
+ .attach = pcm3724_attach,
+ .detach = comedi_legacy_detach,
+};
+module_comedi_driver(pcm3724_driver);
+
+MODULE_AUTHOR("Comedi http://www.comedi.org");
+MODULE_DESCRIPTION("Comedi low-level driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/comedi/drivers/pcmad.c b/drivers/staging/comedi/drivers/pcmad.c
new file mode 100644
index 000000000..12f94fe82
--- /dev/null
+++ b/drivers/staging/comedi/drivers/pcmad.c
@@ -0,0 +1,158 @@
+/*
+ * pcmad.c
+ * Hardware driver for Winsystems PCM-A/D12 and PCM-A/D16
+ *
+ * COMEDI - Linux Control and Measurement Device Interface
+ * Copyright (C) 2000,2001 David A. Schleef <ds@schleef.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+/*
+ * Driver: pcmad
+ * Description: Winsystems PCM-A/D12, PCM-A/D16
+ * Devices: [Winsystems] PCM-A/D12 (pcmad12), PCM-A/D16 (pcmad16)
+ * Author: ds
+ * Status: untested
+ *
+ * This driver was written on a bet that I couldn't write a driver
+ * in less than 2 hours. I won the bet, but never got paid. =(
+ *
+ * Configuration options:
+ * [0] - I/O port base
+ * [1] - IRQ (unused)
+ * [2] - Analog input reference (must match jumpers)
+ * 0 = single-ended (16 channels)
+ * 1 = differential (8 channels)
+ * [3] - Analog input encoding (must match jumpers)
+ * 0 = straight binary (0-5V input range)
+ * 1 = two's complement (+-10V input range)
+ */
+
+#include <linux/module.h>
+#include "../comedidev.h"
+
+#define PCMAD_STATUS 0
+#define PCMAD_LSB 1
+#define PCMAD_MSB 2
+#define PCMAD_CONVERT 1
+
+struct pcmad_board_struct {
+ const char *name;
+ unsigned int ai_maxdata;
+};
+
+static const struct pcmad_board_struct pcmad_boards[] = {
+ {
+ .name = "pcmad12",
+ .ai_maxdata = 0x0fff,
+ }, {
+ .name = "pcmad16",
+ .ai_maxdata = 0xffff,
+ },
+};
+
+static int pcmad_ai_eoc(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned long context)
+{
+ unsigned int status;
+
+ status = inb(dev->iobase + PCMAD_STATUS);
+ if ((status & 0x3) == 0x3)
+ return 0;
+ return -EBUSY;
+}
+
+static int pcmad_ai_insn_read(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ unsigned int chan = CR_CHAN(insn->chanspec);
+ unsigned int range = CR_RANGE(insn->chanspec);
+ unsigned int val;
+ int ret;
+ int i;
+
+ for (i = 0; i < insn->n; i++) {
+ outb(chan, dev->iobase + PCMAD_CONVERT);
+
+ ret = comedi_timeout(dev, s, insn, pcmad_ai_eoc, 0);
+ if (ret)
+ return ret;
+
+ val = inb(dev->iobase + PCMAD_LSB) |
+ (inb(dev->iobase + PCMAD_MSB) << 8);
+
+ /* data is shifted on the pcmad12, fix it */
+ if (s->maxdata == 0x0fff)
+ val >>= 4;
+
+ if (comedi_range_is_bipolar(s, range)) {
+ /* munge the two's complement value */
+ val ^= ((s->maxdata + 1) >> 1);
+ }
+
+ data[i] = val;
+ }
+
+ return insn->n;
+}
+
+static int pcmad_attach(struct comedi_device *dev, struct comedi_devconfig *it)
+{
+ const struct pcmad_board_struct *board = dev->board_ptr;
+ struct comedi_subdevice *s;
+ int ret;
+
+ ret = comedi_request_region(dev, it->options[0], 0x04);
+ if (ret)
+ return ret;
+
+ ret = comedi_alloc_subdevices(dev, 1);
+ if (ret)
+ return ret;
+
+ s = &dev->subdevices[0];
+ s->type = COMEDI_SUBD_AI;
+ if (it->options[1]) {
+ /* 8 differential channels */
+ s->subdev_flags = SDF_READABLE | AREF_DIFF;
+ s->n_chan = 8;
+ } else {
+ /* 16 single-ended channels */
+ s->subdev_flags = SDF_READABLE | AREF_GROUND;
+ s->n_chan = 16;
+ }
+ s->len_chanlist = 1;
+ s->maxdata = board->ai_maxdata;
+ s->range_table = it->options[2] ? &range_bipolar10 : &range_unipolar5;
+ s->insn_read = pcmad_ai_insn_read;
+
+ return 0;
+}
+
+static struct comedi_driver pcmad_driver = {
+ .driver_name = "pcmad",
+ .module = THIS_MODULE,
+ .attach = pcmad_attach,
+ .detach = comedi_legacy_detach,
+ .board_name = &pcmad_boards[0].name,
+ .num_names = ARRAY_SIZE(pcmad_boards),
+ .offset = sizeof(pcmad_boards[0]),
+};
+module_comedi_driver(pcmad_driver);
+
+MODULE_AUTHOR("Comedi http://www.comedi.org");
+MODULE_DESCRIPTION("Comedi low-level driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/comedi/drivers/pcmda12.c b/drivers/staging/comedi/drivers/pcmda12.c
new file mode 100644
index 000000000..d86c5e2cd
--- /dev/null
+++ b/drivers/staging/comedi/drivers/pcmda12.c
@@ -0,0 +1,174 @@
+/*
+ * pcmda12.c
+ * Driver for Winsystems PC-104 based PCM-D/A-12 8-channel AO board.
+ *
+ * COMEDI - Linux Control and Measurement Device Interface
+ * Copyright (C) 2006 Calin A. Culianu <calin@ajvar.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+/*
+ * Driver: pcmda12
+ * Description: A driver for the Winsystems PCM-D/A-12
+ * Devices: [Winsystems] PCM-D/A-12 (pcmda12)
+ * Author: Calin Culianu <calin@ajvar.org>
+ * Updated: Fri, 13 Jan 2006 12:01:01 -0500
+ * Status: works
+ *
+ * A driver for the relatively straightforward-to-program PCM-D/A-12.
+ * This board doesn't support commands, and the only way to set its
+ * analog output range is to jumper the board. As such,
+ * comedi_data_write() ignores the range value specified.
+ *
+ * The board uses 16 consecutive I/O addresses starting at the I/O port
+ * base address. Each address corresponds to the LSB then MSB of a
+ * particular channel from 0-7.
+ *
+ * Note that the board is not ISA-PNP capable and thus needs the I/O
+ * port comedi_config parameter.
+ *
+ * Note that passing a nonzero value as the second config option will
+ * enable "simultaneous xfer" mode for this board, in which AO writes
+ * will not take effect until a subsequent read of any AO channel. This
+ * is so that one can speed up programming by preloading all AO registers
+ * with values before simultaneously setting them to take effect with one
+ * read command.
+ *
+ * Configuration Options:
+ * [0] - I/O port base address
+ * [1] - Do Simultaneous Xfer (see description)
+ */
+
+#include <linux/module.h>
+#include "../comedidev.h"
+
+/* AI range is not configurable, it's set by jumpers on the board */
+static const struct comedi_lrange pcmda12_ranges = {
+ 3, {
+ UNI_RANGE(5),
+ UNI_RANGE(10),
+ BIP_RANGE(5)
+ }
+};
+
+struct pcmda12_private {
+ int simultaneous_xfer_mode;
+};
+
+static int pcmda12_ao_insn_write(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ struct pcmda12_private *devpriv = dev->private;
+ unsigned int chan = CR_CHAN(insn->chanspec);
+ unsigned int val = s->readback[chan];
+ unsigned long ioreg = dev->iobase + (chan * 2);
+ int i;
+
+ for (i = 0; i < insn->n; ++i) {
+ val = data[i];
+ outb(val & 0xff, ioreg);
+ outb((val >> 8) & 0xff, ioreg + 1);
+
+ /*
+ * Initiate transfer if not in simultaneaous xfer
+ * mode by reading one of the AO registers.
+ */
+ if (!devpriv->simultaneous_xfer_mode)
+ inb(ioreg);
+ }
+ s->readback[chan] = val;
+
+ return insn->n;
+}
+
+static int pcmda12_ao_insn_read(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ struct pcmda12_private *devpriv = dev->private;
+
+ /*
+ * Initiate simultaneaous xfer mode by reading one of the
+ * AO registers. All analog outputs will then be updated.
+ */
+ if (devpriv->simultaneous_xfer_mode)
+ inb(dev->iobase);
+
+ return comedi_readback_insn_read(dev, s, insn, data);
+}
+
+static void pcmda12_ao_reset(struct comedi_device *dev,
+ struct comedi_subdevice *s)
+{
+ int i;
+
+ for (i = 0; i < s->n_chan; ++i) {
+ outb(0, dev->iobase + (i * 2));
+ outb(0, dev->iobase + (i * 2) + 1);
+ }
+ /* Initiate transfer by reading one of the AO registers. */
+ inb(dev->iobase);
+}
+
+static int pcmda12_attach(struct comedi_device *dev,
+ struct comedi_devconfig *it)
+{
+ struct pcmda12_private *devpriv;
+ struct comedi_subdevice *s;
+ int ret;
+
+ ret = comedi_request_region(dev, it->options[0], 0x10);
+ if (ret)
+ return ret;
+
+ devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
+ if (!devpriv)
+ return -ENOMEM;
+
+ devpriv->simultaneous_xfer_mode = it->options[1];
+
+ ret = comedi_alloc_subdevices(dev, 1);
+ if (ret)
+ return ret;
+
+ s = &dev->subdevices[0];
+ s->type = COMEDI_SUBD_AO;
+ s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
+ s->n_chan = 8;
+ s->maxdata = 0x0fff;
+ s->range_table = &pcmda12_ranges;
+ s->insn_write = pcmda12_ao_insn_write;
+ s->insn_read = pcmda12_ao_insn_read;
+
+ ret = comedi_alloc_subdev_readback(s);
+ if (ret)
+ return ret;
+
+ pcmda12_ao_reset(dev, s);
+
+ return 0;
+}
+
+static struct comedi_driver pcmda12_driver = {
+ .driver_name = "pcmda12",
+ .module = THIS_MODULE,
+ .attach = pcmda12_attach,
+ .detach = comedi_legacy_detach,
+};
+module_comedi_driver(pcmda12_driver);
+
+MODULE_AUTHOR("Comedi http://www.comedi.org");
+MODULE_DESCRIPTION("Comedi low-level driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/comedi/drivers/pcmmio.c b/drivers/staging/comedi/drivers/pcmmio.c
new file mode 100644
index 000000000..10472e6dd
--- /dev/null
+++ b/drivers/staging/comedi/drivers/pcmmio.c
@@ -0,0 +1,786 @@
+/*
+ * pcmmio.c
+ * Driver for Winsystems PC-104 based multifunction IO board.
+ *
+ * COMEDI - Linux Control and Measurement Device Interface
+ * Copyright (C) 2007 Calin A. Culianu <calin@ajvar.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+/*
+ * Driver: pcmmio
+ * Description: A driver for the PCM-MIO multifunction board
+ * Devices: [Winsystems] PCM-MIO (pcmmio)
+ * Author: Calin Culianu <calin@ajvar.org>
+ * Updated: Wed, May 16 2007 16:21:10 -0500
+ * Status: works
+ *
+ * A driver for the PCM-MIO multifunction board from Winsystems. This
+ * is a PC-104 based I/O board. It contains four subdevices:
+ *
+ * subdevice 0 - 16 channels of 16-bit AI
+ * subdevice 1 - 8 channels of 16-bit AO
+ * subdevice 2 - first 24 channels of the 48 channel of DIO
+ * (with edge-triggered interrupt support)
+ * subdevice 3 - last 24 channels of the 48 channel DIO
+ * (no interrupt support for this bank of channels)
+ *
+ * Some notes:
+ *
+ * Synchronous reads and writes are the only things implemented for analog
+ * input and output. The hardware itself can do streaming acquisition, etc.
+ *
+ * Asynchronous I/O for the DIO subdevices *is* implemented, however! They
+ * are basically edge-triggered interrupts for any configuration of the
+ * channels in subdevice 2.
+ *
+ * Also note that this interrupt support is untested.
+ *
+ * A few words about edge-detection IRQ support (commands on DIO):
+ *
+ * To use edge-detection IRQ support for the DIO subdevice, pass the IRQ
+ * of the board to the comedi_config command. The board IRQ is not jumpered
+ * but rather configured through software, so any IRQ from 1-15 is OK.
+ *
+ * Due to the genericity of the comedi API, you need to create a special
+ * comedi_command in order to use edge-triggered interrupts for DIO.
+ *
+ * Use comedi_commands with TRIG_NOW. Your callback will be called each
+ * time an edge is detected on the specified DIO line(s), and the data
+ * values will be two sample_t's, which should be concatenated to form
+ * one 32-bit unsigned int. This value is the mask of channels that had
+ * edges detected from your channel list. Note that the bits positions
+ * in the mask correspond to positions in your chanlist when you
+ * specified the command and *not* channel id's!
+ *
+ * To set the polarity of the edge-detection interrupts pass a nonzero value
+ * for either CR_RANGE or CR_AREF for edge-up polarity, or a zero
+ * value for both CR_RANGE and CR_AREF if you want edge-down polarity.
+ *
+ * Configuration Options:
+ * [0] - I/O port base address
+ * [1] - IRQ (optional -- for edge-detect interrupt support only,
+ * leave out if you don't need this feature)
+ */
+
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/slab.h>
+
+#include "../comedidev.h"
+
+/*
+ * Register I/O map
+ */
+#define PCMMIO_AI_LSB_REG 0x00
+#define PCMMIO_AI_MSB_REG 0x01
+#define PCMMIO_AI_CMD_REG 0x02
+#define PCMMIO_AI_CMD_SE (1 << 7)
+#define PCMMIO_AI_CMD_ODD_CHAN (1 << 6)
+#define PCMMIO_AI_CMD_CHAN_SEL(x) (((x) & 0x3) << 4)
+#define PCMMIO_AI_CMD_RANGE(x) (((x) & 0x3) << 2)
+#define PCMMIO_RESOURCE_REG 0x02
+#define PCMMIO_RESOURCE_IRQ(x) (((x) & 0xf) << 0)
+#define PCMMIO_AI_STATUS_REG 0x03
+#define PCMMIO_AI_STATUS_DATA_READY (1 << 7)
+#define PCMMIO_AI_STATUS_DATA_DMA_PEND (1 << 6)
+#define PCMMIO_AI_STATUS_CMD_DMA_PEND (1 << 5)
+#define PCMMIO_AI_STATUS_IRQ_PEND (1 << 4)
+#define PCMMIO_AI_STATUS_DATA_DRQ_ENA (1 << 2)
+#define PCMMIO_AI_STATUS_REG_SEL (1 << 3)
+#define PCMMIO_AI_STATUS_CMD_DRQ_ENA (1 << 1)
+#define PCMMIO_AI_STATUS_IRQ_ENA (1 << 0)
+#define PCMMIO_AI_RES_ENA_REG 0x03
+#define PCMMIO_AI_RES_ENA_CMD_REG_ACCESS (0 << 3)
+#define PCMMIO_AI_RES_ENA_AI_RES_ACCESS (1 << 3)
+#define PCMMIO_AI_RES_ENA_DIO_RES_ACCESS (1 << 4)
+#define PCMMIO_AI_2ND_ADC_OFFSET 0x04
+
+#define PCMMIO_AO_LSB_REG 0x08
+#define PCMMIO_AO_LSB_SPAN(x) (((x) & 0xf) << 0)
+#define PCMMIO_AO_MSB_REG 0x09
+#define PCMMIO_AO_CMD_REG 0x0a
+#define PCMMIO_AO_CMD_WR_SPAN (0x2 << 4)
+#define PCMMIO_AO_CMD_WR_CODE (0x3 << 4)
+#define PCMMIO_AO_CMD_UPDATE (0x4 << 4)
+#define PCMMIO_AO_CMD_UPDATE_ALL (0x5 << 4)
+#define PCMMIO_AO_CMD_WR_SPAN_UPDATE (0x6 << 4)
+#define PCMMIO_AO_CMD_WR_CODE_UPDATE (0x7 << 4)
+#define PCMMIO_AO_CMD_WR_SPAN_UPDATE_ALL (0x8 << 4)
+#define PCMMIO_AO_CMD_WR_CODE_UPDATE_ALL (0x9 << 4)
+#define PCMMIO_AO_CMD_RD_B1_SPAN (0xa << 4)
+#define PCMMIO_AO_CMD_RD_B1_CODE (0xb << 4)
+#define PCMMIO_AO_CMD_RD_B2_SPAN (0xc << 4)
+#define PCMMIO_AO_CMD_RD_B2_CODE (0xd << 4)
+#define PCMMIO_AO_CMD_NOP (0xf << 4)
+#define PCMMIO_AO_CMD_CHAN_SEL(x) (((x) & 0x03) << 1)
+#define PCMMIO_AO_CMD_CHAN_SEL_ALL (0x0f << 0)
+#define PCMMIO_AO_STATUS_REG 0x0b
+#define PCMMIO_AO_STATUS_DATA_READY (1 << 7)
+#define PCMMIO_AO_STATUS_DATA_DMA_PEND (1 << 6)
+#define PCMMIO_AO_STATUS_CMD_DMA_PEND (1 << 5)
+#define PCMMIO_AO_STATUS_IRQ_PEND (1 << 4)
+#define PCMMIO_AO_STATUS_DATA_DRQ_ENA (1 << 2)
+#define PCMMIO_AO_STATUS_REG_SEL (1 << 3)
+#define PCMMIO_AO_STATUS_CMD_DRQ_ENA (1 << 1)
+#define PCMMIO_AO_STATUS_IRQ_ENA (1 << 0)
+#define PCMMIO_AO_RESOURCE_ENA_REG 0x0b
+#define PCMMIO_AO_2ND_DAC_OFFSET 0x04
+
+/*
+ * WinSystems WS16C48
+ *
+ * Offset Page 0 Page 1 Page 2 Page 3
+ * ------ ----------- ----------- ----------- -----------
+ * 0x10 Port 0 I/O Port 0 I/O Port 0 I/O Port 0 I/O
+ * 0x11 Port 1 I/O Port 1 I/O Port 1 I/O Port 1 I/O
+ * 0x12 Port 2 I/O Port 2 I/O Port 2 I/O Port 2 I/O
+ * 0x13 Port 3 I/O Port 3 I/O Port 3 I/O Port 3 I/O
+ * 0x14 Port 4 I/O Port 4 I/O Port 4 I/O Port 4 I/O
+ * 0x15 Port 5 I/O Port 5 I/O Port 5 I/O Port 5 I/O
+ * 0x16 INT_PENDING INT_PENDING INT_PENDING INT_PENDING
+ * 0x17 Page/Lock Page/Lock Page/Lock Page/Lock
+ * 0x18 N/A POL_0 ENAB_0 INT_ID0
+ * 0x19 N/A POL_1 ENAB_1 INT_ID1
+ * 0x1a N/A POL_2 ENAB_2 INT_ID2
+ */
+#define PCMMIO_PORT_REG(x) (0x10 + (x))
+#define PCMMIO_INT_PENDING_REG 0x16
+#define PCMMIO_PAGE_LOCK_REG 0x17
+#define PCMMIO_LOCK_PORT(x) ((1 << (x)) & 0x3f)
+#define PCMMIO_PAGE(x) (((x) & 0x3) << 6)
+#define PCMMIO_PAGE_MASK PCMUIO_PAGE(3)
+#define PCMMIO_PAGE_POL 1
+#define PCMMIO_PAGE_ENAB 2
+#define PCMMIO_PAGE_INT_ID 3
+#define PCMMIO_PAGE_REG(x) (0x18 + (x))
+
+static const struct comedi_lrange pcmmio_ai_ranges = {
+ 4, {
+ BIP_RANGE(5),
+ BIP_RANGE(10),
+ UNI_RANGE(5),
+ UNI_RANGE(10)
+ }
+};
+
+static const struct comedi_lrange pcmmio_ao_ranges = {
+ 6, {
+ UNI_RANGE(5),
+ UNI_RANGE(10),
+ BIP_RANGE(5),
+ BIP_RANGE(10),
+ BIP_RANGE(2.5),
+ RANGE(-2.5, 7.5)
+ }
+};
+
+struct pcmmio_private {
+ spinlock_t pagelock; /* protects the page registers */
+ spinlock_t spinlock; /* protects the member variables */
+ unsigned int enabled_mask;
+ unsigned int active:1;
+};
+
+static void pcmmio_dio_write(struct comedi_device *dev, unsigned int val,
+ int page, int port)
+{
+ struct pcmmio_private *devpriv = dev->private;
+ unsigned long iobase = dev->iobase;
+ unsigned long flags;
+
+ spin_lock_irqsave(&devpriv->pagelock, flags);
+ if (page == 0) {
+ /* Port registers are valid for any page */
+ outb(val & 0xff, iobase + PCMMIO_PORT_REG(port + 0));
+ outb((val >> 8) & 0xff, iobase + PCMMIO_PORT_REG(port + 1));
+ outb((val >> 16) & 0xff, iobase + PCMMIO_PORT_REG(port + 2));
+ } else {
+ outb(PCMMIO_PAGE(page), iobase + PCMMIO_PAGE_LOCK_REG);
+ outb(val & 0xff, iobase + PCMMIO_PAGE_REG(0));
+ outb((val >> 8) & 0xff, iobase + PCMMIO_PAGE_REG(1));
+ outb((val >> 16) & 0xff, iobase + PCMMIO_PAGE_REG(2));
+ }
+ spin_unlock_irqrestore(&devpriv->pagelock, flags);
+}
+
+static unsigned int pcmmio_dio_read(struct comedi_device *dev,
+ int page, int port)
+{
+ struct pcmmio_private *devpriv = dev->private;
+ unsigned long iobase = dev->iobase;
+ unsigned long flags;
+ unsigned int val;
+
+ spin_lock_irqsave(&devpriv->pagelock, flags);
+ if (page == 0) {
+ /* Port registers are valid for any page */
+ val = inb(iobase + PCMMIO_PORT_REG(port + 0));
+ val |= (inb(iobase + PCMMIO_PORT_REG(port + 1)) << 8);
+ val |= (inb(iobase + PCMMIO_PORT_REG(port + 2)) << 16);
+ } else {
+ outb(PCMMIO_PAGE(page), iobase + PCMMIO_PAGE_LOCK_REG);
+ val = inb(iobase + PCMMIO_PAGE_REG(0));
+ val |= (inb(iobase + PCMMIO_PAGE_REG(1)) << 8);
+ val |= (inb(iobase + PCMMIO_PAGE_REG(2)) << 16);
+ }
+ spin_unlock_irqrestore(&devpriv->pagelock, flags);
+
+ return val;
+}
+
+/*
+ * Each channel can be individually programmed for input or output.
+ * Writing a '0' to a channel causes the corresponding output pin
+ * to go to a high-z state (pulled high by an external 10K resistor).
+ * This allows it to be used as an input. When used in the input mode,
+ * a read reflects the inverted state of the I/O pin, such that a
+ * high on the pin will read as a '0' in the register. Writing a '1'
+ * to a bit position causes the pin to sink current (up to 12mA),
+ * effectively pulling it low.
+ */
+static int pcmmio_dio_insn_bits(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ /* subdevice 2 uses ports 0-2, subdevice 3 uses ports 3-5 */
+ int port = s->index == 2 ? 0 : 3;
+ unsigned int chanmask = (1 << s->n_chan) - 1;
+ unsigned int mask;
+ unsigned int val;
+
+ mask = comedi_dio_update_state(s, data);
+ if (mask) {
+ /*
+ * Outputs are inverted, invert the state and
+ * update the channels.
+ *
+ * The s->io_bits mask makes sure the input channels
+ * are '0' so that the outputs pins stay in a high
+ * z-state.
+ */
+ val = ~s->state & chanmask;
+ val &= s->io_bits;
+ pcmmio_dio_write(dev, val, 0, port);
+ }
+
+ /* get inverted state of the channels from the port */
+ val = pcmmio_dio_read(dev, 0, port);
+
+ /* return the true state of the channels */
+ data[1] = ~val & chanmask;
+
+ return insn->n;
+}
+
+static int pcmmio_dio_insn_config(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ /* subdevice 2 uses ports 0-2, subdevice 3 uses ports 3-5 */
+ int port = s->index == 2 ? 0 : 3;
+ int ret;
+
+ ret = comedi_dio_insn_config(dev, s, insn, data, 0);
+ if (ret)
+ return ret;
+
+ if (data[0] == INSN_CONFIG_DIO_INPUT)
+ pcmmio_dio_write(dev, s->io_bits, 0, port);
+
+ return insn->n;
+}
+
+static void pcmmio_reset(struct comedi_device *dev)
+{
+ /* Clear all the DIO port bits */
+ pcmmio_dio_write(dev, 0, 0, 0);
+ pcmmio_dio_write(dev, 0, 0, 3);
+
+ /* Clear all the paged registers */
+ pcmmio_dio_write(dev, 0, PCMMIO_PAGE_POL, 0);
+ pcmmio_dio_write(dev, 0, PCMMIO_PAGE_ENAB, 0);
+ pcmmio_dio_write(dev, 0, PCMMIO_PAGE_INT_ID, 0);
+}
+
+/* devpriv->spinlock is already locked */
+static void pcmmio_stop_intr(struct comedi_device *dev,
+ struct comedi_subdevice *s)
+{
+ struct pcmmio_private *devpriv = dev->private;
+
+ devpriv->enabled_mask = 0;
+ devpriv->active = 0;
+ s->async->inttrig = NULL;
+
+ /* disable all dio interrupts */
+ pcmmio_dio_write(dev, 0, PCMMIO_PAGE_ENAB, 0);
+}
+
+static void pcmmio_handle_dio_intr(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ unsigned int triggered)
+{
+ struct pcmmio_private *devpriv = dev->private;
+ struct comedi_cmd *cmd = &s->async->cmd;
+ unsigned int val = 0;
+ unsigned long flags;
+ int i;
+
+ spin_lock_irqsave(&devpriv->spinlock, flags);
+
+ if (!devpriv->active)
+ goto done;
+
+ if (!(triggered & devpriv->enabled_mask))
+ goto done;
+
+ for (i = 0; i < cmd->chanlist_len; i++) {
+ unsigned int chan = CR_CHAN(cmd->chanlist[i]);
+
+ if (triggered & (1 << chan))
+ val |= (1 << i);
+ }
+
+ comedi_buf_write_samples(s, &val, 1);
+
+ if (cmd->stop_src == TRIG_COUNT &&
+ s->async->scans_done >= cmd->stop_arg)
+ s->async->events |= COMEDI_CB_EOA;
+
+done:
+ spin_unlock_irqrestore(&devpriv->spinlock, flags);
+
+ comedi_handle_events(dev, s);
+}
+
+static irqreturn_t interrupt_pcmmio(int irq, void *d)
+{
+ struct comedi_device *dev = d;
+ struct comedi_subdevice *s = dev->read_subdev;
+ unsigned int triggered;
+ unsigned char int_pend;
+
+ /* are there any interrupts pending */
+ int_pend = inb(dev->iobase + PCMMIO_INT_PENDING_REG) & 0x07;
+ if (!int_pend)
+ return IRQ_NONE;
+
+ /* get, and clear, the pending interrupts */
+ triggered = pcmmio_dio_read(dev, PCMMIO_PAGE_INT_ID, 0);
+ pcmmio_dio_write(dev, 0, PCMMIO_PAGE_INT_ID, 0);
+
+ pcmmio_handle_dio_intr(dev, s, triggered);
+
+ return IRQ_HANDLED;
+}
+
+/* devpriv->spinlock is already locked */
+static void pcmmio_start_intr(struct comedi_device *dev,
+ struct comedi_subdevice *s)
+{
+ struct pcmmio_private *devpriv = dev->private;
+ struct comedi_cmd *cmd = &s->async->cmd;
+ unsigned int bits = 0;
+ unsigned int pol_bits = 0;
+ int i;
+
+ devpriv->enabled_mask = 0;
+ devpriv->active = 1;
+ if (cmd->chanlist) {
+ for (i = 0; i < cmd->chanlist_len; i++) {
+ unsigned int chanspec = cmd->chanlist[i];
+ unsigned int chan = CR_CHAN(chanspec);
+ unsigned int range = CR_RANGE(chanspec);
+ unsigned int aref = CR_AREF(chanspec);
+
+ bits |= (1 << chan);
+ pol_bits |= (((aref || range) ? 1 : 0) << chan);
+ }
+ }
+ bits &= ((1 << s->n_chan) - 1);
+ devpriv->enabled_mask = bits;
+
+ /* set polarity and enable interrupts */
+ pcmmio_dio_write(dev, pol_bits, PCMMIO_PAGE_POL, 0);
+ pcmmio_dio_write(dev, bits, PCMMIO_PAGE_ENAB, 0);
+}
+
+static int pcmmio_cancel(struct comedi_device *dev, struct comedi_subdevice *s)
+{
+ struct pcmmio_private *devpriv = dev->private;
+ unsigned long flags;
+
+ spin_lock_irqsave(&devpriv->spinlock, flags);
+ if (devpriv->active)
+ pcmmio_stop_intr(dev, s);
+ spin_unlock_irqrestore(&devpriv->spinlock, flags);
+
+ return 0;
+}
+
+static int pcmmio_inttrig_start_intr(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ unsigned int trig_num)
+{
+ struct pcmmio_private *devpriv = dev->private;
+ struct comedi_cmd *cmd = &s->async->cmd;
+ unsigned long flags;
+
+ if (trig_num != cmd->start_arg)
+ return -EINVAL;
+
+ spin_lock_irqsave(&devpriv->spinlock, flags);
+ s->async->inttrig = NULL;
+ if (devpriv->active)
+ pcmmio_start_intr(dev, s);
+ spin_unlock_irqrestore(&devpriv->spinlock, flags);
+
+ return 1;
+}
+
+/*
+ * 'do_cmd' function for an 'INTERRUPT' subdevice.
+ */
+static int pcmmio_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
+{
+ struct pcmmio_private *devpriv = dev->private;
+ struct comedi_cmd *cmd = &s->async->cmd;
+ unsigned long flags;
+
+ spin_lock_irqsave(&devpriv->spinlock, flags);
+ devpriv->active = 1;
+
+ /* Set up start of acquisition. */
+ if (cmd->start_src == TRIG_INT)
+ s->async->inttrig = pcmmio_inttrig_start_intr;
+ else /* TRIG_NOW */
+ pcmmio_start_intr(dev, s);
+
+ spin_unlock_irqrestore(&devpriv->spinlock, flags);
+
+ return 0;
+}
+
+static int pcmmio_cmdtest(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_cmd *cmd)
+{
+ int err = 0;
+
+ /* Step 1 : check if triggers are trivially valid */
+
+ err |= comedi_check_trigger_src(&cmd->start_src, TRIG_NOW | TRIG_INT);
+ err |= comedi_check_trigger_src(&cmd->scan_begin_src, TRIG_EXT);
+ err |= comedi_check_trigger_src(&cmd->convert_src, TRIG_NOW);
+ err |= comedi_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
+ err |= comedi_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE);
+
+ if (err)
+ return 1;
+
+ /* Step 2a : make sure trigger sources are unique */
+
+ err |= comedi_check_trigger_is_unique(cmd->start_src);
+ err |= comedi_check_trigger_is_unique(cmd->stop_src);
+
+ /* Step 2b : and mutually compatible */
+
+ if (err)
+ return 2;
+
+ /* Step 3: check if arguments are trivially valid */
+
+ err |= comedi_check_trigger_arg_is(&cmd->start_arg, 0);
+ err |= comedi_check_trigger_arg_is(&cmd->scan_begin_arg, 0);
+ err |= comedi_check_trigger_arg_is(&cmd->convert_arg, 0);
+ err |= comedi_check_trigger_arg_is(&cmd->scan_end_arg,
+ cmd->chanlist_len);
+
+ if (cmd->stop_src == TRIG_COUNT)
+ err |= comedi_check_trigger_arg_min(&cmd->stop_arg, 1);
+ else /* TRIG_NONE */
+ err |= comedi_check_trigger_arg_is(&cmd->stop_arg, 0);
+
+ if (err)
+ return 3;
+
+ /* step 4: fix up any arguments */
+
+ /* if (err) return 4; */
+
+ return 0;
+}
+
+static int pcmmio_ai_eoc(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned long context)
+{
+ unsigned char status;
+
+ status = inb(dev->iobase + PCMMIO_AI_STATUS_REG);
+ if (status & PCMMIO_AI_STATUS_DATA_READY)
+ return 0;
+ return -EBUSY;
+}
+
+static int pcmmio_ai_insn_read(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ unsigned long iobase = dev->iobase;
+ unsigned int chan = CR_CHAN(insn->chanspec);
+ unsigned int range = CR_RANGE(insn->chanspec);
+ unsigned int aref = CR_AREF(insn->chanspec);
+ unsigned char cmd = 0;
+ unsigned int val;
+ int ret;
+ int i;
+
+ /*
+ * The PCM-MIO uses two Linear Tech LTC1859CG 8-channel A/D converters.
+ * The devices use a full duplex serial interface which transmits and
+ * receives data simultaneously. An 8-bit command is shifted into the
+ * ADC interface to configure it for the next conversion. At the same
+ * time, the data from the previous conversion is shifted out of the
+ * device. Consequently, the conversion result is delayed by one
+ * conversion from the command word.
+ *
+ * Setup the cmd for the conversions then do a dummy conversion to
+ * flush the junk data. Then do each conversion requested by the
+ * comedi_insn. Note that the last conversion will leave junk data
+ * in ADC which will get flushed on the next comedi_insn.
+ */
+
+ if (chan > 7) {
+ chan -= 8;
+ iobase += PCMMIO_AI_2ND_ADC_OFFSET;
+ }
+
+ if (aref == AREF_GROUND)
+ cmd |= PCMMIO_AI_CMD_SE;
+ if (chan % 2)
+ cmd |= PCMMIO_AI_CMD_ODD_CHAN;
+ cmd |= PCMMIO_AI_CMD_CHAN_SEL(chan / 2);
+ cmd |= PCMMIO_AI_CMD_RANGE(range);
+
+ outb(cmd, iobase + PCMMIO_AI_CMD_REG);
+
+ ret = comedi_timeout(dev, s, insn, pcmmio_ai_eoc, 0);
+ if (ret)
+ return ret;
+
+ val = inb(iobase + PCMMIO_AI_LSB_REG);
+ val |= inb(iobase + PCMMIO_AI_MSB_REG) << 8;
+
+ for (i = 0; i < insn->n; i++) {
+ outb(cmd, iobase + PCMMIO_AI_CMD_REG);
+
+ ret = comedi_timeout(dev, s, insn, pcmmio_ai_eoc, 0);
+ if (ret)
+ return ret;
+
+ val = inb(iobase + PCMMIO_AI_LSB_REG);
+ val |= inb(iobase + PCMMIO_AI_MSB_REG) << 8;
+
+ /* bipolar data is two's complement */
+ if (comedi_range_is_bipolar(s, range))
+ val = comedi_offset_munge(s, val);
+
+ data[i] = val;
+ }
+
+ return insn->n;
+}
+
+static int pcmmio_ao_eoc(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned long context)
+{
+ unsigned char status;
+
+ status = inb(dev->iobase + PCMMIO_AO_STATUS_REG);
+ if (status & PCMMIO_AO_STATUS_DATA_READY)
+ return 0;
+ return -EBUSY;
+}
+
+static int pcmmio_ao_insn_write(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ unsigned long iobase = dev->iobase;
+ unsigned int chan = CR_CHAN(insn->chanspec);
+ unsigned int range = CR_RANGE(insn->chanspec);
+ unsigned char cmd = 0;
+ int ret;
+ int i;
+
+ /*
+ * The PCM-MIO has two Linear Tech LTC2704 DAC devices. Each device
+ * is a 4-channel converter with software-selectable output range.
+ */
+
+ if (chan > 3) {
+ cmd |= PCMMIO_AO_CMD_CHAN_SEL(chan - 4);
+ iobase += PCMMIO_AO_2ND_DAC_OFFSET;
+ } else {
+ cmd |= PCMMIO_AO_CMD_CHAN_SEL(chan);
+ }
+
+ /* set the range for the channel */
+ outb(PCMMIO_AO_LSB_SPAN(range), iobase + PCMMIO_AO_LSB_REG);
+ outb(0, iobase + PCMMIO_AO_MSB_REG);
+ outb(cmd | PCMMIO_AO_CMD_WR_SPAN_UPDATE, iobase + PCMMIO_AO_CMD_REG);
+
+ ret = comedi_timeout(dev, s, insn, pcmmio_ao_eoc, 0);
+ if (ret)
+ return ret;
+
+ for (i = 0; i < insn->n; i++) {
+ unsigned int val = data[i];
+
+ /* write the data to the channel */
+ outb(val & 0xff, iobase + PCMMIO_AO_LSB_REG);
+ outb((val >> 8) & 0xff, iobase + PCMMIO_AO_MSB_REG);
+ outb(cmd | PCMMIO_AO_CMD_WR_CODE_UPDATE,
+ iobase + PCMMIO_AO_CMD_REG);
+
+ ret = comedi_timeout(dev, s, insn, pcmmio_ao_eoc, 0);
+ if (ret)
+ return ret;
+
+ s->readback[chan] = val;
+ }
+
+ return insn->n;
+}
+
+static int pcmmio_attach(struct comedi_device *dev, struct comedi_devconfig *it)
+{
+ struct pcmmio_private *devpriv;
+ struct comedi_subdevice *s;
+ int ret;
+
+ ret = comedi_request_region(dev, it->options[0], 32);
+ if (ret)
+ return ret;
+
+ devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
+ if (!devpriv)
+ return -ENOMEM;
+
+ spin_lock_init(&devpriv->pagelock);
+ spin_lock_init(&devpriv->spinlock);
+
+ pcmmio_reset(dev);
+
+ if (it->options[1]) {
+ ret = request_irq(it->options[1], interrupt_pcmmio, 0,
+ dev->board_name, dev);
+ if (ret == 0) {
+ dev->irq = it->options[1];
+
+ /* configure the interrupt routing on the board */
+ outb(PCMMIO_AI_RES_ENA_DIO_RES_ACCESS,
+ dev->iobase + PCMMIO_AI_RES_ENA_REG);
+ outb(PCMMIO_RESOURCE_IRQ(dev->irq),
+ dev->iobase + PCMMIO_RESOURCE_REG);
+ }
+ }
+
+ ret = comedi_alloc_subdevices(dev, 4);
+ if (ret)
+ return ret;
+
+ /* Analog Input subdevice */
+ s = &dev->subdevices[0];
+ s->type = COMEDI_SUBD_AI;
+ s->subdev_flags = SDF_READABLE | SDF_GROUND | SDF_DIFF;
+ s->n_chan = 16;
+ s->maxdata = 0xffff;
+ s->range_table = &pcmmio_ai_ranges;
+ s->insn_read = pcmmio_ai_insn_read;
+
+ /* initialize the resource enable register by clearing it */
+ outb(PCMMIO_AI_RES_ENA_CMD_REG_ACCESS,
+ dev->iobase + PCMMIO_AI_RES_ENA_REG);
+ outb(PCMMIO_AI_RES_ENA_CMD_REG_ACCESS,
+ dev->iobase + PCMMIO_AI_RES_ENA_REG + PCMMIO_AI_2ND_ADC_OFFSET);
+
+ /* Analog Output subdevice */
+ s = &dev->subdevices[1];
+ s->type = COMEDI_SUBD_AO;
+ s->subdev_flags = SDF_READABLE;
+ s->n_chan = 8;
+ s->maxdata = 0xffff;
+ s->range_table = &pcmmio_ao_ranges;
+ s->insn_write = pcmmio_ao_insn_write;
+
+ ret = comedi_alloc_subdev_readback(s);
+ if (ret)
+ return ret;
+
+ /* initialize the resource enable register by clearing it */
+ outb(0, dev->iobase + PCMMIO_AO_RESOURCE_ENA_REG);
+ outb(0, dev->iobase + PCMMIO_AO_2ND_DAC_OFFSET +
+ PCMMIO_AO_RESOURCE_ENA_REG);
+
+ /* Digital I/O subdevice with interrupt support */
+ s = &dev->subdevices[2];
+ s->type = COMEDI_SUBD_DIO;
+ s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
+ s->n_chan = 24;
+ s->maxdata = 1;
+ s->len_chanlist = 1;
+ s->range_table = &range_digital;
+ s->insn_bits = pcmmio_dio_insn_bits;
+ s->insn_config = pcmmio_dio_insn_config;
+ if (dev->irq) {
+ dev->read_subdev = s;
+ s->subdev_flags |= SDF_CMD_READ | SDF_LSAMPL | SDF_PACKED;
+ s->len_chanlist = s->n_chan;
+ s->cancel = pcmmio_cancel;
+ s->do_cmd = pcmmio_cmd;
+ s->do_cmdtest = pcmmio_cmdtest;
+ }
+
+ /* Digital I/O subdevice */
+ s = &dev->subdevices[3];
+ s->type = COMEDI_SUBD_DIO;
+ s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
+ s->n_chan = 24;
+ s->maxdata = 1;
+ s->range_table = &range_digital;
+ s->insn_bits = pcmmio_dio_insn_bits;
+ s->insn_config = pcmmio_dio_insn_config;
+
+ return 0;
+}
+
+static struct comedi_driver pcmmio_driver = {
+ .driver_name = "pcmmio",
+ .module = THIS_MODULE,
+ .attach = pcmmio_attach,
+ .detach = comedi_legacy_detach,
+};
+module_comedi_driver(pcmmio_driver);
+
+MODULE_AUTHOR("Comedi http://www.comedi.org");
+MODULE_DESCRIPTION("Comedi driver for Winsystems PCM-MIO PC/104 board");
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/comedi/drivers/pcmuio.c b/drivers/staging/comedi/drivers/pcmuio.c
new file mode 100644
index 000000000..7ea813022
--- /dev/null
+++ b/drivers/staging/comedi/drivers/pcmuio.c
@@ -0,0 +1,633 @@
+/*
+ * pcmuio.c
+ * Comedi driver for Winsystems PC-104 based 48/96-channel DIO boards.
+ *
+ * COMEDI - Linux Control and Measurement Device Interface
+ * Copyright (C) 2006 Calin A. Culianu <calin@ajvar.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+/*
+ * Driver: pcmuio
+ * Description: Winsystems PC-104 based 48/96-channel DIO boards.
+ * Devices: [Winsystems] PCM-UIO48A (pcmuio48), PCM-UIO96A (pcmuio96)
+ * Author: Calin Culianu <calin@ajvar.org>
+ * Updated: Fri, 13 Jan 2006 12:01:01 -0500
+ * Status: works
+ *
+ * A driver for the relatively straightforward-to-program PCM-UIO48A and
+ * PCM-UIO96A boards from Winsystems. These boards use either one or two
+ * (in the 96-DIO version) WS16C48 ASIC HighDensity I/O Chips (HDIO). This
+ * chip is interesting in that each I/O line is individually programmable
+ * for INPUT or OUTPUT (thus comedi_dio_config can be done on a per-channel
+ * basis). Also, each chip supports edge-triggered interrupts for the first
+ * 24 I/O lines. Of course, since the 96-channel version of the board has
+ * two ASICs, it can detect polarity changes on up to 48 I/O lines. Since
+ * this is essentially an (non-PnP) ISA board, I/O Address and IRQ selection
+ * are done through jumpers on the board. You need to pass that information
+ * to this driver as the first and second comedi_config option, respectively.
+ * Note that the 48-channel version uses 16 bytes of IO memory and the 96-
+ * channel version uses 32-bytes (in case you are worried about conflicts).
+ * The 48-channel board is split into two 24-channel comedi subdevices. The
+ * 96-channel board is split into 4 24-channel DIO subdevices.
+ *
+ * Note that IRQ support has been added, but it is untested.
+ *
+ * To use edge-detection IRQ support, pass the IRQs of both ASICS (for the
+ * 96 channel version) or just 1 ASIC (for 48-channel version). Then, use
+ * comedi_commands with TRIG_NOW. Your callback will be called each time an
+ * edge is triggered, and the data values will be two sample_t's, which
+ * should be concatenated to form one 32-bit unsigned int. This value is
+ * the mask of channels that had edges detected from your channel list. Note
+ * that the bits positions in the mask correspond to positions in your
+ * chanlist when you specified the command and *not* channel id's!
+ *
+ * To set the polarity of the edge-detection interrupts pass a nonzero value
+ * for either CR_RANGE or CR_AREF for edge-up polarity, or a zero value for
+ * both CR_RANGE and CR_AREF if you want edge-down polarity.
+ *
+ * In the 48-channel version:
+ *
+ * On subdev 0, the first 24 channels channels are edge-detect channels.
+ *
+ * In the 96-channel board you have the following channels that can do edge
+ * detection:
+ *
+ * subdev 0, channels 0-24 (first 24 channels of 1st ASIC)
+ * subdev 2, channels 0-24 (first 24 channels of 2nd ASIC)
+ *
+ * Configuration Options:
+ * [0] - I/O port base address
+ * [1] - IRQ (for first ASIC, or first 24 channels)
+ * [2] - IRQ (for second ASIC, pcmuio96 only - IRQ for chans 48-72
+ * can be the same as first irq!)
+ */
+
+#include <linux/module.h>
+#include <linux/interrupt.h>
+
+#include "../comedidev.h"
+
+/*
+ * Register I/O map
+ *
+ * Offset Page 0 Page 1 Page 2 Page 3
+ * ------ ----------- ----------- ----------- -----------
+ * 0x00 Port 0 I/O Port 0 I/O Port 0 I/O Port 0 I/O
+ * 0x01 Port 1 I/O Port 1 I/O Port 1 I/O Port 1 I/O
+ * 0x02 Port 2 I/O Port 2 I/O Port 2 I/O Port 2 I/O
+ * 0x03 Port 3 I/O Port 3 I/O Port 3 I/O Port 3 I/O
+ * 0x04 Port 4 I/O Port 4 I/O Port 4 I/O Port 4 I/O
+ * 0x05 Port 5 I/O Port 5 I/O Port 5 I/O Port 5 I/O
+ * 0x06 INT_PENDING INT_PENDING INT_PENDING INT_PENDING
+ * 0x07 Page/Lock Page/Lock Page/Lock Page/Lock
+ * 0x08 N/A POL_0 ENAB_0 INT_ID0
+ * 0x09 N/A POL_1 ENAB_1 INT_ID1
+ * 0x0a N/A POL_2 ENAB_2 INT_ID2
+ */
+#define PCMUIO_PORT_REG(x) (0x00 + (x))
+#define PCMUIO_INT_PENDING_REG 0x06
+#define PCMUIO_PAGE_LOCK_REG 0x07
+#define PCMUIO_LOCK_PORT(x) ((1 << (x)) & 0x3f)
+#define PCMUIO_PAGE(x) (((x) & 0x3) << 6)
+#define PCMUIO_PAGE_MASK PCMUIO_PAGE(3)
+#define PCMUIO_PAGE_POL 1
+#define PCMUIO_PAGE_ENAB 2
+#define PCMUIO_PAGE_INT_ID 3
+#define PCMUIO_PAGE_REG(x) (0x08 + (x))
+
+#define PCMUIO_ASIC_IOSIZE 0x10
+#define PCMUIO_MAX_ASICS 2
+
+struct pcmuio_board {
+ const char *name;
+ const int num_asics;
+};
+
+static const struct pcmuio_board pcmuio_boards[] = {
+ {
+ .name = "pcmuio48",
+ .num_asics = 1,
+ }, {
+ .name = "pcmuio96",
+ .num_asics = 2,
+ },
+};
+
+struct pcmuio_asic {
+ spinlock_t pagelock; /* protects the page registers */
+ spinlock_t spinlock; /* protects member variables */
+ unsigned int enabled_mask;
+ unsigned int active:1;
+};
+
+struct pcmuio_private {
+ struct pcmuio_asic asics[PCMUIO_MAX_ASICS];
+ unsigned int irq2;
+};
+
+static inline unsigned long pcmuio_asic_iobase(struct comedi_device *dev,
+ int asic)
+{
+ return dev->iobase + (asic * PCMUIO_ASIC_IOSIZE);
+}
+
+static inline int pcmuio_subdevice_to_asic(struct comedi_subdevice *s)
+{
+ /*
+ * subdevice 0 and 1 are handled by the first asic
+ * subdevice 2 and 3 are handled by the second asic
+ */
+ return s->index / 2;
+}
+
+static inline int pcmuio_subdevice_to_port(struct comedi_subdevice *s)
+{
+ /*
+ * subdevice 0 and 2 use port registers 0-2
+ * subdevice 1 and 3 use port registers 3-5
+ */
+ return (s->index % 2) ? 3 : 0;
+}
+
+static void pcmuio_write(struct comedi_device *dev, unsigned int val,
+ int asic, int page, int port)
+{
+ struct pcmuio_private *devpriv = dev->private;
+ struct pcmuio_asic *chip = &devpriv->asics[asic];
+ unsigned long iobase = pcmuio_asic_iobase(dev, asic);
+ unsigned long flags;
+
+ spin_lock_irqsave(&chip->pagelock, flags);
+ if (page == 0) {
+ /* Port registers are valid for any page */
+ outb(val & 0xff, iobase + PCMUIO_PORT_REG(port + 0));
+ outb((val >> 8) & 0xff, iobase + PCMUIO_PORT_REG(port + 1));
+ outb((val >> 16) & 0xff, iobase + PCMUIO_PORT_REG(port + 2));
+ } else {
+ outb(PCMUIO_PAGE(page), iobase + PCMUIO_PAGE_LOCK_REG);
+ outb(val & 0xff, iobase + PCMUIO_PAGE_REG(0));
+ outb((val >> 8) & 0xff, iobase + PCMUIO_PAGE_REG(1));
+ outb((val >> 16) & 0xff, iobase + PCMUIO_PAGE_REG(2));
+ }
+ spin_unlock_irqrestore(&chip->pagelock, flags);
+}
+
+static unsigned int pcmuio_read(struct comedi_device *dev,
+ int asic, int page, int port)
+{
+ struct pcmuio_private *devpriv = dev->private;
+ struct pcmuio_asic *chip = &devpriv->asics[asic];
+ unsigned long iobase = pcmuio_asic_iobase(dev, asic);
+ unsigned long flags;
+ unsigned int val;
+
+ spin_lock_irqsave(&chip->pagelock, flags);
+ if (page == 0) {
+ /* Port registers are valid for any page */
+ val = inb(iobase + PCMUIO_PORT_REG(port + 0));
+ val |= (inb(iobase + PCMUIO_PORT_REG(port + 1)) << 8);
+ val |= (inb(iobase + PCMUIO_PORT_REG(port + 2)) << 16);
+ } else {
+ outb(PCMUIO_PAGE(page), iobase + PCMUIO_PAGE_LOCK_REG);
+ val = inb(iobase + PCMUIO_PAGE_REG(0));
+ val |= (inb(iobase + PCMUIO_PAGE_REG(1)) << 8);
+ val |= (inb(iobase + PCMUIO_PAGE_REG(2)) << 16);
+ }
+ spin_unlock_irqrestore(&chip->pagelock, flags);
+
+ return val;
+}
+
+/*
+ * Each channel can be individually programmed for input or output.
+ * Writing a '0' to a channel causes the corresponding output pin
+ * to go to a high-z state (pulled high by an external 10K resistor).
+ * This allows it to be used as an input. When used in the input mode,
+ * a read reflects the inverted state of the I/O pin, such that a
+ * high on the pin will read as a '0' in the register. Writing a '1'
+ * to a bit position causes the pin to sink current (up to 12mA),
+ * effectively pulling it low.
+ */
+static int pcmuio_dio_insn_bits(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ int asic = pcmuio_subdevice_to_asic(s);
+ int port = pcmuio_subdevice_to_port(s);
+ unsigned int chanmask = (1 << s->n_chan) - 1;
+ unsigned int mask;
+ unsigned int val;
+
+ mask = comedi_dio_update_state(s, data);
+ if (mask) {
+ /*
+ * Outputs are inverted, invert the state and
+ * update the channels.
+ *
+ * The s->io_bits mask makes sure the input channels
+ * are '0' so that the outputs pins stay in a high
+ * z-state.
+ */
+ val = ~s->state & chanmask;
+ val &= s->io_bits;
+ pcmuio_write(dev, val, asic, 0, port);
+ }
+
+ /* get inverted state of the channels from the port */
+ val = pcmuio_read(dev, asic, 0, port);
+
+ /* return the true state of the channels */
+ data[1] = ~val & chanmask;
+
+ return insn->n;
+}
+
+static int pcmuio_dio_insn_config(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ int asic = pcmuio_subdevice_to_asic(s);
+ int port = pcmuio_subdevice_to_port(s);
+ int ret;
+
+ ret = comedi_dio_insn_config(dev, s, insn, data, 0);
+ if (ret)
+ return ret;
+
+ if (data[0] == INSN_CONFIG_DIO_INPUT)
+ pcmuio_write(dev, s->io_bits, asic, 0, port);
+
+ return insn->n;
+}
+
+static void pcmuio_reset(struct comedi_device *dev)
+{
+ const struct pcmuio_board *board = dev->board_ptr;
+ int asic;
+
+ for (asic = 0; asic < board->num_asics; ++asic) {
+ /* first, clear all the DIO port bits */
+ pcmuio_write(dev, 0, asic, 0, 0);
+ pcmuio_write(dev, 0, asic, 0, 3);
+
+ /* Next, clear all the paged registers for each page */
+ pcmuio_write(dev, 0, asic, PCMUIO_PAGE_POL, 0);
+ pcmuio_write(dev, 0, asic, PCMUIO_PAGE_ENAB, 0);
+ pcmuio_write(dev, 0, asic, PCMUIO_PAGE_INT_ID, 0);
+ }
+}
+
+/* chip->spinlock is already locked */
+static void pcmuio_stop_intr(struct comedi_device *dev,
+ struct comedi_subdevice *s)
+{
+ struct pcmuio_private *devpriv = dev->private;
+ int asic = pcmuio_subdevice_to_asic(s);
+ struct pcmuio_asic *chip = &devpriv->asics[asic];
+
+ chip->enabled_mask = 0;
+ chip->active = 0;
+ s->async->inttrig = NULL;
+
+ /* disable all intrs for this subdev.. */
+ pcmuio_write(dev, 0, asic, PCMUIO_PAGE_ENAB, 0);
+}
+
+static void pcmuio_handle_intr_subdev(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ unsigned triggered)
+{
+ struct pcmuio_private *devpriv = dev->private;
+ int asic = pcmuio_subdevice_to_asic(s);
+ struct pcmuio_asic *chip = &devpriv->asics[asic];
+ struct comedi_cmd *cmd = &s->async->cmd;
+ unsigned int val = 0;
+ unsigned long flags;
+ unsigned int i;
+
+ spin_lock_irqsave(&chip->spinlock, flags);
+
+ if (!chip->active)
+ goto done;
+
+ if (!(triggered & chip->enabled_mask))
+ goto done;
+
+ for (i = 0; i < cmd->chanlist_len; i++) {
+ unsigned int chan = CR_CHAN(cmd->chanlist[i]);
+
+ if (triggered & (1 << chan))
+ val |= (1 << i);
+ }
+
+ comedi_buf_write_samples(s, &val, 1);
+
+ if (cmd->stop_src == TRIG_COUNT &&
+ s->async->scans_done >= cmd->stop_arg)
+ s->async->events |= COMEDI_CB_EOA;
+
+done:
+ spin_unlock_irqrestore(&chip->spinlock, flags);
+
+ comedi_handle_events(dev, s);
+}
+
+static int pcmuio_handle_asic_interrupt(struct comedi_device *dev, int asic)
+{
+ /* there are could be two asics so we can't use dev->read_subdev */
+ struct comedi_subdevice *s = &dev->subdevices[asic * 2];
+ unsigned long iobase = pcmuio_asic_iobase(dev, asic);
+ unsigned int val;
+
+ /* are there any interrupts pending */
+ val = inb(iobase + PCMUIO_INT_PENDING_REG) & 0x07;
+ if (!val)
+ return 0;
+
+ /* get, and clear, the pending interrupts */
+ val = pcmuio_read(dev, asic, PCMUIO_PAGE_INT_ID, 0);
+ pcmuio_write(dev, 0, asic, PCMUIO_PAGE_INT_ID, 0);
+
+ /* handle the pending interrupts */
+ pcmuio_handle_intr_subdev(dev, s, val);
+
+ return 1;
+}
+
+static irqreturn_t pcmuio_interrupt(int irq, void *d)
+{
+ struct comedi_device *dev = d;
+ struct pcmuio_private *devpriv = dev->private;
+ int handled = 0;
+
+ if (irq == dev->irq)
+ handled += pcmuio_handle_asic_interrupt(dev, 0);
+ if (irq == devpriv->irq2)
+ handled += pcmuio_handle_asic_interrupt(dev, 1);
+
+ return handled ? IRQ_HANDLED : IRQ_NONE;
+}
+
+/* chip->spinlock is already locked */
+static void pcmuio_start_intr(struct comedi_device *dev,
+ struct comedi_subdevice *s)
+{
+ struct pcmuio_private *devpriv = dev->private;
+ int asic = pcmuio_subdevice_to_asic(s);
+ struct pcmuio_asic *chip = &devpriv->asics[asic];
+ struct comedi_cmd *cmd = &s->async->cmd;
+ unsigned int bits = 0;
+ unsigned int pol_bits = 0;
+ int i;
+
+ chip->enabled_mask = 0;
+ chip->active = 1;
+ if (cmd->chanlist) {
+ for (i = 0; i < cmd->chanlist_len; i++) {
+ unsigned int chanspec = cmd->chanlist[i];
+ unsigned int chan = CR_CHAN(chanspec);
+ unsigned int range = CR_RANGE(chanspec);
+ unsigned int aref = CR_AREF(chanspec);
+
+ bits |= (1 << chan);
+ pol_bits |= ((aref || range) ? 1 : 0) << chan;
+ }
+ }
+ bits &= ((1 << s->n_chan) - 1);
+ chip->enabled_mask = bits;
+
+ /* set pol and enab intrs for this subdev.. */
+ pcmuio_write(dev, pol_bits, asic, PCMUIO_PAGE_POL, 0);
+ pcmuio_write(dev, bits, asic, PCMUIO_PAGE_ENAB, 0);
+}
+
+static int pcmuio_cancel(struct comedi_device *dev, struct comedi_subdevice *s)
+{
+ struct pcmuio_private *devpriv = dev->private;
+ int asic = pcmuio_subdevice_to_asic(s);
+ struct pcmuio_asic *chip = &devpriv->asics[asic];
+ unsigned long flags;
+
+ spin_lock_irqsave(&chip->spinlock, flags);
+ if (chip->active)
+ pcmuio_stop_intr(dev, s);
+ spin_unlock_irqrestore(&chip->spinlock, flags);
+
+ return 0;
+}
+
+static int pcmuio_inttrig_start_intr(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ unsigned int trig_num)
+{
+ struct pcmuio_private *devpriv = dev->private;
+ struct comedi_cmd *cmd = &s->async->cmd;
+ int asic = pcmuio_subdevice_to_asic(s);
+ struct pcmuio_asic *chip = &devpriv->asics[asic];
+ unsigned long flags;
+
+ if (trig_num != cmd->start_arg)
+ return -EINVAL;
+
+ spin_lock_irqsave(&chip->spinlock, flags);
+ s->async->inttrig = NULL;
+ if (chip->active)
+ pcmuio_start_intr(dev, s);
+
+ spin_unlock_irqrestore(&chip->spinlock, flags);
+
+ return 1;
+}
+
+/*
+ * 'do_cmd' function for an 'INTERRUPT' subdevice.
+ */
+static int pcmuio_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
+{
+ struct pcmuio_private *devpriv = dev->private;
+ struct comedi_cmd *cmd = &s->async->cmd;
+ int asic = pcmuio_subdevice_to_asic(s);
+ struct pcmuio_asic *chip = &devpriv->asics[asic];
+ unsigned long flags;
+
+ spin_lock_irqsave(&chip->spinlock, flags);
+ chip->active = 1;
+
+ /* Set up start of acquisition. */
+ if (cmd->start_src == TRIG_INT)
+ s->async->inttrig = pcmuio_inttrig_start_intr;
+ else /* TRIG_NOW */
+ pcmuio_start_intr(dev, s);
+
+ spin_unlock_irqrestore(&chip->spinlock, flags);
+
+ return 0;
+}
+
+static int pcmuio_cmdtest(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_cmd *cmd)
+{
+ int err = 0;
+
+ /* Step 1 : check if triggers are trivially valid */
+
+ err |= comedi_check_trigger_src(&cmd->start_src, TRIG_NOW | TRIG_INT);
+ err |= comedi_check_trigger_src(&cmd->scan_begin_src, TRIG_EXT);
+ err |= comedi_check_trigger_src(&cmd->convert_src, TRIG_NOW);
+ err |= comedi_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
+ err |= comedi_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE);
+
+ if (err)
+ return 1;
+
+ /* Step 2a : make sure trigger sources are unique */
+
+ err |= comedi_check_trigger_is_unique(cmd->start_src);
+ err |= comedi_check_trigger_is_unique(cmd->stop_src);
+
+ /* Step 2b : and mutually compatible */
+
+ if (err)
+ return 2;
+
+ /* Step 3: check if arguments are trivially valid */
+
+ err |= comedi_check_trigger_arg_is(&cmd->start_arg, 0);
+ err |= comedi_check_trigger_arg_is(&cmd->scan_begin_arg, 0);
+ err |= comedi_check_trigger_arg_is(&cmd->convert_arg, 0);
+ err |= comedi_check_trigger_arg_is(&cmd->scan_end_arg,
+ cmd->chanlist_len);
+
+ if (cmd->stop_src == TRIG_COUNT)
+ err |= comedi_check_trigger_arg_min(&cmd->stop_arg, 1);
+ else /* TRIG_NONE */
+ err |= comedi_check_trigger_arg_is(&cmd->stop_arg, 0);
+
+ if (err)
+ return 3;
+
+ /* step 4: fix up any arguments */
+
+ /* if (err) return 4; */
+
+ return 0;
+}
+
+static int pcmuio_attach(struct comedi_device *dev, struct comedi_devconfig *it)
+{
+ const struct pcmuio_board *board = dev->board_ptr;
+ struct comedi_subdevice *s;
+ struct pcmuio_private *devpriv;
+ int ret;
+ int i;
+
+ ret = comedi_request_region(dev, it->options[0],
+ board->num_asics * PCMUIO_ASIC_IOSIZE);
+ if (ret)
+ return ret;
+
+ devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
+ if (!devpriv)
+ return -ENOMEM;
+
+ for (i = 0; i < PCMUIO_MAX_ASICS; ++i) {
+ struct pcmuio_asic *chip = &devpriv->asics[i];
+
+ spin_lock_init(&chip->pagelock);
+ spin_lock_init(&chip->spinlock);
+ }
+
+ pcmuio_reset(dev);
+
+ if (it->options[1]) {
+ /* request the irq for the 1st asic */
+ ret = request_irq(it->options[1], pcmuio_interrupt, 0,
+ dev->board_name, dev);
+ if (ret == 0)
+ dev->irq = it->options[1];
+ }
+
+ if (board->num_asics == 2) {
+ if (it->options[2] == dev->irq) {
+ /* the same irq (or none) is used by both asics */
+ devpriv->irq2 = it->options[2];
+ } else if (it->options[2]) {
+ /* request the irq for the 2nd asic */
+ ret = request_irq(it->options[2], pcmuio_interrupt, 0,
+ dev->board_name, dev);
+ if (ret == 0)
+ devpriv->irq2 = it->options[2];
+ }
+ }
+
+ ret = comedi_alloc_subdevices(dev, board->num_asics * 2);
+ if (ret)
+ return ret;
+
+ for (i = 0; i < dev->n_subdevices; ++i) {
+ s = &dev->subdevices[i];
+ s->type = COMEDI_SUBD_DIO;
+ s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
+ s->n_chan = 24;
+ s->maxdata = 1;
+ s->range_table = &range_digital;
+ s->insn_bits = pcmuio_dio_insn_bits;
+ s->insn_config = pcmuio_dio_insn_config;
+
+ /* subdevices 0 and 2 can support interrupts */
+ if ((i == 0 && dev->irq) || (i == 2 && devpriv->irq2)) {
+ /* setup the interrupt subdevice */
+ dev->read_subdev = s;
+ s->subdev_flags |= SDF_CMD_READ | SDF_LSAMPL |
+ SDF_PACKED;
+ s->len_chanlist = s->n_chan;
+ s->cancel = pcmuio_cancel;
+ s->do_cmd = pcmuio_cmd;
+ s->do_cmdtest = pcmuio_cmdtest;
+ }
+ }
+
+ return 0;
+}
+
+static void pcmuio_detach(struct comedi_device *dev)
+{
+ struct pcmuio_private *devpriv = dev->private;
+
+ if (devpriv) {
+ pcmuio_reset(dev);
+
+ /* free the 2nd irq if used, the core will free the 1st one */
+ if (devpriv->irq2 && devpriv->irq2 != dev->irq)
+ free_irq(devpriv->irq2, dev);
+ }
+ comedi_legacy_detach(dev);
+}
+
+static struct comedi_driver pcmuio_driver = {
+ .driver_name = "pcmuio",
+ .module = THIS_MODULE,
+ .attach = pcmuio_attach,
+ .detach = pcmuio_detach,
+ .board_name = &pcmuio_boards[0].name,
+ .offset = sizeof(struct pcmuio_board),
+ .num_names = ARRAY_SIZE(pcmuio_boards),
+};
+module_comedi_driver(pcmuio_driver);
+
+MODULE_AUTHOR("Comedi http://www.comedi.org");
+MODULE_DESCRIPTION("Comedi low-level driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/comedi/drivers/plx9052.h b/drivers/staging/comedi/drivers/plx9052.h
new file mode 100644
index 000000000..fbcf25069
--- /dev/null
+++ b/drivers/staging/comedi/drivers/plx9052.h
@@ -0,0 +1,79 @@
+/*
+ comedi/drivers/plx9052.h
+ Definitions for the PLX-9052 PCI interface chip
+
+ Copyright (C) 2002 MEV Ltd. <http://www.mev.co.uk/>
+
+ COMEDI - Linux Control and Measurement Device Interface
+ Copyright (C) 2000 David A. Schleef <ds@schleef.org>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+*/
+
+#ifndef _PLX9052_H_
+#define _PLX9052_H_
+
+/*
+ * INTCSR - Interrupt Control/Status register
+ */
+#define PLX9052_INTCSR 0x4c
+#define PLX9052_INTCSR_LI1ENAB (1 << 0) /* LI1 enabled */
+#define PLX9052_INTCSR_LI1POL (1 << 1) /* LI1 active high */
+#define PLX9052_INTCSR_LI1STAT (1 << 2) /* LI1 active */
+#define PLX9052_INTCSR_LI2ENAB (1 << 3) /* LI2 enabled */
+#define PLX9052_INTCSR_LI2POL (1 << 4) /* LI2 active high */
+#define PLX9052_INTCSR_LI2STAT (1 << 5) /* LI2 active */
+#define PLX9052_INTCSR_PCIENAB (1 << 6) /* PCIINT enabled */
+#define PLX9052_INTCSR_SOFTINT (1 << 7) /* generate soft int */
+#define PLX9052_INTCSR_LI1SEL (1 << 8) /* LI1 edge */
+#define PLX9052_INTCSR_LI2SEL (1 << 9) /* LI2 edge */
+#define PLX9052_INTCSR_LI1CLRINT (1 << 10) /* LI1 clear int */
+#define PLX9052_INTCSR_LI2CLRINT (1 << 11) /* LI2 clear int */
+#define PLX9052_INTCSR_ISAMODE (1 << 12) /* ISA interface mode */
+
+/*
+ * CNTRL - User I/O, Direct Slave Response, Serial EEPROM, and
+ * Initialization Control register
+ */
+#define PLX9052_CNTRL 0x50
+#define PLX9052_CNTRL_WAITO (1 << 0) /* UIO0 or WAITO# select */
+#define PLX9052_CNTRL_UIO0_DIR (1 << 1) /* UIO0 direction */
+#define PLX9052_CNTRL_UIO0_DATA (1 << 2) /* UIO0 data */
+#define PLX9052_CNTRL_LLOCKO (1 << 3) /* UIO1 or LLOCKo# select */
+#define PLX9052_CNTRL_UIO1_DIR (1 << 4) /* UIO1 direction */
+#define PLX9052_CNTRL_UIO1_DATA (1 << 5) /* UIO1 data */
+#define PLX9052_CNTRL_CS2 (1 << 6) /* UIO2 or CS2# select */
+#define PLX9052_CNTRL_UIO2_DIR (1 << 7) /* UIO2 direction */
+#define PLX9052_CNTRL_UIO2_DATA (1 << 8) /* UIO2 data */
+#define PLX9052_CNTRL_CS3 (1 << 9) /* UIO3 or CS3# select */
+#define PLX9052_CNTRL_UIO3_DIR (1 << 10) /* UIO3 direction */
+#define PLX9052_CNTRL_UIO3_DATA (1 << 11) /* UIO3 data */
+#define PLX9052_CNTRL_PCIBAR01 (0 << 12) /* bar 0 (mem) and 1 (I/O) */
+#define PLX9052_CNTRL_PCIBAR0 (1 << 12) /* bar 0 (mem) only */
+#define PLX9052_CNTRL_PCIBAR1 (2 << 12) /* bar 1 (I/O) only */
+#define PLX9052_CNTRL_PCI2_1_FEATURES (1 << 14) /* PCI r2.1 features enabled */
+#define PLX9052_CNTRL_PCI_R_W_FLUSH (1 << 15) /* read w/write flush mode */
+#define PLX9052_CNTRL_PCI_R_NO_FLUSH (1 << 16) /* read no flush mode */
+#define PLX9052_CNTRL_PCI_R_NO_WRITE (1 << 17) /* read no write mode */
+#define PLX9052_CNTRL_PCI_W_RELEASE (1 << 18) /* write release bus mode */
+#define PLX9052_CNTRL_RETRY_CLKS(x) (((x) & 0xf) << 19) /* slave retry clks */
+#define PLX9052_CNTRL_LOCK_ENAB (1 << 23) /* slave LOCK# enable */
+#define PLX9052_CNTRL_EEPROM_MASK (0x1f << 24) /* EEPROM bits */
+#define PLX9052_CNTRL_EEPROM_CLK (1 << 24) /* EEPROM clock */
+#define PLX9052_CNTRL_EEPROM_CS (1 << 25) /* EEPROM chip select */
+#define PLX9052_CNTRL_EEPROM_DOUT (1 << 26) /* EEPROM write bit */
+#define PLX9052_CNTRL_EEPROM_DIN (1 << 27) /* EEPROM read bit */
+#define PLX9052_CNTRL_EEPROM_PRESENT (1 << 28) /* EEPROM present */
+#define PLX9052_CNTRL_RELOAD_CFG (1 << 29) /* reload configuration */
+#define PLX9052_CNTRL_PCI_RESET (1 << 30) /* PCI adapter reset */
+#define PLX9052_CNTRL_MASK_REV (1 << 31) /* mask revision */
+
+#endif /* _PLX9052_H_ */
diff --git a/drivers/staging/comedi/drivers/plx9080.h b/drivers/staging/comedi/drivers/plx9080.h
new file mode 100644
index 000000000..25706531b
--- /dev/null
+++ b/drivers/staging/comedi/drivers/plx9080.h
@@ -0,0 +1,422 @@
+/* plx9080.h
+ *
+ * Copyright (C) 2002,2003 Frank Mori Hess <fmhess@users.sourceforge.net>
+ *
+ * I modified this file from the plx9060.h header for the
+ * wanXL device driver in the linux kernel,
+ * for the register offsets and bit definitions. Made minor modifications,
+ * added plx9080 registers and
+ * stripped out stuff that was specifically for the wanXL driver.
+ * Note: I've only made sure the definitions are correct as far
+ * as I make use of them. There are still various plx9060-isms
+ * left in this header file.
+ *
+ ********************************************************************
+ *
+ * Copyright (C) 1999 RG Studio s.c.
+ * Written by Krzysztof Halasa <khc@rgstudio.com.pl>
+ *
+ * Portions (C) SBE Inc., used by permission.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#ifndef __COMEDI_PLX9080_H
+#define __COMEDI_PLX9080_H
+
+/* descriptor block used for chained dma transfers */
+struct plx_dma_desc {
+ __le32 pci_start_addr;
+ __le32 local_start_addr;
+ /* transfer_size is in bytes, only first 23 bits of register are used */
+ __le32 transfer_size;
+ /* address of next descriptor (quad word aligned), plus some
+ * additional bits (see PLX_DMA0_DESCRIPTOR_REG) */
+ __le32 next;
+};
+
+/**********************************************************************
+** Register Offsets and Bit Definitions
+**
+** Note: All offsets zero relative. IE. Some standard base address
+** must be added to the Register Number to properly access the register.
+**
+**********************************************************************/
+
+#define PLX_LAS0RNG_REG 0x0000 /* L, Local Addr Space 0 Range Register */
+#define PLX_LAS1RNG_REG 0x00f0 /* L, Local Addr Space 1 Range Register */
+#define LRNG_IO 0x00000001 /* Map to: 1=I/O, 0=Mem */
+#define LRNG_ANY32 0x00000000 /* Locate anywhere in 32 bit */
+#define LRNG_LT1MB 0x00000002 /* Locate in 1st meg */
+#define LRNG_ANY64 0x00000004 /* Locate anywhere in 64 bit */
+#define LRNG_MEM_MASK 0xfffffff0 /* bits that specify range for memory io */
+#define LRNG_IO_MASK 0xfffffffa /* bits that specify range for normal io */
+
+#define PLX_LAS0MAP_REG 0x0004 /* L, Local Addr Space 0 Remap Register */
+#define PLX_LAS1MAP_REG 0x00f4 /* L, Local Addr Space 1 Remap Register */
+#define LMAP_EN 0x00000001 /* Enable slave decode */
+#define LMAP_MEM_MASK 0xfffffff0 /* bits that specify decode for memory io */
+#define LMAP_IO_MASK 0xfffffffa /* bits that specify decode bits for normal io */
+
+/* Mode/Arbitration Register.
+*/
+#define PLX_MARB_REG 0x8 /* L, Local Arbitration Register */
+#define PLX_DMAARB_REG 0xac
+enum marb_bits {
+ MARB_LLT_MASK = 0x000000ff, /* Local Bus Latency Timer */
+ MARB_LPT_MASK = 0x0000ff00, /* Local Bus Pause Timer */
+ MARB_LTEN = 0x00010000, /* Latency Timer Enable */
+ MARB_LPEN = 0x00020000, /* Pause Timer Enable */
+ MARB_BREQ = 0x00040000, /* Local Bus BREQ Enable */
+ MARB_DMA_PRIORITY_MASK = 0x00180000,
+ MARB_LBDS_GIVE_UP_BUS_MODE = 0x00200000, /* local bus direct slave give up bus mode */
+ MARB_DS_LLOCK_ENABLE = 0x00400000, /* direct slave LLOCKo# enable */
+ MARB_PCI_REQUEST_MODE = 0x00800000,
+ MARB_PCIv21_MODE = 0x01000000, /* pci specification v2.1 mode */
+ MARB_PCI_READ_NO_WRITE_MODE = 0x02000000,
+ MARB_PCI_READ_WITH_WRITE_FLUSH_MODE = 0x04000000,
+ MARB_GATE_TIMER_WITH_BREQ = 0x08000000, /* gate local bus latency timer with BREQ */
+ MARB_PCI_READ_NO_FLUSH_MODE = 0x10000000,
+ MARB_USE_SUBSYSTEM_IDS = 0x20000000,
+};
+
+#define PLX_BIGEND_REG 0xc
+enum bigend_bits {
+ BIGEND_CONFIG = 0x1, /* use big endian ordering for configuration register accesses */
+ BIGEND_DIRECT_MASTER = 0x2,
+ BIGEND_DIRECT_SLAVE_LOCAL0 = 0x4,
+ BIGEND_ROM = 0x8,
+ BIGEND_BYTE_LANE = 0x10, /* use byte lane consisting of most significant bits instead of least significant */
+ BIGEND_DIRECT_SLAVE_LOCAL1 = 0x20,
+ BIGEND_DMA1 = 0x40,
+ BIGEND_DMA0 = 0x80,
+};
+
+/* Note: The Expansion ROM stuff is only relevant to the PC environment.
+** This expansion ROM code is executed by the host CPU at boot time.
+** For this reason no bit definitions are provided here.
+*/
+#define PLX_ROMRNG_REG 0x0010 /* L, Expn ROM Space Range Register */
+#define PLX_ROMMAP_REG 0x0014 /* L, Local Addr Space Range Register */
+
+#define PLX_REGION0_REG 0x0018 /* L, Local Bus Region 0 Descriptor */
+#define RGN_WIDTH 0x00000002 /* Local bus width bits */
+#define RGN_8BITS 0x00000000 /* 08 bit Local Bus */
+#define RGN_16BITS 0x00000001 /* 16 bit Local Bus */
+#define RGN_32BITS 0x00000002 /* 32 bit Local Bus */
+#define RGN_MWS 0x0000003C /* Memory Access Wait States */
+#define RGN_0MWS 0x00000000
+#define RGN_1MWS 0x00000004
+#define RGN_2MWS 0x00000008
+#define RGN_3MWS 0x0000000C
+#define RGN_4MWS 0x00000010
+#define RGN_6MWS 0x00000018
+#define RGN_8MWS 0x00000020
+#define RGN_MRE 0x00000040 /* Memory Space Ready Input Enable */
+#define RGN_MBE 0x00000080 /* Memory Space Bterm Input Enable */
+#define RGN_READ_PREFETCH_DISABLE 0x00000100
+#define RGN_ROM_PREFETCH_DISABLE 0x00000200
+#define RGN_READ_PREFETCH_COUNT_ENABLE 0x00000400
+#define RGN_RWS 0x003C0000 /* Expn ROM Wait States */
+#define RGN_RRE 0x00400000 /* ROM Space Ready Input Enable */
+#define RGN_RBE 0x00800000 /* ROM Space Bterm Input Enable */
+#define RGN_MBEN 0x01000000 /* Memory Space Burst Enable */
+#define RGN_RBEN 0x04000000 /* ROM Space Burst Enable */
+#define RGN_THROT 0x08000000 /* De-assert TRDY when FIFO full */
+#define RGN_TRD 0xF0000000 /* Target Ready Delay /8 */
+
+#define PLX_REGION1_REG 0x00f8 /* L, Local Bus Region 1 Descriptor */
+
+#define PLX_DMRNG_REG 0x001C /* L, Direct Master Range Register */
+
+#define PLX_LBAPMEM_REG 0x0020 /* L, Lcl Base Addr for PCI mem space */
+
+#define PLX_LBAPIO_REG 0x0024 /* L, Lcl Base Addr for PCI I/O space */
+
+#define PLX_DMMAP_REG 0x0028 /* L, Direct Master Remap Register */
+#define DMM_MAE 0x00000001 /* Direct Mstr Memory Acc Enable */
+#define DMM_IAE 0x00000002 /* Direct Mstr I/O Acc Enable */
+#define DMM_LCK 0x00000004 /* LOCK Input Enable */
+#define DMM_PF4 0x00000008 /* Prefetch 4 Mode Enable */
+#define DMM_THROT 0x00000010 /* Assert IRDY when read FIFO full */
+#define DMM_PAF0 0x00000000 /* Programmable Almost fill level */
+#define DMM_PAF1 0x00000020 /* Programmable Almost fill level */
+#define DMM_PAF2 0x00000040 /* Programmable Almost fill level */
+#define DMM_PAF3 0x00000060 /* Programmable Almost fill level */
+#define DMM_PAF4 0x00000080 /* Programmable Almost fill level */
+#define DMM_PAF5 0x000000A0 /* Programmable Almost fill level */
+#define DMM_PAF6 0x000000C0 /* Programmable Almost fill level */
+#define DMM_PAF7 0x000000D0 /* Programmable Almost fill level */
+#define DMM_MAP 0xFFFF0000 /* Remap Address Bits */
+
+#define PLX_CAR_REG 0x002C /* L, Configuration Address Register */
+#define CAR_CT0 0x00000000 /* Config Type 0 */
+#define CAR_CT1 0x00000001 /* Config Type 1 */
+#define CAR_REG 0x000000FC /* Register Number Bits */
+#define CAR_FUN 0x00000700 /* Function Number Bits */
+#define CAR_DEV 0x0000F800 /* Device Number Bits */
+#define CAR_BUS 0x00FF0000 /* Bus Number Bits */
+#define CAR_CFG 0x80000000 /* Config Spc Access Enable */
+
+#define PLX_DBR_IN_REG 0x0060 /* L, PCI to Local Doorbell Register */
+
+#define PLX_DBR_OUT_REG 0x0064 /* L, Local to PCI Doorbell Register */
+
+#define PLX_INTRCS_REG 0x0068 /* L, Interrupt Control/Status Reg */
+#define ICS_AERR 0x00000001 /* Assert LSERR on ABORT */
+#define ICS_PERR 0x00000002 /* Assert LSERR on Parity Error */
+#define ICS_SERR 0x00000004 /* Generate PCI SERR# */
+#define ICS_MBIE 0x00000008 /* mailbox interrupt enable */
+#define ICS_PIE 0x00000100 /* PCI Interrupt Enable */
+#define ICS_PDIE 0x00000200 /* PCI Doorbell Interrupt Enable */
+#define ICS_PAIE 0x00000400 /* PCI Abort Interrupt Enable */
+#define ICS_PLIE 0x00000800 /* PCI Local Int Enable */
+#define ICS_RAE 0x00001000 /* Retry Abort Enable */
+#define ICS_PDIA 0x00002000 /* PCI Doorbell Interrupt Active */
+#define ICS_PAIA 0x00004000 /* PCI Abort Interrupt Active */
+#define ICS_LIA 0x00008000 /* Local Interrupt Active */
+#define ICS_LIE 0x00010000 /* Local Interrupt Enable */
+#define ICS_LDIE 0x00020000 /* Local Doorbell Int Enable */
+#define ICS_DMA0_E 0x00040000 /* DMA #0 Interrupt Enable */
+#define ICS_DMA1_E 0x00080000 /* DMA #1 Interrupt Enable */
+#define ICS_LDIA 0x00100000 /* Local Doorbell Int Active */
+#define ICS_DMA0_A 0x00200000 /* DMA #0 Interrupt Active */
+#define ICS_DMA1_A 0x00400000 /* DMA #1 Interrupt Active */
+#define ICS_BIA 0x00800000 /* BIST Interrupt Active */
+#define ICS_TA_DM 0x01000000 /* Target Abort - Direct Master */
+#define ICS_TA_DMA0 0x02000000 /* Target Abort - DMA #0 */
+#define ICS_TA_DMA1 0x04000000 /* Target Abort - DMA #1 */
+#define ICS_TA_RA 0x08000000 /* Target Abort - Retry Timeout */
+#define ICS_MBIA(x) (0x10000000 << ((x) & 0x3)) /* mailbox x is active */
+
+#define PLX_CONTROL_REG 0x006C /* L, EEPROM Cntl & PCI Cmd Codes */
+#define CTL_RDMA 0x0000000E /* DMA Read Command */
+#define CTL_WDMA 0x00000070 /* DMA Write Command */
+#define CTL_RMEM 0x00000600 /* Memory Read Command */
+#define CTL_WMEM 0x00007000 /* Memory Write Command */
+#define CTL_USERO 0x00010000 /* USERO output pin control bit */
+#define CTL_USERI 0x00020000 /* USERI input pin bit */
+#define CTL_EE_CLK 0x01000000 /* EEPROM Clock line */
+#define CTL_EE_CS 0x02000000 /* EEPROM Chip Select */
+#define CTL_EE_W 0x04000000 /* EEPROM Write bit */
+#define CTL_EE_R 0x08000000 /* EEPROM Read bit */
+#define CTL_EECHK 0x10000000 /* EEPROM Present bit */
+#define CTL_EERLD 0x20000000 /* EEPROM Reload Register */
+#define CTL_RESET 0x40000000 /* !! Adapter Reset !! */
+#define CTL_READY 0x80000000 /* Local Init Done */
+
+#define PLX_ID_REG 0x70 /* hard-coded plx vendor and device ids */
+
+#define PLX_REVISION_REG 0x74 /* silicon revision */
+
+#define PLX_DMA0_MODE_REG 0x80 /* dma channel 0 mode register */
+#define PLX_DMA1_MODE_REG 0x94 /* dma channel 0 mode register */
+#define PLX_LOCAL_BUS_16_WIDE_BITS 0x1
+#define PLX_LOCAL_BUS_32_WIDE_BITS 0x3
+#define PLX_LOCAL_BUS_WIDTH_MASK 0x3
+#define PLX_DMA_EN_READYIN_BIT 0x40 /* enable ready in input */
+#define PLX_EN_BTERM_BIT 0x80 /* enable BTERM# input */
+#define PLX_DMA_LOCAL_BURST_EN_BIT 0x100 /* enable local burst mode */
+#define PLX_EN_CHAIN_BIT 0x200 /* enables chaining */
+#define PLX_EN_DMA_DONE_INTR_BIT 0x400 /* enables interrupt on dma done */
+#define PLX_LOCAL_ADDR_CONST_BIT 0x800 /* hold local address constant (don't increment) */
+#define PLX_DEMAND_MODE_BIT 0x1000 /* enables demand-mode for dma transfer */
+#define PLX_EOT_ENABLE_BIT 0x4000
+#define PLX_STOP_MODE_BIT 0x8000
+#define PLX_DMA_INTR_PCI_BIT 0x20000 /* routes dma interrupt to pci bus (instead of local bus) */
+
+#define PLX_DMA0_PCI_ADDRESS_REG 0x84 /* pci address that dma transfers start at */
+#define PLX_DMA1_PCI_ADDRESS_REG 0x98
+
+#define PLX_DMA0_LOCAL_ADDRESS_REG 0x88 /* local address that dma transfers start at */
+#define PLX_DMA1_LOCAL_ADDRESS_REG 0x9c
+
+#define PLX_DMA0_TRANSFER_SIZE_REG 0x8c /* number of bytes to transfer (first 23 bits) */
+#define PLX_DMA1_TRANSFER_SIZE_REG 0xa0
+
+#define PLX_DMA0_DESCRIPTOR_REG 0x90 /* descriptor pointer register */
+#define PLX_DMA1_DESCRIPTOR_REG 0xa4
+#define PLX_DESC_IN_PCI_BIT 0x1 /* descriptor is located in pci space (not local space) */
+#define PLX_END_OF_CHAIN_BIT 0x2 /* end of chain bit */
+#define PLX_INTR_TERM_COUNT 0x4 /* interrupt when this descriptor's transfer is finished */
+#define PLX_XFER_LOCAL_TO_PCI 0x8 /* transfer from local to pci bus (not pci to local) */
+
+#define PLX_DMA0_CS_REG 0xa8 /* command status register */
+#define PLX_DMA1_CS_REG 0xa9
+#define PLX_DMA_EN_BIT 0x1 /* enable dma channel */
+#define PLX_DMA_START_BIT 0x2 /* start dma transfer */
+#define PLX_DMA_ABORT_BIT 0x4 /* abort dma transfer */
+#define PLX_CLEAR_DMA_INTR_BIT 0x8 /* clear dma interrupt */
+#define PLX_DMA_DONE_BIT 0x10 /* transfer done status bit */
+
+#define PLX_DMA0_THRESHOLD_REG 0xb0 /* command status register */
+
+/*
+ * Accesses near the end of memory can cause the PLX chip
+ * to pre-fetch data off of end-of-ram. Limit the size of
+ * memory so host-side accesses cannot occur.
+ */
+
+#define PLX_PREFETCH 32
+
+/*
+ * The PCI Interface, via the PCI-9060 Chip, has up to eight (8) Mailbox
+ * Registers. The PUTS (Power-Up Test Suite) handles the board-side
+ * interface/interaction using the first 4 registers. Specifications for
+ * the use of the full PUTS' command and status interface is contained
+ * within a separate SBE PUTS Manual. The Host-Side Device Driver only
+ * uses a subset of the full PUTS interface.
+ */
+
+/*****************************************/
+/*** MAILBOX #(-1) - MEM ACCESS STS ***/
+/*****************************************/
+
+#define MBX_STS_VALID 0x57584744 /* 'WXGD' */
+#define MBX_STS_DILAV 0x44475857 /* swapped = 'DGXW' */
+
+/*****************************************/
+/*** MAILBOX #0 - PUTS STATUS ***/
+/*****************************************/
+
+#define MBX_STS_MASK 0x000000ff /* PUTS Status Register bits */
+#define MBX_STS_TMASK 0x0000000f /* register bits for TEST number */
+
+#define MBX_STS_PCIRESET 0x00000100 /* Host issued PCI reset request */
+#define MBX_STS_BUSY 0x00000080 /* PUTS is in progress */
+#define MBX_STS_ERROR 0x00000040 /* PUTS has failed */
+#define MBX_STS_RESERVED 0x000000c0 /* Undefined -> status in transition.
+ We are in process of changing
+ bits; we SET Error bit before
+ RESET of Busy bit */
+
+#define MBX_RESERVED_5 0x00000020 /* FYI: reserved/unused bit */
+#define MBX_RESERVED_4 0x00000010 /* FYI: reserved/unused bit */
+
+/******************************************/
+/*** MAILBOX #1 - PUTS COMMANDS ***/
+/******************************************/
+
+/*
+ * Any attempt to execute an unimplement command results in the PUTS
+ * interface executing a NOOP and continuing as if the offending command
+ * completed normally. Note: this supplies a simple method to interrogate
+ * mailbox command processing functionality.
+ */
+
+#define MBX_CMD_MASK 0xffff0000 /* PUTS Command Register bits */
+
+#define MBX_CMD_ABORTJ 0x85000000 /* abort and jump */
+#define MBX_CMD_RESETP 0x86000000 /* reset and pause at start */
+#define MBX_CMD_PAUSE 0x87000000 /* pause immediately */
+#define MBX_CMD_PAUSEC 0x88000000 /* pause on completion */
+#define MBX_CMD_RESUME 0x89000000 /* resume operation */
+#define MBX_CMD_STEP 0x8a000000 /* single step tests */
+
+#define MBX_CMD_BSWAP 0x8c000000 /* identify byte swap scheme */
+#define MBX_CMD_BSWAP_0 0x8c000000 /* use scheme 0 */
+#define MBX_CMD_BSWAP_1 0x8c000001 /* use scheme 1 */
+
+#define MBX_CMD_SETHMS 0x8d000000 /* setup host memory access window
+ size */
+#define MBX_CMD_SETHBA 0x8e000000 /* setup host memory access base
+ address */
+#define MBX_CMD_MGO 0x8f000000 /* perform memory setup and continue
+ (IE. Done) */
+#define MBX_CMD_NOOP 0xFF000000 /* dummy, illegal command */
+
+/*****************************************/
+/*** MAILBOX #2 - MEMORY SIZE ***/
+/*****************************************/
+
+#define MBX_MEMSZ_MASK 0xffff0000 /* PUTS Memory Size Register bits */
+
+#define MBX_MEMSZ_128KB 0x00020000 /* 128 kilobyte board */
+#define MBX_MEMSZ_256KB 0x00040000 /* 256 kilobyte board */
+#define MBX_MEMSZ_512KB 0x00080000 /* 512 kilobyte board */
+#define MBX_MEMSZ_1MB 0x00100000 /* 1 megabyte board */
+#define MBX_MEMSZ_2MB 0x00200000 /* 2 megabyte board */
+#define MBX_MEMSZ_4MB 0x00400000 /* 4 megabyte board */
+#define MBX_MEMSZ_8MB 0x00800000 /* 8 megabyte board */
+#define MBX_MEMSZ_16MB 0x01000000 /* 16 megabyte board */
+
+/***************************************/
+/*** MAILBOX #2 - BOARD TYPE ***/
+/***************************************/
+
+#define MBX_BTYPE_MASK 0x0000ffff /* PUTS Board Type Register */
+#define MBX_BTYPE_FAMILY_MASK 0x0000ff00 /* PUTS Board Family Register */
+#define MBX_BTYPE_SUBTYPE_MASK 0x000000ff /* PUTS Board Subtype */
+
+#define MBX_BTYPE_PLX9060 0x00000100 /* PLX family type */
+#define MBX_BTYPE_PLX9080 0x00000300 /* PLX wanXL100s family type */
+
+#define MBX_BTYPE_WANXL_4 0x00000104 /* wanXL400, 4-port */
+#define MBX_BTYPE_WANXL_2 0x00000102 /* wanXL200, 2-port */
+#define MBX_BTYPE_WANXL_1s 0x00000301 /* wanXL100s, 1-port */
+#define MBX_BTYPE_WANXL_1t 0x00000401 /* wanXL100T1, 1-port */
+
+/*****************************************/
+/*** MAILBOX #3 - SHMQ MAILBOX ***/
+/*****************************************/
+
+#define MBX_SMBX_MASK 0x000000ff /* PUTS SHMQ Mailbox bits */
+
+/***************************************/
+/*** GENERIC HOST-SIDE DRIVER ***/
+/***************************************/
+
+#define MBX_ERR 0
+#define MBX_OK 1
+
+/* mailbox check routine - type of testing */
+#define MBXCHK_STS 0x00 /* check for PUTS status */
+#define MBXCHK_NOWAIT 0x01 /* dont care about PUTS status */
+
+/* system allocates this many bytes for address mapping mailbox space */
+#define MBX_ADDR_SPACE_360 0x80 /* wanXL100s/200/400 */
+#define MBX_ADDR_MASK_360 (MBX_ADDR_SPACE_360-1)
+
+static inline int plx9080_abort_dma(void __iomem *iobase, unsigned int channel)
+{
+ void __iomem *dma_cs_addr;
+ uint8_t dma_status;
+ const int timeout = 10000;
+ unsigned int i;
+
+ if (channel)
+ dma_cs_addr = iobase + PLX_DMA1_CS_REG;
+ else
+ dma_cs_addr = iobase + PLX_DMA0_CS_REG;
+
+ /* abort dma transfer if necessary */
+ dma_status = readb(dma_cs_addr);
+ if ((dma_status & PLX_DMA_EN_BIT) == 0)
+ return 0;
+
+ /* wait to make sure done bit is zero */
+ for (i = 0; (dma_status & PLX_DMA_DONE_BIT) && i < timeout; i++) {
+ udelay(1);
+ dma_status = readb(dma_cs_addr);
+ }
+ if (i == timeout)
+ return -ETIMEDOUT;
+
+ /* disable and abort channel */
+ writeb(PLX_DMA_ABORT_BIT, dma_cs_addr);
+ /* wait for dma done bit */
+ dma_status = readb(dma_cs_addr);
+ for (i = 0; (dma_status & PLX_DMA_DONE_BIT) == 0 && i < timeout; i++) {
+ udelay(1);
+ dma_status = readb(dma_cs_addr);
+ }
+ if (i == timeout)
+ return -ETIMEDOUT;
+
+ return 0;
+}
+
+#endif /* __COMEDI_PLX9080_H */
diff --git a/drivers/staging/comedi/drivers/quatech_daqp_cs.c b/drivers/staging/comedi/drivers/quatech_daqp_cs.c
new file mode 100644
index 000000000..152cb146f
--- /dev/null
+++ b/drivers/staging/comedi/drivers/quatech_daqp_cs.c
@@ -0,0 +1,815 @@
+/*======================================================================
+
+ comedi/drivers/quatech_daqp_cs.c
+
+ Quatech DAQP PCMCIA data capture cards COMEDI client driver
+ Copyright (C) 2000, 2003 Brent Baccala <baccala@freesoft.org>
+ The DAQP interface code in this file is released into the public domain.
+
+ COMEDI - Linux Control and Measurement Device Interface
+ Copyright (C) 1998 David A. Schleef <ds@schleef.org>
+ http://www.comedi.org/
+
+ quatech_daqp_cs.c 1.10
+
+ Documentation for the DAQP PCMCIA cards can be found on Quatech's site:
+
+ ftp://ftp.quatech.com/Manuals/daqp-208.pdf
+
+ This manual is for both the DAQP-208 and the DAQP-308.
+
+ What works:
+
+ - A/D conversion
+ - 8 channels
+ - 4 gain ranges
+ - ground ref or differential
+ - single-shot and timed both supported
+ - D/A conversion, single-shot
+ - digital I/O
+
+ What doesn't:
+
+ - any kind of triggering - external or D/A channel 1
+ - the card's optional expansion board
+ - the card's timer (for anything other than A/D conversion)
+ - D/A update modes other than immediate (i.e, timed)
+ - fancier timing modes
+ - setting card's FIFO buffer thresholds to anything but default
+
+======================================================================*/
+
+/*
+Driver: quatech_daqp_cs
+Description: Quatech DAQP PCMCIA data capture cards
+Author: Brent Baccala <baccala@freesoft.org>
+Status: works
+Devices: [Quatech] DAQP-208 (daqp), DAQP-308
+*/
+
+#include <linux/module.h>
+#include <linux/semaphore.h>
+#include <linux/completion.h>
+
+#include "../comedi_pcmcia.h"
+
+struct daqp_private {
+ int stop;
+
+ enum { semaphore, buffer } interrupt_mode;
+
+ struct completion eos;
+};
+
+/* The DAQP communicates with the system through a 16 byte I/O window. */
+
+#define DAQP_FIFO_SIZE 4096
+
+#define DAQP_FIFO 0
+#define DAQP_SCANLIST 1
+#define DAQP_CONTROL 2
+#define DAQP_STATUS 2
+#define DAQP_DIGITAL_IO 3
+#define DAQP_PACER_LOW 4
+#define DAQP_PACER_MID 5
+#define DAQP_PACER_HIGH 6
+#define DAQP_COMMAND 7
+#define DAQP_DA 8
+#define DAQP_TIMER 10
+#define DAQP_AUX 15
+
+#define DAQP_SCANLIST_DIFFERENTIAL 0x4000
+#define DAQP_SCANLIST_GAIN(x) ((x)<<12)
+#define DAQP_SCANLIST_CHANNEL(x) ((x)<<8)
+#define DAQP_SCANLIST_START 0x0080
+#define DAQP_SCANLIST_EXT_GAIN(x) ((x)<<4)
+#define DAQP_SCANLIST_EXT_CHANNEL(x) (x)
+
+#define DAQP_CONTROL_PACER_100kHz 0xc0
+#define DAQP_CONTROL_PACER_1MHz 0x80
+#define DAQP_CONTROL_PACER_5MHz 0x40
+#define DAQP_CONTROL_PACER_EXTERNAL 0x00
+#define DAQP_CONTORL_EXPANSION 0x20
+#define DAQP_CONTROL_EOS_INT_ENABLE 0x10
+#define DAQP_CONTROL_FIFO_INT_ENABLE 0x08
+#define DAQP_CONTROL_TRIGGER_ONESHOT 0x00
+#define DAQP_CONTROL_TRIGGER_CONTINUOUS 0x04
+#define DAQP_CONTROL_TRIGGER_INTERNAL 0x00
+#define DAQP_CONTROL_TRIGGER_EXTERNAL 0x02
+#define DAQP_CONTROL_TRIGGER_RISING 0x00
+#define DAQP_CONTROL_TRIGGER_FALLING 0x01
+
+#define DAQP_STATUS_IDLE 0x80
+#define DAQP_STATUS_RUNNING 0x40
+#define DAQP_STATUS_EVENTS 0x38
+#define DAQP_STATUS_DATA_LOST 0x20
+#define DAQP_STATUS_END_OF_SCAN 0x10
+#define DAQP_STATUS_FIFO_THRESHOLD 0x08
+#define DAQP_STATUS_FIFO_FULL 0x04
+#define DAQP_STATUS_FIFO_NEARFULL 0x02
+#define DAQP_STATUS_FIFO_EMPTY 0x01
+
+#define DAQP_COMMAND_ARM 0x80
+#define DAQP_COMMAND_RSTF 0x40
+#define DAQP_COMMAND_RSTQ 0x20
+#define DAQP_COMMAND_STOP 0x10
+#define DAQP_COMMAND_LATCH 0x08
+#define DAQP_COMMAND_100kHz 0x00
+#define DAQP_COMMAND_50kHz 0x02
+#define DAQP_COMMAND_25kHz 0x04
+#define DAQP_COMMAND_FIFO_DATA 0x01
+#define DAQP_COMMAND_FIFO_PROGRAM 0x00
+
+#define DAQP_AUX_TRIGGER_TTL 0x00
+#define DAQP_AUX_TRIGGER_ANALOG 0x80
+#define DAQP_AUX_TRIGGER_PRETRIGGER 0x40
+#define DAQP_AUX_TIMER_INT_ENABLE 0x20
+#define DAQP_AUX_TIMER_RELOAD 0x00
+#define DAQP_AUX_TIMER_PAUSE 0x08
+#define DAQP_AUX_TIMER_GO 0x10
+#define DAQP_AUX_TIMER_GO_EXTERNAL 0x18
+#define DAQP_AUX_TIMER_EXTERNAL_SRC 0x04
+#define DAQP_AUX_TIMER_INTERNAL_SRC 0x00
+#define DAQP_AUX_DA_DIRECT 0x00
+#define DAQP_AUX_DA_OVERFLOW 0x01
+#define DAQP_AUX_DA_EXTERNAL 0x02
+#define DAQP_AUX_DA_PACER 0x03
+
+#define DAQP_AUX_RUNNING 0x80
+#define DAQP_AUX_TRIGGERED 0x40
+#define DAQP_AUX_DA_BUFFER 0x20
+#define DAQP_AUX_TIMER_OVERFLOW 0x10
+#define DAQP_AUX_CONVERSION 0x08
+#define DAQP_AUX_DATA_LOST 0x04
+#define DAQP_AUX_FIFO_NEARFULL 0x02
+#define DAQP_AUX_FIFO_EMPTY 0x01
+
+static const struct comedi_lrange range_daqp_ai = {
+ 4, {
+ BIP_RANGE(10),
+ BIP_RANGE(5),
+ BIP_RANGE(2.5),
+ BIP_RANGE(1.25)
+ }
+};
+
+/* Cancel a running acquisition */
+
+static int daqp_ai_cancel(struct comedi_device *dev, struct comedi_subdevice *s)
+{
+ struct daqp_private *devpriv = dev->private;
+
+ if (devpriv->stop)
+ return -EIO;
+
+ outb(DAQP_COMMAND_STOP, dev->iobase + DAQP_COMMAND);
+
+ /* flush any linguring data in FIFO - superfluous here */
+ /* outb(DAQP_COMMAND_RSTF, dev->iobase+DAQP_COMMAND); */
+
+ devpriv->interrupt_mode = semaphore;
+
+ return 0;
+}
+
+/* Interrupt handler
+ *
+ * Operates in one of two modes. If devpriv->interrupt_mode is
+ * 'semaphore', just signal the devpriv->eos completion and return
+ * (one-shot mode). Otherwise (continuous mode), read data in from
+ * the card, transfer it to the buffer provided by the higher-level
+ * comedi kernel module, and signal various comedi callback routines,
+ * which run pretty quick.
+ */
+static enum irqreturn daqp_interrupt(int irq, void *dev_id)
+{
+ struct comedi_device *dev = dev_id;
+ struct daqp_private *devpriv = dev->private;
+ struct comedi_subdevice *s = dev->read_subdev;
+ struct comedi_cmd *cmd = &s->async->cmd;
+ int loop_limit = 10000;
+ int status;
+
+ if (!dev->attached)
+ return IRQ_NONE;
+
+ switch (devpriv->interrupt_mode) {
+ case semaphore:
+ complete(&devpriv->eos);
+ break;
+
+ case buffer:
+ while (!((status = inb(dev->iobase + DAQP_STATUS))
+ & DAQP_STATUS_FIFO_EMPTY)) {
+ unsigned short data;
+
+ if (status & DAQP_STATUS_DATA_LOST) {
+ s->async->events |= COMEDI_CB_OVERFLOW;
+ dev_warn(dev->class_dev, "data lost\n");
+ break;
+ }
+
+ data = inb(dev->iobase + DAQP_FIFO);
+ data |= inb(dev->iobase + DAQP_FIFO) << 8;
+ data ^= 0x8000;
+
+ comedi_buf_write_samples(s, &data, 1);
+
+ /* If there's a limit, decrement it
+ * and stop conversion if zero
+ */
+
+ if (cmd->stop_src == TRIG_COUNT &&
+ s->async->scans_done >= cmd->stop_arg) {
+ s->async->events |= COMEDI_CB_EOA;
+ break;
+ }
+
+ if ((loop_limit--) <= 0)
+ break;
+ }
+
+ if (loop_limit <= 0) {
+ dev_warn(dev->class_dev,
+ "loop_limit reached in daqp_interrupt()\n");
+ s->async->events |= COMEDI_CB_ERROR;
+ }
+
+ comedi_handle_events(dev, s);
+ }
+ return IRQ_HANDLED;
+}
+
+static void daqp_ai_set_one_scanlist_entry(struct comedi_device *dev,
+ unsigned int chanspec,
+ int start)
+{
+ unsigned int chan = CR_CHAN(chanspec);
+ unsigned int range = CR_RANGE(chanspec);
+ unsigned int aref = CR_AREF(chanspec);
+ unsigned int val;
+
+ val = DAQP_SCANLIST_CHANNEL(chan) | DAQP_SCANLIST_GAIN(range);
+
+ if (aref == AREF_DIFF)
+ val |= DAQP_SCANLIST_DIFFERENTIAL;
+
+ if (start)
+ val |= DAQP_SCANLIST_START;
+
+ outb(val & 0xff, dev->iobase + DAQP_SCANLIST);
+ outb((val >> 8) & 0xff, dev->iobase + DAQP_SCANLIST);
+}
+
+/* One-shot analog data acquisition routine */
+
+static int daqp_ai_insn_read(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn, unsigned int *data)
+{
+ struct daqp_private *devpriv = dev->private;
+ int i;
+ int v;
+ int counter = 10000;
+
+ if (devpriv->stop)
+ return -EIO;
+
+ /* Stop any running conversion */
+ daqp_ai_cancel(dev, s);
+
+ outb(0, dev->iobase + DAQP_AUX);
+
+ /* Reset scan list queue */
+ outb(DAQP_COMMAND_RSTQ, dev->iobase + DAQP_COMMAND);
+
+ /* Program one scan list entry */
+ daqp_ai_set_one_scanlist_entry(dev, insn->chanspec, 1);
+
+ /* Reset data FIFO (see page 28 of DAQP User's Manual) */
+
+ outb(DAQP_COMMAND_RSTF, dev->iobase + DAQP_COMMAND);
+
+ /* Set trigger */
+
+ v = DAQP_CONTROL_TRIGGER_ONESHOT | DAQP_CONTROL_TRIGGER_INTERNAL
+ | DAQP_CONTROL_PACER_100kHz | DAQP_CONTROL_EOS_INT_ENABLE;
+
+ outb(v, dev->iobase + DAQP_CONTROL);
+
+ /* Reset any pending interrupts (my card has a tendency to require
+ * require multiple reads on the status register to achieve this)
+ */
+
+ while (--counter
+ && (inb(dev->iobase + DAQP_STATUS) & DAQP_STATUS_EVENTS))
+ ;
+ if (!counter) {
+ dev_err(dev->class_dev,
+ "couldn't clear interrupts in status register\n");
+ return -1;
+ }
+
+ init_completion(&devpriv->eos);
+ devpriv->interrupt_mode = semaphore;
+
+ for (i = 0; i < insn->n; i++) {
+ /* Start conversion */
+ outb(DAQP_COMMAND_ARM | DAQP_COMMAND_FIFO_DATA,
+ dev->iobase + DAQP_COMMAND);
+
+ /* Wait for interrupt service routine to unblock completion */
+ /* Maybe could use a timeout here, but it's interruptible */
+ if (wait_for_completion_interruptible(&devpriv->eos))
+ return -EINTR;
+
+ data[i] = inb(dev->iobase + DAQP_FIFO);
+ data[i] |= inb(dev->iobase + DAQP_FIFO) << 8;
+ data[i] ^= 0x8000;
+ }
+
+ return insn->n;
+}
+
+/* This function converts ns nanoseconds to a counter value suitable
+ * for programming the device. We always use the DAQP's 5 MHz clock,
+ * which with its 24-bit counter, allows values up to 84 seconds.
+ * Also, the function adjusts ns so that it cooresponds to the actual
+ * time that the device will use.
+ */
+
+static int daqp_ns_to_timer(unsigned int *ns, unsigned int flags)
+{
+ int timer;
+
+ timer = *ns / 200;
+ *ns = timer * 200;
+
+ return timer;
+}
+
+/* cmdtest tests a particular command to see if it is valid.
+ * Using the cmdtest ioctl, a user can create a valid cmd
+ * and then have it executed by the cmd ioctl.
+ *
+ * cmdtest returns 1,2,3,4 or 0, depending on which tests
+ * the command passes.
+ */
+
+static int daqp_ai_cmdtest(struct comedi_device *dev,
+ struct comedi_subdevice *s, struct comedi_cmd *cmd)
+{
+ int err = 0;
+ unsigned int arg;
+
+ /* Step 1 : check if triggers are trivially valid */
+
+ err |= comedi_check_trigger_src(&cmd->start_src, TRIG_NOW);
+ err |= comedi_check_trigger_src(&cmd->scan_begin_src,
+ TRIG_TIMER | TRIG_FOLLOW);
+ err |= comedi_check_trigger_src(&cmd->convert_src,
+ TRIG_TIMER | TRIG_NOW);
+ err |= comedi_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
+ err |= comedi_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE);
+
+ if (err)
+ return 1;
+
+ /* Step 2a : make sure trigger sources are unique */
+
+ err |= comedi_check_trigger_is_unique(cmd->scan_begin_src);
+ err |= comedi_check_trigger_is_unique(cmd->convert_src);
+ err |= comedi_check_trigger_is_unique(cmd->stop_src);
+
+ /* Step 2b : and mutually compatible */
+
+ if (err)
+ return 2;
+
+ /* Step 3: check if arguments are trivially valid */
+
+ err |= comedi_check_trigger_arg_is(&cmd->start_arg, 0);
+
+#define MAX_SPEED 10000 /* 100 kHz - in nanoseconds */
+
+ if (cmd->scan_begin_src == TRIG_TIMER) {
+ err |= comedi_check_trigger_arg_min(&cmd->scan_begin_arg,
+ MAX_SPEED);
+ }
+
+ /* If both scan_begin and convert are both timer values, the only
+ * way that can make sense is if the scan time is the number of
+ * conversions times the convert time
+ */
+
+ if (cmd->scan_begin_src == TRIG_TIMER && cmd->convert_src == TRIG_TIMER
+ && cmd->scan_begin_arg != cmd->convert_arg * cmd->scan_end_arg) {
+ err |= -EINVAL;
+ }
+
+ if (cmd->convert_src == TRIG_TIMER) {
+ err |= comedi_check_trigger_arg_min(&cmd->convert_arg,
+ MAX_SPEED);
+ }
+
+ err |= comedi_check_trigger_arg_is(&cmd->scan_end_arg,
+ cmd->chanlist_len);
+
+ if (cmd->stop_src == TRIG_COUNT)
+ err |= comedi_check_trigger_arg_max(&cmd->stop_arg, 0x00ffffff);
+ else /* TRIG_NONE */
+ err |= comedi_check_trigger_arg_is(&cmd->stop_arg, 0);
+
+ if (err)
+ return 3;
+
+ /* step 4: fix up any arguments */
+
+ if (cmd->scan_begin_src == TRIG_TIMER) {
+ arg = cmd->scan_begin_arg;
+ daqp_ns_to_timer(&arg, cmd->flags);
+ err |= comedi_check_trigger_arg_is(&cmd->scan_begin_arg, arg);
+ }
+
+ if (cmd->convert_src == TRIG_TIMER) {
+ arg = cmd->convert_arg;
+ daqp_ns_to_timer(&arg, cmd->flags);
+ err |= comedi_check_trigger_arg_is(&cmd->convert_arg, arg);
+ }
+
+ if (err)
+ return 4;
+
+ return 0;
+}
+
+static int daqp_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
+{
+ struct daqp_private *devpriv = dev->private;
+ struct comedi_cmd *cmd = &s->async->cmd;
+ int counter;
+ int scanlist_start_on_every_entry;
+ int threshold;
+
+ int i;
+ int v;
+
+ if (devpriv->stop)
+ return -EIO;
+
+ /* Stop any running conversion */
+ daqp_ai_cancel(dev, s);
+
+ outb(0, dev->iobase + DAQP_AUX);
+
+ /* Reset scan list queue */
+ outb(DAQP_COMMAND_RSTQ, dev->iobase + DAQP_COMMAND);
+
+ /* Program pacer clock
+ *
+ * There's two modes we can operate in. If convert_src is
+ * TRIG_TIMER, then convert_arg specifies the time between
+ * each conversion, so we program the pacer clock to that
+ * frequency and set the SCANLIST_START bit on every scanlist
+ * entry. Otherwise, convert_src is TRIG_NOW, which means
+ * we want the fastest possible conversions, scan_begin_src
+ * is TRIG_TIMER, and scan_begin_arg specifies the time between
+ * each scan, so we program the pacer clock to this frequency
+ * and only set the SCANLIST_START bit on the first entry.
+ */
+
+ if (cmd->convert_src == TRIG_TIMER) {
+ counter = daqp_ns_to_timer(&cmd->convert_arg, cmd->flags);
+ outb(counter & 0xff, dev->iobase + DAQP_PACER_LOW);
+ outb((counter >> 8) & 0xff, dev->iobase + DAQP_PACER_MID);
+ outb((counter >> 16) & 0xff, dev->iobase + DAQP_PACER_HIGH);
+ scanlist_start_on_every_entry = 1;
+ } else {
+ counter = daqp_ns_to_timer(&cmd->scan_begin_arg, cmd->flags);
+ outb(counter & 0xff, dev->iobase + DAQP_PACER_LOW);
+ outb((counter >> 8) & 0xff, dev->iobase + DAQP_PACER_MID);
+ outb((counter >> 16) & 0xff, dev->iobase + DAQP_PACER_HIGH);
+ scanlist_start_on_every_entry = 0;
+ }
+
+ /* Program scan list */
+ for (i = 0; i < cmd->chanlist_len; i++) {
+ int start = (i == 0 || scanlist_start_on_every_entry);
+
+ daqp_ai_set_one_scanlist_entry(dev, cmd->chanlist[i], start);
+ }
+
+ /* Now it's time to program the FIFO threshold, basically the
+ * number of samples the card will buffer before it interrupts
+ * the CPU.
+ *
+ * If we don't have a stop count, then use half the size of
+ * the FIFO (the manufacturer's recommendation). Consider
+ * that the FIFO can hold 2K samples (4K bytes). With the
+ * threshold set at half the FIFO size, we have a margin of
+ * error of 1024 samples. At the chip's maximum sample rate
+ * of 100,000 Hz, the CPU would have to delay interrupt
+ * service for a full 10 milliseconds in order to lose data
+ * here (as opposed to higher up in the kernel). I've never
+ * seen it happen. However, for slow sample rates it may
+ * buffer too much data and introduce too much delay for the
+ * user application.
+ *
+ * If we have a stop count, then things get more interesting.
+ * If the stop count is less than the FIFO size (actually
+ * three-quarters of the FIFO size - see below), we just use
+ * the stop count itself as the threshold, the card interrupts
+ * us when that many samples have been taken, and we kill the
+ * acquisition at that point and are done. If the stop count
+ * is larger than that, then we divide it by 2 until it's less
+ * than three quarters of the FIFO size (we always leave the
+ * top quarter of the FIFO as protection against sluggish CPU
+ * interrupt response) and use that as the threshold. So, if
+ * the stop count is 4000 samples, we divide by two twice to
+ * get 1000 samples, use that as the threshold, take four
+ * interrupts to get our 4000 samples and are done.
+ *
+ * The algorithm could be more clever. For example, if 81000
+ * samples are requested, we could set the threshold to 1500
+ * samples and take 54 interrupts to get 81000. But 54 isn't
+ * a power of two, so this algorithm won't find that option.
+ * Instead, it'll set the threshold at 1266 and take 64
+ * interrupts to get 81024 samples, of which the last 24 will
+ * be discarded... but we won't get the last interrupt until
+ * they've been collected. To find the first option, the
+ * computer could look at the prime decomposition of the
+ * sample count (81000 = 3^4 * 5^3 * 2^3) and factor it into a
+ * threshold (1500 = 3 * 5^3 * 2^2) and an interrupt count (54
+ * = 3^3 * 2). Hmmm... a one-line while loop or prime
+ * decomposition of integers... I'll leave it the way it is.
+ *
+ * I'll also note a mini-race condition before ignoring it in
+ * the code. Let's say we're taking 4000 samples, as before.
+ * After 1000 samples, we get an interrupt. But before that
+ * interrupt is completely serviced, another sample is taken
+ * and loaded into the FIFO. Since the interrupt handler
+ * empties the FIFO before returning, it will read 1001 samples.
+ * If that happens four times, we'll end up taking 4004 samples,
+ * not 4000. The interrupt handler will discard the extra four
+ * samples (by halting the acquisition with four samples still
+ * in the FIFO), but we will have to wait for them.
+ *
+ * In short, this code works pretty well, but for either of
+ * the two reasons noted, might end up waiting for a few more
+ * samples than actually requested. Shouldn't make too much
+ * of a difference.
+ */
+
+ /* Save away the number of conversions we should perform, and
+ * compute the FIFO threshold (in bytes, not samples - that's
+ * why we multiple devpriv->count by 2 = sizeof(sample))
+ */
+
+ if (cmd->stop_src == TRIG_COUNT) {
+ unsigned long long nsamples;
+ unsigned long long nbytes;
+
+ nsamples = (unsigned long long)cmd->stop_arg *
+ cmd->scan_end_arg;
+ nbytes = nsamples * comedi_bytes_per_sample(s);
+ while (nbytes > DAQP_FIFO_SIZE * 3 / 4)
+ nbytes /= 2;
+ threshold = nbytes;
+ } else {
+ threshold = DAQP_FIFO_SIZE / 2;
+ }
+
+ /* Reset data FIFO (see page 28 of DAQP User's Manual) */
+
+ outb(DAQP_COMMAND_RSTF, dev->iobase + DAQP_COMMAND);
+
+ /* Set FIFO threshold. First two bytes are near-empty
+ * threshold, which is unused; next two bytes are near-full
+ * threshold. We computed the number of bytes we want in the
+ * FIFO when the interrupt is generated, what the card wants
+ * is actually the number of available bytes left in the FIFO
+ * when the interrupt is to happen.
+ */
+
+ outb(0x00, dev->iobase + DAQP_FIFO);
+ outb(0x00, dev->iobase + DAQP_FIFO);
+
+ outb((DAQP_FIFO_SIZE - threshold) & 0xff, dev->iobase + DAQP_FIFO);
+ outb((DAQP_FIFO_SIZE - threshold) >> 8, dev->iobase + DAQP_FIFO);
+
+ /* Set trigger */
+
+ v = DAQP_CONTROL_TRIGGER_CONTINUOUS | DAQP_CONTROL_TRIGGER_INTERNAL
+ | DAQP_CONTROL_PACER_5MHz | DAQP_CONTROL_FIFO_INT_ENABLE;
+
+ outb(v, dev->iobase + DAQP_CONTROL);
+
+ /* Reset any pending interrupts (my card has a tendency to require
+ * require multiple reads on the status register to achieve this)
+ */
+ counter = 100;
+ while (--counter
+ && (inb(dev->iobase + DAQP_STATUS) & DAQP_STATUS_EVENTS))
+ ;
+ if (!counter) {
+ dev_err(dev->class_dev,
+ "couldn't clear interrupts in status register\n");
+ return -1;
+ }
+
+ devpriv->interrupt_mode = buffer;
+
+ /* Start conversion */
+ outb(DAQP_COMMAND_ARM | DAQP_COMMAND_FIFO_DATA,
+ dev->iobase + DAQP_COMMAND);
+
+ return 0;
+}
+
+static int daqp_ao_insn_write(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ struct daqp_private *devpriv = dev->private;
+ unsigned int chan = CR_CHAN(insn->chanspec);
+ int i;
+
+ if (devpriv->stop)
+ return -EIO;
+
+ /* Make sure D/A update mode is direct update */
+ outb(0, dev->iobase + DAQP_AUX);
+
+ for (i = 0; i > insn->n; i++) {
+ unsigned val = data[i];
+
+ s->readback[chan] = val;
+
+ val &= 0x0fff;
+ val ^= 0x0800; /* Flip the sign */
+ val |= (chan << 12);
+
+ outw(val, dev->iobase + DAQP_DA);
+ }
+
+ return insn->n;
+}
+
+static int daqp_di_insn_bits(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ struct daqp_private *devpriv = dev->private;
+
+ if (devpriv->stop)
+ return -EIO;
+
+ data[0] = inb(dev->iobase + DAQP_DIGITAL_IO);
+
+ return insn->n;
+}
+
+static int daqp_do_insn_bits(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ struct daqp_private *devpriv = dev->private;
+
+ if (devpriv->stop)
+ return -EIO;
+
+ if (comedi_dio_update_state(s, data))
+ outb(s->state, dev->iobase + DAQP_DIGITAL_IO);
+
+ data[1] = s->state;
+
+ return insn->n;
+}
+
+static int daqp_auto_attach(struct comedi_device *dev,
+ unsigned long context)
+{
+ struct pcmcia_device *link = comedi_to_pcmcia_dev(dev);
+ struct daqp_private *devpriv;
+ struct comedi_subdevice *s;
+ int ret;
+
+ devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
+ if (!devpriv)
+ return -ENOMEM;
+
+ link->config_flags |= CONF_AUTO_SET_IO | CONF_ENABLE_IRQ;
+ ret = comedi_pcmcia_enable(dev, NULL);
+ if (ret)
+ return ret;
+ dev->iobase = link->resource[0]->start;
+
+ link->priv = dev;
+ ret = pcmcia_request_irq(link, daqp_interrupt);
+ if (ret)
+ return ret;
+
+ ret = comedi_alloc_subdevices(dev, 4);
+ if (ret)
+ return ret;
+
+ s = &dev->subdevices[0];
+ dev->read_subdev = s;
+ s->type = COMEDI_SUBD_AI;
+ s->subdev_flags = SDF_READABLE | SDF_GROUND | SDF_DIFF | SDF_CMD_READ;
+ s->n_chan = 8;
+ s->len_chanlist = 2048;
+ s->maxdata = 0xffff;
+ s->range_table = &range_daqp_ai;
+ s->insn_read = daqp_ai_insn_read;
+ s->do_cmdtest = daqp_ai_cmdtest;
+ s->do_cmd = daqp_ai_cmd;
+ s->cancel = daqp_ai_cancel;
+
+ s = &dev->subdevices[1];
+ s->type = COMEDI_SUBD_AO;
+ s->subdev_flags = SDF_WRITABLE;
+ s->n_chan = 2;
+ s->maxdata = 0x0fff;
+ s->range_table = &range_bipolar5;
+ s->insn_write = daqp_ao_insn_write;
+
+ ret = comedi_alloc_subdev_readback(s);
+ if (ret)
+ return ret;
+
+ s = &dev->subdevices[2];
+ s->type = COMEDI_SUBD_DI;
+ s->subdev_flags = SDF_READABLE;
+ s->n_chan = 1;
+ s->maxdata = 1;
+ s->insn_bits = daqp_di_insn_bits;
+
+ s = &dev->subdevices[3];
+ s->type = COMEDI_SUBD_DO;
+ s->subdev_flags = SDF_WRITABLE;
+ s->n_chan = 1;
+ s->maxdata = 1;
+ s->insn_bits = daqp_do_insn_bits;
+
+ return 0;
+}
+
+static struct comedi_driver driver_daqp = {
+ .driver_name = "quatech_daqp_cs",
+ .module = THIS_MODULE,
+ .auto_attach = daqp_auto_attach,
+ .detach = comedi_pcmcia_disable,
+};
+
+static int daqp_cs_suspend(struct pcmcia_device *link)
+{
+ struct comedi_device *dev = link->priv;
+ struct daqp_private *devpriv = dev ? dev->private : NULL;
+
+ /* Mark the device as stopped, to block IO until later */
+ if (devpriv)
+ devpriv->stop = 1;
+
+ return 0;
+}
+
+static int daqp_cs_resume(struct pcmcia_device *link)
+{
+ struct comedi_device *dev = link->priv;
+ struct daqp_private *devpriv = dev ? dev->private : NULL;
+
+ if (devpriv)
+ devpriv->stop = 0;
+
+ return 0;
+}
+
+static int daqp_cs_attach(struct pcmcia_device *link)
+{
+ return comedi_pcmcia_auto_config(link, &driver_daqp);
+}
+
+static const struct pcmcia_device_id daqp_cs_id_table[] = {
+ PCMCIA_DEVICE_MANF_CARD(0x0137, 0x0027),
+ PCMCIA_DEVICE_NULL
+};
+MODULE_DEVICE_TABLE(pcmcia, daqp_cs_id_table);
+
+static struct pcmcia_driver daqp_cs_driver = {
+ .name = "quatech_daqp_cs",
+ .owner = THIS_MODULE,
+ .id_table = daqp_cs_id_table,
+ .probe = daqp_cs_attach,
+ .remove = comedi_pcmcia_auto_unconfig,
+ .suspend = daqp_cs_suspend,
+ .resume = daqp_cs_resume,
+};
+module_comedi_pcmcia_driver(driver_daqp, daqp_cs_driver);
+
+MODULE_DESCRIPTION("Comedi driver for Quatech DAQP PCMCIA data capture cards");
+MODULE_AUTHOR("Brent Baccala <baccala@freesoft.org>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/comedi/drivers/rtd520.c b/drivers/staging/comedi/drivers/rtd520.c
new file mode 100644
index 000000000..4c13f5eb0
--- /dev/null
+++ b/drivers/staging/comedi/drivers/rtd520.c
@@ -0,0 +1,1344 @@
+/*
+ * comedi/drivers/rtd520.c
+ * Comedi driver for Real Time Devices (RTD) PCI4520/DM7520
+ *
+ * COMEDI - Linux Control and Measurement Device Interface
+ * Copyright (C) 2001 David A. Schleef <ds@schleef.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+/*
+ * Driver: rtd520
+ * Description: Real Time Devices PCI4520/DM7520
+ * Devices: [Real Time Devices] DM7520HR-1 (DM7520), DM7520HR-8,
+ * PCI4520 (PCI4520), PCI4520-8
+ * Author: Dan Christian
+ * Status: Works. Only tested on DM7520-8. Not SMP safe.
+ *
+ * Configuration options: not applicable, uses PCI auto config
+ */
+
+/*
+ * Created by Dan Christian, NASA Ames Research Center.
+ *
+ * The PCI4520 is a PCI card. The DM7520 is a PC/104-plus card.
+ * Both have:
+ * 8/16 12 bit ADC with FIFO and channel gain table
+ * 8 bits high speed digital out (for external MUX) (or 8 in or 8 out)
+ * 8 bits high speed digital in with FIFO and interrupt on change (or 8 IO)
+ * 2 12 bit DACs with FIFOs
+ * 2 bits output
+ * 2 bits input
+ * bus mastering DMA
+ * timers: ADC sample, pacer, burst, about, delay, DA1, DA2
+ * sample counter
+ * 3 user timer/counters (8254)
+ * external interrupt
+ *
+ * The DM7520 has slightly fewer features (fewer gain steps).
+ *
+ * These boards can support external multiplexors and multi-board
+ * synchronization, but this driver doesn't support that.
+ *
+ * Board docs: http://www.rtdusa.com/PC104/DM/analog%20IO/dm7520.htm
+ * Data sheet: http://www.rtdusa.com/pdf/dm7520.pdf
+ * Example source: http://www.rtdusa.com/examples/dm/dm7520.zip
+ * Call them and ask for the register level manual.
+ * PCI chip: http://www.plxtech.com/products/io/pci9080
+ *
+ * Notes:
+ * This board is memory mapped. There is some IO stuff, but it isn't needed.
+ *
+ * I use a pretty loose naming style within the driver (rtd_blah).
+ * All externally visible names should be rtd520_blah.
+ * I use camelCase for structures (and inside them).
+ * I may also use upper CamelCase for function names (old habit).
+ *
+ * This board is somewhat related to the RTD PCI4400 board.
+ *
+ * I borrowed heavily from the ni_mio_common, ni_atmio16d, mite, and
+ * das1800, since they have the best documented code. Driver cb_pcidas64.c
+ * uses the same DMA controller.
+ *
+ * As far as I can tell, the About interrupt doesn't work if Sample is
+ * also enabled. It turns out that About really isn't needed, since
+ * we always count down samples read.
+ *
+ * There was some timer/counter code, but it didn't follow the right API.
+ */
+
+/*
+ * driver status:
+ *
+ * Analog-In supports instruction and command mode.
+ *
+ * With DMA, you can sample at 1.15Mhz with 70% idle on a 400Mhz K6-2
+ * (single channel, 64K read buffer). I get random system lockups when
+ * using DMA with ALI-15xx based systems. I haven't been able to test
+ * any other chipsets. The lockups happen soon after the start of an
+ * acquistion, not in the middle of a long run.
+ *
+ * Without DMA, you can do 620Khz sampling with 20% idle on a 400Mhz K6-2
+ * (with a 256K read buffer).
+ *
+ * Digital-IO and Analog-Out only support instruction mode.
+ */
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+
+#include "../comedi_pci.h"
+
+#include "plx9080.h"
+
+/*
+ * Local Address Space 0 Offsets
+ */
+#define LAS0_USER_IO 0x0008 /* User I/O */
+#define LAS0_ADC 0x0010 /* FIFO Status/Software A/D Start */
+#define FS_DAC1_NOT_EMPTY (1 << 0) /* DAC1 FIFO not empty */
+#define FS_DAC1_HEMPTY (1 << 1) /* DAC1 FIFO half empty */
+#define FS_DAC1_NOT_FULL (1 << 2) /* DAC1 FIFO not full */
+#define FS_DAC2_NOT_EMPTY (1 << 4) /* DAC2 FIFO not empty */
+#define FS_DAC2_HEMPTY (1 << 5) /* DAC2 FIFO half empty */
+#define FS_DAC2_NOT_FULL (1 << 6) /* DAC2 FIFO not full */
+#define FS_ADC_NOT_EMPTY (1 << 8) /* ADC FIFO not empty */
+#define FS_ADC_HEMPTY (1 << 9) /* ADC FIFO half empty */
+#define FS_ADC_NOT_FULL (1 << 10) /* ADC FIFO not full */
+#define FS_DIN_NOT_EMPTY (1 << 12) /* DIN FIFO not empty */
+#define FS_DIN_HEMPTY (1 << 13) /* DIN FIFO half empty */
+#define FS_DIN_NOT_FULL (1 << 14) /* DIN FIFO not full */
+#define LAS0_DAC1 0x0014 /* Software D/A1 Update (w) */
+#define LAS0_DAC2 0x0018 /* Software D/A2 Update (w) */
+#define LAS0_DAC 0x0024 /* Software Simultaneous Update (w) */
+#define LAS0_PACER 0x0028 /* Software Pacer Start/Stop */
+#define LAS0_TIMER 0x002c /* Timer Status/HDIN Software Trig. */
+#define LAS0_IT 0x0030 /* Interrupt Status/Enable */
+#define IRQM_ADC_FIFO_WRITE (1 << 0) /* ADC FIFO Write */
+#define IRQM_CGT_RESET (1 << 1) /* Reset CGT */
+#define IRQM_CGT_PAUSE (1 << 3) /* Pause CGT */
+#define IRQM_ADC_ABOUT_CNT (1 << 4) /* About Counter out */
+#define IRQM_ADC_DELAY_CNT (1 << 5) /* Delay Counter out */
+#define IRQM_ADC_SAMPLE_CNT (1 << 6) /* ADC Sample Counter */
+#define IRQM_DAC1_UCNT (1 << 7) /* DAC1 Update Counter */
+#define IRQM_DAC2_UCNT (1 << 8) /* DAC2 Update Counter */
+#define IRQM_UTC1 (1 << 9) /* User TC1 out */
+#define IRQM_UTC1_INV (1 << 10) /* User TC1 out, inverted */
+#define IRQM_UTC2 (1 << 11) /* User TC2 out */
+#define IRQM_DIGITAL_IT (1 << 12) /* Digital Interrupt */
+#define IRQM_EXTERNAL_IT (1 << 13) /* External Interrupt */
+#define IRQM_ETRIG_RISING (1 << 14) /* Ext Trigger rising-edge */
+#define IRQM_ETRIG_FALLING (1 << 15) /* Ext Trigger falling-edge */
+#define LAS0_CLEAR 0x0034 /* Clear/Set Interrupt Clear Mask */
+#define LAS0_OVERRUN 0x0038 /* Pending interrupts/Clear Overrun */
+#define LAS0_PCLK 0x0040 /* Pacer Clock (24bit) */
+#define LAS0_BCLK 0x0044 /* Burst Clock (10bit) */
+#define LAS0_ADC_SCNT 0x0048 /* A/D Sample counter (10bit) */
+#define LAS0_DAC1_UCNT 0x004c /* D/A1 Update counter (10 bit) */
+#define LAS0_DAC2_UCNT 0x0050 /* D/A2 Update counter (10 bit) */
+#define LAS0_DCNT 0x0054 /* Delay counter (16 bit) */
+#define LAS0_ACNT 0x0058 /* About counter (16 bit) */
+#define LAS0_DAC_CLK 0x005c /* DAC clock (16bit) */
+#define LAS0_UTC0 0x0060 /* 8254 TC Counter 0 */
+#define LAS0_UTC1 0x0064 /* 8254 TC Counter 1 */
+#define LAS0_UTC2 0x0068 /* 8254 TC Counter 2 */
+#define LAS0_UTC_CTRL 0x006c /* 8254 TC Control */
+#define LAS0_DIO0 0x0070 /* Digital I/O Port 0 */
+#define LAS0_DIO1 0x0074 /* Digital I/O Port 1 */
+#define LAS0_DIO0_CTRL 0x0078 /* Digital I/O Control */
+#define LAS0_DIO_STATUS 0x007c /* Digital I/O Status */
+#define LAS0_BOARD_RESET 0x0100 /* Board reset */
+#define LAS0_DMA0_SRC 0x0104 /* DMA 0 Sources select */
+#define LAS0_DMA1_SRC 0x0108 /* DMA 1 Sources select */
+#define LAS0_ADC_CONVERSION 0x010c /* A/D Conversion Signal select */
+#define LAS0_BURST_START 0x0110 /* Burst Clock Start Trigger select */
+#define LAS0_PACER_START 0x0114 /* Pacer Clock Start Trigger select */
+#define LAS0_PACER_STOP 0x0118 /* Pacer Clock Stop Trigger select */
+#define LAS0_ACNT_STOP_ENABLE 0x011c /* About Counter Stop Enable */
+#define LAS0_PACER_REPEAT 0x0120 /* Pacer Start Trigger Mode select */
+#define LAS0_DIN_START 0x0124 /* HiSpd DI Sampling Signal select */
+#define LAS0_DIN_FIFO_CLEAR 0x0128 /* Digital Input FIFO Clear */
+#define LAS0_ADC_FIFO_CLEAR 0x012c /* A/D FIFO Clear */
+#define LAS0_CGT_WRITE 0x0130 /* Channel Gain Table Write */
+#define LAS0_CGL_WRITE 0x0134 /* Channel Gain Latch Write */
+#define LAS0_CG_DATA 0x0138 /* Digital Table Write */
+#define LAS0_CGT_ENABLE 0x013c /* Channel Gain Table Enable */
+#define LAS0_CG_ENABLE 0x0140 /* Digital Table Enable */
+#define LAS0_CGT_PAUSE 0x0144 /* Table Pause Enable */
+#define LAS0_CGT_RESET 0x0148 /* Reset Channel Gain Table */
+#define LAS0_CGT_CLEAR 0x014c /* Clear Channel Gain Table */
+#define LAS0_DAC1_CTRL 0x0150 /* D/A1 output type/range */
+#define LAS0_DAC1_SRC 0x0154 /* D/A1 update source */
+#define LAS0_DAC1_CYCLE 0x0158 /* D/A1 cycle mode */
+#define LAS0_DAC1_RESET 0x015c /* D/A1 FIFO reset */
+#define LAS0_DAC1_FIFO_CLEAR 0x0160 /* D/A1 FIFO clear */
+#define LAS0_DAC2_CTRL 0x0164 /* D/A2 output type/range */
+#define LAS0_DAC2_SRC 0x0168 /* D/A2 update source */
+#define LAS0_DAC2_CYCLE 0x016c /* D/A2 cycle mode */
+#define LAS0_DAC2_RESET 0x0170 /* D/A2 FIFO reset */
+#define LAS0_DAC2_FIFO_CLEAR 0x0174 /* D/A2 FIFO clear */
+#define LAS0_ADC_SCNT_SRC 0x0178 /* A/D Sample Counter Source select */
+#define LAS0_PACER_SELECT 0x0180 /* Pacer Clock select */
+#define LAS0_SBUS0_SRC 0x0184 /* SyncBus 0 Source select */
+#define LAS0_SBUS0_ENABLE 0x0188 /* SyncBus 0 enable */
+#define LAS0_SBUS1_SRC 0x018c /* SyncBus 1 Source select */
+#define LAS0_SBUS1_ENABLE 0x0190 /* SyncBus 1 enable */
+#define LAS0_SBUS2_SRC 0x0198 /* SyncBus 2 Source select */
+#define LAS0_SBUS2_ENABLE 0x019c /* SyncBus 2 enable */
+#define LAS0_ETRG_POLARITY 0x01a4 /* Ext. Trigger polarity select */
+#define LAS0_EINT_POLARITY 0x01a8 /* Ext. Interrupt polarity select */
+#define LAS0_UTC0_CLOCK 0x01ac /* UTC0 Clock select */
+#define LAS0_UTC0_GATE 0x01b0 /* UTC0 Gate select */
+#define LAS0_UTC1_CLOCK 0x01b4 /* UTC1 Clock select */
+#define LAS0_UTC1_GATE 0x01b8 /* UTC1 Gate select */
+#define LAS0_UTC2_CLOCK 0x01bc /* UTC2 Clock select */
+#define LAS0_UTC2_GATE 0x01c0 /* UTC2 Gate select */
+#define LAS0_UOUT0_SELECT 0x01c4 /* User Output 0 source select */
+#define LAS0_UOUT1_SELECT 0x01c8 /* User Output 1 source select */
+#define LAS0_DMA0_RESET 0x01cc /* DMA0 Request state machine reset */
+#define LAS0_DMA1_RESET 0x01d0 /* DMA1 Request state machine reset */
+
+/*
+ * Local Address Space 1 Offsets
+ */
+#define LAS1_ADC_FIFO 0x0000 /* A/D FIFO (16bit) */
+#define LAS1_HDIO_FIFO 0x0004 /* HiSpd DI FIFO (16bit) */
+#define LAS1_DAC1_FIFO 0x0008 /* D/A1 FIFO (16bit) */
+#define LAS1_DAC2_FIFO 0x000c /* D/A2 FIFO (16bit) */
+
+/*======================================================================
+ Driver specific stuff (tunable)
+======================================================================*/
+
+/* We really only need 2 buffers. More than that means being much
+ smarter about knowing which ones are full. */
+#define DMA_CHAIN_COUNT 2 /* max DMA segments/buffers in a ring (min 2) */
+
+/* Target period for periodic transfers. This sets the user read latency. */
+/* Note: There are certain rates where we give this up and transfer 1/2 FIFO */
+/* If this is too low, efficiency is poor */
+#define TRANS_TARGET_PERIOD 10000000 /* 10 ms (in nanoseconds) */
+
+/* Set a practical limit on how long a list to support (affects memory use) */
+/* The board support a channel list up to the FIFO length (1K or 8K) */
+#define RTD_MAX_CHANLIST 128 /* max channel list that we allow */
+
+/*======================================================================
+ Board specific stuff
+======================================================================*/
+
+#define RTD_CLOCK_RATE 8000000 /* 8Mhz onboard clock */
+#define RTD_CLOCK_BASE 125 /* clock period in ns */
+
+/* Note: these speed are slower than the spec, but fit the counter resolution*/
+#define RTD_MAX_SPEED 1625 /* when sampling, in nanoseconds */
+/* max speed if we don't have to wait for settling */
+#define RTD_MAX_SPEED_1 875 /* if single channel, in nanoseconds */
+
+#define RTD_MIN_SPEED 2097151875 /* (24bit counter) in nanoseconds */
+/* min speed when only 1 channel (no burst counter) */
+#define RTD_MIN_SPEED_1 5000000 /* 200Hz, in nanoseconds */
+
+/* Setup continuous ring of 1/2 FIFO transfers. See RTD manual p91 */
+#define DMA_MODE_BITS (\
+ PLX_LOCAL_BUS_16_WIDE_BITS \
+ | PLX_DMA_EN_READYIN_BIT \
+ | PLX_DMA_LOCAL_BURST_EN_BIT \
+ | PLX_EN_CHAIN_BIT \
+ | PLX_DMA_INTR_PCI_BIT \
+ | PLX_LOCAL_ADDR_CONST_BIT \
+ | PLX_DEMAND_MODE_BIT)
+
+#define DMA_TRANSFER_BITS (\
+/* descriptors in PCI memory*/ PLX_DESC_IN_PCI_BIT \
+/* interrupt at end of block */ | PLX_INTR_TERM_COUNT \
+/* from board to PCI */ | PLX_XFER_LOCAL_TO_PCI)
+
+/*======================================================================
+ Comedi specific stuff
+======================================================================*/
+
+/*
+ * The board has 3 input modes and the gains of 1,2,4,...32 (, 64, 128)
+ */
+static const struct comedi_lrange rtd_ai_7520_range = {
+ 18, {
+ /* +-5V input range gain steps */
+ BIP_RANGE(5.0),
+ BIP_RANGE(5.0 / 2),
+ BIP_RANGE(5.0 / 4),
+ BIP_RANGE(5.0 / 8),
+ BIP_RANGE(5.0 / 16),
+ BIP_RANGE(5.0 / 32),
+ /* +-10V input range gain steps */
+ BIP_RANGE(10.0),
+ BIP_RANGE(10.0 / 2),
+ BIP_RANGE(10.0 / 4),
+ BIP_RANGE(10.0 / 8),
+ BIP_RANGE(10.0 / 16),
+ BIP_RANGE(10.0 / 32),
+ /* +10V input range gain steps */
+ UNI_RANGE(10.0),
+ UNI_RANGE(10.0 / 2),
+ UNI_RANGE(10.0 / 4),
+ UNI_RANGE(10.0 / 8),
+ UNI_RANGE(10.0 / 16),
+ UNI_RANGE(10.0 / 32),
+ }
+};
+
+/* PCI4520 has two more gains (6 more entries) */
+static const struct comedi_lrange rtd_ai_4520_range = {
+ 24, {
+ /* +-5V input range gain steps */
+ BIP_RANGE(5.0),
+ BIP_RANGE(5.0 / 2),
+ BIP_RANGE(5.0 / 4),
+ BIP_RANGE(5.0 / 8),
+ BIP_RANGE(5.0 / 16),
+ BIP_RANGE(5.0 / 32),
+ BIP_RANGE(5.0 / 64),
+ BIP_RANGE(5.0 / 128),
+ /* +-10V input range gain steps */
+ BIP_RANGE(10.0),
+ BIP_RANGE(10.0 / 2),
+ BIP_RANGE(10.0 / 4),
+ BIP_RANGE(10.0 / 8),
+ BIP_RANGE(10.0 / 16),
+ BIP_RANGE(10.0 / 32),
+ BIP_RANGE(10.0 / 64),
+ BIP_RANGE(10.0 / 128),
+ /* +10V input range gain steps */
+ UNI_RANGE(10.0),
+ UNI_RANGE(10.0 / 2),
+ UNI_RANGE(10.0 / 4),
+ UNI_RANGE(10.0 / 8),
+ UNI_RANGE(10.0 / 16),
+ UNI_RANGE(10.0 / 32),
+ UNI_RANGE(10.0 / 64),
+ UNI_RANGE(10.0 / 128),
+ }
+};
+
+/* Table order matches range values */
+static const struct comedi_lrange rtd_ao_range = {
+ 4, {
+ UNI_RANGE(5),
+ UNI_RANGE(10),
+ BIP_RANGE(5),
+ BIP_RANGE(10),
+ }
+};
+
+enum rtd_boardid {
+ BOARD_DM7520,
+ BOARD_PCI4520,
+};
+
+struct rtd_boardinfo {
+ const char *name;
+ int range_bip10; /* start of +-10V range */
+ int range_uni10; /* start of +10V range */
+ const struct comedi_lrange *ai_range;
+};
+
+static const struct rtd_boardinfo rtd520Boards[] = {
+ [BOARD_DM7520] = {
+ .name = "DM7520",
+ .range_bip10 = 6,
+ .range_uni10 = 12,
+ .ai_range = &rtd_ai_7520_range,
+ },
+ [BOARD_PCI4520] = {
+ .name = "PCI4520",
+ .range_bip10 = 8,
+ .range_uni10 = 16,
+ .ai_range = &rtd_ai_4520_range,
+ },
+};
+
+struct rtd_private {
+ /* memory mapped board structures */
+ void __iomem *las1;
+ void __iomem *lcfg;
+
+ long ai_count; /* total transfer size (samples) */
+ int xfer_count; /* # to transfer data. 0->1/2FIFO */
+ int flags; /* flag event modes */
+ unsigned fifosz;
+};
+
+/* bit defines for "flags" */
+#define SEND_EOS 0x01 /* send End Of Scan events */
+#define DMA0_ACTIVE 0x02 /* DMA0 is active */
+#define DMA1_ACTIVE 0x04 /* DMA1 is active */
+
+/*
+ Given a desired period and the clock period (both in ns),
+ return the proper counter value (divider-1).
+ Sets the original period to be the true value.
+ Note: you have to check if the value is larger than the counter range!
+*/
+static int rtd_ns_to_timer_base(unsigned int *nanosec,
+ unsigned int flags, int base)
+{
+ int divider;
+
+ switch (flags & CMDF_ROUND_MASK) {
+ case CMDF_ROUND_NEAREST:
+ default:
+ divider = (*nanosec + base / 2) / base;
+ break;
+ case CMDF_ROUND_DOWN:
+ divider = (*nanosec) / base;
+ break;
+ case CMDF_ROUND_UP:
+ divider = (*nanosec + base - 1) / base;
+ break;
+ }
+ if (divider < 2)
+ divider = 2; /* min is divide by 2 */
+
+ /* Note: we don't check for max, because different timers
+ have different ranges */
+
+ *nanosec = base * divider;
+ return divider - 1; /* countdown is divisor+1 */
+}
+
+/*
+ Given a desired period (in ns),
+ return the proper counter value (divider-1) for the internal clock.
+ Sets the original period to be the true value.
+*/
+static int rtd_ns_to_timer(unsigned int *ns, unsigned int flags)
+{
+ return rtd_ns_to_timer_base(ns, flags, RTD_CLOCK_BASE);
+}
+
+/*
+ Convert a single comedi channel-gain entry to a RTD520 table entry
+*/
+static unsigned short rtd_convert_chan_gain(struct comedi_device *dev,
+ unsigned int chanspec, int index)
+{
+ const struct rtd_boardinfo *board = dev->board_ptr;
+ unsigned int chan = CR_CHAN(chanspec);
+ unsigned int range = CR_RANGE(chanspec);
+ unsigned int aref = CR_AREF(chanspec);
+ unsigned short r = 0;
+
+ r |= chan & 0xf;
+
+ /* Note: we also setup the channel list bipolar flag array */
+ if (range < board->range_bip10) {
+ /* +-5 range */
+ r |= 0x000;
+ r |= (range & 0x7) << 4;
+ } else if (range < board->range_uni10) {
+ /* +-10 range */
+ r |= 0x100;
+ r |= ((range - board->range_bip10) & 0x7) << 4;
+ } else {
+ /* +10 range */
+ r |= 0x200;
+ r |= ((range - board->range_uni10) & 0x7) << 4;
+ }
+
+ switch (aref) {
+ case AREF_GROUND: /* on-board ground */
+ break;
+
+ case AREF_COMMON:
+ r |= 0x80; /* ref external analog common */
+ break;
+
+ case AREF_DIFF:
+ r |= 0x400; /* differential inputs */
+ break;
+
+ case AREF_OTHER: /* ??? */
+ break;
+ }
+ return r;
+}
+
+/*
+ Setup the channel-gain table from a comedi list
+*/
+static void rtd_load_channelgain_list(struct comedi_device *dev,
+ unsigned int n_chan, unsigned int *list)
+{
+ if (n_chan > 1) { /* setup channel gain table */
+ int ii;
+
+ writel(0, dev->mmio + LAS0_CGT_CLEAR);
+ writel(1, dev->mmio + LAS0_CGT_ENABLE);
+ for (ii = 0; ii < n_chan; ii++) {
+ writel(rtd_convert_chan_gain(dev, list[ii], ii),
+ dev->mmio + LAS0_CGT_WRITE);
+ }
+ } else { /* just use the channel gain latch */
+ writel(0, dev->mmio + LAS0_CGT_ENABLE);
+ writel(rtd_convert_chan_gain(dev, list[0], 0),
+ dev->mmio + LAS0_CGL_WRITE);
+ }
+}
+
+/* determine fifo size by doing adc conversions until the fifo half
+empty status flag clears */
+static int rtd520_probe_fifo_depth(struct comedi_device *dev)
+{
+ unsigned int chanspec = CR_PACK(0, 0, AREF_GROUND);
+ unsigned i;
+ static const unsigned limit = 0x2000;
+ unsigned fifo_size = 0;
+
+ writel(0, dev->mmio + LAS0_ADC_FIFO_CLEAR);
+ rtd_load_channelgain_list(dev, 1, &chanspec);
+ /* ADC conversion trigger source: SOFTWARE */
+ writel(0, dev->mmio + LAS0_ADC_CONVERSION);
+ /* convert samples */
+ for (i = 0; i < limit; ++i) {
+ unsigned fifo_status;
+ /* trigger conversion */
+ writew(0, dev->mmio + LAS0_ADC);
+ udelay(1);
+ fifo_status = readl(dev->mmio + LAS0_ADC);
+ if ((fifo_status & FS_ADC_HEMPTY) == 0) {
+ fifo_size = 2 * i;
+ break;
+ }
+ }
+ if (i == limit) {
+ dev_info(dev->class_dev, "failed to probe fifo size.\n");
+ return -EIO;
+ }
+ writel(0, dev->mmio + LAS0_ADC_FIFO_CLEAR);
+ if (fifo_size != 0x400 && fifo_size != 0x2000) {
+ dev_info(dev->class_dev,
+ "unexpected fifo size of %i, expected 1024 or 8192.\n",
+ fifo_size);
+ return -EIO;
+ }
+ return fifo_size;
+}
+
+static int rtd_ai_eoc(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned long context)
+{
+ unsigned int status;
+
+ status = readl(dev->mmio + LAS0_ADC);
+ if (status & FS_ADC_NOT_EMPTY)
+ return 0;
+ return -EBUSY;
+}
+
+static int rtd_ai_rinsn(struct comedi_device *dev,
+ struct comedi_subdevice *s, struct comedi_insn *insn,
+ unsigned int *data)
+{
+ struct rtd_private *devpriv = dev->private;
+ unsigned int range = CR_RANGE(insn->chanspec);
+ int ret;
+ int n;
+
+ /* clear any old fifo data */
+ writel(0, dev->mmio + LAS0_ADC_FIFO_CLEAR);
+
+ /* write channel to multiplexer and clear channel gain table */
+ rtd_load_channelgain_list(dev, 1, &insn->chanspec);
+
+ /* ADC conversion trigger source: SOFTWARE */
+ writel(0, dev->mmio + LAS0_ADC_CONVERSION);
+
+ /* convert n samples */
+ for (n = 0; n < insn->n; n++) {
+ unsigned short d;
+ /* trigger conversion */
+ writew(0, dev->mmio + LAS0_ADC);
+
+ ret = comedi_timeout(dev, s, insn, rtd_ai_eoc, 0);
+ if (ret)
+ return ret;
+
+ /* read data */
+ d = readw(devpriv->las1 + LAS1_ADC_FIFO);
+ d >>= 3; /* low 3 bits are marker lines */
+
+ /* convert bipolar data to comedi unsigned data */
+ if (comedi_range_is_bipolar(s, range))
+ d = comedi_offset_munge(s, d);
+
+ data[n] = d & s->maxdata;
+ }
+
+ /* return the number of samples read/written */
+ return n;
+}
+
+/*
+ Get what we know is there.... Fast!
+ This uses 1/2 the bus cycles of read_dregs (below).
+
+ The manual claims that we can do a lword read, but it doesn't work here.
+*/
+static int ai_read_n(struct comedi_device *dev, struct comedi_subdevice *s,
+ int count)
+{
+ struct rtd_private *devpriv = dev->private;
+ struct comedi_async *async = s->async;
+ struct comedi_cmd *cmd = &async->cmd;
+ int ii;
+
+ for (ii = 0; ii < count; ii++) {
+ unsigned int range = CR_RANGE(cmd->chanlist[async->cur_chan]);
+ unsigned short d;
+
+ if (0 == devpriv->ai_count) { /* done */
+ d = readw(devpriv->las1 + LAS1_ADC_FIFO);
+ continue;
+ }
+
+ d = readw(devpriv->las1 + LAS1_ADC_FIFO);
+ d >>= 3; /* low 3 bits are marker lines */
+
+ /* convert bipolar data to comedi unsigned data */
+ if (comedi_range_is_bipolar(s, range))
+ d = comedi_offset_munge(s, d);
+ d &= s->maxdata;
+
+ if (!comedi_buf_write_samples(s, &d, 1))
+ return -1;
+
+ if (devpriv->ai_count > 0) /* < 0, means read forever */
+ devpriv->ai_count--;
+ }
+ return 0;
+}
+
+/*
+ Handle all rtd520 interrupts.
+ Runs atomically and is never re-entered.
+ This is a "slow handler"; other interrupts may be active.
+ The data conversion may someday happen in a "bottom half".
+*/
+static irqreturn_t rtd_interrupt(int irq, void *d)
+{
+ struct comedi_device *dev = d;
+ struct comedi_subdevice *s = dev->read_subdev;
+ struct rtd_private *devpriv = dev->private;
+ u32 overrun;
+ u16 status;
+ u16 fifo_status;
+
+ if (!dev->attached)
+ return IRQ_NONE;
+
+ fifo_status = readl(dev->mmio + LAS0_ADC);
+ /* check for FIFO full, this automatically halts the ADC! */
+ if (!(fifo_status & FS_ADC_NOT_FULL)) /* 0 -> full */
+ goto xfer_abort;
+
+ status = readw(dev->mmio + LAS0_IT);
+ /* if interrupt was not caused by our board, or handled above */
+ if (0 == status)
+ return IRQ_HANDLED;
+
+ if (status & IRQM_ADC_ABOUT_CNT) { /* sample count -> read FIFO */
+ /*
+ * since the priority interrupt controller may have queued
+ * a sample counter interrupt, even though we have already
+ * finished, we must handle the possibility that there is
+ * no data here
+ */
+ if (!(fifo_status & FS_ADC_HEMPTY)) {
+ /* FIFO half full */
+ if (ai_read_n(dev, s, devpriv->fifosz / 2) < 0)
+ goto xfer_abort;
+
+ if (0 == devpriv->ai_count)
+ goto xfer_done;
+ } else if (devpriv->xfer_count > 0) {
+ if (fifo_status & FS_ADC_NOT_EMPTY) {
+ /* FIFO not empty */
+ if (ai_read_n(dev, s, devpriv->xfer_count) < 0)
+ goto xfer_abort;
+
+ if (0 == devpriv->ai_count)
+ goto xfer_done;
+ }
+ }
+ }
+
+ overrun = readl(dev->mmio + LAS0_OVERRUN) & 0xffff;
+ if (overrun)
+ goto xfer_abort;
+
+ /* clear the interrupt */
+ writew(status, dev->mmio + LAS0_CLEAR);
+ readw(dev->mmio + LAS0_CLEAR);
+
+ comedi_handle_events(dev, s);
+
+ return IRQ_HANDLED;
+
+xfer_abort:
+ s->async->events |= COMEDI_CB_ERROR;
+
+xfer_done:
+ s->async->events |= COMEDI_CB_EOA;
+
+ /* clear the interrupt */
+ status = readw(dev->mmio + LAS0_IT);
+ writew(status, dev->mmio + LAS0_CLEAR);
+ readw(dev->mmio + LAS0_CLEAR);
+
+ fifo_status = readl(dev->mmio + LAS0_ADC);
+ overrun = readl(dev->mmio + LAS0_OVERRUN) & 0xffff;
+
+ comedi_handle_events(dev, s);
+
+ return IRQ_HANDLED;
+}
+
+/*
+ cmdtest tests a particular command to see if it is valid.
+ Using the cmdtest ioctl, a user can create a valid cmd
+ and then have it executed by the cmd ioctl (asynchronously).
+
+ cmdtest returns 1,2,3,4 or 0, depending on which tests
+ the command passes.
+*/
+
+static int rtd_ai_cmdtest(struct comedi_device *dev,
+ struct comedi_subdevice *s, struct comedi_cmd *cmd)
+{
+ int err = 0;
+ unsigned int arg;
+
+ /* Step 1 : check if triggers are trivially valid */
+
+ err |= comedi_check_trigger_src(&cmd->start_src, TRIG_NOW);
+ err |= comedi_check_trigger_src(&cmd->scan_begin_src,
+ TRIG_TIMER | TRIG_EXT);
+ err |= comedi_check_trigger_src(&cmd->convert_src,
+ TRIG_TIMER | TRIG_EXT);
+ err |= comedi_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
+ err |= comedi_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE);
+
+ if (err)
+ return 1;
+
+ /* Step 2a : make sure trigger sources are unique */
+
+ err |= comedi_check_trigger_is_unique(cmd->scan_begin_src);
+ err |= comedi_check_trigger_is_unique(cmd->convert_src);
+ err |= comedi_check_trigger_is_unique(cmd->stop_src);
+
+ /* Step 2b : and mutually compatible */
+
+ if (err)
+ return 2;
+
+ /* Step 3: check if arguments are trivially valid */
+
+ err |= comedi_check_trigger_arg_is(&cmd->start_arg, 0);
+
+ if (cmd->scan_begin_src == TRIG_TIMER) {
+ /* Note: these are time periods, not actual rates */
+ if (1 == cmd->chanlist_len) { /* no scanning */
+ if (comedi_check_trigger_arg_min(&cmd->scan_begin_arg,
+ RTD_MAX_SPEED_1)) {
+ rtd_ns_to_timer(&cmd->scan_begin_arg,
+ CMDF_ROUND_UP);
+ err |= -EINVAL;
+ }
+ if (comedi_check_trigger_arg_max(&cmd->scan_begin_arg,
+ RTD_MIN_SPEED_1)) {
+ rtd_ns_to_timer(&cmd->scan_begin_arg,
+ CMDF_ROUND_DOWN);
+ err |= -EINVAL;
+ }
+ } else {
+ if (comedi_check_trigger_arg_min(&cmd->scan_begin_arg,
+ RTD_MAX_SPEED)) {
+ rtd_ns_to_timer(&cmd->scan_begin_arg,
+ CMDF_ROUND_UP);
+ err |= -EINVAL;
+ }
+ if (comedi_check_trigger_arg_max(&cmd->scan_begin_arg,
+ RTD_MIN_SPEED)) {
+ rtd_ns_to_timer(&cmd->scan_begin_arg,
+ CMDF_ROUND_DOWN);
+ err |= -EINVAL;
+ }
+ }
+ } else {
+ /* external trigger */
+ /* should be level/edge, hi/lo specification here */
+ /* should specify multiple external triggers */
+ err |= comedi_check_trigger_arg_max(&cmd->scan_begin_arg, 9);
+ }
+
+ if (cmd->convert_src == TRIG_TIMER) {
+ if (1 == cmd->chanlist_len) { /* no scanning */
+ if (comedi_check_trigger_arg_min(&cmd->convert_arg,
+ RTD_MAX_SPEED_1)) {
+ rtd_ns_to_timer(&cmd->convert_arg,
+ CMDF_ROUND_UP);
+ err |= -EINVAL;
+ }
+ if (comedi_check_trigger_arg_max(&cmd->convert_arg,
+ RTD_MIN_SPEED_1)) {
+ rtd_ns_to_timer(&cmd->convert_arg,
+ CMDF_ROUND_DOWN);
+ err |= -EINVAL;
+ }
+ } else {
+ if (comedi_check_trigger_arg_min(&cmd->convert_arg,
+ RTD_MAX_SPEED)) {
+ rtd_ns_to_timer(&cmd->convert_arg,
+ CMDF_ROUND_UP);
+ err |= -EINVAL;
+ }
+ if (comedi_check_trigger_arg_max(&cmd->convert_arg,
+ RTD_MIN_SPEED)) {
+ rtd_ns_to_timer(&cmd->convert_arg,
+ CMDF_ROUND_DOWN);
+ err |= -EINVAL;
+ }
+ }
+ } else {
+ /* external trigger */
+ /* see above */
+ err |= comedi_check_trigger_arg_max(&cmd->convert_arg, 9);
+ }
+
+ err |= comedi_check_trigger_arg_is(&cmd->scan_end_arg,
+ cmd->chanlist_len);
+
+ if (cmd->stop_src == TRIG_COUNT)
+ err |= comedi_check_trigger_arg_min(&cmd->stop_arg, 1);
+ else /* TRIG_NONE */
+ err |= comedi_check_trigger_arg_is(&cmd->stop_arg, 0);
+
+ if (err)
+ return 3;
+
+ /* step 4: fix up any arguments */
+
+ if (cmd->scan_begin_src == TRIG_TIMER) {
+ arg = cmd->scan_begin_arg;
+ rtd_ns_to_timer(&arg, cmd->flags);
+ err |= comedi_check_trigger_arg_is(&cmd->scan_begin_arg, arg);
+ }
+
+ if (cmd->convert_src == TRIG_TIMER) {
+ arg = cmd->convert_arg;
+ rtd_ns_to_timer(&arg, cmd->flags);
+ err |= comedi_check_trigger_arg_is(&cmd->convert_arg, arg);
+
+ if (cmd->scan_begin_src == TRIG_TIMER) {
+ arg = cmd->convert_arg * cmd->scan_end_arg;
+ err |= comedi_check_trigger_arg_min(&cmd->
+ scan_begin_arg,
+ arg);
+ }
+ }
+
+ if (err)
+ return 4;
+
+ return 0;
+}
+
+/*
+ Execute a analog in command with many possible triggering options.
+ The data get stored in the async structure of the subdevice.
+ This is usually done by an interrupt handler.
+ Userland gets to the data using read calls.
+*/
+static int rtd_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
+{
+ struct rtd_private *devpriv = dev->private;
+ struct comedi_cmd *cmd = &s->async->cmd;
+ int timer;
+
+ /* stop anything currently running */
+ /* pacer stop source: SOFTWARE */
+ writel(0, dev->mmio + LAS0_PACER_STOP);
+ writel(0, dev->mmio + LAS0_PACER); /* stop pacer */
+ writel(0, dev->mmio + LAS0_ADC_CONVERSION);
+ writew(0, dev->mmio + LAS0_IT);
+ writel(0, dev->mmio + LAS0_ADC_FIFO_CLEAR);
+ writel(0, dev->mmio + LAS0_OVERRUN);
+
+ /* start configuration */
+ /* load channel list and reset CGT */
+ rtd_load_channelgain_list(dev, cmd->chanlist_len, cmd->chanlist);
+
+ /* setup the common case and override if needed */
+ if (cmd->chanlist_len > 1) {
+ /* pacer start source: SOFTWARE */
+ writel(0, dev->mmio + LAS0_PACER_START);
+ /* burst trigger source: PACER */
+ writel(1, dev->mmio + LAS0_BURST_START);
+ /* ADC conversion trigger source: BURST */
+ writel(2, dev->mmio + LAS0_ADC_CONVERSION);
+ } else { /* single channel */
+ /* pacer start source: SOFTWARE */
+ writel(0, dev->mmio + LAS0_PACER_START);
+ /* ADC conversion trigger source: PACER */
+ writel(1, dev->mmio + LAS0_ADC_CONVERSION);
+ }
+ writel((devpriv->fifosz / 2 - 1) & 0xffff, dev->mmio + LAS0_ACNT);
+
+ if (TRIG_TIMER == cmd->scan_begin_src) {
+ /* scan_begin_arg is in nanoseconds */
+ /* find out how many samples to wait before transferring */
+ if (cmd->flags & CMDF_WAKE_EOS) {
+ /*
+ * this may generate un-sustainable interrupt rates
+ * the application is responsible for doing the
+ * right thing
+ */
+ devpriv->xfer_count = cmd->chanlist_len;
+ devpriv->flags |= SEND_EOS;
+ } else {
+ /* arrange to transfer data periodically */
+ devpriv->xfer_count =
+ (TRANS_TARGET_PERIOD * cmd->chanlist_len) /
+ cmd->scan_begin_arg;
+ if (devpriv->xfer_count < cmd->chanlist_len) {
+ /* transfer after each scan (and avoid 0) */
+ devpriv->xfer_count = cmd->chanlist_len;
+ } else { /* make a multiple of scan length */
+ devpriv->xfer_count =
+ (devpriv->xfer_count +
+ cmd->chanlist_len - 1)
+ / cmd->chanlist_len;
+ devpriv->xfer_count *= cmd->chanlist_len;
+ }
+ devpriv->flags |= SEND_EOS;
+ }
+ if (devpriv->xfer_count >= (devpriv->fifosz / 2)) {
+ /* out of counter range, use 1/2 fifo instead */
+ devpriv->xfer_count = 0;
+ devpriv->flags &= ~SEND_EOS;
+ } else {
+ /* interrupt for each transfer */
+ writel((devpriv->xfer_count - 1) & 0xffff,
+ dev->mmio + LAS0_ACNT);
+ }
+ } else { /* unknown timing, just use 1/2 FIFO */
+ devpriv->xfer_count = 0;
+ devpriv->flags &= ~SEND_EOS;
+ }
+ /* pacer clock source: INTERNAL 8MHz */
+ writel(1, dev->mmio + LAS0_PACER_SELECT);
+ /* just interrupt, don't stop */
+ writel(1, dev->mmio + LAS0_ACNT_STOP_ENABLE);
+
+ /* BUG??? these look like enumerated values, but they are bit fields */
+
+ /* First, setup when to stop */
+ switch (cmd->stop_src) {
+ case TRIG_COUNT: /* stop after N scans */
+ devpriv->ai_count = cmd->stop_arg * cmd->chanlist_len;
+ if ((devpriv->xfer_count > 0)
+ && (devpriv->xfer_count > devpriv->ai_count)) {
+ devpriv->xfer_count = devpriv->ai_count;
+ }
+ break;
+
+ case TRIG_NONE: /* stop when cancel is called */
+ devpriv->ai_count = -1; /* read forever */
+ break;
+ }
+
+ /* Scan timing */
+ switch (cmd->scan_begin_src) {
+ case TRIG_TIMER: /* periodic scanning */
+ timer = rtd_ns_to_timer(&cmd->scan_begin_arg,
+ CMDF_ROUND_NEAREST);
+ /* set PACER clock */
+ writel(timer & 0xffffff, dev->mmio + LAS0_PCLK);
+
+ break;
+
+ case TRIG_EXT:
+ /* pacer start source: EXTERNAL */
+ writel(1, dev->mmio + LAS0_PACER_START);
+ break;
+ }
+
+ /* Sample timing within a scan */
+ switch (cmd->convert_src) {
+ case TRIG_TIMER: /* periodic */
+ if (cmd->chanlist_len > 1) {
+ /* only needed for multi-channel */
+ timer = rtd_ns_to_timer(&cmd->convert_arg,
+ CMDF_ROUND_NEAREST);
+ /* setup BURST clock */
+ writel(timer & 0x3ff, dev->mmio + LAS0_BCLK);
+ }
+
+ break;
+
+ case TRIG_EXT: /* external */
+ /* burst trigger source: EXTERNAL */
+ writel(2, dev->mmio + LAS0_BURST_START);
+ break;
+ }
+ /* end configuration */
+
+ /* This doesn't seem to work. There is no way to clear an interrupt
+ that the priority controller has queued! */
+ writew(~0, dev->mmio + LAS0_CLEAR);
+ readw(dev->mmio + LAS0_CLEAR);
+
+ /* TODO: allow multiple interrupt sources */
+ /* transfer every N samples */
+ writew(IRQM_ADC_ABOUT_CNT, dev->mmio + LAS0_IT);
+
+ /* BUG: start_src is ASSUMED to be TRIG_NOW */
+ /* BUG? it seems like things are running before the "start" */
+ readl(dev->mmio + LAS0_PACER); /* start pacer */
+ return 0;
+}
+
+/*
+ Stop a running data acquisition.
+*/
+static int rtd_ai_cancel(struct comedi_device *dev, struct comedi_subdevice *s)
+{
+ struct rtd_private *devpriv = dev->private;
+
+ /* pacer stop source: SOFTWARE */
+ writel(0, dev->mmio + LAS0_PACER_STOP);
+ writel(0, dev->mmio + LAS0_PACER); /* stop pacer */
+ writel(0, dev->mmio + LAS0_ADC_CONVERSION);
+ writew(0, dev->mmio + LAS0_IT);
+ devpriv->ai_count = 0; /* stop and don't transfer any more */
+ writel(0, dev->mmio + LAS0_ADC_FIFO_CLEAR);
+ return 0;
+}
+
+static int rtd_ao_eoc(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned long context)
+{
+ unsigned int chan = CR_CHAN(insn->chanspec);
+ unsigned int bit = (chan == 0) ? FS_DAC1_NOT_EMPTY : FS_DAC2_NOT_EMPTY;
+ unsigned int status;
+
+ status = readl(dev->mmio + LAS0_ADC);
+ if (status & bit)
+ return 0;
+ return -EBUSY;
+}
+
+static int rtd_ao_winsn(struct comedi_device *dev,
+ struct comedi_subdevice *s, struct comedi_insn *insn,
+ unsigned int *data)
+{
+ struct rtd_private *devpriv = dev->private;
+ int i;
+ int chan = CR_CHAN(insn->chanspec);
+ int range = CR_RANGE(insn->chanspec);
+ int ret;
+
+ /* Configure the output range (table index matches the range values) */
+ writew(range & 7,
+ dev->mmio + ((chan == 0) ? LAS0_DAC1_CTRL : LAS0_DAC2_CTRL));
+
+ /* Writing a list of values to an AO channel is probably not
+ * very useful, but that's how the interface is defined. */
+ for (i = 0; i < insn->n; ++i) {
+ int val = data[i] << 3;
+
+ /* VERIFY: comedi range and offset conversions */
+
+ if ((range > 1) /* bipolar */
+ && (data[i] < 2048)) {
+ /* offset and sign extend */
+ val = (((int)data[i]) - 2048) << 3;
+ } else { /* unipolor */
+ val = data[i] << 3;
+ }
+
+ /* a typical programming sequence */
+ writew(val, devpriv->las1 +
+ ((chan == 0) ? LAS1_DAC1_FIFO : LAS1_DAC2_FIFO));
+ writew(0, dev->mmio + ((chan == 0) ? LAS0_DAC1 : LAS0_DAC2));
+
+ s->readback[chan] = data[i];
+
+ ret = comedi_timeout(dev, s, insn, rtd_ao_eoc, 0);
+ if (ret)
+ return ret;
+ }
+
+ /* return the number of samples read/written */
+ return i;
+}
+
+static int rtd_dio_insn_bits(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ if (comedi_dio_update_state(s, data))
+ writew(s->state & 0xff, dev->mmio + LAS0_DIO0);
+
+ data[1] = readw(dev->mmio + LAS0_DIO0) & 0xff;
+
+ return insn->n;
+}
+
+static int rtd_dio_insn_config(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ int ret;
+
+ ret = comedi_dio_insn_config(dev, s, insn, data, 0);
+ if (ret)
+ return ret;
+
+ /* TODO support digital match interrupts and strobes */
+
+ /* set direction */
+ writew(0x01, dev->mmio + LAS0_DIO_STATUS);
+ writew(s->io_bits & 0xff, dev->mmio + LAS0_DIO0_CTRL);
+
+ /* clear interrupts */
+ writew(0x00, dev->mmio + LAS0_DIO_STATUS);
+
+ /* port1 can only be all input or all output */
+
+ /* there are also 2 user input lines and 2 user output lines */
+
+ return insn->n;
+}
+
+static void rtd_reset(struct comedi_device *dev)
+{
+ struct rtd_private *devpriv = dev->private;
+
+ writel(0, dev->mmio + LAS0_BOARD_RESET);
+ udelay(100); /* needed? */
+ writel(0, devpriv->lcfg + PLX_INTRCS_REG);
+ writew(0, dev->mmio + LAS0_IT);
+ writew(~0, dev->mmio + LAS0_CLEAR);
+ readw(dev->mmio + LAS0_CLEAR);
+}
+
+/*
+ * initialize board, per RTD spec
+ * also, initialize shadow registers
+ */
+static void rtd_init_board(struct comedi_device *dev)
+{
+ rtd_reset(dev);
+
+ writel(0, dev->mmio + LAS0_OVERRUN);
+ writel(0, dev->mmio + LAS0_CGT_CLEAR);
+ writel(0, dev->mmio + LAS0_ADC_FIFO_CLEAR);
+ writel(0, dev->mmio + LAS0_DAC1_RESET);
+ writel(0, dev->mmio + LAS0_DAC2_RESET);
+ /* clear digital IO fifo */
+ writew(0, dev->mmio + LAS0_DIO_STATUS);
+ writeb((0 << 6) | 0x30, dev->mmio + LAS0_UTC_CTRL);
+ writeb((1 << 6) | 0x30, dev->mmio + LAS0_UTC_CTRL);
+ writeb((2 << 6) | 0x30, dev->mmio + LAS0_UTC_CTRL);
+ writeb((3 << 6) | 0x00, dev->mmio + LAS0_UTC_CTRL);
+ /* TODO: set user out source ??? */
+}
+
+/* The RTD driver does this */
+static void rtd_pci_latency_quirk(struct comedi_device *dev,
+ struct pci_dev *pcidev)
+{
+ unsigned char pci_latency;
+
+ pci_read_config_byte(pcidev, PCI_LATENCY_TIMER, &pci_latency);
+ if (pci_latency < 32) {
+ dev_info(dev->class_dev,
+ "PCI latency changed from %d to %d\n",
+ pci_latency, 32);
+ pci_write_config_byte(pcidev, PCI_LATENCY_TIMER, 32);
+ }
+}
+
+static int rtd_auto_attach(struct comedi_device *dev,
+ unsigned long context)
+{
+ struct pci_dev *pcidev = comedi_to_pci_dev(dev);
+ const struct rtd_boardinfo *board = NULL;
+ struct rtd_private *devpriv;
+ struct comedi_subdevice *s;
+ int ret;
+
+ if (context < ARRAY_SIZE(rtd520Boards))
+ board = &rtd520Boards[context];
+ if (!board)
+ return -ENODEV;
+ dev->board_ptr = board;
+ dev->board_name = board->name;
+
+ devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
+ if (!devpriv)
+ return -ENOMEM;
+
+ ret = comedi_pci_enable(dev);
+ if (ret)
+ return ret;
+
+ dev->mmio = pci_ioremap_bar(pcidev, 2);
+ devpriv->las1 = pci_ioremap_bar(pcidev, 3);
+ devpriv->lcfg = pci_ioremap_bar(pcidev, 0);
+ if (!dev->mmio || !devpriv->las1 || !devpriv->lcfg)
+ return -ENOMEM;
+
+ rtd_pci_latency_quirk(dev, pcidev);
+
+ if (pcidev->irq) {
+ ret = request_irq(pcidev->irq, rtd_interrupt, IRQF_SHARED,
+ dev->board_name, dev);
+ if (ret == 0)
+ dev->irq = pcidev->irq;
+ }
+
+ ret = comedi_alloc_subdevices(dev, 4);
+ if (ret)
+ return ret;
+
+ s = &dev->subdevices[0];
+ /* analog input subdevice */
+ s->type = COMEDI_SUBD_AI;
+ s->subdev_flags = SDF_READABLE | SDF_GROUND | SDF_COMMON | SDF_DIFF;
+ s->n_chan = 16;
+ s->maxdata = 0x0fff;
+ s->range_table = board->ai_range;
+ s->len_chanlist = RTD_MAX_CHANLIST;
+ s->insn_read = rtd_ai_rinsn;
+ if (dev->irq) {
+ dev->read_subdev = s;
+ s->subdev_flags |= SDF_CMD_READ;
+ s->do_cmd = rtd_ai_cmd;
+ s->do_cmdtest = rtd_ai_cmdtest;
+ s->cancel = rtd_ai_cancel;
+ }
+
+ s = &dev->subdevices[1];
+ /* analog output subdevice */
+ s->type = COMEDI_SUBD_AO;
+ s->subdev_flags = SDF_WRITABLE;
+ s->n_chan = 2;
+ s->maxdata = 0x0fff;
+ s->range_table = &rtd_ao_range;
+ s->insn_write = rtd_ao_winsn;
+
+ ret = comedi_alloc_subdev_readback(s);
+ if (ret)
+ return ret;
+
+ s = &dev->subdevices[2];
+ /* digital i/o subdevice */
+ s->type = COMEDI_SUBD_DIO;
+ s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
+ /* we only support port 0 right now. Ignoring port 1 and user IO */
+ s->n_chan = 8;
+ s->maxdata = 1;
+ s->range_table = &range_digital;
+ s->insn_bits = rtd_dio_insn_bits;
+ s->insn_config = rtd_dio_insn_config;
+
+ /* timer/counter subdevices (not currently supported) */
+ s = &dev->subdevices[3];
+ s->type = COMEDI_SUBD_COUNTER;
+ s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
+ s->n_chan = 3;
+ s->maxdata = 0xffff;
+
+ rtd_init_board(dev);
+
+ ret = rtd520_probe_fifo_depth(dev);
+ if (ret < 0)
+ return ret;
+ devpriv->fifosz = ret;
+
+ if (dev->irq)
+ writel(ICS_PIE | ICS_PLIE, devpriv->lcfg + PLX_INTRCS_REG);
+
+ return 0;
+}
+
+static void rtd_detach(struct comedi_device *dev)
+{
+ struct rtd_private *devpriv = dev->private;
+
+ if (devpriv) {
+ /* Shut down any board ops by resetting it */
+ if (dev->mmio && devpriv->lcfg)
+ rtd_reset(dev);
+ if (dev->irq)
+ free_irq(dev->irq, dev);
+ if (dev->mmio)
+ iounmap(dev->mmio);
+ if (devpriv->las1)
+ iounmap(devpriv->las1);
+ if (devpriv->lcfg)
+ iounmap(devpriv->lcfg);
+ }
+ comedi_pci_disable(dev);
+}
+
+static struct comedi_driver rtd520_driver = {
+ .driver_name = "rtd520",
+ .module = THIS_MODULE,
+ .auto_attach = rtd_auto_attach,
+ .detach = rtd_detach,
+};
+
+static int rtd520_pci_probe(struct pci_dev *dev,
+ const struct pci_device_id *id)
+{
+ return comedi_pci_auto_config(dev, &rtd520_driver, id->driver_data);
+}
+
+static const struct pci_device_id rtd520_pci_table[] = {
+ { PCI_VDEVICE(RTD, 0x7520), BOARD_DM7520 },
+ { PCI_VDEVICE(RTD, 0x4520), BOARD_PCI4520 },
+ { 0 }
+};
+MODULE_DEVICE_TABLE(pci, rtd520_pci_table);
+
+static struct pci_driver rtd520_pci_driver = {
+ .name = "rtd520",
+ .id_table = rtd520_pci_table,
+ .probe = rtd520_pci_probe,
+ .remove = comedi_pci_auto_unconfig,
+};
+module_comedi_pci_driver(rtd520_driver, rtd520_pci_driver);
+
+MODULE_AUTHOR("Comedi http://www.comedi.org");
+MODULE_DESCRIPTION("Comedi low-level driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/comedi/drivers/rti800.c b/drivers/staging/comedi/drivers/rti800.c
new file mode 100644
index 000000000..340ac776e
--- /dev/null
+++ b/drivers/staging/comedi/drivers/rti800.c
@@ -0,0 +1,362 @@
+/*
+ * comedi/drivers/rti800.c
+ * Hardware driver for Analog Devices RTI-800/815 board
+ *
+ * COMEDI - Linux Control and Measurement Device Interface
+ * Copyright (C) 1998 David A. Schleef <ds@schleef.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+/*
+ * Driver: rti800
+ * Description: Analog Devices RTI-800/815
+ * Devices: [Analog Devices] RTI-800 (rti800), RTI-815 (rti815)
+ * Author: David A. Schleef <ds@schleef.org>
+ * Status: unknown
+ * Updated: Fri, 05 Sep 2008 14:50:44 +0100
+ *
+ * Configuration options:
+ * [0] - I/O port base address
+ * [1] - IRQ (not supported / unused)
+ * [2] - A/D mux/reference (number of channels)
+ * 0 = differential
+ * 1 = pseudodifferential (common)
+ * 2 = single-ended
+ * [3] - A/D range
+ * 0 = [-10,10]
+ * 1 = [-5,5]
+ * 2 = [0,10]
+ * [4] - A/D encoding
+ * 0 = two's complement
+ * 1 = straight binary
+ * [5] - DAC 0 range
+ * 0 = [-10,10]
+ * 1 = [0,10]
+ * [6] - DAC 0 encoding
+ * 0 = two's complement
+ * 1 = straight binary
+ * [7] - DAC 1 range (same as DAC 0)
+ * [8] - DAC 1 encoding (same as DAC 0)
+ */
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include "../comedidev.h"
+
+/*
+ * Register map
+ */
+#define RTI800_CSR 0x00
+#define RTI800_CSR_BUSY (1 << 7)
+#define RTI800_CSR_DONE (1 << 6)
+#define RTI800_CSR_OVERRUN (1 << 5)
+#define RTI800_CSR_TCR (1 << 4)
+#define RTI800_CSR_DMA_ENAB (1 << 3)
+#define RTI800_CSR_INTR_TC (1 << 2)
+#define RTI800_CSR_INTR_EC (1 << 1)
+#define RTI800_CSR_INTR_OVRN (1 << 0)
+#define RTI800_MUXGAIN 0x01
+#define RTI800_CONVERT 0x02
+#define RTI800_ADCLO 0x03
+#define RTI800_ADCHI 0x04
+#define RTI800_DAC0LO 0x05
+#define RTI800_DAC0HI 0x06
+#define RTI800_DAC1LO 0x07
+#define RTI800_DAC1HI 0x08
+#define RTI800_CLRFLAGS 0x09
+#define RTI800_DI 0x0a
+#define RTI800_DO 0x0b
+#define RTI800_9513A_DATA 0x0c
+#define RTI800_9513A_CNTRL 0x0d
+#define RTI800_9513A_STATUS 0x0d
+
+static const struct comedi_lrange range_rti800_ai_10_bipolar = {
+ 4, {
+ BIP_RANGE(10),
+ BIP_RANGE(1),
+ BIP_RANGE(0.1),
+ BIP_RANGE(0.02)
+ }
+};
+
+static const struct comedi_lrange range_rti800_ai_5_bipolar = {
+ 4, {
+ BIP_RANGE(5),
+ BIP_RANGE(0.5),
+ BIP_RANGE(0.05),
+ BIP_RANGE(0.01)
+ }
+};
+
+static const struct comedi_lrange range_rti800_ai_unipolar = {
+ 4, {
+ UNI_RANGE(10),
+ UNI_RANGE(1),
+ UNI_RANGE(0.1),
+ UNI_RANGE(0.02)
+ }
+};
+
+static const struct comedi_lrange *const rti800_ai_ranges[] = {
+ &range_rti800_ai_10_bipolar,
+ &range_rti800_ai_5_bipolar,
+ &range_rti800_ai_unipolar,
+};
+
+static const struct comedi_lrange *const rti800_ao_ranges[] = {
+ &range_bipolar10,
+ &range_unipolar10,
+};
+
+struct rti800_board {
+ const char *name;
+ int has_ao;
+};
+
+static const struct rti800_board rti800_boardtypes[] = {
+ {
+ .name = "rti800",
+ }, {
+ .name = "rti815",
+ .has_ao = 1,
+ },
+};
+
+struct rti800_private {
+ bool adc_2comp;
+ bool dac_2comp[2];
+ const struct comedi_lrange *ao_range_type_list[2];
+ unsigned char muxgain_bits;
+};
+
+static int rti800_ai_eoc(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned long context)
+{
+ unsigned char status;
+
+ status = inb(dev->iobase + RTI800_CSR);
+ if (status & RTI800_CSR_OVERRUN) {
+ outb(0, dev->iobase + RTI800_CLRFLAGS);
+ return -EOVERFLOW;
+ }
+ if (status & RTI800_CSR_DONE)
+ return 0;
+ return -EBUSY;
+}
+
+static int rti800_ai_insn_read(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ struct rti800_private *devpriv = dev->private;
+ unsigned int chan = CR_CHAN(insn->chanspec);
+ unsigned int gain = CR_RANGE(insn->chanspec);
+ unsigned char muxgain_bits;
+ int ret;
+ int i;
+
+ inb(dev->iobase + RTI800_ADCHI);
+ outb(0, dev->iobase + RTI800_CLRFLAGS);
+
+ muxgain_bits = chan | (gain << 5);
+ if (muxgain_bits != devpriv->muxgain_bits) {
+ devpriv->muxgain_bits = muxgain_bits;
+ outb(devpriv->muxgain_bits, dev->iobase + RTI800_MUXGAIN);
+ /*
+ * Without a delay here, the RTI_CSR_OVERRUN bit
+ * gets set, and you will have an error.
+ */
+ if (insn->n > 0) {
+ int delay = (gain == 0) ? 10 :
+ (gain == 1) ? 20 :
+ (gain == 2) ? 40 : 80;
+
+ udelay(delay);
+ }
+ }
+
+ for (i = 0; i < insn->n; i++) {
+ outb(0, dev->iobase + RTI800_CONVERT);
+
+ ret = comedi_timeout(dev, s, insn, rti800_ai_eoc, 0);
+ if (ret)
+ return ret;
+
+ data[i] = inb(dev->iobase + RTI800_ADCLO);
+ data[i] |= (inb(dev->iobase + RTI800_ADCHI) & 0xf) << 8;
+
+ if (devpriv->adc_2comp)
+ data[i] ^= 0x800;
+ }
+
+ return insn->n;
+}
+
+static int rti800_ao_insn_write(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ struct rti800_private *devpriv = dev->private;
+ unsigned int chan = CR_CHAN(insn->chanspec);
+ int reg_lo = chan ? RTI800_DAC1LO : RTI800_DAC0LO;
+ int reg_hi = chan ? RTI800_DAC1HI : RTI800_DAC0HI;
+ int i;
+
+ for (i = 0; i < insn->n; i++) {
+ unsigned int val = data[i];
+
+ s->readback[chan] = val;
+
+ if (devpriv->dac_2comp[chan])
+ val ^= 0x800;
+
+ outb(val & 0xff, dev->iobase + reg_lo);
+ outb((val >> 8) & 0xff, dev->iobase + reg_hi);
+ }
+
+ return insn->n;
+}
+
+static int rti800_di_insn_bits(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ data[1] = inb(dev->iobase + RTI800_DI);
+ return insn->n;
+}
+
+static int rti800_do_insn_bits(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ if (comedi_dio_update_state(s, data)) {
+ /* Outputs are inverted... */
+ outb(s->state ^ 0xff, dev->iobase + RTI800_DO);
+ }
+
+ data[1] = s->state;
+
+ return insn->n;
+}
+
+static int rti800_attach(struct comedi_device *dev, struct comedi_devconfig *it)
+{
+ const struct rti800_board *board = dev->board_ptr;
+ struct rti800_private *devpriv;
+ struct comedi_subdevice *s;
+ int ret;
+
+ ret = comedi_request_region(dev, it->options[0], 0x10);
+ if (ret)
+ return ret;
+
+ outb(0, dev->iobase + RTI800_CSR);
+ inb(dev->iobase + RTI800_ADCHI);
+ outb(0, dev->iobase + RTI800_CLRFLAGS);
+
+ devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
+ if (!devpriv)
+ return -ENOMEM;
+
+ devpriv->adc_2comp = (it->options[4] == 0);
+ devpriv->dac_2comp[0] = (it->options[6] == 0);
+ devpriv->dac_2comp[1] = (it->options[8] == 0);
+ /* invalid, forces the MUXGAIN register to be set when first used */
+ devpriv->muxgain_bits = 0xff;
+
+ ret = comedi_alloc_subdevices(dev, 4);
+ if (ret)
+ return ret;
+
+ s = &dev->subdevices[0];
+ /* ai subdevice */
+ s->type = COMEDI_SUBD_AI;
+ s->subdev_flags = SDF_READABLE | SDF_GROUND;
+ s->n_chan = (it->options[2] ? 16 : 8);
+ s->insn_read = rti800_ai_insn_read;
+ s->maxdata = 0x0fff;
+ s->range_table = (it->options[3] < ARRAY_SIZE(rti800_ai_ranges))
+ ? rti800_ai_ranges[it->options[3]]
+ : &range_unknown;
+
+ s = &dev->subdevices[1];
+ if (board->has_ao) {
+ /* ao subdevice (only on rti815) */
+ s->type = COMEDI_SUBD_AO;
+ s->subdev_flags = SDF_WRITABLE;
+ s->n_chan = 2;
+ s->maxdata = 0x0fff;
+ s->range_table_list = devpriv->ao_range_type_list;
+ devpriv->ao_range_type_list[0] =
+ (it->options[5] < ARRAY_SIZE(rti800_ao_ranges))
+ ? rti800_ao_ranges[it->options[5]]
+ : &range_unknown;
+ devpriv->ao_range_type_list[1] =
+ (it->options[7] < ARRAY_SIZE(rti800_ao_ranges))
+ ? rti800_ao_ranges[it->options[7]]
+ : &range_unknown;
+ s->insn_write = rti800_ao_insn_write;
+
+ ret = comedi_alloc_subdev_readback(s);
+ if (ret)
+ return ret;
+ } else {
+ s->type = COMEDI_SUBD_UNUSED;
+ }
+
+ s = &dev->subdevices[2];
+ /* di */
+ s->type = COMEDI_SUBD_DI;
+ s->subdev_flags = SDF_READABLE;
+ s->n_chan = 8;
+ s->insn_bits = rti800_di_insn_bits;
+ s->maxdata = 1;
+ s->range_table = &range_digital;
+
+ s = &dev->subdevices[3];
+ /* do */
+ s->type = COMEDI_SUBD_DO;
+ s->subdev_flags = SDF_WRITABLE;
+ s->n_chan = 8;
+ s->insn_bits = rti800_do_insn_bits;
+ s->maxdata = 1;
+ s->range_table = &range_digital;
+
+ /*
+ * There is also an Am9513 timer on these boards. This subdevice
+ * is not currently supported.
+ */
+
+ return 0;
+}
+
+static struct comedi_driver rti800_driver = {
+ .driver_name = "rti800",
+ .module = THIS_MODULE,
+ .attach = rti800_attach,
+ .detach = comedi_legacy_detach,
+ .num_names = ARRAY_SIZE(rti800_boardtypes),
+ .board_name = &rti800_boardtypes[0].name,
+ .offset = sizeof(struct rti800_board),
+};
+module_comedi_driver(rti800_driver);
+
+MODULE_DESCRIPTION("Comedi: RTI-800 Multifunction Analog/Digital board");
+MODULE_AUTHOR("Comedi http://www.comedi.org");
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/comedi/drivers/rti802.c b/drivers/staging/comedi/drivers/rti802.c
new file mode 100644
index 000000000..6db58fcfd
--- /dev/null
+++ b/drivers/staging/comedi/drivers/rti802.c
@@ -0,0 +1,129 @@
+/*
+ * rti802.c
+ * Comedi driver for Analog Devices RTI-802 board
+ *
+ * COMEDI - Linux Control and Measurement Device Interface
+ * Copyright (C) 1999 Anders Blomdell <anders.blomdell@control.lth.se>
+ *
+ * This program is free software; you can redistribute it and/or 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.
+ */
+
+/*
+ * Driver: rti802
+ * Description: Analog Devices RTI-802
+ * Author: Anders Blomdell <anders.blomdell@control.lth.se>
+ * Devices: [Analog Devices] RTI-802 (rti802)
+ * Status: works
+ *
+ * Configuration Options:
+ * [0] - i/o base
+ * [1] - unused
+ * [2,4,6,8,10,12,14,16] - dac#[0-7] 0=two's comp, 1=straight
+ * [3,5,7,9,11,13,15,17] - dac#[0-7] 0=bipolar, 1=unipolar
+ */
+
+#include <linux/module.h>
+#include "../comedidev.h"
+
+/*
+ * Register I/O map
+ */
+#define RTI802_SELECT 0x00
+#define RTI802_DATALOW 0x01
+#define RTI802_DATAHIGH 0x02
+
+struct rti802_private {
+ enum {
+ dac_2comp, dac_straight
+ } dac_coding[8];
+ const struct comedi_lrange *range_type_list[8];
+};
+
+static int rti802_ao_insn_write(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ struct rti802_private *devpriv = dev->private;
+ unsigned int chan = CR_CHAN(insn->chanspec);
+ int i;
+
+ outb(chan, dev->iobase + RTI802_SELECT);
+
+ for (i = 0; i < insn->n; i++) {
+ unsigned int val = data[i];
+
+ s->readback[chan] = val;
+
+ /* munge offset binary to two's complement if needed */
+ if (devpriv->dac_coding[chan] == dac_2comp)
+ val = comedi_offset_munge(s, val);
+
+ outb(val & 0xff, dev->iobase + RTI802_DATALOW);
+ outb((val >> 8) & 0xff, dev->iobase + RTI802_DATAHIGH);
+ }
+
+ return insn->n;
+}
+
+static int rti802_attach(struct comedi_device *dev, struct comedi_devconfig *it)
+{
+ struct rti802_private *devpriv;
+ struct comedi_subdevice *s;
+ int i;
+ int ret;
+
+ ret = comedi_request_region(dev, it->options[0], 0x04);
+ if (ret)
+ return ret;
+
+ devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
+ if (!devpriv)
+ return -ENOMEM;
+
+ ret = comedi_alloc_subdevices(dev, 1);
+ if (ret)
+ return ret;
+
+ /* Analog Output subdevice */
+ s = &dev->subdevices[0];
+ s->type = COMEDI_SUBD_AO;
+ s->subdev_flags = SDF_WRITABLE;
+ s->maxdata = 0xfff;
+ s->n_chan = 8;
+ s->insn_write = rti802_ao_insn_write;
+
+ ret = comedi_alloc_subdev_readback(s);
+ if (ret)
+ return ret;
+
+ s->range_table_list = devpriv->range_type_list;
+ for (i = 0; i < 8; i++) {
+ devpriv->dac_coding[i] = (it->options[3 + 2 * i])
+ ? (dac_straight) : (dac_2comp);
+ devpriv->range_type_list[i] = (it->options[2 + 2 * i])
+ ? &range_unipolar10 : &range_bipolar10;
+ }
+
+ return 0;
+}
+
+static struct comedi_driver rti802_driver = {
+ .driver_name = "rti802",
+ .module = THIS_MODULE,
+ .attach = rti802_attach,
+ .detach = comedi_legacy_detach,
+};
+module_comedi_driver(rti802_driver);
+
+MODULE_AUTHOR("Comedi http://www.comedi.org");
+MODULE_DESCRIPTION("Comedi driver for Analog Devices RTI-802 board");
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/comedi/drivers/s526.c b/drivers/staging/comedi/drivers/s526.c
new file mode 100644
index 000000000..6f3e8a08e
--- /dev/null
+++ b/drivers/staging/comedi/drivers/s526.c
@@ -0,0 +1,614 @@
+/*
+ comedi/drivers/s526.c
+ Sensoray s526 Comedi driver
+
+ COMEDI - Linux Control and Measurement Device Interface
+ Copyright (C) 2000 David A. Schleef <ds@schleef.org>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+*/
+/*
+Driver: s526
+Description: Sensoray 526 driver
+Devices: [Sensoray] 526 (s526)
+Author: Richie
+ Everett Wang <everett.wang@everteq.com>
+Updated: Thu, 14 Sep. 2006
+Status: experimental
+
+Encoder works
+Analog input works
+Analog output works
+PWM output works
+Commands are not supported yet.
+
+Configuration Options:
+
+comedi_config /dev/comedi0 s526 0x2C0,0x3
+
+*/
+
+#include <linux/module.h>
+#include "../comedidev.h"
+#include <asm/byteorder.h>
+
+#define S526_START_AI_CONV 0
+#define S526_AI_READ 0
+
+/* Ports */
+#define S526_NUM_PORTS 27
+
+/* registers */
+#define REG_TCR 0x00
+#define REG_WDC 0x02
+#define REG_DAC 0x04
+#define REG_ADC 0x06
+#define REG_ADD 0x08
+#define REG_DIO 0x0A
+#define REG_IER 0x0C
+#define REG_ISR 0x0E
+#define REG_MSC 0x10
+#define REG_C0L 0x12
+#define REG_C0H 0x14
+#define REG_C0M 0x16
+#define REG_C0C 0x18
+#define REG_C1L 0x1A
+#define REG_C1H 0x1C
+#define REG_C1M 0x1E
+#define REG_C1C 0x20
+#define REG_C2L 0x22
+#define REG_C2H 0x24
+#define REG_C2M 0x26
+#define REG_C2C 0x28
+#define REG_C3L 0x2A
+#define REG_C3H 0x2C
+#define REG_C3M 0x2E
+#define REG_C3C 0x30
+#define REG_EED 0x32
+#define REG_EEC 0x34
+
+struct counter_mode_register_t {
+#if defined(__LITTLE_ENDIAN_BITFIELD)
+ unsigned short coutSource:1;
+ unsigned short coutPolarity:1;
+ unsigned short autoLoadResetRcap:3;
+ unsigned short hwCtEnableSource:2;
+ unsigned short ctEnableCtrl:2;
+ unsigned short clockSource:2;
+ unsigned short countDir:1;
+ unsigned short countDirCtrl:1;
+ unsigned short outputRegLatchCtrl:1;
+ unsigned short preloadRegSel:1;
+ unsigned short reserved:1;
+ #elif defined(__BIG_ENDIAN_BITFIELD)
+ unsigned short reserved:1;
+ unsigned short preloadRegSel:1;
+ unsigned short outputRegLatchCtrl:1;
+ unsigned short countDirCtrl:1;
+ unsigned short countDir:1;
+ unsigned short clockSource:2;
+ unsigned short ctEnableCtrl:2;
+ unsigned short hwCtEnableSource:2;
+ unsigned short autoLoadResetRcap:3;
+ unsigned short coutPolarity:1;
+ unsigned short coutSource:1;
+#else
+#error Unknown bit field order
+#endif
+};
+
+union cmReg {
+ struct counter_mode_register_t reg;
+ unsigned short value;
+};
+
+struct s526_private {
+ unsigned int gpct_config[4];
+ unsigned short ai_config;
+};
+
+static int s526_gpct_rinsn(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ unsigned int chan = CR_CHAN(insn->chanspec);
+ unsigned long chan_iobase = dev->iobase + chan * 8;
+ unsigned int lo;
+ unsigned int hi;
+ int i;
+
+ for (i = 0; i < insn->n; i++) {
+ /* Read the low word first */
+ lo = inw(chan_iobase + REG_C0L) & 0xffff;
+ hi = inw(chan_iobase + REG_C0H) & 0xff;
+
+ data[i] = (hi << 16) | lo;
+ }
+
+ return insn->n;
+}
+
+static int s526_gpct_insn_config(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ struct s526_private *devpriv = dev->private;
+ unsigned int chan = CR_CHAN(insn->chanspec);
+ unsigned long chan_iobase = dev->iobase + chan * 8;
+ unsigned int val;
+ union cmReg cmReg;
+
+ /* Check what type of Counter the user requested, data[0] contains */
+ /* the Application type */
+ switch (data[0]) {
+ case INSN_CONFIG_GPCT_QUADRATURE_ENCODER:
+ /*
+ data[0]: Application Type
+ data[1]: Counter Mode Register Value
+ data[2]: Pre-load Register Value
+ data[3]: Conter Control Register
+ */
+ devpriv->gpct_config[chan] = data[0];
+
+#if 0
+ /* Example of Counter Application */
+ /* One-shot (software trigger) */
+ cmReg.reg.coutSource = 0; /* out RCAP */
+ cmReg.reg.coutPolarity = 1; /* Polarity inverted */
+ cmReg.reg.autoLoadResetRcap = 0;/* Auto load disabled */
+ cmReg.reg.hwCtEnableSource = 3; /* NOT RCAP */
+ cmReg.reg.ctEnableCtrl = 2; /* Hardware */
+ cmReg.reg.clockSource = 2; /* Internal */
+ cmReg.reg.countDir = 1; /* Down */
+ cmReg.reg.countDirCtrl = 1; /* Software */
+ cmReg.reg.outputRegLatchCtrl = 0; /* latch on read */
+ cmReg.reg.preloadRegSel = 0; /* PR0 */
+ cmReg.reg.reserved = 0;
+
+ outw(cmReg.value, chan_iobase + REG_C0M);
+
+ outw(0x0001, chan_iobase + REG_C0H);
+ outw(0x3C68, chan_iobase + REG_C0L);
+
+ /* Reset the counter */
+ outw(0x8000, chan_iobase + REG_C0C);
+ /* Load the counter from PR0 */
+ outw(0x4000, chan_iobase + REG_C0C);
+
+ /* Reset RCAP (fires one-shot) */
+ outw(0x0008, chan_iobase + REG_C0C);
+
+#endif
+
+#if 1
+ /* Set Counter Mode Register */
+ cmReg.value = data[1] & 0xffff;
+ outw(cmReg.value, chan_iobase + REG_C0M);
+
+ /* Reset the counter if it is software preload */
+ if (cmReg.reg.autoLoadResetRcap == 0) {
+ /* Reset the counter */
+ outw(0x8000, chan_iobase + REG_C0C);
+ /* Load the counter from PR0
+ * outw(0x4000, chan_iobase + REG_C0C);
+ */
+ }
+#else
+ /* 0 quadrature, 1 software control */
+ cmReg.reg.countDirCtrl = 0;
+
+ /* data[1] contains GPCT_X1, GPCT_X2 or GPCT_X4 */
+ if (data[1] == GPCT_X2)
+ cmReg.reg.clockSource = 1;
+ else if (data[1] == GPCT_X4)
+ cmReg.reg.clockSource = 2;
+ else
+ cmReg.reg.clockSource = 0;
+
+ /* When to take into account the indexpulse: */
+ /*if (data[2] == GPCT_IndexPhaseLowLow) {
+ } else if (data[2] == GPCT_IndexPhaseLowHigh) {
+ } else if (data[2] == GPCT_IndexPhaseHighLow) {
+ } else if (data[2] == GPCT_IndexPhaseHighHigh) {
+ }*/
+ /* Take into account the index pulse? */
+ if (data[3] == GPCT_RESET_COUNTER_ON_INDEX)
+ /* Auto load with INDEX^ */
+ cmReg.reg.autoLoadResetRcap = 4;
+
+ /* Set Counter Mode Register */
+ cmReg.value = data[1] & 0xffff;
+ outw(cmReg.value, chan_iobase + REG_C0M);
+
+ /* Load the pre-load register high word */
+ val = (data[2] >> 16) & 0xffff;
+ outw(val, chan_iobase + REG_C0H);
+
+ /* Load the pre-load register low word */
+ val = data[2] & 0xffff;
+ outw(val, chan_iobase + REG_C0L);
+
+ /* Write the Counter Control Register */
+ if (data[3]) {
+ val = data[3] & 0xffff;
+ outw(val, chan_iobase + REG_C0C);
+ }
+ /* Reset the counter if it is software preload */
+ if (cmReg.reg.autoLoadResetRcap == 0) {
+ /* Reset the counter */
+ outw(0x8000, chan_iobase + REG_C0C);
+ /* Load the counter from PR0 */
+ outw(0x4000, chan_iobase + REG_C0C);
+ }
+#endif
+ break;
+
+ case INSN_CONFIG_GPCT_SINGLE_PULSE_GENERATOR:
+ /*
+ data[0]: Application Type
+ data[1]: Counter Mode Register Value
+ data[2]: Pre-load Register 0 Value
+ data[3]: Pre-load Register 1 Value
+ data[4]: Conter Control Register
+ */
+ devpriv->gpct_config[chan] = data[0];
+
+ /* Set Counter Mode Register */
+ cmReg.value = data[1] & 0xffff;
+ cmReg.reg.preloadRegSel = 0; /* PR0 */
+ outw(cmReg.value, chan_iobase + REG_C0M);
+
+ /* Load the pre-load register 0 high word */
+ val = (data[2] >> 16) & 0xffff;
+ outw(val, chan_iobase + REG_C0H);
+
+ /* Load the pre-load register 0 low word */
+ val = data[2] & 0xffff;
+ outw(val, chan_iobase + REG_C0L);
+
+ /* Set Counter Mode Register */
+ cmReg.value = data[1] & 0xffff;
+ cmReg.reg.preloadRegSel = 1; /* PR1 */
+ outw(cmReg.value, chan_iobase + REG_C0M);
+
+ /* Load the pre-load register 1 high word */
+ val = (data[3] >> 16) & 0xffff;
+ outw(val, chan_iobase + REG_C0H);
+
+ /* Load the pre-load register 1 low word */
+ val = data[3] & 0xffff;
+ outw(val, chan_iobase + REG_C0L);
+
+ /* Write the Counter Control Register */
+ if (data[4]) {
+ val = data[4] & 0xffff;
+ outw(val, chan_iobase + REG_C0C);
+ }
+ break;
+
+ case INSN_CONFIG_GPCT_PULSE_TRAIN_GENERATOR:
+ /*
+ data[0]: Application Type
+ data[1]: Counter Mode Register Value
+ data[2]: Pre-load Register 0 Value
+ data[3]: Pre-load Register 1 Value
+ data[4]: Conter Control Register
+ */
+ devpriv->gpct_config[chan] = data[0];
+
+ /* Set Counter Mode Register */
+ cmReg.value = data[1] & 0xffff;
+ cmReg.reg.preloadRegSel = 0; /* PR0 */
+ outw(cmReg.value, chan_iobase + REG_C0M);
+
+ /* Load the pre-load register 0 high word */
+ val = (data[2] >> 16) & 0xffff;
+ outw(val, chan_iobase + REG_C0H);
+
+ /* Load the pre-load register 0 low word */
+ val = data[2] & 0xffff;
+ outw(val, chan_iobase + REG_C0L);
+
+ /* Set Counter Mode Register */
+ cmReg.value = data[1] & 0xffff;
+ cmReg.reg.preloadRegSel = 1; /* PR1 */
+ outw(cmReg.value, chan_iobase + REG_C0M);
+
+ /* Load the pre-load register 1 high word */
+ val = (data[3] >> 16) & 0xffff;
+ outw(val, chan_iobase + REG_C0H);
+
+ /* Load the pre-load register 1 low word */
+ val = data[3] & 0xffff;
+ outw(val, chan_iobase + REG_C0L);
+
+ /* Write the Counter Control Register */
+ if (data[4]) {
+ val = data[4] & 0xffff;
+ outw(val, chan_iobase + REG_C0C);
+ }
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ return insn->n;
+}
+
+static int s526_gpct_winsn(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ struct s526_private *devpriv = dev->private;
+ unsigned int chan = CR_CHAN(insn->chanspec);
+ unsigned long chan_iobase = dev->iobase + chan * 8;
+
+ inw(chan_iobase + REG_C0M); /* Is this read required? */
+
+ /* Check what Application of Counter this channel is configured for */
+ switch (devpriv->gpct_config[chan]) {
+ case INSN_CONFIG_GPCT_PULSE_TRAIN_GENERATOR:
+ /* data[0] contains the PULSE_WIDTH
+ data[1] contains the PULSE_PERIOD
+ @pre PULSE_PERIOD > PULSE_WIDTH > 0
+ The above periods must be expressed as a multiple of the
+ pulse frequency on the selected source
+ */
+ if ((data[1] <= data[0]) || !data[0])
+ return -EINVAL;
+
+ /* Fall thru to write the PULSE_WIDTH */
+
+ case INSN_CONFIG_GPCT_QUADRATURE_ENCODER:
+ case INSN_CONFIG_GPCT_SINGLE_PULSE_GENERATOR:
+ outw((data[0] >> 16) & 0xffff, chan_iobase + REG_C0H);
+ outw(data[0] & 0xffff, chan_iobase + REG_C0L);
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ return insn->n;
+}
+
+#define ISR_ADC_DONE 0x4
+static int s526_ai_insn_config(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn, unsigned int *data)
+{
+ struct s526_private *devpriv = dev->private;
+ int result = -EINVAL;
+
+ if (insn->n < 1)
+ return result;
+
+ result = insn->n;
+
+ /* data[0] : channels was set in relevant bits.
+ data[1] : delay
+ */
+ /* COMMENT: abbotti 2008-07-24: I don't know why you'd want to
+ * enable channels here. The channel should be enabled in the
+ * INSN_READ handler. */
+
+ /* Enable ADC interrupt */
+ outw(ISR_ADC_DONE, dev->iobase + REG_IER);
+ devpriv->ai_config = (data[0] & 0x3ff) << 5;
+ if (data[1] > 0)
+ devpriv->ai_config |= 0x8000; /* set the delay */
+
+ devpriv->ai_config |= 0x0001; /* ADC start bit */
+
+ return result;
+}
+
+static int s526_ai_eoc(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned long context)
+{
+ unsigned int status;
+
+ status = inw(dev->iobase + REG_ISR);
+ if (status & ISR_ADC_DONE)
+ return 0;
+ return -EBUSY;
+}
+
+static int s526_ai_rinsn(struct comedi_device *dev, struct comedi_subdevice *s,
+ struct comedi_insn *insn, unsigned int *data)
+{
+ struct s526_private *devpriv = dev->private;
+ unsigned int chan = CR_CHAN(insn->chanspec);
+ int n;
+ unsigned short value;
+ unsigned int d;
+ int ret;
+
+ /* Set configured delay, enable channel for this channel only,
+ * select "ADC read" channel, set "ADC start" bit. */
+ value = (devpriv->ai_config & 0x8000) |
+ ((1 << 5) << chan) | (chan << 1) | 0x0001;
+
+ /* convert n samples */
+ for (n = 0; n < insn->n; n++) {
+ /* trigger conversion */
+ outw(value, dev->iobase + REG_ADC);
+
+ /* wait for conversion to end */
+ ret = comedi_timeout(dev, s, insn, s526_ai_eoc, 0);
+ if (ret)
+ return ret;
+
+ outw(ISR_ADC_DONE, dev->iobase + REG_ISR);
+
+ /* read data */
+ d = inw(dev->iobase + REG_ADD);
+
+ /* munge data */
+ data[n] = d ^ 0x8000;
+ }
+
+ /* return the number of samples read/written */
+ return n;
+}
+
+static int s526_ao_insn_write(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ unsigned int chan = CR_CHAN(insn->chanspec);
+ unsigned int val = s->readback[chan];
+ int i;
+
+ outw(chan << 1, dev->iobase + REG_DAC);
+
+ for (i = 0; i < insn->n; i++) {
+ val = data[i];
+ outw(val, dev->iobase + REG_ADD);
+ /* starts the D/A conversion */
+ outw((chan << 1) | 1, dev->iobase + REG_DAC);
+ }
+ s->readback[chan] = val;
+
+ return insn->n;
+}
+
+static int s526_dio_insn_bits(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ if (comedi_dio_update_state(s, data))
+ outw(s->state, dev->iobase + REG_DIO);
+
+ data[1] = inw(dev->iobase + REG_DIO) & 0xff;
+
+ return insn->n;
+}
+
+static int s526_dio_insn_config(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ unsigned int chan = CR_CHAN(insn->chanspec);
+ unsigned int mask;
+ int ret;
+
+ if (chan < 4)
+ mask = 0x0f;
+ else
+ mask = 0xf0;
+
+ ret = comedi_dio_insn_config(dev, s, insn, data, mask);
+ if (ret)
+ return ret;
+
+ /* bit 10/11 set the group 1/2's mode */
+ if (s->io_bits & 0x0f)
+ s->state |= (1 << 10);
+ else
+ s->state &= ~(1 << 10);
+ if (s->io_bits & 0xf0)
+ s->state |= (1 << 11);
+ else
+ s->state &= ~(1 << 11);
+
+ outw(s->state, dev->iobase + REG_DIO);
+
+ return insn->n;
+}
+
+static int s526_attach(struct comedi_device *dev, struct comedi_devconfig *it)
+{
+ struct s526_private *devpriv;
+ struct comedi_subdevice *s;
+ int ret;
+
+ ret = comedi_request_region(dev, it->options[0], 0x40);
+ if (ret)
+ return ret;
+
+ devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
+ if (!devpriv)
+ return -ENOMEM;
+
+ ret = comedi_alloc_subdevices(dev, 4);
+ if (ret)
+ return ret;
+
+ s = &dev->subdevices[0];
+ /* GENERAL-PURPOSE COUNTER/TIME (GPCT) */
+ s->type = COMEDI_SUBD_COUNTER;
+ s->subdev_flags = SDF_READABLE | SDF_WRITABLE | SDF_LSAMPL;
+ s->n_chan = 4;
+ s->maxdata = 0x00ffffff; /* 24 bit counter */
+ s->insn_read = s526_gpct_rinsn;
+ s->insn_config = s526_gpct_insn_config;
+ s->insn_write = s526_gpct_winsn;
+
+ s = &dev->subdevices[1];
+ /* analog input subdevice */
+ s->type = COMEDI_SUBD_AI;
+ s->subdev_flags = SDF_READABLE | SDF_DIFF;
+ /* channels 0 to 7 are the regular differential inputs */
+ /* channel 8 is "reference 0" (+10V), channel 9 is "reference 1" (0V) */
+ s->n_chan = 10;
+ s->maxdata = 0xffff;
+ s->range_table = &range_bipolar10;
+ s->len_chanlist = 16;
+ s->insn_read = s526_ai_rinsn;
+ s->insn_config = s526_ai_insn_config;
+
+ s = &dev->subdevices[2];
+ /* analog output subdevice */
+ s->type = COMEDI_SUBD_AO;
+ s->subdev_flags = SDF_WRITABLE;
+ s->n_chan = 4;
+ s->maxdata = 0xffff;
+ s->range_table = &range_bipolar10;
+ s->insn_write = s526_ao_insn_write;
+
+ ret = comedi_alloc_subdev_readback(s);
+ if (ret)
+ return ret;
+
+ s = &dev->subdevices[3];
+ /* digital i/o subdevice */
+ s->type = COMEDI_SUBD_DIO;
+ s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
+ s->n_chan = 8;
+ s->maxdata = 1;
+ s->range_table = &range_digital;
+ s->insn_bits = s526_dio_insn_bits;
+ s->insn_config = s526_dio_insn_config;
+
+ return 0;
+}
+
+static struct comedi_driver s526_driver = {
+ .driver_name = "s526",
+ .module = THIS_MODULE,
+ .attach = s526_attach,
+ .detach = comedi_legacy_detach,
+};
+module_comedi_driver(s526_driver);
+
+MODULE_AUTHOR("Comedi http://www.comedi.org");
+MODULE_DESCRIPTION("Comedi low-level driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/comedi/drivers/s626.c b/drivers/staging/comedi/drivers/s626.c
new file mode 100644
index 000000000..781918d8d
--- /dev/null
+++ b/drivers/staging/comedi/drivers/s626.c
@@ -0,0 +1,2925 @@
+/*
+ * comedi/drivers/s626.c
+ * Sensoray s626 Comedi driver
+ *
+ * COMEDI - Linux Control and Measurement Device Interface
+ * Copyright (C) 2000 David A. Schleef <ds@schleef.org>
+ *
+ * Based on Sensoray Model 626 Linux driver Version 0.2
+ * Copyright (C) 2002-2004 Sensoray Co., 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 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.
+ */
+
+/*
+ * Driver: s626
+ * Description: Sensoray 626 driver
+ * Devices: [Sensoray] 626 (s626)
+ * Authors: Gianluca Palli <gpalli@deis.unibo.it>,
+ * Updated: Fri, 15 Feb 2008 10:28:42 +0000
+ * Status: experimental
+
+ * Configuration options: not applicable, uses PCI auto config
+
+ * INSN_CONFIG instructions:
+ * analog input:
+ * none
+ *
+ * analog output:
+ * none
+ *
+ * digital channel:
+ * s626 has 3 dio subdevices (2,3 and 4) each with 16 i/o channels
+ * supported configuration options:
+ * INSN_CONFIG_DIO_QUERY
+ * COMEDI_INPUT
+ * COMEDI_OUTPUT
+ *
+ * encoder:
+ * Every channel must be configured before reading.
+ *
+ * Example code
+ *
+ * insn.insn=INSN_CONFIG; //configuration instruction
+ * insn.n=1; //number of operation (must be 1)
+ * insn.data=&initialvalue; //initial value loaded into encoder
+ * //during configuration
+ * insn.subdev=5; //encoder subdevice
+ * insn.chanspec=CR_PACK(encoder_channel,0,AREF_OTHER); //encoder_channel
+ * //to configure
+ *
+ * comedi_do_insn(cf,&insn); //executing configuration
+ */
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+
+#include "../comedi_pci.h"
+
+#include "s626.h"
+
+struct s626_buffer_dma {
+ dma_addr_t physical_base;
+ void *logical_base;
+};
+
+struct s626_private {
+ uint8_t ai_cmd_running; /* ai_cmd is running */
+ unsigned int ai_sample_timer; /* time between samples in
+ * units of the timer */
+ int ai_convert_count; /* conversion counter */
+ unsigned int ai_convert_timer; /* time between conversion in
+ * units of the timer */
+ uint16_t counter_int_enabs; /* counter interrupt enable mask
+ * for MISC2 register */
+ uint8_t adc_items; /* number of items in ADC poll list */
+ struct s626_buffer_dma rps_buf; /* DMA buffer used to hold ADC (RPS1)
+ * program */
+ struct s626_buffer_dma ana_buf; /* DMA buffer used to receive ADC data
+ * and hold DAC data */
+ uint32_t *dac_wbuf; /* pointer to logical adrs of DMA buffer
+ * used to hold DAC data */
+ uint16_t dacpol; /* image of DAC polarity register */
+ uint8_t trim_setpoint[12]; /* images of TrimDAC setpoints */
+ uint32_t i2c_adrs; /* I2C device address for onboard EEPROM
+ * (board rev dependent) */
+};
+
+/* Counter overflow/index event flag masks for RDMISC2. */
+#define S626_INDXMASK(C) (1 << (((C) > 2) ? ((C) * 2 - 1) : ((C) * 2 + 4)))
+#define S626_OVERMASK(C) (1 << (((C) > 2) ? ((C) * 2 + 5) : ((C) * 2 + 10)))
+
+/*
+ * Enable/disable a function or test status bit(s) that are accessed
+ * through Main Control Registers 1 or 2.
+ */
+static void s626_mc_enable(struct comedi_device *dev,
+ unsigned int cmd, unsigned int reg)
+{
+ unsigned int val = (cmd << 16) | cmd;
+
+ mmiowb();
+ writel(val, dev->mmio + reg);
+}
+
+static void s626_mc_disable(struct comedi_device *dev,
+ unsigned int cmd, unsigned int reg)
+{
+ writel(cmd << 16, dev->mmio + reg);
+ mmiowb();
+}
+
+static bool s626_mc_test(struct comedi_device *dev,
+ unsigned int cmd, unsigned int reg)
+{
+ unsigned int val;
+
+ val = readl(dev->mmio + reg);
+
+ return (val & cmd) ? true : false;
+}
+
+#define S626_BUGFIX_STREG(REGADRS) ((REGADRS) - 4)
+
+/* Write a time slot control record to TSL2. */
+#define S626_VECTPORT(VECTNUM) (S626_P_TSL2 + ((VECTNUM) << 2))
+
+static const struct comedi_lrange s626_range_table = {
+ 2, {
+ BIP_RANGE(5),
+ BIP_RANGE(10)
+ }
+};
+
+/*
+ * Execute a DEBI transfer. This must be called from within a critical section.
+ */
+static void s626_debi_transfer(struct comedi_device *dev)
+{
+ static const int timeout = 10000;
+ int i;
+
+ /* Initiate upload of shadow RAM to DEBI control register */
+ s626_mc_enable(dev, S626_MC2_UPLD_DEBI, S626_P_MC2);
+
+ /*
+ * Wait for completion of upload from shadow RAM to
+ * DEBI control register.
+ */
+ for (i = 0; i < timeout; i++) {
+ if (s626_mc_test(dev, S626_MC2_UPLD_DEBI, S626_P_MC2))
+ break;
+ udelay(1);
+ }
+ if (i == timeout)
+ dev_err(dev->class_dev,
+ "Timeout while uploading to DEBI control register\n");
+
+ /* Wait until DEBI transfer is done */
+ for (i = 0; i < timeout; i++) {
+ if (!(readl(dev->mmio + S626_P_PSR) & S626_PSR_DEBI_S))
+ break;
+ udelay(1);
+ }
+ if (i == timeout)
+ dev_err(dev->class_dev, "DEBI transfer timeout\n");
+}
+
+/*
+ * Read a value from a gate array register.
+ */
+static uint16_t s626_debi_read(struct comedi_device *dev, uint16_t addr)
+{
+ /* Set up DEBI control register value in shadow RAM */
+ writel(S626_DEBI_CMD_RDWORD | addr, dev->mmio + S626_P_DEBICMD);
+
+ /* Execute the DEBI transfer. */
+ s626_debi_transfer(dev);
+
+ return readl(dev->mmio + S626_P_DEBIAD);
+}
+
+/*
+ * Write a value to a gate array register.
+ */
+static void s626_debi_write(struct comedi_device *dev, uint16_t addr,
+ uint16_t wdata)
+{
+ /* Set up DEBI control register value in shadow RAM */
+ writel(S626_DEBI_CMD_WRWORD | addr, dev->mmio + S626_P_DEBICMD);
+ writel(wdata, dev->mmio + S626_P_DEBIAD);
+
+ /* Execute the DEBI transfer. */
+ s626_debi_transfer(dev);
+}
+
+/*
+ * Replace the specified bits in a gate array register. Imports: mask
+ * specifies bits that are to be preserved, wdata is new value to be
+ * or'd with the masked original.
+ */
+static void s626_debi_replace(struct comedi_device *dev, unsigned int addr,
+ unsigned int mask, unsigned int wdata)
+{
+ unsigned int val;
+
+ addr &= 0xffff;
+ writel(S626_DEBI_CMD_RDWORD | addr, dev->mmio + S626_P_DEBICMD);
+ s626_debi_transfer(dev);
+
+ writel(S626_DEBI_CMD_WRWORD | addr, dev->mmio + S626_P_DEBICMD);
+ val = readl(dev->mmio + S626_P_DEBIAD);
+ val &= mask;
+ val |= wdata;
+ writel(val & 0xffff, dev->mmio + S626_P_DEBIAD);
+ s626_debi_transfer(dev);
+}
+
+/* ************** EEPROM ACCESS FUNCTIONS ************** */
+
+static int s626_i2c_handshake_eoc(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned long context)
+{
+ bool status;
+
+ status = s626_mc_test(dev, S626_MC2_UPLD_IIC, S626_P_MC2);
+ if (status)
+ return 0;
+ return -EBUSY;
+}
+
+static int s626_i2c_handshake(struct comedi_device *dev, uint32_t val)
+{
+ unsigned int ctrl;
+ int ret;
+
+ /* Write I2C command to I2C Transfer Control shadow register */
+ writel(val, dev->mmio + S626_P_I2CCTRL);
+
+ /*
+ * Upload I2C shadow registers into working registers and
+ * wait for upload confirmation.
+ */
+ s626_mc_enable(dev, S626_MC2_UPLD_IIC, S626_P_MC2);
+ ret = comedi_timeout(dev, NULL, NULL, s626_i2c_handshake_eoc, 0);
+ if (ret)
+ return ret;
+
+ /* Wait until I2C bus transfer is finished or an error occurs */
+ do {
+ ctrl = readl(dev->mmio + S626_P_I2CCTRL);
+ } while ((ctrl & (S626_I2C_BUSY | S626_I2C_ERR)) == S626_I2C_BUSY);
+
+ /* Return non-zero if I2C error occurred */
+ return ctrl & S626_I2C_ERR;
+}
+
+/* Read uint8_t from EEPROM. */
+static uint8_t s626_i2c_read(struct comedi_device *dev, uint8_t addr)
+{
+ struct s626_private *devpriv = dev->private;
+
+ /*
+ * Send EEPROM target address:
+ * Byte2 = I2C command: write to I2C EEPROM device.
+ * Byte1 = EEPROM internal target address.
+ * Byte0 = Not sent.
+ */
+ if (s626_i2c_handshake(dev, S626_I2C_B2(S626_I2C_ATTRSTART,
+ devpriv->i2c_adrs) |
+ S626_I2C_B1(S626_I2C_ATTRSTOP, addr) |
+ S626_I2C_B0(S626_I2C_ATTRNOP, 0)))
+ /* Abort function and declare error if handshake failed. */
+ return 0;
+
+ /*
+ * Execute EEPROM read:
+ * Byte2 = I2C command: read from I2C EEPROM device.
+ * Byte1 receives uint8_t from EEPROM.
+ * Byte0 = Not sent.
+ */
+ if (s626_i2c_handshake(dev, S626_I2C_B2(S626_I2C_ATTRSTART,
+ (devpriv->i2c_adrs | 1)) |
+ S626_I2C_B1(S626_I2C_ATTRSTOP, 0) |
+ S626_I2C_B0(S626_I2C_ATTRNOP, 0)))
+ /* Abort function and declare error if handshake failed. */
+ return 0;
+
+ return (readl(dev->mmio + S626_P_I2CCTRL) >> 16) & 0xff;
+}
+
+/* *********** DAC FUNCTIONS *********** */
+
+/* TrimDac LogicalChan-to-PhysicalChan mapping table. */
+static const uint8_t s626_trimchan[] = { 10, 9, 8, 3, 2, 7, 6, 1, 0, 5, 4 };
+
+/* TrimDac LogicalChan-to-EepromAdrs mapping table. */
+static const uint8_t s626_trimadrs[] = {
+ 0x40, 0x41, 0x42, 0x50, 0x51, 0x52, 0x53, 0x60, 0x61, 0x62, 0x63
+};
+
+enum {
+ s626_send_dac_wait_not_mc1_a2out,
+ s626_send_dac_wait_ssr_af2_out,
+ s626_send_dac_wait_fb_buffer2_msb_00,
+ s626_send_dac_wait_fb_buffer2_msb_ff
+};
+
+static int s626_send_dac_eoc(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned long context)
+{
+ unsigned int status;
+
+ switch (context) {
+ case s626_send_dac_wait_not_mc1_a2out:
+ status = readl(dev->mmio + S626_P_MC1);
+ if (!(status & S626_MC1_A2OUT))
+ return 0;
+ break;
+ case s626_send_dac_wait_ssr_af2_out:
+ status = readl(dev->mmio + S626_P_SSR);
+ if (status & S626_SSR_AF2_OUT)
+ return 0;
+ break;
+ case s626_send_dac_wait_fb_buffer2_msb_00:
+ status = readl(dev->mmio + S626_P_FB_BUFFER2);
+ if (!(status & 0xff000000))
+ return 0;
+ break;
+ case s626_send_dac_wait_fb_buffer2_msb_ff:
+ status = readl(dev->mmio + S626_P_FB_BUFFER2);
+ if (status & 0xff000000)
+ return 0;
+ break;
+ default:
+ return -EINVAL;
+ }
+ return -EBUSY;
+}
+
+/*
+ * Private helper function: Transmit serial data to DAC via Audio
+ * channel 2. Assumes: (1) TSL2 slot records initialized, and (2)
+ * dacpol contains valid target image.
+ */
+static int s626_send_dac(struct comedi_device *dev, uint32_t val)
+{
+ struct s626_private *devpriv = dev->private;
+ int ret;
+
+ /* START THE SERIAL CLOCK RUNNING ------------- */
+
+ /*
+ * Assert DAC polarity control and enable gating of DAC serial clock
+ * and audio bit stream signals. At this point in time we must be
+ * assured of being in time slot 0. If we are not in slot 0, the
+ * serial clock and audio stream signals will be disabled; this is
+ * because the following s626_debi_write statement (which enables
+ * signals to be passed through the gate array) would execute before
+ * the trailing edge of WS1/WS3 (which turns off the signals), thus
+ * causing the signals to be inactive during the DAC write.
+ */
+ s626_debi_write(dev, S626_LP_DACPOL, devpriv->dacpol);
+
+ /* TRANSFER OUTPUT DWORD VALUE INTO A2'S OUTPUT FIFO ---------------- */
+
+ /* Copy DAC setpoint value to DAC's output DMA buffer. */
+ /* writel(val, dev->mmio + (uint32_t)devpriv->dac_wbuf); */
+ *devpriv->dac_wbuf = val;
+
+ /*
+ * Enable the output DMA transfer. This will cause the DMAC to copy
+ * the DAC's data value to A2's output FIFO. The DMA transfer will
+ * then immediately terminate because the protection address is
+ * reached upon transfer of the first DWORD value.
+ */
+ s626_mc_enable(dev, S626_MC1_A2OUT, S626_P_MC1);
+
+ /* While the DMA transfer is executing ... */
+
+ /*
+ * Reset Audio2 output FIFO's underflow flag (along with any
+ * other FIFO underflow/overflow flags). When set, this flag
+ * will indicate that we have emerged from slot 0.
+ */
+ writel(S626_ISR_AFOU, dev->mmio + S626_P_ISR);
+
+ /*
+ * Wait for the DMA transfer to finish so that there will be data
+ * available in the FIFO when time slot 1 tries to transfer a DWORD
+ * from the FIFO to the output buffer register. We test for DMA
+ * Done by polling the DMAC enable flag; this flag is automatically
+ * cleared when the transfer has finished.
+ */
+ ret = comedi_timeout(dev, NULL, NULL, s626_send_dac_eoc,
+ s626_send_dac_wait_not_mc1_a2out);
+ if (ret) {
+ dev_err(dev->class_dev, "DMA transfer timeout\n");
+ return ret;
+ }
+
+ /* START THE OUTPUT STREAM TO THE TARGET DAC -------------------- */
+
+ /*
+ * FIFO data is now available, so we enable execution of time slots
+ * 1 and higher by clearing the EOS flag in slot 0. Note that SD3
+ * will be shifted in and stored in FB_BUFFER2 for end-of-slot-list
+ * detection.
+ */
+ writel(S626_XSD2 | S626_RSD3 | S626_SIB_A2,
+ dev->mmio + S626_VECTPORT(0));
+
+ /*
+ * Wait for slot 1 to execute to ensure that the Packet will be
+ * transmitted. This is detected by polling the Audio2 output FIFO
+ * underflow flag, which will be set when slot 1 execution has
+ * finished transferring the DAC's data DWORD from the output FIFO
+ * to the output buffer register.
+ */
+ ret = comedi_timeout(dev, NULL, NULL, s626_send_dac_eoc,
+ s626_send_dac_wait_ssr_af2_out);
+ if (ret) {
+ dev_err(dev->class_dev,
+ "TSL timeout waiting for slot 1 to execute\n");
+ return ret;
+ }
+
+ /*
+ * Set up to trap execution at slot 0 when the TSL sequencer cycles
+ * back to slot 0 after executing the EOS in slot 5. Also,
+ * simultaneously shift out and in the 0x00 that is ALWAYS the value
+ * stored in the last byte to be shifted out of the FIFO's DWORD
+ * buffer register.
+ */
+ writel(S626_XSD2 | S626_XFIFO_2 | S626_RSD2 | S626_SIB_A2 | S626_EOS,
+ dev->mmio + S626_VECTPORT(0));
+
+ /* WAIT FOR THE TRANSACTION TO FINISH ----------------------- */
+
+ /*
+ * Wait for the TSL to finish executing all time slots before
+ * exiting this function. We must do this so that the next DAC
+ * write doesn't start, thereby enabling clock/chip select signals:
+ *
+ * 1. Before the TSL sequence cycles back to slot 0, which disables
+ * the clock/cs signal gating and traps slot // list execution.
+ * we have not yet finished slot 5 then the clock/cs signals are
+ * still gated and we have not finished transmitting the stream.
+ *
+ * 2. While slots 2-5 are executing due to a late slot 0 trap. In
+ * this case, the slot sequence is currently repeating, but with
+ * clock/cs signals disabled. We must wait for slot 0 to trap
+ * execution before setting up the next DAC setpoint DMA transfer
+ * and enabling the clock/cs signals. To detect the end of slot 5,
+ * we test for the FB_BUFFER2 MSB contents to be equal to 0xFF. If
+ * the TSL has not yet finished executing slot 5 ...
+ */
+ if (readl(dev->mmio + S626_P_FB_BUFFER2) & 0xff000000) {
+ /*
+ * The trap was set on time and we are still executing somewhere
+ * in slots 2-5, so we now wait for slot 0 to execute and trap
+ * TSL execution. This is detected when FB_BUFFER2 MSB changes
+ * from 0xFF to 0x00, which slot 0 causes to happen by shifting
+ * out/in on SD2 the 0x00 that is always referenced by slot 5.
+ */
+ ret = comedi_timeout(dev, NULL, NULL, s626_send_dac_eoc,
+ s626_send_dac_wait_fb_buffer2_msb_00);
+ if (ret) {
+ dev_err(dev->class_dev,
+ "TSL timeout waiting for slot 0 to execute\n");
+ return ret;
+ }
+ }
+ /*
+ * Either (1) we were too late setting the slot 0 trap; the TSL
+ * sequencer restarted slot 0 before we could set the EOS trap flag,
+ * or (2) we were not late and execution is now trapped at slot 0.
+ * In either case, we must now change slot 0 so that it will store
+ * value 0xFF (instead of 0x00) to FB_BUFFER2 next time it executes.
+ * In order to do this, we reprogram slot 0 so that it will shift in
+ * SD3, which is driven only by a pull-up resistor.
+ */
+ writel(S626_RSD3 | S626_SIB_A2 | S626_EOS,
+ dev->mmio + S626_VECTPORT(0));
+
+ /*
+ * Wait for slot 0 to execute, at which time the TSL is setup for
+ * the next DAC write. This is detected when FB_BUFFER2 MSB changes
+ * from 0x00 to 0xFF.
+ */
+ ret = comedi_timeout(dev, NULL, NULL, s626_send_dac_eoc,
+ s626_send_dac_wait_fb_buffer2_msb_ff);
+ if (ret) {
+ dev_err(dev->class_dev,
+ "TSL timeout waiting for slot 0 to execute\n");
+ return ret;
+ }
+ return 0;
+}
+
+/*
+ * Private helper function: Write setpoint to an application DAC channel.
+ */
+static int s626_set_dac(struct comedi_device *dev,
+ uint16_t chan, int16_t dacdata)
+{
+ struct s626_private *devpriv = dev->private;
+ uint16_t signmask;
+ uint32_t ws_image;
+ uint32_t val;
+
+ /*
+ * Adjust DAC data polarity and set up Polarity Control Register image.
+ */
+ signmask = 1 << chan;
+ if (dacdata < 0) {
+ dacdata = -dacdata;
+ devpriv->dacpol |= signmask;
+ } else {
+ devpriv->dacpol &= ~signmask;
+ }
+
+ /* Limit DAC setpoint value to valid range. */
+ if ((uint16_t)dacdata > 0x1FFF)
+ dacdata = 0x1FFF;
+
+ /*
+ * Set up TSL2 records (aka "vectors") for DAC update. Vectors V2
+ * and V3 transmit the setpoint to the target DAC. V4 and V5 send
+ * data to a non-existent TrimDac channel just to keep the clock
+ * running after sending data to the target DAC. This is necessary
+ * to eliminate the clock glitch that would otherwise occur at the
+ * end of the target DAC's serial data stream. When the sequence
+ * restarts at V0 (after executing V5), the gate array automatically
+ * disables gating for the DAC clock and all DAC chip selects.
+ */
+
+ /* Choose DAC chip select to be asserted */
+ ws_image = (chan & 2) ? S626_WS1 : S626_WS2;
+ /* Slot 2: Transmit high data byte to target DAC */
+ writel(S626_XSD2 | S626_XFIFO_1 | ws_image,
+ dev->mmio + S626_VECTPORT(2));
+ /* Slot 3: Transmit low data byte to target DAC */
+ writel(S626_XSD2 | S626_XFIFO_0 | ws_image,
+ dev->mmio + S626_VECTPORT(3));
+ /* Slot 4: Transmit to non-existent TrimDac channel to keep clock */
+ writel(S626_XSD2 | S626_XFIFO_3 | S626_WS3,
+ dev->mmio + S626_VECTPORT(4));
+ /* Slot 5: running after writing target DAC's low data byte */
+ writel(S626_XSD2 | S626_XFIFO_2 | S626_WS3 | S626_EOS,
+ dev->mmio + S626_VECTPORT(5));
+
+ /*
+ * Construct and transmit target DAC's serial packet:
+ * (A10D DDDD), (DDDD DDDD), (0x0F), (0x00) where A is chan<0>,
+ * and D<12:0> is the DAC setpoint. Append a WORD value (that writes
+ * to a non-existent TrimDac channel) that serves to keep the clock
+ * running after the packet has been sent to the target DAC.
+ */
+ val = 0x0F000000; /* Continue clock after target DAC data
+ * (write to non-existent trimdac). */
+ val |= 0x00004000; /* Address the two main dual-DAC devices
+ * (TSL's chip select enables target device). */
+ val |= ((uint32_t)(chan & 1) << 15); /* Address the DAC channel
+ * within the device. */
+ val |= (uint32_t)dacdata; /* Include DAC setpoint data. */
+ return s626_send_dac(dev, val);
+}
+
+static int s626_write_trim_dac(struct comedi_device *dev,
+ uint8_t logical_chan, uint8_t dac_data)
+{
+ struct s626_private *devpriv = dev->private;
+ uint32_t chan;
+
+ /*
+ * Save the new setpoint in case the application needs to read it back
+ * later.
+ */
+ devpriv->trim_setpoint[logical_chan] = (uint8_t)dac_data;
+
+ /* Map logical channel number to physical channel number. */
+ chan = s626_trimchan[logical_chan];
+
+ /*
+ * Set up TSL2 records for TrimDac write operation. All slots shift
+ * 0xFF in from pulled-up SD3 so that the end of the slot sequence
+ * can be detected.
+ */
+
+ /* Slot 2: Send high uint8_t to target TrimDac */
+ writel(S626_XSD2 | S626_XFIFO_1 | S626_WS3,
+ dev->mmio + S626_VECTPORT(2));
+ /* Slot 3: Send low uint8_t to target TrimDac */
+ writel(S626_XSD2 | S626_XFIFO_0 | S626_WS3,
+ dev->mmio + S626_VECTPORT(3));
+ /* Slot 4: Send NOP high uint8_t to DAC0 to keep clock running */
+ writel(S626_XSD2 | S626_XFIFO_3 | S626_WS1,
+ dev->mmio + S626_VECTPORT(4));
+ /* Slot 5: Send NOP low uint8_t to DAC0 */
+ writel(S626_XSD2 | S626_XFIFO_2 | S626_WS1 | S626_EOS,
+ dev->mmio + S626_VECTPORT(5));
+
+ /*
+ * Construct and transmit target DAC's serial packet:
+ * (0000 AAAA), (DDDD DDDD), (0x00), (0x00) where A<3:0> is the
+ * DAC channel's address, and D<7:0> is the DAC setpoint. Append a
+ * WORD value (that writes a channel 0 NOP command to a non-existent
+ * main DAC channel) that serves to keep the clock running after the
+ * packet has been sent to the target DAC.
+ */
+
+ /*
+ * Address the DAC channel within the trimdac device.
+ * Include DAC setpoint data.
+ */
+ return s626_send_dac(dev, (chan << 8) | dac_data);
+}
+
+static int s626_load_trim_dacs(struct comedi_device *dev)
+{
+ uint8_t i;
+ int ret;
+
+ /* Copy TrimDac setpoint values from EEPROM to TrimDacs. */
+ for (i = 0; i < ARRAY_SIZE(s626_trimchan); i++) {
+ ret = s626_write_trim_dac(dev, i,
+ s626_i2c_read(dev, s626_trimadrs[i]));
+ if (ret)
+ return ret;
+ }
+ return 0;
+}
+
+/* ****** COUNTER FUNCTIONS ******* */
+
+/*
+ * All counter functions address a specific counter by means of the
+ * "Counter" argument, which is a logical counter number. The Counter
+ * argument may have any of the following legal values: 0=0A, 1=1A,
+ * 2=2A, 3=0B, 4=1B, 5=2B.
+ */
+
+/*
+ * Return/set a counter pair's latch trigger source. 0: On read
+ * access, 1: A index latches A, 2: B index latches B, 3: A overflow
+ * latches B.
+ */
+static void s626_set_latch_source(struct comedi_device *dev,
+ unsigned int chan, uint16_t value)
+{
+ s626_debi_replace(dev, S626_LP_CRB(chan),
+ ~(S626_CRBMSK_INTCTRL | S626_CRBMSK_LATCHSRC),
+ S626_SET_CRB_LATCHSRC(value));
+}
+
+/*
+ * Write value into counter preload register.
+ */
+static void s626_preload(struct comedi_device *dev,
+ unsigned int chan, uint32_t value)
+{
+ s626_debi_write(dev, S626_LP_CNTR(chan), value);
+ s626_debi_write(dev, S626_LP_CNTR(chan) + 2, value >> 16);
+}
+
+/* ****** PRIVATE COUNTER FUNCTIONS ****** */
+
+/*
+ * Reset a counter's index and overflow event capture flags.
+ */
+static void s626_reset_cap_flags(struct comedi_device *dev,
+ unsigned int chan)
+{
+ uint16_t set;
+
+ set = S626_SET_CRB_INTRESETCMD(1);
+ if (chan < 3)
+ set |= S626_SET_CRB_INTRESET_A(1);
+ else
+ set |= S626_SET_CRB_INTRESET_B(1);
+
+ s626_debi_replace(dev, S626_LP_CRB(chan), ~S626_CRBMSK_INTCTRL, set);
+}
+
+#ifdef unused
+/*
+ * Return counter setup in a format (COUNTER_SETUP) that is consistent
+ * for both A and B counters.
+ */
+static uint16_t s626_get_mode_a(struct comedi_device *dev,
+ unsigned int chan)
+{
+ uint16_t cra;
+ uint16_t crb;
+ uint16_t setup;
+ unsigned cntsrc, clkmult, clkpol, encmode;
+
+ /* Fetch CRA and CRB register images. */
+ cra = s626_debi_read(dev, S626_LP_CRA(chan));
+ crb = s626_debi_read(dev, S626_LP_CRB(chan));
+
+ /*
+ * Populate the standardized counter setup bit fields.
+ */
+ setup =
+ /* LoadSrc = LoadSrcA. */
+ S626_SET_STD_LOADSRC(S626_GET_CRA_LOADSRC_A(cra)) |
+ /* LatchSrc = LatchSrcA. */
+ S626_SET_STD_LATCHSRC(S626_GET_CRB_LATCHSRC(crb)) |
+ /* IntSrc = IntSrcA. */
+ S626_SET_STD_INTSRC(S626_GET_CRA_INTSRC_A(cra)) |
+ /* IndxSrc = IndxSrcA. */
+ S626_SET_STD_INDXSRC(S626_GET_CRA_INDXSRC_A(cra)) |
+ /* IndxPol = IndxPolA. */
+ S626_SET_STD_INDXPOL(S626_GET_CRA_INDXPOL_A(cra)) |
+ /* ClkEnab = ClkEnabA. */
+ S626_SET_STD_CLKENAB(S626_GET_CRB_CLKENAB_A(crb));
+
+ /* Adjust mode-dependent parameters. */
+ cntsrc = S626_GET_CRA_CNTSRC_A(cra);
+ if (cntsrc & S626_CNTSRC_SYSCLK) {
+ /* Timer mode (CntSrcA<1> == 1): */
+ encmode = S626_ENCMODE_TIMER;
+ /* Set ClkPol to indicate count direction (CntSrcA<0>). */
+ clkpol = cntsrc & 1;
+ /* ClkMult must be 1x in Timer mode. */
+ clkmult = S626_CLKMULT_1X;
+ } else {
+ /* Counter mode (CntSrcA<1> == 0): */
+ encmode = S626_ENCMODE_COUNTER;
+ /* Pass through ClkPol. */
+ clkpol = S626_GET_CRA_CLKPOL_A(cra);
+ /* Force ClkMult to 1x if not legal, else pass through. */
+ clkmult = S626_GET_CRA_CLKMULT_A(cra);
+ if (clkmult == S626_CLKMULT_SPECIAL)
+ clkmult = S626_CLKMULT_1X;
+ }
+ setup |= S626_SET_STD_ENCMODE(encmode) | S626_SET_STD_CLKMULT(clkmult) |
+ S626_SET_STD_CLKPOL(clkpol);
+
+ /* Return adjusted counter setup. */
+ return setup;
+}
+
+static uint16_t s626_get_mode_b(struct comedi_device *dev,
+ unsigned int chan)
+{
+ uint16_t cra;
+ uint16_t crb;
+ uint16_t setup;
+ unsigned cntsrc, clkmult, clkpol, encmode;
+
+ /* Fetch CRA and CRB register images. */
+ cra = s626_debi_read(dev, S626_LP_CRA(chan));
+ crb = s626_debi_read(dev, S626_LP_CRB(chan));
+
+ /*
+ * Populate the standardized counter setup bit fields.
+ */
+ setup =
+ /* IntSrc = IntSrcB. */
+ S626_SET_STD_INTSRC(S626_GET_CRB_INTSRC_B(crb)) |
+ /* LatchSrc = LatchSrcB. */
+ S626_SET_STD_LATCHSRC(S626_GET_CRB_LATCHSRC(crb)) |
+ /* LoadSrc = LoadSrcB. */
+ S626_SET_STD_LOADSRC(S626_GET_CRB_LOADSRC_B(crb)) |
+ /* IndxPol = IndxPolB. */
+ S626_SET_STD_INDXPOL(S626_GET_CRB_INDXPOL_B(crb)) |
+ /* ClkEnab = ClkEnabB. */
+ S626_SET_STD_CLKENAB(S626_GET_CRB_CLKENAB_B(crb)) |
+ /* IndxSrc = IndxSrcB. */
+ S626_SET_STD_INDXSRC(S626_GET_CRA_INDXSRC_B(cra));
+
+ /* Adjust mode-dependent parameters. */
+ cntsrc = S626_GET_CRA_CNTSRC_B(cra);
+ clkmult = S626_GET_CRB_CLKMULT_B(crb);
+ if (clkmult == S626_CLKMULT_SPECIAL) {
+ /* Extender mode (ClkMultB == S626_CLKMULT_SPECIAL): */
+ encmode = S626_ENCMODE_EXTENDER;
+ /* Indicate multiplier is 1x. */
+ clkmult = S626_CLKMULT_1X;
+ /* Set ClkPol equal to Timer count direction (CntSrcB<0>). */
+ clkpol = cntsrc & 1;
+ } else if (cntsrc & S626_CNTSRC_SYSCLK) {
+ /* Timer mode (CntSrcB<1> == 1): */
+ encmode = S626_ENCMODE_TIMER;
+ /* Indicate multiplier is 1x. */
+ clkmult = S626_CLKMULT_1X;
+ /* Set ClkPol equal to Timer count direction (CntSrcB<0>). */
+ clkpol = cntsrc & 1;
+ } else {
+ /* If Counter mode (CntSrcB<1> == 0): */
+ encmode = S626_ENCMODE_COUNTER;
+ /* Clock multiplier is passed through. */
+ /* Clock polarity is passed through. */
+ clkpol = S626_GET_CRB_CLKPOL_B(crb);
+ }
+ setup |= S626_SET_STD_ENCMODE(encmode) | S626_SET_STD_CLKMULT(clkmult) |
+ S626_SET_STD_CLKPOL(clkpol);
+
+ /* Return adjusted counter setup. */
+ return setup;
+}
+
+static uint16_t s626_get_mode(struct comedi_device *dev,
+ unsigned int chan)
+{
+ return (chan < 3) ? s626_get_mode_a(dev, chan)
+ : s626_get_mode_b(dev, chan);
+}
+#endif
+
+/*
+ * Set the operating mode for the specified counter. The setup
+ * parameter is treated as a COUNTER_SETUP data type. The following
+ * parameters are programmable (all other parms are ignored): ClkMult,
+ * ClkPol, ClkEnab, IndexSrc, IndexPol, LoadSrc.
+ */
+static void s626_set_mode_a(struct comedi_device *dev,
+ unsigned int chan, uint16_t setup,
+ uint16_t disable_int_src)
+{
+ struct s626_private *devpriv = dev->private;
+ uint16_t cra;
+ uint16_t crb;
+ unsigned cntsrc, clkmult, clkpol;
+
+ /* Initialize CRA and CRB images. */
+ /* Preload trigger is passed through. */
+ cra = S626_SET_CRA_LOADSRC_A(S626_GET_STD_LOADSRC(setup));
+ /* IndexSrc is passed through. */
+ cra |= S626_SET_CRA_INDXSRC_A(S626_GET_STD_INDXSRC(setup));
+
+ /* Reset any pending CounterA event captures. */
+ crb = S626_SET_CRB_INTRESETCMD(1) | S626_SET_CRB_INTRESET_A(1);
+ /* Clock enable is passed through. */
+ crb |= S626_SET_CRB_CLKENAB_A(S626_GET_STD_CLKENAB(setup));
+
+ /* Force IntSrc to Disabled if disable_int_src is asserted. */
+ if (!disable_int_src)
+ cra |= S626_SET_CRA_INTSRC_A(S626_GET_STD_INTSRC(setup));
+
+ /* Populate all mode-dependent attributes of CRA & CRB images. */
+ clkpol = S626_GET_STD_CLKPOL(setup);
+ switch (S626_GET_STD_ENCMODE(setup)) {
+ case S626_ENCMODE_EXTENDER: /* Extender Mode: */
+ /* Force to Timer mode (Extender valid only for B counters). */
+ /* Fall through to case S626_ENCMODE_TIMER: */
+ case S626_ENCMODE_TIMER: /* Timer Mode: */
+ /* CntSrcA<1> selects system clock */
+ cntsrc = S626_CNTSRC_SYSCLK;
+ /* Count direction (CntSrcA<0>) obtained from ClkPol. */
+ cntsrc |= clkpol;
+ /* ClkPolA behaves as always-on clock enable. */
+ clkpol = 1;
+ /* ClkMult must be 1x. */
+ clkmult = S626_CLKMULT_1X;
+ break;
+ default: /* Counter Mode: */
+ /* Select ENC_C and ENC_D as clock/direction inputs. */
+ cntsrc = S626_CNTSRC_ENCODER;
+ /* Clock polarity is passed through. */
+ /* Force multiplier to x1 if not legal, else pass through. */
+ clkmult = S626_GET_STD_CLKMULT(setup);
+ if (clkmult == S626_CLKMULT_SPECIAL)
+ clkmult = S626_CLKMULT_1X;
+ break;
+ }
+ cra |= S626_SET_CRA_CNTSRC_A(cntsrc) | S626_SET_CRA_CLKPOL_A(clkpol) |
+ S626_SET_CRA_CLKMULT_A(clkmult);
+
+ /*
+ * Force positive index polarity if IndxSrc is software-driven only,
+ * otherwise pass it through.
+ */
+ if (S626_GET_STD_INDXSRC(setup) != S626_INDXSRC_SOFT)
+ cra |= S626_SET_CRA_INDXPOL_A(S626_GET_STD_INDXPOL(setup));
+
+ /*
+ * If IntSrc has been forced to Disabled, update the MISC2 interrupt
+ * enable mask to indicate the counter interrupt is disabled.
+ */
+ if (disable_int_src)
+ devpriv->counter_int_enabs &= ~(S626_OVERMASK(chan) |
+ S626_INDXMASK(chan));
+
+ /*
+ * While retaining CounterB and LatchSrc configurations, program the
+ * new counter operating mode.
+ */
+ s626_debi_replace(dev, S626_LP_CRA(chan),
+ S626_CRAMSK_INDXSRC_B | S626_CRAMSK_CNTSRC_B, cra);
+ s626_debi_replace(dev, S626_LP_CRB(chan),
+ ~(S626_CRBMSK_INTCTRL | S626_CRBMSK_CLKENAB_A), crb);
+}
+
+static void s626_set_mode_b(struct comedi_device *dev,
+ unsigned int chan, uint16_t setup,
+ uint16_t disable_int_src)
+{
+ struct s626_private *devpriv = dev->private;
+ uint16_t cra;
+ uint16_t crb;
+ unsigned cntsrc, clkmult, clkpol;
+
+ /* Initialize CRA and CRB images. */
+ /* IndexSrc is passed through. */
+ cra = S626_SET_CRA_INDXSRC_B(S626_GET_STD_INDXSRC(setup));
+
+ /* Reset event captures and disable interrupts. */
+ crb = S626_SET_CRB_INTRESETCMD(1) | S626_SET_CRB_INTRESET_B(1);
+ /* Clock enable is passed through. */
+ crb |= S626_SET_CRB_CLKENAB_B(S626_GET_STD_CLKENAB(setup));
+ /* Preload trigger source is passed through. */
+ crb |= S626_SET_CRB_LOADSRC_B(S626_GET_STD_LOADSRC(setup));
+
+ /* Force IntSrc to Disabled if disable_int_src is asserted. */
+ if (!disable_int_src)
+ crb |= S626_SET_CRB_INTSRC_B(S626_GET_STD_INTSRC(setup));
+
+ /* Populate all mode-dependent attributes of CRA & CRB images. */
+ clkpol = S626_GET_STD_CLKPOL(setup);
+ switch (S626_GET_STD_ENCMODE(setup)) {
+ case S626_ENCMODE_TIMER: /* Timer Mode: */
+ /* CntSrcB<1> selects system clock */
+ cntsrc = S626_CNTSRC_SYSCLK;
+ /* with direction (CntSrcB<0>) obtained from ClkPol. */
+ cntsrc |= clkpol;
+ /* ClkPolB behaves as always-on clock enable. */
+ clkpol = 1;
+ /* ClkMultB must be 1x. */
+ clkmult = S626_CLKMULT_1X;
+ break;
+ case S626_ENCMODE_EXTENDER: /* Extender Mode: */
+ /* CntSrcB source is OverflowA (same as "timer") */
+ cntsrc = S626_CNTSRC_SYSCLK;
+ /* with direction obtained from ClkPol. */
+ cntsrc |= clkpol;
+ /* ClkPolB controls IndexB -- always set to active. */
+ clkpol = 1;
+ /* ClkMultB selects OverflowA as the clock source. */
+ clkmult = S626_CLKMULT_SPECIAL;
+ break;
+ default: /* Counter Mode: */
+ /* Select ENC_C and ENC_D as clock/direction inputs. */
+ cntsrc = S626_CNTSRC_ENCODER;
+ /* ClkPol is passed through. */
+ /* Force ClkMult to x1 if not legal, otherwise pass through. */
+ clkmult = S626_GET_STD_CLKMULT(setup);
+ if (clkmult == S626_CLKMULT_SPECIAL)
+ clkmult = S626_CLKMULT_1X;
+ break;
+ }
+ cra |= S626_SET_CRA_CNTSRC_B(cntsrc);
+ crb |= S626_SET_CRB_CLKPOL_B(clkpol) | S626_SET_CRB_CLKMULT_B(clkmult);
+
+ /*
+ * Force positive index polarity if IndxSrc is software-driven only,
+ * otherwise pass it through.
+ */
+ if (S626_GET_STD_INDXSRC(setup) != S626_INDXSRC_SOFT)
+ crb |= S626_SET_CRB_INDXPOL_B(S626_GET_STD_INDXPOL(setup));
+
+ /*
+ * If IntSrc has been forced to Disabled, update the MISC2 interrupt
+ * enable mask to indicate the counter interrupt is disabled.
+ */
+ if (disable_int_src)
+ devpriv->counter_int_enabs &= ~(S626_OVERMASK(chan) |
+ S626_INDXMASK(chan));
+
+ /*
+ * While retaining CounterA and LatchSrc configurations, program the
+ * new counter operating mode.
+ */
+ s626_debi_replace(dev, S626_LP_CRA(chan),
+ ~(S626_CRAMSK_INDXSRC_B | S626_CRAMSK_CNTSRC_B), cra);
+ s626_debi_replace(dev, S626_LP_CRB(chan),
+ S626_CRBMSK_CLKENAB_A | S626_CRBMSK_LATCHSRC, crb);
+}
+
+static void s626_set_mode(struct comedi_device *dev,
+ unsigned int chan,
+ uint16_t setup, uint16_t disable_int_src)
+{
+ if (chan < 3)
+ s626_set_mode_a(dev, chan, setup, disable_int_src);
+ else
+ s626_set_mode_b(dev, chan, setup, disable_int_src);
+}
+
+/*
+ * Return/set a counter's enable. enab: 0=always enabled, 1=enabled by index.
+ */
+static void s626_set_enable(struct comedi_device *dev,
+ unsigned int chan, uint16_t enab)
+{
+ unsigned int mask = S626_CRBMSK_INTCTRL;
+ unsigned int set;
+
+ if (chan < 3) {
+ mask |= S626_CRBMSK_CLKENAB_A;
+ set = S626_SET_CRB_CLKENAB_A(enab);
+ } else {
+ mask |= S626_CRBMSK_CLKENAB_B;
+ set = S626_SET_CRB_CLKENAB_B(enab);
+ }
+ s626_debi_replace(dev, S626_LP_CRB(chan), ~mask, set);
+}
+
+#ifdef unused
+static uint16_t s626_get_enable(struct comedi_device *dev,
+ unsigned int chan)
+{
+ uint16_t crb = s626_debi_read(dev, S626_LP_CRB(chan));
+
+ return (chan < 3) ? S626_GET_CRB_CLKENAB_A(crb)
+ : S626_GET_CRB_CLKENAB_B(crb);
+}
+#endif
+
+#ifdef unused
+static uint16_t s626_get_latch_source(struct comedi_device *dev,
+ unsigned int chan)
+{
+ return S626_GET_CRB_LATCHSRC(s626_debi_read(dev, S626_LP_CRB(chan)));
+}
+#endif
+
+/*
+ * Return/set the event that will trigger transfer of the preload
+ * register into the counter. 0=ThisCntr_Index, 1=ThisCntr_Overflow,
+ * 2=OverflowA (B counters only), 3=disabled.
+ */
+static void s626_set_load_trig(struct comedi_device *dev,
+ unsigned int chan, uint16_t trig)
+{
+ uint16_t reg;
+ uint16_t mask;
+ uint16_t set;
+
+ if (chan < 3) {
+ reg = S626_LP_CRA(chan);
+ mask = S626_CRAMSK_LOADSRC_A;
+ set = S626_SET_CRA_LOADSRC_A(trig);
+ } else {
+ reg = S626_LP_CRB(chan);
+ mask = S626_CRBMSK_LOADSRC_B | S626_CRBMSK_INTCTRL;
+ set = S626_SET_CRB_LOADSRC_B(trig);
+ }
+ s626_debi_replace(dev, reg, ~mask, set);
+}
+
+#ifdef unused
+static uint16_t s626_get_load_trig(struct comedi_device *dev,
+ unsigned int chan)
+{
+ if (chan < 3)
+ return S626_GET_CRA_LOADSRC_A(s626_debi_read(dev,
+ S626_LP_CRA(chan)));
+ else
+ return S626_GET_CRB_LOADSRC_B(s626_debi_read(dev,
+ S626_LP_CRB(chan)));
+}
+#endif
+
+/*
+ * Return/set counter interrupt source and clear any captured
+ * index/overflow events. int_source: 0=Disabled, 1=OverflowOnly,
+ * 2=IndexOnly, 3=IndexAndOverflow.
+ */
+static void s626_set_int_src(struct comedi_device *dev,
+ unsigned int chan, uint16_t int_source)
+{
+ struct s626_private *devpriv = dev->private;
+ uint16_t cra_reg = S626_LP_CRA(chan);
+ uint16_t crb_reg = S626_LP_CRB(chan);
+
+ if (chan < 3) {
+ /* Reset any pending counter overflow or index captures */
+ s626_debi_replace(dev, crb_reg, ~S626_CRBMSK_INTCTRL,
+ S626_SET_CRB_INTRESETCMD(1) |
+ S626_SET_CRB_INTRESET_A(1));
+
+ /* Program counter interrupt source */
+ s626_debi_replace(dev, cra_reg, ~S626_CRAMSK_INTSRC_A,
+ S626_SET_CRA_INTSRC_A(int_source));
+ } else {
+ uint16_t crb;
+
+ /* Cache writeable CRB register image */
+ crb = s626_debi_read(dev, crb_reg);
+ crb &= ~S626_CRBMSK_INTCTRL;
+
+ /* Reset any pending counter overflow or index captures */
+ s626_debi_write(dev, crb_reg,
+ crb | S626_SET_CRB_INTRESETCMD(1) |
+ S626_SET_CRB_INTRESET_B(1));
+
+ /* Program counter interrupt source */
+ s626_debi_write(dev, crb_reg,
+ (crb & ~S626_CRBMSK_INTSRC_B) |
+ S626_SET_CRB_INTSRC_B(int_source));
+ }
+
+ /* Update MISC2 interrupt enable mask. */
+ devpriv->counter_int_enabs &= ~(S626_OVERMASK(chan) |
+ S626_INDXMASK(chan));
+ switch (int_source) {
+ case 0:
+ default:
+ break;
+ case 1:
+ devpriv->counter_int_enabs |= S626_OVERMASK(chan);
+ break;
+ case 2:
+ devpriv->counter_int_enabs |= S626_INDXMASK(chan);
+ break;
+ case 3:
+ devpriv->counter_int_enabs |= (S626_OVERMASK(chan) |
+ S626_INDXMASK(chan));
+ break;
+ }
+}
+
+#ifdef unused
+static uint16_t s626_get_int_src(struct comedi_device *dev,
+ unsigned int chan)
+{
+ if (chan < 3)
+ return S626_GET_CRA_INTSRC_A(s626_debi_read(dev,
+ S626_LP_CRA(chan)));
+ else
+ return S626_GET_CRB_INTSRC_B(s626_debi_read(dev,
+ S626_LP_CRB(chan)));
+}
+#endif
+
+#ifdef unused
+/*
+ * Return/set the clock multiplier.
+ */
+static void s626_set_clk_mult(struct comedi_device *dev,
+ unsigned int chan, uint16_t value)
+{
+ uint16_t mode;
+
+ mode = s626_get_mode(dev, chan);
+ mode &= ~S626_STDMSK_CLKMULT;
+ mode |= S626_SET_STD_CLKMULT(value);
+
+ s626_set_mode(dev, chan, mode, false);
+}
+
+static uint16_t s626_get_clk_mult(struct comedi_device *dev,
+ unsigned int chan)
+{
+ return S626_GET_STD_CLKMULT(s626_get_mode(dev, chan));
+}
+
+/*
+ * Return/set the clock polarity.
+ */
+static void s626_set_clk_pol(struct comedi_device *dev,
+ unsigned int chan, uint16_t value)
+{
+ uint16_t mode;
+
+ mode = s626_get_mode(dev, chan);
+ mode &= ~S626_STDMSK_CLKPOL;
+ mode |= S626_SET_STD_CLKPOL(value);
+
+ s626_set_mode(dev, chan, mode, false);
+}
+
+static uint16_t s626_get_clk_pol(struct comedi_device *dev,
+ unsigned int chan)
+{
+ return S626_GET_STD_CLKPOL(s626_get_mode(dev, chan));
+}
+
+/*
+ * Return/set the encoder mode.
+ */
+static void s626_set_enc_mode(struct comedi_device *dev,
+ unsigned int chan, uint16_t value)
+{
+ uint16_t mode;
+
+ mode = s626_get_mode(dev, chan);
+ mode &= ~S626_STDMSK_ENCMODE;
+ mode |= S626_SET_STD_ENCMODE(value);
+
+ s626_set_mode(dev, chan, mode, false);
+}
+
+static uint16_t s626_get_enc_mode(struct comedi_device *dev,
+ unsigned int chan)
+{
+ return S626_GET_STD_ENCMODE(s626_get_mode(dev, chan));
+}
+
+/*
+ * Return/set the index polarity.
+ */
+static void s626_set_index_pol(struct comedi_device *dev,
+ unsigned int chan, uint16_t value)
+{
+ uint16_t mode;
+
+ mode = s626_get_mode(dev, chan);
+ mode &= ~S626_STDMSK_INDXPOL;
+ mode |= S626_SET_STD_INDXPOL(value != 0);
+
+ s626_set_mode(dev, chan, mode, false);
+}
+
+static uint16_t s626_get_index_pol(struct comedi_device *dev,
+ unsigned int chan)
+{
+ return S626_GET_STD_INDXPOL(s626_get_mode(dev, chan));
+}
+
+/*
+ * Return/set the index source.
+ */
+static void s626_set_index_src(struct comedi_device *dev,
+ unsigned int chan, uint16_t value)
+{
+ uint16_t mode;
+
+ mode = s626_get_mode(dev, chan);
+ mode &= ~S626_STDMSK_INDXSRC;
+ mode |= S626_SET_STD_INDXSRC(value != 0);
+
+ s626_set_mode(dev, chan, mode, false);
+}
+
+static uint16_t s626_get_index_src(struct comedi_device *dev,
+ unsigned int chan)
+{
+ return S626_GET_STD_INDXSRC(s626_get_mode(dev, chan));
+}
+#endif
+
+/*
+ * Generate an index pulse.
+ */
+static void s626_pulse_index(struct comedi_device *dev,
+ unsigned int chan)
+{
+ if (chan < 3) {
+ uint16_t cra;
+
+ cra = s626_debi_read(dev, S626_LP_CRA(chan));
+
+ /* Pulse index */
+ s626_debi_write(dev, S626_LP_CRA(chan),
+ (cra ^ S626_CRAMSK_INDXPOL_A));
+ s626_debi_write(dev, S626_LP_CRA(chan), cra);
+ } else {
+ uint16_t crb;
+
+ crb = s626_debi_read(dev, S626_LP_CRB(chan));
+ crb &= ~S626_CRBMSK_INTCTRL;
+
+ /* Pulse index */
+ s626_debi_write(dev, S626_LP_CRB(chan),
+ (crb ^ S626_CRBMSK_INDXPOL_B));
+ s626_debi_write(dev, S626_LP_CRB(chan), crb);
+ }
+}
+
+static unsigned int s626_ai_reg_to_uint(unsigned int data)
+{
+ return ((data >> 18) & 0x3fff) ^ 0x2000;
+}
+
+static int s626_dio_set_irq(struct comedi_device *dev, unsigned int chan)
+{
+ unsigned int group = chan / 16;
+ unsigned int mask = 1 << (chan - (16 * group));
+ unsigned int status;
+
+ /* set channel to capture positive edge */
+ status = s626_debi_read(dev, S626_LP_RDEDGSEL(group));
+ s626_debi_write(dev, S626_LP_WREDGSEL(group), mask | status);
+
+ /* enable interrupt on selected channel */
+ status = s626_debi_read(dev, S626_LP_RDINTSEL(group));
+ s626_debi_write(dev, S626_LP_WRINTSEL(group), mask | status);
+
+ /* enable edge capture write command */
+ s626_debi_write(dev, S626_LP_MISC1, S626_MISC1_EDCAP);
+
+ /* enable edge capture on selected channel */
+ status = s626_debi_read(dev, S626_LP_RDCAPSEL(group));
+ s626_debi_write(dev, S626_LP_WRCAPSEL(group), mask | status);
+
+ return 0;
+}
+
+static int s626_dio_reset_irq(struct comedi_device *dev, unsigned int group,
+ unsigned int mask)
+{
+ /* disable edge capture write command */
+ s626_debi_write(dev, S626_LP_MISC1, S626_MISC1_NOEDCAP);
+
+ /* enable edge capture on selected channel */
+ s626_debi_write(dev, S626_LP_WRCAPSEL(group), mask);
+
+ return 0;
+}
+
+static int s626_dio_clear_irq(struct comedi_device *dev)
+{
+ unsigned int group;
+
+ /* disable edge capture write command */
+ s626_debi_write(dev, S626_LP_MISC1, S626_MISC1_NOEDCAP);
+
+ /* clear all dio pending events and interrupt */
+ for (group = 0; group < S626_DIO_BANKS; group++)
+ s626_debi_write(dev, S626_LP_WRCAPSEL(group), 0xffff);
+
+ return 0;
+}
+
+static void s626_handle_dio_interrupt(struct comedi_device *dev,
+ uint16_t irqbit, uint8_t group)
+{
+ struct s626_private *devpriv = dev->private;
+ struct comedi_subdevice *s = dev->read_subdev;
+ struct comedi_cmd *cmd = &s->async->cmd;
+
+ s626_dio_reset_irq(dev, group, irqbit);
+
+ if (devpriv->ai_cmd_running) {
+ /* check if interrupt is an ai acquisition start trigger */
+ if ((irqbit >> (cmd->start_arg - (16 * group))) == 1 &&
+ cmd->start_src == TRIG_EXT) {
+ /* Start executing the RPS program */
+ s626_mc_enable(dev, S626_MC1_ERPS1, S626_P_MC1);
+
+ if (cmd->scan_begin_src == TRIG_EXT)
+ s626_dio_set_irq(dev, cmd->scan_begin_arg);
+ }
+ if ((irqbit >> (cmd->scan_begin_arg - (16 * group))) == 1 &&
+ cmd->scan_begin_src == TRIG_EXT) {
+ /* Trigger ADC scan loop start */
+ s626_mc_enable(dev, S626_MC2_ADC_RPS, S626_P_MC2);
+
+ if (cmd->convert_src == TRIG_EXT) {
+ devpriv->ai_convert_count = cmd->chanlist_len;
+
+ s626_dio_set_irq(dev, cmd->convert_arg);
+ }
+
+ if (cmd->convert_src == TRIG_TIMER) {
+ devpriv->ai_convert_count = cmd->chanlist_len;
+ s626_set_enable(dev, 5, S626_CLKENAB_ALWAYS);
+ }
+ }
+ if ((irqbit >> (cmd->convert_arg - (16 * group))) == 1 &&
+ cmd->convert_src == TRIG_EXT) {
+ /* Trigger ADC scan loop start */
+ s626_mc_enable(dev, S626_MC2_ADC_RPS, S626_P_MC2);
+
+ devpriv->ai_convert_count--;
+ if (devpriv->ai_convert_count > 0)
+ s626_dio_set_irq(dev, cmd->convert_arg);
+ }
+ }
+}
+
+static void s626_check_dio_interrupts(struct comedi_device *dev)
+{
+ uint16_t irqbit;
+ uint8_t group;
+
+ for (group = 0; group < S626_DIO_BANKS; group++) {
+ /* read interrupt type */
+ irqbit = s626_debi_read(dev, S626_LP_RDCAPFLG(group));
+
+ /* check if interrupt is generated from dio channels */
+ if (irqbit) {
+ s626_handle_dio_interrupt(dev, irqbit, group);
+ return;
+ }
+ }
+}
+
+static void s626_check_counter_interrupts(struct comedi_device *dev)
+{
+ struct s626_private *devpriv = dev->private;
+ struct comedi_subdevice *s = dev->read_subdev;
+ struct comedi_async *async = s->async;
+ struct comedi_cmd *cmd = &async->cmd;
+ uint16_t irqbit;
+
+ /* read interrupt type */
+ irqbit = s626_debi_read(dev, S626_LP_RDMISC2);
+
+ /* check interrupt on counters */
+ if (irqbit & S626_IRQ_COINT1A) {
+ /* clear interrupt capture flag */
+ s626_reset_cap_flags(dev, 0);
+ }
+ if (irqbit & S626_IRQ_COINT2A) {
+ /* clear interrupt capture flag */
+ s626_reset_cap_flags(dev, 1);
+ }
+ if (irqbit & S626_IRQ_COINT3A) {
+ /* clear interrupt capture flag */
+ s626_reset_cap_flags(dev, 2);
+ }
+ if (irqbit & S626_IRQ_COINT1B) {
+ /* clear interrupt capture flag */
+ s626_reset_cap_flags(dev, 3);
+ }
+ if (irqbit & S626_IRQ_COINT2B) {
+ /* clear interrupt capture flag */
+ s626_reset_cap_flags(dev, 4);
+
+ if (devpriv->ai_convert_count > 0) {
+ devpriv->ai_convert_count--;
+ if (devpriv->ai_convert_count == 0)
+ s626_set_enable(dev, 4, S626_CLKENAB_INDEX);
+
+ if (cmd->convert_src == TRIG_TIMER) {
+ /* Trigger ADC scan loop start */
+ s626_mc_enable(dev, S626_MC2_ADC_RPS,
+ S626_P_MC2);
+ }
+ }
+ }
+ if (irqbit & S626_IRQ_COINT3B) {
+ /* clear interrupt capture flag */
+ s626_reset_cap_flags(dev, 5);
+
+ if (cmd->scan_begin_src == TRIG_TIMER) {
+ /* Trigger ADC scan loop start */
+ s626_mc_enable(dev, S626_MC2_ADC_RPS, S626_P_MC2);
+ }
+
+ if (cmd->convert_src == TRIG_TIMER) {
+ devpriv->ai_convert_count = cmd->chanlist_len;
+ s626_set_enable(dev, 4, S626_CLKENAB_ALWAYS);
+ }
+ }
+}
+
+static bool s626_handle_eos_interrupt(struct comedi_device *dev)
+{
+ struct s626_private *devpriv = dev->private;
+ struct comedi_subdevice *s = dev->read_subdev;
+ struct comedi_async *async = s->async;
+ struct comedi_cmd *cmd = &async->cmd;
+ /*
+ * Init ptr to DMA buffer that holds new ADC data. We skip the
+ * first uint16_t in the buffer because it contains junk data
+ * from the final ADC of the previous poll list scan.
+ */
+ uint32_t *readaddr = (uint32_t *)devpriv->ana_buf.logical_base + 1;
+ int i;
+
+ /* get the data and hand it over to comedi */
+ for (i = 0; i < cmd->chanlist_len; i++) {
+ unsigned short tempdata;
+
+ /*
+ * Convert ADC data to 16-bit integer values and copy
+ * to application buffer.
+ */
+ tempdata = s626_ai_reg_to_uint(*readaddr);
+ readaddr++;
+
+ comedi_buf_write_samples(s, &tempdata, 1);
+ }
+
+ if (cmd->stop_src == TRIG_COUNT && async->scans_done >= cmd->stop_arg)
+ async->events |= COMEDI_CB_EOA;
+
+ if (async->events & COMEDI_CB_CANCEL_MASK)
+ devpriv->ai_cmd_running = 0;
+
+ if (devpriv->ai_cmd_running && cmd->scan_begin_src == TRIG_EXT)
+ s626_dio_set_irq(dev, cmd->scan_begin_arg);
+
+ comedi_handle_events(dev, s);
+
+ return !devpriv->ai_cmd_running;
+}
+
+static irqreturn_t s626_irq_handler(int irq, void *d)
+{
+ struct comedi_device *dev = d;
+ unsigned long flags;
+ uint32_t irqtype, irqstatus;
+
+ if (!dev->attached)
+ return IRQ_NONE;
+ /* lock to avoid race with comedi_poll */
+ spin_lock_irqsave(&dev->spinlock, flags);
+
+ /* save interrupt enable register state */
+ irqstatus = readl(dev->mmio + S626_P_IER);
+
+ /* read interrupt type */
+ irqtype = readl(dev->mmio + S626_P_ISR);
+
+ /* disable master interrupt */
+ writel(0, dev->mmio + S626_P_IER);
+
+ /* clear interrupt */
+ writel(irqtype, dev->mmio + S626_P_ISR);
+
+ switch (irqtype) {
+ case S626_IRQ_RPS1: /* end_of_scan occurs */
+ if (s626_handle_eos_interrupt(dev))
+ irqstatus = 0;
+ break;
+ case S626_IRQ_GPIO3: /* check dio and counter interrupt */
+ /* s626_dio_clear_irq(dev); */
+ s626_check_dio_interrupts(dev);
+ s626_check_counter_interrupts(dev);
+ break;
+ }
+
+ /* enable interrupt */
+ writel(irqstatus, dev->mmio + S626_P_IER);
+
+ spin_unlock_irqrestore(&dev->spinlock, flags);
+ return IRQ_HANDLED;
+}
+
+/*
+ * This function builds the RPS program for hardware driven acquisition.
+ */
+static void s626_reset_adc(struct comedi_device *dev, uint8_t *ppl)
+{
+ struct s626_private *devpriv = dev->private;
+ struct comedi_subdevice *s = dev->read_subdev;
+ struct comedi_cmd *cmd = &s->async->cmd;
+ uint32_t *rps;
+ uint32_t jmp_adrs;
+ uint16_t i;
+ uint16_t n;
+ uint32_t local_ppl;
+
+ /* Stop RPS program in case it is currently running */
+ s626_mc_disable(dev, S626_MC1_ERPS1, S626_P_MC1);
+
+ /* Set starting logical address to write RPS commands. */
+ rps = (uint32_t *)devpriv->rps_buf.logical_base;
+
+ /* Initialize RPS instruction pointer */
+ writel((uint32_t)devpriv->rps_buf.physical_base,
+ dev->mmio + S626_P_RPSADDR1);
+
+ /* Construct RPS program in rps_buf DMA buffer */
+ if (cmd->scan_begin_src != TRIG_FOLLOW) {
+ /* Wait for Start trigger. */
+ *rps++ = S626_RPS_PAUSE | S626_RPS_SIGADC;
+ *rps++ = S626_RPS_CLRSIGNAL | S626_RPS_SIGADC;
+ }
+
+ /*
+ * SAA7146 BUG WORKAROUND Do a dummy DEBI Write. This is necessary
+ * because the first RPS DEBI Write following a non-RPS DEBI write
+ * seems to always fail. If we don't do this dummy write, the ADC
+ * gain might not be set to the value required for the first slot in
+ * the poll list; the ADC gain would instead remain unchanged from
+ * the previously programmed value.
+ */
+ /* Write DEBI Write command and address to shadow RAM. */
+ *rps++ = S626_RPS_LDREG | (S626_P_DEBICMD >> 2);
+ *rps++ = S626_DEBI_CMD_WRWORD | S626_LP_GSEL;
+ *rps++ = S626_RPS_LDREG | (S626_P_DEBIAD >> 2);
+ /* Write DEBI immediate data to shadow RAM: */
+ *rps++ = S626_GSEL_BIPOLAR5V; /* arbitrary immediate data value. */
+ *rps++ = S626_RPS_CLRSIGNAL | S626_RPS_DEBI;
+ /* Reset "shadow RAM uploaded" flag. */
+ /* Invoke shadow RAM upload. */
+ *rps++ = S626_RPS_UPLOAD | S626_RPS_DEBI;
+ /* Wait for shadow upload to finish. */
+ *rps++ = S626_RPS_PAUSE | S626_RPS_DEBI;
+
+ /*
+ * Digitize all slots in the poll list. This is implemented as a
+ * for loop to limit the slot count to 16 in case the application
+ * forgot to set the S626_EOPL flag in the final slot.
+ */
+ for (devpriv->adc_items = 0; devpriv->adc_items < 16;
+ devpriv->adc_items++) {
+ /*
+ * Convert application's poll list item to private board class
+ * format. Each app poll list item is an uint8_t with form
+ * (EOPL,x,x,RANGE,CHAN<3:0>), where RANGE code indicates 0 =
+ * +-10V, 1 = +-5V, and EOPL = End of Poll List marker.
+ */
+ local_ppl = (*ppl << 8) | (*ppl & 0x10 ? S626_GSEL_BIPOLAR5V :
+ S626_GSEL_BIPOLAR10V);
+
+ /* Switch ADC analog gain. */
+ /* Write DEBI command and address to shadow RAM. */
+ *rps++ = S626_RPS_LDREG | (S626_P_DEBICMD >> 2);
+ *rps++ = S626_DEBI_CMD_WRWORD | S626_LP_GSEL;
+ /* Write DEBI immediate data to shadow RAM. */
+ *rps++ = S626_RPS_LDREG | (S626_P_DEBIAD >> 2);
+ *rps++ = local_ppl;
+ /* Reset "shadow RAM uploaded" flag. */
+ *rps++ = S626_RPS_CLRSIGNAL | S626_RPS_DEBI;
+ /* Invoke shadow RAM upload. */
+ *rps++ = S626_RPS_UPLOAD | S626_RPS_DEBI;
+ /* Wait for shadow upload to finish. */
+ *rps++ = S626_RPS_PAUSE | S626_RPS_DEBI;
+ /* Select ADC analog input channel. */
+ *rps++ = S626_RPS_LDREG | (S626_P_DEBICMD >> 2);
+ /* Write DEBI command and address to shadow RAM. */
+ *rps++ = S626_DEBI_CMD_WRWORD | S626_LP_ISEL;
+ *rps++ = S626_RPS_LDREG | (S626_P_DEBIAD >> 2);
+ /* Write DEBI immediate data to shadow RAM. */
+ *rps++ = local_ppl;
+ /* Reset "shadow RAM uploaded" flag. */
+ *rps++ = S626_RPS_CLRSIGNAL | S626_RPS_DEBI;
+ /* Invoke shadow RAM upload. */
+ *rps++ = S626_RPS_UPLOAD | S626_RPS_DEBI;
+ /* Wait for shadow upload to finish. */
+ *rps++ = S626_RPS_PAUSE | S626_RPS_DEBI;
+
+ /*
+ * Delay at least 10 microseconds for analog input settling.
+ * Instead of padding with NOPs, we use S626_RPS_JUMP
+ * instructions here; this allows us to produce a longer delay
+ * than is possible with NOPs because each S626_RPS_JUMP
+ * flushes the RPS' instruction prefetch pipeline.
+ */
+ jmp_adrs =
+ (uint32_t)devpriv->rps_buf.physical_base +
+ (uint32_t)((unsigned long)rps -
+ (unsigned long)devpriv->
+ rps_buf.logical_base);
+ for (i = 0; i < (10 * S626_RPSCLK_PER_US / 2); i++) {
+ jmp_adrs += 8; /* Repeat to implement time delay: */
+ /* Jump to next RPS instruction. */
+ *rps++ = S626_RPS_JUMP;
+ *rps++ = jmp_adrs;
+ }
+
+ if (cmd->convert_src != TRIG_NOW) {
+ /* Wait for Start trigger. */
+ *rps++ = S626_RPS_PAUSE | S626_RPS_SIGADC;
+ *rps++ = S626_RPS_CLRSIGNAL | S626_RPS_SIGADC;
+ }
+ /* Start ADC by pulsing GPIO1. */
+ /* Begin ADC Start pulse. */
+ *rps++ = S626_RPS_LDREG | (S626_P_GPIO >> 2);
+ *rps++ = S626_GPIO_BASE | S626_GPIO1_LO;
+ *rps++ = S626_RPS_NOP;
+ /* VERSION 2.03 CHANGE: STRETCH OUT ADC START PULSE. */
+ /* End ADC Start pulse. */
+ *rps++ = S626_RPS_LDREG | (S626_P_GPIO >> 2);
+ *rps++ = S626_GPIO_BASE | S626_GPIO1_HI;
+ /*
+ * Wait for ADC to complete (GPIO2 is asserted high when ADC not
+ * busy) and for data from previous conversion to shift into FB
+ * BUFFER 1 register.
+ */
+ /* Wait for ADC done. */
+ *rps++ = S626_RPS_PAUSE | S626_RPS_GPIO2;
+
+ /* Transfer ADC data from FB BUFFER 1 register to DMA buffer. */
+ *rps++ = S626_RPS_STREG |
+ (S626_BUGFIX_STREG(S626_P_FB_BUFFER1) >> 2);
+ *rps++ = (uint32_t)devpriv->ana_buf.physical_base +
+ (devpriv->adc_items << 2);
+
+ /*
+ * If this slot's EndOfPollList flag is set, all channels have
+ * now been processed.
+ */
+ if (*ppl++ & S626_EOPL) {
+ devpriv->adc_items++; /* Adjust poll list item count. */
+ break; /* Exit poll list processing loop. */
+ }
+ }
+
+ /*
+ * VERSION 2.01 CHANGE: DELAY CHANGED FROM 250NS to 2US. Allow the
+ * ADC to stabilize for 2 microseconds before starting the final
+ * (dummy) conversion. This delay is necessary to allow sufficient
+ * time between last conversion finished and the start of the dummy
+ * conversion. Without this delay, the last conversion's data value
+ * is sometimes set to the previous conversion's data value.
+ */
+ for (n = 0; n < (2 * S626_RPSCLK_PER_US); n++)
+ *rps++ = S626_RPS_NOP;
+
+ /*
+ * Start a dummy conversion to cause the data from the last
+ * conversion of interest to be shifted in.
+ */
+ /* Begin ADC Start pulse. */
+ *rps++ = S626_RPS_LDREG | (S626_P_GPIO >> 2);
+ *rps++ = S626_GPIO_BASE | S626_GPIO1_LO;
+ *rps++ = S626_RPS_NOP;
+ /* VERSION 2.03 CHANGE: STRETCH OUT ADC START PULSE. */
+ *rps++ = S626_RPS_LDREG | (S626_P_GPIO >> 2); /* End ADC Start pulse. */
+ *rps++ = S626_GPIO_BASE | S626_GPIO1_HI;
+
+ /*
+ * Wait for the data from the last conversion of interest to arrive
+ * in FB BUFFER 1 register.
+ */
+ *rps++ = S626_RPS_PAUSE | S626_RPS_GPIO2; /* Wait for ADC done. */
+
+ /* Transfer final ADC data from FB BUFFER 1 register to DMA buffer. */
+ *rps++ = S626_RPS_STREG | (S626_BUGFIX_STREG(S626_P_FB_BUFFER1) >> 2);
+ *rps++ = (uint32_t)devpriv->ana_buf.physical_base +
+ (devpriv->adc_items << 2);
+
+ /* Indicate ADC scan loop is finished. */
+ /* Signal ReadADC() that scan is done. */
+ /* *rps++= S626_RPS_CLRSIGNAL | S626_RPS_SIGADC; */
+
+ /* invoke interrupt */
+ if (devpriv->ai_cmd_running == 1)
+ *rps++ = S626_RPS_IRQ;
+
+ /* Restart RPS program at its beginning. */
+ *rps++ = S626_RPS_JUMP; /* Branch to start of RPS program. */
+ *rps++ = (uint32_t)devpriv->rps_buf.physical_base;
+
+ /* End of RPS program build */
+}
+
+#ifdef unused_code
+static int s626_ai_rinsn(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ struct s626_private *devpriv = dev->private;
+ uint8_t i;
+ int32_t *readaddr;
+
+ /* Trigger ADC scan loop start */
+ s626_mc_enable(dev, S626_MC2_ADC_RPS, S626_P_MC2);
+
+ /* Wait until ADC scan loop is finished (RPS Signal 0 reset) */
+ while (s626_mc_test(dev, S626_MC2_ADC_RPS, S626_P_MC2))
+ ;
+
+ /*
+ * Init ptr to DMA buffer that holds new ADC data. We skip the
+ * first uint16_t in the buffer because it contains junk data from
+ * the final ADC of the previous poll list scan.
+ */
+ readaddr = (uint32_t *)devpriv->ana_buf.logical_base + 1;
+
+ /*
+ * Convert ADC data to 16-bit integer values and
+ * copy to application buffer.
+ */
+ for (i = 0; i < devpriv->adc_items; i++) {
+ *data = s626_ai_reg_to_uint(*readaddr++);
+ data++;
+ }
+
+ return i;
+}
+#endif
+
+static int s626_ai_eoc(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned long context)
+{
+ unsigned int status;
+
+ status = readl(dev->mmio + S626_P_PSR);
+ if (status & S626_PSR_GPIO2)
+ return 0;
+ return -EBUSY;
+}
+
+static int s626_ai_insn_read(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ uint16_t chan = CR_CHAN(insn->chanspec);
+ uint16_t range = CR_RANGE(insn->chanspec);
+ uint16_t adc_spec = 0;
+ uint32_t gpio_image;
+ uint32_t tmp;
+ int ret;
+ int n;
+
+ /*
+ * Convert application's ADC specification into form
+ * appropriate for register programming.
+ */
+ if (range == 0)
+ adc_spec = (chan << 8) | (S626_GSEL_BIPOLAR5V);
+ else
+ adc_spec = (chan << 8) | (S626_GSEL_BIPOLAR10V);
+
+ /* Switch ADC analog gain. */
+ s626_debi_write(dev, S626_LP_GSEL, adc_spec); /* Set gain. */
+
+ /* Select ADC analog input channel. */
+ s626_debi_write(dev, S626_LP_ISEL, adc_spec); /* Select channel. */
+
+ for (n = 0; n < insn->n; n++) {
+ /* Delay 10 microseconds for analog input settling. */
+ udelay(10);
+
+ /* Start ADC by pulsing GPIO1 low */
+ gpio_image = readl(dev->mmio + S626_P_GPIO);
+ /* Assert ADC Start command */
+ writel(gpio_image & ~S626_GPIO1_HI, dev->mmio + S626_P_GPIO);
+ /* and stretch it out */
+ writel(gpio_image & ~S626_GPIO1_HI, dev->mmio + S626_P_GPIO);
+ writel(gpio_image & ~S626_GPIO1_HI, dev->mmio + S626_P_GPIO);
+ /* Negate ADC Start command */
+ writel(gpio_image | S626_GPIO1_HI, dev->mmio + S626_P_GPIO);
+
+ /*
+ * Wait for ADC to complete (GPIO2 is asserted high when
+ * ADC not busy) and for data from previous conversion to
+ * shift into FB BUFFER 1 register.
+ */
+
+ /* Wait for ADC done */
+ ret = comedi_timeout(dev, s, insn, s626_ai_eoc, 0);
+ if (ret)
+ return ret;
+
+ /* Fetch ADC data */
+ if (n != 0) {
+ tmp = readl(dev->mmio + S626_P_FB_BUFFER1);
+ data[n - 1] = s626_ai_reg_to_uint(tmp);
+ }
+
+ /*
+ * Allow the ADC to stabilize for 4 microseconds before
+ * starting the next (final) conversion. This delay is
+ * necessary to allow sufficient time between last
+ * conversion finished and the start of the next
+ * conversion. Without this delay, the last conversion's
+ * data value is sometimes set to the previous
+ * conversion's data value.
+ */
+ udelay(4);
+ }
+
+ /*
+ * Start a dummy conversion to cause the data from the
+ * previous conversion to be shifted in.
+ */
+ gpio_image = readl(dev->mmio + S626_P_GPIO);
+ /* Assert ADC Start command */
+ writel(gpio_image & ~S626_GPIO1_HI, dev->mmio + S626_P_GPIO);
+ /* and stretch it out */
+ writel(gpio_image & ~S626_GPIO1_HI, dev->mmio + S626_P_GPIO);
+ writel(gpio_image & ~S626_GPIO1_HI, dev->mmio + S626_P_GPIO);
+ /* Negate ADC Start command */
+ writel(gpio_image | S626_GPIO1_HI, dev->mmio + S626_P_GPIO);
+
+ /* Wait for the data to arrive in FB BUFFER 1 register. */
+
+ /* Wait for ADC done */
+ ret = comedi_timeout(dev, s, insn, s626_ai_eoc, 0);
+ if (ret)
+ return ret;
+
+ /* Fetch ADC data from audio interface's input shift register. */
+
+ /* Fetch ADC data */
+ if (n != 0) {
+ tmp = readl(dev->mmio + S626_P_FB_BUFFER1);
+ data[n - 1] = s626_ai_reg_to_uint(tmp);
+ }
+
+ return n;
+}
+
+static int s626_ai_load_polllist(uint8_t *ppl, struct comedi_cmd *cmd)
+{
+ int n;
+
+ for (n = 0; n < cmd->chanlist_len; n++) {
+ if (CR_RANGE(cmd->chanlist[n]) == 0)
+ ppl[n] = CR_CHAN(cmd->chanlist[n]) | S626_RANGE_5V;
+ else
+ ppl[n] = CR_CHAN(cmd->chanlist[n]) | S626_RANGE_10V;
+ }
+ if (n != 0)
+ ppl[n - 1] |= S626_EOPL;
+
+ return n;
+}
+
+static int s626_ai_inttrig(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ unsigned int trig_num)
+{
+ struct comedi_cmd *cmd = &s->async->cmd;
+
+ if (trig_num != cmd->start_arg)
+ return -EINVAL;
+
+ /* Start executing the RPS program */
+ s626_mc_enable(dev, S626_MC1_ERPS1, S626_P_MC1);
+
+ s->async->inttrig = NULL;
+
+ return 1;
+}
+
+/*
+ * This function doesn't require a particular form, this is just what
+ * happens to be used in some of the drivers. It should convert ns
+ * nanoseconds to a counter value suitable for programming the device.
+ * Also, it should adjust ns so that it cooresponds to the actual time
+ * that the device will use.
+ */
+static int s626_ns_to_timer(unsigned int *nanosec, unsigned int flags)
+{
+ int divider, base;
+
+ base = 500; /* 2MHz internal clock */
+
+ switch (flags & CMDF_ROUND_MASK) {
+ case CMDF_ROUND_NEAREST:
+ default:
+ divider = DIV_ROUND_CLOSEST(*nanosec, base);
+ break;
+ case CMDF_ROUND_DOWN:
+ divider = (*nanosec) / base;
+ break;
+ case CMDF_ROUND_UP:
+ divider = DIV_ROUND_UP(*nanosec, base);
+ break;
+ }
+
+ *nanosec = base * divider;
+ return divider - 1;
+}
+
+static void s626_timer_load(struct comedi_device *dev,
+ unsigned int chan, int tick)
+{
+ uint16_t setup =
+ /* Preload upon index. */
+ S626_SET_STD_LOADSRC(S626_LOADSRC_INDX) |
+ /* Disable hardware index. */
+ S626_SET_STD_INDXSRC(S626_INDXSRC_SOFT) |
+ /* Operating mode is Timer. */
+ S626_SET_STD_ENCMODE(S626_ENCMODE_TIMER) |
+ /* Count direction is Down. */
+ S626_SET_STD_CLKPOL(S626_CNTDIR_DOWN) |
+ /* Clock multiplier is 1x. */
+ S626_SET_STD_CLKMULT(S626_CLKMULT_1X) |
+ /* Enabled by index */
+ S626_SET_STD_CLKENAB(S626_CLKENAB_INDEX);
+ uint16_t value_latchsrc = S626_LATCHSRC_A_INDXA;
+ /* uint16_t enab = S626_CLKENAB_ALWAYS; */
+
+ s626_set_mode(dev, chan, setup, false);
+
+ /* Set the preload register */
+ s626_preload(dev, chan, tick);
+
+ /*
+ * Software index pulse forces the preload register to load
+ * into the counter
+ */
+ s626_set_load_trig(dev, chan, 0);
+ s626_pulse_index(dev, chan);
+
+ /* set reload on counter overflow */
+ s626_set_load_trig(dev, chan, 1);
+
+ /* set interrupt on overflow */
+ s626_set_int_src(dev, chan, S626_INTSRC_OVER);
+
+ s626_set_latch_source(dev, chan, value_latchsrc);
+ /* s626_set_enable(dev, chan, (uint16_t)(enab != 0)); */
+}
+
+/* TO COMPLETE */
+static int s626_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
+{
+ struct s626_private *devpriv = dev->private;
+ uint8_t ppl[16];
+ struct comedi_cmd *cmd = &s->async->cmd;
+ int tick;
+
+ if (devpriv->ai_cmd_running) {
+ dev_err(dev->class_dev,
+ "s626_ai_cmd: Another ai_cmd is running\n");
+ return -EBUSY;
+ }
+ /* disable interrupt */
+ writel(0, dev->mmio + S626_P_IER);
+
+ /* clear interrupt request */
+ writel(S626_IRQ_RPS1 | S626_IRQ_GPIO3, dev->mmio + S626_P_ISR);
+
+ /* clear any pending interrupt */
+ s626_dio_clear_irq(dev);
+ /* s626_enc_clear_irq(dev); */
+
+ /* reset ai_cmd_running flag */
+ devpriv->ai_cmd_running = 0;
+
+ s626_ai_load_polllist(ppl, cmd);
+ devpriv->ai_cmd_running = 1;
+ devpriv->ai_convert_count = 0;
+
+ switch (cmd->scan_begin_src) {
+ case TRIG_FOLLOW:
+ break;
+ case TRIG_TIMER:
+ /*
+ * set a counter to generate adc trigger at scan_begin_arg
+ * interval
+ */
+ tick = s626_ns_to_timer(&cmd->scan_begin_arg, cmd->flags);
+
+ /* load timer value and enable interrupt */
+ s626_timer_load(dev, 5, tick);
+ s626_set_enable(dev, 5, S626_CLKENAB_ALWAYS);
+ break;
+ case TRIG_EXT:
+ /* set the digital line and interrupt for scan trigger */
+ if (cmd->start_src != TRIG_EXT)
+ s626_dio_set_irq(dev, cmd->scan_begin_arg);
+ break;
+ }
+
+ switch (cmd->convert_src) {
+ case TRIG_NOW:
+ break;
+ case TRIG_TIMER:
+ /*
+ * set a counter to generate adc trigger at convert_arg
+ * interval
+ */
+ tick = s626_ns_to_timer(&cmd->convert_arg, cmd->flags);
+
+ /* load timer value and enable interrupt */
+ s626_timer_load(dev, 4, tick);
+ s626_set_enable(dev, 4, S626_CLKENAB_INDEX);
+ break;
+ case TRIG_EXT:
+ /* set the digital line and interrupt for convert trigger */
+ if (cmd->scan_begin_src != TRIG_EXT &&
+ cmd->start_src == TRIG_EXT)
+ s626_dio_set_irq(dev, cmd->convert_arg);
+ break;
+ }
+
+ s626_reset_adc(dev, ppl);
+
+ switch (cmd->start_src) {
+ case TRIG_NOW:
+ /* Trigger ADC scan loop start */
+ /* s626_mc_enable(dev, S626_MC2_ADC_RPS, S626_P_MC2); */
+
+ /* Start executing the RPS program */
+ s626_mc_enable(dev, S626_MC1_ERPS1, S626_P_MC1);
+ s->async->inttrig = NULL;
+ break;
+ case TRIG_EXT:
+ /* configure DIO channel for acquisition trigger */
+ s626_dio_set_irq(dev, cmd->start_arg);
+ s->async->inttrig = NULL;
+ break;
+ case TRIG_INT:
+ s->async->inttrig = s626_ai_inttrig;
+ break;
+ }
+
+ /* enable interrupt */
+ writel(S626_IRQ_GPIO3 | S626_IRQ_RPS1, dev->mmio + S626_P_IER);
+
+ return 0;
+}
+
+static int s626_ai_cmdtest(struct comedi_device *dev,
+ struct comedi_subdevice *s, struct comedi_cmd *cmd)
+{
+ int err = 0;
+ unsigned int arg;
+
+ /* Step 1 : check if triggers are trivially valid */
+
+ err |= comedi_check_trigger_src(&cmd->start_src,
+ TRIG_NOW | TRIG_INT | TRIG_EXT);
+ err |= comedi_check_trigger_src(&cmd->scan_begin_src,
+ TRIG_TIMER | TRIG_EXT | TRIG_FOLLOW);
+ err |= comedi_check_trigger_src(&cmd->convert_src,
+ TRIG_TIMER | TRIG_EXT | TRIG_NOW);
+ err |= comedi_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
+ err |= comedi_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE);
+
+ if (err)
+ return 1;
+
+ /* Step 2a : make sure trigger sources are unique */
+
+ err |= comedi_check_trigger_is_unique(cmd->start_src);
+ err |= comedi_check_trigger_is_unique(cmd->scan_begin_src);
+ err |= comedi_check_trigger_is_unique(cmd->convert_src);
+ err |= comedi_check_trigger_is_unique(cmd->stop_src);
+
+ /* Step 2b : and mutually compatible */
+
+ if (err)
+ return 2;
+
+ /* Step 3: check if arguments are trivially valid */
+
+ switch (cmd->start_src) {
+ case TRIG_NOW:
+ case TRIG_INT:
+ err |= comedi_check_trigger_arg_is(&cmd->start_arg, 0);
+ break;
+ case TRIG_EXT:
+ err |= comedi_check_trigger_arg_max(&cmd->start_arg, 39);
+ break;
+ }
+
+ if (cmd->scan_begin_src == TRIG_EXT)
+ err |= comedi_check_trigger_arg_max(&cmd->scan_begin_arg, 39);
+ if (cmd->convert_src == TRIG_EXT)
+ err |= comedi_check_trigger_arg_max(&cmd->convert_arg, 39);
+
+#define S626_MAX_SPEED 200000 /* in nanoseconds */
+#define S626_MIN_SPEED 2000000000 /* in nanoseconds */
+
+ if (cmd->scan_begin_src == TRIG_TIMER) {
+ err |= comedi_check_trigger_arg_min(&cmd->scan_begin_arg,
+ S626_MAX_SPEED);
+ err |= comedi_check_trigger_arg_max(&cmd->scan_begin_arg,
+ S626_MIN_SPEED);
+ } else {
+ /*
+ * external trigger
+ * should be level/edge, hi/lo specification here
+ * should specify multiple external triggers
+ * err |= comedi_check_trigger_arg_max(&cmd->scan_begin_arg, 9);
+ */
+ }
+ if (cmd->convert_src == TRIG_TIMER) {
+ err |= comedi_check_trigger_arg_min(&cmd->convert_arg,
+ S626_MAX_SPEED);
+ err |= comedi_check_trigger_arg_max(&cmd->convert_arg,
+ S626_MIN_SPEED);
+ } else {
+ /*
+ * external trigger - see above
+ * err |= comedi_check_trigger_arg_max(&cmd->scan_begin_arg, 9);
+ */
+ }
+
+ err |= comedi_check_trigger_arg_is(&cmd->scan_end_arg,
+ cmd->chanlist_len);
+
+ if (cmd->stop_src == TRIG_COUNT)
+ err |= comedi_check_trigger_arg_min(&cmd->stop_arg, 1);
+ else /* TRIG_NONE */
+ err |= comedi_check_trigger_arg_is(&cmd->stop_arg, 0);
+
+ if (err)
+ return 3;
+
+ /* step 4: fix up any arguments */
+
+ if (cmd->scan_begin_src == TRIG_TIMER) {
+ arg = cmd->scan_begin_arg;
+ s626_ns_to_timer(&arg, cmd->flags);
+ err |= comedi_check_trigger_arg_is(&cmd->scan_begin_arg, arg);
+ }
+
+ if (cmd->convert_src == TRIG_TIMER) {
+ arg = cmd->convert_arg;
+ s626_ns_to_timer(&arg, cmd->flags);
+ err |= comedi_check_trigger_arg_is(&cmd->convert_arg, arg);
+
+ if (cmd->scan_begin_src == TRIG_TIMER) {
+ arg = cmd->convert_arg * cmd->scan_end_arg;
+ err |= comedi_check_trigger_arg_min(&cmd->
+ scan_begin_arg,
+ arg);
+ }
+ }
+
+ if (err)
+ return 4;
+
+ return 0;
+}
+
+static int s626_ai_cancel(struct comedi_device *dev, struct comedi_subdevice *s)
+{
+ struct s626_private *devpriv = dev->private;
+
+ /* Stop RPS program in case it is currently running */
+ s626_mc_disable(dev, S626_MC1_ERPS1, S626_P_MC1);
+
+ /* disable master interrupt */
+ writel(0, dev->mmio + S626_P_IER);
+
+ devpriv->ai_cmd_running = 0;
+
+ return 0;
+}
+
+static int s626_ao_insn_write(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ unsigned int chan = CR_CHAN(insn->chanspec);
+ int i;
+
+ for (i = 0; i < insn->n; i++) {
+ int16_t dacdata = (int16_t)data[i];
+ int ret;
+
+ dacdata -= (0x1fff);
+
+ ret = s626_set_dac(dev, chan, dacdata);
+ if (ret)
+ return ret;
+
+ s->readback[chan] = data[i];
+ }
+
+ return insn->n;
+}
+
+/* *************** DIGITAL I/O FUNCTIONS *************** */
+
+/*
+ * All DIO functions address a group of DIO channels by means of
+ * "group" argument. group may be 0, 1 or 2, which correspond to DIO
+ * ports A, B and C, respectively.
+ */
+
+static void s626_dio_init(struct comedi_device *dev)
+{
+ uint16_t group;
+
+ /* Prepare to treat writes to WRCapSel as capture disables. */
+ s626_debi_write(dev, S626_LP_MISC1, S626_MISC1_NOEDCAP);
+
+ /* For each group of sixteen channels ... */
+ for (group = 0; group < S626_DIO_BANKS; group++) {
+ /* Disable all interrupts */
+ s626_debi_write(dev, S626_LP_WRINTSEL(group), 0);
+ /* Disable all event captures */
+ s626_debi_write(dev, S626_LP_WRCAPSEL(group), 0xffff);
+ /* Init all DIOs to default edge polarity */
+ s626_debi_write(dev, S626_LP_WREDGSEL(group), 0);
+ /* Program all outputs to inactive state */
+ s626_debi_write(dev, S626_LP_WRDOUT(group), 0);
+ }
+}
+
+static int s626_dio_insn_bits(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ unsigned long group = (unsigned long)s->private;
+
+ if (comedi_dio_update_state(s, data))
+ s626_debi_write(dev, S626_LP_WRDOUT(group), s->state);
+
+ data[1] = s626_debi_read(dev, S626_LP_RDDIN(group));
+
+ return insn->n;
+}
+
+static int s626_dio_insn_config(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ unsigned long group = (unsigned long)s->private;
+ int ret;
+
+ ret = comedi_dio_insn_config(dev, s, insn, data, 0);
+ if (ret)
+ return ret;
+
+ s626_debi_write(dev, S626_LP_WRDOUT(group), s->io_bits);
+
+ return insn->n;
+}
+
+/*
+ * Now this function initializes the value of the counter (data[0])
+ * and set the subdevice. To complete with trigger and interrupt
+ * configuration.
+ *
+ * FIXME: data[0] is supposed to be an INSN_CONFIG_xxx constant indicating
+ * what is being configured, but this function appears to be using data[0]
+ * as a variable.
+ */
+static int s626_enc_insn_config(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn, unsigned int *data)
+{
+ unsigned int chan = CR_CHAN(insn->chanspec);
+ uint16_t setup =
+ /* Preload upon index. */
+ S626_SET_STD_LOADSRC(S626_LOADSRC_INDX) |
+ /* Disable hardware index. */
+ S626_SET_STD_INDXSRC(S626_INDXSRC_SOFT) |
+ /* Operating mode is Counter. */
+ S626_SET_STD_ENCMODE(S626_ENCMODE_COUNTER) |
+ /* Active high clock. */
+ S626_SET_STD_CLKPOL(S626_CLKPOL_POS) |
+ /* Clock multiplier is 1x. */
+ S626_SET_STD_CLKMULT(S626_CLKMULT_1X) |
+ /* Enabled by index */
+ S626_SET_STD_CLKENAB(S626_CLKENAB_INDEX);
+ /* uint16_t disable_int_src = true; */
+ /* uint32_t Preloadvalue; //Counter initial value */
+ uint16_t value_latchsrc = S626_LATCHSRC_AB_READ;
+ uint16_t enab = S626_CLKENAB_ALWAYS;
+
+ /* (data==NULL) ? (Preloadvalue=0) : (Preloadvalue=data[0]); */
+
+ s626_set_mode(dev, chan, setup, true);
+ s626_preload(dev, chan, data[0]);
+ s626_pulse_index(dev, chan);
+ s626_set_latch_source(dev, chan, value_latchsrc);
+ s626_set_enable(dev, chan, (enab != 0));
+
+ return insn->n;
+}
+
+static int s626_enc_insn_read(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ unsigned int chan = CR_CHAN(insn->chanspec);
+ uint16_t cntr_latch_reg = S626_LP_CNTR(chan);
+ int i;
+
+ for (i = 0; i < insn->n; i++) {
+ unsigned int val;
+
+ /*
+ * Read the counter's output latch LSW/MSW.
+ * Latches on LSW read.
+ */
+ val = s626_debi_read(dev, cntr_latch_reg);
+ val |= (s626_debi_read(dev, cntr_latch_reg + 2) << 16);
+ data[i] = val;
+ }
+
+ return insn->n;
+}
+
+static int s626_enc_insn_write(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn, unsigned int *data)
+{
+ unsigned int chan = CR_CHAN(insn->chanspec);
+
+ /* Set the preload register */
+ s626_preload(dev, chan, data[0]);
+
+ /*
+ * Software index pulse forces the preload register to load
+ * into the counter
+ */
+ s626_set_load_trig(dev, chan, 0);
+ s626_pulse_index(dev, chan);
+ s626_set_load_trig(dev, chan, 2);
+
+ return 1;
+}
+
+static void s626_write_misc2(struct comedi_device *dev, uint16_t new_image)
+{
+ s626_debi_write(dev, S626_LP_MISC1, S626_MISC1_WENABLE);
+ s626_debi_write(dev, S626_LP_WRMISC2, new_image);
+ s626_debi_write(dev, S626_LP_MISC1, S626_MISC1_WDISABLE);
+}
+
+static void s626_counters_init(struct comedi_device *dev)
+{
+ int chan;
+ uint16_t setup =
+ /* Preload upon index. */
+ S626_SET_STD_LOADSRC(S626_LOADSRC_INDX) |
+ /* Disable hardware index. */
+ S626_SET_STD_INDXSRC(S626_INDXSRC_SOFT) |
+ /* Operating mode is counter. */
+ S626_SET_STD_ENCMODE(S626_ENCMODE_COUNTER) |
+ /* Active high clock. */
+ S626_SET_STD_CLKPOL(S626_CLKPOL_POS) |
+ /* Clock multiplier is 1x. */
+ S626_SET_STD_CLKMULT(S626_CLKMULT_1X) |
+ /* Enabled by index */
+ S626_SET_STD_CLKENAB(S626_CLKENAB_INDEX);
+
+ /*
+ * Disable all counter interrupts and clear any captured counter events.
+ */
+ for (chan = 0; chan < S626_ENCODER_CHANNELS; chan++) {
+ s626_set_mode(dev, chan, setup, true);
+ s626_set_int_src(dev, chan, 0);
+ s626_reset_cap_flags(dev, chan);
+ s626_set_enable(dev, chan, S626_CLKENAB_ALWAYS);
+ }
+}
+
+static int s626_allocate_dma_buffers(struct comedi_device *dev)
+{
+ struct pci_dev *pcidev = comedi_to_pci_dev(dev);
+ struct s626_private *devpriv = dev->private;
+ void *addr;
+ dma_addr_t appdma;
+
+ addr = pci_alloc_consistent(pcidev, S626_DMABUF_SIZE, &appdma);
+ if (!addr)
+ return -ENOMEM;
+ devpriv->ana_buf.logical_base = addr;
+ devpriv->ana_buf.physical_base = appdma;
+
+ addr = pci_alloc_consistent(pcidev, S626_DMABUF_SIZE, &appdma);
+ if (!addr)
+ return -ENOMEM;
+ devpriv->rps_buf.logical_base = addr;
+ devpriv->rps_buf.physical_base = appdma;
+
+ return 0;
+}
+
+static void s626_free_dma_buffers(struct comedi_device *dev)
+{
+ struct pci_dev *pcidev = comedi_to_pci_dev(dev);
+ struct s626_private *devpriv = dev->private;
+
+ if (!devpriv)
+ return;
+
+ if (devpriv->rps_buf.logical_base)
+ pci_free_consistent(pcidev, S626_DMABUF_SIZE,
+ devpriv->rps_buf.logical_base,
+ devpriv->rps_buf.physical_base);
+ if (devpriv->ana_buf.logical_base)
+ pci_free_consistent(pcidev, S626_DMABUF_SIZE,
+ devpriv->ana_buf.logical_base,
+ devpriv->ana_buf.physical_base);
+}
+
+static int s626_initialize(struct comedi_device *dev)
+{
+ struct s626_private *devpriv = dev->private;
+ dma_addr_t phys_buf;
+ uint16_t chan;
+ int i;
+ int ret;
+
+ /* Enable DEBI and audio pins, enable I2C interface */
+ s626_mc_enable(dev, S626_MC1_DEBI | S626_MC1_AUDIO | S626_MC1_I2C,
+ S626_P_MC1);
+
+ /*
+ * Configure DEBI operating mode
+ *
+ * Local bus is 16 bits wide
+ * Declare DEBI transfer timeout interval
+ * Set up byte lane steering
+ * Intel-compatible local bus (DEBI never times out)
+ */
+ writel(S626_DEBI_CFG_SLAVE16 |
+ (S626_DEBI_TOUT << S626_DEBI_CFG_TOUT_BIT) | S626_DEBI_SWAP |
+ S626_DEBI_CFG_INTEL, dev->mmio + S626_P_DEBICFG);
+
+ /* Disable MMU paging */
+ writel(S626_DEBI_PAGE_DISABLE, dev->mmio + S626_P_DEBIPAGE);
+
+ /* Init GPIO so that ADC Start* is negated */
+ writel(S626_GPIO_BASE | S626_GPIO1_HI, dev->mmio + S626_P_GPIO);
+
+ /* I2C device address for onboard eeprom (revb) */
+ devpriv->i2c_adrs = 0xA0;
+
+ /*
+ * Issue an I2C ABORT command to halt any I2C
+ * operation in progress and reset BUSY flag.
+ */
+ writel(S626_I2C_CLKSEL | S626_I2C_ABORT,
+ dev->mmio + S626_P_I2CSTAT);
+ s626_mc_enable(dev, S626_MC2_UPLD_IIC, S626_P_MC2);
+ ret = comedi_timeout(dev, NULL, NULL, s626_i2c_handshake_eoc, 0);
+ if (ret)
+ return ret;
+
+ /*
+ * Per SAA7146 data sheet, write to STATUS
+ * reg twice to reset all I2C error flags.
+ */
+ for (i = 0; i < 2; i++) {
+ writel(S626_I2C_CLKSEL, dev->mmio + S626_P_I2CSTAT);
+ s626_mc_enable(dev, S626_MC2_UPLD_IIC, S626_P_MC2);
+ ret = comedi_timeout(dev, NULL, NULL, s626_i2c_handshake_eoc, 0);
+ if (ret)
+ return ret;
+ }
+
+ /*
+ * Init audio interface functional attributes: set DAC/ADC
+ * serial clock rates, invert DAC serial clock so that
+ * DAC data setup times are satisfied, enable DAC serial
+ * clock out.
+ */
+ writel(S626_ACON2_INIT, dev->mmio + S626_P_ACON2);
+
+ /*
+ * Set up TSL1 slot list, which is used to control the
+ * accumulation of ADC data: S626_RSD1 = shift data in on SD1.
+ * S626_SIB_A1 = store data uint8_t at next available location
+ * in FB BUFFER1 register.
+ */
+ writel(S626_RSD1 | S626_SIB_A1, dev->mmio + S626_P_TSL1);
+ writel(S626_RSD1 | S626_SIB_A1 | S626_EOS,
+ dev->mmio + S626_P_TSL1 + 4);
+
+ /* Enable TSL1 slot list so that it executes all the time */
+ writel(S626_ACON1_ADCSTART, dev->mmio + S626_P_ACON1);
+
+ /*
+ * Initialize RPS registers used for ADC
+ */
+
+ /* Physical start of RPS program */
+ writel((uint32_t)devpriv->rps_buf.physical_base,
+ dev->mmio + S626_P_RPSADDR1);
+ /* RPS program performs no explicit mem writes */
+ writel(0, dev->mmio + S626_P_RPSPAGE1);
+ /* Disable RPS timeouts */
+ writel(0, dev->mmio + S626_P_RPS1_TOUT);
+
+#if 0
+ /*
+ * SAA7146 BUG WORKAROUND
+ *
+ * Initialize SAA7146 ADC interface to a known state by
+ * invoking ADCs until FB BUFFER 1 register shows that it
+ * is correctly receiving ADC data. This is necessary
+ * because the SAA7146 ADC interface does not start up in
+ * a defined state after a PCI reset.
+ */
+ {
+ struct comedi_subdevice *s = dev->read_subdev;
+ uint8_t poll_list;
+ uint16_t adc_data;
+ uint16_t start_val;
+ uint16_t index;
+ unsigned int data[16];
+
+ /* Create a simple polling list for analog input channel 0 */
+ poll_list = S626_EOPL;
+ s626_reset_adc(dev, &poll_list);
+
+ /* Get initial ADC value */
+ s626_ai_rinsn(dev, s, NULL, data);
+ start_val = data[0];
+
+ /*
+ * VERSION 2.01 CHANGE: TIMEOUT ADDED TO PREVENT HANGED
+ * EXECUTION.
+ *
+ * Invoke ADCs until the new ADC value differs from the initial
+ * value or a timeout occurs. The timeout protects against the
+ * possibility that the driver is restarting and the ADC data is
+ * a fixed value resulting from the applied ADC analog input
+ * being unusually quiet or at the rail.
+ */
+ for (index = 0; index < 500; index++) {
+ s626_ai_rinsn(dev, s, NULL, data);
+ adc_data = data[0];
+ if (adc_data != start_val)
+ break;
+ }
+ }
+#endif /* SAA7146 BUG WORKAROUND */
+
+ /*
+ * Initialize the DAC interface
+ */
+
+ /*
+ * Init Audio2's output DMAC attributes:
+ * burst length = 1 DWORD
+ * threshold = 1 DWORD.
+ */
+ writel(0, dev->mmio + S626_P_PCI_BT_A);
+
+ /*
+ * Init Audio2's output DMA physical addresses. The protection
+ * address is set to 1 DWORD past the base address so that a
+ * single DWORD will be transferred each time a DMA transfer is
+ * enabled.
+ */
+ phys_buf = devpriv->ana_buf.physical_base +
+ (S626_DAC_WDMABUF_OS * sizeof(uint32_t));
+ writel((uint32_t)phys_buf, dev->mmio + S626_P_BASEA2_OUT);
+ writel((uint32_t)(phys_buf + sizeof(uint32_t)),
+ dev->mmio + S626_P_PROTA2_OUT);
+
+ /*
+ * Cache Audio2's output DMA buffer logical address. This is
+ * where DAC data is buffered for A2 output DMA transfers.
+ */
+ devpriv->dac_wbuf = (uint32_t *)devpriv->ana_buf.logical_base +
+ S626_DAC_WDMABUF_OS;
+
+ /*
+ * Audio2's output channels does not use paging. The
+ * protection violation handling bit is set so that the
+ * DMAC will automatically halt and its PCI address pointer
+ * will be reset when the protection address is reached.
+ */
+ writel(8, dev->mmio + S626_P_PAGEA2_OUT);
+
+ /*
+ * Initialize time slot list 2 (TSL2), which is used to control
+ * the clock generation for and serialization of data to be sent
+ * to the DAC devices. Slot 0 is a NOP that is used to trap TSL
+ * execution; this permits other slots to be safely modified
+ * without first turning off the TSL sequencer (which is
+ * apparently impossible to do). Also, SD3 (which is driven by a
+ * pull-up resistor) is shifted in and stored to the MSB of
+ * FB_BUFFER2 to be used as evidence that the slot sequence has
+ * not yet finished executing.
+ */
+
+ /* Slot 0: Trap TSL execution, shift 0xFF into FB_BUFFER2 */
+ writel(S626_XSD2 | S626_RSD3 | S626_SIB_A2 | S626_EOS,
+ dev->mmio + S626_VECTPORT(0));
+
+ /*
+ * Initialize slot 1, which is constant. Slot 1 causes a
+ * DWORD to be transferred from audio channel 2's output FIFO
+ * to the FIFO's output buffer so that it can be serialized
+ * and sent to the DAC during subsequent slots. All remaining
+ * slots are dynamically populated as required by the target
+ * DAC device.
+ */
+
+ /* Slot 1: Fetch DWORD from Audio2's output FIFO */
+ writel(S626_LF_A2, dev->mmio + S626_VECTPORT(1));
+
+ /* Start DAC's audio interface (TSL2) running */
+ writel(S626_ACON1_DACSTART, dev->mmio + S626_P_ACON1);
+
+ /*
+ * Init Trim DACs to calibrated values. Do it twice because the
+ * SAA7146 audio channel does not always reset properly and
+ * sometimes causes the first few TrimDAC writes to malfunction.
+ */
+ s626_load_trim_dacs(dev);
+ ret = s626_load_trim_dacs(dev);
+ if (ret)
+ return ret;
+
+ /*
+ * Manually init all gate array hardware in case this is a soft
+ * reset (we have no way of determining whether this is a warm
+ * or cold start). This is necessary because the gate array will
+ * reset only in response to a PCI hard reset; there is no soft
+ * reset function.
+ */
+
+ /*
+ * Init all DAC outputs to 0V and init all DAC setpoint and
+ * polarity images.
+ */
+ for (chan = 0; chan < S626_DAC_CHANNELS; chan++) {
+ ret = s626_set_dac(dev, chan, 0);
+ if (ret)
+ return ret;
+ }
+
+ /* Init counters */
+ s626_counters_init(dev);
+
+ /*
+ * Without modifying the state of the Battery Backup enab, disable
+ * the watchdog timer, set DIO channels 0-5 to operate in the
+ * standard DIO (vs. counter overflow) mode, disable the battery
+ * charger, and reset the watchdog interval selector to zero.
+ */
+ s626_write_misc2(dev, (s626_debi_read(dev, S626_LP_RDMISC2) &
+ S626_MISC2_BATT_ENABLE));
+
+ /* Initialize the digital I/O subsystem */
+ s626_dio_init(dev);
+
+ return 0;
+}
+
+static int s626_auto_attach(struct comedi_device *dev,
+ unsigned long context_unused)
+{
+ struct pci_dev *pcidev = comedi_to_pci_dev(dev);
+ struct s626_private *devpriv;
+ struct comedi_subdevice *s;
+ int ret;
+
+ devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
+ if (!devpriv)
+ return -ENOMEM;
+
+ ret = comedi_pci_enable(dev);
+ if (ret)
+ return ret;
+
+ dev->mmio = pci_ioremap_bar(pcidev, 0);
+ if (!dev->mmio)
+ return -ENOMEM;
+
+ /* disable master interrupt */
+ writel(0, dev->mmio + S626_P_IER);
+
+ /* soft reset */
+ writel(S626_MC1_SOFT_RESET, dev->mmio + S626_P_MC1);
+
+ /* DMA FIXME DMA// */
+
+ ret = s626_allocate_dma_buffers(dev);
+ if (ret)
+ return ret;
+
+ if (pcidev->irq) {
+ ret = request_irq(pcidev->irq, s626_irq_handler, IRQF_SHARED,
+ dev->board_name, dev);
+
+ if (ret == 0)
+ dev->irq = pcidev->irq;
+ }
+
+ ret = comedi_alloc_subdevices(dev, 6);
+ if (ret)
+ return ret;
+
+ s = &dev->subdevices[0];
+ /* analog input subdevice */
+ s->type = COMEDI_SUBD_AI;
+ s->subdev_flags = SDF_READABLE | SDF_DIFF;
+ s->n_chan = S626_ADC_CHANNELS;
+ s->maxdata = 0x3fff;
+ s->range_table = &s626_range_table;
+ s->len_chanlist = S626_ADC_CHANNELS;
+ s->insn_read = s626_ai_insn_read;
+ if (dev->irq) {
+ dev->read_subdev = s;
+ s->subdev_flags |= SDF_CMD_READ;
+ s->do_cmd = s626_ai_cmd;
+ s->do_cmdtest = s626_ai_cmdtest;
+ s->cancel = s626_ai_cancel;
+ }
+
+ s = &dev->subdevices[1];
+ /* analog output subdevice */
+ s->type = COMEDI_SUBD_AO;
+ s->subdev_flags = SDF_WRITABLE | SDF_READABLE;
+ s->n_chan = S626_DAC_CHANNELS;
+ s->maxdata = 0x3fff;
+ s->range_table = &range_bipolar10;
+ s->insn_write = s626_ao_insn_write;
+
+ ret = comedi_alloc_subdev_readback(s);
+ if (ret)
+ return ret;
+
+ s = &dev->subdevices[2];
+ /* digital I/O subdevice */
+ s->type = COMEDI_SUBD_DIO;
+ s->subdev_flags = SDF_WRITABLE | SDF_READABLE;
+ s->n_chan = 16;
+ s->maxdata = 1;
+ s->io_bits = 0xffff;
+ s->private = (void *)0; /* DIO group 0 */
+ s->range_table = &range_digital;
+ s->insn_config = s626_dio_insn_config;
+ s->insn_bits = s626_dio_insn_bits;
+
+ s = &dev->subdevices[3];
+ /* digital I/O subdevice */
+ s->type = COMEDI_SUBD_DIO;
+ s->subdev_flags = SDF_WRITABLE | SDF_READABLE;
+ s->n_chan = 16;
+ s->maxdata = 1;
+ s->io_bits = 0xffff;
+ s->private = (void *)1; /* DIO group 1 */
+ s->range_table = &range_digital;
+ s->insn_config = s626_dio_insn_config;
+ s->insn_bits = s626_dio_insn_bits;
+
+ s = &dev->subdevices[4];
+ /* digital I/O subdevice */
+ s->type = COMEDI_SUBD_DIO;
+ s->subdev_flags = SDF_WRITABLE | SDF_READABLE;
+ s->n_chan = 16;
+ s->maxdata = 1;
+ s->io_bits = 0xffff;
+ s->private = (void *)2; /* DIO group 2 */
+ s->range_table = &range_digital;
+ s->insn_config = s626_dio_insn_config;
+ s->insn_bits = s626_dio_insn_bits;
+
+ s = &dev->subdevices[5];
+ /* encoder (counter) subdevice */
+ s->type = COMEDI_SUBD_COUNTER;
+ s->subdev_flags = SDF_WRITABLE | SDF_READABLE | SDF_LSAMPL;
+ s->n_chan = S626_ENCODER_CHANNELS;
+ s->maxdata = 0xffffff;
+ s->range_table = &range_unknown;
+ s->insn_config = s626_enc_insn_config;
+ s->insn_read = s626_enc_insn_read;
+ s->insn_write = s626_enc_insn_write;
+
+ ret = s626_initialize(dev);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static void s626_detach(struct comedi_device *dev)
+{
+ struct s626_private *devpriv = dev->private;
+
+ if (devpriv) {
+ /* stop ai_command */
+ devpriv->ai_cmd_running = 0;
+
+ if (dev->mmio) {
+ /* interrupt mask */
+ /* Disable master interrupt */
+ writel(0, dev->mmio + S626_P_IER);
+ /* Clear board's IRQ status flag */
+ writel(S626_IRQ_GPIO3 | S626_IRQ_RPS1,
+ dev->mmio + S626_P_ISR);
+
+ /* Disable the watchdog timer and battery charger. */
+ s626_write_misc2(dev, 0);
+
+ /* Close all interfaces on 7146 device */
+ writel(S626_MC1_SHUTDOWN, dev->mmio + S626_P_MC1);
+ writel(S626_ACON1_BASE, dev->mmio + S626_P_ACON1);
+ }
+ }
+ comedi_pci_detach(dev);
+ s626_free_dma_buffers(dev);
+}
+
+static struct comedi_driver s626_driver = {
+ .driver_name = "s626",
+ .module = THIS_MODULE,
+ .auto_attach = s626_auto_attach,
+ .detach = s626_detach,
+};
+
+static int s626_pci_probe(struct pci_dev *dev,
+ const struct pci_device_id *id)
+{
+ return comedi_pci_auto_config(dev, &s626_driver, id->driver_data);
+}
+
+/*
+ * For devices with vendor:device id == 0x1131:0x7146 you must specify
+ * also subvendor:subdevice ids, because otherwise it will conflict with
+ * Philips SAA7146 media/dvb based cards.
+ */
+static const struct pci_device_id s626_pci_table[] = {
+ { PCI_DEVICE_SUB(PCI_VENDOR_ID_PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7146,
+ 0x6000, 0x0272) },
+ { 0 }
+};
+MODULE_DEVICE_TABLE(pci, s626_pci_table);
+
+static struct pci_driver s626_pci_driver = {
+ .name = "s626",
+ .id_table = s626_pci_table,
+ .probe = s626_pci_probe,
+ .remove = comedi_pci_auto_unconfig,
+};
+module_comedi_pci_driver(s626_driver, s626_pci_driver);
+
+MODULE_AUTHOR("Gianluca Palli <gpalli@deis.unibo.it>");
+MODULE_DESCRIPTION("Sensoray 626 Comedi driver module");
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/comedi/drivers/s626.h b/drivers/staging/comedi/drivers/s626.h
new file mode 100644
index 000000000..b83424e75
--- /dev/null
+++ b/drivers/staging/comedi/drivers/s626.h
@@ -0,0 +1,760 @@
+/*
+ * comedi/drivers/s626.h
+ * Sensoray s626 Comedi driver, header file
+ *
+ * COMEDI - Linux Control and Measurement Device Interface
+ * Copyright (C) 2000 David A. Schleef <ds@schleef.org>
+ *
+ * Based on Sensoray Model 626 Linux driver Version 0.2
+ * Copyright (C) 2002-2004 Sensoray Co., 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 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.
+ */
+
+#ifndef S626_H_INCLUDED
+#define S626_H_INCLUDED
+
+#define S626_DMABUF_SIZE 4096 /* 4k pages */
+
+#define S626_ADC_CHANNELS 16
+#define S626_DAC_CHANNELS 4
+#define S626_ENCODER_CHANNELS 6
+#define S626_DIO_CHANNELS 48
+#define S626_DIO_BANKS 3 /* Number of DIO groups. */
+#define S626_DIO_EXTCHANS 40 /* Number of extended-capability
+ * DIO channels. */
+
+#define S626_NUM_TRIMDACS 12 /* Number of valid TrimDAC channels. */
+
+/* PCI bus interface types. */
+#define S626_INTEL 1 /* Intel bus type. */
+#define S626_MOTOROLA 2 /* Motorola bus type. */
+
+#define S626_PLATFORM S626_INTEL /* *** SELECT PLATFORM TYPE *** */
+
+#define S626_RANGE_5V 0x10 /* +/-5V range */
+#define S626_RANGE_10V 0x00 /* +/-10V range */
+
+#define S626_EOPL 0x80 /* End of ADC poll list marker. */
+#define S626_GSEL_BIPOLAR5V 0x00F0 /* S626_LP_GSEL setting 5V bipolar. */
+#define S626_GSEL_BIPOLAR10V 0x00A0 /* S626_LP_GSEL setting 10V bipolar. */
+
+/* Error codes that must be visible to this base class. */
+#define S626_ERR_ILLEGAL_PARM 0x00010000 /* Illegal function parameter
+ * value was specified. */
+#define S626_ERR_I2C 0x00020000 /* I2C error. */
+#define S626_ERR_COUNTERSETUP 0x00200000 /* Illegal setup specified for
+ * counter channel. */
+#define S626_ERR_DEBI_TIMEOUT 0x00400000 /* DEBI transfer timed out. */
+
+/*
+ * Organization (physical order) and size (in DWORDs) of logical DMA buffers
+ * contained by ANA_DMABUF.
+ */
+#define S626_ADC_DMABUF_DWORDS 40 /* ADC DMA buffer must hold 16 samples,
+ * plus pre/post garbage samples. */
+#define S626_DAC_WDMABUF_DWORDS 1 /* DAC output DMA buffer holds a single
+ * sample. */
+
+/* All remaining space in 4KB DMA buffer is available for the RPS1 program. */
+
+/* Address offsets, in DWORDS, from base of DMA buffer. */
+#define S626_DAC_WDMABUF_OS S626_ADC_DMABUF_DWORDS
+
+/* Interrupt enable bit in ISR and IER. */
+#define S626_IRQ_GPIO3 0x00000040 /* IRQ enable for GPIO3. */
+#define S626_IRQ_RPS1 0x10000000
+#define S626_ISR_AFOU 0x00000800
+/* Audio fifo under/overflow detected. */
+
+#define S626_IRQ_COINT1A 0x0400 /* counter 1A overflow interrupt mask */
+#define S626_IRQ_COINT1B 0x0800 /* counter 1B overflow interrupt mask */
+#define S626_IRQ_COINT2A 0x1000 /* counter 2A overflow interrupt mask */
+#define S626_IRQ_COINT2B 0x2000 /* counter 2B overflow interrupt mask */
+#define S626_IRQ_COINT3A 0x4000 /* counter 3A overflow interrupt mask */
+#define S626_IRQ_COINT3B 0x8000 /* counter 3B overflow interrupt mask */
+
+/* RPS command codes. */
+#define S626_RPS_CLRSIGNAL 0x00000000 /* CLEAR SIGNAL */
+#define S626_RPS_SETSIGNAL 0x10000000 /* SET SIGNAL */
+#define S626_RPS_NOP 0x00000000 /* NOP */
+#define S626_RPS_PAUSE 0x20000000 /* PAUSE */
+#define S626_RPS_UPLOAD 0x40000000 /* UPLOAD */
+#define S626_RPS_JUMP 0x80000000 /* JUMP */
+#define S626_RPS_LDREG 0x90000100 /* LDREG (1 uint32_t only) */
+#define S626_RPS_STREG 0xA0000100 /* STREG (1 uint32_t only) */
+#define S626_RPS_STOP 0x50000000 /* STOP */
+#define S626_RPS_IRQ 0x60000000 /* IRQ */
+
+#define S626_RPS_LOGICAL_OR 0x08000000 /* Logical OR conditionals. */
+#define S626_RPS_INVERT 0x04000000 /* Test for negated
+ * semaphores. */
+#define S626_RPS_DEBI 0x00000002 /* DEBI done */
+
+#define S626_RPS_SIG0 0x00200000 /* RPS semaphore 0
+ * (used by ADC). */
+#define S626_RPS_SIG1 0x00400000 /* RPS semaphore 1
+ * (used by DAC). */
+#define S626_RPS_SIG2 0x00800000 /* RPS semaphore 2
+ * (not used). */
+#define S626_RPS_GPIO2 0x00080000 /* RPS GPIO2 */
+#define S626_RPS_GPIO3 0x00100000 /* RPS GPIO3 */
+
+#define S626_RPS_SIGADC S626_RPS_SIG0 /* Trigger/status for
+ * ADC's RPS program. */
+#define S626_RPS_SIGDAC S626_RPS_SIG1 /* Trigger/status for
+ * DAC's RPS program. */
+
+/* RPS clock parameters. */
+#define S626_RPSCLK_SCALAR 8 /* This is apparent ratio of
+ * PCI/RPS clks (undocumented!!). */
+#define S626_RPSCLK_PER_US (33 / S626_RPSCLK_SCALAR)
+ /* Number of RPS clocks in one
+ * microsecond. */
+
+/* Event counter source addresses. */
+#define S626_SBA_RPS_A0 0x27 /* Time of RPS0 busy, in PCI clocks. */
+
+/* GPIO constants. */
+#define S626_GPIO_BASE 0x10004000 /* GPIO 0,2,3 = inputs,
+ * GPIO3 = IRQ; GPIO1 = out. */
+#define S626_GPIO1_LO 0x00000000 /* GPIO1 set to LOW. */
+#define S626_GPIO1_HI 0x00001000 /* GPIO1 set to HIGH. */
+
+/* Primary Status Register (PSR) constants. */
+#define S626_PSR_DEBI_E 0x00040000 /* DEBI event flag. */
+#define S626_PSR_DEBI_S 0x00080000 /* DEBI status flag. */
+#define S626_PSR_A2_IN 0x00008000 /* Audio output DMA2 protection
+ * address reached. */
+#define S626_PSR_AFOU 0x00000800 /* Audio FIFO under/overflow
+ * detected. */
+#define S626_PSR_GPIO2 0x00000020 /* GPIO2 input pin: 0=AdcBusy,
+ * 1=AdcIdle. */
+#define S626_PSR_EC0S 0x00000001 /* Event counter 0 threshold
+ * reached. */
+
+/* Secondary Status Register (SSR) constants. */
+#define S626_SSR_AF2_OUT 0x00000200 /* Audio 2 output FIFO
+ * under/overflow detected. */
+
+/* Master Control Register 1 (MC1) constants. */
+#define S626_MC1_SOFT_RESET 0x80000000 /* Invoke 7146 soft reset. */
+#define S626_MC1_SHUTDOWN 0x3FFF0000 /* Shut down all MC1-controlled
+ * enables. */
+
+#define S626_MC1_ERPS1 0x2000 /* Enab/disable RPS task 1. */
+#define S626_MC1_ERPS0 0x1000 /* Enab/disable RPS task 0. */
+#define S626_MC1_DEBI 0x0800 /* Enab/disable DEBI pins. */
+#define S626_MC1_AUDIO 0x0200 /* Enab/disable audio port pins. */
+#define S626_MC1_I2C 0x0100 /* Enab/disable I2C interface. */
+#define S626_MC1_A2OUT 0x0008 /* Enab/disable transfer on A2 out. */
+#define S626_MC1_A2IN 0x0004 /* Enab/disable transfer on A2 in. */
+#define S626_MC1_A1IN 0x0001 /* Enab/disable transfer on A1 in. */
+
+/* Master Control Register 2 (MC2) constants. */
+#define S626_MC2_UPLD_DEBI 0x0002 /* Upload DEBI. */
+#define S626_MC2_UPLD_IIC 0x0001 /* Upload I2C. */
+#define S626_MC2_RPSSIG2 0x2000 /* RPS signal 2 (not used). */
+#define S626_MC2_RPSSIG1 0x1000 /* RPS signal 1 (DAC RPS busy). */
+#define S626_MC2_RPSSIG0 0x0800 /* RPS signal 0 (ADC RPS busy). */
+
+#define S626_MC2_ADC_RPS S626_MC2_RPSSIG0 /* ADC RPS busy. */
+#define S626_MC2_DAC_RPS S626_MC2_RPSSIG1 /* DAC RPS busy. */
+
+/* PCI BUS (SAA7146) REGISTER ADDRESS OFFSETS */
+#define S626_P_PCI_BT_A 0x004C /* Audio DMA burst/threshold control. */
+#define S626_P_DEBICFG 0x007C /* DEBI configuration. */
+#define S626_P_DEBICMD 0x0080 /* DEBI command. */
+#define S626_P_DEBIPAGE 0x0084 /* DEBI page. */
+#define S626_P_DEBIAD 0x0088 /* DEBI target address. */
+#define S626_P_I2CCTRL 0x008C /* I2C control. */
+#define S626_P_I2CSTAT 0x0090 /* I2C status. */
+#define S626_P_BASEA2_IN 0x00AC /* Audio input 2 base physical DMAbuf
+ * address. */
+#define S626_P_PROTA2_IN 0x00B0 /* Audio input 2 physical DMAbuf
+ * protection address. */
+#define S626_P_PAGEA2_IN 0x00B4 /* Audio input 2 paging attributes. */
+#define S626_P_BASEA2_OUT 0x00B8 /* Audio output 2 base physical DMAbuf
+ * address. */
+#define S626_P_PROTA2_OUT 0x00BC /* Audio output 2 physical DMAbuf
+ * protection address. */
+#define S626_P_PAGEA2_OUT 0x00C0 /* Audio output 2 paging attributes. */
+#define S626_P_RPSPAGE0 0x00C4 /* RPS0 page. */
+#define S626_P_RPSPAGE1 0x00C8 /* RPS1 page. */
+#define S626_P_RPS0_TOUT 0x00D4 /* RPS0 time-out. */
+#define S626_P_RPS1_TOUT 0x00D8 /* RPS1 time-out. */
+#define S626_P_IER 0x00DC /* Interrupt enable. */
+#define S626_P_GPIO 0x00E0 /* General-purpose I/O. */
+#define S626_P_EC1SSR 0x00E4 /* Event counter set 1 source select. */
+#define S626_P_ECT1R 0x00EC /* Event counter threshold set 1. */
+#define S626_P_ACON1 0x00F4 /* Audio control 1. */
+#define S626_P_ACON2 0x00F8 /* Audio control 2. */
+#define S626_P_MC1 0x00FC /* Master control 1. */
+#define S626_P_MC2 0x0100 /* Master control 2. */
+#define S626_P_RPSADDR0 0x0104 /* RPS0 instruction pointer. */
+#define S626_P_RPSADDR1 0x0108 /* RPS1 instruction pointer. */
+#define S626_P_ISR 0x010C /* Interrupt status. */
+#define S626_P_PSR 0x0110 /* Primary status. */
+#define S626_P_SSR 0x0114 /* Secondary status. */
+#define S626_P_EC1R 0x0118 /* Event counter set 1. */
+#define S626_P_ADP4 0x0138 /* Logical audio DMA pointer of audio
+ * input FIFO A2_IN. */
+#define S626_P_FB_BUFFER1 0x0144 /* Audio feedback buffer 1. */
+#define S626_P_FB_BUFFER2 0x0148 /* Audio feedback buffer 2. */
+#define S626_P_TSL1 0x0180 /* Audio time slot list 1. */
+#define S626_P_TSL2 0x01C0 /* Audio time slot list 2. */
+
+/* LOCAL BUS (GATE ARRAY) REGISTER ADDRESS OFFSETS */
+/* Analog I/O registers: */
+#define S626_LP_DACPOL 0x0082 /* Write DAC polarity. */
+#define S626_LP_GSEL 0x0084 /* Write ADC gain. */
+#define S626_LP_ISEL 0x0086 /* Write ADC channel select. */
+
+/* Digital I/O registers */
+#define S626_LP_RDDIN(x) (0x0040 + (x) * 0x10) /* R: digital input */
+#define S626_LP_WRINTSEL(x) (0x0042 + (x) * 0x10) /* W: int enable */
+#define S626_LP_WREDGSEL(x) (0x0044 + (x) * 0x10) /* W: edge selection */
+#define S626_LP_WRCAPSEL(x) (0x0046 + (x) * 0x10) /* W: capture enable */
+#define S626_LP_RDCAPFLG(x) (0x0048 + (x) * 0x10) /* R: edges captured */
+#define S626_LP_WRDOUT(x) (0x0048 + (x) * 0x10) /* W: digital output */
+#define S626_LP_RDINTSEL(x) (0x004a + (x) * 0x10) /* R: int enable */
+#define S626_LP_RDEDGSEL(x) (0x004c + (x) * 0x10) /* R: edge selection */
+#define S626_LP_RDCAPSEL(x) (0x004e + (x) * 0x10) /* R: capture enable */
+
+/* Counter registers (read/write): 0A 1A 2A 0B 1B 2B */
+#define S626_LP_CRA(x) (0x0000 + (((x) % 3) * 0x4))
+#define S626_LP_CRB(x) (0x0002 + (((x) % 3) * 0x4))
+
+/* Counter PreLoad (write) and Latch (read) Registers: 0A 1A 2A 0B 1B 2B */
+#define S626_LP_CNTR(x) (0x000c + (((x) < 3) ? 0x0 : 0x4) + \
+ (((x) % 3) * 0x8))
+
+/* Miscellaneous Registers (read/write): */
+#define S626_LP_MISC1 0x0088 /* Read/write Misc1. */
+#define S626_LP_WRMISC2 0x0090 /* Write Misc2. */
+#define S626_LP_RDMISC2 0x0082 /* Read Misc2. */
+
+/* Bit masks for MISC1 register that are the same for reads and writes. */
+#define S626_MISC1_WENABLE 0x8000 /* enab writes to MISC2 (except Clear
+ * Watchdog bit). */
+#define S626_MISC1_WDISABLE 0x0000 /* Disable writes to MISC2. */
+#define S626_MISC1_EDCAP 0x1000 /* Enable edge capture on DIO chans
+ * specified by S626_LP_WRCAPSELx. */
+#define S626_MISC1_NOEDCAP 0x0000 /* Disable edge capture on specified
+ * DIO chans. */
+
+/* Bit masks for MISC1 register reads. */
+#define S626_RDMISC1_WDTIMEOUT 0x4000 /* Watchdog timer timed out. */
+
+/* Bit masks for MISC2 register writes. */
+#define S626_WRMISC2_WDCLEAR 0x8000 /* Reset watchdog timer to zero. */
+#define S626_WRMISC2_CHARGE_ENABLE 0x4000 /* Enable battery trickle charging. */
+
+/* Bit masks for MISC2 register that are the same for reads and writes. */
+#define S626_MISC2_BATT_ENABLE 0x0008 /* Backup battery enable. */
+#define S626_MISC2_WDENABLE 0x0004 /* Watchdog timer enable. */
+#define S626_MISC2_WDPERIOD_MASK 0x0003 /* Watchdog interval select mask. */
+
+/* Bit masks for ACON1 register. */
+#define S626_A2_RUN 0x40000000 /* Run A2 based on TSL2. */
+#define S626_A1_RUN 0x20000000 /* Run A1 based on TSL1. */
+#define S626_A1_SWAP 0x00200000 /* Use big-endian for A1. */
+#define S626_A2_SWAP 0x00100000 /* Use big-endian for A2. */
+#define S626_WS_MODES 0x00019999 /* WS0 = TSL1 trigger input,
+ * WS1-WS4 = CS* outputs. */
+
+#if S626_PLATFORM == S626_INTEL /* Base ACON1 config: always run
+ * A1 based on TSL1. */
+#define S626_ACON1_BASE (S626_WS_MODES | S626_A1_RUN)
+#elif S626_PLATFORM == S626_MOTOROLA
+#define S626_ACON1_BASE \
+ (S626_WS_MODES | S626_A1_RUN | S626_A1_SWAP | S626_A2_SWAP)
+#endif
+
+#define S626_ACON1_ADCSTART S626_ACON1_BASE /* Start ADC: run A1
+ * based on TSL1. */
+#define S626_ACON1_DACSTART (S626_ACON1_BASE | S626_A2_RUN)
+/* Start transmit to DAC: run A2 based on TSL2. */
+#define S626_ACON1_DACSTOP S626_ACON1_BASE /* Halt A2. */
+
+/* Bit masks for ACON2 register. */
+#define S626_A1_CLKSRC_BCLK1 0x00000000 /* A1 bit rate = BCLK1 (ADC). */
+#define S626_A2_CLKSRC_X1 0x00800000 /* A2 bit rate = ACLK/1
+ * (DACs). */
+#define S626_A2_CLKSRC_X2 0x00C00000 /* A2 bit rate = ACLK/2
+ * (DACs). */
+#define S626_A2_CLKSRC_X4 0x01400000 /* A2 bit rate = ACLK/4
+ * (DACs). */
+#define S626_INVERT_BCLK2 0x00100000 /* Invert BCLK2 (DACs). */
+#define S626_BCLK2_OE 0x00040000 /* Enable BCLK2 (DACs). */
+#define S626_ACON2_XORMASK 0x000C0000 /* XOR mask for ACON2
+ * active-low bits. */
+
+#define S626_ACON2_INIT (S626_ACON2_XORMASK ^ \
+ (S626_A1_CLKSRC_BCLK1 | S626_A2_CLKSRC_X2 | \
+ S626_INVERT_BCLK2 | S626_BCLK2_OE))
+
+/* Bit masks for timeslot records. */
+#define S626_WS1 0x40000000 /* WS output to assert. */
+#define S626_WS2 0x20000000
+#define S626_WS3 0x10000000
+#define S626_WS4 0x08000000
+#define S626_RSD1 0x01000000 /* Shift A1 data in on SD1. */
+#define S626_SDW_A1 0x00800000 /* Store rcv'd char at next char
+ * slot of DWORD1 buffer. */
+#define S626_SIB_A1 0x00400000 /* Store rcv'd char at next
+ * char slot of FB1 buffer. */
+#define S626_SF_A1 0x00200000 /* Write unsigned long
+ * buffer to input FIFO. */
+
+/* Select parallel-to-serial converter's data source: */
+#define S626_XFIFO_0 0x00000000 /* Data fifo byte 0. */
+#define S626_XFIFO_1 0x00000010 /* Data fifo byte 1. */
+#define S626_XFIFO_2 0x00000020 /* Data fifo byte 2. */
+#define S626_XFIFO_3 0x00000030 /* Data fifo byte 3. */
+#define S626_XFB0 0x00000040 /* FB_BUFFER byte 0. */
+#define S626_XFB1 0x00000050 /* FB_BUFFER byte 1. */
+#define S626_XFB2 0x00000060 /* FB_BUFFER byte 2. */
+#define S626_XFB3 0x00000070 /* FB_BUFFER byte 3. */
+#define S626_SIB_A2 0x00000200 /* Store next dword from A2's
+ * input shifter to FB2
+ * buffer. */
+#define S626_SF_A2 0x00000100 /* Store next dword from A2's
+ * input shifter to its input
+ * fifo. */
+#define S626_LF_A2 0x00000080 /* Load next dword from A2's
+ * output fifo into its
+ * output dword buffer. */
+#define S626_XSD2 0x00000008 /* Shift data out on SD2. */
+#define S626_RSD3 0x00001800 /* Shift data in on SD3. */
+#define S626_RSD2 0x00001000 /* Shift data in on SD2. */
+#define S626_LOW_A2 0x00000002 /* Drive last SD low for 7 clks,
+ * then tri-state. */
+#define S626_EOS 0x00000001 /* End of superframe. */
+
+/* I2C configuration constants. */
+#define S626_I2C_CLKSEL 0x0400 /* I2C bit rate =
+ * PCIclk/480 = 68.75 KHz. */
+#define S626_I2C_BITRATE 68.75 /* I2C bus data bit rate
+ * (determined by
+ * S626_I2C_CLKSEL) in KHz. */
+#define S626_I2C_WRTIME 15.0 /* Worst case time, in msec,
+ * for EEPROM internal write
+ * op. */
+
+/* I2C manifest constants. */
+
+/* Max retries to wait for EEPROM write. */
+#define S626_I2C_RETRIES (S626_I2C_WRTIME * S626_I2C_BITRATE / 9.0)
+#define S626_I2C_ERR 0x0002 /* I2C control/status flag ERROR. */
+#define S626_I2C_BUSY 0x0001 /* I2C control/status flag BUSY. */
+#define S626_I2C_ABORT 0x0080 /* I2C status flag ABORT. */
+#define S626_I2C_ATTRSTART 0x3 /* I2C attribute START. */
+#define S626_I2C_ATTRCONT 0x2 /* I2C attribute CONT. */
+#define S626_I2C_ATTRSTOP 0x1 /* I2C attribute STOP. */
+#define S626_I2C_ATTRNOP 0x0 /* I2C attribute NOP. */
+
+/* Code macros used for constructing I2C command bytes. */
+#define S626_I2C_B2(ATTR, VAL) (((ATTR) << 6) | ((VAL) << 24))
+#define S626_I2C_B1(ATTR, VAL) (((ATTR) << 4) | ((VAL) << 16))
+#define S626_I2C_B0(ATTR, VAL) (((ATTR) << 2) | ((VAL) << 8))
+
+/* DEBI command constants. */
+#define S626_DEBI_CMD_SIZE16 (2 << 17) /* Transfer size is always
+ * 2 bytes. */
+#define S626_DEBI_CMD_READ 0x00010000 /* Read operation. */
+#define S626_DEBI_CMD_WRITE 0x00000000 /* Write operation. */
+
+/* Read immediate 2 bytes. */
+#define S626_DEBI_CMD_RDWORD (S626_DEBI_CMD_READ | S626_DEBI_CMD_SIZE16)
+
+/* Write immediate 2 bytes. */
+#define S626_DEBI_CMD_WRWORD (S626_DEBI_CMD_WRITE | S626_DEBI_CMD_SIZE16)
+
+/* DEBI configuration constants. */
+#define S626_DEBI_CFG_XIRQ_EN 0x80000000 /* Enable external interrupt
+ * on GPIO3. */
+#define S626_DEBI_CFG_XRESUME 0x40000000 /* Resume block */
+ /* Transfer when XIRQ
+ * deasserted. */
+#define S626_DEBI_CFG_TOQ 0x03C00000 /* Timeout (15 PCI cycles). */
+#define S626_DEBI_CFG_FAST 0x10000000 /* Fast mode enable. */
+
+/* 4-bit field that specifies DEBI timeout value in PCI clock cycles: */
+#define S626_DEBI_CFG_TOUT_BIT 22 /* Finish DEBI cycle after this many
+ * clocks. */
+
+/* 2-bit field that specifies Endian byte lane steering: */
+#define S626_DEBI_CFG_SWAP_NONE 0x00000000 /* Straight - don't swap any
+ * bytes (Intel). */
+#define S626_DEBI_CFG_SWAP_2 0x00100000 /* 2-byte swap (Motorola). */
+#define S626_DEBI_CFG_SWAP_4 0x00200000 /* 4-byte swap. */
+#define S626_DEBI_CFG_SLAVE16 0x00080000 /* Slave is able to serve
+ * 16-bit cycles. */
+#define S626_DEBI_CFG_INC 0x00040000 /* Enable address increment
+ * for block transfers. */
+#define S626_DEBI_CFG_INTEL 0x00020000 /* Intel style local bus. */
+#define S626_DEBI_CFG_TIMEROFF 0x00010000 /* Disable timer. */
+
+#if S626_PLATFORM == S626_INTEL
+
+#define S626_DEBI_TOUT 7 /* Wait 7 PCI clocks (212 ns) before
+ * polling RDY. */
+
+/* Intel byte lane steering (pass through all byte lanes). */
+#define S626_DEBI_SWAP S626_DEBI_CFG_SWAP_NONE
+
+#elif S626_PLATFORM == S626_MOTOROLA
+
+#define S626_DEBI_TOUT 15 /* Wait 15 PCI clocks (454 ns) maximum
+ * before timing out. */
+
+/* Motorola byte lane steering. */
+#define S626_DEBI_SWAP S626_DEBI_CFG_SWAP_2
+
+#endif
+
+/* DEBI page table constants. */
+#define S626_DEBI_PAGE_DISABLE 0x00000000 /* Paging disable. */
+
+/* ******* EXTRA FROM OTHER SENSORAY * .h ******* */
+
+/* LoadSrc values: */
+#define S626_LOADSRC_INDX 0 /* Preload core in response to Index. */
+#define S626_LOADSRC_OVER 1 /* Preload core in response to
+ * Overflow. */
+#define S626_LOADSRCB_OVERA 2 /* Preload B core in response to
+ * A Overflow. */
+#define S626_LOADSRC_NONE 3 /* Never preload core. */
+
+/* IntSrc values: */
+#define S626_INTSRC_NONE 0 /* Interrupts disabled. */
+#define S626_INTSRC_OVER 1 /* Interrupt on Overflow. */
+#define S626_INTSRC_INDX 2 /* Interrupt on Index. */
+#define S626_INTSRC_BOTH 3 /* Interrupt on Index or Overflow. */
+
+/* LatchSrc values: */
+#define S626_LATCHSRC_AB_READ 0 /* Latch on read. */
+#define S626_LATCHSRC_A_INDXA 1 /* Latch A on A Index. */
+#define S626_LATCHSRC_B_INDXB 2 /* Latch B on B Index. */
+#define S626_LATCHSRC_B_OVERA 3 /* Latch B on A Overflow. */
+
+/* IndxSrc values: */
+#define S626_INDXSRC_ENCODER 0 /* Encoder. */
+#define S626_INDXSRC_DIGIN 1 /* Digital inputs. */
+#define S626_INDXSRC_SOFT 2 /* S/w controlled by IndxPol bit. */
+#define S626_INDXSRC_DISABLED 3 /* Index disabled. */
+
+/* IndxPol values: */
+#define S626_INDXPOL_POS 0 /* Index input is active high. */
+#define S626_INDXPOL_NEG 1 /* Index input is active low. */
+
+/* Logical encoder mode values: */
+#define S626_ENCMODE_COUNTER 0 /* Counter mode. */
+#define S626_ENCMODE_TIMER 2 /* Timer mode. */
+#define S626_ENCMODE_EXTENDER 3 /* Extender mode. */
+
+/* Physical CntSrc values (for Counter A source and Counter B source): */
+#define S626_CNTSRC_ENCODER 0 /* Encoder */
+#define S626_CNTSRC_DIGIN 1 /* Digital inputs */
+#define S626_CNTSRC_SYSCLK 2 /* System clock up */
+#define S626_CNTSRC_SYSCLK_DOWN 3 /* System clock down */
+
+/* ClkPol values: */
+#define S626_CLKPOL_POS 0 /* Counter/Extender clock is
+ * active high. */
+#define S626_CLKPOL_NEG 1 /* Counter/Extender clock is
+ * active low. */
+#define S626_CNTDIR_UP 0 /* Timer counts up. */
+#define S626_CNTDIR_DOWN 1 /* Timer counts down. */
+
+/* ClkEnab values: */
+#define S626_CLKENAB_ALWAYS 0 /* Clock always enabled. */
+#define S626_CLKENAB_INDEX 1 /* Clock is enabled by index. */
+
+/* ClkMult values: */
+#define S626_CLKMULT_4X 0 /* 4x clock multiplier. */
+#define S626_CLKMULT_2X 1 /* 2x clock multiplier. */
+#define S626_CLKMULT_1X 2 /* 1x clock multiplier. */
+#define S626_CLKMULT_SPECIAL 3 /* Special clock multiplier value. */
+
+/* Sanity-check limits for parameters. */
+
+#define S626_NUM_COUNTERS 6 /* Maximum valid counter
+ * logical channel number. */
+#define S626_NUM_INTSOURCES 4
+#define S626_NUM_LATCHSOURCES 4
+#define S626_NUM_CLKMULTS 4
+#define S626_NUM_CLKSOURCES 4
+#define S626_NUM_CLKPOLS 2
+#define S626_NUM_INDEXPOLS 2
+#define S626_NUM_INDEXSOURCES 2
+#define S626_NUM_LOADTRIGS 4
+
+/* General macros for manipulating bitfields: */
+#define S626_MAKE(x, w, p) (((x) & ((1 << (w)) - 1)) << (p))
+#define S626_UNMAKE(v, w, p) (((v) >> (p)) & ((1 << (w)) - 1))
+
+/* Bit field positions in CRA: */
+#define S626_CRABIT_INDXSRC_B 14 /* B index source. */
+#define S626_CRABIT_CNTSRC_B 12 /* B counter source. */
+#define S626_CRABIT_INDXPOL_A 11 /* A index polarity. */
+#define S626_CRABIT_LOADSRC_A 9 /* A preload trigger. */
+#define S626_CRABIT_CLKMULT_A 7 /* A clock multiplier. */
+#define S626_CRABIT_INTSRC_A 5 /* A interrupt source. */
+#define S626_CRABIT_CLKPOL_A 4 /* A clock polarity. */
+#define S626_CRABIT_INDXSRC_A 2 /* A index source. */
+#define S626_CRABIT_CNTSRC_A 0 /* A counter source. */
+
+/* Bit field widths in CRA: */
+#define S626_CRAWID_INDXSRC_B 2
+#define S626_CRAWID_CNTSRC_B 2
+#define S626_CRAWID_INDXPOL_A 1
+#define S626_CRAWID_LOADSRC_A 2
+#define S626_CRAWID_CLKMULT_A 2
+#define S626_CRAWID_INTSRC_A 2
+#define S626_CRAWID_CLKPOL_A 1
+#define S626_CRAWID_INDXSRC_A 2
+#define S626_CRAWID_CNTSRC_A 2
+
+/* Bit field masks for CRA: */
+#define S626_CRAMSK_INDXSRC_B S626_SET_CRA_INDXSRC_B(~0)
+#define S626_CRAMSK_CNTSRC_B S626_SET_CRA_CNTSRC_B(~0)
+#define S626_CRAMSK_INDXPOL_A S626_SET_CRA_INDXPOL_A(~0)
+#define S626_CRAMSK_LOADSRC_A S626_SET_CRA_LOADSRC_A(~0)
+#define S626_CRAMSK_CLKMULT_A S626_SET_CRA_CLKMULT_A(~0)
+#define S626_CRAMSK_INTSRC_A S626_SET_CRA_INTSRC_A(~0)
+#define S626_CRAMSK_CLKPOL_A S626_SET_CRA_CLKPOL_A(~0)
+#define S626_CRAMSK_INDXSRC_A S626_SET_CRA_INDXSRC_A(~0)
+#define S626_CRAMSK_CNTSRC_A S626_SET_CRA_CNTSRC_A(~0)
+
+/* Construct parts of the CRA value: */
+#define S626_SET_CRA_INDXSRC_B(x) \
+ S626_MAKE((x), S626_CRAWID_INDXSRC_B, S626_CRABIT_INDXSRC_B)
+#define S626_SET_CRA_CNTSRC_B(x) \
+ S626_MAKE((x), S626_CRAWID_CNTSRC_B, S626_CRABIT_CNTSRC_B)
+#define S626_SET_CRA_INDXPOL_A(x) \
+ S626_MAKE((x), S626_CRAWID_INDXPOL_A, S626_CRABIT_INDXPOL_A)
+#define S626_SET_CRA_LOADSRC_A(x) \
+ S626_MAKE((x), S626_CRAWID_LOADSRC_A, S626_CRABIT_LOADSRC_A)
+#define S626_SET_CRA_CLKMULT_A(x) \
+ S626_MAKE((x), S626_CRAWID_CLKMULT_A, S626_CRABIT_CLKMULT_A)
+#define S626_SET_CRA_INTSRC_A(x) \
+ S626_MAKE((x), S626_CRAWID_INTSRC_A, S626_CRABIT_INTSRC_A)
+#define S626_SET_CRA_CLKPOL_A(x) \
+ S626_MAKE((x), S626_CRAWID_CLKPOL_A, S626_CRABIT_CLKPOL_A)
+#define S626_SET_CRA_INDXSRC_A(x) \
+ S626_MAKE((x), S626_CRAWID_INDXSRC_A, S626_CRABIT_INDXSRC_A)
+#define S626_SET_CRA_CNTSRC_A(x) \
+ S626_MAKE((x), S626_CRAWID_CNTSRC_A, S626_CRABIT_CNTSRC_A)
+
+/* Extract parts of the CRA value: */
+#define S626_GET_CRA_INDXSRC_B(v) \
+ S626_UNMAKE((v), S626_CRAWID_INDXSRC_B, S626_CRABIT_INDXSRC_B)
+#define S626_GET_CRA_CNTSRC_B(v) \
+ S626_UNMAKE((v), S626_CRAWID_CNTSRC_B, S626_CRABIT_CNTSRC_B)
+#define S626_GET_CRA_INDXPOL_A(v) \
+ S626_UNMAKE((v), S626_CRAWID_INDXPOL_A, S626_CRABIT_INDXPOL_A)
+#define S626_GET_CRA_LOADSRC_A(v) \
+ S626_UNMAKE((v), S626_CRAWID_LOADSRC_A, S626_CRABIT_LOADSRC_A)
+#define S626_GET_CRA_CLKMULT_A(v) \
+ S626_UNMAKE((v), S626_CRAWID_CLKMULT_A, S626_CRABIT_CLKMULT_A)
+#define S626_GET_CRA_INTSRC_A(v) \
+ S626_UNMAKE((v), S626_CRAWID_INTSRC_A, S626_CRABIT_INTSRC_A)
+#define S626_GET_CRA_CLKPOL_A(v) \
+ S626_UNMAKE((v), S626_CRAWID_CLKPOL_A, S626_CRABIT_CLKPOL_A)
+#define S626_GET_CRA_INDXSRC_A(v) \
+ S626_UNMAKE((v), S626_CRAWID_INDXSRC_A, S626_CRABIT_INDXSRC_A)
+#define S626_GET_CRA_CNTSRC_A(v) \
+ S626_UNMAKE((v), S626_CRAWID_CNTSRC_A, S626_CRABIT_CNTSRC_A)
+
+/* Bit field positions in CRB: */
+#define S626_CRBBIT_INTRESETCMD 15 /* (w) Interrupt reset command. */
+#define S626_CRBBIT_CNTDIR_B 15 /* (r) B counter direction. */
+#define S626_CRBBIT_INTRESET_B 14 /* (w) B interrupt reset enable. */
+#define S626_CRBBIT_OVERDO_A 14 /* (r) A overflow routed to dig. out. */
+#define S626_CRBBIT_INTRESET_A 13 /* (w) A interrupt reset enable. */
+#define S626_CRBBIT_OVERDO_B 13 /* (r) B overflow routed to dig. out. */
+#define S626_CRBBIT_CLKENAB_A 12 /* A clock enable. */
+#define S626_CRBBIT_INTSRC_B 10 /* B interrupt source. */
+#define S626_CRBBIT_LATCHSRC 8 /* A/B latch source. */
+#define S626_CRBBIT_LOADSRC_B 6 /* B preload trigger. */
+#define S626_CRBBIT_CLEAR_B 7 /* B cleared when A overflows. */
+#define S626_CRBBIT_CLKMULT_B 3 /* B clock multiplier. */
+#define S626_CRBBIT_CLKENAB_B 2 /* B clock enable. */
+#define S626_CRBBIT_INDXPOL_B 1 /* B index polarity. */
+#define S626_CRBBIT_CLKPOL_B 0 /* B clock polarity. */
+
+/* Bit field widths in CRB: */
+#define S626_CRBWID_INTRESETCMD 1
+#define S626_CRBWID_CNTDIR_B 1
+#define S626_CRBWID_INTRESET_B 1
+#define S626_CRBWID_OVERDO_A 1
+#define S626_CRBWID_INTRESET_A 1
+#define S626_CRBWID_OVERDO_B 1
+#define S626_CRBWID_CLKENAB_A 1
+#define S626_CRBWID_INTSRC_B 2
+#define S626_CRBWID_LATCHSRC 2
+#define S626_CRBWID_LOADSRC_B 2
+#define S626_CRBWID_CLEAR_B 1
+#define S626_CRBWID_CLKMULT_B 2
+#define S626_CRBWID_CLKENAB_B 1
+#define S626_CRBWID_INDXPOL_B 1
+#define S626_CRBWID_CLKPOL_B 1
+
+/* Bit field masks for CRB: */
+#define S626_CRBMSK_INTRESETCMD S626_SET_CRB_INTRESETCMD(~0) /* (w) */
+#define S626_CRBMSK_CNTDIR_B S626_CRBMSK_INTRESETCMD /* (r) */
+#define S626_CRBMSK_INTRESET_B S626_SET_CRB_INTRESET_B(~0) /* (w) */
+#define S626_CRBMSK_OVERDO_A S626_CRBMSK_INTRESET_B /* (r) */
+#define S626_CRBMSK_INTRESET_A S626_SET_CRB_INTRESET_A(~0) /* (w) */
+#define S626_CRBMSK_OVERDO_B S626_CRBMSK_INTRESET_A /* (r) */
+#define S626_CRBMSK_CLKENAB_A S626_SET_CRB_CLKENAB_A(~0)
+#define S626_CRBMSK_INTSRC_B S626_SET_CRB_INTSRC_B(~0)
+#define S626_CRBMSK_LATCHSRC S626_SET_CRB_LATCHSRC(~0)
+#define S626_CRBMSK_LOADSRC_B S626_SET_CRB_LOADSRC_B(~0)
+#define S626_CRBMSK_CLEAR_B S626_SET_CRB_CLEAR_B(~0)
+#define S626_CRBMSK_CLKMULT_B S626_SET_CRB_CLKMULT_B(~0)
+#define S626_CRBMSK_CLKENAB_B S626_SET_CRB_CLKENAB_B(~0)
+#define S626_CRBMSK_INDXPOL_B S626_SET_CRB_INDXPOL_B(~0)
+#define S626_CRBMSK_CLKPOL_B S626_SET_CRB_CLKPOL_B(~0)
+
+/* Interrupt reset control bits. */
+#define S626_CRBMSK_INTCTRL (S626_CRBMSK_INTRESETCMD | \
+ S626_CRBMSK_INTRESET_A | \
+ S626_CRBMSK_INTRESET_B)
+
+/* Construct parts of the CRB value: */
+#define S626_SET_CRB_INTRESETCMD(x) \
+ S626_MAKE((x), S626_CRBWID_INTRESETCMD, S626_CRBBIT_INTRESETCMD)
+#define S626_SET_CRB_INTRESET_B(x) \
+ S626_MAKE((x), S626_CRBWID_INTRESET_B, S626_CRBBIT_INTRESET_B)
+#define S626_SET_CRB_INTRESET_A(x) \
+ S626_MAKE((x), S626_CRBWID_INTRESET_A, S626_CRBBIT_INTRESET_A)
+#define S626_SET_CRB_CLKENAB_A(x) \
+ S626_MAKE((x), S626_CRBWID_CLKENAB_A, S626_CRBBIT_CLKENAB_A)
+#define S626_SET_CRB_INTSRC_B(x) \
+ S626_MAKE((x), S626_CRBWID_INTSRC_B, S626_CRBBIT_INTSRC_B)
+#define S626_SET_CRB_LATCHSRC(x) \
+ S626_MAKE((x), S626_CRBWID_LATCHSRC, S626_CRBBIT_LATCHSRC)
+#define S626_SET_CRB_LOADSRC_B(x) \
+ S626_MAKE((x), S626_CRBWID_LOADSRC_B, S626_CRBBIT_LOADSRC_B)
+#define S626_SET_CRB_CLEAR_B(x) \
+ S626_MAKE((x), S626_CRBWID_CLEAR_B, S626_CRBBIT_CLEAR_B)
+#define S626_SET_CRB_CLKMULT_B(x) \
+ S626_MAKE((x), S626_CRBWID_CLKMULT_B, S626_CRBBIT_CLKMULT_B)
+#define S626_SET_CRB_CLKENAB_B(x) \
+ S626_MAKE((x), S626_CRBWID_CLKENAB_B, S626_CRBBIT_CLKENAB_B)
+#define S626_SET_CRB_INDXPOL_B(x) \
+ S626_MAKE((x), S626_CRBWID_INDXPOL_B, S626_CRBBIT_INDXPOL_B)
+#define S626_SET_CRB_CLKPOL_B(x) \
+ S626_MAKE((x), S626_CRBWID_CLKPOL_B, S626_CRBBIT_CLKPOL_B)
+
+/* Extract parts of the CRB value: */
+#define S626_GET_CRB_CNTDIR_B(v) \
+ S626_UNMAKE((v), S626_CRBWID_CNTDIR_B, S626_CRBBIT_CNTDIR_B)
+#define S626_GET_CRB_OVERDO_A(v) \
+ S626_UNMAKE((v), S626_CRBWID_OVERDO_A, S626_CRBBIT_OVERDO_A)
+#define S626_GET_CRB_OVERDO_B(v) \
+ S626_UNMAKE((v), S626_CRBWID_OVERDO_B, S626_CRBBIT_OVERDO_B)
+#define S626_GET_CRB_CLKENAB_A(v) \
+ S626_UNMAKE((v), S626_CRBWID_CLKENAB_A, S626_CRBBIT_CLKENAB_A)
+#define S626_GET_CRB_INTSRC_B(v) \
+ S626_UNMAKE((v), S626_CRBWID_INTSRC_B, S626_CRBBIT_INTSRC_B)
+#define S626_GET_CRB_LATCHSRC(v) \
+ S626_UNMAKE((v), S626_CRBWID_LATCHSRC, S626_CRBBIT_LATCHSRC)
+#define S626_GET_CRB_LOADSRC_B(v) \
+ S626_UNMAKE((v), S626_CRBWID_LOADSRC_B, S626_CRBBIT_LOADSRC_B)
+#define S626_GET_CRB_CLEAR_B(v) \
+ S626_UNMAKE((v), S626_CRBWID_CLEAR_B, S626_CRBBIT_CLEAR_B)
+#define S626_GET_CRB_CLKMULT_B(v) \
+ S626_UNMAKE((v), S626_CRBWID_CLKMULT_B, S626_CRBBIT_CLKMULT_B)
+#define S626_GET_CRB_CLKENAB_B(v) \
+ S626_UNMAKE((v), S626_CRBWID_CLKENAB_B, S626_CRBBIT_CLKENAB_B)
+#define S626_GET_CRB_INDXPOL_B(v) \
+ S626_UNMAKE((v), S626_CRBWID_INDXPOL_B, S626_CRBBIT_INDXPOL_B)
+#define S626_GET_CRB_CLKPOL_B(v) \
+ S626_UNMAKE((v), S626_CRBWID_CLKPOL_B, S626_CRBBIT_CLKPOL_B)
+
+/* Bit field positions for standardized SETUP structure: */
+#define S626_STDBIT_INTSRC 13
+#define S626_STDBIT_LATCHSRC 11
+#define S626_STDBIT_LOADSRC 9
+#define S626_STDBIT_INDXSRC 7
+#define S626_STDBIT_INDXPOL 6
+#define S626_STDBIT_ENCMODE 4
+#define S626_STDBIT_CLKPOL 3
+#define S626_STDBIT_CLKMULT 1
+#define S626_STDBIT_CLKENAB 0
+
+/* Bit field widths for standardized SETUP structure: */
+#define S626_STDWID_INTSRC 2
+#define S626_STDWID_LATCHSRC 2
+#define S626_STDWID_LOADSRC 2
+#define S626_STDWID_INDXSRC 2
+#define S626_STDWID_INDXPOL 1
+#define S626_STDWID_ENCMODE 2
+#define S626_STDWID_CLKPOL 1
+#define S626_STDWID_CLKMULT 2
+#define S626_STDWID_CLKENAB 1
+
+/* Bit field masks for standardized SETUP structure: */
+#define S626_STDMSK_INTSRC S626_SET_STD_INTSRC(~0)
+#define S626_STDMSK_LATCHSRC S626_SET_STD_LATCHSRC(~0)
+#define S626_STDMSK_LOADSRC S626_SET_STD_LOADSRC(~0)
+#define S626_STDMSK_INDXSRC S626_SET_STD_INDXSRC(~0)
+#define S626_STDMSK_INDXPOL S626_SET_STD_INDXPOL(~0)
+#define S626_STDMSK_ENCMODE S626_SET_STD_ENCMODE(~0)
+#define S626_STDMSK_CLKPOL S626_SET_STD_CLKPOL(~0)
+#define S626_STDMSK_CLKMULT S626_SET_STD_CLKMULT(~0)
+#define S626_STDMSK_CLKENAB S626_SET_STD_CLKENAB(~0)
+
+/* Construct parts of standardized SETUP structure: */
+#define S626_SET_STD_INTSRC(x) \
+ S626_MAKE((x), S626_STDWID_INTSRC, S626_STDBIT_INTSRC)
+#define S626_SET_STD_LATCHSRC(x) \
+ S626_MAKE((x), S626_STDWID_LATCHSRC, S626_STDBIT_LATCHSRC)
+#define S626_SET_STD_LOADSRC(x) \
+ S626_MAKE((x), S626_STDWID_LOADSRC, S626_STDBIT_LOADSRC)
+#define S626_SET_STD_INDXSRC(x) \
+ S626_MAKE((x), S626_STDWID_INDXSRC, S626_STDBIT_INDXSRC)
+#define S626_SET_STD_INDXPOL(x) \
+ S626_MAKE((x), S626_STDWID_INDXPOL, S626_STDBIT_INDXPOL)
+#define S626_SET_STD_ENCMODE(x) \
+ S626_MAKE((x), S626_STDWID_ENCMODE, S626_STDBIT_ENCMODE)
+#define S626_SET_STD_CLKPOL(x) \
+ S626_MAKE((x), S626_STDWID_CLKPOL, S626_STDBIT_CLKPOL)
+#define S626_SET_STD_CLKMULT(x) \
+ S626_MAKE((x), S626_STDWID_CLKMULT, S626_STDBIT_CLKMULT)
+#define S626_SET_STD_CLKENAB(x) \
+ S626_MAKE((x), S626_STDWID_CLKENAB, S626_STDBIT_CLKENAB)
+
+/* Extract parts of standardized SETUP structure: */
+#define S626_GET_STD_INTSRC(v) \
+ S626_UNMAKE((v), S626_STDWID_INTSRC, S626_STDBIT_INTSRC)
+#define S626_GET_STD_LATCHSRC(v) \
+ S626_UNMAKE((v), S626_STDWID_LATCHSRC, S626_STDBIT_LATCHSRC)
+#define S626_GET_STD_LOADSRC(v) \
+ S626_UNMAKE((v), S626_STDWID_LOADSRC, S626_STDBIT_LOADSRC)
+#define S626_GET_STD_INDXSRC(v) \
+ S626_UNMAKE((v), S626_STDWID_INDXSRC, S626_STDBIT_INDXSRC)
+#define S626_GET_STD_INDXPOL(v) \
+ S626_UNMAKE((v), S626_STDWID_INDXPOL, S626_STDBIT_INDXPOL)
+#define S626_GET_STD_ENCMODE(v) \
+ S626_UNMAKE((v), S626_STDWID_ENCMODE, S626_STDBIT_ENCMODE)
+#define S626_GET_STD_CLKPOL(v) \
+ S626_UNMAKE((v), S626_STDWID_CLKPOL, S626_STDBIT_CLKPOL)
+#define S626_GET_STD_CLKMULT(v) \
+ S626_UNMAKE((v), S626_STDWID_CLKMULT, S626_STDBIT_CLKMULT)
+#define S626_GET_STD_CLKENAB(v) \
+ S626_UNMAKE((v), S626_STDWID_CLKENAB, S626_STDBIT_CLKENAB)
+
+#endif
diff --git a/drivers/staging/comedi/drivers/serial2002.c b/drivers/staging/comedi/drivers/serial2002.c
new file mode 100644
index 000000000..304ebff11
--- /dev/null
+++ b/drivers/staging/comedi/drivers/serial2002.c
@@ -0,0 +1,798 @@
+/*
+ comedi/drivers/serial2002.c
+ Skeleton code for a Comedi driver
+
+ COMEDI - Linux Control and Measurement Device Interface
+ Copyright (C) 2002 Anders Blomdell <anders.blomdell@control.lth.se>
+
+ This program is free software; you can redistribute it and/or 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.
+*/
+
+/*
+Driver: serial2002
+Description: Driver for serial connected hardware
+Devices:
+Author: Anders Blomdell
+Updated: Fri, 7 Jun 2002 12:56:45 -0700
+Status: in development
+
+*/
+
+#include <linux/module.h>
+#include "../comedidev.h"
+
+#include <linux/delay.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+
+#include <linux/termios.h>
+#include <asm/ioctls.h>
+#include <linux/serial.h>
+#include <linux/poll.h>
+
+struct serial2002_range_table_t {
+ /* HACK... */
+ int length;
+ struct comedi_krange range;
+};
+
+struct serial2002_private {
+ int port; /* /dev/ttyS<port> */
+ int speed; /* baudrate */
+ struct file *tty;
+ unsigned int ao_readback[32];
+ unsigned char digital_in_mapping[32];
+ unsigned char digital_out_mapping[32];
+ unsigned char analog_in_mapping[32];
+ unsigned char analog_out_mapping[32];
+ unsigned char encoder_in_mapping[32];
+ struct serial2002_range_table_t in_range[32], out_range[32];
+};
+
+struct serial_data {
+ enum { is_invalid, is_digital, is_channel } kind;
+ int index;
+ unsigned long value;
+};
+
+/*
+ * The configuration serial_data.value read from the device is
+ * a bitmask that defines specific options of a channel:
+ *
+ * 4:0 - the channel to configure
+ * 7:5 - the kind of channel
+ * 9:8 - the command used to configure the channel
+ *
+ * The remaining bits vary in use depending on the command:
+ *
+ * BITS 15:10 - the channel bits (maxdata)
+ * MIN/MAX 12:10 - the units multiplier for the scale
+ * 13 - the sign of the scale
+ * 33:14 - the base value for the range
+ */
+#define S2002_CFG_CHAN(x) ((x) & 0x1f)
+#define S2002_CFG_KIND(x) (((x) >> 5) & 0x7)
+#define S2002_CFG_KIND_INVALID 0
+#define S2002_CFG_KIND_DIGITAL_IN 1
+#define S2002_CFG_KIND_DIGITAL_OUT 2
+#define S2002_CFG_KIND_ANALOG_IN 3
+#define S2002_CFG_KIND_ANALOG_OUT 4
+#define S2002_CFG_KIND_ENCODER_IN 5
+#define S2002_CFG_CMD(x) (((x) >> 8) & 0x3)
+#define S2002_CFG_CMD_BITS 0
+#define S2002_CFG_CMD_MIN 1
+#define S2002_CFG_CMD_MAX 2
+#define S2002_CFG_BITS(x) (((x) >> 10) & 0x3f)
+#define S2002_CFG_UNITS(x) (((x) >> 10) & 0x7)
+#define S2002_CFG_SIGN(x) (((x) >> 13) & 0x1)
+#define S2002_CFG_BASE(x) (((x) >> 14) & 0xfffff)
+
+static long serial2002_tty_ioctl(struct file *f, unsigned op,
+ unsigned long param)
+{
+ if (f->f_op->unlocked_ioctl)
+ return f->f_op->unlocked_ioctl(f, op, param);
+
+ return -ENOSYS;
+}
+
+static int serial2002_tty_write(struct file *f, unsigned char *buf, int count)
+{
+ const char __user *p = (__force const char __user *)buf;
+ int result;
+ loff_t offset = 0;
+ mm_segment_t oldfs;
+
+ oldfs = get_fs();
+ set_fs(KERNEL_DS);
+ result = __vfs_write(f, p, count, &offset);
+ set_fs(oldfs);
+ return result;
+}
+
+static void serial2002_tty_read_poll_wait(struct file *f, int timeout)
+{
+ struct poll_wqueues table;
+ struct timeval start, now;
+
+ do_gettimeofday(&start);
+ poll_initwait(&table);
+ while (1) {
+ long elapsed;
+ int mask;
+
+ mask = f->f_op->poll(f, &table.pt);
+ if (mask & (POLLRDNORM | POLLRDBAND | POLLIN |
+ POLLHUP | POLLERR)) {
+ break;
+ }
+ do_gettimeofday(&now);
+ elapsed = 1000000 * (now.tv_sec - start.tv_sec) +
+ now.tv_usec - start.tv_usec;
+ if (elapsed > timeout)
+ break;
+ set_current_state(TASK_INTERRUPTIBLE);
+ schedule_timeout(((timeout - elapsed) * HZ) / 10000);
+ }
+ poll_freewait(&table);
+}
+
+static int serial2002_tty_read(struct file *f, int timeout)
+{
+ unsigned char ch;
+ int result;
+
+ result = -1;
+ if (!IS_ERR(f)) {
+ mm_segment_t oldfs;
+ char __user *p = (__force char __user *)&ch;
+ loff_t offset = 0;
+
+ oldfs = get_fs();
+ set_fs(KERNEL_DS);
+ if (f->f_op->poll) {
+ serial2002_tty_read_poll_wait(f, timeout);
+
+ if (__vfs_read(f, p, 1, &offset) == 1)
+ result = ch;
+ } else {
+ /* Device does not support poll, busy wait */
+ int retries = 0;
+
+ while (1) {
+ retries++;
+ if (retries >= timeout)
+ break;
+
+ if (__vfs_read(f, p, 1, &offset) == 1) {
+ result = ch;
+ break;
+ }
+ udelay(100);
+ }
+ }
+ set_fs(oldfs);
+ }
+ return result;
+}
+
+static void serial2002_tty_setspeed(struct file *f, int speed)
+{
+ struct termios termios;
+ struct serial_struct serial;
+ mm_segment_t oldfs;
+
+ oldfs = get_fs();
+ set_fs(KERNEL_DS);
+
+ /* Set speed */
+ serial2002_tty_ioctl(f, TCGETS, (unsigned long)&termios);
+ termios.c_iflag = 0;
+ termios.c_oflag = 0;
+ termios.c_lflag = 0;
+ termios.c_cflag = CLOCAL | CS8 | CREAD;
+ termios.c_cc[VMIN] = 0;
+ termios.c_cc[VTIME] = 0;
+ switch (speed) {
+ case 2400:
+ termios.c_cflag |= B2400;
+ break;
+ case 4800:
+ termios.c_cflag |= B4800;
+ break;
+ case 9600:
+ termios.c_cflag |= B9600;
+ break;
+ case 19200:
+ termios.c_cflag |= B19200;
+ break;
+ case 38400:
+ termios.c_cflag |= B38400;
+ break;
+ case 57600:
+ termios.c_cflag |= B57600;
+ break;
+ case 115200:
+ termios.c_cflag |= B115200;
+ break;
+ default:
+ termios.c_cflag |= B9600;
+ break;
+ }
+ serial2002_tty_ioctl(f, TCSETS, (unsigned long)&termios);
+
+ /* Set low latency */
+ serial2002_tty_ioctl(f, TIOCGSERIAL, (unsigned long)&serial);
+ serial.flags |= ASYNC_LOW_LATENCY;
+ serial2002_tty_ioctl(f, TIOCSSERIAL, (unsigned long)&serial);
+
+ set_fs(oldfs);
+}
+
+static void serial2002_poll_digital(struct file *f, int channel)
+{
+ char cmd;
+
+ cmd = 0x40 | (channel & 0x1f);
+ serial2002_tty_write(f, &cmd, 1);
+}
+
+static void serial2002_poll_channel(struct file *f, int channel)
+{
+ char cmd;
+
+ cmd = 0x60 | (channel & 0x1f);
+ serial2002_tty_write(f, &cmd, 1);
+}
+
+static struct serial_data serial2002_read(struct file *f, int timeout)
+{
+ struct serial_data result;
+ int length;
+
+ result.kind = is_invalid;
+ result.index = 0;
+ result.value = 0;
+ length = 0;
+ while (1) {
+ int data = serial2002_tty_read(f, timeout);
+
+ length++;
+ if (data < 0) {
+ break;
+ } else if (data & 0x80) {
+ result.value = (result.value << 7) | (data & 0x7f);
+ } else {
+ if (length == 1) {
+ switch ((data >> 5) & 0x03) {
+ case 0:
+ result.value = 0;
+ result.kind = is_digital;
+ break;
+ case 1:
+ result.value = 1;
+ result.kind = is_digital;
+ break;
+ }
+ } else {
+ result.value =
+ (result.value << 2) | ((data & 0x60) >> 5);
+ result.kind = is_channel;
+ }
+ result.index = data & 0x1f;
+ break;
+ }
+ }
+ return result;
+}
+
+static void serial2002_write(struct file *f, struct serial_data data)
+{
+ if (data.kind == is_digital) {
+ unsigned char ch =
+ ((data.value << 5) & 0x20) | (data.index & 0x1f);
+ serial2002_tty_write(f, &ch, 1);
+ } else {
+ unsigned char ch[6];
+ int i = 0;
+
+ if (data.value >= (1L << 30)) {
+ ch[i] = 0x80 | ((data.value >> 30) & 0x03);
+ i++;
+ }
+ if (data.value >= (1L << 23)) {
+ ch[i] = 0x80 | ((data.value >> 23) & 0x7f);
+ i++;
+ }
+ if (data.value >= (1L << 16)) {
+ ch[i] = 0x80 | ((data.value >> 16) & 0x7f);
+ i++;
+ }
+ if (data.value >= (1L << 9)) {
+ ch[i] = 0x80 | ((data.value >> 9) & 0x7f);
+ i++;
+ }
+ ch[i] = 0x80 | ((data.value >> 2) & 0x7f);
+ i++;
+ ch[i] = ((data.value << 5) & 0x60) | (data.index & 0x1f);
+ i++;
+ serial2002_tty_write(f, ch, i);
+ }
+}
+
+struct config_t {
+ short int kind;
+ short int bits;
+ int min;
+ int max;
+};
+
+static int serial2002_setup_subdevice(struct comedi_subdevice *s,
+ struct config_t *cfg,
+ struct serial2002_range_table_t *range,
+ unsigned char *mapping,
+ int kind)
+{
+ const struct comedi_lrange **range_table_list = NULL;
+ unsigned int *maxdata_list;
+ int j, chan;
+
+ for (chan = 0, j = 0; j < 32; j++) {
+ if (cfg[j].kind == kind)
+ chan++;
+ }
+ s->n_chan = chan;
+ s->maxdata = 0;
+ kfree(s->maxdata_list);
+ maxdata_list = kmalloc_array(s->n_chan, sizeof(unsigned int),
+ GFP_KERNEL);
+ if (!maxdata_list)
+ return -ENOMEM;
+ s->maxdata_list = maxdata_list;
+ kfree(s->range_table_list);
+ s->range_table = NULL;
+ s->range_table_list = NULL;
+ if (kind == 1 || kind == 2) {
+ s->range_table = &range_digital;
+ } else if (range) {
+ range_table_list = kmalloc_array(s->n_chan, sizeof(*range),
+ GFP_KERNEL);
+ if (!range_table_list)
+ return -ENOMEM;
+ s->range_table_list = range_table_list;
+ }
+ for (chan = 0, j = 0; j < 32; j++) {
+ if (cfg[j].kind == kind) {
+ if (mapping)
+ mapping[chan] = j;
+ if (range) {
+ range[j].length = 1;
+ range[j].range.min = cfg[j].min;
+ range[j].range.max = cfg[j].max;
+ range_table_list[chan] =
+ (const struct comedi_lrange *)&range[j];
+ }
+ maxdata_list[chan] = ((long long)1 << cfg[j].bits) - 1;
+ chan++;
+ }
+ }
+ return 0;
+}
+
+static int serial2002_setup_subdevs(struct comedi_device *dev)
+{
+ struct serial2002_private *devpriv = dev->private;
+ struct config_t *di_cfg;
+ struct config_t *do_cfg;
+ struct config_t *ai_cfg;
+ struct config_t *ao_cfg;
+ struct config_t *cfg;
+ struct comedi_subdevice *s;
+ int result = 0;
+ int i;
+
+ /* Allocate the temporary structs to hold the configuration data */
+ di_cfg = kcalloc(32, sizeof(*cfg), GFP_KERNEL);
+ do_cfg = kcalloc(32, sizeof(*cfg), GFP_KERNEL);
+ ai_cfg = kcalloc(32, sizeof(*cfg), GFP_KERNEL);
+ ao_cfg = kcalloc(32, sizeof(*cfg), GFP_KERNEL);
+ if (!di_cfg || !do_cfg || !ai_cfg || !ao_cfg) {
+ result = -ENOMEM;
+ goto err_alloc_configs;
+ }
+
+ /* Read the configuration from the connected device */
+ serial2002_tty_setspeed(devpriv->tty, devpriv->speed);
+ serial2002_poll_channel(devpriv->tty, 31);
+ while (1) {
+ struct serial_data data = serial2002_read(devpriv->tty, 1000);
+ int kind = S2002_CFG_KIND(data.value);
+ int channel = S2002_CFG_CHAN(data.value);
+ int range = S2002_CFG_BASE(data.value);
+ int cmd = S2002_CFG_CMD(data.value);
+
+ if (data.kind != is_channel || data.index != 31 ||
+ kind == S2002_CFG_KIND_INVALID)
+ break;
+
+ switch (kind) {
+ case S2002_CFG_KIND_DIGITAL_IN:
+ cfg = di_cfg;
+ break;
+ case S2002_CFG_KIND_DIGITAL_OUT:
+ cfg = do_cfg;
+ break;
+ case S2002_CFG_KIND_ANALOG_IN:
+ cfg = ai_cfg;
+ break;
+ case S2002_CFG_KIND_ANALOG_OUT:
+ cfg = ao_cfg;
+ break;
+ case S2002_CFG_KIND_ENCODER_IN:
+ cfg = ai_cfg;
+ break;
+ default:
+ cfg = NULL;
+ break;
+ }
+ if (!cfg)
+ continue; /* unknown kind, skip it */
+
+ cfg[channel].kind = kind;
+
+ switch (cmd) {
+ case S2002_CFG_CMD_BITS:
+ cfg[channel].bits = S2002_CFG_BITS(data.value);
+ break;
+ case S2002_CFG_CMD_MIN:
+ case S2002_CFG_CMD_MAX:
+ switch (S2002_CFG_UNITS(data.value)) {
+ case 0:
+ range *= 1000000;
+ break;
+ case 1:
+ range *= 1000;
+ break;
+ case 2:
+ range *= 1;
+ break;
+ }
+ if (S2002_CFG_SIGN(data.value))
+ range = -range;
+ if (cmd == S2002_CFG_CMD_MIN)
+ cfg[channel].min = range;
+ else
+ cfg[channel].max = range;
+ break;
+ }
+ }
+
+ /* Fill in subdevice data */
+ for (i = 0; i <= 4; i++) {
+ unsigned char *mapping = NULL;
+ struct serial2002_range_table_t *range = NULL;
+ int kind = 0;
+
+ s = &dev->subdevices[i];
+
+ switch (i) {
+ case 0:
+ cfg = di_cfg;
+ mapping = devpriv->digital_in_mapping;
+ kind = S2002_CFG_KIND_DIGITAL_IN;
+ break;
+ case 1:
+ cfg = do_cfg;
+ mapping = devpriv->digital_out_mapping;
+ kind = S2002_CFG_KIND_DIGITAL_OUT;
+ break;
+ case 2:
+ cfg = ai_cfg;
+ mapping = devpriv->analog_in_mapping;
+ range = devpriv->in_range;
+ kind = S2002_CFG_KIND_ANALOG_IN;
+ break;
+ case 3:
+ cfg = ao_cfg;
+ mapping = devpriv->analog_out_mapping;
+ range = devpriv->out_range;
+ kind = S2002_CFG_KIND_ANALOG_OUT;
+ break;
+ case 4:
+ cfg = ai_cfg;
+ mapping = devpriv->encoder_in_mapping;
+ range = devpriv->in_range;
+ kind = S2002_CFG_KIND_ENCODER_IN;
+ break;
+ }
+
+ if (serial2002_setup_subdevice(s, cfg, range, mapping, kind))
+ break; /* err handled below */
+ }
+ if (i <= 4) {
+ /*
+ * Failed to allocate maxdata_list or range_table_list
+ * for a subdevice that needed it.
+ */
+ result = -ENOMEM;
+ for (i = 0; i <= 4; i++) {
+ s = &dev->subdevices[i];
+ kfree(s->maxdata_list);
+ s->maxdata_list = NULL;
+ kfree(s->range_table_list);
+ s->range_table_list = NULL;
+ }
+ }
+
+err_alloc_configs:
+ kfree(di_cfg);
+ kfree(do_cfg);
+ kfree(ai_cfg);
+ kfree(ao_cfg);
+
+ if (result) {
+ if (devpriv->tty) {
+ filp_close(devpriv->tty, NULL);
+ devpriv->tty = NULL;
+ }
+ }
+
+ return result;
+}
+
+static int serial2002_open(struct comedi_device *dev)
+{
+ struct serial2002_private *devpriv = dev->private;
+ int result;
+ char port[20];
+
+ sprintf(port, "/dev/ttyS%d", devpriv->port);
+ devpriv->tty = filp_open(port, O_RDWR, 0);
+ if (IS_ERR(devpriv->tty)) {
+ result = (int)PTR_ERR(devpriv->tty);
+ dev_err(dev->class_dev, "file open error = %d\n", result);
+ } else {
+ result = serial2002_setup_subdevs(dev);
+ }
+ return result;
+}
+
+static void serial2002_close(struct comedi_device *dev)
+{
+ struct serial2002_private *devpriv = dev->private;
+
+ if (!IS_ERR(devpriv->tty) && devpriv->tty)
+ filp_close(devpriv->tty, NULL);
+}
+
+static int serial2002_di_insn_read(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ struct serial2002_private *devpriv = dev->private;
+ int n;
+ int chan;
+
+ chan = devpriv->digital_in_mapping[CR_CHAN(insn->chanspec)];
+ for (n = 0; n < insn->n; n++) {
+ struct serial_data read;
+
+ serial2002_poll_digital(devpriv->tty, chan);
+ while (1) {
+ read = serial2002_read(devpriv->tty, 1000);
+ if (read.kind != is_digital || read.index == chan)
+ break;
+ }
+ data[n] = read.value;
+ }
+ return n;
+}
+
+static int serial2002_do_insn_write(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ struct serial2002_private *devpriv = dev->private;
+ int n;
+ int chan;
+
+ chan = devpriv->digital_out_mapping[CR_CHAN(insn->chanspec)];
+ for (n = 0; n < insn->n; n++) {
+ struct serial_data write;
+
+ write.kind = is_digital;
+ write.index = chan;
+ write.value = data[n];
+ serial2002_write(devpriv->tty, write);
+ }
+ return n;
+}
+
+static int serial2002_ai_insn_read(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ struct serial2002_private *devpriv = dev->private;
+ int n;
+ int chan;
+
+ chan = devpriv->analog_in_mapping[CR_CHAN(insn->chanspec)];
+ for (n = 0; n < insn->n; n++) {
+ struct serial_data read;
+
+ serial2002_poll_channel(devpriv->tty, chan);
+ while (1) {
+ read = serial2002_read(devpriv->tty, 1000);
+ if (read.kind != is_channel || read.index == chan)
+ break;
+ }
+ data[n] = read.value;
+ }
+ return n;
+}
+
+static int serial2002_ao_insn_write(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ struct serial2002_private *devpriv = dev->private;
+ int n;
+ int chan;
+
+ chan = devpriv->analog_out_mapping[CR_CHAN(insn->chanspec)];
+ for (n = 0; n < insn->n; n++) {
+ struct serial_data write;
+
+ write.kind = is_channel;
+ write.index = chan;
+ write.value = data[n];
+ serial2002_write(devpriv->tty, write);
+ devpriv->ao_readback[chan] = data[n];
+ }
+ return n;
+}
+
+static int serial2002_ao_insn_read(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ struct serial2002_private *devpriv = dev->private;
+ int n;
+ int chan = CR_CHAN(insn->chanspec);
+
+ for (n = 0; n < insn->n; n++)
+ data[n] = devpriv->ao_readback[chan];
+
+ return n;
+}
+
+static int serial2002_encoder_insn_read(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ struct serial2002_private *devpriv = dev->private;
+ int n;
+ int chan;
+
+ chan = devpriv->encoder_in_mapping[CR_CHAN(insn->chanspec)];
+ for (n = 0; n < insn->n; n++) {
+ struct serial_data read;
+
+ serial2002_poll_channel(devpriv->tty, chan);
+ while (1) {
+ read = serial2002_read(devpriv->tty, 1000);
+ if (read.kind != is_channel || read.index == chan)
+ break;
+ }
+ data[n] = read.value;
+ }
+ return n;
+}
+
+static int serial2002_attach(struct comedi_device *dev,
+ struct comedi_devconfig *it)
+{
+ struct serial2002_private *devpriv;
+ struct comedi_subdevice *s;
+ int ret;
+
+ devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
+ if (!devpriv)
+ return -ENOMEM;
+
+ devpriv->port = it->options[0];
+ devpriv->speed = it->options[1];
+
+ ret = comedi_alloc_subdevices(dev, 5);
+ if (ret)
+ return ret;
+
+ /* digital input subdevice */
+ s = &dev->subdevices[0];
+ s->type = COMEDI_SUBD_DI;
+ s->subdev_flags = SDF_READABLE;
+ s->n_chan = 0;
+ s->maxdata = 1;
+ s->range_table = &range_digital;
+ s->insn_read = serial2002_di_insn_read;
+
+ /* digital output subdevice */
+ s = &dev->subdevices[1];
+ s->type = COMEDI_SUBD_DO;
+ s->subdev_flags = SDF_WRITABLE;
+ s->n_chan = 0;
+ s->maxdata = 1;
+ s->range_table = &range_digital;
+ s->insn_write = serial2002_do_insn_write;
+
+ /* analog input subdevice */
+ s = &dev->subdevices[2];
+ s->type = COMEDI_SUBD_AI;
+ s->subdev_flags = SDF_READABLE | SDF_GROUND;
+ s->n_chan = 0;
+ s->maxdata = 1;
+ s->range_table = NULL;
+ s->insn_read = serial2002_ai_insn_read;
+
+ /* analog output subdevice */
+ s = &dev->subdevices[3];
+ s->type = COMEDI_SUBD_AO;
+ s->subdev_flags = SDF_WRITABLE;
+ s->n_chan = 0;
+ s->maxdata = 1;
+ s->range_table = NULL;
+ s->insn_write = serial2002_ao_insn_write;
+ s->insn_read = serial2002_ao_insn_read;
+
+ /* encoder input subdevice */
+ s = &dev->subdevices[4];
+ s->type = COMEDI_SUBD_COUNTER;
+ s->subdev_flags = SDF_READABLE | SDF_LSAMPL;
+ s->n_chan = 0;
+ s->maxdata = 1;
+ s->range_table = NULL;
+ s->insn_read = serial2002_encoder_insn_read;
+
+ dev->open = serial2002_open;
+ dev->close = serial2002_close;
+
+ return 0;
+}
+
+static void serial2002_detach(struct comedi_device *dev)
+{
+ struct comedi_subdevice *s;
+ int i;
+
+ for (i = 0; i < dev->n_subdevices; i++) {
+ s = &dev->subdevices[i];
+ kfree(s->maxdata_list);
+ kfree(s->range_table_list);
+ }
+}
+
+static struct comedi_driver serial2002_driver = {
+ .driver_name = "serial2002",
+ .module = THIS_MODULE,
+ .attach = serial2002_attach,
+ .detach = serial2002_detach,
+};
+module_comedi_driver(serial2002_driver);
+
+MODULE_AUTHOR("Comedi http://www.comedi.org");
+MODULE_DESCRIPTION("Comedi low-level driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/comedi/drivers/ssv_dnp.c b/drivers/staging/comedi/drivers/ssv_dnp.c
new file mode 100644
index 000000000..acc7f3445
--- /dev/null
+++ b/drivers/staging/comedi/drivers/ssv_dnp.c
@@ -0,0 +1,186 @@
+/*
+ comedi/drivers/ssv_dnp.c
+ generic comedi driver for SSV Embedded Systems' DIL/Net-PCs
+ Copyright (C) 2001 Robert Schwebel <robert@schwebel.de>
+
+ COMEDI - Linux Control and Measurement Device Interface
+ Copyright (C) 2000 David A. Schleef <ds@schleef.org>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+*/
+/*
+Driver: ssv_dnp
+Description: SSV Embedded Systems DIL/Net-PC
+Author: Robert Schwebel <robert@schwebel.de>
+Devices: [SSV Embedded Systems] DIL/Net-PC 1486 (dnp-1486)
+Status: unknown
+*/
+
+/* include files ----------------------------------------------------------- */
+
+#include <linux/module.h>
+#include "../comedidev.h"
+
+/* Some global definitions: the registers of the DNP ----------------------- */
+/* */
+/* For port A and B the mode register has bits corresponding to the output */
+/* pins, where Bit-N = 0 -> input, Bit-N = 1 -> output. Note that bits */
+/* 4 to 7 correspond to pin 0..3 for port C data register. Ensure that bits */
+/* 0..3 remain unchanged! For details about Port C Mode Register see */
+/* the remarks in dnp_insn_config() below. */
+
+#define CSCIR 0x22 /* Chip Setup and Control Index Register */
+#define CSCDR 0x23 /* Chip Setup and Control Data Register */
+#define PAMR 0xa5 /* Port A Mode Register */
+#define PADR 0xa9 /* Port A Data Register */
+#define PBMR 0xa4 /* Port B Mode Register */
+#define PBDR 0xa8 /* Port B Data Register */
+#define PCMR 0xa3 /* Port C Mode Register */
+#define PCDR 0xa7 /* Port C Data Register */
+
+static int dnp_dio_insn_bits(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ unsigned int mask;
+ unsigned int val;
+
+ /*
+ * Ports A and B are straight forward: each bit corresponds to an
+ * output pin with the same order. Port C is different: bits 0...3
+ * correspond to bits 4...7 of the output register (PCDR).
+ */
+
+ mask = comedi_dio_update_state(s, data);
+ if (mask) {
+ outb(PADR, CSCIR);
+ outb(s->state & 0xff, CSCDR);
+
+ outb(PBDR, CSCIR);
+ outb((s->state >> 8) & 0xff, CSCDR);
+
+ outb(PCDR, CSCIR);
+ val = inb(CSCDR) & 0x0f;
+ outb(((s->state >> 12) & 0xf0) | val, CSCDR);
+ }
+
+ outb(PADR, CSCIR);
+ val = inb(CSCDR);
+ outb(PBDR, CSCIR);
+ val |= (inb(CSCDR) << 8);
+ outb(PCDR, CSCIR);
+ val |= ((inb(CSCDR) & 0xf0) << 12);
+
+ data[1] = val;
+
+ return insn->n;
+}
+
+static int dnp_dio_insn_config(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ unsigned int chan = CR_CHAN(insn->chanspec);
+ unsigned int mask;
+ unsigned int val;
+ int ret;
+
+ ret = comedi_dio_insn_config(dev, s, insn, data, 0);
+ if (ret)
+ return ret;
+
+ if (chan < 8) { /* Port A */
+ mask = 1 << chan;
+ outb(PAMR, CSCIR);
+ } else if (chan < 16) { /* Port B */
+ mask = 1 << (chan - 8);
+ outb(PBMR, CSCIR);
+ } else { /* Port C */
+ /*
+ * We have to pay attention with port C.
+ * This is the meaning of PCMR:
+ * Bit in PCMR: 7 6 5 4 3 2 1 0
+ * Corresponding port C pin: d 3 d 2 d 1 d 0 d= don't touch
+ *
+ * Multiplication by 2 brings bits into correct position
+ * for PCMR!
+ */
+ mask = 1 << ((chan - 16) * 2);
+ outb(PCMR, CSCIR);
+ }
+
+ val = inb(CSCDR);
+ if (data[0] == COMEDI_OUTPUT)
+ val |= mask;
+ else
+ val &= ~mask;
+ outb(val, CSCDR);
+
+ return insn->n;
+}
+
+static int dnp_attach(struct comedi_device *dev, struct comedi_devconfig *it)
+{
+ struct comedi_subdevice *s;
+ int ret;
+
+ ret = comedi_alloc_subdevices(dev, 1);
+ if (ret)
+ return ret;
+
+ s = &dev->subdevices[0];
+ /* digital i/o subdevice */
+ s->type = COMEDI_SUBD_DIO;
+ s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
+ s->n_chan = 20;
+ s->maxdata = 1;
+ s->range_table = &range_digital;
+ s->insn_bits = dnp_dio_insn_bits;
+ s->insn_config = dnp_dio_insn_config;
+
+ /* We use the I/O ports 0x22,0x23 and 0xa3-0xa9, which are always
+ * allocated for the primary 8259, so we don't need to allocate them
+ * ourselves. */
+
+ /* configure all ports as input (default) */
+ outb(PAMR, CSCIR);
+ outb(0x00, CSCDR);
+ outb(PBMR, CSCIR);
+ outb(0x00, CSCDR);
+ outb(PCMR, CSCIR);
+ outb((inb(CSCDR) & 0xAA), CSCDR);
+
+ return 0;
+}
+
+static void dnp_detach(struct comedi_device *dev)
+{
+ outb(PAMR, CSCIR);
+ outb(0x00, CSCDR);
+ outb(PBMR, CSCIR);
+ outb(0x00, CSCDR);
+ outb(PCMR, CSCIR);
+ outb((inb(CSCDR) & 0xAA), CSCDR);
+}
+
+static struct comedi_driver dnp_driver = {
+ .driver_name = "dnp-1486",
+ .module = THIS_MODULE,
+ .attach = dnp_attach,
+ .detach = dnp_detach,
+};
+module_comedi_driver(dnp_driver);
+
+MODULE_AUTHOR("Comedi http://www.comedi.org");
+MODULE_DESCRIPTION("Comedi low-level driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/comedi/drivers/unioxx5.c b/drivers/staging/comedi/drivers/unioxx5.c
new file mode 100644
index 000000000..51498b889
--- /dev/null
+++ b/drivers/staging/comedi/drivers/unioxx5.c
@@ -0,0 +1,506 @@
+/***************************************************************************
+ * *
+ * comedi/drivers/unioxx5.c *
+ * Driver for Fastwel UNIOxx-5 (analog and digital i/o) boards. *
+ * *
+ * Copyright (C) 2006 Kruchinin Daniil (asgard) [asgard@etersoft.ru] *
+ * *
+ * COMEDI - Linux Control and Measurement Device Interface *
+ * Copyright (C) 1998,2000 David A. Schleef <ds@schleef.org> *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ ***************************************************************************/
+/*
+
+Driver: unioxx5
+Description: Driver for Fastwel UNIOxx-5 (analog and digital i/o) boards.
+Author: Kruchinin Daniil (asgard) <asgard@etersoft.ru>
+Status: unknown
+Updated: 2006-10-09
+Devices: [Fastwel] UNIOxx-5 (unioxx5),
+
+ This card supports digital and analog I/O. It written for g01
+ subdevices only.
+ channels range: 0 .. 23 dio channels
+ and 0 .. 11 analog modules range
+ During attaching unioxx5 module displays modules identifiers
+ (see dmesg after comedi_config) in format:
+ | [module_number] module_id |
+
+*/
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include "../comedidev.h"
+
+#define UNIOXX5_SIZE 0x10
+#define UNIOXX5_SUBDEV_BASE 0xA000 /* base addr of first subdev */
+#define UNIOXX5_SUBDEV_ODDS 0x400
+
+/* modules types */
+#define MODULE_DIGITAL 0
+#define MODULE_OUTPUT_MASK 0x80 /* analog input/output */
+
+/* constants for digital i/o */
+#define UNIOXX5_NUM_OF_CHANS 24
+
+/* constants for analog i/o */
+#define TxBE 0x10 /* transmit buffer enable */
+#define RxCA 0x20 /* 1 receive character available */
+#define Rx2CA 0x40 /* 2 receive character available */
+#define Rx4CA 0x80 /* 4 receive character available */
+
+/* bytes mask errors */
+#define Rx2CA_ERR_MASK 0x04 /* 2 bytes receiving error */
+#define Rx4CA_ERR_MASK 0x08 /* 4 bytes receiving error */
+
+/* channel modes */
+#define ALL_2_INPUT 0 /* config all digital channels to input */
+#define ALL_2_OUTPUT 1 /* config all digital channels to output */
+
+/* 'private' structure for each subdevice */
+struct unioxx5_subd_priv {
+ int usp_iobase;
+ /* 12 modules. each can be 70L or 73L */
+ unsigned char usp_module_type[12];
+ /* for saving previous written value for analog modules */
+ unsigned char usp_extra_data[12][4];
+ unsigned char usp_prev_wr_val[3]; /* previous written value */
+ unsigned char usp_prev_cn_val[3]; /* previous channel value */
+};
+
+static int __unioxx5_define_chan_offset(int chan_num)
+{
+ if (chan_num < 0 || chan_num > 23)
+ return -1;
+
+ return (chan_num >> 3) + 1;
+}
+
+#if 0 /* not used? */
+static void __unioxx5_digital_config(struct comedi_subdevice *s, int mode)
+{
+ struct unioxx5_subd_priv *usp = s->private;
+ struct device *csdev = s->device->class_dev;
+ int i, mask;
+
+ mask = (mode == ALL_2_OUTPUT) ? 0xFF : 0x00;
+ dev_dbg(csdev, "mode = %d\n", mask);
+
+ outb(1, usp->usp_iobase + 0);
+
+ for (i = 0; i < 3; i++)
+ outb(mask, usp->usp_iobase + i);
+
+ outb(0, usp->usp_iobase + 0);
+}
+#endif
+
+/* configure channels for analog i/o (even to output, odd to input) */
+static void __unioxx5_analog_config(struct unioxx5_subd_priv *usp, int channel)
+{
+ int chan_a, chan_b, conf, channel_offset;
+
+ channel_offset = __unioxx5_define_chan_offset(channel);
+ conf = usp->usp_prev_cn_val[channel_offset - 1];
+ chan_a = chan_b = 1;
+
+ /* setting channel A and channel B mask */
+ if (channel % 2 == 0) {
+ chan_a <<= channel & 0x07;
+ chan_b <<= (channel + 1) & 0x07;
+ } else {
+ chan_a <<= (channel - 1) & 0x07;
+ chan_b <<= channel & 0x07;
+ }
+
+ conf |= chan_a; /* even channel ot output */
+ conf &= ~chan_b; /* odd channel to input */
+
+ outb(1, usp->usp_iobase + 0);
+ outb(conf, usp->usp_iobase + channel_offset);
+ outb(0, usp->usp_iobase + 0);
+
+ usp->usp_prev_cn_val[channel_offset - 1] = conf;
+}
+
+static int __unioxx5_digital_read(struct comedi_subdevice *s,
+ unsigned int *data, int channel, int minor)
+{
+ struct unioxx5_subd_priv *usp = s->private;
+ struct device *csdev = s->device->class_dev;
+ int channel_offset, mask = 1 << (channel & 0x07);
+
+ channel_offset = __unioxx5_define_chan_offset(channel);
+ if (channel_offset < 0) {
+ dev_err(csdev,
+ "undefined channel %d. channel range is 0 .. 23\n",
+ channel);
+ return 0;
+ }
+
+ *data = inb(usp->usp_iobase + channel_offset);
+ *data &= mask;
+
+ /* correct the read value to 0 or 1 */
+ if (channel_offset > 1)
+ channel -= 2 << channel_offset;
+ *data >>= channel;
+ return 1;
+}
+
+static int __unioxx5_analog_read(struct comedi_subdevice *s,
+ unsigned int *data, int channel, int minor)
+{
+ struct unioxx5_subd_priv *usp = s->private;
+ struct device *csdev = s->device->class_dev;
+ int module_no, read_ch;
+ char control;
+
+ module_no = channel / 2;
+ read_ch = channel % 2; /* depend on type of channel (A or B) */
+
+ /* defining if given module can work on input */
+ if (usp->usp_module_type[module_no] & MODULE_OUTPUT_MASK) {
+ dev_err(csdev,
+ "module in position %d with id 0x%02x is for output only",
+ module_no, usp->usp_module_type[module_no]);
+ return 0;
+ }
+
+ __unioxx5_analog_config(usp, channel);
+ /* sends module number to card(1 .. 12) */
+ outb(module_no + 1, usp->usp_iobase + 5);
+ outb('V', usp->usp_iobase + 6); /* sends to module (V)erify command */
+ control = inb(usp->usp_iobase); /* get control register byte */
+
+ /* waits while reading four bytes will be allowed */
+ while (!((control = inb(usp->usp_iobase + 0)) & Rx4CA))
+ ;
+
+ /* if four bytes readding error occurs - return 0(false) */
+ if ((control & Rx4CA_ERR_MASK)) {
+ dev_err(csdev, "4 bytes error\n");
+ return 0;
+ }
+
+ if (read_ch)
+ *data = inw(usp->usp_iobase + 6); /* channel B */
+ else
+ *data = inw(usp->usp_iobase + 4); /* channel A */
+
+ return 1;
+}
+
+static int __unioxx5_digital_write(struct comedi_subdevice *s,
+ unsigned int *data, int channel, int minor)
+{
+ struct unioxx5_subd_priv *usp = s->private;
+ struct device *csdev = s->device->class_dev;
+ int channel_offset, val;
+ int mask = 1 << (channel & 0x07);
+
+ channel_offset = __unioxx5_define_chan_offset(channel);
+ if (channel_offset < 0) {
+ dev_err(csdev,
+ "undefined channel %d. channel range is 0 .. 23\n",
+ channel);
+ return 0;
+ }
+
+ /* getting previous written value */
+ val = usp->usp_prev_wr_val[channel_offset - 1];
+
+ if (*data)
+ val |= mask;
+ else
+ val &= ~mask;
+
+ outb(val, usp->usp_iobase + channel_offset);
+ /* saving new written value */
+ usp->usp_prev_wr_val[channel_offset - 1] = val;
+
+ return 1;
+}
+
+static int __unioxx5_analog_write(struct comedi_subdevice *s,
+ unsigned int *data, int channel, int minor)
+{
+ struct unioxx5_subd_priv *usp = s->private;
+ struct device *csdev = s->device->class_dev;
+ int module, i;
+
+ module = channel / 2; /* definig module number(0 .. 11) */
+ i = (channel % 2) << 1; /* depends on type of channel (A or B) */
+
+ /* defining if given module can work on output */
+ if (!(usp->usp_module_type[module] & MODULE_OUTPUT_MASK)) {
+ dev_err(csdev,
+ "module in position %d with id 0x%0x is for input only!\n",
+ module, usp->usp_module_type[module]);
+ return 0;
+ }
+
+ __unioxx5_analog_config(usp, channel);
+ /* saving minor byte */
+ usp->usp_extra_data[module][i++] = (unsigned char)(*data & 0x00FF);
+ /* saving major byte */
+ usp->usp_extra_data[module][i] = (unsigned char)((*data & 0xFF00) >> 8);
+
+ /* while(!((inb(usp->usp_iobase + 0)) & TxBE)); */
+ /* sending module number to card(1 .. 12) */
+ outb(module + 1, usp->usp_iobase + 5);
+ outb('W', usp->usp_iobase + 6); /* sends (W)rite command to module */
+
+ /* sending for bytes to module(one byte per cycle iteration) */
+ for (i = 0; i < 4; i++) {
+ while (!((inb(usp->usp_iobase + 0)) & TxBE))
+ ; /* waits while writing will be allowed */
+ outb(usp->usp_extra_data[module][i], usp->usp_iobase + 6);
+ }
+
+ return 1;
+}
+
+static int unioxx5_subdev_read(struct comedi_device *dev,
+ struct comedi_subdevice *subdev,
+ struct comedi_insn *insn, unsigned int *data)
+{
+ struct unioxx5_subd_priv *usp = subdev->private;
+ int channel, type;
+
+ channel = CR_CHAN(insn->chanspec);
+ /* defining module type(analog or digital) */
+ type = usp->usp_module_type[channel / 2];
+
+ if (type == MODULE_DIGITAL) {
+ if (!__unioxx5_digital_read(subdev, data, channel, dev->minor))
+ return -1;
+ } else {
+ if (!__unioxx5_analog_read(subdev, data, channel, dev->minor))
+ return -1;
+ }
+
+ return 1;
+}
+
+static int unioxx5_subdev_write(struct comedi_device *dev,
+ struct comedi_subdevice *subdev,
+ struct comedi_insn *insn, unsigned int *data)
+{
+ struct unioxx5_subd_priv *usp = subdev->private;
+ int channel, type;
+
+ channel = CR_CHAN(insn->chanspec);
+ /* defining module type(analog or digital) */
+ type = usp->usp_module_type[channel / 2];
+
+ if (type == MODULE_DIGITAL) {
+ if (!__unioxx5_digital_write(subdev, data, channel, dev->minor))
+ return -1;
+ } else {
+ if (!__unioxx5_analog_write(subdev, data, channel, dev->minor))
+ return -1;
+ }
+
+ return 1;
+}
+
+/* for digital modules only */
+static int unioxx5_insn_config(struct comedi_device *dev,
+ struct comedi_subdevice *subdev,
+ struct comedi_insn *insn, unsigned int *data)
+{
+ int channel_offset, flags, channel = CR_CHAN(insn->chanspec), type;
+ struct unioxx5_subd_priv *usp = subdev->private;
+ int mask = 1 << (channel & 0x07);
+
+ type = usp->usp_module_type[channel / 2];
+
+ if (type != MODULE_DIGITAL) {
+ dev_err(dev->class_dev,
+ "channel configuration accessible only for digital modules\n");
+ return -1;
+ }
+
+ channel_offset = __unioxx5_define_chan_offset(channel);
+ if (channel_offset < 0) {
+ dev_err(dev->class_dev,
+ "undefined channel %d. channel range is 0 .. 23\n",
+ channel);
+ return -1;
+ }
+
+ /* gets previously written value */
+ flags = usp->usp_prev_cn_val[channel_offset - 1];
+
+ switch (*data) {
+ case COMEDI_INPUT:
+ flags &= ~mask;
+ break;
+ case COMEDI_OUTPUT:
+ flags |= mask;
+ break;
+ default:
+ dev_err(dev->class_dev, "unknown flag\n");
+ return -1;
+ }
+
+ /* *\
+ * sets channels buffer to 1(after this we are allowed to *
+ * change channel type on input or output) *
+ \* */
+ outb(1, usp->usp_iobase + 0);
+ /* changes type of _one_ channel */
+ outb(flags, usp->usp_iobase + channel_offset);
+ /* sets channels bank to 0(allows directly input/output) */
+ outb(0, usp->usp_iobase + 0);
+ /* saves written value */
+ usp->usp_prev_cn_val[channel_offset - 1] = flags;
+
+ return 0;
+}
+
+/* initializing subdevice with given address */
+static int __unioxx5_subdev_init(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ int iobase)
+{
+ struct unioxx5_subd_priv *usp;
+ int i, to, ndef_flag = 0;
+ int ret;
+
+ usp = comedi_alloc_spriv(s, sizeof(*usp));
+ if (!usp)
+ return -ENOMEM;
+
+ ret = __comedi_request_region(dev, iobase, UNIOXX5_SIZE);
+ if (ret)
+ return ret;
+ usp->usp_iobase = iobase;
+
+ /* defining modules types */
+ for (i = 0; i < 12; i++) {
+ to = 10000;
+
+ __unioxx5_analog_config(usp, i * 2);
+ /* sends channel number to card */
+ outb(i + 1, iobase + 5);
+ outb('H', iobase + 6); /* requests EEPROM world */
+ while (!(inb(iobase + 0) & TxBE))
+ ; /* waits while writing will be allowed */
+ outb(0, iobase + 6);
+
+ /* waits while reading of two bytes will be allowed */
+ while (!(inb(iobase + 0) & Rx2CA)) {
+ if (--to <= 0) {
+ ndef_flag = 1;
+ break;
+ }
+ }
+
+ if (ndef_flag) {
+ usp->usp_module_type[i] = 0;
+ ndef_flag = 0;
+ } else {
+ usp->usp_module_type[i] = inb(iobase + 6);
+ }
+
+ udelay(1);
+ }
+
+ /* initial subdevice for digital or analog i/o */
+ s->type = COMEDI_SUBD_DIO;
+ s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
+ s->n_chan = UNIOXX5_NUM_OF_CHANS;
+ s->maxdata = 0xFFF;
+ s->range_table = &range_digital;
+ s->insn_read = unioxx5_subdev_read;
+ s->insn_write = unioxx5_subdev_write;
+ /* for digital modules only!!! */
+ s->insn_config = unioxx5_insn_config;
+
+ return 0;
+}
+
+static int unioxx5_attach(struct comedi_device *dev,
+ struct comedi_devconfig *it)
+{
+ struct comedi_subdevice *s;
+ int iobase, i, n_subd;
+ int id, num, ba;
+ int ret;
+
+ iobase = it->options[0];
+
+ dev->iobase = iobase;
+ iobase += UNIOXX5_SUBDEV_BASE;
+ n_subd = 0;
+
+ /* getting number of subdevices with types 'g01' */
+ for (i = 0, ba = iobase; i < 4; i++, ba += UNIOXX5_SUBDEV_ODDS) {
+ id = inb(ba + 0xE);
+ num = inb(ba + 0xF);
+
+ if (id != 'g' || num != 1)
+ continue;
+
+ n_subd++;
+ }
+
+ /* unioxx5 can has from two to four subdevices */
+ if (n_subd < 2) {
+ dev_err(dev->class_dev,
+ "your card must has at least 2 'g01' subdevices\n");
+ return -1;
+ }
+
+ ret = comedi_alloc_subdevices(dev, n_subd);
+ if (ret)
+ return ret;
+
+ /* initializing each of for same subdevices */
+ for (i = 0; i < n_subd; i++, iobase += UNIOXX5_SUBDEV_ODDS) {
+ s = &dev->subdevices[i];
+ ret = __unioxx5_subdev_init(dev, s, iobase);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static void unioxx5_detach(struct comedi_device *dev)
+{
+ struct comedi_subdevice *s;
+ struct unioxx5_subd_priv *spriv;
+ int i;
+
+ for (i = 0; i < dev->n_subdevices; i++) {
+ s = &dev->subdevices[i];
+ spriv = s->private;
+ if (spriv && spriv->usp_iobase)
+ release_region(spriv->usp_iobase, UNIOXX5_SIZE);
+ }
+}
+
+static struct comedi_driver unioxx5_driver = {
+ .driver_name = "unioxx5",
+ .module = THIS_MODULE,
+ .attach = unioxx5_attach,
+ .detach = unioxx5_detach,
+};
+module_comedi_driver(unioxx5_driver);
+
+MODULE_AUTHOR("Comedi http://www.comedi.org");
+MODULE_DESCRIPTION("Comedi low-level driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/comedi/drivers/usbdux.c b/drivers/staging/comedi/drivers/usbdux.c
new file mode 100644
index 000000000..ced05e581
--- /dev/null
+++ b/drivers/staging/comedi/drivers/usbdux.c
@@ -0,0 +1,1751 @@
+/*
+ * usbdux.c
+ * Copyright (C) 2003-2014 Bernd Porr, mail@berndporr.me.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.
+ */
+
+/*
+ * Driver: usbdux
+ * Description: University of Stirling USB DAQ & INCITE Technology Limited
+ * Devices: [ITL] USB-DUX (usbdux)
+ * Author: Bernd Porr <mail@berndporr.me.uk>
+ * Updated: 10 Oct 2014
+ * Status: Stable
+ *
+ * Connection scheme for the counter at the digital port:
+ * 0=/CLK0, 1=UP/DOWN0, 2=RESET0, 4=/CLK1, 5=UP/DOWN1, 6=RESET1.
+ * The sampling rate of the counter is approximately 500Hz.
+ *
+ * Note that under USB2.0 the length of the channel list determines
+ * the max sampling rate. If you sample only one channel you get 8kHz
+ * sampling rate. If you sample two channels you get 4kHz and so on.
+ */
+
+/*
+ * I must give credit here to Chris Baugher who
+ * wrote the driver for AT-MIO-16d. I used some parts of this
+ * driver. I also must give credits to David Brownell
+ * who supported me with the USB development.
+ *
+ * Bernd Porr
+ *
+ *
+ * Revision history:
+ * 0.94: D/A output should work now with any channel list combinations
+ * 0.95: .owner commented out for kernel vers below 2.4.19
+ * sanity checks in ai/ao_cmd
+ * 0.96: trying to get it working with 2.6, moved all memory alloc to comedi's
+ * attach final USB IDs
+ * moved memory allocation completely to the corresponding comedi
+ * functions firmware upload is by fxload and no longer by comedi (due to
+ * enumeration)
+ * 0.97: USB IDs received, adjusted table
+ * 0.98: SMP, locking, memory alloc: moved all usb memory alloc
+ * to the usb subsystem and moved all comedi related memory
+ * alloc to comedi.
+ * | kernel | registration | usbdux-usb | usbdux-comedi | comedi |
+ * 0.99: USB 2.0: changed protocol to isochronous transfer
+ * IRQ transfer is too buggy and too risky in 2.0
+ * for the high speed ISO transfer is now a working version
+ * available
+ * 0.99b: Increased the iso transfer buffer for high sp.to 10 buffers. Some VIA
+ * chipsets miss out IRQs. Deeper buffering is needed.
+ * 1.00: full USB 2.0 support for the A/D converter. Now: max 8kHz sampling
+ * rate.
+ * Firmware vers 1.00 is needed for this.
+ * Two 16 bit up/down/reset counter with a sampling rate of 1kHz
+ * And loads of cleaning up, in particular streamlining the
+ * bulk transfers.
+ * 1.1: moved EP4 transfers to EP1 to make space for a PWM output on EP4
+ * 1.2: added PWM support via EP4
+ * 2.0: PWM seems to be stable and is not interfering with the other functions
+ * 2.1: changed PWM API
+ * 2.2: added firmware kernel request to fix an udev problem
+ * 2.3: corrected a bug in bulk timeouts which were far too short
+ * 2.4: fixed a bug which causes the driver to hang when it ran out of data.
+ * Thanks to Jan-Matthias Braun and Ian to spot the bug and fix it.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/input.h>
+#include <linux/fcntl.h>
+#include <linux/compiler.h>
+
+#include "../comedi_usb.h"
+
+/* constants for firmware upload and download */
+#define USBDUX_FIRMWARE "usbdux_firmware.bin"
+#define USBDUX_FIRMWARE_MAX_LEN 0x2000
+#define USBDUX_FIRMWARE_CMD 0xa0
+#define VENDOR_DIR_IN 0xc0
+#define VENDOR_DIR_OUT 0x40
+#define USBDUX_CPU_CS 0xe600
+
+/* usbdux bulk transfer commands */
+#define USBDUX_CMD_MULT_AI 0
+#define USBDUX_CMD_AO 1
+#define USBDUX_CMD_DIO_CFG 2
+#define USBDUX_CMD_DIO_BITS 3
+#define USBDUX_CMD_SINGLE_AI 4
+#define USBDUX_CMD_TIMER_RD 5
+#define USBDUX_CMD_TIMER_WR 6
+#define USBDUX_CMD_PWM_ON 7
+#define USBDUX_CMD_PWM_OFF 8
+
+/* timeout for the USB-transfer in ms */
+#define BULK_TIMEOUT 1000
+
+/* 300Hz max frequ under PWM */
+#define MIN_PWM_PERIOD ((long)(1E9/300))
+
+/* Default PWM frequency */
+#define PWM_DEFAULT_PERIOD ((long)(1E9/100))
+
+/* Size of one A/D value */
+#define SIZEADIN ((sizeof(uint16_t)))
+
+/*
+ * Size of the input-buffer IN BYTES
+ * Always multiple of 8 for 8 microframes which is needed in the highspeed mode
+ */
+#define SIZEINBUF ((8*SIZEADIN))
+
+/* 16 bytes. */
+#define SIZEINSNBUF 16
+
+/* size of one value for the D/A converter: channel and value */
+#define SIZEDAOUT ((sizeof(uint8_t)+sizeof(uint16_t)))
+
+/*
+ * Size of the output-buffer in bytes
+ * Actually only the first 4 triplets are used but for the
+ * high speed mode we need to pad it to 8 (microframes).
+ */
+#define SIZEOUTBUF ((8*SIZEDAOUT))
+
+/*
+ * Size of the buffer for the dux commands: just now max size is determined
+ * by the analogue out + command byte + panic bytes...
+ */
+#define SIZEOFDUXBUFFER ((8*SIZEDAOUT+2))
+
+/* Number of in-URBs which receive the data: min=2 */
+#define NUMOFINBUFFERSFULL 5
+
+/* Number of out-URBs which send the data: min=2 */
+#define NUMOFOUTBUFFERSFULL 5
+
+/* Number of in-URBs which receive the data: min=5 */
+/* must have more buffers due to buggy USB ctr */
+#define NUMOFINBUFFERSHIGH 10
+
+/* Number of out-URBs which send the data: min=5 */
+/* must have more buffers due to buggy USB ctr */
+#define NUMOFOUTBUFFERSHIGH 10
+
+/* number of retries to get the right dux command */
+#define RETRIES 10
+
+static const struct comedi_lrange range_usbdux_ai_range = {
+ 4, {
+ BIP_RANGE(4.096),
+ BIP_RANGE(4.096 / 2),
+ UNI_RANGE(4.096),
+ UNI_RANGE(4.096 / 2)
+ }
+};
+
+static const struct comedi_lrange range_usbdux_ao_range = {
+ 2, {
+ BIP_RANGE(4.096),
+ UNI_RANGE(4.096)
+ }
+};
+
+struct usbdux_private {
+ /* actual number of in-buffers */
+ int n_ai_urbs;
+ /* actual number of out-buffers */
+ int n_ao_urbs;
+ /* ISO-transfer handling: buffers */
+ struct urb **ai_urbs;
+ struct urb **ao_urbs;
+ /* pwm-transfer handling */
+ struct urb *pwm_urb;
+ /* PWM period */
+ unsigned int pwm_period;
+ /* PWM internal delay for the GPIF in the FX2 */
+ uint8_t pwm_delay;
+ /* size of the PWM buffer which holds the bit pattern */
+ int pwm_buf_sz;
+ /* input buffer for the ISO-transfer */
+ __le16 *in_buf;
+ /* input buffer for single insn */
+ __le16 *insn_buf;
+
+ unsigned int high_speed:1;
+ unsigned int ai_cmd_running:1;
+ unsigned int ao_cmd_running:1;
+ unsigned int pwm_cmd_running:1;
+
+ /* time between samples in units of the timer */
+ unsigned int ai_timer;
+ unsigned int ao_timer;
+ /* counter between aquisitions */
+ unsigned int ai_counter;
+ unsigned int ao_counter;
+ /* interval in frames/uframes */
+ unsigned int ai_interval;
+ /* commands */
+ uint8_t *dux_commands;
+ struct semaphore sem;
+};
+
+static void usbdux_unlink_urbs(struct urb **urbs, int num_urbs)
+{
+ int i;
+
+ for (i = 0; i < num_urbs; i++)
+ usb_kill_urb(urbs[i]);
+}
+
+static void usbdux_ai_stop(struct comedi_device *dev, int do_unlink)
+{
+ struct usbdux_private *devpriv = dev->private;
+
+ if (do_unlink && devpriv->ai_urbs)
+ usbdux_unlink_urbs(devpriv->ai_urbs, devpriv->n_ai_urbs);
+
+ devpriv->ai_cmd_running = 0;
+}
+
+static int usbdux_ai_cancel(struct comedi_device *dev,
+ struct comedi_subdevice *s)
+{
+ struct usbdux_private *devpriv = dev->private;
+
+ /* prevent other CPUs from submitting new commands just now */
+ down(&devpriv->sem);
+ /* unlink only if the urb really has been submitted */
+ usbdux_ai_stop(dev, devpriv->ai_cmd_running);
+ up(&devpriv->sem);
+
+ return 0;
+}
+
+static void usbduxsub_ai_handle_urb(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct urb *urb)
+{
+ struct usbdux_private *devpriv = dev->private;
+ struct comedi_async *async = s->async;
+ struct comedi_cmd *cmd = &async->cmd;
+ int ret;
+ int i;
+
+ devpriv->ai_counter--;
+ if (devpriv->ai_counter == 0) {
+ devpriv->ai_counter = devpriv->ai_timer;
+
+ /* get the data from the USB bus and hand it over to comedi */
+ for (i = 0; i < cmd->chanlist_len; i++) {
+ unsigned int range = CR_RANGE(cmd->chanlist[i]);
+ uint16_t val = le16_to_cpu(devpriv->in_buf[i]);
+
+ /* bipolar data is two's-complement */
+ if (comedi_range_is_bipolar(s, range))
+ val ^= ((s->maxdata + 1) >> 1);
+
+ /* transfer data */
+ if (!comedi_buf_write_samples(s, &val, 1))
+ return;
+ }
+
+ if (cmd->stop_src == TRIG_COUNT &&
+ async->scans_done >= cmd->stop_arg)
+ async->events |= COMEDI_CB_EOA;
+ }
+
+ /* if command is still running, resubmit urb */
+ if (!(async->events & COMEDI_CB_CANCEL_MASK)) {
+ urb->dev = comedi_to_usb_dev(dev);
+ ret = usb_submit_urb(urb, GFP_ATOMIC);
+ if (ret < 0) {
+ dev_err(dev->class_dev,
+ "urb resubmit failed in int-context! err=%d\n",
+ ret);
+ if (ret == -EL2NSYNC)
+ dev_err(dev->class_dev,
+ "buggy USB host controller or bug in IRQ handler!\n");
+ async->events |= COMEDI_CB_ERROR;
+ }
+ }
+}
+
+static void usbduxsub_ai_isoc_irq(struct urb *urb)
+{
+ struct comedi_device *dev = urb->context;
+ struct comedi_subdevice *s = dev->read_subdev;
+ struct comedi_async *async = s->async;
+ struct usbdux_private *devpriv = dev->private;
+
+ /* exit if not running a command, do not resubmit urb */
+ if (!devpriv->ai_cmd_running)
+ return;
+
+ switch (urb->status) {
+ case 0:
+ /* copy the result in the transfer buffer */
+ memcpy(devpriv->in_buf, urb->transfer_buffer, SIZEINBUF);
+ usbduxsub_ai_handle_urb(dev, s, urb);
+ break;
+
+ case -EILSEQ:
+ /*
+ * error in the ISOchronous data
+ * we don't copy the data into the transfer buffer
+ * and recycle the last data byte
+ */
+ dev_dbg(dev->class_dev, "CRC error in ISO IN stream\n");
+ usbduxsub_ai_handle_urb(dev, s, urb);
+ break;
+
+ case -ECONNRESET:
+ case -ENOENT:
+ case -ESHUTDOWN:
+ case -ECONNABORTED:
+ /* after an unlink command, unplug, ... etc */
+ async->events |= COMEDI_CB_ERROR;
+ break;
+
+ default:
+ /* a real error */
+ dev_err(dev->class_dev,
+ "Non-zero urb status received in ai intr context: %d\n",
+ urb->status);
+ async->events |= COMEDI_CB_ERROR;
+ break;
+ }
+
+ /*
+ * comedi_handle_events() cannot be used in this driver. The (*cancel)
+ * operation would unlink the urb.
+ */
+ if (async->events & COMEDI_CB_CANCEL_MASK)
+ usbdux_ai_stop(dev, 0);
+
+ comedi_event(dev, s);
+}
+
+static void usbdux_ao_stop(struct comedi_device *dev, int do_unlink)
+{
+ struct usbdux_private *devpriv = dev->private;
+
+ if (do_unlink && devpriv->ao_urbs)
+ usbdux_unlink_urbs(devpriv->ao_urbs, devpriv->n_ao_urbs);
+
+ devpriv->ao_cmd_running = 0;
+}
+
+static int usbdux_ao_cancel(struct comedi_device *dev,
+ struct comedi_subdevice *s)
+{
+ struct usbdux_private *devpriv = dev->private;
+
+ /* prevent other CPUs from submitting a command just now */
+ down(&devpriv->sem);
+ /* unlink only if it is really running */
+ usbdux_ao_stop(dev, devpriv->ao_cmd_running);
+ up(&devpriv->sem);
+
+ return 0;
+}
+
+static void usbduxsub_ao_handle_urb(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct urb *urb)
+{
+ struct usbdux_private *devpriv = dev->private;
+ struct comedi_async *async = s->async;
+ struct comedi_cmd *cmd = &async->cmd;
+ uint8_t *datap;
+ int ret;
+ int i;
+
+ devpriv->ao_counter--;
+ if (devpriv->ao_counter == 0) {
+ devpriv->ao_counter = devpriv->ao_timer;
+
+ if (cmd->stop_src == TRIG_COUNT &&
+ async->scans_done >= cmd->stop_arg) {
+ async->events |= COMEDI_CB_EOA;
+ return;
+ }
+
+ /* transmit data to the USB bus */
+ datap = urb->transfer_buffer;
+ *datap++ = cmd->chanlist_len;
+ for (i = 0; i < cmd->chanlist_len; i++) {
+ unsigned int chan = CR_CHAN(cmd->chanlist[i]);
+ unsigned short val;
+
+ if (!comedi_buf_read_samples(s, &val, 1)) {
+ dev_err(dev->class_dev, "buffer underflow\n");
+ async->events |= COMEDI_CB_OVERFLOW;
+ return;
+ }
+
+ /* pointer to the DA */
+ *datap++ = val & 0xff;
+ *datap++ = (val >> 8) & 0xff;
+ *datap++ = chan << 6;
+ s->readback[chan] = val;
+ }
+ }
+
+ /* if command is still running, resubmit urb for BULK transfer */
+ if (!(async->events & COMEDI_CB_CANCEL_MASK)) {
+ urb->transfer_buffer_length = SIZEOUTBUF;
+ urb->dev = comedi_to_usb_dev(dev);
+ urb->status = 0;
+ if (devpriv->high_speed)
+ urb->interval = 8; /* uframes */
+ else
+ urb->interval = 1; /* frames */
+ urb->number_of_packets = 1;
+ urb->iso_frame_desc[0].offset = 0;
+ urb->iso_frame_desc[0].length = SIZEOUTBUF;
+ urb->iso_frame_desc[0].status = 0;
+ ret = usb_submit_urb(urb, GFP_ATOMIC);
+ if (ret < 0) {
+ dev_err(dev->class_dev,
+ "ao urb resubm failed in int-cont. ret=%d",
+ ret);
+ if (ret == -EL2NSYNC)
+ dev_err(dev->class_dev,
+ "buggy USB host controller or bug in IRQ handling!\n");
+ async->events |= COMEDI_CB_ERROR;
+ }
+ }
+}
+
+static void usbduxsub_ao_isoc_irq(struct urb *urb)
+{
+ struct comedi_device *dev = urb->context;
+ struct comedi_subdevice *s = dev->write_subdev;
+ struct comedi_async *async = s->async;
+ struct usbdux_private *devpriv = dev->private;
+
+ /* exit if not running a command, do not resubmit urb */
+ if (!devpriv->ao_cmd_running)
+ return;
+
+ switch (urb->status) {
+ case 0:
+ usbduxsub_ao_handle_urb(dev, s, urb);
+ break;
+
+ case -ECONNRESET:
+ case -ENOENT:
+ case -ESHUTDOWN:
+ case -ECONNABORTED:
+ /* after an unlink command, unplug, ... etc */
+ async->events |= COMEDI_CB_ERROR;
+ break;
+
+ default:
+ /* a real error */
+ dev_err(dev->class_dev,
+ "Non-zero urb status received in ao intr context: %d\n",
+ urb->status);
+ async->events |= COMEDI_CB_ERROR;
+ break;
+ }
+
+ /*
+ * comedi_handle_events() cannot be used in this driver. The (*cancel)
+ * operation would unlink the urb.
+ */
+ if (async->events & COMEDI_CB_CANCEL_MASK)
+ usbdux_ao_stop(dev, 0);
+
+ comedi_event(dev, s);
+}
+
+static int usbdux_submit_urbs(struct comedi_device *dev,
+ struct urb **urbs, int num_urbs,
+ int input_urb)
+{
+ struct usb_device *usb = comedi_to_usb_dev(dev);
+ struct usbdux_private *devpriv = dev->private;
+ struct urb *urb;
+ int ret;
+ int i;
+
+ /* Submit all URBs and start the transfer on the bus */
+ for (i = 0; i < num_urbs; i++) {
+ urb = urbs[i];
+
+ /* in case of a resubmission after an unlink... */
+ if (input_urb)
+ urb->interval = devpriv->ai_interval;
+ urb->context = dev;
+ urb->dev = usb;
+ urb->status = 0;
+ urb->transfer_flags = URB_ISO_ASAP;
+
+ ret = usb_submit_urb(urb, GFP_ATOMIC);
+ if (ret)
+ return ret;
+ }
+ return 0;
+}
+
+static int usbdux_ai_cmdtest(struct comedi_device *dev,
+ struct comedi_subdevice *s, struct comedi_cmd *cmd)
+{
+ struct usbdux_private *this_usbduxsub = dev->private;
+ int err = 0, i;
+ unsigned int tmp_timer;
+
+ /* Step 1 : check if triggers are trivially valid */
+
+ err |= comedi_check_trigger_src(&cmd->start_src, TRIG_NOW | TRIG_INT);
+ err |= comedi_check_trigger_src(&cmd->scan_begin_src, TRIG_TIMER);
+ err |= comedi_check_trigger_src(&cmd->convert_src, TRIG_NOW);
+ err |= comedi_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
+ err |= comedi_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE);
+
+ if (err)
+ return 1;
+
+ /* Step 2a : make sure trigger sources are unique */
+
+ err |= comedi_check_trigger_is_unique(cmd->start_src);
+ err |= comedi_check_trigger_is_unique(cmd->stop_src);
+
+ /* Step 2b : and mutually compatible */
+
+ if (err)
+ return 2;
+
+ /* Step 3: check if arguments are trivially valid */
+
+ err |= comedi_check_trigger_arg_is(&cmd->start_arg, 0);
+
+ if (cmd->scan_begin_src == TRIG_FOLLOW) /* internal trigger */
+ err |= comedi_check_trigger_arg_is(&cmd->scan_begin_arg, 0);
+
+ if (cmd->scan_begin_src == TRIG_TIMER) {
+ if (this_usbduxsub->high_speed) {
+ /*
+ * In high speed mode microframes are possible.
+ * However, during one microframe we can roughly
+ * sample one channel. Thus, the more channels
+ * are in the channel list the more time we need.
+ */
+ i = 1;
+ /* find a power of 2 for the number of channels */
+ while (i < (cmd->chanlist_len))
+ i = i * 2;
+
+ err |= comedi_check_trigger_arg_min(&cmd->
+ scan_begin_arg,
+ 1000000 / 8 * i);
+ /* now calc the real sampling rate with all the
+ * rounding errors */
+ tmp_timer =
+ ((unsigned int)(cmd->scan_begin_arg / 125000)) *
+ 125000;
+ } else {
+ /* full speed */
+ /* 1kHz scans every USB frame */
+ err |= comedi_check_trigger_arg_min(&cmd->
+ scan_begin_arg,
+ 1000000);
+ /*
+ * calc the real sampling rate with the rounding errors
+ */
+ tmp_timer = ((unsigned int)(cmd->scan_begin_arg /
+ 1000000)) * 1000000;
+ }
+ err |= comedi_check_trigger_arg_is(&cmd->scan_begin_arg,
+ tmp_timer);
+ }
+
+ err |= comedi_check_trigger_arg_is(&cmd->scan_end_arg,
+ cmd->chanlist_len);
+
+ if (cmd->stop_src == TRIG_COUNT)
+ err |= comedi_check_trigger_arg_min(&cmd->stop_arg, 1);
+ else /* TRIG_NONE */
+ err |= comedi_check_trigger_arg_is(&cmd->stop_arg, 0);
+
+ if (err)
+ return 3;
+
+ return 0;
+}
+
+/*
+ * creates the ADC command for the MAX1271
+ * range is the range value from comedi
+ */
+static uint8_t create_adc_command(unsigned int chan, unsigned int range)
+{
+ uint8_t p = (range <= 1);
+ uint8_t r = ((range % 2) == 0);
+
+ return (chan << 4) | ((p == 1) << 2) | ((r == 1) << 3);
+}
+
+static int send_dux_commands(struct comedi_device *dev, unsigned int cmd_type)
+{
+ struct usb_device *usb = comedi_to_usb_dev(dev);
+ struct usbdux_private *devpriv = dev->private;
+ int nsent;
+
+ devpriv->dux_commands[0] = cmd_type;
+
+ return usb_bulk_msg(usb, usb_sndbulkpipe(usb, 1),
+ devpriv->dux_commands, SIZEOFDUXBUFFER,
+ &nsent, BULK_TIMEOUT);
+}
+
+static int receive_dux_commands(struct comedi_device *dev, unsigned int command)
+{
+ struct usb_device *usb = comedi_to_usb_dev(dev);
+ struct usbdux_private *devpriv = dev->private;
+ int ret;
+ int nrec;
+ int i;
+
+ for (i = 0; i < RETRIES; i++) {
+ ret = usb_bulk_msg(usb, usb_rcvbulkpipe(usb, 8),
+ devpriv->insn_buf, SIZEINSNBUF,
+ &nrec, BULK_TIMEOUT);
+ if (ret < 0)
+ return ret;
+ if (le16_to_cpu(devpriv->insn_buf[0]) == command)
+ return ret;
+ }
+ /* command not received */
+ return -EFAULT;
+}
+
+static int usbdux_ai_inttrig(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ unsigned int trig_num)
+{
+ struct usbdux_private *devpriv = dev->private;
+ struct comedi_cmd *cmd = &s->async->cmd;
+ int ret;
+
+ if (trig_num != cmd->start_arg)
+ return -EINVAL;
+
+ down(&devpriv->sem);
+
+ if (!devpriv->ai_cmd_running) {
+ devpriv->ai_cmd_running = 1;
+ ret = usbdux_submit_urbs(dev, devpriv->ai_urbs,
+ devpriv->n_ai_urbs, 1);
+ if (ret < 0) {
+ devpriv->ai_cmd_running = 0;
+ goto ai_trig_exit;
+ }
+ s->async->inttrig = NULL;
+ } else {
+ ret = -EBUSY;
+ }
+
+ai_trig_exit:
+ up(&devpriv->sem);
+ return ret;
+}
+
+static int usbdux_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
+{
+ struct usbdux_private *devpriv = dev->private;
+ struct comedi_cmd *cmd = &s->async->cmd;
+ int len = cmd->chanlist_len;
+ int ret = -EBUSY;
+ int i;
+
+ /* block other CPUs from starting an ai_cmd */
+ down(&devpriv->sem);
+
+ if (devpriv->ai_cmd_running)
+ goto ai_cmd_exit;
+
+ devpriv->dux_commands[1] = len;
+ for (i = 0; i < len; ++i) {
+ unsigned int chan = CR_CHAN(cmd->chanlist[i]);
+ unsigned int range = CR_RANGE(cmd->chanlist[i]);
+
+ devpriv->dux_commands[i + 2] = create_adc_command(chan, range);
+ }
+
+ ret = send_dux_commands(dev, USBDUX_CMD_MULT_AI);
+ if (ret < 0)
+ goto ai_cmd_exit;
+
+ if (devpriv->high_speed) {
+ /*
+ * every channel gets a time window of 125us. Thus, if we
+ * sample all 8 channels we need 1ms. If we sample only one
+ * channel we need only 125us
+ */
+ devpriv->ai_interval = 1;
+ /* find a power of 2 for the interval */
+ while (devpriv->ai_interval < len)
+ devpriv->ai_interval *= 2;
+
+ devpriv->ai_timer = cmd->scan_begin_arg /
+ (125000 * devpriv->ai_interval);
+ } else {
+ /* interval always 1ms */
+ devpriv->ai_interval = 1;
+ devpriv->ai_timer = cmd->scan_begin_arg / 1000000;
+ }
+ if (devpriv->ai_timer < 1) {
+ ret = -EINVAL;
+ goto ai_cmd_exit;
+ }
+
+ devpriv->ai_counter = devpriv->ai_timer;
+
+ if (cmd->start_src == TRIG_NOW) {
+ /* enable this acquisition operation */
+ devpriv->ai_cmd_running = 1;
+ ret = usbdux_submit_urbs(dev, devpriv->ai_urbs,
+ devpriv->n_ai_urbs, 1);
+ if (ret < 0) {
+ devpriv->ai_cmd_running = 0;
+ /* fixme: unlink here?? */
+ goto ai_cmd_exit;
+ }
+ s->async->inttrig = NULL;
+ } else {
+ /* TRIG_INT */
+ /* don't enable the acquision operation */
+ /* wait for an internal signal */
+ s->async->inttrig = usbdux_ai_inttrig;
+ }
+
+ai_cmd_exit:
+ up(&devpriv->sem);
+
+ return ret;
+}
+
+/* Mode 0 is used to get a single conversion on demand */
+static int usbdux_ai_insn_read(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ struct usbdux_private *devpriv = dev->private;
+ unsigned int chan = CR_CHAN(insn->chanspec);
+ unsigned int range = CR_RANGE(insn->chanspec);
+ unsigned int val;
+ int ret = -EBUSY;
+ int i;
+
+ down(&devpriv->sem);
+
+ if (devpriv->ai_cmd_running)
+ goto ai_read_exit;
+
+ /* set command for the first channel */
+ devpriv->dux_commands[1] = create_adc_command(chan, range);
+
+ /* adc commands */
+ ret = send_dux_commands(dev, USBDUX_CMD_SINGLE_AI);
+ if (ret < 0)
+ goto ai_read_exit;
+
+ for (i = 0; i < insn->n; i++) {
+ ret = receive_dux_commands(dev, USBDUX_CMD_SINGLE_AI);
+ if (ret < 0)
+ goto ai_read_exit;
+
+ val = le16_to_cpu(devpriv->insn_buf[1]);
+
+ /* bipolar data is two's-complement */
+ if (comedi_range_is_bipolar(s, range))
+ val ^= ((s->maxdata + 1) >> 1);
+
+ data[i] = val;
+ }
+
+ai_read_exit:
+ up(&devpriv->sem);
+
+ return ret ? ret : insn->n;
+}
+
+static int usbdux_ao_insn_read(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ struct usbdux_private *devpriv = dev->private;
+ int ret;
+
+ down(&devpriv->sem);
+ ret = comedi_readback_insn_read(dev, s, insn, data);
+ up(&devpriv->sem);
+
+ return ret;
+}
+
+static int usbdux_ao_insn_write(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ struct usbdux_private *devpriv = dev->private;
+ unsigned int chan = CR_CHAN(insn->chanspec);
+ unsigned int val = s->readback[chan];
+ __le16 *p = (__le16 *)&devpriv->dux_commands[2];
+ int ret = -EBUSY;
+ int i;
+
+ down(&devpriv->sem);
+
+ if (devpriv->ao_cmd_running)
+ goto ao_write_exit;
+
+ /* number of channels: 1 */
+ devpriv->dux_commands[1] = 1;
+ /* channel number */
+ devpriv->dux_commands[4] = chan << 6;
+
+ for (i = 0; i < insn->n; i++) {
+ val = data[i];
+
+ /* one 16 bit value */
+ *p = cpu_to_le16(val);
+
+ ret = send_dux_commands(dev, USBDUX_CMD_AO);
+ if (ret < 0)
+ goto ao_write_exit;
+
+ s->readback[chan] = val;
+ }
+
+ao_write_exit:
+ up(&devpriv->sem);
+
+ return ret ? ret : insn->n;
+}
+
+static int usbdux_ao_inttrig(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ unsigned int trig_num)
+{
+ struct usbdux_private *devpriv = dev->private;
+ struct comedi_cmd *cmd = &s->async->cmd;
+ int ret;
+
+ if (trig_num != cmd->start_arg)
+ return -EINVAL;
+
+ down(&devpriv->sem);
+
+ if (!devpriv->ao_cmd_running) {
+ devpriv->ao_cmd_running = 1;
+ ret = usbdux_submit_urbs(dev, devpriv->ao_urbs,
+ devpriv->n_ao_urbs, 0);
+ if (ret < 0) {
+ devpriv->ao_cmd_running = 0;
+ goto ao_trig_exit;
+ }
+ s->async->inttrig = NULL;
+ } else {
+ ret = -EBUSY;
+ }
+
+ao_trig_exit:
+ up(&devpriv->sem);
+ return ret;
+}
+
+static int usbdux_ao_cmdtest(struct comedi_device *dev,
+ struct comedi_subdevice *s, struct comedi_cmd *cmd)
+{
+ struct usbdux_private *this_usbduxsub = dev->private;
+ int err = 0;
+ unsigned int flags;
+
+ if (!this_usbduxsub)
+ return -EFAULT;
+
+ /* Step 1 : check if triggers are trivially valid */
+
+ err |= comedi_check_trigger_src(&cmd->start_src, TRIG_NOW | TRIG_INT);
+
+ if (0) { /* (this_usbduxsub->high_speed) */
+ /* the sampling rate is set by the coversion rate */
+ flags = TRIG_FOLLOW;
+ } else {
+ /* start a new scan (output at once) with a timer */
+ flags = TRIG_TIMER;
+ }
+ err |= comedi_check_trigger_src(&cmd->scan_begin_src, flags);
+
+ if (0) { /* (this_usbduxsub->high_speed) */
+ /*
+ * in usb-2.0 only one conversion it transmitted
+ * but with 8kHz/n
+ */
+ flags = TRIG_TIMER;
+ } else {
+ /*
+ * all conversion events happen simultaneously with
+ * a rate of 1kHz/n
+ */
+ flags = TRIG_NOW;
+ }
+ err |= comedi_check_trigger_src(&cmd->convert_src, flags);
+
+ err |= comedi_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
+ err |= comedi_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE);
+
+ if (err)
+ return 1;
+
+ /* Step 2a : make sure trigger sources are unique */
+
+ err |= comedi_check_trigger_is_unique(cmd->start_src);
+ err |= comedi_check_trigger_is_unique(cmd->stop_src);
+
+ /* Step 2b : and mutually compatible */
+
+ if (err)
+ return 2;
+
+ /* Step 3: check if arguments are trivially valid */
+
+ err |= comedi_check_trigger_arg_is(&cmd->start_arg, 0);
+
+ if (cmd->scan_begin_src == TRIG_FOLLOW) /* internal trigger */
+ err |= comedi_check_trigger_arg_is(&cmd->scan_begin_arg, 0);
+
+ if (cmd->scan_begin_src == TRIG_TIMER) {
+ err |= comedi_check_trigger_arg_min(&cmd->scan_begin_arg,
+ 1000000);
+ }
+
+ /* not used now, is for later use */
+ if (cmd->convert_src == TRIG_TIMER)
+ err |= comedi_check_trigger_arg_min(&cmd->convert_arg, 125000);
+
+ err |= comedi_check_trigger_arg_is(&cmd->scan_end_arg,
+ cmd->chanlist_len);
+
+ if (cmd->stop_src == TRIG_COUNT)
+ err |= comedi_check_trigger_arg_min(&cmd->stop_arg, 1);
+ else /* TRIG_NONE */
+ err |= comedi_check_trigger_arg_is(&cmd->stop_arg, 0);
+
+ if (err)
+ return 3;
+
+ return 0;
+}
+
+static int usbdux_ao_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
+{
+ struct usbdux_private *devpriv = dev->private;
+ struct comedi_cmd *cmd = &s->async->cmd;
+ int ret = -EBUSY;
+
+ down(&devpriv->sem);
+
+ if (devpriv->ao_cmd_running)
+ goto ao_cmd_exit;
+
+ /* we count in steps of 1ms (125us) */
+ /* 125us mode not used yet */
+ if (0) { /* (devpriv->high_speed) */
+ /* 125us */
+ /* timing of the conversion itself: every 125 us */
+ devpriv->ao_timer = cmd->convert_arg / 125000;
+ } else {
+ /* 1ms */
+ /* timing of the scan: we get all channels at once */
+ devpriv->ao_timer = cmd->scan_begin_arg / 1000000;
+ if (devpriv->ao_timer < 1) {
+ ret = -EINVAL;
+ goto ao_cmd_exit;
+ }
+ }
+
+ devpriv->ao_counter = devpriv->ao_timer;
+
+ if (cmd->start_src == TRIG_NOW) {
+ /* enable this acquisition operation */
+ devpriv->ao_cmd_running = 1;
+ ret = usbdux_submit_urbs(dev, devpriv->ao_urbs,
+ devpriv->n_ao_urbs, 0);
+ if (ret < 0) {
+ devpriv->ao_cmd_running = 0;
+ /* fixme: unlink here?? */
+ goto ao_cmd_exit;
+ }
+ s->async->inttrig = NULL;
+ } else {
+ /* TRIG_INT */
+ /* submit the urbs later */
+ /* wait for an internal signal */
+ s->async->inttrig = usbdux_ao_inttrig;
+ }
+
+ao_cmd_exit:
+ up(&devpriv->sem);
+
+ return ret;
+}
+
+static int usbdux_dio_insn_config(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ int ret;
+
+ ret = comedi_dio_insn_config(dev, s, insn, data, 0);
+ if (ret)
+ return ret;
+
+ /*
+ * We don't tell the firmware here as it would take 8 frames
+ * to submit the information. We do it in the insn_bits.
+ */
+ return insn->n;
+}
+
+static int usbdux_dio_insn_bits(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ struct usbdux_private *devpriv = dev->private;
+ int ret;
+
+ down(&devpriv->sem);
+
+ comedi_dio_update_state(s, data);
+
+ /* Always update the hardware. See the (*insn_config). */
+ devpriv->dux_commands[1] = s->io_bits;
+ devpriv->dux_commands[2] = s->state;
+
+ /*
+ * This command also tells the firmware to return
+ * the digital input lines.
+ */
+ ret = send_dux_commands(dev, USBDUX_CMD_DIO_BITS);
+ if (ret < 0)
+ goto dio_exit;
+ ret = receive_dux_commands(dev, USBDUX_CMD_DIO_BITS);
+ if (ret < 0)
+ goto dio_exit;
+
+ data[1] = le16_to_cpu(devpriv->insn_buf[1]);
+
+dio_exit:
+ up(&devpriv->sem);
+
+ return ret ? ret : insn->n;
+}
+
+static int usbdux_counter_read(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ struct usbdux_private *devpriv = dev->private;
+ unsigned int chan = CR_CHAN(insn->chanspec);
+ int ret = 0;
+ int i;
+
+ down(&devpriv->sem);
+
+ for (i = 0; i < insn->n; i++) {
+ ret = send_dux_commands(dev, USBDUX_CMD_TIMER_RD);
+ if (ret < 0)
+ goto counter_read_exit;
+ ret = receive_dux_commands(dev, USBDUX_CMD_TIMER_RD);
+ if (ret < 0)
+ goto counter_read_exit;
+
+ data[i] = le16_to_cpu(devpriv->insn_buf[chan + 1]);
+ }
+
+counter_read_exit:
+ up(&devpriv->sem);
+
+ return ret ? ret : insn->n;
+}
+
+static int usbdux_counter_write(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ struct usbdux_private *devpriv = dev->private;
+ unsigned int chan = CR_CHAN(insn->chanspec);
+ __le16 *p = (__le16 *)&devpriv->dux_commands[2];
+ int ret = 0;
+ int i;
+
+ down(&devpriv->sem);
+
+ devpriv->dux_commands[1] = chan;
+
+ for (i = 0; i < insn->n; i++) {
+ *p = cpu_to_le16(data[i]);
+
+ ret = send_dux_commands(dev, USBDUX_CMD_TIMER_WR);
+ if (ret < 0)
+ break;
+ }
+
+ up(&devpriv->sem);
+
+ return ret ? ret : insn->n;
+}
+
+static int usbdux_counter_config(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn, unsigned int *data)
+{
+ /* nothing to do so far */
+ return 2;
+}
+
+static void usbduxsub_unlink_pwm_urbs(struct comedi_device *dev)
+{
+ struct usbdux_private *devpriv = dev->private;
+
+ usb_kill_urb(devpriv->pwm_urb);
+}
+
+static void usbdux_pwm_stop(struct comedi_device *dev, int do_unlink)
+{
+ struct usbdux_private *devpriv = dev->private;
+
+ if (do_unlink)
+ usbduxsub_unlink_pwm_urbs(dev);
+
+ devpriv->pwm_cmd_running = 0;
+}
+
+static int usbdux_pwm_cancel(struct comedi_device *dev,
+ struct comedi_subdevice *s)
+{
+ struct usbdux_private *devpriv = dev->private;
+ int ret;
+
+ down(&devpriv->sem);
+ /* unlink only if it is really running */
+ usbdux_pwm_stop(dev, devpriv->pwm_cmd_running);
+ ret = send_dux_commands(dev, USBDUX_CMD_PWM_OFF);
+ up(&devpriv->sem);
+
+ return ret;
+}
+
+static void usbduxsub_pwm_irq(struct urb *urb)
+{
+ struct comedi_device *dev = urb->context;
+ struct usbdux_private *devpriv = dev->private;
+ int ret;
+
+ switch (urb->status) {
+ case 0:
+ /* success */
+ break;
+
+ case -ECONNRESET:
+ case -ENOENT:
+ case -ESHUTDOWN:
+ case -ECONNABORTED:
+ /*
+ * after an unlink command, unplug, ... etc
+ * no unlink needed here. Already shutting down.
+ */
+ if (devpriv->pwm_cmd_running)
+ usbdux_pwm_stop(dev, 0);
+
+ return;
+
+ default:
+ /* a real error */
+ if (devpriv->pwm_cmd_running) {
+ dev_err(dev->class_dev,
+ "Non-zero urb status received in pwm intr context: %d\n",
+ urb->status);
+ usbdux_pwm_stop(dev, 0);
+ }
+ return;
+ }
+
+ /* are we actually running? */
+ if (!devpriv->pwm_cmd_running)
+ return;
+
+ urb->transfer_buffer_length = devpriv->pwm_buf_sz;
+ urb->dev = comedi_to_usb_dev(dev);
+ urb->status = 0;
+ if (devpriv->pwm_cmd_running) {
+ ret = usb_submit_urb(urb, GFP_ATOMIC);
+ if (ret < 0) {
+ dev_err(dev->class_dev,
+ "pwm urb resubm failed in int-cont. ret=%d",
+ ret);
+ if (ret == -EL2NSYNC)
+ dev_err(dev->class_dev,
+ "buggy USB host controller or bug in IRQ handling!\n");
+
+ /* don't do an unlink here */
+ usbdux_pwm_stop(dev, 0);
+ }
+ }
+}
+
+static int usbduxsub_submit_pwm_urbs(struct comedi_device *dev)
+{
+ struct usb_device *usb = comedi_to_usb_dev(dev);
+ struct usbdux_private *devpriv = dev->private;
+ struct urb *urb = devpriv->pwm_urb;
+
+ /* in case of a resubmission after an unlink... */
+ usb_fill_bulk_urb(urb, usb, usb_sndbulkpipe(usb, 4),
+ urb->transfer_buffer,
+ devpriv->pwm_buf_sz,
+ usbduxsub_pwm_irq,
+ dev);
+
+ return usb_submit_urb(urb, GFP_ATOMIC);
+}
+
+static int usbdux_pwm_period(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ unsigned int period)
+{
+ struct usbdux_private *devpriv = dev->private;
+ int fx2delay = 255;
+
+ if (period < MIN_PWM_PERIOD)
+ return -EAGAIN;
+
+ fx2delay = (period / (6 * 512 * 1000 / 33)) - 6;
+ if (fx2delay > 255)
+ return -EAGAIN;
+
+ devpriv->pwm_delay = fx2delay;
+ devpriv->pwm_period = period;
+
+ return 0;
+}
+
+static int usbdux_pwm_start(struct comedi_device *dev,
+ struct comedi_subdevice *s)
+{
+ struct usbdux_private *devpriv = dev->private;
+ int ret = 0;
+
+ down(&devpriv->sem);
+
+ if (devpriv->pwm_cmd_running)
+ goto pwm_start_exit;
+
+ devpriv->dux_commands[1] = devpriv->pwm_delay;
+ ret = send_dux_commands(dev, USBDUX_CMD_PWM_ON);
+ if (ret < 0)
+ goto pwm_start_exit;
+
+ /* initialise the buffer */
+ memset(devpriv->pwm_urb->transfer_buffer, 0, devpriv->pwm_buf_sz);
+
+ devpriv->pwm_cmd_running = 1;
+ ret = usbduxsub_submit_pwm_urbs(dev);
+ if (ret < 0)
+ devpriv->pwm_cmd_running = 0;
+
+pwm_start_exit:
+ up(&devpriv->sem);
+
+ return ret;
+}
+
+static void usbdux_pwm_pattern(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ unsigned int chan,
+ unsigned int value,
+ unsigned int sign)
+{
+ struct usbdux_private *devpriv = dev->private;
+ char pwm_mask = (1 << chan); /* DIO bit for the PWM data */
+ char sgn_mask = (16 << chan); /* DIO bit for the sign */
+ char *buf = (char *)(devpriv->pwm_urb->transfer_buffer);
+ int szbuf = devpriv->pwm_buf_sz;
+ int i;
+
+ for (i = 0; i < szbuf; i++) {
+ char c = *buf;
+
+ c &= ~pwm_mask;
+ if (i < value)
+ c |= pwm_mask;
+ if (!sign)
+ c &= ~sgn_mask;
+ else
+ c |= sgn_mask;
+ *buf++ = c;
+ }
+}
+
+static int usbdux_pwm_write(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ unsigned int chan = CR_CHAN(insn->chanspec);
+
+ /*
+ * It doesn't make sense to support more than one value here
+ * because it would just overwrite the PWM buffer.
+ */
+ if (insn->n != 1)
+ return -EINVAL;
+
+ /*
+ * The sign is set via a special INSN only, this gives us 8 bits
+ * for normal operation, sign is 0 by default.
+ */
+ usbdux_pwm_pattern(dev, s, chan, data[0], 0);
+
+ return insn->n;
+}
+
+static int usbdux_pwm_config(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ struct usbdux_private *devpriv = dev->private;
+ unsigned int chan = CR_CHAN(insn->chanspec);
+
+ switch (data[0]) {
+ case INSN_CONFIG_ARM:
+ /*
+ * if not zero the PWM is limited to a certain time which is
+ * not supported here
+ */
+ if (data[1] != 0)
+ return -EINVAL;
+ return usbdux_pwm_start(dev, s);
+ case INSN_CONFIG_DISARM:
+ return usbdux_pwm_cancel(dev, s);
+ case INSN_CONFIG_GET_PWM_STATUS:
+ data[1] = devpriv->pwm_cmd_running;
+ return 0;
+ case INSN_CONFIG_PWM_SET_PERIOD:
+ return usbdux_pwm_period(dev, s, data[1]);
+ case INSN_CONFIG_PWM_GET_PERIOD:
+ data[1] = devpriv->pwm_period;
+ return 0;
+ case INSN_CONFIG_PWM_SET_H_BRIDGE:
+ /*
+ * data[1] = value
+ * data[2] = sign (for a relay)
+ */
+ usbdux_pwm_pattern(dev, s, chan, data[1], (data[2] != 0));
+ return 0;
+ case INSN_CONFIG_PWM_GET_H_BRIDGE:
+ /* values are not kept in this driver, nothing to return here */
+ return -EINVAL;
+ }
+ return -EINVAL;
+}
+
+static int usbdux_firmware_upload(struct comedi_device *dev,
+ const u8 *data, size_t size,
+ unsigned long context)
+{
+ struct usb_device *usb = comedi_to_usb_dev(dev);
+ uint8_t *buf;
+ uint8_t *tmp;
+ int ret;
+
+ if (!data)
+ return 0;
+
+ if (size > USBDUX_FIRMWARE_MAX_LEN) {
+ dev_err(dev->class_dev,
+ "usbdux firmware binary it too large for FX2.\n");
+ return -ENOMEM;
+ }
+
+ /* we generate a local buffer for the firmware */
+ buf = kmemdup(data, size, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ /* we need a malloc'ed buffer for usb_control_msg() */
+ tmp = kmalloc(1, GFP_KERNEL);
+ if (!tmp) {
+ kfree(buf);
+ return -ENOMEM;
+ }
+
+ /* stop the current firmware on the device */
+ *tmp = 1; /* 7f92 to one */
+ ret = usb_control_msg(usb, usb_sndctrlpipe(usb, 0),
+ USBDUX_FIRMWARE_CMD,
+ VENDOR_DIR_OUT,
+ USBDUX_CPU_CS, 0x0000,
+ tmp, 1,
+ BULK_TIMEOUT);
+ if (ret < 0) {
+ dev_err(dev->class_dev, "can not stop firmware\n");
+ goto done;
+ }
+
+ /* upload the new firmware to the device */
+ ret = usb_control_msg(usb, usb_sndctrlpipe(usb, 0),
+ USBDUX_FIRMWARE_CMD,
+ VENDOR_DIR_OUT,
+ 0, 0x0000,
+ buf, size,
+ BULK_TIMEOUT);
+ if (ret < 0) {
+ dev_err(dev->class_dev, "firmware upload failed\n");
+ goto done;
+ }
+
+ /* start the new firmware on the device */
+ *tmp = 0; /* 7f92 to zero */
+ ret = usb_control_msg(usb, usb_sndctrlpipe(usb, 0),
+ USBDUX_FIRMWARE_CMD,
+ VENDOR_DIR_OUT,
+ USBDUX_CPU_CS, 0x0000,
+ tmp, 1,
+ BULK_TIMEOUT);
+ if (ret < 0)
+ dev_err(dev->class_dev, "can not start firmware\n");
+
+done:
+ kfree(tmp);
+ kfree(buf);
+ return ret;
+}
+
+static int usbdux_alloc_usb_buffers(struct comedi_device *dev)
+{
+ struct usb_device *usb = comedi_to_usb_dev(dev);
+ struct usbdux_private *devpriv = dev->private;
+ struct urb *urb;
+ int i;
+
+ devpriv->dux_commands = kzalloc(SIZEOFDUXBUFFER, GFP_KERNEL);
+ devpriv->in_buf = kzalloc(SIZEINBUF, GFP_KERNEL);
+ devpriv->insn_buf = kzalloc(SIZEINSNBUF, GFP_KERNEL);
+ devpriv->ai_urbs = kcalloc(devpriv->n_ai_urbs, sizeof(void *),
+ GFP_KERNEL);
+ devpriv->ao_urbs = kcalloc(devpriv->n_ao_urbs, sizeof(void *),
+ GFP_KERNEL);
+ if (!devpriv->dux_commands || !devpriv->in_buf || !devpriv->insn_buf ||
+ !devpriv->ai_urbs || !devpriv->ao_urbs)
+ return -ENOMEM;
+
+ for (i = 0; i < devpriv->n_ai_urbs; i++) {
+ /* one frame: 1ms */
+ urb = usb_alloc_urb(1, GFP_KERNEL);
+ if (!urb)
+ return -ENOMEM;
+ devpriv->ai_urbs[i] = urb;
+
+ urb->dev = usb;
+ urb->context = dev;
+ urb->pipe = usb_rcvisocpipe(usb, 6);
+ urb->transfer_flags = URB_ISO_ASAP;
+ urb->transfer_buffer = kzalloc(SIZEINBUF, GFP_KERNEL);
+ if (!urb->transfer_buffer)
+ return -ENOMEM;
+
+ urb->complete = usbduxsub_ai_isoc_irq;
+ urb->number_of_packets = 1;
+ urb->transfer_buffer_length = SIZEINBUF;
+ urb->iso_frame_desc[0].offset = 0;
+ urb->iso_frame_desc[0].length = SIZEINBUF;
+ }
+
+ for (i = 0; i < devpriv->n_ao_urbs; i++) {
+ /* one frame: 1ms */
+ urb = usb_alloc_urb(1, GFP_KERNEL);
+ if (!urb)
+ return -ENOMEM;
+ devpriv->ao_urbs[i] = urb;
+
+ urb->dev = usb;
+ urb->context = dev;
+ urb->pipe = usb_sndisocpipe(usb, 2);
+ urb->transfer_flags = URB_ISO_ASAP;
+ urb->transfer_buffer = kzalloc(SIZEOUTBUF, GFP_KERNEL);
+ if (!urb->transfer_buffer)
+ return -ENOMEM;
+
+ urb->complete = usbduxsub_ao_isoc_irq;
+ urb->number_of_packets = 1;
+ urb->transfer_buffer_length = SIZEOUTBUF;
+ urb->iso_frame_desc[0].offset = 0;
+ urb->iso_frame_desc[0].length = SIZEOUTBUF;
+ if (devpriv->high_speed)
+ urb->interval = 8; /* uframes */
+ else
+ urb->interval = 1; /* frames */
+ }
+
+ /* pwm */
+ if (devpriv->pwm_buf_sz) {
+ urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!urb)
+ return -ENOMEM;
+ devpriv->pwm_urb = urb;
+
+ /* max bulk ep size in high speed */
+ urb->transfer_buffer = kzalloc(devpriv->pwm_buf_sz,
+ GFP_KERNEL);
+ if (!urb->transfer_buffer)
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+static void usbdux_free_usb_buffers(struct comedi_device *dev)
+{
+ struct usbdux_private *devpriv = dev->private;
+ struct urb *urb;
+ int i;
+
+ urb = devpriv->pwm_urb;
+ if (urb) {
+ kfree(urb->transfer_buffer);
+ usb_free_urb(urb);
+ }
+ if (devpriv->ao_urbs) {
+ for (i = 0; i < devpriv->n_ao_urbs; i++) {
+ urb = devpriv->ao_urbs[i];
+ if (urb) {
+ kfree(urb->transfer_buffer);
+ usb_free_urb(urb);
+ }
+ }
+ kfree(devpriv->ao_urbs);
+ }
+ if (devpriv->ai_urbs) {
+ for (i = 0; i < devpriv->n_ai_urbs; i++) {
+ urb = devpriv->ai_urbs[i];
+ if (urb) {
+ kfree(urb->transfer_buffer);
+ usb_free_urb(urb);
+ }
+ }
+ kfree(devpriv->ai_urbs);
+ }
+ kfree(devpriv->insn_buf);
+ kfree(devpriv->in_buf);
+ kfree(devpriv->dux_commands);
+}
+
+static int usbdux_auto_attach(struct comedi_device *dev,
+ unsigned long context_unused)
+{
+ struct usb_interface *intf = comedi_to_usb_interface(dev);
+ struct usb_device *usb = comedi_to_usb_dev(dev);
+ struct usbdux_private *devpriv;
+ struct comedi_subdevice *s;
+ int ret;
+
+ devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
+ if (!devpriv)
+ return -ENOMEM;
+
+ sema_init(&devpriv->sem, 1);
+
+ usb_set_intfdata(intf, devpriv);
+
+ devpriv->high_speed = (usb->speed == USB_SPEED_HIGH);
+ if (devpriv->high_speed) {
+ devpriv->n_ai_urbs = NUMOFINBUFFERSHIGH;
+ devpriv->n_ao_urbs = NUMOFOUTBUFFERSHIGH;
+ devpriv->pwm_buf_sz = 512;
+ } else {
+ devpriv->n_ai_urbs = NUMOFINBUFFERSFULL;
+ devpriv->n_ao_urbs = NUMOFOUTBUFFERSFULL;
+ }
+
+ ret = usbdux_alloc_usb_buffers(dev);
+ if (ret)
+ return ret;
+
+ /* setting to alternate setting 3: enabling iso ep and bulk ep. */
+ ret = usb_set_interface(usb, intf->altsetting->desc.bInterfaceNumber,
+ 3);
+ if (ret < 0) {
+ dev_err(dev->class_dev,
+ "could not set alternate setting 3 in high speed\n");
+ return ret;
+ }
+
+ ret = comedi_load_firmware(dev, &usb->dev, USBDUX_FIRMWARE,
+ usbdux_firmware_upload, 0);
+ if (ret < 0)
+ return ret;
+
+ ret = comedi_alloc_subdevices(dev, (devpriv->high_speed) ? 5 : 4);
+ if (ret)
+ return ret;
+
+ /* Analog Input subdevice */
+ s = &dev->subdevices[0];
+ dev->read_subdev = s;
+ s->type = COMEDI_SUBD_AI;
+ s->subdev_flags = SDF_READABLE | SDF_GROUND | SDF_CMD_READ;
+ s->n_chan = 8;
+ s->maxdata = 0x0fff;
+ s->len_chanlist = 8;
+ s->range_table = &range_usbdux_ai_range;
+ s->insn_read = usbdux_ai_insn_read;
+ s->do_cmdtest = usbdux_ai_cmdtest;
+ s->do_cmd = usbdux_ai_cmd;
+ s->cancel = usbdux_ai_cancel;
+
+ /* Analog Output subdevice */
+ s = &dev->subdevices[1];
+ dev->write_subdev = s;
+ s->type = COMEDI_SUBD_AO;
+ s->subdev_flags = SDF_WRITABLE | SDF_GROUND | SDF_CMD_WRITE;
+ s->n_chan = 4;
+ s->maxdata = 0x0fff;
+ s->len_chanlist = s->n_chan;
+ s->range_table = &range_usbdux_ao_range;
+ s->do_cmdtest = usbdux_ao_cmdtest;
+ s->do_cmd = usbdux_ao_cmd;
+ s->cancel = usbdux_ao_cancel;
+ s->insn_read = usbdux_ao_insn_read;
+ s->insn_write = usbdux_ao_insn_write;
+
+ ret = comedi_alloc_subdev_readback(s);
+ if (ret)
+ return ret;
+
+ /* Digital I/O subdevice */
+ s = &dev->subdevices[2];
+ s->type = COMEDI_SUBD_DIO;
+ s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
+ s->n_chan = 8;
+ s->maxdata = 1;
+ s->range_table = &range_digital;
+ s->insn_bits = usbdux_dio_insn_bits;
+ s->insn_config = usbdux_dio_insn_config;
+
+ /* Counter subdevice */
+ s = &dev->subdevices[3];
+ s->type = COMEDI_SUBD_COUNTER;
+ s->subdev_flags = SDF_WRITABLE | SDF_READABLE;
+ s->n_chan = 4;
+ s->maxdata = 0xffff;
+ s->insn_read = usbdux_counter_read;
+ s->insn_write = usbdux_counter_write;
+ s->insn_config = usbdux_counter_config;
+
+ if (devpriv->high_speed) {
+ /* PWM subdevice */
+ s = &dev->subdevices[4];
+ s->type = COMEDI_SUBD_PWM;
+ s->subdev_flags = SDF_WRITABLE | SDF_PWM_HBRIDGE;
+ s->n_chan = 8;
+ s->maxdata = devpriv->pwm_buf_sz;
+ s->insn_write = usbdux_pwm_write;
+ s->insn_config = usbdux_pwm_config;
+
+ usbdux_pwm_period(dev, s, PWM_DEFAULT_PERIOD);
+ }
+
+ return 0;
+}
+
+static void usbdux_detach(struct comedi_device *dev)
+{
+ struct usb_interface *intf = comedi_to_usb_interface(dev);
+ struct usbdux_private *devpriv = dev->private;
+
+ usb_set_intfdata(intf, NULL);
+
+ if (!devpriv)
+ return;
+
+ down(&devpriv->sem);
+
+ /* force unlink all urbs */
+ usbdux_pwm_stop(dev, 1);
+ usbdux_ao_stop(dev, 1);
+ usbdux_ai_stop(dev, 1);
+
+ usbdux_free_usb_buffers(dev);
+
+ up(&devpriv->sem);
+}
+
+static struct comedi_driver usbdux_driver = {
+ .driver_name = "usbdux",
+ .module = THIS_MODULE,
+ .auto_attach = usbdux_auto_attach,
+ .detach = usbdux_detach,
+};
+
+static int usbdux_usb_probe(struct usb_interface *intf,
+ const struct usb_device_id *id)
+{
+ return comedi_usb_auto_config(intf, &usbdux_driver, 0);
+}
+
+static const struct usb_device_id usbdux_usb_table[] = {
+ { USB_DEVICE(0x13d8, 0x0001) },
+ { USB_DEVICE(0x13d8, 0x0002) },
+ { }
+};
+MODULE_DEVICE_TABLE(usb, usbdux_usb_table);
+
+static struct usb_driver usbdux_usb_driver = {
+ .name = "usbdux",
+ .probe = usbdux_usb_probe,
+ .disconnect = comedi_usb_auto_unconfig,
+ .id_table = usbdux_usb_table,
+};
+module_comedi_usb_driver(usbdux_driver, usbdux_usb_driver);
+
+MODULE_AUTHOR("Bernd Porr, BerndPorr@f2s.com");
+MODULE_DESCRIPTION("Stirling/ITL USB-DUX -- Bernd.Porr@f2s.com");
+MODULE_LICENSE("GPL");
+MODULE_FIRMWARE(USBDUX_FIRMWARE);
diff --git a/drivers/staging/comedi/drivers/usbduxfast.c b/drivers/staging/comedi/drivers/usbduxfast.c
new file mode 100644
index 000000000..d90dc5998
--- /dev/null
+++ b/drivers/staging/comedi/drivers/usbduxfast.c
@@ -0,0 +1,1107 @@
+/*
+ * Copyright (C) 2004-2014 Bernd Porr, mail@berndporr.me.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.
+ */
+
+/*
+ * Driver: usbduxfast
+ * Description: University of Stirling USB DAQ & INCITE Technology Limited
+ * Devices: [ITL] USB-DUX-FAST (usbduxfast)
+ * Author: Bernd Porr <mail@berndporr.me.uk>
+ * Updated: 10 Oct 2014
+ * Status: stable
+ */
+
+/*
+ * I must give credit here to Chris Baugher who
+ * wrote the driver for AT-MIO-16d. I used some parts of this
+ * driver. I also must give credits to David Brownell
+ * who supported me with the USB development.
+ *
+ * Bernd Porr
+ *
+ *
+ * Revision history:
+ * 0.9: Dropping the first data packet which seems to be from the last transfer.
+ * Buffer overflows in the FX2 are handed over to comedi.
+ * 0.92: Dropping now 4 packets. The quad buffer has to be emptied.
+ * Added insn command basically for testing. Sample rate is
+ * 1MHz/16ch=62.5kHz
+ * 0.99: Ian Abbott pointed out a bug which has been corrected. Thanks!
+ * 0.99a: added external trigger.
+ * 1.00: added firmware kernel request to the driver which fixed
+ * udev coldplug problem
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/input.h>
+#include <linux/fcntl.h>
+#include <linux/compiler.h>
+#include "../comedi_usb.h"
+
+/*
+ * timeout for the USB-transfer
+ */
+#define EZTIMEOUT 30
+
+/*
+ * constants for "firmware" upload and download
+ */
+#define FIRMWARE "usbduxfast_firmware.bin"
+#define FIRMWARE_MAX_LEN 0x2000
+#define USBDUXFASTSUB_FIRMWARE 0xA0
+#define VENDOR_DIR_IN 0xC0
+#define VENDOR_DIR_OUT 0x40
+
+/*
+ * internal addresses of the 8051 processor
+ */
+#define USBDUXFASTSUB_CPUCS 0xE600
+
+/*
+ * max lenghth of the transfer-buffer for software upload
+ */
+#define TB_LEN 0x2000
+
+/*
+ * input endpoint number
+ */
+#define BULKINEP 6
+
+/*
+ * endpoint for the A/D channellist: bulk OUT
+ */
+#define CHANNELLISTEP 4
+
+/*
+ * number of channels
+ */
+#define NUMCHANNELS 32
+
+/*
+ * size of the waveform descriptor
+ */
+#define WAVESIZE 0x20
+
+/*
+ * size of one A/D value
+ */
+#define SIZEADIN (sizeof(int16_t))
+
+/*
+ * size of the input-buffer IN BYTES
+ */
+#define SIZEINBUF 512
+
+/*
+ * 16 bytes
+ */
+#define SIZEINSNBUF 512
+
+/*
+ * size of the buffer for the dux commands in bytes
+ */
+#define SIZEOFDUXBUF 256
+
+/*
+ * number of in-URBs which receive the data: min=5
+ */
+#define NUMOFINBUFFERSHIGH 10
+
+/*
+ * min delay steps for more than one channel
+ * basically when the mux gives up ;-)
+ *
+ * steps at 30MHz in the FX2
+ */
+#define MIN_SAMPLING_PERIOD 9
+
+/*
+ * max number of 1/30MHz delay steps
+ */
+#define MAX_SAMPLING_PERIOD 500
+
+/*
+ * number of received packets to ignore before we start handing data
+ * over to comedi, it's quad buffering and we have to ignore 4 packets
+ */
+#define PACKETS_TO_IGNORE 4
+
+/*
+ * comedi constants
+ */
+static const struct comedi_lrange range_usbduxfast_ai_range = {
+ 2, {
+ BIP_RANGE(0.75),
+ BIP_RANGE(0.5)
+ }
+};
+
+/*
+ * private structure of one subdevice
+ *
+ * this is the structure which holds all the data of this driver
+ * one sub device just now: A/D
+ */
+struct usbduxfast_private {
+ struct urb *urb; /* BULK-transfer handling: urb */
+ uint8_t *duxbuf;
+ int8_t *inbuf;
+ short int ai_cmd_running; /* asynchronous command is running */
+ int ignore; /* counter which ignores the first
+ buffers */
+ struct semaphore sem;
+};
+
+/*
+ * bulk transfers to usbduxfast
+ */
+#define SENDADCOMMANDS 0
+#define SENDINITEP6 1
+
+static int usbduxfast_send_cmd(struct comedi_device *dev, int cmd_type)
+{
+ struct usb_device *usb = comedi_to_usb_dev(dev);
+ struct usbduxfast_private *devpriv = dev->private;
+ int nsent;
+ int ret;
+
+ devpriv->duxbuf[0] = cmd_type;
+
+ ret = usb_bulk_msg(usb, usb_sndbulkpipe(usb, CHANNELLISTEP),
+ devpriv->duxbuf, SIZEOFDUXBUF,
+ &nsent, 10000);
+ if (ret < 0)
+ dev_err(dev->class_dev,
+ "could not transmit command to the usb-device, err=%d\n",
+ ret);
+ return ret;
+}
+
+static void usbduxfast_cmd_data(struct comedi_device *dev, int index,
+ uint8_t len, uint8_t op, uint8_t out,
+ uint8_t log)
+{
+ struct usbduxfast_private *devpriv = dev->private;
+
+ /* Set the GPIF bytes, the first byte is the command byte */
+ devpriv->duxbuf[1 + 0x00 + index] = len;
+ devpriv->duxbuf[1 + 0x08 + index] = op;
+ devpriv->duxbuf[1 + 0x10 + index] = out;
+ devpriv->duxbuf[1 + 0x18 + index] = log;
+}
+
+static int usbduxfast_ai_stop(struct comedi_device *dev, int do_unlink)
+{
+ struct usbduxfast_private *devpriv = dev->private;
+
+ /* stop aquistion */
+ devpriv->ai_cmd_running = 0;
+
+ if (do_unlink && devpriv->urb) {
+ /* kill the running transfer */
+ usb_kill_urb(devpriv->urb);
+ }
+
+ return 0;
+}
+
+static int usbduxfast_ai_cancel(struct comedi_device *dev,
+ struct comedi_subdevice *s)
+{
+ struct usbduxfast_private *devpriv = dev->private;
+ int ret;
+
+ if (!devpriv)
+ return -EFAULT;
+
+ down(&devpriv->sem);
+ ret = usbduxfast_ai_stop(dev, 1);
+ up(&devpriv->sem);
+
+ return ret;
+}
+
+static void usbduxfast_ai_handle_urb(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct urb *urb)
+{
+ struct usbduxfast_private *devpriv = dev->private;
+ struct comedi_async *async = s->async;
+ struct comedi_cmd *cmd = &async->cmd;
+ int ret;
+
+ if (devpriv->ignore) {
+ devpriv->ignore--;
+ } else {
+ unsigned int nsamples;
+
+ nsamples = comedi_bytes_to_samples(s, urb->actual_length);
+ nsamples = comedi_nsamples_left(s, nsamples);
+ comedi_buf_write_samples(s, urb->transfer_buffer, nsamples);
+
+ if (cmd->stop_src == TRIG_COUNT &&
+ async->scans_done >= cmd->stop_arg)
+ async->events |= COMEDI_CB_EOA;
+ }
+
+ /* if command is still running, resubmit urb for BULK transfer */
+ if (!(async->events & COMEDI_CB_CANCEL_MASK)) {
+ urb->dev = comedi_to_usb_dev(dev);
+ urb->status = 0;
+ ret = usb_submit_urb(urb, GFP_ATOMIC);
+ if (ret < 0) {
+ dev_err(dev->class_dev, "urb resubm failed: %d", ret);
+ async->events |= COMEDI_CB_ERROR;
+ }
+ }
+}
+
+static void usbduxfast_ai_interrupt(struct urb *urb)
+{
+ struct comedi_device *dev = urb->context;
+ struct comedi_subdevice *s = dev->read_subdev;
+ struct comedi_async *async = s->async;
+ struct usbduxfast_private *devpriv = dev->private;
+
+ /* exit if not running a command, do not resubmit urb */
+ if (!devpriv->ai_cmd_running)
+ return;
+
+ switch (urb->status) {
+ case 0:
+ usbduxfast_ai_handle_urb(dev, s, urb);
+ break;
+
+ case -ECONNRESET:
+ case -ENOENT:
+ case -ESHUTDOWN:
+ case -ECONNABORTED:
+ /* after an unlink command, unplug, ... etc */
+ async->events |= COMEDI_CB_ERROR;
+ break;
+
+ default:
+ /* a real error */
+ dev_err(dev->class_dev,
+ "non-zero urb status received in ai intr context: %d\n",
+ urb->status);
+ async->events |= COMEDI_CB_ERROR;
+ break;
+ }
+
+ /*
+ * comedi_handle_events() cannot be used in this driver. The (*cancel)
+ * operation would unlink the urb.
+ */
+ if (async->events & COMEDI_CB_CANCEL_MASK)
+ usbduxfast_ai_stop(dev, 0);
+
+ comedi_event(dev, s);
+}
+
+static int usbduxfast_submit_urb(struct comedi_device *dev)
+{
+ struct usb_device *usb = comedi_to_usb_dev(dev);
+ struct usbduxfast_private *devpriv = dev->private;
+ int ret;
+
+ if (!devpriv)
+ return -EFAULT;
+
+ usb_fill_bulk_urb(devpriv->urb, usb, usb_rcvbulkpipe(usb, BULKINEP),
+ devpriv->inbuf, SIZEINBUF,
+ usbduxfast_ai_interrupt, dev);
+
+ ret = usb_submit_urb(devpriv->urb, GFP_ATOMIC);
+ if (ret) {
+ dev_err(dev->class_dev, "usb_submit_urb error %d\n", ret);
+ return ret;
+ }
+ return 0;
+}
+
+static int usbduxfast_ai_cmdtest(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_cmd *cmd)
+{
+ int err = 0;
+ long int steps, tmp;
+ int min_sample_period;
+
+ /* Step 1 : check if triggers are trivially valid */
+
+ err |= comedi_check_trigger_src(&cmd->start_src,
+ TRIG_NOW | TRIG_EXT | TRIG_INT);
+ err |= comedi_check_trigger_src(&cmd->scan_begin_src,
+ TRIG_FOLLOW | TRIG_EXT);
+ err |= comedi_check_trigger_src(&cmd->convert_src,
+ TRIG_TIMER | TRIG_EXT);
+ err |= comedi_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
+ err |= comedi_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE);
+
+ if (err)
+ return 1;
+
+ /* Step 2a : make sure trigger sources are unique */
+
+ err |= comedi_check_trigger_is_unique(cmd->start_src);
+ err |= comedi_check_trigger_is_unique(cmd->scan_begin_src);
+ err |= comedi_check_trigger_is_unique(cmd->convert_src);
+ err |= comedi_check_trigger_is_unique(cmd->stop_src);
+
+ /* Step 2b : and mutually compatible */
+
+ /* can't have external stop and start triggers at once */
+ if (cmd->start_src == TRIG_EXT && cmd->stop_src == TRIG_EXT)
+ err |= -EINVAL;
+
+ if (err)
+ return 2;
+
+ /* Step 3: check if arguments are trivially valid */
+
+ err |= comedi_check_trigger_arg_is(&cmd->start_arg, 0);
+
+ if (!cmd->chanlist_len)
+ err |= -EINVAL;
+
+ err |= comedi_check_trigger_arg_is(&cmd->scan_end_arg,
+ cmd->chanlist_len);
+
+ if (cmd->chanlist_len == 1)
+ min_sample_period = 1;
+ else
+ min_sample_period = MIN_SAMPLING_PERIOD;
+
+ if (cmd->convert_src == TRIG_TIMER) {
+ steps = cmd->convert_arg * 30;
+ if (steps < (min_sample_period * 1000))
+ steps = min_sample_period * 1000;
+
+ if (steps > (MAX_SAMPLING_PERIOD * 1000))
+ steps = MAX_SAMPLING_PERIOD * 1000;
+
+ /* calc arg again */
+ tmp = steps / 30;
+ err |= comedi_check_trigger_arg_is(&cmd->convert_arg, tmp);
+ }
+
+ /* stop source */
+ switch (cmd->stop_src) {
+ case TRIG_COUNT:
+ err |= comedi_check_trigger_arg_min(&cmd->stop_arg, 1);
+ break;
+ case TRIG_NONE:
+ err |= comedi_check_trigger_arg_is(&cmd->stop_arg, 0);
+ break;
+ /*
+ * TRIG_EXT doesn't care since it doesn't trigger
+ * off a numbered channel
+ */
+ default:
+ break;
+ }
+
+ if (err)
+ return 3;
+
+ /* step 4: fix up any arguments */
+
+ return 0;
+}
+
+static int usbduxfast_ai_inttrig(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ unsigned int trig_num)
+{
+ struct usbduxfast_private *devpriv = dev->private;
+ struct comedi_cmd *cmd = &s->async->cmd;
+ int ret;
+
+ if (!devpriv)
+ return -EFAULT;
+
+ if (trig_num != cmd->start_arg)
+ return -EINVAL;
+
+ down(&devpriv->sem);
+
+ if (!devpriv->ai_cmd_running) {
+ devpriv->ai_cmd_running = 1;
+ ret = usbduxfast_submit_urb(dev);
+ if (ret < 0) {
+ dev_err(dev->class_dev, "urbSubmit: err=%d\n", ret);
+ devpriv->ai_cmd_running = 0;
+ up(&devpriv->sem);
+ return ret;
+ }
+ s->async->inttrig = NULL;
+ } else {
+ dev_err(dev->class_dev, "ai is already running\n");
+ }
+ up(&devpriv->sem);
+ return 1;
+}
+
+static int usbduxfast_ai_cmd(struct comedi_device *dev,
+ struct comedi_subdevice *s)
+{
+ struct usbduxfast_private *devpriv = dev->private;
+ struct comedi_cmd *cmd = &s->async->cmd;
+ unsigned int chan, gain, rngmask = 0xff;
+ int i, j, ret;
+ int result;
+ long steps, steps_tmp;
+
+ if (!devpriv)
+ return -EFAULT;
+
+ down(&devpriv->sem);
+ if (devpriv->ai_cmd_running) {
+ dev_err(dev->class_dev, "ai_cmd not possible\n");
+ up(&devpriv->sem);
+ return -EBUSY;
+ }
+
+ /*
+ * ignore the first buffers from the device if there
+ * is an error condition
+ */
+ devpriv->ignore = PACKETS_TO_IGNORE;
+
+ gain = CR_RANGE(cmd->chanlist[0]);
+ for (i = 0; i < cmd->chanlist_len; ++i) {
+ chan = CR_CHAN(cmd->chanlist[i]);
+ if (chan != i) {
+ dev_err(dev->class_dev,
+ "channels are not consecutive\n");
+ up(&devpriv->sem);
+ return -EINVAL;
+ }
+ if ((gain != CR_RANGE(cmd->chanlist[i]))
+ && (cmd->chanlist_len > 3)) {
+ dev_err(dev->class_dev,
+ "gain must be the same for all channels\n");
+ up(&devpriv->sem);
+ return -EINVAL;
+ }
+ if (i >= NUMCHANNELS) {
+ dev_err(dev->class_dev, "chanlist too long\n");
+ break;
+ }
+ }
+ steps = 0;
+ if (cmd->convert_src == TRIG_TIMER)
+ steps = (cmd->convert_arg * 30) / 1000;
+
+ if ((steps < MIN_SAMPLING_PERIOD) && (cmd->chanlist_len != 1)) {
+ dev_err(dev->class_dev,
+ "steps=%ld, scan_begin_arg=%d. Not properly tested by cmdtest?\n",
+ steps, cmd->scan_begin_arg);
+ up(&devpriv->sem);
+ return -EINVAL;
+ }
+ if (steps > MAX_SAMPLING_PERIOD) {
+ dev_err(dev->class_dev, "sampling rate too low\n");
+ up(&devpriv->sem);
+ return -EINVAL;
+ }
+ if ((cmd->start_src == TRIG_EXT) && (cmd->chanlist_len != 1)
+ && (cmd->chanlist_len != 16)) {
+ dev_err(dev->class_dev,
+ "TRIG_EXT only with 1 or 16 channels possible\n");
+ up(&devpriv->sem);
+ return -EINVAL;
+ }
+
+ switch (cmd->chanlist_len) {
+ case 1:
+ /*
+ * one channel
+ */
+
+ if (CR_RANGE(cmd->chanlist[0]) > 0)
+ rngmask = 0xff - 0x04;
+ else
+ rngmask = 0xff;
+
+ /*
+ * for external trigger: looping in this state until
+ * the RDY0 pin becomes zero
+ */
+
+ /* we loop here until ready has been set */
+ if (cmd->start_src == TRIG_EXT) {
+ /* branch back to state 0 */
+ /* deceision state w/o data */
+ /* RDY0 = 0 */
+ usbduxfast_cmd_data(dev, 0, 0x01, 0x01, rngmask, 0x00);
+ } else { /* we just proceed to state 1 */
+ usbduxfast_cmd_data(dev, 0, 0x01, 0x00, rngmask, 0x00);
+ }
+
+ if (steps < MIN_SAMPLING_PERIOD) {
+ /* for fast single channel aqu without mux */
+ if (steps <= 1) {
+ /*
+ * we just stay here at state 1 and rexecute
+ * the same state this gives us 30MHz sampling
+ * rate
+ */
+
+ /* branch back to state 1 */
+ /* deceision state with data */
+ /* doesn't matter */
+ usbduxfast_cmd_data(dev, 1,
+ 0x89, 0x03, rngmask, 0xff);
+ } else {
+ /*
+ * we loop through two states: data and delay
+ * max rate is 15MHz
+ */
+ /* data */
+ /* doesn't matter */
+ usbduxfast_cmd_data(dev, 1, steps - 1,
+ 0x02, rngmask, 0x00);
+
+ /* branch back to state 1 */
+ /* deceision state w/o data */
+ /* doesn't matter */
+ usbduxfast_cmd_data(dev, 2,
+ 0x09, 0x01, rngmask, 0xff);
+ }
+ } else {
+ /*
+ * we loop through 3 states: 2x delay and 1x data
+ * this gives a min sampling rate of 60kHz
+ */
+
+ /* we have 1 state with duration 1 */
+ steps = steps - 1;
+
+ /* do the first part of the delay */
+ usbduxfast_cmd_data(dev, 1,
+ steps / 2, 0x00, rngmask, 0x00);
+
+ /* and the second part */
+ usbduxfast_cmd_data(dev, 2, steps - steps / 2,
+ 0x00, rngmask, 0x00);
+
+ /* get the data and branch back */
+
+ /* branch back to state 1 */
+ /* deceision state w data */
+ /* doesn't matter */
+ usbduxfast_cmd_data(dev, 3,
+ 0x09, 0x03, rngmask, 0xff);
+ }
+ break;
+
+ case 2:
+ /*
+ * two channels
+ * commit data to the FIFO
+ */
+
+ if (CR_RANGE(cmd->chanlist[0]) > 0)
+ rngmask = 0xff - 0x04;
+ else
+ rngmask = 0xff;
+
+ /* data */
+ usbduxfast_cmd_data(dev, 0, 0x01, 0x02, rngmask, 0x00);
+
+ /* we have 1 state with duration 1: state 0 */
+ steps_tmp = steps - 1;
+
+ if (CR_RANGE(cmd->chanlist[1]) > 0)
+ rngmask = 0xff - 0x04;
+ else
+ rngmask = 0xff;
+
+ /* do the first part of the delay */
+ /* count */
+ usbduxfast_cmd_data(dev, 1, steps_tmp / 2,
+ 0x00, 0xfe & rngmask, 0x00);
+
+ /* and the second part */
+ usbduxfast_cmd_data(dev, 2, steps_tmp - steps_tmp / 2,
+ 0x00, rngmask, 0x00);
+
+ /* data */
+ usbduxfast_cmd_data(dev, 3, 0x01, 0x02, rngmask, 0x00);
+
+ /*
+ * we have 2 states with duration 1: step 6 and
+ * the IDLE state
+ */
+ steps_tmp = steps - 2;
+
+ if (CR_RANGE(cmd->chanlist[0]) > 0)
+ rngmask = 0xff - 0x04;
+ else
+ rngmask = 0xff;
+
+ /* do the first part of the delay */
+ /* reset */
+ usbduxfast_cmd_data(dev, 4, steps_tmp / 2,
+ 0x00, (0xff - 0x02) & rngmask, 0x00);
+
+ /* and the second part */
+ usbduxfast_cmd_data(dev, 5, steps_tmp - steps_tmp / 2,
+ 0x00, rngmask, 0x00);
+
+ usbduxfast_cmd_data(dev, 6, 0x01, 0x00, rngmask, 0x00);
+ break;
+
+ case 3:
+ /*
+ * three channels
+ */
+ for (j = 0; j < 1; j++) {
+ int index = j * 2;
+
+ if (CR_RANGE(cmd->chanlist[j]) > 0)
+ rngmask = 0xff - 0x04;
+ else
+ rngmask = 0xff;
+ /*
+ * commit data to the FIFO and do the first part
+ * of the delay
+ */
+ /* data */
+ /* no change */
+ usbduxfast_cmd_data(dev, index, steps / 2,
+ 0x02, rngmask, 0x00);
+
+ if (CR_RANGE(cmd->chanlist[j + 1]) > 0)
+ rngmask = 0xff - 0x04;
+ else
+ rngmask = 0xff;
+
+ /* do the second part of the delay */
+ /* no data */
+ /* count */
+ usbduxfast_cmd_data(dev, index + 1, steps - steps / 2,
+ 0x00, 0xfe & rngmask, 0x00);
+ }
+
+ /* 2 steps with duration 1: the idele step and step 6: */
+ steps_tmp = steps - 2;
+
+ /* commit data to the FIFO and do the first part of the delay */
+ /* data */
+ usbduxfast_cmd_data(dev, 4, steps_tmp / 2,
+ 0x02, rngmask, 0x00);
+
+ if (CR_RANGE(cmd->chanlist[0]) > 0)
+ rngmask = 0xff - 0x04;
+ else
+ rngmask = 0xff;
+
+ /* do the second part of the delay */
+ /* no data */
+ /* reset */
+ usbduxfast_cmd_data(dev, 5, steps_tmp - steps_tmp / 2,
+ 0x00, (0xff - 0x02) & rngmask, 0x00);
+
+ usbduxfast_cmd_data(dev, 6, 0x01, 0x00, rngmask, 0x00);
+ break;
+
+ case 16:
+ if (CR_RANGE(cmd->chanlist[0]) > 0)
+ rngmask = 0xff - 0x04;
+ else
+ rngmask = 0xff;
+
+ if (cmd->start_src == TRIG_EXT) {
+ /*
+ * we loop here until ready has been set
+ */
+
+ /* branch back to state 0 */
+ /* deceision state w/o data */
+ /* reset */
+ /* RDY0 = 0 */
+ usbduxfast_cmd_data(dev, 0, 0x01, 0x01,
+ (0xff - 0x02) & rngmask, 0x00);
+ } else {
+ /*
+ * we just proceed to state 1
+ */
+
+ /* 30us reset pulse */
+ /* reset */
+ usbduxfast_cmd_data(dev, 0, 0xff, 0x00,
+ (0xff - 0x02) & rngmask, 0x00);
+ }
+
+ /* commit data to the FIFO */
+ /* data */
+ usbduxfast_cmd_data(dev, 1, 0x01, 0x02, rngmask, 0x00);
+
+ /* we have 2 states with duration 1 */
+ steps = steps - 2;
+
+ /* do the first part of the delay */
+ usbduxfast_cmd_data(dev, 2, steps / 2,
+ 0x00, 0xfe & rngmask, 0x00);
+
+ /* and the second part */
+ usbduxfast_cmd_data(dev, 3, steps - steps / 2,
+ 0x00, rngmask, 0x00);
+
+ /* branch back to state 1 */
+ /* deceision state w/o data */
+ /* doesn't matter */
+ usbduxfast_cmd_data(dev, 4, 0x09, 0x01, rngmask, 0xff);
+
+ break;
+
+ default:
+ dev_err(dev->class_dev, "unsupported combination of channels\n");
+ up(&devpriv->sem);
+ return -EFAULT;
+ }
+
+ /* 0 means that the AD commands are sent */
+ result = usbduxfast_send_cmd(dev, SENDADCOMMANDS);
+ if (result < 0) {
+ up(&devpriv->sem);
+ return result;
+ }
+
+ if ((cmd->start_src == TRIG_NOW) || (cmd->start_src == TRIG_EXT)) {
+ /* enable this acquisition operation */
+ devpriv->ai_cmd_running = 1;
+ ret = usbduxfast_submit_urb(dev);
+ if (ret < 0) {
+ devpriv->ai_cmd_running = 0;
+ /* fixme: unlink here?? */
+ up(&devpriv->sem);
+ return ret;
+ }
+ s->async->inttrig = NULL;
+ } else { /* TRIG_INT */
+ s->async->inttrig = usbduxfast_ai_inttrig;
+ }
+ up(&devpriv->sem);
+
+ return 0;
+}
+
+/*
+ * Mode 0 is used to get a single conversion on demand.
+ */
+static int usbduxfast_ai_insn_read(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ struct usb_device *usb = comedi_to_usb_dev(dev);
+ struct usbduxfast_private *devpriv = dev->private;
+ unsigned int chan = CR_CHAN(insn->chanspec);
+ unsigned int range = CR_RANGE(insn->chanspec);
+ uint8_t rngmask = range ? (0xff - 0x04) : 0xff;
+ int i, j, n, actual_length;
+ int ret;
+
+ down(&devpriv->sem);
+
+ if (devpriv->ai_cmd_running) {
+ dev_err(dev->class_dev,
+ "ai_insn_read not possible, async cmd is running\n");
+ up(&devpriv->sem);
+ return -EBUSY;
+ }
+
+ /* set command for the first channel */
+
+ /* commit data to the FIFO */
+ /* data */
+ usbduxfast_cmd_data(dev, 0, 0x01, 0x02, rngmask, 0x00);
+
+ /* do the first part of the delay */
+ usbduxfast_cmd_data(dev, 1, 0x0c, 0x00, 0xfe & rngmask, 0x00);
+ usbduxfast_cmd_data(dev, 2, 0x01, 0x00, 0xfe & rngmask, 0x00);
+ usbduxfast_cmd_data(dev, 3, 0x01, 0x00, 0xfe & rngmask, 0x00);
+ usbduxfast_cmd_data(dev, 4, 0x01, 0x00, 0xfe & rngmask, 0x00);
+
+ /* second part */
+ usbduxfast_cmd_data(dev, 5, 0x0c, 0x00, rngmask, 0x00);
+ usbduxfast_cmd_data(dev, 6, 0x01, 0x00, rngmask, 0x00);
+
+ ret = usbduxfast_send_cmd(dev, SENDADCOMMANDS);
+ if (ret < 0) {
+ up(&devpriv->sem);
+ return ret;
+ }
+
+ for (i = 0; i < PACKETS_TO_IGNORE; i++) {
+ ret = usb_bulk_msg(usb, usb_rcvbulkpipe(usb, BULKINEP),
+ devpriv->inbuf, SIZEINBUF,
+ &actual_length, 10000);
+ if (ret < 0) {
+ dev_err(dev->class_dev, "insn timeout, no data\n");
+ up(&devpriv->sem);
+ return ret;
+ }
+ }
+
+ for (i = 0; i < insn->n;) {
+ ret = usb_bulk_msg(usb, usb_rcvbulkpipe(usb, BULKINEP),
+ devpriv->inbuf, SIZEINBUF,
+ &actual_length, 10000);
+ if (ret < 0) {
+ dev_err(dev->class_dev, "insn data error: %d\n", ret);
+ up(&devpriv->sem);
+ return ret;
+ }
+ n = actual_length / sizeof(uint16_t);
+ if ((n % 16) != 0) {
+ dev_err(dev->class_dev, "insn data packet corrupted\n");
+ up(&devpriv->sem);
+ return -EINVAL;
+ }
+ for (j = chan; (j < n) && (i < insn->n); j = j + 16) {
+ data[i] = ((uint16_t *) (devpriv->inbuf))[j];
+ i++;
+ }
+ }
+
+ up(&devpriv->sem);
+
+ return insn->n;
+}
+
+static int usbduxfast_attach_common(struct comedi_device *dev)
+{
+ struct usbduxfast_private *devpriv = dev->private;
+ struct comedi_subdevice *s;
+ int ret;
+
+ down(&devpriv->sem);
+
+ ret = comedi_alloc_subdevices(dev, 1);
+ if (ret) {
+ up(&devpriv->sem);
+ return ret;
+ }
+
+ /* Analog Input subdevice */
+ s = &dev->subdevices[0];
+ dev->read_subdev = s;
+ s->type = COMEDI_SUBD_AI;
+ s->subdev_flags = SDF_READABLE | SDF_GROUND | SDF_CMD_READ;
+ s->n_chan = 16;
+ s->len_chanlist = 16;
+ s->insn_read = usbduxfast_ai_insn_read;
+ s->do_cmdtest = usbduxfast_ai_cmdtest;
+ s->do_cmd = usbduxfast_ai_cmd;
+ s->cancel = usbduxfast_ai_cancel;
+ s->maxdata = 0x1000;
+ s->range_table = &range_usbduxfast_ai_range;
+
+ up(&devpriv->sem);
+
+ return 0;
+}
+
+static int usbduxfast_upload_firmware(struct comedi_device *dev,
+ const u8 *data, size_t size,
+ unsigned long context)
+{
+ struct usb_device *usb = comedi_to_usb_dev(dev);
+ uint8_t *buf;
+ unsigned char *tmp;
+ int ret;
+
+ if (!data)
+ return 0;
+
+ if (size > FIRMWARE_MAX_LEN) {
+ dev_err(dev->class_dev, "firmware binary too large for FX2\n");
+ return -ENOMEM;
+ }
+
+ /* we generate a local buffer for the firmware */
+ buf = kmemdup(data, size, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ /* we need a malloc'ed buffer for usb_control_msg() */
+ tmp = kmalloc(1, GFP_KERNEL);
+ if (!tmp) {
+ kfree(buf);
+ return -ENOMEM;
+ }
+
+ /* stop the current firmware on the device */
+ *tmp = 1; /* 7f92 to one */
+ ret = usb_control_msg(usb, usb_sndctrlpipe(usb, 0),
+ USBDUXFASTSUB_FIRMWARE,
+ VENDOR_DIR_OUT,
+ USBDUXFASTSUB_CPUCS, 0x0000,
+ tmp, 1,
+ EZTIMEOUT);
+ if (ret < 0) {
+ dev_err(dev->class_dev, "can not stop firmware\n");
+ goto done;
+ }
+
+ /* upload the new firmware to the device */
+ ret = usb_control_msg(usb, usb_sndctrlpipe(usb, 0),
+ USBDUXFASTSUB_FIRMWARE,
+ VENDOR_DIR_OUT,
+ 0, 0x0000,
+ buf, size,
+ EZTIMEOUT);
+ if (ret < 0) {
+ dev_err(dev->class_dev, "firmware upload failed\n");
+ goto done;
+ }
+
+ /* start the new firmware on the device */
+ *tmp = 0; /* 7f92 to zero */
+ ret = usb_control_msg(usb, usb_sndctrlpipe(usb, 0),
+ USBDUXFASTSUB_FIRMWARE,
+ VENDOR_DIR_OUT,
+ USBDUXFASTSUB_CPUCS, 0x0000,
+ tmp, 1,
+ EZTIMEOUT);
+ if (ret < 0)
+ dev_err(dev->class_dev, "can not start firmware\n");
+
+done:
+ kfree(tmp);
+ kfree(buf);
+ return ret;
+}
+
+static int usbduxfast_auto_attach(struct comedi_device *dev,
+ unsigned long context_unused)
+{
+ struct usb_interface *intf = comedi_to_usb_interface(dev);
+ struct usb_device *usb = comedi_to_usb_dev(dev);
+ struct usbduxfast_private *devpriv;
+ int ret;
+
+ if (usb->speed != USB_SPEED_HIGH) {
+ dev_err(dev->class_dev,
+ "This driver needs USB 2.0 to operate. Aborting...\n");
+ return -ENODEV;
+ }
+
+ devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
+ if (!devpriv)
+ return -ENOMEM;
+
+ sema_init(&devpriv->sem, 1);
+ usb_set_intfdata(intf, devpriv);
+
+ devpriv->duxbuf = kmalloc(SIZEOFDUXBUF, GFP_KERNEL);
+ if (!devpriv->duxbuf)
+ return -ENOMEM;
+
+ ret = usb_set_interface(usb,
+ intf->altsetting->desc.bInterfaceNumber, 1);
+ if (ret < 0) {
+ dev_err(dev->class_dev,
+ "could not switch to alternate setting 1\n");
+ return -ENODEV;
+ }
+
+ devpriv->urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!devpriv->urb) {
+ dev_err(dev->class_dev, "Could not alloc. urb\n");
+ return -ENOMEM;
+ }
+
+ devpriv->inbuf = kmalloc(SIZEINBUF, GFP_KERNEL);
+ if (!devpriv->inbuf)
+ return -ENOMEM;
+
+ ret = comedi_load_firmware(dev, &usb->dev, FIRMWARE,
+ usbduxfast_upload_firmware, 0);
+ if (ret)
+ return ret;
+
+ return usbduxfast_attach_common(dev);
+}
+
+static void usbduxfast_detach(struct comedi_device *dev)
+{
+ struct usb_interface *intf = comedi_to_usb_interface(dev);
+ struct usbduxfast_private *devpriv = dev->private;
+
+ if (!devpriv)
+ return;
+
+ down(&devpriv->sem);
+
+ usb_set_intfdata(intf, NULL);
+
+ if (devpriv->urb) {
+ /* waits until a running transfer is over */
+ usb_kill_urb(devpriv->urb);
+
+ kfree(devpriv->inbuf);
+ devpriv->inbuf = NULL;
+
+ usb_free_urb(devpriv->urb);
+ devpriv->urb = NULL;
+ }
+
+ kfree(devpriv->duxbuf);
+ devpriv->duxbuf = NULL;
+
+ devpriv->ai_cmd_running = 0;
+
+ up(&devpriv->sem);
+}
+
+static struct comedi_driver usbduxfast_driver = {
+ .driver_name = "usbduxfast",
+ .module = THIS_MODULE,
+ .auto_attach = usbduxfast_auto_attach,
+ .detach = usbduxfast_detach,
+};
+
+static int usbduxfast_usb_probe(struct usb_interface *intf,
+ const struct usb_device_id *id)
+{
+ return comedi_usb_auto_config(intf, &usbduxfast_driver, 0);
+}
+
+static const struct usb_device_id usbduxfast_usb_table[] = {
+ /* { USB_DEVICE(0x4b4, 0x8613) }, testing */
+ { USB_DEVICE(0x13d8, 0x0010) }, /* real ID */
+ { USB_DEVICE(0x13d8, 0x0011) }, /* real ID */
+ { }
+};
+MODULE_DEVICE_TABLE(usb, usbduxfast_usb_table);
+
+static struct usb_driver usbduxfast_usb_driver = {
+ .name = "usbduxfast",
+ .probe = usbduxfast_usb_probe,
+ .disconnect = comedi_usb_auto_unconfig,
+ .id_table = usbduxfast_usb_table,
+};
+module_comedi_usb_driver(usbduxfast_driver, usbduxfast_usb_driver);
+
+MODULE_AUTHOR("Bernd Porr, BerndPorr@f2s.com");
+MODULE_DESCRIPTION("USB-DUXfast, BerndPorr@f2s.com");
+MODULE_LICENSE("GPL");
+MODULE_FIRMWARE(FIRMWARE);
diff --git a/drivers/staging/comedi/drivers/usbduxsigma.c b/drivers/staging/comedi/drivers/usbduxsigma.c
new file mode 100644
index 000000000..eaa9add49
--- /dev/null
+++ b/drivers/staging/comedi/drivers/usbduxsigma.c
@@ -0,0 +1,1659 @@
+/*
+ * usbduxsigma.c
+ * Copyright (C) 2011-2014 Bernd Porr, mail@berndporr.me.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.
+ */
+
+/*
+ * Driver: usbduxsigma
+ * Description: University of Stirling USB DAQ & INCITE Technology Limited
+ * Devices: [ITL] USB-DUX-SIGMA (usbduxsigma)
+ * Author: Bernd Porr <mail@berndporr.me.uk>
+ * Updated: 10 Oct 2014
+ * Status: stable
+ */
+
+/*
+ * I must give credit here to Chris Baugher who
+ * wrote the driver for AT-MIO-16d. I used some parts of this
+ * driver. I also must give credits to David Brownell
+ * who supported me with the USB development.
+ *
+ * Note: the raw data from the A/D converter is 24 bit big endian
+ * anything else is little endian to/from the dux board
+ *
+ *
+ * Revision history:
+ * 0.1: initial version
+ * 0.2: all basic functions implemented, digital I/O only for one port
+ * 0.3: proper vendor ID and driver name
+ * 0.4: fixed D/A voltage range
+ * 0.5: various bug fixes, health check at startup
+ * 0.6: corrected wrong input range
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/input.h>
+#include <linux/fcntl.h>
+#include <linux/compiler.h>
+#include <asm/unaligned.h>
+
+#include "../comedi_usb.h"
+
+/* timeout for the USB-transfer in ms*/
+#define BULK_TIMEOUT 1000
+
+/* constants for "firmware" upload and download */
+#define FIRMWARE "usbduxsigma_firmware.bin"
+#define FIRMWARE_MAX_LEN 0x4000
+#define USBDUXSUB_FIRMWARE 0xa0
+#define VENDOR_DIR_IN 0xc0
+#define VENDOR_DIR_OUT 0x40
+
+/* internal addresses of the 8051 processor */
+#define USBDUXSUB_CPUCS 0xE600
+
+/* 300Hz max frequ under PWM */
+#define MIN_PWM_PERIOD ((long)(1E9/300))
+
+/* Default PWM frequency */
+#define PWM_DEFAULT_PERIOD ((long)(1E9/100))
+
+/* Number of channels (16 AD and offset)*/
+#define NUMCHANNELS 16
+
+/* Size of one A/D value */
+#define SIZEADIN ((sizeof(uint32_t)))
+
+/*
+ * Size of the async input-buffer IN BYTES, the DIO state is transmitted
+ * as the first byte.
+ */
+#define SIZEINBUF (((NUMCHANNELS+1)*SIZEADIN))
+
+/* 16 bytes. */
+#define SIZEINSNBUF 16
+
+/* Number of DA channels */
+#define NUMOUTCHANNELS 8
+
+/* size of one value for the D/A converter: channel and value */
+#define SIZEDAOUT ((sizeof(uint8_t)+sizeof(uint16_t)))
+
+/*
+ * Size of the output-buffer in bytes
+ * Actually only the first 4 triplets are used but for the
+ * high speed mode we need to pad it to 8 (microframes).
+ */
+#define SIZEOUTBUF ((8*SIZEDAOUT))
+
+/*
+ * Size of the buffer for the dux commands: just now max size is determined
+ * by the analogue out + command byte + panic bytes...
+ */
+#define SIZEOFDUXBUFFER ((8*SIZEDAOUT+2))
+
+/* Number of in-URBs which receive the data: min=2 */
+#define NUMOFINBUFFERSFULL 5
+
+/* Number of out-URBs which send the data: min=2 */
+#define NUMOFOUTBUFFERSFULL 5
+
+/* Number of in-URBs which receive the data: min=5 */
+/* must have more buffers due to buggy USB ctr */
+#define NUMOFINBUFFERSHIGH 10
+
+/* Number of out-URBs which send the data: min=5 */
+/* must have more buffers due to buggy USB ctr */
+#define NUMOFOUTBUFFERSHIGH 10
+
+/* number of retries to get the right dux command */
+#define RETRIES 10
+
+/* bulk transfer commands to usbduxsigma */
+#define USBBUXSIGMA_AD_CMD 0
+#define USBDUXSIGMA_DA_CMD 1
+#define USBDUXSIGMA_DIO_CFG_CMD 2
+#define USBDUXSIGMA_DIO_BITS_CMD 3
+#define USBDUXSIGMA_SINGLE_AD_CMD 4
+#define USBDUXSIGMA_PWM_ON_CMD 7
+#define USBDUXSIGMA_PWM_OFF_CMD 8
+
+static const struct comedi_lrange usbduxsigma_ai_range = {
+ 1, {
+ BIP_RANGE(2.5 * 0x800000 / 0x780000 / 2.0)
+ }
+};
+
+struct usbduxsigma_private {
+ /* actual number of in-buffers */
+ int n_ai_urbs;
+ /* actual number of out-buffers */
+ int n_ao_urbs;
+ /* ISO-transfer handling: buffers */
+ struct urb **ai_urbs;
+ struct urb **ao_urbs;
+ /* pwm-transfer handling */
+ struct urb *pwm_urb;
+ /* PWM period */
+ unsigned int pwm_period;
+ /* PWM internal delay for the GPIF in the FX2 */
+ uint8_t pwm_delay;
+ /* size of the PWM buffer which holds the bit pattern */
+ int pwm_buf_sz;
+ /* input buffer for the ISO-transfer */
+ __be32 *in_buf;
+ /* input buffer for single insn */
+ uint8_t *insn_buf;
+
+ unsigned high_speed:1;
+ unsigned ai_cmd_running:1;
+ unsigned ao_cmd_running:1;
+ unsigned pwm_cmd_running:1;
+
+ /* time between samples in units of the timer */
+ unsigned int ai_timer;
+ unsigned int ao_timer;
+ /* counter between acquisitions */
+ unsigned int ai_counter;
+ unsigned int ao_counter;
+ /* interval in frames/uframes */
+ unsigned int ai_interval;
+ /* commands */
+ uint8_t *dux_commands;
+ struct semaphore sem;
+};
+
+static void usbduxsigma_unlink_urbs(struct urb **urbs, int num_urbs)
+{
+ int i;
+
+ for (i = 0; i < num_urbs; i++)
+ usb_kill_urb(urbs[i]);
+}
+
+static void usbduxsigma_ai_stop(struct comedi_device *dev, int do_unlink)
+{
+ struct usbduxsigma_private *devpriv = dev->private;
+
+ if (do_unlink && devpriv->ai_urbs)
+ usbduxsigma_unlink_urbs(devpriv->ai_urbs, devpriv->n_ai_urbs);
+
+ devpriv->ai_cmd_running = 0;
+}
+
+static int usbduxsigma_ai_cancel(struct comedi_device *dev,
+ struct comedi_subdevice *s)
+{
+ struct usbduxsigma_private *devpriv = dev->private;
+
+ down(&devpriv->sem);
+ /* unlink only if it is really running */
+ usbduxsigma_ai_stop(dev, devpriv->ai_cmd_running);
+ up(&devpriv->sem);
+
+ return 0;
+}
+
+static void usbduxsigma_ai_handle_urb(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct urb *urb)
+{
+ struct usbduxsigma_private *devpriv = dev->private;
+ struct comedi_async *async = s->async;
+ struct comedi_cmd *cmd = &async->cmd;
+ uint32_t val;
+ int ret;
+ int i;
+
+ devpriv->ai_counter--;
+ if (devpriv->ai_counter == 0) {
+ devpriv->ai_counter = devpriv->ai_timer;
+
+ /* get the data from the USB bus and hand it over to comedi */
+ for (i = 0; i < cmd->chanlist_len; i++) {
+ /* transfer data, note first byte is the DIO state */
+ val = be32_to_cpu(devpriv->in_buf[i+1]);
+ val &= 0x00ffffff; /* strip status byte */
+ val ^= 0x00800000; /* convert to unsigned */
+
+ if (!comedi_buf_write_samples(s, &val, 1))
+ return;
+ }
+
+ if (cmd->stop_src == TRIG_COUNT &&
+ async->scans_done >= cmd->stop_arg)
+ async->events |= COMEDI_CB_EOA;
+ }
+
+ /* if command is still running, resubmit urb */
+ if (!(async->events & COMEDI_CB_CANCEL_MASK)) {
+ urb->dev = comedi_to_usb_dev(dev);
+ ret = usb_submit_urb(urb, GFP_ATOMIC);
+ if (ret < 0) {
+ dev_err(dev->class_dev, "urb resubmit failed (%d)\n",
+ ret);
+ if (ret == -EL2NSYNC)
+ dev_err(dev->class_dev,
+ "buggy USB host controller or bug in IRQ handler\n");
+ async->events |= COMEDI_CB_ERROR;
+ }
+ }
+}
+
+static void usbduxsigma_ai_urb_complete(struct urb *urb)
+{
+ struct comedi_device *dev = urb->context;
+ struct usbduxsigma_private *devpriv = dev->private;
+ struct comedi_subdevice *s = dev->read_subdev;
+ struct comedi_async *async = s->async;
+
+ /* exit if not running a command, do not resubmit urb */
+ if (!devpriv->ai_cmd_running)
+ return;
+
+ switch (urb->status) {
+ case 0:
+ /* copy the result in the transfer buffer */
+ memcpy(devpriv->in_buf, urb->transfer_buffer, SIZEINBUF);
+ usbduxsigma_ai_handle_urb(dev, s, urb);
+ break;
+
+ case -EILSEQ:
+ /*
+ * error in the ISOchronous data
+ * we don't copy the data into the transfer buffer
+ * and recycle the last data byte
+ */
+ dev_dbg(dev->class_dev, "CRC error in ISO IN stream\n");
+ usbduxsigma_ai_handle_urb(dev, s, urb);
+ break;
+
+ case -ECONNRESET:
+ case -ENOENT:
+ case -ESHUTDOWN:
+ case -ECONNABORTED:
+ /* happens after an unlink command */
+ async->events |= COMEDI_CB_ERROR;
+ break;
+
+ default:
+ /* a real error */
+ dev_err(dev->class_dev, "non-zero urb status (%d)\n",
+ urb->status);
+ async->events |= COMEDI_CB_ERROR;
+ break;
+ }
+
+ /*
+ * comedi_handle_events() cannot be used in this driver. The (*cancel)
+ * operation would unlink the urb.
+ */
+ if (async->events & COMEDI_CB_CANCEL_MASK)
+ usbduxsigma_ai_stop(dev, 0);
+
+ comedi_event(dev, s);
+}
+
+static void usbduxsigma_ao_stop(struct comedi_device *dev, int do_unlink)
+{
+ struct usbduxsigma_private *devpriv = dev->private;
+
+ if (do_unlink && devpriv->ao_urbs)
+ usbduxsigma_unlink_urbs(devpriv->ao_urbs, devpriv->n_ao_urbs);
+
+ devpriv->ao_cmd_running = 0;
+}
+
+static int usbduxsigma_ao_cancel(struct comedi_device *dev,
+ struct comedi_subdevice *s)
+{
+ struct usbduxsigma_private *devpriv = dev->private;
+
+ down(&devpriv->sem);
+ /* unlink only if it is really running */
+ usbduxsigma_ao_stop(dev, devpriv->ao_cmd_running);
+ up(&devpriv->sem);
+
+ return 0;
+}
+
+static void usbduxsigma_ao_handle_urb(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct urb *urb)
+{
+ struct usbduxsigma_private *devpriv = dev->private;
+ struct comedi_async *async = s->async;
+ struct comedi_cmd *cmd = &async->cmd;
+ uint8_t *datap;
+ int ret;
+ int i;
+
+ devpriv->ao_counter--;
+ if (devpriv->ao_counter == 0) {
+ devpriv->ao_counter = devpriv->ao_timer;
+
+ if (cmd->stop_src == TRIG_COUNT &&
+ async->scans_done >= cmd->stop_arg) {
+ async->events |= COMEDI_CB_EOA;
+ return;
+ }
+
+ /* transmit data to the USB bus */
+ datap = urb->transfer_buffer;
+ *datap++ = cmd->chanlist_len;
+ for (i = 0; i < cmd->chanlist_len; i++) {
+ unsigned int chan = CR_CHAN(cmd->chanlist[i]);
+ unsigned short val;
+
+ if (!comedi_buf_read_samples(s, &val, 1)) {
+ dev_err(dev->class_dev, "buffer underflow\n");
+ async->events |= COMEDI_CB_OVERFLOW;
+ return;
+ }
+
+ *datap++ = val;
+ *datap++ = chan;
+ s->readback[chan] = val;
+ }
+ }
+
+ /* if command is still running, resubmit urb */
+ if (!(async->events & COMEDI_CB_CANCEL_MASK)) {
+ urb->transfer_buffer_length = SIZEOUTBUF;
+ urb->dev = comedi_to_usb_dev(dev);
+ urb->status = 0;
+ if (devpriv->high_speed)
+ urb->interval = 8; /* uframes */
+ else
+ urb->interval = 1; /* frames */
+ urb->number_of_packets = 1;
+ urb->iso_frame_desc[0].offset = 0;
+ urb->iso_frame_desc[0].length = SIZEOUTBUF;
+ urb->iso_frame_desc[0].status = 0;
+ ret = usb_submit_urb(urb, GFP_ATOMIC);
+ if (ret < 0) {
+ dev_err(dev->class_dev, "urb resubmit failed (%d)\n",
+ ret);
+ if (ret == -EL2NSYNC)
+ dev_err(dev->class_dev,
+ "buggy USB host controller or bug in IRQ handler\n");
+ async->events |= COMEDI_CB_ERROR;
+ }
+ }
+}
+
+static void usbduxsigma_ao_urb_complete(struct urb *urb)
+{
+ struct comedi_device *dev = urb->context;
+ struct usbduxsigma_private *devpriv = dev->private;
+ struct comedi_subdevice *s = dev->write_subdev;
+ struct comedi_async *async = s->async;
+
+ /* exit if not running a command, do not resubmit urb */
+ if (!devpriv->ao_cmd_running)
+ return;
+
+ switch (urb->status) {
+ case 0:
+ usbduxsigma_ao_handle_urb(dev, s, urb);
+ break;
+
+ case -ECONNRESET:
+ case -ENOENT:
+ case -ESHUTDOWN:
+ case -ECONNABORTED:
+ /* happens after an unlink command */
+ async->events |= COMEDI_CB_ERROR;
+ break;
+
+ default:
+ /* a real error */
+ dev_err(dev->class_dev, "non-zero urb status (%d)\n",
+ urb->status);
+ async->events |= COMEDI_CB_ERROR;
+ break;
+ }
+
+ /*
+ * comedi_handle_events() cannot be used in this driver. The (*cancel)
+ * operation would unlink the urb.
+ */
+ if (async->events & COMEDI_CB_CANCEL_MASK)
+ usbduxsigma_ao_stop(dev, 0);
+
+ comedi_event(dev, s);
+}
+
+static int usbduxsigma_submit_urbs(struct comedi_device *dev,
+ struct urb **urbs, int num_urbs,
+ int input_urb)
+{
+ struct usb_device *usb = comedi_to_usb_dev(dev);
+ struct usbduxsigma_private *devpriv = dev->private;
+ struct urb *urb;
+ int ret;
+ int i;
+
+ /* Submit all URBs and start the transfer on the bus */
+ for (i = 0; i < num_urbs; i++) {
+ urb = urbs[i];
+
+ /* in case of a resubmission after an unlink... */
+ if (input_urb)
+ urb->interval = devpriv->ai_interval;
+ urb->context = dev;
+ urb->dev = usb;
+ urb->status = 0;
+ urb->transfer_flags = URB_ISO_ASAP;
+
+ ret = usb_submit_urb(urb, GFP_ATOMIC);
+ if (ret)
+ return ret;
+ }
+ return 0;
+}
+
+static int usbduxsigma_chans_to_interval(int num_chan)
+{
+ if (num_chan <= 2)
+ return 2; /* 4kHz */
+ if (num_chan <= 8)
+ return 4; /* 2kHz */
+ return 8; /* 1kHz */
+}
+
+static int usbduxsigma_ai_cmdtest(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_cmd *cmd)
+{
+ struct usbduxsigma_private *devpriv = dev->private;
+ int high_speed = devpriv->high_speed;
+ int interval = usbduxsigma_chans_to_interval(cmd->chanlist_len);
+ int err = 0;
+
+ /* Step 1 : check if triggers are trivially valid */
+
+ err |= comedi_check_trigger_src(&cmd->start_src, TRIG_NOW | TRIG_INT);
+ err |= comedi_check_trigger_src(&cmd->scan_begin_src, TRIG_TIMER);
+ err |= comedi_check_trigger_src(&cmd->convert_src, TRIG_NOW);
+ err |= comedi_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
+ err |= comedi_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE);
+
+ if (err)
+ return 1;
+
+ /* Step 2a : make sure trigger sources are unique */
+
+ err |= comedi_check_trigger_is_unique(cmd->start_src);
+ err |= comedi_check_trigger_is_unique(cmd->stop_src);
+
+ /* Step 2b : and mutually compatible */
+
+ if (err)
+ return 2;
+
+ /* Step 3: check if arguments are trivially valid */
+
+ err |= comedi_check_trigger_arg_is(&cmd->start_arg, 0);
+
+ if (cmd->scan_begin_src == TRIG_FOLLOW) /* internal trigger */
+ err |= comedi_check_trigger_arg_is(&cmd->scan_begin_arg, 0);
+
+ if (cmd->scan_begin_src == TRIG_TIMER) {
+ unsigned int tmp;
+
+ if (high_speed) {
+ /*
+ * In high speed mode microframes are possible.
+ * However, during one microframe we can roughly
+ * sample two channels. Thus, the more channels
+ * are in the channel list the more time we need.
+ */
+ err |= comedi_check_trigger_arg_min(&cmd->
+ scan_begin_arg,
+ (1000000 / 8 *
+ interval));
+
+ tmp = (cmd->scan_begin_arg / 125000) * 125000;
+ } else {
+ /* full speed */
+ /* 1kHz scans every USB frame */
+ err |= comedi_check_trigger_arg_min(&cmd->
+ scan_begin_arg,
+ 1000000);
+
+ tmp = (cmd->scan_begin_arg / 1000000) * 1000000;
+ }
+ err |= comedi_check_trigger_arg_is(&cmd->scan_begin_arg, tmp);
+ }
+
+ err |= comedi_check_trigger_arg_is(&cmd->scan_end_arg,
+ cmd->chanlist_len);
+
+ if (cmd->stop_src == TRIG_COUNT)
+ err |= comedi_check_trigger_arg_min(&cmd->stop_arg, 1);
+ else /* TRIG_NONE */
+ err |= comedi_check_trigger_arg_is(&cmd->stop_arg, 0);
+
+ if (err)
+ return 3;
+
+ /* Step 4: fix up any arguments */
+
+ if (high_speed) {
+ /*
+ * every 2 channels get a time window of 125us. Thus, if we
+ * sample all 16 channels we need 1ms. If we sample only one
+ * channel we need only 125us
+ */
+ devpriv->ai_interval = interval;
+ devpriv->ai_timer = cmd->scan_begin_arg / (125000 * interval);
+ } else {
+ /* interval always 1ms */
+ devpriv->ai_interval = 1;
+ devpriv->ai_timer = cmd->scan_begin_arg / 1000000;
+ }
+ if (devpriv->ai_timer < 1)
+ err |= -EINVAL;
+
+ if (err)
+ return 4;
+
+ return 0;
+}
+
+/*
+ * creates the ADC command for the MAX1271
+ * range is the range value from comedi
+ */
+static void create_adc_command(unsigned int chan,
+ uint8_t *muxsg0,
+ uint8_t *muxsg1)
+{
+ if (chan < 8)
+ (*muxsg0) = (*muxsg0) | (1 << chan);
+ else if (chan < 16)
+ (*muxsg1) = (*muxsg1) | (1 << (chan-8));
+}
+
+static int usbbuxsigma_send_cmd(struct comedi_device *dev, int cmd_type)
+{
+ struct usb_device *usb = comedi_to_usb_dev(dev);
+ struct usbduxsigma_private *devpriv = dev->private;
+ int nsent;
+
+ devpriv->dux_commands[0] = cmd_type;
+
+ return usb_bulk_msg(usb, usb_sndbulkpipe(usb, 1),
+ devpriv->dux_commands, SIZEOFDUXBUFFER,
+ &nsent, BULK_TIMEOUT);
+}
+
+static int usbduxsigma_receive_cmd(struct comedi_device *dev, int command)
+{
+ struct usb_device *usb = comedi_to_usb_dev(dev);
+ struct usbduxsigma_private *devpriv = dev->private;
+ int nrec;
+ int ret;
+ int i;
+
+ for (i = 0; i < RETRIES; i++) {
+ ret = usb_bulk_msg(usb, usb_rcvbulkpipe(usb, 8),
+ devpriv->insn_buf, SIZEINSNBUF,
+ &nrec, BULK_TIMEOUT);
+ if (ret < 0)
+ return ret;
+
+ if (devpriv->insn_buf[0] == command)
+ return 0;
+ }
+ /*
+ * This is only reached if the data has been requested a
+ * couple of times and the command was not received.
+ */
+ return -EFAULT;
+}
+
+static int usbduxsigma_ai_inttrig(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ unsigned int trig_num)
+{
+ struct usbduxsigma_private *devpriv = dev->private;
+ struct comedi_cmd *cmd = &s->async->cmd;
+ int ret;
+
+ if (trig_num != cmd->start_arg)
+ return -EINVAL;
+
+ down(&devpriv->sem);
+ if (!devpriv->ai_cmd_running) {
+ devpriv->ai_cmd_running = 1;
+ ret = usbduxsigma_submit_urbs(dev, devpriv->ai_urbs,
+ devpriv->n_ai_urbs, 1);
+ if (ret < 0) {
+ devpriv->ai_cmd_running = 0;
+ up(&devpriv->sem);
+ return ret;
+ }
+ s->async->inttrig = NULL;
+ }
+ up(&devpriv->sem);
+
+ return 1;
+}
+
+static int usbduxsigma_ai_cmd(struct comedi_device *dev,
+ struct comedi_subdevice *s)
+{
+ struct usbduxsigma_private *devpriv = dev->private;
+ struct comedi_cmd *cmd = &s->async->cmd;
+ unsigned int len = cmd->chanlist_len;
+ uint8_t muxsg0 = 0;
+ uint8_t muxsg1 = 0;
+ uint8_t sysred = 0;
+ int ret;
+ int i;
+
+ down(&devpriv->sem);
+
+ for (i = 0; i < len; i++) {
+ unsigned int chan = CR_CHAN(cmd->chanlist[i]);
+
+ create_adc_command(chan, &muxsg0, &muxsg1);
+ }
+
+ devpriv->dux_commands[1] = len; /* num channels per time step */
+ devpriv->dux_commands[2] = 0x12; /* CONFIG0 */
+ devpriv->dux_commands[3] = 0x03; /* CONFIG1: 23kHz sample, delay 0us */
+ devpriv->dux_commands[4] = 0x00; /* CONFIG3: diff. channels off */
+ devpriv->dux_commands[5] = muxsg0;
+ devpriv->dux_commands[6] = muxsg1;
+ devpriv->dux_commands[7] = sysred;
+
+ ret = usbbuxsigma_send_cmd(dev, USBBUXSIGMA_AD_CMD);
+ if (ret < 0) {
+ up(&devpriv->sem);
+ return ret;
+ }
+
+ devpriv->ai_counter = devpriv->ai_timer;
+
+ if (cmd->start_src == TRIG_NOW) {
+ /* enable this acquisition operation */
+ devpriv->ai_cmd_running = 1;
+ ret = usbduxsigma_submit_urbs(dev, devpriv->ai_urbs,
+ devpriv->n_ai_urbs, 1);
+ if (ret < 0) {
+ devpriv->ai_cmd_running = 0;
+ up(&devpriv->sem);
+ return ret;
+ }
+ s->async->inttrig = NULL;
+ } else { /* TRIG_INT */
+ s->async->inttrig = usbduxsigma_ai_inttrig;
+ }
+
+ up(&devpriv->sem);
+
+ return 0;
+}
+
+static int usbduxsigma_ai_insn_read(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ struct usbduxsigma_private *devpriv = dev->private;
+ unsigned int chan = CR_CHAN(insn->chanspec);
+ uint8_t muxsg0 = 0;
+ uint8_t muxsg1 = 0;
+ uint8_t sysred = 0;
+ int ret;
+ int i;
+
+ down(&devpriv->sem);
+ if (devpriv->ai_cmd_running) {
+ up(&devpriv->sem);
+ return -EBUSY;
+ }
+
+ create_adc_command(chan, &muxsg0, &muxsg1);
+
+ /* Mode 0 is used to get a single conversion on demand */
+ devpriv->dux_commands[1] = 0x16; /* CONFIG0: chopper on */
+ devpriv->dux_commands[2] = 0x80; /* CONFIG1: 2kHz sampling rate */
+ devpriv->dux_commands[3] = 0x00; /* CONFIG3: diff. channels off */
+ devpriv->dux_commands[4] = muxsg0;
+ devpriv->dux_commands[5] = muxsg1;
+ devpriv->dux_commands[6] = sysred;
+
+ /* adc commands */
+ ret = usbbuxsigma_send_cmd(dev, USBDUXSIGMA_SINGLE_AD_CMD);
+ if (ret < 0) {
+ up(&devpriv->sem);
+ return ret;
+ }
+
+ for (i = 0; i < insn->n; i++) {
+ uint32_t val;
+
+ ret = usbduxsigma_receive_cmd(dev, USBDUXSIGMA_SINGLE_AD_CMD);
+ if (ret < 0) {
+ up(&devpriv->sem);
+ return ret;
+ }
+
+ /* 32 bits big endian from the A/D converter */
+ val = be32_to_cpu(get_unaligned((__be32
+ *)(devpriv->insn_buf + 1)));
+ val &= 0x00ffffff; /* strip status byte */
+ val ^= 0x00800000; /* convert to unsigned */
+
+ data[i] = val;
+ }
+ up(&devpriv->sem);
+
+ return insn->n;
+}
+
+static int usbduxsigma_ao_insn_read(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ struct usbduxsigma_private *devpriv = dev->private;
+ int ret;
+
+ down(&devpriv->sem);
+ ret = comedi_readback_insn_read(dev, s, insn, data);
+ up(&devpriv->sem);
+
+ return ret;
+}
+
+static int usbduxsigma_ao_insn_write(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ struct usbduxsigma_private *devpriv = dev->private;
+ unsigned int chan = CR_CHAN(insn->chanspec);
+ int ret;
+ int i;
+
+ down(&devpriv->sem);
+ if (devpriv->ao_cmd_running) {
+ up(&devpriv->sem);
+ return -EBUSY;
+ }
+
+ for (i = 0; i < insn->n; i++) {
+ devpriv->dux_commands[1] = 1; /* num channels */
+ devpriv->dux_commands[2] = data[i]; /* value */
+ devpriv->dux_commands[3] = chan; /* channel number */
+ ret = usbbuxsigma_send_cmd(dev, USBDUXSIGMA_DA_CMD);
+ if (ret < 0) {
+ up(&devpriv->sem);
+ return ret;
+ }
+ s->readback[chan] = data[i];
+ }
+ up(&devpriv->sem);
+
+ return insn->n;
+}
+
+static int usbduxsigma_ao_inttrig(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ unsigned int trig_num)
+{
+ struct usbduxsigma_private *devpriv = dev->private;
+ struct comedi_cmd *cmd = &s->async->cmd;
+ int ret;
+
+ if (trig_num != cmd->start_arg)
+ return -EINVAL;
+
+ down(&devpriv->sem);
+ if (!devpriv->ao_cmd_running) {
+ devpriv->ao_cmd_running = 1;
+ ret = usbduxsigma_submit_urbs(dev, devpriv->ao_urbs,
+ devpriv->n_ao_urbs, 0);
+ if (ret < 0) {
+ devpriv->ao_cmd_running = 0;
+ up(&devpriv->sem);
+ return ret;
+ }
+ s->async->inttrig = NULL;
+ }
+ up(&devpriv->sem);
+
+ return 1;
+}
+
+static int usbduxsigma_ao_cmdtest(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_cmd *cmd)
+{
+ struct usbduxsigma_private *devpriv = dev->private;
+ int err = 0;
+ int high_speed;
+ unsigned int flags;
+
+ /* high speed conversions are not used yet */
+ high_speed = 0; /* (devpriv->high_speed) */
+
+ /* Step 1 : check if triggers are trivially valid */
+
+ err |= comedi_check_trigger_src(&cmd->start_src, TRIG_NOW | TRIG_INT);
+
+ if (high_speed) {
+ /*
+ * start immediately a new scan
+ * the sampling rate is set by the coversion rate
+ */
+ flags = TRIG_FOLLOW;
+ } else {
+ /* start a new scan (output at once) with a timer */
+ flags = TRIG_TIMER;
+ }
+ err |= comedi_check_trigger_src(&cmd->scan_begin_src, flags);
+
+ err |= comedi_check_trigger_src(&cmd->convert_src, TRIG_NOW);
+ err |= comedi_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
+ err |= comedi_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE);
+
+ if (err) {
+ up(&devpriv->sem);
+ return 1;
+ }
+
+ /* Step 2a : make sure trigger sources are unique */
+
+ err |= comedi_check_trigger_is_unique(cmd->start_src);
+ err |= comedi_check_trigger_is_unique(cmd->stop_src);
+
+ /* Step 2b : and mutually compatible */
+
+ if (err)
+ return 2;
+
+ /* Step 3: check if arguments are trivially valid */
+
+ err |= comedi_check_trigger_arg_is(&cmd->start_arg, 0);
+
+ if (cmd->scan_begin_src == TRIG_FOLLOW) /* internal trigger */
+ err |= comedi_check_trigger_arg_is(&cmd->scan_begin_arg, 0);
+
+ if (cmd->scan_begin_src == TRIG_TIMER) {
+ err |= comedi_check_trigger_arg_min(&cmd->scan_begin_arg,
+ 1000000);
+ }
+
+ /* not used now, is for later use */
+ if (cmd->convert_src == TRIG_TIMER)
+ err |= comedi_check_trigger_arg_min(&cmd->convert_arg, 125000);
+
+ err |= comedi_check_trigger_arg_is(&cmd->scan_end_arg,
+ cmd->chanlist_len);
+
+ if (cmd->stop_src == TRIG_COUNT)
+ err |= comedi_check_trigger_arg_min(&cmd->stop_arg, 1);
+ else /* TRIG_NONE */
+ err |= comedi_check_trigger_arg_is(&cmd->stop_arg, 0);
+
+ if (err)
+ return 3;
+
+ /* Step 4: fix up any arguments */
+
+ /* we count in timer steps */
+ if (high_speed) {
+ /* timing of the conversion itself: every 125 us */
+ devpriv->ao_timer = cmd->convert_arg / 125000;
+ } else {
+ /*
+ * timing of the scan: every 1ms
+ * we get all channels at once
+ */
+ devpriv->ao_timer = cmd->scan_begin_arg / 1000000;
+ }
+ if (devpriv->ao_timer < 1)
+ err |= -EINVAL;
+
+ if (err)
+ return 4;
+
+ return 0;
+}
+
+static int usbduxsigma_ao_cmd(struct comedi_device *dev,
+ struct comedi_subdevice *s)
+{
+ struct usbduxsigma_private *devpriv = dev->private;
+ struct comedi_cmd *cmd = &s->async->cmd;
+ int ret;
+
+ down(&devpriv->sem);
+
+ devpriv->ao_counter = devpriv->ao_timer;
+
+ if (cmd->start_src == TRIG_NOW) {
+ /* enable this acquisition operation */
+ devpriv->ao_cmd_running = 1;
+ ret = usbduxsigma_submit_urbs(dev, devpriv->ao_urbs,
+ devpriv->n_ao_urbs, 0);
+ if (ret < 0) {
+ devpriv->ao_cmd_running = 0;
+ up(&devpriv->sem);
+ return ret;
+ }
+ s->async->inttrig = NULL;
+ } else { /* TRIG_INT */
+ s->async->inttrig = usbduxsigma_ao_inttrig;
+ }
+
+ up(&devpriv->sem);
+
+ return 0;
+}
+
+static int usbduxsigma_dio_insn_config(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ int ret;
+
+ ret = comedi_dio_insn_config(dev, s, insn, data, 0);
+ if (ret)
+ return ret;
+
+ /*
+ * We don't tell the firmware here as it would take 8 frames
+ * to submit the information. We do it in the (*insn_bits).
+ */
+ return insn->n;
+}
+
+static int usbduxsigma_dio_insn_bits(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ struct usbduxsigma_private *devpriv = dev->private;
+ int ret;
+
+ down(&devpriv->sem);
+
+ comedi_dio_update_state(s, data);
+
+ /* Always update the hardware. See the (*insn_config). */
+ devpriv->dux_commands[1] = s->io_bits & 0xff;
+ devpriv->dux_commands[4] = s->state & 0xff;
+ devpriv->dux_commands[2] = (s->io_bits >> 8) & 0xff;
+ devpriv->dux_commands[5] = (s->state >> 8) & 0xff;
+ devpriv->dux_commands[3] = (s->io_bits >> 16) & 0xff;
+ devpriv->dux_commands[6] = (s->state >> 16) & 0xff;
+
+ ret = usbbuxsigma_send_cmd(dev, USBDUXSIGMA_DIO_BITS_CMD);
+ if (ret < 0)
+ goto done;
+ ret = usbduxsigma_receive_cmd(dev, USBDUXSIGMA_DIO_BITS_CMD);
+ if (ret < 0)
+ goto done;
+
+ s->state = devpriv->insn_buf[1] |
+ (devpriv->insn_buf[2] << 8) |
+ (devpriv->insn_buf[3] << 16);
+
+ data[1] = s->state;
+ ret = insn->n;
+
+done:
+ up(&devpriv->sem);
+
+ return ret;
+}
+
+static void usbduxsigma_pwm_stop(struct comedi_device *dev, int do_unlink)
+{
+ struct usbduxsigma_private *devpriv = dev->private;
+
+ if (do_unlink) {
+ if (devpriv->pwm_urb)
+ usb_kill_urb(devpriv->pwm_urb);
+ }
+
+ devpriv->pwm_cmd_running = 0;
+}
+
+static int usbduxsigma_pwm_cancel(struct comedi_device *dev,
+ struct comedi_subdevice *s)
+{
+ struct usbduxsigma_private *devpriv = dev->private;
+
+ /* unlink only if it is really running */
+ usbduxsigma_pwm_stop(dev, devpriv->pwm_cmd_running);
+
+ return usbbuxsigma_send_cmd(dev, USBDUXSIGMA_PWM_OFF_CMD);
+}
+
+static void usbduxsigma_pwm_urb_complete(struct urb *urb)
+{
+ struct comedi_device *dev = urb->context;
+ struct usbduxsigma_private *devpriv = dev->private;
+ int ret;
+
+ switch (urb->status) {
+ case 0:
+ /* success */
+ break;
+
+ case -ECONNRESET:
+ case -ENOENT:
+ case -ESHUTDOWN:
+ case -ECONNABORTED:
+ /* happens after an unlink command */
+ if (devpriv->pwm_cmd_running)
+ usbduxsigma_pwm_stop(dev, 0); /* w/o unlink */
+ return;
+
+ default:
+ /* a real error */
+ if (devpriv->pwm_cmd_running) {
+ dev_err(dev->class_dev, "non-zero urb status (%d)\n",
+ urb->status);
+ usbduxsigma_pwm_stop(dev, 0); /* w/o unlink */
+ }
+ return;
+ }
+
+ if (!devpriv->pwm_cmd_running)
+ return;
+
+ urb->transfer_buffer_length = devpriv->pwm_buf_sz;
+ urb->dev = comedi_to_usb_dev(dev);
+ urb->status = 0;
+ ret = usb_submit_urb(urb, GFP_ATOMIC);
+ if (ret < 0) {
+ dev_err(dev->class_dev, "urb resubmit failed (%d)\n", ret);
+ if (ret == -EL2NSYNC)
+ dev_err(dev->class_dev,
+ "buggy USB host controller or bug in IRQ handler\n");
+ usbduxsigma_pwm_stop(dev, 0); /* w/o unlink */
+ }
+}
+
+static int usbduxsigma_submit_pwm_urb(struct comedi_device *dev)
+{
+ struct usb_device *usb = comedi_to_usb_dev(dev);
+ struct usbduxsigma_private *devpriv = dev->private;
+ struct urb *urb = devpriv->pwm_urb;
+
+ /* in case of a resubmission after an unlink... */
+ usb_fill_bulk_urb(urb, usb, usb_sndbulkpipe(usb, 4),
+ urb->transfer_buffer, devpriv->pwm_buf_sz,
+ usbduxsigma_pwm_urb_complete, dev);
+
+ return usb_submit_urb(urb, GFP_ATOMIC);
+}
+
+static int usbduxsigma_pwm_period(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ unsigned int period)
+{
+ struct usbduxsigma_private *devpriv = dev->private;
+ int fx2delay = 255;
+
+ if (period < MIN_PWM_PERIOD)
+ return -EAGAIN;
+
+ fx2delay = (period / (6 * 512 * 1000 / 33)) - 6;
+ if (fx2delay > 255)
+ return -EAGAIN;
+
+ devpriv->pwm_delay = fx2delay;
+ devpriv->pwm_period = period;
+ return 0;
+}
+
+static int usbduxsigma_pwm_start(struct comedi_device *dev,
+ struct comedi_subdevice *s)
+{
+ struct usbduxsigma_private *devpriv = dev->private;
+ int ret;
+
+ if (devpriv->pwm_cmd_running)
+ return 0;
+
+ devpriv->dux_commands[1] = devpriv->pwm_delay;
+ ret = usbbuxsigma_send_cmd(dev, USBDUXSIGMA_PWM_ON_CMD);
+ if (ret < 0)
+ return ret;
+
+ memset(devpriv->pwm_urb->transfer_buffer, 0, devpriv->pwm_buf_sz);
+
+ devpriv->pwm_cmd_running = 1;
+ ret = usbduxsigma_submit_pwm_urb(dev);
+ if (ret < 0) {
+ devpriv->pwm_cmd_running = 0;
+ return ret;
+ }
+
+ return 0;
+}
+
+static void usbduxsigma_pwm_pattern(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ unsigned int chan,
+ unsigned int value,
+ unsigned int sign)
+{
+ struct usbduxsigma_private *devpriv = dev->private;
+ char pwm_mask = (1 << chan); /* DIO bit for the PWM data */
+ char sgn_mask = (16 << chan); /* DIO bit for the sign */
+ char *buf = (char *)(devpriv->pwm_urb->transfer_buffer);
+ int szbuf = devpriv->pwm_buf_sz;
+ int i;
+
+ for (i = 0; i < szbuf; i++) {
+ char c = *buf;
+
+ c &= ~pwm_mask;
+ if (i < value)
+ c |= pwm_mask;
+ if (!sign)
+ c &= ~sgn_mask;
+ else
+ c |= sgn_mask;
+ *buf++ = c;
+ }
+}
+
+static int usbduxsigma_pwm_write(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ unsigned int chan = CR_CHAN(insn->chanspec);
+
+ /*
+ * It doesn't make sense to support more than one value here
+ * because it would just overwrite the PWM buffer.
+ */
+ if (insn->n != 1)
+ return -EINVAL;
+
+ /*
+ * The sign is set via a special INSN only, this gives us 8 bits
+ * for normal operation, sign is 0 by default.
+ */
+ usbduxsigma_pwm_pattern(dev, s, chan, data[0], 0);
+
+ return insn->n;
+}
+
+static int usbduxsigma_pwm_config(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ struct usbduxsigma_private *devpriv = dev->private;
+ unsigned int chan = CR_CHAN(insn->chanspec);
+
+ switch (data[0]) {
+ case INSN_CONFIG_ARM:
+ /*
+ * if not zero the PWM is limited to a certain time which is
+ * not supported here
+ */
+ if (data[1] != 0)
+ return -EINVAL;
+ return usbduxsigma_pwm_start(dev, s);
+ case INSN_CONFIG_DISARM:
+ return usbduxsigma_pwm_cancel(dev, s);
+ case INSN_CONFIG_GET_PWM_STATUS:
+ data[1] = devpriv->pwm_cmd_running;
+ return 0;
+ case INSN_CONFIG_PWM_SET_PERIOD:
+ return usbduxsigma_pwm_period(dev, s, data[1]);
+ case INSN_CONFIG_PWM_GET_PERIOD:
+ data[1] = devpriv->pwm_period;
+ return 0;
+ case INSN_CONFIG_PWM_SET_H_BRIDGE:
+ /*
+ * data[1] = value
+ * data[2] = sign (for a relay)
+ */
+ usbduxsigma_pwm_pattern(dev, s, chan, data[1], (data[2] != 0));
+ return 0;
+ case INSN_CONFIG_PWM_GET_H_BRIDGE:
+ /* values are not kept in this driver, nothing to return */
+ return -EINVAL;
+ }
+ return -EINVAL;
+}
+
+static int usbduxsigma_getstatusinfo(struct comedi_device *dev, int chan)
+{
+ struct usbduxsigma_private *devpriv = dev->private;
+ uint8_t sysred;
+ uint32_t val;
+ int ret;
+
+ switch (chan) {
+ default:
+ case 0:
+ sysred = 0; /* ADC zero */
+ break;
+ case 1:
+ sysred = 1; /* ADC offset */
+ break;
+ case 2:
+ sysred = 4; /* VCC */
+ break;
+ case 3:
+ sysred = 8; /* temperature */
+ break;
+ case 4:
+ sysred = 16; /* gain */
+ break;
+ case 5:
+ sysred = 32; /* ref */
+ break;
+ }
+
+ devpriv->dux_commands[1] = 0x12; /* CONFIG0 */
+ devpriv->dux_commands[2] = 0x80; /* CONFIG1: 2kHz sampling rate */
+ devpriv->dux_commands[3] = 0x00; /* CONFIG3: diff. channels off */
+ devpriv->dux_commands[4] = 0;
+ devpriv->dux_commands[5] = 0;
+ devpriv->dux_commands[6] = sysred;
+ ret = usbbuxsigma_send_cmd(dev, USBDUXSIGMA_SINGLE_AD_CMD);
+ if (ret < 0)
+ return ret;
+
+ ret = usbduxsigma_receive_cmd(dev, USBDUXSIGMA_SINGLE_AD_CMD);
+ if (ret < 0)
+ return ret;
+
+ /* 32 bits big endian from the A/D converter */
+ val = be32_to_cpu(get_unaligned((__be32 *)(devpriv->insn_buf + 1)));
+ val &= 0x00ffffff; /* strip status byte */
+ val ^= 0x00800000; /* convert to unsigned */
+
+ return (int)val;
+}
+
+static int usbduxsigma_firmware_upload(struct comedi_device *dev,
+ const u8 *data, size_t size,
+ unsigned long context)
+{
+ struct usb_device *usb = comedi_to_usb_dev(dev);
+ uint8_t *buf;
+ uint8_t *tmp;
+ int ret;
+
+ if (!data)
+ return 0;
+
+ if (size > FIRMWARE_MAX_LEN) {
+ dev_err(dev->class_dev, "firmware binary too large for FX2\n");
+ return -ENOMEM;
+ }
+
+ /* we generate a local buffer for the firmware */
+ buf = kmemdup(data, size, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ /* we need a malloc'ed buffer for usb_control_msg() */
+ tmp = kmalloc(1, GFP_KERNEL);
+ if (!tmp) {
+ kfree(buf);
+ return -ENOMEM;
+ }
+
+ /* stop the current firmware on the device */
+ *tmp = 1; /* 7f92 to one */
+ ret = usb_control_msg(usb, usb_sndctrlpipe(usb, 0),
+ USBDUXSUB_FIRMWARE,
+ VENDOR_DIR_OUT,
+ USBDUXSUB_CPUCS, 0x0000,
+ tmp, 1,
+ BULK_TIMEOUT);
+ if (ret < 0) {
+ dev_err(dev->class_dev, "can not stop firmware\n");
+ goto done;
+ }
+
+ /* upload the new firmware to the device */
+ ret = usb_control_msg(usb, usb_sndctrlpipe(usb, 0),
+ USBDUXSUB_FIRMWARE,
+ VENDOR_DIR_OUT,
+ 0, 0x0000,
+ buf, size,
+ BULK_TIMEOUT);
+ if (ret < 0) {
+ dev_err(dev->class_dev, "firmware upload failed\n");
+ goto done;
+ }
+
+ /* start the new firmware on the device */
+ *tmp = 0; /* 7f92 to zero */
+ ret = usb_control_msg(usb, usb_sndctrlpipe(usb, 0),
+ USBDUXSUB_FIRMWARE,
+ VENDOR_DIR_OUT,
+ USBDUXSUB_CPUCS, 0x0000,
+ tmp, 1,
+ BULK_TIMEOUT);
+ if (ret < 0)
+ dev_err(dev->class_dev, "can not start firmware\n");
+
+done:
+ kfree(tmp);
+ kfree(buf);
+ return ret;
+}
+
+static int usbduxsigma_alloc_usb_buffers(struct comedi_device *dev)
+{
+ struct usb_device *usb = comedi_to_usb_dev(dev);
+ struct usbduxsigma_private *devpriv = dev->private;
+ struct urb *urb;
+ int i;
+
+ devpriv->dux_commands = kzalloc(SIZEOFDUXBUFFER, GFP_KERNEL);
+ devpriv->in_buf = kzalloc(SIZEINBUF, GFP_KERNEL);
+ devpriv->insn_buf = kzalloc(SIZEINSNBUF, GFP_KERNEL);
+ devpriv->ai_urbs = kcalloc(devpriv->n_ai_urbs, sizeof(urb), GFP_KERNEL);
+ devpriv->ao_urbs = kcalloc(devpriv->n_ao_urbs, sizeof(urb), GFP_KERNEL);
+ if (!devpriv->dux_commands || !devpriv->in_buf || !devpriv->insn_buf ||
+ !devpriv->ai_urbs || !devpriv->ao_urbs)
+ return -ENOMEM;
+
+ for (i = 0; i < devpriv->n_ai_urbs; i++) {
+ /* one frame: 1ms */
+ urb = usb_alloc_urb(1, GFP_KERNEL);
+ if (!urb)
+ return -ENOMEM;
+ devpriv->ai_urbs[i] = urb;
+ urb->dev = usb;
+ /* will be filled later with a pointer to the comedi-device */
+ /* and ONLY then the urb should be submitted */
+ urb->context = NULL;
+ urb->pipe = usb_rcvisocpipe(usb, 6);
+ urb->transfer_flags = URB_ISO_ASAP;
+ urb->transfer_buffer = kzalloc(SIZEINBUF, GFP_KERNEL);
+ if (!urb->transfer_buffer)
+ return -ENOMEM;
+ urb->complete = usbduxsigma_ai_urb_complete;
+ urb->number_of_packets = 1;
+ urb->transfer_buffer_length = SIZEINBUF;
+ urb->iso_frame_desc[0].offset = 0;
+ urb->iso_frame_desc[0].length = SIZEINBUF;
+ }
+
+ for (i = 0; i < devpriv->n_ao_urbs; i++) {
+ /* one frame: 1ms */
+ urb = usb_alloc_urb(1, GFP_KERNEL);
+ if (!urb)
+ return -ENOMEM;
+ devpriv->ao_urbs[i] = urb;
+ urb->dev = usb;
+ /* will be filled later with a pointer to the comedi-device */
+ /* and ONLY then the urb should be submitted */
+ urb->context = NULL;
+ urb->pipe = usb_sndisocpipe(usb, 2);
+ urb->transfer_flags = URB_ISO_ASAP;
+ urb->transfer_buffer = kzalloc(SIZEOUTBUF, GFP_KERNEL);
+ if (!urb->transfer_buffer)
+ return -ENOMEM;
+ urb->complete = usbduxsigma_ao_urb_complete;
+ urb->number_of_packets = 1;
+ urb->transfer_buffer_length = SIZEOUTBUF;
+ urb->iso_frame_desc[0].offset = 0;
+ urb->iso_frame_desc[0].length = SIZEOUTBUF;
+ if (devpriv->high_speed)
+ urb->interval = 8; /* uframes */
+ else
+ urb->interval = 1; /* frames */
+ }
+
+ if (devpriv->pwm_buf_sz) {
+ urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!urb)
+ return -ENOMEM;
+ devpriv->pwm_urb = urb;
+
+ urb->transfer_buffer = kzalloc(devpriv->pwm_buf_sz,
+ GFP_KERNEL);
+ if (!urb->transfer_buffer)
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+static void usbduxsigma_free_usb_buffers(struct comedi_device *dev)
+{
+ struct usbduxsigma_private *devpriv = dev->private;
+ struct urb *urb;
+ int i;
+
+ urb = devpriv->pwm_urb;
+ if (urb) {
+ kfree(urb->transfer_buffer);
+ usb_free_urb(urb);
+ }
+ if (devpriv->ao_urbs) {
+ for (i = 0; i < devpriv->n_ao_urbs; i++) {
+ urb = devpriv->ao_urbs[i];
+ if (urb) {
+ kfree(urb->transfer_buffer);
+ usb_free_urb(urb);
+ }
+ }
+ kfree(devpriv->ao_urbs);
+ }
+ if (devpriv->ai_urbs) {
+ for (i = 0; i < devpriv->n_ai_urbs; i++) {
+ urb = devpriv->ai_urbs[i];
+ if (urb) {
+ kfree(urb->transfer_buffer);
+ usb_free_urb(urb);
+ }
+ }
+ kfree(devpriv->ai_urbs);
+ }
+ kfree(devpriv->insn_buf);
+ kfree(devpriv->in_buf);
+ kfree(devpriv->dux_commands);
+}
+
+static int usbduxsigma_auto_attach(struct comedi_device *dev,
+ unsigned long context_unused)
+{
+ struct usb_interface *intf = comedi_to_usb_interface(dev);
+ struct usb_device *usb = comedi_to_usb_dev(dev);
+ struct usbduxsigma_private *devpriv;
+ struct comedi_subdevice *s;
+ int offset;
+ int ret;
+
+ devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
+ if (!devpriv)
+ return -ENOMEM;
+
+ sema_init(&devpriv->sem, 1);
+
+ usb_set_intfdata(intf, devpriv);
+
+ devpriv->high_speed = (usb->speed == USB_SPEED_HIGH);
+ if (devpriv->high_speed) {
+ devpriv->n_ai_urbs = NUMOFINBUFFERSHIGH;
+ devpriv->n_ao_urbs = NUMOFOUTBUFFERSHIGH;
+ devpriv->pwm_buf_sz = 512;
+ } else {
+ devpriv->n_ai_urbs = NUMOFINBUFFERSFULL;
+ devpriv->n_ao_urbs = NUMOFOUTBUFFERSFULL;
+ }
+
+ ret = usbduxsigma_alloc_usb_buffers(dev);
+ if (ret)
+ return ret;
+
+ /* setting to alternate setting 3: enabling iso ep and bulk ep. */
+ ret = usb_set_interface(usb, intf->altsetting->desc.bInterfaceNumber,
+ 3);
+ if (ret < 0) {
+ dev_err(dev->class_dev,
+ "could not set alternate setting 3 in high speed\n");
+ return ret;
+ }
+
+ ret = comedi_load_firmware(dev, &usb->dev, FIRMWARE,
+ usbduxsigma_firmware_upload, 0);
+ if (ret)
+ return ret;
+
+ ret = comedi_alloc_subdevices(dev, (devpriv->high_speed) ? 4 : 3);
+ if (ret)
+ return ret;
+
+ /* Analog Input subdevice */
+ s = &dev->subdevices[0];
+ dev->read_subdev = s;
+ s->type = COMEDI_SUBD_AI;
+ s->subdev_flags = SDF_READABLE | SDF_GROUND | SDF_CMD_READ | SDF_LSAMPL;
+ s->n_chan = NUMCHANNELS;
+ s->len_chanlist = NUMCHANNELS;
+ s->maxdata = 0x00ffffff;
+ s->range_table = &usbduxsigma_ai_range;
+ s->insn_read = usbduxsigma_ai_insn_read;
+ s->do_cmdtest = usbduxsigma_ai_cmdtest;
+ s->do_cmd = usbduxsigma_ai_cmd;
+ s->cancel = usbduxsigma_ai_cancel;
+
+ /* Analog Output subdevice */
+ s = &dev->subdevices[1];
+ dev->write_subdev = s;
+ s->type = COMEDI_SUBD_AO;
+ s->subdev_flags = SDF_WRITABLE | SDF_GROUND | SDF_CMD_WRITE;
+ s->n_chan = 4;
+ s->len_chanlist = s->n_chan;
+ s->maxdata = 0x00ff;
+ s->range_table = &range_unipolar2_5;
+ s->insn_write = usbduxsigma_ao_insn_write;
+ s->insn_read = usbduxsigma_ao_insn_read;
+ s->do_cmdtest = usbduxsigma_ao_cmdtest;
+ s->do_cmd = usbduxsigma_ao_cmd;
+ s->cancel = usbduxsigma_ao_cancel;
+
+ ret = comedi_alloc_subdev_readback(s);
+ if (ret)
+ return ret;
+
+ /* Digital I/O subdevice */
+ s = &dev->subdevices[2];
+ s->type = COMEDI_SUBD_DIO;
+ s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
+ s->n_chan = 24;
+ s->maxdata = 1;
+ s->range_table = &range_digital;
+ s->insn_bits = usbduxsigma_dio_insn_bits;
+ s->insn_config = usbduxsigma_dio_insn_config;
+
+ if (devpriv->high_speed) {
+ /* Timer / pwm subdevice */
+ s = &dev->subdevices[3];
+ s->type = COMEDI_SUBD_PWM;
+ s->subdev_flags = SDF_WRITABLE | SDF_PWM_HBRIDGE;
+ s->n_chan = 8;
+ s->maxdata = devpriv->pwm_buf_sz;
+ s->insn_write = usbduxsigma_pwm_write;
+ s->insn_config = usbduxsigma_pwm_config;
+
+ usbduxsigma_pwm_period(dev, s, PWM_DEFAULT_PERIOD);
+ }
+
+ offset = usbduxsigma_getstatusinfo(dev, 0);
+ if (offset < 0) {
+ dev_err(dev->class_dev,
+ "Communication to USBDUXSIGMA failed! Check firmware and cabling.\n");
+ return offset;
+ }
+
+ dev_info(dev->class_dev, "ADC_zero = %x\n", offset);
+
+ return 0;
+}
+
+static void usbduxsigma_detach(struct comedi_device *dev)
+{
+ struct usb_interface *intf = comedi_to_usb_interface(dev);
+ struct usbduxsigma_private *devpriv = dev->private;
+
+ usb_set_intfdata(intf, NULL);
+
+ if (!devpriv)
+ return;
+
+ down(&devpriv->sem);
+
+ /* force unlink all urbs */
+ usbduxsigma_ai_stop(dev, 1);
+ usbduxsigma_ao_stop(dev, 1);
+ usbduxsigma_pwm_stop(dev, 1);
+
+ usbduxsigma_free_usb_buffers(dev);
+
+ up(&devpriv->sem);
+}
+
+static struct comedi_driver usbduxsigma_driver = {
+ .driver_name = "usbduxsigma",
+ .module = THIS_MODULE,
+ .auto_attach = usbduxsigma_auto_attach,
+ .detach = usbduxsigma_detach,
+};
+
+static int usbduxsigma_usb_probe(struct usb_interface *intf,
+ const struct usb_device_id *id)
+{
+ return comedi_usb_auto_config(intf, &usbduxsigma_driver, 0);
+}
+
+static const struct usb_device_id usbduxsigma_usb_table[] = {
+ { USB_DEVICE(0x13d8, 0x0020) },
+ { USB_DEVICE(0x13d8, 0x0021) },
+ { USB_DEVICE(0x13d8, 0x0022) },
+ { }
+};
+MODULE_DEVICE_TABLE(usb, usbduxsigma_usb_table);
+
+static struct usb_driver usbduxsigma_usb_driver = {
+ .name = "usbduxsigma",
+ .probe = usbduxsigma_usb_probe,
+ .disconnect = comedi_usb_auto_unconfig,
+ .id_table = usbduxsigma_usb_table,
+};
+module_comedi_usb_driver(usbduxsigma_driver, usbduxsigma_usb_driver);
+
+MODULE_AUTHOR("Bernd Porr, BerndPorr@f2s.com");
+MODULE_DESCRIPTION("Stirling/ITL USB-DUX SIGMA -- Bernd.Porr@f2s.com");
+MODULE_LICENSE("GPL");
+MODULE_FIRMWARE(FIRMWARE);
diff --git a/drivers/staging/comedi/drivers/vmk80xx.c b/drivers/staging/comedi/drivers/vmk80xx.c
new file mode 100644
index 000000000..a0906685e
--- /dev/null
+++ b/drivers/staging/comedi/drivers/vmk80xx.c
@@ -0,0 +1,885 @@
+/*
+ comedi/drivers/vmk80xx.c
+ Velleman USB Board Low-Level Driver
+
+ Copyright (C) 2009 Manuel Gebele <forensixs@gmx.de>, Germany
+
+ COMEDI - Linux Control and Measurement Device Interface
+ Copyright (C) 2000 David A. Schleef <ds@schleef.org>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+*/
+/*
+ * Driver: vmk80xx
+ * Description: Velleman USB Board Low-Level Driver
+ * Devices: [Velleman] K8055 (K8055/VM110), K8061 (K8061/VM140),
+ * VM110 (K8055/VM110), VM140 (K8061/VM140)
+ * Author: Manuel Gebele <forensixs@gmx.de>
+ * Updated: Sun, 10 May 2009 11:14:59 +0200
+ * Status: works
+ *
+ * Supports:
+ * - analog input
+ * - analog output
+ * - digital input
+ * - digital output
+ * - counter
+ * - pwm
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/errno.h>
+#include <linux/input.h>
+#include <linux/slab.h>
+#include <linux/poll.h>
+#include <linux/uaccess.h>
+
+#include "../comedi_usb.h"
+
+enum {
+ DEVICE_VMK8055,
+ DEVICE_VMK8061
+};
+
+#define VMK8055_DI_REG 0x00
+#define VMK8055_DO_REG 0x01
+#define VMK8055_AO1_REG 0x02
+#define VMK8055_AO2_REG 0x03
+#define VMK8055_AI1_REG 0x02
+#define VMK8055_AI2_REG 0x03
+#define VMK8055_CNT1_REG 0x04
+#define VMK8055_CNT2_REG 0x06
+
+#define VMK8061_CH_REG 0x01
+#define VMK8061_DI_REG 0x01
+#define VMK8061_DO_REG 0x01
+#define VMK8061_PWM_REG1 0x01
+#define VMK8061_PWM_REG2 0x02
+#define VMK8061_CNT_REG 0x02
+#define VMK8061_AO_REG 0x02
+#define VMK8061_AI_REG1 0x02
+#define VMK8061_AI_REG2 0x03
+
+#define VMK8055_CMD_RST 0x00
+#define VMK8055_CMD_DEB1_TIME 0x01
+#define VMK8055_CMD_DEB2_TIME 0x02
+#define VMK8055_CMD_RST_CNT1 0x03
+#define VMK8055_CMD_RST_CNT2 0x04
+#define VMK8055_CMD_WRT_AD 0x05
+
+#define VMK8061_CMD_RD_AI 0x00
+#define VMK8061_CMR_RD_ALL_AI 0x01 /* !non-active! */
+#define VMK8061_CMD_SET_AO 0x02
+#define VMK8061_CMD_SET_ALL_AO 0x03 /* !non-active! */
+#define VMK8061_CMD_OUT_PWM 0x04
+#define VMK8061_CMD_RD_DI 0x05
+#define VMK8061_CMD_DO 0x06 /* !non-active! */
+#define VMK8061_CMD_CLR_DO 0x07
+#define VMK8061_CMD_SET_DO 0x08
+#define VMK8061_CMD_RD_CNT 0x09 /* TODO: completely pointless? */
+#define VMK8061_CMD_RST_CNT 0x0a /* TODO: completely pointless? */
+#define VMK8061_CMD_RD_VERSION 0x0b /* internal usage */
+#define VMK8061_CMD_RD_JMP_STAT 0x0c /* TODO: not implemented yet */
+#define VMK8061_CMD_RD_PWR_STAT 0x0d /* internal usage */
+#define VMK8061_CMD_RD_DO 0x0e
+#define VMK8061_CMD_RD_AO 0x0f
+#define VMK8061_CMD_RD_PWM 0x10
+
+#define IC3_VERSION (1 << 0)
+#define IC6_VERSION (1 << 1)
+
+enum vmk80xx_model {
+ VMK8055_MODEL,
+ VMK8061_MODEL
+};
+
+static const struct comedi_lrange vmk8061_range = {
+ 2, {
+ UNI_RANGE(5),
+ UNI_RANGE(10)
+ }
+};
+
+struct vmk80xx_board {
+ const char *name;
+ enum vmk80xx_model model;
+ const struct comedi_lrange *range;
+ int ai_nchans;
+ unsigned int ai_maxdata;
+ int ao_nchans;
+ int di_nchans;
+ unsigned int cnt_maxdata;
+ int pwm_nchans;
+ unsigned int pwm_maxdata;
+};
+
+static const struct vmk80xx_board vmk80xx_boardinfo[] = {
+ [DEVICE_VMK8055] = {
+ .name = "K8055 (VM110)",
+ .model = VMK8055_MODEL,
+ .range = &range_unipolar5,
+ .ai_nchans = 2,
+ .ai_maxdata = 0x00ff,
+ .ao_nchans = 2,
+ .di_nchans = 6,
+ .cnt_maxdata = 0xffff,
+ },
+ [DEVICE_VMK8061] = {
+ .name = "K8061 (VM140)",
+ .model = VMK8061_MODEL,
+ .range = &vmk8061_range,
+ .ai_nchans = 8,
+ .ai_maxdata = 0x03ff,
+ .ao_nchans = 8,
+ .di_nchans = 8,
+ .cnt_maxdata = 0, /* unknown, device is not writeable */
+ .pwm_nchans = 1,
+ .pwm_maxdata = 0x03ff,
+ },
+};
+
+struct vmk80xx_private {
+ struct usb_endpoint_descriptor *ep_rx;
+ struct usb_endpoint_descriptor *ep_tx;
+ struct semaphore limit_sem;
+ unsigned char *usb_rx_buf;
+ unsigned char *usb_tx_buf;
+ enum vmk80xx_model model;
+};
+
+static void vmk80xx_do_bulk_msg(struct comedi_device *dev)
+{
+ struct vmk80xx_private *devpriv = dev->private;
+ struct usb_device *usb = comedi_to_usb_dev(dev);
+ __u8 tx_addr;
+ __u8 rx_addr;
+ unsigned int tx_pipe;
+ unsigned int rx_pipe;
+ size_t size;
+
+ tx_addr = devpriv->ep_tx->bEndpointAddress;
+ rx_addr = devpriv->ep_rx->bEndpointAddress;
+ tx_pipe = usb_sndbulkpipe(usb, tx_addr);
+ rx_pipe = usb_rcvbulkpipe(usb, rx_addr);
+
+ /*
+ * The max packet size attributes of the K8061
+ * input/output endpoints are identical
+ */
+ size = le16_to_cpu(devpriv->ep_tx->wMaxPacketSize);
+
+ usb_bulk_msg(usb, tx_pipe, devpriv->usb_tx_buf,
+ size, NULL, devpriv->ep_tx->bInterval);
+ usb_bulk_msg(usb, rx_pipe, devpriv->usb_rx_buf, size, NULL, HZ * 10);
+}
+
+static int vmk80xx_read_packet(struct comedi_device *dev)
+{
+ struct vmk80xx_private *devpriv = dev->private;
+ struct usb_device *usb = comedi_to_usb_dev(dev);
+ struct usb_endpoint_descriptor *ep;
+ unsigned int pipe;
+
+ if (devpriv->model == VMK8061_MODEL) {
+ vmk80xx_do_bulk_msg(dev);
+ return 0;
+ }
+
+ ep = devpriv->ep_rx;
+ pipe = usb_rcvintpipe(usb, ep->bEndpointAddress);
+ return usb_interrupt_msg(usb, pipe, devpriv->usb_rx_buf,
+ le16_to_cpu(ep->wMaxPacketSize), NULL,
+ HZ * 10);
+}
+
+static int vmk80xx_write_packet(struct comedi_device *dev, int cmd)
+{
+ struct vmk80xx_private *devpriv = dev->private;
+ struct usb_device *usb = comedi_to_usb_dev(dev);
+ struct usb_endpoint_descriptor *ep;
+ unsigned int pipe;
+
+ devpriv->usb_tx_buf[0] = cmd;
+
+ if (devpriv->model == VMK8061_MODEL) {
+ vmk80xx_do_bulk_msg(dev);
+ return 0;
+ }
+
+ ep = devpriv->ep_tx;
+ pipe = usb_sndintpipe(usb, ep->bEndpointAddress);
+ return usb_interrupt_msg(usb, pipe, devpriv->usb_tx_buf,
+ le16_to_cpu(ep->wMaxPacketSize), NULL,
+ HZ * 10);
+}
+
+static int vmk80xx_reset_device(struct comedi_device *dev)
+{
+ struct vmk80xx_private *devpriv = dev->private;
+ size_t size;
+ int retval;
+
+ size = le16_to_cpu(devpriv->ep_tx->wMaxPacketSize);
+ memset(devpriv->usb_tx_buf, 0, size);
+ retval = vmk80xx_write_packet(dev, VMK8055_CMD_RST);
+ if (retval)
+ return retval;
+ /* set outputs to known state as we cannot read them */
+ return vmk80xx_write_packet(dev, VMK8055_CMD_WRT_AD);
+}
+
+static int vmk80xx_ai_insn_read(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ struct vmk80xx_private *devpriv = dev->private;
+ int chan;
+ int reg[2];
+ int n;
+
+ down(&devpriv->limit_sem);
+ chan = CR_CHAN(insn->chanspec);
+
+ switch (devpriv->model) {
+ case VMK8055_MODEL:
+ if (!chan)
+ reg[0] = VMK8055_AI1_REG;
+ else
+ reg[0] = VMK8055_AI2_REG;
+ break;
+ case VMK8061_MODEL:
+ default:
+ reg[0] = VMK8061_AI_REG1;
+ reg[1] = VMK8061_AI_REG2;
+ devpriv->usb_tx_buf[0] = VMK8061_CMD_RD_AI;
+ devpriv->usb_tx_buf[VMK8061_CH_REG] = chan;
+ break;
+ }
+
+ for (n = 0; n < insn->n; n++) {
+ if (vmk80xx_read_packet(dev))
+ break;
+
+ if (devpriv->model == VMK8055_MODEL) {
+ data[n] = devpriv->usb_rx_buf[reg[0]];
+ continue;
+ }
+
+ /* VMK8061_MODEL */
+ data[n] = devpriv->usb_rx_buf[reg[0]] + 256 *
+ devpriv->usb_rx_buf[reg[1]];
+ }
+
+ up(&devpriv->limit_sem);
+
+ return n;
+}
+
+static int vmk80xx_ao_insn_write(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ struct vmk80xx_private *devpriv = dev->private;
+ int chan;
+ int cmd;
+ int reg;
+ int n;
+
+ down(&devpriv->limit_sem);
+ chan = CR_CHAN(insn->chanspec);
+
+ switch (devpriv->model) {
+ case VMK8055_MODEL:
+ cmd = VMK8055_CMD_WRT_AD;
+ if (!chan)
+ reg = VMK8055_AO1_REG;
+ else
+ reg = VMK8055_AO2_REG;
+ break;
+ default: /* NOTE: avoid compiler warnings */
+ cmd = VMK8061_CMD_SET_AO;
+ reg = VMK8061_AO_REG;
+ devpriv->usb_tx_buf[VMK8061_CH_REG] = chan;
+ break;
+ }
+
+ for (n = 0; n < insn->n; n++) {
+ devpriv->usb_tx_buf[reg] = data[n];
+
+ if (vmk80xx_write_packet(dev, cmd))
+ break;
+ }
+
+ up(&devpriv->limit_sem);
+
+ return n;
+}
+
+static int vmk80xx_ao_insn_read(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ struct vmk80xx_private *devpriv = dev->private;
+ int chan;
+ int reg;
+ int n;
+
+ down(&devpriv->limit_sem);
+ chan = CR_CHAN(insn->chanspec);
+
+ reg = VMK8061_AO_REG - 1;
+
+ devpriv->usb_tx_buf[0] = VMK8061_CMD_RD_AO;
+
+ for (n = 0; n < insn->n; n++) {
+ if (vmk80xx_read_packet(dev))
+ break;
+
+ data[n] = devpriv->usb_rx_buf[reg + chan];
+ }
+
+ up(&devpriv->limit_sem);
+
+ return n;
+}
+
+static int vmk80xx_di_insn_bits(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ struct vmk80xx_private *devpriv = dev->private;
+ unsigned char *rx_buf;
+ int reg;
+ int retval;
+
+ down(&devpriv->limit_sem);
+
+ rx_buf = devpriv->usb_rx_buf;
+
+ if (devpriv->model == VMK8061_MODEL) {
+ reg = VMK8061_DI_REG;
+ devpriv->usb_tx_buf[0] = VMK8061_CMD_RD_DI;
+ } else {
+ reg = VMK8055_DI_REG;
+ }
+
+ retval = vmk80xx_read_packet(dev);
+
+ if (!retval) {
+ if (devpriv->model == VMK8055_MODEL)
+ data[1] = (((rx_buf[reg] >> 4) & 0x03) |
+ ((rx_buf[reg] << 2) & 0x04) |
+ ((rx_buf[reg] >> 3) & 0x18));
+ else
+ data[1] = rx_buf[reg];
+
+ retval = 2;
+ }
+
+ up(&devpriv->limit_sem);
+
+ return retval;
+}
+
+static int vmk80xx_do_insn_bits(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ struct vmk80xx_private *devpriv = dev->private;
+ unsigned char *rx_buf = devpriv->usb_rx_buf;
+ unsigned char *tx_buf = devpriv->usb_tx_buf;
+ int reg, cmd;
+ int ret = 0;
+
+ if (devpriv->model == VMK8061_MODEL) {
+ reg = VMK8061_DO_REG;
+ cmd = VMK8061_CMD_DO;
+ } else { /* VMK8055_MODEL */
+ reg = VMK8055_DO_REG;
+ cmd = VMK8055_CMD_WRT_AD;
+ }
+
+ down(&devpriv->limit_sem);
+
+ if (comedi_dio_update_state(s, data)) {
+ tx_buf[reg] = s->state;
+ ret = vmk80xx_write_packet(dev, cmd);
+ if (ret)
+ goto out;
+ }
+
+ if (devpriv->model == VMK8061_MODEL) {
+ tx_buf[0] = VMK8061_CMD_RD_DO;
+ ret = vmk80xx_read_packet(dev);
+ if (ret)
+ goto out;
+ data[1] = rx_buf[reg];
+ } else {
+ data[1] = s->state;
+ }
+
+out:
+ up(&devpriv->limit_sem);
+
+ return ret ? ret : insn->n;
+}
+
+static int vmk80xx_cnt_insn_read(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ struct vmk80xx_private *devpriv = dev->private;
+ int chan;
+ int reg[2];
+ int n;
+
+ down(&devpriv->limit_sem);
+ chan = CR_CHAN(insn->chanspec);
+
+ switch (devpriv->model) {
+ case VMK8055_MODEL:
+ if (!chan)
+ reg[0] = VMK8055_CNT1_REG;
+ else
+ reg[0] = VMK8055_CNT2_REG;
+ break;
+ case VMK8061_MODEL:
+ default:
+ reg[0] = VMK8061_CNT_REG;
+ reg[1] = VMK8061_CNT_REG;
+ devpriv->usb_tx_buf[0] = VMK8061_CMD_RD_CNT;
+ break;
+ }
+
+ for (n = 0; n < insn->n; n++) {
+ if (vmk80xx_read_packet(dev))
+ break;
+
+ if (devpriv->model == VMK8055_MODEL)
+ data[n] = devpriv->usb_rx_buf[reg[0]];
+ else /* VMK8061_MODEL */
+ data[n] = devpriv->usb_rx_buf[reg[0] * (chan + 1) + 1]
+ + 256 * devpriv->usb_rx_buf[reg[1] * 2 + 2];
+ }
+
+ up(&devpriv->limit_sem);
+
+ return n;
+}
+
+static int vmk80xx_cnt_insn_config(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ struct vmk80xx_private *devpriv = dev->private;
+ unsigned int chan = CR_CHAN(insn->chanspec);
+ int cmd;
+ int reg;
+ int ret;
+
+ down(&devpriv->limit_sem);
+ switch (data[0]) {
+ case INSN_CONFIG_RESET:
+ if (devpriv->model == VMK8055_MODEL) {
+ if (!chan) {
+ cmd = VMK8055_CMD_RST_CNT1;
+ reg = VMK8055_CNT1_REG;
+ } else {
+ cmd = VMK8055_CMD_RST_CNT2;
+ reg = VMK8055_CNT2_REG;
+ }
+ devpriv->usb_tx_buf[reg] = 0x00;
+ } else {
+ cmd = VMK8061_CMD_RST_CNT;
+ }
+ ret = vmk80xx_write_packet(dev, cmd);
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+ up(&devpriv->limit_sem);
+
+ return ret ? ret : insn->n;
+}
+
+static int vmk80xx_cnt_insn_write(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ struct vmk80xx_private *devpriv = dev->private;
+ unsigned long debtime;
+ unsigned long val;
+ int chan;
+ int cmd;
+ int n;
+
+ down(&devpriv->limit_sem);
+ chan = CR_CHAN(insn->chanspec);
+
+ if (!chan)
+ cmd = VMK8055_CMD_DEB1_TIME;
+ else
+ cmd = VMK8055_CMD_DEB2_TIME;
+
+ for (n = 0; n < insn->n; n++) {
+ debtime = data[n];
+ if (debtime == 0)
+ debtime = 1;
+
+ /* TODO: Prevent overflows */
+ if (debtime > 7450)
+ debtime = 7450;
+
+ val = int_sqrt(debtime * 1000 / 115);
+ if (((val + 1) * val) < debtime * 1000 / 115)
+ val += 1;
+
+ devpriv->usb_tx_buf[6 + chan] = val;
+
+ if (vmk80xx_write_packet(dev, cmd))
+ break;
+ }
+
+ up(&devpriv->limit_sem);
+
+ return n;
+}
+
+static int vmk80xx_pwm_insn_read(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ struct vmk80xx_private *devpriv = dev->private;
+ unsigned char *tx_buf;
+ unsigned char *rx_buf;
+ int reg[2];
+ int n;
+
+ down(&devpriv->limit_sem);
+
+ tx_buf = devpriv->usb_tx_buf;
+ rx_buf = devpriv->usb_rx_buf;
+
+ reg[0] = VMK8061_PWM_REG1;
+ reg[1] = VMK8061_PWM_REG2;
+
+ tx_buf[0] = VMK8061_CMD_RD_PWM;
+
+ for (n = 0; n < insn->n; n++) {
+ if (vmk80xx_read_packet(dev))
+ break;
+
+ data[n] = rx_buf[reg[0]] + 4 * rx_buf[reg[1]];
+ }
+
+ up(&devpriv->limit_sem);
+
+ return n;
+}
+
+static int vmk80xx_pwm_insn_write(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ struct vmk80xx_private *devpriv = dev->private;
+ unsigned char *tx_buf;
+ int reg[2];
+ int cmd;
+ int n;
+
+ down(&devpriv->limit_sem);
+
+ tx_buf = devpriv->usb_tx_buf;
+
+ reg[0] = VMK8061_PWM_REG1;
+ reg[1] = VMK8061_PWM_REG2;
+
+ cmd = VMK8061_CMD_OUT_PWM;
+
+ /*
+ * The followin piece of code was translated from the inline
+ * assembler code in the DLL source code.
+ *
+ * asm
+ * mov eax, k ; k is the value (data[n])
+ * and al, 03h ; al are the lower 8 bits of eax
+ * mov lo, al ; lo is the low part (tx_buf[reg[0]])
+ * mov eax, k
+ * shr eax, 2 ; right shift eax register by 2
+ * mov hi, al ; hi is the high part (tx_buf[reg[1]])
+ * end;
+ */
+ for (n = 0; n < insn->n; n++) {
+ tx_buf[reg[0]] = (unsigned char)(data[n] & 0x03);
+ tx_buf[reg[1]] = (unsigned char)(data[n] >> 2) & 0xff;
+
+ if (vmk80xx_write_packet(dev, cmd))
+ break;
+ }
+
+ up(&devpriv->limit_sem);
+
+ return n;
+}
+
+static int vmk80xx_find_usb_endpoints(struct comedi_device *dev)
+{
+ struct vmk80xx_private *devpriv = dev->private;
+ struct usb_interface *intf = comedi_to_usb_interface(dev);
+ struct usb_host_interface *iface_desc = intf->cur_altsetting;
+ struct usb_endpoint_descriptor *ep_desc;
+ int i;
+
+ if (iface_desc->desc.bNumEndpoints != 2)
+ return -ENODEV;
+
+ for (i = 0; i < iface_desc->desc.bNumEndpoints; i++) {
+ ep_desc = &iface_desc->endpoint[i].desc;
+
+ if (usb_endpoint_is_int_in(ep_desc) ||
+ usb_endpoint_is_bulk_in(ep_desc)) {
+ if (!devpriv->ep_rx)
+ devpriv->ep_rx = ep_desc;
+ continue;
+ }
+
+ if (usb_endpoint_is_int_out(ep_desc) ||
+ usb_endpoint_is_bulk_out(ep_desc)) {
+ if (!devpriv->ep_tx)
+ devpriv->ep_tx = ep_desc;
+ continue;
+ }
+ }
+
+ if (!devpriv->ep_rx || !devpriv->ep_tx)
+ return -ENODEV;
+
+ return 0;
+}
+
+static int vmk80xx_alloc_usb_buffers(struct comedi_device *dev)
+{
+ struct vmk80xx_private *devpriv = dev->private;
+ size_t size;
+
+ size = le16_to_cpu(devpriv->ep_rx->wMaxPacketSize);
+ devpriv->usb_rx_buf = kzalloc(size, GFP_KERNEL);
+ if (!devpriv->usb_rx_buf)
+ return -ENOMEM;
+
+ size = le16_to_cpu(devpriv->ep_tx->wMaxPacketSize);
+ devpriv->usb_tx_buf = kzalloc(size, GFP_KERNEL);
+ if (!devpriv->usb_tx_buf) {
+ kfree(devpriv->usb_rx_buf);
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+static int vmk80xx_init_subdevices(struct comedi_device *dev)
+{
+ const struct vmk80xx_board *boardinfo = dev->board_ptr;
+ struct vmk80xx_private *devpriv = dev->private;
+ struct comedi_subdevice *s;
+ int n_subd;
+ int ret;
+
+ down(&devpriv->limit_sem);
+
+ if (devpriv->model == VMK8055_MODEL)
+ n_subd = 5;
+ else
+ n_subd = 6;
+ ret = comedi_alloc_subdevices(dev, n_subd);
+ if (ret) {
+ up(&devpriv->limit_sem);
+ return ret;
+ }
+
+ /* Analog input subdevice */
+ s = &dev->subdevices[0];
+ s->type = COMEDI_SUBD_AI;
+ s->subdev_flags = SDF_READABLE | SDF_GROUND;
+ s->n_chan = boardinfo->ai_nchans;
+ s->maxdata = boardinfo->ai_maxdata;
+ s->range_table = boardinfo->range;
+ s->insn_read = vmk80xx_ai_insn_read;
+
+ /* Analog output subdevice */
+ s = &dev->subdevices[1];
+ s->type = COMEDI_SUBD_AO;
+ s->subdev_flags = SDF_WRITABLE | SDF_GROUND;
+ s->n_chan = boardinfo->ao_nchans;
+ s->maxdata = 0x00ff;
+ s->range_table = boardinfo->range;
+ s->insn_write = vmk80xx_ao_insn_write;
+ if (devpriv->model == VMK8061_MODEL) {
+ s->subdev_flags |= SDF_READABLE;
+ s->insn_read = vmk80xx_ao_insn_read;
+ }
+
+ /* Digital input subdevice */
+ s = &dev->subdevices[2];
+ s->type = COMEDI_SUBD_DI;
+ s->subdev_flags = SDF_READABLE;
+ s->n_chan = boardinfo->di_nchans;
+ s->maxdata = 1;
+ s->range_table = &range_digital;
+ s->insn_bits = vmk80xx_di_insn_bits;
+
+ /* Digital output subdevice */
+ s = &dev->subdevices[3];
+ s->type = COMEDI_SUBD_DO;
+ s->subdev_flags = SDF_WRITABLE;
+ s->n_chan = 8;
+ s->maxdata = 1;
+ s->range_table = &range_digital;
+ s->insn_bits = vmk80xx_do_insn_bits;
+
+ /* Counter subdevice */
+ s = &dev->subdevices[4];
+ s->type = COMEDI_SUBD_COUNTER;
+ s->subdev_flags = SDF_READABLE;
+ s->n_chan = 2;
+ s->maxdata = boardinfo->cnt_maxdata;
+ s->insn_read = vmk80xx_cnt_insn_read;
+ s->insn_config = vmk80xx_cnt_insn_config;
+ if (devpriv->model == VMK8055_MODEL) {
+ s->subdev_flags |= SDF_WRITABLE;
+ s->insn_write = vmk80xx_cnt_insn_write;
+ }
+
+ /* PWM subdevice */
+ if (devpriv->model == VMK8061_MODEL) {
+ s = &dev->subdevices[5];
+ s->type = COMEDI_SUBD_PWM;
+ s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
+ s->n_chan = boardinfo->pwm_nchans;
+ s->maxdata = boardinfo->pwm_maxdata;
+ s->insn_read = vmk80xx_pwm_insn_read;
+ s->insn_write = vmk80xx_pwm_insn_write;
+ }
+
+ up(&devpriv->limit_sem);
+
+ return 0;
+}
+
+static int vmk80xx_auto_attach(struct comedi_device *dev,
+ unsigned long context)
+{
+ struct usb_interface *intf = comedi_to_usb_interface(dev);
+ const struct vmk80xx_board *boardinfo;
+ struct vmk80xx_private *devpriv;
+ int ret;
+
+ boardinfo = &vmk80xx_boardinfo[context];
+ dev->board_ptr = boardinfo;
+ dev->board_name = boardinfo->name;
+
+ devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
+ if (!devpriv)
+ return -ENOMEM;
+
+ devpriv->model = boardinfo->model;
+
+ ret = vmk80xx_find_usb_endpoints(dev);
+ if (ret)
+ return ret;
+
+ ret = vmk80xx_alloc_usb_buffers(dev);
+ if (ret)
+ return ret;
+
+ sema_init(&devpriv->limit_sem, 8);
+
+ usb_set_intfdata(intf, devpriv);
+
+ if (devpriv->model == VMK8055_MODEL)
+ vmk80xx_reset_device(dev);
+
+ return vmk80xx_init_subdevices(dev);
+}
+
+static void vmk80xx_detach(struct comedi_device *dev)
+{
+ struct usb_interface *intf = comedi_to_usb_interface(dev);
+ struct vmk80xx_private *devpriv = dev->private;
+
+ if (!devpriv)
+ return;
+
+ down(&devpriv->limit_sem);
+
+ usb_set_intfdata(intf, NULL);
+
+ kfree(devpriv->usb_rx_buf);
+ kfree(devpriv->usb_tx_buf);
+
+ up(&devpriv->limit_sem);
+}
+
+static struct comedi_driver vmk80xx_driver = {
+ .module = THIS_MODULE,
+ .driver_name = "vmk80xx",
+ .auto_attach = vmk80xx_auto_attach,
+ .detach = vmk80xx_detach,
+};
+
+static int vmk80xx_usb_probe(struct usb_interface *intf,
+ const struct usb_device_id *id)
+{
+ return comedi_usb_auto_config(intf, &vmk80xx_driver, id->driver_info);
+}
+
+static const struct usb_device_id vmk80xx_usb_id_table[] = {
+ { USB_DEVICE(0x10cf, 0x5500), .driver_info = DEVICE_VMK8055 },
+ { USB_DEVICE(0x10cf, 0x5501), .driver_info = DEVICE_VMK8055 },
+ { USB_DEVICE(0x10cf, 0x5502), .driver_info = DEVICE_VMK8055 },
+ { USB_DEVICE(0x10cf, 0x5503), .driver_info = DEVICE_VMK8055 },
+ { USB_DEVICE(0x10cf, 0x8061), .driver_info = DEVICE_VMK8061 },
+ { USB_DEVICE(0x10cf, 0x8062), .driver_info = DEVICE_VMK8061 },
+ { USB_DEVICE(0x10cf, 0x8063), .driver_info = DEVICE_VMK8061 },
+ { USB_DEVICE(0x10cf, 0x8064), .driver_info = DEVICE_VMK8061 },
+ { USB_DEVICE(0x10cf, 0x8065), .driver_info = DEVICE_VMK8061 },
+ { USB_DEVICE(0x10cf, 0x8066), .driver_info = DEVICE_VMK8061 },
+ { USB_DEVICE(0x10cf, 0x8067), .driver_info = DEVICE_VMK8061 },
+ { USB_DEVICE(0x10cf, 0x8068), .driver_info = DEVICE_VMK8061 },
+ { }
+};
+MODULE_DEVICE_TABLE(usb, vmk80xx_usb_id_table);
+
+static struct usb_driver vmk80xx_usb_driver = {
+ .name = "vmk80xx",
+ .id_table = vmk80xx_usb_id_table,
+ .probe = vmk80xx_usb_probe,
+ .disconnect = comedi_usb_auto_unconfig,
+};
+module_comedi_usb_driver(vmk80xx_driver, vmk80xx_usb_driver);
+
+MODULE_AUTHOR("Manuel Gebele <forensixs@gmx.de>");
+MODULE_DESCRIPTION("Velleman USB Board Low-Level Driver");
+MODULE_SUPPORTED_DEVICE("K8055/K8061 aka VM110/VM140");
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/comedi/drivers/z8536.h b/drivers/staging/comedi/drivers/z8536.h
new file mode 100644
index 000000000..7be53109c
--- /dev/null
+++ b/drivers/staging/comedi/drivers/z8536.h
@@ -0,0 +1,202 @@
+/*
+ * Z8536 CIO Internal registers
+ */
+
+#ifndef _Z8536_H
+#define _Z8536_H
+
+/* Master Interrupt Control register */
+#define Z8536_INT_CTRL_REG 0x00
+#define Z8536_INT_CTRL_MIE BIT(7) /* Master Interrupt Enable */
+#define Z8536_INT_CTRL_DLC BIT(6) /* Disable Lower Chain */
+#define Z8536_INT_CTRL_NV BIT(5) /* No Vector */
+#define Z8536_INT_CTRL_PA_VIS BIT(4) /* Port A Vect Inc Status */
+#define Z8536_INT_CTRL_PB_VIS BIT(3) /* Port B Vect Inc Status */
+#define Z8536_INT_CTRL_VT_VIS BIT(2) /* C/T Vect Inc Status */
+#define Z8536_INT_CTRL_RJA BIT(1) /* Right Justified Addresses */
+#define Z8536_INT_CTRL_RESET BIT(0) /* Reset */
+
+/* Master Configuration Control register */
+#define Z8536_CFG_CTRL_REG 0x01
+#define Z8536_CFG_CTRL_PBE BIT(7) /* Port B Enable */
+#define Z8536_CFG_CTRL_CT1E BIT(6) /* C/T 1 Enable */
+#define Z8536_CFG_CTRL_CT2E BIT(5) /* C/T 2 Enable */
+#define Z8536_CFG_CTRL_PCE_CT3E BIT(4) /* Port C & C/T 3 Enable */
+#define Z8536_CFG_CTRL_PLC BIT(3) /* Port A/B Link Control */
+#define Z8536_CFG_CTRL_PAE BIT(2) /* Port A Enable */
+#define Z8536_CFG_CTRL_LC_INDEP (0 << 0)/* C/Ts Independent */
+#define Z8536_CFG_CTRL_LC_GATE (1 << 0)/* C/T 1 Out Gates C/T 2 */
+#define Z8536_CFG_CTRL_LC_TRIG (2 << 0)/* C/T 1 Out Triggers C/T 2 */
+#define Z8536_CFG_CTRL_LC_CLK (3 << 0)/* C/T 1 Out Clocks C/T 2 */
+#define Z8536_CFG_CTRL_LC_MASK (3 << 0)/* C/T Link Control mask */
+
+/* Interrupt Vector registers */
+#define Z8536_PA_INT_VECT_REG 0x02
+#define Z8536_PB_INT_VECT_REG 0x03
+#define Z8536_CT_INT_VECT_REG 0x04
+#define Z8536_CURR_INT_VECT_REG 0x1f
+
+/* Port A/B & Counter/Timer 1/2/3 Command and Status registers */
+#define Z8536_PA_CMDSTAT_REG 0x08
+#define Z8536_PB_CMDSTAT_REG 0x09
+#define Z8536_CT1_CMDSTAT_REG 0x0a
+#define Z8536_CT2_CMDSTAT_REG 0x0b
+#define Z8536_CT3_CMDSTAT_REG 0x0c
+#define Z8536_CT_CMDSTAT_REG(x) (0x0a + (x))
+#define Z8536_CMD_NULL (0 << 5)/* Null Code */
+#define Z8536_CMD_CLR_IP_IUS (1 << 5)/* Clear IP & IUS */
+#define Z8536_CMD_SET_IUS (2 << 5)/* Set IUS */
+#define Z8536_CMD_CLR_IUS (3 << 5)/* Clear IUS */
+#define Z8536_CMD_SET_IP (4 << 5)/* Set IP */
+#define Z8536_CMD_CLR_IP (5 << 5)/* Clear IP */
+#define Z8536_CMD_SET_IE (6 << 5)/* Set IE */
+#define Z8536_CMD_CLR_IE (7 << 5)/* Clear IE */
+#define Z8536_CMD_MASK (7 << 5)
+
+#define Z8536_STAT_IUS BIT(7) /* Interrupt Under Service */
+#define Z8536_STAT_IE BIT(6) /* Interrupt Enable */
+#define Z8536_STAT_IP BIT(5) /* Interrupt Pending */
+#define Z8536_STAT_ERR BIT(4) /* Interrupt Error */
+#define Z8536_STAT_IE_IP (Z8536_STAT_IE | Z8536_STAT_IP)
+
+#define Z8536_PAB_STAT_ORE BIT(3) /* Output Register Empty */
+#define Z8536_PAB_STAT_IRF BIT(2) /* Input Register Full */
+#define Z8536_PAB_STAT_PMF BIT(1) /* Pattern Match Flag */
+#define Z8536_PAB_CMDSTAT_IOE BIT(0) /* Interrupt On Error */
+
+#define Z8536_CT_CMD_RCC BIT(3) /* Read Counter Control */
+#define Z8536_CT_CMDSTAT_GCB BIT(2) /* Gate Command Bit */
+#define Z8536_CT_CMD_TCB BIT(1) /* Trigger Command Bit */
+#define Z8536_CT_STAT_CIP BIT(0) /* Count In Progress */
+
+/* Port Data registers */
+#define Z8536_PA_DATA_REG 0x0d
+#define Z8536_PB_DATA_REG 0x0e
+#define Z8536_PC_DATA_REG 0x0f
+
+/* Counter/Timer 1/2/3 Current Count registers */
+#define Z8536_CT1_VAL_MSB_REG 0x10
+#define Z8536_CT1_VAL_LSB_REG 0x11
+#define Z8536_CT2_VAL_MSB_REG 0x12
+#define Z8536_CT2_VAL_LSB_REG 0x13
+#define Z8536_CT3_VAL_MSB_REG 0x14
+#define Z8536_CT3_VAL_LSB_REG 0x15
+#define Z8536_CT_VAL_MSB_REG(x) (0x10 + ((x) * 2))
+#define Z8536_CT_VAL_LSB_REG(x) (0x11 + ((x) * 2))
+
+/* Counter/Timer 1/2/3 Time Constant registers */
+#define Z8536_CT1_RELOAD_MSB_REG 0x16
+#define Z8536_CT1_RELOAD_LSB_REG 0x17
+#define Z8536_CT2_RELOAD_MSB_REG 0x18
+#define Z8536_CT2_RELOAD_LSB_REG 0x19
+#define Z8536_CT3_RELOAD_MSB_REG 0x1a
+#define Z8536_CT3_RELOAD_LSB_REG 0x1b
+#define Z8536_CT_RELOAD_MSB_REG(x) (0x16 + ((x) * 2))
+#define Z8536_CT_RELOAD_LSB_REG(x) (0x17 + ((x) * 2))
+
+/* Counter/Timer 1/2/3 Mode Specification registers */
+#define Z8536_CT1_MODE_REG 0x1c
+#define Z8536_CT2_MODE_REG 0x1d
+#define Z8536_CT3_MODE_REG 0x1e
+#define Z8536_CT_MODE_REG(x) (0x1c + (x))
+#define Z8536_CT_MODE_CSC BIT(7) /* Continuous/Single Cycle */
+#define Z8536_CT_MODE_EOE BIT(6) /* External Output Enable */
+#define Z8536_CT_MODE_ECE BIT(5) /* External Count Enable */
+#define Z8536_CT_MODE_ETE BIT(4) /* External Trigger Enable */
+#define Z8536_CT_MODE_EGE BIT(3) /* External Gate Enable */
+#define Z8536_CT_MODE_REB BIT(2) /* Retrigger Enable Bit */
+#define Z8536_CT_MODE_DCS_PULSE (0 << 0)/* Duty Cycle - Pulse */
+#define Z8536_CT_MODE_DCS_ONESHOT (1 << 0)/* Duty Cycle - One-Shot */
+#define Z8536_CT_MODE_DCS_SQRWAVE (2 << 0)/* Duty Cycle - Square Wave */
+#define Z8536_CT_MODE_DCS_DO_NOT_USE (3 << 0)/* Duty Cycle - Do Not Use */
+#define Z8536_CT_MODE_DCS_MASK (3 << 0)/* Duty Cycle mask */
+
+/* Port A/B Mode Specification registers */
+#define Z8536_PA_MODE_REG 0x20
+#define Z8536_PB_MODE_REG 0x28
+#define Z8536_PAB_MODE_PTS_BIT (0 << 6)/* Bit Port */
+#define Z8536_PAB_MODE_PTS_INPUT (1 << 6)/* Input Port */
+#define Z8536_PAB_MODE_PTS_OUTPUT (2 << 6)/* Output Port */
+#define Z8536_PAB_MODE_PTS_BIDIR (3 << 6)/* Bidirectional Port */
+#define Z8536_PAB_MODE_PTS_MASK (3 << 6)/* Port Type Select mask */
+#define Z8536_PAB_MODE_ITB BIT(5) /* Interrupt on Two Bytes */
+#define Z8536_PAB_MODE_SB BIT(4) /* Single Buffered mode */
+#define Z8536_PAB_MODE_IMO BIT(3) /* Interrupt on Match Only */
+#define Z8536_PAB_MODE_PMS_DISABLE (0 << 1)/* Disable Pattern Match */
+#define Z8536_PAB_MODE_PMS_AND (1 << 1)/* "AND" mode */
+#define Z8536_PAB_MODE_PMS_OR (2 << 1)/* "OR" mode */
+#define Z8536_PAB_MODE_PMS_OR_PEV (3 << 1)/* "OR-Priority" mode */
+#define Z8536_PAB_MODE_PMS_MASK (3 << 1)/* Pattern Mode mask */
+#define Z8536_PAB_MODE_LPM BIT(0) /* Latch on Pattern Match */
+#define Z8536_PAB_MODE_DTE BIT(0) /* Deskew Timer Enabled */
+
+/* Port A/B Handshake Specification registers */
+#define Z8536_PA_HANDSHAKE_REG 0x21
+#define Z8536_PB_HANDSHAKE_REG 0x29
+#define Z8536_PAB_HANDSHAKE_HST_INTER (0 << 6)/* Interlocked Handshake */
+#define Z8536_PAB_HANDSHAKE_HST_STROBED (1 << 6)/* Strobed Handshake */
+#define Z8536_PAB_HANDSHAKE_HST_PULSED (2 << 6)/* Pulsed Handshake */
+#define Z8536_PAB_HANDSHAKE_HST_3WIRE (3 << 6)/* Three-Wire Handshake */
+#define Z8536_PAB_HANDSHAKE_HST_MASK (3 << 6)/* Handshake Type mask */
+#define Z8536_PAB_HANDSHAKE_RWS_DISABLE (0 << 3)/* Req/Wait Disabled */
+#define Z8536_PAB_HANDSHAKE_RWS_OUTWAIT (1 << 3)/* Output Wait */
+#define Z8536_PAB_HANDSHAKE_RWS_INWAIT (3 << 3)/* Input Wait */
+#define Z8536_PAB_HANDSHAKE_RWS_SPREQ (4 << 3)/* Special Request */
+#define Z8536_PAB_HANDSHAKE_RWS_OUTREQ (5 << 4)/* Output Request */
+#define Z8536_PAB_HANDSHAKE_RWS_INREQ (7 << 3)/* Input Request */
+#define Z8536_PAB_HANDSHAKE_RWS_MASK (7 << 3)/* Req/Wait mask */
+#define Z8536_PAB_HANDSHAKE_DESKEW(x) ((x) << 0)/* Deskew Time */
+#define Z8536_PAB_HANDSHAKE_DESKEW_MASK (3 << 0)/* Deskew Time mask */
+
+/*
+ * Port A/B/C Data Path Polarity registers
+ *
+ * 0 = Non-Inverting
+ * 1 = Inverting
+ */
+#define Z8536_PA_DPP_REG 0x22
+#define Z8536_PB_DPP_REG 0x2a
+#define Z8536_PC_DPP_REG 0x05
+
+/*
+ * Port A/B/C Data Direction registers
+ *
+ * 0 = Output bit
+ * 1 = Input bit
+ */
+#define Z8536_PA_DD_REG 0x23
+#define Z8536_PB_DD_REG 0x2b
+#define Z8536_PC_DD_REG 0x06
+
+/*
+ * Port A/B/C Special I/O Control registers
+ *
+ * 0 = Normal Input or Output
+ * 1 = Output with open drain or Input with 1's catcher
+ */
+#define Z8536_PA_SIO_REG 0x24
+#define Z8536_PB_SIO_REG 0x2c
+#define Z8536_PC_SIO_REG 0x07
+
+/*
+ * Port A/B Pattern Polarity/Transition/Mask registers
+ *
+ * PM PT PP Pattern Specification
+ * -- -- -- -------------------------------------
+ * 0 0 x Bit masked off
+ * 0 1 x Any transition
+ * 1 0 0 Zero (low-level)
+ * 1 0 1 One (high-level)
+ * 1 1 0 One-to-zero transition (falling-edge)
+ * 1 1 1 Zero-to-one transition (rising-edge)
+ */
+#define Z8536_PA_PP_REG 0x25
+#define Z8536_PB_PP_REG 0x2d
+
+#define Z8536_PA_PT_REG 0x26
+#define Z8536_PB_PT_REG 0x2e
+
+#define Z8536_PA_PM_REG 0x27
+#define Z8536_PB_PM_REG 0x2f
+
+#endif /* _Z8536_H */
diff --git a/drivers/staging/comedi/kcomedilib/Makefile b/drivers/staging/comedi/kcomedilib/Makefile
new file mode 100644
index 000000000..3aff8ed08
--- /dev/null
+++ b/drivers/staging/comedi/kcomedilib/Makefile
@@ -0,0 +1,5 @@
+ccflags-$(CONFIG_COMEDI_DEBUG) := -DDEBUG
+
+obj-$(CONFIG_COMEDI_KCOMEDILIB) += kcomedilib.o
+
+kcomedilib-objs := kcomedilib_main.o
diff --git a/drivers/staging/comedi/kcomedilib/kcomedilib_main.c b/drivers/staging/comedi/kcomedilib/kcomedilib_main.c
new file mode 100644
index 000000000..76bf5619f
--- /dev/null
+++ b/drivers/staging/comedi/kcomedilib/kcomedilib_main.c
@@ -0,0 +1,252 @@
+/*
+ kcomedilib/kcomedilib.c
+ a comedlib interface for kernel modules
+
+ COMEDI - Linux Control and Measurement Device Interface
+ Copyright (C) 1997-2000 David A. Schleef <ds@schleef.org>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+*/
+
+#include <linux/module.h>
+
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/fcntl.h>
+#include <linux/mm.h>
+#include <linux/io.h>
+
+#include "../comedi.h"
+#include "../comedilib.h"
+#include "../comedidev.h"
+
+MODULE_AUTHOR("David Schleef <ds@schleef.org>");
+MODULE_DESCRIPTION("Comedi kernel library");
+MODULE_LICENSE("GPL");
+
+struct comedi_device *comedi_open(const char *filename)
+{
+ struct comedi_device *dev, *retval = NULL;
+ unsigned int minor;
+
+ if (strncmp(filename, "/dev/comedi", 11) != 0)
+ return NULL;
+
+ if (kstrtouint(filename + 11, 0, &minor))
+ return NULL;
+
+ if (minor >= COMEDI_NUM_BOARD_MINORS)
+ return NULL;
+
+ dev = comedi_dev_get_from_minor(minor);
+ if (!dev)
+ return NULL;
+
+ down_read(&dev->attach_lock);
+ if (dev->attached)
+ retval = dev;
+ else
+ retval = NULL;
+ up_read(&dev->attach_lock);
+
+ if (!retval)
+ comedi_dev_put(dev);
+
+ return retval;
+}
+EXPORT_SYMBOL_GPL(comedi_open);
+
+int comedi_close(struct comedi_device *dev)
+{
+ comedi_dev_put(dev);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(comedi_close);
+
+static int comedi_do_insn(struct comedi_device *dev,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ struct comedi_subdevice *s;
+ int ret;
+
+ mutex_lock(&dev->mutex);
+
+ if (!dev->attached) {
+ ret = -EINVAL;
+ goto error;
+ }
+
+ /* a subdevice instruction */
+ if (insn->subdev >= dev->n_subdevices) {
+ ret = -EINVAL;
+ goto error;
+ }
+ s = &dev->subdevices[insn->subdev];
+
+ if (s->type == COMEDI_SUBD_UNUSED) {
+ dev_err(dev->class_dev,
+ "%d not usable subdevice\n", insn->subdev);
+ ret = -EIO;
+ goto error;
+ }
+
+ /* XXX check lock */
+
+ ret = comedi_check_chanlist(s, 1, &insn->chanspec);
+ if (ret < 0) {
+ dev_err(dev->class_dev, "bad chanspec\n");
+ ret = -EINVAL;
+ goto error;
+ }
+
+ if (s->busy) {
+ ret = -EBUSY;
+ goto error;
+ }
+ s->busy = dev;
+
+ switch (insn->insn) {
+ case INSN_BITS:
+ ret = s->insn_bits(dev, s, insn, data);
+ break;
+ case INSN_CONFIG:
+ /* XXX should check instruction length */
+ ret = s->insn_config(dev, s, insn, data);
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ s->busy = NULL;
+error:
+
+ mutex_unlock(&dev->mutex);
+ return ret;
+}
+
+int comedi_dio_get_config(struct comedi_device *dev, unsigned int subdev,
+ unsigned int chan, unsigned int *io)
+{
+ struct comedi_insn insn;
+ unsigned int data[2];
+ int ret;
+
+ memset(&insn, 0, sizeof(insn));
+ insn.insn = INSN_CONFIG;
+ insn.n = 2;
+ insn.subdev = subdev;
+ insn.chanspec = CR_PACK(chan, 0, 0);
+ data[0] = INSN_CONFIG_DIO_QUERY;
+ data[1] = 0;
+ ret = comedi_do_insn(dev, &insn, data);
+ if (ret >= 0)
+ *io = data[1];
+ return ret;
+}
+EXPORT_SYMBOL_GPL(comedi_dio_get_config);
+
+int comedi_dio_config(struct comedi_device *dev, unsigned int subdev,
+ unsigned int chan, unsigned int io)
+{
+ struct comedi_insn insn;
+
+ memset(&insn, 0, sizeof(insn));
+ insn.insn = INSN_CONFIG;
+ insn.n = 1;
+ insn.subdev = subdev;
+ insn.chanspec = CR_PACK(chan, 0, 0);
+
+ return comedi_do_insn(dev, &insn, &io);
+}
+EXPORT_SYMBOL_GPL(comedi_dio_config);
+
+int comedi_dio_bitfield2(struct comedi_device *dev, unsigned int subdev,
+ unsigned int mask, unsigned int *bits,
+ unsigned int base_channel)
+{
+ struct comedi_insn insn;
+ unsigned int data[2];
+ unsigned int n_chan;
+ unsigned int shift;
+ int ret;
+
+ base_channel = CR_CHAN(base_channel);
+ n_chan = comedi_get_n_channels(dev, subdev);
+ if (base_channel >= n_chan)
+ return -EINVAL;
+
+ memset(&insn, 0, sizeof(insn));
+ insn.insn = INSN_BITS;
+ insn.chanspec = base_channel;
+ insn.n = 2;
+ insn.subdev = subdev;
+
+ data[0] = mask;
+ data[1] = *bits;
+
+ /*
+ * Most drivers ignore the base channel in insn->chanspec.
+ * Fix this here if the subdevice has <= 32 channels.
+ */
+ if (n_chan <= 32) {
+ shift = base_channel;
+ if (shift) {
+ insn.chanspec = 0;
+ data[0] <<= shift;
+ data[1] <<= shift;
+ }
+ } else {
+ shift = 0;
+ }
+
+ ret = comedi_do_insn(dev, &insn, data);
+ *bits = data[1] >> shift;
+ return ret;
+}
+EXPORT_SYMBOL_GPL(comedi_dio_bitfield2);
+
+int comedi_find_subdevice_by_type(struct comedi_device *dev, int type,
+ unsigned int subd)
+{
+ struct comedi_subdevice *s;
+ int ret = -ENODEV;
+
+ down_read(&dev->attach_lock);
+ if (dev->attached)
+ for (; subd < dev->n_subdevices; subd++) {
+ s = &dev->subdevices[subd];
+ if (s->type == type) {
+ ret = subd;
+ break;
+ }
+ }
+ up_read(&dev->attach_lock);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(comedi_find_subdevice_by_type);
+
+int comedi_get_n_channels(struct comedi_device *dev, unsigned int subdevice)
+{
+ int n;
+
+ down_read(&dev->attach_lock);
+ if (!dev->attached || subdevice >= dev->n_subdevices)
+ n = 0;
+ else
+ n = dev->subdevices[subdevice].n_chan;
+ up_read(&dev->attach_lock);
+
+ return n;
+}
+EXPORT_SYMBOL_GPL(comedi_get_n_channels);
diff --git a/drivers/staging/comedi/proc.c b/drivers/staging/comedi/proc.c
new file mode 100644
index 000000000..91dea25b5
--- /dev/null
+++ b/drivers/staging/comedi/proc.c
@@ -0,0 +1,97 @@
+/*
+ * /proc interface for comedi
+ *
+ * COMEDI - Linux Control and Measurement Device Interface
+ * Copyright (C) 1998 David A. Schleef <ds@schleef.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+/*
+ * This is some serious bloatware.
+ *
+ * Taken from Dave A.'s PCL-711 driver, 'cuz I thought it
+ * was cool.
+ */
+
+#include "comedidev.h"
+#include "comedi_internal.h"
+#include <linux/proc_fs.h>
+#include <linux/seq_file.h>
+
+static int comedi_read(struct seq_file *m, void *v)
+{
+ int i;
+ int devices_q = 0;
+ struct comedi_driver *driv;
+
+ seq_printf(m, "comedi version " COMEDI_RELEASE "\nformat string: %s\n",
+ "\"%2d: %-20s %-20s %4d\", i, driver_name, board_name, n_subdevices");
+
+ for (i = 0; i < COMEDI_NUM_BOARD_MINORS; i++) {
+ struct comedi_device *dev = comedi_dev_get_from_minor(i);
+
+ if (!dev)
+ continue;
+
+ down_read(&dev->attach_lock);
+ if (dev->attached) {
+ devices_q = 1;
+ seq_printf(m, "%2d: %-20s %-20s %4d\n",
+ i, dev->driver->driver_name,
+ dev->board_name, dev->n_subdevices);
+ }
+ up_read(&dev->attach_lock);
+ comedi_dev_put(dev);
+ }
+ if (!devices_q)
+ seq_puts(m, "no devices\n");
+
+ mutex_lock(&comedi_drivers_list_lock);
+ for (driv = comedi_drivers; driv; driv = driv->next) {
+ seq_printf(m, "%s:\n", driv->driver_name);
+ for (i = 0; i < driv->num_names; i++)
+ seq_printf(m, " %s\n",
+ *(char **)((char *)driv->board_name +
+ i * driv->offset));
+
+ if (!driv->num_names)
+ seq_printf(m, " %s\n", driv->driver_name);
+ }
+ mutex_unlock(&comedi_drivers_list_lock);
+
+ return 0;
+}
+
+/*
+ * seq_file wrappers for procfile show routines.
+ */
+static int comedi_proc_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, comedi_read, NULL);
+}
+
+static const struct file_operations comedi_proc_fops = {
+ .open = comedi_proc_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+void comedi_proc_init(void)
+{
+ proc_create("comedi", 0644, NULL, &comedi_proc_fops);
+}
+
+void comedi_proc_cleanup(void)
+{
+ remove_proc_entry("comedi", NULL);
+}
diff --git a/drivers/staging/comedi/range.c b/drivers/staging/comedi/range.c
new file mode 100644
index 000000000..6a393b24b
--- /dev/null
+++ b/drivers/staging/comedi/range.c
@@ -0,0 +1,132 @@
+/*
+ * comedi/range.c
+ * comedi routines for voltage ranges
+ *
+ * COMEDI - Linux Control and Measurement Device Interface
+ * Copyright (C) 1997-8 David A. Schleef <ds@schleef.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/uaccess.h>
+#include "comedidev.h"
+#include "comedi_internal.h"
+
+const struct comedi_lrange range_bipolar10 = { 1, {BIP_RANGE(10)} };
+EXPORT_SYMBOL_GPL(range_bipolar10);
+const struct comedi_lrange range_bipolar5 = { 1, {BIP_RANGE(5)} };
+EXPORT_SYMBOL_GPL(range_bipolar5);
+const struct comedi_lrange range_bipolar2_5 = { 1, {BIP_RANGE(2.5)} };
+EXPORT_SYMBOL_GPL(range_bipolar2_5);
+const struct comedi_lrange range_unipolar10 = { 1, {UNI_RANGE(10)} };
+EXPORT_SYMBOL_GPL(range_unipolar10);
+const struct comedi_lrange range_unipolar5 = { 1, {UNI_RANGE(5)} };
+EXPORT_SYMBOL_GPL(range_unipolar5);
+const struct comedi_lrange range_unipolar2_5 = { 1, {UNI_RANGE(2.5)} };
+EXPORT_SYMBOL_GPL(range_unipolar2_5);
+const struct comedi_lrange range_0_20mA = { 1, {RANGE_mA(0, 20)} };
+EXPORT_SYMBOL_GPL(range_0_20mA);
+const struct comedi_lrange range_4_20mA = { 1, {RANGE_mA(4, 20)} };
+EXPORT_SYMBOL_GPL(range_4_20mA);
+const struct comedi_lrange range_0_32mA = { 1, {RANGE_mA(0, 32)} };
+EXPORT_SYMBOL_GPL(range_0_32mA);
+const struct comedi_lrange range_unknown = { 1, {{0, 1000000, UNIT_none} } };
+EXPORT_SYMBOL_GPL(range_unknown);
+
+/*
+ * COMEDI_RANGEINFO ioctl
+ * range information
+ *
+ * arg:
+ * pointer to comedi_rangeinfo structure
+ *
+ * reads:
+ * comedi_rangeinfo structure
+ *
+ * writes:
+ * array of comedi_krange structures to rangeinfo->range_ptr pointer
+ */
+int do_rangeinfo_ioctl(struct comedi_device *dev,
+ struct comedi_rangeinfo __user *arg)
+{
+ struct comedi_rangeinfo it;
+ int subd, chan;
+ const struct comedi_lrange *lr;
+ struct comedi_subdevice *s;
+
+ if (copy_from_user(&it, arg, sizeof(struct comedi_rangeinfo)))
+ return -EFAULT;
+ subd = (it.range_type >> 24) & 0xf;
+ chan = (it.range_type >> 16) & 0xff;
+
+ if (!dev->attached)
+ return -EINVAL;
+ if (subd >= dev->n_subdevices)
+ return -EINVAL;
+ s = &dev->subdevices[subd];
+ if (s->range_table) {
+ lr = s->range_table;
+ } else if (s->range_table_list) {
+ if (chan >= s->n_chan)
+ return -EINVAL;
+ lr = s->range_table_list[chan];
+ } else {
+ return -EINVAL;
+ }
+
+ if (RANGE_LENGTH(it.range_type) != lr->length) {
+ dev_dbg(dev->class_dev,
+ "wrong length %d should be %d (0x%08x)\n",
+ RANGE_LENGTH(it.range_type),
+ lr->length, it.range_type);
+ return -EINVAL;
+ }
+
+ if (copy_to_user(it.range_ptr, lr->range,
+ sizeof(struct comedi_krange) * lr->length))
+ return -EFAULT;
+
+ return 0;
+}
+
+/**
+ * comedi_check_chanlist() - Validate each element in a chanlist.
+ * @s: comedi_subdevice struct
+ * @n: number of elements in the chanlist
+ * @chanlist: the chanlist to validate
+*/
+int comedi_check_chanlist(struct comedi_subdevice *s, int n,
+ unsigned int *chanlist)
+{
+ struct comedi_device *dev = s->device;
+ unsigned int chanspec;
+ int chan, range_len, i;
+
+ for (i = 0; i < n; i++) {
+ chanspec = chanlist[i];
+ chan = CR_CHAN(chanspec);
+ if (s->range_table)
+ range_len = s->range_table->length;
+ else if (s->range_table_list && chan < s->n_chan)
+ range_len = s->range_table_list[chan]->length;
+ else
+ range_len = 0;
+ if (chan >= s->n_chan ||
+ CR_RANGE(chanspec) >= range_len) {
+ dev_warn(dev->class_dev,
+ "bad chanlist[%d]=0x%08x chan=%d range length=%d\n",
+ i, chanspec, chan, range_len);
+ return -EINVAL;
+ }
+ }
+ return 0;
+}
+EXPORT_SYMBOL_GPL(comedi_check_chanlist);
diff --git a/drivers/staging/dgap/Kconfig b/drivers/staging/dgap/Kconfig
new file mode 100644
index 000000000..3bbe9e122
--- /dev/null
+++ b/drivers/staging/dgap/Kconfig
@@ -0,0 +1,6 @@
+config DGAP
+ tristate "Digi EPCA PCI products"
+ default n
+ depends on TTY && HAS_IOMEM
+ ---help---
+ Driver for the Digi International EPCA PCI based product line
diff --git a/drivers/staging/dgap/Makefile b/drivers/staging/dgap/Makefile
new file mode 100644
index 000000000..0063d044c
--- /dev/null
+++ b/drivers/staging/dgap/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_DGAP) += dgap.o
diff --git a/drivers/staging/dgap/dgap.c b/drivers/staging/dgap/dgap.c
new file mode 100644
index 000000000..3dc011655
--- /dev/null
+++ b/drivers/staging/dgap/dgap.c
@@ -0,0 +1,7187 @@
+/*
+ * Copyright 2003 Digi International (www.digi.com)
+ * Scott H Kilau <Scott_Kilau at digi dot 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, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED; without even the
+ * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ * PURPOSE. See the GNU General Public License for more details.
+ *
+ */
+
+/*
+ * In the original out of kernel Digi dgap driver, firmware
+ * loading was done via user land to driver handshaking.
+ *
+ * For cards that support a concentrator (port expander),
+ * I believe the concentrator its self told the card which
+ * concentrator is actually attached and then that info
+ * was used to tell user land which concentrator firmware
+ * image was to be downloaded. I think even the BIOS or
+ * FEP images required could change with the connection
+ * of a particular concentrator.
+ *
+ * Since I have no access to any of these cards or
+ * concentrators, I cannot put the correct concentrator
+ * firmware file names into the firmware_info structure
+ * as is now done for the BIOS and FEP images.
+ *
+ * I think, but am not certain, that the cards supporting
+ * concentrators will function without them. So support
+ * of these cards has been left in this driver.
+ *
+ * In order to fully support those cards, they would
+ * either have to be acquired for dissection or maybe
+ * Digi International could provide some assistance.
+ */
+#undef DIGI_CONCENTRATORS_SUPPORTED
+
+#define pr_fmt(fmt) "dgap: " fmt
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/delay.h> /* For udelay */
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+#include <linux/sched.h>
+
+#include <linux/interrupt.h> /* For tasklet and interrupt structs/defines */
+#include <linux/ctype.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/serial_reg.h>
+#include <linux/io.h> /* For read[bwl]/write[bwl] */
+
+#include <linux/string.h>
+#include <linux/device.h>
+#include <linux/kdev_t.h>
+#include <linux/firmware.h>
+
+#include "dgap.h"
+
+/*
+ * File operations permitted on Control/Management major.
+ */
+static const struct file_operations dgap_board_fops = {
+ .owner = THIS_MODULE,
+};
+
+static uint dgap_numboards;
+static struct board_t *dgap_board[MAXBOARDS];
+static ulong dgap_poll_counter;
+static int dgap_driver_state = DRIVER_INITIALIZED;
+static int dgap_poll_tick = 20; /* Poll interval - 20 ms */
+
+static struct class *dgap_class;
+
+static uint dgap_count = 500;
+
+/*
+ * Poller stuff
+ */
+static DEFINE_SPINLOCK(dgap_poll_lock); /* Poll scheduling lock */
+static ulong dgap_poll_time; /* Time of next poll */
+static uint dgap_poll_stop; /* Used to tell poller to stop */
+static struct timer_list dgap_poll_timer;
+
+/*
+ SUPPORTED PRODUCTS
+
+ Card Model Number of Ports Interface
+ ----------------------------------------------------------------
+ Acceleport Xem 4 - 64 (EIA232 & EIA422)
+ Acceleport Xr 4 & 8 (EIA232)
+ Acceleport Xr 920 4 & 8 (EIA232)
+ Acceleport C/X 8 - 128 (EIA232)
+ Acceleport EPC/X 8 - 224 (EIA232)
+ Acceleport Xr/422 4 & 8 (EIA422)
+ Acceleport 2r/920 2 (EIA232)
+ Acceleport 4r/920 4 (EIA232)
+ Acceleport 8r/920 8 (EIA232)
+
+ IBM 8-Port Asynchronous PCI Adapter (EIA232)
+ IBM 128-Port Asynchronous PCI Adapter (EIA232 & EIA422)
+*/
+
+static struct pci_device_id dgap_pci_tbl[] = {
+ { DIGI_VID, PCI_DEV_XEM_DID, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
+ { DIGI_VID, PCI_DEV_CX_DID, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 1 },
+ { DIGI_VID, PCI_DEV_CX_IBM_DID, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 2 },
+ { DIGI_VID, PCI_DEV_EPCJ_DID, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 3 },
+ { DIGI_VID, PCI_DEV_920_2_DID, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 4 },
+ { DIGI_VID, PCI_DEV_920_4_DID, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 5 },
+ { DIGI_VID, PCI_DEV_920_8_DID, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 6 },
+ { DIGI_VID, PCI_DEV_XR_DID, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 7 },
+ { DIGI_VID, PCI_DEV_XRJ_DID, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 8 },
+ { DIGI_VID, PCI_DEV_XR_422_DID, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 9 },
+ { DIGI_VID, PCI_DEV_XR_IBM_DID, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 10 },
+ { DIGI_VID, PCI_DEV_XR_SAIP_DID, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 11 },
+ { DIGI_VID, PCI_DEV_XR_BULL_DID, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 12 },
+ { DIGI_VID, PCI_DEV_920_8_HP_DID, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 13 },
+ { DIGI_VID, PCI_DEV_XEM_HP_DID, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 14 },
+ {0,} /* 0 terminated list. */
+};
+MODULE_DEVICE_TABLE(pci, dgap_pci_tbl);
+
+/*
+ * A generic list of Product names, PCI Vendor ID, and PCI Device ID.
+ */
+struct board_id {
+ uint config_type;
+ u8 *name;
+ uint maxports;
+ uint dpatype;
+};
+
+static struct board_id dgap_ids[] = {
+ { PPCM, PCI_DEV_XEM_NAME, 64, (T_PCXM|T_PCLITE|T_PCIBUS) },
+ { PCX, PCI_DEV_CX_NAME, 128, (T_CX|T_PCIBUS) },
+ { PCX, PCI_DEV_CX_IBM_NAME, 128, (T_CX|T_PCIBUS) },
+ { PEPC, PCI_DEV_EPCJ_NAME, 224, (T_EPC|T_PCIBUS) },
+ { APORT2_920P, PCI_DEV_920_2_NAME, 2, (T_PCXR|T_PCLITE|T_PCIBUS) },
+ { APORT4_920P, PCI_DEV_920_4_NAME, 4, (T_PCXR|T_PCLITE|T_PCIBUS) },
+ { APORT8_920P, PCI_DEV_920_8_NAME, 8, (T_PCXR|T_PCLITE|T_PCIBUS) },
+ { PAPORT8, PCI_DEV_XR_NAME, 8, (T_PCXR|T_PCLITE|T_PCIBUS) },
+ { PAPORT8, PCI_DEV_XRJ_NAME, 8, (T_PCXR|T_PCLITE|T_PCIBUS) },
+ { PAPORT8, PCI_DEV_XR_422_NAME, 8, (T_PCXR|T_PCLITE|T_PCIBUS) },
+ { PAPORT8, PCI_DEV_XR_IBM_NAME, 8, (T_PCXR|T_PCLITE|T_PCIBUS) },
+ { PAPORT8, PCI_DEV_XR_SAIP_NAME, 8, (T_PCXR|T_PCLITE|T_PCIBUS) },
+ { PAPORT8, PCI_DEV_XR_BULL_NAME, 8, (T_PCXR|T_PCLITE|T_PCIBUS) },
+ { APORT8_920P, PCI_DEV_920_8_HP_NAME, 8, (T_PCXR|T_PCLITE|T_PCIBUS) },
+ { PPCM, PCI_DEV_XEM_HP_NAME, 64, (T_PCXM|T_PCLITE|T_PCIBUS) },
+ {0,} /* 0 terminated list. */
+};
+
+struct firmware_info {
+ u8 *conf_name; /* dgap.conf */
+ u8 *bios_name; /* BIOS filename */
+ u8 *fep_name; /* FEP filename */
+ u8 *con_name; /* Concentrator filename FIXME*/
+ int num; /* sequence number */
+};
+
+/*
+ * Firmware - BIOS, FEP, and CONC filenames
+ */
+static struct firmware_info fw_info[] = {
+ { "dgap/dgap.conf", "/*(DEBLOBBED)*/", "/*(DEBLOBBED)*/", NULL, 0 },
+ { "dgap/dgap.conf", "/*(DEBLOBBED)*/", "/*(DEBLOBBED)*/", NULL, 1 },
+ { "dgap/dgap.conf", "/*(DEBLOBBED)*/", "/*(DEBLOBBED)*/", NULL, 2 },
+ { "dgap/dgap.conf", "/*(DEBLOBBED)*/", "/*(DEBLOBBED)*/", NULL, 3 },
+ { "dgap/dgap.conf", "/*(DEBLOBBED)*/", "/*(DEBLOBBED)*/", NULL, 4 },
+ { "dgap/dgap.conf", "/*(DEBLOBBED)*/", "/*(DEBLOBBED)*/", NULL, 5 },
+ { "dgap/dgap.conf", "/*(DEBLOBBED)*/", "/*(DEBLOBBED)*/", NULL, 6 },
+ { "dgap/dgap.conf", "/*(DEBLOBBED)*/", "/*(DEBLOBBED)*/", NULL, 7 },
+ { "dgap/dgap.conf", "/*(DEBLOBBED)*/", "/*(DEBLOBBED)*/", NULL, 8 },
+ { "dgap/dgap.conf", "/*(DEBLOBBED)*/", "/*(DEBLOBBED)*/", NULL, 9 },
+ { "dgap/dgap.conf", "/*(DEBLOBBED)*/", "/*(DEBLOBBED)*/", NULL, 10 },
+ { "dgap/dgap.conf", "/*(DEBLOBBED)*/", "/*(DEBLOBBED)*/", NULL, 11 },
+ { "dgap/dgap.conf", "/*(DEBLOBBED)*/", "/*(DEBLOBBED)*/", NULL, 12 },
+ { "dgap/dgap.conf", "/*(DEBLOBBED)*/", "/*(DEBLOBBED)*/", NULL, 13 },
+ { "dgap/dgap.conf", "/*(DEBLOBBED)*/", "/*(DEBLOBBED)*/", NULL, 14 },
+ {NULL,}
+};
+
+/*
+ * Default transparent print information.
+ */
+static struct digi_t dgap_digi_init = {
+ .digi_flags = DIGI_COOK, /* Flags */
+ .digi_maxcps = 100, /* Max CPS */
+ .digi_maxchar = 50, /* Max chars in print queue */
+ .digi_bufsize = 100, /* Printer buffer size */
+ .digi_onlen = 4, /* size of printer on string */
+ .digi_offlen = 4, /* size of printer off string */
+ .digi_onstr = "\033[5i", /* ANSI printer on string ] */
+ .digi_offstr = "\033[4i", /* ANSI printer off string ] */
+ .digi_term = "ansi" /* default terminal type */
+};
+
+/*
+ * Define a local default termios struct. All ports will be created
+ * with this termios initially.
+ *
+ * This defines a raw port at 9600 baud, 8 data bits, no parity,
+ * 1 stop bit.
+ */
+
+static struct ktermios dgap_default_termios = {
+ .c_iflag = (DEFAULT_IFLAGS), /* iflags */
+ .c_oflag = (DEFAULT_OFLAGS), /* oflags */
+ .c_cflag = (DEFAULT_CFLAGS), /* cflags */
+ .c_lflag = (DEFAULT_LFLAGS), /* lflags */
+ .c_cc = INIT_C_CC,
+ .c_line = 0,
+};
+
+/*
+ * Our needed internal static variables from dgap_parse.c
+ */
+static struct cnode dgap_head;
+#define MAXCWORD 200
+static char dgap_cword[MAXCWORD];
+
+struct toklist {
+ int token;
+ char *string;
+};
+
+static struct toklist dgap_brdtype[] = {
+ { PCX, "Digi_AccelePort_C/X_PCI" },
+ { PEPC, "Digi_AccelePort_EPC/X_PCI" },
+ { PPCM, "Digi_AccelePort_Xem_PCI" },
+ { APORT2_920P, "Digi_AccelePort_2r_920_PCI" },
+ { APORT4_920P, "Digi_AccelePort_4r_920_PCI" },
+ { APORT8_920P, "Digi_AccelePort_8r_920_PCI" },
+ { PAPORT4, "Digi_AccelePort_4r_PCI(EIA-232/RS-422)" },
+ { PAPORT8, "Digi_AccelePort_8r_PCI(EIA-232/RS-422)" },
+ { 0, NULL }
+};
+
+static struct toklist dgap_tlist[] = {
+ { BEGIN, "config_begin" },
+ { END, "config_end" },
+ { BOARD, "board" },
+ { IO, "io" },
+ { PCIINFO, "pciinfo" },
+ { LINE, "line" },
+ { CONC, "conc" },
+ { CONC, "concentrator" },
+ { CX, "cx" },
+ { CX, "ccon" },
+ { EPC, "epccon" },
+ { EPC, "epc" },
+ { MOD, "module" },
+ { ID, "id" },
+ { STARTO, "start" },
+ { SPEED, "speed" },
+ { CABLE, "cable" },
+ { CONNECT, "connect" },
+ { METHOD, "method" },
+ { STATUS, "status" },
+ { CUSTOM, "Custom" },
+ { BASIC, "Basic" },
+ { MEM, "mem" },
+ { MEM, "memory" },
+ { PORTS, "ports" },
+ { MODEM, "modem" },
+ { NPORTS, "nports" },
+ { TTYN, "ttyname" },
+ { CU, "cuname" },
+ { PRINT, "prname" },
+ { CMAJOR, "major" },
+ { ALTPIN, "altpin" },
+ { USEINTR, "useintr" },
+ { TTSIZ, "ttysize" },
+ { CHSIZ, "chsize" },
+ { BSSIZ, "boardsize" },
+ { UNTSIZ, "schedsize" },
+ { F2SIZ, "f2200size" },
+ { VPSIZ, "vpixsize" },
+ { 0, NULL }
+};
+
+
+/*
+ * dgap_sindex: much like index(), but it looks for a match of any character in
+ * the group, and returns that position. If the first character is a ^, then
+ * this will match the first occurrence not in that group.
+ */
+static char *dgap_sindex(char *string, char *group)
+{
+ char *ptr;
+
+ if (!string || !group)
+ return NULL;
+
+ if (*group == '^') {
+ group++;
+ for (; *string; string++) {
+ for (ptr = group; *ptr; ptr++) {
+ if (*ptr == *string)
+ break;
+ }
+ if (*ptr == '\0')
+ return string;
+ }
+ } else {
+ for (; *string; string++) {
+ for (ptr = group; *ptr; ptr++) {
+ if (*ptr == *string)
+ return string;
+ }
+ }
+ }
+
+ return NULL;
+}
+
+/*
+ * get a word from the input stream, also keep track of current line number.
+ * words are separated by whitespace.
+ */
+static char *dgap_getword(char **in)
+{
+ char *ret_ptr = *in;
+
+ char *ptr = dgap_sindex(*in, " \t\n");
+
+ /* If no word found, return null */
+ if (!ptr)
+ return NULL;
+
+ /* Mark new location for our buffer */
+ *ptr = '\0';
+ *in = ptr + 1;
+
+ /* Eat any extra spaces/tabs/newlines that might be present */
+ while (*in && **in && ((**in == ' ') ||
+ (**in == '\t') ||
+ (**in == '\n'))) {
+ **in = '\0';
+ *in = *in + 1;
+ }
+
+ return ret_ptr;
+}
+
+
+/*
+ * Get a token from the input file; return 0 if end of file is reached
+ */
+static int dgap_gettok(char **in)
+{
+ char *w;
+ struct toklist *t;
+
+ if (strstr(dgap_cword, "board")) {
+ w = dgap_getword(in);
+ snprintf(dgap_cword, MAXCWORD, "%s", w);
+ for (t = dgap_brdtype; t->token != 0; t++) {
+ if (!strcmp(w, t->string))
+ return t->token;
+ }
+ } else {
+ while ((w = dgap_getword(in))) {
+ snprintf(dgap_cword, MAXCWORD, "%s", w);
+ for (t = dgap_tlist; t->token != 0; t++) {
+ if (!strcmp(w, t->string))
+ return t->token;
+ }
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * dgap_checknode: see if all the necessary info has been supplied for a node
+ * before creating the next node.
+ */
+static int dgap_checknode(struct cnode *p)
+{
+ switch (p->type) {
+ case LNODE:
+ if (p->u.line.v_speed == 0) {
+ pr_err("line speed not specified");
+ return 1;
+ }
+ return 0;
+
+ case CNODE:
+ if (p->u.conc.v_speed == 0) {
+ pr_err("concentrator line speed not specified");
+ return 1;
+ }
+ if (p->u.conc.v_nport == 0) {
+ pr_err("number of ports on concentrator not specified");
+ return 1;
+ }
+ if (p->u.conc.v_id == 0) {
+ pr_err("concentrator id letter not specified");
+ return 1;
+ }
+ return 0;
+
+ case MNODE:
+ if (p->u.module.v_nport == 0) {
+ pr_err("number of ports on EBI module not specified");
+ return 1;
+ }
+ if (p->u.module.v_id == 0) {
+ pr_err("EBI module id letter not specified");
+ return 1;
+ }
+ return 0;
+ }
+ return 0;
+}
+
+/*
+ * Given a board pointer, returns whether we should use interrupts or not.
+ */
+static uint dgap_config_get_useintr(struct board_t *bd)
+{
+ struct cnode *p;
+
+ if (!bd)
+ return 0;
+
+ for (p = bd->bd_config; p; p = p->next) {
+ if (p->type == INTRNODE) {
+ /*
+ * check for pcxr types.
+ */
+ return p->u.useintr;
+ }
+ }
+
+ /* If not found, then don't turn on interrupts. */
+ return 0;
+}
+
+/*
+ * Given a board pointer, returns whether we turn on altpin or not.
+ */
+static uint dgap_config_get_altpin(struct board_t *bd)
+{
+ struct cnode *p;
+
+ if (!bd)
+ return 0;
+
+ for (p = bd->bd_config; p; p = p->next) {
+ if (p->type == ANODE) {
+ /*
+ * check for pcxr types.
+ */
+ return p->u.altpin;
+ }
+ }
+
+ /* If not found, then don't turn on interrupts. */
+ return 0;
+}
+
+/*
+ * Given a specific type of board, if found, detached link and
+ * returns the first occurrence in the list.
+ */
+static struct cnode *dgap_find_config(int type, int bus, int slot)
+{
+ struct cnode *p, *prev, *prev2, *found;
+
+ p = &dgap_head;
+
+ while (p->next) {
+ prev = p;
+ p = p->next;
+
+ if (p->type != BNODE)
+ continue;
+
+ if (p->u.board.type != type)
+ continue;
+
+ if (p->u.board.v_pcibus &&
+ p->u.board.pcibus != bus)
+ continue;
+
+ if (p->u.board.v_pcislot &&
+ p->u.board.pcislot != slot)
+ continue;
+
+ found = p;
+ /*
+ * Keep walking thru the list till we
+ * find the next board.
+ */
+ while (p->next) {
+ prev2 = p;
+ p = p->next;
+
+ if (p->type != BNODE)
+ continue;
+
+ /*
+ * Mark the end of our 1 board
+ * chain of configs.
+ */
+ prev2->next = NULL;
+
+ /*
+ * Link the "next" board to the
+ * previous board, effectively
+ * "unlinking" our board from
+ * the main config.
+ */
+ prev->next = p;
+
+ return found;
+ }
+ /*
+ * It must be the last board in the list.
+ */
+ prev->next = NULL;
+ return found;
+ }
+ return NULL;
+}
+
+/*
+ * Given a board pointer, walks the config link, counting up
+ * all ports user specified should be on the board.
+ * (This does NOT mean they are all actually present right now tho)
+ */
+static uint dgap_config_get_num_prts(struct board_t *bd)
+{
+ int count = 0;
+ struct cnode *p;
+
+ if (!bd)
+ return 0;
+
+ for (p = bd->bd_config; p; p = p->next) {
+
+ switch (p->type) {
+ case BNODE:
+ /*
+ * check for pcxr types.
+ */
+ if (p->u.board.type > EPCFE)
+ count += p->u.board.nport;
+ break;
+ case CNODE:
+ count += p->u.conc.nport;
+ break;
+ case MNODE:
+ count += p->u.module.nport;
+ break;
+ }
+ }
+ return count;
+}
+
+static char *dgap_create_config_string(struct board_t *bd, char *string)
+{
+ char *ptr = string;
+ struct cnode *p;
+ struct cnode *q;
+ int speed;
+
+ if (!bd) {
+ *ptr = 0xff;
+ return string;
+ }
+
+ for (p = bd->bd_config; p; p = p->next) {
+
+ switch (p->type) {
+ case LNODE:
+ *ptr = '\0';
+ ptr++;
+ *ptr = p->u.line.speed;
+ ptr++;
+ break;
+ case CNODE:
+ /*
+ * Because the EPC/con concentrators can have EM modules
+ * hanging off of them, we have to walk ahead in the
+ * list and keep adding the number of ports on each EM
+ * to the config. UGH!
+ */
+ speed = p->u.conc.speed;
+ q = p->next;
+ if (q && (q->type == MNODE)) {
+ *ptr = (p->u.conc.nport + 0x80);
+ ptr++;
+ p = q;
+ while (q->next && (q->next->type) == MNODE) {
+ *ptr = (q->u.module.nport + 0x80);
+ ptr++;
+ p = q;
+ q = q->next;
+ }
+ *ptr = q->u.module.nport;
+ ptr++;
+ } else {
+ *ptr = p->u.conc.nport;
+ ptr++;
+ }
+
+ *ptr = speed;
+ ptr++;
+ break;
+ }
+ }
+
+ *ptr = 0xff;
+ return string;
+}
+
+/*
+ * Parse a configuration file read into memory as a string.
+ */
+static int dgap_parsefile(char **in)
+{
+ struct cnode *p, *brd, *line, *conc;
+ int rc;
+ char *s;
+ int linecnt = 0;
+
+ p = &dgap_head;
+ brd = line = conc = NULL;
+
+ /* perhaps we are adding to an existing list? */
+ while (p->next)
+ p = p->next;
+
+ /* file must start with a BEGIN */
+ while ((rc = dgap_gettok(in)) != BEGIN) {
+ if (rc == 0) {
+ pr_err("unexpected EOF");
+ return -1;
+ }
+ }
+
+ for (; ;) {
+ int board_type = 0;
+ int conc_type = 0;
+ int module_type = 0;
+
+ rc = dgap_gettok(in);
+ if (rc == 0) {
+ pr_err("unexpected EOF");
+ return -1;
+ }
+
+ switch (rc) {
+ case BEGIN: /* should only be 1 begin */
+ pr_err("unexpected config_begin\n");
+ return -1;
+
+ case END:
+ return 0;
+
+ case BOARD: /* board info */
+ if (dgap_checknode(p))
+ return -1;
+
+ p->next = kzalloc(sizeof(struct cnode), GFP_KERNEL);
+ if (!p->next)
+ return -1;
+
+ p = p->next;
+
+ p->type = BNODE;
+ p->u.board.status = kstrdup("No", GFP_KERNEL);
+ line = conc = NULL;
+ brd = p;
+ linecnt = -1;
+
+ board_type = dgap_gettok(in);
+ if (board_type == 0) {
+ pr_err("board !!type not specified");
+ return -1;
+ }
+
+ p->u.board.type = board_type;
+
+ break;
+
+ case IO: /* i/o port */
+ if (p->type != BNODE) {
+ pr_err("IO port only valid for boards");
+ return -1;
+ }
+ s = dgap_getword(in);
+ if (!s) {
+ pr_err("unexpected end of file");
+ return -1;
+ }
+ p->u.board.portstr = kstrdup(s, GFP_KERNEL);
+ if (kstrtol(s, 0, &p->u.board.port)) {
+ pr_err("bad number for IO port");
+ return -1;
+ }
+ p->u.board.v_port = 1;
+ break;
+
+ case MEM: /* memory address */
+ if (p->type != BNODE) {
+ pr_err("memory address only valid for boards");
+ return -1;
+ }
+ s = dgap_getword(in);
+ if (!s) {
+ pr_err("unexpected end of file");
+ return -1;
+ }
+ p->u.board.addrstr = kstrdup(s, GFP_KERNEL);
+ if (kstrtoul(s, 0, &p->u.board.addr)) {
+ pr_err("bad number for memory address");
+ return -1;
+ }
+ p->u.board.v_addr = 1;
+ break;
+
+ case PCIINFO: /* pci information */
+ if (p->type != BNODE) {
+ pr_err("memory address only valid for boards");
+ return -1;
+ }
+ s = dgap_getword(in);
+ if (!s) {
+ pr_err("unexpected end of file");
+ return -1;
+ }
+ p->u.board.pcibusstr = kstrdup(s, GFP_KERNEL);
+ if (kstrtoul(s, 0, &p->u.board.pcibus)) {
+ pr_err("bad number for pci bus");
+ return -1;
+ }
+ p->u.board.v_pcibus = 1;
+ s = dgap_getword(in);
+ if (!s) {
+ pr_err("unexpected end of file");
+ return -1;
+ }
+ p->u.board.pcislotstr = kstrdup(s, GFP_KERNEL);
+ if (kstrtoul(s, 0, &p->u.board.pcislot)) {
+ pr_err("bad number for pci slot");
+ return -1;
+ }
+ p->u.board.v_pcislot = 1;
+ break;
+
+ case METHOD:
+ if (p->type != BNODE) {
+ pr_err("install method only valid for boards");
+ return -1;
+ }
+ s = dgap_getword(in);
+ if (!s) {
+ pr_err("unexpected end of file");
+ return -1;
+ }
+ p->u.board.method = kstrdup(s, GFP_KERNEL);
+ p->u.board.v_method = 1;
+ break;
+
+ case STATUS:
+ if (p->type != BNODE) {
+ pr_err("config status only valid for boards");
+ return -1;
+ }
+ s = dgap_getword(in);
+ if (!s) {
+ pr_err("unexpected end of file");
+ return -1;
+ }
+ p->u.board.status = kstrdup(s, GFP_KERNEL);
+ break;
+
+ case NPORTS: /* number of ports */
+ if (p->type == BNODE) {
+ s = dgap_getword(in);
+ if (!s) {
+ pr_err("unexpected end of file");
+ return -1;
+ }
+ if (kstrtol(s, 0, &p->u.board.nport)) {
+ pr_err("bad number for number of ports");
+ return -1;
+ }
+ p->u.board.v_nport = 1;
+ } else if (p->type == CNODE) {
+ s = dgap_getword(in);
+ if (!s) {
+ pr_err("unexpected end of file");
+ return -1;
+ }
+ if (kstrtol(s, 0, &p->u.conc.nport)) {
+ pr_err("bad number for number of ports");
+ return -1;
+ }
+ p->u.conc.v_nport = 1;
+ } else if (p->type == MNODE) {
+ s = dgap_getword(in);
+ if (!s) {
+ pr_err("unexpected end of file");
+ return -1;
+ }
+ if (kstrtol(s, 0, &p->u.module.nport)) {
+ pr_err("bad number for number of ports");
+ return -1;
+ }
+ p->u.module.v_nport = 1;
+ } else {
+ pr_err("nports only valid for concentrators or modules");
+ return -1;
+ }
+ break;
+
+ case ID: /* letter ID used in tty name */
+ s = dgap_getword(in);
+ if (!s) {
+ pr_err("unexpected end of file");
+ return -1;
+ }
+
+ p->u.board.status = kstrdup(s, GFP_KERNEL);
+
+ if (p->type == CNODE) {
+ p->u.conc.id = kstrdup(s, GFP_KERNEL);
+ p->u.conc.v_id = 1;
+ } else if (p->type == MNODE) {
+ p->u.module.id = kstrdup(s, GFP_KERNEL);
+ p->u.module.v_id = 1;
+ } else {
+ pr_err("id only valid for concentrators or modules");
+ return -1;
+ }
+ break;
+
+ case STARTO: /* start offset of ID */
+ if (p->type == BNODE) {
+ s = dgap_getword(in);
+ if (!s) {
+ pr_err("unexpected end of file");
+ return -1;
+ }
+ if (kstrtol(s, 0, &p->u.board.start)) {
+ pr_err("bad number for start of tty count");
+ return -1;
+ }
+ p->u.board.v_start = 1;
+ } else if (p->type == CNODE) {
+ s = dgap_getword(in);
+ if (!s) {
+ pr_err("unexpected end of file");
+ return -1;
+ }
+ if (kstrtol(s, 0, &p->u.conc.start)) {
+ pr_err("bad number for start of tty count");
+ return -1;
+ }
+ p->u.conc.v_start = 1;
+ } else if (p->type == MNODE) {
+ s = dgap_getword(in);
+ if (!s) {
+ pr_err("unexpected end of file");
+ return -1;
+ }
+ if (kstrtol(s, 0, &p->u.module.start)) {
+ pr_err("bad number for start of tty count");
+ return -1;
+ }
+ p->u.module.v_start = 1;
+ } else {
+ pr_err("start only valid for concentrators or modules");
+ return -1;
+ }
+ break;
+
+ case TTYN: /* tty name prefix */
+ if (dgap_checknode(p))
+ return -1;
+
+ p->next = kzalloc(sizeof(struct cnode), GFP_KERNEL);
+ if (!p->next)
+ return -1;
+
+ p = p->next;
+ p->type = TNODE;
+
+ s = dgap_getword(in);
+ if (!s) {
+ pr_err("unexpeced end of file");
+ return -1;
+ }
+ p->u.ttyname = kstrdup(s, GFP_KERNEL);
+ if (!p->u.ttyname)
+ return -1;
+
+ break;
+
+ case CU: /* cu name prefix */
+ if (dgap_checknode(p))
+ return -1;
+
+ p->next = kzalloc(sizeof(struct cnode), GFP_KERNEL);
+ if (!p->next)
+ return -1;
+
+ p = p->next;
+ p->type = CUNODE;
+
+ s = dgap_getword(in);
+ if (!s) {
+ pr_err("unexpeced end of file");
+ return -1;
+ }
+ p->u.cuname = kstrdup(s, GFP_KERNEL);
+ if (!p->u.cuname)
+ return -1;
+
+ break;
+
+ case LINE: /* line information */
+ if (dgap_checknode(p))
+ return -1;
+ if (!brd) {
+ pr_err("must specify board before line info");
+ return -1;
+ }
+ switch (brd->u.board.type) {
+ case PPCM:
+ pr_err("line not valid for PC/em");
+ return -1;
+ }
+
+ p->next = kzalloc(sizeof(struct cnode), GFP_KERNEL);
+ if (!p->next)
+ return -1;
+
+ p = p->next;
+ p->type = LNODE;
+ conc = NULL;
+ line = p;
+ linecnt++;
+ break;
+
+ case CONC: /* concentrator information */
+ if (dgap_checknode(p))
+ return -1;
+ if (!line) {
+ pr_err("must specify line info before concentrator");
+ return -1;
+ }
+
+ p->next = kzalloc(sizeof(struct cnode), GFP_KERNEL);
+ if (!p->next)
+ return -1;
+
+ p = p->next;
+ p->type = CNODE;
+ conc = p;
+
+ if (linecnt)
+ brd->u.board.conc2++;
+ else
+ brd->u.board.conc1++;
+
+ conc_type = dgap_gettok(in);
+ if (conc_type == 0 || (conc_type != CX &&
+ conc_type != EPC)) {
+ pr_err("failed to set a type of concentratros");
+ return -1;
+ }
+
+ p->u.conc.type = conc_type;
+
+ break;
+
+ case MOD: /* EBI module */
+ if (dgap_checknode(p))
+ return -1;
+ if (!brd) {
+ pr_err("must specify board info before EBI modules");
+ return -1;
+ }
+ switch (brd->u.board.type) {
+ case PPCM:
+ linecnt = 0;
+ break;
+ default:
+ if (!conc) {
+ pr_err("must specify concentrator info before EBI module");
+ return -1;
+ }
+ }
+
+ p->next = kzalloc(sizeof(struct cnode), GFP_KERNEL);
+ if (!p->next)
+ return -1;
+
+ p = p->next;
+ p->type = MNODE;
+
+ if (linecnt)
+ brd->u.board.module2++;
+ else
+ brd->u.board.module1++;
+
+ module_type = dgap_gettok(in);
+ if (module_type == 0 || (module_type != PORTS &&
+ module_type != MODEM)) {
+ pr_err("failed to set a type of module");
+ return -1;
+ }
+
+ p->u.module.type = module_type;
+
+ break;
+
+ case CABLE:
+ if (p->type == LNODE) {
+ s = dgap_getword(in);
+ if (!s) {
+ pr_err("unexpected end of file");
+ return -1;
+ }
+ p->u.line.cable = kstrdup(s, GFP_KERNEL);
+ p->u.line.v_cable = 1;
+ }
+ break;
+
+ case SPEED: /* sync line speed indication */
+ if (p->type == LNODE) {
+ s = dgap_getword(in);
+ if (!s) {
+ pr_err("unexpected end of file");
+ return -1;
+ }
+ if (kstrtol(s, 0, &p->u.line.speed)) {
+ pr_err("bad number for line speed");
+ return -1;
+ }
+ p->u.line.v_speed = 1;
+ } else if (p->type == CNODE) {
+ s = dgap_getword(in);
+ if (!s) {
+ pr_err("unexpected end of file");
+ return -1;
+ }
+ if (kstrtol(s, 0, &p->u.conc.speed)) {
+ pr_err("bad number for line speed");
+ return -1;
+ }
+ p->u.conc.v_speed = 1;
+ } else {
+ pr_err("speed valid only for lines or concentrators.");
+ return -1;
+ }
+ break;
+
+ case CONNECT:
+ if (p->type == CNODE) {
+ s = dgap_getword(in);
+ if (!s) {
+ pr_err("unexpected end of file");
+ return -1;
+ }
+ p->u.conc.connect = kstrdup(s, GFP_KERNEL);
+ p->u.conc.v_connect = 1;
+ }
+ break;
+ case PRINT: /* transparent print name prefix */
+ if (dgap_checknode(p))
+ return -1;
+
+ p->next = kzalloc(sizeof(struct cnode), GFP_KERNEL);
+ if (!p->next)
+ return -1;
+
+ p = p->next;
+ p->type = PNODE;
+
+ s = dgap_getword(in);
+ if (!s) {
+ pr_err("unexpeced end of file");
+ return -1;
+ }
+ p->u.printname = kstrdup(s, GFP_KERNEL);
+ if (!p->u.printname)
+ return -1;
+
+ break;
+
+ case CMAJOR: /* major number */
+ if (dgap_checknode(p))
+ return -1;
+
+ p->next = kzalloc(sizeof(struct cnode), GFP_KERNEL);
+ if (!p->next)
+ return -1;
+
+ p = p->next;
+ p->type = JNODE;
+
+ s = dgap_getword(in);
+ if (!s) {
+ pr_err("unexpected end of file");
+ return -1;
+ }
+ if (kstrtol(s, 0, &p->u.majornumber)) {
+ pr_err("bad number for major number");
+ return -1;
+ }
+ break;
+
+ case ALTPIN: /* altpin setting */
+ if (dgap_checknode(p))
+ return -1;
+
+ p->next = kzalloc(sizeof(struct cnode), GFP_KERNEL);
+ if (!p->next)
+ return -1;
+
+ p = p->next;
+ p->type = ANODE;
+
+ s = dgap_getword(in);
+ if (!s) {
+ pr_err("unexpected end of file");
+ return -1;
+ }
+ if (kstrtol(s, 0, &p->u.altpin)) {
+ pr_err("bad number for altpin");
+ return -1;
+ }
+ break;
+
+ case USEINTR: /* enable interrupt setting */
+ if (dgap_checknode(p))
+ return -1;
+
+ p->next = kzalloc(sizeof(struct cnode), GFP_KERNEL);
+ if (!p->next)
+ return -1;
+
+ p = p->next;
+ p->type = INTRNODE;
+ s = dgap_getword(in);
+ if (!s) {
+ pr_err("unexpected end of file");
+ return -1;
+ }
+ if (kstrtol(s, 0, &p->u.useintr)) {
+ pr_err("bad number for useintr");
+ return -1;
+ }
+ break;
+
+ case TTSIZ: /* size of tty structure */
+ if (dgap_checknode(p))
+ return -1;
+
+ p->next = kzalloc(sizeof(struct cnode), GFP_KERNEL);
+ if (!p->next)
+ return -1;
+
+ p = p->next;
+ p->type = TSNODE;
+
+ s = dgap_getword(in);
+ if (!s) {
+ pr_err("unexpected end of file");
+ return -1;
+ }
+ if (kstrtol(s, 0, &p->u.ttysize)) {
+ pr_err("bad number for ttysize");
+ return -1;
+ }
+ break;
+
+ case CHSIZ: /* channel structure size */
+ if (dgap_checknode(p))
+ return -1;
+
+ p->next = kzalloc(sizeof(struct cnode), GFP_KERNEL);
+ if (!p->next)
+ return -1;
+
+ p = p->next;
+ p->type = CSNODE;
+
+ s = dgap_getword(in);
+ if (!s) {
+ pr_err("unexpected end of file");
+ return -1;
+ }
+ if (kstrtol(s, 0, &p->u.chsize)) {
+ pr_err("bad number for chsize");
+ return -1;
+ }
+ break;
+
+ case BSSIZ: /* board structure size */
+ if (dgap_checknode(p))
+ return -1;
+
+ p->next = kzalloc(sizeof(struct cnode), GFP_KERNEL);
+ if (!p->next)
+ return -1;
+
+ p = p->next;
+ p->type = BSNODE;
+
+ s = dgap_getword(in);
+ if (!s) {
+ pr_err("unexpected end of file");
+ return -1;
+ }
+ if (kstrtol(s, 0, &p->u.bssize)) {
+ pr_err("bad number for bssize");
+ return -1;
+ }
+ break;
+
+ case UNTSIZ: /* sched structure size */
+ if (dgap_checknode(p))
+ return -1;
+
+ p->next = kzalloc(sizeof(struct cnode), GFP_KERNEL);
+ if (!p->next)
+ return -1;
+
+ p = p->next;
+ p->type = USNODE;
+
+ s = dgap_getword(in);
+ if (!s) {
+ pr_err("unexpected end of file");
+ return -1;
+ }
+ if (kstrtol(s, 0, &p->u.unsize)) {
+ pr_err("bad number for schedsize");
+ return -1;
+ }
+ break;
+
+ case F2SIZ: /* f2200 structure size */
+ if (dgap_checknode(p))
+ return -1;
+
+ p->next = kzalloc(sizeof(struct cnode), GFP_KERNEL);
+ if (!p->next)
+ return -1;
+
+ p = p->next;
+ p->type = FSNODE;
+
+ s = dgap_getword(in);
+ if (!s) {
+ pr_err("unexpected end of file");
+ return -1;
+ }
+ if (kstrtol(s, 0, &p->u.f2size)) {
+ pr_err("bad number for f2200size");
+ return -1;
+ }
+ break;
+
+ case VPSIZ: /* vpix structure size */
+ if (dgap_checknode(p))
+ return -1;
+
+ p->next = kzalloc(sizeof(struct cnode), GFP_KERNEL);
+ if (!p->next)
+ return -1;
+
+ p = p->next;
+ p->type = VSNODE;
+
+ s = dgap_getword(in);
+ if (!s) {
+ pr_err("unexpected end of file");
+ return -1;
+ }
+ if (kstrtol(s, 0, &p->u.vpixsize)) {
+ pr_err("bad number for vpixsize");
+ return -1;
+ }
+ break;
+ }
+ }
+}
+
+static void dgap_cleanup_nodes(void)
+{
+ struct cnode *p;
+
+ p = &dgap_head;
+
+ while (p) {
+ struct cnode *tmp = p->next;
+
+ if (p->type == NULLNODE) {
+ p = tmp;
+ continue;
+ }
+
+ switch (p->type) {
+ case BNODE:
+ kfree(p->u.board.portstr);
+ kfree(p->u.board.addrstr);
+ kfree(p->u.board.pcibusstr);
+ kfree(p->u.board.pcislotstr);
+ kfree(p->u.board.method);
+ break;
+ case CNODE:
+ kfree(p->u.conc.id);
+ kfree(p->u.conc.connect);
+ break;
+ case MNODE:
+ kfree(p->u.module.id);
+ break;
+ case TNODE:
+ kfree(p->u.ttyname);
+ break;
+ case CUNODE:
+ kfree(p->u.cuname);
+ break;
+ case LNODE:
+ kfree(p->u.line.cable);
+ break;
+ case PNODE:
+ kfree(p->u.printname);
+ break;
+ }
+
+ kfree(p->u.board.status);
+ kfree(p);
+ p = tmp;
+ }
+}
+
+/*
+ * Retrives the current custom baud rate from FEP memory,
+ * and returns it back to the user.
+ * Returns 0 on error.
+ */
+static uint dgap_get_custom_baud(struct channel_t *ch)
+{
+ u8 __iomem *vaddr;
+ ulong offset;
+
+ if (!ch || ch->magic != DGAP_CHANNEL_MAGIC)
+ return 0;
+
+ if (!ch->ch_bd || ch->ch_bd->magic != DGAP_BOARD_MAGIC)
+ return 0;
+
+ if (!(ch->ch_bd->bd_flags & BD_FEP5PLUS))
+ return 0;
+
+ vaddr = ch->ch_bd->re_map_membase;
+
+ if (!vaddr)
+ return 0;
+
+ /*
+ * Go get from fep mem, what the fep
+ * believes the custom baud rate is.
+ */
+ offset = (ioread16(vaddr + ECS_SEG) << 4) + (ch->ch_portnum * 0x28)
+ + LINE_SPEED;
+
+ return readw(vaddr + offset);
+}
+
+/*
+ * Remap PCI memory.
+ */
+static int dgap_remap(struct board_t *brd)
+{
+ if (!brd || brd->magic != DGAP_BOARD_MAGIC)
+ return -EIO;
+
+ if (!request_mem_region(brd->membase, 0x200000, "dgap"))
+ return -ENOMEM;
+
+ if (!request_mem_region(brd->membase + PCI_IO_OFFSET, 0x200000,
+ "dgap"))
+ goto err_req_mem;
+
+ brd->re_map_membase = ioremap(brd->membase, 0x200000);
+ if (!brd->re_map_membase)
+ goto err_remap_mem;
+
+ brd->re_map_port = ioremap((brd->membase + PCI_IO_OFFSET), 0x200000);
+ if (!brd->re_map_port)
+ goto err_remap_port;
+
+ return 0;
+
+err_remap_port:
+ iounmap(brd->re_map_membase);
+err_remap_mem:
+ release_mem_region(brd->membase + PCI_IO_OFFSET, 0x200000);
+err_req_mem:
+ release_mem_region(brd->membase, 0x200000);
+
+ return -ENOMEM;
+}
+
+static void dgap_unmap(struct board_t *brd)
+{
+ iounmap(brd->re_map_port);
+ iounmap(brd->re_map_membase);
+ release_mem_region(brd->membase + PCI_IO_OFFSET, 0x200000);
+ release_mem_region(brd->membase, 0x200000);
+}
+
+/*
+ * dgap_parity_scan()
+ *
+ * Convert the FEP5 way of reporting parity errors and breaks into
+ * the Linux line discipline way.
+ */
+static void dgap_parity_scan(struct channel_t *ch, unsigned char *cbuf,
+ unsigned char *fbuf, int *len)
+{
+ int l = *len;
+ int count = 0;
+ unsigned char *in, *cout, *fout;
+ unsigned char c;
+
+ in = cbuf;
+ cout = cbuf;
+ fout = fbuf;
+
+ if (!ch || ch->magic != DGAP_CHANNEL_MAGIC)
+ return;
+
+ while (l--) {
+ c = *in++;
+ switch (ch->pscan_state) {
+ default:
+ /* reset to sanity and fall through */
+ ch->pscan_state = 0;
+
+ case 0:
+ /* No FF seen yet */
+ if (c == (unsigned char) '\377')
+ /* delete this character from stream */
+ ch->pscan_state = 1;
+ else {
+ *cout++ = c;
+ *fout++ = TTY_NORMAL;
+ count += 1;
+ }
+ break;
+
+ case 1:
+ /* first FF seen */
+ if (c == (unsigned char) '\377') {
+ /* doubled ff, transform to single ff */
+ *cout++ = c;
+ *fout++ = TTY_NORMAL;
+ count += 1;
+ ch->pscan_state = 0;
+ } else {
+ /* save value examination in next state */
+ ch->pscan_savechar = c;
+ ch->pscan_state = 2;
+ }
+ break;
+
+ case 2:
+ /* third character of ff sequence */
+
+ *cout++ = c;
+
+ if (ch->pscan_savechar == 0x0) {
+
+ if (c == 0x0) {
+ ch->ch_err_break++;
+ *fout++ = TTY_BREAK;
+ } else {
+ ch->ch_err_parity++;
+ *fout++ = TTY_PARITY;
+ }
+ }
+
+ count += 1;
+ ch->pscan_state = 0;
+ }
+ }
+ *len = count;
+}
+
+/*=======================================================================
+ *
+ * dgap_input - Process received data.
+ *
+ * ch - Pointer to channel structure.
+ *
+ *=======================================================================*/
+
+static void dgap_input(struct channel_t *ch)
+{
+ struct board_t *bd;
+ struct bs_t __iomem *bs;
+ struct tty_struct *tp;
+ struct tty_ldisc *ld;
+ uint rmask;
+ uint head;
+ uint tail;
+ int data_len;
+ ulong lock_flags;
+ ulong lock_flags2;
+ int flip_len;
+ int len;
+ int n;
+ u8 *buf;
+ u8 tmpchar;
+ int s;
+
+ if (!ch || ch->magic != DGAP_CHANNEL_MAGIC)
+ return;
+
+ tp = ch->ch_tun.un_tty;
+
+ bs = ch->ch_bs;
+ if (!bs)
+ return;
+
+ bd = ch->ch_bd;
+ if (!bd || bd->magic != DGAP_BOARD_MAGIC)
+ return;
+
+ spin_lock_irqsave(&bd->bd_lock, lock_flags);
+ spin_lock_irqsave(&ch->ch_lock, lock_flags2);
+
+ /*
+ * Figure the number of characters in the buffer.
+ * Exit immediately if none.
+ */
+
+ rmask = ch->ch_rsize - 1;
+
+ head = readw(&(bs->rx_head));
+ head &= rmask;
+ tail = readw(&(bs->rx_tail));
+ tail &= rmask;
+
+ data_len = (head - tail) & rmask;
+
+ if (data_len == 0) {
+ writeb(1, &(bs->idata));
+ spin_unlock_irqrestore(&ch->ch_lock, lock_flags2);
+ spin_unlock_irqrestore(&bd->bd_lock, lock_flags);
+ return;
+ }
+
+ /*
+ * If the device is not open, or CREAD is off, flush
+ * input data and return immediately.
+ */
+ if ((bd->state != BOARD_READY) || !tp ||
+ (tp->magic != TTY_MAGIC) ||
+ !(ch->ch_tun.un_flags & UN_ISOPEN) ||
+ !(tp->termios.c_cflag & CREAD) ||
+ (ch->ch_tun.un_flags & UN_CLOSING)) {
+
+ writew(head, &(bs->rx_tail));
+ writeb(1, &(bs->idata));
+ spin_unlock_irqrestore(&ch->ch_lock, lock_flags2);
+ spin_unlock_irqrestore(&bd->bd_lock, lock_flags);
+ return;
+ }
+
+ /*
+ * If we are throttled, simply don't read any data.
+ */
+ if (ch->ch_flags & CH_RXBLOCK) {
+ writeb(1, &(bs->idata));
+ spin_unlock_irqrestore(&ch->ch_lock, lock_flags2);
+ spin_unlock_irqrestore(&bd->bd_lock, lock_flags);
+ return;
+ }
+
+ /*
+ * Ignore oruns.
+ */
+ tmpchar = readb(&(bs->orun));
+ if (tmpchar) {
+ ch->ch_err_overrun++;
+ writeb(0, &(bs->orun));
+ }
+
+ /* Decide how much data we can send into the tty layer */
+ flip_len = TTY_FLIPBUF_SIZE;
+
+ /* Chop down the length, if needed */
+ len = min(data_len, flip_len);
+ len = min(len, (N_TTY_BUF_SIZE - 1));
+
+ ld = tty_ldisc_ref(tp);
+
+#ifdef TTY_DONT_FLIP
+ /*
+ * If the DONT_FLIP flag is on, don't flush our buffer, and act
+ * like the ld doesn't have any space to put the data right now.
+ */
+ if (test_bit(TTY_DONT_FLIP, &tp->flags))
+ len = 0;
+#endif
+
+ /*
+ * If we were unable to get a reference to the ld,
+ * don't flush our buffer, and act like the ld doesn't
+ * have any space to put the data right now.
+ */
+ if (!ld) {
+ len = 0;
+ } else {
+ /*
+ * If ld doesn't have a pointer to a receive_buf function,
+ * flush the data, then act like the ld doesn't have any
+ * space to put the data right now.
+ */
+ if (!ld->ops->receive_buf) {
+ writew(head, &(bs->rx_tail));
+ len = 0;
+ }
+ }
+
+ if (len <= 0) {
+ writeb(1, &(bs->idata));
+ spin_unlock_irqrestore(&ch->ch_lock, lock_flags2);
+ spin_unlock_irqrestore(&bd->bd_lock, lock_flags);
+ if (ld)
+ tty_ldisc_deref(ld);
+ return;
+ }
+
+ buf = ch->ch_bd->flipbuf;
+ n = len;
+
+ /*
+ * n now contains the most amount of data we can copy,
+ * bounded either by our buffer size or the amount
+ * of data the card actually has pending...
+ */
+ while (n) {
+
+ s = ((head >= tail) ? head : ch->ch_rsize) - tail;
+ s = min(s, n);
+
+ if (s <= 0)
+ break;
+
+ memcpy_fromio(buf, ch->ch_raddr + tail, s);
+
+ tail += s;
+ buf += s;
+
+ n -= s;
+ /* Flip queue if needed */
+ tail &= rmask;
+ }
+
+ writew(tail, &(bs->rx_tail));
+ writeb(1, &(bs->idata));
+ ch->ch_rxcount += len;
+
+ /*
+ * If we are completely raw, we don't need to go through a lot
+ * of the tty layers that exist.
+ * In this case, we take the shortest and fastest route we
+ * can to relay the data to the user.
+ *
+ * On the other hand, if we are not raw, we need to go through
+ * the tty layer, which has its API more well defined.
+ */
+ if (I_PARMRK(tp) || I_BRKINT(tp) || I_INPCK(tp)) {
+ dgap_parity_scan(ch, ch->ch_bd->flipbuf,
+ ch->ch_bd->flipflagbuf, &len);
+
+ len = tty_buffer_request_room(tp->port, len);
+ tty_insert_flip_string_flags(tp->port, ch->ch_bd->flipbuf,
+ ch->ch_bd->flipflagbuf, len);
+ } else {
+ len = tty_buffer_request_room(tp->port, len);
+ tty_insert_flip_string(tp->port, ch->ch_bd->flipbuf, len);
+ }
+
+ spin_unlock_irqrestore(&ch->ch_lock, lock_flags2);
+ spin_unlock_irqrestore(&bd->bd_lock, lock_flags);
+
+ /* Tell the tty layer its okay to "eat" the data now */
+ tty_flip_buffer_push(tp->port);
+
+ if (ld)
+ tty_ldisc_deref(ld);
+
+}
+
+static void dgap_write_wakeup(struct board_t *bd, struct channel_t *ch,
+ struct un_t *un, u32 mask,
+ unsigned long *irq_flags1,
+ unsigned long *irq_flags2)
+{
+ if (!(un->un_flags & mask))
+ return;
+
+ un->un_flags &= ~mask;
+
+ if (!(un->un_flags & UN_ISOPEN))
+ return;
+
+ if ((un->un_tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+ un->un_tty->ldisc->ops->write_wakeup) {
+ spin_unlock_irqrestore(&ch->ch_lock, *irq_flags2);
+ spin_unlock_irqrestore(&bd->bd_lock, *irq_flags1);
+
+ (un->un_tty->ldisc->ops->write_wakeup)(un->un_tty);
+
+ spin_lock_irqsave(&bd->bd_lock, *irq_flags1);
+ spin_lock_irqsave(&ch->ch_lock, *irq_flags2);
+ }
+ wake_up_interruptible(&un->un_tty->write_wait);
+ wake_up_interruptible(&un->un_flags_wait);
+}
+
+/************************************************************************
+ * Determines when CARRIER changes state and takes appropriate
+ * action.
+ ************************************************************************/
+static void dgap_carrier(struct channel_t *ch)
+{
+ struct board_t *bd;
+
+ int virt_carrier = 0;
+ int phys_carrier = 0;
+
+ if (!ch || ch->magic != DGAP_CHANNEL_MAGIC)
+ return;
+
+ bd = ch->ch_bd;
+
+ if (!bd || bd->magic != DGAP_BOARD_MAGIC)
+ return;
+
+ /* Make sure altpin is always set correctly */
+ if (ch->ch_digi.digi_flags & DIGI_ALTPIN) {
+ ch->ch_dsr = DM_CD;
+ ch->ch_cd = DM_DSR;
+ } else {
+ ch->ch_dsr = DM_DSR;
+ ch->ch_cd = DM_CD;
+ }
+
+ if (ch->ch_mistat & D_CD(ch))
+ phys_carrier = 1;
+
+ if (ch->ch_digi.digi_flags & DIGI_FORCEDCD)
+ virt_carrier = 1;
+
+ if (ch->ch_c_cflag & CLOCAL)
+ virt_carrier = 1;
+
+ /*
+ * Test for a VIRTUAL carrier transition to HIGH.
+ */
+ if (((ch->ch_flags & CH_FCAR) == 0) && (virt_carrier == 1)) {
+
+ /*
+ * When carrier rises, wake any threads waiting
+ * for carrier in the open routine.
+ */
+
+ if (waitqueue_active(&(ch->ch_flags_wait)))
+ wake_up_interruptible(&ch->ch_flags_wait);
+ }
+
+ /*
+ * Test for a PHYSICAL carrier transition to HIGH.
+ */
+ if (((ch->ch_flags & CH_CD) == 0) && (phys_carrier == 1)) {
+
+ /*
+ * When carrier rises, wake any threads waiting
+ * for carrier in the open routine.
+ */
+
+ if (waitqueue_active(&(ch->ch_flags_wait)))
+ wake_up_interruptible(&ch->ch_flags_wait);
+ }
+
+ /*
+ * Test for a PHYSICAL transition to low, so long as we aren't
+ * currently ignoring physical transitions (which is what "virtual
+ * carrier" indicates).
+ *
+ * The transition of the virtual carrier to low really doesn't
+ * matter... it really only means "ignore carrier state", not
+ * "make pretend that carrier is there".
+ */
+ if ((virt_carrier == 0) &&
+ ((ch->ch_flags & CH_CD) != 0) &&
+ (phys_carrier == 0)) {
+
+ /*
+ * When carrier drops:
+ *
+ * Drop carrier on all open units.
+ *
+ * Flush queues, waking up any task waiting in the
+ * line discipline.
+ *
+ * Send a hangup to the control terminal.
+ *
+ * Enable all select calls.
+ */
+ if (waitqueue_active(&(ch->ch_flags_wait)))
+ wake_up_interruptible(&ch->ch_flags_wait);
+
+ if (ch->ch_tun.un_open_count > 0)
+ tty_hangup(ch->ch_tun.un_tty);
+
+ if (ch->ch_pun.un_open_count > 0)
+ tty_hangup(ch->ch_pun.un_tty);
+ }
+
+ /*
+ * Make sure that our cached values reflect the current reality.
+ */
+ if (virt_carrier == 1)
+ ch->ch_flags |= CH_FCAR;
+ else
+ ch->ch_flags &= ~CH_FCAR;
+
+ if (phys_carrier == 1)
+ ch->ch_flags |= CH_CD;
+ else
+ ch->ch_flags &= ~CH_CD;
+}
+
+/*=======================================================================
+ *
+ * dgap_event - FEP to host event processing routine.
+ *
+ * bd - Board of current event.
+ *
+ *=======================================================================*/
+static int dgap_event(struct board_t *bd)
+{
+ struct channel_t *ch;
+ ulong lock_flags;
+ ulong lock_flags2;
+ struct bs_t __iomem *bs;
+ u8 __iomem *event;
+ u8 __iomem *vaddr;
+ struct ev_t __iomem *eaddr;
+ uint head;
+ uint tail;
+ int port;
+ int reason;
+ int modem;
+ int b1;
+
+ if (!bd || bd->magic != DGAP_BOARD_MAGIC)
+ return -EIO;
+
+ spin_lock_irqsave(&bd->bd_lock, lock_flags);
+
+ vaddr = bd->re_map_membase;
+
+ if (!vaddr) {
+ spin_unlock_irqrestore(&bd->bd_lock, lock_flags);
+ return -EIO;
+ }
+
+ eaddr = (struct ev_t __iomem *) (vaddr + EVBUF);
+
+ /* Get our head and tail */
+ head = readw(&(eaddr->ev_head));
+ tail = readw(&(eaddr->ev_tail));
+
+ /*
+ * Forget it if pointers out of range.
+ */
+
+ if (head >= EVMAX - EVSTART || tail >= EVMAX - EVSTART ||
+ (head | tail) & 03) {
+ /* Let go of board lock */
+ spin_unlock_irqrestore(&bd->bd_lock, lock_flags);
+ return -EIO;
+ }
+
+ /*
+ * Loop to process all the events in the buffer.
+ */
+ while (tail != head) {
+
+ /*
+ * Get interrupt information.
+ */
+
+ event = bd->re_map_membase + tail + EVSTART;
+
+ port = ioread8(event);
+ reason = ioread8(event + 1);
+ modem = ioread8(event + 2);
+ b1 = ioread8(event + 3);
+
+ /*
+ * Make sure the interrupt is valid.
+ */
+ if (port >= bd->nasync)
+ goto next;
+
+ if (!(reason & (IFMODEM | IFBREAK | IFTLW | IFTEM | IFDATA)))
+ goto next;
+
+ ch = bd->channels[port];
+
+ if (!ch || ch->magic != DGAP_CHANNEL_MAGIC)
+ goto next;
+
+ /*
+ * If we have made it here, the event was valid.
+ * Lock down the channel.
+ */
+ spin_lock_irqsave(&ch->ch_lock, lock_flags2);
+
+ bs = ch->ch_bs;
+
+ if (!bs) {
+ spin_unlock_irqrestore(&ch->ch_lock, lock_flags2);
+ goto next;
+ }
+
+ /*
+ * Process received data.
+ */
+ if (reason & IFDATA) {
+
+ /*
+ * ALL LOCKS *MUST* BE DROPPED BEFORE CALLING INPUT!
+ * input could send some data to ld, which in turn
+ * could do a callback to one of our other functions.
+ */
+ spin_unlock_irqrestore(&ch->ch_lock, lock_flags2);
+ spin_unlock_irqrestore(&bd->bd_lock, lock_flags);
+
+ dgap_input(ch);
+
+ spin_lock_irqsave(&bd->bd_lock, lock_flags);
+ spin_lock_irqsave(&ch->ch_lock, lock_flags2);
+
+ if (ch->ch_flags & CH_RACTIVE)
+ ch->ch_flags |= CH_RENABLE;
+ else
+ writeb(1, &(bs->idata));
+
+ if (ch->ch_flags & CH_RWAIT) {
+ ch->ch_flags &= ~CH_RWAIT;
+
+ wake_up_interruptible
+ (&ch->ch_tun.un_flags_wait);
+ }
+ }
+
+ /*
+ * Process Modem change signals.
+ */
+ if (reason & IFMODEM) {
+ ch->ch_mistat = modem;
+ dgap_carrier(ch);
+ }
+
+ /*
+ * Process break.
+ */
+ if (reason & IFBREAK) {
+
+ if (ch->ch_tun.un_tty) {
+ /* A break has been indicated */
+ ch->ch_err_break++;
+ tty_buffer_request_room
+ (ch->ch_tun.un_tty->port, 1);
+ tty_insert_flip_char(ch->ch_tun.un_tty->port,
+ 0, TTY_BREAK);
+ tty_flip_buffer_push(ch->ch_tun.un_tty->port);
+ }
+ }
+
+ /*
+ * Process Transmit low.
+ */
+ if (reason & IFTLW) {
+ dgap_write_wakeup(bd, ch, &ch->ch_tun, UN_LOW,
+ &lock_flags, &lock_flags2);
+ dgap_write_wakeup(bd, ch, &ch->ch_pun, UN_LOW,
+ &lock_flags, &lock_flags2);
+ if (ch->ch_flags & CH_WLOW) {
+ ch->ch_flags &= ~CH_WLOW;
+ wake_up_interruptible(&ch->ch_flags_wait);
+ }
+ }
+
+ /*
+ * Process Transmit empty.
+ */
+ if (reason & IFTEM) {
+ dgap_write_wakeup(bd, ch, &ch->ch_tun, UN_EMPTY,
+ &lock_flags, &lock_flags2);
+ dgap_write_wakeup(bd, ch, &ch->ch_pun, UN_EMPTY,
+ &lock_flags, &lock_flags2);
+ if (ch->ch_flags & CH_WEMPTY) {
+ ch->ch_flags &= ~CH_WEMPTY;
+ wake_up_interruptible(&ch->ch_flags_wait);
+ }
+ }
+
+ spin_unlock_irqrestore(&ch->ch_lock, lock_flags2);
+
+next:
+ tail = (tail + 4) & (EVMAX - EVSTART - 4);
+ }
+
+ writew(tail, &(eaddr->ev_tail));
+ spin_unlock_irqrestore(&bd->bd_lock, lock_flags);
+
+ return 0;
+}
+
+/*
+ * Our board poller function.
+ */
+static void dgap_poll_tasklet(unsigned long data)
+{
+ struct board_t *bd = (struct board_t *) data;
+ ulong lock_flags;
+ char __iomem *vaddr;
+ u16 head, tail;
+
+ if (!bd || (bd->magic != DGAP_BOARD_MAGIC))
+ return;
+
+ if (bd->inhibit_poller)
+ return;
+
+ spin_lock_irqsave(&bd->bd_lock, lock_flags);
+
+ vaddr = bd->re_map_membase;
+
+ /*
+ * If board is ready, parse deeper to see if there is anything to do.
+ */
+ if (bd->state == BOARD_READY) {
+
+ struct ev_t __iomem *eaddr;
+
+ if (!bd->re_map_membase) {
+ spin_unlock_irqrestore(&bd->bd_lock, lock_flags);
+ return;
+ }
+ if (!bd->re_map_port) {
+ spin_unlock_irqrestore(&bd->bd_lock, lock_flags);
+ return;
+ }
+
+ if (!bd->nasync)
+ goto out;
+
+ eaddr = (struct ev_t __iomem *) (vaddr + EVBUF);
+
+ /* Get our head and tail */
+ head = readw(&(eaddr->ev_head));
+ tail = readw(&(eaddr->ev_tail));
+
+ /*
+ * If there is an event pending. Go service it.
+ */
+ if (head != tail) {
+ spin_unlock_irqrestore(&bd->bd_lock, lock_flags);
+ dgap_event(bd);
+ spin_lock_irqsave(&bd->bd_lock, lock_flags);
+ }
+
+out:
+ /*
+ * If board is doing interrupts, ACK the interrupt.
+ */
+ if (bd && bd->intr_running)
+ readb(bd->re_map_port + 2);
+
+ spin_unlock_irqrestore(&bd->bd_lock, lock_flags);
+ return;
+ }
+
+ spin_unlock_irqrestore(&bd->bd_lock, lock_flags);
+}
+
+/*
+ * dgap_found_board()
+ *
+ * A board has been found, init it.
+ */
+static struct board_t *dgap_found_board(struct pci_dev *pdev, int id,
+ int boardnum)
+{
+ struct board_t *brd;
+ unsigned int pci_irq;
+ int i;
+ int ret;
+
+ /* get the board structure and prep it */
+ brd = kzalloc(sizeof(struct board_t), GFP_KERNEL);
+ if (!brd)
+ return ERR_PTR(-ENOMEM);
+
+ /* store the info for the board we've found */
+ brd->magic = DGAP_BOARD_MAGIC;
+ brd->boardnum = boardnum;
+ brd->vendor = dgap_pci_tbl[id].vendor;
+ brd->device = dgap_pci_tbl[id].device;
+ brd->pdev = pdev;
+ brd->pci_bus = pdev->bus->number;
+ brd->pci_slot = PCI_SLOT(pdev->devfn);
+ brd->name = dgap_ids[id].name;
+ brd->maxports = dgap_ids[id].maxports;
+ brd->type = dgap_ids[id].config_type;
+ brd->dpatype = dgap_ids[id].dpatype;
+ brd->dpastatus = BD_NOFEP;
+ init_waitqueue_head(&brd->state_wait);
+
+ spin_lock_init(&brd->bd_lock);
+
+ brd->inhibit_poller = FALSE;
+ brd->wait_for_bios = 0;
+ brd->wait_for_fep = 0;
+
+ for (i = 0; i < MAXPORTS; i++)
+ brd->channels[i] = NULL;
+
+ /* store which card & revision we have */
+ pci_read_config_word(pdev, PCI_SUBSYSTEM_VENDOR_ID, &brd->subvendor);
+ pci_read_config_word(pdev, PCI_SUBSYSTEM_ID, &brd->subdevice);
+ pci_read_config_byte(pdev, PCI_REVISION_ID, &brd->rev);
+
+ pci_irq = pdev->irq;
+ brd->irq = pci_irq;
+
+ /* get the PCI Base Address Registers */
+
+ /* Xr Jupiter and EPC use BAR 2 */
+ if (brd->device == PCI_DEV_XRJ_DID || brd->device == PCI_DEV_EPCJ_DID) {
+ brd->membase = pci_resource_start(pdev, 2);
+ brd->membase_end = pci_resource_end(pdev, 2);
+ }
+ /* Everyone else uses BAR 0 */
+ else {
+ brd->membase = pci_resource_start(pdev, 0);
+ brd->membase_end = pci_resource_end(pdev, 0);
+ }
+
+ if (!brd->membase) {
+ ret = -ENODEV;
+ goto free_brd;
+ }
+
+ if (brd->membase & 1)
+ brd->membase &= ~3;
+ else
+ brd->membase &= ~15;
+
+ /*
+ * On the PCI boards, there is no IO space allocated
+ * The I/O registers will be in the first 3 bytes of the
+ * upper 2MB of the 4MB memory space. The board memory
+ * will be mapped into the low 2MB of the 4MB memory space
+ */
+ brd->port = brd->membase + PCI_IO_OFFSET;
+ brd->port_end = brd->port + PCI_IO_SIZE_DGAP;
+
+ /*
+ * Special initialization for non-PLX boards
+ */
+ if (brd->device != PCI_DEV_XRJ_DID && brd->device != PCI_DEV_EPCJ_DID) {
+ unsigned short cmd;
+
+ pci_write_config_byte(pdev, 0x40, 0);
+ pci_write_config_byte(pdev, 0x46, 0);
+
+ /* Limit burst length to 2 doubleword transactions */
+ pci_write_config_byte(pdev, 0x42, 1);
+
+ /*
+ * Enable IO and mem if not already done.
+ * This was needed for support on Itanium.
+ */
+ pci_read_config_word(pdev, PCI_COMMAND, &cmd);
+ cmd |= (PCI_COMMAND_IO | PCI_COMMAND_MEMORY);
+ pci_write_config_word(pdev, PCI_COMMAND, cmd);
+ }
+
+ /* init our poll helper tasklet */
+ tasklet_init(&brd->helper_tasklet, dgap_poll_tasklet,
+ (unsigned long) brd);
+
+ ret = dgap_remap(brd);
+ if (ret)
+ goto free_brd;
+
+ pr_info("dgap: board %d: %s (rev %d), irq %ld\n",
+ boardnum, brd->name, brd->rev, brd->irq);
+
+ return brd;
+
+free_brd:
+ kfree(brd);
+
+ return ERR_PTR(ret);
+}
+
+/*
+ * dgap_intr()
+ *
+ * Driver interrupt handler.
+ */
+static irqreturn_t dgap_intr(int irq, void *voidbrd)
+{
+ struct board_t *brd = voidbrd;
+
+ if (!brd)
+ return IRQ_NONE;
+
+ /*
+ * Check to make sure its for us.
+ */
+ if (brd->magic != DGAP_BOARD_MAGIC)
+ return IRQ_NONE;
+
+ brd->intr_count++;
+
+ /*
+ * Schedule tasklet to run at a better time.
+ */
+ tasklet_schedule(&brd->helper_tasklet);
+ return IRQ_HANDLED;
+}
+
+/*****************************************************************************
+*
+* Function:
+*
+* dgap_poll_handler
+*
+* Author:
+*
+* Scott H Kilau
+*
+* Parameters:
+*
+* dummy -- ignored
+*
+* Return Values:
+*
+* none
+*
+* Description:
+*
+* As each timer expires, it determines (a) whether the "transmit"
+* waiter needs to be woken up, and (b) whether the poller needs to
+* be rescheduled.
+*
+******************************************************************************/
+
+static void dgap_poll_handler(ulong dummy)
+{
+ unsigned int i;
+ struct board_t *brd;
+ unsigned long lock_flags;
+ ulong new_time;
+
+ dgap_poll_counter++;
+
+ /*
+ * Do not start the board state machine until
+ * driver tells us its up and running, and has
+ * everything it needs.
+ */
+ if (dgap_driver_state != DRIVER_READY)
+ goto schedule_poller;
+
+ /*
+ * If we have just 1 board, or the system is not SMP,
+ * then use the typical old style poller.
+ * Otherwise, use our new tasklet based poller, which should
+ * speed things up for multiple boards.
+ */
+ if ((dgap_numboards == 1) || (num_online_cpus() <= 1)) {
+ for (i = 0; i < dgap_numboards; i++) {
+
+ brd = dgap_board[i];
+
+ if (brd->state == BOARD_FAILED)
+ continue;
+ if (!brd->intr_running)
+ /* Call the real board poller directly */
+ dgap_poll_tasklet((unsigned long) brd);
+ }
+ } else {
+ /*
+ * Go thru each board, kicking off a
+ * tasklet for each if needed
+ */
+ for (i = 0; i < dgap_numboards; i++) {
+ brd = dgap_board[i];
+
+ /*
+ * Attempt to grab the board lock.
+ *
+ * If we can't get it, no big deal, the next poll
+ * will get it. Basically, I just really don't want
+ * to spin in here, because I want to kick off my
+ * tasklets as fast as I can, and then get out the
+ * poller.
+ */
+ if (!spin_trylock(&brd->bd_lock))
+ continue;
+
+ /*
+ * If board is in a failed state, don't bother
+ * scheduling a tasklet
+ */
+ if (brd->state == BOARD_FAILED) {
+ spin_unlock(&brd->bd_lock);
+ continue;
+ }
+
+ /* Schedule a poll helper task */
+ if (!brd->intr_running)
+ tasklet_schedule(&brd->helper_tasklet);
+
+ /*
+ * Can't do DGAP_UNLOCK here, as we don't have
+ * lock_flags because we did a trylock above.
+ */
+ spin_unlock(&brd->bd_lock);
+ }
+ }
+
+schedule_poller:
+
+ /*
+ * Schedule ourself back at the nominal wakeup interval.
+ */
+ spin_lock_irqsave(&dgap_poll_lock, lock_flags);
+ dgap_poll_time += dgap_jiffies_from_ms(dgap_poll_tick);
+
+ new_time = dgap_poll_time - jiffies;
+
+ if ((ulong) new_time >= 2 * dgap_poll_tick) {
+ dgap_poll_time =
+ jiffies + dgap_jiffies_from_ms(dgap_poll_tick);
+ }
+
+ dgap_poll_timer.function = dgap_poll_handler;
+ dgap_poll_timer.data = 0;
+ dgap_poll_timer.expires = dgap_poll_time;
+ spin_unlock_irqrestore(&dgap_poll_lock, lock_flags);
+
+ if (!dgap_poll_stop)
+ add_timer(&dgap_poll_timer);
+}
+
+/*=======================================================================
+ *
+ * dgap_cmdb - Sends a 2 byte command to the FEP.
+ *
+ * ch - Pointer to channel structure.
+ * cmd - Command to be sent.
+ * byte1 - Integer containing first byte to be sent.
+ * byte2 - Integer containing second byte to be sent.
+ * ncmds - Wait until ncmds or fewer cmds are left
+ * in the cmd buffer before returning.
+ *
+ *=======================================================================*/
+static void dgap_cmdb(struct channel_t *ch, u8 cmd, u8 byte1,
+ u8 byte2, uint ncmds)
+{
+ char __iomem *vaddr;
+ struct __iomem cm_t *cm_addr;
+ uint count;
+ uint n;
+ u16 head;
+ u16 tail;
+
+ if (!ch || ch->magic != DGAP_CHANNEL_MAGIC)
+ return;
+
+ /*
+ * Check if board is still alive.
+ */
+ if (ch->ch_bd->state == BOARD_FAILED)
+ return;
+
+ /*
+ * Make sure the pointers are in range before
+ * writing to the FEP memory.
+ */
+ vaddr = ch->ch_bd->re_map_membase;
+
+ if (!vaddr)
+ return;
+
+ cm_addr = (struct cm_t __iomem *) (vaddr + CMDBUF);
+ head = readw(&(cm_addr->cm_head));
+
+ /*
+ * Forget it if pointers out of range.
+ */
+ if (head >= (CMDMAX - CMDSTART) || (head & 03)) {
+ ch->ch_bd->state = BOARD_FAILED;
+ return;
+ }
+
+ /*
+ * Put the data in the circular command buffer.
+ */
+ writeb(cmd, (vaddr + head + CMDSTART + 0));
+ writeb((u8) ch->ch_portnum, (vaddr + head + CMDSTART + 1));
+ writeb(byte1, (vaddr + head + CMDSTART + 2));
+ writeb(byte2, (vaddr + head + CMDSTART + 3));
+
+ head = (head + 4) & (CMDMAX - CMDSTART - 4);
+
+ writew(head, &(cm_addr->cm_head));
+
+ /*
+ * Wait if necessary before updating the head
+ * pointer to limit the number of outstanding
+ * commands to the FEP. If the time spent waiting
+ * is outlandish, declare the FEP dead.
+ */
+ for (count = dgap_count ;;) {
+
+ head = readw(&(cm_addr->cm_head));
+ tail = readw(&(cm_addr->cm_tail));
+
+ n = (head - tail) & (CMDMAX - CMDSTART - 4);
+
+ if (n <= ncmds * sizeof(struct cm_t))
+ break;
+
+ if (--count == 0) {
+ ch->ch_bd->state = BOARD_FAILED;
+ return;
+ }
+ udelay(10);
+ }
+}
+
+/*=======================================================================
+ *
+ * dgap_cmdw - Sends a 1 word command to the FEP.
+ *
+ * ch - Pointer to channel structure.
+ * cmd - Command to be sent.
+ * word - Integer containing word to be sent.
+ * ncmds - Wait until ncmds or fewer cmds are left
+ * in the cmd buffer before returning.
+ *
+ *=======================================================================*/
+static void dgap_cmdw(struct channel_t *ch, u8 cmd, u16 word, uint ncmds)
+{
+ char __iomem *vaddr;
+ struct __iomem cm_t *cm_addr;
+ uint count;
+ uint n;
+ u16 head;
+ u16 tail;
+
+ if (!ch || ch->magic != DGAP_CHANNEL_MAGIC)
+ return;
+
+ /*
+ * Check if board is still alive.
+ */
+ if (ch->ch_bd->state == BOARD_FAILED)
+ return;
+
+ /*
+ * Make sure the pointers are in range before
+ * writing to the FEP memory.
+ */
+ vaddr = ch->ch_bd->re_map_membase;
+ if (!vaddr)
+ return;
+
+ cm_addr = (struct cm_t __iomem *) (vaddr + CMDBUF);
+ head = readw(&(cm_addr->cm_head));
+
+ /*
+ * Forget it if pointers out of range.
+ */
+ if (head >= (CMDMAX - CMDSTART) || (head & 03)) {
+ ch->ch_bd->state = BOARD_FAILED;
+ return;
+ }
+
+ /*
+ * Put the data in the circular command buffer.
+ */
+ writeb(cmd, (vaddr + head + CMDSTART + 0));
+ writeb((u8) ch->ch_portnum, (vaddr + head + CMDSTART + 1));
+ writew((u16) word, (vaddr + head + CMDSTART + 2));
+
+ head = (head + 4) & (CMDMAX - CMDSTART - 4);
+
+ writew(head, &(cm_addr->cm_head));
+
+ /*
+ * Wait if necessary before updating the head
+ * pointer to limit the number of outstanding
+ * commands to the FEP. If the time spent waiting
+ * is outlandish, declare the FEP dead.
+ */
+ for (count = dgap_count ;;) {
+
+ head = readw(&(cm_addr->cm_head));
+ tail = readw(&(cm_addr->cm_tail));
+
+ n = (head - tail) & (CMDMAX - CMDSTART - 4);
+
+ if (n <= ncmds * sizeof(struct cm_t))
+ break;
+
+ if (--count == 0) {
+ ch->ch_bd->state = BOARD_FAILED;
+ return;
+ }
+ udelay(10);
+ }
+}
+
+/*=======================================================================
+ *
+ * dgap_cmdw_ext - Sends a extended word command to the FEP.
+ *
+ * ch - Pointer to channel structure.
+ * cmd - Command to be sent.
+ * word - Integer containing word to be sent.
+ * ncmds - Wait until ncmds or fewer cmds are left
+ * in the cmd buffer before returning.
+ *
+ *=======================================================================*/
+static void dgap_cmdw_ext(struct channel_t *ch, u16 cmd, u16 word, uint ncmds)
+{
+ char __iomem *vaddr;
+ struct __iomem cm_t *cm_addr;
+ uint count;
+ uint n;
+ u16 head;
+ u16 tail;
+
+ if (!ch || ch->magic != DGAP_CHANNEL_MAGIC)
+ return;
+
+ /*
+ * Check if board is still alive.
+ */
+ if (ch->ch_bd->state == BOARD_FAILED)
+ return;
+
+ /*
+ * Make sure the pointers are in range before
+ * writing to the FEP memory.
+ */
+ vaddr = ch->ch_bd->re_map_membase;
+ if (!vaddr)
+ return;
+
+ cm_addr = (struct cm_t __iomem *) (vaddr + CMDBUF);
+ head = readw(&(cm_addr->cm_head));
+
+ /*
+ * Forget it if pointers out of range.
+ */
+ if (head >= (CMDMAX - CMDSTART) || (head & 03)) {
+ ch->ch_bd->state = BOARD_FAILED;
+ return;
+ }
+
+ /*
+ * Put the data in the circular command buffer.
+ */
+
+ /* Write an FF to tell the FEP that we want an extended command */
+ writeb((u8) 0xff, (vaddr + head + CMDSTART + 0));
+
+ writeb((u8) ch->ch_portnum, (vaddr + head + CMDSTART + 1));
+ writew((u16) cmd, (vaddr + head + CMDSTART + 2));
+
+ /*
+ * If the second part of the command won't fit,
+ * put it at the beginning of the circular buffer.
+ */
+ if (((head + 4) >= ((CMDMAX - CMDSTART)) || (head & 03)))
+ writew((u16) word, (vaddr + CMDSTART));
+ else
+ writew((u16) word, (vaddr + head + CMDSTART + 4));
+
+ head = (head + 8) & (CMDMAX - CMDSTART - 4);
+
+ writew(head, &(cm_addr->cm_head));
+
+ /*
+ * Wait if necessary before updating the head
+ * pointer to limit the number of outstanding
+ * commands to the FEP. If the time spent waiting
+ * is outlandish, declare the FEP dead.
+ */
+ for (count = dgap_count ;;) {
+
+ head = readw(&(cm_addr->cm_head));
+ tail = readw(&(cm_addr->cm_tail));
+
+ n = (head - tail) & (CMDMAX - CMDSTART - 4);
+
+ if (n <= ncmds * sizeof(struct cm_t))
+ break;
+
+ if (--count == 0) {
+ ch->ch_bd->state = BOARD_FAILED;
+ return;
+ }
+ udelay(10);
+ }
+}
+
+/*=======================================================================
+ *
+ * dgap_wmove - Write data to FEP buffer.
+ *
+ * ch - Pointer to channel structure.
+ * buf - Poiter to characters to be moved.
+ * cnt - Number of characters to move.
+ *
+ *=======================================================================*/
+static void dgap_wmove(struct channel_t *ch, char *buf, uint cnt)
+{
+ int n;
+ char __iomem *taddr;
+ struct bs_t __iomem *bs;
+ u16 head;
+
+ if (!ch || ch->magic != DGAP_CHANNEL_MAGIC)
+ return;
+
+ /*
+ * Check parameters.
+ */
+ bs = ch->ch_bs;
+ head = readw(&(bs->tx_head));
+
+ /*
+ * If pointers are out of range, just return.
+ */
+ if ((cnt > ch->ch_tsize) ||
+ (unsigned)(head - ch->ch_tstart) >= ch->ch_tsize)
+ return;
+
+ /*
+ * If the write wraps over the top of the circular buffer,
+ * move the portion up to the wrap point, and reset the
+ * pointers to the bottom.
+ */
+ n = ch->ch_tstart + ch->ch_tsize - head;
+
+ if (cnt >= n) {
+ cnt -= n;
+ taddr = ch->ch_taddr + head;
+ memcpy_toio(taddr, buf, n);
+ head = ch->ch_tstart;
+ buf += n;
+ }
+
+ /*
+ * Move rest of data.
+ */
+ taddr = ch->ch_taddr + head;
+ n = cnt;
+ memcpy_toio(taddr, buf, n);
+ head += cnt;
+
+ writew(head, &(bs->tx_head));
+}
+
+/*
+ * Calls the firmware to reset this channel.
+ */
+static void dgap_firmware_reset_port(struct channel_t *ch)
+{
+ dgap_cmdb(ch, CHRESET, 0, 0, 0);
+
+ /*
+ * Now that the channel is reset, we need to make sure
+ * all the current settings get reapplied to the port
+ * in the firmware.
+ *
+ * So we will set the driver's cache of firmware
+ * settings all to 0, and then call param.
+ */
+ ch->ch_fepiflag = 0;
+ ch->ch_fepcflag = 0;
+ ch->ch_fepoflag = 0;
+ ch->ch_fepstartc = 0;
+ ch->ch_fepstopc = 0;
+ ch->ch_fepastartc = 0;
+ ch->ch_fepastopc = 0;
+ ch->ch_mostat = 0;
+ ch->ch_hflow = 0;
+}
+
+/*=======================================================================
+ *
+ * dgap_param - Set Digi parameters.
+ *
+ * struct tty_struct * - TTY for port.
+ *
+ *=======================================================================*/
+static int dgap_param(struct channel_t *ch, struct board_t *bd, u32 un_type)
+{
+ u16 head;
+ u16 cflag;
+ u16 iflag;
+ u8 mval;
+ u8 hflow;
+
+ /*
+ * If baud rate is zero, flush queues, and set mval to drop DTR.
+ */
+ if ((ch->ch_c_cflag & (CBAUD)) == 0) {
+
+ /* flush rx */
+ head = readw(&(ch->ch_bs->rx_head));
+ writew(head, &(ch->ch_bs->rx_tail));
+
+ /* flush tx */
+ head = readw(&(ch->ch_bs->tx_head));
+ writew(head, &(ch->ch_bs->tx_tail));
+
+ ch->ch_flags |= (CH_BAUD0);
+
+ /* Drop RTS and DTR */
+ ch->ch_mval &= ~(D_RTS(ch)|D_DTR(ch));
+ mval = D_DTR(ch) | D_RTS(ch);
+ ch->ch_baud_info = 0;
+
+ } else if (ch->ch_custom_speed && (bd->bd_flags & BD_FEP5PLUS)) {
+ /*
+ * Tell the fep to do the command
+ */
+
+ dgap_cmdw_ext(ch, 0xff01, ch->ch_custom_speed, 0);
+
+ /*
+ * Now go get from fep mem, what the fep
+ * believes the custom baud rate is.
+ */
+ ch->ch_custom_speed = dgap_get_custom_baud(ch);
+ ch->ch_baud_info = ch->ch_custom_speed;
+
+ /* Handle transition from B0 */
+ if (ch->ch_flags & CH_BAUD0) {
+ ch->ch_flags &= ~(CH_BAUD0);
+ ch->ch_mval |= (D_RTS(ch)|D_DTR(ch));
+ }
+ mval = D_DTR(ch) | D_RTS(ch);
+
+ } else {
+ /*
+ * Set baud rate, character size, and parity.
+ */
+
+
+ int iindex = 0;
+ int jindex = 0;
+ int baud = 0;
+
+ ulong bauds[4][16] = {
+ { /* slowbaud */
+ 0, 50, 75, 110,
+ 134, 150, 200, 300,
+ 600, 1200, 1800, 2400,
+ 4800, 9600, 19200, 38400 },
+ { /* slowbaud & CBAUDEX */
+ 0, 57600, 115200, 230400,
+ 460800, 150, 200, 921600,
+ 600, 1200, 1800, 2400,
+ 4800, 9600, 19200, 38400 },
+ { /* fastbaud */
+ 0, 57600, 76800, 115200,
+ 14400, 57600, 230400, 76800,
+ 115200, 230400, 28800, 460800,
+ 921600, 9600, 19200, 38400 },
+ { /* fastbaud & CBAUDEX */
+ 0, 57600, 115200, 230400,
+ 460800, 150, 200, 921600,
+ 600, 1200, 1800, 2400,
+ 4800, 9600, 19200, 38400 }
+ };
+
+ /*
+ * Only use the TXPrint baud rate if the
+ * terminal unit is NOT open
+ */
+ if (!(ch->ch_tun.un_flags & UN_ISOPEN) &&
+ un_type == DGAP_PRINT)
+ baud = C_BAUD(ch->ch_pun.un_tty) & 0xff;
+ else
+ baud = C_BAUD(ch->ch_tun.un_tty) & 0xff;
+
+ if (ch->ch_c_cflag & CBAUDEX)
+ iindex = 1;
+
+ if (ch->ch_digi.digi_flags & DIGI_FAST)
+ iindex += 2;
+
+ jindex = baud;
+
+ if ((iindex >= 0) && (iindex < 4) &&
+ (jindex >= 0) && (jindex < 16))
+ baud = bauds[iindex][jindex];
+ else
+ baud = 0;
+
+ if (baud == 0)
+ baud = 9600;
+
+ ch->ch_baud_info = baud;
+
+ /*
+ * CBAUD has bit position 0x1000 set these days to
+ * indicate Linux baud rate remap.
+ * We use a different bit assignment for high speed.
+ * Clear this bit out while grabbing the parts of
+ * "cflag" we want.
+ */
+ cflag = ch->ch_c_cflag & ((CBAUD ^ CBAUDEX) | PARODD | PARENB |
+ CSTOPB | CSIZE);
+
+ /*
+ * HUPCL bit is used by FEP to indicate fast baud
+ * table is to be used.
+ */
+ if ((ch->ch_digi.digi_flags & DIGI_FAST) ||
+ (ch->ch_c_cflag & CBAUDEX))
+ cflag |= HUPCL;
+
+ if ((ch->ch_c_cflag & CBAUDEX) &&
+ !(ch->ch_digi.digi_flags & DIGI_FAST)) {
+ /*
+ * The below code is trying to guarantee that only
+ * baud rates 115200, 230400, 460800, 921600 are
+ * remapped. We use exclusive or because the various
+ * baud rates share common bit positions and therefore
+ * can't be tested for easily.
+ */
+ tcflag_t tcflag = (ch->ch_c_cflag & CBAUD) | CBAUDEX;
+ int baudpart = 0;
+
+ /*
+ * Map high speed requests to index
+ * into FEP's baud table
+ */
+ switch (tcflag) {
+ case B57600:
+ baudpart = 1;
+ break;
+#ifdef B76800
+ case B76800:
+ baudpart = 2;
+ break;
+#endif
+ case B115200:
+ baudpart = 3;
+ break;
+ case B230400:
+ baudpart = 9;
+ break;
+ case B460800:
+ baudpart = 11;
+ break;
+#ifdef B921600
+ case B921600:
+ baudpart = 12;
+ break;
+#endif
+ default:
+ baudpart = 0;
+ }
+
+ if (baudpart)
+ cflag = (cflag & ~(CBAUD | CBAUDEX)) | baudpart;
+ }
+
+ cflag &= 0xffff;
+
+ if (cflag != ch->ch_fepcflag) {
+ ch->ch_fepcflag = (u16) (cflag & 0xffff);
+
+ /*
+ * Okay to have channel and board
+ * locks held calling this
+ */
+ dgap_cmdw(ch, SCFLAG, (u16) cflag, 0);
+ }
+
+ /* Handle transition from B0 */
+ if (ch->ch_flags & CH_BAUD0) {
+ ch->ch_flags &= ~(CH_BAUD0);
+ ch->ch_mval |= (D_RTS(ch)|D_DTR(ch));
+ }
+ mval = D_DTR(ch) | D_RTS(ch);
+ }
+
+ /*
+ * Get input flags.
+ */
+ iflag = ch->ch_c_iflag & (IGNBRK | BRKINT | IGNPAR | PARMRK |
+ INPCK | ISTRIP | IXON | IXANY | IXOFF);
+
+ if ((ch->ch_startc == _POSIX_VDISABLE) ||
+ (ch->ch_stopc == _POSIX_VDISABLE)) {
+ iflag &= ~(IXON | IXOFF);
+ ch->ch_c_iflag &= ~(IXON | IXOFF);
+ }
+
+ /*
+ * Only the IBM Xr card can switch between
+ * 232 and 422 modes on the fly
+ */
+ if (bd->device == PCI_DEV_XR_IBM_DID) {
+ if (ch->ch_digi.digi_flags & DIGI_422)
+ dgap_cmdb(ch, SCOMMODE, MODE_422, 0, 0);
+ else
+ dgap_cmdb(ch, SCOMMODE, MODE_232, 0, 0);
+ }
+
+ if (ch->ch_digi.digi_flags & DIGI_ALTPIN)
+ iflag |= IALTPIN;
+
+ if (iflag != ch->ch_fepiflag) {
+ ch->ch_fepiflag = iflag;
+
+ /* Okay to have channel and board locks held calling this */
+ dgap_cmdw(ch, SIFLAG, (u16) ch->ch_fepiflag, 0);
+ }
+
+ /*
+ * Select hardware handshaking.
+ */
+ hflow = 0;
+
+ if (ch->ch_c_cflag & CRTSCTS)
+ hflow |= (D_RTS(ch) | D_CTS(ch));
+ if (ch->ch_digi.digi_flags & RTSPACE)
+ hflow |= D_RTS(ch);
+ if (ch->ch_digi.digi_flags & DTRPACE)
+ hflow |= D_DTR(ch);
+ if (ch->ch_digi.digi_flags & CTSPACE)
+ hflow |= D_CTS(ch);
+ if (ch->ch_digi.digi_flags & DSRPACE)
+ hflow |= D_DSR(ch);
+ if (ch->ch_digi.digi_flags & DCDPACE)
+ hflow |= D_CD(ch);
+
+ if (hflow != ch->ch_hflow) {
+ ch->ch_hflow = hflow;
+
+ /* Okay to have channel and board locks held calling this */
+ dgap_cmdb(ch, SHFLOW, (u8) hflow, 0xff, 0);
+ }
+
+ /*
+ * Set RTS and/or DTR Toggle if needed,
+ * but only if product is FEP5+ based.
+ */
+ if (bd->bd_flags & BD_FEP5PLUS) {
+ u16 hflow2 = 0;
+
+ if (ch->ch_digi.digi_flags & DIGI_RTS_TOGGLE)
+ hflow2 |= (D_RTS(ch));
+ if (ch->ch_digi.digi_flags & DIGI_DTR_TOGGLE)
+ hflow2 |= (D_DTR(ch));
+
+ dgap_cmdw_ext(ch, 0xff03, hflow2, 0);
+ }
+
+ /*
+ * Set modem control lines.
+ */
+
+ mval ^= ch->ch_mforce & (mval ^ ch->ch_mval);
+
+ if (ch->ch_mostat ^ mval) {
+ ch->ch_mostat = mval;
+
+ /* Okay to have channel and board locks held calling this */
+ dgap_cmdb(ch, SMODEM, (u8) mval, D_RTS(ch)|D_DTR(ch), 0);
+ }
+
+ /*
+ * Read modem signals, and then call carrier function.
+ */
+ ch->ch_mistat = readb(&(ch->ch_bs->m_stat));
+ dgap_carrier(ch);
+
+ /*
+ * Set the start and stop characters.
+ */
+ if (ch->ch_startc != ch->ch_fepstartc ||
+ ch->ch_stopc != ch->ch_fepstopc) {
+ ch->ch_fepstartc = ch->ch_startc;
+ ch->ch_fepstopc = ch->ch_stopc;
+
+ /* Okay to have channel and board locks held calling this */
+ dgap_cmdb(ch, SFLOWC, ch->ch_fepstartc, ch->ch_fepstopc, 0);
+ }
+
+ /*
+ * Set the Auxiliary start and stop characters.
+ */
+ if (ch->ch_astartc != ch->ch_fepastartc ||
+ ch->ch_astopc != ch->ch_fepastopc) {
+ ch->ch_fepastartc = ch->ch_astartc;
+ ch->ch_fepastopc = ch->ch_astopc;
+
+ /* Okay to have channel and board locks held calling this */
+ dgap_cmdb(ch, SAFLOWC, ch->ch_fepastartc, ch->ch_fepastopc, 0);
+ }
+
+ return 0;
+}
+
+/*
+ * dgap_block_til_ready()
+ *
+ * Wait for DCD, if needed.
+ */
+static int dgap_block_til_ready(struct tty_struct *tty, struct file *file,
+ struct channel_t *ch)
+{
+ int retval = 0;
+ struct un_t *un;
+ ulong lock_flags;
+ uint old_flags;
+ int sleep_on_un_flags;
+
+ if (!tty || tty->magic != TTY_MAGIC || !file || !ch ||
+ ch->magic != DGAP_CHANNEL_MAGIC)
+ return -EIO;
+
+ un = tty->driver_data;
+ if (!un || un->magic != DGAP_UNIT_MAGIC)
+ return -EIO;
+
+ spin_lock_irqsave(&ch->ch_lock, lock_flags);
+
+ ch->ch_wopen++;
+
+ /* Loop forever */
+ while (1) {
+
+ sleep_on_un_flags = 0;
+
+ /*
+ * If board has failed somehow during our sleep,
+ * bail with error.
+ */
+ if (ch->ch_bd->state == BOARD_FAILED) {
+ retval = -EIO;
+ break;
+ }
+
+ /* If tty was hung up, break out of loop and set error. */
+ if (tty_hung_up_p(file)) {
+ retval = -EAGAIN;
+ break;
+ }
+
+ /*
+ * If either unit is in the middle of the fragile part of close,
+ * we just cannot touch the channel safely.
+ * Go back to sleep, knowing that when the channel can be
+ * touched safely, the close routine will signal the
+ * ch_wait_flags to wake us back up.
+ */
+ if (!((ch->ch_tun.un_flags | ch->ch_pun.un_flags) &
+ UN_CLOSING)) {
+
+ /*
+ * Our conditions to leave cleanly and happily:
+ * 1) NONBLOCKING on the tty is set.
+ * 2) CLOCAL is set.
+ * 3) DCD (fake or real) is active.
+ */
+
+ if (file->f_flags & O_NONBLOCK)
+ break;
+
+ if (tty->flags & (1 << TTY_IO_ERROR))
+ break;
+
+ if (ch->ch_flags & CH_CD)
+ break;
+
+ if (ch->ch_flags & CH_FCAR)
+ break;
+ } else {
+ sleep_on_un_flags = 1;
+ }
+
+ /*
+ * If there is a signal pending, the user probably
+ * interrupted (ctrl-c) us.
+ * Leave loop with error set.
+ */
+ if (signal_pending(current)) {
+ retval = -ERESTARTSYS;
+ break;
+ }
+
+ /*
+ * Store the flags before we let go of channel lock
+ */
+ if (sleep_on_un_flags)
+ old_flags = ch->ch_tun.un_flags | ch->ch_pun.un_flags;
+ else
+ old_flags = ch->ch_flags;
+
+ /*
+ * Let go of channel lock before calling schedule.
+ * Our poller will get any FEP events and wake us up when DCD
+ * eventually goes active.
+ */
+
+ spin_unlock_irqrestore(&ch->ch_lock, lock_flags);
+
+ /*
+ * Wait for something in the flags to change
+ * from the current value.
+ */
+ if (sleep_on_un_flags) {
+ retval = wait_event_interruptible(un->un_flags_wait,
+ (old_flags != (ch->ch_tun.un_flags |
+ ch->ch_pun.un_flags)));
+ } else {
+ retval = wait_event_interruptible(ch->ch_flags_wait,
+ (old_flags != ch->ch_flags));
+ }
+
+ /*
+ * We got woken up for some reason.
+ * Before looping around, grab our channel lock.
+ */
+ spin_lock_irqsave(&ch->ch_lock, lock_flags);
+ }
+
+ ch->ch_wopen--;
+
+ spin_unlock_irqrestore(&ch->ch_lock, lock_flags);
+
+ return retval;
+}
+
+/*
+ * dgap_tty_flush_buffer()
+ *
+ * Flush Tx buffer (make in == out)
+ */
+static void dgap_tty_flush_buffer(struct tty_struct *tty)
+{
+ struct board_t *bd;
+ struct channel_t *ch;
+ struct un_t *un;
+ ulong lock_flags;
+ ulong lock_flags2;
+ u16 head;
+
+ if (!tty || tty->magic != TTY_MAGIC)
+ return;
+
+ un = tty->driver_data;
+ if (!un || un->magic != DGAP_UNIT_MAGIC)
+ return;
+
+ ch = un->un_ch;
+ if (!ch || ch->magic != DGAP_CHANNEL_MAGIC)
+ return;
+
+ bd = ch->ch_bd;
+ if (!bd || bd->magic != DGAP_BOARD_MAGIC)
+ return;
+
+ spin_lock_irqsave(&bd->bd_lock, lock_flags);
+ spin_lock_irqsave(&ch->ch_lock, lock_flags2);
+
+ ch->ch_flags &= ~CH_STOP;
+ head = readw(&(ch->ch_bs->tx_head));
+ dgap_cmdw(ch, FLUSHTX, (u16) head, 0);
+ dgap_cmdw(ch, RESUMETX, 0, 0);
+ if (ch->ch_tun.un_flags & (UN_LOW|UN_EMPTY)) {
+ ch->ch_tun.un_flags &= ~(UN_LOW|UN_EMPTY);
+ wake_up_interruptible(&ch->ch_tun.un_flags_wait);
+ }
+ if (ch->ch_pun.un_flags & (UN_LOW|UN_EMPTY)) {
+ ch->ch_pun.un_flags &= ~(UN_LOW|UN_EMPTY);
+ wake_up_interruptible(&ch->ch_pun.un_flags_wait);
+ }
+
+ spin_unlock_irqrestore(&ch->ch_lock, lock_flags2);
+ spin_unlock_irqrestore(&bd->bd_lock, lock_flags);
+ if (waitqueue_active(&tty->write_wait))
+ wake_up_interruptible(&tty->write_wait);
+ tty_wakeup(tty);
+}
+
+/*
+ * dgap_tty_hangup()
+ *
+ * Hangup the port. Like a close, but don't wait for output to drain.
+ */
+static void dgap_tty_hangup(struct tty_struct *tty)
+{
+ struct board_t *bd;
+ struct channel_t *ch;
+ struct un_t *un;
+
+ if (!tty || tty->magic != TTY_MAGIC)
+ return;
+
+ un = tty->driver_data;
+ if (!un || un->magic != DGAP_UNIT_MAGIC)
+ return;
+
+ ch = un->un_ch;
+ if (!ch || ch->magic != DGAP_CHANNEL_MAGIC)
+ return;
+
+ bd = ch->ch_bd;
+ if (!bd || bd->magic != DGAP_BOARD_MAGIC)
+ return;
+
+ /* flush the transmit queues */
+ dgap_tty_flush_buffer(tty);
+}
+
+/*
+ * dgap_tty_chars_in_buffer()
+ *
+ * Return number of characters that have not been transmitted yet.
+ *
+ * This routine is used by the line discipline to determine if there
+ * is data waiting to be transmitted/drained/flushed or not.
+ */
+static int dgap_tty_chars_in_buffer(struct tty_struct *tty)
+{
+ struct board_t *bd;
+ struct channel_t *ch;
+ struct un_t *un;
+ struct bs_t __iomem *bs;
+ u8 tbusy;
+ uint chars;
+ u16 thead, ttail, tmask, chead, ctail;
+ ulong lock_flags = 0;
+ ulong lock_flags2 = 0;
+
+ if (!tty)
+ return 0;
+
+ un = tty->driver_data;
+ if (!un || un->magic != DGAP_UNIT_MAGIC)
+ return 0;
+
+ ch = un->un_ch;
+ if (!ch || ch->magic != DGAP_CHANNEL_MAGIC)
+ return 0;
+
+ bd = ch->ch_bd;
+ if (!bd || bd->magic != DGAP_BOARD_MAGIC)
+ return 0;
+
+ bs = ch->ch_bs;
+ if (!bs)
+ return 0;
+
+ spin_lock_irqsave(&bd->bd_lock, lock_flags);
+ spin_lock_irqsave(&ch->ch_lock, lock_flags2);
+
+ tmask = (ch->ch_tsize - 1);
+
+ /* Get Transmit queue pointers */
+ thead = readw(&(bs->tx_head)) & tmask;
+ ttail = readw(&(bs->tx_tail)) & tmask;
+
+ /* Get tbusy flag */
+ tbusy = readb(&(bs->tbusy));
+
+ /* Get Command queue pointers */
+ chead = readw(&(ch->ch_cm->cm_head));
+ ctail = readw(&(ch->ch_cm->cm_tail));
+
+ spin_unlock_irqrestore(&ch->ch_lock, lock_flags2);
+ spin_unlock_irqrestore(&bd->bd_lock, lock_flags);
+
+ /*
+ * The only way we know for sure if there is no pending
+ * data left to be transferred, is if:
+ * 1) Transmit head and tail are equal (empty).
+ * 2) Command queue head and tail are equal (empty).
+ * 3) The "TBUSY" flag is 0. (Transmitter not busy).
+ */
+
+ if ((ttail == thead) && (tbusy == 0) && (chead == ctail)) {
+ chars = 0;
+ } else {
+ if (thead >= ttail)
+ chars = thead - ttail;
+ else
+ chars = thead - ttail + ch->ch_tsize;
+ /*
+ * Fudge factor here.
+ * If chars is zero, we know that the command queue had
+ * something in it or tbusy was set. Because we cannot
+ * be sure if there is still some data to be transmitted,
+ * lets lie, and tell ld we have 1 byte left.
+ */
+ if (chars == 0) {
+ /*
+ * If TBUSY is still set, and our tx buffers are empty,
+ * force the firmware to send me another wakeup after
+ * TBUSY has been cleared.
+ */
+ if (tbusy != 0) {
+ spin_lock_irqsave(&ch->ch_lock, lock_flags);
+ un->un_flags |= UN_EMPTY;
+ writeb(1, &(bs->iempty));
+ spin_unlock_irqrestore(&ch->ch_lock,
+ lock_flags);
+ }
+ chars = 1;
+ }
+ }
+
+ return chars;
+}
+
+static int dgap_wait_for_drain(struct tty_struct *tty)
+{
+ struct channel_t *ch;
+ struct un_t *un;
+ struct bs_t __iomem *bs;
+ int ret = 0;
+ uint count = 1;
+ ulong lock_flags = 0;
+
+ if (!tty || tty->magic != TTY_MAGIC)
+ return -EIO;
+
+ un = tty->driver_data;
+ if (!un || un->magic != DGAP_UNIT_MAGIC)
+ return -EIO;
+
+ ch = un->un_ch;
+ if (!ch || ch->magic != DGAP_CHANNEL_MAGIC)
+ return -EIO;
+
+ bs = ch->ch_bs;
+ if (!bs)
+ return -EIO;
+
+ /* Loop until data is drained */
+ while (count != 0) {
+
+ count = dgap_tty_chars_in_buffer(tty);
+
+ if (count == 0)
+ break;
+
+ /* Set flag waiting for drain */
+ spin_lock_irqsave(&ch->ch_lock, lock_flags);
+ un->un_flags |= UN_EMPTY;
+ writeb(1, &(bs->iempty));
+ spin_unlock_irqrestore(&ch->ch_lock, lock_flags);
+
+ /* Go to sleep till we get woken up */
+ ret = wait_event_interruptible(un->un_flags_wait,
+ ((un->un_flags & UN_EMPTY) == 0));
+ /* If ret is non-zero, user ctrl-c'ed us */
+ if (ret)
+ break;
+ }
+
+ spin_lock_irqsave(&ch->ch_lock, lock_flags);
+ un->un_flags &= ~(UN_EMPTY);
+ spin_unlock_irqrestore(&ch->ch_lock, lock_flags);
+
+ return ret;
+}
+
+/*
+ * dgap_maxcps_room
+ *
+ * Reduces bytes_available to the max number of characters
+ * that can be sent currently given the maxcps value, and
+ * returns the new bytes_available. This only affects printer
+ * output.
+ */
+static int dgap_maxcps_room(struct channel_t *ch, struct un_t *un,
+ int bytes_available)
+{
+ /*
+ * If its not the Transparent print device, return
+ * the full data amount.
+ */
+ if (un->un_type != DGAP_PRINT)
+ return bytes_available;
+
+ if (ch->ch_digi.digi_maxcps > 0 && ch->ch_digi.digi_bufsize > 0) {
+ int cps_limit = 0;
+ unsigned long current_time = jiffies;
+ unsigned long buffer_time = current_time +
+ (HZ * ch->ch_digi.digi_bufsize) /
+ ch->ch_digi.digi_maxcps;
+
+ if (ch->ch_cpstime < current_time) {
+ /* buffer is empty */
+ ch->ch_cpstime = current_time; /* reset ch_cpstime */
+ cps_limit = ch->ch_digi.digi_bufsize;
+ } else if (ch->ch_cpstime < buffer_time) {
+ /* still room in the buffer */
+ cps_limit = ((buffer_time - ch->ch_cpstime) *
+ ch->ch_digi.digi_maxcps) / HZ;
+ } else {
+ /* no room in the buffer */
+ cps_limit = 0;
+ }
+
+ bytes_available = min(cps_limit, bytes_available);
+ }
+
+ return bytes_available;
+}
+
+static inline void dgap_set_firmware_event(struct un_t *un, unsigned int event)
+{
+ struct channel_t *ch;
+ struct bs_t __iomem *bs;
+
+ if (!un || un->magic != DGAP_UNIT_MAGIC)
+ return;
+ ch = un->un_ch;
+ if (!ch || ch->magic != DGAP_CHANNEL_MAGIC)
+ return;
+ bs = ch->ch_bs;
+ if (!bs)
+ return;
+
+ if ((event & UN_LOW) != 0) {
+ if ((un->un_flags & UN_LOW) == 0) {
+ un->un_flags |= UN_LOW;
+ writeb(1, &(bs->ilow));
+ }
+ }
+ if ((event & UN_LOW) != 0) {
+ if ((un->un_flags & UN_EMPTY) == 0) {
+ un->un_flags |= UN_EMPTY;
+ writeb(1, &(bs->iempty));
+ }
+ }
+}
+
+/*
+ * dgap_tty_write_room()
+ *
+ * Return space available in Tx buffer
+ */
+static int dgap_tty_write_room(struct tty_struct *tty)
+{
+ struct channel_t *ch;
+ struct un_t *un;
+ struct bs_t __iomem *bs;
+ u16 head, tail, tmask;
+ int ret;
+ ulong lock_flags = 0;
+
+ if (!tty)
+ return 0;
+
+ un = tty->driver_data;
+ if (!un || un->magic != DGAP_UNIT_MAGIC)
+ return 0;
+
+ ch = un->un_ch;
+ if (!ch || ch->magic != DGAP_CHANNEL_MAGIC)
+ return 0;
+
+ bs = ch->ch_bs;
+ if (!bs)
+ return 0;
+
+ spin_lock_irqsave(&ch->ch_lock, lock_flags);
+
+ tmask = ch->ch_tsize - 1;
+ head = readw(&(bs->tx_head)) & tmask;
+ tail = readw(&(bs->tx_tail)) & tmask;
+
+ ret = tail - head - 1;
+ if (ret < 0)
+ ret += ch->ch_tsize;
+
+ /* Limit printer to maxcps */
+ ret = dgap_maxcps_room(ch, un, ret);
+
+ /*
+ * If we are printer device, leave space for
+ * possibly both the on and off strings.
+ */
+ if (un->un_type == DGAP_PRINT) {
+ if (!(ch->ch_flags & CH_PRON))
+ ret -= ch->ch_digi.digi_onlen;
+ ret -= ch->ch_digi.digi_offlen;
+ } else {
+ if (ch->ch_flags & CH_PRON)
+ ret -= ch->ch_digi.digi_offlen;
+ }
+
+ if (ret < 0)
+ ret = 0;
+
+ /*
+ * Schedule FEP to wake us up if needed.
+ *
+ * TODO: This might be overkill...
+ * Do we really need to schedule callbacks from the FEP
+ * in every case? Can we get smarter based on ret?
+ */
+ dgap_set_firmware_event(un, UN_LOW | UN_EMPTY);
+ spin_unlock_irqrestore(&ch->ch_lock, lock_flags);
+
+ return ret;
+}
+
+/*
+ * dgap_tty_write()
+ *
+ * Take data from the user or kernel and send it out to the FEP.
+ * In here exists all the Transparent Print magic as well.
+ */
+static int dgap_tty_write(struct tty_struct *tty, const unsigned char *buf,
+ int count)
+{
+ struct channel_t *ch;
+ struct un_t *un;
+ struct bs_t __iomem *bs;
+ char __iomem *vaddr;
+ u16 head, tail, tmask, remain;
+ int bufcount, n;
+ ulong lock_flags;
+
+ if (!tty)
+ return 0;
+
+ un = tty->driver_data;
+ if (!un || un->magic != DGAP_UNIT_MAGIC)
+ return 0;
+
+ ch = un->un_ch;
+ if (!ch || ch->magic != DGAP_CHANNEL_MAGIC)
+ return 0;
+
+ bs = ch->ch_bs;
+ if (!bs)
+ return 0;
+
+ if (!count)
+ return 0;
+
+ spin_lock_irqsave(&ch->ch_lock, lock_flags);
+
+ /* Get our space available for the channel from the board */
+ tmask = ch->ch_tsize - 1;
+ head = readw(&(bs->tx_head)) & tmask;
+ tail = readw(&(bs->tx_tail)) & tmask;
+
+ bufcount = tail - head - 1;
+ if (bufcount < 0)
+ bufcount += ch->ch_tsize;
+
+ /*
+ * Limit printer output to maxcps overall, with bursts allowed
+ * up to bufsize characters.
+ */
+ bufcount = dgap_maxcps_room(ch, un, bufcount);
+
+ /*
+ * Take minimum of what the user wants to send, and the
+ * space available in the FEP buffer.
+ */
+ count = min(count, bufcount);
+
+ /*
+ * Bail if no space left.
+ */
+ if (count <= 0) {
+ dgap_set_firmware_event(un, UN_LOW | UN_EMPTY);
+ spin_unlock_irqrestore(&ch->ch_lock, lock_flags);
+ return 0;
+ }
+
+ /*
+ * Output the printer ON string, if we are in terminal mode, but
+ * need to be in printer mode.
+ */
+ if ((un->un_type == DGAP_PRINT) && !(ch->ch_flags & CH_PRON)) {
+ dgap_wmove(ch, ch->ch_digi.digi_onstr,
+ (int) ch->ch_digi.digi_onlen);
+ head = readw(&(bs->tx_head)) & tmask;
+ ch->ch_flags |= CH_PRON;
+ }
+
+ /*
+ * On the other hand, output the printer OFF string, if we are
+ * currently in printer mode, but need to output to the terminal.
+ */
+ if ((un->un_type != DGAP_PRINT) && (ch->ch_flags & CH_PRON)) {
+ dgap_wmove(ch, ch->ch_digi.digi_offstr,
+ (int) ch->ch_digi.digi_offlen);
+ head = readw(&(bs->tx_head)) & tmask;
+ ch->ch_flags &= ~CH_PRON;
+ }
+
+ n = count;
+
+ /*
+ * If the write wraps over the top of the circular buffer,
+ * move the portion up to the wrap point, and reset the
+ * pointers to the bottom.
+ */
+ remain = ch->ch_tstart + ch->ch_tsize - head;
+
+ if (n >= remain) {
+ n -= remain;
+ vaddr = ch->ch_taddr + head;
+
+ memcpy_toio(vaddr, (u8 *) buf, remain);
+
+ head = ch->ch_tstart;
+ buf += remain;
+ }
+
+ if (n > 0) {
+
+ /*
+ * Move rest of data.
+ */
+ vaddr = ch->ch_taddr + head;
+ remain = n;
+
+ memcpy_toio(vaddr, (u8 *) buf, remain);
+ head += remain;
+
+ }
+
+ if (count) {
+ ch->ch_txcount += count;
+ head &= tmask;
+ writew(head, &(bs->tx_head));
+ }
+
+ dgap_set_firmware_event(un, UN_LOW | UN_EMPTY);
+
+ /*
+ * If this is the print device, and the
+ * printer is still on, we need to turn it
+ * off before going idle. If the buffer is
+ * non-empty, wait until it goes empty.
+ * Otherwise turn it off right now.
+ */
+ if ((un->un_type == DGAP_PRINT) && (ch->ch_flags & CH_PRON)) {
+ tail = readw(&(bs->tx_tail)) & tmask;
+
+ if (tail != head) {
+ un->un_flags |= UN_EMPTY;
+ writeb(1, &(bs->iempty));
+ } else {
+ dgap_wmove(ch, ch->ch_digi.digi_offstr,
+ (int) ch->ch_digi.digi_offlen);
+ head = readw(&(bs->tx_head)) & tmask;
+ ch->ch_flags &= ~CH_PRON;
+ }
+ }
+
+ /* Update printer buffer empty time. */
+ if ((un->un_type == DGAP_PRINT) && (ch->ch_digi.digi_maxcps > 0)
+ && (ch->ch_digi.digi_bufsize > 0)) {
+ ch->ch_cpstime += (HZ * count) / ch->ch_digi.digi_maxcps;
+ }
+
+ spin_unlock_irqrestore(&ch->ch_lock, lock_flags);
+
+ return count;
+}
+
+/*
+ * dgap_tty_put_char()
+ *
+ * Put a character into ch->ch_buf
+ *
+ * - used by the line discipline for OPOST processing
+ */
+static int dgap_tty_put_char(struct tty_struct *tty, unsigned char c)
+{
+ /*
+ * Simply call tty_write.
+ */
+ dgap_tty_write(tty, &c, 1);
+ return 1;
+}
+
+/*
+ * Return modem signals to ld.
+ */
+static int dgap_tty_tiocmget(struct tty_struct *tty)
+{
+ struct channel_t *ch;
+ struct un_t *un;
+ int result;
+ u8 mstat;
+ ulong lock_flags;
+
+ if (!tty || tty->magic != TTY_MAGIC)
+ return -EIO;
+
+ un = tty->driver_data;
+ if (!un || un->magic != DGAP_UNIT_MAGIC)
+ return -EIO;
+
+ ch = un->un_ch;
+ if (!ch || ch->magic != DGAP_CHANNEL_MAGIC)
+ return -EIO;
+
+ spin_lock_irqsave(&ch->ch_lock, lock_flags);
+
+ mstat = readb(&(ch->ch_bs->m_stat));
+ /* Append any outbound signals that might be pending... */
+ mstat |= ch->ch_mostat;
+
+ spin_unlock_irqrestore(&ch->ch_lock, lock_flags);
+
+ result = 0;
+
+ if (mstat & D_DTR(ch))
+ result |= TIOCM_DTR;
+ if (mstat & D_RTS(ch))
+ result |= TIOCM_RTS;
+ if (mstat & D_CTS(ch))
+ result |= TIOCM_CTS;
+ if (mstat & D_DSR(ch))
+ result |= TIOCM_DSR;
+ if (mstat & D_RI(ch))
+ result |= TIOCM_RI;
+ if (mstat & D_CD(ch))
+ result |= TIOCM_CD;
+
+ return result;
+}
+
+/*
+ * dgap_tty_tiocmset()
+ *
+ * Set modem signals, called by ld.
+ */
+static int dgap_tty_tiocmset(struct tty_struct *tty,
+ unsigned int set, unsigned int clear)
+{
+ struct board_t *bd;
+ struct channel_t *ch;
+ struct un_t *un;
+ ulong lock_flags;
+ ulong lock_flags2;
+
+ if (!tty || tty->magic != TTY_MAGIC)
+ return -EIO;
+
+ un = tty->driver_data;
+ if (!un || un->magic != DGAP_UNIT_MAGIC)
+ return -EIO;
+
+ ch = un->un_ch;
+ if (!ch || ch->magic != DGAP_CHANNEL_MAGIC)
+ return -EIO;
+
+ bd = ch->ch_bd;
+ if (!bd || bd->magic != DGAP_BOARD_MAGIC)
+ return -EIO;
+
+ spin_lock_irqsave(&bd->bd_lock, lock_flags);
+ spin_lock_irqsave(&ch->ch_lock, lock_flags2);
+
+ if (set & TIOCM_RTS) {
+ ch->ch_mforce |= D_RTS(ch);
+ ch->ch_mval |= D_RTS(ch);
+ }
+
+ if (set & TIOCM_DTR) {
+ ch->ch_mforce |= D_DTR(ch);
+ ch->ch_mval |= D_DTR(ch);
+ }
+
+ if (clear & TIOCM_RTS) {
+ ch->ch_mforce |= D_RTS(ch);
+ ch->ch_mval &= ~(D_RTS(ch));
+ }
+
+ if (clear & TIOCM_DTR) {
+ ch->ch_mforce |= D_DTR(ch);
+ ch->ch_mval &= ~(D_DTR(ch));
+ }
+
+ dgap_param(ch, bd, un->un_type);
+
+ spin_unlock_irqrestore(&ch->ch_lock, lock_flags2);
+ spin_unlock_irqrestore(&bd->bd_lock, lock_flags);
+
+ return 0;
+}
+
+/*
+ * dgap_tty_send_break()
+ *
+ * Send a Break, called by ld.
+ */
+static int dgap_tty_send_break(struct tty_struct *tty, int msec)
+{
+ struct board_t *bd;
+ struct channel_t *ch;
+ struct un_t *un;
+ ulong lock_flags;
+ ulong lock_flags2;
+
+ if (!tty || tty->magic != TTY_MAGIC)
+ return -EIO;
+
+ un = tty->driver_data;
+ if (!un || un->magic != DGAP_UNIT_MAGIC)
+ return -EIO;
+
+ ch = un->un_ch;
+ if (!ch || ch->magic != DGAP_CHANNEL_MAGIC)
+ return -EIO;
+
+ bd = ch->ch_bd;
+ if (!bd || bd->magic != DGAP_BOARD_MAGIC)
+ return -EIO;
+
+ switch (msec) {
+ case -1:
+ msec = 0xFFFF;
+ break;
+ case 0:
+ msec = 1;
+ break;
+ default:
+ msec /= 10;
+ break;
+ }
+
+ spin_lock_irqsave(&bd->bd_lock, lock_flags);
+ spin_lock_irqsave(&ch->ch_lock, lock_flags2);
+#if 0
+ dgap_cmdw(ch, SBREAK, (u16) SBREAK_TIME, 0);
+#endif
+ dgap_cmdw(ch, SBREAK, (u16) msec, 0);
+
+ spin_unlock_irqrestore(&ch->ch_lock, lock_flags2);
+ spin_unlock_irqrestore(&bd->bd_lock, lock_flags);
+
+ return 0;
+}
+
+/*
+ * dgap_tty_wait_until_sent()
+ *
+ * wait until data has been transmitted, called by ld.
+ */
+static void dgap_tty_wait_until_sent(struct tty_struct *tty, int timeout)
+{
+ dgap_wait_for_drain(tty);
+}
+
+/*
+ * dgap_send_xchar()
+ *
+ * send a high priority character, called by ld.
+ */
+static void dgap_tty_send_xchar(struct tty_struct *tty, char c)
+{
+ struct board_t *bd;
+ struct channel_t *ch;
+ struct un_t *un;
+ ulong lock_flags;
+ ulong lock_flags2;
+
+ if (!tty || tty->magic != TTY_MAGIC)
+ return;
+
+ un = tty->driver_data;
+ if (!un || un->magic != DGAP_UNIT_MAGIC)
+ return;
+
+ ch = un->un_ch;
+ if (!ch || ch->magic != DGAP_CHANNEL_MAGIC)
+ return;
+
+ bd = ch->ch_bd;
+ if (!bd || bd->magic != DGAP_BOARD_MAGIC)
+ return;
+
+ spin_lock_irqsave(&bd->bd_lock, lock_flags);
+ spin_lock_irqsave(&ch->ch_lock, lock_flags2);
+
+ /*
+ * This is technically what we should do.
+ * However, the NIST tests specifically want
+ * to see each XON or XOFF character that it
+ * sends, so lets just send each character
+ * by hand...
+ */
+#if 0
+ if (c == STOP_CHAR(tty))
+ dgap_cmdw(ch, RPAUSE, 0, 0);
+ else if (c == START_CHAR(tty))
+ dgap_cmdw(ch, RRESUME, 0, 0);
+ else
+ dgap_wmove(ch, &c, 1);
+#else
+ dgap_wmove(ch, &c, 1);
+#endif
+
+ spin_unlock_irqrestore(&ch->ch_lock, lock_flags2);
+ spin_unlock_irqrestore(&bd->bd_lock, lock_flags);
+}
+
+/*
+ * Return modem signals to ld.
+ */
+static int dgap_get_modem_info(struct channel_t *ch, unsigned int __user *value)
+{
+ int result;
+ u8 mstat;
+ ulong lock_flags;
+
+ spin_lock_irqsave(&ch->ch_lock, lock_flags);
+
+ mstat = readb(&(ch->ch_bs->m_stat));
+ /* Append any outbound signals that might be pending... */
+ mstat |= ch->ch_mostat;
+
+ spin_unlock_irqrestore(&ch->ch_lock, lock_flags);
+
+ result = 0;
+
+ if (mstat & D_DTR(ch))
+ result |= TIOCM_DTR;
+ if (mstat & D_RTS(ch))
+ result |= TIOCM_RTS;
+ if (mstat & D_CTS(ch))
+ result |= TIOCM_CTS;
+ if (mstat & D_DSR(ch))
+ result |= TIOCM_DSR;
+ if (mstat & D_RI(ch))
+ result |= TIOCM_RI;
+ if (mstat & D_CD(ch))
+ result |= TIOCM_CD;
+
+ return put_user(result, value);
+}
+
+/*
+ * dgap_set_modem_info()
+ *
+ * Set modem signals, called by ld.
+ */
+static int dgap_set_modem_info(struct channel_t *ch, struct board_t *bd,
+ struct un_t *un, unsigned int command,
+ unsigned int __user *value)
+{
+ int ret;
+ unsigned int arg;
+ ulong lock_flags;
+ ulong lock_flags2;
+
+ ret = get_user(arg, value);
+ if (ret)
+ return ret;
+
+ switch (command) {
+ case TIOCMBIS:
+ if (arg & TIOCM_RTS) {
+ ch->ch_mforce |= D_RTS(ch);
+ ch->ch_mval |= D_RTS(ch);
+ }
+
+ if (arg & TIOCM_DTR) {
+ ch->ch_mforce |= D_DTR(ch);
+ ch->ch_mval |= D_DTR(ch);
+ }
+
+ break;
+
+ case TIOCMBIC:
+ if (arg & TIOCM_RTS) {
+ ch->ch_mforce |= D_RTS(ch);
+ ch->ch_mval &= ~(D_RTS(ch));
+ }
+
+ if (arg & TIOCM_DTR) {
+ ch->ch_mforce |= D_DTR(ch);
+ ch->ch_mval &= ~(D_DTR(ch));
+ }
+
+ break;
+
+ case TIOCMSET:
+ ch->ch_mforce = D_DTR(ch)|D_RTS(ch);
+
+ if (arg & TIOCM_RTS)
+ ch->ch_mval |= D_RTS(ch);
+ else
+ ch->ch_mval &= ~(D_RTS(ch));
+
+ if (arg & TIOCM_DTR)
+ ch->ch_mval |= (D_DTR(ch));
+ else
+ ch->ch_mval &= ~(D_DTR(ch));
+
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ spin_lock_irqsave(&bd->bd_lock, lock_flags);
+ spin_lock_irqsave(&ch->ch_lock, lock_flags2);
+
+ dgap_param(ch, bd, un->un_type);
+
+ spin_unlock_irqrestore(&ch->ch_lock, lock_flags2);
+ spin_unlock_irqrestore(&bd->bd_lock, lock_flags);
+
+ return 0;
+}
+
+/*
+ * dgap_tty_digigeta()
+ *
+ * Ioctl to get the information for ditty.
+ *
+ *
+ *
+ */
+static int dgap_tty_digigeta(struct channel_t *ch,
+ struct digi_t __user *retinfo)
+{
+ struct digi_t tmp;
+ ulong lock_flags;
+
+ if (!retinfo)
+ return -EFAULT;
+
+ memset(&tmp, 0, sizeof(tmp));
+
+ spin_lock_irqsave(&ch->ch_lock, lock_flags);
+ memcpy(&tmp, &ch->ch_digi, sizeof(tmp));
+ spin_unlock_irqrestore(&ch->ch_lock, lock_flags);
+
+ if (copy_to_user(retinfo, &tmp, sizeof(*retinfo)))
+ return -EFAULT;
+
+ return 0;
+}
+
+/*
+ * dgap_tty_digiseta()
+ *
+ * Ioctl to set the information for ditty.
+ *
+ *
+ *
+ */
+static int dgap_tty_digiseta(struct channel_t *ch, struct board_t *bd,
+ struct un_t *un, struct digi_t __user *new_info)
+{
+ struct digi_t new_digi;
+ ulong lock_flags = 0;
+ unsigned long lock_flags2;
+
+ if (copy_from_user(&new_digi, new_info, sizeof(struct digi_t)))
+ return -EFAULT;
+
+ spin_lock_irqsave(&bd->bd_lock, lock_flags);
+ spin_lock_irqsave(&ch->ch_lock, lock_flags2);
+
+ memcpy(&ch->ch_digi, &new_digi, sizeof(struct digi_t));
+
+ if (ch->ch_digi.digi_maxcps < 1)
+ ch->ch_digi.digi_maxcps = 1;
+
+ if (ch->ch_digi.digi_maxcps > 10000)
+ ch->ch_digi.digi_maxcps = 10000;
+
+ if (ch->ch_digi.digi_bufsize < 10)
+ ch->ch_digi.digi_bufsize = 10;
+
+ if (ch->ch_digi.digi_maxchar < 1)
+ ch->ch_digi.digi_maxchar = 1;
+
+ if (ch->ch_digi.digi_maxchar > ch->ch_digi.digi_bufsize)
+ ch->ch_digi.digi_maxchar = ch->ch_digi.digi_bufsize;
+
+ if (ch->ch_digi.digi_onlen > DIGI_PLEN)
+ ch->ch_digi.digi_onlen = DIGI_PLEN;
+
+ if (ch->ch_digi.digi_offlen > DIGI_PLEN)
+ ch->ch_digi.digi_offlen = DIGI_PLEN;
+
+ dgap_param(ch, bd, un->un_type);
+
+ spin_unlock_irqrestore(&ch->ch_lock, lock_flags2);
+ spin_unlock_irqrestore(&bd->bd_lock, lock_flags);
+
+ return 0;
+}
+
+/*
+ * dgap_tty_digigetedelay()
+ *
+ * Ioctl to get the current edelay setting.
+ *
+ *
+ *
+ */
+static int dgap_tty_digigetedelay(struct tty_struct *tty, int __user *retinfo)
+{
+ struct channel_t *ch;
+ struct un_t *un;
+ int tmp;
+ ulong lock_flags;
+
+ if (!retinfo)
+ return -EFAULT;
+
+ if (!tty || tty->magic != TTY_MAGIC)
+ return -EFAULT;
+
+ un = tty->driver_data;
+ if (!un || un->magic != DGAP_UNIT_MAGIC)
+ return -EFAULT;
+
+ ch = un->un_ch;
+ if (!ch || ch->magic != DGAP_CHANNEL_MAGIC)
+ return -EFAULT;
+
+ memset(&tmp, 0, sizeof(tmp));
+
+ spin_lock_irqsave(&ch->ch_lock, lock_flags);
+ tmp = readw(&(ch->ch_bs->edelay));
+ spin_unlock_irqrestore(&ch->ch_lock, lock_flags);
+
+ if (copy_to_user(retinfo, &tmp, sizeof(*retinfo)))
+ return -EFAULT;
+
+ return 0;
+}
+
+/*
+ * dgap_tty_digisetedelay()
+ *
+ * Ioctl to set the EDELAY setting
+ *
+ */
+static int dgap_tty_digisetedelay(struct channel_t *ch, struct board_t *bd,
+ struct un_t *un, int __user *new_info)
+{
+ int new_digi;
+ ulong lock_flags;
+ ulong lock_flags2;
+
+ if (copy_from_user(&new_digi, new_info, sizeof(int)))
+ return -EFAULT;
+
+ spin_lock_irqsave(&bd->bd_lock, lock_flags);
+ spin_lock_irqsave(&ch->ch_lock, lock_flags2);
+
+ writew((u16) new_digi, &(ch->ch_bs->edelay));
+
+ dgap_param(ch, bd, un->un_type);
+
+ spin_unlock_irqrestore(&ch->ch_lock, lock_flags2);
+ spin_unlock_irqrestore(&bd->bd_lock, lock_flags);
+
+ return 0;
+}
+
+/*
+ * dgap_tty_digigetcustombaud()
+ *
+ * Ioctl to get the current custom baud rate setting.
+ */
+static int dgap_tty_digigetcustombaud(struct channel_t *ch, struct un_t *un,
+ int __user *retinfo)
+{
+ int tmp;
+ ulong lock_flags;
+
+ if (!retinfo)
+ return -EFAULT;
+
+ memset(&tmp, 0, sizeof(tmp));
+
+ spin_lock_irqsave(&ch->ch_lock, lock_flags);
+ tmp = dgap_get_custom_baud(ch);
+ spin_unlock_irqrestore(&ch->ch_lock, lock_flags);
+
+ if (copy_to_user(retinfo, &tmp, sizeof(*retinfo)))
+ return -EFAULT;
+
+ return 0;
+}
+
+/*
+ * dgap_tty_digisetcustombaud()
+ *
+ * Ioctl to set the custom baud rate setting
+ */
+static int dgap_tty_digisetcustombaud(struct channel_t *ch, struct board_t *bd,
+ struct un_t *un, int __user *new_info)
+{
+ uint new_rate;
+ ulong lock_flags;
+ ulong lock_flags2;
+
+ if (copy_from_user(&new_rate, new_info, sizeof(unsigned int)))
+ return -EFAULT;
+
+ if (bd->bd_flags & BD_FEP5PLUS) {
+
+ spin_lock_irqsave(&bd->bd_lock, lock_flags);
+ spin_lock_irqsave(&ch->ch_lock, lock_flags2);
+
+ ch->ch_custom_speed = new_rate;
+
+ dgap_param(ch, bd, un->un_type);
+
+ spin_unlock_irqrestore(&ch->ch_lock, lock_flags2);
+ spin_unlock_irqrestore(&bd->bd_lock, lock_flags);
+ }
+
+ return 0;
+}
+
+/*
+ * dgap_set_termios()
+ */
+static void dgap_tty_set_termios(struct tty_struct *tty,
+ struct ktermios *old_termios)
+{
+ struct board_t *bd;
+ struct channel_t *ch;
+ struct un_t *un;
+ unsigned long lock_flags;
+ unsigned long lock_flags2;
+
+ if (!tty || tty->magic != TTY_MAGIC)
+ return;
+
+ un = tty->driver_data;
+ if (!un || un->magic != DGAP_UNIT_MAGIC)
+ return;
+
+ ch = un->un_ch;
+ if (!ch || ch->magic != DGAP_CHANNEL_MAGIC)
+ return;
+
+ bd = ch->ch_bd;
+ if (!bd || bd->magic != DGAP_BOARD_MAGIC)
+ return;
+
+ spin_lock_irqsave(&bd->bd_lock, lock_flags);
+ spin_lock_irqsave(&ch->ch_lock, lock_flags2);
+
+ ch->ch_c_cflag = tty->termios.c_cflag;
+ ch->ch_c_iflag = tty->termios.c_iflag;
+ ch->ch_c_oflag = tty->termios.c_oflag;
+ ch->ch_c_lflag = tty->termios.c_lflag;
+ ch->ch_startc = tty->termios.c_cc[VSTART];
+ ch->ch_stopc = tty->termios.c_cc[VSTOP];
+
+ dgap_carrier(ch);
+ dgap_param(ch, bd, un->un_type);
+
+ spin_unlock_irqrestore(&ch->ch_lock, lock_flags2);
+ spin_unlock_irqrestore(&bd->bd_lock, lock_flags);
+}
+
+static void dgap_tty_throttle(struct tty_struct *tty)
+{
+ struct board_t *bd;
+ struct channel_t *ch;
+ struct un_t *un;
+ ulong lock_flags;
+ ulong lock_flags2;
+
+ if (!tty || tty->magic != TTY_MAGIC)
+ return;
+
+ un = tty->driver_data;
+ if (!un || un->magic != DGAP_UNIT_MAGIC)
+ return;
+
+ ch = un->un_ch;
+ if (!ch || ch->magic != DGAP_CHANNEL_MAGIC)
+ return;
+
+ bd = ch->ch_bd;
+ if (!bd || bd->magic != DGAP_BOARD_MAGIC)
+ return;
+
+ spin_lock_irqsave(&bd->bd_lock, lock_flags);
+ spin_lock_irqsave(&ch->ch_lock, lock_flags2);
+
+ ch->ch_flags |= (CH_RXBLOCK);
+#if 1
+ dgap_cmdw(ch, RPAUSE, 0, 0);
+#endif
+
+ spin_unlock_irqrestore(&ch->ch_lock, lock_flags2);
+ spin_unlock_irqrestore(&bd->bd_lock, lock_flags);
+
+}
+
+static void dgap_tty_unthrottle(struct tty_struct *tty)
+{
+ struct board_t *bd;
+ struct channel_t *ch;
+ struct un_t *un;
+ ulong lock_flags;
+ ulong lock_flags2;
+
+ if (!tty || tty->magic != TTY_MAGIC)
+ return;
+
+ un = tty->driver_data;
+ if (!un || un->magic != DGAP_UNIT_MAGIC)
+ return;
+
+ ch = un->un_ch;
+ if (!ch || ch->magic != DGAP_CHANNEL_MAGIC)
+ return;
+
+ bd = ch->ch_bd;
+ if (!bd || bd->magic != DGAP_BOARD_MAGIC)
+ return;
+
+ spin_lock_irqsave(&bd->bd_lock, lock_flags);
+ spin_lock_irqsave(&ch->ch_lock, lock_flags2);
+
+ ch->ch_flags &= ~(CH_RXBLOCK);
+
+#if 1
+ dgap_cmdw(ch, RRESUME, 0, 0);
+#endif
+
+ spin_unlock_irqrestore(&ch->ch_lock, lock_flags2);
+ spin_unlock_irqrestore(&bd->bd_lock, lock_flags);
+}
+
+static struct board_t *find_board_by_major(unsigned int major)
+{
+ unsigned int i;
+
+ for (i = 0; i < MAXBOARDS; i++) {
+ struct board_t *brd = dgap_board[i];
+
+ if (!brd)
+ return NULL;
+ if (major == brd->serial_driver->major ||
+ major == brd->print_driver->major)
+ return brd;
+ }
+
+ return NULL;
+}
+
+/************************************************************************
+ *
+ * TTY Entry points and helper functions
+ *
+ ************************************************************************/
+
+/*
+ * dgap_tty_open()
+ *
+ */
+static int dgap_tty_open(struct tty_struct *tty, struct file *file)
+{
+ struct board_t *brd;
+ struct channel_t *ch;
+ struct un_t *un;
+ struct bs_t __iomem *bs;
+ uint major;
+ uint minor;
+ int rc;
+ ulong lock_flags;
+ ulong lock_flags2;
+ u16 head;
+
+ major = MAJOR(tty_devnum(tty));
+ minor = MINOR(tty_devnum(tty));
+
+ brd = find_board_by_major(major);
+ if (!brd)
+ return -EIO;
+
+ /*
+ * If board is not yet up to a state of READY, go to
+ * sleep waiting for it to happen or they cancel the open.
+ */
+ rc = wait_event_interruptible(brd->state_wait,
+ (brd->state & BOARD_READY));
+
+ if (rc)
+ return rc;
+
+ spin_lock_irqsave(&brd->bd_lock, lock_flags);
+
+ /* The wait above should guarantee this cannot happen */
+ if (brd->state != BOARD_READY) {
+ spin_unlock_irqrestore(&brd->bd_lock, lock_flags);
+ return -EIO;
+ }
+
+ /* If opened device is greater than our number of ports, bail. */
+ if (MINOR(tty_devnum(tty)) > brd->nasync) {
+ spin_unlock_irqrestore(&brd->bd_lock, lock_flags);
+ return -EIO;
+ }
+
+ ch = brd->channels[minor];
+ if (!ch) {
+ spin_unlock_irqrestore(&brd->bd_lock, lock_flags);
+ return -EIO;
+ }
+
+ /* Grab channel lock */
+ spin_lock_irqsave(&ch->ch_lock, lock_flags2);
+
+ /* Figure out our type */
+ if (major == brd->serial_driver->major) {
+ un = &brd->channels[minor]->ch_tun;
+ un->un_type = DGAP_SERIAL;
+ } else if (major == brd->print_driver->major) {
+ un = &brd->channels[minor]->ch_pun;
+ un->un_type = DGAP_PRINT;
+ } else {
+ spin_unlock_irqrestore(&ch->ch_lock, lock_flags2);
+ spin_unlock_irqrestore(&brd->bd_lock, lock_flags);
+ return -EIO;
+ }
+
+ /* Store our unit into driver_data, so we always have it available. */
+ tty->driver_data = un;
+
+ /*
+ * Error if channel info pointer is NULL.
+ */
+ bs = ch->ch_bs;
+ if (!bs) {
+ spin_unlock_irqrestore(&ch->ch_lock, lock_flags2);
+ spin_unlock_irqrestore(&brd->bd_lock, lock_flags);
+ return -EIO;
+ }
+
+ /*
+ * Initialize tty's
+ */
+ if (!(un->un_flags & UN_ISOPEN)) {
+ /* Store important variables. */
+ un->un_tty = tty;
+
+ /* Maybe do something here to the TTY struct as well? */
+ }
+
+ /*
+ * Initialize if neither terminal or printer is open.
+ */
+ if (!((ch->ch_tun.un_flags | ch->ch_pun.un_flags) & UN_ISOPEN)) {
+
+ ch->ch_mforce = 0;
+ ch->ch_mval = 0;
+
+ /*
+ * Flush input queue.
+ */
+ head = readw(&(bs->rx_head));
+ writew(head, &(bs->rx_tail));
+
+ ch->ch_flags = 0;
+ ch->pscan_state = 0;
+ ch->pscan_savechar = 0;
+
+ ch->ch_c_cflag = tty->termios.c_cflag;
+ ch->ch_c_iflag = tty->termios.c_iflag;
+ ch->ch_c_oflag = tty->termios.c_oflag;
+ ch->ch_c_lflag = tty->termios.c_lflag;
+ ch->ch_startc = tty->termios.c_cc[VSTART];
+ ch->ch_stopc = tty->termios.c_cc[VSTOP];
+
+ /* TODO: flush our TTY struct here? */
+ }
+
+ dgap_carrier(ch);
+ /*
+ * Run param in case we changed anything
+ */
+ dgap_param(ch, brd, un->un_type);
+
+ /*
+ * follow protocol for opening port
+ */
+
+ spin_unlock_irqrestore(&ch->ch_lock, lock_flags2);
+ spin_unlock_irqrestore(&brd->bd_lock, lock_flags);
+
+ rc = dgap_block_til_ready(tty, file, ch);
+
+ if (!un->un_tty)
+ return -ENODEV;
+
+ /* No going back now, increment our unit and channel counters */
+ spin_lock_irqsave(&ch->ch_lock, lock_flags);
+ ch->ch_open_count++;
+ un->un_open_count++;
+ un->un_flags |= (UN_ISOPEN);
+ spin_unlock_irqrestore(&ch->ch_lock, lock_flags);
+
+ return rc;
+}
+
+/*
+ * dgap_tty_close()
+ *
+ */
+static void dgap_tty_close(struct tty_struct *tty, struct file *file)
+{
+ struct ktermios *ts;
+ struct board_t *bd;
+ struct channel_t *ch;
+ struct un_t *un;
+ ulong lock_flags;
+
+ if (!tty || tty->magic != TTY_MAGIC)
+ return;
+
+ un = tty->driver_data;
+ if (!un || un->magic != DGAP_UNIT_MAGIC)
+ return;
+
+ ch = un->un_ch;
+ if (!ch || ch->magic != DGAP_CHANNEL_MAGIC)
+ return;
+
+ bd = ch->ch_bd;
+ if (!bd || bd->magic != DGAP_BOARD_MAGIC)
+ return;
+
+ ts = &tty->termios;
+
+ spin_lock_irqsave(&ch->ch_lock, lock_flags);
+
+ /*
+ * Determine if this is the last close or not - and if we agree about
+ * which type of close it is with the Line Discipline
+ */
+ if ((tty->count == 1) && (un->un_open_count != 1)) {
+ /*
+ * Uh, oh. tty->count is 1, which means that the tty
+ * structure will be freed. un_open_count should always
+ * be one in these conditions. If it's greater than
+ * one, we've got real problems, since it means the
+ * serial port won't be shutdown.
+ */
+ un->un_open_count = 1;
+ }
+
+ if (--un->un_open_count < 0)
+ un->un_open_count = 0;
+
+ ch->ch_open_count--;
+
+ if (ch->ch_open_count && un->un_open_count) {
+ spin_unlock_irqrestore(&ch->ch_lock, lock_flags);
+ return;
+ }
+
+ /* OK, its the last close on the unit */
+
+ un->un_flags |= UN_CLOSING;
+
+ tty->closing = 1;
+
+ /*
+ * Only officially close channel if count is 0 and
+ * DIGI_PRINTER bit is not set.
+ */
+ if ((ch->ch_open_count == 0) &&
+ !(ch->ch_digi.digi_flags & DIGI_PRINTER)) {
+
+ ch->ch_flags &= ~(CH_RXBLOCK);
+
+ spin_unlock_irqrestore(&ch->ch_lock, lock_flags);
+
+ /* wait for output to drain */
+ /* This will also return if we take an interrupt */
+
+ dgap_wait_for_drain(tty);
+
+ dgap_tty_flush_buffer(tty);
+ tty_ldisc_flush(tty);
+
+ spin_lock_irqsave(&ch->ch_lock, lock_flags);
+
+ tty->closing = 0;
+
+ /*
+ * If we have HUPCL set, lower DTR and RTS
+ */
+ if (ch->ch_c_cflag & HUPCL) {
+ ch->ch_mostat &= ~(D_RTS(ch)|D_DTR(ch));
+ dgap_cmdb(ch, SMODEM, 0, D_DTR(ch)|D_RTS(ch), 0);
+
+ /*
+ * Go to sleep to ensure RTS/DTR
+ * have been dropped for modems to see it.
+ */
+ spin_unlock_irqrestore(&ch->ch_lock,
+ lock_flags);
+
+ /* .25 second delay for dropping RTS/DTR */
+ schedule_timeout_interruptible(msecs_to_jiffies(250));
+
+ spin_lock_irqsave(&ch->ch_lock, lock_flags);
+ }
+
+ ch->pscan_state = 0;
+ ch->pscan_savechar = 0;
+ ch->ch_baud_info = 0;
+
+ }
+
+ /*
+ * turn off print device when closing print device.
+ */
+ if ((un->un_type == DGAP_PRINT) && (ch->ch_flags & CH_PRON)) {
+ dgap_wmove(ch, ch->ch_digi.digi_offstr,
+ (int) ch->ch_digi.digi_offlen);
+ ch->ch_flags &= ~CH_PRON;
+ }
+
+ un->un_tty = NULL;
+ un->un_flags &= ~(UN_ISOPEN | UN_CLOSING);
+ tty->driver_data = NULL;
+
+ wake_up_interruptible(&ch->ch_flags_wait);
+ wake_up_interruptible(&un->un_flags_wait);
+
+ spin_unlock_irqrestore(&ch->ch_lock, lock_flags);
+}
+
+static void dgap_tty_start(struct tty_struct *tty)
+{
+ struct board_t *bd;
+ struct channel_t *ch;
+ struct un_t *un;
+ ulong lock_flags;
+ ulong lock_flags2;
+
+ if (!tty || tty->magic != TTY_MAGIC)
+ return;
+
+ un = tty->driver_data;
+ if (!un || un->magic != DGAP_UNIT_MAGIC)
+ return;
+
+ ch = un->un_ch;
+ if (!ch || ch->magic != DGAP_CHANNEL_MAGIC)
+ return;
+
+ bd = ch->ch_bd;
+ if (!bd || bd->magic != DGAP_BOARD_MAGIC)
+ return;
+
+ spin_lock_irqsave(&bd->bd_lock, lock_flags);
+ spin_lock_irqsave(&ch->ch_lock, lock_flags2);
+
+ dgap_cmdw(ch, RESUMETX, 0, 0);
+
+ spin_unlock_irqrestore(&ch->ch_lock, lock_flags2);
+ spin_unlock_irqrestore(&bd->bd_lock, lock_flags);
+}
+
+static void dgap_tty_stop(struct tty_struct *tty)
+{
+ struct board_t *bd;
+ struct channel_t *ch;
+ struct un_t *un;
+ ulong lock_flags;
+ ulong lock_flags2;
+
+ if (!tty || tty->magic != TTY_MAGIC)
+ return;
+
+ un = tty->driver_data;
+ if (!un || un->magic != DGAP_UNIT_MAGIC)
+ return;
+
+ ch = un->un_ch;
+ if (!ch || ch->magic != DGAP_CHANNEL_MAGIC)
+ return;
+
+ bd = ch->ch_bd;
+ if (!bd || bd->magic != DGAP_BOARD_MAGIC)
+ return;
+
+ spin_lock_irqsave(&bd->bd_lock, lock_flags);
+ spin_lock_irqsave(&ch->ch_lock, lock_flags2);
+
+ dgap_cmdw(ch, PAUSETX, 0, 0);
+
+ spin_unlock_irqrestore(&ch->ch_lock, lock_flags2);
+ spin_unlock_irqrestore(&bd->bd_lock, lock_flags);
+}
+
+/*
+ * dgap_tty_flush_chars()
+ *
+ * Flush the cook buffer
+ *
+ * Note to self, and any other poor souls who venture here:
+ *
+ * flush in this case DOES NOT mean dispose of the data.
+ * instead, it means "stop buffering and send it if you
+ * haven't already." Just guess how I figured that out... SRW 2-Jun-98
+ *
+ * It is also always called in interrupt context - JAR 8-Sept-99
+ */
+static void dgap_tty_flush_chars(struct tty_struct *tty)
+{
+ struct board_t *bd;
+ struct channel_t *ch;
+ struct un_t *un;
+ ulong lock_flags;
+ ulong lock_flags2;
+
+ if (!tty || tty->magic != TTY_MAGIC)
+ return;
+
+ un = tty->driver_data;
+ if (!un || un->magic != DGAP_UNIT_MAGIC)
+ return;
+
+ ch = un->un_ch;
+ if (!ch || ch->magic != DGAP_CHANNEL_MAGIC)
+ return;
+
+ bd = ch->ch_bd;
+ if (!bd || bd->magic != DGAP_BOARD_MAGIC)
+ return;
+
+ spin_lock_irqsave(&bd->bd_lock, lock_flags);
+ spin_lock_irqsave(&ch->ch_lock, lock_flags2);
+
+ /* TODO: Do something here */
+
+ spin_unlock_irqrestore(&ch->ch_lock, lock_flags2);
+ spin_unlock_irqrestore(&bd->bd_lock, lock_flags);
+}
+
+/*****************************************************************************
+ *
+ * The IOCTL function and all of its helpers
+ *
+ *****************************************************************************/
+
+/*
+ * dgap_tty_ioctl()
+ *
+ * The usual assortment of ioctl's
+ */
+static int dgap_tty_ioctl(struct tty_struct *tty, unsigned int cmd,
+ unsigned long arg)
+{
+ struct board_t *bd;
+ struct channel_t *ch;
+ struct un_t *un;
+ int rc;
+ u16 head;
+ ulong lock_flags = 0;
+ ulong lock_flags2 = 0;
+ void __user *uarg = (void __user *) arg;
+
+ if (!tty || tty->magic != TTY_MAGIC)
+ return -ENODEV;
+
+ un = tty->driver_data;
+ if (!un || un->magic != DGAP_UNIT_MAGIC)
+ return -ENODEV;
+
+ ch = un->un_ch;
+ if (!ch || ch->magic != DGAP_CHANNEL_MAGIC)
+ return -ENODEV;
+
+ bd = ch->ch_bd;
+ if (!bd || bd->magic != DGAP_BOARD_MAGIC)
+ return -ENODEV;
+
+ spin_lock_irqsave(&bd->bd_lock, lock_flags);
+ spin_lock_irqsave(&ch->ch_lock, lock_flags2);
+
+ if (un->un_open_count <= 0) {
+ spin_unlock_irqrestore(&ch->ch_lock, lock_flags2);
+ spin_unlock_irqrestore(&bd->bd_lock, lock_flags);
+ return -EIO;
+ }
+
+ switch (cmd) {
+
+ /* Here are all the standard ioctl's that we MUST implement */
+
+ case TCSBRK:
+ /*
+ * TCSBRK is SVID version: non-zero arg --> no break
+ * this behaviour is exploited by tcdrain().
+ *
+ * According to POSIX.1 spec (7.2.2.1.2) breaks should be
+ * between 0.25 and 0.5 seconds so we'll ask for something
+ * in the middle: 0.375 seconds.
+ */
+ rc = tty_check_change(tty);
+ spin_unlock_irqrestore(&ch->ch_lock, lock_flags2);
+ spin_unlock_irqrestore(&bd->bd_lock, lock_flags);
+ if (rc)
+ return rc;
+
+ rc = dgap_wait_for_drain(tty);
+
+ if (rc)
+ return -EINTR;
+
+ spin_lock_irqsave(&bd->bd_lock, lock_flags);
+ spin_lock_irqsave(&ch->ch_lock, lock_flags2);
+
+ if (((cmd == TCSBRK) && (!arg)) || (cmd == TCSBRKP))
+ dgap_cmdw(ch, SBREAK, (u16) SBREAK_TIME, 0);
+
+ spin_unlock_irqrestore(&ch->ch_lock, lock_flags2);
+ spin_unlock_irqrestore(&bd->bd_lock, lock_flags);
+
+ return 0;
+
+ case TCSBRKP:
+ /* support for POSIX tcsendbreak()
+
+ * According to POSIX.1 spec (7.2.2.1.2) breaks should be
+ * between 0.25 and 0.5 seconds so we'll ask for something
+ * in the middle: 0.375 seconds.
+ */
+ rc = tty_check_change(tty);
+ spin_unlock_irqrestore(&ch->ch_lock, lock_flags2);
+ spin_unlock_irqrestore(&bd->bd_lock, lock_flags);
+ if (rc)
+ return rc;
+
+ rc = dgap_wait_for_drain(tty);
+ if (rc)
+ return -EINTR;
+
+ spin_lock_irqsave(&bd->bd_lock, lock_flags);
+ spin_lock_irqsave(&ch->ch_lock, lock_flags2);
+
+ dgap_cmdw(ch, SBREAK, (u16) SBREAK_TIME, 0);
+
+ spin_unlock_irqrestore(&ch->ch_lock, lock_flags2);
+ spin_unlock_irqrestore(&bd->bd_lock, lock_flags);
+
+ return 0;
+
+ case TIOCSBRK:
+ /*
+ * FEP5 doesn't support turning on a break unconditionally.
+ * The FEP5 device will stop sending a break automatically
+ * after the specified time value that was sent when turning on
+ * the break.
+ */
+ rc = tty_check_change(tty);
+ spin_unlock_irqrestore(&ch->ch_lock, lock_flags2);
+ spin_unlock_irqrestore(&bd->bd_lock, lock_flags);
+ if (rc)
+ return rc;
+
+ rc = dgap_wait_for_drain(tty);
+ if (rc)
+ return -EINTR;
+
+ spin_lock_irqsave(&bd->bd_lock, lock_flags);
+ spin_lock_irqsave(&ch->ch_lock, lock_flags2);
+
+ dgap_cmdw(ch, SBREAK, (u16) SBREAK_TIME, 0);
+
+ spin_unlock_irqrestore(&ch->ch_lock, lock_flags2);
+ spin_unlock_irqrestore(&bd->bd_lock, lock_flags);
+
+ return 0;
+
+ case TIOCCBRK:
+ /*
+ * FEP5 doesn't support turning off a break unconditionally.
+ * The FEP5 device will stop sending a break automatically
+ * after the specified time value that was sent when turning on
+ * the break.
+ */
+ spin_unlock_irqrestore(&ch->ch_lock, lock_flags2);
+ spin_unlock_irqrestore(&bd->bd_lock, lock_flags);
+ return 0;
+
+ case TIOCGSOFTCAR:
+
+ spin_unlock_irqrestore(&ch->ch_lock, lock_flags2);
+ spin_unlock_irqrestore(&bd->bd_lock, lock_flags);
+
+ rc = put_user(C_CLOCAL(tty) ? 1 : 0,
+ (unsigned long __user *) arg);
+ return rc;
+
+ case TIOCSSOFTCAR:
+ spin_unlock_irqrestore(&ch->ch_lock, lock_flags2);
+ spin_unlock_irqrestore(&bd->bd_lock, lock_flags);
+
+ rc = get_user(arg, (unsigned long __user *) arg);
+ if (rc)
+ return rc;
+
+ spin_lock_irqsave(&bd->bd_lock, lock_flags);
+ spin_lock_irqsave(&ch->ch_lock, lock_flags2);
+ tty->termios.c_cflag = ((tty->termios.c_cflag & ~CLOCAL) |
+ (arg ? CLOCAL : 0));
+ dgap_param(ch, bd, un->un_type);
+ spin_unlock_irqrestore(&ch->ch_lock, lock_flags2);
+ spin_unlock_irqrestore(&bd->bd_lock, lock_flags);
+
+ return 0;
+
+ case TIOCMGET:
+ spin_unlock_irqrestore(&ch->ch_lock, lock_flags2);
+ spin_unlock_irqrestore(&bd->bd_lock, lock_flags);
+ return dgap_get_modem_info(ch, uarg);
+
+ case TIOCMBIS:
+ case TIOCMBIC:
+ case TIOCMSET:
+ spin_unlock_irqrestore(&ch->ch_lock, lock_flags2);
+ spin_unlock_irqrestore(&bd->bd_lock, lock_flags);
+ return dgap_set_modem_info(ch, bd, un, cmd, uarg);
+
+ /*
+ * Here are any additional ioctl's that we want to implement
+ */
+
+ case TCFLSH:
+ /*
+ * The linux tty driver doesn't have a flush
+ * input routine for the driver, assuming all backed
+ * up data is in the line disc. buffers. However,
+ * we all know that's not the case. Here, we
+ * act on the ioctl, but then lie and say we didn't
+ * so the line discipline will process the flush
+ * also.
+ */
+ rc = tty_check_change(tty);
+ if (rc) {
+ spin_unlock_irqrestore(&ch->ch_lock, lock_flags2);
+ spin_unlock_irqrestore(&bd->bd_lock, lock_flags);
+ return rc;
+ }
+
+ if ((arg == TCIFLUSH) || (arg == TCIOFLUSH)) {
+ if (!(un->un_type == DGAP_PRINT)) {
+ head = readw(&(ch->ch_bs->rx_head));
+ writew(head, &(ch->ch_bs->rx_tail));
+ writeb(0, &(ch->ch_bs->orun));
+ }
+ }
+
+ if ((arg != TCOFLUSH) && (arg != TCIOFLUSH)) {
+ /* pretend we didn't recognize this IOCTL */
+ spin_unlock_irqrestore(&ch->ch_lock, lock_flags2);
+ spin_unlock_irqrestore(&bd->bd_lock, lock_flags);
+
+ return -ENOIOCTLCMD;
+ }
+
+ ch->ch_flags &= ~CH_STOP;
+ head = readw(&(ch->ch_bs->tx_head));
+ dgap_cmdw(ch, FLUSHTX, (u16) head, 0);
+ dgap_cmdw(ch, RESUMETX, 0, 0);
+ if (ch->ch_tun.un_flags & (UN_LOW|UN_EMPTY)) {
+ ch->ch_tun.un_flags &= ~(UN_LOW|UN_EMPTY);
+ wake_up_interruptible(&ch->ch_tun.un_flags_wait);
+ }
+ if (ch->ch_pun.un_flags & (UN_LOW|UN_EMPTY)) {
+ ch->ch_pun.un_flags &= ~(UN_LOW|UN_EMPTY);
+ wake_up_interruptible(&ch->ch_pun.un_flags_wait);
+ }
+ if (waitqueue_active(&tty->write_wait))
+ wake_up_interruptible(&tty->write_wait);
+
+ /* Can't hold any locks when calling tty_wakeup! */
+ spin_unlock_irqrestore(&ch->ch_lock, lock_flags2);
+ spin_unlock_irqrestore(&bd->bd_lock, lock_flags);
+ tty_wakeup(tty);
+
+ /* pretend we didn't recognize this IOCTL */
+ return -ENOIOCTLCMD;
+
+ case TCSETSF:
+ case TCSETSW:
+ /*
+ * The linux tty driver doesn't have a flush
+ * input routine for the driver, assuming all backed
+ * up data is in the line disc. buffers. However,
+ * we all know that's not the case. Here, we
+ * act on the ioctl, but then lie and say we didn't
+ * so the line discipline will process the flush
+ * also.
+ */
+ if (cmd == TCSETSF) {
+ /* flush rx */
+ ch->ch_flags &= ~CH_STOP;
+ head = readw(&(ch->ch_bs->rx_head));
+ writew(head, &(ch->ch_bs->rx_tail));
+ }
+
+ /* now wait for all the output to drain */
+ spin_unlock_irqrestore(&ch->ch_lock, lock_flags2);
+ spin_unlock_irqrestore(&bd->bd_lock, lock_flags);
+ rc = dgap_wait_for_drain(tty);
+ if (rc)
+ return -EINTR;
+
+ /* pretend we didn't recognize this */
+ return -ENOIOCTLCMD;
+
+ case TCSETAW:
+
+ spin_unlock_irqrestore(&ch->ch_lock, lock_flags2);
+ spin_unlock_irqrestore(&bd->bd_lock, lock_flags);
+ rc = dgap_wait_for_drain(tty);
+ if (rc)
+ return -EINTR;
+
+ /* pretend we didn't recognize this */
+ return -ENOIOCTLCMD;
+
+ case TCXONC:
+ /*
+ * The Linux Line Discipline (LD) would do this for us if we
+ * let it, but we have the special firmware options to do this
+ * the "right way" regardless of hardware or software flow
+ * control so we'll do it outselves instead of letting the LD
+ * do it.
+ */
+ rc = tty_check_change(tty);
+ if (rc) {
+ spin_unlock_irqrestore(&ch->ch_lock, lock_flags2);
+ spin_unlock_irqrestore(&bd->bd_lock, lock_flags);
+ return rc;
+ }
+
+ switch (arg) {
+
+ case TCOON:
+ spin_unlock_irqrestore(&ch->ch_lock, lock_flags2);
+ spin_unlock_irqrestore(&bd->bd_lock, lock_flags);
+ dgap_tty_start(tty);
+ return 0;
+ case TCOOFF:
+ spin_unlock_irqrestore(&ch->ch_lock, lock_flags2);
+ spin_unlock_irqrestore(&bd->bd_lock, lock_flags);
+ dgap_tty_stop(tty);
+ return 0;
+ case TCION:
+ spin_unlock_irqrestore(&ch->ch_lock, lock_flags2);
+ spin_unlock_irqrestore(&bd->bd_lock, lock_flags);
+ /* Make the ld do it */
+ return -ENOIOCTLCMD;
+ case TCIOFF:
+ spin_unlock_irqrestore(&ch->ch_lock, lock_flags2);
+ spin_unlock_irqrestore(&bd->bd_lock, lock_flags);
+ /* Make the ld do it */
+ return -ENOIOCTLCMD;
+ default:
+ spin_unlock_irqrestore(&ch->ch_lock, lock_flags2);
+ spin_unlock_irqrestore(&bd->bd_lock, lock_flags);
+ return -EINVAL;
+ }
+
+ case DIGI_GETA:
+ /* get information for ditty */
+ spin_unlock_irqrestore(&ch->ch_lock, lock_flags2);
+ spin_unlock_irqrestore(&bd->bd_lock, lock_flags);
+ return dgap_tty_digigeta(ch, uarg);
+
+ case DIGI_SETAW:
+ case DIGI_SETAF:
+
+ /* set information for ditty */
+ if (cmd == (DIGI_SETAW)) {
+
+ spin_unlock_irqrestore(&ch->ch_lock, lock_flags2);
+ spin_unlock_irqrestore(&bd->bd_lock, lock_flags);
+ rc = dgap_wait_for_drain(tty);
+ if (rc)
+ return -EINTR;
+ spin_lock_irqsave(&bd->bd_lock, lock_flags);
+ spin_lock_irqsave(&ch->ch_lock, lock_flags2);
+ } else
+ tty_ldisc_flush(tty);
+ /* fall thru */
+
+ case DIGI_SETA:
+ spin_unlock_irqrestore(&ch->ch_lock, lock_flags2);
+ spin_unlock_irqrestore(&bd->bd_lock, lock_flags);
+ return dgap_tty_digiseta(ch, bd, un, uarg);
+
+ case DIGI_GEDELAY:
+ spin_unlock_irqrestore(&ch->ch_lock, lock_flags2);
+ spin_unlock_irqrestore(&bd->bd_lock, lock_flags);
+ return dgap_tty_digigetedelay(tty, uarg);
+
+ case DIGI_SEDELAY:
+ spin_unlock_irqrestore(&ch->ch_lock, lock_flags2);
+ spin_unlock_irqrestore(&bd->bd_lock, lock_flags);
+ return dgap_tty_digisetedelay(ch, bd, un, uarg);
+
+ case DIGI_GETCUSTOMBAUD:
+ spin_unlock_irqrestore(&ch->ch_lock, lock_flags2);
+ spin_unlock_irqrestore(&bd->bd_lock, lock_flags);
+ return dgap_tty_digigetcustombaud(ch, un, uarg);
+
+ case DIGI_SETCUSTOMBAUD:
+ spin_unlock_irqrestore(&ch->ch_lock, lock_flags2);
+ spin_unlock_irqrestore(&bd->bd_lock, lock_flags);
+ return dgap_tty_digisetcustombaud(ch, bd, un, uarg);
+
+ case DIGI_RESET_PORT:
+ dgap_firmware_reset_port(ch);
+ dgap_param(ch, bd, un->un_type);
+ spin_unlock_irqrestore(&ch->ch_lock, lock_flags2);
+ spin_unlock_irqrestore(&bd->bd_lock, lock_flags);
+ return 0;
+
+ default:
+ spin_unlock_irqrestore(&ch->ch_lock, lock_flags2);
+ spin_unlock_irqrestore(&bd->bd_lock, lock_flags);
+
+ return -ENOIOCTLCMD;
+ }
+}
+
+static const struct tty_operations dgap_tty_ops = {
+ .open = dgap_tty_open,
+ .close = dgap_tty_close,
+ .write = dgap_tty_write,
+ .write_room = dgap_tty_write_room,
+ .flush_buffer = dgap_tty_flush_buffer,
+ .chars_in_buffer = dgap_tty_chars_in_buffer,
+ .flush_chars = dgap_tty_flush_chars,
+ .ioctl = dgap_tty_ioctl,
+ .set_termios = dgap_tty_set_termios,
+ .stop = dgap_tty_stop,
+ .start = dgap_tty_start,
+ .throttle = dgap_tty_throttle,
+ .unthrottle = dgap_tty_unthrottle,
+ .hangup = dgap_tty_hangup,
+ .put_char = dgap_tty_put_char,
+ .tiocmget = dgap_tty_tiocmget,
+ .tiocmset = dgap_tty_tiocmset,
+ .break_ctl = dgap_tty_send_break,
+ .wait_until_sent = dgap_tty_wait_until_sent,
+ .send_xchar = dgap_tty_send_xchar
+};
+
+/************************************************************************
+ *
+ * TTY Initialization/Cleanup Functions
+ *
+ ************************************************************************/
+
+/*
+ * dgap_tty_register()
+ *
+ * Init the tty subsystem for this board.
+ */
+static int dgap_tty_register(struct board_t *brd)
+{
+ int rc;
+
+ brd->serial_driver = tty_alloc_driver(MAXPORTS,
+ TTY_DRIVER_REAL_RAW |
+ TTY_DRIVER_DYNAMIC_DEV |
+ TTY_DRIVER_HARDWARE_BREAK);
+ if (IS_ERR(brd->serial_driver))
+ return PTR_ERR(brd->serial_driver);
+
+ snprintf(brd->serial_name, MAXTTYNAMELEN, "tty_dgap_%d_",
+ brd->boardnum);
+ brd->serial_driver->name = brd->serial_name;
+ brd->serial_driver->name_base = 0;
+ brd->serial_driver->major = 0;
+ brd->serial_driver->minor_start = 0;
+ brd->serial_driver->type = TTY_DRIVER_TYPE_SERIAL;
+ brd->serial_driver->subtype = SERIAL_TYPE_NORMAL;
+ brd->serial_driver->init_termios = dgap_default_termios;
+ brd->serial_driver->driver_name = DRVSTR;
+
+ /*
+ * Entry points for driver. Called by the kernel from
+ * tty_io.c and n_tty.c.
+ */
+ tty_set_operations(brd->serial_driver, &dgap_tty_ops);
+
+ /*
+ * If we're doing transparent print, we have to do all of the above
+ * again, separately so we don't get the LD confused about what major
+ * we are when we get into the dgap_tty_open() routine.
+ */
+ brd->print_driver = tty_alloc_driver(MAXPORTS,
+ TTY_DRIVER_REAL_RAW |
+ TTY_DRIVER_DYNAMIC_DEV |
+ TTY_DRIVER_HARDWARE_BREAK);
+ if (IS_ERR(brd->print_driver)) {
+ rc = PTR_ERR(brd->print_driver);
+ goto free_serial_drv;
+ }
+
+ snprintf(brd->print_name, MAXTTYNAMELEN, "pr_dgap_%d_",
+ brd->boardnum);
+ brd->print_driver->name = brd->print_name;
+ brd->print_driver->name_base = 0;
+ brd->print_driver->major = 0;
+ brd->print_driver->minor_start = 0;
+ brd->print_driver->type = TTY_DRIVER_TYPE_SERIAL;
+ brd->print_driver->subtype = SERIAL_TYPE_NORMAL;
+ brd->print_driver->init_termios = dgap_default_termios;
+ brd->print_driver->driver_name = DRVSTR;
+
+ /*
+ * Entry points for driver. Called by the kernel from
+ * tty_io.c and n_tty.c.
+ */
+ tty_set_operations(brd->print_driver, &dgap_tty_ops);
+
+ /* Register tty devices */
+ rc = tty_register_driver(brd->serial_driver);
+ if (rc < 0)
+ goto free_print_drv;
+
+ /* Register Transparent Print devices */
+ rc = tty_register_driver(brd->print_driver);
+ if (rc < 0)
+ goto unregister_serial_drv;
+
+ return 0;
+
+unregister_serial_drv:
+ tty_unregister_driver(brd->serial_driver);
+free_print_drv:
+ put_tty_driver(brd->print_driver);
+free_serial_drv:
+ put_tty_driver(brd->serial_driver);
+
+ return rc;
+}
+
+static void dgap_tty_unregister(struct board_t *brd)
+{
+ tty_unregister_driver(brd->print_driver);
+ tty_unregister_driver(brd->serial_driver);
+ put_tty_driver(brd->print_driver);
+ put_tty_driver(brd->serial_driver);
+}
+
+static int dgap_alloc_flipbuf(struct board_t *brd)
+{
+ /*
+ * allocate flip buffer for board.
+ */
+ brd->flipbuf = kmalloc(MYFLIPLEN, GFP_KERNEL);
+ if (!brd->flipbuf)
+ return -ENOMEM;
+
+ brd->flipflagbuf = kmalloc(MYFLIPLEN, GFP_KERNEL);
+ if (!brd->flipflagbuf) {
+ kfree(brd->flipbuf);
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+static void dgap_free_flipbuf(struct board_t *brd)
+{
+ kfree(brd->flipbuf);
+ kfree(brd->flipflagbuf);
+}
+
+static struct board_t *dgap_verify_board(struct device *p)
+{
+ struct board_t *bd;
+
+ if (!p)
+ return NULL;
+
+ bd = dev_get_drvdata(p);
+ if (!bd || bd->magic != DGAP_BOARD_MAGIC || bd->state != BOARD_READY)
+ return NULL;
+
+ return bd;
+}
+
+static ssize_t dgap_ports_state_show(struct device *p,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct board_t *bd;
+ int count = 0;
+ unsigned int i;
+
+ bd = dgap_verify_board(p);
+ if (!bd)
+ return 0;
+
+ for (i = 0; i < bd->nasync; i++) {
+ count += snprintf(buf + count, PAGE_SIZE - count,
+ "%d %s\n", bd->channels[i]->ch_portnum,
+ bd->channels[i]->ch_open_count ? "Open" : "Closed");
+ }
+ return count;
+}
+static DEVICE_ATTR(ports_state, S_IRUSR, dgap_ports_state_show, NULL);
+
+static ssize_t dgap_ports_baud_show(struct device *p,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct board_t *bd;
+ int count = 0;
+ unsigned int i;
+
+ bd = dgap_verify_board(p);
+ if (!bd)
+ return 0;
+
+ for (i = 0; i < bd->nasync; i++) {
+ count += snprintf(buf + count, PAGE_SIZE - count, "%d %d\n",
+ bd->channels[i]->ch_portnum,
+ bd->channels[i]->ch_baud_info);
+ }
+ return count;
+}
+static DEVICE_ATTR(ports_baud, S_IRUSR, dgap_ports_baud_show, NULL);
+
+static ssize_t dgap_ports_msignals_show(struct device *p,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct board_t *bd;
+ int count = 0;
+ unsigned int i;
+
+ bd = dgap_verify_board(p);
+ if (!bd)
+ return 0;
+
+ for (i = 0; i < bd->nasync; i++) {
+ if (bd->channels[i]->ch_open_count)
+ count += snprintf(buf + count, PAGE_SIZE - count,
+ "%d %s %s %s %s %s %s\n",
+ bd->channels[i]->ch_portnum,
+ (bd->channels[i]->ch_mostat &
+ UART_MCR_RTS) ? "RTS" : "",
+ (bd->channels[i]->ch_mistat &
+ UART_MSR_CTS) ? "CTS" : "",
+ (bd->channels[i]->ch_mostat &
+ UART_MCR_DTR) ? "DTR" : "",
+ (bd->channels[i]->ch_mistat &
+ UART_MSR_DSR) ? "DSR" : "",
+ (bd->channels[i]->ch_mistat &
+ UART_MSR_DCD) ? "DCD" : "",
+ (bd->channels[i]->ch_mistat &
+ UART_MSR_RI) ? "RI" : "");
+ else
+ count += snprintf(buf + count, PAGE_SIZE - count,
+ "%d\n", bd->channels[i]->ch_portnum);
+ }
+ return count;
+}
+static DEVICE_ATTR(ports_msignals, S_IRUSR, dgap_ports_msignals_show, NULL);
+
+static ssize_t dgap_ports_iflag_show(struct device *p,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct board_t *bd;
+ int count = 0;
+ unsigned int i;
+
+ bd = dgap_verify_board(p);
+ if (!bd)
+ return 0;
+
+ for (i = 0; i < bd->nasync; i++)
+ count += snprintf(buf + count, PAGE_SIZE - count, "%d %x\n",
+ bd->channels[i]->ch_portnum,
+ bd->channels[i]->ch_c_iflag);
+ return count;
+}
+static DEVICE_ATTR(ports_iflag, S_IRUSR, dgap_ports_iflag_show, NULL);
+
+static ssize_t dgap_ports_cflag_show(struct device *p,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct board_t *bd;
+ int count = 0;
+ unsigned int i;
+
+ bd = dgap_verify_board(p);
+ if (!bd)
+ return 0;
+
+ for (i = 0; i < bd->nasync; i++)
+ count += snprintf(buf + count, PAGE_SIZE - count, "%d %x\n",
+ bd->channels[i]->ch_portnum,
+ bd->channels[i]->ch_c_cflag);
+ return count;
+}
+static DEVICE_ATTR(ports_cflag, S_IRUSR, dgap_ports_cflag_show, NULL);
+
+static ssize_t dgap_ports_oflag_show(struct device *p,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct board_t *bd;
+ int count = 0;
+ unsigned int i;
+
+ bd = dgap_verify_board(p);
+ if (!bd)
+ return 0;
+
+ for (i = 0; i < bd->nasync; i++)
+ count += snprintf(buf + count, PAGE_SIZE - count, "%d %x\n",
+ bd->channels[i]->ch_portnum,
+ bd->channels[i]->ch_c_oflag);
+ return count;
+}
+static DEVICE_ATTR(ports_oflag, S_IRUSR, dgap_ports_oflag_show, NULL);
+
+static ssize_t dgap_ports_lflag_show(struct device *p,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct board_t *bd;
+ int count = 0;
+ unsigned int i;
+
+ bd = dgap_verify_board(p);
+ if (!bd)
+ return 0;
+
+ for (i = 0; i < bd->nasync; i++)
+ count += snprintf(buf + count, PAGE_SIZE - count, "%d %x\n",
+ bd->channels[i]->ch_portnum,
+ bd->channels[i]->ch_c_lflag);
+ return count;
+}
+static DEVICE_ATTR(ports_lflag, S_IRUSR, dgap_ports_lflag_show, NULL);
+
+static ssize_t dgap_ports_digi_flag_show(struct device *p,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct board_t *bd;
+ int count = 0;
+ unsigned int i;
+
+ bd = dgap_verify_board(p);
+ if (!bd)
+ return 0;
+
+ for (i = 0; i < bd->nasync; i++)
+ count += snprintf(buf + count, PAGE_SIZE - count, "%d %x\n",
+ bd->channels[i]->ch_portnum,
+ bd->channels[i]->ch_digi.digi_flags);
+ return count;
+}
+static DEVICE_ATTR(ports_digi_flag, S_IRUSR, dgap_ports_digi_flag_show, NULL);
+
+static ssize_t dgap_ports_rxcount_show(struct device *p,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct board_t *bd;
+ int count = 0;
+ unsigned int i;
+
+ bd = dgap_verify_board(p);
+ if (!bd)
+ return 0;
+
+ for (i = 0; i < bd->nasync; i++)
+ count += snprintf(buf + count, PAGE_SIZE - count, "%d %ld\n",
+ bd->channels[i]->ch_portnum,
+ bd->channels[i]->ch_rxcount);
+ return count;
+}
+static DEVICE_ATTR(ports_rxcount, S_IRUSR, dgap_ports_rxcount_show, NULL);
+
+static ssize_t dgap_ports_txcount_show(struct device *p,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct board_t *bd;
+ int count = 0;
+ unsigned int i;
+
+ bd = dgap_verify_board(p);
+ if (!bd)
+ return 0;
+
+ for (i = 0; i < bd->nasync; i++)
+ count += snprintf(buf + count, PAGE_SIZE - count, "%d %ld\n",
+ bd->channels[i]->ch_portnum,
+ bd->channels[i]->ch_txcount);
+ return count;
+}
+static DEVICE_ATTR(ports_txcount, S_IRUSR, dgap_ports_txcount_show, NULL);
+
+static ssize_t dgap_tty_state_show(struct device *d,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct board_t *bd;
+ struct channel_t *ch;
+ struct un_t *un;
+
+ if (!d)
+ return 0;
+ un = dev_get_drvdata(d);
+ if (!un || un->magic != DGAP_UNIT_MAGIC)
+ return 0;
+ ch = un->un_ch;
+ if (!ch || ch->magic != DGAP_CHANNEL_MAGIC)
+ return 0;
+ bd = ch->ch_bd;
+ if (!bd || bd->magic != DGAP_BOARD_MAGIC)
+ return 0;
+ if (bd->state != BOARD_READY)
+ return 0;
+
+ return snprintf(buf, PAGE_SIZE, "%s", un->un_open_count ?
+ "Open" : "Closed");
+}
+static DEVICE_ATTR(state, S_IRUSR, dgap_tty_state_show, NULL);
+
+static ssize_t dgap_tty_baud_show(struct device *d,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct board_t *bd;
+ struct channel_t *ch;
+ struct un_t *un;
+
+ if (!d)
+ return 0;
+ un = dev_get_drvdata(d);
+ if (!un || un->magic != DGAP_UNIT_MAGIC)
+ return 0;
+ ch = un->un_ch;
+ if (!ch || ch->magic != DGAP_CHANNEL_MAGIC)
+ return 0;
+ bd = ch->ch_bd;
+ if (!bd || bd->magic != DGAP_BOARD_MAGIC)
+ return 0;
+ if (bd->state != BOARD_READY)
+ return 0;
+
+ return snprintf(buf, PAGE_SIZE, "%d\n", ch->ch_baud_info);
+}
+static DEVICE_ATTR(baud, S_IRUSR, dgap_tty_baud_show, NULL);
+
+static ssize_t dgap_tty_msignals_show(struct device *d,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct board_t *bd;
+ struct channel_t *ch;
+ struct un_t *un;
+
+ if (!d)
+ return 0;
+ un = dev_get_drvdata(d);
+ if (!un || un->magic != DGAP_UNIT_MAGIC)
+ return 0;
+ ch = un->un_ch;
+ if (!ch || ch->magic != DGAP_CHANNEL_MAGIC)
+ return 0;
+ bd = ch->ch_bd;
+ if (!bd || bd->magic != DGAP_BOARD_MAGIC)
+ return 0;
+ if (bd->state != BOARD_READY)
+ return 0;
+
+ if (ch->ch_open_count) {
+ return snprintf(buf, PAGE_SIZE, "%s %s %s %s %s %s\n",
+ (ch->ch_mostat & UART_MCR_RTS) ? "RTS" : "",
+ (ch->ch_mistat & UART_MSR_CTS) ? "CTS" : "",
+ (ch->ch_mostat & UART_MCR_DTR) ? "DTR" : "",
+ (ch->ch_mistat & UART_MSR_DSR) ? "DSR" : "",
+ (ch->ch_mistat & UART_MSR_DCD) ? "DCD" : "",
+ (ch->ch_mistat & UART_MSR_RI) ? "RI" : "");
+ }
+ return 0;
+}
+static DEVICE_ATTR(msignals, S_IRUSR, dgap_tty_msignals_show, NULL);
+
+static ssize_t dgap_tty_iflag_show(struct device *d,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct board_t *bd;
+ struct channel_t *ch;
+ struct un_t *un;
+
+ if (!d)
+ return 0;
+ un = dev_get_drvdata(d);
+ if (!un || un->magic != DGAP_UNIT_MAGIC)
+ return 0;
+ ch = un->un_ch;
+ if (!ch || ch->magic != DGAP_CHANNEL_MAGIC)
+ return 0;
+ bd = ch->ch_bd;
+ if (!bd || bd->magic != DGAP_BOARD_MAGIC)
+ return 0;
+ if (bd->state != BOARD_READY)
+ return 0;
+
+ return snprintf(buf, PAGE_SIZE, "%x\n", ch->ch_c_iflag);
+}
+static DEVICE_ATTR(iflag, S_IRUSR, dgap_tty_iflag_show, NULL);
+
+static ssize_t dgap_tty_cflag_show(struct device *d,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct board_t *bd;
+ struct channel_t *ch;
+ struct un_t *un;
+
+ if (!d)
+ return 0;
+ un = dev_get_drvdata(d);
+ if (!un || un->magic != DGAP_UNIT_MAGIC)
+ return 0;
+ ch = un->un_ch;
+ if (!ch || ch->magic != DGAP_CHANNEL_MAGIC)
+ return 0;
+ bd = ch->ch_bd;
+ if (!bd || bd->magic != DGAP_BOARD_MAGIC)
+ return 0;
+ if (bd->state != BOARD_READY)
+ return 0;
+
+ return snprintf(buf, PAGE_SIZE, "%x\n", ch->ch_c_cflag);
+}
+static DEVICE_ATTR(cflag, S_IRUSR, dgap_tty_cflag_show, NULL);
+
+static ssize_t dgap_tty_oflag_show(struct device *d,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct board_t *bd;
+ struct channel_t *ch;
+ struct un_t *un;
+
+ if (!d)
+ return 0;
+ un = dev_get_drvdata(d);
+ if (!un || un->magic != DGAP_UNIT_MAGIC)
+ return 0;
+ ch = un->un_ch;
+ if (!ch || ch->magic != DGAP_CHANNEL_MAGIC)
+ return 0;
+ bd = ch->ch_bd;
+ if (!bd || bd->magic != DGAP_BOARD_MAGIC)
+ return 0;
+ if (bd->state != BOARD_READY)
+ return 0;
+
+ return snprintf(buf, PAGE_SIZE, "%x\n", ch->ch_c_oflag);
+}
+static DEVICE_ATTR(oflag, S_IRUSR, dgap_tty_oflag_show, NULL);
+
+static ssize_t dgap_tty_lflag_show(struct device *d,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct board_t *bd;
+ struct channel_t *ch;
+ struct un_t *un;
+
+ if (!d)
+ return 0;
+ un = dev_get_drvdata(d);
+ if (!un || un->magic != DGAP_UNIT_MAGIC)
+ return 0;
+ ch = un->un_ch;
+ if (!ch || ch->magic != DGAP_CHANNEL_MAGIC)
+ return 0;
+ bd = ch->ch_bd;
+ if (!bd || bd->magic != DGAP_BOARD_MAGIC)
+ return 0;
+ if (bd->state != BOARD_READY)
+ return 0;
+
+ return snprintf(buf, PAGE_SIZE, "%x\n", ch->ch_c_lflag);
+}
+static DEVICE_ATTR(lflag, S_IRUSR, dgap_tty_lflag_show, NULL);
+
+static ssize_t dgap_tty_digi_flag_show(struct device *d,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct board_t *bd;
+ struct channel_t *ch;
+ struct un_t *un;
+
+ if (!d)
+ return 0;
+ un = dev_get_drvdata(d);
+ if (!un || un->magic != DGAP_UNIT_MAGIC)
+ return 0;
+ ch = un->un_ch;
+ if (!ch || ch->magic != DGAP_CHANNEL_MAGIC)
+ return 0;
+ bd = ch->ch_bd;
+ if (!bd || bd->magic != DGAP_BOARD_MAGIC)
+ return 0;
+ if (bd->state != BOARD_READY)
+ return 0;
+
+ return snprintf(buf, PAGE_SIZE, "%x\n", ch->ch_digi.digi_flags);
+}
+static DEVICE_ATTR(digi_flag, S_IRUSR, dgap_tty_digi_flag_show, NULL);
+
+static ssize_t dgap_tty_rxcount_show(struct device *d,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct board_t *bd;
+ struct channel_t *ch;
+ struct un_t *un;
+
+ if (!d)
+ return 0;
+ un = dev_get_drvdata(d);
+ if (!un || un->magic != DGAP_UNIT_MAGIC)
+ return 0;
+ ch = un->un_ch;
+ if (!ch || ch->magic != DGAP_CHANNEL_MAGIC)
+ return 0;
+ bd = ch->ch_bd;
+ if (!bd || bd->magic != DGAP_BOARD_MAGIC)
+ return 0;
+ if (bd->state != BOARD_READY)
+ return 0;
+
+ return snprintf(buf, PAGE_SIZE, "%ld\n", ch->ch_rxcount);
+}
+static DEVICE_ATTR(rxcount, S_IRUSR, dgap_tty_rxcount_show, NULL);
+
+static ssize_t dgap_tty_txcount_show(struct device *d,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct board_t *bd;
+ struct channel_t *ch;
+ struct un_t *un;
+
+ if (!d)
+ return 0;
+ un = dev_get_drvdata(d);
+ if (!un || un->magic != DGAP_UNIT_MAGIC)
+ return 0;
+ ch = un->un_ch;
+ if (!ch || ch->magic != DGAP_CHANNEL_MAGIC)
+ return 0;
+ bd = ch->ch_bd;
+ if (!bd || bd->magic != DGAP_BOARD_MAGIC)
+ return 0;
+ if (bd->state != BOARD_READY)
+ return 0;
+
+ return snprintf(buf, PAGE_SIZE, "%ld\n", ch->ch_txcount);
+}
+static DEVICE_ATTR(txcount, S_IRUSR, dgap_tty_txcount_show, NULL);
+
+static ssize_t dgap_tty_name_show(struct device *d,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct board_t *bd;
+ struct channel_t *ch;
+ struct un_t *un;
+ int cn;
+ int bn;
+ struct cnode *cptr;
+ int found = FALSE;
+ int ncount = 0;
+ int starto = 0;
+ int i;
+
+ if (!d)
+ return 0;
+ un = dev_get_drvdata(d);
+ if (!un || un->magic != DGAP_UNIT_MAGIC)
+ return 0;
+ ch = un->un_ch;
+ if (!ch || ch->magic != DGAP_CHANNEL_MAGIC)
+ return 0;
+ bd = ch->ch_bd;
+ if (!bd || bd->magic != DGAP_BOARD_MAGIC)
+ return 0;
+ if (bd->state != BOARD_READY)
+ return 0;
+
+ bn = bd->boardnum;
+ cn = ch->ch_portnum;
+
+ for (cptr = bd->bd_config; cptr; cptr = cptr->next) {
+
+ if ((cptr->type == BNODE) &&
+ ((cptr->u.board.type == APORT2_920P) ||
+ (cptr->u.board.type == APORT4_920P) ||
+ (cptr->u.board.type == APORT8_920P) ||
+ (cptr->u.board.type == PAPORT4) ||
+ (cptr->u.board.type == PAPORT8))) {
+
+ found = TRUE;
+ if (cptr->u.board.v_start)
+ starto = cptr->u.board.start;
+ else
+ starto = 1;
+ }
+
+ if (cptr->type == TNODE && found == TRUE) {
+ char *ptr1;
+
+ if (strstr(cptr->u.ttyname, "tty")) {
+ ptr1 = cptr->u.ttyname;
+ ptr1 += 3;
+ } else
+ ptr1 = cptr->u.ttyname;
+
+ for (i = 0; i < dgap_config_get_num_prts(bd); i++) {
+ if (cn != i)
+ continue;
+
+ return snprintf(buf, PAGE_SIZE, "%s%s%02d\n",
+ (un->un_type == DGAP_PRINT) ?
+ "pr" : "tty",
+ ptr1, i + starto);
+ }
+ }
+
+ if (cptr->type == CNODE) {
+
+ for (i = 0; i < cptr->u.conc.nport; i++) {
+ if (cn != (i + ncount))
+ continue;
+
+ return snprintf(buf, PAGE_SIZE, "%s%s%02ld\n",
+ (un->un_type == DGAP_PRINT) ?
+ "pr" : "tty",
+ cptr->u.conc.id,
+ i + (cptr->u.conc.v_start ?
+ cptr->u.conc.start : 1));
+ }
+
+ ncount += cptr->u.conc.nport;
+ }
+
+ if (cptr->type == MNODE) {
+
+ for (i = 0; i < cptr->u.module.nport; i++) {
+ if (cn != (i + ncount))
+ continue;
+
+ return snprintf(buf, PAGE_SIZE, "%s%s%02ld\n",
+ (un->un_type == DGAP_PRINT) ?
+ "pr" : "tty",
+ cptr->u.module.id,
+ i + (cptr->u.module.v_start ?
+ cptr->u.module.start : 1));
+ }
+
+ ncount += cptr->u.module.nport;
+ }
+ }
+
+ return snprintf(buf, PAGE_SIZE, "%s_dgap_%d_%d\n",
+ (un->un_type == DGAP_PRINT) ? "pr" : "tty", bn, cn);
+}
+static DEVICE_ATTR(custom_name, S_IRUSR, dgap_tty_name_show, NULL);
+
+static struct attribute *dgap_sysfs_tty_entries[] = {
+ &dev_attr_state.attr,
+ &dev_attr_baud.attr,
+ &dev_attr_msignals.attr,
+ &dev_attr_iflag.attr,
+ &dev_attr_cflag.attr,
+ &dev_attr_oflag.attr,
+ &dev_attr_lflag.attr,
+ &dev_attr_digi_flag.attr,
+ &dev_attr_rxcount.attr,
+ &dev_attr_txcount.attr,
+ &dev_attr_custom_name.attr,
+ NULL
+};
+
+
+/* this function creates the sys files that will export each signal status
+ * to sysfs each value will be put in a separate filename
+ */
+static void dgap_create_ports_sysfiles(struct board_t *bd)
+{
+ dev_set_drvdata(&bd->pdev->dev, bd);
+ device_create_file(&(bd->pdev->dev), &dev_attr_ports_state);
+ device_create_file(&(bd->pdev->dev), &dev_attr_ports_baud);
+ device_create_file(&(bd->pdev->dev), &dev_attr_ports_msignals);
+ device_create_file(&(bd->pdev->dev), &dev_attr_ports_iflag);
+ device_create_file(&(bd->pdev->dev), &dev_attr_ports_cflag);
+ device_create_file(&(bd->pdev->dev), &dev_attr_ports_oflag);
+ device_create_file(&(bd->pdev->dev), &dev_attr_ports_lflag);
+ device_create_file(&(bd->pdev->dev), &dev_attr_ports_digi_flag);
+ device_create_file(&(bd->pdev->dev), &dev_attr_ports_rxcount);
+ device_create_file(&(bd->pdev->dev), &dev_attr_ports_txcount);
+}
+
+/* removes all the sys files created for that port */
+static void dgap_remove_ports_sysfiles(struct board_t *bd)
+{
+ device_remove_file(&(bd->pdev->dev), &dev_attr_ports_state);
+ device_remove_file(&(bd->pdev->dev), &dev_attr_ports_baud);
+ device_remove_file(&(bd->pdev->dev), &dev_attr_ports_msignals);
+ device_remove_file(&(bd->pdev->dev), &dev_attr_ports_iflag);
+ device_remove_file(&(bd->pdev->dev), &dev_attr_ports_cflag);
+ device_remove_file(&(bd->pdev->dev), &dev_attr_ports_oflag);
+ device_remove_file(&(bd->pdev->dev), &dev_attr_ports_lflag);
+ device_remove_file(&(bd->pdev->dev), &dev_attr_ports_digi_flag);
+ device_remove_file(&(bd->pdev->dev), &dev_attr_ports_rxcount);
+ device_remove_file(&(bd->pdev->dev), &dev_attr_ports_txcount);
+}
+
+/*
+ * Copies the BIOS code from the user to the board,
+ * and starts the BIOS running.
+ */
+static void dgap_do_bios_load(struct board_t *brd, const u8 *ubios, int len)
+{
+ u8 __iomem *addr;
+ uint offset;
+ unsigned int i;
+
+ if (!brd || (brd->magic != DGAP_BOARD_MAGIC) || !brd->re_map_membase)
+ return;
+
+ addr = brd->re_map_membase;
+
+ /*
+ * clear POST area
+ */
+ for (i = 0; i < 16; i++)
+ writeb(0, addr + POSTAREA + i);
+
+ /*
+ * Download bios
+ */
+ offset = 0x1000;
+ memcpy_toio(addr + offset, ubios, len);
+
+ writel(0x0bf00401, addr);
+ writel(0, (addr + 4));
+
+ /* Clear the reset, and change states. */
+ writeb(FEPCLR, brd->re_map_port);
+}
+
+/*
+ * Checks to see if the BIOS completed running on the card.
+ */
+static int dgap_test_bios(struct board_t *brd)
+{
+ u8 __iomem *addr;
+ u16 word;
+ u16 err1;
+ u16 err2;
+
+ if (!brd || (brd->magic != DGAP_BOARD_MAGIC) || !brd->re_map_membase)
+ return -EINVAL;
+
+ addr = brd->re_map_membase;
+ word = readw(addr + POSTAREA);
+
+ /*
+ * It can take 5-6 seconds for a board to
+ * pass the bios self test and post results.
+ * Give it 10 seconds.
+ */
+ brd->wait_for_bios = 0;
+ while (brd->wait_for_bios < 1000) {
+ /* Check to see if BIOS thinks board is good. (GD). */
+ if (word == *(u16 *) "GD")
+ return 0;
+ msleep_interruptible(10);
+ brd->wait_for_bios++;
+ word = readw(addr + POSTAREA);
+ }
+
+ /* Gave up on board after too long of time taken */
+ err1 = readw(addr + SEQUENCE);
+ err2 = readw(addr + ERROR);
+ dev_warn(&brd->pdev->dev, "%s failed diagnostics. Error #(%x,%x).\n",
+ brd->name, err1, err2);
+ brd->state = BOARD_FAILED;
+ brd->dpastatus = BD_NOBIOS;
+
+ return -EIO;
+}
+
+/*
+ * Copies the FEP code from the user to the board,
+ * and starts the FEP running.
+ */
+static void dgap_do_fep_load(struct board_t *brd, const u8 *ufep, int len)
+{
+ u8 __iomem *addr;
+ uint offset;
+
+ if (!brd || (brd->magic != DGAP_BOARD_MAGIC) || !brd->re_map_membase)
+ return;
+
+ addr = brd->re_map_membase;
+
+ /*
+ * Download FEP
+ */
+ offset = 0x1000;
+ memcpy_toio(addr + offset, ufep, len);
+
+ /*
+ * If board is a concentrator product, we need to give
+ * it its config string describing how the concentrators look.
+ */
+ if ((brd->type == PCX) || (brd->type == PEPC)) {
+ u8 string[100];
+ u8 __iomem *config;
+ u8 *xconfig;
+ unsigned int i = 0;
+
+ xconfig = dgap_create_config_string(brd, string);
+
+ /* Write string to board memory */
+ config = addr + CONFIG;
+ for (; i < CONFIGSIZE; i++, config++, xconfig++) {
+ writeb(*xconfig, config);
+ if ((*xconfig & 0xff) == 0xff)
+ break;
+ }
+ }
+
+ writel(0xbfc01004, (addr + 0xc34));
+ writel(0x3, (addr + 0xc30));
+
+}
+
+/*
+ * Waits for the FEP to report thats its ready for us to use.
+ */
+static int dgap_test_fep(struct board_t *brd)
+{
+ u8 __iomem *addr;
+ u16 word;
+ u16 err1;
+ u16 err2;
+
+ if (!brd || (brd->magic != DGAP_BOARD_MAGIC) || !brd->re_map_membase)
+ return -EINVAL;
+
+ addr = brd->re_map_membase;
+ word = readw(addr + FEPSTAT);
+
+ /*
+ * It can take 2-3 seconds for the FEP to
+ * be up and running. Give it 5 secs.
+ */
+ brd->wait_for_fep = 0;
+ while (brd->wait_for_fep < 500) {
+ /* Check to see if FEP is up and running now. */
+ if (word == *(u16 *) "OS") {
+ /*
+ * Check to see if the board can support FEP5+ commands.
+ */
+ word = readw(addr + FEP5_PLUS);
+ if (word == *(u16 *) "5A")
+ brd->bd_flags |= BD_FEP5PLUS;
+
+ return 0;
+ }
+ msleep_interruptible(10);
+ brd->wait_for_fep++;
+ word = readw(addr + FEPSTAT);
+ }
+
+ /* Gave up on board after too long of time taken */
+ err1 = readw(addr + SEQUENCE);
+ err2 = readw(addr + ERROR);
+ dev_warn(&brd->pdev->dev,
+ "FEPOS for %s not functioning. Error #(%x,%x).\n",
+ brd->name, err1, err2);
+ brd->state = BOARD_FAILED;
+ brd->dpastatus = BD_NOFEP;
+
+ return -EIO;
+}
+
+/*
+ * Physically forces the FEP5 card to reset itself.
+ */
+static void dgap_do_reset_board(struct board_t *brd)
+{
+ u8 check;
+ u32 check1;
+ u32 check2;
+ unsigned int i;
+
+ if (!brd || (brd->magic != DGAP_BOARD_MAGIC) ||
+ !brd->re_map_membase || !brd->re_map_port)
+ return;
+
+ /* FEPRST does not vary among supported boards */
+ writeb(FEPRST, brd->re_map_port);
+
+ for (i = 0; i <= 1000; i++) {
+ check = readb(brd->re_map_port) & 0xe;
+ if (check == FEPRST)
+ break;
+ udelay(10);
+
+ }
+ if (i > 1000) {
+ dev_warn(&brd->pdev->dev,
+ "dgap: Board not resetting... Failing board.\n");
+ brd->state = BOARD_FAILED;
+ brd->dpastatus = BD_NOFEP;
+ return;
+ }
+
+ /*
+ * Make sure there really is memory out there.
+ */
+ writel(0xa55a3cc3, (brd->re_map_membase + LOWMEM));
+ writel(0x5aa5c33c, (brd->re_map_membase + HIGHMEM));
+ check1 = readl(brd->re_map_membase + LOWMEM);
+ check2 = readl(brd->re_map_membase + HIGHMEM);
+
+ if ((check1 != 0xa55a3cc3) || (check2 != 0x5aa5c33c)) {
+ dev_warn(&brd->pdev->dev,
+ "No memory at %p for board.\n",
+ brd->re_map_membase);
+ brd->state = BOARD_FAILED;
+ brd->dpastatus = BD_NOFEP;
+ return;
+ }
+}
+
+#ifdef DIGI_CONCENTRATORS_SUPPORTED
+/*
+ * Sends a concentrator image into the FEP5 board.
+ */
+static void dgap_do_conc_load(struct board_t *brd, u8 *uaddr, int len)
+{
+ char __iomem *vaddr;
+ u16 offset;
+ struct downld_t *to_dp;
+
+ if (!brd || (brd->magic != DGAP_BOARD_MAGIC) || !brd->re_map_membase)
+ return;
+
+ vaddr = brd->re_map_membase;
+
+ offset = readw((u16 *) (vaddr + DOWNREQ));
+ to_dp = (struct downld_t *) (vaddr + (int) offset);
+ memcpy_toio(to_dp, uaddr, len);
+
+ /* Tell card we have data for it */
+ writew(0, vaddr + (DOWNREQ));
+
+ brd->conc_dl_status = NO_PENDING_CONCENTRATOR_REQUESTS;
+}
+#endif
+
+#define EXPANSION_ROM_SIZE (64 * 1024)
+#define FEP5_ROM_MAGIC (0xFEFFFFFF)
+
+static void dgap_get_vpd(struct board_t *brd)
+{
+ u32 magic;
+ u32 base_offset;
+ u16 rom_offset;
+ u16 vpd_offset;
+ u16 image_length;
+ u16 i;
+ u8 byte1;
+ u8 byte2;
+
+ /*
+ * Poke the magic number at the PCI Rom Address location.
+ * If VPD is supported, the value read from that address
+ * will be non-zero.
+ */
+ magic = FEP5_ROM_MAGIC;
+ pci_write_config_dword(brd->pdev, PCI_ROM_ADDRESS, magic);
+ pci_read_config_dword(brd->pdev, PCI_ROM_ADDRESS, &magic);
+
+ /* VPD not supported, bail */
+ if (!magic)
+ return;
+
+ /*
+ * To get to the OTPROM memory, we have to send the boards base
+ * address or'ed with 1 into the PCI Rom Address location.
+ */
+ magic = brd->membase | 0x01;
+ pci_write_config_dword(brd->pdev, PCI_ROM_ADDRESS, magic);
+ pci_read_config_dword(brd->pdev, PCI_ROM_ADDRESS, &magic);
+
+ byte1 = readb(brd->re_map_membase);
+ byte2 = readb(brd->re_map_membase + 1);
+
+ /*
+ * If the board correctly swapped to the OTPROM memory,
+ * the first 2 bytes (header) should be 0x55, 0xAA
+ */
+ if (byte1 == 0x55 && byte2 == 0xAA) {
+
+ base_offset = 0;
+
+ /*
+ * We have to run through all the OTPROM memory looking
+ * for the VPD offset.
+ */
+ while (base_offset <= EXPANSION_ROM_SIZE) {
+
+ /*
+ * Lots of magic numbers here.
+ *
+ * The VPD offset is located inside the ROM Data
+ * Structure.
+ *
+ * We also have to remember the length of each
+ * ROM Data Structure, so we can "hop" to the next
+ * entry if the VPD isn't in the current
+ * ROM Data Structure.
+ */
+ rom_offset = readw(brd->re_map_membase +
+ base_offset + 0x18);
+ image_length = readw(brd->re_map_membase +
+ rom_offset + 0x10) * 512;
+ vpd_offset = readw(brd->re_map_membase +
+ rom_offset + 0x08);
+
+ /* Found the VPD entry */
+ if (vpd_offset)
+ break;
+
+ /* We didn't find a VPD entry, go to next ROM entry. */
+ base_offset += image_length;
+
+ byte1 = readb(brd->re_map_membase + base_offset);
+ byte2 = readb(brd->re_map_membase + base_offset + 1);
+
+ /*
+ * If the new ROM offset doesn't have 0x55, 0xAA
+ * as its header, we have run out of ROM.
+ */
+ if (byte1 != 0x55 || byte2 != 0xAA)
+ break;
+ }
+
+ /*
+ * If we have a VPD offset, then mark the board
+ * as having a valid VPD, and copy VPDSIZE (512) bytes of
+ * that VPD to the buffer we have in our board structure.
+ */
+ if (vpd_offset) {
+ brd->bd_flags |= BD_HAS_VPD;
+ for (i = 0; i < VPDSIZE; i++) {
+ brd->vpd[i] = readb(brd->re_map_membase +
+ vpd_offset + i);
+ }
+ }
+ }
+
+ /*
+ * We MUST poke the magic number at the PCI Rom Address location again.
+ * This makes the card report the regular board memory back to us,
+ * rather than the OTPROM memory.
+ */
+ magic = FEP5_ROM_MAGIC;
+ pci_write_config_dword(brd->pdev, PCI_ROM_ADDRESS, magic);
+}
+
+
+static ssize_t dgap_driver_version_show(struct device_driver *ddp, char *buf)
+{
+ return snprintf(buf, PAGE_SIZE, "%s\n", DG_PART);
+}
+static DRIVER_ATTR(version, S_IRUSR, dgap_driver_version_show, NULL);
+
+
+static ssize_t dgap_driver_boards_show(struct device_driver *ddp, char *buf)
+{
+ return snprintf(buf, PAGE_SIZE, "%d\n", dgap_numboards);
+}
+static DRIVER_ATTR(boards, S_IRUSR, dgap_driver_boards_show, NULL);
+
+
+static ssize_t dgap_driver_maxboards_show(struct device_driver *ddp, char *buf)
+{
+ return snprintf(buf, PAGE_SIZE, "%d\n", MAXBOARDS);
+}
+static DRIVER_ATTR(maxboards, S_IRUSR, dgap_driver_maxboards_show, NULL);
+
+
+static ssize_t dgap_driver_pollcounter_show(struct device_driver *ddp,
+ char *buf)
+{
+ return snprintf(buf, PAGE_SIZE, "%ld\n", dgap_poll_counter);
+}
+static DRIVER_ATTR(pollcounter, S_IRUSR, dgap_driver_pollcounter_show, NULL);
+
+static ssize_t dgap_driver_pollrate_show(struct device_driver *ddp, char *buf)
+{
+ return snprintf(buf, PAGE_SIZE, "%dms\n", dgap_poll_tick);
+}
+
+static ssize_t dgap_driver_pollrate_store(struct device_driver *ddp,
+ const char *buf, size_t count)
+{
+ if (sscanf(buf, "%d\n", &dgap_poll_tick) != 1)
+ return -EINVAL;
+ return count;
+}
+static DRIVER_ATTR(pollrate, (S_IRUSR | S_IWUSR), dgap_driver_pollrate_show,
+ dgap_driver_pollrate_store);
+
+
+static int dgap_create_driver_sysfiles(struct pci_driver *dgap_driver)
+{
+ int rc = 0;
+ struct device_driver *driverfs = &dgap_driver->driver;
+
+ rc |= driver_create_file(driverfs, &driver_attr_version);
+ rc |= driver_create_file(driverfs, &driver_attr_boards);
+ rc |= driver_create_file(driverfs, &driver_attr_maxboards);
+ rc |= driver_create_file(driverfs, &driver_attr_pollrate);
+ rc |= driver_create_file(driverfs, &driver_attr_pollcounter);
+
+ return rc;
+}
+
+static void dgap_remove_driver_sysfiles(struct pci_driver *dgap_driver)
+{
+ struct device_driver *driverfs = &dgap_driver->driver;
+
+ driver_remove_file(driverfs, &driver_attr_version);
+ driver_remove_file(driverfs, &driver_attr_boards);
+ driver_remove_file(driverfs, &driver_attr_maxboards);
+ driver_remove_file(driverfs, &driver_attr_pollrate);
+ driver_remove_file(driverfs, &driver_attr_pollcounter);
+}
+
+static struct attribute_group dgap_tty_attribute_group = {
+ .name = NULL,
+ .attrs = dgap_sysfs_tty_entries,
+};
+
+static void dgap_create_tty_sysfs(struct un_t *un, struct device *c)
+{
+ int ret;
+
+ ret = sysfs_create_group(&c->kobj, &dgap_tty_attribute_group);
+ if (ret)
+ return;
+
+ dev_set_drvdata(c, un);
+
+}
+
+static void dgap_remove_tty_sysfs(struct device *c)
+{
+ sysfs_remove_group(&c->kobj, &dgap_tty_attribute_group);
+}
+
+/*
+ * Create pr and tty device entries
+ */
+static int dgap_tty_register_ports(struct board_t *brd)
+{
+ struct channel_t *ch;
+ int i;
+ int ret;
+
+ brd->serial_ports = kcalloc(brd->nasync, sizeof(*brd->serial_ports),
+ GFP_KERNEL);
+ if (!brd->serial_ports)
+ return -ENOMEM;
+
+ brd->printer_ports = kcalloc(brd->nasync, sizeof(*brd->printer_ports),
+ GFP_KERNEL);
+ if (!brd->printer_ports) {
+ ret = -ENOMEM;
+ goto free_serial_ports;
+ }
+
+ for (i = 0; i < brd->nasync; i++) {
+ tty_port_init(&brd->serial_ports[i]);
+ tty_port_init(&brd->printer_ports[i]);
+ }
+
+ ch = brd->channels[0];
+ for (i = 0; i < brd->nasync; i++, ch = brd->channels[i]) {
+
+ struct device *classp;
+
+ classp = tty_port_register_device(&brd->serial_ports[i],
+ brd->serial_driver,
+ i, NULL);
+
+ if (IS_ERR(classp)) {
+ ret = PTR_ERR(classp);
+ goto unregister_ttys;
+ }
+
+ dgap_create_tty_sysfs(&ch->ch_tun, classp);
+ ch->ch_tun.un_sysfs = classp;
+
+ classp = tty_port_register_device(&brd->printer_ports[i],
+ brd->print_driver,
+ i, NULL);
+
+ if (IS_ERR(classp)) {
+ ret = PTR_ERR(classp);
+ goto unregister_ttys;
+ }
+
+ dgap_create_tty_sysfs(&ch->ch_pun, classp);
+ ch->ch_pun.un_sysfs = classp;
+ }
+ dgap_create_ports_sysfiles(brd);
+
+ return 0;
+
+unregister_ttys:
+ while (i >= 0) {
+ ch = brd->channels[i];
+ if (ch->ch_tun.un_sysfs) {
+ dgap_remove_tty_sysfs(ch->ch_tun.un_sysfs);
+ tty_unregister_device(brd->serial_driver, i);
+ }
+
+ if (ch->ch_pun.un_sysfs) {
+ dgap_remove_tty_sysfs(ch->ch_pun.un_sysfs);
+ tty_unregister_device(brd->print_driver, i);
+ }
+ i--;
+ }
+
+ for (i = 0; i < brd->nasync; i++) {
+ tty_port_destroy(&brd->serial_ports[i]);
+ tty_port_destroy(&brd->printer_ports[i]);
+ }
+
+ kfree(brd->printer_ports);
+ brd->printer_ports = NULL;
+
+free_serial_ports:
+ kfree(brd->serial_ports);
+ brd->serial_ports = NULL;
+
+ return ret;
+}
+
+/*
+ * dgap_cleanup_tty()
+ *
+ * Uninitialize the TTY portion of this driver. Free all memory and
+ * resources.
+ */
+static void dgap_cleanup_tty(struct board_t *brd)
+{
+ struct device *dev;
+ unsigned int i;
+
+ for (i = 0; i < brd->nasync; i++) {
+ tty_port_destroy(&brd->serial_ports[i]);
+ dev = brd->channels[i]->ch_tun.un_sysfs;
+ dgap_remove_tty_sysfs(dev);
+ tty_unregister_device(brd->serial_driver, i);
+ }
+ tty_unregister_driver(brd->serial_driver);
+ put_tty_driver(brd->serial_driver);
+ kfree(brd->serial_ports);
+
+ for (i = 0; i < brd->nasync; i++) {
+ tty_port_destroy(&brd->printer_ports[i]);
+ dev = brd->channels[i]->ch_pun.un_sysfs;
+ dgap_remove_tty_sysfs(dev);
+ tty_unregister_device(brd->print_driver, i);
+ }
+ tty_unregister_driver(brd->print_driver);
+ put_tty_driver(brd->print_driver);
+ kfree(brd->printer_ports);
+}
+
+static int dgap_request_irq(struct board_t *brd)
+{
+ int rc;
+
+ if (!brd || brd->magic != DGAP_BOARD_MAGIC)
+ return -ENODEV;
+
+ /*
+ * Set up our interrupt handler if we are set to do interrupts.
+ */
+ if (dgap_config_get_useintr(brd) && brd->irq) {
+
+ rc = request_irq(brd->irq, dgap_intr, IRQF_SHARED, "DGAP", brd);
+
+ if (!rc)
+ brd->intr_used = 1;
+ }
+ return 0;
+}
+
+static void dgap_free_irq(struct board_t *brd)
+{
+ if (brd->intr_used && brd->irq)
+ free_irq(brd->irq, brd);
+}
+
+static int dgap_firmware_load(struct pci_dev *pdev, int card_type,
+ struct board_t *brd)
+{
+ const struct firmware *fw;
+ char *tmp_ptr;
+ int ret;
+ char *dgap_config_buf;
+
+ dgap_get_vpd(brd);
+ dgap_do_reset_board(brd);
+
+ if (fw_info[card_type].conf_name) {
+ ret = reject_firmware(&fw, fw_info[card_type].conf_name,
+ &pdev->dev);
+ if (ret) {
+ dev_err(&pdev->dev, "config file %s not found\n",
+ fw_info[card_type].conf_name);
+ return ret;
+ }
+
+ dgap_config_buf = kzalloc(fw->size + 1, GFP_KERNEL);
+ if (!dgap_config_buf) {
+ release_firmware(fw);
+ return -ENOMEM;
+ }
+
+ memcpy(dgap_config_buf, fw->data, fw->size);
+ release_firmware(fw);
+
+ /*
+ * preserve dgap_config_buf
+ * as dgap_parsefile would
+ * otherwise alter it.
+ */
+ tmp_ptr = dgap_config_buf;
+
+ if (dgap_parsefile(&tmp_ptr) != 0) {
+ kfree(dgap_config_buf);
+ return -EINVAL;
+ }
+ kfree(dgap_config_buf);
+ }
+
+ /*
+ * Match this board to a config the user created for us.
+ */
+ brd->bd_config =
+ dgap_find_config(brd->type, brd->pci_bus, brd->pci_slot);
+
+ /*
+ * Because the 4 port Xr products share the same PCI ID
+ * as the 8 port Xr products, if we receive a NULL config
+ * back, and this is a PAPORT8 board, retry with a
+ * PAPORT4 attempt as well.
+ */
+ if (brd->type == PAPORT8 && !brd->bd_config)
+ brd->bd_config =
+ dgap_find_config(PAPORT4, brd->pci_bus, brd->pci_slot);
+
+ if (!brd->bd_config) {
+ dev_err(&pdev->dev, "No valid configuration found\n");
+ return -EINVAL;
+ }
+
+ if (fw_info[card_type].bios_name) {
+ ret = reject_firmware(&fw, fw_info[card_type].bios_name,
+ &pdev->dev);
+ if (ret) {
+ dev_err(&pdev->dev, "bios file %s not found\n",
+ fw_info[card_type].bios_name);
+ return ret;
+ }
+ dgap_do_bios_load(brd, fw->data, fw->size);
+ release_firmware(fw);
+
+ /* Wait for BIOS to test board... */
+ ret = dgap_test_bios(brd);
+ if (ret)
+ return ret;
+ }
+
+ if (fw_info[card_type].fep_name) {
+ ret = reject_firmware(&fw, fw_info[card_type].fep_name,
+ &pdev->dev);
+ if (ret) {
+ dev_err(&pdev->dev, "dgap: fep file %s not found\n",
+ fw_info[card_type].fep_name);
+ return ret;
+ }
+ dgap_do_fep_load(brd, fw->data, fw->size);
+ release_firmware(fw);
+
+ /* Wait for FEP to load on board... */
+ ret = dgap_test_fep(brd);
+ if (ret)
+ return ret;
+ }
+
+#ifdef DIGI_CONCENTRATORS_SUPPORTED
+ /*
+ * If this is a CX or EPCX, we need to see if the firmware
+ * is requesting a concentrator image from us.
+ */
+ if ((bd->type == PCX) || (bd->type == PEPC)) {
+ chk_addr = (u16 *) (vaddr + DOWNREQ);
+ /* Nonzero if FEP is requesting concentrator image. */
+ check = readw(chk_addr);
+ vaddr = brd->re_map_membase;
+ }
+
+ if (fw_info[card_type].con_name && check && vaddr) {
+ ret = reject_firmware(&fw, fw_info[card_type].con_name,
+ &pdev->dev);
+ if (ret) {
+ dev_err(&pdev->dev, "conc file %s not found\n",
+ fw_info[card_type].con_name);
+ return ret;
+ }
+ /* Put concentrator firmware loading code here */
+ offset = readw((u16 *) (vaddr + DOWNREQ));
+ memcpy_toio(offset, fw->data, fw->size);
+
+ dgap_do_conc_load(brd, (char *)fw->data, fw->size)
+ release_firmware(fw);
+ }
+#endif
+
+ return 0;
+}
+
+/*
+ * dgap_tty_init()
+ *
+ * Init the tty subsystem. Called once per board after board has been
+ * downloaded and init'ed.
+ */
+static int dgap_tty_init(struct board_t *brd)
+{
+ int i;
+ int tlw;
+ uint true_count;
+ u8 __iomem *vaddr;
+ u8 modem;
+ struct channel_t *ch;
+ struct bs_t __iomem *bs;
+ struct cm_t __iomem *cm;
+ int ret;
+
+ /*
+ * Initialize board structure elements.
+ */
+
+ vaddr = brd->re_map_membase;
+ true_count = readw((vaddr + NCHAN));
+
+ brd->nasync = dgap_config_get_num_prts(brd);
+
+ if (!brd->nasync)
+ brd->nasync = brd->maxports;
+
+ if (brd->nasync > brd->maxports)
+ brd->nasync = brd->maxports;
+
+ if (true_count != brd->nasync) {
+ dev_warn(&brd->pdev->dev,
+ "%s configured for %d ports, has %d ports.\n",
+ brd->name, brd->nasync, true_count);
+
+ if ((brd->type == PPCM) &&
+ (true_count == 64 || true_count == 0)) {
+ dev_warn(&brd->pdev->dev,
+ "Please make SURE the EBI cable running from the card\n");
+ dev_warn(&brd->pdev->dev,
+ "to each EM module is plugged into EBI IN!\n");
+ }
+
+ brd->nasync = true_count;
+
+ /* If no ports, don't bother going any further */
+ if (!brd->nasync) {
+ brd->state = BOARD_FAILED;
+ brd->dpastatus = BD_NOFEP;
+ return -EIO;
+ }
+ }
+
+ /*
+ * Allocate channel memory that might not have been allocated
+ * when the driver was first loaded.
+ */
+ for (i = 0; i < brd->nasync; i++) {
+ brd->channels[i] =
+ kzalloc(sizeof(struct channel_t), GFP_KERNEL);
+ if (!brd->channels[i]) {
+ ret = -ENOMEM;
+ goto free_chan;
+ }
+ }
+
+ ch = brd->channels[0];
+ vaddr = brd->re_map_membase;
+
+ bs = (struct bs_t __iomem *) ((ulong) vaddr + CHANBUF);
+ cm = (struct cm_t __iomem *) ((ulong) vaddr + CMDBUF);
+
+ brd->bd_bs = bs;
+
+ /* Set up channel variables */
+ for (i = 0; i < brd->nasync; i++, ch = brd->channels[i], bs++) {
+
+ spin_lock_init(&ch->ch_lock);
+
+ /* Store all our magic numbers */
+ ch->magic = DGAP_CHANNEL_MAGIC;
+ ch->ch_tun.magic = DGAP_UNIT_MAGIC;
+ ch->ch_tun.un_type = DGAP_SERIAL;
+ ch->ch_tun.un_ch = ch;
+ ch->ch_tun.un_dev = i;
+
+ ch->ch_pun.magic = DGAP_UNIT_MAGIC;
+ ch->ch_pun.un_type = DGAP_PRINT;
+ ch->ch_pun.un_ch = ch;
+ ch->ch_pun.un_dev = i;
+
+ ch->ch_vaddr = vaddr;
+ ch->ch_bs = bs;
+ ch->ch_cm = cm;
+ ch->ch_bd = brd;
+ ch->ch_portnum = i;
+ ch->ch_digi = dgap_digi_init;
+
+ /*
+ * Set up digi dsr and dcd bits based on altpin flag.
+ */
+ if (dgap_config_get_altpin(brd)) {
+ ch->ch_dsr = DM_CD;
+ ch->ch_cd = DM_DSR;
+ ch->ch_digi.digi_flags |= DIGI_ALTPIN;
+ } else {
+ ch->ch_cd = DM_CD;
+ ch->ch_dsr = DM_DSR;
+ }
+
+ ch->ch_taddr = vaddr + (ioread16(&(ch->ch_bs->tx_seg)) << 4);
+ ch->ch_raddr = vaddr + (ioread16(&(ch->ch_bs->rx_seg)) << 4);
+ ch->ch_tx_win = 0;
+ ch->ch_rx_win = 0;
+ ch->ch_tsize = readw(&(ch->ch_bs->tx_max)) + 1;
+ ch->ch_rsize = readw(&(ch->ch_bs->rx_max)) + 1;
+ ch->ch_tstart = 0;
+ ch->ch_rstart = 0;
+
+ /*
+ * Set queue water marks, interrupt mask,
+ * and general tty parameters.
+ */
+ tlw = ch->ch_tsize >= 2000 ? ((ch->ch_tsize * 5) / 8) :
+ ch->ch_tsize / 2;
+ ch->ch_tlw = tlw;
+
+ dgap_cmdw(ch, STLOW, tlw, 0);
+
+ dgap_cmdw(ch, SRLOW, ch->ch_rsize / 2, 0);
+
+ dgap_cmdw(ch, SRHIGH, 7 * ch->ch_rsize / 8, 0);
+
+ ch->ch_mistat = readb(&(ch->ch_bs->m_stat));
+
+ init_waitqueue_head(&ch->ch_flags_wait);
+ init_waitqueue_head(&ch->ch_tun.un_flags_wait);
+ init_waitqueue_head(&ch->ch_pun.un_flags_wait);
+
+ /* Turn on all modem interrupts for now */
+ modem = (DM_CD | DM_DSR | DM_CTS | DM_RI);
+ writeb(modem, &(ch->ch_bs->m_int));
+
+ /*
+ * Set edelay to 0 if interrupts are turned on,
+ * otherwise set edelay to the usual 100.
+ */
+ if (brd->intr_used)
+ writew(0, &(ch->ch_bs->edelay));
+ else
+ writew(100, &(ch->ch_bs->edelay));
+
+ writeb(1, &(ch->ch_bs->idata));
+ }
+
+ return 0;
+
+free_chan:
+ while (--i >= 0) {
+ kfree(brd->channels[i]);
+ brd->channels[i] = NULL;
+ }
+ return ret;
+}
+
+/*
+ * dgap_tty_free()
+ *
+ * Free the channles which are allocated in dgap_tty_init().
+ */
+static void dgap_tty_free(struct board_t *brd)
+{
+ int i;
+
+ for (i = 0; i < brd->nasync; i++)
+ kfree(brd->channels[i]);
+}
+
+static int dgap_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
+{
+ int rc;
+ struct board_t *brd;
+
+ if (dgap_numboards >= MAXBOARDS)
+ return -EPERM;
+
+ rc = pci_enable_device(pdev);
+ if (rc)
+ return -EIO;
+
+ brd = dgap_found_board(pdev, ent->driver_data, dgap_numboards);
+ if (IS_ERR(brd))
+ return PTR_ERR(brd);
+
+ rc = dgap_firmware_load(pdev, ent->driver_data, brd);
+ if (rc)
+ goto cleanup_brd;
+
+ rc = dgap_alloc_flipbuf(brd);
+ if (rc)
+ goto cleanup_brd;
+
+ rc = dgap_tty_register(brd);
+ if (rc)
+ goto free_flipbuf;
+
+ rc = dgap_request_irq(brd);
+ if (rc)
+ goto unregister_tty;
+
+ /*
+ * Do tty device initialization.
+ */
+ rc = dgap_tty_init(brd);
+ if (rc < 0)
+ goto free_irq;
+
+ rc = dgap_tty_register_ports(brd);
+ if (rc)
+ goto tty_free;
+
+ brd->state = BOARD_READY;
+ brd->dpastatus = BD_RUNNING;
+
+ dgap_board[dgap_numboards++] = brd;
+
+ return 0;
+
+tty_free:
+ dgap_tty_free(brd);
+free_irq:
+ dgap_free_irq(brd);
+unregister_tty:
+ dgap_tty_unregister(brd);
+free_flipbuf:
+ dgap_free_flipbuf(brd);
+cleanup_brd:
+ dgap_cleanup_nodes();
+ dgap_unmap(brd);
+ kfree(brd);
+
+ return rc;
+}
+
+static void dgap_remove_one(struct pci_dev *dev)
+{
+ /* Do Nothing */
+}
+
+static struct pci_driver dgap_driver = {
+ .name = "dgap",
+ .probe = dgap_init_one,
+ .id_table = dgap_pci_tbl,
+ .remove = dgap_remove_one,
+};
+
+/*
+ * Start of driver.
+ */
+static int dgap_start(void)
+{
+ int rc;
+ unsigned long flags;
+ struct device *device;
+
+ dgap_numboards = 0;
+
+ pr_info("For the tools package please visit http://www.digi.com\n");
+
+ /*
+ * Register our base character device into the kernel.
+ */
+
+ /*
+ * Register management/dpa devices
+ */
+ rc = register_chrdev(DIGI_DGAP_MAJOR, "dgap", &dgap_board_fops);
+ if (rc < 0)
+ return rc;
+
+ dgap_class = class_create(THIS_MODULE, "dgap_mgmt");
+ if (IS_ERR(dgap_class)) {
+ rc = PTR_ERR(dgap_class);
+ goto failed_class;
+ }
+
+ device = device_create(dgap_class, NULL,
+ MKDEV(DIGI_DGAP_MAJOR, 0),
+ NULL, "dgap_mgmt");
+ if (IS_ERR(device)) {
+ rc = PTR_ERR(device);
+ goto failed_device;
+ }
+
+ /* Start the poller */
+ spin_lock_irqsave(&dgap_poll_lock, flags);
+ setup_timer(&dgap_poll_timer, dgap_poll_handler, 0);
+ dgap_poll_timer.data = 0;
+ dgap_poll_time = jiffies + dgap_jiffies_from_ms(dgap_poll_tick);
+ dgap_poll_timer.expires = dgap_poll_time;
+ spin_unlock_irqrestore(&dgap_poll_lock, flags);
+
+ add_timer(&dgap_poll_timer);
+
+ return rc;
+
+failed_device:
+ class_destroy(dgap_class);
+failed_class:
+ unregister_chrdev(DIGI_DGAP_MAJOR, "dgap");
+ return rc;
+}
+
+static void dgap_stop(void)
+{
+ unsigned long lock_flags;
+
+ spin_lock_irqsave(&dgap_poll_lock, lock_flags);
+ dgap_poll_stop = 1;
+ spin_unlock_irqrestore(&dgap_poll_lock, lock_flags);
+
+ del_timer_sync(&dgap_poll_timer);
+
+ device_destroy(dgap_class, MKDEV(DIGI_DGAP_MAJOR, 0));
+ class_destroy(dgap_class);
+ unregister_chrdev(DIGI_DGAP_MAJOR, "dgap");
+}
+
+/*
+ * dgap_cleanup_board()
+ *
+ * Free all the memory associated with a board
+ */
+static void dgap_cleanup_board(struct board_t *brd)
+{
+ unsigned int i;
+
+ if (!brd || brd->magic != DGAP_BOARD_MAGIC)
+ return;
+
+ dgap_free_irq(brd);
+
+ tasklet_kill(&brd->helper_tasklet);
+
+ dgap_unmap(brd);
+
+ /* Free all allocated channels structs */
+ for (i = 0; i < MAXPORTS ; i++)
+ kfree(brd->channels[i]);
+
+ kfree(brd->flipbuf);
+ kfree(brd->flipflagbuf);
+
+ dgap_board[brd->boardnum] = NULL;
+
+ kfree(brd);
+}
+
+
+/************************************************************************
+ *
+ * Driver load/unload functions
+ *
+ ************************************************************************/
+
+/*
+ * init_module()
+ *
+ * Module load. This is where it all starts.
+ */
+static int dgap_init_module(void)
+{
+ int rc;
+
+ pr_info("%s, Digi International Part Number %s\n", DG_NAME, DG_PART);
+
+ rc = dgap_start();
+ if (rc)
+ return rc;
+
+ rc = pci_register_driver(&dgap_driver);
+ if (rc)
+ goto err_stop;
+
+ rc = dgap_create_driver_sysfiles(&dgap_driver);
+ if (rc)
+ goto err_unregister;
+
+ dgap_driver_state = DRIVER_READY;
+
+ return 0;
+
+err_unregister:
+ pci_unregister_driver(&dgap_driver);
+err_stop:
+ dgap_stop();
+
+ return rc;
+}
+
+/*
+ * dgap_cleanup_module()
+ *
+ * Module unload. This is where it all ends.
+ */
+static void dgap_cleanup_module(void)
+{
+ unsigned int i;
+ ulong lock_flags;
+
+ spin_lock_irqsave(&dgap_poll_lock, lock_flags);
+ dgap_poll_stop = 1;
+ spin_unlock_irqrestore(&dgap_poll_lock, lock_flags);
+
+ /* Turn off poller right away. */
+ del_timer_sync(&dgap_poll_timer);
+
+ dgap_remove_driver_sysfiles(&dgap_driver);
+
+ device_destroy(dgap_class, MKDEV(DIGI_DGAP_MAJOR, 0));
+ class_destroy(dgap_class);
+ unregister_chrdev(DIGI_DGAP_MAJOR, "dgap");
+
+ for (i = 0; i < dgap_numboards; ++i) {
+ dgap_remove_ports_sysfiles(dgap_board[i]);
+ dgap_cleanup_tty(dgap_board[i]);
+ dgap_cleanup_board(dgap_board[i]);
+ }
+
+ dgap_cleanup_nodes();
+
+ if (dgap_numboards)
+ pci_unregister_driver(&dgap_driver);
+}
+
+module_init(dgap_init_module);
+module_exit(dgap_cleanup_module);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Digi International, http://www.digi.com");
+MODULE_DESCRIPTION("Driver for the Digi International EPCA PCI based product line");
+MODULE_SUPPORTED_DEVICE("dgap");
diff --git a/drivers/staging/dgap/dgap.h b/drivers/staging/dgap/dgap.h
new file mode 100644
index 000000000..a2e5b26c6
--- /dev/null
+++ b/drivers/staging/dgap/dgap.h
@@ -0,0 +1,1233 @@
+/*
+ * Copyright 2003 Digi International (www.digi.com)
+ * Scott H Kilau <Scott_Kilau at digi dot 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, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED; 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.
+ *
+ * NOTE: THIS IS A SHARED HEADER. DO NOT CHANGE CODING STYLE!!!
+ *
+ *************************************************************************
+ *
+ * Driver includes
+ *
+ *************************************************************************/
+
+#ifndef __DGAP_DRIVER_H
+#define __DGAP_DRIVER_H
+
+#include <linux/types.h> /* To pick up the varions Linux types */
+#include <linux/tty.h> /* To pick up the various tty structs/defines */
+#include <linux/interrupt.h> /* For irqreturn_t type */
+
+#ifndef TRUE
+# define TRUE 1
+#endif
+
+#ifndef FALSE
+# define FALSE 0
+#endif
+
+#if !defined(TTY_FLIPBUF_SIZE)
+# define TTY_FLIPBUF_SIZE 512
+#endif
+
+/*************************************************************************
+ *
+ * Driver defines
+ *
+ *************************************************************************/
+
+/*
+ * Driver identification
+ */
+#define DG_NAME "dgap-1.3-16"
+#define DG_PART "40002347_C"
+#define DRVSTR "dgap"
+
+/*
+ * defines from dgap_pci.h
+ */
+#define PCIMAX 32 /* maximum number of PCI boards */
+
+#define DIGI_VID 0x114F
+
+#define PCI_DEV_EPC_DID 0x0002
+#define PCI_DEV_XEM_DID 0x0004
+#define PCI_DEV_XR_DID 0x0005
+#define PCI_DEV_CX_DID 0x0006
+#define PCI_DEV_XRJ_DID 0x0009 /* PLX-based Xr adapter */
+#define PCI_DEV_XR_IBM_DID 0x0011 /* IBM 8-port Async Adapter */
+#define PCI_DEV_XR_BULL_DID 0x0013 /* BULL 8-port Async Adapter */
+#define PCI_DEV_XR_SAIP_DID 0x001c /* SAIP card - Xr adapter */
+#define PCI_DEV_XR_422_DID 0x0012 /* Xr-422 */
+#define PCI_DEV_920_2_DID 0x0034 /* XR-Plus 920 K, 2 port */
+#define PCI_DEV_920_4_DID 0x0026 /* XR-Plus 920 K, 4 port */
+#define PCI_DEV_920_8_DID 0x0027 /* XR-Plus 920 K, 8 port */
+#define PCI_DEV_EPCJ_DID 0x000a /* PLX 9060 chip for PCI */
+#define PCI_DEV_CX_IBM_DID 0x001b /* IBM 128-port Async Adapter */
+#define PCI_DEV_920_8_HP_DID 0x0058 /* HP XR-Plus 920 K, 8 port */
+#define PCI_DEV_XEM_HP_DID 0x0059 /* HP Xem PCI */
+
+#define PCI_DEV_XEM_NAME "AccelePort XEM"
+#define PCI_DEV_CX_NAME "AccelePort CX"
+#define PCI_DEV_XR_NAME "AccelePort Xr"
+#define PCI_DEV_XRJ_NAME "AccelePort Xr (PLX)"
+#define PCI_DEV_XR_SAIP_NAME "AccelePort Xr (SAIP)"
+#define PCI_DEV_920_2_NAME "AccelePort Xr920 2 port"
+#define PCI_DEV_920_4_NAME "AccelePort Xr920 4 port"
+#define PCI_DEV_920_8_NAME "AccelePort Xr920 8 port"
+#define PCI_DEV_XR_422_NAME "AccelePort Xr 422"
+#define PCI_DEV_EPCJ_NAME "AccelePort EPC (PLX)"
+#define PCI_DEV_XR_BULL_NAME "AccelePort Xr (BULL)"
+#define PCI_DEV_XR_IBM_NAME "AccelePort Xr (IBM)"
+#define PCI_DEV_CX_IBM_NAME "AccelePort CX (IBM)"
+#define PCI_DEV_920_8_HP_NAME "AccelePort Xr920 8 port (HP)"
+#define PCI_DEV_XEM_HP_NAME "AccelePort XEM (HP)"
+
+/*
+ * On the PCI boards, there is no IO space allocated
+ * The I/O registers will be in the first 3 bytes of the
+ * upper 2MB of the 4MB memory space. The board memory
+ * will be mapped into the low 2MB of the 4MB memory space
+ */
+
+/* Potential location of PCI Bios from E0000 to FFFFF*/
+#define PCI_BIOS_SIZE 0x00020000
+
+/* Size of Memory and I/O for PCI (4MB) */
+#define PCI_RAM_SIZE 0x00400000
+
+/* Size of Memory (2MB) */
+#define PCI_MEM_SIZE 0x00200000
+
+/* Max PCI Window Size (2MB) */
+#define PCI_WIN_SIZE 0x00200000
+
+#define PCI_WIN_SHIFT 21 /* 21 bits max */
+
+/* Offset of I/0 in Memory (2MB) */
+#define PCI_IO_OFFSET 0x00200000
+
+/* Size of IO (2MB) */
+#define PCI_IO_SIZE_DGAP 0x00200000
+
+/* Number of boards we support at once. */
+#define MAXBOARDS 32
+#define MAXPORTS 224
+#define MAXTTYNAMELEN 200
+
+/* Our 3 magic numbers for our board, channel and unit structs */
+#define DGAP_BOARD_MAGIC 0x5c6df104
+#define DGAP_CHANNEL_MAGIC 0x6c6df104
+#define DGAP_UNIT_MAGIC 0x7c6df104
+
+/* Serial port types */
+#define DGAP_SERIAL 0
+#define DGAP_PRINT 1
+
+#define SERIAL_TYPE_NORMAL 1
+
+/* 4 extra for alignment play space */
+#define WRITEBUFLEN ((4096) + 4)
+#define MYFLIPLEN N_TTY_BUF_SIZE
+
+#define SBREAK_TIME 0x25
+#define U2BSIZE 0x400
+
+#define dgap_jiffies_from_ms(a) (((a) * HZ) / 1000)
+
+/*
+ * Our major for the mgmt devices.
+ *
+ * We can use 22, because Digi was allocated 22 and 23 for the epca driver.
+ * 22 has now become obsolete now that the "cu" devices have
+ * been removed from 2.6.
+ * Also, this *IS* the epca driver, just PCI only now.
+ */
+#ifndef DIGI_DGAP_MAJOR
+# define DIGI_DGAP_MAJOR 22
+#endif
+
+/*
+ * The parameters we use to define the periods of the moving averages.
+ */
+#define MA_PERIOD (HZ / 10)
+#define SMA_DUR (1 * HZ)
+#define EMA_DUR (1 * HZ)
+#define SMA_NPERIODS (SMA_DUR / MA_PERIOD)
+#define EMA_NPERIODS (EMA_DUR / MA_PERIOD)
+
+/*
+ * Define a local default termios struct. All ports will be created
+ * with this termios initially. This is the same structure that is defined
+ * as the default in tty_io.c with the same settings overriden as in serial.c
+ *
+ * In short, this should match the internal serial ports' defaults.
+ */
+#define DEFAULT_IFLAGS (ICRNL | IXON)
+#define DEFAULT_OFLAGS (OPOST | ONLCR)
+#define DEFAULT_CFLAGS (B9600 | CS8 | CREAD | HUPCL | CLOCAL)
+#define DEFAULT_LFLAGS (ISIG | ICANON | ECHO | ECHOE | ECHOK | \
+ ECHOCTL | ECHOKE | IEXTEN)
+
+#ifndef _POSIX_VDISABLE
+#define _POSIX_VDISABLE ('\0')
+#endif
+
+#define SNIFF_MAX 65536 /* Sniff buffer size (2^n) */
+#define SNIFF_MASK (SNIFF_MAX - 1) /* Sniff wrap mask */
+
+#define VPDSIZE (512)
+
+/************************************************************************
+ * FEP memory offsets
+ ************************************************************************/
+#define START 0x0004L /* Execution start address */
+
+#define CMDBUF 0x0d10L /* Command (cm_t) structure offset */
+#define CMDSTART 0x0400L /* Start of command buffer */
+#define CMDMAX 0x0800L /* End of command buffer */
+
+#define EVBUF 0x0d18L /* Event (ev_t) structure */
+#define EVSTART 0x0800L /* Start of event buffer */
+#define EVMAX 0x0c00L /* End of event buffer */
+#define FEP5_PLUS 0x0E40 /* ASCII '5' and ASCII 'A' is here */
+#define ECS_SEG 0x0E44 /* Segment of the extended */
+ /* channel structure */
+#define LINE_SPEED 0x10 /* Offset into ECS_SEG for line */
+ /* speed if the fep has extended */
+ /* capabilities */
+
+/* BIOS MAGIC SPOTS */
+#define ERROR 0x0C14L /* BIOS error code */
+#define SEQUENCE 0x0C12L /* BIOS sequence indicator */
+#define POSTAREA 0x0C00L /* POST complete message area */
+
+/* FEP MAGIC SPOTS */
+#define FEPSTAT POSTAREA /* OS here when FEP comes up */
+#define NCHAN 0x0C02L /* number of ports FEP sees */
+#define PANIC 0x0C10L /* PANIC area for FEP */
+#define KMEMEM 0x0C30L /* Memory for KME use */
+#define CONFIG 0x0CD0L /* Concentrator configuration info */
+#define CONFIGSIZE 0x0030 /* configuration info size */
+#define DOWNREQ 0x0D00 /* Download request buffer pointer */
+
+#define CHANBUF 0x1000L /* Async channel (bs_t) structs */
+#define FEPOSSIZE 0x1FFF /* 8K FEPOS */
+
+#define XEMPORTS 0xC02 /*
+ * Offset in board memory where FEP5 stores
+ * how many ports it has detected.
+ * NOTE: FEP5 reports 64 ports when the user
+ * has the cable in EBI OUT instead of EBI IN.
+ */
+
+#define FEPCLR 0x00
+#define FEPMEM 0x02
+#define FEPRST 0x04
+#define FEPINT 0x08
+#define FEPMASK 0x0e
+#define FEPWIN 0x80
+
+#define LOWMEM 0x0100
+#define HIGHMEM 0x7f00
+
+#define FEPTIMEOUT 200000
+
+#define ENABLE_INTR 0x0e04 /* Enable interrupts flag */
+#define FEPPOLL_MIN 1 /* minimum of 1 millisecond */
+#define FEPPOLL_MAX 20 /* maximum of 20 milliseconds */
+#define FEPPOLL 0x0c26 /* Fep event poll interval */
+
+#define IALTPIN 0x0080 /* Input flag to swap DSR <-> DCD */
+
+/************************************************************************
+ * FEP supported functions
+ ************************************************************************/
+#define SRLOW 0xe0 /* Set receive low water */
+#define SRHIGH 0xe1 /* Set receive high water */
+#define FLUSHTX 0xe2 /* Flush transmit buffer */
+#define PAUSETX 0xe3 /* Pause data transmission */
+#define RESUMETX 0xe4 /* Resume data transmission */
+#define SMINT 0xe5 /* Set Modem Interrupt */
+#define SAFLOWC 0xe6 /* Set Aux. flow control chars */
+#define SBREAK 0xe8 /* Send break */
+#define SMODEM 0xe9 /* Set 8530 modem control lines */
+#define SIFLAG 0xea /* Set UNIX iflags */
+#define SFLOWC 0xeb /* Set flow control characters */
+#define STLOW 0xec /* Set transmit low water mark */
+#define RPAUSE 0xee /* Pause receive */
+#define RRESUME 0xef /* Resume receive */
+#define CHRESET 0xf0 /* Reset Channel */
+#define BUFSETALL 0xf2 /* Set Tx & Rx buffer size avail*/
+#define SOFLAG 0xf3 /* Set UNIX oflags */
+#define SHFLOW 0xf4 /* Set hardware handshake */
+#define SCFLAG 0xf5 /* Set UNIX cflags */
+#define SVNEXT 0xf6 /* Set VNEXT character */
+#define SPINTFC 0xfc /* Reserved */
+#define SCOMMODE 0xfd /* Set RS232/422 mode */
+
+/************************************************************************
+ * Modes for SCOMMODE
+ ************************************************************************/
+#define MODE_232 0x00
+#define MODE_422 0x01
+
+/************************************************************************
+ * Event flags.
+ ************************************************************************/
+#define IFBREAK 0x01 /* Break received */
+#define IFTLW 0x02 /* Transmit low water */
+#define IFTEM 0x04 /* Transmitter empty */
+#define IFDATA 0x08 /* Receive data present */
+#define IFMODEM 0x20 /* Modem status change */
+
+/************************************************************************
+ * Modem flags
+ ************************************************************************/
+# define DM_RTS 0x02 /* Request to send */
+# define DM_CD 0x80 /* Carrier detect */
+# define DM_DSR 0x20 /* Data set ready */
+# define DM_CTS 0x10 /* Clear to send */
+# define DM_RI 0x40 /* Ring indicator */
+# define DM_DTR 0x01 /* Data terminal ready */
+
+/*
+ * defines from dgap_conf.h
+ */
+#define NULLNODE 0 /* header node, not used */
+#define BNODE 1 /* Board node */
+#define LNODE 2 /* Line node */
+#define CNODE 3 /* Concentrator node */
+#define MNODE 4 /* EBI Module node */
+#define TNODE 5 /* tty name prefix node */
+#define CUNODE 6 /* cu name prefix (non-SCO) */
+#define PNODE 7 /* trans. print prefix node */
+#define JNODE 8 /* maJor number node */
+#define ANODE 9 /* altpin */
+#define TSNODE 10 /* tty structure size */
+#define CSNODE 11 /* channel structure size */
+#define BSNODE 12 /* board structure size */
+#define USNODE 13 /* unit schedule structure size */
+#define FSNODE 14 /* f2200 structure size */
+#define VSNODE 15 /* size of VPIX structures */
+#define INTRNODE 16 /* enable interrupt */
+
+/* Enumeration of tokens */
+#define BEGIN 1
+#define END 2
+#define BOARD 10
+
+#define EPCFS 11 /* start of EPC family definitions */
+#define ICX 11
+#define MCX 13
+#define PCX 14
+#define IEPC 15
+#define EEPC 16
+#define MEPC 17
+#define IPCM 18
+#define EPCM 19
+#define MPCM 20
+#define PEPC 21
+#define PPCM 22
+#ifdef CP
+#define ICP 23
+#define ECP 24
+#define MCP 25
+#endif
+#define EPCFE 25 /* end of EPC family definitions */
+#define PC2E 26
+#define PC4E 27
+#define PC4E8K 28
+#define PC8E 29
+#define PC8E8K 30
+#define PC16E 31
+#define MC2E8K 34
+#define MC4E8K 35
+#define MC8E8K 36
+
+#define AVANFS 42 /* start of Avanstar family definitions */
+#define A8P 42
+#define A16P 43
+#define AVANFE 43 /* end of Avanstar family definitions */
+
+#define DA2000FS 44 /* start of AccelePort 2000 family definitions */
+#define DA22 44 /* AccelePort 2002 */
+#define DA24 45 /* AccelePort 2004 */
+#define DA28 46 /* AccelePort 2008 */
+#define DA216 47 /* AccelePort 2016 */
+#define DAR4 48 /* AccelePort RAS 4 port */
+#define DAR8 49 /* AccelePort RAS 8 port */
+#define DDR24 50 /* DataFire RAS 24 port */
+#define DDR30 51 /* DataFire RAS 30 port */
+#define DDR48 52 /* DataFire RAS 48 port */
+#define DDR60 53 /* DataFire RAS 60 port */
+#define DA2000FE 53 /* end of AccelePort 2000/RAS family definitions */
+
+#define PCXRFS 106 /* start of PCXR family definitions */
+#define APORT4 106
+#define APORT8 107
+#define PAPORT4 108
+#define PAPORT8 109
+#define APORT4_920I 110
+#define APORT8_920I 111
+#define APORT4_920P 112
+#define APORT8_920P 113
+#define APORT2_920P 114
+#define PCXRFE 117 /* end of PCXR family definitions */
+
+#define LINE 82
+#ifdef T1
+#define T1M 83
+#define E1M 84
+#endif
+#define CONC 64
+#define CX 65
+#define EPC 66
+#define MOD 67
+#define PORTS 68
+#define METHOD 69
+#define CUSTOM 70
+#define BASIC 71
+#define STATUS 72
+#define MODEM 73
+/* The following tokens can appear in multiple places */
+#define SPEED 74
+#define NPORTS 75
+#define ID 76
+#define CABLE 77
+#define CONNECT 78
+#define IO 79
+#define MEM 80
+#define DPSZ 81
+
+#define TTYN 90
+#define CU 91
+#define PRINT 92
+#define XPRINT 93
+#define CMAJOR 94
+#define ALTPIN 95
+#define STARTO 96
+#define USEINTR 97
+#define PCIINFO 98
+
+#define TTSIZ 100
+#define CHSIZ 101
+#define BSSIZ 102
+#define UNTSIZ 103
+#define F2SIZ 104
+#define VPSIZ 105
+
+#define TOTAL_BOARD 2
+#define CURRENT_BRD 4
+#define BOARD_TYPE 6
+#define IO_ADDRESS 8
+#define MEM_ADDRESS 10
+
+#define FIELDS_PER_PAGE 18
+
+#define TB_FIELD 1
+#define CB_FIELD 3
+#define BT_FIELD 5
+#define IO_FIELD 7
+#define ID_FIELD 8
+#define ME_FIELD 9
+#define TTY_FIELD 11
+#define CU_FIELD 13
+#define PR_FIELD 15
+#define MPR_FIELD 17
+
+#define MAX_FIELD 512
+
+#define INIT 0
+#define NITEMS 128
+#define MAX_ITEM 512
+
+#define DSCRINST 1
+#define DSCRNUM 3
+#define ALTPINQ 5
+#define SSAVE 7
+
+#define DSCR "32"
+#define ONETONINE "123456789"
+#define ALL "1234567890"
+
+/*
+ * All the possible states the driver can be while being loaded.
+ */
+enum {
+ DRIVER_INITIALIZED = 0,
+ DRIVER_READY
+};
+
+/*
+ * All the possible states the board can be while booting up.
+ */
+enum {
+ BOARD_FAILED = 0,
+ BOARD_READY
+};
+
+/*
+ * All the possible states that a requested concentrator image can be in.
+ */
+enum {
+ NO_PENDING_CONCENTRATOR_REQUESTS = 0,
+ NEED_CONCENTRATOR,
+ REQUESTED_CONCENTRATOR
+};
+
+/*
+ * Modem line constants are defined as macros because DSR and
+ * DCD are swapable using the ditty altpin option.
+ */
+#define D_CD(ch) ch->ch_cd /* Carrier detect */
+#define D_DSR(ch) ch->ch_dsr /* Data set ready */
+#define D_RTS(ch) DM_RTS /* Request to send */
+#define D_CTS(ch) DM_CTS /* Clear to send */
+#define D_RI(ch) DM_RI /* Ring indicator */
+#define D_DTR(ch) DM_DTR /* Data terminal ready */
+
+/*************************************************************************
+ *
+ * Structures and closely related defines.
+ *
+ *************************************************************************/
+
+/*
+ * A structure to hold a statistics counter. We also
+ * compute moving averages for this counter.
+ */
+struct macounter {
+ u32 cnt; /* Total count */
+ ulong accum; /* Acuumulator per period */
+ ulong sma; /* Simple moving average */
+ ulong ema; /* Exponential moving average */
+};
+
+/************************************************************************
+ * Device flag definitions for bd_flags.
+ ************************************************************************/
+#define BD_FEP5PLUS 0x0001 /* Supports FEP5 Plus commands */
+#define BD_HAS_VPD 0x0002 /* Board has VPD info available */
+
+/*
+ * Per-board information
+ */
+struct board_t {
+ int magic; /* Board Magic number. */
+ int boardnum; /* Board number: 0-3 */
+
+ int type; /* Type of board */
+ char *name; /* Product Name */
+ struct pci_dev *pdev; /* Pointer to the pci_dev struct */
+ u16 vendor; /* PCI vendor ID */
+ u16 device; /* PCI device ID */
+ u16 subvendor; /* PCI subsystem vendor ID */
+ u16 subdevice; /* PCI subsystem device ID */
+ u8 rev; /* PCI revision ID */
+ uint pci_bus; /* PCI bus value */
+ uint pci_slot; /* PCI slot value */
+ u16 maxports; /* MAX ports this board can handle */
+ u8 vpd[VPDSIZE]; /* VPD of board, if found */
+ u32 bd_flags; /* Board flags */
+
+ spinlock_t bd_lock; /* Used to protect board */
+
+ u32 state; /* State of card. */
+ wait_queue_head_t state_wait; /* Place to sleep on for state change */
+
+ struct tasklet_struct helper_tasklet; /* Poll helper tasklet */
+
+ u32 wait_for_bios;
+ u32 wait_for_fep;
+
+ struct cnode *bd_config; /* Config of board */
+
+ u16 nasync; /* Number of ports on card */
+
+ ulong irq; /* Interrupt request number */
+ ulong intr_count; /* Count of interrupts */
+ u32 intr_used; /* Non-zero if using interrupts */
+ u32 intr_running; /* Non-zero if FEP knows its doing */
+ /* interrupts */
+
+ ulong port; /* Start of base io port of the card */
+ ulong port_end; /* End of base io port of the card */
+ ulong membase; /* Start of base memory of the card */
+ ulong membase_end; /* End of base memory of the card */
+
+ u8 __iomem *re_map_port; /* Remapped io port of the card */
+ u8 __iomem *re_map_membase;/* Remapped memory of the card */
+
+ u8 inhibit_poller; /* Tells the poller to leave us alone */
+
+ struct channel_t *channels[MAXPORTS]; /* array of pointers to our */
+ /* channels. */
+
+ struct tty_driver *serial_driver;
+ struct tty_port *serial_ports;
+ char serial_name[200];
+ struct tty_driver *print_driver;
+ struct tty_port *printer_ports;
+ char print_name[200];
+
+ struct bs_t __iomem *bd_bs; /* Base structure pointer */
+
+ char *flipbuf; /* Our flip buffer, alloced if */
+ /* board is found */
+ char *flipflagbuf; /* Our flip flag buffer, alloced */
+ /* if board is found */
+
+ u16 dpatype; /* The board "type", as defined */
+ /* by DPA */
+ u16 dpastatus; /* The board "status", as defined */
+ /* by DPA */
+
+ u32 conc_dl_status; /* Status of any pending conc */
+ /* download */
+};
+
+/************************************************************************
+ * Unit flag definitions for un_flags.
+ ************************************************************************/
+#define UN_ISOPEN 0x0001 /* Device is open */
+#define UN_CLOSING 0x0002 /* Line is being closed */
+#define UN_IMM 0x0004 /* Service immediately */
+#define UN_BUSY 0x0008 /* Some work this channel */
+#define UN_BREAKI 0x0010 /* Input break received */
+#define UN_PWAIT 0x0020 /* Printer waiting for terminal */
+#define UN_TIME 0x0040 /* Waiting on time */
+#define UN_EMPTY 0x0080 /* Waiting output queue empty */
+#define UN_LOW 0x0100 /* Waiting output low water mark*/
+#define UN_EXCL_OPEN 0x0200 /* Open for exclusive use */
+#define UN_WOPEN 0x0400 /* Device waiting for open */
+#define UN_WIOCTL 0x0800 /* Device waiting for open */
+#define UN_HANGUP 0x8000 /* Carrier lost */
+
+struct device;
+
+/************************************************************************
+ * Structure for terminal or printer unit.
+ ************************************************************************/
+struct un_t {
+ int magic; /* Unit Magic Number. */
+ struct channel_t *un_ch;
+ u32 un_time;
+ u32 un_type;
+ int un_open_count; /* Counter of opens to port */
+ struct tty_struct *un_tty;/* Pointer to unit tty structure */
+ u32 un_flags; /* Unit flags */
+ wait_queue_head_t un_flags_wait; /* Place to sleep to wait on unit */
+ u32 un_dev; /* Minor device number */
+ tcflag_t un_oflag; /* oflags being done on board */
+ tcflag_t un_lflag; /* lflags being done on board */
+ struct device *un_sysfs;
+};
+
+/************************************************************************
+ * Device flag definitions for ch_flags.
+ ************************************************************************/
+#define CH_PRON 0x0001 /* Printer on string */
+#define CH_OUT 0x0002 /* Dial-out device open */
+#define CH_STOP 0x0004 /* Output is stopped */
+#define CH_STOPI 0x0008 /* Input is stopped */
+#define CH_CD 0x0010 /* Carrier is present */
+#define CH_FCAR 0x0020 /* Carrier forced on */
+
+#define CH_RXBLOCK 0x0080 /* Enable rx blocked flag */
+#define CH_WLOW 0x0100 /* Term waiting low event */
+#define CH_WEMPTY 0x0200 /* Term waiting empty event */
+#define CH_RENABLE 0x0400 /* Buffer just emptied */
+#define CH_RACTIVE 0x0800 /* Process active in xxread() */
+#define CH_RWAIT 0x1000 /* Process waiting in xxread() */
+#define CH_BAUD0 0x2000 /* Used for checking B0 transitions */
+#define CH_HANGUP 0x8000 /* Hangup received */
+
+/*
+ * Definitions for ch_sniff_flags
+ */
+#define SNIFF_OPEN 0x1
+#define SNIFF_WAIT_DATA 0x2
+#define SNIFF_WAIT_SPACE 0x4
+
+/************************************************************************
+ *** Definitions for Digi ditty(1) command.
+ ************************************************************************/
+
+/************************************************************************
+ * This module provides application access to special Digi
+ * serial line enhancements which are not standard UNIX(tm) features.
+ ************************************************************************/
+
+#if !defined(TIOCMODG)
+
+#define TIOCMODG (('d'<<8) | 250) /* get modem ctrl state */
+#define TIOCMODS (('d'<<8) | 251) /* set modem ctrl state */
+
+#ifndef TIOCM_LE
+#define TIOCM_LE 0x01 /* line enable */
+#define TIOCM_DTR 0x02 /* data terminal ready */
+#define TIOCM_RTS 0x04 /* request to send */
+#define TIOCM_ST 0x08 /* secondary transmit */
+#define TIOCM_SR 0x10 /* secondary receive */
+#define TIOCM_CTS 0x20 /* clear to send */
+#define TIOCM_CAR 0x40 /* carrier detect */
+#define TIOCM_RNG 0x80 /* ring indicator */
+#define TIOCM_DSR 0x100 /* data set ready */
+#define TIOCM_RI TIOCM_RNG /* ring (alternate) */
+#define TIOCM_CD TIOCM_CAR /* carrier detect (alt) */
+#endif
+
+#endif
+
+#if !defined(TIOCMSET)
+#define TIOCMSET (('d'<<8) | 252) /* set modem ctrl state */
+#define TIOCMGET (('d'<<8) | 253) /* set modem ctrl state */
+#endif
+
+#if !defined(TIOCMBIC)
+#define TIOCMBIC (('d'<<8) | 254) /* set modem ctrl state */
+#define TIOCMBIS (('d'<<8) | 255) /* set modem ctrl state */
+#endif
+
+#if !defined(TIOCSDTR)
+#define TIOCSDTR (('e'<<8) | 0) /* set DTR */
+#define TIOCCDTR (('e'<<8) | 1) /* clear DTR */
+#endif
+
+/************************************************************************
+ * Ioctl command arguments for DIGI parameters.
+ ************************************************************************/
+#define DIGI_GETA (('e'<<8) | 94) /* Read params */
+
+#define DIGI_SETA (('e'<<8) | 95) /* Set params */
+#define DIGI_SETAW (('e'<<8) | 96) /* Drain & set params */
+#define DIGI_SETAF (('e'<<8) | 97) /* Drain, flush & set params */
+
+#define DIGI_KME (('e'<<8) | 98) /* Read/Write Host */
+ /* Adapter Memory */
+
+#define DIGI_GETFLOW (('e'<<8) | 99) /* Get startc/stopc flow */
+ /* control characters */
+#define DIGI_SETFLOW (('e'<<8) | 100) /* Set startc/stopc flow */
+ /* control characters */
+#define DIGI_GETAFLOW (('e'<<8) | 101) /* Get Aux. startc/stopc */
+ /* flow control chars */
+#define DIGI_SETAFLOW (('e'<<8) | 102) /* Set Aux. startc/stopc */
+ /* flow control chars */
+
+#define DIGI_GEDELAY (('d'<<8) | 246) /* Get edelay */
+#define DIGI_SEDELAY (('d'<<8) | 247) /* Set edelay */
+
+struct digiflow_t {
+ unsigned char startc; /* flow cntl start char */
+ unsigned char stopc; /* flow cntl stop char */
+};
+
+#ifdef FLOW_2200
+#define F2200_GETA (('e'<<8) | 104) /* Get 2x36 flow cntl flags */
+#define F2200_SETAW (('e'<<8) | 105) /* Set 2x36 flow cntl flags */
+#define F2200_MASK 0x03 /* 2200 flow cntl bit mask */
+#define FCNTL_2200 0x01 /* 2x36 terminal flow cntl */
+#define PCNTL_2200 0x02 /* 2x36 printer flow cntl */
+#define F2200_XON 0xf8
+#define P2200_XON 0xf9
+#define F2200_XOFF 0xfa
+#define P2200_XOFF 0xfb
+
+#define FXOFF_MASK 0x03 /* 2200 flow status mask */
+#define RCVD_FXOFF 0x01 /* 2x36 Terminal XOFF rcvd */
+#define RCVD_PXOFF 0x02 /* 2x36 Printer XOFF rcvd */
+#endif
+
+/************************************************************************
+ * Values for digi_flags
+ ************************************************************************/
+#define DIGI_IXON 0x0001 /* Handle IXON in the FEP */
+#define DIGI_FAST 0x0002 /* Fast baud rates */
+#define RTSPACE 0x0004 /* RTS input flow control */
+#define CTSPACE 0x0008 /* CTS output flow control */
+#define DSRPACE 0x0010 /* DSR output flow control */
+#define DCDPACE 0x0020 /* DCD output flow control */
+#define DTRPACE 0x0040 /* DTR input flow control */
+#define DIGI_COOK 0x0080 /* Cooked processing done in FEP */
+#define DIGI_FORCEDCD 0x0100 /* Force carrier */
+#define DIGI_ALTPIN 0x0200 /* Alternate RJ-45 pin config */
+#define DIGI_AIXON 0x0400 /* Aux flow control in fep */
+#define DIGI_PRINTER 0x0800 /* Hold port open for flow cntrl*/
+#define DIGI_PP_INPUT 0x1000 /* Change parallel port to input*/
+#define DIGI_DTR_TOGGLE 0x2000 /* Support DTR Toggle */
+#define DIGI_422 0x4000 /* for 422/232 selectable panel */
+#define DIGI_RTS_TOGGLE 0x8000 /* Support RTS Toggle */
+
+/************************************************************************
+ * These options are not supported on the comxi.
+ ************************************************************************/
+#define DIGI_COMXI (DIGI_FAST|DIGI_COOK|DSRPACE|DCDPACE|DTRPACE)
+
+#define DIGI_PLEN 28 /* String length */
+#define DIGI_TSIZ 10 /* Terminal string len */
+
+/************************************************************************
+ * Structure used with ioctl commands for DIGI parameters.
+ ************************************************************************/
+struct digi_t {
+ unsigned short digi_flags; /* Flags (see above) */
+ unsigned short digi_maxcps; /* Max printer CPS */
+ unsigned short digi_maxchar; /* Max chars in print queue */
+ unsigned short digi_bufsize; /* Buffer size */
+ unsigned char digi_onlen; /* Length of ON string */
+ unsigned char digi_offlen; /* Length of OFF string */
+ char digi_onstr[DIGI_PLEN]; /* Printer on string */
+ char digi_offstr[DIGI_PLEN]; /* Printer off string */
+ char digi_term[DIGI_TSIZ]; /* terminal string */
+};
+
+/************************************************************************
+ * KME definitions and structures.
+ ************************************************************************/
+#define RW_IDLE 0 /* Operation complete */
+#define RW_READ 1 /* Read Concentrator Memory */
+#define RW_WRITE 2 /* Write Concentrator Memory */
+
+struct rw_t {
+ unsigned char rw_req; /* Request type */
+ unsigned char rw_board; /* Host Adapter board number */
+ unsigned char rw_conc; /* Concentrator number */
+ unsigned char rw_reserved; /* Reserved for expansion */
+ unsigned long rw_addr; /* Address in concentrator */
+ unsigned short rw_size; /* Read/write request length */
+ unsigned char rw_data[128]; /* Data to read/write */
+};
+
+/************************************************************************
+ * Structure to get driver status information
+ ************************************************************************/
+struct digi_dinfo {
+ unsigned long dinfo_nboards; /* # boards configured */
+ char dinfo_reserved[12]; /* for future expansion */
+ char dinfo_version[16]; /* driver version */
+};
+
+#define DIGI_GETDD (('d'<<8) | 248) /* get driver info */
+
+/************************************************************************
+ * Structure used with ioctl commands for per-board information
+ *
+ * physsize and memsize differ when board has "windowed" memory
+ ************************************************************************/
+struct digi_info {
+ unsigned long info_bdnum; /* Board number (0 based) */
+ unsigned long info_ioport; /* io port address */
+ unsigned long info_physaddr; /* memory address */
+ unsigned long info_physsize; /* Size of host mem window */
+ unsigned long info_memsize; /* Amount of dual-port mem */
+ /* on board */
+ unsigned short info_bdtype; /* Board type */
+ unsigned short info_nports; /* number of ports */
+ char info_bdstate; /* board state */
+ char info_reserved[7]; /* for future expansion */
+};
+
+#define DIGI_GETBD (('d'<<8) | 249) /* get board info */
+
+struct digi_stat {
+ unsigned int info_chan; /* Channel number (0 based) */
+ unsigned int info_brd; /* Board number (0 based) */
+ unsigned long info_cflag; /* cflag for channel */
+ unsigned long info_iflag; /* iflag for channel */
+ unsigned long info_oflag; /* oflag for channel */
+ unsigned long info_mstat; /* mstat for channel */
+ unsigned long info_tx_data; /* tx_data for channel */
+ unsigned long info_rx_data; /* rx_data for channel */
+ unsigned long info_hflow; /* hflow for channel */
+ unsigned long info_reserved[8]; /* for future expansion */
+};
+
+#define DIGI_GETSTAT (('d'<<8) | 244) /* get board info */
+/************************************************************************
+ *
+ * Structure used with ioctl commands for per-channel information
+ *
+ ************************************************************************/
+struct digi_ch {
+ unsigned long info_bdnum; /* Board number (0 based) */
+ unsigned long info_channel; /* Channel index number */
+ unsigned long info_ch_cflag; /* Channel cflag */
+ unsigned long info_ch_iflag; /* Channel iflag */
+ unsigned long info_ch_oflag; /* Channel oflag */
+ unsigned long info_chsize; /* Channel structure size */
+ unsigned long info_sleep_stat; /* sleep status */
+ dev_t info_dev; /* device number */
+ unsigned char info_initstate; /* Channel init state */
+ unsigned char info_running; /* Channel running state */
+ long reserved[8]; /* reserved for future use */
+};
+
+/*
+* This structure is used with the DIGI_FEPCMD ioctl to
+* tell the driver which port to send the command for.
+*/
+struct digi_cmd {
+ int cmd;
+ int word;
+ int ncmds;
+ int chan; /* channel index (zero based) */
+ int bdid; /* board index (zero based) */
+};
+
+/*
+* info_sleep_stat defines
+*/
+#define INFO_RUNWAIT 0x0001
+#define INFO_WOPEN 0x0002
+#define INFO_TTIOW 0x0004
+#define INFO_CH_RWAIT 0x0008
+#define INFO_CH_WEMPTY 0x0010
+#define INFO_CH_WLOW 0x0020
+#define INFO_XXBUF_BUSY 0x0040
+
+#define DIGI_GETCH (('d'<<8) | 245) /* get board info */
+
+/* Board type definitions */
+
+#define SUBTYPE 0007
+#define T_PCXI 0000
+#define T_PCXM 0001
+#define T_PCXE 0002
+#define T_PCXR 0003
+#define T_SP 0004
+#define T_SP_PLUS 0005
+# define T_HERC 0000
+# define T_HOU 0001
+# define T_LON 0002
+# define T_CHA 0003
+#define FAMILY 0070
+#define T_COMXI 0000
+#define T_PCXX 0010
+#define T_CX 0020
+#define T_EPC 0030
+#define T_PCLITE 0040
+#define T_SPXX 0050
+#define T_AVXX 0060
+#define T_DXB 0070
+#define T_A2K_4_8 0070
+#define BUSTYPE 0700
+#define T_ISABUS 0000
+#define T_MCBUS 0100
+#define T_EISABUS 0200
+#define T_PCIBUS 0400
+
+/* Board State Definitions */
+
+#define BD_RUNNING 0x0
+#define BD_REASON 0x7f
+#define BD_NOTFOUND 0x1
+#define BD_NOIOPORT 0x2
+#define BD_NOMEM 0x3
+#define BD_NOBIOS 0x4
+#define BD_NOFEP 0x5
+#define BD_FAILED 0x6
+#define BD_ALLOCATED 0x7
+#define BD_TRIBOOT 0x8
+#define BD_BADKME 0x80
+
+#define DIGI_LOOPBACK (('d'<<8) | 252) /* Enable/disable UART */
+ /* internal loopback */
+#define DIGI_SPOLL (('d'<<8) | 254) /* change poller rate */
+
+#define DIGI_SETCUSTOMBAUD _IOW('e', 106, int) /* Set integer baud rate */
+#define DIGI_GETCUSTOMBAUD _IOR('e', 107, int) /* Get integer baud rate */
+#define DIGI_RESET_PORT (('e'<<8) | 93) /* Reset port */
+
+/************************************************************************
+ * Channel information structure.
+ ************************************************************************/
+struct channel_t {
+ int magic; /* Channel Magic Number */
+ struct bs_t __iomem *ch_bs; /* Base structure pointer */
+ struct cm_t __iomem *ch_cm; /* Command queue pointer */
+ struct board_t *ch_bd; /* Board structure pointer */
+ u8 __iomem *ch_vaddr; /* FEP memory origin */
+ u8 __iomem *ch_taddr; /* Write buffer origin */
+ u8 __iomem *ch_raddr; /* Read buffer origin */
+ struct digi_t ch_digi; /* Transparent Print structure */
+ struct un_t ch_tun; /* Terminal unit info */
+ struct un_t ch_pun; /* Printer unit info */
+
+ spinlock_t ch_lock; /* provide for serialization */
+ wait_queue_head_t ch_flags_wait;
+
+ u32 pscan_state;
+ u8 pscan_savechar;
+
+ u32 ch_portnum; /* Port number, 0 offset. */
+ u32 ch_open_count; /* open count */
+ u32 ch_flags; /* Channel flags */
+
+ u32 ch_cpstime; /* Time for CPS calculations */
+
+ tcflag_t ch_c_iflag; /* channel iflags */
+ tcflag_t ch_c_cflag; /* channel cflags */
+ tcflag_t ch_c_oflag; /* channel oflags */
+ tcflag_t ch_c_lflag; /* channel lflags */
+
+ u16 ch_fepiflag; /* FEP tty iflags */
+ u16 ch_fepcflag; /* FEP tty cflags */
+ u16 ch_fepoflag; /* FEP tty oflags */
+ u16 ch_wopen; /* Waiting for open process cnt */
+ u16 ch_tstart; /* Transmit buffer start */
+ u16 ch_tsize; /* Transmit buffer size */
+ u16 ch_rstart; /* Receive buffer start */
+ u16 ch_rsize; /* Receive buffer size */
+ u16 ch_rdelay; /* Receive delay time */
+
+ u16 ch_tlw; /* Our currently set low water mark */
+
+ u16 ch_cook; /* Output character mask */
+
+ u8 ch_card; /* Card channel is on */
+ u8 ch_stopc; /* Stop character */
+ u8 ch_startc; /* Start character */
+
+ u8 ch_mostat; /* FEP output modem status */
+ u8 ch_mistat; /* FEP input modem status */
+ u8 ch_mforce; /* Modem values to be forced */
+ u8 ch_mval; /* Force values */
+ u8 ch_fepstopc; /* FEP stop character */
+ u8 ch_fepstartc; /* FEP start character */
+
+ u8 ch_astopc; /* Auxiliary Stop character */
+ u8 ch_astartc; /* Auxiliary Start character */
+ u8 ch_fepastopc; /* Auxiliary FEP stop char */
+ u8 ch_fepastartc; /* Auxiliary FEP start char */
+
+ u8 ch_hflow; /* FEP hardware handshake */
+ u8 ch_dsr; /* stores real dsr value */
+ u8 ch_cd; /* stores real cd value */
+ u8 ch_tx_win; /* channel tx buffer window */
+ u8 ch_rx_win; /* channel rx buffer window */
+ uint ch_custom_speed; /* Custom baud, if set */
+ uint ch_baud_info; /* Current baud info for /proc output */
+ ulong ch_rxcount; /* total of data received so far */
+ ulong ch_txcount; /* total of data transmitted so far */
+ ulong ch_err_parity; /* Count of parity errors on channel */
+ ulong ch_err_frame; /* Count of framing errors on channel */
+ ulong ch_err_break; /* Count of breaks on channel */
+ ulong ch_err_overrun; /* Count of overruns on channel */
+};
+
+/************************************************************************
+ * Command structure definition.
+ ************************************************************************/
+struct cm_t {
+ unsigned short cm_head; /* Command buffer head offset */
+ unsigned short cm_tail; /* Command buffer tail offset */
+ unsigned short cm_start; /* start offset of buffer */
+ unsigned short cm_max; /* last offset of buffer */
+};
+
+/************************************************************************
+ * Event structure definition.
+ ************************************************************************/
+struct ev_t {
+ unsigned short ev_head; /* Command buffer head offset */
+ unsigned short ev_tail; /* Command buffer tail offset */
+ unsigned short ev_start; /* start offset of buffer */
+ unsigned short ev_max; /* last offset of buffer */
+};
+
+/************************************************************************
+ * Download buffer structure.
+ ************************************************************************/
+struct downld_t {
+ u8 dl_type; /* Header */
+ u8 dl_seq; /* Download sequence */
+ ushort dl_srev; /* Software revision number */
+ ushort dl_lrev; /* Low revision number */
+ ushort dl_hrev; /* High revision number */
+ ushort dl_seg; /* Start segment address */
+ ushort dl_size; /* Number of bytes to download */
+ u8 dl_data[1024]; /* Download data */
+};
+
+/************************************************************************
+ * Per channel buffer structure
+ ************************************************************************
+ * Base Structure Entries Usage Meanings to Host *
+ * *
+ * W = read write R = read only *
+ * C = changed by commands only *
+ * U = unknown (may be changed w/o notice) *
+ ************************************************************************/
+struct bs_t {
+ unsigned short tp_jmp; /* Transmit poll jump */
+ unsigned short tc_jmp; /* Cooked procedure jump */
+ unsigned short ri_jmp; /* Not currently used */
+ unsigned short rp_jmp; /* Receive poll jump */
+
+ unsigned short tx_seg; /* W Tx segment */
+ unsigned short tx_head; /* W Tx buffer head offset */
+ unsigned short tx_tail; /* R Tx buffer tail offset */
+ unsigned short tx_max; /* W Tx buffer size - 1 */
+
+ unsigned short rx_seg; /* W Rx segment */
+ unsigned short rx_head; /* W Rx buffer head offset */
+ unsigned short rx_tail; /* R Rx buffer tail offset */
+ unsigned short rx_max; /* W Rx buffer size - 1 */
+
+ unsigned short tx_lw; /* W Tx buffer low water mark */
+ unsigned short rx_lw; /* W Rx buffer low water mark */
+ unsigned short rx_hw; /* W Rx buffer high water mark*/
+ unsigned short incr; /* W Increment to next channel*/
+
+ unsigned short fepdev; /* U SCC device base address */
+ unsigned short edelay; /* W Exception delay */
+ unsigned short blen; /* W Break length */
+ unsigned short btime; /* U Break complete time */
+
+ unsigned short iflag; /* C UNIX input flags */
+ unsigned short oflag; /* C UNIX output flags */
+ unsigned short cflag; /* C UNIX control flags */
+ unsigned short wfill[13]; /* U Reserved for expansion */
+
+ unsigned char num; /* U Channel number */
+ unsigned char ract; /* U Receiver active counter */
+ unsigned char bstat; /* U Break status bits */
+ unsigned char tbusy; /* W Transmit busy */
+ unsigned char iempty; /* W Transmit empty event */
+ /* enable */
+ unsigned char ilow; /* W Transmit low-water event */
+ /* enable */
+ unsigned char idata; /* W Receive data interrupt */
+ /* enable */
+ unsigned char eflag; /* U Host event flags */
+
+ unsigned char tflag; /* U Transmit flags */
+ unsigned char rflag; /* U Receive flags */
+ unsigned char xmask; /* U Transmit ready flags */
+ unsigned char xval; /* U Transmit ready value */
+ unsigned char m_stat; /* RC Modem status bits */
+ unsigned char m_change; /* U Modem bits which changed */
+ unsigned char m_int; /* W Modem interrupt enable */
+ /* bits */
+ unsigned char m_last; /* U Last modem status */
+
+ unsigned char mtran; /* C Unreported modem trans */
+ unsigned char orun; /* C Buffer overrun occurred */
+ unsigned char astartc; /* W Auxiliary Xon char */
+ unsigned char astopc; /* W Auxiliary Xoff char */
+ unsigned char startc; /* W Xon character */
+ unsigned char stopc; /* W Xoff character */
+ unsigned char vnextc; /* W Vnext character */
+ unsigned char hflow; /* C Software flow control */
+
+ unsigned char fillc; /* U Delay Fill character */
+ unsigned char ochar; /* U Saved output character */
+ unsigned char omask; /* U Output character mask */
+
+ unsigned char bfill[13]; /* U Reserved for expansion */
+
+ unsigned char scc[16]; /* U SCC registers */
+};
+
+struct cnode {
+ struct cnode *next;
+ int type;
+ int numbrd;
+
+ union {
+ struct {
+ char type; /* Board Type */
+ long port; /* I/O Address */
+ char *portstr; /* I/O Address in string */
+ long addr; /* Memory Address */
+ char *addrstr; /* Memory Address in string */
+ long pcibus; /* PCI BUS */
+ char *pcibusstr; /* PCI BUS in string */
+ long pcislot; /* PCI SLOT */
+ char *pcislotstr; /* PCI SLOT in string */
+ long nport; /* Number of Ports */
+ char *id; /* tty id */
+ long start; /* start of tty counting */
+ char *method; /* Install method */
+ char v_port;
+ char v_addr;
+ char v_pcibus;
+ char v_pcislot;
+ char v_nport;
+ char v_id;
+ char v_start;
+ char v_method;
+ char line1;
+ char line2;
+ char conc1; /* total concs in line1 */
+ char conc2; /* total concs in line2 */
+ char module1; /* total modules for line1 */
+ char module2; /* total modules for line2 */
+ char *status; /* config status */
+ char *dimstatus; /* Y/N */
+ int status_index; /* field pointer */
+ } board;
+
+ struct {
+ char *cable;
+ char v_cable;
+ long speed;
+ char v_speed;
+ } line;
+
+ struct {
+ char type;
+ char *connect;
+ long speed;
+ long nport;
+ char *id;
+ char *idstr;
+ long start;
+ char v_connect;
+ char v_speed;
+ char v_nport;
+ char v_id;
+ char v_start;
+ } conc;
+
+ struct {
+ char type;
+ long nport;
+ char *id;
+ char *idstr;
+ long start;
+ char v_nport;
+ char v_id;
+ char v_start;
+ } module;
+
+ char *ttyname;
+ char *cuname;
+ char *printname;
+ long majornumber;
+ long altpin;
+ long ttysize;
+ long chsize;
+ long bssize;
+ long unsize;
+ long f2size;
+ long vpixsize;
+ long useintr;
+ } u;
+};
+#endif
diff --git a/drivers/staging/dgnc/Kconfig b/drivers/staging/dgnc/Kconfig
new file mode 100644
index 000000000..032c2a795
--- /dev/null
+++ b/drivers/staging/dgnc/Kconfig
@@ -0,0 +1,6 @@
+config DGNC
+ tristate "Digi Neo and Classic PCI Products"
+ default n
+ depends on TTY && PCI
+ ---help---
+ Driver for the Digi International Neo and Classic PCI based product line.
diff --git a/drivers/staging/dgnc/Makefile b/drivers/staging/dgnc/Makefile
new file mode 100644
index 000000000..995c874f4
--- /dev/null
+++ b/drivers/staging/dgnc/Makefile
@@ -0,0 +1,6 @@
+obj-$(CONFIG_DGNC) += dgnc.o
+
+dgnc-objs := dgnc_cls.o dgnc_driver.o\
+ dgnc_mgmt.o dgnc_neo.o\
+ dgnc_tty.o dgnc_sysfs.o\
+ dgnc_utils.o
diff --git a/drivers/staging/dgnc/TODO b/drivers/staging/dgnc/TODO
new file mode 100644
index 000000000..2b2c6ea03
--- /dev/null
+++ b/drivers/staging/dgnc/TODO
@@ -0,0 +1,10 @@
+* checkpatch fixes
+* remove unecessary comments
+* remove unecessary error messages. Example kzalloc() has its
+ own error message. Adding an extra one is useless.
+* use goto statements for error handling when appropriate
+* there is a lot of unecessary code in the driver. It was
+ originally a standalone driver. Remove uneeded code.
+
+Please send patches to Greg Kroah-Hartman <greg@kroah.com> and
+Cc: Lidza Louina <lidza.louina@gmail.com>
diff --git a/drivers/staging/dgnc/dgnc_cls.c b/drivers/staging/dgnc/dgnc_cls.c
new file mode 100644
index 000000000..e3564d278
--- /dev/null
+++ b/drivers/staging/dgnc/dgnc_cls.c
@@ -0,0 +1,1316 @@
+/*
+ * Copyright 2003 Digi International (www.digi.com)
+ * Scott H Kilau <Scott_Kilau at digi dot 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, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED; without even the
+ * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ * PURPOSE. See the GNU General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/sched.h> /* For jiffies, task states */
+#include <linux/interrupt.h> /* For tasklet and interrupt structs/defines */
+#include <linux/delay.h> /* For udelay */
+#include <linux/io.h> /* For read[bwl]/write[bwl] */
+#include <linux/serial.h> /* For struct async_serial */
+#include <linux/serial_reg.h> /* For the various UART offsets */
+#include <linux/pci.h>
+
+#include "dgnc_driver.h" /* Driver main header file */
+#include "dgnc_cls.h"
+#include "dgnc_tty.h"
+
+static inline void cls_parse_isr(struct dgnc_board *brd, uint port);
+static inline void cls_clear_break(struct channel_t *ch, int force);
+static inline void cls_set_cts_flow_control(struct channel_t *ch);
+static inline void cls_set_rts_flow_control(struct channel_t *ch);
+static inline void cls_set_ixon_flow_control(struct channel_t *ch);
+static inline void cls_set_ixoff_flow_control(struct channel_t *ch);
+static inline void cls_set_no_output_flow_control(struct channel_t *ch);
+static inline void cls_set_no_input_flow_control(struct channel_t *ch);
+static void cls_parse_modem(struct channel_t *ch, unsigned char signals);
+static void cls_tasklet(unsigned long data);
+static void cls_vpd(struct dgnc_board *brd);
+static void cls_uart_init(struct channel_t *ch);
+static void cls_uart_off(struct channel_t *ch);
+static int cls_drain(struct tty_struct *tty, uint seconds);
+static void cls_param(struct tty_struct *tty);
+static void cls_assert_modem_signals(struct channel_t *ch);
+static void cls_flush_uart_write(struct channel_t *ch);
+static void cls_flush_uart_read(struct channel_t *ch);
+static void cls_disable_receiver(struct channel_t *ch);
+static void cls_enable_receiver(struct channel_t *ch);
+static void cls_send_break(struct channel_t *ch, int msecs);
+static void cls_send_start_character(struct channel_t *ch);
+static void cls_send_stop_character(struct channel_t *ch);
+static void cls_copy_data_from_uart_to_queue(struct channel_t *ch);
+static void cls_copy_data_from_queue_to_uart(struct channel_t *ch);
+static uint cls_get_uart_bytes_left(struct channel_t *ch);
+static void cls_send_immediate_char(struct channel_t *ch, unsigned char);
+static irqreturn_t cls_intr(int irq, void *voidbrd);
+
+struct board_ops dgnc_cls_ops = {
+ .tasklet = cls_tasklet,
+ .intr = cls_intr,
+ .uart_init = cls_uart_init,
+ .uart_off = cls_uart_off,
+ .drain = cls_drain,
+ .param = cls_param,
+ .vpd = cls_vpd,
+ .assert_modem_signals = cls_assert_modem_signals,
+ .flush_uart_write = cls_flush_uart_write,
+ .flush_uart_read = cls_flush_uart_read,
+ .disable_receiver = cls_disable_receiver,
+ .enable_receiver = cls_enable_receiver,
+ .send_break = cls_send_break,
+ .send_start_character = cls_send_start_character,
+ .send_stop_character = cls_send_stop_character,
+ .copy_data_from_queue_to_uart = cls_copy_data_from_queue_to_uart,
+ .get_uart_bytes_left = cls_get_uart_bytes_left,
+ .send_immediate_char = cls_send_immediate_char
+};
+
+static inline void cls_set_cts_flow_control(struct channel_t *ch)
+{
+ unsigned char lcrb = readb(&ch->ch_cls_uart->lcr);
+ unsigned char ier = readb(&ch->ch_cls_uart->ier);
+ unsigned char isr_fcr = 0;
+
+ /*
+ * The Enhanced Register Set may only be accessed when
+ * the Line Control Register is set to 0xBFh.
+ */
+ writeb(UART_EXAR654_ENHANCED_REGISTER_SET, &ch->ch_cls_uart->lcr);
+
+ isr_fcr = readb(&ch->ch_cls_uart->isr_fcr);
+
+ /* Turn on CTS flow control, turn off IXON flow control */
+ isr_fcr |= (UART_EXAR654_EFR_ECB | UART_EXAR654_EFR_CTSDSR);
+ isr_fcr &= ~(UART_EXAR654_EFR_IXON);
+
+ writeb(isr_fcr, &ch->ch_cls_uart->isr_fcr);
+
+ /* Write old LCR value back out, which turns enhanced access off */
+ writeb(lcrb, &ch->ch_cls_uart->lcr);
+
+ /*
+ * Enable interrupts for CTS flow, turn off interrupts for
+ * received XOFF chars
+ */
+ ier |= (UART_EXAR654_IER_CTSDSR);
+ ier &= ~(UART_EXAR654_IER_XOFF);
+ writeb(ier, &ch->ch_cls_uart->ier);
+
+ /* Set the usual FIFO values */
+ writeb((UART_FCR_ENABLE_FIFO), &ch->ch_cls_uart->isr_fcr);
+
+ writeb((UART_FCR_ENABLE_FIFO | UART_16654_FCR_RXTRIGGER_56 |
+ UART_16654_FCR_TXTRIGGER_16 | UART_FCR_CLEAR_RCVR),
+ &ch->ch_cls_uart->isr_fcr);
+
+ ch->ch_t_tlevel = 16;
+
+}
+
+static inline void cls_set_ixon_flow_control(struct channel_t *ch)
+{
+ unsigned char lcrb = readb(&ch->ch_cls_uart->lcr);
+ unsigned char ier = readb(&ch->ch_cls_uart->ier);
+ unsigned char isr_fcr = 0;
+
+ /*
+ * The Enhanced Register Set may only be accessed when
+ * the Line Control Register is set to 0xBFh.
+ */
+ writeb(UART_EXAR654_ENHANCED_REGISTER_SET, &ch->ch_cls_uart->lcr);
+
+ isr_fcr = readb(&ch->ch_cls_uart->isr_fcr);
+
+ /* Turn on IXON flow control, turn off CTS flow control */
+ isr_fcr |= (UART_EXAR654_EFR_ECB | UART_EXAR654_EFR_IXON);
+ isr_fcr &= ~(UART_EXAR654_EFR_CTSDSR);
+
+ writeb(isr_fcr, &ch->ch_cls_uart->isr_fcr);
+
+ /* Now set our current start/stop chars while in enhanced mode */
+ writeb(ch->ch_startc, &ch->ch_cls_uart->mcr);
+ writeb(0, &ch->ch_cls_uart->lsr);
+ writeb(ch->ch_stopc, &ch->ch_cls_uart->msr);
+ writeb(0, &ch->ch_cls_uart->spr);
+
+ /* Write old LCR value back out, which turns enhanced access off */
+ writeb(lcrb, &ch->ch_cls_uart->lcr);
+
+ /*
+ * Disable interrupts for CTS flow, turn on interrupts for
+ * received XOFF chars
+ */
+ ier &= ~(UART_EXAR654_IER_CTSDSR);
+ ier |= (UART_EXAR654_IER_XOFF);
+ writeb(ier, &ch->ch_cls_uart->ier);
+
+ /* Set the usual FIFO values */
+ writeb((UART_FCR_ENABLE_FIFO), &ch->ch_cls_uart->isr_fcr);
+
+ writeb((UART_FCR_ENABLE_FIFO | UART_16654_FCR_RXTRIGGER_16 |
+ UART_16654_FCR_TXTRIGGER_16 | UART_FCR_CLEAR_RCVR),
+ &ch->ch_cls_uart->isr_fcr);
+
+}
+
+static inline void cls_set_no_output_flow_control(struct channel_t *ch)
+{
+ unsigned char lcrb = readb(&ch->ch_cls_uart->lcr);
+ unsigned char ier = readb(&ch->ch_cls_uart->ier);
+ unsigned char isr_fcr = 0;
+
+ /*
+ * The Enhanced Register Set may only be accessed when
+ * the Line Control Register is set to 0xBFh.
+ */
+ writeb(UART_EXAR654_ENHANCED_REGISTER_SET, &ch->ch_cls_uart->lcr);
+
+ isr_fcr = readb(&ch->ch_cls_uart->isr_fcr);
+
+ /* Turn off IXON flow control, turn off CTS flow control */
+ isr_fcr |= (UART_EXAR654_EFR_ECB);
+ isr_fcr &= ~(UART_EXAR654_EFR_CTSDSR | UART_EXAR654_EFR_IXON);
+
+ writeb(isr_fcr, &ch->ch_cls_uart->isr_fcr);
+
+ /* Write old LCR value back out, which turns enhanced access off */
+ writeb(lcrb, &ch->ch_cls_uart->lcr);
+
+ /*
+ * Disable interrupts for CTS flow, turn off interrupts for
+ * received XOFF chars
+ */
+ ier &= ~(UART_EXAR654_IER_CTSDSR);
+ ier &= ~(UART_EXAR654_IER_XOFF);
+ writeb(ier, &ch->ch_cls_uart->ier);
+
+ /* Set the usual FIFO values */
+ writeb((UART_FCR_ENABLE_FIFO), &ch->ch_cls_uart->isr_fcr);
+
+ writeb((UART_FCR_ENABLE_FIFO | UART_16654_FCR_RXTRIGGER_16 |
+ UART_16654_FCR_TXTRIGGER_16 | UART_FCR_CLEAR_RCVR),
+ &ch->ch_cls_uart->isr_fcr);
+
+ ch->ch_r_watermark = 0;
+ ch->ch_t_tlevel = 16;
+ ch->ch_r_tlevel = 16;
+
+}
+
+static inline void cls_set_rts_flow_control(struct channel_t *ch)
+{
+ unsigned char lcrb = readb(&ch->ch_cls_uart->lcr);
+ unsigned char ier = readb(&ch->ch_cls_uart->ier);
+ unsigned char isr_fcr = 0;
+
+ /*
+ * The Enhanced Register Set may only be accessed when
+ * the Line Control Register is set to 0xBFh.
+ */
+ writeb(UART_EXAR654_ENHANCED_REGISTER_SET, &ch->ch_cls_uart->lcr);
+
+ isr_fcr = readb(&ch->ch_cls_uart->isr_fcr);
+
+ /* Turn on RTS flow control, turn off IXOFF flow control */
+ isr_fcr |= (UART_EXAR654_EFR_ECB | UART_EXAR654_EFR_RTSDTR);
+ isr_fcr &= ~(UART_EXAR654_EFR_IXOFF);
+
+ writeb(isr_fcr, &ch->ch_cls_uart->isr_fcr);
+
+ /* Write old LCR value back out, which turns enhanced access off */
+ writeb(lcrb, &ch->ch_cls_uart->lcr);
+
+ /* Enable interrupts for RTS flow */
+ ier |= (UART_EXAR654_IER_RTSDTR);
+ writeb(ier, &ch->ch_cls_uart->ier);
+
+ /* Set the usual FIFO values */
+ writeb((UART_FCR_ENABLE_FIFO), &ch->ch_cls_uart->isr_fcr);
+
+ writeb((UART_FCR_ENABLE_FIFO | UART_16654_FCR_RXTRIGGER_56 |
+ UART_16654_FCR_TXTRIGGER_16 | UART_FCR_CLEAR_RCVR),
+ &ch->ch_cls_uart->isr_fcr);
+
+ ch->ch_r_watermark = 4;
+ ch->ch_r_tlevel = 8;
+
+}
+
+static inline void cls_set_ixoff_flow_control(struct channel_t *ch)
+{
+ unsigned char lcrb = readb(&ch->ch_cls_uart->lcr);
+ unsigned char ier = readb(&ch->ch_cls_uart->ier);
+ unsigned char isr_fcr = 0;
+
+ /*
+ * The Enhanced Register Set may only be accessed when
+ * the Line Control Register is set to 0xBFh.
+ */
+ writeb(UART_EXAR654_ENHANCED_REGISTER_SET, &ch->ch_cls_uart->lcr);
+
+ isr_fcr = readb(&ch->ch_cls_uart->isr_fcr);
+
+ /* Turn on IXOFF flow control, turn off RTS flow control */
+ isr_fcr |= (UART_EXAR654_EFR_ECB | UART_EXAR654_EFR_IXOFF);
+ isr_fcr &= ~(UART_EXAR654_EFR_RTSDTR);
+
+ writeb(isr_fcr, &ch->ch_cls_uart->isr_fcr);
+
+ /* Now set our current start/stop chars while in enhanced mode */
+ writeb(ch->ch_startc, &ch->ch_cls_uart->mcr);
+ writeb(0, &ch->ch_cls_uart->lsr);
+ writeb(ch->ch_stopc, &ch->ch_cls_uart->msr);
+ writeb(0, &ch->ch_cls_uart->spr);
+
+ /* Write old LCR value back out, which turns enhanced access off */
+ writeb(lcrb, &ch->ch_cls_uart->lcr);
+
+ /* Disable interrupts for RTS flow */
+ ier &= ~(UART_EXAR654_IER_RTSDTR);
+ writeb(ier, &ch->ch_cls_uart->ier);
+
+ /* Set the usual FIFO values */
+ writeb((UART_FCR_ENABLE_FIFO), &ch->ch_cls_uart->isr_fcr);
+
+ writeb((UART_FCR_ENABLE_FIFO | UART_16654_FCR_RXTRIGGER_16 |
+ UART_16654_FCR_TXTRIGGER_16 | UART_FCR_CLEAR_RCVR),
+ &ch->ch_cls_uart->isr_fcr);
+
+}
+
+static inline void cls_set_no_input_flow_control(struct channel_t *ch)
+{
+ unsigned char lcrb = readb(&ch->ch_cls_uart->lcr);
+ unsigned char ier = readb(&ch->ch_cls_uart->ier);
+ unsigned char isr_fcr = 0;
+
+ /*
+ * The Enhanced Register Set may only be accessed when
+ * the Line Control Register is set to 0xBFh.
+ */
+ writeb(UART_EXAR654_ENHANCED_REGISTER_SET, &ch->ch_cls_uart->lcr);
+
+ isr_fcr = readb(&ch->ch_cls_uart->isr_fcr);
+
+ /* Turn off IXOFF flow control, turn off RTS flow control */
+ isr_fcr |= (UART_EXAR654_EFR_ECB);
+ isr_fcr &= ~(UART_EXAR654_EFR_RTSDTR | UART_EXAR654_EFR_IXOFF);
+
+ writeb(isr_fcr, &ch->ch_cls_uart->isr_fcr);
+
+ /* Write old LCR value back out, which turns enhanced access off */
+ writeb(lcrb, &ch->ch_cls_uart->lcr);
+
+ /* Disable interrupts for RTS flow */
+ ier &= ~(UART_EXAR654_IER_RTSDTR);
+ writeb(ier, &ch->ch_cls_uart->ier);
+
+ /* Set the usual FIFO values */
+ writeb((UART_FCR_ENABLE_FIFO), &ch->ch_cls_uart->isr_fcr);
+
+ writeb((UART_FCR_ENABLE_FIFO | UART_16654_FCR_RXTRIGGER_16 |
+ UART_16654_FCR_TXTRIGGER_16 | UART_FCR_CLEAR_RCVR),
+ &ch->ch_cls_uart->isr_fcr);
+
+ ch->ch_t_tlevel = 16;
+ ch->ch_r_tlevel = 16;
+
+}
+
+/*
+ * cls_clear_break.
+ * Determines whether its time to shut off break condition.
+ *
+ * No locks are assumed to be held when calling this function.
+ * channel lock is held and released in this function.
+ */
+static inline void cls_clear_break(struct channel_t *ch, int force)
+{
+ unsigned long flags;
+
+ if (!ch || ch->magic != DGNC_CHANNEL_MAGIC)
+ return;
+
+ spin_lock_irqsave(&ch->ch_lock, flags);
+
+ /* Bail if we aren't currently sending a break. */
+ if (!ch->ch_stop_sending_break) {
+ spin_unlock_irqrestore(&ch->ch_lock, flags);
+ return;
+ }
+
+ /* Turn break off, and unset some variables */
+ if (ch->ch_flags & CH_BREAK_SENDING) {
+ if (time_after(jiffies, ch->ch_stop_sending_break) || force) {
+ unsigned char temp = readb(&ch->ch_cls_uart->lcr);
+
+ writeb((temp & ~UART_LCR_SBC), &ch->ch_cls_uart->lcr);
+ ch->ch_flags &= ~(CH_BREAK_SENDING);
+ ch->ch_stop_sending_break = 0;
+ }
+ }
+ spin_unlock_irqrestore(&ch->ch_lock, flags);
+}
+
+/* Parse the ISR register for the specific port */
+static inline void cls_parse_isr(struct dgnc_board *brd, uint port)
+{
+ struct channel_t *ch;
+ unsigned char isr = 0;
+ unsigned long flags;
+
+ /*
+ * No need to verify board pointer, it was already
+ * verified in the interrupt routine.
+ */
+
+ if (port >= brd->nasync)
+ return;
+
+ ch = brd->channels[port];
+ if (!ch || ch->magic != DGNC_CHANNEL_MAGIC)
+ return;
+
+ /* Here we try to figure out what caused the interrupt to happen */
+ while (1) {
+
+ isr = readb(&ch->ch_cls_uart->isr_fcr);
+
+ /* Bail if no pending interrupt on port */
+ if (isr & UART_IIR_NO_INT)
+ break;
+
+ /* Receive Interrupt pending */
+ if (isr & (UART_IIR_RDI | UART_IIR_RDI_TIMEOUT)) {
+ /* Read data from uart -> queue */
+ brd->intr_rx++;
+ ch->ch_intr_rx++;
+ cls_copy_data_from_uart_to_queue(ch);
+ dgnc_check_queue_flow_control(ch);
+ }
+
+ /* Transmit Hold register empty pending */
+ if (isr & UART_IIR_THRI) {
+ /* Transfer data (if any) from Write Queue -> UART. */
+ spin_lock_irqsave(&ch->ch_lock, flags);
+ ch->ch_flags |= (CH_TX_FIFO_EMPTY | CH_TX_FIFO_LWM);
+ brd->intr_tx++;
+ ch->ch_intr_tx++;
+ spin_unlock_irqrestore(&ch->ch_lock, flags);
+ cls_copy_data_from_queue_to_uart(ch);
+ }
+
+ /* CTS/RTS change of state */
+ if (isr & UART_IIR_CTSRTS) {
+ brd->intr_modem++;
+ ch->ch_intr_modem++;
+ /*
+ * Don't need to do anything, the cls_parse_modem
+ * below will grab the updated modem signals.
+ */
+ }
+
+ /* Parse any modem signal changes */
+ cls_parse_modem(ch, readb(&ch->ch_cls_uart->msr));
+ }
+}
+
+/*
+ * cls_param()
+ * Send any/all changes to the line to the UART.
+ */
+static void cls_param(struct tty_struct *tty)
+{
+ unsigned char lcr = 0;
+ unsigned char uart_lcr = 0;
+ unsigned char ier = 0;
+ unsigned char uart_ier = 0;
+ uint baud = 9600;
+ int quot = 0;
+ struct dgnc_board *bd;
+ struct channel_t *ch;
+ struct un_t *un;
+
+ if (!tty || tty->magic != TTY_MAGIC)
+ return;
+
+ un = (struct un_t *) tty->driver_data;
+ if (!un || un->magic != DGNC_UNIT_MAGIC)
+ return;
+
+ ch = un->un_ch;
+ if (!ch || ch->magic != DGNC_CHANNEL_MAGIC)
+ return;
+
+ bd = ch->ch_bd;
+ if (!bd || bd->magic != DGNC_BOARD_MAGIC)
+ return;
+
+ /*
+ * If baud rate is zero, flush queues, and set mval to drop DTR.
+ */
+ if ((ch->ch_c_cflag & (CBAUD)) == 0) {
+ ch->ch_r_head = 0;
+ ch->ch_r_tail = 0;
+ ch->ch_e_head = 0;
+ ch->ch_e_tail = 0;
+ ch->ch_w_head = 0;
+ ch->ch_w_tail = 0;
+
+ cls_flush_uart_write(ch);
+ cls_flush_uart_read(ch);
+
+ /* The baudrate is B0 so all modem lines are to be dropped. */
+ ch->ch_flags |= (CH_BAUD0);
+ ch->ch_mostat &= ~(UART_MCR_RTS | UART_MCR_DTR);
+ cls_assert_modem_signals(ch);
+ ch->ch_old_baud = 0;
+ return;
+ } else if (ch->ch_custom_speed) {
+
+ baud = ch->ch_custom_speed;
+ /* Handle transition from B0 */
+ if (ch->ch_flags & CH_BAUD0) {
+ ch->ch_flags &= ~(CH_BAUD0);
+
+ /*
+ * Bring back up RTS and DTR...
+ * Also handle RTS or DTR toggle if set.
+ */
+ if (!(ch->ch_digi.digi_flags & DIGI_RTS_TOGGLE))
+ ch->ch_mostat |= (UART_MCR_RTS);
+ if (!(ch->ch_digi.digi_flags & DIGI_DTR_TOGGLE))
+ ch->ch_mostat |= (UART_MCR_DTR);
+ }
+
+ } else {
+ int iindex = 0;
+ int jindex = 0;
+
+ ulong bauds[4][16] = {
+ { /* slowbaud */
+ 0, 50, 75, 110,
+ 134, 150, 200, 300,
+ 600, 1200, 1800, 2400,
+ 4800, 9600, 19200, 38400 },
+ { /* slowbaud & CBAUDEX */
+ 0, 57600, 115200, 230400,
+ 460800, 150, 200, 921600,
+ 600, 1200, 1800, 2400,
+ 4800, 9600, 19200, 38400 },
+ { /* fastbaud */
+ 0, 57600, 76800, 115200,
+ 131657, 153600, 230400, 460800,
+ 921600, 1200, 1800, 2400,
+ 4800, 9600, 19200, 38400 },
+ { /* fastbaud & CBAUDEX */
+ 0, 57600, 115200, 230400,
+ 460800, 150, 200, 921600,
+ 600, 1200, 1800, 2400,
+ 4800, 9600, 19200, 38400 }
+ };
+
+ /*
+ * Only use the TXPrint baud rate if the terminal
+ * unit is NOT open
+ */
+ if (!(ch->ch_tun.un_flags & UN_ISOPEN) &&
+ (un->un_type == DGNC_PRINT))
+ baud = C_BAUD(ch->ch_pun.un_tty) & 0xff;
+ else
+ baud = C_BAUD(ch->ch_tun.un_tty) & 0xff;
+
+ if (ch->ch_c_cflag & CBAUDEX)
+ iindex = 1;
+
+ if (ch->ch_digi.digi_flags & DIGI_FAST)
+ iindex += 2;
+
+ jindex = baud;
+
+ if ((iindex >= 0) && (iindex < 4) && (jindex >= 0) &&
+ (jindex < 16)) {
+ baud = bauds[iindex][jindex];
+ } else {
+ baud = 0;
+ }
+
+ if (baud == 0)
+ baud = 9600;
+
+ /* Handle transition from B0 */
+ if (ch->ch_flags & CH_BAUD0) {
+ ch->ch_flags &= ~(CH_BAUD0);
+
+ /*
+ * Bring back up RTS and DTR...
+ * Also handle RTS or DTR toggle if set.
+ */
+ if (!(ch->ch_digi.digi_flags & DIGI_RTS_TOGGLE))
+ ch->ch_mostat |= (UART_MCR_RTS);
+ if (!(ch->ch_digi.digi_flags & DIGI_DTR_TOGGLE))
+ ch->ch_mostat |= (UART_MCR_DTR);
+ }
+ }
+
+ if (ch->ch_c_cflag & PARENB)
+ lcr |= UART_LCR_PARITY;
+
+ if (!(ch->ch_c_cflag & PARODD))
+ lcr |= UART_LCR_EPAR;
+
+ /*
+ * Not all platforms support mark/space parity,
+ * so this will hide behind an ifdef.
+ */
+#ifdef CMSPAR
+ if (ch->ch_c_cflag & CMSPAR)
+ lcr |= UART_LCR_SPAR;
+#endif
+
+ if (ch->ch_c_cflag & CSTOPB)
+ lcr |= UART_LCR_STOP;
+
+ switch (ch->ch_c_cflag & CSIZE) {
+ case CS5:
+ lcr |= UART_LCR_WLEN5;
+ break;
+ case CS6:
+ lcr |= UART_LCR_WLEN6;
+ break;
+ case CS7:
+ lcr |= UART_LCR_WLEN7;
+ break;
+ case CS8:
+ default:
+ lcr |= UART_LCR_WLEN8;
+ break;
+ }
+
+ uart_ier = readb(&ch->ch_cls_uart->ier);
+ ier = uart_ier;
+ uart_lcr = readb(&ch->ch_cls_uart->lcr);
+
+ if (baud == 0)
+ baud = 9600;
+
+ quot = ch->ch_bd->bd_dividend / baud;
+
+ if (quot != 0 && ch->ch_old_baud != baud) {
+ ch->ch_old_baud = baud;
+ writeb(UART_LCR_DLAB, &ch->ch_cls_uart->lcr);
+ writeb((quot & 0xff), &ch->ch_cls_uart->txrx);
+ writeb((quot >> 8), &ch->ch_cls_uart->ier);
+ writeb(lcr, &ch->ch_cls_uart->lcr);
+ }
+
+ if (uart_lcr != lcr)
+ writeb(lcr, &ch->ch_cls_uart->lcr);
+
+ if (ch->ch_c_cflag & CREAD)
+ ier |= (UART_IER_RDI | UART_IER_RLSI);
+ else
+ ier &= ~(UART_IER_RDI | UART_IER_RLSI);
+
+ /*
+ * Have the UART interrupt on modem signal changes ONLY when
+ * we are in hardware flow control mode, or CLOCAL/FORCEDCD is not set.
+ */
+ if ((ch->ch_digi.digi_flags & CTSPACE) ||
+ (ch->ch_digi.digi_flags & RTSPACE) ||
+ (ch->ch_c_cflag & CRTSCTS) ||
+ !(ch->ch_digi.digi_flags & DIGI_FORCEDCD) ||
+ !(ch->ch_c_cflag & CLOCAL))
+ ier |= UART_IER_MSI;
+ else
+ ier &= ~UART_IER_MSI;
+
+ ier |= UART_IER_THRI;
+
+ if (ier != uart_ier)
+ writeb(ier, &ch->ch_cls_uart->ier);
+
+ if (ch->ch_digi.digi_flags & CTSPACE || ch->ch_c_cflag & CRTSCTS) {
+ cls_set_cts_flow_control(ch);
+ } else if (ch->ch_c_iflag & IXON) {
+ /*
+ * If start/stop is set to disable, then we should
+ * disable flow control
+ */
+ if ((ch->ch_startc == _POSIX_VDISABLE) ||
+ (ch->ch_stopc == _POSIX_VDISABLE))
+ cls_set_no_output_flow_control(ch);
+ else
+ cls_set_ixon_flow_control(ch);
+ } else {
+ cls_set_no_output_flow_control(ch);
+ }
+
+ if (ch->ch_digi.digi_flags & RTSPACE || ch->ch_c_cflag & CRTSCTS) {
+ cls_set_rts_flow_control(ch);
+ } else if (ch->ch_c_iflag & IXOFF) {
+ /*
+ * If start/stop is set to disable, then we should disable
+ * flow control
+ */
+ if ((ch->ch_startc == _POSIX_VDISABLE) ||
+ (ch->ch_stopc == _POSIX_VDISABLE))
+ cls_set_no_input_flow_control(ch);
+ else
+ cls_set_ixoff_flow_control(ch);
+ } else {
+ cls_set_no_input_flow_control(ch);
+ }
+
+ cls_assert_modem_signals(ch);
+
+ /* Get current status of the modem signals now */
+ cls_parse_modem(ch, readb(&ch->ch_cls_uart->msr));
+}
+
+/*
+ * Our board poller function.
+ */
+static void cls_tasklet(unsigned long data)
+{
+ struct dgnc_board *bd = (struct dgnc_board *) data;
+ struct channel_t *ch;
+ unsigned long flags;
+ int i;
+ int state = 0;
+ int ports = 0;
+
+ if (!bd || bd->magic != DGNC_BOARD_MAGIC)
+ return;
+
+ /* Cache a couple board values */
+ spin_lock_irqsave(&bd->bd_lock, flags);
+ state = bd->state;
+ ports = bd->nasync;
+ spin_unlock_irqrestore(&bd->bd_lock, flags);
+
+ /*
+ * Do NOT allow the interrupt routine to read the intr registers
+ * Until we release this lock.
+ */
+ spin_lock_irqsave(&bd->bd_intr_lock, flags);
+
+ /*
+ * If board is ready, parse deeper to see if there is anything to do.
+ */
+ if ((state == BOARD_READY) && (ports > 0)) {
+
+ /* Loop on each port */
+ for (i = 0; i < ports; i++) {
+ ch = bd->channels[i];
+ if (!ch)
+ continue;
+
+ /*
+ * NOTE: Remember you CANNOT hold any channel
+ * locks when calling input.
+ * During input processing, its possible we
+ * will call ld, which might do callbacks back
+ * into us.
+ */
+ dgnc_input(ch);
+
+ /*
+ * Channel lock is grabbed and then released
+ * inside this routine.
+ */
+ cls_copy_data_from_queue_to_uart(ch);
+ dgnc_wakeup_writes(ch);
+
+ /*
+ * Check carrier function.
+ */
+ dgnc_carrier(ch);
+
+ /*
+ * The timing check of turning off the break is done
+ * inside clear_break()
+ */
+ if (ch->ch_stop_sending_break)
+ cls_clear_break(ch, 0);
+ }
+ }
+
+ spin_unlock_irqrestore(&bd->bd_intr_lock, flags);
+
+}
+
+/*
+ * cls_intr()
+ *
+ * Classic specific interrupt handler.
+ */
+static irqreturn_t cls_intr(int irq, void *voidbrd)
+{
+ struct dgnc_board *brd = voidbrd;
+ uint i = 0;
+ unsigned char poll_reg;
+ unsigned long flags;
+
+ /*
+ * Check to make sure it didn't receive interrupt with a null board
+ * associated or a board pointer that wasn't ours.
+ */
+ if (!brd || brd->magic != DGNC_BOARD_MAGIC)
+ return IRQ_NONE;
+
+ spin_lock_irqsave(&brd->bd_intr_lock, flags);
+
+ brd->intr_count++;
+
+ /*
+ * Check the board's global interrupt offset to see if we
+ * we actually do have an interrupt pending for us.
+ */
+ poll_reg = readb(brd->re_map_membase + UART_CLASSIC_POLL_ADDR_OFFSET);
+
+ /* If 0, no interrupts pending */
+ if (!poll_reg) {
+ spin_unlock_irqrestore(&brd->bd_intr_lock, flags);
+ return IRQ_NONE;
+ }
+
+ /* Parse each port to find out what caused the interrupt */
+ for (i = 0; i < brd->nasync; i++)
+ cls_parse_isr(brd, i);
+
+ /*
+ * Schedule tasklet to more in-depth servicing at a better time.
+ */
+ tasklet_schedule(&brd->helper_tasklet);
+
+ spin_unlock_irqrestore(&brd->bd_intr_lock, flags);
+
+ return IRQ_HANDLED;
+}
+
+static void cls_disable_receiver(struct channel_t *ch)
+{
+ unsigned char tmp = readb(&ch->ch_cls_uart->ier);
+
+ tmp &= ~(UART_IER_RDI);
+ writeb(tmp, &ch->ch_cls_uart->ier);
+}
+
+static void cls_enable_receiver(struct channel_t *ch)
+{
+ unsigned char tmp = readb(&ch->ch_cls_uart->ier);
+
+ tmp |= (UART_IER_RDI);
+ writeb(tmp, &ch->ch_cls_uart->ier);
+}
+
+static void cls_copy_data_from_uart_to_queue(struct channel_t *ch)
+{
+ int qleft = 0;
+ unsigned char linestatus = 0;
+ unsigned char error_mask = 0;
+ ushort head;
+ ushort tail;
+ unsigned long flags;
+
+ if (!ch || ch->magic != DGNC_CHANNEL_MAGIC)
+ return;
+
+ spin_lock_irqsave(&ch->ch_lock, flags);
+
+ /* cache head and tail of queue */
+ head = ch->ch_r_head;
+ tail = ch->ch_r_tail;
+
+ /* Store how much space we have left in the queue */
+ qleft = (tail - head - 1);
+ if (qleft < 0)
+ qleft += RQUEUEMASK + 1;
+
+ /*
+ * Create a mask to determine whether we should
+ * insert the character (if any) into our queue.
+ */
+ if (ch->ch_c_iflag & IGNBRK)
+ error_mask |= UART_LSR_BI;
+
+ while (1) {
+ linestatus = readb(&ch->ch_cls_uart->lsr);
+
+ if (!(linestatus & (UART_LSR_DR)))
+ break;
+
+ /*
+ * Discard character if we are ignoring the error mask.
+ */
+ if (linestatus & error_mask) {
+ unsigned char discard;
+
+ linestatus = 0;
+ discard = readb(&ch->ch_cls_uart->txrx);
+ continue;
+ }
+
+ /*
+ * If our queue is full, we have no choice but to drop some
+ * data. The assumption is that HWFLOW or SWFLOW should have
+ * stopped things way way before we got to this point.
+ *
+ * I decided that I wanted to ditch the oldest data first,
+ * I hope thats okay with everyone? Yes? Good.
+ */
+ while (qleft < 1) {
+ tail = (tail + 1) & RQUEUEMASK;
+ ch->ch_r_tail = tail;
+ ch->ch_err_overrun++;
+ qleft++;
+ }
+
+ ch->ch_equeue[head] = linestatus & (UART_LSR_BI | UART_LSR_PE
+ | UART_LSR_FE);
+ ch->ch_rqueue[head] = readb(&ch->ch_cls_uart->txrx);
+
+ qleft--;
+
+ if (ch->ch_equeue[head] & UART_LSR_PE)
+ ch->ch_err_parity++;
+ if (ch->ch_equeue[head] & UART_LSR_BI)
+ ch->ch_err_break++;
+ if (ch->ch_equeue[head] & UART_LSR_FE)
+ ch->ch_err_frame++;
+
+ /* Add to, and flip head if needed */
+ head = (head + 1) & RQUEUEMASK;
+ ch->ch_rxcount++;
+ }
+
+ /*
+ * Write new final heads to channel structure.
+ */
+ ch->ch_r_head = head & RQUEUEMASK;
+ ch->ch_e_head = head & EQUEUEMASK;
+
+ spin_unlock_irqrestore(&ch->ch_lock, flags);
+}
+
+/*
+ * This function basically goes to sleep for secs, or until
+ * it gets signalled that the port has fully drained.
+ */
+static int cls_drain(struct tty_struct *tty, uint seconds)
+{
+ unsigned long flags;
+ struct channel_t *ch;
+ struct un_t *un;
+
+ if (!tty || tty->magic != TTY_MAGIC)
+ return -ENXIO;
+
+ un = (struct un_t *) tty->driver_data;
+ if (!un || un->magic != DGNC_UNIT_MAGIC)
+ return -ENXIO;
+
+ ch = un->un_ch;
+ if (!ch || ch->magic != DGNC_CHANNEL_MAGIC)
+ return -ENXIO;
+
+ spin_lock_irqsave(&ch->ch_lock, flags);
+ un->un_flags |= UN_EMPTY;
+ spin_unlock_irqrestore(&ch->ch_lock, flags);
+
+ /*
+ * NOTE: Do something with time passed in.
+ */
+
+ /* If ret is non-zero, user ctrl-c'ed us */
+
+ return wait_event_interruptible(un->un_flags_wait,
+ ((un->un_flags & UN_EMPTY) == 0));
+}
+
+/* Channel lock MUST be held before calling this function! */
+static void cls_flush_uart_write(struct channel_t *ch)
+{
+ if (!ch || ch->magic != DGNC_CHANNEL_MAGIC)
+ return;
+
+ writeb((UART_FCR_ENABLE_FIFO | UART_FCR_CLEAR_XMIT),
+ &ch->ch_cls_uart->isr_fcr);
+ udelay(10);
+
+ ch->ch_flags |= (CH_TX_FIFO_EMPTY | CH_TX_FIFO_LWM);
+}
+
+/* Channel lock MUST be held before calling this function! */
+static void cls_flush_uart_read(struct channel_t *ch)
+{
+ if (!ch || ch->magic != DGNC_CHANNEL_MAGIC)
+ return;
+
+ /*
+ * For complete POSIX compatibility, we should be purging the
+ * read FIFO in the UART here.
+ *
+ * However, clearing the read FIFO (UART_FCR_CLEAR_RCVR) also
+ * incorrectly flushes write data as well as just basically trashing the
+ * FIFO.
+ *
+ * Presumably, this is a bug in this UART.
+ */
+
+ udelay(10);
+}
+
+static void cls_copy_data_from_queue_to_uart(struct channel_t *ch)
+{
+ ushort head;
+ ushort tail;
+ int n;
+ int qlen;
+ uint len_written = 0;
+ unsigned long flags;
+
+ if (!ch || ch->magic != DGNC_CHANNEL_MAGIC)
+ return;
+
+ spin_lock_irqsave(&ch->ch_lock, flags);
+
+ /* No data to write to the UART */
+ if (ch->ch_w_tail == ch->ch_w_head)
+ goto exit_unlock;
+
+ /* If port is "stopped", don't send any data to the UART */
+ if ((ch->ch_flags & CH_FORCED_STOP) ||
+ (ch->ch_flags & CH_BREAK_SENDING))
+ goto exit_unlock;
+
+ if (!(ch->ch_flags & (CH_TX_FIFO_EMPTY | CH_TX_FIFO_LWM)))
+ goto exit_unlock;
+
+ n = 32;
+
+ /* cache head and tail of queue */
+ head = ch->ch_w_head & WQUEUEMASK;
+ tail = ch->ch_w_tail & WQUEUEMASK;
+ qlen = (head - tail) & WQUEUEMASK;
+
+ /* Find minimum of the FIFO space, versus queue length */
+ n = min(n, qlen);
+
+ while (n > 0) {
+
+ /*
+ * If RTS Toggle mode is on, turn on RTS now if not already set,
+ * and make sure we get an event when the data transfer has
+ * completed.
+ */
+ if (ch->ch_digi.digi_flags & DIGI_RTS_TOGGLE) {
+ if (!(ch->ch_mostat & UART_MCR_RTS)) {
+ ch->ch_mostat |= (UART_MCR_RTS);
+ cls_assert_modem_signals(ch);
+ }
+ ch->ch_tun.un_flags |= (UN_EMPTY);
+ }
+
+ /*
+ * If DTR Toggle mode is on, turn on DTR now if not already set,
+ * and make sure we get an event when the data transfer has
+ * completed.
+ */
+ if (ch->ch_digi.digi_flags & DIGI_DTR_TOGGLE) {
+ if (!(ch->ch_mostat & UART_MCR_DTR)) {
+ ch->ch_mostat |= (UART_MCR_DTR);
+ cls_assert_modem_signals(ch);
+ }
+ ch->ch_tun.un_flags |= (UN_EMPTY);
+ }
+ writeb(ch->ch_wqueue[ch->ch_w_tail], &ch->ch_cls_uart->txrx);
+ ch->ch_w_tail++;
+ ch->ch_w_tail &= WQUEUEMASK;
+ ch->ch_txcount++;
+ len_written++;
+ n--;
+ }
+
+ if (len_written > 0)
+ ch->ch_flags &= ~(CH_TX_FIFO_EMPTY | CH_TX_FIFO_LWM);
+
+exit_unlock:
+ spin_unlock_irqrestore(&ch->ch_lock, flags);
+}
+
+static void cls_parse_modem(struct channel_t *ch, unsigned char signals)
+{
+ unsigned char msignals = signals;
+ unsigned long flags;
+
+ if (!ch || ch->magic != DGNC_CHANNEL_MAGIC)
+ return;
+
+ /*
+ * Do altpin switching. Altpin switches DCD and DSR.
+ * This prolly breaks DSRPACE, so we should be more clever here.
+ */
+ spin_lock_irqsave(&ch->ch_lock, flags);
+ if (ch->ch_digi.digi_flags & DIGI_ALTPIN) {
+ unsigned char mswap = signals;
+
+ if (mswap & UART_MSR_DDCD) {
+ msignals &= ~UART_MSR_DDCD;
+ msignals |= UART_MSR_DDSR;
+ }
+ if (mswap & UART_MSR_DDSR) {
+ msignals &= ~UART_MSR_DDSR;
+ msignals |= UART_MSR_DDCD;
+ }
+ if (mswap & UART_MSR_DCD) {
+ msignals &= ~UART_MSR_DCD;
+ msignals |= UART_MSR_DSR;
+ }
+ if (mswap & UART_MSR_DSR) {
+ msignals &= ~UART_MSR_DSR;
+ msignals |= UART_MSR_DCD;
+ }
+ }
+ spin_unlock_irqrestore(&ch->ch_lock, flags);
+
+ /*
+ * Scrub off lower bits. They signify delta's, which I don't
+ * care about
+ */
+ signals &= 0xf0;
+
+ spin_lock_irqsave(&ch->ch_lock, flags);
+ if (msignals & UART_MSR_DCD)
+ ch->ch_mistat |= UART_MSR_DCD;
+ else
+ ch->ch_mistat &= ~UART_MSR_DCD;
+
+ if (msignals & UART_MSR_DSR)
+ ch->ch_mistat |= UART_MSR_DSR;
+ else
+ ch->ch_mistat &= ~UART_MSR_DSR;
+
+ if (msignals & UART_MSR_RI)
+ ch->ch_mistat |= UART_MSR_RI;
+ else
+ ch->ch_mistat &= ~UART_MSR_RI;
+
+ if (msignals & UART_MSR_CTS)
+ ch->ch_mistat |= UART_MSR_CTS;
+ else
+ ch->ch_mistat &= ~UART_MSR_CTS;
+ spin_unlock_irqrestore(&ch->ch_lock, flags);
+}
+
+/* Make the UART raise any of the output signals we want up */
+static void cls_assert_modem_signals(struct channel_t *ch)
+{
+ unsigned char out;
+
+ if (!ch || ch->magic != DGNC_CHANNEL_MAGIC)
+ return;
+
+ out = ch->ch_mostat;
+
+ if (ch->ch_flags & CH_LOOPBACK)
+ out |= UART_MCR_LOOP;
+
+ writeb(out, &ch->ch_cls_uart->mcr);
+
+ /* Give time for the UART to actually drop the signals */
+ udelay(10);
+}
+
+static void cls_send_start_character(struct channel_t *ch)
+{
+ if (!ch || ch->magic != DGNC_CHANNEL_MAGIC)
+ return;
+
+ if (ch->ch_startc != _POSIX_VDISABLE) {
+ ch->ch_xon_sends++;
+ writeb(ch->ch_startc, &ch->ch_cls_uart->txrx);
+ }
+}
+
+static void cls_send_stop_character(struct channel_t *ch)
+{
+ if (!ch || ch->magic != DGNC_CHANNEL_MAGIC)
+ return;
+
+ if (ch->ch_stopc != _POSIX_VDISABLE) {
+ ch->ch_xoff_sends++;
+ writeb(ch->ch_stopc, &ch->ch_cls_uart->txrx);
+ }
+}
+
+/* Inits UART */
+static void cls_uart_init(struct channel_t *ch)
+{
+ unsigned char lcrb = readb(&ch->ch_cls_uart->lcr);
+ unsigned char isr_fcr = 0;
+
+ writeb(0, &ch->ch_cls_uart->ier);
+
+ /*
+ * The Enhanced Register Set may only be accessed when
+ * the Line Control Register is set to 0xBFh.
+ */
+ writeb(UART_EXAR654_ENHANCED_REGISTER_SET, &ch->ch_cls_uart->lcr);
+
+ isr_fcr = readb(&ch->ch_cls_uart->isr_fcr);
+
+ /* Turn on Enhanced/Extended controls */
+ isr_fcr |= (UART_EXAR654_EFR_ECB);
+
+ writeb(isr_fcr, &ch->ch_cls_uart->isr_fcr);
+
+ /* Write old LCR value back out, which turns enhanced access off */
+ writeb(lcrb, &ch->ch_cls_uart->lcr);
+
+ /* Clear out UART and FIFO */
+ readb(&ch->ch_cls_uart->txrx);
+
+ writeb((UART_FCR_ENABLE_FIFO|UART_FCR_CLEAR_RCVR|UART_FCR_CLEAR_XMIT),
+ &ch->ch_cls_uart->isr_fcr);
+ udelay(10);
+
+ ch->ch_flags |= (CH_FIFO_ENABLED | CH_TX_FIFO_EMPTY | CH_TX_FIFO_LWM);
+
+ readb(&ch->ch_cls_uart->lsr);
+ readb(&ch->ch_cls_uart->msr);
+}
+
+/*
+ * Turns off UART.
+ */
+static void cls_uart_off(struct channel_t *ch)
+{
+ writeb(0, &ch->ch_cls_uart->ier);
+}
+
+/*
+ * cls_get_uarts_bytes_left.
+ * Returns 0 is nothing left in the FIFO, returns 1 otherwise.
+ *
+ * The channel lock MUST be held by the calling function.
+ */
+static uint cls_get_uart_bytes_left(struct channel_t *ch)
+{
+ unsigned char left = 0;
+ unsigned char lsr = 0;
+
+ if (!ch || ch->magic != DGNC_CHANNEL_MAGIC)
+ return 0;
+
+ lsr = readb(&ch->ch_cls_uart->lsr);
+
+ /* Determine whether the Transmitter is empty or not */
+ if (!(lsr & UART_LSR_TEMT)) {
+ if (ch->ch_flags & CH_TX_FIFO_EMPTY)
+ tasklet_schedule(&ch->ch_bd->helper_tasklet);
+ left = 1;
+ } else {
+ ch->ch_flags |= (CH_TX_FIFO_EMPTY | CH_TX_FIFO_LWM);
+ left = 0;
+ }
+
+ return left;
+}
+
+/*
+ * cls_send_break.
+ * Starts sending a break thru the UART.
+ *
+ * The channel lock MUST be held by the calling function.
+ */
+static void cls_send_break(struct channel_t *ch, int msecs)
+{
+ if (!ch || ch->magic != DGNC_CHANNEL_MAGIC)
+ return;
+
+ /*
+ * If we receive a time of 0, this means turn off the break.
+ */
+ if (msecs == 0) {
+ /* Turn break off, and unset some variables */
+ if (ch->ch_flags & CH_BREAK_SENDING) {
+ unsigned char temp = readb(&ch->ch_cls_uart->lcr);
+
+ writeb((temp & ~UART_LCR_SBC), &ch->ch_cls_uart->lcr);
+ ch->ch_flags &= ~(CH_BREAK_SENDING);
+ ch->ch_stop_sending_break = 0;
+ }
+ return;
+ }
+
+ /*
+ * Set the time we should stop sending the break.
+ * If we are already sending a break, toss away the existing
+ * time to stop, and use this new value instead.
+ */
+ ch->ch_stop_sending_break = jiffies + dgnc_jiffies_from_ms(msecs);
+
+ /* Tell the UART to start sending the break */
+ if (!(ch->ch_flags & CH_BREAK_SENDING)) {
+ unsigned char temp = readb(&ch->ch_cls_uart->lcr);
+
+ writeb((temp | UART_LCR_SBC), &ch->ch_cls_uart->lcr);
+ ch->ch_flags |= (CH_BREAK_SENDING);
+ }
+}
+
+/*
+ * cls_send_immediate_char.
+ * Sends a specific character as soon as possible to the UART,
+ * jumping over any bytes that might be in the write queue.
+ *
+ * The channel lock MUST be held by the calling function.
+ */
+static void cls_send_immediate_char(struct channel_t *ch, unsigned char c)
+{
+ if (!ch || ch->magic != DGNC_CHANNEL_MAGIC)
+ return;
+
+ writeb(c, &ch->ch_cls_uart->txrx);
+}
+
+static void cls_vpd(struct dgnc_board *brd)
+{
+ ulong vpdbase; /* Start of io base of the card */
+ u8 __iomem *re_map_vpdbase;/* Remapped memory of the card */
+ int i = 0;
+
+ vpdbase = pci_resource_start(brd->pdev, 3);
+
+ /* No VPD */
+ if (!vpdbase)
+ return;
+
+ re_map_vpdbase = ioremap(vpdbase, 0x400);
+
+ if (!re_map_vpdbase)
+ return;
+
+ /* Store the VPD into our buffer */
+ for (i = 0; i < 0x40; i++) {
+ brd->vpd[i] = readb(re_map_vpdbase + i);
+ pr_info("%x ", brd->vpd[i]);
+ }
+ pr_info("\n");
+
+ if (re_map_vpdbase)
+ iounmap(re_map_vpdbase);
+}
diff --git a/drivers/staging/dgnc/dgnc_cls.h b/drivers/staging/dgnc/dgnc_cls.h
new file mode 100644
index 000000000..2597e36d3
--- /dev/null
+++ b/drivers/staging/dgnc/dgnc_cls.h
@@ -0,0 +1,82 @@
+/*
+ * Copyright 2003 Digi International (www.digi.com)
+ * Scott H Kilau <Scott_Kilau at digi dot 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, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED; without even the
+ * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ * PURPOSE. See the GNU General Public License for more details.
+ */
+
+#ifndef __DGNC_CLS_H
+#define __DGNC_CLS_H
+
+/************************************************************************
+ * Per channel/port Classic UART structure *
+ ************************************************************************
+ * Base Structure Entries Usage Meanings to Host *
+ * *
+ * W = read write R = read only *
+ * U = Unused. *
+ ************************************************************************/
+
+/*
+ * txrx : WR RHR/THR - Holding reg
+ * ier : WR IER - Interrupt Enable Reg
+ * isr_fcr : WR ISR/FCR - Interrupt Status Reg/Fifo Control Reg
+ * lcr : WR LCR - Line Control Reg
+ * mcr : WR MCR - Modem Control Reg
+ * lsr : WR LSR - Line Status Reg
+ * msr : WR MSG - Modem Status Reg
+ * spr : WR SPR - Scratch pad Reg
+ */
+struct cls_uart_struct {
+ u8 txrx;
+ u8 ier;
+ u8 isr_fcr;
+ u8 lcr;
+ u8 mcr;
+ u8 lsr;
+ u8 msr;
+ u8 spr;
+};
+
+/* Where to read the interrupt register (8bits) */
+#define UART_CLASSIC_POLL_ADDR_OFFSET 0x40
+
+#define UART_EXAR654_ENHANCED_REGISTER_SET 0xBF
+
+#define UART_16654_FCR_TXTRIGGER_16 0x10
+#define UART_16654_FCR_RXTRIGGER_16 0x40
+#define UART_16654_FCR_RXTRIGGER_56 0x80
+
+/* Received CTS/RTS change of state */
+#define UART_IIR_CTSRTS 0x20
+
+/* Receiver data TIMEOUT */
+#define UART_IIR_RDI_TIMEOUT 0x0C
+
+/*
+ * These are the EXTENDED definitions for the Exar 654's Interrupt
+ * Enable Register.
+ */
+#define UART_EXAR654_EFR_ECB 0x10 /* Enhanced control bit */
+#define UART_EXAR654_EFR_IXON 0x2 /* Receiver compares Xon1/Xoff1 */
+#define UART_EXAR654_EFR_IXOFF 0x8 /* Transmit Xon1/Xoff1 */
+#define UART_EXAR654_EFR_RTSDTR 0x40 /* Auto RTS/DTR Flow Control Enable */
+#define UART_EXAR654_EFR_CTSDSR 0x80 /* Auto CTS/DSR Flow COntrol Enable */
+#define UART_EXAR654_IER_XOFF 0x20 /* Xoff Interrupt Enable */
+#define UART_EXAR654_IER_RTSDTR 0x40 /* Output Interrupt Enable */
+#define UART_EXAR654_IER_CTSDSR 0x80 /* Input Interrupt Enable */
+
+/*
+ * Our Global Variables
+ */
+extern struct board_ops dgnc_cls_ops;
+
+#endif
diff --git a/drivers/staging/dgnc/dgnc_driver.c b/drivers/staging/dgnc/dgnc_driver.c
new file mode 100644
index 000000000..805dc617e
--- /dev/null
+++ b/drivers/staging/dgnc/dgnc_driver.c
@@ -0,0 +1,729 @@
+/*
+ * Copyright 2003 Digi International (www.digi.com)
+ * Scott H Kilau <Scott_Kilau at digi dot 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, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED; without even the
+ * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ * PURPOSE. See the GNU General Public License for more details.
+ */
+
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/slab.h>
+#include <linux/sched.h>
+#include "dgnc_driver.h"
+#include "dgnc_pci.h"
+#include "dgnc_mgmt.h"
+#include "dgnc_tty.h"
+#include "dgnc_cls.h"
+#include "dgnc_neo.h"
+#include "dgnc_sysfs.h"
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Digi International, http://www.digi.com");
+MODULE_DESCRIPTION("Driver for the Digi International Neo and Classic PCI based product line");
+MODULE_SUPPORTED_DEVICE("dgnc");
+
+/**************************************************************************
+ *
+ * protos for this file
+ *
+ */
+static int dgnc_start(void);
+static int dgnc_finalize_board_init(struct dgnc_board *brd);
+static void dgnc_init_globals(void);
+static int dgnc_found_board(struct pci_dev *pdev, int id);
+static void dgnc_cleanup_board(struct dgnc_board *brd);
+static void dgnc_poll_handler(ulong dummy);
+static int dgnc_init_one(struct pci_dev *pdev,
+ const struct pci_device_id *ent);
+static void dgnc_do_remap(struct dgnc_board *brd);
+
+/*
+ * File operations permitted on Control/Management major.
+ */
+static const struct file_operations dgnc_BoardFops = {
+ .owner = THIS_MODULE,
+ .unlocked_ioctl = dgnc_mgmt_ioctl,
+ .open = dgnc_mgmt_open,
+ .release = dgnc_mgmt_close
+};
+
+
+/*
+ * Globals
+ */
+uint dgnc_NumBoards;
+struct dgnc_board *dgnc_Board[MAXBOARDS];
+DEFINE_SPINLOCK(dgnc_global_lock);
+uint dgnc_Major;
+int dgnc_poll_tick = 20; /* Poll interval - 20 ms */
+
+/*
+ * Static vars.
+ */
+static struct class *dgnc_class;
+
+/*
+ * Poller stuff
+ */
+static DEFINE_SPINLOCK(dgnc_poll_lock); /* Poll scheduling lock */
+static ulong dgnc_poll_time; /* Time of next poll */
+static uint dgnc_poll_stop; /* Used to tell poller to stop */
+static struct timer_list dgnc_poll_timer;
+
+
+static const struct pci_device_id dgnc_pci_tbl[] = {
+ {PCI_DEVICE(DIGI_VID, PCI_DEVICE_CLASSIC_4_DID), .driver_data = 0},
+ {PCI_DEVICE(DIGI_VID, PCI_DEVICE_CLASSIC_4_422_DID), .driver_data = 1},
+ {PCI_DEVICE(DIGI_VID, PCI_DEVICE_CLASSIC_8_DID), .driver_data = 2},
+ {PCI_DEVICE(DIGI_VID, PCI_DEVICE_CLASSIC_8_422_DID), .driver_data = 3},
+ {0,}
+};
+MODULE_DEVICE_TABLE(pci, dgnc_pci_tbl);
+
+struct board_id {
+ unsigned char *name;
+ uint maxports;
+ unsigned int is_pci_express;
+};
+
+static struct board_id dgnc_Ids[] = {
+ { PCI_DEVICE_CLASSIC_4_PCI_NAME, 4, 0 },
+ { PCI_DEVICE_CLASSIC_4_422_PCI_NAME, 4, 0 },
+ { PCI_DEVICE_CLASSIC_8_PCI_NAME, 8, 0 },
+ { PCI_DEVICE_CLASSIC_8_422_PCI_NAME, 8, 0 },
+ { PCI_DEVICE_NEO_4_PCI_NAME, 4, 0 },
+ { PCI_DEVICE_NEO_8_PCI_NAME, 8, 0 },
+ { PCI_DEVICE_NEO_2DB9_PCI_NAME, 2, 0 },
+ { PCI_DEVICE_NEO_2DB9PRI_PCI_NAME, 2, 0 },
+ { PCI_DEVICE_NEO_2RJ45_PCI_NAME, 2, 0 },
+ { PCI_DEVICE_NEO_2RJ45PRI_PCI_NAME, 2, 0 },
+ { PCI_DEVICE_NEO_1_422_PCI_NAME, 1, 0 },
+ { PCI_DEVICE_NEO_1_422_485_PCI_NAME, 1, 0 },
+ { PCI_DEVICE_NEO_2_422_485_PCI_NAME, 2, 0 },
+ { PCI_DEVICE_NEO_EXPRESS_8_PCI_NAME, 8, 1 },
+ { PCI_DEVICE_NEO_EXPRESS_4_PCI_NAME, 4, 1 },
+ { PCI_DEVICE_NEO_EXPRESS_4RJ45_PCI_NAME, 4, 1 },
+ { PCI_DEVICE_NEO_EXPRESS_8RJ45_PCI_NAME, 8, 1 },
+ { NULL, 0, 0 }
+};
+
+static struct pci_driver dgnc_driver = {
+ .name = "dgnc",
+ .probe = dgnc_init_one,
+ .id_table = dgnc_pci_tbl,
+};
+
+/************************************************************************
+ *
+ * Driver load/unload functions
+ *
+ ************************************************************************/
+
+/*
+ * dgnc_cleanup_module()
+ *
+ * Module unload. This is where it all ends.
+ */
+static void dgnc_cleanup_module(void)
+{
+ int i;
+ unsigned long flags;
+
+ spin_lock_irqsave(&dgnc_poll_lock, flags);
+ dgnc_poll_stop = 1;
+ spin_unlock_irqrestore(&dgnc_poll_lock, flags);
+
+ /* Turn off poller right away. */
+ del_timer_sync(&dgnc_poll_timer);
+
+ dgnc_remove_driver_sysfiles(&dgnc_driver);
+
+ device_destroy(dgnc_class, MKDEV(dgnc_Major, 0));
+ class_destroy(dgnc_class);
+ unregister_chrdev(dgnc_Major, "dgnc");
+
+ for (i = 0; i < dgnc_NumBoards; ++i) {
+ dgnc_remove_ports_sysfiles(dgnc_Board[i]);
+ dgnc_tty_uninit(dgnc_Board[i]);
+ dgnc_cleanup_board(dgnc_Board[i]);
+ }
+
+ dgnc_tty_post_uninit();
+
+ if (dgnc_NumBoards)
+ pci_unregister_driver(&dgnc_driver);
+}
+
+/*
+ * init_module()
+ *
+ * Module load. This is where it all starts.
+ */
+static int __init dgnc_init_module(void)
+{
+ int rc = 0;
+
+ /*
+ * Initialize global stuff
+ */
+ rc = dgnc_start();
+
+ if (rc < 0)
+ return rc;
+
+ /*
+ * Find and configure all the cards
+ */
+ rc = pci_register_driver(&dgnc_driver);
+
+ /*
+ * If something went wrong in the scan, bail out of driver.
+ */
+ if (rc < 0) {
+ /* Only unregister if it was actually registered. */
+ if (dgnc_NumBoards)
+ pci_unregister_driver(&dgnc_driver);
+ else
+ pr_warn("WARNING: dgnc driver load failed. No Digi Neo or Classic boards found.\n");
+
+ dgnc_cleanup_module();
+ } else {
+ dgnc_create_driver_sysfiles(&dgnc_driver);
+ }
+
+ return rc;
+}
+
+module_init(dgnc_init_module);
+module_exit(dgnc_cleanup_module);
+
+/*
+ * Start of driver.
+ */
+static int dgnc_start(void)
+{
+ int rc = 0;
+ unsigned long flags;
+ struct device *dev;
+
+ /* make sure that the globals are init'd before we do anything else */
+ dgnc_init_globals();
+
+ /*
+ * Register our base character device into the kernel.
+ * This allows the download daemon to connect to the downld device
+ * before any of the boards are init'ed.
+ *
+ * Register management/dpa devices
+ */
+ rc = register_chrdev(0, "dgnc", &dgnc_BoardFops);
+ if (rc < 0) {
+ pr_err(DRVSTR ": Can't register dgnc driver device (%d)\n", rc);
+ return rc;
+ }
+ dgnc_Major = rc;
+
+ dgnc_class = class_create(THIS_MODULE, "dgnc_mgmt");
+ if (IS_ERR(dgnc_class)) {
+ rc = PTR_ERR(dgnc_class);
+ pr_err(DRVSTR ": Can't create dgnc_mgmt class (%d)\n", rc);
+ goto failed_class;
+ }
+
+ dev = device_create(dgnc_class, NULL,
+ MKDEV(dgnc_Major, 0),
+ NULL, "dgnc_mgmt");
+ if (IS_ERR(dev)) {
+ rc = PTR_ERR(dev);
+ pr_err(DRVSTR ": Can't create device (%d)\n", rc);
+ goto failed_device;
+ }
+
+ /*
+ * Init any global tty stuff.
+ */
+ rc = dgnc_tty_preinit();
+
+ if (rc < 0) {
+ pr_err(DRVSTR ": tty preinit - not enough memory (%d)\n", rc);
+ goto failed_tty;
+ }
+
+ /* Start the poller */
+ spin_lock_irqsave(&dgnc_poll_lock, flags);
+ setup_timer(&dgnc_poll_timer, dgnc_poll_handler, 0);
+ dgnc_poll_time = jiffies + dgnc_jiffies_from_ms(dgnc_poll_tick);
+ dgnc_poll_timer.expires = dgnc_poll_time;
+ spin_unlock_irqrestore(&dgnc_poll_lock, flags);
+
+ add_timer(&dgnc_poll_timer);
+
+ return 0;
+
+failed_tty:
+ device_destroy(dgnc_class, MKDEV(dgnc_Major, 0));
+failed_device:
+ class_destroy(dgnc_class);
+failed_class:
+ unregister_chrdev(dgnc_Major, "dgnc");
+ return rc;
+}
+
+/* returns count (>= 0), or negative on error */
+static int dgnc_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
+{
+ int rc;
+
+ /* wake up and enable device */
+ rc = pci_enable_device(pdev);
+
+ if (rc < 0) {
+ rc = -EIO;
+ } else {
+ rc = dgnc_found_board(pdev, ent->driver_data);
+ if (rc == 0)
+ dgnc_NumBoards++;
+ }
+ return rc;
+}
+
+/*
+ * dgnc_cleanup_board()
+ *
+ * Free all the memory associated with a board
+ */
+static void dgnc_cleanup_board(struct dgnc_board *brd)
+{
+ int i = 0;
+
+ if (!brd || brd->magic != DGNC_BOARD_MAGIC)
+ return;
+
+ switch (brd->device) {
+ case PCI_DEVICE_CLASSIC_4_DID:
+ case PCI_DEVICE_CLASSIC_8_DID:
+ case PCI_DEVICE_CLASSIC_4_422_DID:
+ case PCI_DEVICE_CLASSIC_8_422_DID:
+
+ /* Tell card not to interrupt anymore. */
+ outb(0, brd->iobase + 0x4c);
+ break;
+
+ default:
+ break;
+ }
+
+ if (brd->irq)
+ free_irq(brd->irq, brd);
+
+ tasklet_kill(&brd->helper_tasklet);
+
+ if (brd->re_map_membase) {
+ iounmap(brd->re_map_membase);
+ brd->re_map_membase = NULL;
+ }
+
+ if (brd->msgbuf_head) {
+ unsigned long flags;
+
+ spin_lock_irqsave(&dgnc_global_lock, flags);
+ brd->msgbuf = NULL;
+ dev_dbg(&brd->pdev->dev, "%s\n", brd->msgbuf_head);
+ kfree(brd->msgbuf_head);
+ brd->msgbuf_head = NULL;
+ spin_unlock_irqrestore(&dgnc_global_lock, flags);
+ }
+
+ /* Free all allocated channels structs */
+ for (i = 0; i < MAXPORTS ; i++) {
+ if (brd->channels[i]) {
+ kfree(brd->channels[i]->ch_rqueue);
+ kfree(brd->channels[i]->ch_equeue);
+ kfree(brd->channels[i]->ch_wqueue);
+ kfree(brd->channels[i]);
+ brd->channels[i] = NULL;
+ }
+ }
+
+ kfree(brd->flipbuf);
+
+ dgnc_Board[brd->boardnum] = NULL;
+
+ kfree(brd);
+}
+
+
+/*
+ * dgnc_found_board()
+ *
+ * A board has been found, init it.
+ */
+static int dgnc_found_board(struct pci_dev *pdev, int id)
+{
+ struct dgnc_board *brd;
+ unsigned int pci_irq;
+ int i = 0;
+ int rc = 0;
+ unsigned long flags;
+
+ /* get the board structure and prep it */
+ dgnc_Board[dgnc_NumBoards] = kzalloc(sizeof(*brd), GFP_KERNEL);
+ brd = dgnc_Board[dgnc_NumBoards];
+
+ if (!brd)
+ return -ENOMEM;
+
+ /* make a temporary message buffer for the boot messages */
+ brd->msgbuf_head = kcalloc(8192, sizeof(u8), GFP_KERNEL);
+ brd->msgbuf = brd->msgbuf_head;
+
+ if (!brd->msgbuf) {
+ kfree(brd);
+ return -ENOMEM;
+ }
+
+ /* store the info for the board we've found */
+ brd->magic = DGNC_BOARD_MAGIC;
+ brd->boardnum = dgnc_NumBoards;
+ brd->vendor = dgnc_pci_tbl[id].vendor;
+ brd->device = dgnc_pci_tbl[id].device;
+ brd->pdev = pdev;
+ brd->pci_bus = pdev->bus->number;
+ brd->pci_slot = PCI_SLOT(pdev->devfn);
+ brd->name = dgnc_Ids[id].name;
+ brd->maxports = dgnc_Ids[id].maxports;
+ if (dgnc_Ids[i].is_pci_express)
+ brd->bd_flags |= BD_IS_PCI_EXPRESS;
+ brd->dpastatus = BD_NOFEP;
+ init_waitqueue_head(&brd->state_wait);
+
+ spin_lock_init(&brd->bd_lock);
+ spin_lock_init(&brd->bd_intr_lock);
+
+ brd->state = BOARD_FOUND;
+
+ for (i = 0; i < MAXPORTS; i++)
+ brd->channels[i] = NULL;
+
+ /* store which card & revision we have */
+ pci_read_config_word(pdev, PCI_SUBSYSTEM_VENDOR_ID, &brd->subvendor);
+ pci_read_config_word(pdev, PCI_SUBSYSTEM_ID, &brd->subdevice);
+ pci_read_config_byte(pdev, PCI_REVISION_ID, &brd->rev);
+
+ pci_irq = pdev->irq;
+ brd->irq = pci_irq;
+
+
+ switch (brd->device) {
+
+ case PCI_DEVICE_CLASSIC_4_DID:
+ case PCI_DEVICE_CLASSIC_8_DID:
+ case PCI_DEVICE_CLASSIC_4_422_DID:
+ case PCI_DEVICE_CLASSIC_8_422_DID:
+
+ brd->dpatype = T_CLASSIC | T_PCIBUS;
+
+ /*
+ * For PCI ClassicBoards
+ * PCI Local Address (i.e. "resource" number) space
+ * 0 PLX Memory Mapped Config
+ * 1 PLX I/O Mapped Config
+ * 2 I/O Mapped UARTs and Status
+ * 3 Memory Mapped VPD
+ * 4 Memory Mapped UARTs and Status
+ */
+
+
+ /* get the PCI Base Address Registers */
+ brd->membase = pci_resource_start(pdev, 4);
+
+ if (!brd->membase) {
+ dev_err(&brd->pdev->dev,
+ "Card has no PCI IO resources, failing.\n");
+ return -ENODEV;
+ }
+
+ brd->membase_end = pci_resource_end(pdev, 4);
+
+ if (brd->membase & 1)
+ brd->membase &= ~3;
+ else
+ brd->membase &= ~15;
+
+ brd->iobase = pci_resource_start(pdev, 1);
+ brd->iobase_end = pci_resource_end(pdev, 1);
+ brd->iobase = ((unsigned int) (brd->iobase)) & 0xFFFE;
+
+ /* Assign the board_ops struct */
+ brd->bd_ops = &dgnc_cls_ops;
+
+ brd->bd_uart_offset = 0x8;
+ brd->bd_dividend = 921600;
+
+ dgnc_do_remap(brd);
+
+ /* Get and store the board VPD, if it exists */
+ brd->bd_ops->vpd(brd);
+
+ /*
+ * Enable Local Interrupt 1 (0x1),
+ * Local Interrupt 1 Polarity Active high (0x2),
+ * Enable PCI interrupt (0x40)
+ */
+ outb(0x43, brd->iobase + 0x4c);
+
+ break;
+
+
+ case PCI_DEVICE_NEO_4_DID:
+ case PCI_DEVICE_NEO_8_DID:
+ case PCI_DEVICE_NEO_2DB9_DID:
+ case PCI_DEVICE_NEO_2DB9PRI_DID:
+ case PCI_DEVICE_NEO_2RJ45_DID:
+ case PCI_DEVICE_NEO_2RJ45PRI_DID:
+ case PCI_DEVICE_NEO_1_422_DID:
+ case PCI_DEVICE_NEO_1_422_485_DID:
+ case PCI_DEVICE_NEO_2_422_485_DID:
+ case PCI_DEVICE_NEO_EXPRESS_8_DID:
+ case PCI_DEVICE_NEO_EXPRESS_4_DID:
+ case PCI_DEVICE_NEO_EXPRESS_4RJ45_DID:
+ case PCI_DEVICE_NEO_EXPRESS_8RJ45_DID:
+
+ /*
+ * This chip is set up 100% when we get to it.
+ * No need to enable global interrupts or anything.
+ */
+ if (brd->bd_flags & BD_IS_PCI_EXPRESS)
+ brd->dpatype = T_NEO_EXPRESS | T_PCIBUS;
+ else
+ brd->dpatype = T_NEO | T_PCIBUS;
+
+ /* get the PCI Base Address Registers */
+ brd->membase = pci_resource_start(pdev, 0);
+ brd->membase_end = pci_resource_end(pdev, 0);
+
+ if (brd->membase & 1)
+ brd->membase &= ~3;
+ else
+ brd->membase &= ~15;
+
+ /* Assign the board_ops struct */
+ brd->bd_ops = &dgnc_neo_ops;
+
+ brd->bd_uart_offset = 0x200;
+ brd->bd_dividend = 921600;
+
+ dgnc_do_remap(brd);
+
+ if (brd->re_map_membase) {
+
+ /* Read and store the dvid after remapping */
+ brd->dvid = readb(brd->re_map_membase + 0x8D);
+
+ /* Get and store the board VPD, if it exists */
+ brd->bd_ops->vpd(brd);
+ }
+ break;
+
+ default:
+ dev_err(&brd->pdev->dev,
+ "Didn't find any compatible Neo/Classic PCI boards.\n");
+ return -ENXIO;
+
+ }
+
+ /*
+ * Do tty device initialization.
+ */
+
+ rc = dgnc_tty_register(brd);
+ if (rc < 0) {
+ pr_err(DRVSTR ": Can't register tty devices (%d)\n", rc);
+ goto failed;
+ }
+
+ rc = dgnc_finalize_board_init(brd);
+ if (rc < 0) {
+ pr_err(DRVSTR ": Can't finalize board init (%d)\n", rc);
+ goto failed;
+ }
+
+ rc = dgnc_tty_init(brd);
+ if (rc < 0) {
+ pr_err(DRVSTR ": Can't init tty devices (%d)\n", rc);
+ goto failed;
+ }
+
+ brd->state = BOARD_READY;
+ brd->dpastatus = BD_RUNNING;
+
+ dgnc_create_ports_sysfiles(brd);
+
+ /* init our poll helper tasklet */
+ tasklet_init(&brd->helper_tasklet,
+ brd->bd_ops->tasklet,
+ (unsigned long) brd);
+
+ spin_lock_irqsave(&dgnc_global_lock, flags);
+ brd->msgbuf = NULL;
+ dev_dbg(&brd->pdev->dev, "%s\n", brd->msgbuf_head);
+ kfree(brd->msgbuf_head);
+ brd->msgbuf_head = NULL;
+ spin_unlock_irqrestore(&dgnc_global_lock, flags);
+
+ /*
+ * allocate flip buffer for board.
+ *
+ * Okay to malloc with GFP_KERNEL, we are not at interrupt
+ * context, and there are no locks held.
+ */
+ brd->flipbuf = kzalloc(MYFLIPLEN, GFP_KERNEL);
+
+ wake_up_interruptible(&brd->state_wait);
+
+ return 0;
+
+failed:
+ dgnc_tty_uninit(brd);
+ brd->state = BOARD_FAILED;
+ brd->dpastatus = BD_NOFEP;
+
+ return -ENXIO;
+
+}
+
+
+static int dgnc_finalize_board_init(struct dgnc_board *brd)
+{
+ int rc = 0;
+
+ if (!brd || brd->magic != DGNC_BOARD_MAGIC)
+ return -ENODEV;
+
+ if (brd->irq) {
+ rc = request_irq(brd->irq, brd->bd_ops->intr,
+ IRQF_SHARED, "DGNC", brd);
+
+ if (rc) {
+ dev_err(&brd->pdev->dev,
+ "Failed to hook IRQ %d\n", brd->irq);
+ brd->state = BOARD_FAILED;
+ brd->dpastatus = BD_NOFEP;
+ rc = -ENODEV;
+ }
+ }
+ return rc;
+}
+
+/*
+ * Remap PCI memory.
+ */
+static void dgnc_do_remap(struct dgnc_board *brd)
+{
+
+ if (!brd || brd->magic != DGNC_BOARD_MAGIC)
+ return;
+
+ brd->re_map_membase = ioremap(brd->membase, 0x1000);
+}
+
+
+/*****************************************************************************
+*
+* Function:
+*
+* dgnc_poll_handler
+*
+* Author:
+*
+* Scott H Kilau
+*
+* Parameters:
+*
+* dummy -- ignored
+*
+* Return Values:
+*
+* none
+*
+* Description:
+*
+* As each timer expires, it determines (a) whether the "transmit"
+* waiter needs to be woken up, and (b) whether the poller needs to
+* be rescheduled.
+*
+******************************************************************************/
+
+static void dgnc_poll_handler(ulong dummy)
+{
+ struct dgnc_board *brd;
+ unsigned long flags;
+ int i;
+ unsigned long new_time;
+
+ /* Go thru each board, kicking off a tasklet for each if needed */
+ for (i = 0; i < dgnc_NumBoards; i++) {
+ brd = dgnc_Board[i];
+
+ spin_lock_irqsave(&brd->bd_lock, flags);
+
+ /* If board is in a failed state don't schedule a tasklet */
+ if (brd->state == BOARD_FAILED) {
+ spin_unlock_irqrestore(&brd->bd_lock, flags);
+ continue;
+ }
+
+ /* Schedule a poll helper task */
+ tasklet_schedule(&brd->helper_tasklet);
+
+ spin_unlock_irqrestore(&brd->bd_lock, flags);
+ }
+
+ /*
+ * Schedule ourself back at the nominal wakeup interval.
+ */
+ spin_lock_irqsave(&dgnc_poll_lock, flags);
+ dgnc_poll_time += dgnc_jiffies_from_ms(dgnc_poll_tick);
+
+ new_time = dgnc_poll_time - jiffies;
+
+ if ((ulong) new_time >= 2 * dgnc_poll_tick)
+ dgnc_poll_time = jiffies + dgnc_jiffies_from_ms(dgnc_poll_tick);
+
+ setup_timer(&dgnc_poll_timer, dgnc_poll_handler, 0);
+ dgnc_poll_timer.expires = dgnc_poll_time;
+ spin_unlock_irqrestore(&dgnc_poll_lock, flags);
+
+ if (!dgnc_poll_stop)
+ add_timer(&dgnc_poll_timer);
+}
+
+/*
+ * dgnc_init_globals()
+ *
+ * This is where we initialize the globals from the static insmod
+ * configuration variables. These are declared near the head of
+ * this file.
+ */
+static void dgnc_init_globals(void)
+{
+ int i = 0;
+
+ dgnc_NumBoards = 0;
+
+ for (i = 0; i < MAXBOARDS; i++)
+ dgnc_Board[i] = NULL;
+
+ init_timer(&dgnc_poll_timer);
+}
+
diff --git a/drivers/staging/dgnc/dgnc_driver.h b/drivers/staging/dgnc/dgnc_driver.h
new file mode 100644
index 000000000..f77fed57b
--- /dev/null
+++ b/drivers/staging/dgnc/dgnc_driver.h
@@ -0,0 +1,400 @@
+/*
+ * Copyright 2003 Digi International (www.digi.com)
+ * Scott H Kilau <Scott_Kilau at digi dot 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, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED; without even the
+ * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ * PURPOSE. See the GNU General Public License for more details.
+ *
+ *************************************************************************
+ *
+ * Driver includes
+ *
+ *************************************************************************/
+
+#ifndef __DGNC_DRIVER_H
+#define __DGNC_DRIVER_H
+
+#include <linux/types.h> /* To pick up the varions Linux types */
+#include <linux/tty.h> /* To pick up the various tty structs/defines */
+#include <linux/interrupt.h> /* For irqreturn_t type */
+
+#include "digi.h" /* Digi specific ioctl header */
+#include "dgnc_sysfs.h" /* Support for SYSFS */
+
+/*************************************************************************
+ *
+ * Driver defines
+ *
+ *************************************************************************/
+
+/* Driver identification and error statments */
+#define PROCSTR "dgnc" /* /proc entries */
+#define DEVSTR "/dev/dg/dgnc" /* /dev entries */
+#define DRVSTR "dgnc" /* Driver name string */
+#define DG_PART "40002369_F" /* RPM part number */
+
+#define TRC_TO_CONSOLE 1
+
+/* Number of boards we support at once. */
+#define MAXBOARDS 20
+#define MAXPORTS 8
+#define MAXTTYNAMELEN 200
+
+/* Our 3 magic numbers for our board, channel and unit structs */
+#define DGNC_BOARD_MAGIC 0x5c6df104
+#define DGNC_CHANNEL_MAGIC 0x6c6df104
+#define DGNC_UNIT_MAGIC 0x7c6df104
+
+/* Serial port types */
+#define DGNC_SERIAL 0
+#define DGNC_PRINT 1
+
+#define SERIAL_TYPE_NORMAL 1
+
+#define PORT_NUM(dev) ((dev) & 0x7f)
+#define IS_PRINT(dev) (((dev) & 0xff) >= 0x80)
+
+/* MAX number of stop characters we will send when our read queue is getting full */
+#define MAX_STOPS_SENT 5
+
+/* 4 extra for alignment play space */
+#define WRITEBUFLEN ((4096) + 4)
+#define MYFLIPLEN N_TTY_BUF_SIZE
+
+#define dgnc_jiffies_from_ms(a) (((a) * HZ) / 1000)
+
+/*
+ * Define a local default termios struct. All ports will be created
+ * with this termios initially. This is the same structure that is defined
+ * as the default in tty_io.c with the same settings overriden as in serial.c
+ *
+ * In short, this should match the internal serial ports' defaults.
+ */
+#define DEFAULT_IFLAGS (ICRNL | IXON)
+#define DEFAULT_OFLAGS (OPOST | ONLCR)
+#define DEFAULT_CFLAGS (B9600 | CS8 | CREAD | HUPCL | CLOCAL)
+#define DEFAULT_LFLAGS (ISIG | ICANON | ECHO | ECHOE | ECHOK | \
+ ECHOCTL | ECHOKE | IEXTEN)
+
+#ifndef _POSIX_VDISABLE
+#define _POSIX_VDISABLE '\0'
+#endif
+
+
+/*
+ * All the possible states the driver can be while being loaded.
+ */
+enum {
+ DRIVER_INITIALIZED = 0,
+ DRIVER_READY
+};
+
+/*
+ * All the possible states the board can be while booting up.
+ */
+enum {
+ BOARD_FAILED = 0,
+ BOARD_FOUND,
+ BOARD_READY
+};
+
+
+/*************************************************************************
+ *
+ * Structures and closely related defines.
+ *
+ *************************************************************************/
+
+struct dgnc_board;
+struct channel_t;
+
+/************************************************************************
+ * Per board operations structure *
+ ************************************************************************/
+struct board_ops {
+ void (*tasklet)(unsigned long data);
+ irqreturn_t (*intr)(int irq, void *voidbrd);
+ void (*uart_init)(struct channel_t *ch);
+ void (*uart_off)(struct channel_t *ch);
+ int (*drain)(struct tty_struct *tty, uint seconds);
+ void (*param)(struct tty_struct *tty);
+ void (*vpd)(struct dgnc_board *brd);
+ void (*assert_modem_signals)(struct channel_t *ch);
+ void (*flush_uart_write)(struct channel_t *ch);
+ void (*flush_uart_read)(struct channel_t *ch);
+ void (*disable_receiver)(struct channel_t *ch);
+ void (*enable_receiver)(struct channel_t *ch);
+ void (*send_break)(struct channel_t *ch, int);
+ void (*send_start_character)(struct channel_t *ch);
+ void (*send_stop_character)(struct channel_t *ch);
+ void (*copy_data_from_queue_to_uart)(struct channel_t *ch);
+ uint (*get_uart_bytes_left)(struct channel_t *ch);
+ void (*send_immediate_char)(struct channel_t *ch, unsigned char);
+};
+
+/************************************************************************
+ * Device flag definitions for bd_flags.
+ ************************************************************************/
+#define BD_IS_PCI_EXPRESS 0x0001 /* Is a PCI Express board */
+
+
+/*
+ * Per-board information
+ */
+struct dgnc_board {
+ int magic; /* Board Magic number. */
+ int boardnum; /* Board number: 0-32 */
+
+ int type; /* Type of board */
+ char *name; /* Product Name */
+ struct pci_dev *pdev; /* Pointer to the pci_dev struct */
+ unsigned long bd_flags; /* Board flags */
+ u16 vendor; /* PCI vendor ID */
+ u16 device; /* PCI device ID */
+ u16 subvendor; /* PCI subsystem vendor ID */
+ u16 subdevice; /* PCI subsystem device ID */
+ unsigned char rev; /* PCI revision ID */
+ uint pci_bus; /* PCI bus value */
+ uint pci_slot; /* PCI slot value */
+ uint maxports; /* MAX ports this board can handle */
+ unsigned char dvid; /* Board specific device id */
+ unsigned char vpd[128]; /* VPD of board, if found */
+ unsigned char serial_num[20]; /* Serial number of board, if found in VPD */
+
+ spinlock_t bd_lock; /* Used to protect board */
+
+ spinlock_t bd_intr_lock; /* Used to protect the poller tasklet and
+ * the interrupt routine from each other.
+ */
+
+ uint state; /* State of card. */
+ wait_queue_head_t state_wait; /* Place to sleep on for state change */
+
+ struct tasklet_struct helper_tasklet; /* Poll helper tasklet */
+
+ uint nasync; /* Number of ports on card */
+
+ uint irq; /* Interrupt request number */
+ ulong intr_count; /* Count of interrupts */
+ ulong intr_modem; /* Count of interrupts */
+ ulong intr_tx; /* Count of interrupts */
+ ulong intr_rx; /* Count of interrupts */
+
+ ulong membase; /* Start of base memory of the card */
+ ulong membase_end; /* End of base memory of the card */
+
+ u8 __iomem *re_map_membase;/* Remapped memory of the card */
+
+ ulong iobase; /* Start of io base of the card */
+ ulong iobase_end; /* End of io base of the card */
+
+ uint bd_uart_offset; /* Space between each UART */
+
+ struct channel_t *channels[MAXPORTS]; /* array of pointers to our channels. */
+
+ struct tty_driver SerialDriver;
+ char SerialName[200];
+ struct tty_driver PrintDriver;
+ char PrintName[200];
+
+ bool dgnc_Major_Serial_Registered;
+ bool dgnc_Major_TransparentPrint_Registered;
+
+ uint dgnc_Serial_Major;
+ uint dgnc_TransparentPrint_Major;
+
+ uint TtyRefCnt;
+
+ char *flipbuf; /* Our flip buffer, alloced if board is found */
+
+ u16 dpatype; /* The board "type", as defined by DPA */
+ u16 dpastatus; /* The board "status", as defined by DPA */
+
+ /*
+ * Mgmt data.
+ */
+ char *msgbuf_head;
+ char *msgbuf;
+
+ uint bd_dividend; /* Board/UARTs specific dividend */
+
+ struct board_ops *bd_ops;
+
+ /* /proc/<board> entries */
+ struct proc_dir_entry *proc_entry_pointer;
+ struct dgnc_proc_entry *dgnc_board_table;
+
+};
+
+
+/************************************************************************
+ * Unit flag definitions for un_flags.
+ ************************************************************************/
+#define UN_ISOPEN 0x0001 /* Device is open */
+#define UN_CLOSING 0x0002 /* Line is being closed */
+#define UN_IMM 0x0004 /* Service immediately */
+#define UN_BUSY 0x0008 /* Some work this channel */
+#define UN_BREAKI 0x0010 /* Input break received */
+#define UN_PWAIT 0x0020 /* Printer waiting for terminal */
+#define UN_TIME 0x0040 /* Waiting on time */
+#define UN_EMPTY 0x0080 /* Waiting output queue empty */
+#define UN_LOW 0x0100 /* Waiting output low water mark*/
+#define UN_EXCL_OPEN 0x0200 /* Open for exclusive use */
+#define UN_WOPEN 0x0400 /* Device waiting for open */
+#define UN_WIOCTL 0x0800 /* Device waiting for open */
+#define UN_HANGUP 0x8000 /* Carrier lost */
+
+struct device;
+
+/************************************************************************
+ * Structure for terminal or printer unit.
+ ************************************************************************/
+struct un_t {
+ int magic; /* Unit Magic Number. */
+ struct channel_t *un_ch;
+ ulong un_time;
+ uint un_type;
+ uint un_open_count; /* Counter of opens to port */
+ struct tty_struct *un_tty;/* Pointer to unit tty structure */
+ uint un_flags; /* Unit flags */
+ wait_queue_head_t un_flags_wait; /* Place to sleep to wait on unit */
+ uint un_dev; /* Minor device number */
+ struct device *un_sysfs;
+};
+
+
+/************************************************************************
+ * Device flag definitions for ch_flags.
+ ************************************************************************/
+#define CH_PRON 0x0001 /* Printer on string */
+#define CH_STOP 0x0002 /* Output is stopped */
+#define CH_STOPI 0x0004 /* Input is stopped */
+#define CH_CD 0x0008 /* Carrier is present */
+#define CH_FCAR 0x0010 /* Carrier forced on */
+#define CH_HANGUP 0x0020 /* Hangup received */
+
+#define CH_RECEIVER_OFF 0x0040 /* Receiver is off */
+#define CH_OPENING 0x0080 /* Port in fragile open state */
+#define CH_CLOSING 0x0100 /* Port in fragile close state */
+#define CH_FIFO_ENABLED 0x0200 /* Port has FIFOs enabled */
+#define CH_TX_FIFO_EMPTY 0x0400 /* TX Fifo is completely empty */
+#define CH_TX_FIFO_LWM 0x0800 /* TX Fifo is below Low Water */
+#define CH_BREAK_SENDING 0x1000 /* Break is being sent */
+#define CH_LOOPBACK 0x2000 /* Channel is in lookback mode */
+#define CH_FLIPBUF_IN_USE 0x4000 /* Channel's flipbuf is in use */
+#define CH_BAUD0 0x08000 /* Used for checking B0 transitions */
+#define CH_FORCED_STOP 0x20000 /* Output is forcibly stopped */
+#define CH_FORCED_STOPI 0x40000 /* Input is forcibly stopped */
+
+
+/* Our Read/Error/Write queue sizes */
+#define RQUEUEMASK 0x1FFF /* 8 K - 1 */
+#define EQUEUEMASK 0x1FFF /* 8 K - 1 */
+#define WQUEUEMASK 0x0FFF /* 4 K - 1 */
+#define RQUEUESIZE (RQUEUEMASK + 1)
+#define EQUEUESIZE RQUEUESIZE
+#define WQUEUESIZE (WQUEUEMASK + 1)
+
+
+/************************************************************************
+ * Channel information structure.
+ ************************************************************************/
+struct channel_t {
+ int magic; /* Channel Magic Number */
+ struct dgnc_board *ch_bd; /* Board structure pointer */
+ struct digi_t ch_digi; /* Transparent Print structure */
+ struct un_t ch_tun; /* Terminal unit info */
+ struct un_t ch_pun; /* Printer unit info */
+
+ spinlock_t ch_lock; /* provide for serialization */
+ wait_queue_head_t ch_flags_wait;
+
+ uint ch_portnum; /* Port number, 0 offset. */
+ uint ch_open_count; /* open count */
+ uint ch_flags; /* Channel flags */
+
+ ulong ch_close_delay; /* How long we should drop RTS/DTR for */
+
+ ulong ch_cpstime; /* Time for CPS calculations */
+
+ tcflag_t ch_c_iflag; /* channel iflags */
+ tcflag_t ch_c_cflag; /* channel cflags */
+ tcflag_t ch_c_oflag; /* channel oflags */
+ tcflag_t ch_c_lflag; /* channel lflags */
+ unsigned char ch_stopc; /* Stop character */
+ unsigned char ch_startc; /* Start character */
+
+ uint ch_old_baud; /* Cache of the current baud */
+ uint ch_custom_speed;/* Custom baud, if set */
+
+ uint ch_wopen; /* Waiting for open process cnt */
+
+ unsigned char ch_mostat; /* FEP output modem status */
+ unsigned char ch_mistat; /* FEP input modem status */
+
+ struct neo_uart_struct __iomem *ch_neo_uart; /* Pointer to the "mapped" UART struct */
+ struct cls_uart_struct __iomem *ch_cls_uart; /* Pointer to the "mapped" UART struct */
+
+ unsigned char ch_cached_lsr; /* Cached value of the LSR register */
+
+ unsigned char *ch_rqueue; /* Our read queue buffer - malloc'ed */
+ ushort ch_r_head; /* Head location of the read queue */
+ ushort ch_r_tail; /* Tail location of the read queue */
+
+ unsigned char *ch_equeue; /* Our error queue buffer - malloc'ed */
+ ushort ch_e_head; /* Head location of the error queue */
+ ushort ch_e_tail; /* Tail location of the error queue */
+
+ unsigned char *ch_wqueue; /* Our write queue buffer - malloc'ed */
+ ushort ch_w_head; /* Head location of the write queue */
+ ushort ch_w_tail; /* Tail location of the write queue */
+
+ ulong ch_rxcount; /* total of data received so far */
+ ulong ch_txcount; /* total of data transmitted so far */
+
+ unsigned char ch_r_tlevel; /* Receive Trigger level */
+ unsigned char ch_t_tlevel; /* Transmit Trigger level */
+
+ unsigned char ch_r_watermark; /* Receive Watermark */
+
+ ulong ch_stop_sending_break; /* Time we should STOP sending a break */
+
+ uint ch_stops_sent; /* How many times I have sent a stop character
+ * to try to stop the other guy sending.
+ */
+ ulong ch_err_parity; /* Count of parity errors on channel */
+ ulong ch_err_frame; /* Count of framing errors on channel */
+ ulong ch_err_break; /* Count of breaks on channel */
+ ulong ch_err_overrun; /* Count of overruns on channel */
+
+ ulong ch_xon_sends; /* Count of xons transmitted */
+ ulong ch_xoff_sends; /* Count of xoffs transmitted */
+
+ ulong ch_intr_modem; /* Count of interrupts */
+ ulong ch_intr_tx; /* Count of interrupts */
+ ulong ch_intr_rx; /* Count of interrupts */
+
+
+ /* /proc/<board>/<channel> entries */
+ struct proc_dir_entry *proc_entry_pointer;
+ struct dgnc_proc_entry *dgnc_channel_table;
+
+};
+
+/*
+ * Our Global Variables.
+ */
+extern uint dgnc_Major; /* Our driver/mgmt major */
+extern int dgnc_poll_tick; /* Poll interval - 20 ms */
+extern spinlock_t dgnc_global_lock; /* Driver global spinlock */
+extern uint dgnc_NumBoards; /* Total number of boards */
+extern struct dgnc_board *dgnc_Board[MAXBOARDS]; /* Array of board structs */
+
+#endif
diff --git a/drivers/staging/dgnc/dgnc_mgmt.c b/drivers/staging/dgnc/dgnc_mgmt.c
new file mode 100644
index 000000000..b13318a82
--- /dev/null
+++ b/drivers/staging/dgnc/dgnc_mgmt.c
@@ -0,0 +1,262 @@
+/*
+ * Copyright 2003 Digi International (www.digi.com)
+ * Scott H Kilau <Scott_Kilau at digi dot 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, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED; without even the
+ * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ * PURPOSE. See the GNU General Public License for more details.
+ */
+
+/************************************************************************
+ *
+ * This file implements the mgmt functionality for the
+ * Neo and ClassicBoard based product lines.
+ *
+ ************************************************************************
+ */
+#include <linux/kernel.h>
+#include <linux/ctype.h>
+#include <linux/sched.h> /* For jiffies, task states */
+#include <linux/interrupt.h> /* For tasklet and interrupt structs/defines */
+#include <linux/serial_reg.h>
+#include <linux/termios.h>
+#include <linux/uaccess.h> /* For copy_from_user/copy_to_user */
+
+#include "dgnc_driver.h"
+#include "dgnc_pci.h"
+#include "dgnc_mgmt.h"
+
+
+/* Our "in use" variables, to enforce 1 open only */
+static int dgnc_mgmt_in_use[MAXMGMTDEVICES];
+
+
+/*
+ * dgnc_mgmt_open()
+ *
+ * Open the mgmt/downld/dpa device
+ */
+int dgnc_mgmt_open(struct inode *inode, struct file *file)
+{
+ unsigned long flags;
+ unsigned int minor = iminor(inode);
+
+ spin_lock_irqsave(&dgnc_global_lock, flags);
+
+ /* mgmt device */
+ if (minor < MAXMGMTDEVICES) {
+ /* Only allow 1 open at a time on mgmt device */
+ if (dgnc_mgmt_in_use[minor]) {
+ spin_unlock_irqrestore(&dgnc_global_lock, flags);
+ return -EBUSY;
+ }
+ dgnc_mgmt_in_use[minor]++;
+ } else {
+ spin_unlock_irqrestore(&dgnc_global_lock, flags);
+ return -ENXIO;
+ }
+
+ spin_unlock_irqrestore(&dgnc_global_lock, flags);
+
+ return 0;
+}
+
+
+/*
+ * dgnc_mgmt_close()
+ *
+ * Open the mgmt/dpa device
+ */
+int dgnc_mgmt_close(struct inode *inode, struct file *file)
+{
+ unsigned long flags;
+ unsigned int minor = iminor(inode);
+
+ spin_lock_irqsave(&dgnc_global_lock, flags);
+
+ /* mgmt device */
+ if (minor < MAXMGMTDEVICES) {
+ if (dgnc_mgmt_in_use[minor])
+ dgnc_mgmt_in_use[minor] = 0;
+ }
+ spin_unlock_irqrestore(&dgnc_global_lock, flags);
+
+ return 0;
+}
+
+
+/*
+ * dgnc_mgmt_ioctl()
+ *
+ * ioctl the mgmt/dpa device
+ */
+
+long dgnc_mgmt_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+ unsigned long flags;
+ void __user *uarg = (void __user *) arg;
+
+ switch (cmd) {
+
+ case DIGI_GETDD:
+ {
+ /*
+ * This returns the total number of boards
+ * in the system, as well as driver version
+ * and has space for a reserved entry
+ */
+ struct digi_dinfo ddi;
+
+ spin_lock_irqsave(&dgnc_global_lock, flags);
+
+ ddi.dinfo_nboards = dgnc_NumBoards;
+ sprintf(ddi.dinfo_version, "%s", DG_PART);
+
+ spin_unlock_irqrestore(&dgnc_global_lock, flags);
+
+ if (copy_to_user(uarg, &ddi, sizeof(ddi)))
+ return -EFAULT;
+
+ break;
+ }
+
+ case DIGI_GETBD:
+ {
+ int brd;
+
+ struct digi_info di;
+
+ if (copy_from_user(&brd, uarg, sizeof(int)))
+ return -EFAULT;
+
+ if (brd < 0 || brd >= dgnc_NumBoards)
+ return -ENODEV;
+
+ memset(&di, 0, sizeof(di));
+
+ di.info_bdnum = brd;
+
+ spin_lock_irqsave(&dgnc_Board[brd]->bd_lock, flags);
+
+ di.info_bdtype = dgnc_Board[brd]->dpatype;
+ di.info_bdstate = dgnc_Board[brd]->dpastatus;
+ di.info_ioport = 0;
+ di.info_physaddr = (ulong) dgnc_Board[brd]->membase;
+ di.info_physsize = (ulong) dgnc_Board[brd]->membase - dgnc_Board[brd]->membase_end;
+ if (dgnc_Board[brd]->state != BOARD_FAILED)
+ di.info_nports = dgnc_Board[brd]->nasync;
+ else
+ di.info_nports = 0;
+
+ spin_unlock_irqrestore(&dgnc_Board[brd]->bd_lock, flags);
+
+ if (copy_to_user(uarg, &di, sizeof(di)))
+ return -EFAULT;
+
+ break;
+ }
+
+ case DIGI_GET_NI_INFO:
+ {
+ struct channel_t *ch;
+ struct ni_info ni;
+ unsigned char mstat = 0;
+ uint board = 0;
+ uint channel = 0;
+
+ if (copy_from_user(&ni, uarg, sizeof(ni)))
+ return -EFAULT;
+
+ board = ni.board;
+ channel = ni.channel;
+
+ /* Verify boundaries on board */
+ if (board >= dgnc_NumBoards)
+ return -ENODEV;
+
+ /* Verify boundaries on channel */
+ if (channel >= dgnc_Board[board]->nasync)
+ return -ENODEV;
+
+ ch = dgnc_Board[board]->channels[channel];
+
+ if (!ch || ch->magic != DGNC_CHANNEL_MAGIC)
+ return -ENODEV;
+
+ memset(&ni, 0, sizeof(ni));
+ ni.board = board;
+ ni.channel = channel;
+
+ spin_lock_irqsave(&ch->ch_lock, flags);
+
+ mstat = (ch->ch_mostat | ch->ch_mistat);
+
+ if (mstat & UART_MCR_DTR) {
+ ni.mstat |= TIOCM_DTR;
+ ni.dtr = TIOCM_DTR;
+ }
+ if (mstat & UART_MCR_RTS) {
+ ni.mstat |= TIOCM_RTS;
+ ni.rts = TIOCM_RTS;
+ }
+ if (mstat & UART_MSR_CTS) {
+ ni.mstat |= TIOCM_CTS;
+ ni.cts = TIOCM_CTS;
+ }
+ if (mstat & UART_MSR_RI) {
+ ni.mstat |= TIOCM_RI;
+ ni.ri = TIOCM_RI;
+ }
+ if (mstat & UART_MSR_DCD) {
+ ni.mstat |= TIOCM_CD;
+ ni.dcd = TIOCM_CD;
+ }
+ if (mstat & UART_MSR_DSR)
+ ni.mstat |= TIOCM_DSR;
+
+ ni.iflag = ch->ch_c_iflag;
+ ni.oflag = ch->ch_c_oflag;
+ ni.cflag = ch->ch_c_cflag;
+ ni.lflag = ch->ch_c_lflag;
+
+ if (ch->ch_digi.digi_flags & CTSPACE ||
+ ch->ch_c_cflag & CRTSCTS)
+ ni.hflow = 1;
+ else
+ ni.hflow = 0;
+
+ if ((ch->ch_flags & CH_STOPI) ||
+ (ch->ch_flags & CH_FORCED_STOPI))
+ ni.recv_stopped = 1;
+ else
+ ni.recv_stopped = 0;
+
+ if ((ch->ch_flags & CH_STOP) || (ch->ch_flags & CH_FORCED_STOP))
+ ni.xmit_stopped = 1;
+ else
+ ni.xmit_stopped = 0;
+
+ ni.curtx = ch->ch_txcount;
+ ni.currx = ch->ch_rxcount;
+
+ ni.baud = ch->ch_old_baud;
+
+ spin_unlock_irqrestore(&ch->ch_lock, flags);
+
+ if (copy_to_user(uarg, &ni, sizeof(ni)))
+ return -EFAULT;
+
+ break;
+ }
+
+
+ }
+
+ return 0;
+}
diff --git a/drivers/staging/dgnc/dgnc_mgmt.h b/drivers/staging/dgnc/dgnc_mgmt.h
new file mode 100644
index 000000000..708abe959
--- /dev/null
+++ b/drivers/staging/dgnc/dgnc_mgmt.h
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2003 Digi International (www.digi.com)
+ * Scott H Kilau <Scott_Kilau at digi dot 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, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED; without even the
+ * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ * PURPOSE. See the GNU General Public License for more details.
+ */
+
+#ifndef __DGNC_MGMT_H
+#define __DGNC_MGMT_H
+
+#define MAXMGMTDEVICES 8
+
+int dgnc_mgmt_open(struct inode *inode, struct file *file);
+int dgnc_mgmt_close(struct inode *inode, struct file *file);
+long dgnc_mgmt_ioctl(struct file *file, unsigned int cmd, unsigned long arg);
+#endif
+
diff --git a/drivers/staging/dgnc/dgnc_neo.c b/drivers/staging/dgnc/dgnc_neo.c
new file mode 100644
index 000000000..f5a4d3651
--- /dev/null
+++ b/drivers/staging/dgnc/dgnc_neo.c
@@ -0,0 +1,1846 @@
+/*
+ * Copyright 2003 Digi International (www.digi.com)
+ * Scott H Kilau <Scott_Kilau at digi dot 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, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED; without even the
+ * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ * PURPOSE. See the GNU General Public License for more details.
+ */
+
+
+#include <linux/kernel.h>
+#include <linux/sched.h> /* For jiffies, task states */
+#include <linux/interrupt.h> /* For tasklet and interrupt structs/defines */
+#include <linux/delay.h> /* For udelay */
+#include <linux/io.h> /* For read[bwl]/write[bwl] */
+#include <linux/serial.h> /* For struct async_serial */
+#include <linux/serial_reg.h> /* For the various UART offsets */
+
+#include "dgnc_driver.h" /* Driver main header file */
+#include "dgnc_neo.h" /* Our header file */
+#include "dgnc_tty.h"
+
+static inline void neo_parse_lsr(struct dgnc_board *brd, uint port);
+static inline void neo_parse_isr(struct dgnc_board *brd, uint port);
+static void neo_copy_data_from_uart_to_queue(struct channel_t *ch);
+static inline void neo_clear_break(struct channel_t *ch, int force);
+static inline void neo_set_cts_flow_control(struct channel_t *ch);
+static inline void neo_set_rts_flow_control(struct channel_t *ch);
+static inline void neo_set_ixon_flow_control(struct channel_t *ch);
+static inline void neo_set_ixoff_flow_control(struct channel_t *ch);
+static inline void neo_set_no_output_flow_control(struct channel_t *ch);
+static inline void neo_set_no_input_flow_control(struct channel_t *ch);
+static inline void neo_set_new_start_stop_chars(struct channel_t *ch);
+static void neo_parse_modem(struct channel_t *ch, unsigned char signals);
+static void neo_tasklet(unsigned long data);
+static void neo_vpd(struct dgnc_board *brd);
+static void neo_uart_init(struct channel_t *ch);
+static void neo_uart_off(struct channel_t *ch);
+static int neo_drain(struct tty_struct *tty, uint seconds);
+static void neo_param(struct tty_struct *tty);
+static void neo_assert_modem_signals(struct channel_t *ch);
+static void neo_flush_uart_write(struct channel_t *ch);
+static void neo_flush_uart_read(struct channel_t *ch);
+static void neo_disable_receiver(struct channel_t *ch);
+static void neo_enable_receiver(struct channel_t *ch);
+static void neo_send_break(struct channel_t *ch, int msecs);
+static void neo_send_start_character(struct channel_t *ch);
+static void neo_send_stop_character(struct channel_t *ch);
+static void neo_copy_data_from_queue_to_uart(struct channel_t *ch);
+static uint neo_get_uart_bytes_left(struct channel_t *ch);
+static void neo_send_immediate_char(struct channel_t *ch, unsigned char c);
+static irqreturn_t neo_intr(int irq, void *voidbrd);
+
+
+struct board_ops dgnc_neo_ops = {
+ .tasklet = neo_tasklet,
+ .intr = neo_intr,
+ .uart_init = neo_uart_init,
+ .uart_off = neo_uart_off,
+ .drain = neo_drain,
+ .param = neo_param,
+ .vpd = neo_vpd,
+ .assert_modem_signals = neo_assert_modem_signals,
+ .flush_uart_write = neo_flush_uart_write,
+ .flush_uart_read = neo_flush_uart_read,
+ .disable_receiver = neo_disable_receiver,
+ .enable_receiver = neo_enable_receiver,
+ .send_break = neo_send_break,
+ .send_start_character = neo_send_start_character,
+ .send_stop_character = neo_send_stop_character,
+ .copy_data_from_queue_to_uart = neo_copy_data_from_queue_to_uart,
+ .get_uart_bytes_left = neo_get_uart_bytes_left,
+ .send_immediate_char = neo_send_immediate_char
+};
+
+static uint dgnc_offset_table[8] = { 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80 };
+
+
+/*
+ * This function allows calls to ensure that all outstanding
+ * PCI writes have been completed, by doing a PCI read against
+ * a non-destructive, read-only location on the Neo card.
+ *
+ * In this case, we are reading the DVID (Read-only Device Identification)
+ * value of the Neo card.
+ */
+static inline void neo_pci_posting_flush(struct dgnc_board *bd)
+{
+ readb(bd->re_map_membase + 0x8D);
+}
+
+static inline void neo_set_cts_flow_control(struct channel_t *ch)
+{
+ unsigned char ier = readb(&ch->ch_neo_uart->ier);
+ unsigned char efr = readb(&ch->ch_neo_uart->efr);
+
+
+ /* Turn on auto CTS flow control */
+#if 1
+ ier |= UART_17158_IER_CTSDSR;
+#else
+ ier &= ~(UART_17158_IER_CTSDSR);
+#endif
+
+ efr |= (UART_17158_EFR_ECB | UART_17158_EFR_CTSDSR);
+
+ /* Turn off auto Xon flow control */
+ efr &= ~UART_17158_EFR_IXON;
+
+ /* Why? Becuz Exar's spec says we have to zero it out before setting it */
+ writeb(0, &ch->ch_neo_uart->efr);
+
+ /* Turn on UART enhanced bits */
+ writeb(efr, &ch->ch_neo_uart->efr);
+
+ /* Turn on table D, with 8 char hi/low watermarks */
+ writeb((UART_17158_FCTR_TRGD | UART_17158_FCTR_RTS_4DELAY), &ch->ch_neo_uart->fctr);
+
+ /* Feed the UART our trigger levels */
+ writeb(8, &ch->ch_neo_uart->tfifo);
+ ch->ch_t_tlevel = 8;
+
+ writeb(ier, &ch->ch_neo_uart->ier);
+
+ neo_pci_posting_flush(ch->ch_bd);
+}
+
+
+static inline void neo_set_rts_flow_control(struct channel_t *ch)
+{
+ unsigned char ier = readb(&ch->ch_neo_uart->ier);
+ unsigned char efr = readb(&ch->ch_neo_uart->efr);
+
+ /* Turn on auto RTS flow control */
+#if 1
+ ier |= UART_17158_IER_RTSDTR;
+#else
+ ier &= ~(UART_17158_IER_RTSDTR);
+#endif
+ efr |= (UART_17158_EFR_ECB | UART_17158_EFR_RTSDTR);
+
+ /* Turn off auto Xoff flow control */
+ ier &= ~UART_17158_IER_XOFF;
+ efr &= ~UART_17158_EFR_IXOFF;
+
+ /* Why? Becuz Exar's spec says we have to zero it out before setting it */
+ writeb(0, &ch->ch_neo_uart->efr);
+
+ /* Turn on UART enhanced bits */
+ writeb(efr, &ch->ch_neo_uart->efr);
+
+ writeb((UART_17158_FCTR_TRGD | UART_17158_FCTR_RTS_4DELAY), &ch->ch_neo_uart->fctr);
+ ch->ch_r_watermark = 4;
+
+ writeb(32, &ch->ch_neo_uart->rfifo);
+ ch->ch_r_tlevel = 32;
+
+ writeb(ier, &ch->ch_neo_uart->ier);
+
+ /*
+ * From the Neo UART spec sheet:
+ * The auto RTS/DTR function must be started by asserting
+ * RTS/DTR# output pin (MCR bit-0 or 1 to logic 1 after
+ * it is enabled.
+ */
+ ch->ch_mostat |= UART_MCR_RTS;
+
+ neo_pci_posting_flush(ch->ch_bd);
+}
+
+
+static inline void neo_set_ixon_flow_control(struct channel_t *ch)
+{
+ unsigned char ier = readb(&ch->ch_neo_uart->ier);
+ unsigned char efr = readb(&ch->ch_neo_uart->efr);
+
+ /* Turn off auto CTS flow control */
+ ier &= ~UART_17158_IER_CTSDSR;
+ efr &= ~UART_17158_EFR_CTSDSR;
+
+ /* Turn on auto Xon flow control */
+ efr |= (UART_17158_EFR_ECB | UART_17158_EFR_IXON);
+
+ /* Why? Becuz Exar's spec says we have to zero it out before setting it */
+ writeb(0, &ch->ch_neo_uart->efr);
+
+ /* Turn on UART enhanced bits */
+ writeb(efr, &ch->ch_neo_uart->efr);
+
+ writeb((UART_17158_FCTR_TRGD | UART_17158_FCTR_RTS_8DELAY), &ch->ch_neo_uart->fctr);
+ ch->ch_r_watermark = 4;
+
+ writeb(32, &ch->ch_neo_uart->rfifo);
+ ch->ch_r_tlevel = 32;
+
+ /* Tell UART what start/stop chars it should be looking for */
+ writeb(ch->ch_startc, &ch->ch_neo_uart->xonchar1);
+ writeb(0, &ch->ch_neo_uart->xonchar2);
+
+ writeb(ch->ch_stopc, &ch->ch_neo_uart->xoffchar1);
+ writeb(0, &ch->ch_neo_uart->xoffchar2);
+
+ writeb(ier, &ch->ch_neo_uart->ier);
+
+ neo_pci_posting_flush(ch->ch_bd);
+}
+
+
+static inline void neo_set_ixoff_flow_control(struct channel_t *ch)
+{
+ unsigned char ier = readb(&ch->ch_neo_uart->ier);
+ unsigned char efr = readb(&ch->ch_neo_uart->efr);
+
+ /* Turn off auto RTS flow control */
+ ier &= ~UART_17158_IER_RTSDTR;
+ efr &= ~UART_17158_EFR_RTSDTR;
+
+ /* Turn on auto Xoff flow control */
+ ier |= UART_17158_IER_XOFF;
+ efr |= (UART_17158_EFR_ECB | UART_17158_EFR_IXOFF);
+
+ /* Why? Becuz Exar's spec says we have to zero it out before setting it */
+ writeb(0, &ch->ch_neo_uart->efr);
+
+ /* Turn on UART enhanced bits */
+ writeb(efr, &ch->ch_neo_uart->efr);
+
+ /* Turn on table D, with 8 char hi/low watermarks */
+ writeb((UART_17158_FCTR_TRGD | UART_17158_FCTR_RTS_8DELAY), &ch->ch_neo_uart->fctr);
+
+ writeb(8, &ch->ch_neo_uart->tfifo);
+ ch->ch_t_tlevel = 8;
+
+ /* Tell UART what start/stop chars it should be looking for */
+ writeb(ch->ch_startc, &ch->ch_neo_uart->xonchar1);
+ writeb(0, &ch->ch_neo_uart->xonchar2);
+
+ writeb(ch->ch_stopc, &ch->ch_neo_uart->xoffchar1);
+ writeb(0, &ch->ch_neo_uart->xoffchar2);
+
+ writeb(ier, &ch->ch_neo_uart->ier);
+
+ neo_pci_posting_flush(ch->ch_bd);
+}
+
+
+static inline void neo_set_no_input_flow_control(struct channel_t *ch)
+{
+ unsigned char ier = readb(&ch->ch_neo_uart->ier);
+ unsigned char efr = readb(&ch->ch_neo_uart->efr);
+
+ /* Turn off auto RTS flow control */
+ ier &= ~UART_17158_IER_RTSDTR;
+ efr &= ~UART_17158_EFR_RTSDTR;
+
+ /* Turn off auto Xoff flow control */
+ ier &= ~UART_17158_IER_XOFF;
+ if (ch->ch_c_iflag & IXON)
+ efr &= ~(UART_17158_EFR_IXOFF);
+ else
+ efr &= ~(UART_17158_EFR_ECB | UART_17158_EFR_IXOFF);
+
+
+ /* Why? Becuz Exar's spec says we have to zero it out before setting it */
+ writeb(0, &ch->ch_neo_uart->efr);
+
+ /* Turn on UART enhanced bits */
+ writeb(efr, &ch->ch_neo_uart->efr);
+
+ /* Turn on table D, with 8 char hi/low watermarks */
+ writeb((UART_17158_FCTR_TRGD | UART_17158_FCTR_RTS_8DELAY), &ch->ch_neo_uart->fctr);
+
+ ch->ch_r_watermark = 0;
+
+ writeb(16, &ch->ch_neo_uart->tfifo);
+ ch->ch_t_tlevel = 16;
+
+ writeb(16, &ch->ch_neo_uart->rfifo);
+ ch->ch_r_tlevel = 16;
+
+ writeb(ier, &ch->ch_neo_uart->ier);
+
+ neo_pci_posting_flush(ch->ch_bd);
+}
+
+
+static inline void neo_set_no_output_flow_control(struct channel_t *ch)
+{
+ unsigned char ier = readb(&ch->ch_neo_uart->ier);
+ unsigned char efr = readb(&ch->ch_neo_uart->efr);
+
+ /* Turn off auto CTS flow control */
+ ier &= ~UART_17158_IER_CTSDSR;
+ efr &= ~UART_17158_EFR_CTSDSR;
+
+ /* Turn off auto Xon flow control */
+ if (ch->ch_c_iflag & IXOFF)
+ efr &= ~UART_17158_EFR_IXON;
+ else
+ efr &= ~(UART_17158_EFR_ECB | UART_17158_EFR_IXON);
+
+ /* Why? Becuz Exar's spec says we have to zero it out before setting it */
+ writeb(0, &ch->ch_neo_uart->efr);
+
+ /* Turn on UART enhanced bits */
+ writeb(efr, &ch->ch_neo_uart->efr);
+
+ /* Turn on table D, with 8 char hi/low watermarks */
+ writeb((UART_17158_FCTR_TRGD | UART_17158_FCTR_RTS_8DELAY), &ch->ch_neo_uart->fctr);
+
+ ch->ch_r_watermark = 0;
+
+ writeb(16, &ch->ch_neo_uart->tfifo);
+ ch->ch_t_tlevel = 16;
+
+ writeb(16, &ch->ch_neo_uart->rfifo);
+ ch->ch_r_tlevel = 16;
+
+ writeb(ier, &ch->ch_neo_uart->ier);
+
+ neo_pci_posting_flush(ch->ch_bd);
+}
+
+
+/* change UARTs start/stop chars */
+static inline void neo_set_new_start_stop_chars(struct channel_t *ch)
+{
+
+ /* if hardware flow control is set, then skip this whole thing */
+ if (ch->ch_digi.digi_flags & (CTSPACE | RTSPACE) || ch->ch_c_cflag & CRTSCTS)
+ return;
+
+ /* Tell UART what start/stop chars it should be looking for */
+ writeb(ch->ch_startc, &ch->ch_neo_uart->xonchar1);
+ writeb(0, &ch->ch_neo_uart->xonchar2);
+
+ writeb(ch->ch_stopc, &ch->ch_neo_uart->xoffchar1);
+ writeb(0, &ch->ch_neo_uart->xoffchar2);
+
+ neo_pci_posting_flush(ch->ch_bd);
+}
+
+
+/*
+ * No locks are assumed to be held when calling this function.
+ */
+static inline void neo_clear_break(struct channel_t *ch, int force)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&ch->ch_lock, flags);
+
+ /* Bail if we aren't currently sending a break. */
+ if (!ch->ch_stop_sending_break) {
+ spin_unlock_irqrestore(&ch->ch_lock, flags);
+ return;
+ }
+
+ /* Turn break off, and unset some variables */
+ if (ch->ch_flags & CH_BREAK_SENDING) {
+ if (time_after_eq(jiffies, ch->ch_stop_sending_break)
+ || force) {
+ unsigned char temp = readb(&ch->ch_neo_uart->lcr);
+
+ writeb((temp & ~UART_LCR_SBC), &ch->ch_neo_uart->lcr);
+ neo_pci_posting_flush(ch->ch_bd);
+ ch->ch_flags &= ~(CH_BREAK_SENDING);
+ ch->ch_stop_sending_break = 0;
+ }
+ }
+ spin_unlock_irqrestore(&ch->ch_lock, flags);
+}
+
+
+/*
+ * Parse the ISR register.
+ */
+static inline void neo_parse_isr(struct dgnc_board *brd, uint port)
+{
+ struct channel_t *ch;
+ unsigned char isr;
+ unsigned char cause;
+ unsigned long flags;
+
+ if (!brd || brd->magic != DGNC_BOARD_MAGIC)
+ return;
+
+ if (port >= brd->maxports)
+ return;
+
+ ch = brd->channels[port];
+ if (!ch || ch->magic != DGNC_CHANNEL_MAGIC)
+ return;
+
+ /* Here we try to figure out what caused the interrupt to happen */
+ while (1) {
+
+ isr = readb(&ch->ch_neo_uart->isr_fcr);
+
+ /* Bail if no pending interrupt */
+ if (isr & UART_IIR_NO_INT)
+ break;
+
+ /*
+ * Yank off the upper 2 bits, which just show that the FIFO's are enabled.
+ */
+ isr &= ~(UART_17158_IIR_FIFO_ENABLED);
+
+ if (isr & (UART_17158_IIR_RDI_TIMEOUT | UART_IIR_RDI)) {
+ /* Read data from uart -> queue */
+ brd->intr_rx++;
+ ch->ch_intr_rx++;
+ neo_copy_data_from_uart_to_queue(ch);
+
+ /* Call our tty layer to enforce queue flow control if needed. */
+ spin_lock_irqsave(&ch->ch_lock, flags);
+ dgnc_check_queue_flow_control(ch);
+ spin_unlock_irqrestore(&ch->ch_lock, flags);
+ }
+
+ if (isr & UART_IIR_THRI) {
+ brd->intr_tx++;
+ ch->ch_intr_tx++;
+ /* Transfer data (if any) from Write Queue -> UART. */
+ spin_lock_irqsave(&ch->ch_lock, flags);
+ ch->ch_flags |= (CH_TX_FIFO_EMPTY | CH_TX_FIFO_LWM);
+ spin_unlock_irqrestore(&ch->ch_lock, flags);
+ neo_copy_data_from_queue_to_uart(ch);
+ }
+
+ if (isr & UART_17158_IIR_XONXOFF) {
+ cause = readb(&ch->ch_neo_uart->xoffchar1);
+
+ /*
+ * Since the UART detected either an XON or
+ * XOFF match, we need to figure out which
+ * one it was, so we can suspend or resume data flow.
+ */
+ if (cause == UART_17158_XON_DETECT) {
+ /* Is output stopped right now, if so, resume it */
+ if (brd->channels[port]->ch_flags & CH_STOP) {
+ spin_lock_irqsave(&ch->ch_lock,
+ flags);
+ ch->ch_flags &= ~(CH_STOP);
+ spin_unlock_irqrestore(&ch->ch_lock,
+ flags);
+ }
+ } else if (cause == UART_17158_XOFF_DETECT) {
+ if (!(brd->channels[port]->ch_flags & CH_STOP)) {
+ spin_lock_irqsave(&ch->ch_lock,
+ flags);
+ ch->ch_flags |= CH_STOP;
+ spin_unlock_irqrestore(&ch->ch_lock,
+ flags);
+ }
+ }
+ }
+
+ if (isr & UART_17158_IIR_HWFLOW_STATE_CHANGE) {
+ /*
+ * If we get here, this means the hardware is doing auto flow control.
+ * Check to see whether RTS/DTR or CTS/DSR caused this interrupt.
+ */
+ brd->intr_modem++;
+ ch->ch_intr_modem++;
+ cause = readb(&ch->ch_neo_uart->mcr);
+ /* Which pin is doing auto flow? RTS or DTR? */
+ if ((cause & 0x4) == 0) {
+ if (cause & UART_MCR_RTS) {
+ spin_lock_irqsave(&ch->ch_lock,
+ flags);
+ ch->ch_mostat |= UART_MCR_RTS;
+ spin_unlock_irqrestore(&ch->ch_lock,
+ flags);
+ } else {
+ spin_lock_irqsave(&ch->ch_lock,
+ flags);
+ ch->ch_mostat &= ~(UART_MCR_RTS);
+ spin_unlock_irqrestore(&ch->ch_lock,
+ flags);
+ }
+ } else {
+ if (cause & UART_MCR_DTR) {
+ spin_lock_irqsave(&ch->ch_lock,
+ flags);
+ ch->ch_mostat |= UART_MCR_DTR;
+ spin_unlock_irqrestore(&ch->ch_lock,
+ flags);
+ } else {
+ spin_lock_irqsave(&ch->ch_lock,
+ flags);
+ ch->ch_mostat &= ~(UART_MCR_DTR);
+ spin_unlock_irqrestore(&ch->ch_lock,
+ flags);
+ }
+ }
+ }
+
+ /* Parse any modem signal changes */
+ neo_parse_modem(ch, readb(&ch->ch_neo_uart->msr));
+ }
+}
+
+
+static inline void neo_parse_lsr(struct dgnc_board *brd, uint port)
+{
+ struct channel_t *ch;
+ int linestatus;
+ unsigned long flags;
+
+ /*
+ * Check to make sure it didn't receive interrupt with a null board
+ * associated or a board pointer that wasn't ours.
+ */
+ if (!brd || brd->magic != DGNC_BOARD_MAGIC)
+ return;
+
+ if (port >= brd->maxports)
+ return;
+
+ ch = brd->channels[port];
+ if (!ch || ch->magic != DGNC_CHANNEL_MAGIC)
+ return;
+
+ linestatus = readb(&ch->ch_neo_uart->lsr);
+
+ ch->ch_cached_lsr |= linestatus;
+
+ if (ch->ch_cached_lsr & UART_LSR_DR) {
+ brd->intr_rx++;
+ ch->ch_intr_rx++;
+ /* Read data from uart -> queue */
+ neo_copy_data_from_uart_to_queue(ch);
+ spin_lock_irqsave(&ch->ch_lock, flags);
+ dgnc_check_queue_flow_control(ch);
+ spin_unlock_irqrestore(&ch->ch_lock, flags);
+ }
+
+ /*
+ * The next 3 tests should *NOT* happen, as the above test
+ * should encapsulate all 3... At least, thats what Exar says.
+ */
+
+ if (linestatus & UART_LSR_PE)
+ ch->ch_err_parity++;
+
+ if (linestatus & UART_LSR_FE)
+ ch->ch_err_frame++;
+
+ if (linestatus & UART_LSR_BI)
+ ch->ch_err_break++;
+
+ if (linestatus & UART_LSR_OE) {
+ /*
+ * Rx Oruns. Exar says that an orun will NOT corrupt
+ * the FIFO. It will just replace the holding register
+ * with this new data byte. So basically just ignore this.
+ * Probably we should eventually have an orun stat in our driver...
+ */
+ ch->ch_err_overrun++;
+ }
+
+ if (linestatus & UART_LSR_THRE) {
+ brd->intr_tx++;
+ ch->ch_intr_tx++;
+ spin_lock_irqsave(&ch->ch_lock, flags);
+ ch->ch_flags |= (CH_TX_FIFO_EMPTY | CH_TX_FIFO_LWM);
+ spin_unlock_irqrestore(&ch->ch_lock, flags);
+
+ /* Transfer data (if any) from Write Queue -> UART. */
+ neo_copy_data_from_queue_to_uart(ch);
+ } else if (linestatus & UART_17158_TX_AND_FIFO_CLR) {
+ brd->intr_tx++;
+ ch->ch_intr_tx++;
+ spin_lock_irqsave(&ch->ch_lock, flags);
+ ch->ch_flags |= (CH_TX_FIFO_EMPTY | CH_TX_FIFO_LWM);
+ spin_unlock_irqrestore(&ch->ch_lock, flags);
+
+ /* Transfer data (if any) from Write Queue -> UART. */
+ neo_copy_data_from_queue_to_uart(ch);
+ }
+}
+
+
+/*
+ * neo_param()
+ * Send any/all changes to the line to the UART.
+ */
+static void neo_param(struct tty_struct *tty)
+{
+ unsigned char lcr = 0;
+ unsigned char uart_lcr = 0;
+ unsigned char ier = 0;
+ unsigned char uart_ier = 0;
+ uint baud = 9600;
+ int quot = 0;
+ struct dgnc_board *bd;
+ struct channel_t *ch;
+ struct un_t *un;
+
+ if (!tty || tty->magic != TTY_MAGIC)
+ return;
+
+ un = (struct un_t *) tty->driver_data;
+ if (!un || un->magic != DGNC_UNIT_MAGIC)
+ return;
+
+ ch = un->un_ch;
+ if (!ch || ch->magic != DGNC_CHANNEL_MAGIC)
+ return;
+
+ bd = ch->ch_bd;
+ if (!bd || bd->magic != DGNC_BOARD_MAGIC)
+ return;
+
+ /*
+ * If baud rate is zero, flush queues, and set mval to drop DTR.
+ */
+ if ((ch->ch_c_cflag & (CBAUD)) == 0) {
+ ch->ch_r_head = 0;
+ ch->ch_r_tail = 0;
+ ch->ch_e_head = 0;
+ ch->ch_e_tail = 0;
+ ch->ch_w_head = 0;
+ ch->ch_w_tail = 0;
+
+ neo_flush_uart_write(ch);
+ neo_flush_uart_read(ch);
+
+ /* The baudrate is B0 so all modem lines are to be dropped. */
+ ch->ch_flags |= (CH_BAUD0);
+ ch->ch_mostat &= ~(UART_MCR_RTS | UART_MCR_DTR);
+ neo_assert_modem_signals(ch);
+ ch->ch_old_baud = 0;
+ return;
+
+ } else if (ch->ch_custom_speed) {
+
+ baud = ch->ch_custom_speed;
+ /* Handle transition from B0 */
+ if (ch->ch_flags & CH_BAUD0) {
+ ch->ch_flags &= ~(CH_BAUD0);
+
+ /*
+ * Bring back up RTS and DTR...
+ * Also handle RTS or DTR toggle if set.
+ */
+ if (!(ch->ch_digi.digi_flags & DIGI_RTS_TOGGLE))
+ ch->ch_mostat |= (UART_MCR_RTS);
+ if (!(ch->ch_digi.digi_flags & DIGI_DTR_TOGGLE))
+ ch->ch_mostat |= (UART_MCR_DTR);
+ }
+ } else {
+ int iindex = 0;
+ int jindex = 0;
+
+ ulong bauds[4][16] = {
+ { /* slowbaud */
+ 0, 50, 75, 110,
+ 134, 150, 200, 300,
+ 600, 1200, 1800, 2400,
+ 4800, 9600, 19200, 38400 },
+ { /* slowbaud & CBAUDEX */
+ 0, 57600, 115200, 230400,
+ 460800, 150, 200, 921600,
+ 600, 1200, 1800, 2400,
+ 4800, 9600, 19200, 38400 },
+ { /* fastbaud */
+ 0, 57600, 76800, 115200,
+ 131657, 153600, 230400, 460800,
+ 921600, 1200, 1800, 2400,
+ 4800, 9600, 19200, 38400 },
+ { /* fastbaud & CBAUDEX */
+ 0, 57600, 115200, 230400,
+ 460800, 150, 200, 921600,
+ 600, 1200, 1800, 2400,
+ 4800, 9600, 19200, 38400 }
+ };
+
+ /* Only use the TXPrint baud rate if the terminal unit is NOT open */
+ if (!(ch->ch_tun.un_flags & UN_ISOPEN) && (un->un_type == DGNC_PRINT))
+ baud = C_BAUD(ch->ch_pun.un_tty) & 0xff;
+ else
+ baud = C_BAUD(ch->ch_tun.un_tty) & 0xff;
+
+ if (ch->ch_c_cflag & CBAUDEX)
+ iindex = 1;
+
+ if (ch->ch_digi.digi_flags & DIGI_FAST)
+ iindex += 2;
+
+ jindex = baud;
+
+ if ((iindex >= 0) && (iindex < 4) && (jindex >= 0) && (jindex < 16))
+ baud = bauds[iindex][jindex];
+ else
+ baud = 0;
+
+ if (baud == 0)
+ baud = 9600;
+
+ /* Handle transition from B0 */
+ if (ch->ch_flags & CH_BAUD0) {
+ ch->ch_flags &= ~(CH_BAUD0);
+
+ /*
+ * Bring back up RTS and DTR...
+ * Also handle RTS or DTR toggle if set.
+ */
+ if (!(ch->ch_digi.digi_flags & DIGI_RTS_TOGGLE))
+ ch->ch_mostat |= (UART_MCR_RTS);
+ if (!(ch->ch_digi.digi_flags & DIGI_DTR_TOGGLE))
+ ch->ch_mostat |= (UART_MCR_DTR);
+ }
+ }
+
+ if (ch->ch_c_cflag & PARENB)
+ lcr |= UART_LCR_PARITY;
+
+ if (!(ch->ch_c_cflag & PARODD))
+ lcr |= UART_LCR_EPAR;
+
+ /*
+ * Not all platforms support mark/space parity,
+ * so this will hide behind an ifdef.
+ */
+#ifdef CMSPAR
+ if (ch->ch_c_cflag & CMSPAR)
+ lcr |= UART_LCR_SPAR;
+#endif
+
+ if (ch->ch_c_cflag & CSTOPB)
+ lcr |= UART_LCR_STOP;
+
+ switch (ch->ch_c_cflag & CSIZE) {
+ case CS5:
+ lcr |= UART_LCR_WLEN5;
+ break;
+ case CS6:
+ lcr |= UART_LCR_WLEN6;
+ break;
+ case CS7:
+ lcr |= UART_LCR_WLEN7;
+ break;
+ case CS8:
+ default:
+ lcr |= UART_LCR_WLEN8;
+ break;
+ }
+
+ uart_ier = readb(&ch->ch_neo_uart->ier);
+ ier = uart_ier;
+
+ uart_lcr = readb(&ch->ch_neo_uart->lcr);
+
+ if (baud == 0)
+ baud = 9600;
+
+ quot = ch->ch_bd->bd_dividend / baud;
+
+ if (quot != 0 && ch->ch_old_baud != baud) {
+ ch->ch_old_baud = baud;
+ writeb(UART_LCR_DLAB, &ch->ch_neo_uart->lcr);
+ writeb((quot & 0xff), &ch->ch_neo_uart->txrx);
+ writeb((quot >> 8), &ch->ch_neo_uart->ier);
+ writeb(lcr, &ch->ch_neo_uart->lcr);
+ }
+
+ if (uart_lcr != lcr)
+ writeb(lcr, &ch->ch_neo_uart->lcr);
+
+ if (ch->ch_c_cflag & CREAD)
+ ier |= (UART_IER_RDI | UART_IER_RLSI);
+ else
+ ier &= ~(UART_IER_RDI | UART_IER_RLSI);
+
+ /*
+ * Have the UART interrupt on modem signal changes ONLY when
+ * we are in hardware flow control mode, or CLOCAL/FORCEDCD is not set.
+ */
+ if ((ch->ch_digi.digi_flags & CTSPACE) ||
+ (ch->ch_digi.digi_flags & RTSPACE) ||
+ (ch->ch_c_cflag & CRTSCTS) ||
+ !(ch->ch_digi.digi_flags & DIGI_FORCEDCD) ||
+ !(ch->ch_c_cflag & CLOCAL))
+ ier |= UART_IER_MSI;
+ else
+ ier &= ~UART_IER_MSI;
+
+ ier |= UART_IER_THRI;
+
+ if (ier != uart_ier)
+ writeb(ier, &ch->ch_neo_uart->ier);
+
+ /* Set new start/stop chars */
+ neo_set_new_start_stop_chars(ch);
+
+ if (ch->ch_digi.digi_flags & CTSPACE || ch->ch_c_cflag & CRTSCTS) {
+ neo_set_cts_flow_control(ch);
+ } else if (ch->ch_c_iflag & IXON) {
+ /* If start/stop is set to disable, then we should disable flow control */
+ if ((ch->ch_startc == _POSIX_VDISABLE) || (ch->ch_stopc == _POSIX_VDISABLE))
+ neo_set_no_output_flow_control(ch);
+ else
+ neo_set_ixon_flow_control(ch);
+ } else {
+ neo_set_no_output_flow_control(ch);
+ }
+
+ if (ch->ch_digi.digi_flags & RTSPACE || ch->ch_c_cflag & CRTSCTS) {
+ neo_set_rts_flow_control(ch);
+ } else if (ch->ch_c_iflag & IXOFF) {
+ /* If start/stop is set to disable, then we should disable flow control */
+ if ((ch->ch_startc == _POSIX_VDISABLE) || (ch->ch_stopc == _POSIX_VDISABLE))
+ neo_set_no_input_flow_control(ch);
+ else
+ neo_set_ixoff_flow_control(ch);
+ } else {
+ neo_set_no_input_flow_control(ch);
+ }
+
+ /*
+ * Adjust the RX FIFO Trigger level if baud is less than 9600.
+ * Not exactly elegant, but this is needed because of the Exar chip's
+ * delay on firing off the RX FIFO interrupt on slower baud rates.
+ */
+ if (baud < 9600) {
+ writeb(1, &ch->ch_neo_uart->rfifo);
+ ch->ch_r_tlevel = 1;
+ }
+
+ neo_assert_modem_signals(ch);
+
+ /* Get current status of the modem signals now */
+ neo_parse_modem(ch, readb(&ch->ch_neo_uart->msr));
+}
+
+
+/*
+ * Our board poller function.
+ */
+static void neo_tasklet(unsigned long data)
+{
+ struct dgnc_board *bd = (struct dgnc_board *) data;
+ struct channel_t *ch;
+ unsigned long flags;
+ int i;
+ int state = 0;
+ int ports = 0;
+
+ if (!bd || bd->magic != DGNC_BOARD_MAGIC)
+ return;
+
+ /* Cache a couple board values */
+ spin_lock_irqsave(&bd->bd_lock, flags);
+ state = bd->state;
+ ports = bd->nasync;
+ spin_unlock_irqrestore(&bd->bd_lock, flags);
+
+ /*
+ * Do NOT allow the interrupt routine to read the intr registers
+ * Until we release this lock.
+ */
+ spin_lock_irqsave(&bd->bd_intr_lock, flags);
+
+ /*
+ * If board is ready, parse deeper to see if there is anything to do.
+ */
+ if ((state == BOARD_READY) && (ports > 0)) {
+ /* Loop on each port */
+ for (i = 0; i < ports; i++) {
+ ch = bd->channels[i];
+
+ /* Just being careful... */
+ if (!ch || ch->magic != DGNC_CHANNEL_MAGIC)
+ continue;
+
+ /*
+ * NOTE: Remember you CANNOT hold any channel
+ * locks when calling the input routine.
+ *
+ * During input processing, its possible we
+ * will call the Linux ld, which might in turn,
+ * do a callback right back into us, resulting
+ * in us trying to grab the channel lock twice!
+ */
+ dgnc_input(ch);
+
+ /*
+ * Channel lock is grabbed and then released
+ * inside both of these routines, but neither
+ * call anything else that could call back into us.
+ */
+ neo_copy_data_from_queue_to_uart(ch);
+ dgnc_wakeup_writes(ch);
+
+ /*
+ * Call carrier carrier function, in case something
+ * has changed.
+ */
+ dgnc_carrier(ch);
+
+ /*
+ * Check to see if we need to turn off a sending break.
+ * The timing check is done inside clear_break()
+ */
+ if (ch->ch_stop_sending_break)
+ neo_clear_break(ch, 0);
+ }
+ }
+
+ /* Allow interrupt routine to access the interrupt register again */
+ spin_unlock_irqrestore(&bd->bd_intr_lock, flags);
+
+}
+
+
+/*
+ * dgnc_neo_intr()
+ *
+ * Neo specific interrupt handler.
+ */
+static irqreturn_t neo_intr(int irq, void *voidbrd)
+{
+ struct dgnc_board *brd = voidbrd;
+ struct channel_t *ch;
+ int port = 0;
+ int type = 0;
+ int current_port;
+ u32 tmp;
+ u32 uart_poll;
+ unsigned long flags;
+ unsigned long flags2;
+
+ /*
+ * Check to make sure it didn't receive interrupt with a null board
+ * associated or a board pointer that wasn't ours.
+ */
+ if (!brd || brd->magic != DGNC_BOARD_MAGIC)
+ return IRQ_NONE;
+
+ brd->intr_count++;
+
+ /* Lock out the slow poller from running on this board. */
+ spin_lock_irqsave(&brd->bd_intr_lock, flags);
+
+ /*
+ * Read in "extended" IRQ information from the 32bit Neo register.
+ * Bits 0-7: What port triggered the interrupt.
+ * Bits 8-31: Each 3bits indicate what type of interrupt occurred.
+ */
+ uart_poll = readl(brd->re_map_membase + UART_17158_POLL_ADDR_OFFSET);
+
+ /*
+ * If 0, no interrupts pending.
+ * This can happen if the IRQ is shared among a couple Neo/Classic boards.
+ */
+ if (!uart_poll) {
+ spin_unlock_irqrestore(&brd->bd_intr_lock, flags);
+ return IRQ_NONE;
+ }
+
+ /* At this point, we have at least SOMETHING to service, dig further... */
+
+ current_port = 0;
+
+ /* Loop on each port */
+ while ((uart_poll & 0xff) != 0) {
+
+ tmp = uart_poll;
+
+ /* Check current port to see if it has interrupt pending */
+ if ((tmp & dgnc_offset_table[current_port]) != 0) {
+ port = current_port;
+ type = tmp >> (8 + (port * 3));
+ type &= 0x7;
+ } else {
+ current_port++;
+ continue;
+ }
+
+ /* Remove this port + type from uart_poll */
+ uart_poll &= ~(dgnc_offset_table[port]);
+
+ if (!type) {
+ /* If no type, just ignore it, and move onto next port */
+ continue;
+ }
+
+ /* Switch on type of interrupt we have */
+ switch (type) {
+
+ case UART_17158_RXRDY_TIMEOUT:
+ /*
+ * RXRDY Time-out is cleared by reading data in the
+ * RX FIFO until it falls below the trigger level.
+ */
+
+ /* Verify the port is in range. */
+ if (port >= brd->nasync)
+ continue;
+
+ ch = brd->channels[port];
+ neo_copy_data_from_uart_to_queue(ch);
+
+ /* Call our tty layer to enforce queue flow control if needed. */
+ spin_lock_irqsave(&ch->ch_lock, flags2);
+ dgnc_check_queue_flow_control(ch);
+ spin_unlock_irqrestore(&ch->ch_lock, flags2);
+
+ continue;
+
+ case UART_17158_RX_LINE_STATUS:
+ /*
+ * RXRDY and RX LINE Status (logic OR of LSR[4:1])
+ */
+ neo_parse_lsr(brd, port);
+ continue;
+
+ case UART_17158_TXRDY:
+ /*
+ * TXRDY interrupt clears after reading ISR register for the UART channel.
+ */
+
+ /*
+ * Yes, this is odd...
+ * Why would I check EVERY possibility of type of
+ * interrupt, when we know its TXRDY???
+ * Becuz for some reason, even tho we got triggered for TXRDY,
+ * it seems to be occasionally wrong. Instead of TX, which
+ * it should be, I was getting things like RXDY too. Weird.
+ */
+ neo_parse_isr(brd, port);
+ continue;
+
+ case UART_17158_MSR:
+ /*
+ * MSR or flow control was seen.
+ */
+ neo_parse_isr(brd, port);
+ continue;
+
+ default:
+ /*
+ * The UART triggered us with a bogus interrupt type.
+ * It appears the Exar chip, when REALLY bogged down, will throw
+ * these once and awhile.
+ * Its harmless, just ignore it and move on.
+ */
+ continue;
+ }
+ }
+
+ /*
+ * Schedule tasklet to more in-depth servicing at a better time.
+ */
+ tasklet_schedule(&brd->helper_tasklet);
+
+ spin_unlock_irqrestore(&brd->bd_intr_lock, flags);
+
+ return IRQ_HANDLED;
+}
+
+
+/*
+ * Neo specific way of turning off the receiver.
+ * Used as a way to enforce queue flow control when in
+ * hardware flow control mode.
+ */
+static void neo_disable_receiver(struct channel_t *ch)
+{
+ unsigned char tmp = readb(&ch->ch_neo_uart->ier);
+
+ tmp &= ~(UART_IER_RDI);
+ writeb(tmp, &ch->ch_neo_uart->ier);
+ neo_pci_posting_flush(ch->ch_bd);
+}
+
+
+/*
+ * Neo specific way of turning on the receiver.
+ * Used as a way to un-enforce queue flow control when in
+ * hardware flow control mode.
+ */
+static void neo_enable_receiver(struct channel_t *ch)
+{
+ unsigned char tmp = readb(&ch->ch_neo_uart->ier);
+
+ tmp |= (UART_IER_RDI);
+ writeb(tmp, &ch->ch_neo_uart->ier);
+ neo_pci_posting_flush(ch->ch_bd);
+}
+
+
+static void neo_copy_data_from_uart_to_queue(struct channel_t *ch)
+{
+ int qleft = 0;
+ unsigned char linestatus = 0;
+ unsigned char error_mask = 0;
+ int n = 0;
+ int total = 0;
+ ushort head;
+ ushort tail;
+ unsigned long flags;
+
+ if (!ch || ch->magic != DGNC_CHANNEL_MAGIC)
+ return;
+
+ spin_lock_irqsave(&ch->ch_lock, flags);
+
+ /* cache head and tail of queue */
+ head = ch->ch_r_head & RQUEUEMASK;
+ tail = ch->ch_r_tail & RQUEUEMASK;
+
+ /* Get our cached LSR */
+ linestatus = ch->ch_cached_lsr;
+ ch->ch_cached_lsr = 0;
+
+ /* Store how much space we have left in the queue */
+ qleft = tail - head - 1;
+ if (qleft < 0)
+ qleft += RQUEUEMASK + 1;
+
+ /*
+ * If the UART is not in FIFO mode, force the FIFO copy to
+ * NOT be run, by setting total to 0.
+ *
+ * On the other hand, if the UART IS in FIFO mode, then ask
+ * the UART to give us an approximation of data it has RX'ed.
+ */
+ if (!(ch->ch_flags & CH_FIFO_ENABLED))
+ total = 0;
+ else {
+ total = readb(&ch->ch_neo_uart->rfifo);
+
+ /*
+ * EXAR chip bug - RX FIFO COUNT - Fudge factor.
+ *
+ * This resolves a problem/bug with the Exar chip that sometimes
+ * returns a bogus value in the rfifo register.
+ * The count can be any where from 0-3 bytes "off".
+ * Bizarre, but true.
+ */
+ if ((ch->ch_bd->dvid & 0xf0) >= UART_XR17E158_DVID)
+ total -= 1;
+ else
+ total -= 3;
+ }
+
+
+ /*
+ * Finally, bound the copy to make sure we don't overflow
+ * our own queue...
+ * The byte by byte copy loop below this loop this will
+ * deal with the queue overflow possibility.
+ */
+ total = min(total, qleft);
+
+ while (total > 0) {
+
+ /*
+ * Grab the linestatus register, we need to check
+ * to see if there are any errors in the FIFO.
+ */
+ linestatus = readb(&ch->ch_neo_uart->lsr);
+
+ /*
+ * Break out if there is a FIFO error somewhere.
+ * This will allow us to go byte by byte down below,
+ * finding the exact location of the error.
+ */
+ if (linestatus & UART_17158_RX_FIFO_DATA_ERROR)
+ break;
+
+ /* Make sure we don't go over the end of our queue */
+ n = min(((uint) total), (RQUEUESIZE - (uint) head));
+
+ /*
+ * Cut down n even further if needed, this is to fix
+ * a problem with memcpy_fromio() with the Neo on the
+ * IBM pSeries platform.
+ * 15 bytes max appears to be the magic number.
+ */
+ n = min_t(uint, n, 12);
+
+ /*
+ * Since we are grabbing the linestatus register, which
+ * will reset some bits after our read, we need to ensure
+ * we don't miss our TX FIFO emptys.
+ */
+ if (linestatus & (UART_LSR_THRE | UART_17158_TX_AND_FIFO_CLR))
+ ch->ch_flags |= (CH_TX_FIFO_EMPTY | CH_TX_FIFO_LWM);
+
+ linestatus = 0;
+
+ /* Copy data from uart to the queue */
+ memcpy_fromio(ch->ch_rqueue + head, &ch->ch_neo_uart->txrxburst, n);
+
+ /*
+ * Since RX_FIFO_DATA_ERROR was 0, we are guarenteed
+ * that all the data currently in the FIFO is free of
+ * breaks and parity/frame/orun errors.
+ */
+ memset(ch->ch_equeue + head, 0, n);
+
+ /* Add to and flip head if needed */
+ head = (head + n) & RQUEUEMASK;
+ total -= n;
+ qleft -= n;
+ ch->ch_rxcount += n;
+ }
+
+ /*
+ * Create a mask to determine whether we should
+ * insert the character (if any) into our queue.
+ */
+ if (ch->ch_c_iflag & IGNBRK)
+ error_mask |= UART_LSR_BI;
+
+ /*
+ * Now cleanup any leftover bytes still in the UART.
+ * Also deal with any possible queue overflow here as well.
+ */
+ while (1) {
+
+ /*
+ * Its possible we have a linestatus from the loop above
+ * this, so we "OR" on any extra bits.
+ */
+ linestatus |= readb(&ch->ch_neo_uart->lsr);
+
+ /*
+ * If the chip tells us there is no more data pending to
+ * be read, we can then leave.
+ * But before we do, cache the linestatus, just in case.
+ */
+ if (!(linestatus & UART_LSR_DR)) {
+ ch->ch_cached_lsr = linestatus;
+ break;
+ }
+
+ /* No need to store this bit */
+ linestatus &= ~UART_LSR_DR;
+
+ /*
+ * Since we are grabbing the linestatus register, which
+ * will reset some bits after our read, we need to ensure
+ * we don't miss our TX FIFO emptys.
+ */
+ if (linestatus & (UART_LSR_THRE | UART_17158_TX_AND_FIFO_CLR)) {
+ linestatus &= ~(UART_LSR_THRE | UART_17158_TX_AND_FIFO_CLR);
+ ch->ch_flags |= (CH_TX_FIFO_EMPTY | CH_TX_FIFO_LWM);
+ }
+
+ /*
+ * Discard character if we are ignoring the error mask.
+ */
+ if (linestatus & error_mask) {
+ unsigned char discard;
+
+ linestatus = 0;
+ memcpy_fromio(&discard, &ch->ch_neo_uart->txrxburst, 1);
+ continue;
+ }
+
+ /*
+ * If our queue is full, we have no choice but to drop some data.
+ * The assumption is that HWFLOW or SWFLOW should have stopped
+ * things way way before we got to this point.
+ *
+ * I decided that I wanted to ditch the oldest data first,
+ * I hope thats okay with everyone? Yes? Good.
+ */
+ while (qleft < 1) {
+ tail = (tail + 1) & RQUEUEMASK;
+ ch->ch_r_tail = tail;
+ ch->ch_err_overrun++;
+ qleft++;
+ }
+
+ memcpy_fromio(ch->ch_rqueue + head, &ch->ch_neo_uart->txrxburst, 1);
+ ch->ch_equeue[head] = (unsigned char) linestatus;
+
+ /* Ditch any remaining linestatus value. */
+ linestatus = 0;
+
+ /* Add to and flip head if needed */
+ head = (head + 1) & RQUEUEMASK;
+
+ qleft--;
+ ch->ch_rxcount++;
+ }
+
+ /*
+ * Write new final heads to channel structure.
+ */
+ ch->ch_r_head = head & RQUEUEMASK;
+ ch->ch_e_head = head & EQUEUEMASK;
+
+ spin_unlock_irqrestore(&ch->ch_lock, flags);
+}
+
+
+/*
+ * This function basically goes to sleep for secs, or until
+ * it gets signalled that the port has fully drained.
+ */
+static int neo_drain(struct tty_struct *tty, uint seconds)
+{
+ unsigned long flags;
+ struct channel_t *ch;
+ struct un_t *un;
+ int rc = 0;
+
+ if (!tty || tty->magic != TTY_MAGIC)
+ return -ENXIO;
+
+ un = (struct un_t *) tty->driver_data;
+ if (!un || un->magic != DGNC_UNIT_MAGIC)
+ return -ENXIO;
+
+ ch = un->un_ch;
+ if (!ch || ch->magic != DGNC_CHANNEL_MAGIC)
+ return -ENXIO;
+
+ spin_lock_irqsave(&ch->ch_lock, flags);
+ un->un_flags |= UN_EMPTY;
+ spin_unlock_irqrestore(&ch->ch_lock, flags);
+
+ /*
+ * Go to sleep waiting for the tty layer to wake me back up when
+ * the empty flag goes away.
+ *
+ * NOTE: TODO: Do something with time passed in.
+ */
+ rc = wait_event_interruptible(un->un_flags_wait, ((un->un_flags & UN_EMPTY) == 0));
+
+ /* If ret is non-zero, user ctrl-c'ed us */
+ return rc;
+}
+
+
+/*
+ * Flush the WRITE FIFO on the Neo.
+ *
+ * NOTE: Channel lock MUST be held before calling this function!
+ */
+static void neo_flush_uart_write(struct channel_t *ch)
+{
+ unsigned char tmp = 0;
+ int i = 0;
+
+ if (!ch || ch->magic != DGNC_CHANNEL_MAGIC)
+ return;
+
+ writeb((UART_FCR_ENABLE_FIFO | UART_FCR_CLEAR_XMIT), &ch->ch_neo_uart->isr_fcr);
+ neo_pci_posting_flush(ch->ch_bd);
+
+ for (i = 0; i < 10; i++) {
+
+ /* Check to see if the UART feels it completely flushed the FIFO. */
+ tmp = readb(&ch->ch_neo_uart->isr_fcr);
+ if (tmp & 4)
+ udelay(10);
+ else
+ break;
+ }
+
+ ch->ch_flags |= (CH_TX_FIFO_EMPTY | CH_TX_FIFO_LWM);
+}
+
+
+/*
+ * Flush the READ FIFO on the Neo.
+ *
+ * NOTE: Channel lock MUST be held before calling this function!
+ */
+static void neo_flush_uart_read(struct channel_t *ch)
+{
+ unsigned char tmp = 0;
+ int i = 0;
+
+ if (!ch || ch->magic != DGNC_CHANNEL_MAGIC)
+ return;
+
+ writeb((UART_FCR_ENABLE_FIFO | UART_FCR_CLEAR_RCVR), &ch->ch_neo_uart->isr_fcr);
+ neo_pci_posting_flush(ch->ch_bd);
+
+ for (i = 0; i < 10; i++) {
+
+ /* Check to see if the UART feels it completely flushed the FIFO. */
+ tmp = readb(&ch->ch_neo_uart->isr_fcr);
+ if (tmp & 2)
+ udelay(10);
+ else
+ break;
+ }
+}
+
+
+static void neo_copy_data_from_queue_to_uart(struct channel_t *ch)
+{
+ ushort head;
+ ushort tail;
+ int n;
+ int s;
+ int qlen;
+ uint len_written = 0;
+ unsigned long flags;
+
+ if (!ch || ch->magic != DGNC_CHANNEL_MAGIC)
+ return;
+
+ spin_lock_irqsave(&ch->ch_lock, flags);
+
+ /* No data to write to the UART */
+ if (ch->ch_w_tail == ch->ch_w_head)
+ goto exit_unlock;
+
+ /* If port is "stopped", don't send any data to the UART */
+ if ((ch->ch_flags & CH_FORCED_STOP) ||
+ (ch->ch_flags & CH_BREAK_SENDING))
+ goto exit_unlock;
+
+ /*
+ * If FIFOs are disabled. Send data directly to txrx register
+ */
+ if (!(ch->ch_flags & CH_FIFO_ENABLED)) {
+ unsigned char lsrbits = readb(&ch->ch_neo_uart->lsr);
+
+ /* Cache the LSR bits for later parsing */
+ ch->ch_cached_lsr |= lsrbits;
+ if (ch->ch_cached_lsr & UART_LSR_THRE) {
+ ch->ch_cached_lsr &= ~(UART_LSR_THRE);
+
+ /*
+ * If RTS Toggle mode is on, turn on RTS now if not already set,
+ * and make sure we get an event when the data transfer has completed.
+ */
+ if (ch->ch_digi.digi_flags & DIGI_RTS_TOGGLE) {
+ if (!(ch->ch_mostat & UART_MCR_RTS)) {
+ ch->ch_mostat |= (UART_MCR_RTS);
+ neo_assert_modem_signals(ch);
+ }
+ ch->ch_tun.un_flags |= (UN_EMPTY);
+ }
+ /*
+ * If DTR Toggle mode is on, turn on DTR now if not already set,
+ * and make sure we get an event when the data transfer has completed.
+ */
+ if (ch->ch_digi.digi_flags & DIGI_DTR_TOGGLE) {
+ if (!(ch->ch_mostat & UART_MCR_DTR)) {
+ ch->ch_mostat |= (UART_MCR_DTR);
+ neo_assert_modem_signals(ch);
+ }
+ ch->ch_tun.un_flags |= (UN_EMPTY);
+ }
+
+ writeb(ch->ch_wqueue[ch->ch_w_tail], &ch->ch_neo_uart->txrx);
+ ch->ch_w_tail++;
+ ch->ch_w_tail &= WQUEUEMASK;
+ ch->ch_txcount++;
+ }
+
+ goto exit_unlock;
+ }
+
+ /*
+ * We have to do it this way, because of the EXAR TXFIFO count bug.
+ */
+ if ((ch->ch_bd->dvid & 0xf0) < UART_XR17E158_DVID) {
+ if (!(ch->ch_flags & (CH_TX_FIFO_EMPTY | CH_TX_FIFO_LWM)))
+ goto exit_unlock;
+
+ len_written = 0;
+
+ n = readb(&ch->ch_neo_uart->tfifo);
+
+ if ((unsigned int) n > ch->ch_t_tlevel)
+ goto exit_unlock;
+
+ n = UART_17158_TX_FIFOSIZE - ch->ch_t_tlevel;
+ } else {
+ n = UART_17158_TX_FIFOSIZE - readb(&ch->ch_neo_uart->tfifo);
+ }
+
+ /* cache head and tail of queue */
+ head = ch->ch_w_head & WQUEUEMASK;
+ tail = ch->ch_w_tail & WQUEUEMASK;
+ qlen = (head - tail) & WQUEUEMASK;
+
+ /* Find minimum of the FIFO space, versus queue length */
+ n = min(n, qlen);
+
+ while (n > 0) {
+
+ s = ((head >= tail) ? head : WQUEUESIZE) - tail;
+ s = min(s, n);
+
+ if (s <= 0)
+ break;
+
+ /*
+ * If RTS Toggle mode is on, turn on RTS now if not already set,
+ * and make sure we get an event when the data transfer has completed.
+ */
+ if (ch->ch_digi.digi_flags & DIGI_RTS_TOGGLE) {
+ if (!(ch->ch_mostat & UART_MCR_RTS)) {
+ ch->ch_mostat |= (UART_MCR_RTS);
+ neo_assert_modem_signals(ch);
+ }
+ ch->ch_tun.un_flags |= (UN_EMPTY);
+ }
+
+ /*
+ * If DTR Toggle mode is on, turn on DTR now if not already set,
+ * and make sure we get an event when the data transfer has completed.
+ */
+ if (ch->ch_digi.digi_flags & DIGI_DTR_TOGGLE) {
+ if (!(ch->ch_mostat & UART_MCR_DTR)) {
+ ch->ch_mostat |= (UART_MCR_DTR);
+ neo_assert_modem_signals(ch);
+ }
+ ch->ch_tun.un_flags |= (UN_EMPTY);
+ }
+
+ memcpy_toio(&ch->ch_neo_uart->txrxburst, ch->ch_wqueue + tail, s);
+
+ /* Add and flip queue if needed */
+ tail = (tail + s) & WQUEUEMASK;
+ n -= s;
+ ch->ch_txcount += s;
+ len_written += s;
+ }
+
+ /* Update the final tail */
+ ch->ch_w_tail = tail & WQUEUEMASK;
+
+ if (len_written > 0) {
+ neo_pci_posting_flush(ch->ch_bd);
+ ch->ch_flags &= ~(CH_TX_FIFO_EMPTY | CH_TX_FIFO_LWM);
+ }
+
+exit_unlock:
+ spin_unlock_irqrestore(&ch->ch_lock, flags);
+}
+
+
+static void neo_parse_modem(struct channel_t *ch, unsigned char signals)
+{
+ unsigned char msignals = signals;
+
+ if (!ch || ch->magic != DGNC_CHANNEL_MAGIC)
+ return;
+
+ /*
+ * Do altpin switching. Altpin switches DCD and DSR.
+ * This prolly breaks DSRPACE, so we should be more clever here.
+ */
+ if (ch->ch_digi.digi_flags & DIGI_ALTPIN) {
+ unsigned char mswap = msignals;
+
+ if (mswap & UART_MSR_DDCD) {
+ msignals &= ~UART_MSR_DDCD;
+ msignals |= UART_MSR_DDSR;
+ }
+ if (mswap & UART_MSR_DDSR) {
+ msignals &= ~UART_MSR_DDSR;
+ msignals |= UART_MSR_DDCD;
+ }
+ if (mswap & UART_MSR_DCD) {
+ msignals &= ~UART_MSR_DCD;
+ msignals |= UART_MSR_DSR;
+ }
+ if (mswap & UART_MSR_DSR) {
+ msignals &= ~UART_MSR_DSR;
+ msignals |= UART_MSR_DCD;
+ }
+ }
+
+ /* Scrub off lower bits. They signify delta's, which I don't care about */
+ msignals &= 0xf0;
+
+ if (msignals & UART_MSR_DCD)
+ ch->ch_mistat |= UART_MSR_DCD;
+ else
+ ch->ch_mistat &= ~UART_MSR_DCD;
+
+ if (msignals & UART_MSR_DSR)
+ ch->ch_mistat |= UART_MSR_DSR;
+ else
+ ch->ch_mistat &= ~UART_MSR_DSR;
+
+ if (msignals & UART_MSR_RI)
+ ch->ch_mistat |= UART_MSR_RI;
+ else
+ ch->ch_mistat &= ~UART_MSR_RI;
+
+ if (msignals & UART_MSR_CTS)
+ ch->ch_mistat |= UART_MSR_CTS;
+ else
+ ch->ch_mistat &= ~UART_MSR_CTS;
+}
+
+
+/* Make the UART raise any of the output signals we want up */
+static void neo_assert_modem_signals(struct channel_t *ch)
+{
+ unsigned char out;
+
+ if (!ch || ch->magic != DGNC_CHANNEL_MAGIC)
+ return;
+
+ out = ch->ch_mostat;
+
+ if (ch->ch_flags & CH_LOOPBACK)
+ out |= UART_MCR_LOOP;
+
+ writeb(out, &ch->ch_neo_uart->mcr);
+ neo_pci_posting_flush(ch->ch_bd);
+
+ /* Give time for the UART to actually raise/drop the signals */
+ udelay(10);
+}
+
+
+static void neo_send_start_character(struct channel_t *ch)
+{
+ if (!ch || ch->magic != DGNC_CHANNEL_MAGIC)
+ return;
+
+ if (ch->ch_startc != _POSIX_VDISABLE) {
+ ch->ch_xon_sends++;
+ writeb(ch->ch_startc, &ch->ch_neo_uart->txrx);
+ neo_pci_posting_flush(ch->ch_bd);
+ udelay(10);
+ }
+}
+
+
+static void neo_send_stop_character(struct channel_t *ch)
+{
+ if (!ch || ch->magic != DGNC_CHANNEL_MAGIC)
+ return;
+
+ if (ch->ch_stopc != _POSIX_VDISABLE) {
+ ch->ch_xoff_sends++;
+ writeb(ch->ch_stopc, &ch->ch_neo_uart->txrx);
+ neo_pci_posting_flush(ch->ch_bd);
+ udelay(10);
+ }
+}
+
+
+/*
+ * neo_uart_init
+ */
+static void neo_uart_init(struct channel_t *ch)
+{
+
+ writeb(0, &ch->ch_neo_uart->ier);
+ writeb(0, &ch->ch_neo_uart->efr);
+ writeb(UART_EFR_ECB, &ch->ch_neo_uart->efr);
+
+
+ /* Clear out UART and FIFO */
+ readb(&ch->ch_neo_uart->txrx);
+ writeb((UART_FCR_ENABLE_FIFO|UART_FCR_CLEAR_RCVR|UART_FCR_CLEAR_XMIT), &ch->ch_neo_uart->isr_fcr);
+ readb(&ch->ch_neo_uart->lsr);
+ readb(&ch->ch_neo_uart->msr);
+
+ ch->ch_flags |= CH_FIFO_ENABLED;
+
+ /* Assert any signals we want up */
+ writeb(ch->ch_mostat, &ch->ch_neo_uart->mcr);
+ neo_pci_posting_flush(ch->ch_bd);
+}
+
+
+/*
+ * Make the UART completely turn off.
+ */
+static void neo_uart_off(struct channel_t *ch)
+{
+ /* Turn off UART enhanced bits */
+ writeb(0, &ch->ch_neo_uart->efr);
+
+ /* Stop all interrupts from occurring. */
+ writeb(0, &ch->ch_neo_uart->ier);
+ neo_pci_posting_flush(ch->ch_bd);
+}
+
+
+static uint neo_get_uart_bytes_left(struct channel_t *ch)
+{
+ unsigned char left = 0;
+ unsigned char lsr = readb(&ch->ch_neo_uart->lsr);
+
+ /* We must cache the LSR as some of the bits get reset once read... */
+ ch->ch_cached_lsr |= lsr;
+
+ /* Determine whether the Transmitter is empty or not */
+ if (!(lsr & UART_LSR_TEMT)) {
+ if (ch->ch_flags & CH_TX_FIFO_EMPTY)
+ tasklet_schedule(&ch->ch_bd->helper_tasklet);
+ left = 1;
+ } else {
+ ch->ch_flags |= (CH_TX_FIFO_EMPTY | CH_TX_FIFO_LWM);
+ left = 0;
+ }
+
+ return left;
+}
+
+
+/* Channel lock MUST be held by the calling function! */
+static void neo_send_break(struct channel_t *ch, int msecs)
+{
+ /*
+ * If we receive a time of 0, this means turn off the break.
+ */
+ if (msecs == 0) {
+ if (ch->ch_flags & CH_BREAK_SENDING) {
+ unsigned char temp = readb(&ch->ch_neo_uart->lcr);
+
+ writeb((temp & ~UART_LCR_SBC), &ch->ch_neo_uart->lcr);
+ neo_pci_posting_flush(ch->ch_bd);
+ ch->ch_flags &= ~(CH_BREAK_SENDING);
+ ch->ch_stop_sending_break = 0;
+ }
+ return;
+ }
+
+ /*
+ * Set the time we should stop sending the break.
+ * If we are already sending a break, toss away the existing
+ * time to stop, and use this new value instead.
+ */
+ ch->ch_stop_sending_break = jiffies + dgnc_jiffies_from_ms(msecs);
+
+ /* Tell the UART to start sending the break */
+ if (!(ch->ch_flags & CH_BREAK_SENDING)) {
+ unsigned char temp = readb(&ch->ch_neo_uart->lcr);
+
+ writeb((temp | UART_LCR_SBC), &ch->ch_neo_uart->lcr);
+ neo_pci_posting_flush(ch->ch_bd);
+ ch->ch_flags |= (CH_BREAK_SENDING);
+ }
+}
+
+
+/*
+ * neo_send_immediate_char.
+ *
+ * Sends a specific character as soon as possible to the UART,
+ * jumping over any bytes that might be in the write queue.
+ *
+ * The channel lock MUST be held by the calling function.
+ */
+static void neo_send_immediate_char(struct channel_t *ch, unsigned char c)
+{
+ if (!ch || ch->magic != DGNC_CHANNEL_MAGIC)
+ return;
+
+ writeb(c, &ch->ch_neo_uart->txrx);
+ neo_pci_posting_flush(ch->ch_bd);
+}
+
+
+static unsigned int neo_read_eeprom(unsigned char __iomem *base, unsigned int address)
+{
+ unsigned int enable;
+ unsigned int bits;
+ unsigned int databit;
+ unsigned int val;
+
+ /* enable chip select */
+ writeb(NEO_EECS, base + NEO_EEREG);
+ /* READ */
+ enable = (address | 0x180);
+
+ for (bits = 9; bits--; ) {
+ databit = (enable & (1 << bits)) ? NEO_EEDI : 0;
+ /* Set read address */
+ writeb(databit | NEO_EECS, base + NEO_EEREG);
+ writeb(databit | NEO_EECS | NEO_EECK, base + NEO_EEREG);
+ }
+
+ val = 0;
+
+ for (bits = 17; bits--; ) {
+ /* clock to EEPROM */
+ writeb(NEO_EECS, base + NEO_EEREG);
+ writeb(NEO_EECS | NEO_EECK, base + NEO_EEREG);
+ val <<= 1;
+ /* read EEPROM */
+ if (readb(base + NEO_EEREG) & NEO_EEDO)
+ val |= 1;
+ }
+
+ /* clock falling edge */
+ writeb(NEO_EECS, base + NEO_EEREG);
+
+ /* drop chip select */
+ writeb(0x00, base + NEO_EEREG);
+
+ return val;
+}
+
+
+static void neo_vpd(struct dgnc_board *brd)
+{
+ unsigned int i = 0;
+ unsigned int a;
+
+ if (!brd || brd->magic != DGNC_BOARD_MAGIC)
+ return;
+
+ if (!brd->re_map_membase)
+ return;
+
+ /* Store the VPD into our buffer */
+ for (i = 0; i < NEO_VPD_IMAGESIZE; i++) {
+ a = neo_read_eeprom(brd->re_map_membase, i);
+ brd->vpd[i*2] = a & 0xff;
+ brd->vpd[(i*2)+1] = (a >> 8) & 0xff;
+ }
+
+ if (((brd->vpd[0x08] != 0x82) /* long resource name tag */
+ && (brd->vpd[0x10] != 0x82)) /* long resource name tag (PCI-66 files)*/
+ || (brd->vpd[0x7F] != 0x78)) { /* small resource end tag */
+
+ memset(brd->vpd, '\0', NEO_VPD_IMAGESIZE);
+ } else {
+ /* Search for the serial number */
+ for (i = 0; i < NEO_VPD_IMAGEBYTES - 3; i++)
+ if (brd->vpd[i] == 'S' && brd->vpd[i + 1] == 'N')
+ strncpy(brd->serial_num, &(brd->vpd[i + 3]), 9);
+ }
+}
diff --git a/drivers/staging/dgnc/dgnc_neo.h b/drivers/staging/dgnc/dgnc_neo.h
new file mode 100644
index 000000000..c528df5a0
--- /dev/null
+++ b/drivers/staging/dgnc/dgnc_neo.h
@@ -0,0 +1,149 @@
+/*
+ * Copyright 2003 Digi International (www.digi.com)
+ * Scott H Kilau <Scott_Kilau at digi dot 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, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED; without even the
+ * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ * PURPOSE. See the GNU General Public License for more details.
+ */
+
+#ifndef __DGNC_NEO_H
+#define __DGNC_NEO_H
+
+#include "dgnc_driver.h"
+
+/************************************************************************
+ * Per channel/port NEO UART structure *
+ ************************************************************************
+ * Base Structure Entries Usage Meanings to Host *
+ * *
+ * W = read write R = read only *
+ * U = Unused. *
+ ************************************************************************/
+
+struct neo_uart_struct {
+ u8 txrx; /* WR RHR/THR - Holding Reg */
+ u8 ier; /* WR IER - Interrupt Enable Reg */
+ u8 isr_fcr; /* WR ISR/FCR - Interrupt Status Reg/Fifo Control Reg */
+ u8 lcr; /* WR LCR - Line Control Reg */
+ u8 mcr; /* WR MCR - Modem Control Reg */
+ u8 lsr; /* WR LSR - Line Status Reg */
+ u8 msr; /* WR MSR - Modem Status Reg */
+ u8 spr; /* WR SPR - Scratch Pad Reg */
+ u8 fctr; /* WR FCTR - Feature Control Reg */
+ u8 efr; /* WR EFR - Enhanced Function Reg */
+ u8 tfifo; /* WR TXCNT/TXTRG - Transmit FIFO Reg */
+ u8 rfifo; /* WR RXCNT/RXTRG - Receive FIFO Reg */
+ u8 xoffchar1; /* WR XOFF 1 - XOff Character 1 Reg */
+ u8 xoffchar2; /* WR XOFF 2 - XOff Character 2 Reg */
+ u8 xonchar1; /* WR XON 1 - Xon Character 1 Reg */
+ u8 xonchar2; /* WR XON 2 - XOn Character 2 Reg */
+
+ u8 reserved1[0x2ff - 0x200]; /* U Reserved by Exar */
+ u8 txrxburst[64]; /* RW 64 bytes of RX/TX FIFO Data */
+ u8 reserved2[0x37f - 0x340]; /* U Reserved by Exar */
+ u8 rxburst_with_errors[64]; /* R 64 bytes of RX FIFO Data + LSR */
+};
+
+/* Where to read the extended interrupt register (32bits instead of 8bits) */
+#define UART_17158_POLL_ADDR_OFFSET 0x80
+
+/* These are the current dvid's of the Neo boards */
+#define UART_XR17C158_DVID 0x20
+#define UART_XR17D158_DVID 0x20
+#define UART_XR17E158_DVID 0x40
+
+#define NEO_EECK 0x10 /* Clock */
+#define NEO_EECS 0x20 /* Chip Select */
+#define NEO_EEDI 0x40 /* Data In is an Output Pin */
+#define NEO_EEDO 0x80 /* Data Out is an Input Pin */
+#define NEO_EEREG 0x8E /* offset to EEPROM control reg */
+
+
+#define NEO_VPD_IMAGESIZE 0x40 /* size of image to read from EEPROM in words */
+#define NEO_VPD_IMAGEBYTES (NEO_VPD_IMAGESIZE * 2)
+
+/*
+ * These are the redefinitions for the FCTR on the XR17C158, since
+ * Exar made them different than their earlier design. (XR16C854)
+ */
+
+/* These are only applicable when table D is selected */
+#define UART_17158_FCTR_RTS_NODELAY 0x00
+#define UART_17158_FCTR_RTS_4DELAY 0x01
+#define UART_17158_FCTR_RTS_6DELAY 0x02
+#define UART_17158_FCTR_RTS_8DELAY 0x03
+#define UART_17158_FCTR_RTS_12DELAY 0x12
+#define UART_17158_FCTR_RTS_16DELAY 0x05
+#define UART_17158_FCTR_RTS_20DELAY 0x13
+#define UART_17158_FCTR_RTS_24DELAY 0x06
+#define UART_17158_FCTR_RTS_28DELAY 0x14
+#define UART_17158_FCTR_RTS_32DELAY 0x07
+#define UART_17158_FCTR_RTS_36DELAY 0x16
+#define UART_17158_FCTR_RTS_40DELAY 0x08
+#define UART_17158_FCTR_RTS_44DELAY 0x09
+#define UART_17158_FCTR_RTS_48DELAY 0x10
+#define UART_17158_FCTR_RTS_52DELAY 0x11
+
+#define UART_17158_FCTR_RTS_IRDA 0x10
+#define UART_17158_FCTR_RS485 0x20
+#define UART_17158_FCTR_TRGA 0x00
+#define UART_17158_FCTR_TRGB 0x40
+#define UART_17158_FCTR_TRGC 0x80
+#define UART_17158_FCTR_TRGD 0xC0
+
+/* 17158 trigger table selects.. */
+#define UART_17158_FCTR_BIT6 0x40
+#define UART_17158_FCTR_BIT7 0x80
+
+/* 17158 TX/RX memmapped buffer offsets */
+#define UART_17158_RX_FIFOSIZE 64
+#define UART_17158_TX_FIFOSIZE 64
+
+/* 17158 Extended IIR's */
+#define UART_17158_IIR_RDI_TIMEOUT 0x0C /* Receiver data TIMEOUT */
+#define UART_17158_IIR_XONXOFF 0x10 /* Received an XON/XOFF char */
+#define UART_17158_IIR_HWFLOW_STATE_CHANGE 0x20 /* CTS/DSR or RTS/DTR state change */
+#define UART_17158_IIR_FIFO_ENABLED 0xC0 /* 16550 FIFOs are Enabled */
+
+/*
+ * These are the extended interrupts that get sent
+ * back to us from the UART's 32bit interrupt register
+ */
+#define UART_17158_RX_LINE_STATUS 0x1 /* RX Ready */
+#define UART_17158_RXRDY_TIMEOUT 0x2 /* RX Ready Timeout */
+#define UART_17158_TXRDY 0x3 /* TX Ready */
+#define UART_17158_MSR 0x4 /* Modem State Change */
+#define UART_17158_TX_AND_FIFO_CLR 0x40 /* Transmitter Holding Reg Empty */
+#define UART_17158_RX_FIFO_DATA_ERROR 0x80 /* UART detected an RX FIFO Data error */
+
+/*
+ * These are the EXTENDED definitions for the 17C158's Interrupt
+ * Enable Register.
+ */
+#define UART_17158_EFR_ECB 0x10 /* Enhanced control bit */
+#define UART_17158_EFR_IXON 0x2 /* Receiver compares Xon1/Xoff1 */
+#define UART_17158_EFR_IXOFF 0x8 /* Transmit Xon1/Xoff1 */
+#define UART_17158_EFR_RTSDTR 0x40 /* Auto RTS/DTR Flow Control Enable */
+#define UART_17158_EFR_CTSDSR 0x80 /* Auto CTS/DSR Flow COntrol Enable */
+
+#define UART_17158_XOFF_DETECT 0x1 /* Indicates whether chip saw an incoming XOFF char */
+#define UART_17158_XON_DETECT 0x2 /* Indicates whether chip saw an incoming XON char */
+
+#define UART_17158_IER_RSVD1 0x10 /* Reserved by Exar */
+#define UART_17158_IER_XOFF 0x20 /* Xoff Interrupt Enable */
+#define UART_17158_IER_RTSDTR 0x40 /* Output Interrupt Enable */
+#define UART_17158_IER_CTSDSR 0x80 /* Input Interrupt Enable */
+
+/*
+ * Our Global Variables
+ */
+extern struct board_ops dgnc_neo_ops;
+
+#endif
diff --git a/drivers/staging/dgnc/dgnc_pci.h b/drivers/staging/dgnc/dgnc_pci.h
new file mode 100644
index 000000000..617d40d1e
--- /dev/null
+++ b/drivers/staging/dgnc/dgnc_pci.h
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2003 Digi International (www.digi.com)
+ * Scott H Kilau <Scott_Kilau at digi dot 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, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED; without even the
+ * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ * PURPOSE. See the GNU General Public License for more details.
+ */
+
+#ifndef __DGNC_PCI_H
+#define __DGNC_PCI_H
+
+#define PCIMAX 32 /* maximum number of PCI boards */
+
+#define DIGI_VID 0x114F
+
+#define PCI_DEVICE_CLASSIC_4_DID 0x0028
+#define PCI_DEVICE_CLASSIC_8_DID 0x0029
+#define PCI_DEVICE_CLASSIC_4_422_DID 0x00D0
+#define PCI_DEVICE_CLASSIC_8_422_DID 0x00D1
+#define PCI_DEVICE_NEO_4_DID 0x00B0
+#define PCI_DEVICE_NEO_8_DID 0x00B1
+#define PCI_DEVICE_NEO_2DB9_DID 0x00C8
+#define PCI_DEVICE_NEO_2DB9PRI_DID 0x00C9
+#define PCI_DEVICE_NEO_2RJ45_DID 0x00CA
+#define PCI_DEVICE_NEO_2RJ45PRI_DID 0x00CB
+#define PCI_DEVICE_NEO_1_422_DID 0x00CC
+#define PCI_DEVICE_NEO_1_422_485_DID 0x00CD
+#define PCI_DEVICE_NEO_2_422_485_DID 0x00CE
+#define PCI_DEVICE_NEO_EXPRESS_8_DID 0x00F0
+#define PCI_DEVICE_NEO_EXPRESS_4_DID 0x00F1
+#define PCI_DEVICE_NEO_EXPRESS_4RJ45_DID 0x00F2
+#define PCI_DEVICE_NEO_EXPRESS_8RJ45_DID 0x00F3
+#define PCI_DEVICE_NEO_EXPRESS_4_IBM_DID 0x00F4
+
+#define PCI_DEVICE_CLASSIC_4_PCI_NAME "ClassicBoard 4 PCI"
+#define PCI_DEVICE_CLASSIC_8_PCI_NAME "ClassicBoard 8 PCI"
+#define PCI_DEVICE_CLASSIC_4_422_PCI_NAME "ClassicBoard 4 422 PCI"
+#define PCI_DEVICE_CLASSIC_8_422_PCI_NAME "ClassicBoard 8 422 PCI"
+#define PCI_DEVICE_NEO_4_PCI_NAME "Neo 4 PCI"
+#define PCI_DEVICE_NEO_8_PCI_NAME "Neo 8 PCI"
+#define PCI_DEVICE_NEO_2DB9_PCI_NAME "Neo 2 - DB9 Universal PCI"
+#define PCI_DEVICE_NEO_2DB9PRI_PCI_NAME "Neo 2 - DB9 Universal PCI - Powered Ring Indicator"
+#define PCI_DEVICE_NEO_2RJ45_PCI_NAME "Neo 2 - RJ45 Universal PCI"
+#define PCI_DEVICE_NEO_2RJ45PRI_PCI_NAME "Neo 2 - RJ45 Universal PCI - Powered Ring Indicator"
+#define PCI_DEVICE_NEO_1_422_PCI_NAME "Neo 1 422 PCI"
+#define PCI_DEVICE_NEO_1_422_485_PCI_NAME "Neo 1 422/485 PCI"
+#define PCI_DEVICE_NEO_2_422_485_PCI_NAME "Neo 2 422/485 PCI"
+
+#define PCI_DEVICE_NEO_EXPRESS_8_PCI_NAME "Neo 8 PCI Express"
+#define PCI_DEVICE_NEO_EXPRESS_4_PCI_NAME "Neo 4 PCI Express"
+#define PCI_DEVICE_NEO_EXPRESS_4RJ45_PCI_NAME "Neo 4 PCI Express RJ45"
+#define PCI_DEVICE_NEO_EXPRESS_8RJ45_PCI_NAME "Neo 8 PCI Express RJ45"
+#define PCI_DEVICE_NEO_EXPRESS_4_IBM_PCI_NAME "Neo 4 PCI Express IBM"
+
+
+/* Size of Memory and I/O for PCI (4 K) */
+#define PCI_RAM_SIZE 0x1000
+
+/* Size of Memory (2MB) */
+#define PCI_MEM_SIZE 0x1000
+
+#endif
diff --git a/drivers/staging/dgnc/dgnc_sysfs.c b/drivers/staging/dgnc/dgnc_sysfs.c
new file mode 100644
index 000000000..65551d190
--- /dev/null
+++ b/drivers/staging/dgnc/dgnc_sysfs.c
@@ -0,0 +1,697 @@
+/*
+ * Copyright 2004 Digi International (www.digi.com)
+ * Scott H Kilau <Scott_Kilau at digi dot 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, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED; without even the
+ * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ * PURPOSE. See the GNU General Public License for more details.
+ */
+
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/ctype.h>
+#include <linux/string.h>
+#include <linux/serial_reg.h>
+#include <linux/device.h>
+#include <linux/pci.h>
+#include <linux/kdev_t.h>
+
+#include "dgnc_driver.h"
+#include "dgnc_mgmt.h"
+
+
+static ssize_t dgnc_driver_version_show(struct device_driver *ddp, char *buf)
+{
+ return snprintf(buf, PAGE_SIZE, "%s\n", DG_PART);
+}
+static DRIVER_ATTR(version, S_IRUSR, dgnc_driver_version_show, NULL);
+
+
+static ssize_t dgnc_driver_boards_show(struct device_driver *ddp, char *buf)
+{
+ return snprintf(buf, PAGE_SIZE, "%d\n", dgnc_NumBoards);
+}
+static DRIVER_ATTR(boards, S_IRUSR, dgnc_driver_boards_show, NULL);
+
+
+static ssize_t dgnc_driver_maxboards_show(struct device_driver *ddp, char *buf)
+{
+ return snprintf(buf, PAGE_SIZE, "%d\n", MAXBOARDS);
+}
+static DRIVER_ATTR(maxboards, S_IRUSR, dgnc_driver_maxboards_show, NULL);
+
+
+static ssize_t dgnc_driver_pollrate_show(struct device_driver *ddp, char *buf)
+{
+ return snprintf(buf, PAGE_SIZE, "%dms\n", dgnc_poll_tick);
+}
+
+static ssize_t dgnc_driver_pollrate_store(struct device_driver *ddp, const char *buf, size_t count)
+{
+ int ret;
+
+ ret = sscanf(buf, "%d\n", &dgnc_poll_tick);
+ if (ret != 1)
+ return -EINVAL;
+ return count;
+}
+static DRIVER_ATTR(pollrate, (S_IRUSR | S_IWUSR), dgnc_driver_pollrate_show, dgnc_driver_pollrate_store);
+
+
+void dgnc_create_driver_sysfiles(struct pci_driver *dgnc_driver)
+{
+ int rc = 0;
+ struct device_driver *driverfs = &dgnc_driver->driver;
+
+ rc |= driver_create_file(driverfs, &driver_attr_version);
+ rc |= driver_create_file(driverfs, &driver_attr_boards);
+ rc |= driver_create_file(driverfs, &driver_attr_maxboards);
+ rc |= driver_create_file(driverfs, &driver_attr_pollrate);
+ if (rc)
+ pr_err("DGNC: sysfs driver_create_file failed!\n");
+}
+
+
+void dgnc_remove_driver_sysfiles(struct pci_driver *dgnc_driver)
+{
+ struct device_driver *driverfs = &dgnc_driver->driver;
+
+ driver_remove_file(driverfs, &driver_attr_version);
+ driver_remove_file(driverfs, &driver_attr_boards);
+ driver_remove_file(driverfs, &driver_attr_maxboards);
+ driver_remove_file(driverfs, &driver_attr_pollrate);
+}
+
+
+#define DGNC_VERIFY_BOARD(p, bd) \
+ do { \
+ if (!p) \
+ return 0; \
+ \
+ bd = dev_get_drvdata(p); \
+ if (!bd || bd->magic != DGNC_BOARD_MAGIC) \
+ return 0; \
+ if (bd->state != BOARD_READY) \
+ return 0; \
+ } while (0)
+
+
+
+static ssize_t dgnc_vpd_show(struct device *p, struct device_attribute *attr, char *buf)
+{
+ struct dgnc_board *bd;
+ int count = 0;
+ int i = 0;
+
+ DGNC_VERIFY_BOARD(p, bd);
+
+ count += sprintf(buf + count, "\n 0 1 2 3 4 5 6 7 8 9 A B C D E F");
+ for (i = 0; i < 0x40 * 2; i++) {
+ if (!(i % 16))
+ count += sprintf(buf + count, "\n%04X ", i * 2);
+ count += sprintf(buf + count, "%02X ", bd->vpd[i]);
+ }
+ count += sprintf(buf + count, "\n");
+
+ return count;
+}
+static DEVICE_ATTR(vpd, S_IRUSR, dgnc_vpd_show, NULL);
+
+static ssize_t dgnc_serial_number_show(struct device *p, struct device_attribute *attr, char *buf)
+{
+ struct dgnc_board *bd;
+ int count = 0;
+
+ DGNC_VERIFY_BOARD(p, bd);
+
+ if (bd->serial_num[0] == '\0')
+ count += sprintf(buf + count, "<UNKNOWN>\n");
+ else
+ count += sprintf(buf + count, "%s\n", bd->serial_num);
+
+ return count;
+}
+static DEVICE_ATTR(serial_number, S_IRUSR, dgnc_serial_number_show, NULL);
+
+
+static ssize_t dgnc_ports_state_show(struct device *p, struct device_attribute *attr, char *buf)
+{
+ struct dgnc_board *bd;
+ int count = 0;
+ int i = 0;
+
+ DGNC_VERIFY_BOARD(p, bd);
+
+ for (i = 0; i < bd->nasync; i++) {
+ count += snprintf(buf + count, PAGE_SIZE - count,
+ "%d %s\n", bd->channels[i]->ch_portnum,
+ bd->channels[i]->ch_open_count ? "Open" : "Closed");
+ }
+ return count;
+}
+static DEVICE_ATTR(ports_state, S_IRUSR, dgnc_ports_state_show, NULL);
+
+
+static ssize_t dgnc_ports_baud_show(struct device *p, struct device_attribute *attr, char *buf)
+{
+ struct dgnc_board *bd;
+ int count = 0;
+ int i = 0;
+
+ DGNC_VERIFY_BOARD(p, bd);
+
+ for (i = 0; i < bd->nasync; i++) {
+ count += snprintf(buf + count, PAGE_SIZE - count,
+ "%d %d\n", bd->channels[i]->ch_portnum, bd->channels[i]->ch_old_baud);
+ }
+ return count;
+}
+static DEVICE_ATTR(ports_baud, S_IRUSR, dgnc_ports_baud_show, NULL);
+
+
+static ssize_t dgnc_ports_msignals_show(struct device *p, struct device_attribute *attr, char *buf)
+{
+ struct dgnc_board *bd;
+ int count = 0;
+ int i = 0;
+
+ DGNC_VERIFY_BOARD(p, bd);
+
+ for (i = 0; i < bd->nasync; i++) {
+ if (bd->channels[i]->ch_open_count) {
+ count += snprintf(buf + count, PAGE_SIZE - count,
+ "%d %s %s %s %s %s %s\n", bd->channels[i]->ch_portnum,
+ (bd->channels[i]->ch_mostat & UART_MCR_RTS) ? "RTS" : "",
+ (bd->channels[i]->ch_mistat & UART_MSR_CTS) ? "CTS" : "",
+ (bd->channels[i]->ch_mostat & UART_MCR_DTR) ? "DTR" : "",
+ (bd->channels[i]->ch_mistat & UART_MSR_DSR) ? "DSR" : "",
+ (bd->channels[i]->ch_mistat & UART_MSR_DCD) ? "DCD" : "",
+ (bd->channels[i]->ch_mistat & UART_MSR_RI) ? "RI" : "");
+ } else {
+ count += snprintf(buf + count, PAGE_SIZE - count,
+ "%d\n", bd->channels[i]->ch_portnum);
+ }
+ }
+ return count;
+}
+static DEVICE_ATTR(ports_msignals, S_IRUSR, dgnc_ports_msignals_show, NULL);
+
+
+static ssize_t dgnc_ports_iflag_show(struct device *p, struct device_attribute *attr, char *buf)
+{
+ struct dgnc_board *bd;
+ int count = 0;
+ int i = 0;
+
+ DGNC_VERIFY_BOARD(p, bd);
+
+ for (i = 0; i < bd->nasync; i++) {
+ count += snprintf(buf + count, PAGE_SIZE - count, "%d %x\n",
+ bd->channels[i]->ch_portnum, bd->channels[i]->ch_c_iflag);
+ }
+ return count;
+}
+static DEVICE_ATTR(ports_iflag, S_IRUSR, dgnc_ports_iflag_show, NULL);
+
+
+static ssize_t dgnc_ports_cflag_show(struct device *p, struct device_attribute *attr, char *buf)
+{
+ struct dgnc_board *bd;
+ int count = 0;
+ int i = 0;
+
+ DGNC_VERIFY_BOARD(p, bd);
+
+ for (i = 0; i < bd->nasync; i++) {
+ count += snprintf(buf + count, PAGE_SIZE - count, "%d %x\n",
+ bd->channels[i]->ch_portnum, bd->channels[i]->ch_c_cflag);
+ }
+ return count;
+}
+static DEVICE_ATTR(ports_cflag, S_IRUSR, dgnc_ports_cflag_show, NULL);
+
+
+static ssize_t dgnc_ports_oflag_show(struct device *p, struct device_attribute *attr, char *buf)
+{
+ struct dgnc_board *bd;
+ int count = 0;
+ int i = 0;
+
+ DGNC_VERIFY_BOARD(p, bd);
+
+ for (i = 0; i < bd->nasync; i++) {
+ count += snprintf(buf + count, PAGE_SIZE - count, "%d %x\n",
+ bd->channels[i]->ch_portnum, bd->channels[i]->ch_c_oflag);
+ }
+ return count;
+}
+static DEVICE_ATTR(ports_oflag, S_IRUSR, dgnc_ports_oflag_show, NULL);
+
+
+static ssize_t dgnc_ports_lflag_show(struct device *p, struct device_attribute *attr, char *buf)
+{
+ struct dgnc_board *bd;
+ int count = 0;
+ int i = 0;
+
+ DGNC_VERIFY_BOARD(p, bd);
+
+ for (i = 0; i < bd->nasync; i++) {
+ count += snprintf(buf + count, PAGE_SIZE - count, "%d %x\n",
+ bd->channels[i]->ch_portnum, bd->channels[i]->ch_c_lflag);
+ }
+ return count;
+}
+static DEVICE_ATTR(ports_lflag, S_IRUSR, dgnc_ports_lflag_show, NULL);
+
+
+static ssize_t dgnc_ports_digi_flag_show(struct device *p, struct device_attribute *attr, char *buf)
+{
+ struct dgnc_board *bd;
+ int count = 0;
+ int i = 0;
+
+ DGNC_VERIFY_BOARD(p, bd);
+
+ for (i = 0; i < bd->nasync; i++) {
+ count += snprintf(buf + count, PAGE_SIZE - count, "%d %x\n",
+ bd->channels[i]->ch_portnum, bd->channels[i]->ch_digi.digi_flags);
+ }
+ return count;
+}
+static DEVICE_ATTR(ports_digi_flag, S_IRUSR, dgnc_ports_digi_flag_show, NULL);
+
+
+static ssize_t dgnc_ports_rxcount_show(struct device *p, struct device_attribute *attr, char *buf)
+{
+ struct dgnc_board *bd;
+ int count = 0;
+ int i = 0;
+
+ DGNC_VERIFY_BOARD(p, bd);
+
+ for (i = 0; i < bd->nasync; i++) {
+ count += snprintf(buf + count, PAGE_SIZE - count, "%d %ld\n",
+ bd->channels[i]->ch_portnum, bd->channels[i]->ch_rxcount);
+ }
+ return count;
+}
+static DEVICE_ATTR(ports_rxcount, S_IRUSR, dgnc_ports_rxcount_show, NULL);
+
+
+static ssize_t dgnc_ports_txcount_show(struct device *p, struct device_attribute *attr, char *buf)
+{
+ struct dgnc_board *bd;
+ int count = 0;
+ int i = 0;
+
+ DGNC_VERIFY_BOARD(p, bd);
+
+ for (i = 0; i < bd->nasync; i++) {
+ count += snprintf(buf + count, PAGE_SIZE - count, "%d %ld\n",
+ bd->channels[i]->ch_portnum, bd->channels[i]->ch_txcount);
+ }
+ return count;
+}
+static DEVICE_ATTR(ports_txcount, S_IRUSR, dgnc_ports_txcount_show, NULL);
+
+
+/* this function creates the sys files that will export each signal status
+ * to sysfs each value will be put in a separate filename
+ */
+void dgnc_create_ports_sysfiles(struct dgnc_board *bd)
+{
+ int rc = 0;
+
+ dev_set_drvdata(&bd->pdev->dev, bd);
+ rc |= device_create_file(&(bd->pdev->dev), &dev_attr_ports_state);
+ rc |= device_create_file(&(bd->pdev->dev), &dev_attr_ports_baud);
+ rc |= device_create_file(&(bd->pdev->dev), &dev_attr_ports_msignals);
+ rc |= device_create_file(&(bd->pdev->dev), &dev_attr_ports_iflag);
+ rc |= device_create_file(&(bd->pdev->dev), &dev_attr_ports_cflag);
+ rc |= device_create_file(&(bd->pdev->dev), &dev_attr_ports_oflag);
+ rc |= device_create_file(&(bd->pdev->dev), &dev_attr_ports_lflag);
+ rc |= device_create_file(&(bd->pdev->dev), &dev_attr_ports_digi_flag);
+ rc |= device_create_file(&(bd->pdev->dev), &dev_attr_ports_rxcount);
+ rc |= device_create_file(&(bd->pdev->dev), &dev_attr_ports_txcount);
+ rc |= device_create_file(&(bd->pdev->dev), &dev_attr_vpd);
+ rc |= device_create_file(&(bd->pdev->dev), &dev_attr_serial_number);
+ if (rc)
+ dev_err(&bd->pdev->dev, "dgnc: sysfs device_create_file failed!\n");
+}
+
+
+/* removes all the sys files created for that port */
+void dgnc_remove_ports_sysfiles(struct dgnc_board *bd)
+{
+ device_remove_file(&(bd->pdev->dev), &dev_attr_ports_state);
+ device_remove_file(&(bd->pdev->dev), &dev_attr_ports_baud);
+ device_remove_file(&(bd->pdev->dev), &dev_attr_ports_msignals);
+ device_remove_file(&(bd->pdev->dev), &dev_attr_ports_iflag);
+ device_remove_file(&(bd->pdev->dev), &dev_attr_ports_cflag);
+ device_remove_file(&(bd->pdev->dev), &dev_attr_ports_oflag);
+ device_remove_file(&(bd->pdev->dev), &dev_attr_ports_lflag);
+ device_remove_file(&(bd->pdev->dev), &dev_attr_ports_digi_flag);
+ device_remove_file(&(bd->pdev->dev), &dev_attr_ports_rxcount);
+ device_remove_file(&(bd->pdev->dev), &dev_attr_ports_txcount);
+ device_remove_file(&(bd->pdev->dev), &dev_attr_vpd);
+ device_remove_file(&(bd->pdev->dev), &dev_attr_serial_number);
+}
+
+
+static ssize_t dgnc_tty_state_show(struct device *d, struct device_attribute *attr, char *buf)
+{
+ struct dgnc_board *bd;
+ struct channel_t *ch;
+ struct un_t *un;
+
+ if (!d)
+ return 0;
+ un = dev_get_drvdata(d);
+ if (!un || un->magic != DGNC_UNIT_MAGIC)
+ return 0;
+ ch = un->un_ch;
+ if (!ch || ch->magic != DGNC_CHANNEL_MAGIC)
+ return 0;
+ bd = ch->ch_bd;
+ if (!bd || bd->magic != DGNC_BOARD_MAGIC)
+ return 0;
+ if (bd->state != BOARD_READY)
+ return 0;
+
+ return snprintf(buf, PAGE_SIZE, "%s", un->un_open_count ? "Open" : "Closed");
+}
+static DEVICE_ATTR(state, S_IRUSR, dgnc_tty_state_show, NULL);
+
+
+static ssize_t dgnc_tty_baud_show(struct device *d, struct device_attribute *attr, char *buf)
+{
+ struct dgnc_board *bd;
+ struct channel_t *ch;
+ struct un_t *un;
+
+ if (!d)
+ return 0;
+ un = dev_get_drvdata(d);
+ if (!un || un->magic != DGNC_UNIT_MAGIC)
+ return 0;
+ ch = un->un_ch;
+ if (!ch || ch->magic != DGNC_CHANNEL_MAGIC)
+ return 0;
+ bd = ch->ch_bd;
+ if (!bd || bd->magic != DGNC_BOARD_MAGIC)
+ return 0;
+ if (bd->state != BOARD_READY)
+ return 0;
+
+ return snprintf(buf, PAGE_SIZE, "%d\n", ch->ch_old_baud);
+}
+static DEVICE_ATTR(baud, S_IRUSR, dgnc_tty_baud_show, NULL);
+
+
+static ssize_t dgnc_tty_msignals_show(struct device *d, struct device_attribute *attr, char *buf)
+{
+ struct dgnc_board *bd;
+ struct channel_t *ch;
+ struct un_t *un;
+
+ if (!d)
+ return 0;
+ un = dev_get_drvdata(d);
+ if (!un || un->magic != DGNC_UNIT_MAGIC)
+ return 0;
+ ch = un->un_ch;
+ if (!ch || ch->magic != DGNC_CHANNEL_MAGIC)
+ return 0;
+ bd = ch->ch_bd;
+ if (!bd || bd->magic != DGNC_BOARD_MAGIC)
+ return 0;
+ if (bd->state != BOARD_READY)
+ return 0;
+
+ if (ch->ch_open_count) {
+ return snprintf(buf, PAGE_SIZE, "%s %s %s %s %s %s\n",
+ (ch->ch_mostat & UART_MCR_RTS) ? "RTS" : "",
+ (ch->ch_mistat & UART_MSR_CTS) ? "CTS" : "",
+ (ch->ch_mostat & UART_MCR_DTR) ? "DTR" : "",
+ (ch->ch_mistat & UART_MSR_DSR) ? "DSR" : "",
+ (ch->ch_mistat & UART_MSR_DCD) ? "DCD" : "",
+ (ch->ch_mistat & UART_MSR_RI) ? "RI" : "");
+ }
+ return 0;
+}
+static DEVICE_ATTR(msignals, S_IRUSR, dgnc_tty_msignals_show, NULL);
+
+
+static ssize_t dgnc_tty_iflag_show(struct device *d, struct device_attribute *attr, char *buf)
+{
+ struct dgnc_board *bd;
+ struct channel_t *ch;
+ struct un_t *un;
+
+ if (!d)
+ return 0;
+ un = dev_get_drvdata(d);
+ if (!un || un->magic != DGNC_UNIT_MAGIC)
+ return 0;
+ ch = un->un_ch;
+ if (!ch || ch->magic != DGNC_CHANNEL_MAGIC)
+ return 0;
+ bd = ch->ch_bd;
+ if (!bd || bd->magic != DGNC_BOARD_MAGIC)
+ return 0;
+ if (bd->state != BOARD_READY)
+ return 0;
+
+ return snprintf(buf, PAGE_SIZE, "%x\n", ch->ch_c_iflag);
+}
+static DEVICE_ATTR(iflag, S_IRUSR, dgnc_tty_iflag_show, NULL);
+
+
+static ssize_t dgnc_tty_cflag_show(struct device *d, struct device_attribute *attr, char *buf)
+{
+ struct dgnc_board *bd;
+ struct channel_t *ch;
+ struct un_t *un;
+
+ if (!d)
+ return 0;
+ un = dev_get_drvdata(d);
+ if (!un || un->magic != DGNC_UNIT_MAGIC)
+ return 0;
+ ch = un->un_ch;
+ if (!ch || ch->magic != DGNC_CHANNEL_MAGIC)
+ return 0;
+ bd = ch->ch_bd;
+ if (!bd || bd->magic != DGNC_BOARD_MAGIC)
+ return 0;
+ if (bd->state != BOARD_READY)
+ return 0;
+
+ return snprintf(buf, PAGE_SIZE, "%x\n", ch->ch_c_cflag);
+}
+static DEVICE_ATTR(cflag, S_IRUSR, dgnc_tty_cflag_show, NULL);
+
+
+static ssize_t dgnc_tty_oflag_show(struct device *d, struct device_attribute *attr, char *buf)
+{
+ struct dgnc_board *bd;
+ struct channel_t *ch;
+ struct un_t *un;
+
+ if (!d)
+ return 0;
+ un = dev_get_drvdata(d);
+ if (!un || un->magic != DGNC_UNIT_MAGIC)
+ return 0;
+ ch = un->un_ch;
+ if (!ch || ch->magic != DGNC_CHANNEL_MAGIC)
+ return 0;
+ bd = ch->ch_bd;
+ if (!bd || bd->magic != DGNC_BOARD_MAGIC)
+ return 0;
+ if (bd->state != BOARD_READY)
+ return 0;
+
+ return snprintf(buf, PAGE_SIZE, "%x\n", ch->ch_c_oflag);
+}
+static DEVICE_ATTR(oflag, S_IRUSR, dgnc_tty_oflag_show, NULL);
+
+
+static ssize_t dgnc_tty_lflag_show(struct device *d, struct device_attribute *attr, char *buf)
+{
+ struct dgnc_board *bd;
+ struct channel_t *ch;
+ struct un_t *un;
+
+ if (!d)
+ return 0;
+ un = dev_get_drvdata(d);
+ if (!un || un->magic != DGNC_UNIT_MAGIC)
+ return 0;
+ ch = un->un_ch;
+ if (!ch || ch->magic != DGNC_CHANNEL_MAGIC)
+ return 0;
+ bd = ch->ch_bd;
+ if (!bd || bd->magic != DGNC_BOARD_MAGIC)
+ return 0;
+ if (bd->state != BOARD_READY)
+ return 0;
+
+ return snprintf(buf, PAGE_SIZE, "%x\n", ch->ch_c_lflag);
+}
+static DEVICE_ATTR(lflag, S_IRUSR, dgnc_tty_lflag_show, NULL);
+
+
+static ssize_t dgnc_tty_digi_flag_show(struct device *d, struct device_attribute *attr, char *buf)
+{
+ struct dgnc_board *bd;
+ struct channel_t *ch;
+ struct un_t *un;
+
+ if (!d)
+ return 0;
+ un = dev_get_drvdata(d);
+ if (!un || un->magic != DGNC_UNIT_MAGIC)
+ return 0;
+ ch = un->un_ch;
+ if (!ch || ch->magic != DGNC_CHANNEL_MAGIC)
+ return 0;
+ bd = ch->ch_bd;
+ if (!bd || bd->magic != DGNC_BOARD_MAGIC)
+ return 0;
+ if (bd->state != BOARD_READY)
+ return 0;
+
+ return snprintf(buf, PAGE_SIZE, "%x\n", ch->ch_digi.digi_flags);
+}
+static DEVICE_ATTR(digi_flag, S_IRUSR, dgnc_tty_digi_flag_show, NULL);
+
+
+static ssize_t dgnc_tty_rxcount_show(struct device *d, struct device_attribute *attr, char *buf)
+{
+ struct dgnc_board *bd;
+ struct channel_t *ch;
+ struct un_t *un;
+
+ if (!d)
+ return 0;
+ un = dev_get_drvdata(d);
+ if (!un || un->magic != DGNC_UNIT_MAGIC)
+ return 0;
+ ch = un->un_ch;
+ if (!ch || ch->magic != DGNC_CHANNEL_MAGIC)
+ return 0;
+ bd = ch->ch_bd;
+ if (!bd || bd->magic != DGNC_BOARD_MAGIC)
+ return 0;
+ if (bd->state != BOARD_READY)
+ return 0;
+
+ return snprintf(buf, PAGE_SIZE, "%ld\n", ch->ch_rxcount);
+}
+static DEVICE_ATTR(rxcount, S_IRUSR, dgnc_tty_rxcount_show, NULL);
+
+
+static ssize_t dgnc_tty_txcount_show(struct device *d, struct device_attribute *attr, char *buf)
+{
+ struct dgnc_board *bd;
+ struct channel_t *ch;
+ struct un_t *un;
+
+ if (!d)
+ return 0;
+ un = dev_get_drvdata(d);
+ if (!un || un->magic != DGNC_UNIT_MAGIC)
+ return 0;
+ ch = un->un_ch;
+ if (!ch || ch->magic != DGNC_CHANNEL_MAGIC)
+ return 0;
+ bd = ch->ch_bd;
+ if (!bd || bd->magic != DGNC_BOARD_MAGIC)
+ return 0;
+ if (bd->state != BOARD_READY)
+ return 0;
+
+ return snprintf(buf, PAGE_SIZE, "%ld\n", ch->ch_txcount);
+}
+static DEVICE_ATTR(txcount, S_IRUSR, dgnc_tty_txcount_show, NULL);
+
+
+static ssize_t dgnc_tty_name_show(struct device *d, struct device_attribute *attr, char *buf)
+{
+ struct dgnc_board *bd;
+ struct channel_t *ch;
+ struct un_t *un;
+
+ if (!d)
+ return 0;
+ un = dev_get_drvdata(d);
+ if (!un || un->magic != DGNC_UNIT_MAGIC)
+ return 0;
+ ch = un->un_ch;
+ if (!ch || ch->magic != DGNC_CHANNEL_MAGIC)
+ return 0;
+ bd = ch->ch_bd;
+ if (!bd || bd->magic != DGNC_BOARD_MAGIC)
+ return 0;
+ if (bd->state != BOARD_READY)
+ return 0;
+
+ return snprintf(buf, PAGE_SIZE, "%sn%d%c\n",
+ (un->un_type == DGNC_PRINT) ? "pr" : "tty",
+ bd->boardnum + 1, 'a' + ch->ch_portnum);
+}
+static DEVICE_ATTR(custom_name, S_IRUSR, dgnc_tty_name_show, NULL);
+
+
+static struct attribute *dgnc_sysfs_tty_entries[] = {
+ &dev_attr_state.attr,
+ &dev_attr_baud.attr,
+ &dev_attr_msignals.attr,
+ &dev_attr_iflag.attr,
+ &dev_attr_cflag.attr,
+ &dev_attr_oflag.attr,
+ &dev_attr_lflag.attr,
+ &dev_attr_digi_flag.attr,
+ &dev_attr_rxcount.attr,
+ &dev_attr_txcount.attr,
+ &dev_attr_custom_name.attr,
+ NULL
+};
+
+
+static struct attribute_group dgnc_tty_attribute_group = {
+ .name = NULL,
+ .attrs = dgnc_sysfs_tty_entries,
+};
+
+
+void dgnc_create_tty_sysfs(struct un_t *un, struct device *c)
+{
+ int ret;
+
+ ret = sysfs_create_group(&c->kobj, &dgnc_tty_attribute_group);
+ if (ret) {
+ dev_err(c, "dgnc: failed to create sysfs tty device attributes.\n");
+ sysfs_remove_group(&c->kobj, &dgnc_tty_attribute_group);
+ return;
+ }
+
+ dev_set_drvdata(c, un);
+
+}
+
+
+void dgnc_remove_tty_sysfs(struct device *c)
+{
+ sysfs_remove_group(&c->kobj, &dgnc_tty_attribute_group);
+}
+
diff --git a/drivers/staging/dgnc/dgnc_sysfs.h b/drivers/staging/dgnc/dgnc_sysfs.h
new file mode 100644
index 000000000..be0f90a67
--- /dev/null
+++ b/drivers/staging/dgnc/dgnc_sysfs.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2003 Digi International (www.digi.com)
+ * Scott H Kilau <Scott_Kilau at digi dot 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, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED; without even the
+ * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ * PURPOSE. See the GNU General Public License for more details.
+ */
+
+#ifndef __DGNC_SYSFS_H
+#define __DGNC_SYSFS_H
+
+#include <linux/device.h>
+#include "dgnc_driver.h"
+
+struct dgnc_board;
+struct channel_t;
+struct un_t;
+struct pci_driver;
+struct class_device;
+
+extern void dgnc_create_ports_sysfiles(struct dgnc_board *bd);
+extern void dgnc_remove_ports_sysfiles(struct dgnc_board *bd);
+
+extern void dgnc_create_driver_sysfiles(struct pci_driver *);
+extern void dgnc_remove_driver_sysfiles(struct pci_driver *);
+
+extern int dgnc_tty_class_init(void);
+extern int dgnc_tty_class_destroy(void);
+
+extern void dgnc_create_tty_sysfs(struct un_t *un, struct device *c);
+extern void dgnc_remove_tty_sysfs(struct device *c);
+
+#endif
diff --git a/drivers/staging/dgnc/dgnc_tty.c b/drivers/staging/dgnc/dgnc_tty.c
new file mode 100644
index 000000000..ce4187f60
--- /dev/null
+++ b/drivers/staging/dgnc/dgnc_tty.c
@@ -0,0 +1,3057 @@
+/*
+ * Copyright 2003 Digi International (www.digi.com)
+ * Scott H Kilau <Scott_Kilau at digi dot 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, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED; without even the
+ * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ * PURPOSE. See the GNU General Public License for more details.
+ */
+
+/************************************************************************
+ *
+ * This file implements the tty driver functionality for the
+ * Neo and ClassicBoard PCI based product lines.
+ *
+ ************************************************************************
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/sched.h> /* For jiffies, task states */
+#include <linux/interrupt.h> /* For tasklet and interrupt structs/defines */
+#include <linux/module.h>
+#include <linux/ctype.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/types.h>
+#include <linux/serial_reg.h>
+#include <linux/slab.h>
+#include <linux/delay.h> /* For udelay */
+#include <linux/uaccess.h> /* For copy_from_user/copy_to_user */
+#include <linux/pci.h>
+#include "dgnc_driver.h"
+#include "dgnc_tty.h"
+#include "dgnc_neo.h"
+#include "dgnc_cls.h"
+#include "dgnc_sysfs.h"
+#include "dgnc_utils.h"
+
+#define init_MUTEX(sem) sema_init(sem, 1)
+#define DECLARE_MUTEX(name) \
+ struct semaphore name = __SEMAPHORE_INITIALIZER(name, 1)
+
+/*
+ * internal variables
+ */
+static struct dgnc_board *dgnc_BoardsByMajor[256];
+static unsigned char *dgnc_TmpWriteBuf;
+static DECLARE_MUTEX(dgnc_TmpWriteSem);
+
+/*
+ * Default transparent print information.
+ */
+static struct digi_t dgnc_digi_init = {
+ .digi_flags = DIGI_COOK, /* Flags */
+ .digi_maxcps = 100, /* Max CPS */
+ .digi_maxchar = 50, /* Max chars in print queue */
+ .digi_bufsize = 100, /* Printer buffer size */
+ .digi_onlen = 4, /* size of printer on string */
+ .digi_offlen = 4, /* size of printer off string */
+ .digi_onstr = "\033[5i", /* ANSI printer on string ] */
+ .digi_offstr = "\033[4i", /* ANSI printer off string ] */
+ .digi_term = "ansi" /* default terminal type */
+};
+
+
+/*
+ * Define a local default termios struct. All ports will be created
+ * with this termios initially.
+ *
+ * This defines a raw port at 9600 baud, 8 data bits, no parity,
+ * 1 stop bit.
+ */
+static struct ktermios DgncDefaultTermios = {
+ .c_iflag = (DEFAULT_IFLAGS), /* iflags */
+ .c_oflag = (DEFAULT_OFLAGS), /* oflags */
+ .c_cflag = (DEFAULT_CFLAGS), /* cflags */
+ .c_lflag = (DEFAULT_LFLAGS), /* lflags */
+ .c_cc = INIT_C_CC,
+ .c_line = 0,
+};
+
+
+/* Our function prototypes */
+static int dgnc_tty_open(struct tty_struct *tty, struct file *file);
+static void dgnc_tty_close(struct tty_struct *tty, struct file *file);
+static int dgnc_block_til_ready(struct tty_struct *tty, struct file *file, struct channel_t *ch);
+static int dgnc_tty_ioctl(struct tty_struct *tty, unsigned int cmd, unsigned long arg);
+static int dgnc_tty_digigeta(struct tty_struct *tty, struct digi_t __user *retinfo);
+static int dgnc_tty_digiseta(struct tty_struct *tty, struct digi_t __user *new_info);
+static int dgnc_tty_write_room(struct tty_struct *tty);
+static int dgnc_tty_put_char(struct tty_struct *tty, unsigned char c);
+static int dgnc_tty_chars_in_buffer(struct tty_struct *tty);
+static void dgnc_tty_start(struct tty_struct *tty);
+static void dgnc_tty_stop(struct tty_struct *tty);
+static void dgnc_tty_throttle(struct tty_struct *tty);
+static void dgnc_tty_unthrottle(struct tty_struct *tty);
+static void dgnc_tty_flush_chars(struct tty_struct *tty);
+static void dgnc_tty_flush_buffer(struct tty_struct *tty);
+static void dgnc_tty_hangup(struct tty_struct *tty);
+static int dgnc_set_modem_info(struct tty_struct *tty, unsigned int command, unsigned int __user *value);
+static int dgnc_get_modem_info(struct channel_t *ch, unsigned int __user *value);
+static int dgnc_tty_tiocmget(struct tty_struct *tty);
+static int dgnc_tty_tiocmset(struct tty_struct *tty, unsigned int set, unsigned int clear);
+static int dgnc_tty_send_break(struct tty_struct *tty, int msec);
+static void dgnc_tty_wait_until_sent(struct tty_struct *tty, int timeout);
+static int dgnc_tty_write(struct tty_struct *tty, const unsigned char *buf, int count);
+static void dgnc_tty_set_termios(struct tty_struct *tty, struct ktermios *old_termios);
+static void dgnc_tty_send_xchar(struct tty_struct *tty, char ch);
+
+
+static const struct tty_operations dgnc_tty_ops = {
+ .open = dgnc_tty_open,
+ .close = dgnc_tty_close,
+ .write = dgnc_tty_write,
+ .write_room = dgnc_tty_write_room,
+ .flush_buffer = dgnc_tty_flush_buffer,
+ .chars_in_buffer = dgnc_tty_chars_in_buffer,
+ .flush_chars = dgnc_tty_flush_chars,
+ .ioctl = dgnc_tty_ioctl,
+ .set_termios = dgnc_tty_set_termios,
+ .stop = dgnc_tty_stop,
+ .start = dgnc_tty_start,
+ .throttle = dgnc_tty_throttle,
+ .unthrottle = dgnc_tty_unthrottle,
+ .hangup = dgnc_tty_hangup,
+ .put_char = dgnc_tty_put_char,
+ .tiocmget = dgnc_tty_tiocmget,
+ .tiocmset = dgnc_tty_tiocmset,
+ .break_ctl = dgnc_tty_send_break,
+ .wait_until_sent = dgnc_tty_wait_until_sent,
+ .send_xchar = dgnc_tty_send_xchar
+};
+
+/************************************************************************
+ *
+ * TTY Initialization/Cleanup Functions
+ *
+ ************************************************************************/
+
+/*
+ * dgnc_tty_preinit()
+ *
+ * Initialize any global tty related data before we download any boards.
+ */
+int dgnc_tty_preinit(void)
+{
+ /*
+ * Allocate a buffer for doing the copy from user space to
+ * kernel space in dgnc_write(). We only use one buffer and
+ * control access to it with a semaphore. If we are paging, we
+ * are already in trouble so one buffer won't hurt much anyway.
+ *
+ * We are okay to sleep in the malloc, as this routine
+ * is only called during module load, (not in interrupt context),
+ * and with no locks held.
+ */
+ dgnc_TmpWriteBuf = kmalloc(WRITEBUFLEN, GFP_KERNEL);
+
+ if (!dgnc_TmpWriteBuf)
+ return -ENOMEM;
+
+ return 0;
+}
+
+
+/*
+ * dgnc_tty_register()
+ *
+ * Init the tty subsystem for this board.
+ */
+int dgnc_tty_register(struct dgnc_board *brd)
+{
+ int rc = 0;
+
+ brd->SerialDriver.magic = TTY_DRIVER_MAGIC;
+
+ snprintf(brd->SerialName, MAXTTYNAMELEN, "tty_dgnc_%d_", brd->boardnum);
+
+ brd->SerialDriver.name = brd->SerialName;
+ brd->SerialDriver.name_base = 0;
+ brd->SerialDriver.major = 0;
+ brd->SerialDriver.minor_start = 0;
+ brd->SerialDriver.num = brd->maxports;
+ brd->SerialDriver.type = TTY_DRIVER_TYPE_SERIAL;
+ brd->SerialDriver.subtype = SERIAL_TYPE_NORMAL;
+ brd->SerialDriver.init_termios = DgncDefaultTermios;
+ brd->SerialDriver.driver_name = DRVSTR;
+ brd->SerialDriver.flags = (TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV | TTY_DRIVER_HARDWARE_BREAK);
+
+ /*
+ * The kernel wants space to store pointers to
+ * tty_struct's and termios's.
+ */
+ brd->SerialDriver.ttys = kcalloc(brd->maxports, sizeof(*brd->SerialDriver.ttys), GFP_KERNEL);
+ if (!brd->SerialDriver.ttys)
+ return -ENOMEM;
+
+ kref_init(&brd->SerialDriver.kref);
+ brd->SerialDriver.termios = kcalloc(brd->maxports, sizeof(*brd->SerialDriver.termios), GFP_KERNEL);
+ if (!brd->SerialDriver.termios)
+ return -ENOMEM;
+
+ /*
+ * Entry points for driver. Called by the kernel from
+ * tty_io.c and n_tty.c.
+ */
+ tty_set_operations(&brd->SerialDriver, &dgnc_tty_ops);
+
+ if (!brd->dgnc_Major_Serial_Registered) {
+ /* Register tty devices */
+ rc = tty_register_driver(&brd->SerialDriver);
+ if (rc < 0) {
+ dev_dbg(&brd->pdev->dev,
+ "Can't register tty device (%d)\n", rc);
+ return rc;
+ }
+ brd->dgnc_Major_Serial_Registered = true;
+ }
+
+ /*
+ * If we're doing transparent print, we have to do all of the above
+ * again, separately so we don't get the LD confused about what major
+ * we are when we get into the dgnc_tty_open() routine.
+ */
+ brd->PrintDriver.magic = TTY_DRIVER_MAGIC;
+ snprintf(brd->PrintName, MAXTTYNAMELEN, "pr_dgnc_%d_", brd->boardnum);
+
+ brd->PrintDriver.name = brd->PrintName;
+ brd->PrintDriver.name_base = 0;
+ brd->PrintDriver.major = brd->SerialDriver.major;
+ brd->PrintDriver.minor_start = 0x80;
+ brd->PrintDriver.num = brd->maxports;
+ brd->PrintDriver.type = TTY_DRIVER_TYPE_SERIAL;
+ brd->PrintDriver.subtype = SERIAL_TYPE_NORMAL;
+ brd->PrintDriver.init_termios = DgncDefaultTermios;
+ brd->PrintDriver.driver_name = DRVSTR;
+ brd->PrintDriver.flags = (TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV | TTY_DRIVER_HARDWARE_BREAK);
+
+ /*
+ * The kernel wants space to store pointers to
+ * tty_struct's and termios's. Must be separated from
+ * the Serial Driver so we don't get confused
+ */
+ brd->PrintDriver.ttys = kcalloc(brd->maxports, sizeof(*brd->PrintDriver.ttys), GFP_KERNEL);
+ if (!brd->PrintDriver.ttys)
+ return -ENOMEM;
+ kref_init(&brd->PrintDriver.kref);
+ brd->PrintDriver.termios = kcalloc(brd->maxports, sizeof(*brd->PrintDriver.termios), GFP_KERNEL);
+ if (!brd->PrintDriver.termios)
+ return -ENOMEM;
+
+ /*
+ * Entry points for driver. Called by the kernel from
+ * tty_io.c and n_tty.c.
+ */
+ tty_set_operations(&brd->PrintDriver, &dgnc_tty_ops);
+
+ if (!brd->dgnc_Major_TransparentPrint_Registered) {
+ /* Register Transparent Print devices */
+ rc = tty_register_driver(&brd->PrintDriver);
+ if (rc < 0) {
+ dev_dbg(&brd->pdev->dev,
+ "Can't register Transparent Print device(%d)\n",
+ rc);
+ return rc;
+ }
+ brd->dgnc_Major_TransparentPrint_Registered = true;
+ }
+
+ dgnc_BoardsByMajor[brd->SerialDriver.major] = brd;
+ brd->dgnc_Serial_Major = brd->SerialDriver.major;
+ brd->dgnc_TransparentPrint_Major = brd->PrintDriver.major;
+
+ return rc;
+}
+
+
+/*
+ * dgnc_tty_init()
+ *
+ * Init the tty subsystem. Called once per board after board has been
+ * downloaded and init'ed.
+ */
+int dgnc_tty_init(struct dgnc_board *brd)
+{
+ int i;
+ void __iomem *vaddr;
+ struct channel_t *ch;
+
+ if (!brd)
+ return -ENXIO;
+
+ /*
+ * Initialize board structure elements.
+ */
+
+ vaddr = brd->re_map_membase;
+
+ brd->nasync = brd->maxports;
+
+ /*
+ * Allocate channel memory that might not have been allocated
+ * when the driver was first loaded.
+ */
+ for (i = 0; i < brd->nasync; i++) {
+ if (!brd->channels[i]) {
+
+ /*
+ * Okay to malloc with GFP_KERNEL, we are not at
+ * interrupt context, and there are no locks held.
+ */
+ brd->channels[i] = kzalloc(sizeof(*brd->channels[i]), GFP_KERNEL);
+ }
+ }
+
+ ch = brd->channels[0];
+ vaddr = brd->re_map_membase;
+
+ /* Set up channel variables */
+ for (i = 0; i < brd->nasync; i++, ch = brd->channels[i]) {
+
+ if (!brd->channels[i])
+ continue;
+
+ spin_lock_init(&ch->ch_lock);
+
+ /* Store all our magic numbers */
+ ch->magic = DGNC_CHANNEL_MAGIC;
+ ch->ch_tun.magic = DGNC_UNIT_MAGIC;
+ ch->ch_tun.un_ch = ch;
+ ch->ch_tun.un_type = DGNC_SERIAL;
+ ch->ch_tun.un_dev = i;
+
+ ch->ch_pun.magic = DGNC_UNIT_MAGIC;
+ ch->ch_pun.un_ch = ch;
+ ch->ch_pun.un_type = DGNC_PRINT;
+ ch->ch_pun.un_dev = i + 128;
+
+ if (brd->bd_uart_offset == 0x200)
+ ch->ch_neo_uart = vaddr + (brd->bd_uart_offset * i);
+ else
+ ch->ch_cls_uart = vaddr + (brd->bd_uart_offset * i);
+
+ ch->ch_bd = brd;
+ ch->ch_portnum = i;
+ ch->ch_digi = dgnc_digi_init;
+
+ /* .25 second delay */
+ ch->ch_close_delay = 250;
+
+ init_waitqueue_head(&ch->ch_flags_wait);
+ init_waitqueue_head(&ch->ch_tun.un_flags_wait);
+ init_waitqueue_head(&ch->ch_pun.un_flags_wait);
+
+ {
+ struct device *classp;
+
+ classp = tty_register_device(&brd->SerialDriver, i,
+ &(ch->ch_bd->pdev->dev));
+ ch->ch_tun.un_sysfs = classp;
+ dgnc_create_tty_sysfs(&ch->ch_tun, classp);
+
+ classp = tty_register_device(&brd->PrintDriver, i,
+ &(ch->ch_bd->pdev->dev));
+ ch->ch_pun.un_sysfs = classp;
+ dgnc_create_tty_sysfs(&ch->ch_pun, classp);
+ }
+
+ }
+
+ return 0;
+}
+
+
+/*
+ * dgnc_tty_post_uninit()
+ *
+ * UnInitialize any global tty related data.
+ */
+void dgnc_tty_post_uninit(void)
+{
+ kfree(dgnc_TmpWriteBuf);
+ dgnc_TmpWriteBuf = NULL;
+}
+
+
+/*
+ * dgnc_tty_uninit()
+ *
+ * Uninitialize the TTY portion of this driver. Free all memory and
+ * resources.
+ */
+void dgnc_tty_uninit(struct dgnc_board *brd)
+{
+ int i = 0;
+
+ if (brd->dgnc_Major_Serial_Registered) {
+ dgnc_BoardsByMajor[brd->SerialDriver.major] = NULL;
+ brd->dgnc_Serial_Major = 0;
+ for (i = 0; i < brd->nasync; i++) {
+ dgnc_remove_tty_sysfs(brd->channels[i]->ch_tun.un_sysfs);
+ tty_unregister_device(&brd->SerialDriver, i);
+ }
+ tty_unregister_driver(&brd->SerialDriver);
+ brd->dgnc_Major_Serial_Registered = false;
+ }
+
+ if (brd->dgnc_Major_TransparentPrint_Registered) {
+ dgnc_BoardsByMajor[brd->PrintDriver.major] = NULL;
+ brd->dgnc_TransparentPrint_Major = 0;
+ for (i = 0; i < brd->nasync; i++) {
+ dgnc_remove_tty_sysfs(brd->channels[i]->ch_pun.un_sysfs);
+ tty_unregister_device(&brd->PrintDriver, i);
+ }
+ tty_unregister_driver(&brd->PrintDriver);
+ brd->dgnc_Major_TransparentPrint_Registered = false;
+ }
+
+ kfree(brd->SerialDriver.ttys);
+ brd->SerialDriver.ttys = NULL;
+ kfree(brd->PrintDriver.ttys);
+ brd->PrintDriver.ttys = NULL;
+}
+
+
+#define TMPBUFLEN (1024)
+
+/*=======================================================================
+ *
+ * dgnc_wmove - Write data to transmit queue.
+ *
+ * ch - Pointer to channel structure.
+ * buf - Poiter to characters to be moved.
+ * n - Number of characters to move.
+ *
+ *=======================================================================*/
+static void dgnc_wmove(struct channel_t *ch, char *buf, uint n)
+{
+ int remain;
+ uint head;
+
+ if (!ch || ch->magic != DGNC_CHANNEL_MAGIC)
+ return;
+
+ head = ch->ch_w_head & WQUEUEMASK;
+
+ /*
+ * If the write wraps over the top of the circular buffer,
+ * move the portion up to the wrap point, and reset the
+ * pointers to the bottom.
+ */
+ remain = WQUEUESIZE - head;
+
+ if (n >= remain) {
+ n -= remain;
+ memcpy(ch->ch_wqueue + head, buf, remain);
+ head = 0;
+ buf += remain;
+ }
+
+ if (n > 0) {
+ /*
+ * Move rest of data.
+ */
+ remain = n;
+ memcpy(ch->ch_wqueue + head, buf, remain);
+ head += remain;
+ }
+
+ head &= WQUEUEMASK;
+ ch->ch_w_head = head;
+}
+
+
+
+
+/*=======================================================================
+ *
+ * dgnc_input - Process received data.
+ *
+ * ch - Pointer to channel structure.
+ *
+ *=======================================================================*/
+void dgnc_input(struct channel_t *ch)
+{
+ struct dgnc_board *bd;
+ struct tty_struct *tp;
+ struct tty_ldisc *ld = NULL;
+ uint rmask;
+ ushort head;
+ ushort tail;
+ int data_len;
+ unsigned long flags;
+ int flip_len;
+ int len = 0;
+ int n = 0;
+ int s = 0;
+ int i = 0;
+
+ if (!ch || ch->magic != DGNC_CHANNEL_MAGIC)
+ return;
+
+ tp = ch->ch_tun.un_tty;
+
+ bd = ch->ch_bd;
+ if (!bd || bd->magic != DGNC_BOARD_MAGIC)
+ return;
+
+ spin_lock_irqsave(&ch->ch_lock, flags);
+
+ /*
+ * Figure the number of characters in the buffer.
+ * Exit immediately if none.
+ */
+ rmask = RQUEUEMASK;
+ head = ch->ch_r_head & rmask;
+ tail = ch->ch_r_tail & rmask;
+ data_len = (head - tail) & rmask;
+
+ if (data_len == 0)
+ goto exit_unlock;
+
+ /*
+ * If the device is not open, or CREAD is off,
+ * flush input data and return immediately.
+ */
+ if (!tp || (tp->magic != TTY_MAGIC) || !(ch->ch_tun.un_flags & UN_ISOPEN) ||
+ !(tp->termios.c_cflag & CREAD) || (ch->ch_tun.un_flags & UN_CLOSING)) {
+
+ ch->ch_r_head = tail;
+
+ /* Force queue flow control to be released, if needed */
+ dgnc_check_queue_flow_control(ch);
+
+ goto exit_unlock;
+ }
+
+ /*
+ * If we are throttled, simply don't read any data.
+ */
+ if (ch->ch_flags & CH_FORCED_STOPI)
+ goto exit_unlock;
+
+ flip_len = TTY_FLIPBUF_SIZE;
+
+ /* Chop down the length, if needed */
+ len = min(data_len, flip_len);
+ len = min(len, (N_TTY_BUF_SIZE - 1));
+
+ ld = tty_ldisc_ref(tp);
+
+#ifdef TTY_DONT_FLIP
+ /*
+ * If the DONT_FLIP flag is on, don't flush our buffer, and act
+ * like the ld doesn't have any space to put the data right now.
+ */
+ if (test_bit(TTY_DONT_FLIP, &tp->flags))
+ len = 0;
+#endif
+
+ /*
+ * If we were unable to get a reference to the ld,
+ * don't flush our buffer, and act like the ld doesn't
+ * have any space to put the data right now.
+ */
+ if (!ld) {
+ len = 0;
+ } else {
+ /*
+ * If ld doesn't have a pointer to a receive_buf function,
+ * flush the data, then act like the ld doesn't have any
+ * space to put the data right now.
+ */
+ if (!ld->ops->receive_buf) {
+ ch->ch_r_head = ch->ch_r_tail;
+ len = 0;
+ }
+ }
+
+ if (len <= 0)
+ goto exit_unlock;
+
+ /*
+ * The tty layer in the kernel has changed in 2.6.16+.
+ *
+ * The flip buffers in the tty structure are no longer exposed,
+ * and probably will be going away eventually.
+ *
+ * If we are completely raw, we don't need to go through a lot
+ * of the tty layers that exist.
+ * In this case, we take the shortest and fastest route we
+ * can to relay the data to the user.
+ *
+ * On the other hand, if we are not raw, we need to go through
+ * the new 2.6.16+ tty layer, which has its API more well defined.
+ */
+ len = tty_buffer_request_room(tp->port, len);
+ n = len;
+
+ /*
+ * n now contains the most amount of data we can copy,
+ * bounded either by how much the Linux tty layer can handle,
+ * or the amount of data the card actually has pending...
+ */
+ while (n) {
+ s = ((head >= tail) ? head : RQUEUESIZE) - tail;
+ s = min(s, n);
+
+ if (s <= 0)
+ break;
+
+ /*
+ * If conditions are such that ld needs to see all
+ * UART errors, we will have to walk each character
+ * and error byte and send them to the buffer one at
+ * a time.
+ */
+ if (I_PARMRK(tp) || I_BRKINT(tp) || I_INPCK(tp)) {
+ for (i = 0; i < s; i++) {
+ if (*(ch->ch_equeue + tail + i) & UART_LSR_BI)
+ tty_insert_flip_char(tp->port, *(ch->ch_rqueue + tail + i), TTY_BREAK);
+ else if (*(ch->ch_equeue + tail + i) & UART_LSR_PE)
+ tty_insert_flip_char(tp->port, *(ch->ch_rqueue + tail + i), TTY_PARITY);
+ else if (*(ch->ch_equeue + tail + i) & UART_LSR_FE)
+ tty_insert_flip_char(tp->port, *(ch->ch_rqueue + tail + i), TTY_FRAME);
+ else
+ tty_insert_flip_char(tp->port, *(ch->ch_rqueue + tail + i), TTY_NORMAL);
+ }
+ } else {
+ tty_insert_flip_string(tp->port, ch->ch_rqueue + tail, s);
+ }
+
+ tail += s;
+ n -= s;
+ /* Flip queue if needed */
+ tail &= rmask;
+ }
+
+ ch->ch_r_tail = tail & rmask;
+ ch->ch_e_tail = tail & rmask;
+ dgnc_check_queue_flow_control(ch);
+ spin_unlock_irqrestore(&ch->ch_lock, flags);
+
+ /* Tell the tty layer its okay to "eat" the data now */
+ tty_flip_buffer_push(tp->port);
+
+ if (ld)
+ tty_ldisc_deref(ld);
+ return;
+
+exit_unlock:
+ spin_unlock_irqrestore(&ch->ch_lock, flags);
+ if (ld)
+ tty_ldisc_deref(ld);
+}
+
+
+/************************************************************************
+ * Determines when CARRIER changes state and takes appropriate
+ * action.
+ ************************************************************************/
+void dgnc_carrier(struct channel_t *ch)
+{
+ struct dgnc_board *bd;
+
+ int virt_carrier = 0;
+ int phys_carrier = 0;
+
+ if (!ch || ch->magic != DGNC_CHANNEL_MAGIC)
+ return;
+
+ bd = ch->ch_bd;
+
+ if (!bd || bd->magic != DGNC_BOARD_MAGIC)
+ return;
+
+ if (ch->ch_mistat & UART_MSR_DCD)
+ phys_carrier = 1;
+
+ if (ch->ch_digi.digi_flags & DIGI_FORCEDCD)
+ virt_carrier = 1;
+
+ if (ch->ch_c_cflag & CLOCAL)
+ virt_carrier = 1;
+
+ /*
+ * Test for a VIRTUAL carrier transition to HIGH.
+ */
+ if (((ch->ch_flags & CH_FCAR) == 0) && (virt_carrier == 1)) {
+
+ /*
+ * When carrier rises, wake any threads waiting
+ * for carrier in the open routine.
+ */
+
+ if (waitqueue_active(&(ch->ch_flags_wait)))
+ wake_up_interruptible(&ch->ch_flags_wait);
+ }
+
+ /*
+ * Test for a PHYSICAL carrier transition to HIGH.
+ */
+ if (((ch->ch_flags & CH_CD) == 0) && (phys_carrier == 1)) {
+
+ /*
+ * When carrier rises, wake any threads waiting
+ * for carrier in the open routine.
+ */
+
+ if (waitqueue_active(&(ch->ch_flags_wait)))
+ wake_up_interruptible(&ch->ch_flags_wait);
+ }
+
+ /*
+ * Test for a PHYSICAL transition to low, so long as we aren't
+ * currently ignoring physical transitions (which is what "virtual
+ * carrier" indicates).
+ *
+ * The transition of the virtual carrier to low really doesn't
+ * matter... it really only means "ignore carrier state", not
+ * "make pretend that carrier is there".
+ */
+ if ((virt_carrier == 0) && ((ch->ch_flags & CH_CD) != 0) &&
+ (phys_carrier == 0)) {
+
+ /*
+ * When carrier drops:
+ *
+ * Drop carrier on all open units.
+ *
+ * Flush queues, waking up any task waiting in the
+ * line discipline.
+ *
+ * Send a hangup to the control terminal.
+ *
+ * Enable all select calls.
+ */
+ if (waitqueue_active(&(ch->ch_flags_wait)))
+ wake_up_interruptible(&ch->ch_flags_wait);
+
+ if (ch->ch_tun.un_open_count > 0)
+ tty_hangup(ch->ch_tun.un_tty);
+
+ if (ch->ch_pun.un_open_count > 0)
+ tty_hangup(ch->ch_pun.un_tty);
+ }
+
+ /*
+ * Make sure that our cached values reflect the current reality.
+ */
+ if (virt_carrier == 1)
+ ch->ch_flags |= CH_FCAR;
+ else
+ ch->ch_flags &= ~CH_FCAR;
+
+ if (phys_carrier == 1)
+ ch->ch_flags |= CH_CD;
+ else
+ ch->ch_flags &= ~CH_CD;
+}
+
+/*
+ * Assign the custom baud rate to the channel structure
+ */
+static void dgnc_set_custom_speed(struct channel_t *ch, uint newrate)
+{
+ int testdiv;
+ int testrate_high;
+ int testrate_low;
+ int deltahigh;
+ int deltalow;
+
+ if (newrate <= 0) {
+ ch->ch_custom_speed = 0;
+ return;
+ }
+
+ /*
+ * Since the divisor is stored in a 16-bit integer, we make sure
+ * we don't allow any rates smaller than a 16-bit integer would allow.
+ * And of course, rates above the dividend won't fly.
+ */
+ if (newrate && newrate < ((ch->ch_bd->bd_dividend / 0xFFFF) + 1))
+ newrate = ((ch->ch_bd->bd_dividend / 0xFFFF) + 1);
+
+ if (newrate && newrate > ch->ch_bd->bd_dividend)
+ newrate = ch->ch_bd->bd_dividend;
+
+ if (newrate > 0) {
+ testdiv = ch->ch_bd->bd_dividend / newrate;
+
+ /*
+ * If we try to figure out what rate the board would use
+ * with the test divisor, it will be either equal or higher
+ * than the requested baud rate. If we then determine the
+ * rate with a divisor one higher, we will get the next lower
+ * supported rate below the requested.
+ */
+ testrate_high = ch->ch_bd->bd_dividend / testdiv;
+ testrate_low = ch->ch_bd->bd_dividend / (testdiv + 1);
+
+ /*
+ * If the rate for the requested divisor is correct, just
+ * use it and be done.
+ */
+ if (testrate_high != newrate) {
+ /*
+ * Otherwise, pick the rate that is closer (i.e. whichever rate
+ * has a smaller delta).
+ */
+ deltahigh = testrate_high - newrate;
+ deltalow = newrate - testrate_low;
+
+ if (deltahigh < deltalow)
+ newrate = testrate_high;
+ else
+ newrate = testrate_low;
+ }
+ }
+
+ ch->ch_custom_speed = newrate;
+}
+
+
+void dgnc_check_queue_flow_control(struct channel_t *ch)
+{
+ int qleft = 0;
+
+ /* Store how much space we have left in the queue */
+ qleft = ch->ch_r_tail - ch->ch_r_head - 1;
+ if (qleft < 0)
+ qleft += RQUEUEMASK + 1;
+
+ /*
+ * Check to see if we should enforce flow control on our queue because
+ * the ld (or user) isn't reading data out of our queue fast enuf.
+ *
+ * NOTE: This is done based on what the current flow control of the
+ * port is set for.
+ *
+ * 1) HWFLOW (RTS) - Turn off the UART's Receive interrupt.
+ * This will cause the UART's FIFO to back up, and force
+ * the RTS signal to be dropped.
+ * 2) SWFLOW (IXOFF) - Keep trying to send a stop character to
+ * the other side, in hopes it will stop sending data to us.
+ * 3) NONE - Nothing we can do. We will simply drop any extra data
+ * that gets sent into us when the queue fills up.
+ */
+ if (qleft < 256) {
+ /* HWFLOW */
+ if (ch->ch_digi.digi_flags & CTSPACE || ch->ch_c_cflag & CRTSCTS) {
+ if (!(ch->ch_flags & CH_RECEIVER_OFF)) {
+ ch->ch_bd->bd_ops->disable_receiver(ch);
+ ch->ch_flags |= (CH_RECEIVER_OFF);
+ }
+ }
+ /* SWFLOW */
+ else if (ch->ch_c_iflag & IXOFF) {
+ if (ch->ch_stops_sent <= MAX_STOPS_SENT) {
+ ch->ch_bd->bd_ops->send_stop_character(ch);
+ ch->ch_stops_sent++;
+ }
+ }
+ }
+
+ /*
+ * Check to see if we should unenforce flow control because
+ * ld (or user) finally read enuf data out of our queue.
+ *
+ * NOTE: This is done based on what the current flow control of the
+ * port is set for.
+ *
+ * 1) HWFLOW (RTS) - Turn back on the UART's Receive interrupt.
+ * This will cause the UART's FIFO to raise RTS back up,
+ * which will allow the other side to start sending data again.
+ * 2) SWFLOW (IXOFF) - Send a start character to
+ * the other side, so it will start sending data to us again.
+ * 3) NONE - Do nothing. Since we didn't do anything to turn off the
+ * other side, we don't need to do anything now.
+ */
+ if (qleft > (RQUEUESIZE / 2)) {
+ /* HWFLOW */
+ if (ch->ch_digi.digi_flags & RTSPACE || ch->ch_c_cflag & CRTSCTS) {
+ if (ch->ch_flags & CH_RECEIVER_OFF) {
+ ch->ch_bd->bd_ops->enable_receiver(ch);
+ ch->ch_flags &= ~(CH_RECEIVER_OFF);
+ }
+ }
+ /* SWFLOW */
+ else if (ch->ch_c_iflag & IXOFF && ch->ch_stops_sent) {
+ ch->ch_stops_sent = 0;
+ ch->ch_bd->bd_ops->send_start_character(ch);
+ }
+ /* No FLOW */
+ else {
+ /* Nothing needed. */
+ }
+ }
+}
+
+
+void dgnc_wakeup_writes(struct channel_t *ch)
+{
+ int qlen = 0;
+ unsigned long flags;
+
+ if (!ch || ch->magic != DGNC_CHANNEL_MAGIC)
+ return;
+
+ spin_lock_irqsave(&ch->ch_lock, flags);
+
+ /*
+ * If channel now has space, wake up anyone waiting on the condition.
+ */
+ qlen = ch->ch_w_head - ch->ch_w_tail;
+ if (qlen < 0)
+ qlen += WQUEUESIZE;
+
+ if (qlen >= (WQUEUESIZE - 256)) {
+ spin_unlock_irqrestore(&ch->ch_lock, flags);
+ return;
+ }
+
+ if (ch->ch_tun.un_flags & UN_ISOPEN) {
+ if ((ch->ch_tun.un_tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+ ch->ch_tun.un_tty->ldisc->ops->write_wakeup) {
+ spin_unlock_irqrestore(&ch->ch_lock, flags);
+ (ch->ch_tun.un_tty->ldisc->ops->write_wakeup)(ch->ch_tun.un_tty);
+ spin_lock_irqsave(&ch->ch_lock, flags);
+ }
+
+ wake_up_interruptible(&ch->ch_tun.un_tty->write_wait);
+
+ /*
+ * If unit is set to wait until empty, check to make sure
+ * the queue AND FIFO are both empty.
+ */
+ if (ch->ch_tun.un_flags & UN_EMPTY) {
+ if ((qlen == 0) && (ch->ch_bd->bd_ops->get_uart_bytes_left(ch) == 0)) {
+ ch->ch_tun.un_flags &= ~(UN_EMPTY);
+
+ /*
+ * If RTS Toggle mode is on, whenever
+ * the queue and UART is empty, keep RTS low.
+ */
+ if (ch->ch_digi.digi_flags & DIGI_RTS_TOGGLE) {
+ ch->ch_mostat &= ~(UART_MCR_RTS);
+ ch->ch_bd->bd_ops->assert_modem_signals(ch);
+ }
+
+ /*
+ * If DTR Toggle mode is on, whenever
+ * the queue and UART is empty, keep DTR low.
+ */
+ if (ch->ch_digi.digi_flags & DIGI_DTR_TOGGLE) {
+ ch->ch_mostat &= ~(UART_MCR_DTR);
+ ch->ch_bd->bd_ops->assert_modem_signals(ch);
+ }
+ }
+ }
+
+ wake_up_interruptible(&ch->ch_tun.un_flags_wait);
+ }
+
+ if (ch->ch_pun.un_flags & UN_ISOPEN) {
+ if ((ch->ch_pun.un_tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+ ch->ch_pun.un_tty->ldisc->ops->write_wakeup) {
+ spin_unlock_irqrestore(&ch->ch_lock, flags);
+ (ch->ch_pun.un_tty->ldisc->ops->write_wakeup)(ch->ch_pun.un_tty);
+ spin_lock_irqsave(&ch->ch_lock, flags);
+ }
+
+ wake_up_interruptible(&ch->ch_pun.un_tty->write_wait);
+
+ /*
+ * If unit is set to wait until empty, check to make sure
+ * the queue AND FIFO are both empty.
+ */
+ if (ch->ch_pun.un_flags & UN_EMPTY) {
+ if ((qlen == 0) && (ch->ch_bd->bd_ops->get_uart_bytes_left(ch) == 0))
+ ch->ch_pun.un_flags &= ~(UN_EMPTY);
+ }
+
+ wake_up_interruptible(&ch->ch_pun.un_flags_wait);
+ }
+
+ spin_unlock_irqrestore(&ch->ch_lock, flags);
+}
+
+
+
+/************************************************************************
+ *
+ * TTY Entry points and helper functions
+ *
+ ************************************************************************/
+
+/*
+ * dgnc_tty_open()
+ *
+ */
+static int dgnc_tty_open(struct tty_struct *tty, struct file *file)
+{
+ struct dgnc_board *brd;
+ struct channel_t *ch;
+ struct un_t *un;
+ uint major = 0;
+ uint minor = 0;
+ int rc = 0;
+ unsigned long flags;
+
+ rc = 0;
+
+ major = MAJOR(tty_devnum(tty));
+ minor = MINOR(tty_devnum(tty));
+
+ if (major > 255)
+ return -ENXIO;
+
+ /* Get board pointer from our array of majors we have allocated */
+ brd = dgnc_BoardsByMajor[major];
+ if (!brd)
+ return -ENXIO;
+
+ /*
+ * If board is not yet up to a state of READY, go to
+ * sleep waiting for it to happen or they cancel the open.
+ */
+ rc = wait_event_interruptible(brd->state_wait,
+ (brd->state & BOARD_READY));
+
+ if (rc)
+ return rc;
+
+ spin_lock_irqsave(&brd->bd_lock, flags);
+
+ /* If opened device is greater than our number of ports, bail. */
+ if (PORT_NUM(minor) >= brd->nasync) {
+ spin_unlock_irqrestore(&brd->bd_lock, flags);
+ return -ENXIO;
+ }
+
+ ch = brd->channels[PORT_NUM(minor)];
+ if (!ch) {
+ spin_unlock_irqrestore(&brd->bd_lock, flags);
+ return -ENXIO;
+ }
+
+ /* Drop board lock */
+ spin_unlock_irqrestore(&brd->bd_lock, flags);
+
+ /* Grab channel lock */
+ spin_lock_irqsave(&ch->ch_lock, flags);
+
+ /* Figure out our type */
+ if (!IS_PRINT(minor)) {
+ un = &brd->channels[PORT_NUM(minor)]->ch_tun;
+ un->un_type = DGNC_SERIAL;
+ } else if (IS_PRINT(minor)) {
+ un = &brd->channels[PORT_NUM(minor)]->ch_pun;
+ un->un_type = DGNC_PRINT;
+ } else {
+ spin_unlock_irqrestore(&ch->ch_lock, flags);
+ return -ENXIO;
+ }
+
+ /*
+ * If the port is still in a previous open, and in a state
+ * where we simply cannot safely keep going, wait until the
+ * state clears.
+ */
+ spin_unlock_irqrestore(&ch->ch_lock, flags);
+
+ rc = wait_event_interruptible(ch->ch_flags_wait, ((ch->ch_flags & CH_OPENING) == 0));
+
+ /* If ret is non-zero, user ctrl-c'ed us */
+ if (rc)
+ return -EINTR;
+
+ /*
+ * If either unit is in the middle of the fragile part of close,
+ * we just cannot touch the channel safely.
+ * Go to sleep, knowing that when the channel can be
+ * touched safely, the close routine will signal the
+ * ch_flags_wait to wake us back up.
+ */
+ rc = wait_event_interruptible(ch->ch_flags_wait,
+ (((ch->ch_tun.un_flags | ch->ch_pun.un_flags) & UN_CLOSING) == 0));
+
+ /* If ret is non-zero, user ctrl-c'ed us */
+ if (rc)
+ return -EINTR;
+
+ spin_lock_irqsave(&ch->ch_lock, flags);
+
+
+ /* Store our unit into driver_data, so we always have it available. */
+ tty->driver_data = un;
+
+
+ /*
+ * Initialize tty's
+ */
+ if (!(un->un_flags & UN_ISOPEN)) {
+ /* Store important variables. */
+ un->un_tty = tty;
+
+ /* Maybe do something here to the TTY struct as well? */
+ }
+
+
+ /*
+ * Allocate channel buffers for read/write/error.
+ * Set flag, so we don't get trounced on.
+ */
+ ch->ch_flags |= (CH_OPENING);
+
+ /* Drop locks, as malloc with GFP_KERNEL can sleep */
+ spin_unlock_irqrestore(&ch->ch_lock, flags);
+
+ if (!ch->ch_rqueue)
+ ch->ch_rqueue = kzalloc(RQUEUESIZE, GFP_KERNEL);
+ if (!ch->ch_equeue)
+ ch->ch_equeue = kzalloc(EQUEUESIZE, GFP_KERNEL);
+ if (!ch->ch_wqueue)
+ ch->ch_wqueue = kzalloc(WQUEUESIZE, GFP_KERNEL);
+
+ spin_lock_irqsave(&ch->ch_lock, flags);
+
+ ch->ch_flags &= ~(CH_OPENING);
+ wake_up_interruptible(&ch->ch_flags_wait);
+
+ /*
+ * Initialize if neither terminal or printer is open.
+ */
+ if (!((ch->ch_tun.un_flags | ch->ch_pun.un_flags) & UN_ISOPEN)) {
+
+ /*
+ * Flush input queues.
+ */
+ ch->ch_r_head = 0;
+ ch->ch_r_tail = 0;
+ ch->ch_e_head = 0;
+ ch->ch_e_tail = 0;
+ ch->ch_w_head = 0;
+ ch->ch_w_tail = 0;
+
+ brd->bd_ops->flush_uart_write(ch);
+ brd->bd_ops->flush_uart_read(ch);
+
+ ch->ch_flags = 0;
+ ch->ch_cached_lsr = 0;
+ ch->ch_stop_sending_break = 0;
+ ch->ch_stops_sent = 0;
+
+ ch->ch_c_cflag = tty->termios.c_cflag;
+ ch->ch_c_iflag = tty->termios.c_iflag;
+ ch->ch_c_oflag = tty->termios.c_oflag;
+ ch->ch_c_lflag = tty->termios.c_lflag;
+ ch->ch_startc = tty->termios.c_cc[VSTART];
+ ch->ch_stopc = tty->termios.c_cc[VSTOP];
+
+ /*
+ * Bring up RTS and DTR...
+ * Also handle RTS or DTR toggle if set.
+ */
+ if (!(ch->ch_digi.digi_flags & DIGI_RTS_TOGGLE))
+ ch->ch_mostat |= (UART_MCR_RTS);
+ if (!(ch->ch_digi.digi_flags & DIGI_DTR_TOGGLE))
+ ch->ch_mostat |= (UART_MCR_DTR);
+
+ /* Tell UART to init itself */
+ brd->bd_ops->uart_init(ch);
+ }
+
+ /*
+ * Run param in case we changed anything
+ */
+ brd->bd_ops->param(tty);
+
+ dgnc_carrier(ch);
+
+ /*
+ * follow protocol for opening port
+ */
+
+ spin_unlock_irqrestore(&ch->ch_lock, flags);
+
+ rc = dgnc_block_til_ready(tty, file, ch);
+
+ /* No going back now, increment our unit and channel counters */
+ spin_lock_irqsave(&ch->ch_lock, flags);
+ ch->ch_open_count++;
+ un->un_open_count++;
+ un->un_flags |= (UN_ISOPEN);
+ spin_unlock_irqrestore(&ch->ch_lock, flags);
+
+ return rc;
+}
+
+
+/*
+ * dgnc_block_til_ready()
+ *
+ * Wait for DCD, if needed.
+ */
+static int dgnc_block_til_ready(struct tty_struct *tty, struct file *file, struct channel_t *ch)
+{
+ int retval = 0;
+ struct un_t *un = NULL;
+ unsigned long flags;
+ uint old_flags = 0;
+ int sleep_on_un_flags = 0;
+
+ if (!tty || tty->magic != TTY_MAGIC || !file || !ch || ch->magic != DGNC_CHANNEL_MAGIC)
+ return -ENXIO;
+
+ un = tty->driver_data;
+ if (!un || un->magic != DGNC_UNIT_MAGIC)
+ return -ENXIO;
+
+ spin_lock_irqsave(&ch->ch_lock, flags);
+
+ ch->ch_wopen++;
+
+ /* Loop forever */
+ while (1) {
+
+ sleep_on_un_flags = 0;
+
+ /*
+ * If board has failed somehow during our sleep, bail with error.
+ */
+ if (ch->ch_bd->state == BOARD_FAILED) {
+ retval = -ENXIO;
+ break;
+ }
+
+ /* If tty was hung up, break out of loop and set error. */
+ if (tty_hung_up_p(file)) {
+ retval = -EAGAIN;
+ break;
+ }
+
+ /*
+ * If either unit is in the middle of the fragile part of close,
+ * we just cannot touch the channel safely.
+ * Go back to sleep, knowing that when the channel can be
+ * touched safely, the close routine will signal the
+ * ch_wait_flags to wake us back up.
+ */
+ if (!((ch->ch_tun.un_flags | ch->ch_pun.un_flags) & UN_CLOSING)) {
+
+ /*
+ * Our conditions to leave cleanly and happily:
+ * 1) NONBLOCKING on the tty is set.
+ * 2) CLOCAL is set.
+ * 3) DCD (fake or real) is active.
+ */
+
+ if (file->f_flags & O_NONBLOCK)
+ break;
+
+ if (tty->flags & (1 << TTY_IO_ERROR)) {
+ retval = -EIO;
+ break;
+ }
+
+ if (ch->ch_flags & CH_CD)
+ break;
+
+ if (ch->ch_flags & CH_FCAR)
+ break;
+ } else {
+ sleep_on_un_flags = 1;
+ }
+
+ /*
+ * If there is a signal pending, the user probably
+ * interrupted (ctrl-c) us.
+ * Leave loop with error set.
+ */
+ if (signal_pending(current)) {
+ retval = -ERESTARTSYS;
+ break;
+ }
+
+ /*
+ * Store the flags before we let go of channel lock
+ */
+ if (sleep_on_un_flags)
+ old_flags = ch->ch_tun.un_flags | ch->ch_pun.un_flags;
+ else
+ old_flags = ch->ch_flags;
+
+ /*
+ * Let go of channel lock before calling schedule.
+ * Our poller will get any FEP events and wake us up when DCD
+ * eventually goes active.
+ */
+
+ spin_unlock_irqrestore(&ch->ch_lock, flags);
+
+ /*
+ * Wait for something in the flags to change from the current value.
+ */
+ if (sleep_on_un_flags)
+ retval = wait_event_interruptible(un->un_flags_wait,
+ (old_flags != (ch->ch_tun.un_flags | ch->ch_pun.un_flags)));
+ else
+ retval = wait_event_interruptible(ch->ch_flags_wait,
+ (old_flags != ch->ch_flags));
+
+ /*
+ * We got woken up for some reason.
+ * Before looping around, grab our channel lock.
+ */
+ spin_lock_irqsave(&ch->ch_lock, flags);
+ }
+
+ ch->ch_wopen--;
+
+ spin_unlock_irqrestore(&ch->ch_lock, flags);
+
+ if (retval)
+ return retval;
+
+ return 0;
+}
+
+
+/*
+ * dgnc_tty_hangup()
+ *
+ * Hangup the port. Like a close, but don't wait for output to drain.
+ */
+static void dgnc_tty_hangup(struct tty_struct *tty)
+{
+ struct un_t *un;
+
+ if (!tty || tty->magic != TTY_MAGIC)
+ return;
+
+ un = tty->driver_data;
+ if (!un || un->magic != DGNC_UNIT_MAGIC)
+ return;
+
+ /* flush the transmit queues */
+ dgnc_tty_flush_buffer(tty);
+
+}
+
+
+/*
+ * dgnc_tty_close()
+ *
+ */
+static void dgnc_tty_close(struct tty_struct *tty, struct file *file)
+{
+ struct ktermios *ts;
+ struct dgnc_board *bd;
+ struct channel_t *ch;
+ struct un_t *un;
+ unsigned long flags;
+ int rc = 0;
+
+ if (!tty || tty->magic != TTY_MAGIC)
+ return;
+
+ un = tty->driver_data;
+ if (!un || un->magic != DGNC_UNIT_MAGIC)
+ return;
+
+ ch = un->un_ch;
+ if (!ch || ch->magic != DGNC_CHANNEL_MAGIC)
+ return;
+
+ bd = ch->ch_bd;
+ if (!bd || bd->magic != DGNC_BOARD_MAGIC)
+ return;
+
+ ts = &tty->termios;
+
+ spin_lock_irqsave(&ch->ch_lock, flags);
+
+ /*
+ * Determine if this is the last close or not - and if we agree about
+ * which type of close it is with the Line Discipline
+ */
+ if ((tty->count == 1) && (un->un_open_count != 1)) {
+ /*
+ * Uh, oh. tty->count is 1, which means that the tty
+ * structure will be freed. un_open_count should always
+ * be one in these conditions. If it's greater than
+ * one, we've got real problems, since it means the
+ * serial port won't be shutdown.
+ */
+ dev_dbg(tty->dev,
+ "tty->count is 1, un open count is %d\n",
+ un->un_open_count);
+ un->un_open_count = 1;
+ }
+
+ if (un->un_open_count)
+ un->un_open_count--;
+ else
+ dev_dbg(tty->dev,
+ "bad serial port open count of %d\n",
+ un->un_open_count);
+
+ ch->ch_open_count--;
+
+ if (ch->ch_open_count && un->un_open_count) {
+ spin_unlock_irqrestore(&ch->ch_lock, flags);
+ return;
+ }
+
+ /* OK, its the last close on the unit */
+ un->un_flags |= UN_CLOSING;
+
+ tty->closing = 1;
+
+
+ /*
+ * Only officially close channel if count is 0 and
+ * DIGI_PRINTER bit is not set.
+ */
+ if ((ch->ch_open_count == 0) && !(ch->ch_digi.digi_flags & DIGI_PRINTER)) {
+
+ ch->ch_flags &= ~(CH_STOPI | CH_FORCED_STOPI);
+
+ /*
+ * turn off print device when closing print device.
+ */
+ if ((un->un_type == DGNC_PRINT) && (ch->ch_flags & CH_PRON)) {
+ dgnc_wmove(ch, ch->ch_digi.digi_offstr,
+ (int) ch->ch_digi.digi_offlen);
+ ch->ch_flags &= ~CH_PRON;
+ }
+
+ spin_unlock_irqrestore(&ch->ch_lock, flags);
+ /* wait for output to drain */
+ /* This will also return if we take an interrupt */
+
+ rc = bd->bd_ops->drain(tty, 0);
+
+ dgnc_tty_flush_buffer(tty);
+ tty_ldisc_flush(tty);
+
+ spin_lock_irqsave(&ch->ch_lock, flags);
+
+ tty->closing = 0;
+
+ /*
+ * If we have HUPCL set, lower DTR and RTS
+ */
+ if (ch->ch_c_cflag & HUPCL) {
+
+ /* Drop RTS/DTR */
+ ch->ch_mostat &= ~(UART_MCR_DTR | UART_MCR_RTS);
+ bd->bd_ops->assert_modem_signals(ch);
+
+ /*
+ * Go to sleep to ensure RTS/DTR
+ * have been dropped for modems to see it.
+ */
+ if (ch->ch_close_delay) {
+ spin_unlock_irqrestore(&ch->ch_lock,
+ flags);
+ dgnc_ms_sleep(ch->ch_close_delay);
+ spin_lock_irqsave(&ch->ch_lock, flags);
+ }
+ }
+
+ ch->ch_old_baud = 0;
+
+ /* Turn off UART interrupts for this port */
+ ch->ch_bd->bd_ops->uart_off(ch);
+ } else {
+ /*
+ * turn off print device when closing print device.
+ */
+ if ((un->un_type == DGNC_PRINT) && (ch->ch_flags & CH_PRON)) {
+ dgnc_wmove(ch, ch->ch_digi.digi_offstr,
+ (int) ch->ch_digi.digi_offlen);
+ ch->ch_flags &= ~CH_PRON;
+ }
+ }
+
+ un->un_tty = NULL;
+ un->un_flags &= ~(UN_ISOPEN | UN_CLOSING);
+
+ wake_up_interruptible(&ch->ch_flags_wait);
+ wake_up_interruptible(&un->un_flags_wait);
+
+ spin_unlock_irqrestore(&ch->ch_lock, flags);
+}
+
+
+/*
+ * dgnc_tty_chars_in_buffer()
+ *
+ * Return number of characters that have not been transmitted yet.
+ *
+ * This routine is used by the line discipline to determine if there
+ * is data waiting to be transmitted/drained/flushed or not.
+ */
+static int dgnc_tty_chars_in_buffer(struct tty_struct *tty)
+{
+ struct channel_t *ch = NULL;
+ struct un_t *un = NULL;
+ ushort thead;
+ ushort ttail;
+ uint tmask;
+ uint chars = 0;
+ unsigned long flags;
+
+ if (tty == NULL)
+ return 0;
+
+ un = tty->driver_data;
+ if (!un || un->magic != DGNC_UNIT_MAGIC)
+ return 0;
+
+ ch = un->un_ch;
+ if (!ch || ch->magic != DGNC_CHANNEL_MAGIC)
+ return 0;
+
+ spin_lock_irqsave(&ch->ch_lock, flags);
+
+ tmask = WQUEUEMASK;
+ thead = ch->ch_w_head & tmask;
+ ttail = ch->ch_w_tail & tmask;
+
+ spin_unlock_irqrestore(&ch->ch_lock, flags);
+
+ if (ttail == thead) {
+ chars = 0;
+ } else {
+ if (thead >= ttail)
+ chars = thead - ttail;
+ else
+ chars = thead - ttail + WQUEUESIZE;
+ }
+
+ return chars;
+}
+
+
+/*
+ * dgnc_maxcps_room
+ *
+ * Reduces bytes_available to the max number of characters
+ * that can be sent currently given the maxcps value, and
+ * returns the new bytes_available. This only affects printer
+ * output.
+ */
+static int dgnc_maxcps_room(struct tty_struct *tty, int bytes_available)
+{
+ struct channel_t *ch = NULL;
+ struct un_t *un = NULL;
+
+ if (!tty)
+ return bytes_available;
+
+ un = tty->driver_data;
+ if (!un || un->magic != DGNC_UNIT_MAGIC)
+ return bytes_available;
+
+ ch = un->un_ch;
+ if (!ch || ch->magic != DGNC_CHANNEL_MAGIC)
+ return bytes_available;
+
+ /*
+ * If its not the Transparent print device, return
+ * the full data amount.
+ */
+ if (un->un_type != DGNC_PRINT)
+ return bytes_available;
+
+ if (ch->ch_digi.digi_maxcps > 0 && ch->ch_digi.digi_bufsize > 0) {
+ int cps_limit = 0;
+ unsigned long current_time = jiffies;
+ unsigned long buffer_time = current_time +
+ (HZ * ch->ch_digi.digi_bufsize) / ch->ch_digi.digi_maxcps;
+
+ if (ch->ch_cpstime < current_time) {
+ /* buffer is empty */
+ ch->ch_cpstime = current_time; /* reset ch_cpstime */
+ cps_limit = ch->ch_digi.digi_bufsize;
+ } else if (ch->ch_cpstime < buffer_time) {
+ /* still room in the buffer */
+ cps_limit = ((buffer_time - ch->ch_cpstime) * ch->ch_digi.digi_maxcps) / HZ;
+ } else {
+ /* no room in the buffer */
+ cps_limit = 0;
+ }
+
+ bytes_available = min(cps_limit, bytes_available);
+ }
+
+ return bytes_available;
+}
+
+
+/*
+ * dgnc_tty_write_room()
+ *
+ * Return space available in Tx buffer
+ */
+static int dgnc_tty_write_room(struct tty_struct *tty)
+{
+ struct channel_t *ch = NULL;
+ struct un_t *un = NULL;
+ ushort head;
+ ushort tail;
+ ushort tmask;
+ int ret = 0;
+ unsigned long flags;
+
+ if (tty == NULL || dgnc_TmpWriteBuf == NULL)
+ return 0;
+
+ un = tty->driver_data;
+ if (!un || un->magic != DGNC_UNIT_MAGIC)
+ return 0;
+
+ ch = un->un_ch;
+ if (!ch || ch->magic != DGNC_CHANNEL_MAGIC)
+ return 0;
+
+ spin_lock_irqsave(&ch->ch_lock, flags);
+
+ tmask = WQUEUEMASK;
+ head = (ch->ch_w_head) & tmask;
+ tail = (ch->ch_w_tail) & tmask;
+
+ ret = tail - head - 1;
+ if (ret < 0)
+ ret += WQUEUESIZE;
+
+ /* Limit printer to maxcps */
+ ret = dgnc_maxcps_room(tty, ret);
+
+ /*
+ * If we are printer device, leave space for
+ * possibly both the on and off strings.
+ */
+ if (un->un_type == DGNC_PRINT) {
+ if (!(ch->ch_flags & CH_PRON))
+ ret -= ch->ch_digi.digi_onlen;
+ ret -= ch->ch_digi.digi_offlen;
+ } else {
+ if (ch->ch_flags & CH_PRON)
+ ret -= ch->ch_digi.digi_offlen;
+ }
+
+ if (ret < 0)
+ ret = 0;
+
+ spin_unlock_irqrestore(&ch->ch_lock, flags);
+
+ return ret;
+}
+
+
+/*
+ * dgnc_tty_put_char()
+ *
+ * Put a character into ch->ch_buf
+ *
+ * - used by the line discipline for OPOST processing
+ */
+static int dgnc_tty_put_char(struct tty_struct *tty, unsigned char c)
+{
+ /*
+ * Simply call tty_write.
+ */
+ dgnc_tty_write(tty, &c, 1);
+ return 1;
+}
+
+
+/*
+ * dgnc_tty_write()
+ *
+ * Take data from the user or kernel and send it out to the FEP.
+ * In here exists all the Transparent Print magic as well.
+ */
+static int dgnc_tty_write(struct tty_struct *tty,
+ const unsigned char *buf, int count)
+{
+ struct channel_t *ch = NULL;
+ struct un_t *un = NULL;
+ int bufcount = 0, n = 0;
+ int orig_count = 0;
+ unsigned long flags;
+ ushort head;
+ ushort tail;
+ ushort tmask;
+ uint remain;
+ int from_user = 0;
+
+ if (tty == NULL || dgnc_TmpWriteBuf == NULL)
+ return 0;
+
+ un = tty->driver_data;
+ if (!un || un->magic != DGNC_UNIT_MAGIC)
+ return 0;
+
+ ch = un->un_ch;
+ if (!ch || ch->magic != DGNC_CHANNEL_MAGIC)
+ return 0;
+
+ if (!count)
+ return 0;
+
+ /*
+ * Store original amount of characters passed in.
+ * This helps to figure out if we should ask the FEP
+ * to send us an event when it has more space available.
+ */
+ orig_count = count;
+
+ spin_lock_irqsave(&ch->ch_lock, flags);
+
+ /* Get our space available for the channel from the board */
+ tmask = WQUEUEMASK;
+ head = (ch->ch_w_head) & tmask;
+ tail = (ch->ch_w_tail) & tmask;
+
+ bufcount = tail - head - 1;
+ if (bufcount < 0)
+ bufcount += WQUEUESIZE;
+
+ /*
+ * Limit printer output to maxcps overall, with bursts allowed
+ * up to bufsize characters.
+ */
+ bufcount = dgnc_maxcps_room(tty, bufcount);
+
+ /*
+ * Take minimum of what the user wants to send, and the
+ * space available in the FEP buffer.
+ */
+ count = min(count, bufcount);
+
+ /*
+ * Bail if no space left.
+ */
+ if (count <= 0)
+ goto exit_retry;
+
+ /*
+ * Output the printer ON string, if we are in terminal mode, but
+ * need to be in printer mode.
+ */
+ if ((un->un_type == DGNC_PRINT) && !(ch->ch_flags & CH_PRON)) {
+ dgnc_wmove(ch, ch->ch_digi.digi_onstr,
+ (int) ch->ch_digi.digi_onlen);
+ head = (ch->ch_w_head) & tmask;
+ ch->ch_flags |= CH_PRON;
+ }
+
+ /*
+ * On the other hand, output the printer OFF string, if we are
+ * currently in printer mode, but need to output to the terminal.
+ */
+ if ((un->un_type != DGNC_PRINT) && (ch->ch_flags & CH_PRON)) {
+ dgnc_wmove(ch, ch->ch_digi.digi_offstr,
+ (int) ch->ch_digi.digi_offlen);
+ head = (ch->ch_w_head) & tmask;
+ ch->ch_flags &= ~CH_PRON;
+ }
+
+ /*
+ * If there is nothing left to copy, or I can't handle any more data, leave.
+ */
+ if (count <= 0)
+ goto exit_retry;
+
+ if (from_user) {
+
+ count = min(count, WRITEBUFLEN);
+
+ spin_unlock_irqrestore(&ch->ch_lock, flags);
+
+ /*
+ * If data is coming from user space, copy it into a temporary
+ * buffer so we don't get swapped out while doing the copy to
+ * the board.
+ */
+ /* we're allowed to block if it's from_user */
+ if (down_interruptible(&dgnc_TmpWriteSem))
+ return -EINTR;
+
+ /*
+ * copy_from_user() returns the number
+ * of bytes that could *NOT* be copied.
+ */
+ count -= copy_from_user(dgnc_TmpWriteBuf, (const unsigned char __user *) buf, count);
+
+ if (!count) {
+ up(&dgnc_TmpWriteSem);
+ return -EFAULT;
+ }
+
+ spin_lock_irqsave(&ch->ch_lock, flags);
+
+ buf = dgnc_TmpWriteBuf;
+
+ }
+
+ n = count;
+
+ /*
+ * If the write wraps over the top of the circular buffer,
+ * move the portion up to the wrap point, and reset the
+ * pointers to the bottom.
+ */
+ remain = WQUEUESIZE - head;
+
+ if (n >= remain) {
+ n -= remain;
+ memcpy(ch->ch_wqueue + head, buf, remain);
+ head = 0;
+ buf += remain;
+ }
+
+ if (n > 0) {
+ /*
+ * Move rest of data.
+ */
+ remain = n;
+ memcpy(ch->ch_wqueue + head, buf, remain);
+ head += remain;
+ }
+
+ if (count) {
+ head &= tmask;
+ ch->ch_w_head = head;
+ }
+
+ /* Update printer buffer empty time. */
+ if ((un->un_type == DGNC_PRINT) && (ch->ch_digi.digi_maxcps > 0)
+ && (ch->ch_digi.digi_bufsize > 0)) {
+ ch->ch_cpstime += (HZ * count) / ch->ch_digi.digi_maxcps;
+ }
+
+ if (from_user) {
+ spin_unlock_irqrestore(&ch->ch_lock, flags);
+ up(&dgnc_TmpWriteSem);
+ } else {
+ spin_unlock_irqrestore(&ch->ch_lock, flags);
+ }
+
+ if (count) {
+ /*
+ * Channel lock is grabbed and then released
+ * inside this routine.
+ */
+ ch->ch_bd->bd_ops->copy_data_from_queue_to_uart(ch);
+ }
+
+ return count;
+
+exit_retry:
+
+ spin_unlock_irqrestore(&ch->ch_lock, flags);
+ return 0;
+}
+
+
+/*
+ * Return modem signals to ld.
+ */
+
+static int dgnc_tty_tiocmget(struct tty_struct *tty)
+{
+ struct channel_t *ch;
+ struct un_t *un;
+ int result = -EIO;
+ unsigned char mstat = 0;
+ unsigned long flags;
+
+ if (!tty || tty->magic != TTY_MAGIC)
+ return result;
+
+ un = tty->driver_data;
+ if (!un || un->magic != DGNC_UNIT_MAGIC)
+ return result;
+
+ ch = un->un_ch;
+ if (!ch || ch->magic != DGNC_CHANNEL_MAGIC)
+ return result;
+
+ spin_lock_irqsave(&ch->ch_lock, flags);
+
+ mstat = (ch->ch_mostat | ch->ch_mistat);
+
+ spin_unlock_irqrestore(&ch->ch_lock, flags);
+
+ result = 0;
+
+ if (mstat & UART_MCR_DTR)
+ result |= TIOCM_DTR;
+ if (mstat & UART_MCR_RTS)
+ result |= TIOCM_RTS;
+ if (mstat & UART_MSR_CTS)
+ result |= TIOCM_CTS;
+ if (mstat & UART_MSR_DSR)
+ result |= TIOCM_DSR;
+ if (mstat & UART_MSR_RI)
+ result |= TIOCM_RI;
+ if (mstat & UART_MSR_DCD)
+ result |= TIOCM_CD;
+
+ return result;
+}
+
+
+/*
+ * dgnc_tty_tiocmset()
+ *
+ * Set modem signals, called by ld.
+ */
+
+static int dgnc_tty_tiocmset(struct tty_struct *tty,
+ unsigned int set, unsigned int clear)
+{
+ struct dgnc_board *bd;
+ struct channel_t *ch;
+ struct un_t *un;
+ int ret = -EIO;
+ unsigned long flags;
+
+ if (!tty || tty->magic != TTY_MAGIC)
+ return ret;
+
+ un = tty->driver_data;
+ if (!un || un->magic != DGNC_UNIT_MAGIC)
+ return ret;
+
+ ch = un->un_ch;
+ if (!ch || ch->magic != DGNC_CHANNEL_MAGIC)
+ return ret;
+
+ bd = ch->ch_bd;
+ if (!bd || bd->magic != DGNC_BOARD_MAGIC)
+ return ret;
+
+ spin_lock_irqsave(&ch->ch_lock, flags);
+
+ if (set & TIOCM_RTS)
+ ch->ch_mostat |= UART_MCR_RTS;
+
+ if (set & TIOCM_DTR)
+ ch->ch_mostat |= UART_MCR_DTR;
+
+ if (clear & TIOCM_RTS)
+ ch->ch_mostat &= ~(UART_MCR_RTS);
+
+ if (clear & TIOCM_DTR)
+ ch->ch_mostat &= ~(UART_MCR_DTR);
+
+ ch->ch_bd->bd_ops->assert_modem_signals(ch);
+
+ spin_unlock_irqrestore(&ch->ch_lock, flags);
+
+ return 0;
+}
+
+
+/*
+ * dgnc_tty_send_break()
+ *
+ * Send a Break, called by ld.
+ */
+static int dgnc_tty_send_break(struct tty_struct *tty, int msec)
+{
+ struct dgnc_board *bd;
+ struct channel_t *ch;
+ struct un_t *un;
+ int ret = -EIO;
+ unsigned long flags;
+
+ if (!tty || tty->magic != TTY_MAGIC)
+ return ret;
+
+ un = tty->driver_data;
+ if (!un || un->magic != DGNC_UNIT_MAGIC)
+ return ret;
+
+ ch = un->un_ch;
+ if (!ch || ch->magic != DGNC_CHANNEL_MAGIC)
+ return ret;
+
+ bd = ch->ch_bd;
+ if (!bd || bd->magic != DGNC_BOARD_MAGIC)
+ return ret;
+
+ switch (msec) {
+ case -1:
+ msec = 0xFFFF;
+ break;
+ case 0:
+ msec = 0;
+ break;
+ default:
+ break;
+ }
+
+ spin_lock_irqsave(&ch->ch_lock, flags);
+
+ ch->ch_bd->bd_ops->send_break(ch, msec);
+
+ spin_unlock_irqrestore(&ch->ch_lock, flags);
+
+ return 0;
+
+}
+
+
+/*
+ * dgnc_tty_wait_until_sent()
+ *
+ * wait until data has been transmitted, called by ld.
+ */
+static void dgnc_tty_wait_until_sent(struct tty_struct *tty, int timeout)
+{
+ struct dgnc_board *bd;
+ struct channel_t *ch;
+ struct un_t *un;
+ int rc;
+
+ if (!tty || tty->magic != TTY_MAGIC)
+ return;
+
+ un = tty->driver_data;
+ if (!un || un->magic != DGNC_UNIT_MAGIC)
+ return;
+
+ ch = un->un_ch;
+ if (!ch || ch->magic != DGNC_CHANNEL_MAGIC)
+ return;
+
+ bd = ch->ch_bd;
+ if (!bd || bd->magic != DGNC_BOARD_MAGIC)
+ return;
+
+ rc = bd->bd_ops->drain(tty, 0);
+}
+
+
+/*
+ * dgnc_send_xchar()
+ *
+ * send a high priority character, called by ld.
+ */
+static void dgnc_tty_send_xchar(struct tty_struct *tty, char c)
+{
+ struct dgnc_board *bd;
+ struct channel_t *ch;
+ struct un_t *un;
+ unsigned long flags;
+
+ if (!tty || tty->magic != TTY_MAGIC)
+ return;
+
+ un = tty->driver_data;
+ if (!un || un->magic != DGNC_UNIT_MAGIC)
+ return;
+
+ ch = un->un_ch;
+ if (!ch || ch->magic != DGNC_CHANNEL_MAGIC)
+ return;
+
+ bd = ch->ch_bd;
+ if (!bd || bd->magic != DGNC_BOARD_MAGIC)
+ return;
+
+ dev_dbg(tty->dev, "dgnc_tty_send_xchar start\n");
+
+ spin_lock_irqsave(&ch->ch_lock, flags);
+ bd->bd_ops->send_immediate_char(ch, c);
+ spin_unlock_irqrestore(&ch->ch_lock, flags);
+
+ dev_dbg(tty->dev, "dgnc_tty_send_xchar finish\n");
+}
+
+
+
+
+/*
+ * Return modem signals to ld.
+ */
+static inline int dgnc_get_mstat(struct channel_t *ch)
+{
+ unsigned char mstat;
+ int result = -EIO;
+ unsigned long flags;
+
+ if (!ch || ch->magic != DGNC_CHANNEL_MAGIC)
+ return -ENXIO;
+
+ spin_lock_irqsave(&ch->ch_lock, flags);
+
+ mstat = (ch->ch_mostat | ch->ch_mistat);
+
+ spin_unlock_irqrestore(&ch->ch_lock, flags);
+
+ result = 0;
+
+ if (mstat & UART_MCR_DTR)
+ result |= TIOCM_DTR;
+ if (mstat & UART_MCR_RTS)
+ result |= TIOCM_RTS;
+ if (mstat & UART_MSR_CTS)
+ result |= TIOCM_CTS;
+ if (mstat & UART_MSR_DSR)
+ result |= TIOCM_DSR;
+ if (mstat & UART_MSR_RI)
+ result |= TIOCM_RI;
+ if (mstat & UART_MSR_DCD)
+ result |= TIOCM_CD;
+
+ return result;
+}
+
+
+
+/*
+ * Return modem signals to ld.
+ */
+static int dgnc_get_modem_info(struct channel_t *ch, unsigned int __user *value)
+{
+ int result;
+
+ if (!ch || ch->magic != DGNC_CHANNEL_MAGIC)
+ return -ENXIO;
+
+ result = dgnc_get_mstat(ch);
+
+ if (result < 0)
+ return -ENXIO;
+
+ return put_user(result, value);
+}
+
+
+/*
+ * dgnc_set_modem_info()
+ *
+ * Set modem signals, called by ld.
+ */
+static int dgnc_set_modem_info(struct tty_struct *tty, unsigned int command, unsigned int __user *value)
+{
+ struct dgnc_board *bd;
+ struct channel_t *ch;
+ struct un_t *un;
+ int ret = -ENXIO;
+ unsigned int arg = 0;
+ unsigned long flags;
+
+ if (!tty || tty->magic != TTY_MAGIC)
+ return ret;
+
+ un = tty->driver_data;
+ if (!un || un->magic != DGNC_UNIT_MAGIC)
+ return ret;
+
+ ch = un->un_ch;
+ if (!ch || ch->magic != DGNC_CHANNEL_MAGIC)
+ return ret;
+
+ bd = ch->ch_bd;
+ if (!bd || bd->magic != DGNC_BOARD_MAGIC)
+ return ret;
+
+ ret = get_user(arg, value);
+ if (ret)
+ return ret;
+
+ switch (command) {
+ case TIOCMBIS:
+ if (arg & TIOCM_RTS)
+ ch->ch_mostat |= UART_MCR_RTS;
+
+ if (arg & TIOCM_DTR)
+ ch->ch_mostat |= UART_MCR_DTR;
+
+ break;
+
+ case TIOCMBIC:
+ if (arg & TIOCM_RTS)
+ ch->ch_mostat &= ~(UART_MCR_RTS);
+
+ if (arg & TIOCM_DTR)
+ ch->ch_mostat &= ~(UART_MCR_DTR);
+
+ break;
+
+ case TIOCMSET:
+
+ if (arg & TIOCM_RTS)
+ ch->ch_mostat |= UART_MCR_RTS;
+ else
+ ch->ch_mostat &= ~(UART_MCR_RTS);
+
+ if (arg & TIOCM_DTR)
+ ch->ch_mostat |= UART_MCR_DTR;
+ else
+ ch->ch_mostat &= ~(UART_MCR_DTR);
+
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ spin_lock_irqsave(&ch->ch_lock, flags);
+
+ ch->ch_bd->bd_ops->assert_modem_signals(ch);
+
+ spin_unlock_irqrestore(&ch->ch_lock, flags);
+
+ return 0;
+}
+
+
+/*
+ * dgnc_tty_digigeta()
+ *
+ * Ioctl to get the information for ditty.
+ *
+ *
+ *
+ */
+static int dgnc_tty_digigeta(struct tty_struct *tty, struct digi_t __user *retinfo)
+{
+ struct channel_t *ch;
+ struct un_t *un;
+ struct digi_t tmp;
+ unsigned long flags;
+
+ if (!retinfo)
+ return -EFAULT;
+
+ if (!tty || tty->magic != TTY_MAGIC)
+ return -EFAULT;
+
+ un = tty->driver_data;
+ if (!un || un->magic != DGNC_UNIT_MAGIC)
+ return -EFAULT;
+
+ ch = un->un_ch;
+ if (!ch || ch->magic != DGNC_CHANNEL_MAGIC)
+ return -EFAULT;
+
+ memset(&tmp, 0, sizeof(tmp));
+
+ spin_lock_irqsave(&ch->ch_lock, flags);
+ memcpy(&tmp, &ch->ch_digi, sizeof(tmp));
+ spin_unlock_irqrestore(&ch->ch_lock, flags);
+
+ if (copy_to_user(retinfo, &tmp, sizeof(*retinfo)))
+ return -EFAULT;
+
+ return 0;
+}
+
+
+/*
+ * dgnc_tty_digiseta()
+ *
+ * Ioctl to set the information for ditty.
+ *
+ *
+ *
+ */
+static int dgnc_tty_digiseta(struct tty_struct *tty, struct digi_t __user *new_info)
+{
+ struct dgnc_board *bd;
+ struct channel_t *ch;
+ struct un_t *un;
+ struct digi_t new_digi;
+ unsigned long flags;
+
+ if (!tty || tty->magic != TTY_MAGIC)
+ return -EFAULT;
+
+ un = tty->driver_data;
+ if (!un || un->magic != DGNC_UNIT_MAGIC)
+ return -EFAULT;
+
+ ch = un->un_ch;
+ if (!ch || ch->magic != DGNC_CHANNEL_MAGIC)
+ return -EFAULT;
+
+ bd = ch->ch_bd;
+ if (!bd || bd->magic != DGNC_BOARD_MAGIC)
+ return -EFAULT;
+
+ if (copy_from_user(&new_digi, new_info, sizeof(new_digi)))
+ return -EFAULT;
+
+ spin_lock_irqsave(&ch->ch_lock, flags);
+
+ /*
+ * Handle transistions to and from RTS Toggle.
+ */
+ if (!(ch->ch_digi.digi_flags & DIGI_RTS_TOGGLE) && (new_digi.digi_flags & DIGI_RTS_TOGGLE))
+ ch->ch_mostat &= ~(UART_MCR_RTS);
+ if ((ch->ch_digi.digi_flags & DIGI_RTS_TOGGLE) && !(new_digi.digi_flags & DIGI_RTS_TOGGLE))
+ ch->ch_mostat |= (UART_MCR_RTS);
+
+ /*
+ * Handle transistions to and from DTR Toggle.
+ */
+ if (!(ch->ch_digi.digi_flags & DIGI_DTR_TOGGLE) && (new_digi.digi_flags & DIGI_DTR_TOGGLE))
+ ch->ch_mostat &= ~(UART_MCR_DTR);
+ if ((ch->ch_digi.digi_flags & DIGI_DTR_TOGGLE) && !(new_digi.digi_flags & DIGI_DTR_TOGGLE))
+ ch->ch_mostat |= (UART_MCR_DTR);
+
+ memcpy(&ch->ch_digi, &new_digi, sizeof(new_digi));
+
+ if (ch->ch_digi.digi_maxcps < 1)
+ ch->ch_digi.digi_maxcps = 1;
+
+ if (ch->ch_digi.digi_maxcps > 10000)
+ ch->ch_digi.digi_maxcps = 10000;
+
+ if (ch->ch_digi.digi_bufsize < 10)
+ ch->ch_digi.digi_bufsize = 10;
+
+ if (ch->ch_digi.digi_maxchar < 1)
+ ch->ch_digi.digi_maxchar = 1;
+
+ if (ch->ch_digi.digi_maxchar > ch->ch_digi.digi_bufsize)
+ ch->ch_digi.digi_maxchar = ch->ch_digi.digi_bufsize;
+
+ if (ch->ch_digi.digi_onlen > DIGI_PLEN)
+ ch->ch_digi.digi_onlen = DIGI_PLEN;
+
+ if (ch->ch_digi.digi_offlen > DIGI_PLEN)
+ ch->ch_digi.digi_offlen = DIGI_PLEN;
+
+ ch->ch_bd->bd_ops->param(tty);
+
+ spin_unlock_irqrestore(&ch->ch_lock, flags);
+
+ return 0;
+}
+
+
+/*
+ * dgnc_set_termios()
+ */
+static void dgnc_tty_set_termios(struct tty_struct *tty, struct ktermios *old_termios)
+{
+ struct dgnc_board *bd;
+ struct channel_t *ch;
+ struct un_t *un;
+ unsigned long flags;
+
+ if (!tty || tty->magic != TTY_MAGIC)
+ return;
+
+ un = tty->driver_data;
+ if (!un || un->magic != DGNC_UNIT_MAGIC)
+ return;
+
+ ch = un->un_ch;
+ if (!ch || ch->magic != DGNC_CHANNEL_MAGIC)
+ return;
+
+ bd = ch->ch_bd;
+ if (!bd || bd->magic != DGNC_BOARD_MAGIC)
+ return;
+
+ spin_lock_irqsave(&ch->ch_lock, flags);
+
+ ch->ch_c_cflag = tty->termios.c_cflag;
+ ch->ch_c_iflag = tty->termios.c_iflag;
+ ch->ch_c_oflag = tty->termios.c_oflag;
+ ch->ch_c_lflag = tty->termios.c_lflag;
+ ch->ch_startc = tty->termios.c_cc[VSTART];
+ ch->ch_stopc = tty->termios.c_cc[VSTOP];
+
+ ch->ch_bd->bd_ops->param(tty);
+ dgnc_carrier(ch);
+
+ spin_unlock_irqrestore(&ch->ch_lock, flags);
+}
+
+
+static void dgnc_tty_throttle(struct tty_struct *tty)
+{
+ struct channel_t *ch;
+ struct un_t *un;
+ unsigned long flags;
+
+ if (!tty || tty->magic != TTY_MAGIC)
+ return;
+
+ un = tty->driver_data;
+ if (!un || un->magic != DGNC_UNIT_MAGIC)
+ return;
+
+ ch = un->un_ch;
+ if (!ch || ch->magic != DGNC_CHANNEL_MAGIC)
+ return;
+
+ spin_lock_irqsave(&ch->ch_lock, flags);
+
+ ch->ch_flags |= (CH_FORCED_STOPI);
+
+ spin_unlock_irqrestore(&ch->ch_lock, flags);
+}
+
+
+static void dgnc_tty_unthrottle(struct tty_struct *tty)
+{
+ struct channel_t *ch;
+ struct un_t *un;
+ unsigned long flags;
+
+ if (!tty || tty->magic != TTY_MAGIC)
+ return;
+
+ un = tty->driver_data;
+ if (!un || un->magic != DGNC_UNIT_MAGIC)
+ return;
+
+ ch = un->un_ch;
+ if (!ch || ch->magic != DGNC_CHANNEL_MAGIC)
+ return;
+
+ spin_lock_irqsave(&ch->ch_lock, flags);
+
+ ch->ch_flags &= ~(CH_FORCED_STOPI);
+
+ spin_unlock_irqrestore(&ch->ch_lock, flags);
+}
+
+
+static void dgnc_tty_start(struct tty_struct *tty)
+{
+ struct dgnc_board *bd;
+ struct channel_t *ch;
+ struct un_t *un;
+ unsigned long flags;
+
+ if (!tty || tty->magic != TTY_MAGIC)
+ return;
+
+ un = tty->driver_data;
+ if (!un || un->magic != DGNC_UNIT_MAGIC)
+ return;
+
+ ch = un->un_ch;
+ if (!ch || ch->magic != DGNC_CHANNEL_MAGIC)
+ return;
+
+ bd = ch->ch_bd;
+ if (!bd || bd->magic != DGNC_BOARD_MAGIC)
+ return;
+
+ spin_lock_irqsave(&ch->ch_lock, flags);
+
+ ch->ch_flags &= ~(CH_FORCED_STOP);
+
+ spin_unlock_irqrestore(&ch->ch_lock, flags);
+}
+
+
+static void dgnc_tty_stop(struct tty_struct *tty)
+{
+ struct dgnc_board *bd;
+ struct channel_t *ch;
+ struct un_t *un;
+ unsigned long flags;
+
+ if (!tty || tty->magic != TTY_MAGIC)
+ return;
+
+ un = tty->driver_data;
+ if (!un || un->magic != DGNC_UNIT_MAGIC)
+ return;
+
+ ch = un->un_ch;
+ if (!ch || ch->magic != DGNC_CHANNEL_MAGIC)
+ return;
+
+ bd = ch->ch_bd;
+ if (!bd || bd->magic != DGNC_BOARD_MAGIC)
+ return;
+
+ spin_lock_irqsave(&ch->ch_lock, flags);
+
+ ch->ch_flags |= (CH_FORCED_STOP);
+
+ spin_unlock_irqrestore(&ch->ch_lock, flags);
+}
+
+
+/*
+ * dgnc_tty_flush_chars()
+ *
+ * Flush the cook buffer
+ *
+ * Note to self, and any other poor souls who venture here:
+ *
+ * flush in this case DOES NOT mean dispose of the data.
+ * instead, it means "stop buffering and send it if you
+ * haven't already." Just guess how I figured that out... SRW 2-Jun-98
+ *
+ * It is also always called in interrupt context - JAR 8-Sept-99
+ */
+static void dgnc_tty_flush_chars(struct tty_struct *tty)
+{
+ struct dgnc_board *bd;
+ struct channel_t *ch;
+ struct un_t *un;
+ unsigned long flags;
+
+ if (!tty || tty->magic != TTY_MAGIC)
+ return;
+
+ un = tty->driver_data;
+ if (!un || un->magic != DGNC_UNIT_MAGIC)
+ return;
+
+ ch = un->un_ch;
+ if (!ch || ch->magic != DGNC_CHANNEL_MAGIC)
+ return;
+
+ bd = ch->ch_bd;
+ if (!bd || bd->magic != DGNC_BOARD_MAGIC)
+ return;
+
+ spin_lock_irqsave(&ch->ch_lock, flags);
+
+ /* Do something maybe here */
+
+ spin_unlock_irqrestore(&ch->ch_lock, flags);
+}
+
+
+
+/*
+ * dgnc_tty_flush_buffer()
+ *
+ * Flush Tx buffer (make in == out)
+ */
+static void dgnc_tty_flush_buffer(struct tty_struct *tty)
+{
+ struct channel_t *ch;
+ struct un_t *un;
+ unsigned long flags;
+
+ if (!tty || tty->magic != TTY_MAGIC)
+ return;
+
+ un = tty->driver_data;
+ if (!un || un->magic != DGNC_UNIT_MAGIC)
+ return;
+
+ ch = un->un_ch;
+ if (!ch || ch->magic != DGNC_CHANNEL_MAGIC)
+ return;
+
+ spin_lock_irqsave(&ch->ch_lock, flags);
+
+ ch->ch_flags &= ~CH_STOP;
+
+ /* Flush our write queue */
+ ch->ch_w_head = ch->ch_w_tail;
+
+ /* Flush UARTs transmit FIFO */
+ ch->ch_bd->bd_ops->flush_uart_write(ch);
+
+ if (ch->ch_tun.un_flags & (UN_LOW|UN_EMPTY)) {
+ ch->ch_tun.un_flags &= ~(UN_LOW|UN_EMPTY);
+ wake_up_interruptible(&ch->ch_tun.un_flags_wait);
+ }
+ if (ch->ch_pun.un_flags & (UN_LOW|UN_EMPTY)) {
+ ch->ch_pun.un_flags &= ~(UN_LOW|UN_EMPTY);
+ wake_up_interruptible(&ch->ch_pun.un_flags_wait);
+ }
+
+ spin_unlock_irqrestore(&ch->ch_lock, flags);
+}
+
+
+
+/*****************************************************************************
+ *
+ * The IOCTL function and all of its helpers
+ *
+ *****************************************************************************/
+
+/*
+ * dgnc_tty_ioctl()
+ *
+ * The usual assortment of ioctl's
+ */
+static int dgnc_tty_ioctl(struct tty_struct *tty, unsigned int cmd,
+ unsigned long arg)
+{
+ struct dgnc_board *bd;
+ struct channel_t *ch;
+ struct un_t *un;
+ int rc;
+ unsigned long flags;
+ void __user *uarg = (void __user *) arg;
+
+ if (!tty || tty->magic != TTY_MAGIC)
+ return -ENODEV;
+
+ un = tty->driver_data;
+ if (!un || un->magic != DGNC_UNIT_MAGIC)
+ return -ENODEV;
+
+ ch = un->un_ch;
+ if (!ch || ch->magic != DGNC_CHANNEL_MAGIC)
+ return -ENODEV;
+
+ bd = ch->ch_bd;
+ if (!bd || bd->magic != DGNC_BOARD_MAGIC)
+ return -ENODEV;
+
+ spin_lock_irqsave(&ch->ch_lock, flags);
+
+ if (un->un_open_count <= 0) {
+ spin_unlock_irqrestore(&ch->ch_lock, flags);
+ return -EIO;
+ }
+
+ switch (cmd) {
+
+ /* Here are all the standard ioctl's that we MUST implement */
+
+ case TCSBRK:
+ /*
+ * TCSBRK is SVID version: non-zero arg --> no break
+ * this behaviour is exploited by tcdrain().
+ *
+ * According to POSIX.1 spec (7.2.2.1.2) breaks should be
+ * between 0.25 and 0.5 seconds so we'll ask for something
+ * in the middle: 0.375 seconds.
+ */
+ rc = tty_check_change(tty);
+ spin_unlock_irqrestore(&ch->ch_lock, flags);
+ if (rc)
+ return rc;
+
+ rc = ch->ch_bd->bd_ops->drain(tty, 0);
+
+ if (rc)
+ return -EINTR;
+
+ spin_lock_irqsave(&ch->ch_lock, flags);
+
+ if (((cmd == TCSBRK) && (!arg)) || (cmd == TCSBRKP))
+ ch->ch_bd->bd_ops->send_break(ch, 250);
+
+ spin_unlock_irqrestore(&ch->ch_lock, flags);
+
+ return 0;
+
+
+ case TCSBRKP:
+ /* support for POSIX tcsendbreak()
+ * According to POSIX.1 spec (7.2.2.1.2) breaks should be
+ * between 0.25 and 0.5 seconds so we'll ask for something
+ * in the middle: 0.375 seconds.
+ */
+ rc = tty_check_change(tty);
+ spin_unlock_irqrestore(&ch->ch_lock, flags);
+ if (rc)
+ return rc;
+
+ rc = ch->ch_bd->bd_ops->drain(tty, 0);
+ if (rc)
+ return -EINTR;
+
+ spin_lock_irqsave(&ch->ch_lock, flags);
+
+ ch->ch_bd->bd_ops->send_break(ch, 250);
+
+ spin_unlock_irqrestore(&ch->ch_lock, flags);
+
+ return 0;
+
+ case TIOCSBRK:
+ rc = tty_check_change(tty);
+ spin_unlock_irqrestore(&ch->ch_lock, flags);
+ if (rc)
+ return rc;
+
+ rc = ch->ch_bd->bd_ops->drain(tty, 0);
+ if (rc)
+ return -EINTR;
+
+ spin_lock_irqsave(&ch->ch_lock, flags);
+
+ ch->ch_bd->bd_ops->send_break(ch, 250);
+
+ spin_unlock_irqrestore(&ch->ch_lock, flags);
+
+ return 0;
+
+ case TIOCCBRK:
+ /* Do Nothing */
+ spin_unlock_irqrestore(&ch->ch_lock, flags);
+ return 0;
+
+ case TIOCGSOFTCAR:
+
+ spin_unlock_irqrestore(&ch->ch_lock, flags);
+
+ rc = put_user(C_CLOCAL(tty) ? 1 : 0, (unsigned long __user *) arg);
+ return rc;
+
+ case TIOCSSOFTCAR:
+
+ spin_unlock_irqrestore(&ch->ch_lock, flags);
+ rc = get_user(arg, (unsigned long __user *) arg);
+ if (rc)
+ return rc;
+
+ spin_lock_irqsave(&ch->ch_lock, flags);
+ tty->termios.c_cflag = ((tty->termios.c_cflag & ~CLOCAL) | (arg ? CLOCAL : 0));
+ ch->ch_bd->bd_ops->param(tty);
+ spin_unlock_irqrestore(&ch->ch_lock, flags);
+
+ return 0;
+
+ case TIOCMGET:
+ spin_unlock_irqrestore(&ch->ch_lock, flags);
+ return dgnc_get_modem_info(ch, uarg);
+
+ case TIOCMBIS:
+ case TIOCMBIC:
+ case TIOCMSET:
+ spin_unlock_irqrestore(&ch->ch_lock, flags);
+ return dgnc_set_modem_info(tty, cmd, uarg);
+
+ /*
+ * Here are any additional ioctl's that we want to implement
+ */
+
+ case TCFLSH:
+ /*
+ * The linux tty driver doesn't have a flush
+ * input routine for the driver, assuming all backed
+ * up data is in the line disc. buffers. However,
+ * we all know that's not the case. Here, we
+ * act on the ioctl, but then lie and say we didn't
+ * so the line discipline will process the flush
+ * also.
+ */
+ rc = tty_check_change(tty);
+ if (rc) {
+ spin_unlock_irqrestore(&ch->ch_lock, flags);
+ return rc;
+ }
+
+ if ((arg == TCIFLUSH) || (arg == TCIOFLUSH)) {
+ ch->ch_r_head = ch->ch_r_tail;
+ ch->ch_bd->bd_ops->flush_uart_read(ch);
+ /* Force queue flow control to be released, if needed */
+ dgnc_check_queue_flow_control(ch);
+ }
+
+ if ((arg == TCOFLUSH) || (arg == TCIOFLUSH)) {
+ if (!(un->un_type == DGNC_PRINT)) {
+ ch->ch_w_head = ch->ch_w_tail;
+ ch->ch_bd->bd_ops->flush_uart_write(ch);
+
+ if (ch->ch_tun.un_flags & (UN_LOW|UN_EMPTY)) {
+ ch->ch_tun.un_flags &= ~(UN_LOW|UN_EMPTY);
+ wake_up_interruptible(&ch->ch_tun.un_flags_wait);
+ }
+
+ if (ch->ch_pun.un_flags & (UN_LOW|UN_EMPTY)) {
+ ch->ch_pun.un_flags &= ~(UN_LOW|UN_EMPTY);
+ wake_up_interruptible(&ch->ch_pun.un_flags_wait);
+ }
+
+ }
+ }
+
+ /* pretend we didn't recognize this IOCTL */
+ spin_unlock_irqrestore(&ch->ch_lock, flags);
+ return -ENOIOCTLCMD;
+ case TCSETSF:
+ case TCSETSW:
+ /*
+ * The linux tty driver doesn't have a flush
+ * input routine for the driver, assuming all backed
+ * up data is in the line disc. buffers. However,
+ * we all know that's not the case. Here, we
+ * act on the ioctl, but then lie and say we didn't
+ * so the line discipline will process the flush
+ * also.
+ */
+ if (cmd == TCSETSF) {
+ /* flush rx */
+ ch->ch_flags &= ~CH_STOP;
+ ch->ch_r_head = ch->ch_r_tail;
+ ch->ch_bd->bd_ops->flush_uart_read(ch);
+ /* Force queue flow control to be released, if needed */
+ dgnc_check_queue_flow_control(ch);
+ }
+
+ /* now wait for all the output to drain */
+ spin_unlock_irqrestore(&ch->ch_lock, flags);
+ rc = ch->ch_bd->bd_ops->drain(tty, 0);
+ if (rc)
+ return -EINTR;
+
+ /* pretend we didn't recognize this */
+ return -ENOIOCTLCMD;
+
+ case TCSETAW:
+
+ spin_unlock_irqrestore(&ch->ch_lock, flags);
+ rc = ch->ch_bd->bd_ops->drain(tty, 0);
+ if (rc)
+ return -EINTR;
+
+ /* pretend we didn't recognize this */
+ return -ENOIOCTLCMD;
+
+ case TCXONC:
+ spin_unlock_irqrestore(&ch->ch_lock, flags);
+ /* Make the ld do it */
+ return -ENOIOCTLCMD;
+
+ case DIGI_GETA:
+ /* get information for ditty */
+ spin_unlock_irqrestore(&ch->ch_lock, flags);
+ return dgnc_tty_digigeta(tty, uarg);
+
+ case DIGI_SETAW:
+ case DIGI_SETAF:
+
+ /* set information for ditty */
+ if (cmd == (DIGI_SETAW)) {
+
+ spin_unlock_irqrestore(&ch->ch_lock, flags);
+ rc = ch->ch_bd->bd_ops->drain(tty, 0);
+
+ if (rc)
+ return -EINTR;
+
+ spin_lock_irqsave(&ch->ch_lock, flags);
+ } else {
+ tty_ldisc_flush(tty);
+ }
+ /* fall thru */
+
+ case DIGI_SETA:
+ spin_unlock_irqrestore(&ch->ch_lock, flags);
+ return dgnc_tty_digiseta(tty, uarg);
+
+ case DIGI_LOOPBACK:
+ {
+ uint loopback = 0;
+ /* Let go of locks when accessing user space, could sleep */
+ spin_unlock_irqrestore(&ch->ch_lock, flags);
+ rc = get_user(loopback, (unsigned int __user *) arg);
+ if (rc)
+ return rc;
+ spin_lock_irqsave(&ch->ch_lock, flags);
+
+ /* Enable/disable internal loopback for this port */
+ if (loopback)
+ ch->ch_flags |= CH_LOOPBACK;
+ else
+ ch->ch_flags &= ~(CH_LOOPBACK);
+
+ ch->ch_bd->bd_ops->param(tty);
+ spin_unlock_irqrestore(&ch->ch_lock, flags);
+ return 0;
+ }
+
+ case DIGI_GETCUSTOMBAUD:
+ spin_unlock_irqrestore(&ch->ch_lock, flags);
+ rc = put_user(ch->ch_custom_speed, (unsigned int __user *) arg);
+ return rc;
+
+ case DIGI_SETCUSTOMBAUD:
+ {
+ int new_rate;
+ /* Let go of locks when accessing user space, could sleep */
+ spin_unlock_irqrestore(&ch->ch_lock, flags);
+ rc = get_user(new_rate, (int __user *) arg);
+ if (rc)
+ return rc;
+ spin_lock_irqsave(&ch->ch_lock, flags);
+ dgnc_set_custom_speed(ch, new_rate);
+ ch->ch_bd->bd_ops->param(tty);
+ spin_unlock_irqrestore(&ch->ch_lock, flags);
+ return 0;
+ }
+
+ /*
+ * This ioctl allows insertion of a character into the front
+ * of any pending data to be transmitted.
+ *
+ * This ioctl is to satify the "Send Character Immediate"
+ * call that the RealPort protocol spec requires.
+ */
+ case DIGI_REALPORT_SENDIMMEDIATE:
+ {
+ unsigned char c;
+
+ spin_unlock_irqrestore(&ch->ch_lock, flags);
+ rc = get_user(c, (unsigned char __user *) arg);
+ if (rc)
+ return rc;
+ spin_lock_irqsave(&ch->ch_lock, flags);
+ ch->ch_bd->bd_ops->send_immediate_char(ch, c);
+ spin_unlock_irqrestore(&ch->ch_lock, flags);
+ return 0;
+ }
+
+ /*
+ * This ioctl returns all the current counts for the port.
+ *
+ * This ioctl is to satify the "Line Error Counters"
+ * call that the RealPort protocol spec requires.
+ */
+ case DIGI_REALPORT_GETCOUNTERS:
+ {
+ struct digi_getcounter buf;
+
+ buf.norun = ch->ch_err_overrun;
+ buf.noflow = 0; /* The driver doesn't keep this stat */
+ buf.nframe = ch->ch_err_frame;
+ buf.nparity = ch->ch_err_parity;
+ buf.nbreak = ch->ch_err_break;
+ buf.rbytes = ch->ch_rxcount;
+ buf.tbytes = ch->ch_txcount;
+
+ spin_unlock_irqrestore(&ch->ch_lock, flags);
+
+ if (copy_to_user(uarg, &buf, sizeof(buf)))
+ return -EFAULT;
+
+ return 0;
+ }
+
+ /*
+ * This ioctl returns all current events.
+ *
+ * This ioctl is to satify the "Event Reporting"
+ * call that the RealPort protocol spec requires.
+ */
+ case DIGI_REALPORT_GETEVENTS:
+ {
+ unsigned int events = 0;
+
+ /* NOTE: MORE EVENTS NEEDS TO BE ADDED HERE */
+ if (ch->ch_flags & CH_BREAK_SENDING)
+ events |= EV_TXB;
+ if ((ch->ch_flags & CH_STOP) || (ch->ch_flags & CH_FORCED_STOP))
+ events |= (EV_OPU | EV_OPS);
+
+ if ((ch->ch_flags & CH_STOPI) || (ch->ch_flags & CH_FORCED_STOPI))
+ events |= (EV_IPU | EV_IPS);
+
+ spin_unlock_irqrestore(&ch->ch_lock, flags);
+ rc = put_user(events, (unsigned int __user *) arg);
+ return rc;
+ }
+
+ /*
+ * This ioctl returns TOUT and TIN counters based
+ * upon the values passed in by the RealPort Server.
+ * It also passes back whether the UART Transmitter is
+ * empty as well.
+ */
+ case DIGI_REALPORT_GETBUFFERS:
+ {
+ struct digi_getbuffer buf;
+ int tdist;
+ int count;
+
+ spin_unlock_irqrestore(&ch->ch_lock, flags);
+
+ /*
+ * Get data from user first.
+ */
+ if (copy_from_user(&buf, uarg, sizeof(buf)))
+ return -EFAULT;
+
+ spin_lock_irqsave(&ch->ch_lock, flags);
+
+ /*
+ * Figure out how much data is in our RX and TX queues.
+ */
+ buf.rxbuf = (ch->ch_r_head - ch->ch_r_tail) & RQUEUEMASK;
+ buf.txbuf = (ch->ch_w_head - ch->ch_w_tail) & WQUEUEMASK;
+
+ /*
+ * Is the UART empty? Add that value to whats in our TX queue.
+ */
+ count = buf.txbuf + ch->ch_bd->bd_ops->get_uart_bytes_left(ch);
+
+ /*
+ * Figure out how much data the RealPort Server believes should
+ * be in our TX queue.
+ */
+ tdist = (buf.tIn - buf.tOut) & 0xffff;
+
+ /*
+ * If we have more data than the RealPort Server believes we
+ * should have, reduce our count to its amount.
+ *
+ * This count difference CAN happen because the Linux LD can
+ * insert more characters into our queue for OPOST processing
+ * that the RealPort Server doesn't know about.
+ */
+ if (buf.txbuf > tdist)
+ buf.txbuf = tdist;
+
+ /*
+ * Report whether our queue and UART TX are completely empty.
+ */
+ if (count)
+ buf.txdone = 0;
+ else
+ buf.txdone = 1;
+
+ spin_unlock_irqrestore(&ch->ch_lock, flags);
+
+ if (copy_to_user(uarg, &buf, sizeof(buf)))
+ return -EFAULT;
+
+ return 0;
+ }
+ default:
+ spin_unlock_irqrestore(&ch->ch_lock, flags);
+
+ return -ENOIOCTLCMD;
+ }
+}
diff --git a/drivers/staging/dgnc/dgnc_tty.h b/drivers/staging/dgnc/dgnc_tty.h
new file mode 100644
index 000000000..21d3369b8
--- /dev/null
+++ b/drivers/staging/dgnc/dgnc_tty.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2003 Digi International (www.digi.com)
+ * Scott H Kilau <Scott_Kilau at digi dot 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, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED; without even the
+ * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ * PURPOSE. See the GNU General Public License for more details.
+ */
+
+#ifndef __DGNC_TTY_H
+#define __DGNC_TTY_H
+
+#include "dgnc_driver.h"
+
+int dgnc_tty_register(struct dgnc_board *brd);
+
+int dgnc_tty_preinit(void);
+int dgnc_tty_init(struct dgnc_board *);
+
+void dgnc_tty_post_uninit(void);
+void dgnc_tty_uninit(struct dgnc_board *);
+
+void dgnc_input(struct channel_t *ch);
+void dgnc_carrier(struct channel_t *ch);
+void dgnc_wakeup_writes(struct channel_t *ch);
+void dgnc_check_queue_flow_control(struct channel_t *ch);
+
+#endif
diff --git a/drivers/staging/dgnc/dgnc_utils.c b/drivers/staging/dgnc/dgnc_utils.c
new file mode 100644
index 000000000..f76de8290
--- /dev/null
+++ b/drivers/staging/dgnc/dgnc_utils.c
@@ -0,0 +1,18 @@
+#include <linux/tty.h>
+#include <linux/sched.h>
+#include "dgnc_utils.h"
+#include "digi.h"
+
+/*
+ * dgnc_ms_sleep()
+ *
+ * Put the driver to sleep for x ms's
+ *
+ * Returns 0 if timed out, !0 (showing signal) if interrupted by a signal.
+ */
+int dgnc_ms_sleep(ulong ms)
+{
+ __set_current_state(TASK_INTERRUPTIBLE);
+ schedule_timeout((ms * HZ) / 1000);
+ return signal_pending(current);
+}
diff --git a/drivers/staging/dgnc/dgnc_utils.h b/drivers/staging/dgnc/dgnc_utils.h
new file mode 100644
index 000000000..1164c3a09
--- /dev/null
+++ b/drivers/staging/dgnc/dgnc_utils.h
@@ -0,0 +1,6 @@
+#ifndef __DGNC_UTILS_H
+#define __DGNC_UTILS_H
+
+int dgnc_ms_sleep(ulong ms);
+
+#endif
diff --git a/drivers/staging/dgnc/digi.h b/drivers/staging/dgnc/digi.h
new file mode 100644
index 000000000..d637a7802
--- /dev/null
+++ b/drivers/staging/dgnc/digi.h
@@ -0,0 +1,179 @@
+/*
+ * Copyright 2003 Digi International (www.digi.com)
+ * Scott H Kilau <Scott_Kilau at digi dot 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, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED; without even the
+ * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ * PURPOSE. See the GNU General Public License for more details.
+ */
+
+#ifndef __DIGI_H
+#define __DIGI_H
+
+#ifndef TIOCM_LE
+#define TIOCM_LE 0x01 /* line enable */
+#define TIOCM_DTR 0x02 /* data terminal ready */
+#define TIOCM_RTS 0x04 /* request to send */
+#define TIOCM_ST 0x08 /* secondary transmit */
+#define TIOCM_SR 0x10 /* secondary receive */
+#define TIOCM_CTS 0x20 /* clear to send */
+#define TIOCM_CAR 0x40 /* carrier detect */
+#define TIOCM_RNG 0x80 /* ring indicator */
+#define TIOCM_DSR 0x100 /* data set ready */
+#define TIOCM_RI TIOCM_RNG /* ring (alternate) */
+#define TIOCM_CD TIOCM_CAR /* carrier detect (alt) */
+#endif
+
+#if !defined(TIOCMSET)
+#define TIOCMSET (('d'<<8) | 252) /* set modem ctrl state */
+#define TIOCMGET (('d'<<8) | 253) /* set modem ctrl state */
+#endif
+
+#if !defined(TIOCMBIC)
+#define TIOCMBIC (('d'<<8) | 254) /* set modem ctrl state */
+#define TIOCMBIS (('d'<<8) | 255) /* set modem ctrl state */
+#endif
+
+#define DIGI_GETA (('e'<<8) | 94) /* Read params */
+#define DIGI_SETA (('e'<<8) | 95) /* Set params */
+#define DIGI_SETAW (('e'<<8) | 96) /* Drain & set params */
+#define DIGI_SETAF (('e'<<8) | 97) /* Drain, flush & set params */
+#define DIGI_GET_NI_INFO (('d'<<8) | 250) /* Non-intelligent state info */
+#define DIGI_LOOPBACK (('d'<<8) | 252) /*
+ * Enable/disable UART
+ * internal loopback
+ */
+#define DIGI_FAST 0x0002 /* Fast baud rates */
+#define RTSPACE 0x0004 /* RTS input flow control */
+#define CTSPACE 0x0008 /* CTS output flow control */
+#define DIGI_COOK 0x0080 /* Cooked processing done in FEP */
+#define DIGI_FORCEDCD 0x0100 /* Force carrier */
+#define DIGI_ALTPIN 0x0200 /* Alternate RJ-45 pin config */
+#define DIGI_PRINTER 0x0800 /* Hold port open for flow cntrl*/
+#define DIGI_DTR_TOGGLE 0x2000 /* Support DTR Toggle */
+#define DIGI_RTS_TOGGLE 0x8000 /* Support RTS Toggle */
+#define DIGI_PLEN 28 /* String length */
+#define DIGI_TSIZ 10 /* Terminal string len */
+
+/************************************************************************
+ * Structure used with ioctl commands for DIGI parameters.
+ ************************************************************************/
+struct digi_t {
+ unsigned short digi_flags; /* Flags (see above) */
+ unsigned short digi_maxcps; /* Max printer CPS */
+ unsigned short digi_maxchar; /* Max chars in print queue */
+ unsigned short digi_bufsize; /* Buffer size */
+ unsigned char digi_onlen; /* Length of ON string */
+ unsigned char digi_offlen; /* Length of OFF string */
+ char digi_onstr[DIGI_PLEN]; /* Printer on string */
+ char digi_offstr[DIGI_PLEN]; /* Printer off string */
+ char digi_term[DIGI_TSIZ]; /* terminal string */
+};
+
+/************************************************************************
+ * Structure to get driver status information
+ ************************************************************************/
+struct digi_dinfo {
+ unsigned int dinfo_nboards; /* # boards configured */
+ char dinfo_reserved[12]; /* for future expansion */
+ char dinfo_version[16]; /* driver version */
+};
+
+#define DIGI_GETDD (('d'<<8) | 248) /* get driver info */
+
+/************************************************************************
+ * Structure used with ioctl commands for per-board information
+ *
+ * physsize and memsize differ when board has "windowed" memory
+ ************************************************************************/
+struct digi_info {
+ unsigned int info_bdnum; /* Board number (0 based) */
+ unsigned int info_ioport; /* io port address */
+ unsigned int info_physaddr; /* memory address */
+ unsigned int info_physsize; /* Size of host mem window */
+ unsigned int info_memsize; /* Amount of dual-port mem */
+ /* on board */
+ unsigned short info_bdtype; /* Board type */
+ unsigned short info_nports; /* number of ports */
+ char info_bdstate; /* board state */
+ char info_reserved[7]; /* for future expansion */
+};
+
+#define DIGI_GETBD (('d'<<8) | 249) /* get board info */
+
+struct digi_getbuffer /* Struct for holding buffer use counts */
+{
+ unsigned long tIn;
+ unsigned long tOut;
+ unsigned long rxbuf;
+ unsigned long txbuf;
+ unsigned long txdone;
+};
+
+struct digi_getcounter {
+ unsigned long norun; /* number of UART overrun errors */
+ unsigned long noflow; /* number of buffer overflow errors */
+ unsigned long nframe; /* number of framing errors */
+ unsigned long nparity; /* number of parity errors */
+ unsigned long nbreak; /* number of breaks received */
+ unsigned long rbytes; /* number of received bytes */
+ unsigned long tbytes; /* number of bytes transmitted fully */
+};
+
+/* Board State Definitions */
+#define BD_RUNNING 0x0
+#define BD_NOFEP 0x5
+
+#define DIGI_SETCUSTOMBAUD _IOW('e', 106, int) /* Set integer baud rate */
+#define DIGI_GETCUSTOMBAUD _IOR('e', 107, int) /* Get integer baud rate */
+
+#define DIGI_REALPORT_GETBUFFERS (('e'<<8) | 108)
+#define DIGI_REALPORT_SENDIMMEDIATE (('e'<<8) | 109)
+#define DIGI_REALPORT_GETCOUNTERS (('e'<<8) | 110)
+#define DIGI_REALPORT_GETEVENTS (('e'<<8) | 111)
+
+#define EV_OPU 0x0001 /* !<Output paused by client */
+#define EV_OPS 0x0002 /* !<Output paused by reqular sw flowctrl */
+#define EV_IPU 0x0010 /* !<Input paused unconditionally by user */
+#define EV_IPS 0x0020 /* !<Input paused by high/low water marks */
+#define EV_TXB 0x0040 /* !<Transmit break pending */
+
+/*
+ * This structure holds data needed for the intelligent <--> nonintelligent
+ * DPA translation
+ */
+struct ni_info {
+ int board;
+ int channel;
+ int dtr;
+ int rts;
+ int cts;
+ int dsr;
+ int ri;
+ int dcd;
+ int curtx;
+ int currx;
+ unsigned short iflag;
+ unsigned short oflag;
+ unsigned short cflag;
+ unsigned short lflag;
+ unsigned int mstat;
+ unsigned char hflow;
+ unsigned char xmit_stopped;
+ unsigned char recv_stopped;
+ unsigned int baud;
+};
+
+#define T_CLASSIC 0002
+#define T_PCIBUS 0400
+#define T_NEO_EXPRESS 0001
+#define T_NEO 0000
+
+#define TTY_FLIPBUF_SIZE 512
+#endif /* DIGI_H */
diff --git a/drivers/staging/emxx_udc/Kconfig b/drivers/staging/emxx_udc/Kconfig
new file mode 100644
index 000000000..cc3402020
--- /dev/null
+++ b/drivers/staging/emxx_udc/Kconfig
@@ -0,0 +1,10 @@
+config USB_EMXX
+ bool "EMXX USB Function Device Controller"
+ depends on USB_GADGET && (ARCH_SHMOBILE || (ARM && COMPILE_TEST))
+ help
+ The Emma Mobile series of SoCs from Renesas Electronics and
+ former NEC Electronics include USB Function hardware.
+
+ Say "y" to link the driver statically, or "m" to build a
+ dynamically linked module called "emxx_udc" and force all
+ gadget drivers to also be dynamically linked.
diff --git a/drivers/staging/emxx_udc/Makefile b/drivers/staging/emxx_udc/Makefile
new file mode 100644
index 000000000..6352724c0
--- /dev/null
+++ b/drivers/staging/emxx_udc/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_USB_EMXX) := emxx_udc.o
diff --git a/drivers/staging/emxx_udc/TODO b/drivers/staging/emxx_udc/TODO
new file mode 100644
index 000000000..1319379be
--- /dev/null
+++ b/drivers/staging/emxx_udc/TODO
@@ -0,0 +1,4 @@
+* add clock framework support (platform device with CCF needs special care)
+* break out board-specific VBUS GPIO to work with multiplatform
+* DT bindings
+* move driver into drivers/usb/gadget/
diff --git a/drivers/staging/emxx_udc/emxx_udc.c b/drivers/staging/emxx_udc/emxx_udc.c
new file mode 100644
index 000000000..fbf82bc73
--- /dev/null
+++ b/drivers/staging/emxx_udc/emxx_udc.c
@@ -0,0 +1,3475 @@
+/*
+ * drivers/usb/gadget/emxx_udc.c
+ * EMXX FCD (Function Controller Driver) for USB.
+ *
+ * Copyright (C) 2010 Renesas Electronics Corporation
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/delay.h>
+#include <linux/ioport.h>
+#include <linux/slab.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/list.h>
+#include <linux/interrupt.h>
+#include <linux/proc_fs.h>
+#include <linux/clk.h>
+#include <linux/ctype.h>
+#include <linux/string.h>
+#include <linux/dma-mapping.h>
+#include <linux/workqueue.h>
+#include <linux/device.h>
+
+#include <linux/usb/ch9.h>
+#include <linux/usb/gadget.h>
+
+#include <linux/irq.h>
+#include <linux/gpio.h>
+
+#include "emxx_udc.h"
+
+#define DRIVER_DESC "EMXX UDC driver"
+#define DMA_ADDR_INVALID (~(dma_addr_t)0)
+
+static const char driver_name[] = "emxx_udc";
+static const char driver_desc[] = DRIVER_DESC;
+
+/*===========================================================================*/
+/* Prototype */
+static void _nbu2ss_ep_dma_abort(struct nbu2ss_udc *, struct nbu2ss_ep *);
+static void _nbu2ss_ep0_enable(struct nbu2ss_udc *);
+/*static void _nbu2ss_ep0_disable(struct nbu2ss_udc *);*/
+static void _nbu2ss_ep_done(struct nbu2ss_ep *, struct nbu2ss_req *, int);
+static void _nbu2ss_set_test_mode(struct nbu2ss_udc *, u32 mode);
+static void _nbu2ss_endpoint_toggle_reset(struct nbu2ss_udc *udc, u8 ep_adrs);
+
+static int _nbu2ss_pullup(struct nbu2ss_udc *, int);
+static void _nbu2ss_fifo_flush(struct nbu2ss_udc *, struct nbu2ss_ep *);
+
+/*===========================================================================*/
+/* Macro */
+#define _nbu2ss_zero_len_pkt(udc, epnum) \
+ _nbu2ss_ep_in_end(udc, epnum, 0, 0)
+
+
+/*===========================================================================*/
+/* Global */
+struct nbu2ss_udc udc_controller;
+
+
+/*-------------------------------------------------------------------------*/
+/* Read */
+static inline u32 _nbu2ss_readl(void *address)
+{
+ return __raw_readl(address);
+}
+
+/*-------------------------------------------------------------------------*/
+/* Write */
+static inline void _nbu2ss_writel(void *address, u32 udata)
+{
+ __raw_writel(udata, address);
+}
+
+/*-------------------------------------------------------------------------*/
+/* Set Bit */
+static inline void _nbu2ss_bitset(void *address, u32 udata)
+{
+ u32 reg_dt = __raw_readl(address) | (udata);
+
+ __raw_writel(reg_dt, address);
+}
+
+/*-------------------------------------------------------------------------*/
+/* Clear Bit */
+static inline void _nbu2ss_bitclr(void *address, u32 udata)
+{
+ u32 reg_dt = __raw_readl(address) & ~(udata);
+
+ __raw_writel(reg_dt, address);
+}
+
+#ifdef UDC_DEBUG_DUMP
+/*-------------------------------------------------------------------------*/
+static void _nbu2ss_dump_register(struct nbu2ss_udc *udc)
+{
+ int i;
+ u32 reg_data;
+
+ pr_info("=== %s()\n", __func__);
+
+ if (udc == NULL) {
+ pr_err("%s udc == NULL\n", __func__);
+ return;
+ }
+
+ spin_unlock(&udc->lock);
+
+ dev_dbg(&udc->dev, "\n-USB REG-\n");
+ for (i = 0x0 ; i < USB_BASE_SIZE ; i += 16) {
+ reg_data = _nbu2ss_readl(
+ (u32 *)IO_ADDRESS(USB_BASE_ADDRESS + i));
+ dev_dbg(&udc->dev, "USB%04x =%08x", i, (int)reg_data);
+
+ reg_data = _nbu2ss_readl(
+ (u32 *)IO_ADDRESS(USB_BASE_ADDRESS + i + 4));
+ dev_dbg(&udc->dev, " %08x", (int)reg_data);
+
+ reg_data = _nbu2ss_readl(
+ (u32 *)IO_ADDRESS(USB_BASE_ADDRESS + i + 8));
+ dev_dbg(&udc->dev, " %08x", (int)reg_data);
+
+ reg_data = _nbu2ss_readl(
+ (u32 *)IO_ADDRESS(USB_BASE_ADDRESS + i + 12));
+ dev_dbg(&udc->dev, " %08x\n", (int)reg_data);
+
+ }
+
+ spin_lock(&udc->lock);
+}
+#endif /* UDC_DEBUG_DUMP */
+
+/*-------------------------------------------------------------------------*/
+/* Endpoint 0 Callback (Complete) */
+static void _nbu2ss_ep0_complete(struct usb_ep *_ep, struct usb_request *_req)
+{
+ u8 recipient;
+ u16 selector;
+ u32 test_mode;
+ struct usb_ctrlrequest *p_ctrl;
+ struct nbu2ss_udc *udc;
+
+ if ((_ep == NULL) || (_req == NULL))
+ return;
+
+ udc = (struct nbu2ss_udc *)_req->context;
+ p_ctrl = &udc->ctrl;
+ if ((p_ctrl->bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD) {
+
+ if (p_ctrl->bRequest == USB_REQ_SET_FEATURE) {
+ /*-------------------------------------------------*/
+ /* SET_FEATURE */
+ recipient = (u8)(p_ctrl->bRequestType & USB_RECIP_MASK);
+ selector = p_ctrl->wValue;
+ if ((recipient == USB_RECIP_DEVICE) &&
+ (selector == USB_DEVICE_TEST_MODE)) {
+ test_mode = (u32)(p_ctrl->wIndex >> 8);
+ _nbu2ss_set_test_mode(udc, test_mode);
+ }
+ }
+ }
+}
+
+/*-------------------------------------------------------------------------*/
+/* Initialization usb_request */
+static void _nbu2ss_create_ep0_packet(
+ struct nbu2ss_udc *udc,
+ void *p_buf,
+ unsigned length
+)
+{
+ udc->ep0_req.req.buf = p_buf;
+ udc->ep0_req.req.length = length;
+ udc->ep0_req.req.dma = 0;
+ udc->ep0_req.req.zero = TRUE;
+ udc->ep0_req.req.complete = _nbu2ss_ep0_complete;
+ udc->ep0_req.req.status = -EINPROGRESS;
+ udc->ep0_req.req.context = udc;
+ udc->ep0_req.req.actual = 0;
+}
+
+/*-------------------------------------------------------------------------*/
+/* Acquisition of the first address of RAM(FIFO) */
+static u32 _nbu2ss_get_begin_ram_address(struct nbu2ss_udc *udc)
+{
+ u32 num, buf_type;
+ u32 data, last_ram_adr, use_ram_size;
+
+ struct ep_regs *p_ep_regs;
+
+ last_ram_adr = (D_RAM_SIZE_CTRL / sizeof(u32)) * 2;
+ use_ram_size = 0;
+
+ for (num = 0; num < NUM_ENDPOINTS - 1; num++) {
+ p_ep_regs = &udc->p_regs->EP_REGS[num];
+ data = _nbu2ss_readl(&p_ep_regs->EP_PCKT_ADRS);
+ buf_type = _nbu2ss_readl(&p_ep_regs->EP_CONTROL) & EPn_BUF_TYPE;
+ if (buf_type == 0) {
+ /* Single Buffer */
+ use_ram_size += (data & EPn_MPKT) / sizeof(u32);
+ } else {
+ /* Double Buffer */
+ use_ram_size += ((data & EPn_MPKT) / sizeof(u32)) * 2;
+ }
+
+ if ((data >> 16) > last_ram_adr)
+ last_ram_adr = data>>16;
+ }
+
+ return last_ram_adr + use_ram_size;
+}
+
+/*-------------------------------------------------------------------------*/
+/* Construction of Endpoint */
+static int _nbu2ss_ep_init(struct nbu2ss_udc *udc, struct nbu2ss_ep *ep)
+{
+ u32 num;
+ u32 data;
+ u32 begin_adrs;
+
+ if (ep->epnum == 0)
+ return -EINVAL;
+
+ num = ep->epnum - 1;
+
+ /*-------------------------------------------------------------*/
+ /* RAM Transfer Address */
+ begin_adrs = _nbu2ss_get_begin_ram_address(udc);
+ data = (begin_adrs << 16) | ep->ep.maxpacket;
+ _nbu2ss_writel(&udc->p_regs->EP_REGS[num].EP_PCKT_ADRS, data);
+
+ /*-------------------------------------------------------------*/
+ /* Interrupt Enable */
+ data = 1 << (ep->epnum + 8);
+ _nbu2ss_bitset(&udc->p_regs->USB_INT_ENA, data);
+
+ /*-------------------------------------------------------------*/
+ /* Endpoint Type(Mode) */
+ /* Bulk, Interrupt, ISO */
+ switch (ep->ep_type) {
+ case USB_ENDPOINT_XFER_BULK:
+ data = EPn_BULK;
+ break;
+
+ case USB_ENDPOINT_XFER_INT:
+ data = EPn_BUF_SINGLE | EPn_INTERRUPT;
+ break;
+
+ case USB_ENDPOINT_XFER_ISOC:
+ data = EPn_ISO;
+ break;
+
+ default:
+ data = 0;
+ break;
+ }
+
+ _nbu2ss_bitset(&udc->p_regs->EP_REGS[num].EP_CONTROL, data);
+ _nbu2ss_endpoint_toggle_reset(udc, (ep->epnum|ep->direct));
+
+ if (ep->direct == USB_DIR_OUT) {
+ /*---------------------------------------------------------*/
+ /* OUT */
+ data = EPn_EN | EPn_BCLR | EPn_DIR0;
+ _nbu2ss_bitset(&udc->p_regs->EP_REGS[num].EP_CONTROL, data);
+
+ data = (EPn_ONAK | EPn_OSTL_EN | EPn_OSTL);
+ _nbu2ss_bitclr(&udc->p_regs->EP_REGS[num].EP_CONTROL, data);
+
+ data = (EPn_OUT_EN | EPn_OUT_END_EN);
+ _nbu2ss_bitset(&udc->p_regs->EP_REGS[num].EP_INT_ENA, data);
+ } else {
+ /*---------------------------------------------------------*/
+ /* IN */
+ data = (EPn_EN | EPn_BCLR | EPn_AUTO);
+ _nbu2ss_bitset(&udc->p_regs->EP_REGS[num].EP_CONTROL, data);
+
+ data = (EPn_ISTL);
+ _nbu2ss_bitclr(&udc->p_regs->EP_REGS[num].EP_CONTROL, data);
+
+ data = (EPn_IN_EN | EPn_IN_END_EN);
+ _nbu2ss_bitset(&udc->p_regs->EP_REGS[num].EP_INT_ENA, data);
+ }
+
+ return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+/* Release of Endpoint */
+static int _nbu2ss_epn_exit(struct nbu2ss_udc *udc, struct nbu2ss_ep *ep)
+{
+ u32 num;
+ u32 data;
+
+ if ((ep->epnum == 0) || (udc->vbus_active == 0))
+ return -EINVAL;
+
+ num = ep->epnum - 1;
+
+ /*-------------------------------------------------------------*/
+ /* RAM Transfer Address */
+ _nbu2ss_writel(&udc->p_regs->EP_REGS[num].EP_PCKT_ADRS, 0);
+
+ /*-------------------------------------------------------------*/
+ /* Interrupt Disable */
+ data = 1 << (ep->epnum + 8);
+ _nbu2ss_bitclr(&udc->p_regs->USB_INT_ENA, data);
+
+ if (ep->direct == USB_DIR_OUT) {
+ /*---------------------------------------------------------*/
+ /* OUT */
+ data = EPn_ONAK | EPn_BCLR;
+ _nbu2ss_bitset(&udc->p_regs->EP_REGS[num].EP_CONTROL, data);
+
+ data = EPn_EN | EPn_DIR0;
+ _nbu2ss_bitclr(&udc->p_regs->EP_REGS[num].EP_CONTROL, data);
+
+ data = EPn_OUT_EN | EPn_OUT_END_EN;
+ _nbu2ss_bitclr(&udc->p_regs->EP_REGS[num].EP_INT_ENA, data);
+ } else {
+ /*---------------------------------------------------------*/
+ /* IN */
+ data = EPn_BCLR;
+ _nbu2ss_bitset(&udc->p_regs->EP_REGS[num].EP_CONTROL, data);
+
+ data = EPn_EN | EPn_AUTO;
+ _nbu2ss_bitclr(&udc->p_regs->EP_REGS[num].EP_CONTROL, data);
+
+ data = EPn_IN_EN | EPn_IN_END_EN;
+ _nbu2ss_bitclr(&udc->p_regs->EP_REGS[num].EP_INT_ENA, data);
+ }
+
+ return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+/* DMA setting (without Endpoint 0) */
+static void _nbu2ss_ep_dma_init(struct nbu2ss_udc *udc, struct nbu2ss_ep *ep)
+{
+ u32 num;
+ u32 data;
+
+ data = _nbu2ss_readl(&udc->p_regs->USBSSCONF);
+ if (((ep->epnum == 0) || (data & (1 << ep->epnum)) == 0))
+ return; /* Not Support DMA */
+
+ num = ep->epnum - 1;
+
+ if (ep->direct == USB_DIR_OUT) {
+ /*---------------------------------------------------------*/
+ /* OUT */
+ data = ep->ep.maxpacket;
+ _nbu2ss_writel(&udc->p_regs->EP_DCR[num].EP_DCR2, data);
+
+ /*---------------------------------------------------------*/
+ /* Transfer Direct */
+ data = DCR1_EPn_DIR0;
+ _nbu2ss_bitset(&udc->p_regs->EP_DCR[num].EP_DCR1, data);
+
+ /*---------------------------------------------------------*/
+ /* DMA Mode etc. */
+ data = EPn_STOP_MODE | EPn_STOP_SET | EPn_DMAMODE0;
+ _nbu2ss_writel(&udc->p_regs->EP_REGS[num].EP_DMA_CTRL, data);
+ } else {
+ /*---------------------------------------------------------*/
+ /* IN */
+ _nbu2ss_bitset(&udc->p_regs->EP_REGS[num].EP_CONTROL, EPn_AUTO);
+
+ /*---------------------------------------------------------*/
+ /* DMA Mode etc. */
+ data = EPn_BURST_SET | EPn_DMAMODE0;
+ _nbu2ss_writel(&udc->p_regs->EP_REGS[num].EP_DMA_CTRL, data);
+ }
+}
+
+/*-------------------------------------------------------------------------*/
+/* DMA setting release */
+static void _nbu2ss_ep_dma_exit(struct nbu2ss_udc *udc, struct nbu2ss_ep *ep)
+{
+ u32 num;
+ u32 data;
+ struct fc_regs *preg = udc->p_regs;
+
+ if (udc->vbus_active == 0)
+ return; /* VBUS OFF */
+
+ data = _nbu2ss_readl(&preg->USBSSCONF);
+ if ((ep->epnum == 0) || ((data & (1 << ep->epnum)) == 0))
+ return; /* Not Support DMA */
+
+ num = ep->epnum - 1;
+
+ _nbu2ss_ep_dma_abort(udc, ep);
+
+ if (ep->direct == USB_DIR_OUT) {
+ /*---------------------------------------------------------*/
+ /* OUT */
+ _nbu2ss_writel(&preg->EP_DCR[num].EP_DCR2, 0);
+ _nbu2ss_bitclr(&preg->EP_DCR[num].EP_DCR1, DCR1_EPn_DIR0);
+ _nbu2ss_writel(&preg->EP_REGS[num].EP_DMA_CTRL, 0);
+ } else {
+ /*---------------------------------------------------------*/
+ /* IN */
+ _nbu2ss_bitclr(&preg->EP_REGS[num].EP_CONTROL, EPn_AUTO);
+ _nbu2ss_writel(&preg->EP_REGS[num].EP_DMA_CTRL, 0);
+ }
+}
+
+/*-------------------------------------------------------------------------*/
+/* Abort DMA */
+static void _nbu2ss_ep_dma_abort(struct nbu2ss_udc *udc, struct nbu2ss_ep *ep)
+{
+ struct fc_regs *preg = udc->p_regs;
+
+ _nbu2ss_bitclr(&preg->EP_DCR[ep->epnum-1].EP_DCR1, DCR1_EPn_REQEN);
+ mdelay(DMA_DISABLE_TIME); /* DCR1_EPn_REQEN Clear */
+ _nbu2ss_bitclr(&preg->EP_REGS[ep->epnum-1].EP_DMA_CTRL, EPn_DMA_EN);
+}
+
+/*-------------------------------------------------------------------------*/
+/* Start IN Transfer */
+static void _nbu2ss_ep_in_end(
+ struct nbu2ss_udc *udc,
+ u32 epnum,
+ u32 data32,
+ u32 length
+)
+{
+ u32 data;
+ u32 num;
+ struct fc_regs *preg = udc->p_regs;
+
+ if (length >= sizeof(u32))
+ return;
+
+ if (epnum == 0) {
+ _nbu2ss_bitclr(&preg->EP0_CONTROL, EP0_AUTO);
+
+ /* Writing of 1-4 bytes */
+ if (length)
+ _nbu2ss_writel(&preg->EP0_WRITE, data32);
+
+ data = ((length << 5) & EP0_DW) | EP0_DEND;
+ _nbu2ss_writel(&preg->EP0_CONTROL, data);
+
+ _nbu2ss_bitset(&preg->EP0_CONTROL, EP0_AUTO);
+ } else {
+ num = epnum - 1;
+
+ _nbu2ss_bitclr(&preg->EP_REGS[num].EP_CONTROL, EPn_AUTO);
+
+ /* Writing of 1-4 bytes */
+ if (length)
+ _nbu2ss_writel(&preg->EP_REGS[num].EP_WRITE, data32);
+
+ data = (((((u32)length) << 5) & EPn_DW) | EPn_DEND);
+ _nbu2ss_bitset(&preg->EP_REGS[num].EP_CONTROL, data);
+
+ _nbu2ss_bitset(&preg->EP_REGS[num].EP_CONTROL, EPn_AUTO);
+ }
+}
+
+#ifdef USE_DMA
+/*-------------------------------------------------------------------------*/
+static void _nbu2ss_dma_map_single(
+ struct nbu2ss_udc *udc,
+ struct nbu2ss_ep *ep,
+ struct nbu2ss_req *req,
+ u8 direct
+)
+{
+ if (req->req.dma == DMA_ADDR_INVALID) {
+ if (req->unaligned)
+ req->req.dma = ep->phys_buf;
+ else {
+ req->req.dma = dma_map_single(
+ udc->gadget.dev.parent,
+ req->req.buf,
+ req->req.length,
+ (direct == USB_DIR_IN)
+ ? DMA_TO_DEVICE : DMA_FROM_DEVICE);
+ }
+ req->mapped = 1;
+ } else {
+ if (!req->unaligned)
+ dma_sync_single_for_device(
+ udc->gadget.dev.parent,
+ req->req.dma,
+ req->req.length,
+ (direct == USB_DIR_IN)
+ ? DMA_TO_DEVICE : DMA_FROM_DEVICE);
+
+ req->mapped = 0;
+ }
+}
+
+/*-------------------------------------------------------------------------*/
+static void _nbu2ss_dma_unmap_single(
+ struct nbu2ss_udc *udc,
+ struct nbu2ss_ep *ep,
+ struct nbu2ss_req *req,
+ u8 direct
+)
+{
+ u8 data[4];
+ u8 *p;
+ u32 count = 0;
+
+ if (direct == USB_DIR_OUT) {
+ count = req->req.actual % 4;
+ if (count) {
+ p = req->req.buf;
+ p += (req->req.actual - count);
+ memcpy(data, p, count);
+ }
+ }
+
+ if (req->mapped) {
+ if (req->unaligned) {
+ if (direct == USB_DIR_OUT)
+ memcpy(req->req.buf, ep->virt_buf,
+ req->req.actual & 0xfffffffc);
+ } else
+ dma_unmap_single(udc->gadget.dev.parent,
+ req->req.dma, req->req.length,
+ (direct == USB_DIR_IN)
+ ? DMA_TO_DEVICE
+ : DMA_FROM_DEVICE);
+ req->req.dma = DMA_ADDR_INVALID;
+ req->mapped = 0;
+ } else {
+ if (!req->unaligned)
+ dma_sync_single_for_cpu(udc->gadget.dev.parent,
+ req->req.dma, req->req.length,
+ (direct == USB_DIR_IN)
+ ? DMA_TO_DEVICE
+ : DMA_FROM_DEVICE);
+ }
+
+ if (count) {
+ p = req->req.buf;
+ p += (req->req.actual - count);
+ memcpy(p, data, count);
+ }
+}
+#endif
+
+/*-------------------------------------------------------------------------*/
+/* Endpoint 0 OUT Transfer (PIO) */
+static int EP0_out_PIO(struct nbu2ss_udc *udc, u8 *pBuf, u32 length)
+{
+ u32 i;
+ int nret = 0;
+ u32 iWordLength = 0;
+ union usb_reg_access *pBuf32 = (union usb_reg_access *)pBuf;
+
+ /*------------------------------------------------------------*/
+ /* Read Length */
+ iWordLength = length / sizeof(u32);
+
+ /*------------------------------------------------------------*/
+ /* PIO Read */
+ if (iWordLength) {
+ for (i = 0; i < iWordLength; i++) {
+ pBuf32->dw = _nbu2ss_readl(&udc->p_regs->EP0_READ);
+ pBuf32++;
+ }
+ nret = iWordLength * sizeof(u32);
+ }
+
+ return nret;
+}
+
+/*-------------------------------------------------------------------------*/
+/* Endpoint 0 OUT Transfer (PIO, OverBytes) */
+static int EP0_out_OverBytes(struct nbu2ss_udc *udc, u8 *pBuf, u32 length)
+{
+ u32 i;
+ u32 iReadSize = 0;
+ union usb_reg_access Temp32;
+ union usb_reg_access *pBuf32 = (union usb_reg_access *)pBuf;
+
+ if ((0 < length) && (length < sizeof(u32))) {
+ Temp32.dw = _nbu2ss_readl(&udc->p_regs->EP0_READ);
+ for (i = 0 ; i < length ; i++)
+ pBuf32->byte.DATA[i] = Temp32.byte.DATA[i];
+ iReadSize += length;
+ }
+
+ return iReadSize;
+}
+
+/*-------------------------------------------------------------------------*/
+/* Endpoint 0 IN Transfer (PIO) */
+static int EP0_in_PIO(struct nbu2ss_udc *udc, u8 *pBuf, u32 length)
+{
+ u32 i;
+ u32 iMaxLength = EP0_PACKETSIZE;
+ u32 iWordLength = 0;
+ u32 iWriteLength = 0;
+ union usb_reg_access *pBuf32 = (union usb_reg_access *)pBuf;
+
+ /*------------------------------------------------------------*/
+ /* Transfer Length */
+ if (iMaxLength < length)
+ iWordLength = iMaxLength / sizeof(u32);
+ else
+ iWordLength = length / sizeof(u32);
+
+ /*------------------------------------------------------------*/
+ /* PIO */
+ for (i = 0; i < iWordLength; i++) {
+ _nbu2ss_writel(&udc->p_regs->EP0_WRITE, pBuf32->dw);
+ pBuf32++;
+ iWriteLength += sizeof(u32);
+ }
+
+ return iWriteLength;
+}
+
+/*-------------------------------------------------------------------------*/
+/* Endpoint 0 IN Transfer (PIO, OverBytes) */
+static int EP0_in_OverBytes(struct nbu2ss_udc *udc, u8 *pBuf, u32 iRemainSize)
+{
+ u32 i;
+ union usb_reg_access Temp32;
+ union usb_reg_access *pBuf32 = (union usb_reg_access *)pBuf;
+
+ if ((0 < iRemainSize) && (iRemainSize < sizeof(u32))) {
+ for (i = 0 ; i < iRemainSize ; i++)
+ Temp32.byte.DATA[i] = pBuf32->byte.DATA[i];
+ _nbu2ss_ep_in_end(udc, 0, Temp32.dw, iRemainSize);
+
+ return iRemainSize;
+ }
+
+ return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+/* Transfer NULL Packet (Epndoint 0) */
+static int EP0_send_NULL(struct nbu2ss_udc *udc, bool pid_flag)
+{
+ u32 data;
+
+ data = _nbu2ss_readl(&udc->p_regs->EP0_CONTROL);
+ data &= ~(u32)EP0_INAK;
+
+ if (pid_flag)
+ data |= (EP0_INAK_EN | EP0_PIDCLR | EP0_DEND);
+ else
+ data |= (EP0_INAK_EN | EP0_DEND);
+
+ _nbu2ss_writel(&udc->p_regs->EP0_CONTROL, data);
+
+ return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+/* Receive NULL Packet (Endpoint 0) */
+static int EP0_receive_NULL(struct nbu2ss_udc *udc, bool pid_flag)
+{
+ u32 data;
+
+ data = _nbu2ss_readl(&udc->p_regs->EP0_CONTROL);
+ data &= ~(u32)EP0_ONAK;
+
+ if (pid_flag)
+ data |= EP0_PIDCLR;
+
+ _nbu2ss_writel(&udc->p_regs->EP0_CONTROL, data);
+
+ return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+static int _nbu2ss_ep0_in_transfer(
+ struct nbu2ss_udc *udc,
+ struct nbu2ss_ep *ep,
+ struct nbu2ss_req *req
+)
+{
+ u8 *pBuffer; /* IN Data Buffer */
+ u32 data;
+ u32 iRemainSize = 0;
+ int result = 0;
+
+ /*-------------------------------------------------------------*/
+ /* End confirmation */
+ if (req->req.actual == req->req.length) {
+ if ((req->req.actual % EP0_PACKETSIZE) == 0) {
+ if (req->zero) {
+ req->zero = false;
+ EP0_send_NULL(udc, FALSE);
+ return 1;
+ }
+ }
+
+ return 0; /* Transfer End */
+ }
+
+ /*-------------------------------------------------------------*/
+ /* NAK release */
+ data = _nbu2ss_readl(&udc->p_regs->EP0_CONTROL);
+ data |= EP0_INAK_EN;
+ data &= ~(u32)EP0_INAK;
+ _nbu2ss_writel(&udc->p_regs->EP0_CONTROL, data);
+
+ iRemainSize = req->req.length - req->req.actual;
+ pBuffer = (u8 *)req->req.buf;
+ pBuffer += req->req.actual;
+
+ /*-------------------------------------------------------------*/
+ /* Data transfer */
+ result = EP0_in_PIO(udc, pBuffer, iRemainSize);
+
+ req->div_len = result;
+ iRemainSize -= result;
+
+ if (iRemainSize == 0) {
+ EP0_send_NULL(udc, FALSE);
+ return result;
+ }
+
+ if ((iRemainSize < sizeof(u32)) && (result != EP0_PACKETSIZE)) {
+ pBuffer += result;
+ result += EP0_in_OverBytes(udc, pBuffer, iRemainSize);
+ req->div_len = result;
+ }
+
+ return result;
+}
+
+/*-------------------------------------------------------------------------*/
+static int _nbu2ss_ep0_out_transfer(
+ struct nbu2ss_udc *udc,
+ struct nbu2ss_ep *ep,
+ struct nbu2ss_req *req
+)
+{
+ u8 *pBuffer;
+ u32 iRemainSize;
+ u32 iRecvLength;
+ int result = 0;
+ int fRcvZero;
+
+ /*-------------------------------------------------------------*/
+ /* Receive data confirmation */
+ iRecvLength = _nbu2ss_readl(&udc->p_regs->EP0_LENGTH) & EP0_LDATA;
+ if (iRecvLength != 0) {
+
+ fRcvZero = 0;
+
+ iRemainSize = req->req.length - req->req.actual;
+ pBuffer = (u8 *)req->req.buf;
+ pBuffer += req->req.actual;
+
+ result = EP0_out_PIO(udc, pBuffer
+ , min(iRemainSize, iRecvLength));
+ if (result < 0)
+ return result;
+
+ req->req.actual += result;
+ iRecvLength -= result;
+
+ if ((0 < iRecvLength) && (iRecvLength < sizeof(u32))) {
+ pBuffer += result;
+ iRemainSize -= result;
+
+ result = EP0_out_OverBytes(udc, pBuffer
+ , min(iRemainSize, iRecvLength));
+ req->req.actual += result;
+ }
+ } else {
+ fRcvZero = 1;
+ }
+
+ /*-------------------------------------------------------------*/
+ /* End confirmation */
+ if (req->req.actual == req->req.length) {
+ if ((req->req.actual % EP0_PACKETSIZE) == 0) {
+ if (req->zero) {
+ req->zero = false;
+ EP0_receive_NULL(udc, FALSE);
+ return 1;
+ }
+ }
+
+ return 0; /* Transfer End */
+ }
+
+ if ((req->req.actual % EP0_PACKETSIZE) != 0)
+ return 0; /* Short Packet Transfer End */
+
+ if (req->req.actual > req->req.length) {
+ dev_err(udc->dev, " *** Overrun Error\n");
+ return -EOVERFLOW;
+ }
+
+ if (fRcvZero != 0) {
+ iRemainSize = _nbu2ss_readl(&udc->p_regs->EP0_CONTROL);
+ if (iRemainSize & EP0_ONAK) {
+ /*---------------------------------------------------*/
+ /* NACK release */
+ _nbu2ss_bitclr(&udc->p_regs->EP0_CONTROL, EP0_ONAK);
+ }
+ result = 1;
+ }
+
+ return result;
+}
+
+/*-------------------------------------------------------------------------*/
+static int _nbu2ss_out_dma(
+ struct nbu2ss_udc *udc,
+ struct nbu2ss_req *req,
+ u32 num,
+ u32 length
+)
+{
+ u8 *pBuffer;
+ u32 mpkt;
+ u32 lmpkt;
+ u32 dmacnt;
+ u32 burst = 1;
+ u32 data;
+ int result = -EINVAL;
+ struct fc_regs *preg = udc->p_regs;
+
+ if (req->dma_flag)
+ return 1; /* DMA is forwarded */
+
+ req->dma_flag = TRUE;
+ pBuffer = (u8 *)req->req.dma;
+ pBuffer += req->req.actual;
+
+ /* DMA Address */
+ _nbu2ss_writel(&preg->EP_DCR[num].EP_TADR, (u32)pBuffer);
+
+ /* Number of transfer packets */
+ mpkt = _nbu2ss_readl(&preg->EP_REGS[num].EP_PCKT_ADRS) & EPn_MPKT;
+ dmacnt = (length / mpkt);
+ lmpkt = (length % mpkt) & ~(u32)0x03;
+
+ if (DMA_MAX_COUNT < dmacnt) {
+ dmacnt = DMA_MAX_COUNT;
+ lmpkt = 0;
+ } else if (0 != lmpkt) {
+ if (0 == dmacnt)
+ burst = 0; /* Burst OFF */
+ dmacnt++;
+ }
+
+ data = mpkt | (lmpkt << 16);
+ _nbu2ss_writel(&preg->EP_DCR[num].EP_DCR2, data);
+
+ data = ((dmacnt & 0xff) << 16) | DCR1_EPn_DIR0 | DCR1_EPn_REQEN;
+ _nbu2ss_writel(&preg->EP_DCR[num].EP_DCR1, data);
+
+ if (0 == burst) {
+ _nbu2ss_writel(&preg->EP_REGS[num].EP_LEN_DCNT, 0);
+ _nbu2ss_bitclr(&preg->EP_REGS[num].EP_DMA_CTRL, EPn_BURST_SET);
+ } else {
+ _nbu2ss_writel(&preg->EP_REGS[num].EP_LEN_DCNT
+ , (dmacnt << 16));
+ _nbu2ss_bitset(&preg->EP_REGS[num].EP_DMA_CTRL, EPn_BURST_SET);
+ }
+ _nbu2ss_bitset(&preg->EP_REGS[num].EP_DMA_CTRL, EPn_DMA_EN);
+
+ result = length & ~(u32)0x03;
+ req->div_len = result;
+
+ return result;
+}
+
+/*-------------------------------------------------------------------------*/
+static int _nbu2ss_epn_out_pio(
+ struct nbu2ss_udc *udc,
+ struct nbu2ss_ep *ep,
+ struct nbu2ss_req *req,
+ u32 length
+)
+{
+ u8 *pBuffer;
+ u32 i;
+ u32 data;
+ u32 iWordLength;
+ union usb_reg_access Temp32;
+ union usb_reg_access *pBuf32;
+ int result = 0;
+ struct fc_regs *preg = udc->p_regs;
+
+ if (req->dma_flag)
+ return 1; /* DMA is forwarded */
+
+ if (length == 0)
+ return 0;
+
+ pBuffer = (u8 *)req->req.buf;
+ pBuf32 = (union usb_reg_access *)(pBuffer + req->req.actual);
+
+ iWordLength = length / sizeof(u32);
+ if (iWordLength > 0) {
+ /*---------------------------------------------------------*/
+ /* Copy of every four bytes */
+ for (i = 0; i < iWordLength; i++) {
+ pBuf32->dw =
+ _nbu2ss_readl(&preg->EP_REGS[ep->epnum-1].EP_READ);
+ pBuf32++;
+ }
+ result = iWordLength * sizeof(u32);
+ }
+
+ data = length - result;
+ if (data > 0) {
+ /*---------------------------------------------------------*/
+ /* Copy of fraction byte */
+ Temp32.dw = _nbu2ss_readl(&preg->EP_REGS[ep->epnum-1].EP_READ);
+ for (i = 0 ; i < data ; i++)
+ pBuf32->byte.DATA[i] = Temp32.byte.DATA[i];
+ result += data;
+ }
+
+ req->req.actual += result;
+
+ if ((req->req.actual == req->req.length)
+ || ((req->req.actual % ep->ep.maxpacket) != 0)) {
+
+ result = 0;
+ }
+
+ return result;
+}
+
+/*-------------------------------------------------------------------------*/
+static int _nbu2ss_epn_out_data(
+ struct nbu2ss_udc *udc,
+ struct nbu2ss_ep *ep,
+ struct nbu2ss_req *req,
+ u32 data_size
+)
+{
+ u32 num;
+ u32 iBufSize;
+ int nret = 1;
+
+ if (ep->epnum == 0)
+ return -EINVAL;
+
+ num = ep->epnum - 1;
+
+ iBufSize = min((req->req.length - req->req.actual), data_size);
+
+ if ((ep->ep_type != USB_ENDPOINT_XFER_INT)
+ && (req->req.dma != 0)
+ && (iBufSize >= sizeof(u32))) {
+ nret = _nbu2ss_out_dma(udc, req, num, iBufSize);
+ } else {
+ iBufSize = min_t(u32, iBufSize, ep->ep.maxpacket);
+ nret = _nbu2ss_epn_out_pio(udc, ep, req, iBufSize);
+ }
+
+ return nret;
+}
+
+/*-------------------------------------------------------------------------*/
+static int _nbu2ss_epn_out_transfer(
+ struct nbu2ss_udc *udc,
+ struct nbu2ss_ep *ep,
+ struct nbu2ss_req *req
+)
+{
+ u32 num;
+ u32 iRecvLength;
+ int result = 1;
+ struct fc_regs *preg = udc->p_regs;
+
+ if (ep->epnum == 0)
+ return -EINVAL;
+
+ num = ep->epnum - 1;
+
+ /*-------------------------------------------------------------*/
+ /* Receive Length */
+ iRecvLength
+ = _nbu2ss_readl(&preg->EP_REGS[num].EP_LEN_DCNT) & EPn_LDATA;
+
+ if (iRecvLength != 0) {
+ result = _nbu2ss_epn_out_data(udc, ep, req, iRecvLength);
+ if (iRecvLength < ep->ep.maxpacket) {
+ if (iRecvLength == result) {
+ req->req.actual += result;
+ result = 0;
+ }
+ }
+ } else {
+ if ((req->req.actual == req->req.length)
+ || ((req->req.actual % ep->ep.maxpacket) != 0)) {
+
+ result = 0;
+ }
+ }
+
+ if (result == 0) {
+ if ((req->req.actual % ep->ep.maxpacket) == 0) {
+ if (req->zero) {
+ req->zero = false;
+ return 1;
+ }
+ }
+ }
+
+ if (req->req.actual > req->req.length) {
+ dev_err(udc->dev, " Overrun Error\n");
+ dev_err(udc->dev, " actual = %d, length = %d\n",
+ req->req.actual, req->req.length);
+ result = -EOVERFLOW;
+ }
+
+ return result;
+}
+
+/*-------------------------------------------------------------------------*/
+static int _nbu2ss_in_dma(
+ struct nbu2ss_udc *udc,
+ struct nbu2ss_ep *ep,
+ struct nbu2ss_req *req,
+ u32 num,
+ u32 length
+)
+{
+ u8 *pBuffer;
+ u32 mpkt; /* MaxPacketSize */
+ u32 lmpkt; /* Last Packet Data Size */
+ u32 dmacnt; /* IN Data Size */
+ u32 iWriteLength;
+ u32 data;
+ int result = -EINVAL;
+ struct fc_regs *preg = udc->p_regs;
+
+ if (req->dma_flag)
+ return 1; /* DMA is forwarded */
+
+#ifdef USE_DMA
+ if (req->req.actual == 0)
+ _nbu2ss_dma_map_single(udc, ep, req, USB_DIR_IN);
+#endif
+ req->dma_flag = TRUE;
+
+ /* MAX Packet Size */
+ mpkt = _nbu2ss_readl(&preg->EP_REGS[num].EP_PCKT_ADRS) & EPn_MPKT;
+
+ if ((DMA_MAX_COUNT * mpkt) < length)
+ iWriteLength = DMA_MAX_COUNT * mpkt;
+ else
+ iWriteLength = length;
+
+ /*------------------------------------------------------------*/
+ /* Number of transmission packets */
+ if (mpkt < iWriteLength) {
+ dmacnt = iWriteLength / mpkt;
+ lmpkt = (iWriteLength % mpkt) & ~(u32)0x3;
+ if (lmpkt != 0)
+ dmacnt++;
+ else
+ lmpkt = mpkt & ~(u32)0x3;
+
+ } else {
+ dmacnt = 1;
+ lmpkt = iWriteLength & ~(u32)0x3;
+ }
+
+ /* Packet setting */
+ data = mpkt | (lmpkt << 16);
+ _nbu2ss_writel(&preg->EP_DCR[num].EP_DCR2, data);
+
+ /* Address setting */
+ pBuffer = (u8 *)req->req.dma;
+ pBuffer += req->req.actual;
+ _nbu2ss_writel(&preg->EP_DCR[num].EP_TADR, (u32)pBuffer);
+
+ /* Packet and DMA setting */
+ data = ((dmacnt & 0xff) << 16) | DCR1_EPn_REQEN;
+ _nbu2ss_writel(&preg->EP_DCR[num].EP_DCR1, data);
+
+ /* Packet setting of EPC */
+ data = dmacnt << 16;
+ _nbu2ss_writel(&preg->EP_REGS[num].EP_LEN_DCNT, data);
+
+ /*DMA setting of EPC */
+ _nbu2ss_bitset(&preg->EP_REGS[num].EP_DMA_CTRL, EPn_DMA_EN);
+
+ result = iWriteLength & ~(u32)0x3;
+ req->div_len = result;
+
+ return result;
+}
+
+/*-------------------------------------------------------------------------*/
+static int _nbu2ss_epn_in_pio(
+ struct nbu2ss_udc *udc,
+ struct nbu2ss_ep *ep,
+ struct nbu2ss_req *req,
+ u32 length
+)
+{
+ u8 *pBuffer;
+ u32 i;
+ u32 data;
+ u32 iWordLength;
+ union usb_reg_access Temp32;
+ union usb_reg_access *pBuf32 = NULL;
+ int result = 0;
+ struct fc_regs *preg = udc->p_regs;
+
+ if (req->dma_flag)
+ return 1; /* DMA is forwarded */
+
+ if (length > 0) {
+ pBuffer = (u8 *)req->req.buf;
+ pBuf32 = (union usb_reg_access *)(pBuffer + req->req.actual);
+
+ iWordLength = length / sizeof(u32);
+ if (iWordLength > 0) {
+ for (i = 0; i < iWordLength; i++) {
+ _nbu2ss_writel(
+ &preg->EP_REGS[ep->epnum-1].EP_WRITE
+ , pBuf32->dw
+ );
+
+ pBuf32++;
+ }
+ result = iWordLength * sizeof(u32);
+ }
+ }
+
+ if (result != ep->ep.maxpacket) {
+ data = length - result;
+ Temp32.dw = 0;
+ for (i = 0 ; i < data ; i++)
+ Temp32.byte.DATA[i] = pBuf32->byte.DATA[i];
+
+ _nbu2ss_ep_in_end(udc, ep->epnum, Temp32.dw, data);
+ result += data;
+ }
+
+ req->div_len = result;
+
+ return result;
+}
+
+/*-------------------------------------------------------------------------*/
+static int _nbu2ss_epn_in_data(
+ struct nbu2ss_udc *udc,
+ struct nbu2ss_ep *ep,
+ struct nbu2ss_req *req,
+ u32 data_size
+)
+{
+ u32 num;
+ int nret = 1;
+
+ if (ep->epnum == 0)
+ return -EINVAL;
+
+ num = ep->epnum - 1;
+
+ if ((ep->ep_type != USB_ENDPOINT_XFER_INT)
+ && (req->req.dma != 0)
+ && (data_size >= sizeof(u32))) {
+ nret = _nbu2ss_in_dma(udc, ep, req, num, data_size);
+ } else {
+ data_size = min_t(u32, data_size, ep->ep.maxpacket);
+ nret = _nbu2ss_epn_in_pio(udc, ep, req, data_size);
+ }
+
+ return nret;
+}
+
+/*-------------------------------------------------------------------------*/
+static int _nbu2ss_epn_in_transfer(
+ struct nbu2ss_udc *udc,
+ struct nbu2ss_ep *ep,
+ struct nbu2ss_req *req
+)
+{
+ u32 num;
+ u32 iBufSize;
+ int result = 0;
+ u32 status;
+
+ if (ep->epnum == 0)
+ return -EINVAL;
+
+ num = ep->epnum - 1;
+
+ status = _nbu2ss_readl(&udc->p_regs->EP_REGS[num].EP_STATUS);
+
+ /*-------------------------------------------------------------*/
+ /* State confirmation of FIFO */
+ if (req->req.actual == 0) {
+ if ((status & EPn_IN_EMPTY) == 0)
+ return 1; /* Not Empty */
+
+ } else {
+ if ((status & EPn_IN_FULL) != 0)
+ return 1; /* Not Empty */
+ }
+
+ /*-------------------------------------------------------------*/
+ /* Start tranfer */
+ iBufSize = req->req.length - req->req.actual;
+ if (iBufSize > 0)
+ result = _nbu2ss_epn_in_data(udc, ep, req, iBufSize);
+ else if (req->req.length == 0)
+ _nbu2ss_zero_len_pkt(udc, ep->epnum);
+
+ return result;
+}
+
+/*-------------------------------------------------------------------------*/
+static int _nbu2ss_start_transfer(
+ struct nbu2ss_udc *udc,
+ struct nbu2ss_ep *ep,
+ struct nbu2ss_req *req,
+ bool bflag)
+{
+ int nret = -EINVAL;
+
+ req->dma_flag = FALSE;
+ req->div_len = 0;
+
+ if (req->req.length == 0)
+ req->zero = false;
+ else {
+ if ((req->req.length % ep->ep.maxpacket) == 0)
+ req->zero = req->req.zero;
+ else
+ req->zero = false;
+ }
+
+ if (ep->epnum == 0) {
+ /* EP0 */
+ switch (udc->ep0state) {
+ case EP0_IN_DATA_PHASE:
+ nret = _nbu2ss_ep0_in_transfer(udc, ep, req);
+ break;
+
+ case EP0_OUT_DATA_PHASE:
+ nret = _nbu2ss_ep0_out_transfer(udc, ep, req);
+ break;
+
+ case EP0_IN_STATUS_PHASE:
+ nret = EP0_send_NULL(udc, TRUE);
+ break;
+
+ default:
+ break;
+ }
+
+ } else {
+ /* EPn */
+ if (ep->direct == USB_DIR_OUT) {
+ /* OUT */
+ if (bflag == FALSE)
+ nret = _nbu2ss_epn_out_transfer(udc, ep, req);
+ } else {
+ /* IN */
+ nret = _nbu2ss_epn_in_transfer(udc, ep, req);
+ }
+ }
+
+ return nret;
+}
+
+/*-------------------------------------------------------------------------*/
+static void _nbu2ss_restert_transfer(struct nbu2ss_ep *ep)
+{
+ u32 length;
+ bool bflag = FALSE;
+ struct nbu2ss_req *req;
+
+ if (list_empty(&ep->queue))
+ req = NULL;
+ else
+ req = list_entry(ep->queue.next, struct nbu2ss_req, queue);
+
+ if (req == NULL)
+ return;
+
+ if (ep->epnum > 0) {
+ length = _nbu2ss_readl(
+ &ep->udc->p_regs->EP_REGS[ep->epnum-1].EP_LEN_DCNT);
+
+ length &= EPn_LDATA;
+ if (length < ep->ep.maxpacket)
+ bflag = TRUE;
+ }
+
+ _nbu2ss_start_transfer(ep->udc, ep, req, bflag);
+}
+
+/*-------------------------------------------------------------------------*/
+/* Endpoint Toggle Reset */
+static void _nbu2ss_endpoint_toggle_reset(
+ struct nbu2ss_udc *udc,
+ u8 ep_adrs)
+{
+ u8 num;
+ u32 data;
+
+ if ((ep_adrs == 0) || (ep_adrs == 0x80))
+ return;
+
+ num = (ep_adrs & 0x7F) - 1;
+
+ if (ep_adrs & USB_DIR_IN)
+ data = EPn_IPIDCLR;
+ else
+ data = EPn_BCLR | EPn_OPIDCLR;
+
+ _nbu2ss_bitset(&udc->p_regs->EP_REGS[num].EP_CONTROL, data);
+}
+
+/*-------------------------------------------------------------------------*/
+/* Endpoint STALL set */
+static void _nbu2ss_set_endpoint_stall(
+ struct nbu2ss_udc *udc,
+ u8 ep_adrs,
+ bool bstall)
+{
+ u8 num, epnum;
+ u32 data;
+ struct nbu2ss_ep *ep;
+ struct fc_regs *preg = udc->p_regs;
+
+ if ((ep_adrs == 0) || (ep_adrs == 0x80)) {
+ if (bstall) {
+ /* Set STALL */
+ _nbu2ss_bitset(&preg->EP0_CONTROL, EP0_STL);
+ } else {
+ /* Clear STALL */
+ _nbu2ss_bitclr(&preg->EP0_CONTROL, EP0_STL);
+ }
+ } else {
+ epnum = ep_adrs & USB_ENDPOINT_NUMBER_MASK;
+ num = epnum - 1;
+ ep = &udc->ep[epnum];
+
+ if (bstall) {
+ /* Set STALL */
+ ep->halted = TRUE;
+
+ if (ep_adrs & USB_DIR_IN)
+ data = EPn_BCLR | EPn_ISTL;
+ else
+ data = EPn_OSTL_EN | EPn_OSTL;
+
+ _nbu2ss_bitset(&preg->EP_REGS[num].EP_CONTROL, data);
+ } else {
+ /* Clear STALL */
+ ep->stalled = FALSE;
+ if (ep_adrs & USB_DIR_IN) {
+ _nbu2ss_bitclr(&preg->EP_REGS[num].EP_CONTROL
+ , EPn_ISTL);
+ } else {
+ data =
+ _nbu2ss_readl(&preg->EP_REGS[num].EP_CONTROL);
+
+ data &= ~EPn_OSTL;
+ data |= EPn_OSTL_EN;
+
+ _nbu2ss_writel(&preg->EP_REGS[num].EP_CONTROL
+ , data);
+ }
+
+ ep->stalled = FALSE;
+ if (ep->halted) {
+ ep->halted = FALSE;
+ _nbu2ss_restert_transfer(ep);
+ }
+ }
+ }
+}
+
+
+/*-------------------------------------------------------------------------*/
+/* Device Descriptor */
+static struct usb_device_descriptor device_desc = {
+ .bLength = sizeof(device_desc),
+ .bDescriptorType = USB_DT_DEVICE,
+ .bcdUSB = cpu_to_le16(0x0200),
+ .bDeviceClass = USB_CLASS_VENDOR_SPEC,
+ .bDeviceSubClass = 0x00,
+ .bDeviceProtocol = 0x00,
+ .bMaxPacketSize0 = 64,
+ .idVendor = cpu_to_le16(0x0409),
+ .idProduct = cpu_to_le16(0xfff0),
+ .bcdDevice = 0xffff,
+ .iManufacturer = 0x00,
+ .iProduct = 0x00,
+ .iSerialNumber = 0x00,
+ .bNumConfigurations = 0x01,
+};
+
+/*-------------------------------------------------------------------------*/
+static void _nbu2ss_set_test_mode(struct nbu2ss_udc *udc, u32 mode)
+{
+ u32 data;
+
+ if (mode > MAX_TEST_MODE_NUM)
+ return;
+
+ dev_info(udc->dev, "SET FEATURE : test mode = %d\n", mode);
+
+ data = _nbu2ss_readl(&udc->p_regs->USB_CONTROL);
+ data &= ~TEST_FORCE_ENABLE;
+ data |= mode << TEST_MODE_SHIFT;
+
+ _nbu2ss_writel(&udc->p_regs->USB_CONTROL, data);
+ _nbu2ss_bitset(&udc->p_regs->TEST_CONTROL, CS_TESTMODEEN);
+}
+
+/*-------------------------------------------------------------------------*/
+static int _nbu2ss_set_feature_device(
+ struct nbu2ss_udc *udc,
+ u16 selector,
+ u16 wIndex
+)
+{
+ int result = -EOPNOTSUPP;
+
+ switch (selector) {
+ case USB_DEVICE_REMOTE_WAKEUP:
+ if (0x0000 == wIndex) {
+ udc->remote_wakeup = U2F_ENABLE;
+ result = 0;
+ }
+ break;
+
+ case USB_DEVICE_TEST_MODE:
+ wIndex >>= 8;
+ if (wIndex <= MAX_TEST_MODE_NUM)
+ result = 0;
+ break;
+
+ default:
+ break;
+ }
+
+ return result;
+}
+
+/*-------------------------------------------------------------------------*/
+static int _nbu2ss_get_ep_stall(struct nbu2ss_udc *udc, u8 ep_adrs)
+{
+ u8 epnum;
+ u32 data = 0, bit_data;
+ struct fc_regs *preg = udc->p_regs;
+
+ epnum = ep_adrs & ~USB_ENDPOINT_DIR_MASK;
+ if (epnum == 0) {
+ data = _nbu2ss_readl(&preg->EP0_CONTROL);
+ bit_data = EP0_STL;
+
+ } else {
+ data = _nbu2ss_readl(&preg->EP_REGS[epnum-1].EP_CONTROL);
+ if ((data & EPn_EN) == 0)
+ return -1;
+
+ if (ep_adrs & USB_ENDPOINT_DIR_MASK)
+ bit_data = EPn_ISTL;
+ else
+ bit_data = EPn_OSTL;
+ }
+
+ if ((data & bit_data) == 0)
+ return 0;
+ return 1;
+}
+
+/*-------------------------------------------------------------------------*/
+static inline int _nbu2ss_req_feature(struct nbu2ss_udc *udc, bool bset)
+{
+ u8 recipient = (u8)(udc->ctrl.bRequestType & USB_RECIP_MASK);
+ u8 direction = (u8)(udc->ctrl.bRequestType & USB_DIR_IN);
+ u16 selector = udc->ctrl.wValue;
+ u16 wIndex = udc->ctrl.wIndex;
+ u8 ep_adrs;
+ int result = -EOPNOTSUPP;
+
+ if ((0x0000 != udc->ctrl.wLength) ||
+ (USB_DIR_OUT != direction)) {
+ return -EINVAL;
+ }
+
+ switch (recipient) {
+ case USB_RECIP_DEVICE:
+ if (bset)
+ result =
+ _nbu2ss_set_feature_device(udc, selector, wIndex);
+ break;
+
+ case USB_RECIP_ENDPOINT:
+ if (0x0000 == (wIndex & 0xFF70)) {
+ if (USB_ENDPOINT_HALT == selector) {
+ ep_adrs = wIndex & 0xFF;
+ if (bset == FALSE) {
+ _nbu2ss_endpoint_toggle_reset(
+ udc, ep_adrs);
+ }
+
+ _nbu2ss_set_endpoint_stall(
+ udc, ep_adrs, bset);
+
+ result = 0;
+ }
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ if (result >= 0)
+ _nbu2ss_create_ep0_packet(udc, udc->ep0_buf, 0);
+
+ return result;
+}
+
+/*-------------------------------------------------------------------------*/
+static inline enum usb_device_speed _nbu2ss_get_speed(struct nbu2ss_udc *udc)
+{
+ u32 data;
+ enum usb_device_speed speed = USB_SPEED_FULL;
+
+ data = _nbu2ss_readl(&udc->p_regs->USB_STATUS);
+ if (data & HIGH_SPEED)
+ speed = USB_SPEED_HIGH;
+
+ return speed;
+}
+
+/*-------------------------------------------------------------------------*/
+static void _nbu2ss_epn_set_stall(
+ struct nbu2ss_udc *udc,
+ struct nbu2ss_ep *ep
+)
+{
+ u8 ep_adrs;
+ u32 regdata;
+ int limit_cnt = 0;
+
+ struct fc_regs *preg = udc->p_regs;
+
+ if (ep->direct == USB_DIR_IN) {
+ for (limit_cnt = 0
+ ; limit_cnt < IN_DATA_EMPTY_COUNT
+ ; limit_cnt++) {
+
+ regdata = _nbu2ss_readl(
+ &preg->EP_REGS[ep->epnum-1].EP_STATUS);
+
+ if ((regdata & EPn_IN_DATA) == 0)
+ break;
+
+ mdelay(1);
+ }
+ }
+
+ ep_adrs = ep->epnum | ep->direct;
+ _nbu2ss_set_endpoint_stall(udc, ep_adrs, 1);
+}
+
+/*-------------------------------------------------------------------------*/
+static int std_req_get_status(struct nbu2ss_udc *udc)
+{
+ u32 length;
+ u16 status_data = 0;
+ u8 recipient = (u8)(udc->ctrl.bRequestType & USB_RECIP_MASK);
+ u8 direction = (u8)(udc->ctrl.bRequestType & USB_DIR_IN);
+ u8 ep_adrs;
+ int result = -EINVAL;
+
+ if ((0x0000 != udc->ctrl.wValue)
+ || (USB_DIR_IN != direction)) {
+
+ return result;
+ }
+
+ length = min_t(u16, udc->ctrl.wLength, sizeof(status_data));
+
+ switch (recipient) {
+ case USB_RECIP_DEVICE:
+ if (udc->ctrl.wIndex == 0x0000) {
+ if (udc->gadget.is_selfpowered)
+ status_data |= (1 << USB_DEVICE_SELF_POWERED);
+
+ if (udc->remote_wakeup)
+ status_data |= (1 << USB_DEVICE_REMOTE_WAKEUP);
+
+ result = 0;
+ }
+ break;
+
+ case USB_RECIP_ENDPOINT:
+ if (0x0000 == (udc->ctrl.wIndex & 0xFF70)) {
+ ep_adrs = (u8)(udc->ctrl.wIndex & 0xFF);
+ result = _nbu2ss_get_ep_stall(udc, ep_adrs);
+
+ if (result > 0)
+ status_data |= (1 << USB_ENDPOINT_HALT);
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ if (result >= 0) {
+ memcpy(udc->ep0_buf, &status_data, length);
+ _nbu2ss_create_ep0_packet(udc, udc->ep0_buf, length);
+ _nbu2ss_ep0_in_transfer(udc, &udc->ep[0], &udc->ep0_req);
+
+ } else {
+ dev_err(udc->dev, " Error GET_STATUS\n");
+ }
+
+ return result;
+}
+
+/*-------------------------------------------------------------------------*/
+static int std_req_clear_feature(struct nbu2ss_udc *udc)
+{
+ return _nbu2ss_req_feature(udc, FALSE);
+}
+
+/*-------------------------------------------------------------------------*/
+static int std_req_set_feature(struct nbu2ss_udc *udc)
+{
+ return _nbu2ss_req_feature(udc, TRUE);
+}
+
+/*-------------------------------------------------------------------------*/
+static int std_req_set_address(struct nbu2ss_udc *udc)
+{
+ int result = 0;
+ u32 wValue = udc->ctrl.wValue;
+
+ if ((0x00 != udc->ctrl.bRequestType) ||
+ (0x0000 != udc->ctrl.wIndex) ||
+ (0x0000 != udc->ctrl.wLength)) {
+ return -EINVAL;
+ }
+
+ if (wValue != (wValue & 0x007F))
+ return -EINVAL;
+
+ wValue <<= USB_ADRS_SHIFT;
+
+ _nbu2ss_writel(&udc->p_regs->USB_ADDRESS, wValue);
+ _nbu2ss_create_ep0_packet(udc, udc->ep0_buf, 0);
+
+ return result;
+}
+
+/*-------------------------------------------------------------------------*/
+static int std_req_set_configuration(struct nbu2ss_udc *udc)
+{
+ u32 ConfigValue = (u32)(udc->ctrl.wValue & 0x00ff);
+
+ if ((0x0000 != udc->ctrl.wIndex) ||
+ (0x0000 != udc->ctrl.wLength) ||
+ (0x00 != udc->ctrl.bRequestType)) {
+ return -EINVAL;
+ }
+
+ udc->curr_config = ConfigValue;
+
+ if (ConfigValue > 0) {
+ _nbu2ss_bitset(&udc->p_regs->USB_CONTROL, CONF);
+ udc->devstate = USB_STATE_CONFIGURED;
+
+ } else {
+ _nbu2ss_bitclr(&udc->p_regs->USB_CONTROL, CONF);
+ udc->devstate = USB_STATE_ADDRESS;
+ }
+
+ return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+static inline void _nbu2ss_read_request_data(struct nbu2ss_udc *udc, u32 *pdata)
+{
+ if ((udc == NULL) && (pdata == NULL))
+ return;
+
+ *pdata = _nbu2ss_readl(&udc->p_regs->SETUP_DATA0);
+ pdata++;
+ *pdata = _nbu2ss_readl(&udc->p_regs->SETUP_DATA1);
+}
+
+/*-------------------------------------------------------------------------*/
+static inline int _nbu2ss_decode_request(struct nbu2ss_udc *udc)
+{
+ bool bcall_back = TRUE;
+ int nret = -EINVAL;
+ struct usb_ctrlrequest *p_ctrl;
+
+ p_ctrl = &udc->ctrl;
+ _nbu2ss_read_request_data(udc, (u32 *)p_ctrl);
+
+ /* ep0 state control */
+ if (p_ctrl->wLength == 0) {
+ udc->ep0state = EP0_IN_STATUS_PHASE;
+
+ } else {
+ if (p_ctrl->bRequestType & USB_DIR_IN)
+ udc->ep0state = EP0_IN_DATA_PHASE;
+ else
+ udc->ep0state = EP0_OUT_DATA_PHASE;
+ }
+
+ if ((p_ctrl->bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD) {
+ switch (p_ctrl->bRequest) {
+ case USB_REQ_GET_STATUS:
+ nret = std_req_get_status(udc);
+ bcall_back = FALSE;
+ break;
+
+ case USB_REQ_CLEAR_FEATURE:
+ nret = std_req_clear_feature(udc);
+ bcall_back = FALSE;
+ break;
+
+ case USB_REQ_SET_FEATURE:
+ nret = std_req_set_feature(udc);
+ bcall_back = FALSE;
+ break;
+
+ case USB_REQ_SET_ADDRESS:
+ nret = std_req_set_address(udc);
+ bcall_back = FALSE;
+ break;
+
+ case USB_REQ_SET_CONFIGURATION:
+ nret = std_req_set_configuration(udc);
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ if (bcall_back == FALSE) {
+ if (udc->ep0state == EP0_IN_STATUS_PHASE) {
+ if (nret >= 0) {
+ /*--------------------------------------*/
+ /* Status Stage */
+ nret = EP0_send_NULL(udc, TRUE);
+ }
+ }
+
+ } else {
+ spin_unlock(&udc->lock);
+ nret = udc->driver->setup(&udc->gadget, &udc->ctrl);
+ spin_lock(&udc->lock);
+ }
+
+ if (nret < 0)
+ udc->ep0state = EP0_IDLE;
+
+ return nret;
+}
+
+/*-------------------------------------------------------------------------*/
+static inline int _nbu2ss_ep0_in_data_stage(struct nbu2ss_udc *udc)
+{
+ int nret;
+ struct nbu2ss_req *req;
+ struct nbu2ss_ep *ep = &udc->ep[0];
+
+ if (list_empty(&ep->queue))
+ req = NULL;
+ else
+ req = list_entry(ep->queue.next, struct nbu2ss_req, queue);
+
+ if (req == NULL)
+ req = &udc->ep0_req;
+
+ req->req.actual += req->div_len;
+ req->div_len = 0;
+
+ nret = _nbu2ss_ep0_in_transfer(udc, ep, req);
+ if (nret == 0) {
+ udc->ep0state = EP0_OUT_STATUS_PAHSE;
+ EP0_receive_NULL(udc, TRUE);
+ }
+
+ return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+static inline int _nbu2ss_ep0_out_data_stage(struct nbu2ss_udc *udc)
+{
+ int nret;
+ struct nbu2ss_req *req;
+ struct nbu2ss_ep *ep = &udc->ep[0];
+
+ if (list_empty(&ep->queue))
+ req = NULL;
+ else
+ req = list_entry(ep->queue.next, struct nbu2ss_req, queue);
+
+ if (req == NULL)
+ req = &udc->ep0_req;
+
+ nret = _nbu2ss_ep0_out_transfer(udc, ep, req);
+ if (nret == 0) {
+ udc->ep0state = EP0_IN_STATUS_PHASE;
+ EP0_send_NULL(udc, TRUE);
+
+ } else if (nret < 0) {
+ _nbu2ss_bitset(&udc->p_regs->EP0_CONTROL, EP0_BCLR);
+ req->req.status = nret;
+ }
+
+ return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+static inline int _nbu2ss_ep0_status_stage(struct nbu2ss_udc *udc)
+{
+ struct nbu2ss_req *req;
+ struct nbu2ss_ep *ep = &udc->ep[0];
+
+ if (list_empty(&ep->queue))
+ req = NULL;
+ else
+ req = list_entry(ep->queue.next, struct nbu2ss_req, queue);
+
+ if (req == NULL) {
+ req = &udc->ep0_req;
+ if (req->req.complete)
+ req->req.complete(&ep->ep, &req->req);
+
+ } else {
+ if (req->req.complete)
+ _nbu2ss_ep_done(ep, req, 0);
+ }
+
+ udc->ep0state = EP0_IDLE;
+
+ return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+static inline void _nbu2ss_ep0_int(struct nbu2ss_udc *udc)
+{
+ int i;
+ u32 status;
+ u32 intr;
+ int nret = -1;
+
+ status = _nbu2ss_readl(&udc->p_regs->EP0_STATUS);
+ intr = status & EP0_STATUS_RW_BIT;
+ _nbu2ss_writel(&udc->p_regs->EP0_STATUS, ~(u32)intr);
+
+ status &= (SETUP_INT | EP0_IN_INT | EP0_OUT_INT
+ | STG_END_INT | EP0_OUT_NULL_INT);
+
+ if (status == 0) {
+ dev_info(udc->dev, "%s Not Decode Interrupt\n", __func__);
+ dev_info(udc->dev, "EP0_STATUS = 0x%08x\n", intr);
+ return;
+ }
+
+ if (udc->gadget.speed == USB_SPEED_UNKNOWN)
+ udc->gadget.speed = _nbu2ss_get_speed(udc);
+
+ for (i = 0; i < EP0_END_XFER; i++) {
+ switch (udc->ep0state) {
+ case EP0_IDLE:
+ if (status & SETUP_INT) {
+ status = 0;
+ nret = _nbu2ss_decode_request(udc);
+ }
+ break;
+
+ case EP0_IN_DATA_PHASE:
+ if (status & EP0_IN_INT) {
+ status &= ~EP0_IN_INT;
+ nret = _nbu2ss_ep0_in_data_stage(udc);
+ }
+ break;
+
+ case EP0_OUT_DATA_PHASE:
+ if (status & EP0_OUT_INT) {
+ status &= ~EP0_OUT_INT;
+ nret = _nbu2ss_ep0_out_data_stage(udc);
+ }
+ break;
+
+ case EP0_IN_STATUS_PHASE:
+ if ((status & STG_END_INT) || (status & SETUP_INT)) {
+ status &= ~(STG_END_INT | EP0_IN_INT);
+ nret = _nbu2ss_ep0_status_stage(udc);
+ }
+ break;
+
+ case EP0_OUT_STATUS_PAHSE:
+ if ((status & STG_END_INT)
+ || (status & SETUP_INT)
+ || (status & EP0_OUT_NULL_INT)) {
+ status &= ~(STG_END_INT
+ | EP0_OUT_INT
+ | EP0_OUT_NULL_INT);
+
+ nret = _nbu2ss_ep0_status_stage(udc);
+ }
+
+ break;
+
+ default:
+ status = 0;
+ break;
+ }
+
+ if (status == 0)
+ break;
+ }
+
+ if (nret < 0) {
+ /* Send Stall */
+ _nbu2ss_set_endpoint_stall(udc, 0, TRUE);
+ }
+}
+
+/*-------------------------------------------------------------------------*/
+static void _nbu2ss_ep_done(
+ struct nbu2ss_ep *ep,
+ struct nbu2ss_req *req,
+ int status)
+{
+ struct nbu2ss_udc *udc = ep->udc;
+
+ list_del_init(&req->queue);
+
+ if (status == -ECONNRESET)
+ _nbu2ss_fifo_flush(udc, ep);
+
+ if (likely(req->req.status == -EINPROGRESS))
+ req->req.status = status;
+
+ if (ep->stalled)
+ _nbu2ss_epn_set_stall(udc, ep);
+ else {
+ if (!list_empty(&ep->queue))
+ _nbu2ss_restert_transfer(ep);
+ }
+
+#ifdef USE_DMA
+ if ((ep->direct == USB_DIR_OUT) && (ep->epnum > 0) &&
+ (req->req.dma != 0))
+ _nbu2ss_dma_unmap_single(udc, ep, req, USB_DIR_OUT);
+#endif
+
+ spin_unlock(&udc->lock);
+ req->req.complete(&ep->ep, &req->req);
+ spin_lock(&udc->lock);
+}
+
+/*-------------------------------------------------------------------------*/
+static inline void _nbu2ss_epn_in_int(
+ struct nbu2ss_udc *udc,
+ struct nbu2ss_ep *ep,
+ struct nbu2ss_req *req)
+{
+ int result = 0;
+ u32 status;
+
+ struct fc_regs *preg = udc->p_regs;
+
+ if (req->dma_flag)
+ return; /* DMA is forwarded */
+
+ req->req.actual += req->div_len;
+ req->div_len = 0;
+
+ if (req->req.actual != req->req.length) {
+ /*---------------------------------------------------------*/
+ /* remainder of data */
+ result = _nbu2ss_epn_in_transfer(udc, ep, req);
+
+ } else {
+ if (req->zero && ((req->req.actual % ep->ep.maxpacket) == 0)) {
+
+ status =
+ _nbu2ss_readl(&preg->EP_REGS[ep->epnum-1].EP_STATUS);
+
+ if ((status & EPn_IN_FULL) == 0) {
+ /*-----------------------------------------*/
+ /* 0 Length Packet */
+ req->zero = false;
+ _nbu2ss_zero_len_pkt(udc, ep->epnum);
+ }
+ return;
+ }
+ }
+
+ if (result <= 0) {
+ /*---------------------------------------------------------*/
+ /* Complete */
+ _nbu2ss_ep_done(ep, req, result);
+ }
+}
+
+/*-------------------------------------------------------------------------*/
+static inline void _nbu2ss_epn_out_int(
+ struct nbu2ss_udc *udc,
+ struct nbu2ss_ep *ep,
+ struct nbu2ss_req *req)
+{
+ int result;
+
+ result = _nbu2ss_epn_out_transfer(udc, ep, req);
+ if (result <= 0)
+ _nbu2ss_ep_done(ep, req, result);
+}
+
+/*-------------------------------------------------------------------------*/
+static inline void _nbu2ss_epn_in_dma_int(
+ struct nbu2ss_udc *udc,
+ struct nbu2ss_ep *ep,
+ struct nbu2ss_req *req)
+{
+ u32 mpkt;
+ u32 size;
+ struct usb_request *preq;
+
+ preq = &req->req;
+
+ if (req->dma_flag == FALSE)
+ return;
+
+ preq->actual += req->div_len;
+ req->div_len = 0;
+ req->dma_flag = FALSE;
+
+#ifdef USE_DMA
+ _nbu2ss_dma_unmap_single(udc, ep, req, USB_DIR_IN);
+#endif
+
+ if (preq->actual != preq->length) {
+ _nbu2ss_epn_in_transfer(udc, ep, req);
+ } else {
+ mpkt = ep->ep.maxpacket;
+ size = preq->actual % mpkt;
+ if (size > 0) {
+ if (((preq->actual & 0x03) == 0) && (size < mpkt))
+ _nbu2ss_ep_in_end(udc, ep->epnum, 0, 0);
+ } else {
+ _nbu2ss_epn_in_int(udc, ep, req);
+ }
+ }
+}
+
+/*-------------------------------------------------------------------------*/
+static inline void _nbu2ss_epn_out_dma_int(
+ struct nbu2ss_udc *udc,
+ struct nbu2ss_ep *ep,
+ struct nbu2ss_req *req)
+{
+ int i;
+ u32 num;
+ u32 dmacnt, ep_dmacnt;
+ u32 mpkt;
+ struct fc_regs *preg = udc->p_regs;
+
+ num = ep->epnum - 1;
+
+ if (req->req.actual == req->req.length) {
+ if ((req->req.length % ep->ep.maxpacket) && !req->zero) {
+ req->div_len = 0;
+ req->dma_flag = FALSE;
+ _nbu2ss_ep_done(ep, req, 0);
+ return;
+ }
+ }
+
+ ep_dmacnt = _nbu2ss_readl(&preg->EP_REGS[num].EP_LEN_DCNT)
+ & EPn_DMACNT;
+ ep_dmacnt >>= 16;
+
+ for (i = 0; i < EPC_PLL_LOCK_COUNT; i++) {
+ dmacnt = _nbu2ss_readl(&preg->EP_DCR[num].EP_DCR1)
+ & DCR1_EPn_DMACNT;
+ dmacnt >>= 16;
+ if (ep_dmacnt == dmacnt)
+ break;
+ }
+
+ _nbu2ss_bitclr(&preg->EP_DCR[num].EP_DCR1, DCR1_EPn_REQEN);
+
+ if (dmacnt != 0) {
+ mpkt = ep->ep.maxpacket;
+ if ((req->div_len % mpkt) == 0)
+ req->div_len -= mpkt * dmacnt;
+ }
+
+ if ((req->req.actual % ep->ep.maxpacket) > 0) {
+ if (req->req.actual == req->div_len) {
+ req->div_len = 0;
+ req->dma_flag = FALSE;
+ _nbu2ss_ep_done(ep, req, 0);
+ return;
+ }
+ }
+
+ req->req.actual += req->div_len;
+ req->div_len = 0;
+ req->dma_flag = FALSE;
+
+ _nbu2ss_epn_out_int(udc, ep, req);
+}
+
+/*-------------------------------------------------------------------------*/
+static inline void _nbu2ss_epn_int(struct nbu2ss_udc *udc, u32 epnum)
+{
+ u32 num;
+ u32 status;
+
+ struct nbu2ss_req *req;
+ struct nbu2ss_ep *ep = &udc->ep[epnum];
+
+ num = epnum - 1;
+
+ /* Interrupt Status */
+ status = _nbu2ss_readl(&udc->p_regs->EP_REGS[num].EP_STATUS);
+
+ /* Interrupt Clear */
+ _nbu2ss_writel(&udc->p_regs->EP_REGS[num].EP_STATUS, ~(u32)status);
+
+ if (list_empty(&ep->queue))
+ req = NULL;
+ else
+ req = list_entry(ep->queue.next, struct nbu2ss_req, queue);
+
+ if (req == NULL) {
+ /* pr_warn("=== %s(%d) req == NULL\n", __func__, epnum); */
+ return;
+ }
+
+ if (status & EPn_OUT_END_INT) {
+ status &= ~EPn_OUT_INT;
+ _nbu2ss_epn_out_dma_int(udc, ep, req);
+ }
+
+ if (status & EPn_OUT_INT)
+ _nbu2ss_epn_out_int(udc, ep, req);
+
+ if (status & EPn_IN_END_INT) {
+ status &= ~EPn_IN_INT;
+ _nbu2ss_epn_in_dma_int(udc, ep, req);
+ }
+
+ if (status & EPn_IN_INT)
+ _nbu2ss_epn_in_int(udc, ep, req);
+}
+
+/*-------------------------------------------------------------------------*/
+static inline void _nbu2ss_ep_int(struct nbu2ss_udc *udc, u32 epnum)
+{
+ if (epnum == 0)
+ _nbu2ss_ep0_int(udc);
+ else
+ _nbu2ss_epn_int(udc, epnum);
+}
+
+/*-------------------------------------------------------------------------*/
+static void _nbu2ss_ep0_enable(struct nbu2ss_udc *udc)
+{
+ _nbu2ss_bitset(&udc->p_regs->EP0_CONTROL, (EP0_AUTO | EP0_BCLR));
+ _nbu2ss_writel(&udc->p_regs->EP0_INT_ENA, EP0_INT_EN_BIT);
+}
+
+#if 0
+/*-------------------------------------------------------------------------*/
+static void _nbu2ss_ep0_disable(struct nbu2ss_udc *udc)
+{
+ _nbu2ss_bitclr(&udc->p_regs->EP0_INT_ENA, EP0_INT_EN_BIT);
+
+ _nbu2ss_bitset(&udc->p_regs->EP0_CONTROL
+ , (EP0_BCLR | EP0_INAK | EP0_ONAK | EP0_BCLR));
+
+ _nbu2ss_bitclr(&udc->p_regs->EP0_CONTROL, EP0_AUTO);
+}
+#endif
+
+/*-------------------------------------------------------------------------*/
+static int _nbu2ss_nuke(struct nbu2ss_udc *udc,
+ struct nbu2ss_ep *ep,
+ int status)
+{
+ struct nbu2ss_req *req;
+
+ /* Endpoint Disable */
+ _nbu2ss_epn_exit(udc, ep);
+
+ /* DMA Disable */
+ _nbu2ss_ep_dma_exit(udc, ep);
+
+ if (list_empty(&ep->queue))
+ return 0;
+
+ /* called with irqs blocked */
+ list_for_each_entry(req, &ep->queue, queue) {
+ _nbu2ss_ep_done(ep, req, status);
+ }
+
+ return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+static void _nbu2ss_quiesce(struct nbu2ss_udc *udc)
+{
+ struct nbu2ss_ep *ep;
+
+ udc->gadget.speed = USB_SPEED_UNKNOWN;
+
+ _nbu2ss_nuke(udc, &udc->ep[0], -ESHUTDOWN);
+
+ /* Endpoint n */
+ list_for_each_entry(ep, &udc->gadget.ep_list, ep.ep_list) {
+ _nbu2ss_nuke(udc, ep, -ESHUTDOWN);
+ }
+}
+
+/*-------------------------------------------------------------------------*/
+static int _nbu2ss_pullup(struct nbu2ss_udc *udc, int is_on)
+{
+ u32 reg_dt;
+
+ if (udc->vbus_active == 0)
+ return -ESHUTDOWN;
+
+ if (is_on) {
+ /* D+ Pullup */
+/* INFO(" --- D+ Pullup\n"); */
+
+ if (udc->driver) {
+ reg_dt = (_nbu2ss_readl(&udc->p_regs->USB_CONTROL)
+ | PUE2) & ~(u32)CONNECTB;
+
+ _nbu2ss_writel(&udc->p_regs->USB_CONTROL, reg_dt);
+ }
+
+ } else {
+ /* D+ Pulldown */
+/* INFO(" --- D+ Pulldown\n"); */
+
+ reg_dt = (_nbu2ss_readl(&udc->p_regs->USB_CONTROL) | CONNECTB)
+ & ~(u32)PUE2;
+
+ _nbu2ss_writel(&udc->p_regs->USB_CONTROL, reg_dt);
+ udc->gadget.speed = USB_SPEED_UNKNOWN;
+ }
+
+ return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+static void _nbu2ss_fifo_flush(struct nbu2ss_udc *udc, struct nbu2ss_ep *ep)
+{
+ struct fc_regs *p = udc->p_regs;
+
+ if (udc->vbus_active == 0)
+ return;
+
+ if (ep->epnum == 0) {
+ /* EP0 */
+ _nbu2ss_bitset(&p->EP0_CONTROL, EP0_BCLR);
+
+ } else {
+ /* EPn */
+ _nbu2ss_ep_dma_abort(udc, ep);
+ _nbu2ss_bitset(&p->EP_REGS[ep->epnum - 1].EP_CONTROL, EPn_BCLR);
+ }
+}
+
+/*-------------------------------------------------------------------------*/
+static int _nbu2ss_enable_controller(struct nbu2ss_udc *udc)
+{
+ int waitcnt = 0;
+
+ if (udc->udc_enabled)
+ return 0;
+
+#if 0
+ emxx_open_clockgate(EMXX_CLK_USB1);
+ /* emxx_clkctrl_off(EMXX_CLKCTRL_USB1); */
+ /* emxx_clkctrl_on(EMXX_CLKCTRL_USB1); */
+ emxx_unreset_device(EMXX_RST_USB1);
+#endif
+ /*
+ Reset
+ */
+ _nbu2ss_bitset(&udc->p_regs->EPCTR, (DIRPD | EPC_RST));
+ udelay(EPC_RST_DISABLE_TIME); /* 1us wait */
+
+ _nbu2ss_bitclr(&udc->p_regs->EPCTR, DIRPD);
+ mdelay(EPC_DIRPD_DISABLE_TIME); /* 1ms wait */
+
+ _nbu2ss_bitclr(&udc->p_regs->EPCTR, EPC_RST);
+
+ _nbu2ss_writel(&udc->p_regs->AHBSCTR, WAIT_MODE);
+
+#if 0
+ /* DMA Mode Setting */
+ if ((system_rev & EMXX_REV_MASK) == EMXX_REV_ES1) {
+ _nbu2ss_bitset(&udc->p_regs->AHBMCTR, BURST_TYPE);
+ _nbu2ss_bitclr(&udc->p_regs->AHBMCTR, HTRANS_MODE);
+ } else
+#endif
+ _nbu2ss_writel(&udc->p_regs->AHBMCTR,
+ HBUSREQ_MODE | HTRANS_MODE | WBURST_TYPE);
+
+ while (!(_nbu2ss_readl(&udc->p_regs->EPCTR) & PLL_LOCK)) {
+ waitcnt++;
+ udelay(1); /* 1us wait */
+ if (waitcnt == EPC_PLL_LOCK_COUNT) {
+ dev_err(udc->dev, "*** Reset Cancel failed\n");
+ return -EINVAL;
+ }
+ };
+
+#if 0
+ if ((system_rev & EMXX_REV_MASK) < EMXX_REV_ES3)
+#endif
+ _nbu2ss_bitset(&udc->p_regs->UTMI_CHARACTER_1, USB_SQUSET);
+
+ _nbu2ss_bitset(&udc->p_regs->USB_CONTROL, (INT_SEL | SOF_RCV));
+
+ /* EP0 */
+ _nbu2ss_ep0_enable(udc);
+
+ /* USB Interrupt Enable */
+ _nbu2ss_bitset(&udc->p_regs->USB_INT_ENA, USB_INT_EN_BIT);
+
+ udc->udc_enabled = TRUE;
+
+ return 0;
+}
+
+
+/*-------------------------------------------------------------------------*/
+static void _nbu2ss_reset_controller(struct nbu2ss_udc *udc)
+{
+ _nbu2ss_bitset(&udc->p_regs->EPCTR, EPC_RST);
+ _nbu2ss_bitclr(&udc->p_regs->EPCTR, EPC_RST);
+}
+
+/*-------------------------------------------------------------------------*/
+static void _nbu2ss_disable_controller(struct nbu2ss_udc *udc)
+{
+ if (udc->udc_enabled) {
+ udc->udc_enabled = FALSE;
+ _nbu2ss_reset_controller(udc);
+ _nbu2ss_bitset(&udc->p_regs->EPCTR, (DIRPD | EPC_RST));
+ }
+#if 0
+ emxx_reset_device(EMXX_RST_USB1);
+ /* emxx_clkctrl_on(EMXX_CLKCTRL_USB1); */
+ emxx_close_clockgate(EMXX_CLK_USB1);
+#endif
+}
+
+/*-------------------------------------------------------------------------*/
+static inline void _nbu2ss_check_vbus(struct nbu2ss_udc *udc)
+{
+ int nret;
+ u32 reg_dt;
+
+ /* chattering */
+ mdelay(VBUS_CHATTERING_MDELAY); /* wait (ms) */
+
+ /* VBUS ON Check*/
+ reg_dt = gpio_get_value(VBUS_VALUE);
+ if (reg_dt == 0) {
+
+ udc->linux_suspended = 0;
+
+ _nbu2ss_reset_controller(udc);
+ dev_info(udc->dev, " ----- VBUS OFF\n");
+
+ if (udc->vbus_active == 1) {
+ /* VBUS OFF */
+ udc->vbus_active = 0;
+ if (udc->usb_suspended) {
+ udc->usb_suspended = 0;
+ /* _nbu2ss_reset_controller(udc); */
+ }
+ udc->devstate = USB_STATE_NOTATTACHED;
+
+ _nbu2ss_quiesce(udc);
+ if (udc->driver) {
+ spin_unlock(&udc->lock);
+ udc->driver->disconnect(&udc->gadget);
+ spin_lock(&udc->lock);
+ }
+
+ _nbu2ss_disable_controller(udc);
+ }
+ } else {
+ mdelay(5); /* wait (5ms) */
+ reg_dt = gpio_get_value(VBUS_VALUE);
+ if (reg_dt == 0)
+ return;
+
+ dev_info(udc->dev, " ----- VBUS ON\n");
+
+ if (udc->linux_suspended)
+ return;
+
+ if (udc->vbus_active == 0) {
+ /* VBUS ON */
+ udc->vbus_active = 1;
+ udc->devstate = USB_STATE_POWERED;
+
+ nret = _nbu2ss_enable_controller(udc);
+ if (nret < 0) {
+ _nbu2ss_disable_controller(udc);
+ udc->vbus_active = 0;
+ return;
+ }
+
+ _nbu2ss_pullup(udc, 1);
+
+#ifdef UDC_DEBUG_DUMP
+ _nbu2ss_dump_register(udc);
+#endif /* UDC_DEBUG_DUMP */
+
+ } else {
+ if (udc->devstate == USB_STATE_POWERED)
+ _nbu2ss_pullup(udc, 1);
+ }
+ }
+}
+
+/*-------------------------------------------------------------------------*/
+static inline void _nbu2ss_int_bus_reset(struct nbu2ss_udc *udc)
+{
+ udc->devstate = USB_STATE_DEFAULT;
+ udc->remote_wakeup = 0;
+
+ _nbu2ss_quiesce(udc);
+
+ udc->ep0state = EP0_IDLE;
+}
+
+/*-------------------------------------------------------------------------*/
+static inline void _nbu2ss_int_usb_resume(struct nbu2ss_udc *udc)
+{
+ if (udc->usb_suspended == 1) {
+ udc->usb_suspended = 0;
+ if (udc->driver && udc->driver->resume) {
+ spin_unlock(&udc->lock);
+ udc->driver->resume(&udc->gadget);
+ spin_lock(&udc->lock);
+ }
+ }
+}
+
+/*-------------------------------------------------------------------------*/
+static inline void _nbu2ss_int_usb_suspend(struct nbu2ss_udc *udc)
+{
+ u32 reg_dt;
+
+ if (udc->usb_suspended == 0) {
+ reg_dt = gpio_get_value(VBUS_VALUE);
+
+ if (reg_dt == 0)
+ return;
+
+ udc->usb_suspended = 1;
+ if (udc->driver && udc->driver->suspend) {
+ spin_unlock(&udc->lock);
+ udc->driver->suspend(&udc->gadget);
+ spin_lock(&udc->lock);
+ }
+
+ _nbu2ss_bitset(&udc->p_regs->USB_CONTROL, SUSPEND);
+ }
+}
+
+/*-------------------------------------------------------------------------*/
+/* VBUS (GPIO153) Interrupt */
+static irqreturn_t _nbu2ss_vbus_irq(int irq, void *_udc)
+{
+ struct nbu2ss_udc *udc = (struct nbu2ss_udc *)_udc;
+
+ spin_lock(&udc->lock);
+ _nbu2ss_check_vbus(udc);
+ spin_unlock(&udc->lock);
+
+ return IRQ_HANDLED;
+}
+
+/*-------------------------------------------------------------------------*/
+/* Interrupt (udc) */
+static irqreturn_t _nbu2ss_udc_irq(int irq, void *_udc)
+{
+ u8 suspend_flag = 0;
+ u32 status;
+ u32 epnum, int_bit;
+
+ struct nbu2ss_udc *udc = (struct nbu2ss_udc *)_udc;
+ struct fc_regs *preg = udc->p_regs;
+
+ if (gpio_get_value(VBUS_VALUE) == 0) {
+ _nbu2ss_writel(&preg->USB_INT_STA, ~USB_INT_STA_RW);
+ _nbu2ss_writel(&preg->USB_INT_ENA, 0);
+ return IRQ_HANDLED;
+ }
+
+ spin_lock(&udc->lock);
+
+ for (;;) {
+ if (gpio_get_value(VBUS_VALUE) == 0) {
+ _nbu2ss_writel(&preg->USB_INT_STA, ~USB_INT_STA_RW);
+ _nbu2ss_writel(&preg->USB_INT_ENA, 0);
+ status = 0;
+ } else
+ status = _nbu2ss_readl(&preg->USB_INT_STA);
+
+ if (status == 0)
+ break;
+
+ _nbu2ss_writel(&preg->USB_INT_STA, ~(status & USB_INT_STA_RW));
+
+ if (status & USB_RST_INT) {
+ /* USB Reset */
+ _nbu2ss_int_bus_reset(udc);
+ }
+
+ if (status & RSUM_INT) {
+ /* Resume */
+ _nbu2ss_int_usb_resume(udc);
+ }
+
+ if (status & SPND_INT) {
+ /* Suspend */
+ suspend_flag = 1;
+ }
+
+ if (status & EPn_INT) {
+ /* EP INT */
+ int_bit = status >> 8;
+
+ for (epnum = 0; epnum < NUM_ENDPOINTS; epnum++) {
+
+ if (0x01 & int_bit)
+ _nbu2ss_ep_int(udc, epnum);
+
+ int_bit >>= 1;
+
+ if (int_bit == 0)
+ break;
+ }
+ }
+ }
+
+ if (suspend_flag)
+ _nbu2ss_int_usb_suspend(udc);
+
+ spin_unlock(&udc->lock);
+
+ return IRQ_HANDLED;
+}
+
+/*-------------------------------------------------------------------------*/
+/* usb_ep_ops */
+static int nbu2ss_ep_enable(
+ struct usb_ep *_ep,
+ const struct usb_endpoint_descriptor *desc)
+{
+ u8 ep_type;
+ unsigned long flags;
+
+ struct nbu2ss_ep *ep;
+ struct nbu2ss_udc *udc;
+
+ if ((_ep == NULL) || (desc == NULL)) {
+ pr_err(" *** %s, bad param\n", __func__);
+ return -EINVAL;
+ }
+
+ ep = container_of(_ep, struct nbu2ss_ep, ep);
+ if ((ep == NULL) || (ep->udc == NULL)) {
+ pr_err(" *** %s, ep == NULL !!\n", __func__);
+ return -EINVAL;
+ }
+
+ ep_type = usb_endpoint_type(desc);
+ if ((ep_type == USB_ENDPOINT_XFER_CONTROL)
+ || (ep_type == USB_ENDPOINT_XFER_ISOC)) {
+
+ pr_err(" *** %s, bat bmAttributes\n", __func__);
+ return -EINVAL;
+ }
+
+ udc = ep->udc;
+ if (udc->vbus_active == 0)
+ return -ESHUTDOWN;
+
+ if ((udc->driver == NULL)
+ || (udc->gadget.speed == USB_SPEED_UNKNOWN)) {
+
+ dev_err(ep->udc->dev, " *** %s, udc !!\n", __func__);
+ return -ESHUTDOWN;
+ }
+
+ spin_lock_irqsave(&udc->lock, flags);
+
+ ep->desc = desc;
+ ep->epnum = usb_endpoint_num(desc);
+ ep->direct = desc->bEndpointAddress & USB_ENDPOINT_DIR_MASK;
+ ep->ep_type = ep_type;
+ ep->wedged = 0;
+ ep->halted = FALSE;
+ ep->stalled = FALSE;
+
+ ep->ep.maxpacket = le16_to_cpu(desc->wMaxPacketSize);
+
+ /* DMA setting */
+ _nbu2ss_ep_dma_init(udc, ep);
+
+ /* Endpoint setting */
+ _nbu2ss_ep_init(udc, ep);
+
+ spin_unlock_irqrestore(&udc->lock, flags);
+
+ return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+static int nbu2ss_ep_disable(struct usb_ep *_ep)
+{
+ struct nbu2ss_ep *ep;
+ struct nbu2ss_udc *udc;
+ unsigned long flags;
+
+ if (_ep == NULL) {
+ pr_err(" *** %s, bad param\n", __func__);
+ return -EINVAL;
+ }
+
+ ep = container_of(_ep, struct nbu2ss_ep, ep);
+ if ((ep == NULL) || (ep->udc == NULL)) {
+ pr_err("udc: *** %s, ep == NULL !!\n", __func__);
+ return -EINVAL;
+ }
+
+ udc = ep->udc;
+ if (udc->vbus_active == 0)
+ return -ESHUTDOWN;
+
+ spin_lock_irqsave(&udc->lock, flags);
+ _nbu2ss_nuke(udc, ep, -EINPROGRESS); /* dequeue request */
+ spin_unlock_irqrestore(&udc->lock, flags);
+
+ return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+static struct usb_request *nbu2ss_ep_alloc_request(
+ struct usb_ep *ep,
+ gfp_t gfp_flags)
+{
+ struct nbu2ss_req *req;
+
+ req = kzalloc(sizeof(*req), gfp_flags);
+ if (!req)
+ return 0;
+
+#ifdef USE_DMA
+ req->req.dma = DMA_ADDR_INVALID;
+#endif
+ INIT_LIST_HEAD(&req->queue);
+
+ return &req->req;
+}
+
+/*-------------------------------------------------------------------------*/
+static void nbu2ss_ep_free_request(
+ struct usb_ep *_ep,
+ struct usb_request *_req)
+{
+ struct nbu2ss_req *req;
+
+ if (_req != NULL) {
+ req = container_of(_req, struct nbu2ss_req, req);
+
+ kfree(req);
+ }
+}
+
+/*-------------------------------------------------------------------------*/
+static int nbu2ss_ep_queue(
+ struct usb_ep *_ep,
+ struct usb_request *_req,
+ gfp_t gfp_flags)
+{
+ struct nbu2ss_req *req;
+ struct nbu2ss_ep *ep;
+ struct nbu2ss_udc *udc;
+ unsigned long flags;
+ bool bflag;
+ int result = -EINVAL;
+
+ /* catch various bogus parameters */
+ if ((_ep == NULL) || (_req == NULL)) {
+ if (_ep == NULL)
+ pr_err("udc: %s --- _ep == NULL\n", __func__);
+
+ if (_req == NULL)
+ pr_err("udc: %s --- _req == NULL\n", __func__);
+
+ return -EINVAL;
+ }
+
+ req = container_of(_req, struct nbu2ss_req, req);
+ if (unlikely
+ (!_req->complete || !_req->buf
+ || !list_empty(&req->queue))) {
+
+ if (!_req->complete)
+ pr_err("udc: %s --- !_req->complete\n", __func__);
+
+ if (!_req->buf)
+ pr_err("udc:%s --- !_req->buf\n", __func__);
+
+ if (!list_empty(&req->queue))
+ pr_err("%s --- !list_empty(&req->queue)\n", __func__);
+
+ return -EINVAL;
+ }
+
+ ep = container_of(_ep, struct nbu2ss_ep, ep);
+ udc = ep->udc;
+
+/* INFO("=== %s(ep%d), zero=%d\n", __func__, ep->epnum, _req->zero); */
+
+ if (udc->vbus_active == 0) {
+ dev_info(udc->dev, "Can't ep_queue (VBUS OFF)\n");
+ return -ESHUTDOWN;
+ }
+
+ if (unlikely(!udc->driver)) {
+ dev_err(udc->dev, "%s, bogus device state %p\n", __func__,
+ udc->driver);
+ return -ESHUTDOWN;
+ }
+
+ spin_lock_irqsave(&udc->lock, flags);
+
+#ifdef USE_DMA
+ if ((u32)req->req.buf & 0x3)
+ req->unaligned = TRUE;
+ else
+ req->unaligned = FALSE;
+
+ if (req->unaligned) {
+ if (ep->virt_buf == NULL)
+ ep->virt_buf = (u8 *)dma_alloc_coherent(
+ NULL, PAGE_SIZE,
+ &ep->phys_buf, GFP_ATOMIC | GFP_DMA);
+ if (ep->epnum > 0) {
+ if (ep->direct == USB_DIR_IN)
+ memcpy(ep->virt_buf, req->req.buf,
+ req->req.length);
+ }
+ }
+
+ if ((ep->epnum > 0) && (ep->direct == USB_DIR_OUT) &&
+ (req->req.dma != 0))
+ _nbu2ss_dma_map_single(udc, ep, req, USB_DIR_OUT);
+#endif
+
+ _req->status = -EINPROGRESS;
+ _req->actual = 0;
+
+ bflag = list_empty(&ep->queue);
+ list_add_tail(&req->queue, &ep->queue);
+
+ if ((bflag != FALSE) && (ep->stalled == FALSE)) {
+
+ result = _nbu2ss_start_transfer(udc, ep, req, FALSE);
+ if (result < 0) {
+ dev_err(udc->dev, " *** %s, result = %d\n", __func__,
+ result);
+ list_del(&req->queue);
+ } else if ((ep->epnum > 0) && (ep->direct == USB_DIR_OUT)) {
+#ifdef USE_DMA
+ if (req->req.length < 4 &&
+ req->req.length == req->req.actual)
+#else
+ if (req->req.length == req->req.actual)
+#endif
+ _nbu2ss_ep_done(ep, req, result);
+ }
+ }
+
+ spin_unlock_irqrestore(&udc->lock, flags);
+
+ return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+static int nbu2ss_ep_dequeue(
+ struct usb_ep *_ep,
+ struct usb_request *_req)
+{
+ struct nbu2ss_req *req;
+ struct nbu2ss_ep *ep;
+ struct nbu2ss_udc *udc;
+ unsigned long flags;
+
+ /*INFO("=== %s()\n", __func__);*/
+
+ /* catch various bogus parameters */
+ if ((_ep == NULL) || (_req == NULL)) {
+ /* pr_err("%s, bad param(1)\n", __func__); */
+ return -EINVAL;
+ }
+
+ ep = container_of(_ep, struct nbu2ss_ep, ep);
+ if (!ep) {
+ pr_err("%s, ep == NULL !!\n", __func__);
+ return -EINVAL;
+ }
+
+ udc = ep->udc;
+ if (udc == NULL)
+ return -EINVAL;
+
+ spin_lock_irqsave(&udc->lock, flags);
+
+ /* 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) {
+ spin_unlock_irqrestore(&udc->lock, flags);
+ pr_debug("%s no queue(EINVAL)\n", __func__);
+ return -EINVAL;
+ }
+
+ _nbu2ss_ep_done(ep, req, -ECONNRESET);
+
+ spin_unlock_irqrestore(&udc->lock, flags);
+
+ return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+static int nbu2ss_ep_set_halt(struct usb_ep *_ep, int value)
+{
+ u8 ep_adrs;
+ unsigned long flags;
+
+ struct nbu2ss_ep *ep;
+ struct nbu2ss_udc *udc;
+
+/* INFO("=== %s()\n", __func__); */
+
+ if (!_ep) {
+ pr_err("%s, bad param\n", __func__);
+ return -EINVAL;
+ }
+
+ ep = container_of(_ep, struct nbu2ss_ep, ep);
+ if (!ep) {
+ pr_err("%s, bad ep\n", __func__);
+ return -EINVAL;
+ }
+
+ udc = ep->udc;
+ if (!udc) {
+ dev_err(ep->udc->dev, " *** %s, bad udc\n", __func__);
+ return -EINVAL;
+ }
+
+ spin_lock_irqsave(&udc->lock, flags);
+
+ ep_adrs = ep->epnum | ep->direct;
+ if (value == 0) {
+ _nbu2ss_set_endpoint_stall(udc, ep_adrs, value);
+ ep->stalled = FALSE;
+ } else {
+ if (list_empty(&ep->queue))
+ _nbu2ss_epn_set_stall(udc, ep);
+ else
+ ep->stalled = TRUE;
+ }
+
+ if (value == 0)
+ ep->wedged = 0;
+
+ spin_unlock_irqrestore(&udc->lock, flags);
+
+ return 0;
+}
+
+static int nbu2ss_ep_set_wedge(struct usb_ep *_ep)
+{
+ return nbu2ss_ep_set_halt(_ep, 1);
+}
+
+/*-------------------------------------------------------------------------*/
+static int nbu2ss_ep_fifo_status(struct usb_ep *_ep)
+{
+ u32 data;
+ struct nbu2ss_ep *ep;
+ struct nbu2ss_udc *udc;
+ unsigned long flags;
+ struct fc_regs *preg;
+
+/* INFO("=== %s()\n", __func__); */
+
+ if (!_ep) {
+ pr_err("%s, bad param\n", __func__);
+ return -EINVAL;
+ }
+
+ ep = container_of(_ep, struct nbu2ss_ep, ep);
+ if (!ep) {
+ pr_err("%s, bad ep\n", __func__);
+ return -EINVAL;
+ }
+
+ udc = ep->udc;
+ if (!udc) {
+ dev_err(ep->udc->dev, "%s, bad udc\n", __func__);
+ return -EINVAL;
+ }
+
+ preg = udc->p_regs;
+
+ data = gpio_get_value(VBUS_VALUE);
+ if (data == 0)
+ return -EINVAL;
+
+ spin_lock_irqsave(&udc->lock, flags);
+
+ if (ep->epnum == 0) {
+ data = _nbu2ss_readl(&preg->EP0_LENGTH) & EP0_LDATA;
+
+ } else {
+ data = _nbu2ss_readl(&preg->EP_REGS[ep->epnum-1].EP_LEN_DCNT)
+ & EPn_LDATA;
+ }
+
+ spin_unlock_irqrestore(&udc->lock, flags);
+
+ return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+static void nbu2ss_ep_fifo_flush(struct usb_ep *_ep)
+{
+ u32 data;
+ struct nbu2ss_ep *ep;
+ struct nbu2ss_udc *udc;
+ unsigned long flags;
+
+/* INFO("=== %s()\n", __func__); */
+
+ if (!_ep) {
+ pr_err("udc: %s, bad param\n", __func__);
+ return;
+ }
+
+ ep = container_of(_ep, struct nbu2ss_ep, ep);
+ if (!_ep) {
+ pr_err("udc: %s, bad ep\n", __func__);
+ return;
+ }
+
+ udc = ep->udc;
+ if (!udc) {
+ dev_err(ep->udc->dev, "%s, bad udc\n", __func__);
+ return;
+ }
+
+ data = gpio_get_value(VBUS_VALUE);
+ if (data == 0)
+ return;
+
+ spin_lock_irqsave(&udc->lock, flags);
+ _nbu2ss_fifo_flush(udc, ep);
+ spin_unlock_irqrestore(&udc->lock, flags);
+}
+
+/*-------------------------------------------------------------------------*/
+static struct usb_ep_ops nbu2ss_ep_ops = {
+ .enable = nbu2ss_ep_enable,
+ .disable = nbu2ss_ep_disable,
+
+ .alloc_request = nbu2ss_ep_alloc_request,
+ .free_request = nbu2ss_ep_free_request,
+
+ .queue = nbu2ss_ep_queue,
+ .dequeue = nbu2ss_ep_dequeue,
+
+ .set_halt = nbu2ss_ep_set_halt,
+ .set_wedge = nbu2ss_ep_set_wedge,
+
+ .fifo_status = nbu2ss_ep_fifo_status,
+ .fifo_flush = nbu2ss_ep_fifo_flush,
+};
+
+
+/*-------------------------------------------------------------------------*/
+/* usb_gadget_ops */
+
+/*-------------------------------------------------------------------------*/
+static int nbu2ss_gad_get_frame(struct usb_gadget *pgadget)
+{
+ u32 data;
+ struct nbu2ss_udc *udc;
+
+/* INFO("=== %s()\n", __func__); */
+
+ if (pgadget == NULL) {
+ pr_err("udc: %s, bad param\n", __func__);
+ return -EINVAL;
+ }
+
+ udc = container_of(pgadget, struct nbu2ss_udc, gadget);
+ if (udc == NULL) {
+ dev_err(&pgadget->dev, "%s, udc == NULL\n", __func__);
+ return -EINVAL;
+ }
+
+ data = gpio_get_value(VBUS_VALUE);
+ if (data == 0)
+ return -EINVAL;
+
+ data = _nbu2ss_readl(&udc->p_regs->USB_ADDRESS) & FRAME;
+
+ return data;
+}
+
+/*-------------------------------------------------------------------------*/
+static int nbu2ss_gad_wakeup(struct usb_gadget *pgadget)
+{
+ int i;
+ u32 data;
+
+ struct nbu2ss_udc *udc;
+
+/* INFO("=== %s()\n", __func__); */
+
+ if (pgadget == NULL) {
+ pr_err("%s, bad param\n", __func__);
+ return -EINVAL;
+ }
+
+ udc = container_of(pgadget, struct nbu2ss_udc, gadget);
+ if (udc == NULL) {
+ dev_err(&pgadget->dev, "%s, udc == NULL\n", __func__);
+ return -EINVAL;
+ }
+
+ data = gpio_get_value(VBUS_VALUE);
+ if (data == 0) {
+ dev_warn(&pgadget->dev, "VBUS LEVEL = %d\n", data);
+ return -EINVAL;
+ }
+
+ _nbu2ss_bitset(&udc->p_regs->EPCTR, PLL_RESUME);
+
+ for (i = 0; i < EPC_PLL_LOCK_COUNT; i++) {
+ data = _nbu2ss_readl(&udc->p_regs->EPCTR);
+
+ if (data & PLL_LOCK)
+ break;
+ }
+
+ _nbu2ss_bitclr(&udc->p_regs->EPCTR, PLL_RESUME);
+
+ return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+static int nbu2ss_gad_set_selfpowered(struct usb_gadget *pgadget,
+ int is_selfpowered)
+{
+ struct nbu2ss_udc *udc;
+ unsigned long flags;
+
+/* INFO("=== %s()\n", __func__); */
+
+ if (pgadget == NULL) {
+ pr_err("%s, bad param\n", __func__);
+ return -EINVAL;
+ }
+
+ udc = container_of(pgadget, struct nbu2ss_udc, gadget);
+
+ spin_lock_irqsave(&udc->lock, flags);
+ pgadget->is_selfpowered = (is_selfpowered != 0);
+ spin_unlock_irqrestore(&udc->lock, flags);
+
+ return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+static int nbu2ss_gad_vbus_session(struct usb_gadget *pgadget, int is_active)
+{
+/* INFO("=== %s()\n", __func__); */
+ return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+static int nbu2ss_gad_vbus_draw(struct usb_gadget *pgadget, unsigned mA)
+{
+ struct nbu2ss_udc *udc;
+ unsigned long flags;
+
+/* INFO("=== %s()\n", __func__); */
+
+ if (pgadget == NULL) {
+ pr_err("%s, bad param\n", __func__);
+ return -EINVAL;
+ }
+
+ udc = container_of(pgadget, struct nbu2ss_udc, gadget);
+
+ spin_lock_irqsave(&udc->lock, flags);
+ udc->mA = mA;
+ spin_unlock_irqrestore(&udc->lock, flags);
+
+ return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+static int nbu2ss_gad_pullup(struct usb_gadget *pgadget, int is_on)
+{
+ struct nbu2ss_udc *udc;
+ unsigned long flags;
+
+/* INFO("=== %s()\n", __func__); */
+
+ if (pgadget == NULL) {
+ pr_err("%s, bad param\n", __func__);
+ return -EINVAL;
+ }
+
+ udc = container_of(pgadget, struct nbu2ss_udc, gadget);
+
+ if (udc->driver == NULL) {
+ pr_warn("%s, Not Regist Driver\n", __func__);
+ return -EINVAL;
+ }
+
+ if (udc->vbus_active == 0)
+ return -ESHUTDOWN;
+
+ spin_lock_irqsave(&udc->lock, flags);
+ _nbu2ss_pullup(udc, is_on);
+ spin_unlock_irqrestore(&udc->lock, flags);
+
+ return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+static int nbu2ss_gad_ioctl(
+ struct usb_gadget *pgadget,
+ unsigned code,
+ unsigned long param)
+{
+/* INFO("=== %s()\n", __func__); */
+ return 0;
+}
+
+
+static const struct usb_gadget_ops nbu2ss_gadget_ops = {
+ .get_frame = nbu2ss_gad_get_frame,
+ .wakeup = nbu2ss_gad_wakeup,
+ .set_selfpowered = nbu2ss_gad_set_selfpowered,
+ .vbus_session = nbu2ss_gad_vbus_session,
+ .vbus_draw = nbu2ss_gad_vbus_draw,
+ .pullup = nbu2ss_gad_pullup,
+ .ioctl = nbu2ss_gad_ioctl,
+};
+
+static const char g_ep0_name[] = "ep0";
+static const char g_ep1_name[] = "ep1-bulk";
+static const char g_ep2_name[] = "ep2-bulk";
+static const char g_ep3_name[] = "ep3in-int";
+static const char g_ep4_name[] = "ep4-iso";
+static const char g_ep5_name[] = "ep5-iso";
+static const char g_ep6_name[] = "ep6-bulk";
+static const char g_ep7_name[] = "ep7-bulk";
+static const char g_ep8_name[] = "ep8in-int";
+static const char g_ep9_name[] = "ep9-iso";
+static const char g_epa_name[] = "epa-iso";
+static const char g_epb_name[] = "epb-bulk";
+static const char g_epc_name[] = "epc-nulk";
+static const char g_epd_name[] = "epdin-int";
+
+static const char *gp_ep_name[NUM_ENDPOINTS] = {
+ g_ep0_name,
+ g_ep1_name,
+ g_ep2_name,
+ g_ep3_name,
+ g_ep4_name,
+ g_ep5_name,
+ g_ep6_name,
+ g_ep7_name,
+ g_ep8_name,
+ g_ep9_name,
+ g_epa_name,
+ g_epb_name,
+ g_epc_name,
+ g_epd_name,
+};
+
+/*-------------------------------------------------------------------------*/
+static void __init nbu2ss_drv_ep_init(struct nbu2ss_udc *udc)
+{
+ int i;
+
+ INIT_LIST_HEAD(&udc->gadget.ep_list);
+ udc->gadget.ep0 = &udc->ep[0].ep;
+
+ for (i = 0; i < NUM_ENDPOINTS; i++) {
+ struct nbu2ss_ep *ep = &udc->ep[i];
+
+ ep->udc = udc;
+ ep->desc = NULL;
+
+ ep->ep.driver_data = NULL;
+ ep->ep.name = gp_ep_name[i];
+ ep->ep.ops = &nbu2ss_ep_ops;
+
+ ep->ep.maxpacket = (i == 0 ? EP0_PACKETSIZE : EP_PACKETSIZE);
+
+ list_add_tail(&ep->ep.ep_list, &udc->gadget.ep_list);
+ INIT_LIST_HEAD(&ep->queue);
+ }
+
+ list_del_init(&udc->ep[0].ep.ep_list);
+}
+
+/*-------------------------------------------------------------------------*/
+/* platform_driver */
+static int __init nbu2ss_drv_contest_init(
+ struct platform_device *pdev,
+ struct nbu2ss_udc *udc)
+{
+ spin_lock_init(&udc->lock);
+ udc->dev = &pdev->dev;
+
+ udc->gadget.is_selfpowered = 1;
+ udc->devstate = USB_STATE_NOTATTACHED;
+ udc->pdev = pdev;
+ udc->mA = 0;
+
+ udc->pdev->dev.coherent_dma_mask = DMA_BIT_MASK(32);
+
+ /* init Endpoint */
+ nbu2ss_drv_ep_init(udc);
+
+ /* init Gadget */
+ udc->gadget.ops = &nbu2ss_gadget_ops;
+ udc->gadget.ep0 = &udc->ep[0].ep;
+ udc->gadget.speed = USB_SPEED_UNKNOWN;
+ udc->gadget.name = driver_name;
+ /* udc->gadget.is_dualspeed = 1; */
+
+ device_initialize(&udc->gadget.dev);
+
+ dev_set_name(&udc->gadget.dev, "gadget");
+ udc->gadget.dev.parent = &pdev->dev;
+ udc->gadget.dev.dma_mask = pdev->dev.dma_mask;
+
+ return 0;
+}
+
+/*
+ * probe - binds to the platform device
+ */
+static int nbu2ss_drv_probe(struct platform_device *pdev)
+{
+ int status = -ENODEV;
+ struct nbu2ss_udc *udc;
+ struct resource *r;
+ int irq;
+ void __iomem *mmio_base;
+
+ udc = &udc_controller;
+ memset(udc, 0, sizeof(struct nbu2ss_udc));
+
+ platform_set_drvdata(pdev, udc);
+
+ /* require I/O memory and IRQ to be provided as resources */
+ r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ mmio_base = devm_ioremap_resource(&pdev->dev, r);
+ if (IS_ERR(mmio_base))
+ return PTR_ERR(mmio_base);
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0) {
+ dev_err(&pdev->dev, "failed to get IRQ\n");
+ return irq;
+ }
+ status = devm_request_irq(&pdev->dev, irq, _nbu2ss_udc_irq,
+ 0, driver_name, udc);
+
+ /* IO Memory */
+ udc->p_regs = (struct fc_regs *)mmio_base;
+
+ /* USB Function Controller Interrupt */
+ if (status != 0) {
+ dev_err(udc->dev, "request_irq(USB_UDC_IRQ_1) failed\n");
+ goto cleanup1;
+ }
+
+ /* Driver Initialization */
+ status = nbu2ss_drv_contest_init(pdev, udc);
+ if (status < 0) {
+ /* Error */
+ goto cleanup1;
+ }
+
+ /* VBUS Interrupt */
+ irq_set_irq_type(INT_VBUS, IRQ_TYPE_EDGE_BOTH);
+ status = request_irq(INT_VBUS,
+ _nbu2ss_vbus_irq,
+ IRQF_SHARED,
+ driver_name,
+ udc);
+
+ if (status != 0) {
+ dev_err(udc->dev, "request_irq(INT_VBUS) failed\n");
+ goto cleanup1;
+ }
+
+ return status;
+
+cleanup1:
+ return status;
+}
+
+/*-------------------------------------------------------------------------*/
+static void nbu2ss_drv_shutdown(struct platform_device *pdev)
+{
+ struct nbu2ss_udc *udc;
+
+ udc = platform_get_drvdata(pdev);
+ if (udc == NULL)
+ return;
+
+ _nbu2ss_disable_controller(udc);
+}
+
+/*-------------------------------------------------------------------------*/
+static int __exit nbu2ss_drv_remove(struct platform_device *pdev)
+{
+ struct nbu2ss_udc *udc;
+ struct nbu2ss_ep *ep;
+ int i;
+
+ udc = &udc_controller;
+
+ for (i = 0; i < NUM_ENDPOINTS; i++) {
+ ep = &udc->ep[i];
+ if (ep->virt_buf)
+ dma_free_coherent(NULL, PAGE_SIZE,
+ (void *)ep->virt_buf, ep->phys_buf);
+ }
+
+ /* Interrupt Handler - Release */
+ free_irq(INT_VBUS, udc);
+
+ return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+static int nbu2ss_drv_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ struct nbu2ss_udc *udc;
+
+ udc = platform_get_drvdata(pdev);
+ if (udc == NULL)
+ return 0;
+
+ if (udc->vbus_active) {
+ udc->vbus_active = 0;
+ udc->devstate = USB_STATE_NOTATTACHED;
+ udc->linux_suspended = 1;
+
+ if (udc->usb_suspended) {
+ udc->usb_suspended = 0;
+ _nbu2ss_reset_controller(udc);
+ }
+
+ _nbu2ss_quiesce(udc);
+ }
+ _nbu2ss_disable_controller(udc);
+
+ return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+static int nbu2ss_drv_resume(struct platform_device *pdev)
+{
+ u32 data;
+ struct nbu2ss_udc *udc;
+
+ udc = platform_get_drvdata(pdev);
+ if (udc == NULL)
+ return 0;
+
+ data = gpio_get_value(VBUS_VALUE);
+ if (data) {
+ udc->vbus_active = 1;
+ udc->devstate = USB_STATE_POWERED;
+ _nbu2ss_enable_controller(udc);
+ _nbu2ss_pullup(udc, 1);
+ }
+
+ udc->linux_suspended = 0;
+
+ return 0;
+}
+
+
+static struct platform_driver udc_driver = {
+ .probe = nbu2ss_drv_probe,
+ .shutdown = nbu2ss_drv_shutdown,
+ .remove = __exit_p(nbu2ss_drv_remove),
+ .suspend = nbu2ss_drv_suspend,
+ .resume = nbu2ss_drv_resume,
+ .driver = {
+ .name = driver_name,
+ },
+};
+
+module_platform_driver(udc_driver);
+
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_AUTHOR("Renesas Electronics Corporation");
+MODULE_LICENSE("GPL");
+
+
diff --git a/drivers/staging/emxx_udc/emxx_udc.h b/drivers/staging/emxx_udc/emxx_udc.h
new file mode 100644
index 000000000..c19168f78
--- /dev/null
+++ b/drivers/staging/emxx_udc/emxx_udc.h
@@ -0,0 +1,646 @@
+/*
+ * EMXX FCD (Function Controller Driver) for USB.
+ *
+ * Copyright (C) 2010 Renesas Electronics Corporation
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA.
+ */
+
+
+
+
+#ifndef _LINUX_EMXX_H
+#define _LINUX_EMXX_H
+
+
+
+/*---------------------------------------------------------------------------*/
+/*----------------- Default undef */
+#if 0
+#define DEBUG
+#define UDC_DEBUG_DUMP
+#endif
+
+/* #define USE_INT_COUNT_OVER */
+
+/*----------------- Default define */
+#define USE_DMA 1
+#define USE_SUSPEND_WAIT 1
+
+
+
+#ifndef TRUE
+#define TRUE 1
+#define FALSE 0
+#endif
+
+
+/*------------ Board dependence(Resource) */
+#define VBUS_VALUE GPIO_VBUS
+
+/* below hacked up for staging integration */
+#define GPIO_VBUS 0 /* GPIO_P153 on KZM9D */
+#define INT_VBUS 0 /* IRQ for GPIO_P153 */
+
+/*------------ Board dependence(Wait) */
+
+/* CHATTERING wait time ms */
+#define VBUS_CHATTERING_MDELAY 1
+/* DMA Abort wait time ms */
+#define DMA_DISABLE_TIME 10
+
+
+
+/*------------ Controller dependence */
+#define NUM_ENDPOINTS 14 /* Endpoint */
+#define REG_EP_NUM 15 /* Endpoint Register */
+#define DMA_MAX_COUNT 256 /* DMA Block */
+
+
+
+#define EPC_RST_DISABLE_TIME 1 /* 1 usec */
+#define EPC_DIRPD_DISABLE_TIME 1 /* 1 msec */
+#define EPC_PLL_LOCK_COUNT 1000 /* 1000 */
+#define IN_DATA_EMPTY_COUNT 1000 /* 1000 */
+
+#define CHATGER_TIME 700 /* 700msec */
+#define USB_SUSPEND_TIME 2000 /* 2 sec */
+
+
+/* U2F FLAG */
+#define U2F_ENABLE 1
+#define U2F_DISABLE 0
+
+
+/*------- BIT */
+#define BIT00 0x00000001
+#define BIT01 0x00000002
+#define BIT02 0x00000004
+#define BIT03 0x00000008
+#define BIT04 0x00000010
+#define BIT05 0x00000020
+#define BIT06 0x00000040
+#define BIT07 0x00000080
+#define BIT08 0x00000100
+#define BIT09 0x00000200
+#define BIT10 0x00000400
+#define BIT11 0x00000800
+#define BIT12 0x00001000
+#define BIT13 0x00002000
+#define BIT14 0x00004000
+#define BIT15 0x00008000
+#define BIT16 0x00010000
+#define BIT17 0x00020000
+#define BIT18 0x00040000
+#define BIT19 0x00080000
+#define BIT20 0x00100000
+#define BIT21 0x00200000
+#define BIT22 0x00400000
+#define BIT23 0x00800000
+#define BIT24 0x01000000
+#define BIT25 0x02000000
+#define BIT26 0x04000000
+#define BIT27 0x08000000
+#define BIT28 0x10000000
+#define BIT29 0x20000000
+#define BIT30 0x40000000
+#define BIT31 0x80000000
+
+#if 0
+/*------- (0x0000) USB Control Register */
+#define USBTESTMODE (BIT18+BIT17+BIT16)
+#define TEST_J BIT16
+#define TEST_K BIT17
+#define TEST_SE0_NAK (BIT17+BIT16)
+#define TEST_PACKET BIT18
+#endif
+#define TEST_FORCE_ENABLE (BIT18+BIT16)
+
+#define INT_SEL BIT10
+#define CONSTFS BIT09
+#define SOF_RCV BIT08
+#define RSUM_IN BIT07
+#define SUSPEND BIT06
+#define CONF BIT05
+#define DEFAULT BIT04
+#define CONNECTB BIT03
+#define PUE2 BIT02
+
+#define MAX_TEST_MODE_NUM 0x05
+#define TEST_MODE_SHIFT 16
+
+/*------- (0x0004) USB Status Register */
+#define SPEED_MODE BIT06
+#define HIGH_SPEED BIT06
+
+#define CONF BIT05
+#define DEFAULT BIT04
+#define USB_RST BIT03
+#define SPND_OUT BIT02
+#define RSUM_OUT BIT01
+
+/*------- (0x0008) USB Address Register */
+#define USB_ADDR 0x007F0000
+#define SOF_STATUS BIT15
+#define UFRAME (BIT14+BIT13+BIT12)
+#define FRAME 0x000007FF
+
+#define USB_ADRS_SHIFT 16
+
+/*------- (0x000C) UTMI Characteristic 1 Register */
+#define SQUSET (BIT07+BIT06+BIT05+BIT04)
+
+#define USB_SQUSET (BIT06+BIT05+BIT04)
+
+/*------- (0x0010) TEST Control Register */
+#define FORCEHS BIT02
+#define CS_TESTMODEEN BIT01
+#define LOOPBACK BIT00
+
+/*------- (0x0018) Setup Data 0 Register */
+/*------- (0x001C) Setup Data 1 Register */
+
+/*------- (0x0020) USB Interrupt Status Register */
+#define EPn_INT 0x00FFFF00
+#define EP15_INT BIT23
+#define EP14_INT BIT22
+#define EP13_INT BIT21
+#define EP12_INT BIT20
+#define EP11_INT BIT19
+#define EP10_INT BIT18
+#define EP9_INT BIT17
+#define EP8_INT BIT16
+#define EP7_INT BIT15
+#define EP6_INT BIT14
+#define EP5_INT BIT13
+#define EP4_INT BIT12
+#define EP3_INT BIT11
+#define EP2_INT BIT10
+#define EP1_INT BIT09
+#define EP0_INT BIT08
+#define SPEED_MODE_INT BIT06
+#define SOF_ERROR_INT BIT05
+#define SOF_INT BIT04
+#define USB_RST_INT BIT03
+#define SPND_INT BIT02
+#define RSUM_INT BIT01
+
+#define USB_INT_STA_RW 0x7E
+
+/*------- (0x0024) USB Interrupt Enable Register */
+#define EP15_0_EN 0x00FFFF00
+#define EP15_EN BIT23
+#define EP14_EN BIT22
+#define EP13_EN BIT21
+#define EP12_EN BIT20
+#define EP11_EN BIT19
+#define EP10_EN BIT18
+#define EP9_EN BIT17
+#define EP8_EN BIT16
+#define EP7_EN BIT15
+#define EP6_EN BIT14
+#define EP5_EN BIT13
+#define EP4_EN BIT12
+#define EP3_EN BIT11
+#define EP2_EN BIT10
+#define EP1_EN BIT09
+#define EP0_EN BIT08
+#define SPEED_MODE_EN BIT06
+#define SOF_ERROR_EN BIT05
+#define SOF_EN BIT04
+#define USB_RST_EN BIT03
+#define SPND_EN BIT02
+#define RSUM_EN BIT01
+
+#define USB_INT_EN_BIT \
+ (EP0_EN|SPEED_MODE_EN|USB_RST_EN|SPND_EN|RSUM_EN)
+
+/*------- (0x0028) EP0 Control Register */
+#define EP0_STGSEL BIT18
+#define EP0_OVERSEL BIT17
+#define EP0_AUTO BIT16
+#define EP0_PIDCLR BIT09
+#define EP0_BCLR BIT08
+#define EP0_DEND BIT07
+#define EP0_DW (BIT06+BIT05)
+#define EP0_DW4 0
+#define EP0_DW3 (BIT06+BIT05)
+#define EP0_DW2 BIT06
+#define EP0_DW1 BIT05
+
+#define EP0_INAK_EN BIT04
+#define EP0_PERR_NAK_CLR BIT03
+#define EP0_STL BIT02
+#define EP0_INAK BIT01
+#define EP0_ONAK BIT00
+
+/*------- (0x002C) EP0 Status Register */
+#define EP0_PID BIT18
+#define EP0_PERR_NAK BIT17
+#define EP0_PERR_NAK_INT BIT16
+#define EP0_OUT_NAK_INT BIT15
+#define EP0_OUT_NULL BIT14
+#define EP0_OUT_FULL BIT13
+#define EP0_OUT_EMPTY BIT12
+#define EP0_IN_NAK_INT BIT11
+#define EP0_IN_DATA BIT10
+#define EP0_IN_FULL BIT09
+#define EP0_IN_EMPTY BIT08
+#define EP0_OUT_NULL_INT BIT07
+#define EP0_OUT_OR_INT BIT06
+#define EP0_OUT_INT BIT05
+#define EP0_IN_INT BIT04
+#define EP0_STALL_INT BIT03
+#define STG_END_INT BIT02
+#define STG_START_INT BIT01
+#define SETUP_INT BIT00
+
+#define EP0_STATUS_RW_BIT (BIT16|BIT15|BIT11|0xFF)
+
+/*------- (0x0030) EP0 Interrupt Enable Register */
+#define EP0_PERR_NAK_EN BIT16
+#define EP0_OUT_NAK_EN BIT15
+
+#define EP0_IN_NAK_EN BIT11
+
+#define EP0_OUT_NULL_EN BIT07
+#define EP0_OUT_OR_EN BIT06
+#define EP0_OUT_EN BIT05
+#define EP0_IN_EN BIT04
+#define EP0_STALL_EN BIT03
+#define STG_END_EN BIT02
+#define STG_START_EN BIT01
+#define SETUP_EN BIT00
+
+#define EP0_INT_EN_BIT \
+ (EP0_OUT_OR_EN|EP0_OUT_EN|EP0_IN_EN|STG_END_EN|SETUP_EN)
+
+/*------- (0x0034) EP0 Length Register */
+#define EP0_LDATA 0x0000007F
+
+/*------- (0x0038) EP0 Read Register */
+/*------- (0x003C) EP0 Write Register */
+
+/*------- (0x0040:) EPn Control Register */
+#define EPn_EN BIT31
+#define EPn_BUF_TYPE BIT30
+#define EPn_BUF_SINGLE BIT30
+
+#define EPn_DIR0 BIT26
+#define EPn_MODE (BIT25+BIT24)
+#define EPn_BULK 0
+#define EPn_INTERRUPT BIT24
+#define EPn_ISO BIT25
+
+#define EPn_OVERSEL BIT17
+#define EPn_AUTO BIT16
+
+#define EPn_IPIDCLR BIT11
+#define EPn_OPIDCLR BIT10
+#define EPn_BCLR BIT09
+#define EPn_CBCLR BIT08
+#define EPn_DEND BIT07
+#define EPn_DW (BIT06+BIT05)
+#define EPn_DW4 0
+#define EPn_DW3 (BIT06+BIT05)
+#define EPn_DW2 BIT06
+#define EPn_DW1 BIT05
+
+#define EPn_OSTL_EN BIT04
+#define EPn_ISTL BIT03
+#define EPn_OSTL BIT02
+
+#define EPn_ONAK BIT00
+
+/*------- (0x0044:) EPn Status Register */
+#define EPn_ISO_PIDERR BIT29 /* R */
+#define EPn_OPID BIT28 /* R */
+#define EPn_OUT_NOTKN BIT27 /* R */
+#define EPn_ISO_OR BIT26 /* R */
+
+#define EPn_ISO_CRC BIT24 /* R */
+#define EPn_OUT_END_INT BIT23 /* RW */
+#define EPn_OUT_OR_INT BIT22 /* RW */
+#define EPn_OUT_NAK_ERR_INT BIT21 /* RW */
+#define EPn_OUT_STALL_INT BIT20 /* RW */
+#define EPn_OUT_INT BIT19 /* RW */
+#define EPn_OUT_NULL_INT BIT18 /* RW */
+#define EPn_OUT_FULL BIT17 /* R */
+#define EPn_OUT_EMPTY BIT16 /* R */
+
+#define EPn_IPID BIT10 /* R */
+#define EPn_IN_NOTKN BIT09 /* R */
+#define EPn_ISO_UR BIT08 /* R */
+#define EPn_IN_END_INT BIT07 /* RW */
+
+#define EPn_IN_NAK_ERR_INT BIT05 /* RW */
+#define EPn_IN_STALL_INT BIT04 /* RW */
+#define EPn_IN_INT BIT03 /* RW */
+#define EPn_IN_DATA BIT02 /* R */
+#define EPn_IN_FULL BIT01 /* R */
+#define EPn_IN_EMPTY BIT00 /* R */
+
+#define EPn_INT_EN \
+ (EPn_OUT_END_INT|EPn_OUT_INT|EPn_IN_END_INT|EPn_IN_INT)
+
+/*------- (0x0048:) EPn Interrupt Enable Register */
+#define EPn_OUT_END_EN BIT23 /* RW */
+#define EPn_OUT_OR_EN BIT22 /* RW */
+#define EPn_OUT_NAK_ERR_EN BIT21 /* RW */
+#define EPn_OUT_STALL_EN BIT20 /* RW */
+#define EPn_OUT_EN BIT19 /* RW */
+#define EPn_OUT_NULL_EN BIT18 /* RW */
+
+#define EPn_IN_END_EN BIT07 /* RW */
+
+#define EPn_IN_NAK_ERR_EN BIT05 /* RW */
+#define EPn_IN_STALL_EN BIT04 /* RW */
+#define EPn_IN_EN BIT03 /* RW */
+
+/*------- (0x004C:) EPn Interrupt Enable Register */
+#define EPn_STOP_MODE BIT11
+#define EPn_DEND_SET BIT10
+#define EPn_BURST_SET BIT09
+#define EPn_STOP_SET BIT08
+
+#define EPn_DMA_EN BIT04
+
+#define EPn_DMAMODE0 BIT00
+
+/*------- (0x0050:) EPn MaxPacket & BaseAddress Register */
+#define EPn_BASEAD 0x1FFF0000
+#define EPn_MPKT 0x000007FF
+
+/*------- (0x0054:) EPn Length & DMA Count Register */
+#define EPn_DMACNT 0x01FF0000
+#define EPn_LDATA 0x000007FF
+
+/*------- (0x0058:) EPn Read Register */
+/*------- (0x005C:) EPn Write Register */
+
+/*------- (0x1000) AHBSCTR Register */
+#define WAIT_MODE BIT00
+
+/*------- (0x1004) AHBMCTR Register */
+#define ARBITER_CTR BIT31 /* RW */
+#define MCYCLE_RST BIT12 /* RW */
+
+#define ENDIAN_CTR (BIT09+BIT08) /* RW */
+#define ENDIAN_BYTE_SWAP BIT09
+#define ENDIAN_HALF_WORD_SWAP ENDIAN_CTR
+
+#define HBUSREQ_MODE BIT05 /* RW */
+#define HTRANS_MODE BIT04 /* RW */
+
+#define WBURST_TYPE BIT02 /* RW */
+#define BURST_TYPE (BIT01+BIT00) /* RW */
+#define BURST_MAX_16 0
+#define BURST_MAX_8 BIT00
+#define BURST_MAX_4 BIT01
+#define BURST_SINGLE BURST_TYPE
+
+/*------- (0x1008) AHBBINT Register */
+#define DMA_ENDINT 0xFFFE0000 /* RW */
+
+#define AHB_VBUS_INT BIT13 /* RW */
+
+#define MBUS_ERRINT BIT06 /* RW */
+
+#define SBUS_ERRINT0 BIT04 /* RW */
+#define ERR_MASTER 0x0000000F /* R */
+
+/*------- (0x100C) AHBBINTEN Register */
+#define DMA_ENDINTEN 0xFFFE0000 /* RW */
+
+#define VBUS_INTEN BIT13 /* RW */
+
+#define MBUS_ERRINTEN BIT06 /* RW */
+
+#define SBUS_ERRINT0EN BIT04 /* RW */
+
+/*------- (0x1010) EPCTR Register */
+#define DIRPD BIT12 /* RW */
+
+#define VBUS_LEVEL BIT08 /* R */
+
+#define PLL_RESUME BIT05 /* RW */
+#define PLL_LOCK BIT04 /* R */
+
+#define EPC_RST BIT00 /* RW */
+
+/*------- (0x1014) USBF_EPTEST Register */
+#define LINESTATE (BIT09+BIT08) /* R */
+#define DM_LEVEL BIT09 /* R */
+#define DP_LEVEL BIT08 /* R */
+
+#define PHY_TST BIT01 /* RW */
+#define PHY_TSTCLK BIT00 /* RW */
+
+/*------- (0x1020) USBSSVER Register */
+#define AHBB_VER 0x00FF0000 /* R */
+#define EPC_VER 0x0000FF00 /* R */
+#define SS_VER 0x000000FF /* R */
+
+/*------- (0x1024) USBSSCONF Register */
+#define EP_AVAILABLE 0xFFFF0000 /* R */
+#define DMA_AVAILABLE 0x0000FFFF /* R */
+
+/*------- (0x1110:) EPnDCR1 Register */
+#define DCR1_EPn_DMACNT 0x00FF0000 /* RW */
+
+#define DCR1_EPn_DIR0 BIT01 /* RW */
+#define DCR1_EPn_REQEN BIT00 /* RW */
+
+/*------- (0x1114:) EPnDCR2 Register */
+#define DCR2_EPn_LMPKT 0x07FF0000 /* RW */
+
+#define DCR2_EPn_MPKT 0x000007FF /* RW */
+
+/*------- (0x1118:) EPnTADR Register */
+#define EPn_TADR 0xFFFFFFFF /* RW */
+
+
+
+/*===========================================================================*/
+/* Struct */
+/*------- ep_regs */
+struct ep_regs {
+ u32 EP_CONTROL; /* EP Control */
+ u32 EP_STATUS; /* EP Status */
+ u32 EP_INT_ENA; /* EP Interrupt Enable */
+ u32 EP_DMA_CTRL; /* EP DMA Control */
+ u32 EP_PCKT_ADRS; /* EP Maxpacket & BaseAddress */
+ u32 EP_LEN_DCNT; /* EP Length & DMA count */
+ u32 EP_READ; /* EP Read */
+ u32 EP_WRITE; /* EP Write */
+};
+
+/*------- ep_dcr */
+struct ep_dcr {
+ u32 EP_DCR1; /* EP_DCR1 */
+ u32 EP_DCR2; /* EP_DCR2 */
+ u32 EP_TADR; /* EP_TADR */
+ u32 Reserved; /* Reserved */
+};
+
+/*------- Function Registers */
+struct fc_regs {
+ u32 USB_CONTROL; /* (0x0000) USB Control */
+ u32 USB_STATUS; /* (0x0004) USB Status */
+ u32 USB_ADDRESS; /* (0x0008) USB Address */
+ u32 UTMI_CHARACTER_1; /* (0x000C) UTMI Setting */
+ u32 TEST_CONTROL; /* (0x0010) TEST Control */
+ u32 Reserved_14; /* (0x0014) Reserved */
+ u32 SETUP_DATA0; /* (0x0018) Setup Data0 */
+ u32 SETUP_DATA1; /* (0x001C) Setup Data1 */
+ u32 USB_INT_STA; /* (0x0020) USB Interrupt Status */
+ u32 USB_INT_ENA; /* (0x0024) USB Interrupt Enable */
+ u32 EP0_CONTROL; /* (0x0028) EP0 Control */
+ u32 EP0_STATUS; /* (0x002C) EP0 Status */
+ u32 EP0_INT_ENA; /* (0x0030) EP0 Interrupt Enable */
+ u32 EP0_LENGTH; /* (0x0034) EP0 Length */
+ u32 EP0_READ; /* (0x0038) EP0 Read */
+ u32 EP0_WRITE; /* (0x003C) EP0 Write */
+
+ struct ep_regs EP_REGS[REG_EP_NUM]; /* Endpoint Register */
+
+ u8 Reserved220[0x1000-0x220]; /* (0x0220:0x0FFF) Reserved */
+
+ u32 AHBSCTR; /* (0x1000) AHBSCTR */
+ u32 AHBMCTR; /* (0x1004) AHBMCTR */
+ u32 AHBBINT; /* (0x1008) AHBBINT */
+ u32 AHBBINTEN; /* (0x100C) AHBBINTEN */
+ u32 EPCTR; /* (0x1010) EPCTR */
+ u32 USBF_EPTEST; /* (0x1014) USBF_EPTEST */
+
+ u8 Reserved1018[0x20-0x18]; /* (0x1018:0x101F) Reserved */
+
+ u32 USBSSVER; /* (0x1020) USBSSVER */
+ u32 USBSSCONF; /* (0x1024) USBSSCONF */
+
+ u8 Reserved1028[0x110-0x28]; /* (0x1028:0x110F) Reserved */
+
+ struct ep_dcr EP_DCR[REG_EP_NUM]; /* */
+
+ u8 Reserved1200[0x1000-0x200]; /* Reserved */
+} __aligned(32);
+
+
+
+
+
+
+
+
+#define EP0_PACKETSIZE 64
+#define EP_PACKETSIZE 1024
+
+/* EPn RAM SIZE */
+#define D_RAM_SIZE_CTRL 64
+
+/* EPn Bulk Endpoint Max Packet Size */
+#define D_FS_RAM_SIZE_BULK 64
+#define D_HS_RAM_SIZE_BULK 512
+
+
+struct nbu2ss_udc;
+
+
+enum ep0_state {
+ EP0_IDLE,
+ EP0_IN_DATA_PHASE,
+ EP0_OUT_DATA_PHASE,
+ EP0_IN_STATUS_PHASE,
+ EP0_OUT_STATUS_PAHSE,
+ EP0_END_XFER,
+ EP0_SUSPEND,
+ EP0_STALL,
+};
+
+struct nbu2ss_req {
+ struct usb_request req;
+ struct list_head queue;
+
+ u32 div_len;
+ bool dma_flag;
+ bool zero;
+
+ bool unaligned;
+
+ unsigned mapped:1;
+};
+
+struct nbu2ss_ep {
+ struct usb_ep ep;
+ struct list_head queue;
+
+ struct nbu2ss_udc *udc;
+
+ const struct usb_endpoint_descriptor *desc;
+
+ u8 epnum;
+ u8 direct;
+ u8 ep_type;
+
+ unsigned wedged:1;
+ unsigned halted:1;
+ unsigned stalled:1;
+
+ u8 *virt_buf;
+ dma_addr_t phys_buf;
+};
+
+
+struct nbu2ss_udc {
+ struct usb_gadget gadget;
+ struct usb_gadget_driver *driver;
+ struct platform_device *pdev;
+ struct device *dev;
+ spinlock_t lock;
+ struct completion *pdone;
+
+ enum ep0_state ep0state;
+ enum usb_device_state devstate;
+ struct usb_ctrlrequest ctrl;
+ struct nbu2ss_req ep0_req;
+ u8 ep0_buf[EP0_PACKETSIZE];
+
+ struct nbu2ss_ep ep[NUM_ENDPOINTS];
+
+ unsigned softconnect:1;
+ unsigned vbus_active:1;
+ unsigned linux_suspended:1;
+ unsigned linux_resume:1;
+ unsigned usb_suspended:1;
+ unsigned remote_wakeup:1;
+ unsigned udc_enabled:1;
+
+ unsigned mA;
+
+ u32 curr_config; /* Current Configuration Number */
+
+ struct fc_regs *p_regs;
+};
+
+/* USB register access structure */
+union usb_reg_access {
+ struct {
+ unsigned char DATA[4];
+ } byte;
+ unsigned int dw;
+};
+
+/*-------------------------------------------------------------------------*/
+
+#endif /* _LINUX_EMXX_H */
diff --git a/drivers/staging/fbtft/Kconfig b/drivers/staging/fbtft/Kconfig
new file mode 100644
index 000000000..6cf0c58f5
--- /dev/null
+++ b/drivers/staging/fbtft/Kconfig
@@ -0,0 +1,175 @@
+menuconfig FB_TFT
+ tristate "Support for small TFT LCD display modules"
+ depends on FB && SPI && GPIOLIB
+ select FB_SYS_FILLRECT
+ select FB_SYS_COPYAREA
+ select FB_SYS_IMAGEBLIT
+ select FB_SYS_FOPS
+ select FB_DEFERRED_IO
+ select FB_BACKLIGHT
+
+config FB_TFT_AGM1264K_FL
+ tristate "FB driver for the AGM1264K-FL LCD display"
+ depends on FB_TFT
+ help
+ Framebuffer support for the AGM1264K-FL LCD display (two Samsung KS0108 compatable chips)
+
+config FB_TFT_BD663474
+ tristate "FB driver for the BD663474 LCD Controller"
+ depends on FB_TFT
+ help
+ Generic Framebuffer support for BD663474
+
+config FB_TFT_HX8340BN
+ tristate "FB driver for the HX8340BN LCD Controller"
+ depends on FB_TFT
+ help
+ Generic Framebuffer support for HX8340BN
+
+config FB_TFT_HX8347D
+ tristate "FB driver for the HX8347D LCD Controller"
+ depends on FB_TFT
+ help
+ Generic Framebuffer support for HX8347D
+
+config FB_TFT_HX8353D
+ tristate "FB driver for the HX8353D LCD Controller"
+ depends on FB_TFT
+ help
+ Generic Framebuffer support for HX8353D
+
+config FB_TFT_ILI9163
+ tristate "FB driver for the ILI9163 LCD Controller"
+ depends on FB_TFT
+ help
+ Generic Framebuffer support for ILI9163
+
+config FB_TFT_ILI9320
+ tristate "FB driver for the ILI9320 LCD Controller"
+ depends on FB_TFT
+ help
+ Generic Framebuffer support for ILI9320
+
+config FB_TFT_ILI9325
+ tristate "FB driver for the ILI9325 LCD Controller"
+ depends on FB_TFT
+ help
+ Generic Framebuffer support for ILI9325
+
+config FB_TFT_ILI9340
+ tristate "FB driver for the ILI9340 LCD Controller"
+ depends on FB_TFT
+ help
+ Generic Framebuffer support for ILI9340
+
+config FB_TFT_ILI9341
+ tristate "FB driver for the ILI9341 LCD Controller"
+ depends on FB_TFT
+ help
+ Generic Framebuffer support for ILI9341
+
+config FB_TFT_ILI9481
+ tristate "FB driver for the ILI9481 LCD Controller"
+ depends on FB_TFT
+ help
+ Generic Framebuffer support for ILI9481
+
+config FB_TFT_ILI9486
+ tristate "FB driver for the ILI9486 LCD Controller"
+ depends on FB_TFT
+ help
+ Generic Framebuffer support for ILI9486
+
+config FB_TFT_PCD8544
+ tristate "FB driver for the PCD8544 LCD Controller"
+ depends on FB_TFT
+ help
+ Generic Framebuffer support for PCD8544
+
+config FB_TFT_RA8875
+ tristate "FB driver for the RA8875 LCD Controller"
+ depends on FB_TFT
+ help
+ Generic Framebuffer support for RA8875
+
+config FB_TFT_S6D02A1
+ tristate "FB driver for the S6D02A1 LCD Controller"
+ depends on FB_TFT
+ help
+ Generic Framebuffer support for S6D02A1
+
+config FB_TFT_S6D1121
+ tristate "FB driver for the S6D1211 LCD Controller"
+ depends on FB_TFT
+ help
+ Generic Framebuffer support for S6D1121
+
+config FB_TFT_SSD1289
+ tristate "FB driver for the SSD1289 LCD Controller"
+ depends on FB_TFT
+ help
+ Framebuffer support for SSD1289
+
+config FB_TFT_SSD1306
+ tristate "FB driver for the SSD1306 OLED Controller"
+ depends on FB_TFT
+ help
+ Framebuffer support for SSD1306
+
+config FB_TFT_SSD1331
+ tristate "FB driver for the SSD1331 LCD Controller"
+ depends on FB_TFT
+ help
+ Framebuffer support for SSD1331
+
+config FB_TFT_SSD1351
+ tristate "FB driver for the SSD1351 LCD Controller"
+ depends on FB_TFT
+ help
+ Framebuffer support for SSD1351
+
+config FB_TFT_ST7735R
+ tristate "FB driver for the ST7735R LCD Controller"
+ depends on FB_TFT
+ help
+ Generic Framebuffer support for ST7735R
+
+config FB_TFT_TINYLCD
+ tristate "FB driver for tinylcd.com display"
+ depends on FB_TFT
+ help
+ Custom Framebuffer support for tinylcd.com display
+
+config FB_TFT_TLS8204
+ tristate "FB driver for the TLS8204 LCD Controller"
+ depends on FB_TFT
+ help
+ Generic Framebuffer support for TLS8204
+
+config FB_TFT_UC1701
+ tristate "FB driver for the UC1701 LCD Controller"
+ depends on FB_TFT
+ help
+ Generic Framebuffer support for UC1701
+
+config FB_TFT_UPD161704
+ tristate "FB driver for the uPD161704 LCD Controller"
+ depends on FB_TFT
+ help
+ Generic Framebuffer support for uPD161704
+
+config FB_TFT_WATTEROTT
+ tristate "FB driver for the WATTEROTT LCD Controller"
+ depends on FB_TFT
+ help
+ Generic Framebuffer support for WATTEROTT
+
+config FB_FLEX
+ tristate "Generic FB driver for TFT LCD displays"
+ depends on FB_TFT
+ help
+ Generic Framebuffer support for TFT LCD displays.
+
+config FB_TFT_FBTFT_DEVICE
+ tristate "Module to for adding FBTFT devices"
+ depends on FB_TFT
diff --git a/drivers/staging/fbtft/Makefile b/drivers/staging/fbtft/Makefile
new file mode 100644
index 000000000..9e73beee2
--- /dev/null
+++ b/drivers/staging/fbtft/Makefile
@@ -0,0 +1,35 @@
+# Core module
+obj-$(CONFIG_FB_TFT) += fbtft.o
+fbtft-y += fbtft-core.o fbtft-sysfs.o fbtft-bus.o fbtft-io.o
+
+# drivers
+obj-$(CONFIG_FB_TFT_AGM1264K_FL) += fb_agm1264k-fl.o
+obj-$(CONFIG_FB_TFT_BD663474) += fb_bd663474.o
+obj-$(CONFIG_FB_TFT_HX8340BN) += fb_hx8340bn.o
+obj-$(CONFIG_FB_TFT_HX8347D) += fb_hx8347d.o
+obj-$(CONFIG_FB_TFT_HX8353D) += fb_hx8353d.o
+obj-$(CONFIG_FB_TFT_ILI9163) += fb_ili9163.o
+obj-$(CONFIG_FB_TFT_ILI9320) += fb_ili9320.o
+obj-$(CONFIG_FB_TFT_ILI9325) += fb_ili9325.o
+obj-$(CONFIG_FB_TFT_ILI9340) += fb_ili9340.o
+obj-$(CONFIG_FB_TFT_ILI9341) += fb_ili9341.o
+obj-$(CONFIG_FB_TFT_ILI9481) += fb_ili9481.o
+obj-$(CONFIG_FB_TFT_ILI9486) += fb_ili9486.o
+obj-$(CONFIG_FB_TFT_PCD8544) += fb_pcd8544.o
+obj-$(CONFIG_FB_TFT_RA8875) += fb_ra8875.o
+obj-$(CONFIG_FB_TFT_S6D02A1) += fb_s6d02a1.o
+obj-$(CONFIG_FB_TFT_S6D1121) += fb_s6d1121.o
+obj-$(CONFIG_FB_TFT_SSD1289) += fb_ssd1289.o
+obj-$(CONFIG_FB_TFT_SSD1306) += fb_ssd1306.o
+obj-$(CONFIG_FB_TFT_SSD1331) += fb_ssd1331.o
+obj-$(CONFIG_FB_TFT_SSD1351) += fb_ssd1351.o
+obj-$(CONFIG_FB_TFT_ST7735R) += fb_st7735r.o
+obj-$(CONFIG_FB_TFT_TINYLCD) += fb_tinylcd.o
+obj-$(CONFIG_FB_TFT_TLS8204) += fb_tls8204.o
+obj-$(CONFIG_FB_TFT_UC1701) += fb_uc1701.o
+obj-$(CONFIG_FB_TFT_UPD161704) += fb_upd161704.o
+obj-$(CONFIG_FB_TFT_WATTEROTT) += fb_watterott.o
+obj-$(CONFIG_FB_FLEX) += flexfb.o
+
+# Device modules
+obj-$(CONFIG_FB_TFT_FBTFT_DEVICE) += fbtft_device.o
diff --git a/drivers/staging/fbtft/README b/drivers/staging/fbtft/README
new file mode 100644
index 000000000..ba4c74c92
--- /dev/null
+++ b/drivers/staging/fbtft/README
@@ -0,0 +1,32 @@
+ FBTFT
+=========
+
+Linux Framebuffer drivers for small TFT LCD display modules.
+The module 'fbtft' makes writing drivers for some of these displays very easy.
+
+Development is done on a Raspberry Pi running the Raspbian "wheezy" distribution.
+
+INSTALLATION
+ Download kernel sources
+
+ From Linux 3.15
+ cd drivers/video/fbdev/fbtft
+ git clone https://github.com/notro/fbtft.git
+
+ Add to drivers/video/fbdev/Kconfig: source "drivers/video/fbdev/fbtft/Kconfig"
+ Add to drivers/video/fbdev/Makefile: obj-y += fbtft/
+
+ Before Linux 3.15
+ cd drivers/video
+ git clone https://github.com/notro/fbtft.git
+
+ Add to drivers/video/Kconfig: source "drivers/video/fbtft/Kconfig"
+ Add to drivers/video/Makefile: obj-y += fbtft/
+
+ Enable driver(s) in menuconfig and build the kernel
+
+
+See wiki for more information: https://github.com/notro/fbtft/wiki
+
+
+Source: https://github.com/notro/fbtft/
diff --git a/drivers/staging/fbtft/fb_agm1264k-fl.c b/drivers/staging/fbtft/fb_agm1264k-fl.c
new file mode 100644
index 000000000..8f5af1db8
--- /dev/null
+++ b/drivers/staging/fbtft/fb_agm1264k-fl.c
@@ -0,0 +1,465 @@
+/*
+ * FB driver for Two KS0108 LCD controllers in AGM1264K-FL display
+ *
+ * Copyright (C) 2014 ololoshka2871
+ *
+ * This program is free software; you can redistribute it and/or 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.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/gpio.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+
+#include "fbtft.h"
+
+/* Uncomment text line to use negative image on display */
+/*#define NEGATIVE*/
+
+#define WHITE 0xff
+#define BLACK 0
+
+#define DRVNAME "fb_agm1264k-fl"
+#define WIDTH 64
+#define HEIGHT 64
+#define TOTALWIDTH (WIDTH * 2) /* because 2 x ks0108 in one display */
+#define FPS 20
+
+#define EPIN gpio.wr
+#define RS gpio.dc
+#define RW gpio.aux[2]
+#define CS0 gpio.aux[0]
+#define CS1 gpio.aux[1]
+
+
+/* diffusing error (“Floyd-Steinberg”) */
+#define DIFFUSING_MATRIX_WIDTH 2
+#define DIFFUSING_MATRIX_HEIGHT 2
+
+static const signed char
+diffusing_matrix[DIFFUSING_MATRIX_WIDTH][DIFFUSING_MATRIX_HEIGHT] = {
+ {-1, 3},
+ {3, 2},
+};
+
+static const unsigned char gamma_correction_table[] = {
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1,
+1, 1, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 6,
+6, 6, 6, 7, 7, 7, 8, 8, 8, 9, 9, 9, 10, 10, 11, 11, 11, 12, 12, 13,
+13, 13, 14, 14, 15, 15, 16, 16, 17, 17, 18, 18, 19, 19, 20, 20, 21,
+22, 22, 23, 23, 24, 25, 25, 26, 26, 27, 28, 28, 29, 30, 30, 31, 32,
+33, 33, 34, 35, 35, 36, 37, 38, 39, 39, 40, 41, 42, 43, 43, 44, 45,
+46, 47, 48, 49, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61,
+62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 73, 74, 75, 76, 77, 78, 79, 81,
+82, 83, 84, 85, 87, 88, 89, 90, 91, 93, 94, 95, 97, 98, 99, 100, 102,
+103, 105, 106, 107, 109, 110, 111, 113, 114, 116, 117, 119, 120, 121,
+123, 124, 126, 127, 129, 130, 132, 133, 135, 137, 138, 140, 141, 143,
+145, 146, 148, 149, 151, 153, 154, 156, 158, 159, 161, 163, 165, 166,
+168, 170, 172, 173, 175, 177, 179, 181, 182, 184, 186, 188, 190, 192,
+194, 196, 197, 199, 201, 203, 205, 207, 209, 211, 213, 215, 217, 219,
+221, 223, 225, 227, 229, 231, 234, 236, 238, 240, 242, 244, 246, 248,
+251, 253, 255
+};
+
+static int init_display(struct fbtft_par *par)
+{
+ u8 i;
+
+ fbtft_par_dbg(DEBUG_INIT_DISPLAY, par, "%s()\n", __func__);
+
+ par->fbtftops.reset(par);
+
+ for (i = 0; i < 2; ++i) {
+ write_reg(par, i, 0x3f); /* display on */
+ write_reg(par, i, 0x40); /* set x to 0 */
+ write_reg(par, i, 0xb0); /* set page to 0 */
+ write_reg(par, i, 0xc0); /* set start line to 0 */
+ }
+
+ return 0;
+}
+
+static void reset(struct fbtft_par *par)
+{
+ if (par->gpio.reset == -1)
+ return;
+
+ fbtft_dev_dbg(DEBUG_RESET, par, par->info->device, "%s()\n", __func__);
+
+ gpio_set_value(par->gpio.reset, 0);
+ udelay(20);
+ gpio_set_value(par->gpio.reset, 1);
+ mdelay(120);
+}
+
+/* Check if all necessary GPIOS defined */
+static int verify_gpios(struct fbtft_par *par)
+{
+ int i;
+
+ fbtft_dev_dbg(DEBUG_VERIFY_GPIOS, par, par->info->device,
+ "%s()\n", __func__);
+
+ if (par->EPIN < 0) {
+ dev_err(par->info->device,
+ "Missing info about 'wr' (aka E) gpio. Aborting.\n");
+ return -EINVAL;
+ }
+ for (i = 0; i < 8; ++i) {
+ if (par->gpio.db[i] < 0) {
+ dev_err(par->info->device,
+ "Missing info about 'db[%i]' gpio. Aborting.\n",
+ i);
+ return -EINVAL;
+ }
+ }
+ if (par->CS0 < 0) {
+ dev_err(par->info->device,
+ "Missing info about 'cs0' gpio. Aborting.\n");
+ return -EINVAL;
+ }
+ if (par->CS1 < 0) {
+ dev_err(par->info->device,
+ "Missing info about 'cs1' gpio. Aborting.\n");
+ return -EINVAL;
+ }
+ if (par->RW < 0) {
+ dev_err(par->info->device,
+ "Missing info about 'rw' gpio. Aborting.\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static unsigned long
+request_gpios_match(struct fbtft_par *par, const struct fbtft_gpio *gpio)
+{
+ fbtft_dev_dbg(DEBUG_REQUEST_GPIOS_MATCH, par, par->info->device,
+ "%s('%s')\n", __func__, gpio->name);
+
+ if (strcasecmp(gpio->name, "wr") == 0) {
+ /* left ks0108 E pin */
+ par->EPIN = gpio->gpio;
+ return GPIOF_OUT_INIT_LOW;
+ } else if (strcasecmp(gpio->name, "cs0") == 0) {
+ /* left ks0108 controller pin */
+ par->CS0 = gpio->gpio;
+ return GPIOF_OUT_INIT_HIGH;
+ } else if (strcasecmp(gpio->name, "cs1") == 0) {
+ /* right ks0108 controller pin */
+ par->CS1 = gpio->gpio;
+ return GPIOF_OUT_INIT_HIGH;
+ }
+
+ /* if write (rw = 0) e(1->0) perform write */
+ /* if read (rw = 1) e(0->1) set data on D0-7*/
+ else if (strcasecmp(gpio->name, "rw") == 0) {
+ par->RW = gpio->gpio;
+ return GPIOF_OUT_INIT_LOW;
+ }
+
+ return FBTFT_GPIO_NO_MATCH;
+}
+
+/* This function oses to enter commands
+ * first byte - destination controller 0 or 1
+ * following - commands
+ */
+static void write_reg8_bus8(struct fbtft_par *par, int len, ...)
+{
+ va_list args;
+ int i, ret;
+ u8 *buf = (u8 *)par->buf;
+
+ if (unlikely(par->debug & DEBUG_WRITE_REGISTER)) {
+ va_start(args, len);
+ for (i = 0; i < len; i++)
+ buf[i] = (u8)va_arg(args, unsigned int);
+
+ va_end(args);
+ fbtft_par_dbg_hex(DEBUG_WRITE_REGISTER, par,
+ par->info->device, u8, buf, len, "%s: ", __func__);
+ }
+
+ va_start(args, len);
+
+ *buf = (u8)va_arg(args, unsigned int);
+
+ if (*buf > 1) {
+ va_end(args);
+ dev_err(par->info->device,
+ "Incorrect chip select request (%d)\n", *buf);
+ return;
+ }
+
+ /* select chip */
+ if (*buf) {
+ /* cs1 */
+ gpio_set_value(par->CS0, 1);
+ gpio_set_value(par->CS1, 0);
+ } else {
+ /* cs0 */
+ gpio_set_value(par->CS0, 0);
+ gpio_set_value(par->CS1, 1);
+ }
+
+ gpio_set_value(par->RS, 0); /* RS->0 (command mode) */
+ len--;
+
+ if (len) {
+ i = len;
+ while (i--)
+ *buf++ = (u8)va_arg(args, unsigned int);
+ ret = par->fbtftops.write(par, par->buf, len * (sizeof(u8)));
+ if (ret < 0) {
+ va_end(args);
+ dev_err(par->info->device,
+ "write() failed and returned %d\n", ret);
+ return;
+ }
+ }
+
+ va_end(args);
+}
+
+static struct
+{
+ int xs, ys_page, xe, ye_page;
+} addr_win;
+
+/* save display writing zone */
+static void set_addr_win(struct fbtft_par *par, int xs, int ys, int xe, int ye)
+{
+ addr_win.xs = xs;
+ addr_win.ys_page = ys / 8;
+ addr_win.xe = xe;
+ addr_win.ye_page = ye / 8;
+
+ fbtft_par_dbg(DEBUG_SET_ADDR_WIN, par,
+ "%s(xs=%d, ys_page=%d, xe=%d, ye_page=%d)\n", __func__,
+ addr_win.xs, addr_win.ys_page, addr_win.xe, addr_win.ye_page);
+}
+
+static void
+construct_line_bitmap(struct fbtft_par *par, u8 *dest, signed short *src,
+ int xs, int xe, int y)
+{
+ int x, i;
+
+ for (x = xs; x < xe; ++x) {
+ u8 res = 0;
+
+ for (i = 0; i < 8; i++)
+ if (src[(y * 8 + i) * par->info->var.xres + x])
+ res |= 1 << i;
+#ifdef NEGATIVE
+ *dest++ = res;
+#else
+ *dest++ = ~res;
+#endif
+ }
+}
+
+static int write_vmem(struct fbtft_par *par, size_t offset, size_t len)
+{
+ u16 *vmem16 = (u16 *)par->info->screen_base;
+ u8 *buf = par->txbuf.buf;
+ int x, y;
+ int ret = 0;
+
+ /* buffer to convert RGB565 -> grayscale16 -> Dithered image 1bpp */
+ signed short *convert_buf = kmalloc(par->info->var.xres *
+ par->info->var.yres * sizeof(signed short), GFP_NOIO);
+
+ if (!convert_buf)
+ return -ENOMEM;
+
+ fbtft_par_dbg(DEBUG_WRITE_VMEM, par, "%s()\n", __func__);
+
+ /* converting to grayscale16 */
+ for (x = 0; x < par->info->var.xres; ++x)
+ for (y = 0; y < par->info->var.yres; ++y) {
+ u16 pixel = vmem16[y * par->info->var.xres + x];
+ u16 b = pixel & 0x1f;
+ u16 g = (pixel & (0x3f << 5)) >> 5;
+ u16 r = (pixel & (0x1f << (5 + 6))) >> (5 + 6);
+
+ pixel = (299 * r + 587 * g + 114 * b) / 200;
+ if (pixel > 255)
+ pixel = 255;
+
+ /* gamma-correction by table */
+ convert_buf[y * par->info->var.xres + x] =
+ (signed short)gamma_correction_table[pixel];
+ }
+
+ /* Image Dithering */
+ for (x = 0; x < par->info->var.xres; ++x)
+ for (y = 0; y < par->info->var.yres; ++y) {
+ signed short pixel =
+ convert_buf[y * par->info->var.xres + x];
+ signed short error_b = pixel - BLACK;
+ signed short error_w = pixel - WHITE;
+ signed short error;
+ u16 i, j;
+
+ /* what color close? */
+ if (abs(error_b) >= abs(error_w)) {
+ /* white */
+ error = error_w;
+ pixel = 0xff;
+ } else {
+ /* black */
+ error = error_b;
+ pixel = 0;
+ }
+
+ error /= 8;
+
+ /* diffusion matrix row */
+ for (i = 0; i < DIFFUSING_MATRIX_WIDTH; ++i)
+ /* diffusion matrix column */
+ for (j = 0; j < DIFFUSING_MATRIX_HEIGHT; ++j) {
+ signed short *write_pos;
+ signed char coeff;
+
+ /* skip pixels out of zone */
+ if (x + i < 0 ||
+ x + i >= par->info->var.xres
+ || y + j >= par->info->var.yres)
+ continue;
+ write_pos = &convert_buf[
+ (y + j) * par->info->var.xres +
+ x + i];
+ coeff = diffusing_matrix[i][j];
+ if (coeff == -1)
+ /* pixel itself */
+ *write_pos = pixel;
+ else {
+ signed short p = *write_pos +
+ error * coeff;
+
+ if (p > WHITE)
+ p = WHITE;
+ if (p < BLACK)
+ p = BLACK;
+ *write_pos = p;
+ }
+ }
+ }
+
+ /* 1 string = 2 pages */
+ for (y = addr_win.ys_page; y <= addr_win.ye_page; ++y) {
+ /* left half of display */
+ if (addr_win.xs < par->info->var.xres / 2) {
+ construct_line_bitmap(par, buf, convert_buf,
+ addr_win.xs, par->info->var.xres / 2, y);
+
+ len = par->info->var.xres / 2 - addr_win.xs;
+
+ /* select left side (sc0)
+ * set addr
+ */
+ write_reg(par, 0x00, (1 << 6) | (u8)addr_win.xs);
+ write_reg(par, 0x00, (0x17 << 3) | (u8)y);
+
+ /* write bitmap */
+ gpio_set_value(par->RS, 1); /* RS->1 (data mode) */
+ ret = par->fbtftops.write(par, buf, len);
+ if (ret < 0)
+ dev_err(par->info->device,
+ "write failed and returned: %d\n",
+ ret);
+ }
+ /* right half of display */
+ if (addr_win.xe >= par->info->var.xres / 2) {
+ construct_line_bitmap(par, buf,
+ convert_buf, par->info->var.xres / 2,
+ addr_win.xe + 1, y);
+
+ len = addr_win.xe + 1 - par->info->var.xres / 2;
+
+ /* select right side (sc1)
+ * set addr
+ */
+ write_reg(par, 0x01, 1 << 6);
+ write_reg(par, 0x01, (0x17 << 3) | (u8)y);
+
+ /* write bitmap */
+ gpio_set_value(par->RS, 1); /* RS->1 (data mode) */
+ par->fbtftops.write(par, buf, len);
+ if (ret < 0)
+ dev_err(par->info->device,
+ "write failed and returned: %d\n",
+ ret);
+ }
+ }
+ kfree(convert_buf);
+
+ gpio_set_value(par->CS0, 1);
+ gpio_set_value(par->CS1, 1);
+
+ return ret;
+}
+
+static int write(struct fbtft_par *par, void *buf, size_t len)
+{
+ fbtft_par_dbg_hex(DEBUG_WRITE, par, par->info->device, u8, buf, len,
+ "%s(len=%d): ", __func__, len);
+
+ gpio_set_value(par->RW, 0); /* set write mode */
+
+
+ while (len--) {
+ u8 i, data;
+
+ data = *(u8 *) buf++;
+
+ /* set data bus */
+ for (i = 0; i < 8; ++i)
+ gpio_set_value(par->gpio.db[i], data & (1 << i));
+ /* set E */
+ gpio_set_value(par->EPIN, 1);
+ udelay(5);
+ /* unset E - write */
+ gpio_set_value(par->EPIN, 0);
+ udelay(1);
+ }
+
+ return 0;
+}
+
+static struct fbtft_display display = {
+ .regwidth = 8,
+ .width = TOTALWIDTH,
+ .height = HEIGHT,
+ .fps = FPS,
+ .fbtftops = {
+ .init_display = init_display,
+ .set_addr_win = set_addr_win,
+ .verify_gpios = verify_gpios,
+ .request_gpios_match = request_gpios_match,
+ .reset = reset,
+ .write = write,
+ .write_register = write_reg8_bus8,
+ .write_vmem = write_vmem,
+ },
+};
+FBTFT_REGISTER_DRIVER(DRVNAME, "displaytronic,fb_agm1264k-fl", &display);
+
+MODULE_ALIAS("platform:" DRVNAME);
+
+MODULE_DESCRIPTION("Two KS0108 LCD controllers in AGM1264K-FL display");
+MODULE_AUTHOR("ololoshka2871");
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/fbtft/fb_bd663474.c b/drivers/staging/fbtft/fb_bd663474.c
new file mode 100644
index 000000000..17a2162a7
--- /dev/null
+++ b/drivers/staging/fbtft/fb_bd663474.c
@@ -0,0 +1,193 @@
+/*
+ * FB driver for the uPD161704 LCD Controller
+ *
+ * Copyright (C) 2014 Seong-Woo Kim
+ *
+ * Based on fb_ili9325.c by Noralf Tronnes
+ * Based on ili9325.c by Jeroen Domburg
+ * Init code from UTFT library by Henning Karlsen
+ *
+ * This program is free software; you can redistribute it and/or 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/kernel.h>
+#include <linux/init.h>
+#include <linux/gpio.h>
+#include <linux/delay.h>
+
+#include "fbtft.h"
+
+#define DRVNAME "fb_bd663474"
+#define WIDTH 240
+#define HEIGHT 320
+#define BPP 16
+
+static int init_display(struct fbtft_par *par)
+{
+ fbtft_par_dbg(DEBUG_INIT_DISPLAY, par, "%s()\n", __func__);
+
+ if (par->gpio.cs != -1)
+ gpio_set_value(par->gpio.cs, 0); /* Activate chip */
+
+ par->fbtftops.reset(par);
+
+ /* Initialization sequence from Lib_UTFT */
+
+ /* oscillator start */
+ write_reg(par, 0x000, 0x0001); /*oscillator 0: stop, 1: operation */
+ mdelay(10);
+
+ /* Power settings */
+ write_reg(par, 0x100, 0x0000); /* power supply setup */
+ write_reg(par, 0x101, 0x0000);
+ write_reg(par, 0x102, 0x3110);
+ write_reg(par, 0x103, 0xe200);
+ write_reg(par, 0x110, 0x009d);
+ write_reg(par, 0x111, 0x0022);
+ write_reg(par, 0x100, 0x0120);
+ mdelay(20);
+
+ write_reg(par, 0x100, 0x3120);
+ mdelay(80);
+ /* Display control */
+ write_reg(par, 0x001, 0x0100);
+ write_reg(par, 0x002, 0x0000);
+ write_reg(par, 0x003, 0x1230);
+ write_reg(par, 0x006, 0x0000);
+ write_reg(par, 0x007, 0x0101);
+ write_reg(par, 0x008, 0x0808);
+ write_reg(par, 0x009, 0x0000);
+ write_reg(par, 0x00b, 0x0000);
+ write_reg(par, 0x00c, 0x0000);
+ write_reg(par, 0x00d, 0x0018);
+ /* LTPS control settings */
+ write_reg(par, 0x012, 0x0000);
+ write_reg(par, 0x013, 0x0000);
+ write_reg(par, 0x018, 0x0000);
+ write_reg(par, 0x019, 0x0000);
+
+ write_reg(par, 0x203, 0x0000);
+ write_reg(par, 0x204, 0x0000);
+
+ write_reg(par, 0x210, 0x0000);
+ write_reg(par, 0x211, 0x00ef);
+ write_reg(par, 0x212, 0x0000);
+ write_reg(par, 0x213, 0x013f);
+ write_reg(par, 0x214, 0x0000);
+ write_reg(par, 0x215, 0x0000);
+ write_reg(par, 0x216, 0x0000);
+ write_reg(par, 0x217, 0x0000);
+
+ /* Gray scale settings */
+ write_reg(par, 0x300, 0x5343);
+ write_reg(par, 0x301, 0x1021);
+ write_reg(par, 0x302, 0x0003);
+ write_reg(par, 0x303, 0x0011);
+ write_reg(par, 0x304, 0x050a);
+ write_reg(par, 0x305, 0x4342);
+ write_reg(par, 0x306, 0x1100);
+ write_reg(par, 0x307, 0x0003);
+ write_reg(par, 0x308, 0x1201);
+ write_reg(par, 0x309, 0x050a);
+
+ /* RAM access settings */
+ write_reg(par, 0x400, 0x4027);
+ write_reg(par, 0x401, 0x0000);
+ write_reg(par, 0x402, 0x0000); /* First screen drive position (1) */
+ write_reg(par, 0x403, 0x013f); /* First screen drive position (2) */
+ write_reg(par, 0x404, 0x0000);
+
+ write_reg(par, 0x200, 0x0000);
+ write_reg(par, 0x201, 0x0000);
+ write_reg(par, 0x100, 0x7120);
+ write_reg(par, 0x007, 0x0103);
+ mdelay(10);
+ write_reg(par, 0x007, 0x0113);
+
+ return 0;
+}
+
+static void set_addr_win(struct fbtft_par *par, int xs, int ys, int xe, int ye)
+{
+ fbtft_par_dbg(DEBUG_SET_ADDR_WIN, par,
+ "%s(xs=%d, ys=%d, xe=%d, ye=%d)\n", __func__, xs, ys, xe, ye);
+ switch (par->info->var.rotate) {
+ /* R200h = Horizontal GRAM Start Address */
+ /* R201h = Vertical GRAM Start Address */
+ case 0:
+ write_reg(par, 0x0200, xs);
+ write_reg(par, 0x0201, ys);
+ break;
+ case 180:
+ write_reg(par, 0x0200, WIDTH - 1 - xs);
+ write_reg(par, 0x0201, HEIGHT - 1 - ys);
+ break;
+ case 270:
+ write_reg(par, 0x0200, WIDTH - 1 - ys);
+ write_reg(par, 0x0201, xs);
+ break;
+ case 90:
+ write_reg(par, 0x0200, ys);
+ write_reg(par, 0x0201, HEIGHT - 1 - xs);
+ break;
+ }
+ write_reg(par, 0x202); /* Write Data to GRAM */
+}
+
+static int set_var(struct fbtft_par *par)
+{
+ fbtft_par_dbg(DEBUG_INIT_DISPLAY, par, "%s()\n", __func__);
+
+ switch (par->info->var.rotate) {
+ /* AM: GRAM update direction */
+ case 0:
+ write_reg(par, 0x003, 0x1230);
+ break;
+ case 180:
+ write_reg(par, 0x003, 0x1200);
+ break;
+ case 270:
+ write_reg(par, 0x003, 0x1228);
+ break;
+ case 90:
+ write_reg(par, 0x003, 0x1218);
+ break;
+ }
+
+ return 0;
+}
+
+static struct fbtft_display display = {
+ .regwidth = 16,
+ .width = WIDTH,
+ .height = HEIGHT,
+ .bpp = BPP,
+ .fbtftops = {
+ .init_display = init_display,
+ .set_addr_win = set_addr_win,
+ .set_var = set_var,
+ },
+};
+FBTFT_REGISTER_DRIVER(DRVNAME, "hitachi,bd663474", &display);
+
+MODULE_ALIAS("spi:" DRVNAME);
+MODULE_ALIAS("platform:" DRVNAME);
+MODULE_ALIAS("spi:bd663474");
+MODULE_ALIAS("platform:bd663474");
+
+MODULE_DESCRIPTION("FB driver for the uPD161704 LCD Controller");
+MODULE_AUTHOR("Seong-Woo Kim");
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/fbtft/fb_hx8340bn.c b/drivers/staging/fbtft/fb_hx8340bn.c
new file mode 100644
index 000000000..54528aa0c
--- /dev/null
+++ b/drivers/staging/fbtft/fb_hx8340bn.c
@@ -0,0 +1,220 @@
+/*
+ * FB driver for the HX8340BN LCD Controller
+ *
+ * This display uses 9-bit SPI: Data/Command bit + 8 data bits
+ * For platforms that doesn't support 9-bit, the driver is capable
+ * of emulating this using 8-bit transfer.
+ * This is done by transferring eight 9-bit words in 9 bytes.
+ *
+ * Copyright (C) 2013 Noralf Tronnes
+ *
+ * This program is free software; you can redistribute it and/or 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/kernel.h>
+#include <linux/init.h>
+#include <linux/vmalloc.h>
+#include <linux/spi/spi.h>
+#include <linux/delay.h>
+
+#include "fbtft.h"
+
+#define DRVNAME "fb_hx8340bn"
+#define WIDTH 176
+#define HEIGHT 220
+#define TXBUFLEN (4 * PAGE_SIZE)
+#define DEFAULT_GAMMA "1 3 0E 5 0 2 09 0 6 1 7 1 0 2 2\n" \
+ "3 3 17 8 4 7 05 7 6 0 3 1 6 0 0 "
+
+
+static bool emulate;
+module_param(emulate, bool, 0);
+MODULE_PARM_DESC(emulate, "Force emulation in 9-bit mode");
+
+
+static int init_display(struct fbtft_par *par)
+{
+ par->fbtftops.reset(par);
+
+ /* BTL221722-276L startup sequence, from datasheet */
+
+ /* SETEXTCOM: Set extended command set (C1h)
+ This command is used to set extended command set access enable.
+ Enable: After command (C1h), must write: ffh,83h,40h */
+ write_reg(par, 0xC1, 0xFF, 0x83, 0x40);
+
+ /* Sleep out
+ This command turns off sleep mode.
+ In this mode the DC/DC converter is enabled, Internal oscillator
+ is started, and panel scanning is started. */
+ write_reg(par, 0x11);
+ mdelay(150);
+
+ /* Undoc'd register? */
+ write_reg(par, 0xCA, 0x70, 0x00, 0xD9);
+
+ /* SETOSC: Set Internal Oscillator (B0h)
+ This command is used to set internal oscillator related settings */
+ /* OSC_EN: Enable internal oscillator */
+ /* Internal oscillator frequency: 125% x 2.52MHz */
+ write_reg(par, 0xB0, 0x01, 0x11);
+
+ /* Drive ability setting */
+ write_reg(par, 0xC9, 0x90, 0x49, 0x10, 0x28, 0x28, 0x10, 0x00, 0x06);
+ mdelay(20);
+
+ /* SETPWCTR5: Set Power Control 5(B5h)
+ This command is used to set VCOM Low and VCOM High Voltage */
+ /* VCOMH 0110101 : 3.925 */
+ /* VCOML 0100000 : -1.700 */
+ /* 45h=69 VCOMH: "VMH" + 5d VCOML: "VMH" + 5d */
+ write_reg(par, 0xB5, 0x35, 0x20, 0x45);
+
+ /* SETPWCTR4: Set Power Control 4(B4h)
+ VRH[4:0]: Specify the VREG1 voltage adjusting.
+ VREG1 voltage is for gamma voltage setting.
+ BT[2:0]: Switch the output factor of step-up circuit 2
+ for VGH and VGL voltage generation. */
+ write_reg(par, 0xB4, 0x33, 0x25, 0x4C);
+ mdelay(10);
+
+ /* Interface Pixel Format (3Ah)
+ This command is used to define the format of RGB picture data,
+ which is to be transfer via the system and RGB interface. */
+ /* RGB interface: 16 Bit/Pixel */
+ write_reg(par, 0x3A, 0x05);
+
+ /* Display on (29h)
+ This command is used to recover from DISPLAY OFF mode.
+ Output from the Frame Memory is enabled. */
+ write_reg(par, 0x29);
+ mdelay(10);
+
+ return 0;
+}
+
+static void set_addr_win(struct fbtft_par *par, int xs, int ys, int xe, int ye)
+{
+ write_reg(par, FBTFT_CASET, 0x00, xs, 0x00, xe);
+ write_reg(par, FBTFT_RASET, 0x00, ys, 0x00, ye);
+ write_reg(par, FBTFT_RAMWR);
+}
+
+static int set_var(struct fbtft_par *par)
+{
+ /* MADCTL - Memory data access control */
+ /* RGB/BGR can be set with H/W pin SRGB and MADCTL BGR bit */
+#define MY (1 << 7)
+#define MX (1 << 6)
+#define MV (1 << 5)
+ switch (par->info->var.rotate) {
+ case 0:
+ write_reg(par, 0x36, par->bgr << 3);
+ break;
+ case 270:
+ write_reg(par, 0x36, MX | MV | (par->bgr << 3));
+ break;
+ case 180:
+ write_reg(par, 0x36, MX | MY | (par->bgr << 3));
+ break;
+ case 90:
+ write_reg(par, 0x36, MY | MV | (par->bgr << 3));
+ break;
+ }
+
+ return 0;
+}
+
+/*
+ Gamma Curve selection, GC (only GC0 can be customized):
+ 0 = 2.2, 1 = 1.8, 2 = 2.5, 3 = 1.0
+ Gamma string format:
+ OP0 OP1 CP0 CP1 CP2 CP3 CP4 MP0 MP1 MP2 MP3 MP4 MP5 CGM0 CGM1
+ ON0 ON1 CN0 CN1 CN2 CN3 CN4 MN0 MN1 MN2 MN3 MN4 MN5 XXXX GC
+*/
+#define CURVE(num, idx) curves[num*par->gamma.num_values + idx]
+static int set_gamma(struct fbtft_par *par, unsigned long *curves)
+{
+ unsigned long mask[] = {
+ 0x0f, 0x0f, 0x1f, 0x0f, 0x0f, 0x0f, 0x1f, 0x07, 0x07, 0x07,
+ 0x07, 0x07, 0x07, 0x03, 0x03, 0x0f, 0x0f, 0x1f, 0x0f, 0x0f,
+ 0x0f, 0x1f, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x00, 0x00,
+ };
+ int i, j;
+
+ /* apply mask */
+ for (i = 0; i < par->gamma.num_curves; i++)
+ for (j = 0; j < par->gamma.num_values; j++)
+ CURVE(i, j) &= mask[i * par->gamma.num_values + j];
+
+ write_reg(par, 0x26, 1 << CURVE(1, 14)); /* Gamma Set (26h) */
+
+ if (CURVE(1, 14))
+ return 0; /* only GC0 can be customized */
+
+ write_reg(par, 0xC2,
+ (CURVE(0, 8) << 4) | CURVE(0, 7),
+ (CURVE(0, 10) << 4) | CURVE(0, 9),
+ (CURVE(0, 12) << 4) | CURVE(0, 11),
+ CURVE(0, 2),
+ (CURVE(0, 4) << 4) | CURVE(0, 3),
+ CURVE(0, 5),
+ CURVE(0, 6),
+ (CURVE(0, 1) << 4) | CURVE(0, 0),
+ (CURVE(0, 14) << 2) | CURVE(0, 13));
+
+ write_reg(par, 0xC3,
+ (CURVE(1, 8) << 4) | CURVE(1, 7),
+ (CURVE(1, 10) << 4) | CURVE(1, 9),
+ (CURVE(1, 12) << 4) | CURVE(1, 11),
+ CURVE(1, 2),
+ (CURVE(1, 4) << 4) | CURVE(1, 3),
+ CURVE(1, 5),
+ CURVE(1, 6),
+ (CURVE(1, 1) << 4) | CURVE(1, 0));
+
+ mdelay(10);
+
+ return 0;
+}
+#undef CURVE
+
+
+static struct fbtft_display display = {
+ .regwidth = 8,
+ .width = WIDTH,
+ .height = HEIGHT,
+ .txbuflen = TXBUFLEN,
+ .gamma_num = 2,
+ .gamma_len = 15,
+ .gamma = DEFAULT_GAMMA,
+ .fbtftops = {
+ .init_display = init_display,
+ .set_addr_win = set_addr_win,
+ .set_var = set_var,
+ .set_gamma = set_gamma,
+ },
+};
+FBTFT_REGISTER_DRIVER(DRVNAME, "himax,hx8340bn", &display);
+
+MODULE_ALIAS("spi:" DRVNAME);
+MODULE_ALIAS("platform:" DRVNAME);
+MODULE_ALIAS("spi:hx8340bn");
+MODULE_ALIAS("platform:hx8340bn");
+
+MODULE_DESCRIPTION("FB driver for the HX8340BN LCD Controller");
+MODULE_AUTHOR("Noralf Tronnes");
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/fbtft/fb_hx8347d.c b/drivers/staging/fbtft/fb_hx8347d.c
new file mode 100644
index 000000000..03ae95b4f
--- /dev/null
+++ b/drivers/staging/fbtft/fb_hx8347d.c
@@ -0,0 +1,180 @@
+/*
+ * FB driver for the HX8347D LCD Controller
+ *
+ * Copyright (C) 2013 Christian Vogelgsang
+ *
+ * Based on driver code found here: https://github.com/watterott/r61505u-Adapter
+ *
+ * This program is free software; you can redistribute it and/or 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/kernel.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+
+#include "fbtft.h"
+
+#define DRVNAME "fb_hx8347d"
+#define WIDTH 320
+#define HEIGHT 240
+#define DEFAULT_GAMMA "0 0 0 0 0 0 0 0 0 0 0 0 0 0\n" \
+ "0 0 0 0 0 0 0 0 0 0 0 0 0 0"
+
+
+static int init_display(struct fbtft_par *par)
+{
+ fbtft_par_dbg(DEBUG_INIT_DISPLAY, par, "%s()\n", __func__);
+
+ par->fbtftops.reset(par);
+
+ /* driving ability */
+ write_reg(par, 0xEA, 0x00);
+ write_reg(par, 0xEB, 0x20);
+ write_reg(par, 0xEC, 0x0C);
+ write_reg(par, 0xED, 0xC4);
+ write_reg(par, 0xE8, 0x40);
+ write_reg(par, 0xE9, 0x38);
+ write_reg(par, 0xF1, 0x01);
+ write_reg(par, 0xF2, 0x10);
+ write_reg(par, 0x27, 0xA3);
+
+ /* power voltage */
+ write_reg(par, 0x1B, 0x1B);
+ write_reg(par, 0x1A, 0x01);
+ write_reg(par, 0x24, 0x2F);
+ write_reg(par, 0x25, 0x57);
+
+ /* VCOM offset */
+ write_reg(par, 0x23, 0x8D); /* for flicker adjust */
+
+ /* power on */
+ write_reg(par, 0x18, 0x36);
+ write_reg(par, 0x19, 0x01); /* start osc */
+ write_reg(par, 0x01, 0x00); /* wakeup */
+ write_reg(par, 0x1F, 0x88);
+ mdelay(5);
+ write_reg(par, 0x1F, 0x80);
+ mdelay(5);
+ write_reg(par, 0x1F, 0x90);
+ mdelay(5);
+ write_reg(par, 0x1F, 0xD0);
+ mdelay(5);
+
+ /* color selection */
+ write_reg(par, 0x17, 0x05); /* 65k */
+
+ /*panel characteristic */
+ write_reg(par, 0x36, 0x00);
+
+ /*display on */
+ write_reg(par, 0x28, 0x38);
+ mdelay(40);
+ write_reg(par, 0x28, 0x3C);
+
+ /* orientation */
+ write_reg(par, 0x16, 0x60 | (par->bgr << 3));
+
+ return 0;
+}
+
+static void set_addr_win(struct fbtft_par *par, int xs, int ys, int xe, int ye)
+{
+ fbtft_par_dbg(DEBUG_SET_ADDR_WIN, par,
+ "%s(xs=%d, ys=%d, xe=%d, ye=%d)\n", __func__, xs, ys, xe, ye);
+
+ write_reg(par, 0x02, (xs >> 8) & 0xFF);
+ write_reg(par, 0x03, xs & 0xFF);
+ write_reg(par, 0x04, (xe >> 8) & 0xFF);
+ write_reg(par, 0x05, xe & 0xFF);
+ write_reg(par, 0x06, (ys >> 8) & 0xFF);
+ write_reg(par, 0x07, ys & 0xFF);
+ write_reg(par, 0x08, (ye >> 8) & 0xFF);
+ write_reg(par, 0x09, ye & 0xFF);
+ write_reg(par, 0x22);
+}
+
+/*
+ Gamma string format:
+ VRP0 VRP1 VRP2 VRP3 VRP4 VRP5 PRP0 PRP1 PKP0 PKP1 PKP2 PKP3 PKP4 CGM
+ VRN0 VRN1 VRN2 VRN3 VRN4 VRN5 PRN0 PRN1 PKN0 PKN1 PKN2 PKN3 PKN4 CGM
+*/
+#define CURVE(num, idx) curves[num*par->gamma.num_values + idx]
+static int set_gamma(struct fbtft_par *par, unsigned long *curves)
+{
+ unsigned long mask[] = {
+ 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x7f, 0x7f, 0x1f, 0x1f,
+ 0x1f, 0x1f, 0x1f, 0x0f,
+ };
+ int i, j;
+ int acc = 0;
+
+ fbtft_par_dbg(DEBUG_INIT_DISPLAY, par, "%s()\n", __func__);
+
+ /* apply mask */
+ for (i = 0; i < par->gamma.num_curves; i++)
+ for (j = 0; j < par->gamma.num_values; j++) {
+ acc += CURVE(i, j);
+ CURVE(i, j) &= mask[j];
+ }
+
+ if (acc == 0) /* skip if all values are zero */
+ return 0;
+
+ for (i = 0; i < par->gamma.num_curves; i++) {
+ write_reg(par, 0x40 + (i * 0x10), CURVE(i, 0));
+ write_reg(par, 0x41 + (i * 0x10), CURVE(i, 1));
+ write_reg(par, 0x42 + (i * 0x10), CURVE(i, 2));
+ write_reg(par, 0x43 + (i * 0x10), CURVE(i, 3));
+ write_reg(par, 0x44 + (i * 0x10), CURVE(i, 4));
+ write_reg(par, 0x45 + (i * 0x10), CURVE(i, 5));
+ write_reg(par, 0x46 + (i * 0x10), CURVE(i, 6));
+ write_reg(par, 0x47 + (i * 0x10), CURVE(i, 7));
+ write_reg(par, 0x48 + (i * 0x10), CURVE(i, 8));
+ write_reg(par, 0x49 + (i * 0x10), CURVE(i, 9));
+ write_reg(par, 0x4A + (i * 0x10), CURVE(i, 10));
+ write_reg(par, 0x4B + (i * 0x10), CURVE(i, 11));
+ write_reg(par, 0x4C + (i * 0x10), CURVE(i, 12));
+ }
+ write_reg(par, 0x5D, (CURVE(1, 0) << 4) | CURVE(0, 0));
+
+ return 0;
+}
+#undef CURVE
+
+
+static struct fbtft_display display = {
+ .regwidth = 8,
+ .width = WIDTH,
+ .height = HEIGHT,
+ .gamma_num = 2,
+ .gamma_len = 14,
+ .gamma = DEFAULT_GAMMA,
+ .fbtftops = {
+ .init_display = init_display,
+ .set_addr_win = set_addr_win,
+ .set_gamma = set_gamma,
+ },
+};
+FBTFT_REGISTER_DRIVER(DRVNAME, "himax,hx8347d", &display);
+
+MODULE_ALIAS("spi:" DRVNAME);
+MODULE_ALIAS("platform:" DRVNAME);
+MODULE_ALIAS("spi:hx8347d");
+MODULE_ALIAS("platform:hx8347d");
+
+MODULE_DESCRIPTION("FB driver for the HX8347D LCD Controller");
+MODULE_AUTHOR("Christian Vogelgsang");
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/fbtft/fb_hx8353d.c b/drivers/staging/fbtft/fb_hx8353d.c
new file mode 100644
index 000000000..d7f4308e1
--- /dev/null
+++ b/drivers/staging/fbtft/fb_hx8353d.c
@@ -0,0 +1,166 @@
+/*
+ * FB driver for the HX8353D LCD Controller
+ *
+ * Copyright (c) 2014 Petr Olivka
+ * Copyright (c) 2013 Noralf Tronnes
+ *
+ * This program is free software; you can redistribute it and/or 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/kernel.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+
+#include "fbtft.h"
+
+#define DRVNAME "fb_hx8353d"
+#define DEFAULT_GAMMA "50 77 40 08 BF 00 03 0F 00 01 73 00 72 03 B0 0F 08 00 0F"
+
+static int init_display(struct fbtft_par *par)
+{
+
+ fbtft_par_dbg(DEBUG_INIT_DISPLAY, par, "%s()\n", __func__);
+
+ par->fbtftops.reset(par);
+ mdelay(150);
+
+ /* SETEXTC */
+ write_reg(par, 0xB9, 0xFF, 0x83, 0x53);
+
+ /* RADJ */
+ write_reg(par, 0xB0, 0x3C, 0x01);
+
+ /* VCOM */
+ write_reg(par, 0xB6, 0x94, 0x6C, 0x50);
+
+ /* PWR */
+ write_reg(par, 0xB1, 0x00, 0x01, 0x1B, 0x03, 0x01, 0x08, 0x77, 0x89);
+
+ /* COLMOD */
+ write_reg(par, 0x3A, 0x05);
+
+ /* MEM ACCESS */
+ write_reg(par, 0x36, 0xC0);
+
+ /* SLPOUT - Sleep out & booster on */
+ write_reg(par, 0x11);
+ mdelay(150);
+
+ /* DISPON - Display On */
+ write_reg(par, 0x29);
+
+ /* RGBSET */
+ write_reg(par, 0x2D,
+ 0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30,
+ 32, 34, 36, 38, 40, 42, 44, 46, 48, 50, 52, 54, 56, 58, 60, 62,
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
+ 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
+ 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
+ 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63,
+ 0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30,
+ 32, 34, 36, 38, 40, 42, 44, 46, 48, 50, 52, 54, 56, 58, 60, 62);
+
+ return 0;
+};
+
+static void set_addr_win(struct fbtft_par *par, int xs, int ys, int xe, int ye)
+{
+ fbtft_par_dbg(DEBUG_SET_ADDR_WIN, par,
+ "%s(xs=%d, ys=%d, xe=%d, ye=%d)\n", __func__, xs, ys, xe, ye);
+
+ /* column address */
+ write_reg(par, 0x2a, xs >> 8, xs & 0xff, xe >> 8, xe & 0xff);
+
+ /* Row address */
+ write_reg(par, 0x2b, ys >> 8, ys & 0xff, ye >> 8, ye & 0xff);
+
+ /* memory write */
+ write_reg(par, 0x2c);
+}
+
+#define my (1 << 7)
+#define mx (1 << 6)
+#define mv (1 << 5)
+static int set_var(struct fbtft_par *par)
+{
+ fbtft_par_dbg(DEBUG_INIT_DISPLAY, par, "%s()\n", __func__);
+
+ /* madctl - memory data access control
+ rgb/bgr:
+ 1. mode selection pin srgb
+ rgb h/w pin for color filter setting: 0=rgb, 1=bgr
+ 2. madctl rgb bit
+ rgb-bgr order color filter panel: 0=rgb, 1=bgr */
+ switch (par->info->var.rotate) {
+ case 0:
+ write_reg(par, 0x36, mx | my | (par->bgr << 3));
+ break;
+ case 270:
+ write_reg(par, 0x36, my | mv | (par->bgr << 3));
+ break;
+ case 180:
+ write_reg(par, 0x36, par->bgr << 3);
+ break;
+ case 90:
+ write_reg(par, 0x36, mx | mv | (par->bgr << 3));
+ break;
+ }
+
+ return 0;
+}
+
+/*
+ gamma string format:
+*/
+static int set_gamma(struct fbtft_par *par, unsigned long *curves)
+{
+ fbtft_par_dbg(DEBUG_INIT_DISPLAY, par, "%s()\n", __func__);
+
+ write_reg(par, 0xE0,
+ curves[0], curves[1], curves[2], curves[3],
+ curves[4], curves[5], curves[6], curves[7],
+ curves[8], curves[9], curves[10], curves[11],
+ curves[12], curves[13], curves[14], curves[15],
+ curves[16], curves[17], curves[18]);
+
+ return 0;
+}
+
+
+static struct fbtft_display display = {
+ .regwidth = 8,
+ .width = 128,
+ .height = 160,
+ .gamma_num = 1,
+ .gamma_len = 19,
+ .gamma = DEFAULT_GAMMA,
+ .fbtftops = {
+ .init_display = init_display,
+ .set_addr_win = set_addr_win,
+ .set_var = set_var,
+ .set_gamma = set_gamma,
+ },
+};
+FBTFT_REGISTER_DRIVER(DRVNAME, "himax,hx8353d", &display);
+
+MODULE_ALIAS("spi:" DRVNAME);
+MODULE_ALIAS("platform:" DRVNAME);
+MODULE_ALIAS("spi:hx8353d");
+MODULE_ALIAS("platform:hx8353d");
+
+MODULE_DESCRIPTION("FB driver for the HX8353D LCD Controller");
+MODULE_AUTHOR("Petr Olivka");
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/fbtft/fb_ili9163.c b/drivers/staging/fbtft/fb_ili9163.c
new file mode 100644
index 000000000..ed92a6430
--- /dev/null
+++ b/drivers/staging/fbtft/fb_ili9163.c
@@ -0,0 +1,303 @@
+/*
+ * FB driver for the ILI9163 LCD Controller
+ *
+ * Copyright (C) 2015 Kozhevnikov Anatoly
+ *
+ * Based on ili9325.c by Noralf Tronnes and
+ * .S.U.M.O.T.O.Y. by Max MC Costa (https://github.com/sumotoy/TFT_ILI9163C).
+ *
+ * This program is free software; you can redistribute it and/or 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.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/gpio.h>
+#include <linux/delay.h>
+
+#include "fbtft.h"
+
+#define DRVNAME "fb_ili9163"
+#define WIDTH 128
+#define HEIGHT 128
+#define BPP 16
+#define FPS 30
+
+#ifdef GAMMA_ADJ
+#define GAMMA_LEN 15
+#define GAMMA_NUM 1
+#define DEFAULT_GAMMA "36 29 12 22 1C 15 42 B7 2F 13 12 0A 11 0B 06\n"
+#endif
+
+/* ILI9163C commands */
+#define CMD_NOP 0x00 /* Non operation*/
+#define CMD_SWRESET 0x01 /* Soft Reset */
+#define CMD_SLPIN 0x10 /* Sleep ON */
+#define CMD_SLPOUT 0x11 /* Sleep OFF */
+#define CMD_PTLON 0x12 /* Partial Mode ON */
+#define CMD_NORML 0x13 /* Normal Display ON */
+#define CMD_DINVOF 0x20 /* Display Inversion OFF */
+#define CMD_DINVON 0x21 /* Display Inversion ON */
+#define CMD_GAMMASET 0x26 /* Gamma Set (0x01[1],0x02[2],0x04[3],0x08[4]) */
+#define CMD_DISPOFF 0x28 /* Display OFF */
+#define CMD_DISPON 0x29 /* Display ON */
+#define CMD_IDLEON 0x39 /* Idle Mode ON */
+#define CMD_IDLEOF 0x38 /* Idle Mode OFF */
+#define CMD_CLMADRS 0x2A /* Column Address Set */
+#define CMD_PGEADRS 0x2B /* Page Address Set */
+
+#define CMD_RAMWR 0x2C /* Memory Write */
+#define CMD_RAMRD 0x2E /* Memory Read */
+#define CMD_CLRSPACE 0x2D /* Color Space : 4K/65K/262K */
+#define CMD_PARTAREA 0x30 /* Partial Area */
+#define CMD_VSCLLDEF 0x33 /* Vertical Scroll Definition */
+#define CMD_TEFXLON 0x34 /* Tearing Effect Line ON */
+#define CMD_TEFXLOF 0x35 /* Tearing Effect Line OFF */
+#define CMD_MADCTL 0x36 /* Memory Access Control */
+
+#define CMD_PIXFMT 0x3A /* Interface Pixel Format */
+#define CMD_FRMCTR1 0xB1 /* Frame Rate Control
+ (In normal mode/Full colors) */
+#define CMD_FRMCTR2 0xB2 /* Frame Rate Control (In Idle mode/8-colors) */
+#define CMD_FRMCTR3 0xB3 /* Frame Rate Control
+ (In Partial mode/full colors) */
+#define CMD_DINVCTR 0xB4 /* Display Inversion Control */
+#define CMD_RGBBLK 0xB5 /* RGB Interface Blanking Porch setting */
+#define CMD_DFUNCTR 0xB6 /* Display Function set 5 */
+#define CMD_SDRVDIR 0xB7 /* Source Driver Direction Control */
+#define CMD_GDRVDIR 0xB8 /* Gate Driver Direction Control */
+
+#define CMD_PWCTR1 0xC0 /* Power_Control1 */
+#define CMD_PWCTR2 0xC1 /* Power_Control2 */
+#define CMD_PWCTR3 0xC2 /* Power_Control3 */
+#define CMD_PWCTR4 0xC3 /* Power_Control4 */
+#define CMD_PWCTR5 0xC4 /* Power_Control5 */
+#define CMD_VCOMCTR1 0xC5 /* VCOM_Control 1 */
+#define CMD_VCOMCTR2 0xC6 /* VCOM_Control 2 */
+#define CMD_VCOMOFFS 0xC7 /* VCOM Offset Control */
+#define CMD_PGAMMAC 0xE0 /* Positive Gamma Correction Setting */
+#define CMD_NGAMMAC 0xE1 /* Negative Gamma Correction Setting */
+#define CMD_GAMRSEL 0xF2 /* GAM_R_SEL */
+
+/*
+This display:
+http://www.ebay.com/itm/Replace-Nokia-5110-LCD-1-44-Red-Serial-128X128-SPI-Color-TFT-LCD-Display-Module-/271422122271
+This particular display has a design error! The controller has 3 pins to
+configure to constrain the memory and resolution to a fixed dimension (in
+that case 128x128) but they leaved those pins configured for 128x160 so
+there was several pixel memory addressing problems.
+I solved by setup several parameters that dinamically fix the resolution as
+needit so below the parameters for this display. If you have a strain or a
+correct display (can happen with chinese) you can copy those parameters and
+create setup for different displays.
+*/
+
+#ifdef RED
+#define __OFFSET 32 /*see note 2 - this is the red version */
+#else
+#define __OFFSET 0 /*see note 2 - this is the black version */
+#endif
+
+static int init_display(struct fbtft_par *par)
+{
+ fbtft_par_dbg(DEBUG_INIT_DISPLAY, par, "%s()\n", __func__);
+
+ par->fbtftops.reset(par);
+
+ if (par->gpio.cs != -1)
+ gpio_set_value(par->gpio.cs, 0); /* Activate chip */
+
+ write_reg(par, CMD_SWRESET); /* software reset */
+ mdelay(500);
+ write_reg(par, CMD_SLPOUT); /* exit sleep */
+ mdelay(5);
+ write_reg(par, CMD_PIXFMT, 0x05); /* Set Color Format 16bit */
+ write_reg(par, CMD_GAMMASET, 0x02); /* default gamma curve 3 */
+#ifdef GAMMA_ADJ
+ write_reg(par, CMD_GAMRSEL, 0x01); /* Enable Gamma adj */
+#endif
+ write_reg(par, CMD_NORML);
+ write_reg(par, CMD_DFUNCTR, 0xff, 0x06);
+ /* Frame Rate Control (In normal mode/Full colors) */
+ write_reg(par, CMD_FRMCTR1, 0x08, 0x02);
+ write_reg(par, CMD_DINVCTR, 0x07); /* display inversion */
+ /* Set VRH1[4:0] & VC[2:0] for VCI1 & GVDD */
+ write_reg(par, CMD_PWCTR1, 0x0A, 0x02);
+ /* Set BT[2:0] for AVDD & VCL & VGH & VGL */
+ write_reg(par, CMD_PWCTR2, 0x02);
+ /* Set VMH[6:0] & VML[6:0] for VOMH & VCOML */
+ write_reg(par, CMD_VCOMCTR1, 0x50, 0x63);
+ write_reg(par, CMD_VCOMOFFS, 0);
+
+ write_reg(par, CMD_CLMADRS, 0, 0, 0, WIDTH); /* Set Column Address */
+ write_reg(par, CMD_PGEADRS, 0, 0, 0, HEIGHT); /* Set Page Address */
+
+ write_reg(par, CMD_DISPON); /* display ON */
+ write_reg(par, CMD_RAMWR); /* Memory Write */
+
+ return 0;
+}
+
+static void set_addr_win(struct fbtft_par *par, int xs, int ys,
+ int xe, int ye)
+{
+ fbtft_par_dbg(DEBUG_SET_ADDR_WIN, par,
+ "%s(xs=%d, ys=%d, xe=%d, ye=%d)\n", __func__, xs, ys, xe, ye);
+
+ switch (par->info->var.rotate) {
+ case 0:
+ write_reg(par, CMD_CLMADRS, xs >> 8, xs & 0xff, xe >> 8,
+ xe & 0xff);
+ write_reg(par, CMD_PGEADRS,
+ (ys + __OFFSET) >> 8, (ys + __OFFSET) & 0xff,
+ (ye + __OFFSET) >> 8, (ye + __OFFSET) & 0xff);
+ break;
+ case 90:
+ write_reg(par, CMD_CLMADRS,
+ (xs + __OFFSET) >> 8, (xs + __OFFSET) & 0xff,
+ (xe + __OFFSET) >> 8, (xe + __OFFSET) & 0xff);
+ write_reg(par, CMD_PGEADRS, ys >> 8, ys & 0xff, ye >> 8,
+ ye & 0xff);
+ break;
+ case 180:
+ case 270:
+ write_reg(par, CMD_CLMADRS, xs >> 8, xs & 0xff, xe >> 8,
+ xe & 0xff);
+ write_reg(par, CMD_PGEADRS, ys >> 8, ys & 0xff, ye >> 8,
+ ye & 0xff);
+ break;
+ default:
+ par->info->var.rotate = 0; /* Fix incorrect setting */
+ }
+ write_reg(par, CMD_RAMWR); /* Write Data to GRAM mode */
+}
+
+/*
+7) MY: 1(bottom to top), 0(top to bottom) Row Address Order
+6) MX: 1(R to L), 0(L to R) Column Address Order
+5) MV: 1(Exchanged), 0(normal) Row/Column exchange
+4) ML: 1(bottom to top), 0(top to bottom) Vertical Refresh Order
+3) RGB: 1(BGR), 0(RGB) Color Space
+2) MH: 1(R to L), 0(L to R) Horizontal Refresh Order
+1)
+0)
+
+ MY, MX, MV, ML,RGB, MH, D1, D0
+ 0 | 0 | 0 | 0 | 1 | 0 | 0 | 0 //normal
+ 1 | 0 | 0 | 0 | 1 | 0 | 0 | 0 //Y-Mirror
+ 0 | 1 | 0 | 0 | 1 | 0 | 0 | 0 //X-Mirror
+ 1 | 1 | 0 | 0 | 1 | 0 | 0 | 0 //X-Y-Mirror
+ 0 | 0 | 1 | 0 | 1 | 0 | 0 | 0 //X-Y Exchange
+ 1 | 0 | 1 | 0 | 1 | 0 | 0 | 0 //X-Y Exchange, Y-Mirror
+ 0 | 1 | 1 | 0 | 1 | 0 | 0 | 0 //XY exchange
+ 1 | 1 | 1 | 0 | 1 | 0 | 0 | 0
+*/
+static int set_var(struct fbtft_par *par)
+{
+ u8 mactrl_data = 0; /* Avoid compiler warning */
+
+ fbtft_par_dbg(DEBUG_INIT_DISPLAY, par, "%s()\n", __func__);
+
+ switch (par->info->var.rotate) {
+ case 0:
+ mactrl_data = 0x08;
+ break;
+ case 180:
+ mactrl_data = 0xC8;
+ break;
+ case 270:
+ mactrl_data = 0xA8;
+ break;
+ case 90:
+ mactrl_data = 0x68;
+ break;
+ }
+
+ /* Colorspcae */
+ if (par->bgr)
+ mactrl_data |= (1 << 2);
+ write_reg(par, CMD_MADCTL, mactrl_data);
+ write_reg(par, CMD_RAMWR); /* Write Data to GRAM mode */
+ return 0;
+}
+
+#ifdef GAMMA_ADJ
+#define CURVE(num, idx) curves[num*par->gamma.num_values + idx]
+static int gamma_adj(struct fbtft_par *par, unsigned long *curves)
+{
+ unsigned long mask[] = {
+ 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
+ 0x1f, 0x3f, 0x0f, 0x0f, 0x7f, 0x1f,
+ 0x3F, 0x3F, 0x3F, 0x3F, 0x3F};
+ int i, j;
+
+ fbtft_par_dbg(DEBUG_INIT_DISPLAY, par, "%s()\n", __func__);
+
+ for (i = 0; i < GAMMA_NUM; i++)
+ for (j = 0; j < GAMMA_LEN; j++)
+ CURVE(i, j) &= mask[i*par->gamma.num_values + j];
+
+ write_reg(par, CMD_PGAMMAC,
+ CURVE(0, 0),
+ CURVE(0, 1),
+ CURVE(0, 2),
+ CURVE(0, 3),
+ CURVE(0, 4),
+ CURVE(0, 5),
+ CURVE(0, 6),
+ (CURVE(0, 7) << 4) | CURVE(0, 8),
+ CURVE(0, 9),
+ CURVE(0, 10),
+ CURVE(0, 11),
+ CURVE(0, 12),
+ CURVE(0, 13),
+ CURVE(0, 14),
+ CURVE(0, 15)
+ );
+
+ write_reg(par, CMD_RAMWR); /* Write Data to GRAM mode */
+
+ return 0;
+}
+#undef CURVE
+#endif
+
+static struct fbtft_display display = {
+ .regwidth = 8,
+ .width = WIDTH,
+ .height = HEIGHT,
+ .bpp = BPP,
+ .fps = FPS,
+#ifdef GAMMA_ADJ
+ .gamma_num = GAMMA_NUM,
+ .gamma_len = GAMMA_LEN,
+ .gamma = DEFAULT_GAMMA,
+#endif
+ .fbtftops = {
+ .init_display = init_display,
+ .set_addr_win = set_addr_win,
+ .set_var = set_var,
+#ifdef GAMMA_ADJ
+ .set_gamma = gamma_adj,
+#endif
+ },
+};
+
+FBTFT_REGISTER_DRIVER(DRVNAME, "ilitek,ili9163", &display);
+
+MODULE_ALIAS("spi:" DRVNAME);
+MODULE_ALIAS("platform:" DRVNAME);
+MODULE_ALIAS("spi:ili9163");
+MODULE_ALIAS("platform:ili9163");
+
+MODULE_DESCRIPTION("FB driver for the ILI9163 LCD Controller");
+MODULE_AUTHOR("Kozhevnikov Anatoly");
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/fbtft/fb_ili9320.c b/drivers/staging/fbtft/fb_ili9320.c
new file mode 100644
index 000000000..3a02edd44
--- /dev/null
+++ b/drivers/staging/fbtft/fb_ili9320.c
@@ -0,0 +1,234 @@
+/*
+ * FB driver for the ILI9320 LCD Controller
+ *
+ * Copyright (C) 2013 Noralf Tronnes
+ *
+ * This program is free software; you can redistribute it and/or 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/kernel.h>
+#include <linux/init.h>
+#include <linux/gpio.h>
+#include <linux/spi/spi.h>
+#include <linux/delay.h>
+
+#include "fbtft.h"
+
+#define DRVNAME "fb_ili9320"
+#define WIDTH 240
+#define HEIGHT 320
+#define DEFAULT_GAMMA "07 07 6 0 0 0 5 5 4 0\n" \
+ "07 08 4 7 5 1 2 0 7 7"
+
+
+static unsigned read_devicecode(struct fbtft_par *par)
+{
+ int ret;
+ u8 rxbuf[8] = {0, };
+
+ write_reg(par, 0x0000);
+ ret = par->fbtftops.read(par, rxbuf, 4);
+ return (rxbuf[2] << 8) | rxbuf[3];
+}
+
+static int init_display(struct fbtft_par *par)
+{
+ unsigned devcode;
+
+ fbtft_par_dbg(DEBUG_INIT_DISPLAY, par, "%s()\n", __func__);
+
+ par->fbtftops.reset(par);
+
+ devcode = read_devicecode(par);
+ fbtft_par_dbg(DEBUG_INIT_DISPLAY, par, "Device code: 0x%04X\n",
+ devcode);
+ if ((devcode != 0x0000) && (devcode != 0x9320))
+ dev_warn(par->info->device,
+ "Unrecognized Device code: 0x%04X (expected 0x9320)\n",
+ devcode);
+
+ /* Initialization sequence from ILI9320 Application Notes */
+
+ /* *********** Start Initial Sequence ********* */
+ write_reg(par, 0x00E5, 0x8000); /* Set the Vcore voltage and this setting is must. */
+ write_reg(par, 0x0000, 0x0001); /* Start internal OSC. */
+ write_reg(par, 0x0001, 0x0100); /* set SS and SM bit */
+ write_reg(par, 0x0002, 0x0700); /* set 1 line inversion */
+ write_reg(par, 0x0004, 0x0000); /* Resize register */
+ write_reg(par, 0x0008, 0x0202); /* set the back and front porch */
+ write_reg(par, 0x0009, 0x0000); /* set non-display area refresh cycle */
+ write_reg(par, 0x000A, 0x0000); /* FMARK function */
+ write_reg(par, 0x000C, 0x0000); /* RGB interface setting */
+ write_reg(par, 0x000D, 0x0000); /* Frame marker Position */
+ write_reg(par, 0x000F, 0x0000); /* RGB interface polarity */
+
+ /* ***********Power On sequence *************** */
+ write_reg(par, 0x0010, 0x0000); /* SAP, BT[3:0], AP, DSTB, SLP, STB */
+ write_reg(par, 0x0011, 0x0007); /* DC1[2:0], DC0[2:0], VC[2:0] */
+ write_reg(par, 0x0012, 0x0000); /* VREG1OUT voltage */
+ write_reg(par, 0x0013, 0x0000); /* VDV[4:0] for VCOM amplitude */
+ mdelay(200); /* Dis-charge capacitor power voltage */
+ write_reg(par, 0x0010, 0x17B0); /* SAP, BT[3:0], AP, DSTB, SLP, STB */
+ write_reg(par, 0x0011, 0x0031); /* R11h=0x0031 at VCI=3.3V DC1[2:0], DC0[2:0], VC[2:0] */
+ mdelay(50);
+ write_reg(par, 0x0012, 0x0138); /* R12h=0x0138 at VCI=3.3V VREG1OUT voltage */
+ mdelay(50);
+ write_reg(par, 0x0013, 0x1800); /* R13h=0x1800 at VCI=3.3V VDV[4:0] for VCOM amplitude */
+ write_reg(par, 0x0029, 0x0008); /* R29h=0x0008 at VCI=3.3V VCM[4:0] for VCOMH */
+ mdelay(50);
+ write_reg(par, 0x0020, 0x0000); /* GRAM horizontal Address */
+ write_reg(par, 0x0021, 0x0000); /* GRAM Vertical Address */
+
+ /* ------------------ Set GRAM area --------------- */
+ write_reg(par, 0x0050, 0x0000); /* Horizontal GRAM Start Address */
+ write_reg(par, 0x0051, 0x00EF); /* Horizontal GRAM End Address */
+ write_reg(par, 0x0052, 0x0000); /* Vertical GRAM Start Address */
+ write_reg(par, 0x0053, 0x013F); /* Vertical GRAM Start Address */
+ write_reg(par, 0x0060, 0x2700); /* Gate Scan Line */
+ write_reg(par, 0x0061, 0x0001); /* NDL,VLE, REV */
+ write_reg(par, 0x006A, 0x0000); /* set scrolling line */
+
+ /* -------------- Partial Display Control --------- */
+ write_reg(par, 0x0080, 0x0000);
+ write_reg(par, 0x0081, 0x0000);
+ write_reg(par, 0x0082, 0x0000);
+ write_reg(par, 0x0083, 0x0000);
+ write_reg(par, 0x0084, 0x0000);
+ write_reg(par, 0x0085, 0x0000);
+
+ /* -------------- Panel Control ------------------- */
+ write_reg(par, 0x0090, 0x0010);
+ write_reg(par, 0x0092, 0x0000);
+ write_reg(par, 0x0093, 0x0003);
+ write_reg(par, 0x0095, 0x0110);
+ write_reg(par, 0x0097, 0x0000);
+ write_reg(par, 0x0098, 0x0000);
+ write_reg(par, 0x0007, 0x0173); /* 262K color and display ON */
+
+ return 0;
+}
+
+static void set_addr_win(struct fbtft_par *par, int xs, int ys, int xe, int ye)
+{
+ fbtft_par_dbg(DEBUG_SET_ADDR_WIN, par,
+ "%s(xs=%d, ys=%d, xe=%d, ye=%d)\n", __func__, xs, ys, xe, ye);
+
+ switch (par->info->var.rotate) {
+ /* R20h = Horizontal GRAM Start Address */
+ /* R21h = Vertical GRAM Start Address */
+ case 0:
+ write_reg(par, 0x0020, xs);
+ write_reg(par, 0x0021, ys);
+ break;
+ case 180:
+ write_reg(par, 0x0020, WIDTH - 1 - xs);
+ write_reg(par, 0x0021, HEIGHT - 1 - ys);
+ break;
+ case 270:
+ write_reg(par, 0x0020, WIDTH - 1 - ys);
+ write_reg(par, 0x0021, xs);
+ break;
+ case 90:
+ write_reg(par, 0x0020, ys);
+ write_reg(par, 0x0021, HEIGHT - 1 - xs);
+ break;
+ }
+ write_reg(par, 0x0022); /* Write Data to GRAM */
+}
+
+static int set_var(struct fbtft_par *par)
+{
+ fbtft_par_dbg(DEBUG_INIT_DISPLAY, par, "%s()\n", __func__);
+
+ switch (par->info->var.rotate) {
+ case 0:
+ write_reg(par, 0x3, (par->bgr << 12) | 0x30);
+ break;
+ case 270:
+ write_reg(par, 0x3, (par->bgr << 12) | 0x28);
+ break;
+ case 180:
+ write_reg(par, 0x3, (par->bgr << 12) | 0x00);
+ break;
+ case 90:
+ write_reg(par, 0x3, (par->bgr << 12) | 0x18);
+ break;
+ }
+ return 0;
+}
+
+/*
+ Gamma string format:
+ VRP0 VRP1 RP0 RP1 KP0 KP1 KP2 KP3 KP4 KP5
+ VRN0 VRN1 RN0 RN1 KN0 KN1 KN2 KN3 KN4 KN5
+*/
+#define CURVE(num, idx) curves[num*par->gamma.num_values + idx]
+static int set_gamma(struct fbtft_par *par, unsigned long *curves)
+{
+ unsigned long mask[] = {
+ 0x1f, 0x1f, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+ 0x1f, 0x1f, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+ };
+ int i, j;
+
+ fbtft_par_dbg(DEBUG_INIT_DISPLAY, par, "%s()\n", __func__);
+
+ /* apply mask */
+ for (i = 0; i < 2; i++)
+ for (j = 0; j < 10; j++)
+ CURVE(i, j) &= mask[i*par->gamma.num_values + j];
+
+ write_reg(par, 0x0030, CURVE(0, 5) << 8 | CURVE(0, 4));
+ write_reg(par, 0x0031, CURVE(0, 7) << 8 | CURVE(0, 6));
+ write_reg(par, 0x0032, CURVE(0, 9) << 8 | CURVE(0, 8));
+ write_reg(par, 0x0035, CURVE(0, 3) << 8 | CURVE(0, 2));
+ write_reg(par, 0x0036, CURVE(0, 1) << 8 | CURVE(0, 0));
+
+ write_reg(par, 0x0037, CURVE(1, 5) << 8 | CURVE(1, 4));
+ write_reg(par, 0x0038, CURVE(1, 7) << 8 | CURVE(1, 6));
+ write_reg(par, 0x0039, CURVE(1, 9) << 8 | CURVE(1, 8));
+ write_reg(par, 0x003C, CURVE(1, 3) << 8 | CURVE(1, 2));
+ write_reg(par, 0x003D, CURVE(1, 1) << 8 | CURVE(1, 0));
+
+ return 0;
+}
+#undef CURVE
+
+
+static struct fbtft_display display = {
+ .regwidth = 16,
+ .width = WIDTH,
+ .height = HEIGHT,
+ .gamma_num = 2,
+ .gamma_len = 10,
+ .gamma = DEFAULT_GAMMA,
+ .fbtftops = {
+ .init_display = init_display,
+ .set_addr_win = set_addr_win,
+ .set_var = set_var,
+ .set_gamma = set_gamma,
+ },
+};
+FBTFT_REGISTER_DRIVER(DRVNAME, "ilitek,ili9320", &display);
+
+MODULE_ALIAS("spi:" DRVNAME);
+MODULE_ALIAS("platform:" DRVNAME);
+MODULE_ALIAS("spi:ili9320");
+MODULE_ALIAS("platform:ili9320");
+
+MODULE_DESCRIPTION("FB driver for the ILI9320 LCD Controller");
+MODULE_AUTHOR("Noralf Tronnes");
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/fbtft/fb_ili9325.c b/drivers/staging/fbtft/fb_ili9325.c
new file mode 100644
index 000000000..19d254e9a
--- /dev/null
+++ b/drivers/staging/fbtft/fb_ili9325.c
@@ -0,0 +1,290 @@
+/*
+ * FB driver for the ILI9325 LCD Controller
+ *
+ * Copyright (C) 2013 Noralf Tronnes
+ *
+ * Based on ili9325.c by Jeroen Domburg
+ *
+ * This program is free software; you can redistribute it and/or 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/kernel.h>
+#include <linux/init.h>
+#include <linux/gpio.h>
+#include <linux/delay.h>
+
+#include "fbtft.h"
+
+#define DRVNAME "fb_ili9325"
+#define WIDTH 240
+#define HEIGHT 320
+#define BPP 16
+#define FPS 20
+#define DEFAULT_GAMMA "0F 00 7 2 0 0 6 5 4 1\n" \
+ "04 16 2 7 6 3 2 1 7 7"
+
+
+static unsigned bt = 6; /* VGL=Vci*4 , VGH=Vci*4 */
+module_param(bt, uint, 0);
+MODULE_PARM_DESC(bt, "Sets the factor used in the step-up circuits");
+
+static unsigned vc = 0x03; /* Vci1=Vci*0.80 */
+module_param(vc, uint, 0);
+MODULE_PARM_DESC(vc,
+"Sets the ratio factor of Vci to generate the reference voltages Vci1");
+
+static unsigned vrh = 0x0d; /* VREG1OUT=Vci*1.85 */
+module_param(vrh, uint, 0);
+MODULE_PARM_DESC(vrh,
+"Set the amplifying rate (1.6 ~ 1.9) of Vci applied to output the VREG1OUT");
+
+static unsigned vdv = 0x12; /* VCOMH amplitude=VREG1OUT*0.98 */
+module_param(vdv, uint, 0);
+MODULE_PARM_DESC(vdv,
+"Select the factor of VREG1OUT to set the amplitude of Vcom");
+
+static unsigned vcm = 0x0a; /* VCOMH=VREG1OUT*0.735 */
+module_param(vcm, uint, 0);
+MODULE_PARM_DESC(vcm, "Set the internal VcomH voltage");
+
+
+/*
+Verify that this configuration is within the Voltage limits
+
+Display module configuration: Vcc = IOVcc = Vci = 3.3V
+
+ Voltages
+----------
+Vci = 3.3
+Vci1 = Vci * 0.80 = 2.64
+DDVDH = Vci1 * 2 = 5.28
+VCL = -Vci1 = -2.64
+VREG1OUT = Vci * 1.85 = 4.88
+VCOMH = VREG1OUT * 0.735 = 3.59
+VCOM amplitude = VREG1OUT * 0.98 = 4.79
+VGH = Vci * 4 = 13.2
+VGL = -Vci * 4 = -13.2
+
+ Limits
+--------
+Power supplies
+1.65 < IOVcc < 3.30 => 1.65 < 3.3 < 3.30
+2.40 < Vcc < 3.30 => 2.40 < 3.3 < 3.30
+2.50 < Vci < 3.30 => 2.50 < 3.3 < 3.30
+
+Source/VCOM power supply voltage
+ 4.50 < DDVDH < 6.0 => 4.50 < 5.28 < 6.0
+-3.0 < VCL < -2.0 => -3.0 < -2.64 < -2.0
+VCI - VCL < 6.0 => 5.94 < 6.0
+
+Gate driver output voltage
+ 10 < VGH < 20 => 10 < 13.2 < 20
+-15 < VGL < -5 => -15 < -13.2 < -5
+VGH - VGL < 32 => 26.4 < 32
+
+VCOM driver output voltage
+VCOMH - VCOML < 6.0 => 4.79 < 6.0
+*/
+
+static int init_display(struct fbtft_par *par)
+{
+ fbtft_par_dbg(DEBUG_INIT_DISPLAY, par, "%s()\n", __func__);
+
+ par->fbtftops.reset(par);
+
+ if (par->gpio.cs != -1)
+ gpio_set_value(par->gpio.cs, 0); /* Activate chip */
+
+ bt &= 0x07;
+ vc &= 0x07;
+ vrh &= 0x0f;
+ vdv &= 0x1f;
+ vcm &= 0x3f;
+
+ /* Initialization sequence from ILI9325 Application Notes */
+
+ /* ----------- Start Initial Sequence ----------- */
+ write_reg(par, 0x00E3, 0x3008); /* Set internal timing */
+ write_reg(par, 0x00E7, 0x0012); /* Set internal timing */
+ write_reg(par, 0x00EF, 0x1231); /* Set internal timing */
+ write_reg(par, 0x0001, 0x0100); /* set SS and SM bit */
+ write_reg(par, 0x0002, 0x0700); /* set 1 line inversion */
+ write_reg(par, 0x0004, 0x0000); /* Resize register */
+ write_reg(par, 0x0008, 0x0207); /* set the back porch and front porch */
+ write_reg(par, 0x0009, 0x0000); /* set non-display area refresh cycle */
+ write_reg(par, 0x000A, 0x0000); /* FMARK function */
+ write_reg(par, 0x000C, 0x0000); /* RGB interface setting */
+ write_reg(par, 0x000D, 0x0000); /* Frame marker Position */
+ write_reg(par, 0x000F, 0x0000); /* RGB interface polarity */
+
+ /* ----------- Power On sequence ----------- */
+ write_reg(par, 0x0010, 0x0000); /* SAP, BT[3:0], AP, DSTB, SLP, STB */
+ write_reg(par, 0x0011, 0x0007); /* DC1[2:0], DC0[2:0], VC[2:0] */
+ write_reg(par, 0x0012, 0x0000); /* VREG1OUT voltage */
+ write_reg(par, 0x0013, 0x0000); /* VDV[4:0] for VCOM amplitude */
+ mdelay(200); /* Dis-charge capacitor power voltage */
+ write_reg(par, 0x0010, /* SAP, BT[3:0], AP, DSTB, SLP, STB */
+ (1 << 12) | (bt << 8) | (1 << 7) | (0x01 << 4));
+ write_reg(par, 0x0011, 0x220 | vc); /* DC1[2:0], DC0[2:0], VC[2:0] */
+ mdelay(50); /* Delay 50ms */
+ write_reg(par, 0x0012, vrh); /* Internal reference voltage= Vci; */
+ mdelay(50); /* Delay 50ms */
+ write_reg(par, 0x0013, vdv << 8); /* Set VDV[4:0] for VCOM amplitude */
+ write_reg(par, 0x0029, vcm); /* Set VCM[5:0] for VCOMH */
+ write_reg(par, 0x002B, 0x000C); /* Set Frame Rate */
+ mdelay(50); /* Delay 50ms */
+ write_reg(par, 0x0020, 0x0000); /* GRAM horizontal Address */
+ write_reg(par, 0x0021, 0x0000); /* GRAM Vertical Address */
+
+ /*------------------ Set GRAM area --------------- */
+ write_reg(par, 0x0050, 0x0000); /* Horizontal GRAM Start Address */
+ write_reg(par, 0x0051, 0x00EF); /* Horizontal GRAM End Address */
+ write_reg(par, 0x0052, 0x0000); /* Vertical GRAM Start Address */
+ write_reg(par, 0x0053, 0x013F); /* Vertical GRAM Start Address */
+ write_reg(par, 0x0060, 0xA700); /* Gate Scan Line */
+ write_reg(par, 0x0061, 0x0001); /* NDL,VLE, REV */
+ write_reg(par, 0x006A, 0x0000); /* set scrolling line */
+
+ /*-------------- Partial Display Control --------- */
+ write_reg(par, 0x0080, 0x0000);
+ write_reg(par, 0x0081, 0x0000);
+ write_reg(par, 0x0082, 0x0000);
+ write_reg(par, 0x0083, 0x0000);
+ write_reg(par, 0x0084, 0x0000);
+ write_reg(par, 0x0085, 0x0000);
+
+ /*-------------- Panel Control ------------------- */
+ write_reg(par, 0x0090, 0x0010);
+ write_reg(par, 0x0092, 0x0600);
+ write_reg(par, 0x0007, 0x0133); /* 262K color and display ON */
+
+ return 0;
+}
+
+static void set_addr_win(struct fbtft_par *par, int xs, int ys, int xe, int ye)
+{
+ fbtft_par_dbg(DEBUG_SET_ADDR_WIN, par,
+ "%s(xs=%d, ys=%d, xe=%d, ye=%d)\n", __func__, xs, ys, xe, ye);
+ switch (par->info->var.rotate) {
+ /* R20h = Horizontal GRAM Start Address */
+ /* R21h = Vertical GRAM Start Address */
+ case 0:
+ write_reg(par, 0x0020, xs);
+ write_reg(par, 0x0021, ys);
+ break;
+ case 180:
+ write_reg(par, 0x0020, WIDTH - 1 - xs);
+ write_reg(par, 0x0021, HEIGHT - 1 - ys);
+ break;
+ case 270:
+ write_reg(par, 0x0020, WIDTH - 1 - ys);
+ write_reg(par, 0x0021, xs);
+ break;
+ case 90:
+ write_reg(par, 0x0020, ys);
+ write_reg(par, 0x0021, HEIGHT - 1 - xs);
+ break;
+ }
+ write_reg(par, 0x0022); /* Write Data to GRAM */
+}
+
+static int set_var(struct fbtft_par *par)
+{
+ fbtft_par_dbg(DEBUG_INIT_DISPLAY, par, "%s()\n", __func__);
+
+ switch (par->info->var.rotate) {
+ /* AM: GRAM update direction */
+ case 0:
+ write_reg(par, 0x03, 0x0030 | (par->bgr << 12));
+ break;
+ case 180:
+ write_reg(par, 0x03, 0x0000 | (par->bgr << 12));
+ break;
+ case 270:
+ write_reg(par, 0x03, 0x0028 | (par->bgr << 12));
+ break;
+ case 90:
+ write_reg(par, 0x03, 0x0018 | (par->bgr << 12));
+ break;
+ }
+
+ return 0;
+}
+
+/*
+ Gamma string format:
+ VRP0 VRP1 RP0 RP1 KP0 KP1 KP2 KP3 KP4 KP5
+ VRN0 VRN1 RN0 RN1 KN0 KN1 KN2 KN3 KN4 KN5
+*/
+#define CURVE(num, idx) curves[num*par->gamma.num_values + idx]
+static int set_gamma(struct fbtft_par *par, unsigned long *curves)
+{
+ unsigned long mask[] = {
+ 0x1f, 0x1f, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+ 0x1f, 0x1f, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+ };
+ int i, j;
+
+ fbtft_par_dbg(DEBUG_INIT_DISPLAY, par, "%s()\n", __func__);
+
+ /* apply mask */
+ for (i = 0; i < 2; i++)
+ for (j = 0; j < 10; j++)
+ CURVE(i, j) &= mask[i*par->gamma.num_values + j];
+
+ write_reg(par, 0x0030, CURVE(0, 5) << 8 | CURVE(0, 4));
+ write_reg(par, 0x0031, CURVE(0, 7) << 8 | CURVE(0, 6));
+ write_reg(par, 0x0032, CURVE(0, 9) << 8 | CURVE(0, 8));
+ write_reg(par, 0x0035, CURVE(0, 3) << 8 | CURVE(0, 2));
+ write_reg(par, 0x0036, CURVE(0, 1) << 8 | CURVE(0, 0));
+
+ write_reg(par, 0x0037, CURVE(1, 5) << 8 | CURVE(1, 4));
+ write_reg(par, 0x0038, CURVE(1, 7) << 8 | CURVE(1, 6));
+ write_reg(par, 0x0039, CURVE(1, 9) << 8 | CURVE(1, 8));
+ write_reg(par, 0x003C, CURVE(1, 3) << 8 | CURVE(1, 2));
+ write_reg(par, 0x003D, CURVE(1, 1) << 8 | CURVE(1, 0));
+
+ return 0;
+}
+#undef CURVE
+
+
+static struct fbtft_display display = {
+ .regwidth = 16,
+ .width = WIDTH,
+ .height = HEIGHT,
+ .bpp = BPP,
+ .fps = FPS,
+ .gamma_num = 2,
+ .gamma_len = 10,
+ .gamma = DEFAULT_GAMMA,
+ .fbtftops = {
+ .init_display = init_display,
+ .set_addr_win = set_addr_win,
+ .set_var = set_var,
+ .set_gamma = set_gamma,
+ },
+};
+FBTFT_REGISTER_DRIVER(DRVNAME, "ilitek,ili9325", &display);
+
+MODULE_ALIAS("spi:" DRVNAME);
+MODULE_ALIAS("platform:" DRVNAME);
+MODULE_ALIAS("spi:ili9325");
+MODULE_ALIAS("platform:ili9325");
+
+MODULE_DESCRIPTION("FB driver for the ILI9325 LCD Controller");
+MODULE_AUTHOR("Noralf Tronnes");
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/fbtft/fb_ili9340.c b/drivers/staging/fbtft/fb_ili9340.c
new file mode 100644
index 000000000..0f4a42f89
--- /dev/null
+++ b/drivers/staging/fbtft/fb_ili9340.c
@@ -0,0 +1,163 @@
+/*
+ * FB driver for the ILI9340 LCD Controller
+ *
+ * Copyright (C) 2013 Noralf Tronnes
+ *
+ * This program is free software; you can redistribute it and/or 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/kernel.h>
+#include <linux/init.h>
+#include <linux/gpio.h>
+#include <linux/delay.h>
+
+#include "fbtft.h"
+
+#define DRVNAME "fb_ili9340"
+#define WIDTH 240
+#define HEIGHT 320
+
+
+/* Init sequence taken from: Arduino Library for the Adafruit 2.2" display */
+static int init_display(struct fbtft_par *par)
+{
+ fbtft_par_dbg(DEBUG_INIT_DISPLAY, par, "%s()\n", __func__);
+
+ par->fbtftops.reset(par);
+
+ write_reg(par, 0xEF, 0x03, 0x80, 0x02);
+ write_reg(par, 0xCF, 0x00, 0XC1, 0X30);
+ write_reg(par, 0xED, 0x64, 0x03, 0X12, 0X81);
+ write_reg(par, 0xE8, 0x85, 0x00, 0x78);
+ write_reg(par, 0xCB, 0x39, 0x2C, 0x00, 0x34, 0x02);
+ write_reg(par, 0xF7, 0x20);
+ write_reg(par, 0xEA, 0x00, 0x00);
+
+ /* Power Control 1 */
+ write_reg(par, 0xC0, 0x23);
+
+ /* Power Control 2 */
+ write_reg(par, 0xC1, 0x10);
+
+ /* VCOM Control 1 */
+ write_reg(par, 0xC5, 0x3e, 0x28);
+
+ /* VCOM Control 2 */
+ write_reg(par, 0xC7, 0x86);
+
+ /* COLMOD: Pixel Format Set */
+ /* 16 bits/pixel */
+ write_reg(par, 0x3A, 0x55);
+
+ /* Frame Rate Control */
+ /* Division ratio = fosc, Frame Rate = 79Hz */
+ write_reg(par, 0xB1, 0x00, 0x18);
+
+ /* Display Function Control */
+ write_reg(par, 0xB6, 0x08, 0x82, 0x27);
+
+ /* Gamma Function Disable */
+ write_reg(par, 0xF2, 0x00);
+
+ /* Gamma curve selected */
+ write_reg(par, 0x26, 0x01);
+
+ /* Positive Gamma Correction */
+ write_reg(par, 0xE0,
+ 0x0F, 0x31, 0x2B, 0x0C, 0x0E, 0x08, 0x4E, 0xF1,
+ 0x37, 0x07, 0x10, 0x03, 0x0E, 0x09, 0x00);
+
+ /* Negative Gamma Correction */
+ write_reg(par, 0xE1,
+ 0x00, 0x0E, 0x14, 0x03, 0x11, 0x07, 0x31, 0xC1,
+ 0x48, 0x08, 0x0F, 0x0C, 0x31, 0x36, 0x0F);
+
+ /* Sleep OUT */
+ write_reg(par, 0x11);
+
+ mdelay(120);
+
+ /* Display ON */
+ write_reg(par, 0x29);
+
+ return 0;
+}
+
+static void set_addr_win(struct fbtft_par *par, int xs, int ys, int xe, int ye)
+{
+ fbtft_par_dbg(DEBUG_SET_ADDR_WIN, par,
+ "%s(xs=%d, ys=%d, xe=%d, ye=%d)\n", __func__, xs, ys, xe, ye);
+
+ /* Column address */
+ write_reg(par, 0x2A, xs >> 8, xs & 0xFF, xe >> 8, xe & 0xFF);
+
+ /* Row address */
+ write_reg(par, 0x2B, ys >> 8, ys & 0xFF, ye >> 8, ye & 0xFF);
+
+ /* Memory write */
+ write_reg(par, 0x2C);
+}
+
+#define ILI9340_MADCTL_MV 0x20
+#define ILI9340_MADCTL_MX 0x40
+#define ILI9340_MADCTL_MY 0x80
+static int set_var(struct fbtft_par *par)
+{
+ u8 val;
+
+ fbtft_par_dbg(DEBUG_INIT_DISPLAY, par, "%s()\n", __func__);
+
+ switch (par->info->var.rotate) {
+ case 270:
+ val = ILI9340_MADCTL_MV;
+ break;
+ case 180:
+ val = ILI9340_MADCTL_MY;
+ break;
+ case 90:
+ val = ILI9340_MADCTL_MV | ILI9340_MADCTL_MY | ILI9340_MADCTL_MX;
+ break;
+ default:
+ val = ILI9340_MADCTL_MX;
+ break;
+ }
+ /* Memory Access Control */
+ write_reg(par, 0x36, val | (par->bgr << 3));
+
+ return 0;
+}
+
+
+static struct fbtft_display display = {
+ .regwidth = 8,
+ .width = WIDTH,
+ .height = HEIGHT,
+ .fbtftops = {
+ .init_display = init_display,
+ .set_addr_win = set_addr_win,
+ .set_var = set_var,
+ },
+};
+FBTFT_REGISTER_DRIVER(DRVNAME, "ilitek,ili9340", &display);
+
+MODULE_ALIAS("spi:" DRVNAME);
+MODULE_ALIAS("platform:" DRVNAME);
+MODULE_ALIAS("spi:ili9340");
+MODULE_ALIAS("platform:ili9340");
+
+MODULE_DESCRIPTION("FB driver for the ILI9340 LCD Controller");
+MODULE_AUTHOR("Noralf Tronnes");
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/fbtft/fb_ili9341.c b/drivers/staging/fbtft/fb_ili9341.c
new file mode 100644
index 000000000..709492e56
--- /dev/null
+++ b/drivers/staging/fbtft/fb_ili9341.c
@@ -0,0 +1,179 @@
+/*
+ * FB driver for the ILI9341 LCD display controller
+ *
+ * This display uses 9-bit SPI: Data/Command bit + 8 data bits
+ * For platforms that doesn't support 9-bit, the driver is capable
+ * of emulating this using 8-bit transfer.
+ * This is done by transferring eight 9-bit words in 9 bytes.
+ *
+ * Copyright (C) 2013 Christian Vogelgsang
+ * Based on adafruit22fb.c by Noralf Tronnes
+ *
+ * This program is free software; you can redistribute it and/or 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/kernel.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+
+#include "fbtft.h"
+
+#define DRVNAME "fb_ili9341"
+#define WIDTH 240
+#define HEIGHT 320
+#define TXBUFLEN (4 * PAGE_SIZE)
+#define DEFAULT_GAMMA "1F 1A 18 0A 0F 06 45 87 32 0A 07 02 07 05 00\n" \
+ "00 25 27 05 10 09 3A 78 4D 05 18 0D 38 3A 1F"
+
+
+static int init_display(struct fbtft_par *par)
+{
+ fbtft_par_dbg(DEBUG_INIT_DISPLAY, par, "%s()\n", __func__);
+
+ par->fbtftops.reset(par);
+
+ /* startup sequence for MI0283QT-9A */
+ write_reg(par, 0x01); /* software reset */
+ mdelay(5);
+ write_reg(par, 0x28); /* display off */
+ /* --------------------------------------------------------- */
+ write_reg(par, 0xCF, 0x00, 0x83, 0x30);
+ write_reg(par, 0xED, 0x64, 0x03, 0x12, 0x81);
+ write_reg(par, 0xE8, 0x85, 0x01, 0x79);
+ write_reg(par, 0xCB, 0x39, 0X2C, 0x00, 0x34, 0x02);
+ write_reg(par, 0xF7, 0x20);
+ write_reg(par, 0xEA, 0x00, 0x00);
+ /* ------------power control-------------------------------- */
+ write_reg(par, 0xC0, 0x26);
+ write_reg(par, 0xC1, 0x11);
+ /* ------------VCOM --------- */
+ write_reg(par, 0xC5, 0x35, 0x3E);
+ write_reg(par, 0xC7, 0xBE);
+ /* ------------memory access control------------------------ */
+ write_reg(par, 0x3A, 0x55); /* 16bit pixel */
+ /* ------------frame rate----------------------------------- */
+ write_reg(par, 0xB1, 0x00, 0x1B);
+ /* ------------Gamma---------------------------------------- */
+ /* write_reg(par, 0xF2, 0x08); */ /* Gamma Function Disable */
+ write_reg(par, 0x26, 0x01);
+ /* ------------display-------------------------------------- */
+ write_reg(par, 0xB7, 0x07); /* entry mode set */
+ write_reg(par, 0xB6, 0x0A, 0x82, 0x27, 0x00);
+ write_reg(par, 0x11); /* sleep out */
+ mdelay(100);
+ write_reg(par, 0x29); /* display on */
+ mdelay(20);
+
+ return 0;
+}
+
+static void set_addr_win(struct fbtft_par *par, int xs, int ys, int xe, int ye)
+{
+ fbtft_par_dbg(DEBUG_SET_ADDR_WIN, par,
+ "%s(xs=%d, ys=%d, xe=%d, ye=%d)\n", __func__, xs, ys, xe, ye);
+
+ /* Column address set */
+ write_reg(par, 0x2A,
+ (xs >> 8) & 0xFF, xs & 0xFF, (xe >> 8) & 0xFF, xe & 0xFF);
+
+ /* Row address set */
+ write_reg(par, 0x2B,
+ (ys >> 8) & 0xFF, ys & 0xFF, (ye >> 8) & 0xFF, ye & 0xFF);
+
+ /* Memory write */
+ write_reg(par, 0x2C);
+}
+
+#define MEM_Y (7) /* MY row address order */
+#define MEM_X (6) /* MX column address order */
+#define MEM_V (5) /* MV row / column exchange */
+#define MEM_L (4) /* ML vertical refresh order */
+#define MEM_H (2) /* MH horizontal refresh order */
+#define MEM_BGR (3) /* RGB-BGR Order */
+static int set_var(struct fbtft_par *par)
+{
+ fbtft_par_dbg(DEBUG_INIT_DISPLAY, par, "%s()\n", __func__);
+
+ switch (par->info->var.rotate) {
+ case 0:
+ write_reg(par, 0x36, (1 << MEM_X) | (par->bgr << MEM_BGR));
+ break;
+ case 270:
+ write_reg(par, 0x36,
+ (1<<MEM_V) | (1 << MEM_L) | (par->bgr << MEM_BGR));
+ break;
+ case 180:
+ write_reg(par, 0x36, (1 << MEM_Y) | (par->bgr << MEM_BGR));
+ break;
+ case 90:
+ write_reg(par, 0x36, (1 << MEM_Y) | (1 << MEM_X) |
+ (1 << MEM_V) | (par->bgr << MEM_BGR));
+ break;
+ }
+
+ return 0;
+}
+
+/*
+ Gamma string format:
+ Positive: Par1 Par2 [...] Par15
+ Negative: Par1 Par2 [...] Par15
+*/
+#define CURVE(num, idx) curves[num*par->gamma.num_values + idx]
+static int set_gamma(struct fbtft_par *par, unsigned long *curves)
+{
+ int i;
+
+ fbtft_par_dbg(DEBUG_INIT_DISPLAY, par, "%s()\n", __func__);
+
+ for (i = 0; i < par->gamma.num_curves; i++)
+ write_reg(par, 0xE0 + i,
+ CURVE(i, 0), CURVE(i, 1), CURVE(i, 2),
+ CURVE(i, 3), CURVE(i, 4), CURVE(i, 5),
+ CURVE(i, 6), CURVE(i, 7), CURVE(i, 8),
+ CURVE(i, 9), CURVE(i, 10), CURVE(i, 11),
+ CURVE(i, 12), CURVE(i, 13), CURVE(i, 14));
+
+ return 0;
+}
+#undef CURVE
+
+
+static struct fbtft_display display = {
+ .regwidth = 8,
+ .width = WIDTH,
+ .height = HEIGHT,
+ .txbuflen = TXBUFLEN,
+ .gamma_num = 2,
+ .gamma_len = 15,
+ .gamma = DEFAULT_GAMMA,
+ .fbtftops = {
+ .init_display = init_display,
+ .set_addr_win = set_addr_win,
+ .set_var = set_var,
+ .set_gamma = set_gamma,
+ },
+};
+FBTFT_REGISTER_DRIVER(DRVNAME, "ilitek,ili9341", &display);
+
+MODULE_ALIAS("spi:" DRVNAME);
+MODULE_ALIAS("platform:" DRVNAME);
+MODULE_ALIAS("spi:ili9341");
+MODULE_ALIAS("platform:ili9341");
+
+MODULE_DESCRIPTION("FB driver for the ILI9341 LCD display controller");
+MODULE_AUTHOR("Christian Vogelgsang");
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/fbtft/fb_ili9481.c b/drivers/staging/fbtft/fb_ili9481.c
new file mode 100644
index 000000000..8bae09c2d
--- /dev/null
+++ b/drivers/staging/fbtft/fb_ili9481.c
@@ -0,0 +1,117 @@
+/*
+ * FB driver for the ILI9481 LCD Controller
+ *
+ * Copyright (c) 2014 Petr Olivka
+ * Copyright (c) 2013 Noralf Tronnes
+ *
+ * This program is free software; you can redistribute it and/or 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/kernel.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+
+#include "fbtft.h"
+
+#define DRVNAME "fb_ili9481"
+#define WIDTH 320
+#define HEIGHT 480
+
+static int default_init_sequence[] = {
+
+ /* SLP_OUT - Sleep out */
+ -1, 0x11,
+ -2, 50,
+ /* Power setting */
+ -1, 0xD0, 0x07, 0x42, 0x18,
+ /* VCOM */
+ -1, 0xD1, 0x00, 0x07, 0x10,
+ /* Power setting for norm. mode */
+ -1, 0xD2, 0x01, 0x02,
+ /* Panel driving setting */
+ -1, 0xC0, 0x10, 0x3B, 0x00, 0x02, 0x11,
+ /* Frame rate & inv. */
+ -1, 0xC5, 0x03,
+ /* Pixel format */
+ -1, 0x3A, 0x55,
+ /* Gamma */
+ -1, 0xC8, 0x00, 0x32, 0x36, 0x45, 0x06, 0x16,
+ 0x37, 0x75, 0x77, 0x54, 0x0C, 0x00,
+ /* DISP_ON */
+ -1, 0x29,
+ -3
+};
+
+static void set_addr_win(struct fbtft_par *par, int xs, int ys, int xe, int ye)
+{
+ fbtft_par_dbg(DEBUG_SET_ADDR_WIN, par,
+ "%s(xs=%d, ys=%d, xe=%d, ye=%d)\n", __func__, xs, ys, xe, ye);
+
+ /* column address */
+ write_reg(par, 0x2a, xs >> 8, xs & 0xff, xe >> 8, xe & 0xff);
+
+ /* Row address */
+ write_reg(par, 0x2b, ys >> 8, ys & 0xff, ye >> 8, ye & 0xff);
+
+ /* memory write */
+ write_reg(par, 0x2c);
+}
+
+#define HFLIP 0x01
+#define VFLIP 0x02
+#define ROWxCOL 0x20
+static int set_var(struct fbtft_par *par)
+{
+ fbtft_par_dbg(DEBUG_INIT_DISPLAY, par, "%s()\n", __func__);
+
+ switch (par->info->var.rotate) {
+ case 270:
+ write_reg(par, 0x36, ROWxCOL | HFLIP | VFLIP | (par->bgr << 3));
+ break;
+ case 180:
+ write_reg(par, 0x36, VFLIP | (par->bgr << 3));
+ break;
+ case 90:
+ write_reg(par, 0x36, ROWxCOL | (par->bgr << 3));
+ break;
+ default:
+ write_reg(par, 0x36, HFLIP | (par->bgr << 3));
+ break;
+ }
+
+ return 0;
+}
+
+static struct fbtft_display display = {
+ .regwidth = 8,
+ .width = WIDTH,
+ .height = HEIGHT,
+ .init_sequence = default_init_sequence,
+ .fbtftops = {
+ .set_addr_win = set_addr_win,
+ .set_var = set_var,
+ },
+};
+FBTFT_REGISTER_DRIVER(DRVNAME, "ilitek,ili9481", &display);
+
+MODULE_ALIAS("spi:" DRVNAME);
+MODULE_ALIAS("platform:" DRVNAME);
+MODULE_ALIAS("spi:ili9481");
+MODULE_ALIAS("platform:ili9481");
+
+MODULE_DESCRIPTION("FB driver for the ILI9481 LCD Controller");
+MODULE_AUTHOR("Petr Olivka");
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/fbtft/fb_ili9486.c b/drivers/staging/fbtft/fb_ili9486.c
new file mode 100644
index 000000000..dd4ddca38
--- /dev/null
+++ b/drivers/staging/fbtft/fb_ili9486.c
@@ -0,0 +1,121 @@
+/*
+ * FB driver for the ILI9486 LCD Controller
+ *
+ * Copyright (C) 2014 Noralf Tronnes
+ *
+ * This program is free software; you can redistribute it and/or 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/kernel.h>
+#include <linux/init.h>
+
+#include "fbtft.h"
+
+#define DRVNAME "fb_ili9486"
+#define WIDTH 320
+#define HEIGHT 480
+
+
+/* this init sequence matches PiScreen */
+static int default_init_sequence[] = {
+ /* Interface Mode Control */
+ -1, 0xb0, 0x0,
+ /* Sleep OUT */
+ -1, 0x11,
+ -2, 250,
+ /* Interface Pixel Format */
+ -1, 0x3A, 0x55,
+ /* Power Control 3 */
+ -1, 0xC2, 0x44,
+ /* VCOM Control 1 */
+ -1, 0xC5, 0x00, 0x00, 0x00, 0x00,
+ /* PGAMCTRL(Positive Gamma Control) */
+ -1, 0xE0, 0x0F, 0x1F, 0x1C, 0x0C, 0x0F, 0x08, 0x48, 0x98,
+ 0x37, 0x0A, 0x13, 0x04, 0x11, 0x0D, 0x00,
+ /* NGAMCTRL(Negative Gamma Control) */
+ -1, 0xE1, 0x0F, 0x32, 0x2E, 0x0B, 0x0D, 0x05, 0x47, 0x75,
+ 0x37, 0x06, 0x10, 0x03, 0x24, 0x20, 0x00,
+ /* Digital Gamma Control 1 */
+ -1, 0xE2, 0x0F, 0x32, 0x2E, 0x0B, 0x0D, 0x05, 0x47, 0x75,
+ 0x37, 0x06, 0x10, 0x03, 0x24, 0x20, 0x00,
+ /* Sleep OUT */
+ -1, 0x11,
+ /* Display ON */
+ -1, 0x29,
+ /* end marker */
+ -3
+};
+
+static void set_addr_win(struct fbtft_par *par, int xs, int ys, int xe, int ye)
+{
+ fbtft_par_dbg(DEBUG_SET_ADDR_WIN, par,
+ "%s(xs=%d, ys=%d, xe=%d, ye=%d)\n", __func__, xs, ys, xe, ye);
+
+ /* Column address */
+ write_reg(par, 0x2A, xs >> 8, xs & 0xFF, xe >> 8, xe & 0xFF);
+
+ /* Row address */
+ write_reg(par, 0x2B, ys >> 8, ys & 0xFF, ye >> 8, ye & 0xFF);
+
+ /* Memory write */
+ write_reg(par, 0x2C);
+}
+
+static int set_var(struct fbtft_par *par)
+{
+ fbtft_par_dbg(DEBUG_INIT_DISPLAY, par, "%s()\n", __func__);
+
+ switch (par->info->var.rotate) {
+ case 0:
+ write_reg(par, 0x36, 0x80 | (par->bgr << 3));
+ break;
+ case 90:
+ write_reg(par, 0x36, 0x20 | (par->bgr << 3));
+ break;
+ case 180:
+ write_reg(par, 0x36, 0x40 | (par->bgr << 3));
+ break;
+ case 270:
+ write_reg(par, 0x36, 0xE0 | (par->bgr << 3));
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+
+static struct fbtft_display display = {
+ .regwidth = 8,
+ .width = WIDTH,
+ .height = HEIGHT,
+ .init_sequence = default_init_sequence,
+ .fbtftops = {
+ .set_addr_win = set_addr_win,
+ .set_var = set_var,
+ },
+};
+FBTFT_REGISTER_DRIVER(DRVNAME, "ilitek,ili9486", &display);
+
+MODULE_ALIAS("spi:" DRVNAME);
+MODULE_ALIAS("platform:" DRVNAME);
+MODULE_ALIAS("spi:ili9486");
+MODULE_ALIAS("platform:ili9486");
+
+MODULE_DESCRIPTION("FB driver for the ILI9486 LCD Controller");
+MODULE_AUTHOR("Noralf Tronnes");
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/fbtft/fb_pcd8544.c b/drivers/staging/fbtft/fb_pcd8544.c
new file mode 100644
index 000000000..15da0ec77
--- /dev/null
+++ b/drivers/staging/fbtft/fb_pcd8544.c
@@ -0,0 +1,189 @@
+/*
+ * FB driver for the PCD8544 LCD Controller
+ *
+ * The display is monochrome and the video memory is RGB565.
+ * Any pixel value except 0 turns the pixel on.
+ *
+ * Copyright (C) 2013 Noralf Tronnes
+ *
+ * This program is free software; you can redistribute it and/or 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/kernel.h>
+#include <linux/init.h>
+#include <linux/gpio.h>
+#include <linux/spi/spi.h>
+#include <linux/delay.h>
+
+#include "fbtft.h"
+
+#define DRVNAME "fb_pcd8544"
+#define WIDTH 84
+#define HEIGHT 48
+#define TXBUFLEN (84*6)
+#define DEFAULT_GAMMA "40" /* gamma controls the contrast in this driver */
+
+static unsigned tc;
+module_param(tc, uint, 0);
+MODULE_PARM_DESC(tc, "TC[1:0] Temperature coefficient: 0-3 (default: 0)");
+
+static unsigned bs = 4;
+module_param(bs, uint, 0);
+MODULE_PARM_DESC(bs, "BS[2:0] Bias voltage level: 0-7 (default: 4)");
+
+
+static int init_display(struct fbtft_par *par)
+{
+ fbtft_par_dbg(DEBUG_INIT_DISPLAY, par, "%s()\n", __func__);
+
+ par->fbtftops.reset(par);
+
+ /* Function set
+ *
+ * 5:1 1
+ * 2:0 PD - Powerdown control: chip is active
+ * 1:0 V - Entry mode: horizontal addressing
+ * 0:1 H - Extended instruction set control: extended
+ */
+ write_reg(par, 0x21);
+
+ /* H=1 Temperature control
+ *
+ * 2:1 1
+ * 1:x TC1 - Temperature Coefficient: 0x10
+ * 0:x TC0
+ */
+ write_reg(par, 0x04 | (tc & 0x3));
+
+ /* H=1 Bias system
+ *
+ * 4:1 1
+ * 3:0 0
+ * 2:x BS2 - Bias System
+ * 1:x BS1
+ * 0:x BS0
+ */
+ write_reg(par, 0x10 | (bs & 0x7));
+
+ /* Function set
+ *
+ * 5:1 1
+ * 2:0 PD - Powerdown control: chip is active
+ * 1:1 V - Entry mode: vertical addressing
+ * 0:0 H - Extended instruction set control: basic
+ */
+ write_reg(par, 0x22);
+
+ /* H=0 Display control
+ *
+ * 3:1 1
+ * 2:1 D - DE: 10=normal mode
+ * 1:0 0
+ * 0:0 E
+ */
+ write_reg(par, 0x08 | 4);
+
+ return 0;
+}
+
+static void set_addr_win(struct fbtft_par *par, int xs, int ys, int xe, int ye)
+{
+ fbtft_par_dbg(DEBUG_SET_ADDR_WIN, par, "%s(xs=%d, ys=%d, xe=%d, ye=%d)\n",
+ __func__, xs, ys, xe, ye);
+
+ /* H=0 Set X address of RAM
+ *
+ * 7:1 1
+ * 6-0: X[6:0] - 0x00
+ */
+ write_reg(par, 0x80);
+
+ /* H=0 Set Y address of RAM
+ *
+ * 7:0 0
+ * 6:1 1
+ * 2-0: Y[2:0] - 0x0
+ */
+ write_reg(par, 0x40);
+}
+
+static int write_vmem(struct fbtft_par *par, size_t offset, size_t len)
+{
+ u16 *vmem16 = (u16 *)par->info->screen_base;
+ u8 *buf = par->txbuf.buf;
+ int x, y, i;
+ int ret = 0;
+
+ fbtft_par_dbg(DEBUG_WRITE_VMEM, par, "%s()\n", __func__);
+
+ for (x = 0; x < 84; x++) {
+ for (y = 0; y < 6; y++) {
+ *buf = 0x00;
+ for (i = 0; i < 8; i++)
+ *buf |= (vmem16[(y*8+i)*84+x] ? 1 : 0) << i;
+ buf++;
+ }
+ }
+
+ /* Write data */
+ gpio_set_value(par->gpio.dc, 1);
+ ret = par->fbtftops.write(par, par->txbuf.buf, 6*84);
+ if (ret < 0)
+ dev_err(par->info->device, "write failed and returned: %d\n",
+ ret);
+
+ return ret;
+}
+
+static int set_gamma(struct fbtft_par *par, unsigned long *curves)
+{
+ fbtft_par_dbg(DEBUG_INIT_DISPLAY, par, "%s()\n", __func__);
+
+ /* apply mask */
+ curves[0] &= 0x7F;
+
+ write_reg(par, 0x23); /* turn on extended instruction set */
+ write_reg(par, 0x80 | curves[0]);
+ write_reg(par, 0x22); /* turn off extended instruction set */
+
+ return 0;
+}
+
+
+static struct fbtft_display display = {
+ .regwidth = 8,
+ .width = WIDTH,
+ .height = HEIGHT,
+ .txbuflen = TXBUFLEN,
+ .gamma_num = 1,
+ .gamma_len = 1,
+ .gamma = DEFAULT_GAMMA,
+ .fbtftops = {
+ .init_display = init_display,
+ .set_addr_win = set_addr_win,
+ .write_vmem = write_vmem,
+ .set_gamma = set_gamma,
+ },
+ .backlight = 1,
+};
+FBTFT_REGISTER_DRIVER(DRVNAME, "philips,pdc8544", &display);
+
+MODULE_ALIAS("spi:" DRVNAME);
+MODULE_ALIAS("spi:pdc8544");
+
+MODULE_DESCRIPTION("FB driver for the PCD8544 LCD Controller");
+MODULE_AUTHOR("Noralf Tronnes");
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/fbtft/fb_ra8875.c b/drivers/staging/fbtft/fb_ra8875.c
new file mode 100644
index 000000000..2c4d4dc70
--- /dev/null
+++ b/drivers/staging/fbtft/fb_ra8875.c
@@ -0,0 +1,331 @@
+/******************************************************************************
+
+ ProjectName: FBTFT driver ***** *****
+ for the RA8875 LCD Controller * * ************
+ * ** ** * *
+ Copyright © by Pf@nne & NOTRO * * * * * **** *
+ * * * * * * *
+ Last modification by: * * * * **** *
+ - Pf@nne (pf@nne-mail.de) * * ***** *
+ * * * *******
+ ***** * *
+ Date : 10.06.2014 * *
+ Version : V1.13 *****
+ Revision : 5
+
+*******************************************************************************
+ * This program is free software; you can redistribute it and/or 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/kernel.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+
+#include <linux/gpio.h>
+#include "fbtft.h"
+
+#define DRVNAME "fb_ra8875"
+
+static int write_spi(struct fbtft_par *par, void *buf, size_t len)
+{
+ struct spi_transfer t = {
+ .tx_buf = buf,
+ .len = len,
+ .speed_hz = 1000000,
+ };
+ struct spi_message m;
+
+ fbtft_par_dbg_hex(DEBUG_WRITE, par, par->info->device, u8, buf, len,
+ "%s(len=%d): ", __func__, len);
+
+ if (!par->spi) {
+ dev_err(par->info->device,
+ "%s: par->spi is unexpectedly NULL\n", __func__);
+ return -1;
+ }
+
+ spi_message_init(&m);
+ if (par->txbuf.dma && buf == par->txbuf.buf) {
+ t.tx_dma = par->txbuf.dma;
+ m.is_dma_mapped = 1;
+ }
+ spi_message_add_tail(&t, &m);
+ return spi_sync(par->spi, &m);
+}
+
+static int init_display(struct fbtft_par *par)
+{
+ gpio_set_value(par->gpio.dc, 1);
+
+ fbtft_par_dbg(DEBUG_INIT_DISPLAY, par,
+ "%s()\n", __func__);
+ fbtft_par_dbg(DEBUG_INIT_DISPLAY, par,
+ "display size %dx%d\n", par->info->var.xres, par->info->var.yres);
+
+ par->fbtftops.reset(par);
+
+ if ((par->info->var.xres == 320) && (par->info->var.yres == 240)) {
+ /* PLL clock frequency */
+ write_reg(par, 0x88, 0x0A);
+ write_reg(par, 0x89, 0x02);
+ mdelay(10);
+ /* color deep / MCU Interface */
+ write_reg(par, 0x10, 0x0C);
+ /* pixel clock period */
+ write_reg(par, 0x04, 0x03);
+ mdelay(1);
+ /* horizontal settings */
+ write_reg(par, 0x14, 0x27);
+ write_reg(par, 0x15, 0x00);
+ write_reg(par, 0x16, 0x05);
+ write_reg(par, 0x17, 0x04);
+ write_reg(par, 0x18, 0x03);
+ /* vertical settings */
+ write_reg(par, 0x19, 0xEF);
+ write_reg(par, 0x1A, 0x00);
+ write_reg(par, 0x1B, 0x05);
+ write_reg(par, 0x1C, 0x00);
+ write_reg(par, 0x1D, 0x0E);
+ write_reg(par, 0x1E, 0x00);
+ write_reg(par, 0x1F, 0x02);
+ } else if ((par->info->var.xres == 480) && (par->info->var.yres == 272)) {
+ /* PLL clock frequency */
+ write_reg(par, 0x88, 0x0A);
+ write_reg(par, 0x89, 0x02);
+ mdelay(10);
+ /* color deep / MCU Interface */
+ write_reg(par, 0x10, 0x0C);
+ /* pixel clock period */
+ write_reg(par, 0x04, 0x82);
+ mdelay(1);
+ /* horizontal settings */
+ write_reg(par, 0x14, 0x3B);
+ write_reg(par, 0x15, 0x00);
+ write_reg(par, 0x16, 0x01);
+ write_reg(par, 0x17, 0x00);
+ write_reg(par, 0x18, 0x05);
+ /* vertical settings */
+ write_reg(par, 0x19, 0x0F);
+ write_reg(par, 0x1A, 0x01);
+ write_reg(par, 0x1B, 0x02);
+ write_reg(par, 0x1C, 0x00);
+ write_reg(par, 0x1D, 0x07);
+ write_reg(par, 0x1E, 0x00);
+ write_reg(par, 0x1F, 0x09);
+ } else if ((par->info->var.xres == 640) && (par->info->var.yres == 480)) {
+ /* PLL clock frequency */
+ write_reg(par, 0x88, 0x0B);
+ write_reg(par, 0x89, 0x02);
+ mdelay(10);
+ /* color deep / MCU Interface */
+ write_reg(par, 0x10, 0x0C);
+ /* pixel clock period */
+ write_reg(par, 0x04, 0x01);
+ mdelay(1);
+ /* horizontal settings */
+ write_reg(par, 0x14, 0x4F);
+ write_reg(par, 0x15, 0x05);
+ write_reg(par, 0x16, 0x0F);
+ write_reg(par, 0x17, 0x01);
+ write_reg(par, 0x18, 0x00);
+ /* vertical settings */
+ write_reg(par, 0x19, 0xDF);
+ write_reg(par, 0x1A, 0x01);
+ write_reg(par, 0x1B, 0x0A);
+ write_reg(par, 0x1C, 0x00);
+ write_reg(par, 0x1D, 0x0E);
+ write_reg(par, 0x1E, 0x00);
+ write_reg(par, 0x1F, 0x01);
+ } else if ((par->info->var.xres == 800) && (par->info->var.yres == 480)) {
+ /* PLL clock frequency */
+ write_reg(par, 0x88, 0x0B);
+ write_reg(par, 0x89, 0x02);
+ mdelay(10);
+ /* color deep / MCU Interface */
+ write_reg(par, 0x10, 0x0C);
+ /* pixel clock period */
+ write_reg(par, 0x04, 0x81);
+ mdelay(1);
+ /* horizontal settings */
+ write_reg(par, 0x14, 0x63);
+ write_reg(par, 0x15, 0x03);
+ write_reg(par, 0x16, 0x03);
+ write_reg(par, 0x17, 0x02);
+ write_reg(par, 0x18, 0x00);
+ /* vertical settings */
+ write_reg(par, 0x19, 0xDF);
+ write_reg(par, 0x1A, 0x01);
+ write_reg(par, 0x1B, 0x14);
+ write_reg(par, 0x1C, 0x00);
+ write_reg(par, 0x1D, 0x06);
+ write_reg(par, 0x1E, 0x00);
+ write_reg(par, 0x1F, 0x01);
+ } else {
+ dev_err(par->info->device, "display size is not supported!!");
+ return -1;
+ }
+
+ /* PWM clock */
+ write_reg(par, 0x8a, 0x81);
+ write_reg(par, 0x8b, 0xFF);
+ mdelay(10);
+
+ /* Display ON */
+ write_reg(par, 0x01, 0x80);
+ mdelay(10);
+
+ return 0;
+}
+
+static void set_addr_win(struct fbtft_par *par, int xs, int ys, int xe, int ye)
+{
+ fbtft_par_dbg(DEBUG_SET_ADDR_WIN, par,
+ "%s(xs=%d, ys=%d, xe=%d, ye=%d)\n", __func__, xs, ys, xe, ye);
+
+ /* Set_Active_Window */
+ write_reg(par, 0x30, xs & 0x00FF);
+ write_reg(par, 0x31, (xs & 0xFF00) >> 8);
+ write_reg(par, 0x32, ys & 0x00FF);
+ write_reg(par, 0x33, (ys & 0xFF00) >> 8);
+ write_reg(par, 0x34, (xs+xe) & 0x00FF);
+ write_reg(par, 0x35, ((xs+xe) & 0xFF00) >> 8);
+ write_reg(par, 0x36, (ys+ye) & 0x00FF);
+ write_reg(par, 0x37, ((ys+ye) & 0xFF00) >> 8);
+
+ /* Set_Memory_Write_Cursor */
+ write_reg(par, 0x46, xs & 0xff);
+ write_reg(par, 0x47, (xs >> 8) & 0x03);
+ write_reg(par, 0x48, ys & 0xff);
+ write_reg(par, 0x49, (ys >> 8) & 0x01);
+
+ write_reg(par, 0x02);
+}
+
+static void write_reg8_bus8(struct fbtft_par *par, int len, ...)
+{
+ va_list args;
+ int i, ret;
+ u8 *buf = (u8 *)par->buf;
+
+ /* slow down spi-speed for writing registers */
+ par->fbtftops.write = write_spi;
+
+ if (unlikely(par->debug & DEBUG_WRITE_REGISTER)) {
+ va_start(args, len);
+ for (i = 0; i < len; i++)
+ buf[i] = (u8)va_arg(args, unsigned int);
+ va_end(args);
+ fbtft_par_dbg_hex(DEBUG_WRITE_REGISTER, par, par->info->device,
+ u8, buf, len, "%s: ", __func__);
+ }
+
+ va_start(args, len);
+ *buf++ = 0x80;
+ *buf = (u8)va_arg(args, unsigned int);
+ ret = par->fbtftops.write(par, par->buf, 2);
+ if (ret < 0) {
+ va_end(args);
+ dev_err(par->info->device, "write() failed and returned %dn",
+ ret);
+ return;
+ }
+ len--;
+
+ udelay(100);
+
+ if (len) {
+ buf = (u8 *)par->buf;
+ *buf++ = 0x00;
+ i = len;
+ while (i--)
+ *buf++ = (u8)va_arg(args, unsigned int);
+
+ ret = par->fbtftops.write(par, par->buf, len + 1);
+ if (ret < 0) {
+ va_end(args);
+ dev_err(par->info->device,
+ "write() failed and returned %dn", ret);
+ return;
+ }
+ }
+ va_end(args);
+
+ /* restore user spi-speed */
+ par->fbtftops.write = fbtft_write_spi;
+ udelay(100);
+}
+
+static int write_vmem16_bus8(struct fbtft_par *par, size_t offset, size_t len)
+{
+ u16 *vmem16;
+ u16 *txbuf16 = (u16 *)par->txbuf.buf;
+ size_t remain;
+ size_t to_copy;
+ size_t tx_array_size;
+ int i;
+ int ret = 0;
+ size_t startbyte_size = 0;
+
+ fbtft_par_dbg(DEBUG_WRITE_VMEM, par, "%s(offset=%zu, len=%zu)\n",
+ __func__, offset, len);
+
+ remain = len / 2;
+ vmem16 = (u16 *)(par->info->screen_base + offset);
+ tx_array_size = par->txbuf.len / 2;
+ txbuf16 = (u16 *)(par->txbuf.buf + 1);
+ tx_array_size -= 2;
+ *(u8 *)(par->txbuf.buf) = 0x00;
+ startbyte_size = 1;
+
+ while (remain) {
+ to_copy = remain > tx_array_size ? tx_array_size : remain;
+ dev_dbg(par->info->device, " to_copy=%zu, remain=%zu\n",
+ to_copy, remain - to_copy);
+
+ for (i = 0; i < to_copy; i++)
+ txbuf16[i] = cpu_to_be16(vmem16[i]);
+
+ vmem16 = vmem16 + to_copy;
+ ret = par->fbtftops.write(par, par->txbuf.buf,
+ startbyte_size + to_copy * 2);
+ if (ret < 0)
+ return ret;
+ remain -= to_copy;
+ }
+
+ return ret;
+}
+
+static struct fbtft_display display = {
+ .regwidth = 8,
+ .fbtftops = {
+ .init_display = init_display,
+ .set_addr_win = set_addr_win,
+ .write_register = write_reg8_bus8,
+ .write_vmem = write_vmem16_bus8,
+ .write = write_spi,
+ },
+};
+FBTFT_REGISTER_DRIVER(DRVNAME, "raio,ra8875", &display);
+
+MODULE_ALIAS("spi:" DRVNAME);
+MODULE_ALIAS("platform:" DRVNAME);
+MODULE_ALIAS("spi:ra8875");
+MODULE_ALIAS("platform:ra8875");
+
+MODULE_DESCRIPTION("FB driver for the RA8875 LCD Controller");
+MODULE_AUTHOR("Pf@nne");
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/fbtft/fb_s6d02a1.c b/drivers/staging/fbtft/fb_s6d02a1.c
new file mode 100644
index 000000000..f3302525e
--- /dev/null
+++ b/drivers/staging/fbtft/fb_s6d02a1.c
@@ -0,0 +1,168 @@
+/*
+ * FB driver for the S6D02A1 LCD Controller
+ *
+ * Based on fb_st7735r.c by Noralf Tronnes
+ * Init code from UTFT library by Henning Karlsen
+ *
+ * This program is free software; you can redistribute it and/or 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/kernel.h>
+#include <linux/init.h>
+
+#include "fbtft.h"
+
+#define DRVNAME "fb_s6d02a1"
+
+static int default_init_sequence[] = {
+
+ -1, 0xf0, 0x5a, 0x5a,
+
+ -1, 0xfc, 0x5a, 0x5a,
+
+ -1, 0xfa, 0x02, 0x1f, 0x00, 0x10, 0x22, 0x30, 0x38, 0x3A, 0x3A, 0x3A, 0x3A, 0x3A, 0x3d, 0x02, 0x01,
+
+ -1, 0xfb, 0x21, 0x00, 0x02, 0x04, 0x07, 0x0a, 0x0b, 0x0c, 0x0c, 0x16, 0x1e, 0x30, 0x3f, 0x01, 0x02,
+
+ /* power setting sequence */
+ -1, 0xfd, 0x00, 0x00, 0x00, 0x17, 0x10, 0x00, 0x01, 0x01, 0x00, 0x1f, 0x1f,
+
+ -1, 0xf4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x3f, 0x07, 0x00, 0x3C, 0x36, 0x00, 0x3C, 0x36, 0x00,
+
+ -1, 0xf5, 0x00, 0x70, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6d, 0x66, 0x06,
+
+ -1, 0xf6, 0x02, 0x00, 0x3f, 0x00, 0x00, 0x00, 0x02, 0x00, 0x06, 0x01, 0x00,
+
+ -1, 0xf2, 0x00, 0x01, 0x03, 0x08, 0x08, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x04, 0x08, 0x08,
+
+ -1, 0xf8, 0x11,
+
+ -1, 0xf7, 0xc8, 0x20, 0x00, 0x00,
+
+ -1, 0xf3, 0x00, 0x00,
+
+ -1, 0x11,
+ -2, 50,
+
+ -1, 0xf3, 0x00, 0x01,
+ -2, 50,
+ -1, 0xf3, 0x00, 0x03,
+ -2, 50,
+ -1, 0xf3, 0x00, 0x07,
+ -2, 50,
+ -1, 0xf3, 0x00, 0x0f,
+ -2, 50,
+
+ -1, 0xf4, 0x00, 0x04, 0x00, 0x00, 0x00, 0x3f, 0x3f, 0x07, 0x00, 0x3C, 0x36, 0x00, 0x3C, 0x36, 0x00,
+ -2, 50,
+
+ -1, 0xf3, 0x00, 0x1f,
+ -2, 50,
+ -1, 0xf3, 0x00, 0x7f,
+ -2, 50,
+
+ -1, 0xf3, 0x00, 0xff,
+ -2, 50,
+
+ -1, 0xfd, 0x00, 0x00, 0x00, 0x17, 0x10, 0x00, 0x00, 0x01, 0x00, 0x16, 0x16,
+
+ -1, 0xf4, 0x00, 0x09, 0x00, 0x00, 0x00, 0x3f, 0x3f, 0x07, 0x00, 0x3C, 0x36, 0x00, 0x3C, 0x36, 0x00,
+
+ /* initializing sequence */
+
+ -1, 0x36, 0x08,
+
+ -1, 0x35, 0x00,
+
+ -1, 0x3a, 0x05,
+
+ /* gamma setting sequence */
+ -1, 0x26, 0x01, /* preset gamma curves, possible values 0x01, 0x02, 0x04, 0x08 */
+
+ -2, 150,
+ -1, 0x29,
+ -1, 0x2c,
+ /* end marker */
+ -3
+
+};
+
+static void set_addr_win(struct fbtft_par *par, int xs, int ys, int xe, int ye)
+{
+ fbtft_par_dbg(DEBUG_SET_ADDR_WIN, par,
+ "%s(xs=%d, ys=%d, xe=%d, ye=%d)\n", __func__, xs, ys, xe, ye);
+
+ /* Column address */
+ write_reg(par, 0x2A, xs >> 8, xs & 0xFF, xe >> 8, xe & 0xFF);
+
+ /* Row address */
+ write_reg(par, 0x2B, ys >> 8, ys & 0xFF, ye >> 8, ye & 0xFF);
+
+ /* Memory write */
+ write_reg(par, 0x2C);
+}
+
+#define MY (1 << 7)
+#define MX (1 << 6)
+#define MV (1 << 5)
+static int set_var(struct fbtft_par *par)
+{
+ fbtft_par_dbg(DEBUG_INIT_DISPLAY, par, "%s()\n", __func__);
+
+ /* MADCTL - Memory data access control
+ RGB/BGR:
+ 1. Mode selection pin SRGB
+ RGB H/W pin for color filter setting: 0=RGB, 1=BGR
+ 2. MADCTL RGB bit
+ RGB-BGR ORDER color filter panel: 0=RGB, 1=BGR */
+ switch (par->info->var.rotate) {
+ case 0:
+ write_reg(par, 0x36, MX | MY | (par->bgr << 3));
+ break;
+ case 270:
+ write_reg(par, 0x36, MY | MV | (par->bgr << 3));
+ break;
+ case 180:
+ write_reg(par, 0x36, par->bgr << 3);
+ break;
+ case 90:
+ write_reg(par, 0x36, MX | MV | (par->bgr << 3));
+ break;
+ }
+
+ return 0;
+}
+
+static struct fbtft_display display = {
+ .regwidth = 8,
+ .width = 128,
+ .height = 160,
+ .init_sequence = default_init_sequence,
+ .fbtftops = {
+ .set_addr_win = set_addr_win,
+ .set_var = set_var,
+ },
+};
+FBTFT_REGISTER_DRIVER(DRVNAME, "samsung,s6d02a1", &display);
+
+MODULE_ALIAS("spi:" DRVNAME);
+MODULE_ALIAS("platform:" DRVNAME);
+MODULE_ALIAS("spi:s6d02a1");
+MODULE_ALIAS("platform:s6d02a1");
+
+MODULE_DESCRIPTION("FB driver for the S6D02A1 LCD Controller");
+MODULE_AUTHOR("WOLFGANG BUENING");
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/fbtft/fb_s6d1121.c b/drivers/staging/fbtft/fb_s6d1121.c
new file mode 100644
index 000000000..2e1b72ad5
--- /dev/null
+++ b/drivers/staging/fbtft/fb_s6d1121.c
@@ -0,0 +1,206 @@
+/*
+ * FB driver for the S6D1121 LCD Controller
+ *
+ * Copyright (C) 2013 Roman Rolinsky
+ *
+ * Based on fb_ili9325.c by Noralf Tronnes
+ * Based on ili9325.c by Jeroen Domburg
+ * Init code from UTFT library by Henning Karlsen
+ *
+ * This program is free software; you can redistribute it and/or 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/kernel.h>
+#include <linux/init.h>
+#include <linux/gpio.h>
+#include <linux/delay.h>
+
+#include "fbtft.h"
+
+#define DRVNAME "fb_s6d1121"
+#define WIDTH 240
+#define HEIGHT 320
+#define BPP 16
+#define FPS 20
+#define DEFAULT_GAMMA "26 09 24 2C 1F 23 24 25 22 26 25 23 0D 00\n" \
+ "1C 1A 13 1D 0B 11 12 10 13 15 36 19 00 0D"
+
+static int init_display(struct fbtft_par *par)
+{
+ fbtft_par_dbg(DEBUG_INIT_DISPLAY, par, "%s()\n", __func__);
+
+ par->fbtftops.reset(par);
+
+ if (par->gpio.cs != -1)
+ gpio_set_value(par->gpio.cs, 0); /* Activate chip */
+
+ /* Initialization sequence from Lib_UTFT */
+
+ write_reg(par, 0x0011, 0x2004);
+ write_reg(par, 0x0013, 0xCC00);
+ write_reg(par, 0x0015, 0x2600);
+ write_reg(par, 0x0014, 0x252A);
+ write_reg(par, 0x0012, 0x0033);
+ write_reg(par, 0x0013, 0xCC04);
+ write_reg(par, 0x0013, 0xCC06);
+ write_reg(par, 0x0013, 0xCC4F);
+ write_reg(par, 0x0013, 0x674F);
+ write_reg(par, 0x0011, 0x2003);
+ write_reg(par, 0x0016, 0x0007);
+ write_reg(par, 0x0002, 0x0013);
+ write_reg(par, 0x0003, 0x0003);
+ write_reg(par, 0x0001, 0x0127);
+ write_reg(par, 0x0008, 0x0303);
+ write_reg(par, 0x000A, 0x000B);
+ write_reg(par, 0x000B, 0x0003);
+ write_reg(par, 0x000C, 0x0000);
+ write_reg(par, 0x0041, 0x0000);
+ write_reg(par, 0x0050, 0x0000);
+ write_reg(par, 0x0060, 0x0005);
+ write_reg(par, 0x0070, 0x000B);
+ write_reg(par, 0x0071, 0x0000);
+ write_reg(par, 0x0078, 0x0000);
+ write_reg(par, 0x007A, 0x0000);
+ write_reg(par, 0x0079, 0x0007);
+ write_reg(par, 0x0007, 0x0051);
+ write_reg(par, 0x0007, 0x0053);
+ write_reg(par, 0x0079, 0x0000);
+
+ write_reg(par, 0x0022); /* Write Data to GRAM */
+
+ return 0;
+}
+
+static void set_addr_win(struct fbtft_par *par, int xs, int ys, int xe, int ye)
+{
+ fbtft_par_dbg(DEBUG_SET_ADDR_WIN, par,
+ "%s(xs=%d, ys=%d, xe=%d, ye=%d)\n", __func__, xs, ys, xe, ye);
+ switch (par->info->var.rotate) {
+ /* R20h = Horizontal GRAM Start Address */
+ /* R21h = Vertical GRAM Start Address */
+ case 0:
+ write_reg(par, 0x0020, xs);
+ write_reg(par, 0x0021, ys);
+ break;
+ case 180:
+ write_reg(par, 0x0020, WIDTH - 1 - xs);
+ write_reg(par, 0x0021, HEIGHT - 1 - ys);
+ break;
+ case 270:
+ write_reg(par, 0x0020, WIDTH - 1 - ys);
+ write_reg(par, 0x0021, xs);
+ break;
+ case 90:
+ write_reg(par, 0x0020, ys);
+ write_reg(par, 0x0021, HEIGHT - 1 - xs);
+ break;
+ }
+ write_reg(par, 0x0022); /* Write Data to GRAM */
+}
+
+static int set_var(struct fbtft_par *par)
+{
+ fbtft_par_dbg(DEBUG_INIT_DISPLAY, par, "%s()\n", __func__);
+
+ switch (par->info->var.rotate) {
+ /* AM: GRAM update direction */
+ case 0:
+ write_reg(par, 0x03, 0x0003 | (par->bgr << 12));
+ break;
+ case 180:
+ write_reg(par, 0x03, 0x0000 | (par->bgr << 12));
+ break;
+ case 270:
+ write_reg(par, 0x03, 0x000A | (par->bgr << 12));
+ break;
+ case 90:
+ write_reg(par, 0x03, 0x0009 | (par->bgr << 12));
+ break;
+ }
+
+ return 0;
+}
+
+/*
+ Gamma string format:
+ PKP0 PKP1 PKP2 PKP3 PKP4 PKP5 PKP6 PKP7 PKP8 PKP9 PKP10 PKP11 VRP0 VRP1
+ PKN0 PKN1 PKN2 PKN3 PKN4 PKN5 PKN6 PKN7 PRN8 PRN9 PRN10 PRN11 VRN0 VRN1
+*/
+#define CURVE(num, idx) curves[num*par->gamma.num_values + idx]
+static int set_gamma(struct fbtft_par *par, unsigned long *curves)
+{
+ unsigned long mask[] = {
+ 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f,
+ 0x3f, 0x3f, 0x1f, 0x1f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f,
+ 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x1f, 0x1f,
+ };
+ int i, j;
+
+ fbtft_par_dbg(DEBUG_INIT_DISPLAY, par, "%s()\n", __func__);
+
+ /* apply mask */
+ for (i = 0; i < 2; i++)
+ for (j = 0; j < 14; j++)
+ CURVE(i, j) &= mask[i*par->gamma.num_values + j];
+
+ write_reg(par, 0x0030, CURVE(0, 1) << 8 | CURVE(0, 0));
+ write_reg(par, 0x0031, CURVE(0, 3) << 8 | CURVE(0, 2));
+ write_reg(par, 0x0032, CURVE(0, 5) << 8 | CURVE(0, 3));
+ write_reg(par, 0x0033, CURVE(0, 7) << 8 | CURVE(0, 6));
+ write_reg(par, 0x0034, CURVE(0, 9) << 8 | CURVE(0, 8));
+ write_reg(par, 0x0035, CURVE(0, 11) << 8 | CURVE(0, 10));
+
+ write_reg(par, 0x0036, CURVE(1, 1) << 8 | CURVE(1, 0));
+ write_reg(par, 0x0037, CURVE(1, 3) << 8 | CURVE(1, 2));
+ write_reg(par, 0x0038, CURVE(1, 5) << 8 | CURVE(1, 4));
+ write_reg(par, 0x0039, CURVE(1, 7) << 8 | CURVE(1, 6));
+ write_reg(par, 0x003A, CURVE(1, 9) << 8 | CURVE(1, 8));
+ write_reg(par, 0x003B, CURVE(1, 11) << 8 | CURVE(1, 10));
+
+ write_reg(par, 0x003C, CURVE(0, 13) << 8 | CURVE(0, 12));
+ write_reg(par, 0x003D, CURVE(1, 13) << 8 | CURVE(1, 12));
+
+ return 0;
+}
+#undef CURVE
+
+
+static struct fbtft_display display = {
+ .regwidth = 16,
+ .width = WIDTH,
+ .height = HEIGHT,
+ .bpp = BPP,
+ .fps = FPS,
+ .gamma_num = 2,
+ .gamma_len = 14,
+ .gamma = DEFAULT_GAMMA,
+ .fbtftops = {
+ .init_display = init_display,
+ .set_addr_win = set_addr_win,
+ .set_var = set_var,
+ .set_gamma = set_gamma,
+ },
+};
+FBTFT_REGISTER_DRIVER(DRVNAME, "samsung,s6d1121", &display);
+
+MODULE_ALIAS("spi:" DRVNAME);
+MODULE_ALIAS("platform:" DRVNAME);
+MODULE_ALIAS("spi:s6d1121");
+MODULE_ALIAS("platform:s6d1121");
+
+MODULE_DESCRIPTION("FB driver for the S6D1121 LCD Controller");
+MODULE_AUTHOR("Roman Rolinsky");
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/fbtft/fb_ssd1289.c b/drivers/staging/fbtft/fb_ssd1289.c
new file mode 100644
index 000000000..17a77e061
--- /dev/null
+++ b/drivers/staging/fbtft/fb_ssd1289.c
@@ -0,0 +1,205 @@
+/*
+ * FB driver for the SSD1289 LCD Controller
+ *
+ * Copyright (C) 2013 Noralf Tronnes
+ *
+ * Init sequence taken from ITDB02_Graph16.cpp - (C)2010-2011 Henning Karlsen
+ *
+ * This program is free software; you can redistribute it and/or 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/kernel.h>
+#include <linux/init.h>
+#include <linux/gpio.h>
+
+#include "fbtft.h"
+
+#define DRVNAME "fb_ssd1289"
+#define WIDTH 240
+#define HEIGHT 320
+#define DEFAULT_GAMMA "02 03 2 5 7 7 4 2 4 2\n" \
+ "02 03 2 5 7 5 4 2 4 2"
+
+static unsigned reg11 = 0x6040;
+module_param(reg11, uint, 0);
+MODULE_PARM_DESC(reg11, "Register 11h value");
+
+
+static int init_display(struct fbtft_par *par)
+{
+ fbtft_par_dbg(DEBUG_INIT_DISPLAY, par, "%s()\n", __func__);
+
+ par->fbtftops.reset(par);
+
+ if (par->gpio.cs != -1)
+ gpio_set_value(par->gpio.cs, 0); /* Activate chip */
+
+ write_reg(par, 0x00, 0x0001);
+ write_reg(par, 0x03, 0xA8A4);
+ write_reg(par, 0x0C, 0x0000);
+ write_reg(par, 0x0D, 0x080C);
+ write_reg(par, 0x0E, 0x2B00);
+ write_reg(par, 0x1E, 0x00B7);
+ write_reg(par, 0x01,
+ (1 << 13) | (par->bgr << 11) | (1 << 9) | (HEIGHT - 1));
+ write_reg(par, 0x02, 0x0600);
+ write_reg(par, 0x10, 0x0000);
+ write_reg(par, 0x05, 0x0000);
+ write_reg(par, 0x06, 0x0000);
+ write_reg(par, 0x16, 0xEF1C);
+ write_reg(par, 0x17, 0x0003);
+ write_reg(par, 0x07, 0x0233);
+ write_reg(par, 0x0B, 0x0000);
+ write_reg(par, 0x0F, 0x0000);
+ write_reg(par, 0x41, 0x0000);
+ write_reg(par, 0x42, 0x0000);
+ write_reg(par, 0x48, 0x0000);
+ write_reg(par, 0x49, 0x013F);
+ write_reg(par, 0x4A, 0x0000);
+ write_reg(par, 0x4B, 0x0000);
+ write_reg(par, 0x44, 0xEF00);
+ write_reg(par, 0x45, 0x0000);
+ write_reg(par, 0x46, 0x013F);
+ write_reg(par, 0x23, 0x0000);
+ write_reg(par, 0x24, 0x0000);
+ write_reg(par, 0x25, 0x8000);
+ write_reg(par, 0x4f, 0x0000);
+ write_reg(par, 0x4e, 0x0000);
+ write_reg(par, 0x22);
+ return 0;
+}
+
+static void set_addr_win(struct fbtft_par *par, int xs, int ys, int xe, int ye)
+{
+ fbtft_par_dbg(DEBUG_SET_ADDR_WIN, par,
+ "%s(xs=%d, ys=%d, xe=%d, ye=%d)\n", __func__, xs, ys, xe, ye);
+
+ switch (par->info->var.rotate) {
+ /* R4Eh - Set GDDRAM X address counter */
+ /* R4Fh - Set GDDRAM Y address counter */
+ case 0:
+ write_reg(par, 0x4e, xs);
+ write_reg(par, 0x4f, ys);
+ break;
+ case 180:
+ write_reg(par, 0x4e, par->info->var.xres - 1 - xs);
+ write_reg(par, 0x4f, par->info->var.yres - 1 - ys);
+ break;
+ case 270:
+ write_reg(par, 0x4e, par->info->var.yres - 1 - ys);
+ write_reg(par, 0x4f, xs);
+ break;
+ case 90:
+ write_reg(par, 0x4e, ys);
+ write_reg(par, 0x4f, par->info->var.xres - 1 - xs);
+ break;
+ }
+
+ /* R22h - RAM data write */
+ write_reg(par, 0x22);
+}
+
+static int set_var(struct fbtft_par *par)
+{
+ fbtft_par_dbg(DEBUG_INIT_DISPLAY, par, "%s()\n", __func__);
+
+ if (par->fbtftops.init_display != init_display) {
+ /* don't risk messing up register 11h */
+ fbtft_par_dbg(DEBUG_INIT_DISPLAY, par,
+ "%s: skipping since custom init_display() is used\n",
+ __func__);
+ return 0;
+ }
+
+ switch (par->info->var.rotate) {
+ case 0:
+ write_reg(par, 0x11, reg11 | 0x30);
+ break;
+ case 270:
+ write_reg(par, 0x11, reg11 | 0x28);
+ break;
+ case 180:
+ write_reg(par, 0x11, reg11 | 0x00);
+ break;
+ case 90:
+ write_reg(par, 0x11, reg11 | 0x18);
+ break;
+ }
+
+ return 0;
+}
+
+/*
+ Gamma string format:
+ VRP0 VRP1 PRP0 PRP1 PKP0 PKP1 PKP2 PKP3 PKP4 PKP5
+ VRN0 VRN1 PRN0 PRN1 PKN0 PKN1 PKN2 PKN3 PKN4 PKN5
+*/
+#define CURVE(num, idx) curves[num*par->gamma.num_values + idx]
+static int set_gamma(struct fbtft_par *par, unsigned long *curves)
+{
+ unsigned long mask[] = {
+ 0x1f, 0x1f, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+ 0x1f, 0x1f, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+ };
+ int i, j;
+
+ fbtft_par_dbg(DEBUG_INIT_DISPLAY, par, "%s()\n", __func__);
+
+ /* apply mask */
+ for (i = 0; i < 2; i++)
+ for (j = 0; j < 10; j++)
+ CURVE(i, j) &= mask[i*par->gamma.num_values + j];
+
+ write_reg(par, 0x0030, CURVE(0, 5) << 8 | CURVE(0, 4));
+ write_reg(par, 0x0031, CURVE(0, 7) << 8 | CURVE(0, 6));
+ write_reg(par, 0x0032, CURVE(0, 9) << 8 | CURVE(0, 8));
+ write_reg(par, 0x0033, CURVE(0, 3) << 8 | CURVE(0, 2));
+ write_reg(par, 0x0034, CURVE(1, 5) << 8 | CURVE(1, 4));
+ write_reg(par, 0x0035, CURVE(1, 7) << 8 | CURVE(1, 6));
+ write_reg(par, 0x0036, CURVE(1, 9) << 8 | CURVE(1, 8));
+ write_reg(par, 0x0037, CURVE(1, 3) << 8 | CURVE(1, 2));
+ write_reg(par, 0x003A, CURVE(0, 1) << 8 | CURVE(0, 0));
+ write_reg(par, 0x003B, CURVE(1, 1) << 8 | CURVE(1, 0));
+
+ return 0;
+}
+#undef CURVE
+
+
+static struct fbtft_display display = {
+ .regwidth = 16,
+ .width = WIDTH,
+ .height = HEIGHT,
+ .gamma_num = 2,
+ .gamma_len = 10,
+ .gamma = DEFAULT_GAMMA,
+ .fbtftops = {
+ .init_display = init_display,
+ .set_addr_win = set_addr_win,
+ .set_var = set_var,
+ .set_gamma = set_gamma,
+ },
+};
+FBTFT_REGISTER_DRIVER(DRVNAME, "solomon,ssd1289", &display);
+
+MODULE_ALIAS("spi:" DRVNAME);
+MODULE_ALIAS("platform:" DRVNAME);
+MODULE_ALIAS("spi:ssd1289");
+MODULE_ALIAS("platform:ssd1289");
+
+MODULE_DESCRIPTION("FB driver for the SSD1289 LCD Controller");
+MODULE_AUTHOR("Noralf Tronnes");
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/fbtft/fb_ssd1306.c b/drivers/staging/fbtft/fb_ssd1306.c
new file mode 100644
index 000000000..15ee44dd1
--- /dev/null
+++ b/drivers/staging/fbtft/fb_ssd1306.c
@@ -0,0 +1,229 @@
+/*
+ * FB driver for the SSD1306 OLED Controller
+ *
+ * Copyright (C) 2013 Noralf Tronnes
+ *
+ * This program is free software; you can redistribute it and/or 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/kernel.h>
+#include <linux/init.h>
+#include <linux/gpio.h>
+#include <linux/delay.h>
+
+#include "fbtft.h"
+
+#define DRVNAME "fb_ssd1306"
+#define WIDTH 128
+#define HEIGHT 64
+
+
+/*
+ write_reg() caveat:
+
+ This doesn't work because D/C has to be LOW for both values:
+ write_reg(par, val1, val2);
+
+ Do it like this:
+ write_reg(par, val1);
+ write_reg(par, val2);
+*/
+
+/* Init sequence taken from the Adafruit SSD1306 Arduino library */
+static int init_display(struct fbtft_par *par)
+{
+ fbtft_par_dbg(DEBUG_INIT_DISPLAY, par, "%s()\n", __func__);
+
+ par->fbtftops.reset(par);
+
+ if (par->gamma.curves[0] == 0) {
+ mutex_lock(&par->gamma.lock);
+ if (par->info->var.yres == 64)
+ par->gamma.curves[0] = 0xCF;
+ else
+ par->gamma.curves[0] = 0x8F;
+ mutex_unlock(&par->gamma.lock);
+ }
+
+ /* Set Display OFF */
+ write_reg(par, 0xAE);
+
+ /* Set Display Clock Divide Ratio/ Oscillator Frequency */
+ write_reg(par, 0xD5);
+ write_reg(par, 0x80);
+
+ /* Set Multiplex Ratio */
+ write_reg(par, 0xA8);
+ if (par->info->var.yres == 64)
+ write_reg(par, 0x3F);
+ else
+ write_reg(par, 0x1F);
+
+ /* Set Display Offset */
+ write_reg(par, 0xD3);
+ write_reg(par, 0x0);
+
+ /* Set Display Start Line */
+ write_reg(par, 0x40 | 0x0);
+
+ /* Charge Pump Setting */
+ write_reg(par, 0x8D);
+ /* A[2] = 1b, Enable charge pump during display on */
+ write_reg(par, 0x14);
+
+ /* Set Memory Addressing Mode */
+ write_reg(par, 0x20);
+ /* Vertical addressing mode */
+ write_reg(par, 0x01);
+
+ /*Set Segment Re-map */
+ /* column address 127 is mapped to SEG0 */
+ write_reg(par, 0xA0 | 0x1);
+
+ /* Set COM Output Scan Direction */
+ /* remapped mode. Scan from COM[N-1] to COM0 */
+ write_reg(par, 0xC8);
+
+ /* Set COM Pins Hardware Configuration */
+ write_reg(par, 0xDA);
+ if (par->info->var.yres == 64)
+ /* A[4]=1b, Alternative COM pin configuration */
+ write_reg(par, 0x12);
+ else
+ /* A[4]=0b, Sequential COM pin configuration */
+ write_reg(par, 0x02);
+
+ /* Set Pre-charge Period */
+ write_reg(par, 0xD9);
+ write_reg(par, 0xF1);
+
+ /* Set VCOMH Deselect Level */
+ write_reg(par, 0xDB);
+ /* according to the datasheet, this value is out of bounds */
+ write_reg(par, 0x40);
+
+ /* Entire Display ON */
+ /* Resume to RAM content display. Output follows RAM content */
+ write_reg(par, 0xA4);
+
+ /* Set Normal Display
+ 0 in RAM: OFF in display panel
+ 1 in RAM: ON in display panel */
+ write_reg(par, 0xA6);
+
+ /* Set Display ON */
+ write_reg(par, 0xAF);
+
+ return 0;
+}
+
+static void set_addr_win(struct fbtft_par *par, int xs, int ys, int xe, int ye)
+{
+ fbtft_par_dbg(DEBUG_SET_ADDR_WIN, par,
+ "%s(xs=%d, ys=%d, xe=%d, ye=%d)\n", __func__, xs, ys, xe, ye);
+
+ /* Set Lower Column Start Address for Page Addressing Mode */
+ write_reg(par, 0x00 | 0x0);
+ /* Set Higher Column Start Address for Page Addressing Mode */
+ write_reg(par, 0x10 | 0x0);
+ /* Set Display Start Line */
+ write_reg(par, 0x40 | 0x0);
+}
+
+static int blank(struct fbtft_par *par, bool on)
+{
+ fbtft_par_dbg(DEBUG_BLANK, par, "%s(blank=%s)\n",
+ __func__, on ? "true" : "false");
+
+ if (on)
+ write_reg(par, 0xAE);
+ else
+ write_reg(par, 0xAF);
+ return 0;
+}
+
+/* Gamma is used to control Contrast */
+static int set_gamma(struct fbtft_par *par, unsigned long *curves)
+{
+ fbtft_par_dbg(DEBUG_INIT_DISPLAY, par, "%s()\n", __func__);
+
+ /* apply mask */
+ curves[0] &= 0xFF;
+
+ /* Set Contrast Control for BANK0 */
+ write_reg(par, 0x81);
+ write_reg(par, curves[0]);
+
+ return 0;
+}
+
+static int write_vmem(struct fbtft_par *par, size_t offset, size_t len)
+{
+ u16 *vmem16 = (u16 *)par->info->screen_base;
+ u8 *buf = par->txbuf.buf;
+ int x, y, i;
+ int ret = 0;
+
+ fbtft_par_dbg(DEBUG_WRITE_VMEM, par, "%s()\n", __func__);
+
+ for (x = 0; x < par->info->var.xres; x++) {
+ for (y = 0; y < par->info->var.yres/8; y++) {
+ *buf = 0x00;
+ for (i = 0; i < 8; i++)
+ *buf |= (vmem16[(y*8+i)*par->info->var.xres+x] ? 1 : 0) << i;
+ buf++;
+ }
+ }
+
+ /* Write data */
+ gpio_set_value(par->gpio.dc, 1);
+ ret = par->fbtftops.write(par, par->txbuf.buf,
+ par->info->var.xres*par->info->var.yres/8);
+ if (ret < 0)
+ dev_err(par->info->device, "write failed and returned: %d\n",
+ ret);
+
+ return ret;
+}
+
+
+static struct fbtft_display display = {
+ .regwidth = 8,
+ .width = WIDTH,
+ .height = HEIGHT,
+ .gamma_num = 1,
+ .gamma_len = 1,
+ .gamma = "00",
+ .fbtftops = {
+ .write_vmem = write_vmem,
+ .init_display = init_display,
+ .set_addr_win = set_addr_win,
+ .blank = blank,
+ .set_gamma = set_gamma,
+ },
+};
+
+
+FBTFT_REGISTER_DRIVER(DRVNAME, "solomon,ssd1306", &display);
+
+MODULE_ALIAS("spi:" DRVNAME);
+MODULE_ALIAS("platform:" DRVNAME);
+MODULE_ALIAS("spi:ssd1306");
+MODULE_ALIAS("platform:ssd1306");
+
+MODULE_DESCRIPTION("SSD1306 OLED Driver");
+MODULE_AUTHOR("Noralf Tronnes");
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/fbtft/fb_ssd1331.c b/drivers/staging/fbtft/fb_ssd1331.c
new file mode 100644
index 000000000..5bb741046
--- /dev/null
+++ b/drivers/staging/fbtft/fb_ssd1331.c
@@ -0,0 +1,205 @@
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/gpio.h>
+#include <linux/spi/spi.h>
+#include <linux/delay.h>
+
+#include "fbtft.h"
+
+#define DRVNAME "fb_ssd1331"
+#define WIDTH 96
+#define HEIGHT 64
+#define GAMMA_NUM 1
+#define GAMMA_LEN 63
+#define DEFAULT_GAMMA "0 2 2 2 2 2 2 2 " \
+ "2 2 2 2 2 2 2 2 " \
+ "2 2 2 2 2 2 2 2 " \
+ "2 2 2 2 2 2 2 2 " \
+ "2 2 2 2 2 2 2 2 " \
+ "2 2 2 2 2 2 2 2 " \
+ "2 2 2 2 2 2 2 2 " \
+ "2 2 2 2 2 2 2" \
+
+static int init_display(struct fbtft_par *par)
+{
+ fbtft_par_dbg(DEBUG_INIT_DISPLAY, par, "%s()\n", __func__);
+
+ par->fbtftops.reset(par);
+
+ write_reg(par, 0xae); /* Display Off */
+ write_reg(par, 0xa0, 0x70 | (par->bgr << 2)); /* Set Colour Depth */
+ write_reg(par, 0x72); /* RGB colour */
+ write_reg(par, 0xa1, 0x00); /* Set Display Start Line */
+ write_reg(par, 0xa2, 0x00); /* Set Display Offset */
+ write_reg(par, 0xa4); /* NORMALDISPLAY */
+ write_reg(par, 0xa8, 0x3f); /* Set multiplex */
+ write_reg(par, 0xad, 0x8e); /* Set master */
+ /* write_reg(par, 0xb0, 0x0b); Set power mode */
+ write_reg(par, 0xb1, 0x31); /* Precharge */
+ write_reg(par, 0xb3, 0xf0); /* Clock div */
+ write_reg(par, 0x8a, 0x64); /* Precharge A */
+ write_reg(par, 0x8b, 0x78); /* Precharge B */
+ write_reg(par, 0x8c, 0x64); /* Precharge C */
+ write_reg(par, 0xbb, 0x3a); /* Precharge level */
+ write_reg(par, 0xbe, 0x3e); /* vcomh */
+ write_reg(par, 0x87, 0x06); /* Master current */
+ write_reg(par, 0x81, 0x91); /* Contrast A */
+ write_reg(par, 0x82, 0x50); /* Contrast B */
+ write_reg(par, 0x83, 0x7d); /* Contrast C */
+ write_reg(par, 0xaf); /* Set Sleep Mode Display On */
+
+ return 0;
+}
+
+static void set_addr_win(struct fbtft_par *par, int xs, int ys, int xe, int ye)
+{
+ fbtft_par_dbg(DEBUG_SET_ADDR_WIN, par,
+ "%s(xs=%d, ys=%d, xe=%d, ye=%d)\n", __func__, xs, ys, xe, ye);
+
+ write_reg(par, 0x15, xs, xe);
+ write_reg(par, 0x75, ys, ye);
+}
+
+static void write_reg8_bus8(struct fbtft_par *par, int len, ...)
+{
+ va_list args;
+ int i, ret;
+ u8 *buf = (u8 *)par->buf;
+
+ if (unlikely(par->debug & DEBUG_WRITE_REGISTER)) {
+ va_start(args, len);
+ for (i = 0; i < len; i++)
+ buf[i] = (u8)va_arg(args, unsigned int);
+ va_end(args);
+ fbtft_par_dbg_hex(DEBUG_WRITE_REGISTER, par, par->info->device, u8, buf, len, "%s: ", __func__);
+ }
+
+ va_start(args, len);
+
+ *buf = (u8)va_arg(args, unsigned int);
+ if (par->gpio.dc != -1)
+ gpio_set_value(par->gpio.dc, 0);
+ ret = par->fbtftops.write(par, par->buf, sizeof(u8));
+ if (ret < 0) {
+ va_end(args);
+ dev_err(par->info->device,
+ "write() failed and returned %d\n", ret);
+ return;
+ }
+ len--;
+
+ if (len) {
+ i = len;
+ while (i--)
+ *buf++ = (u8)va_arg(args, unsigned int);
+ ret = par->fbtftops.write(par, par->buf, len * (sizeof(u8)));
+ if (ret < 0) {
+ va_end(args);
+ dev_err(par->info->device,
+ "write() failed and returned %d\n", ret);
+ return;
+ }
+ }
+ if (par->gpio.dc != -1)
+ gpio_set_value(par->gpio.dc, 1);
+ va_end(args);
+}
+
+/*
+ Grayscale Lookup Table
+ GS1 - GS63
+ The driver Gamma curve contains the relative values between the entries
+ in the Lookup table.
+
+ From datasheet:
+ 8.8 Gray Scale Decoder
+
+ there are total 180 Gamma Settings (Setting 0 to Setting 180)
+ available for the Gray Scale table.
+
+ The gray scale is defined in incremental way, with reference
+ to the length of previous table entry:
+ Setting of GS1 has to be >= 0
+ Setting of GS2 has to be > Setting of GS1 +1
+ Setting of GS3 has to be > Setting of GS2 +1
+ :
+ Setting of GS63 has to be > Setting of GS62 +1
+
+
+*/
+static int set_gamma(struct fbtft_par *par, unsigned long *curves)
+{
+ unsigned long tmp[GAMMA_NUM * GAMMA_LEN];
+ int i, acc = 0;
+
+ fbtft_par_dbg(DEBUG_INIT_DISPLAY, par, "%s()\n", __func__);
+
+ for (i = 0; i < 63; i++) {
+ if (i > 0 && curves[i] < 2) {
+ dev_err(par->info->device,
+ "Illegal value in Grayscale Lookup Table at index %d. " \
+ "Must be greater than 1\n", i);
+ return -EINVAL;
+ }
+ acc += curves[i];
+ tmp[i] = acc;
+ if (acc > 180) {
+ dev_err(par->info->device,
+ "Illegal value(s) in Grayscale Lookup Table. " \
+ "At index=%d, the accumulated value has exceeded 180\n", i);
+ return -EINVAL;
+ }
+ }
+
+ write_reg(par, 0xB8,
+ tmp[0], tmp[1], tmp[2], tmp[3], tmp[4], tmp[5], tmp[6], tmp[7],
+ tmp[8], tmp[9], tmp[10], tmp[11], tmp[12], tmp[13], tmp[14], tmp[15],
+ tmp[16], tmp[17], tmp[18], tmp[19], tmp[20], tmp[21], tmp[22], tmp[23],
+ tmp[24], tmp[25], tmp[26], tmp[27], tmp[28], tmp[29], tmp[30], tmp[31],
+ tmp[32], tmp[33], tmp[34], tmp[35], tmp[36], tmp[37], tmp[38], tmp[39],
+ tmp[40], tmp[41], tmp[42], tmp[43], tmp[44], tmp[45], tmp[46], tmp[47],
+ tmp[48], tmp[49], tmp[50], tmp[51], tmp[52], tmp[53], tmp[54], tmp[55],
+ tmp[56], tmp[57], tmp[58], tmp[59], tmp[60], tmp[61], tmp[62]);
+
+ return 0;
+}
+
+static int blank(struct fbtft_par *par, bool on)
+{
+ fbtft_par_dbg(DEBUG_BLANK, par, "%s(blank=%s)\n",
+ __func__, on ? "true" : "false");
+ if (on)
+ write_reg(par, 0xAE);
+ else
+ write_reg(par, 0xAF);
+ return 0;
+}
+
+
+static struct fbtft_display display = {
+ .regwidth = 8,
+ .width = WIDTH,
+ .height = HEIGHT,
+ .gamma_num = GAMMA_NUM,
+ .gamma_len = GAMMA_LEN,
+ .gamma = DEFAULT_GAMMA,
+ .fbtftops = {
+ .write_register = write_reg8_bus8,
+ .init_display = init_display,
+ .set_addr_win = set_addr_win,
+ .set_gamma = set_gamma,
+ .blank = blank,
+ },
+};
+
+FBTFT_REGISTER_DRIVER(DRVNAME, "solomon,ssd1331", &display);
+
+MODULE_ALIAS("spi:" DRVNAME);
+MODULE_ALIAS("platform:" DRVNAME);
+MODULE_ALIAS("spi:ssd1331");
+MODULE_ALIAS("platform:ssd1331");
+
+MODULE_DESCRIPTION("SSD1331 OLED Driver");
+MODULE_AUTHOR("Alec Smecher (adapted from SSD1351 by James Davies)");
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/fbtft/fb_ssd1351.c b/drivers/staging/fbtft/fb_ssd1351.c
new file mode 100644
index 000000000..9bcd7a0ae
--- /dev/null
+++ b/drivers/staging/fbtft/fb_ssd1351.c
@@ -0,0 +1,255 @@
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/gpio.h>
+#include <linux/spi/spi.h>
+#include <linux/delay.h>
+
+#include "fbtft.h"
+
+#define DRVNAME "fb_ssd1351"
+#define WIDTH 128
+#define HEIGHT 128
+#define GAMMA_NUM 1
+#define GAMMA_LEN 63
+#define DEFAULT_GAMMA "0 2 2 2 2 2 2 2 " \
+ "2 2 2 2 2 2 2 2 " \
+ "2 2 2 2 2 2 2 2 " \
+ "2 2 2 2 2 2 2 2 " \
+ "2 2 2 2 2 2 2 2 " \
+ "2 2 2 2 2 2 2 2 " \
+ "2 2 2 2 2 2 2 2 " \
+ "2 2 2 2 2 2 2" \
+
+static void register_onboard_backlight(struct fbtft_par *par);
+
+static int init_display(struct fbtft_par *par)
+{
+ fbtft_par_dbg(DEBUG_INIT_DISPLAY, par, "%s()\n", __func__);
+
+ if (par->pdata
+ && par->pdata->display.backlight == FBTFT_ONBOARD_BACKLIGHT) {
+ /* module uses onboard GPIO for panel power */
+ par->fbtftops.register_backlight = register_onboard_backlight;
+ }
+
+ par->fbtftops.reset(par);
+
+ write_reg(par, 0xfd, 0x12); /* Command Lock */
+ write_reg(par, 0xfd, 0xb1); /* Command Lock */
+ write_reg(par, 0xae); /* Display Off */
+ write_reg(par, 0xb3, 0xf1); /* Front Clock Div */
+ write_reg(par, 0xca, 0x7f); /* Set Mux Ratio */
+ write_reg(par, 0x15, 0x00, 0x7f); /* Set Column Address */
+ write_reg(par, 0x75, 0x00, 0x7f); /* Set Row Address */
+ write_reg(par, 0xa1, 0x00); /* Set Display Start Line */
+ write_reg(par, 0xa2, 0x00); /* Set Display Offset */
+ write_reg(par, 0xb5, 0x00); /* Set GPIO */
+ write_reg(par, 0xab, 0x01); /* Set Function Selection */
+ write_reg(par, 0xb1, 0x32); /* Set Phase Length */
+ write_reg(par, 0xb4, 0xa0, 0xb5, 0x55); /* Set Segment Low Voltage */
+ write_reg(par, 0xbb, 0x17); /* Set Precharge Voltage */
+ write_reg(par, 0xbe, 0x05); /* Set VComH Voltage */
+ write_reg(par, 0xc1, 0xc8, 0x80, 0xc8); /* Set Contrast */
+ write_reg(par, 0xc7, 0x0f); /* Set Master Contrast */
+ write_reg(par, 0xb6, 0x01); /* Set Second Precharge Period */
+ write_reg(par, 0xa6); /* Set Display Mode Reset */
+ write_reg(par, 0xaf); /* Set Sleep Mode Display On */
+
+ return 0;
+}
+
+static void set_addr_win(struct fbtft_par *par, int xs, int ys, int xe, int ye)
+{
+ fbtft_par_dbg(DEBUG_SET_ADDR_WIN, par,
+ "%s(xs=%d, ys=%d, xe=%d, ye=%d)\n", __func__, xs, ys, xe, ye);
+
+ write_reg(par, 0x15, xs, xe);
+ write_reg(par, 0x75, ys, ye);
+ write_reg(par, 0x5c);
+}
+
+static int set_var(struct fbtft_par *par)
+{
+ unsigned remap;
+
+ fbtft_par_dbg(DEBUG_INIT_DISPLAY, par, "%s()\n", __func__);
+
+ if (par->fbtftops.init_display != init_display) {
+ /* don't risk messing up register A0h */
+ fbtft_par_dbg(DEBUG_INIT_DISPLAY, par,
+ "%s: skipping since custom init_display() is used\n",
+ __func__);
+ return 0;
+ }
+
+ remap = 0x60 | (par->bgr << 2); /* Set Colour Depth */
+
+ switch (par->info->var.rotate) {
+ case 0:
+ write_reg(par, 0xA0, remap | 0x00 | 1<<4);
+ break;
+ case 270:
+ write_reg(par, 0xA0, remap | 0x03 | 1<<4);
+ break;
+ case 180:
+ write_reg(par, 0xA0, remap | 0x02);
+ break;
+ case 90:
+ write_reg(par, 0xA0, remap | 0x01);
+ break;
+ }
+
+ return 0;
+}
+
+/*
+ Grayscale Lookup Table
+ GS1 - GS63
+ The driver Gamma curve contains the relative values between the entries
+ in the Lookup table.
+
+ From datasheet:
+ 8.8 Gray Scale Decoder
+
+ there are total 180 Gamma Settings (Setting 0 to Setting 180)
+ available for the Gray Scale table.
+
+ The gray scale is defined in incremental way, with reference
+ to the length of previous table entry:
+ Setting of GS1 has to be >= 0
+ Setting of GS2 has to be > Setting of GS1 +1
+ Setting of GS3 has to be > Setting of GS2 +1
+ :
+ Setting of GS63 has to be > Setting of GS62 +1
+
+
+*/
+static int set_gamma(struct fbtft_par *par, unsigned long *curves)
+{
+ unsigned long tmp[GAMMA_NUM * GAMMA_LEN];
+ int i, acc = 0;
+
+ fbtft_par_dbg(DEBUG_INIT_DISPLAY, par, "%s()\n", __func__);
+
+ for (i = 0; i < 63; i++) {
+ if (i > 0 && curves[i] < 2) {
+ dev_err(par->info->device,
+ "Illegal value in Grayscale Lookup Table at index %d. " \
+ "Must be greater than 1\n", i);
+ return -EINVAL;
+ }
+ acc += curves[i];
+ tmp[i] = acc;
+ if (acc > 180) {
+ dev_err(par->info->device,
+ "Illegal value(s) in Grayscale Lookup Table. " \
+ "At index=%d, the accumulated value has exceeded 180\n", i);
+ return -EINVAL;
+ }
+ }
+
+ write_reg(par, 0xB8,
+ tmp[0], tmp[1], tmp[2], tmp[3], tmp[4], tmp[5], tmp[6], tmp[7],
+ tmp[8], tmp[9], tmp[10], tmp[11], tmp[12], tmp[13], tmp[14], tmp[15],
+ tmp[16], tmp[17], tmp[18], tmp[19], tmp[20], tmp[21], tmp[22], tmp[23],
+ tmp[24], tmp[25], tmp[26], tmp[27], tmp[28], tmp[29], tmp[30], tmp[31],
+ tmp[32], tmp[33], tmp[34], tmp[35], tmp[36], tmp[37], tmp[38], tmp[39],
+ tmp[40], tmp[41], tmp[42], tmp[43], tmp[44], tmp[45], tmp[46], tmp[47],
+ tmp[48], tmp[49], tmp[50], tmp[51], tmp[52], tmp[53], tmp[54], tmp[55],
+ tmp[56], tmp[57], tmp[58], tmp[59], tmp[60], tmp[61], tmp[62]);
+
+ return 0;
+}
+
+static int blank(struct fbtft_par *par, bool on)
+{
+ fbtft_par_dbg(DEBUG_BLANK, par, "%s(blank=%s)\n",
+ __func__, on ? "true" : "false");
+ if (on)
+ write_reg(par, 0xAE);
+ else
+ write_reg(par, 0xAF);
+ return 0;
+}
+
+
+static struct fbtft_display display = {
+ .regwidth = 8,
+ .width = WIDTH,
+ .height = HEIGHT,
+ .gamma_num = GAMMA_NUM,
+ .gamma_len = GAMMA_LEN,
+ .gamma = DEFAULT_GAMMA,
+ .fbtftops = {
+ .init_display = init_display,
+ .set_addr_win = set_addr_win,
+ .set_var = set_var,
+ .set_gamma = set_gamma,
+ .blank = blank,
+ },
+};
+
+#ifdef CONFIG_FB_BACKLIGHT
+static int update_onboard_backlight(struct backlight_device *bd)
+{
+ struct fbtft_par *par = bl_get_data(bd);
+ bool on;
+
+ fbtft_par_dbg(DEBUG_BACKLIGHT, par,
+ "%s: power=%d, fb_blank=%d\n",
+ __func__, bd->props.power, bd->props.fb_blank);
+
+ on = (bd->props.power == FB_BLANK_UNBLANK)
+ && (bd->props.fb_blank == FB_BLANK_UNBLANK);
+ /* Onboard backlight connected to GPIO0 on SSD1351, GPIO1 unused */
+ write_reg(par, 0xB5, on ? 0x03 : 0x02);
+
+ return 0;
+}
+
+static void register_onboard_backlight(struct fbtft_par *par)
+{
+ struct backlight_device *bd;
+ struct backlight_properties bl_props = { 0, };
+ struct backlight_ops *bl_ops;
+
+ fbtft_par_dbg(DEBUG_BACKLIGHT, par, "%s()\n", __func__);
+
+ bl_ops = devm_kzalloc(par->info->device, sizeof(struct backlight_ops),
+ GFP_KERNEL);
+ if (!bl_ops)
+ return;
+
+ bl_ops->update_status = update_onboard_backlight;
+ bl_props.type = BACKLIGHT_RAW;
+ bl_props.power = FB_BLANK_POWERDOWN;
+
+ bd = backlight_device_register(dev_driver_string(par->info->device),
+ par->info->device, par, bl_ops, &bl_props);
+ if (IS_ERR(bd)) {
+ dev_err(par->info->device,
+ "cannot register backlight device (%ld)\n",
+ PTR_ERR(bd));
+ return;
+ }
+ par->info->bl_dev = bd;
+
+ if (!par->fbtftops.unregister_backlight)
+ par->fbtftops.unregister_backlight = fbtft_unregister_backlight;
+}
+#else
+static void register_onboard_backlight(struct fbtft_par *par) { };
+#endif
+
+
+FBTFT_REGISTER_DRIVER(DRVNAME, "solomon,ssd1351", &display);
+
+MODULE_ALIAS("spi:" DRVNAME);
+MODULE_ALIAS("platform:" DRVNAME);
+MODULE_ALIAS("spi:ssd1351");
+MODULE_ALIAS("platform:ssd1351");
+
+MODULE_DESCRIPTION("SSD1351 OLED Driver");
+MODULE_AUTHOR("James Davies");
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/fbtft/fb_st7735r.c b/drivers/staging/fbtft/fb_st7735r.c
new file mode 100644
index 000000000..9d8743084
--- /dev/null
+++ b/drivers/staging/fbtft/fb_st7735r.c
@@ -0,0 +1,195 @@
+/*
+ * FB driver for the ST7735R LCD Controller
+ *
+ * Copyright (C) 2013 Noralf Tronnes
+ *
+ * This program is free software; you can redistribute it and/or 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/kernel.h>
+#include <linux/init.h>
+
+#include "fbtft.h"
+
+#define DRVNAME "fb_st7735r"
+#define DEFAULT_GAMMA "0F 1A 0F 18 2F 28 20 22 1F 1B 23 37 00 07 02 10\n" \
+ "0F 1B 0F 17 33 2C 29 2E 30 30 39 3F 00 07 03 10"
+
+
+static int default_init_sequence[] = {
+ /* SWRESET - Software reset */
+ -1, 0x01,
+ -2, 150, /* delay */
+
+ /* SLPOUT - Sleep out & booster on */
+ -1, 0x11,
+ -2, 500, /* delay */
+
+ /* FRMCTR1 - frame rate control: normal mode
+ frame rate = fosc / (1 x 2 + 40) * (LINE + 2C + 2D) */
+ -1, 0xB1, 0x01, 0x2C, 0x2D,
+
+ /* FRMCTR2 - frame rate control: idle mode
+ frame rate = fosc / (1 x 2 + 40) * (LINE + 2C + 2D) */
+ -1, 0xB2, 0x01, 0x2C, 0x2D,
+
+ /* FRMCTR3 - frame rate control - partial mode
+ dot inversion mode, line inversion mode */
+ -1, 0xB3, 0x01, 0x2C, 0x2D, 0x01, 0x2C, 0x2D,
+
+ /* INVCTR - display inversion control
+ no inversion */
+ -1, 0xB4, 0x07,
+
+ /* PWCTR1 - Power Control
+ -4.6V, AUTO mode */
+ -1, 0xC0, 0xA2, 0x02, 0x84,
+
+ /* PWCTR2 - Power Control
+ VGH25 = 2.4C VGSEL = -10 VGH = 3 * AVDD */
+ -1, 0xC1, 0xC5,
+
+ /* PWCTR3 - Power Control
+ Opamp current small, Boost frequency */
+ -1, 0xC2, 0x0A, 0x00,
+
+ /* PWCTR4 - Power Control
+ BCLK/2, Opamp current small & Medium low */
+ -1, 0xC3, 0x8A, 0x2A,
+
+ /* PWCTR5 - Power Control */
+ -1, 0xC4, 0x8A, 0xEE,
+
+ /* VMCTR1 - Power Control */
+ -1, 0xC5, 0x0E,
+
+ /* INVOFF - Display inversion off */
+ -1, 0x20,
+
+ /* COLMOD - Interface pixel format */
+ -1, 0x3A, 0x05,
+
+ /* DISPON - Display On */
+ -1, 0x29,
+ -2, 100, /* delay */
+
+ /* NORON - Partial off (Normal) */
+ -1, 0x13,
+ -2, 10, /* delay */
+
+ /* end marker */
+ -3
+};
+
+static void set_addr_win(struct fbtft_par *par, int xs, int ys, int xe, int ye)
+{
+ fbtft_par_dbg(DEBUG_SET_ADDR_WIN, par,
+ "%s(xs=%d, ys=%d, xe=%d, ye=%d)\n", __func__, xs, ys, xe, ye);
+
+ /* Column address */
+ write_reg(par, 0x2A, xs >> 8, xs & 0xFF, xe >> 8, xe & 0xFF);
+
+ /* Row address */
+ write_reg(par, 0x2B, ys >> 8, ys & 0xFF, ye >> 8, ye & 0xFF);
+
+ /* Memory write */
+ write_reg(par, 0x2C);
+}
+
+#define MY (1 << 7)
+#define MX (1 << 6)
+#define MV (1 << 5)
+static int set_var(struct fbtft_par *par)
+{
+ fbtft_par_dbg(DEBUG_INIT_DISPLAY, par, "%s()\n", __func__);
+
+ /* MADCTL - Memory data access control
+ RGB/BGR:
+ 1. Mode selection pin SRGB
+ RGB H/W pin for color filter setting: 0=RGB, 1=BGR
+ 2. MADCTL RGB bit
+ RGB-BGR ORDER color filter panel: 0=RGB, 1=BGR */
+ switch (par->info->var.rotate) {
+ case 0:
+ write_reg(par, 0x36, MX | MY | (par->bgr << 3));
+ break;
+ case 270:
+ write_reg(par, 0x36, MY | MV | (par->bgr << 3));
+ break;
+ case 180:
+ write_reg(par, 0x36, par->bgr << 3);
+ break;
+ case 90:
+ write_reg(par, 0x36, MX | MV | (par->bgr << 3));
+ break;
+ }
+
+ return 0;
+}
+
+/*
+ Gamma string format:
+ VRF0P VOS0P PK0P PK1P PK2P PK3P PK4P PK5P PK6P PK7P PK8P PK9P SELV0P SELV1P SELV62P SELV63P
+ VRF0N VOS0N PK0N PK1N PK2N PK3N PK4N PK5N PK6N PK7N PK8N PK9N SELV0N SELV1N SELV62N SELV63N
+*/
+#define CURVE(num, idx) curves[num*par->gamma.num_values + idx]
+static int set_gamma(struct fbtft_par *par, unsigned long *curves)
+{
+ int i, j;
+
+ fbtft_par_dbg(DEBUG_INIT_DISPLAY, par, "%s()\n", __func__);
+
+ /* apply mask */
+ for (i = 0; i < par->gamma.num_curves; i++)
+ for (j = 0; j < par->gamma.num_values; j++)
+ CURVE(i, j) &= 0x3f;
+
+ for (i = 0; i < par->gamma.num_curves; i++)
+ write_reg(par, 0xE0 + i,
+ CURVE(i, 0), CURVE(i, 1), CURVE(i, 2), CURVE(i, 3),
+ CURVE(i, 4), CURVE(i, 5), CURVE(i, 6), CURVE(i, 7),
+ CURVE(i, 8), CURVE(i, 9), CURVE(i, 10), CURVE(i, 11),
+ CURVE(i, 12), CURVE(i, 13), CURVE(i, 14), CURVE(i, 15));
+
+ return 0;
+}
+#undef CURVE
+
+
+static struct fbtft_display display = {
+ .regwidth = 8,
+ .width = 128,
+ .height = 160,
+ .init_sequence = default_init_sequence,
+ .gamma_num = 2,
+ .gamma_len = 16,
+ .gamma = DEFAULT_GAMMA,
+ .fbtftops = {
+ .set_addr_win = set_addr_win,
+ .set_var = set_var,
+ .set_gamma = set_gamma,
+ },
+};
+FBTFT_REGISTER_DRIVER(DRVNAME, "sitronix,st7735r", &display);
+
+MODULE_ALIAS("spi:" DRVNAME);
+MODULE_ALIAS("platform:" DRVNAME);
+MODULE_ALIAS("spi:st7735r");
+MODULE_ALIAS("platform:st7735r");
+
+MODULE_DESCRIPTION("FB driver for the ST7735R LCD Controller");
+MODULE_AUTHOR("Noralf Tronnes");
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/fbtft/fb_tinylcd.c b/drivers/staging/fbtft/fb_tinylcd.c
new file mode 100644
index 000000000..c0b1a337f
--- /dev/null
+++ b/drivers/staging/fbtft/fb_tinylcd.c
@@ -0,0 +1,124 @@
+/*
+ * Custom FB driver for tinylcd.com display
+ *
+ * Copyright (C) 2013 Noralf Tronnes
+ *
+ * This program is free software; you can redistribute it and/or 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/kernel.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+
+#include "fbtft.h"
+
+#define DRVNAME "fb_tinylcd"
+#define WIDTH 320
+#define HEIGHT 480
+
+
+static int init_display(struct fbtft_par *par)
+{
+ fbtft_par_dbg(DEBUG_INIT_DISPLAY, par, "%s()\n", __func__);
+
+ par->fbtftops.reset(par);
+
+ write_reg(par, 0xB0, 0x80);
+ write_reg(par, 0xC0, 0x0A, 0x0A);
+ write_reg(par, 0xC1, 0x45, 0x07);
+ write_reg(par, 0xC2, 0x33);
+ write_reg(par, 0xC5, 0x00, 0x42, 0x80);
+ write_reg(par, 0xB1, 0xD0, 0x11);
+ write_reg(par, 0xB4, 0x02);
+ write_reg(par, 0xB6, 0x00, 0x22, 0x3B);
+ write_reg(par, 0xB7, 0x07);
+ write_reg(par, 0x36, 0x58);
+ write_reg(par, 0xF0, 0x36, 0xA5, 0xD3);
+ write_reg(par, 0xE5, 0x80);
+ write_reg(par, 0xE5, 0x01);
+ write_reg(par, 0xB3, 0x00);
+ write_reg(par, 0xE5, 0x00);
+ write_reg(par, 0xF0, 0x36, 0xA5, 0x53);
+ write_reg(par, 0xE0, 0x00, 0x35, 0x33, 0x00, 0x00, 0x00,
+ 0x00, 0x35, 0x33, 0x00, 0x00, 0x00);
+ write_reg(par, 0x3A, 0x55);
+ write_reg(par, 0x11);
+ udelay(250);
+ write_reg(par, 0x29);
+
+ return 0;
+}
+
+static void set_addr_win(struct fbtft_par *par, int xs, int ys, int xe, int ye)
+{
+ fbtft_par_dbg(DEBUG_SET_ADDR_WIN, par,
+ "%s(xs=%d, ys=%d, xe=%d, ye=%d)\n", __func__, xs, ys, xe, ye);
+
+ /* Column address */
+ write_reg(par, 0x2A, xs >> 8, xs & 0xFF, xe >> 8, xe & 0xFF);
+
+ /* Row address */
+ write_reg(par, 0x2B, ys >> 8, ys & 0xFF, ye >> 8, ye & 0xFF);
+
+ /* Memory write */
+ write_reg(par, 0x2C);
+}
+
+static int set_var(struct fbtft_par *par)
+{
+ fbtft_par_dbg(DEBUG_INIT_DISPLAY, par, "%s()\n", __func__);
+
+ switch (par->info->var.rotate) {
+ case 270:
+ write_reg(par, 0xB6, 0x00, 0x02, 0x3B);
+ write_reg(par, 0x36, 0x28);
+ break;
+ case 180:
+ write_reg(par, 0xB6, 0x00, 0x22, 0x3B);
+ write_reg(par, 0x36, 0x58);
+ break;
+ case 90:
+ write_reg(par, 0xB6, 0x00, 0x22, 0x3B);
+ write_reg(par, 0x36, 0x38);
+ break;
+ default:
+ write_reg(par, 0xB6, 0x00, 0x22, 0x3B);
+ write_reg(par, 0x36, 0x08);
+ break;
+ }
+
+ return 0;
+}
+
+
+static struct fbtft_display display = {
+ .regwidth = 8,
+ .width = WIDTH,
+ .height = HEIGHT,
+ .fbtftops = {
+ .init_display = init_display,
+ .set_addr_win = set_addr_win,
+ .set_var = set_var,
+ },
+};
+FBTFT_REGISTER_DRIVER(DRVNAME, "neosec,tinylcd", &display);
+
+MODULE_ALIAS("spi:" DRVNAME);
+MODULE_ALIAS("spi:tinylcd");
+
+MODULE_DESCRIPTION("Custom FB driver for tinylcd.com display");
+MODULE_AUTHOR("Noralf Tronnes");
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/fbtft/fb_tls8204.c b/drivers/staging/fbtft/fb_tls8204.c
new file mode 100644
index 000000000..fcd38bf2e
--- /dev/null
+++ b/drivers/staging/fbtft/fb_tls8204.c
@@ -0,0 +1,176 @@
+/*
+ * FB driver for the TLS8204 LCD Controller
+ *
+ * The display is monochrome and the video memory is RGB565.
+ * Any pixel value except 0 turns the pixel on.
+ *
+ * Copyright (C) 2013 Noralf Tronnes
+ * Copyright (C) 2014 Michael Hope (adapted for the TLS8204)
+ *
+ * This program is free software; you can redistribute it and/or 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/kernel.h>
+#include <linux/init.h>
+#include <linux/gpio.h>
+#include <linux/spi/spi.h>
+#include <linux/delay.h>
+
+#include "fbtft.h"
+
+#define DRVNAME "fb_tls8204"
+#define WIDTH 84
+#define HEIGHT 48
+#define TXBUFLEN WIDTH
+#define DEFAULT_GAMMA "40" /* gamma is used to control contrast in this driver */
+
+static unsigned bs = 4;
+module_param(bs, uint, 0);
+MODULE_PARM_DESC(bs, "BS[2:0] Bias voltage level: 0-7 (default: 4)");
+
+static int init_display(struct fbtft_par *par)
+{
+ fbtft_par_dbg(DEBUG_INIT_DISPLAY, par, "%s()\n", __func__);
+
+ par->fbtftops.reset(par);
+
+ /* Enter extended command mode */
+ write_reg(par, 0x21); /* 5:1 1
+ 2:0 PD - Powerdown control: chip is active
+ 1:0 V - Entry mode: horizontal addressing
+ 0:1 H - Extended instruction set control: extended
+ */
+
+ /* H=1 Bias system */
+ write_reg(par, 0x10 | (bs & 0x7)); /*
+ 4:1 1
+ 3:0 0
+ 2:x BS2 - Bias System
+ 1:x BS1
+ 0:x BS0
+ */
+
+ /* Set the address of the first display line. */
+ write_reg(par, 0x04 | (64 >> 6));
+ write_reg(par, 0x40 | (64 & 0x3F));
+
+ /* Enter H=0 standard command mode */
+ write_reg(par, 0x20);
+
+ /* H=0 Display control */
+ write_reg(par, 0x08 | 4); /*
+ 3:1 1
+ 2:1 D - DE: 10=normal mode
+ 1:0 0
+ 0:0 E
+ */
+
+ return 0;
+}
+
+static void set_addr_win(struct fbtft_par *par, int xs, int ys, int xe, int ye)
+{
+ fbtft_par_dbg(DEBUG_SET_ADDR_WIN, par, "%s(xs=%d, ys=%d, xe=%d, ye=%d)\n", __func__, xs, ys, xe, ye);
+
+ /* H=0 Set X address of RAM */
+ write_reg(par, 0x80); /* 7:1 1
+ 6-0: X[6:0] - 0x00
+ */
+
+ /* H=0 Set Y address of RAM */
+ write_reg(par, 0x40); /* 7:0 0
+ 6:1 1
+ 2-0: Y[2:0] - 0x0
+ */
+}
+
+static int write_vmem(struct fbtft_par *par, size_t offset, size_t len)
+{
+ u16 *vmem16 = (u16 *)par->info->screen_base;
+ int x, y, i;
+ int ret = 0;
+
+ fbtft_par_dbg(DEBUG_WRITE_VMEM, par, "%s()\n", __func__);
+
+ for (y = 0; y < HEIGHT/8; y++) {
+ u8 *buf = par->txbuf.buf;
+ /* The display is 102x68 but the LCD is 84x48. Set
+ the write pointer at the start of each row. */
+ gpio_set_value(par->gpio.dc, 0);
+ write_reg(par, 0x80 | 0);
+ write_reg(par, 0x40 | y);
+
+ for (x = 0; x < WIDTH; x++) {
+ u8 ch = 0;
+ for (i = 0; i < 8*WIDTH; i += WIDTH) {
+ ch >>= 1;
+ if (vmem16[(y*8*WIDTH)+i+x])
+ ch |= 0x80;
+ }
+ *buf++ = ch;
+ }
+ /* Write the row */
+ gpio_set_value(par->gpio.dc, 1);
+ ret = par->fbtftops.write(par, par->txbuf.buf, WIDTH);
+ if (ret < 0) {
+ dev_err(par->info->device,
+ "write failed and returned: %d\n", ret);
+ break;
+ }
+ }
+
+ return ret;
+}
+
+static int set_gamma(struct fbtft_par *par, unsigned long *curves)
+{
+ fbtft_par_dbg(DEBUG_INIT_DISPLAY, par, "%s()\n", __func__);
+
+ /* apply mask */
+ curves[0] &= 0x7F;
+
+ write_reg(par, 0x21); /* turn on extended instruction set */
+ write_reg(par, 0x80 | curves[0]);
+ write_reg(par, 0x20); /* turn off extended instruction set */
+
+ return 0;
+}
+
+
+static struct fbtft_display display = {
+ .regwidth = 8,
+ .width = WIDTH,
+ .height = HEIGHT,
+ .txbuflen = TXBUFLEN,
+ .gamma_num = 1,
+ .gamma_len = 1,
+ .gamma = DEFAULT_GAMMA,
+ .fbtftops = {
+ .init_display = init_display,
+ .set_addr_win = set_addr_win,
+ .write_vmem = write_vmem,
+ .set_gamma = set_gamma,
+ },
+ .backlight = 1,
+};
+FBTFT_REGISTER_DRIVER(DRVNAME, "teralane,tls8204", &display);
+
+MODULE_ALIAS("spi:" DRVNAME);
+MODULE_ALIAS("spi:tls8204");
+
+MODULE_DESCRIPTION("FB driver for the TLS8204 LCD Controller");
+MODULE_AUTHOR("Michael Hope");
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/fbtft/fb_uc1701.c b/drivers/staging/fbtft/fb_uc1701.c
new file mode 100644
index 000000000..26d669b57
--- /dev/null
+++ b/drivers/staging/fbtft/fb_uc1701.c
@@ -0,0 +1,211 @@
+/*
+ * FB driver for the UC1701 LCD Controller
+ *
+ * The display is monochrome and the video memory is RGB565.
+ * Any pixel value except 0 turns the pixel on.
+ *
+ * Copyright (C) 2014 Juergen Holzmann
+ *
+ * This program is free software; you can redistribute it and/or 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/kernel.h>
+#include <linux/init.h>
+#include <linux/gpio.h>
+#include <linux/spi/spi.h>
+#include <linux/delay.h>
+
+#include "fbtft.h"
+
+#define DRVNAME "fb_uc1701"
+#define WIDTH 102
+#define HEIGHT 64
+#define PAGES (HEIGHT/8)
+
+/* 1: Display on/off */
+#define LCD_DISPLAY_ENABLE 0xAE
+/* 2: display start line set */
+#define LCD_START_LINE 0x40
+/* 3: Page address set (lower 4 bits select one of the pages) */
+#define LCD_PAGE_ADDRESS 0xB0
+/* 4: column address */
+#define LCD_COL_ADDRESS 0x10
+/* 8: select orientation */
+#define LCD_BOTTOMVIEW 0xA0
+/* 9: inverted display */
+#define LCD_DISPLAY_INVERT 0xA6
+/* 10: show memory content or switch all pixels on */
+#define LCD_ALL_PIXEL 0xA4
+/* 11: lcd bias set */
+#define LCD_BIAS 0xA2
+/* 14: Reset Controller */
+#define LCD_RESET_CMD 0xE2
+/* 15: output mode select (turns display upside-down) */
+#define LCD_SCAN_DIR 0xC0
+/* 16: power control set */
+#define LCD_POWER_CONTROL 0x28
+/* 17: voltage regulator resistor ratio set */
+#define LCD_VOLTAGE 0x20
+/* 18: Volume mode set */
+#define LCD_VOLUME_MODE 0x81
+/* 22: NOP command */
+#define LCD_NO_OP 0xE3
+/* 25: advanced program control */
+#define LCD_ADV_PROG_CTRL 0xFA
+/* 25: advanced program control2 */
+#define LCD_ADV_PROG_CTRL2 0x10
+#define LCD_TEMPCOMP_HIGH 0x80
+/* column offset for normal orientation */
+#define SHIFT_ADDR_NORMAL 0
+/* column offset for bottom view orientation */
+#define SHIFT_ADDR_TOPVIEW 30
+
+
+static int init_display(struct fbtft_par *par)
+{
+ fbtft_par_dbg(DEBUG_INIT_DISPLAY, par, "%s()\n", __func__);
+
+ par->fbtftops.reset(par);
+
+ /* softreset of LCD */
+ write_reg(par, LCD_RESET_CMD);
+ mdelay(10);
+
+ /* set startpoint */
+ /* LCD_START_LINE | (pos & 0x3F) */
+ write_reg(par, LCD_START_LINE);
+
+ /* select orientation BOTTOMVIEW */
+ write_reg(par, LCD_BOTTOMVIEW | 1);
+ /* output mode select (turns display upside-down) */
+ write_reg(par, LCD_SCAN_DIR | 0x00);
+
+ /* Normal Pixel mode */
+ write_reg(par, LCD_ALL_PIXEL | 0);
+
+ /* positive display */
+ write_reg(par, LCD_DISPLAY_INVERT | 0);
+
+ /* bias 1/9 */
+ write_reg(par, LCD_BIAS | 0);
+
+ /* power control mode: all features on */
+ /* LCD_POWER_CONTROL | (val&0x07) */
+ write_reg(par, LCD_POWER_CONTROL | 0x07);
+
+ /* set voltage regulator R/R */
+ /* LCD_VOLTAGE | (val&0x07) */
+ write_reg(par, LCD_VOLTAGE | 0x07);
+
+ /* volume mode set */
+ /* LCD_VOLUME_MODE,val&0x3f,LCD_NO_OP */
+ write_reg(par, LCD_VOLUME_MODE);
+ /* LCD_VOLUME_MODE,val&0x3f,LCD_NO_OP */
+ write_reg(par, 0x09);
+ /* ???? */
+ /* LCD_VOLUME_MODE,val&0x3f,LCD_NO_OP */
+ write_reg(par, LCD_NO_OP);
+
+ /* advanced program control */
+ write_reg(par, LCD_ADV_PROG_CTRL);
+ write_reg(par, LCD_ADV_PROG_CTRL2|LCD_TEMPCOMP_HIGH);
+
+ /* enable display */
+ write_reg(par, LCD_DISPLAY_ENABLE | 1);
+
+ return 0;
+}
+
+static void set_addr_win(struct fbtft_par *par, int xs, int ys, int xe, int ye)
+{
+ fbtft_par_dbg(DEBUG_SET_ADDR_WIN, par, "%s(xs=%d, ys=%d, xe=%d, ye=%d)\n", __func__, xs, ys, xe, ye);
+
+ /* goto address */
+ /* LCD_PAGE_ADDRESS | ((page) & 0x1F),
+ (((col)+SHIFT_ADDR_NORMAL) & 0x0F),
+ LCD_COL_ADDRESS | ((((col)+SHIFT_ADDR_NORMAL)>>4) & 0x0F) */
+ write_reg(par, LCD_PAGE_ADDRESS);
+ /* LCD_PAGE_ADDRESS | ((page) & 0x1F),
+ (((col)+SHIFT_ADDR_NORMAL) & 0x0F),
+ LCD_COL_ADDRESS | ((((col)+SHIFT_ADDR_NORMAL)>>4) & 0x0F) */
+ write_reg(par, 0x00);
+ /* LCD_PAGE_ADDRESS | ((page) & 0x1F),
+ (((col)+SHIFT_ADDR_NORMAL) & 0x0F),
+ LCD_COL_ADDRESS | ((((col)+SHIFT_ADDR_NORMAL)>>4) & 0x0F) */
+ write_reg(par, LCD_COL_ADDRESS);
+}
+
+static int write_vmem(struct fbtft_par *par, size_t offset, size_t len)
+{
+ u16 *vmem16 = (u16 *)par->info->screen_base;
+ u8 *buf = par->txbuf.buf;
+ int x, y, i;
+ int ret = 0;
+
+ fbtft_par_dbg(DEBUG_WRITE_VMEM, par, "%s()\n", __func__);
+
+ for (y = 0; y < PAGES; y++) {
+ buf = par->txbuf.buf;
+ for (x = 0; x < WIDTH; x++) {
+ *buf = 0x00;
+ for (i = 0; i < 8; i++)
+ *buf |= (vmem16[((y*8*WIDTH)+(i*WIDTH))+x] ? 1 : 0) << i;
+ buf++;
+ }
+ /* LCD_PAGE_ADDRESS | ((page) & 0x1F),
+ (((col)+SHIFT_ADDR_NORMAL) & 0x0F),
+ LCD_COL_ADDRESS | ((((col)+SHIFT_ADDR_NORMAL)>>4) & 0x0F) */
+ write_reg(par, LCD_PAGE_ADDRESS|(u8)y);
+ /* LCD_PAGE_ADDRESS | ((page) & 0x1F),
+ (((col)+SHIFT_ADDR_NORMAL) & 0x0F),
+ LCD_COL_ADDRESS | ((((col)+SHIFT_ADDR_NORMAL)>>4) & 0x0F) */
+ write_reg(par, 0x00);
+ /* LCD_PAGE_ADDRESS | ((page) & 0x1F),
+ (((col)+SHIFT_ADDR_NORMAL) & 0x0F),
+ LCD_COL_ADDRESS | ((((col)+SHIFT_ADDR_NORMAL)>>4) & 0x0F) */
+ write_reg(par, LCD_COL_ADDRESS);
+ gpio_set_value(par->gpio.dc, 1);
+ ret = par->fbtftops.write(par, par->txbuf.buf, WIDTH);
+ gpio_set_value(par->gpio.dc, 0);
+ }
+
+ if (ret < 0)
+ dev_err(par->info->device, "write failed and returned: %d\n",
+ ret);
+
+ return ret;
+}
+
+
+static struct fbtft_display display = {
+ .regwidth = 8,
+ .width = WIDTH,
+ .height = HEIGHT,
+ .fbtftops = {
+ .init_display = init_display,
+ .set_addr_win = set_addr_win,
+ .write_vmem = write_vmem,
+ },
+ .backlight = 1,
+};
+FBTFT_REGISTER_DRIVER(DRVNAME, "UltraChip,uc1701", &display);
+
+MODULE_ALIAS("spi:" DRVNAME);
+MODULE_ALIAS("spi:uc1701");
+
+MODULE_DESCRIPTION("FB driver for the UC1701 LCD Controller");
+MODULE_AUTHOR("Juergen Holzmann");
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/fbtft/fb_upd161704.c b/drivers/staging/fbtft/fb_upd161704.c
new file mode 100644
index 000000000..176c2106d
--- /dev/null
+++ b/drivers/staging/fbtft/fb_upd161704.c
@@ -0,0 +1,206 @@
+/*
+ * FB driver for the uPD161704 LCD Controller
+ *
+ * Copyright (C) 2014 Seong-Woo Kim
+ *
+ * Based on fb_ili9325.c by Noralf Tronnes
+ * Based on ili9325.c by Jeroen Domburg
+ * Init code from UTFT library by Henning Karlsen
+ *
+ * This program is free software; you can redistribute it and/or 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/kernel.h>
+#include <linux/init.h>
+#include <linux/gpio.h>
+#include <linux/delay.h>
+
+#include "fbtft.h"
+
+#define DRVNAME "fb_upd161704"
+#define WIDTH 240
+#define HEIGHT 320
+#define BPP 16
+
+static int init_display(struct fbtft_par *par)
+{
+ fbtft_par_dbg(DEBUG_INIT_DISPLAY, par, "%s()\n", __func__);
+
+ par->fbtftops.reset(par);
+
+ if (par->gpio.cs != -1)
+ gpio_set_value(par->gpio.cs, 0); /* Activate chip */
+
+ /* Initialization sequence from Lib_UTFT */
+
+ /* register reset */
+ write_reg(par, 0x0003, 0x0001); /* Soft reset */
+
+ /* oscillator start */
+ write_reg(par, 0x003A, 0x0001); /*Oscillator 0: stop, 1: operation */
+ udelay(100);
+
+ /* y-setting */
+ write_reg(par, 0x0024, 0x007B); /* amplitude setting */
+ udelay(10);
+ write_reg(par, 0x0025, 0x003B); /* amplitude setting */
+ write_reg(par, 0x0026, 0x0034); /* amplitude setting */
+ udelay(10);
+ write_reg(par, 0x0027, 0x0004); /* amplitude setting */
+ write_reg(par, 0x0052, 0x0025); /* circuit setting 1 */
+ udelay(10);
+ write_reg(par, 0x0053, 0x0033); /* circuit setting 2 */
+ write_reg(par, 0x0061, 0x001C); /* adjustment V10 positive polarity */
+ udelay(10);
+ write_reg(par, 0x0062, 0x002C); /* adjustment V9 negative polarity */
+ write_reg(par, 0x0063, 0x0022); /* adjustment V34 positive polarity */
+ udelay(10);
+ write_reg(par, 0x0064, 0x0027); /* adjustment V31 negative polarity */
+ udelay(10);
+ write_reg(par, 0x0065, 0x0014); /* adjustment V61 negative polarity */
+ udelay(10);
+ write_reg(par, 0x0066, 0x0010); /* adjustment V61 negative polarity */
+
+ /* Basical clock for 1 line (BASECOUNT[7:0]) number specified */
+ write_reg(par, 0x002E, 0x002D);
+
+ /* Power supply setting */
+ write_reg(par, 0x0019, 0x0000); /* DC/DC output setting */
+ udelay(200);
+ write_reg(par, 0x001A, 0x1000); /* DC/DC frequency setting */
+ write_reg(par, 0x001B, 0x0023); /* DC/DC rising setting */
+ write_reg(par, 0x001C, 0x0C01); /* Regulator voltage setting */
+ write_reg(par, 0x001D, 0x0000); /* Regulator current setting */
+ write_reg(par, 0x001E, 0x0009); /* VCOM output setting */
+ write_reg(par, 0x001F, 0x0035); /* VCOM amplitude setting */
+ write_reg(par, 0x0020, 0x0015); /* VCOMM cencter setting */
+ write_reg(par, 0x0018, 0x1E7B); /* DC/DC operation setting */
+
+ /* windows setting */
+ write_reg(par, 0x0008, 0x0000); /* Minimum X address */
+ write_reg(par, 0x0009, 0x00EF); /* Maximum X address */
+ write_reg(par, 0x000a, 0x0000); /* Minimum Y address */
+ write_reg(par, 0x000b, 0x013F); /* Maximum Y address */
+
+ /* LCD display area setting */
+ write_reg(par, 0x0029, 0x0000); /* [LCDSIZE] X MIN. size set */
+ write_reg(par, 0x002A, 0x0000); /* [LCDSIZE] Y MIN. size set */
+ write_reg(par, 0x002B, 0x00EF); /* [LCDSIZE] X MAX. size set */
+ write_reg(par, 0x002C, 0x013F); /* [LCDSIZE] Y MAX. size set */
+
+ /* Gate scan setting */
+ write_reg(par, 0x0032, 0x0002);
+
+ /* n line inversion line number */
+ write_reg(par, 0x0033, 0x0000);
+
+ /* Line inversion/frame inversion/interlace setting */
+ write_reg(par, 0x0037, 0x0000);
+
+ /* Gate scan operation setting register */
+ write_reg(par, 0x003B, 0x0001);
+
+ /* Color mode */
+ /*GS = 0: 260-k color (64 gray scale), GS = 1: 8 color (2 gray scale) */
+ write_reg(par, 0x0004, 0x0000);
+
+ /* RAM control register */
+ write_reg(par, 0x0005, 0x0000); /*Window access 00:Normal, 10:Window */
+
+ /* Display setting register 2 */
+ write_reg(par, 0x0001, 0x0000);
+
+ /* display setting */
+ write_reg(par, 0x0000, 0x0000); /* display on */
+
+ return 0;
+}
+
+static void set_addr_win(struct fbtft_par *par, int xs, int ys, int xe, int ye)
+{
+ fbtft_par_dbg(DEBUG_SET_ADDR_WIN, par,
+ "%s(xs=%d, ys=%d, xe=%d, ye=%d)\n", __func__, xs, ys, xe, ye);
+ switch (par->info->var.rotate) {
+ /* R20h = Horizontal GRAM Start Address */
+ /* R21h = Vertical GRAM Start Address */
+ case 0:
+ write_reg(par, 0x0006, xs);
+ write_reg(par, 0x0007, ys);
+ break;
+ case 180:
+ write_reg(par, 0x0006, WIDTH - 1 - xs);
+ write_reg(par, 0x0007, HEIGHT - 1 - ys);
+ break;
+ case 270:
+ write_reg(par, 0x0006, WIDTH - 1 - ys);
+ write_reg(par, 0x0007, xs);
+ break;
+ case 90:
+ write_reg(par, 0x0006, ys);
+ write_reg(par, 0x0007, HEIGHT - 1 - xs);
+ break;
+ }
+
+ write_reg(par, 0x0e); /* Write Data to GRAM */
+}
+
+static int set_var(struct fbtft_par *par)
+{
+ fbtft_par_dbg(DEBUG_INIT_DISPLAY, par, "%s()\n", __func__);
+
+ switch (par->info->var.rotate) {
+ /* AM: GRAM update direction */
+ case 0:
+ write_reg(par, 0x01, 0x0000);
+ write_reg(par, 0x05, 0x0000);
+ break;
+ case 180:
+ write_reg(par, 0x01, 0x00C0);
+ write_reg(par, 0x05, 0x0000);
+ break;
+ case 270:
+ write_reg(par, 0x01, 0x0080);
+ write_reg(par, 0x05, 0x0001);
+ break;
+ case 90:
+ write_reg(par, 0x01, 0x0040);
+ write_reg(par, 0x05, 0x0001);
+ break;
+ }
+
+ return 0;
+}
+
+static struct fbtft_display display = {
+ .regwidth = 16,
+ .width = WIDTH,
+ .height = HEIGHT,
+ .fbtftops = {
+ .init_display = init_display,
+ .set_addr_win = set_addr_win,
+ .set_var = set_var,
+ },
+};
+FBTFT_REGISTER_DRIVER(DRVNAME, "nec,upd161704", &display);
+
+MODULE_ALIAS("spi:" DRVNAME);
+MODULE_ALIAS("platform:" DRVNAME);
+MODULE_ALIAS("spi:upd161704");
+MODULE_ALIAS("platform:upd161704");
+
+MODULE_DESCRIPTION("FB driver for the uPD161704 LCD Controller");
+MODULE_AUTHOR("Seong-Woo Kim");
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/fbtft/fb_watterott.c b/drivers/staging/fbtft/fb_watterott.c
new file mode 100644
index 000000000..88fb2c013
--- /dev/null
+++ b/drivers/staging/fbtft/fb_watterott.c
@@ -0,0 +1,324 @@
+/*
+ * FB driver for the Watterott LCD Controller
+ *
+ * Copyright (C) 2013 Noralf Tronnes
+ *
+ * This program is free software; you can redistribute it and/or 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/kernel.h>
+#include <linux/init.h>
+#include <linux/gpio.h>
+#include <linux/delay.h>
+
+#include "fbtft.h"
+
+#define DRVNAME "fb_watterott"
+#define WIDTH 320
+#define HEIGHT 240
+#define FPS 5
+#define TXBUFLEN 1024
+#define DEFAULT_BRIGHTNESS 50
+
+#define CMD_VERSION 0x01
+#define CMD_LCD_LED 0x10
+#define CMD_LCD_RESET 0x11
+#define CMD_LCD_ORIENTATION 0x20
+#define CMD_LCD_DRAWIMAGE 0x27
+#define COLOR_RGB323 8
+#define COLOR_RGB332 9
+#define COLOR_RGB233 10
+#define COLOR_RGB565 16
+
+
+static short mode = 565;
+module_param(mode, short, 0);
+MODULE_PARM_DESC(mode, "RGB color transfer mode: 332, 565 (default)");
+
+static void write_reg8_bus8(struct fbtft_par *par, int len, ...)
+{
+ va_list args;
+ int i, ret;
+ u8 *buf = par->buf;
+
+ va_start(args, len);
+ for (i = 0; i < len; i++)
+ *buf++ = (u8)va_arg(args, unsigned int);
+ va_end(args);
+
+ fbtft_par_dbg_hex(DEBUG_WRITE_REGISTER, par,
+ par->info->device, u8, par->buf, len, "%s: ", __func__);
+
+ ret = par->fbtftops.write(par, par->buf, len);
+ if (ret < 0) {
+ dev_err(par->info->device,
+ "write() failed and returned %d\n", ret);
+ return;
+ }
+}
+
+static int write_vmem(struct fbtft_par *par, size_t offset, size_t len)
+{
+ unsigned start_line, end_line;
+ u16 *vmem16 = (u16 *)(par->info->screen_base + offset);
+ u16 *pos = par->txbuf.buf + 1;
+ u16 *buf16 = par->txbuf.buf + 10;
+ int i, j;
+ int ret = 0;
+
+ fbtft_par_dbg(DEBUG_WRITE_VMEM, par, "%s()\n", __func__);
+
+ start_line = offset / par->info->fix.line_length;
+ end_line = start_line + (len / par->info->fix.line_length) - 1;
+
+ /* Set command header. pos: x, y, w, h */
+ ((u8 *)par->txbuf.buf)[0] = CMD_LCD_DRAWIMAGE;
+ pos[0] = 0;
+ pos[2] = cpu_to_be16(par->info->var.xres);
+ pos[3] = cpu_to_be16(1);
+ ((u8 *)par->txbuf.buf)[9] = COLOR_RGB565;
+
+ for (i = start_line; i <= end_line; i++) {
+ pos[1] = cpu_to_be16(i);
+ for (j = 0; j < par->info->var.xres; j++)
+ buf16[j] = cpu_to_be16(*vmem16++);
+ ret = par->fbtftops.write(par,
+ par->txbuf.buf, 10 + par->info->fix.line_length);
+ if (ret < 0)
+ return ret;
+ udelay(300);
+ }
+
+ return 0;
+}
+
+#define RGB565toRGB323(c) (((c&0xE000)>>8) | ((c&0600)>>6) | ((c&0x001C)>>2))
+#define RGB565toRGB332(c) (((c&0xE000)>>8) | ((c&0700)>>6) | ((c&0x0018)>>3))
+#define RGB565toRGB233(c) (((c&0xC000)>>8) | ((c&0700)>>5) | ((c&0x001C)>>2))
+
+static int write_vmem_8bit(struct fbtft_par *par, size_t offset, size_t len)
+{
+ unsigned start_line, end_line;
+ u16 *vmem16 = (u16 *)(par->info->screen_base + offset);
+ u16 *pos = par->txbuf.buf + 1;
+ u8 *buf8 = par->txbuf.buf + 10;
+ int i, j;
+ int ret = 0;
+
+ fbtft_par_dbg(DEBUG_WRITE_VMEM, par, "%s()\n", __func__);
+
+ start_line = offset / par->info->fix.line_length;
+ end_line = start_line + (len / par->info->fix.line_length) - 1;
+
+ /* Set command header. pos: x, y, w, h */
+ ((u8 *)par->txbuf.buf)[0] = CMD_LCD_DRAWIMAGE;
+ pos[0] = 0;
+ pos[2] = cpu_to_be16(par->info->var.xres);
+ pos[3] = cpu_to_be16(1);
+ ((u8 *)par->txbuf.buf)[9] = COLOR_RGB332;
+
+ for (i = start_line; i <= end_line; i++) {
+ pos[1] = cpu_to_be16(i);
+ for (j = 0; j < par->info->var.xres; j++) {
+ buf8[j] = RGB565toRGB332(*vmem16);
+ vmem16++;
+ }
+ ret = par->fbtftops.write(par,
+ par->txbuf.buf, 10 + par->info->var.xres);
+ if (ret < 0)
+ return ret;
+ udelay(700);
+ }
+
+ return 0;
+}
+
+static unsigned firmware_version(struct fbtft_par *par)
+{
+ u8 rxbuf[4] = {0, };
+
+ write_reg(par, CMD_VERSION);
+ par->fbtftops.read(par, rxbuf, 4);
+ if (rxbuf[1] != '.')
+ return 0;
+
+ return (rxbuf[0] - '0') << 8 | (rxbuf[2] - '0') << 4 | (rxbuf[3] - '0');
+}
+
+static int init_display(struct fbtft_par *par)
+{
+ int ret;
+ unsigned version;
+ u8 save_mode;
+
+ fbtft_par_dbg(DEBUG_INIT_DISPLAY, par, "%s()\n", __func__);
+
+ /* enable SPI interface by having CS and MOSI low during reset */
+ save_mode = par->spi->mode;
+ par->spi->mode |= SPI_CS_HIGH;
+ ret = par->spi->master->setup(par->spi); /* set CS inactive low */
+ if (ret) {
+ dev_err(par->info->device, "Could not set SPI_CS_HIGH\n");
+ return ret;
+ }
+ write_reg(par, 0x00); /* make sure mode is set */
+
+ mdelay(50);
+ par->fbtftops.reset(par);
+ mdelay(1000);
+ par->spi->mode = save_mode;
+ ret = par->spi->master->setup(par->spi);
+ if (ret) {
+ dev_err(par->info->device, "Could not restore SPI mode\n");
+ return ret;
+ }
+ write_reg(par, 0x00);
+
+ version = firmware_version(par);
+ fbtft_par_dbg(DEBUG_INIT_DISPLAY, par, "Firmware version: %x.%02x\n",
+ version >> 8, version & 0xFF);
+
+ if (mode == 332)
+ par->fbtftops.write_vmem = write_vmem_8bit;
+ return 0;
+}
+
+static void set_addr_win(struct fbtft_par *par, int xs, int ys, int xe, int ye)
+{
+ /* not used on this controller */
+}
+
+static int set_var(struct fbtft_par *par)
+{
+ u8 rotate;
+
+ fbtft_par_dbg(DEBUG_INIT_DISPLAY, par, "%s()\n", __func__);
+
+ /* this controller rotates clock wise */
+ switch (par->info->var.rotate) {
+ case 90:
+ rotate = 27;
+ break;
+ case 180:
+ rotate = 18;
+ break;
+ case 270:
+ rotate = 9;
+ break;
+ default:
+ rotate = 0;
+ }
+ write_reg(par, CMD_LCD_ORIENTATION, rotate);
+
+ return 0;
+}
+
+static int verify_gpios(struct fbtft_par *par)
+{
+ if (par->gpio.reset < 0) {
+ dev_err(par->info->device, "Missing 'reset' gpio. Aborting.\n");
+ return -EINVAL;
+ }
+ return 0;
+}
+
+#ifdef CONFIG_FB_BACKLIGHT
+static int backlight_chip_update_status(struct backlight_device *bd)
+{
+ struct fbtft_par *par = bl_get_data(bd);
+ int brightness = bd->props.brightness;
+
+ fbtft_par_dbg(DEBUG_BACKLIGHT, par,
+ "%s: brightness=%d, power=%d, fb_blank=%d\n",
+ __func__, bd->props.brightness, bd->props.power,
+ bd->props.fb_blank);
+
+ if (bd->props.power != FB_BLANK_UNBLANK)
+ brightness = 0;
+
+ if (bd->props.fb_blank != FB_BLANK_UNBLANK)
+ brightness = 0;
+
+ write_reg(par, CMD_LCD_LED, brightness);
+
+ return 0;
+}
+
+static void register_chip_backlight(struct fbtft_par *par)
+{
+ struct backlight_device *bd;
+ struct backlight_properties bl_props = { 0, };
+ struct backlight_ops *bl_ops;
+
+ fbtft_par_dbg(DEBUG_BACKLIGHT, par, "%s()\n", __func__);
+
+ bl_ops = devm_kzalloc(par->info->device, sizeof(struct backlight_ops),
+ GFP_KERNEL);
+ if (!bl_ops) {
+ dev_err(par->info->device,
+ "%s: could not allocate memory for backlight operations.\n",
+ __func__);
+ return;
+ }
+
+ bl_ops->update_status = backlight_chip_update_status;
+ bl_props.type = BACKLIGHT_RAW;
+ bl_props.power = FB_BLANK_POWERDOWN;
+ bl_props.max_brightness = 100;
+ bl_props.brightness = DEFAULT_BRIGHTNESS;
+
+ bd = backlight_device_register(dev_driver_string(par->info->device),
+ par->info->device, par, bl_ops, &bl_props);
+ if (IS_ERR(bd)) {
+ dev_err(par->info->device,
+ "cannot register backlight device (%ld)\n",
+ PTR_ERR(bd));
+ return;
+ }
+ par->info->bl_dev = bd;
+
+ if (!par->fbtftops.unregister_backlight)
+ par->fbtftops.unregister_backlight = fbtft_unregister_backlight;
+}
+#else
+#define register_chip_backlight NULL
+#endif
+
+
+static struct fbtft_display display = {
+ .regwidth = 8,
+ .buswidth = 8,
+ .width = WIDTH,
+ .height = HEIGHT,
+ .fps = FPS,
+ .txbuflen = TXBUFLEN,
+ .fbtftops = {
+ .write_register = write_reg8_bus8,
+ .write_vmem = write_vmem,
+ .init_display = init_display,
+ .set_addr_win = set_addr_win,
+ .set_var = set_var,
+ .verify_gpios = verify_gpios,
+ .register_backlight = register_chip_backlight,
+ },
+};
+FBTFT_REGISTER_DRIVER(DRVNAME, "watterott,openlcd", &display);
+
+MODULE_ALIAS("spi:" DRVNAME);
+
+MODULE_DESCRIPTION("FB driver for the Watterott LCD Controller");
+MODULE_AUTHOR("Noralf Tronnes");
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/fbtft/fbtft-bus.c b/drivers/staging/fbtft/fbtft-bus.c
new file mode 100644
index 000000000..52af9cbbc
--- /dev/null
+++ b/drivers/staging/fbtft/fbtft-bus.c
@@ -0,0 +1,256 @@
+#include <linux/export.h>
+#include <linux/errno.h>
+#include <linux/gpio.h>
+#include <linux/spi/spi.h>
+#include "fbtft.h"
+
+
+
+
+/*****************************************************************************
+ *
+ * void (*write_reg)(struct fbtft_par *par, int len, ...);
+ *
+ *****************************************************************************/
+
+#define define_fbtft_write_reg(func, type, modifier) \
+void func(struct fbtft_par *par, int len, ...) \
+{ \
+ va_list args; \
+ int i, ret; \
+ int offset = 0; \
+ type *buf = (type *)par->buf; \
+ \
+ if (unlikely(par->debug & DEBUG_WRITE_REGISTER)) { \
+ va_start(args, len); \
+ for (i = 0; i < len; i++) { \
+ buf[i] = (type)va_arg(args, unsigned int); \
+ } \
+ va_end(args); \
+ fbtft_par_dbg_hex(DEBUG_WRITE_REGISTER, par, par->info->device, type, buf, len, "%s: ", __func__); \
+ } \
+ \
+ va_start(args, len); \
+ \
+ if (par->startbyte) { \
+ *(u8 *)par->buf = par->startbyte; \
+ buf = (type *)(par->buf + 1); \
+ offset = 1; \
+ } \
+ \
+ *buf = modifier((type)va_arg(args, unsigned int)); \
+ if (par->gpio.dc != -1) \
+ gpio_set_value(par->gpio.dc, 0); \
+ ret = par->fbtftops.write(par, par->buf, sizeof(type)+offset); \
+ if (ret < 0) { \
+ va_end(args); \
+ dev_err(par->info->device, "%s: write() failed and returned %d\n", __func__, ret); \
+ return; \
+ } \
+ len--; \
+ \
+ if (par->startbyte) \
+ *(u8 *)par->buf = par->startbyte | 0x2; \
+ \
+ if (len) { \
+ i = len; \
+ while (i--) { \
+ *buf++ = modifier((type)va_arg(args, unsigned int)); \
+ } \
+ if (par->gpio.dc != -1) \
+ gpio_set_value(par->gpio.dc, 1); \
+ ret = par->fbtftops.write(par, par->buf, len * (sizeof(type)+offset)); \
+ if (ret < 0) { \
+ va_end(args); \
+ dev_err(par->info->device, "%s: write() failed and returned %d\n", __func__, ret); \
+ return; \
+ } \
+ } \
+ va_end(args); \
+} \
+EXPORT_SYMBOL(func);
+
+define_fbtft_write_reg(fbtft_write_reg8_bus8, u8, )
+define_fbtft_write_reg(fbtft_write_reg16_bus8, u16, cpu_to_be16)
+define_fbtft_write_reg(fbtft_write_reg16_bus16, u16, )
+
+void fbtft_write_reg8_bus9(struct fbtft_par *par, int len, ...)
+{
+ va_list args;
+ int i, ret;
+ int pad = 0;
+ u16 *buf = (u16 *)par->buf;
+
+ if (unlikely(par->debug & DEBUG_WRITE_REGISTER)) {
+ va_start(args, len);
+ for (i = 0; i < len; i++)
+ *(((u8 *)buf) + i) = (u8)va_arg(args, unsigned int);
+ va_end(args);
+ fbtft_par_dbg_hex(DEBUG_WRITE_REGISTER, par,
+ par->info->device, u8, buf, len, "%s: ", __func__);
+ }
+ if (len <= 0)
+ return;
+
+ if (par->spi && (par->spi->bits_per_word == 8)) {
+ /* we're emulating 9-bit, pad start of buffer with no-ops
+ (assuming here that zero is a no-op) */
+ pad = (len % 4) ? 4 - (len % 4) : 0;
+ for (i = 0; i < pad; i++)
+ *buf++ = 0x000;
+ }
+
+ va_start(args, len);
+ *buf++ = (u8)va_arg(args, unsigned int);
+ i = len - 1;
+ while (i--) {
+ *buf = (u8)va_arg(args, unsigned int);
+ *buf++ |= 0x100; /* dc=1 */
+ }
+ va_end(args);
+ ret = par->fbtftops.write(par, par->buf, (len + pad) * sizeof(u16));
+ if (ret < 0) {
+ dev_err(par->info->device,
+ "write() failed and returned %d\n", ret);
+ return;
+ }
+}
+EXPORT_SYMBOL(fbtft_write_reg8_bus9);
+
+
+
+
+/*****************************************************************************
+ *
+ * int (*write_vmem)(struct fbtft_par *par);
+ *
+ *****************************************************************************/
+
+/* 16 bit pixel over 8-bit databus */
+int fbtft_write_vmem16_bus8(struct fbtft_par *par, size_t offset, size_t len)
+{
+ u16 *vmem16;
+ u16 *txbuf16 = (u16 *)par->txbuf.buf;
+ size_t remain;
+ size_t to_copy;
+ size_t tx_array_size;
+ int i;
+ int ret = 0;
+ size_t startbyte_size = 0;
+
+ fbtft_par_dbg(DEBUG_WRITE_VMEM, par, "%s(offset=%zu, len=%zu)\n",
+ __func__, offset, len);
+
+ remain = len / 2;
+ vmem16 = (u16 *)(par->info->screen_base + offset);
+
+ if (par->gpio.dc != -1)
+ gpio_set_value(par->gpio.dc, 1);
+
+ /* non buffered write */
+ if (!par->txbuf.buf)
+ return par->fbtftops.write(par, vmem16, len);
+
+ /* buffered write */
+ tx_array_size = par->txbuf.len / 2;
+
+ if (par->startbyte) {
+ txbuf16 = (u16 *)(par->txbuf.buf + 1);
+ tx_array_size -= 2;
+ *(u8 *)(par->txbuf.buf) = par->startbyte | 0x2;
+ startbyte_size = 1;
+ }
+
+ while (remain) {
+ to_copy = remain > tx_array_size ? tx_array_size : remain;
+ dev_dbg(par->info->device, " to_copy=%zu, remain=%zu\n",
+ to_copy, remain - to_copy);
+
+ for (i = 0; i < to_copy; i++)
+ txbuf16[i] = cpu_to_be16(vmem16[i]);
+
+ vmem16 = vmem16 + to_copy;
+ ret = par->fbtftops.write(par, par->txbuf.buf,
+ startbyte_size + to_copy * 2);
+ if (ret < 0)
+ return ret;
+ remain -= to_copy;
+ }
+
+ return ret;
+}
+EXPORT_SYMBOL(fbtft_write_vmem16_bus8);
+
+/* 16 bit pixel over 9-bit SPI bus: dc + high byte, dc + low byte */
+int fbtft_write_vmem16_bus9(struct fbtft_par *par, size_t offset, size_t len)
+{
+ u8 *vmem8;
+ u16 *txbuf16 = par->txbuf.buf;
+ size_t remain;
+ size_t to_copy;
+ size_t tx_array_size;
+ int i;
+ int ret = 0;
+
+ fbtft_par_dbg(DEBUG_WRITE_VMEM, par, "%s(offset=%zu, len=%zu)\n",
+ __func__, offset, len);
+
+ if (!par->txbuf.buf) {
+ dev_err(par->info->device, "%s: txbuf.buf is NULL\n", __func__);
+ return -1;
+ }
+
+ remain = len;
+ vmem8 = par->info->screen_base + offset;
+
+ tx_array_size = par->txbuf.len / 2;
+
+ while (remain) {
+ to_copy = remain > tx_array_size ? tx_array_size : remain;
+ dev_dbg(par->info->device, " to_copy=%zu, remain=%zu\n",
+ to_copy, remain - to_copy);
+
+#ifdef __LITTLE_ENDIAN
+ for (i = 0; i < to_copy; i += 2) {
+ txbuf16[i] = 0x0100 | vmem8[i+1];
+ txbuf16[i+1] = 0x0100 | vmem8[i];
+ }
+#else
+ for (i = 0; i < to_copy; i++)
+ txbuf16[i] = 0x0100 | vmem8[i];
+#endif
+ vmem8 = vmem8 + to_copy;
+ ret = par->fbtftops.write(par, par->txbuf.buf, to_copy*2);
+ if (ret < 0)
+ return ret;
+ remain -= to_copy;
+ }
+
+ return ret;
+}
+EXPORT_SYMBOL(fbtft_write_vmem16_bus9);
+
+int fbtft_write_vmem8_bus8(struct fbtft_par *par, size_t offset, size_t len)
+{
+ dev_err(par->info->device, "%s: function not implemented\n", __func__);
+ return -1;
+}
+EXPORT_SYMBOL(fbtft_write_vmem8_bus8);
+
+/* 16 bit pixel over 16-bit databus */
+int fbtft_write_vmem16_bus16(struct fbtft_par *par, size_t offset, size_t len)
+{
+ u16 *vmem16;
+
+ fbtft_par_dbg(DEBUG_WRITE_VMEM, par, "%s(offset=%zu, len=%zu)\n",
+ __func__, offset, len);
+
+ vmem16 = (u16 *)(par->info->screen_base + offset);
+
+ if (par->gpio.dc != -1)
+ gpio_set_value(par->gpio.dc, 1);
+
+ /* no need for buffered write with 16-bit bus */
+ return par->fbtftops.write(par, vmem16, len);
+}
+EXPORT_SYMBOL(fbtft_write_vmem16_bus16);
diff --git a/drivers/staging/fbtft/fbtft-core.c b/drivers/staging/fbtft/fbtft-core.c
new file mode 100644
index 000000000..53b748be2
--- /dev/null
+++ b/drivers/staging/fbtft/fbtft-core.c
@@ -0,0 +1,1509 @@
+/*
+ * Copyright (C) 2013 Noralf Tronnes
+ *
+ * This driver is inspired by:
+ * st7735fb.c, Copyright (C) 2011, Matt Porter
+ * broadsheetfb.c, Copyright (C) 2008, Jaya Kumar
+ *
+ * This program is free software; you can redistribute it and/or 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/kernel.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/mm.h>
+#include <linux/vmalloc.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/fb.h>
+#include <linux/gpio.h>
+#include <linux/spi/spi.h>
+#include <linux/delay.h>
+#include <linux/uaccess.h>
+#include <linux/backlight.h>
+#include <linux/platform_device.h>
+#include <linux/spinlock.h>
+#include <linux/dma-mapping.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
+
+#include "fbtft.h"
+#include "internal.h"
+
+static unsigned long debug;
+module_param(debug, ulong, 0);
+MODULE_PARM_DESC(debug, "override device debug level");
+
+static bool dma = true;
+module_param(dma, bool, 0);
+MODULE_PARM_DESC(dma, "Use DMA buffer");
+
+
+void fbtft_dbg_hex(const struct device *dev, int groupsize,
+ void *buf, size_t len, const char *fmt, ...)
+{
+ va_list args;
+ static char textbuf[512];
+ char *text = textbuf;
+ size_t text_len;
+
+ va_start(args, fmt);
+ text_len = vscnprintf(text, sizeof(textbuf), fmt, args);
+ va_end(args);
+
+ hex_dump_to_buffer(buf, len, 32, groupsize, text + text_len,
+ 512 - text_len, false);
+
+ if (len > 32)
+ dev_info(dev, "%s ...\n", text);
+ else
+ dev_info(dev, "%s\n", text);
+}
+EXPORT_SYMBOL(fbtft_dbg_hex);
+
+static unsigned long fbtft_request_gpios_match(struct fbtft_par *par,
+ const struct fbtft_gpio *gpio)
+{
+ int ret;
+ long val;
+
+ fbtft_par_dbg(DEBUG_REQUEST_GPIOS_MATCH, par, "%s('%s')\n",
+ __func__, gpio->name);
+
+ if (strcasecmp(gpio->name, "reset") == 0) {
+ par->gpio.reset = gpio->gpio;
+ return GPIOF_OUT_INIT_HIGH;
+ } else if (strcasecmp(gpio->name, "dc") == 0) {
+ par->gpio.dc = gpio->gpio;
+ return GPIOF_OUT_INIT_LOW;
+ } else if (strcasecmp(gpio->name, "cs") == 0) {
+ par->gpio.cs = gpio->gpio;
+ return GPIOF_OUT_INIT_HIGH;
+ } else if (strcasecmp(gpio->name, "wr") == 0) {
+ par->gpio.wr = gpio->gpio;
+ return GPIOF_OUT_INIT_HIGH;
+ } else if (strcasecmp(gpio->name, "rd") == 0) {
+ par->gpio.rd = gpio->gpio;
+ return GPIOF_OUT_INIT_HIGH;
+ } else if (strcasecmp(gpio->name, "latch") == 0) {
+ par->gpio.latch = gpio->gpio;
+ return GPIOF_OUT_INIT_LOW;
+ } else if (gpio->name[0] == 'd' && gpio->name[1] == 'b') {
+ ret = kstrtol(&gpio->name[2], 10, &val);
+ if (ret == 0 && val < 16) {
+ par->gpio.db[val] = gpio->gpio;
+ return GPIOF_OUT_INIT_LOW;
+ }
+ } else if (strcasecmp(gpio->name, "led") == 0) {
+ par->gpio.led[0] = gpio->gpio;
+ return GPIOF_OUT_INIT_LOW;
+ } else if (strcasecmp(gpio->name, "led_") == 0) {
+ par->gpio.led[0] = gpio->gpio;
+ return GPIOF_OUT_INIT_HIGH;
+ }
+
+ return FBTFT_GPIO_NO_MATCH;
+}
+
+static int fbtft_request_gpios(struct fbtft_par *par)
+{
+ struct fbtft_platform_data *pdata = par->pdata;
+ const struct fbtft_gpio *gpio;
+ unsigned long flags;
+ int ret;
+
+ if (pdata && pdata->gpios) {
+ gpio = pdata->gpios;
+ while (gpio->name[0]) {
+ flags = FBTFT_GPIO_NO_MATCH;
+ /* if driver provides match function, try it first,
+ if no match use our own */
+ if (par->fbtftops.request_gpios_match)
+ flags = par->fbtftops.request_gpios_match(par, gpio);
+ if (flags == FBTFT_GPIO_NO_MATCH)
+ flags = fbtft_request_gpios_match(par, gpio);
+ if (flags != FBTFT_GPIO_NO_MATCH) {
+ ret = devm_gpio_request_one(par->info->device,
+ gpio->gpio, flags,
+ par->info->device->driver->name);
+ if (ret < 0) {
+ dev_err(par->info->device,
+ "%s: gpio_request_one('%s'=%d) failed with %d\n",
+ __func__, gpio->name,
+ gpio->gpio, ret);
+ return ret;
+ }
+ fbtft_par_dbg(DEBUG_REQUEST_GPIOS, par,
+ "%s: '%s' = GPIO%d\n",
+ __func__, gpio->name, gpio->gpio);
+ }
+ gpio++;
+ }
+ }
+
+ return 0;
+}
+
+#ifdef CONFIG_OF
+static int fbtft_request_one_gpio(struct fbtft_par *par,
+ const char *name, int index, int *gpiop)
+{
+ struct device *dev = par->info->device;
+ struct device_node *node = dev->of_node;
+ int gpio, flags, ret = 0;
+ enum of_gpio_flags of_flags;
+
+ if (of_find_property(node, name, NULL)) {
+ gpio = of_get_named_gpio_flags(node, name, index, &of_flags);
+ if (gpio == -ENOENT)
+ return 0;
+ if (gpio == -EPROBE_DEFER)
+ return gpio;
+ if (gpio < 0) {
+ dev_err(dev,
+ "failed to get '%s' from DT\n", name);
+ return gpio;
+ }
+
+ /* active low translates to initially low */
+ flags = (of_flags & OF_GPIO_ACTIVE_LOW) ? GPIOF_OUT_INIT_LOW :
+ GPIOF_OUT_INIT_HIGH;
+ ret = devm_gpio_request_one(dev, gpio, flags,
+ dev->driver->name);
+ if (ret) {
+ dev_err(dev,
+ "gpio_request_one('%s'=%d) failed with %d\n",
+ name, gpio, ret);
+ return ret;
+ }
+ if (gpiop)
+ *gpiop = gpio;
+ fbtft_par_dbg(DEBUG_REQUEST_GPIOS, par, "%s: '%s' = GPIO%d\n",
+ __func__, name, gpio);
+ }
+
+ return ret;
+}
+
+static int fbtft_request_gpios_dt(struct fbtft_par *par)
+{
+ int i;
+ int ret;
+
+ if (!par->info->device->of_node)
+ return -EINVAL;
+
+ ret = fbtft_request_one_gpio(par, "reset-gpios", 0, &par->gpio.reset);
+ if (ret)
+ return ret;
+ ret = fbtft_request_one_gpio(par, "dc-gpios", 0, &par->gpio.dc);
+ if (ret)
+ return ret;
+ ret = fbtft_request_one_gpio(par, "rd-gpios", 0, &par->gpio.rd);
+ if (ret)
+ return ret;
+ ret = fbtft_request_one_gpio(par, "wr-gpios", 0, &par->gpio.wr);
+ if (ret)
+ return ret;
+ ret = fbtft_request_one_gpio(par, "cs-gpios", 0, &par->gpio.cs);
+ if (ret)
+ return ret;
+ ret = fbtft_request_one_gpio(par, "latch-gpios", 0, &par->gpio.latch);
+ if (ret)
+ return ret;
+ for (i = 0; i < 16; i++) {
+ ret = fbtft_request_one_gpio(par, "db-gpios", i,
+ &par->gpio.db[i]);
+ if (ret)
+ return ret;
+ ret = fbtft_request_one_gpio(par, "led-gpios", i,
+ &par->gpio.led[i]);
+ if (ret)
+ return ret;
+ ret = fbtft_request_one_gpio(par, "aux-gpios", i,
+ &par->gpio.aux[i]);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+#endif
+
+#ifdef CONFIG_FB_BACKLIGHT
+static int fbtft_backlight_update_status(struct backlight_device *bd)
+{
+ struct fbtft_par *par = bl_get_data(bd);
+ bool polarity = !!(bd->props.state & BL_CORE_DRIVER1);
+
+ fbtft_par_dbg(DEBUG_BACKLIGHT, par,
+ "%s: polarity=%d, power=%d, fb_blank=%d\n",
+ __func__, polarity, bd->props.power, bd->props.fb_blank);
+
+ if ((bd->props.power == FB_BLANK_UNBLANK) && (bd->props.fb_blank == FB_BLANK_UNBLANK))
+ gpio_set_value(par->gpio.led[0], polarity);
+ else
+ gpio_set_value(par->gpio.led[0], !polarity);
+
+ return 0;
+}
+
+static int fbtft_backlight_get_brightness(struct backlight_device *bd)
+{
+ return bd->props.brightness;
+}
+
+void fbtft_unregister_backlight(struct fbtft_par *par)
+{
+ const struct backlight_ops *bl_ops;
+
+ fbtft_par_dbg(DEBUG_BACKLIGHT, par, "%s()\n", __func__);
+
+ if (par->info->bl_dev) {
+ par->info->bl_dev->props.power = FB_BLANK_POWERDOWN;
+ backlight_update_status(par->info->bl_dev);
+ bl_ops = par->info->bl_dev->ops;
+ backlight_device_unregister(par->info->bl_dev);
+ par->info->bl_dev = NULL;
+ }
+}
+
+void fbtft_register_backlight(struct fbtft_par *par)
+{
+ struct backlight_device *bd;
+ struct backlight_properties bl_props = { 0, };
+ struct backlight_ops *bl_ops;
+
+ fbtft_par_dbg(DEBUG_BACKLIGHT, par, "%s()\n", __func__);
+
+ if (par->gpio.led[0] == -1) {
+ fbtft_par_dbg(DEBUG_BACKLIGHT, par,
+ "%s(): led pin not set, exiting.\n", __func__);
+ return;
+ }
+
+ bl_ops = devm_kzalloc(par->info->device, sizeof(struct backlight_ops),
+ GFP_KERNEL);
+ if (!bl_ops)
+ return;
+
+ bl_ops->get_brightness = fbtft_backlight_get_brightness;
+ bl_ops->update_status = fbtft_backlight_update_status;
+ bl_props.type = BACKLIGHT_RAW;
+ /* Assume backlight is off, get polarity from current state of pin */
+ bl_props.power = FB_BLANK_POWERDOWN;
+ if (!gpio_get_value(par->gpio.led[0]))
+ bl_props.state |= BL_CORE_DRIVER1;
+
+ bd = backlight_device_register(dev_driver_string(par->info->device),
+ par->info->device, par, bl_ops, &bl_props);
+ if (IS_ERR(bd)) {
+ dev_err(par->info->device,
+ "cannot register backlight device (%ld)\n",
+ PTR_ERR(bd));
+ return;
+ }
+ par->info->bl_dev = bd;
+
+ if (!par->fbtftops.unregister_backlight)
+ par->fbtftops.unregister_backlight = fbtft_unregister_backlight;
+}
+#else
+void fbtft_register_backlight(struct fbtft_par *par) { };
+void fbtft_unregister_backlight(struct fbtft_par *par) { };
+#endif
+EXPORT_SYMBOL(fbtft_register_backlight);
+EXPORT_SYMBOL(fbtft_unregister_backlight);
+
+static void fbtft_set_addr_win(struct fbtft_par *par, int xs, int ys, int xe,
+ int ye)
+{
+ fbtft_par_dbg(DEBUG_SET_ADDR_WIN, par,
+ "%s(xs=%d, ys=%d, xe=%d, ye=%d)\n", __func__, xs, ys, xe, ye);
+
+ /* Column address set */
+ write_reg(par, 0x2A,
+ (xs >> 8) & 0xFF, xs & 0xFF, (xe >> 8) & 0xFF, xe & 0xFF);
+
+ /* Row address set */
+ write_reg(par, 0x2B,
+ (ys >> 8) & 0xFF, ys & 0xFF, (ye >> 8) & 0xFF, ye & 0xFF);
+
+ /* Memory write */
+ write_reg(par, 0x2C);
+}
+
+
+static void fbtft_reset(struct fbtft_par *par)
+{
+ if (par->gpio.reset == -1)
+ return;
+ fbtft_par_dbg(DEBUG_RESET, par, "%s()\n", __func__);
+ gpio_set_value(par->gpio.reset, 0);
+ udelay(20);
+ gpio_set_value(par->gpio.reset, 1);
+ mdelay(120);
+}
+
+
+static void fbtft_update_display(struct fbtft_par *par, unsigned start_line,
+ unsigned end_line)
+{
+ size_t offset, len;
+ struct timespec ts_start, ts_end, ts_fps, ts_duration;
+ long fps_ms, fps_us, duration_ms, duration_us;
+ long fps, throughput;
+ bool timeit = false;
+ int ret = 0;
+
+ if (unlikely(par->debug & (DEBUG_TIME_FIRST_UPDATE | DEBUG_TIME_EACH_UPDATE))) {
+ if ((par->debug & DEBUG_TIME_EACH_UPDATE) ||
+ ((par->debug & DEBUG_TIME_FIRST_UPDATE) && !par->first_update_done)) {
+ getnstimeofday(&ts_start);
+ timeit = true;
+ }
+ }
+
+ /* Sanity checks */
+ if (start_line > end_line) {
+ dev_warn(par->info->device,
+ "%s: start_line=%u is larger than end_line=%u. Shouldn't happen, will do full display update\n",
+ __func__, start_line, end_line);
+ start_line = 0;
+ end_line = par->info->var.yres - 1;
+ }
+ if (start_line > par->info->var.yres - 1 || end_line > par->info->var.yres - 1) {
+ dev_warn(par->info->device,
+ "%s: start_line=%u or end_line=%u is larger than max=%d. Shouldn't happen, will do full display update\n",
+ __func__, start_line, end_line, par->info->var.yres - 1);
+ start_line = 0;
+ end_line = par->info->var.yres - 1;
+ }
+
+ fbtft_par_dbg(DEBUG_UPDATE_DISPLAY, par, "%s(start_line=%u, end_line=%u)\n",
+ __func__, start_line, end_line);
+
+ if (par->fbtftops.set_addr_win)
+ par->fbtftops.set_addr_win(par, 0, start_line,
+ par->info->var.xres-1, end_line);
+
+ offset = start_line * par->info->fix.line_length;
+ len = (end_line - start_line + 1) * par->info->fix.line_length;
+ ret = par->fbtftops.write_vmem(par, offset, len);
+ if (ret < 0)
+ dev_err(par->info->device,
+ "%s: write_vmem failed to update display buffer\n",
+ __func__);
+
+ if (unlikely(timeit)) {
+ getnstimeofday(&ts_end);
+ if (par->update_time.tv_nsec == 0 && par->update_time.tv_sec == 0) {
+ par->update_time.tv_sec = ts_start.tv_sec;
+ par->update_time.tv_nsec = ts_start.tv_nsec;
+ }
+ ts_fps = timespec_sub(ts_start, par->update_time);
+ par->update_time.tv_sec = ts_start.tv_sec;
+ par->update_time.tv_nsec = ts_start.tv_nsec;
+ fps_ms = (ts_fps.tv_sec * 1000) + ((ts_fps.tv_nsec / 1000000) % 1000);
+ fps_us = (ts_fps.tv_nsec / 1000) % 1000;
+ fps = fps_ms * 1000 + fps_us;
+ fps = fps ? 1000000 / fps : 0;
+
+ ts_duration = timespec_sub(ts_end, ts_start);
+ duration_ms = (ts_duration.tv_sec * 1000) + ((ts_duration.tv_nsec / 1000000) % 1000);
+ duration_us = (ts_duration.tv_nsec / 1000) % 1000;
+ throughput = duration_ms * 1000 + duration_us;
+ throughput = throughput ? (len * 1000) / throughput : 0;
+ throughput = throughput * 1000 / 1024;
+
+ dev_info(par->info->device,
+ "Display update: %ld kB/s (%ld.%.3ld ms), fps=%ld (%ld.%.3ld ms)\n",
+ throughput, duration_ms, duration_us,
+ fps, fps_ms, fps_us);
+ par->first_update_done = true;
+ }
+}
+
+
+static void fbtft_mkdirty(struct fb_info *info, int y, int height)
+{
+ struct fbtft_par *par = info->par;
+ struct fb_deferred_io *fbdefio = info->fbdefio;
+
+ /* special case, needed ? */
+ if (y == -1) {
+ y = 0;
+ height = info->var.yres - 1;
+ }
+
+ /* Mark display lines/area as dirty */
+ spin_lock(&par->dirty_lock);
+ if (y < par->dirty_lines_start)
+ par->dirty_lines_start = y;
+ if (y + height - 1 > par->dirty_lines_end)
+ par->dirty_lines_end = y + height - 1;
+ spin_unlock(&par->dirty_lock);
+
+ /* Schedule deferred_io to update display (no-op if already on queue)*/
+ schedule_delayed_work(&info->deferred_work, fbdefio->delay);
+}
+
+static void fbtft_deferred_io(struct fb_info *info, struct list_head *pagelist)
+{
+ struct fbtft_par *par = info->par;
+ unsigned dirty_lines_start, dirty_lines_end;
+ struct page *page;
+ unsigned long index;
+ unsigned y_low = 0, y_high = 0;
+ int count = 0;
+
+ spin_lock(&par->dirty_lock);
+ dirty_lines_start = par->dirty_lines_start;
+ dirty_lines_end = par->dirty_lines_end;
+ /* set display line markers as clean */
+ par->dirty_lines_start = par->info->var.yres - 1;
+ par->dirty_lines_end = 0;
+ spin_unlock(&par->dirty_lock);
+
+ /* Mark display lines as dirty */
+ list_for_each_entry(page, pagelist, lru) {
+ count++;
+ index = page->index << PAGE_SHIFT;
+ y_low = index / info->fix.line_length;
+ y_high = (index + PAGE_SIZE - 1) / info->fix.line_length;
+ fbtft_dev_dbg(DEBUG_DEFERRED_IO, par, info->device,
+ "page->index=%lu y_low=%d y_high=%d\n",
+ page->index, y_low, y_high);
+ if (y_high > info->var.yres - 1)
+ y_high = info->var.yres - 1;
+ if (y_low < dirty_lines_start)
+ dirty_lines_start = y_low;
+ if (y_high > dirty_lines_end)
+ dirty_lines_end = y_high;
+ }
+
+ par->fbtftops.update_display(info->par,
+ dirty_lines_start, dirty_lines_end);
+}
+
+
+static void fbtft_fb_fillrect(struct fb_info *info,
+ const struct fb_fillrect *rect)
+{
+ struct fbtft_par *par = info->par;
+
+ fbtft_dev_dbg(DEBUG_FB_FILLRECT, par, info->dev,
+ "%s: dx=%d, dy=%d, width=%d, height=%d\n",
+ __func__, rect->dx, rect->dy, rect->width, rect->height);
+ sys_fillrect(info, rect);
+
+ par->fbtftops.mkdirty(info, rect->dy, rect->height);
+}
+
+static void fbtft_fb_copyarea(struct fb_info *info,
+ const struct fb_copyarea *area)
+{
+ struct fbtft_par *par = info->par;
+
+ fbtft_dev_dbg(DEBUG_FB_COPYAREA, par, info->dev,
+ "%s: dx=%d, dy=%d, width=%d, height=%d\n",
+ __func__, area->dx, area->dy, area->width, area->height);
+ sys_copyarea(info, area);
+
+ par->fbtftops.mkdirty(info, area->dy, area->height);
+}
+
+static void fbtft_fb_imageblit(struct fb_info *info,
+ const struct fb_image *image)
+{
+ struct fbtft_par *par = info->par;
+
+ fbtft_dev_dbg(DEBUG_FB_IMAGEBLIT, par, info->dev,
+ "%s: dx=%d, dy=%d, width=%d, height=%d\n",
+ __func__, image->dx, image->dy, image->width, image->height);
+ sys_imageblit(info, image);
+
+ par->fbtftops.mkdirty(info, image->dy, image->height);
+}
+
+static ssize_t fbtft_fb_write(struct fb_info *info, const char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ struct fbtft_par *par = info->par;
+ ssize_t res;
+
+ fbtft_dev_dbg(DEBUG_FB_WRITE, par, info->dev,
+ "%s: count=%zd, ppos=%llu\n", __func__, count, *ppos);
+ res = fb_sys_write(info, buf, count, ppos);
+
+ /* TODO: only mark changed area
+ update all for now */
+ par->fbtftops.mkdirty(info, -1, 0);
+
+ return res;
+}
+
+/* from pxafb.c */
+static unsigned int chan_to_field(unsigned chan, struct fb_bitfield *bf)
+{
+ chan &= 0xffff;
+ chan >>= 16 - bf->length;
+ return chan << bf->offset;
+}
+
+static int fbtft_fb_setcolreg(unsigned regno, unsigned red, unsigned green,
+ unsigned blue, unsigned transp,
+ struct fb_info *info)
+{
+ struct fbtft_par *par = info->par;
+ unsigned val;
+ int ret = 1;
+
+ fbtft_dev_dbg(DEBUG_FB_SETCOLREG, par, info->dev,
+ "%s(regno=%u, red=0x%X, green=0x%X, blue=0x%X, trans=0x%X)\n",
+ __func__, regno, red, green, blue, transp);
+
+ switch (info->fix.visual) {
+ case FB_VISUAL_TRUECOLOR:
+ if (regno < 16) {
+ u32 *pal = info->pseudo_palette;
+
+ val = chan_to_field(red, &info->var.red);
+ val |= chan_to_field(green, &info->var.green);
+ val |= chan_to_field(blue, &info->var.blue);
+
+ pal[regno] = val;
+ ret = 0;
+ }
+ break;
+
+ }
+ return ret;
+}
+
+static int fbtft_fb_blank(int blank, struct fb_info *info)
+{
+ struct fbtft_par *par = info->par;
+ int ret = -EINVAL;
+
+ fbtft_dev_dbg(DEBUG_FB_BLANK, par, info->dev, "%s(blank=%d)\n",
+ __func__, blank);
+
+ if (!par->fbtftops.blank)
+ return ret;
+
+ switch (blank) {
+ case FB_BLANK_POWERDOWN:
+ case FB_BLANK_VSYNC_SUSPEND:
+ case FB_BLANK_HSYNC_SUSPEND:
+ case FB_BLANK_NORMAL:
+ ret = par->fbtftops.blank(par, true);
+ break;
+ case FB_BLANK_UNBLANK:
+ ret = par->fbtftops.blank(par, false);
+ break;
+ }
+ return ret;
+}
+
+static void fbtft_merge_fbtftops(struct fbtft_ops *dst, struct fbtft_ops *src)
+{
+ if (src->write)
+ dst->write = src->write;
+ if (src->read)
+ dst->read = src->read;
+ if (src->write_vmem)
+ dst->write_vmem = src->write_vmem;
+ if (src->write_register)
+ dst->write_register = src->write_register;
+ if (src->set_addr_win)
+ dst->set_addr_win = src->set_addr_win;
+ if (src->reset)
+ dst->reset = src->reset;
+ if (src->mkdirty)
+ dst->mkdirty = src->mkdirty;
+ if (src->update_display)
+ dst->update_display = src->update_display;
+ if (src->init_display)
+ dst->init_display = src->init_display;
+ if (src->blank)
+ dst->blank = src->blank;
+ if (src->request_gpios_match)
+ dst->request_gpios_match = src->request_gpios_match;
+ if (src->request_gpios)
+ dst->request_gpios = src->request_gpios;
+ if (src->verify_gpios)
+ dst->verify_gpios = src->verify_gpios;
+ if (src->register_backlight)
+ dst->register_backlight = src->register_backlight;
+ if (src->unregister_backlight)
+ dst->unregister_backlight = src->unregister_backlight;
+ if (src->set_var)
+ dst->set_var = src->set_var;
+ if (src->set_gamma)
+ dst->set_gamma = src->set_gamma;
+}
+
+/**
+ * fbtft_framebuffer_alloc - creates a new frame buffer info structure
+ *
+ * @display: pointer to structure describing the display
+ * @dev: pointer to the device for this fb, this can be NULL
+ *
+ * Creates a new frame buffer info structure.
+ *
+ * Also creates and populates the following structures:
+ * info->fbops
+ * info->fbdefio
+ * info->pseudo_palette
+ * par->fbtftops
+ * par->txbuf
+ *
+ * Returns the new structure, or NULL if an error occurred.
+ *
+ */
+struct fb_info *fbtft_framebuffer_alloc(struct fbtft_display *display,
+ struct device *dev)
+{
+ struct fb_info *info;
+ struct fbtft_par *par;
+ struct fb_ops *fbops = NULL;
+ struct fb_deferred_io *fbdefio = NULL;
+ struct fbtft_platform_data *pdata = dev->platform_data;
+ u8 *vmem = NULL;
+ void *txbuf = NULL;
+ void *buf = NULL;
+ unsigned width;
+ unsigned height;
+ int txbuflen = display->txbuflen;
+ unsigned bpp = display->bpp;
+ unsigned fps = display->fps;
+ int vmem_size, i;
+ int *init_sequence = display->init_sequence;
+ char *gamma = display->gamma;
+ unsigned long *gamma_curves = NULL;
+
+ /* sanity check */
+ if (display->gamma_num * display->gamma_len > FBTFT_GAMMA_MAX_VALUES_TOTAL) {
+ dev_err(dev, "FBTFT_GAMMA_MAX_VALUES_TOTAL=%d is exceeded\n",
+ FBTFT_GAMMA_MAX_VALUES_TOTAL);
+ return NULL;
+ }
+
+ /* defaults */
+ if (!fps)
+ fps = 20;
+ if (!bpp)
+ bpp = 16;
+
+ if (!pdata) {
+ dev_err(dev, "platform data is missing\n");
+ return NULL;
+ }
+
+ /* override driver values? */
+ if (pdata->fps)
+ fps = pdata->fps;
+ if (pdata->txbuflen)
+ txbuflen = pdata->txbuflen;
+ if (pdata->display.init_sequence)
+ init_sequence = pdata->display.init_sequence;
+ if (pdata->gamma)
+ gamma = pdata->gamma;
+ if (pdata->display.debug)
+ display->debug = pdata->display.debug;
+ if (pdata->display.backlight)
+ display->backlight = pdata->display.backlight;
+ if (pdata->display.width)
+ display->width = pdata->display.width;
+ if (pdata->display.height)
+ display->height = pdata->display.height;
+ if (pdata->display.buswidth)
+ display->buswidth = pdata->display.buswidth;
+ if (pdata->display.regwidth)
+ display->regwidth = pdata->display.regwidth;
+
+ display->debug |= debug;
+ fbtft_expand_debug_value(&display->debug);
+
+ switch (pdata->rotate) {
+ case 90:
+ case 270:
+ width = display->height;
+ height = display->width;
+ break;
+ default:
+ width = display->width;
+ height = display->height;
+ }
+
+ vmem_size = display->width * display->height * bpp / 8;
+ vmem = vzalloc(vmem_size);
+ if (!vmem)
+ goto alloc_fail;
+
+ fbops = devm_kzalloc(dev, sizeof(struct fb_ops), GFP_KERNEL);
+ if (!fbops)
+ goto alloc_fail;
+
+ fbdefio = devm_kzalloc(dev, sizeof(struct fb_deferred_io), GFP_KERNEL);
+ if (!fbdefio)
+ goto alloc_fail;
+
+ buf = devm_kzalloc(dev, 128, GFP_KERNEL);
+ if (!buf)
+ goto alloc_fail;
+
+ if (display->gamma_num && display->gamma_len) {
+ gamma_curves = devm_kzalloc(dev, display->gamma_num * display->gamma_len * sizeof(gamma_curves[0]),
+ GFP_KERNEL);
+ if (!gamma_curves)
+ goto alloc_fail;
+ }
+
+ info = framebuffer_alloc(sizeof(struct fbtft_par), dev);
+ if (!info)
+ goto alloc_fail;
+
+ info->screen_base = (u8 __force __iomem *)vmem;
+ info->fbops = fbops;
+ info->fbdefio = fbdefio;
+
+ fbops->owner = dev->driver->owner;
+ fbops->fb_read = fb_sys_read;
+ fbops->fb_write = fbtft_fb_write;
+ fbops->fb_fillrect = fbtft_fb_fillrect;
+ fbops->fb_copyarea = fbtft_fb_copyarea;
+ fbops->fb_imageblit = fbtft_fb_imageblit;
+ fbops->fb_setcolreg = fbtft_fb_setcolreg;
+ fbops->fb_blank = fbtft_fb_blank;
+
+ fbdefio->delay = HZ/fps;
+ fbdefio->deferred_io = fbtft_deferred_io;
+ fb_deferred_io_init(info);
+
+ strncpy(info->fix.id, dev->driver->name, 16);
+ info->fix.type = FB_TYPE_PACKED_PIXELS;
+ info->fix.visual = FB_VISUAL_TRUECOLOR;
+ info->fix.xpanstep = 0;
+ info->fix.ypanstep = 0;
+ info->fix.ywrapstep = 0;
+ info->fix.line_length = width*bpp/8;
+ info->fix.accel = FB_ACCEL_NONE;
+ info->fix.smem_len = vmem_size;
+
+ info->var.rotate = pdata->rotate;
+ info->var.xres = width;
+ info->var.yres = height;
+ info->var.xres_virtual = info->var.xres;
+ info->var.yres_virtual = info->var.yres;
+ info->var.bits_per_pixel = bpp;
+ info->var.nonstd = 1;
+
+ /* RGB565 */
+ info->var.red.offset = 11;
+ info->var.red.length = 5;
+ info->var.green.offset = 5;
+ info->var.green.length = 6;
+ info->var.blue.offset = 0;
+ info->var.blue.length = 5;
+ info->var.transp.offset = 0;
+ info->var.transp.length = 0;
+
+ info->flags = FBINFO_FLAG_DEFAULT | FBINFO_VIRTFB;
+
+ par = info->par;
+ par->info = info;
+ par->pdata = dev->platform_data;
+ par->debug = display->debug;
+ par->buf = buf;
+ spin_lock_init(&par->dirty_lock);
+ par->bgr = pdata->bgr;
+ par->startbyte = pdata->startbyte;
+ par->init_sequence = init_sequence;
+ par->gamma.curves = gamma_curves;
+ par->gamma.num_curves = display->gamma_num;
+ par->gamma.num_values = display->gamma_len;
+ mutex_init(&par->gamma.lock);
+ info->pseudo_palette = par->pseudo_palette;
+
+ if (par->gamma.curves && gamma) {
+ if (fbtft_gamma_parse_str(par,
+ par->gamma.curves, gamma, strlen(gamma)))
+ goto alloc_fail;
+ }
+
+ /* Transmit buffer */
+ if (txbuflen == -1)
+ txbuflen = vmem_size + 2; /* add in case startbyte is used */
+
+#ifdef __LITTLE_ENDIAN
+ if ((!txbuflen) && (bpp > 8))
+ txbuflen = PAGE_SIZE; /* need buffer for byteswapping */
+#endif
+
+ if (txbuflen > 0) {
+ if (dma) {
+ dev->coherent_dma_mask = ~0;
+ txbuf = dmam_alloc_coherent(dev, txbuflen, &par->txbuf.dma, GFP_DMA);
+ } else {
+ txbuf = devm_kzalloc(par->info->device, txbuflen, GFP_KERNEL);
+ }
+ if (!txbuf)
+ goto alloc_fail;
+ par->txbuf.buf = txbuf;
+ par->txbuf.len = txbuflen;
+ }
+
+ /* Initialize gpios to disabled */
+ par->gpio.reset = -1;
+ par->gpio.dc = -1;
+ par->gpio.rd = -1;
+ par->gpio.wr = -1;
+ par->gpio.cs = -1;
+ par->gpio.latch = -1;
+ for (i = 0; i < 16; i++) {
+ par->gpio.db[i] = -1;
+ par->gpio.led[i] = -1;
+ par->gpio.aux[i] = -1;
+ }
+
+ /* default fbtft operations */
+ par->fbtftops.write = fbtft_write_spi;
+ par->fbtftops.read = fbtft_read_spi;
+ par->fbtftops.write_vmem = fbtft_write_vmem16_bus8;
+ par->fbtftops.write_register = fbtft_write_reg8_bus8;
+ par->fbtftops.set_addr_win = fbtft_set_addr_win;
+ par->fbtftops.reset = fbtft_reset;
+ par->fbtftops.mkdirty = fbtft_mkdirty;
+ par->fbtftops.update_display = fbtft_update_display;
+ par->fbtftops.request_gpios = fbtft_request_gpios;
+ if (display->backlight)
+ par->fbtftops.register_backlight = fbtft_register_backlight;
+
+ /* use driver provided functions */
+ fbtft_merge_fbtftops(&par->fbtftops, &display->fbtftops);
+
+ return info;
+
+alloc_fail:
+ vfree(vmem);
+
+ return NULL;
+}
+EXPORT_SYMBOL(fbtft_framebuffer_alloc);
+
+/**
+ * fbtft_framebuffer_release - frees up all memory used by the framebuffer
+ *
+ * @info: frame buffer info structure
+ *
+ */
+void fbtft_framebuffer_release(struct fb_info *info)
+{
+ fb_deferred_io_cleanup(info);
+ vfree(info->screen_base);
+ framebuffer_release(info);
+}
+EXPORT_SYMBOL(fbtft_framebuffer_release);
+
+/**
+ * fbtft_register_framebuffer - registers a tft frame buffer device
+ * @fb_info: frame buffer info structure
+ *
+ * Sets SPI driverdata if needed
+ * Requests needed gpios.
+ * Initializes display
+ * Updates display.
+ * Registers a frame buffer device @fb_info.
+ *
+ * Returns negative errno on error, or zero for success.
+ *
+ */
+int fbtft_register_framebuffer(struct fb_info *fb_info)
+{
+ int ret;
+ char text1[50] = "";
+ char text2[50] = "";
+ struct fbtft_par *par = fb_info->par;
+ struct spi_device *spi = par->spi;
+
+ /* sanity checks */
+ if (!par->fbtftops.init_display) {
+ dev_err(fb_info->device, "missing fbtftops.init_display()\n");
+ return -EINVAL;
+ }
+
+ if (spi)
+ spi_set_drvdata(spi, fb_info);
+ if (par->pdev)
+ platform_set_drvdata(par->pdev, fb_info);
+
+ ret = par->fbtftops.request_gpios(par);
+ if (ret < 0)
+ goto reg_fail;
+
+ if (par->fbtftops.verify_gpios) {
+ ret = par->fbtftops.verify_gpios(par);
+ if (ret < 0)
+ goto reg_fail;
+ }
+
+ ret = par->fbtftops.init_display(par);
+ if (ret < 0)
+ goto reg_fail;
+ if (par->fbtftops.set_var) {
+ ret = par->fbtftops.set_var(par);
+ if (ret < 0)
+ goto reg_fail;
+ }
+
+ /* update the entire display */
+ par->fbtftops.update_display(par, 0, par->info->var.yres - 1);
+
+ if (par->fbtftops.set_gamma && par->gamma.curves) {
+ ret = par->fbtftops.set_gamma(par, par->gamma.curves);
+ if (ret)
+ goto reg_fail;
+ }
+
+ if (par->fbtftops.register_backlight)
+ par->fbtftops.register_backlight(par);
+
+ ret = register_framebuffer(fb_info);
+ if (ret < 0)
+ goto reg_fail;
+
+ fbtft_sysfs_init(par);
+
+ if (par->txbuf.buf)
+ sprintf(text1, ", %zu KiB %sbuffer memory",
+ par->txbuf.len >> 10, par->txbuf.dma ? "DMA " : "");
+ if (spi)
+ sprintf(text2, ", spi%d.%d at %d MHz", spi->master->bus_num,
+ spi->chip_select, spi->max_speed_hz/1000000);
+ dev_info(fb_info->dev,
+ "%s frame buffer, %dx%d, %d KiB video memory%s, fps=%lu%s\n",
+ fb_info->fix.id, fb_info->var.xres, fb_info->var.yres,
+ fb_info->fix.smem_len >> 10, text1,
+ HZ/fb_info->fbdefio->delay, text2);
+
+#ifdef CONFIG_FB_BACKLIGHT
+ /* Turn on backlight if available */
+ if (fb_info->bl_dev) {
+ fb_info->bl_dev->props.power = FB_BLANK_UNBLANK;
+ fb_info->bl_dev->ops->update_status(fb_info->bl_dev);
+ }
+#endif
+
+ return 0;
+
+reg_fail:
+ if (par->fbtftops.unregister_backlight)
+ par->fbtftops.unregister_backlight(par);
+ if (spi)
+ spi_set_drvdata(spi, NULL);
+ if (par->pdev)
+ platform_set_drvdata(par->pdev, NULL);
+
+ return ret;
+}
+EXPORT_SYMBOL(fbtft_register_framebuffer);
+
+/**
+ * fbtft_unregister_framebuffer - releases a tft frame buffer device
+ * @fb_info: frame buffer info structure
+ *
+ * Frees SPI driverdata if needed
+ * Frees gpios.
+ * Unregisters frame buffer device.
+ *
+ */
+int fbtft_unregister_framebuffer(struct fb_info *fb_info)
+{
+ struct fbtft_par *par = fb_info->par;
+ struct spi_device *spi = par->spi;
+
+ if (spi)
+ spi_set_drvdata(spi, NULL);
+ if (par->pdev)
+ platform_set_drvdata(par->pdev, NULL);
+ if (par->fbtftops.unregister_backlight)
+ par->fbtftops.unregister_backlight(par);
+ fbtft_sysfs_exit(par);
+ return unregister_framebuffer(fb_info);
+}
+EXPORT_SYMBOL(fbtft_unregister_framebuffer);
+
+#ifdef CONFIG_OF
+/**
+ * fbtft_init_display_dt() - Device Tree init_display() function
+ * @par: Driver data
+ *
+ * Return: 0 if successful, negative if error
+ */
+static int fbtft_init_display_dt(struct fbtft_par *par)
+{
+ struct device_node *node = par->info->device->of_node;
+ struct property *prop;
+ const __be32 *p;
+ u32 val;
+ int buf[64], i, j;
+ char msg[128];
+ char str[16];
+
+ fbtft_par_dbg(DEBUG_INIT_DISPLAY, par, "%s()\n", __func__);
+
+ if (!node)
+ return -EINVAL;
+
+ prop = of_find_property(node, "init", NULL);
+ p = of_prop_next_u32(prop, NULL, &val);
+ if (!p)
+ return -EINVAL;
+ while (p) {
+ if (val & FBTFT_OF_INIT_CMD) {
+ val &= 0xFFFF;
+ i = 0;
+ while (p && !(val & 0xFFFF0000)) {
+ if (i > 63) {
+ dev_err(par->info->device,
+ "%s: Maximum register values exceeded\n",
+ __func__);
+ return -EINVAL;
+ }
+ buf[i++] = val;
+ p = of_prop_next_u32(prop, p, &val);
+ }
+ /* make debug message */
+ msg[0] = '\0';
+ for (j = 0; j < i; j++) {
+ snprintf(str, 128, " %02X", buf[j]);
+ strcat(msg, str);
+ }
+ fbtft_par_dbg(DEBUG_INIT_DISPLAY, par,
+ "init: write_register:%s\n", msg);
+
+ par->fbtftops.write_register(par, i,
+ buf[0], buf[1], buf[2], buf[3],
+ buf[4], buf[5], buf[6], buf[7],
+ buf[8], buf[9], buf[10], buf[11],
+ buf[12], buf[13], buf[14], buf[15],
+ buf[16], buf[17], buf[18], buf[19],
+ buf[20], buf[21], buf[22], buf[23],
+ buf[24], buf[25], buf[26], buf[27],
+ buf[28], buf[29], buf[30], buf[31],
+ buf[32], buf[33], buf[34], buf[35],
+ buf[36], buf[37], buf[38], buf[39],
+ buf[40], buf[41], buf[42], buf[43],
+ buf[44], buf[45], buf[46], buf[47],
+ buf[48], buf[49], buf[50], buf[51],
+ buf[52], buf[53], buf[54], buf[55],
+ buf[56], buf[57], buf[58], buf[59],
+ buf[60], buf[61], buf[62], buf[63]);
+ } else if (val & FBTFT_OF_INIT_DELAY) {
+ fbtft_par_dbg(DEBUG_INIT_DISPLAY, par,
+ "init: msleep(%u)\n", val & 0xFFFF);
+ msleep(val & 0xFFFF);
+ p = of_prop_next_u32(prop, p, &val);
+ } else {
+ dev_err(par->info->device, "illegal init value 0x%X\n",
+ val);
+ return -EINVAL;
+ }
+ }
+
+ return 0;
+}
+#endif
+
+/**
+ * fbtft_init_display() - Generic init_display() function
+ * @par: Driver data
+ *
+ * Uses par->init_sequence to do the initialization
+ *
+ * Return: 0 if successful, negative if error
+ */
+int fbtft_init_display(struct fbtft_par *par)
+{
+ int buf[64];
+ char msg[128];
+ char str[16];
+ int i = 0;
+ int j;
+
+ fbtft_par_dbg(DEBUG_INIT_DISPLAY, par, "%s()\n", __func__);
+
+ /* sanity check */
+ if (!par->init_sequence) {
+ dev_err(par->info->device,
+ "error: init_sequence is not set\n");
+ return -EINVAL;
+ }
+
+ /* make sure stop marker exists */
+ for (i = 0; i < FBTFT_MAX_INIT_SEQUENCE; i++)
+ if (par->init_sequence[i] == -3)
+ break;
+ if (i == FBTFT_MAX_INIT_SEQUENCE) {
+ dev_err(par->info->device,
+ "missing stop marker at end of init sequence\n");
+ return -EINVAL;
+ }
+
+ par->fbtftops.reset(par);
+ if (par->gpio.cs != -1)
+ gpio_set_value(par->gpio.cs, 0); /* Activate chip */
+
+ i = 0;
+ while (i < FBTFT_MAX_INIT_SEQUENCE) {
+ if (par->init_sequence[i] == -3) {
+ /* done */
+ return 0;
+ }
+ if (par->init_sequence[i] >= 0) {
+ dev_err(par->info->device,
+ "missing delimiter at position %d\n", i);
+ return -EINVAL;
+ }
+ if (par->init_sequence[i+1] < 0) {
+ dev_err(par->info->device,
+ "missing value after delimiter %d at position %d\n",
+ par->init_sequence[i], i);
+ return -EINVAL;
+ }
+ switch (par->init_sequence[i]) {
+ case -1:
+ i++;
+ /* make debug message */
+ strcpy(msg, "");
+ j = i + 1;
+ while (par->init_sequence[j] >= 0) {
+ sprintf(str, "0x%02X ", par->init_sequence[j]);
+ strcat(msg, str);
+ j++;
+ }
+ fbtft_par_dbg(DEBUG_INIT_DISPLAY, par,
+ "init: write(0x%02X) %s\n",
+ par->init_sequence[i], msg);
+
+ /* Write */
+ j = 0;
+ while (par->init_sequence[i] >= 0) {
+ if (j > 63) {
+ dev_err(par->info->device,
+ "%s: Maximum register values exceeded\n",
+ __func__);
+ return -EINVAL;
+ }
+ buf[j++] = par->init_sequence[i++];
+ }
+ par->fbtftops.write_register(par, j,
+ buf[0], buf[1], buf[2], buf[3],
+ buf[4], buf[5], buf[6], buf[7],
+ buf[8], buf[9], buf[10], buf[11],
+ buf[12], buf[13], buf[14], buf[15],
+ buf[16], buf[17], buf[18], buf[19],
+ buf[20], buf[21], buf[22], buf[23],
+ buf[24], buf[25], buf[26], buf[27],
+ buf[28], buf[29], buf[30], buf[31],
+ buf[32], buf[33], buf[34], buf[35],
+ buf[36], buf[37], buf[38], buf[39],
+ buf[40], buf[41], buf[42], buf[43],
+ buf[44], buf[45], buf[46], buf[47],
+ buf[48], buf[49], buf[50], buf[51],
+ buf[52], buf[53], buf[54], buf[55],
+ buf[56], buf[57], buf[58], buf[59],
+ buf[60], buf[61], buf[62], buf[63]);
+ break;
+ case -2:
+ i++;
+ fbtft_par_dbg(DEBUG_INIT_DISPLAY, par,
+ "init: mdelay(%d)\n", par->init_sequence[i]);
+ mdelay(par->init_sequence[i++]);
+ break;
+ default:
+ dev_err(par->info->device,
+ "unknown delimiter %d at position %d\n",
+ par->init_sequence[i], i);
+ return -EINVAL;
+ }
+ }
+
+ dev_err(par->info->device,
+ "%s: something is wrong. Shouldn't get here.\n", __func__);
+ return -EINVAL;
+}
+EXPORT_SYMBOL(fbtft_init_display);
+
+/**
+ * fbtft_verify_gpios() - Generic verify_gpios() function
+ * @par: Driver data
+ *
+ * Uses @spi, @pdev and @buswidth to determine which GPIOs is needed
+ *
+ * Return: 0 if successful, negative if error
+ */
+static int fbtft_verify_gpios(struct fbtft_par *par)
+{
+ struct fbtft_platform_data *pdata;
+ int i;
+
+ fbtft_par_dbg(DEBUG_VERIFY_GPIOS, par, "%s()\n", __func__);
+
+ pdata = par->info->device->platform_data;
+ if (pdata->display.buswidth != 9 && par->startbyte == 0 &&
+ par->gpio.dc < 0) {
+ dev_err(par->info->device,
+ "Missing info about 'dc' gpio. Aborting.\n");
+ return -EINVAL;
+ }
+
+ if (!par->pdev)
+ return 0;
+
+ if (par->gpio.wr < 0) {
+ dev_err(par->info->device, "Missing 'wr' gpio. Aborting.\n");
+ return -EINVAL;
+ }
+ for (i = 0; i < pdata->display.buswidth; i++) {
+ if (par->gpio.db[i] < 0) {
+ dev_err(par->info->device,
+ "Missing 'db%02d' gpio. Aborting.\n", i);
+ return -EINVAL;
+ }
+ }
+
+ return 0;
+}
+
+#ifdef CONFIG_OF
+/* returns 0 if the property is not present */
+static u32 fbtft_of_value(struct device_node *node, const char *propname)
+{
+ int ret;
+ u32 val = 0;
+
+ ret = of_property_read_u32(node, propname, &val);
+ if (ret == 0)
+ pr_info("%s: %s = %u\n", __func__, propname, val);
+
+ return val;
+}
+
+static struct fbtft_platform_data *fbtft_probe_dt(struct device *dev)
+{
+ struct device_node *node = dev->of_node;
+ struct fbtft_platform_data *pdata;
+
+ if (!node) {
+ dev_err(dev, "Missing platform data or DT\n");
+ return ERR_PTR(-EINVAL);
+ }
+
+ pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
+ if (!pdata)
+ return ERR_PTR(-ENOMEM);
+
+ pdata->display.width = fbtft_of_value(node, "width");
+ pdata->display.height = fbtft_of_value(node, "height");
+ pdata->display.regwidth = fbtft_of_value(node, "regwidth");
+ pdata->display.buswidth = fbtft_of_value(node, "buswidth");
+ pdata->display.backlight = fbtft_of_value(node, "backlight");
+ pdata->display.bpp = fbtft_of_value(node, "bpp");
+ pdata->display.debug = fbtft_of_value(node, "debug");
+ pdata->rotate = fbtft_of_value(node, "rotate");
+ pdata->bgr = of_property_read_bool(node, "bgr");
+ pdata->fps = fbtft_of_value(node, "fps");
+ pdata->txbuflen = fbtft_of_value(node, "txbuflen");
+ pdata->startbyte = fbtft_of_value(node, "startbyte");
+ of_property_read_string(node, "gamma", (const char **)&pdata->gamma);
+
+ if (of_find_property(node, "led-gpios", NULL))
+ pdata->display.backlight = 1;
+ if (of_find_property(node, "init", NULL))
+ pdata->display.fbtftops.init_display = fbtft_init_display_dt;
+ pdata->display.fbtftops.request_gpios = fbtft_request_gpios_dt;
+
+ return pdata;
+}
+#else
+static struct fbtft_platform_data *fbtft_probe_dt(struct device *dev)
+{
+ dev_err(dev, "Missing platform data\n");
+ return ERR_PTR(-EINVAL);
+}
+#endif
+
+/**
+ * fbtft_probe_common() - Generic device probe() helper function
+ * @display: Display properties
+ * @sdev: SPI device
+ * @pdev: Platform device
+ *
+ * Allocates, initializes and registers a framebuffer
+ *
+ * Either @sdev or @pdev should be NULL
+ *
+ * Return: 0 if successful, negative if error
+ */
+int fbtft_probe_common(struct fbtft_display *display,
+ struct spi_device *sdev, struct platform_device *pdev)
+{
+ struct device *dev;
+ struct fb_info *info;
+ struct fbtft_par *par;
+ struct fbtft_platform_data *pdata;
+ int ret;
+
+ if (sdev)
+ dev = &sdev->dev;
+ else
+ dev = &pdev->dev;
+
+ if (unlikely(display->debug & DEBUG_DRIVER_INIT_FUNCTIONS))
+ dev_info(dev, "%s()\n", __func__);
+
+ pdata = dev->platform_data;
+ if (!pdata) {
+ pdata = fbtft_probe_dt(dev);
+ if (IS_ERR(pdata))
+ return PTR_ERR(pdata);
+ dev->platform_data = pdata;
+ }
+
+ info = fbtft_framebuffer_alloc(display, dev);
+ if (!info)
+ return -ENOMEM;
+
+ par = info->par;
+ par->spi = sdev;
+ par->pdev = pdev;
+
+ if (display->buswidth == 0) {
+ dev_err(dev, "buswidth is not set\n");
+ return -EINVAL;
+ }
+
+ /* write register functions */
+ if (display->regwidth == 8 && display->buswidth == 8) {
+ par->fbtftops.write_register = fbtft_write_reg8_bus8;
+ } else
+ if (display->regwidth == 8 && display->buswidth == 9 && par->spi) {
+ par->fbtftops.write_register = fbtft_write_reg8_bus9;
+ } else if (display->regwidth == 16 && display->buswidth == 8) {
+ par->fbtftops.write_register = fbtft_write_reg16_bus8;
+ } else if (display->regwidth == 16 && display->buswidth == 16) {
+ par->fbtftops.write_register = fbtft_write_reg16_bus16;
+ } else {
+ dev_warn(dev,
+ "no default functions for regwidth=%d and buswidth=%d\n",
+ display->regwidth, display->buswidth);
+ }
+
+ /* write_vmem() functions */
+ if (display->buswidth == 8)
+ par->fbtftops.write_vmem = fbtft_write_vmem16_bus8;
+ else if (display->buswidth == 9)
+ par->fbtftops.write_vmem = fbtft_write_vmem16_bus9;
+ else if (display->buswidth == 16)
+ par->fbtftops.write_vmem = fbtft_write_vmem16_bus16;
+
+ /* GPIO write() functions */
+ if (par->pdev) {
+ if (display->buswidth == 8)
+ par->fbtftops.write = fbtft_write_gpio8_wr;
+ else if (display->buswidth == 16)
+ par->fbtftops.write = fbtft_write_gpio16_wr;
+ }
+
+ /* 9-bit SPI setup */
+ if (par->spi && display->buswidth == 9) {
+ par->spi->bits_per_word = 9;
+ ret = par->spi->master->setup(par->spi);
+ if (ret) {
+ dev_warn(&par->spi->dev,
+ "9-bit SPI not available, emulating using 8-bit.\n");
+ par->spi->bits_per_word = 8;
+ ret = par->spi->master->setup(par->spi);
+ if (ret)
+ goto out_release;
+ /* allocate buffer with room for dc bits */
+ par->extra = devm_kzalloc(par->info->device,
+ par->txbuf.len + (par->txbuf.len / 8) + 8,
+ GFP_KERNEL);
+ if (!par->extra) {
+ ret = -ENOMEM;
+ goto out_release;
+ }
+ par->fbtftops.write = fbtft_write_spi_emulate_9;
+ }
+ }
+
+ if (!par->fbtftops.verify_gpios)
+ par->fbtftops.verify_gpios = fbtft_verify_gpios;
+
+ /* make sure we still use the driver provided functions */
+ fbtft_merge_fbtftops(&par->fbtftops, &display->fbtftops);
+
+ /* use init_sequence if provided */
+ if (par->init_sequence)
+ par->fbtftops.init_display = fbtft_init_display;
+
+ /* use platform_data provided functions above all */
+ fbtft_merge_fbtftops(&par->fbtftops, &pdata->display.fbtftops);
+
+ ret = fbtft_register_framebuffer(info);
+ if (ret < 0)
+ goto out_release;
+
+ return 0;
+
+out_release:
+ fbtft_framebuffer_release(info);
+
+ return ret;
+}
+EXPORT_SYMBOL(fbtft_probe_common);
+
+/**
+ * fbtft_remove_common() - Generic device remove() helper function
+ * @dev: Device
+ * @info: Framebuffer
+ *
+ * Unregisters and releases the framebuffer
+ *
+ * Return: 0 if successful, negative if error
+ */
+int fbtft_remove_common(struct device *dev, struct fb_info *info)
+{
+ struct fbtft_par *par;
+
+ if (!info)
+ return -EINVAL;
+ par = info->par;
+ if (par)
+ fbtft_par_dbg(DEBUG_DRIVER_INIT_FUNCTIONS, par,
+ "%s()\n", __func__);
+ fbtft_unregister_framebuffer(info);
+ fbtft_framebuffer_release(info);
+
+ return 0;
+}
+EXPORT_SYMBOL(fbtft_remove_common);
+
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/fbtft/fbtft-io.c b/drivers/staging/fbtft/fbtft-io.c
new file mode 100644
index 000000000..a6f091fb9
--- /dev/null
+++ b/drivers/staging/fbtft/fbtft-io.c
@@ -0,0 +1,238 @@
+#include <linux/export.h>
+#include <linux/errno.h>
+#include <linux/gpio.h>
+#include <linux/spi/spi.h>
+#include "fbtft.h"
+
+int fbtft_write_spi(struct fbtft_par *par, void *buf, size_t len)
+{
+ struct spi_transfer t = {
+ .tx_buf = buf,
+ .len = len,
+ };
+ struct spi_message m;
+
+ fbtft_par_dbg_hex(DEBUG_WRITE, par, par->info->device, u8, buf, len,
+ "%s(len=%d): ", __func__, len);
+
+ if (!par->spi) {
+ dev_err(par->info->device,
+ "%s: par->spi is unexpectedly NULL\n", __func__);
+ return -1;
+ }
+
+ spi_message_init(&m);
+ if (par->txbuf.dma && buf == par->txbuf.buf) {
+ t.tx_dma = par->txbuf.dma;
+ m.is_dma_mapped = 1;
+ }
+ spi_message_add_tail(&t, &m);
+ return spi_sync(par->spi, &m);
+}
+EXPORT_SYMBOL(fbtft_write_spi);
+
+/**
+ * fbtft_write_spi_emulate_9() - write SPI emulating 9-bit
+ * @par: Driver data
+ * @buf: Buffer to write
+ * @len: Length of buffer (must be divisible by 8)
+ *
+ * When 9-bit SPI is not available, this function can be used to emulate that.
+ * par->extra must hold a transformation buffer used for transfer.
+ */
+int fbtft_write_spi_emulate_9(struct fbtft_par *par, void *buf, size_t len)
+{
+ u16 *src = buf;
+ u8 *dst = par->extra;
+ size_t size = len / 2;
+ size_t added = 0;
+ int bits, i, j;
+ u64 val, dc, tmp;
+
+ fbtft_par_dbg_hex(DEBUG_WRITE, par, par->info->device, u8, buf, len,
+ "%s(len=%d): ", __func__, len);
+
+ if (!par->extra) {
+ dev_err(par->info->device, "%s: error: par->extra is NULL\n",
+ __func__);
+ return -EINVAL;
+ }
+ if ((len % 8) != 0) {
+ dev_err(par->info->device,
+ "error: len=%zu must be divisible by 8\n", len);
+ return -EINVAL;
+ }
+
+ for (i = 0; i < size; i += 8) {
+ tmp = 0;
+ bits = 63;
+ for (j = 0; j < 7; j++) {
+ dc = (*src & 0x0100) ? 1 : 0;
+ val = *src & 0x00FF;
+ tmp |= dc << bits;
+ bits -= 8;
+ tmp |= val << bits--;
+ src++;
+ }
+ tmp |= ((*src & 0x0100) ? 1 : 0);
+ *(u64 *)dst = cpu_to_be64(tmp);
+ dst += 8;
+ *dst++ = (u8)(*src++ & 0x00FF);
+ added++;
+ }
+
+ return spi_write(par->spi, par->extra, size + added);
+}
+EXPORT_SYMBOL(fbtft_write_spi_emulate_9);
+
+int fbtft_read_spi(struct fbtft_par *par, void *buf, size_t len)
+{
+ int ret;
+ u8 txbuf[32] = { 0, };
+ struct spi_transfer t = {
+ .speed_hz = 2000000,
+ .rx_buf = buf,
+ .len = len,
+ };
+ struct spi_message m;
+
+ if (!par->spi) {
+ dev_err(par->info->device,
+ "%s: par->spi is unexpectedly NULL\n", __func__);
+ return -ENODEV;
+ }
+
+ if (par->startbyte) {
+ if (len > 32) {
+ dev_err(par->info->device,
+ "len=%zu can't be larger than 32 when using 'startbyte'\n",
+ len);
+ return -EINVAL;
+ }
+ txbuf[0] = par->startbyte | 0x3;
+ t.tx_buf = txbuf;
+ fbtft_par_dbg_hex(DEBUG_READ, par, par->info->device, u8,
+ txbuf, len, "%s(len=%d) txbuf => ", __func__, len);
+ }
+
+ spi_message_init(&m);
+ spi_message_add_tail(&t, &m);
+ ret = spi_sync(par->spi, &m);
+ fbtft_par_dbg_hex(DEBUG_READ, par, par->info->device, u8, buf, len,
+ "%s(len=%d) buf <= ", __func__, len);
+
+ return ret;
+}
+EXPORT_SYMBOL(fbtft_read_spi);
+
+/*
+ * Optimized use of gpiolib is twice as fast as no optimization
+ * only one driver can use the optimized version at a time
+ */
+int fbtft_write_gpio8_wr(struct fbtft_par *par, void *buf, size_t len)
+{
+ u8 data;
+ int i;
+#ifndef DO_NOT_OPTIMIZE_FBTFT_WRITE_GPIO
+ static u8 prev_data;
+#endif
+
+ fbtft_par_dbg_hex(DEBUG_WRITE, par, par->info->device, u8, buf, len,
+ "%s(len=%d): ", __func__, len);
+
+ while (len--) {
+ data = *(u8 *) buf;
+
+ /* Start writing by pulling down /WR */
+ gpio_set_value(par->gpio.wr, 0);
+
+ /* Set data */
+#ifndef DO_NOT_OPTIMIZE_FBTFT_WRITE_GPIO
+ if (data == prev_data) {
+ gpio_set_value(par->gpio.wr, 0); /* used as delay */
+ } else {
+ for (i = 0; i < 8; i++) {
+ if ((data & 1) != (prev_data & 1))
+ gpio_set_value(par->gpio.db[i],
+ data & 1);
+ data >>= 1;
+ prev_data >>= 1;
+ }
+ }
+#else
+ for (i = 0; i < 8; i++) {
+ gpio_set_value(par->gpio.db[i], data & 1);
+ data >>= 1;
+ }
+#endif
+
+ /* Pullup /WR */
+ gpio_set_value(par->gpio.wr, 1);
+
+#ifndef DO_NOT_OPTIMIZE_FBTFT_WRITE_GPIO
+ prev_data = *(u8 *) buf;
+#endif
+ buf++;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL(fbtft_write_gpio8_wr);
+
+int fbtft_write_gpio16_wr(struct fbtft_par *par, void *buf, size_t len)
+{
+ u16 data;
+ int i;
+#ifndef DO_NOT_OPTIMIZE_FBTFT_WRITE_GPIO
+ static u16 prev_data;
+#endif
+
+ fbtft_par_dbg_hex(DEBUG_WRITE, par, par->info->device, u8, buf, len,
+ "%s(len=%d): ", __func__, len);
+
+ while (len) {
+ data = *(u16 *) buf;
+
+ /* Start writing by pulling down /WR */
+ gpio_set_value(par->gpio.wr, 0);
+
+ /* Set data */
+#ifndef DO_NOT_OPTIMIZE_FBTFT_WRITE_GPIO
+ if (data == prev_data) {
+ gpio_set_value(par->gpio.wr, 0); /* used as delay */
+ } else {
+ for (i = 0; i < 16; i++) {
+ if ((data & 1) != (prev_data & 1))
+ gpio_set_value(par->gpio.db[i],
+ data & 1);
+ data >>= 1;
+ prev_data >>= 1;
+ }
+ }
+#else
+ for (i = 0; i < 16; i++) {
+ gpio_set_value(par->gpio.db[i], data & 1);
+ data >>= 1;
+ }
+#endif
+
+ /* Pullup /WR */
+ gpio_set_value(par->gpio.wr, 1);
+
+#ifndef DO_NOT_OPTIMIZE_FBTFT_WRITE_GPIO
+ prev_data = *(u16 *) buf;
+#endif
+ buf += 2;
+ len -= 2;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL(fbtft_write_gpio16_wr);
+
+int fbtft_write_gpio16_wr_latched(struct fbtft_par *par, void *buf, size_t len)
+{
+ dev_err(par->info->device, "%s: function not implemented\n", __func__);
+ return -1;
+}
+EXPORT_SYMBOL(fbtft_write_gpio16_wr_latched);
diff --git a/drivers/staging/fbtft/fbtft-sysfs.c b/drivers/staging/fbtft/fbtft-sysfs.c
new file mode 100644
index 000000000..c4cc452f9
--- /dev/null
+++ b/drivers/staging/fbtft/fbtft-sysfs.c
@@ -0,0 +1,221 @@
+#include "fbtft.h"
+#include "internal.h"
+
+static int get_next_ulong(char **str_p, unsigned long *val, char *sep, int base)
+{
+ char *p_val;
+ int ret;
+
+ if (!str_p || !(*str_p))
+ return -EINVAL;
+
+ p_val = strsep(str_p, sep);
+
+ if (!p_val)
+ return -EINVAL;
+
+ ret = kstrtoul(p_val, base, val);
+ if (ret)
+ return -EINVAL;
+
+ return 0;
+}
+
+int fbtft_gamma_parse_str(struct fbtft_par *par, unsigned long *curves,
+ const char *str, int size)
+{
+ char *str_p, *curve_p = NULL;
+ char *tmp;
+ unsigned long val = 0;
+ int ret = 0;
+ int curve_counter, value_counter;
+
+ fbtft_par_dbg(DEBUG_SYSFS, par, "%s() str=\n", __func__);
+
+ if (!str || !curves)
+ return -EINVAL;
+
+ fbtft_par_dbg(DEBUG_SYSFS, par, "%s\n", str);
+
+ tmp = kmemdup(str, size + 1, GFP_KERNEL);
+ if (!tmp)
+ return -ENOMEM;
+
+ /* replace optional separators */
+ str_p = tmp;
+ while (*str_p) {
+ if (*str_p == ',')
+ *str_p = ' ';
+ if (*str_p == ';')
+ *str_p = '\n';
+ str_p++;
+ }
+
+ str_p = strim(tmp);
+
+ curve_counter = 0;
+ while (str_p) {
+ if (curve_counter == par->gamma.num_curves) {
+ dev_err(par->info->device, "Gamma: Too many curves\n");
+ ret = -EINVAL;
+ goto out;
+ }
+ curve_p = strsep(&str_p, "\n");
+ value_counter = 0;
+ while (curve_p) {
+ if (value_counter == par->gamma.num_values) {
+ dev_err(par->info->device,
+ "Gamma: Too many values\n");
+ ret = -EINVAL;
+ goto out;
+ }
+ ret = get_next_ulong(&curve_p, &val, " ", 16);
+ if (ret)
+ goto out;
+ curves[curve_counter * par->gamma.num_values + value_counter] = val;
+ value_counter++;
+ }
+ if (value_counter != par->gamma.num_values) {
+ dev_err(par->info->device, "Gamma: Too few values\n");
+ ret = -EINVAL;
+ goto out;
+ }
+ curve_counter++;
+ }
+ if (curve_counter != par->gamma.num_curves) {
+ dev_err(par->info->device, "Gamma: Too few curves\n");
+ ret = -EINVAL;
+ goto out;
+ }
+
+out:
+ kfree(tmp);
+ return ret;
+}
+
+static ssize_t
+sprintf_gamma(struct fbtft_par *par, unsigned long *curves, char *buf)
+{
+ ssize_t len = 0;
+ unsigned int i, j;
+
+ mutex_lock(&par->gamma.lock);
+ for (i = 0; i < par->gamma.num_curves; i++) {
+ for (j = 0; j < par->gamma.num_values; j++)
+ len += scnprintf(&buf[len], PAGE_SIZE,
+ "%04lx ", curves[i*par->gamma.num_values + j]);
+ buf[len-1] = '\n';
+ }
+ mutex_unlock(&par->gamma.lock);
+
+ return len;
+}
+
+static ssize_t store_gamma_curve(struct device *device,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct fb_info *fb_info = dev_get_drvdata(device);
+ struct fbtft_par *par = fb_info->par;
+ unsigned long tmp_curves[FBTFT_GAMMA_MAX_VALUES_TOTAL];
+ int ret;
+
+ ret = fbtft_gamma_parse_str(par, tmp_curves, buf, count);
+ if (ret)
+ return ret;
+
+ ret = par->fbtftops.set_gamma(par, tmp_curves);
+ if (ret)
+ return ret;
+
+ mutex_lock(&par->gamma.lock);
+ memcpy(par->gamma.curves, tmp_curves,
+ par->gamma.num_curves * par->gamma.num_values * sizeof(tmp_curves[0]));
+ mutex_unlock(&par->gamma.lock);
+
+ return count;
+}
+
+static ssize_t show_gamma_curve(struct device *device,
+ struct device_attribute *attr, char *buf)
+{
+ struct fb_info *fb_info = dev_get_drvdata(device);
+ struct fbtft_par *par = fb_info->par;
+
+ return sprintf_gamma(par, par->gamma.curves, buf);
+}
+
+static struct device_attribute gamma_device_attrs[] = {
+ __ATTR(gamma, 0660, show_gamma_curve, store_gamma_curve),
+};
+
+
+void fbtft_expand_debug_value(unsigned long *debug)
+{
+ switch (*debug & 0x7) {
+ case 1:
+ *debug |= DEBUG_LEVEL_1;
+ break;
+ case 2:
+ *debug |= DEBUG_LEVEL_2;
+ break;
+ case 3:
+ *debug |= DEBUG_LEVEL_3;
+ break;
+ case 4:
+ *debug |= DEBUG_LEVEL_4;
+ break;
+ case 5:
+ *debug |= DEBUG_LEVEL_5;
+ break;
+ case 6:
+ *debug |= DEBUG_LEVEL_6;
+ break;
+ case 7:
+ *debug = 0xFFFFFFFF;
+ break;
+ }
+}
+
+static ssize_t store_debug(struct device *device,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct fb_info *fb_info = dev_get_drvdata(device);
+ struct fbtft_par *par = fb_info->par;
+ int ret;
+
+ ret = kstrtoul(buf, 10, &par->debug);
+ if (ret)
+ return ret;
+ fbtft_expand_debug_value(&par->debug);
+
+ return count;
+}
+
+static ssize_t show_debug(struct device *device,
+ struct device_attribute *attr, char *buf)
+{
+ struct fb_info *fb_info = dev_get_drvdata(device);
+ struct fbtft_par *par = fb_info->par;
+
+ return snprintf(buf, PAGE_SIZE, "%lu\n", par->debug);
+}
+
+static struct device_attribute debug_device_attr = \
+ __ATTR(debug, 0660, show_debug, store_debug);
+
+
+void fbtft_sysfs_init(struct fbtft_par *par)
+{
+ device_create_file(par->info->dev, &debug_device_attr);
+ if (par->gamma.curves && par->fbtftops.set_gamma)
+ device_create_file(par->info->dev, &gamma_device_attrs[0]);
+}
+
+void fbtft_sysfs_exit(struct fbtft_par *par)
+{
+ device_remove_file(par->info->dev, &debug_device_attr);
+ if (par->gamma.curves && par->fbtftops.set_gamma)
+ device_remove_file(par->info->dev, &gamma_device_attrs[0]);
+}
diff --git a/drivers/staging/fbtft/fbtft.h b/drivers/staging/fbtft/fbtft.h
new file mode 100644
index 000000000..9fd98cb53
--- /dev/null
+++ b/drivers/staging/fbtft/fbtft.h
@@ -0,0 +1,445 @@
+/*
+ * Copyright (C) 2013 Noralf Tronnes
+ *
+ * This program is free software; you can redistribute it and/or 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 __LINUX_FBTFT_H
+#define __LINUX_FBTFT_H
+
+#include <linux/fb.h>
+#include <linux/spinlock.h>
+#include <linux/spi/spi.h>
+#include <linux/platform_device.h>
+
+
+#define FBTFT_NOP 0x00
+#define FBTFT_SWRESET 0x01
+#define FBTFT_RDDID 0x04
+#define FBTFT_RDDST 0x09
+#define FBTFT_CASET 0x2A
+#define FBTFT_RASET 0x2B
+#define FBTFT_RAMWR 0x2C
+
+#define FBTFT_ONBOARD_BACKLIGHT 2
+
+#define FBTFT_GPIO_NO_MATCH 0xFFFF
+#define FBTFT_GPIO_NAME_SIZE 32
+#define FBTFT_MAX_INIT_SEQUENCE 512
+#define FBTFT_GAMMA_MAX_VALUES_TOTAL 128
+
+#define FBTFT_OF_INIT_CMD BIT(24)
+#define FBTFT_OF_INIT_DELAY BIT(25)
+
+/**
+ * struct fbtft_gpio - Structure that holds one pinname to gpio mapping
+ * @name: pinname (reset, dc, etc.)
+ * @gpio: GPIO number
+ *
+ */
+struct fbtft_gpio {
+ char name[FBTFT_GPIO_NAME_SIZE];
+ unsigned gpio;
+};
+
+struct fbtft_par;
+
+/**
+ * struct fbtft_ops - FBTFT operations structure
+ * @write: Writes to interface bus
+ * @read: Reads from interface bus
+ * @write_vmem: Writes video memory to display
+ * @write_reg: Writes to controller register
+ * @set_addr_win: Set the GRAM update window
+ * @reset: Reset the LCD controller
+ * @mkdirty: Marks display lines for update
+ * @update_display: Updates the display
+ * @init_display: Initializes the display
+ * @blank: Blank the display (optional)
+ * @request_gpios_match: Do pinname to gpio matching
+ * @request_gpios: Request gpios from the kernel
+ * @free_gpios: Free previously requested gpios
+ * @verify_gpios: Verify that necessary gpios is present (optional)
+ * @register_backlight: Used to register backlight device (optional)
+ * @unregister_backlight: Unregister backlight device (optional)
+ * @set_var: Configure LCD with values from variables like @rotate and @bgr
+ * (optional)
+ * @set_gamma: Set Gamma curve (optional)
+ *
+ * Most of these operations have default functions assigned to them in
+ * fbtft_framebuffer_alloc()
+ */
+struct fbtft_ops {
+ int (*write)(struct fbtft_par *par, void *buf, size_t len);
+ int (*read)(struct fbtft_par *par, void *buf, size_t len);
+ int (*write_vmem)(struct fbtft_par *par, size_t offset, size_t len);
+ void (*write_register)(struct fbtft_par *par, int len, ...);
+
+ void (*set_addr_win)(struct fbtft_par *par,
+ int xs, int ys, int xe, int ye);
+ void (*reset)(struct fbtft_par *par);
+ void (*mkdirty)(struct fb_info *info, int from, int to);
+ void (*update_display)(struct fbtft_par *par,
+ unsigned start_line, unsigned end_line);
+ int (*init_display)(struct fbtft_par *par);
+ int (*blank)(struct fbtft_par *par, bool on);
+
+ unsigned long (*request_gpios_match)(struct fbtft_par *par,
+ const struct fbtft_gpio *gpio);
+ int (*request_gpios)(struct fbtft_par *par);
+ int (*verify_gpios)(struct fbtft_par *par);
+
+ void (*register_backlight)(struct fbtft_par *par);
+ void (*unregister_backlight)(struct fbtft_par *par);
+
+ int (*set_var)(struct fbtft_par *par);
+ int (*set_gamma)(struct fbtft_par *par, unsigned long *curves);
+};
+
+/**
+ * struct fbtft_display - Describes the display properties
+ * @width: Width of display in pixels
+ * @height: Height of display in pixels
+ * @regwidth: LCD Controller Register width in bits
+ * @buswidth: Display interface bus width in bits
+ * @backlight: Backlight type.
+ * @fbtftops: FBTFT operations provided by driver or device (platform_data)
+ * @bpp: Bits per pixel
+ * @fps: Frames per second
+ * @txbuflen: Size of transmit buffer
+ * @init_sequence: Pointer to LCD initialization array
+ * @gamma: String representation of Gamma curve(s)
+ * @gamma_num: Number of Gamma curves
+ * @gamma_len: Number of values per Gamma curve
+ * @debug: Initial debug value
+ *
+ * This structure is not stored by FBTFT except for init_sequence.
+ */
+struct fbtft_display {
+ unsigned width;
+ unsigned height;
+ unsigned regwidth;
+ unsigned buswidth;
+ unsigned backlight;
+ struct fbtft_ops fbtftops;
+ unsigned bpp;
+ unsigned fps;
+ int txbuflen;
+ int *init_sequence;
+ char *gamma;
+ int gamma_num;
+ int gamma_len;
+ unsigned long debug;
+};
+
+/**
+ * struct fbtft_platform_data - Passes display specific data to the driver
+ * @display: Display properties
+ * @gpios: Pointer to an array of pinname to gpio mappings
+ * @rotate: Display rotation angle
+ * @bgr: LCD Controller BGR bit
+ * @fps: Frames per second (this will go away, use @fps in @fbtft_display)
+ * @txbuflen: Size of transmit buffer
+ * @startbyte: When set, enables use of Startbyte in transfers
+ * @gamma: String representation of Gamma curve(s)
+ * @extra: A way to pass extra info
+ */
+struct fbtft_platform_data {
+ struct fbtft_display display;
+ const struct fbtft_gpio *gpios;
+ unsigned rotate;
+ bool bgr;
+ unsigned fps;
+ int txbuflen;
+ u8 startbyte;
+ char *gamma;
+ void *extra;
+};
+
+/**
+ * struct fbtft_par - Main FBTFT data structure
+ *
+ * This structure holds all relevant data to operate the display
+ *
+ * See sourcefile for documentation since nested structs is not
+ * supported by kernel-doc.
+ *
+ */
+/* @spi: Set if it is a SPI device
+ * @pdev: Set if it is a platform device
+ * @info: Pointer to framebuffer fb_info structure
+ * @pdata: Pointer to platform data
+ * @ssbuf: Not used
+ * @pseudo_palette: Used by fb_set_colreg()
+ * @txbuf.buf: Transmit buffer
+ * @txbuf.len: Transmit buffer length
+ * @buf: Small buffer used when writing init data over SPI
+ * @startbyte: Used by some controllers when in SPI mode.
+ * Format: 6 bit Device id + RS bit + RW bit
+ * @fbtftops: FBTFT operations provided by driver or device (platform_data)
+ * @dirty_lock: Protects dirty_lines_start and dirty_lines_end
+ * @dirty_lines_start: Where to begin updating display
+ * @dirty_lines_end: Where to end updating display
+ * @gpio.reset: GPIO used to reset display
+ * @gpio.dc: Data/Command signal, also known as RS
+ * @gpio.rd: Read latching signal
+ * @gpio.wr: Write latching signal
+ * @gpio.latch: Bus latch signal, eg. 16->8 bit bus latch
+ * @gpio.cs: LCD Chip Select with parallel interface bus
+ * @gpio.db[16]: Parallel databus
+ * @gpio.led[16]: Led control signals
+ * @gpio.aux[16]: Auxiliary signals, not used by core
+ * @init_sequence: Pointer to LCD initialization array
+ * @gamma.lock: Mutex for Gamma curve locking
+ * @gamma.curves: Pointer to Gamma curve array
+ * @gamma.num_values: Number of values per Gamma curve
+ * @gamma.num_curves: Number of Gamma curves
+ * @debug: Pointer to debug value
+ * @current_debug:
+ * @first_update_done: Used to only time the first display update
+ * @update_time: Used to calculate 'fps' in debug output
+ * @bgr: BGR mode/\n
+ * @extra: Extra info needed by driver
+ */
+struct fbtft_par {
+ struct spi_device *spi;
+ struct platform_device *pdev;
+ struct fb_info *info;
+ struct fbtft_platform_data *pdata;
+ u16 *ssbuf;
+ u32 pseudo_palette[16];
+ struct {
+ void *buf;
+ dma_addr_t dma;
+ size_t len;
+ } txbuf;
+ u8 *buf;
+ u8 startbyte;
+ struct fbtft_ops fbtftops;
+ spinlock_t dirty_lock;
+ unsigned dirty_lines_start;
+ unsigned dirty_lines_end;
+ struct {
+ int reset;
+ int dc;
+ int rd;
+ int wr;
+ int latch;
+ int cs;
+ int db[16];
+ int led[16];
+ int aux[16];
+ } gpio;
+ int *init_sequence;
+ struct {
+ struct mutex lock;
+ unsigned long *curves;
+ int num_values;
+ int num_curves;
+ } gamma;
+ unsigned long debug;
+ bool first_update_done;
+ struct timespec update_time;
+ bool bgr;
+ void *extra;
+};
+
+#define NUMARGS(...) (sizeof((int[]){__VA_ARGS__})/sizeof(int))
+
+#define write_reg(par, ...) \
+ par->fbtftops.write_register(par, NUMARGS(__VA_ARGS__), __VA_ARGS__)
+
+/* fbtft-core.c */
+extern void fbtft_dbg_hex(const struct device *dev,
+ int groupsize, void *buf, size_t len, const char *fmt, ...);
+extern struct fb_info *fbtft_framebuffer_alloc(struct fbtft_display *display,
+ struct device *dev);
+extern void fbtft_framebuffer_release(struct fb_info *info);
+extern int fbtft_register_framebuffer(struct fb_info *fb_info);
+extern int fbtft_unregister_framebuffer(struct fb_info *fb_info);
+extern void fbtft_register_backlight(struct fbtft_par *par);
+extern void fbtft_unregister_backlight(struct fbtft_par *par);
+extern int fbtft_init_display(struct fbtft_par *par);
+extern int fbtft_probe_common(struct fbtft_display *display,
+ struct spi_device *sdev, struct platform_device *pdev);
+extern int fbtft_remove_common(struct device *dev, struct fb_info *info);
+
+/* fbtft-io.c */
+extern int fbtft_write_spi(struct fbtft_par *par, void *buf, size_t len);
+extern int fbtft_write_spi_emulate_9(struct fbtft_par *par,
+ void *buf, size_t len);
+extern int fbtft_read_spi(struct fbtft_par *par, void *buf, size_t len);
+extern int fbtft_write_gpio8_wr(struct fbtft_par *par, void *buf, size_t len);
+extern int fbtft_write_gpio16_wr(struct fbtft_par *par, void *buf, size_t len);
+extern int fbtft_write_gpio16_wr_latched(struct fbtft_par *par,
+ void *buf, size_t len);
+
+/* fbtft-bus.c */
+extern int fbtft_write_vmem8_bus8(struct fbtft_par *par, size_t offset, size_t len);
+extern int fbtft_write_vmem16_bus16(struct fbtft_par *par, size_t offset, size_t len);
+extern int fbtft_write_vmem16_bus8(struct fbtft_par *par, size_t offset, size_t len);
+extern int fbtft_write_vmem16_bus9(struct fbtft_par *par, size_t offset, size_t len);
+extern void fbtft_write_reg8_bus8(struct fbtft_par *par, int len, ...);
+extern void fbtft_write_reg8_bus9(struct fbtft_par *par, int len, ...);
+extern void fbtft_write_reg16_bus8(struct fbtft_par *par, int len, ...);
+extern void fbtft_write_reg16_bus16(struct fbtft_par *par, int len, ...);
+
+
+#define FBTFT_REGISTER_DRIVER(_name, _compatible, _display) \
+ \
+static int fbtft_driver_probe_spi(struct spi_device *spi) \
+{ \
+ return fbtft_probe_common(_display, spi, NULL); \
+} \
+ \
+static int fbtft_driver_remove_spi(struct spi_device *spi) \
+{ \
+ struct fb_info *info = spi_get_drvdata(spi); \
+ \
+ return fbtft_remove_common(&spi->dev, info); \
+} \
+ \
+static int fbtft_driver_probe_pdev(struct platform_device *pdev) \
+{ \
+ return fbtft_probe_common(_display, NULL, pdev); \
+} \
+ \
+static int fbtft_driver_remove_pdev(struct platform_device *pdev) \
+{ \
+ struct fb_info *info = platform_get_drvdata(pdev); \
+ \
+ return fbtft_remove_common(&pdev->dev, info); \
+} \
+ \
+static const struct of_device_id dt_ids[] = { \
+ { .compatible = _compatible }, \
+ {}, \
+}; \
+ \
+MODULE_DEVICE_TABLE(of, dt_ids); \
+ \
+ \
+static struct spi_driver fbtft_driver_spi_driver = { \
+ .driver = { \
+ .name = _name, \
+ .owner = THIS_MODULE, \
+ .of_match_table = of_match_ptr(dt_ids), \
+ }, \
+ .probe = fbtft_driver_probe_spi, \
+ .remove = fbtft_driver_remove_spi, \
+}; \
+ \
+static struct platform_driver fbtft_driver_platform_driver = { \
+ .driver = { \
+ .name = _name, \
+ .owner = THIS_MODULE, \
+ .of_match_table = of_match_ptr(dt_ids), \
+ }, \
+ .probe = fbtft_driver_probe_pdev, \
+ .remove = fbtft_driver_remove_pdev, \
+}; \
+ \
+static int __init fbtft_driver_module_init(void) \
+{ \
+ int ret; \
+ \
+ ret = spi_register_driver(&fbtft_driver_spi_driver); \
+ if (ret < 0) \
+ return ret; \
+ return platform_driver_register(&fbtft_driver_platform_driver); \
+} \
+ \
+static void __exit fbtft_driver_module_exit(void) \
+{ \
+ spi_unregister_driver(&fbtft_driver_spi_driver); \
+ platform_driver_unregister(&fbtft_driver_platform_driver); \
+} \
+ \
+module_init(fbtft_driver_module_init); \
+module_exit(fbtft_driver_module_exit);
+
+
+/* Debug macros */
+
+/* shorthand debug levels */
+#define DEBUG_LEVEL_1 DEBUG_REQUEST_GPIOS
+#define DEBUG_LEVEL_2 (DEBUG_LEVEL_1 | DEBUG_DRIVER_INIT_FUNCTIONS | DEBUG_TIME_FIRST_UPDATE)
+#define DEBUG_LEVEL_3 (DEBUG_LEVEL_2 | DEBUG_RESET | DEBUG_INIT_DISPLAY | DEBUG_BLANK | DEBUG_REQUEST_GPIOS | DEBUG_FREE_GPIOS | DEBUG_VERIFY_GPIOS | DEBUG_BACKLIGHT | DEBUG_SYSFS)
+#define DEBUG_LEVEL_4 (DEBUG_LEVEL_2 | DEBUG_FB_READ | DEBUG_FB_WRITE | DEBUG_FB_FILLRECT | DEBUG_FB_COPYAREA | DEBUG_FB_IMAGEBLIT | DEBUG_FB_BLANK)
+#define DEBUG_LEVEL_5 (DEBUG_LEVEL_3 | DEBUG_UPDATE_DISPLAY)
+#define DEBUG_LEVEL_6 (DEBUG_LEVEL_4 | DEBUG_LEVEL_5)
+#define DEBUG_LEVEL_7 0xFFFFFFFF
+
+#define DEBUG_DRIVER_INIT_FUNCTIONS (1<<3)
+#define DEBUG_TIME_FIRST_UPDATE (1<<4)
+#define DEBUG_TIME_EACH_UPDATE (1<<5)
+#define DEBUG_DEFERRED_IO (1<<6)
+#define DEBUG_FBTFT_INIT_FUNCTIONS (1<<7)
+
+/* fbops */
+#define DEBUG_FB_READ (1<<8)
+#define DEBUG_FB_WRITE (1<<9)
+#define DEBUG_FB_FILLRECT (1<<10)
+#define DEBUG_FB_COPYAREA (1<<11)
+#define DEBUG_FB_IMAGEBLIT (1<<12)
+#define DEBUG_FB_SETCOLREG (1<<13)
+#define DEBUG_FB_BLANK (1<<14)
+
+#define DEBUG_SYSFS (1<<16)
+
+/* fbtftops */
+#define DEBUG_BACKLIGHT (1<<17)
+#define DEBUG_READ (1<<18)
+#define DEBUG_WRITE (1<<19)
+#define DEBUG_WRITE_VMEM (1<<20)
+#define DEBUG_WRITE_REGISTER (1<<21)
+#define DEBUG_SET_ADDR_WIN (1<<22)
+#define DEBUG_RESET (1<<23)
+#define DEBUG_MKDIRTY (1<<24)
+#define DEBUG_UPDATE_DISPLAY (1<<25)
+#define DEBUG_INIT_DISPLAY (1<<26)
+#define DEBUG_BLANK (1<<27)
+#define DEBUG_REQUEST_GPIOS (1<<28)
+#define DEBUG_FREE_GPIOS (1<<29)
+#define DEBUG_REQUEST_GPIOS_MATCH (1<<30)
+#define DEBUG_VERIFY_GPIOS (1<<31)
+
+
+#define fbtft_init_dbg(dev, format, arg...) \
+do { \
+ if (unlikely((dev)->platform_data && \
+ (((struct fbtft_platform_data *)(dev)->platform_data)->display.debug & DEBUG_DRIVER_INIT_FUNCTIONS))) \
+ dev_info(dev, format, ##arg); \
+} while (0)
+
+#define fbtft_par_dbg(level, par, format, arg...) \
+do { \
+ if (unlikely(par->debug & level)) \
+ dev_info(par->info->device, format, ##arg); \
+} while (0)
+
+#define fbtft_dev_dbg(level, par, dev, format, arg...) \
+do { \
+ if (unlikely(par->debug & level)) \
+ dev_info(dev, format, ##arg); \
+} while (0)
+
+#define fbtft_par_dbg_hex(level, par, dev, type, buf, num, format, arg...) \
+do { \
+ if (unlikely(par->debug & level)) \
+ fbtft_dbg_hex(dev, sizeof(type), buf, num * sizeof(type), format, ##arg); \
+} while (0)
+
+#endif /* __LINUX_FBTFT_H */
diff --git a/drivers/staging/fbtft/fbtft_device.c b/drivers/staging/fbtft/fbtft_device.c
new file mode 100644
index 000000000..df6cd775a
--- /dev/null
+++ b/drivers/staging/fbtft/fbtft_device.c
@@ -0,0 +1,1488 @@
+/*
+ *
+ * Copyright (C) 2013, Noralf Tronnes
+ *
+ * This program is free software; you can redistribute it and/or 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/kernel.h>
+#include <linux/init.h>
+#include <linux/gpio.h>
+#include <linux/spi/spi.h>
+
+#include "fbtft.h"
+
+#define DRVNAME "fbtft_device"
+
+#define MAX_GPIOS 32
+
+static struct spi_device *spi_device;
+static struct platform_device *p_device;
+
+static char *name;
+module_param(name, charp, 0);
+MODULE_PARM_DESC(name, "Devicename (required). " \
+"name=list => list all supported devices.");
+
+static unsigned rotate;
+module_param(rotate, uint, 0);
+MODULE_PARM_DESC(rotate,
+"Angle to rotate display counter clockwise: 0, 90, 180, 270");
+
+static unsigned busnum;
+module_param(busnum, uint, 0);
+MODULE_PARM_DESC(busnum, "SPI bus number (default=0)");
+
+static unsigned cs;
+module_param(cs, uint, 0);
+MODULE_PARM_DESC(cs, "SPI chip select (default=0)");
+
+static unsigned speed;
+module_param(speed, uint, 0);
+MODULE_PARM_DESC(speed, "SPI speed (override device default)");
+
+static int mode = -1;
+module_param(mode, int, 0);
+MODULE_PARM_DESC(mode, "SPI mode (override device default)");
+
+static char *gpios;
+module_param(gpios, charp, 0);
+MODULE_PARM_DESC(gpios,
+"List of gpios. Comma separated with the form: reset:23,dc:24 " \
+"(when overriding the default, all gpios must be specified)");
+
+static unsigned fps;
+module_param(fps, uint, 0);
+MODULE_PARM_DESC(fps, "Frames per second (override driver default)");
+
+static char *gamma;
+module_param(gamma, charp, 0);
+MODULE_PARM_DESC(gamma,
+"String representation of Gamma Curve(s). Driver specific.");
+
+static int txbuflen;
+module_param(txbuflen, int, 0);
+MODULE_PARM_DESC(txbuflen, "txbuflen (override driver default)");
+
+static int bgr = -1;
+module_param(bgr, int, 0);
+MODULE_PARM_DESC(bgr,
+"BGR bit (supported by some drivers).");
+
+static unsigned startbyte;
+module_param(startbyte, uint, 0);
+MODULE_PARM_DESC(startbyte, "Sets the Start byte used by some SPI displays.");
+
+static bool custom;
+module_param(custom, bool, 0);
+MODULE_PARM_DESC(custom, "Add a custom display device. " \
+"Use speed= argument to make it a SPI device, else platform_device");
+
+static unsigned width;
+module_param(width, uint, 0);
+MODULE_PARM_DESC(width, "Display width, used with the custom argument");
+
+static unsigned height;
+module_param(height, uint, 0);
+MODULE_PARM_DESC(height, "Display height, used with the custom argument");
+
+static unsigned buswidth = 8;
+module_param(buswidth, uint, 0);
+MODULE_PARM_DESC(buswidth, "Display bus width, used with the custom argument");
+
+static int init[FBTFT_MAX_INIT_SEQUENCE];
+static int init_num;
+module_param_array(init, int, &init_num, 0);
+MODULE_PARM_DESC(init, "Init sequence, used with the custom argument");
+
+static unsigned long debug;
+module_param(debug, ulong, 0);
+MODULE_PARM_DESC(debug,
+"level: 0-7 (the remaining 29 bits is for advanced usage)");
+
+static unsigned verbose = 3;
+module_param(verbose, uint, 0);
+MODULE_PARM_DESC(verbose,
+"0 silent, >0 show gpios, >1 show devices, >2 show devices before (default=3)");
+
+
+struct fbtft_device_display {
+ char *name;
+ struct spi_board_info *spi;
+ struct platform_device *pdev;
+};
+
+static void fbtft_device_pdev_release(struct device *dev);
+
+static int write_gpio16_wr_slow(struct fbtft_par *par, void *buf, size_t len);
+static void adafruit18_green_tab_set_addr_win(struct fbtft_par *par,
+ int xs, int ys, int xe, int ye);
+
+#define ADAFRUIT18_GAMMA \
+ "02 1c 07 12 37 32 29 2d 29 25 2B 39 00 01 03 10\n" \
+ "03 1d 07 06 2E 2C 29 2D 2E 2E 37 3F 00 00 02 10"
+
+static int hy28b_init_sequence[] = {
+ -1, 0x00e7, 0x0010, -1, 0x0000, 0x0001,
+ -1, 0x0001, 0x0100, -1, 0x0002, 0x0700,
+ -1, 0x0003, 0x1030, -1, 0x0004, 0x0000,
+ -1, 0x0008, 0x0207, -1, 0x0009, 0x0000,
+ -1, 0x000a, 0x0000, -1, 0x000c, 0x0001,
+ -1, 0x000d, 0x0000, -1, 0x000f, 0x0000,
+ -1, 0x0010, 0x0000, -1, 0x0011, 0x0007,
+ -1, 0x0012, 0x0000, -1, 0x0013, 0x0000,
+ -2, 50, -1, 0x0010, 0x1590, -1, 0x0011,
+ 0x0227, -2, 50, -1, 0x0012, 0x009c, -2, 50,
+ -1, 0x0013, 0x1900, -1, 0x0029, 0x0023,
+ -1, 0x002b, 0x000e, -2, 50,
+ -1, 0x0020, 0x0000, -1, 0x0021, 0x0000,
+ -2, 50, -1, 0x0050, 0x0000,
+ -1, 0x0051, 0x00ef, -1, 0x0052, 0x0000,
+ -1, 0x0053, 0x013f, -1, 0x0060, 0xa700,
+ -1, 0x0061, 0x0001, -1, 0x006a, 0x0000,
+ -1, 0x0080, 0x0000, -1, 0x0081, 0x0000,
+ -1, 0x0082, 0x0000, -1, 0x0083, 0x0000,
+ -1, 0x0084, 0x0000, -1, 0x0085, 0x0000,
+ -1, 0x0090, 0x0010, -1, 0x0092, 0x0000,
+ -1, 0x0093, 0x0003, -1, 0x0095, 0x0110,
+ -1, 0x0097, 0x0000, -1, 0x0098, 0x0000,
+ -1, 0x0007, 0x0133, -1, 0x0020, 0x0000,
+ -1, 0x0021, 0x0000, -2, 100, -3 };
+
+#define HY28B_GAMMA \
+ "04 1F 4 7 7 0 7 7 6 0\n" \
+ "0F 00 1 7 4 0 0 0 6 7"
+
+static int pitft_init_sequence[] = {
+ -1, 0x01, -2, 5, -1, 0x28, -1, 0xEF,
+ 0x03, 0x80, 0x02, -1, 0xCF, 0x00, 0xC1, 0x30,
+ -1, 0xED, 0x64, 0x03, 0x12, 0x81,
+ -1, 0xE8, 0x85, 0x00, 0x78,
+ -1, 0xCB, 0x39, 0x2C, 0x00, 0x34, 0x02,
+ -1, 0xF7, 0x20, -1, 0xEA, 0x00, 0x00,
+ -1, 0xC0, 0x23, -1, 0xC1, 0x10, -1, 0xC5,
+ 0x3e, 0x28, -1, 0xC7, 0x86, -1, 0x3A, 0x55,
+ -1, 0xB1, 0x00, 0x18, -1, 0xB6, 0x08, 0x82,
+ 0x27, -1, 0xF2, 0x00, -1, 0x26, 0x01,
+ -1, 0xE0, 0x0F, 0x31, 0x2B, 0x0C, 0x0E, 0x08,
+ 0x4E, 0xF1, 0x37, 0x07, 0x10, 0x03,
+ 0x0E, 0x09, 0x00, -1, 0xE1, 0x00, 0x0E, 0x14,
+ 0x03, 0x11, 0x07, 0x31, 0xC1, 0x48,
+ 0x08, 0x0F, 0x0C, 0x31, 0x36, 0x0F, -1,
+ 0x11, -2, 100, -1, 0x29, -2, 20, -3 };
+
+static int waveshare32b_init_sequence[] = {
+ -1, 0xCB, 0x39, 0x2C, 0x00, 0x34, 0x02,
+ -1, 0xCF, 0x00, 0xC1, 0x30,
+ -1, 0xE8, 0x85, 0x00, 0x78, -1, 0xEA, 0x00,
+ 0x00, -1, 0xED, 0x64, 0x03, 0x12, 0x81,
+ -1, 0xF7, 0x20, -1, 0xC0, 0x23, -1, 0xC1,
+ 0x10, -1, 0xC5, 0x3e, 0x28, -1, 0xC7, 0x86,
+ -1, 0x36, 0x28, -1, 0x3A, 0x55, -1, 0xB1, 0x00,
+ 0x18, -1, 0xB6, 0x08, 0x82, 0x27,
+ -1, 0xF2, 0x00, -1, 0x26, 0x01,
+ -1, 0xE0, 0x0F, 0x31, 0x2B, 0x0C, 0x0E, 0x08, 0x4E,
+ 0xF1, 0x37, 0x07, 0x10, 0x03, 0x0E, 0x09, 0x00,
+ -1, 0xE1, 0x00, 0x0E, 0x14, 0x03, 0x11, 0x07, 0x31,
+ 0xC1, 0x48, 0x08, 0x0F, 0x0C, 0x31, 0x36, 0x0F,
+ -1, 0x11, -2, 120, -1, 0x29, -1, 0x2c, -3 };
+
+/* Supported displays in alphabetical order */
+static struct fbtft_device_display displays[] = {
+ {
+ .name = "adafruit18",
+ .spi = &(struct spi_board_info) {
+ .modalias = "fb_st7735r",
+ .max_speed_hz = 32000000,
+ .mode = SPI_MODE_0,
+ .platform_data = &(struct fbtft_platform_data) {
+ .display = {
+ .buswidth = 8,
+ .backlight = 1,
+ },
+ .gpios = (const struct fbtft_gpio []) {
+ { "reset", 25 },
+ { "dc", 24 },
+ { "led", 18 },
+ {},
+ },
+ .gamma = ADAFRUIT18_GAMMA,
+ }
+ }
+ }, {
+ .name = "adafruit18_green",
+ .spi = &(struct spi_board_info) {
+ .modalias = "fb_st7735r",
+ .max_speed_hz = 4000000,
+ .mode = SPI_MODE_0,
+ .platform_data = &(struct fbtft_platform_data) {
+ .display = {
+ .buswidth = 8,
+ .backlight = 1,
+ .fbtftops.set_addr_win = \
+ adafruit18_green_tab_set_addr_win,
+ },
+ .bgr = true,
+ .gpios = (const struct fbtft_gpio []) {
+ { "reset", 25 },
+ { "dc", 24 },
+ { "led", 18 },
+ {},
+ },
+ .gamma = ADAFRUIT18_GAMMA,
+ }
+ }
+ }, {
+ .name = "adafruit22",
+ .spi = &(struct spi_board_info) {
+ .modalias = "fb_hx8340bn",
+ .max_speed_hz = 32000000,
+ .mode = SPI_MODE_0,
+ .platform_data = &(struct fbtft_platform_data) {
+ .display = {
+ .buswidth = 9,
+ .backlight = 1,
+ },
+ .bgr = true,
+ .gpios = (const struct fbtft_gpio []) {
+ { "reset", 25 },
+ { "led", 23 },
+ {},
+ },
+ }
+ }
+ }, {
+ .name = "adafruit22a",
+ .spi = &(struct spi_board_info) {
+ .modalias = "fb_ili9340",
+ .max_speed_hz = 32000000,
+ .mode = SPI_MODE_0,
+ .platform_data = &(struct fbtft_platform_data) {
+ .display = {
+ .buswidth = 8,
+ .backlight = 1,
+ },
+ .bgr = true,
+ .gpios = (const struct fbtft_gpio []) {
+ { "reset", 25 },
+ { "dc", 24 },
+ { "led", 18 },
+ {},
+ },
+ }
+ }
+ }, {
+ .name = "adafruit28",
+ .spi = &(struct spi_board_info) {
+ .modalias = "fb_ili9341",
+ .max_speed_hz = 32000000,
+ .mode = SPI_MODE_0,
+ .platform_data = &(struct fbtft_platform_data) {
+ .display = {
+ .buswidth = 8,
+ .backlight = 1,
+ },
+ .bgr = true,
+ .gpios = (const struct fbtft_gpio []) {
+ { "reset", 25 },
+ { "dc", 24 },
+ { "led", 18 },
+ {},
+ },
+ }
+ }
+ }, {
+ .name = "adafruit13m",
+ .spi = &(struct spi_board_info) {
+ .modalias = "fb_ssd1306",
+ .max_speed_hz = 16000000,
+ .mode = SPI_MODE_0,
+ .platform_data = &(struct fbtft_platform_data) {
+ .display = {
+ .buswidth = 8,
+ },
+ .gpios = (const struct fbtft_gpio []) {
+ { "reset", 25 },
+ { "dc", 24 },
+ {},
+ },
+ }
+ }
+ }, {
+ .name = "agm1264k-fl",
+ .pdev = &(struct platform_device) {
+ .name = "fb_agm1264k-fl",
+ .id = 0,
+ .dev = {
+ .release = fbtft_device_pdev_release,
+ .platform_data = &(struct fbtft_platform_data) {
+ .display = {
+ .buswidth = 8,
+ .backlight = FBTFT_ONBOARD_BACKLIGHT,
+ },
+ .gpios = (const struct fbtft_gpio []) {
+ {},
+ },
+ },
+ }
+ }
+ }, {
+ .name = "dogs102",
+ .spi = &(struct spi_board_info) {
+ .modalias = "fb_uc1701",
+ .max_speed_hz = 8000000,
+ .mode = SPI_MODE_0,
+ .platform_data = &(struct fbtft_platform_data) {
+ .display = {
+ .buswidth = 8,
+ },
+ .bgr = true,
+ .gpios = (const struct fbtft_gpio []) {
+ { "reset", 13 },
+ { "dc", 6 },
+ {},
+ },
+ }
+ }
+ }, {
+ .name = "er_tftm050_2",
+ .spi = &(struct spi_board_info) {
+ .modalias = "fb_ra8875",
+ .max_speed_hz = 5000000,
+ .mode = SPI_MODE_3,
+ .platform_data = &(struct fbtft_platform_data) {
+ .display = {
+ .buswidth = 8,
+ .backlight = 1,
+ .width = 480,
+ .height = 272,
+ },
+ .bgr = true,
+ .gpios = (const struct fbtft_gpio []) {
+ { "reset", 25 },
+ { "dc", 24 },
+ {},
+ },
+ }
+ }
+ }, {
+ .name = "er_tftm070_5",
+ .spi = &(struct spi_board_info) {
+ .modalias = "fb_ra8875",
+ .max_speed_hz = 5000000,
+ .mode = SPI_MODE_3,
+ .platform_data = &(struct fbtft_platform_data) {
+ .display = {
+ .buswidth = 8,
+ .backlight = 1,
+ .width = 800,
+ .height = 480,
+ },
+ .bgr = true,
+ .gpios = (const struct fbtft_gpio []) {
+ { "reset", 25 },
+ { "dc", 24 },
+ {},
+ },
+ }
+ }
+ }, {
+ .name = "flexfb",
+ .spi = &(struct spi_board_info) {
+ .modalias = "flexfb",
+ .max_speed_hz = 32000000,
+ .mode = SPI_MODE_0,
+ .platform_data = &(struct fbtft_platform_data) {
+ .gpios = (const struct fbtft_gpio []) {
+ { "reset", 25 },
+ { "dc", 24 },
+ {},
+ },
+ }
+ }
+ }, {
+ .name = "flexpfb",
+ .pdev = &(struct platform_device) {
+ .name = "flexpfb",
+ .id = 0,
+ .dev = {
+ .release = fbtft_device_pdev_release,
+ .platform_data = &(struct fbtft_platform_data) {
+ .gpios = (const struct fbtft_gpio []) {
+ { "reset", 17 },
+ { "dc", 1 },
+ { "wr", 0 },
+ { "cs", 21 },
+ { "db00", 9 },
+ { "db01", 11 },
+ { "db02", 18 },
+ { "db03", 23 },
+ { "db04", 24 },
+ { "db05", 25 },
+ { "db06", 8 },
+ { "db07", 7 },
+ { "led", 4 },
+ {},
+ },
+ },
+ }
+ }
+ }, {
+ .name = "freetronicsoled128",
+ .spi = &(struct spi_board_info) {
+ .modalias = "fb_ssd1351",
+ .max_speed_hz = 20000000,
+ .mode = SPI_MODE_0,
+ .platform_data = &(struct fbtft_platform_data) {
+ .display = {
+ .buswidth = 8,
+ .backlight = FBTFT_ONBOARD_BACKLIGHT,
+ },
+ .bgr = true,
+ .gpios = (const struct fbtft_gpio []) {
+ { "reset", 24 },
+ { "dc", 25 },
+ {},
+ },
+ }
+ }
+ }, {
+ .name = "hx8353d",
+ .spi = &(struct spi_board_info) {
+ .modalias = "fb_hx8353d",
+ .max_speed_hz = 16000000,
+ .mode = SPI_MODE_0,
+ .platform_data = &(struct fbtft_platform_data) {
+ .display = {
+ .buswidth = 8,
+ .backlight = 1,
+ },
+ .gpios = (const struct fbtft_gpio []) {
+ { "reset", 25 },
+ { "dc", 24 },
+ { "led", 23 },
+ {},
+ },
+ }
+ }
+ }, {
+ .name = "hy28a",
+ .spi = &(struct spi_board_info) {
+ .modalias = "fb_ili9320",
+ .max_speed_hz = 32000000,
+ .mode = SPI_MODE_3,
+ .platform_data = &(struct fbtft_platform_data) {
+ .display = {
+ .buswidth = 8,
+ .backlight = 1,
+ },
+ .startbyte = 0x70,
+ .bgr = true,
+ .gpios = (const struct fbtft_gpio []) {
+ { "reset", 25 },
+ { "led", 18 },
+ {},
+ },
+ }
+ }
+ }, {
+ .name = "hy28b",
+ .spi = &(struct spi_board_info) {
+ .modalias = "fb_ili9325",
+ .max_speed_hz = 48000000,
+ .mode = SPI_MODE_3,
+ .platform_data = &(struct fbtft_platform_data) {
+ .display = {
+ .buswidth = 8,
+ .backlight = 1,
+ .init_sequence = hy28b_init_sequence,
+ },
+ .startbyte = 0x70,
+ .bgr = true,
+ .fps = 50,
+ .gpios = (const struct fbtft_gpio []) {
+ { "reset", 25 },
+ { "led", 18 },
+ {},
+ },
+ .gamma = HY28B_GAMMA,
+ }
+ }
+ }, {
+ .name = "ili9481",
+ .spi = &(struct spi_board_info) {
+ .modalias = "fb_ili9481",
+ .max_speed_hz = 32000000,
+ .mode = SPI_MODE_0,
+ .platform_data = &(struct fbtft_platform_data) {
+ .display = {
+ .regwidth = 16,
+ .buswidth = 8,
+ .backlight = 1,
+ },
+ .bgr = true,
+ .gpios = (const struct fbtft_gpio []) {
+ { "reset", 25 },
+ { "dc", 24 },
+ { "led", 22 },
+ {},
+ },
+ }
+ }
+ }, {
+ .name = "itdb24",
+ .pdev = &(struct platform_device) {
+ .name = "fb_s6d1121",
+ .id = 0,
+ .dev = {
+ .release = fbtft_device_pdev_release,
+ .platform_data = &(struct fbtft_platform_data) {
+ .display = {
+ .buswidth = 8,
+ .backlight = 1,
+ },
+ .bgr = false,
+ .gpios = (const struct fbtft_gpio []) {
+ /* Wiring for LCD adapter kit */
+ { "reset", 7 },
+ { "dc", 0 }, /* rev 2: 2 */
+ { "wr", 1 }, /* rev 2: 3 */
+ { "cs", 8 },
+ { "db00", 17 },
+ { "db01", 18 },
+ { "db02", 21 }, /* rev 2: 27 */
+ { "db03", 22 },
+ { "db04", 23 },
+ { "db05", 24 },
+ { "db06", 25 },
+ { "db07", 4 },
+ {}
+ },
+ },
+ }
+ }
+ }, {
+ .name = "itdb28",
+ .pdev = &(struct platform_device) {
+ .name = "fb_ili9325",
+ .id = 0,
+ .dev = {
+ .release = fbtft_device_pdev_release,
+ .platform_data = &(struct fbtft_platform_data) {
+ .display = {
+ .buswidth = 8,
+ .backlight = 1,
+ },
+ .bgr = true,
+ .gpios = (const struct fbtft_gpio []) {
+ {},
+ },
+ },
+ }
+ }
+ }, {
+ .name = "itdb28_spi",
+ .spi = &(struct spi_board_info) {
+ .modalias = "fb_ili9325",
+ .max_speed_hz = 32000000,
+ .mode = SPI_MODE_0,
+ .platform_data = &(struct fbtft_platform_data) {
+ .display = {
+ .buswidth = 8,
+ .backlight = 1,
+ },
+ .bgr = true,
+ .gpios = (const struct fbtft_gpio []) {
+ { "reset", 25 },
+ { "dc", 24 },
+ {},
+ },
+ }
+ }
+ }, {
+ .name = "mi0283qt-2",
+ .spi = &(struct spi_board_info) {
+ .modalias = "fb_hx8347d",
+ .max_speed_hz = 32000000,
+ .mode = SPI_MODE_0,
+ .platform_data = &(struct fbtft_platform_data) {
+ .display = {
+ .buswidth = 8,
+ .backlight = 1,
+ },
+ .startbyte = 0x70,
+ .bgr = true,
+ .gpios = (const struct fbtft_gpio []) {
+ { "reset", 25 },
+ { "dc", 24 },
+ { "led", 18 },
+ {},
+ },
+ }
+ }
+ }, {
+ .name = "mi0283qt-9a",
+ .spi = &(struct spi_board_info) {
+ .modalias = "fb_ili9341",
+ .max_speed_hz = 32000000,
+ .mode = SPI_MODE_0,
+ .platform_data = &(struct fbtft_platform_data) {
+ .display = {
+ .buswidth = 9,
+ .backlight = 1,
+ },
+ .bgr = true,
+ .gpios = (const struct fbtft_gpio []) {
+ { "reset", 25 },
+ { "led", 18 },
+ {},
+ },
+ }
+ }
+ }, {
+ .name = "mi0283qt-v2",
+ .spi = &(struct spi_board_info) {
+ .modalias = "fb_watterott",
+ .max_speed_hz = 4000000,
+ .mode = SPI_MODE_3,
+ .platform_data = &(struct fbtft_platform_data) {
+ .gpios = (const struct fbtft_gpio []) {
+ { "reset", 25 },
+ {},
+ },
+ }
+ }
+ }, {
+ .name = "nokia3310",
+ .spi = &(struct spi_board_info) {
+ .modalias = "fb_pcd8544",
+ .max_speed_hz = 400000,
+ .mode = SPI_MODE_0,
+ .platform_data = &(struct fbtft_platform_data) {
+ .display = {
+ .buswidth = 8,
+ },
+ .gpios = (const struct fbtft_gpio []) {
+ { "reset", 25 },
+ { "dc", 24 },
+ { "led", 23 },
+ {},
+ },
+ }
+ }
+ }, {
+ .name = "nokia3310a",
+ .spi = &(struct spi_board_info) {
+ .modalias = "fb_tls8204",
+ .max_speed_hz = 1000000,
+ .mode = SPI_MODE_0,
+ .platform_data = &(struct fbtft_platform_data) {
+ .display = {
+ .buswidth = 8,
+ },
+ .gpios = (const struct fbtft_gpio []) {
+ { "reset", 25 },
+ { "dc", 24 },
+ { "led", 23 },
+ {},
+ },
+ }
+ }
+ }, {
+ .name = "nokia5110",
+ .spi = &(struct spi_board_info) {
+ .modalias = "fb_ili9163",
+ .max_speed_hz = 12000000,
+ .mode = SPI_MODE_0,
+ .platform_data = &(struct fbtft_platform_data) {
+ .display = {
+ .buswidth = 8,
+ .backlight = 1,
+ },
+ .bgr = true,
+ .gpios = (const struct fbtft_gpio []) {
+ {},
+ },
+ }
+ }
+ }, {
+
+ .name = "piscreen",
+ .spi = &(struct spi_board_info) {
+ .modalias = "fb_ili9486",
+ .max_speed_hz = 32000000,
+ .mode = SPI_MODE_0,
+ .platform_data = &(struct fbtft_platform_data) {
+ .display = {
+ .regwidth = 16,
+ .buswidth = 8,
+ .backlight = 1,
+ },
+ .bgr = true,
+ .gpios = (const struct fbtft_gpio []) {
+ { "reset", 25 },
+ { "dc", 24 },
+ { "led", 22 },
+ {},
+ },
+ }
+ }
+ }, {
+ .name = "pitft",
+ .spi = &(struct spi_board_info) {
+ .modalias = "fb_ili9340",
+ .max_speed_hz = 32000000,
+ .mode = SPI_MODE_0,
+ .chip_select = 0,
+ .platform_data = &(struct fbtft_platform_data) {
+ .display = {
+ .buswidth = 8,
+ .backlight = 1,
+ .init_sequence = pitft_init_sequence,
+ },
+ .bgr = true,
+ .gpios = (const struct fbtft_gpio []) {
+ { "dc", 25 },
+ {},
+ },
+ }
+ }
+ }, {
+ .name = "pioled",
+ .spi = &(struct spi_board_info) {
+ .modalias = "fb_ssd1351",
+ .max_speed_hz = 20000000,
+ .mode = SPI_MODE_0,
+ .platform_data = &(struct fbtft_platform_data) {
+ .display = {
+ .buswidth = 8,
+ },
+ .bgr = true,
+ .gpios = (const struct fbtft_gpio []) {
+ { "reset", 24 },
+ { "dc", 25 },
+ {},
+ },
+ .gamma = "0 2 2 2 2 2 2 2 " \
+ "2 2 2 2 2 2 2 2 " \
+ "2 2 2 2 2 2 2 2 " \
+ "2 2 2 2 2 2 2 3 " \
+ "3 3 3 3 3 3 3 3 " \
+ "3 3 3 3 3 3 3 3 " \
+ "3 3 3 4 4 4 4 4 " \
+ "4 4 4 4 4 4 4"
+ }
+ }
+ }, {
+ .name = "rpi-display",
+ .spi = &(struct spi_board_info) {
+ .modalias = "fb_ili9341",
+ .max_speed_hz = 32000000,
+ .mode = SPI_MODE_0,
+ .platform_data = &(struct fbtft_platform_data) {
+ .display = {
+ .buswidth = 8,
+ .backlight = 1,
+ },
+ .bgr = true,
+ .gpios = (const struct fbtft_gpio []) {
+ { "reset", 23 },
+ { "dc", 24 },
+ { "led", 18 },
+ {},
+ },
+ }
+ }
+ }, {
+ .name = "s6d02a1",
+ .spi = &(struct spi_board_info) {
+ .modalias = "fb_s6d02a1",
+ .max_speed_hz = 32000000,
+ .mode = SPI_MODE_0,
+ .platform_data = &(struct fbtft_platform_data) {
+ .display = {
+ .buswidth = 8,
+ .backlight = 1,
+ },
+ .bgr = true,
+ .gpios = (const struct fbtft_gpio []) {
+ { "reset", 25 },
+ { "dc", 24 },
+ { "led", 23 },
+ {},
+ },
+ }
+ }
+ }, {
+ .name = "sainsmart18",
+ .spi = &(struct spi_board_info) {
+ .modalias = "fb_st7735r",
+ .max_speed_hz = 32000000,
+ .mode = SPI_MODE_0,
+ .platform_data = &(struct fbtft_platform_data) {
+ .display = {
+ .buswidth = 8,
+ },
+ .gpios = (const struct fbtft_gpio []) {
+ { "reset", 25 },
+ { "dc", 24 },
+ {},
+ },
+ }
+ }
+ }, {
+ .name = "sainsmart32",
+ .pdev = &(struct platform_device) {
+ .name = "fb_ssd1289",
+ .id = 0,
+ .dev = {
+ .release = fbtft_device_pdev_release,
+ .platform_data = &(struct fbtft_platform_data) {
+ .display = {
+ .buswidth = 16,
+ .txbuflen = -2, /* disable buffer */
+ .backlight = 1,
+ .fbtftops.write = write_gpio16_wr_slow,
+ },
+ .bgr = true,
+ .gpios = (const struct fbtft_gpio []) {
+ {},
+ },
+ },
+ },
+ }
+ }, {
+ .name = "sainsmart32_fast",
+ .pdev = &(struct platform_device) {
+ .name = "fb_ssd1289",
+ .id = 0,
+ .dev = {
+ .release = fbtft_device_pdev_release,
+ .platform_data = &(struct fbtft_platform_data) {
+ .display = {
+ .buswidth = 16,
+ .txbuflen = -2, /* disable buffer */
+ .backlight = 1,
+ },
+ .bgr = true,
+ .gpios = (const struct fbtft_gpio []) {
+ {},
+ },
+ },
+ },
+ }
+ }, {
+ .name = "sainsmart32_latched",
+ .pdev = &(struct platform_device) {
+ .name = "fb_ssd1289",
+ .id = 0,
+ .dev = {
+ .release = fbtft_device_pdev_release,
+ .platform_data = &(struct fbtft_platform_data) {
+ .display = {
+ .buswidth = 16,
+ .txbuflen = -2, /* disable buffer */
+ .backlight = 1,
+ .fbtftops.write = \
+ fbtft_write_gpio16_wr_latched,
+ },
+ .bgr = true,
+ .gpios = (const struct fbtft_gpio []) {
+ {},
+ },
+ },
+ },
+ }
+ }, {
+ .name = "sainsmart32_spi",
+ .spi = &(struct spi_board_info) {
+ .modalias = "fb_ssd1289",
+ .max_speed_hz = 16000000,
+ .mode = SPI_MODE_0,
+ .platform_data = &(struct fbtft_platform_data) {
+ .display = {
+ .buswidth = 8,
+ .backlight = 1,
+ },
+ .bgr = true,
+ .gpios = (const struct fbtft_gpio []) {
+ { "reset", 25 },
+ { "dc", 24 },
+ {},
+ },
+ }
+ }
+ }, {
+ .name = "spidev",
+ .spi = &(struct spi_board_info) {
+ .modalias = "spidev",
+ .max_speed_hz = 500000,
+ .bus_num = 0,
+ .chip_select = 0,
+ .mode = SPI_MODE_0,
+ .platform_data = &(struct fbtft_platform_data) {
+ .gpios = (const struct fbtft_gpio []) {
+ {},
+ },
+ }
+ }
+ }, {
+ .name = "ssd1331",
+ .spi = &(struct spi_board_info) {
+ .modalias = "fb_ssd1331",
+ .max_speed_hz = 20000000,
+ .mode = SPI_MODE_3,
+ .platform_data = &(struct fbtft_platform_data) {
+ .display = {
+ .buswidth = 8,
+ },
+ .gpios = (const struct fbtft_gpio []) {
+ { "reset", 24 },
+ { "dc", 25 },
+ {},
+ },
+ }
+ }
+ }, {
+ .name = "tinylcd35",
+ .spi = &(struct spi_board_info) {
+ .modalias = "fb_tinylcd",
+ .max_speed_hz = 32000000,
+ .mode = SPI_MODE_0,
+ .platform_data = &(struct fbtft_platform_data) {
+ .display = {
+ .buswidth = 8,
+ .backlight = 1,
+ },
+ .bgr = true,
+ .gpios = (const struct fbtft_gpio []) {
+ { "reset", 25 },
+ { "dc", 24 },
+ { "led", 18 },
+ {},
+ },
+ }
+ }
+ }, {
+ .name = "tm022hdh26",
+ .spi = &(struct spi_board_info) {
+ .modalias = "fb_ili9341",
+ .max_speed_hz = 32000000,
+ .mode = SPI_MODE_0,
+ .platform_data = &(struct fbtft_platform_data) {
+ .display = {
+ .buswidth = 8,
+ .backlight = 1,
+ },
+ .bgr = true,
+ .gpios = (const struct fbtft_gpio []) {
+ { "reset", 25 },
+ { "dc", 24 },
+ { "led", 18 },
+ {},
+ },
+ }
+ }
+ }, {
+ .name = "tontec35_9481", /* boards before 02 July 2014 */
+ .spi = &(struct spi_board_info) {
+ .modalias = "fb_ili9481",
+ .max_speed_hz = 128000000,
+ .mode = SPI_MODE_3,
+ .platform_data = &(struct fbtft_platform_data) {
+ .display = {
+ .buswidth = 8,
+ .backlight = 1,
+ },
+ .bgr = true,
+ .gpios = (const struct fbtft_gpio []) {
+ { "reset", 15 },
+ { "dc", 25 },
+ { "led_", 18 },
+ {},
+ },
+ }
+ }
+ }, {
+ .name = "tontec35_9486", /* boards after 02 July 2014 */
+ .spi = &(struct spi_board_info) {
+ .modalias = "fb_ili9486",
+ .max_speed_hz = 128000000,
+ .mode = SPI_MODE_3,
+ .platform_data = &(struct fbtft_platform_data) {
+ .display = {
+ .buswidth = 8,
+ .backlight = 1,
+ },
+ .bgr = true,
+ .gpios = (const struct fbtft_gpio []) {
+ { "reset", 15 },
+ { "dc", 25 },
+ { "led_", 18 },
+ {},
+ },
+ }
+ }
+ }, {
+ .name = "upd161704",
+ .spi = &(struct spi_board_info) {
+ .modalias = "fb_upd161704",
+ .max_speed_hz = 32000000,
+ .mode = SPI_MODE_0,
+ .platform_data = &(struct fbtft_platform_data) {
+ .display = {
+ .buswidth = 8,
+ },
+ .gpios = (const struct fbtft_gpio []) {
+ { "reset", 24 },
+ { "dc", 25 },
+ {},
+ },
+ }
+ }
+ }, {
+ .name = "waveshare32b",
+ .spi = &(struct spi_board_info) {
+ .modalias = "fb_ili9340",
+ .max_speed_hz = 48000000,
+ .mode = SPI_MODE_0,
+ .platform_data = &(struct fbtft_platform_data) {
+ .display = {
+ .buswidth = 8,
+ .backlight = 1,
+ .init_sequence = waveshare32b_init_sequence,
+ },
+ .bgr = true,
+ .gpios = (const struct fbtft_gpio []) {
+ { "reset", 27 },
+ { "dc", 22 },
+ {},
+ },
+ }
+ }
+ }, {
+ .name = "waveshare22",
+ .spi = &(struct spi_board_info) {
+ .modalias = "fb_bd663474",
+ .max_speed_hz = 32000000,
+ .mode = SPI_MODE_3,
+ .platform_data = &(struct fbtft_platform_data) {
+ .display = {
+ .buswidth = 8,
+ },
+ .gpios = (const struct fbtft_gpio []) {
+ { "reset", 24 },
+ { "dc", 25 },
+ {},
+ },
+ }
+ }
+ }, {
+ /* This should be the last item.
+ Used with the custom argument */
+ .name = "",
+ .spi = &(struct spi_board_info) {
+ .modalias = "",
+ .max_speed_hz = 0,
+ .mode = SPI_MODE_0,
+ .platform_data = &(struct fbtft_platform_data) {
+ .gpios = (const struct fbtft_gpio []) {
+ {},
+ },
+ }
+ },
+ .pdev = &(struct platform_device) {
+ .name = "",
+ .id = 0,
+ .dev = {
+ .release = fbtft_device_pdev_release,
+ .platform_data = &(struct fbtft_platform_data) {
+ .gpios = (const struct fbtft_gpio []) {
+ {},
+ },
+ },
+ },
+ },
+ }
+};
+
+static int write_gpio16_wr_slow(struct fbtft_par *par, void *buf, size_t len)
+{
+ u16 data;
+ int i;
+#ifndef DO_NOT_OPTIMIZE_FBTFT_WRITE_GPIO
+ static u16 prev_data;
+#endif
+
+ fbtft_par_dbg_hex(DEBUG_WRITE, par, par->info->device, u8, buf, len,
+ "%s(len=%d): ", __func__, len);
+
+ while (len) {
+ data = *(u16 *) buf;
+
+ /* Start writing by pulling down /WR */
+ gpio_set_value(par->gpio.wr, 0);
+
+ /* Set data */
+#ifndef DO_NOT_OPTIMIZE_FBTFT_WRITE_GPIO
+ if (data == prev_data) {
+ gpio_set_value(par->gpio.wr, 0); /* used as delay */
+ } else {
+ for (i = 0; i < 16; i++) {
+ if ((data & 1) != (prev_data & 1))
+ gpio_set_value(par->gpio.db[i],
+ data & 1);
+ data >>= 1;
+ prev_data >>= 1;
+ }
+ }
+#else
+ for (i = 0; i < 16; i++) {
+ gpio_set_value(par->gpio.db[i], data & 1);
+ data >>= 1;
+ }
+#endif
+
+ /* Pullup /WR */
+ gpio_set_value(par->gpio.wr, 1);
+
+#ifndef DO_NOT_OPTIMIZE_FBTFT_WRITE_GPIO
+ prev_data = *(u16 *) buf;
+#endif
+ buf += 2;
+ len -= 2;
+ }
+
+ return 0;
+}
+
+static void adafruit18_green_tab_set_addr_win(struct fbtft_par *par,
+ int xs, int ys, int xe, int ye)
+{
+ fbtft_par_dbg(DEBUG_SET_ADDR_WIN, par,
+ "%s(xs=%d, ys=%d, xe=%d, ye=%d)\n", __func__, xs, ys, xe, ye);
+ write_reg(par, 0x2A, 0, xs + 2, 0, xe + 2);
+ write_reg(par, 0x2B, 0, ys + 1, 0, ye + 1);
+ write_reg(par, 0x2C);
+}
+
+/* used if gpios parameter is present */
+static struct fbtft_gpio fbtft_device_param_gpios[MAX_GPIOS+1] = { };
+
+static void fbtft_device_pdev_release(struct device *dev)
+{
+/* Needed to silence this message:
+Device 'xxx' does not have a release() function, it is broken and must be fixed
+*/
+}
+
+static int spi_device_found(struct device *dev, void *data)
+{
+ struct spi_device *spi = container_of(dev, struct spi_device, dev);
+
+ pr_info(DRVNAME": %s %s %dkHz %d bits mode=0x%02X\n",
+ spi->modalias, dev_name(dev), spi->max_speed_hz/1000,
+ spi->bits_per_word, spi->mode);
+
+ return 0;
+}
+
+static void pr_spi_devices(void)
+{
+ pr_info(DRVNAME": SPI devices registered:\n");
+ bus_for_each_dev(&spi_bus_type, NULL, NULL, spi_device_found);
+}
+
+static int p_device_found(struct device *dev, void *data)
+{
+ struct platform_device
+ *pdev = container_of(dev, struct platform_device, dev);
+
+ if (strstr(pdev->name, "fb"))
+ pr_info(DRVNAME": %s id=%d pdata? %s\n",
+ pdev->name, pdev->id,
+ pdev->dev.platform_data ? "yes" : "no");
+
+ return 0;
+}
+
+static void pr_p_devices(void)
+{
+ pr_info(DRVNAME": 'fb' Platform devices registered:\n");
+ bus_for_each_dev(&platform_bus_type, NULL, NULL, p_device_found);
+}
+
+#ifdef MODULE
+static void fbtft_device_spi_delete(struct spi_master *master, unsigned cs)
+{
+ struct device *dev;
+ char str[32];
+
+ snprintf(str, sizeof(str), "%s.%u", dev_name(&master->dev), cs);
+
+ dev = bus_find_device_by_name(&spi_bus_type, NULL, str);
+ if (dev) {
+ if (verbose)
+ pr_info(DRVNAME": Deleting %s\n", str);
+ device_del(dev);
+ }
+}
+
+static int fbtft_device_spi_device_register(struct spi_board_info *spi)
+{
+ struct spi_master *master;
+
+ master = spi_busnum_to_master(spi->bus_num);
+ if (!master) {
+ pr_err(DRVNAME ": spi_busnum_to_master(%d) returned NULL\n",
+ spi->bus_num);
+ return -EINVAL;
+ }
+ /* make sure it's available */
+ fbtft_device_spi_delete(master, spi->chip_select);
+ spi_device = spi_new_device(master, spi);
+ put_device(&master->dev);
+ if (!spi_device) {
+ pr_err(DRVNAME ": spi_new_device() returned NULL\n");
+ return -EPERM;
+ }
+ return 0;
+}
+#else
+static int fbtft_device_spi_device_register(struct spi_board_info *spi)
+{
+ return spi_register_board_info(spi, 1);
+}
+#endif
+
+static int __init fbtft_device_init(void)
+{
+ struct spi_board_info *spi = NULL;
+ struct fbtft_platform_data *pdata;
+ const struct fbtft_gpio *gpio = NULL;
+ char *p_gpio, *p_name, *p_num;
+ bool found = false;
+ int i = 0;
+ long val;
+ int ret = 0;
+
+ pr_debug("\n\n"DRVNAME": init\n");
+
+ if (name == NULL) {
+#ifdef MODULE
+ pr_err(DRVNAME": missing module parameter: 'name'\n");
+ return -EINVAL;
+#else
+ return 0;
+#endif
+ }
+
+ if (init_num > FBTFT_MAX_INIT_SEQUENCE) {
+ pr_err(DRVNAME \
+ ": init parameter: exceeded max array size: %d\n",
+ FBTFT_MAX_INIT_SEQUENCE);
+ return -EINVAL;
+ }
+
+ /* parse module parameter: gpios */
+ while ((p_gpio = strsep(&gpios, ","))) {
+ if (strchr(p_gpio, ':') == NULL) {
+ pr_err(DRVNAME \
+ ": error: missing ':' in gpios parameter: %s\n",
+ p_gpio);
+ return -EINVAL;
+ }
+ p_num = p_gpio;
+ p_name = strsep(&p_num, ":");
+ if (p_name == NULL || p_num == NULL) {
+ pr_err(DRVNAME \
+ ": something bad happened parsing gpios parameter: %s\n",
+ p_gpio);
+ return -EINVAL;
+ }
+ ret = kstrtol(p_num, 10, &val);
+ if (ret) {
+ pr_err(DRVNAME \
+ ": could not parse number in gpios parameter: %s:%s\n",
+ p_name, p_num);
+ return -EINVAL;
+ }
+ strcpy(fbtft_device_param_gpios[i].name, p_name);
+ fbtft_device_param_gpios[i++].gpio = (int) val;
+ if (i == MAX_GPIOS) {
+ pr_err(DRVNAME \
+ ": gpios parameter: exceeded max array size: %d\n",
+ MAX_GPIOS);
+ return -EINVAL;
+ }
+ }
+ if (fbtft_device_param_gpios[0].name[0])
+ gpio = fbtft_device_param_gpios;
+
+ if (verbose > 2)
+ pr_spi_devices(); /* print list of registered SPI devices */
+
+ if (verbose > 2)
+ pr_p_devices(); /* print list of 'fb' platform devices */
+
+ pr_debug(DRVNAME": name='%s', busnum=%d, cs=%d\n", name, busnum, cs);
+
+ if (rotate > 0 && rotate < 4) {
+ rotate = (4 - rotate) * 90;
+ pr_warn("argument 'rotate' should be an angle. Values 1-3 is deprecated. Setting it to %d.\n",
+ rotate);
+ }
+ if (rotate != 0 && rotate != 90 && rotate != 180 && rotate != 270) {
+ pr_warn("argument 'rotate' illegal value: %d. Setting it to 0.\n",
+ rotate);
+ rotate = 0;
+ }
+
+ /* name=list lists all supported displays */
+ if (strncmp(name, "list", 32) == 0) {
+ pr_info(DRVNAME": Supported displays:\n");
+
+ for (i = 0; i < ARRAY_SIZE(displays); i++)
+ pr_info(DRVNAME": %s\n", displays[i].name);
+ return -ECANCELED;
+ }
+
+ if (custom) {
+ i = ARRAY_SIZE(displays) - 1;
+ displays[i].name = name;
+ if (speed == 0) {
+ displays[i].pdev->name = name;
+ displays[i].spi = NULL;
+ } else {
+ strncpy(displays[i].spi->modalias, name, SPI_NAME_SIZE);
+ displays[i].pdev = NULL;
+ }
+ }
+
+ for (i = 0; i < ARRAY_SIZE(displays); i++) {
+ if (strncmp(name, displays[i].name, 32) == 0) {
+ if (displays[i].spi) {
+ spi = displays[i].spi;
+ spi->chip_select = cs;
+ spi->bus_num = busnum;
+ if (speed)
+ spi->max_speed_hz = speed;
+ if (mode != -1)
+ spi->mode = mode;
+ pdata = (void *)spi->platform_data;
+ } else if (displays[i].pdev) {
+ p_device = displays[i].pdev;
+ pdata = p_device->dev.platform_data;
+ } else {
+ pr_err(DRVNAME": broken displays array\n");
+ return -EINVAL;
+ }
+
+ pdata->rotate = rotate;
+ if (bgr == 0)
+ pdata->bgr = false;
+ else if (bgr == 1)
+ pdata->bgr = true;
+ if (startbyte)
+ pdata->startbyte = startbyte;
+ if (gamma)
+ pdata->gamma = gamma;
+ pdata->display.debug = debug;
+ if (fps)
+ pdata->fps = fps;
+ if (txbuflen)
+ pdata->txbuflen = txbuflen;
+ if (init_num)
+ pdata->display.init_sequence = init;
+ if (gpio)
+ pdata->gpios = gpio;
+ if (custom) {
+ pdata->display.width = width;
+ pdata->display.height = height;
+ pdata->display.buswidth = buswidth;
+ pdata->display.backlight = 1;
+ }
+
+ if (displays[i].spi) {
+ ret = fbtft_device_spi_device_register(spi);
+ if (ret) {
+ pr_err(DRVNAME \
+ ": failed to register SPI device\n");
+ return ret;
+ }
+ found = true;
+ break;
+ } else {
+ ret = platform_device_register(p_device);
+ if (ret < 0) {
+ pr_err(DRVNAME \
+ ": platform_device_register() returned %d\n",
+ ret);
+ return ret;
+ }
+ found = true;
+ break;
+ }
+ }
+ }
+
+ if (!found) {
+ pr_err(DRVNAME": display not supported: '%s'\n", name);
+ return -EINVAL;
+ }
+
+ if (verbose && pdata && pdata->gpios) {
+ gpio = pdata->gpios;
+ pr_info(DRVNAME": GPIOS used by '%s':\n", name);
+ found = false;
+ while (verbose && gpio->name[0]) {
+ pr_info(DRVNAME": '%s' = GPIO%d\n",
+ gpio->name, gpio->gpio);
+ gpio++;
+ found = true;
+ }
+ if (!found)
+ pr_info(DRVNAME": (none)\n");
+ }
+
+ if (spi_device && (verbose > 1))
+ pr_spi_devices();
+ if (p_device && (verbose > 1))
+ pr_p_devices();
+
+ return 0;
+}
+
+static void __exit fbtft_device_exit(void)
+{
+ pr_debug(DRVNAME" - exit\n");
+
+ if (spi_device) {
+ device_del(&spi_device->dev);
+ kfree(spi_device);
+ }
+
+ if (p_device)
+ platform_device_unregister(p_device);
+
+}
+
+arch_initcall(fbtft_device_init);
+module_exit(fbtft_device_exit);
+
+MODULE_DESCRIPTION("Add a FBTFT device.");
+MODULE_AUTHOR("Noralf Tronnes");
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/fbtft/flexfb.c b/drivers/staging/fbtft/flexfb.c
new file mode 100644
index 000000000..ca39fe90d
--- /dev/null
+++ b/drivers/staging/fbtft/flexfb.c
@@ -0,0 +1,592 @@
+/*
+ * Generic FB driver for TFT LCD displays
+ *
+ * Copyright (C) 2013 Noralf Tronnes
+ *
+ * This program is free software; you can redistribute it and/or 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/kernel.h>
+#include <linux/init.h>
+#include <linux/vmalloc.h>
+#include <linux/gpio.h>
+#include <linux/spi/spi.h>
+#include <linux/delay.h>
+
+#include "fbtft.h"
+
+#define DRVNAME "flexfb"
+
+
+static char *chip;
+module_param(chip, charp, 0);
+MODULE_PARM_DESC(chip, "LCD controller");
+
+static unsigned int width;
+module_param(width, uint, 0);
+MODULE_PARM_DESC(width, "Display width");
+
+static unsigned int height;
+module_param(height, uint, 0);
+MODULE_PARM_DESC(height, "Display height");
+
+static int init[512];
+static int init_num;
+module_param_array(init, int, &init_num, 0);
+MODULE_PARM_DESC(init, "Init sequence");
+
+static unsigned int setaddrwin;
+module_param(setaddrwin, uint, 0);
+MODULE_PARM_DESC(setaddrwin, "Which set_addr_win() implementation to use");
+
+static unsigned int buswidth = 8;
+module_param(buswidth, uint, 0);
+MODULE_PARM_DESC(buswidth, "Width of databus (default: 8)");
+
+static unsigned int regwidth = 8;
+module_param(regwidth, uint, 0);
+MODULE_PARM_DESC(regwidth, "Width of controller register (default: 8)");
+
+static bool nobacklight;
+module_param(nobacklight, bool, 0);
+MODULE_PARM_DESC(nobacklight, "Turn off backlight functionality.");
+
+static bool latched;
+module_param(latched, bool, 0);
+MODULE_PARM_DESC(latched, "Use with latched 16-bit databus");
+
+
+static int *initp;
+static int initp_num;
+
+/* default init sequences */
+static int st7735r_init[] = { \
+-1,0x01,-2,150,-1,0x11,-2,500,-1,0xB1,0x01,0x2C,0x2D,-1,0xB2,0x01,0x2C,0x2D,-1,0xB3,0x01,0x2C,0x2D,0x01,0x2C,0x2D, \
+-1,0xB4,0x07,-1,0xC0,0xA2,0x02,0x84,-1,0xC1,0xC5,-1,0xC2,0x0A,0x00,-1,0xC3,0x8A,0x2A,-1,0xC4,0x8A,0xEE,-1,0xC5,0x0E, \
+-1,0x20,-1,0x36,0xC0,-1,0x3A,0x05,-1,0xE0,0x0f,0x1a,0x0f,0x18,0x2f,0x28,0x20,0x22,0x1f,0x1b,0x23,0x37,0x00,0x07,0x02,0x10, \
+-1,0xE1,0x0f,0x1b,0x0f,0x17,0x33,0x2c,0x29,0x2e,0x30,0x30,0x39,0x3f,0x00,0x07,0x03,0x10,-1,0x29,-2,100,-1,0x13,-2,10,-3 };
+
+static int ssd1289_init[] = { \
+-1,0x00,0x0001,-1,0x03,0xA8A4,-1,0x0C,0x0000,-1,0x0D,0x080C,-1,0x0E,0x2B00,-1,0x1E,0x00B7,-1,0x01,0x2B3F,-1,0x02,0x0600, \
+-1,0x10,0x0000,-1,0x11,0x6070,-1,0x05,0x0000,-1,0x06,0x0000,-1,0x16,0xEF1C,-1,0x17,0x0003,-1,0x07,0x0233,-1,0x0B,0x0000, \
+-1,0x0F,0x0000,-1,0x41,0x0000,-1,0x42,0x0000,-1,0x48,0x0000,-1,0x49,0x013F,-1,0x4A,0x0000,-1,0x4B,0x0000,-1,0x44,0xEF00, \
+-1,0x45,0x0000,-1,0x46,0x013F,-1,0x30,0x0707,-1,0x31,0x0204,-1,0x32,0x0204,-1,0x33,0x0502,-1,0x34,0x0507,-1,0x35,0x0204, \
+-1,0x36,0x0204,-1,0x37,0x0502,-1,0x3A,0x0302,-1,0x3B,0x0302,-1,0x23,0x0000,-1,0x24,0x0000,-1,0x25,0x8000,-1,0x4f,0x0000, \
+-1,0x4e,0x0000,-1,0x22,-3 };
+
+static int hx8340bn_init[] = { \
+-1,0xC1,0xFF,0x83,0x40,-1,0x11,-2,150,-1,0xCA,0x70,0x00,0xD9,-1,0xB0,0x01,0x11, \
+-1,0xC9,0x90,0x49,0x10,0x28,0x28,0x10,0x00,0x06,-2,20,-1,0xC2,0x60,0x71,0x01,0x0E,0x05,0x02,0x09,0x31,0x0A, \
+-1,0xC3,0x67,0x30,0x61,0x17,0x48,0x07,0x05,0x33,-2,10,-1,0xB5,0x35,0x20,0x45,-1,0xB4,0x33,0x25,0x4C,-2,10, \
+-1,0x3A,0x05,-1,0x29,-2,10,-3 };
+
+static int ili9225_init[] = { \
+-1,0x0001,0x011C,-1,0x0002,0x0100,-1,0x0003,0x1030,-1,0x0008,0x0808,-1,0x000C,0x0000,-1,0x000F,0x0A01,-1,0x0020,0x0000, \
+-1,0x0021,0x0000,-2,50,-1,0x0010,0x0A00,-1,0x0011,0x1038,-2,50,-1,0x0012,0x1121,-1,0x0013,0x004E,-1,0x0014,0x676F, \
+-1,0x0030,0x0000,-1,0x0031,0x00DB,-1,0x0032,0x0000,-1,0x0033,0x0000,-1,0x0034,0x00DB,-1,0x0035,0x0000,-1,0x0036,0x00AF, \
+-1,0x0037,0x0000,-1,0x0038,0x00DB,-1,0x0039,0x0000,-1,0x0050,0x0000,-1,0x0051,0x060A,-1,0x0052,0x0D0A,-1,0x0053,0x0303, \
+-1,0x0054,0x0A0D,-1,0x0055,0x0A06,-1,0x0056,0x0000,-1,0x0057,0x0303,-1,0x0058,0x0000,-1,0x0059,0x0000,-2,50, \
+-1,0x0007,0x1017,-2,50,-3 };
+
+static int ili9320_init[] = { \
+-1,0x00E5,0x8000,-1,0x0000,0x0001,-1,0x0001,0x0100,-1,0x0002,0x0700,-1,0x0003,0x1030,-1,0x0004,0x0000,-1,0x0008,0x0202, \
+-1,0x0009,0x0000,-1,0x000A,0x0000,-1,0x000C,0x0000,-1,0x000D,0x0000,-1,0x000F,0x0000,-1,0x0010,0x0000,-1,0x0011,0x0007, \
+-1,0x0012,0x0000,-1,0x0013,0x0000,-2,200,-1,0x0010,0x17B0,-1,0x0011,0x0031,-2,50,-1,0x0012,0x0138,-2,50,-1,0x0013,0x1800, \
+-1,0x0029,0x0008,-2,50,-1,0x0020,0x0000,-1,0x0021,0x0000,-1,0x0030,0x0000,-1,0x0031,0x0505,-1,0x0032,0x0004, \
+-1,0x0035,0x0006,-1,0x0036,0x0707,-1,0x0037,0x0105,-1,0x0038,0x0002,-1,0x0039,0x0707,-1,0x003C,0x0704,-1,0x003D,0x0807, \
+-1,0x0050,0x0000,-1,0x0051,0x00EF,-1,0x0052,0x0000,-1,0x0053,0x013F,-1,0x0060,0x2700,-1,0x0061,0x0001,-1,0x006A,0x0000, \
+-1,0x0080,0x0000,-1,0x0081,0x0000,-1,0x0082,0x0000,-1,0x0083,0x0000,-1,0x0084,0x0000,-1,0x0085,0x0000,-1,0x0090,0x0010, \
+-1,0x0092,0x0000,-1,0x0093,0x0003,-1,0x0095,0x0110,-1,0x0097,0x0000,-1,0x0098,0x0000,-1,0x0007,0x0173,-3 };
+
+static int ili9325_init[] = { \
+-1,0x00E3,0x3008,-1,0x00E7,0x0012,-1,0x00EF,0x1231,-1,0x0001,0x0100,-1,0x0002,0x0700,-1,0x0003,0x1030,-1,0x0004,0x0000, \
+-1,0x0008,0x0207,-1,0x0009,0x0000,-1,0x000A,0x0000,-1,0x000C,0x0000,-1,0x000D,0x0000,-1,0x000F,0x0000,-1,0x0010,0x0000, \
+-1,0x0011,0x0007,-1,0x0012,0x0000,-1,0x0013,0x0000,-2,200,-1,0x0010,0x1690,-1,0x0011,0x0223,-2,50,-1,0x0012,0x000D,-2,50, \
+-1,0x0013,0x1200,-1,0x0029,0x000A,-1,0x002B,0x000C,-2,50,-1,0x0020,0x0000,-1,0x0021,0x0000,-1,0x0030,0x0000, \
+-1,0x0031,0x0506,-1,0x0032,0x0104,-1,0x0035,0x0207,-1,0x0036,0x000F,-1,0x0037,0x0306,-1,0x0038,0x0102,-1,0x0039,0x0707, \
+-1,0x003C,0x0702,-1,0x003D,0x1604,-1,0x0050,0x0000,-1,0x0051,0x00EF,-1,0x0052,0x0000,-1,0x0053,0x013F,-1,0x0060,0xA700, \
+-1,0x0061,0x0001,-1,0x006A,0x0000,-1,0x0080,0x0000,-1,0x0081,0x0000,-1,0x0082,0x0000,-1,0x0083,0x0000,-1,0x0084,0x0000, \
+-1,0x0085,0x0000,-1,0x0090,0x0010,-1,0x0092,0x0600,-1,0x0007,0x0133,-3 };
+
+static int ili9341_init[] = { \
+-1,0x28,-2,20,-1,0xCF,0x00,0x83,0x30,-1,0xED,0x64,0x03,0x12,0x81,-1,0xE8,0x85,0x01,0x79, \
+-1,0xCB,0x39,0x2c,0x00,0x34,0x02,-1,0xF7,0x20,-1,0xEA,0x00,0x00,-1,0xC0,0x26,-1,0xC1,0x11, \
+-1,0xC5,0x35,0x3E,-1,0xC7,0xBE,-1,0xB1,0x00,0x1B,-1,0xB6,0x0a,0x82,0x27,0x00,-1,0xB7,0x07, \
+-1,0x3A,0x55,-1,0x36,0x48,-1,0x11,-2,120,-1,0x29,-2,20,-3 };
+
+static int ssd1351_init[] = { -1,0xfd,0x12,-1,0xfd,0xb1,-1,0xae,-1,0xb3,0xf1,-1,0xca,0x7f,-1,0xa0,0x74, \
+ -1,0x15,0x00,0x7f,-1,0x75,0x00,0x7f,-1,0xa1,0x00,-1,0xa2,0x00,-1,0xb5,0x00, \
+ -1,0xab,0x01,-1,0xb1,0x32,-1,0xb4,0xa0,0xb5,0x55,-1,0xbb,0x17,-1,0xbe,0x05, \
+ -1,0xc1,0xc8,0x80,0xc8,-1,0xc7,0x0f,-1,0xb6,0x01,-1,0xa6,-1,0xaf,-3 };
+
+
+/* ili9320, ili9325 */
+static void flexfb_set_addr_win_1(struct fbtft_par *par, int xs, int ys, int xe, int ye)
+{
+ fbtft_par_dbg(DEBUG_SET_ADDR_WIN, par, "%s(xs=%d, ys=%d, xe=%d, ye=%d)\n", __func__, xs, ys, xe, ye);
+ switch (par->info->var.rotate) {
+ /* R20h = Horizontal GRAM Start Address */
+ /* R21h = Vertical GRAM Start Address */
+ case 0:
+ write_reg(par, 0x0020, xs);
+ write_reg(par, 0x0021, ys);
+ break;
+ case 180:
+ write_reg(par, 0x0020, width - 1 - xs);
+ write_reg(par, 0x0021, height - 1 - ys);
+ break;
+ case 270:
+ write_reg(par, 0x0020, width - 1 - ys);
+ write_reg(par, 0x0021, xs);
+ break;
+ case 90:
+ write_reg(par, 0x0020, ys);
+ write_reg(par, 0x0021, height - 1 - xs);
+ break;
+ }
+ write_reg(par, 0x0022); /* Write Data to GRAM */
+}
+
+/* ssd1289 */
+static void flexfb_set_addr_win_2(struct fbtft_par *par, int xs, int ys, int xe, int ye)
+{
+ fbtft_par_dbg(DEBUG_SET_ADDR_WIN, par, "%s(xs=%d, ys=%d, xe=%d, ye=%d)\n", __func__, xs, ys, xe, ye);
+
+ switch (par->info->var.rotate) {
+ /* R4Eh - Set GDDRAM X address counter */
+ /* R4Fh - Set GDDRAM Y address counter */
+ case 0:
+ write_reg(par, 0x4e, xs);
+ write_reg(par, 0x4f, ys);
+ break;
+ case 180:
+ write_reg(par, 0x4e, par->info->var.xres - 1 - xs);
+ write_reg(par, 0x4f, par->info->var.yres - 1 - ys);
+ break;
+ case 270:
+ write_reg(par, 0x4e, par->info->var.yres - 1 - ys);
+ write_reg(par, 0x4f, xs);
+ break;
+ case 90:
+ write_reg(par, 0x4e, ys);
+ write_reg(par, 0x4f, par->info->var.xres - 1 - xs);
+ break;
+ }
+
+ /* R22h - RAM data write */
+ write_reg(par, 0x22, 0);
+}
+
+/* ssd1351 */
+static void set_addr_win_3(struct fbtft_par *par, int xs, int ys, int xe, int ye)
+{
+ fbtft_par_dbg(DEBUG_SET_ADDR_WIN, par, "%s(xs=%d, ys=%d, xe=%d, ye=%d)\n", __func__, xs, ys, xe, ye);
+
+ write_reg(par, 0x15, xs, xe);
+ write_reg(par, 0x75, ys, ye);
+ write_reg(par, 0x5C);
+}
+
+static int flexfb_verify_gpios_dc(struct fbtft_par *par)
+{
+ fbtft_par_dbg(DEBUG_VERIFY_GPIOS, par, "%s()\n", __func__);
+
+ if (par->gpio.dc < 0) {
+ dev_err(par->info->device, "Missing info about 'dc' gpio. Aborting.\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int flexfb_verify_gpios_db(struct fbtft_par *par)
+{
+ int i;
+ int num_db = buswidth;
+
+ fbtft_par_dbg(DEBUG_VERIFY_GPIOS, par, "%s()\n", __func__);
+
+ if (par->gpio.dc < 0) {
+ dev_err(par->info->device, "Missing info about 'dc' gpio. Aborting.\n");
+ return -EINVAL;
+ }
+ if (par->gpio.wr < 0) {
+ dev_err(par->info->device, "Missing info about 'wr' gpio. Aborting.\n");
+ return -EINVAL;
+ }
+ if (latched && (par->gpio.latch < 0)) {
+ dev_err(par->info->device, "Missing info about 'latch' gpio. Aborting.\n");
+ return -EINVAL;
+ }
+ if (latched)
+ num_db=buswidth/2;
+ for (i=0;i < num_db;i++) {
+ if (par->gpio.db[i] < 0) {
+ dev_err(par->info->device, "Missing info about 'db%02d' gpio. Aborting.\n", i);
+ return -EINVAL;
+ }
+ }
+
+ return 0;
+}
+
+static struct fbtft_display flex_display = { };
+
+static int flexfb_probe_common(struct spi_device *sdev, struct platform_device *pdev)
+{
+ struct device *dev;
+ struct fb_info *info;
+ struct fbtft_par *par;
+ int ret;
+
+ initp = init;
+ initp_num = init_num;
+
+ if (sdev)
+ dev = &sdev->dev;
+ else
+ dev = &pdev->dev;
+
+ fbtft_init_dbg(dev, "%s(%s)\n", __func__, sdev ? "'SPI device'" : "'Platform device'");
+
+ if (chip) {
+
+ if (!strcmp(chip, "st7735r")) {
+ if (!width)
+ width = 128;
+ if (!height)
+ height = 160;
+ if (init_num == 0) {
+ initp = st7735r_init;
+ initp_num = ARRAY_SIZE(st7735r_init);
+ }
+
+
+ } else if (!strcmp(chip, "hx8340bn")) {
+ if (!width)
+ width = 176;
+ if (!height)
+ height = 220;
+ setaddrwin = 0;
+ if (init_num == 0) {
+ initp = hx8340bn_init;
+ initp_num = ARRAY_SIZE(hx8340bn_init);
+ }
+
+
+ } else if (!strcmp(chip, "ili9225")) {
+ if (!width)
+ width = 176;
+ if (!height)
+ height = 220;
+ setaddrwin = 0;
+ regwidth = 16;
+ if (init_num == 0) {
+ initp = ili9225_init;
+ initp_num = ARRAY_SIZE(ili9225_init);
+ }
+
+
+
+ } else if (!strcmp(chip, "ili9320")) {
+ if (!width)
+ width = 240;
+ if (!height)
+ height = 320;
+ setaddrwin = 1;
+ regwidth = 16;
+ if (init_num == 0) {
+ initp = ili9320_init;
+ initp_num = ARRAY_SIZE(ili9320_init);
+ }
+
+
+ } else if (!strcmp(chip, "ili9325")) {
+ if (!width)
+ width = 240;
+ if (!height)
+ height = 320;
+ setaddrwin = 1;
+ regwidth = 16;
+ if (init_num == 0) {
+ initp = ili9325_init;
+ initp_num = ARRAY_SIZE(ili9325_init);
+ }
+
+ } else if (!strcmp(chip, "ili9341")) {
+ if (!width)
+ width = 240;
+ if (!height)
+ height = 320;
+ setaddrwin = 0;
+ regwidth = 8;
+ if (init_num == 0) {
+ initp = ili9341_init;
+ initp_num = ARRAY_SIZE(ili9341_init);
+ }
+
+
+ } else if (!strcmp(chip, "ssd1289")) {
+ if (!width)
+ width = 240;
+ if (!height)
+ height = 320;
+ setaddrwin = 2;
+ regwidth = 16;
+ if (init_num == 0) {
+ initp = ssd1289_init;
+ initp_num = ARRAY_SIZE(ssd1289_init);
+ }
+
+
+
+ } else if (!strcmp(chip, "ssd1351")) {
+ if (!width)
+ width = 128;
+ if (!height)
+ height = 128;
+ setaddrwin = 3;
+ if (init_num == 0) {
+ initp = ssd1351_init;
+ initp_num = ARRAY_SIZE(ssd1351_init);
+ }
+ } else {
+ dev_err(dev, "chip=%s is not supported\n", chip);
+ return -EINVAL;
+ }
+ }
+
+ if (width == 0 || height == 0) {
+ dev_err(dev, "argument(s) missing: width and height has to be set.\n");
+ return -EINVAL;
+ }
+ flex_display.width = width;
+ flex_display.height = height;
+ fbtft_init_dbg(dev, "Display resolution: %dx%d\n", width, height);
+ fbtft_init_dbg(dev, "chip = %s\n", chip ? chip : "not set");
+ fbtft_init_dbg(dev, "setaddrwin = %d\n", setaddrwin);
+ fbtft_init_dbg(dev, "regwidth = %d\n", regwidth);
+ fbtft_init_dbg(dev, "buswidth = %d\n", buswidth);
+
+ info = fbtft_framebuffer_alloc(&flex_display, dev);
+ if (!info)
+ return -ENOMEM;
+
+ par = info->par;
+ if (sdev)
+ par->spi = sdev;
+ else
+ par->pdev = pdev;
+ if (!par->init_sequence)
+ par->init_sequence = initp;
+ par->fbtftops.init_display = fbtft_init_display;
+
+ /* registerwrite functions */
+ switch (regwidth) {
+ case 8:
+ par->fbtftops.write_register = fbtft_write_reg8_bus8;
+ break;
+ case 16:
+ par->fbtftops.write_register = fbtft_write_reg16_bus8;
+ break;
+ default:
+ dev_err(dev, "argument 'regwidth': %d is not supported.\n", regwidth);
+ return -EINVAL;
+ }
+
+ /* bus functions */
+ if (sdev) {
+ par->fbtftops.write = fbtft_write_spi;
+ switch (buswidth) {
+ case 8:
+ par->fbtftops.write_vmem = fbtft_write_vmem16_bus8;
+ if (!par->startbyte)
+ par->fbtftops.verify_gpios = flexfb_verify_gpios_dc;
+ break;
+ case 9:
+ if (regwidth == 16) {
+ dev_err(dev, "argument 'regwidth': %d is not supported with buswidth=%d and SPI.\n", regwidth, buswidth);
+ return -EINVAL;
+ }
+ par->fbtftops.write_register = fbtft_write_reg8_bus9;
+ par->fbtftops.write_vmem = fbtft_write_vmem16_bus9;
+ sdev->bits_per_word = 9;
+ ret = sdev->master->setup(sdev);
+ if (ret) {
+ dev_warn(dev,
+ "9-bit SPI not available, emulating using 8-bit.\n");
+ sdev->bits_per_word = 8;
+ ret = sdev->master->setup(sdev);
+ if (ret)
+ goto out_release;
+ /* allocate buffer with room for dc bits */
+ par->extra = devm_kzalloc(par->info->device,
+ par->txbuf.len + (par->txbuf.len / 8) + 8,
+ GFP_KERNEL);
+ if (!par->extra) {
+ ret = -ENOMEM;
+ goto out_release;
+ }
+ par->fbtftops.write = fbtft_write_spi_emulate_9;
+ }
+ break;
+ default:
+ dev_err(dev, "argument 'buswidth': %d is not supported with SPI.\n", buswidth);
+ return -EINVAL;
+ }
+ } else {
+ par->fbtftops.verify_gpios = flexfb_verify_gpios_db;
+ switch (buswidth) {
+ case 8:
+ par->fbtftops.write = fbtft_write_gpio8_wr;
+ par->fbtftops.write_vmem = fbtft_write_vmem16_bus8;
+ break;
+ case 16:
+ par->fbtftops.write_register = fbtft_write_reg16_bus16;
+ if (latched)
+ par->fbtftops.write = fbtft_write_gpio16_wr_latched;
+ else
+ par->fbtftops.write = fbtft_write_gpio16_wr;
+ par->fbtftops.write_vmem = fbtft_write_vmem16_bus16;
+ break;
+ default:
+ dev_err(dev, "argument 'buswidth': %d is not supported with parallel.\n", buswidth);
+ return -EINVAL;
+ }
+ }
+
+ /* set_addr_win function */
+ switch (setaddrwin) {
+ case 0:
+ /* use default */
+ break;
+ case 1:
+ par->fbtftops.set_addr_win = flexfb_set_addr_win_1;
+ break;
+ case 2:
+ par->fbtftops.set_addr_win = flexfb_set_addr_win_2;
+ break;
+ case 3:
+ par->fbtftops.set_addr_win = set_addr_win_3;
+ break;
+ default:
+ dev_err(dev, "argument 'setaddrwin': unknown value %d.\n", setaddrwin);
+ return -EINVAL;
+ }
+
+ if (!nobacklight)
+ par->fbtftops.register_backlight = fbtft_register_backlight;
+
+ ret = fbtft_register_framebuffer(info);
+ if (ret < 0)
+ goto out_release;
+
+ return 0;
+
+out_release:
+ fbtft_framebuffer_release(info);
+
+ return ret;
+}
+
+static int flexfb_remove_common(struct device *dev, struct fb_info *info)
+{
+ struct fbtft_par *par;
+
+ if (!info)
+ return -EINVAL;
+ par = info->par;
+ if (par)
+ fbtft_par_dbg(DEBUG_DRIVER_INIT_FUNCTIONS, par,
+ "%s()\n", __func__);
+ fbtft_unregister_framebuffer(info);
+ fbtft_framebuffer_release(info);
+
+ return 0;
+}
+
+static int flexfb_probe_spi(struct spi_device *spi)
+{
+ return flexfb_probe_common(spi, NULL);
+}
+
+static int flexfb_remove_spi(struct spi_device *spi)
+{
+ struct fb_info *info = spi_get_drvdata(spi);
+
+ return flexfb_remove_common(&spi->dev, info);
+}
+
+static int flexfb_probe_pdev(struct platform_device *pdev)
+{
+ return flexfb_probe_common(NULL, pdev);
+}
+
+static int flexfb_remove_pdev(struct platform_device *pdev)
+{
+ struct fb_info *info = platform_get_drvdata(pdev);
+
+ return flexfb_remove_common(&pdev->dev, info);
+}
+
+static struct spi_driver flexfb_spi_driver = {
+ .driver = {
+ .name = DRVNAME,
+ .owner = THIS_MODULE,
+ },
+ .probe = flexfb_probe_spi,
+ .remove = flexfb_remove_spi,
+};
+
+static const struct platform_device_id flexfb_platform_ids[] = {
+ { "flexpfb", 0 },
+ { },
+};
+
+static struct platform_driver flexfb_platform_driver = {
+ .driver = {
+ .name = DRVNAME,
+ },
+ .id_table = flexfb_platform_ids,
+ .probe = flexfb_probe_pdev,
+ .remove = flexfb_remove_pdev,
+};
+
+static int __init flexfb_init(void)
+{
+ int ret, ret2;
+
+ ret = spi_register_driver(&flexfb_spi_driver);
+ ret2 = platform_driver_register(&flexfb_platform_driver);
+ if (ret < 0)
+ return ret;
+ return ret2;
+}
+
+static void __exit flexfb_exit(void)
+{
+ spi_unregister_driver(&flexfb_spi_driver);
+ platform_driver_unregister(&flexfb_platform_driver);
+}
+
+/* ------------------------------------------------------------------------- */
+
+module_init(flexfb_init);
+module_exit(flexfb_exit);
+
+MODULE_DESCRIPTION("Generic FB driver for TFT LCD displays");
+MODULE_AUTHOR("Noralf Tronnes");
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/fbtft/internal.h b/drivers/staging/fbtft/internal.h
new file mode 100644
index 000000000..f69db8289
--- /dev/null
+++ b/drivers/staging/fbtft/internal.h
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2013 Noralf Tronnes
+ *
+ * This program is free software; you can redistribute it and/or 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.
+ *
+ */
+
+#ifndef __LINUX_FBTFT__INTERNAL_H
+#define __LINUX_FBTFT_INTERNAL_H
+
+void fbtft_sysfs_init(struct fbtft_par *par);
+void fbtft_sysfs_exit(struct fbtft_par *par);
+void fbtft_expand_debug_value(unsigned long *debug);
+int fbtft_gamma_parse_str(struct fbtft_par *par, unsigned long *curves,
+ const char *str, int size);
+
+#endif /* __LINUX_FBTFT_INTERNAL_H */
diff --git a/drivers/staging/fsl-mc/Kconfig b/drivers/staging/fsl-mc/Kconfig
new file mode 100644
index 000000000..32df07b15
--- /dev/null
+++ b/drivers/staging/fsl-mc/Kconfig
@@ -0,0 +1 @@
+source "drivers/staging/fsl-mc/bus/Kconfig"
diff --git a/drivers/staging/fsl-mc/Makefile b/drivers/staging/fsl-mc/Makefile
new file mode 100644
index 000000000..9c6a00128
--- /dev/null
+++ b/drivers/staging/fsl-mc/Makefile
@@ -0,0 +1,2 @@
+# Freescale Management Complex (MC) bus drivers
+obj-$(CONFIG_FSL_MC_BUS) += bus/
diff --git a/drivers/staging/fsl-mc/TODO b/drivers/staging/fsl-mc/TODO
new file mode 100644
index 000000000..d78288b4e
--- /dev/null
+++ b/drivers/staging/fsl-mc/TODO
@@ -0,0 +1,13 @@
+* Add README file (with ASCII art) describing relationships between
+ DPAA2 objects and how combine them to make a NIC, an LS2 switch, etc.
+ Also, define all acronyms used.
+
+* Decide if multiple root fsl-mc buses will be supported per Linux instance,
+ and if so add support for this.
+
+* Add at least one device driver for a DPAA2 object (child device of the
+ fsl-mc bus).
+
+Please send any patches to Greg Kroah-Hartman <gregkh@linuxfoundation.org>,
+german.rivera@freescale.com, devel@driverdev.osuosl.org,
+linux-kernel@vger.kernel.org
diff --git a/drivers/staging/fsl-mc/bus/Kconfig b/drivers/staging/fsl-mc/bus/Kconfig
new file mode 100644
index 000000000..0d779d9cc
--- /dev/null
+++ b/drivers/staging/fsl-mc/bus/Kconfig
@@ -0,0 +1,24 @@
+#
+# Freescale Management Complex (MC) bus drivers
+#
+# Copyright (C) 2014 Freescale Semiconductor, Inc.
+#
+# This file is released under the GPLv2
+#
+
+config FSL_MC_BUS
+ tristate "Freescale Management Complex (MC) bus driver"
+ depends on OF && ARM64
+ help
+ Driver to enable the bus infrastructure for the Freescale
+ QorIQ Management Complex (fsl-mc). The fsl-mc is a hardware
+ module of the QorIQ LS2 SoCs, that does resource management
+ for hardware building-blocks in the SoC that can be used
+ to dynamically create networking hardware objects such as
+ network interfaces (NICs), crypto accelerator instances,
+ or L2 switches.
+
+ Only enable this option when building the kernel for
+ Freescale QorQIQ LS2xxxx SoCs.
+
+
diff --git a/drivers/staging/fsl-mc/bus/Makefile b/drivers/staging/fsl-mc/bus/Makefile
new file mode 100644
index 000000000..25433a998
--- /dev/null
+++ b/drivers/staging/fsl-mc/bus/Makefile
@@ -0,0 +1,17 @@
+#
+# Freescale Management Complex (MC) bus drivers
+#
+# Copyright (C) 2014 Freescale Semiconductor, Inc.
+#
+# This file is released under the GPLv2
+#
+obj-$(CONFIG_FSL_MC_BUS) += mc-bus-driver.o
+
+mc-bus-driver-objs := mc-bus.o \
+ mc-sys.o \
+ dprc.o \
+ dpmng.o \
+ dprc-driver.o \
+ mc-allocator.o \
+ dpmcp.o \
+ dpbp.o
diff --git a/drivers/staging/fsl-mc/bus/dpbp.c b/drivers/staging/fsl-mc/bus/dpbp.c
new file mode 100644
index 000000000..d99ab6d0b
--- /dev/null
+++ b/drivers/staging/fsl-mc/bus/dpbp.c
@@ -0,0 +1,358 @@
+/* Copyright 2013-2014 Freescale Semiconductor Inc.
+*
+* Redistribution and use in source and binary forms, with or without
+* modification, are permitted provided that the following conditions are met:
+* * Redistributions of source code must retain the above copyright
+* notice, this list of conditions and the following disclaimer.
+* * Redistributions in binary form must reproduce the above copyright
+* notice, this list of conditions and the following disclaimer in the
+* documentation and/or other materials provided with the distribution.
+* * Neither the name of the above-listed copyright holders nor the
+* names of any contributors may be used to endorse or promote products
+* derived from this software without specific prior written permission.
+*
+*
+* ALTERNATIVELY, this software may be distributed under the terms of the
+* GNU General Public License ("GPL") as published by the Free Software
+* Foundation, either version 2 of that License or (at your option) any
+* later version.
+*
+* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT HOLDERS OR CONTRIBUTORS 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.
+*/
+#include "../include/mc-sys.h"
+#include "../include/mc-cmd.h"
+#include "../include/dpbp.h"
+#include "../include/dpbp-cmd.h"
+
+int dpbp_open(struct fsl_mc_io *mc_io, int dpbp_id, uint16_t *token)
+{
+ struct mc_command cmd = { 0 };
+ int err;
+
+ /* prepare command */
+ cmd.header = mc_encode_cmd_header(DPBP_CMDID_OPEN,
+ MC_CMD_PRI_LOW, 0);
+ cmd.params[0] |= mc_enc(0, 32, dpbp_id);
+
+ /* send command to mc*/
+ err = mc_send_command(mc_io, &cmd);
+ if (err)
+ return err;
+
+ /* retrieve response parameters */
+ *token = MC_CMD_HDR_READ_TOKEN(cmd.header);
+
+ return err;
+}
+EXPORT_SYMBOL(dpbp_open);
+
+int dpbp_close(struct fsl_mc_io *mc_io, uint16_t token)
+{
+ struct mc_command cmd = { 0 };
+
+ /* prepare command */
+ cmd.header = mc_encode_cmd_header(DPBP_CMDID_CLOSE, MC_CMD_PRI_HIGH,
+ token);
+
+ /* send command to mc*/
+ return mc_send_command(mc_io, &cmd);
+}
+EXPORT_SYMBOL(dpbp_close);
+
+int dpbp_create(struct fsl_mc_io *mc_io,
+ const struct dpbp_cfg *cfg,
+ uint16_t *token)
+{
+ struct mc_command cmd = { 0 };
+ int err;
+
+ (void)(cfg); /* unused */
+
+ /* prepare command */
+ cmd.header = mc_encode_cmd_header(DPBP_CMDID_CREATE,
+ MC_CMD_PRI_LOW, 0);
+
+ /* send command to mc*/
+ err = mc_send_command(mc_io, &cmd);
+ if (err)
+ return err;
+
+ /* retrieve response parameters */
+ *token = MC_CMD_HDR_READ_TOKEN(cmd.header);
+
+ return 0;
+}
+
+int dpbp_destroy(struct fsl_mc_io *mc_io, uint16_t token)
+{
+ struct mc_command cmd = { 0 };
+
+ /* prepare command */
+ cmd.header = mc_encode_cmd_header(DPBP_CMDID_DESTROY,
+ MC_CMD_PRI_LOW, token);
+
+ /* send command to mc*/
+ return mc_send_command(mc_io, &cmd);
+}
+
+int dpbp_enable(struct fsl_mc_io *mc_io, uint16_t token)
+{
+ struct mc_command cmd = { 0 };
+
+ /* prepare command */
+ cmd.header = mc_encode_cmd_header(DPBP_CMDID_ENABLE, MC_CMD_PRI_LOW,
+ token);
+
+ /* send command to mc*/
+ return mc_send_command(mc_io, &cmd);
+}
+EXPORT_SYMBOL(dpbp_enable);
+
+int dpbp_disable(struct fsl_mc_io *mc_io, uint16_t token)
+{
+ struct mc_command cmd = { 0 };
+
+ /* prepare command */
+ cmd.header = mc_encode_cmd_header(DPBP_CMDID_DISABLE,
+ MC_CMD_PRI_LOW, token);
+
+ /* send command to mc*/
+ return mc_send_command(mc_io, &cmd);
+}
+EXPORT_SYMBOL(dpbp_disable);
+
+int dpbp_is_enabled(struct fsl_mc_io *mc_io, uint16_t token, int *en)
+{
+ struct mc_command cmd = { 0 };
+ int err;
+ /* prepare command */
+ cmd.header = mc_encode_cmd_header(DPBP_CMDID_IS_ENABLED, MC_CMD_PRI_LOW,
+ token);
+
+ /* send command to mc*/
+ err = mc_send_command(mc_io, &cmd);
+ if (err)
+ return err;
+
+ /* retrieve response parameters */
+ *en = (int)mc_dec(cmd.params[0], 0, 1);
+
+ return 0;
+}
+
+int dpbp_reset(struct fsl_mc_io *mc_io, uint16_t token)
+{
+ struct mc_command cmd = { 0 };
+
+ /* prepare command */
+ cmd.header = mc_encode_cmd_header(DPBP_CMDID_RESET,
+ MC_CMD_PRI_LOW, token);
+
+ /* send command to mc*/
+ return mc_send_command(mc_io, &cmd);
+}
+
+int dpbp_set_irq(struct fsl_mc_io *mc_io,
+ uint16_t token,
+ uint8_t irq_index,
+ uint64_t irq_paddr,
+ uint32_t irq_val,
+ int user_irq_id)
+{
+ struct mc_command cmd = { 0 };
+
+ /* prepare command */
+ cmd.header = mc_encode_cmd_header(DPBP_CMDID_SET_IRQ,
+ MC_CMD_PRI_LOW, token);
+ cmd.params[0] |= mc_enc(0, 8, irq_index);
+ cmd.params[0] |= mc_enc(32, 32, irq_val);
+ cmd.params[1] |= mc_enc(0, 64, irq_paddr);
+ cmd.params[2] |= mc_enc(0, 32, user_irq_id);
+
+ /* send command to mc*/
+ return mc_send_command(mc_io, &cmd);
+}
+
+int dpbp_get_irq(struct fsl_mc_io *mc_io,
+ uint16_t token,
+ uint8_t irq_index,
+ int *type,
+ uint64_t *irq_paddr,
+ uint32_t *irq_val,
+ int *user_irq_id)
+{
+ struct mc_command cmd = { 0 };
+ int err;
+
+ /* prepare command */
+ cmd.header = mc_encode_cmd_header(DPBP_CMDID_GET_IRQ,
+ MC_CMD_PRI_LOW, token);
+ cmd.params[0] |= mc_enc(32, 8, irq_index);
+
+ /* send command to mc*/
+ err = mc_send_command(mc_io, &cmd);
+ if (err)
+ return err;
+
+ /* retrieve response parameters */
+ *irq_val = (uint32_t)mc_dec(cmd.params[0], 0, 32);
+ *irq_paddr = (uint64_t)mc_dec(cmd.params[1], 0, 64);
+ *user_irq_id = (int)mc_dec(cmd.params[2], 0, 32);
+ *type = (int)mc_dec(cmd.params[2], 32, 32);
+ return 0;
+}
+
+int dpbp_set_irq_enable(struct fsl_mc_io *mc_io,
+ uint16_t token,
+ uint8_t irq_index,
+ uint8_t en)
+{
+ struct mc_command cmd = { 0 };
+
+ /* prepare command */
+ cmd.header = mc_encode_cmd_header(DPBP_CMDID_SET_IRQ_ENABLE,
+ MC_CMD_PRI_LOW, token);
+ cmd.params[0] |= mc_enc(0, 8, en);
+ cmd.params[0] |= mc_enc(32, 8, irq_index);
+
+ /* send command to mc*/
+ return mc_send_command(mc_io, &cmd);
+}
+
+int dpbp_get_irq_enable(struct fsl_mc_io *mc_io,
+ uint16_t token,
+ uint8_t irq_index,
+ uint8_t *en)
+{
+ struct mc_command cmd = { 0 };
+ int err;
+
+ /* prepare command */
+ cmd.header = mc_encode_cmd_header(DPBP_CMDID_GET_IRQ_ENABLE,
+ MC_CMD_PRI_LOW, token);
+ cmd.params[0] |= mc_enc(32, 8, irq_index);
+
+ /* send command to mc*/
+ err = mc_send_command(mc_io, &cmd);
+ if (err)
+ return err;
+
+ /* retrieve response parameters */
+ *en = (uint8_t)mc_dec(cmd.params[0], 0, 8);
+ return 0;
+}
+
+int dpbp_set_irq_mask(struct fsl_mc_io *mc_io,
+ uint16_t token,
+ uint8_t irq_index,
+ uint32_t mask)
+{
+ struct mc_command cmd = { 0 };
+
+ /* prepare command */
+ cmd.header = mc_encode_cmd_header(DPBP_CMDID_SET_IRQ_MASK,
+ MC_CMD_PRI_LOW, token);
+ cmd.params[0] |= mc_enc(0, 32, mask);
+ cmd.params[0] |= mc_enc(32, 8, irq_index);
+
+ /* send command to mc*/
+ return mc_send_command(mc_io, &cmd);
+}
+
+int dpbp_get_irq_mask(struct fsl_mc_io *mc_io,
+ uint16_t token,
+ uint8_t irq_index,
+ uint32_t *mask)
+{
+ struct mc_command cmd = { 0 };
+ int err;
+
+ /* prepare command */
+ cmd.header = mc_encode_cmd_header(DPBP_CMDID_GET_IRQ_MASK,
+ MC_CMD_PRI_LOW, token);
+ cmd.params[0] |= mc_enc(32, 8, irq_index);
+
+ /* send command to mc*/
+ err = mc_send_command(mc_io, &cmd);
+ if (err)
+ return err;
+
+ /* retrieve response parameters */
+ *mask = (uint32_t)mc_dec(cmd.params[0], 0, 32);
+ return 0;
+}
+
+int dpbp_get_irq_status(struct fsl_mc_io *mc_io,
+ uint16_t token,
+ uint8_t irq_index,
+ uint32_t *status)
+{
+ struct mc_command cmd = { 0 };
+ int err;
+
+ /* prepare command */
+ cmd.header = mc_encode_cmd_header(DPBP_CMDID_GET_IRQ_STATUS,
+ MC_CMD_PRI_LOW, token);
+ cmd.params[0] |= mc_enc(32, 8, irq_index);
+
+ /* send command to mc*/
+ err = mc_send_command(mc_io, &cmd);
+ if (err)
+ return err;
+
+ /* retrieve response parameters */
+ *status = (uint32_t)mc_dec(cmd.params[0], 0, 32);
+ return 0;
+}
+
+int dpbp_clear_irq_status(struct fsl_mc_io *mc_io,
+ uint16_t token,
+ uint8_t irq_index,
+ uint32_t status)
+{
+ struct mc_command cmd = { 0 };
+
+ /* prepare command */
+ cmd.header = mc_encode_cmd_header(DPBP_CMDID_CLEAR_IRQ_STATUS,
+ MC_CMD_PRI_LOW, token);
+ cmd.params[0] |= mc_enc(0, 32, status);
+ cmd.params[0] |= mc_enc(32, 8, irq_index);
+
+ /* send command to mc*/
+ return mc_send_command(mc_io, &cmd);
+}
+
+int dpbp_get_attributes(struct fsl_mc_io *mc_io,
+ uint16_t token,
+ struct dpbp_attr *attr)
+{
+ struct mc_command cmd = { 0 };
+ int err;
+
+ /* prepare command */
+ cmd.header = mc_encode_cmd_header(DPBP_CMDID_GET_ATTR,
+ MC_CMD_PRI_LOW, token);
+
+ /* send command to mc*/
+ err = mc_send_command(mc_io, &cmd);
+ if (err)
+ return err;
+
+ /* retrieve response parameters */
+ attr->bpid = (uint16_t)mc_dec(cmd.params[0], 16, 16);
+ attr->id = (int)mc_dec(cmd.params[0], 32, 32);
+ attr->version.major = (uint16_t)mc_dec(cmd.params[1], 0, 16);
+ attr->version.minor = (uint16_t)mc_dec(cmd.params[1], 16, 16);
+ return 0;
+}
+EXPORT_SYMBOL(dpbp_get_attributes);
diff --git a/drivers/staging/fsl-mc/bus/dpmcp-cmd.h b/drivers/staging/fsl-mc/bus/dpmcp-cmd.h
new file mode 100644
index 000000000..57f326b60
--- /dev/null
+++ b/drivers/staging/fsl-mc/bus/dpmcp-cmd.h
@@ -0,0 +1,136 @@
+/* Copyright 2013-2015 Freescale Semiconductor Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of the above-listed copyright holders nor the
+ * names of any contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ *
+ * ALTERNATIVELY, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") as published by the Free Software
+ * Foundation, either version 2 of that License or (at your option) any
+ * later version.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT HOLDERS OR CONTRIBUTORS 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.
+ */
+#ifndef _FSL_DPMCP_CMD_H
+#define _FSL_DPMCP_CMD_H
+
+/* DPMCP Version */
+#define DPMCP_VER_MAJOR 2
+#define DPMCP_VER_MINOR 0
+
+/* Command IDs */
+#define DPMCP_CMDID_CLOSE 0x800
+#define DPMCP_CMDID_OPEN 0x80b
+#define DPMCP_CMDID_CREATE 0x90b
+#define DPMCP_CMDID_DESTROY 0x900
+
+#define DPMCP_CMDID_GET_ATTR 0x004
+#define DPMCP_CMDID_RESET 0x005
+
+#define DPMCP_CMDID_SET_IRQ 0x010
+#define DPMCP_CMDID_GET_IRQ 0x011
+#define DPMCP_CMDID_SET_IRQ_ENABLE 0x012
+#define DPMCP_CMDID_GET_IRQ_ENABLE 0x013
+#define DPMCP_CMDID_SET_IRQ_MASK 0x014
+#define DPMCP_CMDID_GET_IRQ_MASK 0x015
+#define DPMCP_CMDID_GET_IRQ_STATUS 0x016
+#define DPMCP_CMDID_CLEAR_IRQ_STATUS 0x017
+
+/* cmd, param, offset, width, type, arg_name */
+#define DPMCP_CMD_CREATE(cmd, cfg) \
+ MC_CMD_OP(cmd, 0, 0, 32, int, cfg->portal_id)
+
+/* cmd, param, offset, width, type, arg_name */
+#define DPMCP_CMD_SET_IRQ(cmd, irq_index, irq_addr, irq_val, user_irq_id) \
+do { \
+ MC_CMD_OP(cmd, 0, 0, 8, uint8_t, irq_index);\
+ MC_CMD_OP(cmd, 0, 32, 32, uint32_t, irq_val);\
+ MC_CMD_OP(cmd, 1, 0, 64, uint64_t, irq_addr); \
+ MC_CMD_OP(cmd, 2, 0, 32, int, user_irq_id); \
+} while (0)
+
+/* cmd, param, offset, width, type, arg_name */
+#define DPMCP_CMD_GET_IRQ(cmd, irq_index) \
+ MC_CMD_OP(cmd, 0, 32, 8, uint8_t, irq_index)
+
+/* cmd, param, offset, width, type, arg_name */
+#define DPMCP_RSP_GET_IRQ(cmd, type, irq_addr, irq_val, user_irq_id) \
+do { \
+ MC_RSP_OP(cmd, 0, 0, 32, uint32_t, irq_val); \
+ MC_RSP_OP(cmd, 1, 0, 64, uint64_t, irq_addr); \
+ MC_RSP_OP(cmd, 2, 0, 32, int, user_irq_id); \
+ MC_RSP_OP(cmd, 2, 32, 32, int, type); \
+} while (0)
+
+/* cmd, param, offset, width, type, arg_name */
+#define DPMCP_CMD_SET_IRQ_ENABLE(cmd, irq_index, en) \
+do { \
+ MC_CMD_OP(cmd, 0, 0, 8, uint8_t, en); \
+ MC_CMD_OP(cmd, 0, 32, 8, uint8_t, irq_index);\
+} while (0)
+
+/* cmd, param, offset, width, type, arg_name */
+#define DPMCP_CMD_GET_IRQ_ENABLE(cmd, irq_index) \
+ MC_CMD_OP(cmd, 0, 32, 8, uint8_t, irq_index)
+
+/* cmd, param, offset, width, type, arg_name */
+#define DPMCP_RSP_GET_IRQ_ENABLE(cmd, en) \
+ MC_RSP_OP(cmd, 0, 0, 8, uint8_t, en)
+
+/* cmd, param, offset, width, type, arg_name */
+#define DPMCP_CMD_SET_IRQ_MASK(cmd, irq_index, mask) \
+do { \
+ MC_CMD_OP(cmd, 0, 0, 32, uint32_t, mask);\
+ MC_CMD_OP(cmd, 0, 32, 8, uint8_t, irq_index);\
+} while (0)
+
+/* cmd, param, offset, width, type, arg_name */
+#define DPMCP_CMD_GET_IRQ_MASK(cmd, irq_index) \
+ MC_CMD_OP(cmd, 0, 32, 8, uint8_t, irq_index)
+
+/* cmd, param, offset, width, type, arg_name */
+#define DPMCP_RSP_GET_IRQ_MASK(cmd, mask) \
+ MC_RSP_OP(cmd, 0, 0, 32, uint32_t, mask)
+
+/* cmd, param, offset, width, type, arg_name */
+#define DPMCP_CMD_GET_IRQ_STATUS(cmd, irq_index) \
+ MC_CMD_OP(cmd, 0, 32, 8, uint8_t, irq_index)
+
+/* cmd, param, offset, width, type, arg_name */
+#define DPMCP_RSP_GET_IRQ_STATUS(cmd, status) \
+ MC_RSP_OP(cmd, 0, 0, 32, uint32_t, status)
+
+/* cmd, param, offset, width, type, arg_name */
+#define DPMCP_CMD_CLEAR_IRQ_STATUS(cmd, irq_index, status) \
+do { \
+ MC_CMD_OP(cmd, 0, 0, 32, uint32_t, status); \
+ MC_CMD_OP(cmd, 0, 32, 8, uint8_t, irq_index);\
+} while (0)
+
+/* cmd, param, offset, width, type, arg_name */
+#define DPMCP_RSP_GET_ATTRIBUTES(cmd, attr) \
+do { \
+ MC_RSP_OP(cmd, 0, 32, 32, int, attr->id);\
+ MC_RSP_OP(cmd, 1, 0, 16, uint16_t, attr->version.major);\
+ MC_RSP_OP(cmd, 1, 16, 16, uint16_t, attr->version.minor);\
+} while (0)
+
+#endif /* _FSL_DPMCP_CMD_H */
diff --git a/drivers/staging/fsl-mc/bus/dpmcp.c b/drivers/staging/fsl-mc/bus/dpmcp.c
new file mode 100644
index 000000000..6b9da5b7f
--- /dev/null
+++ b/drivers/staging/fsl-mc/bus/dpmcp.c
@@ -0,0 +1,308 @@
+/* Copyright 2013-2015 Freescale Semiconductor Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of the above-listed copyright holders nor the
+ * names of any contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ *
+ * ALTERNATIVELY, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") as published by the Free Software
+ * Foundation, either version 2 of that License or (at your option) any
+ * later version.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT HOLDERS OR CONTRIBUTORS 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.
+ */
+#include "../include/mc-sys.h"
+#include "../include/mc-cmd.h"
+#include "dpmcp.h"
+#include "dpmcp-cmd.h"
+
+int dpmcp_open(struct fsl_mc_io *mc_io, int dpmcp_id, uint16_t *token)
+{
+ struct mc_command cmd = { 0 };
+ int err;
+
+ /* prepare command */
+ cmd.header = mc_encode_cmd_header(DPMCP_CMDID_OPEN,
+ MC_CMD_PRI_LOW, 0);
+ cmd.params[0] |= mc_enc(0, 32, dpmcp_id);
+
+ /* send command to mc*/
+ err = mc_send_command(mc_io, &cmd);
+ if (err)
+ return err;
+
+ /* retrieve response parameters */
+ *token = MC_CMD_HDR_READ_TOKEN(cmd.header);
+
+ return err;
+}
+
+int dpmcp_close(struct fsl_mc_io *mc_io, uint16_t token)
+{
+ struct mc_command cmd = { 0 };
+
+ /* prepare command */
+ cmd.header = mc_encode_cmd_header(DPMCP_CMDID_CLOSE, MC_CMD_PRI_HIGH,
+ token);
+
+ /* send command to mc*/
+ return mc_send_command(mc_io, &cmd);
+}
+
+int dpmcp_create(struct fsl_mc_io *mc_io,
+ const struct dpmcp_cfg *cfg,
+ uint16_t *token)
+{
+ struct mc_command cmd = { 0 };
+ int err;
+
+ /* prepare command */
+ cmd.header = mc_encode_cmd_header(DPMCP_CMDID_CREATE,
+ MC_CMD_PRI_LOW, 0);
+ cmd.params[0] |= mc_enc(0, 32, cfg->portal_id);
+
+ /* send command to mc*/
+ err = mc_send_command(mc_io, &cmd);
+ if (err)
+ return err;
+
+ /* retrieve response parameters */
+ *token = MC_CMD_HDR_READ_TOKEN(cmd.header);
+
+ return 0;
+}
+
+int dpmcp_destroy(struct fsl_mc_io *mc_io, uint16_t token)
+{
+ struct mc_command cmd = { 0 };
+
+ /* prepare command */
+ cmd.header = mc_encode_cmd_header(DPMCP_CMDID_DESTROY,
+ MC_CMD_PRI_LOW, token);
+
+ /* send command to mc*/
+ return mc_send_command(mc_io, &cmd);
+}
+
+int dpmcp_reset(struct fsl_mc_io *mc_io, uint16_t token)
+{
+ struct mc_command cmd = { 0 };
+
+ /* prepare command */
+ cmd.header = mc_encode_cmd_header(DPMCP_CMDID_RESET,
+ MC_CMD_PRI_LOW, token);
+
+ /* send command to mc*/
+ return mc_send_command(mc_io, &cmd);
+}
+
+int dpmcp_set_irq(struct fsl_mc_io *mc_io,
+ uint16_t token,
+ uint8_t irq_index,
+ uint64_t irq_addr,
+ uint32_t irq_val,
+ int user_irq_id)
+{
+ struct mc_command cmd = { 0 };
+
+ /* prepare command */
+ cmd.header = mc_encode_cmd_header(DPMCP_CMDID_SET_IRQ,
+ MC_CMD_PRI_LOW, token);
+ cmd.params[0] |= mc_enc(0, 8, irq_index);
+ cmd.params[0] |= mc_enc(32, 32, irq_val);
+ cmd.params[1] |= mc_enc(0, 64, irq_addr);
+ cmd.params[2] |= mc_enc(0, 32, user_irq_id);
+
+ /* send command to mc*/
+ return mc_send_command(mc_io, &cmd);
+}
+
+int dpmcp_get_irq(struct fsl_mc_io *mc_io,
+ uint16_t token,
+ uint8_t irq_index,
+ int *type,
+ uint64_t *irq_addr,
+ uint32_t *irq_val,
+ int *user_irq_id)
+{
+ struct mc_command cmd = { 0 };
+ int err;
+
+ /* prepare command */
+ cmd.header = mc_encode_cmd_header(DPMCP_CMDID_GET_IRQ,
+ MC_CMD_PRI_LOW, token);
+ cmd.params[0] |= mc_enc(32, 8, irq_index);
+
+ /* send command to mc*/
+ err = mc_send_command(mc_io, &cmd);
+ if (err)
+ return err;
+
+ /* retrieve response parameters */
+ *irq_val = (uint32_t)mc_dec(cmd.params[0], 0, 32);
+ *irq_addr = (uint64_t)mc_dec(cmd.params[1], 0, 64);
+ *user_irq_id = (int)mc_dec(cmd.params[2], 0, 32);
+ *type = (int)mc_dec(cmd.params[2], 32, 32);
+ return 0;
+}
+
+int dpmcp_set_irq_enable(struct fsl_mc_io *mc_io,
+ uint16_t token,
+ uint8_t irq_index,
+ uint8_t en)
+{
+ struct mc_command cmd = { 0 };
+
+ /* prepare command */
+ cmd.header = mc_encode_cmd_header(DPMCP_CMDID_SET_IRQ_ENABLE,
+ MC_CMD_PRI_LOW, token);
+ cmd.params[0] |= mc_enc(0, 8, en);
+ cmd.params[0] |= mc_enc(32, 8, irq_index);
+
+ /* send command to mc*/
+ return mc_send_command(mc_io, &cmd);
+}
+
+int dpmcp_get_irq_enable(struct fsl_mc_io *mc_io,
+ uint16_t token,
+ uint8_t irq_index,
+ uint8_t *en)
+{
+ struct mc_command cmd = { 0 };
+ int err;
+
+ /* prepare command */
+ cmd.header = mc_encode_cmd_header(DPMCP_CMDID_GET_IRQ_ENABLE,
+ MC_CMD_PRI_LOW, token);
+ cmd.params[0] |= mc_enc(32, 8, irq_index);
+
+ /* send command to mc*/
+ err = mc_send_command(mc_io, &cmd);
+ if (err)
+ return err;
+
+ /* retrieve response parameters */
+ *en = (uint8_t)mc_dec(cmd.params[0], 0, 8);
+ return 0;
+}
+
+int dpmcp_set_irq_mask(struct fsl_mc_io *mc_io,
+ uint16_t token,
+ uint8_t irq_index,
+ uint32_t mask)
+{
+ struct mc_command cmd = { 0 };
+
+ /* prepare command */
+ cmd.header = mc_encode_cmd_header(DPMCP_CMDID_SET_IRQ_MASK,
+ MC_CMD_PRI_LOW, token);
+ cmd.params[0] |= mc_enc(0, 32, mask);
+ cmd.params[0] |= mc_enc(32, 8, irq_index);
+
+ /* send command to mc*/
+ return mc_send_command(mc_io, &cmd);
+}
+
+int dpmcp_get_irq_mask(struct fsl_mc_io *mc_io,
+ uint16_t token,
+ uint8_t irq_index,
+ uint32_t *mask)
+{
+ struct mc_command cmd = { 0 };
+ int err;
+
+ /* prepare command */
+ cmd.header = mc_encode_cmd_header(DPMCP_CMDID_GET_IRQ_MASK,
+ MC_CMD_PRI_LOW, token);
+ cmd.params[0] |= mc_enc(32, 8, irq_index);
+
+ /* send command to mc*/
+ err = mc_send_command(mc_io, &cmd);
+ if (err)
+ return err;
+
+ /* retrieve response parameters */
+ *mask = (uint32_t)mc_dec(cmd.params[0], 0, 32);
+ return 0;
+}
+
+int dpmcp_get_irq_status(struct fsl_mc_io *mc_io,
+ uint16_t token,
+ uint8_t irq_index,
+ uint32_t *status)
+{
+ struct mc_command cmd = { 0 };
+ int err;
+
+ /* prepare command */
+ cmd.header = mc_encode_cmd_header(DPMCP_CMDID_GET_IRQ_STATUS,
+ MC_CMD_PRI_LOW, token);
+ cmd.params[0] |= mc_enc(32, 8, irq_index);
+
+ /* send command to mc*/
+ err = mc_send_command(mc_io, &cmd);
+ if (err)
+ return err;
+
+ /* retrieve response parameters */
+ *status = (uint32_t)mc_dec(cmd.params[0], 0, 32);
+ return 0;
+}
+
+int dpmcp_clear_irq_status(struct fsl_mc_io *mc_io,
+ uint16_t token,
+ uint8_t irq_index,
+ uint32_t status)
+{
+ struct mc_command cmd = { 0 };
+
+ /* prepare command */
+ cmd.header = mc_encode_cmd_header(DPMCP_CMDID_CLEAR_IRQ_STATUS,
+ MC_CMD_PRI_LOW, token);
+ cmd.params[0] |= mc_enc(0, 32, status);
+ cmd.params[0] |= mc_enc(32, 8, irq_index);
+
+ /* send command to mc*/
+ return mc_send_command(mc_io, &cmd);
+}
+
+int dpmcp_get_attributes(struct fsl_mc_io *mc_io,
+ uint16_t token,
+ struct dpmcp_attr *attr)
+{
+ struct mc_command cmd = { 0 };
+ int err;
+
+ /* prepare command */
+ cmd.header = mc_encode_cmd_header(DPMCP_CMDID_GET_ATTR,
+ MC_CMD_PRI_LOW, token);
+
+ /* send command to mc*/
+ err = mc_send_command(mc_io, &cmd);
+ if (err)
+ return err;
+
+ /* retrieve response parameters */
+ attr->id = (int)mc_dec(cmd.params[0], 32, 32);
+ attr->version.major = (uint16_t)mc_dec(cmd.params[1], 0, 16);
+ attr->version.minor = (uint16_t)mc_dec(cmd.params[1], 16, 16);
+ return 0;
+}
diff --git a/drivers/staging/fsl-mc/bus/dpmcp.h b/drivers/staging/fsl-mc/bus/dpmcp.h
new file mode 100644
index 000000000..5e7c21952
--- /dev/null
+++ b/drivers/staging/fsl-mc/bus/dpmcp.h
@@ -0,0 +1,311 @@
+/* Copyright 2013-2015 Freescale Semiconductor Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of the above-listed copyright holders nor the
+ * names of any contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ *
+ * ALTERNATIVELY, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") as published by the Free Software
+ * Foundation, either version 2 of that License or (at your option) any
+ * later version.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT HOLDERS OR CONTRIBUTORS 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.
+ */
+#ifndef __FSL_DPMCP_H
+#define __FSL_DPMCP_H
+
+/* Data Path Management Command Portal API
+ * Contains initialization APIs and runtime control APIs for DPMCP
+ */
+
+struct fsl_mc_io;
+
+/**
+ * dpmcp_open() - Open a control session for the specified object.
+ * @mc_io: Pointer to MC portal's I/O object
+ * @dpmcp_id: DPMCP unique ID
+ * @token: Returned token; use in subsequent API calls
+ *
+ * This function can be used to open a control session for an
+ * already created object; an object may have been declared in
+ * the DPL or by calling the dpmcp_create function.
+ * This function returns a unique authentication token,
+ * associated with the specific object ID and the specific MC
+ * portal; this token must be used in all subsequent commands for
+ * this specific object
+ *
+ * Return: '0' on Success; Error code otherwise.
+ */
+int dpmcp_open(struct fsl_mc_io *mc_io, int dpmcp_id, uint16_t *token);
+
+/* Get portal ID from pool */
+#define DPMCP_GET_PORTAL_ID_FROM_POOL (-1)
+
+/**
+ * dpmcp_close() - Close the control session of the object
+ * @mc_io: Pointer to MC portal's I/O object
+ * @token: Token of DPMCP object
+ *
+ * After this function is called, no further operations are
+ * allowed on the object without opening a new control session.
+ *
+ * Return: '0' on Success; Error code otherwise.
+ */
+int dpmcp_close(struct fsl_mc_io *mc_io, uint16_t token);
+
+/**
+ * struct dpmcp_cfg() - Structure representing DPMCP configuration
+ * @portal_id: Portal ID; 'DPMCP_GET_PORTAL_ID_FROM_POOL' to get the portal ID
+ * from pool
+ */
+struct dpmcp_cfg {
+ int portal_id;
+};
+
+/**
+ * dpmcp_create() - Create the DPMCP object.
+ * @mc_io: Pointer to MC portal's I/O object
+ * @cfg: Configuration structure
+ * @token: Returned token; use in subsequent API calls
+ *
+ * Create the DPMCP object, allocate required resources and
+ * perform required initialization.
+ *
+ * The object can be created either by declaring it in the
+ * DPL file, or by calling this function.
+ * This function returns a unique authentication token,
+ * associated with the specific object ID and the specific MC
+ * portal; this token must be used in all subsequent calls to
+ * this specific object. For objects that are created using the
+ * DPL file, call dpmcp_open function to get an authentication
+ * token first.
+ *
+ * Return: '0' on Success; Error code otherwise.
+ */
+int dpmcp_create(struct fsl_mc_io *mc_io,
+ const struct dpmcp_cfg *cfg,
+ uint16_t *token);
+
+/**
+ * dpmcp_destroy() - Destroy the DPMCP object and release all its resources.
+ * @mc_io: Pointer to MC portal's I/O object
+ * @token: Token of DPMCP object
+ *
+ * Return: '0' on Success; error code otherwise.
+ */
+int dpmcp_destroy(struct fsl_mc_io *mc_io, uint16_t token);
+
+/**
+ * dpmcp_reset() - Reset the DPMCP, returns the object to initial state.
+ * @mc_io: Pointer to MC portal's I/O object
+ * @token: Token of DPMCP object
+ *
+ * Return: '0' on Success; Error code otherwise.
+ */
+int dpmcp_reset(struct fsl_mc_io *mc_io, uint16_t token);
+
+/* IRQ */
+/*!
+ * @name dpmcp IRQ Index and Events
+ */
+#define DPMCP_IRQ_INDEX 0
+/*!< Irq index */
+#define DPMCP_IRQ_EVENT_CMD_DONE 0x00000001
+/*!< irq event - Indicates that the link state changed */
+/* @} */
+
+/**
+ * dpmcp_set_irq() - Set IRQ information for the DPMCP to trigger an interrupt.
+ * @mc_io: Pointer to MC portal's I/O object
+ * @token: Token of DPMCP object
+ * @irq_index: Identifies the interrupt index to configure
+ * @irq_addr: Address that must be written to
+ * signal a message-based interrupt
+ * @irq_val: Value to write into irq_addr address
+ * @user_irq_id: A user defined number associated with this IRQ
+ *
+ * Return: '0' on Success; Error code otherwise.
+ */
+int dpmcp_set_irq(struct fsl_mc_io *mc_io,
+ uint16_t token,
+ uint8_t irq_index,
+ uint64_t irq_addr,
+ uint32_t irq_val,
+ int user_irq_id);
+
+/**
+ * dpmcp_get_irq() - Get IRQ information from the DPMCP.
+ * @mc_io: Pointer to MC portal's I/O object
+ * @token: Token of DPMCP object
+ * @irq_index: The interrupt index to configure
+ * @type: Interrupt type: 0 represents message interrupt
+ * type (both irq_addr and irq_val are valid)
+ * @irq_addr: Returned address that must be written to
+ * signal the message-based interrupt
+ * @irq_val: Value to write into irq_addr address
+ * @user_irq_id: A user defined number associated with this IRQ
+ *
+ * Return: '0' on Success; Error code otherwise.
+ */
+int dpmcp_get_irq(struct fsl_mc_io *mc_io,
+ uint16_t token,
+ uint8_t irq_index,
+ int *type,
+ uint64_t *irq_addr,
+ uint32_t *irq_val,
+ int *user_irq_id);
+
+/**
+ * dpmcp_set_irq_enable() - Set overall interrupt state.
+ * @mc_io: Pointer to MC portal's I/O object
+ * @token: Token of DPMCP object
+ * @irq_index: The interrupt index to configure
+ * @en: Interrupt state - enable = 1, disable = 0
+ *
+ * Allows GPP software to control when interrupts are generated.
+ * Each interrupt can have up to 32 causes. The enable/disable control's the
+ * overall interrupt state. if the interrupt is disabled no causes will cause
+ * an interrupt.
+ *
+ * Return: '0' on Success; Error code otherwise.
+ */
+int dpmcp_set_irq_enable(struct fsl_mc_io *mc_io,
+ uint16_t token,
+ uint8_t irq_index,
+ uint8_t en);
+
+/**
+ * dpmcp_get_irq_enable() - Get overall interrupt state
+ * @mc_io: Pointer to MC portal's I/O object
+ * @token: Token of DPMCP object
+ * @irq_index: The interrupt index to configure
+ * @en: Returned interrupt state - enable = 1, disable = 0
+ *
+ * Return: '0' on Success; Error code otherwise.
+ */
+int dpmcp_get_irq_enable(struct fsl_mc_io *mc_io,
+ uint16_t token,
+ uint8_t irq_index,
+ uint8_t *en);
+
+/**
+ * dpmcp_set_irq_mask() - Set interrupt mask.
+ * @mc_io: Pointer to MC portal's I/O object
+ * @token: Token of DPMCP object
+ * @irq_index: The interrupt index to configure
+ * @mask: Event mask to trigger interrupt;
+ * each bit:
+ * 0 = ignore event
+ * 1 = consider event for asserting IRQ
+ *
+ * Every interrupt can have up to 32 causes and the interrupt model supports
+ * masking/unmasking each cause independently
+ *
+ * Return: '0' on Success; Error code otherwise.
+ */
+int dpmcp_set_irq_mask(struct fsl_mc_io *mc_io,
+ uint16_t token,
+ uint8_t irq_index,
+ uint32_t mask);
+
+/**
+ * dpmcp_get_irq_mask() - Get interrupt mask.
+ * @mc_io: Pointer to MC portal's I/O object
+ * @token: Token of DPMCP object
+ * @irq_index: The interrupt index to configure
+ * @mask: Returned event mask to trigger interrupt
+ *
+ * Every interrupt can have up to 32 causes and the interrupt model supports
+ * masking/unmasking each cause independently
+ *
+ * Return: '0' on Success; Error code otherwise.
+ */
+int dpmcp_get_irq_mask(struct fsl_mc_io *mc_io,
+ uint16_t token,
+ uint8_t irq_index,
+ uint32_t *mask);
+
+/**
+ * dpmcp_get_irq_status() - Get the current status of any pending interrupts.
+ *
+ * @mc_io: Pointer to MC portal's I/O object
+ * @token: Token of DPMCP object
+ * @irq_index: The interrupt index to configure
+ * @status: Returned interrupts status - one bit per cause:
+ * 0 = no interrupt pending
+ * 1 = interrupt pending
+ *
+ * Return: '0' on Success; Error code otherwise.
+ */
+int dpmcp_get_irq_status(struct fsl_mc_io *mc_io,
+ uint16_t token,
+ uint8_t irq_index,
+ uint32_t *status);
+
+/**
+ * dpmcp_clear_irq_status() - Clear a pending interrupt's status
+ *
+ * @mc_io: Pointer to MC portal's I/O object
+ * @token: Token of DPMCP object
+ * @irq_index: The interrupt index to configure
+ * @status: Bits to clear (W1C) - one bit per cause:
+ * 0 = don't change
+ * 1 = clear status bit
+ *
+ * Return: '0' on Success; Error code otherwise.
+ */
+int dpmcp_clear_irq_status(struct fsl_mc_io *mc_io,
+ uint16_t token,
+ uint8_t irq_index,
+ uint32_t status);
+
+/**
+ * struct dpmcp_attr - Structure representing DPMCP attributes
+ * @id: DPMCP object ID
+ * @version: DPMCP version
+ */
+struct dpmcp_attr {
+ int id;
+ /**
+ * struct version - Structure representing DPMCP version
+ * @major: DPMCP major version
+ * @minor: DPMCP minor version
+ */
+ struct {
+ uint16_t major;
+ uint16_t minor;
+ } version;
+};
+
+/**
+ * dpmcp_get_attributes - Retrieve DPMCP attributes.
+ *
+ * @mc_io: Pointer to MC portal's I/O object
+ * @token: Token of DPMCP object
+ * @attr: Returned object's attributes
+ *
+ * Return: '0' on Success; Error code otherwise.
+ */
+int dpmcp_get_attributes(struct fsl_mc_io *mc_io,
+ uint16_t token,
+ struct dpmcp_attr *attr);
+
+#endif /* __FSL_DPMCP_H */
diff --git a/drivers/staging/fsl-mc/bus/dpmng-cmd.h b/drivers/staging/fsl-mc/bus/dpmng-cmd.h
new file mode 100644
index 000000000..ba8cfa963
--- /dev/null
+++ b/drivers/staging/fsl-mc/bus/dpmng-cmd.h
@@ -0,0 +1,47 @@
+/* Copyright 2013-2014 Freescale Semiconductor Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of the above-listed copyright holders nor the
+ * names of any contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ *
+ * ALTERNATIVELY, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") as published by the Free Software
+ * Foundation, either version 2 of that License or (at your option) any
+ * later version.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT HOLDERS OR CONTRIBUTORS 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.
+ */
+
+/*************************************************************************//*
+ dpmng-cmd.h
+
+ defines portal commands
+
+ *//**************************************************************************/
+
+#ifndef __FSL_DPMNG_CMD_H
+#define __FSL_DPMNG_CMD_H
+
+/* Command IDs */
+#define DPMNG_CMDID_GET_CONT_ID 0x830
+#define DPMNG_CMDID_GET_VERSION 0x831
+
+#endif /* __FSL_DPMNG_CMD_H */
diff --git a/drivers/staging/fsl-mc/bus/dpmng.c b/drivers/staging/fsl-mc/bus/dpmng.c
new file mode 100644
index 000000000..58328e811
--- /dev/null
+++ b/drivers/staging/fsl-mc/bus/dpmng.c
@@ -0,0 +1,78 @@
+/* Copyright 2013-2014 Freescale Semiconductor Inc.
+*
+* Redistribution and use in source and binary forms, with or without
+* modification, are permitted provided that the following conditions are met:
+* * Redistributions of source code must retain the above copyright
+* notice, this list of conditions and the following disclaimer.
+* * Redistributions in binary form must reproduce the above copyright
+* notice, this list of conditions and the following disclaimer in the
+* documentation and/or other materials provided with the distribution.
+* * Neither the name of the above-listed copyright holders nor the
+* names of any contributors may be used to endorse or promote products
+* derived from this software without specific prior written permission.
+*
+*
+* ALTERNATIVELY, this software may be distributed under the terms of the
+* GNU General Public License ("GPL") as published by the Free Software
+* Foundation, either version 2 of that License or (at your option) any
+* later version.
+*
+* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT HOLDERS OR CONTRIBUTORS 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.
+*/
+#include "../include/mc-sys.h"
+#include "../include/mc-cmd.h"
+#include "../include/dpmng.h"
+#include "dpmng-cmd.h"
+
+int mc_get_version(struct fsl_mc_io *mc_io, struct mc_version *mc_ver_info)
+{
+ struct mc_command cmd = { 0 };
+ int err;
+
+ /* prepare command */
+ cmd.header = mc_encode_cmd_header(DPMNG_CMDID_GET_VERSION,
+ MC_CMD_PRI_LOW, 0);
+
+ /* send command to mc*/
+ err = mc_send_command(mc_io, &cmd);
+ if (err)
+ return err;
+
+ /* retrieve response parameters */
+ mc_ver_info->revision = mc_dec(cmd.params[0], 0, 32);
+ mc_ver_info->major = mc_dec(cmd.params[0], 32, 32);
+ mc_ver_info->minor = mc_dec(cmd.params[1], 0, 32);
+
+ return 0;
+}
+
+int dpmng_get_container_id(struct fsl_mc_io *mc_io, int *container_id)
+{
+ struct mc_command cmd = { 0 };
+ int err;
+
+ /* prepare command */
+ cmd.header = mc_encode_cmd_header(DPMNG_CMDID_GET_CONT_ID,
+ MC_CMD_PRI_LOW, 0);
+
+ /* send command to mc*/
+ err = mc_send_command(mc_io, &cmd);
+ if (err)
+ return err;
+
+ /* retrieve response parameters */
+ *container_id = mc_dec(cmd.params[0], 0, 32);
+
+ return 0;
+}
+
diff --git a/drivers/staging/fsl-mc/bus/dprc-cmd.h b/drivers/staging/fsl-mc/bus/dprc-cmd.h
new file mode 100644
index 000000000..09202489c
--- /dev/null
+++ b/drivers/staging/fsl-mc/bus/dprc-cmd.h
@@ -0,0 +1,84 @@
+/* Copyright 2013-2014 Freescale Semiconductor Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of the above-listed copyright holders nor the
+ * names of any contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ *
+ * ALTERNATIVELY, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") as published by the Free Software
+ * Foundation, either version 2 of that License or (at your option) any
+ * later version.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT HOLDERS OR CONTRIBUTORS 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.
+ */
+
+/*************************************************************************//*
+ dprc-cmd.h
+
+ defines dprc portal commands
+
+ *//**************************************************************************/
+
+#ifndef _FSL_DPRC_CMD_H
+#define _FSL_DPRC_CMD_H
+
+/* DPRC Version */
+#define DPRC_VER_MAJOR 3
+#define DPRC_VER_MINOR 0
+
+/* Command IDs */
+#define DPRC_CMDID_CLOSE 0x800
+#define DPRC_CMDID_OPEN 0x805
+#define DPRC_CMDID_CREATE 0x905
+
+#define DPRC_CMDID_GET_ATTR 0x004
+#define DPRC_CMDID_RESET_CONT 0x005
+
+#define DPRC_CMDID_SET_IRQ 0x010
+#define DPRC_CMDID_GET_IRQ 0x011
+#define DPRC_CMDID_SET_IRQ_ENABLE 0x012
+#define DPRC_CMDID_GET_IRQ_ENABLE 0x013
+#define DPRC_CMDID_SET_IRQ_MASK 0x014
+#define DPRC_CMDID_GET_IRQ_MASK 0x015
+#define DPRC_CMDID_GET_IRQ_STATUS 0x016
+#define DPRC_CMDID_CLEAR_IRQ_STATUS 0x017
+
+#define DPRC_CMDID_CREATE_CONT 0x151
+#define DPRC_CMDID_DESTROY_CONT 0x152
+#define DPRC_CMDID_SET_RES_QUOTA 0x155
+#define DPRC_CMDID_GET_RES_QUOTA 0x156
+#define DPRC_CMDID_ASSIGN 0x157
+#define DPRC_CMDID_UNASSIGN 0x158
+#define DPRC_CMDID_GET_OBJ_COUNT 0x159
+#define DPRC_CMDID_GET_OBJ 0x15A
+#define DPRC_CMDID_GET_RES_COUNT 0x15B
+#define DPRC_CMDID_GET_RES_IDS 0x15C
+#define DPRC_CMDID_GET_OBJ_REG 0x15E
+
+#define DPRC_CMDID_CONNECT 0x167
+#define DPRC_CMDID_DISCONNECT 0x168
+#define DPRC_CMDID_GET_POOL 0x169
+#define DPRC_CMDID_GET_POOL_COUNT 0x16A
+#define DPRC_CMDID_GET_PORTAL_PADDR 0x16B
+
+#define DPRC_CMDID_GET_CONNECTION 0x16C
+
+#endif /* _FSL_DPRC_CMD_H */
diff --git a/drivers/staging/fsl-mc/bus/dprc-driver.c b/drivers/staging/fsl-mc/bus/dprc-driver.c
new file mode 100644
index 000000000..35c06cff1
--- /dev/null
+++ b/drivers/staging/fsl-mc/bus/dprc-driver.c
@@ -0,0 +1,486 @@
+/*
+ * Freescale data path resource container (DPRC) driver
+ *
+ * Copyright (C) 2014 Freescale Semiconductor, Inc.
+ * Author: German Rivera <German.Rivera@freescale.com>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#include "../include/mc-private.h"
+#include "../include/mc-sys.h"
+#include <linux/module.h>
+#include <linux/slab.h>
+#include "dprc-cmd.h"
+
+struct dprc_child_objs {
+ int child_count;
+ struct dprc_obj_desc *child_array;
+};
+
+static int __fsl_mc_device_remove_if_not_in_mc(struct device *dev, void *data)
+{
+ int i;
+ struct dprc_child_objs *objs;
+ struct fsl_mc_device *mc_dev;
+
+ WARN_ON(!dev);
+ WARN_ON(!data);
+ mc_dev = to_fsl_mc_device(dev);
+ objs = data;
+
+ for (i = 0; i < objs->child_count; i++) {
+ struct dprc_obj_desc *obj_desc = &objs->child_array[i];
+
+ if (strlen(obj_desc->type) != 0 &&
+ FSL_MC_DEVICE_MATCH(mc_dev, obj_desc))
+ break;
+ }
+
+ if (i == objs->child_count)
+ fsl_mc_device_remove(mc_dev);
+
+ return 0;
+}
+
+static int __fsl_mc_device_remove(struct device *dev, void *data)
+{
+ WARN_ON(!dev);
+ WARN_ON(data);
+ fsl_mc_device_remove(to_fsl_mc_device(dev));
+ return 0;
+}
+
+/**
+ * dprc_remove_devices - Removes devices for objects removed from a DPRC
+ *
+ * @mc_bus_dev: pointer to the fsl-mc device that represents a DPRC object
+ * @obj_desc_array: array of object descriptors for child objects currently
+ * present in the DPRC in the MC.
+ * @num_child_objects_in_mc: number of entries in obj_desc_array
+ *
+ * Synchronizes the state of the Linux bus driver with the actual state of
+ * the MC by removing devices that represent MC objects that have
+ * been dynamically removed in the physical DPRC.
+ */
+static void dprc_remove_devices(struct fsl_mc_device *mc_bus_dev,
+ struct dprc_obj_desc *obj_desc_array,
+ int num_child_objects_in_mc)
+{
+ if (num_child_objects_in_mc != 0) {
+ /*
+ * Remove child objects that are in the DPRC in Linux,
+ * but not in the MC:
+ */
+ struct dprc_child_objs objs;
+
+ objs.child_count = num_child_objects_in_mc;
+ objs.child_array = obj_desc_array;
+ device_for_each_child(&mc_bus_dev->dev, &objs,
+ __fsl_mc_device_remove_if_not_in_mc);
+ } else {
+ /*
+ * There are no child objects for this DPRC in the MC.
+ * So, remove all the child devices from Linux:
+ */
+ device_for_each_child(&mc_bus_dev->dev, NULL,
+ __fsl_mc_device_remove);
+ }
+}
+
+static int __fsl_mc_device_match(struct device *dev, void *data)
+{
+ struct dprc_obj_desc *obj_desc = data;
+ struct fsl_mc_device *mc_dev = to_fsl_mc_device(dev);
+
+ return FSL_MC_DEVICE_MATCH(mc_dev, obj_desc);
+}
+
+static struct fsl_mc_device *fsl_mc_device_lookup(struct dprc_obj_desc
+ *obj_desc,
+ struct fsl_mc_device
+ *mc_bus_dev)
+{
+ struct device *dev;
+
+ dev = device_find_child(&mc_bus_dev->dev, obj_desc,
+ __fsl_mc_device_match);
+
+ return dev ? to_fsl_mc_device(dev) : NULL;
+}
+
+/**
+ * check_plugged_state_change - Check change in an MC object's plugged state
+ *
+ * @mc_dev: pointer to the fsl-mc device for a given MC object
+ * @obj_desc: pointer to the MC object's descriptor in the MC
+ *
+ * If the plugged state has changed from unplugged to plugged, the fsl-mc
+ * device is bound to the corresponding device driver.
+ * If the plugged state has changed from plugged to unplugged, the fsl-mc
+ * device is unbound from the corresponding device driver.
+ */
+static void check_plugged_state_change(struct fsl_mc_device *mc_dev,
+ struct dprc_obj_desc *obj_desc)
+{
+ int error;
+ uint32_t plugged_flag_at_mc =
+ (obj_desc->state & DPRC_OBJ_STATE_PLUGGED);
+
+ if (plugged_flag_at_mc !=
+ (mc_dev->obj_desc.state & DPRC_OBJ_STATE_PLUGGED)) {
+ if (plugged_flag_at_mc) {
+ mc_dev->obj_desc.state |= DPRC_OBJ_STATE_PLUGGED;
+ error = device_attach(&mc_dev->dev);
+ if (error < 0) {
+ dev_err(&mc_dev->dev,
+ "device_attach() failed: %d\n",
+ error);
+ }
+ } else {
+ mc_dev->obj_desc.state &= ~DPRC_OBJ_STATE_PLUGGED;
+ device_release_driver(&mc_dev->dev);
+ }
+ }
+}
+
+/**
+ * dprc_add_new_devices - Adds devices to the logical bus for a DPRC
+ *
+ * @mc_bus_dev: pointer to the fsl-mc device that represents a DPRC object
+ * @obj_desc_array: array of device descriptors for child devices currently
+ * present in the physical DPRC.
+ * @num_child_objects_in_mc: number of entries in obj_desc_array
+ *
+ * Synchronizes the state of the Linux bus driver with the actual
+ * state of the MC by adding objects that have been newly discovered
+ * in the physical DPRC.
+ */
+static void dprc_add_new_devices(struct fsl_mc_device *mc_bus_dev,
+ struct dprc_obj_desc *obj_desc_array,
+ int num_child_objects_in_mc)
+{
+ int error;
+ int i;
+
+ for (i = 0; i < num_child_objects_in_mc; i++) {
+ struct fsl_mc_device *child_dev;
+ struct dprc_obj_desc *obj_desc = &obj_desc_array[i];
+
+ if (strlen(obj_desc->type) == 0)
+ continue;
+
+ /*
+ * Check if device is already known to Linux:
+ */
+ child_dev = fsl_mc_device_lookup(obj_desc, mc_bus_dev);
+ if (child_dev) {
+ check_plugged_state_change(child_dev, obj_desc);
+ continue;
+ }
+
+ error = fsl_mc_device_add(obj_desc, NULL, &mc_bus_dev->dev,
+ &child_dev);
+ if (error < 0)
+ continue;
+ }
+}
+
+static void dprc_init_all_resource_pools(struct fsl_mc_device *mc_bus_dev)
+{
+ int pool_type;
+ struct fsl_mc_bus *mc_bus = to_fsl_mc_bus(mc_bus_dev);
+
+ for (pool_type = 0; pool_type < FSL_MC_NUM_POOL_TYPES; pool_type++) {
+ struct fsl_mc_resource_pool *res_pool =
+ &mc_bus->resource_pools[pool_type];
+
+ res_pool->type = pool_type;
+ res_pool->max_count = 0;
+ res_pool->free_count = 0;
+ res_pool->mc_bus = mc_bus;
+ INIT_LIST_HEAD(&res_pool->free_list);
+ mutex_init(&res_pool->mutex);
+ }
+}
+
+static void dprc_cleanup_resource_pool(struct fsl_mc_device *mc_bus_dev,
+ enum fsl_mc_pool_type pool_type)
+{
+ struct fsl_mc_resource *resource;
+ struct fsl_mc_resource *next;
+ struct fsl_mc_bus *mc_bus = to_fsl_mc_bus(mc_bus_dev);
+ struct fsl_mc_resource_pool *res_pool =
+ &mc_bus->resource_pools[pool_type];
+ int free_count = 0;
+
+ WARN_ON(res_pool->type != pool_type);
+ WARN_ON(res_pool->free_count != res_pool->max_count);
+
+ list_for_each_entry_safe(resource, next, &res_pool->free_list, node) {
+ free_count++;
+ WARN_ON(resource->type != res_pool->type);
+ WARN_ON(resource->parent_pool != res_pool);
+ devm_kfree(&mc_bus_dev->dev, resource);
+ }
+
+ WARN_ON(free_count != res_pool->free_count);
+}
+
+static void dprc_cleanup_all_resource_pools(struct fsl_mc_device *mc_bus_dev)
+{
+ int pool_type;
+
+ for (pool_type = 0; pool_type < FSL_MC_NUM_POOL_TYPES; pool_type++)
+ dprc_cleanup_resource_pool(mc_bus_dev, pool_type);
+}
+
+/**
+ * dprc_scan_objects - Discover objects in a DPRC
+ *
+ * @mc_bus_dev: pointer to the fsl-mc device that represents a DPRC object
+ *
+ * Detects objects added and removed from a DPRC and synchronizes the
+ * state of the Linux bus driver, MC by adding and removing
+ * devices accordingly.
+ * Two types of devices can be found in a DPRC: allocatable objects (e.g.,
+ * dpbp, dpmcp) and non-allocatable devices (e.g., dprc, dpni).
+ * All allocatable devices needed to be probed before all non-allocatable
+ * devices, to ensure that device drivers for non-allocatable
+ * devices can allocate any type of allocatable devices.
+ * That is, we need to ensure that the corresponding resource pools are
+ * populated before they can get allocation requests from probe callbacks
+ * of the device drivers for the non-allocatable devices.
+ */
+int dprc_scan_objects(struct fsl_mc_device *mc_bus_dev)
+{
+ int num_child_objects;
+ int dprc_get_obj_failures;
+ int error;
+ struct dprc_obj_desc *child_obj_desc_array = NULL;
+
+ error = dprc_get_obj_count(mc_bus_dev->mc_io,
+ mc_bus_dev->mc_handle,
+ &num_child_objects);
+ if (error < 0) {
+ dev_err(&mc_bus_dev->dev, "dprc_get_obj_count() failed: %d\n",
+ error);
+ return error;
+ }
+
+ if (num_child_objects != 0) {
+ int i;
+
+ child_obj_desc_array =
+ devm_kmalloc_array(&mc_bus_dev->dev, num_child_objects,
+ sizeof(*child_obj_desc_array),
+ GFP_KERNEL);
+ if (!child_obj_desc_array)
+ return -ENOMEM;
+
+ /*
+ * Discover objects currently present in the physical DPRC:
+ */
+ dprc_get_obj_failures = 0;
+ for (i = 0; i < num_child_objects; i++) {
+ struct dprc_obj_desc *obj_desc =
+ &child_obj_desc_array[i];
+
+ error = dprc_get_obj(mc_bus_dev->mc_io,
+ mc_bus_dev->mc_handle,
+ i, obj_desc);
+ if (error < 0) {
+ dev_err(&mc_bus_dev->dev,
+ "dprc_get_obj(i=%d) failed: %d\n",
+ i, error);
+ /*
+ * Mark the obj entry as "invalid", by using the
+ * empty string as obj type:
+ */
+ obj_desc->type[0] = '\0';
+ obj_desc->id = error;
+ dprc_get_obj_failures++;
+ continue;
+ }
+
+ dev_dbg(&mc_bus_dev->dev,
+ "Discovered object: type %s, id %d\n",
+ obj_desc->type, obj_desc->id);
+ }
+
+ if (dprc_get_obj_failures != 0) {
+ dev_err(&mc_bus_dev->dev,
+ "%d out of %d devices could not be retrieved\n",
+ dprc_get_obj_failures, num_child_objects);
+ }
+ }
+
+ dprc_remove_devices(mc_bus_dev, child_obj_desc_array,
+ num_child_objects);
+
+ dprc_add_new_devices(mc_bus_dev, child_obj_desc_array,
+ num_child_objects);
+
+ if (child_obj_desc_array)
+ devm_kfree(&mc_bus_dev->dev, child_obj_desc_array);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(dprc_scan_objects);
+
+/**
+ * dprc_scan_container - Scans a physical DPRC and synchronizes Linux bus state
+ *
+ * @mc_bus_dev: pointer to the fsl-mc device that represents a DPRC object
+ *
+ * Scans the physical DPRC and synchronizes the state of the Linux
+ * bus driver with the actual state of the MC by adding and removing
+ * devices as appropriate.
+ */
+int dprc_scan_container(struct fsl_mc_device *mc_bus_dev)
+{
+ int error;
+ struct fsl_mc_bus *mc_bus = to_fsl_mc_bus(mc_bus_dev);
+
+ dprc_init_all_resource_pools(mc_bus_dev);
+
+ /*
+ * Discover objects in the DPRC:
+ */
+ mutex_lock(&mc_bus->scan_mutex);
+ error = dprc_scan_objects(mc_bus_dev);
+ mutex_unlock(&mc_bus->scan_mutex);
+ if (error < 0)
+ goto error;
+
+ return 0;
+error:
+ dprc_cleanup_all_resource_pools(mc_bus_dev);
+ return error;
+}
+EXPORT_SYMBOL_GPL(dprc_scan_container);
+
+/**
+ * dprc_probe - callback invoked when a DPRC is being bound to this driver
+ *
+ * @mc_dev: Pointer to fsl-mc device representing a DPRC
+ *
+ * It opens the physical DPRC in the MC.
+ * It scans the DPRC to discover the MC objects contained in it.
+ * It creates the interrupt pool for the MC bus associated with the DPRC.
+ * It configures the interrupts for the DPRC device itself.
+ */
+static int dprc_probe(struct fsl_mc_device *mc_dev)
+{
+ int error;
+ size_t region_size;
+ struct fsl_mc_bus *mc_bus = to_fsl_mc_bus(mc_dev);
+
+ if (WARN_ON(strcmp(mc_dev->obj_desc.type, "dprc") != 0))
+ return -EINVAL;
+
+ if (!mc_dev->mc_io) {
+ /*
+ * This is a child DPRC:
+ */
+ if (WARN_ON(mc_dev->obj_desc.region_count == 0))
+ return -EINVAL;
+
+ region_size = mc_dev->regions[0].end -
+ mc_dev->regions[0].start + 1;
+
+ error = fsl_create_mc_io(&mc_dev->dev,
+ mc_dev->regions[0].start,
+ region_size,
+ NULL, 0, &mc_dev->mc_io);
+ if (error < 0)
+ return error;
+ }
+
+ error = dprc_open(mc_dev->mc_io, mc_dev->obj_desc.id,
+ &mc_dev->mc_handle);
+ if (error < 0) {
+ dev_err(&mc_dev->dev, "dprc_open() failed: %d\n", error);
+ goto error_cleanup_mc_io;
+ }
+
+ mutex_init(&mc_bus->scan_mutex);
+
+ /*
+ * Discover MC objects in DPRC object:
+ */
+ error = dprc_scan_container(mc_dev);
+ if (error < 0)
+ goto error_cleanup_open;
+
+ dev_info(&mc_dev->dev, "DPRC device bound to driver");
+ return 0;
+
+error_cleanup_open:
+ (void)dprc_close(mc_dev->mc_io, mc_dev->mc_handle);
+
+error_cleanup_mc_io:
+ fsl_destroy_mc_io(mc_dev->mc_io);
+ return error;
+}
+
+/**
+ * dprc_remove - callback invoked when a DPRC is being unbound from this driver
+ *
+ * @mc_dev: Pointer to fsl-mc device representing the DPRC
+ *
+ * It removes the DPRC's child objects from Linux (not from the MC) and
+ * closes the DPRC device in the MC.
+ * It tears down the interrupts that were configured for the DPRC device.
+ * It destroys the interrupt pool associated with this MC bus.
+ */
+static int dprc_remove(struct fsl_mc_device *mc_dev)
+{
+ int error;
+
+ if (WARN_ON(strcmp(mc_dev->obj_desc.type, "dprc") != 0))
+ return -EINVAL;
+ if (WARN_ON(!mc_dev->mc_io))
+ return -EINVAL;
+
+ device_for_each_child(&mc_dev->dev, NULL, __fsl_mc_device_remove);
+ dprc_cleanup_all_resource_pools(mc_dev);
+ error = dprc_close(mc_dev->mc_io, mc_dev->mc_handle);
+ if (error < 0)
+ dev_err(&mc_dev->dev, "dprc_close() failed: %d\n", error);
+
+ dev_info(&mc_dev->dev, "DPRC device unbound from driver");
+ return 0;
+}
+
+static const struct fsl_mc_device_match_id match_id_table[] = {
+ {
+ .vendor = FSL_MC_VENDOR_FREESCALE,
+ .obj_type = "dprc",
+ .ver_major = DPRC_VER_MAJOR,
+ .ver_minor = DPRC_VER_MINOR},
+ {.vendor = 0x0},
+};
+
+static struct fsl_mc_driver dprc_driver = {
+ .driver = {
+ .name = FSL_MC_DPRC_DRIVER_NAME,
+ .owner = THIS_MODULE,
+ .pm = NULL,
+ },
+ .match_id_table = match_id_table,
+ .probe = dprc_probe,
+ .remove = dprc_remove,
+};
+
+int __init dprc_driver_init(void)
+{
+ return fsl_mc_driver_register(&dprc_driver);
+}
+
+void __exit dprc_driver_exit(void)
+{
+ fsl_mc_driver_unregister(&dprc_driver);
+}
diff --git a/drivers/staging/fsl-mc/bus/dprc.c b/drivers/staging/fsl-mc/bus/dprc.c
new file mode 100644
index 000000000..19b26e630
--- /dev/null
+++ b/drivers/staging/fsl-mc/bus/dprc.c
@@ -0,0 +1,913 @@
+/* Copyright 2013-2014 Freescale Semiconductor Inc.
+*
+* Redistribution and use in source and binary forms, with or without
+* modification, are permitted provided that the following conditions are met:
+* * Redistributions of source code must retain the above copyright
+* notice, this list of conditions and the following disclaimer.
+* * Redistributions in binary form must reproduce the above copyright
+* notice, this list of conditions and the following disclaimer in the
+* documentation and/or other materials provided with the distribution.
+* * Neither the name of the above-listed copyright holders nor the
+* names of any contributors may be used to endorse or promote products
+* derived from this software without specific prior written permission.
+*
+*
+* ALTERNATIVELY, this software may be distributed under the terms of the
+* GNU General Public License ("GPL") as published by the Free Software
+* Foundation, either version 2 of that License or (at your option) any
+* later version.
+*
+* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT HOLDERS OR CONTRIBUTORS 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.
+*/
+#include "../include/mc-sys.h"
+#include "../include/mc-cmd.h"
+#include "../include/dprc.h"
+#include "dprc-cmd.h"
+
+int dprc_open(struct fsl_mc_io *mc_io, int container_id, uint16_t *token)
+{
+ struct mc_command cmd = { 0 };
+ int err;
+
+ /* prepare command */
+ cmd.header = mc_encode_cmd_header(DPRC_CMDID_OPEN, MC_CMD_PRI_LOW,
+ 0);
+ cmd.params[0] |= mc_enc(0, 32, container_id);
+
+ /* send command to mc*/
+ err = mc_send_command(mc_io, &cmd);
+ if (err)
+ return err;
+
+ /* retrieve response parameters */
+ *token = MC_CMD_HDR_READ_TOKEN(cmd.header);
+
+ return 0;
+}
+EXPORT_SYMBOL(dprc_open);
+
+int dprc_close(struct fsl_mc_io *mc_io, uint16_t token)
+{
+ struct mc_command cmd = { 0 };
+
+ /* prepare command */
+ cmd.header = mc_encode_cmd_header(DPRC_CMDID_CLOSE, MC_CMD_PRI_HIGH,
+ token);
+
+ /* send command to mc*/
+ return mc_send_command(mc_io, &cmd);
+}
+EXPORT_SYMBOL(dprc_close);
+
+int dprc_create_container(struct fsl_mc_io *mc_io,
+ uint16_t token,
+ struct dprc_cfg *cfg,
+ int *child_container_id,
+ uint64_t *child_portal_paddr)
+{
+ struct mc_command cmd = { 0 };
+ int err;
+
+ /* prepare command */
+ cmd.params[0] |= mc_enc(32, 16, cfg->icid);
+ cmd.params[0] |= mc_enc(0, 32, cfg->options);
+ cmd.params[1] |= mc_enc(32, 32, cfg->portal_id);
+
+ cmd.header = mc_encode_cmd_header(DPRC_CMDID_CREATE_CONT,
+ MC_CMD_PRI_LOW, token);
+
+ /* send command to mc*/
+ err = mc_send_command(mc_io, &cmd);
+ if (err)
+ return err;
+
+ /* retrieve response parameters */
+ *child_container_id = mc_dec(cmd.params[1], 0, 32);
+ *child_portal_paddr = mc_dec(cmd.params[2], 0, 64);
+
+ return 0;
+}
+
+int dprc_destroy_container(struct fsl_mc_io *mc_io,
+ uint16_t token,
+ int child_container_id)
+{
+ struct mc_command cmd = { 0 };
+
+ /* prepare command */
+ cmd.header = mc_encode_cmd_header(DPRC_CMDID_DESTROY_CONT,
+ MC_CMD_PRI_LOW, token);
+ cmd.params[0] |= mc_enc(0, 32, child_container_id);
+
+ /* send command to mc*/
+ return mc_send_command(mc_io, &cmd);
+}
+
+int dprc_reset_container(struct fsl_mc_io *mc_io,
+ uint16_t token,
+ int child_container_id)
+{
+ struct mc_command cmd = { 0 };
+
+ /* prepare command */
+ cmd.header = mc_encode_cmd_header(DPRC_CMDID_RESET_CONT,
+ MC_CMD_PRI_LOW, token);
+ cmd.params[0] |= mc_enc(0, 32, child_container_id);
+
+ /* send command to mc*/
+ return mc_send_command(mc_io, &cmd);
+}
+
+int dprc_get_irq(struct fsl_mc_io *mc_io,
+ uint16_t token,
+ uint8_t irq_index,
+ int *type,
+ uint64_t *irq_paddr,
+ uint32_t *irq_val,
+ int *user_irq_id)
+{
+ struct mc_command cmd = { 0 };
+ int err;
+
+ /* prepare command */
+ cmd.header = mc_encode_cmd_header(DPRC_CMDID_GET_IRQ,
+ MC_CMD_PRI_LOW,
+ token);
+ cmd.params[0] |= mc_enc(32, 8, irq_index);
+
+ /* send command to mc*/
+ err = mc_send_command(mc_io, &cmd);
+ if (err)
+ return err;
+
+ /* retrieve response parameters */
+ *irq_val = mc_dec(cmd.params[0], 0, 32);
+ *irq_paddr = mc_dec(cmd.params[1], 0, 64);
+ *user_irq_id = mc_dec(cmd.params[2], 0, 32);
+ *type = mc_dec(cmd.params[2], 32, 32);
+
+ return 0;
+}
+
+int dprc_set_irq(struct fsl_mc_io *mc_io,
+ uint16_t token,
+ uint8_t irq_index,
+ uint64_t irq_paddr,
+ uint32_t irq_val,
+ int user_irq_id)
+{
+ struct mc_command cmd = { 0 };
+
+ /* prepare command */
+ cmd.header = mc_encode_cmd_header(DPRC_CMDID_SET_IRQ,
+ MC_CMD_PRI_LOW,
+ token);
+ cmd.params[0] |= mc_enc(32, 8, irq_index);
+ cmd.params[0] |= mc_enc(0, 32, irq_val);
+ cmd.params[1] |= mc_enc(0, 64, irq_paddr);
+ cmd.params[2] |= mc_enc(0, 32, user_irq_id);
+
+ /* send command to mc*/
+ return mc_send_command(mc_io, &cmd);
+}
+
+int dprc_get_irq_enable(struct fsl_mc_io *mc_io,
+ uint16_t token,
+ uint8_t irq_index,
+ uint8_t *en)
+{
+ struct mc_command cmd = { 0 };
+ int err;
+
+ /* prepare command */
+ cmd.header = mc_encode_cmd_header(DPRC_CMDID_GET_IRQ_ENABLE,
+ MC_CMD_PRI_LOW, token);
+ cmd.params[0] |= mc_enc(32, 8, irq_index);
+
+ /* send command to mc*/
+ err = mc_send_command(mc_io, &cmd);
+ if (err)
+ return err;
+
+ /* retrieve response parameters */
+ *en = mc_dec(cmd.params[0], 0, 8);
+
+ return 0;
+}
+
+int dprc_set_irq_enable(struct fsl_mc_io *mc_io,
+ uint16_t token,
+ uint8_t irq_index,
+ uint8_t en)
+{
+ struct mc_command cmd = { 0 };
+
+ /* prepare command */
+ cmd.header = mc_encode_cmd_header(DPRC_CMDID_SET_IRQ_ENABLE,
+ MC_CMD_PRI_LOW, token);
+ cmd.params[0] |= mc_enc(0, 8, en);
+ cmd.params[0] |= mc_enc(32, 8, irq_index);
+
+ /* send command to mc*/
+ return mc_send_command(mc_io, &cmd);
+}
+
+int dprc_get_irq_mask(struct fsl_mc_io *mc_io,
+ uint16_t token,
+ uint8_t irq_index,
+ uint32_t *mask)
+{
+ struct mc_command cmd = { 0 };
+ int err;
+
+ /* prepare command */
+ cmd.header = mc_encode_cmd_header(DPRC_CMDID_GET_IRQ_MASK,
+ MC_CMD_PRI_LOW, token);
+ cmd.params[0] |= mc_enc(32, 8, irq_index);
+
+ /* send command to mc*/
+ err = mc_send_command(mc_io, &cmd);
+ if (err)
+ return err;
+
+ /* retrieve response parameters */
+ *mask = mc_dec(cmd.params[0], 0, 32);
+
+ return 0;
+}
+
+int dprc_set_irq_mask(struct fsl_mc_io *mc_io,
+ uint16_t token,
+ uint8_t irq_index,
+ uint32_t mask)
+{
+ struct mc_command cmd = { 0 };
+
+ /* prepare command */
+ cmd.header = mc_encode_cmd_header(DPRC_CMDID_SET_IRQ_MASK,
+ MC_CMD_PRI_LOW, token);
+ cmd.params[0] |= mc_enc(0, 32, mask);
+ cmd.params[0] |= mc_enc(32, 8, irq_index);
+
+ /* send command to mc*/
+ return mc_send_command(mc_io, &cmd);
+}
+
+int dprc_get_irq_status(struct fsl_mc_io *mc_io,
+ uint16_t token,
+ uint8_t irq_index,
+ uint32_t *status)
+{
+ struct mc_command cmd = { 0 };
+ int err;
+
+ /* prepare command */
+ cmd.header = mc_encode_cmd_header(DPRC_CMDID_GET_IRQ_STATUS,
+ MC_CMD_PRI_LOW, token);
+ cmd.params[0] |= mc_enc(32, 8, irq_index);
+
+ /* send command to mc*/
+ err = mc_send_command(mc_io, &cmd);
+ if (err)
+ return err;
+
+ /* retrieve response parameters */
+ *status = mc_dec(cmd.params[0], 0, 32);
+
+ return 0;
+}
+
+int dprc_clear_irq_status(struct fsl_mc_io *mc_io,
+ uint16_t token,
+ uint8_t irq_index,
+ uint32_t status)
+{
+ struct mc_command cmd = { 0 };
+
+ /* prepare command */
+ cmd.header = mc_encode_cmd_header(DPRC_CMDID_CLEAR_IRQ_STATUS,
+ MC_CMD_PRI_LOW, token);
+ cmd.params[0] |= mc_enc(0, 32, status);
+ cmd.params[0] |= mc_enc(32, 8, irq_index);
+
+ /* send command to mc*/
+ return mc_send_command(mc_io, &cmd);
+}
+
+int dprc_get_attributes(struct fsl_mc_io *mc_io,
+ uint16_t token,
+ struct dprc_attributes *attr)
+{
+ struct mc_command cmd = { 0 };
+ int err;
+
+ /* prepare command */
+ cmd.header = mc_encode_cmd_header(DPRC_CMDID_GET_ATTR,
+ MC_CMD_PRI_LOW,
+ token);
+
+ /* send command to mc*/
+ err = mc_send_command(mc_io, &cmd);
+ if (err)
+ return err;
+
+ /* retrieve response parameters */
+ attr->container_id = mc_dec(cmd.params[0], 0, 32);
+ attr->icid = mc_dec(cmd.params[0], 32, 16);
+ attr->options = mc_dec(cmd.params[1], 0, 32);
+ attr->portal_id = mc_dec(cmd.params[1], 32, 32);
+ attr->version.major = mc_dec(cmd.params[2], 0, 16);
+ attr->version.minor = mc_dec(cmd.params[2], 16, 16);
+
+ return 0;
+}
+
+int dprc_set_res_quota(struct fsl_mc_io *mc_io,
+ uint16_t token,
+ int child_container_id,
+ char *type,
+ uint16_t quota)
+{
+ struct mc_command cmd = { 0 };
+
+ /* prepare command */
+ cmd.header = mc_encode_cmd_header(DPRC_CMDID_SET_RES_QUOTA,
+ MC_CMD_PRI_LOW, token);
+ cmd.params[0] |= mc_enc(0, 32, child_container_id);
+ cmd.params[0] |= mc_enc(32, 16, quota);
+ cmd.params[1] |= mc_enc(0, 8, type[0]);
+ cmd.params[1] |= mc_enc(8, 8, type[1]);
+ cmd.params[1] |= mc_enc(16, 8, type[2]);
+ cmd.params[1] |= mc_enc(24, 8, type[3]);
+ cmd.params[1] |= mc_enc(32, 8, type[4]);
+ cmd.params[1] |= mc_enc(40, 8, type[5]);
+ cmd.params[1] |= mc_enc(48, 8, type[6]);
+ cmd.params[1] |= mc_enc(56, 8, type[7]);
+ cmd.params[2] |= mc_enc(0, 8, type[8]);
+ cmd.params[2] |= mc_enc(8, 8, type[9]);
+ cmd.params[2] |= mc_enc(16, 8, type[10]);
+ cmd.params[2] |= mc_enc(24, 8, type[11]);
+ cmd.params[2] |= mc_enc(32, 8, type[12]);
+ cmd.params[2] |= mc_enc(40, 8, type[13]);
+ cmd.params[2] |= mc_enc(48, 8, type[14]);
+ cmd.params[2] |= mc_enc(56, 8, '\0');
+
+ /* send command to mc*/
+ return mc_send_command(mc_io, &cmd);
+}
+
+int dprc_get_res_quota(struct fsl_mc_io *mc_io,
+ uint16_t token,
+ int child_container_id,
+ char *type,
+ uint16_t *quota)
+{
+ struct mc_command cmd = { 0 };
+ int err;
+
+ /* prepare command */
+ cmd.header = mc_encode_cmd_header(DPRC_CMDID_GET_RES_QUOTA,
+ MC_CMD_PRI_LOW, token);
+ cmd.params[0] |= mc_enc(0, 32, child_container_id);
+ cmd.params[1] |= mc_enc(0, 8, type[0]);
+ cmd.params[1] |= mc_enc(8, 8, type[1]);
+ cmd.params[1] |= mc_enc(16, 8, type[2]);
+ cmd.params[1] |= mc_enc(24, 8, type[3]);
+ cmd.params[1] |= mc_enc(32, 8, type[4]);
+ cmd.params[1] |= mc_enc(40, 8, type[5]);
+ cmd.params[1] |= mc_enc(48, 8, type[6]);
+ cmd.params[1] |= mc_enc(56, 8, type[7]);
+ cmd.params[2] |= mc_enc(0, 8, type[8]);
+ cmd.params[2] |= mc_enc(8, 8, type[9]);
+ cmd.params[2] |= mc_enc(16, 8, type[10]);
+ cmd.params[2] |= mc_enc(24, 8, type[11]);
+ cmd.params[2] |= mc_enc(32, 8, type[12]);
+ cmd.params[2] |= mc_enc(40, 8, type[13]);
+ cmd.params[2] |= mc_enc(48, 8, type[14]);
+ cmd.params[2] |= mc_enc(56, 8, '\0');
+
+ /* send command to mc*/
+ err = mc_send_command(mc_io, &cmd);
+ if (err)
+ return err;
+
+ /* retrieve response parameters */
+ *quota = mc_dec(cmd.params[0], 32, 16);
+
+ return 0;
+}
+
+int dprc_assign(struct fsl_mc_io *mc_io,
+ uint16_t token,
+ int container_id,
+ struct dprc_res_req *res_req)
+{
+ struct mc_command cmd = { 0 };
+
+ /* prepare command */
+ cmd.header = mc_encode_cmd_header(DPRC_CMDID_ASSIGN,
+ MC_CMD_PRI_LOW, token);
+ cmd.params[0] |= mc_enc(0, 32, container_id);
+ cmd.params[0] |= mc_enc(32, 32, res_req->options);
+ cmd.params[1] |= mc_enc(0, 32, res_req->num);
+ cmd.params[1] |= mc_enc(32, 32, res_req->id_base_align);
+ cmd.params[2] |= mc_enc(0, 8, res_req->type[0]);
+ cmd.params[2] |= mc_enc(8, 8, res_req->type[1]);
+ cmd.params[2] |= mc_enc(16, 8, res_req->type[2]);
+ cmd.params[2] |= mc_enc(24, 8, res_req->type[3]);
+ cmd.params[2] |= mc_enc(32, 8, res_req->type[4]);
+ cmd.params[2] |= mc_enc(40, 8, res_req->type[5]);
+ cmd.params[2] |= mc_enc(48, 8, res_req->type[6]);
+ cmd.params[2] |= mc_enc(56, 8, res_req->type[7]);
+ cmd.params[3] |= mc_enc(0, 8, res_req->type[8]);
+ cmd.params[3] |= mc_enc(8, 8, res_req->type[9]);
+ cmd.params[3] |= mc_enc(16, 8, res_req->type[10]);
+ cmd.params[3] |= mc_enc(24, 8, res_req->type[11]);
+ cmd.params[3] |= mc_enc(32, 8, res_req->type[12]);
+ cmd.params[3] |= mc_enc(40, 8, res_req->type[13]);
+ cmd.params[3] |= mc_enc(48, 8, res_req->type[14]);
+ cmd.params[3] |= mc_enc(56, 8, res_req->type[15]);
+
+ /* send command to mc*/
+ return mc_send_command(mc_io, &cmd);
+}
+
+int dprc_unassign(struct fsl_mc_io *mc_io,
+ uint16_t token,
+ int child_container_id,
+ struct dprc_res_req *res_req)
+{
+ struct mc_command cmd = { 0 };
+
+ /* prepare command */
+ cmd.header = mc_encode_cmd_header(DPRC_CMDID_UNASSIGN,
+ MC_CMD_PRI_LOW,
+ token);
+ cmd.params[0] |= mc_enc(0, 32, child_container_id);
+ cmd.params[0] |= mc_enc(32, 32, res_req->options);
+ cmd.params[1] |= mc_enc(0, 32, res_req->num);
+ cmd.params[1] |= mc_enc(32, 32, res_req->id_base_align);
+ cmd.params[2] |= mc_enc(0, 8, res_req->type[0]);
+ cmd.params[2] |= mc_enc(8, 8, res_req->type[1]);
+ cmd.params[2] |= mc_enc(16, 8, res_req->type[2]);
+ cmd.params[2] |= mc_enc(24, 8, res_req->type[3]);
+ cmd.params[2] |= mc_enc(32, 8, res_req->type[4]);
+ cmd.params[2] |= mc_enc(40, 8, res_req->type[5]);
+ cmd.params[2] |= mc_enc(48, 8, res_req->type[6]);
+ cmd.params[2] |= mc_enc(56, 8, res_req->type[7]);
+ cmd.params[3] |= mc_enc(0, 8, res_req->type[8]);
+ cmd.params[3] |= mc_enc(8, 8, res_req->type[9]);
+ cmd.params[3] |= mc_enc(16, 8, res_req->type[10]);
+ cmd.params[3] |= mc_enc(24, 8, res_req->type[11]);
+ cmd.params[3] |= mc_enc(32, 8, res_req->type[12]);
+ cmd.params[3] |= mc_enc(40, 8, res_req->type[13]);
+ cmd.params[3] |= mc_enc(48, 8, res_req->type[14]);
+ cmd.params[3] |= mc_enc(56, 8, res_req->type[15]);
+
+ /* send command to mc*/
+ return mc_send_command(mc_io, &cmd);
+}
+
+int dprc_get_pool_count(struct fsl_mc_io *mc_io,
+ uint16_t token,
+ int *pool_count)
+{
+ struct mc_command cmd = { 0 };
+ int err;
+
+ /* prepare command */
+ cmd.header = mc_encode_cmd_header(DPRC_CMDID_GET_POOL_COUNT,
+ MC_CMD_PRI_LOW, token);
+
+ /* send command to mc*/
+ err = mc_send_command(mc_io, &cmd);
+ if (err)
+ return err;
+
+ /* retrieve response parameters */
+ *pool_count = mc_dec(cmd.params[0], 0, 32);
+
+ return 0;
+}
+
+int dprc_get_pool(struct fsl_mc_io *mc_io,
+ uint16_t token,
+ int pool_index,
+ char *type)
+{
+ struct mc_command cmd = { 0 };
+ int err;
+
+ /* prepare command */
+ cmd.header = mc_encode_cmd_header(DPRC_CMDID_GET_POOL,
+ MC_CMD_PRI_LOW,
+ token);
+ cmd.params[0] |= mc_enc(0, 32, pool_index);
+
+ /* send command to mc*/
+ err = mc_send_command(mc_io, &cmd);
+ if (err)
+ return err;
+
+ /* retrieve response parameters */
+ type[0] = mc_dec(cmd.params[1], 0, 8);
+ type[1] = mc_dec(cmd.params[1], 8, 8);
+ type[2] = mc_dec(cmd.params[1], 16, 8);
+ type[3] = mc_dec(cmd.params[1], 24, 8);
+ type[4] = mc_dec(cmd.params[1], 32, 8);
+ type[5] = mc_dec(cmd.params[1], 40, 8);
+ type[6] = mc_dec(cmd.params[1], 48, 8);
+ type[7] = mc_dec(cmd.params[1], 56, 8);
+ type[8] = mc_dec(cmd.params[2], 0, 8);
+ type[9] = mc_dec(cmd.params[2], 8, 8);
+ type[10] = mc_dec(cmd.params[2], 16, 8);
+ type[11] = mc_dec(cmd.params[2], 24, 8);
+ type[12] = mc_dec(cmd.params[2], 32, 8);
+ type[13] = mc_dec(cmd.params[2], 40, 8);
+ type[14] = mc_dec(cmd.params[2], 48, 8);
+ type[15] = '\0';
+
+ return 0;
+}
+
+int dprc_get_obj_count(struct fsl_mc_io *mc_io, uint16_t token, int *obj_count)
+{
+ struct mc_command cmd = { 0 };
+ int err;
+
+ /* prepare command */
+ cmd.header = mc_encode_cmd_header(DPRC_CMDID_GET_OBJ_COUNT,
+ MC_CMD_PRI_LOW, token);
+
+ /* send command to mc*/
+ err = mc_send_command(mc_io, &cmd);
+ if (err)
+ return err;
+
+ /* retrieve response parameters */
+ *obj_count = mc_dec(cmd.params[0], 32, 32);
+
+ return 0;
+}
+EXPORT_SYMBOL(dprc_get_obj_count);
+
+int dprc_get_obj(struct fsl_mc_io *mc_io,
+ uint16_t token,
+ int obj_index,
+ struct dprc_obj_desc *obj_desc)
+{
+ struct mc_command cmd = { 0 };
+ int err;
+
+ /* prepare command */
+ cmd.header = mc_encode_cmd_header(DPRC_CMDID_GET_OBJ,
+ MC_CMD_PRI_LOW,
+ token);
+ cmd.params[0] |= mc_enc(0, 32, obj_index);
+
+ /* send command to mc*/
+ err = mc_send_command(mc_io, &cmd);
+ if (err)
+ return err;
+
+ /* retrieve response parameters */
+ obj_desc->id = mc_dec(cmd.params[0], 32, 32);
+ obj_desc->vendor = mc_dec(cmd.params[1], 0, 16);
+ obj_desc->irq_count = mc_dec(cmd.params[1], 16, 8);
+ obj_desc->region_count = mc_dec(cmd.params[1], 24, 8);
+ obj_desc->state = mc_dec(cmd.params[1], 32, 32);
+ obj_desc->ver_major = mc_dec(cmd.params[2], 0, 16);
+ obj_desc->ver_minor = mc_dec(cmd.params[2], 16, 16);
+ obj_desc->type[0] = mc_dec(cmd.params[3], 0, 8);
+ obj_desc->type[1] = mc_dec(cmd.params[3], 8, 8);
+ obj_desc->type[2] = mc_dec(cmd.params[3], 16, 8);
+ obj_desc->type[3] = mc_dec(cmd.params[3], 24, 8);
+ obj_desc->type[4] = mc_dec(cmd.params[3], 32, 8);
+ obj_desc->type[5] = mc_dec(cmd.params[3], 40, 8);
+ obj_desc->type[6] = mc_dec(cmd.params[3], 48, 8);
+ obj_desc->type[7] = mc_dec(cmd.params[3], 56, 8);
+ obj_desc->type[8] = mc_dec(cmd.params[4], 0, 8);
+ obj_desc->type[9] = mc_dec(cmd.params[4], 8, 8);
+ obj_desc->type[10] = mc_dec(cmd.params[4], 16, 8);
+ obj_desc->type[11] = mc_dec(cmd.params[4], 24, 8);
+ obj_desc->type[12] = mc_dec(cmd.params[4], 32, 8);
+ obj_desc->type[13] = mc_dec(cmd.params[4], 40, 8);
+ obj_desc->type[14] = mc_dec(cmd.params[4], 48, 8);
+ obj_desc->type[15] = '\0';
+
+ return 0;
+}
+EXPORT_SYMBOL(dprc_get_obj);
+
+int dprc_get_res_count(struct fsl_mc_io *mc_io,
+ uint16_t token,
+ char *type,
+ int *res_count)
+{
+ struct mc_command cmd = { 0 };
+ int err;
+
+ *res_count = 0;
+
+ /* prepare command */
+ cmd.header = mc_encode_cmd_header(DPRC_CMDID_GET_RES_COUNT,
+ MC_CMD_PRI_LOW, token);
+ cmd.params[1] |= mc_enc(0, 8, type[0]);
+ cmd.params[1] |= mc_enc(8, 8, type[1]);
+ cmd.params[1] |= mc_enc(16, 8, type[2]);
+ cmd.params[1] |= mc_enc(24, 8, type[3]);
+ cmd.params[1] |= mc_enc(32, 8, type[4]);
+ cmd.params[1] |= mc_enc(40, 8, type[5]);
+ cmd.params[1] |= mc_enc(48, 8, type[6]);
+ cmd.params[1] |= mc_enc(56, 8, type[7]);
+ cmd.params[2] |= mc_enc(0, 8, type[8]);
+ cmd.params[2] |= mc_enc(8, 8, type[9]);
+ cmd.params[2] |= mc_enc(16, 8, type[10]);
+ cmd.params[2] |= mc_enc(24, 8, type[11]);
+ cmd.params[2] |= mc_enc(32, 8, type[12]);
+ cmd.params[2] |= mc_enc(40, 8, type[13]);
+ cmd.params[2] |= mc_enc(48, 8, type[14]);
+ cmd.params[2] |= mc_enc(56, 8, '\0');
+
+ /* send command to mc*/
+ err = mc_send_command(mc_io, &cmd);
+ if (err)
+ return err;
+
+ /* retrieve response parameters */
+ *res_count = mc_dec(cmd.params[0], 0, 32);
+
+ return 0;
+}
+EXPORT_SYMBOL(dprc_get_res_count);
+
+int dprc_get_res_ids(struct fsl_mc_io *mc_io,
+ uint16_t token,
+ char *type,
+ struct dprc_res_ids_range_desc *range_desc)
+{
+ struct mc_command cmd = { 0 };
+ int err;
+
+ /* prepare command */
+ cmd.header = mc_encode_cmd_header(DPRC_CMDID_GET_RES_IDS,
+ MC_CMD_PRI_LOW, token);
+ cmd.params[0] |= mc_enc(42, 7, range_desc->iter_status);
+ cmd.params[1] |= mc_enc(0, 32, range_desc->base_id);
+ cmd.params[1] |= mc_enc(32, 32, range_desc->last_id);
+ cmd.params[2] |= mc_enc(0, 8, type[0]);
+ cmd.params[2] |= mc_enc(8, 8, type[1]);
+ cmd.params[2] |= mc_enc(16, 8, type[2]);
+ cmd.params[2] |= mc_enc(24, 8, type[3]);
+ cmd.params[2] |= mc_enc(32, 8, type[4]);
+ cmd.params[2] |= mc_enc(40, 8, type[5]);
+ cmd.params[2] |= mc_enc(48, 8, type[6]);
+ cmd.params[2] |= mc_enc(56, 8, type[7]);
+ cmd.params[3] |= mc_enc(0, 8, type[8]);
+ cmd.params[3] |= mc_enc(8, 8, type[9]);
+ cmd.params[3] |= mc_enc(16, 8, type[10]);
+ cmd.params[3] |= mc_enc(24, 8, type[11]);
+ cmd.params[3] |= mc_enc(32, 8, type[12]);
+ cmd.params[3] |= mc_enc(40, 8, type[13]);
+ cmd.params[3] |= mc_enc(48, 8, type[14]);
+ cmd.params[3] |= mc_enc(56, 8, '\0');
+
+ /* send command to mc*/
+ err = mc_send_command(mc_io, &cmd);
+ if (err)
+ return err;
+
+ /* retrieve response parameters */
+ range_desc->iter_status = mc_dec(cmd.params[0], 42, 7);
+ range_desc->base_id = mc_dec(cmd.params[1], 0, 32);
+ range_desc->last_id = mc_dec(cmd.params[1], 32, 32);
+
+ return 0;
+}
+EXPORT_SYMBOL(dprc_get_res_ids);
+
+int dprc_get_portal_paddr(struct fsl_mc_io *mc_io,
+ uint16_t token,
+ int portal_id,
+ uint64_t *portal_addr)
+{
+ struct mc_command cmd = { 0 };
+ int err;
+
+ /* prepare command */
+ cmd.header = mc_encode_cmd_header(DPRC_CMDID_GET_PORTAL_PADDR,
+ MC_CMD_PRI_LOW, token);
+ cmd.params[0] |= mc_enc(0, 32, portal_id);
+
+ /* send command to mc*/
+ err = mc_send_command(mc_io, &cmd);
+ if (err)
+ return err;
+
+ /* retrieve response parameters */
+ *portal_addr = mc_dec(cmd.params[1], 0, 64);
+
+ return 0;
+}
+EXPORT_SYMBOL(dprc_get_portal_paddr);
+
+int dprc_get_obj_region(struct fsl_mc_io *mc_io,
+ uint16_t token,
+ char *obj_type,
+ int obj_id,
+ uint8_t region_index,
+ struct dprc_region_desc *region_desc)
+{
+ struct mc_command cmd = { 0 };
+ int err;
+
+ /* prepare command */
+ cmd.header = mc_encode_cmd_header(DPRC_CMDID_GET_OBJ_REG,
+ MC_CMD_PRI_LOW, token);
+ cmd.params[0] |= mc_enc(0, 32, obj_id);
+ cmd.params[0] |= mc_enc(48, 8, region_index);
+ cmd.params[3] |= mc_enc(0, 8, obj_type[0]);
+ cmd.params[3] |= mc_enc(8, 8, obj_type[1]);
+ cmd.params[3] |= mc_enc(16, 8, obj_type[2]);
+ cmd.params[3] |= mc_enc(24, 8, obj_type[3]);
+ cmd.params[3] |= mc_enc(32, 8, obj_type[4]);
+ cmd.params[3] |= mc_enc(40, 8, obj_type[5]);
+ cmd.params[3] |= mc_enc(48, 8, obj_type[6]);
+ cmd.params[3] |= mc_enc(56, 8, obj_type[7]);
+ cmd.params[4] |= mc_enc(0, 8, obj_type[8]);
+ cmd.params[4] |= mc_enc(8, 8, obj_type[9]);
+ cmd.params[4] |= mc_enc(16, 8, obj_type[10]);
+ cmd.params[4] |= mc_enc(24, 8, obj_type[11]);
+ cmd.params[4] |= mc_enc(32, 8, obj_type[12]);
+ cmd.params[4] |= mc_enc(40, 8, obj_type[13]);
+ cmd.params[4] |= mc_enc(48, 8, obj_type[14]);
+ cmd.params[4] |= mc_enc(56, 8, '\0');
+
+ /* send command to mc*/
+ err = mc_send_command(mc_io, &cmd);
+ if (err)
+ return err;
+
+ /* retrieve response parameters */
+ region_desc->base_paddr = mc_dec(cmd.params[1], 0, 64);
+ region_desc->size = mc_dec(cmd.params[2], 0, 32);
+
+ return 0;
+}
+EXPORT_SYMBOL(dprc_get_obj_region);
+
+int dprc_connect(struct fsl_mc_io *mc_io,
+ uint16_t token,
+ const struct dprc_endpoint *endpoint1,
+ const struct dprc_endpoint *endpoint2)
+{
+ struct mc_command cmd = { 0 };
+
+ /* prepare command */
+ cmd.header = mc_encode_cmd_header(DPRC_CMDID_CONNECT,
+ MC_CMD_PRI_LOW,
+ token);
+ cmd.params[0] |= mc_enc(0, 32, endpoint1->id);
+ cmd.params[0] |= mc_enc(32, 32, endpoint1->interface_id);
+ cmd.params[1] |= mc_enc(0, 32, endpoint2->id);
+ cmd.params[1] |= mc_enc(32, 32, endpoint2->interface_id);
+ cmd.params[2] |= mc_enc(0, 8, endpoint1->type[0]);
+ cmd.params[2] |= mc_enc(8, 8, endpoint1->type[1]);
+ cmd.params[2] |= mc_enc(16, 8, endpoint1->type[2]);
+ cmd.params[2] |= mc_enc(24, 8, endpoint1->type[3]);
+ cmd.params[2] |= mc_enc(32, 8, endpoint1->type[4]);
+ cmd.params[2] |= mc_enc(40, 8, endpoint1->type[5]);
+ cmd.params[2] |= mc_enc(48, 8, endpoint1->type[6]);
+ cmd.params[2] |= mc_enc(56, 8, endpoint1->type[7]);
+ cmd.params[3] |= mc_enc(0, 8, endpoint1->type[8]);
+ cmd.params[3] |= mc_enc(8, 8, endpoint1->type[9]);
+ cmd.params[3] |= mc_enc(16, 8, endpoint1->type[10]);
+ cmd.params[3] |= mc_enc(24, 8, endpoint1->type[11]);
+ cmd.params[3] |= mc_enc(32, 8, endpoint1->type[12]);
+ cmd.params[3] |= mc_enc(40, 8, endpoint1->type[13]);
+ cmd.params[3] |= mc_enc(48, 8, endpoint1->type[14]);
+ cmd.params[3] |= mc_enc(56, 8, endpoint1->type[15]);
+ cmd.params[5] |= mc_enc(0, 8, endpoint2->type[0]);
+ cmd.params[5] |= mc_enc(8, 8, endpoint2->type[1]);
+ cmd.params[5] |= mc_enc(16, 8, endpoint2->type[2]);
+ cmd.params[5] |= mc_enc(24, 8, endpoint2->type[3]);
+ cmd.params[5] |= mc_enc(32, 8, endpoint2->type[4]);
+ cmd.params[5] |= mc_enc(40, 8, endpoint2->type[5]);
+ cmd.params[5] |= mc_enc(48, 8, endpoint2->type[6]);
+ cmd.params[5] |= mc_enc(56, 8, endpoint2->type[7]);
+ cmd.params[6] |= mc_enc(0, 8, endpoint2->type[8]);
+ cmd.params[6] |= mc_enc(8, 8, endpoint2->type[9]);
+ cmd.params[6] |= mc_enc(16, 8, endpoint2->type[10]);
+ cmd.params[6] |= mc_enc(24, 8, endpoint2->type[11]);
+ cmd.params[6] |= mc_enc(32, 8, endpoint2->type[12]);
+ cmd.params[6] |= mc_enc(40, 8, endpoint2->type[13]);
+ cmd.params[6] |= mc_enc(48, 8, endpoint2->type[14]);
+ cmd.params[6] |= mc_enc(56, 8, endpoint2->type[15]);
+
+ /* send command to mc*/
+ return mc_send_command(mc_io, &cmd);
+}
+
+int dprc_disconnect(struct fsl_mc_io *mc_io,
+ uint16_t token,
+ const struct dprc_endpoint *endpoint)
+{
+ struct mc_command cmd = { 0 };
+
+ /* prepare command */
+ cmd.header = mc_encode_cmd_header(DPRC_CMDID_DISCONNECT,
+ MC_CMD_PRI_LOW,
+ token);
+ cmd.params[0] |= mc_enc(0, 32, endpoint->id);
+ cmd.params[0] |= mc_enc(32, 32, endpoint->interface_id);
+ cmd.params[1] |= mc_enc(0, 8, endpoint->type[0]);
+ cmd.params[1] |= mc_enc(8, 8, endpoint->type[1]);
+ cmd.params[1] |= mc_enc(16, 8, endpoint->type[2]);
+ cmd.params[1] |= mc_enc(24, 8, endpoint->type[3]);
+ cmd.params[1] |= mc_enc(32, 8, endpoint->type[4]);
+ cmd.params[1] |= mc_enc(40, 8, endpoint->type[5]);
+ cmd.params[1] |= mc_enc(48, 8, endpoint->type[6]);
+ cmd.params[1] |= mc_enc(56, 8, endpoint->type[7]);
+ cmd.params[2] |= mc_enc(0, 8, endpoint->type[8]);
+ cmd.params[2] |= mc_enc(8, 8, endpoint->type[9]);
+ cmd.params[2] |= mc_enc(16, 8, endpoint->type[10]);
+ cmd.params[2] |= mc_enc(24, 8, endpoint->type[11]);
+ cmd.params[2] |= mc_enc(32, 8, endpoint->type[12]);
+ cmd.params[2] |= mc_enc(40, 8, endpoint->type[13]);
+ cmd.params[2] |= mc_enc(48, 8, endpoint->type[14]);
+ cmd.params[2] |= mc_enc(56, 8, endpoint->type[15]);
+
+ /* send command to mc*/
+ return mc_send_command(mc_io, &cmd);
+}
+
+int dprc_get_connection(struct fsl_mc_io *mc_io,
+ uint16_t token,
+ const struct dprc_endpoint *endpoint1,
+ struct dprc_endpoint *endpoint2,
+ int *state)
+{
+ struct mc_command cmd = { 0 };
+ int err;
+
+ /* prepare command */
+ cmd.header = mc_encode_cmd_header(DPRC_CMDID_GET_CONNECTION,
+ MC_CMD_PRI_LOW,
+ token);
+ cmd.params[0] |= mc_enc(0, 32, endpoint1->id);
+ cmd.params[0] |= mc_enc(32, 32, endpoint1->interface_id);
+ cmd.params[1] |= mc_enc(0, 8, endpoint1->type[0]);
+ cmd.params[1] |= mc_enc(8, 8, endpoint1->type[1]);
+ cmd.params[1] |= mc_enc(16, 8, endpoint1->type[2]);
+ cmd.params[1] |= mc_enc(24, 8, endpoint1->type[3]);
+ cmd.params[1] |= mc_enc(32, 8, endpoint1->type[4]);
+ cmd.params[1] |= mc_enc(40, 8, endpoint1->type[5]);
+ cmd.params[1] |= mc_enc(48, 8, endpoint1->type[6]);
+ cmd.params[1] |= mc_enc(56, 8, endpoint1->type[7]);
+ cmd.params[2] |= mc_enc(0, 8, endpoint1->type[8]);
+ cmd.params[2] |= mc_enc(8, 8, endpoint1->type[9]);
+ cmd.params[2] |= mc_enc(16, 8, endpoint1->type[10]);
+ cmd.params[2] |= mc_enc(24, 8, endpoint1->type[11]);
+ cmd.params[2] |= mc_enc(32, 8, endpoint1->type[12]);
+ cmd.params[2] |= mc_enc(40, 8, endpoint1->type[13]);
+ cmd.params[2] |= mc_enc(48, 8, endpoint1->type[14]);
+ cmd.params[2] |= mc_enc(56, 8, endpoint1->type[15]);
+
+ /* send command to mc*/
+ err = mc_send_command(mc_io, &cmd);
+ if (err)
+ return err;
+
+ /* retrieve response parameters */
+ endpoint2->id = mc_dec(cmd.params[3], 0, 32);
+ endpoint2->interface_id = mc_dec(cmd.params[3], 32, 32);
+ endpoint2->type[0] = mc_dec(cmd.params[4], 0, 8);
+ endpoint2->type[1] = mc_dec(cmd.params[4], 8, 8);
+ endpoint2->type[2] = mc_dec(cmd.params[4], 16, 8);
+ endpoint2->type[3] = mc_dec(cmd.params[4], 24, 8);
+ endpoint2->type[4] = mc_dec(cmd.params[4], 32, 8);
+ endpoint2->type[5] = mc_dec(cmd.params[4], 40, 8);
+ endpoint2->type[6] = mc_dec(cmd.params[4], 48, 8);
+ endpoint2->type[7] = mc_dec(cmd.params[4], 56, 8);
+ endpoint2->type[8] = mc_dec(cmd.params[5], 0, 8);
+ endpoint2->type[9] = mc_dec(cmd.params[5], 8, 8);
+ endpoint2->type[10] = mc_dec(cmd.params[5], 16, 8);
+ endpoint2->type[11] = mc_dec(cmd.params[5], 24, 8);
+ endpoint2->type[12] = mc_dec(cmd.params[5], 32, 8);
+ endpoint2->type[13] = mc_dec(cmd.params[5], 40, 8);
+ endpoint2->type[14] = mc_dec(cmd.params[5], 48, 8);
+ endpoint2->type[15] = mc_dec(cmd.params[5], 56, 8);
+ *state = mc_dec(cmd.params[6], 0, 32);
+
+ return 0;
+}
diff --git a/drivers/staging/fsl-mc/bus/mc-allocator.c b/drivers/staging/fsl-mc/bus/mc-allocator.c
new file mode 100644
index 000000000..e36235ddb
--- /dev/null
+++ b/drivers/staging/fsl-mc/bus/mc-allocator.c
@@ -0,0 +1,573 @@
+/*
+ * Freescale MC object device allocator driver
+ *
+ * Copyright (C) 2013 Freescale Semiconductor, Inc.
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#include "../include/mc-private.h"
+#include "../include/mc-sys.h"
+#include <linux/module.h>
+#include "../include/dpbp-cmd.h"
+#include "../include/dpcon-cmd.h"
+#include "dpmcp-cmd.h"
+#include "dpmcp.h"
+
+/**
+ * fsl_mc_resource_pool_add_device - add allocatable device to a resource
+ * pool of a given MC bus
+ *
+ * @mc_bus: pointer to the MC bus
+ * @pool_type: MC bus pool type
+ * @mc_dev: Pointer to allocatable MC object device
+ *
+ * It adds an allocatable MC object device to a container's resource pool of
+ * the given resource type
+ */
+static int __must_check fsl_mc_resource_pool_add_device(struct fsl_mc_bus
+ *mc_bus,
+ enum fsl_mc_pool_type
+ pool_type,
+ struct fsl_mc_device
+ *mc_dev)
+{
+ struct fsl_mc_resource_pool *res_pool;
+ struct fsl_mc_resource *resource;
+ struct fsl_mc_device *mc_bus_dev = &mc_bus->mc_dev;
+ int error = -EINVAL;
+ bool mutex_locked = false;
+
+ if (WARN_ON(pool_type < 0 || pool_type >= FSL_MC_NUM_POOL_TYPES))
+ goto out;
+ if (WARN_ON(!FSL_MC_IS_ALLOCATABLE(mc_dev->obj_desc.type)))
+ goto out;
+ if (WARN_ON(mc_dev->resource))
+ goto out;
+
+ res_pool = &mc_bus->resource_pools[pool_type];
+ if (WARN_ON(res_pool->type != pool_type))
+ goto out;
+ if (WARN_ON(res_pool->mc_bus != mc_bus))
+ goto out;
+
+ mutex_lock(&res_pool->mutex);
+ mutex_locked = true;
+
+ if (WARN_ON(res_pool->max_count < 0))
+ goto out;
+ if (WARN_ON(res_pool->free_count < 0 ||
+ res_pool->free_count > res_pool->max_count))
+ goto out;
+
+ resource = devm_kzalloc(&mc_bus_dev->dev, sizeof(*resource),
+ GFP_KERNEL);
+ if (!resource) {
+ error = -ENOMEM;
+ dev_err(&mc_bus_dev->dev,
+ "Failed to allocate memory for fsl_mc_resource\n");
+ goto out;
+ }
+
+ resource->type = pool_type;
+ resource->id = mc_dev->obj_desc.id;
+ resource->data = mc_dev;
+ resource->parent_pool = res_pool;
+ INIT_LIST_HEAD(&resource->node);
+ list_add_tail(&resource->node, &res_pool->free_list);
+ mc_dev->resource = resource;
+ res_pool->free_count++;
+ res_pool->max_count++;
+ error = 0;
+out:
+ if (mutex_locked)
+ mutex_unlock(&res_pool->mutex);
+
+ return error;
+}
+
+/**
+ * fsl_mc_resource_pool_remove_device - remove an allocatable device from a
+ * resource pool
+ *
+ * @mc_dev: Pointer to allocatable MC object device
+ *
+ * It permanently removes an allocatable MC object device from the resource
+ * pool, the device is currently in, as long as it is in the pool's free list.
+ */
+static int __must_check fsl_mc_resource_pool_remove_device(struct fsl_mc_device
+ *mc_dev)
+{
+ struct fsl_mc_device *mc_bus_dev;
+ struct fsl_mc_bus *mc_bus;
+ struct fsl_mc_resource_pool *res_pool;
+ struct fsl_mc_resource *resource;
+ int error = -EINVAL;
+ bool mutex_locked = false;
+
+ if (WARN_ON(!FSL_MC_IS_ALLOCATABLE(mc_dev->obj_desc.type)))
+ goto out;
+
+ resource = mc_dev->resource;
+ if (WARN_ON(resource->data != mc_dev))
+ goto out;
+
+ mc_bus_dev = to_fsl_mc_device(mc_dev->dev.parent);
+ mc_bus = to_fsl_mc_bus(mc_bus_dev);
+ res_pool = resource->parent_pool;
+ if (WARN_ON(res_pool != &mc_bus->resource_pools[resource->type]))
+ goto out;
+
+ mutex_lock(&res_pool->mutex);
+ mutex_locked = true;
+
+ if (WARN_ON(res_pool->max_count <= 0))
+ goto out;
+ if (WARN_ON(res_pool->free_count <= 0 ||
+ res_pool->free_count > res_pool->max_count))
+ goto out;
+
+ /*
+ * If the device is currently allocated, its resource is not
+ * in the free list and thus, the device cannot be removed.
+ */
+ if (list_empty(&resource->node)) {
+ error = -EBUSY;
+ dev_err(&mc_bus_dev->dev,
+ "Device %s cannot be removed from resource pool\n",
+ dev_name(&mc_dev->dev));
+ goto out;
+ }
+
+ list_del(&resource->node);
+ INIT_LIST_HEAD(&resource->node);
+ res_pool->free_count--;
+ res_pool->max_count--;
+
+ devm_kfree(&mc_bus_dev->dev, resource);
+ mc_dev->resource = NULL;
+ error = 0;
+out:
+ if (mutex_locked)
+ mutex_unlock(&res_pool->mutex);
+
+ return error;
+}
+
+static const char *const fsl_mc_pool_type_strings[] = {
+ [FSL_MC_POOL_DPMCP] = "dpmcp",
+ [FSL_MC_POOL_DPBP] = "dpbp",
+ [FSL_MC_POOL_DPCON] = "dpcon",
+};
+
+static int __must_check object_type_to_pool_type(const char *object_type,
+ enum fsl_mc_pool_type
+ *pool_type)
+{
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(fsl_mc_pool_type_strings); i++) {
+ if (strcmp(object_type, fsl_mc_pool_type_strings[i]) == 0) {
+ *pool_type = i;
+ return 0;
+ }
+ }
+
+ return -EINVAL;
+}
+
+int __must_check fsl_mc_resource_allocate(struct fsl_mc_bus *mc_bus,
+ enum fsl_mc_pool_type pool_type,
+ struct fsl_mc_resource **new_resource)
+{
+ struct fsl_mc_resource_pool *res_pool;
+ struct fsl_mc_resource *resource;
+ struct fsl_mc_device *mc_bus_dev = &mc_bus->mc_dev;
+ int error = -EINVAL;
+ bool mutex_locked = false;
+
+ BUILD_BUG_ON(ARRAY_SIZE(fsl_mc_pool_type_strings) !=
+ FSL_MC_NUM_POOL_TYPES);
+
+ *new_resource = NULL;
+ if (WARN_ON(pool_type < 0 || pool_type >= FSL_MC_NUM_POOL_TYPES))
+ goto error;
+
+ res_pool = &mc_bus->resource_pools[pool_type];
+ if (WARN_ON(res_pool->mc_bus != mc_bus))
+ goto error;
+
+ mutex_lock(&res_pool->mutex);
+ mutex_locked = true;
+ resource = list_first_entry_or_null(&res_pool->free_list,
+ struct fsl_mc_resource, node);
+
+ if (!resource) {
+ WARN_ON(res_pool->free_count != 0);
+ error = -ENXIO;
+ dev_err(&mc_bus_dev->dev,
+ "No more resources of type %s left\n",
+ fsl_mc_pool_type_strings[pool_type]);
+ goto error;
+ }
+
+ if (WARN_ON(resource->type != pool_type))
+ goto error;
+ if (WARN_ON(resource->parent_pool != res_pool))
+ goto error;
+ if (WARN_ON(res_pool->free_count <= 0 ||
+ res_pool->free_count > res_pool->max_count))
+ goto error;
+
+ list_del(&resource->node);
+ INIT_LIST_HEAD(&resource->node);
+
+ res_pool->free_count--;
+ mutex_unlock(&res_pool->mutex);
+ *new_resource = resource;
+ return 0;
+error:
+ if (mutex_locked)
+ mutex_unlock(&res_pool->mutex);
+
+ return error;
+}
+EXPORT_SYMBOL_GPL(fsl_mc_resource_allocate);
+
+void fsl_mc_resource_free(struct fsl_mc_resource *resource)
+{
+ struct fsl_mc_resource_pool *res_pool;
+ bool mutex_locked = false;
+
+ res_pool = resource->parent_pool;
+ if (WARN_ON(resource->type != res_pool->type))
+ goto out;
+
+ mutex_lock(&res_pool->mutex);
+ mutex_locked = true;
+ if (WARN_ON(res_pool->free_count < 0 ||
+ res_pool->free_count >= res_pool->max_count))
+ goto out;
+
+ if (WARN_ON(!list_empty(&resource->node)))
+ goto out;
+
+ list_add_tail(&resource->node, &res_pool->free_list);
+ res_pool->free_count++;
+out:
+ if (mutex_locked)
+ mutex_unlock(&res_pool->mutex);
+}
+EXPORT_SYMBOL_GPL(fsl_mc_resource_free);
+
+/**
+ * fsl_mc_portal_allocate - Allocates an MC portal
+ *
+ * @mc_dev: MC device for which the MC portal is to be allocated
+ * @mc_io_flags: Flags for the fsl_mc_io object that wraps the allocated
+ * MC portal.
+ * @new_mc_io: Pointer to area where the pointer to the fsl_mc_io object
+ * that wraps the allocated MC portal is to be returned
+ *
+ * This function allocates an MC portal from the device's parent DPRC,
+ * from the corresponding MC bus' pool of MC portals and wraps
+ * it in a new fsl_mc_io object. If 'mc_dev' is a DPRC itself, the
+ * portal is allocated from its own MC bus.
+ */
+int __must_check fsl_mc_portal_allocate(struct fsl_mc_device *mc_dev,
+ uint16_t mc_io_flags,
+ struct fsl_mc_io **new_mc_io)
+{
+ struct fsl_mc_device *mc_bus_dev;
+ struct fsl_mc_bus *mc_bus;
+ phys_addr_t mc_portal_phys_addr;
+ size_t mc_portal_size;
+ struct fsl_mc_device *mc_adev;
+ int error = -EINVAL;
+ struct fsl_mc_resource *resource = NULL;
+ struct fsl_mc_io *mc_io = NULL;
+
+ if (mc_dev->flags & FSL_MC_IS_DPRC) {
+ mc_bus_dev = mc_dev;
+ } else {
+ if (WARN_ON(mc_dev->dev.parent->bus != &fsl_mc_bus_type))
+ return error;
+
+ mc_bus_dev = to_fsl_mc_device(mc_dev->dev.parent);
+ }
+
+ mc_bus = to_fsl_mc_bus(mc_bus_dev);
+ *new_mc_io = NULL;
+ error = fsl_mc_resource_allocate(mc_bus, FSL_MC_POOL_DPMCP, &resource);
+ if (error < 0)
+ return error;
+
+ mc_adev = resource->data;
+ if (WARN_ON(!mc_adev))
+ goto error_cleanup_resource;
+
+ if (WARN_ON(mc_adev->obj_desc.region_count == 0))
+ goto error_cleanup_resource;
+
+ mc_portal_phys_addr = mc_adev->regions[0].start;
+ mc_portal_size = mc_adev->regions[0].end -
+ mc_adev->regions[0].start + 1;
+
+ if (WARN_ON(mc_portal_size != mc_bus_dev->mc_io->portal_size))
+ goto error_cleanup_resource;
+
+ error = fsl_create_mc_io(&mc_bus_dev->dev,
+ mc_portal_phys_addr,
+ mc_portal_size, resource,
+ mc_io_flags, &mc_io);
+ if (error < 0)
+ goto error_cleanup_resource;
+
+ *new_mc_io = mc_io;
+ return 0;
+
+error_cleanup_resource:
+ fsl_mc_resource_free(resource);
+ return error;
+}
+EXPORT_SYMBOL_GPL(fsl_mc_portal_allocate);
+
+/**
+ * fsl_mc_portal_free - Returns an MC portal to the pool of free MC portals
+ * of a given MC bus
+ *
+ * @mc_io: Pointer to the fsl_mc_io object that wraps the MC portal to free
+ */
+void fsl_mc_portal_free(struct fsl_mc_io *mc_io)
+{
+ struct fsl_mc_resource *resource;
+
+ resource = mc_io->resource;
+ if (WARN_ON(resource->type != FSL_MC_POOL_DPMCP))
+ return;
+ if (WARN_ON(!resource->data))
+ return;
+
+ fsl_destroy_mc_io(mc_io);
+ fsl_mc_resource_free(resource);
+}
+EXPORT_SYMBOL_GPL(fsl_mc_portal_free);
+
+/**
+ * fsl_mc_portal_reset - Resets the dpmcp object for a given fsl_mc_io object
+ *
+ * @mc_io: Pointer to the fsl_mc_io object that wraps the MC portal to free
+ */
+int fsl_mc_portal_reset(struct fsl_mc_io *mc_io)
+{
+ int error;
+ uint16_t token;
+ struct fsl_mc_resource *resource = mc_io->resource;
+ struct fsl_mc_device *mc_dev = resource->data;
+
+ if (WARN_ON(resource->type != FSL_MC_POOL_DPMCP))
+ return -EINVAL;
+
+ if (WARN_ON(!mc_dev))
+ return -EINVAL;
+
+ error = dpmcp_open(mc_io, mc_dev->obj_desc.id, &token);
+ if (error < 0) {
+ dev_err(&mc_dev->dev, "dpmcp_open() failed: %d\n", error);
+ return error;
+ }
+
+ error = dpmcp_reset(mc_io, token);
+ if (error < 0) {
+ dev_err(&mc_dev->dev, "dpmcp_reset() failed: %d\n", error);
+ return error;
+ }
+
+ error = dpmcp_close(mc_io, token);
+ if (error < 0) {
+ dev_err(&mc_dev->dev, "dpmcp_close() failed: %d\n", error);
+ return error;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(fsl_mc_portal_reset);
+
+/**
+ * fsl_mc_object_allocate - Allocates a MC object device of the given
+ * pool type from a given MC bus
+ *
+ * @mc_dev: MC device for which the MC object device is to be allocated
+ * @pool_type: MC bus resource pool type
+ * @new_mc_dev: Pointer to area where the pointer to the allocated
+ * MC object device is to be returned
+ *
+ * This function allocates a MC object device from the device's parent DPRC,
+ * from the corresponding MC bus' pool of allocatable MC object devices of
+ * the given resource type. mc_dev cannot be a DPRC itself.
+ *
+ * NOTE: pool_type must be different from FSL_MC_POOL_MCP, since MC
+ * portals are allocated using fsl_mc_portal_allocate(), instead of
+ * this function.
+ */
+int __must_check fsl_mc_object_allocate(struct fsl_mc_device *mc_dev,
+ enum fsl_mc_pool_type pool_type,
+ struct fsl_mc_device **new_mc_adev)
+{
+ struct fsl_mc_device *mc_bus_dev;
+ struct fsl_mc_bus *mc_bus;
+ struct fsl_mc_device *mc_adev;
+ int error = -EINVAL;
+ struct fsl_mc_resource *resource = NULL;
+
+ *new_mc_adev = NULL;
+ if (WARN_ON(mc_dev->flags & FSL_MC_IS_DPRC))
+ goto error;
+
+ if (WARN_ON(mc_dev->dev.parent->bus != &fsl_mc_bus_type))
+ goto error;
+
+ if (WARN_ON(pool_type == FSL_MC_POOL_DPMCP))
+ goto error;
+
+ mc_bus_dev = to_fsl_mc_device(mc_dev->dev.parent);
+ mc_bus = to_fsl_mc_bus(mc_bus_dev);
+ error = fsl_mc_resource_allocate(mc_bus, pool_type, &resource);
+ if (error < 0)
+ goto error;
+
+ mc_adev = resource->data;
+ if (WARN_ON(!mc_adev))
+ goto error;
+
+ *new_mc_adev = mc_adev;
+ return 0;
+error:
+ if (resource)
+ fsl_mc_resource_free(resource);
+
+ return error;
+}
+EXPORT_SYMBOL_GPL(fsl_mc_object_allocate);
+
+/**
+ * fsl_mc_object_free - Returns an allocatable MC object device to the
+ * corresponding resource pool of a given MC bus.
+ *
+ * @mc_adev: Pointer to the MC object device
+ */
+void fsl_mc_object_free(struct fsl_mc_device *mc_adev)
+{
+ struct fsl_mc_resource *resource;
+
+ resource = mc_adev->resource;
+ if (WARN_ON(resource->type == FSL_MC_POOL_DPMCP))
+ return;
+ if (WARN_ON(resource->data != mc_adev))
+ return;
+
+ fsl_mc_resource_free(resource);
+}
+EXPORT_SYMBOL_GPL(fsl_mc_object_free);
+
+/**
+ * fsl_mc_allocator_probe - callback invoked when an allocatable device is
+ * being added to the system
+ */
+static int fsl_mc_allocator_probe(struct fsl_mc_device *mc_dev)
+{
+ enum fsl_mc_pool_type pool_type;
+ struct fsl_mc_device *mc_bus_dev;
+ struct fsl_mc_bus *mc_bus;
+ int error = -EINVAL;
+
+ if (WARN_ON(!FSL_MC_IS_ALLOCATABLE(mc_dev->obj_desc.type)))
+ goto error;
+
+ mc_bus_dev = to_fsl_mc_device(mc_dev->dev.parent);
+ if (WARN_ON(mc_bus_dev->dev.bus != &fsl_mc_bus_type))
+ goto error;
+
+ mc_bus = to_fsl_mc_bus(mc_bus_dev);
+ error = object_type_to_pool_type(mc_dev->obj_desc.type, &pool_type);
+ if (error < 0)
+ goto error;
+
+ error = fsl_mc_resource_pool_add_device(mc_bus, pool_type, mc_dev);
+ if (error < 0)
+ goto error;
+
+ dev_info(&mc_dev->dev,
+ "Allocatable MC object device bound to fsl_mc_allocator driver");
+ return 0;
+error:
+
+ return error;
+}
+
+/**
+ * fsl_mc_allocator_remove - callback invoked when an allocatable device is
+ * being removed from the system
+ */
+static int fsl_mc_allocator_remove(struct fsl_mc_device *mc_dev)
+{
+ int error = -EINVAL;
+
+ if (WARN_ON(!FSL_MC_IS_ALLOCATABLE(mc_dev->obj_desc.type)))
+ goto out;
+
+ error = fsl_mc_resource_pool_remove_device(mc_dev);
+ if (error < 0)
+ goto out;
+
+ dev_info(&mc_dev->dev,
+ "Allocatable MC object device unbound from fsl_mc_allocator driver");
+ error = 0;
+out:
+ return error;
+}
+
+static const struct fsl_mc_device_match_id match_id_table[] = {
+ {
+ .vendor = FSL_MC_VENDOR_FREESCALE,
+ .obj_type = "dpbp",
+ .ver_major = DPBP_VER_MAJOR,
+ .ver_minor = DPBP_VER_MINOR
+ },
+ {
+ .vendor = FSL_MC_VENDOR_FREESCALE,
+ .obj_type = "dpmcp",
+ .ver_major = DPMCP_VER_MAJOR,
+ .ver_minor = DPMCP_VER_MINOR
+ },
+ {
+ .vendor = FSL_MC_VENDOR_FREESCALE,
+ .obj_type = "dpcon",
+ .ver_major = DPCON_VER_MAJOR,
+ .ver_minor = DPCON_VER_MINOR
+ },
+ {.vendor = 0x0},
+};
+
+static struct fsl_mc_driver fsl_mc_allocator_driver = {
+ .driver = {
+ .name = "fsl_mc_allocator",
+ .owner = THIS_MODULE,
+ .pm = NULL,
+ },
+ .match_id_table = match_id_table,
+ .probe = fsl_mc_allocator_probe,
+ .remove = fsl_mc_allocator_remove,
+};
+
+int __init fsl_mc_allocator_driver_init(void)
+{
+ return fsl_mc_driver_register(&fsl_mc_allocator_driver);
+}
+
+void __exit fsl_mc_allocator_driver_exit(void)
+{
+ fsl_mc_driver_unregister(&fsl_mc_allocator_driver);
+}
diff --git a/drivers/staging/fsl-mc/bus/mc-bus.c b/drivers/staging/fsl-mc/bus/mc-bus.c
new file mode 100644
index 000000000..23512d096
--- /dev/null
+++ b/drivers/staging/fsl-mc/bus/mc-bus.c
@@ -0,0 +1,793 @@
+/*
+ * Freescale Management Complex (MC) bus driver
+ *
+ * Copyright (C) 2014 Freescale Semiconductor, Inc.
+ * Author: German Rivera <German.Rivera@freescale.com>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#include "../include/mc-private.h"
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/of_address.h>
+#include <linux/ioport.h>
+#include <linux/slab.h>
+#include <linux/limits.h>
+#include "../include/dpmng.h"
+#include "../include/mc-sys.h"
+#include "dprc-cmd.h"
+
+static struct kmem_cache *mc_dev_cache;
+
+/**
+ * fsl_mc_bus_match - device to driver matching callback
+ * @dev: the MC object device structure to match against
+ * @drv: the device driver to search for matching MC object device id
+ * structures
+ *
+ * Returns 1 on success, 0 otherwise.
+ */
+static int fsl_mc_bus_match(struct device *dev, struct device_driver *drv)
+{
+ const struct fsl_mc_device_match_id *id;
+ struct fsl_mc_device *mc_dev = to_fsl_mc_device(dev);
+ struct fsl_mc_driver *mc_drv = to_fsl_mc_driver(drv);
+ bool found = false;
+ bool major_version_mismatch = false;
+ bool minor_version_mismatch = false;
+
+ if (WARN_ON(!fsl_mc_bus_type.dev_root))
+ goto out;
+
+ if (!mc_drv->match_id_table)
+ goto out;
+
+ /*
+ * If the object is not 'plugged' don't match.
+ * Only exception is the root DPRC, which is a special case.
+ */
+ if ((mc_dev->obj_desc.state & DPRC_OBJ_STATE_PLUGGED) == 0 &&
+ &mc_dev->dev != fsl_mc_bus_type.dev_root)
+ goto out;
+
+ /*
+ * Traverse the match_id table of the given driver, trying to find
+ * a matching for the given MC object device.
+ */
+ for (id = mc_drv->match_id_table; id->vendor != 0x0; id++) {
+ if (id->vendor == mc_dev->obj_desc.vendor &&
+ strcmp(id->obj_type, mc_dev->obj_desc.type) == 0) {
+ if (id->ver_major == mc_dev->obj_desc.ver_major) {
+ found = true;
+ if (id->ver_minor != mc_dev->obj_desc.ver_minor)
+ minor_version_mismatch = true;
+ } else {
+ major_version_mismatch = true;
+ }
+
+ break;
+ }
+ }
+
+ if (major_version_mismatch) {
+ dev_warn(dev,
+ "Major version mismatch: driver version %u.%u, MC object version %u.%u\n",
+ id->ver_major, id->ver_minor,
+ mc_dev->obj_desc.ver_major,
+ mc_dev->obj_desc.ver_minor);
+ } else if (minor_version_mismatch) {
+ dev_warn(dev,
+ "Minor version mismatch: driver version %u.%u, MC object version %u.%u\n",
+ id->ver_major, id->ver_minor,
+ mc_dev->obj_desc.ver_major,
+ mc_dev->obj_desc.ver_minor);
+ }
+
+out:
+ dev_dbg(dev, "%smatched\n", found ? "" : "not ");
+ return found;
+}
+
+/**
+ * fsl_mc_bus_uevent - callback invoked when a device is added
+ */
+static int fsl_mc_bus_uevent(struct device *dev, struct kobj_uevent_env *env)
+{
+ pr_debug("%s invoked\n", __func__);
+ return 0;
+}
+
+struct bus_type fsl_mc_bus_type = {
+ .name = "fsl-mc",
+ .match = fsl_mc_bus_match,
+ .uevent = fsl_mc_bus_uevent,
+};
+EXPORT_SYMBOL_GPL(fsl_mc_bus_type);
+
+static int fsl_mc_driver_probe(struct device *dev)
+{
+ struct fsl_mc_driver *mc_drv;
+ struct fsl_mc_device *mc_dev = to_fsl_mc_device(dev);
+ int error;
+
+ if (WARN_ON(!dev->driver))
+ return -EINVAL;
+
+ mc_drv = to_fsl_mc_driver(dev->driver);
+ if (WARN_ON(!mc_drv->probe))
+ return -EINVAL;
+
+ error = mc_drv->probe(mc_dev);
+ if (error < 0) {
+ dev_err(dev, "MC object device probe callback failed: %d\n",
+ error);
+ return error;
+ }
+
+ return 0;
+}
+
+static int fsl_mc_driver_remove(struct device *dev)
+{
+ struct fsl_mc_driver *mc_drv = to_fsl_mc_driver(dev->driver);
+ struct fsl_mc_device *mc_dev = to_fsl_mc_device(dev);
+ int error;
+
+ if (WARN_ON(!dev->driver))
+ return -EINVAL;
+
+ error = mc_drv->remove(mc_dev);
+ if (error < 0) {
+ dev_err(dev,
+ "MC object device remove callback failed: %d\n",
+ error);
+ return error;
+ }
+
+ return 0;
+}
+
+static void fsl_mc_driver_shutdown(struct device *dev)
+{
+ struct fsl_mc_driver *mc_drv = to_fsl_mc_driver(dev->driver);
+ struct fsl_mc_device *mc_dev = to_fsl_mc_device(dev);
+
+ mc_drv->shutdown(mc_dev);
+}
+
+/**
+ * __fsl_mc_driver_register - registers a child device driver with the
+ * MC bus
+ *
+ * This function is implicitly invoked from the registration function of
+ * fsl_mc device drivers, which is generated by the
+ * module_fsl_mc_driver() macro.
+ */
+int __fsl_mc_driver_register(struct fsl_mc_driver *mc_driver,
+ struct module *owner)
+{
+ int error;
+
+ mc_driver->driver.owner = owner;
+ mc_driver->driver.bus = &fsl_mc_bus_type;
+
+ if (mc_driver->probe)
+ mc_driver->driver.probe = fsl_mc_driver_probe;
+
+ if (mc_driver->remove)
+ mc_driver->driver.remove = fsl_mc_driver_remove;
+
+ if (mc_driver->shutdown)
+ mc_driver->driver.shutdown = fsl_mc_driver_shutdown;
+
+ error = driver_register(&mc_driver->driver);
+ if (error < 0) {
+ pr_err("driver_register() failed for %s: %d\n",
+ mc_driver->driver.name, error);
+ return error;
+ }
+
+ pr_info("MC object device driver %s registered\n",
+ mc_driver->driver.name);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(__fsl_mc_driver_register);
+
+/**
+ * fsl_mc_driver_unregister - unregisters a device driver from the
+ * MC bus
+ */
+void fsl_mc_driver_unregister(struct fsl_mc_driver *mc_driver)
+{
+ driver_unregister(&mc_driver->driver);
+}
+EXPORT_SYMBOL_GPL(fsl_mc_driver_unregister);
+
+static int get_dprc_icid(struct fsl_mc_io *mc_io,
+ int container_id, uint16_t *icid)
+{
+ uint16_t dprc_handle;
+ struct dprc_attributes attr;
+ int error;
+
+ error = dprc_open(mc_io, container_id, &dprc_handle);
+ if (error < 0) {
+ pr_err("dprc_open() failed: %d\n", error);
+ return error;
+ }
+
+ memset(&attr, 0, sizeof(attr));
+ error = dprc_get_attributes(mc_io, dprc_handle, &attr);
+ if (error < 0) {
+ pr_err("dprc_get_attributes() failed: %d\n", error);
+ goto common_cleanup;
+ }
+
+ *icid = attr.icid;
+ error = 0;
+
+common_cleanup:
+ (void)dprc_close(mc_io, dprc_handle);
+ return error;
+}
+
+static int translate_mc_addr(uint64_t mc_addr, phys_addr_t *phys_addr)
+{
+ int i;
+ struct fsl_mc *mc = dev_get_drvdata(fsl_mc_bus_type.dev_root->parent);
+
+ if (mc->num_translation_ranges == 0) {
+ /*
+ * Do identity mapping:
+ */
+ *phys_addr = mc_addr;
+ return 0;
+ }
+
+ for (i = 0; i < mc->num_translation_ranges; i++) {
+ struct fsl_mc_addr_translation_range *range =
+ &mc->translation_ranges[i];
+
+ if (mc_addr >= range->start_mc_addr &&
+ mc_addr < range->end_mc_addr) {
+ *phys_addr = range->start_phys_addr +
+ (mc_addr - range->start_mc_addr);
+ return 0;
+ }
+ }
+
+ return -EFAULT;
+}
+
+static int fsl_mc_device_get_mmio_regions(struct fsl_mc_device *mc_dev,
+ struct fsl_mc_device *mc_bus_dev)
+{
+ int i;
+ int error;
+ struct resource *regions;
+ struct dprc_obj_desc *obj_desc = &mc_dev->obj_desc;
+ struct device *parent_dev = mc_dev->dev.parent;
+
+ regions = kmalloc_array(obj_desc->region_count,
+ sizeof(regions[0]), GFP_KERNEL);
+ if (!regions)
+ return -ENOMEM;
+
+ for (i = 0; i < obj_desc->region_count; i++) {
+ struct dprc_region_desc region_desc;
+
+ error = dprc_get_obj_region(mc_bus_dev->mc_io,
+ mc_bus_dev->mc_handle,
+ obj_desc->type,
+ obj_desc->id, i, &region_desc);
+ if (error < 0) {
+ dev_err(parent_dev,
+ "dprc_get_obj_region() failed: %d\n", error);
+ goto error_cleanup_regions;
+ }
+
+ WARN_ON(region_desc.base_paddr == 0x0);
+ WARN_ON(region_desc.size == 0);
+ error = translate_mc_addr(region_desc.base_paddr,
+ &regions[i].start);
+ if (error < 0) {
+ dev_err(parent_dev,
+ "Invalid MC address: %#llx\n",
+ region_desc.base_paddr);
+ goto error_cleanup_regions;
+ }
+
+ regions[i].end = regions[i].start + region_desc.size - 1;
+ regions[i].name = "fsl-mc object MMIO region";
+ regions[i].flags = IORESOURCE_IO;
+ }
+
+ mc_dev->regions = regions;
+ return 0;
+
+error_cleanup_regions:
+ kfree(regions);
+ return error;
+}
+
+/**
+ * Add a newly discovered MC object device to be visible in Linux
+ */
+int fsl_mc_device_add(struct dprc_obj_desc *obj_desc,
+ struct fsl_mc_io *mc_io,
+ struct device *parent_dev,
+ struct fsl_mc_device **new_mc_dev)
+{
+ int error;
+ struct fsl_mc_device *mc_dev = NULL;
+ struct fsl_mc_bus *mc_bus = NULL;
+ struct fsl_mc_device *parent_mc_dev;
+
+ if (parent_dev->bus == &fsl_mc_bus_type)
+ parent_mc_dev = to_fsl_mc_device(parent_dev);
+ else
+ parent_mc_dev = NULL;
+
+ if (strcmp(obj_desc->type, "dprc") == 0) {
+ /*
+ * Allocate an MC bus device object:
+ */
+ mc_bus = devm_kzalloc(parent_dev, sizeof(*mc_bus), GFP_KERNEL);
+ if (!mc_bus)
+ return -ENOMEM;
+
+ mc_dev = &mc_bus->mc_dev;
+ } else {
+ /*
+ * Allocate a regular fsl_mc_device object:
+ */
+ mc_dev = kmem_cache_zalloc(mc_dev_cache, GFP_KERNEL);
+ if (!mc_dev)
+ return -ENOMEM;
+ }
+
+ mc_dev->obj_desc = *obj_desc;
+ mc_dev->mc_io = mc_io;
+ device_initialize(&mc_dev->dev);
+ mc_dev->dev.parent = parent_dev;
+ mc_dev->dev.bus = &fsl_mc_bus_type;
+ dev_set_name(&mc_dev->dev, "%s.%d", obj_desc->type, obj_desc->id);
+
+ if (strcmp(obj_desc->type, "dprc") == 0) {
+ struct fsl_mc_io *mc_io2;
+
+ mc_dev->flags |= FSL_MC_IS_DPRC;
+
+ /*
+ * To get the DPRC's ICID, we need to open the DPRC
+ * in get_dprc_icid(). For child DPRCs, we do so using the
+ * parent DPRC's MC portal instead of the child DPRC's MC
+ * portal, in case the child DPRC is already opened with
+ * its own portal (e.g., the DPRC used by AIOP).
+ *
+ * NOTE: There cannot be more than one active open for a
+ * given MC object, using the same MC portal.
+ */
+ if (parent_mc_dev) {
+ /*
+ * device being added is a child DPRC device
+ */
+ mc_io2 = parent_mc_dev->mc_io;
+ } else {
+ /*
+ * device being added is the root DPRC device
+ */
+ if (WARN_ON(!mc_io)) {
+ error = -EINVAL;
+ goto error_cleanup_dev;
+ }
+
+ mc_io2 = mc_io;
+
+ if (!fsl_mc_bus_type.dev_root)
+ fsl_mc_bus_type.dev_root = &mc_dev->dev;
+ }
+
+ error = get_dprc_icid(mc_io2, obj_desc->id, &mc_dev->icid);
+ if (error < 0)
+ goto error_cleanup_dev;
+ } else {
+ /*
+ * A non-DPRC MC object device has to be a child of another
+ * MC object (specifically a DPRC object)
+ */
+ mc_dev->icid = parent_mc_dev->icid;
+ mc_dev->dma_mask = FSL_MC_DEFAULT_DMA_MASK;
+ mc_dev->dev.dma_mask = &mc_dev->dma_mask;
+ }
+
+ /*
+ * Get MMIO regions for the device from the MC:
+ *
+ * NOTE: the root DPRC is a special case as its MMIO region is
+ * obtained from the device tree
+ */
+ if (parent_mc_dev && obj_desc->region_count != 0) {
+ error = fsl_mc_device_get_mmio_regions(mc_dev,
+ parent_mc_dev);
+ if (error < 0)
+ goto error_cleanup_dev;
+ }
+
+ /*
+ * The device-specific probe callback will get invoked by device_add()
+ */
+ error = device_add(&mc_dev->dev);
+ if (error < 0) {
+ dev_err(parent_dev,
+ "device_add() failed for device %s: %d\n",
+ dev_name(&mc_dev->dev), error);
+ goto error_cleanup_dev;
+ }
+
+ (void)get_device(&mc_dev->dev);
+ dev_dbg(parent_dev, "Added MC object device %s\n",
+ dev_name(&mc_dev->dev));
+
+ *new_mc_dev = mc_dev;
+ return 0;
+
+error_cleanup_dev:
+ kfree(mc_dev->regions);
+ if (mc_bus)
+ devm_kfree(parent_dev, mc_bus);
+ else
+ kmem_cache_free(mc_dev_cache, mc_dev);
+
+ return error;
+}
+EXPORT_SYMBOL_GPL(fsl_mc_device_add);
+
+/**
+ * fsl_mc_device_remove - Remove a MC object device from being visible to
+ * Linux
+ *
+ * @mc_dev: Pointer to a MC object device object
+ */
+void fsl_mc_device_remove(struct fsl_mc_device *mc_dev)
+{
+ struct fsl_mc_bus *mc_bus = NULL;
+
+ kfree(mc_dev->regions);
+
+ /*
+ * The device-specific remove callback will get invoked by device_del()
+ */
+ device_del(&mc_dev->dev);
+ put_device(&mc_dev->dev);
+
+ if (strcmp(mc_dev->obj_desc.type, "dprc") == 0) {
+ mc_bus = to_fsl_mc_bus(mc_dev);
+ if (mc_dev->mc_io) {
+ fsl_destroy_mc_io(mc_dev->mc_io);
+ mc_dev->mc_io = NULL;
+ }
+
+ if (&mc_dev->dev == fsl_mc_bus_type.dev_root)
+ fsl_mc_bus_type.dev_root = NULL;
+ }
+
+ if (mc_bus)
+ devm_kfree(mc_dev->dev.parent, mc_bus);
+ else
+ kmem_cache_free(mc_dev_cache, mc_dev);
+}
+EXPORT_SYMBOL_GPL(fsl_mc_device_remove);
+
+static int parse_mc_ranges(struct device *dev,
+ int *paddr_cells,
+ int *mc_addr_cells,
+ int *mc_size_cells,
+ const __be32 **ranges_start,
+ uint8_t *num_ranges)
+{
+ const __be32 *prop;
+ int range_tuple_cell_count;
+ int ranges_len;
+ int tuple_len;
+ struct device_node *mc_node = dev->of_node;
+
+ *ranges_start = of_get_property(mc_node, "ranges", &ranges_len);
+ if (!(*ranges_start) || !ranges_len) {
+ dev_warn(dev,
+ "missing or empty ranges property for device tree node '%s'\n",
+ mc_node->name);
+
+ *num_ranges = 0;
+ return 0;
+ }
+
+ *paddr_cells = of_n_addr_cells(mc_node);
+
+ prop = of_get_property(mc_node, "#address-cells", NULL);
+ if (prop)
+ *mc_addr_cells = be32_to_cpup(prop);
+ else
+ *mc_addr_cells = *paddr_cells;
+
+ prop = of_get_property(mc_node, "#size-cells", NULL);
+ if (prop)
+ *mc_size_cells = be32_to_cpup(prop);
+ else
+ *mc_size_cells = of_n_size_cells(mc_node);
+
+ range_tuple_cell_count = *paddr_cells + *mc_addr_cells +
+ *mc_size_cells;
+
+ tuple_len = range_tuple_cell_count * sizeof(__be32);
+ if (ranges_len % tuple_len != 0) {
+ dev_err(dev, "malformed ranges property '%s'\n", mc_node->name);
+ return -EINVAL;
+ }
+
+ *num_ranges = ranges_len / tuple_len;
+ return 0;
+}
+
+static int get_mc_addr_translation_ranges(struct device *dev,
+ struct fsl_mc_addr_translation_range
+ **ranges,
+ uint8_t *num_ranges)
+{
+ int error;
+ int paddr_cells;
+ int mc_addr_cells;
+ int mc_size_cells;
+ int i;
+ const __be32 *ranges_start;
+ const __be32 *cell;
+
+ error = parse_mc_ranges(dev,
+ &paddr_cells,
+ &mc_addr_cells,
+ &mc_size_cells,
+ &ranges_start,
+ num_ranges);
+ if (error < 0)
+ return error;
+
+ if (!(*num_ranges)) {
+ /*
+ * Missing or empty ranges property ("ranges;") for the
+ * 'fsl,qoriq-mc' node. In this case, identity mapping
+ * will be used.
+ */
+ *ranges = NULL;
+ return 0;
+ }
+
+ *ranges = devm_kcalloc(dev, *num_ranges,
+ sizeof(struct fsl_mc_addr_translation_range),
+ GFP_KERNEL);
+ if (!(*ranges))
+ return -ENOMEM;
+
+ cell = ranges_start;
+ for (i = 0; i < *num_ranges; ++i) {
+ struct fsl_mc_addr_translation_range *range = &(*ranges)[i];
+
+ range->start_mc_addr = of_read_number(cell, mc_addr_cells);
+ cell += mc_addr_cells;
+ range->start_phys_addr = of_read_number(cell, paddr_cells);
+ cell += paddr_cells;
+ range->end_mc_addr = range->start_mc_addr +
+ of_read_number(cell, mc_size_cells);
+
+ cell += mc_size_cells;
+ }
+
+ return 0;
+}
+
+/**
+ * fsl_mc_bus_probe - callback invoked when the root MC bus is being
+ * added
+ */
+static int fsl_mc_bus_probe(struct platform_device *pdev)
+{
+ struct dprc_obj_desc obj_desc;
+ int error;
+ struct fsl_mc *mc;
+ struct fsl_mc_device *mc_bus_dev = NULL;
+ struct fsl_mc_io *mc_io = NULL;
+ int container_id;
+ phys_addr_t mc_portal_phys_addr;
+ uint32_t mc_portal_size;
+ struct mc_version mc_version;
+ struct resource res;
+
+ dev_info(&pdev->dev, "Root MC bus device probed");
+
+ mc = devm_kzalloc(&pdev->dev, sizeof(*mc), GFP_KERNEL);
+ if (!mc)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, mc);
+
+ /*
+ * Get physical address of MC portal for the root DPRC:
+ */
+ error = of_address_to_resource(pdev->dev.of_node, 0, &res);
+ if (error < 0) {
+ dev_err(&pdev->dev,
+ "of_address_to_resource() failed for %s\n",
+ pdev->dev.of_node->full_name);
+ return error;
+ }
+
+ mc_portal_phys_addr = res.start;
+ mc_portal_size = resource_size(&res);
+ error = fsl_create_mc_io(&pdev->dev, mc_portal_phys_addr,
+ mc_portal_size, NULL, 0, &mc_io);
+ if (error < 0)
+ return error;
+
+ error = mc_get_version(mc_io, &mc_version);
+ if (error != 0) {
+ dev_err(&pdev->dev,
+ "mc_get_version() failed with error %d\n", error);
+ goto error_cleanup_mc_io;
+ }
+
+ dev_info(&pdev->dev,
+ "Freescale Management Complex Firmware version: %u.%u.%u\n",
+ mc_version.major, mc_version.minor, mc_version.revision);
+
+ if (mc_version.major < MC_VER_MAJOR) {
+ dev_err(&pdev->dev,
+ "ERROR: MC firmware version not supported by driver (driver version: %u.%u)\n",
+ MC_VER_MAJOR, MC_VER_MINOR);
+ error = -ENOTSUPP;
+ goto error_cleanup_mc_io;
+ }
+
+ if (mc_version.major > MC_VER_MAJOR) {
+ dev_warn(&pdev->dev,
+ "WARNING: driver may not support newer MC firmware features (driver version: %u.%u)\n",
+ MC_VER_MAJOR, MC_VER_MINOR);
+ }
+
+ error = get_mc_addr_translation_ranges(&pdev->dev,
+ &mc->translation_ranges,
+ &mc->num_translation_ranges);
+ if (error < 0)
+ goto error_cleanup_mc_io;
+
+ error = dpmng_get_container_id(mc_io, &container_id);
+ if (error < 0) {
+ dev_err(&pdev->dev,
+ "dpmng_get_container_id() failed: %d\n", error);
+ goto error_cleanup_mc_io;
+ }
+
+ obj_desc.vendor = FSL_MC_VENDOR_FREESCALE;
+ strcpy(obj_desc.type, "dprc");
+ obj_desc.id = container_id;
+ obj_desc.ver_major = DPRC_VER_MAJOR;
+ obj_desc.ver_minor = DPRC_VER_MINOR;
+ obj_desc.region_count = 0;
+
+ error = fsl_mc_device_add(&obj_desc, mc_io, &pdev->dev, &mc_bus_dev);
+ if (error < 0)
+ goto error_cleanup_mc_io;
+
+ mc->root_mc_bus_dev = mc_bus_dev;
+ return 0;
+
+error_cleanup_mc_io:
+ fsl_destroy_mc_io(mc_io);
+ return error;
+}
+
+/**
+ * fsl_mc_bus_remove - callback invoked when the root MC bus is being
+ * removed
+ */
+static int fsl_mc_bus_remove(struct platform_device *pdev)
+{
+ struct fsl_mc *mc = platform_get_drvdata(pdev);
+
+ if (WARN_ON(&mc->root_mc_bus_dev->dev != fsl_mc_bus_type.dev_root))
+ return -EINVAL;
+
+ fsl_mc_device_remove(mc->root_mc_bus_dev);
+ dev_info(&pdev->dev, "Root MC bus device removed");
+ return 0;
+}
+
+static const struct of_device_id fsl_mc_bus_match_table[] = {
+ {.compatible = "fsl,qoriq-mc",},
+ {},
+};
+
+MODULE_DEVICE_TABLE(of, fsl_mc_bus_match_table);
+
+static struct platform_driver fsl_mc_bus_driver = {
+ .driver = {
+ .name = "fsl_mc_bus",
+ .owner = THIS_MODULE,
+ .pm = NULL,
+ .of_match_table = fsl_mc_bus_match_table,
+ },
+ .probe = fsl_mc_bus_probe,
+ .remove = fsl_mc_bus_remove,
+};
+
+static int __init fsl_mc_bus_driver_init(void)
+{
+ int error;
+
+ mc_dev_cache = kmem_cache_create("fsl_mc_device",
+ sizeof(struct fsl_mc_device), 0, 0,
+ NULL);
+ if (!mc_dev_cache) {
+ pr_err("Could not create fsl_mc_device cache\n");
+ return -ENOMEM;
+ }
+
+ error = bus_register(&fsl_mc_bus_type);
+ if (error < 0) {
+ pr_err("fsl-mc bus type registration failed: %d\n", error);
+ goto error_cleanup_cache;
+ }
+
+ pr_info("fsl-mc bus type registered\n");
+
+ error = platform_driver_register(&fsl_mc_bus_driver);
+ if (error < 0) {
+ pr_err("platform_driver_register() failed: %d\n", error);
+ goto error_cleanup_bus;
+ }
+
+ error = dprc_driver_init();
+ if (error < 0)
+ goto error_cleanup_driver;
+
+ error = fsl_mc_allocator_driver_init();
+ if (error < 0)
+ goto error_cleanup_dprc_driver;
+
+ return 0;
+
+error_cleanup_dprc_driver:
+ dprc_driver_exit();
+
+error_cleanup_driver:
+ platform_driver_unregister(&fsl_mc_bus_driver);
+
+error_cleanup_bus:
+ bus_unregister(&fsl_mc_bus_type);
+
+error_cleanup_cache:
+ kmem_cache_destroy(mc_dev_cache);
+ return error;
+}
+
+postcore_initcall(fsl_mc_bus_driver_init);
+
+static void __exit fsl_mc_bus_driver_exit(void)
+{
+ if (WARN_ON(!mc_dev_cache))
+ return;
+
+ fsl_mc_allocator_driver_exit();
+ dprc_driver_exit();
+ platform_driver_unregister(&fsl_mc_bus_driver);
+ bus_unregister(&fsl_mc_bus_type);
+ kmem_cache_destroy(mc_dev_cache);
+ pr_info("MC bus unregistered\n");
+}
+
+module_exit(fsl_mc_bus_driver_exit);
+
+MODULE_AUTHOR("Freescale Semiconductor Inc.");
+MODULE_DESCRIPTION("Freescale Management Complex (MC) bus driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/fsl-mc/bus/mc-sys.c b/drivers/staging/fsl-mc/bus/mc-sys.c
new file mode 100644
index 000000000..5737f599f
--- /dev/null
+++ b/drivers/staging/fsl-mc/bus/mc-sys.c
@@ -0,0 +1,287 @@
+/* Copyright 2013-2014 Freescale Semiconductor Inc.
+ *
+ * I/O services to send MC commands to the MC hardware
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of the above-listed copyright holders nor the
+ * names of any contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ *
+ * ALTERNATIVELY, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") as published by the Free Software
+ * Foundation, either version 2 of that License or (at your option) any
+ * later version.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT HOLDERS OR CONTRIBUTORS 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.
+ */
+
+#include "../include/mc-sys.h"
+#include "../include/mc-cmd.h"
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/ioport.h>
+#include <linux/device.h>
+
+/**
+ * Timeout in jiffies to wait for the completion of an MC command
+ */
+#define MC_CMD_COMPLETION_TIMEOUT_JIFFIES (HZ / 2) /* 500 ms */
+
+/*
+ * usleep_range() min and max values used to throttle down polling
+ * iterations while waiting for MC command completion
+ */
+#define MC_CMD_COMPLETION_POLLING_MIN_SLEEP_USECS 10
+#define MC_CMD_COMPLETION_POLLING_MAX_SLEEP_USECS 500
+
+#define MC_CMD_HDR_READ_CMDID(_hdr) \
+ ((uint16_t)mc_dec((_hdr), MC_CMD_HDR_CMDID_O, MC_CMD_HDR_CMDID_S))
+
+/**
+ * Creates an MC I/O object
+ *
+ * @dev: device to be associated with the MC I/O object
+ * @mc_portal_phys_addr: physical address of the MC portal to use
+ * @mc_portal_size: size in bytes of the MC portal
+ * @resource: Pointer to MC bus object allocator resource associated
+ * with this MC I/O object or NULL if none.
+ * @flags: flags for the new MC I/O object
+ * @new_mc_io: Area to return pointer to newly created MC I/O object
+ *
+ * Returns '0' on Success; Error code otherwise.
+ */
+int __must_check fsl_create_mc_io(struct device *dev,
+ phys_addr_t mc_portal_phys_addr,
+ uint32_t mc_portal_size,
+ struct fsl_mc_resource *resource,
+ uint32_t flags, struct fsl_mc_io **new_mc_io)
+{
+ struct fsl_mc_io *mc_io;
+ void __iomem *mc_portal_virt_addr;
+ struct resource *res;
+
+ mc_io = devm_kzalloc(dev, sizeof(*mc_io), GFP_KERNEL);
+ if (!mc_io)
+ return -ENOMEM;
+
+ mc_io->dev = dev;
+ mc_io->flags = flags;
+ mc_io->portal_phys_addr = mc_portal_phys_addr;
+ mc_io->portal_size = mc_portal_size;
+ mc_io->resource = resource;
+ res = devm_request_mem_region(dev,
+ mc_portal_phys_addr,
+ mc_portal_size,
+ "mc_portal");
+ if (!res) {
+ dev_err(dev,
+ "devm_request_mem_region failed for MC portal %#llx\n",
+ mc_portal_phys_addr);
+ return -EBUSY;
+ }
+
+ mc_portal_virt_addr = devm_ioremap_nocache(dev,
+ mc_portal_phys_addr,
+ mc_portal_size);
+ if (!mc_portal_virt_addr) {
+ dev_err(dev,
+ "devm_ioremap_nocache failed for MC portal %#llx\n",
+ mc_portal_phys_addr);
+ return -ENXIO;
+ }
+
+ mc_io->portal_virt_addr = mc_portal_virt_addr;
+ *new_mc_io = mc_io;
+ return 0;
+}
+EXPORT_SYMBOL_GPL(fsl_create_mc_io);
+
+/**
+ * Destroys an MC I/O object
+ *
+ * @mc_io: MC I/O object to destroy
+ */
+void fsl_destroy_mc_io(struct fsl_mc_io *mc_io)
+{
+ devm_iounmap(mc_io->dev, mc_io->portal_virt_addr);
+ devm_release_mem_region(mc_io->dev,
+ mc_io->portal_phys_addr,
+ mc_io->portal_size);
+
+ mc_io->portal_virt_addr = NULL;
+ devm_kfree(mc_io->dev, mc_io);
+}
+EXPORT_SYMBOL_GPL(fsl_destroy_mc_io);
+
+static int mc_status_to_error(enum mc_cmd_status status)
+{
+ static const int mc_status_to_error_map[] = {
+ [MC_CMD_STATUS_OK] = 0,
+ [MC_CMD_STATUS_AUTH_ERR] = -EACCES,
+ [MC_CMD_STATUS_NO_PRIVILEGE] = -EPERM,
+ [MC_CMD_STATUS_DMA_ERR] = -EIO,
+ [MC_CMD_STATUS_CONFIG_ERR] = -ENXIO,
+ [MC_CMD_STATUS_TIMEOUT] = -ETIMEDOUT,
+ [MC_CMD_STATUS_NO_RESOURCE] = -ENAVAIL,
+ [MC_CMD_STATUS_NO_MEMORY] = -ENOMEM,
+ [MC_CMD_STATUS_BUSY] = -EBUSY,
+ [MC_CMD_STATUS_UNSUPPORTED_OP] = -ENOTSUPP,
+ [MC_CMD_STATUS_INVALID_STATE] = -ENODEV,
+ };
+
+ if (WARN_ON((u32)status >= ARRAY_SIZE(mc_status_to_error_map)))
+ return -EINVAL;
+
+ return mc_status_to_error_map[status];
+}
+
+static const char *mc_status_to_string(enum mc_cmd_status status)
+{
+ static const char *const status_strings[] = {
+ [MC_CMD_STATUS_OK] = "Command completed successfully",
+ [MC_CMD_STATUS_READY] = "Command ready to be processed",
+ [MC_CMD_STATUS_AUTH_ERR] = "Authentication error",
+ [MC_CMD_STATUS_NO_PRIVILEGE] = "No privilege",
+ [MC_CMD_STATUS_DMA_ERR] = "DMA or I/O error",
+ [MC_CMD_STATUS_CONFIG_ERR] = "Configuration error",
+ [MC_CMD_STATUS_TIMEOUT] = "Operation timed out",
+ [MC_CMD_STATUS_NO_RESOURCE] = "No resources",
+ [MC_CMD_STATUS_NO_MEMORY] = "No memory available",
+ [MC_CMD_STATUS_BUSY] = "Device is busy",
+ [MC_CMD_STATUS_UNSUPPORTED_OP] = "Unsupported operation",
+ [MC_CMD_STATUS_INVALID_STATE] = "Invalid state"
+ };
+
+ if ((unsigned int)status >= ARRAY_SIZE(status_strings))
+ return "Unknown MC error";
+
+ return status_strings[status];
+}
+
+/**
+ * mc_write_command - writes a command to a Management Complex (MC) portal
+ *
+ * @portal: pointer to an MC portal
+ * @cmd: pointer to a filled command
+ */
+static inline void mc_write_command(struct mc_command __iomem *portal,
+ struct mc_command *cmd)
+{
+ int i;
+
+ /* copy command parameters into the portal */
+ for (i = 0; i < MC_CMD_NUM_OF_PARAMS; i++)
+ writeq(cmd->params[i], &portal->params[i]);
+
+ /* submit the command by writing the header */
+ writeq(cmd->header, &portal->header);
+}
+
+/**
+ * mc_read_response - reads the response for the last MC command from a
+ * Management Complex (MC) portal
+ *
+ * @portal: pointer to an MC portal
+ * @resp: pointer to command response buffer
+ *
+ * Returns MC_CMD_STATUS_OK on Success; Error code otherwise.
+ */
+static inline enum mc_cmd_status mc_read_response(struct mc_command __iomem *
+ portal,
+ struct mc_command *resp)
+{
+ int i;
+ enum mc_cmd_status status;
+
+ /* Copy command response header from MC portal: */
+ resp->header = readq(&portal->header);
+ status = MC_CMD_HDR_READ_STATUS(resp->header);
+ if (status != MC_CMD_STATUS_OK)
+ return status;
+
+ /* Copy command response data from MC portal: */
+ for (i = 0; i < MC_CMD_NUM_OF_PARAMS; i++)
+ resp->params[i] = readq(&portal->params[i]);
+
+ return status;
+}
+
+/**
+ * Sends an command to the MC device using the given MC I/O object
+ *
+ * @mc_io: MC I/O object to be used
+ * @cmd: command to be sent
+ *
+ * Returns '0' on Success; Error code otherwise.
+ *
+ * NOTE: This function cannot be invoked from from atomic contexts.
+ */
+int mc_send_command(struct fsl_mc_io *mc_io, struct mc_command *cmd)
+{
+ enum mc_cmd_status status;
+ unsigned long jiffies_until_timeout =
+ jiffies + MC_CMD_COMPLETION_TIMEOUT_JIFFIES;
+
+ /*
+ * Send command to the MC hardware:
+ */
+ mc_write_command(mc_io->portal_virt_addr, cmd);
+
+ /*
+ * Wait for response from the MC hardware:
+ */
+ for (;;) {
+ status = mc_read_response(mc_io->portal_virt_addr, cmd);
+ if (status != MC_CMD_STATUS_READY)
+ break;
+
+ /*
+ * TODO: When MC command completion interrupts are supported
+ * call wait function here instead of usleep_range()
+ */
+ usleep_range(MC_CMD_COMPLETION_POLLING_MIN_SLEEP_USECS,
+ MC_CMD_COMPLETION_POLLING_MAX_SLEEP_USECS);
+
+ if (time_after_eq(jiffies, jiffies_until_timeout)) {
+ pr_debug("MC command timed out (portal: %#llx, obj handle: %#x, command: %#x)\n",
+ mc_io->portal_phys_addr,
+ (unsigned int)
+ MC_CMD_HDR_READ_TOKEN(cmd->header),
+ (unsigned int)
+ MC_CMD_HDR_READ_CMDID(cmd->header));
+
+ return -ETIMEDOUT;
+ }
+ }
+
+ if (status != MC_CMD_STATUS_OK) {
+ pr_debug("MC command failed: portal: %#llx, obj handle: %#x, command: %#x, status: %s (%#x)\n",
+ mc_io->portal_phys_addr,
+ (unsigned int)MC_CMD_HDR_READ_TOKEN(cmd->header),
+ (unsigned int)MC_CMD_HDR_READ_CMDID(cmd->header),
+ mc_status_to_string(status),
+ (unsigned int)status);
+
+ return mc_status_to_error(status);
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL(mc_send_command);
diff --git a/drivers/staging/fsl-mc/include/dpbp-cmd.h b/drivers/staging/fsl-mc/include/dpbp-cmd.h
new file mode 100644
index 000000000..1fd70a215
--- /dev/null
+++ b/drivers/staging/fsl-mc/include/dpbp-cmd.h
@@ -0,0 +1,60 @@
+/* Copyright 2013-2014 Freescale Semiconductor Inc.
+*
+* Redistribution and use in source and binary forms, with or without
+* modification, are permitted provided that the following conditions are met:
+* * Redistributions of source code must retain the above copyright
+* notice, this list of conditions and the following disclaimer.
+* * Redistributions in binary form must reproduce the above copyright
+* notice, this list of conditions and the following disclaimer in the
+* documentation and/or other materials provided with the distribution.
+* * Neither the name of the above-listed copyright holders nor the
+* names of any contributors may be used to endorse or promote products
+* derived from this software without specific prior written permission.
+*
+*
+* ALTERNATIVELY, this software may be distributed under the terms of the
+* GNU General Public License ("GPL") as published by the Free Software
+* Foundation, either version 2 of that License or (at your option) any
+* later version.
+*
+* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT HOLDERS OR CONTRIBUTORS 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.
+*/
+#ifndef _FSL_DPBP_CMD_H
+#define _FSL_DPBP_CMD_H
+
+/* DPBP Version */
+#define DPBP_VER_MAJOR 2
+#define DPBP_VER_MINOR 0
+
+/* Command IDs */
+#define DPBP_CMDID_CLOSE 0x800
+#define DPBP_CMDID_OPEN 0x804
+#define DPBP_CMDID_CREATE 0x904
+#define DPBP_CMDID_DESTROY 0x900
+
+#define DPBP_CMDID_ENABLE 0x002
+#define DPBP_CMDID_DISABLE 0x003
+#define DPBP_CMDID_GET_ATTR 0x004
+#define DPBP_CMDID_RESET 0x005
+#define DPBP_CMDID_IS_ENABLED 0x006
+
+#define DPBP_CMDID_SET_IRQ 0x010
+#define DPBP_CMDID_GET_IRQ 0x011
+#define DPBP_CMDID_SET_IRQ_ENABLE 0x012
+#define DPBP_CMDID_GET_IRQ_ENABLE 0x013
+#define DPBP_CMDID_SET_IRQ_MASK 0x014
+#define DPBP_CMDID_GET_IRQ_MASK 0x015
+#define DPBP_CMDID_GET_IRQ_STATUS 0x016
+#define DPBP_CMDID_CLEAR_IRQ_STATUS 0x017
+
+#endif /* _FSL_DPBP_CMD_H */
diff --git a/drivers/staging/fsl-mc/include/dpbp.h b/drivers/staging/fsl-mc/include/dpbp.h
new file mode 100644
index 000000000..5f3c8e74d
--- /dev/null
+++ b/drivers/staging/fsl-mc/include/dpbp.h
@@ -0,0 +1,330 @@
+/* Copyright 2013-2015 Freescale Semiconductor Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of the above-listed copyright holders nor the
+ * names of any contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ *
+ * ALTERNATIVELY, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") as published by the Free Software
+ * Foundation, either version 2 of that License or (at your option) any
+ * later version.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT HOLDERS OR CONTRIBUTORS 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.
+ */
+#ifndef __FSL_DPBP_H
+#define __FSL_DPBP_H
+
+/* Data Path Buffer Pool API
+ * Contains initialization APIs and runtime control APIs for DPBP
+ */
+
+struct fsl_mc_io;
+
+/**
+ * dpbp_open() - Open a control session for the specified object.
+ * @mc_io: Pointer to MC portal's I/O object
+ * @dpbp_id: DPBP unique ID
+ * @token: Returned token; use in subsequent API calls
+ *
+ * This function can be used to open a control session for an
+ * already created object; an object may have been declared in
+ * the DPL or by calling the dpbp_create function.
+ * This function returns a unique authentication token,
+ * associated with the specific object ID and the specific MC
+ * portal; this token must be used in all subsequent commands for
+ * this specific object
+ *
+ * Return: '0' on Success; Error code otherwise.
+ */
+int dpbp_open(struct fsl_mc_io *mc_io, int dpbp_id, uint16_t *token);
+
+/**
+ * dpbp_close() - Close the control session of the object
+ * @mc_io: Pointer to MC portal's I/O object
+ * @token: Token of DPBP object
+ *
+ * After this function is called, no further operations are
+ * allowed on the object without opening a new control session.
+ *
+ * Return: '0' on Success; Error code otherwise.
+ */
+int dpbp_close(struct fsl_mc_io *mc_io, uint16_t token);
+
+/**
+ * struct dpbp_cfg() - Structure representing DPBP configuration
+ * @options: place holder
+ */
+struct dpbp_cfg {
+ uint32_t options;
+};
+
+/**
+ * dpbp_create() - Create the DPBP object.
+ * @mc_io: Pointer to MC portal's I/O object
+ * @cfg: Configuration structure
+ * @token: Returned token; use in subsequent API calls
+ *
+ * Create the DPBP object, allocate required resources and
+ * perform required initialization.
+ *
+ * The object can be created either by declaring it in the
+ * DPL file, or by calling this function.
+ * This function returns a unique authentication token,
+ * associated with the specific object ID and the specific MC
+ * portal; this token must be used in all subsequent calls to
+ * this specific object. For objects that are created using the
+ * DPL file, call dpbp_open function to get an authentication
+ * token first.
+ *
+ * Return: '0' on Success; Error code otherwise.
+ */
+int dpbp_create(struct fsl_mc_io *mc_io,
+ const struct dpbp_cfg *cfg,
+ uint16_t *token);
+
+/**
+ * dpbp_destroy() - Destroy the DPBP object and release all its resources.
+ * @mc_io: Pointer to MC portal's I/O object
+ * @token: Token of DPBP object
+ *
+ * Return: '0' on Success; error code otherwise.
+ */
+int dpbp_destroy(struct fsl_mc_io *mc_io, uint16_t token);
+
+/**
+ * dpbp_enable() - Enable the DPBP.
+ * @mc_io: Pointer to MC portal's I/O object
+ * @token: Token of DPBP object
+ *
+ * Return: '0' on Success; Error code otherwise.
+ */
+int dpbp_enable(struct fsl_mc_io *mc_io, uint16_t token);
+
+/**
+ * dpbp_disable() - Disable the DPBP.
+ * @mc_io: Pointer to MC portal's I/O object
+ * @token: Token of DPBP object
+ *
+ * Return: '0' on Success; Error code otherwise.
+ */
+int dpbp_disable(struct fsl_mc_io *mc_io, uint16_t token);
+
+/**
+ * dpbp_is_enabled() - Check if the DPBP is enabled.
+ * @mc_io: Pointer to MC portal's I/O object
+ * @token: Token of DPBP object
+ * @en: Returns '1' if object is enabled; '0' otherwise
+ *
+ * Return: '0' on Success; Error code otherwise.
+ */
+int dpbp_is_enabled(struct fsl_mc_io *mc_io, uint16_t token, int *en);
+
+/**
+ * dpbp_reset() - Reset the DPBP, returns the object to initial state.
+ * @mc_io: Pointer to MC portal's I/O object
+ * @token: Token of DPBP object
+ *
+ * Return: '0' on Success; Error code otherwise.
+ */
+int dpbp_reset(struct fsl_mc_io *mc_io, uint16_t token);
+
+/**
+ * dpbp_set_irq() - Set IRQ information for the DPBP to trigger an interrupt.
+ * @mc_io: Pointer to MC portal's I/O object
+ * @token: Token of DPBP object
+ * @irq_index: Identifies the interrupt index to configure
+ * @irq_addr: Address that must be written to
+ * signal a message-based interrupt
+ * @irq_val: Value to write into irq_addr address
+ * @user_irq_id: A user defined number associated with this IRQ
+ *
+ * Return: '0' on Success; Error code otherwise.
+ */
+int dpbp_set_irq(struct fsl_mc_io *mc_io,
+ uint16_t token,
+ uint8_t irq_index,
+ uint64_t irq_addr,
+ uint32_t irq_val,
+ int user_irq_id);
+
+/**
+ * dpbp_get_irq() - Get IRQ information from the DPBP.
+ * @mc_io: Pointer to MC portal's I/O object
+ * @token: Token of DPBP object
+ * @irq_index: The interrupt index to configure
+ * @type: Interrupt type: 0 represents message interrupt
+ * type (both irq_addr and irq_val are valid)
+ * @irq_addr: Returned address that must be written to
+ * signal the message-based interrupt
+ * @irq_val: Value to write into irq_addr address
+ * @user_irq_id: A user defined number associated with this IRQ
+ *
+ * Return: '0' on Success; Error code otherwise.
+ */
+int dpbp_get_irq(struct fsl_mc_io *mc_io,
+ uint16_t token,
+ uint8_t irq_index,
+ int *type,
+ uint64_t *irq_addr,
+ uint32_t *irq_val,
+ int *user_irq_id);
+
+/**
+ * dpbp_set_irq_enable() - Set overall interrupt state.
+ * @mc_io: Pointer to MC portal's I/O object
+ * @token: Token of DPBP object
+ * @irq_index: The interrupt index to configure
+ * @en: Interrupt state - enable = 1, disable = 0
+ *
+ * Allows GPP software to control when interrupts are generated.
+ * Each interrupt can have up to 32 causes. The enable/disable control's the
+ * overall interrupt state. if the interrupt is disabled no causes will cause
+ * an interrupt.
+ *
+ * Return: '0' on Success; Error code otherwise.
+ */
+int dpbp_set_irq_enable(struct fsl_mc_io *mc_io,
+ uint16_t token,
+ uint8_t irq_index,
+ uint8_t en);
+
+/**
+ * dpbp_get_irq_enable() - Get overall interrupt state
+ * @mc_io: Pointer to MC portal's I/O object
+ * @token: Token of DPBP object
+ * @irq_index: The interrupt index to configure
+ * @en: Returned interrupt state - enable = 1, disable = 0
+ *
+ * Return: '0' on Success; Error code otherwise.
+ */
+int dpbp_get_irq_enable(struct fsl_mc_io *mc_io,
+ uint16_t token,
+ uint8_t irq_index,
+ uint8_t *en);
+
+/**
+ * dpbp_set_irq_mask() - Set interrupt mask.
+ * @mc_io: Pointer to MC portal's I/O object
+ * @token: Token of DPBP object
+ * @irq_index: The interrupt index to configure
+ * @mask: Event mask to trigger interrupt;
+ * each bit:
+ * 0 = ignore event
+ * 1 = consider event for asserting IRQ
+ *
+ * Every interrupt can have up to 32 causes and the interrupt model supports
+ * masking/unmasking each cause independently
+ *
+ * Return: '0' on Success; Error code otherwise.
+ */
+int dpbp_set_irq_mask(struct fsl_mc_io *mc_io,
+ uint16_t token,
+ uint8_t irq_index,
+ uint32_t mask);
+
+/**
+ * dpbp_get_irq_mask() - Get interrupt mask.
+ * @mc_io: Pointer to MC portal's I/O object
+ * @token: Token of DPBP object
+ * @irq_index: The interrupt index to configure
+ * @mask: Returned event mask to trigger interrupt
+ *
+ * Every interrupt can have up to 32 causes and the interrupt model supports
+ * masking/unmasking each cause independently
+ *
+ * Return: '0' on Success; Error code otherwise.
+ */
+int dpbp_get_irq_mask(struct fsl_mc_io *mc_io,
+ uint16_t token,
+ uint8_t irq_index,
+ uint32_t *mask);
+
+/**
+ * dpbp_get_irq_status() - Get the current status of any pending interrupts.
+ *
+ * @mc_io: Pointer to MC portal's I/O object
+ * @token: Token of DPBP object
+ * @irq_index: The interrupt index to configure
+ * @status: Returned interrupts status - one bit per cause:
+ * 0 = no interrupt pending
+ * 1 = interrupt pending
+ *
+ * Return: '0' on Success; Error code otherwise.
+ */
+int dpbp_get_irq_status(struct fsl_mc_io *mc_io,
+ uint16_t token,
+ uint8_t irq_index,
+ uint32_t *status);
+
+/**
+ * dpbp_clear_irq_status() - Clear a pending interrupt's status
+ *
+ * @mc_io: Pointer to MC portal's I/O object
+ * @token: Token of DPBP object
+ * @irq_index: The interrupt index to configure
+ * @status: Bits to clear (W1C) - one bit per cause:
+ * 0 = don't change
+ * 1 = clear status bit
+ *
+ * Return: '0' on Success; Error code otherwise.
+ */
+int dpbp_clear_irq_status(struct fsl_mc_io *mc_io,
+ uint16_t token,
+ uint8_t irq_index,
+ uint32_t status);
+
+/**
+ * struct dpbp_attr - Structure representing DPBP attributes
+ * @id: DPBP object ID
+ * @version: DPBP version
+ * @bpid: Hardware buffer pool ID; should be used as an argument in
+ * acquire/release operations on buffers
+ */
+struct dpbp_attr {
+ int id;
+ /**
+ * struct version - Structure representing DPBP version
+ * @major: DPBP major version
+ * @minor: DPBP minor version
+ */
+ struct {
+ uint16_t major;
+ uint16_t minor;
+ } version;
+ uint16_t bpid;
+};
+
+/**
+ * dpbp_get_attributes - Retrieve DPBP attributes.
+ *
+ * @mc_io: Pointer to MC portal's I/O object
+ * @token: Token of DPBP object
+ * @attr: Returned object's attributes
+ *
+ * Return: '0' on Success; Error code otherwise.
+ */
+int dpbp_get_attributes(struct fsl_mc_io *mc_io,
+ uint16_t token,
+ struct dpbp_attr *attr);
+
+/** @} */
+
+#endif /* __FSL_DPBP_H */
diff --git a/drivers/staging/fsl-mc/include/dpcon-cmd.h b/drivers/staging/fsl-mc/include/dpcon-cmd.h
new file mode 100644
index 000000000..c878d33bf
--- /dev/null
+++ b/drivers/staging/fsl-mc/include/dpcon-cmd.h
@@ -0,0 +1,62 @@
+/* Copyright 2013-2015 Freescale Semiconductor Inc.
+*
+* Redistribution and use in source and binary forms, with or without
+* modification, are permitted provided that the following conditions are met:
+* * Redistributions of source code must retain the above copyright
+* notice, this list of conditions and the following disclaimer.
+* * Redistributions in binary form must reproduce the above copyright
+* notice, this list of conditions and the following disclaimer in the
+* documentation and/or other materials provided with the distribution.
+* * Neither the name of the above-listed copyright holders nor the
+* names of any contributors may be used to endorse or promote products
+* derived from this software without specific prior written permission.
+*
+*
+* ALTERNATIVELY, this software may be distributed under the terms of the
+* GNU General Public License ("GPL") as published by the Free Software
+* Foundation, either version 2 of that License or (at your option) any
+* later version.
+*
+* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT HOLDERS OR CONTRIBUTORS 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.
+*/
+#ifndef _FSL_DPCON_CMD_H
+#define _FSL_DPCON_CMD_H
+
+/* DPCON Version */
+#define DPCON_VER_MAJOR 2
+#define DPCON_VER_MINOR 0
+
+/* Command IDs */
+#define DPCON_CMDID_CLOSE 0x800
+#define DPCON_CMDID_OPEN 0x808
+#define DPCON_CMDID_CREATE 0x908
+#define DPCON_CMDID_DESTROY 0x900
+
+#define DPCON_CMDID_ENABLE 0x002
+#define DPCON_CMDID_DISABLE 0x003
+#define DPCON_CMDID_GET_ATTR 0x004
+#define DPCON_CMDID_RESET 0x005
+#define DPCON_CMDID_IS_ENABLED 0x006
+
+#define DPCON_CMDID_SET_IRQ 0x010
+#define DPCON_CMDID_GET_IRQ 0x011
+#define DPCON_CMDID_SET_IRQ_ENABLE 0x012
+#define DPCON_CMDID_GET_IRQ_ENABLE 0x013
+#define DPCON_CMDID_SET_IRQ_MASK 0x014
+#define DPCON_CMDID_GET_IRQ_MASK 0x015
+#define DPCON_CMDID_GET_IRQ_STATUS 0x016
+#define DPCON_CMDID_CLEAR_IRQ_STATUS 0x017
+
+#define DPCON_CMDID_SET_NOTIFICATION 0x100
+
+#endif /* _FSL_DPCON_CMD_H */
diff --git a/drivers/staging/fsl-mc/include/dpmng.h b/drivers/staging/fsl-mc/include/dpmng.h
new file mode 100644
index 000000000..1b052b830
--- /dev/null
+++ b/drivers/staging/fsl-mc/include/dpmng.h
@@ -0,0 +1,80 @@
+/* Copyright 2013-2015 Freescale Semiconductor Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of the above-listed copyright holders nor the
+ * names of any contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ *
+ * ALTERNATIVELY, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") as published by the Free Software
+ * Foundation, either version 2 of that License or (at your option) any
+ * later version.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT HOLDERS OR CONTRIBUTORS 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.
+ */
+#ifndef __FSL_DPMNG_H
+#define __FSL_DPMNG_H
+
+/* Management Complex General API
+ * Contains general API for the Management Complex firmware
+ */
+
+struct fsl_mc_io;
+
+/**
+ * Management Complex firmware version information
+ */
+#define MC_VER_MAJOR 6
+#define MC_VER_MINOR 0
+
+/**
+ * struct mc_versoin
+ * @major: Major version number: incremented on API compatibility changes
+ * @minor: Minor version number: incremented on API additions (that are
+ * backward compatible); reset when major version is incremented
+ * @revision: Internal revision number: incremented on implementation changes
+ * and/or bug fixes that have no impact on API
+ */
+struct mc_version {
+ uint32_t major;
+ uint32_t minor;
+ uint32_t revision;
+};
+
+/**
+ * mc_get_version() - Retrieves the Management Complex firmware
+ * version information
+ * @mc_io: Pointer to opaque I/O object
+ * @mc_ver_info: Returned version information structure
+ *
+ * Return: '0' on Success; Error code otherwise.
+ */
+int mc_get_version(struct fsl_mc_io *mc_io, struct mc_version *mc_ver_info);
+
+/**
+ * dpmng_get_container_id() - Get container ID associated with a given portal.
+ * @mc_io: Pointer to MC portal's I/O object
+ * @container_id: Requested container ID
+ *
+ * Return: '0' on Success; Error code otherwise.
+ */
+int dpmng_get_container_id(struct fsl_mc_io *mc_io, int *container_id);
+
+#endif /* __FSL_DPMNG_H */
diff --git a/drivers/staging/fsl-mc/include/dprc.h b/drivers/staging/fsl-mc/include/dprc.h
new file mode 100644
index 000000000..f1862a78a
--- /dev/null
+++ b/drivers/staging/fsl-mc/include/dprc.h
@@ -0,0 +1,801 @@
+/* Copyright 2013-2015 Freescale Semiconductor Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of the above-listed copyright holders nor the
+ * names of any contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ *
+ * ALTERNATIVELY, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") as published by the Free Software
+ * Foundation, either version 2 of that License or (at your option) any
+ * later version.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT HOLDERS OR CONTRIBUTORS 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.
+ */
+#ifndef _FSL_DPRC_H
+#define _FSL_DPRC_H
+
+/* Data Path Resource Container API
+ * Contains DPRC API for managing and querying DPAA resources
+ */
+
+struct fsl_mc_io;
+
+/**
+ * Set this value as the icid value in dprc_cfg structure when creating a
+ * container, in case the ICID is not selected by the user and should be
+ * allocated by the DPRC from the pool of ICIDs.
+ */
+#define DPRC_GET_ICID_FROM_POOL (uint16_t)(~(0))
+
+/**
+ * Set this value as the portal_id value in dprc_cfg structure when creating a
+ * container, in case the portal ID is not specifically selected by the
+ * user and should be allocated by the DPRC from the pool of portal ids.
+ */
+#define DPRC_GET_PORTAL_ID_FROM_POOL (int)(~(0))
+
+/**
+ * dprc_open() - Open DPRC object for use
+ * @mc_io: Pointer to MC portal's I/O object
+ * @container_id: Container ID to open
+ * @token: Returned token of DPRC object
+ *
+ * Return: '0' on Success; Error code otherwise.
+ *
+ * @warning Required before any operation on the object.
+ */
+int dprc_open(struct fsl_mc_io *mc_io, int container_id, uint16_t *token);
+
+/**
+ * dprc_close() - Close the control session of the object
+ * @mc_io: Pointer to MC portal's I/O object
+ * @token: Token of DPRC object
+ *
+ * After this function is called, no further operations are
+ * allowed on the object without opening a new control session.
+ *
+ * Return: '0' on Success; Error code otherwise.
+ */
+int dprc_close(struct fsl_mc_io *mc_io, uint16_t token);
+
+/**
+ * Container general options
+ *
+ * These options may be selected at container creation by the container creator
+ * and can be retrieved using dprc_get_attributes()
+ */
+
+/* Spawn Policy Option allowed - Indicates that the new container is allowed
+ * to spawn and have its own child containers.
+ */
+#define DPRC_CFG_OPT_SPAWN_ALLOWED 0x00000001
+
+/* General Container allocation policy - Indicates that the new container is
+ * allowed to allocate requested resources from its parent container; if not
+ * set, the container is only allowed to use resources in its own pools; Note
+ * that this is a container's global policy, but the parent container may
+ * override it and set specific quota per resource type.
+ */
+#define DPRC_CFG_OPT_ALLOC_ALLOWED 0x00000002
+
+/* Object initialization allowed - software context associated with this
+ * container is allowed to invoke object initialization operations.
+ */
+#define DPRC_CFG_OPT_OBJ_CREATE_ALLOWED 0x00000004
+
+/* Topology change allowed - software context associated with this
+ * container is allowed to invoke topology operations, such as attach/detach
+ * of network objects.
+ */
+#define DPRC_CFG_OPT_TOPOLOGY_CHANGES_ALLOWED 0x00000008
+
+/* IOMMU bypass - indicates whether objects of this container are permitted
+ * to bypass the IOMMU.
+ */
+#define DPRC_CFG_OPT_IOMMU_BYPASS 0x00000010
+
+/* AIOP - Indicates that container belongs to AIOP. */
+#define DPRC_CFG_OPT_AIOP 0x00000020
+
+/**
+ * struct dprc_cfg - Container configuration options
+ * @icid: Container's ICID; if set to 'DPRC_GET_ICID_FROM_POOL', a free
+ * ICID value is allocated by the DPRC
+ * @portal_id: Portal ID; if set to 'DPRC_GET_PORTAL_ID_FROM_POOL', a free
+ * portal ID is allocated by the DPRC
+ * @options: Combination of 'DPRC_CFG_OPT_<X>' options
+ */
+struct dprc_cfg {
+ uint16_t icid;
+ int portal_id;
+ uint64_t options;
+};
+
+/**
+ * dprc_create_container() - Create child container
+ * @mc_io: Pointer to MC portal's I/O object
+ * @token: Token of DPRC object
+ * @cfg: Child container configuration
+ * @child_container_id: Returned child container ID
+ * @child_portal_paddr: Returned base physical address of the
+ * child portal
+ *
+ * Return: '0' on Success; Error code otherwise.
+ */
+int dprc_create_container(struct fsl_mc_io *mc_io,
+ uint16_t token,
+ struct dprc_cfg *cfg,
+ int *child_container_id,
+ uint64_t *child_portal_paddr);
+
+/**
+ * dprc_destroy_container() - Destroy child container.
+ * @mc_io: Pointer to MC portal's I/O object
+ * @token: Token of DPRC object
+ * @child_container_id: ID of the container to destroy
+ *
+ * This function terminates the child container, so following this call the
+ * child container ID becomes invalid.
+ *
+ * Notes:
+ * - All resources and objects of the destroyed container are returned to the
+ * parent container or destroyed if were created be the destroyed container.
+ * - This function destroy all the child containers of the specified
+ * container prior to destroying the container itself.
+ *
+ * warning: Only the parent container is allowed to destroy a child policy
+ * Container 0 can't be destroyed
+ *
+ * Return: '0' on Success; Error code otherwise.
+ *
+ */
+int dprc_destroy_container(struct fsl_mc_io *mc_io,
+ uint16_t token,
+ int child_container_id);
+
+/**
+ * dprc_reset_container - Reset child container.
+ * @mc_io: Pointer to MC portal's I/O object
+ * @token: Token of DPRC object
+ * @child_container_id: ID of the container to reset
+ *
+ * In case a software context crashes or becomes non-responsive, the parent
+ * may wish to reset its resources container before the software context is
+ * restarted.
+ *
+ * This routine informs all objects assigned to the child container that the
+ * container is being reset, so they may perform any cleanup operations that are
+ * needed. All objects handles that were owned by the child container shall be
+ * closed.
+ *
+ * Note that such request may be submitted even if the child software context
+ * has not crashed, but the resulting object cleanup operations will not be
+ * aware of that.
+ *
+ * Return: '0' on Success; Error code otherwise.
+ */
+int dprc_reset_container(struct fsl_mc_io *mc_io,
+ uint16_t token,
+ int child_container_id);
+
+/* IRQ */
+
+/* Number of dprc's IRQs */
+#define DPRC_NUM_OF_IRQS 1
+
+/* Object irq events */
+
+/* IRQ event - Indicates that a new object assigned to the container */
+#define DPRC_IRQ_EVENT_OBJ_ADDED 0x00000001
+/* IRQ event - Indicates that an object was unassigned from the container */
+#define DPRC_IRQ_EVENT_OBJ_REMOVED 0x00000002
+/* IRQ event - Indicates that resources assigned to the container */
+#define DPRC_IRQ_EVENT_RES_ADDED 0x00000004
+/* IRQ event - Indicates that resources unassigned from the container */
+#define DPRC_IRQ_EVENT_RES_REMOVED 0x00000008
+/* IRQ event - Indicates that one of the descendant containers that opened by
+ * this container is destroyed
+ */
+#define DPRC_IRQ_EVENT_CONTAINER_DESTROYED 0x00000010
+
+/* IRQ event - Indicates that on one of the container's opened object is
+ * destroyed
+ */
+#define DPRC_IRQ_EVENT_OBJ_DESTROYED 0x00000020
+
+/* Irq event - Indicates that object is created at the container */
+#define DPRC_IRQ_EVENT_OBJ_CREATED 0x00000040
+
+/**
+ * dprc_set_irq() - Set IRQ information for the DPRC to trigger an interrupt.
+ * @mc_io: Pointer to MC portal's I/O object
+ * @token: Token of DPRC object
+ * @irq_index: Identifies the interrupt index to configure
+ * @irq_addr: Address that must be written to
+ * signal a message-based interrupt
+ * @irq_val: Value to write into irq_addr address
+ * @user_irq_id: Returned a user defined number associated with this IRQ
+ *
+ * Return: '0' on Success; Error code otherwise.
+ */
+int dprc_set_irq(struct fsl_mc_io *mc_io,
+ uint16_t token,
+ uint8_t irq_index,
+ uint64_t irq_addr,
+ uint32_t irq_val,
+ int user_irq_id);
+
+/**
+ * dprc_get_irq() - Get IRQ information from the DPRC.
+ * @mc_io: Pointer to MC portal's I/O object
+ * @token: Token of DPRC object
+ * @irq_index: The interrupt index to configure
+ * @type: Returned interrupt type: 0 represents message interrupt
+ * type (both irq_addr and irq_val are valid)
+ * @irq_addr: Returned address that must be written to
+ * signal the message-based interrupt
+ * @irq_val: Value to write into irq_addr address
+ * @user_irq_id: A user defined number associated with this IRQ
+ *
+ * Return: '0' on Success; Error code otherwise.
+ */
+int dprc_get_irq(struct fsl_mc_io *mc_io,
+ uint16_t token,
+ uint8_t irq_index,
+ int *type,
+ uint64_t *irq_addr,
+ uint32_t *irq_val,
+ int *user_irq_id);
+
+/**
+ * dprc_set_irq_enable() - Set overall interrupt state.
+ * @mc_io: Pointer to MC portal's I/O object
+ * @token: Token of DPRC object
+ * @irq_index: The interrupt index to configure
+ * @en: Interrupt state - enable = 1, disable = 0
+ *
+ * Allows GPP software to control when interrupts are generated.
+ * Each interrupt can have up to 32 causes. The enable/disable control's the
+ * overall interrupt state. if the interrupt is disabled no causes will cause
+ * an interrupt.
+ *
+ * Return: '0' on Success; Error code otherwise.
+ */
+int dprc_set_irq_enable(struct fsl_mc_io *mc_io,
+ uint16_t token,
+ uint8_t irq_index,
+ uint8_t en);
+
+/**
+ * dprc_get_irq_enable() - Get overall interrupt state.
+ * @mc_io: Pointer to MC portal's I/O object
+ * @token: Token of DPRC object
+ * @irq_index: The interrupt index to configure
+ * @en: Returned interrupt state - enable = 1, disable = 0
+ *
+ * Return: '0' on Success; Error code otherwise.
+ */
+int dprc_get_irq_enable(struct fsl_mc_io *mc_io,
+ uint16_t token,
+ uint8_t irq_index,
+ uint8_t *en);
+
+/**
+ * dprc_set_irq_mask() - Set interrupt mask.
+ * @mc_io: Pointer to MC portal's I/O object
+ * @token: Token of DPRC object
+ * @irq_index: The interrupt index to configure
+ * @mask: event mask to trigger interrupt;
+ * each bit:
+ * 0 = ignore event
+ * 1 = consider event for asserting irq
+ *
+ * Every interrupt can have up to 32 causes and the interrupt model supports
+ * masking/unmasking each cause independently
+ *
+ * Return: '0' on Success; Error code otherwise.
+ */
+int dprc_set_irq_mask(struct fsl_mc_io *mc_io,
+ uint16_t token,
+ uint8_t irq_index,
+ uint32_t mask);
+
+/**
+ * dprc_get_irq_mask() - Get interrupt mask.
+ * @mc_io: Pointer to MC portal's I/O object
+ * @token: Token of DPRC object
+ * @irq_index: The interrupt index to configure
+ * @mask: Returned event mask to trigger interrupt
+ *
+ * Every interrupt can have up to 32 causes and the interrupt model supports
+ * masking/unmasking each cause independently
+ *
+ * Return: '0' on Success; Error code otherwise.
+ */
+int dprc_get_irq_mask(struct fsl_mc_io *mc_io,
+ uint16_t token,
+ uint8_t irq_index,
+ uint32_t *mask);
+
+/**
+ * dprc_get_irq_status() - Get the current status of any pending interrupts.
+ * @mc_io: Pointer to MC portal's I/O object
+ * @token: Token of DPRC object
+ * @irq_index: The interrupt index to configure
+ * @status: Returned interrupts status - one bit per cause:
+ * 0 = no interrupt pending
+ * 1 = interrupt pending
+ *
+ * Return: '0' on Success; Error code otherwise.
+ */
+int dprc_get_irq_status(struct fsl_mc_io *mc_io,
+ uint16_t token,
+ uint8_t irq_index,
+ uint32_t *status);
+
+/**
+ * dprc_clear_irq_status() - Clear a pending interrupt's status
+ * @mc_io: Pointer to MC portal's I/O object
+ * @token: Token of DPRC object
+ * @irq_index: The interrupt index to configure
+ * @status: bits to clear (W1C) - one bit per cause:
+ * 0 = don't change
+ * 1 = clear status bit
+ *
+ * Return: '0' on Success; Error code otherwise.
+ */
+int dprc_clear_irq_status(struct fsl_mc_io *mc_io,
+ uint16_t token,
+ uint8_t irq_index,
+ uint32_t status);
+
+/**
+ * struct dprc_attributes - Container attributes
+ * @container_id: Container's ID
+ * @icid: Container's ICID
+ * @portal_id: Container's portal ID
+ * @options: Container's options as set at container's creation
+ * @version: DPRC version
+ */
+struct dprc_attributes {
+ int container_id;
+ uint16_t icid;
+ int portal_id;
+ uint64_t options;
+ /**
+ * struct version - DPRC version
+ * @major: DPRC major version
+ * @minor: DPRC minor version
+ */
+ struct {
+ uint16_t major;
+ uint16_t minor;
+ } version;
+};
+
+/**
+ * dprc_get_attributes() - Obtains container attributes
+ * @mc_io: Pointer to MC portal's I/O object
+ * @token: Token of DPRC object
+ * @attributes Returned container attributes
+ *
+ * Return: '0' on Success; Error code otherwise.
+ */
+int dprc_get_attributes(struct fsl_mc_io *mc_io,
+ uint16_t token,
+ struct dprc_attributes *attributes);
+
+/**
+ * dprc_set_res_quota() - Set allocation policy for a specific resource/object
+ * type in a child container
+ * @mc_io: Pointer to MC portal's I/O object
+ * @token: Token of DPRC object
+ * @child_container_id: ID of the child container
+ * @type: Resource/object type
+ * @quota: Sets the maximum number of resources of the selected type
+ * that the child container is allowed to allocate from its parent;
+ * when quota is set to -1, the policy is the same as container's
+ * general policy.
+ *
+ * Allocation policy determines whether or not a container may allocate
+ * resources from its parent. Each container has a 'global' allocation policy
+ * that is set when the container is created.
+ *
+ * This function sets allocation policy for a specific resource type.
+ * The default policy for all resource types matches the container's 'global'
+ * allocation policy.
+ *
+ * Return: '0' on Success; Error code otherwise.
+ *
+ * @warning Only the parent container is allowed to change a child policy.
+ */
+int dprc_set_res_quota(struct fsl_mc_io *mc_io,
+ uint16_t token,
+ int child_container_id,
+ char *type,
+ uint16_t quota);
+
+/**
+ * dprc_get_res_quota() - Gets the allocation policy of a specific
+ * resource/object type in a child container
+ * @mc_io: Pointer to MC portal's I/O object
+ * @token: Token of DPRC object
+ * @child_container_id; ID of the child container
+ * @type: resource/object type
+ * @quota: Returnes the maximum number of resources of the selected type
+ * that the child container is allowed to allocate from the parent;
+ * when quota is set to -1, the policy is the same as container's
+ * general policy.
+ *
+ * Return: '0' on Success; Error code otherwise.
+ */
+int dprc_get_res_quota(struct fsl_mc_io *mc_io,
+ uint16_t token,
+ int child_container_id,
+ char *type,
+ uint16_t *quota);
+
+/* Resource request options */
+
+/* Explicit resource ID request - The requested objects/resources
+ * are explicit and sequential (in case of resources).
+ * The base ID is given at res_req at base_align field
+ */
+#define DPRC_RES_REQ_OPT_EXPLICIT 0x00000001
+
+/* Aligned resources request - Relevant only for resources
+ * request (and not objects). Indicates that resources base ID should be
+ * sequential and aligned to the value given at dprc_res_req base_align field
+ */
+#define DPRC_RES_REQ_OPT_ALIGNED 0x00000002
+
+/* Plugged Flag - Relevant only for object assignment request.
+ * Indicates that after all objects assigned. An interrupt will be invoked at
+ * the relevant GPP. The assigned object will be marked as plugged.
+ * plugged objects can't be assigned from their container
+ */
+#define DPRC_RES_REQ_OPT_PLUGGED 0x00000004
+
+/**
+ * struct dprc_res_req - Resource request descriptor, to be used in assignment
+ * or un-assignment of resources and objects.
+ * @type: Resource/object type: Represent as a NULL terminated string.
+ * This string may received by using dprc_get_pool() to get resource
+ * type and dprc_get_obj() to get object type;
+ * Note: it is not possible to assign/un-assign DPRC objects
+ * @num: Number of resources
+ * @options: Request options: combination of DPRC_RES_REQ_OPT_ options
+ * @id_base_align: In case of explicit assignment (DPRC_RES_REQ_OPT_EXPLICIT
+ * is set at option), this field represents the required base ID
+ * for resource allocation; In case of aligned assignment
+ * (DPRC_RES_REQ_OPT_ALIGNED is set at option), this field
+ * indicates the required alignment for the resource ID(s) -
+ * use 0 if there is no alignment or explicit ID requirements
+ */
+struct dprc_res_req {
+ char type[16];
+ uint32_t num;
+ uint32_t options;
+ int id_base_align;
+};
+
+/**
+ * dprc_assign() - Assigns objects or resource to a child container.
+ * @mc_io: Pointer to MC portal's I/O object
+ * @token: Token of DPRC object
+ * @container_id: ID of the child container
+ * @res_req: Describes the type and amount of resources to
+ * assign to the given container
+ *
+ * Assignment is usually done by a parent (this DPRC) to one of its child
+ * containers.
+ *
+ * According to the DPRC allocation policy, the assigned resources may be taken
+ * (allocated) from the container's ancestors, if not enough resources are
+ * available in the container itself.
+ *
+ * The type of assignment depends on the dprc_res_req options, as follows:
+ * - DPRC_RES_REQ_OPT_EXPLICIT: indicates that assigned resources should have
+ * the explicit base ID specified at the id_base_align field of res_req.
+ * - DPRC_RES_REQ_OPT_ALIGNED: indicates that the assigned resources should be
+ * aligned to the value given at id_base_align field of res_req.
+ * - DPRC_RES_REQ_OPT_PLUGGED: Relevant only for object assignment,
+ * and indicates that the object must be set to the plugged state.
+ *
+ * A container may use this function with its own ID in order to change a
+ * object state to plugged or unplugged.
+ *
+ * If IRQ information has been set in the child DPRC, it will signal an
+ * interrupt following every change in its object assignment.
+ *
+ * Return: '0' on Success; Error code otherwise.
+ */
+int dprc_assign(struct fsl_mc_io *mc_io,
+ uint16_t token,
+ int container_id,
+ struct dprc_res_req *res_req);
+
+/**
+ * dprc_unassign() - Un-assigns objects or resources from a child container
+ * and moves them into this (parent) DPRC.
+ * @mc_io: Pointer to MC portal's I/O object
+ * @token: Token of DPRC object
+ * @child_container_id: ID of the child container
+ * @res_req: Describes the type and amount of resources to un-assign from
+ * the child container
+ *
+ * Un-assignment of objects can succeed only if the object is not in the
+ * plugged or opened state.
+ *
+ * Return: '0' on Success; Error code otherwise.
+ */
+int dprc_unassign(struct fsl_mc_io *mc_io,
+ uint16_t token,
+ int child_container_id,
+ struct dprc_res_req *res_req);
+
+/**
+ * dprc_get_pool_count() - Get the number of dprc's pools
+ * @mc_io: Pointer to MC portal's I/O object
+ * @token: Token of DPRC object
+ * @pool_count: Returned number of resource pools in the dprc
+ *
+ * Return: '0' on Success; Error code otherwise.
+ */
+int dprc_get_pool_count(struct fsl_mc_io *mc_io,
+ uint16_t token,
+ int *pool_count);
+
+/**
+ * dprc_get_pool() - Get the type (string) of a certain dprc's pool
+ * @mc_io: Pointer to MC portal's I/O object
+ * @token: Token of DPRC object
+ * @pool_index; Index of the pool to be queried (< pool_count)
+ * @type: The type of the pool
+ *
+ * The pool types retrieved one by one by incrementing
+ * pool_index up to (not including) the value of pool_count returned
+ * from dprc_get_pool_count(). dprc_get_pool_count() must
+ * be called prior to dprc_get_pool().
+ *
+ * Return: '0' on Success; Error code otherwise.
+ */
+int dprc_get_pool(struct fsl_mc_io *mc_io,
+ uint16_t token,
+ int pool_index,
+ char *type);
+
+/**
+ * dprc_get_obj_count() - Obtains the number of objects in the DPRC
+ * @mc_io: Pointer to MC portal's I/O object
+ * @token: Token of DPRC object
+ * @obj_count: Number of objects assigned to the DPRC
+ *
+ * Return: '0' on Success; Error code otherwise.
+ */
+int dprc_get_obj_count(struct fsl_mc_io *mc_io, uint16_t token, int *obj_count);
+
+/* Objects Attributes Flags */
+
+/* Opened state - Indicates that an object is open by at least one owner */
+#define DPRC_OBJ_STATE_OPEN 0x00000001
+/* Plugged state - Indicates that the object is plugged */
+#define DPRC_OBJ_STATE_PLUGGED 0x00000002
+
+/**
+ * struct dprc_obj_desc - Object descriptor, returned from dprc_get_obj()
+ * @type: Type of object: NULL terminated string
+ * @id: ID of logical object resource
+ * @vendor: Object vendor identifier
+ * @ver_major: Major version number
+ * @ver_minor: Minor version number
+ * @irq_count: Number of interrupts supported by the object
+ * @region_count: Number of mappable regions supported by the object
+ * @state: Object state: combination of DPRC_OBJ_STATE_ states
+ */
+struct dprc_obj_desc {
+ char type[16];
+ int id;
+ uint16_t vendor;
+ uint16_t ver_major;
+ uint16_t ver_minor;
+ uint8_t irq_count;
+ uint8_t region_count;
+ uint32_t state;
+};
+
+/**
+ * dprc_get_obj() - Get general information on an object
+ * @mc_io: Pointer to MC portal's I/O object
+ * @token: Token of DPRC object
+ * @obj_index: Index of the object to be queried (< obj_count)
+ * @obj_desc: Returns the requested object descriptor
+ *
+ * The object descriptors are retrieved one by one by incrementing
+ * obj_index up to (not including) the value of obj_count returned
+ * from dprc_get_obj_count(). dprc_get_obj_count() must
+ * be called prior to dprc_get_obj().
+ *
+ * Return: '0' on Success; Error code otherwise.
+ */
+int dprc_get_obj(struct fsl_mc_io *mc_io,
+ uint16_t token,
+ int obj_index,
+ struct dprc_obj_desc *obj_desc);
+
+/**
+ * dprc_get_res_count() - Obtains the number of free resources that are assigned
+ * to this container, by pool type
+ * @mc_io: Pointer to MC portal's I/O object
+ * @token: Token of DPRC object
+ * @type: pool type
+ * @res_count: Returned number of free resources of the given
+ * resource type that are assigned to this DPRC
+ *
+ * Return: '0' on Success; Error code otherwise.
+ */
+int dprc_get_res_count(struct fsl_mc_io *mc_io,
+ uint16_t token,
+ char *type,
+ int *res_count);
+
+/**
+ * enum dprc_iter_status - Iteration status
+ * @DPRC_ITER_STATUS_FIRST: Perform first iteration
+ * @DPRC_ITER_STATUS_MORE: Indicates more/next iteration is needed
+ * @DPRC_ITER_STATUS_LAST: Indicates last iteration
+ */
+enum dprc_iter_status {
+ DPRC_ITER_STATUS_FIRST = 0,
+ DPRC_ITER_STATUS_MORE = 1,
+ DPRC_ITER_STATUS_LAST = 2
+};
+
+/**
+ * struct dprc_res_ids_range_desc - Resource ID range descriptor
+ * @base_id: Base resource ID of this range
+ * @last_id: Last resource ID of this range
+ * @iter_status: Iteration status - should be set to DPRC_ITER_STATUS_FIRST at
+ * first iteration; while the returned marker is DPRC_ITER_STATUS_MORE,
+ * additional iterations are needed, until the returned marker is
+ * DPRC_ITER_STATUS_LAST
+ */
+struct dprc_res_ids_range_desc {
+ int base_id;
+ int last_id;
+ enum dprc_iter_status iter_status;
+};
+
+/**
+ * dprc_get_res_ids() - Obtains IDs of free resources in the container
+ * @mc_io: Pointer to MC portal's I/O object
+ * @token: Token of DPRC object
+ * @type: pool type
+ * @range_desc: range descriptor
+ *
+ * Return: '0' on Success; Error code otherwise.
+ */
+int dprc_get_res_ids(struct fsl_mc_io *mc_io,
+ uint16_t token,
+ char *type,
+ struct dprc_res_ids_range_desc *range_desc);
+
+/**
+ * dprc_get_portal_paddr() - Get the physical address of MC portals
+ * @mc_io: Pointer to MC portal's I/O object
+ * @token: Token of DPRC object
+ * @portal_id: MC portal ID
+ * @portal_addr: The physical address of the MC portal ID
+ *
+ * Return: '0' on Success; Error code otherwise.
+ */
+int dprc_get_portal_paddr(struct fsl_mc_io *mc_io,
+ uint16_t token,
+ int portal_id,
+ uint64_t *portal_addr);
+
+/**
+ * struct dprc_region_desc - Mappable region descriptor
+ * @base_paddr: Region base physical address
+ * @size: Region size (in bytes)
+ */
+struct dprc_region_desc {
+ uint64_t base_paddr;
+ uint32_t size;
+};
+
+/**
+ * dprc_get_obj_region() - Get region information for a specified object.
+ * @mc_io: Pointer to MC portal's I/O object
+ * @token: Token of DPRC object
+ * @obj_type; Object type as returned in dprc_get_obj()
+ * @obj_id: Unique object instance as returned in dprc_get_obj()
+ * @region_index: The specific region to query
+ * @region_desc: Returns the requested region descriptor
+ *
+ * Return: '0' on Success; Error code otherwise.
+ */
+int dprc_get_obj_region(struct fsl_mc_io *mc_io,
+ uint16_t token,
+ char *obj_type,
+ int obj_id,
+ uint8_t region_index,
+ struct dprc_region_desc *region_desc);
+
+/**
+ * struct dprc_endpoint - Endpoint description for link connect/disconnect
+ * operations
+ * @type: Endpoint object type: NULL terminated string
+ * @id: Endpoint object ID
+ * @interface_id: Interface ID; should be set for endpoints with multiple
+ * interfaces ("dpsw", "dpdmux"); for others, always set to 0
+ */
+struct dprc_endpoint {
+ char type[16];
+ int id;
+ int interface_id;
+};
+
+/**
+ * dprc_connect() - Connect two endpoints to create a network link between them
+ * @mc_io: Pointer to MC portal's I/O object
+ * @token: Token of DPRC object
+ * @endpoint1: Endpoint 1 configuration parameters
+ * @endpoint2: Endpoint 2 configuration parameters
+ *
+ * Return: '0' on Success; Error code otherwise.
+ */
+int dprc_connect(struct fsl_mc_io *mc_io,
+ uint16_t token,
+ const struct dprc_endpoint *endpoint1,
+ const struct dprc_endpoint *endpoint2);
+
+/**
+ * dprc_disconnect() - Disconnect one endpoint to remove its network connection
+ * @mc_io: Pointer to MC portal's I/O object
+ * @token: Token of DPRC object
+ * @endpoint: Endpoint configuration parameters
+ *
+ * Return: '0' on Success; Error code otherwise.
+ */
+int dprc_disconnect(struct fsl_mc_io *mc_io,
+ uint16_t token,
+ const struct dprc_endpoint *endpoint);
+
+/**
+* dprc_get_connection() - Get connected endpoint and link status if connection
+* exists.
+* @mc_io Pointer to MC portal's I/O object
+* @token Token of DPRC object
+* @endpoint1 Endpoint 1 configuration parameters
+* @endpoint2 Returned endpoint 2 configuration parameters
+* @state: Returned link state: 1 - link is up, 0 - link is down
+*
+* Return: '0' on Success; -ENAVAIL if connection does not exist.
+*/
+int dprc_get_connection(struct fsl_mc_io *mc_io,
+ uint16_t token,
+ const struct dprc_endpoint *endpoint1,
+ struct dprc_endpoint *endpoint2,
+ int *state);
+
+#endif /* _FSL_DPRC_H */
+
diff --git a/drivers/staging/fsl-mc/include/mc-cmd.h b/drivers/staging/fsl-mc/include/mc-cmd.h
new file mode 100644
index 000000000..32501e020
--- /dev/null
+++ b/drivers/staging/fsl-mc/include/mc-cmd.h
@@ -0,0 +1,113 @@
+/* Copyright 2013-2015 Freescale Semiconductor Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of the above-listed copyright holders nor the
+ * names of any contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ *
+ * ALTERNATIVELY, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") as published by the Free Software
+ * Foundation, either version 2 of that License or (at your option) any
+ * later version.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT HOLDERS OR CONTRIBUTORS 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.
+ */
+#ifndef __FSL_MC_CMD_H
+#define __FSL_MC_CMD_H
+
+#define MC_CMD_NUM_OF_PARAMS 7
+
+#define MAKE_UMASK64(_width) \
+ ((uint64_t)((_width) < 64 ? ((uint64_t)1 << (_width)) - 1 : -1))
+
+static inline uint64_t mc_enc(int lsoffset, int width, uint64_t val)
+{
+ return (uint64_t)(((uint64_t)val & MAKE_UMASK64(width)) << lsoffset);
+}
+
+static inline uint64_t mc_dec(uint64_t val, int lsoffset, int width)
+{
+ return (uint64_t)((val >> lsoffset) & MAKE_UMASK64(width));
+}
+
+struct mc_command {
+ uint64_t header;
+ uint64_t params[MC_CMD_NUM_OF_PARAMS];
+};
+
+enum mc_cmd_status {
+ MC_CMD_STATUS_OK = 0x0, /* Completed successfully */
+ MC_CMD_STATUS_READY = 0x1, /* Ready to be processed */
+ MC_CMD_STATUS_AUTH_ERR = 0x3, /* Authentication error */
+ MC_CMD_STATUS_NO_PRIVILEGE = 0x4, /* No privilege */
+ MC_CMD_STATUS_DMA_ERR = 0x5, /* DMA or I/O error */
+ MC_CMD_STATUS_CONFIG_ERR = 0x6, /* Configuration error */
+ MC_CMD_STATUS_TIMEOUT = 0x7, /* Operation timed out */
+ MC_CMD_STATUS_NO_RESOURCE = 0x8, /* No resources */
+ MC_CMD_STATUS_NO_MEMORY = 0x9, /* No memory available */
+ MC_CMD_STATUS_BUSY = 0xA, /* Device is busy */
+ MC_CMD_STATUS_UNSUPPORTED_OP = 0xB, /* Unsupported operation */
+ MC_CMD_STATUS_INVALID_STATE = 0xC /* Invalid state */
+};
+
+#define MC_CMD_HDR_CMDID_O 52 /* Command ID field offset */
+#define MC_CMD_HDR_CMDID_S 12 /* Command ID field size */
+#define MC_CMD_HDR_TOKEN_O 38 /* Token field offset */
+#define MC_CMD_HDR_TOKEN_S 10 /* Token field size */
+#define MC_CMD_HDR_STATUS_O 16 /* Status field offset */
+#define MC_CMD_HDR_STATUS_S 8 /* Status field size*/
+#define MC_CMD_HDR_PRI_O 15 /* Priority field offset */
+#define MC_CMD_HDR_PRI_S 1 /* Priority field size */
+
+#define MC_CMD_HDR_READ_STATUS(_hdr) \
+ ((enum mc_cmd_status)mc_dec((_hdr), \
+ MC_CMD_HDR_STATUS_O, MC_CMD_HDR_STATUS_S))
+
+#define MC_CMD_HDR_READ_TOKEN(_hdr) \
+ ((uint16_t)mc_dec((_hdr), MC_CMD_HDR_TOKEN_O, MC_CMD_HDR_TOKEN_S))
+
+#define MC_CMD_PRI_LOW 0 /* Low Priority command indication */
+#define MC_CMD_PRI_HIGH 1 /* High Priority command indication */
+
+#define MC_EXT_OP(_ext, _param, _offset, _width, _type, _arg) \
+ ((_ext)[_param] |= mc_enc((_offset), (_width), _arg))
+
+#define MC_CMD_OP(_cmd, _param, _offset, _width, _type, _arg) \
+ ((_cmd).params[_param] |= mc_enc((_offset), (_width), _arg))
+
+#define MC_RSP_OP(_cmd, _param, _offset, _width, _type, _arg) \
+ (_arg = (_type)mc_dec(_cmd.params[_param], (_offset), (_width)))
+
+static inline uint64_t mc_encode_cmd_header(uint16_t cmd_id,
+ uint8_t priority,
+ uint16_t token)
+{
+ uint64_t hdr;
+
+ hdr = mc_enc(MC_CMD_HDR_CMDID_O, MC_CMD_HDR_CMDID_S, cmd_id);
+ hdr |= mc_enc(MC_CMD_HDR_TOKEN_O, MC_CMD_HDR_TOKEN_S, token);
+ hdr |= mc_enc(MC_CMD_HDR_PRI_O, MC_CMD_HDR_PRI_S, priority);
+ hdr |= mc_enc(MC_CMD_HDR_STATUS_O, MC_CMD_HDR_STATUS_S,
+ MC_CMD_STATUS_READY);
+
+ return hdr;
+}
+
+#endif /* __FSL_MC_CMD_H */
diff --git a/drivers/staging/fsl-mc/include/mc-private.h b/drivers/staging/fsl-mc/include/mc-private.h
new file mode 100644
index 000000000..c045f49f2
--- /dev/null
+++ b/drivers/staging/fsl-mc/include/mc-private.h
@@ -0,0 +1,116 @@
+/*
+ * Freescale Management Complex (MC) bus private declarations
+ *
+ * Copyright (C) 2014 Freescale Semiconductor, Inc.
+ * Author: German Rivera <German.Rivera@freescale.com>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+#ifndef _FSL_MC_PRIVATE_H_
+#define _FSL_MC_PRIVATE_H_
+
+#include "../include/mc.h"
+#include <linux/mutex.h>
+#include <linux/stringify.h>
+
+#define FSL_MC_DPRC_DRIVER_NAME "fsl_mc_dprc"
+
+#define FSL_MC_DEVICE_MATCH(_mc_dev, _obj_desc) \
+ (strcmp((_mc_dev)->obj_desc.type, (_obj_desc)->type) == 0 && \
+ (_mc_dev)->obj_desc.id == (_obj_desc)->id)
+
+#define FSL_MC_IS_ALLOCATABLE(_obj_type) \
+ (strcmp(_obj_type, "dpbp") == 0 || \
+ strcmp(_obj_type, "dpmcp") == 0 || \
+ strcmp(_obj_type, "dpcon") == 0)
+
+/**
+ * struct fsl_mc - Private data of a "fsl,qoriq-mc" platform device
+ * @root_mc_bus_dev: MC object device representing the root DPRC
+ * @addr_translation_ranges: array of bus to system address translation ranges
+ */
+struct fsl_mc {
+ struct fsl_mc_device *root_mc_bus_dev;
+ uint8_t num_translation_ranges;
+ struct fsl_mc_addr_translation_range *translation_ranges;
+};
+
+/**
+ * struct fsl_mc_addr_translation_range - bus to system address translation
+ * range
+ * @start_mc_addr: Start MC address of the range being translated
+ * @end_mc_addr: MC address of the first byte after the range (last MC
+ * address of the range is end_mc_addr - 1)
+ * @start_phys_addr: system physical address corresponding to start_mc_addr
+ */
+struct fsl_mc_addr_translation_range {
+ uint64_t start_mc_addr;
+ uint64_t end_mc_addr;
+ phys_addr_t start_phys_addr;
+};
+
+/**
+ * struct fsl_mc_resource_pool - Pool of MC resources of a given
+ * type
+ * @type: type of resources in the pool
+ * @max_count: maximum number of resources in the pool
+ * @free_count: number of free resources in the pool
+ * @mutex: mutex to serialize access to the pool's free list
+ * @free_list: anchor node of list of free resources in the pool
+ * @mc_bus: pointer to the MC bus that owns this resource pool
+ */
+struct fsl_mc_resource_pool {
+ enum fsl_mc_pool_type type;
+ int16_t max_count;
+ int16_t free_count;
+ struct mutex mutex; /* serializes access to free_list */
+ struct list_head free_list;
+ struct fsl_mc_bus *mc_bus;
+};
+
+/**
+ * struct fsl_mc_bus - logical bus that corresponds to a physical DPRC
+ * @mc_dev: fsl-mc device for the bus device itself.
+ * @resource_pools: array of resource pools (one pool per resource type)
+ * for this MC bus. These resources represent allocatable entities
+ * from the physical DPRC.
+ * @scan_mutex: Serializes bus scanning
+ */
+struct fsl_mc_bus {
+ struct fsl_mc_device mc_dev;
+ struct fsl_mc_resource_pool resource_pools[FSL_MC_NUM_POOL_TYPES];
+ struct mutex scan_mutex; /* serializes bus scanning */
+};
+
+#define to_fsl_mc_bus(_mc_dev) \
+ container_of(_mc_dev, struct fsl_mc_bus, mc_dev)
+
+int __must_check fsl_mc_device_add(struct dprc_obj_desc *obj_desc,
+ struct fsl_mc_io *mc_io,
+ struct device *parent_dev,
+ struct fsl_mc_device **new_mc_dev);
+
+void fsl_mc_device_remove(struct fsl_mc_device *mc_dev);
+
+int dprc_scan_container(struct fsl_mc_device *mc_bus_dev);
+
+int dprc_scan_objects(struct fsl_mc_device *mc_bus_dev);
+
+int __init dprc_driver_init(void);
+
+void __exit dprc_driver_exit(void);
+
+int __init fsl_mc_allocator_driver_init(void);
+
+void __exit fsl_mc_allocator_driver_exit(void);
+
+int __must_check fsl_mc_resource_allocate(struct fsl_mc_bus *mc_bus,
+ enum fsl_mc_pool_type pool_type,
+ struct fsl_mc_resource
+ **new_resource);
+
+void fsl_mc_resource_free(struct fsl_mc_resource *resource);
+
+#endif /* _FSL_MC_PRIVATE_H_ */
diff --git a/drivers/staging/fsl-mc/include/mc-sys.h b/drivers/staging/fsl-mc/include/mc-sys.h
new file mode 100644
index 000000000..cb3b5a296
--- /dev/null
+++ b/drivers/staging/fsl-mc/include/mc-sys.h
@@ -0,0 +1,76 @@
+/* Copyright 2013-2014 Freescale Semiconductor Inc.
+ *
+ * Interface of the I/O services to send MC commands to the MC hardware
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of the above-listed copyright holders nor the
+ * names of any contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ *
+ * ALTERNATIVELY, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") as published by the Free Software
+ * Foundation, either version 2 of that License or (at your option) any
+ * later version.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT HOLDERS OR CONTRIBUTORS 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.
+ */
+
+#ifndef _FSL_MC_SYS_H
+#define _FSL_MC_SYS_H
+
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/io.h>
+#include <linux/dma-mapping.h>
+
+struct fsl_mc_resource;
+struct mc_command;
+
+/**
+ * struct fsl_mc_io - MC I/O object to be passed-in to mc_send_command()
+ * @dev: device associated with this Mc I/O object
+ * @flags: flags for mc_send_command()
+ * @portal_size: MC command portal size in bytes
+ * @portal_phys_addr: MC command portal physical address
+ * @portal_virt_addr: MC command portal virtual address
+ * @resource: generic resource associated with the MC portal if
+ * the MC portal came from a resource pool, or NULL if the MC portal
+ * is permanently bound to a device (e.g., a DPRC)
+ */
+struct fsl_mc_io {
+ struct device *dev;
+ uint32_t flags;
+ uint32_t portal_size;
+ phys_addr_t portal_phys_addr;
+ void __iomem *portal_virt_addr;
+ struct fsl_mc_resource *resource;
+};
+
+int __must_check fsl_create_mc_io(struct device *dev,
+ phys_addr_t mc_portal_phys_addr,
+ uint32_t mc_portal_size,
+ struct fsl_mc_resource *resource,
+ uint32_t flags, struct fsl_mc_io **new_mc_io);
+
+void fsl_destroy_mc_io(struct fsl_mc_io *mc_io);
+
+int mc_send_command(struct fsl_mc_io *mc_io, struct mc_command *cmd);
+
+#endif /* _FSL_MC_SYS_H */
diff --git a/drivers/staging/fsl-mc/include/mc.h b/drivers/staging/fsl-mc/include/mc.h
new file mode 100644
index 000000000..fa02ef052
--- /dev/null
+++ b/drivers/staging/fsl-mc/include/mc.h
@@ -0,0 +1,201 @@
+/*
+ * Freescale Management Complex (MC) bus public interface
+ *
+ * Copyright (C) 2014 Freescale Semiconductor, Inc.
+ * Author: German Rivera <German.Rivera@freescale.com>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+#ifndef _FSL_MC_H_
+#define _FSL_MC_H_
+
+#include <linux/device.h>
+#include <linux/mod_devicetable.h>
+#include <linux/list.h>
+#include "../include/dprc.h"
+
+#define FSL_MC_VENDOR_FREESCALE 0x1957
+
+struct fsl_mc_device;
+struct fsl_mc_io;
+
+/**
+ * struct fsl_mc_driver - MC object device driver object
+ * @driver: Generic device driver
+ * @match_id_table: table of supported device matching Ids
+ * @probe: Function called when a device is added
+ * @remove: Function called when a device is removed
+ * @shutdown: Function called at shutdown time to quiesce the device
+ * @suspend: Function called when a device is stopped
+ * @resume: Function called when a device is resumed
+ *
+ * Generic DPAA device driver object for device drivers that are registered
+ * with a DPRC bus. This structure is to be embedded in each device-specific
+ * driver structure.
+ */
+struct fsl_mc_driver {
+ struct device_driver driver;
+ const struct fsl_mc_device_match_id *match_id_table;
+ int (*probe)(struct fsl_mc_device *dev);
+ int (*remove)(struct fsl_mc_device *dev);
+ void (*shutdown)(struct fsl_mc_device *dev);
+ int (*suspend)(struct fsl_mc_device *dev, pm_message_t state);
+ int (*resume)(struct fsl_mc_device *dev);
+};
+
+#define to_fsl_mc_driver(_drv) \
+ container_of(_drv, struct fsl_mc_driver, driver)
+
+/**
+ * struct fsl_mc_device_match_id - MC object device Id entry for driver matching
+ * @vendor: vendor ID
+ * @obj_type: MC object type
+ * @ver_major: MC object version major number
+ * @ver_minor: MC object version minor number
+ *
+ * Type of entries in the "device Id" table for MC object devices supported by
+ * a MC object device driver. The last entry of the table has vendor set to 0x0
+ */
+struct fsl_mc_device_match_id {
+ uint16_t vendor;
+ const char obj_type[16];
+ uint32_t ver_major;
+ uint32_t ver_minor;
+};
+
+/**
+ * enum fsl_mc_pool_type - Types of allocatable MC bus resources
+ *
+ * Entries in these enum are used as indices in the array of resource
+ * pools of an fsl_mc_bus object.
+ */
+enum fsl_mc_pool_type {
+ FSL_MC_POOL_DPMCP = 0x0, /* corresponds to "dpmcp" in the MC */
+ FSL_MC_POOL_DPBP, /* corresponds to "dpbp" in the MC */
+ FSL_MC_POOL_DPCON, /* corresponds to "dpcon" in the MC */
+
+ /*
+ * NOTE: New resource pool types must be added before this entry
+ */
+ FSL_MC_NUM_POOL_TYPES
+};
+
+/**
+ * struct fsl_mc_resource - MC generic resource
+ * @type: type of resource
+ * @id: unique MC resource Id within the resources of the same type
+ * @data: pointer to resource-specific data if the resource is currently
+ * allocated, or NULL if the resource is not currently allocated.
+ * @parent_pool: pointer to the parent resource pool from which this
+ * resource is allocated from.
+ * @node: Node in the free list of the corresponding resource pool
+ *
+ * NOTE: This structure is to be embedded as a field of specific
+ * MC resource structures.
+ */
+struct fsl_mc_resource {
+ enum fsl_mc_pool_type type;
+ int32_t id;
+ void *data;
+ struct fsl_mc_resource_pool *parent_pool;
+ struct list_head node;
+};
+
+/**
+ * Bit masks for a MC object device (struct fsl_mc_device) flags
+ */
+#define FSL_MC_IS_DPRC 0x0001
+
+/**
+ * Default DMA mask for devices on a fsl-mc bus
+ */
+#define FSL_MC_DEFAULT_DMA_MASK (~0ULL)
+
+/**
+ * struct fsl_mc_device - MC object device object
+ * @dev: Linux driver model device object
+ * @dma_mask: Default DMA mask
+ * @flags: MC object device flags
+ * @icid: Isolation context ID for the device
+ * @mc_handle: MC handle for the corresponding MC object opened
+ * @mc_io: Pointer to MC IO object assigned to this device or
+ * NULL if none.
+ * @obj_desc: MC description of the DPAA device
+ * @regions: pointer to array of MMIO region entries
+ * @resource: generic resource associated with this MC object device, if any.
+ *
+ * Generic device object for MC object devices that are "attached" to a
+ * MC bus.
+ *
+ * NOTES:
+ * - For a non-DPRC object its icid is the same as its parent DPRC's icid.
+ * - The SMMU notifier callback gets invoked after device_add() has been
+ * called for an MC object device, but before the device-specific probe
+ * callback gets called.
+ * - DP_OBJ_DPRC objects are the only MC objects that have built-in MC
+ * portals. For all other MC objects, their device drivers are responsible for
+ * allocating MC portals for them by calling fsl_mc_portal_allocate().
+ * - Some types of MC objects (e.g., DP_OBJ_DPBP, DP_OBJ_DPCON) are
+ * treated as resources that can be allocated/deallocated from the
+ * corresponding resource pool in the object's parent DPRC, using the
+ * fsl_mc_object_allocate()/fsl_mc_object_free() functions. These MC objects
+ * are known as "allocatable" objects. For them, the corresponding
+ * fsl_mc_device's 'resource' points to the associated resource object.
+ * For MC objects that are not allocatable (e.g., DP_OBJ_DPRC, DP_OBJ_DPNI),
+ * 'resource' is NULL.
+ */
+struct fsl_mc_device {
+ struct device dev;
+ uint64_t dma_mask;
+ uint16_t flags;
+ uint16_t icid;
+ uint16_t mc_handle;
+ struct fsl_mc_io *mc_io;
+ struct dprc_obj_desc obj_desc;
+ struct resource *regions;
+ struct fsl_mc_resource *resource;
+};
+
+#define to_fsl_mc_device(_dev) \
+ container_of(_dev, struct fsl_mc_device, dev)
+
+/*
+ * module_fsl_mc_driver() - Helper macro for drivers that don't do
+ * anything special in module init/exit. This eliminates a lot of
+ * boilerplate. Each module may only use this macro once, and
+ * calling it replaces module_init() and module_exit()
+ */
+#define module_fsl_mc_driver(__fsl_mc_driver) \
+ module_driver(__fsl_mc_driver, fsl_mc_driver_register, \
+ fsl_mc_driver_unregister)
+
+/*
+ * Macro to avoid include chaining to get THIS_MODULE
+ */
+#define fsl_mc_driver_register(drv) \
+ __fsl_mc_driver_register(drv, THIS_MODULE)
+
+int __must_check __fsl_mc_driver_register(struct fsl_mc_driver *fsl_mc_driver,
+ struct module *owner);
+
+void fsl_mc_driver_unregister(struct fsl_mc_driver *driver);
+
+int __must_check fsl_mc_portal_allocate(struct fsl_mc_device *mc_dev,
+ uint16_t mc_io_flags,
+ struct fsl_mc_io **new_mc_io);
+
+void fsl_mc_portal_free(struct fsl_mc_io *mc_io);
+
+int fsl_mc_portal_reset(struct fsl_mc_io *mc_io);
+
+int __must_check fsl_mc_object_allocate(struct fsl_mc_device *mc_dev,
+ enum fsl_mc_pool_type pool_type,
+ struct fsl_mc_device **new_mc_adev);
+
+void fsl_mc_object_free(struct fsl_mc_device *mc_adev);
+
+extern struct bus_type fsl_mc_bus_type;
+
+#endif /* _FSL_MC_H_ */
diff --git a/drivers/staging/ft1000/Kconfig b/drivers/staging/ft1000/Kconfig
new file mode 100644
index 000000000..c54b4e83d
--- /dev/null
+++ b/drivers/staging/ft1000/Kconfig
@@ -0,0 +1,22 @@
+config FT1000
+ tristate "Drivers for Flarion ft1000 devices"
+
+if FT1000
+
+config FT1000_USB
+ tristate "Driver for ft1000 usb devices."
+ depends on USB
+ depends on NET
+ help
+ Say Y if you want to have support for Qleadtek FLASH-OFDM USB Modem [LR7F04],
+ Qleadtek Express Card or Leadtek Multi-band modem HSDPA.
+
+config FT1000_PCMCIA
+ tristate "Driver for ft1000 pcmcia device."
+ depends on PCMCIA
+ depends on NET
+ help
+ Say Y if you want to have support for Flarion card also called
+ Multimedia Net Card.
+
+endif
diff --git a/drivers/staging/ft1000/Makefile b/drivers/staging/ft1000/Makefile
new file mode 100644
index 000000000..3e987770a
--- /dev/null
+++ b/drivers/staging/ft1000/Makefile
@@ -0,0 +1,3 @@
+obj-$(CONFIG_FT1000_USB) += ft1000-usb/
+obj-$(CONFIG_FT1000_PCMCIA) += ft1000-pcmcia/
+
diff --git a/drivers/staging/ft1000/TODO b/drivers/staging/ft1000/TODO
new file mode 100644
index 000000000..1d346bc4f
--- /dev/null
+++ b/drivers/staging/ft1000/TODO
@@ -0,0 +1,9 @@
+TODO:
+ - checkpatch.pl cleanups
+ - coding style
+ - sparse fixes
+ - adapt to latest usb and pcmcia api changes
+ - change firmware loading for usb driver to proper kernel method (request_firmware)
+
+Please send patches to Greg Kroah-Hartman <greg@kroah.com> and
+Cc: Marek Belisko <marek.belisko@gmail.com>
diff --git a/drivers/staging/ft1000/ft1000-pcmcia/Makefile b/drivers/staging/ft1000/ft1000-pcmcia/Makefile
new file mode 100644
index 000000000..715de3f00
--- /dev/null
+++ b/drivers/staging/ft1000/ft1000-pcmcia/Makefile
@@ -0,0 +1,2 @@
+obj-$(CONFIG_FT1000_PCMCIA) = ft1000_pcmcia.o
+ft1000_pcmcia-y := ft1000_hw.o ft1000_dnld.o ft1000_cs.o
diff --git a/drivers/staging/ft1000/ft1000-pcmcia/boot.h b/drivers/staging/ft1000/ft1000-pcmcia/boot.h
new file mode 100644
index 000000000..37b7901c3
--- /dev/null
+++ b/drivers/staging/ft1000/ft1000-pcmcia/boot.h
@@ -0,0 +1,34 @@
+/*---------------------------------------------------------------------------
+ FT1000 driver for Flarion Flash OFDM NIC Device
+
+ Copyright (C) 2002 Flarion Technologies, All rights reserved.
+
+ This program is free software; you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by the Free
+ Software Foundation; either version 2 of the License, or (at your option) any
+ later version. This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ 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.
+ ---------------------------------------------------------------------------
+
+ File: boot.h
+
+ Description: boatloader
+
+ History:
+ 1/11/05 Whc Ported to Linux.
+
+ ---------------------------------------------------------------------------*/
+#ifndef _BOOTH_
+#define _BOOTH_
+
+/* Official bootloader */
+static unsigned char bootimage[] = {
+ /*(DEBLOBBED)*/
+};
+
+#endif
diff --git a/drivers/staging/ft1000/ft1000-pcmcia/ft1000.h b/drivers/staging/ft1000/ft1000-pcmcia/ft1000.h
new file mode 100644
index 000000000..5992670f7
--- /dev/null
+++ b/drivers/staging/ft1000/ft1000-pcmcia/ft1000.h
@@ -0,0 +1,71 @@
+/*---------------------------------------------------------------------------
+ FT1000 driver for Flarion Flash OFDM NIC Device
+
+ Copyright (C) 2002 Flarion Technologies, All rights reserved.
+
+ This program is free software; you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by the Free
+ Software Foundation; either version 2 of the License, or (at your option) any
+ later version. This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ 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.
+ ---------------------------------------------------------------------------
+ Description: Common structures and defines
+ ---------------------------------------------------------------------------*/
+#ifndef _FT1000H_
+#define _FT1000H_
+
+#include "../ft1000.h"
+
+#define FT1000_DRV_VER 0x01010300
+
+#define FT1000_DPRAM_BASE 0x0000 /* Dual Port RAM starting offset */
+
+/*
+ * Maximum number of occurrence of pseudo header errors before resetting PC
+ * Card.
+ */
+#define MAX_PH_ERR 300
+
+#define SUCCESS 0x00
+#define FAILURE 0x01
+
+struct ft1000_pcmcia {
+ int PktIntfErr;
+ u16 packetseqnum;
+ void *link;
+};
+
+struct pcmcia_device;
+struct net_device;
+extern struct net_device *init_ft1000_card(struct pcmcia_device *link,
+ void *ft1000_reset);
+extern void stop_ft1000_card(struct net_device *dev);
+extern int card_download(struct net_device *dev, const u8 *pFileStart,
+ size_t FileLength);
+
+extern u16 ft1000_read_dpram(struct net_device *dev, int offset);
+extern void card_bootload(struct net_device *dev);
+extern u16 ft1000_read_dpram_mag_16(struct net_device *dev, int offset,
+ int Index);
+extern u32 ft1000_read_dpram_mag_32(struct net_device *dev, int offset);
+void ft1000_write_dpram_mag_32(struct net_device *dev, int offset, u32 value);
+
+/* Read the value of a given ASIC register. */
+static inline u16 ft1000_read_reg(struct net_device *dev, u16 offset)
+{
+ return inw(dev->base_addr + offset);
+}
+
+/* Set the value of a given ASIC register. */
+static inline void ft1000_write_reg(struct net_device *dev, u16 offset,
+ u16 value)
+{
+ outw(value, dev->base_addr + offset);
+}
+
+#endif
diff --git a/drivers/staging/ft1000/ft1000-pcmcia/ft1000_cs.c b/drivers/staging/ft1000/ft1000-pcmcia/ft1000_cs.c
new file mode 100644
index 000000000..e5cc5bedf
--- /dev/null
+++ b/drivers/staging/ft1000/ft1000-pcmcia/ft1000_cs.c
@@ -0,0 +1,158 @@
+/*---------------------------------------------------------------------------
+ FT1000 driver for Flarion Flash OFDM NIC Device
+
+ Copyright (C) 1999 David A. Hinds. All Rights Reserved.
+ Copyright (C) 2002 Flarion Technologies, All rights reserved.
+ Copyright (C) 2006 Patrik Ostrihon, All rights reserved.
+ Copyright (C) 2006 ProWeb Consulting, a.s, All rights reserved.
+
+ The initial developer of the original code is David A. Hinds
+ <dahinds@users.sourceforge.net>. Portions created by David A. Hinds.
+
+ This file was modified to support the Flarion Flash OFDM NIC Device
+ by Wai Chan (w.chan@flarion.com).
+
+ Port for kernel 2.6 created by Patrik Ostrihon (patrik.ostrihon@pwc.sk)
+
+ This program is free software; you can redistribute it and/or 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/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/netdevice.h>
+#include <pcmcia/cistpl.h>
+#include <pcmcia/ds.h>
+
+/*====================================================================*/
+
+MODULE_AUTHOR("Wai Chan");
+MODULE_DESCRIPTION("FT1000 PCMCIA driver");
+MODULE_LICENSE("GPL");
+
+/*====================================================================*/
+
+static int ft1000_config(struct pcmcia_device *link);
+static void ft1000_detach(struct pcmcia_device *link);
+static int ft1000_attach(struct pcmcia_device *link);
+
+#include "ft1000.h"
+
+/*====================================================================*/
+
+static void ft1000_reset(struct pcmcia_device *link)
+{
+ pcmcia_reset_card(link->socket);
+}
+
+static int ft1000_attach(struct pcmcia_device *link)
+{
+ link->priv = NULL;
+ link->config_flags |= CONF_ENABLE_IRQ | CONF_AUTO_SET_IO;
+
+ return ft1000_config(link);
+}
+
+static void ft1000_detach(struct pcmcia_device *link)
+{
+ struct net_device *dev = link->priv;
+
+ if (dev)
+ stop_ft1000_card(dev);
+
+ pcmcia_disable_device(link);
+ free_netdev(dev);
+}
+
+static int ft1000_confcheck(struct pcmcia_device *link, void *priv_data)
+{
+ return pcmcia_request_io(link);
+}
+
+/*======================================================================
+
+ ft1000_config() is scheduled to run after a CARD_INSERTION event
+ is received, to configure the PCMCIA socket, and to make the
+ device available to the system.
+
+ ======================================================================*/
+
+static int ft1000_config(struct pcmcia_device *link)
+{
+ int ret;
+
+ dev_dbg(&link->dev, "ft1000_cs: ft1000_config(0x%p)\n", link);
+
+ /* setup IO window */
+ ret = pcmcia_loop_config(link, ft1000_confcheck, NULL);
+ if (ret) {
+ dev_err(&link->dev, "Could not configure pcmcia\n");
+ return -ENODEV;
+ }
+
+ /* configure device */
+ ret = pcmcia_enable_device(link);
+ if (ret) {
+ dev_err(&link->dev, "Could not enable pcmcia\n");
+ goto failed;
+ }
+
+ link->priv = init_ft1000_card(link, &ft1000_reset);
+ if (!link->priv) {
+ dev_err(&link->dev, "Could not register as network device\n");
+ goto failed;
+ }
+
+ /* Finally, report what we've done */
+
+ return 0;
+failed:
+ pcmcia_disable_device(link);
+ return -ENODEV;
+}
+
+static int ft1000_suspend(struct pcmcia_device *link)
+{
+ struct net_device *dev = link->priv;
+
+ if (link->open)
+ netif_device_detach(dev);
+ return 0;
+}
+
+static int ft1000_resume(struct pcmcia_device *link)
+{
+ return 0;
+}
+
+/*====================================================================*/
+
+static const struct pcmcia_device_id ft1000_ids[] = {
+ PCMCIA_DEVICE_MANF_CARD(0x02cc, 0x0100),
+ PCMCIA_DEVICE_MANF_CARD(0x02cc, 0x1000),
+ PCMCIA_DEVICE_MANF_CARD(0x02cc, 0x1300),
+ PCMCIA_DEVICE_NULL,
+};
+
+MODULE_DEVICE_TABLE(pcmcia, ft1000_ids);
+
+static struct pcmcia_driver ft1000_cs_driver = {
+ .owner = THIS_MODULE,
+ .name = "ft1000_cs",
+ .probe = ft1000_attach,
+ .remove = ft1000_detach,
+ .id_table = ft1000_ids,
+ .suspend = ft1000_suspend,
+ .resume = ft1000_resume,
+};
+
+module_pcmcia_driver(ft1000_cs_driver);
diff --git a/drivers/staging/ft1000/ft1000-pcmcia/ft1000_dnld.c b/drivers/staging/ft1000/ft1000-pcmcia/ft1000_dnld.c
new file mode 100644
index 000000000..83683e9a1
--- /dev/null
+++ b/drivers/staging/ft1000/ft1000-pcmcia/ft1000_dnld.c
@@ -0,0 +1,769 @@
+/*---------------------------------------------------------------------------
+ FT1000 driver for Flarion Flash OFDM NIC Device
+
+ Copyright (C) 2002 Flarion Technologies, All rights reserved.
+
+ This program is free software; you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by the Free
+ Software Foundation; either version 2 of the License, or (at your option) any
+ later version. This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ 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.
+ --------------------------------------------------------------------------
+
+ Description: This module will handshake with the DSP bootloader to
+ download the DSP runtime image.
+
+ ---------------------------------------------------------------------------*/
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#define __KERNEL_SYSCALLS__
+
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <linux/unistd.h>
+#include <linux/netdevice.h>
+#include <linux/timer.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/uaccess.h>
+#include <linux/vmalloc.h>
+
+#include "ft1000.h"
+#include "boot.h"
+
+#define MAX_DSP_WAIT_LOOPS 100
+#define DSP_WAIT_SLEEP_TIME 1 /* 1 millisecond */
+
+#define MAX_LENGTH 0x7f0
+
+#define DWNLD_MAG_HANDSHAKE_LOC 0x00
+#define DWNLD_MAG_TYPE_LOC 0x01
+#define DWNLD_MAG_SIZE_LOC 0x02
+#define DWNLD_MAG_PS_HDR_LOC 0x03
+
+#define DWNLD_HANDSHAKE_LOC 0x02
+#define DWNLD_TYPE_LOC 0x04
+#define DWNLD_SIZE_MSW_LOC 0x06
+#define DWNLD_SIZE_LSW_LOC 0x08
+#define DWNLD_PS_HDR_LOC 0x0A
+
+#define HANDSHAKE_TIMEOUT_VALUE 0xF1F1
+#define HANDSHAKE_RESET_VALUE 0xFEFE /* When DSP requests startover */
+#define HANDSHAKE_DSP_BL_READY 0xFEFE /* At start DSP writes this when bootloader ready */
+#define HANDSHAKE_DRIVER_READY 0xFFFF /* Driver writes after receiving 0xFEFE */
+#define HANDSHAKE_SEND_DATA 0x0000 /* DSP writes this when ready for more data */
+
+#define HANDSHAKE_REQUEST 0x0001 /* Request from DSP */
+#define HANDSHAKE_RESPONSE 0x0000 /* Satisfied DSP request */
+
+#define REQUEST_CODE_LENGTH 0x0000
+#define REQUEST_RUN_ADDRESS 0x0001
+#define REQUEST_CODE_SEGMENT 0x0002 /* In WORD count */
+#define REQUEST_DONE_BL 0x0003
+#define REQUEST_DONE_CL 0x0004
+#define REQUEST_VERSION_INFO 0x0005
+#define REQUEST_CODE_BY_VERSION 0x0006
+#define REQUEST_MAILBOX_DATA 0x0007
+#define REQUEST_FILE_CHECKSUM 0x0008
+
+#define STATE_START_DWNLD 0x01
+#define STATE_BOOT_DWNLD 0x02
+#define STATE_CODE_DWNLD 0x03
+#define STATE_DONE_DWNLD 0x04
+#define STATE_SECTION_PROV 0x05
+#define STATE_DONE_PROV 0x06
+#define STATE_DONE_FILE 0x07
+
+u16 get_handshake(struct net_device *dev, u16 expected_value);
+void put_handshake(struct net_device *dev, u16 handshake_value);
+u16 get_request_type(struct net_device *dev);
+long get_request_value(struct net_device *dev);
+void put_request_value(struct net_device *dev, long lvalue);
+u16 hdr_checksum(struct pseudo_hdr *pHdr);
+
+struct dsp_file_hdr {
+ u32 version_id; /* Version ID of this image format. */
+ u32 package_id; /* Package ID of code release. */
+ u32 build_date; /* Date/time stamp when file was built. */
+ u32 commands_offset; /* Offset to attached commands in Pseudo Hdr format. */
+ u32 loader_offset; /* Offset to bootloader code. */
+ u32 loader_code_address; /* Start address of bootloader. */
+ u32 loader_code_end; /* Where bootloader code ends. */
+ u32 loader_code_size;
+ u32 version_data_offset; /* Offset were scrambled version data begins. */
+ u32 version_data_size; /* Size, in words, of scrambled version data. */
+ u32 nDspImages; /* Number of DSP images in file. */
+} __packed;
+
+struct dsp_image_info {
+ u32 coff_date; /* Date/time when DSP Coff image was built. */
+ u32 begin_offset; /* Offset in file where image begins. */
+ u32 end_offset; /* Offset in file where image begins. */
+ u32 run_address; /* On chip Start address of DSP code. */
+ u32 image_size; /* Size of image. */
+ u32 version; /* Embedded version # of DSP code. */
+ unsigned short checksum; /* Dsp File checksum */
+ unsigned short pad1;
+} __packed;
+
+void card_bootload(struct net_device *dev)
+{
+ struct ft1000_info *info = netdev_priv(dev);
+ unsigned long flags;
+ u32 *pdata;
+ u32 size;
+ u32 i;
+ u32 templong;
+
+ netdev_dbg(dev, "card_bootload is called\n");
+
+ pdata = (u32 *)bootimage;
+ size = sizeof(bootimage);
+
+ /* check for odd word */
+ if (size & 0x0003)
+ size += 4;
+
+ /* Provide mutual exclusive access while reading ASIC registers. */
+ spin_lock_irqsave(&info->dpram_lock, flags);
+
+ /* need to set i/o base address initially and hardware will autoincrement */
+ ft1000_write_reg(dev, FT1000_REG_DPRAM_ADDR, FT1000_DPRAM_BASE);
+ /* write bytes */
+ for (i = 0; i < (size >> 2); i++) {
+ templong = *pdata++;
+ outl(templong, dev->base_addr + FT1000_REG_MAG_DPDATA);
+ }
+
+ spin_unlock_irqrestore(&info->dpram_lock, flags);
+}
+
+u16 get_handshake(struct net_device *dev, u16 expected_value)
+{
+ struct ft1000_info *info = netdev_priv(dev);
+ u16 handshake;
+ u32 tempx;
+ int loopcnt;
+
+ loopcnt = 0;
+ while (loopcnt < MAX_DSP_WAIT_LOOPS) {
+ if (info->AsicID == ELECTRABUZZ_ID) {
+ ft1000_write_reg(dev, FT1000_REG_DPRAM_ADDR,
+ DWNLD_HANDSHAKE_LOC);
+
+ handshake = ft1000_read_reg(dev, FT1000_REG_DPRAM_DATA);
+ } else {
+ tempx =
+ ntohl(ft1000_read_dpram_mag_32
+ (dev, DWNLD_MAG_HANDSHAKE_LOC));
+ handshake = (u16)tempx;
+ }
+
+ if ((handshake == expected_value)
+ || (handshake == HANDSHAKE_RESET_VALUE)) {
+ return handshake;
+ }
+ loopcnt++;
+ mdelay(DSP_WAIT_SLEEP_TIME);
+
+ }
+
+ return HANDSHAKE_TIMEOUT_VALUE;
+
+}
+
+void put_handshake(struct net_device *dev, u16 handshake_value)
+{
+ struct ft1000_info *info = netdev_priv(dev);
+ u32 tempx;
+
+ if (info->AsicID == ELECTRABUZZ_ID) {
+ ft1000_write_reg(dev, FT1000_REG_DPRAM_ADDR,
+ DWNLD_HANDSHAKE_LOC);
+ ft1000_write_reg(dev, FT1000_REG_DPRAM_DATA, handshake_value); /* Handshake */
+ } else {
+ tempx = (u32)handshake_value;
+ tempx = ntohl(tempx);
+ ft1000_write_dpram_mag_32(dev, DWNLD_MAG_HANDSHAKE_LOC, tempx); /* Handshake */
+ }
+}
+
+u16 get_request_type(struct net_device *dev)
+{
+ struct ft1000_info *info = netdev_priv(dev);
+ u16 request_type;
+ u32 tempx;
+
+ if (info->AsicID == ELECTRABUZZ_ID) {
+ ft1000_write_reg(dev, FT1000_REG_DPRAM_ADDR, DWNLD_TYPE_LOC);
+ request_type = ft1000_read_reg(dev, FT1000_REG_DPRAM_DATA);
+ } else {
+ tempx = ft1000_read_dpram_mag_32(dev, DWNLD_MAG_TYPE_LOC);
+ tempx = ntohl(tempx);
+ request_type = (u16)tempx;
+ }
+
+ return request_type;
+
+}
+
+long get_request_value(struct net_device *dev)
+{
+ struct ft1000_info *info = netdev_priv(dev);
+ long value;
+ u16 w_val;
+
+ if (info->AsicID == ELECTRABUZZ_ID) {
+ ft1000_write_reg(dev, FT1000_REG_DPRAM_ADDR,
+ DWNLD_SIZE_MSW_LOC);
+
+ w_val = ft1000_read_reg(dev, FT1000_REG_DPRAM_DATA);
+
+ value = (long)(w_val << 16);
+
+ ft1000_write_reg(dev, FT1000_REG_DPRAM_ADDR,
+ DWNLD_SIZE_LSW_LOC);
+
+ w_val = ft1000_read_reg(dev, FT1000_REG_DPRAM_DATA);
+
+ value = (long)(value | w_val);
+ } else {
+ value = ft1000_read_dpram_mag_32(dev, DWNLD_MAG_SIZE_LOC);
+ value = ntohl(value);
+ }
+
+ return value;
+
+}
+
+void put_request_value(struct net_device *dev, long lvalue)
+{
+ struct ft1000_info *info = netdev_priv(dev);
+ u16 size;
+ u32 tempx;
+
+ if (info->AsicID == ELECTRABUZZ_ID) {
+ size = (u16) (lvalue >> 16);
+
+ ft1000_write_reg(dev, FT1000_REG_DPRAM_ADDR,
+ DWNLD_SIZE_MSW_LOC);
+
+ ft1000_write_reg(dev, FT1000_REG_DPRAM_DATA, size);
+
+ size = (u16) (lvalue);
+
+ ft1000_write_reg(dev, FT1000_REG_DPRAM_ADDR,
+ DWNLD_SIZE_LSW_LOC);
+
+ ft1000_write_reg(dev, FT1000_REG_DPRAM_DATA, size);
+ } else {
+ tempx = ntohl(lvalue);
+ ft1000_write_dpram_mag_32(dev, DWNLD_MAG_SIZE_LOC, tempx); /* Handshake */
+ }
+
+}
+
+u16 hdr_checksum(struct pseudo_hdr *pHdr)
+{
+ u16 *usPtr = (u16 *)pHdr;
+ u16 chksum;
+
+ chksum = (((((usPtr[0] ^ usPtr[1]) ^ usPtr[2]) ^ usPtr[3]) ^
+ usPtr[4]) ^ usPtr[5]) ^ usPtr[6];
+
+ return chksum;
+}
+
+int card_download(struct net_device *dev, const u8 *pFileStart,
+ size_t FileLength)
+{
+ struct ft1000_info *info = netdev_priv(dev);
+ int Status = SUCCESS;
+ u32 uiState;
+ u16 handshake;
+ struct pseudo_hdr *pHdr;
+ u16 usHdrLength;
+ long word_length;
+ u16 request;
+ u16 temp;
+ struct prov_record *pprov_record;
+ u8 *pbuffer;
+ struct dsp_file_hdr *pFileHdr5;
+ struct dsp_image_info *pDspImageInfoV6 = NULL;
+ long requested_version;
+ bool bGoodVersion = false;
+ struct drv_msg *pMailBoxData;
+ u16 *pUsData = NULL;
+ u16 *pUsFile = NULL;
+ u8 *pUcFile = NULL;
+ u8 *pBootEnd = NULL;
+ u8 *pCodeEnd = NULL;
+ int imageN;
+ long file_version;
+ long loader_code_address = 0;
+ long loader_code_size = 0;
+ long run_address = 0;
+ long run_size = 0;
+ unsigned long flags;
+ unsigned long templong;
+ unsigned long image_chksum = 0;
+
+ file_version = *(long *)pFileStart;
+ if (file_version != 6) {
+ pr_err("unsupported firmware version %ld\n", file_version);
+ Status = FAILURE;
+ }
+
+ uiState = STATE_START_DWNLD;
+
+ pFileHdr5 = (struct dsp_file_hdr *)pFileStart;
+
+ pUsFile = (u16 *) ((long)pFileStart + pFileHdr5->loader_offset);
+ pUcFile = (u8 *) ((long)pFileStart + pFileHdr5->loader_offset);
+ pBootEnd = (u8 *) ((long)pFileStart + pFileHdr5->loader_code_end);
+ loader_code_address = pFileHdr5->loader_code_address;
+ loader_code_size = pFileHdr5->loader_code_size;
+ bGoodVersion = false;
+
+ while ((Status == SUCCESS) && (uiState != STATE_DONE_FILE)) {
+
+ switch (uiState) {
+ case STATE_START_DWNLD:
+
+ handshake = get_handshake(dev, HANDSHAKE_DSP_BL_READY);
+
+ if (handshake == HANDSHAKE_DSP_BL_READY)
+ put_handshake(dev, HANDSHAKE_DRIVER_READY);
+ else
+ Status = FAILURE;
+
+ uiState = STATE_BOOT_DWNLD;
+
+ break;
+
+ case STATE_BOOT_DWNLD:
+ handshake = get_handshake(dev, HANDSHAKE_REQUEST);
+ if (handshake == HANDSHAKE_REQUEST) {
+ /*
+ * Get type associated with the request.
+ */
+ request = get_request_type(dev);
+ switch (request) {
+ case REQUEST_RUN_ADDRESS:
+ put_request_value(dev,
+ loader_code_address);
+ break;
+ case REQUEST_CODE_LENGTH:
+ put_request_value(dev,
+ loader_code_size);
+ break;
+ case REQUEST_DONE_BL:
+ /* Reposition ptrs to beginning of code section */
+ pUsFile = (u16 *) ((long)pBootEnd);
+ pUcFile = (u8 *) ((long)pBootEnd);
+ uiState = STATE_CODE_DWNLD;
+ break;
+ case REQUEST_CODE_SEGMENT:
+ word_length = get_request_value(dev);
+ if (word_length > MAX_LENGTH) {
+ Status = FAILURE;
+ break;
+ }
+ if ((word_length * 2 + (long)pUcFile) >
+ (long)pBootEnd) {
+ /*
+ * Error, beyond boot code range.
+ */
+ Status = FAILURE;
+ break;
+ }
+ /* Provide mutual exclusive access while reading ASIC registers. */
+ spin_lock_irqsave(&info->dpram_lock,
+ flags);
+ /*
+ * Position ASIC DPRAM auto-increment pointer.
+ */
+ outw(DWNLD_MAG_PS_HDR_LOC,
+ dev->base_addr +
+ FT1000_REG_DPRAM_ADDR);
+ if (word_length & 0x01)
+ word_length++;
+ word_length = word_length / 2;
+
+ for (; word_length > 0; word_length--) { /* In words */
+ templong = *pUsFile++;
+ templong |=
+ (*pUsFile++ << 16);
+ pUcFile += 4;
+ outl(templong,
+ dev->base_addr +
+ FT1000_REG_MAG_DPDATAL);
+ }
+ spin_unlock_irqrestore(&info->
+ dpram_lock,
+ flags);
+ break;
+ default:
+ Status = FAILURE;
+ break;
+ }
+ put_handshake(dev, HANDSHAKE_RESPONSE);
+ } else {
+ Status = FAILURE;
+ }
+
+ break;
+
+ case STATE_CODE_DWNLD:
+ handshake = get_handshake(dev, HANDSHAKE_REQUEST);
+ if (handshake == HANDSHAKE_REQUEST) {
+ /*
+ * Get type associated with the request.
+ */
+ request = get_request_type(dev);
+ switch (request) {
+ case REQUEST_FILE_CHECKSUM:
+ netdev_dbg(dev,
+ "ft1000_dnld: REQUEST_FOR_CHECKSUM\n");
+ put_request_value(dev, image_chksum);
+ break;
+ case REQUEST_RUN_ADDRESS:
+ if (bGoodVersion) {
+ put_request_value(dev,
+ run_address);
+ } else {
+ Status = FAILURE;
+ break;
+ }
+ break;
+ case REQUEST_CODE_LENGTH:
+ if (bGoodVersion) {
+ put_request_value(dev,
+ run_size);
+ } else {
+ Status = FAILURE;
+ break;
+ }
+ break;
+ case REQUEST_DONE_CL:
+ /* Reposition ptrs to beginning of provisioning section */
+ pUsFile = (u16 *) ((long)pFileStart + pFileHdr5->commands_offset);
+ pUcFile = (u8 *) ((long)pFileStart + pFileHdr5->commands_offset);
+ uiState = STATE_DONE_DWNLD;
+ break;
+ case REQUEST_CODE_SEGMENT:
+ if (!bGoodVersion) {
+ Status = FAILURE;
+ break;
+ }
+ word_length = get_request_value(dev);
+ if (word_length > MAX_LENGTH) {
+ Status = FAILURE;
+ break;
+ }
+ if ((word_length * 2 + (long)pUcFile) >
+ (long)pCodeEnd) {
+ /*
+ * Error, beyond boot code range.
+ */
+ Status = FAILURE;
+ break;
+ }
+ /*
+ * Position ASIC DPRAM auto-increment pointer.
+ */
+ outw(DWNLD_MAG_PS_HDR_LOC,
+ dev->base_addr +
+ FT1000_REG_DPRAM_ADDR);
+ if (word_length & 0x01)
+ word_length++;
+ word_length = word_length / 2;
+
+ for (; word_length > 0; word_length--) { /* In words */
+ templong = *pUsFile++;
+ templong |=
+ (*pUsFile++ << 16);
+ pUcFile += 4;
+ outl(templong,
+ dev->base_addr +
+ FT1000_REG_MAG_DPDATAL);
+ }
+ break;
+
+ case REQUEST_MAILBOX_DATA:
+ /* Convert length from byte count to word count. Make sure we round up. */
+ word_length =
+ (long)(info->DSPInfoBlklen + 1) / 2;
+ put_request_value(dev, word_length);
+ pMailBoxData =
+ (struct drv_msg *)&info->DSPInfoBlk[0];
+ pUsData =
+ (u16 *)&pMailBoxData->data[0];
+ /* Provide mutual exclusive access while reading ASIC registers. */
+ spin_lock_irqsave(&info->dpram_lock,
+ flags);
+ if (file_version == 5) {
+ /*
+ * Position ASIC DPRAM auto-increment pointer.
+ */
+ ft1000_write_reg(dev,
+ FT1000_REG_DPRAM_ADDR,
+ DWNLD_PS_HDR_LOC);
+
+ for (; word_length > 0; word_length--) { /* In words */
+ temp = ntohs(*pUsData);
+ ft1000_write_reg(dev,
+ FT1000_REG_DPRAM_DATA,
+ temp);
+ pUsData++;
+ }
+ } else {
+ /*
+ * Position ASIC DPRAM auto-increment pointer.
+ */
+ outw(DWNLD_MAG_PS_HDR_LOC,
+ dev->base_addr +
+ FT1000_REG_DPRAM_ADDR);
+ if (word_length & 0x01)
+ word_length++;
+
+ word_length = word_length / 2;
+
+ for (; word_length > 0; word_length--) { /* In words */
+ templong = *pUsData++;
+ templong |=
+ (*pUsData++ << 16);
+ outl(templong,
+ dev->base_addr +
+ FT1000_REG_MAG_DPDATAL);
+ }
+ }
+ spin_unlock_irqrestore(&info->
+ dpram_lock,
+ flags);
+ break;
+
+ case REQUEST_VERSION_INFO:
+ word_length =
+ pFileHdr5->version_data_size;
+ put_request_value(dev, word_length);
+ pUsFile =
+ (u16 *) ((long)pFileStart +
+ pFileHdr5->
+ version_data_offset);
+ /* Provide mutual exclusive access while reading ASIC registers. */
+ spin_lock_irqsave(&info->dpram_lock,
+ flags);
+ /*
+ * Position ASIC DPRAM auto-increment pointer.
+ */
+ outw(DWNLD_MAG_PS_HDR_LOC,
+ dev->base_addr +
+ FT1000_REG_DPRAM_ADDR);
+ if (word_length & 0x01)
+ word_length++;
+ word_length = word_length / 2;
+
+ for (; word_length > 0; word_length--) { /* In words */
+ templong =
+ ntohs(*pUsFile++);
+ temp =
+ ntohs(*pUsFile++);
+ templong |=
+ (temp << 16);
+ outl(templong,
+ dev->base_addr +
+ FT1000_REG_MAG_DPDATAL);
+ }
+ spin_unlock_irqrestore(&info->
+ dpram_lock,
+ flags);
+ break;
+
+ case REQUEST_CODE_BY_VERSION:
+ bGoodVersion = false;
+ requested_version =
+ get_request_value(dev);
+ pDspImageInfoV6 =
+ (struct dsp_image_info *) ((long)
+ pFileStart
+ +
+ sizeof
+ (struct dsp_file_hdr));
+ for (imageN = 0;
+ imageN <
+ pFileHdr5->nDspImages;
+ imageN++) {
+ temp = (u16)
+ (pDspImageInfoV6->
+ version);
+ templong = temp;
+ temp = (u16)
+ (pDspImageInfoV6->
+ version >> 16);
+ templong |=
+ (temp << 16);
+ if (templong ==
+ requested_version) {
+ bGoodVersion =
+ true;
+ pUsFile =
+ (u16
+ *) ((long)
+ pFileStart
+ +
+ pDspImageInfoV6->
+ begin_offset);
+ pUcFile =
+ (u8
+ *) ((long)
+ pFileStart
+ +
+ pDspImageInfoV6->
+ begin_offset);
+ pCodeEnd =
+ (u8
+ *) ((long)
+ pFileStart
+ +
+ pDspImageInfoV6->
+ end_offset);
+ run_address =
+ pDspImageInfoV6->
+ run_address;
+ run_size =
+ pDspImageInfoV6->
+ image_size;
+ image_chksum =
+ (u32)
+ pDspImageInfoV6->
+ checksum;
+ netdev_dbg(dev,
+ "ft1000_dnld: image_chksum = 0x%8x\n",
+ (unsigned
+ int)
+ image_chksum);
+ break;
+ }
+ pDspImageInfoV6++;
+ }
+ if (!bGoodVersion) {
+ /*
+ * Error, beyond boot code range.
+ */
+ Status = FAILURE;
+ break;
+ }
+ break;
+
+ default:
+ Status = FAILURE;
+ break;
+ }
+ put_handshake(dev, HANDSHAKE_RESPONSE);
+ } else {
+ Status = FAILURE;
+ }
+
+ break;
+
+ case STATE_DONE_DWNLD:
+ if (((unsigned long)(pUcFile) - (unsigned long) pFileStart) >=
+ (unsigned long)FileLength) {
+ uiState = STATE_DONE_FILE;
+ break;
+ }
+
+ pHdr = (struct pseudo_hdr *)pUsFile;
+
+ if (pHdr->portdest == 0x80 /* DspOAM */
+ && (pHdr->portsrc == 0x00 /* Driver */
+ || pHdr->portsrc == 0x10 /* FMM */)) {
+ uiState = STATE_SECTION_PROV;
+ } else {
+ netdev_dbg(dev,
+ "Download error: Bad Port IDs in Pseudo Record\n");
+ netdev_dbg(dev, "\t Port Source = 0x%2.2x\n",
+ pHdr->portsrc);
+ netdev_dbg(dev, "\t Port Destination = 0x%2.2x\n",
+ pHdr->portdest);
+ Status = FAILURE;
+ }
+
+ break;
+
+ case STATE_SECTION_PROV:
+
+ pHdr = (struct pseudo_hdr *)pUcFile;
+
+ if (pHdr->checksum == hdr_checksum(pHdr)) {
+ if (pHdr->portdest != 0x80 /* Dsp OAM */) {
+ uiState = STATE_DONE_PROV;
+ break;
+ }
+ usHdrLength = ntohs(pHdr->length); /* Byte length for PROV records */
+
+ /* Get buffer for provisioning data */
+ pbuffer =
+ kmalloc(usHdrLength + sizeof(struct pseudo_hdr),
+ GFP_ATOMIC);
+ if (pbuffer) {
+ memcpy(pbuffer, pUcFile,
+ (u32) (usHdrLength +
+ sizeof(struct pseudo_hdr)));
+ /* link provisioning data */
+ pprov_record =
+ kmalloc(sizeof(struct prov_record),
+ GFP_ATOMIC);
+ if (pprov_record) {
+ pprov_record->pprov_data =
+ pbuffer;
+ list_add_tail(&pprov_record->
+ list,
+ &info->prov_list);
+ /* Move to next entry if available */
+ pUcFile =
+ (u8 *)((unsigned long) pUcFile +
+ (unsigned long) ((usHdrLength + 1) & 0xFFFFFFFE) + sizeof(struct pseudo_hdr));
+ if ((unsigned long) (pUcFile) -
+ (unsigned long) (pFileStart) >=
+ (unsigned long)FileLength) {
+ uiState =
+ STATE_DONE_FILE;
+ }
+ } else {
+ kfree(pbuffer);
+ Status = FAILURE;
+ }
+ } else {
+ Status = FAILURE;
+ }
+ } else {
+ /* Checksum did not compute */
+ Status = FAILURE;
+ }
+
+ break;
+
+ case STATE_DONE_PROV:
+ uiState = STATE_DONE_FILE;
+ break;
+
+ default:
+ Status = FAILURE;
+ break;
+ } /* End Switch */
+
+ } /* End while */
+
+ return Status;
+
+}
diff --git a/drivers/staging/ft1000/ft1000-pcmcia/ft1000_hw.c b/drivers/staging/ft1000/ft1000-pcmcia/ft1000_hw.c
new file mode 100644
index 000000000..0b7739847
--- /dev/null
+++ b/drivers/staging/ft1000/ft1000-pcmcia/ft1000_hw.c
@@ -0,0 +1,2069 @@
+/*---------------------------------------------------------------------------
+ FT1000 driver for Flarion Flash OFDM NIC Device
+
+ Copyright (C) 2002 Flarion Technologies, All rights reserved.
+ Copyright (C) 2006 Patrik Ostrihon, All rights reserved.
+ Copyright (C) 2006 ProWeb Consulting, a.s, All rights reserved.
+
+ This program is free software; you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by the Free
+ Software Foundation; either version 2 of the License, or (at your option) any
+ later version. This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ 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 pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/ptrace.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/timer.h>
+#include <linux/interrupt.h>
+#include <linux/in.h>
+#include <linux/io.h>
+#include <linux/bitops.h>
+
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/if_arp.h>
+#include <linux/ioport.h>
+#include <linux/wait.h>
+#include <linux/vmalloc.h>
+
+#include <linux/firmware.h>
+#include <linux/ethtool.h>
+
+#include <pcmcia/cistpl.h>
+#include <pcmcia/cisreg.h>
+#include <pcmcia/ds.h>
+
+#include <linux/delay.h>
+#include "ft1000.h"
+
+static const struct firmware *fw_entry;
+
+static void ft1000_hbchk(u_long data);
+static struct timer_list poll_timer = {
+ .function = ft1000_hbchk
+};
+
+static u16 cmdbuffer[1024];
+static u8 tempbuffer[1600];
+static u8 ft1000_card_present;
+static u8 flarion_ft1000_cnt;
+
+static irqreturn_t ft1000_interrupt(int irq, void *dev_id);
+static void ft1000_enable_interrupts(struct net_device *dev);
+static void ft1000_disable_interrupts(struct net_device *dev);
+
+/* new kernel */
+MODULE_AUTHOR("");
+MODULE_DESCRIPTION("Support for Flarion Flash OFDM NIC Device. Support for PCMCIA when used with ft1000_cs.");
+MODULE_LICENSE("GPL");
+MODULE_SUPPORTED_DEVICE("FT1000");
+
+#define MAX_RCV_LOOP 100
+
+/*---------------------------------------------------------------------------
+
+ Function: ft1000_read_fifo_len
+ Description: This function will read the ASIC Uplink FIFO status register
+ which will return the number of bytes remaining in the Uplink FIFO.
+ Sixteen bytes are subtracted to make sure that the ASIC does not
+ reach its threshold.
+ Input:
+ dev - network device structure
+ Output:
+ value - number of bytes available in the ASIC Uplink FIFO.
+
+ -------------------------------------------------------------------------*/
+static inline u16 ft1000_read_fifo_len(struct net_device *dev)
+{
+ struct ft1000_info *info = netdev_priv(dev);
+
+ if (info->AsicID == ELECTRABUZZ_ID)
+ return (ft1000_read_reg(dev, FT1000_REG_UFIFO_STAT) - 16);
+ else
+ return (ft1000_read_reg(dev, FT1000_REG_MAG_UFSR) - 16);
+}
+
+/*---------------------------------------------------------------------------
+
+ Function: ft1000_read_dpram
+ Description: This function will read the specific area of dpram
+ (Electrabuzz ASIC only)
+ Input:
+ dev - device structure
+ offset - index of dpram
+ Output:
+ value - value of dpram
+
+ -------------------------------------------------------------------------*/
+u16 ft1000_read_dpram(struct net_device *dev, int offset)
+{
+ struct ft1000_info *info = netdev_priv(dev);
+ unsigned long flags;
+ u16 data;
+
+ /* Provide mutual exclusive access while reading ASIC registers. */
+ spin_lock_irqsave(&info->dpram_lock, flags);
+ ft1000_write_reg(dev, FT1000_REG_DPRAM_ADDR, offset);
+ data = ft1000_read_reg(dev, FT1000_REG_DPRAM_DATA);
+ spin_unlock_irqrestore(&info->dpram_lock, flags);
+
+ return data;
+}
+
+/*---------------------------------------------------------------------------
+
+ Function: ft1000_write_dpram
+ Description: This function will write to a specific area of dpram
+ (Electrabuzz ASIC only)
+ Input:
+ dev - device structure
+ offset - index of dpram
+ value - value to write
+ Output:
+ none.
+
+ -------------------------------------------------------------------------*/
+static inline void ft1000_write_dpram(struct net_device *dev,
+ int offset, u16 value)
+{
+ struct ft1000_info *info = netdev_priv(dev);
+ unsigned long flags;
+
+ /* Provide mutual exclusive access while reading ASIC registers. */
+ spin_lock_irqsave(&info->dpram_lock, flags);
+ ft1000_write_reg(dev, FT1000_REG_DPRAM_ADDR, offset);
+ ft1000_write_reg(dev, FT1000_REG_DPRAM_DATA, value);
+ spin_unlock_irqrestore(&info->dpram_lock, flags);
+}
+
+/*---------------------------------------------------------------------------
+
+ Function: ft1000_read_dpram_mag_16
+ Description: This function will read the specific area of dpram
+ (Magnemite ASIC only)
+ Input:
+ dev - device structure
+ offset - index of dpram
+ Output:
+ value - value of dpram
+
+ -------------------------------------------------------------------------*/
+u16 ft1000_read_dpram_mag_16(struct net_device *dev, int offset, int Index)
+{
+ struct ft1000_info *info = netdev_priv(dev);
+ unsigned long flags;
+ u16 data;
+
+ /* Provide mutual exclusive access while reading ASIC registers. */
+ spin_lock_irqsave(&info->dpram_lock, flags);
+ ft1000_write_reg(dev, FT1000_REG_DPRAM_ADDR, offset);
+ /* check if we want to read upper or lower 32-bit word */
+ if (Index)
+ data = ft1000_read_reg(dev, FT1000_REG_MAG_DPDATAL);
+ else
+ data = ft1000_read_reg(dev, FT1000_REG_MAG_DPDATAH);
+
+ spin_unlock_irqrestore(&info->dpram_lock, flags);
+
+ return data;
+}
+
+/*---------------------------------------------------------------------------
+
+ Function: ft1000_write_dpram_mag_16
+ Description: This function will write to a specific area of dpram
+ (Magnemite ASIC only)
+ Input:
+ dev - device structure
+ offset - index of dpram
+ value - value to write
+ Output:
+ none.
+
+ -------------------------------------------------------------------------*/
+static inline void ft1000_write_dpram_mag_16(struct net_device *dev,
+ int offset, u16 value, int Index)
+{
+ struct ft1000_info *info = netdev_priv(dev);
+ unsigned long flags;
+
+ /* Provide mutual exclusive access while reading ASIC registers. */
+ spin_lock_irqsave(&info->dpram_lock, flags);
+ ft1000_write_reg(dev, FT1000_REG_DPRAM_ADDR, offset);
+ if (Index)
+ ft1000_write_reg(dev, FT1000_REG_MAG_DPDATAL, value);
+ else
+ ft1000_write_reg(dev, FT1000_REG_MAG_DPDATAH, value);
+
+ spin_unlock_irqrestore(&info->dpram_lock, flags);
+}
+
+/*---------------------------------------------------------------------------
+
+ Function: ft1000_read_dpram_mag_32
+ Description: This function will read the specific area of dpram
+ (Magnemite ASIC only)
+ Input:
+ dev - device structure
+ offset - index of dpram
+ Output:
+ value - value of dpram
+
+ -------------------------------------------------------------------------*/
+u32 ft1000_read_dpram_mag_32(struct net_device *dev, int offset)
+{
+ struct ft1000_info *info = netdev_priv(dev);
+ unsigned long flags;
+ u32 data;
+
+ /* Provide mutual exclusive access while reading ASIC registers. */
+ spin_lock_irqsave(&info->dpram_lock, flags);
+ ft1000_write_reg(dev, FT1000_REG_DPRAM_ADDR, offset);
+ data = inl(dev->base_addr + FT1000_REG_MAG_DPDATAL);
+ spin_unlock_irqrestore(&info->dpram_lock, flags);
+
+ return data;
+}
+
+/*---------------------------------------------------------------------------
+
+ Function: ft1000_write_dpram_mag_32
+ Description: This function will write to a specific area of dpram
+ (Magnemite ASIC only)
+ Input:
+ dev - device structure
+ offset - index of dpram
+ value - value to write
+ Output:
+ none.
+
+ -------------------------------------------------------------------------*/
+void ft1000_write_dpram_mag_32(struct net_device *dev, int offset, u32 value)
+{
+ struct ft1000_info *info = netdev_priv(dev);
+ unsigned long flags;
+
+ /* Provide mutual exclusive access while reading ASIC registers. */
+ spin_lock_irqsave(&info->dpram_lock, flags);
+ ft1000_write_reg(dev, FT1000_REG_DPRAM_ADDR, offset);
+ outl(value, dev->base_addr + FT1000_REG_MAG_DPDATAL);
+ spin_unlock_irqrestore(&info->dpram_lock, flags);
+}
+
+/*---------------------------------------------------------------------------
+
+ Function: ft1000_enable_interrupts
+ Description: This function will enable interrupts base on the current
+ interrupt mask.
+ Input:
+ dev - device structure
+ Output:
+ None.
+
+ -------------------------------------------------------------------------*/
+static void ft1000_enable_interrupts(struct net_device *dev)
+{
+ u16 tempword;
+
+ ft1000_write_reg(dev, FT1000_REG_SUP_IMASK, ISR_DEFAULT_MASK);
+ tempword = ft1000_read_reg(dev, FT1000_REG_SUP_IMASK);
+ pr_debug("current interrupt enable mask = 0x%x\n", tempword);
+}
+
+/*---------------------------------------------------------------------------
+
+ Function: ft1000_disable_interrupts
+ Description: This function will disable all interrupts.
+ Input:
+ dev - device structure
+ Output:
+ None.
+
+ -------------------------------------------------------------------------*/
+static void ft1000_disable_interrupts(struct net_device *dev)
+{
+ u16 tempword;
+
+ ft1000_write_reg(dev, FT1000_REG_SUP_IMASK, ISR_MASK_ALL);
+ tempword = ft1000_read_reg(dev, FT1000_REG_SUP_IMASK);
+ pr_debug("current interrupt enable mask = 0x%x\n", tempword);
+}
+
+/*---------------------------------------------------------------------------
+ Function: ft1000_read_dsp_timer
+ Description: This function reads the DSP timer and stores its value in the
+ DSP_TIME field of the ft1000_info struct passed as argument
+ Input:
+ dev - device structure
+ info - ft1000_info structure
+ Output:
+ None.
+
+ -------------------------------------------------------------------------*/
+static void ft1000_read_dsp_timer(struct net_device *dev,
+ struct ft1000_info *info)
+{
+ if (info->AsicID == ELECTRABUZZ_ID) {
+ info->DSP_TIME[0] = ft1000_read_dpram(dev, FT1000_DSP_TIMER0);
+ info->DSP_TIME[1] = ft1000_read_dpram(dev, FT1000_DSP_TIMER1);
+ info->DSP_TIME[2] = ft1000_read_dpram(dev, FT1000_DSP_TIMER2);
+ info->DSP_TIME[3] = ft1000_read_dpram(dev, FT1000_DSP_TIMER3);
+ } else {
+ info->DSP_TIME[0] =
+ ft1000_read_dpram_mag_16(dev, FT1000_MAG_DSP_TIMER0,
+ FT1000_MAG_DSP_TIMER0_INDX);
+ info->DSP_TIME[1] =
+ ft1000_read_dpram_mag_16(dev, FT1000_MAG_DSP_TIMER1,
+ FT1000_MAG_DSP_TIMER1_INDX);
+ info->DSP_TIME[2] =
+ ft1000_read_dpram_mag_16(dev, FT1000_MAG_DSP_TIMER2,
+ FT1000_MAG_DSP_TIMER2_INDX);
+ info->DSP_TIME[3] =
+ ft1000_read_dpram_mag_16(dev, FT1000_MAG_DSP_TIMER3,
+ FT1000_MAG_DSP_TIMER3_INDX);
+ }
+}
+
+/*---------------------------------------------------------------------------
+
+ Function: ft1000_reset_asic
+ Description: This function will call the Card Service function to reset the
+ ASIC.
+ Input:
+ dev - device structure
+ Output:
+ none
+
+ -------------------------------------------------------------------------*/
+static void ft1000_reset_asic(struct net_device *dev)
+{
+ struct ft1000_info *info = netdev_priv(dev);
+ struct ft1000_pcmcia *pcmcia = info->priv;
+ u16 tempword;
+
+ (*info->ft1000_reset) (pcmcia->link);
+
+ /*
+ * Let's use the register provided by the Magnemite ASIC to reset the
+ * ASIC and DSP.
+ */
+ if (info->AsicID == MAGNEMITE_ID) {
+ ft1000_write_reg(dev, FT1000_REG_RESET,
+ DSP_RESET_BIT | ASIC_RESET_BIT);
+ }
+ mdelay(1);
+ if (info->AsicID == ELECTRABUZZ_ID) {
+ /* set watermark to -1 in order to not generate an interrupt */
+ ft1000_write_reg(dev, FT1000_REG_WATERMARK, 0xffff);
+ } else {
+ /* set watermark to -1 in order to not generate an interrupt */
+ ft1000_write_reg(dev, FT1000_REG_MAG_WATERMARK, 0xffff);
+ }
+ /* clear interrupts */
+ tempword = ft1000_read_reg(dev, FT1000_REG_SUP_ISR);
+ pr_debug("interrupt status register = 0x%x\n", tempword);
+ ft1000_write_reg(dev, FT1000_REG_SUP_ISR, tempword);
+ tempword = ft1000_read_reg(dev, FT1000_REG_SUP_ISR);
+ pr_debug("interrupt status register = 0x%x\n", tempword);
+
+}
+
+/*---------------------------------------------------------------------------
+
+ Function: ft1000_reset_card
+ Description: This function will reset the card
+ Input:
+ dev - device structure
+ Output:
+ status - false (card reset fail)
+ true (card reset successful)
+
+ -------------------------------------------------------------------------*/
+static int ft1000_reset_card(struct net_device *dev)
+{
+ struct ft1000_info *info = netdev_priv(dev);
+ u16 tempword;
+ int i;
+ unsigned long flags;
+ struct prov_record *ptr;
+ struct prov_record *tmp;
+
+ info->CardReady = 0;
+ info->ProgConStat = 0;
+ info->squeseqnum = 0;
+ ft1000_disable_interrupts(dev);
+
+ /* del_timer(&poll_timer); */
+
+ /* Make sure we free any memory reserve for provisioning */
+ list_for_each_entry_safe(ptr, tmp, &info->prov_list, list) {
+ pr_debug("deleting provisioning record\n");
+ list_del(&ptr->list);
+ kfree(ptr->pprov_data);
+ kfree(ptr);
+ }
+
+ if (info->AsicID == ELECTRABUZZ_ID) {
+ pr_debug("resetting DSP\n");
+ ft1000_write_reg(dev, FT1000_REG_RESET, DSP_RESET_BIT);
+ } else {
+ pr_debug("resetting ASIC and DSP\n");
+ ft1000_write_reg(dev, FT1000_REG_RESET,
+ DSP_RESET_BIT | ASIC_RESET_BIT);
+ }
+
+ /* Copy DSP session record into info block if this is not a coldstart */
+ if (ft1000_card_present == 1) {
+ spin_lock_irqsave(&info->dpram_lock, flags);
+ if (info->AsicID == ELECTRABUZZ_ID) {
+ ft1000_write_reg(dev, FT1000_REG_DPRAM_ADDR,
+ FT1000_DPRAM_RX_BASE);
+ for (i = 0; i < MAX_DSP_SESS_REC; i++) {
+ info->DSPSess.Rec[i] =
+ ft1000_read_reg(dev,
+ FT1000_REG_DPRAM_DATA);
+ }
+ } else {
+ ft1000_write_reg(dev, FT1000_REG_DPRAM_ADDR,
+ FT1000_DPRAM_MAG_RX_BASE);
+ for (i = 0; i < MAX_DSP_SESS_REC / 2; i++) {
+ info->DSPSess.MagRec[i] =
+ inl(dev->base_addr
+ + FT1000_REG_MAG_DPDATA);
+ }
+ }
+ spin_unlock_irqrestore(&info->dpram_lock, flags);
+ }
+
+ pr_debug("resetting ASIC\n");
+ mdelay(10);
+ /* reset ASIC */
+ ft1000_reset_asic(dev);
+
+ pr_debug("downloading dsp image\n");
+
+ if (info->AsicID == MAGNEMITE_ID) {
+ /* Put dsp in reset and take ASIC out of reset */
+ pr_debug("Put DSP in reset and take ASIC out of reset\n");
+ ft1000_write_reg(dev, FT1000_REG_RESET, DSP_RESET_BIT);
+
+ /* Setting MAGNEMITE ASIC to big endian mode */
+ ft1000_write_reg(dev, FT1000_REG_SUP_CTRL, HOST_INTF_BE);
+ /* Download bootloader */
+ return /*(DEBLOBBED)*/ false;
+ card_bootload(dev);
+
+ /* Take DSP out of reset */
+ ft1000_write_reg(dev, FT1000_REG_RESET, 0);
+ /* FLARION_DSP_ACTIVE; */
+ mdelay(10);
+ pr_debug("Take DSP out of reset\n");
+
+ /*
+ * Wait for 0xfefe indicating dsp ready before starting
+ * download
+ */
+ for (i = 0; i < 50; i++) {
+ tempword = ft1000_read_dpram_mag_16(dev,
+ FT1000_MAG_DPRAM_FEFE,
+ FT1000_MAG_DPRAM_FEFE_INDX);
+ if (tempword == 0xfefe)
+ break;
+ mdelay(20);
+ }
+
+ if (i == 50) {
+ pr_debug("No FEFE detected from DSP\n");
+ return false;
+ }
+
+ } else {
+ /* Take DSP out of reset */
+ ft1000_write_reg(dev, FT1000_REG_RESET, ~DSP_RESET_BIT);
+ mdelay(10);
+ }
+
+ if (card_download(dev, fw_entry->data, fw_entry->size)) {
+ pr_debug("card download unsuccessful\n");
+ return false;
+ }
+ pr_debug("card download successful\n");
+
+ mdelay(10);
+
+ if (info->AsicID == ELECTRABUZZ_ID) {
+ /*
+ * Need to initialize the FIFO length counter to zero in order
+ * to sync up with the DSP
+ */
+ info->fifo_cnt = 0;
+ ft1000_write_dpram(dev, FT1000_FIFO_LEN, info->fifo_cnt);
+ /* Initialize DSP heartbeat area to ho */
+ ft1000_write_dpram(dev, FT1000_HI_HO, ho);
+ tempword = ft1000_read_dpram(dev, FT1000_HI_HO);
+ pr_debug("hi_ho value = 0x%x\n", tempword);
+ } else {
+ /* Initialize DSP heartbeat area to ho */
+ ft1000_write_dpram_mag_16(dev, FT1000_MAG_HI_HO, ho_mag,
+ FT1000_MAG_HI_HO_INDX);
+ tempword =
+ ft1000_read_dpram_mag_16(dev, FT1000_MAG_HI_HO,
+ FT1000_MAG_HI_HO_INDX);
+ pr_debug("hi_ho value = 0x%x\n", tempword);
+ }
+
+ info->CardReady = 1;
+ ft1000_enable_interrupts(dev);
+
+ /* Schedule heartbeat process to run every 2 seconds */
+ /* poll_timer.expires = jiffies + (2*HZ); */
+ /* poll_timer.data = (u_long)dev; */
+ /* add_timer(&poll_timer); */
+
+ return true;
+
+}
+
+/*---------------------------------------------------------------------------
+
+ Function: ft1000_chkcard
+ Description: This function will check if the device is presently available on
+ the system.
+ Input:
+ dev - device structure
+ Output:
+ status - false (device is not present)
+ true (device is present)
+
+ -------------------------------------------------------------------------*/
+static int ft1000_chkcard(struct net_device *dev)
+{
+ u16 tempword;
+
+ /*
+ * Mask register is used to check for device presence since it is never
+ * set to zero.
+ */
+ tempword = ft1000_read_reg(dev, FT1000_REG_SUP_IMASK);
+ if (tempword == 0) {
+ pr_debug("IMASK = 0 Card not detected\n");
+ return false;
+ }
+ /*
+ * The system will return the value of 0xffff for the version register
+ * if the device is not present.
+ */
+ tempword = ft1000_read_reg(dev, FT1000_REG_ASIC_ID);
+ if (tempword == 0xffff) {
+ pr_debug("Version = 0xffff Card not detected\n");
+ return false;
+ }
+ return true;
+}
+
+
+/*---------------------------------------------------------------------------
+
+ Function: ft1000_hbchk
+ Description: This function will perform the heart beat check of the DSP as
+ well as the ASIC.
+ Input:
+ dev - device structure
+ Output:
+ none
+
+ -------------------------------------------------------------------------*/
+static void ft1000_hbchk(u_long data)
+{
+ struct net_device *dev = (struct net_device *)data;
+
+ struct ft1000_info *info;
+ u16 tempword;
+
+ info = netdev_priv(dev);
+
+ if (info->CardReady == 1) {
+ /* Perform dsp heartbeat check */
+ if (info->AsicID == ELECTRABUZZ_ID) {
+ tempword = ft1000_read_dpram(dev, FT1000_HI_HO);
+ } else {
+ tempword =
+ ntohs(ft1000_read_dpram_mag_16
+ (dev, FT1000_MAG_HI_HO,
+ FT1000_MAG_HI_HO_INDX));
+ }
+ pr_debug("hi_ho value = 0x%x\n", tempword);
+ /* Let's perform another check if ho is not detected */
+ if (tempword != ho) {
+ if (info->AsicID == ELECTRABUZZ_ID)
+ tempword = ft1000_read_dpram(dev, FT1000_HI_HO);
+ else
+ tempword = ntohs(ft1000_read_dpram_mag_16(dev,
+ FT1000_MAG_HI_HO,
+ FT1000_MAG_HI_HO_INDX));
+ }
+ if (tempword != ho) {
+ pr_info("heartbeat failed - no ho detected\n");
+ ft1000_read_dsp_timer(dev, info);
+ info->DrvErrNum = DSP_HB_INFO;
+ if (ft1000_reset_card(dev) == 0) {
+ pr_info("Hardware Failure Detected - PC Card disabled\n");
+ info->ProgConStat = 0xff;
+ return;
+ }
+ /* Schedule this module to run every 2 seconds */
+ poll_timer.expires = jiffies + (2*HZ);
+ poll_timer.data = (u_long)dev;
+ add_timer(&poll_timer);
+ return;
+ }
+
+ tempword = ft1000_read_reg(dev, FT1000_REG_DOORBELL);
+ /* Let's check doorbell again if fail */
+ if (tempword & FT1000_DB_HB)
+ tempword = ft1000_read_reg(dev, FT1000_REG_DOORBELL);
+
+ if (tempword & FT1000_DB_HB) {
+ pr_info("heartbeat doorbell not clear by firmware\n");
+ ft1000_read_dsp_timer(dev, info);
+ info->DrvErrNum = DSP_HB_INFO;
+ if (ft1000_reset_card(dev) == 0) {
+ pr_info("Hardware Failure Detected - PC Card disabled\n");
+ info->ProgConStat = 0xff;
+ return;
+ }
+ /* Schedule this module to run every 2 seconds */
+ poll_timer.expires = jiffies + (2*HZ);
+ poll_timer.data = (u_long)dev;
+ add_timer(&poll_timer);
+ return;
+ }
+ /*
+ * Set dedicated area to hi and ring appropriate doorbell
+ * according to hi/ho heartbeat protocol
+ */
+ if (info->AsicID == ELECTRABUZZ_ID) {
+ ft1000_write_dpram(dev, FT1000_HI_HO, hi);
+ } else {
+ ft1000_write_dpram_mag_16(dev, FT1000_MAG_HI_HO, hi_mag,
+ FT1000_MAG_HI_HO_INDX);
+ }
+
+ if (info->AsicID == ELECTRABUZZ_ID) {
+ tempword = ft1000_read_dpram(dev, FT1000_HI_HO);
+ } else {
+ tempword =
+ ntohs(ft1000_read_dpram_mag_16
+ (dev, FT1000_MAG_HI_HO,
+ FT1000_MAG_HI_HO_INDX));
+ }
+ /* Let's write hi again if fail */
+ if (tempword != hi) {
+ if (info->AsicID == ELECTRABUZZ_ID)
+ ft1000_write_dpram(dev, FT1000_HI_HO, hi);
+ else
+ ft1000_write_dpram_mag_16(dev, FT1000_MAG_HI_HO,
+ hi_mag, FT1000_MAG_HI_HO_INDX);
+
+ if (info->AsicID == ELECTRABUZZ_ID)
+ tempword = ft1000_read_dpram(dev, FT1000_HI_HO);
+ else
+ tempword = ntohs(ft1000_read_dpram_mag_16(dev,
+ FT1000_MAG_HI_HO,
+ FT1000_MAG_HI_HO_INDX));
+ }
+
+ if (tempword != hi) {
+ pr_info("heartbeat failed - cannot write hi into DPRAM\n");
+ ft1000_read_dsp_timer(dev, info);
+ info->DrvErrNum = DSP_HB_INFO;
+ if (ft1000_reset_card(dev) == 0) {
+ pr_info("Hardware Failure Detected - PC Card disabled\n");
+ info->ProgConStat = 0xff;
+ return;
+ }
+ /* Schedule this module to run every 2 seconds */
+ poll_timer.expires = jiffies + (2*HZ);
+ poll_timer.data = (u_long)dev;
+ add_timer(&poll_timer);
+ return;
+ }
+ ft1000_write_reg(dev, FT1000_REG_DOORBELL, FT1000_DB_HB);
+
+ }
+
+ /* Schedule this module to run every 2 seconds */
+ poll_timer.expires = jiffies + (2 * HZ);
+ poll_timer.data = (u_long)dev;
+ add_timer(&poll_timer);
+}
+
+/*---------------------------------------------------------------------------
+
+ Function: ft1000_send_cmd
+ Description:
+ Input:
+ Output:
+
+ -------------------------------------------------------------------------*/
+static void ft1000_send_cmd(struct net_device *dev, u16 *ptempbuffer, int size,
+ u16 qtype)
+{
+ struct ft1000_info *info = netdev_priv(dev);
+ int i;
+ u16 tempword;
+ unsigned long flags;
+
+ size += sizeof(struct pseudo_hdr);
+ /* check for odd byte and increment to 16-bit word align value */
+ if ((size & 0x0001))
+ size++;
+ pr_debug("total length = %d\n", size);
+ pr_debug("length = %d\n", ntohs(*ptempbuffer));
+ /*
+ * put message into slow queue area
+ * All messages are in the form total_len + pseudo header + message body
+ */
+ spin_lock_irqsave(&info->dpram_lock, flags);
+
+ /* Make sure SLOWQ doorbell is clear */
+ tempword = ft1000_read_reg(dev, FT1000_REG_DOORBELL);
+ i = 0;
+ while (tempword & FT1000_DB_DPRAM_TX) {
+ mdelay(10);
+ i++;
+ if (i == 10) {
+ spin_unlock_irqrestore(&info->dpram_lock, flags);
+ return;
+ }
+ tempword = ft1000_read_reg(dev, FT1000_REG_DOORBELL);
+ }
+
+ if (info->AsicID == ELECTRABUZZ_ID) {
+ ft1000_write_reg(dev, FT1000_REG_DPRAM_ADDR,
+ FT1000_DPRAM_TX_BASE);
+ /* Write total length to dpram */
+ ft1000_write_reg(dev, FT1000_REG_DPRAM_DATA, size);
+ /* Write pseudo header and messgae body */
+ for (i = 0; i < (size >> 1); i++) {
+ pr_debug("data %d = 0x%x\n", i, *ptempbuffer);
+ tempword = htons(*ptempbuffer++);
+ ft1000_write_reg(dev, FT1000_REG_DPRAM_DATA, tempword);
+ }
+ } else {
+ ft1000_write_reg(dev, FT1000_REG_DPRAM_ADDR,
+ FT1000_DPRAM_MAG_TX_BASE);
+ /* Write total length to dpram */
+ ft1000_write_reg(dev, FT1000_REG_MAG_DPDATAH, htons(size));
+ /* Write pseudo header and messgae body */
+ ft1000_write_reg(dev, FT1000_REG_DPRAM_ADDR,
+ FT1000_DPRAM_MAG_TX_BASE + 1);
+ for (i = 0; i < (size >> 2); i++) {
+ pr_debug("data = 0x%x\n", *ptempbuffer);
+ outw(*ptempbuffer++,
+ dev->base_addr + FT1000_REG_MAG_DPDATAL);
+ pr_debug("data = 0x%x\n", *ptempbuffer);
+ outw(*ptempbuffer++,
+ dev->base_addr + FT1000_REG_MAG_DPDATAH);
+ }
+ pr_debug("data = 0x%x\n", *ptempbuffer);
+ outw(*ptempbuffer++, dev->base_addr + FT1000_REG_MAG_DPDATAL);
+ pr_debug("data = 0x%x\n", *ptempbuffer);
+ outw(*ptempbuffer++, dev->base_addr + FT1000_REG_MAG_DPDATAH);
+ }
+ spin_unlock_irqrestore(&info->dpram_lock, flags);
+
+ /* ring doorbell to notify DSP that we have a message ready */
+ ft1000_write_reg(dev, FT1000_REG_DOORBELL, FT1000_DB_DPRAM_TX);
+}
+
+/*---------------------------------------------------------------------------
+
+ Function: ft1000_receive_cmd
+ Description: This function will read a message from the dpram area.
+ Input:
+ dev - network device structure
+ pbuffer - caller supply address to buffer
+ pnxtph - pointer to next pseudo header
+ Output:
+ Status = 0 (unsuccessful)
+ = 1 (successful)
+
+ -------------------------------------------------------------------------*/
+static bool ft1000_receive_cmd(struct net_device *dev, u16 *pbuffer,
+ int maxsz, u16 *pnxtph)
+{
+ struct ft1000_info *info = netdev_priv(dev);
+ u16 size;
+ u16 *ppseudohdr;
+ int i;
+ u16 tempword;
+ unsigned long flags;
+
+ if (info->AsicID == ELECTRABUZZ_ID) {
+ size = ft1000_read_dpram(dev, *pnxtph)
+ + sizeof(struct pseudo_hdr);
+ } else {
+ size = ntohs(ft1000_read_dpram_mag_16(dev, FT1000_MAG_PH_LEN,
+ FT1000_MAG_PH_LEN_INDX))
+ + sizeof(struct pseudo_hdr);
+ }
+ if (size > maxsz) {
+ pr_debug("Invalid command length = %d\n", size);
+ return false;
+ }
+ ppseudohdr = (u16 *)pbuffer;
+ spin_lock_irqsave(&info->dpram_lock, flags);
+ if (info->AsicID == ELECTRABUZZ_ID) {
+ ft1000_write_reg(dev, FT1000_REG_DPRAM_ADDR,
+ FT1000_DPRAM_RX_BASE + 2);
+ for (i = 0; i <= (size >> 1); i++) {
+ tempword =
+ ft1000_read_reg(dev, FT1000_REG_DPRAM_DATA);
+ *pbuffer++ = ntohs(tempword);
+ }
+ } else {
+ ft1000_write_reg(dev, FT1000_REG_DPRAM_ADDR,
+ FT1000_DPRAM_MAG_RX_BASE);
+ *pbuffer = inw(dev->base_addr + FT1000_REG_MAG_DPDATAH);
+ pr_debug("received data = 0x%x\n", *pbuffer);
+ pbuffer++;
+ ft1000_write_reg(dev, FT1000_REG_DPRAM_ADDR,
+ FT1000_DPRAM_MAG_RX_BASE + 1);
+ for (i = 0; i <= (size >> 2); i++) {
+ *pbuffer =
+ inw(dev->base_addr +
+ FT1000_REG_MAG_DPDATAL);
+ pbuffer++;
+ *pbuffer =
+ inw(dev->base_addr +
+ FT1000_REG_MAG_DPDATAH);
+ pbuffer++;
+ }
+ /* copy odd aligned word */
+ *pbuffer = inw(dev->base_addr + FT1000_REG_MAG_DPDATAL);
+ pr_debug("received data = 0x%x\n", *pbuffer);
+ pbuffer++;
+ *pbuffer = inw(dev->base_addr + FT1000_REG_MAG_DPDATAH);
+ pr_debug("received data = 0x%x\n", *pbuffer);
+ pbuffer++;
+ }
+ if (size & 0x0001) {
+ /* copy odd byte from fifo */
+ tempword = ft1000_read_reg(dev, FT1000_REG_DPRAM_DATA);
+ *pbuffer = ntohs(tempword);
+ }
+ spin_unlock_irqrestore(&info->dpram_lock, flags);
+
+ /*
+ * Check if pseudo header checksum is good
+ * Calculate pseudo header checksum
+ */
+ tempword = *ppseudohdr++;
+ for (i = 1; i < 7; i++)
+ tempword ^= *ppseudohdr++;
+ if (tempword != *ppseudohdr) {
+ pr_debug("Pseudo header checksum mismatch\n");
+ /* Drop this message */
+ return false;
+ }
+ return true;
+}
+
+/*---------------------------------------------------------------------------
+
+ Function: ft1000_proc_drvmsg
+ Description: This function will process the various driver messages.
+ Input:
+ dev - device structure
+ pnxtph - pointer to next pseudo header
+ Output:
+ none
+
+ -------------------------------------------------------------------------*/
+static void ft1000_proc_drvmsg(struct net_device *dev)
+{
+ struct ft1000_info *info = netdev_priv(dev);
+ u16 msgtype;
+ u16 tempword;
+ struct media_msg *pmediamsg;
+ struct dsp_init_msg *pdspinitmsg;
+ struct drv_msg *pdrvmsg;
+ u16 len;
+ u16 i;
+ struct prov_record *ptr;
+ struct pseudo_hdr *ppseudo_hdr;
+ u16 *pmsg;
+ struct timeval tv;
+ union {
+ u8 byte[2];
+ u16 wrd;
+ } convert;
+
+ if (info->AsicID == ELECTRABUZZ_ID)
+ tempword = FT1000_DPRAM_RX_BASE+2;
+ else
+ tempword = FT1000_DPRAM_MAG_RX_BASE;
+
+ if (ft1000_receive_cmd(dev, &cmdbuffer[0], MAX_CMD_SQSIZE, &tempword)) {
+
+ /*
+ * Get the message type which is total_len + PSEUDO header
+ * + msgtype + message body
+ */
+ pdrvmsg = (struct drv_msg *)&cmdbuffer[0];
+ msgtype = ntohs(pdrvmsg->type);
+ pr_debug("Command message type = 0x%x\n", msgtype);
+ switch (msgtype) {
+ case DSP_PROVISION:
+ pr_debug("Got a provisioning request message from DSP\n");
+ mdelay(25);
+ while (list_empty(&info->prov_list) == 0) {
+ pr_debug("Sending a provisioning message\n");
+ /* Make sure SLOWQ doorbell is clear */
+ tempword = ft1000_read_reg(dev,
+ FT1000_REG_DOORBELL);
+ i = 0;
+ while (tempword & FT1000_DB_DPRAM_TX) {
+ mdelay(5);
+ i++;
+ if (i == 10)
+ break;
+ }
+ ptr = list_entry(info->prov_list.next,
+ struct prov_record, list);
+ len = *(u16 *)ptr->pprov_data;
+ len = htons(len);
+
+ pmsg = (u16 *)ptr->pprov_data;
+ ppseudo_hdr = (struct pseudo_hdr *)pmsg;
+ /* Insert slow queue sequence number */
+ ppseudo_hdr->seq_num = info->squeseqnum++;
+ ppseudo_hdr->portsrc = 0;
+ /* Calculate new checksum */
+ ppseudo_hdr->checksum = *pmsg++;
+ pr_debug("checksum = 0x%x\n",
+ ppseudo_hdr->checksum);
+ for (i = 1; i < 7; i++) {
+ ppseudo_hdr->checksum ^= *pmsg++;
+ pr_debug("checksum = 0x%x\n",
+ ppseudo_hdr->checksum);
+ }
+
+ ft1000_send_cmd(dev, (u16 *)ptr->pprov_data,
+ len, SLOWQ_TYPE);
+ list_del(&ptr->list);
+ kfree(ptr->pprov_data);
+ kfree(ptr);
+ }
+ /*
+ * Indicate adapter is ready to take application
+ * messages after all provisioning messages are sent
+ */
+ info->CardReady = 1;
+ break;
+ case MEDIA_STATE:
+ pmediamsg = (struct media_msg *)&cmdbuffer[0];
+ if (info->ProgConStat != 0xFF) {
+ if (pmediamsg->state) {
+ pr_debug("Media is up\n");
+ if (info->mediastate == 0) {
+ netif_carrier_on(dev);
+ netif_wake_queue(dev);
+ info->mediastate = 1;
+ do_gettimeofday(&tv);
+ info->ConTm = tv.tv_sec;
+ }
+ } else {
+ pr_debug("Media is down\n");
+ if (info->mediastate == 1) {
+ info->mediastate = 0;
+ netif_carrier_off(dev);
+ netif_stop_queue(dev);
+ info->ConTm = 0;
+ }
+ }
+ } else {
+ pr_debug("Media is down\n");
+ if (info->mediastate == 1) {
+ info->mediastate = 0;
+ netif_carrier_off(dev);
+ netif_stop_queue(dev);
+ info->ConTm = 0;
+ }
+ }
+ break;
+ case DSP_INIT_MSG:
+ pdspinitmsg = (struct dsp_init_msg *)&cmdbuffer[0];
+ memcpy(info->DspVer, pdspinitmsg->DspVer, DSPVERSZ);
+ pr_debug("DSPVER = 0x%2x 0x%2x 0x%2x 0x%2x\n",
+ info->DspVer[0], info->DspVer[1],
+ info->DspVer[2], info->DspVer[3]);
+ memcpy(info->HwSerNum, pdspinitmsg->HwSerNum,
+ HWSERNUMSZ);
+ memcpy(info->Sku, pdspinitmsg->Sku, SKUSZ);
+ memcpy(info->eui64, pdspinitmsg->eui64, EUISZ);
+ dev->dev_addr[0] = info->eui64[0];
+ dev->dev_addr[1] = info->eui64[1];
+ dev->dev_addr[2] = info->eui64[2];
+ dev->dev_addr[3] = info->eui64[5];
+ dev->dev_addr[4] = info->eui64[6];
+ dev->dev_addr[5] = info->eui64[7];
+
+ if (ntohs(pdspinitmsg->length) ==
+ (sizeof(struct dsp_init_msg) - 20)) {
+ memcpy(info->ProductMode,
+ pdspinitmsg->ProductMode, MODESZ);
+ memcpy(info->RfCalVer, pdspinitmsg->RfCalVer,
+ CALVERSZ);
+ memcpy(info->RfCalDate, pdspinitmsg->RfCalDate,
+ CALDATESZ);
+ pr_debug("RFCalVer = 0x%2x 0x%2x\n",
+ info->RfCalVer[0], info->RfCalVer[1]);
+ }
+
+ break;
+ case DSP_STORE_INFO:
+ pr_debug("Got DSP_STORE_INFO\n");
+ tempword = ntohs(pdrvmsg->length);
+ info->DSPInfoBlklen = tempword;
+ if (tempword < (MAX_DSP_SESS_REC - 4)) {
+ pmsg = (u16 *)&pdrvmsg->data[0];
+ for (i = 0; i < ((tempword + 1) / 2); i++) {
+ pr_debug("dsp info data = 0x%x\n",
+ *pmsg);
+ info->DSPInfoBlk[i + 10] = *pmsg++;
+ }
+ }
+ break;
+ case DSP_GET_INFO:
+ pr_debug("Got DSP_GET_INFO\n");
+ /*
+ * copy dsp info block to dsp
+ * allow any outstanding ioctl to finish
+ */
+ mdelay(10);
+ tempword = ft1000_read_reg(dev, FT1000_REG_DOORBELL);
+ if (tempword & FT1000_DB_DPRAM_TX) {
+ mdelay(10);
+ tempword = ft1000_read_reg(dev,
+ FT1000_REG_DOORBELL);
+ if (tempword & FT1000_DB_DPRAM_TX)
+ mdelay(10);
+ }
+
+ if ((tempword & FT1000_DB_DPRAM_TX) == 0) {
+ /*
+ * Put message into Slow Queue
+ * Form Pseudo header
+ */
+ pmsg = (u16 *)info->DSPInfoBlk;
+ ppseudo_hdr = (struct pseudo_hdr *)pmsg;
+ ppseudo_hdr->length =
+ htons(info->DSPInfoBlklen + 4);
+ ppseudo_hdr->source = 0x10;
+ ppseudo_hdr->destination = 0x20;
+ ppseudo_hdr->portdest = 0;
+ ppseudo_hdr->portsrc = 0;
+ ppseudo_hdr->sh_str_id = 0;
+ ppseudo_hdr->control = 0;
+ ppseudo_hdr->rsvd1 = 0;
+ ppseudo_hdr->rsvd2 = 0;
+ ppseudo_hdr->qos_class = 0;
+ /* Insert slow queue sequence number */
+ ppseudo_hdr->seq_num = info->squeseqnum++;
+ /* Insert application id */
+ ppseudo_hdr->portsrc = 0;
+ /* Calculate new checksum */
+ ppseudo_hdr->checksum = *pmsg++;
+ for (i = 1; i < 7; i++)
+ ppseudo_hdr->checksum ^= *pmsg++;
+
+ info->DSPInfoBlk[8] = 0x7200;
+ info->DSPInfoBlk[9] =
+ htons(info->DSPInfoBlklen);
+ ft1000_send_cmd(dev, info->DSPInfoBlk,
+ (u16)(info->DSPInfoBlklen+4),
+ 0);
+ }
+
+ break;
+ case GET_DRV_ERR_RPT_MSG:
+ pr_debug("Got GET_DRV_ERR_RPT_MSG\n");
+ /*
+ * copy driver error message to dsp
+ * allow any outstanding ioctl to finish
+ */
+ mdelay(10);
+ tempword = ft1000_read_reg(dev, FT1000_REG_DOORBELL);
+ if (tempword & FT1000_DB_DPRAM_TX) {
+ mdelay(10);
+ tempword = ft1000_read_reg(dev,
+ FT1000_REG_DOORBELL);
+ if (tempword & FT1000_DB_DPRAM_TX)
+ mdelay(10);
+ }
+
+ if ((tempword & FT1000_DB_DPRAM_TX) == 0) {
+ /*
+ * Put message into Slow Queue
+ * Form Pseudo header
+ */
+ pmsg = (u16 *)&tempbuffer[0];
+ ppseudo_hdr = (struct pseudo_hdr *)pmsg;
+ ppseudo_hdr->length = htons(0x0012);
+ ppseudo_hdr->source = 0x10;
+ ppseudo_hdr->destination = 0x20;
+ ppseudo_hdr->portdest = 0;
+ ppseudo_hdr->portsrc = 0;
+ ppseudo_hdr->sh_str_id = 0;
+ ppseudo_hdr->control = 0;
+ ppseudo_hdr->rsvd1 = 0;
+ ppseudo_hdr->rsvd2 = 0;
+ ppseudo_hdr->qos_class = 0;
+ /* Insert slow queue sequence number */
+ ppseudo_hdr->seq_num = info->squeseqnum++;
+ /* Insert application id */
+ ppseudo_hdr->portsrc = 0;
+ /* Calculate new checksum */
+ ppseudo_hdr->checksum = *pmsg++;
+ for (i = 1; i < 7; i++)
+ ppseudo_hdr->checksum ^= *pmsg++;
+
+ pmsg = (u16 *)&tempbuffer[16];
+ *pmsg++ = htons(RSP_DRV_ERR_RPT_MSG);
+ *pmsg++ = htons(0x000e);
+ *pmsg++ = htons(info->DSP_TIME[0]);
+ *pmsg++ = htons(info->DSP_TIME[1]);
+ *pmsg++ = htons(info->DSP_TIME[2]);
+ *pmsg++ = htons(info->DSP_TIME[3]);
+ convert.byte[0] = info->DspVer[0];
+ convert.byte[1] = info->DspVer[1];
+ *pmsg++ = convert.wrd;
+ convert.byte[0] = info->DspVer[2];
+ convert.byte[1] = info->DspVer[3];
+ *pmsg++ = convert.wrd;
+ *pmsg++ = htons(info->DrvErrNum);
+
+ ft1000_send_cmd(dev, (u16 *)&tempbuffer[0],
+ (u16)(0x0012), 0);
+ info->DrvErrNum = 0;
+ }
+
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+/*---------------------------------------------------------------------------
+
+ Function: ft1000_parse_dpram_msg
+ Description: This function will parse the message received from the DSP
+ via the DPRAM interface.
+ Input:
+ dev - device structure
+ Output:
+ status - FAILURE
+ SUCCESS
+
+ -------------------------------------------------------------------------*/
+static int ft1000_parse_dpram_msg(struct net_device *dev)
+{
+ struct ft1000_info *info = netdev_priv(dev);
+ u16 doorbell;
+ u16 portid;
+ u16 nxtph;
+ u16 total_len;
+ int i = 0;
+ unsigned long flags;
+
+ doorbell = ft1000_read_reg(dev, FT1000_REG_DOORBELL);
+ pr_debug("Doorbell = 0x%x\n", doorbell);
+
+ if (doorbell & FT1000_ASIC_RESET_REQ) {
+ /* Copy DSP session record from info block */
+ spin_lock_irqsave(&info->dpram_lock, flags);
+ if (info->AsicID == ELECTRABUZZ_ID) {
+ ft1000_write_reg(dev, FT1000_REG_DPRAM_ADDR,
+ FT1000_DPRAM_RX_BASE);
+ for (i = 0; i < MAX_DSP_SESS_REC; i++) {
+ ft1000_write_reg(dev, FT1000_REG_DPRAM_DATA,
+ info->DSPSess.Rec[i]);
+ }
+ } else {
+ ft1000_write_reg(dev, FT1000_REG_DPRAM_ADDR,
+ FT1000_DPRAM_MAG_RX_BASE);
+ for (i = 0; i < MAX_DSP_SESS_REC / 2; i++) {
+ outl(info->DSPSess.MagRec[i],
+ dev->base_addr + FT1000_REG_MAG_DPDATA);
+ }
+ }
+ spin_unlock_irqrestore(&info->dpram_lock, flags);
+
+ /* clear ASIC RESET request */
+ ft1000_write_reg(dev, FT1000_REG_DOORBELL,
+ FT1000_ASIC_RESET_REQ);
+ pr_debug("Got an ASIC RESET Request\n");
+ ft1000_write_reg(dev, FT1000_REG_DOORBELL,
+ FT1000_ASIC_RESET_DSP);
+
+ if (info->AsicID == MAGNEMITE_ID) {
+ /* Setting MAGNEMITE ASIC to big endian mode */
+ ft1000_write_reg(dev, FT1000_REG_SUP_CTRL,
+ HOST_INTF_BE);
+ }
+ }
+
+ if (doorbell & FT1000_DSP_ASIC_RESET) {
+ pr_debug("Got a dsp ASIC reset message\n");
+ ft1000_write_reg(dev, FT1000_REG_DOORBELL,
+ FT1000_DSP_ASIC_RESET);
+ udelay(200);
+ return SUCCESS;
+ }
+
+ if (doorbell & FT1000_DB_DPRAM_RX) {
+ pr_debug("Got a slow queue message\n");
+ nxtph = FT1000_DPRAM_RX_BASE + 2;
+ if (info->AsicID == ELECTRABUZZ_ID) {
+ total_len =
+ ft1000_read_dpram(dev, FT1000_DPRAM_RX_BASE);
+ } else {
+ total_len =
+ ntohs(ft1000_read_dpram_mag_16
+ (dev, FT1000_MAG_TOTAL_LEN,
+ FT1000_MAG_TOTAL_LEN_INDX));
+ }
+ pr_debug("total length = %d\n", total_len);
+ if ((total_len < MAX_CMD_SQSIZE)
+ && (total_len > sizeof(struct pseudo_hdr))) {
+ total_len += nxtph;
+ /*
+ * ft1000_read_reg will return a value that needs to be
+ * byteswap in order to get DSP_QID_OFFSET.
+ */
+ if (info->AsicID == ELECTRABUZZ_ID) {
+ portid = (ft1000_read_dpram(dev, DSP_QID_OFFSET
+ + FT1000_DPRAM_RX_BASE + 2)
+ >> 8) & 0xff;
+ } else {
+ portid =
+ ft1000_read_dpram_mag_16
+ (dev, FT1000_MAG_PORT_ID,
+ FT1000_MAG_PORT_ID_INDX) & 0xff;
+ }
+ pr_debug("DSP_QID = 0x%x\n", portid);
+
+ if (portid == DRIVERID) {
+ /*
+ * We are assumming one driver message from the
+ * DSP at a time.
+ */
+ ft1000_proc_drvmsg(dev);
+ }
+ }
+ ft1000_write_reg(dev, FT1000_REG_DOORBELL, FT1000_DB_DPRAM_RX);
+ }
+
+ if (doorbell & FT1000_DB_COND_RESET) {
+ /* Reset ASIC and DSP */
+ ft1000_read_dsp_timer(dev, info);
+ info->DrvErrNum = DSP_CONDRESET_INFO;
+ pr_debug("DSP conditional reset requested\n");
+ ft1000_reset_card(dev);
+ ft1000_write_reg(dev, FT1000_REG_DOORBELL,
+ FT1000_DB_COND_RESET);
+ }
+ /* let's clear any unexpected doorbells from DSP */
+ doorbell =
+ doorbell & ~(FT1000_DB_DPRAM_RX | FT1000_ASIC_RESET_REQ |
+ FT1000_DB_COND_RESET | 0xff00);
+ if (doorbell) {
+ pr_debug("Clearing unexpected doorbell = 0x%x\n", doorbell);
+ ft1000_write_reg(dev, FT1000_REG_DOORBELL, doorbell);
+ }
+
+ return SUCCESS;
+
+}
+
+/*---------------------------------------------------------------------------
+
+ Function: ft1000_flush_fifo
+ Description: This function will flush one packet from the downlink
+ FIFO.
+ Input:
+ dev - device structure
+ drv_err - driver error causing the flush fifo
+ Output:
+ None.
+
+ -------------------------------------------------------------------------*/
+static void ft1000_flush_fifo(struct net_device *dev, u16 DrvErrNum)
+{
+ struct ft1000_info *info = netdev_priv(dev);
+ struct ft1000_pcmcia *pcmcia = info->priv;
+ u16 i;
+ u32 templong;
+ u16 tempword;
+
+ if (pcmcia->PktIntfErr > MAX_PH_ERR) {
+ ft1000_read_dsp_timer(dev, info);
+ info->DrvErrNum = DrvErrNum;
+ ft1000_reset_card(dev);
+ return;
+ }
+ /* Flush corrupted pkt from FIFO */
+ i = 0;
+ do {
+ if (info->AsicID == ELECTRABUZZ_ID) {
+ tempword =
+ ft1000_read_reg(dev, FT1000_REG_DFIFO);
+ tempword =
+ ft1000_read_reg(dev, FT1000_REG_DFIFO_STAT);
+ } else {
+ templong =
+ inl(dev->base_addr + FT1000_REG_MAG_DFR);
+ tempword =
+ inw(dev->base_addr + FT1000_REG_MAG_DFSR);
+ }
+ i++;
+ /*
+ * This should never happen unless the ASIC is broken.
+ * We must reset to recover.
+ */
+ if ((i > 2048) || (tempword == 0)) {
+ ft1000_read_dsp_timer(dev, info);
+ if (tempword == 0) {
+ /*
+ * Let's check if ASIC reads are still ok by
+ * reading the Mask register which is never zero
+ * at this point of the code.
+ */
+ tempword =
+ inw(dev->base_addr +
+ FT1000_REG_SUP_IMASK);
+ if (tempword == 0) {
+ /*
+ * This indicates that we can not
+ * communicate with the ASIC
+ */
+ info->DrvErrNum = FIFO_FLUSH_BADCNT;
+ } else {
+ /*
+ * Let's assume that we really flush
+ * the FIFO
+ */
+ pcmcia->PktIntfErr++;
+ return;
+ }
+ } else {
+ info->DrvErrNum = FIFO_FLUSH_MAXLIMIT;
+ }
+ return;
+ }
+ tempword = inw(dev->base_addr + FT1000_REG_SUP_STAT);
+ } while ((tempword & 0x03) != 0x03);
+ if (info->AsicID == ELECTRABUZZ_ID) {
+ i++;
+ pr_debug("Flushing FIFO complete = %x\n", tempword);
+ /* Flush last word in FIFO. */
+ tempword = ft1000_read_reg(dev, FT1000_REG_DFIFO);
+ /* Update FIFO counter for DSP */
+ i = i * 2;
+ pr_debug("Flush Data byte count to dsp = %d\n", i);
+ info->fifo_cnt += i;
+ ft1000_write_dpram(dev, FT1000_FIFO_LEN,
+ info->fifo_cnt);
+ } else {
+ pr_debug("Flushing FIFO complete = %x\n", tempword);
+ /* Flush last word in FIFO */
+ templong = inl(dev->base_addr + FT1000_REG_MAG_DFR);
+ tempword = inw(dev->base_addr + FT1000_REG_SUP_STAT);
+ pr_debug("FT1000_REG_SUP_STAT = 0x%x\n", tempword);
+ tempword = inw(dev->base_addr + FT1000_REG_MAG_DFSR);
+ pr_debug("FT1000_REG_MAG_DFSR = 0x%x\n", tempword);
+ }
+ if (DrvErrNum)
+ pcmcia->PktIntfErr++;
+}
+
+/*---------------------------------------------------------------------------
+
+ Function: ft1000_copy_up_pkt
+ Description: This function will pull Flarion packets out of the Downlink
+ FIFO and convert it to an ethernet packet. The ethernet packet will
+ then be deliver to the TCP/IP stack.
+ Input:
+ dev - device structure
+ Output:
+ status - FAILURE
+ SUCCESS
+
+ -------------------------------------------------------------------------*/
+static int ft1000_copy_up_pkt(struct net_device *dev)
+{
+ u16 tempword;
+ struct ft1000_info *info = netdev_priv(dev);
+ u16 len;
+ struct sk_buff *skb;
+ u16 i;
+ u8 *pbuffer = NULL;
+ u8 *ptemp = NULL;
+ u16 chksum;
+ u32 *ptemplong;
+ u32 templong;
+
+ /* Read length */
+ if (info->AsicID == ELECTRABUZZ_ID) {
+ tempword = ft1000_read_reg(dev, FT1000_REG_DFIFO);
+ len = tempword;
+ } else {
+ tempword = ft1000_read_reg(dev, FT1000_REG_MAG_DFRL);
+ len = ntohs(tempword);
+ }
+ chksum = tempword;
+ pr_debug("Number of Bytes in FIFO = %d\n", len);
+
+ if (len > ENET_MAX_SIZE) {
+ pr_debug("size of ethernet packet invalid\n");
+ if (info->AsicID == MAGNEMITE_ID) {
+ /* Read High word to complete 32 bit access */
+ tempword = ft1000_read_reg(dev, FT1000_REG_MAG_DFRH);
+ }
+ ft1000_flush_fifo(dev, DSP_PKTLEN_INFO);
+ info->stats.rx_errors++;
+ return FAILURE;
+ }
+
+ skb = dev_alloc_skb(len + 12 + 2);
+
+ if (skb == NULL) {
+ /* Read High word to complete 32 bit access */
+ if (info->AsicID == MAGNEMITE_ID)
+ tempword = ft1000_read_reg(dev, FT1000_REG_MAG_DFRH);
+
+ ft1000_flush_fifo(dev, 0);
+ info->stats.rx_errors++;
+ return FAILURE;
+ }
+ pbuffer = (u8 *)skb_put(skb, len + 12);
+
+ /* Pseudo header */
+ if (info->AsicID == ELECTRABUZZ_ID) {
+ for (i = 1; i < 7; i++) {
+ tempword = ft1000_read_reg(dev, FT1000_REG_DFIFO);
+ chksum ^= tempword;
+ }
+ /* read checksum value */
+ tempword = ft1000_read_reg(dev, FT1000_REG_DFIFO);
+ } else {
+ tempword = ft1000_read_reg(dev, FT1000_REG_MAG_DFRH);
+ pr_debug("Pseudo = 0x%x\n", tempword);
+ chksum ^= tempword;
+
+ tempword = ft1000_read_reg(dev, FT1000_REG_MAG_DFRL);
+ pr_debug("Pseudo = 0x%x\n", tempword);
+ chksum ^= tempword;
+
+ tempword = ft1000_read_reg(dev, FT1000_REG_MAG_DFRH);
+ pr_debug("Pseudo = 0x%x\n", tempword);
+ chksum ^= tempword;
+
+ tempword = ft1000_read_reg(dev, FT1000_REG_MAG_DFRL);
+ pr_debug("Pseudo = 0x%x\n", tempword);
+ chksum ^= tempword;
+
+ tempword = ft1000_read_reg(dev, FT1000_REG_MAG_DFRH);
+ pr_debug("Pseudo = 0x%x\n", tempword);
+ chksum ^= tempword;
+
+ tempword = ft1000_read_reg(dev, FT1000_REG_MAG_DFRL);
+ pr_debug("Pseudo = 0x%x\n", tempword);
+ chksum ^= tempword;
+
+ /* read checksum value */
+ tempword = ft1000_read_reg(dev, FT1000_REG_MAG_DFRH);
+ pr_debug("Pseudo = 0x%x\n", tempword);
+ }
+
+ if (chksum != tempword) {
+ pr_debug("Packet checksum mismatch 0x%x 0x%x\n",
+ chksum, tempword);
+ ft1000_flush_fifo(dev, DSP_PKTPHCKSUM_INFO);
+ info->stats.rx_errors++;
+ kfree_skb(skb);
+ return FAILURE;
+ }
+ /* subtract the number of bytes read already */
+ ptemp = pbuffer;
+
+ /* fake MAC address */
+ *pbuffer++ = dev->dev_addr[0];
+ *pbuffer++ = dev->dev_addr[1];
+ *pbuffer++ = dev->dev_addr[2];
+ *pbuffer++ = dev->dev_addr[3];
+ *pbuffer++ = dev->dev_addr[4];
+ *pbuffer++ = dev->dev_addr[5];
+ *pbuffer++ = 0x00;
+ *pbuffer++ = 0x07;
+ *pbuffer++ = 0x35;
+ *pbuffer++ = 0xff;
+ *pbuffer++ = 0xff;
+ *pbuffer++ = 0xfe;
+
+ if (info->AsicID == ELECTRABUZZ_ID) {
+ for (i = 0; i < len / 2; i++) {
+ tempword = ft1000_read_reg(dev, FT1000_REG_DFIFO);
+ *pbuffer++ = (u8) (tempword >> 8);
+ *pbuffer++ = (u8)tempword;
+ if (ft1000_chkcard(dev) == false) {
+ kfree_skb(skb);
+ return FAILURE;
+ }
+ }
+
+ /* Need to read one more word if odd byte */
+ if (len & 0x0001) {
+ tempword = ft1000_read_reg(dev, FT1000_REG_DFIFO);
+ *pbuffer++ = (u8) (tempword >> 8);
+ }
+ } else {
+ ptemplong = (u32 *)pbuffer;
+ for (i = 0; i < len / 4; i++) {
+ templong = inl(dev->base_addr + FT1000_REG_MAG_DFR);
+ pr_debug("Data = 0x%8x\n", templong);
+ *ptemplong++ = templong;
+ }
+
+ /* Need to read one more word if odd align. */
+ if (len & 0x0003) {
+ templong = inl(dev->base_addr + FT1000_REG_MAG_DFR);
+ pr_debug("Data = 0x%8x\n", templong);
+ *ptemplong++ = templong;
+ }
+
+ }
+
+ pr_debug("Data passed to Protocol layer:\n");
+ for (i = 0; i < len + 12; i++)
+ pr_debug("Protocol Data: 0x%x\n", *ptemp++);
+
+ skb->dev = dev;
+ skb->protocol = eth_type_trans(skb, dev);
+ skb->ip_summed = CHECKSUM_UNNECESSARY;
+ netif_rx(skb);
+
+ info->stats.rx_packets++;
+ /* Add on 12 bytes for MAC address which was removed */
+ info->stats.rx_bytes += (len + 12);
+
+ if (info->AsicID == ELECTRABUZZ_ID) {
+ /* track how many bytes have been read from FIFO - round up to
+ * 16 bit word */
+ tempword = len + 16;
+ if (tempword & 0x01)
+ tempword++;
+ info->fifo_cnt += tempword;
+ ft1000_write_reg(dev, FT1000_REG_DPRAM_ADDR, FT1000_FIFO_LEN);
+ ft1000_write_reg(dev, FT1000_REG_DPRAM_DATA, info->fifo_cnt);
+ }
+
+ return SUCCESS;
+}
+
+/*---------------------------------------------------------------------------
+
+ Function: ft1000_copy_down_pkt
+ Description: This function will take an ethernet packet and convert it to
+ a Flarion packet prior to sending it to the ASIC Downlink
+ FIFO.
+ Input:
+ dev - device structure
+ packet - address of ethernet packet
+ len - length of IP packet
+ Output:
+ status - FAILURE
+ SUCCESS
+
+ -------------------------------------------------------------------------*/
+static int ft1000_copy_down_pkt(struct net_device *dev, u16 *packet, u16 len)
+{
+ struct ft1000_info *info = netdev_priv(dev);
+ struct ft1000_pcmcia *pcmcia = info->priv;
+ union {
+ struct pseudo_hdr blk;
+ u16 buff[sizeof(struct pseudo_hdr) >> 1];
+ u8 buffc[sizeof(struct pseudo_hdr)];
+ } pseudo;
+ int i;
+ u32 *plong;
+
+ /* Check if there is room on the FIFO */
+ if (len > ft1000_read_fifo_len(dev)) {
+ udelay(10);
+ if (len > ft1000_read_fifo_len(dev))
+ udelay(20);
+ if (len > ft1000_read_fifo_len(dev))
+ udelay(20);
+ if (len > ft1000_read_fifo_len(dev))
+ udelay(20);
+ if (len > ft1000_read_fifo_len(dev))
+ udelay(20);
+ if (len > ft1000_read_fifo_len(dev))
+ udelay(20);
+ if (len > ft1000_read_fifo_len(dev)) {
+ pr_debug("Transmit FIFO is full - pkt drop\n");
+ info->stats.tx_errors++;
+ return SUCCESS;
+ }
+ }
+ /* Create pseudo header and send pseudo/ip to hardware */
+ if (info->AsicID == ELECTRABUZZ_ID)
+ pseudo.blk.length = len;
+ else
+ pseudo.blk.length = ntohs(len);
+
+ pseudo.blk.source = DSPID; /* Need to swap to get in correct
+ order */
+ pseudo.blk.destination = HOSTID;
+ pseudo.blk.portdest = NETWORKID; /* Need to swap to get in
+ correct order */
+ pseudo.blk.portsrc = DSPAIRID;
+ pseudo.blk.sh_str_id = 0;
+ pseudo.blk.control = 0;
+ pseudo.blk.rsvd1 = 0;
+ pseudo.blk.seq_num = 0;
+ pseudo.blk.rsvd2 = pcmcia->packetseqnum++;
+ pseudo.blk.qos_class = 0;
+ /* Calculate pseudo header checksum */
+ pseudo.blk.checksum = pseudo.buff[0];
+ for (i = 1; i < 7; i++)
+ pseudo.blk.checksum ^= pseudo.buff[i];
+
+ /* Production Mode */
+ if (info->AsicID == ELECTRABUZZ_ID) {
+ /* copy first word to UFIFO_BEG reg */
+ ft1000_write_reg(dev, FT1000_REG_UFIFO_BEG, pseudo.buff[0]);
+ pr_debug("data 0 BEG = 0x%04x\n", pseudo.buff[0]);
+
+ /* copy subsequent words to UFIFO_MID reg */
+ ft1000_write_reg(dev, FT1000_REG_UFIFO_MID, pseudo.buff[1]);
+ pr_debug("data 1 MID = 0x%04x\n", pseudo.buff[1]);
+ ft1000_write_reg(dev, FT1000_REG_UFIFO_MID, pseudo.buff[2]);
+ pr_debug("data 2 MID = 0x%04x\n", pseudo.buff[2]);
+ ft1000_write_reg(dev, FT1000_REG_UFIFO_MID, pseudo.buff[3]);
+ pr_debug("data 3 MID = 0x%04x\n", pseudo.buff[3]);
+ ft1000_write_reg(dev, FT1000_REG_UFIFO_MID, pseudo.buff[4]);
+ pr_debug("data 4 MID = 0x%04x\n", pseudo.buff[4]);
+ ft1000_write_reg(dev, FT1000_REG_UFIFO_MID, pseudo.buff[5]);
+ pr_debug("data 5 MID = 0x%04x\n", pseudo.buff[5]);
+ ft1000_write_reg(dev, FT1000_REG_UFIFO_MID, pseudo.buff[6]);
+ pr_debug("data 6 MID = 0x%04x\n", pseudo.buff[6]);
+ ft1000_write_reg(dev, FT1000_REG_UFIFO_MID, pseudo.buff[7]);
+ pr_debug("data 7 MID = 0x%04x\n", pseudo.buff[7]);
+
+ /* Write PPP type + IP Packet into Downlink FIFO */
+ for (i = 0; i < (len >> 1) - 1; i++) {
+ ft1000_write_reg(dev, FT1000_REG_UFIFO_MID,
+ htons(*packet));
+ pr_debug("data %d MID = 0x%04x\n",
+ i + 8, htons(*packet));
+ packet++;
+ }
+
+ /* Check for odd byte */
+ if (len & 0x0001) {
+ ft1000_write_reg(dev, FT1000_REG_UFIFO_MID,
+ htons(*packet));
+ pr_debug("data MID = 0x%04x\n", htons(*packet));
+ packet++;
+ ft1000_write_reg(dev, FT1000_REG_UFIFO_END,
+ htons(*packet));
+ pr_debug("data %d MID = 0x%04x\n",
+ i + 8, htons(*packet));
+ } else {
+ ft1000_write_reg(dev, FT1000_REG_UFIFO_END,
+ htons(*packet));
+ pr_debug("data %d MID = 0x%04x\n",
+ i + 8, htons(*packet));
+ }
+ } else {
+ outl(*(u32 *)&pseudo.buff[0],
+ dev->base_addr + FT1000_REG_MAG_UFDR);
+ pr_debug("Pseudo = 0x%8x\n", *(u32 *)&pseudo.buff[0]);
+ outl(*(u32 *)&pseudo.buff[2],
+ dev->base_addr + FT1000_REG_MAG_UFDR);
+ pr_debug("Pseudo = 0x%8x\n", *(u32 *)&pseudo.buff[2]);
+ outl(*(u32 *)&pseudo.buff[4],
+ dev->base_addr + FT1000_REG_MAG_UFDR);
+ pr_debug("Pseudo = 0x%8x\n", *(u32 *)&pseudo.buff[4]);
+ outl(*(u32 *)&pseudo.buff[6],
+ dev->base_addr + FT1000_REG_MAG_UFDR);
+ pr_debug("Pseudo = 0x%8x\n", *(u32 *)&pseudo.buff[6]);
+
+ plong = (u32 *)packet;
+ /* Write PPP type + IP Packet into Downlink FIFO */
+ for (i = 0; i < (len >> 2); i++)
+ outl(*plong++, dev->base_addr + FT1000_REG_MAG_UFDR);
+
+ /* Check for odd alignment */
+ if (len & 0x0003) {
+ pr_debug("data = 0x%8x\n", *plong);
+ outl(*plong++, dev->base_addr + FT1000_REG_MAG_UFDR);
+ }
+ outl(1, dev->base_addr + FT1000_REG_MAG_UFER);
+ }
+
+ info->stats.tx_packets++;
+ /* Add 14 bytes for MAC address plus ethernet type */
+ info->stats.tx_bytes += (len + 14);
+ return SUCCESS;
+}
+
+static struct net_device_stats *ft1000_stats(struct net_device *dev)
+{
+ struct ft1000_info *info = netdev_priv(dev);
+
+ return &info->stats;
+}
+
+static int ft1000_open(struct net_device *dev)
+{
+ ft1000_reset_card(dev);
+
+ /* schedule ft1000_hbchk to perform periodic heartbeat checks on DSP
+ * and ASIC */
+ init_timer(&poll_timer);
+ poll_timer.expires = jiffies + (2 * HZ);
+ poll_timer.data = (u_long)dev;
+ add_timer(&poll_timer);
+
+ return 0;
+}
+
+static int ft1000_close(struct net_device *dev)
+{
+ struct ft1000_info *info = netdev_priv(dev);
+
+ info->CardReady = 0;
+ del_timer(&poll_timer);
+
+ if (ft1000_card_present == 1) {
+ pr_debug("Media is down\n");
+ netif_stop_queue(dev);
+
+ ft1000_disable_interrupts(dev);
+ ft1000_write_reg(dev, FT1000_REG_RESET, DSP_RESET_BIT);
+
+ /* reset ASIC */
+ ft1000_reset_asic(dev);
+ }
+ return 0;
+}
+
+static int ft1000_start_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+ struct ft1000_info *info = netdev_priv(dev);
+ u8 *pdata;
+
+ if (skb == NULL) {
+ pr_debug("skb == NULL!!!\n");
+ return 0;
+ }
+
+ pr_debug("length of packet = %d\n", skb->len);
+
+ pdata = (u8 *)skb->data;
+
+ if (info->mediastate == 0) {
+ /* Drop packet is mediastate is down */
+ pr_debug("mediastate is down\n");
+ return SUCCESS;
+ }
+
+ if ((skb->len < ENET_HEADER_SIZE) || (skb->len > ENET_MAX_SIZE)) {
+ /* Drop packet which has invalid size */
+ pr_debug("invalid ethernet length\n");
+ return SUCCESS;
+ }
+ ft1000_copy_down_pkt(dev, (u16 *) (pdata + ENET_HEADER_SIZE - 2),
+ skb->len - ENET_HEADER_SIZE + 2);
+
+ dev_kfree_skb(skb);
+
+ return 0;
+}
+
+static irqreturn_t ft1000_interrupt(int irq, void *dev_id)
+{
+ struct net_device *dev = dev_id;
+ struct ft1000_info *info = netdev_priv(dev);
+ u16 tempword;
+ u16 inttype;
+ int cnt;
+
+ if (info->CardReady == 0) {
+ ft1000_disable_interrupts(dev);
+ return IRQ_HANDLED;
+ }
+
+ if (ft1000_chkcard(dev) == false) {
+ ft1000_disable_interrupts(dev);
+ return IRQ_HANDLED;
+ }
+
+ ft1000_disable_interrupts(dev);
+
+ /* Read interrupt type */
+ inttype = ft1000_read_reg(dev, FT1000_REG_SUP_ISR);
+
+ /* Make sure we process all interrupt before leaving the ISR due to the
+ * edge trigger interrupt type */
+ while (inttype) {
+ if (inttype & ISR_DOORBELL_PEND)
+ ft1000_parse_dpram_msg(dev);
+
+ if (inttype & ISR_RCV) {
+ pr_debug("Data in FIFO\n");
+
+ cnt = 0;
+ do {
+ /* Check if we have packets in the Downlink
+ * FIFO */
+ if (info->AsicID == ELECTRABUZZ_ID) {
+ tempword = ft1000_read_reg(dev,
+ FT1000_REG_DFIFO_STAT);
+ } else {
+ tempword = ft1000_read_reg(dev,
+ FT1000_REG_MAG_DFSR);
+ }
+ if (!(tempword & 0x1f))
+ break;
+ ft1000_copy_up_pkt(dev);
+ cnt++;
+ } while (cnt < MAX_RCV_LOOP);
+
+ }
+ /* clear interrupts */
+ tempword = ft1000_read_reg(dev, FT1000_REG_SUP_ISR);
+ pr_debug("interrupt status register = 0x%x\n", tempword);
+ ft1000_write_reg(dev, FT1000_REG_SUP_ISR, tempword);
+
+ /* Read interrupt type */
+ inttype = ft1000_read_reg(dev, FT1000_REG_SUP_ISR);
+ pr_debug("interrupt status register after clear = 0x%x\n",
+ inttype);
+ }
+ ft1000_enable_interrupts(dev);
+ return IRQ_HANDLED;
+}
+
+void stop_ft1000_card(struct net_device *dev)
+{
+ struct ft1000_info *info = netdev_priv(dev);
+ struct prov_record *ptr;
+ struct prov_record *tmp;
+ /* int cnt; */
+
+ info->CardReady = 0;
+ ft1000_card_present = 0;
+ netif_stop_queue(dev);
+ ft1000_disable_interrupts(dev);
+
+ /* Make sure we free any memory reserve for provisioning */
+ list_for_each_entry_safe(ptr, tmp, &info->prov_list, list) {
+ list_del(&ptr->list);
+ kfree(ptr->pprov_data);
+ kfree(ptr);
+ }
+
+ kfree(info->priv);
+
+ if (info->registered) {
+ unregister_netdev(dev);
+ info->registered = 0;
+ }
+
+ free_irq(dev->irq, dev);
+ release_region(dev->base_addr, 256);
+ release_firmware(fw_entry);
+ flarion_ft1000_cnt--;
+
+}
+
+static void ft1000_get_drvinfo(struct net_device *dev,
+ struct ethtool_drvinfo *info)
+{
+ struct ft1000_info *ft_info;
+
+ ft_info = netdev_priv(dev);
+
+ strlcpy(info->driver, "ft1000", sizeof(info->driver));
+ snprintf(info->bus_info, sizeof(info->bus_info), "PCMCIA 0x%lx",
+ dev->base_addr);
+ snprintf(info->fw_version, sizeof(info->fw_version), "%d.%d.%d.%d",
+ ft_info->DspVer[0], ft_info->DspVer[1], ft_info->DspVer[2],
+ ft_info->DspVer[3]);
+}
+
+static u32 ft1000_get_link(struct net_device *dev)
+{
+ struct ft1000_info *info;
+
+ info = netdev_priv(dev);
+ return info->mediastate;
+}
+
+static const struct ethtool_ops ops = {
+ .get_drvinfo = ft1000_get_drvinfo,
+ .get_link = ft1000_get_link
+};
+
+struct net_device *init_ft1000_card(struct pcmcia_device *link,
+ void *ft1000_reset)
+{
+ struct ft1000_info *info;
+ struct ft1000_pcmcia *pcmcia;
+ struct net_device *dev;
+
+ static const struct net_device_ops ft1000ops = {
+ .ndo_open = &ft1000_open,
+ .ndo_stop = &ft1000_close,
+ .ndo_start_xmit = &ft1000_start_xmit,
+ .ndo_get_stats = &ft1000_stats,
+ };
+
+ pr_debug("irq = %d, port = 0x%04llx\n",
+ link->irq, (unsigned long long)link->resource[0]->start);
+
+ flarion_ft1000_cnt++;
+
+ if (flarion_ft1000_cnt > 1) {
+ flarion_ft1000_cnt--;
+
+ dev_info(&link->dev,
+ "This driver can not support more than one instance\n");
+ return NULL;
+ }
+
+ dev = alloc_etherdev(sizeof(struct ft1000_info));
+ if (!dev) {
+ dev_err(&link->dev, "Failed to allocate etherdev\n");
+ return NULL;
+ }
+
+ SET_NETDEV_DEV(dev, &link->dev);
+ info = netdev_priv(dev);
+
+ memset(info, 0, sizeof(struct ft1000_info));
+
+ pr_debug("address of dev = 0x%p\n", dev);
+ pr_debug("address of dev info = 0x%p\n", info);
+ pr_debug("device name = %s\n", dev->name);
+
+ memset(&info->stats, 0, sizeof(struct net_device_stats));
+
+ info->priv = kzalloc(sizeof(struct ft1000_pcmcia), GFP_KERNEL);
+ pcmcia = info->priv;
+ pcmcia->link = link;
+
+ spin_lock_init(&info->dpram_lock);
+ info->DrvErrNum = 0;
+ info->registered = 1;
+ info->ft1000_reset = ft1000_reset;
+ info->mediastate = 0;
+ info->fifo_cnt = 0;
+ info->CardReady = 0;
+ info->DSP_TIME[0] = 0;
+ info->DSP_TIME[1] = 0;
+ info->DSP_TIME[2] = 0;
+ info->DSP_TIME[3] = 0;
+ flarion_ft1000_cnt = 0;
+
+ INIT_LIST_HEAD(&info->prov_list);
+
+ info->squeseqnum = 0;
+
+ /* dev->hard_start_xmit = &ft1000_start_xmit; */
+ /* dev->get_stats = &ft1000_stats; */
+ /* dev->open = &ft1000_open; */
+ /* dev->stop = &ft1000_close; */
+
+ dev->netdev_ops = &ft1000ops;
+
+ pr_debug("device name = %s\n", dev->name);
+
+ dev->irq = link->irq;
+ dev->base_addr = link->resource[0]->start;
+ if (pcmcia_get_mac_from_cis(link, dev)) {
+ netdev_err(dev, "Could not read mac address\n");
+ goto err_dev;
+ }
+
+ if (request_irq(dev->irq, ft1000_interrupt, IRQF_SHARED, dev->name,
+ dev)) {
+ netdev_err(dev, "Could not request_irq\n");
+ goto err_dev;
+ }
+
+ if (request_region(dev->base_addr, 256, dev->name) == NULL) {
+ netdev_err(dev, "Could not request_region\n");
+ goto err_irq;
+ }
+
+ if (register_netdev(dev)) {
+ pr_debug("Could not register netdev\n");
+ goto err_reg;
+ }
+
+ info->AsicID = ft1000_read_reg(dev, FT1000_REG_ASIC_ID);
+ if (info->AsicID == ELECTRABUZZ_ID) {
+ pr_debug("ELECTRABUZZ ASIC\n");
+ if (reject_firmware(&fw_entry, "/*(DEBLOBBED)*/",
+ &link->dev) != 0) {
+ pr_info("Could not open /*(DEBLOBBED)*/\n");
+ goto err_unreg;
+ }
+ } else {
+ pr_debug("MAGNEMITE ASIC\n");
+ if (reject_firmware(&fw_entry, "/*(DEBLOBBED)*/",
+ &link->dev) != 0) {
+ pr_info("Could not open /*(DEBLOBBED)*/\n");
+ goto err_unreg;
+ }
+ }
+
+ ft1000_enable_interrupts(dev);
+
+ ft1000_card_present = 1;
+ dev->ethtool_ops = &ops;
+ pr_info("%s: addr 0x%04lx irq %d, MAC addr %pM\n",
+ dev->name, dev->base_addr, dev->irq, dev->dev_addr);
+ return dev;
+
+err_unreg:
+ unregister_netdev(dev);
+err_reg:
+ release_region(dev->base_addr, 256);
+err_irq:
+ free_irq(dev->irq, dev);
+err_dev:
+ free_netdev(dev);
+ return NULL;
+}
diff --git a/drivers/staging/ft1000/ft1000-usb/Makefile b/drivers/staging/ft1000/ft1000-usb/Makefile
new file mode 100644
index 000000000..7c4b78456
--- /dev/null
+++ b/drivers/staging/ft1000/ft1000-usb/Makefile
@@ -0,0 +1,3 @@
+obj-$(CONFIG_FT1000_USB) += ft1000.o
+
+ft1000-y := ft1000_debug.o ft1000_download.o ft1000_hw.o ft1000_usb.o
diff --git a/drivers/staging/ft1000/ft1000-usb/ft1000_debug.c b/drivers/staging/ft1000/ft1000-usb/ft1000_debug.c
new file mode 100644
index 000000000..2d758fb26
--- /dev/null
+++ b/drivers/staging/ft1000/ft1000-usb/ft1000_debug.c
@@ -0,0 +1,779 @@
+/*
+ *---------------------------------------------------------------------------
+ * FT1000 driver for Flarion Flash OFDM NIC Device
+ *
+ * Copyright (C) 2006 Flarion Technologies, All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option) any
+ * later version. This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * 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.
+ *---------------------------------------------------------------------------
+ *
+ * File: ft1000_chdev.c
+ *
+ * Description: Custom character device dispatch routines.
+ *
+ * History:
+ * 8/29/02 Whc Ported to Linux.
+ * 6/05/06 Whc Porting to Linux 2.6.9
+ *
+ *---------------------------------------------------------------------------
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/poll.h>
+#include <linux/netdevice.h>
+#include <linux/delay.h>
+
+#include <linux/ioctl.h>
+#include <linux/debugfs.h>
+#include "ft1000_usb.h"
+
+static int ft1000_flarion_cnt;
+
+static int ft1000_open(struct inode *inode, struct file *file);
+static unsigned int ft1000_poll_dev(struct file *file, poll_table *wait);
+static long ft1000_ioctl(struct file *file, unsigned int command,
+ unsigned long argument);
+static int ft1000_release(struct inode *inode, struct file *file);
+
+/* List to free receive command buffer pool */
+struct list_head freercvpool;
+
+/* lock to arbitrate free buffer list for receive command data */
+spinlock_t free_buff_lock;
+
+int numofmsgbuf = 0;
+
+/*
+ * Table of entry-point routines for char device
+ */
+static const struct file_operations ft1000fops = {
+ .unlocked_ioctl = ft1000_ioctl,
+ .poll = ft1000_poll_dev,
+ .open = ft1000_open,
+ .release = ft1000_release,
+ .llseek = no_llseek,
+};
+
+/*
+ ---------------------------------------------------------------------------
+ * Function: ft1000_get_buffer
+ *
+ * Parameters:
+ *
+ * Returns:
+ *
+ * Description:
+ *
+ * Notes:
+ *
+ *---------------------------------------------------------------------------
+ */
+struct dpram_blk *ft1000_get_buffer(struct list_head *bufflist)
+{
+ unsigned long flags;
+ struct dpram_blk *ptr;
+
+ spin_lock_irqsave(&free_buff_lock, flags);
+ /* Check if buffer is available */
+ if (list_empty(bufflist)) {
+ pr_debug("No more buffer - %d\n", numofmsgbuf);
+ ptr = NULL;
+ } else {
+ numofmsgbuf--;
+ ptr = list_entry(bufflist->next, struct dpram_blk, list);
+ list_del(&ptr->list);
+ /* pr_debug("number of free msg buffers = %d\n", numofmsgbuf); */
+ }
+ spin_unlock_irqrestore(&free_buff_lock, flags);
+
+ return ptr;
+}
+
+
+
+
+/*
+ *---------------------------------------------------------------------------
+ * Function: ft1000_free_buffer
+ *
+ * Parameters:
+ *
+ * Returns:
+ *
+ * Description:
+ *
+ * Notes:
+ *
+ *---------------------------------------------------------------------------
+ */
+void ft1000_free_buffer(struct dpram_blk *pdpram_blk, struct list_head *plist)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&free_buff_lock, flags);
+ /* Put memory back to list */
+ list_add_tail(&pdpram_blk->list, plist);
+ numofmsgbuf++;
+ /*pr_debug("number of free msg buffers = %d\n", numofmsgbuf); */
+ spin_unlock_irqrestore(&free_buff_lock, flags);
+}
+
+/*
+ *---------------------------------------------------------------------------
+ * Function: ft1000_CreateDevice
+ *
+ * Parameters: dev - pointer to adapter object
+ *
+ * Returns: 0 if successful
+ *
+ * Description: Creates a private char device.
+ *
+ * Notes: Only called by init_module().
+ *
+ *---------------------------------------------------------------------------
+ */
+int ft1000_create_dev(struct ft1000_usb *dev)
+{
+ int result;
+ int i;
+ struct dentry *dir, *file;
+ struct ft1000_debug_dirs *tmp;
+
+ /* make a new device name */
+ sprintf(dev->DeviceName, "%s%d", "FT1000_", dev->CardNumber);
+
+ pr_debug("number of instance = %d\n", ft1000_flarion_cnt);
+ pr_debug("DeviceCreated = %x\n", dev->DeviceCreated);
+
+ if (dev->DeviceCreated) {
+ pr_debug("\"%s\" already registered\n", dev->DeviceName);
+ return -EIO;
+ }
+
+
+ /* register the device */
+ pr_debug("\"%s\" debugfs device registration\n", dev->DeviceName);
+
+ tmp = kmalloc(sizeof(struct ft1000_debug_dirs), GFP_KERNEL);
+ if (tmp == NULL) {
+ result = -1;
+ goto fail;
+ }
+
+ dir = debugfs_create_dir(dev->DeviceName, NULL);
+ if (IS_ERR(dir)) {
+ result = PTR_ERR(dir);
+ goto debug_dir_fail;
+ }
+
+ file = debugfs_create_file("device", S_IRUGO | S_IWUSR, dir,
+ dev, &ft1000fops);
+ if (IS_ERR(file)) {
+ result = PTR_ERR(file);
+ goto debug_file_fail;
+ }
+
+ tmp->dent = dir;
+ tmp->file = file;
+ tmp->int_number = dev->CardNumber;
+ list_add(&tmp->list, &dev->nodes.list);
+
+ pr_debug("registered debugfs directory \"%s\"\n", dev->DeviceName);
+
+ /* initialize application information */
+ dev->appcnt = 0;
+ for (i = 0; i < MAX_NUM_APP; i++) {
+ dev->app_info[i].nTxMsg = 0;
+ dev->app_info[i].nRxMsg = 0;
+ dev->app_info[i].nTxMsgReject = 0;
+ dev->app_info[i].nRxMsgMiss = 0;
+ dev->app_info[i].fileobject = NULL;
+ dev->app_info[i].app_id = i+1;
+ dev->app_info[i].DspBCMsgFlag = 0;
+ dev->app_info[i].NumOfMsg = 0;
+ init_waitqueue_head(&dev->app_info[i].wait_dpram_msg);
+ INIT_LIST_HEAD(&dev->app_info[i].app_sqlist);
+ }
+
+ dev->DeviceCreated = TRUE;
+ ft1000_flarion_cnt++;
+
+ return 0;
+
+debug_file_fail:
+ debugfs_remove(dir);
+debug_dir_fail:
+ kfree(tmp);
+fail:
+ return result;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ * Function: ft1000_DestroyDeviceDEBUG
+ *
+ * Parameters: dev - pointer to adapter object
+ *
+ * Description: Destroys a private char device.
+ *
+ * Notes: Only called by cleanup_module().
+ *
+ *---------------------------------------------------------------------------
+ */
+void ft1000_destroy_dev(struct net_device *netdev)
+{
+ struct ft1000_info *info = netdev_priv(netdev);
+ struct ft1000_usb *dev = info->priv;
+ int i;
+ struct dpram_blk *pdpram_blk;
+ struct dpram_blk *ptr;
+ struct list_head *pos, *q;
+ struct ft1000_debug_dirs *dir;
+
+ if (dev->DeviceCreated) {
+ ft1000_flarion_cnt--;
+ list_for_each_safe(pos, q, &dev->nodes.list) {
+ dir = list_entry(pos, struct ft1000_debug_dirs, list);
+ if (dir->int_number == dev->CardNumber) {
+ debugfs_remove(dir->file);
+ debugfs_remove(dir->dent);
+ list_del(pos);
+ kfree(dir);
+ }
+ }
+ pr_debug("unregistered device \"%s\"\n", dev->DeviceName);
+
+ /* Make sure we free any memory reserve for slow Queue */
+ for (i = 0; i < MAX_NUM_APP; i++) {
+ while (list_empty(&dev->app_info[i].app_sqlist) == 0) {
+ pdpram_blk = list_entry(dev->app_info[i].app_sqlist.next, struct dpram_blk, list);
+ list_del(&pdpram_blk->list);
+ ft1000_free_buffer(pdpram_blk, &freercvpool);
+
+ }
+ wake_up_interruptible(&dev->app_info[i].wait_dpram_msg);
+ }
+
+ /* Remove buffer allocated for receive command data */
+ if (ft1000_flarion_cnt == 0) {
+ while (list_empty(&freercvpool) == 0) {
+ ptr = list_entry(freercvpool.next, struct dpram_blk, list);
+ list_del(&ptr->list);
+ kfree(ptr->pbuffer);
+ kfree(ptr);
+ }
+ }
+ dev->DeviceCreated = FALSE;
+ }
+
+
+}
+
+/*
+ *---------------------------------------------------------------------------
+ * Function: ft1000_open
+ *
+ * Parameters:
+ *
+ * Description:
+ *
+ * Notes:
+ *
+ *---------------------------------------------------------------------------
+ */
+static int ft1000_open(struct inode *inode, struct file *file)
+{
+ struct ft1000_info *info;
+ struct ft1000_usb *dev = (struct ft1000_usb *)inode->i_private;
+ int i, num;
+
+ num = MINOR(inode->i_rdev) & 0xf;
+ pr_debug("minor number=%d\n", num);
+
+ info = file->private_data = netdev_priv(dev->net);
+
+ pr_debug("f_owner = %p number of application = %d\n",
+ &file->f_owner, dev->appcnt);
+
+ /* Check if maximum number of application exceeded */
+ if (dev->appcnt > MAX_NUM_APP) {
+ pr_debug("Maximum number of application exceeded\n");
+ return -EACCES;
+ }
+
+ /* Search for available application info block */
+ for (i = 0; i < MAX_NUM_APP; i++) {
+ if ((dev->app_info[i].fileobject == NULL))
+ break;
+ }
+
+ /* Fail due to lack of application info block */
+ if (i == MAX_NUM_APP) {
+ pr_debug("Could not find an application info block\n");
+ return -EACCES;
+ }
+
+ dev->appcnt++;
+ dev->app_info[i].fileobject = &file->f_owner;
+ dev->app_info[i].nTxMsg = 0;
+ dev->app_info[i].nRxMsg = 0;
+ dev->app_info[i].nTxMsgReject = 0;
+ dev->app_info[i].nRxMsgMiss = 0;
+
+ nonseekable_open(inode, file);
+ return 0;
+}
+
+
+/*
+ *---------------------------------------------------------------------------
+ * Function: ft1000_poll_dev
+ *
+ * Parameters:
+ *
+ * Description:
+ *
+ * Notes:
+ *
+ *---------------------------------------------------------------------------
+ */
+
+static unsigned int ft1000_poll_dev(struct file *file, poll_table *wait)
+{
+ struct net_device *netdev = file->private_data;
+ struct ft1000_info *info = netdev_priv(netdev);
+ struct ft1000_usb *dev = info->priv;
+ int i;
+
+ if (ft1000_flarion_cnt == 0) {
+ pr_debug("called with ft1000_flarion_cnt value zero\n");
+ return -EBADF;
+ }
+
+ /* Search for matching file object */
+ for (i = 0; i < MAX_NUM_APP; i++) {
+ if (dev->app_info[i].fileobject == &file->f_owner) {
+ /* pr_debug("Message is for AppId = %d\n", dev->app_info[i].app_id); */
+ break;
+ }
+ }
+
+ /* Could not find application info block */
+ if (i == MAX_NUM_APP) {
+ pr_debug("Could not find application info block\n");
+ return -EACCES;
+ }
+
+ if (list_empty(&dev->app_info[i].app_sqlist) == 0) {
+ pr_debug("Message detected in slow queue\n");
+ return(POLLIN | POLLRDNORM | POLLPRI);
+ }
+
+ poll_wait(file, &dev->app_info[i].wait_dpram_msg, wait);
+ /* pr_debug("Polling for data from DSP\n"); */
+
+ return 0;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ * Function: ft1000_ioctl
+ *
+ * Parameters:
+ *
+ * Description:
+ *
+ * Notes:
+ *
+ *---------------------------------------------------------------------------
+ */
+static long ft1000_ioctl(struct file *file, unsigned int command,
+ unsigned long argument)
+{
+ void __user *argp = (void __user *)argument;
+ struct ft1000_info *info;
+ struct ft1000_usb *ft1000dev;
+ int result = 0;
+ int cmd;
+ int i;
+ u16 tempword;
+ unsigned long flags;
+ struct timeval tv;
+ struct IOCTL_GET_VER get_ver_data;
+ struct IOCTL_GET_DSP_STAT get_stat_data;
+ u8 ConnectionMsg[] = {0x00, 0x44, 0x10, 0x20, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x93, 0x64,
+ 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x0a,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x02, 0x37, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x7f, 0x00,
+ 0x00, 0x01, 0x00, 0x00};
+
+ unsigned short ledStat = 0;
+ unsigned short conStat = 0;
+
+ if (ft1000_flarion_cnt == 0) {
+ pr_debug("called with ft1000_flarion_cnt of zero\n");
+ return -EBADF;
+ }
+
+ /* pr_debug("command = 0x%x argument = 0x%8x\n", command, (u32)argument); */
+
+ info = file->private_data;
+ ft1000dev = info->priv;
+ cmd = _IOC_NR(command);
+ /* pr_debug("cmd = 0x%x\n", cmd); */
+
+ /* process the command */
+ switch (cmd) {
+ case IOCTL_REGISTER_CMD:
+ pr_debug("IOCTL_FT1000_REGISTER called\n");
+ result = get_user(tempword, (__u16 __user *)argp);
+ if (result) {
+ pr_debug("result = %d failed to get_user\n", result);
+ break;
+ }
+ if (tempword == DSPBCMSGID) {
+ /* Search for matching file object */
+ for (i = 0; i < MAX_NUM_APP; i++) {
+ if (ft1000dev->app_info[i].fileobject == &file->f_owner) {
+ ft1000dev->app_info[i].DspBCMsgFlag = 1;
+ pr_debug("Registered for broadcast messages\n");
+ break;
+ }
+ }
+ }
+ break;
+
+ case IOCTL_GET_VER_CMD:
+ pr_debug("IOCTL_FT1000_GET_VER called\n");
+
+ get_ver_data.drv_ver = FT1000_DRV_VER;
+
+ if (copy_to_user(argp, &get_ver_data, sizeof(get_ver_data))) {
+ pr_debug("copy fault occurred\n");
+ result = -EFAULT;
+ break;
+ }
+
+ pr_debug("driver version = 0x%x\n",
+ (unsigned int)get_ver_data.drv_ver);
+
+ break;
+ case IOCTL_CONNECT:
+ /* Connect Message */
+ pr_debug("IOCTL_FT1000_CONNECT\n");
+ ConnectionMsg[79] = 0xfc;
+ result = card_send_command(ft1000dev, ConnectionMsg, 0x4c);
+
+ break;
+ case IOCTL_DISCONNECT:
+ /* Disconnect Message */
+ pr_debug("IOCTL_FT1000_DISCONNECT\n");
+ ConnectionMsg[79] = 0xfd;
+ result = card_send_command(ft1000dev, ConnectionMsg, 0x4c);
+ break;
+ case IOCTL_GET_DSP_STAT_CMD:
+ /* pr_debug("IOCTL_FT1000_GET_DSP_STAT\n"); */
+ memset(&get_stat_data, 0, sizeof(get_stat_data));
+ memcpy(get_stat_data.DspVer, info->DspVer, DSPVERSZ);
+ memcpy(get_stat_data.HwSerNum, info->HwSerNum, HWSERNUMSZ);
+ memcpy(get_stat_data.Sku, info->Sku, SKUSZ);
+ memcpy(get_stat_data.eui64, info->eui64, EUISZ);
+
+ if (info->ProgConStat != 0xFF) {
+ ft1000_read_dpram16(ft1000dev, FT1000_MAG_DSP_LED, (u8 *)&ledStat, FT1000_MAG_DSP_LED_INDX);
+ get_stat_data.LedStat = ntohs(ledStat);
+ pr_debug("LedStat = 0x%x\n", get_stat_data.LedStat);
+ ft1000_read_dpram16(ft1000dev, FT1000_MAG_DSP_CON_STATE, (u8 *)&conStat, FT1000_MAG_DSP_CON_STATE_INDX);
+ get_stat_data.ConStat = ntohs(conStat);
+ pr_debug("ConStat = 0x%x\n", get_stat_data.ConStat);
+ } else {
+ get_stat_data.ConStat = 0x0f;
+ }
+
+
+ get_stat_data.nTxPkts = info->stats.tx_packets;
+ get_stat_data.nRxPkts = info->stats.rx_packets;
+ get_stat_data.nTxBytes = info->stats.tx_bytes;
+ get_stat_data.nRxBytes = info->stats.rx_bytes;
+ do_gettimeofday(&tv);
+ get_stat_data.ConTm = (u32)(tv.tv_sec - info->ConTm);
+ pr_debug("Connection Time = %d\n", (int)get_stat_data.ConTm);
+ if (copy_to_user(argp, &get_stat_data, sizeof(get_stat_data))) {
+ pr_debug("copy fault occurred\n");
+ result = -EFAULT;
+ break;
+ }
+ pr_debug("GET_DSP_STAT succeed\n");
+ break;
+ case IOCTL_SET_DPRAM_CMD:
+ {
+ struct IOCTL_DPRAM_BLK *dpram_data = NULL;
+ /* struct IOCTL_DPRAM_COMMAND dpram_command; */
+ u16 qtype;
+ u16 msgsz;
+ struct pseudo_hdr *ppseudo_hdr;
+ u16 *pmsg;
+ u16 total_len;
+ u16 app_index;
+ u16 status;
+
+ /* pr_debug("IOCTL_FT1000_SET_DPRAM called\n");*/
+
+
+ if (ft1000_flarion_cnt == 0)
+ return -EBADF;
+
+ if (ft1000dev->DrvMsgPend)
+ return -ENOTTY;
+
+ if (ft1000dev->fProvComplete == 0)
+ return -EACCES;
+
+ ft1000dev->fAppMsgPend = true;
+
+ if (info->CardReady) {
+
+ /* pr_debug("try to SET_DPRAM\n"); */
+
+ /* Get the length field to see how many bytes to copy */
+ result = get_user(msgsz, (__u16 __user *)argp);
+ if (result)
+ break;
+ msgsz = ntohs(msgsz);
+ /* pr_debug("length of message = %d\n", msgsz); */
+
+ if (msgsz > MAX_CMD_SQSIZE) {
+ pr_debug("bad message length = %d\n", msgsz);
+ result = -EINVAL;
+ break;
+ }
+
+ result = -ENOMEM;
+ dpram_data = kmalloc(msgsz + 2, GFP_KERNEL);
+ if (!dpram_data)
+ break;
+
+ if (copy_from_user(dpram_data, argp, msgsz+2)) {
+ pr_debug("copy fault occurred\n");
+ result = -EFAULT;
+ } else {
+ /* Check if this message came from a registered application */
+ for (i = 0; i < MAX_NUM_APP; i++) {
+ if (ft1000dev->app_info[i].fileobject == &file->f_owner)
+ break;
+ }
+ if (i == MAX_NUM_APP) {
+ pr_debug("No matching application fileobject\n");
+ result = -EINVAL;
+ kfree(dpram_data);
+ break;
+ }
+ app_index = i;
+
+ /* Check message qtype type which is the lower byte within qos_class */
+ qtype = ntohs(dpram_data->pseudohdr.qos_class) & 0xff;
+ /* pr_debug("qtype = %d\n", qtype); */
+ if (qtype) {
+ } else {
+ /* Put message into Slow Queue */
+ /* Only put a message into the DPRAM if msg doorbell is available */
+ status = ft1000_read_register(ft1000dev, &tempword, FT1000_REG_DOORBELL);
+ /* pr_debug("READ REGISTER tempword=%x\n", tempword); */
+ if (tempword & FT1000_DB_DPRAM_TX) {
+ /* Suspend for 2ms and try again due to DSP doorbell busy */
+ mdelay(2);
+ status = ft1000_read_register(ft1000dev, &tempword, FT1000_REG_DOORBELL);
+ if (tempword & FT1000_DB_DPRAM_TX) {
+ /* Suspend for 1ms and try again due to DSP doorbell busy */
+ mdelay(1);
+ status = ft1000_read_register(ft1000dev, &tempword, FT1000_REG_DOORBELL);
+ if (tempword & FT1000_DB_DPRAM_TX) {
+ status = ft1000_read_register(ft1000dev, &tempword, FT1000_REG_DOORBELL);
+ if (tempword & FT1000_DB_DPRAM_TX) {
+ /* Suspend for 3ms and try again due to DSP doorbell busy */
+ mdelay(3);
+ status = ft1000_read_register(ft1000dev, &tempword, FT1000_REG_DOORBELL);
+ if (tempword & FT1000_DB_DPRAM_TX) {
+ pr_debug("Doorbell not available\n");
+ result = -ENOTTY;
+ kfree(dpram_data);
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ /*pr_debug("finished reading register\n"); */
+
+ /* Make sure we are within the limits of the slow queue memory limitation */
+ if ((msgsz < MAX_CMD_SQSIZE) && (msgsz > PSEUDOSZ)) {
+ /* Need to put sequence number plus new checksum for message */
+ pmsg = (u16 *)&dpram_data->pseudohdr;
+ ppseudo_hdr = (struct pseudo_hdr *)pmsg;
+ total_len = msgsz+2;
+ if (total_len & 0x1)
+ total_len++;
+
+ /* Insert slow queue sequence number */
+ ppseudo_hdr->seq_num = info->squeseqnum++;
+ ppseudo_hdr->portsrc = ft1000dev->app_info[app_index].app_id;
+ /* Calculate new checksum */
+ ppseudo_hdr->checksum = *pmsg++;
+ /* pr_debug("checksum = 0x%x\n", ppseudo_hdr->checksum); */
+ for (i = 1; i < 7; i++) {
+ ppseudo_hdr->checksum ^= *pmsg++;
+ /* pr_debug("checksum = 0x%x\n", ppseudo_hdr->checksum); */
+ }
+ pmsg++;
+ ppseudo_hdr = (struct pseudo_hdr *)pmsg;
+ result = card_send_command(ft1000dev, dpram_data, total_len+2);
+
+
+ ft1000dev->app_info[app_index].nTxMsg++;
+ } else {
+ result = -EINVAL;
+ }
+ }
+ }
+ } else {
+ pr_debug("Card not ready take messages\n");
+ result = -EACCES;
+ }
+ kfree(dpram_data);
+
+ }
+ break;
+ case IOCTL_GET_DPRAM_CMD:
+ {
+ struct dpram_blk *pdpram_blk;
+ struct IOCTL_DPRAM_BLK __user *pioctl_dpram;
+ int msglen;
+
+ /* pr_debug("IOCTL_FT1000_GET_DPRAM called\n"); */
+
+ if (ft1000_flarion_cnt == 0)
+ return -EBADF;
+
+ /* Search for matching file object */
+ for (i = 0; i < MAX_NUM_APP; i++) {
+ if (ft1000dev->app_info[i].fileobject == &file->f_owner) {
+ /*pr_debug("Message is for AppId = %d\n", ft1000dev->app_info[i].app_id); */
+ break;
+ }
+ }
+
+ /* Could not find application info block */
+ if (i == MAX_NUM_APP) {
+ pr_debug("Could not find application info block\n");
+ result = -EBADF;
+ break;
+ }
+
+ result = 0;
+ pioctl_dpram = argp;
+ if (list_empty(&ft1000dev->app_info[i].app_sqlist) == 0) {
+ /* pr_debug("Message detected in slow queue\n"); */
+ spin_lock_irqsave(&free_buff_lock, flags);
+ pdpram_blk = list_entry(ft1000dev->app_info[i].app_sqlist.next, struct dpram_blk, list);
+ list_del(&pdpram_blk->list);
+ ft1000dev->app_info[i].NumOfMsg--;
+ /* pr_debug("NumOfMsg for app %d = %d\n", i, ft1000dev->app_info[i].NumOfMsg); */
+ spin_unlock_irqrestore(&free_buff_lock, flags);
+ msglen = ntohs(*(u16 *)pdpram_blk->pbuffer) + PSEUDOSZ;
+ result = get_user(msglen, &pioctl_dpram->total_len);
+ if (result)
+ break;
+ msglen = htons(msglen);
+ /* pr_debug("msg length = %x\n", msglen); */
+ if (copy_to_user(&pioctl_dpram->pseudohdr, pdpram_blk->pbuffer, msglen)) {
+ pr_debug("copy fault occurred\n");
+ result = -EFAULT;
+ break;
+ }
+
+ ft1000_free_buffer(pdpram_blk, &freercvpool);
+ result = msglen;
+ }
+ /* pr_debug("IOCTL_FT1000_GET_DPRAM no message\n"); */
+ }
+ break;
+
+ default:
+ pr_debug("unknown command: 0x%x\n", command);
+ result = -ENOTTY;
+ break;
+ }
+ ft1000dev->fAppMsgPend = false;
+ return result;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ * Function: ft1000_release
+ *
+ * Parameters:
+ *
+ * Description:
+ *
+ * Notes:
+ *
+ *---------------------------------------------------------------------------
+ */
+static int ft1000_release(struct inode *inode, struct file *file)
+{
+ struct ft1000_info *info;
+ struct net_device *dev;
+ struct ft1000_usb *ft1000dev;
+ int i;
+ struct dpram_blk *pdpram_blk;
+ struct dpram_blk *tmp;
+
+ dev = file->private_data;
+ info = netdev_priv(dev);
+ ft1000dev = info->priv;
+
+ if (ft1000_flarion_cnt == 0) {
+ ft1000dev->appcnt--;
+ return -EBADF;
+ }
+
+ /* Search for matching file object */
+ for (i = 0; i < MAX_NUM_APP; i++) {
+ if (ft1000dev->app_info[i].fileobject == &file->f_owner) {
+ /* pr_debug("Message is for AppId = %d\n", ft1000dev->app_info[i].app_id); */
+ break;
+ }
+ }
+
+ if (i == MAX_NUM_APP)
+ return 0;
+
+ list_for_each_entry_safe(pdpram_blk, tmp, &ft1000dev->app_info[i].app_sqlist, list) {
+ pr_debug("Remove and free memory queue up on slow queue\n");
+ list_del(&pdpram_blk->list);
+ ft1000_free_buffer(pdpram_blk, &freercvpool);
+ }
+
+ /* initialize application information */
+ ft1000dev->appcnt--;
+ pr_debug("appcnt = %d\n", ft1000dev->appcnt);
+ ft1000dev->app_info[i].fileobject = NULL;
+
+ return 0;
+}
diff --git a/drivers/staging/ft1000/ft1000-usb/ft1000_download.c b/drivers/staging/ft1000/ft1000-usb/ft1000_download.c
new file mode 100644
index 000000000..5def347be
--- /dev/null
+++ b/drivers/staging/ft1000/ft1000-usb/ft1000_download.c
@@ -0,0 +1,1052 @@
+/*
+ * CopyRight (C) 2007 Qualcomm Inc. All Rights Reserved.
+ *
+ * This file is part of Express Card USB Driver
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/usb.h>
+#include <linux/vmalloc.h>
+#include "ft1000_usb.h"
+
+
+#define DWNLD_HANDSHAKE_LOC 0x02
+#define DWNLD_TYPE_LOC 0x04
+#define DWNLD_SIZE_MSW_LOC 0x06
+#define DWNLD_SIZE_LSW_LOC 0x08
+#define DWNLD_PS_HDR_LOC 0x0A
+
+#define MAX_DSP_WAIT_LOOPS 40
+#define DSP_WAIT_SLEEP_TIME 1000 /* 1 millisecond */
+#define DSP_WAIT_DISPATCH_LVL 50 /* 50 usec */
+
+#define HANDSHAKE_TIMEOUT_VALUE 0xF1F1
+#define HANDSHAKE_RESET_VALUE 0xFEFE /* When DSP requests startover */
+#define HANDSHAKE_RESET_VALUE_USB 0xFE7E /* When DSP requests startover */
+#define HANDSHAKE_DSP_BL_READY 0xFEFE /* At start DSP writes this when bootloader ready */
+#define HANDSHAKE_DSP_BL_READY_USB 0xFE7E /* At start DSP writes this when bootloader ready */
+#define HANDSHAKE_DRIVER_READY 0xFFFF /* Driver writes after receiving 0xFEFE */
+#define HANDSHAKE_SEND_DATA 0x0000 /* DSP writes this when ready for more data */
+
+#define HANDSHAKE_REQUEST 0x0001 /* Request from DSP */
+#define HANDSHAKE_RESPONSE 0x0000 /* Satisfied DSP request */
+
+#define REQUEST_CODE_LENGTH 0x0000
+#define REQUEST_RUN_ADDRESS 0x0001
+#define REQUEST_CODE_SEGMENT 0x0002 /* In WORD count */
+#define REQUEST_DONE_BL 0x0003
+#define REQUEST_DONE_CL 0x0004
+#define REQUEST_VERSION_INFO 0x0005
+#define REQUEST_CODE_BY_VERSION 0x0006
+#define REQUEST_MAILBOX_DATA 0x0007
+#define REQUEST_FILE_CHECKSUM 0x0008
+
+#define STATE_START_DWNLD 0x01
+#define STATE_BOOT_DWNLD 0x02
+#define STATE_CODE_DWNLD 0x03
+#define STATE_DONE_DWNLD 0x04
+#define STATE_SECTION_PROV 0x05
+#define STATE_DONE_PROV 0x06
+#define STATE_DONE_FILE 0x07
+
+#define MAX_LENGTH 0x7f0
+
+/* Temporary download mechanism for Magnemite */
+#define DWNLD_MAG_TYPE_LOC 0x00
+#define DWNLD_MAG_LEN_LOC 0x01
+#define DWNLD_MAG_ADDR_LOC 0x02
+#define DWNLD_MAG_CHKSUM_LOC 0x03
+#define DWNLD_MAG_VAL_LOC 0x04
+
+#define HANDSHAKE_MAG_DSP_BL_READY 0xFEFE0000 /* At start DSP writes this when bootloader ready */
+#define HANDSHAKE_MAG_DSP_ENTRY 0x01000000 /* Dsp writes this to request for entry address */
+#define HANDSHAKE_MAG_DSP_DATA 0x02000000 /* Dsp writes this to request for data block */
+#define HANDSHAKE_MAG_DSP_DONE 0x03000000 /* Dsp writes this to indicate download done */
+
+#define HANDSHAKE_MAG_DRV_READY 0xFFFF0000 /* Driver writes this to indicate ready to download */
+#define HANDSHAKE_MAG_DRV_DATA 0x02FECDAB /* Driver writes this to indicate data available to DSP */
+#define HANDSHAKE_MAG_DRV_ENTRY 0x01FECDAB /* Driver writes this to indicate entry point to DSP */
+
+#define HANDSHAKE_MAG_TIMEOUT_VALUE 0xF1F1
+
+
+/* New Magnemite downloader */
+#define DWNLD_MAG1_HANDSHAKE_LOC 0x00
+#define DWNLD_MAG1_TYPE_LOC 0x01
+#define DWNLD_MAG1_SIZE_LOC 0x02
+#define DWNLD_MAG1_PS_HDR_LOC 0x03
+
+struct dsp_file_hdr {
+ long version_id; /* Version ID of this image format. */
+ long package_id; /* Package ID of code release. */
+ long build_date; /* Date/time stamp when file was built. */
+ long commands_offset; /* Offset to attached commands in Pseudo Hdr format. */
+ long loader_offset; /* Offset to bootloader code. */
+ long loader_code_address; /* Start address of bootloader. */
+ long loader_code_end; /* Where bootloader code ends. */
+ long loader_code_size;
+ long version_data_offset; /* Offset were scrambled version data begins. */
+ long version_data_size; /* Size, in words, of scrambled version data. */
+ long nDspImages; /* Number of DSP images in file. */
+};
+
+#pragma pack(1)
+struct dsp_image_info {
+ long coff_date; /* Date/time when DSP Coff image was built. */
+ long begin_offset; /* Offset in file where image begins. */
+ long end_offset; /* Offset in file where image begins. */
+ long run_address; /* On chip Start address of DSP code. */
+ long image_size; /* Size of image. */
+ long version; /* Embedded version # of DSP code. */
+ unsigned short checksum; /* DSP File checksum */
+ unsigned short pad1;
+};
+
+
+/* checks if the doorbell register is cleared */
+static int check_usb_db(struct ft1000_usb *ft1000dev)
+{
+ int loopcnt;
+ u16 temp;
+ int status;
+
+ loopcnt = 0;
+
+ while (loopcnt < 10) {
+ status = ft1000_read_register(ft1000dev, &temp,
+ FT1000_REG_DOORBELL);
+ pr_debug("read FT1000_REG_DOORBELL value is %x\n", temp);
+ if (temp & 0x0080) {
+ pr_debug("Got checkusb doorbell\n");
+ status = ft1000_write_register(ft1000dev, 0x0080,
+ FT1000_REG_DOORBELL);
+ status = ft1000_write_register(ft1000dev, 0x0100,
+ FT1000_REG_DOORBELL);
+ status = ft1000_write_register(ft1000dev, 0x8000,
+ FT1000_REG_DOORBELL);
+ break;
+ }
+ loopcnt++;
+ msleep(10);
+
+ }
+
+ loopcnt = 0;
+ while (loopcnt < 20) {
+ status = ft1000_read_register(ft1000dev, &temp,
+ FT1000_REG_DOORBELL);
+ pr_debug("Doorbell = 0x%x\n", temp);
+ if (temp & 0x8000) {
+ loopcnt++;
+ msleep(10);
+ } else {
+ pr_debug("door bell is cleared, return 0\n");
+ return 0;
+ }
+ }
+
+ return -1;
+}
+
+/* gets the handshake and compares it with the expected value */
+static u16 get_handshake(struct ft1000_usb *ft1000dev, u16 expected_value)
+{
+ u16 handshake;
+ int loopcnt;
+ int status = 0;
+
+ loopcnt = 0;
+
+ while (loopcnt < 100) {
+ /* Need to clear downloader doorbell if Hartley ASIC */
+ status = ft1000_write_register(ft1000dev, FT1000_DB_DNLD_RX,
+ FT1000_REG_DOORBELL);
+ if (ft1000dev->fcodeldr) {
+ pr_debug("fcodeldr is %d\n", ft1000dev->fcodeldr);
+ ft1000dev->fcodeldr = 0;
+ status = check_usb_db(ft1000dev);
+ if (status != 0) {
+ pr_debug("check_usb_db failed\n");
+ break;
+ }
+ status = ft1000_write_register(ft1000dev,
+ FT1000_DB_DNLD_RX,
+ FT1000_REG_DOORBELL);
+ }
+
+ status = ft1000_read_dpram16(ft1000dev,
+ DWNLD_MAG1_HANDSHAKE_LOC, (u8 *)&handshake, 1);
+ handshake = ntohs(handshake);
+
+ if (status)
+ return HANDSHAKE_TIMEOUT_VALUE;
+
+ if ((handshake == expected_value) ||
+ (handshake == HANDSHAKE_RESET_VALUE_USB)) {
+ return handshake;
+ }
+ loopcnt++;
+ msleep(10);
+ }
+
+ return HANDSHAKE_TIMEOUT_VALUE;
+}
+
+/* write the handshake value to the handshake location */
+static void put_handshake(struct ft1000_usb *ft1000dev, u16 handshake_value)
+{
+ u32 tempx;
+ u16 tempword;
+ int status;
+
+ tempx = (u32)handshake_value;
+ tempx = ntohl(tempx);
+
+ tempword = (u16)(tempx & 0xffff);
+ status = ft1000_write_dpram16(ft1000dev, DWNLD_MAG1_HANDSHAKE_LOC,
+ tempword, 0);
+ tempword = (u16)(tempx >> 16);
+ status = ft1000_write_dpram16(ft1000dev, DWNLD_MAG1_HANDSHAKE_LOC,
+ tempword, 1);
+ status = ft1000_write_register(ft1000dev, FT1000_DB_DNLD_TX,
+ FT1000_REG_DOORBELL);
+}
+
+static u16 get_handshake_usb(struct ft1000_usb *ft1000dev, u16 expected_value)
+{
+ u16 handshake;
+ int loopcnt;
+ u16 temp;
+ int status = 0;
+
+ loopcnt = 0;
+ handshake = 0;
+
+ while (loopcnt < 100) {
+ if (ft1000dev->usbboot == 2) {
+ status = ft1000_read_dpram32(ft1000dev, 0,
+ (u8 *)&ft1000dev->tempbuf[0], 64);
+ for (temp = 0; temp < 16; temp++) {
+ pr_debug("tempbuf %d = 0x%x\n",
+ temp, ft1000dev->tempbuf[temp]);
+ }
+ status = ft1000_read_dpram16(ft1000dev,
+ DWNLD_MAG1_HANDSHAKE_LOC,
+ (u8 *)&handshake, 1);
+ pr_debug("handshake from read_dpram16 = 0x%x\n",
+ handshake);
+ if (ft1000dev->dspalive == ft1000dev->tempbuf[6]) {
+ handshake = 0;
+ } else {
+ handshake = ft1000dev->tempbuf[1];
+ ft1000dev->dspalive =
+ ft1000dev->tempbuf[6];
+ }
+ } else {
+ status = ft1000_read_dpram16(ft1000dev,
+ DWNLD_MAG1_HANDSHAKE_LOC,
+ (u8 *)&handshake, 1);
+ }
+
+ loopcnt++;
+ msleep(10);
+ handshake = ntohs(handshake);
+ if ((handshake == expected_value) ||
+ (handshake == HANDSHAKE_RESET_VALUE_USB))
+ return handshake;
+ }
+
+ return HANDSHAKE_TIMEOUT_VALUE;
+}
+
+static void put_handshake_usb(struct ft1000_usb *ft1000dev, u16 handshake_value)
+{
+ int i;
+
+ for (i = 0; i < 1000; i++)
+ ;
+}
+
+static u16 get_request_type(struct ft1000_usb *ft1000dev)
+{
+ u16 request_type;
+ int status;
+ u16 tempword;
+ u32 tempx;
+
+ if (ft1000dev->bootmode == 1) {
+ status = fix_ft1000_read_dpram32(ft1000dev,
+ DWNLD_MAG1_TYPE_LOC, (u8 *)&tempx);
+ tempx = ntohl(tempx);
+ } else {
+ tempx = 0;
+ status = ft1000_read_dpram16(ft1000dev,
+ DWNLD_MAG1_TYPE_LOC, (u8 *)&tempword, 1);
+ tempx |= (tempword << 16);
+ tempx = ntohl(tempx);
+ }
+ request_type = (u16)tempx;
+
+ return request_type;
+}
+
+static u16 get_request_type_usb(struct ft1000_usb *ft1000dev)
+{
+ u16 request_type;
+ int status;
+ u16 tempword;
+ u32 tempx;
+
+ if (ft1000dev->bootmode == 1) {
+ status = fix_ft1000_read_dpram32(ft1000dev,
+ DWNLD_MAG1_TYPE_LOC, (u8 *)&tempx);
+ tempx = ntohl(tempx);
+ } else {
+ if (ft1000dev->usbboot == 2) {
+ tempx = ft1000dev->tempbuf[2];
+ tempword = ft1000dev->tempbuf[3];
+ } else {
+ tempx = 0;
+ status = ft1000_read_dpram16(ft1000dev,
+ DWNLD_MAG1_TYPE_LOC,
+ (u8 *)&tempword, 1);
+ }
+ tempx |= (tempword << 16);
+ tempx = ntohl(tempx);
+ }
+ request_type = (u16)tempx;
+
+ return request_type;
+}
+
+static long get_request_value(struct ft1000_usb *ft1000dev)
+{
+ u32 value;
+ u16 tempword;
+ int status;
+
+ if (ft1000dev->bootmode == 1) {
+ status = fix_ft1000_read_dpram32(ft1000dev,
+ DWNLD_MAG1_SIZE_LOC, (u8 *)&value);
+ value = ntohl(value);
+ } else {
+ status = ft1000_read_dpram16(ft1000dev,
+ DWNLD_MAG1_SIZE_LOC, (u8 *)&tempword, 0);
+ value = tempword;
+ status = ft1000_read_dpram16(ft1000dev,
+ DWNLD_MAG1_SIZE_LOC, (u8 *)&tempword, 1);
+ value |= (tempword << 16);
+ value = ntohl(value);
+ }
+
+ return value;
+}
+
+
+/* writes a value to DWNLD_MAG1_SIZE_LOC */
+static void put_request_value(struct ft1000_usb *ft1000dev, long lvalue)
+{
+ u32 tempx;
+ int status;
+
+ tempx = ntohl(lvalue);
+ status = fix_ft1000_write_dpram32(ft1000dev, DWNLD_MAG1_SIZE_LOC,
+ (u8 *)&tempx);
+}
+
+
+
+/* returns the checksum of the pseudo header */
+static u16 hdr_checksum(struct pseudo_hdr *pHdr)
+{
+ u16 *usPtr = (u16 *)pHdr;
+ u16 chksum;
+
+
+ chksum = (((((usPtr[0] ^ usPtr[1]) ^ usPtr[2]) ^ usPtr[3]) ^
+ usPtr[4]) ^ usPtr[5]) ^ usPtr[6];
+
+ return chksum;
+}
+
+static int check_buffers(u16 *buff_w, u16 *buff_r, int len, int offset)
+{
+ int i;
+
+ for (i = 0; i < len; i++) {
+ if (buff_w[i] != buff_r[i + offset])
+ return -EREMOTEIO;
+ }
+
+ return 0;
+}
+
+static int write_dpram32_and_check(struct ft1000_usb *ft1000dev,
+ u16 tempbuffer[], u16 dpram)
+{
+ int status;
+ u16 resultbuffer[64];
+ int i;
+
+ for (i = 0; i < 10; i++) {
+ status = ft1000_write_dpram32(ft1000dev, dpram,
+ (u8 *)&tempbuffer[0], 64);
+ if (status == 0) {
+ /* Work around for ASIC bit stuffing problem. */
+ if ((tempbuffer[31] & 0xfe00) == 0xfe00) {
+ status = ft1000_write_dpram32(ft1000dev,
+ dpram+12, (u8 *)&tempbuffer[24],
+ 64);
+ }
+ /* Let's check the data written */
+ status = ft1000_read_dpram32(ft1000dev, dpram,
+ (u8 *)&resultbuffer[0], 64);
+ if ((tempbuffer[31] & 0xfe00) == 0xfe00) {
+ if (check_buffers(tempbuffer, resultbuffer, 28,
+ 0)) {
+ pr_debug("DPRAM write failed 1 during bootloading\n");
+ usleep_range(9000, 11000);
+ break;
+ }
+ status = ft1000_read_dpram32(ft1000dev,
+ dpram+12,
+ (u8 *)&resultbuffer[0], 64);
+
+ if (check_buffers(tempbuffer, resultbuffer, 16,
+ 24)) {
+ pr_debug("DPRAM write failed 2 during bootloading\n");
+ usleep_range(9000, 11000);
+ break;
+ }
+ } else {
+ if (check_buffers(tempbuffer, resultbuffer, 32,
+ 0)) {
+ pr_debug("DPRAM write failed 3 during bootloading\n");
+ usleep_range(9000, 11000);
+ break;
+ }
+ }
+ if (status == 0)
+ break;
+ }
+ }
+ return status;
+}
+
+/* writes a block of DSP image to DPRAM
+ * Parameters: struct ft1000_usb - device structure
+ * u16 **pUsFile - DSP image file pointer in u16
+ * u8 **pUcFile - DSP image file pointer in u8
+ * long word_length - length of the buffer to be written to DPRAM
+ */
+static int write_blk(struct ft1000_usb *ft1000dev, u16 **pUsFile, u8 **pUcFile,
+ long word_length)
+{
+ int status = 0;
+ u16 dpram;
+ int loopcnt, i;
+ u16 tempword;
+ u16 tempbuffer[64];
+
+ /*pr_debug("start word_length = %d\n",(int)word_length); */
+ dpram = (u16)DWNLD_MAG1_PS_HDR_LOC;
+ tempword = *(*pUsFile);
+ (*pUsFile)++;
+ status = ft1000_write_dpram16(ft1000dev, dpram, tempword, 0);
+ tempword = *(*pUsFile);
+ (*pUsFile)++;
+ status = ft1000_write_dpram16(ft1000dev, dpram++, tempword, 1);
+
+ *pUcFile = *pUcFile + 4;
+ word_length--;
+ tempword = (u16)word_length;
+ word_length = (word_length / 16) + 1;
+ for (; word_length > 0; word_length--) { /* In words */
+ loopcnt = 0;
+ for (i = 0; i < 32; i++) {
+ if (tempword != 0) {
+ tempbuffer[i++] = *(*pUsFile);
+ (*pUsFile)++;
+ tempbuffer[i] = *(*pUsFile);
+ (*pUsFile)++;
+ *pUcFile = *pUcFile + 4;
+ loopcnt++;
+ tempword--;
+ } else {
+ tempbuffer[i++] = 0;
+ tempbuffer[i] = 0;
+ }
+ }
+
+ /*pr_debug("loopcnt is %d\n", loopcnt); */
+ /*pr_debug("write_blk: bootmode = %d\n", bootmode); */
+ /*pr_debug("write_blk: dpram = %x\n", dpram); */
+ if (ft1000dev->bootmode == 0) {
+ if (dpram >= 0x3F4)
+ status = ft1000_write_dpram32(ft1000dev, dpram,
+ (u8 *)&tempbuffer[0], 8);
+ else
+ status = ft1000_write_dpram32(ft1000dev, dpram,
+ (u8 *)&tempbuffer[0], 64);
+ } else {
+ status = write_dpram32_and_check(ft1000dev, tempbuffer,
+ dpram);
+ if (status != 0) {
+ pr_debug("Write failed tempbuffer[31] = 0x%x\n",
+ tempbuffer[31]);
+ break;
+ }
+ }
+ dpram = dpram + loopcnt;
+ }
+ return status;
+}
+
+static void usb_dnld_complete(struct urb *urb)
+{
+ /* pr_debug("****** usb_dnld_complete\n"); */
+}
+
+/* writes a block of DSP image to DPRAM
+ * Parameters: struct ft1000_usb - device structure
+ * u16 **pUsFile - DSP image file pointer in u16
+ * u8 **pUcFile - DSP image file pointer in u8
+ * long word_length - length of the buffer to be written to DPRAM
+ */
+static int write_blk_fifo(struct ft1000_usb *ft1000dev, u16 **pUsFile,
+ u8 **pUcFile, long word_length)
+{
+ int byte_length;
+
+ byte_length = word_length * 4;
+
+ if (byte_length && ((byte_length % 64) == 0))
+ byte_length += 4;
+
+ if (byte_length < 64)
+ byte_length = 68;
+
+ usb_init_urb(ft1000dev->tx_urb);
+ memcpy(ft1000dev->tx_buf, *pUcFile, byte_length);
+ usb_fill_bulk_urb(ft1000dev->tx_urb,
+ ft1000dev->dev,
+ usb_sndbulkpipe(ft1000dev->dev,
+ ft1000dev->bulk_out_endpointAddr),
+ ft1000dev->tx_buf, byte_length, usb_dnld_complete,
+ ft1000dev);
+
+ usb_submit_urb(ft1000dev->tx_urb, GFP_ATOMIC);
+
+ *pUsFile = *pUsFile + (word_length << 1);
+ *pUcFile = *pUcFile + (word_length << 2);
+
+ return 0;
+}
+
+static int scram_start_dwnld(struct ft1000_usb *ft1000dev, u16 *hshake,
+ u32 *state)
+{
+ int status = 0;
+
+ if (ft1000dev->usbboot)
+ *hshake = get_handshake_usb(ft1000dev, HANDSHAKE_DSP_BL_READY);
+ else
+ *hshake = get_handshake(ft1000dev, HANDSHAKE_DSP_BL_READY);
+ if (*hshake == HANDSHAKE_DSP_BL_READY) {
+ pr_debug("handshake is HANDSHAKE_DSP_BL_READY, call put_handshake(HANDSHAKE_DRIVER_READY)\n");
+ put_handshake(ft1000dev, HANDSHAKE_DRIVER_READY);
+ } else if (*hshake == HANDSHAKE_TIMEOUT_VALUE) {
+ status = -ETIMEDOUT;
+ } else {
+ pr_debug("Download error: Handshake failed\n");
+ status = -ENETRESET;
+ }
+ *state = STATE_BOOT_DWNLD;
+ return status;
+}
+
+static int request_code_segment(struct ft1000_usb *ft1000dev, u16 **s_file,
+ u8 **c_file, const u8 *endpoint, bool boot_case)
+{
+ long word_length;
+ int status = 0;
+
+ word_length = get_request_value(ft1000dev);
+ /*pr_debug("word_length = 0x%x\n", (int)word_length); */
+ /*NdisMSleep (100); */
+ if (word_length > MAX_LENGTH) {
+ pr_debug("Download error: Max length exceeded\n");
+ return -1;
+ }
+ if ((word_length * 2 + (long)c_file) > (long)endpoint) {
+ /* Error, beyond boot code range.*/
+ pr_debug("Download error: Requested len=%d exceeds BOOT code boundary\n",
+ (int)word_length);
+ return -1;
+ }
+ if (word_length & 0x1)
+ word_length++;
+ word_length = word_length / 2;
+
+ if (boot_case) {
+ status = write_blk(ft1000dev, s_file, c_file, word_length);
+ /*pr_debug("write_blk returned %d\n", status); */
+ } else {
+ status = write_blk_fifo(ft1000dev, s_file, c_file, word_length);
+ if (ft1000dev->usbboot == 0)
+ ft1000dev->usbboot++;
+ if (ft1000dev->usbboot == 1)
+ status |= ft1000_write_dpram16(ft1000dev,
+ DWNLD_MAG1_PS_HDR_LOC, 0, 0);
+ }
+ return status;
+}
+
+/* Scramble downloader for Harley based ASIC via USB interface */
+int scram_dnldr(struct ft1000_usb *ft1000dev, void *pFileStart,
+ u32 FileLength)
+{
+ int status = 0;
+ u32 state;
+ u16 handshake;
+ struct pseudo_hdr *pseudo_header;
+ u16 pseudo_header_len;
+ long word_length;
+ u16 request;
+ u16 temp;
+
+ struct dsp_file_hdr *file_hdr;
+ struct dsp_image_info *dsp_img_info = NULL;
+ long requested_version;
+ bool correct_version;
+ struct drv_msg *mailbox_data;
+ u16 *data = NULL;
+ u16 *s_file = NULL;
+ u8 *c_file = NULL;
+ u8 *boot_end = NULL, *code_end = NULL;
+ int image;
+ long loader_code_address, loader_code_size = 0;
+ long run_address = 0, run_size = 0;
+
+ u32 templong;
+ u32 image_chksum = 0;
+
+ u16 dpram = 0;
+ u8 *pbuffer;
+ struct prov_record *pprov_record;
+ struct ft1000_info *pft1000info = netdev_priv(ft1000dev->net);
+
+ ft1000dev->fcodeldr = 0;
+ ft1000dev->usbboot = 0;
+ ft1000dev->dspalive = 0xffff;
+
+ /*
+ * Get version id of file, at first 4 bytes of file, for newer files.
+ */
+
+ state = STATE_START_DWNLD;
+
+ file_hdr = pFileStart;
+
+ ft1000_write_register(ft1000dev, 0x800, FT1000_REG_MAG_WATERMARK);
+
+ s_file = (u16 *) (pFileStart + file_hdr->loader_offset);
+ c_file = (u8 *) (pFileStart + file_hdr->loader_offset);
+
+ boot_end = (u8 *) (pFileStart + file_hdr->loader_code_end);
+
+ loader_code_address = file_hdr->loader_code_address;
+ loader_code_size = file_hdr->loader_code_size;
+ correct_version = false;
+
+ while ((status == 0) && (state != STATE_DONE_FILE)) {
+ switch (state) {
+ case STATE_START_DWNLD:
+ status = scram_start_dwnld(ft1000dev, &handshake,
+ &state);
+ break;
+
+ case STATE_BOOT_DWNLD:
+ pr_debug("STATE_BOOT_DWNLD\n");
+ ft1000dev->bootmode = 1;
+ handshake = get_handshake(ft1000dev, HANDSHAKE_REQUEST);
+ if (handshake == HANDSHAKE_REQUEST) {
+ /*
+ * Get type associated with the request.
+ */
+ request = get_request_type(ft1000dev);
+ switch (request) {
+ case REQUEST_RUN_ADDRESS:
+ pr_debug("REQUEST_RUN_ADDRESS\n");
+ put_request_value(ft1000dev,
+ loader_code_address);
+ break;
+ case REQUEST_CODE_LENGTH:
+ pr_debug("REQUEST_CODE_LENGTH\n");
+ put_request_value(ft1000dev,
+ loader_code_size);
+ break;
+ case REQUEST_DONE_BL:
+ pr_debug("REQUEST_DONE_BL\n");
+ /* Reposition ptrs to beginning of code section */
+ s_file = (u16 *) (boot_end);
+ c_file = (u8 *) (boot_end);
+ /* pr_debug("download:s_file = 0x%8x\n", (int)s_file); */
+ /* pr_debug("FT1000:download:c_file = 0x%8x\n", (int)c_file); */
+ state = STATE_CODE_DWNLD;
+ ft1000dev->fcodeldr = 1;
+ break;
+ case REQUEST_CODE_SEGMENT:
+ status = request_code_segment(ft1000dev,
+ &s_file, &c_file,
+ boot_end,
+ true);
+ break;
+ default:
+ pr_debug("Download error: Bad request type=%d in BOOT download state\n",
+ request);
+ status = -1;
+ break;
+ }
+ if (ft1000dev->usbboot)
+ put_handshake_usb(ft1000dev,
+ HANDSHAKE_RESPONSE);
+ else
+ put_handshake(ft1000dev,
+ HANDSHAKE_RESPONSE);
+ } else {
+ pr_debug("Download error: Handshake failed\n");
+ status = -1;
+ }
+
+ break;
+
+ case STATE_CODE_DWNLD:
+ /* pr_debug("STATE_CODE_DWNLD\n"); */
+ ft1000dev->bootmode = 0;
+ if (ft1000dev->usbboot)
+ handshake =
+ get_handshake_usb(ft1000dev,
+ HANDSHAKE_REQUEST);
+ else
+ handshake =
+ get_handshake(ft1000dev, HANDSHAKE_REQUEST);
+ if (handshake == HANDSHAKE_REQUEST) {
+ /*
+ * Get type associated with the request.
+ */
+ if (ft1000dev->usbboot)
+ request =
+ get_request_type_usb(ft1000dev);
+ else
+ request = get_request_type(ft1000dev);
+ switch (request) {
+ case REQUEST_FILE_CHECKSUM:
+ pr_debug("image_chksum = 0x%8x\n",
+ image_chksum);
+ put_request_value(ft1000dev,
+ image_chksum);
+ break;
+ case REQUEST_RUN_ADDRESS:
+ pr_debug("REQUEST_RUN_ADDRESS\n");
+ if (correct_version) {
+ pr_debug("run_address = 0x%8x\n",
+ (int)run_address);
+ put_request_value(ft1000dev,
+ run_address);
+ } else {
+ pr_debug("Download error: Got Run address request before image offset request\n");
+ status = -1;
+ break;
+ }
+ break;
+ case REQUEST_CODE_LENGTH:
+ pr_debug("REQUEST_CODE_LENGTH\n");
+ if (correct_version) {
+ pr_debug("run_size = 0x%8x\n",
+ (int)run_size);
+ put_request_value(ft1000dev,
+ run_size);
+ } else {
+ pr_debug("Download error: Got Size request before image offset request\n");
+ status = -1;
+ break;
+ }
+ break;
+ case REQUEST_DONE_CL:
+ ft1000dev->usbboot = 3;
+ /* Reposition ptrs to beginning of provisioning section */
+ s_file =
+ (u16 *) (pFileStart +
+ file_hdr->commands_offset);
+ c_file =
+ (u8 *) (pFileStart +
+ file_hdr->commands_offset);
+ state = STATE_DONE_DWNLD;
+ break;
+ case REQUEST_CODE_SEGMENT:
+ /* pr_debug("REQUEST_CODE_SEGMENT - CODELOADER\n"); */
+ if (!correct_version) {
+ pr_debug("Download error: Got Code Segment request before image offset request\n");
+ status = -1;
+ break;
+ }
+
+ status = request_code_segment(ft1000dev,
+ &s_file, &c_file,
+ code_end,
+ false);
+
+ break;
+
+ case REQUEST_MAILBOX_DATA:
+ pr_debug("REQUEST_MAILBOX_DATA\n");
+ /* Convert length from byte count to word count. Make sure we round up. */
+ word_length =
+ (long)(pft1000info->DSPInfoBlklen +
+ 1) / 2;
+ put_request_value(ft1000dev,
+ word_length);
+ mailbox_data =
+ (struct drv_msg *)&(pft1000info->
+ DSPInfoBlk[0]);
+ /*
+ * Position ASIC DPRAM auto-increment pointer.
+ */
+
+ data = (u16 *)&mailbox_data->data[0];
+ dpram = (u16)DWNLD_MAG1_PS_HDR_LOC;
+ if (word_length & 0x1)
+ word_length++;
+
+ word_length = word_length / 2;
+
+ for (; word_length > 0; word_length--) { /* In words */
+
+ templong = *data++;
+ templong |= (*data++ << 16);
+ status =
+ fix_ft1000_write_dpram32
+ (ft1000dev, dpram++,
+ (u8 *)&templong);
+
+ }
+ break;
+
+ case REQUEST_VERSION_INFO:
+ pr_debug("REQUEST_VERSION_INFO\n");
+ word_length =
+ file_hdr->version_data_size;
+ put_request_value(ft1000dev,
+ word_length);
+ /*
+ * Position ASIC DPRAM auto-increment pointer.
+ */
+
+ s_file =
+ (u16 *) (pFileStart +
+ file_hdr->
+ version_data_offset);
+
+ dpram = (u16)DWNLD_MAG1_PS_HDR_LOC;
+ if (word_length & 0x1)
+ word_length++;
+
+ word_length = word_length / 2;
+
+ for (; word_length > 0; word_length--) { /* In words */
+
+ templong = ntohs(*s_file++);
+ temp = ntohs(*s_file++);
+ templong |= (temp << 16);
+ status =
+ fix_ft1000_write_dpram32
+ (ft1000dev, dpram++,
+ (u8 *)&templong);
+
+ }
+ break;
+
+ case REQUEST_CODE_BY_VERSION:
+ pr_debug("REQUEST_CODE_BY_VERSION\n");
+ correct_version = false;
+ requested_version =
+ get_request_value(ft1000dev);
+
+ dsp_img_info =
+ (struct dsp_image_info *)(pFileStart
+ +
+ sizeof
+ (struct
+ dsp_file_hdr));
+
+ for (image = 0;
+ image < file_hdr->nDspImages;
+ image++) {
+
+ if (dsp_img_info->version ==
+ requested_version) {
+ correct_version = true;
+ pr_debug("correct_version is TRUE\n");
+ s_file =
+ (u16 *) (pFileStart
+ +
+ dsp_img_info->
+ begin_offset);
+ c_file =
+ (u8 *) (pFileStart +
+ dsp_img_info->
+ begin_offset);
+ code_end =
+ (u8 *) (pFileStart +
+ dsp_img_info->
+ end_offset);
+ run_address =
+ dsp_img_info->
+ run_address;
+ run_size =
+ dsp_img_info->
+ image_size;
+ image_chksum =
+ (u32)dsp_img_info->
+ checksum;
+ break;
+ }
+ dsp_img_info++;
+
+ } /* end of for */
+
+ if (!correct_version) {
+ /*
+ * Error, beyond boot code range.
+ */
+ pr_debug("Download error: Bad Version Request = 0x%x.\n",
+ (int)requested_version);
+ status = -1;
+ break;
+ }
+ break;
+
+ default:
+ pr_debug("Download error: Bad request type=%d in CODE download state.\n",
+ request);
+ status = -1;
+ break;
+ }
+ if (ft1000dev->usbboot)
+ put_handshake_usb(ft1000dev,
+ HANDSHAKE_RESPONSE);
+ else
+ put_handshake(ft1000dev,
+ HANDSHAKE_RESPONSE);
+ } else {
+ pr_debug("Download error: Handshake failed\n");
+ status = -1;
+ }
+
+ break;
+
+ case STATE_DONE_DWNLD:
+ pr_debug("Code loader is done...\n");
+ state = STATE_SECTION_PROV;
+ break;
+
+ case STATE_SECTION_PROV:
+ pr_debug("STATE_SECTION_PROV\n");
+ pseudo_header = (struct pseudo_hdr *)c_file;
+
+ if (pseudo_header->checksum ==
+ hdr_checksum(pseudo_header)) {
+ if (pseudo_header->portdest !=
+ 0x80 /* Dsp OAM */) {
+ state = STATE_DONE_PROV;
+ break;
+ }
+ pseudo_header_len = ntohs(pseudo_header->length); /* Byte length for PROV records */
+
+ /* Get buffer for provisioning data */
+ pbuffer =
+ kmalloc(pseudo_header_len +
+ sizeof(struct pseudo_hdr),
+ GFP_ATOMIC);
+ if (pbuffer) {
+ memcpy(pbuffer, c_file,
+ (u32) (pseudo_header_len +
+ sizeof(struct
+ pseudo_hdr)));
+ /* link provisioning data */
+ pprov_record =
+ kmalloc(sizeof(struct prov_record),
+ GFP_ATOMIC);
+ if (pprov_record) {
+ pprov_record->pprov_data =
+ pbuffer;
+ list_add_tail(&pprov_record->
+ list,
+ &pft1000info->
+ prov_list);
+ /* Move to next entry if available */
+ c_file =
+ (u8 *) ((unsigned long)
+ c_file +
+ (u32) ((pseudo_header_len + 1) & 0xFFFFFFFE) + sizeof(struct pseudo_hdr));
+ if ((unsigned long)(c_file) -
+ (unsigned long)(pFileStart)
+ >=
+ (unsigned long)FileLength) {
+ state = STATE_DONE_FILE;
+ }
+ } else {
+ kfree(pbuffer);
+ status = -1;
+ }
+ } else {
+ status = -1;
+ }
+ } else {
+ /* Checksum did not compute */
+ status = -1;
+ }
+ pr_debug("after STATE_SECTION_PROV, state = %d, status= %d\n",
+ state, status);
+ break;
+
+ case STATE_DONE_PROV:
+ pr_debug("STATE_DONE_PROV\n");
+ state = STATE_DONE_FILE;
+ break;
+
+ default:
+ status = -1;
+ break;
+ } /* End Switch */
+
+ if (status != 0)
+ break;
+
+/****
+ // Check if Card is present
+ status = Harley_Read_Register(&temp, FT1000_REG_SUP_IMASK);
+ if ( (status != NDIS_STATUS_SUCCESS) || (temp == 0x0000) ) {
+ break;
+ }
+
+ status = Harley_Read_Register(&temp, FT1000_REG_ASIC_ID);
+ if ( (status != NDIS_STATUS_SUCCESS) || (temp == 0xffff) ) {
+ break;
+ }
+****/
+
+ } /* End while */
+
+ pr_debug("Download exiting with status = 0x%8x\n", status);
+ ft1000_write_register(ft1000dev, FT1000_DB_DNLD_TX,
+ FT1000_REG_DOORBELL);
+
+ return status;
+}
diff --git a/drivers/staging/ft1000/ft1000-usb/ft1000_hw.c b/drivers/staging/ft1000/ft1000-usb/ft1000_hw.c
new file mode 100644
index 000000000..e6fb066e0
--- /dev/null
+++ b/drivers/staging/ft1000/ft1000-usb/ft1000_hw.c
@@ -0,0 +1,1587 @@
+/* CopyRight (C) 2007 Qualcomm Inc. All Rights Reserved.
+ *
+ *
+ * This file is part of Express Card USB Driver
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/usb.h>
+#include "ft1000_usb.h"
+#include <linux/types.h>
+
+#define HARLEY_READ_REGISTER 0x0
+#define HARLEY_WRITE_REGISTER 0x01
+#define HARLEY_READ_DPRAM_32 0x02
+#define HARLEY_READ_DPRAM_LOW 0x03
+#define HARLEY_READ_DPRAM_HIGH 0x04
+#define HARLEY_WRITE_DPRAM_32 0x05
+#define HARLEY_WRITE_DPRAM_LOW 0x06
+#define HARLEY_WRITE_DPRAM_HIGH 0x07
+
+#define HARLEY_READ_OPERATION 0xc1
+#define HARLEY_WRITE_OPERATION 0x41
+
+#if 0
+#define JDEBUG
+#endif
+
+static int ft1000_submit_rx_urb(struct ft1000_info *info);
+
+static u8 tempbuffer[1600];
+
+#define MAX_RCV_LOOP 100
+
+/* send a control message via USB interface synchronously
+ * Parameters: ft1000_usb - device structure
+ * pipe - usb control message pipe
+ * request - control request
+ * requesttype - control message request type
+ * value - value to be written or 0
+ * index - register index
+ * data - data buffer to hold the read/write values
+ * size - data size
+ * timeout - control message time out value
+ */
+static int ft1000_control(struct ft1000_usb *ft1000dev, unsigned int pipe,
+ u8 request, u8 requesttype, u16 value, u16 index,
+ void *data, u16 size, int timeout)
+{
+ int ret;
+
+ if ((ft1000dev == NULL) || (ft1000dev->dev == NULL)) {
+ pr_debug("ft1000dev or ft1000dev->dev == NULL, failure\n");
+ return -ENODEV;
+ }
+
+ ret = usb_control_msg(ft1000dev->dev, pipe, request, requesttype,
+ value, index, data, size, timeout);
+
+ if (ret > 0)
+ ret = 0;
+
+ return ret;
+}
+
+/* returns the value in a register */
+int ft1000_read_register(struct ft1000_usb *ft1000dev, u16 *Data,
+ u16 nRegIndx)
+{
+ int ret = 0;
+
+ ret = ft1000_control(ft1000dev,
+ usb_rcvctrlpipe(ft1000dev->dev, 0),
+ HARLEY_READ_REGISTER,
+ HARLEY_READ_OPERATION,
+ 0,
+ nRegIndx,
+ Data,
+ 2,
+ USB_CTRL_GET_TIMEOUT);
+
+ return ret;
+}
+
+/* writes the value in a register */
+int ft1000_write_register(struct ft1000_usb *ft1000dev, u16 value,
+ u16 nRegIndx)
+{
+ int ret = 0;
+
+ ret = ft1000_control(ft1000dev,
+ usb_sndctrlpipe(ft1000dev->dev, 0),
+ HARLEY_WRITE_REGISTER,
+ HARLEY_WRITE_OPERATION,
+ value,
+ nRegIndx,
+ NULL,
+ 0,
+ USB_CTRL_SET_TIMEOUT);
+
+ return ret;
+}
+
+/* read a number of bytes from DPRAM */
+int ft1000_read_dpram32(struct ft1000_usb *ft1000dev, u16 indx, u8 *buffer,
+ u16 cnt)
+{
+ int ret = 0;
+
+ ret = ft1000_control(ft1000dev,
+ usb_rcvctrlpipe(ft1000dev->dev, 0),
+ HARLEY_READ_DPRAM_32,
+ HARLEY_READ_OPERATION,
+ 0,
+ indx,
+ buffer,
+ cnt,
+ USB_CTRL_GET_TIMEOUT);
+
+ return ret;
+}
+
+/* writes into DPRAM a number of bytes */
+int ft1000_write_dpram32(struct ft1000_usb *ft1000dev, u16 indx, u8 *buffer,
+ u16 cnt)
+{
+ int ret = 0;
+
+ if (cnt % 4)
+ cnt += cnt - (cnt % 4);
+
+ ret = ft1000_control(ft1000dev,
+ usb_sndctrlpipe(ft1000dev->dev, 0),
+ HARLEY_WRITE_DPRAM_32,
+ HARLEY_WRITE_OPERATION,
+ 0,
+ indx,
+ buffer,
+ cnt,
+ USB_CTRL_SET_TIMEOUT);
+
+ return ret;
+}
+
+/* read 16 bits from DPRAM */
+int ft1000_read_dpram16(struct ft1000_usb *ft1000dev, u16 indx, u8 *buffer,
+ u8 highlow)
+{
+ int ret = 0;
+ u8 request;
+
+ if (highlow == 0)
+ request = HARLEY_READ_DPRAM_LOW;
+ else
+ request = HARLEY_READ_DPRAM_HIGH;
+
+ ret = ft1000_control(ft1000dev,
+ usb_rcvctrlpipe(ft1000dev->dev, 0),
+ request,
+ HARLEY_READ_OPERATION,
+ 0,
+ indx,
+ buffer,
+ 2,
+ USB_CTRL_GET_TIMEOUT);
+
+ return ret;
+}
+
+/* write into DPRAM a number of bytes */
+int ft1000_write_dpram16(struct ft1000_usb *ft1000dev, u16 indx, u16 value,
+ u8 highlow)
+{
+ int ret = 0;
+ u8 request;
+
+ if (highlow == 0)
+ request = HARLEY_WRITE_DPRAM_LOW;
+ else
+ request = HARLEY_WRITE_DPRAM_HIGH;
+
+ ret = ft1000_control(ft1000dev,
+ usb_sndctrlpipe(ft1000dev->dev, 0),
+ request,
+ HARLEY_WRITE_OPERATION,
+ value,
+ indx,
+ NULL,
+ 0,
+ USB_CTRL_SET_TIMEOUT);
+
+ return ret;
+}
+
+/* read DPRAM 4 words at a time */
+int fix_ft1000_read_dpram32(struct ft1000_usb *ft1000dev, u16 indx,
+ u8 *buffer)
+{
+ u8 buf[16];
+ u16 pos;
+ int ret = 0;
+
+ pos = (indx / 4) * 4;
+ ret = ft1000_read_dpram32(ft1000dev, pos, buf, 16);
+
+ if (ret == 0) {
+ pos = (indx % 4) * 4;
+ *buffer++ = buf[pos++];
+ *buffer++ = buf[pos++];
+ *buffer++ = buf[pos++];
+ *buffer++ = buf[pos++];
+ } else {
+ pr_debug("DPRAM32 Read failed\n");
+ *buffer++ = 0;
+ *buffer++ = 0;
+ *buffer++ = 0;
+ *buffer++ = 0;
+ }
+
+ return ret;
+}
+
+
+/* Description: This function write to DPRAM 4 words at a time */
+int fix_ft1000_write_dpram32(struct ft1000_usb *ft1000dev, u16 indx, u8 *buffer)
+{
+ u16 pos1;
+ u16 pos2;
+ u16 i;
+ u8 buf[32];
+ u8 resultbuffer[32];
+ u8 *pdata;
+ int ret = 0;
+
+ pos1 = (indx / 4) * 4;
+ pdata = buffer;
+ ret = ft1000_read_dpram32(ft1000dev, pos1, buf, 16);
+
+ if (ret == 0) {
+ pos2 = (indx % 4)*4;
+ buf[pos2++] = *buffer++;
+ buf[pos2++] = *buffer++;
+ buf[pos2++] = *buffer++;
+ buf[pos2++] = *buffer++;
+ ret = ft1000_write_dpram32(ft1000dev, pos1, buf, 16);
+ } else {
+ pr_debug("DPRAM32 Read failed\n");
+ return ret;
+ }
+
+ ret = ft1000_read_dpram32(ft1000dev, pos1, (u8 *)&resultbuffer[0], 16);
+
+ if (ret == 0) {
+ buffer = pdata;
+ for (i = 0; i < 16; i++) {
+ if (buf[i] != resultbuffer[i])
+ ret = -1;
+ }
+ }
+
+ if (ret == -1) {
+ ret = ft1000_write_dpram32(ft1000dev, pos1,
+ (u8 *)&tempbuffer[0], 16);
+ ret = ft1000_read_dpram32(ft1000dev, pos1,
+ (u8 *)&resultbuffer[0], 16);
+ if (ret == 0) {
+ buffer = pdata;
+ for (i = 0; i < 16; i++) {
+ if (tempbuffer[i] != resultbuffer[i]) {
+ ret = -1;
+ pr_debug("Failed to write\n");
+ }
+ }
+ }
+ }
+
+ return ret;
+}
+
+/* reset or activate the DSP */
+static void card_reset_dsp(struct ft1000_usb *ft1000dev, bool value)
+{
+ int status = 0;
+ u16 tempword;
+
+ status = ft1000_write_register(ft1000dev, HOST_INTF_BE,
+ FT1000_REG_SUP_CTRL);
+ status = ft1000_read_register(ft1000dev, &tempword,
+ FT1000_REG_SUP_CTRL);
+
+ if (value) {
+ pr_debug("Reset DSP\n");
+ status = ft1000_read_register(ft1000dev, &tempword,
+ FT1000_REG_RESET);
+ tempword |= DSP_RESET_BIT;
+ status = ft1000_write_register(ft1000dev, tempword,
+ FT1000_REG_RESET);
+ } else {
+ pr_debug("Activate DSP\n");
+ status = ft1000_read_register(ft1000dev, &tempword,
+ FT1000_REG_RESET);
+ tempword |= DSP_ENCRYPTED;
+ tempword &= ~DSP_UNENCRYPTED;
+ status = ft1000_write_register(ft1000dev, tempword,
+ FT1000_REG_RESET);
+ status = ft1000_read_register(ft1000dev, &tempword,
+ FT1000_REG_RESET);
+ tempword &= ~EFUSE_MEM_DISABLE;
+ tempword &= ~DSP_RESET_BIT;
+ status = ft1000_write_register(ft1000dev, tempword,
+ FT1000_REG_RESET);
+ status = ft1000_read_register(ft1000dev, &tempword,
+ FT1000_REG_RESET);
+ }
+}
+
+/* send a command to ASIC
+ * Parameters: ft1000_usb - device structure
+ * ptempbuffer - command buffer
+ * size - command buffer size
+ */
+int card_send_command(struct ft1000_usb *ft1000dev, void *ptempbuffer,
+ int size)
+{
+ int ret;
+ unsigned short temp;
+ unsigned char *commandbuf;
+
+ pr_debug("enter card_send_command... size=%d\n", size);
+
+ ret = ft1000_read_register(ft1000dev, &temp, FT1000_REG_DOORBELL);
+ if (ret)
+ return ret;
+
+ commandbuf = kmalloc(size + 2, GFP_KERNEL);
+ if (!commandbuf)
+ return -ENOMEM;
+ memcpy((void *)commandbuf + 2, ptempbuffer, size);
+
+ if (temp & 0x0100)
+ usleep_range(900, 1100);
+
+ /* check for odd word */
+ size = size + 2;
+
+ /* Must force to be 32 bit aligned */
+ if (size % 4)
+ size += 4 - (size % 4);
+
+ ret = ft1000_write_dpram32(ft1000dev, 0, commandbuf, size);
+ if (ret)
+ return ret;
+ usleep_range(900, 1100);
+ ret = ft1000_write_register(ft1000dev, FT1000_DB_DPRAM_TX,
+ FT1000_REG_DOORBELL);
+ if (ret)
+ return ret;
+ usleep_range(900, 1100);
+
+ ret = ft1000_read_register(ft1000dev, &temp, FT1000_REG_DOORBELL);
+
+#if 0
+ if ((temp & 0x0100) == 0)
+ pr_debug("Message sent\n");
+#endif
+ return ret;
+}
+
+/* load or reload the DSP */
+int dsp_reload(struct ft1000_usb *ft1000dev)
+{
+ int status;
+ u16 tempword;
+ u32 templong;
+
+ struct ft1000_info *pft1000info;
+
+ pft1000info = netdev_priv(ft1000dev->net);
+
+ pft1000info->CardReady = 0;
+
+ /* Program Interrupt Mask register */
+ status = ft1000_write_register(ft1000dev, 0xffff, FT1000_REG_SUP_IMASK);
+
+ status = ft1000_read_register(ft1000dev, &tempword, FT1000_REG_RESET);
+ tempword |= ASIC_RESET_BIT;
+ status = ft1000_write_register(ft1000dev, tempword, FT1000_REG_RESET);
+ msleep(1000);
+ status = ft1000_read_register(ft1000dev, &tempword, FT1000_REG_RESET);
+ pr_debug("Reset Register = 0x%x\n", tempword);
+
+ /* Toggle DSP reset */
+ card_reset_dsp(ft1000dev, 1);
+ msleep(1000);
+ card_reset_dsp(ft1000dev, 0);
+ msleep(1000);
+
+ status =
+ ft1000_write_register(ft1000dev, HOST_INTF_BE, FT1000_REG_SUP_CTRL);
+
+ /* Let's check for FEFE */
+ status =
+ ft1000_read_dpram32(ft1000dev, FT1000_MAG_DPRAM_FEFE_INDX,
+ (u8 *)&templong, 4);
+ pr_debug("templong (fefe) = 0x%8x\n", templong);
+
+ /* call codeloader */
+ status = scram_dnldr(ft1000dev, pFileStart, FileLength);
+
+ if (status != 0)
+ return -EIO;
+
+ msleep(1000);
+
+ return 0;
+}
+
+/* call the Card Service function to reset the ASIC. */
+static void ft1000_reset_asic(struct net_device *dev)
+{
+ struct ft1000_info *info = netdev_priv(dev);
+ struct ft1000_usb *ft1000dev = info->priv;
+ u16 tempword;
+
+ /* Let's use the register provided by the Magnemite ASIC to reset the
+ * ASIC and DSP.
+ */
+ ft1000_write_register(ft1000dev, DSP_RESET_BIT | ASIC_RESET_BIT,
+ FT1000_REG_RESET);
+
+ mdelay(1);
+
+ /* set watermark to -1 in order to not generate an interrupt */
+ ft1000_write_register(ft1000dev, 0xffff, FT1000_REG_MAG_WATERMARK);
+
+ /* clear interrupts */
+ ft1000_read_register(ft1000dev, &tempword, FT1000_REG_SUP_ISR);
+ pr_debug("interrupt status register = 0x%x\n", tempword);
+ ft1000_write_register(ft1000dev, tempword, FT1000_REG_SUP_ISR);
+ ft1000_read_register(ft1000dev, &tempword, FT1000_REG_SUP_ISR);
+ pr_debug("interrupt status register = 0x%x\n", tempword);
+}
+
+static int ft1000_reset_card(struct net_device *dev)
+{
+ struct ft1000_info *info = netdev_priv(dev);
+ struct ft1000_usb *ft1000dev = info->priv;
+ u16 tempword;
+ struct prov_record *ptr;
+ struct prov_record *tmp;
+
+ ft1000dev->fCondResetPend = true;
+ info->CardReady = 0;
+ ft1000dev->fProvComplete = false;
+
+ /* Make sure we free any memory reserve for provisioning */
+ list_for_each_entry_safe(ptr, tmp, &info->prov_list, list) {
+ pr_debug("deleting provisioning record\n");
+ list_del(&ptr->list);
+ kfree(ptr->pprov_data);
+ kfree(ptr);
+ }
+
+ pr_debug("reset asic\n");
+ ft1000_reset_asic(dev);
+
+ pr_debug("call dsp_reload\n");
+ dsp_reload(ft1000dev);
+
+ pr_debug("dsp reload successful\n");
+
+ mdelay(10);
+
+ /* Initialize DSP heartbeat area */
+ ft1000_write_dpram16(ft1000dev, FT1000_MAG_HI_HO, ho_mag,
+ FT1000_MAG_HI_HO_INDX);
+ ft1000_read_dpram16(ft1000dev, FT1000_MAG_HI_HO, (u8 *)&tempword,
+ FT1000_MAG_HI_HO_INDX);
+ pr_debug("hi_ho value = 0x%x\n", tempword);
+
+ info->CardReady = 1;
+
+ ft1000dev->fCondResetPend = false;
+
+ return TRUE;
+}
+
+/* callback function when a urb is transmitted */
+static void ft1000_usb_transmit_complete(struct urb *urb)
+{
+
+ struct ft1000_usb *ft1000dev = urb->context;
+
+ if (urb->status)
+ pr_err("%s: TX status %d\n", ft1000dev->net->name, urb->status);
+
+ netif_wake_queue(ft1000dev->net);
+}
+
+/* take an ethernet packet and convert it to a Flarion
+ * packet prior to sending it to the ASIC Downlink FIFO.
+ */
+static int ft1000_copy_down_pkt(struct net_device *netdev, u8 *packet, u16 len)
+{
+ struct ft1000_info *pInfo = netdev_priv(netdev);
+ struct ft1000_usb *pFt1000Dev = pInfo->priv;
+
+ int count, ret;
+ u8 *t;
+ struct pseudo_hdr hdr;
+
+ if (!pInfo->CardReady) {
+ pr_debug("Card Not Ready\n");
+ return -ENODEV;
+ }
+
+ count = sizeof(struct pseudo_hdr) + len;
+ if (count > MAX_BUF_SIZE) {
+ pr_debug("Message Size Overflow! size = %d\n", count);
+ return -EINVAL;
+ }
+
+ if (count % 4)
+ count = count + (4 - (count % 4));
+
+ memset(&hdr, 0, sizeof(struct pseudo_hdr));
+
+ hdr.length = ntohs(count);
+ hdr.source = 0x10;
+ hdr.destination = 0x20;
+ hdr.portdest = 0x20;
+ hdr.portsrc = 0x10;
+ hdr.sh_str_id = 0x91;
+ hdr.control = 0x00;
+
+ hdr.checksum = hdr.length ^ hdr.source ^ hdr.destination ^
+ hdr.portdest ^ hdr.portsrc ^ hdr.sh_str_id ^ hdr.control;
+
+ memcpy(&pFt1000Dev->tx_buf[0], &hdr, sizeof(hdr));
+ memcpy(&pFt1000Dev->tx_buf[sizeof(struct pseudo_hdr)], packet, len);
+
+ netif_stop_queue(netdev);
+
+ usb_fill_bulk_urb(pFt1000Dev->tx_urb,
+ pFt1000Dev->dev,
+ usb_sndbulkpipe(pFt1000Dev->dev,
+ pFt1000Dev->bulk_out_endpointAddr),
+ pFt1000Dev->tx_buf, count,
+ ft1000_usb_transmit_complete, pFt1000Dev);
+
+ t = (u8 *)pFt1000Dev->tx_urb->transfer_buffer;
+
+ ret = usb_submit_urb(pFt1000Dev->tx_urb, GFP_ATOMIC);
+
+ if (ret) {
+ pr_debug("failed tx_urb %d\n", ret);
+ return ret;
+ }
+ pInfo->stats.tx_packets++;
+ pInfo->stats.tx_bytes += (len + 14);
+
+ return 0;
+}
+
+/* transmit an ethernet packet
+ * Parameters: skb - socket buffer to be sent
+ * dev - network device
+ */
+static int ft1000_start_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+ struct ft1000_info *pInfo = netdev_priv(dev);
+ struct ft1000_usb *pFt1000Dev = pInfo->priv;
+ u8 *pdata;
+ int maxlen, pipe;
+
+ if (skb == NULL) {
+ pr_debug("skb == NULL!!!\n");
+ return NETDEV_TX_OK;
+ }
+
+ if (pFt1000Dev->status & FT1000_STATUS_CLOSING) {
+ pr_debug("network driver is closed, return\n");
+ goto err;
+ }
+
+ pipe =
+ usb_sndbulkpipe(pFt1000Dev->dev, pFt1000Dev->bulk_out_endpointAddr);
+ maxlen = usb_maxpacket(pFt1000Dev->dev, pipe, usb_pipeout(pipe));
+
+ pdata = (u8 *)skb->data;
+
+ if (pInfo->mediastate == 0) {
+ /* Drop packet is mediastate is down */
+ pr_debug("mediastate is down\n");
+ goto err;
+ }
+
+ if ((skb->len < ENET_HEADER_SIZE) || (skb->len > ENET_MAX_SIZE)) {
+ /* Drop packet which has invalid size */
+ pr_debug("invalid ethernet length\n");
+ goto err;
+ }
+
+ ft1000_copy_down_pkt(dev, pdata + ENET_HEADER_SIZE - 2,
+ skb->len - ENET_HEADER_SIZE + 2);
+
+err:
+ dev_kfree_skb(skb);
+
+ return NETDEV_TX_OK;
+}
+
+/* open the network driver */
+static int ft1000_open(struct net_device *dev)
+{
+ struct ft1000_info *pInfo = netdev_priv(dev);
+ struct ft1000_usb *pFt1000Dev = pInfo->priv;
+ struct timeval tv;
+
+ pr_debug("ft1000_open is called for card %d\n", pFt1000Dev->CardNumber);
+
+ pInfo->stats.rx_bytes = 0;
+ pInfo->stats.tx_bytes = 0;
+ pInfo->stats.rx_packets = 0;
+ pInfo->stats.tx_packets = 0;
+ do_gettimeofday(&tv);
+ pInfo->ConTm = tv.tv_sec;
+ pInfo->ProgConStat = 0;
+
+ netif_start_queue(dev);
+
+ netif_carrier_on(dev);
+
+ return ft1000_submit_rx_urb(pInfo);
+}
+
+static struct net_device_stats *ft1000_netdev_stats(struct net_device *dev)
+{
+ struct ft1000_info *info = netdev_priv(dev);
+
+ return &(info->stats);
+}
+
+static const struct net_device_ops ftnet_ops = {
+ .ndo_open = &ft1000_open,
+ .ndo_stop = &ft1000_close,
+ .ndo_start_xmit = &ft1000_start_xmit,
+ .ndo_get_stats = &ft1000_netdev_stats,
+};
+
+/* initialize the network device */
+static int ft1000_reset(void *dev)
+{
+ ft1000_reset_card(dev);
+ return 0;
+}
+
+int init_ft1000_netdev(struct ft1000_usb *ft1000dev)
+{
+ struct net_device *netdev;
+ struct ft1000_info *pInfo = NULL;
+ struct dpram_blk *pdpram_blk;
+ int i, ret_val;
+ struct list_head *cur, *tmp;
+ char card_nr[2];
+ u8 gCardIndex = 0;
+
+ netdev = alloc_etherdev(sizeof(struct ft1000_info));
+ if (!netdev) {
+ pr_debug("can not allocate network device\n");
+ return -ENOMEM;
+ }
+
+ pInfo = netdev_priv(netdev);
+
+ memset(pInfo, 0, sizeof(struct ft1000_info));
+
+ dev_alloc_name(netdev, netdev->name);
+
+ pr_debug("network device name is %s\n", netdev->name);
+
+ if (strncmp(netdev->name, "eth", 3) == 0) {
+ card_nr[0] = netdev->name[3];
+ card_nr[1] = '\0';
+ ret_val = kstrtou8(card_nr, 10, &gCardIndex);
+ if (ret_val) {
+ netdev_err(ft1000dev->net, "Can't parse netdev\n");
+ goto err_net;
+ }
+
+ ft1000dev->CardNumber = gCardIndex;
+ pr_debug("card number = %d\n", ft1000dev->CardNumber);
+ } else {
+ netdev_err(ft1000dev->net, "ft1000: Invalid device name\n");
+ ret_val = -ENXIO;
+ goto err_net;
+ }
+
+ memset(&pInfo->stats, 0, sizeof(struct net_device_stats));
+
+ spin_lock_init(&pInfo->dpram_lock);
+ pInfo->priv = ft1000dev;
+ pInfo->DrvErrNum = 0;
+ pInfo->registered = 1;
+ pInfo->ft1000_reset = ft1000_reset;
+ pInfo->mediastate = 0;
+ pInfo->fifo_cnt = 0;
+ ft1000dev->DeviceCreated = FALSE;
+ pInfo->CardReady = 0;
+ pInfo->DSP_TIME[0] = 0;
+ pInfo->DSP_TIME[1] = 0;
+ pInfo->DSP_TIME[2] = 0;
+ pInfo->DSP_TIME[3] = 0;
+ ft1000dev->fAppMsgPend = false;
+ ft1000dev->fCondResetPend = false;
+ ft1000dev->usbboot = 0;
+ ft1000dev->dspalive = 0;
+ memset(&ft1000dev->tempbuf[0], 0, sizeof(ft1000dev->tempbuf));
+
+ INIT_LIST_HEAD(&pInfo->prov_list);
+
+ INIT_LIST_HEAD(&ft1000dev->nodes.list);
+
+ netdev->netdev_ops = &ftnet_ops;
+
+ ft1000dev->net = netdev;
+
+ pr_debug("Initialize free_buff_lock and freercvpool\n");
+ spin_lock_init(&free_buff_lock);
+
+ /* initialize a list of buffers to be use for queuing
+ * up receive command data
+ */
+ INIT_LIST_HEAD(&freercvpool);
+
+ /* create list of free buffers */
+ for (i = 0; i < NUM_OF_FREE_BUFFERS; i++) {
+ /* Get memory for DPRAM_DATA link list */
+ pdpram_blk = kmalloc(sizeof(struct dpram_blk), GFP_KERNEL);
+ if (pdpram_blk == NULL) {
+ ret_val = -ENOMEM;
+ goto err_free;
+ }
+ /* Get a block of memory to store command data */
+ pdpram_blk->pbuffer = kmalloc(MAX_CMD_SQSIZE, GFP_KERNEL);
+ if (pdpram_blk->pbuffer == NULL) {
+ ret_val = -ENOMEM;
+ kfree(pdpram_blk);
+ goto err_free;
+ }
+ /* link provisioning data */
+ list_add_tail(&pdpram_blk->list, &freercvpool);
+ }
+ numofmsgbuf = NUM_OF_FREE_BUFFERS;
+
+ return 0;
+
+err_free:
+ list_for_each_safe(cur, tmp, &freercvpool) {
+ pdpram_blk = list_entry(cur, struct dpram_blk, list);
+ list_del(&pdpram_blk->list);
+ kfree(pdpram_blk->pbuffer);
+ kfree(pdpram_blk);
+ }
+err_net:
+ free_netdev(netdev);
+ return ret_val;
+}
+
+/* register the network driver */
+int reg_ft1000_netdev(struct ft1000_usb *ft1000dev,
+ struct usb_interface *intf)
+{
+ struct net_device *netdev;
+ struct ft1000_info *pInfo;
+ int rc;
+
+ netdev = ft1000dev->net;
+ pInfo = netdev_priv(ft1000dev->net);
+
+ ft1000_read_register(ft1000dev, &pInfo->AsicID, FT1000_REG_ASIC_ID);
+
+ usb_set_intfdata(intf, pInfo);
+ SET_NETDEV_DEV(netdev, &intf->dev);
+
+ rc = register_netdev(netdev);
+ if (rc) {
+ pr_debug("could not register network device\n");
+ free_netdev(netdev);
+ return rc;
+ }
+
+ ft1000_create_dev(ft1000dev);
+
+ pInfo->CardReady = 1;
+
+ return 0;
+}
+
+/* take a packet from the FIFO up link and
+ * convert it into an ethernet packet and deliver it to the IP stack
+ */
+static int ft1000_copy_up_pkt(struct urb *urb)
+{
+ struct ft1000_info *info = urb->context;
+ struct ft1000_usb *ft1000dev = info->priv;
+ struct net_device *net = ft1000dev->net;
+
+ u16 tempword;
+ u16 len;
+ u16 lena;
+ struct sk_buff *skb;
+ u16 i;
+ u8 *pbuffer = NULL;
+ u8 *ptemp = NULL;
+ u16 *chksum;
+
+ if (ft1000dev->status & FT1000_STATUS_CLOSING) {
+ pr_debug("network driver is closed, return\n");
+ return 0;
+ }
+ /* Read length */
+ len = urb->transfer_buffer_length;
+ lena = urb->actual_length;
+
+ chksum = (u16 *)ft1000dev->rx_buf;
+
+ tempword = *chksum++;
+ for (i = 1; i < 7; i++)
+ tempword ^= *chksum++;
+
+ if (tempword != *chksum) {
+ info->stats.rx_errors++;
+ ft1000_submit_rx_urb(info);
+ return -1;
+ }
+
+ skb = dev_alloc_skb(len + 12 + 2);
+
+ if (skb == NULL) {
+ pr_debug("No Network buffers available\n");
+ info->stats.rx_errors++;
+ ft1000_submit_rx_urb(info);
+ return -1;
+ }
+
+ pbuffer = (u8 *)skb_put(skb, len + 12);
+
+ /* subtract the number of bytes read already */
+ ptemp = pbuffer;
+
+ /* fake MAC address */
+ *pbuffer++ = net->dev_addr[0];
+ *pbuffer++ = net->dev_addr[1];
+ *pbuffer++ = net->dev_addr[2];
+ *pbuffer++ = net->dev_addr[3];
+ *pbuffer++ = net->dev_addr[4];
+ *pbuffer++ = net->dev_addr[5];
+ *pbuffer++ = 0x00;
+ *pbuffer++ = 0x07;
+ *pbuffer++ = 0x35;
+ *pbuffer++ = 0xff;
+ *pbuffer++ = 0xff;
+ *pbuffer++ = 0xfe;
+
+ memcpy(pbuffer, ft1000dev->rx_buf + sizeof(struct pseudo_hdr),
+ len - sizeof(struct pseudo_hdr));
+
+ skb->dev = net;
+
+ skb->protocol = eth_type_trans(skb, net);
+ skb->ip_summed = CHECKSUM_UNNECESSARY;
+ netif_rx(skb);
+
+ info->stats.rx_packets++;
+ /* Add on 12 bytes for MAC address which was removed */
+ info->stats.rx_bytes += (lena + 12);
+
+ ft1000_submit_rx_urb(info);
+
+ return 0;
+}
+
+
+/* the receiving function of the network driver */
+static int ft1000_submit_rx_urb(struct ft1000_info *info)
+{
+ int result;
+ struct ft1000_usb *pFt1000Dev = info->priv;
+
+ if (pFt1000Dev->status & FT1000_STATUS_CLOSING) {
+ pr_debug("network driver is closed, return\n");
+ return -ENODEV;
+ }
+
+ usb_fill_bulk_urb(pFt1000Dev->rx_urb,
+ pFt1000Dev->dev,
+ usb_rcvbulkpipe(pFt1000Dev->dev,
+ pFt1000Dev->bulk_in_endpointAddr),
+ pFt1000Dev->rx_buf, MAX_BUF_SIZE,
+ (usb_complete_t)ft1000_copy_up_pkt, info);
+
+ result = usb_submit_urb(pFt1000Dev->rx_urb, GFP_ATOMIC);
+
+ if (result) {
+ pr_err("submitting rx_urb %d failed\n", result);
+ return result;
+ }
+
+ return 0;
+}
+
+/* close the network driver */
+int ft1000_close(struct net_device *net)
+{
+ struct ft1000_info *pInfo = netdev_priv(net);
+ struct ft1000_usb *ft1000dev = pInfo->priv;
+
+ ft1000dev->status |= FT1000_STATUS_CLOSING;
+
+ pr_debug("pInfo=%p, ft1000dev=%p\n", pInfo, ft1000dev);
+ netif_carrier_off(net);
+ netif_stop_queue(net);
+ ft1000dev->status &= ~FT1000_STATUS_CLOSING;
+
+ pInfo->ProgConStat = 0xff;
+
+ return 0;
+}
+
+/* check if the device is presently available on the system. */
+static int ft1000_chkcard(struct ft1000_usb *dev)
+{
+ u16 tempword;
+ int status;
+
+ if (dev->fCondResetPend) {
+ pr_debug("Card is being reset, return FALSE\n");
+ return TRUE;
+ }
+ /* Mask register is used to check for device presence since it is never
+ * set to zero.
+ */
+ status = ft1000_read_register(dev, &tempword, FT1000_REG_SUP_IMASK);
+ if (tempword == 0) {
+ pr_debug("IMASK = 0 Card not detected\n");
+ return FALSE;
+ }
+ /* The system will return the value of 0xffff for the version register
+ * if the device is not present.
+ */
+ status = ft1000_read_register(dev, &tempword, FT1000_REG_ASIC_ID);
+ if (tempword != 0x1b01) {
+ dev->status |= FT1000_STATUS_CLOSING;
+ pr_debug("Version = 0xffff Card not detected\n");
+ return FALSE;
+ }
+ return TRUE;
+}
+
+/* read a message from the dpram area.
+ * Input:
+ * dev - network device structure
+ * pbuffer - caller supply address to buffer
+ */
+static bool ft1000_receive_cmd(struct ft1000_usb *dev, u16 *pbuffer,
+ int maxsz)
+{
+ u16 size;
+ int ret;
+ u16 *ppseudohdr;
+ int i;
+ u16 tempword;
+
+ ret =
+ ft1000_read_dpram16(dev, FT1000_MAG_PH_LEN, (u8 *)&size,
+ FT1000_MAG_PH_LEN_INDX);
+ size = ntohs(size) + PSEUDOSZ;
+ if (size > maxsz) {
+ pr_debug("Invalid command length = %d\n", size);
+ return FALSE;
+ }
+ ppseudohdr = (u16 *)pbuffer;
+ ft1000_write_register(dev, FT1000_DPRAM_MAG_RX_BASE,
+ FT1000_REG_DPRAM_ADDR);
+ ret =
+ ft1000_read_register(dev, pbuffer, FT1000_REG_MAG_DPDATAH);
+ pbuffer++;
+ ft1000_write_register(dev, FT1000_DPRAM_MAG_RX_BASE + 1,
+ FT1000_REG_DPRAM_ADDR);
+ for (i = 0; i <= (size >> 2); i++) {
+ ret =
+ ft1000_read_register(dev, pbuffer,
+ FT1000_REG_MAG_DPDATAL);
+ pbuffer++;
+ ret =
+ ft1000_read_register(dev, pbuffer,
+ FT1000_REG_MAG_DPDATAH);
+ pbuffer++;
+ }
+ /* copy odd aligned word */
+ ret =
+ ft1000_read_register(dev, pbuffer, FT1000_REG_MAG_DPDATAL);
+
+ pbuffer++;
+ ret =
+ ft1000_read_register(dev, pbuffer, FT1000_REG_MAG_DPDATAH);
+
+ pbuffer++;
+ if (size & 0x0001) {
+ /* copy odd byte from fifo */
+ ret =
+ ft1000_read_register(dev, &tempword,
+ FT1000_REG_DPRAM_DATA);
+ *pbuffer = ntohs(tempword);
+ }
+ /* Check if pseudo header checksum is good
+ * Calculate pseudo header checksum
+ */
+ tempword = *ppseudohdr++;
+ for (i = 1; i < 7; i++)
+ tempword ^= *ppseudohdr++;
+
+ if (tempword != *ppseudohdr)
+ return FALSE;
+
+ return TRUE;
+}
+
+static int ft1000_dsp_prov(void *arg)
+{
+ struct ft1000_usb *dev = (struct ft1000_usb *)arg;
+ struct ft1000_info *info = netdev_priv(dev->net);
+ u16 tempword;
+ u16 len;
+ u16 i = 0;
+ struct prov_record *ptr;
+ struct pseudo_hdr *ppseudo_hdr;
+ u16 *pmsg;
+ int status;
+ u16 TempShortBuf[256];
+
+ while (list_empty(&info->prov_list) == 0) {
+ pr_debug("DSP Provisioning List Entry\n");
+
+ /* Check if doorbell is available */
+ pr_debug("check if doorbell is cleared\n");
+ status =
+ ft1000_read_register(dev, &tempword, FT1000_REG_DOORBELL);
+ if (status) {
+ pr_debug("ft1000_read_register error\n");
+ break;
+ }
+
+ while (tempword & FT1000_DB_DPRAM_TX) {
+ mdelay(10);
+ i++;
+ if (i == 10) {
+ pr_debug("message drop\n");
+ return -1;
+ }
+ ft1000_read_register(dev, &tempword,
+ FT1000_REG_DOORBELL);
+ }
+
+ if (!(tempword & FT1000_DB_DPRAM_TX)) {
+ pr_debug("*** Provision Data Sent to DSP\n");
+
+ /* Send provisioning data */
+ ptr =
+ list_entry(info->prov_list.next, struct prov_record,
+ list);
+ len = *(u16 *)ptr->pprov_data;
+ len = htons(len);
+ len += PSEUDOSZ;
+
+ pmsg = (u16 *)ptr->pprov_data;
+ ppseudo_hdr = (struct pseudo_hdr *)pmsg;
+ /* Insert slow queue sequence number */
+ ppseudo_hdr->seq_num = info->squeseqnum++;
+ ppseudo_hdr->portsrc = 0;
+ /* Calculate new checksum */
+ ppseudo_hdr->checksum = *pmsg++;
+ for (i = 1; i < 7; i++)
+ ppseudo_hdr->checksum ^= *pmsg++;
+
+ TempShortBuf[0] = 0;
+ TempShortBuf[1] = htons(len);
+ memcpy(&TempShortBuf[2], ppseudo_hdr, len);
+
+ status =
+ ft1000_write_dpram32(dev, 0,
+ (u8 *)&TempShortBuf[0],
+ (unsigned short)(len + 2));
+ status =
+ ft1000_write_register(dev, FT1000_DB_DPRAM_TX,
+ FT1000_REG_DOORBELL);
+
+ list_del(&ptr->list);
+ kfree(ptr->pprov_data);
+ kfree(ptr);
+ }
+ usleep_range(9000, 11000);
+ }
+
+ pr_debug("DSP Provisioning List Entry finished\n");
+
+ msleep(100);
+
+ dev->fProvComplete = true;
+ info->CardReady = 1;
+
+ return 0;
+}
+
+static int ft1000_proc_drvmsg(struct ft1000_usb *dev, u16 size)
+{
+ struct ft1000_info *info = netdev_priv(dev->net);
+ u16 msgtype;
+ u16 tempword;
+ struct media_msg *pmediamsg;
+ struct dsp_init_msg *pdspinitmsg;
+ struct drv_msg *pdrvmsg;
+ u16 i;
+ struct pseudo_hdr *ppseudo_hdr;
+ u16 *pmsg;
+ int status;
+ union {
+ u8 byte[2];
+ u16 wrd;
+ } convert;
+
+ char *cmdbuffer = kmalloc(1600, GFP_KERNEL);
+
+ if (!cmdbuffer)
+ return -ENOMEM;
+
+ status = ft1000_read_dpram32(dev, 0x200, cmdbuffer, size);
+
+#ifdef JDEBUG
+ print_hex_dump_debug("cmdbuffer: ", HEX_DUMP_OFFSET, 16, 1,
+ cmdbuffer, size, true);
+#endif
+ pdrvmsg = (struct drv_msg *)&cmdbuffer[2];
+ msgtype = ntohs(pdrvmsg->type);
+ pr_debug("Command message type = 0x%x\n", msgtype);
+ switch (msgtype) {
+ case MEDIA_STATE:{
+ pr_debug("Command message type = MEDIA_STATE\n");
+ pmediamsg = (struct media_msg *)&cmdbuffer[0];
+ if (info->ProgConStat != 0xFF) {
+ if (pmediamsg->state) {
+ pr_debug("Media is up\n");
+ if (info->mediastate == 0) {
+ if (dev->NetDevRegDone)
+ netif_wake_queue(dev->net);
+ info->mediastate = 1;
+ }
+ } else {
+ pr_debug("Media is down\n");
+ if (info->mediastate == 1) {
+ info->mediastate = 0;
+ if (dev->NetDevRegDone)
+ info->ConTm = 0;
+ }
+ }
+ } else {
+ pr_debug("Media is down\n");
+ if (info->mediastate == 1) {
+ info->mediastate = 0;
+ info->ConTm = 0;
+ }
+ }
+ break;
+ }
+ case DSP_INIT_MSG:{
+ pr_debug("Command message type = DSP_INIT_MSG\n");
+ pdspinitmsg = (struct dsp_init_msg *)&cmdbuffer[2];
+ memcpy(info->DspVer, pdspinitmsg->DspVer, DSPVERSZ);
+ pr_debug("DSPVER = 0x%2x 0x%2x 0x%2x 0x%2x\n",
+ info->DspVer[0], info->DspVer[1], info->DspVer[2],
+ info->DspVer[3]);
+ memcpy(info->HwSerNum, pdspinitmsg->HwSerNum,
+ HWSERNUMSZ);
+ memcpy(info->Sku, pdspinitmsg->Sku, SKUSZ);
+ memcpy(info->eui64, pdspinitmsg->eui64, EUISZ);
+ pr_debug("EUI64=%2x.%2x.%2x.%2x.%2x.%2x.%2x.%2x\n",
+ info->eui64[0], info->eui64[1], info->eui64[2],
+ info->eui64[3], info->eui64[4], info->eui64[5],
+ info->eui64[6], info->eui64[7]);
+ dev->net->dev_addr[0] = info->eui64[0];
+ dev->net->dev_addr[1] = info->eui64[1];
+ dev->net->dev_addr[2] = info->eui64[2];
+ dev->net->dev_addr[3] = info->eui64[5];
+ dev->net->dev_addr[4] = info->eui64[6];
+ dev->net->dev_addr[5] = info->eui64[7];
+
+ if (ntohs(pdspinitmsg->length) ==
+ (sizeof(struct dsp_init_msg) - 20)) {
+ memcpy(info->ProductMode, pdspinitmsg->ProductMode,
+ MODESZ);
+ memcpy(info->RfCalVer, pdspinitmsg->RfCalVer, CALVERSZ);
+ memcpy(info->RfCalDate, pdspinitmsg->RfCalDate,
+ CALDATESZ);
+ pr_debug("RFCalVer = 0x%2x 0x%2x\n",
+ info->RfCalVer[0], info->RfCalVer[1]);
+ }
+ break;
+ }
+ case DSP_PROVISION:{
+ pr_debug("Command message type = DSP_PROVISION\n");
+
+ /* kick off dspprov routine to start provisioning
+ * Send provisioning data to DSP
+ */
+ if (list_empty(&info->prov_list) == 0) {
+ dev->fProvComplete = false;
+ status = ft1000_dsp_prov(dev);
+ if (status != 0)
+ goto out;
+ } else {
+ dev->fProvComplete = true;
+ status = ft1000_write_register(dev, FT1000_DB_HB,
+ FT1000_REG_DOORBELL);
+ pr_debug("No more DSP provisioning data in dsp image\n");
+ }
+ pr_debug("DSP PROVISION is done\n");
+ break;
+ }
+ case DSP_STORE_INFO:{
+ pr_debug("Command message type = DSP_STORE_INFO");
+ tempword = ntohs(pdrvmsg->length);
+ info->DSPInfoBlklen = tempword;
+ if (tempword < (MAX_DSP_SESS_REC - 4)) {
+ pmsg = (u16 *)&pdrvmsg->data[0];
+ for (i = 0; i < ((tempword + 1) / 2); i++) {
+ pr_debug("dsp info data = 0x%x\n", *pmsg);
+ info->DSPInfoBlk[i + 10] = *pmsg++;
+ }
+ } else {
+ info->DSPInfoBlklen = 0;
+ }
+ break;
+ }
+ case DSP_GET_INFO:{
+ pr_debug("Got DSP_GET_INFO\n");
+ /* copy dsp info block to dsp */
+ dev->DrvMsgPend = 1;
+ /* allow any outstanding ioctl to finish */
+ mdelay(10);
+ status = ft1000_read_register(dev, &tempword,
+ FT1000_REG_DOORBELL);
+ if (tempword & FT1000_DB_DPRAM_TX) {
+ mdelay(10);
+ status = ft1000_read_register(dev, &tempword,
+ FT1000_REG_DOORBELL);
+ if (tempword & FT1000_DB_DPRAM_TX) {
+ mdelay(10);
+ status = ft1000_read_register(dev, &tempword,
+ FT1000_REG_DOORBELL);
+ if (tempword & FT1000_DB_DPRAM_TX)
+ break;
+ }
+ }
+ /* Put message into Slow Queue Form Pseudo header */
+ pmsg = (u16 *)info->DSPInfoBlk;
+ *pmsg++ = 0;
+ *pmsg++ = htons(info->DSPInfoBlklen + 20 + info->DSPInfoBlklen);
+ ppseudo_hdr =
+ (struct pseudo_hdr *)(u16 *)&info->DSPInfoBlk[2];
+ ppseudo_hdr->length = htons(info->DSPInfoBlklen + 4
+ + info->DSPInfoBlklen);
+ ppseudo_hdr->source = 0x10;
+ ppseudo_hdr->destination = 0x20;
+ ppseudo_hdr->portdest = 0;
+ ppseudo_hdr->portsrc = 0;
+ ppseudo_hdr->sh_str_id = 0;
+ ppseudo_hdr->control = 0;
+ ppseudo_hdr->rsvd1 = 0;
+ ppseudo_hdr->rsvd2 = 0;
+ ppseudo_hdr->qos_class = 0;
+ /* Insert slow queue sequence number */
+ ppseudo_hdr->seq_num = info->squeseqnum++;
+ /* Insert application id */
+ ppseudo_hdr->portsrc = 0;
+ /* Calculate new checksum */
+ ppseudo_hdr->checksum = *pmsg++;
+ for (i = 1; i < 7; i++)
+ ppseudo_hdr->checksum ^= *pmsg++;
+
+ info->DSPInfoBlk[10] = 0x7200;
+ info->DSPInfoBlk[11] = htons(info->DSPInfoBlklen);
+ status = ft1000_write_dpram32(dev, 0,
+ (u8 *)&info->DSPInfoBlk[0],
+ (unsigned short)(info->DSPInfoBlklen + 22));
+ status = ft1000_write_register(dev, FT1000_DB_DPRAM_TX,
+ FT1000_REG_DOORBELL);
+ dev->DrvMsgPend = 0;
+ break;
+ }
+ case GET_DRV_ERR_RPT_MSG:{
+ pr_debug("Got GET_DRV_ERR_RPT_MSG\n");
+ /* copy driver error message to dsp */
+ dev->DrvMsgPend = 1;
+ /* allow any outstanding ioctl to finish */
+ mdelay(10);
+ status = ft1000_read_register(dev, &tempword,
+ FT1000_REG_DOORBELL);
+ if (tempword & FT1000_DB_DPRAM_TX) {
+ mdelay(10);
+ status = ft1000_read_register(dev, &tempword,
+ FT1000_REG_DOORBELL);
+ if (tempword & FT1000_DB_DPRAM_TX)
+ mdelay(10);
+ }
+ if ((tempword & FT1000_DB_DPRAM_TX) == 0) {
+ /* Put message into Slow Queue Form Pseudo header */
+ pmsg = (u16 *)&tempbuffer[0];
+ ppseudo_hdr = (struct pseudo_hdr *)pmsg;
+ ppseudo_hdr->length = htons(0x0012);
+ ppseudo_hdr->source = 0x10;
+ ppseudo_hdr->destination = 0x20;
+ ppseudo_hdr->portdest = 0;
+ ppseudo_hdr->portsrc = 0;
+ ppseudo_hdr->sh_str_id = 0;
+ ppseudo_hdr->control = 0;
+ ppseudo_hdr->rsvd1 = 0;
+ ppseudo_hdr->rsvd2 = 0;
+ ppseudo_hdr->qos_class = 0;
+ /* Insert slow queue sequence number */
+ ppseudo_hdr->seq_num = info->squeseqnum++;
+ /* Insert application id */
+ ppseudo_hdr->portsrc = 0;
+ /* Calculate new checksum */
+ ppseudo_hdr->checksum = *pmsg++;
+ for (i = 1; i < 7; i++)
+ ppseudo_hdr->checksum ^= *pmsg++;
+
+ pmsg = (u16 *)&tempbuffer[16];
+ *pmsg++ = htons(RSP_DRV_ERR_RPT_MSG);
+ *pmsg++ = htons(0x000e);
+ *pmsg++ = htons(info->DSP_TIME[0]);
+ *pmsg++ = htons(info->DSP_TIME[1]);
+ *pmsg++ = htons(info->DSP_TIME[2]);
+ *pmsg++ = htons(info->DSP_TIME[3]);
+ convert.byte[0] = info->DspVer[0];
+ convert.byte[1] = info->DspVer[1];
+ *pmsg++ = convert.wrd;
+ convert.byte[0] = info->DspVer[2];
+ convert.byte[1] = info->DspVer[3];
+ *pmsg++ = convert.wrd;
+ *pmsg++ = htons(info->DrvErrNum);
+
+ status = card_send_command(dev, (unsigned char *)&tempbuffer[0],
+ (u16)(0x0012 + PSEUDOSZ));
+ if (status)
+ goto out;
+ info->DrvErrNum = 0;
+ }
+ dev->DrvMsgPend = 0;
+ break;
+ }
+ default:
+ break;
+ }
+
+ status = 0;
+out:
+ kfree(cmdbuffer);
+ return status;
+}
+
+/* Check which application has registered for dsp broadcast messages */
+static int dsp_broadcast_msg_id(struct ft1000_usb *dev)
+{
+ struct dpram_blk *pdpram_blk;
+ unsigned long flags;
+ int i;
+
+ for (i = 0; i < MAX_NUM_APP; i++) {
+ if ((dev->app_info[i].DspBCMsgFlag)
+ && (dev->app_info[i].fileobject)
+ && (dev->app_info[i].NumOfMsg
+ < MAX_MSG_LIMIT)) {
+ pdpram_blk = ft1000_get_buffer(&freercvpool);
+ if (pdpram_blk == NULL) {
+ pr_debug("Out of memory in free receive command pool\n");
+ dev->app_info[i].nRxMsgMiss++;
+ return -1;
+ }
+ if (ft1000_receive_cmd(dev, pdpram_blk->pbuffer,
+ MAX_CMD_SQSIZE)) {
+ /* Put message into the
+ * appropriate application block
+ */
+ dev->app_info[i].nRxMsg++;
+ spin_lock_irqsave(&free_buff_lock, flags);
+ list_add_tail(&pdpram_blk->list,
+ &dev->app_info[i] .app_sqlist);
+ dev->app_info[i].NumOfMsg++;
+ spin_unlock_irqrestore(&free_buff_lock, flags);
+ wake_up_interruptible(&dev->app_info[i]
+ .wait_dpram_msg);
+ } else {
+ dev->app_info[i].nRxMsgMiss++;
+ ft1000_free_buffer(pdpram_blk, &freercvpool);
+ pr_debug("ft1000_get_buffer NULL\n");
+ return -1;
+ }
+ }
+ }
+ return 0;
+}
+
+static int handle_misc_portid(struct ft1000_usb *dev)
+{
+ struct dpram_blk *pdpram_blk;
+ int i;
+
+ pdpram_blk = ft1000_get_buffer(&freercvpool);
+ if (pdpram_blk == NULL) {
+ pr_debug("Out of memory in free receive command pool\n");
+ return -1;
+ }
+ if (!ft1000_receive_cmd(dev, pdpram_blk->pbuffer, MAX_CMD_SQSIZE))
+ goto exit_failure;
+
+ /* Search for correct application block */
+ for (i = 0; i < MAX_NUM_APP; i++) {
+ if (dev->app_info[i].app_id == ((struct pseudo_hdr *)
+ pdpram_blk->pbuffer)->portdest)
+ break;
+ }
+ if (i == MAX_NUM_APP) {
+ pr_debug("No application matching id = %d\n",
+ ((struct pseudo_hdr *)pdpram_blk->pbuffer)->portdest);
+ goto exit_failure;
+ } else if (dev->app_info[i].NumOfMsg > MAX_MSG_LIMIT) {
+ goto exit_failure;
+ } else {
+ dev->app_info[i].nRxMsg++;
+ /* Put message into the appropriate application block */
+ list_add_tail(&pdpram_blk->list, &dev->app_info[i].app_sqlist);
+ dev->app_info[i].NumOfMsg++;
+ }
+ return 0;
+
+exit_failure:
+ ft1000_free_buffer(pdpram_blk, &freercvpool);
+ return -1;
+}
+
+int ft1000_poll(void *dev_id)
+{
+ struct ft1000_usb *dev = (struct ft1000_usb *)dev_id;
+ struct ft1000_info *info = netdev_priv(dev->net);
+ u16 tempword;
+ int status;
+ u16 size;
+ int i;
+ u16 data;
+ u16 modulo;
+ u16 portid;
+
+ if (ft1000_chkcard(dev) == FALSE) {
+ pr_debug("failed\n");
+ return -1;
+ }
+ status = ft1000_read_register(dev, &tempword, FT1000_REG_DOORBELL);
+ if (!status) {
+ if (tempword & FT1000_DB_DPRAM_RX) {
+ status = ft1000_read_dpram16(dev,
+ 0x200, (u8 *)&data, 0);
+ size = ntohs(data) + 16 + 2;
+ if (size % 4) {
+ modulo = 4 - (size % 4);
+ size = size + modulo;
+ }
+ status = ft1000_read_dpram16(dev, 0x201,
+ (u8 *)&portid, 1);
+ portid &= 0xff;
+ if (size < MAX_CMD_SQSIZE) {
+ switch (portid) {
+ case DRIVERID:
+ pr_debug("FT1000_REG_DOORBELL message type: FT1000_DB_DPRAM_RX : portid DRIVERID\n");
+ status = ft1000_proc_drvmsg(dev, size);
+ if (status != 0)
+ return status;
+ break;
+ case DSPBCMSGID:
+ status = dsp_broadcast_msg_id(dev);
+ break;
+ default:
+ status = handle_misc_portid(dev);
+ break;
+ }
+ } else
+ pr_debug("Invalid total length for SlowQ = %d\n",
+ size);
+ status = ft1000_write_register(dev,
+ FT1000_DB_DPRAM_RX,
+ FT1000_REG_DOORBELL);
+ } else if (tempword & FT1000_DSP_ASIC_RESET) {
+ /* Let's reset the ASIC from the Host side as well */
+ status = ft1000_write_register(dev, ASIC_RESET_BIT,
+ FT1000_REG_RESET);
+ status = ft1000_read_register(dev, &tempword,
+ FT1000_REG_RESET);
+ i = 0;
+ while (tempword & ASIC_RESET_BIT) {
+ status = ft1000_read_register(dev, &tempword,
+ FT1000_REG_RESET);
+ usleep_range(9000, 11000);
+ i++;
+ if (i == 100)
+ break;
+ }
+ if (i == 100) {
+ pr_debug("Unable to reset ASIC\n");
+ return 0;
+ }
+ usleep_range(9000, 11000);
+ /* Program WMARK register */
+ status = ft1000_write_register(dev, 0x600,
+ FT1000_REG_MAG_WATERMARK);
+ /* clear ASIC reset doorbell */
+ status = ft1000_write_register(dev,
+ FT1000_DSP_ASIC_RESET,
+ FT1000_REG_DOORBELL);
+ usleep_range(9000, 11000);
+ } else if (tempword & FT1000_ASIC_RESET_REQ) {
+ pr_debug("FT1000_REG_DOORBELL message type: FT1000_ASIC_RESET_REQ\n");
+ /* clear ASIC reset request from DSP */
+ status = ft1000_write_register(dev,
+ FT1000_ASIC_RESET_REQ,
+ FT1000_REG_DOORBELL);
+ status = ft1000_write_register(dev, HOST_INTF_BE,
+ FT1000_REG_SUP_CTRL);
+ /* copy dsp session record from Adapter block */
+ status = ft1000_write_dpram32(dev, 0,
+ (u8 *)&info->DSPSess.Rec[0], 1024);
+ status = ft1000_write_register(dev, 0x600,
+ FT1000_REG_MAG_WATERMARK);
+ /* ring doorbell to tell DSP that
+ * ASIC is out of reset
+ * */
+ status = ft1000_write_register(dev,
+ FT1000_ASIC_RESET_DSP,
+ FT1000_REG_DOORBELL);
+ } else if (tempword & FT1000_DB_COND_RESET) {
+ pr_debug("FT1000_REG_DOORBELL message type: FT1000_DB_COND_RESET\n");
+ if (!dev->fAppMsgPend) {
+ /* Reset ASIC and DSP */
+ status = ft1000_read_dpram16(dev,
+ FT1000_MAG_DSP_TIMER0,
+ (u8 *)&info->DSP_TIME[0],
+ FT1000_MAG_DSP_TIMER0_INDX);
+ status = ft1000_read_dpram16(dev,
+ FT1000_MAG_DSP_TIMER1,
+ (u8 *)&info->DSP_TIME[1],
+ FT1000_MAG_DSP_TIMER1_INDX);
+ status = ft1000_read_dpram16(dev,
+ FT1000_MAG_DSP_TIMER2,
+ (u8 *)&info->DSP_TIME[2],
+ FT1000_MAG_DSP_TIMER2_INDX);
+ status = ft1000_read_dpram16(dev,
+ FT1000_MAG_DSP_TIMER3,
+ (u8 *)&info->DSP_TIME[3],
+ FT1000_MAG_DSP_TIMER3_INDX);
+ info->CardReady = 0;
+ info->DrvErrNum = DSP_CONDRESET_INFO;
+ pr_debug("DSP conditional reset requested\n");
+ info->ft1000_reset(dev->net);
+ } else {
+ dev->fProvComplete = false;
+ dev->fCondResetPend = true;
+ }
+ ft1000_write_register(dev, FT1000_DB_COND_RESET,
+ FT1000_REG_DOORBELL);
+ }
+ }
+ return 0;
+}
diff --git a/drivers/staging/ft1000/ft1000-usb/ft1000_ioctl.h b/drivers/staging/ft1000/ft1000-usb/ft1000_ioctl.h
new file mode 100644
index 000000000..e9472bebd
--- /dev/null
+++ b/drivers/staging/ft1000/ft1000-usb/ft1000_ioctl.h
@@ -0,0 +1,123 @@
+/*
+ *---------------------------------------------------------------------------
+ * FT1000 driver for Flarion Flash OFDM NIC Device
+ *
+ * Copyright (C) 2002 Flarion Technologies, All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option) any
+ * later version. This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * 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.
+ *---------------------------------------------------------------------------
+ *
+ * File: ft1000_ioctl.h
+ *
+ * Description: Common structures and defines relating to IOCTL
+ *
+ * History:
+ * 11/5/02 Whc Created.
+ *
+ *---------------------------------------------------------------------------
+ */
+#ifndef _FT1000IOCTLH_
+#define _FT1000IOCTLH_
+
+struct IOCTL_GET_VER {
+ unsigned long drv_ver;
+} __packed;
+
+/* Data structure for Dsp statistics */
+struct IOCTL_GET_DSP_STAT {
+ unsigned char DspVer[DSPVERSZ]; /* DSP version number */
+ unsigned char HwSerNum[HWSERNUMSZ]; /* Hardware Serial Number */
+ unsigned char Sku[SKUSZ]; /* SKU */
+ unsigned char eui64[EUISZ]; /* EUI64 */
+ unsigned short ConStat; /* Connection Status */
+ /* Bits 0-3 = Connection Status Field */
+ /* 0000=Idle (Disconnect) */
+ /* 0001=Searching */
+ /* 0010=Active (Connected) */
+ /* 0011=Waiting for L2 down */
+ /* 0100=Sleep */
+ unsigned short LedStat; /* Led Status */
+ /* Bits 0-3 = Signal Strength Field */
+ /* 0000 = -105dBm to -92dBm */
+ /* 0001 = -92dBm to -85dBm */
+ /* 0011 = -85dBm to -75dBm */
+ /* 0111 = -75dBm to -50dBm */
+ /* 1111 = -50dBm to 0dBm */
+ /* Bits 4-7 = Reserved */
+ /* Bits 8-11 = SNR Field */
+ /* 0000 = <2dB */
+ /* 0001 = 2dB to 8dB */
+ /* 0011 = 8dB to 15dB */
+ /* 0111 = 15dB to 22dB */
+ /* 1111 = >22dB */
+ /* Bits 12-15 = Reserved */
+ unsigned long nTxPkts; /* Number of packets transmitted
+ * from host to dsp
+ */
+ unsigned long nRxPkts; /* Number of packets received from
+ * dsp to host
+ */
+ unsigned long nTxBytes; /* Number of bytes transmitted
+ * from host to dsp
+ */
+ unsigned long nRxBytes; /* Number of bytes received from
+ * dsp to host
+ */
+ unsigned long ConTm; /* Current session connection time
+ * in seconds
+ */
+ unsigned char CalVer[CALVERSZ]; /* Proprietary Calibration
+ * Version
+ */
+ unsigned char CalDate[CALDATESZ]; /* Proprietary Calibration Date */
+} __packed;
+
+/* Data structure for Dual Ported RAM messaging between Host and Dsp */
+struct IOCTL_DPRAM_BLK {
+ unsigned short total_len;
+ struct pseudo_hdr pseudohdr;
+ unsigned char buffer[1780];
+} __packed;
+
+struct IOCTL_DPRAM_COMMAND {
+ unsigned short extra;
+ struct IOCTL_DPRAM_BLK dpram_blk;
+} __packed;
+
+/*
+ * Custom IOCTL command codes
+ */
+#define FT1000_MAGIC_CODE 'F'
+
+#define IOCTL_REGISTER_CMD 0
+#define IOCTL_SET_DPRAM_CMD 3
+#define IOCTL_GET_DPRAM_CMD 4
+#define IOCTL_GET_DSP_STAT_CMD 6
+#define IOCTL_GET_VER_CMD 7
+#define IOCTL_CONNECT 10
+#define IOCTL_DISCONNECT 11
+
+#define IOCTL_FT1000_GET_DSP_STAT _IOR(FT1000_MAGIC_CODE, \
+ IOCTL_GET_DSP_STAT_CMD, \
+ struct IOCTL_GET_DSP_STAT)
+#define IOCTL_FT1000_GET_VER _IOR(FT1000_MAGIC_CODE, IOCTL_GET_VER_CMD, \
+ struct IOCTL_GET_VER)
+#define IOCTL_FT1000_CONNECT _IO(FT1000_MAGIC_CODE, IOCTL_CONNECT)
+#define IOCTL_FT1000_DISCONNECT _IO(FT1000_MAGIC_CODE, IOCTL_DISCONNECT)
+#define IOCTL_FT1000_SET_DPRAM _IOW(FT1000_MAGIC_CODE, IOCTL_SET_DPRAM_CMD, \
+ struct IOCTL_DPRAM_BLK)
+#define IOCTL_FT1000_GET_DPRAM _IOR(FT1000_MAGIC_CODE, IOCTL_GET_DPRAM_CMD, \
+ struct IOCTL_DPRAM_BLK)
+#define IOCTL_FT1000_REGISTER _IOW(FT1000_MAGIC_CODE, IOCTL_REGISTER_CMD, \
+ unsigned short *)
+
+#endif /* _FT1000IOCTLH_ */
diff --git a/drivers/staging/ft1000/ft1000-usb/ft1000_usb.c b/drivers/staging/ft1000/ft1000-usb/ft1000_usb.c
new file mode 100644
index 000000000..c4874e85d
--- /dev/null
+++ b/drivers/staging/ft1000/ft1000-usb/ft1000_usb.c
@@ -0,0 +1,254 @@
+/*=====================================================
+ * CopyRight (C) 2007 Qualcomm Inc. All Rights Reserved.
+ *
+ *
+ * This file is part of Express Card USB Driver
+ *
+ * $Id:
+ *====================================================
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/usb.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/firmware.h>
+#include "ft1000_usb.h"
+
+#include <linux/kthread.h>
+
+MODULE_DESCRIPTION("FT1000 EXPRESS CARD DRIVER");
+MODULE_LICENSE("Dual MPL/GPL");
+MODULE_SUPPORTED_DEVICE("QFT FT1000 Express Cards");
+
+void *pFileStart;
+size_t FileLength;
+
+#define VENDOR_ID 0x1291 /* Qualcomm vendor id */
+#define PRODUCT_ID 0x11 /* fake product id */
+
+/* table of devices that work with this driver */
+static struct usb_device_id id_table[] = {
+ {USB_DEVICE(VENDOR_ID, PRODUCT_ID)},
+ {},
+};
+
+MODULE_DEVICE_TABLE(usb, id_table);
+
+static bool gPollingfailed;
+static int ft1000_poll_thread(void *arg)
+{
+ int ret;
+
+ while (!kthread_should_stop()) {
+ usleep_range(10000, 11000);
+ if (!gPollingfailed) {
+ ret = ft1000_poll(arg);
+ if (ret != 0) {
+ pr_debug("polling failed\n");
+ gPollingfailed = true;
+ }
+ }
+ }
+ return 0;
+}
+
+static int ft1000_probe(struct usb_interface *interface,
+ const struct usb_device_id *id)
+{
+ struct usb_host_interface *iface_desc;
+ struct usb_endpoint_descriptor *endpoint;
+ struct usb_device *dev;
+ unsigned numaltsetting;
+ int i, ret = 0, size;
+
+ struct ft1000_usb *ft1000dev;
+ struct ft1000_info *pft1000info = NULL;
+ const struct firmware *dsp_fw;
+
+ ft1000dev = kzalloc(sizeof(struct ft1000_usb), GFP_KERNEL);
+ if (!ft1000dev)
+ return -ENOMEM;
+
+ dev = interface_to_usbdev(interface);
+ pr_debug("usb device descriptor info - number of configuration is %d\n",
+ dev->descriptor.bNumConfigurations);
+
+ ft1000dev->dev = dev;
+ ft1000dev->status = 0;
+ ft1000dev->net = NULL;
+ ft1000dev->tx_urb = usb_alloc_urb(0, GFP_KERNEL);
+ ft1000dev->rx_urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!ft1000dev->tx_urb || !ft1000dev->rx_urb) {
+ ret = -ENOMEM;
+ goto err_fw;
+ }
+
+ numaltsetting = interface->num_altsetting;
+ pr_debug("number of alt settings is: %d\n", numaltsetting);
+ iface_desc = interface->cur_altsetting;
+ pr_debug("number of endpoints is: %d\n",
+ iface_desc->desc.bNumEndpoints);
+ pr_debug("descriptor type is: %d\n", iface_desc->desc.bDescriptorType);
+ pr_debug("interface number is: %d\n",
+ iface_desc->desc.bInterfaceNumber);
+ pr_debug("alternatesetting is: %d\n",
+ iface_desc->desc.bAlternateSetting);
+ pr_debug("interface class is: %d\n", iface_desc->desc.bInterfaceClass);
+ pr_debug("control endpoint info:\n");
+ pr_debug("descriptor0 type -- %d\n",
+ iface_desc->endpoint[0].desc.bmAttributes);
+ pr_debug("descriptor1 type -- %d\n",
+ iface_desc->endpoint[1].desc.bmAttributes);
+ pr_debug("descriptor2 type -- %d\n",
+ iface_desc->endpoint[2].desc.bmAttributes);
+
+ for (i = 0; i < iface_desc->desc.bNumEndpoints; i++) {
+ endpoint =
+ (struct usb_endpoint_descriptor *)&iface_desc->
+ endpoint[i].desc;
+ pr_debug("endpoint %d\n", i);
+ pr_debug("bEndpointAddress=%x, bmAttributes=%x\n",
+ endpoint->bEndpointAddress, endpoint->bmAttributes);
+ if ((endpoint->bEndpointAddress & USB_DIR_IN)
+ && ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) ==
+ USB_ENDPOINT_XFER_BULK)) {
+ ft1000dev->bulk_in_endpointAddr =
+ endpoint->bEndpointAddress;
+ pr_debug("in: %d\n", endpoint->bEndpointAddress);
+ }
+
+ if (!(endpoint->bEndpointAddress & USB_DIR_IN)
+ && ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) ==
+ USB_ENDPOINT_XFER_BULK)) {
+ ft1000dev->bulk_out_endpointAddr =
+ endpoint->bEndpointAddress;
+ pr_debug("out: %d\n", endpoint->bEndpointAddress);
+ }
+ }
+
+ pr_debug("bulk_in=%d, bulk_out=%d\n",
+ ft1000dev->bulk_in_endpointAddr,
+ ft1000dev->bulk_out_endpointAddr);
+
+ ret = reject_firmware(&dsp_fw, "/*(DEBLOBBED)*/", &dev->dev);
+ if (ret < 0) {
+ dev_err(interface->usb_dev, "Error reject_firmware()\n");
+ goto err_fw;
+ }
+
+ size = max_t(uint, dsp_fw->size, 4096);
+ pFileStart = kmalloc(size, GFP_KERNEL);
+
+ if (!pFileStart) {
+ release_firmware(dsp_fw);
+ ret = -ENOMEM;
+ goto err_fw;
+ }
+
+ memcpy(pFileStart, dsp_fw->data, dsp_fw->size);
+ FileLength = dsp_fw->size;
+ release_firmware(dsp_fw);
+
+ pr_debug("start downloading dsp image...\n");
+
+ ret = init_ft1000_netdev(ft1000dev);
+ if (ret)
+ goto err_load;
+
+ pft1000info = netdev_priv(ft1000dev->net);
+
+ pr_debug("pft1000info=%p\n", pft1000info);
+ ret = dsp_reload(ft1000dev);
+ if (ret) {
+ dev_err(interface->usb_dev,
+ "Problem with DSP image loading\n");
+ goto err_load;
+ }
+
+ gPollingfailed = false;
+ ft1000dev->pPollThread =
+ kthread_run(ft1000_poll_thread, ft1000dev, "ft1000_poll");
+
+ if (IS_ERR(ft1000dev->pPollThread)) {
+ ret = PTR_ERR(ft1000dev->pPollThread);
+ goto err_load;
+ }
+
+ msleep(500);
+
+ while (!pft1000info->CardReady) {
+ if (gPollingfailed) {
+ ret = -EIO;
+ goto err_thread;
+ }
+ msleep(100);
+ pr_debug("Waiting for Card Ready\n");
+ }
+
+ pr_debug("Card Ready!!!! Registering network device\n");
+
+ ret = reg_ft1000_netdev(ft1000dev, interface);
+ if (ret)
+ goto err_thread;
+
+ ft1000dev->NetDevRegDone = 1;
+
+ return 0;
+
+err_thread:
+ kthread_stop(ft1000dev->pPollThread);
+err_load:
+ kfree(pFileStart);
+err_fw:
+ usb_free_urb(ft1000dev->rx_urb);
+ usb_free_urb(ft1000dev->tx_urb);
+ kfree(ft1000dev);
+ return ret;
+}
+
+static void ft1000_disconnect(struct usb_interface *interface)
+{
+ struct ft1000_info *pft1000info;
+ struct ft1000_usb *ft1000dev;
+
+ pft1000info = (struct ft1000_info *)usb_get_intfdata(interface);
+ pr_debug("In disconnect pft1000info=%p\n", pft1000info);
+
+ if (pft1000info) {
+ ft1000dev = pft1000info->priv;
+ if (ft1000dev->pPollThread)
+ kthread_stop(ft1000dev->pPollThread);
+
+ pr_debug("threads are terminated\n");
+
+ if (ft1000dev->net) {
+ pr_debug("destroy char driver\n");
+ ft1000_destroy_dev(ft1000dev->net);
+ unregister_netdev(ft1000dev->net);
+ pr_debug("network device unregistered\n");
+ free_netdev(ft1000dev->net);
+
+ }
+
+ usb_free_urb(ft1000dev->rx_urb);
+ usb_free_urb(ft1000dev->tx_urb);
+
+ pr_debug("urb freed\n");
+
+ kfree(ft1000dev);
+ }
+ kfree(pFileStart);
+}
+
+static struct usb_driver ft1000_usb_driver = {
+ .name = "ft1000usb",
+ .probe = ft1000_probe,
+ .disconnect = ft1000_disconnect,
+ .id_table = id_table,
+};
+
+module_usb_driver(ft1000_usb_driver);
diff --git a/drivers/staging/ft1000/ft1000-usb/ft1000_usb.h b/drivers/staging/ft1000/ft1000-usb/ft1000_usb.h
new file mode 100644
index 000000000..fea60d565
--- /dev/null
+++ b/drivers/staging/ft1000/ft1000-usb/ft1000_usb.h
@@ -0,0 +1,150 @@
+#ifndef _FT1000_USB_H_
+#define _FT1000_USB_H_
+
+#include "../ft1000.h"
+#include "ft1000_ioctl.h"
+#define FT1000_DRV_VER 0x01010403
+
+#define MAX_NUM_APP 6
+#define MAX_MSG_LIMIT 200
+#define NUM_OF_FREE_BUFFERS 1500
+
+#define PSEUDOSZ 16
+
+struct app_info_block {
+ u32 nTxMsg; /* DPRAM msg sent to DSP with app_id */
+ u32 nRxMsg; /* DPRAM msg rcv from dsp with app_id */
+ u32 nTxMsgReject; /* DPRAM msg rejected due to DSP doorbell
+ * set
+ */
+ u32 nRxMsgMiss; /* DPRAM msg dropped due to overflow */
+ struct fown_struct *fileobject;/* Application's file object */
+ u16 app_id; /* Application id */
+ int DspBCMsgFlag;
+ int NumOfMsg; /* number of messages queued up */
+ wait_queue_head_t wait_dpram_msg;
+ struct list_head app_sqlist; /* link list of msgs for applicaton on
+ * slow queue
+ */
+} __packed;
+
+#define FALSE 0
+#define TRUE 1
+
+#define FT1000_STATUS_CLOSING 0x01
+
+#define DSPBCMSGID 0x10
+
+/* Electrabuzz specific DPRAM mapping */
+/* this is used by ft1000_usb driver - isn't that a bug? */
+#undef FT1000_DPRAM_RX_BASE
+#define FT1000_DPRAM_RX_BASE 0x1800 /* RX AREA (SlowQ) */
+
+/* MEMORY MAP FOR MAGNEMITE */
+/* the indexes are swapped comparing to PCMCIA - is it OK or a bug? */
+#undef FT1000_MAG_DSP_LED_INDX
+#define FT1000_MAG_DSP_LED_INDX 0x1 /* dsp led status for PAD
+ * device
+ */
+#undef FT1000_MAG_DSP_CON_STATE_INDX
+#define FT1000_MAG_DSP_CON_STATE_INDX 0x0 /* DSP Connection Status Info */
+
+/* Maximum times trying to get ASIC out of reset */
+#define MAX_ASIC_RESET_CNT 20
+
+#define MAX_BUF_SIZE 4096
+
+struct ft1000_debug_dirs {
+ struct list_head list;
+ struct dentry *dent;
+ struct dentry *file;
+ int int_number;
+};
+
+struct ft1000_usb {
+ struct usb_device *dev;
+ struct net_device *net;
+
+ u32 status;
+
+ struct urb *rx_urb;
+ struct urb *tx_urb;
+
+ u8 tx_buf[MAX_BUF_SIZE];
+ u8 rx_buf[MAX_BUF_SIZE];
+
+ u8 bulk_in_endpointAddr;
+ u8 bulk_out_endpointAddr;
+
+ struct task_struct *pPollThread;
+ unsigned char fcodeldr;
+ unsigned char bootmode;
+ unsigned char usbboot;
+ unsigned short dspalive;
+ bool fProvComplete;
+ bool fCondResetPend;
+ bool fAppMsgPend;
+ int DeviceCreated;
+ int NetDevRegDone;
+ u8 CardNumber;
+ u8 DeviceName[15];
+ struct ft1000_debug_dirs nodes;
+ spinlock_t fifo_lock;
+ int appcnt;
+ struct app_info_block app_info[MAX_NUM_APP];
+ u16 DrvMsgPend;
+ unsigned short tempbuf[32];
+} __packed;
+
+
+struct dpram_blk {
+ struct list_head list;
+ u16 *pbuffer;
+} __packed;
+
+int ft1000_read_register(struct ft1000_usb *ft1000dev,
+ u16 *Data, u16 nRegIndx);
+int ft1000_write_register(struct ft1000_usb *ft1000dev,
+ u16 value, u16 nRegIndx);
+int ft1000_read_dpram32(struct ft1000_usb *ft1000dev,
+ u16 indx, u8 *buffer, u16 cnt);
+int ft1000_write_dpram32(struct ft1000_usb *ft1000dev,
+ u16 indx, u8 *buffer, u16 cnt);
+int ft1000_read_dpram16(struct ft1000_usb *ft1000dev,
+ u16 indx, u8 *buffer, u8 highlow);
+int ft1000_write_dpram16(struct ft1000_usb *ft1000dev,
+ u16 indx, u16 value, u8 highlow);
+int fix_ft1000_read_dpram32(struct ft1000_usb *ft1000dev,
+ u16 indx, u8 *buffer);
+int fix_ft1000_write_dpram32(struct ft1000_usb *ft1000dev,
+ u16 indx, u8 *buffer);
+
+extern void *pFileStart;
+extern size_t FileLength;
+extern int numofmsgbuf;
+
+int ft1000_close(struct net_device *dev);
+int scram_dnldr(struct ft1000_usb *ft1000dev, void *pFileStart,
+ u32 FileLength);
+
+extern struct list_head freercvpool;
+
+/* lock to arbitrate free buffer list for receive command data */
+extern spinlock_t free_buff_lock;
+
+int ft1000_create_dev(struct ft1000_usb *dev);
+void ft1000_destroy_dev(struct net_device *dev);
+extern int card_send_command(struct ft1000_usb *ft1000dev,
+ void *ptempbuffer, int size);
+
+struct dpram_blk *ft1000_get_buffer(struct list_head *bufflist);
+void ft1000_free_buffer(struct dpram_blk *pdpram_blk, struct list_head *plist);
+
+int dsp_reload(struct ft1000_usb *ft1000dev);
+int init_ft1000_netdev(struct ft1000_usb *ft1000dev);
+struct usb_interface;
+int reg_ft1000_netdev(struct ft1000_usb *ft1000dev,
+ struct usb_interface *intf);
+int ft1000_poll(void *dev_id);
+
+#endif /* _FT1000_USB_H_ */
diff --git a/drivers/staging/ft1000/ft1000.h b/drivers/staging/ft1000/ft1000.h
new file mode 100644
index 000000000..8a2e4caa5
--- /dev/null
+++ b/drivers/staging/ft1000/ft1000.h
@@ -0,0 +1,366 @@
+/*
+ * Common structures and definitions for FT1000 Flarion Flash OFDM PCMCIA and
+ * USB devices.
+ *
+ * Originally copyright (c) 2002 Flarion Technologies
+ *
+ */
+
+#define DSPVERSZ 4
+#define HWSERNUMSZ 16
+#define SKUSZ 20
+#define EUISZ 8
+#define MODESZ 2
+#define CALVERSZ 2
+#define CALDATESZ 6
+
+#define ELECTRABUZZ_ID 0 /* ASIC ID for Electrabuzz */
+#define MAGNEMITE_ID 0x1a01 /* ASIC ID for Magnemite */
+
+/* MEMORY MAP common to both ELECTRABUZZ and MAGNEMITE */
+#define FT1000_REG_DPRAM_ADDR 0x000E /* DPADR - Dual Port Ram Indirect
+ * Address Register
+ */
+#define FT1000_REG_SUP_CTRL 0x0020 /* HCTR - Host Control Register */
+#define FT1000_REG_SUP_STAT 0x0022 /* HSTAT - Host Status Register */
+#define FT1000_REG_RESET 0x0024 /* HCTR - Host Control Register */
+#define FT1000_REG_SUP_ISR 0x0026 /* HISR - Host Interrupt Status
+ * Register
+ */
+#define FT1000_REG_SUP_IMASK 0x0028 /* HIMASK - Host Interrupt Mask */
+#define FT1000_REG_DOORBELL 0x002a /* DBELL - Door Bell Register */
+#define FT1000_REG_ASIC_ID 0x002e /* ASICID - ASIC Identification
+ * Number
+ */
+
+/* MEMORY MAP FOR ELECTRABUZZ ASIC */
+#define FT1000_REG_UFIFO_STAT 0x0000 /* UFSR - Uplink FIFO status register */
+#define FT1000_REG_UFIFO_BEG 0x0002 /* UFBR - Uplink FIFO beginning
+ * register
+ */
+#define FT1000_REG_UFIFO_MID 0x0004 /* UFMR - Uplink FIFO middle register */
+#define FT1000_REG_UFIFO_END 0x0006 /* UFER - Uplink FIFO end register */
+#define FT1000_REG_DFIFO_STAT 0x0008 /* DFSR - Downlink FIFO status
+ * register
+ */
+#define FT1000_REG_DFIFO 0x000A /* DFR - Downlink FIFO Register */
+#define FT1000_REG_DPRAM_DATA 0x000C /* DPRAM - Dual Port Indirect
+ * Data Register
+ */
+#define FT1000_REG_WATERMARK 0x0010 /* WMARK - Watermark Register */
+
+/* MEMORY MAP FOR MAGNEMITE */
+#define FT1000_REG_MAG_UFDR 0x0000 /* UFDR - Uplink FIFO Data
+ * Register (32-bits)
+ */
+#define FT1000_REG_MAG_UFDRL 0x0000 /* UFDRL - Uplink FIFO Data
+ * Register low-word (16-bits)
+ */
+#define FT1000_REG_MAG_UFDRH 0x0002 /* UFDRH - Uplink FIFO Data Register
+ * high-word (16-bits)
+ */
+#define FT1000_REG_MAG_UFER 0x0004 /* UFER - Uplink FIFO End Register */
+#define FT1000_REG_MAG_UFSR 0x0006 /* UFSR - Uplink FIFO Status Register */
+#define FT1000_REG_MAG_DFR 0x0008 /* DFR - Downlink FIFO Register
+ * (32-bits)
+ */
+#define FT1000_REG_MAG_DFRL 0x0008 /* DFRL - Downlink FIFO Register
+ * low-word (16-bits)
+ */
+#define FT1000_REG_MAG_DFRH 0x000a /* DFRH - Downlink FIFO Register
+ * high-word (16-bits)
+ */
+#define FT1000_REG_MAG_DFSR 0x000c /* DFSR - Downlink FIFO Status
+ * Register
+ */
+#define FT1000_REG_MAG_DPDATA 0x0010 /* DPDATA - Dual Port RAM Indirect
+ * Data Register (32-bits)
+ */
+#define FT1000_REG_MAG_DPDATAL 0x0010 /* DPDATAL - Dual Port RAM Indirect
+ * Data Register low-word (16-bits)
+ */
+#define FT1000_REG_MAG_DPDATAH 0x0012 /* DPDATAH - Dual Port RAM Indirect Data
+ * Register high-word (16-bits)
+ */
+#define FT1000_REG_MAG_WATERMARK 0x002c /* WMARK - Watermark Register */
+#define FT1000_REG_MAG_VERSION 0x0030 /* LLC Version */
+
+/* Reserved Dual Port RAM offsets for Electrabuzz */
+#define FT1000_DPRAM_TX_BASE 0x0002 /* Host to PC Card Messaging Area */
+#define FT1000_DPRAM_RX_BASE 0x0800 /* PC Card to Host Messaging Area */
+#define FT1000_FIFO_LEN 0x07FC /* total length for DSP FIFO tracking */
+#define FT1000_HI_HO 0x07FE /* heartbeat with HI/HO */
+#define FT1000_DSP_STATUS 0x0FFE /* dsp status - non-zero is a request
+ * to reset dsp
+ */
+#define FT1000_DSP_LED 0x0FFA /* dsp led status for PAD device */
+#define FT1000_DSP_CON_STATE 0x0FF8 /* DSP Connection Status Info */
+#define FT1000_DPRAM_FEFE 0x0002 /* location for dsp ready indicator */
+#define FT1000_DSP_TIMER0 0x1FF0 /* Timer Field from Basestation */
+#define FT1000_DSP_TIMER1 0x1FF2 /* Timer Field from Basestation */
+#define FT1000_DSP_TIMER2 0x1FF4 /* Timer Field from Basestation */
+#define FT1000_DSP_TIMER3 0x1FF6 /* Timer Field from Basestation */
+
+/* Reserved Dual Port RAM offsets for Magnemite */
+#define FT1000_DPRAM_MAG_TX_BASE 0x0000 /* Host to PC Card
+ * Messaging Area
+ */
+#define FT1000_DPRAM_MAG_RX_BASE 0x0200 /* PC Card to Host
+ * Messaging Area
+ */
+
+#define FT1000_MAG_FIFO_LEN 0x1FF /* total length for DSP
+ * FIFO tracking
+ */
+#define FT1000_MAG_FIFO_LEN_INDX 0x1 /* low-word index */
+#define FT1000_MAG_HI_HO 0x1FF /* heartbeat with HI/HO */
+#define FT1000_MAG_HI_HO_INDX 0x0 /* high-word index */
+#define FT1000_MAG_DSP_LED 0x3FE /* dsp led status for
+ * PAD device
+ */
+#define FT1000_MAG_DSP_LED_INDX 0x0 /* dsp led status for
+ * PAD device
+ */
+#define FT1000_MAG_DSP_CON_STATE 0x3FE /* DSP Connection Status Info */
+#define FT1000_MAG_DSP_CON_STATE_INDX 0x1 /* DSP Connection Status Info */
+#define FT1000_MAG_DPRAM_FEFE 0x000 /* location for dsp ready
+ * indicator
+ */
+#define FT1000_MAG_DPRAM_FEFE_INDX 0x0 /* location for dsp ready
+ * indicator
+ */
+#define FT1000_MAG_DSP_TIMER0 0x3FC /* Timer Field from
+ * Basestation
+ */
+#define FT1000_MAG_DSP_TIMER0_INDX 0x1
+#define FT1000_MAG_DSP_TIMER1 0x3FC /* Timer Field from
+ * Basestation
+ */
+#define FT1000_MAG_DSP_TIMER1_INDX 0x0
+#define FT1000_MAG_DSP_TIMER2 0x3FD /* Timer Field from
+ * Basestation
+ */
+#define FT1000_MAG_DSP_TIMER2_INDX 0x1
+#define FT1000_MAG_DSP_TIMER3 0x3FD /* Timer Field from
+ * Basestation
+ */
+#define FT1000_MAG_DSP_TIMER3_INDX 0x0
+#define FT1000_MAG_TOTAL_LEN 0x200
+#define FT1000_MAG_TOTAL_LEN_INDX 0x1
+#define FT1000_MAG_PH_LEN 0x200
+#define FT1000_MAG_PH_LEN_INDX 0x0
+#define FT1000_MAG_PORT_ID 0x201
+#define FT1000_MAG_PORT_ID_INDX 0x0
+
+#define HOST_INTF_LE 0x0 /* Host interface little endian mode */
+#define HOST_INTF_BE 0x1 /* Host interface big endian mode */
+
+/* FT1000 to Host Doorbell assignments */
+#define FT1000_DB_DPRAM_RX 0x0001 /* this value indicates that DSP
+ * has data for host in DPRAM
+ */
+#define FT1000_DB_DNLD_RX 0x0002 /* Downloader handshake doorbell */
+#define FT1000_ASIC_RESET_REQ 0x0004 /* DSP requesting host to
+ * reset the ASIC
+ */
+#define FT1000_DSP_ASIC_RESET 0x0008 /* DSP indicating host that
+ * it will reset the ASIC
+ */
+#define FT1000_DB_COND_RESET 0x0010 /* DSP request for a card reset. */
+
+/* Host to FT1000 Doorbell assignments */
+#define FT1000_DB_DPRAM_TX 0x0100 /* this value indicates that host
+ * has data for DSP in DPRAM.
+ */
+#define FT1000_DB_DNLD_TX 0x0200 /* Downloader handshake doorbell */
+#define FT1000_ASIC_RESET_DSP 0x0400 /* Responds to FT1000_ASIC_RESET_REQ */
+#define FT1000_DB_HB 0x1000 /* Indicates that supervisor has a
+ * heartbeat message for DSP.
+ */
+
+#define hi 0x6869 /* PC Card heartbeat values */
+#define ho 0x686f /* PC Card heartbeat values */
+
+/* Magnemite specific defines */
+#define hi_mag 0x6968 /* Byte swap hi to avoid
+ * additional system call
+ */
+#define ho_mag 0x6f68 /* Byte swap ho to avoid
+ * additional system call
+ */
+
+/* Bit field definitions for Host Interrupt Status Register */
+/* Indicate the cause of an interrupt. */
+#define ISR_EMPTY 0x00 /* no bits set */
+#define ISR_DOORBELL_ACK 0x01 /* Doorbell acknowledge from DSP */
+#define ISR_DOORBELL_PEND 0x02 /* Doorbell pending from DSP */
+#define ISR_RCV 0x04 /* Packet available in Downlink FIFO */
+#define ISR_WATERMARK 0x08 /* Watermark requirements satisfied */
+
+/* Bit field definition for Host Interrupt Mask */
+#define ISR_MASK_NONE 0x0000 /* no bits set */
+#define ISR_MASK_DOORBELL_ACK 0x0001 /* Doorbell acknowledge mask */
+#define ISR_MASK_DOORBELL_PEND 0x0002 /* Doorbell pending mask */
+#define ISR_MASK_RCV 0x0004 /* Downlink Packet available mask */
+#define ISR_MASK_WATERMARK 0x0008 /* Watermark interrupt mask */
+#define ISR_MASK_ALL 0xffff /* Mask all interrupts */
+/* Default interrupt mask
+ * (Enable Doorbell pending and Packet available interrupts)
+ */
+#define ISR_DEFAULT_MASK 0x7ff9
+
+/* Bit field definition for Host Control Register */
+#define DSP_RESET_BIT 0x0001 /* Bit field to control
+ * dsp reset state
+ */
+ /* (0 = out of reset 1 = reset) */
+#define ASIC_RESET_BIT 0x0002 /* Bit field to control
+ * ASIC reset state
+ */
+ /* (0 = out of reset 1 = reset) */
+#define DSP_UNENCRYPTED 0x0004
+#define DSP_ENCRYPTED 0x0008
+#define EFUSE_MEM_DISABLE 0x0040
+
+/* Application specific IDs */
+#define DSPID 0x20
+#define HOSTID 0x10
+#define DSPAIRID 0x90
+#define DRIVERID 0x00
+#define NETWORKID 0x20
+
+/* Size of DPRAM Message */
+#define MAX_CMD_SQSIZE 1780
+
+#define ENET_MAX_SIZE 1514
+#define ENET_HEADER_SIZE 14
+
+#define SLOWQ_TYPE 0
+#define FASTQ_TYPE 1
+
+#define MAX_DSP_SESS_REC 1024
+
+#define DSP_QID_OFFSET 4
+
+/* Driver message types */
+#define MEDIA_STATE 0x0010
+#define TIME_UPDATE 0x0020
+#define DSP_PROVISION 0x0030
+#define DSP_INIT_MSG 0x0050
+#define DSP_HIBERNATE 0x0060
+#define DSP_STORE_INFO 0x0070
+#define DSP_GET_INFO 0x0071
+#define GET_DRV_ERR_RPT_MSG 0x0073
+#define RSP_DRV_ERR_RPT_MSG 0x0074
+
+/* Driver Error Messages for DSP */
+#define DSP_HB_INFO 0x7ef0
+#define DSP_FIFO_INFO 0x7ef1
+#define DSP_CONDRESET_INFO 0x7ef2
+#define DSP_CMDLEN_INFO 0x7ef3
+#define DSP_CMDPHCKSUM_INFO 0x7ef4
+#define DSP_PKTPHCKSUM_INFO 0x7ef5
+#define DSP_PKTLEN_INFO 0x7ef6
+#define DSP_USER_RESET 0x7ef7
+#define FIFO_FLUSH_MAXLIMIT 0x7ef8
+#define FIFO_FLUSH_BADCNT 0x7ef9
+#define FIFO_ZERO_LEN 0x7efa
+
+/* Pseudo Header structure */
+struct pseudo_hdr {
+ unsigned short length; /* length of msg body */
+ unsigned char source; /* hardware source id */
+ /* Host = 0x10 */
+ /* Dsp = 0x20 */
+ unsigned char destination; /* hardware destination id
+ * (refer to source)
+ */
+ unsigned char portdest; /* software destination port id */
+ /* Host = 0x00 */
+ /* Applicaton Broadcast = 0x10 */
+ /* Network Stack = 0x20 */
+ /* Dsp OAM = 0x80 */
+ /* Dsp Airlink = 0x90 */
+ /* Dsp Loader = 0xa0 */
+ /* Dsp MIP = 0xb0 */
+ unsigned char portsrc; /* software source port id
+ * (refer to portdest)
+ */
+ unsigned short sh_str_id; /* not used */
+ unsigned char control; /* not used */
+ unsigned char rsvd1;
+ unsigned char seq_num; /* message sequence number */
+ unsigned char rsvd2;
+ unsigned short qos_class; /* not used */
+ unsigned short checksum; /* pseudo header checksum */
+} __packed;
+
+struct drv_msg {
+ struct pseudo_hdr pseudo;
+ u16 type;
+ u16 length;
+ u8 data[0];
+} __packed;
+
+struct media_msg {
+ struct pseudo_hdr pseudo;
+ u16 type;
+ u16 length;
+ u16 state;
+ u32 ip_addr;
+ u32 net_mask;
+ u32 gateway;
+ u32 dns_1;
+ u32 dns_2;
+} __packed;
+
+struct dsp_init_msg {
+ struct pseudo_hdr pseudo;
+ u16 type;
+ u16 length;
+ u8 DspVer[DSPVERSZ]; /* DSP version number */
+ u8 HwSerNum[HWSERNUMSZ]; /* Hardware Serial Number */
+ u8 Sku[SKUSZ]; /* SKU */
+ u8 eui64[EUISZ]; /* EUI64 */
+ u8 ProductMode[MODESZ]; /* Product Mode (Market/Production) */
+ u8 RfCalVer[CALVERSZ]; /* Rf Calibration version */
+ u8 RfCalDate[CALDATESZ]; /* Rf Calibration date */
+} __packed;
+
+struct prov_record {
+ struct list_head list;
+ u8 *pprov_data;
+};
+
+struct ft1000_info {
+ void *priv;
+ struct net_device_stats stats;
+ u16 DrvErrNum;
+ u16 AsicID;
+ int CardReady;
+ int registered;
+ int mediastate;
+ u8 squeseqnum; /* sequence number on slow queue */
+ spinlock_t dpram_lock;
+ u16 fifo_cnt;
+ u8 DspVer[DSPVERSZ]; /* DSP version number */
+ u8 HwSerNum[HWSERNUMSZ]; /* Hardware Serial Number */
+ u8 Sku[SKUSZ]; /* SKU */
+ u8 eui64[EUISZ]; /* EUI64 */
+ time_t ConTm; /* Connection Time */
+ u8 ProductMode[MODESZ];
+ u8 RfCalVer[CALVERSZ];
+ u8 RfCalDate[CALDATESZ];
+ u16 DSP_TIME[4];
+ u16 LedStat;
+ u16 ConStat;
+ u16 ProgConStat;
+ struct list_head prov_list;
+ u16 DSPInfoBlklen;
+ int (*ft1000_reset)(void *);
+ u16 DSPInfoBlk[MAX_DSP_SESS_REC];
+ union {
+ u16 Rec[MAX_DSP_SESS_REC];
+ u32 MagRec[MAX_DSP_SESS_REC/2];
+ } DSPSess;
+};
diff --git a/drivers/staging/fwserial/Kconfig b/drivers/staging/fwserial/Kconfig
new file mode 100644
index 000000000..9c7c9267d
--- /dev/null
+++ b/drivers/staging/fwserial/Kconfig
@@ -0,0 +1,31 @@
+config FIREWIRE_SERIAL
+ tristate "TTY over Firewire"
+ depends on FIREWIRE && TTY
+ help
+ This enables TTY over IEEE 1394, providing high-speed serial
+ connectivity to cabled peers. This driver implements a
+ ad-hoc transport protocol and is currently limited to
+ Linux-to-Linux communication.
+
+ To compile this driver as a module, say M here: the module will
+ be called firewire-serial.
+
+if FIREWIRE_SERIAL
+
+config FWTTY_MAX_TOTAL_PORTS
+ int "Maximum number of serial ports supported"
+ default "64"
+ help
+ Set this to the maximum number of serial ports you want the
+ firewire-serial driver to support.
+
+config FWTTY_MAX_CARD_PORTS
+ int "Maximum number of serial ports supported per adapter"
+ range 0 FWTTY_MAX_TOTAL_PORTS
+ default "32"
+ help
+ Set this to the maximum number of serial ports each firewire
+ adapter supports. The actual number of serial ports registered
+ is set with the module parameter "ttys".
+
+endif
diff --git a/drivers/staging/fwserial/Makefile b/drivers/staging/fwserial/Makefile
new file mode 100644
index 000000000..2170869a1
--- /dev/null
+++ b/drivers/staging/fwserial/Makefile
@@ -0,0 +1,2 @@
+obj-$(CONFIG_FIREWIRE_SERIAL) += firewire-serial.o
+firewire-serial-objs := fwserial.o dma_fifo.o
diff --git a/drivers/staging/fwserial/TODO b/drivers/staging/fwserial/TODO
new file mode 100644
index 000000000..382a79594
--- /dev/null
+++ b/drivers/staging/fwserial/TODO
@@ -0,0 +1,14 @@
+TODOs prior to this driver moving out of staging
+------------------------------------------------
+1. Implement retries for RCODE_BUSY, RCODE_NO_ACK and RCODE_SEND_ERROR
+ - I/O is handled asynchronously which presents some issues when error
+ conditions occur.
+2. Implement _robust_ console on top of this. The existing prototype console
+ driver is not ready for the big leagues yet.
+3. Expose means of controlling attach/detach of peers via sysfs. Include
+ GUID-to-port matching/whitelist/blacklist.
+
+-- Issues with firewire stack --
+1. This driver uses the same unregistered vendor id that the firewire core does
+ (0xd00d1e). Perhaps this could be exposed as a define in
+ firewire.h?
diff --git a/drivers/staging/fwserial/dma_fifo.c b/drivers/staging/fwserial/dma_fifo.c
new file mode 100644
index 000000000..027906249
--- /dev/null
+++ b/drivers/staging/fwserial/dma_fifo.c
@@ -0,0 +1,303 @@
+/*
+ * DMA-able FIFO implementation
+ *
+ * Copyright (C) 2012 Peter Hurley <peter@hurleysoftware.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/list.h>
+#include <linux/bug.h>
+
+#include "dma_fifo.h"
+
+#ifdef DEBUG_TRACING
+#define df_trace(s, args...) pr_debug(s, ##args)
+#else
+#define df_trace(s, args...)
+#endif
+
+#define FAIL(fifo, condition, format...) ({ \
+ fifo->corrupt = !!(condition); \
+ WARN(fifo->corrupt, format); \
+})
+
+/*
+ * private helper fn to determine if check is in open interval (lo,hi)
+ */
+static bool addr_check(unsigned check, unsigned lo, unsigned hi)
+{
+ return check - (lo + 1) < (hi - 1) - lo;
+}
+
+/**
+ * dma_fifo_init: initialize the fifo to a valid but inoperative state
+ * @fifo: address of in-place "struct dma_fifo" object
+ */
+void dma_fifo_init(struct dma_fifo *fifo)
+{
+ memset(fifo, 0, sizeof(*fifo));
+ INIT_LIST_HEAD(&fifo->pending);
+}
+
+/**
+ * dma_fifo_alloc - initialize and allocate dma_fifo
+ * @fifo: address of in-place "struct dma_fifo" object
+ * @size: 'apparent' size, in bytes, of fifo
+ * @align: dma alignment to maintain (should be at least cpu cache alignment),
+ * must be power of 2
+ * @tx_limit: maximum # of bytes transmissable per dma (rounded down to
+ * multiple of alignment, but at least align size)
+ * @open_limit: maximum # of outstanding dma transactions allowed
+ * @gfp_mask: get_free_pages mask, passed to kmalloc()
+ *
+ * The 'apparent' size will be rounded up to next greater aligned size.
+ * Returns 0 if no error, otherwise an error code
+ */
+int dma_fifo_alloc(struct dma_fifo *fifo, int size, unsigned align,
+ int tx_limit, int open_limit, gfp_t gfp_mask)
+{
+ int capacity;
+
+ if (!is_power_of_2(align) || size < 0)
+ return -EINVAL;
+
+ size = round_up(size, align);
+ capacity = size + align * open_limit + align * DMA_FIFO_GUARD;
+ fifo->data = kmalloc(capacity, gfp_mask);
+ if (!fifo->data)
+ return -ENOMEM;
+
+ fifo->in = 0;
+ fifo->out = 0;
+ fifo->done = 0;
+ fifo->size = size;
+ fifo->avail = size;
+ fifo->align = align;
+ fifo->tx_limit = max_t(int, round_down(tx_limit, align), align);
+ fifo->open = 0;
+ fifo->open_limit = open_limit;
+ fifo->guard = size + align * open_limit;
+ fifo->capacity = capacity;
+ fifo->corrupt = 0;
+
+ return 0;
+}
+
+/**
+ * dma_fifo_free - frees the fifo
+ * @fifo: address of in-place "struct dma_fifo" to free
+ *
+ * Also reinits the fifo to a valid but inoperative state. This
+ * allows the fifo to be reused with a different target requiring
+ * different fifo parameters.
+ */
+void dma_fifo_free(struct dma_fifo *fifo)
+{
+ struct dma_pending *pending, *next;
+
+ if (fifo->data == NULL)
+ return;
+
+ list_for_each_entry_safe(pending, next, &fifo->pending, link)
+ list_del_init(&pending->link);
+ kfree(fifo->data);
+ fifo->data = NULL;
+}
+
+/**
+ * dma_fifo_reset - dumps the fifo contents and reinits for reuse
+ * @fifo: address of in-place "struct dma_fifo" to reset
+ */
+void dma_fifo_reset(struct dma_fifo *fifo)
+{
+ struct dma_pending *pending, *next;
+
+ if (fifo->data == NULL)
+ return;
+
+ list_for_each_entry_safe(pending, next, &fifo->pending, link)
+ list_del_init(&pending->link);
+ fifo->in = 0;
+ fifo->out = 0;
+ fifo->done = 0;
+ fifo->avail = fifo->size;
+ fifo->open = 0;
+ fifo->corrupt = 0;
+}
+
+/**
+ * dma_fifo_in - copies data into the fifo
+ * @fifo: address of in-place "struct dma_fifo" to write to
+ * @src: buffer to copy from
+ * @n: # of bytes to copy
+ *
+ * Returns the # of bytes actually copied, which can be less than requested if
+ * the fifo becomes full. If < 0, return is error code.
+ */
+int dma_fifo_in(struct dma_fifo *fifo, const void *src, int n)
+{
+ int ofs, l;
+
+ if (fifo->data == NULL)
+ return -ENOENT;
+ if (fifo->corrupt)
+ return -ENXIO;
+
+ if (n > fifo->avail)
+ n = fifo->avail;
+ if (n <= 0)
+ return 0;
+
+ ofs = fifo->in % fifo->capacity;
+ l = min(n, fifo->capacity - ofs);
+ memcpy(fifo->data + ofs, src, l);
+ memcpy(fifo->data, src + l, n - l);
+
+ if (FAIL(fifo, addr_check(fifo->done, fifo->in, fifo->in + n) ||
+ fifo->avail < n,
+ "fifo corrupt: in:%u out:%u done:%u n:%d avail:%d",
+ fifo->in, fifo->out, fifo->done, n, fifo->avail))
+ return -ENXIO;
+
+ fifo->in += n;
+ fifo->avail -= n;
+
+ df_trace("in:%u out:%u done:%u n:%d avail:%d", fifo->in, fifo->out,
+ fifo->done, n, fifo->avail);
+
+ return n;
+}
+
+/**
+ * dma_fifo_out_pend - gets address/len of next avail read and marks as pended
+ * @fifo: address of in-place "struct dma_fifo" to read from
+ * @pended: address of structure to fill with read address/len
+ * The data/len fields will be NULL/0 if no dma is pended.
+ *
+ * Returns the # of used bytes remaining in fifo (ie, if > 0, more data
+ * remains in the fifo that was not pended). If < 0, return is error code.
+ */
+int dma_fifo_out_pend(struct dma_fifo *fifo, struct dma_pending *pended)
+{
+ unsigned len, n, ofs, l, limit;
+
+ if (fifo->data == NULL)
+ return -ENOENT;
+ if (fifo->corrupt)
+ return -ENXIO;
+
+ pended->len = 0;
+ pended->data = NULL;
+ pended->out = fifo->out;
+
+ len = fifo->in - fifo->out;
+ if (!len)
+ return -ENODATA;
+ if (fifo->open == fifo->open_limit)
+ return -EAGAIN;
+
+ n = len;
+ ofs = fifo->out % fifo->capacity;
+ l = fifo->capacity - ofs;
+ limit = min_t(unsigned, l, fifo->tx_limit);
+ if (n > limit) {
+ n = limit;
+ fifo->out += limit;
+ } else if (ofs + n > fifo->guard) {
+ fifo->out += l;
+ fifo->in = fifo->out;
+ } else {
+ fifo->out += round_up(n, fifo->align);
+ fifo->in = fifo->out;
+ }
+
+ df_trace("in: %u out: %u done: %u n: %d len: %u avail: %d", fifo->in,
+ fifo->out, fifo->done, n, len, fifo->avail);
+
+ pended->len = n;
+ pended->data = fifo->data + ofs;
+ pended->next = fifo->out;
+ list_add_tail(&pended->link, &fifo->pending);
+ ++fifo->open;
+
+ if (FAIL(fifo, fifo->open > fifo->open_limit,
+ "past open limit:%d (limit:%d)",
+ fifo->open, fifo->open_limit))
+ return -ENXIO;
+ if (FAIL(fifo, fifo->out & (fifo->align - 1),
+ "fifo out unaligned:%u (align:%u)",
+ fifo->out, fifo->align))
+ return -ENXIO;
+
+ return len - n;
+}
+
+/**
+ * dma_fifo_out_complete - marks pended dma as completed
+ * @fifo: address of in-place "struct dma_fifo" which was read from
+ * @complete: address of structure for previously pended dma to mark completed
+ */
+int dma_fifo_out_complete(struct dma_fifo *fifo, struct dma_pending *complete)
+{
+ struct dma_pending *pending, *next, *tmp;
+
+ if (fifo->data == NULL)
+ return -ENOENT;
+ if (fifo->corrupt)
+ return -ENXIO;
+ if (list_empty(&fifo->pending) && fifo->open == 0)
+ return -EINVAL;
+
+ if (FAIL(fifo, list_empty(&fifo->pending) != (fifo->open == 0),
+ "pending list disagrees with open count:%d",
+ fifo->open))
+ return -ENXIO;
+
+ tmp = complete->data;
+ *tmp = *complete;
+ list_replace(&complete->link, &tmp->link);
+ dp_mark_completed(tmp);
+
+ /* Only update the fifo in the original pended order */
+ list_for_each_entry_safe(pending, next, &fifo->pending, link) {
+ if (!dp_is_completed(pending)) {
+ df_trace("still pending: saved out: %u len: %d",
+ pending->out, pending->len);
+ break;
+ }
+
+ if (FAIL(fifo, pending->out != fifo->done ||
+ addr_check(fifo->in, fifo->done, pending->next),
+ "in:%u out:%u done:%u saved:%u next:%u",
+ fifo->in, fifo->out, fifo->done, pending->out,
+ pending->next))
+ return -ENXIO;
+
+ list_del_init(&pending->link);
+ fifo->done = pending->next;
+ fifo->avail += pending->len;
+ --fifo->open;
+
+ df_trace("in: %u out: %u done: %u len: %u avail: %d", fifo->in,
+ fifo->out, fifo->done, pending->len, fifo->avail);
+ }
+
+ if (FAIL(fifo, fifo->open < 0, "open dma:%d < 0", fifo->open))
+ return -ENXIO;
+ if (FAIL(fifo, fifo->avail > fifo->size, "fifo avail:%d > size:%d",
+ fifo->avail, fifo->size))
+ return -ENXIO;
+
+ return 0;
+}
diff --git a/drivers/staging/fwserial/dma_fifo.h b/drivers/staging/fwserial/dma_fifo.h
new file mode 100644
index 000000000..410988224
--- /dev/null
+++ b/drivers/staging/fwserial/dma_fifo.h
@@ -0,0 +1,126 @@
+/*
+ * DMA-able FIFO interface
+ *
+ * Copyright (C) 2012 Peter Hurley <peter@hurleysoftware.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _DMA_FIFO_H_
+#define _DMA_FIFO_H_
+
+/**
+ * The design basis for the DMA FIFO is to provide an output side that
+ * complies with the streaming DMA API design that can be DMA'd from directly
+ * (without additional copying), coupled with an input side that maintains a
+ * logically consistent 'apparent' size (ie, bytes in + bytes avail is static
+ * for the lifetime of the FIFO).
+ *
+ * DMA output transactions originate on a cache line boundary and can be
+ * variably-sized. DMA output transactions can be retired out-of-order but
+ * the FIFO will only advance the output in the original input sequence.
+ * This means the FIFO will eventually stall if a transaction is never retired.
+ *
+ * Chunking the output side into cache line multiples means that some FIFO
+ * memory is unused. For example, if all the avail input has been pended out,
+ * then the in and out markers are re-aligned to the next cache line.
+ * The maximum possible waste is
+ * (cache line alignment - 1) * (max outstanding dma transactions)
+ * This potential waste requires additional hidden capacity within the FIFO
+ * to be able to accept input while the 'apparent' size has not been reached.
+ *
+ * Additional cache lines (ie, guard area) are used to minimize DMA
+ * fragmentation when wrapping at the end of the FIFO. Input is allowed into the
+ * guard area, but the in and out FIFO markers are wrapped when DMA is pended.
+ */
+
+#define DMA_FIFO_GUARD 3 /* # of cache lines to reserve for the guard area */
+
+struct dma_fifo {
+ unsigned in;
+ unsigned out; /* updated when dma is pended */
+ unsigned done; /* updated upon dma completion */
+ struct {
+ unsigned corrupt:1;
+ };
+ int size; /* 'apparent' size of fifo */
+ int guard; /* ofs of guard area */
+ int capacity; /* size + reserved */
+ int avail; /* # of unused bytes in fifo */
+ unsigned align; /* must be power of 2 */
+ int tx_limit; /* max # of bytes per dma transaction */
+ int open_limit; /* max # of outstanding allowed */
+ int open; /* # of outstanding dma transactions */
+ struct list_head pending; /* fifo markers for outstanding dma */
+ void *data;
+};
+
+struct dma_pending {
+ struct list_head link;
+ void *data;
+ unsigned len;
+ unsigned next;
+ unsigned out;
+};
+
+static inline void dp_mark_completed(struct dma_pending *dp)
+{
+ dp->data += 1;
+}
+
+static inline bool dp_is_completed(struct dma_pending *dp)
+{
+ return (unsigned long)dp->data & 1UL;
+}
+
+void dma_fifo_init(struct dma_fifo *fifo);
+int dma_fifo_alloc(struct dma_fifo *fifo, int size, unsigned align,
+ int tx_limit, int open_limit, gfp_t gfp_mask);
+void dma_fifo_free(struct dma_fifo *fifo);
+void dma_fifo_reset(struct dma_fifo *fifo);
+int dma_fifo_in(struct dma_fifo *fifo, const void *src, int n);
+int dma_fifo_out_pend(struct dma_fifo *fifo, struct dma_pending *pended);
+int dma_fifo_out_complete(struct dma_fifo *fifo,
+ struct dma_pending *complete);
+
+/* returns the # of used bytes in the fifo */
+static inline int dma_fifo_level(struct dma_fifo *fifo)
+{
+ return fifo->size - fifo->avail;
+}
+
+/* returns the # of bytes ready for output in the fifo */
+static inline int dma_fifo_out_level(struct dma_fifo *fifo)
+{
+ return fifo->in - fifo->out;
+}
+
+/* returns the # of unused bytes in the fifo */
+static inline int dma_fifo_avail(struct dma_fifo *fifo)
+{
+ return fifo->avail;
+}
+
+/* returns true if fifo has max # of outstanding dmas */
+static inline bool dma_fifo_busy(struct dma_fifo *fifo)
+{
+ return fifo->open == fifo->open_limit;
+}
+
+/* changes the max size of dma returned from dma_fifo_out_pend() */
+static inline int dma_fifo_change_tx_limit(struct dma_fifo *fifo, int tx_limit)
+{
+ tx_limit = round_down(tx_limit, fifo->align);
+ fifo->tx_limit = max_t(int, tx_limit, fifo->align);
+ return 0;
+}
+
+#endif /* _DMA_FIFO_H_ */
diff --git a/drivers/staging/fwserial/fwserial.c b/drivers/staging/fwserial/fwserial.c
new file mode 100644
index 000000000..fdb2418c5
--- /dev/null
+++ b/drivers/staging/fwserial/fwserial.c
@@ -0,0 +1,2954 @@
+/*
+ * FireWire Serial driver
+ *
+ * Copyright (C) 2012 Peter Hurley <peter@hurleysoftware.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/device.h>
+#include <linux/mod_devicetable.h>
+#include <linux/rculist.h>
+#include <linux/workqueue.h>
+#include <linux/ratelimit.h>
+#include <linux/bug.h>
+#include <linux/uaccess.h>
+
+#include "fwserial.h"
+
+#define be32_to_u64(hi, lo) ((u64)be32_to_cpu(hi) << 32 | be32_to_cpu(lo))
+
+#define LINUX_VENDOR_ID 0xd00d1eU /* same id used in card root directory */
+#define FWSERIAL_VERSION 0x00e81cU /* must be unique within LINUX_VENDOR_ID */
+
+/* configurable options */
+static int num_ttys = 4; /* # of std ttys to create per fw_card */
+ /* - doubles as loopback port index */
+static bool auto_connect = true; /* try to VIRT_CABLE to every peer */
+static bool create_loop_dev = true; /* create a loopback device for each card */
+
+module_param_named(ttys, num_ttys, int, S_IRUGO | S_IWUSR);
+module_param_named(auto, auto_connect, bool, S_IRUGO | S_IWUSR);
+module_param_named(loop, create_loop_dev, bool, S_IRUGO | S_IWUSR);
+
+/*
+ * Threshold below which the tty is woken for writing
+ * - should be equal to WAKEUP_CHARS in drivers/tty/n_tty.c because
+ * even if the writer is woken, n_tty_poll() won't set POLLOUT until
+ * our fifo is below this level
+ */
+#define WAKEUP_CHARS 256
+
+/**
+ * fwserial_list: list of every fw_serial created for each fw_card
+ * See discussion in fwserial_probe.
+ */
+static LIST_HEAD(fwserial_list);
+static DEFINE_MUTEX(fwserial_list_mutex);
+
+/**
+ * port_table: array of tty ports allocated to each fw_card
+ *
+ * tty ports are allocated during probe when an fw_serial is first
+ * created for a given fw_card. Ports are allocated in a contiguous block,
+ * each block consisting of 'num_ports' ports.
+ */
+static struct fwtty_port *port_table[MAX_TOTAL_PORTS];
+static DEFINE_MUTEX(port_table_lock);
+static bool port_table_corrupt;
+#define FWTTY_INVALID_INDEX MAX_TOTAL_PORTS
+
+#define loop_idx(port) (((port)->index) / num_ports)
+#define table_idx(loop) ((loop) * num_ports + num_ttys)
+
+/* total # of tty ports created per fw_card */
+static int num_ports;
+
+/* slab used as pool for struct fwtty_transactions */
+static struct kmem_cache *fwtty_txn_cache;
+
+struct tty_driver *fwtty_driver;
+static struct tty_driver *fwloop_driver;
+
+static struct dentry *fwserial_debugfs;
+
+struct fwtty_transaction;
+typedef void (*fwtty_transaction_cb)(struct fw_card *card, int rcode,
+ void *data, size_t length,
+ struct fwtty_transaction *txn);
+
+struct fwtty_transaction {
+ struct fw_transaction fw_txn;
+ fwtty_transaction_cb callback;
+ struct fwtty_port *port;
+ union {
+ struct dma_pending dma_pended;
+ };
+};
+
+#define to_device(a, b) (a->b)
+#define fwtty_err(p, fmt, ...) \
+ dev_err(to_device(p, device), fmt, ##__VA_ARGS__)
+#define fwtty_info(p, fmt, ...) \
+ dev_info(to_device(p, device), fmt, ##__VA_ARGS__)
+#define fwtty_notice(p, fmt, ...) \
+ dev_notice(to_device(p, device), fmt, ##__VA_ARGS__)
+#define fwtty_dbg(p, fmt, ...) \
+ dev_dbg(to_device(p, device), "%s: " fmt, __func__, ##__VA_ARGS__)
+#define fwtty_err_ratelimited(p, fmt, ...) \
+ dev_err_ratelimited(to_device(p, device), fmt, ##__VA_ARGS__)
+
+#ifdef DEBUG
+static inline void debug_short_write(struct fwtty_port *port, int c, int n)
+{
+ int avail;
+
+ if (n < c) {
+ spin_lock_bh(&port->lock);
+ avail = dma_fifo_avail(&port->tx_fifo);
+ spin_unlock_bh(&port->lock);
+ fwtty_dbg(port, "short write: avail:%d req:%d wrote:%d\n",
+ avail, c, n);
+ }
+}
+#else
+#define debug_short_write(port, c, n)
+#endif
+
+static struct fwtty_peer *__fwserial_peer_by_node_id(struct fw_card *card,
+ int generation, int id);
+
+#ifdef FWTTY_PROFILING
+
+static void fwtty_profile_fifo(struct fwtty_port *port, unsigned *stat)
+{
+ spin_lock_bh(&port->lock);
+ fwtty_profile_data(stat, dma_fifo_avail(&port->tx_fifo));
+ spin_unlock_bh(&port->lock);
+}
+
+static void fwtty_dump_profile(struct seq_file *m, struct stats *stats)
+{
+ /* for each stat, print sum of 0 to 2^k, then individually */
+ int k = 4;
+ unsigned sum;
+ int j;
+ char t[10];
+
+ snprintf(t, 10, "< %d", 1 << k);
+ seq_printf(m, "\n%14s %6s", " ", t);
+ for (j = k + 1; j < DISTRIBUTION_MAX_INDEX; ++j)
+ seq_printf(m, "%6d", 1 << j);
+
+ ++k;
+ for (j = 0, sum = 0; j <= k; ++j)
+ sum += stats->reads[j];
+ seq_printf(m, "\n%14s: %6d", "reads", sum);
+ for (j = k + 1; j <= DISTRIBUTION_MAX_INDEX; ++j)
+ seq_printf(m, "%6d", stats->reads[j]);
+
+ for (j = 0, sum = 0; j <= k; ++j)
+ sum += stats->writes[j];
+ seq_printf(m, "\n%14s: %6d", "writes", sum);
+ for (j = k + 1; j <= DISTRIBUTION_MAX_INDEX; ++j)
+ seq_printf(m, "%6d", stats->writes[j]);
+
+ for (j = 0, sum = 0; j <= k; ++j)
+ sum += stats->txns[j];
+ seq_printf(m, "\n%14s: %6d", "txns", sum);
+ for (j = k + 1; j <= DISTRIBUTION_MAX_INDEX; ++j)
+ seq_printf(m, "%6d", stats->txns[j]);
+
+ for (j = 0, sum = 0; j <= k; ++j)
+ sum += stats->unthrottle[j];
+ seq_printf(m, "\n%14s: %6d", "avail @ unthr", sum);
+ for (j = k + 1; j <= DISTRIBUTION_MAX_INDEX; ++j)
+ seq_printf(m, "%6d", stats->unthrottle[j]);
+}
+
+#else
+#define fwtty_profile_fifo(port, stat)
+#define fwtty_dump_profile(m, stats)
+#endif
+
+/*
+ * Returns the max receive packet size for the given node
+ * Devices which are OHCI v1.0/ v1.1/ v1.2-draft or RFC 2734 compliant
+ * are required by specification to support max_rec of 8 (512 bytes) or more.
+ */
+static inline int device_max_receive(struct fw_device *fw_device)
+{
+ /* see IEEE 1394-2008 table 8-8 */
+ return min(2 << fw_device->max_rec, 4096);
+}
+
+static void fwtty_log_tx_error(struct fwtty_port *port, int rcode)
+{
+ switch (rcode) {
+ case RCODE_SEND_ERROR:
+ fwtty_err_ratelimited(port, "card busy\n");
+ break;
+ case RCODE_ADDRESS_ERROR:
+ fwtty_err_ratelimited(port, "bad unit addr or write length\n");
+ break;
+ case RCODE_DATA_ERROR:
+ fwtty_err_ratelimited(port, "failed rx\n");
+ break;
+ case RCODE_NO_ACK:
+ fwtty_err_ratelimited(port, "missing ack\n");
+ break;
+ case RCODE_BUSY:
+ fwtty_err_ratelimited(port, "remote busy\n");
+ break;
+ default:
+ fwtty_err_ratelimited(port, "failed tx: %d\n", rcode);
+ }
+}
+
+static void fwtty_txn_constructor(void *this)
+{
+ struct fwtty_transaction *txn = this;
+
+ init_timer(&txn->fw_txn.split_timeout_timer);
+}
+
+static void fwtty_common_callback(struct fw_card *card, int rcode,
+ void *payload, size_t len, void *cb_data)
+{
+ struct fwtty_transaction *txn = cb_data;
+ struct fwtty_port *port = txn->port;
+
+ if (port && rcode != RCODE_COMPLETE)
+ fwtty_log_tx_error(port, rcode);
+ if (txn->callback)
+ txn->callback(card, rcode, payload, len, txn);
+ kmem_cache_free(fwtty_txn_cache, txn);
+}
+
+static int fwtty_send_data_async(struct fwtty_peer *peer, int tcode,
+ unsigned long long addr, void *payload,
+ size_t len, fwtty_transaction_cb callback,
+ struct fwtty_port *port)
+{
+ struct fwtty_transaction *txn;
+ int generation;
+
+ txn = kmem_cache_alloc(fwtty_txn_cache, GFP_ATOMIC);
+ if (!txn)
+ return -ENOMEM;
+
+ txn->callback = callback;
+ txn->port = port;
+
+ generation = peer->generation;
+ smp_rmb();
+ fw_send_request(peer->serial->card, &txn->fw_txn, tcode,
+ peer->node_id, generation, peer->speed, addr, payload,
+ len, fwtty_common_callback, txn);
+ return 0;
+}
+
+static void fwtty_send_txn_async(struct fwtty_peer *peer,
+ struct fwtty_transaction *txn, int tcode,
+ unsigned long long addr, void *payload,
+ size_t len, fwtty_transaction_cb callback,
+ struct fwtty_port *port)
+{
+ int generation;
+
+ txn->callback = callback;
+ txn->port = port;
+
+ generation = peer->generation;
+ smp_rmb();
+ fw_send_request(peer->serial->card, &txn->fw_txn, tcode,
+ peer->node_id, generation, peer->speed, addr, payload,
+ len, fwtty_common_callback, txn);
+}
+
+static void __fwtty_restart_tx(struct fwtty_port *port)
+{
+ int len, avail;
+
+ len = dma_fifo_out_level(&port->tx_fifo);
+ if (len)
+ schedule_delayed_work(&port->drain, 0);
+ avail = dma_fifo_avail(&port->tx_fifo);
+
+ fwtty_dbg(port, "fifo len: %d avail: %d\n", len, avail);
+}
+
+static void fwtty_restart_tx(struct fwtty_port *port)
+{
+ spin_lock_bh(&port->lock);
+ __fwtty_restart_tx(port);
+ spin_unlock_bh(&port->lock);
+}
+
+/**
+ * fwtty_update_port_status - decodes & dispatches line status changes
+ *
+ * Note: in loopback, the port->lock is being held. Only use functions that
+ * don't attempt to reclaim the port->lock.
+ */
+static void fwtty_update_port_status(struct fwtty_port *port, unsigned status)
+{
+ unsigned delta;
+ struct tty_struct *tty;
+
+ /* simulated LSR/MSR status from remote */
+ status &= ~MCTRL_MASK;
+ delta = (port->mstatus ^ status) & ~MCTRL_MASK;
+ delta &= ~(status & TIOCM_RNG);
+ port->mstatus = status;
+
+ if (delta & TIOCM_RNG)
+ ++port->icount.rng;
+ if (delta & TIOCM_DSR)
+ ++port->icount.dsr;
+ if (delta & TIOCM_CAR)
+ ++port->icount.dcd;
+ if (delta & TIOCM_CTS)
+ ++port->icount.cts;
+
+ fwtty_dbg(port, "status: %x delta: %x\n", status, delta);
+
+ if (delta & TIOCM_CAR) {
+ tty = tty_port_tty_get(&port->port);
+ if (tty && !C_CLOCAL(tty)) {
+ if (status & TIOCM_CAR)
+ wake_up_interruptible(&port->port.open_wait);
+ else
+ schedule_work(&port->hangup);
+ }
+ tty_kref_put(tty);
+ }
+
+ if (delta & TIOCM_CTS) {
+ tty = tty_port_tty_get(&port->port);
+ if (tty && C_CRTSCTS(tty)) {
+ if (tty->hw_stopped) {
+ if (status & TIOCM_CTS) {
+ tty->hw_stopped = 0;
+ if (port->loopback)
+ __fwtty_restart_tx(port);
+ else
+ fwtty_restart_tx(port);
+ }
+ } else {
+ if (~status & TIOCM_CTS)
+ tty->hw_stopped = 1;
+ }
+ }
+ tty_kref_put(tty);
+
+ } else if (delta & OOB_TX_THROTTLE) {
+ tty = tty_port_tty_get(&port->port);
+ if (tty) {
+ if (tty->hw_stopped) {
+ if (~status & OOB_TX_THROTTLE) {
+ tty->hw_stopped = 0;
+ if (port->loopback)
+ __fwtty_restart_tx(port);
+ else
+ fwtty_restart_tx(port);
+ }
+ } else {
+ if (status & OOB_TX_THROTTLE)
+ tty->hw_stopped = 1;
+ }
+ }
+ tty_kref_put(tty);
+ }
+
+ if (delta & (UART_LSR_BI << 24)) {
+ if (status & (UART_LSR_BI << 24)) {
+ port->break_last = jiffies;
+ schedule_delayed_work(&port->emit_breaks, 0);
+ } else {
+ /* run emit_breaks one last time (if pending) */
+ mod_delayed_work(system_wq, &port->emit_breaks, 0);
+ }
+ }
+
+ if (delta & (TIOCM_DSR | TIOCM_CAR | TIOCM_CTS | TIOCM_RNG))
+ wake_up_interruptible(&port->port.delta_msr_wait);
+}
+
+/**
+ * __fwtty_port_line_status - generate 'line status' for indicated port
+ *
+ * This function returns a remote 'MSR' state based on the local 'MCR' state,
+ * as if a null modem cable was attached. The actual status is a mangling
+ * of TIOCM_* bits suitable for sending to a peer's status_addr.
+ *
+ * Note: caller must be holding port lock
+ */
+static unsigned __fwtty_port_line_status(struct fwtty_port *port)
+{
+ unsigned status = 0;
+
+ /* TODO: add module param to tie RNG to DTR as well */
+
+ if (port->mctrl & TIOCM_DTR)
+ status |= TIOCM_DSR | TIOCM_CAR;
+ if (port->mctrl & TIOCM_RTS)
+ status |= TIOCM_CTS;
+ if (port->mctrl & OOB_RX_THROTTLE)
+ status |= OOB_TX_THROTTLE;
+ /* emulate BRK as add'l line status */
+ if (port->break_ctl)
+ status |= UART_LSR_BI << 24;
+
+ return status;
+}
+
+/**
+ * __fwtty_write_port_status - send the port line status to peer
+ *
+ * Note: caller must be holding the port lock.
+ */
+static int __fwtty_write_port_status(struct fwtty_port *port)
+{
+ struct fwtty_peer *peer;
+ int err = -ENOENT;
+ unsigned status = __fwtty_port_line_status(port);
+
+ rcu_read_lock();
+ peer = rcu_dereference(port->peer);
+ if (peer) {
+ err = fwtty_send_data_async(peer, TCODE_WRITE_QUADLET_REQUEST,
+ peer->status_addr, &status,
+ sizeof(status), NULL, port);
+ }
+ rcu_read_unlock();
+
+ return err;
+}
+
+/**
+ * fwtty_write_port_status - same as above but locked by port lock
+ */
+static int fwtty_write_port_status(struct fwtty_port *port)
+{
+ int err;
+
+ spin_lock_bh(&port->lock);
+ err = __fwtty_write_port_status(port);
+ spin_unlock_bh(&port->lock);
+ return err;
+}
+
+static void fwtty_throttle_port(struct fwtty_port *port)
+{
+ struct tty_struct *tty;
+ unsigned old;
+
+ tty = tty_port_tty_get(&port->port);
+ if (!tty)
+ return;
+
+ spin_lock_bh(&port->lock);
+
+ old = port->mctrl;
+ port->mctrl |= OOB_RX_THROTTLE;
+ if (C_CRTSCTS(tty))
+ port->mctrl &= ~TIOCM_RTS;
+ if (~old & OOB_RX_THROTTLE)
+ __fwtty_write_port_status(port);
+
+ spin_unlock_bh(&port->lock);
+
+ tty_kref_put(tty);
+}
+
+/**
+ * fwtty_do_hangup - wait for ldisc to deliver all pending rx; only then hangup
+ *
+ * When the remote has finished tx, and all in-flight rx has been received and
+ * and pushed to the flip buffer, the remote may close its device. This will
+ * drop DTR on the remote which will drop carrier here. Typically, the tty is
+ * hung up when carrier is dropped or lost.
+ *
+ * However, there is a race between the hang up and the line discipline
+ * delivering its data to the reader. A hangup will cause the ldisc to flush
+ * (ie., clear) the read buffer and flip buffer. Because of firewire's
+ * relatively high throughput, the ldisc frequently lags well behind the driver,
+ * resulting in lost data (which has already been received and written to
+ * the flip buffer) when the remote closes its end.
+ *
+ * Unfortunately, since the flip buffer offers no direct method for determining
+ * if it holds data, ensuring the ldisc has delivered all data is problematic.
+ */
+
+/* FIXME: drop this workaround when __tty_hangup waits for ldisc completion */
+static void fwtty_do_hangup(struct work_struct *work)
+{
+ struct fwtty_port *port = to_port(work, hangup);
+ struct tty_struct *tty;
+
+ schedule_timeout_uninterruptible(msecs_to_jiffies(50));
+
+ tty = tty_port_tty_get(&port->port);
+ if (tty)
+ tty_vhangup(tty);
+ tty_kref_put(tty);
+}
+
+static void fwtty_emit_breaks(struct work_struct *work)
+{
+ struct fwtty_port *port = to_port(to_delayed_work(work), emit_breaks);
+ static const char buf[16];
+ unsigned long now = jiffies;
+ unsigned long elapsed = now - port->break_last;
+ int n, t, c, brk = 0;
+
+ /* generate breaks at the line rate (but at least 1) */
+ n = (elapsed * port->cps) / HZ + 1;
+ port->break_last = now;
+
+ fwtty_dbg(port, "sending %d brks\n", n);
+
+ while (n) {
+ t = min(n, 16);
+ c = tty_insert_flip_string_fixed_flag(&port->port, buf,
+ TTY_BREAK, t);
+ n -= c;
+ brk += c;
+ if (c < t)
+ break;
+ }
+ tty_flip_buffer_push(&port->port);
+
+ if (port->mstatus & (UART_LSR_BI << 24))
+ schedule_delayed_work(&port->emit_breaks, FREQ_BREAKS);
+ port->icount.brk += brk;
+}
+
+static int fwtty_rx(struct fwtty_port *port, unsigned char *data, size_t len)
+{
+ int c, n = len;
+ unsigned lsr;
+ int err = 0;
+
+ fwtty_dbg(port, "%d\n", n);
+ fwtty_profile_data(port->stats.reads, n);
+
+ if (port->write_only) {
+ n = 0;
+ goto out;
+ }
+
+ /* disregard break status; breaks are generated by emit_breaks work */
+ lsr = (port->mstatus >> 24) & ~UART_LSR_BI;
+
+ if (port->overrun)
+ lsr |= UART_LSR_OE;
+
+ if (lsr & UART_LSR_OE)
+ ++port->icount.overrun;
+
+ lsr &= port->status_mask;
+ if (lsr & ~port->ignore_mask & UART_LSR_OE) {
+ if (!tty_insert_flip_char(&port->port, 0, TTY_OVERRUN)) {
+ err = -EIO;
+ goto out;
+ }
+ }
+ port->overrun = false;
+
+ if (lsr & port->ignore_mask & ~UART_LSR_OE) {
+ /* TODO: don't drop SAK and Magic SysRq here */
+ n = 0;
+ goto out;
+ }
+
+ c = tty_insert_flip_string_fixed_flag(&port->port, data, TTY_NORMAL, n);
+ if (c > 0)
+ tty_flip_buffer_push(&port->port);
+ n -= c;
+
+ if (n) {
+ port->overrun = true;
+ err = -EIO;
+ fwtty_err_ratelimited(port, "flip buffer overrun\n");
+
+ } else {
+ /* throttle the sender if remaining flip buffer space has
+ * reached high watermark to avoid losing data which may be
+ * in-flight. Since the AR request context is 32k, that much
+ * data may have _already_ been acked.
+ */
+ if (tty_buffer_space_avail(&port->port) < HIGH_WATERMARK)
+ fwtty_throttle_port(port);
+ }
+
+out:
+ port->icount.rx += len;
+ port->stats.lost += n;
+ return err;
+}
+
+/**
+ * fwtty_port_handler - bus address handler for port reads/writes
+ * @parameters: fw_address_callback_t as specified by firewire core interface
+ *
+ * This handler is responsible for handling inbound read/write dma from remotes.
+ */
+static void fwtty_port_handler(struct fw_card *card,
+ struct fw_request *request,
+ int tcode, int destination, int source,
+ int generation,
+ unsigned long long addr,
+ void *data, size_t len,
+ void *callback_data)
+{
+ struct fwtty_port *port = callback_data;
+ struct fwtty_peer *peer;
+ int err;
+ int rcode;
+
+ /* Only accept rx from the peer virtual-cabled to this port */
+ rcu_read_lock();
+ peer = __fwserial_peer_by_node_id(card, generation, source);
+ rcu_read_unlock();
+ if (!peer || peer != rcu_access_pointer(port->peer)) {
+ rcode = RCODE_ADDRESS_ERROR;
+ fwtty_err_ratelimited(port, "ignoring unauthenticated data\n");
+ goto respond;
+ }
+
+ switch (tcode) {
+ case TCODE_WRITE_QUADLET_REQUEST:
+ if (addr != port->rx_handler.offset || len != 4) {
+ rcode = RCODE_ADDRESS_ERROR;
+ } else {
+ fwtty_update_port_status(port, *(unsigned *)data);
+ rcode = RCODE_COMPLETE;
+ }
+ break;
+
+ case TCODE_WRITE_BLOCK_REQUEST:
+ if (addr != port->rx_handler.offset + 4 ||
+ len > port->rx_handler.length - 4) {
+ rcode = RCODE_ADDRESS_ERROR;
+ } else {
+ err = fwtty_rx(port, data, len);
+ switch (err) {
+ case 0:
+ rcode = RCODE_COMPLETE;
+ break;
+ case -EIO:
+ rcode = RCODE_DATA_ERROR;
+ break;
+ default:
+ rcode = RCODE_CONFLICT_ERROR;
+ break;
+ }
+ }
+ break;
+
+ default:
+ rcode = RCODE_TYPE_ERROR;
+ }
+
+respond:
+ fw_send_response(card, request, rcode);
+}
+
+/**
+ * fwtty_tx_complete - callback for tx dma
+ * @data: ignored, has no meaning for write txns
+ * @length: ignored, has no meaning for write txns
+ *
+ * The writer must be woken here if the fifo has been emptied because it
+ * may have slept if chars_in_buffer was != 0
+ */
+static void fwtty_tx_complete(struct fw_card *card, int rcode,
+ void *data, size_t length,
+ struct fwtty_transaction *txn)
+{
+ struct fwtty_port *port = txn->port;
+ int len;
+
+ fwtty_dbg(port, "rcode: %d\n", rcode);
+
+ switch (rcode) {
+ case RCODE_COMPLETE:
+ spin_lock_bh(&port->lock);
+ dma_fifo_out_complete(&port->tx_fifo, &txn->dma_pended);
+ len = dma_fifo_level(&port->tx_fifo);
+ spin_unlock_bh(&port->lock);
+
+ port->icount.tx += txn->dma_pended.len;
+ break;
+
+ default:
+ /* TODO: implement retries */
+ spin_lock_bh(&port->lock);
+ dma_fifo_out_complete(&port->tx_fifo, &txn->dma_pended);
+ len = dma_fifo_level(&port->tx_fifo);
+ spin_unlock_bh(&port->lock);
+
+ port->stats.dropped += txn->dma_pended.len;
+ }
+
+ if (len < WAKEUP_CHARS)
+ tty_port_tty_wakeup(&port->port);
+}
+
+static int fwtty_tx(struct fwtty_port *port, bool drain)
+{
+ struct fwtty_peer *peer;
+ struct fwtty_transaction *txn;
+ struct tty_struct *tty;
+ int n, len;
+
+ tty = tty_port_tty_get(&port->port);
+ if (!tty)
+ return -ENOENT;
+
+ rcu_read_lock();
+ peer = rcu_dereference(port->peer);
+ if (!peer) {
+ n = -EIO;
+ goto out;
+ }
+
+ if (test_and_set_bit(IN_TX, &port->flags)) {
+ n = -EALREADY;
+ goto out;
+ }
+
+ /* try to write as many dma transactions out as possible */
+ n = -EAGAIN;
+ while (!tty->stopped && !tty->hw_stopped &&
+ !test_bit(STOP_TX, &port->flags)) {
+ txn = kmem_cache_alloc(fwtty_txn_cache, GFP_ATOMIC);
+ if (!txn) {
+ n = -ENOMEM;
+ break;
+ }
+
+ spin_lock_bh(&port->lock);
+ n = dma_fifo_out_pend(&port->tx_fifo, &txn->dma_pended);
+ spin_unlock_bh(&port->lock);
+
+ fwtty_dbg(port, "out: %u rem: %d\n", txn->dma_pended.len, n);
+
+ if (n < 0) {
+ kmem_cache_free(fwtty_txn_cache, txn);
+ if (n == -EAGAIN) {
+ ++port->stats.tx_stall;
+ } else if (n == -ENODATA) {
+ fwtty_profile_data(port->stats.txns, 0);
+ } else {
+ ++port->stats.fifo_errs;
+ fwtty_err_ratelimited(port, "fifo err: %d\n",
+ n);
+ }
+ break;
+ }
+
+ fwtty_profile_data(port->stats.txns, txn->dma_pended.len);
+
+ fwtty_send_txn_async(peer, txn, TCODE_WRITE_BLOCK_REQUEST,
+ peer->fifo_addr, txn->dma_pended.data,
+ txn->dma_pended.len, fwtty_tx_complete,
+ port);
+ ++port->stats.sent;
+
+ /*
+ * Stop tx if the 'last view' of the fifo is empty or if
+ * this is the writer and there's not enough data to bother
+ */
+ if (n == 0 || (!drain && n < WRITER_MINIMUM))
+ break;
+ }
+
+ if (n >= 0 || n == -EAGAIN || n == -ENOMEM || n == -ENODATA) {
+ spin_lock_bh(&port->lock);
+ len = dma_fifo_out_level(&port->tx_fifo);
+ if (len) {
+ unsigned long delay = (n == -ENOMEM) ? HZ : 1;
+
+ schedule_delayed_work(&port->drain, delay);
+ }
+ len = dma_fifo_level(&port->tx_fifo);
+ spin_unlock_bh(&port->lock);
+
+ /* wakeup the writer */
+ if (drain && len < WAKEUP_CHARS)
+ tty_wakeup(tty);
+ }
+
+ clear_bit(IN_TX, &port->flags);
+ wake_up_interruptible(&port->wait_tx);
+
+out:
+ rcu_read_unlock();
+ tty_kref_put(tty);
+ return n;
+}
+
+static void fwtty_drain_tx(struct work_struct *work)
+{
+ struct fwtty_port *port = to_port(to_delayed_work(work), drain);
+
+ fwtty_tx(port, true);
+}
+
+static void fwtty_write_xchar(struct fwtty_port *port, char ch)
+{
+ struct fwtty_peer *peer;
+
+ ++port->stats.xchars;
+
+ fwtty_dbg(port, "%02x\n", ch);
+
+ rcu_read_lock();
+ peer = rcu_dereference(port->peer);
+ if (peer) {
+ fwtty_send_data_async(peer, TCODE_WRITE_BLOCK_REQUEST,
+ peer->fifo_addr, &ch, sizeof(ch),
+ NULL, port);
+ }
+ rcu_read_unlock();
+}
+
+struct fwtty_port *fwtty_port_get(unsigned index)
+{
+ struct fwtty_port *port;
+
+ if (index >= MAX_TOTAL_PORTS)
+ return NULL;
+
+ mutex_lock(&port_table_lock);
+ port = port_table[index];
+ if (port)
+ kref_get(&port->serial->kref);
+ mutex_unlock(&port_table_lock);
+ return port;
+}
+EXPORT_SYMBOL(fwtty_port_get);
+
+static int fwtty_ports_add(struct fw_serial *serial)
+{
+ int err = -EBUSY;
+ int i, j;
+
+ if (port_table_corrupt)
+ return err;
+
+ mutex_lock(&port_table_lock);
+ for (i = 0; i + num_ports <= MAX_TOTAL_PORTS; i += num_ports) {
+ if (!port_table[i]) {
+ for (j = 0; j < num_ports; ++i, ++j) {
+ serial->ports[j]->index = i;
+ port_table[i] = serial->ports[j];
+ }
+ err = 0;
+ break;
+ }
+ }
+ mutex_unlock(&port_table_lock);
+ return err;
+}
+
+static void fwserial_destroy(struct kref *kref)
+{
+ struct fw_serial *serial = to_serial(kref, kref);
+ struct fwtty_port **ports = serial->ports;
+ int j, i = ports[0]->index;
+
+ synchronize_rcu();
+
+ mutex_lock(&port_table_lock);
+ for (j = 0; j < num_ports; ++i, ++j) {
+ port_table_corrupt |= port_table[i] != ports[j];
+ WARN_ONCE(port_table_corrupt, "port_table[%d]: %p != ports[%d]: %p",
+ i, port_table[i], j, ports[j]);
+
+ port_table[i] = NULL;
+ }
+ mutex_unlock(&port_table_lock);
+
+ for (j = 0; j < num_ports; ++j) {
+ fw_core_remove_address_handler(&ports[j]->rx_handler);
+ tty_port_destroy(&ports[j]->port);
+ kfree(ports[j]);
+ }
+ kfree(serial);
+}
+
+void fwtty_port_put(struct fwtty_port *port)
+{
+ kref_put(&port->serial->kref, fwserial_destroy);
+}
+EXPORT_SYMBOL(fwtty_port_put);
+
+static void fwtty_port_dtr_rts(struct tty_port *tty_port, int on)
+{
+ struct fwtty_port *port = to_port(tty_port, port);
+
+ fwtty_dbg(port, "on/off: %d\n", on);
+
+ spin_lock_bh(&port->lock);
+ /* Don't change carrier state if this is a console */
+ if (!port->port.console) {
+ if (on)
+ port->mctrl |= TIOCM_DTR | TIOCM_RTS;
+ else
+ port->mctrl &= ~(TIOCM_DTR | TIOCM_RTS);
+ }
+
+ __fwtty_write_port_status(port);
+ spin_unlock_bh(&port->lock);
+}
+
+/**
+ * fwtty_port_carrier_raised: required tty_port operation
+ *
+ * This port operation is polled after a tty has been opened and is waiting for
+ * carrier detect -- see drivers/tty/tty_port:tty_port_block_til_ready().
+ */
+static int fwtty_port_carrier_raised(struct tty_port *tty_port)
+{
+ struct fwtty_port *port = to_port(tty_port, port);
+ int rc;
+
+ rc = (port->mstatus & TIOCM_CAR);
+
+ fwtty_dbg(port, "%d\n", rc);
+
+ return rc;
+}
+
+static unsigned set_termios(struct fwtty_port *port, struct tty_struct *tty)
+{
+ unsigned baud, frame;
+
+ baud = tty_termios_baud_rate(&tty->termios);
+ tty_termios_encode_baud_rate(&tty->termios, baud, baud);
+
+ /* compute bit count of 2 frames */
+ frame = 12 + ((C_CSTOPB(tty)) ? 4 : 2) + ((C_PARENB(tty)) ? 2 : 0);
+
+ switch (C_CSIZE(tty)) {
+ case CS5:
+ frame -= (C_CSTOPB(tty)) ? 1 : 0;
+ break;
+ case CS6:
+ frame += 2;
+ break;
+ case CS7:
+ frame += 4;
+ break;
+ case CS8:
+ frame += 6;
+ break;
+ }
+
+ port->cps = (baud << 1) / frame;
+
+ port->status_mask = UART_LSR_OE;
+ if (_I_FLAG(tty, BRKINT | PARMRK))
+ port->status_mask |= UART_LSR_BI;
+
+ port->ignore_mask = 0;
+ if (I_IGNBRK(tty)) {
+ port->ignore_mask |= UART_LSR_BI;
+ if (I_IGNPAR(tty))
+ port->ignore_mask |= UART_LSR_OE;
+ }
+
+ port->write_only = !C_CREAD(tty);
+
+ /* turn off echo and newline xlat if loopback */
+ if (port->loopback) {
+ tty->termios.c_lflag &= ~(ECHO | ECHOE | ECHOK | ECHOKE |
+ ECHONL | ECHOPRT | ECHOCTL);
+ tty->termios.c_oflag &= ~ONLCR;
+ }
+
+ return baud;
+}
+
+static int fwtty_port_activate(struct tty_port *tty_port,
+ struct tty_struct *tty)
+{
+ struct fwtty_port *port = to_port(tty_port, port);
+ unsigned baud;
+ int err;
+
+ set_bit(TTY_IO_ERROR, &tty->flags);
+
+ err = dma_fifo_alloc(&port->tx_fifo, FWTTY_PORT_TXFIFO_LEN,
+ cache_line_size(),
+ port->max_payload,
+ FWTTY_PORT_MAX_PEND_DMA,
+ GFP_KERNEL);
+ if (err)
+ return err;
+
+ spin_lock_bh(&port->lock);
+
+ baud = set_termios(port, tty);
+
+ /* if console, don't change carrier state */
+ if (!port->port.console) {
+ port->mctrl = 0;
+ if (baud != 0)
+ port->mctrl = TIOCM_DTR | TIOCM_RTS;
+ }
+
+ if (C_CRTSCTS(tty) && ~port->mstatus & TIOCM_CTS)
+ tty->hw_stopped = 1;
+
+ __fwtty_write_port_status(port);
+ spin_unlock_bh(&port->lock);
+
+ clear_bit(TTY_IO_ERROR, &tty->flags);
+
+ return 0;
+}
+
+/**
+ * fwtty_port_shutdown
+ *
+ * Note: the tty port core ensures this is not the console and
+ * manages TTY_IO_ERROR properly
+ */
+static void fwtty_port_shutdown(struct tty_port *tty_port)
+{
+ struct fwtty_port *port = to_port(tty_port, port);
+
+ /* TODO: cancel outstanding transactions */
+
+ cancel_delayed_work_sync(&port->emit_breaks);
+ cancel_delayed_work_sync(&port->drain);
+
+ spin_lock_bh(&port->lock);
+ port->flags = 0;
+ port->break_ctl = 0;
+ port->overrun = 0;
+ __fwtty_write_port_status(port);
+ dma_fifo_free(&port->tx_fifo);
+ spin_unlock_bh(&port->lock);
+}
+
+static int fwtty_open(struct tty_struct *tty, struct file *fp)
+{
+ struct fwtty_port *port = tty->driver_data;
+
+ return tty_port_open(&port->port, tty, fp);
+}
+
+static void fwtty_close(struct tty_struct *tty, struct file *fp)
+{
+ struct fwtty_port *port = tty->driver_data;
+
+ tty_port_close(&port->port, tty, fp);
+}
+
+static void fwtty_hangup(struct tty_struct *tty)
+{
+ struct fwtty_port *port = tty->driver_data;
+
+ tty_port_hangup(&port->port);
+}
+
+static void fwtty_cleanup(struct tty_struct *tty)
+{
+ struct fwtty_port *port = tty->driver_data;
+
+ tty->driver_data = NULL;
+ fwtty_port_put(port);
+}
+
+static int fwtty_install(struct tty_driver *driver, struct tty_struct *tty)
+{
+ struct fwtty_port *port = fwtty_port_get(tty->index);
+ int err;
+
+ err = tty_standard_install(driver, tty);
+ if (!err)
+ tty->driver_data = port;
+ else
+ fwtty_port_put(port);
+ return err;
+}
+
+static int fwloop_install(struct tty_driver *driver, struct tty_struct *tty)
+{
+ struct fwtty_port *port = fwtty_port_get(table_idx(tty->index));
+ int err;
+
+ err = tty_standard_install(driver, tty);
+ if (!err)
+ tty->driver_data = port;
+ else
+ fwtty_port_put(port);
+ return err;
+}
+
+static int fwtty_write(struct tty_struct *tty, const unsigned char *buf, int c)
+{
+ struct fwtty_port *port = tty->driver_data;
+ int n, len;
+
+ fwtty_dbg(port, "%d\n", c);
+ fwtty_profile_data(port->stats.writes, c);
+
+ spin_lock_bh(&port->lock);
+ n = dma_fifo_in(&port->tx_fifo, buf, c);
+ len = dma_fifo_out_level(&port->tx_fifo);
+ if (len < DRAIN_THRESHOLD)
+ schedule_delayed_work(&port->drain, 1);
+ spin_unlock_bh(&port->lock);
+
+ if (len >= DRAIN_THRESHOLD)
+ fwtty_tx(port, false);
+
+ debug_short_write(port, c, n);
+
+ return (n < 0) ? 0 : n;
+}
+
+static int fwtty_write_room(struct tty_struct *tty)
+{
+ struct fwtty_port *port = tty->driver_data;
+ int n;
+
+ spin_lock_bh(&port->lock);
+ n = dma_fifo_avail(&port->tx_fifo);
+ spin_unlock_bh(&port->lock);
+
+ fwtty_dbg(port, "%d\n", n);
+
+ return n;
+}
+
+static int fwtty_chars_in_buffer(struct tty_struct *tty)
+{
+ struct fwtty_port *port = tty->driver_data;
+ int n;
+
+ spin_lock_bh(&port->lock);
+ n = dma_fifo_level(&port->tx_fifo);
+ spin_unlock_bh(&port->lock);
+
+ fwtty_dbg(port, "%d\n", n);
+
+ return n;
+}
+
+static void fwtty_send_xchar(struct tty_struct *tty, char ch)
+{
+ struct fwtty_port *port = tty->driver_data;
+
+ fwtty_dbg(port, "%02x\n", ch);
+
+ fwtty_write_xchar(port, ch);
+}
+
+static void fwtty_throttle(struct tty_struct *tty)
+{
+ struct fwtty_port *port = tty->driver_data;
+
+ /*
+ * Ignore throttling (but not unthrottling).
+ * It only makes sense to throttle when data will no longer be
+ * accepted by the tty flip buffer. For example, it is
+ * possible for received data to overflow the tty buffer long
+ * before the line discipline ever has a chance to throttle the driver.
+ * Additionally, the driver may have already completed the I/O
+ * but the tty buffer is still emptying, so the line discipline is
+ * throttling and unthrottling nothing.
+ */
+
+ ++port->stats.throttled;
+}
+
+static void fwtty_unthrottle(struct tty_struct *tty)
+{
+ struct fwtty_port *port = tty->driver_data;
+
+ fwtty_dbg(port, "CRTSCTS: %d\n", C_CRTSCTS(tty) != 0);
+
+ fwtty_profile_fifo(port, port->stats.unthrottle);
+
+ spin_lock_bh(&port->lock);
+ port->mctrl &= ~OOB_RX_THROTTLE;
+ if (C_CRTSCTS(tty))
+ port->mctrl |= TIOCM_RTS;
+ __fwtty_write_port_status(port);
+ spin_unlock_bh(&port->lock);
+}
+
+static int check_msr_delta(struct fwtty_port *port, unsigned long mask,
+ struct async_icount *prev)
+{
+ struct async_icount now;
+ int delta;
+
+ now = port->icount;
+
+ delta = ((mask & TIOCM_RNG && prev->rng != now.rng) ||
+ (mask & TIOCM_DSR && prev->dsr != now.dsr) ||
+ (mask & TIOCM_CAR && prev->dcd != now.dcd) ||
+ (mask & TIOCM_CTS && prev->cts != now.cts));
+
+ *prev = now;
+
+ return delta;
+}
+
+static int wait_msr_change(struct fwtty_port *port, unsigned long mask)
+{
+ struct async_icount prev;
+
+ prev = port->icount;
+
+ return wait_event_interruptible(port->port.delta_msr_wait,
+ check_msr_delta(port, mask, &prev));
+}
+
+static int get_serial_info(struct fwtty_port *port,
+ struct serial_struct __user *info)
+{
+ struct serial_struct tmp;
+
+ memset(&tmp, 0, sizeof(tmp));
+
+ tmp.type = PORT_UNKNOWN;
+ tmp.line = port->port.tty->index;
+ tmp.flags = port->port.flags;
+ tmp.xmit_fifo_size = FWTTY_PORT_TXFIFO_LEN;
+ tmp.baud_base = 400000000;
+ tmp.close_delay = port->port.close_delay;
+
+ return (copy_to_user(info, &tmp, sizeof(*info))) ? -EFAULT : 0;
+}
+
+static int set_serial_info(struct fwtty_port *port,
+ struct serial_struct __user *info)
+{
+ struct serial_struct tmp;
+
+ if (copy_from_user(&tmp, info, sizeof(tmp)))
+ return -EFAULT;
+
+ if (tmp.irq != 0 || tmp.port != 0 || tmp.custom_divisor != 0 ||
+ tmp.baud_base != 400000000)
+ return -EPERM;
+
+ if (!capable(CAP_SYS_ADMIN)) {
+ if (((tmp.flags & ~ASYNC_USR_MASK) !=
+ (port->port.flags & ~ASYNC_USR_MASK)))
+ return -EPERM;
+ } else {
+ port->port.close_delay = tmp.close_delay * HZ / 100;
+ }
+
+ return 0;
+}
+
+static int fwtty_ioctl(struct tty_struct *tty, unsigned cmd,
+ unsigned long arg)
+{
+ struct fwtty_port *port = tty->driver_data;
+ int err;
+
+ switch (cmd) {
+ case TIOCGSERIAL:
+ mutex_lock(&port->port.mutex);
+ err = get_serial_info(port, (void __user *)arg);
+ mutex_unlock(&port->port.mutex);
+ break;
+
+ case TIOCSSERIAL:
+ mutex_lock(&port->port.mutex);
+ err = set_serial_info(port, (void __user *)arg);
+ mutex_unlock(&port->port.mutex);
+ break;
+
+ case TIOCMIWAIT:
+ err = wait_msr_change(port, arg);
+ break;
+
+ default:
+ err = -ENOIOCTLCMD;
+ }
+
+ return err;
+}
+
+static void fwtty_set_termios(struct tty_struct *tty, struct ktermios *old)
+{
+ struct fwtty_port *port = tty->driver_data;
+ unsigned baud;
+
+ spin_lock_bh(&port->lock);
+ baud = set_termios(port, tty);
+
+ if ((baud == 0) && (old->c_cflag & CBAUD)) {
+ port->mctrl &= ~(TIOCM_DTR | TIOCM_RTS);
+ } else if ((baud != 0) && !(old->c_cflag & CBAUD)) {
+ if (C_CRTSCTS(tty) || !test_bit(TTY_THROTTLED, &tty->flags))
+ port->mctrl |= TIOCM_DTR | TIOCM_RTS;
+ else
+ port->mctrl |= TIOCM_DTR;
+ }
+ __fwtty_write_port_status(port);
+ spin_unlock_bh(&port->lock);
+
+ if (old->c_cflag & CRTSCTS) {
+ if (!C_CRTSCTS(tty)) {
+ tty->hw_stopped = 0;
+ fwtty_restart_tx(port);
+ }
+ } else if (C_CRTSCTS(tty) && ~port->mstatus & TIOCM_CTS) {
+ tty->hw_stopped = 1;
+ }
+}
+
+/**
+ * fwtty_break_ctl - start/stop sending breaks
+ *
+ * Signals the remote to start or stop generating simulated breaks.
+ * First, stop dequeueing from the fifo and wait for writer/drain to leave tx
+ * before signalling the break line status. This guarantees any pending rx will
+ * be queued to the line discipline before break is simulated on the remote.
+ * Conversely, turning off break_ctl requires signalling the line status change,
+ * then enabling tx.
+ */
+static int fwtty_break_ctl(struct tty_struct *tty, int state)
+{
+ struct fwtty_port *port = tty->driver_data;
+ long ret;
+
+ fwtty_dbg(port, "%d\n", state);
+
+ if (state == -1) {
+ set_bit(STOP_TX, &port->flags);
+ ret = wait_event_interruptible_timeout(port->wait_tx,
+ !test_bit(IN_TX, &port->flags),
+ 10);
+ if (ret == 0 || ret == -ERESTARTSYS) {
+ clear_bit(STOP_TX, &port->flags);
+ fwtty_restart_tx(port);
+ return -EINTR;
+ }
+ }
+
+ spin_lock_bh(&port->lock);
+ port->break_ctl = (state == -1);
+ __fwtty_write_port_status(port);
+ spin_unlock_bh(&port->lock);
+
+ if (state == 0) {
+ spin_lock_bh(&port->lock);
+ dma_fifo_reset(&port->tx_fifo);
+ clear_bit(STOP_TX, &port->flags);
+ spin_unlock_bh(&port->lock);
+ }
+ return 0;
+}
+
+static int fwtty_tiocmget(struct tty_struct *tty)
+{
+ struct fwtty_port *port = tty->driver_data;
+ unsigned tiocm;
+
+ spin_lock_bh(&port->lock);
+ tiocm = (port->mctrl & MCTRL_MASK) | (port->mstatus & ~MCTRL_MASK);
+ spin_unlock_bh(&port->lock);
+
+ fwtty_dbg(port, "%x\n", tiocm);
+
+ return tiocm;
+}
+
+static int fwtty_tiocmset(struct tty_struct *tty, unsigned set, unsigned clear)
+{
+ struct fwtty_port *port = tty->driver_data;
+
+ fwtty_dbg(port, "set: %x clear: %x\n", set, clear);
+
+ /* TODO: simulate loopback if TIOCM_LOOP set */
+
+ spin_lock_bh(&port->lock);
+ port->mctrl &= ~(clear & MCTRL_MASK & 0xffff);
+ port->mctrl |= set & MCTRL_MASK & 0xffff;
+ __fwtty_write_port_status(port);
+ spin_unlock_bh(&port->lock);
+ return 0;
+}
+
+static int fwtty_get_icount(struct tty_struct *tty,
+ struct serial_icounter_struct *icount)
+{
+ struct fwtty_port *port = tty->driver_data;
+ struct stats stats;
+
+ memcpy(&stats, &port->stats, sizeof(stats));
+ if (port->port.console)
+ (*port->fwcon_ops->stats)(&stats, port->con_data);
+
+ icount->cts = port->icount.cts;
+ icount->dsr = port->icount.dsr;
+ icount->rng = port->icount.rng;
+ icount->dcd = port->icount.dcd;
+ icount->rx = port->icount.rx;
+ icount->tx = port->icount.tx + stats.xchars;
+ icount->frame = port->icount.frame;
+ icount->overrun = port->icount.overrun;
+ icount->parity = port->icount.parity;
+ icount->brk = port->icount.brk;
+ icount->buf_overrun = port->icount.overrun;
+ return 0;
+}
+
+static void fwtty_proc_show_port(struct seq_file *m, struct fwtty_port *port)
+{
+ struct stats stats;
+
+ memcpy(&stats, &port->stats, sizeof(stats));
+ if (port->port.console)
+ (*port->fwcon_ops->stats)(&stats, port->con_data);
+
+ seq_printf(m, " addr:%012llx tx:%d rx:%d", port->rx_handler.offset,
+ port->icount.tx + stats.xchars, port->icount.rx);
+ seq_printf(m, " cts:%d dsr:%d rng:%d dcd:%d", port->icount.cts,
+ port->icount.dsr, port->icount.rng, port->icount.dcd);
+ seq_printf(m, " fe:%d oe:%d pe:%d brk:%d", port->icount.frame,
+ port->icount.overrun, port->icount.parity, port->icount.brk);
+}
+
+static void fwtty_debugfs_show_port(struct seq_file *m, struct fwtty_port *port)
+{
+ struct stats stats;
+
+ memcpy(&stats, &port->stats, sizeof(stats));
+ if (port->port.console)
+ (*port->fwcon_ops->stats)(&stats, port->con_data);
+
+ seq_printf(m, " dr:%d st:%d err:%d lost:%d", stats.dropped,
+ stats.tx_stall, stats.fifo_errs, stats.lost);
+ seq_printf(m, " pkts:%d thr:%d", stats.sent, stats.throttled);
+
+ if (port->port.console) {
+ seq_puts(m, "\n ");
+ (*port->fwcon_ops->proc_show)(m, port->con_data);
+ }
+
+ fwtty_dump_profile(m, &port->stats);
+}
+
+static void fwtty_debugfs_show_peer(struct seq_file *m, struct fwtty_peer *peer)
+{
+ int generation = peer->generation;
+
+ smp_rmb();
+ seq_printf(m, " %s:", dev_name(&peer->unit->device));
+ seq_printf(m, " node:%04x gen:%d", peer->node_id, generation);
+ seq_printf(m, " sp:%d max:%d guid:%016llx", peer->speed,
+ peer->max_payload, (unsigned long long) peer->guid);
+ seq_printf(m, " mgmt:%012llx", (unsigned long long) peer->mgmt_addr);
+ seq_printf(m, " addr:%012llx", (unsigned long long) peer->status_addr);
+ seq_putc(m, '\n');
+}
+
+static int fwtty_proc_show(struct seq_file *m, void *v)
+{
+ struct fwtty_port *port;
+ int i;
+
+ seq_puts(m, "fwserinfo: 1.0 driver: 1.0\n");
+ for (i = 0; i < MAX_TOTAL_PORTS && (port = fwtty_port_get(i)); ++i) {
+ seq_printf(m, "%2d:", i);
+ if (capable(CAP_SYS_ADMIN))
+ fwtty_proc_show_port(m, port);
+ fwtty_port_put(port);
+ seq_puts(m, "\n");
+ }
+ return 0;
+}
+
+static int fwtty_debugfs_stats_show(struct seq_file *m, void *v)
+{
+ struct fw_serial *serial = m->private;
+ struct fwtty_port *port;
+ int i;
+
+ for (i = 0; i < num_ports; ++i) {
+ port = fwtty_port_get(serial->ports[i]->index);
+ if (port) {
+ seq_printf(m, "%2d:", port->index);
+ fwtty_proc_show_port(m, port);
+ fwtty_debugfs_show_port(m, port);
+ fwtty_port_put(port);
+ seq_puts(m, "\n");
+ }
+ }
+ return 0;
+}
+
+static int fwtty_debugfs_peers_show(struct seq_file *m, void *v)
+{
+ struct fw_serial *serial = m->private;
+ struct fwtty_peer *peer;
+
+ rcu_read_lock();
+ seq_printf(m, "card: %s guid: %016llx\n",
+ dev_name(serial->card->device),
+ (unsigned long long) serial->card->guid);
+ list_for_each_entry_rcu(peer, &serial->peer_list, list)
+ fwtty_debugfs_show_peer(m, peer);
+ rcu_read_unlock();
+ return 0;
+}
+
+static int fwtty_proc_open(struct inode *inode, struct file *fp)
+{
+ return single_open(fp, fwtty_proc_show, NULL);
+}
+
+static int fwtty_stats_open(struct inode *inode, struct file *fp)
+{
+ return single_open(fp, fwtty_debugfs_stats_show, inode->i_private);
+}
+
+static int fwtty_peers_open(struct inode *inode, struct file *fp)
+{
+ return single_open(fp, fwtty_debugfs_peers_show, inode->i_private);
+}
+
+static const struct file_operations fwtty_stats_fops = {
+ .owner = THIS_MODULE,
+ .open = fwtty_stats_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static const struct file_operations fwtty_peers_fops = {
+ .owner = THIS_MODULE,
+ .open = fwtty_peers_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static const struct file_operations fwtty_proc_fops = {
+ .owner = THIS_MODULE,
+ .open = fwtty_proc_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static const struct tty_port_operations fwtty_port_ops = {
+ .dtr_rts = fwtty_port_dtr_rts,
+ .carrier_raised = fwtty_port_carrier_raised,
+ .shutdown = fwtty_port_shutdown,
+ .activate = fwtty_port_activate,
+};
+
+static const struct tty_operations fwtty_ops = {
+ .open = fwtty_open,
+ .close = fwtty_close,
+ .hangup = fwtty_hangup,
+ .cleanup = fwtty_cleanup,
+ .install = fwtty_install,
+ .write = fwtty_write,
+ .write_room = fwtty_write_room,
+ .chars_in_buffer = fwtty_chars_in_buffer,
+ .send_xchar = fwtty_send_xchar,
+ .throttle = fwtty_throttle,
+ .unthrottle = fwtty_unthrottle,
+ .ioctl = fwtty_ioctl,
+ .set_termios = fwtty_set_termios,
+ .break_ctl = fwtty_break_ctl,
+ .tiocmget = fwtty_tiocmget,
+ .tiocmset = fwtty_tiocmset,
+ .get_icount = fwtty_get_icount,
+ .proc_fops = &fwtty_proc_fops,
+};
+
+static const struct tty_operations fwloop_ops = {
+ .open = fwtty_open,
+ .close = fwtty_close,
+ .hangup = fwtty_hangup,
+ .cleanup = fwtty_cleanup,
+ .install = fwloop_install,
+ .write = fwtty_write,
+ .write_room = fwtty_write_room,
+ .chars_in_buffer = fwtty_chars_in_buffer,
+ .send_xchar = fwtty_send_xchar,
+ .throttle = fwtty_throttle,
+ .unthrottle = fwtty_unthrottle,
+ .ioctl = fwtty_ioctl,
+ .set_termios = fwtty_set_termios,
+ .break_ctl = fwtty_break_ctl,
+ .tiocmget = fwtty_tiocmget,
+ .tiocmset = fwtty_tiocmset,
+ .get_icount = fwtty_get_icount,
+};
+
+static inline int mgmt_pkt_expected_len(__be16 code)
+{
+ static const struct fwserial_mgmt_pkt pkt;
+
+ switch (be16_to_cpu(code)) {
+ case FWSC_VIRT_CABLE_PLUG:
+ return sizeof(pkt.hdr) + sizeof(pkt.plug_req);
+
+ case FWSC_VIRT_CABLE_PLUG_RSP: /* | FWSC_RSP_OK */
+ return sizeof(pkt.hdr) + sizeof(pkt.plug_rsp);
+
+ case FWSC_VIRT_CABLE_UNPLUG:
+ case FWSC_VIRT_CABLE_UNPLUG_RSP:
+ case FWSC_VIRT_CABLE_PLUG_RSP | FWSC_RSP_NACK:
+ case FWSC_VIRT_CABLE_UNPLUG_RSP | FWSC_RSP_NACK:
+ return sizeof(pkt.hdr);
+
+ default:
+ return -1;
+ }
+}
+
+static inline void fill_plug_params(struct virt_plug_params *params,
+ struct fwtty_port *port)
+{
+ u64 status_addr = port->rx_handler.offset;
+ u64 fifo_addr = port->rx_handler.offset + 4;
+ size_t fifo_len = port->rx_handler.length - 4;
+
+ params->status_hi = cpu_to_be32(status_addr >> 32);
+ params->status_lo = cpu_to_be32(status_addr);
+ params->fifo_hi = cpu_to_be32(fifo_addr >> 32);
+ params->fifo_lo = cpu_to_be32(fifo_addr);
+ params->fifo_len = cpu_to_be32(fifo_len);
+}
+
+static inline void fill_plug_req(struct fwserial_mgmt_pkt *pkt,
+ struct fwtty_port *port)
+{
+ pkt->hdr.code = cpu_to_be16(FWSC_VIRT_CABLE_PLUG);
+ pkt->hdr.len = cpu_to_be16(mgmt_pkt_expected_len(pkt->hdr.code));
+ fill_plug_params(&pkt->plug_req, port);
+}
+
+static inline void fill_plug_rsp_ok(struct fwserial_mgmt_pkt *pkt,
+ struct fwtty_port *port)
+{
+ pkt->hdr.code = cpu_to_be16(FWSC_VIRT_CABLE_PLUG_RSP);
+ pkt->hdr.len = cpu_to_be16(mgmt_pkt_expected_len(pkt->hdr.code));
+ fill_plug_params(&pkt->plug_rsp, port);
+}
+
+static inline void fill_plug_rsp_nack(struct fwserial_mgmt_pkt *pkt)
+{
+ pkt->hdr.code = cpu_to_be16(FWSC_VIRT_CABLE_PLUG_RSP | FWSC_RSP_NACK);
+ pkt->hdr.len = cpu_to_be16(mgmt_pkt_expected_len(pkt->hdr.code));
+}
+
+static inline void fill_unplug_req(struct fwserial_mgmt_pkt *pkt)
+{
+ pkt->hdr.code = cpu_to_be16(FWSC_VIRT_CABLE_UNPLUG);
+ pkt->hdr.len = cpu_to_be16(mgmt_pkt_expected_len(pkt->hdr.code));
+}
+
+static inline void fill_unplug_rsp_nack(struct fwserial_mgmt_pkt *pkt)
+{
+ pkt->hdr.code = cpu_to_be16(FWSC_VIRT_CABLE_UNPLUG_RSP | FWSC_RSP_NACK);
+ pkt->hdr.len = cpu_to_be16(mgmt_pkt_expected_len(pkt->hdr.code));
+}
+
+static inline void fill_unplug_rsp_ok(struct fwserial_mgmt_pkt *pkt)
+{
+ pkt->hdr.code = cpu_to_be16(FWSC_VIRT_CABLE_UNPLUG_RSP);
+ pkt->hdr.len = cpu_to_be16(mgmt_pkt_expected_len(pkt->hdr.code));
+}
+
+static void fwserial_virt_plug_complete(struct fwtty_peer *peer,
+ struct virt_plug_params *params)
+{
+ struct fwtty_port *port = peer->port;
+
+ peer->status_addr = be32_to_u64(params->status_hi, params->status_lo);
+ peer->fifo_addr = be32_to_u64(params->fifo_hi, params->fifo_lo);
+ peer->fifo_len = be32_to_cpu(params->fifo_len);
+ peer_set_state(peer, FWPS_ATTACHED);
+
+ /* reconfigure tx_fifo optimally for this peer */
+ spin_lock_bh(&port->lock);
+ port->max_payload = min(peer->max_payload, peer->fifo_len);
+ dma_fifo_change_tx_limit(&port->tx_fifo, port->max_payload);
+ spin_unlock_bh(&peer->port->lock);
+
+ if (port->port.console && port->fwcon_ops->notify != NULL)
+ (*port->fwcon_ops->notify)(FWCON_NOTIFY_ATTACH, port->con_data);
+
+ fwtty_info(&peer->unit, "peer (guid:%016llx) connected on %s\n",
+ (unsigned long long)peer->guid, dev_name(port->device));
+}
+
+static inline int fwserial_send_mgmt_sync(struct fwtty_peer *peer,
+ struct fwserial_mgmt_pkt *pkt)
+{
+ int generation;
+ int rcode, tries = 5;
+
+ do {
+ generation = peer->generation;
+ smp_rmb();
+
+ rcode = fw_run_transaction(peer->serial->card,
+ TCODE_WRITE_BLOCK_REQUEST,
+ peer->node_id,
+ generation, peer->speed,
+ peer->mgmt_addr,
+ pkt, be16_to_cpu(pkt->hdr.len));
+ if (rcode == RCODE_BUSY || rcode == RCODE_SEND_ERROR ||
+ rcode == RCODE_GENERATION) {
+ fwtty_dbg(&peer->unit, "mgmt write error: %d\n", rcode);
+ continue;
+ } else {
+ break;
+ }
+ } while (--tries > 0);
+ return rcode;
+}
+
+/**
+ * fwserial_claim_port - attempt to claim port @ index for peer
+ *
+ * Returns ptr to claimed port or error code (as ERR_PTR())
+ * Can sleep - must be called from process context
+ */
+static struct fwtty_port *fwserial_claim_port(struct fwtty_peer *peer,
+ int index)
+{
+ struct fwtty_port *port;
+
+ if (index < 0 || index >= num_ports)
+ return ERR_PTR(-EINVAL);
+
+ /* must guarantee that previous port releases have completed */
+ synchronize_rcu();
+
+ port = peer->serial->ports[index];
+ spin_lock_bh(&port->lock);
+ if (!rcu_access_pointer(port->peer))
+ rcu_assign_pointer(port->peer, peer);
+ else
+ port = ERR_PTR(-EBUSY);
+ spin_unlock_bh(&port->lock);
+
+ return port;
+}
+
+/**
+ * fwserial_find_port - find avail port and claim for peer
+ *
+ * Returns ptr to claimed port or NULL if none avail
+ * Can sleep - must be called from process context
+ */
+static struct fwtty_port *fwserial_find_port(struct fwtty_peer *peer)
+{
+ struct fwtty_port **ports = peer->serial->ports;
+ int i;
+
+ /* must guarantee that previous port releases have completed */
+ synchronize_rcu();
+
+ /* TODO: implement optional GUID-to-specific port # matching */
+
+ /* find an unattached port (but not the loopback port, if present) */
+ for (i = 0; i < num_ttys; ++i) {
+ spin_lock_bh(&ports[i]->lock);
+ if (!ports[i]->peer) {
+ /* claim port */
+ rcu_assign_pointer(ports[i]->peer, peer);
+ spin_unlock_bh(&ports[i]->lock);
+ return ports[i];
+ }
+ spin_unlock_bh(&ports[i]->lock);
+ }
+ return NULL;
+}
+
+static void fwserial_release_port(struct fwtty_port *port, bool reset)
+{
+ /* drop carrier (and all other line status) */
+ if (reset)
+ fwtty_update_port_status(port, 0);
+
+ spin_lock_bh(&port->lock);
+
+ /* reset dma fifo max transmission size back to S100 */
+ port->max_payload = link_speed_to_max_payload(SCODE_100);
+ dma_fifo_change_tx_limit(&port->tx_fifo, port->max_payload);
+
+ RCU_INIT_POINTER(port->peer, NULL);
+ spin_unlock_bh(&port->lock);
+
+ if (port->port.console && port->fwcon_ops->notify != NULL)
+ (*port->fwcon_ops->notify)(FWCON_NOTIFY_DETACH, port->con_data);
+}
+
+static void fwserial_plug_timeout(unsigned long data)
+{
+ struct fwtty_peer *peer = (struct fwtty_peer *)data;
+ struct fwtty_port *port;
+
+ spin_lock_bh(&peer->lock);
+ if (peer->state != FWPS_PLUG_PENDING) {
+ spin_unlock_bh(&peer->lock);
+ return;
+ }
+
+ port = peer_revert_state(peer);
+ spin_unlock_bh(&peer->lock);
+
+ if (port)
+ fwserial_release_port(port, false);
+}
+
+/**
+ * fwserial_connect_peer - initiate virtual cable with peer
+ *
+ * Returns 0 if VIRT_CABLE_PLUG request was successfully sent,
+ * otherwise error code. Must be called from process context.
+ */
+static int fwserial_connect_peer(struct fwtty_peer *peer)
+{
+ struct fwtty_port *port;
+ struct fwserial_mgmt_pkt *pkt;
+ int err, rcode;
+
+ pkt = kmalloc(sizeof(*pkt), GFP_KERNEL);
+ if (!pkt)
+ return -ENOMEM;
+
+ port = fwserial_find_port(peer);
+ if (!port) {
+ fwtty_err(&peer->unit, "avail ports in use\n");
+ err = -EBUSY;
+ goto free_pkt;
+ }
+
+ spin_lock_bh(&peer->lock);
+
+ /* only initiate VIRT_CABLE_PLUG if peer is currently not attached */
+ if (peer->state != FWPS_NOT_ATTACHED) {
+ err = -EBUSY;
+ goto release_port;
+ }
+
+ peer->port = port;
+ peer_set_state(peer, FWPS_PLUG_PENDING);
+
+ fill_plug_req(pkt, peer->port);
+
+ setup_timer(&peer->timer, fwserial_plug_timeout, (unsigned long)peer);
+ mod_timer(&peer->timer, jiffies + VIRT_CABLE_PLUG_TIMEOUT);
+ spin_unlock_bh(&peer->lock);
+
+ rcode = fwserial_send_mgmt_sync(peer, pkt);
+
+ spin_lock_bh(&peer->lock);
+ if (peer->state == FWPS_PLUG_PENDING && rcode != RCODE_COMPLETE) {
+ if (rcode == RCODE_CONFLICT_ERROR)
+ err = -EAGAIN;
+ else
+ err = -EIO;
+ goto cancel_timer;
+ }
+ spin_unlock_bh(&peer->lock);
+
+ kfree(pkt);
+ return 0;
+
+cancel_timer:
+ del_timer(&peer->timer);
+ peer_revert_state(peer);
+release_port:
+ spin_unlock_bh(&peer->lock);
+ fwserial_release_port(port, false);
+free_pkt:
+ kfree(pkt);
+ return err;
+}
+
+/**
+ * fwserial_close_port -
+ * HUP the tty (if the tty exists) and unregister the tty device.
+ * Only used by the unit driver upon unit removal to disconnect and
+ * cleanup all attached ports
+ *
+ * The port reference is put by fwtty_cleanup (if a reference was
+ * ever taken).
+ */
+static void fwserial_close_port(struct tty_driver *driver,
+ struct fwtty_port *port)
+{
+ struct tty_struct *tty;
+
+ mutex_lock(&port->port.mutex);
+ tty = tty_port_tty_get(&port->port);
+ if (tty) {
+ tty_vhangup(tty);
+ tty_kref_put(tty);
+ }
+ mutex_unlock(&port->port.mutex);
+
+ if (driver == fwloop_driver)
+ tty_unregister_device(driver, loop_idx(port));
+ else
+ tty_unregister_device(driver, port->index);
+}
+
+/**
+ * fwserial_lookup - finds first fw_serial associated with card
+ * @card: fw_card to match
+ *
+ * NB: caller must be holding fwserial_list_mutex
+ */
+static struct fw_serial *fwserial_lookup(struct fw_card *card)
+{
+ struct fw_serial *serial;
+
+ list_for_each_entry(serial, &fwserial_list, list) {
+ if (card == serial->card)
+ return serial;
+ }
+
+ return NULL;
+}
+
+/**
+ * __fwserial_lookup_rcu - finds first fw_serial associated with card
+ * @card: fw_card to match
+ *
+ * NB: caller must be inside rcu_read_lock() section
+ */
+static struct fw_serial *__fwserial_lookup_rcu(struct fw_card *card)
+{
+ struct fw_serial *serial;
+
+ list_for_each_entry_rcu(serial, &fwserial_list, list) {
+ if (card == serial->card)
+ return serial;
+ }
+
+ return NULL;
+}
+
+/**
+ * __fwserial_peer_by_node_id - finds a peer matching the given generation + id
+ *
+ * If a matching peer could not be found for the specified generation/node id,
+ * this could be because:
+ * a) the generation has changed and one of the nodes hasn't updated yet
+ * b) the remote node has created its remote unit device before this
+ * local node has created its corresponding remote unit device
+ * In either case, the remote node should retry
+ *
+ * Note: caller must be in rcu_read_lock() section
+ */
+static struct fwtty_peer *__fwserial_peer_by_node_id(struct fw_card *card,
+ int generation, int id)
+{
+ struct fw_serial *serial;
+ struct fwtty_peer *peer;
+
+ serial = __fwserial_lookup_rcu(card);
+ if (!serial) {
+ /*
+ * Something is very wrong - there should be a matching
+ * fw_serial structure for every fw_card. Maybe the remote node
+ * has created its remote unit device before this driver has
+ * been probed for any unit devices...
+ */
+ fwtty_err(card, "unknown card (guid %016llx)\n",
+ (unsigned long long) card->guid);
+ return NULL;
+ }
+
+ list_for_each_entry_rcu(peer, &serial->peer_list, list) {
+ int g = peer->generation;
+
+ smp_rmb();
+ if (generation == g && id == peer->node_id)
+ return peer;
+ }
+
+ return NULL;
+}
+
+#ifdef DEBUG
+static void __dump_peer_list(struct fw_card *card)
+{
+ struct fw_serial *serial;
+ struct fwtty_peer *peer;
+
+ serial = __fwserial_lookup_rcu(card);
+ if (!serial)
+ return;
+
+ list_for_each_entry_rcu(peer, &serial->peer_list, list) {
+ int g = peer->generation;
+
+ smp_rmb();
+ fwtty_dbg(card, "peer(%d:%x) guid: %016llx\n",
+ g, peer->node_id, (unsigned long long) peer->guid);
+ }
+}
+#else
+#define __dump_peer_list(s)
+#endif
+
+static void fwserial_auto_connect(struct work_struct *work)
+{
+ struct fwtty_peer *peer = to_peer(to_delayed_work(work), connect);
+ int err;
+
+ err = fwserial_connect_peer(peer);
+ if (err == -EAGAIN && ++peer->connect_retries < MAX_CONNECT_RETRIES)
+ schedule_delayed_work(&peer->connect, CONNECT_RETRY_DELAY);
+}
+
+static void fwserial_peer_workfn(struct work_struct *work)
+{
+ struct fwtty_peer *peer = to_peer(work, work);
+
+ peer->workfn(work);
+}
+
+/**
+ * fwserial_add_peer - add a newly probed 'serial' unit device as a 'peer'
+ * @serial: aggregate representing the specific fw_card to add the peer to
+ * @unit: 'peer' to create and add to peer_list of serial
+ *
+ * Adds a 'peer' (ie, a local or remote 'serial' unit device) to the list of
+ * peers for a specific fw_card. Optionally, auto-attach this peer to an
+ * available tty port. This function is called either directly or indirectly
+ * as a result of a 'serial' unit device being created & probed.
+ *
+ * Note: this function is serialized with fwserial_remove_peer() by the
+ * fwserial_list_mutex held in fwserial_probe().
+ *
+ * A 1:1 correspondence between an fw_unit and an fwtty_peer is maintained
+ * via the dev_set_drvdata() for the device of the fw_unit.
+ */
+static int fwserial_add_peer(struct fw_serial *serial, struct fw_unit *unit)
+{
+ struct device *dev = &unit->device;
+ struct fw_device *parent = fw_parent_device(unit);
+ struct fwtty_peer *peer;
+ struct fw_csr_iterator ci;
+ int key, val;
+ int generation;
+
+ peer = kzalloc(sizeof(*peer), GFP_KERNEL);
+ if (!peer)
+ return -ENOMEM;
+
+ peer_set_state(peer, FWPS_NOT_ATTACHED);
+
+ dev_set_drvdata(dev, peer);
+ peer->unit = unit;
+ peer->guid = (u64)parent->config_rom[3] << 32 | parent->config_rom[4];
+ peer->speed = parent->max_speed;
+ peer->max_payload = min(device_max_receive(parent),
+ link_speed_to_max_payload(peer->speed));
+
+ generation = parent->generation;
+ smp_rmb();
+ peer->node_id = parent->node_id;
+ smp_wmb();
+ peer->generation = generation;
+
+ /* retrieve the mgmt bus addr from the unit directory */
+ fw_csr_iterator_init(&ci, unit->directory);
+ while (fw_csr_iterator_next(&ci, &key, &val)) {
+ if (key == (CSR_OFFSET | CSR_DEPENDENT_INFO)) {
+ peer->mgmt_addr = CSR_REGISTER_BASE + 4 * val;
+ break;
+ }
+ }
+ if (peer->mgmt_addr == 0ULL) {
+ /*
+ * No mgmt address effectively disables VIRT_CABLE_PLUG -
+ * this peer will not be able to attach to a remote
+ */
+ peer_set_state(peer, FWPS_NO_MGMT_ADDR);
+ }
+
+ spin_lock_init(&peer->lock);
+ peer->port = NULL;
+
+ init_timer(&peer->timer);
+ INIT_WORK(&peer->work, fwserial_peer_workfn);
+ INIT_DELAYED_WORK(&peer->connect, fwserial_auto_connect);
+
+ /* associate peer with specific fw_card */
+ peer->serial = serial;
+ list_add_rcu(&peer->list, &serial->peer_list);
+
+ fwtty_info(&peer->unit, "peer added (guid:%016llx)\n",
+ (unsigned long long)peer->guid);
+
+ /* identify the local unit & virt cable to loopback port */
+ if (parent->is_local) {
+ serial->self = peer;
+ if (create_loop_dev) {
+ struct fwtty_port *port;
+
+ port = fwserial_claim_port(peer, num_ttys);
+ if (!IS_ERR(port)) {
+ struct virt_plug_params params;
+
+ spin_lock_bh(&peer->lock);
+ peer->port = port;
+ fill_plug_params(&params, port);
+ fwserial_virt_plug_complete(peer, &params);
+ spin_unlock_bh(&peer->lock);
+
+ fwtty_write_port_status(port);
+ }
+ }
+
+ } else if (auto_connect) {
+ /* auto-attach to remote units only (if policy allows) */
+ schedule_delayed_work(&peer->connect, 1);
+ }
+
+ return 0;
+}
+
+/**
+ * fwserial_remove_peer - remove a 'serial' unit device as a 'peer'
+ *
+ * Remove a 'peer' from its list of peers. This function is only
+ * called by fwserial_remove() on bus removal of the unit device.
+ *
+ * Note: this function is serialized with fwserial_add_peer() by the
+ * fwserial_list_mutex held in fwserial_remove().
+ */
+static void fwserial_remove_peer(struct fwtty_peer *peer)
+{
+ struct fwtty_port *port;
+
+ spin_lock_bh(&peer->lock);
+ peer_set_state(peer, FWPS_GONE);
+ spin_unlock_bh(&peer->lock);
+
+ cancel_delayed_work_sync(&peer->connect);
+ cancel_work_sync(&peer->work);
+
+ spin_lock_bh(&peer->lock);
+ /* if this unit is the local unit, clear link */
+ if (peer == peer->serial->self)
+ peer->serial->self = NULL;
+
+ /* cancel the request timeout timer (if running) */
+ del_timer(&peer->timer);
+
+ port = peer->port;
+ peer->port = NULL;
+
+ list_del_rcu(&peer->list);
+
+ fwtty_info(&peer->unit, "peer removed (guid:%016llx)\n",
+ (unsigned long long)peer->guid);
+
+ spin_unlock_bh(&peer->lock);
+
+ if (port)
+ fwserial_release_port(port, true);
+
+ synchronize_rcu();
+ kfree(peer);
+}
+
+/**
+ * fwserial_create - init everything to create TTYs for a specific fw_card
+ * @unit: fw_unit for first 'serial' unit device probed for this fw_card
+ *
+ * This function inits the aggregate structure (an fw_serial instance)
+ * used to manage the TTY ports registered by a specific fw_card. Also, the
+ * unit device is added as the first 'peer'.
+ *
+ * This unit device may represent a local unit device (as specified by the
+ * config ROM unit directory) or it may represent a remote unit device
+ * (as specified by the reading of the remote node's config ROM).
+ *
+ * Returns 0 to indicate "ownership" of the unit device, or a negative errno
+ * value to indicate which error.
+ */
+static int fwserial_create(struct fw_unit *unit)
+{
+ struct fw_device *parent = fw_parent_device(unit);
+ struct fw_card *card = parent->card;
+ struct fw_serial *serial;
+ struct fwtty_port *port;
+ struct device *tty_dev;
+ int i, j;
+ int err;
+
+ serial = kzalloc(sizeof(*serial), GFP_KERNEL);
+ if (!serial)
+ return -ENOMEM;
+
+ kref_init(&serial->kref);
+ serial->card = card;
+ INIT_LIST_HEAD(&serial->peer_list);
+
+ for (i = 0; i < num_ports; ++i) {
+ port = kzalloc(sizeof(*port), GFP_KERNEL);
+ if (!port) {
+ err = -ENOMEM;
+ goto free_ports;
+ }
+ tty_port_init(&port->port);
+ port->index = FWTTY_INVALID_INDEX;
+ port->port.ops = &fwtty_port_ops;
+ port->serial = serial;
+ tty_buffer_set_limit(&port->port, 128 * 1024);
+
+ spin_lock_init(&port->lock);
+ INIT_DELAYED_WORK(&port->drain, fwtty_drain_tx);
+ INIT_DELAYED_WORK(&port->emit_breaks, fwtty_emit_breaks);
+ INIT_WORK(&port->hangup, fwtty_do_hangup);
+ init_waitqueue_head(&port->wait_tx);
+ port->max_payload = link_speed_to_max_payload(SCODE_100);
+ dma_fifo_init(&port->tx_fifo);
+
+ RCU_INIT_POINTER(port->peer, NULL);
+ serial->ports[i] = port;
+
+ /* get unique bus addr region for port's status & recv fifo */
+ port->rx_handler.length = FWTTY_PORT_RXFIFO_LEN + 4;
+ port->rx_handler.address_callback = fwtty_port_handler;
+ port->rx_handler.callback_data = port;
+ /*
+ * XXX: use custom memory region above cpu physical memory addrs
+ * this will ease porting to 64-bit firewire adapters
+ */
+ err = fw_core_add_address_handler(&port->rx_handler,
+ &fw_high_memory_region);
+ if (err) {
+ kfree(port);
+ goto free_ports;
+ }
+ }
+ /* preserve i for error cleanup */
+
+ err = fwtty_ports_add(serial);
+ if (err) {
+ fwtty_err(&unit, "no space in port table\n");
+ goto free_ports;
+ }
+
+ for (j = 0; j < num_ttys; ++j) {
+ tty_dev = tty_port_register_device(&serial->ports[j]->port,
+ fwtty_driver,
+ serial->ports[j]->index,
+ card->device);
+ if (IS_ERR(tty_dev)) {
+ err = PTR_ERR(tty_dev);
+ fwtty_err(&unit, "register tty device error (%d)\n",
+ err);
+ goto unregister_ttys;
+ }
+
+ serial->ports[j]->device = tty_dev;
+ }
+ /* preserve j for error cleanup */
+
+ if (create_loop_dev) {
+ struct device *loop_dev;
+
+ loop_dev = tty_port_register_device(&serial->ports[j]->port,
+ fwloop_driver,
+ loop_idx(serial->ports[j]),
+ card->device);
+ if (IS_ERR(loop_dev)) {
+ err = PTR_ERR(loop_dev);
+ fwtty_err(&unit, "create loop device failed (%d)\n",
+ err);
+ goto unregister_ttys;
+ }
+ serial->ports[j]->device = loop_dev;
+ serial->ports[j]->loopback = true;
+ }
+
+ if (!IS_ERR_OR_NULL(fwserial_debugfs)) {
+ serial->debugfs = debugfs_create_dir(dev_name(&unit->device),
+ fwserial_debugfs);
+ if (!IS_ERR_OR_NULL(serial->debugfs)) {
+ debugfs_create_file("peers", 0444, serial->debugfs,
+ serial, &fwtty_peers_fops);
+ debugfs_create_file("stats", 0444, serial->debugfs,
+ serial, &fwtty_stats_fops);
+ }
+ }
+
+ list_add_rcu(&serial->list, &fwserial_list);
+
+ fwtty_notice(&unit, "TTY over FireWire on device %s (guid %016llx)\n",
+ dev_name(card->device), (unsigned long long) card->guid);
+
+ err = fwserial_add_peer(serial, unit);
+ if (!err)
+ return 0;
+
+ fwtty_err(&unit, "unable to add peer unit device (%d)\n", err);
+
+ /* fall-through to error processing */
+ debugfs_remove_recursive(serial->debugfs);
+
+ list_del_rcu(&serial->list);
+ if (create_loop_dev)
+ tty_unregister_device(fwloop_driver,
+ loop_idx(serial->ports[j]));
+unregister_ttys:
+ for (--j; j >= 0; --j)
+ tty_unregister_device(fwtty_driver, serial->ports[j]->index);
+ kref_put(&serial->kref, fwserial_destroy);
+ return err;
+
+free_ports:
+ for (--i; i >= 0; --i) {
+ tty_port_destroy(&serial->ports[i]->port);
+ kfree(serial->ports[i]);
+ }
+ kfree(serial);
+ return err;
+}
+
+/**
+ * fwserial_probe: bus probe function for firewire 'serial' unit devices
+ *
+ * A 'serial' unit device is created and probed as a result of:
+ * - declaring a ieee1394 bus id table for 'devices' matching a fabricated
+ * 'serial' unit specifier id
+ * - adding a unit directory to the config ROM(s) for a 'serial' unit
+ *
+ * The firewire core registers unit devices by enumerating unit directories
+ * of a node's config ROM after reading the config ROM when a new node is
+ * added to the bus topology after a bus reset.
+ *
+ * The practical implications of this are:
+ * - this probe is called for both local and remote nodes that have a 'serial'
+ * unit directory in their config ROM (that matches the specifiers in
+ * fwserial_id_table).
+ * - no specific order is enforced for local vs. remote unit devices
+ *
+ * This unit driver copes with the lack of specific order in the same way the
+ * firewire net driver does -- each probe, for either a local or remote unit
+ * device, is treated as a 'peer' (has a struct fwtty_peer instance) and the
+ * first peer created for a given fw_card (tracked by the global fwserial_list)
+ * creates the underlying TTYs (aggregated in a fw_serial instance).
+ *
+ * NB: an early attempt to differentiate local & remote unit devices by creating
+ * peers only for remote units and fw_serial instances (with their
+ * associated TTY devices) only for local units was discarded. Managing
+ * the peer lifetimes on device removal proved too complicated.
+ *
+ * fwserial_probe/fwserial_remove are effectively serialized by the
+ * fwserial_list_mutex. This is necessary because the addition of the first peer
+ * for a given fw_card will trigger the creation of the fw_serial for that
+ * fw_card, which must not simultaneously contend with the removal of the
+ * last peer for a given fw_card triggering the destruction of the same
+ * fw_serial for the same fw_card.
+ */
+static int fwserial_probe(struct fw_unit *unit,
+ const struct ieee1394_device_id *id)
+{
+ struct fw_serial *serial;
+ int err;
+
+ mutex_lock(&fwserial_list_mutex);
+ serial = fwserial_lookup(fw_parent_device(unit)->card);
+ if (!serial)
+ err = fwserial_create(unit);
+ else
+ err = fwserial_add_peer(serial, unit);
+ mutex_unlock(&fwserial_list_mutex);
+ return err;
+}
+
+/**
+ * fwserial_remove: bus removal function for firewire 'serial' unit devices
+ *
+ * The corresponding 'peer' for this unit device is removed from the list of
+ * peers for the associated fw_serial (which has a 1:1 correspondence with a
+ * specific fw_card). If this is the last peer being removed, then trigger
+ * the destruction of the underlying TTYs.
+ */
+static void fwserial_remove(struct fw_unit *unit)
+{
+ struct fwtty_peer *peer = dev_get_drvdata(&unit->device);
+ struct fw_serial *serial = peer->serial;
+ int i;
+
+ mutex_lock(&fwserial_list_mutex);
+ fwserial_remove_peer(peer);
+
+ if (list_empty(&serial->peer_list)) {
+ /* unlink from the fwserial_list here */
+ list_del_rcu(&serial->list);
+
+ debugfs_remove_recursive(serial->debugfs);
+
+ for (i = 0; i < num_ttys; ++i)
+ fwserial_close_port(fwtty_driver, serial->ports[i]);
+ if (create_loop_dev)
+ fwserial_close_port(fwloop_driver, serial->ports[i]);
+ kref_put(&serial->kref, fwserial_destroy);
+ }
+ mutex_unlock(&fwserial_list_mutex);
+}
+
+/**
+ * fwserial_update: bus update function for 'firewire' serial unit devices
+ *
+ * Updates the new node_id and bus generation for this peer. Note that locking
+ * is unnecessary; but careful memory barrier usage is important to enforce the
+ * load and store order of generation & node_id.
+ *
+ * The fw-core orders the write of node_id before generation in the parent
+ * fw_device to ensure that a stale node_id cannot be used with a current
+ * bus generation. So the generation value must be read before the node_id.
+ *
+ * In turn, this orders the write of node_id before generation in the peer to
+ * also ensure a stale node_id cannot be used with a current bus generation.
+ */
+static void fwserial_update(struct fw_unit *unit)
+{
+ struct fw_device *parent = fw_parent_device(unit);
+ struct fwtty_peer *peer = dev_get_drvdata(&unit->device);
+ int generation;
+
+ generation = parent->generation;
+ smp_rmb();
+ peer->node_id = parent->node_id;
+ smp_wmb();
+ peer->generation = generation;
+}
+
+static const struct ieee1394_device_id fwserial_id_table[] = {
+ {
+ .match_flags = IEEE1394_MATCH_SPECIFIER_ID |
+ IEEE1394_MATCH_VERSION,
+ .specifier_id = LINUX_VENDOR_ID,
+ .version = FWSERIAL_VERSION,
+ },
+ { }
+};
+
+static struct fw_driver fwserial_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = KBUILD_MODNAME,
+ .bus = &fw_bus_type,
+ },
+ .probe = fwserial_probe,
+ .update = fwserial_update,
+ .remove = fwserial_remove,
+ .id_table = fwserial_id_table,
+};
+
+#define FW_UNIT_SPECIFIER(id) ((CSR_SPECIFIER_ID << 24) | (id))
+#define FW_UNIT_VERSION(ver) ((CSR_VERSION << 24) | (ver))
+#define FW_UNIT_ADDRESS(ofs) (((CSR_OFFSET | CSR_DEPENDENT_INFO) << 24) \
+ | (((ofs) - CSR_REGISTER_BASE) >> 2))
+/* XXX: config ROM definitons could be improved with semi-automated offset
+ * and length calculation
+ */
+#define FW_ROM_LEN(quads) ((quads) << 16)
+#define FW_ROM_DESCRIPTOR(ofs) (((CSR_LEAF | CSR_DESCRIPTOR) << 24) | (ofs))
+
+struct fwserial_unit_directory_data {
+ u32 len_crc;
+ u32 unit_specifier;
+ u32 unit_sw_version;
+ u32 unit_addr_offset;
+ u32 desc1_ofs;
+ u32 desc1_len_crc;
+ u32 desc1_data[5];
+} __packed;
+
+static struct fwserial_unit_directory_data fwserial_unit_directory_data = {
+ .len_crc = FW_ROM_LEN(4),
+ .unit_specifier = FW_UNIT_SPECIFIER(LINUX_VENDOR_ID),
+ .unit_sw_version = FW_UNIT_VERSION(FWSERIAL_VERSION),
+ .desc1_ofs = FW_ROM_DESCRIPTOR(1),
+ .desc1_len_crc = FW_ROM_LEN(5),
+ .desc1_data = {
+ 0x00000000, /* type = text */
+ 0x00000000, /* enc = ASCII, lang EN */
+ 0x4c696e75, /* 'Linux TTY' */
+ 0x78205454,
+ 0x59000000,
+ },
+};
+
+static struct fw_descriptor fwserial_unit_directory = {
+ .length = sizeof(fwserial_unit_directory_data) / sizeof(u32),
+ .key = (CSR_DIRECTORY | CSR_UNIT) << 24,
+ .data = (u32 *)&fwserial_unit_directory_data,
+};
+
+/*
+ * The management address is in the unit space region but above other known
+ * address users (to keep wild writes from causing havoc)
+ */
+static const struct fw_address_region fwserial_mgmt_addr_region = {
+ .start = CSR_REGISTER_BASE + 0x1e0000ULL,
+ .end = 0x1000000000000ULL,
+};
+
+static struct fw_address_handler fwserial_mgmt_addr_handler;
+
+/**
+ * fwserial_handle_plug_req - handle VIRT_CABLE_PLUG request work
+ * @work: ptr to peer->work
+ *
+ * Attempts to complete the VIRT_CABLE_PLUG handshake sequence for this peer.
+ *
+ * This checks for a collided request-- ie, that a VIRT_CABLE_PLUG request was
+ * already sent to this peer. If so, the collision is resolved by comparing
+ * guid values; the loser sends the plug response.
+ *
+ * Note: if an error prevents a response, don't do anything -- the
+ * remote will timeout its request.
+ */
+static void fwserial_handle_plug_req(struct work_struct *work)
+{
+ struct fwtty_peer *peer = to_peer(work, work);
+ struct virt_plug_params *plug_req = &peer->work_params.plug_req;
+ struct fwtty_port *port;
+ struct fwserial_mgmt_pkt *pkt;
+ int rcode;
+
+ pkt = kmalloc(sizeof(*pkt), GFP_KERNEL);
+ if (!pkt)
+ return;
+
+ port = fwserial_find_port(peer);
+
+ spin_lock_bh(&peer->lock);
+
+ switch (peer->state) {
+ case FWPS_NOT_ATTACHED:
+ if (!port) {
+ fwtty_err(&peer->unit, "no more ports avail\n");
+ fill_plug_rsp_nack(pkt);
+ } else {
+ peer->port = port;
+ fill_plug_rsp_ok(pkt, peer->port);
+ peer_set_state(peer, FWPS_PLUG_RESPONDING);
+ /* don't release claimed port */
+ port = NULL;
+ }
+ break;
+
+ case FWPS_PLUG_PENDING:
+ if (peer->serial->card->guid > peer->guid)
+ goto cleanup;
+
+ /* We lost - hijack the already-claimed port and send ok */
+ del_timer(&peer->timer);
+ fill_plug_rsp_ok(pkt, peer->port);
+ peer_set_state(peer, FWPS_PLUG_RESPONDING);
+ break;
+
+ default:
+ fill_plug_rsp_nack(pkt);
+ }
+
+ spin_unlock_bh(&peer->lock);
+ if (port)
+ fwserial_release_port(port, false);
+
+ rcode = fwserial_send_mgmt_sync(peer, pkt);
+
+ spin_lock_bh(&peer->lock);
+ if (peer->state == FWPS_PLUG_RESPONDING) {
+ if (rcode == RCODE_COMPLETE) {
+ struct fwtty_port *tmp = peer->port;
+
+ fwserial_virt_plug_complete(peer, plug_req);
+ spin_unlock_bh(&peer->lock);
+
+ fwtty_write_port_status(tmp);
+ spin_lock_bh(&peer->lock);
+ } else {
+ fwtty_err(&peer->unit, "PLUG_RSP error (%d)\n", rcode);
+ port = peer_revert_state(peer);
+ }
+ }
+cleanup:
+ spin_unlock_bh(&peer->lock);
+ if (port)
+ fwserial_release_port(port, false);
+ kfree(pkt);
+}
+
+static void fwserial_handle_unplug_req(struct work_struct *work)
+{
+ struct fwtty_peer *peer = to_peer(work, work);
+ struct fwtty_port *port = NULL;
+ struct fwserial_mgmt_pkt *pkt;
+ int rcode;
+
+ pkt = kmalloc(sizeof(*pkt), GFP_KERNEL);
+ if (!pkt)
+ return;
+
+ spin_lock_bh(&peer->lock);
+
+ switch (peer->state) {
+ case FWPS_ATTACHED:
+ fill_unplug_rsp_ok(pkt);
+ peer_set_state(peer, FWPS_UNPLUG_RESPONDING);
+ break;
+
+ case FWPS_UNPLUG_PENDING:
+ if (peer->serial->card->guid > peer->guid)
+ goto cleanup;
+
+ /* We lost - send unplug rsp */
+ del_timer(&peer->timer);
+ fill_unplug_rsp_ok(pkt);
+ peer_set_state(peer, FWPS_UNPLUG_RESPONDING);
+ break;
+
+ default:
+ fill_unplug_rsp_nack(pkt);
+ }
+
+ spin_unlock_bh(&peer->lock);
+
+ rcode = fwserial_send_mgmt_sync(peer, pkt);
+
+ spin_lock_bh(&peer->lock);
+ if (peer->state == FWPS_UNPLUG_RESPONDING) {
+ if (rcode != RCODE_COMPLETE)
+ fwtty_err(&peer->unit, "UNPLUG_RSP error (%d)\n",
+ rcode);
+ port = peer_revert_state(peer);
+ }
+cleanup:
+ spin_unlock_bh(&peer->lock);
+ if (port)
+ fwserial_release_port(port, true);
+ kfree(pkt);
+}
+
+static int fwserial_parse_mgmt_write(struct fwtty_peer *peer,
+ struct fwserial_mgmt_pkt *pkt,
+ unsigned long long addr,
+ size_t len)
+{
+ struct fwtty_port *port = NULL;
+ bool reset = false;
+ int rcode;
+
+ if (addr != fwserial_mgmt_addr_handler.offset || len < sizeof(pkt->hdr))
+ return RCODE_ADDRESS_ERROR;
+
+ if (len != be16_to_cpu(pkt->hdr.len) ||
+ len != mgmt_pkt_expected_len(pkt->hdr.code))
+ return RCODE_DATA_ERROR;
+
+ spin_lock_bh(&peer->lock);
+ if (peer->state == FWPS_GONE) {
+ /*
+ * This should never happen - it would mean that the
+ * remote unit that just wrote this transaction was
+ * already removed from the bus -- and the removal was
+ * processed before we rec'd this transaction
+ */
+ fwtty_err(&peer->unit, "peer already removed\n");
+ spin_unlock_bh(&peer->lock);
+ return RCODE_ADDRESS_ERROR;
+ }
+
+ rcode = RCODE_COMPLETE;
+
+ fwtty_dbg(&peer->unit, "mgmt: hdr.code: %04hx\n", pkt->hdr.code);
+
+ switch (be16_to_cpu(pkt->hdr.code) & FWSC_CODE_MASK) {
+ case FWSC_VIRT_CABLE_PLUG:
+ if (work_pending(&peer->work)) {
+ fwtty_err(&peer->unit, "plug req: busy\n");
+ rcode = RCODE_CONFLICT_ERROR;
+
+ } else {
+ peer->work_params.plug_req = pkt->plug_req;
+ peer->workfn = fwserial_handle_plug_req;
+ queue_work(system_unbound_wq, &peer->work);
+ }
+ break;
+
+ case FWSC_VIRT_CABLE_PLUG_RSP:
+ if (peer->state != FWPS_PLUG_PENDING) {
+ rcode = RCODE_CONFLICT_ERROR;
+
+ } else if (be16_to_cpu(pkt->hdr.code) & FWSC_RSP_NACK) {
+ fwtty_notice(&peer->unit, "NACK plug rsp\n");
+ port = peer_revert_state(peer);
+
+ } else {
+ struct fwtty_port *tmp = peer->port;
+
+ fwserial_virt_plug_complete(peer, &pkt->plug_rsp);
+ spin_unlock_bh(&peer->lock);
+
+ fwtty_write_port_status(tmp);
+ spin_lock_bh(&peer->lock);
+ }
+ break;
+
+ case FWSC_VIRT_CABLE_UNPLUG:
+ if (work_pending(&peer->work)) {
+ fwtty_err(&peer->unit, "unplug req: busy\n");
+ rcode = RCODE_CONFLICT_ERROR;
+ } else {
+ peer->workfn = fwserial_handle_unplug_req;
+ queue_work(system_unbound_wq, &peer->work);
+ }
+ break;
+
+ case FWSC_VIRT_CABLE_UNPLUG_RSP:
+ if (peer->state != FWPS_UNPLUG_PENDING) {
+ rcode = RCODE_CONFLICT_ERROR;
+ } else {
+ if (be16_to_cpu(pkt->hdr.code) & FWSC_RSP_NACK)
+ fwtty_notice(&peer->unit, "NACK unplug?\n");
+ port = peer_revert_state(peer);
+ reset = true;
+ }
+ break;
+
+ default:
+ fwtty_err(&peer->unit, "unknown mgmt code %d\n",
+ be16_to_cpu(pkt->hdr.code));
+ rcode = RCODE_DATA_ERROR;
+ }
+ spin_unlock_bh(&peer->lock);
+
+ if (port)
+ fwserial_release_port(port, reset);
+
+ return rcode;
+}
+
+/**
+ * fwserial_mgmt_handler: bus address handler for mgmt requests
+ * @parameters: fw_address_callback_t as specified by firewire core interface
+ *
+ * This handler is responsible for handling virtual cable requests from remotes
+ * for all cards.
+ */
+static void fwserial_mgmt_handler(struct fw_card *card,
+ struct fw_request *request,
+ int tcode, int destination, int source,
+ int generation,
+ unsigned long long addr,
+ void *data, size_t len,
+ void *callback_data)
+{
+ struct fwserial_mgmt_pkt *pkt = data;
+ struct fwtty_peer *peer;
+ int rcode;
+
+ rcu_read_lock();
+ peer = __fwserial_peer_by_node_id(card, generation, source);
+ if (!peer) {
+ fwtty_dbg(card, "peer(%d:%x) not found\n", generation, source);
+ __dump_peer_list(card);
+ rcode = RCODE_CONFLICT_ERROR;
+
+ } else {
+ switch (tcode) {
+ case TCODE_WRITE_BLOCK_REQUEST:
+ rcode = fwserial_parse_mgmt_write(peer, pkt, addr, len);
+ break;
+
+ default:
+ rcode = RCODE_TYPE_ERROR;
+ }
+ }
+
+ rcu_read_unlock();
+ fw_send_response(card, request, rcode);
+}
+
+static int __init fwserial_init(void)
+{
+ int err, num_loops = !!(create_loop_dev);
+
+ /* XXX: placeholder for a "firewire" debugfs node */
+ fwserial_debugfs = debugfs_create_dir(KBUILD_MODNAME, NULL);
+
+ /* num_ttys/num_ports must not be set above the static alloc avail */
+ if (num_ttys + num_loops > MAX_CARD_PORTS)
+ num_ttys = MAX_CARD_PORTS - num_loops;
+ num_ports = num_ttys + num_loops;
+
+ fwtty_driver = tty_alloc_driver(MAX_TOTAL_PORTS, TTY_DRIVER_REAL_RAW
+ | TTY_DRIVER_DYNAMIC_DEV);
+ if (IS_ERR(fwtty_driver)) {
+ err = PTR_ERR(fwtty_driver);
+ return err;
+ }
+
+ fwtty_driver->driver_name = KBUILD_MODNAME;
+ fwtty_driver->name = tty_dev_name;
+ fwtty_driver->major = 0;
+ fwtty_driver->minor_start = 0;
+ fwtty_driver->type = TTY_DRIVER_TYPE_SERIAL;
+ fwtty_driver->subtype = SERIAL_TYPE_NORMAL;
+ fwtty_driver->init_termios = tty_std_termios;
+ fwtty_driver->init_termios.c_cflag |= CLOCAL;
+ tty_set_operations(fwtty_driver, &fwtty_ops);
+
+ err = tty_register_driver(fwtty_driver);
+ if (err) {
+ pr_err("register tty driver failed (%d)\n", err);
+ goto put_tty;
+ }
+
+ if (create_loop_dev) {
+ fwloop_driver = tty_alloc_driver(MAX_TOTAL_PORTS / num_ports,
+ TTY_DRIVER_REAL_RAW
+ | TTY_DRIVER_DYNAMIC_DEV);
+ if (IS_ERR(fwloop_driver)) {
+ err = PTR_ERR(fwloop_driver);
+ goto unregister_driver;
+ }
+
+ fwloop_driver->driver_name = KBUILD_MODNAME "_loop";
+ fwloop_driver->name = loop_dev_name;
+ fwloop_driver->major = 0;
+ fwloop_driver->minor_start = 0;
+ fwloop_driver->type = TTY_DRIVER_TYPE_SERIAL;
+ fwloop_driver->subtype = SERIAL_TYPE_NORMAL;
+ fwloop_driver->init_termios = tty_std_termios;
+ fwloop_driver->init_termios.c_cflag |= CLOCAL;
+ tty_set_operations(fwloop_driver, &fwloop_ops);
+
+ err = tty_register_driver(fwloop_driver);
+ if (err) {
+ pr_err("register loop driver failed (%d)\n", err);
+ goto put_loop;
+ }
+ }
+
+ fwtty_txn_cache = kmem_cache_create("fwtty_txn_cache",
+ sizeof(struct fwtty_transaction),
+ 0, 0, fwtty_txn_constructor);
+ if (!fwtty_txn_cache) {
+ err = -ENOMEM;
+ goto unregister_loop;
+ }
+
+ /*
+ * Ideally, this address handler would be registered per local node
+ * (rather than the same handler for all local nodes). However,
+ * since the firewire core requires the config rom descriptor *before*
+ * the local unit device(s) are created, a single management handler
+ * must suffice for all local serial units.
+ */
+ fwserial_mgmt_addr_handler.length = sizeof(struct fwserial_mgmt_pkt);
+ fwserial_mgmt_addr_handler.address_callback = fwserial_mgmt_handler;
+
+ err = fw_core_add_address_handler(&fwserial_mgmt_addr_handler,
+ &fwserial_mgmt_addr_region);
+ if (err) {
+ pr_err("add management handler failed (%d)\n", err);
+ goto destroy_cache;
+ }
+
+ fwserial_unit_directory_data.unit_addr_offset =
+ FW_UNIT_ADDRESS(fwserial_mgmt_addr_handler.offset);
+ err = fw_core_add_descriptor(&fwserial_unit_directory);
+ if (err) {
+ pr_err("add unit descriptor failed (%d)\n", err);
+ goto remove_handler;
+ }
+
+ err = driver_register(&fwserial_driver.driver);
+ if (err) {
+ pr_err("register fwserial driver failed (%d)\n", err);
+ goto remove_descriptor;
+ }
+
+ return 0;
+
+remove_descriptor:
+ fw_core_remove_descriptor(&fwserial_unit_directory);
+remove_handler:
+ fw_core_remove_address_handler(&fwserial_mgmt_addr_handler);
+destroy_cache:
+ kmem_cache_destroy(fwtty_txn_cache);
+unregister_loop:
+ if (create_loop_dev)
+ tty_unregister_driver(fwloop_driver);
+put_loop:
+ if (create_loop_dev)
+ put_tty_driver(fwloop_driver);
+unregister_driver:
+ tty_unregister_driver(fwtty_driver);
+put_tty:
+ put_tty_driver(fwtty_driver);
+ debugfs_remove_recursive(fwserial_debugfs);
+ return err;
+}
+
+static void __exit fwserial_exit(void)
+{
+ driver_unregister(&fwserial_driver.driver);
+ fw_core_remove_descriptor(&fwserial_unit_directory);
+ fw_core_remove_address_handler(&fwserial_mgmt_addr_handler);
+ kmem_cache_destroy(fwtty_txn_cache);
+ if (create_loop_dev) {
+ tty_unregister_driver(fwloop_driver);
+ put_tty_driver(fwloop_driver);
+ }
+ tty_unregister_driver(fwtty_driver);
+ put_tty_driver(fwtty_driver);
+ debugfs_remove_recursive(fwserial_debugfs);
+}
+
+module_init(fwserial_init);
+module_exit(fwserial_exit);
+
+MODULE_AUTHOR("Peter Hurley (peter@hurleysoftware.com)");
+MODULE_DESCRIPTION("FireWire Serial TTY Driver");
+MODULE_LICENSE("GPL");
+MODULE_DEVICE_TABLE(ieee1394, fwserial_id_table);
+MODULE_PARM_DESC(ttys, "Number of ttys to create for each local firewire node");
+MODULE_PARM_DESC(auto, "Auto-connect a tty to each firewire node discovered");
+MODULE_PARM_DESC(loop, "Create a loopback device, fwloop<n>, with ttys");
diff --git a/drivers/staging/fwserial/fwserial.h b/drivers/staging/fwserial/fwserial.h
new file mode 100644
index 000000000..98b853d4a
--- /dev/null
+++ b/drivers/staging/fwserial/fwserial.h
@@ -0,0 +1,369 @@
+#ifndef _FIREWIRE_FWSERIAL_H
+#define _FIREWIRE_FWSERIAL_H
+
+#include <linux/kernel.h>
+#include <linux/tty.h>
+#include <linux/tty_driver.h>
+#include <linux/tty_flip.h>
+#include <linux/list.h>
+#include <linux/firewire.h>
+#include <linux/firewire-constants.h>
+#include <linux/spinlock.h>
+#include <linux/rcupdate.h>
+#include <linux/mutex.h>
+#include <linux/serial.h>
+#include <linux/serial_reg.h>
+#include <linux/module.h>
+#include <linux/seq_file.h>
+#include <linux/debugfs.h>
+
+#include "dma_fifo.h"
+
+#ifdef FWTTY_PROFILING
+#define DISTRIBUTION_MAX_SIZE 8192
+#define DISTRIBUTION_MAX_INDEX (ilog2(DISTRIBUTION_MAX_SIZE) + 1)
+static inline void fwtty_profile_data(unsigned stat[], unsigned val)
+{
+ int n = (val) ? min(ilog2(val) + 1, DISTRIBUTION_MAX_INDEX) : 0;
+ ++stat[n];
+}
+#else
+#define DISTRIBUTION_MAX_INDEX 0
+#define fwtty_profile_data(st, n)
+#endif
+
+/* Parameters for both VIRT_CABLE_PLUG & VIRT_CABLE_PLUG_RSP mgmt codes */
+struct virt_plug_params {
+ __be32 status_hi;
+ __be32 status_lo;
+ __be32 fifo_hi;
+ __be32 fifo_lo;
+ __be32 fifo_len;
+};
+
+struct peer_work_params {
+ union {
+ struct virt_plug_params plug_req;
+ };
+};
+
+/**
+ * fwtty_peer: structure representing local & remote unit devices
+ * @unit: unit child device of fw_device node
+ * @serial: back pointer to associated fw_serial aggregate
+ * @guid: unique 64-bit guid for this unit device
+ * @generation: most recent bus generation
+ * @node_id: most recent node_id
+ * @speed: link speed of peer (0 = S100, 2 = S400, ... 5 = S3200)
+ * @mgmt_addr: bus addr region to write mgmt packets to
+ * @status_addr: bus addr register to write line status to
+ * @fifo_addr: bus addr region to write serial output to
+ * @fifo_len: max length for single write to fifo_addr
+ * @list: link for insertion into fw_serial's peer_list
+ * @rcu: for deferring peer reclamation
+ * @lock: spinlock to synchonize changes to state & port fields
+ * @work: only one work item can be queued at any one time
+ * Note: pending work is canceled prior to removal, so this
+ * peer is valid for at least the lifetime of the work function
+ * @work_params: parameter block for work functions
+ * @timer: timer for resetting peer state if remote request times out
+ * @state: current state
+ * @connect: work item for auto-connecting
+ * @connect_retries: # of connections already attempted
+ * @port: associated tty_port (usable if state == FWSC_ATTACHED)
+ */
+struct fwtty_peer {
+ struct fw_unit *unit;
+ struct fw_serial *serial;
+ u64 guid;
+ int generation;
+ int node_id;
+ unsigned speed;
+ int max_payload;
+ u64 mgmt_addr;
+
+ /* these are usable only if state == FWSC_ATTACHED */
+ u64 status_addr;
+ u64 fifo_addr;
+ int fifo_len;
+
+ struct list_head list;
+ struct rcu_head rcu;
+
+ spinlock_t lock;
+ work_func_t workfn;
+ struct work_struct work;
+ struct peer_work_params work_params;
+ struct timer_list timer;
+ int state;
+ struct delayed_work connect;
+ int connect_retries;
+
+ struct fwtty_port *port;
+};
+
+#define to_peer(ptr, field) (container_of(ptr, struct fwtty_peer, field))
+
+/* state values for fwtty_peer.state field */
+enum fwtty_peer_state {
+ FWPS_GONE,
+ FWPS_NOT_ATTACHED,
+ FWPS_ATTACHED,
+ FWPS_PLUG_PENDING,
+ FWPS_PLUG_RESPONDING,
+ FWPS_UNPLUG_PENDING,
+ FWPS_UNPLUG_RESPONDING,
+
+ FWPS_NO_MGMT_ADDR = -1,
+};
+
+#define CONNECT_RETRY_DELAY HZ
+#define MAX_CONNECT_RETRIES 10
+
+/* must be holding peer lock for these state funclets */
+static inline void peer_set_state(struct fwtty_peer *peer, int new)
+{
+ peer->state = new;
+}
+
+static inline struct fwtty_port *peer_revert_state(struct fwtty_peer *peer)
+{
+ struct fwtty_port *port = peer->port;
+
+ peer->port = NULL;
+ peer_set_state(peer, FWPS_NOT_ATTACHED);
+ return port;
+}
+
+struct fwserial_mgmt_pkt {
+ struct {
+ __be16 len;
+ __be16 code;
+ } hdr;
+ union {
+ struct virt_plug_params plug_req;
+ struct virt_plug_params plug_rsp;
+ };
+} __packed;
+
+/* fwserial_mgmt_packet codes */
+#define FWSC_RSP_OK 0x0000
+#define FWSC_RSP_NACK 0x8000
+#define FWSC_CODE_MASK 0x0fff
+
+#define FWSC_VIRT_CABLE_PLUG 1
+#define FWSC_VIRT_CABLE_UNPLUG 2
+#define FWSC_VIRT_CABLE_PLUG_RSP 3
+#define FWSC_VIRT_CABLE_UNPLUG_RSP 4
+
+/* 1 min. plug timeout -- suitable for userland authorization */
+#define VIRT_CABLE_PLUG_TIMEOUT (60 * HZ)
+
+struct stats {
+ unsigned xchars;
+ unsigned dropped;
+ unsigned tx_stall;
+ unsigned fifo_errs;
+ unsigned sent;
+ unsigned lost;
+ unsigned throttled;
+ unsigned reads[DISTRIBUTION_MAX_INDEX + 1];
+ unsigned writes[DISTRIBUTION_MAX_INDEX + 1];
+ unsigned txns[DISTRIBUTION_MAX_INDEX + 1];
+ unsigned unthrottle[DISTRIBUTION_MAX_INDEX + 1];
+};
+
+struct fwconsole_ops {
+ void (*notify)(int code, void *data);
+ void (*stats)(struct stats *stats, void *data);
+ void (*proc_show)(struct seq_file *m, void *data);
+};
+
+/* codes for console ops notify */
+#define FWCON_NOTIFY_ATTACH 1
+#define FWCON_NOTIFY_DETACH 2
+
+/**
+ * fwtty_port: structure used to track/represent underlying tty_port
+ * @port: underlying tty_port
+ * @device: tty device
+ * @index: index into port_table for this particular port
+ * note: minor = index + minor_start assigned by tty_alloc_driver()
+ * @serial: back pointer to the containing fw_serial
+ * @rx_handler: bus address handler for unique addr region used by remotes
+ * to communicate with this port. Every port uses
+ * fwtty_port_handler() for per port transactions.
+ * @fwcon_ops: ops for attached fw_console (if any)
+ * @con_data: private data for fw_console
+ * @wait_tx: waitqueue for sleeping until writer/drain completes tx
+ * @emit_breaks: delayed work responsible for generating breaks when the
+ * break line status is active
+ * @cps : characters per second computed from the termios settings
+ * @break_last: timestamp in jiffies from last emit_breaks
+ * @hangup: work responsible for HUPing when carrier is dropped/lost
+ * @mstatus: loose virtualization of LSR/MSR
+ * bits 15..0 correspond to TIOCM_* bits
+ * bits 19..16 reserved for mctrl
+ * bit 20 OOB_TX_THROTTLE
+ * bits 23..21 reserved
+ * bits 31..24 correspond to UART_LSR_* bits
+ * @lock: spinlock for protecting concurrent access to fields below it
+ * @mctrl: loose virtualization of MCR
+ * bits 15..0 correspond to TIOCM_* bits
+ * bit 16 OOB_RX_THROTTLE
+ * bits 19..17 reserved
+ * bits 31..20 reserved for mstatus
+ * @drain: delayed work scheduled to ensure that writes are flushed.
+ * The work can race with the writer but concurrent sending is
+ * prevented with the IN_TX flag. Scheduled under lock to
+ * limit scheduling when fifo has just been drained.
+ * @tx_fifo: fifo used to store & block-up writes for dma to remote
+ * @max_payload: max bytes transmissable per dma (based on peer's max_payload)
+ * @status_mask: UART_LSR_* bitmask significant to rx (based on termios)
+ * @ignore_mask: UART_LSR_* bitmask of states to ignore (also based on termios)
+ * @break_ctl: if set, port is 'sending break' to remote
+ * @write_only: self-explanatory
+ * @overrun: previous rx was lost (partially or completely)
+ * @loopback: if set, port is in loopback mode
+ * @flags: atomic bit flags
+ * bit 0: IN_TX - gate to allow only one cpu to send from the dma fifo
+ * at a time.
+ * bit 1: STOP_TX - force tx to exit while sending
+ * @peer: rcu-pointer to associated fwtty_peer (if attached)
+ * NULL if no peer attached
+ * @icount: predefined statistics reported by the TIOCGICOUNT ioctl
+ * @stats: additional statistics reported in /proc/tty/driver/firewire_serial
+ */
+struct fwtty_port {
+ struct tty_port port;
+ struct device *device;
+ unsigned index;
+ struct fw_serial *serial;
+ struct fw_address_handler rx_handler;
+
+ struct fwconsole_ops *fwcon_ops;
+ void *con_data;
+
+ wait_queue_head_t wait_tx;
+ struct delayed_work emit_breaks;
+ unsigned cps;
+ unsigned long break_last;
+
+ struct work_struct hangup;
+
+ unsigned mstatus;
+
+ spinlock_t lock;
+ unsigned mctrl;
+ struct delayed_work drain;
+ struct dma_fifo tx_fifo;
+ int max_payload;
+ unsigned status_mask;
+ unsigned ignore_mask;
+ unsigned break_ctl:1,
+ write_only:1,
+ overrun:1,
+ loopback:1;
+ unsigned long flags;
+
+ struct fwtty_peer __rcu *peer;
+
+ struct async_icount icount;
+ struct stats stats;
+};
+
+#define to_port(ptr, field) (container_of(ptr, struct fwtty_port, field))
+
+/* bit #s for flags field */
+#define IN_TX 0
+#define STOP_TX 1
+
+/* bitmasks for special mctrl/mstatus bits */
+#define OOB_RX_THROTTLE 0x00010000
+#define MCTRL_RSRVD 0x000e0000
+#define OOB_TX_THROTTLE 0x00100000
+#define MSTATUS_RSRVD 0x00e00000
+
+#define MCTRL_MASK (TIOCM_DTR | TIOCM_RTS | TIOCM_OUT1 | TIOCM_OUT2 | \
+ TIOCM_LOOP | OOB_RX_THROTTLE | MCTRL_RSRVD)
+
+/* XXX even every 1/50th secs. may be unnecessarily accurate */
+/* delay in jiffies between brk emits */
+#define FREQ_BREAKS (HZ / 50)
+
+/* Ports are allocated in blocks of num_ports for each fw_card */
+#define MAX_CARD_PORTS CONFIG_FWTTY_MAX_CARD_PORTS
+#define MAX_TOTAL_PORTS CONFIG_FWTTY_MAX_TOTAL_PORTS
+
+/* tuning parameters */
+#define FWTTY_PORT_TXFIFO_LEN 4096
+#define FWTTY_PORT_MAX_PEND_DMA 8 /* costs a cache line per pend */
+#define DRAIN_THRESHOLD 1024
+#define MAX_ASYNC_PAYLOAD 4096 /* ohci-defined limit */
+#define WRITER_MINIMUM 128
+/* TODO: how to set watermark to AR context size? see fwtty_rx() */
+#define HIGH_WATERMARK 32768 /* AR context is 32K */
+
+/*
+ * Size of bus addr region above 4GB used per port as the recv addr
+ * - must be at least as big as the MAX_ASYNC_PAYLOAD
+ */
+#define FWTTY_PORT_RXFIFO_LEN MAX_ASYNC_PAYLOAD
+
+/**
+ * fw_serial: aggregate used to associate tty ports with specific fw_card
+ * @card: fw_card associated with this fw_serial device (1:1 association)
+ * @kref: reference-counted multi-port management allows delayed destroy
+ * @self: local unit device as 'peer'. Not valid until local unit device
+ * is enumerated.
+ * @list: link for insertion into fwserial_list
+ * @peer_list: list of local & remote unit devices attached to this card
+ * @ports: fixed array of tty_ports provided by this serial device
+ */
+struct fw_serial {
+ struct fw_card *card;
+ struct kref kref;
+
+ struct dentry *debugfs;
+ struct fwtty_peer *self;
+
+ struct list_head list;
+ struct list_head peer_list;
+
+ struct fwtty_port *ports[MAX_CARD_PORTS];
+};
+
+#define to_serial(ptr, field) (container_of(ptr, struct fw_serial, field))
+
+#define TTY_DEV_NAME "fwtty" /* ttyFW was taken */
+static const char tty_dev_name[] = TTY_DEV_NAME;
+static const char loop_dev_name[] = "fwloop";
+
+extern struct tty_driver *fwtty_driver;
+
+struct fwtty_port *fwtty_port_get(unsigned index);
+void fwtty_port_put(struct fwtty_port *port);
+
+static inline void fwtty_bind_console(struct fwtty_port *port,
+ struct fwconsole_ops *fwcon_ops,
+ void *data)
+{
+ port->con_data = data;
+ port->fwcon_ops = fwcon_ops;
+}
+
+/*
+ * Returns the max send async payload size in bytes based on the unit device
+ * link speed. Self-limiting asynchronous bandwidth (via reducing the payload)
+ * is not necessary and does not work, because
+ * 1) asynchronous traffic will absorb all available bandwidth (less that
+ * being used for isochronous traffic)
+ * 2) isochronous arbitration always wins.
+ */
+static inline int link_speed_to_max_payload(unsigned speed)
+{
+ /* Max async payload is 4096 - see IEEE 1394-2008 tables 6-4, 16-18 */
+ return min(512 << speed, 4096);
+}
+
+#endif /* _FIREWIRE_FWSERIAL_H */
diff --git a/drivers/staging/gdm724x/Kconfig b/drivers/staging/gdm724x/Kconfig
new file mode 100644
index 000000000..0a1f090bb
--- /dev/null
+++ b/drivers/staging/gdm724x/Kconfig
@@ -0,0 +1,15 @@
+#
+# GCT GDM724x LTE driver configuration
+#
+
+config LTE_GDM724X
+ tristate "GCT GDM724x LTE support"
+ depends on NET && USB && TTY && m
+ help
+ This driver supports GCT GDM724x LTE chip based USB modem devices.
+ It exposes 4 network devices to be used per PDN and 2 tty devices to be
+ used for AT commands and DM monitoring applications.
+ The modules will be called gdmulte.ko and gdmtty.ko
+
+ GCT-ATCx can be used for AT Commands
+ GCT-DMx can be used for LTE protocol monitoring
diff --git a/drivers/staging/gdm724x/Makefile b/drivers/staging/gdm724x/Makefile
new file mode 100644
index 000000000..ba7f11a6a
--- /dev/null
+++ b/drivers/staging/gdm724x/Makefile
@@ -0,0 +1,7 @@
+obj-$(CONFIG_LTE_GDM724X) := gdmulte.o
+gdmulte-y += gdm_lte.o netlink_k.o
+gdmulte-y += gdm_usb.o gdm_endian.o
+
+obj-$(CONFIG_LTE_GDM724X) += gdmtty.o
+gdmtty-y := gdm_tty.o gdm_mux.o
+
diff --git a/drivers/staging/gdm724x/TODO b/drivers/staging/gdm724x/TODO
new file mode 100644
index 000000000..b2b571ecb
--- /dev/null
+++ b/drivers/staging/gdm724x/TODO
@@ -0,0 +1,16 @@
+TODO:
+- Clean up coding style to meet kernel standard. (80 line limit, netdev_err)
+- Remove test for host endian
+- Remove confusing macros (endian, hci_send, sdu_send, rcv_with_cb)
+- Fixes for every instances of function returning -1
+- Check for skb->len in gdm_lte_emulate_arp()
+- Use ALIGN() macro for dummy_cnt in up_to_host()
+- Error handling in init_usb()
+- Explain reason for multiples of 512 bytes in alloc_tx_struct()
+- Review use of atomic allocation for tx structs
+- No error checking for alloc_tx_struct in do_tx()
+- fix up static tty port allocation to be dynamic
+
+Patches to:
+ Jonathan Kim <jonathankim@gctsemi.com>
+ Dean ahn <deanahn@gctsemi.com>
diff --git a/drivers/staging/gdm724x/gdm_endian.c b/drivers/staging/gdm724x/gdm_endian.c
new file mode 100644
index 000000000..f6cc90ae9
--- /dev/null
+++ b/drivers/staging/gdm724x/gdm_endian.c
@@ -0,0 +1,67 @@
+/*
+ * Copyright (c) 2012 GCT Semiconductor, Inc. All rights reserved.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ */
+
+#include <linux/slab.h>
+#include "gdm_endian.h"
+
+void gdm_set_endian(struct gdm_endian *ed, u8 dev_endian)
+{
+ u8 a[2] = {0x12, 0x34};
+ u8 b[2] = {0, };
+ u16 c = 0x1234;
+
+ if (dev_endian == ENDIANNESS_BIG)
+ ed->dev_ed = ENDIANNESS_BIG;
+ else
+ ed->dev_ed = ENDIANNESS_LITTLE;
+
+ memcpy(b, &c, 2);
+
+ if (a[0] != b[0])
+ ed->host_ed = ENDIANNESS_LITTLE;
+ else
+ ed->host_ed = ENDIANNESS_BIG;
+
+}
+
+u16 gdm_cpu_to_dev16(struct gdm_endian *ed, u16 x)
+{
+ if (ed->dev_ed == ed->host_ed)
+ return x;
+
+ return Endian16_Swap(x);
+}
+
+u16 gdm_dev16_to_cpu(struct gdm_endian *ed, u16 x)
+{
+ if (ed->dev_ed == ed->host_ed)
+ return x;
+
+ return Endian16_Swap(x);
+}
+
+u32 gdm_cpu_to_dev32(struct gdm_endian *ed, u32 x)
+{
+ if (ed->dev_ed == ed->host_ed)
+ return x;
+
+ return Endian32_Swap(x);
+}
+
+u32 gdm_dev32_to_cpu(struct gdm_endian *ed, u32 x)
+{
+ if (ed->dev_ed == ed->host_ed)
+ return x;
+
+ return Endian32_Swap(x);
+}
diff --git a/drivers/staging/gdm724x/gdm_endian.h b/drivers/staging/gdm724x/gdm_endian.h
new file mode 100644
index 000000000..9b2531ff9
--- /dev/null
+++ b/drivers/staging/gdm724x/gdm_endian.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2012 GCT Semiconductor, Inc. All rights reserved.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ */
+
+#ifndef __GDM_ENDIAN_H__
+#define __GDM_ENDIAN_H__
+
+#include <linux/types.h>
+
+#define Endian16_Swap(value) \
+ ((((u16)((value) & 0x00FF)) << 8) | \
+ (((u16)((value) & 0xFF00)) >> 8))
+
+#define Endian32_Swap(value) \
+ ((((u32)((value) & 0x000000FF)) << 24) | \
+ (((u32)((value) & 0x0000FF00)) << 8) | \
+ (((u32)((value) & 0x00FF0000)) >> 8) | \
+ (((u32)((value) & 0xFF000000)) >> 24))
+
+enum {
+ ENDIANNESS_MIN = 0,
+ ENDIANNESS_UNKNOWN,
+ ENDIANNESS_LITTLE,
+ ENDIANNESS_BIG,
+ ENDIANNESS_MIDDLE,
+ ENDIANNESS_MAX
+};
+
+struct gdm_endian {
+ u8 dev_ed;
+ u8 host_ed;
+};
+
+void gdm_set_endian(struct gdm_endian *ed, u8 dev_endian);
+u16 gdm_cpu_to_dev16(struct gdm_endian *ed, u16 x);
+u16 gdm_dev16_to_cpu(struct gdm_endian *ed, u16 x);
+u32 gdm_cpu_to_dev32(struct gdm_endian *ed, u32 x);
+u32 gdm_dev32_to_cpu(struct gdm_endian *ed, u32 x);
+
+#endif /*__GDM_ENDIAN_H__*/
diff --git a/drivers/staging/gdm724x/gdm_lte.c b/drivers/staging/gdm724x/gdm_lte.c
new file mode 100644
index 000000000..a8d2cffb4
--- /dev/null
+++ b/drivers/staging/gdm724x/gdm_lte.c
@@ -0,0 +1,947 @@
+/*
+ * Copyright (c) 2012 GCT Semiconductor, Inc. All rights reserved.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/etherdevice.h>
+#include <linux/ip.h>
+#include <linux/ipv6.h>
+#include <linux/udp.h>
+#include <linux/in.h>
+#include <linux/if_arp.h>
+#include <linux/if_ether.h>
+#include <linux/if_vlan.h>
+#include <linux/in6.h>
+#include <linux/tcp.h>
+#include <linux/icmp.h>
+#include <linux/icmpv6.h>
+#include <linux/uaccess.h>
+#include <net/ndisc.h>
+
+#include "gdm_lte.h"
+#include "netlink_k.h"
+#include "hci.h"
+#include "hci_packet.h"
+#include "gdm_endian.h"
+
+/*
+ * Netlink protocol number
+ */
+#define NETLINK_LTE 30
+
+/*
+ * Default MTU Size
+ */
+#define DEFAULT_MTU_SIZE 1500
+
+#define IP_VERSION_4 4
+#define IP_VERSION_6 6
+
+static struct {
+ int ref_cnt;
+ struct sock *sock;
+} lte_event;
+
+static struct device_type wwan_type = {
+ .name = "wwan",
+};
+
+static int gdm_lte_open(struct net_device *dev)
+{
+ netif_start_queue(dev);
+ return 0;
+}
+
+static int gdm_lte_close(struct net_device *dev)
+{
+ netif_stop_queue(dev);
+ return 0;
+}
+
+static int gdm_lte_set_config(struct net_device *dev, struct ifmap *map)
+{
+ if (dev->flags & IFF_UP)
+ return -EBUSY;
+ return 0;
+}
+
+static void tx_complete(void *arg)
+{
+ struct nic *nic = arg;
+
+ if (netif_queue_stopped(nic->netdev))
+ netif_wake_queue(nic->netdev);
+}
+
+static int gdm_lte_rx(struct sk_buff *skb, struct nic *nic, int nic_type)
+{
+ int ret;
+
+ ret = netif_rx_ni(skb);
+ if (ret == NET_RX_DROP) {
+ nic->stats.rx_dropped++;
+ } else {
+ nic->stats.rx_packets++;
+ nic->stats.rx_bytes += skb->len + ETH_HLEN;
+ }
+
+ return 0;
+}
+
+static int gdm_lte_emulate_arp(struct sk_buff *skb_in, u32 nic_type)
+{
+ struct nic *nic = netdev_priv(skb_in->dev);
+ struct sk_buff *skb_out;
+ struct ethhdr eth;
+ struct vlan_ethhdr vlan_eth;
+ struct arphdr *arp_in;
+ struct arphdr *arp_out;
+ struct arpdata {
+ u8 ar_sha[ETH_ALEN];
+ u8 ar_sip[4];
+ u8 ar_tha[ETH_ALEN];
+ u8 ar_tip[4];
+ };
+ struct arpdata *arp_data_in;
+ struct arpdata *arp_data_out;
+ u8 arp_temp[60];
+ void *mac_header_data;
+ u32 mac_header_len;
+
+ /* Format the mac header so that it can be put to skb */
+ if (ntohs(((struct ethhdr *)skb_in->data)->h_proto) == ETH_P_8021Q) {
+ memcpy(&vlan_eth, skb_in->data, sizeof(struct vlan_ethhdr));
+ mac_header_data = &vlan_eth;
+ mac_header_len = VLAN_ETH_HLEN;
+ } else {
+ memcpy(&eth, skb_in->data, sizeof(struct ethhdr));
+ mac_header_data = &eth;
+ mac_header_len = ETH_HLEN;
+ }
+
+ /* Get the pointer of the original request */
+ arp_in = (struct arphdr *)(skb_in->data + mac_header_len);
+ arp_data_in = (struct arpdata *)(skb_in->data + mac_header_len +
+ sizeof(struct arphdr));
+
+ /* Get the pointer of the outgoing response */
+ arp_out = (struct arphdr *)arp_temp;
+ arp_data_out = (struct arpdata *)(arp_temp + sizeof(struct arphdr));
+
+ /* Copy the arp header */
+ memcpy(arp_out, arp_in, sizeof(struct arphdr));
+ arp_out->ar_op = htons(ARPOP_REPLY);
+
+ /* Copy the arp payload: based on 2 bytes of mac and fill the IP */
+ arp_data_out->ar_sha[0] = arp_data_in->ar_sha[0];
+ arp_data_out->ar_sha[1] = arp_data_in->ar_sha[1];
+ memcpy(&arp_data_out->ar_sha[2], &arp_data_in->ar_tip[0], 4);
+ memcpy(&arp_data_out->ar_sip[0], &arp_data_in->ar_tip[0], 4);
+ memcpy(&arp_data_out->ar_tha[0], &arp_data_in->ar_sha[0], 6);
+ memcpy(&arp_data_out->ar_tip[0], &arp_data_in->ar_sip[0], 4);
+
+ /* Fill the destination mac with source mac of the received packet */
+ memcpy(mac_header_data, mac_header_data + ETH_ALEN, ETH_ALEN);
+ /* Fill the source mac with nic's source mac */
+ memcpy(mac_header_data + ETH_ALEN, nic->src_mac_addr, ETH_ALEN);
+
+ /* Alloc skb and reserve align */
+ skb_out = dev_alloc_skb(skb_in->len);
+ if (!skb_out)
+ return -ENOMEM;
+ skb_reserve(skb_out, NET_IP_ALIGN);
+
+ memcpy(skb_put(skb_out, mac_header_len), mac_header_data,
+ mac_header_len);
+ memcpy(skb_put(skb_out, sizeof(struct arphdr)), arp_out,
+ sizeof(struct arphdr));
+ memcpy(skb_put(skb_out, sizeof(struct arpdata)), arp_data_out,
+ sizeof(struct arpdata));
+
+ skb_out->protocol = ((struct ethhdr *)mac_header_data)->h_proto;
+ skb_out->dev = skb_in->dev;
+ skb_reset_mac_header(skb_out);
+ skb_pull(skb_out, ETH_HLEN);
+
+ gdm_lte_rx(skb_out, nic, nic_type);
+
+ return 0;
+}
+
+static int icmp6_checksum(struct ipv6hdr *ipv6, u16 *ptr, int len)
+{
+ unsigned short *w = ptr;
+ int sum = 0;
+ int i;
+
+ union {
+ struct {
+ u8 ph_src[16];
+ u8 ph_dst[16];
+ u32 ph_len;
+ u8 ph_zero[3];
+ u8 ph_nxt;
+ } ph __packed;
+ u16 pa[20];
+ } pseudo_header;
+
+ memset(&pseudo_header, 0, sizeof(pseudo_header));
+ memcpy(&pseudo_header.ph.ph_src, &ipv6->saddr.in6_u.u6_addr8, 16);
+ memcpy(&pseudo_header.ph.ph_dst, &ipv6->daddr.in6_u.u6_addr8, 16);
+ pseudo_header.ph.ph_len = ipv6->payload_len;
+ pseudo_header.ph.ph_nxt = ipv6->nexthdr;
+
+ w = (u16 *)&pseudo_header;
+ for (i = 0; i < ARRAY_SIZE(pseudo_header.pa); i++)
+ sum += pseudo_header.pa[i];
+
+ w = ptr;
+ while (len > 1) {
+ sum += *w++;
+ len -= 2;
+ }
+
+ sum = (sum >> 16) + (sum & 0xFFFF);
+ sum += (sum >> 16);
+ sum = ~sum & 0xffff;
+
+ return sum;
+}
+
+static int gdm_lte_emulate_ndp(struct sk_buff *skb_in, u32 nic_type)
+{
+ struct nic *nic = netdev_priv(skb_in->dev);
+ struct sk_buff *skb_out;
+ struct ethhdr eth;
+ struct vlan_ethhdr vlan_eth;
+ struct neighbour_advertisement {
+ u8 target_address[16];
+ u8 type;
+ u8 length;
+ u8 link_layer_address[6];
+ };
+ struct neighbour_advertisement na;
+ struct neighbour_solicitation {
+ u8 target_address[16];
+ };
+ struct neighbour_solicitation *ns;
+ struct ipv6hdr *ipv6_in;
+ struct ipv6hdr ipv6_out;
+ struct icmp6hdr *icmp6_in;
+ struct icmp6hdr icmp6_out;
+
+ void *mac_header_data;
+ u32 mac_header_len;
+
+ /* Format the mac header so that it can be put to skb */
+ if (ntohs(((struct ethhdr *)skb_in->data)->h_proto) == ETH_P_8021Q) {
+ memcpy(&vlan_eth, skb_in->data, sizeof(struct vlan_ethhdr));
+ if (ntohs(vlan_eth.h_vlan_encapsulated_proto) != ETH_P_IPV6)
+ return -1;
+ mac_header_data = &vlan_eth;
+ mac_header_len = VLAN_ETH_HLEN;
+ } else {
+ memcpy(&eth, skb_in->data, sizeof(struct ethhdr));
+ if (ntohs(eth.h_proto) != ETH_P_IPV6)
+ return -1;
+ mac_header_data = &eth;
+ mac_header_len = ETH_HLEN;
+ }
+
+ /* Check if this is IPv6 ICMP packet */
+ ipv6_in = (struct ipv6hdr *)(skb_in->data + mac_header_len);
+ if (ipv6_in->version != 6 || ipv6_in->nexthdr != IPPROTO_ICMPV6)
+ return -1;
+
+ /* Check if this is NDP packet */
+ icmp6_in = (struct icmp6hdr *)(skb_in->data + mac_header_len +
+ sizeof(struct ipv6hdr));
+ if (icmp6_in->icmp6_type == NDISC_ROUTER_SOLICITATION) { /* Check RS */
+ return -1;
+ } else if (icmp6_in->icmp6_type == NDISC_NEIGHBOUR_SOLICITATION) {
+ /* Check NS */
+ u8 icmp_na[sizeof(struct icmp6hdr) +
+ sizeof(struct neighbour_advertisement)];
+ u8 zero_addr8[16] = {0,};
+
+ if (memcmp(ipv6_in->saddr.in6_u.u6_addr8, zero_addr8, 16) == 0)
+ /* Duplicate Address Detection: Source IP is all zero */
+ return 0;
+
+ icmp6_out.icmp6_type = NDISC_NEIGHBOUR_ADVERTISEMENT;
+ icmp6_out.icmp6_code = 0;
+ icmp6_out.icmp6_cksum = 0;
+ /* R=0, S=1, O=1 */
+ icmp6_out.icmp6_dataun.un_data32[0] = htonl(0x60000000);
+
+ ns = (struct neighbour_solicitation *)
+ (skb_in->data + mac_header_len +
+ sizeof(struct ipv6hdr) + sizeof(struct icmp6hdr));
+ memcpy(&na.target_address, ns->target_address, 16);
+ na.type = 0x02;
+ na.length = 1;
+ na.link_layer_address[0] = 0x00;
+ na.link_layer_address[1] = 0x0a;
+ na.link_layer_address[2] = 0x3b;
+ na.link_layer_address[3] = 0xaf;
+ na.link_layer_address[4] = 0x63;
+ na.link_layer_address[5] = 0xc7;
+
+ memcpy(&ipv6_out, ipv6_in, sizeof(struct ipv6hdr));
+ memcpy(ipv6_out.saddr.in6_u.u6_addr8, &na.target_address, 16);
+ memcpy(ipv6_out.daddr.in6_u.u6_addr8,
+ ipv6_in->saddr.in6_u.u6_addr8, 16);
+ ipv6_out.payload_len = htons(sizeof(struct icmp6hdr) +
+ sizeof(struct neighbour_advertisement));
+
+ memcpy(icmp_na, &icmp6_out, sizeof(struct icmp6hdr));
+ memcpy(icmp_na + sizeof(struct icmp6hdr), &na,
+ sizeof(struct neighbour_advertisement));
+
+ icmp6_out.icmp6_cksum = icmp6_checksum(&ipv6_out,
+ (u16 *)icmp_na, sizeof(icmp_na));
+ } else {
+ return -1;
+ }
+
+ /* Fill the destination mac with source mac of the received packet */
+ memcpy(mac_header_data, mac_header_data + ETH_ALEN, ETH_ALEN);
+ /* Fill the source mac with nic's source mac */
+ memcpy(mac_header_data + ETH_ALEN, nic->src_mac_addr, ETH_ALEN);
+
+ /* Alloc skb and reserve align */
+ skb_out = dev_alloc_skb(skb_in->len);
+ if (!skb_out)
+ return -ENOMEM;
+ skb_reserve(skb_out, NET_IP_ALIGN);
+
+ memcpy(skb_put(skb_out, mac_header_len), mac_header_data,
+ mac_header_len);
+ memcpy(skb_put(skb_out, sizeof(struct ipv6hdr)), &ipv6_out,
+ sizeof(struct ipv6hdr));
+ memcpy(skb_put(skb_out, sizeof(struct icmp6hdr)), &icmp6_out,
+ sizeof(struct icmp6hdr));
+ memcpy(skb_put(skb_out, sizeof(struct neighbour_advertisement)), &na,
+ sizeof(struct neighbour_advertisement));
+
+ skb_out->protocol = ((struct ethhdr *)mac_header_data)->h_proto;
+ skb_out->dev = skb_in->dev;
+ skb_reset_mac_header(skb_out);
+ skb_pull(skb_out, ETH_HLEN);
+
+ gdm_lte_rx(skb_out, nic, nic_type);
+
+ return 0;
+}
+
+static s32 gdm_lte_tx_nic_type(struct net_device *dev, struct sk_buff *skb)
+{
+ struct nic *nic = netdev_priv(dev);
+ struct ethhdr *eth;
+ struct vlan_ethhdr *vlan_eth;
+ struct iphdr *ip;
+ struct ipv6hdr *ipv6;
+ int mac_proto;
+ void *network_data;
+ u32 nic_type = 0;
+
+ /* NIC TYPE is based on the nic_id of this net_device */
+ nic_type = 0x00000010 | nic->nic_id;
+
+ /* Get ethernet protocol */
+ eth = (struct ethhdr *)skb->data;
+ if (ntohs(eth->h_proto) == ETH_P_8021Q) {
+ vlan_eth = (struct vlan_ethhdr *)skb->data;
+ mac_proto = ntohs(vlan_eth->h_vlan_encapsulated_proto);
+ network_data = skb->data + VLAN_ETH_HLEN;
+ nic_type |= NIC_TYPE_F_VLAN;
+ } else {
+ mac_proto = ntohs(eth->h_proto);
+ network_data = skb->data + ETH_HLEN;
+ }
+
+ /* Process packet for nic type */
+ switch (mac_proto) {
+ case ETH_P_ARP:
+ nic_type |= NIC_TYPE_ARP;
+ break;
+ case ETH_P_IP:
+ nic_type |= NIC_TYPE_F_IPV4;
+ ip = (struct iphdr *)network_data;
+
+ /* Check DHCPv4 */
+ if (ip->protocol == IPPROTO_UDP) {
+ struct udphdr *udp = (struct udphdr *)
+ (network_data + sizeof(struct iphdr));
+ if (ntohs(udp->dest) == 67 || ntohs(udp->dest) == 68)
+ nic_type |= NIC_TYPE_F_DHCP;
+ }
+ break;
+ case ETH_P_IPV6:
+ nic_type |= NIC_TYPE_F_IPV6;
+ ipv6 = (struct ipv6hdr *)network_data;
+
+ if (ipv6->nexthdr == IPPROTO_ICMPV6) /* Check NDP request */ {
+ struct icmp6hdr *icmp6 = (struct icmp6hdr *)
+ (network_data + sizeof(struct ipv6hdr));
+ if (icmp6->icmp6_type == NDISC_NEIGHBOUR_SOLICITATION)
+ nic_type |= NIC_TYPE_ICMPV6;
+ } else if (ipv6->nexthdr == IPPROTO_UDP) /* Check DHCPv6 */ {
+ struct udphdr *udp = (struct udphdr *)
+ (network_data + sizeof(struct ipv6hdr));
+ if (ntohs(udp->dest) == 546 || ntohs(udp->dest) == 547)
+ nic_type |= NIC_TYPE_F_DHCP;
+ }
+ break;
+ default:
+ break;
+ }
+
+ return nic_type;
+}
+
+static int gdm_lte_tx(struct sk_buff *skb, struct net_device *dev)
+{
+ struct nic *nic = netdev_priv(dev);
+ u32 nic_type;
+ void *data_buf;
+ int data_len;
+ int idx;
+ int ret = 0;
+
+ nic_type = gdm_lte_tx_nic_type(dev, skb);
+ if (nic_type == 0) {
+ netdev_err(dev, "tx - invalid nic_type\n");
+ return -1;
+ }
+
+ if (nic_type & NIC_TYPE_ARP) {
+ if (gdm_lte_emulate_arp(skb, nic_type) == 0) {
+ dev_kfree_skb(skb);
+ return 0;
+ }
+ }
+
+ if (nic_type & NIC_TYPE_ICMPV6) {
+ if (gdm_lte_emulate_ndp(skb, nic_type) == 0) {
+ dev_kfree_skb(skb);
+ return 0;
+ }
+ }
+
+ /*
+ * Need byte shift (that is, remove VLAN tag) if there is one
+ * For the case of ARP, this breaks the offset as vlan_ethhdr+4
+ * is treated as ethhdr However, it shouldn't be a problem as
+ * the response starts from arp_hdr and ethhdr is created by this
+ * driver based on the NIC mac
+ */
+ if (nic_type & NIC_TYPE_F_VLAN) {
+ struct vlan_ethhdr *vlan_eth = (struct vlan_ethhdr *)skb->data;
+
+ nic->vlan_id = ntohs(vlan_eth->h_vlan_TCI) & VLAN_VID_MASK;
+ data_buf = skb->data + (VLAN_ETH_HLEN - ETH_HLEN);
+ data_len = skb->len - (VLAN_ETH_HLEN - ETH_HLEN);
+ } else {
+ nic->vlan_id = 0;
+ data_buf = skb->data;
+ data_len = skb->len;
+ }
+
+ /* If it is a ICMPV6 packet, clear all the other bits :
+ * for backward compatibility with the firmware
+ */
+ if (nic_type & NIC_TYPE_ICMPV6)
+ nic_type = NIC_TYPE_ICMPV6;
+
+ /* If it is not a dhcp packet, clear all the flag bits :
+ * original NIC, otherwise the special flag (IPVX | DHCP)
+ */
+ if (!(nic_type & NIC_TYPE_F_DHCP))
+ nic_type &= NIC_TYPE_MASK;
+
+ ret = sscanf(dev->name, "lte%d", &idx);
+ if (ret != 1) {
+ dev_kfree_skb(skb);
+ return -EINVAL;
+ }
+
+ ret = nic->phy_dev->send_sdu_func(nic->phy_dev->priv_dev,
+ data_buf, data_len,
+ nic->pdn_table.dft_eps_id, 0,
+ tx_complete, nic, idx,
+ nic_type);
+
+ if (ret == TX_NO_BUFFER || ret == TX_NO_SPC) {
+ netif_stop_queue(dev);
+ if (ret == TX_NO_BUFFER)
+ ret = 0;
+ else
+ ret = -ENOSPC;
+ } else if (ret == TX_NO_DEV) {
+ ret = -ENODEV;
+ }
+
+ /* Updates tx stats */
+ if (ret) {
+ nic->stats.tx_dropped++;
+ } else {
+ nic->stats.tx_packets++;
+ nic->stats.tx_bytes += data_len;
+ }
+ dev_kfree_skb(skb);
+
+ return 0;
+}
+
+static struct net_device_stats *gdm_lte_stats(struct net_device *dev)
+{
+ struct nic *nic = netdev_priv(dev);
+
+ return &nic->stats;
+}
+
+static int gdm_lte_event_send(struct net_device *dev, char *buf, int len)
+{
+ struct nic *nic = netdev_priv(dev);
+ struct hci_packet *hci = (struct hci_packet *)buf;
+ int idx;
+ int ret;
+
+ ret = sscanf(dev->name, "lte%d", &idx);
+ if (ret != 1)
+ return -EINVAL;
+
+ return netlink_send(lte_event.sock, idx, 0, buf,
+ gdm_dev16_to_cpu(
+ nic->phy_dev->get_endian(
+ nic->phy_dev->priv_dev), hci->len)
+ + HCI_HEADER_SIZE);
+}
+
+static void gdm_lte_event_rcv(struct net_device *dev, u16 type,
+ void *msg, int len)
+{
+ struct nic *nic = netdev_priv(dev);
+
+ nic->phy_dev->send_hci_func(nic->phy_dev->priv_dev, msg, len, NULL,
+ NULL);
+}
+
+int gdm_lte_event_init(void)
+{
+ if (lte_event.ref_cnt == 0)
+ lte_event.sock = netlink_init(NETLINK_LTE, gdm_lte_event_rcv);
+
+ if (lte_event.sock) {
+ lte_event.ref_cnt++;
+ return 0;
+ }
+
+ pr_err("event init failed\n");
+ return -1;
+}
+
+void gdm_lte_event_exit(void)
+{
+ if (lte_event.sock && --lte_event.ref_cnt == 0) {
+ netlink_exit(lte_event.sock);
+ lte_event.sock = NULL;
+ }
+}
+
+static u8 find_dev_index(u32 nic_type)
+{
+ u8 index;
+
+ index = (u8)(nic_type & 0x0000000f);
+ if (index > MAX_NIC_TYPE)
+ index = 0;
+
+ return index;
+}
+
+static void gdm_lte_netif_rx(struct net_device *dev, char *buf,
+ int len, int flagged_nic_type)
+{
+ u32 nic_type;
+ struct nic *nic;
+ struct sk_buff *skb;
+ struct ethhdr eth;
+ struct vlan_ethhdr vlan_eth;
+ void *mac_header_data;
+ u32 mac_header_len;
+ char ip_version = 0;
+
+ nic_type = flagged_nic_type & NIC_TYPE_MASK;
+ nic = netdev_priv(dev);
+
+ if (flagged_nic_type & NIC_TYPE_F_DHCP) {
+ /* Change the destination mac address
+ * with the one requested the IP
+ */
+ if (flagged_nic_type & NIC_TYPE_F_IPV4) {
+ struct dhcp_packet {
+ u8 op; /* BOOTREQUEST or BOOTREPLY */
+ u8 htype; /* hardware address type.
+ * 1 = 10mb ethernet
+ */
+ u8 hlen; /* hardware address length */
+ u8 hops; /* used by relay agents only */
+ u32 xid; /* unique id */
+ u16 secs; /* elapsed since client began
+ * acquisition/renewal
+ */
+ u16 flags; /* only one flag so far: */
+ #define BROADCAST_FLAG 0x8000
+ /* "I need broadcast replies" */
+ u32 ciaddr; /* client IP (if client is in
+ * BOUND, RENEW or REBINDING state)
+ */
+ u32 yiaddr; /* 'your' (client) IP address */
+ /* IP address of next server to use in
+ * bootstrap, returned in DHCPOFFER,
+ * DHCPACK by server
+ */
+ u32 siaddr_nip;
+ u32 gateway_nip; /* relay agent IP address */
+ u8 chaddr[16]; /* link-layer client hardware
+ * address (MAC)
+ */
+ u8 sname[64]; /* server host name (ASCIZ) */
+ u8 file[128]; /* boot file name (ASCIZ) */
+ u32 cookie; /* fixed first four option
+ * bytes (99,130,83,99 dec)
+ */
+ } __packed;
+ void *addr = buf + sizeof(struct iphdr) +
+ sizeof(struct udphdr) +
+ offsetof(struct dhcp_packet, chaddr);
+ ether_addr_copy(nic->dest_mac_addr, addr);
+ }
+ }
+
+ if (nic->vlan_id > 0) {
+ mac_header_data = (void *)&vlan_eth;
+ mac_header_len = VLAN_ETH_HLEN;
+ } else {
+ mac_header_data = (void *)&eth;
+ mac_header_len = ETH_HLEN;
+ }
+
+ /* Format the data so that it can be put to skb */
+ ether_addr_copy(mac_header_data, nic->dest_mac_addr);
+ memcpy(mac_header_data + ETH_ALEN, nic->src_mac_addr, ETH_ALEN);
+
+ vlan_eth.h_vlan_TCI = htons(nic->vlan_id);
+ vlan_eth.h_vlan_proto = htons(ETH_P_8021Q);
+
+ if (nic_type == NIC_TYPE_ARP) {
+ /* Should be response: Only happens because
+ * there was a request from the host
+ */
+ eth.h_proto = htons(ETH_P_ARP);
+ vlan_eth.h_vlan_encapsulated_proto = htons(ETH_P_ARP);
+ } else {
+ ip_version = buf[0] >> 4;
+ if (ip_version == IP_VERSION_4) {
+ eth.h_proto = htons(ETH_P_IP);
+ vlan_eth.h_vlan_encapsulated_proto = htons(ETH_P_IP);
+ } else if (ip_version == IP_VERSION_6) {
+ eth.h_proto = htons(ETH_P_IPV6);
+ vlan_eth.h_vlan_encapsulated_proto = htons(ETH_P_IPV6);
+ } else {
+ netdev_err(dev, "Unknown IP version %d\n", ip_version);
+ return;
+ }
+ }
+
+ /* Alloc skb and reserve align */
+ skb = dev_alloc_skb(len + mac_header_len + NET_IP_ALIGN);
+ if (!skb)
+ return;
+ skb_reserve(skb, NET_IP_ALIGN);
+
+ memcpy(skb_put(skb, mac_header_len), mac_header_data, mac_header_len);
+ memcpy(skb_put(skb, len), buf, len);
+
+ skb->protocol = ((struct ethhdr *)mac_header_data)->h_proto;
+ skb->dev = dev;
+ skb_reset_mac_header(skb);
+ skb_pull(skb, ETH_HLEN);
+
+ gdm_lte_rx(skb, nic, nic_type);
+}
+
+static void gdm_lte_multi_sdu_pkt(struct phy_dev *phy_dev, char *buf, int len)
+{
+ struct net_device *dev;
+ struct multi_sdu *multi_sdu = (struct multi_sdu *)buf;
+ struct sdu *sdu = NULL;
+ u8 *data = (u8 *)multi_sdu->data;
+ u16 i = 0;
+ u16 num_packet;
+ u16 hci_len;
+ u16 cmd_evt;
+ u32 nic_type;
+ u8 index;
+
+ hci_len = gdm_dev16_to_cpu(phy_dev->get_endian(phy_dev->priv_dev),
+ multi_sdu->len);
+ num_packet = gdm_dev16_to_cpu(phy_dev->get_endian(phy_dev->priv_dev),
+ multi_sdu->num_packet);
+
+ for (i = 0; i < num_packet; i++) {
+ sdu = (struct sdu *)data;
+
+ cmd_evt = gdm_dev16_to_cpu(phy_dev->
+ get_endian(phy_dev->priv_dev), sdu->cmd_evt);
+ hci_len = gdm_dev16_to_cpu(phy_dev->
+ get_endian(phy_dev->priv_dev), sdu->len);
+ nic_type = gdm_dev32_to_cpu(phy_dev->
+ get_endian(phy_dev->priv_dev), sdu->nic_type);
+
+ if (cmd_evt != LTE_RX_SDU) {
+ pr_err("rx sdu wrong hci %04x\n", cmd_evt);
+ return;
+ }
+ if (hci_len < 12) {
+ pr_err("rx sdu invalid len %d\n", hci_len);
+ return;
+ }
+
+ index = find_dev_index(nic_type);
+ if (index < MAX_NIC_TYPE) {
+ dev = phy_dev->dev[index];
+ gdm_lte_netif_rx(dev, (char *)sdu->data,
+ (int)(hci_len-12), nic_type);
+ } else {
+ pr_err("rx sdu invalid nic_type :%x\n", nic_type);
+ }
+
+ data += ((hci_len+3) & 0xfffc) + HCI_HEADER_SIZE;
+ }
+}
+
+static void gdm_lte_pdn_table(struct net_device *dev, char *buf, int len)
+{
+ struct nic *nic = netdev_priv(dev);
+ struct hci_pdn_table_ind *pdn_table = (struct hci_pdn_table_ind *)buf;
+
+ if (pdn_table->activate) {
+ nic->pdn_table.activate = pdn_table->activate;
+ nic->pdn_table.dft_eps_id = gdm_dev32_to_cpu(
+ nic->phy_dev->get_endian(
+ nic->phy_dev->priv_dev),
+ pdn_table->dft_eps_id);
+ nic->pdn_table.nic_type = gdm_dev32_to_cpu(
+ nic->phy_dev->get_endian(
+ nic->phy_dev->priv_dev),
+ pdn_table->nic_type);
+
+ netdev_info(dev, "pdn activated, nic_type=0x%x\n",
+ nic->pdn_table.nic_type);
+ } else {
+ memset(&nic->pdn_table, 0x00, sizeof(struct pdn_table));
+ netdev_info(dev, "pdn deactivated\n");
+ }
+}
+
+static int gdm_lte_receive_pkt(struct phy_dev *phy_dev, char *buf, int len)
+{
+ struct hci_packet *hci = (struct hci_packet *)buf;
+ struct hci_pdn_table_ind *pdn_table = (struct hci_pdn_table_ind *)buf;
+ struct sdu *sdu;
+ struct net_device *dev;
+ int ret = 0;
+ u16 cmd_evt;
+ u32 nic_type;
+ u8 index;
+
+ if (!len)
+ return ret;
+
+ cmd_evt = gdm_dev16_to_cpu(phy_dev->get_endian(phy_dev->priv_dev),
+ hci->cmd_evt);
+
+ dev = phy_dev->dev[0];
+ if (dev == NULL)
+ return 0;
+
+ switch (cmd_evt) {
+ case LTE_RX_SDU:
+ sdu = (struct sdu *)hci->data;
+ nic_type = gdm_dev32_to_cpu(phy_dev->
+ get_endian(phy_dev->priv_dev), sdu->nic_type);
+ index = find_dev_index(nic_type);
+ dev = phy_dev->dev[index];
+ gdm_lte_netif_rx(dev, hci->data, len, nic_type);
+ break;
+ case LTE_RX_MULTI_SDU:
+ gdm_lte_multi_sdu_pkt(phy_dev, buf, len);
+ break;
+ case LTE_LINK_ON_OFF_INDICATION:
+ netdev_info(dev, "link %s\n",
+ ((struct hci_connect_ind *)buf)->connect
+ ? "on" : "off");
+ break;
+ case LTE_PDN_TABLE_IND:
+ pdn_table = (struct hci_pdn_table_ind *)buf;
+ nic_type = gdm_dev32_to_cpu(phy_dev->
+ get_endian(phy_dev->priv_dev),
+ pdn_table->nic_type);
+ index = find_dev_index(nic_type);
+ dev = phy_dev->dev[index];
+ gdm_lte_pdn_table(dev, buf, len);
+ /* Fall through */
+ default:
+ ret = gdm_lte_event_send(dev, buf, len);
+ break;
+ }
+
+ return ret;
+}
+
+static int rx_complete(void *arg, void *data, int len, int context)
+{
+ struct phy_dev *phy_dev = (struct phy_dev *)arg;
+
+ return gdm_lte_receive_pkt(phy_dev, (char *)data, len);
+}
+
+void start_rx_proc(struct phy_dev *phy_dev)
+{
+ int i;
+
+ for (i = 0; i < MAX_RX_SUBMIT_COUNT; i++)
+ phy_dev->rcv_func(phy_dev->priv_dev,
+ rx_complete, phy_dev, USB_COMPLETE);
+}
+
+static struct net_device_ops gdm_netdev_ops = {
+ .ndo_open = gdm_lte_open,
+ .ndo_stop = gdm_lte_close,
+ .ndo_set_config = gdm_lte_set_config,
+ .ndo_start_xmit = gdm_lte_tx,
+ .ndo_get_stats = gdm_lte_stats,
+};
+
+static u8 gdm_lte_macaddr[ETH_ALEN] = {0x00, 0x0a, 0x3b, 0x00, 0x00, 0x00};
+
+static void form_mac_address(u8 *dev_addr, u8 *nic_src, u8 *nic_dest,
+ u8 *mac_address, u8 index)
+{
+ /* Form the dev_addr */
+ if (!mac_address)
+ ether_addr_copy(dev_addr, gdm_lte_macaddr);
+ else
+ ether_addr_copy(dev_addr, mac_address);
+
+ /* The last byte of the mac address
+ * should be less than or equal to 0xFC
+ */
+ dev_addr[ETH_ALEN-1] += index;
+
+ /* Create random nic src and copy the first
+ * 3 bytes to be the same as dev_addr
+ */
+ random_ether_addr(nic_src);
+ memcpy(nic_src, dev_addr, 3);
+
+ /* Copy the nic_dest from dev_addr*/
+ ether_addr_copy(nic_dest, dev_addr);
+}
+
+static void validate_mac_address(u8 *mac_address)
+{
+ /* if zero address or multicast bit set, restore the default value */
+ if (is_zero_ether_addr(mac_address) || (mac_address[0] & 0x01)) {
+ pr_err("MAC invalid, restoring default\n");
+ memcpy(mac_address, gdm_lte_macaddr, 6);
+ }
+}
+
+int register_lte_device(struct phy_dev *phy_dev,
+ struct device *dev, u8 *mac_address)
+{
+ struct nic *nic;
+ struct net_device *net;
+ char pdn_dev_name[16];
+ int ret = 0;
+ u8 index;
+
+ validate_mac_address(mac_address);
+
+ for (index = 0; index < MAX_NIC_TYPE; index++) {
+ /* Create device name lteXpdnX */
+ sprintf(pdn_dev_name, "lte%%dpdn%d", index);
+
+ /* Allocate netdev */
+ net = alloc_netdev(sizeof(struct nic), pdn_dev_name,
+ NET_NAME_UNKNOWN, ether_setup);
+ if (!net) {
+ pr_err("alloc_netdev failed\n");
+ ret = -ENOMEM;
+ goto err;
+ }
+ net->netdev_ops = &gdm_netdev_ops;
+ net->flags &= ~IFF_MULTICAST;
+ net->mtu = DEFAULT_MTU_SIZE;
+
+ nic = netdev_priv(net);
+ memset(nic, 0, sizeof(struct nic));
+ nic->netdev = net;
+ nic->phy_dev = phy_dev;
+ nic->nic_id = index;
+
+ form_mac_address(
+ net->dev_addr,
+ nic->src_mac_addr,
+ nic->dest_mac_addr,
+ mac_address,
+ index);
+
+ SET_NETDEV_DEV(net, dev);
+ SET_NETDEV_DEVTYPE(net, &wwan_type);
+
+ ret = register_netdev(net);
+ if (ret)
+ goto err;
+
+ netif_carrier_on(net);
+
+ phy_dev->dev[index] = net;
+ }
+
+ return 0;
+
+err:
+ unregister_lte_device(phy_dev);
+
+ return ret;
+}
+
+void unregister_lte_device(struct phy_dev *phy_dev)
+{
+ struct net_device *net;
+ int index;
+
+ for (index = 0; index < MAX_NIC_TYPE; index++) {
+ net = phy_dev->dev[index];
+ if (net == NULL)
+ continue;
+
+ unregister_netdev(net);
+ free_netdev(net);
+ }
+}
diff --git a/drivers/staging/gdm724x/gdm_lte.h b/drivers/staging/gdm724x/gdm_lte.h
new file mode 100644
index 000000000..88414e5a7
--- /dev/null
+++ b/drivers/staging/gdm724x/gdm_lte.h
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 2012 GCT Semiconductor, Inc. All rights reserved.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ */
+
+#ifndef _GDM_LTE_H_
+#define _GDM_LTE_H_
+
+#include <linux/netdevice.h>
+#include <linux/types.h>
+
+#include "gdm_endian.h"
+
+#define MAX_NIC_TYPE 4
+#define MAX_RX_SUBMIT_COUNT 3
+#define DRIVER_VERSION "3.7.17.0"
+
+enum TX_ERROR_CODE {
+ TX_NO_ERROR = 0,
+ TX_NO_DEV,
+ TX_NO_SPC,
+ TX_NO_BUFFER,
+};
+
+enum CALLBACK_CONTEXT {
+ KERNEL_THREAD = 0,
+ USB_COMPLETE,
+};
+
+struct pdn_table {
+ u8 activate;
+ u32 dft_eps_id;
+ u32 nic_type;
+} __packed;
+
+struct nic;
+
+struct phy_dev {
+ void *priv_dev;
+ struct net_device *dev[MAX_NIC_TYPE];
+ int (*send_hci_func)(void *priv_dev, void *data, int len,
+ void (*cb)(void *cb_data), void *cb_data);
+ int (*send_sdu_func)(void *priv_dev, void *data, int len,
+ unsigned int dftEpsId, unsigned int epsId,
+ void (*cb)(void *cb_data), void *cb_data,
+ int dev_idx, int nic_type);
+ int (*rcv_func)(void *priv_dev,
+ int (*cb)(void *cb_data, void *data, int len,
+ int context),
+ void *cb_data, int context);
+ struct gdm_endian * (*get_endian)(void *priv_dev);
+};
+
+struct nic {
+ struct net_device *netdev;
+ struct phy_dev *phy_dev;
+ struct net_device_stats stats;
+ struct pdn_table pdn_table;
+ u8 dest_mac_addr[ETH_ALEN];
+ u8 src_mac_addr[ETH_ALEN];
+ u32 nic_id;
+ u16 vlan_id;
+};
+
+int gdm_lte_event_init(void);
+void gdm_lte_event_exit(void);
+
+void start_rx_proc(struct phy_dev *phy_dev);
+int register_lte_device(struct phy_dev *phy_dev, struct device *dev,
+ u8 *mac_address);
+void unregister_lte_device(struct phy_dev *phy_dev);
+
+#endif /* _GDM_LTE_H_ */
diff --git a/drivers/staging/gdm724x/gdm_mux.c b/drivers/staging/gdm724x/gdm_mux.c
new file mode 100644
index 000000000..1cf24e4ed
--- /dev/null
+++ b/drivers/staging/gdm724x/gdm_mux.c
@@ -0,0 +1,691 @@
+/*
+ * Copyright (c) 2012 GCT Semiconductor, Inc. All rights reserved.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/usb.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/tty.h>
+#include <linux/tty_driver.h>
+#include <linux/tty_flip.h>
+#include <linux/slab.h>
+#include <linux/usb/cdc.h>
+
+#include "gdm_mux.h"
+
+static struct workqueue_struct *mux_rx_wq;
+
+static u16 packet_type[TTY_MAX_COUNT] = {0xF011, 0xF010};
+
+#define USB_DEVICE_CDC_DATA(vid, pid) \
+ .match_flags = \
+ USB_DEVICE_ID_MATCH_DEVICE |\
+ USB_DEVICE_ID_MATCH_INT_CLASS |\
+ USB_DEVICE_ID_MATCH_INT_SUBCLASS,\
+ .idVendor = vid,\
+ .idProduct = pid,\
+ .bInterfaceClass = USB_CLASS_COMM,\
+ .bInterfaceSubClass = USB_CDC_SUBCLASS_ACM
+
+static const struct usb_device_id id_table[] = {
+ { USB_DEVICE_CDC_DATA(0x1076, 0x8000) }, /* GCT GDM7240 */
+ { USB_DEVICE_CDC_DATA(0x1076, 0x8f00) }, /* GCT GDM7243 */
+ { USB_DEVICE_CDC_DATA(0x1076, 0x9000) }, /* GCT GDM7243 */
+ { USB_DEVICE_CDC_DATA(0x1d74, 0x2300) }, /* LGIT Phoenix */
+ {}
+};
+
+
+MODULE_DEVICE_TABLE(usb, id_table);
+
+static int packet_type_to_index(u16 packetType)
+{
+ int i;
+
+ for (i = 0; i < TTY_MAX_COUNT; i++) {
+ if (packet_type[i] == packetType)
+ return i;
+ }
+
+ return -1;
+}
+
+static struct mux_tx *alloc_mux_tx(int len)
+{
+ struct mux_tx *t = NULL;
+
+ t = kzalloc(sizeof(struct mux_tx), GFP_ATOMIC);
+ if (!t)
+ return NULL;
+
+ t->urb = usb_alloc_urb(0, GFP_ATOMIC);
+ t->buf = kmalloc(MUX_TX_MAX_SIZE, GFP_ATOMIC);
+ if (!t->urb || !t->buf) {
+ usb_free_urb(t->urb);
+ kfree(t->buf);
+ kfree(t);
+ return NULL;
+ }
+
+ return t;
+}
+
+static void free_mux_tx(struct mux_tx *t)
+{
+ if (t) {
+ usb_free_urb(t->urb);
+ kfree(t->buf);
+ kfree(t);
+ }
+}
+
+static struct mux_rx *alloc_mux_rx(void)
+{
+ struct mux_rx *r = NULL;
+
+ r = kzalloc(sizeof(struct mux_rx), GFP_KERNEL);
+ if (!r)
+ return NULL;
+
+ r->urb = usb_alloc_urb(0, GFP_KERNEL);
+ r->buf = kmalloc(MUX_RX_MAX_SIZE, GFP_KERNEL);
+ if (!r->urb || !r->buf) {
+ usb_free_urb(r->urb);
+ kfree(r->buf);
+ kfree(r);
+ return NULL;
+ }
+
+ return r;
+}
+
+static void free_mux_rx(struct mux_rx *r)
+{
+ if (r) {
+ usb_free_urb(r->urb);
+ kfree(r->buf);
+ kfree(r);
+ }
+}
+
+static struct mux_rx *get_rx_struct(struct rx_cxt *rx)
+{
+ struct mux_rx *r;
+ unsigned long flags;
+
+ spin_lock_irqsave(&rx->free_list_lock, flags);
+
+ if (list_empty(&rx->rx_free_list)) {
+ spin_unlock_irqrestore(&rx->free_list_lock, flags);
+ return NULL;
+ }
+
+ r = list_entry(rx->rx_free_list.prev, struct mux_rx, free_list);
+ list_del(&r->free_list);
+
+ spin_unlock_irqrestore(&rx->free_list_lock, flags);
+
+ return r;
+}
+
+static void put_rx_struct(struct rx_cxt *rx, struct mux_rx *r)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&rx->free_list_lock, flags);
+ list_add_tail(&r->free_list, &rx->rx_free_list);
+ spin_unlock_irqrestore(&rx->free_list_lock, flags);
+}
+
+
+static int up_to_host(struct mux_rx *r)
+{
+ struct mux_dev *mux_dev = (struct mux_dev *)r->mux_dev;
+ struct mux_pkt_header *mux_header;
+ unsigned int start_flag;
+ unsigned int payload_size;
+ unsigned short packet_type;
+ int total_len;
+ u32 packet_size_sum = r->offset;
+ int index;
+ int ret = TO_HOST_INVALID_PACKET;
+ int len = r->len;
+
+ while (1) {
+ mux_header = (struct mux_pkt_header *)(r->buf +
+ packet_size_sum);
+ start_flag = __le32_to_cpu(mux_header->start_flag);
+ payload_size = __le32_to_cpu(mux_header->payload_size);
+ packet_type = __le16_to_cpu(mux_header->packet_type);
+
+ if (start_flag != START_FLAG) {
+ pr_err("invalid START_FLAG %x\n", start_flag);
+ break;
+ }
+
+ total_len = ALIGN(MUX_HEADER_SIZE + payload_size, 4);
+
+ if (len - packet_size_sum <
+ total_len) {
+ pr_err("invalid payload : %d %d %04x\n",
+ payload_size, len, packet_type);
+ break;
+ }
+
+ index = packet_type_to_index(packet_type);
+ if (index < 0) {
+ pr_err("invalid index %d\n", index);
+ break;
+ }
+
+ ret = r->callback(mux_header->data,
+ payload_size,
+ index,
+ mux_dev->tty_dev,
+ RECV_PACKET_PROCESS_CONTINUE
+ );
+ if (ret == TO_HOST_BUFFER_REQUEST_FAIL) {
+ r->offset += packet_size_sum;
+ break;
+ }
+
+ packet_size_sum += total_len;
+ if (len - packet_size_sum <= MUX_HEADER_SIZE + 2) {
+ ret = r->callback(NULL,
+ 0,
+ index,
+ mux_dev->tty_dev,
+ RECV_PACKET_PROCESS_COMPLETE
+ );
+ break;
+ }
+ }
+
+ return ret;
+}
+
+static void do_rx(struct work_struct *work)
+{
+ struct mux_dev *mux_dev =
+ container_of(work, struct mux_dev, work_rx.work);
+ struct mux_rx *r;
+ struct rx_cxt *rx = (struct rx_cxt *)&mux_dev->rx;
+ unsigned long flags;
+ int ret = 0;
+
+ while (1) {
+ spin_lock_irqsave(&rx->to_host_lock, flags);
+ if (list_empty(&rx->to_host_list)) {
+ spin_unlock_irqrestore(&rx->to_host_lock, flags);
+ break;
+ }
+ r = list_entry(rx->to_host_list.next, struct mux_rx,
+ to_host_list);
+ list_del(&r->to_host_list);
+ spin_unlock_irqrestore(&rx->to_host_lock, flags);
+
+ ret = up_to_host(r);
+ if (ret == TO_HOST_BUFFER_REQUEST_FAIL)
+ pr_err("failed to send mux data to host\n");
+ else
+ put_rx_struct(rx, r);
+ }
+}
+
+static void remove_rx_submit_list(struct mux_rx *r, struct rx_cxt *rx)
+{
+ unsigned long flags;
+ struct mux_rx *r_remove, *r_remove_next;
+
+ spin_lock_irqsave(&rx->submit_list_lock, flags);
+ list_for_each_entry_safe(r_remove, r_remove_next, &rx->rx_submit_list,
+ rx_submit_list) {
+ if (r == r_remove)
+ list_del(&r->rx_submit_list);
+ }
+ spin_unlock_irqrestore(&rx->submit_list_lock, flags);
+}
+
+static void gdm_mux_rcv_complete(struct urb *urb)
+{
+ struct mux_rx *r = urb->context;
+ struct mux_dev *mux_dev = (struct mux_dev *)r->mux_dev;
+ struct rx_cxt *rx = &mux_dev->rx;
+ unsigned long flags;
+
+ remove_rx_submit_list(r, rx);
+
+ if (urb->status) {
+ if (mux_dev->usb_state == PM_NORMAL)
+ dev_err(&urb->dev->dev, "%s: urb status error %d\n",
+ __func__, urb->status);
+ put_rx_struct(rx, r);
+ } else {
+ r->len = r->urb->actual_length;
+ spin_lock_irqsave(&rx->to_host_lock, flags);
+ list_add_tail(&r->to_host_list, &rx->to_host_list);
+ queue_work(mux_rx_wq, &mux_dev->work_rx.work);
+ spin_unlock_irqrestore(&rx->to_host_lock, flags);
+ }
+}
+
+static int gdm_mux_recv(void *priv_dev, int (*cb)(void *data, int len,
+ int tty_index, struct tty_dev *tty_dev, int complete))
+{
+ struct mux_dev *mux_dev = priv_dev;
+ struct usb_device *usbdev = mux_dev->usbdev;
+ struct mux_rx *r;
+ struct rx_cxt *rx = &mux_dev->rx;
+ unsigned long flags;
+ int ret;
+
+ if (!usbdev) {
+ pr_err("device is disconnected\n");
+ return -ENODEV;
+ }
+
+ r = get_rx_struct(rx);
+ if (!r) {
+ pr_err("get_rx_struct fail\n");
+ return -ENOMEM;
+ }
+
+ r->offset = 0;
+ r->mux_dev = (void *)mux_dev;
+ r->callback = cb;
+ mux_dev->rx_cb = cb;
+
+ usb_fill_bulk_urb(r->urb,
+ usbdev,
+ usb_rcvbulkpipe(usbdev, 0x86),
+ r->buf,
+ MUX_RX_MAX_SIZE,
+ gdm_mux_rcv_complete,
+ r);
+
+ spin_lock_irqsave(&rx->submit_list_lock, flags);
+ list_add_tail(&r->rx_submit_list, &rx->rx_submit_list);
+ spin_unlock_irqrestore(&rx->submit_list_lock, flags);
+
+ ret = usb_submit_urb(r->urb, GFP_KERNEL);
+
+ if (ret) {
+ spin_lock_irqsave(&rx->submit_list_lock, flags);
+ list_del(&r->rx_submit_list);
+ spin_unlock_irqrestore(&rx->submit_list_lock, flags);
+
+ put_rx_struct(rx, r);
+
+ pr_err("usb_submit_urb ret=%d\n", ret);
+ }
+
+ usb_mark_last_busy(usbdev);
+
+ return ret;
+}
+
+static void gdm_mux_send_complete(struct urb *urb)
+{
+ struct mux_tx *t = urb->context;
+
+ if (urb->status == -ECONNRESET) {
+ dev_info(&urb->dev->dev, "CONNRESET\n");
+ free_mux_tx(t);
+ return;
+ }
+
+ if (t->callback)
+ t->callback(t->cb_data);
+
+ free_mux_tx(t);
+}
+
+static int gdm_mux_send(void *priv_dev, void *data, int len, int tty_index,
+ void (*cb)(void *data), void *cb_data)
+{
+ struct mux_dev *mux_dev = priv_dev;
+ struct usb_device *usbdev = mux_dev->usbdev;
+ struct mux_pkt_header *mux_header;
+ struct mux_tx *t = NULL;
+ static u32 seq_num = 1;
+ int total_len;
+ int ret;
+ unsigned long flags;
+
+ if (mux_dev->usb_state == PM_SUSPEND) {
+ ret = usb_autopm_get_interface(mux_dev->intf);
+ if (!ret)
+ usb_autopm_put_interface(mux_dev->intf);
+ }
+
+ spin_lock_irqsave(&mux_dev->write_lock, flags);
+
+ total_len = ALIGN(MUX_HEADER_SIZE + len, 4);
+
+ t = alloc_mux_tx(total_len);
+ if (!t) {
+ pr_err("alloc_mux_tx fail\n");
+ spin_unlock_irqrestore(&mux_dev->write_lock, flags);
+ return -ENOMEM;
+ }
+
+ mux_header = (struct mux_pkt_header *)t->buf;
+ mux_header->start_flag = __cpu_to_le32(START_FLAG);
+ mux_header->seq_num = __cpu_to_le32(seq_num++);
+ mux_header->payload_size = __cpu_to_le32((u32)len);
+ mux_header->packet_type = __cpu_to_le16(packet_type[tty_index]);
+
+ memcpy(t->buf+MUX_HEADER_SIZE, data, len);
+ memset(t->buf+MUX_HEADER_SIZE+len, 0, total_len - MUX_HEADER_SIZE -
+ len);
+
+ t->len = total_len;
+ t->callback = cb;
+ t->cb_data = cb_data;
+
+ usb_fill_bulk_urb(t->urb,
+ usbdev,
+ usb_sndbulkpipe(usbdev, 5),
+ t->buf,
+ total_len,
+ gdm_mux_send_complete,
+ t);
+
+ ret = usb_submit_urb(t->urb, GFP_ATOMIC);
+
+ spin_unlock_irqrestore(&mux_dev->write_lock, flags);
+
+ if (ret)
+ pr_err("usb_submit_urb Error: %d\n", ret);
+
+ usb_mark_last_busy(usbdev);
+
+ return ret;
+}
+
+static int gdm_mux_send_control(void *priv_dev, int request, int value,
+ void *buf, int len)
+{
+ struct mux_dev *mux_dev = priv_dev;
+ struct usb_device *usbdev = mux_dev->usbdev;
+ int ret;
+
+ ret = usb_control_msg(usbdev,
+ usb_sndctrlpipe(usbdev, 0),
+ request,
+ USB_RT_ACM,
+ value,
+ 2,
+ buf,
+ len,
+ 5000
+ );
+
+ if (ret < 0)
+ pr_err("usb_control_msg error: %d\n", ret);
+
+ return ret < 0 ? ret : 0;
+}
+
+static void release_usb(struct mux_dev *mux_dev)
+{
+ struct rx_cxt *rx = &mux_dev->rx;
+ struct mux_rx *r, *r_next;
+ unsigned long flags;
+
+ cancel_delayed_work(&mux_dev->work_rx);
+
+ spin_lock_irqsave(&rx->submit_list_lock, flags);
+ list_for_each_entry_safe(r, r_next, &rx->rx_submit_list,
+ rx_submit_list) {
+ spin_unlock_irqrestore(&rx->submit_list_lock, flags);
+ usb_kill_urb(r->urb);
+ spin_lock_irqsave(&rx->submit_list_lock, flags);
+ }
+ spin_unlock_irqrestore(&rx->submit_list_lock, flags);
+
+ spin_lock_irqsave(&rx->free_list_lock, flags);
+ list_for_each_entry_safe(r, r_next, &rx->rx_free_list, free_list) {
+ list_del(&r->free_list);
+ free_mux_rx(r);
+ }
+ spin_unlock_irqrestore(&rx->free_list_lock, flags);
+
+ spin_lock_irqsave(&rx->to_host_lock, flags);
+ list_for_each_entry_safe(r, r_next, &rx->to_host_list, to_host_list) {
+ if (r->mux_dev == (void *)mux_dev) {
+ list_del(&r->to_host_list);
+ free_mux_rx(r);
+ }
+ }
+ spin_unlock_irqrestore(&rx->to_host_lock, flags);
+}
+
+
+static int init_usb(struct mux_dev *mux_dev)
+{
+ struct mux_rx *r;
+ struct rx_cxt *rx = &mux_dev->rx;
+ int ret = 0;
+ int i;
+
+ spin_lock_init(&mux_dev->write_lock);
+ INIT_LIST_HEAD(&rx->to_host_list);
+ INIT_LIST_HEAD(&rx->rx_submit_list);
+ INIT_LIST_HEAD(&rx->rx_free_list);
+ spin_lock_init(&rx->to_host_lock);
+ spin_lock_init(&rx->submit_list_lock);
+ spin_lock_init(&rx->free_list_lock);
+
+ for (i = 0; i < MAX_ISSUE_NUM * 2; i++) {
+ r = alloc_mux_rx();
+ if (r == NULL) {
+ ret = -ENOMEM;
+ break;
+ }
+
+ list_add(&r->free_list, &rx->rx_free_list);
+ }
+
+ INIT_DELAYED_WORK(&mux_dev->work_rx, do_rx);
+
+ return ret;
+}
+
+static int gdm_mux_probe(struct usb_interface *intf,
+ const struct usb_device_id *id)
+{
+ struct mux_dev *mux_dev;
+ struct tty_dev *tty_dev;
+ u16 idVendor, idProduct;
+ int bInterfaceNumber;
+ int ret;
+ int i;
+ struct usb_device *usbdev = interface_to_usbdev(intf);
+
+ bInterfaceNumber = intf->cur_altsetting->desc.bInterfaceNumber;
+
+ idVendor = __le16_to_cpu(usbdev->descriptor.idVendor);
+ idProduct = __le16_to_cpu(usbdev->descriptor.idProduct);
+
+ pr_info("mux vid = 0x%04x pid = 0x%04x\n", idVendor, idProduct);
+
+ if (bInterfaceNumber != 2)
+ return -ENODEV;
+
+ mux_dev = kzalloc(sizeof(struct mux_dev), GFP_KERNEL);
+ if (!mux_dev)
+ return -ENOMEM;
+
+ tty_dev = kzalloc(sizeof(struct tty_dev), GFP_KERNEL);
+ if (!tty_dev) {
+ ret = -ENOMEM;
+ goto err_free_mux;
+ }
+
+ mux_dev->usbdev = usbdev;
+ mux_dev->control_intf = intf;
+
+ ret = init_usb(mux_dev);
+ if (ret)
+ goto err_free_usb;
+
+ tty_dev->priv_dev = (void *)mux_dev;
+ tty_dev->send_func = gdm_mux_send;
+ tty_dev->recv_func = gdm_mux_recv;
+ tty_dev->send_control = gdm_mux_send_control;
+
+ ret = register_lte_tty_device(tty_dev, &intf->dev);
+ if (ret)
+ goto err_unregister_tty;
+
+ for (i = 0; i < TTY_MAX_COUNT; i++)
+ mux_dev->tty_dev = tty_dev;
+
+ mux_dev->intf = intf;
+ mux_dev->usb_state = PM_NORMAL;
+
+ usb_get_dev(usbdev);
+ usb_set_intfdata(intf, tty_dev);
+
+ return 0;
+
+err_unregister_tty:
+ unregister_lte_tty_device(tty_dev);
+err_free_usb:
+ release_usb(mux_dev);
+ kfree(tty_dev);
+err_free_mux:
+ kfree(mux_dev);
+
+ return ret;
+}
+
+static void gdm_mux_disconnect(struct usb_interface *intf)
+{
+ struct tty_dev *tty_dev;
+ struct mux_dev *mux_dev;
+ struct usb_device *usbdev = interface_to_usbdev(intf);
+
+ tty_dev = usb_get_intfdata(intf);
+
+ mux_dev = tty_dev->priv_dev;
+
+ release_usb(mux_dev);
+ unregister_lte_tty_device(tty_dev);
+
+ kfree(mux_dev);
+ kfree(tty_dev);
+
+ usb_put_dev(usbdev);
+}
+
+static int gdm_mux_suspend(struct usb_interface *intf, pm_message_t pm_msg)
+{
+ struct tty_dev *tty_dev;
+ struct mux_dev *mux_dev;
+ struct rx_cxt *rx;
+ struct mux_rx *r, *r_next;
+ unsigned long flags;
+
+ tty_dev = usb_get_intfdata(intf);
+ mux_dev = tty_dev->priv_dev;
+ rx = &mux_dev->rx;
+
+ if (mux_dev->usb_state != PM_NORMAL) {
+ dev_err(intf->usb_dev, "usb suspend - invalid state\n");
+ return -1;
+ }
+
+ mux_dev->usb_state = PM_SUSPEND;
+
+
+ spin_lock_irqsave(&rx->submit_list_lock, flags);
+ list_for_each_entry_safe(r, r_next, &rx->rx_submit_list,
+ rx_submit_list) {
+ spin_unlock_irqrestore(&rx->submit_list_lock, flags);
+ usb_kill_urb(r->urb);
+ spin_lock_irqsave(&rx->submit_list_lock, flags);
+ }
+ spin_unlock_irqrestore(&rx->submit_list_lock, flags);
+
+ return 0;
+}
+
+static int gdm_mux_resume(struct usb_interface *intf)
+{
+ struct tty_dev *tty_dev;
+ struct mux_dev *mux_dev;
+ u8 i;
+
+ tty_dev = usb_get_intfdata(intf);
+ mux_dev = tty_dev->priv_dev;
+
+ if (mux_dev->usb_state != PM_SUSPEND) {
+ dev_err(intf->usb_dev, "usb resume - invalid state\n");
+ return -1;
+ }
+
+ mux_dev->usb_state = PM_NORMAL;
+
+ for (i = 0; i < MAX_ISSUE_NUM; i++)
+ gdm_mux_recv(mux_dev, mux_dev->rx_cb);
+
+ return 0;
+}
+
+static struct usb_driver gdm_mux_driver = {
+ .name = "gdm_mux",
+ .probe = gdm_mux_probe,
+ .disconnect = gdm_mux_disconnect,
+ .id_table = id_table,
+ .supports_autosuspend = 1,
+ .suspend = gdm_mux_suspend,
+ .resume = gdm_mux_resume,
+ .reset_resume = gdm_mux_resume,
+};
+
+static int __init gdm_usb_mux_init(void)
+{
+
+ mux_rx_wq = create_workqueue("mux_rx_wq");
+ if (mux_rx_wq == NULL) {
+ pr_err("work queue create fail\n");
+ return -1;
+ }
+
+ register_lte_tty_driver();
+
+ return usb_register(&gdm_mux_driver);
+}
+
+static void __exit gdm_usb_mux_exit(void)
+{
+ unregister_lte_tty_driver();
+
+ if (mux_rx_wq) {
+ flush_workqueue(mux_rx_wq);
+ destroy_workqueue(mux_rx_wq);
+ }
+
+ usb_deregister(&gdm_mux_driver);
+}
+
+module_init(gdm_usb_mux_init);
+module_exit(gdm_usb_mux_exit);
+
+MODULE_DESCRIPTION("GCT LTE TTY Device Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/gdm724x/gdm_mux.h b/drivers/staging/gdm724x/gdm_mux.h
new file mode 100644
index 000000000..3d50383c6
--- /dev/null
+++ b/drivers/staging/gdm724x/gdm_mux.h
@@ -0,0 +1,95 @@
+/*
+ * Copyright (c) 2012 GCT Semiconductor, Inc. All rights reserved.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ */
+
+#ifndef _GDM_MUX_H_
+#define _GDM_MUX_H_
+
+#include <linux/types.h>
+#include <linux/usb.h>
+#include <linux/list.h>
+
+#include "gdm_tty.h"
+
+#define PM_NORMAL 0
+#define PM_SUSPEND 1
+
+#define USB_RT_ACM (USB_TYPE_CLASS | USB_RECIP_INTERFACE)
+
+#define START_FLAG 0xA512485A
+#define MUX_HEADER_SIZE 14
+#define MUX_TX_MAX_SIZE (1024*10)
+#define MUX_RX_MAX_SIZE (1024*30)
+#define AT_PKT_TYPE 0xF011
+#define DM_PKT_TYPE 0xF010
+
+#define RETRY_TIMER 30 /* msec */
+
+struct mux_pkt_header {
+ __le32 start_flag;
+ __le32 seq_num;
+ __le32 payload_size;
+ __le16 packet_type;
+ unsigned char data[0];
+};
+
+struct mux_tx {
+ struct urb *urb;
+ u8 *buf;
+ int len;
+ void (*callback)(void *cb_data);
+ void *cb_data;
+};
+
+struct mux_rx {
+ struct list_head free_list;
+ struct list_head rx_submit_list;
+ struct list_head to_host_list;
+ struct urb *urb;
+ u8 *buf;
+ void *mux_dev;
+ u32 offset;
+ u32 len;
+ int (*callback)(void *data,
+ int len,
+ int tty_index,
+ struct tty_dev *tty_dev,
+ int complete);
+};
+
+struct rx_cxt {
+ struct list_head to_host_list;
+ struct list_head rx_submit_list;
+ struct list_head rx_free_list;
+ spinlock_t to_host_lock;
+ spinlock_t submit_list_lock;
+ spinlock_t free_list_lock;
+};
+
+struct mux_dev {
+ struct usb_device *usbdev;
+ struct usb_interface *control_intf;
+ struct usb_interface *data_intf;
+ struct rx_cxt rx;
+ struct delayed_work work_rx;
+ struct usb_interface *intf;
+ int usb_state;
+ int (*rx_cb)(void *data,
+ int len,
+ int tty_index,
+ struct tty_dev *tty_dev,
+ int complete);
+ spinlock_t write_lock;
+ struct tty_dev *tty_dev;
+};
+
+#endif /* _GDM_MUX_H_ */
diff --git a/drivers/staging/gdm724x/gdm_tty.c b/drivers/staging/gdm724x/gdm_tty.c
new file mode 100644
index 000000000..001348cca
--- /dev/null
+++ b/drivers/staging/gdm724x/gdm_tty.c
@@ -0,0 +1,351 @@
+/*
+ * Copyright (c) 2012 GCT Semiconductor, Inc. All rights reserved.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/tty.h>
+#include <linux/tty_driver.h>
+#include <linux/tty_flip.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/usb/cdc.h>
+#include <linux/serial.h>
+#include "gdm_tty.h"
+
+#define GDM_TTY_MAJOR 0
+#define GDM_TTY_MINOR 32
+
+#define ACM_CTRL_DTR 0x01
+#define ACM_CTRL_RTS 0x02
+#define ACM_CTRL_DSR 0x02
+#define ACM_CTRL_RI 0x08
+#define ACM_CTRL_DCD 0x01
+
+#define WRITE_SIZE 2048
+
+#define MUX_TX_MAX_SIZE 2048
+
+#define gdm_tty_send(n, d, l, i, c, b) (\
+ n->tty_dev->send_func(n->tty_dev->priv_dev, d, l, i, c, b))
+#define gdm_tty_recv(n, c) (\
+ n->tty_dev->recv_func(n->tty_dev->priv_dev, c))
+#define gdm_tty_send_control(n, r, v, d, l) (\
+ n->tty_dev->send_control(n->tty_dev->priv_dev, r, v, d, l))
+
+#define GDM_TTY_READY(gdm) (gdm && gdm->tty_dev && gdm->port.count)
+
+static struct tty_driver *gdm_driver[TTY_MAX_COUNT];
+static struct gdm *gdm_table[TTY_MAX_COUNT][GDM_TTY_MINOR];
+static DEFINE_MUTEX(gdm_table_lock);
+
+static char *DRIVER_STRING[TTY_MAX_COUNT] = {"GCTATC", "GCTDM"};
+static char *DEVICE_STRING[TTY_MAX_COUNT] = {"GCT-ATC", "GCT-DM"};
+
+static void gdm_port_destruct(struct tty_port *port)
+{
+ struct gdm *gdm = container_of(port, struct gdm, port);
+
+ mutex_lock(&gdm_table_lock);
+ gdm_table[gdm->index][gdm->minor] = NULL;
+ mutex_unlock(&gdm_table_lock);
+
+ kfree(gdm);
+}
+
+static struct tty_port_operations gdm_port_ops = {
+ .destruct = gdm_port_destruct,
+};
+
+static int gdm_tty_install(struct tty_driver *driver, struct tty_struct *tty)
+{
+ struct gdm *gdm = NULL;
+ int ret;
+ int i;
+ int j;
+
+ j = GDM_TTY_MINOR;
+ for (i = 0; i < TTY_MAX_COUNT; i++) {
+ if (!strcmp(tty->driver->driver_name, DRIVER_STRING[i])) {
+ j = tty->index;
+ break;
+ }
+ }
+
+ if (j == GDM_TTY_MINOR)
+ return -ENODEV;
+
+ mutex_lock(&gdm_table_lock);
+ gdm = gdm_table[i][j];
+ if (gdm == NULL) {
+ mutex_unlock(&gdm_table_lock);
+ return -ENODEV;
+ }
+
+ tty_port_get(&gdm->port);
+
+ ret = tty_standard_install(driver, tty);
+ if (ret) {
+ tty_port_put(&gdm->port);
+ mutex_unlock(&gdm_table_lock);
+ return ret;
+ }
+
+ tty->driver_data = gdm;
+ mutex_unlock(&gdm_table_lock);
+
+ return 0;
+}
+
+static int gdm_tty_open(struct tty_struct *tty, struct file *filp)
+{
+ struct gdm *gdm = tty->driver_data;
+
+ return tty_port_open(&gdm->port, tty, filp);
+}
+
+static void gdm_tty_cleanup(struct tty_struct *tty)
+{
+ struct gdm *gdm = tty->driver_data;
+
+ tty_port_put(&gdm->port);
+}
+
+static void gdm_tty_hangup(struct tty_struct *tty)
+{
+ struct gdm *gdm = tty->driver_data;
+
+ tty_port_hangup(&gdm->port);
+}
+
+static void gdm_tty_close(struct tty_struct *tty, struct file *filp)
+{
+ struct gdm *gdm = tty->driver_data;
+
+ tty_port_close(&gdm->port, tty, filp);
+}
+
+static int gdm_tty_recv_complete(void *data,
+ int len,
+ int index,
+ struct tty_dev *tty_dev,
+ int complete)
+{
+ struct gdm *gdm = tty_dev->gdm[index];
+
+ if (!GDM_TTY_READY(gdm)) {
+ if (complete == RECV_PACKET_PROCESS_COMPLETE)
+ gdm_tty_recv(gdm, gdm_tty_recv_complete);
+ return TO_HOST_PORT_CLOSE;
+ }
+
+ if (data && len) {
+ if (tty_buffer_request_room(&gdm->port, len) == len) {
+ tty_insert_flip_string(&gdm->port, data, len);
+ tty_flip_buffer_push(&gdm->port);
+ } else {
+ return TO_HOST_BUFFER_REQUEST_FAIL;
+ }
+ }
+
+ if (complete == RECV_PACKET_PROCESS_COMPLETE)
+ gdm_tty_recv(gdm, gdm_tty_recv_complete);
+
+ return 0;
+}
+
+static void gdm_tty_send_complete(void *arg)
+{
+ struct gdm *gdm = (struct gdm *)arg;
+
+ if (!GDM_TTY_READY(gdm))
+ return;
+
+ tty_port_tty_wakeup(&gdm->port);
+}
+
+static int gdm_tty_write(struct tty_struct *tty, const unsigned char *buf,
+ int len)
+{
+ struct gdm *gdm = tty->driver_data;
+ int remain = len;
+ int sent_len = 0;
+ int sending_len = 0;
+
+ if (!GDM_TTY_READY(gdm))
+ return -ENODEV;
+
+ if (!len)
+ return 0;
+
+ while (1) {
+ sending_len = remain > MUX_TX_MAX_SIZE ? MUX_TX_MAX_SIZE :
+ remain;
+ gdm_tty_send(gdm,
+ (void *)(buf+sent_len),
+ sending_len,
+ gdm->index,
+ gdm_tty_send_complete,
+ gdm
+ );
+ sent_len += sending_len;
+ remain -= sending_len;
+ if (remain <= 0)
+ break;
+ }
+
+ return len;
+}
+
+static int gdm_tty_write_room(struct tty_struct *tty)
+{
+ struct gdm *gdm = tty->driver_data;
+
+ if (!GDM_TTY_READY(gdm))
+ return -ENODEV;
+
+ return WRITE_SIZE;
+}
+
+int register_lte_tty_device(struct tty_dev *tty_dev, struct device *device)
+{
+ struct gdm *gdm;
+ int i;
+ int j;
+
+ for (i = 0; i < TTY_MAX_COUNT; i++) {
+
+ gdm = kmalloc(sizeof(struct gdm), GFP_KERNEL);
+ if (!gdm)
+ return -ENOMEM;
+
+ mutex_lock(&gdm_table_lock);
+ for (j = 0; j < GDM_TTY_MINOR; j++) {
+ if (!gdm_table[i][j])
+ break;
+ }
+
+ if (j == GDM_TTY_MINOR) {
+ kfree(gdm);
+ mutex_unlock(&gdm_table_lock);
+ return -EINVAL;
+ }
+
+ gdm_table[i][j] = gdm;
+ mutex_unlock(&gdm_table_lock);
+
+ tty_dev->gdm[i] = gdm;
+ tty_port_init(&gdm->port);
+
+ gdm->port.ops = &gdm_port_ops;
+ gdm->index = i;
+ gdm->minor = j;
+ gdm->tty_dev = tty_dev;
+
+ tty_port_register_device(&gdm->port, gdm_driver[i],
+ gdm->minor, device);
+ }
+
+ for (i = 0; i < MAX_ISSUE_NUM; i++)
+ gdm_tty_recv(gdm, gdm_tty_recv_complete);
+
+ return 0;
+}
+
+void unregister_lte_tty_device(struct tty_dev *tty_dev)
+{
+ struct gdm *gdm;
+ struct tty_struct *tty;
+ int i;
+
+ for (i = 0; i < TTY_MAX_COUNT; i++) {
+ gdm = tty_dev->gdm[i];
+ if (!gdm)
+ continue;
+
+ mutex_lock(&gdm_table_lock);
+ gdm_table[gdm->index][gdm->minor] = NULL;
+ mutex_unlock(&gdm_table_lock);
+
+ tty = tty_port_tty_get(&gdm->port);
+ if (tty) {
+ tty_vhangup(tty);
+ tty_kref_put(tty);
+ }
+
+ tty_unregister_device(gdm_driver[i], gdm->minor);
+ tty_port_put(&gdm->port);
+ }
+}
+
+static const struct tty_operations gdm_tty_ops = {
+ .install = gdm_tty_install,
+ .open = gdm_tty_open,
+ .close = gdm_tty_close,
+ .cleanup = gdm_tty_cleanup,
+ .hangup = gdm_tty_hangup,
+ .write = gdm_tty_write,
+ .write_room = gdm_tty_write_room,
+};
+
+int register_lte_tty_driver(void)
+{
+ struct tty_driver *tty_driver;
+ int i;
+ int ret;
+
+ for (i = 0; i < TTY_MAX_COUNT; i++) {
+ tty_driver = alloc_tty_driver(GDM_TTY_MINOR);
+ if (!tty_driver)
+ return -ENOMEM;
+
+ tty_driver->owner = THIS_MODULE;
+ tty_driver->driver_name = DRIVER_STRING[i];
+ tty_driver->name = DEVICE_STRING[i];
+ tty_driver->major = GDM_TTY_MAJOR;
+ tty_driver->type = TTY_DRIVER_TYPE_SERIAL;
+ tty_driver->subtype = SERIAL_TYPE_NORMAL;
+ tty_driver->flags = TTY_DRIVER_REAL_RAW |
+ TTY_DRIVER_DYNAMIC_DEV;
+ tty_driver->init_termios = tty_std_termios;
+ tty_driver->init_termios.c_cflag = B9600 | CS8 | HUPCL | CLOCAL;
+ tty_driver->init_termios.c_lflag = ISIG | ICANON | IEXTEN;
+ tty_set_operations(tty_driver, &gdm_tty_ops);
+
+ ret = tty_register_driver(tty_driver);
+ if (ret) {
+ put_tty_driver(tty_driver);
+ return ret;
+ }
+
+ gdm_driver[i] = tty_driver;
+ }
+
+ return ret;
+}
+
+void unregister_lte_tty_driver(void)
+{
+ struct tty_driver *tty_driver;
+ int i;
+
+ for (i = 0; i < TTY_MAX_COUNT; i++) {
+ tty_driver = gdm_driver[i];
+ if (tty_driver) {
+ tty_unregister_driver(tty_driver);
+ put_tty_driver(tty_driver);
+ }
+ }
+}
+
diff --git a/drivers/staging/gdm724x/gdm_tty.h b/drivers/staging/gdm724x/gdm_tty.h
new file mode 100644
index 000000000..297438b4d
--- /dev/null
+++ b/drivers/staging/gdm724x/gdm_tty.h
@@ -0,0 +1,71 @@
+/*
+ * Copyright (c) 2012 GCT Semiconductor, Inc. All rights reserved.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ */
+
+#ifndef _GDM_TTY_H_
+#define _GDM_TTY_H_
+
+#include <linux/types.h>
+#include <linux/tty.h>
+
+
+#define TTY_MAX_COUNT 2
+
+#define MAX_ISSUE_NUM 3
+
+enum TO_HOST_RESULT {
+ TO_HOST_BUFFER_REQUEST_FAIL = 1,
+ TO_HOST_PORT_CLOSE = 2,
+ TO_HOST_INVALID_PACKET = 3,
+};
+
+enum RECV_PACKET_PROCESS {
+ RECV_PACKET_PROCESS_COMPLETE = 0,
+ RECV_PACKET_PROCESS_CONTINUE = 1,
+};
+
+struct gdm {
+ struct tty_dev *tty_dev;
+ struct tty_port port;
+ unsigned int index;
+ unsigned int minor;
+};
+
+struct tty_dev {
+ void *priv_dev;
+ int (*send_func)(void *priv_dev,
+ void *data,
+ int len,
+ int tty_index,
+ void (*cb)(void *cb_data),
+ void *cb_data);
+ int (*recv_func)(void *priv_dev,
+ int (*cb)(void *data,
+ int len,
+ int tty_index,
+ struct tty_dev *tty_dev,
+ int complete));
+ int (*send_control)(void *priv_dev,
+ int request,
+ int value,
+ void *data,
+ int len);
+ struct gdm *gdm[2];
+};
+
+int register_lte_tty_driver(void);
+void unregister_lte_tty_driver(void);
+int register_lte_tty_device(struct tty_dev *tty_dev, struct device *dev);
+void unregister_lte_tty_device(struct tty_dev *tty_dev);
+
+#endif /* _GDM_USB_H_ */
+
diff --git a/drivers/staging/gdm724x/gdm_usb.c b/drivers/staging/gdm724x/gdm_usb.c
new file mode 100644
index 000000000..ed1a12f50
--- /dev/null
+++ b/drivers/staging/gdm724x/gdm_usb.c
@@ -0,0 +1,1041 @@
+/*
+ * Copyright (c) 2012 GCT Semiconductor, Inc. All rights reserved.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/usb.h>
+#include <linux/sched.h>
+#include <linux/kthread.h>
+#include <linux/usb/cdc.h>
+#include <linux/wait.h>
+#include <linux/if_ether.h>
+#include <linux/pm_runtime.h>
+
+#include "gdm_usb.h"
+#include "gdm_lte.h"
+#include "hci.h"
+#include "hci_packet.h"
+#include "gdm_endian.h"
+
+#define USB_DEVICE_CDC_DATA(vid, pid) \
+ .match_flags = USB_DEVICE_ID_MATCH_DEVICE | \
+ USB_DEVICE_ID_MATCH_INT_CLASS | \
+ USB_DEVICE_ID_MATCH_INT_SUBCLASS,\
+ .idVendor = vid,\
+ .idProduct = pid,\
+ .bInterfaceClass = USB_CLASS_COMM,\
+ .bInterfaceSubClass = USB_CDC_SUBCLASS_ETHERNET
+
+#define USB_DEVICE_MASS_DATA(vid, pid) \
+ .match_flags = USB_DEVICE_ID_MATCH_DEVICE | \
+ USB_DEVICE_ID_MATCH_INT_INFO,\
+ .idVendor = vid,\
+ .idProduct = pid,\
+ .bInterfaceSubClass = USB_SC_SCSI, \
+ .bInterfaceClass = USB_CLASS_MASS_STORAGE,\
+ .bInterfaceProtocol = USB_PR_BULK
+
+static const struct usb_device_id id_table[] = {
+ { USB_DEVICE_CDC_DATA(VID_GCT, PID_GDM7240) }, /* GCT GDM7240 */
+ { USB_DEVICE_CDC_DATA(VID_GCT, PID_GDM7243) }, /* GCT GDM7243 */
+ { }
+};
+
+MODULE_DEVICE_TABLE(usb, id_table);
+
+static struct workqueue_struct *usb_tx_wq;
+static struct workqueue_struct *usb_rx_wq;
+
+static void do_tx(struct work_struct *work);
+static void do_rx(struct work_struct *work);
+
+static int gdm_usb_recv(void *priv_dev,
+ int (*cb)(void *cb_data,
+ void *data, int len, int context),
+ void *cb_data,
+ int context);
+
+static int request_mac_address(struct lte_udev *udev)
+{
+ u8 buf[16] = {0,};
+ struct hci_packet *hci = (struct hci_packet *)buf;
+ struct usb_device *usbdev = udev->usbdev;
+ int actual;
+ int ret = -1;
+
+ hci->cmd_evt = gdm_cpu_to_dev16(&udev->gdm_ed, LTE_GET_INFORMATION);
+ hci->len = gdm_cpu_to_dev16(&udev->gdm_ed, 1);
+ hci->data[0] = MAC_ADDRESS;
+
+ ret = usb_bulk_msg(usbdev, usb_sndbulkpipe(usbdev, 2), buf, 5,
+ &actual, 1000);
+
+ udev->request_mac_addr = 1;
+
+ return ret;
+}
+
+static struct usb_tx *alloc_tx_struct(int len)
+{
+ struct usb_tx *t = NULL;
+ int ret = 0;
+
+ t = kzalloc(sizeof(struct usb_tx), GFP_ATOMIC);
+ if (!t) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ t->urb = usb_alloc_urb(0, GFP_ATOMIC);
+ if (!(len % 512))
+ len++;
+
+ t->buf = kmalloc(len, GFP_ATOMIC);
+ if (!t->urb || !t->buf) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+out:
+ if (ret < 0) {
+ if (t) {
+ usb_free_urb(t->urb);
+ kfree(t->buf);
+ kfree(t);
+ }
+ return NULL;
+ }
+
+ return t;
+}
+
+static struct usb_tx_sdu *alloc_tx_sdu_struct(void)
+{
+ struct usb_tx_sdu *t_sdu;
+
+ t_sdu = kzalloc(sizeof(struct usb_tx_sdu), GFP_KERNEL);
+ if (!t_sdu)
+ return NULL;
+
+ t_sdu->buf = kmalloc(SDU_BUF_SIZE, GFP_KERNEL);
+ if (!t_sdu->buf) {
+ kfree(t_sdu);
+ return NULL;
+ }
+
+ return t_sdu;
+}
+
+static void free_tx_struct(struct usb_tx *t)
+{
+ if (t) {
+ usb_free_urb(t->urb);
+ kfree(t->buf);
+ kfree(t);
+ }
+}
+
+static void free_tx_sdu_struct(struct usb_tx_sdu *t_sdu)
+{
+ if (t_sdu) {
+ kfree(t_sdu->buf);
+ kfree(t_sdu);
+ }
+}
+
+static struct usb_tx_sdu *get_tx_sdu_struct(struct tx_cxt *tx, int *no_spc)
+{
+ struct usb_tx_sdu *t_sdu;
+
+ if (list_empty(&tx->free_list))
+ return NULL;
+
+ t_sdu = list_entry(tx->free_list.next, struct usb_tx_sdu, list);
+ list_del(&t_sdu->list);
+
+ tx->avail_count--;
+
+ *no_spc = list_empty(&tx->free_list) ? 1 : 0;
+
+ return t_sdu;
+}
+
+static void put_tx_struct(struct tx_cxt *tx, struct usb_tx_sdu *t_sdu)
+{
+ list_add_tail(&t_sdu->list, &tx->free_list);
+ tx->avail_count++;
+}
+
+static struct usb_rx *alloc_rx_struct(void)
+{
+ struct usb_rx *r = NULL;
+ int ret = 0;
+
+ r = kmalloc(sizeof(struct usb_rx), GFP_KERNEL);
+ if (!r) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ r->urb = usb_alloc_urb(0, GFP_KERNEL);
+ r->buf = kmalloc(RX_BUF_SIZE, GFP_KERNEL);
+ if (!r->urb || !r->buf) {
+ ret = -ENOMEM;
+ goto out;
+ }
+out:
+
+ if (ret < 0) {
+ if (r) {
+ usb_free_urb(r->urb);
+ kfree(r->buf);
+ kfree(r);
+ }
+ return NULL;
+ }
+
+ return r;
+}
+
+static void free_rx_struct(struct usb_rx *r)
+{
+ if (r) {
+ usb_free_urb(r->urb);
+ kfree(r->buf);
+ kfree(r);
+ }
+}
+
+static struct usb_rx *get_rx_struct(struct rx_cxt *rx, int *no_spc)
+{
+ struct usb_rx *r;
+ unsigned long flags;
+
+ spin_lock_irqsave(&rx->rx_lock, flags);
+
+ if (list_empty(&rx->free_list)) {
+ spin_unlock_irqrestore(&rx->rx_lock, flags);
+ return NULL;
+ }
+
+ r = list_entry(rx->free_list.next, struct usb_rx, free_list);
+ list_del(&r->free_list);
+
+ rx->avail_count--;
+
+ *no_spc = list_empty(&rx->free_list) ? 1 : 0;
+
+ spin_unlock_irqrestore(&rx->rx_lock, flags);
+
+ return r;
+}
+
+static void put_rx_struct(struct rx_cxt *rx, struct usb_rx *r)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&rx->rx_lock, flags);
+
+ list_add_tail(&r->free_list, &rx->free_list);
+ rx->avail_count++;
+
+ spin_unlock_irqrestore(&rx->rx_lock, flags);
+}
+
+static void release_usb(struct lte_udev *udev)
+{
+ struct rx_cxt *rx = &udev->rx;
+ struct tx_cxt *tx = &udev->tx;
+ struct usb_tx *t, *t_next;
+ struct usb_rx *r, *r_next;
+ struct usb_tx_sdu *t_sdu, *t_sdu_next;
+ unsigned long flags;
+
+ spin_lock_irqsave(&tx->lock, flags);
+ list_for_each_entry_safe(t_sdu, t_sdu_next, &tx->sdu_list, list) {
+ list_del(&t_sdu->list);
+ free_tx_sdu_struct(t_sdu);
+ }
+
+ list_for_each_entry_safe(t, t_next, &tx->hci_list, list) {
+ list_del(&t->list);
+ free_tx_struct(t);
+ }
+
+ list_for_each_entry_safe(t_sdu, t_sdu_next, &tx->free_list, list) {
+ list_del(&t_sdu->list);
+ free_tx_sdu_struct(t_sdu);
+ }
+ spin_unlock_irqrestore(&tx->lock, flags);
+
+ spin_lock_irqsave(&rx->submit_lock, flags);
+ list_for_each_entry_safe(r, r_next, &rx->rx_submit_list,
+ rx_submit_list) {
+ spin_unlock_irqrestore(&rx->submit_lock, flags);
+ usb_kill_urb(r->urb);
+ spin_lock_irqsave(&rx->submit_lock, flags);
+ }
+ spin_unlock_irqrestore(&rx->submit_lock, flags);
+
+ spin_lock_irqsave(&rx->rx_lock, flags);
+ list_for_each_entry_safe(r, r_next, &rx->free_list, free_list) {
+ list_del(&r->free_list);
+ free_rx_struct(r);
+ }
+ spin_unlock_irqrestore(&rx->rx_lock, flags);
+
+ spin_lock_irqsave(&rx->to_host_lock, flags);
+ list_for_each_entry_safe(r, r_next, &rx->to_host_list, to_host_list) {
+ if (r->index == (void *)udev) {
+ list_del(&r->to_host_list);
+ free_rx_struct(r);
+ }
+ }
+ spin_unlock_irqrestore(&rx->to_host_lock, flags);
+}
+
+static int init_usb(struct lte_udev *udev)
+{
+ int ret = 0;
+ int i;
+ struct tx_cxt *tx = &udev->tx;
+ struct rx_cxt *rx = &udev->rx;
+ struct usb_tx_sdu *t_sdu = NULL;
+ struct usb_rx *r = NULL;
+
+ udev->send_complete = 1;
+ udev->tx_stop = 0;
+ udev->request_mac_addr = 0;
+ udev->usb_state = PM_NORMAL;
+
+ INIT_LIST_HEAD(&tx->sdu_list);
+ INIT_LIST_HEAD(&tx->hci_list);
+ INIT_LIST_HEAD(&tx->free_list);
+ INIT_LIST_HEAD(&rx->rx_submit_list);
+ INIT_LIST_HEAD(&rx->free_list);
+ INIT_LIST_HEAD(&rx->to_host_list);
+ spin_lock_init(&tx->lock);
+ spin_lock_init(&rx->rx_lock);
+ spin_lock_init(&rx->submit_lock);
+ spin_lock_init(&rx->to_host_lock);
+
+ tx->avail_count = 0;
+ rx->avail_count = 0;
+
+ udev->rx_cb = NULL;
+
+ for (i = 0; i < MAX_NUM_SDU_BUF; i++) {
+ t_sdu = alloc_tx_sdu_struct();
+ if (t_sdu == NULL) {
+ ret = -ENOMEM;
+ goto fail;
+ }
+
+ list_add(&t_sdu->list, &tx->free_list);
+ tx->avail_count++;
+ }
+
+ for (i = 0; i < MAX_RX_SUBMIT_COUNT*2; i++) {
+ r = alloc_rx_struct();
+ if (r == NULL) {
+ ret = -ENOMEM;
+ goto fail;
+ }
+
+ list_add(&r->free_list, &rx->free_list);
+ rx->avail_count++;
+ }
+ INIT_DELAYED_WORK(&udev->work_tx, do_tx);
+ INIT_DELAYED_WORK(&udev->work_rx, do_rx);
+ return 0;
+fail:
+ release_usb(udev);
+ return ret;
+}
+
+static int set_mac_address(u8 *data, void *arg)
+{
+ struct phy_dev *phy_dev = (struct phy_dev *)arg;
+ struct lte_udev *udev = phy_dev->priv_dev;
+ struct tlv *tlv = (struct tlv *)data;
+ u8 mac_address[ETH_ALEN] = {0, };
+
+ if (tlv->type == MAC_ADDRESS && udev->request_mac_addr) {
+ memcpy(mac_address, tlv->data, tlv->len);
+
+ if (register_lte_device(phy_dev,
+ &udev->intf->dev, mac_address) < 0)
+ pr_err("register lte device failed\n");
+
+ udev->request_mac_addr = 0;
+
+ return 1;
+ }
+
+ return 0;
+}
+
+static void do_rx(struct work_struct *work)
+{
+ struct lte_udev *udev =
+ container_of(work, struct lte_udev, work_rx.work);
+ struct rx_cxt *rx = &udev->rx;
+ struct usb_rx *r;
+ struct hci_packet *hci;
+ struct phy_dev *phy_dev;
+ u16 cmd_evt;
+ int ret;
+ unsigned long flags;
+
+ while (1) {
+ spin_lock_irqsave(&rx->to_host_lock, flags);
+ if (list_empty(&rx->to_host_list)) {
+ spin_unlock_irqrestore(&rx->to_host_lock, flags);
+ break;
+ }
+ r = list_entry(rx->to_host_list.next,
+ struct usb_rx, to_host_list);
+ list_del(&r->to_host_list);
+ spin_unlock_irqrestore(&rx->to_host_lock, flags);
+
+ phy_dev = (struct phy_dev *)r->cb_data;
+ udev = (struct lte_udev *)phy_dev->priv_dev;
+ hci = (struct hci_packet *)r->buf;
+ cmd_evt = gdm_dev16_to_cpu(&udev->gdm_ed, hci->cmd_evt);
+
+ switch (cmd_evt) {
+ case LTE_GET_INFORMATION_RESULT:
+ if (set_mac_address(hci->data, r->cb_data) == 0) {
+ ret = r->callback(r->cb_data,
+ r->buf,
+ r->urb->actual_length,
+ KERNEL_THREAD);
+ }
+ break;
+
+ default:
+ if (r->callback) {
+ ret = r->callback(r->cb_data,
+ r->buf,
+ r->urb->actual_length,
+ KERNEL_THREAD);
+
+ if (ret == -EAGAIN)
+ pr_err("failed to send received data\n");
+ }
+ break;
+ }
+
+ put_rx_struct(rx, r);
+
+ gdm_usb_recv(udev,
+ r->callback,
+ r->cb_data,
+ USB_COMPLETE);
+ }
+}
+
+static void remove_rx_submit_list(struct usb_rx *r, struct rx_cxt *rx)
+{
+ unsigned long flags;
+ struct usb_rx *r_remove, *r_remove_next;
+
+ spin_lock_irqsave(&rx->submit_lock, flags);
+ list_for_each_entry_safe(r_remove, r_remove_next,
+ &rx->rx_submit_list, rx_submit_list) {
+ if (r == r_remove) {
+ list_del(&r->rx_submit_list);
+ break;
+ }
+ }
+ spin_unlock_irqrestore(&rx->submit_lock, flags);
+}
+
+static void gdm_usb_rcv_complete(struct urb *urb)
+{
+ struct usb_rx *r = urb->context;
+ struct rx_cxt *rx = r->rx;
+ unsigned long flags;
+ struct lte_udev *udev = container_of(r->rx, struct lte_udev, rx);
+ struct usb_device *usbdev = udev->usbdev;
+
+ remove_rx_submit_list(r, rx);
+
+ if (!urb->status && r->callback) {
+ spin_lock_irqsave(&rx->to_host_lock, flags);
+ list_add_tail(&r->to_host_list, &rx->to_host_list);
+ queue_work(usb_rx_wq, &udev->work_rx.work);
+ spin_unlock_irqrestore(&rx->to_host_lock, flags);
+ } else {
+ if (urb->status && udev->usb_state == PM_NORMAL)
+ dev_err(&urb->dev->dev, "%s: urb status error %d\n",
+ __func__, urb->status);
+
+ put_rx_struct(rx, r);
+ }
+
+ usb_mark_last_busy(usbdev);
+}
+
+static int gdm_usb_recv(void *priv_dev,
+ int (*cb)(void *cb_data,
+ void *data, int len, int context),
+ void *cb_data,
+ int context)
+{
+ struct lte_udev *udev = priv_dev;
+ struct usb_device *usbdev = udev->usbdev;
+ struct rx_cxt *rx = &udev->rx;
+ struct usb_rx *r;
+ int no_spc;
+ int ret;
+ unsigned long flags;
+
+ if (!udev->usbdev) {
+ pr_err("invalid device\n");
+ return -ENODEV;
+ }
+
+ r = get_rx_struct(rx, &no_spc);
+ if (!r) {
+ pr_err("Out of Memory\n");
+ return -ENOMEM;
+ }
+
+ udev->rx_cb = cb;
+ r->callback = cb;
+ r->cb_data = cb_data;
+ r->index = (void *)udev;
+ r->rx = rx;
+
+ usb_fill_bulk_urb(r->urb,
+ usbdev,
+ usb_rcvbulkpipe(usbdev, 0x83),
+ r->buf,
+ RX_BUF_SIZE,
+ gdm_usb_rcv_complete,
+ r);
+
+ spin_lock_irqsave(&rx->submit_lock, flags);
+ list_add_tail(&r->rx_submit_list, &rx->rx_submit_list);
+ spin_unlock_irqrestore(&rx->submit_lock, flags);
+
+ if (context == KERNEL_THREAD)
+ ret = usb_submit_urb(r->urb, GFP_KERNEL);
+ else
+ ret = usb_submit_urb(r->urb, GFP_ATOMIC);
+
+ if (ret) {
+ spin_lock_irqsave(&rx->submit_lock, flags);
+ list_del(&r->rx_submit_list);
+ spin_unlock_irqrestore(&rx->submit_lock, flags);
+
+ pr_err("usb_submit_urb failed (%p)\n", r);
+ put_rx_struct(rx, r);
+ }
+
+ return ret;
+}
+
+static void gdm_usb_send_complete(struct urb *urb)
+{
+ struct usb_tx *t = urb->context;
+ struct tx_cxt *tx = t->tx;
+ struct lte_udev *udev = container_of(tx, struct lte_udev, tx);
+ unsigned long flags;
+
+ if (urb->status == -ECONNRESET) {
+ dev_info(&urb->dev->dev, "CONNRESET\n");
+ return;
+ }
+
+ if (t->callback)
+ t->callback(t->cb_data);
+
+ free_tx_struct(t);
+
+ spin_lock_irqsave(&tx->lock, flags);
+ udev->send_complete = 1;
+ queue_work(usb_tx_wq, &udev->work_tx.work);
+ spin_unlock_irqrestore(&tx->lock, flags);
+}
+
+static int send_tx_packet(struct usb_device *usbdev, struct usb_tx *t, u32 len)
+{
+ int ret = 0;
+
+ if (!(len%512))
+ len++;
+
+ usb_fill_bulk_urb(t->urb,
+ usbdev,
+ usb_sndbulkpipe(usbdev, 2),
+ t->buf,
+ len,
+ gdm_usb_send_complete,
+ t);
+
+ ret = usb_submit_urb(t->urb, GFP_ATOMIC);
+
+ if (ret)
+ dev_err(&usbdev->dev, "usb_submit_urb failed: %d\n",
+ ret);
+
+ usb_mark_last_busy(usbdev);
+
+ return ret;
+}
+
+static u32 packet_aggregation(struct lte_udev *udev, u8 *send_buf)
+{
+ struct tx_cxt *tx = &udev->tx;
+ struct usb_tx_sdu *t_sdu = NULL;
+ struct multi_sdu *multi_sdu = (struct multi_sdu *)send_buf;
+ u16 send_len = 0;
+ u16 num_packet = 0;
+ unsigned long flags;
+
+ multi_sdu->cmd_evt = gdm_cpu_to_dev16(&udev->gdm_ed, LTE_TX_MULTI_SDU);
+
+ while (num_packet < MAX_PACKET_IN_MULTI_SDU) {
+ spin_lock_irqsave(&tx->lock, flags);
+ if (list_empty(&tx->sdu_list)) {
+ spin_unlock_irqrestore(&tx->lock, flags);
+ break;
+ }
+
+ t_sdu = list_entry(tx->sdu_list.next, struct usb_tx_sdu, list);
+ if (send_len + t_sdu->len > MAX_SDU_SIZE) {
+ spin_unlock_irqrestore(&tx->lock, flags);
+ break;
+ }
+
+ list_del(&t_sdu->list);
+ spin_unlock_irqrestore(&tx->lock, flags);
+
+ memcpy(multi_sdu->data + send_len, t_sdu->buf, t_sdu->len);
+
+ send_len += (t_sdu->len + 3) & 0xfffc;
+ num_packet++;
+
+ if (tx->avail_count > 10)
+ t_sdu->callback(t_sdu->cb_data);
+
+ spin_lock_irqsave(&tx->lock, flags);
+ put_tx_struct(tx, t_sdu);
+ spin_unlock_irqrestore(&tx->lock, flags);
+ }
+
+ multi_sdu->len = gdm_cpu_to_dev16(&udev->gdm_ed, send_len);
+ multi_sdu->num_packet = gdm_cpu_to_dev16(&udev->gdm_ed, num_packet);
+
+ return send_len + offsetof(struct multi_sdu, data);
+}
+
+static void do_tx(struct work_struct *work)
+{
+ struct lte_udev *udev =
+ container_of(work, struct lte_udev, work_tx.work);
+ struct usb_device *usbdev = udev->usbdev;
+ struct tx_cxt *tx = &udev->tx;
+ struct usb_tx *t = NULL;
+ int is_send = 0;
+ u32 len = 0;
+ unsigned long flags;
+
+ if (!usb_autopm_get_interface(udev->intf))
+ usb_autopm_put_interface(udev->intf);
+
+ if (udev->usb_state == PM_SUSPEND)
+ return;
+
+ spin_lock_irqsave(&tx->lock, flags);
+ if (!udev->send_complete) {
+ spin_unlock_irqrestore(&tx->lock, flags);
+ return;
+ }
+ udev->send_complete = 0;
+
+ if (!list_empty(&tx->hci_list)) {
+ t = list_entry(tx->hci_list.next, struct usb_tx, list);
+ list_del(&t->list);
+ len = t->len;
+ t->is_sdu = 0;
+ is_send = 1;
+ } else if (!list_empty(&tx->sdu_list)) {
+ if (udev->tx_stop) {
+ udev->send_complete = 1;
+ spin_unlock_irqrestore(&tx->lock, flags);
+ return;
+ }
+
+ t = alloc_tx_struct(TX_BUF_SIZE);
+ if (t == NULL) {
+ spin_unlock_irqrestore(&tx->lock, flags);
+ return;
+ }
+ t->callback = NULL;
+ t->tx = tx;
+ t->is_sdu = 1;
+ is_send = 1;
+ }
+
+ if (!is_send) {
+ udev->send_complete = 1;
+ spin_unlock_irqrestore(&tx->lock, flags);
+ return;
+ }
+ spin_unlock_irqrestore(&tx->lock, flags);
+
+ if (t->is_sdu)
+ len = packet_aggregation(udev, t->buf);
+
+ if (send_tx_packet(usbdev, t, len)) {
+ pr_err("send_tx_packet failed\n");
+ t->callback = NULL;
+ gdm_usb_send_complete(t->urb);
+ }
+}
+
+#define SDU_PARAM_LEN 12
+static int gdm_usb_sdu_send(void *priv_dev, void *data, int len,
+ unsigned int dftEpsId, unsigned int epsId,
+ void (*cb)(void *data), void *cb_data,
+ int dev_idx, int nic_type)
+{
+ struct lte_udev *udev = priv_dev;
+ struct tx_cxt *tx = &udev->tx;
+ struct usb_tx_sdu *t_sdu;
+ struct sdu *sdu = NULL;
+ unsigned long flags;
+ int no_spc = 0;
+ u16 send_len;
+
+ if (!udev->usbdev) {
+ pr_err("sdu send - invalid device\n");
+ return TX_NO_DEV;
+ }
+
+ spin_lock_irqsave(&tx->lock, flags);
+ t_sdu = get_tx_sdu_struct(tx, &no_spc);
+ spin_unlock_irqrestore(&tx->lock, flags);
+
+ if (t_sdu == NULL) {
+ pr_err("sdu send - free list empty\n");
+ return TX_NO_SPC;
+ }
+
+ sdu = (struct sdu *)t_sdu->buf;
+ sdu->cmd_evt = gdm_cpu_to_dev16(&udev->gdm_ed, LTE_TX_SDU);
+ if (nic_type == NIC_TYPE_ARP) {
+ send_len = len + SDU_PARAM_LEN;
+ memcpy(sdu->data, data, len);
+ } else {
+ send_len = len - ETH_HLEN;
+ send_len += SDU_PARAM_LEN;
+ memcpy(sdu->data, data+ETH_HLEN, len-ETH_HLEN);
+ }
+
+ sdu->len = gdm_cpu_to_dev16(&udev->gdm_ed, send_len);
+ sdu->dftEpsId = gdm_cpu_to_dev32(&udev->gdm_ed, dftEpsId);
+ sdu->bearer_ID = gdm_cpu_to_dev32(&udev->gdm_ed, epsId);
+ sdu->nic_type = gdm_cpu_to_dev32(&udev->gdm_ed, nic_type);
+
+ t_sdu->len = send_len + HCI_HEADER_SIZE;
+ t_sdu->callback = cb;
+ t_sdu->cb_data = cb_data;
+
+ spin_lock_irqsave(&tx->lock, flags);
+ list_add_tail(&t_sdu->list, &tx->sdu_list);
+ queue_work(usb_tx_wq, &udev->work_tx.work);
+ spin_unlock_irqrestore(&tx->lock, flags);
+
+ if (no_spc)
+ return TX_NO_BUFFER;
+
+ return 0;
+}
+
+static int gdm_usb_hci_send(void *priv_dev, void *data, int len,
+ void (*cb)(void *data), void *cb_data)
+{
+ struct lte_udev *udev = priv_dev;
+ struct tx_cxt *tx = &udev->tx;
+ struct usb_tx *t;
+ unsigned long flags;
+
+ if (!udev->usbdev) {
+ pr_err("hci send - invalid device\n");
+ return -ENODEV;
+ }
+
+ t = alloc_tx_struct(len);
+ if (t == NULL) {
+ pr_err("hci_send - out of memory\n");
+ return -ENOMEM;
+ }
+
+ memcpy(t->buf, data, len);
+ t->callback = cb;
+ t->cb_data = cb_data;
+ t->len = len;
+ t->tx = tx;
+ t->is_sdu = 0;
+
+ spin_lock_irqsave(&tx->lock, flags);
+ list_add_tail(&t->list, &tx->hci_list);
+ queue_work(usb_tx_wq, &udev->work_tx.work);
+ spin_unlock_irqrestore(&tx->lock, flags);
+
+ return 0;
+}
+
+static struct gdm_endian *gdm_usb_get_endian(void *priv_dev)
+{
+ struct lte_udev *udev = priv_dev;
+
+ return &udev->gdm_ed;
+}
+
+static int gdm_usb_probe(struct usb_interface *intf,
+ const struct usb_device_id *id)
+{
+ int ret = 0;
+ struct phy_dev *phy_dev = NULL;
+ struct lte_udev *udev = NULL;
+ u16 idVendor, idProduct;
+ int bInterfaceNumber;
+ struct usb_device *usbdev = interface_to_usbdev(intf);
+
+ bInterfaceNumber = intf->cur_altsetting->desc.bInterfaceNumber;
+ idVendor = __le16_to_cpu(usbdev->descriptor.idVendor);
+ idProduct = __le16_to_cpu(usbdev->descriptor.idProduct);
+
+ pr_info("net vid = 0x%04x pid = 0x%04x\n", idVendor, idProduct);
+
+ if (bInterfaceNumber > NETWORK_INTERFACE) {
+ pr_info("not a network device\n");
+ return -ENODEV;
+ }
+
+ phy_dev = kzalloc(sizeof(struct phy_dev), GFP_KERNEL);
+ if (!phy_dev)
+ return -ENOMEM;
+
+ udev = kzalloc(sizeof(struct lte_udev), GFP_KERNEL);
+ if (!udev) {
+ ret = -ENOMEM;
+ goto err_udev;
+ }
+
+ phy_dev->priv_dev = (void *)udev;
+ phy_dev->send_hci_func = gdm_usb_hci_send;
+ phy_dev->send_sdu_func = gdm_usb_sdu_send;
+ phy_dev->rcv_func = gdm_usb_recv;
+ phy_dev->get_endian = gdm_usb_get_endian;
+
+ udev->usbdev = usbdev;
+ ret = init_usb(udev);
+ if (ret < 0) {
+ dev_err(intf->usb_dev, "init_usb func failed\n");
+ goto err_init_usb;
+ }
+ udev->intf = intf;
+
+ intf->needs_remote_wakeup = 1;
+ usb_enable_autosuspend(usbdev);
+ pm_runtime_set_autosuspend_delay(&usbdev->dev, AUTO_SUSPEND_TIMER);
+
+ /* List up hosts with big endians, otherwise,
+ * defaults to little endian
+ */
+ if (idProduct == PID_GDM7243)
+ gdm_set_endian(&udev->gdm_ed, ENDIANNESS_BIG);
+ else
+ gdm_set_endian(&udev->gdm_ed, ENDIANNESS_LITTLE);
+
+ ret = request_mac_address(udev);
+ if (ret < 0) {
+ dev_err(intf->usb_dev, "request Mac address failed\n");
+ goto err_mac_address;
+ }
+
+ start_rx_proc(phy_dev);
+ usb_get_dev(usbdev);
+ usb_set_intfdata(intf, phy_dev);
+
+ return 0;
+
+err_mac_address:
+ release_usb(udev);
+err_init_usb:
+ kfree(udev);
+err_udev:
+ kfree(phy_dev);
+
+ return ret;
+}
+
+static void gdm_usb_disconnect(struct usb_interface *intf)
+{
+ struct phy_dev *phy_dev;
+ struct lte_udev *udev;
+ u16 idVendor, idProduct;
+ struct usb_device *usbdev;
+
+ usbdev = interface_to_usbdev(intf);
+
+ idVendor = __le16_to_cpu(usbdev->descriptor.idVendor);
+ idProduct = __le16_to_cpu(usbdev->descriptor.idProduct);
+
+ phy_dev = usb_get_intfdata(intf);
+
+ udev = phy_dev->priv_dev;
+ unregister_lte_device(phy_dev);
+
+ release_usb(udev);
+
+ kfree(udev);
+ udev = NULL;
+
+ kfree(phy_dev);
+ phy_dev = NULL;
+
+ usb_put_dev(usbdev);
+}
+
+static int gdm_usb_suspend(struct usb_interface *intf, pm_message_t pm_msg)
+{
+ struct phy_dev *phy_dev;
+ struct lte_udev *udev;
+ struct rx_cxt *rx;
+ struct usb_rx *r;
+ struct usb_rx *r_next;
+ unsigned long flags;
+
+ phy_dev = usb_get_intfdata(intf);
+ udev = phy_dev->priv_dev;
+ rx = &udev->rx;
+ if (udev->usb_state != PM_NORMAL) {
+ dev_err(intf->usb_dev, "usb suspend - invalid state\n");
+ return -1;
+ }
+
+ udev->usb_state = PM_SUSPEND;
+
+ spin_lock_irqsave(&rx->submit_lock, flags);
+ list_for_each_entry_safe(r, r_next, &rx->rx_submit_list,
+ rx_submit_list) {
+ spin_unlock_irqrestore(&rx->submit_lock, flags);
+ usb_kill_urb(r->urb);
+ spin_lock_irqsave(&rx->submit_lock, flags);
+ }
+ spin_unlock_irqrestore(&rx->submit_lock, flags);
+
+ return 0;
+}
+
+static int gdm_usb_resume(struct usb_interface *intf)
+{
+ struct phy_dev *phy_dev;
+ struct lte_udev *udev;
+ struct tx_cxt *tx;
+ struct rx_cxt *rx;
+ unsigned long flags;
+ int issue_count;
+ int i;
+
+ phy_dev = usb_get_intfdata(intf);
+ udev = phy_dev->priv_dev;
+ rx = &udev->rx;
+
+ if (udev->usb_state != PM_SUSPEND) {
+ dev_err(intf->usb_dev, "usb resume - invalid state\n");
+ return -1;
+ }
+ udev->usb_state = PM_NORMAL;
+
+ spin_lock_irqsave(&rx->rx_lock, flags);
+ issue_count = rx->avail_count - MAX_RX_SUBMIT_COUNT;
+ spin_unlock_irqrestore(&rx->rx_lock, flags);
+
+ if (issue_count >= 0) {
+ for (i = 0; i < issue_count; i++)
+ gdm_usb_recv(phy_dev->priv_dev,
+ udev->rx_cb,
+ phy_dev,
+ USB_COMPLETE);
+ }
+
+ tx = &udev->tx;
+ spin_lock_irqsave(&tx->lock, flags);
+ queue_work(usb_tx_wq, &udev->work_tx.work);
+ spin_unlock_irqrestore(&tx->lock, flags);
+
+ return 0;
+}
+
+static struct usb_driver gdm_usb_lte_driver = {
+ .name = "gdm_lte",
+ .probe = gdm_usb_probe,
+ .disconnect = gdm_usb_disconnect,
+ .id_table = id_table,
+ .supports_autosuspend = 1,
+ .suspend = gdm_usb_suspend,
+ .resume = gdm_usb_resume,
+ .reset_resume = gdm_usb_resume,
+};
+
+static int __init gdm_usb_lte_init(void)
+{
+ if (gdm_lte_event_init() < 0) {
+ pr_err("error creating event\n");
+ return -1;
+ }
+
+ usb_tx_wq = create_workqueue("usb_tx_wq");
+ if (usb_tx_wq == NULL)
+ return -1;
+
+ usb_rx_wq = create_workqueue("usb_rx_wq");
+ if (usb_rx_wq == NULL)
+ return -1;
+
+ return usb_register(&gdm_usb_lte_driver);
+}
+
+static void __exit gdm_usb_lte_exit(void)
+{
+ gdm_lte_event_exit();
+
+ usb_deregister(&gdm_usb_lte_driver);
+
+ if (usb_tx_wq) {
+ flush_workqueue(usb_tx_wq);
+ destroy_workqueue(usb_tx_wq);
+ }
+
+ if (usb_rx_wq) {
+ flush_workqueue(usb_rx_wq);
+ destroy_workqueue(usb_rx_wq);
+ }
+}
+
+module_init(gdm_usb_lte_init);
+module_exit(gdm_usb_lte_exit);
+
+MODULE_VERSION(DRIVER_VERSION);
+MODULE_DESCRIPTION("GCT LTE USB Device Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/gdm724x/gdm_usb.h b/drivers/staging/gdm724x/gdm_usb.h
new file mode 100644
index 000000000..e6486e71a
--- /dev/null
+++ b/drivers/staging/gdm724x/gdm_usb.h
@@ -0,0 +1,109 @@
+/*
+ * Copyright (c) 2012 GCT Semiconductor, Inc. All rights reserved.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ */
+
+#ifndef _GDM_USB_H_
+#define _GDM_USB_H_
+
+#include <linux/types.h>
+#include <linux/usb.h>
+#include <linux/list.h>
+#include <linux/time.h>
+
+#include "gdm_endian.h"
+#include "hci_packet.h"
+
+#define PM_NORMAL 0
+#define PM_SUSPEND 1
+#define AUTO_SUSPEND_TIMER 5000 /* ms */
+
+#define RX_BUF_SIZE (1024*32)
+#define TX_BUF_SIZE (1024*32)
+#define SDU_BUF_SIZE 2048
+#define MAX_SDU_SIZE (1024*30)
+#define MAX_PACKET_IN_MULTI_SDU 256
+
+#define VID_GCT 0x1076
+#define PID_GDM7240 0x8000
+#define PID_GDM7243 0x9000
+
+#define NETWORK_INTERFACE 1
+#define USB_SC_SCSI 0x06
+#define USB_PR_BULK 0x50
+
+#define MAX_NUM_SDU_BUF 64
+
+struct usb_tx {
+ struct list_head list;
+ struct urb *urb;
+ u8 *buf;
+ u32 len;
+ void (*callback)(void *cb_data);
+ void *cb_data;
+ struct tx_cxt *tx;
+ u8 is_sdu;
+};
+
+struct usb_tx_sdu {
+ struct list_head list;
+ u8 *buf;
+ u32 len;
+ void (*callback)(void *cb_data);
+ void *cb_data;
+};
+
+struct usb_rx {
+ struct list_head to_host_list;
+ struct list_head free_list;
+ struct list_head rx_submit_list;
+ struct rx_cxt *rx;
+ struct urb *urb;
+ u8 *buf;
+ int (*callback)(void *cb_data, void *data, int len, int context);
+ void *cb_data;
+ void *index;
+};
+
+struct tx_cxt {
+ struct list_head sdu_list;
+ struct list_head hci_list;
+ struct list_head free_list;
+ u32 avail_count;
+ spinlock_t lock;
+};
+
+struct rx_cxt {
+ struct list_head to_host_list;
+ struct list_head rx_submit_list;
+ struct list_head free_list;
+ u32 avail_count;
+ spinlock_t to_host_lock;
+ spinlock_t rx_lock;
+ spinlock_t submit_lock;
+};
+
+struct lte_udev {
+ struct usb_device *usbdev;
+ struct gdm_endian gdm_ed;
+ struct tx_cxt tx;
+ struct rx_cxt rx;
+ struct delayed_work work_tx;
+ struct delayed_work work_rx;
+ u8 send_complete;
+ u8 tx_stop;
+ struct usb_interface *intf;
+ int (*rx_cb)(void *cb_data, void *data, int len, int context);
+ int usb_state;
+ u8 request_mac_addr;
+};
+
+#endif /* _GDM_USB_H_ */
diff --git a/drivers/staging/gdm724x/hci.h b/drivers/staging/gdm724x/hci.h
new file mode 100644
index 000000000..9a591b0db
--- /dev/null
+++ b/drivers/staging/gdm724x/hci.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2012 GCT Semiconductor, Inc. All rights reserved.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ */
+
+#ifndef _HCI_H_
+#define _HCI_H_
+
+#define LTE_GET_INFORMATION 0x3002
+#define LTE_GET_INFORMATION_RESULT 0xB003
+ #define MAC_ADDRESS 0xA2
+
+#define LTE_LINK_ON_OFF_INDICATION 0xB133
+#define LTE_PDN_TABLE_IND 0xB143
+
+#define LTE_TX_SDU 0x3200
+#define LTE_RX_SDU 0xB201
+#define LTE_TX_MULTI_SDU 0x3202
+#define LTE_RX_MULTI_SDU 0xB203
+
+#define LTE_DL_SDU_FLOW_CONTROL 0x3305
+#define LTE_UL_SDU_FLOW_CONTROL 0xB306
+
+#define LTE_AT_CMD_TO_DEVICE 0x3307
+#define LTE_AT_CMD_FROM_DEVICE 0xB308
+
+#define LTE_SDIO_DM_SEND_PKT 0x3312
+#define LTE_SDIO_DM_RECV_PKT 0xB313
+
+#define LTE_NV_RESTORE_REQUEST 0xB30C
+#define LTE_NV_RESTORE_RESPONSE 0x330D
+#define LTE_NV_SAVE_REQUEST 0xB30E
+ #define NV_TYPE_LTE_INFO 0x00
+ #define NV_TYPE_BOARD_CONFIG 0x01
+ #define NV_TYPE_RF_CAL 0x02
+ #define NV_TYPE_TEMP 0x03
+ #define NV_TYPE_NET_INFO 0x04
+ #define NV_TYPE_SAFETY_INFO 0x05
+ #define NV_TYPE_CDMA_CAL 0x06
+ #define NV_TYPE_VENDOR 0x07
+ #define NV_TYPE_ALL 0xff
+#define LTE_NV_SAVE_RESPONSE 0x330F
+
+#define LTE_AT_CMD_TO_DEVICE_EXT 0x3323
+#define LTE_AT_CMD_FROM_DEVICE_EXT 0xB324
+
+#endif /* _HCI_H_ */
diff --git a/drivers/staging/gdm724x/hci_packet.h b/drivers/staging/gdm724x/hci_packet.h
new file mode 100644
index 000000000..7fba8a687
--- /dev/null
+++ b/drivers/staging/gdm724x/hci_packet.h
@@ -0,0 +1,93 @@
+/*
+ * Copyright (c) 2012 GCT Semiconductor, Inc. All rights reserved.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ */
+
+#ifndef _HCI_PACKET_H_
+#define _HCI_PACKET_H_
+
+#define HCI_HEADER_SIZE 4
+
+/*
+ * The NIC type definition:
+ * For backward compatibility, lower 16 bits used as they were.
+ * Lower 16 bit: NIC_TYPE values
+ * Uppoer 16 bit: NIC_TYPE Flags
+ */
+#define NIC_TYPE_NIC0 0x00000010
+#define NIC_TYPE_NIC1 0x00000011
+#define NIC_TYPE_NIC2 0x00000012
+#define NIC_TYPE_NIC3 0x00000013
+#define NIC_TYPE_ARP 0x00000100
+#define NIC_TYPE_ICMPV6 0x00000200
+#define NIC_TYPE_MASK 0x0000FFFF
+#define NIC_TYPE_F_IPV4 0x00010000
+#define NIC_TYPE_F_IPV6 0x00020000
+#define NIC_TYPE_F_DHCP 0x00040000
+#define NIC_TYPE_F_NDP 0x00080000
+#define NIC_TYPE_F_VLAN 0x00100000
+
+struct hci_packet {
+ u16 cmd_evt;
+ u16 len;
+ u8 data[0];
+} __packed;
+
+struct tlv {
+ u8 type;
+ u8 len;
+ u8 *data[1];
+} __packed;
+
+struct sdu_header {
+ u16 cmd_evt;
+ u16 len;
+ u32 dftEpsId;
+ u32 bearer_ID;
+ u32 nic_type;
+} __packed;
+
+struct sdu {
+ u16 cmd_evt;
+ u16 len;
+ u32 dftEpsId;
+ u32 bearer_ID;
+ u32 nic_type;
+ u8 data[0];
+} __packed;
+
+struct multi_sdu {
+ u16 cmd_evt;
+ u16 len;
+ u16 num_packet;
+ u16 reserved;
+ u8 data[0];
+} __packed;
+
+struct hci_pdn_table_ind {
+ u16 cmd_evt;
+ u16 len;
+ u8 activate;
+ u32 dft_eps_id;
+ u32 nic_type;
+ u8 pdn_type;
+ u8 ipv4_addr[4];
+ u8 ipv6_intf_id[8];
+} __packed;
+
+struct hci_connect_ind {
+ u16 cmd_evt;
+ u16 len;
+ u32 connect;
+} __packed;
+
+
+#endif /* _HCI_PACKET_H_ */
diff --git a/drivers/staging/gdm724x/netlink_k.c b/drivers/staging/gdm724x/netlink_k.c
new file mode 100644
index 000000000..59a18304e
--- /dev/null
+++ b/drivers/staging/gdm724x/netlink_k.c
@@ -0,0 +1,150 @@
+/*
+ * Copyright (c) 2012 GCT Semiconductor, Inc. All rights reserved.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/export.h>
+#include <linux/etherdevice.h>
+#include <linux/netlink.h>
+#include <asm/byteorder.h>
+#include <net/sock.h>
+
+#include "netlink_k.h"
+
+#if defined(DEFINE_MUTEX)
+static DEFINE_MUTEX(netlink_mutex);
+#else
+static struct semaphore netlink_mutex;
+#define mutex_lock(x) down(x)
+#define mutex_unlock(x) up(x)
+#endif
+
+#define ND_MAX_GROUP 30
+#define ND_IFINDEX_LEN sizeof(int)
+#define ND_NLMSG_SPACE(len) (NLMSG_SPACE(len) + ND_IFINDEX_LEN)
+#define ND_NLMSG_DATA(nlh) ((void *)((char *)NLMSG_DATA(nlh) + \
+ ND_IFINDEX_LEN))
+#define ND_NLMSG_S_LEN(len) (len+ND_IFINDEX_LEN)
+#define ND_NLMSG_R_LEN(nlh) (nlh->nlmsg_len-ND_IFINDEX_LEN)
+#define ND_NLMSG_IFIDX(nlh) NLMSG_DATA(nlh)
+#define ND_MAX_MSG_LEN (1024 * 32)
+
+static void (*rcv_cb)(struct net_device *dev, u16 type, void *msg, int len);
+
+static void netlink_rcv_cb(struct sk_buff *skb)
+{
+ struct nlmsghdr *nlh;
+ struct net_device *dev;
+ u32 mlen;
+ void *msg;
+ int ifindex;
+
+ if (!rcv_cb) {
+ pr_err("nl cb - unregistered\n");
+ return;
+ }
+
+ if (skb->len < NLMSG_HDRLEN) {
+ pr_err("nl cb - invalid skb length\n");
+ return;
+ }
+
+ nlh = (struct nlmsghdr *)skb->data;
+
+ if (skb->len < nlh->nlmsg_len || nlh->nlmsg_len > ND_MAX_MSG_LEN) {
+ pr_err("nl cb - invalid length (%d,%d)\n",
+ skb->len, nlh->nlmsg_len);
+ return;
+ }
+
+ memcpy(&ifindex, ND_NLMSG_IFIDX(nlh), ND_IFINDEX_LEN);
+ msg = ND_NLMSG_DATA(nlh);
+ mlen = ND_NLMSG_R_LEN(nlh);
+
+ dev = dev_get_by_index(&init_net, ifindex);
+ if (dev) {
+ rcv_cb(dev, nlh->nlmsg_type, msg, mlen);
+ dev_put(dev);
+ } else {
+ pr_err("nl cb - dev (%d) not found\n", ifindex);
+ }
+}
+
+static void netlink_rcv(struct sk_buff *skb)
+{
+ mutex_lock(&netlink_mutex);
+ netlink_rcv_cb(skb);
+ mutex_unlock(&netlink_mutex);
+}
+
+struct sock *netlink_init(int unit,
+ void (*cb)(struct net_device *dev, u16 type, void *msg, int len))
+{
+ struct sock *sock;
+ struct netlink_kernel_cfg cfg = {
+ .input = netlink_rcv,
+ };
+
+#if !defined(DEFINE_MUTEX)
+ init_MUTEX(&netlink_mutex);
+#endif
+
+ sock = netlink_kernel_create(&init_net, unit, &cfg);
+
+ if (sock)
+ rcv_cb = cb;
+
+ return sock;
+}
+
+void netlink_exit(struct sock *sock)
+{
+ sock_release(sock->sk_socket);
+}
+
+int netlink_send(struct sock *sock, int group, u16 type, void *msg, int len)
+{
+ static u32 seq;
+ struct sk_buff *skb = NULL;
+ struct nlmsghdr *nlh;
+ int ret = 0;
+
+ if (group > ND_MAX_GROUP)
+ return -EINVAL;
+
+ if (!netlink_has_listeners(sock, group+1))
+ return -ESRCH;
+
+ skb = alloc_skb(NLMSG_SPACE(len), GFP_ATOMIC);
+ if (!skb)
+ return -ENOMEM;
+
+ seq++;
+
+ nlh = nlmsg_put(skb, 0, seq, type, len, 0);
+ memcpy(NLMSG_DATA(nlh), msg, len);
+ NETLINK_CB(skb).portid = 0;
+ NETLINK_CB(skb).dst_group = 0;
+
+ ret = netlink_broadcast(sock, skb, 0, group+1, GFP_ATOMIC);
+ if (!ret)
+ return len;
+
+ if (ret != -ESRCH)
+ pr_err("nl broadcast g=%d, t=%d, l=%d, r=%d\n",
+ group, type, len, ret);
+ else if (netlink_has_listeners(sock, group+1))
+ return -EAGAIN;
+
+ return ret;
+}
diff --git a/drivers/staging/gdm724x/netlink_k.h b/drivers/staging/gdm724x/netlink_k.h
new file mode 100644
index 000000000..589486d76
--- /dev/null
+++ b/drivers/staging/gdm724x/netlink_k.h
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2012 GCT Semiconductor, Inc. All rights reserved.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ */
+
+#ifndef _NETLINK_K_H
+#define _NETLINK_K_H
+
+#include <linux/netdevice.h>
+#include <net/sock.h>
+
+struct sock *netlink_init(int unit,
+ void (*cb)(struct net_device *dev, u16 type, void *msg, int len));
+void netlink_exit(struct sock *sock);
+int netlink_send(struct sock *sock, int group, u16 type, void *msg, int len);
+
+#endif /* _NETLINK_K_H_ */
diff --git a/drivers/staging/gdm72xx/Kconfig b/drivers/staging/gdm72xx/Kconfig
new file mode 100644
index 000000000..bf11a7fbf
--- /dev/null
+++ b/drivers/staging/gdm72xx/Kconfig
@@ -0,0 +1,63 @@
+#
+# GCT GDM72xx WiMAX driver configuration
+#
+
+menuconfig WIMAX_GDM72XX
+ tristate "GCT GDM72xx WiMAX support"
+ depends on NET && (USB || MMC)
+ help
+ Support a WiMAX module based on the GCT GDM72xx WiMAX chip.
+
+if WIMAX_GDM72XX
+
+config WIMAX_GDM72XX_QOS
+ bool "Enable QoS support"
+ default n
+ help
+ Enable Quality of Service support based on the data protocol of
+ transmitting packets.
+
+config WIMAX_GDM72XX_K_MODE
+ bool "Enable K mode"
+ default n
+ help
+ Enable support for proprietary functions for KT (Korea Telecom).
+
+config WIMAX_GDM72XX_WIMAX2
+ bool "Enable WiMAX2 support"
+ default n
+ help
+ Enable support for transmitting multiple packets (packet
+ aggregation) from the WiMAX module to the host processor.
+
+choice
+ prompt "Select interface"
+
+config WIMAX_GDM72XX_USB
+ bool "USB interface"
+ depends on (USB = y || USB = WIMAX_GDM72XX)
+ help
+ Select this option if the WiMAX module interfaces with the host
+ processor via USB.
+
+config WIMAX_GDM72XX_SDIO
+ bool "SDIO interface"
+ depends on (MMC = y || MMC = WIMAX_GDM72XX)
+ help
+ Select this option if the WiMAX module interfaces with the host
+ processor via SDIO.
+
+endchoice
+
+if WIMAX_GDM72XX_USB
+
+config WIMAX_GDM72XX_USB_PM
+ bool "Enable power management support"
+ depends on PM
+ help
+ Enable USB power management in order to reduce power consumption
+ while the interface is not in use.
+
+endif # WIMAX_GDM72XX_USB
+
+endif # WIMAX_GDM72XX
diff --git a/drivers/staging/gdm72xx/Makefile b/drivers/staging/gdm72xx/Makefile
new file mode 100644
index 000000000..35da7b90b
--- /dev/null
+++ b/drivers/staging/gdm72xx/Makefile
@@ -0,0 +1,6 @@
+obj-$(CONFIG_WIMAX_GDM72XX) := gdmwm.o
+
+gdmwm-y += gdm_wimax.o netlink_k.o
+gdmwm-$(CONFIG_WIMAX_GDM72XX_QOS) += gdm_qos.o
+gdmwm-$(CONFIG_WIMAX_GDM72XX_SDIO) += gdm_sdio.o sdio_boot.o
+gdmwm-$(CONFIG_WIMAX_GDM72XX_USB) += gdm_usb.o usb_boot.o
diff --git a/drivers/staging/gdm72xx/TODO b/drivers/staging/gdm72xx/TODO
new file mode 100644
index 000000000..62d0cd622
--- /dev/null
+++ b/drivers/staging/gdm72xx/TODO
@@ -0,0 +1,2 @@
+TODO:
+- Clean up coding style to meet kernel standard.
diff --git a/drivers/staging/gdm72xx/gdm_qos.c b/drivers/staging/gdm72xx/gdm_qos.c
new file mode 100644
index 000000000..96bf2bf87
--- /dev/null
+++ b/drivers/staging/gdm72xx/gdm_qos.c
@@ -0,0 +1,438 @@
+/*
+ * Copyright (c) 2012 GCT Semiconductor, Inc. All rights reserved.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/etherdevice.h>
+#include <asm/byteorder.h>
+
+#include <linux/ip.h>
+#include <linux/tcp.h>
+#include <linux/if_ether.h>
+
+#include "gdm_wimax.h"
+#include "hci.h"
+#include "gdm_qos.h"
+
+#define MAX_FREE_LIST_CNT 32
+static struct {
+ struct list_head head;
+ int cnt;
+ spinlock_t lock;
+} qos_free_list;
+
+static void init_qos_entry_list(void)
+{
+ qos_free_list.cnt = 0;
+ INIT_LIST_HEAD(&qos_free_list.head);
+ spin_lock_init(&qos_free_list.lock);
+}
+
+static void *alloc_qos_entry(void)
+{
+ struct qos_entry_s *entry;
+ unsigned long flags;
+
+ spin_lock_irqsave(&qos_free_list.lock, flags);
+ if (qos_free_list.cnt) {
+ entry = list_entry(qos_free_list.head.prev, struct qos_entry_s,
+ list);
+ list_del(&entry->list);
+ qos_free_list.cnt--;
+ spin_unlock_irqrestore(&qos_free_list.lock, flags);
+ return entry;
+ }
+ spin_unlock_irqrestore(&qos_free_list.lock, flags);
+
+ return kmalloc(sizeof(*entry), GFP_ATOMIC);
+}
+
+static void free_qos_entry(void *entry)
+{
+ struct qos_entry_s *qentry = (struct qos_entry_s *)entry;
+ unsigned long flags;
+
+ spin_lock_irqsave(&qos_free_list.lock, flags);
+ if (qos_free_list.cnt < MAX_FREE_LIST_CNT) {
+ list_add(&qentry->list, &qos_free_list.head);
+ qos_free_list.cnt++;
+ spin_unlock_irqrestore(&qos_free_list.lock, flags);
+ return;
+ }
+ spin_unlock_irqrestore(&qos_free_list.lock, flags);
+
+ kfree(entry);
+}
+
+static void free_qos_entry_list(struct list_head *free_list)
+{
+ struct qos_entry_s *entry, *n;
+ int total_free = 0;
+
+ list_for_each_entry_safe(entry, n, free_list, list) {
+ list_del(&entry->list);
+ kfree(entry);
+ total_free++;
+ }
+
+ pr_debug("%s: total_free_cnt=%d\n", __func__, total_free);
+}
+
+void gdm_qos_init(void *nic_ptr)
+{
+ struct nic *nic = nic_ptr;
+ struct qos_cb_s *qcb = &nic->qos;
+ int i;
+
+ for (i = 0; i < QOS_MAX; i++) {
+ INIT_LIST_HEAD(&qcb->qos_list[i]);
+ qcb->csr[i].qos_buf_count = 0;
+ qcb->csr[i].enabled = false;
+ }
+
+ qcb->qos_list_cnt = 0;
+ qcb->qos_null_idx = QOS_MAX-1;
+ qcb->qos_limit_size = 255;
+
+ spin_lock_init(&qcb->qos_lock);
+
+ init_qos_entry_list();
+}
+
+void gdm_qos_release_list(void *nic_ptr)
+{
+ struct nic *nic = nic_ptr;
+ struct qos_cb_s *qcb = &nic->qos;
+ unsigned long flags;
+ struct qos_entry_s *entry, *n;
+ struct list_head free_list;
+ int i;
+
+ INIT_LIST_HEAD(&free_list);
+
+ spin_lock_irqsave(&qcb->qos_lock, flags);
+
+ for (i = 0; i < QOS_MAX; i++) {
+ qcb->csr[i].qos_buf_count = 0;
+ qcb->csr[i].enabled = false;
+ }
+
+ qcb->qos_list_cnt = 0;
+ qcb->qos_null_idx = QOS_MAX-1;
+
+ for (i = 0; i < QOS_MAX; i++) {
+ list_for_each_entry_safe(entry, n, &qcb->qos_list[i], list) {
+ list_move_tail(&entry->list, &free_list);
+ }
+ }
+ spin_unlock_irqrestore(&qcb->qos_lock, flags);
+ free_qos_entry_list(&free_list);
+}
+
+static int chk_ipv4_rule(struct gdm_wimax_csr_s *csr, u8 *stream, u8 *port)
+{
+ int i;
+
+ if (csr->classifier_rule_en&IPTYPEOFSERVICE) {
+ if (((stream[1] & csr->ip2s_mask) < csr->ip2s_lo) ||
+ ((stream[1] & csr->ip2s_mask) > csr->ip2s_hi))
+ return 1;
+ }
+
+ if (csr->classifier_rule_en&PROTOCOL) {
+ if (stream[9] != csr->protocol)
+ return 1;
+ }
+
+ if (csr->classifier_rule_en&IPMASKEDSRCADDRESS) {
+ for (i = 0; i < 4; i++) {
+ if ((stream[12 + i] & csr->ipsrc_addrmask[i]) !=
+ (csr->ipsrc_addr[i] & csr->ipsrc_addrmask[i]))
+ return 1;
+ }
+ }
+
+ if (csr->classifier_rule_en&IPMASKEDDSTADDRESS) {
+ for (i = 0; i < 4; i++) {
+ if ((stream[16 + i] & csr->ipdst_addrmask[i]) !=
+ (csr->ipdst_addr[i] & csr->ipdst_addrmask[i]))
+ return 1;
+ }
+ }
+
+ if (csr->classifier_rule_en&PROTOCOLSRCPORTRANGE) {
+ i = ((port[0]<<8)&0xff00)+port[1];
+ if ((i < csr->srcport_lo) || (i > csr->srcport_hi))
+ return 1;
+ }
+
+ if (csr->classifier_rule_en&PROTOCOLDSTPORTRANGE) {
+ i = ((port[2]<<8)&0xff00)+port[3];
+ if ((i < csr->dstport_lo) || (i > csr->dstport_hi))
+ return 1;
+ }
+
+ return 0;
+}
+
+static int get_qos_index(struct nic *nic, u8 *iph, u8 *tcpudph)
+{
+ int ip_ver, i;
+ struct qos_cb_s *qcb = &nic->qos;
+
+ if (iph == NULL || tcpudph == NULL)
+ return -1;
+
+ ip_ver = (iph[0]>>4)&0xf;
+
+ if (ip_ver != 4)
+ return -1;
+
+ for (i = 0; i < QOS_MAX; i++) {
+ if (!qcb->csr[i].enabled)
+ continue;
+ if (!qcb->csr[i].classifier_rule_en)
+ continue;
+ if (chk_ipv4_rule(&qcb->csr[i], iph, tcpudph) == 0)
+ return i;
+ }
+
+ return -1;
+}
+
+static void extract_qos_list(struct nic *nic, struct list_head *head)
+{
+ struct qos_cb_s *qcb = &nic->qos;
+ struct qos_entry_s *entry;
+ int i;
+
+ INIT_LIST_HEAD(head);
+
+ for (i = 0; i < QOS_MAX; i++) {
+ if (!qcb->csr[i].enabled)
+ continue;
+ if (qcb->csr[i].qos_buf_count >= qcb->qos_limit_size)
+ continue;
+ if (list_empty(&qcb->qos_list[i]))
+ continue;
+
+ entry = list_entry(qcb->qos_list[i].prev, struct qos_entry_s,
+ list);
+
+ list_move_tail(&entry->list, head);
+ qcb->csr[i].qos_buf_count++;
+
+ if (!list_empty(&qcb->qos_list[i]))
+ netdev_warn(nic->netdev, "Index(%d) is piled!!\n", i);
+ }
+}
+
+static void send_qos_list(struct nic *nic, struct list_head *head)
+{
+ struct qos_entry_s *entry, *n;
+
+ list_for_each_entry_safe(entry, n, head, list) {
+ list_del(&entry->list);
+ gdm_wimax_send_tx(entry->skb, entry->dev);
+ free_qos_entry(entry);
+ }
+}
+
+int gdm_qos_send_hci_pkt(struct sk_buff *skb, struct net_device *dev)
+{
+ struct nic *nic = netdev_priv(dev);
+ int index;
+ struct qos_cb_s *qcb = &nic->qos;
+ unsigned long flags;
+ struct ethhdr *ethh = (struct ethhdr *)(skb->data + HCI_HEADER_SIZE);
+ struct iphdr *iph = (struct iphdr *)((char *)ethh + ETH_HLEN);
+ struct tcphdr *tcph;
+ struct qos_entry_s *entry = NULL;
+ struct list_head send_list;
+ int ret = 0;
+
+ tcph = (struct tcphdr *)iph + iph->ihl*4;
+
+ if (ethh->h_proto == cpu_to_be16(ETH_P_IP)) {
+ if (qcb->qos_list_cnt && !qos_free_list.cnt) {
+ entry = alloc_qos_entry();
+ entry->skb = skb;
+ entry->dev = dev;
+ netdev_dbg(dev, "qcb->qos_list_cnt=%d\n",
+ qcb->qos_list_cnt);
+ }
+
+ spin_lock_irqsave(&qcb->qos_lock, flags);
+ if (qcb->qos_list_cnt) {
+ index = get_qos_index(nic, (u8 *)iph, (u8 *)tcph);
+ if (index == -1)
+ index = qcb->qos_null_idx;
+
+ if (!entry) {
+ entry = alloc_qos_entry();
+ entry->skb = skb;
+ entry->dev = dev;
+ }
+
+ list_add_tail(&entry->list, &qcb->qos_list[index]);
+ extract_qos_list(nic, &send_list);
+ spin_unlock_irqrestore(&qcb->qos_lock, flags);
+ send_qos_list(nic, &send_list);
+ goto out;
+ }
+ spin_unlock_irqrestore(&qcb->qos_lock, flags);
+ if (entry)
+ free_qos_entry(entry);
+ }
+
+ ret = gdm_wimax_send_tx(skb, dev);
+out:
+ return ret;
+}
+
+static int get_csr(struct qos_cb_s *qcb, u32 sfid, int mode)
+{
+ int i;
+
+ for (i = 0; i < qcb->qos_list_cnt; i++) {
+ if (qcb->csr[i].sfid == sfid)
+ return i;
+ }
+
+ if (mode) {
+ for (i = 0; i < QOS_MAX; i++) {
+ if (!qcb->csr[i].enabled) {
+ qcb->csr[i].enabled = true;
+ qcb->qos_list_cnt++;
+ return i;
+ }
+ }
+ }
+ return -1;
+}
+
+#define QOS_CHANGE_DEL 0xFC
+#define QOS_ADD 0xFD
+#define QOS_REPORT 0xFE
+
+void gdm_recv_qos_hci_packet(void *nic_ptr, u8 *buf, int size)
+{
+ struct nic *nic = nic_ptr;
+ int i, index, pos;
+ u32 sfid;
+ u8 sub_cmd_evt;
+ struct qos_cb_s *qcb = &nic->qos;
+ struct qos_entry_s *entry, *n;
+ struct list_head send_list;
+ struct list_head free_list;
+ unsigned long flags;
+
+ sub_cmd_evt = (u8)buf[4];
+
+ if (sub_cmd_evt == QOS_REPORT) {
+ spin_lock_irqsave(&qcb->qos_lock, flags);
+ for (i = 0; i < qcb->qos_list_cnt; i++) {
+ sfid = ((buf[(i*5)+6]<<24)&0xff000000);
+ sfid += ((buf[(i*5)+7]<<16)&0xff0000);
+ sfid += ((buf[(i*5)+8]<<8)&0xff00);
+ sfid += (buf[(i*5)+9]);
+ index = get_csr(qcb, sfid, 0);
+ if (index == -1) {
+ spin_unlock_irqrestore(&qcb->qos_lock, flags);
+ netdev_err(nic->netdev, "QoS ERROR: No SF\n");
+ return;
+ }
+ qcb->csr[index].qos_buf_count = buf[(i*5)+10];
+ }
+
+ extract_qos_list(nic, &send_list);
+ spin_unlock_irqrestore(&qcb->qos_lock, flags);
+ send_qos_list(nic, &send_list);
+ return;
+ }
+
+ /* sub_cmd_evt == QOS_ADD || sub_cmd_evt == QOS_CHANG_DEL */
+ pos = 6;
+ sfid = ((buf[pos++]<<24)&0xff000000);
+ sfid += ((buf[pos++]<<16)&0xff0000);
+ sfid += ((buf[pos++]<<8)&0xff00);
+ sfid += (buf[pos++]);
+
+ index = get_csr(qcb, sfid, 1);
+ if (index == -1) {
+ netdev_err(nic->netdev,
+ "QoS ERROR: csr Update Error / Wrong index (%d)\n",
+ index);
+ return;
+ }
+
+ if (sub_cmd_evt == QOS_ADD) {
+ netdev_dbg(nic->netdev, "QOS_ADD SFID = 0x%x, index=%d\n",
+ sfid, index);
+
+ spin_lock_irqsave(&qcb->qos_lock, flags);
+ qcb->csr[index].sfid = sfid;
+ qcb->csr[index].classifier_rule_en = ((buf[pos++]<<8)&0xff00);
+ qcb->csr[index].classifier_rule_en += buf[pos++];
+ if (qcb->csr[index].classifier_rule_en == 0)
+ qcb->qos_null_idx = index;
+ qcb->csr[index].ip2s_mask = buf[pos++];
+ qcb->csr[index].ip2s_lo = buf[pos++];
+ qcb->csr[index].ip2s_hi = buf[pos++];
+ qcb->csr[index].protocol = buf[pos++];
+ qcb->csr[index].ipsrc_addrmask[0] = buf[pos++];
+ qcb->csr[index].ipsrc_addrmask[1] = buf[pos++];
+ qcb->csr[index].ipsrc_addrmask[2] = buf[pos++];
+ qcb->csr[index].ipsrc_addrmask[3] = buf[pos++];
+ qcb->csr[index].ipsrc_addr[0] = buf[pos++];
+ qcb->csr[index].ipsrc_addr[1] = buf[pos++];
+ qcb->csr[index].ipsrc_addr[2] = buf[pos++];
+ qcb->csr[index].ipsrc_addr[3] = buf[pos++];
+ qcb->csr[index].ipdst_addrmask[0] = buf[pos++];
+ qcb->csr[index].ipdst_addrmask[1] = buf[pos++];
+ qcb->csr[index].ipdst_addrmask[2] = buf[pos++];
+ qcb->csr[index].ipdst_addrmask[3] = buf[pos++];
+ qcb->csr[index].ipdst_addr[0] = buf[pos++];
+ qcb->csr[index].ipdst_addr[1] = buf[pos++];
+ qcb->csr[index].ipdst_addr[2] = buf[pos++];
+ qcb->csr[index].ipdst_addr[3] = buf[pos++];
+ qcb->csr[index].srcport_lo = ((buf[pos++]<<8)&0xff00);
+ qcb->csr[index].srcport_lo += buf[pos++];
+ qcb->csr[index].srcport_hi = ((buf[pos++]<<8)&0xff00);
+ qcb->csr[index].srcport_hi += buf[pos++];
+ qcb->csr[index].dstport_lo = ((buf[pos++]<<8)&0xff00);
+ qcb->csr[index].dstport_lo += buf[pos++];
+ qcb->csr[index].dstport_hi = ((buf[pos++]<<8)&0xff00);
+ qcb->csr[index].dstport_hi += buf[pos++];
+
+ qcb->qos_limit_size = 254/qcb->qos_list_cnt;
+ spin_unlock_irqrestore(&qcb->qos_lock, flags);
+ } else if (sub_cmd_evt == QOS_CHANGE_DEL) {
+ netdev_dbg(nic->netdev, "QOS_CHANGE_DEL SFID = 0x%x, index=%d\n",
+ sfid, index);
+
+ INIT_LIST_HEAD(&free_list);
+
+ spin_lock_irqsave(&qcb->qos_lock, flags);
+ qcb->csr[index].enabled = false;
+ qcb->qos_list_cnt--;
+ qcb->qos_limit_size = 254/qcb->qos_list_cnt;
+
+ list_for_each_entry_safe(entry, n, &qcb->qos_list[index],
+ list) {
+ list_move_tail(&entry->list, &free_list);
+ }
+ spin_unlock_irqrestore(&qcb->qos_lock, flags);
+ free_qos_entry_list(&free_list);
+ }
+}
diff --git a/drivers/staging/gdm72xx/gdm_qos.h b/drivers/staging/gdm72xx/gdm_qos.h
new file mode 100644
index 000000000..bbc8aab33
--- /dev/null
+++ b/drivers/staging/gdm72xx/gdm_qos.h
@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) 2012 GCT Semiconductor, Inc. All rights reserved.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ */
+
+#ifndef __GDM72XX_GDM_QOS_H__
+#define __GDM72XX_GDM_QOS_H__
+
+#include <linux/types.h>
+#include <linux/usb.h>
+#include <linux/list.h>
+
+#define QOS_MAX 16
+#define IPTYPEOFSERVICE 0x8000
+#define PROTOCOL 0x4000
+#define IPMASKEDSRCADDRESS 0x2000
+#define IPMASKEDDSTADDRESS 0x1000
+#define PROTOCOLSRCPORTRANGE 0x800
+#define PROTOCOLDSTPORTRANGE 0x400
+#define DSTMACADDR 0x200
+#define SRCMACADDR 0x100
+#define ETHERTYPE 0x80
+#define IEEE802_1DUSERPRIORITY 0x40
+#define IEEE802_1QVLANID 0x10
+
+struct gdm_wimax_csr_s {
+ bool enabled;
+ u32 sfid;
+ u8 qos_buf_count;
+ u16 classifier_rule_en;
+ u8 ip2s_lo;
+ u8 ip2s_hi;
+ u8 ip2s_mask;
+ u8 protocol;
+ u8 ipsrc_addr[16];
+ u8 ipsrc_addrmask[16];
+ u8 ipdst_addr[16];
+ u8 ipdst_addrmask[16];
+ u16 srcport_lo;
+ u16 srcport_hi;
+ u16 dstport_lo;
+ u16 dstport_hi;
+};
+
+struct qos_entry_s {
+ struct list_head list;
+ struct sk_buff *skb;
+ struct net_device *dev;
+
+};
+
+struct qos_cb_s {
+ struct list_head qos_list[QOS_MAX];
+ int qos_list_cnt;
+ int qos_null_idx;
+ struct gdm_wimax_csr_s csr[QOS_MAX];
+ spinlock_t qos_lock;
+ int qos_limit_size;
+};
+
+void gdm_qos_init(void *nic_ptr);
+void gdm_qos_release_list(void *nic_ptr);
+int gdm_qos_send_hci_pkt(struct sk_buff *skb, struct net_device *dev);
+void gdm_recv_qos_hci_packet(void *nic_ptr, u8 *buf, int size);
+
+#endif /* __GDM72XX_GDM_QOS_H__ */
diff --git a/drivers/staging/gdm72xx/gdm_sdio.c b/drivers/staging/gdm72xx/gdm_sdio.c
new file mode 100644
index 000000000..a5fd07948
--- /dev/null
+++ b/drivers/staging/gdm72xx/gdm_sdio.c
@@ -0,0 +1,701 @@
+/*
+ * Copyright (c) 2012 GCT Semiconductor, Inc. All rights reserved.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+
+#include <linux/mmc/core.h>
+#include <linux/mmc/card.h>
+#include <linux/mmc/sdio_func.h>
+#include <linux/mmc/sdio_ids.h>
+
+#include "gdm_sdio.h"
+#include "gdm_wimax.h"
+#include "sdio_boot.h"
+#include "hci.h"
+
+#define TYPE_A_HEADER_SIZE 4
+#define TYPE_A_LOOKAHEAD_SIZE 16
+
+#define MAX_NR_RX_BUF 4
+
+#define SDU_TX_BUF_SIZE 2048
+#define TX_BUF_SIZE 2048
+#define TX_CHUNK_SIZE (2048 - TYPE_A_HEADER_SIZE)
+#define RX_BUF_SIZE (25*1024)
+
+#define TX_HZ 2000
+#define TX_INTERVAL (1000000/TX_HZ)
+
+static struct sdio_tx *alloc_tx_struct(struct tx_cxt *tx)
+{
+ struct sdio_tx *t = kzalloc(sizeof(*t), GFP_ATOMIC);
+
+ if (!t)
+ return NULL;
+
+ t->buf = kmalloc(TX_BUF_SIZE, GFP_ATOMIC);
+ if (!t->buf) {
+ kfree(t);
+ return NULL;
+ }
+
+ t->tx_cxt = tx;
+
+ return t;
+}
+
+static void free_tx_struct(struct sdio_tx *t)
+{
+ if (t) {
+ kfree(t->buf);
+ kfree(t);
+ }
+}
+
+static struct sdio_rx *alloc_rx_struct(struct rx_cxt *rx)
+{
+ struct sdio_rx *r = kzalloc(sizeof(*r), GFP_ATOMIC);
+
+ if (r)
+ r->rx_cxt = rx;
+
+ return r;
+}
+
+static void free_rx_struct(struct sdio_rx *r)
+{
+ kfree(r);
+}
+
+/* Before this function is called, spin lock should be locked. */
+static struct sdio_tx *get_tx_struct(struct tx_cxt *tx, int *no_spc)
+{
+ struct sdio_tx *t;
+
+ if (list_empty(&tx->free_list))
+ return NULL;
+
+ t = list_entry(tx->free_list.prev, struct sdio_tx, list);
+ list_del(&t->list);
+
+ *no_spc = list_empty(&tx->free_list) ? 1 : 0;
+
+ return t;
+}
+
+/* Before this function is called, spin lock should be locked. */
+static void put_tx_struct(struct tx_cxt *tx, struct sdio_tx *t)
+{
+ list_add_tail(&t->list, &tx->free_list);
+}
+
+/* Before this function is called, spin lock should be locked. */
+static struct sdio_rx *get_rx_struct(struct rx_cxt *rx)
+{
+ struct sdio_rx *r;
+
+ if (list_empty(&rx->free_list))
+ return NULL;
+
+ r = list_entry(rx->free_list.prev, struct sdio_rx, list);
+ list_del(&r->list);
+
+ return r;
+}
+
+/* Before this function is called, spin lock should be locked. */
+static void put_rx_struct(struct rx_cxt *rx, struct sdio_rx *r)
+{
+ list_add_tail(&r->list, &rx->free_list);
+}
+
+static void release_sdio(struct sdiowm_dev *sdev)
+{
+ struct tx_cxt *tx = &sdev->tx;
+ struct rx_cxt *rx = &sdev->rx;
+ struct sdio_tx *t, *t_next;
+ struct sdio_rx *r, *r_next;
+
+ kfree(tx->sdu_buf);
+
+ list_for_each_entry_safe(t, t_next, &tx->free_list, list) {
+ list_del(&t->list);
+ free_tx_struct(t);
+ }
+
+ list_for_each_entry_safe(t, t_next, &tx->sdu_list, list) {
+ list_del(&t->list);
+ free_tx_struct(t);
+ }
+
+ list_for_each_entry_safe(t, t_next, &tx->hci_list, list) {
+ list_del(&t->list);
+ free_tx_struct(t);
+ }
+
+ kfree(rx->rx_buf);
+
+ list_for_each_entry_safe(r, r_next, &rx->free_list, list) {
+ list_del(&r->list);
+ free_rx_struct(r);
+ }
+
+ list_for_each_entry_safe(r, r_next, &rx->req_list, list) {
+ list_del(&r->list);
+ free_rx_struct(r);
+ }
+}
+
+static int init_sdio(struct sdiowm_dev *sdev)
+{
+ int ret = 0, i;
+ struct tx_cxt *tx = &sdev->tx;
+ struct rx_cxt *rx = &sdev->rx;
+ struct sdio_tx *t;
+ struct sdio_rx *r;
+
+ INIT_LIST_HEAD(&tx->free_list);
+ INIT_LIST_HEAD(&tx->sdu_list);
+ INIT_LIST_HEAD(&tx->hci_list);
+
+ spin_lock_init(&tx->lock);
+
+ tx->sdu_buf = kmalloc(SDU_TX_BUF_SIZE, GFP_KERNEL);
+ if (tx->sdu_buf == NULL)
+ goto fail;
+
+ for (i = 0; i < MAX_NR_SDU_BUF; i++) {
+ t = alloc_tx_struct(tx);
+ if (t == NULL) {
+ ret = -ENOMEM;
+ goto fail;
+ }
+ list_add(&t->list, &tx->free_list);
+ }
+
+ INIT_LIST_HEAD(&rx->free_list);
+ INIT_LIST_HEAD(&rx->req_list);
+
+ spin_lock_init(&rx->lock);
+
+ for (i = 0; i < MAX_NR_RX_BUF; i++) {
+ r = alloc_rx_struct(rx);
+ if (r == NULL) {
+ ret = -ENOMEM;
+ goto fail;
+ }
+ list_add(&r->list, &rx->free_list);
+ }
+
+ rx->rx_buf = kmalloc(RX_BUF_SIZE, GFP_KERNEL);
+ if (rx->rx_buf == NULL)
+ goto fail;
+
+ return 0;
+
+fail:
+ release_sdio(sdev);
+ return ret;
+}
+
+static void send_sdio_pkt(struct sdio_func *func, u8 *data, int len)
+{
+ int n, blocks, ret, remain;
+
+ sdio_claim_host(func);
+
+ blocks = len / func->cur_blksize;
+ n = blocks * func->cur_blksize;
+ if (blocks) {
+ ret = sdio_memcpy_toio(func, 0, data, n);
+ if (ret < 0) {
+ if (ret != -ENOMEDIUM)
+ dev_err(&func->dev,
+ "gdmwms: error: ret = %d\n", ret);
+ goto end_io;
+ }
+ }
+
+ remain = len - n;
+ remain = (remain + 3) & ~3;
+
+ if (remain) {
+ ret = sdio_memcpy_toio(func, 0, data + n, remain);
+ if (ret < 0) {
+ if (ret != -ENOMEDIUM)
+ dev_err(&func->dev,
+ "gdmwms: error: ret = %d\n", ret);
+ goto end_io;
+ }
+ }
+
+end_io:
+ sdio_release_host(func);
+}
+
+static void send_sdu(struct sdio_func *func, struct tx_cxt *tx)
+{
+ struct list_head *l, *next;
+ struct hci_s *hci;
+ struct sdio_tx *t;
+ int pos, len, i, estlen, aggr_num = 0, aggr_len;
+ u8 *buf;
+ unsigned long flags;
+
+ spin_lock_irqsave(&tx->lock, flags);
+
+ pos = TYPE_A_HEADER_SIZE + HCI_HEADER_SIZE;
+ list_for_each_entry(t, &tx->sdu_list, list) {
+ estlen = ((t->len + 3) & ~3) + 4;
+ if ((pos + estlen) > SDU_TX_BUF_SIZE)
+ break;
+
+ aggr_num++;
+ memcpy(tx->sdu_buf + pos, t->buf, t->len);
+ memset(tx->sdu_buf + pos + t->len, 0, estlen - t->len);
+ pos += estlen;
+ }
+ aggr_len = pos;
+
+ hci = (struct hci_s *)(tx->sdu_buf + TYPE_A_HEADER_SIZE);
+ hci->cmd_evt = cpu_to_be16(WIMAX_TX_SDU_AGGR);
+ hci->length = cpu_to_be16(aggr_len - TYPE_A_HEADER_SIZE -
+ HCI_HEADER_SIZE);
+
+ spin_unlock_irqrestore(&tx->lock, flags);
+
+ dev_dbg(&func->dev, "sdio_send: %*ph\n", aggr_len - TYPE_A_HEADER_SIZE,
+ tx->sdu_buf + TYPE_A_HEADER_SIZE);
+
+ for (pos = TYPE_A_HEADER_SIZE; pos < aggr_len; pos += TX_CHUNK_SIZE) {
+ len = aggr_len - pos;
+ len = len > TX_CHUNK_SIZE ? TX_CHUNK_SIZE : len;
+ buf = tx->sdu_buf + pos - TYPE_A_HEADER_SIZE;
+
+ buf[0] = len & 0xff;
+ buf[1] = (len >> 8) & 0xff;
+ buf[2] = (len >> 16) & 0xff;
+ buf[3] = (pos + len) >= aggr_len ? 0 : 1;
+ send_sdio_pkt(func, buf, len + TYPE_A_HEADER_SIZE);
+ }
+
+ spin_lock_irqsave(&tx->lock, flags);
+
+ for (l = tx->sdu_list.next, i = 0; i < aggr_num; i++, l = next) {
+ next = l->next;
+ t = list_entry(l, struct sdio_tx, list);
+ if (t->callback)
+ t->callback(t->cb_data);
+
+ list_del(l);
+ put_tx_struct(t->tx_cxt, t);
+ }
+
+ do_gettimeofday(&tx->sdu_stamp);
+ spin_unlock_irqrestore(&tx->lock, flags);
+}
+
+static void send_hci(struct sdio_func *func, struct tx_cxt *tx,
+ struct sdio_tx *t)
+{
+ unsigned long flags;
+
+ dev_dbg(&func->dev, "sdio_send: %*ph\n", t->len - TYPE_A_HEADER_SIZE,
+ t->buf + TYPE_A_HEADER_SIZE);
+
+ send_sdio_pkt(func, t->buf, t->len);
+
+ spin_lock_irqsave(&tx->lock, flags);
+ if (t->callback)
+ t->callback(t->cb_data);
+ free_tx_struct(t);
+ spin_unlock_irqrestore(&tx->lock, flags);
+}
+
+static void do_tx(struct work_struct *work)
+{
+ struct sdiowm_dev *sdev = container_of(work, struct sdiowm_dev, ws);
+ struct sdio_func *func = sdev->func;
+ struct tx_cxt *tx = &sdev->tx;
+ struct sdio_tx *t = NULL;
+ struct timeval now, *before;
+ int is_sdu = 0;
+ long diff;
+ unsigned long flags;
+
+ spin_lock_irqsave(&tx->lock, flags);
+ if (!tx->can_send) {
+ spin_unlock_irqrestore(&tx->lock, flags);
+ return;
+ }
+
+ if (!list_empty(&tx->hci_list)) {
+ t = list_entry(tx->hci_list.next, struct sdio_tx, list);
+ list_del(&t->list);
+ is_sdu = 0;
+ } else if (!tx->stop_sdu_tx && !list_empty(&tx->sdu_list)) {
+ do_gettimeofday(&now);
+ before = &tx->sdu_stamp;
+
+ diff = (now.tv_sec - before->tv_sec) * 1000000 +
+ (now.tv_usec - before->tv_usec);
+ if (diff >= 0 && diff < TX_INTERVAL) {
+ schedule_work(&sdev->ws);
+ spin_unlock_irqrestore(&tx->lock, flags);
+ return;
+ }
+ is_sdu = 1;
+ }
+
+ if (!is_sdu && t == NULL) {
+ spin_unlock_irqrestore(&tx->lock, flags);
+ return;
+ }
+
+ tx->can_send = 0;
+
+ spin_unlock_irqrestore(&tx->lock, flags);
+
+ if (is_sdu)
+ send_sdu(func, tx);
+ else
+ send_hci(func, tx, t);
+}
+
+static int gdm_sdio_send(void *priv_dev, void *data, int len,
+ void (*cb)(void *data), void *cb_data)
+{
+ struct sdiowm_dev *sdev = priv_dev;
+ struct tx_cxt *tx = &sdev->tx;
+ struct sdio_tx *t;
+ u8 *pkt = data;
+ int no_spc = 0;
+ u16 cmd_evt;
+ unsigned long flags;
+
+ if (len > TX_BUF_SIZE - TYPE_A_HEADER_SIZE)
+ return -EINVAL;
+
+ spin_lock_irqsave(&tx->lock, flags);
+
+ cmd_evt = (pkt[0] << 8) | pkt[1];
+ if (cmd_evt == WIMAX_TX_SDU) {
+ t = get_tx_struct(tx, &no_spc);
+ if (t == NULL) {
+ /* This case must not happen. */
+ spin_unlock_irqrestore(&tx->lock, flags);
+ return -ENOSPC;
+ }
+ list_add_tail(&t->list, &tx->sdu_list);
+
+ memcpy(t->buf, data, len);
+
+ t->len = len;
+ t->callback = cb;
+ t->cb_data = cb_data;
+ } else {
+ t = alloc_tx_struct(tx);
+ if (t == NULL) {
+ spin_unlock_irqrestore(&tx->lock, flags);
+ return -ENOMEM;
+ }
+ list_add_tail(&t->list, &tx->hci_list);
+
+ t->buf[0] = len & 0xff;
+ t->buf[1] = (len >> 8) & 0xff;
+ t->buf[2] = (len >> 16) & 0xff;
+ t->buf[3] = 2;
+ memcpy(t->buf + TYPE_A_HEADER_SIZE, data, len);
+
+ t->len = len + TYPE_A_HEADER_SIZE;
+ t->callback = cb;
+ t->cb_data = cb_data;
+ }
+
+ if (tx->can_send)
+ schedule_work(&sdev->ws);
+
+ spin_unlock_irqrestore(&tx->lock, flags);
+
+ if (no_spc)
+ return -ENOSPC;
+
+ return 0;
+}
+
+/* Handle the HCI, WIMAX_SDU_TX_FLOW. */
+static int control_sdu_tx_flow(struct sdiowm_dev *sdev, u8 *hci_data, int len)
+{
+ struct tx_cxt *tx = &sdev->tx;
+ u16 cmd_evt;
+ unsigned long flags;
+
+ spin_lock_irqsave(&tx->lock, flags);
+
+ cmd_evt = (hci_data[0] << 8) | (hci_data[1]);
+ if (cmd_evt != WIMAX_SDU_TX_FLOW)
+ goto out;
+
+ if (hci_data[4] == 0) {
+ dev_dbg(&sdev->func->dev, "WIMAX ==> STOP SDU TX\n");
+ tx->stop_sdu_tx = 1;
+ } else if (hci_data[4] == 1) {
+ dev_dbg(&sdev->func->dev, "WIMAX ==> START SDU TX\n");
+ tx->stop_sdu_tx = 0;
+ if (tx->can_send)
+ schedule_work(&sdev->ws);
+ /* If free buffer for sdu tx doesn't exist, then tx queue
+ * should not be woken. For this reason, don't pass the command,
+ * START_SDU_TX.
+ */
+ if (list_empty(&tx->free_list))
+ len = 0;
+ }
+
+out:
+ spin_unlock_irqrestore(&tx->lock, flags);
+ return len;
+}
+
+static void gdm_sdio_irq(struct sdio_func *func)
+{
+ struct phy_dev *phy_dev = sdio_get_drvdata(func);
+ struct sdiowm_dev *sdev = phy_dev->priv_dev;
+ struct tx_cxt *tx = &sdev->tx;
+ struct rx_cxt *rx = &sdev->rx;
+ struct sdio_rx *r;
+ unsigned long flags;
+ u8 val, hdr[TYPE_A_LOOKAHEAD_SIZE], *buf;
+ u32 len, blocks, n;
+ int ret, remain;
+
+ /* Check interrupt */
+ val = sdio_readb(func, 0x13, &ret);
+ if (val & 0x01)
+ sdio_writeb(func, 0x01, 0x13, &ret); /* clear interrupt */
+ else
+ return;
+
+ ret = sdio_memcpy_fromio(func, hdr, 0x0, TYPE_A_LOOKAHEAD_SIZE);
+ if (ret) {
+ dev_err(&func->dev,
+ "Cannot read from function %d\n", func->num);
+ goto done;
+ }
+
+ len = (hdr[2] << 16) | (hdr[1] << 8) | hdr[0];
+ if (len > (RX_BUF_SIZE - TYPE_A_HEADER_SIZE)) {
+ dev_err(&func->dev, "Too big Type-A size: %d\n", len);
+ goto done;
+ }
+
+ if (hdr[3] == 1) { /* Ack */
+ u32 *ack_seq = (u32 *)&hdr[4];
+
+ spin_lock_irqsave(&tx->lock, flags);
+ tx->can_send = 1;
+
+ if (!list_empty(&tx->sdu_list) || !list_empty(&tx->hci_list))
+ schedule_work(&sdev->ws);
+ spin_unlock_irqrestore(&tx->lock, flags);
+ dev_dbg(&func->dev, "Ack... %0x\n", ntohl(*ack_seq));
+ goto done;
+ }
+
+ memcpy(rx->rx_buf, hdr + TYPE_A_HEADER_SIZE,
+ TYPE_A_LOOKAHEAD_SIZE - TYPE_A_HEADER_SIZE);
+
+ buf = rx->rx_buf + TYPE_A_LOOKAHEAD_SIZE - TYPE_A_HEADER_SIZE;
+ remain = len - TYPE_A_LOOKAHEAD_SIZE + TYPE_A_HEADER_SIZE;
+ if (remain <= 0)
+ goto end_io;
+
+ blocks = remain / func->cur_blksize;
+
+ if (blocks) {
+ n = blocks * func->cur_blksize;
+ ret = sdio_memcpy_fromio(func, buf, 0x0, n);
+ if (ret) {
+ dev_err(&func->dev,
+ "Cannot read from function %d\n", func->num);
+ goto done;
+ }
+ buf += n;
+ remain -= n;
+ }
+
+ if (remain) {
+ ret = sdio_memcpy_fromio(func, buf, 0x0, remain);
+ if (ret) {
+ dev_err(&func->dev,
+ "Cannot read from function %d\n", func->num);
+ goto done;
+ }
+ }
+
+end_io:
+ dev_dbg(&func->dev, "sdio_receive: %*ph\n", len, rx->rx_buf);
+
+ len = control_sdu_tx_flow(sdev, rx->rx_buf, len);
+
+ spin_lock_irqsave(&rx->lock, flags);
+
+ if (!list_empty(&rx->req_list)) {
+ r = list_entry(rx->req_list.next, struct sdio_rx, list);
+ spin_unlock_irqrestore(&rx->lock, flags);
+ if (r->callback)
+ r->callback(r->cb_data, rx->rx_buf, len);
+ spin_lock_irqsave(&rx->lock, flags);
+ list_del(&r->list);
+ put_rx_struct(rx, r);
+ }
+
+ spin_unlock_irqrestore(&rx->lock, flags);
+
+done:
+ sdio_writeb(func, 0x00, 0x10, &ret); /* PCRRT */
+ if (!phy_dev->netdev)
+ register_wimax_device(phy_dev, &func->dev);
+}
+
+static int gdm_sdio_receive(void *priv_dev,
+ void (*cb)(void *cb_data, void *data, int len),
+ void *cb_data)
+{
+ struct sdiowm_dev *sdev = priv_dev;
+ struct rx_cxt *rx = &sdev->rx;
+ struct sdio_rx *r;
+ unsigned long flags;
+
+ spin_lock_irqsave(&rx->lock, flags);
+ r = get_rx_struct(rx);
+ if (r == NULL) {
+ spin_unlock_irqrestore(&rx->lock, flags);
+ return -ENOMEM;
+ }
+
+ r->callback = cb;
+ r->cb_data = cb_data;
+
+ list_add_tail(&r->list, &rx->req_list);
+ spin_unlock_irqrestore(&rx->lock, flags);
+
+ return 0;
+}
+
+static int sdio_wimax_probe(struct sdio_func *func,
+ const struct sdio_device_id *id)
+{
+ int ret;
+ struct phy_dev *phy_dev = NULL;
+ struct sdiowm_dev *sdev = NULL;
+
+ dev_info(&func->dev, "Found GDM SDIO VID = 0x%04x PID = 0x%04x...\n",
+ func->vendor, func->device);
+ dev_info(&func->dev, "GCT WiMax driver version %s\n", DRIVER_VERSION);
+
+ sdio_claim_host(func);
+ sdio_enable_func(func);
+ sdio_claim_irq(func, gdm_sdio_irq);
+
+ ret = sdio_boot(func);
+ if (ret)
+ return ret;
+
+ phy_dev = kzalloc(sizeof(*phy_dev), GFP_KERNEL);
+ if (phy_dev == NULL) {
+ ret = -ENOMEM;
+ goto out;
+ }
+ sdev = kzalloc(sizeof(*sdev), GFP_KERNEL);
+ if (sdev == NULL) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ phy_dev->priv_dev = (void *)sdev;
+ phy_dev->send_func = gdm_sdio_send;
+ phy_dev->rcv_func = gdm_sdio_receive;
+
+ ret = init_sdio(sdev);
+ if (ret < 0)
+ goto out;
+
+ sdev->func = func;
+
+ sdio_writeb(func, 1, 0x14, &ret); /* Enable interrupt */
+ sdio_release_host(func);
+
+ INIT_WORK(&sdev->ws, do_tx);
+
+ sdio_set_drvdata(func, phy_dev);
+out:
+ if (ret) {
+ kfree(phy_dev);
+ kfree(sdev);
+ }
+
+ return ret;
+}
+
+static void sdio_wimax_remove(struct sdio_func *func)
+{
+ struct phy_dev *phy_dev = sdio_get_drvdata(func);
+ struct sdiowm_dev *sdev = phy_dev->priv_dev;
+
+ cancel_work_sync(&sdev->ws);
+ if (phy_dev->netdev)
+ unregister_wimax_device(phy_dev);
+ sdio_claim_host(func);
+ sdio_release_irq(func);
+ sdio_disable_func(func);
+ sdio_release_host(func);
+ release_sdio(sdev);
+
+ kfree(sdev);
+ kfree(phy_dev);
+}
+
+static const struct sdio_device_id sdio_wimax_ids[] = {
+ { SDIO_DEVICE(0x0296, 0x5347) },
+ {0}
+};
+
+MODULE_DEVICE_TABLE(sdio, sdio_wimax_ids);
+
+static struct sdio_driver sdio_wimax_driver = {
+ .probe = sdio_wimax_probe,
+ .remove = sdio_wimax_remove,
+ .name = "sdio_wimax",
+ .id_table = sdio_wimax_ids,
+};
+
+static int __init sdio_gdm_wimax_init(void)
+{
+ return sdio_register_driver(&sdio_wimax_driver);
+}
+
+static void __exit sdio_gdm_wimax_exit(void)
+{
+ sdio_unregister_driver(&sdio_wimax_driver);
+}
+
+module_init(sdio_gdm_wimax_init);
+module_exit(sdio_gdm_wimax_exit);
+
+MODULE_VERSION(DRIVER_VERSION);
+MODULE_DESCRIPTION("GCT WiMax SDIO Device Driver");
+MODULE_AUTHOR("Ethan Park");
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/gdm72xx/gdm_sdio.h b/drivers/staging/gdm72xx/gdm_sdio.h
new file mode 100644
index 000000000..77ad9d686
--- /dev/null
+++ b/drivers/staging/gdm72xx/gdm_sdio.h
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2012 GCT Semiconductor, Inc. All rights reserved.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ */
+
+#ifndef __GDM72XX_GDM_SDIO_H__
+#define __GDM72XX_GDM_SDIO_H__
+
+#include <linux/types.h>
+#include <linux/time.h>
+
+#define MAX_NR_SDU_BUF 64
+
+struct sdio_tx {
+ struct list_head list;
+ struct tx_cxt *tx_cxt;
+ u8 *buf;
+ int len;
+ void (*callback)(void *cb_data);
+ void *cb_data;
+};
+
+struct tx_cxt {
+ struct list_head free_list;
+ struct list_head sdu_list;
+ struct list_head hci_list;
+ struct timeval sdu_stamp;
+ u8 *sdu_buf;
+ spinlock_t lock;
+ int can_send;
+ int stop_sdu_tx;
+};
+
+struct sdio_rx {
+ struct list_head list;
+ struct rx_cxt *rx_cxt;
+ void (*callback)(void *cb_data, void *data, int len);
+ void *cb_data;
+};
+
+struct rx_cxt {
+ struct list_head free_list;
+ struct list_head req_list;
+ u8 *rx_buf;
+ spinlock_t lock;
+};
+
+struct sdiowm_dev {
+ struct sdio_func *func;
+ struct tx_cxt tx;
+ struct rx_cxt rx;
+ struct work_struct ws;
+};
+
+#endif /* __GDM72XX_GDM_SDIO_H__ */
diff --git a/drivers/staging/gdm72xx/gdm_usb.c b/drivers/staging/gdm72xx/gdm_usb.c
new file mode 100644
index 000000000..eac2f3478
--- /dev/null
+++ b/drivers/staging/gdm72xx/gdm_usb.c
@@ -0,0 +1,789 @@
+/*
+ * Copyright (c) 2012 GCT Semiconductor, Inc. All rights reserved.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/usb.h>
+#include <asm/byteorder.h>
+#include <linux/kthread.h>
+
+#include "gdm_usb.h"
+#include "gdm_wimax.h"
+#include "usb_boot.h"
+#include "hci.h"
+
+#include "usb_ids.h"
+
+MODULE_DEVICE_TABLE(usb, id_table);
+
+#define TX_BUF_SIZE 2048
+
+#if defined(CONFIG_WIMAX_GDM72XX_WIMAX2)
+#define RX_BUF_SIZE (128*1024) /* For packet aggregation */
+#else
+#define RX_BUF_SIZE 2048
+#endif
+
+#define GDM7205_PADDING 256
+
+#define DOWNLOAD_CONF_VALUE 0x21
+
+#ifdef CONFIG_WIMAX_GDM72XX_K_MODE
+
+static DECLARE_WAIT_QUEUE_HEAD(k_wait);
+static LIST_HEAD(k_list);
+static DEFINE_SPINLOCK(k_lock);
+static int k_mode_stop;
+
+#define K_WAIT_TIME (2 * HZ / 100)
+
+#endif /* CONFIG_WIMAX_GDM72XX_K_MODE */
+
+static struct usb_tx *alloc_tx_struct(struct tx_cxt *tx)
+{
+ struct usb_tx *t = kzalloc(sizeof(*t), GFP_ATOMIC);
+
+ if (!t)
+ return NULL;
+
+ t->urb = usb_alloc_urb(0, GFP_ATOMIC);
+ t->buf = kmalloc(TX_BUF_SIZE, GFP_ATOMIC);
+ if (!t->urb || !t->buf) {
+ usb_free_urb(t->urb);
+ kfree(t->buf);
+ kfree(t);
+ return NULL;
+ }
+
+ t->tx_cxt = tx;
+
+ return t;
+}
+
+static void free_tx_struct(struct usb_tx *t)
+{
+ if (t) {
+ usb_free_urb(t->urb);
+ kfree(t->buf);
+ kfree(t);
+ }
+}
+
+static struct usb_rx *alloc_rx_struct(struct rx_cxt *rx)
+{
+ struct usb_rx *r = kzalloc(sizeof(*r), GFP_ATOMIC);
+
+ if (!r)
+ return NULL;
+
+ r->urb = usb_alloc_urb(0, GFP_ATOMIC);
+ r->buf = kmalloc(RX_BUF_SIZE, GFP_ATOMIC);
+ if (!r->urb || !r->buf) {
+ usb_free_urb(r->urb);
+ kfree(r->buf);
+ kfree(r);
+ return NULL;
+ }
+
+ r->rx_cxt = rx;
+ return r;
+}
+
+static void free_rx_struct(struct usb_rx *r)
+{
+ if (r) {
+ usb_free_urb(r->urb);
+ kfree(r->buf);
+ kfree(r);
+ }
+}
+
+/* Before this function is called, spin lock should be locked. */
+static struct usb_tx *get_tx_struct(struct tx_cxt *tx, int *no_spc)
+{
+ struct usb_tx *t;
+
+ if (list_empty(&tx->free_list)) {
+ *no_spc = 1;
+ return NULL;
+ }
+
+ t = list_entry(tx->free_list.next, struct usb_tx, list);
+ list_del(&t->list);
+
+ *no_spc = list_empty(&tx->free_list) ? 1 : 0;
+
+ return t;
+}
+
+/* Before this function is called, spin lock should be locked. */
+static void put_tx_struct(struct tx_cxt *tx, struct usb_tx *t)
+{
+ list_add_tail(&t->list, &tx->free_list);
+}
+
+/* Before this function is called, spin lock should be locked. */
+static struct usb_rx *get_rx_struct(struct rx_cxt *rx)
+{
+ struct usb_rx *r;
+
+ if (list_empty(&rx->free_list)) {
+ r = alloc_rx_struct(rx);
+ if (r == NULL)
+ return NULL;
+
+ list_add(&r->list, &rx->free_list);
+ }
+
+ r = list_entry(rx->free_list.next, struct usb_rx, list);
+ list_move_tail(&r->list, &rx->used_list);
+
+ return r;
+}
+
+/* Before this function is called, spin lock should be locked. */
+static void put_rx_struct(struct rx_cxt *rx, struct usb_rx *r)
+{
+ list_move(&r->list, &rx->free_list);
+}
+
+static void release_usb(struct usbwm_dev *udev)
+{
+ struct tx_cxt *tx = &udev->tx;
+ struct rx_cxt *rx = &udev->rx;
+ struct usb_tx *t, *t_next;
+ struct usb_rx *r, *r_next;
+ unsigned long flags;
+
+ spin_lock_irqsave(&tx->lock, flags);
+
+ list_for_each_entry_safe(t, t_next, &tx->sdu_list, list) {
+ list_del(&t->list);
+ free_tx_struct(t);
+ }
+
+ list_for_each_entry_safe(t, t_next, &tx->hci_list, list) {
+ list_del(&t->list);
+ free_tx_struct(t);
+ }
+
+ list_for_each_entry_safe(t, t_next, &tx->free_list, list) {
+ list_del(&t->list);
+ free_tx_struct(t);
+ }
+
+ spin_unlock_irqrestore(&tx->lock, flags);
+
+ spin_lock_irqsave(&rx->lock, flags);
+
+ list_for_each_entry_safe(r, r_next, &rx->free_list, list) {
+ list_del(&r->list);
+ free_rx_struct(r);
+ }
+
+ list_for_each_entry_safe(r, r_next, &rx->used_list, list) {
+ list_del(&r->list);
+ free_rx_struct(r);
+ }
+
+ spin_unlock_irqrestore(&rx->lock, flags);
+}
+
+static int init_usb(struct usbwm_dev *udev)
+{
+ int ret = 0, i;
+ struct tx_cxt *tx = &udev->tx;
+ struct rx_cxt *rx = &udev->rx;
+ struct usb_tx *t;
+ struct usb_rx *r;
+ unsigned long flags;
+
+ INIT_LIST_HEAD(&tx->free_list);
+ INIT_LIST_HEAD(&tx->sdu_list);
+ INIT_LIST_HEAD(&tx->hci_list);
+#if defined(CONFIG_WIMAX_GDM72XX_USB_PM) || defined(CONFIG_WIMAX_GDM72XX_K_MODE)
+ INIT_LIST_HEAD(&tx->pending_list);
+#endif
+
+ INIT_LIST_HEAD(&rx->free_list);
+ INIT_LIST_HEAD(&rx->used_list);
+
+ spin_lock_init(&tx->lock);
+ spin_lock_init(&rx->lock);
+
+ spin_lock_irqsave(&tx->lock, flags);
+ for (i = 0; i < MAX_NR_SDU_BUF; i++) {
+ t = alloc_tx_struct(tx);
+ if (t == NULL) {
+ spin_unlock_irqrestore(&tx->lock, flags);
+ ret = -ENOMEM;
+ goto fail;
+ }
+ list_add(&t->list, &tx->free_list);
+ }
+ spin_unlock_irqrestore(&tx->lock, flags);
+
+ r = alloc_rx_struct(rx);
+ if (r == NULL) {
+ ret = -ENOMEM;
+ goto fail;
+ }
+
+ spin_lock_irqsave(&rx->lock, flags);
+ list_add(&r->list, &rx->free_list);
+ spin_unlock_irqrestore(&rx->lock, flags);
+ return ret;
+
+fail:
+ release_usb(udev);
+ return ret;
+}
+
+static void __gdm_usb_send_complete(struct urb *urb)
+{
+ struct usb_tx *t = urb->context;
+ struct tx_cxt *tx = t->tx_cxt;
+ u8 *pkt = t->buf;
+ u16 cmd_evt;
+
+ /* Completion by usb_unlink_urb */
+ if (urb->status == -ECONNRESET)
+ return;
+
+ if (t->callback)
+ t->callback(t->cb_data);
+
+ /* Delete from sdu list or hci list. */
+ list_del(&t->list);
+
+ cmd_evt = (pkt[0] << 8) | pkt[1];
+ if (cmd_evt == WIMAX_TX_SDU)
+ put_tx_struct(tx, t);
+ else
+ free_tx_struct(t);
+}
+
+static void gdm_usb_send_complete(struct urb *urb)
+{
+ struct usb_tx *t = urb->context;
+ struct tx_cxt *tx = t->tx_cxt;
+ unsigned long flags;
+
+ spin_lock_irqsave(&tx->lock, flags);
+ __gdm_usb_send_complete(urb);
+ spin_unlock_irqrestore(&tx->lock, flags);
+}
+
+static int gdm_usb_send(void *priv_dev, void *data, int len,
+ void (*cb)(void *data), void *cb_data)
+{
+ struct usbwm_dev *udev = priv_dev;
+ struct usb_device *usbdev = udev->usbdev;
+ struct tx_cxt *tx = &udev->tx;
+ struct usb_tx *t;
+ int padding = udev->padding;
+ int no_spc = 0, ret;
+ u8 *pkt = data;
+ u16 cmd_evt;
+ unsigned long flags;
+#ifdef CONFIG_WIMAX_GDM72XX_K_MODE
+ unsigned long flags2;
+#endif /* CONFIG_WIMAX_GDM72XX_K_MODE */
+
+ if (!udev->usbdev) {
+ dev_err(&usbdev->dev, "%s: No such device\n", __func__);
+ return -ENODEV;
+ }
+
+ if (len > TX_BUF_SIZE - padding - 1)
+ return -EINVAL;
+
+ spin_lock_irqsave(&tx->lock, flags);
+
+ cmd_evt = (pkt[0] << 8) | pkt[1];
+ if (cmd_evt == WIMAX_TX_SDU) {
+ t = get_tx_struct(tx, &no_spc);
+ if (t == NULL) {
+ /* This case must not happen. */
+ spin_unlock_irqrestore(&tx->lock, flags);
+ return -ENOSPC;
+ }
+ list_add_tail(&t->list, &tx->sdu_list);
+ } else {
+ t = alloc_tx_struct(tx);
+ if (t == NULL) {
+ spin_unlock_irqrestore(&tx->lock, flags);
+ return -ENOMEM;
+ }
+ list_add_tail(&t->list, &tx->hci_list);
+ }
+
+ memcpy(t->buf + padding, data, len);
+ t->callback = cb;
+ t->cb_data = cb_data;
+
+ /* In some cases, USB Module of WiMax is blocked when data size is
+ * the multiple of 512. So, increment length by one in that case.
+ */
+ if ((len % 512) == 0)
+ len++;
+
+ usb_fill_bulk_urb(t->urb, usbdev, usb_sndbulkpipe(usbdev, 1), t->buf,
+ len + padding, gdm_usb_send_complete, t);
+
+ dev_dbg(&usbdev->dev, "usb_send: %*ph\n", len + padding, t->buf);
+
+#ifdef CONFIG_WIMAX_GDM72XX_USB_PM
+ if (usbdev->state & USB_STATE_SUSPENDED) {
+ list_add_tail(&t->p_list, &tx->pending_list);
+ schedule_work(&udev->pm_ws);
+ goto out;
+ }
+#endif /* CONFIG_WIMAX_GDM72XX_USB_PM */
+
+#ifdef CONFIG_WIMAX_GDM72XX_K_MODE
+ if (udev->bw_switch) {
+ list_add_tail(&t->p_list, &tx->pending_list);
+ goto out;
+ } else if (cmd_evt == WIMAX_SCAN) {
+ struct rx_cxt *rx;
+ struct usb_rx *r;
+
+ rx = &udev->rx;
+
+ spin_lock_irqsave(&rx->lock, flags2);
+ list_for_each_entry(r, &rx->used_list, list)
+ usb_unlink_urb(r->urb);
+ spin_unlock_irqrestore(&rx->lock, flags2);
+
+ udev->bw_switch = 1;
+
+ spin_lock_irqsave(&k_lock, flags2);
+ list_add_tail(&udev->list, &k_list);
+ spin_unlock_irqrestore(&k_lock, flags2);
+
+ wake_up(&k_wait);
+ }
+#endif /* CONFIG_WIMAX_GDM72XX_K_MODE */
+
+ ret = usb_submit_urb(t->urb, GFP_ATOMIC);
+ if (ret)
+ goto send_fail;
+
+#ifdef CONFIG_WIMAX_GDM72XX_USB_PM
+ usb_mark_last_busy(usbdev);
+#endif /* CONFIG_WIMAX_GDM72XX_USB_PM */
+
+#if defined(CONFIG_WIMAX_GDM72XX_USB_PM) || defined(CONFIG_WIMAX_GDM72XX_K_MODE)
+out:
+#endif
+ spin_unlock_irqrestore(&tx->lock, flags);
+
+ if (no_spc)
+ return -ENOSPC;
+
+ return 0;
+
+send_fail:
+ t->callback = NULL;
+ __gdm_usb_send_complete(t->urb);
+ spin_unlock_irqrestore(&tx->lock, flags);
+ return ret;
+}
+
+static void gdm_usb_rcv_complete(struct urb *urb)
+{
+ struct usb_rx *r = urb->context;
+ struct rx_cxt *rx = r->rx_cxt;
+ struct usbwm_dev *udev = container_of(r->rx_cxt, struct usbwm_dev, rx);
+ struct tx_cxt *tx = &udev->tx;
+ struct usb_tx *t;
+ u16 cmd_evt;
+ unsigned long flags, flags2;
+ struct usb_device *dev = urb->dev;
+
+ /* Completion by usb_unlink_urb */
+ if (urb->status == -ECONNRESET)
+ return;
+
+ spin_lock_irqsave(&tx->lock, flags);
+
+ if (!urb->status) {
+ cmd_evt = (r->buf[0] << 8) | (r->buf[1]);
+
+ dev_dbg(&dev->dev, "usb_receive: %*ph\n", urb->actual_length,
+ r->buf);
+
+ if (cmd_evt == WIMAX_SDU_TX_FLOW) {
+ if (r->buf[4] == 0) {
+ dev_dbg(&dev->dev, "WIMAX ==> STOP SDU TX\n");
+ list_for_each_entry(t, &tx->sdu_list, list)
+ usb_unlink_urb(t->urb);
+ } else if (r->buf[4] == 1) {
+ dev_dbg(&dev->dev, "WIMAX ==> START SDU TX\n");
+ list_for_each_entry(t, &tx->sdu_list, list) {
+ usb_submit_urb(t->urb, GFP_ATOMIC);
+ }
+ /* If free buffer for sdu tx doesn't
+ * exist, then tx queue should not be
+ * woken. For this reason, don't pass
+ * the command, START_SDU_TX.
+ */
+ if (list_empty(&tx->free_list))
+ urb->actual_length = 0;
+ }
+ }
+ }
+
+ if (!urb->status && r->callback)
+ r->callback(r->cb_data, r->buf, urb->actual_length);
+
+ spin_lock_irqsave(&rx->lock, flags2);
+ put_rx_struct(rx, r);
+ spin_unlock_irqrestore(&rx->lock, flags2);
+
+ spin_unlock_irqrestore(&tx->lock, flags);
+
+#ifdef CONFIG_WIMAX_GDM72XX_USB_PM
+ usb_mark_last_busy(dev);
+#endif
+}
+
+static int gdm_usb_receive(void *priv_dev,
+ void (*cb)(void *cb_data, void *data, int len),
+ void *cb_data)
+{
+ struct usbwm_dev *udev = priv_dev;
+ struct usb_device *usbdev = udev->usbdev;
+ struct rx_cxt *rx = &udev->rx;
+ struct usb_rx *r;
+ unsigned long flags;
+
+ if (!udev->usbdev) {
+ dev_err(&usbdev->dev, "%s: No such device\n", __func__);
+ return -ENODEV;
+ }
+
+ spin_lock_irqsave(&rx->lock, flags);
+ r = get_rx_struct(rx);
+ spin_unlock_irqrestore(&rx->lock, flags);
+
+ if (r == NULL)
+ return -ENOMEM;
+
+ r->callback = cb;
+ r->cb_data = cb_data;
+
+ usb_fill_bulk_urb(r->urb, usbdev, usb_rcvbulkpipe(usbdev, 0x82), r->buf,
+ RX_BUF_SIZE, gdm_usb_rcv_complete, r);
+
+ return usb_submit_urb(r->urb, GFP_ATOMIC);
+}
+
+#ifdef CONFIG_WIMAX_GDM72XX_USB_PM
+static void do_pm_control(struct work_struct *work)
+{
+ struct usbwm_dev *udev = container_of(work, struct usbwm_dev, pm_ws);
+ struct tx_cxt *tx = &udev->tx;
+ int ret;
+ unsigned long flags;
+
+ ret = usb_autopm_get_interface(udev->intf);
+ if (!ret)
+ usb_autopm_put_interface(udev->intf);
+
+ spin_lock_irqsave(&tx->lock, flags);
+ if (!(udev->usbdev->state & USB_STATE_SUSPENDED) &&
+ (!list_empty(&tx->hci_list) || !list_empty(&tx->sdu_list))) {
+ struct usb_tx *t, *temp;
+
+ list_for_each_entry_safe(t, temp, &tx->pending_list, p_list) {
+ list_del(&t->p_list);
+ ret = usb_submit_urb(t->urb, GFP_ATOMIC);
+
+ if (ret) {
+ t->callback = NULL;
+ __gdm_usb_send_complete(t->urb);
+ }
+ }
+ }
+ spin_unlock_irqrestore(&tx->lock, flags);
+}
+#endif /* CONFIG_WIMAX_GDM72XX_USB_PM */
+
+static int gdm_usb_probe(struct usb_interface *intf,
+ const struct usb_device_id *id)
+{
+ int ret = 0;
+ u8 bConfigurationValue;
+ struct phy_dev *phy_dev = NULL;
+ struct usbwm_dev *udev = NULL;
+ u16 idVendor, idProduct, bcdDevice;
+
+ struct usb_device *usbdev = interface_to_usbdev(intf);
+
+ usb_get_dev(usbdev);
+ bConfigurationValue = usbdev->actconfig->desc.bConfigurationValue;
+
+ /*USB description is set up with Little-Endian*/
+ idVendor = le16_to_cpu(usbdev->descriptor.idVendor);
+ idProduct = le16_to_cpu(usbdev->descriptor.idProduct);
+ bcdDevice = le16_to_cpu(usbdev->descriptor.bcdDevice);
+
+ dev_info(&intf->dev, "Found GDM USB VID = 0x%04x PID = 0x%04x...\n",
+ idVendor, idProduct);
+ dev_info(&intf->dev, "GCT WiMax driver version %s\n", DRIVER_VERSION);
+
+
+ if (idProduct == EMERGENCY_PID) {
+ ret = usb_emergency(usbdev);
+ goto out;
+ }
+
+ /* Support for EEPROM bootloader */
+ if (bConfigurationValue == DOWNLOAD_CONF_VALUE ||
+ idProduct & B_DOWNLOAD) {
+ ret = usb_boot(usbdev, bcdDevice);
+ goto out;
+ }
+
+ phy_dev = kzalloc(sizeof(*phy_dev), GFP_KERNEL);
+ if (phy_dev == NULL) {
+ ret = -ENOMEM;
+ goto out;
+ }
+ udev = kzalloc(sizeof(*udev), GFP_KERNEL);
+ if (udev == NULL) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ if (idProduct == 0x7205 || idProduct == 0x7206)
+ udev->padding = GDM7205_PADDING;
+ else
+ udev->padding = 0;
+
+ phy_dev->priv_dev = (void *)udev;
+ phy_dev->send_func = gdm_usb_send;
+ phy_dev->rcv_func = gdm_usb_receive;
+
+ ret = init_usb(udev);
+ if (ret < 0)
+ goto out;
+
+ udev->usbdev = usbdev;
+
+#ifdef CONFIG_WIMAX_GDM72XX_USB_PM
+ udev->intf = intf;
+
+ intf->needs_remote_wakeup = 1;
+ device_init_wakeup(&intf->dev, 1);
+
+ pm_runtime_set_autosuspend_delay(&usbdev->dev, 10 * 1000); /* msec */
+
+ INIT_WORK(&udev->pm_ws, do_pm_control);
+#endif /* CONFIG_WIMAX_GDM72XX_USB_PM */
+
+ ret = register_wimax_device(phy_dev, &intf->dev);
+ if (ret)
+ release_usb(udev);
+
+out:
+ if (ret) {
+ kfree(phy_dev);
+ kfree(udev);
+ usb_put_dev(usbdev);
+ } else {
+ usb_set_intfdata(intf, phy_dev);
+ }
+ return ret;
+}
+
+static void gdm_usb_disconnect(struct usb_interface *intf)
+{
+ u8 bConfigurationValue;
+ struct phy_dev *phy_dev;
+ struct usbwm_dev *udev;
+ u16 idProduct;
+ struct usb_device *usbdev = interface_to_usbdev(intf);
+
+ bConfigurationValue = usbdev->actconfig->desc.bConfigurationValue;
+ phy_dev = usb_get_intfdata(intf);
+
+ /*USB description is set up with Little-Endian*/
+ idProduct = le16_to_cpu(usbdev->descriptor.idProduct);
+
+ if (idProduct != EMERGENCY_PID &&
+ bConfigurationValue != DOWNLOAD_CONF_VALUE &&
+ (idProduct & B_DOWNLOAD) == 0) {
+ udev = phy_dev->priv_dev;
+ udev->usbdev = NULL;
+
+ unregister_wimax_device(phy_dev);
+ release_usb(udev);
+ kfree(udev);
+ kfree(phy_dev);
+ }
+
+ usb_put_dev(usbdev);
+}
+
+#ifdef CONFIG_WIMAX_GDM72XX_USB_PM
+static int gdm_suspend(struct usb_interface *intf, pm_message_t pm_msg)
+{
+ struct phy_dev *phy_dev;
+ struct usbwm_dev *udev;
+ struct rx_cxt *rx;
+ struct usb_rx *r;
+ unsigned long flags;
+
+ phy_dev = usb_get_intfdata(intf);
+ if (!phy_dev)
+ return 0;
+
+ udev = phy_dev->priv_dev;
+ rx = &udev->rx;
+
+ spin_lock_irqsave(&rx->lock, flags);
+
+ list_for_each_entry(r, &rx->used_list, list)
+ usb_unlink_urb(r->urb);
+
+ spin_unlock_irqrestore(&rx->lock, flags);
+
+ return 0;
+}
+
+static int gdm_resume(struct usb_interface *intf)
+{
+ struct phy_dev *phy_dev;
+ struct usbwm_dev *udev;
+ struct rx_cxt *rx;
+ struct usb_rx *r;
+ unsigned long flags;
+
+ phy_dev = usb_get_intfdata(intf);
+ if (!phy_dev)
+ return 0;
+
+ udev = phy_dev->priv_dev;
+ rx = &udev->rx;
+
+ spin_lock_irqsave(&rx->lock, flags);
+
+ list_for_each_entry(r, &rx->used_list, list)
+ usb_submit_urb(r->urb, GFP_ATOMIC);
+
+ spin_unlock_irqrestore(&rx->lock, flags);
+
+ return 0;
+}
+
+#endif /* CONFIG_WIMAX_GDM72XX_USB_PM */
+
+#ifdef CONFIG_WIMAX_GDM72XX_K_MODE
+static int k_mode_thread(void *arg)
+{
+ struct usbwm_dev *udev;
+ struct tx_cxt *tx;
+ struct rx_cxt *rx;
+ struct usb_tx *t, *temp;
+ struct usb_rx *r;
+ unsigned long flags, flags2, expire;
+ int ret;
+
+ while (!k_mode_stop) {
+ spin_lock_irqsave(&k_lock, flags2);
+ while (!list_empty(&k_list)) {
+ udev = list_entry(k_list.next, struct usbwm_dev, list);
+ tx = &udev->tx;
+ rx = &udev->rx;
+
+ list_del(&udev->list);
+ spin_unlock_irqrestore(&k_lock, flags2);
+
+ expire = jiffies + K_WAIT_TIME;
+ while (time_before(jiffies, expire))
+ schedule_timeout(K_WAIT_TIME);
+
+ spin_lock_irqsave(&rx->lock, flags);
+
+ list_for_each_entry(r, &rx->used_list, list)
+ usb_submit_urb(r->urb, GFP_ATOMIC);
+
+ spin_unlock_irqrestore(&rx->lock, flags);
+
+ spin_lock_irqsave(&tx->lock, flags);
+
+ list_for_each_entry_safe(t, temp, &tx->pending_list,
+ p_list) {
+ list_del(&t->p_list);
+ ret = usb_submit_urb(t->urb, GFP_ATOMIC);
+
+ if (ret) {
+ t->callback = NULL;
+ __gdm_usb_send_complete(t->urb);
+ }
+ }
+
+ udev->bw_switch = 0;
+ spin_unlock_irqrestore(&tx->lock, flags);
+
+ spin_lock_irqsave(&k_lock, flags2);
+ }
+ wait_event_interruptible_lock_irq(k_wait,
+ !list_empty(&k_list) ||
+ k_mode_stop, k_lock);
+ spin_unlock_irqrestore(&k_lock, flags2);
+ }
+ return 0;
+}
+#endif /* CONFIG_WIMAX_GDM72XX_K_MODE */
+
+static struct usb_driver gdm_usb_driver = {
+ .name = "gdm_wimax",
+ .probe = gdm_usb_probe,
+ .disconnect = gdm_usb_disconnect,
+ .id_table = id_table,
+#ifdef CONFIG_WIMAX_GDM72XX_USB_PM
+ .supports_autosuspend = 1,
+ .suspend = gdm_suspend,
+ .resume = gdm_resume,
+ .reset_resume = gdm_resume,
+#endif
+};
+
+static int __init usb_gdm_wimax_init(void)
+{
+#ifdef CONFIG_WIMAX_GDM72XX_K_MODE
+ kthread_run(k_mode_thread, NULL, "k_mode_wimax");
+#endif /* CONFIG_WIMAX_GDM72XX_K_MODE */
+ return usb_register(&gdm_usb_driver);
+}
+
+static void __exit usb_gdm_wimax_exit(void)
+{
+#ifdef CONFIG_WIMAX_GDM72XX_K_MODE
+ k_mode_stop = 1;
+ wake_up(&k_wait);
+#endif
+ usb_deregister(&gdm_usb_driver);
+}
+
+module_init(usb_gdm_wimax_init);
+module_exit(usb_gdm_wimax_exit);
+
+MODULE_VERSION(DRIVER_VERSION);
+MODULE_DESCRIPTION("GCT WiMax Device Driver");
+MODULE_AUTHOR("Ethan Park");
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/gdm72xx/gdm_usb.h b/drivers/staging/gdm72xx/gdm_usb.h
new file mode 100644
index 000000000..8e58a25e7
--- /dev/null
+++ b/drivers/staging/gdm72xx/gdm_usb.h
@@ -0,0 +1,78 @@
+/*
+ * Copyright (c) 2012 GCT Semiconductor, Inc. All rights reserved.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ */
+
+#ifndef __GDM72XX_GDM_USB_H__
+#define __GDM72XX_GDM_USB_H__
+
+#include <linux/types.h>
+#include <linux/usb.h>
+#include <linux/list.h>
+
+#define B_DIFF_DL_DRV (1 << 4)
+#define B_DOWNLOAD (1 << 5)
+#define MAX_NR_SDU_BUF 64
+
+struct usb_tx {
+ struct list_head list;
+#if defined(CONFIG_WIMAX_GDM72XX_USB_PM) || defined(CONFIG_WIMAX_GDM72XX_K_MODE)
+ struct list_head p_list;
+#endif
+ struct tx_cxt *tx_cxt;
+ struct urb *urb;
+ u8 *buf;
+ void (*callback)(void *cb_data);
+ void *cb_data;
+};
+
+struct tx_cxt {
+ struct list_head free_list;
+ struct list_head sdu_list;
+ struct list_head hci_list;
+#if defined(CONFIG_WIMAX_GDM72XX_USB_PM) || defined(CONFIG_WIMAX_GDM72XX_K_MODE)
+ struct list_head pending_list;
+#endif
+ spinlock_t lock;
+};
+
+struct usb_rx {
+ struct list_head list;
+ struct rx_cxt *rx_cxt;
+ struct urb *urb;
+ u8 *buf;
+ void (*callback)(void *cb_data, void *data, int len);
+ void *cb_data;
+};
+
+struct rx_cxt {
+ struct list_head free_list;
+ struct list_head used_list;
+ spinlock_t lock;
+};
+
+struct usbwm_dev {
+ struct usb_device *usbdev;
+#ifdef CONFIG_WIMAX_GDM72XX_USB_PM
+ struct work_struct pm_ws;
+
+ struct usb_interface *intf;
+#endif
+#ifdef CONFIG_WIMAX_GDM72XX_K_MODE
+ int bw_switch;
+ struct list_head list;
+#endif
+ struct tx_cxt tx;
+ struct rx_cxt rx;
+ int padding;
+};
+
+#endif /* __GDM72XX_GDM_USB_H__ */
diff --git a/drivers/staging/gdm72xx/gdm_wimax.c b/drivers/staging/gdm72xx/gdm_wimax.c
new file mode 100644
index 000000000..61d168e82
--- /dev/null
+++ b/drivers/staging/gdm72xx/gdm_wimax.c
@@ -0,0 +1,816 @@
+/*
+ * Copyright (c) 2012 GCT Semiconductor, Inc. All rights reserved.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/etherdevice.h>
+#include <asm/byteorder.h>
+#include <linux/ip.h>
+#include <linux/ipv6.h>
+#include <linux/udp.h>
+#include <linux/in.h>
+
+#include "gdm_wimax.h"
+#include "hci.h"
+#include "wm_ioctl.h"
+#include "netlink_k.h"
+
+#define gdm_wimax_send(n, d, l) \
+ (n->phy_dev->send_func)(n->phy_dev->priv_dev, d, l, NULL, NULL)
+#define gdm_wimax_send_with_cb(n, d, l, c, b) \
+ (n->phy_dev->send_func)(n->phy_dev->priv_dev, d, l, c, b)
+#define gdm_wimax_rcv_with_cb(n, c, b) \
+ (n->phy_dev->rcv_func)(n->phy_dev->priv_dev, c, b)
+
+#define EVT_MAX_SIZE 2048
+
+struct evt_entry {
+ struct list_head list;
+ struct net_device *dev;
+ char evt_data[EVT_MAX_SIZE];
+ int size;
+};
+
+static struct {
+ int ref_cnt;
+ struct sock *sock;
+ struct list_head evtq;
+ spinlock_t evt_lock;
+ struct list_head freeq;
+ struct work_struct ws;
+} wm_event;
+
+static u8 gdm_wimax_macaddr[6] = {0x00, 0x0a, 0x3b, 0xf0, 0x01, 0x30};
+
+static inline int gdm_wimax_header(struct sk_buff **pskb)
+{
+ u16 buf[HCI_HEADER_SIZE / sizeof(u16)];
+ struct hci_s *hci = (struct hci_s *)buf;
+ struct sk_buff *skb = *pskb;
+
+ if (unlikely(skb_headroom(skb) < HCI_HEADER_SIZE)) {
+ struct sk_buff *skb2;
+
+ skb2 = skb_realloc_headroom(skb, HCI_HEADER_SIZE);
+ if (skb2 == NULL)
+ return -ENOMEM;
+ if (skb->sk)
+ skb_set_owner_w(skb2, skb->sk);
+ kfree_skb(skb);
+ skb = skb2;
+ }
+
+ skb_push(skb, HCI_HEADER_SIZE);
+ hci->cmd_evt = cpu_to_be16(WIMAX_TX_SDU);
+ hci->length = cpu_to_be16(skb->len - HCI_HEADER_SIZE);
+ memcpy(skb->data, buf, HCI_HEADER_SIZE);
+
+ *pskb = skb;
+ return 0;
+}
+
+static inline struct evt_entry *alloc_event_entry(void)
+{
+ return kmalloc(sizeof(struct evt_entry), GFP_ATOMIC);
+}
+
+static inline void free_event_entry(struct evt_entry *e)
+{
+ kfree(e);
+}
+
+static struct evt_entry *get_event_entry(void)
+{
+ struct evt_entry *e;
+
+ if (list_empty(&wm_event.freeq)) {
+ e = alloc_event_entry();
+ } else {
+ e = list_entry(wm_event.freeq.next, struct evt_entry, list);
+ list_del(&e->list);
+ }
+
+ return e;
+}
+
+static void put_event_entry(struct evt_entry *e)
+{
+ BUG_ON(!e);
+
+ list_add_tail(&e->list, &wm_event.freeq);
+}
+
+static void gdm_wimax_event_rcv(struct net_device *dev, u16 type, void *msg,
+ int len)
+{
+ struct nic *nic = netdev_priv(dev);
+
+ u8 *buf = msg;
+ u16 hci_cmd = (buf[0]<<8) | buf[1];
+ u16 hci_len = (buf[2]<<8) | buf[3];
+
+ netdev_dbg(dev, "H=>D: 0x%04x(%d)\n", hci_cmd, hci_len);
+
+ gdm_wimax_send(nic, msg, len);
+}
+
+static void __gdm_wimax_event_send(struct work_struct *work)
+{
+ int idx;
+ unsigned long flags;
+ struct evt_entry *e;
+ struct evt_entry *tmp;
+
+ spin_lock_irqsave(&wm_event.evt_lock, flags);
+
+ list_for_each_entry_safe(e, tmp, &wm_event.evtq, list) {
+ spin_unlock_irqrestore(&wm_event.evt_lock, flags);
+
+ if (sscanf(e->dev->name, "wm%d", &idx) == 1)
+ netlink_send(wm_event.sock, idx, 0, e->evt_data,
+ e->size);
+
+ spin_lock_irqsave(&wm_event.evt_lock, flags);
+ list_del(&e->list);
+ put_event_entry(e);
+ }
+
+ spin_unlock_irqrestore(&wm_event.evt_lock, flags);
+}
+
+static int gdm_wimax_event_init(void)
+{
+ if (!wm_event.ref_cnt) {
+ wm_event.sock = netlink_init(NETLINK_WIMAX,
+ gdm_wimax_event_rcv);
+ if (wm_event.sock) {
+ INIT_LIST_HEAD(&wm_event.evtq);
+ INIT_LIST_HEAD(&wm_event.freeq);
+ INIT_WORK(&wm_event.ws, __gdm_wimax_event_send);
+ spin_lock_init(&wm_event.evt_lock);
+ }
+ }
+
+ if (wm_event.sock) {
+ wm_event.ref_cnt++;
+ return 0;
+ }
+
+ pr_err("Creating WiMax Event netlink is failed\n");
+ return -1;
+}
+
+static void gdm_wimax_event_exit(void)
+{
+ if (wm_event.sock && --wm_event.ref_cnt == 0) {
+ struct evt_entry *e, *temp;
+ unsigned long flags;
+
+ spin_lock_irqsave(&wm_event.evt_lock, flags);
+
+ list_for_each_entry_safe(e, temp, &wm_event.evtq, list) {
+ list_del(&e->list);
+ free_event_entry(e);
+ }
+ list_for_each_entry_safe(e, temp, &wm_event.freeq, list) {
+ list_del(&e->list);
+ free_event_entry(e);
+ }
+
+ spin_unlock_irqrestore(&wm_event.evt_lock, flags);
+ netlink_exit(wm_event.sock);
+ wm_event.sock = NULL;
+ }
+}
+
+static int gdm_wimax_event_send(struct net_device *dev, char *buf, int size)
+{
+ struct evt_entry *e;
+ unsigned long flags;
+
+ u16 hci_cmd = ((u8)buf[0]<<8) | (u8)buf[1];
+ u16 hci_len = ((u8)buf[2]<<8) | (u8)buf[3];
+
+ netdev_dbg(dev, "D=>H: 0x%04x(%d)\n", hci_cmd, hci_len);
+
+ spin_lock_irqsave(&wm_event.evt_lock, flags);
+
+ e = get_event_entry();
+ if (!e) {
+ netdev_err(dev, "%s: No memory for event\n", __func__);
+ spin_unlock_irqrestore(&wm_event.evt_lock, flags);
+ return -ENOMEM;
+ }
+
+ e->dev = dev;
+ e->size = size;
+ memcpy(e->evt_data, buf, size);
+
+ list_add_tail(&e->list, &wm_event.evtq);
+ spin_unlock_irqrestore(&wm_event.evt_lock, flags);
+
+ schedule_work(&wm_event.ws);
+
+ return 0;
+}
+
+static void tx_complete(void *arg)
+{
+ struct nic *nic = arg;
+
+ if (netif_queue_stopped(nic->netdev))
+ netif_wake_queue(nic->netdev);
+}
+
+int gdm_wimax_send_tx(struct sk_buff *skb, struct net_device *dev)
+{
+ int ret = 0;
+ struct nic *nic = netdev_priv(dev);
+
+ ret = gdm_wimax_send_with_cb(nic, skb->data, skb->len, tx_complete,
+ nic);
+ if (ret == -ENOSPC) {
+ netif_stop_queue(dev);
+ ret = 0;
+ }
+
+ if (ret) {
+ skb_pull(skb, HCI_HEADER_SIZE);
+ return ret;
+ }
+
+ dev->stats.tx_packets++;
+ dev->stats.tx_bytes += skb->len - HCI_HEADER_SIZE;
+ kfree_skb(skb);
+ return ret;
+}
+
+static int gdm_wimax_tx(struct sk_buff *skb, struct net_device *dev)
+{
+ int ret = 0;
+
+ ret = gdm_wimax_header(&skb);
+ if (ret < 0) {
+ skb_pull(skb, HCI_HEADER_SIZE);
+ return ret;
+ }
+
+#if defined(CONFIG_WIMAX_GDM72XX_QOS)
+ ret = gdm_qos_send_hci_pkt(skb, dev);
+#else
+ ret = gdm_wimax_send_tx(skb, dev);
+#endif
+ return ret;
+}
+
+static int gdm_wimax_set_config(struct net_device *dev, struct ifmap *map)
+{
+ if (dev->flags & IFF_UP)
+ return -EBUSY;
+
+ return 0;
+}
+
+static void __gdm_wimax_set_mac_addr(struct net_device *dev, char *mac_addr)
+{
+ u16 hci_pkt_buf[32 / sizeof(u16)];
+ struct hci_s *hci = (struct hci_s *)hci_pkt_buf;
+ struct nic *nic = netdev_priv(dev);
+
+ /* Since dev is registered as a ethernet device,
+ * ether_setup has made dev->addr_len to be ETH_ALEN
+ */
+ memcpy(dev->dev_addr, mac_addr, dev->addr_len);
+
+ /* Let lower layer know of this change by sending
+ * SetInformation(MAC Address)
+ */
+ hci->cmd_evt = cpu_to_be16(WIMAX_SET_INFO);
+ hci->length = cpu_to_be16(8);
+ hci->data[0] = 0; /* T */
+ hci->data[1] = 6; /* L */
+ memcpy(&hci->data[2], mac_addr, dev->addr_len); /* V */
+
+ gdm_wimax_send(nic, hci, HCI_HEADER_SIZE + 8);
+}
+
+/* A driver function */
+static int gdm_wimax_set_mac_addr(struct net_device *dev, void *p)
+{
+ struct sockaddr *addr = p;
+
+ if (netif_running(dev))
+ return -EBUSY;
+
+ if (!is_valid_ether_addr(addr->sa_data))
+ return -EADDRNOTAVAIL;
+
+ __gdm_wimax_set_mac_addr(dev, addr->sa_data);
+
+ return 0;
+}
+
+static void gdm_wimax_ind_if_updown(struct net_device *dev, int if_up)
+{
+ u16 buf[32 / sizeof(u16)];
+ struct hci_s *hci = (struct hci_s *)buf;
+ unsigned char up_down;
+
+ up_down = if_up ? WIMAX_IF_UP : WIMAX_IF_DOWN;
+
+ /* Indicate updating fsm */
+ hci->cmd_evt = cpu_to_be16(WIMAX_IF_UPDOWN);
+ hci->length = cpu_to_be16(sizeof(up_down));
+ hci->data[0] = up_down;
+
+ gdm_wimax_event_send(dev, (char *)hci, HCI_HEADER_SIZE+sizeof(up_down));
+}
+
+static int gdm_wimax_open(struct net_device *dev)
+{
+ struct nic *nic = netdev_priv(dev);
+ struct fsm_s *fsm = (struct fsm_s *)nic->sdk_data[SIOC_DATA_FSM].buf;
+
+ netif_start_queue(dev);
+
+ if (fsm && fsm->m_status != M_INIT)
+ gdm_wimax_ind_if_updown(dev, 1);
+ return 0;
+}
+
+static int gdm_wimax_close(struct net_device *dev)
+{
+ struct nic *nic = netdev_priv(dev);
+ struct fsm_s *fsm = (struct fsm_s *)nic->sdk_data[SIOC_DATA_FSM].buf;
+
+ netif_stop_queue(dev);
+
+ if (fsm && fsm->m_status != M_INIT)
+ gdm_wimax_ind_if_updown(dev, 0);
+ return 0;
+}
+
+static void kdelete(void **buf)
+{
+ if (buf && *buf) {
+ kfree(*buf);
+ *buf = NULL;
+ }
+}
+
+static int gdm_wimax_ioctl_get_data(struct data_s *dst, struct data_s *src)
+{
+ int size;
+
+ size = dst->size < src->size ? dst->size : src->size;
+
+ dst->size = size;
+ if (src->size) {
+ if (!dst->buf)
+ return -EINVAL;
+ if (copy_to_user((void __user *)dst->buf, src->buf, size))
+ return -EFAULT;
+ }
+ return 0;
+}
+
+static int gdm_wimax_ioctl_set_data(struct data_s *dst, struct data_s *src)
+{
+ if (!src->size) {
+ dst->size = 0;
+ return 0;
+ }
+
+ if (!src->buf)
+ return -EINVAL;
+
+ if (!(dst->buf && dst->size == src->size)) {
+ kdelete(&dst->buf);
+ dst->buf = kmalloc(src->size, GFP_KERNEL);
+ if (dst->buf == NULL)
+ return -ENOMEM;
+ }
+
+ if (copy_from_user(dst->buf, (void __user *)src->buf, src->size)) {
+ kdelete(&dst->buf);
+ return -EFAULT;
+ }
+ dst->size = src->size;
+ return 0;
+}
+
+static void gdm_wimax_cleanup_ioctl(struct net_device *dev)
+{
+ struct nic *nic = netdev_priv(dev);
+ int i;
+
+ for (i = 0; i < SIOC_DATA_MAX; i++)
+ kdelete(&nic->sdk_data[i].buf);
+}
+
+static void gdm_wimax_ind_fsm_update(struct net_device *dev, struct fsm_s *fsm)
+{
+ u16 buf[32 / sizeof(u16)];
+ struct hci_s *hci = (struct hci_s *)buf;
+
+ /* Indicate updating fsm */
+ hci->cmd_evt = cpu_to_be16(WIMAX_FSM_UPDATE);
+ hci->length = cpu_to_be16(sizeof(struct fsm_s));
+ memcpy(&hci->data[0], fsm, sizeof(struct fsm_s));
+
+ gdm_wimax_event_send(dev, (char *)hci,
+ HCI_HEADER_SIZE + sizeof(struct fsm_s));
+}
+
+static void gdm_update_fsm(struct net_device *dev, struct fsm_s *new_fsm)
+{
+ struct nic *nic = netdev_priv(dev);
+ struct fsm_s *cur_fsm = (struct fsm_s *)
+ nic->sdk_data[SIOC_DATA_FSM].buf;
+
+ if (!cur_fsm)
+ return;
+
+ if (cur_fsm->m_status != new_fsm->m_status ||
+ cur_fsm->c_status != new_fsm->c_status) {
+ if (new_fsm->m_status == M_CONNECTED) {
+ netif_carrier_on(dev);
+ } else if (cur_fsm->m_status == M_CONNECTED) {
+ netif_carrier_off(dev);
+ #if defined(CONFIG_WIMAX_GDM72XX_QOS)
+ gdm_qos_release_list(nic);
+ #endif
+ }
+ gdm_wimax_ind_fsm_update(dev, new_fsm);
+ }
+}
+
+static int gdm_wimax_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
+{
+ struct wm_req_s *req = (struct wm_req_s *)ifr;
+ struct nic *nic = netdev_priv(dev);
+ int ret;
+
+ if (cmd != SIOCWMIOCTL)
+ return -EOPNOTSUPP;
+
+ switch (req->cmd) {
+ case SIOCG_DATA:
+ case SIOCS_DATA:
+ if (req->data_id >= SIOC_DATA_MAX) {
+ netdev_err(dev, "%s error: data-index(%d) is invalid!!\n",
+ __func__, req->data_id);
+ return -EOPNOTSUPP;
+ }
+ if (req->cmd == SIOCG_DATA) {
+ ret = gdm_wimax_ioctl_get_data(
+ &req->data, &nic->sdk_data[req->data_id]);
+ if (ret < 0)
+ return ret;
+ } else if (req->cmd == SIOCS_DATA) {
+ if (req->data_id == SIOC_DATA_FSM) {
+ /* NOTE: gdm_update_fsm should be called
+ * before gdm_wimax_ioctl_set_data is called.
+ */
+ gdm_update_fsm(dev,
+ (struct fsm_s *)req->data.buf);
+ }
+ ret = gdm_wimax_ioctl_set_data(
+ &nic->sdk_data[req->data_id], &req->data);
+ if (ret < 0)
+ return ret;
+ }
+ break;
+ default:
+ netdev_err(dev, "%s: %x unknown ioctl\n", __func__, cmd);
+ return -EOPNOTSUPP;
+ }
+
+ return 0;
+}
+
+static void gdm_wimax_prepare_device(struct net_device *dev)
+{
+ struct nic *nic = netdev_priv(dev);
+ u16 buf[32 / sizeof(u16)];
+ struct hci_s *hci = (struct hci_s *)buf;
+ u16 len = 0;
+ u32 val = 0;
+ __be32 val_be32;
+
+ /* GetInformation mac address */
+ len = 0;
+ hci->cmd_evt = cpu_to_be16(WIMAX_GET_INFO);
+ hci->data[len++] = TLV_T(T_MAC_ADDRESS);
+ hci->length = cpu_to_be16(len);
+ gdm_wimax_send(nic, hci, HCI_HEADER_SIZE+len);
+
+ val = T_CAPABILITY_WIMAX | T_CAPABILITY_MULTI_CS;
+ #if defined(CONFIG_WIMAX_GDM72XX_QOS)
+ val |= T_CAPABILITY_QOS;
+ #endif
+ #if defined(CONFIG_WIMAX_GDM72XX_WIMAX2)
+ val |= T_CAPABILITY_AGGREGATION;
+ #endif
+
+ /* Set capability */
+ len = 0;
+ hci->cmd_evt = cpu_to_be16(WIMAX_SET_INFO);
+ hci->data[len++] = TLV_T(T_CAPABILITY);
+ hci->data[len++] = TLV_L(T_CAPABILITY);
+ val_be32 = cpu_to_be32(val);
+ memcpy(&hci->data[len], &val_be32, TLV_L(T_CAPABILITY));
+ len += TLV_L(T_CAPABILITY);
+ hci->length = cpu_to_be16(len);
+ gdm_wimax_send(nic, hci, HCI_HEADER_SIZE+len);
+
+ netdev_info(dev, "GDM WiMax Set CAPABILITY: 0x%08X\n", val);
+}
+
+static int gdm_wimax_hci_get_tlv(u8 *buf, u8 *T, u16 *L, u8 **V)
+{
+ #define __U82U16(b) ((u16)((u8 *)(b))[0] | ((u16)((u8 *)(b))[1] << 8))
+ int next_pos;
+
+ *T = buf[0];
+ if (buf[1] == 0x82) {
+ *L = be16_to_cpu(__U82U16(&buf[2]));
+ next_pos = 1/*type*/+3/*len*/;
+ } else {
+ *L = buf[1];
+ next_pos = 1/*type*/+1/*len*/;
+ }
+ *V = &buf[next_pos];
+
+ next_pos += *L/*length of val*/;
+ return next_pos;
+}
+
+static int gdm_wimax_get_prepared_info(struct net_device *dev, char *buf,
+ int len)
+{
+ u8 T, *V;
+ u16 L;
+ u16 cmd_evt, cmd_len;
+ int pos = HCI_HEADER_SIZE;
+
+ cmd_evt = be16_to_cpup((const __be16 *)&buf[0]);
+ cmd_len = be16_to_cpup((const __be16 *)&buf[2]);
+
+ if (len < cmd_len + HCI_HEADER_SIZE) {
+ netdev_err(dev, "%s: invalid length [%d/%d]\n", __func__,
+ cmd_len + HCI_HEADER_SIZE, len);
+ return -1;
+ }
+
+ if (cmd_evt == WIMAX_GET_INFO_RESULT) {
+ if (cmd_len < 2) {
+ netdev_err(dev, "%s: len is too short [%x/%d]\n",
+ __func__, cmd_evt, len);
+ return -1;
+ }
+
+ pos += gdm_wimax_hci_get_tlv(&buf[pos], &T, &L, &V);
+ if (T == TLV_T(T_MAC_ADDRESS)) {
+ if (L != dev->addr_len) {
+ netdev_err(dev,
+ "%s Invalid inofrmation result T/L [%x/%d]\n",
+ __func__, T, L);
+ return -1;
+ }
+ netdev_info(dev, "MAC change [%pM]->[%pM]\n",
+ dev->dev_addr, V);
+ memcpy(dev->dev_addr, V, dev->addr_len);
+ return 1;
+ }
+ }
+
+ gdm_wimax_event_send(dev, buf, len);
+ return 0;
+}
+
+static void gdm_wimax_netif_rx(struct net_device *dev, char *buf, int len)
+{
+ struct sk_buff *skb;
+ int ret;
+
+ skb = dev_alloc_skb(len + 2);
+ if (!skb)
+ return;
+ skb_reserve(skb, 2);
+
+ dev->stats.rx_packets++;
+ dev->stats.rx_bytes += len;
+
+ memcpy(skb_put(skb, len), buf, len);
+
+ skb->dev = dev;
+ skb->protocol = eth_type_trans(skb, dev); /* what will happen? */
+
+ ret = in_interrupt() ? netif_rx(skb) : netif_rx_ni(skb);
+ if (ret == NET_RX_DROP)
+ netdev_err(dev, "%s skb dropped\n", __func__);
+}
+
+static void gdm_wimax_transmit_aggr_pkt(struct net_device *dev, char *buf,
+ int len)
+{
+ #define HCI_PADDING_BYTE 4
+ #define HCI_RESERVED_BYTE 4
+ struct hci_s *hci;
+ int length;
+
+ while (len > 0) {
+ hci = (struct hci_s *)buf;
+
+ if (hci->cmd_evt != cpu_to_be16(WIMAX_RX_SDU)) {
+ netdev_err(dev, "Wrong cmd_evt(0x%04X)\n",
+ be16_to_cpu(hci->cmd_evt));
+ break;
+ }
+
+ length = be16_to_cpu(hci->length);
+ gdm_wimax_netif_rx(dev, hci->data, length);
+
+ if (length & 0x3) {
+ /* Add padding size */
+ length += HCI_PADDING_BYTE - (length & 0x3);
+ }
+
+ length += HCI_HEADER_SIZE + HCI_RESERVED_BYTE;
+ len -= length;
+ buf += length;
+ }
+}
+
+static void gdm_wimax_transmit_pkt(struct net_device *dev, char *buf, int len)
+{
+ #if defined(CONFIG_WIMAX_GDM72XX_QOS)
+ struct nic *nic = netdev_priv(dev);
+ #endif
+ u16 cmd_evt, cmd_len;
+
+ /* This code is added for certain rx packet to be ignored. */
+ if (len == 0)
+ return;
+
+ cmd_evt = be16_to_cpup((const __be16 *)&buf[0]);
+ cmd_len = be16_to_cpup((const __be16 *)&buf[2]);
+
+ if (len < cmd_len + HCI_HEADER_SIZE) {
+ if (len)
+ netdev_err(dev, "%s: invalid length [%d/%d]\n",
+ __func__, cmd_len + HCI_HEADER_SIZE, len);
+ return;
+ }
+
+ switch (cmd_evt) {
+ case WIMAX_RX_SDU_AGGR:
+ gdm_wimax_transmit_aggr_pkt(dev, &buf[HCI_HEADER_SIZE],
+ cmd_len);
+ break;
+ case WIMAX_RX_SDU:
+ gdm_wimax_netif_rx(dev, &buf[HCI_HEADER_SIZE], cmd_len);
+ break;
+ #if defined(CONFIG_WIMAX_GDM72XX_QOS)
+ case WIMAX_EVT_MODEM_REPORT:
+ gdm_recv_qos_hci_packet(nic, buf, len);
+ break;
+ #endif
+ case WIMAX_SDU_TX_FLOW:
+ if (buf[4] == 0) {
+ if (!netif_queue_stopped(dev))
+ netif_stop_queue(dev);
+ } else if (buf[4] == 1) {
+ if (netif_queue_stopped(dev))
+ netif_wake_queue(dev);
+ }
+ break;
+ default:
+ gdm_wimax_event_send(dev, buf, len);
+ break;
+ }
+}
+
+static void rx_complete(void *arg, void *data, int len)
+{
+ struct nic *nic = arg;
+
+ gdm_wimax_transmit_pkt(nic->netdev, data, len);
+ gdm_wimax_rcv_with_cb(nic, rx_complete, nic);
+}
+
+static void prepare_rx_complete(void *arg, void *data, int len)
+{
+ struct nic *nic = arg;
+ int ret;
+
+ ret = gdm_wimax_get_prepared_info(nic->netdev, data, len);
+ if (ret == 1) {
+ gdm_wimax_rcv_with_cb(nic, rx_complete, nic);
+ } else {
+ if (ret < 0)
+ netdev_err(nic->netdev,
+ "get_prepared_info failed(%d)\n", ret);
+ gdm_wimax_rcv_with_cb(nic, prepare_rx_complete, nic);
+ }
+}
+
+static void start_rx_proc(struct nic *nic)
+{
+ gdm_wimax_rcv_with_cb(nic, prepare_rx_complete, nic);
+}
+
+static struct net_device_ops gdm_netdev_ops = {
+ .ndo_open = gdm_wimax_open,
+ .ndo_stop = gdm_wimax_close,
+ .ndo_set_config = gdm_wimax_set_config,
+ .ndo_start_xmit = gdm_wimax_tx,
+ .ndo_set_mac_address = gdm_wimax_set_mac_addr,
+ .ndo_do_ioctl = gdm_wimax_ioctl,
+};
+
+int register_wimax_device(struct phy_dev *phy_dev, struct device *pdev)
+{
+ struct nic *nic = NULL;
+ struct net_device *dev;
+ int ret;
+
+ dev = alloc_netdev(sizeof(*nic), "wm%d", NET_NAME_UNKNOWN,
+ ether_setup);
+
+ if (!dev) {
+ pr_err("alloc_etherdev failed\n");
+ return -ENOMEM;
+ }
+
+ SET_NETDEV_DEV(dev, pdev);
+ dev->mtu = 1400;
+ dev->netdev_ops = &gdm_netdev_ops;
+ dev->flags &= ~IFF_MULTICAST;
+ memcpy(dev->dev_addr, gdm_wimax_macaddr, sizeof(gdm_wimax_macaddr));
+
+ nic = netdev_priv(dev);
+ nic->netdev = dev;
+ nic->phy_dev = phy_dev;
+ phy_dev->netdev = dev;
+
+ /* event socket init */
+ ret = gdm_wimax_event_init();
+ if (ret < 0) {
+ pr_err("Cannot create event.\n");
+ goto cleanup;
+ }
+
+ ret = register_netdev(dev);
+ if (ret)
+ goto cleanup;
+
+ netif_carrier_off(dev);
+
+#ifdef CONFIG_WIMAX_GDM72XX_QOS
+ gdm_qos_init(nic);
+#endif
+
+ start_rx_proc(nic);
+
+ /* Prepare WiMax device */
+ gdm_wimax_prepare_device(dev);
+
+ return 0;
+
+cleanup:
+ pr_err("register_netdev failed\n");
+ free_netdev(dev);
+ return ret;
+}
+
+void unregister_wimax_device(struct phy_dev *phy_dev)
+{
+ struct nic *nic = netdev_priv(phy_dev->netdev);
+ struct fsm_s *fsm = (struct fsm_s *)nic->sdk_data[SIOC_DATA_FSM].buf;
+
+ if (fsm)
+ fsm->m_status = M_INIT;
+ unregister_netdev(nic->netdev);
+
+ gdm_wimax_event_exit();
+
+#if defined(CONFIG_WIMAX_GDM72XX_QOS)
+ gdm_qos_release_list(nic);
+#endif
+
+ gdm_wimax_cleanup_ioctl(phy_dev->netdev);
+
+ free_netdev(nic->netdev);
+}
diff --git a/drivers/staging/gdm72xx/gdm_wimax.h b/drivers/staging/gdm72xx/gdm_wimax.h
new file mode 100644
index 000000000..3330cd798
--- /dev/null
+++ b/drivers/staging/gdm72xx/gdm_wimax.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2012 GCT Semiconductor, Inc. All rights reserved.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ */
+
+#ifndef __GDM72XX_GDM_WIMAX_H__
+#define __GDM72XX_GDM_WIMAX_H__
+
+#include <linux/netdevice.h>
+#include <linux/types.h>
+#include "wm_ioctl.h"
+#if defined(CONFIG_WIMAX_GDM72XX_QOS)
+#include "gdm_qos.h"
+#endif
+
+#define DRIVER_VERSION "3.2.3"
+
+struct phy_dev {
+ void *priv_dev;
+ struct net_device *netdev;
+ int (*send_func)(void *priv_dev, void *data, int len,
+ void (*cb)(void *cb_data), void *cb_data);
+ int (*rcv_func)(void *priv_dev,
+ void (*cb)(void *cb_data, void *data, int len),
+ void *cb_data);
+};
+
+struct nic {
+ struct net_device *netdev;
+ struct phy_dev *phy_dev;
+ struct data_s sdk_data[SIOC_DATA_MAX];
+#if defined(CONFIG_WIMAX_GDM72XX_QOS)
+ struct qos_cb_s qos;
+#endif
+};
+
+int register_wimax_device(struct phy_dev *phy_dev, struct device *pdev);
+int gdm_wimax_send_tx(struct sk_buff *skb, struct net_device *dev);
+void unregister_wimax_device(struct phy_dev *phy_dev);
+
+#endif /* __GDM72XX_GDM_WIMAX_H__ */
diff --git a/drivers/staging/gdm72xx/hci.h b/drivers/staging/gdm72xx/hci.h
new file mode 100644
index 000000000..10a6bfa6e
--- /dev/null
+++ b/drivers/staging/gdm72xx/hci.h
@@ -0,0 +1,213 @@
+/*
+ * Copyright (c) 2012 GCT Semiconductor, Inc. All rights reserved.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ */
+
+#ifndef __GDM72XX_HCI_H__
+#define __GDM72XX_HCI_H__
+
+#define HCI_HEADER_SIZE 4
+#define HCI_VALUE_OFFS (HCI_HEADER_SIZE)
+#define HCI_MAX_PACKET 2048
+#define HCI_MAX_PARAM (HCI_MAX_PACKET-HCI_HEADER_SIZE)
+#define HCI_MAX_TLV 32
+
+/* CMD-EVT */
+
+/* Category 0 */
+#define WIMAX_RESET 0x0000
+#define WIMAX_SET_INFO 0x0001
+#define WIMAX_GET_INFO 0x0002
+#define WIMAX_GET_INFO_RESULT 0x8003
+#define WIMAX_RADIO_OFF 0x0004
+#define WIMAX_RADIO_ON 0x0006
+#define WIMAX_WIMAX_RESET 0x0007 /* Is this still here */
+
+/* Category 1 */
+#define WIMAX_NET_ENTRY 0x0100
+#define WIMAX_NET_DISCONN 0x0102
+#define WIMAX_ENTER_SLEEP 0x0103
+#define WIMAX_EXIT_SLEEP 0x0104
+#define WIMAX_ENTER_IDLE 0x0105
+#define WIMAX_EXIT_IDLE 0x0106
+#define WIMAX_MODE_CHANGE 0x8108
+#define WIMAX_HANDOVER 0x8109 /* obsolete */
+#define WIMAX_SCAN 0x010d
+#define WIMAX_SCAN_COMPLETE 0x810e
+#define WIMAX_SCAN_RESULT 0x810f
+#define WIMAX_CONNECT 0x0110
+#define WIMAX_CONNECT_START 0x8111
+#define WIMAX_CONNECT_COMPLETE 0x8112
+#define WIMAX_ASSOC_START 0x8113
+#define WIMAX_ASSOC_COMPLETE 0x8114
+#define WIMAX_DISCONN_IND 0x8115
+#define WIMAX_ENTRY_IND 0x8116
+#define WIMAX_HO_START 0x8117
+#define WIMAX_HO_COMPLETE 0x8118
+#define WIMAX_RADIO_STATE_IND 0x8119
+#define WIMAX_IP_RENEW_IND 0x811a
+#define WIMAX_DISCOVER_NSP 0x011d
+#define WIMAX_DISCOVER_NSP_RESULT 0x811e
+#define WIMAX_SDU_TX_FLOW 0x8125
+
+/* Category 2 */
+#define WIMAX_TX_EAP 0x0200
+#define WIMAX_RX_EAP 0x8201
+#define WIMAX_TX_SDU 0x0202
+#define WIMAX_RX_SDU 0x8203
+#define WIMAX_RX_SDU_AGGR 0x8204
+#define WIMAX_TX_SDU_AGGR 0x0205
+
+/* Category 3 */
+#define WIMAX_DM_CMD 0x030a
+#define WIMAX_DM_RSP 0x830b
+
+#define WIMAX_CLI_CMD 0x030c
+#define WIMAX_CLI_RSP 0x830d
+
+#define WIMAX_DL_IMAGE 0x0310
+#define WIMAX_DL_IMAGE_STATUS 0x8311
+#define WIMAX_UL_IMAGE 0x0312
+#define WIMAX_UL_IMAGE_RESULT 0x8313
+#define WIMAX_UL_IMAGE_STATUS 0x0314
+#define WIMAX_EVT_MODEM_REPORT 0x8325
+
+/* Category 0xF */
+#define WIMAX_FSM_UPDATE 0x8F01
+#define WIMAX_IF_UPDOWN 0x8F02
+#define WIMAX_IF_UP 1
+#define WIMAX_IF_DOWN 2
+
+/* WIMAX mode */
+#define W_NULL 0
+#define W_STANDBY 1
+#define W_OOZ 2
+#define W_AWAKE 3
+#define W_IDLE 4
+#define W_SLEEP 5
+#define W_WAIT 6
+
+#define W_NET_ENTRY_RNG 0x80
+#define W_NET_ENTRY_SBC 0x81
+#define W_NET_ENTRY_PKM 0x82
+#define W_NET_ENTRY_REG 0x83
+#define W_NET_ENTRY_DSX 0x84
+
+#define W_NET_ENTRY_RNG_FAIL 0x1100100
+#define W_NET_ENTRY_SBC_FAIL 0x1100200
+#define W_NET_ENTRY_PKM_FAIL 0x1102000
+#define W_NET_ENTRY_REG_FAIL 0x1103000
+#define W_NET_ENTRY_DSX_FAIL 0x1104000
+
+/* Scan Type */
+#define W_SCAN_ALL_CHANNEL 0
+#define W_SCAN_ALL_SUBSCRIPTION 1
+#define W_SCAN_SPECIFIED_SUBSCRIPTION 2
+
+/* TLV
+ *
+ * [31:31] indicates the type is composite.
+ * [30:16] is the length of the type. 0 length means length is variable.
+ * [15:0] is the actual type.
+ */
+#define TLV_L(x) (((x) >> 16) & 0xff)
+#define TLV_T(x) ((x) & 0xff)
+#define TLV_COMPOSITE(x) ((x) >> 31)
+
+/* GENERAL */
+#define T_MAC_ADDRESS (0x00 | (6 << 16))
+#define T_BSID (0x01 | (6 << 16))
+#define T_MSK (0x02 | (64 << 16))
+#define T_RSSI_THRSHLD (0x03 | (1 << 16))
+#define T_FREQUENCY (0x04 | (4 << 16))
+#define T_CONN_CS_TYPE (0x05 | (1 << 16))
+#define T_HOST_IP_VER (0x06 | (1 << 16))
+#define T_STBY_SCAN_INTERVAL (0x07 | (4 << 16))
+#define T_OOZ_SCAN_INTERVAL (0x08 | (4 << 16))
+#define T_IMEI (0x09 | (8 << 16))
+#define T_PID (0x0a | (12 << 16))
+#define T_CAPABILITY (0x1a | (4 << 16))
+#define T_RELEASE_NUMBER (0x1b | (4 << 16))
+#define T_DRIVER_REVISION (0x1c | (4 << 16))
+#define T_FW_REVISION (0x1d | (4 << 16))
+#define T_MAC_HW_REVISION (0x1e | (4 << 16))
+#define T_PHY_HW_REVISION (0x1f | (4 << 16))
+
+/* HANDOVER */
+#define T_SCAN_INTERVAL (0x20 | (1 << 16))
+#define T_RSC_RETAIN_TIME (0x2f | (2 << 16))
+
+/* SLEEP */
+#define T_TYPE1_ISW (0x40 | (1 << 16))
+#define T_SLP_START_TO (0x4a | (2 << 16))
+
+/* IDLE */
+#define T_IDLE_MODE_TO (0x50 | (2 << 16))
+#define T_IDLE_START_TO (0x54 | (2 << 16))
+
+/* MONITOR */
+#define T_RSSI (0x60 | (1 << 16))
+#define T_CINR (0x61 | (1 << 16))
+#define T_TX_POWER (0x6a | (1 << 16))
+#define T_CUR_FREQ (0x7f | (4 << 16))
+
+
+/* WIMAX */
+#define T_MAX_SUBSCRIPTION (0xa1 | (1 << 16))
+#define T_MAX_SF (0xa2 | (1 << 16))
+#define T_PHY_TYPE (0xa3 | (1 << 16))
+#define T_PKM (0xa4 | (1 << 16))
+#define T_AUTH_POLICY (0xa5 | (1 << 16))
+#define T_CS_TYPE (0xa6 | (2 << 16))
+#define T_VENDOR_NAME (0xa7 | (0 << 16))
+#define T_MOD_NAME (0xa8 | (0 << 16))
+#define T_PACKET_FILTER (0xa9 | (1 << 16))
+#define T_NSP_CHANGE_COUNT (0xaa | (4 << 16))
+#define T_RADIO_STATE (0xab | (1 << 16))
+#define T_URI_CONTACT_TYPE (0xac | (1 << 16))
+#define T_URI_TEXT (0xad | (0 << 16))
+#define T_URI (0xae | (0 << 16))
+#define T_ENABLE_AUTH (0xaf | (1 << 16))
+#define T_TIMEOUT (0xb0 | (2 << 16))
+#define T_RUN_MODE (0xb1 | (1 << 16))
+#define T_OMADMT_VER (0xb2 | (4 << 16))
+/* This is measured in seconds from 00:00:00 GMT January 1, 1970. */
+#define T_RTC_TIME (0xb3 | (4 << 16))
+#define T_CERT_STATUS (0xb4 | (4 << 16))
+#define T_CERT_MASK (0xb5 | (4 << 16))
+#define T_EMSK (0xb6 | (64 << 16))
+
+/* Subscription TLV */
+#define T_SUBSCRIPTION_LIST (0xd1 | (0 << 16) | (1 << 31))
+#define T_H_NSPID (0xd2 | (3 << 16))
+#define T_NSP_NAME (0xd3 | (0 << 16))
+#define T_SUBSCRIPTION_NAME (0xd4 | (0 << 16))
+#define T_SUBSCRIPTION_FLAG (0xd5 | (2 << 16))
+#define T_V_NSPID (0xd6 | (3 << 16))
+#define T_NAP_ID (0xd7 | (3 << 16))
+#define T_PREAMBLES (0xd8 | (15 << 16))
+#define T_BW (0xd9 | (4 << 16))
+#define T_FFTSIZE (0xda | (4 << 16))
+#define T_DUPLEX_MODE (0xdb | (4 << 16))
+
+/* T_CAPABILITY */
+#define T_CAPABILITY_MULTI_CS (1 << 0)
+#define T_CAPABILITY_WIMAX (1 << 1)
+#define T_CAPABILITY_QOS (1 << 2)
+#define T_CAPABILITY_AGGREGATION (1 << 3)
+
+struct hci_s {
+ __be16 cmd_evt;
+ __be16 length;
+ u8 data[0];
+} __packed;
+
+#endif /* __GDM72XX_HCI_H__ */
diff --git a/drivers/staging/gdm72xx/netlink_k.c b/drivers/staging/gdm72xx/netlink_k.c
new file mode 100644
index 000000000..9d78bfcdb
--- /dev/null
+++ b/drivers/staging/gdm72xx/netlink_k.c
@@ -0,0 +1,156 @@
+/*
+ * Copyright (c) 2012 GCT Semiconductor, Inc. All rights reserved.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/module.h>
+#include <linux/etherdevice.h>
+#include <net/netlink.h>
+#include <asm/byteorder.h>
+#include <net/sock.h>
+#include "netlink_k.h"
+
+#if !defined(NLMSG_HDRLEN)
+#define NLMSG_HDRLEN ((int) NLMSG_ALIGN(sizeof(struct nlmsghdr)))
+#endif
+
+#define ND_MAX_GROUP 30
+#define ND_IFINDEX_LEN sizeof(int)
+#define ND_NLMSG_SPACE(len) (nlmsg_total_size(len) + ND_IFINDEX_LEN)
+#define ND_NLMSG_DATA(nlh) \
+ ((void *)((char *)nlmsg_data(nlh) + ND_IFINDEX_LEN))
+#define ND_NLMSG_S_LEN(len) (len+ND_IFINDEX_LEN)
+#define ND_NLMSG_R_LEN(nlh) (nlh->nlmsg_len-ND_IFINDEX_LEN)
+#define ND_NLMSG_IFIDX(nlh) nlmsg_data(nlh)
+#define ND_MAX_MSG_LEN 8096
+
+#if defined(DEFINE_MUTEX)
+static DEFINE_MUTEX(netlink_mutex);
+#else
+static struct semaphore netlink_mutex;
+#define mutex_lock(x) down(x)
+#define mutex_unlock(x) up(x)
+#endif
+
+static void (*rcv_cb)(struct net_device *dev, u16 type, void *msg, int len);
+
+static void netlink_rcv_cb(struct sk_buff *skb)
+{
+ struct nlmsghdr *nlh;
+ struct net_device *dev;
+ u32 mlen;
+ void *msg;
+ int ifindex;
+
+ if (skb->len >= NLMSG_HDRLEN) {
+ nlh = (struct nlmsghdr *)skb->data;
+
+ if (skb->len < nlh->nlmsg_len ||
+ nlh->nlmsg_len > ND_MAX_MSG_LEN) {
+ netdev_err(skb->dev, "Invalid length (%d,%d)\n",
+ skb->len, nlh->nlmsg_len);
+ return;
+ }
+
+ memcpy(&ifindex, ND_NLMSG_IFIDX(nlh), ND_IFINDEX_LEN);
+ msg = ND_NLMSG_DATA(nlh);
+ mlen = ND_NLMSG_R_LEN(nlh);
+
+ if (rcv_cb) {
+ dev = dev_get_by_index(&init_net, ifindex);
+ if (dev) {
+ rcv_cb(dev, nlh->nlmsg_type, msg, mlen);
+ dev_put(dev);
+ } else
+ netdev_err(skb->dev,
+ "dev_get_by_index(%d) is not found.\n",
+ ifindex);
+ } else {
+ netdev_err(skb->dev, "Unregistered Callback\n");
+ }
+ }
+}
+
+static void netlink_rcv(struct sk_buff *skb)
+{
+ mutex_lock(&netlink_mutex);
+ netlink_rcv_cb(skb);
+ mutex_unlock(&netlink_mutex);
+}
+
+struct sock *netlink_init(int unit, void (*cb)(struct net_device *dev, u16 type,
+ void *msg, int len))
+{
+ struct sock *sock;
+ struct netlink_kernel_cfg cfg = {
+ .input = netlink_rcv,
+ };
+
+#if !defined(DEFINE_MUTEX)
+ init_MUTEX(&netlink_mutex);
+#endif
+
+ sock = netlink_kernel_create(&init_net, unit, &cfg);
+
+ if (sock)
+ rcv_cb = cb;
+
+ return sock;
+}
+
+void netlink_exit(struct sock *sock)
+{
+ netlink_kernel_release(sock);
+}
+
+int netlink_send(struct sock *sock, int group, u16 type, void *msg, int len)
+{
+ static u32 seq;
+ struct sk_buff *skb = NULL;
+ struct nlmsghdr *nlh;
+ int ret = 0;
+
+ if (group > ND_MAX_GROUP) {
+ pr_err("Group %d is invalied.\n", group);
+ pr_err("Valid group is 0 ~ %d.\n", ND_MAX_GROUP);
+ return -EINVAL;
+ }
+
+ skb = nlmsg_new(len, GFP_ATOMIC);
+ if (!skb) {
+ pr_err("netlink_broadcast ret=%d\n", ret);
+ return -ENOMEM;
+ }
+
+ seq++;
+ nlh = nlmsg_put(skb, 0, seq, type, len, 0);
+ if (!nlh) {
+ kfree_skb(skb);
+ return -EMSGSIZE;
+ }
+ memcpy(nlmsg_data(nlh), msg, len);
+
+ NETLINK_CB(skb).portid = 0;
+ NETLINK_CB(skb).dst_group = 0;
+
+ ret = netlink_broadcast(sock, skb, 0, group+1, GFP_ATOMIC);
+
+ if (!ret)
+ return len;
+ if (ret != -ESRCH) {
+ pr_err("netlink_broadcast g=%d, t=%d, l=%d, r=%d\n",
+ group, type, len, ret);
+ }
+ ret = 0;
+ return ret;
+}
diff --git a/drivers/staging/gdm72xx/netlink_k.h b/drivers/staging/gdm72xx/netlink_k.h
new file mode 100644
index 000000000..1fe7198d5
--- /dev/null
+++ b/drivers/staging/gdm72xx/netlink_k.h
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2012 GCT Semiconductor, Inc. All rights reserved.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ */
+
+#ifndef __GDM72XX_NETLINK_K_H__
+#define __GDM72XX_NETLINK_K_H__
+
+#include <linux/netdevice.h>
+#include <net/sock.h>
+
+struct sock *netlink_init(int unit, void (*cb)(struct net_device *dev, u16 type,
+ void *msg, int len));
+void netlink_exit(struct sock *sock);
+int netlink_send(struct sock *sock, int group, u16 type, void *msg, int len);
+
+#endif /* __GDM72XX_NETLINK_K_H__ */
diff --git a/drivers/staging/gdm72xx/sdio_boot.c b/drivers/staging/gdm72xx/sdio_boot.c
new file mode 100644
index 000000000..c7082657a
--- /dev/null
+++ b/drivers/staging/gdm72xx/sdio_boot.c
@@ -0,0 +1,158 @@
+/*
+ * Copyright (c) 2012 GCT Semiconductor, Inc. All rights reserved.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/uaccess.h>
+#include <linux/fs.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+
+#include <linux/mmc/core.h>
+#include <linux/mmc/card.h>
+#include <linux/mmc/sdio_func.h>
+
+#include <linux/firmware.h>
+
+#include "gdm_sdio.h"
+#include "sdio_boot.h"
+
+#define TYPE_A_HEADER_SIZE 4
+#define TYPE_A_LOOKAHEAD_SIZE 16
+#define YMEM0_SIZE 0x8000 /* 32kbytes */
+#define DOWNLOAD_SIZE (YMEM0_SIZE - TYPE_A_HEADER_SIZE)
+
+#define FW_DIR "gdm72xx/"
+#define FW_KRN "/*(DEBLOBBED)*/"
+#define FW_RFS "/*(DEBLOBBED)*/"
+
+static u8 *tx_buf;
+
+static int ack_ready(struct sdio_func *func)
+{
+ unsigned long wait = jiffies + HZ;
+ u8 val;
+ int ret;
+
+ while (time_before(jiffies, wait)) {
+ val = sdio_readb(func, 0x13, &ret);
+ if (val & 0x01)
+ return 1;
+ schedule();
+ }
+
+ return 0;
+}
+
+static int download_image(struct sdio_func *func, const char *img_name)
+{
+ int ret = 0, len, pno;
+ u8 *buf = tx_buf;
+ loff_t pos = 0;
+ int img_len;
+ const struct firmware *firm;
+
+ ret = reject_firmware(&firm, img_name, &func->dev);
+ if (ret < 0) {
+ dev_err(&func->dev,
+ "requesting firmware %s failed with error %d\n",
+ img_name, ret);
+ return ret;
+ }
+
+ buf = kmalloc(DOWNLOAD_SIZE + TYPE_A_HEADER_SIZE, GFP_KERNEL);
+ if (buf == NULL)
+ return -ENOMEM;
+
+ img_len = firm->size;
+
+ if (img_len <= 0) {
+ ret = -1;
+ goto out;
+ }
+
+ pno = 0;
+ while (img_len > 0) {
+ if (img_len > DOWNLOAD_SIZE) {
+ len = DOWNLOAD_SIZE;
+ buf[3] = 0;
+ } else {
+ len = img_len; /* the last packet */
+ buf[3] = 2;
+ }
+
+ buf[0] = len & 0xff;
+ buf[1] = (len >> 8) & 0xff;
+ buf[2] = (len >> 16) & 0xff;
+
+ memcpy(buf+TYPE_A_HEADER_SIZE, firm->data + pos, len);
+ ret = sdio_memcpy_toio(func, 0, buf, len + TYPE_A_HEADER_SIZE);
+ if (ret < 0) {
+ dev_err(&func->dev,
+ "send image error: packet number = %d ret = %d\n",
+ pno, ret);
+ goto out;
+ }
+
+ if (buf[3] == 2) /* The last packet */
+ break;
+ if (!ack_ready(func)) {
+ ret = -EIO;
+ dev_err(&func->dev, "Ack is not ready.\n");
+ goto out;
+ }
+ ret = sdio_memcpy_fromio(func, buf, 0, TYPE_A_LOOKAHEAD_SIZE);
+ if (ret < 0) {
+ dev_err(&func->dev,
+ "receive ack error: packet number = %d ret = %d\n",
+ pno, ret);
+ goto out;
+ }
+ sdio_writeb(func, 0x01, 0x13, &ret);
+ sdio_writeb(func, 0x00, 0x10, &ret); /* PCRRT */
+
+ img_len -= DOWNLOAD_SIZE;
+ pos += DOWNLOAD_SIZE;
+ pno++;
+ }
+
+out:
+ kfree(buf);
+ return ret;
+}
+
+int sdio_boot(struct sdio_func *func)
+{
+ int ret;
+ const char *krn_name = FW_DIR FW_KRN;
+ const char *rfs_name = FW_DIR FW_RFS;
+
+ tx_buf = kmalloc(YMEM0_SIZE, GFP_KERNEL);
+ if (tx_buf == NULL)
+ return -ENOMEM;
+
+ ret = download_image(func, krn_name);
+ if (ret)
+ goto restore_fs;
+ dev_info(&func->dev, "GCT: Kernel download success.\n");
+
+ ret = download_image(func, rfs_name);
+ if (ret)
+ goto restore_fs;
+ dev_info(&func->dev, "GCT: Filesystem download success.\n");
+
+restore_fs:
+ kfree(tx_buf);
+ return ret;
+}
diff --git a/drivers/staging/gdm72xx/sdio_boot.h b/drivers/staging/gdm72xx/sdio_boot.h
new file mode 100644
index 000000000..e0800c6fe
--- /dev/null
+++ b/drivers/staging/gdm72xx/sdio_boot.h
@@ -0,0 +1,21 @@
+/*
+ * Copyright (c) 2012 GCT Semiconductor, Inc. All rights reserved.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ */
+
+#ifndef __GDM72XX_SDIO_BOOT_H__
+#define __GDM72XX_SDIO_BOOT_H__
+
+struct sdio_func;
+
+int sdio_boot(struct sdio_func *func);
+
+#endif /* __GDM72XX_SDIO_BOOT_H__ */
diff --git a/drivers/staging/gdm72xx/usb_boot.c b/drivers/staging/gdm72xx/usb_boot.c
new file mode 100644
index 000000000..89a16dbc0
--- /dev/null
+++ b/drivers/staging/gdm72xx/usb_boot.c
@@ -0,0 +1,359 @@
+/*
+ * Copyright (c) 2012 GCT Semiconductor, Inc. All rights reserved.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ */
+
+#include <linux/uaccess.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/usb.h>
+#include <linux/unistd.h>
+#include <linux/slab.h>
+#include <linux/firmware.h>
+
+#include <asm/byteorder.h>
+#include "gdm_usb.h"
+#include "usb_boot.h"
+
+#define DN_KERNEL_MAGIC_NUMBER 0x10760001
+#define DN_ROOTFS_MAGIC_NUMBER 0x10760002
+
+#define DOWNLOAD_SIZE 1024
+
+#define MAX_IMG_CNT 16
+#define FW_DIR "gdm72xx/"
+#define FW_UIMG "/*(DEBLOBBED)*/"
+#define FW_KERN "zImage"
+#define FW_FS "ramdisk.jffs2"
+
+struct dn_header {
+ __be32 magic_num;
+ __be32 file_size;
+};
+
+struct img_header {
+ u32 magic_code;
+ u32 count;
+ u32 len;
+ u32 offset[MAX_IMG_CNT];
+ char hostname[32];
+ char date[32];
+};
+
+struct fw_info {
+ u32 id;
+ u32 len;
+ u32 kernel_len;
+ u32 rootfs_len;
+ u32 kernel_offset;
+ u32 rootfs_offset;
+ u32 fw_ver;
+ u32 mac_ver;
+ char hostname[32];
+ char userid[16];
+ char date[32];
+ char user_desc[128];
+};
+
+static void array_le32_to_cpu(u32 *arr, int num)
+{
+ int i;
+
+ for (i = 0; i < num; i++, arr++)
+ le32_to_cpus(arr);
+}
+
+static u8 *tx_buf;
+
+static int gdm_wibro_send(struct usb_device *usbdev, void *data, int len)
+{
+ int ret;
+ int actual;
+
+ ret = usb_bulk_msg(usbdev, usb_sndbulkpipe(usbdev, 1), data, len,
+ &actual, 1000);
+
+ if (ret < 0) {
+ dev_err(&usbdev->dev, "Error : usb_bulk_msg ( result = %d )\n",
+ ret);
+ return ret;
+ }
+ return 0;
+}
+
+static int gdm_wibro_recv(struct usb_device *usbdev, void *data, int len)
+{
+ int ret;
+ int actual;
+
+ ret = usb_bulk_msg(usbdev, usb_rcvbulkpipe(usbdev, 2), data, len,
+ &actual, 5000);
+
+ if (ret < 0) {
+ dev_err(&usbdev->dev,
+ "Error : usb_bulk_msg(recv) ( result = %d )\n", ret);
+ return ret;
+ }
+ return 0;
+}
+
+static int download_image(struct usb_device *usbdev,
+ const struct firmware *firm,
+ loff_t pos, u32 img_len, u32 magic_num)
+{
+ struct dn_header h;
+ int ret = 0;
+ u32 size;
+
+ size = ALIGN(img_len, DOWNLOAD_SIZE);
+ h.magic_num = cpu_to_be32(magic_num);
+ h.file_size = cpu_to_be32(size);
+
+ ret = gdm_wibro_send(usbdev, &h, sizeof(h));
+ if (ret < 0)
+ return ret;
+
+ while (img_len > 0) {
+ if (img_len > DOWNLOAD_SIZE)
+ size = DOWNLOAD_SIZE;
+ else
+ size = img_len; /* the last chunk of data */
+
+ memcpy(tx_buf, firm->data + pos, size);
+ ret = gdm_wibro_send(usbdev, tx_buf, size);
+
+ if (ret < 0)
+ return ret;
+
+ img_len -= size;
+ pos += size;
+ }
+
+ return ret;
+}
+
+int usb_boot(struct usb_device *usbdev, u16 pid)
+{
+ int i, ret = 0;
+ struct img_header hdr;
+ struct fw_info fw_info;
+ loff_t pos = 0;
+ char *img_name = FW_DIR FW_UIMG;
+ const struct firmware *firm;
+
+ ret = reject_firmware(&firm, img_name, &usbdev->dev);
+ if (ret < 0) {
+ dev_err(&usbdev->dev,
+ "requesting firmware %s failed with error %d\n",
+ img_name, ret);
+ return ret;
+ }
+
+ tx_buf = kmalloc(DOWNLOAD_SIZE, GFP_KERNEL);
+ if (tx_buf == NULL)
+ return -ENOMEM;
+
+ if (firm->size < sizeof(hdr)) {
+ dev_err(&usbdev->dev, "Cannot read the image info.\n");
+ ret = -EIO;
+ goto out;
+ }
+ memcpy(&hdr, firm->data, sizeof(hdr));
+
+ array_le32_to_cpu((u32 *)&hdr, 19);
+
+ if (hdr.count > MAX_IMG_CNT) {
+ dev_err(&usbdev->dev, "Too many images. %d\n", hdr.count);
+ ret = -EINVAL;
+ goto out;
+ }
+
+ for (i = 0; i < hdr.count; i++) {
+ if (hdr.offset[i] > hdr.len) {
+ dev_err(&usbdev->dev,
+ "Invalid offset. Entry = %d Offset = 0x%08x Image length = 0x%08x\n",
+ i, hdr.offset[i], hdr.len);
+ ret = -EINVAL;
+ goto out;
+ }
+
+ pos = hdr.offset[i];
+ if (firm->size < sizeof(fw_info) + pos) {
+ dev_err(&usbdev->dev, "Cannot read the FW info.\n");
+ ret = -EIO;
+ goto out;
+ }
+ memcpy(&fw_info, firm->data + pos, sizeof(fw_info));
+
+ array_le32_to_cpu((u32 *)&fw_info, 8);
+
+ if ((fw_info.id & 0xffff) != pid)
+ continue;
+
+ pos = hdr.offset[i] + fw_info.kernel_offset;
+ if (firm->size < fw_info.kernel_len + pos) {
+ dev_err(&usbdev->dev, "Kernel FW is too small.\n");
+ goto out;
+ }
+
+ ret = download_image(usbdev, firm, pos, fw_info.kernel_len,
+ DN_KERNEL_MAGIC_NUMBER);
+ if (ret < 0)
+ goto out;
+ dev_info(&usbdev->dev, "GCT: Kernel download success.\n");
+
+ pos = hdr.offset[i] + fw_info.rootfs_offset;
+ if (firm->size < fw_info.rootfs_len + pos) {
+ dev_err(&usbdev->dev, "Filesystem FW is too small.\n");
+ goto out;
+ }
+ ret = download_image(usbdev, firm, pos, fw_info.rootfs_len,
+ DN_ROOTFS_MAGIC_NUMBER);
+ if (ret < 0)
+ goto out;
+ dev_info(&usbdev->dev, "GCT: Filesystem download success.\n");
+
+ break;
+ }
+
+ if (i == hdr.count) {
+ dev_err(&usbdev->dev, "Firmware for gsk%x is not installed.\n",
+ pid);
+ ret = -EINVAL;
+ }
+out:
+ release_firmware(firm);
+ kfree(tx_buf);
+ return ret;
+}
+
+/*#define GDM7205_PADDING 256 */
+#define DOWNLOAD_CHUCK 2048
+#define KERNEL_TYPE_STRING "linux"
+#define FS_TYPE_STRING "rootfs"
+
+static int em_wait_ack(struct usb_device *usbdev, int send_zlp)
+{
+ int ack;
+ int ret = -1;
+
+ if (send_zlp) {
+ /*Send ZLP*/
+ ret = gdm_wibro_send(usbdev, NULL, 0);
+ if (ret < 0)
+ goto out;
+ }
+
+ /*Wait for ACK*/
+ ret = gdm_wibro_recv(usbdev, &ack, sizeof(ack));
+ if (ret < 0)
+ goto out;
+out:
+ return ret;
+}
+
+static int em_download_image(struct usb_device *usbdev, const char *img_name,
+ char *type_string)
+{
+ char *buf = NULL;
+ loff_t pos = 0;
+ int ret = 0;
+ int len;
+ int img_len;
+ const struct firmware *firm;
+ #if defined(GDM7205_PADDING)
+ const int pad_size = GDM7205_PADDING;
+ #else
+ const int pad_size = 0;
+ #endif
+
+ ret = reject_firmware(&firm, img_name, &usbdev->dev);
+ if (ret < 0) {
+ dev_err(&usbdev->dev,
+ "requesting firmware %s failed with error %d\n",
+ img_name, ret);
+ return ret;
+ }
+
+ buf = kmalloc(DOWNLOAD_CHUCK + pad_size, GFP_KERNEL);
+ if (buf == NULL)
+ return -ENOMEM;
+
+ strcpy(buf+pad_size, type_string);
+ ret = gdm_wibro_send(usbdev, buf, strlen(type_string)+pad_size);
+ if (ret < 0)
+ goto out;
+
+ img_len = firm->size;
+
+ if (img_len <= 0) {
+ ret = -1;
+ goto out;
+ }
+
+ while (img_len > 0) {
+ if (img_len > DOWNLOAD_CHUCK)
+ len = DOWNLOAD_CHUCK;
+ else
+ len = img_len; /* the last chunk of data */
+
+ memcpy(buf+pad_size, firm->data + pos, len);
+ ret = gdm_wibro_send(usbdev, buf, len+pad_size);
+
+ if (ret < 0)
+ goto out;
+
+ img_len -= DOWNLOAD_CHUCK;
+ pos += DOWNLOAD_CHUCK;
+
+ ret = em_wait_ack(usbdev, ((len+pad_size) % 512 == 0));
+ if (ret < 0)
+ goto out;
+ }
+
+ ret = em_wait_ack(usbdev, 1);
+ if (ret < 0)
+ goto out;
+
+out:
+ release_firmware(firm);
+ kfree(buf);
+
+ return ret;
+}
+
+static int em_fw_reset(struct usb_device *usbdev)
+{
+ /*Send ZLP*/
+ return gdm_wibro_send(usbdev, NULL, 0);
+}
+
+int usb_emergency(struct usb_device *usbdev)
+{
+ int ret;
+ const char *kern_name = FW_DIR FW_KERN;
+ const char *fs_name = FW_DIR FW_FS;
+
+ ret = em_download_image(usbdev, kern_name, KERNEL_TYPE_STRING);
+ if (ret < 0)
+ return ret;
+ dev_err(&usbdev->dev, "GCT Emergency: Kernel download success.\n");
+
+ ret = em_download_image(usbdev, fs_name, FS_TYPE_STRING);
+ if (ret < 0)
+ return ret;
+ dev_info(&usbdev->dev, "GCT Emergency: Filesystem download success.\n");
+
+ ret = em_fw_reset(usbdev);
+
+ return ret;
+}
diff --git a/drivers/staging/gdm72xx/usb_boot.h b/drivers/staging/gdm72xx/usb_boot.h
new file mode 100644
index 000000000..5bf719037
--- /dev/null
+++ b/drivers/staging/gdm72xx/usb_boot.h
@@ -0,0 +1,22 @@
+/*
+ * Copyright (c) 2012 GCT Semiconductor, Inc. All rights reserved.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ */
+
+#ifndef __GDM72XX_USB_BOOT_H__
+#define __GDM72XX_USB_BOOT_H__
+
+struct usb_device;
+
+int usb_boot(struct usb_device *usbdev, u16 pid);
+int usb_emergency(struct usb_device *usbdev);
+
+#endif /* __GDM72XX_USB_BOOT_H__ */
diff --git a/drivers/staging/gdm72xx/usb_ids.h b/drivers/staging/gdm72xx/usb_ids.h
new file mode 100644
index 000000000..8ce544de7
--- /dev/null
+++ b/drivers/staging/gdm72xx/usb_ids.h
@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) 2012 GCT Semiconductor, Inc. All rights reserved.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ */
+
+#ifndef __GDM72XX_USB_IDS_H__
+#define __GDM72XX_USB_IDS_H__
+
+/*You can replace vendor-ID as yours.*/
+#define GCT_VID 0x1076
+
+/*You can replace product-ID as yours.*/
+#define GCT_PID1 0x7e00
+#define GCT_PID2 0x7f00
+
+#define USB_DEVICE_ID_MATCH_DEVICE_INTERFACE \
+ (USB_DEVICE_ID_MATCH_DEVICE | USB_DEVICE_ID_MATCH_INT_CLASS)
+
+#define USB_DEVICE_INTF(vend, prod, intf) \
+ .match_flags = USB_DEVICE_ID_MATCH_DEVICE_INTERFACE, \
+ .idVendor = (vend), .idProduct = (prod), .bInterfaceClass = (intf)
+
+#define EMERGENCY_PID 0x720f
+#define BL_PID_MASK 0xffc0
+
+#define USB_DEVICE_BOOTLOADER(vid, pid) \
+ {USB_DEVICE((vid), ((pid)&BL_PID_MASK)|B_DOWNLOAD)}, \
+ {USB_DEVICE((vid), ((pid)&BL_PID_MASK)|B_DOWNLOAD|B_DIFF_DL_DRV)}
+
+#define USB_DEVICE_CDC_DATA(vid, pid) \
+ {USB_DEVICE_INTF((vid), (pid), USB_CLASS_CDC_DATA)}
+
+static const struct usb_device_id id_table[] = {
+ USB_DEVICE_BOOTLOADER(GCT_VID, GCT_PID1),
+ USB_DEVICE_CDC_DATA(GCT_VID, GCT_PID1),
+ USB_DEVICE_CDC_DATA(GCT_VID, GCT_PID1+0x1),
+ USB_DEVICE_CDC_DATA(GCT_VID, GCT_PID1+0x2),
+ USB_DEVICE_CDC_DATA(GCT_VID, GCT_PID1+0x3),
+ USB_DEVICE_CDC_DATA(GCT_VID, GCT_PID1+0x4),
+ USB_DEVICE_CDC_DATA(GCT_VID, GCT_PID1+0x5),
+ USB_DEVICE_CDC_DATA(GCT_VID, GCT_PID1+0x6),
+ USB_DEVICE_CDC_DATA(GCT_VID, GCT_PID1+0x7),
+ USB_DEVICE_CDC_DATA(GCT_VID, GCT_PID1+0x8),
+ USB_DEVICE_CDC_DATA(GCT_VID, GCT_PID1+0x9),
+ USB_DEVICE_CDC_DATA(GCT_VID, GCT_PID1+0xa),
+ USB_DEVICE_CDC_DATA(GCT_VID, GCT_PID1+0xb),
+ USB_DEVICE_CDC_DATA(GCT_VID, GCT_PID1+0xc),
+ USB_DEVICE_CDC_DATA(GCT_VID, GCT_PID1+0xd),
+ USB_DEVICE_CDC_DATA(GCT_VID, GCT_PID1+0xe),
+ USB_DEVICE_CDC_DATA(GCT_VID, GCT_PID1+0xf),
+
+ USB_DEVICE_BOOTLOADER(GCT_VID, GCT_PID2),
+ USB_DEVICE_CDC_DATA(GCT_VID, GCT_PID2),
+ USB_DEVICE_CDC_DATA(GCT_VID, GCT_PID2+0x1),
+ USB_DEVICE_CDC_DATA(GCT_VID, GCT_PID2+0x2),
+ USB_DEVICE_CDC_DATA(GCT_VID, GCT_PID2+0x3),
+ USB_DEVICE_CDC_DATA(GCT_VID, GCT_PID2+0x4),
+ USB_DEVICE_CDC_DATA(GCT_VID, GCT_PID2+0x5),
+ USB_DEVICE_CDC_DATA(GCT_VID, GCT_PID2+0x6),
+ USB_DEVICE_CDC_DATA(GCT_VID, GCT_PID2+0x7),
+ USB_DEVICE_CDC_DATA(GCT_VID, GCT_PID2+0x8),
+ USB_DEVICE_CDC_DATA(GCT_VID, GCT_PID2+0x9),
+ USB_DEVICE_CDC_DATA(GCT_VID, GCT_PID2+0xa),
+ USB_DEVICE_CDC_DATA(GCT_VID, GCT_PID2+0xb),
+ USB_DEVICE_CDC_DATA(GCT_VID, GCT_PID2+0xc),
+ USB_DEVICE_CDC_DATA(GCT_VID, GCT_PID2+0xd),
+ USB_DEVICE_CDC_DATA(GCT_VID, GCT_PID2+0xe),
+ USB_DEVICE_CDC_DATA(GCT_VID, GCT_PID2+0xf),
+
+ {USB_DEVICE(GCT_VID, EMERGENCY_PID)},
+ { }
+};
+
+#endif /* __GDM72XX_USB_IDS_H__ */
diff --git a/drivers/staging/gdm72xx/wm_ioctl.h b/drivers/staging/gdm72xx/wm_ioctl.h
new file mode 100644
index 000000000..ed8f649c0
--- /dev/null
+++ b/drivers/staging/gdm72xx/wm_ioctl.h
@@ -0,0 +1,96 @@
+/*
+ * Copyright (c) 2012 GCT Semiconductor, Inc. All rights reserved.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ */
+
+#ifndef __GDM72XX_WM_IOCTL_H__
+#define __GDM72XX_WM_IOCTL_H__
+
+#if !defined(__KERNEL__)
+#include <net/if.h>
+#endif
+
+#define NETLINK_WIMAX 31
+
+#define SIOCWMIOCTL SIOCDEVPRIVATE
+
+#define SIOCG_DATA 0x8D10
+#define SIOCS_DATA 0x8D11
+
+enum {
+ SIOC_DATA_FSM,
+ SIOC_DATA_NETLIST,
+ SIOC_DATA_CONNNSP,
+ SIOC_DATA_CONNCOMP,
+ SIOC_DATA_PROFILEID,
+
+ SIOC_DATA_END
+};
+
+#define SIOC_DATA_MAX 16
+
+/* FSM */
+enum {
+ M_INIT = 0,
+ M_OPEN_OFF,
+ M_OPEN_ON,
+ M_SCAN,
+ M_CONNECTING,
+ M_CONNECTED,
+ M_FSM_END,
+
+ C_INIT = 0,
+ C_CONNSTART,
+ C_ASSOCSTART,
+ C_RNG,
+ C_SBC,
+ C_AUTH,
+ C_REG,
+ C_DSX,
+ C_ASSOCCOMPLETE,
+ C_CONNCOMPLETE,
+ C_FSM_END,
+
+ D_INIT = 0,
+ D_READY,
+ D_LISTEN,
+ D_IPACQUISITION,
+
+ END_FSM
+};
+
+struct fsm_s {
+ int m_status; /*main status*/
+ int c_status; /*connection status*/
+ int d_status; /*oma-dm status*/
+};
+
+struct data_s {
+ int size;
+ void *buf;
+};
+
+struct wm_req_s {
+ union {
+ char ifrn_name[IFNAMSIZ];
+ } ifr_ifrn;
+ unsigned short cmd;
+ unsigned short data_id;
+ struct data_s data;
+
+/* NOTE: sizeof(struct wm_req_s) must be less than sizeof(struct ifreq). */
+};
+
+#ifndef ifr_name
+#define ifr_name ifr_ifrn.ifrn_name
+#endif
+
+#endif /* __GDM72XX_WM_IOCTL_H__ */
diff --git a/drivers/staging/goldfish/Kconfig b/drivers/staging/goldfish/Kconfig
new file mode 100644
index 000000000..4e0946024
--- /dev/null
+++ b/drivers/staging/goldfish/Kconfig
@@ -0,0 +1,13 @@
+config GOLDFISH_AUDIO
+ tristate "Goldfish AVD Audio Device"
+ depends on GOLDFISH
+ ---help---
+ Emulated audio channel for the Goldfish Android Virtual Device
+
+config MTD_GOLDFISH_NAND
+ tristate "Goldfish NAND device"
+ depends on GOLDFISH
+ depends on MTD
+ help
+ Drives the emulated NAND flash device on the Google Goldfish
+ Android virtual device.
diff --git a/drivers/staging/goldfish/Makefile b/drivers/staging/goldfish/Makefile
new file mode 100644
index 000000000..dec34ad58
--- /dev/null
+++ b/drivers/staging/goldfish/Makefile
@@ -0,0 +1,6 @@
+#
+# Makefile for the Goldfish audio driver
+#
+
+obj-$(CONFIG_GOLDFISH_AUDIO) += goldfish_audio.o
+obj-$(CONFIG_MTD_GOLDFISH_NAND) += goldfish_nand.o
diff --git a/drivers/staging/goldfish/README b/drivers/staging/goldfish/README
new file mode 100644
index 000000000..183af0053
--- /dev/null
+++ b/drivers/staging/goldfish/README
@@ -0,0 +1,11 @@
+Audio
+-----
+- Move to using the ALSA framework not faking it
+- Fix the wrong user page DMA (moving to ALSA may fix that too)
+
+NAND
+----
+- Remove excess checking of parameters in calls
+- Use dma coherent memory not kmalloc/__pa for the memory (this is just
+ a cleanliness issue not a correctness one)
+
diff --git a/drivers/staging/goldfish/goldfish_audio.c b/drivers/staging/goldfish/goldfish_audio.c
new file mode 100644
index 000000000..702ae04df
--- /dev/null
+++ b/drivers/staging/goldfish/goldfish_audio.c
@@ -0,0 +1,355 @@
+/*
+ * drivers/misc/goldfish_audio.c
+ *
+ * Copyright (C) 2007 Google, Inc.
+ * Copyright (C) 2012 Intel, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/miscdevice.h>
+#include <linux/fs.h>
+#include <linux/platform_device.h>
+#include <linux/types.h>
+#include <linux/pci.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/sched.h>
+#include <linux/dma-mapping.h>
+#include <linux/uaccess.h>
+#include <linux/goldfish.h>
+
+MODULE_AUTHOR("Google, Inc.");
+MODULE_DESCRIPTION("Android QEMU Audio Driver");
+MODULE_LICENSE("GPL");
+MODULE_VERSION("1.0");
+
+struct goldfish_audio {
+ char __iomem *reg_base;
+ int irq;
+ /* lock protects access to buffer_status and to device registers */
+ spinlock_t lock;
+ wait_queue_head_t wait;
+
+ char *buffer_virt; /* combined buffer virtual address */
+ unsigned long buffer_phys; /* combined buffer physical address */
+
+ char *write_buffer1; /* write buffer 1 virtual address */
+ char *write_buffer2; /* write buffer 2 virtual address */
+ char *read_buffer; /* read buffer virtual address */
+ int buffer_status;
+ int read_supported; /* true if we have audio input support */
+};
+
+/*
+ * We will allocate two read buffers and two write buffers.
+ * Having two read buffers facilitate stereo -> mono conversion.
+ * Having two write buffers facilitate interleaved IO.
+ */
+#define READ_BUFFER_SIZE 16384
+#define WRITE_BUFFER_SIZE 16384
+#define COMBINED_BUFFER_SIZE ((2 * READ_BUFFER_SIZE) + \
+ (2 * WRITE_BUFFER_SIZE))
+
+#define AUDIO_READ(data, addr) (readl(data->reg_base + addr))
+#define AUDIO_WRITE(data, addr, x) (writel(x, data->reg_base + addr))
+#define AUDIO_WRITE64(data, addr, addr2, x) \
+ (gf_write64((u64)(x), data->reg_base + addr, data->reg_base+addr2))
+
+/*
+ * temporary variable used between goldfish_audio_probe() and
+ * goldfish_audio_open()
+ */
+static struct goldfish_audio *audio_data;
+
+enum {
+ /* audio status register */
+ AUDIO_INT_STATUS = 0x00,
+ /* set this to enable IRQ */
+ AUDIO_INT_ENABLE = 0x04,
+ /* set these to specify buffer addresses */
+ AUDIO_SET_WRITE_BUFFER_1 = 0x08,
+ AUDIO_SET_WRITE_BUFFER_2 = 0x0C,
+ /* set number of bytes in buffer to write */
+ AUDIO_WRITE_BUFFER_1 = 0x10,
+ AUDIO_WRITE_BUFFER_2 = 0x14,
+ AUDIO_SET_WRITE_BUFFER_1_HIGH = 0x28,
+ AUDIO_SET_WRITE_BUFFER_2_HIGH = 0x30,
+
+ /* true if audio input is supported */
+ AUDIO_READ_SUPPORTED = 0x18,
+ /* buffer to use for audio input */
+ AUDIO_SET_READ_BUFFER = 0x1C,
+ AUDIO_SET_READ_BUFFER_HIGH = 0x34,
+
+ /* driver writes number of bytes to read */
+ AUDIO_START_READ = 0x20,
+
+ /* number of bytes available in read buffer */
+ AUDIO_READ_BUFFER_AVAILABLE = 0x24,
+
+ /* AUDIO_INT_STATUS bits */
+
+ /* this bit set when it is safe to write more bytes to the buffer */
+ AUDIO_INT_WRITE_BUFFER_1_EMPTY = 1U << 0,
+ AUDIO_INT_WRITE_BUFFER_2_EMPTY = 1U << 1,
+ AUDIO_INT_READ_BUFFER_FULL = 1U << 2,
+
+ AUDIO_INT_MASK = AUDIO_INT_WRITE_BUFFER_1_EMPTY |
+ AUDIO_INT_WRITE_BUFFER_2_EMPTY |
+ AUDIO_INT_READ_BUFFER_FULL,
+};
+
+static atomic_t open_count = ATOMIC_INIT(0);
+
+static ssize_t goldfish_audio_read(struct file *fp, char __user *buf,
+ size_t count, loff_t *pos)
+{
+ struct goldfish_audio *data = fp->private_data;
+ int length;
+ int result = 0;
+
+ if (!data->read_supported)
+ return -ENODEV;
+
+ while (count > 0) {
+ length = (count > READ_BUFFER_SIZE ? READ_BUFFER_SIZE : count);
+ AUDIO_WRITE(data, AUDIO_START_READ, length);
+
+ wait_event_interruptible(data->wait, data->buffer_status &
+ AUDIO_INT_READ_BUFFER_FULL);
+
+ length = AUDIO_READ(data, AUDIO_READ_BUFFER_AVAILABLE);
+
+ /* copy data to user space */
+ if (copy_to_user(buf, data->read_buffer, length))
+ return -EFAULT;
+
+ result += length;
+ buf += length;
+ count -= length;
+ }
+ return result;
+}
+
+static ssize_t goldfish_audio_write(struct file *fp, const char __user *buf,
+ size_t count, loff_t *pos)
+{
+ struct goldfish_audio *data = fp->private_data;
+ unsigned long irq_flags;
+ ssize_t result = 0;
+ char *kbuf;
+
+ while (count > 0) {
+ ssize_t copy = count;
+
+ if (copy > WRITE_BUFFER_SIZE)
+ copy = WRITE_BUFFER_SIZE;
+ wait_event_interruptible(data->wait, data->buffer_status &
+ (AUDIO_INT_WRITE_BUFFER_1_EMPTY |
+ AUDIO_INT_WRITE_BUFFER_2_EMPTY));
+
+ if ((data->buffer_status & AUDIO_INT_WRITE_BUFFER_1_EMPTY) != 0)
+ kbuf = data->write_buffer1;
+ else
+ kbuf = data->write_buffer2;
+
+ /* copy from user space to the appropriate buffer */
+ if (copy_from_user(kbuf, buf, copy)) {
+ result = -EFAULT;
+ break;
+ }
+
+ spin_lock_irqsave(&data->lock, irq_flags);
+ /*
+ * clear the buffer empty flag, and signal the emulator
+ * to start writing the buffer
+ */
+ if (kbuf == data->write_buffer1) {
+ data->buffer_status &= ~AUDIO_INT_WRITE_BUFFER_1_EMPTY;
+ AUDIO_WRITE(data, AUDIO_WRITE_BUFFER_1, copy);
+ } else {
+ data->buffer_status &= ~AUDIO_INT_WRITE_BUFFER_2_EMPTY;
+ AUDIO_WRITE(data, AUDIO_WRITE_BUFFER_2, copy);
+ }
+ spin_unlock_irqrestore(&data->lock, irq_flags);
+
+ buf += copy;
+ result += copy;
+ count -= copy;
+ }
+ return result;
+}
+
+static int goldfish_audio_open(struct inode *ip, struct file *fp)
+{
+ if (!audio_data)
+ return -ENODEV;
+
+ if (atomic_inc_return(&open_count) == 1) {
+ fp->private_data = audio_data;
+ audio_data->buffer_status = (AUDIO_INT_WRITE_BUFFER_1_EMPTY |
+ AUDIO_INT_WRITE_BUFFER_2_EMPTY);
+ AUDIO_WRITE(audio_data, AUDIO_INT_ENABLE, AUDIO_INT_MASK);
+ return 0;
+ }
+
+ atomic_dec(&open_count);
+ return -EBUSY;
+}
+
+static int goldfish_audio_release(struct inode *ip, struct file *fp)
+{
+ atomic_dec(&open_count);
+ /* FIXME: surely this is wrong for the multi-opened case */
+ AUDIO_WRITE(audio_data, AUDIO_INT_ENABLE, 0);
+ return 0;
+}
+
+static long goldfish_audio_ioctl(struct file *fp, unsigned int cmd,
+ unsigned long arg)
+{
+ /* temporary workaround, until we switch to the ALSA API */
+ if (cmd == 315)
+ return -1;
+
+ return 0;
+}
+
+static irqreturn_t goldfish_audio_interrupt(int irq, void *dev_id)
+{
+ unsigned long irq_flags;
+ struct goldfish_audio *data = dev_id;
+ u32 status;
+
+ spin_lock_irqsave(&data->lock, irq_flags);
+
+ /* read buffer status flags */
+ status = AUDIO_READ(data, AUDIO_INT_STATUS);
+ status &= AUDIO_INT_MASK;
+ /*
+ * if buffers are newly empty, wake up blocked
+ * goldfish_audio_write() call
+ */
+ if (status) {
+ data->buffer_status = status;
+ wake_up(&data->wait);
+ }
+
+ spin_unlock_irqrestore(&data->lock, irq_flags);
+ return status ? IRQ_HANDLED : IRQ_NONE;
+}
+
+/* file operations for /dev/eac */
+static const struct file_operations goldfish_audio_fops = {
+ .owner = THIS_MODULE,
+ .read = goldfish_audio_read,
+ .write = goldfish_audio_write,
+ .open = goldfish_audio_open,
+ .release = goldfish_audio_release,
+ .unlocked_ioctl = goldfish_audio_ioctl,
+};
+
+static struct miscdevice goldfish_audio_device = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "eac",
+ .fops = &goldfish_audio_fops,
+};
+
+static int goldfish_audio_probe(struct platform_device *pdev)
+{
+ int ret;
+ struct resource *r;
+ struct goldfish_audio *data;
+ dma_addr_t buf_addr;
+
+ data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+ spin_lock_init(&data->lock);
+ init_waitqueue_head(&data->wait);
+ platform_set_drvdata(pdev, data);
+
+ r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (r == NULL) {
+ dev_err(&pdev->dev, "platform_get_resource failed\n");
+ return -ENODEV;
+ }
+ data->reg_base = devm_ioremap(&pdev->dev, r->start, PAGE_SIZE);
+ if (data->reg_base == NULL)
+ return -ENOMEM;
+
+ data->irq = platform_get_irq(pdev, 0);
+ if (data->irq < 0) {
+ dev_err(&pdev->dev, "platform_get_irq failed\n");
+ return -ENODEV;
+ }
+ data->buffer_virt = dmam_alloc_coherent(&pdev->dev,
+ COMBINED_BUFFER_SIZE, &buf_addr, GFP_KERNEL);
+ if (data->buffer_virt == NULL) {
+ dev_err(&pdev->dev, "allocate buffer failed\n");
+ return -ENOMEM;
+ }
+ data->buffer_phys = buf_addr;
+ data->write_buffer1 = data->buffer_virt;
+ data->write_buffer2 = data->buffer_virt + WRITE_BUFFER_SIZE;
+ data->read_buffer = data->buffer_virt + 2 * WRITE_BUFFER_SIZE;
+
+ ret = devm_request_irq(&pdev->dev, data->irq, goldfish_audio_interrupt,
+ IRQF_SHARED, pdev->name, data);
+ if (ret) {
+ dev_err(&pdev->dev, "request_irq failed\n");
+ return ret;
+ }
+
+ ret = misc_register(&goldfish_audio_device);
+ if (ret) {
+ dev_err(&pdev->dev,
+ "misc_register returned %d in goldfish_audio_init\n",
+ ret);
+ return ret;
+ }
+
+ AUDIO_WRITE64(data, AUDIO_SET_WRITE_BUFFER_1,
+ AUDIO_SET_WRITE_BUFFER_1_HIGH, buf_addr);
+ buf_addr += WRITE_BUFFER_SIZE;
+
+ AUDIO_WRITE64(data, AUDIO_SET_WRITE_BUFFER_2,
+ AUDIO_SET_WRITE_BUFFER_2_HIGH, buf_addr);
+
+ buf_addr += WRITE_BUFFER_SIZE;
+
+ data->read_supported = AUDIO_READ(data, AUDIO_READ_SUPPORTED);
+ if (data->read_supported)
+ AUDIO_WRITE64(data, AUDIO_SET_READ_BUFFER,
+ AUDIO_SET_READ_BUFFER_HIGH, buf_addr);
+
+ audio_data = data;
+ return 0;
+}
+
+static int goldfish_audio_remove(struct platform_device *pdev)
+{
+ misc_deregister(&goldfish_audio_device);
+ audio_data = NULL;
+ return 0;
+}
+
+static struct platform_driver goldfish_audio_driver = {
+ .probe = goldfish_audio_probe,
+ .remove = goldfish_audio_remove,
+ .driver = {
+ .name = "goldfish_audio"
+ }
+};
+
+module_platform_driver(goldfish_audio_driver);
diff --git a/drivers/staging/goldfish/goldfish_nand.c b/drivers/staging/goldfish/goldfish_nand.c
new file mode 100644
index 000000000..213877a2c
--- /dev/null
+++ b/drivers/staging/goldfish/goldfish_nand.c
@@ -0,0 +1,442 @@
+/*
+ * drivers/mtd/devices/goldfish_nand.c
+ *
+ * Copyright (C) 2007 Google, Inc.
+ * Copyright (C) 2012 Intel, Inc.
+ * Copyright (C) 2013 Intel, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ */
+
+#include <linux/io.h>
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/ioport.h>
+#include <linux/vmalloc.h>
+#include <linux/mtd/mtd.h>
+#include <linux/platform_device.h>
+#include <linux/mutex.h>
+#include <linux/goldfish.h>
+#include <asm/div64.h>
+
+#include "goldfish_nand_reg.h"
+
+struct goldfish_nand {
+ /* lock protects access to the device registers */
+ struct mutex lock;
+ unsigned char __iomem *base;
+ struct cmd_params *cmd_params;
+ size_t mtd_count;
+ struct mtd_info mtd[0];
+};
+
+static u32 goldfish_nand_cmd_with_params(struct mtd_info *mtd,
+ enum nand_cmd cmd, u64 addr, u32 len,
+ void *ptr, u32 *rv)
+{
+ u32 cmdp;
+ struct goldfish_nand *nand = mtd->priv;
+ struct cmd_params *cps = nand->cmd_params;
+ unsigned char __iomem *base = nand->base;
+
+ if (cps == NULL)
+ return -1;
+
+ switch (cmd) {
+ case NAND_CMD_ERASE:
+ cmdp = NAND_CMD_ERASE_WITH_PARAMS;
+ break;
+ case NAND_CMD_READ:
+ cmdp = NAND_CMD_READ_WITH_PARAMS;
+ break;
+ case NAND_CMD_WRITE:
+ cmdp = NAND_CMD_WRITE_WITH_PARAMS;
+ break;
+ default:
+ return -1;
+ }
+ cps->dev = mtd - nand->mtd;
+ cps->addr_high = (u32)(addr >> 32);
+ cps->addr_low = (u32)addr;
+ cps->transfer_size = len;
+ cps->data = (unsigned long)ptr;
+ writel(cmdp, base + NAND_COMMAND);
+ *rv = cps->result;
+ return 0;
+}
+
+static u32 goldfish_nand_cmd(struct mtd_info *mtd, enum nand_cmd cmd,
+ u64 addr, u32 len, void *ptr)
+{
+ struct goldfish_nand *nand = mtd->priv;
+ u32 rv;
+ unsigned char __iomem *base = nand->base;
+
+ mutex_lock(&nand->lock);
+ if (goldfish_nand_cmd_with_params(mtd, cmd, addr, len, ptr, &rv)) {
+ writel(mtd - nand->mtd, base + NAND_DEV);
+ writel((u32)(addr >> 32), base + NAND_ADDR_HIGH);
+ writel((u32)addr, base + NAND_ADDR_LOW);
+ writel(len, base + NAND_TRANSFER_SIZE);
+ gf_write64((u64)ptr, base + NAND_DATA, base + NAND_DATA_HIGH);
+ writel(cmd, base + NAND_COMMAND);
+ rv = readl(base + NAND_RESULT);
+ }
+ mutex_unlock(&nand->lock);
+ return rv;
+}
+
+static int goldfish_nand_erase(struct mtd_info *mtd, struct erase_info *instr)
+{
+ loff_t ofs = instr->addr;
+ u32 len = instr->len;
+ u32 rem;
+
+ if (ofs + len > mtd->size)
+ goto invalid_arg;
+ rem = do_div(ofs, mtd->writesize);
+ if (rem)
+ goto invalid_arg;
+ ofs *= (mtd->writesize + mtd->oobsize);
+
+ if (len % mtd->writesize)
+ goto invalid_arg;
+ len = len / mtd->writesize * (mtd->writesize + mtd->oobsize);
+
+ if (goldfish_nand_cmd(mtd, NAND_CMD_ERASE, ofs, len, NULL) != len) {
+ pr_err("goldfish_nand_erase: erase failed, start %llx, len %x, dev_size %llx, erase_size %x\n",
+ ofs, len, mtd->size, mtd->erasesize);
+ return -EIO;
+ }
+
+ instr->state = MTD_ERASE_DONE;
+ mtd_erase_callback(instr);
+
+ return 0;
+
+invalid_arg:
+ pr_err("goldfish_nand_erase: invalid erase, start %llx, len %x, dev_size %llx, erase_size %x\n",
+ ofs, len, mtd->size, mtd->erasesize);
+ return -EINVAL;
+}
+
+static int goldfish_nand_read_oob(struct mtd_info *mtd, loff_t ofs,
+ struct mtd_oob_ops *ops)
+{
+ u32 rem;
+
+ if (ofs + ops->len > mtd->size)
+ goto invalid_arg;
+ if (ops->datbuf && ops->len && ops->len != mtd->writesize)
+ goto invalid_arg;
+ if (ops->ooblen + ops->ooboffs > mtd->oobsize)
+ goto invalid_arg;
+
+ rem = do_div(ofs, mtd->writesize);
+ if (rem)
+ goto invalid_arg;
+ ofs *= (mtd->writesize + mtd->oobsize);
+
+ if (ops->datbuf)
+ ops->retlen = goldfish_nand_cmd(mtd, NAND_CMD_READ, ofs,
+ ops->len, ops->datbuf);
+ ofs += mtd->writesize + ops->ooboffs;
+ if (ops->oobbuf)
+ ops->oobretlen = goldfish_nand_cmd(mtd, NAND_CMD_READ, ofs,
+ ops->ooblen, ops->oobbuf);
+ return 0;
+
+invalid_arg:
+ pr_err("goldfish_nand_read_oob: invalid read, start %llx, len %zx, ooblen %zx, dev_size %llx, write_size %x\n",
+ ofs, ops->len, ops->ooblen, mtd->size, mtd->writesize);
+ return -EINVAL;
+}
+
+static int goldfish_nand_write_oob(struct mtd_info *mtd, loff_t ofs,
+ struct mtd_oob_ops *ops)
+{
+ u32 rem;
+
+ if (ofs + ops->len > mtd->size)
+ goto invalid_arg;
+ if (ops->len && ops->len != mtd->writesize)
+ goto invalid_arg;
+ if (ops->ooblen + ops->ooboffs > mtd->oobsize)
+ goto invalid_arg;
+
+ rem = do_div(ofs, mtd->writesize);
+ if (rem)
+ goto invalid_arg;
+ ofs *= (mtd->writesize + mtd->oobsize);
+
+ if (ops->datbuf)
+ ops->retlen = goldfish_nand_cmd(mtd, NAND_CMD_WRITE, ofs,
+ ops->len, ops->datbuf);
+ ofs += mtd->writesize + ops->ooboffs;
+ if (ops->oobbuf)
+ ops->oobretlen = goldfish_nand_cmd(mtd, NAND_CMD_WRITE, ofs,
+ ops->ooblen, ops->oobbuf);
+ return 0;
+
+invalid_arg:
+ pr_err("goldfish_nand_write_oob: invalid write, start %llx, len %zx, ooblen %zx, dev_size %llx, write_size %x\n",
+ ofs, ops->len, ops->ooblen, mtd->size, mtd->writesize);
+ return -EINVAL;
+}
+
+static int goldfish_nand_read(struct mtd_info *mtd, loff_t from, size_t len,
+ size_t *retlen, u_char *buf)
+{
+ u32 rem;
+
+ if (from + len > mtd->size)
+ goto invalid_arg;
+
+ rem = do_div(from, mtd->writesize);
+ if (rem)
+ goto invalid_arg;
+ from *= (mtd->writesize + mtd->oobsize);
+
+ *retlen = goldfish_nand_cmd(mtd, NAND_CMD_READ, from, len, buf);
+ return 0;
+
+invalid_arg:
+ pr_err("goldfish_nand_read: invalid read, start %llx, len %zx, dev_size %llx, write_size %x\n",
+ from, len, mtd->size, mtd->writesize);
+ return -EINVAL;
+}
+
+static int goldfish_nand_write(struct mtd_info *mtd, loff_t to, size_t len,
+ size_t *retlen, const u_char *buf)
+{
+ u32 rem;
+
+ if (to + len > mtd->size)
+ goto invalid_arg;
+
+ rem = do_div(to, mtd->writesize);
+ if (rem)
+ goto invalid_arg;
+ to *= (mtd->writesize + mtd->oobsize);
+
+ *retlen = goldfish_nand_cmd(mtd, NAND_CMD_WRITE, to, len, (void *)buf);
+ return 0;
+
+invalid_arg:
+ pr_err("goldfish_nand_write: invalid write, start %llx, len %zx, dev_size %llx, write_size %x\n",
+ to, len, mtd->size, mtd->writesize);
+ return -EINVAL;
+}
+
+static int goldfish_nand_block_isbad(struct mtd_info *mtd, loff_t ofs)
+{
+ u32 rem;
+
+ if (ofs >= mtd->size)
+ goto invalid_arg;
+
+ rem = do_div(ofs, mtd->erasesize);
+ if (rem)
+ goto invalid_arg;
+ ofs *= mtd->erasesize / mtd->writesize;
+ ofs *= (mtd->writesize + mtd->oobsize);
+
+ return goldfish_nand_cmd(mtd, NAND_CMD_BLOCK_BAD_GET, ofs, 0, NULL);
+
+invalid_arg:
+ pr_err("goldfish_nand_block_isbad: invalid arg, ofs %llx, dev_size %llx, write_size %x\n",
+ ofs, mtd->size, mtd->writesize);
+ return -EINVAL;
+}
+
+static int goldfish_nand_block_markbad(struct mtd_info *mtd, loff_t ofs)
+{
+ u32 rem;
+
+ if (ofs >= mtd->size)
+ goto invalid_arg;
+
+ rem = do_div(ofs, mtd->erasesize);
+ if (rem)
+ goto invalid_arg;
+ ofs *= mtd->erasesize / mtd->writesize;
+ ofs *= (mtd->writesize + mtd->oobsize);
+
+ if (goldfish_nand_cmd(mtd, NAND_CMD_BLOCK_BAD_SET, ofs, 0, NULL) != 1)
+ return -EIO;
+ return 0;
+
+invalid_arg:
+ pr_err("goldfish_nand_block_markbad: invalid arg, ofs %llx, dev_size %llx, write_size %x\n",
+ ofs, mtd->size, mtd->writesize);
+ return -EINVAL;
+}
+
+static int nand_setup_cmd_params(struct platform_device *pdev,
+ struct goldfish_nand *nand)
+{
+ u64 paddr;
+ unsigned char __iomem *base = nand->base;
+
+ nand->cmd_params = devm_kzalloc(&pdev->dev,
+ sizeof(struct cmd_params), GFP_KERNEL);
+ if (!nand->cmd_params)
+ return -1;
+
+ paddr = __pa(nand->cmd_params);
+ writel((u32)(paddr >> 32), base + NAND_CMD_PARAMS_ADDR_HIGH);
+ writel((u32)paddr, base + NAND_CMD_PARAMS_ADDR_LOW);
+ return 0;
+}
+
+static int goldfish_nand_init_device(struct platform_device *pdev,
+ struct goldfish_nand *nand, int id)
+{
+ u32 name_len;
+ u32 result;
+ u32 flags;
+ unsigned char __iomem *base = nand->base;
+ struct mtd_info *mtd = &nand->mtd[id];
+ char *name;
+
+ mutex_lock(&nand->lock);
+ writel(id, base + NAND_DEV);
+ flags = readl(base + NAND_DEV_FLAGS);
+ name_len = readl(base + NAND_DEV_NAME_LEN);
+ mtd->writesize = readl(base + NAND_DEV_PAGE_SIZE);
+ mtd->size = readl(base + NAND_DEV_SIZE_LOW);
+ mtd->size |= (u64)readl(base + NAND_DEV_SIZE_HIGH) << 32;
+ mtd->oobsize = readl(base + NAND_DEV_EXTRA_SIZE);
+ mtd->oobavail = mtd->oobsize;
+ mtd->erasesize = readl(base + NAND_DEV_ERASE_SIZE) /
+ (mtd->writesize + mtd->oobsize) * mtd->writesize;
+ do_div(mtd->size, mtd->writesize + mtd->oobsize);
+ mtd->size *= mtd->writesize;
+ dev_dbg(&pdev->dev,
+ "goldfish nand dev%d: size %llx, page %d, extra %d, erase %d\n",
+ id, mtd->size, mtd->writesize,
+ mtd->oobsize, mtd->erasesize);
+ mutex_unlock(&nand->lock);
+
+ mtd->priv = nand;
+
+ name = devm_kzalloc(&pdev->dev, name_len + 1, GFP_KERNEL);
+ if (!name)
+ return -ENOMEM;
+ mtd->name = name;
+
+ result = goldfish_nand_cmd(mtd, NAND_CMD_GET_DEV_NAME, 0, name_len,
+ name);
+ if (result != name_len) {
+ dev_err(&pdev->dev,
+ "goldfish_nand_init_device failed to get dev name %d != %d\n",
+ result, name_len);
+ return -ENODEV;
+ }
+ ((char *)mtd->name)[name_len] = '\0';
+
+ /* Setup the MTD structure */
+ mtd->type = MTD_NANDFLASH;
+ mtd->flags = MTD_CAP_NANDFLASH;
+ if (flags & NAND_DEV_FLAG_READ_ONLY)
+ mtd->flags &= ~MTD_WRITEABLE;
+ if (flags & NAND_DEV_FLAG_CMD_PARAMS_CAP)
+ nand_setup_cmd_params(pdev, nand);
+
+ mtd->owner = THIS_MODULE;
+ mtd->_erase = goldfish_nand_erase;
+ mtd->_read = goldfish_nand_read;
+ mtd->_write = goldfish_nand_write;
+ mtd->_read_oob = goldfish_nand_read_oob;
+ mtd->_write_oob = goldfish_nand_write_oob;
+ mtd->_block_isbad = goldfish_nand_block_isbad;
+ mtd->_block_markbad = goldfish_nand_block_markbad;
+
+ if (mtd_device_register(mtd, NULL, 0))
+ return -EIO;
+
+ return 0;
+}
+
+static int goldfish_nand_probe(struct platform_device *pdev)
+{
+ u32 num_dev;
+ int i;
+ int err;
+ u32 num_dev_working;
+ u32 version;
+ struct resource *r;
+ struct goldfish_nand *nand;
+ unsigned char __iomem *base;
+
+ r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (r == NULL)
+ return -ENODEV;
+
+ base = devm_ioremap(&pdev->dev, r->start, PAGE_SIZE);
+ if (!base)
+ return -ENOMEM;
+
+ version = readl(base + NAND_VERSION);
+ if (version != NAND_VERSION_CURRENT) {
+ dev_err(&pdev->dev,
+ "goldfish_nand_init: version mismatch, got %d, expected %d\n",
+ version, NAND_VERSION_CURRENT);
+ return -ENODEV;
+ }
+ num_dev = readl(base + NAND_NUM_DEV);
+ if (num_dev == 0)
+ return -ENODEV;
+
+ nand = devm_kzalloc(&pdev->dev, sizeof(*nand) +
+ sizeof(struct mtd_info) * num_dev, GFP_KERNEL);
+ if (!nand)
+ return -ENOMEM;
+
+ mutex_init(&nand->lock);
+ nand->base = base;
+ nand->mtd_count = num_dev;
+ platform_set_drvdata(pdev, nand);
+
+ num_dev_working = 0;
+ for (i = 0; i < num_dev; i++) {
+ err = goldfish_nand_init_device(pdev, nand, i);
+ if (err == 0)
+ num_dev_working++;
+ }
+ if (num_dev_working == 0)
+ return -ENODEV;
+ return 0;
+}
+
+static int goldfish_nand_remove(struct platform_device *pdev)
+{
+ struct goldfish_nand *nand = platform_get_drvdata(pdev);
+ int i;
+
+ for (i = 0; i < nand->mtd_count; i++) {
+ if (nand->mtd[i].name)
+ mtd_device_unregister(&nand->mtd[i]);
+ }
+ return 0;
+}
+
+static struct platform_driver goldfish_nand_driver = {
+ .probe = goldfish_nand_probe,
+ .remove = goldfish_nand_remove,
+ .driver = {
+ .name = "goldfish_nand"
+ }
+};
+
+module_platform_driver(goldfish_nand_driver);
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/goldfish/goldfish_nand_reg.h b/drivers/staging/goldfish/goldfish_nand_reg.h
new file mode 100644
index 000000000..fe7f47c7a
--- /dev/null
+++ b/drivers/staging/goldfish/goldfish_nand_reg.h
@@ -0,0 +1,76 @@
+/*
+ * drivers/mtd/devices/goldfish_nand_reg.h
+ *
+ * Copyright (C) 2007 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ */
+
+#ifndef GOLDFISH_NAND_REG_H
+#define GOLDFISH_NAND_REG_H
+
+enum nand_cmd {
+ /* Write device name for NAND_DEV to NAND_DATA (vaddr) */
+ NAND_CMD_GET_DEV_NAME,
+ NAND_CMD_READ,
+ NAND_CMD_WRITE,
+ NAND_CMD_ERASE,
+ /* NAND_RESULT is 1 if block is bad, 0 if it is not */
+ NAND_CMD_BLOCK_BAD_GET,
+ NAND_CMD_BLOCK_BAD_SET,
+ NAND_CMD_READ_WITH_PARAMS,
+ NAND_CMD_WRITE_WITH_PARAMS,
+ NAND_CMD_ERASE_WITH_PARAMS
+};
+
+enum nand_dev_flags {
+ NAND_DEV_FLAG_READ_ONLY = 0x00000001,
+ NAND_DEV_FLAG_CMD_PARAMS_CAP = 0x00000002,
+};
+
+#define NAND_VERSION_CURRENT (1)
+
+enum nand_reg {
+ /* Global */
+ NAND_VERSION = 0x000,
+ NAND_NUM_DEV = 0x004,
+ NAND_DEV = 0x008,
+
+ /* Dev info */
+ NAND_DEV_FLAGS = 0x010,
+ NAND_DEV_NAME_LEN = 0x014,
+ NAND_DEV_PAGE_SIZE = 0x018,
+ NAND_DEV_EXTRA_SIZE = 0x01c,
+ NAND_DEV_ERASE_SIZE = 0x020,
+ NAND_DEV_SIZE_LOW = 0x028,
+ NAND_DEV_SIZE_HIGH = 0x02c,
+
+ /* Command */
+ NAND_RESULT = 0x040,
+ NAND_COMMAND = 0x044,
+ NAND_DATA = 0x048,
+ NAND_DATA_HIGH = 0x100,
+ NAND_TRANSFER_SIZE = 0x04c,
+ NAND_ADDR_LOW = 0x050,
+ NAND_ADDR_HIGH = 0x054,
+ NAND_CMD_PARAMS_ADDR_LOW = 0x058,
+ NAND_CMD_PARAMS_ADDR_HIGH = 0x05c,
+};
+
+struct cmd_params {
+ uint32_t dev;
+ uint32_t addr_low;
+ uint32_t addr_high;
+ uint32_t transfer_size;
+ unsigned long data;
+ uint32_t result;
+};
+#endif
diff --git a/drivers/staging/gs_fpgaboot/Kconfig b/drivers/staging/gs_fpgaboot/Kconfig
new file mode 100644
index 000000000..550645291
--- /dev/null
+++ b/drivers/staging/gs_fpgaboot/Kconfig
@@ -0,0 +1,8 @@
+#
+# "xilinx FPGA firmware download, fpgaboot"
+#
+config GS_FPGABOOT
+ tristate "Xilinx FPGA firmware download module"
+ default n
+ help
+ Xilinx FPGA firmware download module
diff --git a/drivers/staging/gs_fpgaboot/Makefile b/drivers/staging/gs_fpgaboot/Makefile
new file mode 100644
index 000000000..d2f0211ba
--- /dev/null
+++ b/drivers/staging/gs_fpgaboot/Makefile
@@ -0,0 +1,2 @@
+gs_fpga-y += gs_fpgaboot.o io.o
+obj-$(CONFIG_GS_FPGABOOT) += gs_fpga.o
diff --git a/drivers/staging/gs_fpgaboot/README b/drivers/staging/gs_fpgaboot/README
new file mode 100644
index 000000000..8d793c176
--- /dev/null
+++ b/drivers/staging/gs_fpgaboot/README
@@ -0,0 +1,70 @@
+==============================================================================
+Linux Driver Source for Xilinx FPGA firmware download
+==============================================================================
+
+
+TABLE OF CONTENTS.
+
+1. SUMMARY
+2. BACKGROUND
+3. DESIGN
+4. HOW TO USE
+5. REFERENCE
+
+1. SUMMARY
+
+ - Download Xilinx FPGA firmware
+ - This module downloads Xilinx FPGA firmware using gpio pins.
+
+2. BACKGROUND
+
+ An FPGA (Field Programmable Gate Array) is a programmable hardware that is
+ used in various applications. Hardware design needs to programmed through
+ a dedicated device or CPU assisted way (serial or parallel).
+ This driver provides a way to download FPGA firmware.
+
+3. DESIGN
+
+ - load Xilinx FPGA bitstream format[1] firmware image file using
+ kernel firmware framework, request_firmware()
+ - program the Xilinx FPGA using SelectMAP (parallel) mode [2]
+ - FPGA prgram is done by gpio based bit-banging, as an example
+ - platform independent file: gs_fpgaboot.c
+ - platform dependent file: io.c
+
+4. HOW TO USE
+
+ $ insmod gs_fpga.ko file="xlinx_fpga_top_bitstream.bit"
+ $ rmmod gs_fpga
+
+5. USE CASE (from a mailing list discussion with Greg)
+
+ a. As a FPGA development support tool,
+ During FPGA firmware development, you need to download a new FPGA
+ image frequently.
+ You would do that with a dedicated JTAG, which usually a limited
+ resource in the lab.
+ However, if you use my driver, you don't have to have a dedicated JTAG.
+ This is a real gain :)
+
+ b. For the FPGA that runs without config after the download, which
+ doesn't talk to any of Linux interfaces (such as PCIE).
+
+ We download FPGA firmware from user triggered or some other way, and that's it.
+ Since that FPGA runs on its own, it doesn't require a linux driver
+ after the download.
+
+ c. For the FPGA that requires config after the download, which talk to
+ any of linux interfaces (such as PCIE)
+
+ Then, this type of FPGA config can be put into device tree and have a
+ separate driver (pcie or others), then THAT driver calls my driver to
+ download FPGA firmware during the Linux boot, the take over the device
+ through the interface.
+
+6. REFERENCE
+
+ 1. Xilinx APP NOTE XAPP583:
+ http://www.xilinx.com/support/documentation/application_notes/xapp583-fpga-configuration.pdf
+ 2. bitstream file info:
+ http://home.earthlink.net/~davesullins/software/bitinfo.html
diff --git a/drivers/staging/gs_fpgaboot/TODO b/drivers/staging/gs_fpgaboot/TODO
new file mode 100644
index 000000000..2d9fb17d6
--- /dev/null
+++ b/drivers/staging/gs_fpgaboot/TODO
@@ -0,0 +1,7 @@
+TODO:
+ - get bus width input instead of hardcoded bus width
+ - get it reviewed
+
+Please send any patches for this driver to Insop Song<insop.song@gainspeed.com>
+and Greg Kroah-Hartman <gregkh@linuxfoundation.org>.
+And please CC to "Staging subsystem" mail list <devel@driverdev.osuosl.org> too.
diff --git a/drivers/staging/gs_fpgaboot/gs_fpgaboot.c b/drivers/staging/gs_fpgaboot/gs_fpgaboot.c
new file mode 100644
index 000000000..a3a10f9a2
--- /dev/null
+++ b/drivers/staging/gs_fpgaboot/gs_fpgaboot.c
@@ -0,0 +1,389 @@
+/*
+ * This program is free software; you can redistribute it and/or 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/kernel.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/device.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+#include <linux/fs.h>
+#include <linux/platform_device.h>
+#include <linux/of.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/firmware.h>
+
+#include "gs_fpgaboot.h"
+#include "io.h"
+
+#define DEVICE_NAME "device"
+#define CLASS_NAME "fpgaboot"
+
+static uint8_t bits_magic[] = {
+ 0x0, 0x9, 0xf, 0xf0, 0xf, 0xf0,
+ 0xf, 0xf0, 0xf, 0xf0, 0x0, 0x0, 0x1};
+
+/* fake device for request_firmware */
+static struct platform_device *firmware_pdev;
+
+static char *file = "xlinx_fpga_firmware.bit";
+module_param(file, charp, S_IRUGO);
+MODULE_PARM_DESC(file, "Xilinx FPGA firmware file.");
+
+static void read_bitstream(char *bitdata, char *buf, int *offset, int rdsize)
+{
+ memcpy(buf, bitdata + *offset, rdsize);
+ *offset += rdsize;
+}
+
+static void readinfo_bitstream(char *bitdata, char *buf, int *offset)
+{
+ char tbuf[64];
+ int32_t len;
+
+ /* read section char */
+ read_bitstream(bitdata, tbuf, offset, 1);
+
+ /* read length */
+ read_bitstream(bitdata, tbuf, offset, 2);
+
+ len = tbuf[0] << 8 | tbuf[1];
+
+ read_bitstream(bitdata, buf, offset, len);
+ buf[len] = '\0';
+}
+
+/*
+ * read bitdata length
+ */
+static int readlength_bitstream(char *bitdata, int *lendata, int *offset)
+{
+ char tbuf[64];
+
+ /* read section char */
+ read_bitstream(bitdata, tbuf, offset, 1);
+
+ /* make sure it is section 'e' */
+ if (tbuf[0] != 'e') {
+ pr_err("error: length section is not 'e', but %c\n", tbuf[0]);
+ return -1;
+ }
+
+ /* read 4bytes length */
+ read_bitstream(bitdata, tbuf, offset, 4);
+
+ *lendata = tbuf[0] << 24 | tbuf[1] << 16 |
+ tbuf[2] << 8 | tbuf[3];
+
+ return 0;
+}
+
+
+/*
+ * read first 13 bytes to check bitstream magic number
+ */
+static int readmagic_bitstream(char *bitdata, int *offset)
+{
+ char buf[13];
+ int r;
+
+ read_bitstream(bitdata, buf, offset, 13);
+ r = memcmp(buf, bits_magic, 13);
+ if (r) {
+ pr_err("error: corrupted header");
+ return -1;
+ }
+ pr_info("bitstream file magic number Ok\n");
+
+ *offset = 13; /* magic length */
+
+ return 0;
+}
+
+/*
+ * NOTE: supports only bitstream format
+ */
+static enum fmt_image get_imageformat(struct fpgaimage *fimage)
+{
+ return f_bit;
+}
+
+static void gs_print_header(struct fpgaimage *fimage)
+{
+ pr_info("file: %s\n", fimage->filename);
+ pr_info("part: %s\n", fimage->part);
+ pr_info("date: %s\n", fimage->date);
+ pr_info("time: %s\n", fimage->time);
+ pr_info("lendata: %d\n", fimage->lendata);
+}
+
+static void gs_read_bitstream(struct fpgaimage *fimage)
+{
+ char *bitdata;
+ int offset;
+
+ offset = 0;
+ bitdata = (char *)fimage->fw_entry->data;
+
+ readmagic_bitstream(bitdata, &offset);
+ readinfo_bitstream(bitdata, fimage->filename, &offset);
+ readinfo_bitstream(bitdata, fimage->part, &offset);
+ readinfo_bitstream(bitdata, fimage->date, &offset);
+ readinfo_bitstream(bitdata, fimage->time, &offset);
+ readlength_bitstream(bitdata, &fimage->lendata, &offset);
+
+ fimage->fpgadata = bitdata + offset;
+}
+
+static int gs_read_image(struct fpgaimage *fimage)
+{
+ int img_fmt;
+
+ img_fmt = get_imageformat(fimage);
+
+ switch (img_fmt) {
+ case f_bit:
+ pr_info("image is bitstream format\n");
+ gs_read_bitstream(fimage);
+ break;
+ default:
+ pr_err("unsupported fpga image format\n");
+ return -1;
+ }
+
+ gs_print_header(fimage);
+
+ return 0;
+}
+
+static int gs_load_image(struct fpgaimage *fimage, char *fw_file)
+{
+ int err;
+
+ pr_info("load fpgaimage %s\n", fw_file);
+
+ err = request_firmware(&fimage->fw_entry, fw_file, &firmware_pdev->dev);
+ if (err != 0) {
+ pr_err("firmware %s is missing, cannot continue.\n", fw_file);
+ return err;
+ }
+
+ return 0;
+}
+
+static int gs_download_image(struct fpgaimage *fimage, enum wbus bus_bytes)
+{
+ char *bitdata;
+ int size, i, cnt;
+
+ cnt = 0;
+ bitdata = (char *)fimage->fpgadata;
+ size = fimage->lendata;
+
+#ifdef DEBUG_FPGA
+ print_hex_dump_bytes("bitfile sample: ", DUMP_PREFIX_OFFSET,
+ bitdata, 0x100);
+#endif /* DEBUG_FPGA */
+ if (!xl_supported_prog_bus_width(bus_bytes)) {
+ pr_err("unsupported program bus width %d\n",
+ bus_bytes);
+ return -1;
+ }
+
+ /* Bring csi_b, rdwr_b Low and program_b High */
+ xl_program_b(1);
+ xl_rdwr_b(0);
+ xl_csi_b(0);
+
+ /* Configuration reset */
+ xl_program_b(0);
+ msleep(20);
+ xl_program_b(1);
+
+ /* Wait for Device Initialization */
+ while (xl_get_init_b() == 0)
+ ;
+
+ pr_info("device init done\n");
+
+ for (i = 0; i < size; i += bus_bytes)
+ xl_shift_bytes_out(bus_bytes, bitdata+i);
+
+ pr_info("program done\n");
+
+ /* Check INIT_B */
+ if (xl_get_init_b() == 0) {
+ pr_err("init_b 0\n");
+ return -1;
+ }
+
+ while (xl_get_done_b() == 0) {
+ if (cnt++ > MAX_WAIT_DONE) {
+ pr_err("init_B %d\n", xl_get_init_b());
+ break;
+ }
+ }
+
+ if (cnt > MAX_WAIT_DONE) {
+ pr_err("fpga download fail\n");
+ return -1;
+ }
+
+ pr_info("download fpgaimage\n");
+
+ /* Compensate for Special Startup Conditions */
+ xl_shift_cclk(8);
+
+ return 0;
+}
+
+static int gs_release_image(struct fpgaimage *fimage)
+{
+ release_firmware(fimage->fw_entry);
+ pr_info("release fpgaimage\n");
+
+ return 0;
+}
+
+/*
+ * NOTE: supports systemmap parallel programming
+ */
+static int gs_set_download_method(struct fpgaimage *fimage)
+{
+ pr_info("set program method\n");
+
+ fimage->dmethod = m_systemmap;
+
+ pr_info("systemmap program method\n");
+
+ return 0;
+}
+
+static int init_driver(void)
+{
+ firmware_pdev = platform_device_register_simple("fpgaboot", -1,
+ NULL, 0);
+ return PTR_ERR_OR_ZERO(firmware_pdev);
+}
+
+static void finish_driver(void)
+{
+ platform_device_unregister(firmware_pdev);
+}
+
+static int gs_fpgaboot(void)
+{
+ int err;
+ struct fpgaimage *fimage;
+
+ fimage = kmalloc(sizeof(struct fpgaimage), GFP_KERNEL);
+ if (!fimage)
+ return -ENOMEM;
+
+ err = gs_load_image(fimage, file);
+ if (err) {
+ pr_err("gs_load_image error\n");
+ goto err_out1;
+ }
+
+ err = gs_read_image(fimage);
+ if (err) {
+ pr_err("gs_read_image error\n");
+ goto err_out2;
+ }
+
+ err = gs_set_download_method(fimage);
+ if (err) {
+ pr_err("gs_set_download_method error\n");
+ goto err_out2;
+ }
+
+ err = gs_download_image(fimage, bus_2byte);
+ if (err) {
+ pr_err("gs_download_image error\n");
+ goto err_out2;
+ }
+
+ err = gs_release_image(fimage);
+ if (err) {
+ pr_err("gs_release_image error\n");
+ goto err_out1;
+ }
+
+ kfree(fimage);
+ return 0;
+
+err_out2:
+ err = gs_release_image(fimage);
+ if (err)
+ pr_err("gs_release_image error\n");
+err_out1:
+ kfree(fimage);
+
+ return -1;
+
+}
+
+static int __init gs_fpgaboot_init(void)
+{
+ int err;
+
+ pr_info("FPGA DOWNLOAD --->\n");
+
+ pr_info("FPGA image file name: %s\n", file);
+
+ err = init_driver();
+ if (err) {
+ pr_err("FPGA DRIVER INIT FAIL!!\n");
+ return err;
+ }
+
+ err = xl_init_io();
+ if (err) {
+ pr_err("GPIO INIT FAIL!!\n");
+ goto errout;
+ }
+
+ err = gs_fpgaboot();
+ if (err) {
+ pr_err("FPGA DOWNLOAD FAIL!!\n");
+ goto errout;
+ }
+
+ pr_info("FPGA DOWNLOAD DONE <---\n");
+
+ return 0;
+
+errout:
+ finish_driver();
+
+ return err;
+}
+
+static void __exit gs_fpgaboot_exit(void)
+{
+ finish_driver();
+ pr_info("FPGA image download module removed\n");
+}
+
+module_init(gs_fpgaboot_init);
+module_exit(gs_fpgaboot_exit);
+
+MODULE_AUTHOR("Insop Song");
+MODULE_DESCRIPTION("Xlinix FPGA firmware download");
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/gs_fpgaboot/gs_fpgaboot.h b/drivers/staging/gs_fpgaboot/gs_fpgaboot.h
new file mode 100644
index 000000000..f41f4cc79
--- /dev/null
+++ b/drivers/staging/gs_fpgaboot/gs_fpgaboot.h
@@ -0,0 +1,56 @@
+/*
+ * This program is free software; you can redistribute it and/or 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/firmware.h>
+
+#define MAX_STR 256
+
+enum fmt_image {
+ f_bit, /* only bitstream is supported */
+ f_rbt,
+ f_bin,
+ f_mcs,
+ f_hex,
+};
+
+enum mdownload {
+ m_systemmap, /* only system map is supported */
+ m_serial,
+ m_jtag,
+};
+
+/*
+ * xilinx fpgaimage information
+ * NOTE: use MAX_STR instead of dynamic alloc for simplicity
+ */
+struct fpgaimage {
+ enum fmt_image fmt_img;
+ enum mdownload dmethod;
+
+ const struct firmware *fw_entry;
+
+ /*
+ * the followings can be read from bitstream,
+ * but other image format should have as well
+ */
+ char filename[MAX_STR];
+ char part[MAX_STR];
+ char date[MAX_STR];
+ char time[MAX_STR];
+ int32_t lendata;
+ char *fpgadata;
+};
diff --git a/drivers/staging/gs_fpgaboot/io.c b/drivers/staging/gs_fpgaboot/io.c
new file mode 100644
index 000000000..819db53da
--- /dev/null
+++ b/drivers/staging/gs_fpgaboot/io.c
@@ -0,0 +1,122 @@
+/*
+ * This program is free software; you can redistribute it and/or 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/kernel.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/device.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+#include <linux/fs.h>
+#include <linux/platform_device.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/firmware.h>
+#include <linux/io.h>
+
+#include "io.h"
+
+static inline void byte0_out(unsigned char data);
+static inline void byte1_out(unsigned char data);
+static inline void xl_cclk_b(int32_t i);
+
+
+/* Assert and Deassert CCLK */
+void xl_shift_cclk(int count)
+{
+ int i;
+
+ for (i = 0; i < count; i++) {
+ xl_cclk_b(1);
+ xl_cclk_b(0);
+ }
+}
+
+int xl_supported_prog_bus_width(enum wbus bus_bytes)
+{
+ switch (bus_bytes) {
+ case bus_1byte:
+ break;
+ case bus_2byte:
+ break;
+ default:
+ pr_err("unsupported program bus width %d\n",
+ bus_bytes);
+ return 0;
+ }
+
+ return 1;
+}
+
+/* Serialize byte and clock each bit on target's DIN and CCLK pins */
+void xl_shift_bytes_out(enum wbus bus_byte, unsigned char *pdata)
+{
+ /*
+ * supports 1 and 2 bytes programming mode
+ */
+ if (likely(bus_byte == bus_2byte))
+ byte0_out(pdata[0]);
+
+ byte1_out(pdata[1]);
+ xl_shift_cclk(1);
+}
+
+/*
+ * generic bit swap for xilinx SYSTEMMAP FPGA programming
+ */
+void xl_program_b(int32_t i)
+{
+}
+
+void xl_rdwr_b(int32_t i)
+{
+}
+
+void xl_csi_b(int32_t i)
+{
+}
+
+int xl_get_init_b(void)
+{
+ return -1;
+}
+
+int xl_get_done_b(void)
+{
+ return -1;
+}
+
+static inline void byte0_out(unsigned char data)
+{
+}
+
+static inline void byte1_out(unsigned char data)
+{
+}
+
+static inline void xl_cclk_b(int32_t i)
+{
+}
+
+/*
+ * configurable per device type for different I/O config
+ */
+int xl_init_io(void)
+{
+ return -1;
+}
diff --git a/drivers/staging/gs_fpgaboot/io.h b/drivers/staging/gs_fpgaboot/io.h
new file mode 100644
index 000000000..7b46ac24b
--- /dev/null
+++ b/drivers/staging/gs_fpgaboot/io.h
@@ -0,0 +1,90 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#define GPDIR 0
+#define GPCFG 4 /* open drain or not */
+#define GPDAT 8
+
+/*
+ * gpio port and pin definitions
+ * NOTE: port number starts from 0
+ */
+#define XL_INITN_PORT 1
+#define XL_INITN_PIN 14
+#define XL_RDWRN_PORT 1
+#define XL_RDWRN_PIN 13
+#define XL_CCLK_PORT 1
+#define XL_CCLK_PIN 10
+#define XL_PROGN_PORT 1
+#define XL_PROGN_PIN 25
+#define XL_CSIN_PORT 1
+#define XL_CSIN_PIN 26
+#define XL_DONE_PORT 1
+#define XL_DONE_PIN 27
+
+/*
+ * gpio mapping
+ *
+ XL_config_D0 – gpio1_31
+ Xl_config_d1 – gpio1_30
+ Xl_config_d2 – gpio1_29
+ Xl_config_d3 – gpio1_28
+ Xl_config_d4 – gpio1_27
+ Xl_config_d5 – gpio1_26
+ Xl_config_d6 – gpio1_25
+ Xl_config_d7 – gpio1_24
+ Xl_config_d8 – gpio1_23
+ Xl_config_d9 – gpio1_22
+ Xl_config_d10 – gpio1_21
+ Xl_config_d11 – gpio1_20
+ Xl_config_d12 – gpio1_19
+ Xl_config_d13 – gpio1_18
+ Xl_config_d14 – gpio1_16
+ Xl_config_d15 – gpio1_14
+*
+*/
+
+/*
+ * program bus width in bytes
+ */
+enum wbus {
+ bus_1byte = 1,
+ bus_2byte = 2,
+};
+
+
+#define MAX_WAIT_DONE 10000
+
+
+struct gpiobus {
+ int ngpio;
+ void __iomem *r[4];
+};
+
+int xl_supported_prog_bus_width(enum wbus bus_bytes);
+
+void xl_program_b(int32_t i);
+void xl_rdwr_b(int32_t i);
+void xl_csi_b(int32_t i);
+
+int xl_get_init_b(void);
+int xl_get_done_b(void);
+
+void xl_shift_cclk(int count);
+void xl_shift_bytes_out(enum wbus bus_byte, unsigned char *pdata);
+
+int xl_init_io(void);
diff --git a/drivers/staging/i2o/Kconfig b/drivers/staging/i2o/Kconfig
new file mode 100644
index 000000000..286c53f4b
--- /dev/null
+++ b/drivers/staging/i2o/Kconfig
@@ -0,0 +1,120 @@
+menuconfig I2O
+ tristate "I2O device support"
+ depends on PCI
+ ---help---
+ The Intelligent Input/Output (I2O) architecture allows hardware
+ drivers to be split into two parts: an operating system specific
+ module called the OSM and an hardware specific module called the
+ HDM. The OSM can talk to a whole range of HDM's, and ideally the
+ HDM's are not OS dependent. This allows for the same HDM driver to
+ be used under different operating systems if the relevant OSM is in
+ place. In order for this to work, you need to have an I2O interface
+ adapter card in your computer. This card contains a special I/O
+ processor (IOP), thus allowing high speeds since the CPU does not
+ have to deal with I/O.
+
+ If you say Y here, you will get a choice of interface adapter
+ drivers and OSM's with the following questions.
+
+ To compile this support as a module, choose M here: the
+ modules will be called i2o_core.
+
+ If unsure, say N.
+
+if I2O
+
+config I2O_LCT_NOTIFY_ON_CHANGES
+ bool "Enable LCT notification"
+ default y
+ ---help---
+ Only say N here if you have a I2O controller from SUN. The SUN
+ firmware doesn't support LCT notification on changes. If this option
+ is enabled on such a controller the driver will hang up in a endless
+ loop. On all other controllers say Y.
+
+ If unsure, say Y.
+
+config I2O_EXT_ADAPTEC
+ bool "Enable Adaptec extensions"
+ default y
+ ---help---
+ Say Y for support of raidutils for Adaptec I2O controllers. You also
+ have to say Y to "I2O Configuration support", "I2O SCSI OSM" below
+ and to "SCSI generic support" under "SCSI device configuration".
+
+config I2O_EXT_ADAPTEC_DMA64
+ bool "Enable 64-bit DMA"
+ depends on I2O_EXT_ADAPTEC && ( 64BIT || HIGHMEM64G )
+ default y
+ ---help---
+ Say Y for support of 64-bit DMA transfer mode on Adaptec I2O
+ controllers.
+ Note: You need at least firmware version 3709.
+
+config I2O_CONFIG
+ tristate "I2O Configuration support"
+ depends on VIRT_TO_BUS
+ ---help---
+ Say Y for support of the configuration interface for the I2O adapters.
+ If you have a RAID controller from Adaptec and you want to use the
+ raidutils to manage your RAID array, you have to say Y here.
+
+ To compile this support as a module, choose M here: the
+ module will be called i2o_config.
+
+ Note: If you want to use the new API you have to download the
+ i2o_config patch from http://i2o.shadowconnect.com/
+
+config I2O_CONFIG_OLD_IOCTL
+ bool "Enable ioctls (OBSOLETE)"
+ depends on I2O_CONFIG
+ default y
+ ---help---
+ Enables old ioctls.
+
+config I2O_BUS
+ tristate "I2O Bus Adapter OSM"
+ ---help---
+ Include support for the I2O Bus Adapter OSM. The Bus Adapter OSM
+ provides access to the busses on the I2O controller. The main purpose
+ is to rescan the bus to find new devices.
+
+ To compile this support as a module, choose M here: the
+ module will be called i2o_bus.
+
+config I2O_BLOCK
+ tristate "I2O Block OSM"
+ depends on BLOCK
+ ---help---
+ Include support for the I2O Block OSM. The Block OSM presents disk
+ and other structured block devices to the operating system. If you
+ are using an RAID controller, you could access the array only by
+ the Block OSM driver. But it is possible to access the single disks
+ by the SCSI OSM driver, for example to monitor the disks.
+
+ To compile this support as a module, choose M here: the
+ module will be called i2o_block.
+
+config I2O_SCSI
+ tristate "I2O SCSI OSM"
+ depends on SCSI
+ ---help---
+ Allows direct SCSI access to SCSI devices on a SCSI or FibreChannel
+ I2O controller. You can use both the SCSI and Block OSM together if
+ you wish. To access a RAID array, you must use the Block OSM driver.
+ But you could use the SCSI OSM driver to monitor the single disks.
+
+ To compile this support as a module, choose M here: the
+ module will be called i2o_scsi.
+
+config I2O_PROC
+ tristate "I2O /proc support"
+ ---help---
+ If you say Y here and to "/proc file system support", you will be
+ able to read I2O related information from the virtual directory
+ /proc/i2o.
+
+ To compile this support as a module, choose M here: the
+ module will be called i2o_proc.
+
+endif # I2O
diff --git a/drivers/staging/i2o/Makefile b/drivers/staging/i2o/Makefile
new file mode 100644
index 000000000..b0982dacf
--- /dev/null
+++ b/drivers/staging/i2o/Makefile
@@ -0,0 +1,16 @@
+#
+# Makefile for the kernel I2O OSM.
+#
+# Note : at this point, these files are compiled on all systems.
+# In the future, some of these should be built conditionally.
+#
+
+i2o_core-y += iop.o driver.o device.o debug.o pci.o exec-osm.o memory.o
+i2o_bus-y += bus-osm.o
+i2o_config-y += config-osm.o
+obj-$(CONFIG_I2O) += i2o_core.o
+obj-$(CONFIG_I2O_CONFIG)+= i2o_config.o
+obj-$(CONFIG_I2O_BUS) += i2o_bus.o
+obj-$(CONFIG_I2O_BLOCK) += i2o_block.o
+obj-$(CONFIG_I2O_SCSI) += i2o_scsi.o
+obj-$(CONFIG_I2O_PROC) += i2o_proc.o
diff --git a/drivers/staging/i2o/README b/drivers/staging/i2o/README
new file mode 100644
index 000000000..f072a8eb3
--- /dev/null
+++ b/drivers/staging/i2o/README
@@ -0,0 +1,98 @@
+
+ Linux I2O Support (c) Copyright 1999 Red Hat Software
+ and others.
+
+ This program is free software; you can redistribute it and/or
+ 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.
+
+AUTHORS (so far)
+
+Alan Cox, Building Number Three Ltd.
+ Core code, SCSI and Block OSMs
+
+Steve Ralston, LSI Logic Corp.
+ Debugging SCSI and Block OSM
+
+Deepak Saxena, Intel Corp.
+ Various core/block extensions
+ /proc interface, bug fixes
+ Ioctl interfaces for control
+ Debugging LAN OSM
+
+Philip Rumpf
+ Fixed assorted dumb SMP locking bugs
+
+Juha Sievanen, University of Helsinki Finland
+ LAN OSM code
+ /proc interface to LAN class
+ Bug fixes
+ Core code extensions
+
+Auvo Häkkinen, University of Helsinki Finland
+ LAN OSM code
+ /Proc interface to LAN class
+ Bug fixes
+ Core code extensions
+
+Taneli Vähäkangas, University of Helsinki Finland
+ Fixes to i2o_config
+
+CREDITS
+
+ This work was made possible by
+
+Red Hat Software
+ Funding for the Building #3 part of the project
+
+Symbios Logic (Now LSI)
+ Host adapters, hints, known to work platforms when I hit
+ compatibility problems
+
+BoxHill Corporation
+ Loan of initial FibreChannel disk array used for development work.
+
+European Commission
+ Funding the work done by the University of Helsinki
+
+SysKonnect
+ Loan of FDDI and Gigabit Ethernet cards
+
+ASUSTeK
+ Loan of I2O motherboard
+
+STATUS:
+
+o The core setup works within limits.
+o The scsi layer seems to almost work.
+ I'm still chasing down the hang bug.
+o The block OSM is mostly functional
+o LAN OSM works with FDDI and Ethernet cards.
+
+TO DO:
+
+General:
+o Provide hidden address space if asked
+o Long term message flow control
+o PCI IOP's without interrupts are not supported yet
+o Push FAIL handling into the core
+o DDM control interfaces for module load etc
+o Add I2O 2.0 support (Deffered to 2.5 kernel)
+
+Block:
+o Multiple major numbers
+o Read ahead and cache handling stuff. Talk to Ingo and people
+o Power management
+o Finish Media changers
+
+SCSI:
+o Find the right way to associate drives/luns/busses
+
+Lan:
+o Performance tuning
+o Test Fibre Channel code
+
+Tape:
+o Anyone seen anything implementing this ?
+ (D.S: Will attempt to do so if spare cycles permit)
diff --git a/drivers/staging/i2o/README.ioctl b/drivers/staging/i2o/README.ioctl
new file mode 100644
index 000000000..4a7d2ebdf
--- /dev/null
+++ b/drivers/staging/i2o/README.ioctl
@@ -0,0 +1,394 @@
+
+Linux I2O User Space Interface
+rev 0.3 - 04/20/99
+
+=============================================================================
+Originally written by Deepak Saxena(deepak@plexity.net)
+Currently maintained by Deepak Saxena(deepak@plexity.net)
+=============================================================================
+
+I. Introduction
+
+The Linux I2O subsystem provides a set of ioctl() commands that can be
+utilized by user space applications to communicate with IOPs and devices
+on individual IOPs. This document defines the specific ioctl() commands
+that are available to the user and provides examples of their uses.
+
+This document assumes the reader is familiar with or has access to the
+I2O specification as no I2O message parameters are outlined. For information
+on the specification, see http://www.i2osig.org
+
+This document and the I2O user space interface are currently maintained
+by Deepak Saxena. Please send all comments, errata, and bug fixes to
+deepak@csociety.purdue.edu
+
+II. IOP Access
+
+Access to the I2O subsystem is provided through the device file named
+/dev/i2o/ctl. This file is a character file with major number 10 and minor
+number 166. It can be created through the following command:
+
+ mknod /dev/i2o/ctl c 10 166
+
+III. Determining the IOP Count
+
+ SYNOPSIS
+
+ ioctl(fd, I2OGETIOPS, int *count);
+
+ u8 count[MAX_I2O_CONTROLLERS];
+
+ DESCRIPTION
+
+ This function returns the system's active IOP table. count should
+ point to a buffer containing MAX_I2O_CONTROLLERS entries. Upon
+ returning, each entry will contain a non-zero value if the given
+ IOP unit is active, and NULL if it is inactive or non-existent.
+
+ RETURN VALUE.
+
+ Returns 0 if no errors occur, and -1 otherwise. If an error occurs,
+ errno is set appropriately:
+
+ EFAULT Invalid user space pointer was passed
+
+IV. Getting Hardware Resource Table
+
+ SYNOPSIS
+
+ ioctl(fd, I2OHRTGET, struct i2o_cmd_hrt *hrt);
+
+ struct i2o_cmd_hrtlct
+ {
+ u32 iop; /* IOP unit number */
+ void *resbuf; /* Buffer for result */
+ u32 *reslen; /* Buffer length in bytes */
+ };
+
+ DESCRIPTION
+
+ This function returns the Hardware Resource Table of the IOP specified
+ by hrt->iop in the buffer pointed to by hrt->resbuf. The actual size of
+ the data is written into *(hrt->reslen).
+
+ RETURNS
+
+ This function returns 0 if no errors occur. If an error occurs, -1
+ is returned and errno is set appropriately:
+
+ EFAULT Invalid user space pointer was passed
+ ENXIO Invalid IOP number
+ ENOBUFS Buffer not large enough. If this occurs, the required
+ buffer length is written into *(hrt->reslen)
+
+V. Getting Logical Configuration Table
+
+ SYNOPSIS
+
+ ioctl(fd, I2OLCTGET, struct i2o_cmd_lct *lct);
+
+ struct i2o_cmd_hrtlct
+ {
+ u32 iop; /* IOP unit number */
+ void *resbuf; /* Buffer for result */
+ u32 *reslen; /* Buffer length in bytes */
+ };
+
+ DESCRIPTION
+
+ This function returns the Logical Configuration Table of the IOP specified
+ by lct->iop in the buffer pointed to by lct->resbuf. The actual size of
+ the data is written into *(lct->reslen).
+
+ RETURNS
+
+ This function returns 0 if no errors occur. If an error occurs, -1
+ is returned and errno is set appropriately:
+
+ EFAULT Invalid user space pointer was passed
+ ENXIO Invalid IOP number
+ ENOBUFS Buffer not large enough. If this occurs, the required
+ buffer length is written into *(lct->reslen)
+
+VI. Setting Parameters
+
+ SYNOPSIS
+
+ ioctl(fd, I2OPARMSET, struct i2o_parm_setget *ops);
+
+ struct i2o_cmd_psetget
+ {
+ u32 iop; /* IOP unit number */
+ u32 tid; /* Target device TID */
+ void *opbuf; /* Operation List buffer */
+ u32 oplen; /* Operation List buffer length in bytes */
+ void *resbuf; /* Result List buffer */
+ u32 *reslen; /* Result List buffer length in bytes */
+ };
+
+ DESCRIPTION
+
+ This function posts a UtilParamsSet message to the device identified
+ by ops->iop and ops->tid. The operation list for the message is
+ sent through the ops->opbuf buffer, and the result list is written
+ into the buffer pointed to by ops->resbuf. The number of bytes
+ written is placed into *(ops->reslen).
+
+ RETURNS
+
+ The return value is the size in bytes of the data written into
+ ops->resbuf if no errors occur. If an error occurs, -1 is returned
+ and errno is set appropriately:
+
+ EFAULT Invalid user space pointer was passed
+ ENXIO Invalid IOP number
+ ENOBUFS Buffer not large enough. If this occurs, the required
+ buffer length is written into *(ops->reslen)
+ ETIMEDOUT Timeout waiting for reply message
+ ENOMEM Kernel memory allocation error
+
+ A return value of 0 does not mean that the value was actually
+ changed properly on the IOP. The user should check the result
+ list to determine the specific status of the transaction.
+
+VII. Getting Parameters
+
+ SYNOPSIS
+
+ ioctl(fd, I2OPARMGET, struct i2o_parm_setget *ops);
+
+ struct i2o_parm_setget
+ {
+ u32 iop; /* IOP unit number */
+ u32 tid; /* Target device TID */
+ void *opbuf; /* Operation List buffer */
+ u32 oplen; /* Operation List buffer length in bytes */
+ void *resbuf; /* Result List buffer */
+ u32 *reslen; /* Result List buffer length in bytes */
+ };
+
+ DESCRIPTION
+
+ This function posts a UtilParamsGet message to the device identified
+ by ops->iop and ops->tid. The operation list for the message is
+ sent through the ops->opbuf buffer, and the result list is written
+ into the buffer pointed to by ops->resbuf. The actual size of data
+ written is placed into *(ops->reslen).
+
+ RETURNS
+
+ EFAULT Invalid user space pointer was passed
+ ENXIO Invalid IOP number
+ ENOBUFS Buffer not large enough. If this occurs, the required
+ buffer length is written into *(ops->reslen)
+ ETIMEDOUT Timeout waiting for reply message
+ ENOMEM Kernel memory allocation error
+
+ A return value of 0 does not mean that the value was actually
+ properly retrieved. The user should check the result list
+ to determine the specific status of the transaction.
+
+VIII. Downloading Software
+
+ SYNOPSIS
+
+ ioctl(fd, I2OSWDL, struct i2o_sw_xfer *sw);
+
+ struct i2o_sw_xfer
+ {
+ u32 iop; /* IOP unit number */
+ u8 flags; /* DownloadFlags field */
+ u8 sw_type; /* Software type */
+ u32 sw_id; /* Software ID */
+ void *buf; /* Pointer to software buffer */
+ u32 *swlen; /* Length of software buffer */
+ u32 *maxfrag; /* Number of fragments */
+ u32 *curfrag; /* Current fragment number */
+ };
+
+ DESCRIPTION
+
+ This function downloads a software fragment pointed by sw->buf
+ to the iop identified by sw->iop. The DownloadFlags, SwID, SwType
+ and SwSize fields of the ExecSwDownload message are filled in with
+ the values of sw->flags, sw->sw_id, sw->sw_type and *(sw->swlen).
+
+ The fragments _must_ be sent in order and be 8K in size. The last
+ fragment _may_ be shorter, however. The kernel will compute its
+ size based on information in the sw->swlen field.
+
+ Please note that SW transfers can take a long time.
+
+ RETURNS
+
+ This function returns 0 no errors occur. If an error occurs, -1
+ is returned and errno is set appropriately:
+
+ EFAULT Invalid user space pointer was passed
+ ENXIO Invalid IOP number
+ ETIMEDOUT Timeout waiting for reply message
+ ENOMEM Kernel memory allocation error
+
+IX. Uploading Software
+
+ SYNOPSIS
+
+ ioctl(fd, I2OSWUL, struct i2o_sw_xfer *sw);
+
+ struct i2o_sw_xfer
+ {
+ u32 iop; /* IOP unit number */
+ u8 flags; /* UploadFlags */
+ u8 sw_type; /* Software type */
+ u32 sw_id; /* Software ID */
+ void *buf; /* Pointer to software buffer */
+ u32 *swlen; /* Length of software buffer */
+ u32 *maxfrag; /* Number of fragments */
+ u32 *curfrag; /* Current fragment number */
+ };
+
+ DESCRIPTION
+
+ This function uploads a software fragment from the IOP identified
+ by sw->iop, sw->sw_type, sw->sw_id and optionally sw->swlen fields.
+ The UploadFlags, SwID, SwType and SwSize fields of the ExecSwUpload
+ message are filled in with the values of sw->flags, sw->sw_id,
+ sw->sw_type and *(sw->swlen).
+
+ The fragments _must_ be requested in order and be 8K in size. The
+ user is responsible for allocating memory pointed by sw->buf. The
+ last fragment _may_ be shorter.
+
+ Please note that SW transfers can take a long time.
+
+ RETURNS
+
+ This function returns 0 if no errors occur. If an error occurs, -1
+ is returned and errno is set appropriately:
+
+ EFAULT Invalid user space pointer was passed
+ ENXIO Invalid IOP number
+ ETIMEDOUT Timeout waiting for reply message
+ ENOMEM Kernel memory allocation error
+
+X. Removing Software
+
+ SYNOPSIS
+
+ ioctl(fd, I2OSWDEL, struct i2o_sw_xfer *sw);
+
+ struct i2o_sw_xfer
+ {
+ u32 iop; /* IOP unit number */
+ u8 flags; /* RemoveFlags */
+ u8 sw_type; /* Software type */
+ u32 sw_id; /* Software ID */
+ void *buf; /* Unused */
+ u32 *swlen; /* Length of the software data */
+ u32 *maxfrag; /* Unused */
+ u32 *curfrag; /* Unused */
+ };
+
+ DESCRIPTION
+
+ This function removes software from the IOP identified by sw->iop.
+ The RemoveFlags, SwID, SwType and SwSize fields of the ExecSwRemove message
+ are filled in with the values of sw->flags, sw->sw_id, sw->sw_type and
+ *(sw->swlen). Give zero in *(sw->len) if the value is unknown. IOP uses
+ *(sw->swlen) value to verify correct identication of the module to remove.
+ The actual size of the module is written into *(sw->swlen).
+
+ RETURNS
+
+ This function returns 0 if no errors occur. If an error occurs, -1
+ is returned and errno is set appropriately:
+
+ EFAULT Invalid user space pointer was passed
+ ENXIO Invalid IOP number
+ ETIMEDOUT Timeout waiting for reply message
+ ENOMEM Kernel memory allocation error
+
+X. Validating Configuration
+
+ SYNOPSIS
+
+ ioctl(fd, I2OVALIDATE, int *iop);
+ u32 iop;
+
+ DESCRIPTION
+
+ This function posts an ExecConfigValidate message to the controller
+ identified by iop. This message indicates that the current
+ configuration is accepted. The iop changes the status of suspect drivers
+ to valid and may delete old drivers from its store.
+
+ RETURNS
+
+ This function returns 0 if no erro occur. If an error occurs, -1 is
+ returned and errno is set appropriately:
+
+ ETIMEDOUT Timeout waiting for reply message
+ ENXIO Invalid IOP number
+
+XI. Configuration Dialog
+
+ SYNOPSIS
+
+ ioctl(fd, I2OHTML, struct i2o_html *htquery);
+ struct i2o_html
+ {
+ u32 iop; /* IOP unit number */
+ u32 tid; /* Target device ID */
+ u32 page; /* HTML page */
+ void *resbuf; /* Buffer for reply HTML page */
+ u32 *reslen; /* Length in bytes of reply buffer */
+ void *qbuf; /* Pointer to HTTP query string */
+ u32 qlen; /* Length in bytes of query string buffer */
+ };
+
+ DESCRIPTION
+
+ This function posts an UtilConfigDialog message to the device identified
+ by htquery->iop and htquery->tid. The requested HTML page number is
+ provided by the htquery->page field, and the resultant data is stored
+ in the buffer pointed to by htquery->resbuf. If there is an HTTP query
+ string that is to be sent to the device, it should be sent in the buffer
+ pointed to by htquery->qbuf. If there is no query string, this field
+ should be set to NULL. The actual size of the reply received is written
+ into *(htquery->reslen).
+
+ RETURNS
+
+ This function returns 0 if no error occur. If an error occurs, -1
+ is returned and errno is set appropriately:
+
+ EFAULT Invalid user space pointer was passed
+ ENXIO Invalid IOP number
+ ENOBUFS Buffer not large enough. If this occurs, the required
+ buffer length is written into *(ops->reslen)
+ ETIMEDOUT Timeout waiting for reply message
+ ENOMEM Kernel memory allocation error
+
+XII. Events
+
+ In the process of determining this. Current idea is to have use
+ the select() interface to allow user apps to periodically poll
+ the /dev/i2o/ctl device for events. When select() notifies the user
+ that an event is available, the user would call read() to retrieve
+ a list of all the events that are pending for the specific device.
+
+=============================================================================
+Revision History
+=============================================================================
+
+Rev 0.1 - 04/01/99
+- Initial revision
+
+Rev 0.2 - 04/06/99
+- Changed return values to match UNIX ioctl() standard. Only return values
+ are 0 and -1. All errors are reported through errno.
+- Added summary of proposed possible event interfaces
+
+Rev 0.3 - 04/20/99
+- Changed all ioctls() to use pointers to user data instead of actual data
+- Updated error values to match the code
diff --git a/drivers/staging/i2o/bus-osm.c b/drivers/staging/i2o/bus-osm.c
new file mode 100644
index 000000000..43e357eee
--- /dev/null
+++ b/drivers/staging/i2o/bus-osm.c
@@ -0,0 +1,177 @@
+/*
+ * Bus Adapter OSM
+ *
+ * Copyright (C) 2005 Markus Lidel <Markus.Lidel@shadowconnect.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.
+ *
+ * Fixes/additions:
+ * Markus Lidel <Markus.Lidel@shadowconnect.com>
+ * initial version.
+ */
+
+#include <linux/module.h>
+#include "i2o.h"
+
+#define OSM_NAME "bus-osm"
+#define OSM_VERSION "1.317"
+#define OSM_DESCRIPTION "I2O Bus Adapter OSM"
+
+static struct i2o_driver i2o_bus_driver;
+
+/* Bus OSM class handling definition */
+static struct i2o_class_id i2o_bus_class_id[] = {
+ {I2O_CLASS_BUS_ADAPTER},
+ {I2O_CLASS_END}
+};
+
+/**
+ * i2o_bus_scan - Scan the bus for new devices
+ * @dev: I2O device of the bus, which should be scanned
+ *
+ * Scans the bus dev for new / removed devices. After the scan a new LCT
+ * will be fetched automatically.
+ *
+ * Returns 0 on success or negative error code on failure.
+ */
+static int i2o_bus_scan(struct i2o_device *dev)
+{
+ struct i2o_message *msg;
+
+ msg = i2o_msg_get_wait(dev->iop, I2O_TIMEOUT_MESSAGE_GET);
+ if (IS_ERR(msg))
+ return -ETIMEDOUT;
+
+ msg->u.head[0] = cpu_to_le32(FIVE_WORD_MSG_SIZE | SGL_OFFSET_0);
+ msg->u.head[1] =
+ cpu_to_le32(I2O_CMD_BUS_SCAN << 24 | HOST_TID << 12 | dev->lct_data.
+ tid);
+
+ return i2o_msg_post_wait(dev->iop, msg, 60);
+};
+
+/**
+ * i2o_bus_store_scan - Scan the I2O Bus Adapter
+ * @d: device which should be scanned
+ * @attr: device_attribute
+ * @buf: output buffer
+ * @count: buffer size
+ *
+ * Returns count.
+ */
+static ssize_t i2o_bus_store_scan(struct device *d,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct i2o_device *i2o_dev = to_i2o_device(d);
+ int rc;
+
+ rc = i2o_bus_scan(i2o_dev);
+ if (rc)
+ osm_warn("bus scan failed %d\n", rc);
+
+ return count;
+}
+
+/* Bus Adapter OSM device attributes */
+static DEVICE_ATTR(scan, S_IWUSR, NULL, i2o_bus_store_scan);
+
+/**
+ * i2o_bus_probe - verify if dev is a I2O Bus Adapter device and install it
+ * @dev: device to verify if it is a I2O Bus Adapter device
+ *
+ * Because we want all Bus Adapters always return 0.
+ * Except when we fail. Then we are sad.
+ *
+ * Returns 0, except when we fail to excel.
+ */
+static int i2o_bus_probe(struct device *dev)
+{
+ struct i2o_device *i2o_dev = to_i2o_device(get_device(dev));
+ int rc;
+
+ rc = device_create_file(dev, &dev_attr_scan);
+ if (rc)
+ goto err_out;
+
+ osm_info("device added (TID: %03x)\n", i2o_dev->lct_data.tid);
+
+ return 0;
+
+err_out:
+ put_device(dev);
+ return rc;
+};
+
+/**
+ * i2o_bus_remove - remove the I2O Bus Adapter device from the system again
+ * @dev: I2O Bus Adapter device which should be removed
+ *
+ * Always returns 0.
+ */
+static int i2o_bus_remove(struct device *dev)
+{
+ struct i2o_device *i2o_dev = to_i2o_device(dev);
+
+ device_remove_file(dev, &dev_attr_scan);
+
+ put_device(dev);
+
+ osm_info("device removed (TID: %03x)\n", i2o_dev->lct_data.tid);
+
+ return 0;
+};
+
+/* Bus Adapter OSM driver struct */
+static struct i2o_driver i2o_bus_driver = {
+ .name = OSM_NAME,
+ .classes = i2o_bus_class_id,
+ .driver = {
+ .probe = i2o_bus_probe,
+ .remove = i2o_bus_remove,
+ },
+};
+
+/**
+ * i2o_bus_init - Bus Adapter OSM initialization function
+ *
+ * Only register the Bus Adapter OSM in the I2O core.
+ *
+ * Returns 0 on success or negative error code on failure.
+ */
+static int __init i2o_bus_init(void)
+{
+ int rc;
+
+ printk(KERN_INFO OSM_DESCRIPTION " v" OSM_VERSION "\n");
+
+ /* Register Bus Adapter OSM into I2O core */
+ rc = i2o_driver_register(&i2o_bus_driver);
+ if (rc) {
+ osm_err("Could not register Bus Adapter OSM\n");
+ return rc;
+ }
+
+ return 0;
+};
+
+/**
+ * i2o_bus_exit - Bus Adapter OSM exit function
+ *
+ * Unregisters Bus Adapter OSM from I2O core.
+ */
+static void __exit i2o_bus_exit(void)
+{
+ i2o_driver_unregister(&i2o_bus_driver);
+};
+
+MODULE_AUTHOR("Markus Lidel <Markus.Lidel@shadowconnect.com>");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION(OSM_DESCRIPTION);
+MODULE_VERSION(OSM_VERSION);
+
+module_init(i2o_bus_init);
+module_exit(i2o_bus_exit);
diff --git a/drivers/staging/i2o/config-osm.c b/drivers/staging/i2o/config-osm.c
new file mode 100644
index 000000000..45091ac66
--- /dev/null
+++ b/drivers/staging/i2o/config-osm.c
@@ -0,0 +1,90 @@
+/*
+ * Configuration OSM
+ *
+ * Copyright (C) 2005 Markus Lidel <Markus.Lidel@shadowconnect.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.
+ *
+ * Fixes/additions:
+ * Markus Lidel <Markus.Lidel@shadowconnect.com>
+ * initial version.
+ */
+
+#include <linux/module.h>
+#include "i2o.h"
+#include <linux/dcache.h>
+#include <linux/namei.h>
+#include <linux/fs.h>
+
+#include <linux/uaccess.h>
+
+#define OSM_NAME "config-osm"
+#define OSM_VERSION "1.323"
+#define OSM_DESCRIPTION "I2O Configuration OSM"
+
+/* access mode user rw */
+#define S_IWRSR (S_IRUSR | S_IWUSR)
+
+static struct i2o_driver i2o_config_driver;
+
+/* Config OSM driver struct */
+static struct i2o_driver i2o_config_driver = {
+ .name = OSM_NAME,
+};
+
+#ifdef CONFIG_I2O_CONFIG_OLD_IOCTL
+#include "i2o_config.c"
+#endif
+
+/**
+ * i2o_config_init - Configuration OSM initialization function
+ *
+ * Registers Configuration OSM in the I2O core and if old ioctl's are
+ * compiled in initialize them.
+ *
+ * Returns 0 on success or negative error code on failure.
+ */
+static int __init i2o_config_init(void)
+{
+ printk(KERN_INFO OSM_DESCRIPTION " v" OSM_VERSION "\n");
+
+ if (i2o_driver_register(&i2o_config_driver)) {
+ osm_err("handler register failed.\n");
+ return -EBUSY;
+ }
+#ifdef CONFIG_I2O_CONFIG_OLD_IOCTL
+ if (i2o_config_old_init()) {
+ osm_err("old config handler initialization failed\n");
+ i2o_driver_unregister(&i2o_config_driver);
+ return -EBUSY;
+ }
+#endif
+
+ return 0;
+}
+
+/**
+ * i2o_config_exit - Configuration OSM exit function
+ *
+ * If old ioctl's are compiled in exit remove them and unregisters
+ * Configuration OSM from I2O core.
+ */
+static void i2o_config_exit(void)
+{
+#ifdef CONFIG_I2O_CONFIG_OLD_IOCTL
+ i2o_config_old_exit();
+#endif
+
+ i2o_driver_unregister(&i2o_config_driver);
+}
+
+MODULE_AUTHOR("Markus Lidel <Markus.Lidel@shadowconnect.com>");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION(OSM_DESCRIPTION);
+MODULE_VERSION(OSM_VERSION);
+
+module_init(i2o_config_init);
+module_exit(i2o_config_exit);
diff --git a/drivers/staging/i2o/core.h b/drivers/staging/i2o/core.h
new file mode 100644
index 000000000..91614f11f
--- /dev/null
+++ b/drivers/staging/i2o/core.h
@@ -0,0 +1,69 @@
+/*
+ * I2O core internal declarations
+ *
+ * Copyright (C) 2005 Markus Lidel <Markus.Lidel@shadowconnect.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.
+ *
+ * Fixes/additions:
+ * Markus Lidel <Markus.Lidel@shadowconnect.com>
+ * initial version.
+ */
+
+/* Exec-OSM */
+extern struct i2o_driver i2o_exec_driver;
+extern int i2o_exec_lct_get(struct i2o_controller *);
+
+extern int __init i2o_exec_init(void);
+extern void i2o_exec_exit(void);
+
+/* driver */
+extern struct bus_type i2o_bus_type;
+
+extern int i2o_driver_dispatch(struct i2o_controller *, u32);
+
+extern int __init i2o_driver_init(void);
+extern void i2o_driver_exit(void);
+
+/* PCI */
+extern int __init i2o_pci_init(void);
+extern void __exit i2o_pci_exit(void);
+
+/* device */
+extern const struct attribute_group *i2o_device_groups[];
+
+extern void i2o_device_remove(struct i2o_device *);
+extern int i2o_device_parse_lct(struct i2o_controller *);
+
+int i2o_parm_issue(struct i2o_device *i2o_dev, int cmd, void *oplist,
+ int oplen, void *reslist, int reslen);
+
+/* IOP */
+extern struct i2o_controller *i2o_iop_alloc(void);
+
+/**
+ * i2o_iop_free - Free the i2o_controller struct
+ * @c: I2O controller to free
+ */
+static inline void i2o_iop_free(struct i2o_controller *c)
+{
+ i2o_pool_free(&c->in_msg);
+ kfree(c);
+}
+
+extern int i2o_iop_add(struct i2o_controller *);
+extern void i2o_iop_remove(struct i2o_controller *);
+
+/* control registers relative to c->base */
+#define I2O_IRQ_STATUS 0x30
+#define I2O_IRQ_MASK 0x34
+#define I2O_IN_PORT 0x40
+#define I2O_OUT_PORT 0x44
+
+/* Motorola/Freescale specific register offset */
+#define I2O_MOTOROLA_PORT_OFFSET 0x10400
+
+#define I2O_IRQ_OUTBOUND_POST 0x00000008
diff --git a/drivers/staging/i2o/debug.c b/drivers/staging/i2o/debug.c
new file mode 100644
index 000000000..12b783b2a
--- /dev/null
+++ b/drivers/staging/i2o/debug.c
@@ -0,0 +1,473 @@
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/pci.h>
+#include "i2o.h"
+
+static void i2o_report_util_cmd(u8 cmd);
+static void i2o_report_exec_cmd(u8 cmd);
+static void i2o_report_fail_status(u8 req_status, u32 *msg);
+static void i2o_report_common_status(u8 req_status);
+static void i2o_report_common_dsc(u16 detailed_status);
+
+/*
+ * Used for error reporting/debugging purposes.
+ * Report Cmd name, Request status, Detailed Status.
+ */
+void i2o_report_status(const char *severity, const char *str,
+ struct i2o_message *m)
+{
+ u32 *msg = (u32 *) m;
+ u8 cmd = (msg[1] >> 24) & 0xFF;
+ u8 req_status = (msg[4] >> 24) & 0xFF;
+ u16 detailed_status = msg[4] & 0xFFFF;
+
+ if (cmd == I2O_CMD_UTIL_EVT_REGISTER)
+ return; /* No status in this reply */
+
+ printk("%s%s: ", severity, str);
+
+ if (cmd < 0x1F) // Utility cmd
+ i2o_report_util_cmd(cmd);
+
+ else if (cmd >= 0xA0 && cmd <= 0xEF) // Executive cmd
+ i2o_report_exec_cmd(cmd);
+ else
+ printk("Cmd = %0#2x, ", cmd); // Other cmds
+
+ if (msg[0] & MSG_FAIL) {
+ i2o_report_fail_status(req_status, msg);
+ return;
+ }
+
+ i2o_report_common_status(req_status);
+
+ if (cmd < 0x1F || (cmd >= 0xA0 && cmd <= 0xEF))
+ i2o_report_common_dsc(detailed_status);
+ else
+ printk(" / DetailedStatus = %0#4x.\n",
+ detailed_status);
+}
+
+/* Used to dump a message to syslog during debugging */
+void i2o_dump_message(struct i2o_message *m)
+{
+#ifdef DEBUG
+ u32 *msg = (u32 *) m;
+ int i;
+
+ printk(KERN_INFO "Dumping I2O message size %d @ %p\n",
+ msg[0] >> 16 & 0xffff, msg);
+ for (i = 0; i < ((msg[0] >> 16) & 0xffff); i++)
+ printk(KERN_INFO " msg[%d] = %0#10x\n", i, msg[i]);
+#endif
+}
+
+/*
+ * Used for error reporting/debugging purposes.
+ * Following fail status are common to all classes.
+ * The preserved message must be handled in the reply handler.
+ */
+static void i2o_report_fail_status(u8 req_status, u32 *msg)
+{
+ static char *FAIL_STATUS[] = {
+ "0x80", /* not used */
+ "SERVICE_SUSPENDED", /* 0x81 */
+ "SERVICE_TERMINATED", /* 0x82 */
+ "CONGESTION",
+ "FAILURE",
+ "STATE_ERROR",
+ "TIME_OUT",
+ "ROUTING_FAILURE",
+ "INVALID_VERSION",
+ "INVALID_OFFSET",
+ "INVALID_MSG_FLAGS",
+ "FRAME_TOO_SMALL",
+ "FRAME_TOO_LARGE",
+ "INVALID_TARGET_ID",
+ "INVALID_INITIATOR_ID",
+ "INVALID_INITIATOR_CONTEX", /* 0x8F */
+ "UNKNOWN_FAILURE" /* 0xFF */
+ };
+
+ if (req_status == I2O_FSC_TRANSPORT_UNKNOWN_FAILURE)
+ printk("TRANSPORT_UNKNOWN_FAILURE (%0#2x).\n",
+ req_status);
+ else
+ printk("TRANSPORT_%s.\n",
+ FAIL_STATUS[req_status & 0x0F]);
+
+ /* Dump some details */
+
+ printk(KERN_ERR " InitiatorId = %d, TargetId = %d\n",
+ (msg[1] >> 12) & 0xFFF, msg[1] & 0xFFF);
+ printk(KERN_ERR " LowestVersion = 0x%02X, HighestVersion = 0x%02X\n",
+ (msg[4] >> 8) & 0xFF, msg[4] & 0xFF);
+ printk(KERN_ERR " FailingHostUnit = 0x%04X, FailingIOP = 0x%03X\n",
+ msg[5] >> 16, msg[5] & 0xFFF);
+
+ printk(KERN_ERR " Severity: 0x%02X\n", (msg[4] >> 16) & 0xFF);
+ if (msg[4] & (1 << 16))
+ printk(KERN_DEBUG "(FormatError), "
+ "this msg can never be delivered/processed.\n");
+ if (msg[4] & (1 << 17))
+ printk(KERN_DEBUG "(PathError), "
+ "this msg can no longer be delivered/processed.\n");
+ if (msg[4] & (1 << 18))
+ printk(KERN_DEBUG "(PathState), "
+ "the system state does not allow delivery.\n");
+ if (msg[4] & (1 << 19))
+ printk(KERN_DEBUG
+ "(Congestion), resources temporarily not available;"
+ "do not retry immediately.\n");
+}
+
+/*
+ * Used for error reporting/debugging purposes.
+ * Following reply status are common to all classes.
+ */
+static void i2o_report_common_status(u8 req_status)
+{
+ static char *REPLY_STATUS[] = {
+ "SUCCESS",
+ "ABORT_DIRTY",
+ "ABORT_NO_DATA_TRANSFER",
+ "ABORT_PARTIAL_TRANSFER",
+ "ERROR_DIRTY",
+ "ERROR_NO_DATA_TRANSFER",
+ "ERROR_PARTIAL_TRANSFER",
+ "PROCESS_ABORT_DIRTY",
+ "PROCESS_ABORT_NO_DATA_TRANSFER",
+ "PROCESS_ABORT_PARTIAL_TRANSFER",
+ "TRANSACTION_ERROR",
+ "PROGRESS_REPORT"
+ };
+
+ if (req_status >= ARRAY_SIZE(REPLY_STATUS))
+ printk("RequestStatus = %0#2x", req_status);
+ else
+ printk("%s", REPLY_STATUS[req_status]);
+}
+
+/*
+ * Used for error reporting/debugging purposes.
+ * Following detailed status are valid for executive class,
+ * utility class, DDM class and for transaction error replies.
+ */
+static void i2o_report_common_dsc(u16 detailed_status)
+{
+ static char *COMMON_DSC[] = {
+ "SUCCESS",
+ "0x01", // not used
+ "BAD_KEY",
+ "TCL_ERROR",
+ "REPLY_BUFFER_FULL",
+ "NO_SUCH_PAGE",
+ "INSUFFICIENT_RESOURCE_SOFT",
+ "INSUFFICIENT_RESOURCE_HARD",
+ "0x08", // not used
+ "CHAIN_BUFFER_TOO_LARGE",
+ "UNSUPPORTED_FUNCTION",
+ "DEVICE_LOCKED",
+ "DEVICE_RESET",
+ "INAPPROPRIATE_FUNCTION",
+ "INVALID_INITIATOR_ADDRESS",
+ "INVALID_MESSAGE_FLAGS",
+ "INVALID_OFFSET",
+ "INVALID_PARAMETER",
+ "INVALID_REQUEST",
+ "INVALID_TARGET_ADDRESS",
+ "MESSAGE_TOO_LARGE",
+ "MESSAGE_TOO_SMALL",
+ "MISSING_PARAMETER",
+ "TIMEOUT",
+ "UNKNOWN_ERROR",
+ "UNKNOWN_FUNCTION",
+ "UNSUPPORTED_VERSION",
+ "DEVICE_BUSY",
+ "DEVICE_NOT_AVAILABLE"
+ };
+
+ if (detailed_status > I2O_DSC_DEVICE_NOT_AVAILABLE)
+ printk(" / DetailedStatus = %0#4x.\n",
+ detailed_status);
+ else
+ printk(" / %s.\n", COMMON_DSC[detailed_status]);
+}
+
+/*
+ * Used for error reporting/debugging purposes
+ */
+static void i2o_report_util_cmd(u8 cmd)
+{
+ switch (cmd) {
+ case I2O_CMD_UTIL_NOP:
+ printk("UTIL_NOP, ");
+ break;
+ case I2O_CMD_UTIL_ABORT:
+ printk("UTIL_ABORT, ");
+ break;
+ case I2O_CMD_UTIL_CLAIM:
+ printk("UTIL_CLAIM, ");
+ break;
+ case I2O_CMD_UTIL_RELEASE:
+ printk("UTIL_CLAIM_RELEASE, ");
+ break;
+ case I2O_CMD_UTIL_CONFIG_DIALOG:
+ printk("UTIL_CONFIG_DIALOG, ");
+ break;
+ case I2O_CMD_UTIL_DEVICE_RESERVE:
+ printk("UTIL_DEVICE_RESERVE, ");
+ break;
+ case I2O_CMD_UTIL_DEVICE_RELEASE:
+ printk("UTIL_DEVICE_RELEASE, ");
+ break;
+ case I2O_CMD_UTIL_EVT_ACK:
+ printk("UTIL_EVENT_ACKNOWLEDGE, ");
+ break;
+ case I2O_CMD_UTIL_EVT_REGISTER:
+ printk("UTIL_EVENT_REGISTER, ");
+ break;
+ case I2O_CMD_UTIL_LOCK:
+ printk("UTIL_LOCK, ");
+ break;
+ case I2O_CMD_UTIL_LOCK_RELEASE:
+ printk("UTIL_LOCK_RELEASE, ");
+ break;
+ case I2O_CMD_UTIL_PARAMS_GET:
+ printk("UTIL_PARAMS_GET, ");
+ break;
+ case I2O_CMD_UTIL_PARAMS_SET:
+ printk("UTIL_PARAMS_SET, ");
+ break;
+ case I2O_CMD_UTIL_REPLY_FAULT_NOTIFY:
+ printk("UTIL_REPLY_FAULT_NOTIFY, ");
+ break;
+ default:
+ printk("Cmd = %0#2x, ", cmd);
+ }
+}
+
+/*
+ * Used for error reporting/debugging purposes
+ */
+static void i2o_report_exec_cmd(u8 cmd)
+{
+ switch (cmd) {
+ case I2O_CMD_ADAPTER_ASSIGN:
+ printk("EXEC_ADAPTER_ASSIGN, ");
+ break;
+ case I2O_CMD_ADAPTER_READ:
+ printk("EXEC_ADAPTER_READ, ");
+ break;
+ case I2O_CMD_ADAPTER_RELEASE:
+ printk("EXEC_ADAPTER_RELEASE, ");
+ break;
+ case I2O_CMD_BIOS_INFO_SET:
+ printk("EXEC_BIOS_INFO_SET, ");
+ break;
+ case I2O_CMD_BOOT_DEVICE_SET:
+ printk("EXEC_BOOT_DEVICE_SET, ");
+ break;
+ case I2O_CMD_CONFIG_VALIDATE:
+ printk("EXEC_CONFIG_VALIDATE, ");
+ break;
+ case I2O_CMD_CONN_SETUP:
+ printk("EXEC_CONN_SETUP, ");
+ break;
+ case I2O_CMD_DDM_DESTROY:
+ printk("EXEC_DDM_DESTROY, ");
+ break;
+ case I2O_CMD_DDM_ENABLE:
+ printk("EXEC_DDM_ENABLE, ");
+ break;
+ case I2O_CMD_DDM_QUIESCE:
+ printk("EXEC_DDM_QUIESCE, ");
+ break;
+ case I2O_CMD_DDM_RESET:
+ printk("EXEC_DDM_RESET, ");
+ break;
+ case I2O_CMD_DDM_SUSPEND:
+ printk("EXEC_DDM_SUSPEND, ");
+ break;
+ case I2O_CMD_DEVICE_ASSIGN:
+ printk("EXEC_DEVICE_ASSIGN, ");
+ break;
+ case I2O_CMD_DEVICE_RELEASE:
+ printk("EXEC_DEVICE_RELEASE, ");
+ break;
+ case I2O_CMD_HRT_GET:
+ printk("EXEC_HRT_GET, ");
+ break;
+ case I2O_CMD_ADAPTER_CLEAR:
+ printk("EXEC_IOP_CLEAR, ");
+ break;
+ case I2O_CMD_ADAPTER_CONNECT:
+ printk("EXEC_IOP_CONNECT, ");
+ break;
+ case I2O_CMD_ADAPTER_RESET:
+ printk("EXEC_IOP_RESET, ");
+ break;
+ case I2O_CMD_LCT_NOTIFY:
+ printk("EXEC_LCT_NOTIFY, ");
+ break;
+ case I2O_CMD_OUTBOUND_INIT:
+ printk("EXEC_OUTBOUND_INIT, ");
+ break;
+ case I2O_CMD_PATH_ENABLE:
+ printk("EXEC_PATH_ENABLE, ");
+ break;
+ case I2O_CMD_PATH_QUIESCE:
+ printk("EXEC_PATH_QUIESCE, ");
+ break;
+ case I2O_CMD_PATH_RESET:
+ printk("EXEC_PATH_RESET, ");
+ break;
+ case I2O_CMD_STATIC_MF_CREATE:
+ printk("EXEC_STATIC_MF_CREATE, ");
+ break;
+ case I2O_CMD_STATIC_MF_RELEASE:
+ printk("EXEC_STATIC_MF_RELEASE, ");
+ break;
+ case I2O_CMD_STATUS_GET:
+ printk("EXEC_STATUS_GET, ");
+ break;
+ case I2O_CMD_SW_DOWNLOAD:
+ printk("EXEC_SW_DOWNLOAD, ");
+ break;
+ case I2O_CMD_SW_UPLOAD:
+ printk("EXEC_SW_UPLOAD, ");
+ break;
+ case I2O_CMD_SW_REMOVE:
+ printk("EXEC_SW_REMOVE, ");
+ break;
+ case I2O_CMD_SYS_ENABLE:
+ printk("EXEC_SYS_ENABLE, ");
+ break;
+ case I2O_CMD_SYS_MODIFY:
+ printk("EXEC_SYS_MODIFY, ");
+ break;
+ case I2O_CMD_SYS_QUIESCE:
+ printk("EXEC_SYS_QUIESCE, ");
+ break;
+ case I2O_CMD_SYS_TAB_SET:
+ printk("EXEC_SYS_TAB_SET, ");
+ break;
+ default:
+ printk("Cmd = %#02x, ", cmd);
+ }
+}
+
+void i2o_debug_state(struct i2o_controller *c)
+{
+ printk(KERN_INFO "%s: State = ", c->name);
+ switch (((i2o_status_block *) c->status_block.virt)->iop_state) {
+ case 0x01:
+ printk("INIT\n");
+ break;
+ case 0x02:
+ printk("RESET\n");
+ break;
+ case 0x04:
+ printk("HOLD\n");
+ break;
+ case 0x05:
+ printk("READY\n");
+ break;
+ case 0x08:
+ printk("OPERATIONAL\n");
+ break;
+ case 0x10:
+ printk("FAILED\n");
+ break;
+ case 0x11:
+ printk("FAULTED\n");
+ break;
+ default:
+ printk("%x (unknown !!)\n",
+ ((i2o_status_block *) c->status_block.virt)->iop_state);
+ }
+};
+
+void i2o_dump_hrt(struct i2o_controller *c)
+{
+ u32 *rows = (u32 *) c->hrt.virt;
+ u8 *p = (u8 *) c->hrt.virt;
+ u8 *d;
+ int count;
+ int length;
+ int i;
+ int state;
+
+ if (p[3] != 0) {
+ printk(KERN_ERR
+ "%s: HRT table for controller is too new a version.\n",
+ c->name);
+ return;
+ }
+
+ count = p[0] | (p[1] << 8);
+ length = p[2];
+
+ printk(KERN_INFO "%s: HRT has %d entries of %d bytes each.\n",
+ c->name, count, length << 2);
+
+ rows += 2;
+
+ for (i = 0; i < count; i++) {
+ printk(KERN_INFO "Adapter %08X: ", rows[0]);
+ p = (u8 *) (rows + 1);
+ d = (u8 *) (rows + 2);
+ state = p[1] << 8 | p[0];
+
+ printk("TID %04X:[", state & 0xFFF);
+ state >>= 12;
+ if (state & (1 << 0))
+ printk("H"); /* Hidden */
+ if (state & (1 << 2)) {
+ printk("P"); /* Present */
+ if (state & (1 << 1))
+ printk("C"); /* Controlled */
+ }
+ if (state > 9)
+ printk("*"); /* Hard */
+
+ printk("]:");
+
+ switch (p[3] & 0xFFFF) {
+ case 0:
+ /* Adapter private bus - easy */
+ printk("Local bus %d: I/O at 0x%04X Mem 0x%08X", p[2],
+ d[1] << 8 | d[0], *(u32 *) (d + 4));
+ break;
+ case 1:
+ /* ISA bus */
+ printk("ISA %d: CSN %d I/O at 0x%04X Mem 0x%08X", p[2],
+ d[2], d[1] << 8 | d[0], *(u32 *) (d + 4));
+ break;
+
+ case 2: /* EISA bus */
+ printk("EISA %d: Slot %d I/O at 0x%04X Mem 0x%08X",
+ p[2], d[3], d[1] << 8 | d[0], *(u32 *) (d + 4));
+ break;
+
+ case 3: /* MCA bus */
+ printk("MCA %d: Slot %d I/O at 0x%04X Mem 0x%08X", p[2],
+ d[3], d[1] << 8 | d[0], *(u32 *) (d + 4));
+ break;
+
+ case 4: /* PCI bus */
+ printk("PCI %d: Bus %d Device %d Function %d", p[2],
+ d[2], d[1], d[0]);
+ break;
+
+ case 0x80: /* Other */
+ default:
+ printk("Unsupported bus type.");
+ break;
+ }
+ printk("\n");
+ rows += length;
+ }
+}
+
+EXPORT_SYMBOL(i2o_dump_message);
diff --git a/drivers/staging/i2o/device.c b/drivers/staging/i2o/device.c
new file mode 100644
index 000000000..e47496cb0
--- /dev/null
+++ b/drivers/staging/i2o/device.c
@@ -0,0 +1,592 @@
+/*
+ * Functions to handle I2O devices
+ *
+ * Copyright (C) 2004 Markus Lidel <Markus.Lidel@shadowconnect.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.
+ *
+ * Fixes/additions:
+ * Markus Lidel <Markus.Lidel@shadowconnect.com>
+ * initial version.
+ */
+
+#include <linux/module.h>
+#include "i2o.h"
+#include <linux/delay.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+#include "core.h"
+
+/**
+ * i2o_device_issue_claim - claim or release a device
+ * @dev: I2O device to claim or release
+ * @cmd: claim or release command
+ * @type: type of claim
+ *
+ * Issue I2O UTIL_CLAIM or UTIL_RELEASE messages. The message to be sent
+ * is set by cmd. dev is the I2O device which should be claim or
+ * released and the type is the claim type (see the I2O spec).
+ *
+ * Returs 0 on success or negative error code on failure.
+ */
+static inline int i2o_device_issue_claim(struct i2o_device *dev, u32 cmd,
+ u32 type)
+{
+ struct i2o_message *msg;
+
+ msg = i2o_msg_get_wait(dev->iop, I2O_TIMEOUT_MESSAGE_GET);
+ if (IS_ERR(msg))
+ return PTR_ERR(msg);
+
+ msg->u.head[0] = cpu_to_le32(FIVE_WORD_MSG_SIZE | SGL_OFFSET_0);
+ msg->u.head[1] =
+ cpu_to_le32(cmd << 24 | HOST_TID << 12 | dev->lct_data.tid);
+ msg->body[0] = cpu_to_le32(type);
+
+ return i2o_msg_post_wait(dev->iop, msg, 60);
+}
+
+/**
+ * i2o_device_claim - claim a device for use by an OSM
+ * @dev: I2O device to claim
+ *
+ * Do the leg work to assign a device to a given OSM. If the claim succeeds,
+ * the owner is the primary. If the attempt fails a negative errno code
+ * is returned. On success zero is returned.
+ */
+int i2o_device_claim(struct i2o_device *dev)
+{
+ int rc = 0;
+
+ mutex_lock(&dev->lock);
+
+ rc = i2o_device_issue_claim(dev, I2O_CMD_UTIL_CLAIM, I2O_CLAIM_PRIMARY);
+ if (!rc)
+ pr_debug("i2o: claim of device %d succeeded\n",
+ dev->lct_data.tid);
+ else
+ pr_debug("i2o: claim of device %d failed %d\n",
+ dev->lct_data.tid, rc);
+
+ mutex_unlock(&dev->lock);
+
+ return rc;
+}
+
+/**
+ * i2o_device_claim_release - release a device that the OSM is using
+ * @dev: device to release
+ *
+ * Drop a claim by an OSM on a given I2O device.
+ *
+ * AC - some devices seem to want to refuse an unclaim until they have
+ * finished internal processing. It makes sense since you don't want a
+ * new device to go reconfiguring the entire system until you are done.
+ * Thus we are prepared to wait briefly.
+ *
+ * Returns 0 on success or negative error code on failure.
+ */
+int i2o_device_claim_release(struct i2o_device *dev)
+{
+ int tries;
+ int rc = 0;
+
+ mutex_lock(&dev->lock);
+
+ /*
+ * If the controller takes a nonblocking approach to
+ * releases we have to sleep/poll for a few times.
+ */
+ for (tries = 0; tries < 10; tries++) {
+ rc = i2o_device_issue_claim(dev, I2O_CMD_UTIL_RELEASE,
+ I2O_CLAIM_PRIMARY);
+ if (!rc)
+ break;
+
+ ssleep(1);
+ }
+
+ if (!rc)
+ pr_debug("i2o: claim release of device %d succeeded\n",
+ dev->lct_data.tid);
+ else
+ pr_debug("i2o: claim release of device %d failed %d\n",
+ dev->lct_data.tid, rc);
+
+ mutex_unlock(&dev->lock);
+
+ return rc;
+}
+
+/**
+ * i2o_device_release - release the memory for a I2O device
+ * @dev: I2O device which should be released
+ *
+ * Release the allocated memory. This function is called if refcount of
+ * device reaches 0 automatically.
+ */
+static void i2o_device_release(struct device *dev)
+{
+ struct i2o_device *i2o_dev = to_i2o_device(dev);
+
+ pr_debug("i2o: device %s released\n", dev_name(dev));
+
+ kfree(i2o_dev);
+}
+
+/**
+ * class_id_show - Displays class id of I2O device
+ * @dev: device of which the class id should be displayed
+ * @attr: pointer to device attribute
+ * @buf: buffer into which the class id should be printed
+ *
+ * Returns the number of bytes which are printed into the buffer.
+ */
+static ssize_t class_id_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct i2o_device *i2o_dev = to_i2o_device(dev);
+
+ sprintf(buf, "0x%03x\n", i2o_dev->lct_data.class_id);
+ return strlen(buf) + 1;
+}
+static DEVICE_ATTR_RO(class_id);
+
+/**
+ * tid_show - Displays TID of I2O device
+ * @dev: device of which the TID should be displayed
+ * @attr: pointer to device attribute
+ * @buf: buffer into which the TID should be printed
+ *
+ * Returns the number of bytes which are printed into the buffer.
+ */
+static ssize_t tid_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct i2o_device *i2o_dev = to_i2o_device(dev);
+
+ sprintf(buf, "0x%03x\n", i2o_dev->lct_data.tid);
+ return strlen(buf) + 1;
+}
+static DEVICE_ATTR_RO(tid);
+
+/* I2O device attributes */
+static struct attribute *i2o_device_attrs[] = {
+ &dev_attr_class_id.attr,
+ &dev_attr_tid.attr,
+ NULL,
+};
+
+static const struct attribute_group i2o_device_group = {
+ .attrs = i2o_device_attrs,
+};
+
+const struct attribute_group *i2o_device_groups[] = {
+ &i2o_device_group,
+ NULL,
+};
+
+/**
+ * i2o_device_alloc - Allocate a I2O device and initialize it
+ *
+ * Allocate the memory for a I2O device and initialize locks and lists
+ *
+ * Returns the allocated I2O device or a negative error code if the device
+ * could not be allocated.
+ */
+static struct i2o_device *i2o_device_alloc(void)
+{
+ struct i2o_device *dev;
+
+ dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+ if (!dev)
+ return ERR_PTR(-ENOMEM);
+
+ INIT_LIST_HEAD(&dev->list);
+ mutex_init(&dev->lock);
+
+ dev->device.bus = &i2o_bus_type;
+ dev->device.release = &i2o_device_release;
+
+ return dev;
+}
+
+/**
+ * i2o_device_add - allocate a new I2O device and add it to the IOP
+ * @c: I2O controller that the device is on
+ * @entry: LCT entry of the I2O device
+ *
+ * Allocate a new I2O device and initialize it with the LCT entry. The
+ * device is appended to the device list of the controller.
+ *
+ * Returns zero on success, or a -ve errno.
+ */
+static int i2o_device_add(struct i2o_controller *c, i2o_lct_entry *entry)
+{
+ struct i2o_device *i2o_dev, *tmp;
+ int rc;
+
+ i2o_dev = i2o_device_alloc();
+ if (IS_ERR(i2o_dev)) {
+ printk(KERN_ERR "i2o: unable to allocate i2o device\n");
+ return PTR_ERR(i2o_dev);
+ }
+
+ i2o_dev->lct_data = *entry;
+
+ dev_set_name(&i2o_dev->device, "%d:%03x", c->unit,
+ i2o_dev->lct_data.tid);
+
+ i2o_dev->iop = c;
+ i2o_dev->device.parent = &c->device;
+
+ rc = device_register(&i2o_dev->device);
+ if (rc)
+ goto err;
+
+ list_add_tail(&i2o_dev->list, &c->devices);
+
+ /* create user entries for this device */
+ tmp = i2o_iop_find_device(i2o_dev->iop, i2o_dev->lct_data.user_tid);
+ if (tmp && (tmp != i2o_dev)) {
+ rc = sysfs_create_link(&i2o_dev->device.kobj,
+ &tmp->device.kobj, "user");
+ if (rc)
+ goto unreg_dev;
+ }
+
+ /* create user entries referring to this device */
+ list_for_each_entry(tmp, &c->devices, list)
+ if ((tmp->lct_data.user_tid == i2o_dev->lct_data.tid)
+ && (tmp != i2o_dev)) {
+ rc = sysfs_create_link(&tmp->device.kobj,
+ &i2o_dev->device.kobj, "user");
+ if (rc)
+ goto rmlink1;
+ }
+
+ /* create parent entries for this device */
+ tmp = i2o_iop_find_device(i2o_dev->iop, i2o_dev->lct_data.parent_tid);
+ if (tmp && (tmp != i2o_dev)) {
+ rc = sysfs_create_link(&i2o_dev->device.kobj,
+ &tmp->device.kobj, "parent");
+ if (rc)
+ goto rmlink1;
+ }
+
+ /* create parent entries referring to this device */
+ list_for_each_entry(tmp, &c->devices, list)
+ if ((tmp->lct_data.parent_tid == i2o_dev->lct_data.tid)
+ && (tmp != i2o_dev)) {
+ rc = sysfs_create_link(&tmp->device.kobj,
+ &i2o_dev->device.kobj, "parent");
+ if (rc)
+ goto rmlink2;
+ }
+
+ i2o_driver_notify_device_add_all(i2o_dev);
+
+ pr_debug("i2o: device %s added\n", dev_name(&i2o_dev->device));
+
+ return 0;
+
+rmlink2:
+ /* If link creating failed halfway, we loop whole list to cleanup.
+ * And we don't care wrong removing of link, because sysfs_remove_link
+ * will take care of it.
+ */
+ list_for_each_entry(tmp, &c->devices, list) {
+ if (tmp->lct_data.parent_tid == i2o_dev->lct_data.tid)
+ sysfs_remove_link(&tmp->device.kobj, "parent");
+ }
+ sysfs_remove_link(&i2o_dev->device.kobj, "parent");
+rmlink1:
+ list_for_each_entry(tmp, &c->devices, list)
+ if (tmp->lct_data.user_tid == i2o_dev->lct_data.tid)
+ sysfs_remove_link(&tmp->device.kobj, "user");
+ sysfs_remove_link(&i2o_dev->device.kobj, "user");
+unreg_dev:
+ list_del(&i2o_dev->list);
+ device_unregister(&i2o_dev->device);
+err:
+ kfree(i2o_dev);
+ return rc;
+}
+
+/**
+ * i2o_device_remove - remove an I2O device from the I2O core
+ * @i2o_dev: I2O device which should be released
+ *
+ * Is used on I2O controller removal or LCT modification, when the device
+ * is removed from the system. Note that the device could still hang
+ * around until the refcount reaches 0.
+ */
+void i2o_device_remove(struct i2o_device *i2o_dev)
+{
+ struct i2o_device *tmp;
+ struct i2o_controller *c = i2o_dev->iop;
+
+ i2o_driver_notify_device_remove_all(i2o_dev);
+
+ sysfs_remove_link(&i2o_dev->device.kobj, "parent");
+ sysfs_remove_link(&i2o_dev->device.kobj, "user");
+
+ list_for_each_entry(tmp, &c->devices, list) {
+ if (tmp->lct_data.parent_tid == i2o_dev->lct_data.tid)
+ sysfs_remove_link(&tmp->device.kobj, "parent");
+ if (tmp->lct_data.user_tid == i2o_dev->lct_data.tid)
+ sysfs_remove_link(&tmp->device.kobj, "user");
+ }
+ list_del(&i2o_dev->list);
+
+ device_unregister(&i2o_dev->device);
+}
+
+/**
+ * i2o_device_parse_lct - Parse a previously fetched LCT and create devices
+ * @c: I2O controller from which the LCT should be parsed.
+ *
+ * The Logical Configuration Table tells us what we can talk to on the
+ * board. For every entry we create an I2O device, which is registered in
+ * the I2O core.
+ *
+ * Returns 0 on success or negative error code on failure.
+ */
+int i2o_device_parse_lct(struct i2o_controller *c)
+{
+ struct i2o_device *dev, *tmp;
+ i2o_lct *lct;
+ u32 *dlct = c->dlct.virt;
+ int max = 0, i = 0;
+ u16 table_size;
+ u32 buf;
+
+ mutex_lock(&c->lct_lock);
+
+ kfree(c->lct);
+
+ buf = le32_to_cpu(*dlct++);
+ table_size = buf & 0xffff;
+
+ lct = c->lct = kmalloc(table_size * 4, GFP_KERNEL);
+ if (!lct) {
+ mutex_unlock(&c->lct_lock);
+ return -ENOMEM;
+ }
+
+ lct->lct_ver = buf >> 28;
+ lct->boot_tid = buf >> 16 & 0xfff;
+ lct->table_size = table_size;
+ lct->change_ind = le32_to_cpu(*dlct++);
+ lct->iop_flags = le32_to_cpu(*dlct++);
+
+ table_size -= 3;
+
+ pr_debug("%s: LCT has %d entries (LCT size: %d)\n", c->name, max,
+ lct->table_size);
+
+ while (table_size > 0) {
+ i2o_lct_entry *entry = &lct->lct_entry[max];
+ int found = 0;
+
+ buf = le32_to_cpu(*dlct++);
+ entry->entry_size = buf & 0xffff;
+ entry->tid = buf >> 16 & 0xfff;
+
+ entry->change_ind = le32_to_cpu(*dlct++);
+ entry->device_flags = le32_to_cpu(*dlct++);
+
+ buf = le32_to_cpu(*dlct++);
+ entry->class_id = buf & 0xfff;
+ entry->version = buf >> 12 & 0xf;
+ entry->vendor_id = buf >> 16;
+
+ entry->sub_class = le32_to_cpu(*dlct++);
+
+ buf = le32_to_cpu(*dlct++);
+ entry->user_tid = buf & 0xfff;
+ entry->parent_tid = buf >> 12 & 0xfff;
+ entry->bios_info = buf >> 24;
+
+ memcpy(&entry->identity_tag, dlct, 8);
+ dlct += 2;
+
+ entry->event_capabilities = le32_to_cpu(*dlct++);
+
+ /* add new devices, which are new in the LCT */
+ list_for_each_entry_safe(dev, tmp, &c->devices, list) {
+ if (entry->tid == dev->lct_data.tid) {
+ found = 1;
+ break;
+ }
+ }
+
+ if (!found)
+ i2o_device_add(c, entry);
+
+ table_size -= 9;
+ max++;
+ }
+
+ /* remove devices, which are not in the LCT anymore */
+ list_for_each_entry_safe(dev, tmp, &c->devices, list) {
+ int found = 0;
+
+ for (i = 0; i < max; i++) {
+ if (lct->lct_entry[i].tid == dev->lct_data.tid) {
+ found = 1;
+ break;
+ }
+ }
+
+ if (!found)
+ i2o_device_remove(dev);
+ }
+
+ mutex_unlock(&c->lct_lock);
+
+ return 0;
+}
+
+/*
+ * Run time support routines
+ */
+
+/* Issue UTIL_PARAMS_GET or UTIL_PARAMS_SET
+ *
+ * This function can be used for all UtilParamsGet/Set operations.
+ * The OperationList is given in oplist-buffer,
+ * and results are returned in reslist-buffer.
+ * Note that the minimum sized reslist is 8 bytes and contains
+ * ResultCount, ErrorInfoSize, BlockStatus and BlockSize.
+ */
+int i2o_parm_issue(struct i2o_device *i2o_dev, int cmd, void *oplist,
+ int oplen, void *reslist, int reslen)
+{
+ struct i2o_message *msg;
+ int i = 0;
+ int rc;
+ struct i2o_dma res;
+ struct i2o_controller *c = i2o_dev->iop;
+ struct device *dev = &c->pdev->dev;
+
+ res.virt = NULL;
+
+ if (i2o_dma_alloc(dev, &res, reslen))
+ return -ENOMEM;
+
+ msg = i2o_msg_get_wait(c, I2O_TIMEOUT_MESSAGE_GET);
+ if (IS_ERR(msg)) {
+ i2o_dma_free(dev, &res);
+ return PTR_ERR(msg);
+ }
+
+ i = 0;
+ msg->u.head[1] =
+ cpu_to_le32(cmd << 24 | HOST_TID << 12 | i2o_dev->lct_data.tid);
+ msg->body[i++] = cpu_to_le32(0x00000000);
+ msg->body[i++] = cpu_to_le32(0x4C000000 | oplen); /* OperationList */
+ memcpy(&msg->body[i], oplist, oplen);
+ i += (oplen / 4 + (oplen % 4 ? 1 : 0));
+ msg->body[i++] = cpu_to_le32(0xD0000000 | res.len); /* ResultList */
+ msg->body[i++] = cpu_to_le32(res.phys);
+
+ msg->u.head[0] =
+ cpu_to_le32(I2O_MESSAGE_SIZE(i + sizeof(struct i2o_message) / 4) |
+ SGL_OFFSET_5);
+
+ rc = i2o_msg_post_wait_mem(c, msg, 10, &res);
+
+ /* This only looks like a memory leak - don't "fix" it. */
+ if (rc == -ETIMEDOUT)
+ return rc;
+
+ memcpy(reslist, res.virt, res.len);
+ i2o_dma_free(dev, &res);
+
+ return rc;
+}
+
+/*
+ * Query one field group value or a whole scalar group.
+ */
+int i2o_parm_field_get(struct i2o_device *i2o_dev, int group, int field,
+ void *buf, int buflen)
+{
+ u32 opblk[] = { cpu_to_le32(0x00000001),
+ cpu_to_le32((u16) group << 16 | I2O_PARAMS_FIELD_GET),
+ cpu_to_le32((s16) field << 16 | 0x00000001)
+ };
+ u8 *resblk; /* 8 bytes for header */
+ int rc;
+
+ resblk = kmalloc(buflen + 8, GFP_KERNEL);
+ if (!resblk)
+ return -ENOMEM;
+
+ rc = i2o_parm_issue(i2o_dev, I2O_CMD_UTIL_PARAMS_GET, opblk,
+ sizeof(opblk), resblk, buflen + 8);
+
+ memcpy(buf, resblk + 8, buflen); /* cut off header */
+
+ kfree(resblk);
+
+ return rc;
+}
+
+/*
+ * if oper == I2O_PARAMS_TABLE_GET, get from all rows
+ * if fieldcount == -1 return all fields
+ * ibuf and ibuflen are unused (use NULL, 0)
+ * else return specific fields
+ * ibuf contains fieldindexes
+ *
+ * if oper == I2O_PARAMS_LIST_GET, get from specific rows
+ * if fieldcount == -1 return all fields
+ * ibuf contains rowcount, keyvalues
+ * else return specific fields
+ * fieldcount is # of fieldindexes
+ * ibuf contains fieldindexes, rowcount, keyvalues
+ *
+ * You could also use directly function i2o_issue_params().
+ */
+int i2o_parm_table_get(struct i2o_device *dev, int oper, int group,
+ int fieldcount, void *ibuf, int ibuflen, void *resblk,
+ int reslen)
+{
+ u16 *opblk;
+ int size;
+
+ size = 10 + ibuflen;
+ if (size % 4)
+ size += 4 - size % 4;
+
+ opblk = kmalloc(size, GFP_KERNEL);
+ if (opblk == NULL)
+ return -ENOMEM;
+
+ opblk[0] = 1; /* operation count */
+ opblk[1] = 0; /* pad */
+ opblk[2] = oper;
+ opblk[3] = group;
+ opblk[4] = fieldcount;
+ memcpy(opblk + 5, ibuf, ibuflen); /* other params */
+
+ size = i2o_parm_issue(dev, I2O_CMD_UTIL_PARAMS_GET, opblk,
+ size, resblk, reslen);
+
+ kfree(opblk);
+ if (size > reslen)
+ return reslen;
+
+ return size;
+}
+
+EXPORT_SYMBOL(i2o_device_claim);
+EXPORT_SYMBOL(i2o_device_claim_release);
+EXPORT_SYMBOL(i2o_parm_field_get);
+EXPORT_SYMBOL(i2o_parm_table_get);
+EXPORT_SYMBOL(i2o_parm_issue);
diff --git a/drivers/staging/i2o/driver.c b/drivers/staging/i2o/driver.c
new file mode 100644
index 000000000..06119bb3e
--- /dev/null
+++ b/drivers/staging/i2o/driver.c
@@ -0,0 +1,381 @@
+/*
+ * Functions to handle I2O drivers (OSMs) and I2O bus type for sysfs
+ *
+ * Copyright (C) 2004 Markus Lidel <Markus.Lidel@shadowconnect.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.
+ *
+ * Fixes/additions:
+ * Markus Lidel <Markus.Lidel@shadowconnect.com>
+ * initial version.
+ */
+
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/rwsem.h>
+#include "i2o.h"
+#include <linux/workqueue.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+#include "core.h"
+
+#define OSM_NAME "i2o"
+
+/* max_drivers - Maximum I2O drivers (OSMs) which could be registered */
+static unsigned int i2o_max_drivers = I2O_MAX_DRIVERS;
+module_param_named(max_drivers, i2o_max_drivers, uint, 0);
+MODULE_PARM_DESC(max_drivers, "maximum number of OSM's to support");
+
+/* I2O drivers lock and array */
+static spinlock_t i2o_drivers_lock;
+static struct i2o_driver **i2o_drivers;
+
+/**
+ * i2o_bus_match - Tell if I2O device class id matches the class ids of the I2O driver (OSM)
+ * @dev: device which should be verified
+ * @drv: the driver to match against
+ *
+ * Used by the bus to check if the driver wants to handle the device.
+ *
+ * Returns 1 if the class ids of the driver match the class id of the
+ * device, otherwise 0.
+ */
+static int i2o_bus_match(struct device *dev, struct device_driver *drv)
+{
+ struct i2o_device *i2o_dev = to_i2o_device(dev);
+ struct i2o_driver *i2o_drv = to_i2o_driver(drv);
+ struct i2o_class_id *ids = i2o_drv->classes;
+
+ if (ids)
+ while (ids->class_id != I2O_CLASS_END) {
+ if (ids->class_id == i2o_dev->lct_data.class_id)
+ return 1;
+ ids++;
+ }
+ return 0;
+};
+
+/* I2O bus type */
+struct bus_type i2o_bus_type = {
+ .name = "i2o",
+ .match = i2o_bus_match,
+ .dev_groups = i2o_device_groups,
+};
+
+/**
+ * i2o_driver_register - Register a I2O driver (OSM) in the I2O core
+ * @drv: I2O driver which should be registered
+ *
+ * Registers the OSM drv in the I2O core and creates an event queues if
+ * necessary.
+ *
+ * Returns 0 on success or negative error code on failure.
+ */
+int i2o_driver_register(struct i2o_driver *drv)
+{
+ struct i2o_controller *c;
+ int i;
+ int rc = 0;
+ unsigned long flags;
+
+ osm_debug("Register driver %s\n", drv->name);
+
+ if (drv->event) {
+ drv->event_queue = alloc_workqueue("%s", WQ_MEM_RECLAIM, 1,
+ drv->name);
+ if (!drv->event_queue) {
+ osm_err("Could not initialize event queue for driver "
+ "%s\n", drv->name);
+ return -EFAULT;
+ }
+ osm_debug("Event queue initialized for driver %s\n", drv->name);
+ } else
+ drv->event_queue = NULL;
+
+ drv->driver.name = drv->name;
+ drv->driver.bus = &i2o_bus_type;
+
+ spin_lock_irqsave(&i2o_drivers_lock, flags);
+
+ for (i = 0; i2o_drivers[i]; i++)
+ if (i >= i2o_max_drivers) {
+ osm_err("too many drivers registered, increase max_drivers\n");
+ spin_unlock_irqrestore(&i2o_drivers_lock, flags);
+ rc = -EFAULT;
+ goto out;
+ }
+
+ drv->context = i;
+ i2o_drivers[i] = drv;
+
+ spin_unlock_irqrestore(&i2o_drivers_lock, flags);
+
+ osm_debug("driver %s gets context id %d\n", drv->name, drv->context);
+
+ list_for_each_entry(c, &i2o_controllers, list) {
+ struct i2o_device *i2o_dev;
+
+ i2o_driver_notify_controller_add(drv, c);
+ list_for_each_entry(i2o_dev, &c->devices, list)
+ i2o_driver_notify_device_add(drv, i2o_dev);
+ }
+
+ rc = driver_register(&drv->driver);
+ if (rc)
+ goto out;
+
+ return 0;
+out:
+ if (drv->event_queue) {
+ destroy_workqueue(drv->event_queue);
+ drv->event_queue = NULL;
+ }
+
+ return rc;
+};
+
+/**
+ * i2o_driver_unregister - Unregister a I2O driver (OSM) from the I2O core
+ * @drv: I2O driver which should be unregistered
+ *
+ * Unregisters the OSM drv from the I2O core and cleanup event queues if
+ * necessary.
+ */
+void i2o_driver_unregister(struct i2o_driver *drv)
+{
+ struct i2o_controller *c;
+ unsigned long flags;
+
+ osm_debug("unregister driver %s\n", drv->name);
+
+ driver_unregister(&drv->driver);
+
+ list_for_each_entry(c, &i2o_controllers, list) {
+ struct i2o_device *i2o_dev;
+
+ list_for_each_entry(i2o_dev, &c->devices, list)
+ i2o_driver_notify_device_remove(drv, i2o_dev);
+
+ i2o_driver_notify_controller_remove(drv, c);
+ }
+
+ spin_lock_irqsave(&i2o_drivers_lock, flags);
+ i2o_drivers[drv->context] = NULL;
+ spin_unlock_irqrestore(&i2o_drivers_lock, flags);
+
+ if (drv->event_queue) {
+ destroy_workqueue(drv->event_queue);
+ drv->event_queue = NULL;
+ osm_debug("event queue removed for %s\n", drv->name);
+ }
+};
+
+/**
+ * i2o_driver_dispatch - dispatch an I2O reply message
+ * @c: I2O controller of the message
+ * @m: I2O message number
+ *
+ * The reply is delivered to the driver from which the original message
+ * was. This function is only called from interrupt context.
+ *
+ * Returns 0 on success and the message should not be flushed. Returns > 0
+ * on success and if the message should be flushed afterwords. Returns
+ * negative error code on failure (the message will be flushed too).
+ */
+int i2o_driver_dispatch(struct i2o_controller *c, u32 m)
+{
+ struct i2o_driver *drv;
+ struct i2o_message *msg = i2o_msg_out_to_virt(c, m);
+ u32 context = le32_to_cpu(msg->u.s.icntxt);
+ unsigned long flags;
+
+ if (unlikely(context >= i2o_max_drivers)) {
+ osm_warn("%s: Spurious reply to unknown driver %d\n", c->name,
+ context);
+ return -EIO;
+ }
+
+ spin_lock_irqsave(&i2o_drivers_lock, flags);
+ drv = i2o_drivers[context];
+ spin_unlock_irqrestore(&i2o_drivers_lock, flags);
+
+ if (unlikely(!drv)) {
+ osm_warn("%s: Spurious reply to unknown driver %d\n", c->name,
+ context);
+ return -EIO;
+ }
+
+ if ((le32_to_cpu(msg->u.head[1]) >> 24) == I2O_CMD_UTIL_EVT_REGISTER) {
+ struct i2o_device *dev, *tmp;
+ struct i2o_event *evt;
+ u16 size;
+ u16 tid = le32_to_cpu(msg->u.head[1]) & 0xfff;
+
+ osm_debug("event received from device %d\n", tid);
+
+ if (!drv->event)
+ return -EIO;
+
+ /* cut of header from message size (in 32-bit words) */
+ size = (le32_to_cpu(msg->u.head[0]) >> 16) - 5;
+
+ evt = kzalloc(size * 4 + sizeof(*evt), GFP_ATOMIC);
+ if (!evt)
+ return -ENOMEM;
+
+ evt->size = size;
+ evt->tcntxt = le32_to_cpu(msg->u.s.tcntxt);
+ evt->event_indicator = le32_to_cpu(msg->body[0]);
+ memcpy(&evt->data, &msg->body[1], size * 4);
+
+ list_for_each_entry_safe(dev, tmp, &c->devices, list)
+ if (dev->lct_data.tid == tid) {
+ evt->i2o_dev = dev;
+ break;
+ }
+
+ INIT_WORK(&evt->work, drv->event);
+ queue_work(drv->event_queue, &evt->work);
+ return 1;
+ }
+
+ if (unlikely(!drv->reply)) {
+ osm_debug("%s: Reply to driver %s, but no reply function defined!\n",
+ c->name, drv->name);
+ return -EIO;
+ }
+
+ return drv->reply(c, m, msg);
+}
+
+/**
+ * i2o_driver_notify_controller_add_all - Send notify of added controller
+ * @c: newly added controller
+ *
+ * Send notifications to all registered drivers that a new controller was
+ * added.
+ */
+void i2o_driver_notify_controller_add_all(struct i2o_controller *c)
+{
+ int i;
+ struct i2o_driver *drv;
+
+ for (i = 0; i < i2o_max_drivers; i++) {
+ drv = i2o_drivers[i];
+
+ if (drv)
+ i2o_driver_notify_controller_add(drv, c);
+ }
+}
+
+/**
+ * i2o_driver_notify_controller_remove_all - Send notify of removed controller
+ * @c: controller that is being removed
+ *
+ * Send notifications to all registered drivers that a controller was
+ * removed.
+ */
+void i2o_driver_notify_controller_remove_all(struct i2o_controller *c)
+{
+ int i;
+ struct i2o_driver *drv;
+
+ for (i = 0; i < i2o_max_drivers; i++) {
+ drv = i2o_drivers[i];
+
+ if (drv)
+ i2o_driver_notify_controller_remove(drv, c);
+ }
+}
+
+/**
+ * i2o_driver_notify_device_add_all - Send notify of added device
+ * @i2o_dev: newly added I2O device
+ *
+ * Send notifications to all registered drivers that a device was added.
+ */
+void i2o_driver_notify_device_add_all(struct i2o_device *i2o_dev)
+{
+ int i;
+ struct i2o_driver *drv;
+
+ for (i = 0; i < i2o_max_drivers; i++) {
+ drv = i2o_drivers[i];
+
+ if (drv)
+ i2o_driver_notify_device_add(drv, i2o_dev);
+ }
+}
+
+/**
+ * i2o_driver_notify_device_remove_all - Send notify of removed device
+ * @i2o_dev: device that is being removed
+ *
+ * Send notifications to all registered drivers that a device was removed.
+ */
+void i2o_driver_notify_device_remove_all(struct i2o_device *i2o_dev)
+{
+ int i;
+ struct i2o_driver *drv;
+
+ for (i = 0; i < i2o_max_drivers; i++) {
+ drv = i2o_drivers[i];
+
+ if (drv)
+ i2o_driver_notify_device_remove(drv, i2o_dev);
+ }
+}
+
+/**
+ * i2o_driver_init - initialize I2O drivers (OSMs)
+ *
+ * Registers the I2O bus and allocate memory for the array of OSMs.
+ *
+ * Returns 0 on success or negative error code on failure.
+ */
+int __init i2o_driver_init(void)
+{
+ int rc = 0;
+
+ spin_lock_init(&i2o_drivers_lock);
+
+ if ((i2o_max_drivers < 2) || (i2o_max_drivers > 64)) {
+ osm_warn("max_drivers set to %d, but must be >=2 and <= 64\n",
+ i2o_max_drivers);
+ i2o_max_drivers = I2O_MAX_DRIVERS;
+ }
+ osm_info("max drivers = %d\n", i2o_max_drivers);
+
+ i2o_drivers =
+ kcalloc(i2o_max_drivers, sizeof(*i2o_drivers), GFP_KERNEL);
+ if (!i2o_drivers)
+ return -ENOMEM;
+
+ rc = bus_register(&i2o_bus_type);
+
+ if (rc < 0)
+ kfree(i2o_drivers);
+
+ return rc;
+};
+
+/**
+ * i2o_driver_exit - clean up I2O drivers (OSMs)
+ *
+ * Unregisters the I2O bus and frees driver array.
+ */
+void i2o_driver_exit(void)
+{
+ bus_unregister(&i2o_bus_type);
+ kfree(i2o_drivers);
+};
+
+EXPORT_SYMBOL(i2o_driver_register);
+EXPORT_SYMBOL(i2o_driver_unregister);
+EXPORT_SYMBOL(i2o_driver_notify_controller_add_all);
+EXPORT_SYMBOL(i2o_driver_notify_controller_remove_all);
+EXPORT_SYMBOL(i2o_driver_notify_device_add_all);
+EXPORT_SYMBOL(i2o_driver_notify_device_remove_all);
diff --git a/drivers/staging/i2o/exec-osm.c b/drivers/staging/i2o/exec-osm.c
new file mode 100644
index 000000000..dce16e425
--- /dev/null
+++ b/drivers/staging/i2o/exec-osm.c
@@ -0,0 +1,612 @@
+/*
+ * Executive OSM
+ *
+ * Copyright (C) 1999-2002 Red Hat Software
+ *
+ * Written by Alan Cox, Building Number Three Ltd
+ *
+ * This program is free software; you can redistribute it and/or 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.
+ *
+ * A lot of the I2O message side code from this is taken from the Red
+ * Creek RCPCI45 adapter driver by Red Creek Communications
+ *
+ * Fixes/additions:
+ * Philipp Rumpf
+ * Juha Sievänen <Juha.Sievanen@cs.Helsinki.FI>
+ * Auvo Häkkinen <Auvo.Hakkinen@cs.Helsinki.FI>
+ * Deepak Saxena <deepak@plexity.net>
+ * Boji T Kannanthanam <boji.t.kannanthanam@intel.com>
+ * Alan Cox <alan@lxorguk.ukuu.org.uk>:
+ * Ported to Linux 2.5.
+ * Markus Lidel <Markus.Lidel@shadowconnect.com>:
+ * Minor fixes for 2.6.
+ * Markus Lidel <Markus.Lidel@shadowconnect.com>:
+ * Support for sysfs included.
+ */
+
+#include <linux/module.h>
+#include "i2o.h"
+#include <linux/delay.h>
+#include <linux/workqueue.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+#include <linux/sched.h> /* wait_event_interruptible_timeout() needs this */
+#include <asm/param.h> /* HZ */
+#include "core.h"
+
+#define OSM_NAME "exec-osm"
+
+struct i2o_driver i2o_exec_driver;
+
+/* global wait list for POST WAIT */
+static LIST_HEAD(i2o_exec_wait_list);
+
+/* Wait struct needed for POST WAIT */
+struct i2o_exec_wait {
+ wait_queue_head_t *wq; /* Pointer to Wait queue */
+ struct i2o_dma dma; /* DMA buffers to free on failure */
+ u32 tcntxt; /* transaction context from reply */
+ int complete; /* 1 if reply received otherwise 0 */
+ u32 m; /* message id */
+ struct i2o_message *msg; /* pointer to the reply message */
+ struct list_head list; /* node in global wait list */
+ spinlock_t lock; /* lock before modifying */
+};
+
+/* Work struct needed to handle LCT NOTIFY replies */
+struct i2o_exec_lct_notify_work {
+ struct work_struct work; /* work struct */
+ struct i2o_controller *c; /* controller on which the LCT NOTIFY
+ was received */
+};
+
+/* Exec OSM class handling definition */
+static struct i2o_class_id i2o_exec_class_id[] = {
+ {I2O_CLASS_EXECUTIVE},
+ {I2O_CLASS_END}
+};
+
+/**
+ * i2o_exec_wait_alloc - Allocate a i2o_exec_wait struct an initialize it
+ *
+ * Allocate the i2o_exec_wait struct and initialize the wait.
+ *
+ * Returns i2o_exec_wait pointer on success or negative error code on
+ * failure.
+ */
+static struct i2o_exec_wait *i2o_exec_wait_alloc(void)
+{
+ struct i2o_exec_wait *wait;
+
+ wait = kzalloc(sizeof(*wait), GFP_KERNEL);
+ if (!wait)
+ return NULL;
+
+ INIT_LIST_HEAD(&wait->list);
+ spin_lock_init(&wait->lock);
+
+ return wait;
+};
+
+/**
+ * i2o_exec_wait_free - Free an i2o_exec_wait struct
+ * @wait: I2O wait data which should be cleaned up
+ */
+static void i2o_exec_wait_free(struct i2o_exec_wait *wait)
+{
+ kfree(wait);
+};
+
+/**
+ * i2o_msg_post_wait_mem - Post and wait a message with DMA buffers
+ * @c: controller
+ * @msg: message to post
+ * @timeout: time in seconds to wait
+ * @dma: i2o_dma struct of the DMA buffer to free on failure
+ *
+ * This API allows an OSM to post a message and then be told whether or
+ * not the system received a successful reply. If the message times out
+ * then the value '-ETIMEDOUT' is returned. This is a special case. In
+ * this situation the message may (should) complete at an indefinite time
+ * in the future. When it completes it will use the memory buffer
+ * attached to the request. If -ETIMEDOUT is returned then the memory
+ * buffer must not be freed. Instead the event completion will free them
+ * for you. In all other cases the buffer are your problem.
+ *
+ * Returns 0 on success, negative error code on timeout or positive error
+ * code from reply.
+ */
+int i2o_msg_post_wait_mem(struct i2o_controller *c, struct i2o_message *msg,
+ unsigned long timeout, struct i2o_dma *dma)
+{
+ DECLARE_WAIT_QUEUE_HEAD_ONSTACK(wq);
+ struct i2o_exec_wait *wait;
+ static u32 tcntxt = 0x80000000;
+ unsigned long flags;
+ int rc = 0;
+
+ wait = i2o_exec_wait_alloc();
+ if (!wait) {
+ i2o_msg_nop(c, msg);
+ return -ENOMEM;
+ }
+
+ if (tcntxt == 0xffffffff)
+ tcntxt = 0x80000000;
+
+ if (dma)
+ wait->dma = *dma;
+
+ /*
+ * Fill in the message initiator context and transaction context.
+ * We will only use transaction contexts >= 0x80000000 for POST WAIT,
+ * so we could find a POST WAIT reply easier in the reply handler.
+ */
+ msg->u.s.icntxt = cpu_to_le32(i2o_exec_driver.context);
+ wait->tcntxt = tcntxt++;
+ msg->u.s.tcntxt = cpu_to_le32(wait->tcntxt);
+
+ wait->wq = &wq;
+ /*
+ * we add elements to the head, because if a entry in the list will
+ * never be removed, we have to iterate over it every time
+ */
+ list_add(&wait->list, &i2o_exec_wait_list);
+
+ /*
+ * Post the message to the controller. At some point later it will
+ * return. If we time out before it returns then complete will be zero.
+ */
+ i2o_msg_post(c, msg);
+
+ wait_event_interruptible_timeout(wq, wait->complete, timeout * HZ);
+
+ spin_lock_irqsave(&wait->lock, flags);
+
+ wait->wq = NULL;
+
+ if (wait->complete)
+ rc = le32_to_cpu(wait->msg->body[0]) >> 24;
+ else {
+ /*
+ * We cannot remove it now. This is important. When it does
+ * terminate (which it must do if the controller has not
+ * died...) then it will otherwise scribble on stuff.
+ *
+ * FIXME: try abort message
+ */
+ if (dma)
+ dma->virt = NULL;
+
+ rc = -ETIMEDOUT;
+ }
+
+ spin_unlock_irqrestore(&wait->lock, flags);
+
+ if (rc != -ETIMEDOUT) {
+ i2o_flush_reply(c, wait->m);
+ i2o_exec_wait_free(wait);
+ }
+
+ return rc;
+};
+
+/**
+ * i2o_msg_post_wait_complete - Reply to a i2o_msg_post request from IOP
+ * @c: I2O controller which answers
+ * @m: message id
+ * @msg: pointer to the I2O reply message
+ * @context: transaction context of request
+ *
+ * This function is called in interrupt context only. If the reply reached
+ * before the timeout, the i2o_exec_wait struct is filled with the message
+ * and the task will be waked up. The task is now responsible for returning
+ * the message m back to the controller! If the message reaches us after
+ * the timeout clean up the i2o_exec_wait struct (including allocated
+ * DMA buffer).
+ *
+ * Return 0 on success and if the message m should not be given back to the
+ * I2O controller, or >0 on success and if the message should be given back
+ * afterwords. Returns negative error code on failure. In this case the
+ * message must also be given back to the controller.
+ */
+static int i2o_msg_post_wait_complete(struct i2o_controller *c, u32 m,
+ struct i2o_message *msg, u32 context)
+{
+ struct i2o_exec_wait *wait, *tmp;
+ unsigned long flags;
+ int rc = 1;
+
+ /*
+ * We need to search through the i2o_exec_wait_list to see if the given
+ * message is still outstanding. If not, it means that the IOP took
+ * longer to respond to the message than we had allowed and timer has
+ * already expired. Not much we can do about that except log it for
+ * debug purposes, increase timeout, and recompile.
+ */
+ list_for_each_entry_safe(wait, tmp, &i2o_exec_wait_list, list) {
+ if (wait->tcntxt == context) {
+ spin_lock_irqsave(&wait->lock, flags);
+
+ list_del(&wait->list);
+
+ wait->m = m;
+ wait->msg = msg;
+ wait->complete = 1;
+
+ if (wait->wq)
+ rc = 0;
+ else
+ rc = -1;
+
+ spin_unlock_irqrestore(&wait->lock, flags);
+
+ if (rc) {
+ struct device *dev;
+
+ dev = &c->pdev->dev;
+
+ pr_debug("%s: timedout reply received!\n",
+ c->name);
+ i2o_dma_free(dev, &wait->dma);
+ i2o_exec_wait_free(wait);
+ } else
+ wake_up_interruptible(wait->wq);
+
+ return rc;
+ }
+ }
+
+ osm_warn("%s: Bogus reply in POST WAIT (tr-context: %08x)!\n", c->name,
+ context);
+
+ return -1;
+};
+
+/**
+ * i2o_exec_show_vendor_id - Displays Vendor ID of controller
+ * @d: device of which the Vendor ID should be displayed
+ * @attr: device_attribute to display
+ * @buf: buffer into which the Vendor ID should be printed
+ *
+ * Returns number of bytes printed into buffer.
+ */
+static ssize_t i2o_exec_show_vendor_id(struct device *d,
+ struct device_attribute *attr, char *buf)
+{
+ struct i2o_device *dev = to_i2o_device(d);
+ u16 id;
+
+ if (!i2o_parm_field_get(dev, 0x0000, 0, &id, 2)) {
+ sprintf(buf, "0x%04x", le16_to_cpu(id));
+ return strlen(buf) + 1;
+ }
+
+ return 0;
+};
+
+/**
+ * i2o_exec_show_product_id - Displays Product ID of controller
+ * @d: device of which the Product ID should be displayed
+ * @attr: device_attribute to display
+ * @buf: buffer into which the Product ID should be printed
+ *
+ * Returns number of bytes printed into buffer.
+ */
+static ssize_t i2o_exec_show_product_id(struct device *d,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct i2o_device *dev = to_i2o_device(d);
+ u16 id;
+
+ if (!i2o_parm_field_get(dev, 0x0000, 1, &id, 2)) {
+ sprintf(buf, "0x%04x", le16_to_cpu(id));
+ return strlen(buf) + 1;
+ }
+
+ return 0;
+};
+
+/* Exec-OSM device attributes */
+static DEVICE_ATTR(vendor_id, S_IRUGO, i2o_exec_show_vendor_id, NULL);
+static DEVICE_ATTR(product_id, S_IRUGO, i2o_exec_show_product_id, NULL);
+
+/**
+ * i2o_exec_probe - Called if a new I2O device (executive class) appears
+ * @dev: I2O device which should be probed
+ *
+ * Registers event notification for every event from Executive device. The
+ * return is always 0, because we want all devices of class Executive.
+ *
+ * Returns 0 on success.
+ */
+static int i2o_exec_probe(struct device *dev)
+{
+ struct i2o_device *i2o_dev = to_i2o_device(dev);
+ int rc;
+
+ rc = i2o_event_register(i2o_dev, &i2o_exec_driver, 0, 0xffffffff);
+ if (rc) goto err_out;
+
+ rc = device_create_file(dev, &dev_attr_vendor_id);
+ if (rc) goto err_evtreg;
+ rc = device_create_file(dev, &dev_attr_product_id);
+ if (rc) goto err_vid;
+
+ i2o_dev->iop->exec = i2o_dev;
+
+ return 0;
+
+err_vid:
+ device_remove_file(dev, &dev_attr_vendor_id);
+err_evtreg:
+ i2o_event_register(to_i2o_device(dev), &i2o_exec_driver, 0, 0);
+err_out:
+ return rc;
+};
+
+/**
+ * i2o_exec_remove - Called on I2O device removal
+ * @dev: I2O device which was removed
+ *
+ * Unregisters event notification from Executive I2O device.
+ *
+ * Returns 0 on success.
+ */
+static int i2o_exec_remove(struct device *dev)
+{
+ device_remove_file(dev, &dev_attr_product_id);
+ device_remove_file(dev, &dev_attr_vendor_id);
+
+ i2o_event_register(to_i2o_device(dev), &i2o_exec_driver, 0, 0);
+
+ return 0;
+};
+
+#ifdef CONFIG_I2O_LCT_NOTIFY_ON_CHANGES
+/**
+ * i2o_exec_lct_notify - Send a asynchronus LCT NOTIFY request
+ * @c: I2O controller to which the request should be send
+ * @change_ind: change indicator
+ *
+ * This function sends a LCT NOTIFY request to the I2O controller with
+ * the change indicator change_ind. If the change_ind == 0 the controller
+ * replies immediately after the request. If change_ind > 0 the reply is
+ * send after change indicator of the LCT is > change_ind.
+ */
+static int i2o_exec_lct_notify(struct i2o_controller *c, u32 change_ind)
+{
+ i2o_status_block *sb = c->status_block.virt;
+ struct device *dev;
+ struct i2o_message *msg;
+
+ mutex_lock(&c->lct_lock);
+
+ dev = &c->pdev->dev;
+
+ if (i2o_dma_realloc(dev, &c->dlct,
+ le32_to_cpu(sb->expected_lct_size))) {
+ mutex_unlock(&c->lct_lock);
+ return -ENOMEM;
+ }
+
+ msg = i2o_msg_get_wait(c, I2O_TIMEOUT_MESSAGE_GET);
+ if (IS_ERR(msg)) {
+ mutex_unlock(&c->lct_lock);
+ return PTR_ERR(msg);
+ }
+
+ msg->u.head[0] = cpu_to_le32(EIGHT_WORD_MSG_SIZE | SGL_OFFSET_6);
+ msg->u.head[1] = cpu_to_le32(I2O_CMD_LCT_NOTIFY << 24 | HOST_TID << 12 |
+ ADAPTER_TID);
+ msg->u.s.icntxt = cpu_to_le32(i2o_exec_driver.context);
+ msg->u.s.tcntxt = cpu_to_le32(0x00000000);
+ msg->body[0] = cpu_to_le32(0xffffffff);
+ msg->body[1] = cpu_to_le32(change_ind);
+ msg->body[2] = cpu_to_le32(0xd0000000 | c->dlct.len);
+ msg->body[3] = cpu_to_le32(c->dlct.phys);
+
+ i2o_msg_post(c, msg);
+
+ mutex_unlock(&c->lct_lock);
+
+ return 0;
+}
+#endif
+
+/**
+ * i2o_exec_lct_modified - Called on LCT NOTIFY reply
+ * @_work: work struct for a specific controller
+ *
+ * This function handles asynchronus LCT NOTIFY replies. It parses the
+ * new LCT and if the buffer for the LCT was to small sends a LCT NOTIFY
+ * again, otherwise send LCT NOTIFY to get informed on next LCT change.
+ */
+static void i2o_exec_lct_modified(struct work_struct *_work)
+{
+ struct i2o_exec_lct_notify_work *work =
+ container_of(_work, struct i2o_exec_lct_notify_work, work);
+ u32 change_ind = 0;
+ struct i2o_controller *c = work->c;
+
+ kfree(work);
+
+ if (i2o_device_parse_lct(c) != -EAGAIN)
+ change_ind = c->lct->change_ind + 1;
+
+#ifdef CONFIG_I2O_LCT_NOTIFY_ON_CHANGES
+ i2o_exec_lct_notify(c, change_ind);
+#endif
+};
+
+/**
+ * i2o_exec_reply - I2O Executive reply handler
+ * @c: I2O controller from which the reply comes
+ * @m: message id
+ * @msg: pointer to the I2O reply message
+ *
+ * This function is always called from interrupt context. If a POST WAIT
+ * reply was received, pass it to the complete function. If a LCT NOTIFY
+ * reply was received, a new event is created to handle the update.
+ *
+ * Returns 0 on success and if the reply should not be flushed or > 0
+ * on success and if the reply should be flushed. Returns negative error
+ * code on failure and if the reply should be flushed.
+ */
+static int i2o_exec_reply(struct i2o_controller *c, u32 m,
+ struct i2o_message *msg)
+{
+ u32 context;
+
+ if (le32_to_cpu(msg->u.head[0]) & MSG_FAIL) {
+ struct i2o_message __iomem *pmsg;
+ u32 pm;
+
+ /*
+ * If Fail bit is set we must take the transaction context of
+ * the preserved message to find the right request again.
+ */
+
+ pm = le32_to_cpu(msg->body[3]);
+ pmsg = i2o_msg_in_to_virt(c, pm);
+ context = readl(&pmsg->u.s.tcntxt);
+
+ i2o_report_status(KERN_INFO, "i2o_core", msg);
+
+ /* Release the preserved msg */
+ i2o_msg_nop_mfa(c, pm);
+ } else
+ context = le32_to_cpu(msg->u.s.tcntxt);
+
+ if (context & 0x80000000)
+ return i2o_msg_post_wait_complete(c, m, msg, context);
+
+ if ((le32_to_cpu(msg->u.head[1]) >> 24) == I2O_CMD_LCT_NOTIFY) {
+ struct i2o_exec_lct_notify_work *work;
+
+ pr_debug("%s: LCT notify received\n", c->name);
+
+ work = kmalloc(sizeof(*work), GFP_ATOMIC);
+ if (!work)
+ return -ENOMEM;
+
+ work->c = c;
+
+ INIT_WORK(&work->work, i2o_exec_lct_modified);
+ queue_work(i2o_exec_driver.event_queue, &work->work);
+ return 1;
+ }
+
+ /*
+ * If this happens, we want to dump the message to the syslog so
+ * it can be sent back to the card manufacturer by the end user
+ * to aid in debugging.
+ *
+ */
+ printk(KERN_WARNING "%s: Unsolicited message reply sent to core! Message dumped to syslog\n",
+ c->name);
+ i2o_dump_message(msg);
+
+ return -EFAULT;
+}
+
+/**
+ * i2o_exec_event - Event handling function
+ * @work: Work item in occurring event
+ *
+ * Handles events send by the Executive device. At the moment does not do
+ * anything useful.
+ */
+static void i2o_exec_event(struct work_struct *work)
+{
+ struct i2o_event *evt = container_of(work, struct i2o_event, work);
+
+ if (likely(evt->i2o_dev))
+ osm_debug("Event received from device: %d\n",
+ evt->i2o_dev->lct_data.tid);
+ kfree(evt);
+};
+
+/**
+ * i2o_exec_lct_get - Get the IOP's Logical Configuration Table
+ * @c: I2O controller from which the LCT should be fetched
+ *
+ * Send a LCT NOTIFY request to the controller, and wait
+ * I2O_TIMEOUT_LCT_GET seconds until arrival of response. If the LCT is
+ * to large, retry it.
+ *
+ * Returns 0 on success or negative error code on failure.
+ */
+int i2o_exec_lct_get(struct i2o_controller *c)
+{
+ struct i2o_message *msg;
+ int i = 0;
+ int rc = -EAGAIN;
+
+ for (i = 1; i <= I2O_LCT_GET_TRIES; i++) {
+ msg = i2o_msg_get_wait(c, I2O_TIMEOUT_MESSAGE_GET);
+ if (IS_ERR(msg))
+ return PTR_ERR(msg);
+
+ msg->u.head[0] =
+ cpu_to_le32(EIGHT_WORD_MSG_SIZE | SGL_OFFSET_6);
+ msg->u.head[1] =
+ cpu_to_le32(I2O_CMD_LCT_NOTIFY << 24 | HOST_TID << 12 |
+ ADAPTER_TID);
+ msg->body[0] = cpu_to_le32(0xffffffff);
+ msg->body[1] = cpu_to_le32(0x00000000);
+ msg->body[2] = cpu_to_le32(0xd0000000 | c->dlct.len);
+ msg->body[3] = cpu_to_le32(c->dlct.phys);
+
+ rc = i2o_msg_post_wait(c, msg, I2O_TIMEOUT_LCT_GET);
+ if (rc < 0)
+ break;
+
+ rc = i2o_device_parse_lct(c);
+ if (rc != -EAGAIN)
+ break;
+ }
+
+ return rc;
+}
+
+/* Exec OSM driver struct */
+struct i2o_driver i2o_exec_driver = {
+ .name = OSM_NAME,
+ .reply = i2o_exec_reply,
+ .event = i2o_exec_event,
+ .classes = i2o_exec_class_id,
+ .driver = {
+ .probe = i2o_exec_probe,
+ .remove = i2o_exec_remove,
+ },
+};
+
+/**
+ * i2o_exec_init - Registers the Exec OSM
+ *
+ * Registers the Exec OSM in the I2O core.
+ *
+ * Returns 0 on success or negative error code on failure.
+ */
+int __init i2o_exec_init(void)
+{
+ return i2o_driver_register(&i2o_exec_driver);
+};
+
+/**
+ * i2o_exec_exit - Removes the Exec OSM
+ *
+ * Unregisters the Exec OSM from the I2O core.
+ */
+void i2o_exec_exit(void)
+{
+ i2o_driver_unregister(&i2o_exec_driver);
+};
+
+EXPORT_SYMBOL(i2o_msg_post_wait_mem);
+EXPORT_SYMBOL(i2o_exec_lct_get);
diff --git a/drivers/staging/i2o/i2o.h b/drivers/staging/i2o/i2o.h
new file mode 100644
index 000000000..d23c3c20b
--- /dev/null
+++ b/drivers/staging/i2o/i2o.h
@@ -0,0 +1,988 @@
+/*
+ * I2O kernel space accessible structures/APIs
+ *
+ * (c) Copyright 1999, 2000 Red Hat Software
+ *
+ * This program is free software; you can redistribute it and/or
+ * 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 header file defined the I2O APIs/structures for use by
+ * the I2O kernel modules.
+ *
+ */
+
+#ifndef _I2O_H
+#define _I2O_H
+
+#include <linux/i2o-dev.h>
+
+/* How many different OSM's are we allowing */
+#define I2O_MAX_DRIVERS 8
+
+#include <linux/pci.h>
+#include <linux/bug.h>
+#include <linux/dma-mapping.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+#include <linux/workqueue.h> /* work_struct */
+#include <linux/mempool.h>
+#include <linux/mutex.h>
+#include <linux/scatterlist.h>
+#include <linux/semaphore.h> /* Needed for MUTEX init macros */
+
+#include <asm/io.h>
+
+/* message queue empty */
+#define I2O_QUEUE_EMPTY 0xffffffff
+
+/*
+ * Cache strategies
+ */
+
+/* The NULL strategy leaves everything up to the controller. This tends to be a
+ * pessimal but functional choice.
+ */
+#define CACHE_NULL 0
+/* Prefetch data when reading. We continually attempt to load the next 32 sectors
+ * into the controller cache.
+ */
+#define CACHE_PREFETCH 1
+/* Prefetch data when reading. We sometimes attempt to load the next 32 sectors
+ * into the controller cache. When an I/O is less <= 8K we assume its probably
+ * not sequential and don't prefetch (default)
+ */
+#define CACHE_SMARTFETCH 2
+/* Data is written to the cache and then out on to the disk. The I/O must be
+ * physically on the medium before the write is acknowledged (default without
+ * NVRAM)
+ */
+#define CACHE_WRITETHROUGH 17
+/* Data is written to the cache and then out on to the disk. The controller
+ * is permitted to write back the cache any way it wants. (default if battery
+ * backed NVRAM is present). It can be useful to set this for swap regardless of
+ * battery state.
+ */
+#define CACHE_WRITEBACK 18
+/* Optimise for under powered controllers, especially on RAID1 and RAID0. We
+ * write large I/O's directly to disk bypassing the cache to avoid the extra
+ * memory copy hits. Small writes are writeback cached
+ */
+#define CACHE_SMARTBACK 19
+/* Optimise for under powered controllers, especially on RAID1 and RAID0. We
+ * write large I/O's directly to disk bypassing the cache to avoid the extra
+ * memory copy hits. Small writes are writethrough cached. Suitable for devices
+ * lacking battery backup
+ */
+#define CACHE_SMARTTHROUGH 20
+
+/*
+ * Ioctl structures
+ */
+
+#define BLKI2OGRSTRAT _IOR('2', 1, int)
+#define BLKI2OGWSTRAT _IOR('2', 2, int)
+#define BLKI2OSRSTRAT _IOW('2', 3, int)
+#define BLKI2OSWSTRAT _IOW('2', 4, int)
+
+/*
+ * I2O Function codes
+ */
+
+/*
+ * Executive Class
+ */
+#define I2O_CMD_ADAPTER_ASSIGN 0xB3
+#define I2O_CMD_ADAPTER_READ 0xB2
+#define I2O_CMD_ADAPTER_RELEASE 0xB5
+#define I2O_CMD_BIOS_INFO_SET 0xA5
+#define I2O_CMD_BOOT_DEVICE_SET 0xA7
+#define I2O_CMD_CONFIG_VALIDATE 0xBB
+#define I2O_CMD_CONN_SETUP 0xCA
+#define I2O_CMD_DDM_DESTROY 0xB1
+#define I2O_CMD_DDM_ENABLE 0xD5
+#define I2O_CMD_DDM_QUIESCE 0xC7
+#define I2O_CMD_DDM_RESET 0xD9
+#define I2O_CMD_DDM_SUSPEND 0xAF
+#define I2O_CMD_DEVICE_ASSIGN 0xB7
+#define I2O_CMD_DEVICE_RELEASE 0xB9
+#define I2O_CMD_HRT_GET 0xA8
+#define I2O_CMD_ADAPTER_CLEAR 0xBE
+#define I2O_CMD_ADAPTER_CONNECT 0xC9
+#define I2O_CMD_ADAPTER_RESET 0xBD
+#define I2O_CMD_LCT_NOTIFY 0xA2
+#define I2O_CMD_OUTBOUND_INIT 0xA1
+#define I2O_CMD_PATH_ENABLE 0xD3
+#define I2O_CMD_PATH_QUIESCE 0xC5
+#define I2O_CMD_PATH_RESET 0xD7
+#define I2O_CMD_STATIC_MF_CREATE 0xDD
+#define I2O_CMD_STATIC_MF_RELEASE 0xDF
+#define I2O_CMD_STATUS_GET 0xA0
+#define I2O_CMD_SW_DOWNLOAD 0xA9
+#define I2O_CMD_SW_UPLOAD 0xAB
+#define I2O_CMD_SW_REMOVE 0xAD
+#define I2O_CMD_SYS_ENABLE 0xD1
+#define I2O_CMD_SYS_MODIFY 0xC1
+#define I2O_CMD_SYS_QUIESCE 0xC3
+#define I2O_CMD_SYS_TAB_SET 0xA3
+
+/*
+ * Utility Class
+ */
+#define I2O_CMD_UTIL_NOP 0x00
+#define I2O_CMD_UTIL_ABORT 0x01
+#define I2O_CMD_UTIL_CLAIM 0x09
+#define I2O_CMD_UTIL_RELEASE 0x0B
+#define I2O_CMD_UTIL_PARAMS_GET 0x06
+#define I2O_CMD_UTIL_PARAMS_SET 0x05
+#define I2O_CMD_UTIL_EVT_REGISTER 0x13
+#define I2O_CMD_UTIL_EVT_ACK 0x14
+#define I2O_CMD_UTIL_CONFIG_DIALOG 0x10
+#define I2O_CMD_UTIL_DEVICE_RESERVE 0x0D
+#define I2O_CMD_UTIL_DEVICE_RELEASE 0x0F
+#define I2O_CMD_UTIL_LOCK 0x17
+#define I2O_CMD_UTIL_LOCK_RELEASE 0x19
+#define I2O_CMD_UTIL_REPLY_FAULT_NOTIFY 0x15
+
+/*
+ * SCSI Host Bus Adapter Class
+ */
+#define I2O_CMD_SCSI_EXEC 0x81
+#define I2O_CMD_SCSI_ABORT 0x83
+#define I2O_CMD_SCSI_BUSRESET 0x27
+
+/*
+ * Bus Adapter Class
+ */
+#define I2O_CMD_BUS_ADAPTER_RESET 0x85
+#define I2O_CMD_BUS_RESET 0x87
+#define I2O_CMD_BUS_SCAN 0x89
+#define I2O_CMD_BUS_QUIESCE 0x8b
+
+/*
+ * Random Block Storage Class
+ */
+#define I2O_CMD_BLOCK_READ 0x30
+#define I2O_CMD_BLOCK_WRITE 0x31
+#define I2O_CMD_BLOCK_CFLUSH 0x37
+#define I2O_CMD_BLOCK_MLOCK 0x49
+#define I2O_CMD_BLOCK_MUNLOCK 0x4B
+#define I2O_CMD_BLOCK_MMOUNT 0x41
+#define I2O_CMD_BLOCK_MEJECT 0x43
+#define I2O_CMD_BLOCK_POWER 0x70
+
+#define I2O_CMD_PRIVATE 0xFF
+
+/* Command status values */
+
+#define I2O_CMD_IN_PROGRESS 0x01
+#define I2O_CMD_REJECTED 0x02
+#define I2O_CMD_FAILED 0x03
+#define I2O_CMD_COMPLETED 0x04
+
+/* I2O API function return values */
+
+#define I2O_RTN_NO_ERROR 0
+#define I2O_RTN_NOT_INIT 1
+#define I2O_RTN_FREE_Q_EMPTY 2
+#define I2O_RTN_TCB_ERROR 3
+#define I2O_RTN_TRANSACTION_ERROR 4
+#define I2O_RTN_ADAPTER_ALREADY_INIT 5
+#define I2O_RTN_MALLOC_ERROR 6
+#define I2O_RTN_ADPTR_NOT_REGISTERED 7
+#define I2O_RTN_MSG_REPLY_TIMEOUT 8
+#define I2O_RTN_NO_STATUS 9
+#define I2O_RTN_NO_FIRM_VER 10
+#define I2O_RTN_NO_LINK_SPEED 11
+
+/* Reply message status defines for all messages */
+
+#define I2O_REPLY_STATUS_SUCCESS 0x00
+#define I2O_REPLY_STATUS_ABORT_DIRTY 0x01
+#define I2O_REPLY_STATUS_ABORT_NO_DATA_TRANSFER 0x02
+#define I2O_REPLY_STATUS_ABORT_PARTIAL_TRANSFER 0x03
+#define I2O_REPLY_STATUS_ERROR_DIRTY 0x04
+#define I2O_REPLY_STATUS_ERROR_NO_DATA_TRANSFER 0x05
+#define I2O_REPLY_STATUS_ERROR_PARTIAL_TRANSFER 0x06
+#define I2O_REPLY_STATUS_PROCESS_ABORT_DIRTY 0x08
+#define I2O_REPLY_STATUS_PROCESS_ABORT_NO_DATA_TRANSFER 0x09
+#define I2O_REPLY_STATUS_PROCESS_ABORT_PARTIAL_TRANSFER 0x0A
+#define I2O_REPLY_STATUS_TRANSACTION_ERROR 0x0B
+#define I2O_REPLY_STATUS_PROGRESS_REPORT 0x80
+
+/* Status codes and Error Information for Parameter functions */
+
+#define I2O_PARAMS_STATUS_SUCCESS 0x00
+#define I2O_PARAMS_STATUS_BAD_KEY_ABORT 0x01
+#define I2O_PARAMS_STATUS_BAD_KEY_CONTINUE 0x02
+#define I2O_PARAMS_STATUS_BUFFER_FULL 0x03
+#define I2O_PARAMS_STATUS_BUFFER_TOO_SMALL 0x04
+#define I2O_PARAMS_STATUS_FIELD_UNREADABLE 0x05
+#define I2O_PARAMS_STATUS_FIELD_UNWRITEABLE 0x06
+#define I2O_PARAMS_STATUS_INSUFFICIENT_FIELDS 0x07
+#define I2O_PARAMS_STATUS_INVALID_GROUP_ID 0x08
+#define I2O_PARAMS_STATUS_INVALID_OPERATION 0x09
+#define I2O_PARAMS_STATUS_NO_KEY_FIELD 0x0A
+#define I2O_PARAMS_STATUS_NO_SUCH_FIELD 0x0B
+#define I2O_PARAMS_STATUS_NON_DYNAMIC_GROUP 0x0C
+#define I2O_PARAMS_STATUS_OPERATION_ERROR 0x0D
+#define I2O_PARAMS_STATUS_SCALAR_ERROR 0x0E
+#define I2O_PARAMS_STATUS_TABLE_ERROR 0x0F
+#define I2O_PARAMS_STATUS_WRONG_GROUP_TYPE 0x10
+
+/* DetailedStatusCode defines for Executive, DDM, Util and Transaction error
+ * messages: Table 3-2 Detailed Status Codes.*/
+
+#define I2O_DSC_SUCCESS 0x0000
+#define I2O_DSC_BAD_KEY 0x0002
+#define I2O_DSC_TCL_ERROR 0x0003
+#define I2O_DSC_REPLY_BUFFER_FULL 0x0004
+#define I2O_DSC_NO_SUCH_PAGE 0x0005
+#define I2O_DSC_INSUFFICIENT_RESOURCE_SOFT 0x0006
+#define I2O_DSC_INSUFFICIENT_RESOURCE_HARD 0x0007
+#define I2O_DSC_CHAIN_BUFFER_TOO_LARGE 0x0009
+#define I2O_DSC_UNSUPPORTED_FUNCTION 0x000A
+#define I2O_DSC_DEVICE_LOCKED 0x000B
+#define I2O_DSC_DEVICE_RESET 0x000C
+#define I2O_DSC_INAPPROPRIATE_FUNCTION 0x000D
+#define I2O_DSC_INVALID_INITIATOR_ADDRESS 0x000E
+#define I2O_DSC_INVALID_MESSAGE_FLAGS 0x000F
+#define I2O_DSC_INVALID_OFFSET 0x0010
+#define I2O_DSC_INVALID_PARAMETER 0x0011
+#define I2O_DSC_INVALID_REQUEST 0x0012
+#define I2O_DSC_INVALID_TARGET_ADDRESS 0x0013
+#define I2O_DSC_MESSAGE_TOO_LARGE 0x0014
+#define I2O_DSC_MESSAGE_TOO_SMALL 0x0015
+#define I2O_DSC_MISSING_PARAMETER 0x0016
+#define I2O_DSC_TIMEOUT 0x0017
+#define I2O_DSC_UNKNOWN_ERROR 0x0018
+#define I2O_DSC_UNKNOWN_FUNCTION 0x0019
+#define I2O_DSC_UNSUPPORTED_VERSION 0x001A
+#define I2O_DSC_DEVICE_BUSY 0x001B
+#define I2O_DSC_DEVICE_NOT_AVAILABLE 0x001C
+
+/* DetailedStatusCode defines for Block Storage Operation: Table 6-7 Detailed
+ Status Codes.*/
+
+#define I2O_BSA_DSC_SUCCESS 0x0000
+#define I2O_BSA_DSC_MEDIA_ERROR 0x0001
+#define I2O_BSA_DSC_ACCESS_ERROR 0x0002
+#define I2O_BSA_DSC_DEVICE_FAILURE 0x0003
+#define I2O_BSA_DSC_DEVICE_NOT_READY 0x0004
+#define I2O_BSA_DSC_MEDIA_NOT_PRESENT 0x0005
+#define I2O_BSA_DSC_MEDIA_LOCKED 0x0006
+#define I2O_BSA_DSC_MEDIA_FAILURE 0x0007
+#define I2O_BSA_DSC_PROTOCOL_FAILURE 0x0008
+#define I2O_BSA_DSC_BUS_FAILURE 0x0009
+#define I2O_BSA_DSC_ACCESS_VIOLATION 0x000A
+#define I2O_BSA_DSC_WRITE_PROTECTED 0x000B
+#define I2O_BSA_DSC_DEVICE_RESET 0x000C
+#define I2O_BSA_DSC_VOLUME_CHANGED 0x000D
+#define I2O_BSA_DSC_TIMEOUT 0x000E
+
+/* FailureStatusCodes, Table 3-3 Message Failure Codes */
+
+#define I2O_FSC_TRANSPORT_SERVICE_SUSPENDED 0x81
+#define I2O_FSC_TRANSPORT_SERVICE_TERMINATED 0x82
+#define I2O_FSC_TRANSPORT_CONGESTION 0x83
+#define I2O_FSC_TRANSPORT_FAILURE 0x84
+#define I2O_FSC_TRANSPORT_STATE_ERROR 0x85
+#define I2O_FSC_TRANSPORT_TIME_OUT 0x86
+#define I2O_FSC_TRANSPORT_ROUTING_FAILURE 0x87
+#define I2O_FSC_TRANSPORT_INVALID_VERSION 0x88
+#define I2O_FSC_TRANSPORT_INVALID_OFFSET 0x89
+#define I2O_FSC_TRANSPORT_INVALID_MSG_FLAGS 0x8A
+#define I2O_FSC_TRANSPORT_FRAME_TOO_SMALL 0x8B
+#define I2O_FSC_TRANSPORT_FRAME_TOO_LARGE 0x8C
+#define I2O_FSC_TRANSPORT_INVALID_TARGET_ID 0x8D
+#define I2O_FSC_TRANSPORT_INVALID_INITIATOR_ID 0x8E
+#define I2O_FSC_TRANSPORT_INVALID_INITIATOR_CONTEXT 0x8F
+#define I2O_FSC_TRANSPORT_UNKNOWN_FAILURE 0xFF
+
+/* Device Claim Types */
+#define I2O_CLAIM_PRIMARY 0x01000000
+#define I2O_CLAIM_MANAGEMENT 0x02000000
+#define I2O_CLAIM_AUTHORIZED 0x03000000
+#define I2O_CLAIM_SECONDARY 0x04000000
+
+/* Message header defines for VersionOffset */
+#define I2OVER15 0x0001
+#define I2OVER20 0x0002
+
+/* Default is 1.5 */
+#define I2OVERSION I2OVER15
+
+#define SGL_OFFSET_0 I2OVERSION
+#define SGL_OFFSET_4 (0x0040 | I2OVERSION)
+#define SGL_OFFSET_5 (0x0050 | I2OVERSION)
+#define SGL_OFFSET_6 (0x0060 | I2OVERSION)
+#define SGL_OFFSET_7 (0x0070 | I2OVERSION)
+#define SGL_OFFSET_8 (0x0080 | I2OVERSION)
+#define SGL_OFFSET_9 (0x0090 | I2OVERSION)
+#define SGL_OFFSET_10 (0x00A0 | I2OVERSION)
+#define SGL_OFFSET_11 (0x00B0 | I2OVERSION)
+#define SGL_OFFSET_12 (0x00C0 | I2OVERSION)
+#define SGL_OFFSET(x) (((x)<<4) | I2OVERSION)
+
+/* Transaction Reply Lists (TRL) Control Word structure */
+#define TRL_SINGLE_FIXED_LENGTH 0x00
+#define TRL_SINGLE_VARIABLE_LENGTH 0x40
+#define TRL_MULTIPLE_FIXED_LENGTH 0x80
+
+ /* msg header defines for MsgFlags */
+#define MSG_STATIC 0x0100
+#define MSG_64BIT_CNTXT 0x0200
+#define MSG_MULTI_TRANS 0x1000
+#define MSG_FAIL 0x2000
+#define MSG_FINAL 0x4000
+#define MSG_REPLY 0x8000
+
+ /* minimum size msg */
+#define THREE_WORD_MSG_SIZE 0x00030000
+#define FOUR_WORD_MSG_SIZE 0x00040000
+#define FIVE_WORD_MSG_SIZE 0x00050000
+#define SIX_WORD_MSG_SIZE 0x00060000
+#define SEVEN_WORD_MSG_SIZE 0x00070000
+#define EIGHT_WORD_MSG_SIZE 0x00080000
+#define NINE_WORD_MSG_SIZE 0x00090000
+#define TEN_WORD_MSG_SIZE 0x000A0000
+#define ELEVEN_WORD_MSG_SIZE 0x000B0000
+#define I2O_MESSAGE_SIZE(x) ((x)<<16)
+
+/* special TID assignments */
+#define ADAPTER_TID 0
+#define HOST_TID 1
+
+/* outbound queue defines */
+#define I2O_MAX_OUTBOUND_MSG_FRAMES 128
+#define I2O_OUTBOUND_MSG_FRAME_SIZE 128 /* in 32-bit words */
+
+/* inbound queue definitions */
+#define I2O_MSG_INPOOL_MIN 32
+#define I2O_INBOUND_MSG_FRAME_SIZE 128 /* in 32-bit words */
+
+#define I2O_POST_WAIT_OK 0
+#define I2O_POST_WAIT_TIMEOUT -ETIMEDOUT
+
+#define I2O_CONTEXT_LIST_MIN_LENGTH 15
+#define I2O_CONTEXT_LIST_USED 0x01
+#define I2O_CONTEXT_LIST_DELETED 0x02
+
+/* timeouts */
+#define I2O_TIMEOUT_INIT_OUTBOUND_QUEUE 15
+#define I2O_TIMEOUT_MESSAGE_GET 5
+#define I2O_TIMEOUT_RESET 30
+#define I2O_TIMEOUT_STATUS_GET 5
+#define I2O_TIMEOUT_LCT_GET 360
+#define I2O_TIMEOUT_SCSI_SCB_ABORT 240
+
+/* retries */
+#define I2O_HRT_GET_TRIES 3
+#define I2O_LCT_GET_TRIES 3
+
+/* defines for max_sectors and max_phys_segments */
+#define I2O_MAX_SECTORS 1024
+#define I2O_MAX_SECTORS_LIMITED 128
+#define I2O_MAX_PHYS_SEGMENTS BLK_MAX_SEGMENTS
+
+/*
+ * Message structures
+ */
+struct i2o_message {
+ union {
+ struct {
+ u8 version_offset;
+ u8 flags;
+ u16 size;
+ u32 target_tid:12;
+ u32 init_tid:12;
+ u32 function:8;
+ u32 icntxt; /* initiator context */
+ u32 tcntxt; /* transaction context */
+ } s;
+ u32 head[4];
+ } u;
+ /* List follows */
+ u32 body[0];
+};
+
+/* MFA and I2O message used by mempool */
+struct i2o_msg_mfa {
+ u32 mfa; /* MFA returned by the controller */
+ struct i2o_message msg; /* I2O message */
+};
+
+/*
+ * Each I2O device entity has one of these. There is one per device.
+ */
+struct i2o_device {
+ i2o_lct_entry lct_data; /* Device LCT information */
+
+ struct i2o_controller *iop; /* Controlling IOP */
+ struct list_head list; /* node in IOP devices list */
+
+ struct device device;
+
+ struct mutex lock; /* device lock */
+};
+
+/*
+ * Event structure provided to the event handling function
+ */
+struct i2o_event {
+ struct work_struct work;
+ struct i2o_device *i2o_dev; /* I2O device pointer from which the
+ event reply was initiated */
+ u16 size; /* Size of data in 32-bit words */
+ u32 tcntxt; /* Transaction context used at
+ registration */
+ u32 event_indicator; /* Event indicator from reply */
+ u32 data[0]; /* Event data from reply */
+};
+
+/*
+ * I2O classes which could be handled by the OSM
+ */
+struct i2o_class_id {
+ u16 class_id:12;
+};
+
+/*
+ * I2O driver structure for OSMs
+ */
+struct i2o_driver {
+ char *name; /* OSM name */
+ int context; /* Low 8 bits of the transaction info */
+ struct i2o_class_id *classes; /* I2O classes that this OSM handles */
+
+ /* Message reply handler */
+ int (*reply) (struct i2o_controller *, u32, struct i2o_message *);
+
+ /* Event handler */
+ work_func_t event;
+
+ struct workqueue_struct *event_queue; /* Event queue */
+
+ struct device_driver driver;
+
+ /* notification of changes */
+ void (*notify_controller_add) (struct i2o_controller *);
+ void (*notify_controller_remove) (struct i2o_controller *);
+ void (*notify_device_add) (struct i2o_device *);
+ void (*notify_device_remove) (struct i2o_device *);
+
+ struct semaphore lock;
+};
+
+/*
+ * Contains DMA mapped address information
+ */
+struct i2o_dma {
+ void *virt;
+ dma_addr_t phys;
+ size_t len;
+};
+
+/*
+ * Contains slab cache and mempool information
+ */
+struct i2o_pool {
+ char *name;
+ struct kmem_cache *slab;
+ mempool_t *mempool;
+};
+
+/*
+ * Contains IO mapped address information
+ */
+struct i2o_io {
+ void __iomem *virt;
+ unsigned long phys;
+ unsigned long len;
+};
+
+/*
+ * Context queue entry, used for 32-bit context on 64-bit systems
+ */
+struct i2o_context_list_element {
+ struct list_head list;
+ u32 context;
+ void *ptr;
+ unsigned long timestamp;
+};
+
+/*
+ * Each I2O controller has one of these objects
+ */
+struct i2o_controller {
+ char name[16];
+ int unit;
+ int type;
+
+ struct pci_dev *pdev; /* PCI device */
+
+ unsigned int promise:1; /* Promise controller */
+ unsigned int adaptec:1; /* DPT / Adaptec controller */
+ unsigned int raptor:1; /* split bar */
+ unsigned int no_quiesce:1; /* dont quiesce before reset */
+ unsigned int short_req:1; /* use small block sizes */
+ unsigned int limit_sectors:1; /* limit number of sectors / request */
+ unsigned int pae_support:1; /* controller has 64-bit SGL support */
+
+ struct list_head devices; /* list of I2O devices */
+ struct list_head list; /* Controller list */
+
+ void __iomem *in_port; /* Inbout port address */
+ void __iomem *out_port; /* Outbound port address */
+ void __iomem *irq_status; /* Interrupt status register address */
+ void __iomem *irq_mask; /* Interrupt mask register address */
+
+ struct i2o_dma status; /* IOP status block */
+
+ struct i2o_dma hrt; /* HW Resource Table */
+ i2o_lct *lct; /* Logical Config Table */
+ struct i2o_dma dlct; /* Temp LCT */
+ struct mutex lct_lock; /* Lock for LCT updates */
+ struct i2o_dma status_block; /* IOP status block */
+
+ struct i2o_io base; /* controller messaging unit */
+ struct i2o_io in_queue; /* inbound message queue Host->IOP */
+ struct i2o_dma out_queue; /* outbound message queue IOP->Host */
+
+ struct i2o_pool in_msg; /* mempool for inbound messages */
+
+ unsigned int battery:1; /* Has a battery backup */
+ unsigned int io_alloc:1; /* An I/O resource was allocated */
+ unsigned int mem_alloc:1; /* A memory resource was allocated */
+
+ struct resource io_resource; /* I/O resource allocated to the IOP */
+ struct resource mem_resource; /* Mem resource allocated to the IOP */
+
+ struct device device;
+ struct i2o_device *exec; /* Executive */
+#if BITS_PER_LONG == 64
+ spinlock_t context_list_lock; /* lock for context_list */
+ atomic_t context_list_counter; /* needed for unique contexts */
+ struct list_head context_list; /* list of context id's
+ and pointers */
+#endif
+ spinlock_t lock; /* lock for controller
+ configuration */
+ void *driver_data[I2O_MAX_DRIVERS]; /* storage for drivers */
+};
+
+/*
+ * I2O System table entry
+ *
+ * The system table contains information about all the IOPs in the
+ * system. It is sent to all IOPs so that they can create peer2peer
+ * connections between them.
+ */
+struct i2o_sys_tbl_entry {
+ u16 org_id;
+ u16 reserved1;
+ u32 iop_id:12;
+ u32 reserved2:20;
+ u16 seg_num:12;
+ u16 i2o_version:4;
+ u8 iop_state;
+ u8 msg_type;
+ u16 frame_size;
+ u16 reserved3;
+ u32 last_changed;
+ u32 iop_capabilities;
+ u32 inbound_low;
+ u32 inbound_high;
+};
+
+struct i2o_sys_tbl {
+ u8 num_entries;
+ u8 version;
+ u16 reserved1;
+ u32 change_ind;
+ u32 reserved2;
+ u32 reserved3;
+ struct i2o_sys_tbl_entry iops[0];
+};
+
+extern struct list_head i2o_controllers;
+
+/* Message functions */
+extern struct i2o_message *i2o_msg_get_wait(struct i2o_controller *, int);
+extern int i2o_msg_post_wait_mem(struct i2o_controller *, struct i2o_message *,
+ unsigned long, struct i2o_dma *);
+
+/* IOP functions */
+extern int i2o_status_get(struct i2o_controller *);
+
+extern int i2o_event_register(struct i2o_device *, struct i2o_driver *, int,
+ u32);
+extern struct i2o_device *i2o_iop_find_device(struct i2o_controller *, u16);
+extern struct i2o_controller *i2o_find_iop(int);
+
+/* Functions needed for handling 64-bit pointers in 32-bit context */
+#if BITS_PER_LONG == 64
+extern u32 i2o_cntxt_list_add(struct i2o_controller *, void *);
+extern void *i2o_cntxt_list_get(struct i2o_controller *, u32);
+extern u32 i2o_cntxt_list_remove(struct i2o_controller *, void *);
+extern u32 i2o_cntxt_list_get_ptr(struct i2o_controller *, void *);
+
+static inline u32 i2o_ptr_low(void *ptr)
+{
+ return (u32) (u64) ptr;
+};
+
+static inline u32 i2o_ptr_high(void *ptr)
+{
+ return (u32) ((u64) ptr >> 32);
+};
+
+static inline u32 i2o_dma_low(dma_addr_t dma_addr)
+{
+ return (u32) (u64) dma_addr;
+};
+
+static inline u32 i2o_dma_high(dma_addr_t dma_addr)
+{
+ return (u32) ((u64) dma_addr >> 32);
+};
+#else
+static inline u32 i2o_cntxt_list_add(struct i2o_controller *c, void *ptr)
+{
+ return (u32) ptr;
+};
+
+static inline void *i2o_cntxt_list_get(struct i2o_controller *c, u32 context)
+{
+ return (void *)context;
+};
+
+static inline u32 i2o_cntxt_list_remove(struct i2o_controller *c, void *ptr)
+{
+ return (u32) ptr;
+};
+
+static inline u32 i2o_cntxt_list_get_ptr(struct i2o_controller *c, void *ptr)
+{
+ return (u32) ptr;
+};
+
+static inline u32 i2o_ptr_low(void *ptr)
+{
+ return (u32) ptr;
+};
+
+static inline u32 i2o_ptr_high(void *ptr)
+{
+ return 0;
+};
+
+static inline u32 i2o_dma_low(dma_addr_t dma_addr)
+{
+ return (u32) dma_addr;
+};
+
+static inline u32 i2o_dma_high(dma_addr_t dma_addr)
+{
+ return 0;
+};
+#endif
+
+extern u16 i2o_sg_tablesize(struct i2o_controller *c, u16 body_size);
+extern dma_addr_t i2o_dma_map_single(struct i2o_controller *c, void *ptr,
+ size_t size,
+ enum dma_data_direction direction,
+ u32 ** sg_ptr);
+extern int i2o_dma_map_sg(struct i2o_controller *c,
+ struct scatterlist *sg, int sg_count,
+ enum dma_data_direction direction,
+ u32 ** sg_ptr);
+extern int i2o_dma_alloc(struct device *dev, struct i2o_dma *addr, size_t len);
+extern void i2o_dma_free(struct device *dev, struct i2o_dma *addr);
+extern int i2o_dma_realloc(struct device *dev, struct i2o_dma *addr,
+ size_t len);
+extern int i2o_pool_alloc(struct i2o_pool *pool, const char *name,
+ size_t size, int min_nr);
+extern void i2o_pool_free(struct i2o_pool *pool);
+/* I2O driver (OSM) functions */
+extern int i2o_driver_register(struct i2o_driver *);
+extern void i2o_driver_unregister(struct i2o_driver *);
+
+/**
+ * i2o_driver_notify_controller_add - Send notification of added controller
+ * @drv: I2O driver
+ * @c: I2O controller
+ *
+ * Send notification of added controller to a single registered driver.
+ */
+static inline void i2o_driver_notify_controller_add(struct i2o_driver *drv,
+ struct i2o_controller *c)
+{
+ if (drv->notify_controller_add)
+ drv->notify_controller_add(c);
+};
+
+/**
+ * i2o_driver_notify_controller_remove - Send notification of removed controller
+ * @drv: I2O driver
+ * @c: I2O controller
+ *
+ * Send notification of removed controller to a single registered driver.
+ */
+static inline void i2o_driver_notify_controller_remove(struct i2o_driver *drv,
+ struct i2o_controller *c)
+{
+ if (drv->notify_controller_remove)
+ drv->notify_controller_remove(c);
+};
+
+/**
+ * i2o_driver_notify_device_add - Send notification of added device
+ * @drv: I2O driver
+ * @i2o_dev: the added i2o_device
+ *
+ * Send notification of added device to a single registered driver.
+ */
+static inline void i2o_driver_notify_device_add(struct i2o_driver *drv,
+ struct i2o_device *i2o_dev)
+{
+ if (drv->notify_device_add)
+ drv->notify_device_add(i2o_dev);
+};
+
+/**
+ * i2o_driver_notify_device_remove - Send notification of removed device
+ * @drv: I2O driver
+ * @i2o_dev: the added i2o_device
+ *
+ * Send notification of removed device to a single registered driver.
+ */
+static inline void i2o_driver_notify_device_remove(struct i2o_driver *drv,
+ struct i2o_device *i2o_dev)
+{
+ if (drv->notify_device_remove)
+ drv->notify_device_remove(i2o_dev);
+};
+
+extern void i2o_driver_notify_controller_add_all(struct i2o_controller *);
+extern void i2o_driver_notify_controller_remove_all(struct i2o_controller *);
+extern void i2o_driver_notify_device_add_all(struct i2o_device *);
+extern void i2o_driver_notify_device_remove_all(struct i2o_device *);
+
+/* I2O device functions */
+extern int i2o_device_claim(struct i2o_device *);
+extern int i2o_device_claim_release(struct i2o_device *);
+
+/* Exec OSM functions */
+extern int i2o_exec_lct_get(struct i2o_controller *);
+
+/* device / driver / kobject conversion functions */
+#define to_i2o_driver(drv) container_of(drv,struct i2o_driver, driver)
+#define to_i2o_device(dev) container_of(dev, struct i2o_device, device)
+#define to_i2o_controller(dev) container_of(dev, struct i2o_controller, device)
+
+/**
+ * i2o_out_to_virt - Turn an I2O message to a virtual address
+ * @c: controller
+ * @m: message engine value
+ *
+ * Turn a receive message from an I2O controller bus address into
+ * a Linux virtual address. The shared page frame is a linear block
+ * so we simply have to shift the offset. This function does not
+ * work for sender side messages as they are ioremap objects
+ * provided by the I2O controller.
+ */
+static inline struct i2o_message *i2o_msg_out_to_virt(struct i2o_controller *c,
+ u32 m)
+{
+ BUG_ON(m < c->out_queue.phys
+ || m >= c->out_queue.phys + c->out_queue.len);
+
+ return c->out_queue.virt + (m - c->out_queue.phys);
+};
+
+/**
+ * i2o_msg_in_to_virt - Turn an I2O message to a virtual address
+ * @c: controller
+ * @m: message engine value
+ *
+ * Turn a send message from an I2O controller bus address into
+ * a Linux virtual address. The shared page frame is a linear block
+ * so we simply have to shift the offset. This function does not
+ * work for receive side messages as they are kmalloc objects
+ * in a different pool.
+ */
+static inline struct i2o_message __iomem *i2o_msg_in_to_virt(struct
+ i2o_controller *c,
+ u32 m)
+{
+ return c->in_queue.virt + m;
+};
+
+/**
+ * i2o_msg_get - obtain an I2O message from the IOP
+ * @c: I2O controller
+ *
+ * This function tries to get a message frame. If no message frame is
+ * available do not wait until one is available (see also i2o_msg_get_wait).
+ * The returned pointer to the message frame is not in I/O memory, it is
+ * allocated from a mempool. But because a MFA is allocated from the
+ * controller too it is guaranteed that i2o_msg_post() will never fail.
+ *
+ * On a success a pointer to the message frame is returned. If the message
+ * queue is empty -EBUSY is returned and if no memory is available -ENOMEM
+ * is returned.
+ */
+static inline struct i2o_message *i2o_msg_get(struct i2o_controller *c)
+{
+ struct i2o_msg_mfa *mmsg = mempool_alloc(c->in_msg.mempool, GFP_ATOMIC);
+ if (!mmsg)
+ return ERR_PTR(-ENOMEM);
+
+ mmsg->mfa = readl(c->in_port);
+ if (unlikely(mmsg->mfa >= c->in_queue.len)) {
+ u32 mfa = mmsg->mfa;
+
+ mempool_free(mmsg, c->in_msg.mempool);
+
+ if (mfa == I2O_QUEUE_EMPTY)
+ return ERR_PTR(-EBUSY);
+ return ERR_PTR(-EFAULT);
+ }
+
+ return &mmsg->msg;
+};
+
+/**
+ * i2o_msg_post - Post I2O message to I2O controller
+ * @c: I2O controller to which the message should be send
+ * @msg: message returned by i2o_msg_get()
+ *
+ * Post the message to the I2O controller and return immediately.
+ */
+static inline void i2o_msg_post(struct i2o_controller *c,
+ struct i2o_message *msg)
+{
+ struct i2o_msg_mfa *mmsg;
+
+ mmsg = container_of(msg, struct i2o_msg_mfa, msg);
+ memcpy_toio(i2o_msg_in_to_virt(c, mmsg->mfa), msg,
+ (le32_to_cpu(msg->u.head[0]) >> 16) << 2);
+ writel(mmsg->mfa, c->in_port);
+ mempool_free(mmsg, c->in_msg.mempool);
+};
+
+/**
+ * i2o_msg_post_wait - Post and wait a message and wait until return
+ * @c: controller
+ * @msg: message to post
+ * @timeout: time in seconds to wait
+ *
+ * This API allows an OSM to post a message and then be told whether or
+ * not the system received a successful reply. If the message times out
+ * then the value '-ETIMEDOUT' is returned.
+ *
+ * Returns 0 on success or negative error code on failure.
+ */
+static inline int i2o_msg_post_wait(struct i2o_controller *c,
+ struct i2o_message *msg,
+ unsigned long timeout)
+{
+ return i2o_msg_post_wait_mem(c, msg, timeout, NULL);
+};
+
+/**
+ * i2o_msg_nop_mfa - Returns a fetched MFA back to the controller
+ * @c: I2O controller from which the MFA was fetched
+ * @mfa: MFA which should be returned
+ *
+ * This function must be used for preserved messages, because i2o_msg_nop()
+ * also returns the allocated memory back to the msg_pool mempool.
+ */
+static inline void i2o_msg_nop_mfa(struct i2o_controller *c, u32 mfa)
+{
+ struct i2o_message __iomem *msg;
+ u32 nop[3] = {
+ THREE_WORD_MSG_SIZE | SGL_OFFSET_0,
+ I2O_CMD_UTIL_NOP << 24 | HOST_TID << 12 | ADAPTER_TID,
+ 0x00000000
+ };
+
+ msg = i2o_msg_in_to_virt(c, mfa);
+ memcpy_toio(msg, nop, sizeof(nop));
+ writel(mfa, c->in_port);
+};
+
+/**
+ * i2o_msg_nop - Returns a message which is not used
+ * @c: I2O controller from which the message was created
+ * @msg: message which should be returned
+ *
+ * If you fetch a message via i2o_msg_get, and can't use it, you must
+ * return the message with this function. Otherwise the MFA is lost as well
+ * as the allocated memory from the mempool.
+ */
+static inline void i2o_msg_nop(struct i2o_controller *c,
+ struct i2o_message *msg)
+{
+ struct i2o_msg_mfa *mmsg;
+ mmsg = container_of(msg, struct i2o_msg_mfa, msg);
+
+ i2o_msg_nop_mfa(c, mmsg->mfa);
+ mempool_free(mmsg, c->in_msg.mempool);
+};
+
+/**
+ * i2o_flush_reply - Flush reply from I2O controller
+ * @c: I2O controller
+ * @m: the message identifier
+ *
+ * The I2O controller must be informed that the reply message is not needed
+ * anymore. If you forget to flush the reply, the message frame can't be
+ * used by the controller anymore and is therefore lost.
+ */
+static inline void i2o_flush_reply(struct i2o_controller *c, u32 m)
+{
+ writel(m, c->out_port);
+};
+
+/*
+ * Endian handling wrapped into the macro - keeps the core code
+ * cleaner.
+ */
+
+#define i2o_raw_writel(val, mem) __raw_writel(cpu_to_le32(val), mem)
+
+extern int i2o_parm_field_get(struct i2o_device *, int, int, void *, int);
+extern int i2o_parm_table_get(struct i2o_device *, int, int, int, void *, int,
+ void *, int);
+
+/* debugging and troubleshooting/diagnostic helpers. */
+#define osm_printk(level, format, arg...) \
+ printk(level "%s: " format, OSM_NAME , ## arg)
+
+#ifdef DEBUG
+#define osm_debug(format, arg...) \
+ osm_printk(KERN_DEBUG, format , ## arg)
+#else
+#define osm_debug(format, arg...) \
+ do { } while (0)
+#endif
+
+#define osm_err(format, arg...) \
+ osm_printk(KERN_ERR, format , ## arg)
+#define osm_info(format, arg...) \
+ osm_printk(KERN_INFO, format , ## arg)
+#define osm_warn(format, arg...) \
+ osm_printk(KERN_WARNING, format , ## arg)
+
+/* debugging functions */
+extern void i2o_report_status(const char *, const char *, struct i2o_message *);
+extern void i2o_dump_message(struct i2o_message *);
+extern void i2o_dump_hrt(struct i2o_controller *c);
+extern void i2o_debug_state(struct i2o_controller *c);
+
+#endif /* _I2O_H */
diff --git a/drivers/staging/i2o/i2o_block.c b/drivers/staging/i2o/i2o_block.c
new file mode 100644
index 000000000..406758f75
--- /dev/null
+++ b/drivers/staging/i2o/i2o_block.c
@@ -0,0 +1,1228 @@
+/*
+ * Block OSM
+ *
+ * Copyright (C) 1999-2002 Red Hat Software
+ *
+ * Written by Alan Cox, Building Number Three Ltd
+ *
+ * This program is free software; you can redistribute it and/or 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.
+ *
+ * For the purpose of avoiding doubt the preferred form of the work
+ * for making modifications shall be a standards compliant form such
+ * gzipped tar and not one requiring a proprietary or patent encumbered
+ * tool to unpack.
+ *
+ * Fixes/additions:
+ * Steve Ralston:
+ * Multiple device handling error fixes,
+ * Added a queue depth.
+ * Alan Cox:
+ * FC920 has an rmw bug. Dont or in the end marker.
+ * Removed queue walk, fixed for 64bitness.
+ * Rewrote much of the code over time
+ * Added indirect block lists
+ * Handle 64K limits on many controllers
+ * Don't use indirects on the Promise (breaks)
+ * Heavily chop down the queue depths
+ * Deepak Saxena:
+ * Independent queues per IOP
+ * Support for dynamic device creation/deletion
+ * Code cleanup
+ * Support for larger I/Os through merge* functions
+ * (taken from DAC960 driver)
+ * Boji T Kannanthanam:
+ * Set the I2O Block devices to be detected in increasing
+ * order of TIDs during boot.
+ * Search and set the I2O block device that we boot off
+ * from as the first device to be claimed (as /dev/i2o/hda)
+ * Properly attach/detach I2O gendisk structure from the
+ * system gendisk list. The I2O block devices now appear in
+ * /proc/partitions.
+ * Markus Lidel <Markus.Lidel@shadowconnect.com>:
+ * Minor bugfixes for 2.6.
+ */
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include "i2o.h"
+#include <linux/mutex.h>
+
+#include <linux/mempool.h>
+
+#include <linux/genhd.h>
+#include <linux/blkdev.h>
+#include <linux/hdreg.h>
+
+#include <scsi/scsi.h>
+
+#include "i2o_block.h"
+
+#define OSM_NAME "block-osm"
+#define OSM_VERSION "1.325"
+#define OSM_DESCRIPTION "I2O Block Device OSM"
+
+static DEFINE_MUTEX(i2o_block_mutex);
+static struct i2o_driver i2o_block_driver;
+
+/* global Block OSM request mempool */
+static struct i2o_block_mempool i2o_blk_req_pool;
+
+/* Block OSM class handling definition */
+static struct i2o_class_id i2o_block_class_id[] = {
+ {I2O_CLASS_RANDOM_BLOCK_STORAGE},
+ {I2O_CLASS_END}
+};
+
+/**
+ * i2o_block_device_free - free the memory of the I2O Block device
+ * @dev: I2O Block device, which should be cleaned up
+ *
+ * Frees the request queue, gendisk and the i2o_block_device structure.
+ */
+static void i2o_block_device_free(struct i2o_block_device *dev)
+{
+ blk_cleanup_queue(dev->gd->queue);
+
+ put_disk(dev->gd);
+
+ kfree(dev);
+};
+
+/**
+ * i2o_block_remove - remove the I2O Block device from the system again
+ * @dev: I2O Block device which should be removed
+ *
+ * Remove gendisk from system and free all allocated memory.
+ *
+ * Always returns 0.
+ */
+static int i2o_block_remove(struct device *dev)
+{
+ struct i2o_device *i2o_dev = to_i2o_device(dev);
+ struct i2o_block_device *i2o_blk_dev = dev_get_drvdata(dev);
+
+ osm_info("device removed (TID: %03x): %s\n", i2o_dev->lct_data.tid,
+ i2o_blk_dev->gd->disk_name);
+
+ i2o_event_register(i2o_dev, &i2o_block_driver, 0, 0);
+
+ del_gendisk(i2o_blk_dev->gd);
+
+ dev_set_drvdata(dev, NULL);
+
+ i2o_device_claim_release(i2o_dev);
+
+ i2o_block_device_free(i2o_blk_dev);
+
+ return 0;
+};
+
+/**
+ * i2o_block_device flush - Flush all dirty data of I2O device dev
+ * @dev: I2O device which should be flushed
+ *
+ * Flushes all dirty data on device dev.
+ *
+ * Returns 0 on success or negative error code on failure.
+ */
+static int i2o_block_device_flush(struct i2o_device *dev)
+{
+ struct i2o_message *msg;
+
+ msg = i2o_msg_get_wait(dev->iop, I2O_TIMEOUT_MESSAGE_GET);
+ if (IS_ERR(msg))
+ return PTR_ERR(msg);
+
+ msg->u.head[0] = cpu_to_le32(FIVE_WORD_MSG_SIZE | SGL_OFFSET_0);
+ msg->u.head[1] =
+ cpu_to_le32(I2O_CMD_BLOCK_CFLUSH << 24 | HOST_TID << 12 | dev->
+ lct_data.tid);
+ msg->body[0] = cpu_to_le32(60 << 16);
+ osm_debug("Flushing...\n");
+
+ return i2o_msg_post_wait(dev->iop, msg, 60);
+};
+
+/**
+ * i2o_block_device_mount - Mount (load) the media of device dev
+ * @dev: I2O device which should receive the mount request
+ * @media_id: Media Identifier
+ *
+ * Load a media into drive. Identifier should be set to -1, because the
+ * spec does not support any other value.
+ *
+ * Returns 0 on success or negative error code on failure.
+ */
+static int i2o_block_device_mount(struct i2o_device *dev, u32 media_id)
+{
+ struct i2o_message *msg;
+
+ msg = i2o_msg_get_wait(dev->iop, I2O_TIMEOUT_MESSAGE_GET);
+ if (IS_ERR(msg))
+ return PTR_ERR(msg);
+
+ msg->u.head[0] = cpu_to_le32(FIVE_WORD_MSG_SIZE | SGL_OFFSET_0);
+ msg->u.head[1] =
+ cpu_to_le32(I2O_CMD_BLOCK_MMOUNT << 24 | HOST_TID << 12 | dev->
+ lct_data.tid);
+ msg->body[0] = cpu_to_le32(-1);
+ msg->body[1] = cpu_to_le32(0x00000000);
+ osm_debug("Mounting...\n");
+
+ return i2o_msg_post_wait(dev->iop, msg, 2);
+};
+
+/**
+ * i2o_block_device_lock - Locks the media of device dev
+ * @dev: I2O device which should receive the lock request
+ * @media_id: Media Identifier
+ *
+ * Lock media of device dev to prevent removal. The media identifier
+ * should be set to -1, because the spec does not support any other value.
+ *
+ * Returns 0 on success or negative error code on failure.
+ */
+static int i2o_block_device_lock(struct i2o_device *dev, u32 media_id)
+{
+ struct i2o_message *msg;
+
+ msg = i2o_msg_get_wait(dev->iop, I2O_TIMEOUT_MESSAGE_GET);
+ if (IS_ERR(msg))
+ return PTR_ERR(msg);
+
+ msg->u.head[0] = cpu_to_le32(FIVE_WORD_MSG_SIZE | SGL_OFFSET_0);
+ msg->u.head[1] =
+ cpu_to_le32(I2O_CMD_BLOCK_MLOCK << 24 | HOST_TID << 12 | dev->
+ lct_data.tid);
+ msg->body[0] = cpu_to_le32(-1);
+ osm_debug("Locking...\n");
+
+ return i2o_msg_post_wait(dev->iop, msg, 2);
+};
+
+/**
+ * i2o_block_device_unlock - Unlocks the media of device dev
+ * @dev: I2O device which should receive the unlocked request
+ * @media_id: Media Identifier
+ *
+ * Unlocks the media in device dev. The media identifier should be set to
+ * -1, because the spec does not support any other value.
+ *
+ * Returns 0 on success or negative error code on failure.
+ */
+static int i2o_block_device_unlock(struct i2o_device *dev, u32 media_id)
+{
+ struct i2o_message *msg;
+
+ msg = i2o_msg_get_wait(dev->iop, I2O_TIMEOUT_MESSAGE_GET);
+ if (IS_ERR(msg))
+ return PTR_ERR(msg);
+
+ msg->u.head[0] = cpu_to_le32(FIVE_WORD_MSG_SIZE | SGL_OFFSET_0);
+ msg->u.head[1] =
+ cpu_to_le32(I2O_CMD_BLOCK_MUNLOCK << 24 | HOST_TID << 12 | dev->
+ lct_data.tid);
+ msg->body[0] = cpu_to_le32(media_id);
+ osm_debug("Unlocking...\n");
+
+ return i2o_msg_post_wait(dev->iop, msg, 2);
+};
+
+/**
+ * i2o_block_device_power - Power management for device dev
+ * @dev: I2O device which should receive the power management request
+ * @op: Operation to send
+ *
+ * Send a power management request to the device dev.
+ *
+ * Returns 0 on success or negative error code on failure.
+ */
+static int i2o_block_device_power(struct i2o_block_device *dev, u8 op)
+{
+ struct i2o_device *i2o_dev = dev->i2o_dev;
+ struct i2o_controller *c = i2o_dev->iop;
+ struct i2o_message *msg;
+ int rc;
+
+ msg = i2o_msg_get_wait(c, I2O_TIMEOUT_MESSAGE_GET);
+ if (IS_ERR(msg))
+ return PTR_ERR(msg);
+
+ msg->u.head[0] = cpu_to_le32(FOUR_WORD_MSG_SIZE | SGL_OFFSET_0);
+ msg->u.head[1] =
+ cpu_to_le32(I2O_CMD_BLOCK_POWER << 24 | HOST_TID << 12 | i2o_dev->
+ lct_data.tid);
+ msg->body[0] = cpu_to_le32(op << 24);
+ osm_debug("Power...\n");
+
+ rc = i2o_msg_post_wait(c, msg, 60);
+ if (!rc)
+ dev->power = op;
+
+ return rc;
+};
+
+/**
+ * i2o_block_request_alloc - Allocate an I2O block request struct
+ *
+ * Allocates an I2O block request struct and initialize the list.
+ *
+ * Returns a i2o_block_request pointer on success or negative error code
+ * on failure.
+ */
+static inline struct i2o_block_request *i2o_block_request_alloc(void)
+{
+ struct i2o_block_request *ireq;
+
+ ireq = mempool_alloc(i2o_blk_req_pool.pool, GFP_ATOMIC);
+ if (!ireq)
+ return ERR_PTR(-ENOMEM);
+
+ INIT_LIST_HEAD(&ireq->queue);
+ sg_init_table(ireq->sg_table, I2O_MAX_PHYS_SEGMENTS);
+
+ return ireq;
+};
+
+/**
+ * i2o_block_request_free - Frees a I2O block request
+ * @ireq: I2O block request which should be freed
+ *
+ * Frees the allocated memory (give it back to the request mempool).
+ */
+static inline void i2o_block_request_free(struct i2o_block_request *ireq)
+{
+ mempool_free(ireq, i2o_blk_req_pool.pool);
+};
+
+/**
+ * i2o_block_sglist_alloc - Allocate the SG list and map it
+ * @c: I2O controller to which the request belongs
+ * @ireq: I2O block request
+ * @mptr: message body pointer
+ *
+ * Builds the SG list and map it to be accessible by the controller.
+ *
+ * Returns 0 on failure or 1 on success.
+ */
+static inline int i2o_block_sglist_alloc(struct i2o_controller *c,
+ struct i2o_block_request *ireq,
+ u32 ** mptr)
+{
+ int nents;
+ enum dma_data_direction direction;
+
+ ireq->dev = &c->pdev->dev;
+ nents = blk_rq_map_sg(ireq->req->q, ireq->req, ireq->sg_table);
+
+ if (rq_data_dir(ireq->req) == READ)
+ direction = PCI_DMA_FROMDEVICE;
+ else
+ direction = PCI_DMA_TODEVICE;
+
+ ireq->sg_nents = nents;
+
+ return i2o_dma_map_sg(c, ireq->sg_table, nents, direction, mptr);
+};
+
+/**
+ * i2o_block_sglist_free - Frees the SG list
+ * @ireq: I2O block request from which the SG should be freed
+ *
+ * Frees the SG list from the I2O block request.
+ */
+static inline void i2o_block_sglist_free(struct i2o_block_request *ireq)
+{
+ enum dma_data_direction direction;
+
+ if (rq_data_dir(ireq->req) == READ)
+ direction = PCI_DMA_FROMDEVICE;
+ else
+ direction = PCI_DMA_TODEVICE;
+
+ dma_unmap_sg(ireq->dev, ireq->sg_table, ireq->sg_nents, direction);
+};
+
+/**
+ * i2o_block_prep_req_fn - Allocates I2O block device specific struct
+ * @q: request queue for the request
+ * @req: the request to prepare
+ *
+ * Allocate the necessary i2o_block_request struct and connect it to
+ * the request. This is needed that we not lose the SG list later on.
+ *
+ * Returns BLKPREP_OK on success or BLKPREP_DEFER on failure.
+ */
+static int i2o_block_prep_req_fn(struct request_queue *q, struct request *req)
+{
+ struct i2o_block_device *i2o_blk_dev = q->queuedata;
+ struct i2o_block_request *ireq;
+
+ if (unlikely(!i2o_blk_dev)) {
+ osm_err("block device already removed\n");
+ return BLKPREP_KILL;
+ }
+
+ /* connect the i2o_block_request to the request */
+ if (!req->special) {
+ ireq = i2o_block_request_alloc();
+ if (IS_ERR(ireq)) {
+ osm_debug("unable to allocate i2o_block_request!\n");
+ return BLKPREP_DEFER;
+ }
+
+ ireq->i2o_blk_dev = i2o_blk_dev;
+ req->special = ireq;
+ ireq->req = req;
+ }
+ /* do not come back here */
+ req->cmd_flags |= REQ_DONTPREP;
+
+ return BLKPREP_OK;
+};
+
+/**
+ * i2o_block_delayed_request_fn - delayed request queue function
+ * @work: the delayed request with the queue to start
+ *
+ * If the request queue is stopped for a disk, and there is no open
+ * request, a new event is created, which calls this function to start
+ * the queue after I2O_BLOCK_REQUEST_TIME. Otherwise the queue will never
+ * be started again.
+ */
+static void i2o_block_delayed_request_fn(struct work_struct *work)
+{
+ struct i2o_block_delayed_request *dreq =
+ container_of(work, struct i2o_block_delayed_request,
+ work.work);
+ struct request_queue *q = dreq->queue;
+ unsigned long flags;
+
+ spin_lock_irqsave(q->queue_lock, flags);
+ blk_start_queue(q);
+ spin_unlock_irqrestore(q->queue_lock, flags);
+ kfree(dreq);
+};
+
+/**
+ * i2o_block_end_request - Post-processing of completed commands
+ * @req: request which should be completed
+ * @error: 0 for success, < 0 for error
+ * @nr_bytes: number of bytes to complete
+ *
+ * Mark the request as complete. The lock must not be held when entering.
+ *
+ */
+static void i2o_block_end_request(struct request *req, int error,
+ int nr_bytes)
+{
+ struct i2o_block_request *ireq = req->special;
+ struct i2o_block_device *dev = ireq->i2o_blk_dev;
+ struct request_queue *q = req->q;
+ unsigned long flags;
+
+ if (blk_end_request(req, error, nr_bytes))
+ if (error)
+ blk_end_request_all(req, -EIO);
+
+ spin_lock_irqsave(q->queue_lock, flags);
+
+ if (likely(dev)) {
+ dev->open_queue_depth--;
+ list_del(&ireq->queue);
+ }
+
+ blk_start_queue(q);
+
+ spin_unlock_irqrestore(q->queue_lock, flags);
+
+ i2o_block_sglist_free(ireq);
+ i2o_block_request_free(ireq);
+};
+
+/**
+ * i2o_block_reply - Block OSM reply handler.
+ * @c: I2O controller from which the message arrives
+ * @m: message id of reply
+ * @msg: the actual I2O message reply
+ *
+ * This function gets all the message replies.
+ *
+ */
+static int i2o_block_reply(struct i2o_controller *c, u32 m,
+ struct i2o_message *msg)
+{
+ struct request *req;
+ int error = 0;
+
+ req = i2o_cntxt_list_get(c, le32_to_cpu(msg->u.s.tcntxt));
+ if (unlikely(!req)) {
+ osm_err("NULL reply received!\n");
+ return -1;
+ }
+
+ /*
+ * Lets see what is cooking. We stuffed the
+ * request in the context.
+ */
+
+ if ((le32_to_cpu(msg->body[0]) >> 24) != 0) {
+ u32 status = le32_to_cpu(msg->body[0]);
+ /*
+ * Device not ready means two things. One is that the
+ * the thing went offline (but not a removal media)
+ *
+ * The second is that you have a SuperTrak 100 and the
+ * firmware got constipated. Unlike standard i2o card
+ * setups the supertrak returns an error rather than
+ * blocking for the timeout in these cases.
+ *
+ * Don't stick a supertrak100 into cache aggressive modes
+ */
+
+ osm_err("TID %03x error status: 0x%02x, detailed status: "
+ "0x%04x\n", (le32_to_cpu(msg->u.head[1]) >> 12 & 0xfff),
+ status >> 24, status & 0xffff);
+
+ req->errors++;
+
+ error = -EIO;
+ }
+
+ i2o_block_end_request(req, error, le32_to_cpu(msg->body[1]));
+
+ return 1;
+};
+
+static void i2o_block_event(struct work_struct *work)
+{
+ struct i2o_event *evt = container_of(work, struct i2o_event, work);
+ osm_debug("event received\n");
+ kfree(evt);
+};
+
+/*
+ * SCSI-CAM for ioctl geometry mapping
+ * Duplicated with SCSI - this should be moved into somewhere common
+ * perhaps genhd ?
+ *
+ * LBA -> CHS mapping table taken from:
+ *
+ * "Incorporating the I2O Architecture into BIOS for Intel Architecture
+ * Platforms"
+ *
+ * This is an I2O document that is only available to I2O members,
+ * not developers.
+ *
+ * From my understanding, this is how all the I2O cards do this
+ *
+ * Disk Size | Sectors | Heads | Cylinders
+ * ---------------+---------+-------+-------------------
+ * 1 < X <= 528M | 63 | 16 | X/(63 * 16 * 512)
+ * 528M < X <= 1G | 63 | 32 | X/(63 * 32 * 512)
+ * 1 < X <528M | 63 | 16 | X/(63 * 16 * 512)
+ * 1 < X <528M | 63 | 16 | X/(63 * 16 * 512)
+ *
+ */
+#define BLOCK_SIZE_528M 1081344
+#define BLOCK_SIZE_1G 2097152
+#define BLOCK_SIZE_21G 4403200
+#define BLOCK_SIZE_42G 8806400
+#define BLOCK_SIZE_84G 17612800
+
+static void i2o_block_biosparam(unsigned long capacity, unsigned short *cyls,
+ unsigned char *hds, unsigned char *secs)
+{
+ unsigned long heads, sectors, cylinders;
+
+ sectors = 63L; /* Maximize sectors per track */
+ if (capacity <= BLOCK_SIZE_528M)
+ heads = 16;
+ else if (capacity <= BLOCK_SIZE_1G)
+ heads = 32;
+ else if (capacity <= BLOCK_SIZE_21G)
+ heads = 64;
+ else if (capacity <= BLOCK_SIZE_42G)
+ heads = 128;
+ else
+ heads = 255;
+
+ cylinders = (unsigned long)capacity / (heads * sectors);
+
+ *cyls = (unsigned short)cylinders; /* Stuff return values */
+ *secs = (unsigned char)sectors;
+ *hds = (unsigned char)heads;
+}
+
+/**
+ * i2o_block_open - Open the block device
+ * @bdev: block device being opened
+ * @mode: file open mode
+ *
+ * Power up the device, mount and lock the media. This function is called,
+ * if the block device is opened for access.
+ *
+ * Returns 0 on success or negative error code on failure.
+ */
+static int i2o_block_open(struct block_device *bdev, fmode_t mode)
+{
+ struct i2o_block_device *dev = bdev->bd_disk->private_data;
+
+ if (!dev->i2o_dev)
+ return -ENODEV;
+
+ mutex_lock(&i2o_block_mutex);
+ if (dev->power > 0x1f)
+ i2o_block_device_power(dev, 0x02);
+
+ i2o_block_device_mount(dev->i2o_dev, -1);
+
+ i2o_block_device_lock(dev->i2o_dev, -1);
+
+ osm_debug("Ready.\n");
+ mutex_unlock(&i2o_block_mutex);
+
+ return 0;
+};
+
+/**
+ * i2o_block_release - Release the I2O block device
+ * @disk: gendisk device being released
+ * @mode: file open mode
+ *
+ * Unlock and unmount the media, and power down the device. Gets called if
+ * the block device is closed.
+ */
+static void i2o_block_release(struct gendisk *disk, fmode_t mode)
+{
+ struct i2o_block_device *dev = disk->private_data;
+ u8 operation;
+
+ /*
+ * This is to deal with the case of an application
+ * opening a device and then the device disappears while
+ * it's in use, and then the application tries to release
+ * it. ex: Unmounting a deleted RAID volume at reboot.
+ * If we send messages, it will just cause FAILs since
+ * the TID no longer exists.
+ */
+ if (!dev->i2o_dev)
+ return;
+
+ mutex_lock(&i2o_block_mutex);
+ i2o_block_device_flush(dev->i2o_dev);
+
+ i2o_block_device_unlock(dev->i2o_dev, -1);
+
+ if (dev->flags & (1 << 3 | 1 << 4)) /* Removable */
+ operation = 0x21;
+ else
+ operation = 0x24;
+
+ i2o_block_device_power(dev, operation);
+ mutex_unlock(&i2o_block_mutex);
+}
+
+static int i2o_block_getgeo(struct block_device *bdev, struct hd_geometry *geo)
+{
+ i2o_block_biosparam(get_capacity(bdev->bd_disk),
+ &geo->cylinders, &geo->heads, &geo->sectors);
+ return 0;
+}
+
+/**
+ * i2o_block_ioctl - Issue device specific ioctl calls.
+ * @bdev: block device being opened
+ * @mode: file open mode
+ * @cmd: ioctl command
+ * @arg: arg
+ *
+ * Handles ioctl request for the block device.
+ *
+ * Return 0 on success or negative error on failure.
+ */
+static int i2o_block_ioctl(struct block_device *bdev, fmode_t mode,
+ unsigned int cmd, unsigned long arg)
+{
+ struct gendisk *disk = bdev->bd_disk;
+ struct i2o_block_device *dev = disk->private_data;
+ int ret = -ENOTTY;
+
+ /* Anyone capable of this syscall can do *real bad* things */
+
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+
+ mutex_lock(&i2o_block_mutex);
+ switch (cmd) {
+ case BLKI2OGRSTRAT:
+ ret = put_user(dev->rcache, (int __user *)arg);
+ break;
+ case BLKI2OGWSTRAT:
+ ret = put_user(dev->wcache, (int __user *)arg);
+ break;
+ case BLKI2OSRSTRAT:
+ ret = -EINVAL;
+ if (arg < 0 || arg > CACHE_SMARTFETCH)
+ break;
+ dev->rcache = arg;
+ ret = 0;
+ break;
+ case BLKI2OSWSTRAT:
+ ret = -EINVAL;
+ if (arg != 0
+ && (arg < CACHE_WRITETHROUGH || arg > CACHE_SMARTBACK))
+ break;
+ dev->wcache = arg;
+ ret = 0;
+ break;
+ }
+ mutex_unlock(&i2o_block_mutex);
+
+ return ret;
+};
+
+/**
+ * i2o_block_check_events - Have we seen a media change?
+ * @disk: gendisk which should be verified
+ * @clearing: events being cleared
+ *
+ * Verifies if the media has changed.
+ *
+ * Returns 1 if the media was changed or 0 otherwise.
+ */
+static unsigned int i2o_block_check_events(struct gendisk *disk,
+ unsigned int clearing)
+{
+ struct i2o_block_device *p = disk->private_data;
+
+ if (p->media_change_flag) {
+ p->media_change_flag = 0;
+ return DISK_EVENT_MEDIA_CHANGE;
+ }
+ return 0;
+}
+
+/**
+ * i2o_block_transfer - Transfer a request to/from the I2O controller
+ * @req: the request which should be transferred
+ *
+ * This function converts the request into a I2O message. The necessary
+ * DMA buffers are allocated and after everything is setup post the message
+ * to the I2O controller. No cleanup is done by this function. It is done
+ * on the interrupt side when the reply arrives.
+ *
+ * Return 0 on success or negative error code on failure.
+ */
+static int i2o_block_transfer(struct request *req)
+{
+ struct i2o_block_device *dev = req->rq_disk->private_data;
+ struct i2o_controller *c;
+ u32 tid;
+ struct i2o_message *msg;
+ u32 *mptr;
+ struct i2o_block_request *ireq = req->special;
+ u32 tcntxt;
+ u32 sgl_offset = SGL_OFFSET_8;
+ u32 ctl_flags = 0x00000000;
+ int rc;
+ u32 cmd;
+
+ if (unlikely(!dev->i2o_dev)) {
+ osm_err("transfer to removed drive\n");
+ rc = -ENODEV;
+ goto exit;
+ }
+
+ tid = dev->i2o_dev->lct_data.tid;
+ c = dev->i2o_dev->iop;
+
+ msg = i2o_msg_get(c);
+ if (IS_ERR(msg)) {
+ rc = PTR_ERR(msg);
+ goto exit;
+ }
+
+ tcntxt = i2o_cntxt_list_add(c, req);
+ if (!tcntxt) {
+ rc = -ENOMEM;
+ goto nop_msg;
+ }
+
+ msg->u.s.icntxt = cpu_to_le32(i2o_block_driver.context);
+ msg->u.s.tcntxt = cpu_to_le32(tcntxt);
+
+ mptr = &msg->body[0];
+
+ if (rq_data_dir(req) == READ) {
+ cmd = I2O_CMD_BLOCK_READ << 24;
+
+ switch (dev->rcache) {
+ case CACHE_PREFETCH:
+ ctl_flags = 0x201F0008;
+ break;
+
+ case CACHE_SMARTFETCH:
+ if (blk_rq_sectors(req) > 16)
+ ctl_flags = 0x201F0008;
+ else
+ ctl_flags = 0x001F0000;
+ break;
+
+ default:
+ break;
+ }
+ } else {
+ cmd = I2O_CMD_BLOCK_WRITE << 24;
+
+ switch (dev->wcache) {
+ case CACHE_WRITETHROUGH:
+ ctl_flags = 0x001F0008;
+ break;
+ case CACHE_WRITEBACK:
+ ctl_flags = 0x001F0010;
+ break;
+ case CACHE_SMARTBACK:
+ if (blk_rq_sectors(req) > 16)
+ ctl_flags = 0x001F0004;
+ else
+ ctl_flags = 0x001F0010;
+ break;
+ case CACHE_SMARTTHROUGH:
+ if (blk_rq_sectors(req) > 16)
+ ctl_flags = 0x001F0004;
+ else
+ ctl_flags = 0x001F0010;
+ default:
+ break;
+ }
+ }
+
+#ifdef CONFIG_I2O_EXT_ADAPTEC
+ if (c->adaptec) {
+ u8 cmd[10];
+ u32 scsi_flags;
+ u16 hwsec;
+
+ hwsec = queue_logical_block_size(req->q) >> KERNEL_SECTOR_SHIFT;
+ memset(cmd, 0, 10);
+
+ sgl_offset = SGL_OFFSET_12;
+
+ msg->u.head[1] =
+ cpu_to_le32(I2O_CMD_PRIVATE << 24 | HOST_TID << 12 | tid);
+
+ *mptr++ = cpu_to_le32(I2O_VENDOR_DPT << 16 | I2O_CMD_SCSI_EXEC);
+ *mptr++ = cpu_to_le32(tid);
+
+ /*
+ * ENABLE_DISCONNECT
+ * SIMPLE_TAG
+ * RETURN_SENSE_DATA_IN_REPLY_MESSAGE_FRAME
+ */
+ if (rq_data_dir(req) == READ) {
+ cmd[0] = READ_10;
+ scsi_flags = 0x60a0000a;
+ } else {
+ cmd[0] = WRITE_10;
+ scsi_flags = 0xa0a0000a;
+ }
+
+ *mptr++ = cpu_to_le32(scsi_flags);
+
+ *((u32 *) & cmd[2]) = cpu_to_be32(blk_rq_pos(req) * hwsec);
+ *((u16 *) & cmd[7]) = cpu_to_be16(blk_rq_sectors(req) * hwsec);
+
+ memcpy(mptr, cmd, 10);
+ mptr += 4;
+ *mptr++ = cpu_to_le32(blk_rq_bytes(req));
+ } else
+#endif
+ {
+ msg->u.head[1] = cpu_to_le32(cmd | HOST_TID << 12 | tid);
+ *mptr++ = cpu_to_le32(ctl_flags);
+ *mptr++ = cpu_to_le32(blk_rq_bytes(req));
+ *mptr++ =
+ cpu_to_le32((u32) (blk_rq_pos(req) << KERNEL_SECTOR_SHIFT));
+ *mptr++ =
+ cpu_to_le32(blk_rq_pos(req) >> (32 - KERNEL_SECTOR_SHIFT));
+ }
+
+ if (!i2o_block_sglist_alloc(c, ireq, &mptr)) {
+ rc = -ENOMEM;
+ goto context_remove;
+ }
+
+ msg->u.head[0] =
+ cpu_to_le32(I2O_MESSAGE_SIZE(mptr - &msg->u.head[0]) | sgl_offset);
+
+ list_add_tail(&ireq->queue, &dev->open_queue);
+ dev->open_queue_depth++;
+
+ i2o_msg_post(c, msg);
+
+ return 0;
+
+context_remove:
+ i2o_cntxt_list_remove(c, req);
+
+nop_msg:
+ i2o_msg_nop(c, msg);
+
+exit:
+ return rc;
+};
+
+/**
+ * i2o_block_request_fn - request queue handling function
+ * @q: request queue from which the request could be fetched
+ *
+ * Takes the next request from the queue, transfers it and if no error
+ * occurs dequeue it from the queue. On arrival of the reply the message
+ * will be processed further. If an error occurs requeue the request.
+ */
+static void i2o_block_request_fn(struct request_queue *q)
+{
+ struct request *req;
+
+ while ((req = blk_peek_request(q)) != NULL) {
+ if (req->cmd_type == REQ_TYPE_FS) {
+ struct i2o_block_delayed_request *dreq;
+ struct i2o_block_request *ireq = req->special;
+ unsigned int queue_depth;
+
+ queue_depth = ireq->i2o_blk_dev->open_queue_depth;
+
+ if (queue_depth < I2O_BLOCK_MAX_OPEN_REQUESTS) {
+ if (!i2o_block_transfer(req)) {
+ blk_start_request(req);
+ continue;
+ } else
+ osm_info("transfer error\n");
+ }
+
+ if (queue_depth)
+ break;
+
+ /* stop the queue and retry later */
+ dreq = kmalloc(sizeof(*dreq), GFP_ATOMIC);
+ if (!dreq)
+ continue;
+
+ dreq->queue = q;
+ INIT_DELAYED_WORK(&dreq->work,
+ i2o_block_delayed_request_fn);
+
+ if (!queue_delayed_work(i2o_block_driver.event_queue,
+ &dreq->work,
+ I2O_BLOCK_RETRY_TIME))
+ kfree(dreq);
+ else {
+ blk_stop_queue(q);
+ break;
+ }
+ } else {
+ blk_start_request(req);
+ __blk_end_request_all(req, -EIO);
+ }
+ }
+};
+
+/* I2O Block device operations definition */
+static const struct block_device_operations i2o_block_fops = {
+ .owner = THIS_MODULE,
+ .open = i2o_block_open,
+ .release = i2o_block_release,
+ .ioctl = i2o_block_ioctl,
+ .compat_ioctl = i2o_block_ioctl,
+ .getgeo = i2o_block_getgeo,
+ .check_events = i2o_block_check_events,
+};
+
+/**
+ * i2o_block_device_alloc - Allocate memory for a I2O Block device
+ *
+ * Allocate memory for the i2o_block_device struct, gendisk and request
+ * queue and initialize them as far as no additional information is needed.
+ *
+ * Returns a pointer to the allocated I2O Block device on success or a
+ * negative error code on failure.
+ */
+static struct i2o_block_device *i2o_block_device_alloc(void)
+{
+ struct i2o_block_device *dev;
+ struct gendisk *gd;
+ struct request_queue *queue;
+ int rc;
+
+ dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+ if (!dev) {
+ osm_err("Insufficient memory to allocate I2O Block disk.\n");
+ rc = -ENOMEM;
+ goto exit;
+ }
+
+ INIT_LIST_HEAD(&dev->open_queue);
+ spin_lock_init(&dev->lock);
+ dev->rcache = CACHE_PREFETCH;
+ dev->wcache = CACHE_WRITEBACK;
+
+ /* allocate a gendisk with 16 partitions */
+ gd = alloc_disk(16);
+ if (!gd) {
+ osm_err("Insufficient memory to allocate gendisk.\n");
+ rc = -ENOMEM;
+ goto cleanup_dev;
+ }
+
+ /* initialize the request queue */
+ queue = blk_init_queue(i2o_block_request_fn, &dev->lock);
+ if (!queue) {
+ osm_err("Insufficient memory to allocate request queue.\n");
+ rc = -ENOMEM;
+ goto cleanup_queue;
+ }
+
+ blk_queue_prep_rq(queue, i2o_block_prep_req_fn);
+
+ gd->major = I2O_MAJOR;
+ gd->queue = queue;
+ gd->fops = &i2o_block_fops;
+ gd->private_data = dev;
+
+ dev->gd = gd;
+
+ return dev;
+
+cleanup_queue:
+ put_disk(gd);
+
+cleanup_dev:
+ kfree(dev);
+
+exit:
+ return ERR_PTR(rc);
+};
+
+/**
+ * i2o_block_probe - verify if dev is a I2O Block device and install it
+ * @dev: device to verify if it is a I2O Block device
+ *
+ * We only verify if the user_tid of the device is 0xfff and then install
+ * the device. Otherwise it is used by some other device (e. g. RAID).
+ *
+ * Returns 0 on success or negative error code on failure.
+ */
+static int i2o_block_probe(struct device *dev)
+{
+ struct i2o_device *i2o_dev = to_i2o_device(dev);
+ struct i2o_controller *c = i2o_dev->iop;
+ struct i2o_block_device *i2o_blk_dev;
+ struct gendisk *gd;
+ struct request_queue *queue;
+ static int unit;
+ int rc;
+ u64 size;
+ u32 blocksize;
+ u16 body_size = 4;
+ u16 power;
+ unsigned short max_sectors;
+
+#ifdef CONFIG_I2O_EXT_ADAPTEC
+ if (c->adaptec)
+ body_size = 8;
+#endif
+
+ if (c->limit_sectors)
+ max_sectors = I2O_MAX_SECTORS_LIMITED;
+ else
+ max_sectors = I2O_MAX_SECTORS;
+
+ /* skip devices which are used by IOP */
+ if (i2o_dev->lct_data.user_tid != 0xfff) {
+ osm_debug("skipping used device %03x\n", i2o_dev->lct_data.tid);
+ return -ENODEV;
+ }
+
+ if (i2o_device_claim(i2o_dev)) {
+ osm_warn("Unable to claim device. Installation aborted\n");
+ rc = -EFAULT;
+ goto exit;
+ }
+
+ i2o_blk_dev = i2o_block_device_alloc();
+ if (IS_ERR(i2o_blk_dev)) {
+ osm_err("could not alloc a new I2O block device");
+ rc = PTR_ERR(i2o_blk_dev);
+ goto claim_release;
+ }
+
+ i2o_blk_dev->i2o_dev = i2o_dev;
+ dev_set_drvdata(dev, i2o_blk_dev);
+
+ /* setup gendisk */
+ gd = i2o_blk_dev->gd;
+ gd->first_minor = unit << 4;
+ sprintf(gd->disk_name, "i2o/hd%c", 'a' + unit);
+ gd->driverfs_dev = &i2o_dev->device;
+
+ /* setup request queue */
+ queue = gd->queue;
+ queue->queuedata = i2o_blk_dev;
+
+ blk_queue_max_hw_sectors(queue, max_sectors);
+ blk_queue_max_segments(queue, i2o_sg_tablesize(c, body_size));
+
+ osm_debug("max sectors = %d\n", queue->max_sectors);
+ osm_debug("phys segments = %d\n", queue->max_phys_segments);
+ osm_debug("max hw segments = %d\n", queue->max_hw_segments);
+
+ /*
+ * Ask for the current media data. If that isn't supported
+ * then we ask for the device capacity data
+ */
+ if (!i2o_parm_field_get(i2o_dev, 0x0004, 1, &blocksize, 4) ||
+ !i2o_parm_field_get(i2o_dev, 0x0000, 3, &blocksize, 4)) {
+ blk_queue_logical_block_size(queue, le32_to_cpu(blocksize));
+ } else
+ osm_warn("unable to get blocksize of %s\n", gd->disk_name);
+
+ if (!i2o_parm_field_get(i2o_dev, 0x0004, 0, &size, 8) ||
+ !i2o_parm_field_get(i2o_dev, 0x0000, 4, &size, 8)) {
+ set_capacity(gd, le64_to_cpu(size) >> KERNEL_SECTOR_SHIFT);
+ } else
+ osm_warn("could not get size of %s\n", gd->disk_name);
+
+ if (!i2o_parm_field_get(i2o_dev, 0x0000, 2, &power, 2))
+ i2o_blk_dev->power = power;
+
+ i2o_event_register(i2o_dev, &i2o_block_driver, 0, 0xffffffff);
+
+ add_disk(gd);
+
+ unit++;
+
+ osm_info("device added (TID: %03x): %s\n", i2o_dev->lct_data.tid,
+ i2o_blk_dev->gd->disk_name);
+
+ return 0;
+
+claim_release:
+ i2o_device_claim_release(i2o_dev);
+
+exit:
+ return rc;
+};
+
+/* Block OSM driver struct */
+static struct i2o_driver i2o_block_driver = {
+ .name = OSM_NAME,
+ .event = i2o_block_event,
+ .reply = i2o_block_reply,
+ .classes = i2o_block_class_id,
+ .driver = {
+ .probe = i2o_block_probe,
+ .remove = i2o_block_remove,
+ },
+};
+
+/**
+ * i2o_block_init - Block OSM initialization function
+ *
+ * Allocate the slab and mempool for request structs, registers i2o_block
+ * block device and finally register the Block OSM in the I2O core.
+ *
+ * Returns 0 on success or negative error code on failure.
+ */
+static int __init i2o_block_init(void)
+{
+ int rc;
+ int size;
+
+ printk(KERN_INFO OSM_DESCRIPTION " v" OSM_VERSION "\n");
+
+ /* Allocate request mempool and slab */
+ size = sizeof(struct i2o_block_request);
+ i2o_blk_req_pool.slab = kmem_cache_create("i2o_block_req", size, 0,
+ SLAB_HWCACHE_ALIGN, NULL);
+ if (!i2o_blk_req_pool.slab) {
+ osm_err("can't init request slab\n");
+ rc = -ENOMEM;
+ goto exit;
+ }
+
+ i2o_blk_req_pool.pool =
+ mempool_create_slab_pool(I2O_BLOCK_REQ_MEMPOOL_SIZE,
+ i2o_blk_req_pool.slab);
+ if (!i2o_blk_req_pool.pool) {
+ osm_err("can't init request mempool\n");
+ rc = -ENOMEM;
+ goto free_slab;
+ }
+
+ /* Register the block device interfaces */
+ rc = register_blkdev(I2O_MAJOR, "i2o_block");
+ if (rc) {
+ osm_err("unable to register block device\n");
+ goto free_mempool;
+ }
+#ifdef MODULE
+ osm_info("registered device at major %d\n", I2O_MAJOR);
+#endif
+
+ /* Register Block OSM into I2O core */
+ rc = i2o_driver_register(&i2o_block_driver);
+ if (rc) {
+ osm_err("Could not register Block driver\n");
+ goto unregister_blkdev;
+ }
+
+ return 0;
+
+unregister_blkdev:
+ unregister_blkdev(I2O_MAJOR, "i2o_block");
+
+free_mempool:
+ mempool_destroy(i2o_blk_req_pool.pool);
+
+free_slab:
+ kmem_cache_destroy(i2o_blk_req_pool.slab);
+
+exit:
+ return rc;
+};
+
+/**
+ * i2o_block_exit - Block OSM exit function
+ *
+ * Unregisters Block OSM from I2O core, unregisters i2o_block block device
+ * and frees the mempool and slab.
+ */
+static void __exit i2o_block_exit(void)
+{
+ /* Unregister I2O Block OSM from I2O core */
+ i2o_driver_unregister(&i2o_block_driver);
+
+ /* Unregister block device */
+ unregister_blkdev(I2O_MAJOR, "i2o_block");
+
+ /* Free request mempool and slab */
+ mempool_destroy(i2o_blk_req_pool.pool);
+ kmem_cache_destroy(i2o_blk_req_pool.slab);
+};
+
+MODULE_AUTHOR("Red Hat");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION(OSM_DESCRIPTION);
+MODULE_VERSION(OSM_VERSION);
+
+module_init(i2o_block_init);
+module_exit(i2o_block_exit);
diff --git a/drivers/staging/i2o/i2o_block.h b/drivers/staging/i2o/i2o_block.h
new file mode 100644
index 000000000..cf8873cbc
--- /dev/null
+++ b/drivers/staging/i2o/i2o_block.h
@@ -0,0 +1,103 @@
+/*
+ * Block OSM structures/API
+ *
+ * Copyright (C) 1999-2002 Red Hat Software
+ *
+ * Written by Alan Cox, Building Number Three Ltd
+ *
+ * This program is free software; you can redistribute it and/or 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.
+ *
+ * For the purpose of avoiding doubt the preferred form of the work
+ * for making modifications shall be a standards compliant form such
+ * gzipped tar and not one requiring a proprietary or patent encumbered
+ * tool to unpack.
+ *
+ * Fixes/additions:
+ * Steve Ralston:
+ * Multiple device handling error fixes,
+ * Added a queue depth.
+ * Alan Cox:
+ * FC920 has an rmw bug. Dont or in the end marker.
+ * Removed queue walk, fixed for 64bitness.
+ * Rewrote much of the code over time
+ * Added indirect block lists
+ * Handle 64K limits on many controllers
+ * Don't use indirects on the Promise (breaks)
+ * Heavily chop down the queue depths
+ * Deepak Saxena:
+ * Independent queues per IOP
+ * Support for dynamic device creation/deletion
+ * Code cleanup
+ * Support for larger I/Os through merge* functions
+ * (taken from DAC960 driver)
+ * Boji T Kannanthanam:
+ * Set the I2O Block devices to be detected in increasing
+ * order of TIDs during boot.
+ * Search and set the I2O block device that we boot off
+ * from as the first device to be claimed (as /dev/i2o/hda)
+ * Properly attach/detach I2O gendisk structure from the
+ * system gendisk list. The I2O block devices now appear in
+ * /proc/partitions.
+ * Markus Lidel <Markus.Lidel@shadowconnect.com>:
+ * Minor bugfixes for 2.6.
+ */
+
+#ifndef I2O_BLOCK_OSM_H
+#define I2O_BLOCK_OSM_H
+
+#define I2O_BLOCK_RETRY_TIME HZ/4
+#define I2O_BLOCK_MAX_OPEN_REQUESTS 50
+
+/* request queue sizes */
+#define I2O_BLOCK_REQ_MEMPOOL_SIZE 32
+
+#define KERNEL_SECTOR_SHIFT 9
+#define KERNEL_SECTOR_SIZE (1 << KERNEL_SECTOR_SHIFT)
+
+/* I2O Block OSM mempool struct */
+struct i2o_block_mempool {
+ struct kmem_cache *slab;
+ mempool_t *pool;
+};
+
+/* I2O Block device descriptor */
+struct i2o_block_device {
+ struct i2o_device *i2o_dev; /* pointer to I2O device */
+ struct gendisk *gd;
+ spinlock_t lock; /* queue lock */
+ struct list_head open_queue; /* list of transferred, but unfinished
+ requests */
+ unsigned int open_queue_depth; /* number of requests in the queue */
+
+ int rcache; /* read cache flags */
+ int wcache; /* write cache flags */
+ int flags;
+ u16 power; /* power state */
+ int media_change_flag; /* media changed flag */
+};
+
+/* I2O Block device request */
+struct i2o_block_request {
+ struct list_head queue;
+ struct request *req; /* corresponding request */
+ struct i2o_block_device *i2o_blk_dev; /* I2O block device */
+ struct device *dev; /* device used for DMA */
+ int sg_nents; /* number of SG elements */
+ struct scatterlist sg_table[I2O_MAX_PHYS_SEGMENTS]; /* SG table */
+};
+
+/* I2O Block device delayed request */
+struct i2o_block_delayed_request {
+ struct delayed_work work;
+ struct request_queue *queue;
+};
+
+#endif
diff --git a/drivers/staging/i2o/i2o_config.c b/drivers/staging/i2o/i2o_config.c
new file mode 100644
index 000000000..cd7ca5eb1
--- /dev/null
+++ b/drivers/staging/i2o/i2o_config.c
@@ -0,0 +1,1162 @@
+/*
+ * I2O Configuration Interface Driver
+ *
+ * (C) Copyright 1999-2002 Red Hat
+ *
+ * Written by Alan Cox, Building Number Three Ltd
+ *
+ * Fixes/additions:
+ * Deepak Saxena (04/20/1999):
+ * Added basic ioctl() support
+ * Deepak Saxena (06/07/1999):
+ * Added software download ioctl (still testing)
+ * Auvo Häkkinen (09/10/1999):
+ * Changes to i2o_cfg_reply(), ioctl_parms()
+ * Added ioct_validate()
+ * Taneli Vähäkangas (09/30/1999):
+ * Fixed ioctl_swdl()
+ * Taneli Vähäkangas (10/04/1999):
+ * Changed ioctl_swdl(), implemented ioctl_swul() and ioctl_swdel()
+ * Deepak Saxena (11/18/1999):
+ * Added event managmenet support
+ * Alan Cox <alan@lxorguk.ukuu.org.uk>:
+ * 2.4 rewrite ported to 2.5
+ * Markus Lidel <Markus.Lidel@shadowconnect.com>:
+ * Added pass-thru support for Adaptec's raidutils
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/miscdevice.h>
+#include <linux/mutex.h>
+#include <linux/compat.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+
+#include "core.h"
+
+#define SG_TABLESIZE 30
+
+static DEFINE_MUTEX(i2o_cfg_mutex);
+static long i2o_cfg_ioctl(struct file *, unsigned int, unsigned long);
+
+static spinlock_t i2o_config_lock;
+
+#define MODINC(x,y) ((x) = ((x) + 1) % (y))
+
+struct sg_simple_element {
+ u32 flag_count;
+ u32 addr_bus;
+};
+
+struct i2o_cfg_info {
+ struct file *fp;
+ struct fasync_struct *fasync;
+ struct i2o_evt_info event_q[I2O_EVT_Q_LEN];
+ u16 q_in; // Queue head index
+ u16 q_out; // Queue tail index
+ u16 q_len; // Queue length
+ u16 q_lost; // Number of lost events
+ ulong q_id; // Event queue ID...used as tx_context
+ struct i2o_cfg_info *next;
+};
+static struct i2o_cfg_info *open_files = NULL;
+static ulong i2o_cfg_info_id;
+
+static int i2o_cfg_getiops(unsigned long arg)
+{
+ struct i2o_controller *c;
+ u8 __user *user_iop_table = (void __user *)arg;
+ u8 tmp[MAX_I2O_CONTROLLERS];
+ int ret = 0;
+
+ memset(tmp, 0, MAX_I2O_CONTROLLERS);
+
+ list_for_each_entry(c, &i2o_controllers, list)
+ tmp[c->unit] = 1;
+
+ if (copy_to_user(user_iop_table, tmp, MAX_I2O_CONTROLLERS))
+ ret = -EFAULT;
+
+ return ret;
+};
+
+static int i2o_cfg_gethrt(unsigned long arg)
+{
+ struct i2o_controller *c;
+ struct i2o_cmd_hrtlct __user *cmd = (struct i2o_cmd_hrtlct __user *)arg;
+ struct i2o_cmd_hrtlct kcmd;
+ i2o_hrt *hrt;
+ int len;
+ u32 reslen;
+ int ret = 0;
+
+ if (copy_from_user(&kcmd, cmd, sizeof(struct i2o_cmd_hrtlct)))
+ return -EFAULT;
+
+ if (get_user(reslen, kcmd.reslen) < 0)
+ return -EFAULT;
+
+ if (kcmd.resbuf == NULL)
+ return -EFAULT;
+
+ c = i2o_find_iop(kcmd.iop);
+ if (!c)
+ return -ENXIO;
+
+ hrt = (i2o_hrt *) c->hrt.virt;
+
+ len = 8 + ((hrt->entry_len * hrt->num_entries) << 2);
+
+ if (put_user(len, kcmd.reslen))
+ ret = -EFAULT;
+ else if (len > reslen)
+ ret = -ENOBUFS;
+ else if (copy_to_user(kcmd.resbuf, (void *)hrt, len))
+ ret = -EFAULT;
+
+ return ret;
+};
+
+static int i2o_cfg_getlct(unsigned long arg)
+{
+ struct i2o_controller *c;
+ struct i2o_cmd_hrtlct __user *cmd = (struct i2o_cmd_hrtlct __user *)arg;
+ struct i2o_cmd_hrtlct kcmd;
+ i2o_lct *lct;
+ int len;
+ int ret = 0;
+ u32 reslen;
+
+ if (copy_from_user(&kcmd, cmd, sizeof(struct i2o_cmd_hrtlct)))
+ return -EFAULT;
+
+ if (get_user(reslen, kcmd.reslen) < 0)
+ return -EFAULT;
+
+ if (kcmd.resbuf == NULL)
+ return -EFAULT;
+
+ c = i2o_find_iop(kcmd.iop);
+ if (!c)
+ return -ENXIO;
+
+ lct = (i2o_lct *) c->lct;
+
+ len = (unsigned int)lct->table_size << 2;
+ if (put_user(len, kcmd.reslen))
+ ret = -EFAULT;
+ else if (len > reslen)
+ ret = -ENOBUFS;
+ else if (copy_to_user(kcmd.resbuf, lct, len))
+ ret = -EFAULT;
+
+ return ret;
+};
+
+static int i2o_cfg_parms(unsigned long arg, unsigned int type)
+{
+ int ret = 0;
+ struct i2o_controller *c;
+ struct i2o_device *dev;
+ struct i2o_cmd_psetget __user *cmd =
+ (struct i2o_cmd_psetget __user *)arg;
+ struct i2o_cmd_psetget kcmd;
+ u32 reslen;
+ u8 *ops;
+ u8 *res;
+ int len = 0;
+
+ u32 i2o_cmd = (type == I2OPARMGET ?
+ I2O_CMD_UTIL_PARAMS_GET : I2O_CMD_UTIL_PARAMS_SET);
+
+ if (copy_from_user(&kcmd, cmd, sizeof(struct i2o_cmd_psetget)))
+ return -EFAULT;
+
+ if (get_user(reslen, kcmd.reslen))
+ return -EFAULT;
+
+ c = i2o_find_iop(kcmd.iop);
+ if (!c)
+ return -ENXIO;
+
+ dev = i2o_iop_find_device(c, kcmd.tid);
+ if (!dev)
+ return -ENXIO;
+
+ /*
+ * Stop users being able to try and allocate arbitrary amounts
+ * of DMA space. 64K is way more than sufficient for this.
+ */
+ if (kcmd.oplen > 65536)
+ return -EMSGSIZE;
+
+ ops = memdup_user(kcmd.opbuf, kcmd.oplen);
+ if (IS_ERR(ops))
+ return PTR_ERR(ops);
+
+ /*
+ * It's possible to have a _very_ large table
+ * and that the user asks for all of it at once...
+ */
+ res = kmalloc(65536, GFP_KERNEL);
+ if (!res) {
+ kfree(ops);
+ return -ENOMEM;
+ }
+
+ len = i2o_parm_issue(dev, i2o_cmd, ops, kcmd.oplen, res, 65536);
+ kfree(ops);
+
+ if (len < 0) {
+ kfree(res);
+ return -EAGAIN;
+ }
+
+ if (put_user(len, kcmd.reslen))
+ ret = -EFAULT;
+ else if (len > reslen)
+ ret = -ENOBUFS;
+ else if (copy_to_user(kcmd.resbuf, res, len))
+ ret = -EFAULT;
+
+ kfree(res);
+
+ return ret;
+};
+
+static int i2o_cfg_swdl(unsigned long arg)
+{
+ struct i2o_sw_xfer kxfer;
+ struct i2o_sw_xfer __user *pxfer = (struct i2o_sw_xfer __user *)arg;
+ unsigned char maxfrag = 0, curfrag = 1;
+ struct i2o_dma buffer;
+ struct i2o_message *msg;
+ unsigned int status = 0, swlen = 0, fragsize = 8192;
+ struct i2o_controller *c;
+
+ if (copy_from_user(&kxfer, pxfer, sizeof(struct i2o_sw_xfer)))
+ return -EFAULT;
+
+ if (get_user(swlen, kxfer.swlen) < 0)
+ return -EFAULT;
+
+ if (get_user(maxfrag, kxfer.maxfrag) < 0)
+ return -EFAULT;
+
+ if (get_user(curfrag, kxfer.curfrag) < 0)
+ return -EFAULT;
+
+ if (curfrag == maxfrag)
+ fragsize = swlen - (maxfrag - 1) * 8192;
+
+ if (!kxfer.buf || !access_ok(VERIFY_READ, kxfer.buf, fragsize))
+ return -EFAULT;
+
+ c = i2o_find_iop(kxfer.iop);
+ if (!c)
+ return -ENXIO;
+
+ msg = i2o_msg_get_wait(c, I2O_TIMEOUT_MESSAGE_GET);
+ if (IS_ERR(msg))
+ return PTR_ERR(msg);
+
+ if (i2o_dma_alloc(&c->pdev->dev, &buffer, fragsize)) {
+ i2o_msg_nop(c, msg);
+ return -ENOMEM;
+ }
+
+ if (__copy_from_user(buffer.virt, kxfer.buf, fragsize)) {
+ i2o_msg_nop(c, msg);
+ i2o_dma_free(&c->pdev->dev, &buffer);
+ return -EFAULT;
+ }
+
+ msg->u.head[0] = cpu_to_le32(NINE_WORD_MSG_SIZE | SGL_OFFSET_7);
+ msg->u.head[1] =
+ cpu_to_le32(I2O_CMD_SW_DOWNLOAD << 24 | HOST_TID << 12 |
+ ADAPTER_TID);
+ msg->u.head[2] = cpu_to_le32(i2o_config_driver.context);
+ msg->u.head[3] = cpu_to_le32(0);
+ msg->body[0] =
+ cpu_to_le32((((u32) kxfer.flags) << 24) | (((u32) kxfer.
+ sw_type) << 16) |
+ (((u32) maxfrag) << 8) | (((u32) curfrag)));
+ msg->body[1] = cpu_to_le32(swlen);
+ msg->body[2] = cpu_to_le32(kxfer.sw_id);
+ msg->body[3] = cpu_to_le32(0xD0000000 | fragsize);
+ msg->body[4] = cpu_to_le32(buffer.phys);
+
+ osm_debug("swdl frag %d/%d (size %d)\n", curfrag, maxfrag, fragsize);
+ status = i2o_msg_post_wait_mem(c, msg, 60, &buffer);
+
+ if (status != -ETIMEDOUT)
+ i2o_dma_free(&c->pdev->dev, &buffer);
+
+ if (status != I2O_POST_WAIT_OK) {
+ // it fails if you try and send frags out of order
+ // and for some yet unknown reasons too
+ osm_info("swdl failed, DetailedStatus = %d\n", status);
+ return status;
+ }
+
+ return 0;
+};
+
+static int i2o_cfg_swul(unsigned long arg)
+{
+ struct i2o_sw_xfer kxfer;
+ struct i2o_sw_xfer __user *pxfer = (struct i2o_sw_xfer __user *)arg;
+ unsigned char maxfrag = 0, curfrag = 1;
+ struct i2o_dma buffer;
+ struct i2o_message *msg;
+ unsigned int status = 0, swlen = 0, fragsize = 8192;
+ struct i2o_controller *c;
+ int ret = 0;
+
+ if (copy_from_user(&kxfer, pxfer, sizeof(struct i2o_sw_xfer)))
+ return -EFAULT;
+
+ if (get_user(swlen, kxfer.swlen) < 0)
+ return -EFAULT;
+
+ if (get_user(maxfrag, kxfer.maxfrag) < 0)
+ return -EFAULT;
+
+ if (get_user(curfrag, kxfer.curfrag) < 0)
+ return -EFAULT;
+
+ if (curfrag == maxfrag)
+ fragsize = swlen - (maxfrag - 1) * 8192;
+
+ if (!kxfer.buf)
+ return -EFAULT;
+
+ c = i2o_find_iop(kxfer.iop);
+ if (!c)
+ return -ENXIO;
+
+ msg = i2o_msg_get_wait(c, I2O_TIMEOUT_MESSAGE_GET);
+ if (IS_ERR(msg))
+ return PTR_ERR(msg);
+
+ if (i2o_dma_alloc(&c->pdev->dev, &buffer, fragsize)) {
+ i2o_msg_nop(c, msg);
+ return -ENOMEM;
+ }
+
+ msg->u.head[0] = cpu_to_le32(NINE_WORD_MSG_SIZE | SGL_OFFSET_7);
+ msg->u.head[1] =
+ cpu_to_le32(I2O_CMD_SW_UPLOAD << 24 | HOST_TID << 12 | ADAPTER_TID);
+ msg->u.head[2] = cpu_to_le32(i2o_config_driver.context);
+ msg->u.head[3] = cpu_to_le32(0);
+ msg->body[0] =
+ cpu_to_le32((u32) kxfer.flags << 24 | (u32) kxfer.
+ sw_type << 16 | (u32) maxfrag << 8 | (u32) curfrag);
+ msg->body[1] = cpu_to_le32(swlen);
+ msg->body[2] = cpu_to_le32(kxfer.sw_id);
+ msg->body[3] = cpu_to_le32(0xD0000000 | fragsize);
+ msg->body[4] = cpu_to_le32(buffer.phys);
+
+ osm_debug("swul frag %d/%d (size %d)\n", curfrag, maxfrag, fragsize);
+ status = i2o_msg_post_wait_mem(c, msg, 60, &buffer);
+
+ if (status != I2O_POST_WAIT_OK) {
+ if (status != -ETIMEDOUT)
+ i2o_dma_free(&c->pdev->dev, &buffer);
+
+ osm_info("swul failed, DetailedStatus = %d\n", status);
+ return status;
+ }
+
+ if (copy_to_user(kxfer.buf, buffer.virt, fragsize))
+ ret = -EFAULT;
+
+ i2o_dma_free(&c->pdev->dev, &buffer);
+
+ return ret;
+}
+
+static int i2o_cfg_swdel(unsigned long arg)
+{
+ struct i2o_controller *c;
+ struct i2o_sw_xfer kxfer;
+ struct i2o_sw_xfer __user *pxfer = (struct i2o_sw_xfer __user *)arg;
+ struct i2o_message *msg;
+ unsigned int swlen;
+ int token;
+
+ if (copy_from_user(&kxfer, pxfer, sizeof(struct i2o_sw_xfer)))
+ return -EFAULT;
+
+ if (get_user(swlen, kxfer.swlen) < 0)
+ return -EFAULT;
+
+ c = i2o_find_iop(kxfer.iop);
+ if (!c)
+ return -ENXIO;
+
+ msg = i2o_msg_get_wait(c, I2O_TIMEOUT_MESSAGE_GET);
+ if (IS_ERR(msg))
+ return PTR_ERR(msg);
+
+ msg->u.head[0] = cpu_to_le32(SEVEN_WORD_MSG_SIZE | SGL_OFFSET_0);
+ msg->u.head[1] =
+ cpu_to_le32(I2O_CMD_SW_REMOVE << 24 | HOST_TID << 12 | ADAPTER_TID);
+ msg->u.head[2] = cpu_to_le32(i2o_config_driver.context);
+ msg->u.head[3] = cpu_to_le32(0);
+ msg->body[0] =
+ cpu_to_le32((u32) kxfer.flags << 24 | (u32) kxfer.sw_type << 16);
+ msg->body[1] = cpu_to_le32(swlen);
+ msg->body[2] = cpu_to_le32(kxfer.sw_id);
+
+ token = i2o_msg_post_wait(c, msg, 10);
+
+ if (token != I2O_POST_WAIT_OK) {
+ osm_info("swdel failed, DetailedStatus = %d\n", token);
+ return -ETIMEDOUT;
+ }
+
+ return 0;
+};
+
+static int i2o_cfg_validate(unsigned long arg)
+{
+ int token;
+ int iop = (int)arg;
+ struct i2o_message *msg;
+ struct i2o_controller *c;
+
+ c = i2o_find_iop(iop);
+ if (!c)
+ return -ENXIO;
+
+ msg = i2o_msg_get_wait(c, I2O_TIMEOUT_MESSAGE_GET);
+ if (IS_ERR(msg))
+ return PTR_ERR(msg);
+
+ msg->u.head[0] = cpu_to_le32(FOUR_WORD_MSG_SIZE | SGL_OFFSET_0);
+ msg->u.head[1] =
+ cpu_to_le32(I2O_CMD_CONFIG_VALIDATE << 24 | HOST_TID << 12 | iop);
+ msg->u.head[2] = cpu_to_le32(i2o_config_driver.context);
+ msg->u.head[3] = cpu_to_le32(0);
+
+ token = i2o_msg_post_wait(c, msg, 10);
+
+ if (token != I2O_POST_WAIT_OK) {
+ osm_info("Can't validate configuration, ErrorStatus = %d\n",
+ token);
+ return -ETIMEDOUT;
+ }
+
+ return 0;
+};
+
+static int i2o_cfg_evt_reg(unsigned long arg, struct file *fp)
+{
+ struct i2o_message *msg;
+ struct i2o_evt_id __user *pdesc = (struct i2o_evt_id __user *)arg;
+ struct i2o_evt_id kdesc;
+ struct i2o_controller *c;
+ struct i2o_device *d;
+
+ if (copy_from_user(&kdesc, pdesc, sizeof(struct i2o_evt_id)))
+ return -EFAULT;
+
+ /* IOP exists? */
+ c = i2o_find_iop(kdesc.iop);
+ if (!c)
+ return -ENXIO;
+
+ /* Device exists? */
+ d = i2o_iop_find_device(c, kdesc.tid);
+ if (!d)
+ return -ENODEV;
+
+ msg = i2o_msg_get_wait(c, I2O_TIMEOUT_MESSAGE_GET);
+ if (IS_ERR(msg))
+ return PTR_ERR(msg);
+
+ msg->u.head[0] = cpu_to_le32(FOUR_WORD_MSG_SIZE | SGL_OFFSET_0);
+ msg->u.head[1] =
+ cpu_to_le32(I2O_CMD_UTIL_EVT_REGISTER << 24 | HOST_TID << 12 |
+ kdesc.tid);
+ msg->u.head[2] = cpu_to_le32(i2o_config_driver.context);
+ msg->u.head[3] = cpu_to_le32(i2o_cntxt_list_add(c, fp->private_data));
+ msg->body[0] = cpu_to_le32(kdesc.evt_mask);
+
+ i2o_msg_post(c, msg);
+
+ return 0;
+}
+
+static int i2o_cfg_evt_get(unsigned long arg, struct file *fp)
+{
+ struct i2o_cfg_info *p = NULL;
+ struct i2o_evt_get __user *uget = (struct i2o_evt_get __user *)arg;
+ struct i2o_evt_get kget;
+ unsigned long flags;
+
+ for (p = open_files; p; p = p->next)
+ if (p->q_id == (ulong) fp->private_data)
+ break;
+
+ if (!p->q_len)
+ return -ENOENT;
+
+ memcpy(&kget.info, &p->event_q[p->q_out], sizeof(struct i2o_evt_info));
+ MODINC(p->q_out, I2O_EVT_Q_LEN);
+ spin_lock_irqsave(&i2o_config_lock, flags);
+ p->q_len--;
+ kget.pending = p->q_len;
+ kget.lost = p->q_lost;
+ spin_unlock_irqrestore(&i2o_config_lock, flags);
+
+ if (copy_to_user(uget, &kget, sizeof(struct i2o_evt_get)))
+ return -EFAULT;
+ return 0;
+}
+
+#ifdef CONFIG_COMPAT
+static int i2o_cfg_passthru32(struct file *file, unsigned cmnd,
+ unsigned long arg)
+{
+ struct i2o_cmd_passthru32 __user *cmd;
+ struct i2o_controller *c;
+ u32 __user *user_msg;
+ u32 *reply = NULL;
+ u32 __user *user_reply = NULL;
+ u32 size = 0;
+ u32 reply_size = 0;
+ u32 rcode = 0;
+ struct i2o_dma sg_list[SG_TABLESIZE];
+ u32 sg_offset = 0;
+ u32 sg_count = 0;
+ u32 i = 0;
+ u32 sg_index = 0;
+ i2o_status_block *sb;
+ struct i2o_message *msg;
+ unsigned int iop;
+
+ cmd = (struct i2o_cmd_passthru32 __user *)arg;
+
+ if (get_user(iop, &cmd->iop) || get_user(i, &cmd->msg))
+ return -EFAULT;
+
+ user_msg = compat_ptr(i);
+
+ c = i2o_find_iop(iop);
+ if (!c) {
+ osm_debug("controller %d not found\n", iop);
+ return -ENXIO;
+ }
+
+ sb = c->status_block.virt;
+
+ if (get_user(size, &user_msg[0])) {
+ osm_warn("unable to get size!\n");
+ return -EFAULT;
+ }
+ size = size >> 16;
+
+ if (size > sb->inbound_frame_size) {
+ osm_warn("size of message > inbound_frame_size");
+ return -EFAULT;
+ }
+
+ user_reply = &user_msg[size];
+
+ size <<= 2; // Convert to bytes
+
+ msg = i2o_msg_get_wait(c, I2O_TIMEOUT_MESSAGE_GET);
+ if (IS_ERR(msg))
+ return PTR_ERR(msg);
+
+ rcode = -EFAULT;
+ /* Copy in the user's I2O command */
+ if (copy_from_user(msg, user_msg, size)) {
+ osm_warn("unable to copy user message\n");
+ goto out;
+ }
+ i2o_dump_message(msg);
+
+ if (get_user(reply_size, &user_reply[0]) < 0)
+ goto out;
+
+ reply_size >>= 16;
+ reply_size <<= 2;
+
+ rcode = -ENOMEM;
+ reply = kzalloc(reply_size, GFP_KERNEL);
+ if (!reply) {
+ printk(KERN_WARNING "%s: Could not allocate reply buffer\n",
+ c->name);
+ goto out;
+ }
+
+ sg_offset = (msg->u.head[0] >> 4) & 0x0f;
+
+ memset(sg_list, 0, sizeof(sg_list[0]) * SG_TABLESIZE);
+ if (sg_offset) {
+ struct sg_simple_element *sg;
+
+ if (sg_offset * 4 >= size) {
+ rcode = -EFAULT;
+ goto cleanup;
+ }
+ // TODO 64bit fix
+ sg = (struct sg_simple_element *)((&msg->u.head[0]) +
+ sg_offset);
+ sg_count =
+ (size - sg_offset * 4) / sizeof(struct sg_simple_element);
+ if (sg_count > SG_TABLESIZE) {
+ printk(KERN_DEBUG "%s:IOCTL SG List too large (%u)\n",
+ c->name, sg_count);
+ rcode = -EINVAL;
+ goto cleanup;
+ }
+
+ for (i = 0; i < sg_count; i++) {
+ int sg_size;
+ struct i2o_dma *p;
+
+ if (!(sg[i].flag_count & 0x10000000
+ /*I2O_SGL_FLAGS_SIMPLE_ADDRESS_ELEMENT */ )) {
+ printk(KERN_DEBUG
+ "%s:Bad SG element %d - not simple (%x)\n",
+ c->name, i, sg[i].flag_count);
+ rcode = -EINVAL;
+ goto cleanup;
+ }
+ sg_size = sg[i].flag_count & 0xffffff;
+ p = &(sg_list[sg_index]);
+ /* Allocate memory for the transfer */
+ if (i2o_dma_alloc(&c->pdev->dev, p, sg_size)) {
+ printk(KERN_DEBUG
+ "%s: Could not allocate SG buffer - size = %d buffer number %d of %d\n",
+ c->name, sg_size, i, sg_count);
+ rcode = -ENOMEM;
+ goto sg_list_cleanup;
+ }
+ sg_index++;
+ /* Copy in the user's SG buffer if necessary */
+ if (sg[i].
+ flag_count & 0x04000000 /*I2O_SGL_FLAGS_DIR */ ) {
+ // TODO 64bit fix
+ if (copy_from_user
+ (p->virt,
+ (void __user *)(unsigned long)sg[i].
+ addr_bus, sg_size)) {
+ printk(KERN_DEBUG
+ "%s: Could not copy SG buf %d FROM user\n",
+ c->name, i);
+ rcode = -EFAULT;
+ goto sg_list_cleanup;
+ }
+ }
+ //TODO 64bit fix
+ sg[i].addr_bus = (u32) p->phys;
+ }
+ }
+
+ rcode = i2o_msg_post_wait(c, msg, 60);
+ msg = NULL;
+ if (rcode) {
+ reply[4] = ((u32) rcode) << 24;
+ goto sg_list_cleanup;
+ }
+
+ if (sg_offset) {
+ u32 rmsg[I2O_OUTBOUND_MSG_FRAME_SIZE];
+ /* Copy back the Scatter Gather buffers back to user space */
+ u32 j;
+ // TODO 64bit fix
+ struct sg_simple_element *sg;
+ int sg_size;
+
+ // re-acquire the original message to handle correctly the sg copy operation
+ memset(&rmsg, 0, I2O_OUTBOUND_MSG_FRAME_SIZE * 4);
+ // get user msg size in u32s
+ if (get_user(size, &user_msg[0])) {
+ rcode = -EFAULT;
+ goto sg_list_cleanup;
+ }
+ size = size >> 16;
+ size *= 4;
+ if (size > sizeof(rmsg)) {
+ rcode = -EINVAL;
+ goto sg_list_cleanup;
+ }
+
+ /* Copy in the user's I2O command */
+ if (copy_from_user(rmsg, user_msg, size)) {
+ rcode = -EFAULT;
+ goto sg_list_cleanup;
+ }
+ sg_count =
+ (size - sg_offset * 4) / sizeof(struct sg_simple_element);
+
+ // TODO 64bit fix
+ sg = (struct sg_simple_element *)(rmsg + sg_offset);
+ for (j = 0; j < sg_count; j++) {
+ /* Copy out the SG list to user's buffer if necessary */
+ if (!
+ (sg[j].
+ flag_count & 0x4000000 /*I2O_SGL_FLAGS_DIR */ )) {
+ sg_size = sg[j].flag_count & 0xffffff;
+ // TODO 64bit fix
+ if (copy_to_user
+ ((void __user *)(u64) sg[j].addr_bus,
+ sg_list[j].virt, sg_size)) {
+ printk(KERN_WARNING
+ "%s: Could not copy %p TO user %x\n",
+ c->name, sg_list[j].virt,
+ sg[j].addr_bus);
+ rcode = -EFAULT;
+ goto sg_list_cleanup;
+ }
+ }
+ }
+ }
+
+sg_list_cleanup:
+ /* Copy back the reply to user space */
+ if (reply_size) {
+ // we wrote our own values for context - now restore the user supplied ones
+ if (copy_from_user(reply + 2, user_msg + 2, sizeof(u32) * 2)) {
+ printk(KERN_WARNING
+ "%s: Could not copy message context FROM user\n",
+ c->name);
+ rcode = -EFAULT;
+ }
+ if (copy_to_user(user_reply, reply, reply_size)) {
+ printk(KERN_WARNING
+ "%s: Could not copy reply TO user\n", c->name);
+ rcode = -EFAULT;
+ }
+ }
+ for (i = 0; i < sg_index; i++)
+ i2o_dma_free(&c->pdev->dev, &sg_list[i]);
+
+cleanup:
+ kfree(reply);
+out:
+ if (msg)
+ i2o_msg_nop(c, msg);
+ return rcode;
+}
+
+static long i2o_cfg_compat_ioctl(struct file *file, unsigned cmd,
+ unsigned long arg)
+{
+ int ret;
+ switch (cmd) {
+ case I2OGETIOPS:
+ ret = i2o_cfg_ioctl(file, cmd, arg);
+ break;
+ case I2OPASSTHRU32:
+ mutex_lock(&i2o_cfg_mutex);
+ ret = i2o_cfg_passthru32(file, cmd, arg);
+ mutex_unlock(&i2o_cfg_mutex);
+ break;
+ default:
+ ret = -ENOIOCTLCMD;
+ break;
+ }
+ return ret;
+}
+
+#endif
+
+#ifdef CONFIG_I2O_EXT_ADAPTEC
+static int i2o_cfg_passthru(unsigned long arg)
+{
+ struct i2o_cmd_passthru __user *cmd =
+ (struct i2o_cmd_passthru __user *)arg;
+ struct i2o_controller *c;
+ u32 __user *user_msg;
+ u32 *reply = NULL;
+ u32 __user *user_reply = NULL;
+ u32 size = 0;
+ u32 reply_size = 0;
+ u32 rcode = 0;
+ struct i2o_dma sg_list[SG_TABLESIZE];
+ u32 sg_offset = 0;
+ u32 sg_count = 0;
+ int sg_index = 0;
+ u32 i = 0;
+ i2o_status_block *sb;
+ struct i2o_message *msg;
+ unsigned int iop;
+
+ if (get_user(iop, &cmd->iop) || get_user(user_msg, &cmd->msg))
+ return -EFAULT;
+
+ c = i2o_find_iop(iop);
+ if (!c) {
+ osm_warn("controller %d not found\n", iop);
+ return -ENXIO;
+ }
+
+ sb = c->status_block.virt;
+
+ if (get_user(size, &user_msg[0]))
+ return -EFAULT;
+ size = size >> 16;
+
+ if (size > sb->inbound_frame_size) {
+ osm_warn("size of message > inbound_frame_size");
+ return -EFAULT;
+ }
+
+ user_reply = &user_msg[size];
+
+ size <<= 2; // Convert to bytes
+
+ msg = i2o_msg_get_wait(c, I2O_TIMEOUT_MESSAGE_GET);
+ if (IS_ERR(msg))
+ return PTR_ERR(msg);
+
+ rcode = -EFAULT;
+ /* Copy in the user's I2O command */
+ if (copy_from_user(msg, user_msg, size))
+ goto out;
+
+ if (get_user(reply_size, &user_reply[0]) < 0)
+ goto out;
+
+ reply_size >>= 16;
+ reply_size <<= 2;
+
+ reply = kzalloc(reply_size, GFP_KERNEL);
+ if (!reply) {
+ printk(KERN_WARNING "%s: Could not allocate reply buffer\n",
+ c->name);
+ rcode = -ENOMEM;
+ goto out;
+ }
+
+ sg_offset = (msg->u.head[0] >> 4) & 0x0f;
+
+ memset(sg_list, 0, sizeof(sg_list[0]) * SG_TABLESIZE);
+ if (sg_offset) {
+ struct sg_simple_element *sg;
+ struct i2o_dma *p;
+
+ if (sg_offset * 4 >= size) {
+ rcode = -EFAULT;
+ goto cleanup;
+ }
+ // TODO 64bit fix
+ sg = (struct sg_simple_element *)((&msg->u.head[0]) +
+ sg_offset);
+ sg_count =
+ (size - sg_offset * 4) / sizeof(struct sg_simple_element);
+ if (sg_count > SG_TABLESIZE) {
+ printk(KERN_DEBUG "%s:IOCTL SG List too large (%u)\n",
+ c->name, sg_count);
+ rcode = -EINVAL;
+ goto cleanup;
+ }
+
+ for (i = 0; i < sg_count; i++) {
+ int sg_size;
+
+ if (!(sg[i].flag_count & 0x10000000
+ /*I2O_SGL_FLAGS_SIMPLE_ADDRESS_ELEMENT */ )) {
+ printk(KERN_DEBUG
+ "%s:Bad SG element %d - not simple (%x)\n",
+ c->name, i, sg[i].flag_count);
+ rcode = -EINVAL;
+ goto sg_list_cleanup;
+ }
+ sg_size = sg[i].flag_count & 0xffffff;
+ p = &(sg_list[sg_index]);
+ if (i2o_dma_alloc(&c->pdev->dev, p, sg_size)) {
+ /* Allocate memory for the transfer */
+ printk(KERN_DEBUG
+ "%s: Could not allocate SG buffer - size = %d buffer number %d of %d\n",
+ c->name, sg_size, i, sg_count);
+ rcode = -ENOMEM;
+ goto sg_list_cleanup;
+ }
+ sg_index++;
+ /* Copy in the user's SG buffer if necessary */
+ if (sg[i].
+ flag_count & 0x04000000 /*I2O_SGL_FLAGS_DIR */ ) {
+ // TODO 64bit fix
+ if (copy_from_user
+ (p->virt, (void __user *)sg[i].addr_bus,
+ sg_size)) {
+ printk(KERN_DEBUG
+ "%s: Could not copy SG buf %d FROM user\n",
+ c->name, i);
+ rcode = -EFAULT;
+ goto sg_list_cleanup;
+ }
+ }
+ sg[i].addr_bus = p->phys;
+ }
+ }
+
+ rcode = i2o_msg_post_wait(c, msg, 60);
+ msg = NULL;
+ if (rcode) {
+ reply[4] = ((u32) rcode) << 24;
+ goto sg_list_cleanup;
+ }
+
+ if (sg_offset) {
+ u32 rmsg[I2O_OUTBOUND_MSG_FRAME_SIZE];
+ /* Copy back the Scatter Gather buffers back to user space */
+ u32 j;
+ // TODO 64bit fix
+ struct sg_simple_element *sg;
+ int sg_size;
+
+ // re-acquire the original message to handle correctly the sg copy operation
+ memset(&rmsg, 0, I2O_OUTBOUND_MSG_FRAME_SIZE * 4);
+ // get user msg size in u32s
+ if (get_user(size, &user_msg[0])) {
+ rcode = -EFAULT;
+ goto sg_list_cleanup;
+ }
+ size = size >> 16;
+ size *= 4;
+ if (size > sizeof(rmsg)) {
+ rcode = -EFAULT;
+ goto sg_list_cleanup;
+ }
+
+ /* Copy in the user's I2O command */
+ if (copy_from_user(rmsg, user_msg, size)) {
+ rcode = -EFAULT;
+ goto sg_list_cleanup;
+ }
+ sg_count =
+ (size - sg_offset * 4) / sizeof(struct sg_simple_element);
+
+ // TODO 64bit fix
+ sg = (struct sg_simple_element *)(rmsg + sg_offset);
+ for (j = 0; j < sg_count; j++) {
+ /* Copy out the SG list to user's buffer if necessary */
+ if (!
+ (sg[j].
+ flag_count & 0x4000000 /*I2O_SGL_FLAGS_DIR */ )) {
+ sg_size = sg[j].flag_count & 0xffffff;
+ // TODO 64bit fix
+ if (copy_to_user
+ ((void __user *)sg[j].addr_bus, sg_list[j].virt,
+ sg_size)) {
+ printk(KERN_WARNING
+ "%s: Could not copy %p TO user %x\n",
+ c->name, sg_list[j].virt,
+ sg[j].addr_bus);
+ rcode = -EFAULT;
+ goto sg_list_cleanup;
+ }
+ }
+ }
+ }
+
+sg_list_cleanup:
+ /* Copy back the reply to user space */
+ if (reply_size) {
+ // we wrote our own values for context - now restore the user supplied ones
+ if (copy_from_user(reply + 2, user_msg + 2, sizeof(u32) * 2)) {
+ printk(KERN_WARNING
+ "%s: Could not copy message context FROM user\n",
+ c->name);
+ rcode = -EFAULT;
+ }
+ if (copy_to_user(user_reply, reply, reply_size)) {
+ printk(KERN_WARNING
+ "%s: Could not copy reply TO user\n", c->name);
+ rcode = -EFAULT;
+ }
+ }
+
+ for (i = 0; i < sg_index; i++)
+ i2o_dma_free(&c->pdev->dev, &sg_list[i]);
+
+cleanup:
+ kfree(reply);
+out:
+ if (msg)
+ i2o_msg_nop(c, msg);
+ return rcode;
+}
+#endif
+
+/*
+ * IOCTL Handler
+ */
+static long i2o_cfg_ioctl(struct file *fp, unsigned int cmd, unsigned long arg)
+{
+ int ret;
+
+ mutex_lock(&i2o_cfg_mutex);
+ switch (cmd) {
+ case I2OGETIOPS:
+ ret = i2o_cfg_getiops(arg);
+ break;
+
+ case I2OHRTGET:
+ ret = i2o_cfg_gethrt(arg);
+ break;
+
+ case I2OLCTGET:
+ ret = i2o_cfg_getlct(arg);
+ break;
+
+ case I2OPARMSET:
+ ret = i2o_cfg_parms(arg, I2OPARMSET);
+ break;
+
+ case I2OPARMGET:
+ ret = i2o_cfg_parms(arg, I2OPARMGET);
+ break;
+
+ case I2OSWDL:
+ ret = i2o_cfg_swdl(arg);
+ break;
+
+ case I2OSWUL:
+ ret = i2o_cfg_swul(arg);
+ break;
+
+ case I2OSWDEL:
+ ret = i2o_cfg_swdel(arg);
+ break;
+
+ case I2OVALIDATE:
+ ret = i2o_cfg_validate(arg);
+ break;
+
+ case I2OEVTREG:
+ ret = i2o_cfg_evt_reg(arg, fp);
+ break;
+
+ case I2OEVTGET:
+ ret = i2o_cfg_evt_get(arg, fp);
+ break;
+
+#ifdef CONFIG_I2O_EXT_ADAPTEC
+ case I2OPASSTHRU:
+ ret = i2o_cfg_passthru(arg);
+ break;
+#endif
+
+ default:
+ osm_debug("unknown ioctl called!\n");
+ ret = -EINVAL;
+ }
+ mutex_unlock(&i2o_cfg_mutex);
+ return ret;
+}
+
+static int cfg_open(struct inode *inode, struct file *file)
+{
+ struct i2o_cfg_info *tmp = kmalloc(sizeof(struct i2o_cfg_info),
+ GFP_KERNEL);
+ unsigned long flags;
+
+ if (!tmp)
+ return -ENOMEM;
+
+ mutex_lock(&i2o_cfg_mutex);
+ file->private_data = (void *)(i2o_cfg_info_id++);
+ tmp->fp = file;
+ tmp->fasync = NULL;
+ tmp->q_id = (ulong) file->private_data;
+ tmp->q_len = 0;
+ tmp->q_in = 0;
+ tmp->q_out = 0;
+ tmp->q_lost = 0;
+ tmp->next = open_files;
+
+ spin_lock_irqsave(&i2o_config_lock, flags);
+ open_files = tmp;
+ spin_unlock_irqrestore(&i2o_config_lock, flags);
+ mutex_unlock(&i2o_cfg_mutex);
+
+ return 0;
+}
+
+static int cfg_fasync(int fd, struct file *fp, int on)
+{
+ ulong id = (ulong) fp->private_data;
+ struct i2o_cfg_info *p;
+ int ret = -EBADF;
+
+ mutex_lock(&i2o_cfg_mutex);
+ for (p = open_files; p; p = p->next)
+ if (p->q_id == id)
+ break;
+
+ if (p)
+ ret = fasync_helper(fd, fp, on, &p->fasync);
+ mutex_unlock(&i2o_cfg_mutex);
+ return ret;
+}
+
+static int cfg_release(struct inode *inode, struct file *file)
+{
+ ulong id = (ulong) file->private_data;
+ struct i2o_cfg_info *p, **q;
+ unsigned long flags;
+
+ mutex_lock(&i2o_cfg_mutex);
+ spin_lock_irqsave(&i2o_config_lock, flags);
+ for (q = &open_files; (p = *q) != NULL; q = &p->next) {
+ if (p->q_id == id) {
+ *q = p->next;
+ kfree(p);
+ break;
+ }
+ }
+ spin_unlock_irqrestore(&i2o_config_lock, flags);
+ mutex_unlock(&i2o_cfg_mutex);
+
+ return 0;
+}
+
+static const struct file_operations config_fops = {
+ .owner = THIS_MODULE,
+ .llseek = no_llseek,
+ .unlocked_ioctl = i2o_cfg_ioctl,
+#ifdef CONFIG_COMPAT
+ .compat_ioctl = i2o_cfg_compat_ioctl,
+#endif
+ .open = cfg_open,
+ .release = cfg_release,
+ .fasync = cfg_fasync,
+};
+
+static struct miscdevice i2o_miscdev = {
+ I2O_MINOR,
+ "i2octl",
+ &config_fops
+};
+
+static int __init i2o_config_old_init(void)
+{
+ spin_lock_init(&i2o_config_lock);
+
+ if (misc_register(&i2o_miscdev) < 0) {
+ osm_err("can't register device.\n");
+ return -EBUSY;
+ }
+
+ return 0;
+}
+
+static void i2o_config_old_exit(void)
+{
+ misc_deregister(&i2o_miscdev);
+}
+
+MODULE_AUTHOR("Red Hat Software");
diff --git a/drivers/staging/i2o/i2o_proc.c b/drivers/staging/i2o/i2o_proc.c
new file mode 100644
index 000000000..780fee322
--- /dev/null
+++ b/drivers/staging/i2o/i2o_proc.c
@@ -0,0 +1,2049 @@
+/*
+ * procfs handler for Linux I2O subsystem
+ *
+ * (c) Copyright 1999 Deepak Saxena
+ *
+ * Originally written by Deepak Saxena(deepak@plexity.net)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This is an initial test release. The code is based on the design of the
+ * ide procfs system (drivers/block/ide-proc.c). Some code taken from
+ * i2o-core module by Alan Cox.
+ *
+ * DISCLAIMER: This code is still under development/test and may cause
+ * your system to behave unpredictably. Use at your own discretion.
+ *
+ *
+ * Fixes/additions:
+ * Juha Sievänen (Juha.Sievanen@cs.Helsinki.FI),
+ * Auvo Häkkinen (Auvo.Hakkinen@cs.Helsinki.FI)
+ * University of Helsinki, Department of Computer Science
+ * LAN entries
+ * Markus Lidel <Markus.Lidel@shadowconnect.com>
+ * Changes for new I2O API
+ */
+
+#define OSM_NAME "proc-osm"
+#define OSM_VERSION "1.316"
+#define OSM_DESCRIPTION "I2O ProcFS OSM"
+
+#define I2O_MAX_MODULES 4
+// FIXME!
+#define FMT_U64_HEX "0x%08x%08x"
+#define U64_VAL(pu64) *((u32*)(pu64)+1), *((u32*)(pu64))
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/pci.h>
+#include "i2o.h"
+#include <linux/slab.h>
+#include <linux/proc_fs.h>
+#include <linux/seq_file.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/spinlock.h>
+#include <linux/workqueue.h>
+#include <linux/uaccess.h>
+
+#include <asm/io.h>
+#include <asm/byteorder.h>
+
+/* Structure used to define /proc entries */
+typedef struct _i2o_proc_entry_t {
+ char *name; /* entry name */
+ umode_t mode; /* mode */
+ const struct file_operations *fops; /* open function */
+} i2o_proc_entry;
+
+/* global I2O /proc/i2o entry */
+static struct proc_dir_entry *i2o_proc_dir_root;
+
+/* proc OSM driver struct */
+static struct i2o_driver i2o_proc_driver = {
+ .name = OSM_NAME,
+};
+
+static int print_serial_number(struct seq_file *seq, u8 * serialno, int max_len)
+{
+ int i;
+
+ /* 19990419 -sralston
+ * The I2O v1.5 (and v2.0 so far) "official specification"
+ * got serial numbers WRONG!
+ * Apparently, and despite what Section 3.4.4 says and
+ * Figure 3-35 shows (pg 3-39 in the pdf doc),
+ * the convention / consensus seems to be:
+ * + First byte is SNFormat
+ * + Second byte is SNLen (but only if SNFormat==7 (?))
+ * + (v2.0) SCSI+BS may use IEEE Registered (64 or 128 bit) format
+ */
+ switch (serialno[0]) {
+ case I2O_SNFORMAT_BINARY: /* Binary */
+ seq_printf(seq, "0x");
+ for (i = 0; i < serialno[1]; i++)
+ seq_printf(seq, "%02X", serialno[2 + i]);
+ break;
+
+ case I2O_SNFORMAT_ASCII: /* ASCII */
+ if (serialno[1] < ' ') { /* printable or SNLen? */
+ /* sanity */
+ max_len =
+ (max_len < serialno[1]) ? max_len : serialno[1];
+ serialno[1 + max_len] = '\0';
+
+ /* just print it */
+ seq_printf(seq, "%s", &serialno[2]);
+ } else {
+ /* print chars for specified length */
+ for (i = 0; i < serialno[1]; i++)
+ seq_printf(seq, "%c", serialno[2 + i]);
+ }
+ break;
+
+ case I2O_SNFORMAT_UNICODE: /* UNICODE */
+ seq_printf(seq, "UNICODE Format. Can't Display\n");
+ break;
+
+ case I2O_SNFORMAT_LAN48_MAC: /* LAN-48 MAC Address */
+ seq_printf(seq, "LAN-48 MAC address @ %pM", &serialno[2]);
+ break;
+
+ case I2O_SNFORMAT_WAN: /* WAN MAC Address */
+ /* FIXME: Figure out what a WAN access address looks like?? */
+ seq_printf(seq, "WAN Access Address");
+ break;
+
+/* plus new in v2.0 */
+ case I2O_SNFORMAT_LAN64_MAC: /* LAN-64 MAC Address */
+ /* FIXME: Figure out what a LAN-64 address really looks like?? */
+ seq_printf(seq,
+ "LAN-64 MAC address @ [?:%02X:%02X:?] %pM",
+ serialno[8], serialno[9], &serialno[2]);
+ break;
+
+ case I2O_SNFORMAT_DDM: /* I2O DDM */
+ seq_printf(seq,
+ "DDM: Tid=%03Xh, Rsvd=%04Xh, OrgId=%04Xh",
+ *(u16 *) & serialno[2],
+ *(u16 *) & serialno[4], *(u16 *) & serialno[6]);
+ break;
+
+ case I2O_SNFORMAT_IEEE_REG64: /* IEEE Registered (64-bit) */
+ case I2O_SNFORMAT_IEEE_REG128: /* IEEE Registered (128-bit) */
+ /* FIXME: Figure if this is even close?? */
+ seq_printf(seq,
+ "IEEE NodeName(hi,lo)=(%08Xh:%08Xh), PortName(hi,lo)=(%08Xh:%08Xh)\n",
+ *(u32 *) & serialno[2],
+ *(u32 *) & serialno[6],
+ *(u32 *) & serialno[10], *(u32 *) & serialno[14]);
+ break;
+
+ case I2O_SNFORMAT_UNKNOWN: /* Unknown 0 */
+ case I2O_SNFORMAT_UNKNOWN2: /* Unknown 0xff */
+ default:
+ seq_printf(seq, "Unknown data format (0x%02x)", serialno[0]);
+ break;
+ }
+
+ return 0;
+}
+
+/**
+ * i2o_get_class_name - do i2o class name lookup
+ * @class: class number
+ *
+ * Return a descriptive string for an i2o class.
+ */
+static const char *i2o_get_class_name(int class)
+{
+ int idx = 16;
+ static char *i2o_class_name[] = {
+ "Executive",
+ "Device Driver Module",
+ "Block Device",
+ "Tape Device",
+ "LAN Interface",
+ "WAN Interface",
+ "Fibre Channel Port",
+ "Fibre Channel Device",
+ "SCSI Device",
+ "ATE Port",
+ "ATE Device",
+ "Floppy Controller",
+ "Floppy Device",
+ "Secondary Bus Port",
+ "Peer Transport Agent",
+ "Peer Transport",
+ "Unknown"
+ };
+
+ switch (class & 0xfff) {
+ case I2O_CLASS_EXECUTIVE:
+ idx = 0;
+ break;
+ case I2O_CLASS_DDM:
+ idx = 1;
+ break;
+ case I2O_CLASS_RANDOM_BLOCK_STORAGE:
+ idx = 2;
+ break;
+ case I2O_CLASS_SEQUENTIAL_STORAGE:
+ idx = 3;
+ break;
+ case I2O_CLASS_LAN:
+ idx = 4;
+ break;
+ case I2O_CLASS_WAN:
+ idx = 5;
+ break;
+ case I2O_CLASS_FIBRE_CHANNEL_PORT:
+ idx = 6;
+ break;
+ case I2O_CLASS_FIBRE_CHANNEL_PERIPHERAL:
+ idx = 7;
+ break;
+ case I2O_CLASS_SCSI_PERIPHERAL:
+ idx = 8;
+ break;
+ case I2O_CLASS_ATE_PORT:
+ idx = 9;
+ break;
+ case I2O_CLASS_ATE_PERIPHERAL:
+ idx = 10;
+ break;
+ case I2O_CLASS_FLOPPY_CONTROLLER:
+ idx = 11;
+ break;
+ case I2O_CLASS_FLOPPY_DEVICE:
+ idx = 12;
+ break;
+ case I2O_CLASS_BUS_ADAPTER:
+ idx = 13;
+ break;
+ case I2O_CLASS_PEER_TRANSPORT_AGENT:
+ idx = 14;
+ break;
+ case I2O_CLASS_PEER_TRANSPORT:
+ idx = 15;
+ break;
+ }
+
+ return i2o_class_name[idx];
+}
+
+#define SCSI_TABLE_SIZE 13
+static char *scsi_devices[] = {
+ "Direct-Access Read/Write",
+ "Sequential-Access Storage",
+ "Printer",
+ "Processor",
+ "WORM Device",
+ "CD-ROM Device",
+ "Scanner Device",
+ "Optical Memory Device",
+ "Medium Changer Device",
+ "Communications Device",
+ "Graphics Art Pre-Press Device",
+ "Graphics Art Pre-Press Device",
+ "Array Controller Device"
+};
+
+static char *chtostr(char *tmp, u8 *chars, int n)
+{
+ tmp[0] = 0;
+ return strncat(tmp, (char *)chars, n);
+}
+
+static int i2o_report_query_status(struct seq_file *seq, int block_status,
+ char *group)
+{
+ switch (block_status) {
+ case -ETIMEDOUT:
+ seq_printf(seq, "Timeout reading group %s.\n", group);
+ break;
+ case -ENOMEM:
+ seq_puts(seq, "No free memory to read the table.\n");
+ break;
+ case -I2O_PARAMS_STATUS_INVALID_GROUP_ID:
+ seq_printf(seq, "Group %s not supported.\n", group);
+ break;
+ default:
+ seq_printf(seq,
+ "Error reading group %s. BlockStatus 0x%02X\n",
+ group, -block_status);
+ break;
+ }
+
+ return 0;
+}
+
+static char *bus_strings[] = {
+ "Local Bus",
+ "ISA",
+ "EISA",
+ "PCI",
+ "PCMCIA",
+ "NUBUS",
+ "CARDBUS"
+};
+
+static int i2o_seq_show_hrt(struct seq_file *seq, void *v)
+{
+ struct i2o_controller *c = (struct i2o_controller *)seq->private;
+ i2o_hrt *hrt = (i2o_hrt *) c->hrt.virt;
+ u32 bus;
+ int i;
+
+ if (hrt->hrt_version) {
+ seq_printf(seq,
+ "HRT table for controller is too new a version.\n");
+ return 0;
+ }
+
+ seq_printf(seq, "HRT has %d entries of %d bytes each.\n",
+ hrt->num_entries, hrt->entry_len << 2);
+
+ for (i = 0; i < hrt->num_entries; i++) {
+ seq_printf(seq, "Entry %d:\n", i);
+ seq_printf(seq, " Adapter ID: %0#10x\n",
+ hrt->hrt_entry[i].adapter_id);
+ seq_printf(seq, " Controlling tid: %0#6x\n",
+ hrt->hrt_entry[i].parent_tid);
+
+ if (hrt->hrt_entry[i].bus_type != 0x80) {
+ bus = hrt->hrt_entry[i].bus_type;
+ seq_printf(seq, " %s Information\n",
+ bus_strings[bus]);
+
+ switch (bus) {
+ case I2O_BUS_LOCAL:
+ seq_printf(seq, " IOBase: %0#6x,",
+ hrt->hrt_entry[i].bus.local_bus.
+ LbBaseIOPort);
+ seq_printf(seq, " MemoryBase: %0#10x\n",
+ hrt->hrt_entry[i].bus.local_bus.
+ LbBaseMemoryAddress);
+ break;
+
+ case I2O_BUS_ISA:
+ seq_printf(seq, " IOBase: %0#6x,",
+ hrt->hrt_entry[i].bus.isa_bus.
+ IsaBaseIOPort);
+ seq_printf(seq, " MemoryBase: %0#10x,",
+ hrt->hrt_entry[i].bus.isa_bus.
+ IsaBaseMemoryAddress);
+ seq_printf(seq, " CSN: %0#4x,",
+ hrt->hrt_entry[i].bus.isa_bus.CSN);
+ break;
+
+ case I2O_BUS_EISA:
+ seq_printf(seq, " IOBase: %0#6x,",
+ hrt->hrt_entry[i].bus.eisa_bus.
+ EisaBaseIOPort);
+ seq_printf(seq, " MemoryBase: %0#10x,",
+ hrt->hrt_entry[i].bus.eisa_bus.
+ EisaBaseMemoryAddress);
+ seq_printf(seq, " Slot: %0#4x,",
+ hrt->hrt_entry[i].bus.eisa_bus.
+ EisaSlotNumber);
+ break;
+
+ case I2O_BUS_PCI:
+ seq_printf(seq, " Bus: %0#4x",
+ hrt->hrt_entry[i].bus.pci_bus.
+ PciBusNumber);
+ seq_printf(seq, " Dev: %0#4x",
+ hrt->hrt_entry[i].bus.pci_bus.
+ PciDeviceNumber);
+ seq_printf(seq, " Func: %0#4x",
+ hrt->hrt_entry[i].bus.pci_bus.
+ PciFunctionNumber);
+ seq_printf(seq, " Vendor: %0#6x",
+ hrt->hrt_entry[i].bus.pci_bus.
+ PciVendorID);
+ seq_printf(seq, " Device: %0#6x\n",
+ hrt->hrt_entry[i].bus.pci_bus.
+ PciDeviceID);
+ break;
+
+ default:
+ seq_printf(seq, " Unsupported Bus Type\n");
+ }
+ } else
+ seq_printf(seq, " Unknown Bus Type\n");
+ }
+
+ return 0;
+}
+
+static int i2o_seq_show_lct(struct seq_file *seq, void *v)
+{
+ struct i2o_controller *c = (struct i2o_controller *)seq->private;
+ i2o_lct *lct = (i2o_lct *) c->lct;
+ int entries;
+ int i;
+
+#define BUS_TABLE_SIZE 3
+ static char *bus_ports[] = {
+ "Generic Bus",
+ "SCSI Bus",
+ "Fibre Channel Bus"
+ };
+
+ entries = (lct->table_size - 3) / 9;
+
+ seq_printf(seq, "LCT contains %d %s\n", entries,
+ entries == 1 ? "entry" : "entries");
+ if (lct->boot_tid)
+ seq_printf(seq, "Boot Device @ ID %d\n", lct->boot_tid);
+
+ seq_printf(seq, "Current Change Indicator: %#10x\n", lct->change_ind);
+
+ for (i = 0; i < entries; i++) {
+ seq_printf(seq, "Entry %d\n", i);
+ seq_printf(seq, " Class, SubClass : %s",
+ i2o_get_class_name(lct->lct_entry[i].class_id));
+
+ /*
+ * Classes which we'll print subclass info for
+ */
+ switch (lct->lct_entry[i].class_id & 0xFFF) {
+ case I2O_CLASS_RANDOM_BLOCK_STORAGE:
+ switch (lct->lct_entry[i].sub_class) {
+ case 0x00:
+ seq_printf(seq, ", Direct-Access Read/Write");
+ break;
+
+ case 0x04:
+ seq_printf(seq, ", WORM Drive");
+ break;
+
+ case 0x05:
+ seq_printf(seq, ", CD-ROM Drive");
+ break;
+
+ case 0x07:
+ seq_printf(seq, ", Optical Memory Device");
+ break;
+
+ default:
+ seq_printf(seq, ", Unknown (0x%02x)",
+ lct->lct_entry[i].sub_class);
+ break;
+ }
+ break;
+
+ case I2O_CLASS_LAN:
+ switch (lct->lct_entry[i].sub_class & 0xFF) {
+ case 0x30:
+ seq_printf(seq, ", Ethernet");
+ break;
+
+ case 0x40:
+ seq_printf(seq, ", 100base VG");
+ break;
+
+ case 0x50:
+ seq_printf(seq, ", IEEE 802.5/Token-Ring");
+ break;
+
+ case 0x60:
+ seq_printf(seq, ", ANSI X3T9.5 FDDI");
+ break;
+
+ case 0x70:
+ seq_printf(seq, ", Fibre Channel");
+ break;
+
+ default:
+ seq_printf(seq, ", Unknown Sub-Class (0x%02x)",
+ lct->lct_entry[i].sub_class & 0xFF);
+ break;
+ }
+ break;
+
+ case I2O_CLASS_SCSI_PERIPHERAL:
+ if (lct->lct_entry[i].sub_class < SCSI_TABLE_SIZE)
+ seq_printf(seq, ", %s",
+ scsi_devices[lct->lct_entry[i].
+ sub_class]);
+ else
+ seq_printf(seq, ", Unknown Device Type");
+ break;
+
+ case I2O_CLASS_BUS_ADAPTER:
+ if (lct->lct_entry[i].sub_class < BUS_TABLE_SIZE)
+ seq_printf(seq, ", %s",
+ bus_ports[lct->lct_entry[i].
+ sub_class]);
+ else
+ seq_printf(seq, ", Unknown Bus Type");
+ break;
+ }
+ seq_printf(seq, "\n");
+
+ seq_printf(seq, " Local TID : 0x%03x\n",
+ lct->lct_entry[i].tid);
+ seq_printf(seq, " User TID : 0x%03x\n",
+ lct->lct_entry[i].user_tid);
+ seq_printf(seq, " Parent TID : 0x%03x\n",
+ lct->lct_entry[i].parent_tid);
+ seq_printf(seq, " Identity Tag : 0x%x%x%x%x%x%x%x%x\n",
+ lct->lct_entry[i].identity_tag[0],
+ lct->lct_entry[i].identity_tag[1],
+ lct->lct_entry[i].identity_tag[2],
+ lct->lct_entry[i].identity_tag[3],
+ lct->lct_entry[i].identity_tag[4],
+ lct->lct_entry[i].identity_tag[5],
+ lct->lct_entry[i].identity_tag[6],
+ lct->lct_entry[i].identity_tag[7]);
+ seq_printf(seq, " Change Indicator : %0#10x\n",
+ lct->lct_entry[i].change_ind);
+ seq_printf(seq, " Event Capab Mask : %0#10x\n",
+ lct->lct_entry[i].device_flags);
+ }
+
+ return 0;
+}
+
+static int i2o_seq_show_status(struct seq_file *seq, void *v)
+{
+ struct i2o_controller *c = (struct i2o_controller *)seq->private;
+ char prodstr[25];
+ int version;
+ i2o_status_block *sb = c->status_block.virt;
+
+ i2o_status_get(c); // reread the status block
+
+ seq_printf(seq, "Organization ID : %0#6x\n", sb->org_id);
+
+ version = sb->i2o_version;
+
+/* FIXME for Spec 2.0
+ if (version == 0x02) {
+ seq_printf(seq, "Lowest I2O version supported: ");
+ switch(workspace[2]) {
+ case 0x00:
+ seq_printf(seq, "1.0\n");
+ break;
+ case 0x01:
+ seq_printf(seq, "1.5\n");
+ break;
+ case 0x02:
+ seq_printf(seq, "2.0\n");
+ break;
+ }
+
+ seq_printf(seq, "Highest I2O version supported: ");
+ switch(workspace[3]) {
+ case 0x00:
+ seq_printf(seq, "1.0\n");
+ break;
+ case 0x01:
+ seq_printf(seq, "1.5\n");
+ break;
+ case 0x02:
+ seq_printf(seq, "2.0\n");
+ break;
+ }
+ }
+*/
+ seq_printf(seq, "IOP ID : %0#5x\n", sb->iop_id);
+ seq_printf(seq, "Host Unit ID : %0#6x\n", sb->host_unit_id);
+ seq_printf(seq, "Segment Number : %0#5x\n", sb->segment_number);
+
+ seq_printf(seq, "I2O version : ");
+ switch (version) {
+ case 0x00:
+ seq_printf(seq, "1.0\n");
+ break;
+ case 0x01:
+ seq_printf(seq, "1.5\n");
+ break;
+ case 0x02:
+ seq_printf(seq, "2.0\n");
+ break;
+ default:
+ seq_printf(seq, "Unknown version\n");
+ }
+
+ seq_printf(seq, "IOP State : ");
+ switch (sb->iop_state) {
+ case 0x01:
+ seq_printf(seq, "INIT\n");
+ break;
+
+ case 0x02:
+ seq_printf(seq, "RESET\n");
+ break;
+
+ case 0x04:
+ seq_printf(seq, "HOLD\n");
+ break;
+
+ case 0x05:
+ seq_printf(seq, "READY\n");
+ break;
+
+ case 0x08:
+ seq_printf(seq, "OPERATIONAL\n");
+ break;
+
+ case 0x10:
+ seq_printf(seq, "FAILED\n");
+ break;
+
+ case 0x11:
+ seq_printf(seq, "FAULTED\n");
+ break;
+
+ default:
+ seq_printf(seq, "Unknown\n");
+ break;
+ }
+
+ seq_printf(seq, "Messenger Type : ");
+ switch (sb->msg_type) {
+ case 0x00:
+ seq_printf(seq, "Memory mapped\n");
+ break;
+ case 0x01:
+ seq_printf(seq, "Memory mapped only\n");
+ break;
+ case 0x02:
+ seq_printf(seq, "Remote only\n");
+ break;
+ case 0x03:
+ seq_printf(seq, "Memory mapped and remote\n");
+ break;
+ default:
+ seq_printf(seq, "Unknown\n");
+ }
+
+ seq_printf(seq, "Inbound Frame Size : %d bytes\n",
+ sb->inbound_frame_size << 2);
+ seq_printf(seq, "Max Inbound Frames : %d\n",
+ sb->max_inbound_frames);
+ seq_printf(seq, "Current Inbound Frames : %d\n",
+ sb->cur_inbound_frames);
+ seq_printf(seq, "Max Outbound Frames : %d\n",
+ sb->max_outbound_frames);
+
+ /* Spec doesn't say if NULL terminated or not... */
+ memcpy(prodstr, sb->product_id, 24);
+ prodstr[24] = '\0';
+ seq_printf(seq, "Product ID : %s\n", prodstr);
+ seq_printf(seq, "Expected LCT Size : %d bytes\n",
+ sb->expected_lct_size);
+
+ seq_printf(seq, "IOP Capabilities\n");
+ seq_printf(seq, " Context Field Size Support : ");
+ switch (sb->iop_capabilities & 0x0000003) {
+ case 0:
+ seq_printf(seq, "Supports only 32-bit context fields\n");
+ break;
+ case 1:
+ seq_printf(seq, "Supports only 64-bit context fields\n");
+ break;
+ case 2:
+ seq_printf(seq, "Supports 32-bit and 64-bit context fields, "
+ "but not concurrently\n");
+ break;
+ case 3:
+ seq_printf(seq, "Supports 32-bit and 64-bit context fields "
+ "concurrently\n");
+ break;
+ default:
+ seq_printf(seq, "0x%08x\n", sb->iop_capabilities);
+ }
+ seq_printf(seq, " Current Context Field Size : ");
+ switch (sb->iop_capabilities & 0x0000000C) {
+ case 0:
+ seq_printf(seq, "not configured\n");
+ break;
+ case 4:
+ seq_printf(seq, "Supports only 32-bit context fields\n");
+ break;
+ case 8:
+ seq_printf(seq, "Supports only 64-bit context fields\n");
+ break;
+ case 12:
+ seq_printf(seq, "Supports both 32-bit or 64-bit context fields "
+ "concurrently\n");
+ break;
+ default:
+ seq_printf(seq, "\n");
+ }
+ seq_printf(seq, " Inbound Peer Support : %s\n",
+ (sb->
+ iop_capabilities & 0x00000010) ? "Supported" :
+ "Not supported");
+ seq_printf(seq, " Outbound Peer Support : %s\n",
+ (sb->
+ iop_capabilities & 0x00000020) ? "Supported" :
+ "Not supported");
+ seq_printf(seq, " Peer to Peer Support : %s\n",
+ (sb->
+ iop_capabilities & 0x00000040) ? "Supported" :
+ "Not supported");
+
+ seq_printf(seq, "Desired private memory size : %d kB\n",
+ sb->desired_mem_size >> 10);
+ seq_printf(seq, "Allocated private memory size : %d kB\n",
+ sb->current_mem_size >> 10);
+ seq_printf(seq, "Private memory base address : %0#10x\n",
+ sb->current_mem_base);
+ seq_printf(seq, "Desired private I/O size : %d kB\n",
+ sb->desired_io_size >> 10);
+ seq_printf(seq, "Allocated private I/O size : %d kB\n",
+ sb->current_io_size >> 10);
+ seq_printf(seq, "Private I/O base address : %0#10x\n",
+ sb->current_io_base);
+
+ return 0;
+}
+
+static int i2o_seq_show_hw(struct seq_file *seq, void *v)
+{
+ struct i2o_controller *c = (struct i2o_controller *)seq->private;
+ static u32 work32[5];
+ static u8 *work8 = (u8 *) work32;
+ static u16 *work16 = (u16 *) work32;
+ int token;
+ u32 hwcap;
+
+ static char *cpu_table[] = {
+ "Intel 80960 series",
+ "AMD2900 series",
+ "Motorola 68000 series",
+ "ARM series",
+ "MIPS series",
+ "Sparc series",
+ "PowerPC series",
+ "Intel x86 series"
+ };
+
+ token =
+ i2o_parm_field_get(c->exec, 0x0000, -1, &work32, sizeof(work32));
+
+ if (token < 0) {
+ i2o_report_query_status(seq, token, "0x0000 IOP Hardware");
+ return 0;
+ }
+
+ seq_printf(seq, "I2O Vendor ID : %0#6x\n", work16[0]);
+ seq_printf(seq, "Product ID : %0#6x\n", work16[1]);
+ seq_printf(seq, "CPU : ");
+ if (work8[16] > 8)
+ seq_printf(seq, "Unknown\n");
+ else
+ seq_printf(seq, "%s\n", cpu_table[work8[16]]);
+ /* Anyone using ProcessorVersion? */
+
+ seq_printf(seq, "RAM : %dkB\n", work32[1] >> 10);
+ seq_printf(seq, "Non-Volatile Mem : %dkB\n", work32[2] >> 10);
+
+ hwcap = work32[3];
+ seq_printf(seq, "Capabilities : 0x%08x\n", hwcap);
+ seq_printf(seq, " [%s] Self booting\n",
+ (hwcap & 0x00000001) ? "+" : "-");
+ seq_printf(seq, " [%s] Upgradable IRTOS\n",
+ (hwcap & 0x00000002) ? "+" : "-");
+ seq_printf(seq, " [%s] Supports downloading DDMs\n",
+ (hwcap & 0x00000004) ? "+" : "-");
+ seq_printf(seq, " [%s] Supports installing DDMs\n",
+ (hwcap & 0x00000008) ? "+" : "-");
+ seq_printf(seq, " [%s] Battery-backed RAM\n",
+ (hwcap & 0x00000010) ? "+" : "-");
+
+ return 0;
+}
+
+/* Executive group 0003h - Executing DDM List (table) */
+static int i2o_seq_show_ddm_table(struct seq_file *seq, void *v)
+{
+ struct i2o_controller *c = (struct i2o_controller *)seq->private;
+ int token;
+ int i;
+
+ typedef struct _i2o_exec_execute_ddm_table {
+ u16 ddm_tid;
+ u8 module_type;
+ u8 reserved;
+ u16 i2o_vendor_id;
+ u16 module_id;
+ u8 module_name_version[28];
+ u32 data_size;
+ u32 code_size;
+ } i2o_exec_execute_ddm_table;
+
+ struct {
+ u16 result_count;
+ u16 pad;
+ u16 block_size;
+ u8 block_status;
+ u8 error_info_size;
+ u16 row_count;
+ u16 more_flag;
+ i2o_exec_execute_ddm_table ddm_table[I2O_MAX_MODULES];
+ } *result;
+
+ i2o_exec_execute_ddm_table ddm_table;
+ char tmp[28 + 1];
+
+ result = kmalloc(sizeof(*result), GFP_KERNEL);
+ if (!result)
+ return -ENOMEM;
+
+ token = i2o_parm_table_get(c->exec, I2O_PARAMS_TABLE_GET, 0x0003, -1,
+ NULL, 0, result, sizeof(*result));
+
+ if (token < 0) {
+ i2o_report_query_status(seq, token,
+ "0x0003 Executing DDM List");
+ goto out;
+ }
+
+ seq_printf(seq,
+ "Tid Module_type Vendor Mod_id Module_name Vrs Data_size Code_size\n");
+ ddm_table = result->ddm_table[0];
+
+ for (i = 0; i < result->row_count; ddm_table = result->ddm_table[++i]) {
+ seq_printf(seq, "0x%03x ", ddm_table.ddm_tid & 0xFFF);
+
+ switch (ddm_table.module_type) {
+ case 0x01:
+ seq_printf(seq, "Downloaded DDM ");
+ break;
+ case 0x22:
+ seq_printf(seq, "Embedded DDM ");
+ break;
+ default:
+ seq_printf(seq, " ");
+ }
+
+ seq_printf(seq, "%-#7x", ddm_table.i2o_vendor_id);
+ seq_printf(seq, "%-#8x", ddm_table.module_id);
+ seq_printf(seq, "%-29s",
+ chtostr(tmp, ddm_table.module_name_version, 28));
+ seq_printf(seq, "%9d ", ddm_table.data_size);
+ seq_printf(seq, "%8d", ddm_table.code_size);
+
+ seq_printf(seq, "\n");
+ }
+ out:
+ kfree(result);
+ return 0;
+}
+
+/* Executive group 0004h - Driver Store (scalar) */
+static int i2o_seq_show_driver_store(struct seq_file *seq, void *v)
+{
+ struct i2o_controller *c = (struct i2o_controller *)seq->private;
+ u32 work32[8];
+ int token;
+
+ token =
+ i2o_parm_field_get(c->exec, 0x0004, -1, &work32, sizeof(work32));
+ if (token < 0) {
+ i2o_report_query_status(seq, token, "0x0004 Driver Store");
+ return 0;
+ }
+
+ seq_printf(seq, "Module limit : %d\n"
+ "Module count : %d\n"
+ "Current space : %d kB\n"
+ "Free space : %d kB\n",
+ work32[0], work32[1], work32[2] >> 10, work32[3] >> 10);
+
+ return 0;
+}
+
+/* Executive group 0005h - Driver Store Table (table) */
+static int i2o_seq_show_drivers_stored(struct seq_file *seq, void *v)
+{
+ typedef struct _i2o_driver_store {
+ u16 stored_ddm_index;
+ u8 module_type;
+ u8 reserved;
+ u16 i2o_vendor_id;
+ u16 module_id;
+ u8 module_name_version[28];
+ u8 date[8];
+ u32 module_size;
+ u32 mpb_size;
+ u32 module_flags;
+ } i2o_driver_store_table;
+
+ struct i2o_controller *c = (struct i2o_controller *)seq->private;
+ int token;
+ int i;
+
+ typedef struct {
+ u16 result_count;
+ u16 pad;
+ u16 block_size;
+ u8 block_status;
+ u8 error_info_size;
+ u16 row_count;
+ u16 more_flag;
+ i2o_driver_store_table dst[I2O_MAX_MODULES];
+ } i2o_driver_result_table;
+
+ i2o_driver_result_table *result;
+ i2o_driver_store_table *dst;
+ char tmp[28 + 1];
+
+ result = kmalloc(sizeof(i2o_driver_result_table), GFP_KERNEL);
+ if (result == NULL)
+ return -ENOMEM;
+
+ token = i2o_parm_table_get(c->exec, I2O_PARAMS_TABLE_GET, 0x0005, -1,
+ NULL, 0, result, sizeof(*result));
+
+ if (token < 0) {
+ i2o_report_query_status(seq, token,
+ "0x0005 DRIVER STORE TABLE");
+ kfree(result);
+ return 0;
+ }
+
+ seq_printf(seq,
+ "# Module_type Vendor Mod_id Module_name Vrs"
+ "Date Mod_size Par_size Flags\n");
+ for (i = 0, dst = &result->dst[0]; i < result->row_count;
+ dst = &result->dst[++i]) {
+ seq_printf(seq, "%-3d", dst->stored_ddm_index);
+ switch (dst->module_type) {
+ case 0x01:
+ seq_printf(seq, "Downloaded DDM ");
+ break;
+ case 0x22:
+ seq_printf(seq, "Embedded DDM ");
+ break;
+ default:
+ seq_printf(seq, " ");
+ }
+
+ seq_printf(seq, "%-#7x", dst->i2o_vendor_id);
+ seq_printf(seq, "%-#8x", dst->module_id);
+ seq_printf(seq, "%-29s",
+ chtostr(tmp, dst->module_name_version, 28));
+ seq_printf(seq, "%-9s", chtostr(tmp, dst->date, 8));
+ seq_printf(seq, "%8d ", dst->module_size);
+ seq_printf(seq, "%8d ", dst->mpb_size);
+ seq_printf(seq, "0x%04x", dst->module_flags);
+ seq_printf(seq, "\n");
+ }
+
+ kfree(result);
+ return 0;
+}
+
+/* Generic group F000h - Params Descriptor (table) */
+static int i2o_seq_show_groups(struct seq_file *seq, void *v)
+{
+ struct i2o_device *d = (struct i2o_device *)seq->private;
+ int token;
+ int i;
+ u8 properties;
+
+ typedef struct _i2o_group_info {
+ u16 group_number;
+ u16 field_count;
+ u16 row_count;
+ u8 properties;
+ u8 reserved;
+ } i2o_group_info;
+
+ struct {
+ u16 result_count;
+ u16 pad;
+ u16 block_size;
+ u8 block_status;
+ u8 error_info_size;
+ u16 row_count;
+ u16 more_flag;
+ i2o_group_info group[256];
+ } *result;
+
+ result = kmalloc(sizeof(*result), GFP_KERNEL);
+ if (!result)
+ return -ENOMEM;
+
+ token = i2o_parm_table_get(d, I2O_PARAMS_TABLE_GET, 0xF000, -1, NULL, 0,
+ result, sizeof(*result));
+
+ if (token < 0) {
+ i2o_report_query_status(seq, token, "0xF000 Params Descriptor");
+ goto out;
+ }
+
+ seq_printf(seq,
+ "# Group FieldCount RowCount Type Add Del Clear\n");
+
+ for (i = 0; i < result->row_count; i++) {
+ seq_printf(seq, "%-3d", i);
+ seq_printf(seq, "0x%04X ", result->group[i].group_number);
+ seq_printf(seq, "%10d ", result->group[i].field_count);
+ seq_printf(seq, "%8d ", result->group[i].row_count);
+
+ properties = result->group[i].properties;
+ if (properties & 0x1)
+ seq_printf(seq, "Table ");
+ else
+ seq_printf(seq, "Scalar ");
+ if (properties & 0x2)
+ seq_printf(seq, " + ");
+ else
+ seq_printf(seq, " - ");
+ if (properties & 0x4)
+ seq_printf(seq, " + ");
+ else
+ seq_printf(seq, " - ");
+ if (properties & 0x8)
+ seq_printf(seq, " + ");
+ else
+ seq_printf(seq, " - ");
+
+ seq_printf(seq, "\n");
+ }
+
+ if (result->more_flag)
+ seq_printf(seq, "There is more...\n");
+ out:
+ kfree(result);
+ return 0;
+}
+
+/* Generic group F001h - Physical Device Table (table) */
+static int i2o_seq_show_phys_device(struct seq_file *seq, void *v)
+{
+ struct i2o_device *d = (struct i2o_device *)seq->private;
+ int token;
+ int i;
+
+ struct {
+ u16 result_count;
+ u16 pad;
+ u16 block_size;
+ u8 block_status;
+ u8 error_info_size;
+ u16 row_count;
+ u16 more_flag;
+ u32 adapter_id[64];
+ } result;
+
+ token = i2o_parm_table_get(d, I2O_PARAMS_TABLE_GET, 0xF001, -1, NULL, 0,
+ &result, sizeof(result));
+
+ if (token < 0) {
+ i2o_report_query_status(seq, token,
+ "0xF001 Physical Device Table");
+ return 0;
+ }
+
+ if (result.row_count)
+ seq_printf(seq, "# AdapterId\n");
+
+ for (i = 0; i < result.row_count; i++) {
+ seq_printf(seq, "%-2d", i);
+ seq_printf(seq, "%#7x\n", result.adapter_id[i]);
+ }
+
+ if (result.more_flag)
+ seq_printf(seq, "There is more...\n");
+
+ return 0;
+}
+
+/* Generic group F002h - Claimed Table (table) */
+static int i2o_seq_show_claimed(struct seq_file *seq, void *v)
+{
+ struct i2o_device *d = (struct i2o_device *)seq->private;
+ int token;
+ int i;
+
+ struct {
+ u16 result_count;
+ u16 pad;
+ u16 block_size;
+ u8 block_status;
+ u8 error_info_size;
+ u16 row_count;
+ u16 more_flag;
+ u16 claimed_tid[64];
+ } result;
+
+ token = i2o_parm_table_get(d, I2O_PARAMS_TABLE_GET, 0xF002, -1, NULL, 0,
+ &result, sizeof(result));
+
+ if (token < 0) {
+ i2o_report_query_status(seq, token, "0xF002 Claimed Table");
+ return 0;
+ }
+
+ if (result.row_count)
+ seq_printf(seq, "# ClaimedTid\n");
+
+ for (i = 0; i < result.row_count; i++) {
+ seq_printf(seq, "%-2d", i);
+ seq_printf(seq, "%#7x\n", result.claimed_tid[i]);
+ }
+
+ if (result.more_flag)
+ seq_printf(seq, "There is more...\n");
+
+ return 0;
+}
+
+/* Generic group F003h - User Table (table) */
+static int i2o_seq_show_users(struct seq_file *seq, void *v)
+{
+ struct i2o_device *d = (struct i2o_device *)seq->private;
+ int token;
+ int i;
+
+ typedef struct _i2o_user_table {
+ u16 instance;
+ u16 user_tid;
+ u8 claim_type;
+ u8 reserved1;
+ u16 reserved2;
+ } i2o_user_table;
+
+ struct {
+ u16 result_count;
+ u16 pad;
+ u16 block_size;
+ u8 block_status;
+ u8 error_info_size;
+ u16 row_count;
+ u16 more_flag;
+ i2o_user_table user[64];
+ } *result;
+
+ result = kmalloc(sizeof(*result), GFP_KERNEL);
+ if (!result)
+ return -ENOMEM;
+
+ token = i2o_parm_table_get(d, I2O_PARAMS_TABLE_GET, 0xF003, -1, NULL, 0,
+ result, sizeof(*result));
+
+ if (token < 0) {
+ i2o_report_query_status(seq, token, "0xF003 User Table");
+ goto out;
+ }
+
+ seq_printf(seq, "# Instance UserTid ClaimType\n");
+
+ for (i = 0; i < result->row_count; i++) {
+ seq_printf(seq, "%-3d", i);
+ seq_printf(seq, "%#8x ", result->user[i].instance);
+ seq_printf(seq, "%#7x ", result->user[i].user_tid);
+ seq_printf(seq, "%#9x\n", result->user[i].claim_type);
+ }
+
+ if (result->more_flag)
+ seq_printf(seq, "There is more...\n");
+ out:
+ kfree(result);
+ return 0;
+}
+
+/* Generic group F005h - Private message extensions (table) (optional) */
+static int i2o_seq_show_priv_msgs(struct seq_file *seq, void *v)
+{
+ struct i2o_device *d = (struct i2o_device *)seq->private;
+ int token;
+ int i;
+
+ typedef struct _i2o_private {
+ u16 ext_instance;
+ u16 organization_id;
+ u16 x_function_code;
+ } i2o_private;
+
+ struct {
+ u16 result_count;
+ u16 pad;
+ u16 block_size;
+ u8 block_status;
+ u8 error_info_size;
+ u16 row_count;
+ u16 more_flag;
+ i2o_private extension[64];
+ } result;
+
+ token = i2o_parm_table_get(d, I2O_PARAMS_TABLE_GET, 0xF000, -1, NULL, 0,
+ &result, sizeof(result));
+
+ if (token < 0) {
+ i2o_report_query_status(seq, token,
+ "0xF005 Private Message Extensions (optional)");
+ return 0;
+ }
+
+ seq_printf(seq, "Instance# OrgId FunctionCode\n");
+
+ for (i = 0; i < result.row_count; i++) {
+ seq_printf(seq, "%0#9x ", result.extension[i].ext_instance);
+ seq_printf(seq, "%0#6x ", result.extension[i].organization_id);
+ seq_printf(seq, "%0#6x", result.extension[i].x_function_code);
+
+ seq_printf(seq, "\n");
+ }
+
+ if (result.more_flag)
+ seq_printf(seq, "There is more...\n");
+
+ return 0;
+}
+
+/* Generic group F006h - Authorized User Table (table) */
+static int i2o_seq_show_authorized_users(struct seq_file *seq, void *v)
+{
+ struct i2o_device *d = (struct i2o_device *)seq->private;
+ int token;
+ int i;
+
+ struct {
+ u16 result_count;
+ u16 pad;
+ u16 block_size;
+ u8 block_status;
+ u8 error_info_size;
+ u16 row_count;
+ u16 more_flag;
+ u32 alternate_tid[64];
+ } result;
+
+ token = i2o_parm_table_get(d, I2O_PARAMS_TABLE_GET, 0xF006, -1, NULL, 0,
+ &result, sizeof(result));
+
+ if (token < 0) {
+ i2o_report_query_status(seq, token,
+ "0xF006 Autohorized User Table");
+ return 0;
+ }
+
+ if (result.row_count)
+ seq_printf(seq, "# AlternateTid\n");
+
+ for (i = 0; i < result.row_count; i++) {
+ seq_printf(seq, "%-2d", i);
+ seq_printf(seq, "%#7x ", result.alternate_tid[i]);
+ }
+
+ if (result.more_flag)
+ seq_printf(seq, "There is more...\n");
+
+ return 0;
+}
+
+/* Generic group F100h - Device Identity (scalar) */
+static int i2o_seq_show_dev_identity(struct seq_file *seq, void *v)
+{
+ struct i2o_device *d = (struct i2o_device *)seq->private;
+ static u32 work32[128]; // allow for "stuff" + up to 256 byte (max) serial number
+ // == (allow) 512d bytes (max)
+ static u16 *work16 = (u16 *) work32;
+ int token;
+ char tmp[16 + 1];
+
+ token = i2o_parm_field_get(d, 0xF100, -1, &work32, sizeof(work32));
+
+ if (token < 0) {
+ i2o_report_query_status(seq, token, "0xF100 Device Identity");
+ return 0;
+ }
+
+ seq_printf(seq, "Device Class : %s\n", i2o_get_class_name(work16[0]));
+ seq_printf(seq, "Owner TID : %0#5x\n", work16[2]);
+ seq_printf(seq, "Parent TID : %0#5x\n", work16[3]);
+ seq_printf(seq, "Vendor info : %s\n",
+ chtostr(tmp, (u8 *) (work32 + 2), 16));
+ seq_printf(seq, "Product info : %s\n",
+ chtostr(tmp, (u8 *) (work32 + 6), 16));
+ seq_printf(seq, "Description : %s\n",
+ chtostr(tmp, (u8 *) (work32 + 10), 16));
+ seq_printf(seq, "Product rev. : %s\n",
+ chtostr(tmp, (u8 *) (work32 + 14), 8));
+
+ seq_printf(seq, "Serial number : ");
+ print_serial_number(seq, (u8 *) (work32 + 16),
+ /* allow for SNLen plus
+ * possible trailing '\0'
+ */
+ sizeof(work32) - (16 * sizeof(u32)) - 2);
+ seq_printf(seq, "\n");
+
+ return 0;
+}
+
+static int i2o_seq_show_dev_name(struct seq_file *seq, void *v)
+{
+ struct i2o_device *d = (struct i2o_device *)seq->private;
+
+ seq_printf(seq, "%s\n", dev_name(&d->device));
+
+ return 0;
+}
+
+/* Generic group F101h - DDM Identity (scalar) */
+static int i2o_seq_show_ddm_identity(struct seq_file *seq, void *v)
+{
+ struct i2o_device *d = (struct i2o_device *)seq->private;
+ int token;
+
+ struct {
+ u16 ddm_tid;
+ u8 module_name[24];
+ u8 module_rev[8];
+ u8 sn_format;
+ u8 serial_number[12];
+ u8 pad[256]; // allow up to 256 byte (max) serial number
+ } result;
+
+ char tmp[24 + 1];
+
+ token = i2o_parm_field_get(d, 0xF101, -1, &result, sizeof(result));
+
+ if (token < 0) {
+ i2o_report_query_status(seq, token, "0xF101 DDM Identity");
+ return 0;
+ }
+
+ seq_printf(seq, "Registering DDM TID : 0x%03x\n", result.ddm_tid);
+ seq_printf(seq, "Module name : %s\n",
+ chtostr(tmp, result.module_name, 24));
+ seq_printf(seq, "Module revision : %s\n",
+ chtostr(tmp, result.module_rev, 8));
+
+ seq_printf(seq, "Serial number : ");
+ print_serial_number(seq, result.serial_number, sizeof(result) - 36);
+ /* allow for SNLen plus possible trailing '\0' */
+
+ seq_printf(seq, "\n");
+
+ return 0;
+}
+
+/* Generic group F102h - User Information (scalar) */
+static int i2o_seq_show_uinfo(struct seq_file *seq, void *v)
+{
+ struct i2o_device *d = (struct i2o_device *)seq->private;
+ int token;
+
+ struct {
+ u8 device_name[64];
+ u8 service_name[64];
+ u8 physical_location[64];
+ u8 instance_number[4];
+ } result;
+
+ char tmp[64 + 1];
+
+ token = i2o_parm_field_get(d, 0xF102, -1, &result, sizeof(result));
+
+ if (token < 0) {
+ i2o_report_query_status(seq, token, "0xF102 User Information");
+ return 0;
+ }
+
+ seq_printf(seq, "Device name : %s\n",
+ chtostr(tmp, result.device_name, 64));
+ seq_printf(seq, "Service name : %s\n",
+ chtostr(tmp, result.service_name, 64));
+ seq_printf(seq, "Physical name : %s\n",
+ chtostr(tmp, result.physical_location, 64));
+ seq_printf(seq, "Instance number : %s\n",
+ chtostr(tmp, result.instance_number, 4));
+
+ return 0;
+}
+
+/* Generic group F103h - SGL Operating Limits (scalar) */
+static int i2o_seq_show_sgl_limits(struct seq_file *seq, void *v)
+{
+ struct i2o_device *d = (struct i2o_device *)seq->private;
+ static u32 work32[12];
+ static u16 *work16 = (u16 *) work32;
+ static u8 *work8 = (u8 *) work32;
+ int token;
+
+ token = i2o_parm_field_get(d, 0xF103, -1, &work32, sizeof(work32));
+
+ if (token < 0) {
+ i2o_report_query_status(seq, token,
+ "0xF103 SGL Operating Limits");
+ return 0;
+ }
+
+ seq_printf(seq, "SGL chain size : %d\n", work32[0]);
+ seq_printf(seq, "Max SGL chain size : %d\n", work32[1]);
+ seq_printf(seq, "SGL chain size target : %d\n", work32[2]);
+ seq_printf(seq, "SGL frag count : %d\n", work16[6]);
+ seq_printf(seq, "Max SGL frag count : %d\n", work16[7]);
+ seq_printf(seq, "SGL frag count target : %d\n", work16[8]);
+
+/* FIXME
+ if (d->i2oversion == 0x02)
+ {
+*/
+ seq_printf(seq, "SGL data alignment : %d\n", work16[8]);
+ seq_printf(seq, "SGL addr limit : %d\n", work8[20]);
+ seq_printf(seq, "SGL addr sizes supported : ");
+ if (work8[21] & 0x01)
+ seq_printf(seq, "32 bit ");
+ if (work8[21] & 0x02)
+ seq_printf(seq, "64 bit ");
+ if (work8[21] & 0x04)
+ seq_printf(seq, "96 bit ");
+ if (work8[21] & 0x08)
+ seq_printf(seq, "128 bit ");
+ seq_printf(seq, "\n");
+/*
+ }
+*/
+
+ return 0;
+}
+
+/* Generic group F200h - Sensors (scalar) */
+static int i2o_seq_show_sensors(struct seq_file *seq, void *v)
+{
+ struct i2o_device *d = (struct i2o_device *)seq->private;
+ int token;
+
+ struct {
+ u16 sensor_instance;
+ u8 component;
+ u16 component_instance;
+ u8 sensor_class;
+ u8 sensor_type;
+ u8 scaling_exponent;
+ u32 actual_reading;
+ u32 minimum_reading;
+ u32 low2lowcat_treshold;
+ u32 lowcat2low_treshold;
+ u32 lowwarn2low_treshold;
+ u32 low2lowwarn_treshold;
+ u32 norm2lowwarn_treshold;
+ u32 lowwarn2norm_treshold;
+ u32 nominal_reading;
+ u32 hiwarn2norm_treshold;
+ u32 norm2hiwarn_treshold;
+ u32 high2hiwarn_treshold;
+ u32 hiwarn2high_treshold;
+ u32 hicat2high_treshold;
+ u32 hi2hicat_treshold;
+ u32 maximum_reading;
+ u8 sensor_state;
+ u16 event_enable;
+ } result;
+
+ token = i2o_parm_field_get(d, 0xF200, -1, &result, sizeof(result));
+
+ if (token < 0) {
+ i2o_report_query_status(seq, token,
+ "0xF200 Sensors (optional)");
+ return 0;
+ }
+
+ seq_printf(seq, "Sensor instance : %d\n", result.sensor_instance);
+
+ seq_printf(seq, "Component : %d = ", result.component);
+ switch (result.component) {
+ case 0:
+ seq_printf(seq, "Other");
+ break;
+ case 1:
+ seq_printf(seq, "Planar logic Board");
+ break;
+ case 2:
+ seq_printf(seq, "CPU");
+ break;
+ case 3:
+ seq_printf(seq, "Chassis");
+ break;
+ case 4:
+ seq_printf(seq, "Power Supply");
+ break;
+ case 5:
+ seq_printf(seq, "Storage");
+ break;
+ case 6:
+ seq_printf(seq, "External");
+ break;
+ }
+ seq_printf(seq, "\n");
+
+ seq_printf(seq, "Component instance : %d\n",
+ result.component_instance);
+ seq_printf(seq, "Sensor class : %s\n",
+ result.sensor_class ? "Analog" : "Digital");
+
+ seq_printf(seq, "Sensor type : %d = ", result.sensor_type);
+ switch (result.sensor_type) {
+ case 0:
+ seq_printf(seq, "Other\n");
+ break;
+ case 1:
+ seq_printf(seq, "Thermal\n");
+ break;
+ case 2:
+ seq_printf(seq, "DC voltage (DC volts)\n");
+ break;
+ case 3:
+ seq_printf(seq, "AC voltage (AC volts)\n");
+ break;
+ case 4:
+ seq_printf(seq, "DC current (DC amps)\n");
+ break;
+ case 5:
+ seq_printf(seq, "AC current (AC volts)\n");
+ break;
+ case 6:
+ seq_printf(seq, "Door open\n");
+ break;
+ case 7:
+ seq_printf(seq, "Fan operational\n");
+ break;
+ }
+
+ seq_printf(seq, "Scaling exponent : %d\n",
+ result.scaling_exponent);
+ seq_printf(seq, "Actual reading : %d\n", result.actual_reading);
+ seq_printf(seq, "Minimum reading : %d\n", result.minimum_reading);
+ seq_printf(seq, "Low2LowCat treshold : %d\n",
+ result.low2lowcat_treshold);
+ seq_printf(seq, "LowCat2Low treshold : %d\n",
+ result.lowcat2low_treshold);
+ seq_printf(seq, "LowWarn2Low treshold : %d\n",
+ result.lowwarn2low_treshold);
+ seq_printf(seq, "Low2LowWarn treshold : %d\n",
+ result.low2lowwarn_treshold);
+ seq_printf(seq, "Norm2LowWarn treshold : %d\n",
+ result.norm2lowwarn_treshold);
+ seq_printf(seq, "LowWarn2Norm treshold : %d\n",
+ result.lowwarn2norm_treshold);
+ seq_printf(seq, "Nominal reading : %d\n", result.nominal_reading);
+ seq_printf(seq, "HiWarn2Norm treshold : %d\n",
+ result.hiwarn2norm_treshold);
+ seq_printf(seq, "Norm2HiWarn treshold : %d\n",
+ result.norm2hiwarn_treshold);
+ seq_printf(seq, "High2HiWarn treshold : %d\n",
+ result.high2hiwarn_treshold);
+ seq_printf(seq, "HiWarn2High treshold : %d\n",
+ result.hiwarn2high_treshold);
+ seq_printf(seq, "HiCat2High treshold : %d\n",
+ result.hicat2high_treshold);
+ seq_printf(seq, "High2HiCat treshold : %d\n",
+ result.hi2hicat_treshold);
+ seq_printf(seq, "Maximum reading : %d\n", result.maximum_reading);
+
+ seq_printf(seq, "Sensor state : %d = ", result.sensor_state);
+ switch (result.sensor_state) {
+ case 0:
+ seq_printf(seq, "Normal\n");
+ break;
+ case 1:
+ seq_printf(seq, "Abnormal\n");
+ break;
+ case 2:
+ seq_printf(seq, "Unknown\n");
+ break;
+ case 3:
+ seq_printf(seq, "Low Catastrophic (LoCat)\n");
+ break;
+ case 4:
+ seq_printf(seq, "Low (Low)\n");
+ break;
+ case 5:
+ seq_printf(seq, "Low Warning (LoWarn)\n");
+ break;
+ case 6:
+ seq_printf(seq, "High Warning (HiWarn)\n");
+ break;
+ case 7:
+ seq_printf(seq, "High (High)\n");
+ break;
+ case 8:
+ seq_printf(seq, "High Catastrophic (HiCat)\n");
+ break;
+ }
+
+ seq_printf(seq, "Event_enable : 0x%02X\n", result.event_enable);
+ seq_printf(seq, " [%s] Operational state change. \n",
+ (result.event_enable & 0x01) ? "+" : "-");
+ seq_printf(seq, " [%s] Low catastrophic. \n",
+ (result.event_enable & 0x02) ? "+" : "-");
+ seq_printf(seq, " [%s] Low reading. \n",
+ (result.event_enable & 0x04) ? "+" : "-");
+ seq_printf(seq, " [%s] Low warning. \n",
+ (result.event_enable & 0x08) ? "+" : "-");
+ seq_printf(seq,
+ " [%s] Change back to normal from out of range state. \n",
+ (result.event_enable & 0x10) ? "+" : "-");
+ seq_printf(seq, " [%s] High warning. \n",
+ (result.event_enable & 0x20) ? "+" : "-");
+ seq_printf(seq, " [%s] High reading. \n",
+ (result.event_enable & 0x40) ? "+" : "-");
+ seq_printf(seq, " [%s] High catastrophic. \n",
+ (result.event_enable & 0x80) ? "+" : "-");
+
+ return 0;
+}
+
+static int i2o_seq_open_hrt(struct inode *inode, struct file *file)
+{
+ return single_open(file, i2o_seq_show_hrt, PDE_DATA(inode));
+};
+
+static int i2o_seq_open_lct(struct inode *inode, struct file *file)
+{
+ return single_open(file, i2o_seq_show_lct, PDE_DATA(inode));
+};
+
+static int i2o_seq_open_status(struct inode *inode, struct file *file)
+{
+ return single_open(file, i2o_seq_show_status, PDE_DATA(inode));
+};
+
+static int i2o_seq_open_hw(struct inode *inode, struct file *file)
+{
+ return single_open(file, i2o_seq_show_hw, PDE_DATA(inode));
+};
+
+static int i2o_seq_open_ddm_table(struct inode *inode, struct file *file)
+{
+ return single_open(file, i2o_seq_show_ddm_table, PDE_DATA(inode));
+};
+
+static int i2o_seq_open_driver_store(struct inode *inode, struct file *file)
+{
+ return single_open(file, i2o_seq_show_driver_store, PDE_DATA(inode));
+};
+
+static int i2o_seq_open_drivers_stored(struct inode *inode, struct file *file)
+{
+ return single_open(file, i2o_seq_show_drivers_stored, PDE_DATA(inode));
+};
+
+static int i2o_seq_open_groups(struct inode *inode, struct file *file)
+{
+ return single_open(file, i2o_seq_show_groups, PDE_DATA(inode));
+};
+
+static int i2o_seq_open_phys_device(struct inode *inode, struct file *file)
+{
+ return single_open(file, i2o_seq_show_phys_device, PDE_DATA(inode));
+};
+
+static int i2o_seq_open_claimed(struct inode *inode, struct file *file)
+{
+ return single_open(file, i2o_seq_show_claimed, PDE_DATA(inode));
+};
+
+static int i2o_seq_open_users(struct inode *inode, struct file *file)
+{
+ return single_open(file, i2o_seq_show_users, PDE_DATA(inode));
+};
+
+static int i2o_seq_open_priv_msgs(struct inode *inode, struct file *file)
+{
+ return single_open(file, i2o_seq_show_priv_msgs, PDE_DATA(inode));
+};
+
+static int i2o_seq_open_authorized_users(struct inode *inode, struct file *file)
+{
+ return single_open(file, i2o_seq_show_authorized_users,
+ PDE_DATA(inode));
+};
+
+static int i2o_seq_open_dev_identity(struct inode *inode, struct file *file)
+{
+ return single_open(file, i2o_seq_show_dev_identity, PDE_DATA(inode));
+};
+
+static int i2o_seq_open_ddm_identity(struct inode *inode, struct file *file)
+{
+ return single_open(file, i2o_seq_show_ddm_identity, PDE_DATA(inode));
+};
+
+static int i2o_seq_open_uinfo(struct inode *inode, struct file *file)
+{
+ return single_open(file, i2o_seq_show_uinfo, PDE_DATA(inode));
+};
+
+static int i2o_seq_open_sgl_limits(struct inode *inode, struct file *file)
+{
+ return single_open(file, i2o_seq_show_sgl_limits, PDE_DATA(inode));
+};
+
+static int i2o_seq_open_sensors(struct inode *inode, struct file *file)
+{
+ return single_open(file, i2o_seq_show_sensors, PDE_DATA(inode));
+};
+
+static int i2o_seq_open_dev_name(struct inode *inode, struct file *file)
+{
+ return single_open(file, i2o_seq_show_dev_name, PDE_DATA(inode));
+};
+
+static const struct file_operations i2o_seq_fops_lct = {
+ .open = i2o_seq_open_lct,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static const struct file_operations i2o_seq_fops_hrt = {
+ .open = i2o_seq_open_hrt,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static const struct file_operations i2o_seq_fops_status = {
+ .open = i2o_seq_open_status,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static const struct file_operations i2o_seq_fops_hw = {
+ .open = i2o_seq_open_hw,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static const struct file_operations i2o_seq_fops_ddm_table = {
+ .open = i2o_seq_open_ddm_table,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static const struct file_operations i2o_seq_fops_driver_store = {
+ .open = i2o_seq_open_driver_store,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static const struct file_operations i2o_seq_fops_drivers_stored = {
+ .open = i2o_seq_open_drivers_stored,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static const struct file_operations i2o_seq_fops_groups = {
+ .open = i2o_seq_open_groups,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static const struct file_operations i2o_seq_fops_phys_device = {
+ .open = i2o_seq_open_phys_device,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static const struct file_operations i2o_seq_fops_claimed = {
+ .open = i2o_seq_open_claimed,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static const struct file_operations i2o_seq_fops_users = {
+ .open = i2o_seq_open_users,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static const struct file_operations i2o_seq_fops_priv_msgs = {
+ .open = i2o_seq_open_priv_msgs,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static const struct file_operations i2o_seq_fops_authorized_users = {
+ .open = i2o_seq_open_authorized_users,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static const struct file_operations i2o_seq_fops_dev_name = {
+ .open = i2o_seq_open_dev_name,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static const struct file_operations i2o_seq_fops_dev_identity = {
+ .open = i2o_seq_open_dev_identity,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static const struct file_operations i2o_seq_fops_ddm_identity = {
+ .open = i2o_seq_open_ddm_identity,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static const struct file_operations i2o_seq_fops_uinfo = {
+ .open = i2o_seq_open_uinfo,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static const struct file_operations i2o_seq_fops_sgl_limits = {
+ .open = i2o_seq_open_sgl_limits,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static const struct file_operations i2o_seq_fops_sensors = {
+ .open = i2o_seq_open_sensors,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+/*
+ * IOP specific entries...write field just in case someone
+ * ever wants one.
+ */
+static i2o_proc_entry i2o_proc_generic_iop_entries[] = {
+ {"hrt", S_IFREG | S_IRUGO, &i2o_seq_fops_hrt},
+ {"lct", S_IFREG | S_IRUGO, &i2o_seq_fops_lct},
+ {"status", S_IFREG | S_IRUGO, &i2o_seq_fops_status},
+ {"hw", S_IFREG | S_IRUGO, &i2o_seq_fops_hw},
+ {"ddm_table", S_IFREG | S_IRUGO, &i2o_seq_fops_ddm_table},
+ {"driver_store", S_IFREG | S_IRUGO, &i2o_seq_fops_driver_store},
+ {"drivers_stored", S_IFREG | S_IRUGO, &i2o_seq_fops_drivers_stored},
+ {NULL, 0, NULL}
+};
+
+/*
+ * Device specific entries
+ */
+static i2o_proc_entry generic_dev_entries[] = {
+ {"groups", S_IFREG | S_IRUGO, &i2o_seq_fops_groups},
+ {"phys_dev", S_IFREG | S_IRUGO, &i2o_seq_fops_phys_device},
+ {"claimed", S_IFREG | S_IRUGO, &i2o_seq_fops_claimed},
+ {"users", S_IFREG | S_IRUGO, &i2o_seq_fops_users},
+ {"priv_msgs", S_IFREG | S_IRUGO, &i2o_seq_fops_priv_msgs},
+ {"authorized_users", S_IFREG | S_IRUGO, &i2o_seq_fops_authorized_users},
+ {"dev_identity", S_IFREG | S_IRUGO, &i2o_seq_fops_dev_identity},
+ {"ddm_identity", S_IFREG | S_IRUGO, &i2o_seq_fops_ddm_identity},
+ {"user_info", S_IFREG | S_IRUGO, &i2o_seq_fops_uinfo},
+ {"sgl_limits", S_IFREG | S_IRUGO, &i2o_seq_fops_sgl_limits},
+ {"sensors", S_IFREG | S_IRUGO, &i2o_seq_fops_sensors},
+ {NULL, 0, NULL}
+};
+
+/*
+ * Storage unit specific entries (SCSI Periph, BS) with device names
+ */
+static i2o_proc_entry rbs_dev_entries[] = {
+ {"dev_name", S_IFREG | S_IRUGO, &i2o_seq_fops_dev_name},
+ {NULL, 0, NULL}
+};
+
+/**
+ * i2o_proc_create_entries - Creates proc dir entries
+ * @dir: proc dir entry under which the entries should be placed
+ * @i2o_pe: pointer to the entries which should be added
+ * @data: pointer to I2O controller or device
+ *
+ * Create proc dir entries for a I2O controller or I2O device.
+ *
+ * Returns 0 on success or negative error code on failure.
+ */
+static int i2o_proc_create_entries(struct proc_dir_entry *dir,
+ i2o_proc_entry * i2o_pe, void *data)
+{
+ struct proc_dir_entry *tmp;
+
+ while (i2o_pe->name) {
+ tmp = proc_create_data(i2o_pe->name, i2o_pe->mode, dir,
+ i2o_pe->fops, data);
+ if (!tmp)
+ return -1;
+
+ i2o_pe++;
+ }
+
+ return 0;
+}
+
+/**
+ * i2o_proc_device_add - Add an I2O device to the proc dir
+ * @dir: proc dir entry to which the device should be added
+ * @dev: I2O device which should be added
+ *
+ * Add an I2O device to the proc dir entry dir and create the entries for
+ * the device depending on the class of the I2O device.
+ */
+static void i2o_proc_device_add(struct proc_dir_entry *dir,
+ struct i2o_device *dev)
+{
+ char buff[10];
+ struct proc_dir_entry *devdir;
+ i2o_proc_entry *i2o_pe = NULL;
+
+ sprintf(buff, "%03x", dev->lct_data.tid);
+
+ osm_debug("adding device /proc/i2o/%s/%s\n", dev->iop->name, buff);
+
+ devdir = proc_mkdir_data(buff, 0, dir, dev);
+ if (!devdir) {
+ osm_warn("Could not allocate procdir!\n");
+ return;
+ }
+
+ i2o_proc_create_entries(devdir, generic_dev_entries, dev);
+
+ /* Inform core that we want updates about this device's status */
+ switch (dev->lct_data.class_id) {
+ case I2O_CLASS_SCSI_PERIPHERAL:
+ case I2O_CLASS_RANDOM_BLOCK_STORAGE:
+ i2o_pe = rbs_dev_entries;
+ break;
+ default:
+ break;
+ }
+ if (i2o_pe)
+ i2o_proc_create_entries(devdir, i2o_pe, dev);
+}
+
+/**
+ * i2o_proc_iop_add - Add an I2O controller to the i2o proc tree
+ * @dir: parent proc dir entry
+ * @c: I2O controller which should be added
+ *
+ * Add the entries to the parent proc dir entry. Also each device is added
+ * to the controllers proc dir entry.
+ *
+ * Returns 0 on success or negative error code on failure.
+ */
+static int i2o_proc_iop_add(struct proc_dir_entry *dir,
+ struct i2o_controller *c)
+{
+ struct proc_dir_entry *iopdir;
+ struct i2o_device *dev;
+
+ osm_debug("adding IOP /proc/i2o/%s\n", c->name);
+
+ iopdir = proc_mkdir_data(c->name, 0, dir, c);
+ if (!iopdir)
+ return -1;
+
+ i2o_proc_create_entries(iopdir, i2o_proc_generic_iop_entries, c);
+
+ list_for_each_entry(dev, &c->devices, list)
+ i2o_proc_device_add(iopdir, dev);
+
+ return 0;
+}
+
+/**
+ * i2o_proc_fs_create - Create the i2o proc fs.
+ *
+ * Iterate over each I2O controller and create the entries for it.
+ *
+ * Returns 0 on success or negative error code on failure.
+ */
+static int __init i2o_proc_fs_create(void)
+{
+ struct i2o_controller *c;
+
+ i2o_proc_dir_root = proc_mkdir("i2o", NULL);
+ if (!i2o_proc_dir_root)
+ return -1;
+
+ list_for_each_entry(c, &i2o_controllers, list)
+ i2o_proc_iop_add(i2o_proc_dir_root, c);
+
+ return 0;
+};
+
+/**
+ * i2o_proc_fs_destroy - Cleanup the all i2o proc entries
+ *
+ * Iterate over each I2O controller and remove the entries for it.
+ *
+ * Returns 0 on success or negative error code on failure.
+ */
+static int __exit i2o_proc_fs_destroy(void)
+{
+ remove_proc_subtree("i2o", NULL);
+
+ return 0;
+};
+
+/**
+ * i2o_proc_init - Init function for procfs
+ *
+ * Registers Proc OSM and creates procfs entries.
+ *
+ * Returns 0 on success or negative error code on failure.
+ */
+static int __init i2o_proc_init(void)
+{
+ int rc;
+
+ printk(KERN_INFO OSM_DESCRIPTION " v" OSM_VERSION "\n");
+
+ rc = i2o_driver_register(&i2o_proc_driver);
+ if (rc)
+ return rc;
+
+ rc = i2o_proc_fs_create();
+ if (rc) {
+ i2o_driver_unregister(&i2o_proc_driver);
+ return rc;
+ }
+
+ return 0;
+};
+
+/**
+ * i2o_proc_exit - Exit function for procfs
+ *
+ * Unregisters Proc OSM and removes procfs entries.
+ */
+static void __exit i2o_proc_exit(void)
+{
+ i2o_driver_unregister(&i2o_proc_driver);
+ i2o_proc_fs_destroy();
+};
+
+MODULE_AUTHOR("Deepak Saxena");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION(OSM_DESCRIPTION);
+MODULE_VERSION(OSM_VERSION);
+
+module_init(i2o_proc_init);
+module_exit(i2o_proc_exit);
diff --git a/drivers/staging/i2o/i2o_scsi.c b/drivers/staging/i2o/i2o_scsi.c
new file mode 100644
index 000000000..1b11dcb3f
--- /dev/null
+++ b/drivers/staging/i2o/i2o_scsi.c
@@ -0,0 +1,814 @@
+/*
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * For the avoidance of doubt the "preferred form" of this code is one which
+ * is in an open non patent encumbered format. Where cryptographic key signing
+ * forms part of the process of creating an executable the information
+ * including keys needed to generate an equivalently functional executable
+ * are deemed to be part of the source code.
+ *
+ * Complications for I2O scsi
+ *
+ * o Each (bus,lun) is a logical device in I2O. We keep a map
+ * table. We spoof failed selection for unmapped units
+ * o Request sense buffers can come back for free.
+ * o Scatter gather is a bit dynamic. We have to investigate at
+ * setup time.
+ * o Some of our resources are dynamically shared. The i2o core
+ * needs a message reservation protocol to avoid swap v net
+ * deadlocking. We need to back off queue requests.
+ *
+ * In general the firmware wants to help. Where its help isn't performance
+ * useful we just ignore the aid. Its not worth the code in truth.
+ *
+ * Fixes/additions:
+ * Steve Ralston:
+ * Scatter gather now works
+ * Markus Lidel <Markus.Lidel@shadowconnect.com>:
+ * Minor fixes for 2.6.
+ *
+ * To Do:
+ * 64bit cleanups
+ * Fix the resource management problems.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/string.h>
+#include <linux/ioport.h>
+#include <linux/jiffies.h>
+#include <linux/interrupt.h>
+#include <linux/timer.h>
+#include <linux/delay.h>
+#include <linux/proc_fs.h>
+#include <linux/prefetch.h>
+#include <linux/pci.h>
+#include <linux/blkdev.h>
+#include "i2o.h"
+#include <linux/scatterlist.h>
+
+#include <asm/dma.h>
+#include <asm/io.h>
+#include <linux/atomic.h>
+
+#include <scsi/scsi.h>
+#include <scsi/scsi_host.h>
+#include <scsi/scsi_device.h>
+#include <scsi/scsi_cmnd.h>
+#include <scsi/sg.h>
+
+#define OSM_NAME "scsi-osm"
+#define OSM_VERSION "1.316"
+#define OSM_DESCRIPTION "I2O SCSI Peripheral OSM"
+
+static struct i2o_driver i2o_scsi_driver;
+
+static unsigned int i2o_scsi_max_id = 16;
+static unsigned int i2o_scsi_max_lun = 255;
+
+struct i2o_scsi_host {
+ struct Scsi_Host *scsi_host; /* pointer to the SCSI host */
+ struct i2o_controller *iop; /* pointer to the I2O controller */
+ u64 lun; /* lun's used for block devices */
+ struct i2o_device *channel[0]; /* channel->i2o_dev mapping table */
+};
+
+static struct scsi_host_template i2o_scsi_host_template;
+
+#define I2O_SCSI_CAN_QUEUE 4
+
+/* SCSI OSM class handling definition */
+static struct i2o_class_id i2o_scsi_class_id[] = {
+ {I2O_CLASS_SCSI_PERIPHERAL},
+ {I2O_CLASS_END}
+};
+
+static struct i2o_scsi_host *i2o_scsi_host_alloc(struct i2o_controller *c)
+{
+ struct i2o_scsi_host *i2o_shost;
+ struct i2o_device *i2o_dev;
+ struct Scsi_Host *scsi_host;
+ int max_channel = 0;
+ u8 type;
+ int i;
+ size_t size;
+ u16 body_size = 6;
+
+#ifdef CONFIG_I2O_EXT_ADAPTEC
+ if (c->adaptec)
+ body_size = 8;
+#endif
+
+ list_for_each_entry(i2o_dev, &c->devices, list)
+ if (i2o_dev->lct_data.class_id == I2O_CLASS_BUS_ADAPTER) {
+ if (!i2o_parm_field_get(i2o_dev, 0x0000, 0, &type, 1)
+ && (type == 0x01)) /* SCSI bus */
+ max_channel++;
+ }
+
+ if (!max_channel) {
+ osm_warn("no channels found on %s\n", c->name);
+ return ERR_PTR(-EFAULT);
+ }
+
+ size = max_channel * sizeof(struct i2o_device *)
+ + sizeof(struct i2o_scsi_host);
+
+ scsi_host = scsi_host_alloc(&i2o_scsi_host_template, size);
+ if (!scsi_host) {
+ osm_warn("Could not allocate SCSI host\n");
+ return ERR_PTR(-ENOMEM);
+ }
+
+ scsi_host->max_channel = max_channel - 1;
+ scsi_host->max_id = i2o_scsi_max_id;
+ scsi_host->max_lun = i2o_scsi_max_lun;
+ scsi_host->this_id = c->unit;
+ scsi_host->sg_tablesize = i2o_sg_tablesize(c, body_size);
+
+ i2o_shost = (struct i2o_scsi_host *)scsi_host->hostdata;
+ i2o_shost->scsi_host = scsi_host;
+ i2o_shost->iop = c;
+ i2o_shost->lun = 1;
+
+ i = 0;
+ list_for_each_entry(i2o_dev, &c->devices, list)
+ if (i2o_dev->lct_data.class_id == I2O_CLASS_BUS_ADAPTER) {
+ if (!i2o_parm_field_get(i2o_dev, 0x0000, 0, &type, 1)
+ && (type == 0x01)) /* only SCSI bus */
+ i2o_shost->channel[i++] = i2o_dev;
+
+ if (i >= max_channel)
+ break;
+ }
+
+ return i2o_shost;
+};
+
+/**
+ * i2o_scsi_get_host - Get an I2O SCSI host
+ * @c: I2O controller to for which to get the SCSI host
+ *
+ * If the I2O controller already exists as SCSI host, the SCSI host
+ * is returned, otherwise the I2O controller is added to the SCSI
+ * core.
+ *
+ * Returns pointer to the I2O SCSI host on success or NULL on failure.
+ */
+static struct i2o_scsi_host *i2o_scsi_get_host(struct i2o_controller *c)
+{
+ return c->driver_data[i2o_scsi_driver.context];
+};
+
+/**
+ * i2o_scsi_remove - Remove I2O device from SCSI core
+ * @dev: device which should be removed
+ *
+ * Removes the I2O device from the SCSI core again.
+ *
+ * Returns 0 on success.
+ */
+static int i2o_scsi_remove(struct device *dev)
+{
+ struct i2o_device *i2o_dev = to_i2o_device(dev);
+ struct i2o_controller *c = i2o_dev->iop;
+ struct i2o_scsi_host *i2o_shost;
+ struct scsi_device *scsi_dev;
+
+ osm_info("device removed (TID: %03x)\n", i2o_dev->lct_data.tid);
+
+ i2o_shost = i2o_scsi_get_host(c);
+
+ shost_for_each_device(scsi_dev, i2o_shost->scsi_host)
+ if (scsi_dev->hostdata == i2o_dev) {
+ sysfs_remove_link(&i2o_dev->device.kobj, "scsi");
+ scsi_remove_device(scsi_dev);
+ scsi_device_put(scsi_dev);
+ break;
+ }
+
+ return 0;
+};
+
+/**
+ * i2o_scsi_probe - verify if dev is a I2O SCSI device and install it
+ * @dev: device to verify if it is a I2O SCSI device
+ *
+ * Retrieve channel, id and lun for I2O device. If everything goes well
+ * register the I2O device as SCSI device on the I2O SCSI controller.
+ *
+ * Returns 0 on success or negative error code on failure.
+ */
+static int i2o_scsi_probe(struct device *dev)
+{
+ struct i2o_device *i2o_dev = to_i2o_device(dev);
+ struct i2o_controller *c = i2o_dev->iop;
+ struct i2o_scsi_host *i2o_shost;
+ struct Scsi_Host *scsi_host;
+ struct i2o_device *parent;
+ struct scsi_device *scsi_dev;
+ u32 id = -1;
+ u64 lun = -1;
+ int channel = -1;
+ int i, rc;
+
+ i2o_shost = i2o_scsi_get_host(c);
+ if (!i2o_shost)
+ return -EFAULT;
+
+ scsi_host = i2o_shost->scsi_host;
+
+ switch (i2o_dev->lct_data.class_id) {
+ case I2O_CLASS_RANDOM_BLOCK_STORAGE:
+ case I2O_CLASS_EXECUTIVE:
+#ifdef CONFIG_I2O_EXT_ADAPTEC
+ if (c->adaptec) {
+ u8 type;
+ struct i2o_device *d = i2o_shost->channel[0];
+
+ if (!i2o_parm_field_get(d, 0x0000, 0, &type, 1)
+ && (type == 0x01)) /* SCSI bus */
+ if (!i2o_parm_field_get(d, 0x0200, 4, &id, 4)) {
+ channel = 0;
+ if (i2o_dev->lct_data.class_id ==
+ I2O_CLASS_RANDOM_BLOCK_STORAGE)
+ lun =
+ cpu_to_le64(i2o_shost->
+ lun++);
+ else
+ lun = 0;
+ }
+ }
+#endif
+ break;
+
+ case I2O_CLASS_SCSI_PERIPHERAL:
+ if (i2o_parm_field_get(i2o_dev, 0x0000, 3, &id, 4))
+ return -EFAULT;
+
+ if (i2o_parm_field_get(i2o_dev, 0x0000, 4, &lun, 8))
+ return -EFAULT;
+
+ parent = i2o_iop_find_device(c, i2o_dev->lct_data.parent_tid);
+ if (!parent) {
+ osm_warn("can not find parent of device %03x\n",
+ i2o_dev->lct_data.tid);
+ return -EFAULT;
+ }
+
+ for (i = 0; i <= i2o_shost->scsi_host->max_channel; i++)
+ if (i2o_shost->channel[i] == parent)
+ channel = i;
+ break;
+
+ default:
+ return -EFAULT;
+ }
+
+ if (channel == -1) {
+ osm_warn("can not find channel of device %03x\n",
+ i2o_dev->lct_data.tid);
+ return -EFAULT;
+ }
+
+ if (le32_to_cpu(id) >= scsi_host->max_id) {
+ osm_warn("SCSI device id (%d) >= max_id of I2O host (%d)",
+ le32_to_cpu(id), scsi_host->max_id);
+ return -EFAULT;
+ }
+
+ if (le64_to_cpu(lun) >= scsi_host->max_lun) {
+ osm_warn("SCSI device lun (%llu) >= max_lun of I2O host (%llu)",
+ le64_to_cpu(lun), scsi_host->max_lun);
+ return -EFAULT;
+ }
+
+ scsi_dev =
+ __scsi_add_device(i2o_shost->scsi_host, channel, le32_to_cpu(id),
+ le64_to_cpu(lun), i2o_dev);
+
+ if (IS_ERR(scsi_dev)) {
+ osm_warn("can not add SCSI device %03x\n",
+ i2o_dev->lct_data.tid);
+ return PTR_ERR(scsi_dev);
+ }
+
+ rc = sysfs_create_link(&i2o_dev->device.kobj,
+ &scsi_dev->sdev_gendev.kobj, "scsi");
+ if (rc)
+ goto err;
+
+ osm_info("device added (TID: %03x) channel: %d, id: %d, lun: %llu\n",
+ i2o_dev->lct_data.tid, channel, le32_to_cpu(id),
+ le64_to_cpu(lun));
+
+ return 0;
+
+err:
+ scsi_remove_device(scsi_dev);
+ return rc;
+};
+
+static const char *i2o_scsi_info(struct Scsi_Host *SChost)
+{
+ struct i2o_scsi_host *hostdata;
+ hostdata = (struct i2o_scsi_host *)SChost->hostdata;
+ return hostdata->iop->name;
+}
+
+/**
+ * i2o_scsi_reply - SCSI OSM message reply handler
+ * @c: controller issuing the reply
+ * @m: message id for flushing
+ * @msg: the message from the controller
+ *
+ * Process reply messages (interrupts in normal scsi controller think).
+ * We can get a variety of messages to process. The normal path is
+ * scsi command completions. We must also deal with IOP failures,
+ * the reply to a bus reset and the reply to a LUN query.
+ *
+ * Returns 0 on success and if the reply should not be flushed or > 0
+ * on success and if the reply should be flushed. Returns negative error
+ * code on failure and if the reply should be flushed.
+ */
+static int i2o_scsi_reply(struct i2o_controller *c, u32 m,
+ struct i2o_message *msg)
+{
+ struct scsi_cmnd *cmd;
+ u32 error;
+ struct device *dev;
+
+ cmd = i2o_cntxt_list_get(c, le32_to_cpu(msg->u.s.tcntxt));
+ if (unlikely(!cmd)) {
+ osm_err("NULL reply received!\n");
+ return -1;
+ }
+
+ /*
+ * Low byte is device status, next is adapter status,
+ * (then one byte reserved), then request status.
+ */
+ error = le32_to_cpu(msg->body[0]);
+
+ osm_debug("Completed %0x%p\n", cmd);
+
+ cmd->result = error & 0xff;
+ /*
+ * if DeviceStatus is not SCSI_SUCCESS copy over the sense data and let
+ * the SCSI layer handle the error
+ */
+ if (cmd->result)
+ memcpy(cmd->sense_buffer, &msg->body[3],
+ min(SCSI_SENSE_BUFFERSIZE, 40));
+
+ /* only output error code if AdapterStatus is not HBA_SUCCESS */
+ if ((error >> 8) & 0xff)
+ osm_err("SCSI error %08x\n", error);
+
+ dev = &c->pdev->dev;
+
+ scsi_dma_unmap(cmd);
+
+ cmd->scsi_done(cmd);
+
+ return 1;
+};
+
+/**
+ * i2o_scsi_notify_device_add - Retrieve notifications of added devices
+ * @i2o_dev: the I2O device which was added
+ *
+ * If a I2O device is added we catch the notification, because I2O classes
+ * other than SCSI peripheral will not be received through
+ * i2o_scsi_probe().
+ */
+static void i2o_scsi_notify_device_add(struct i2o_device *i2o_dev)
+{
+ switch (i2o_dev->lct_data.class_id) {
+ case I2O_CLASS_EXECUTIVE:
+ case I2O_CLASS_RANDOM_BLOCK_STORAGE:
+ i2o_scsi_probe(&i2o_dev->device);
+ break;
+
+ default:
+ break;
+ }
+};
+
+/**
+ * i2o_scsi_notify_device_remove - Retrieve notifications of removed devices
+ * @i2o_dev: the I2O device which was removed
+ *
+ * If a I2O device is removed, we catch the notification to remove the
+ * corresponding SCSI device.
+ */
+static void i2o_scsi_notify_device_remove(struct i2o_device *i2o_dev)
+{
+ switch (i2o_dev->lct_data.class_id) {
+ case I2O_CLASS_EXECUTIVE:
+ case I2O_CLASS_RANDOM_BLOCK_STORAGE:
+ i2o_scsi_remove(&i2o_dev->device);
+ break;
+
+ default:
+ break;
+ }
+};
+
+/**
+ * i2o_scsi_notify_controller_add - Retrieve notifications of added controllers
+ * @c: the controller which was added
+ *
+ * If a I2O controller is added, we catch the notification to add a
+ * corresponding Scsi_Host.
+ */
+static void i2o_scsi_notify_controller_add(struct i2o_controller *c)
+{
+ struct i2o_scsi_host *i2o_shost;
+ int rc;
+
+ i2o_shost = i2o_scsi_host_alloc(c);
+ if (IS_ERR(i2o_shost)) {
+ osm_err("Could not initialize SCSI host\n");
+ return;
+ }
+
+ rc = scsi_add_host(i2o_shost->scsi_host, &c->device);
+ if (rc) {
+ osm_err("Could not add SCSI host\n");
+ scsi_host_put(i2o_shost->scsi_host);
+ return;
+ }
+
+ c->driver_data[i2o_scsi_driver.context] = i2o_shost;
+
+ osm_debug("new I2O SCSI host added\n");
+};
+
+/**
+ * i2o_scsi_notify_controller_remove - Retrieve notifications of removed controllers
+ * @c: the controller which was removed
+ *
+ * If a I2O controller is removed, we catch the notification to remove the
+ * corresponding Scsi_Host.
+ */
+static void i2o_scsi_notify_controller_remove(struct i2o_controller *c)
+{
+ struct i2o_scsi_host *i2o_shost;
+ i2o_shost = i2o_scsi_get_host(c);
+ if (!i2o_shost)
+ return;
+
+ c->driver_data[i2o_scsi_driver.context] = NULL;
+
+ scsi_remove_host(i2o_shost->scsi_host);
+ scsi_host_put(i2o_shost->scsi_host);
+ osm_debug("I2O SCSI host removed\n");
+};
+
+/* SCSI OSM driver struct */
+static struct i2o_driver i2o_scsi_driver = {
+ .name = OSM_NAME,
+ .reply = i2o_scsi_reply,
+ .classes = i2o_scsi_class_id,
+ .notify_device_add = i2o_scsi_notify_device_add,
+ .notify_device_remove = i2o_scsi_notify_device_remove,
+ .notify_controller_add = i2o_scsi_notify_controller_add,
+ .notify_controller_remove = i2o_scsi_notify_controller_remove,
+ .driver = {
+ .probe = i2o_scsi_probe,
+ .remove = i2o_scsi_remove,
+ },
+};
+
+/**
+ * i2o_scsi_queuecommand - queue a SCSI command
+ * @SCpnt: scsi command pointer
+ * @done: callback for completion
+ *
+ * Issue a scsi command asynchronously. Return 0 on success or 1 if
+ * we hit an error (normally message queue congestion). The only
+ * minor complication here is that I2O deals with the device addressing
+ * so we have to map the bus/dev/lun back to an I2O handle as well
+ * as faking absent devices ourself.
+ *
+ * Locks: takes the controller lock on error path only
+ */
+
+static int i2o_scsi_queuecommand_lck(struct scsi_cmnd *SCpnt,
+ void (*done) (struct scsi_cmnd *))
+{
+ struct i2o_controller *c;
+ struct i2o_device *i2o_dev;
+ int tid;
+ struct i2o_message *msg;
+ /*
+ * ENABLE_DISCONNECT
+ * SIMPLE_TAG
+ * RETURN_SENSE_DATA_IN_REPLY_MESSAGE_FRAME
+ */
+ u32 scsi_flags = 0x20a00000;
+ u32 sgl_offset;
+ u32 *mptr;
+ u32 cmd = I2O_CMD_SCSI_EXEC << 24;
+ int rc = 0;
+
+ /*
+ * Do the incoming paperwork
+ */
+ i2o_dev = SCpnt->device->hostdata;
+
+ SCpnt->scsi_done = done;
+
+ if (unlikely(!i2o_dev)) {
+ osm_warn("no I2O device in request\n");
+ SCpnt->result = DID_NO_CONNECT << 16;
+ done(SCpnt);
+ goto exit;
+ }
+ c = i2o_dev->iop;
+ tid = i2o_dev->lct_data.tid;
+
+ osm_debug("qcmd: Tid = %03x\n", tid);
+ osm_debug("Real scsi messages.\n");
+
+ /*
+ * Put together a scsi execscb message
+ */
+ switch (SCpnt->sc_data_direction) {
+ case PCI_DMA_NONE:
+ /* DATA NO XFER */
+ sgl_offset = SGL_OFFSET_0;
+ break;
+
+ case PCI_DMA_TODEVICE:
+ /* DATA OUT (iop-->dev) */
+ scsi_flags |= 0x80000000;
+ sgl_offset = SGL_OFFSET_10;
+ break;
+
+ case PCI_DMA_FROMDEVICE:
+ /* DATA IN (iop<--dev) */
+ scsi_flags |= 0x40000000;
+ sgl_offset = SGL_OFFSET_10;
+ break;
+
+ default:
+ /* Unknown - kill the command */
+ SCpnt->result = DID_NO_CONNECT << 16;
+ done(SCpnt);
+ goto exit;
+ }
+
+ /*
+ * Obtain an I2O message. If there are none free then
+ * throw it back to the scsi layer
+ */
+
+ msg = i2o_msg_get(c);
+ if (IS_ERR(msg)) {
+ rc = SCSI_MLQUEUE_HOST_BUSY;
+ goto exit;
+ }
+
+ mptr = &msg->body[0];
+
+#if 0 /* this code can't work */
+#ifdef CONFIG_I2O_EXT_ADAPTEC
+ if (c->adaptec) {
+ u32 adpt_flags = 0;
+
+ if (SCpnt->sc_request && SCpnt->sc_request->upper_private_data) {
+ i2o_sg_io_hdr_t __user *usr_ptr =
+ ((Sg_request *) (SCpnt->sc_request->
+ upper_private_data))->header.
+ usr_ptr;
+
+ if (usr_ptr)
+ get_user(adpt_flags, &usr_ptr->flags);
+ }
+
+ switch (i2o_dev->lct_data.class_id) {
+ case I2O_CLASS_EXECUTIVE:
+ case I2O_CLASS_RANDOM_BLOCK_STORAGE:
+ /* interpret flag has to be set for executive */
+ adpt_flags ^= I2O_DPT_SG_FLAG_INTERPRET;
+ break;
+
+ default:
+ break;
+ }
+
+ /*
+ * for Adaptec controllers we use the PRIVATE command, because
+ * the normal SCSI EXEC doesn't support all SCSI commands on
+ * all controllers (for example READ CAPACITY).
+ */
+ if (sgl_offset == SGL_OFFSET_10)
+ sgl_offset = SGL_OFFSET_12;
+ cmd = I2O_CMD_PRIVATE << 24;
+ *mptr++ = cpu_to_le32(I2O_VENDOR_DPT << 16 | I2O_CMD_SCSI_EXEC);
+ *mptr++ = cpu_to_le32(adpt_flags | tid);
+ }
+#endif
+#endif
+
+ msg->u.head[1] = cpu_to_le32(cmd | HOST_TID << 12 | tid);
+ msg->u.s.icntxt = cpu_to_le32(i2o_scsi_driver.context);
+
+ /* We want the SCSI control block back */
+ msg->u.s.tcntxt = cpu_to_le32(i2o_cntxt_list_add(c, SCpnt));
+
+ /* LSI_920_PCI_QUIRK
+ *
+ * Intermittant observations of msg frame word data corruption
+ * observed on msg[4] after:
+ * WRITE, READ-MODIFY-WRITE
+ * operations. 19990606 -sralston
+ *
+ * (Hence we build this word via tag. Its good practice anyway
+ * we don't want fetches over PCI needlessly)
+ */
+
+ /* Attach tags to the devices */
+ /* FIXME: implement
+ if(SCpnt->device->tagged_supported) {
+ if(SCpnt->tag == HEAD_OF_QUEUE_TAG)
+ scsi_flags |= 0x01000000;
+ else if(SCpnt->tag == ORDERED_QUEUE_TAG)
+ scsi_flags |= 0x01800000;
+ }
+ */
+
+ *mptr++ = cpu_to_le32(scsi_flags | SCpnt->cmd_len);
+
+ /* Write SCSI command into the message - always 16 byte block */
+ memcpy(mptr, SCpnt->cmnd, 16);
+ mptr += 4;
+
+ if (sgl_offset != SGL_OFFSET_0) {
+ /* write size of data addressed by SGL */
+ *mptr++ = cpu_to_le32(scsi_bufflen(SCpnt));
+
+ /* Now fill in the SGList and command */
+
+ if (scsi_sg_count(SCpnt)) {
+ if (!i2o_dma_map_sg(c, scsi_sglist(SCpnt),
+ scsi_sg_count(SCpnt),
+ SCpnt->sc_data_direction, &mptr))
+ goto nomem;
+ }
+ }
+
+ /* Stick the headers on */
+ msg->u.head[0] =
+ cpu_to_le32(I2O_MESSAGE_SIZE(mptr - &msg->u.head[0]) | sgl_offset);
+
+ /* Queue the message */
+ i2o_msg_post(c, msg);
+
+ osm_debug("Issued %0x%p\n", SCpnt);
+
+ return 0;
+
+ nomem:
+ rc = -ENOMEM;
+ i2o_msg_nop(c, msg);
+
+ exit:
+ return rc;
+}
+
+static DEF_SCSI_QCMD(i2o_scsi_queuecommand)
+
+/**
+ * i2o_scsi_abort - abort a running command
+ * @SCpnt: command to abort
+ *
+ * Ask the I2O controller to abort a command. This is an asynchrnous
+ * process and our callback handler will see the command complete with an
+ * aborted message if it succeeds.
+ *
+ * Returns 0 if the command is successfully aborted or negative error code
+ * on failure.
+ */
+static int i2o_scsi_abort(struct scsi_cmnd *SCpnt)
+{
+ struct i2o_device *i2o_dev;
+ struct i2o_controller *c;
+ struct i2o_message *msg;
+ int tid;
+ int status = FAILED;
+
+ osm_warn("Aborting command block.\n");
+
+ i2o_dev = SCpnt->device->hostdata;
+ c = i2o_dev->iop;
+ tid = i2o_dev->lct_data.tid;
+
+ msg = i2o_msg_get_wait(c, I2O_TIMEOUT_MESSAGE_GET);
+ if (IS_ERR(msg))
+ return SCSI_MLQUEUE_HOST_BUSY;
+
+ msg->u.head[0] = cpu_to_le32(FIVE_WORD_MSG_SIZE | SGL_OFFSET_0);
+ msg->u.head[1] =
+ cpu_to_le32(I2O_CMD_SCSI_ABORT << 24 | HOST_TID << 12 | tid);
+ msg->body[0] = cpu_to_le32(i2o_cntxt_list_get_ptr(c, SCpnt));
+
+ if (!i2o_msg_post_wait(c, msg, I2O_TIMEOUT_SCSI_SCB_ABORT))
+ status = SUCCESS;
+
+ return status;
+}
+
+/**
+ * i2o_scsi_bios_param - Invent disk geometry
+ * @sdev: scsi device
+ * @dev: block layer device
+ * @capacity: size in sectors
+ * @ip: geometry array
+ *
+ * This is anyone's guess quite frankly. We use the same rules everyone
+ * else appears to and hope. It seems to work.
+ */
+
+static int i2o_scsi_bios_param(struct scsi_device *sdev,
+ struct block_device *dev, sector_t capacity,
+ int *ip)
+{
+ int size;
+
+ size = capacity;
+ ip[0] = 64; /* heads */
+ ip[1] = 32; /* sectors */
+ if ((ip[2] = size >> 11) > 1024) { /* cylinders, test for big disk */
+ ip[0] = 255; /* heads */
+ ip[1] = 63; /* sectors */
+ ip[2] = size / (255 * 63); /* cylinders */
+ }
+ return 0;
+}
+
+static struct scsi_host_template i2o_scsi_host_template = {
+ .proc_name = OSM_NAME,
+ .name = OSM_DESCRIPTION,
+ .info = i2o_scsi_info,
+ .queuecommand = i2o_scsi_queuecommand,
+ .eh_abort_handler = i2o_scsi_abort,
+ .bios_param = i2o_scsi_bios_param,
+ .can_queue = I2O_SCSI_CAN_QUEUE,
+ .sg_tablesize = 8,
+ .cmd_per_lun = 6,
+ .use_clustering = ENABLE_CLUSTERING,
+};
+
+/**
+ * i2o_scsi_init - SCSI OSM initialization function
+ *
+ * Register SCSI OSM into I2O core.
+ *
+ * Returns 0 on success or negative error code on failure.
+ */
+static int __init i2o_scsi_init(void)
+{
+ int rc;
+
+ printk(KERN_INFO OSM_DESCRIPTION " v" OSM_VERSION "\n");
+
+ /* Register SCSI OSM into I2O core */
+ rc = i2o_driver_register(&i2o_scsi_driver);
+ if (rc) {
+ osm_err("Could not register SCSI driver\n");
+ return rc;
+ }
+
+ return 0;
+};
+
+/**
+ * i2o_scsi_exit - SCSI OSM exit function
+ *
+ * Unregisters SCSI OSM from I2O core.
+ */
+static void __exit i2o_scsi_exit(void)
+{
+ /* Unregister I2O SCSI OSM from I2O core */
+ i2o_driver_unregister(&i2o_scsi_driver);
+};
+
+MODULE_AUTHOR("Red Hat Software");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION(OSM_DESCRIPTION);
+MODULE_VERSION(OSM_VERSION);
+
+module_init(i2o_scsi_init);
+module_exit(i2o_scsi_exit);
diff --git a/drivers/staging/i2o/iop.c b/drivers/staging/i2o/iop.c
new file mode 100644
index 000000000..23bdbe4aa
--- /dev/null
+++ b/drivers/staging/i2o/iop.c
@@ -0,0 +1,1255 @@
+/*
+ * Functions to handle I2O controllers and I2O message handling
+ *
+ * Copyright (C) 1999-2002 Red Hat Software
+ *
+ * Written by Alan Cox, Building Number Three Ltd
+ *
+ * This program is free software; you can redistribute it and/or 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.
+ *
+ * A lot of the I2O message side code from this is taken from the
+ * Red Creek RCPCI45 adapter driver by Red Creek Communications
+ *
+ * Fixes/additions:
+ * Philipp Rumpf
+ * Juha Sievänen <Juha.Sievanen@cs.Helsinki.FI>
+ * Auvo Häkkinen <Auvo.Hakkinen@cs.Helsinki.FI>
+ * Deepak Saxena <deepak@plexity.net>
+ * Boji T Kannanthanam <boji.t.kannanthanam@intel.com>
+ * Alan Cox <alan@lxorguk.ukuu.org.uk>:
+ * Ported to Linux 2.5.
+ * Markus Lidel <Markus.Lidel@shadowconnect.com>:
+ * Minor fixes for 2.6.
+ */
+
+#include <linux/module.h>
+#include "i2o.h"
+#include <linux/delay.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include "core.h"
+
+#define OSM_NAME "i2o"
+#define OSM_VERSION "1.325"
+#define OSM_DESCRIPTION "I2O subsystem"
+
+/* global I2O controller list */
+LIST_HEAD(i2o_controllers);
+
+/*
+ * global I2O System Table. Contains information about all the IOPs in the
+ * system. Used to inform IOPs about each others existence.
+ */
+static struct i2o_dma i2o_systab;
+
+static int i2o_hrt_get(struct i2o_controller *c);
+
+/**
+ * i2o_msg_get_wait - obtain an I2O message from the IOP
+ * @c: I2O controller
+ * @wait: how long to wait until timeout
+ *
+ * This function waits up to wait seconds for a message slot to be
+ * available.
+ *
+ * On a success the message is returned and the pointer to the message is
+ * set in msg. The returned message is the physical page frame offset
+ * address from the read port (see the i2o spec). If no message is
+ * available returns I2O_QUEUE_EMPTY and msg is leaved untouched.
+ */
+struct i2o_message *i2o_msg_get_wait(struct i2o_controller *c, int wait)
+{
+ unsigned long timeout = jiffies + wait * HZ;
+ struct i2o_message *msg;
+
+ while (IS_ERR(msg = i2o_msg_get(c))) {
+ if (time_after(jiffies, timeout)) {
+ osm_debug("%s: Timeout waiting for message frame.\n",
+ c->name);
+ return ERR_PTR(-ETIMEDOUT);
+ }
+ schedule_timeout_uninterruptible(1);
+ }
+
+ return msg;
+};
+
+#if BITS_PER_LONG == 64
+/**
+ * i2o_cntxt_list_add - Append a pointer to context list and return a id
+ * @c: controller to which the context list belong
+ * @ptr: pointer to add to the context list
+ *
+ * Because the context field in I2O is only 32-bit large, on 64-bit the
+ * pointer is to large to fit in the context field. The i2o_cntxt_list
+ * functions therefore map pointers to context fields.
+ *
+ * Returns context id > 0 on success or 0 on failure.
+ */
+u32 i2o_cntxt_list_add(struct i2o_controller * c, void *ptr)
+{
+ struct i2o_context_list_element *entry;
+ unsigned long flags;
+
+ if (!ptr)
+ osm_err("%s: couldn't add NULL pointer to context list!\n",
+ c->name);
+
+ entry = kmalloc(sizeof(*entry), GFP_ATOMIC);
+ if (!entry) {
+ osm_err("%s: Could not allocate memory for context list element"
+ "\n", c->name);
+ return 0;
+ }
+
+ entry->ptr = ptr;
+ entry->timestamp = jiffies;
+ INIT_LIST_HEAD(&entry->list);
+
+ spin_lock_irqsave(&c->context_list_lock, flags);
+
+ if (unlikely(atomic_inc_and_test(&c->context_list_counter)))
+ atomic_inc(&c->context_list_counter);
+
+ entry->context = atomic_read(&c->context_list_counter);
+
+ list_add(&entry->list, &c->context_list);
+
+ spin_unlock_irqrestore(&c->context_list_lock, flags);
+
+ osm_debug("%s: Add context to list %p -> %d\n", c->name, ptr, context);
+
+ return entry->context;
+};
+
+/**
+ * i2o_cntxt_list_remove - Remove a pointer from the context list
+ * @c: controller to which the context list belong
+ * @ptr: pointer which should be removed from the context list
+ *
+ * Removes a previously added pointer from the context list and returns
+ * the matching context id.
+ *
+ * Returns context id on success or 0 on failure.
+ */
+u32 i2o_cntxt_list_remove(struct i2o_controller * c, void *ptr)
+{
+ struct i2o_context_list_element *entry;
+ u32 context = 0;
+ unsigned long flags;
+
+ spin_lock_irqsave(&c->context_list_lock, flags);
+ list_for_each_entry(entry, &c->context_list, list)
+ if (entry->ptr == ptr) {
+ list_del(&entry->list);
+ context = entry->context;
+ kfree(entry);
+ break;
+ }
+ spin_unlock_irqrestore(&c->context_list_lock, flags);
+
+ if (!context)
+ osm_warn("%s: Could not remove nonexistent ptr %p\n", c->name,
+ ptr);
+
+ osm_debug("%s: remove ptr from context list %d -> %p\n", c->name,
+ context, ptr);
+
+ return context;
+};
+
+/**
+ * i2o_cntxt_list_get - Get a pointer from the context list and remove it
+ * @c: controller to which the context list belong
+ * @context: context id to which the pointer belong
+ *
+ * Returns pointer to the matching context id on success or NULL on
+ * failure.
+ */
+void *i2o_cntxt_list_get(struct i2o_controller *c, u32 context)
+{
+ struct i2o_context_list_element *entry;
+ unsigned long flags;
+ void *ptr = NULL;
+
+ spin_lock_irqsave(&c->context_list_lock, flags);
+ list_for_each_entry(entry, &c->context_list, list)
+ if (entry->context == context) {
+ list_del(&entry->list);
+ ptr = entry->ptr;
+ kfree(entry);
+ break;
+ }
+ spin_unlock_irqrestore(&c->context_list_lock, flags);
+
+ if (!ptr)
+ osm_warn("%s: context id %d not found\n", c->name, context);
+
+ osm_debug("%s: get ptr from context list %d -> %p\n", c->name, context,
+ ptr);
+
+ return ptr;
+};
+
+/**
+ * i2o_cntxt_list_get_ptr - Get a context id from the context list
+ * @c: controller to which the context list belong
+ * @ptr: pointer to which the context id should be fetched
+ *
+ * Returns context id which matches to the pointer on success or 0 on
+ * failure.
+ */
+u32 i2o_cntxt_list_get_ptr(struct i2o_controller * c, void *ptr)
+{
+ struct i2o_context_list_element *entry;
+ u32 context = 0;
+ unsigned long flags;
+
+ spin_lock_irqsave(&c->context_list_lock, flags);
+ list_for_each_entry(entry, &c->context_list, list)
+ if (entry->ptr == ptr) {
+ context = entry->context;
+ break;
+ }
+ spin_unlock_irqrestore(&c->context_list_lock, flags);
+
+ if (!context)
+ osm_warn("%s: Could not find nonexistent ptr %p\n", c->name,
+ ptr);
+
+ osm_debug("%s: get context id from context list %p -> %d\n", c->name,
+ ptr, context);
+
+ return context;
+};
+#endif
+
+/**
+ * i2o_iop_find - Find an I2O controller by id
+ * @unit: unit number of the I2O controller to search for
+ *
+ * Lookup the I2O controller on the controller list.
+ *
+ * Returns pointer to the I2O controller on success or NULL if not found.
+ */
+struct i2o_controller *i2o_find_iop(int unit)
+{
+ struct i2o_controller *c;
+
+ list_for_each_entry(c, &i2o_controllers, list) {
+ if (c->unit == unit)
+ return c;
+ }
+
+ return NULL;
+};
+
+/**
+ * i2o_iop_find_device - Find a I2O device on an I2O controller
+ * @c: I2O controller where the I2O device hangs on
+ * @tid: TID of the I2O device to search for
+ *
+ * Searches the devices of the I2O controller for a device with TID tid and
+ * returns it.
+ *
+ * Returns a pointer to the I2O device if found, otherwise NULL.
+ */
+struct i2o_device *i2o_iop_find_device(struct i2o_controller *c, u16 tid)
+{
+ struct i2o_device *dev;
+
+ list_for_each_entry(dev, &c->devices, list)
+ if (dev->lct_data.tid == tid)
+ return dev;
+
+ return NULL;
+};
+
+/**
+ * i2o_quiesce_controller - quiesce controller
+ * @c: controller
+ *
+ * Quiesce an IOP. Causes IOP to make external operation quiescent
+ * (i2o 'READY' state). Internal operation of the IOP continues normally.
+ *
+ * Returns 0 on success or negative error code on failure.
+ */
+static int i2o_iop_quiesce(struct i2o_controller *c)
+{
+ struct i2o_message *msg;
+ i2o_status_block *sb = c->status_block.virt;
+ int rc;
+
+ i2o_status_get(c);
+
+ /* SysQuiesce discarded if IOP not in READY or OPERATIONAL state */
+ if ((sb->iop_state != ADAPTER_STATE_READY) &&
+ (sb->iop_state != ADAPTER_STATE_OPERATIONAL))
+ return 0;
+
+ msg = i2o_msg_get_wait(c, I2O_TIMEOUT_MESSAGE_GET);
+ if (IS_ERR(msg))
+ return PTR_ERR(msg);
+
+ msg->u.head[0] = cpu_to_le32(FOUR_WORD_MSG_SIZE | SGL_OFFSET_0);
+ msg->u.head[1] =
+ cpu_to_le32(I2O_CMD_SYS_QUIESCE << 24 | HOST_TID << 12 |
+ ADAPTER_TID);
+
+ /* Long timeout needed for quiesce if lots of devices */
+ if ((rc = i2o_msg_post_wait(c, msg, 240)))
+ osm_info("%s: Unable to quiesce (status=%#x).\n", c->name, -rc);
+ else
+ osm_debug("%s: Quiesced.\n", c->name);
+
+ i2o_status_get(c); // Entered READY state
+
+ return rc;
+};
+
+/**
+ * i2o_iop_enable - move controller from ready to OPERATIONAL
+ * @c: I2O controller
+ *
+ * Enable IOP. This allows the IOP to resume external operations and
+ * reverses the effect of a quiesce. Returns zero or an error code if
+ * an error occurs.
+ */
+static int i2o_iop_enable(struct i2o_controller *c)
+{
+ struct i2o_message *msg;
+ i2o_status_block *sb = c->status_block.virt;
+ int rc;
+
+ i2o_status_get(c);
+
+ /* Enable only allowed on READY state */
+ if (sb->iop_state != ADAPTER_STATE_READY)
+ return -EINVAL;
+
+ msg = i2o_msg_get_wait(c, I2O_TIMEOUT_MESSAGE_GET);
+ if (IS_ERR(msg))
+ return PTR_ERR(msg);
+
+ msg->u.head[0] = cpu_to_le32(FOUR_WORD_MSG_SIZE | SGL_OFFSET_0);
+ msg->u.head[1] =
+ cpu_to_le32(I2O_CMD_SYS_ENABLE << 24 | HOST_TID << 12 |
+ ADAPTER_TID);
+
+ /* How long of a timeout do we need? */
+ if ((rc = i2o_msg_post_wait(c, msg, 240)))
+ osm_err("%s: Could not enable (status=%#x).\n", c->name, -rc);
+ else
+ osm_debug("%s: Enabled.\n", c->name);
+
+ i2o_status_get(c); // entered OPERATIONAL state
+
+ return rc;
+};
+
+/**
+ * i2o_iop_quiesce_all - Quiesce all I2O controllers on the system
+ *
+ * Quiesce all I2O controllers which are connected to the system.
+ */
+static inline void i2o_iop_quiesce_all(void)
+{
+ struct i2o_controller *c, *tmp;
+
+ list_for_each_entry_safe(c, tmp, &i2o_controllers, list) {
+ if (!c->no_quiesce)
+ i2o_iop_quiesce(c);
+ }
+};
+
+/**
+ * i2o_iop_enable_all - Enables all controllers on the system
+ *
+ * Enables all I2O controllers which are connected to the system.
+ */
+static inline void i2o_iop_enable_all(void)
+{
+ struct i2o_controller *c, *tmp;
+
+ list_for_each_entry_safe(c, tmp, &i2o_controllers, list)
+ i2o_iop_enable(c);
+};
+
+/**
+ * i2o_clear_controller - Bring I2O controller into HOLD state
+ * @c: controller
+ *
+ * Clear an IOP to HOLD state, ie. terminate external operations, clear all
+ * input queues and prepare for a system restart. IOP's internal operation
+ * continues normally and the outbound queue is alive. The IOP is not
+ * expected to rebuild its LCT.
+ *
+ * Returns 0 on success or negative error code on failure.
+ */
+static int i2o_iop_clear(struct i2o_controller *c)
+{
+ struct i2o_message *msg;
+ int rc;
+
+ msg = i2o_msg_get_wait(c, I2O_TIMEOUT_MESSAGE_GET);
+ if (IS_ERR(msg))
+ return PTR_ERR(msg);
+
+ /* Quiesce all IOPs first */
+ i2o_iop_quiesce_all();
+
+ msg->u.head[0] = cpu_to_le32(FOUR_WORD_MSG_SIZE | SGL_OFFSET_0);
+ msg->u.head[1] =
+ cpu_to_le32(I2O_CMD_ADAPTER_CLEAR << 24 | HOST_TID << 12 |
+ ADAPTER_TID);
+
+ if ((rc = i2o_msg_post_wait(c, msg, 30)))
+ osm_info("%s: Unable to clear (status=%#x).\n", c->name, -rc);
+ else
+ osm_debug("%s: Cleared.\n", c->name);
+
+ /* Enable all IOPs */
+ i2o_iop_enable_all();
+
+ return rc;
+}
+
+/**
+ * i2o_iop_init_outbound_queue - setup the outbound message queue
+ * @c: I2O controller
+ *
+ * Clear and (re)initialize IOP's outbound queue and post the message
+ * frames to the IOP.
+ *
+ * Returns 0 on success or negative error code on failure.
+ */
+static int i2o_iop_init_outbound_queue(struct i2o_controller *c)
+{
+ u32 m;
+ volatile u8 *status = c->status.virt;
+ struct i2o_message *msg;
+ ulong timeout;
+ int i;
+
+ osm_debug("%s: Initializing Outbound Queue...\n", c->name);
+
+ memset(c->status.virt, 0, 4);
+
+ msg = i2o_msg_get_wait(c, I2O_TIMEOUT_MESSAGE_GET);
+ if (IS_ERR(msg))
+ return PTR_ERR(msg);
+
+ msg->u.head[0] = cpu_to_le32(EIGHT_WORD_MSG_SIZE | SGL_OFFSET_6);
+ msg->u.head[1] =
+ cpu_to_le32(I2O_CMD_OUTBOUND_INIT << 24 | HOST_TID << 12 |
+ ADAPTER_TID);
+ msg->u.s.icntxt = cpu_to_le32(i2o_exec_driver.context);
+ msg->u.s.tcntxt = cpu_to_le32(0x00000000);
+ msg->body[0] = cpu_to_le32(PAGE_SIZE);
+ /* Outbound msg frame size in words and Initcode */
+ msg->body[1] = cpu_to_le32(I2O_OUTBOUND_MSG_FRAME_SIZE << 16 | 0x80);
+ msg->body[2] = cpu_to_le32(0xd0000004);
+ msg->body[3] = cpu_to_le32(i2o_dma_low(c->status.phys));
+ msg->body[4] = cpu_to_le32(i2o_dma_high(c->status.phys));
+
+ i2o_msg_post(c, msg);
+
+ timeout = jiffies + I2O_TIMEOUT_INIT_OUTBOUND_QUEUE * HZ;
+ while (*status <= I2O_CMD_IN_PROGRESS) {
+ if (time_after(jiffies, timeout)) {
+ osm_warn("%s: Timeout Initializing\n", c->name);
+ return -ETIMEDOUT;
+ }
+ schedule_timeout_uninterruptible(1);
+ }
+
+ m = c->out_queue.phys;
+
+ /* Post frames */
+ for (i = 0; i < I2O_MAX_OUTBOUND_MSG_FRAMES; i++) {
+ i2o_flush_reply(c, m);
+ udelay(1); /* Promise */
+ m += I2O_OUTBOUND_MSG_FRAME_SIZE * sizeof(u32);
+ }
+
+ return 0;
+}
+
+/**
+ * i2o_iop_reset - reset an I2O controller
+ * @c: controller to reset
+ *
+ * Reset the IOP into INIT state and wait until IOP gets into RESET state.
+ * Terminate all external operations, clear IOP's inbound and outbound
+ * queues, terminate all DDMs, and reload the IOP's operating environment
+ * and all local DDMs. The IOP rebuilds its LCT.
+ */
+static int i2o_iop_reset(struct i2o_controller *c)
+{
+ volatile u8 *status = c->status.virt;
+ struct i2o_message *msg;
+ unsigned long timeout;
+ i2o_status_block *sb = c->status_block.virt;
+ int rc = 0;
+
+ osm_debug("%s: Resetting controller\n", c->name);
+
+ msg = i2o_msg_get_wait(c, I2O_TIMEOUT_MESSAGE_GET);
+ if (IS_ERR(msg))
+ return PTR_ERR(msg);
+
+ memset(c->status_block.virt, 0, 8);
+
+ /* Quiesce all IOPs first */
+ i2o_iop_quiesce_all();
+
+ msg->u.head[0] = cpu_to_le32(EIGHT_WORD_MSG_SIZE | SGL_OFFSET_0);
+ msg->u.head[1] =
+ cpu_to_le32(I2O_CMD_ADAPTER_RESET << 24 | HOST_TID << 12 |
+ ADAPTER_TID);
+ msg->u.s.icntxt = cpu_to_le32(i2o_exec_driver.context);
+ msg->u.s.tcntxt = cpu_to_le32(0x00000000);
+ msg->body[0] = cpu_to_le32(0x00000000);
+ msg->body[1] = cpu_to_le32(0x00000000);
+ msg->body[2] = cpu_to_le32(i2o_dma_low(c->status.phys));
+ msg->body[3] = cpu_to_le32(i2o_dma_high(c->status.phys));
+
+ i2o_msg_post(c, msg);
+
+ /* Wait for a reply */
+ timeout = jiffies + I2O_TIMEOUT_RESET * HZ;
+ while (!*status) {
+ if (time_after(jiffies, timeout))
+ break;
+
+ schedule_timeout_uninterruptible(1);
+ }
+
+ switch (*status) {
+ case I2O_CMD_REJECTED:
+ osm_warn("%s: IOP reset rejected\n", c->name);
+ rc = -EPERM;
+ break;
+
+ case I2O_CMD_IN_PROGRESS:
+ /*
+ * Once the reset is sent, the IOP goes into the INIT state
+ * which is indeterminate. We need to wait until the IOP has
+ * rebooted before we can let the system talk to it. We read
+ * the inbound Free_List until a message is available. If we
+ * can't read one in the given amount of time, we assume the
+ * IOP could not reboot properly.
+ */
+ osm_debug("%s: Reset in progress, waiting for reboot...\n",
+ c->name);
+
+ while (IS_ERR(msg = i2o_msg_get_wait(c, I2O_TIMEOUT_RESET))) {
+ if (time_after(jiffies, timeout)) {
+ osm_err("%s: IOP reset timeout.\n", c->name);
+ rc = PTR_ERR(msg);
+ goto exit;
+ }
+ schedule_timeout_uninterruptible(1);
+ }
+ i2o_msg_nop(c, msg);
+
+ /* from here all quiesce commands are safe */
+ c->no_quiesce = 0;
+
+ /* verify if controller is in state RESET */
+ i2o_status_get(c);
+
+ if (!c->promise && (sb->iop_state != ADAPTER_STATE_RESET))
+ osm_warn("%s: reset completed, but adapter not in RESET"
+ " state.\n", c->name);
+ else
+ osm_debug("%s: reset completed.\n", c->name);
+
+ break;
+
+ default:
+ osm_err("%s: IOP reset timeout.\n", c->name);
+ rc = -ETIMEDOUT;
+ break;
+ }
+
+ exit:
+ /* Enable all IOPs */
+ i2o_iop_enable_all();
+
+ return rc;
+};
+
+/**
+ * i2o_iop_activate - Bring controller up to HOLD
+ * @c: controller
+ *
+ * This function brings an I2O controller into HOLD state. The adapter
+ * is reset if necessary and then the queues and resource table are read.
+ *
+ * Returns 0 on success or negative error code on failure.
+ */
+static int i2o_iop_activate(struct i2o_controller *c)
+{
+ i2o_status_block *sb = c->status_block.virt;
+ int rc;
+ int state;
+
+ /* In INIT state, Wait Inbound Q to initialize (in i2o_status_get) */
+ /* In READY state, Get status */
+
+ rc = i2o_status_get(c);
+ if (rc) {
+ osm_info("%s: Unable to obtain status, attempting a reset.\n",
+ c->name);
+ rc = i2o_iop_reset(c);
+ if (rc)
+ return rc;
+ }
+
+ if (sb->i2o_version > I2OVER15) {
+ osm_err("%s: Not running version 1.5 of the I2O Specification."
+ "\n", c->name);
+ return -ENODEV;
+ }
+
+ switch (sb->iop_state) {
+ case ADAPTER_STATE_FAULTED:
+ osm_err("%s: hardware fault\n", c->name);
+ return -EFAULT;
+
+ case ADAPTER_STATE_READY:
+ case ADAPTER_STATE_OPERATIONAL:
+ case ADAPTER_STATE_HOLD:
+ case ADAPTER_STATE_FAILED:
+ osm_debug("%s: already running, trying to reset...\n", c->name);
+ rc = i2o_iop_reset(c);
+ if (rc)
+ return rc;
+ }
+
+ /* preserve state */
+ state = sb->iop_state;
+
+ rc = i2o_iop_init_outbound_queue(c);
+ if (rc)
+ return rc;
+
+ /* if adapter was not in RESET state clear now */
+ if (state != ADAPTER_STATE_RESET)
+ i2o_iop_clear(c);
+
+ i2o_status_get(c);
+
+ if (sb->iop_state != ADAPTER_STATE_HOLD) {
+ osm_err("%s: failed to bring IOP into HOLD state\n", c->name);
+ return -EIO;
+ }
+
+ return i2o_hrt_get(c);
+};
+
+static void i2o_res_alloc(struct i2o_controller *c, unsigned long flags)
+{
+ i2o_status_block *sb = c->status_block.virt;
+ struct resource *res = &c->mem_resource;
+ resource_size_t size, align;
+ int err;
+
+ res->name = c->pdev->bus->name;
+ res->flags = flags;
+ res->start = 0;
+ res->end = 0;
+ osm_info("%s: requires private memory resources.\n", c->name);
+
+ if (flags & IORESOURCE_MEM) {
+ size = sb->desired_mem_size;
+ align = 1 << 20; /* unspecified, use 1Mb and play safe */
+ } else {
+ size = sb->desired_io_size;
+ align = 1 << 12; /* unspecified, use 4Kb and play safe */
+ }
+
+ err = pci_bus_alloc_resource(c->pdev->bus, res, size, align, 0, 0,
+ NULL, NULL);
+ if (err < 0)
+ return;
+
+ if (flags & IORESOURCE_MEM) {
+ c->mem_alloc = 1;
+ sb->current_mem_size = resource_size(res);
+ sb->current_mem_base = res->start;
+ } else if (flags & IORESOURCE_IO) {
+ c->io_alloc = 1;
+ sb->current_io_size = resource_size(res);
+ sb->current_io_base = res->start;
+ }
+ osm_info("%s: allocated PCI space %pR\n", c->name, res);
+}
+
+/**
+ * i2o_iop_systab_set - Set the I2O System Table of the specified IOP
+ * @c: I2O controller to which the system table should be send
+ *
+ * Before the systab could be set i2o_systab_build() must be called.
+ *
+ * Returns 0 on success or negative error code on failure.
+ */
+static int i2o_iop_systab_set(struct i2o_controller *c)
+{
+ struct i2o_message *msg;
+ i2o_status_block *sb = c->status_block.virt;
+ struct device *dev = &c->pdev->dev;
+ int rc;
+
+ if (sb->current_mem_size < sb->desired_mem_size)
+ i2o_res_alloc(c, IORESOURCE_MEM);
+
+ if (sb->current_io_size < sb->desired_io_size)
+ i2o_res_alloc(c, IORESOURCE_IO);
+
+ msg = i2o_msg_get_wait(c, I2O_TIMEOUT_MESSAGE_GET);
+ if (IS_ERR(msg))
+ return PTR_ERR(msg);
+
+ i2o_systab.phys = dma_map_single(dev, i2o_systab.virt, i2o_systab.len,
+ PCI_DMA_TODEVICE);
+ if (!i2o_systab.phys) {
+ i2o_msg_nop(c, msg);
+ return -ENOMEM;
+ }
+
+ msg->u.head[0] = cpu_to_le32(I2O_MESSAGE_SIZE(12) | SGL_OFFSET_6);
+ msg->u.head[1] =
+ cpu_to_le32(I2O_CMD_SYS_TAB_SET << 24 | HOST_TID << 12 |
+ ADAPTER_TID);
+
+ /*
+ * Provide three SGL-elements:
+ * System table (SysTab), Private memory space declaration and
+ * Private i/o space declaration
+ */
+
+ msg->body[0] = cpu_to_le32(c->unit + 2);
+ msg->body[1] = cpu_to_le32(0x00000000);
+ msg->body[2] = cpu_to_le32(0x54000000 | i2o_systab.len);
+ msg->body[3] = cpu_to_le32(i2o_systab.phys);
+ msg->body[4] = cpu_to_le32(0x54000000 | sb->current_mem_size);
+ msg->body[5] = cpu_to_le32(sb->current_mem_base);
+ msg->body[6] = cpu_to_le32(0xd4000000 | sb->current_io_size);
+ msg->body[6] = cpu_to_le32(sb->current_io_base);
+
+ rc = i2o_msg_post_wait(c, msg, 120);
+
+ dma_unmap_single(dev, i2o_systab.phys, i2o_systab.len,
+ PCI_DMA_TODEVICE);
+
+ if (rc < 0)
+ osm_err("%s: Unable to set SysTab (status=%#x).\n", c->name,
+ -rc);
+ else
+ osm_debug("%s: SysTab set.\n", c->name);
+
+ return rc;
+}
+
+/**
+ * i2o_iop_online - Bring a controller online into OPERATIONAL state.
+ * @c: I2O controller
+ *
+ * Send the system table and enable the I2O controller.
+ *
+ * Returns 0 on success or negative error code on failure.
+ */
+static int i2o_iop_online(struct i2o_controller *c)
+{
+ int rc;
+
+ rc = i2o_iop_systab_set(c);
+ if (rc)
+ return rc;
+
+ /* In READY state */
+ osm_debug("%s: Attempting to enable...\n", c->name);
+ rc = i2o_iop_enable(c);
+ if (rc)
+ return rc;
+
+ return 0;
+};
+
+/**
+ * i2o_iop_remove - Remove the I2O controller from the I2O core
+ * @c: I2O controller
+ *
+ * Remove the I2O controller from the I2O core. If devices are attached to
+ * the controller remove these also and finally reset the controller.
+ */
+void i2o_iop_remove(struct i2o_controller *c)
+{
+ struct i2o_device *dev, *tmp;
+
+ osm_debug("%s: deleting controller\n", c->name);
+
+ i2o_driver_notify_controller_remove_all(c);
+
+ list_del(&c->list);
+
+ list_for_each_entry_safe(dev, tmp, &c->devices, list)
+ i2o_device_remove(dev);
+
+ device_del(&c->device);
+
+ /* Ask the IOP to switch to RESET state */
+ i2o_iop_reset(c);
+}
+
+/**
+ * i2o_systab_build - Build system table
+ *
+ * The system table contains information about all the IOPs in the system
+ * (duh) and is used by the Executives on the IOPs to establish peer2peer
+ * connections. We're not supporting peer2peer at the moment, but this
+ * will be needed down the road for things like lan2lan forwarding.
+ *
+ * Returns 0 on success or negative error code on failure.
+ */
+static int i2o_systab_build(void)
+{
+ struct i2o_controller *c, *tmp;
+ int num_controllers = 0;
+ u32 change_ind = 0;
+ int count = 0;
+ struct i2o_sys_tbl *systab = i2o_systab.virt;
+
+ list_for_each_entry_safe(c, tmp, &i2o_controllers, list)
+ num_controllers++;
+
+ if (systab) {
+ change_ind = systab->change_ind;
+ kfree(i2o_systab.virt);
+ }
+
+ /* Header + IOPs */
+ i2o_systab.len = sizeof(struct i2o_sys_tbl) + num_controllers *
+ sizeof(struct i2o_sys_tbl_entry);
+
+ systab = i2o_systab.virt = kzalloc(i2o_systab.len, GFP_KERNEL);
+ if (!systab) {
+ osm_err("unable to allocate memory for System Table\n");
+ return -ENOMEM;
+ }
+
+ systab->version = I2OVERSION;
+ systab->change_ind = change_ind + 1;
+
+ list_for_each_entry_safe(c, tmp, &i2o_controllers, list) {
+ i2o_status_block *sb;
+
+ if (count >= num_controllers) {
+ osm_err("controller added while building system table"
+ "\n");
+ break;
+ }
+
+ sb = c->status_block.virt;
+
+ /*
+ * Get updated IOP state so we have the latest information
+ *
+ * We should delete the controller at this point if it
+ * doesn't respond since if it's not on the system table
+ * it is techninically not part of the I2O subsystem...
+ */
+ if (unlikely(i2o_status_get(c))) {
+ osm_err("%s: Deleting b/c could not get status while "
+ "attempting to build system table\n", c->name);
+ i2o_iop_remove(c);
+ continue; // try the next one
+ }
+
+ systab->iops[count].org_id = sb->org_id;
+ systab->iops[count].iop_id = c->unit + 2;
+ systab->iops[count].seg_num = 0;
+ systab->iops[count].i2o_version = sb->i2o_version;
+ systab->iops[count].iop_state = sb->iop_state;
+ systab->iops[count].msg_type = sb->msg_type;
+ systab->iops[count].frame_size = sb->inbound_frame_size;
+ systab->iops[count].last_changed = change_ind;
+ systab->iops[count].iop_capabilities = sb->iop_capabilities;
+ systab->iops[count].inbound_low =
+ i2o_dma_low(c->base.phys + I2O_IN_PORT);
+ systab->iops[count].inbound_high =
+ i2o_dma_high(c->base.phys + I2O_IN_PORT);
+
+ count++;
+ }
+
+ systab->num_entries = count;
+
+ return 0;
+};
+
+/**
+ * i2o_parse_hrt - Parse the hardware resource table.
+ * @c: I2O controller
+ *
+ * We don't do anything with it except dumping it (in debug mode).
+ *
+ * Returns 0.
+ */
+static int i2o_parse_hrt(struct i2o_controller *c)
+{
+ i2o_dump_hrt(c);
+ return 0;
+};
+
+/**
+ * i2o_status_get - Get the status block from the I2O controller
+ * @c: I2O controller
+ *
+ * Issue a status query on the controller. This updates the attached
+ * status block. The status block could then be accessed through
+ * c->status_block.
+ *
+ * Returns 0 on success or negative error code on failure.
+ */
+int i2o_status_get(struct i2o_controller *c)
+{
+ struct i2o_message *msg;
+ volatile u8 *status_block;
+ unsigned long timeout;
+
+ status_block = (u8 *) c->status_block.virt;
+ memset(c->status_block.virt, 0, sizeof(i2o_status_block));
+
+ msg = i2o_msg_get_wait(c, I2O_TIMEOUT_MESSAGE_GET);
+ if (IS_ERR(msg))
+ return PTR_ERR(msg);
+
+ msg->u.head[0] = cpu_to_le32(NINE_WORD_MSG_SIZE | SGL_OFFSET_0);
+ msg->u.head[1] =
+ cpu_to_le32(I2O_CMD_STATUS_GET << 24 | HOST_TID << 12 |
+ ADAPTER_TID);
+ msg->u.s.icntxt = cpu_to_le32(i2o_exec_driver.context);
+ msg->u.s.tcntxt = cpu_to_le32(0x00000000);
+ msg->body[0] = cpu_to_le32(0x00000000);
+ msg->body[1] = cpu_to_le32(0x00000000);
+ msg->body[2] = cpu_to_le32(i2o_dma_low(c->status_block.phys));
+ msg->body[3] = cpu_to_le32(i2o_dma_high(c->status_block.phys));
+ msg->body[4] = cpu_to_le32(sizeof(i2o_status_block)); /* always 88 bytes */
+
+ i2o_msg_post(c, msg);
+
+ /* Wait for a reply */
+ timeout = jiffies + I2O_TIMEOUT_STATUS_GET * HZ;
+ while (status_block[87] != 0xFF) {
+ if (time_after(jiffies, timeout)) {
+ osm_err("%s: Get status timeout.\n", c->name);
+ return -ETIMEDOUT;
+ }
+
+ schedule_timeout_uninterruptible(1);
+ }
+
+#ifdef DEBUG
+ i2o_debug_state(c);
+#endif
+
+ return 0;
+}
+
+/*
+ * i2o_hrt_get - Get the Hardware Resource Table from the I2O controller
+ * @c: I2O controller from which the HRT should be fetched
+ *
+ * The HRT contains information about possible hidden devices but is
+ * mostly useless to us.
+ *
+ * Returns 0 on success or negative error code on failure.
+ */
+static int i2o_hrt_get(struct i2o_controller *c)
+{
+ int rc;
+ int i;
+ i2o_hrt *hrt = c->hrt.virt;
+ u32 size = sizeof(i2o_hrt);
+ struct device *dev = &c->pdev->dev;
+
+ for (i = 0; i < I2O_HRT_GET_TRIES; i++) {
+ struct i2o_message *msg;
+
+ msg = i2o_msg_get_wait(c, I2O_TIMEOUT_MESSAGE_GET);
+ if (IS_ERR(msg))
+ return PTR_ERR(msg);
+
+ msg->u.head[0] = cpu_to_le32(SIX_WORD_MSG_SIZE | SGL_OFFSET_4);
+ msg->u.head[1] =
+ cpu_to_le32(I2O_CMD_HRT_GET << 24 | HOST_TID << 12 |
+ ADAPTER_TID);
+ msg->body[0] = cpu_to_le32(0xd0000000 | c->hrt.len);
+ msg->body[1] = cpu_to_le32(c->hrt.phys);
+
+ rc = i2o_msg_post_wait_mem(c, msg, 20, &c->hrt);
+
+ if (rc < 0) {
+ osm_err("%s: Unable to get HRT (status=%#x)\n", c->name,
+ -rc);
+ return rc;
+ }
+
+ size = hrt->num_entries * hrt->entry_len << 2;
+ if (size > c->hrt.len) {
+ if (i2o_dma_realloc(dev, &c->hrt, size))
+ return -ENOMEM;
+ else
+ hrt = c->hrt.virt;
+ } else
+ return i2o_parse_hrt(c);
+ }
+
+ osm_err("%s: Unable to get HRT after %d tries, giving up\n", c->name,
+ I2O_HRT_GET_TRIES);
+
+ return -EBUSY;
+}
+
+/**
+ * i2o_iop_release - release the memory for a I2O controller
+ * @dev: I2O controller which should be released
+ *
+ * Release the allocated memory. This function is called if refcount of
+ * device reaches 0 automatically.
+ */
+static void i2o_iop_release(struct device *dev)
+{
+ struct i2o_controller *c = to_i2o_controller(dev);
+
+ i2o_iop_free(c);
+};
+
+/**
+ * i2o_iop_alloc - Allocate and initialize a i2o_controller struct
+ *
+ * Allocate the necessary memory for a i2o_controller struct and
+ * initialize the lists and message mempool.
+ *
+ * Returns a pointer to the I2O controller or a negative error code on
+ * failure.
+ */
+struct i2o_controller *i2o_iop_alloc(void)
+{
+ static int unit; /* 0 and 1 are NULL IOP and Local Host */
+ struct i2o_controller *c;
+ char poolname[32];
+
+ c = kzalloc(sizeof(*c), GFP_KERNEL);
+ if (!c) {
+ osm_err("i2o: Insufficient memory to allocate a I2O controller."
+ "\n");
+ return ERR_PTR(-ENOMEM);
+ }
+
+ c->unit = unit++;
+ sprintf(c->name, "iop%d", c->unit);
+
+ snprintf(poolname, sizeof(poolname), "i2o_%s_msg_inpool", c->name);
+ if (i2o_pool_alloc
+ (&c->in_msg, poolname, I2O_INBOUND_MSG_FRAME_SIZE * 4 + sizeof(u32),
+ I2O_MSG_INPOOL_MIN)) {
+ kfree(c);
+ return ERR_PTR(-ENOMEM);
+ };
+
+ INIT_LIST_HEAD(&c->devices);
+ spin_lock_init(&c->lock);
+ mutex_init(&c->lct_lock);
+
+ device_initialize(&c->device);
+
+ c->device.release = &i2o_iop_release;
+
+ dev_set_name(&c->device, "iop%d", c->unit);
+
+#if BITS_PER_LONG == 64
+ spin_lock_init(&c->context_list_lock);
+ atomic_set(&c->context_list_counter, 0);
+ INIT_LIST_HEAD(&c->context_list);
+#endif
+
+ return c;
+};
+
+/**
+ * i2o_iop_add - Initialize the I2O controller and add him to the I2O core
+ * @c: controller
+ *
+ * Initialize the I2O controller and if no error occurs add him to the I2O
+ * core.
+ *
+ * Returns 0 on success or negative error code on failure.
+ */
+int i2o_iop_add(struct i2o_controller *c)
+{
+ int rc;
+
+ rc = device_add(&c->device);
+ if (rc) {
+ osm_err("%s: could not add controller\n", c->name);
+ goto iop_reset;
+ }
+
+ osm_info("%s: Activating I2O controller...\n", c->name);
+ osm_info("%s: This may take a few minutes if there are many devices\n",
+ c->name);
+
+ rc = i2o_iop_activate(c);
+ if (rc) {
+ osm_err("%s: could not activate controller\n", c->name);
+ goto device_del;
+ }
+
+ osm_debug("%s: building sys table...\n", c->name);
+
+ rc = i2o_systab_build();
+ if (rc)
+ goto device_del;
+
+ osm_debug("%s: online controller...\n", c->name);
+
+ rc = i2o_iop_online(c);
+ if (rc)
+ goto device_del;
+
+ osm_debug("%s: getting LCT...\n", c->name);
+
+ rc = i2o_exec_lct_get(c);
+ if (rc)
+ goto device_del;
+
+ list_add(&c->list, &i2o_controllers);
+
+ i2o_driver_notify_controller_add_all(c);
+
+ osm_info("%s: Controller added\n", c->name);
+
+ return 0;
+
+ device_del:
+ device_del(&c->device);
+
+ iop_reset:
+ i2o_iop_reset(c);
+
+ return rc;
+};
+
+/**
+ * i2o_event_register - Turn on/off event notification for a I2O device
+ * @dev: I2O device which should receive the event registration request
+ * @drv: driver which want to get notified
+ * @tcntxt: transaction context to use with this notifier
+ * @evt_mask: mask of events
+ *
+ * Create and posts an event registration message to the task. No reply
+ * is waited for, or expected. If you do not want further notifications,
+ * call the i2o_event_register again with a evt_mask of 0.
+ *
+ * Returns 0 on success or negative error code on failure.
+ */
+int i2o_event_register(struct i2o_device *dev, struct i2o_driver *drv,
+ int tcntxt, u32 evt_mask)
+{
+ struct i2o_controller *c = dev->iop;
+ struct i2o_message *msg;
+
+ msg = i2o_msg_get_wait(c, I2O_TIMEOUT_MESSAGE_GET);
+ if (IS_ERR(msg))
+ return PTR_ERR(msg);
+
+ msg->u.head[0] = cpu_to_le32(FIVE_WORD_MSG_SIZE | SGL_OFFSET_0);
+ msg->u.head[1] =
+ cpu_to_le32(I2O_CMD_UTIL_EVT_REGISTER << 24 | HOST_TID << 12 | dev->
+ lct_data.tid);
+ msg->u.s.icntxt = cpu_to_le32(drv->context);
+ msg->u.s.tcntxt = cpu_to_le32(tcntxt);
+ msg->body[0] = cpu_to_le32(evt_mask);
+
+ i2o_msg_post(c, msg);
+
+ return 0;
+};
+
+/**
+ * i2o_iop_init - I2O main initialization function
+ *
+ * Initialize the I2O drivers (OSM) functions, register the Executive OSM,
+ * initialize the I2O PCI part and finally initialize I2O device stuff.
+ *
+ * Returns 0 on success or negative error code on failure.
+ */
+static int __init i2o_iop_init(void)
+{
+ int rc = 0;
+
+ printk(KERN_INFO OSM_DESCRIPTION " v" OSM_VERSION "\n");
+
+ rc = i2o_driver_init();
+ if (rc)
+ goto exit;
+
+ rc = i2o_exec_init();
+ if (rc)
+ goto driver_exit;
+
+ rc = i2o_pci_init();
+ if (rc)
+ goto exec_exit;
+
+ return 0;
+
+ exec_exit:
+ i2o_exec_exit();
+
+ driver_exit:
+ i2o_driver_exit();
+
+ exit:
+ return rc;
+}
+
+/**
+ * i2o_iop_exit - I2O main exit function
+ *
+ * Removes I2O controllers from PCI subsystem and shut down OSMs.
+ */
+static void __exit i2o_iop_exit(void)
+{
+ i2o_pci_exit();
+ i2o_exec_exit();
+ i2o_driver_exit();
+};
+
+module_init(i2o_iop_init);
+module_exit(i2o_iop_exit);
+
+MODULE_AUTHOR("Red Hat Software");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION(OSM_DESCRIPTION);
+MODULE_VERSION(OSM_VERSION);
+
+#if BITS_PER_LONG == 64
+EXPORT_SYMBOL(i2o_cntxt_list_add);
+EXPORT_SYMBOL(i2o_cntxt_list_get);
+EXPORT_SYMBOL(i2o_cntxt_list_remove);
+EXPORT_SYMBOL(i2o_cntxt_list_get_ptr);
+#endif
+EXPORT_SYMBOL(i2o_msg_get_wait);
+EXPORT_SYMBOL(i2o_find_iop);
+EXPORT_SYMBOL(i2o_iop_find_device);
+EXPORT_SYMBOL(i2o_event_register);
+EXPORT_SYMBOL(i2o_status_get);
+EXPORT_SYMBOL(i2o_controllers);
diff --git a/drivers/staging/i2o/memory.c b/drivers/staging/i2o/memory.c
new file mode 100644
index 000000000..78b702c18
--- /dev/null
+++ b/drivers/staging/i2o/memory.c
@@ -0,0 +1,312 @@
+/*
+ * Functions to handle I2O memory
+ *
+ * Pulled from the inlines in i2o headers and uninlined
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ */
+
+#include <linux/module.h>
+#include "i2o.h"
+#include <linux/delay.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+#include "core.h"
+
+/* Protects our 32/64bit mask switching */
+static DEFINE_MUTEX(mem_lock);
+
+/**
+ * i2o_sg_tablesize - Calculate the maximum number of elements in a SGL
+ * @c: I2O controller for which the calculation should be done
+ * @body_size: maximum body size used for message in 32-bit words.
+ *
+ * Return the maximum number of SG elements in a SG list.
+ */
+u16 i2o_sg_tablesize(struct i2o_controller *c, u16 body_size)
+{
+ i2o_status_block *sb = c->status_block.virt;
+ u16 sg_count =
+ (sb->inbound_frame_size - sizeof(struct i2o_message) / 4) -
+ body_size;
+
+ if (c->pae_support) {
+ /*
+ * for 64-bit a SG attribute element must be added and each
+ * SG element needs 12 bytes instead of 8.
+ */
+ sg_count -= 2;
+ sg_count /= 3;
+ } else
+ sg_count /= 2;
+
+ if (c->short_req && (sg_count > 8))
+ sg_count = 8;
+
+ return sg_count;
+}
+EXPORT_SYMBOL_GPL(i2o_sg_tablesize);
+
+
+/**
+ * i2o_dma_map_single - Map pointer to controller and fill in I2O message.
+ * @c: I2O controller
+ * @ptr: pointer to the data which should be mapped
+ * @size: size of data in bytes
+ * @direction: DMA_TO_DEVICE / DMA_FROM_DEVICE
+ * @sg_ptr: pointer to the SG list inside the I2O message
+ *
+ * This function does all necessary DMA handling and also writes the I2O
+ * SGL elements into the I2O message. For details on DMA handling see also
+ * dma_map_single(). The pointer sg_ptr will only be set to the end of the
+ * SG list if the allocation was successful.
+ *
+ * Returns DMA address which must be checked for failures using
+ * dma_mapping_error().
+ */
+dma_addr_t i2o_dma_map_single(struct i2o_controller *c, void *ptr,
+ size_t size,
+ enum dma_data_direction direction,
+ u32 ** sg_ptr)
+{
+ u32 sg_flags;
+ u32 *mptr = *sg_ptr;
+ dma_addr_t dma_addr;
+
+ switch (direction) {
+ case DMA_TO_DEVICE:
+ sg_flags = 0xd4000000;
+ break;
+ case DMA_FROM_DEVICE:
+ sg_flags = 0xd0000000;
+ break;
+ default:
+ return 0;
+ }
+
+ dma_addr = dma_map_single(&c->pdev->dev, ptr, size, direction);
+ if (!dma_mapping_error(&c->pdev->dev, dma_addr)) {
+#ifdef CONFIG_I2O_EXT_ADAPTEC_DMA64
+ if ((sizeof(dma_addr_t) > 4) && c->pae_support) {
+ *mptr++ = cpu_to_le32(0x7C020002);
+ *mptr++ = cpu_to_le32(PAGE_SIZE);
+ }
+#endif
+
+ *mptr++ = cpu_to_le32(sg_flags | size);
+ *mptr++ = cpu_to_le32(i2o_dma_low(dma_addr));
+#ifdef CONFIG_I2O_EXT_ADAPTEC_DMA64
+ if ((sizeof(dma_addr_t) > 4) && c->pae_support)
+ *mptr++ = cpu_to_le32(i2o_dma_high(dma_addr));
+#endif
+ *sg_ptr = mptr;
+ }
+ return dma_addr;
+}
+EXPORT_SYMBOL_GPL(i2o_dma_map_single);
+
+/**
+ * i2o_dma_map_sg - Map a SG List to controller and fill in I2O message.
+ * @c: I2O controller
+ * @sg: SG list to be mapped
+ * @sg_count: number of elements in the SG list
+ * @direction: DMA_TO_DEVICE / DMA_FROM_DEVICE
+ * @sg_ptr: pointer to the SG list inside the I2O message
+ *
+ * This function does all necessary DMA handling and also writes the I2O
+ * SGL elements into the I2O message. For details on DMA handling see also
+ * dma_map_sg(). The pointer sg_ptr will only be set to the end of the SG
+ * list if the allocation was successful.
+ *
+ * Returns 0 on failure or 1 on success.
+ */
+int i2o_dma_map_sg(struct i2o_controller *c, struct scatterlist *sg,
+ int sg_count, enum dma_data_direction direction, u32 ** sg_ptr)
+{
+ u32 sg_flags;
+ u32 *mptr = *sg_ptr;
+
+ switch (direction) {
+ case DMA_TO_DEVICE:
+ sg_flags = 0x14000000;
+ break;
+ case DMA_FROM_DEVICE:
+ sg_flags = 0x10000000;
+ break;
+ default:
+ return 0;
+ }
+
+ sg_count = dma_map_sg(&c->pdev->dev, sg, sg_count, direction);
+ if (!sg_count)
+ return 0;
+
+#ifdef CONFIG_I2O_EXT_ADAPTEC_DMA64
+ if ((sizeof(dma_addr_t) > 4) && c->pae_support) {
+ *mptr++ = cpu_to_le32(0x7C020002);
+ *mptr++ = cpu_to_le32(PAGE_SIZE);
+ }
+#endif
+
+ while (sg_count-- > 0) {
+ if (!sg_count)
+ sg_flags |= 0xC0000000;
+ *mptr++ = cpu_to_le32(sg_flags | sg_dma_len(sg));
+ *mptr++ = cpu_to_le32(i2o_dma_low(sg_dma_address(sg)));
+#ifdef CONFIG_I2O_EXT_ADAPTEC_DMA64
+ if ((sizeof(dma_addr_t) > 4) && c->pae_support)
+ *mptr++ = cpu_to_le32(i2o_dma_high(sg_dma_address(sg)));
+#endif
+ sg = sg_next(sg);
+ }
+ *sg_ptr = mptr;
+
+ return 1;
+}
+EXPORT_SYMBOL_GPL(i2o_dma_map_sg);
+
+/**
+ * i2o_dma_alloc - Allocate DMA memory
+ * @dev: struct device pointer to the PCI device of the I2O controller
+ * @addr: i2o_dma struct which should get the DMA buffer
+ * @len: length of the new DMA memory
+ *
+ * Allocate a coherent DMA memory and write the pointers into addr.
+ *
+ * Returns 0 on success or -ENOMEM on failure.
+ */
+int i2o_dma_alloc(struct device *dev, struct i2o_dma *addr, size_t len)
+{
+ struct pci_dev *pdev = to_pci_dev(dev);
+ int dma_64 = 0;
+
+ mutex_lock(&mem_lock);
+ if ((sizeof(dma_addr_t) > 4) && (pdev->dma_mask == DMA_BIT_MASK(64))) {
+ dma_64 = 1;
+ if (pci_set_dma_mask(pdev, DMA_BIT_MASK(32))) {
+ mutex_unlock(&mem_lock);
+ return -ENOMEM;
+ }
+ }
+
+ addr->virt = dma_alloc_coherent(dev, len, &addr->phys, GFP_KERNEL);
+
+ if ((sizeof(dma_addr_t) > 4) && dma_64)
+ if (pci_set_dma_mask(pdev, DMA_BIT_MASK(64)))
+ printk(KERN_WARNING "i2o: unable to set 64-bit DMA");
+ mutex_unlock(&mem_lock);
+
+ if (!addr->virt)
+ return -ENOMEM;
+
+ memset(addr->virt, 0, len);
+ addr->len = len;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(i2o_dma_alloc);
+
+
+/**
+ * i2o_dma_free - Free DMA memory
+ * @dev: struct device pointer to the PCI device of the I2O controller
+ * @addr: i2o_dma struct which contains the DMA buffer
+ *
+ * Free a coherent DMA memory and set virtual address of addr to NULL.
+ */
+void i2o_dma_free(struct device *dev, struct i2o_dma *addr)
+{
+ if (addr->virt) {
+ if (addr->phys)
+ dma_free_coherent(dev, addr->len, addr->virt,
+ addr->phys);
+ else
+ kfree(addr->virt);
+ addr->virt = NULL;
+ }
+}
+EXPORT_SYMBOL_GPL(i2o_dma_free);
+
+
+/**
+ * i2o_dma_realloc - Realloc DMA memory
+ * @dev: struct device pointer to the PCI device of the I2O controller
+ * @addr: pointer to a i2o_dma struct DMA buffer
+ * @len: new length of memory
+ *
+ * If there was something allocated in the addr, free it first. If len > 0
+ * than try to allocate it and write the addresses back to the addr
+ * structure. If len == 0 set the virtual address to NULL.
+ *
+ * Returns the 0 on success or negative error code on failure.
+ */
+int i2o_dma_realloc(struct device *dev, struct i2o_dma *addr, size_t len)
+{
+ i2o_dma_free(dev, addr);
+
+ if (len)
+ return i2o_dma_alloc(dev, addr, len);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(i2o_dma_realloc);
+
+/*
+ * i2o_pool_alloc - Allocate an slab cache and mempool
+ * @mempool: pointer to struct i2o_pool to write data into.
+ * @name: name which is used to identify cache
+ * @size: size of each object
+ * @min_nr: minimum number of objects
+ *
+ * First allocates a slab cache with name and size. Then allocates a
+ * mempool which uses the slab cache for allocation and freeing.
+ *
+ * Returns 0 on success or negative error code on failure.
+ */
+int i2o_pool_alloc(struct i2o_pool *pool, const char *name,
+ size_t size, int min_nr)
+{
+ pool->name = kstrdup(name, GFP_KERNEL);
+ if (!pool->name)
+ goto exit;
+
+ pool->slab =
+ kmem_cache_create(pool->name, size, 0, SLAB_HWCACHE_ALIGN, NULL);
+ if (!pool->slab)
+ goto free_name;
+
+ pool->mempool = mempool_create_slab_pool(min_nr, pool->slab);
+ if (!pool->mempool)
+ goto free_slab;
+
+ return 0;
+
+free_slab:
+ kmem_cache_destroy(pool->slab);
+
+free_name:
+ kfree(pool->name);
+
+exit:
+ return -ENOMEM;
+}
+EXPORT_SYMBOL_GPL(i2o_pool_alloc);
+
+/*
+ * i2o_pool_free - Free slab cache and mempool again
+ * @mempool: pointer to struct i2o_pool which should be freed
+ *
+ * Note that you have to return all objects to the mempool again before
+ * calling i2o_pool_free().
+ */
+void i2o_pool_free(struct i2o_pool *pool)
+{
+ mempool_destroy(pool->mempool);
+ kmem_cache_destroy(pool->slab);
+ kfree(pool->name);
+};
+EXPORT_SYMBOL_GPL(i2o_pool_free);
diff --git a/drivers/staging/i2o/pci.c b/drivers/staging/i2o/pci.c
new file mode 100644
index 000000000..49804c9cf
--- /dev/null
+++ b/drivers/staging/i2o/pci.c
@@ -0,0 +1,500 @@
+/*
+ * PCI handling of I2O controller
+ *
+ * Copyright (C) 1999-2002 Red Hat Software
+ *
+ * Written by Alan Cox, Building Number Three Ltd
+ *
+ * This program is free software; you can redistribute it and/or 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.
+ *
+ * A lot of the I2O message side code from this is taken from the Red
+ * Creek RCPCI45 adapter driver by Red Creek Communications
+ *
+ * Fixes/additions:
+ * Philipp Rumpf
+ * Juha Sievänen <Juha.Sievanen@cs.Helsinki.FI>
+ * Auvo Häkkinen <Auvo.Hakkinen@cs.Helsinki.FI>
+ * Deepak Saxena <deepak@plexity.net>
+ * Boji T Kannanthanam <boji.t.kannanthanam@intel.com>
+ * Alan Cox <alan@lxorguk.ukuu.org.uk>:
+ * Ported to Linux 2.5.
+ * Markus Lidel <Markus.Lidel@shadowconnect.com>:
+ * Minor fixes for 2.6.
+ * Markus Lidel <Markus.Lidel@shadowconnect.com>:
+ * Support for sysfs included.
+ */
+
+#include <linux/pci.h>
+#include <linux/interrupt.h>
+#include <linux/slab.h>
+#include "i2o.h"
+#include <linux/module.h>
+#include "core.h"
+
+#define OSM_DESCRIPTION "I2O-subsystem"
+
+/* PCI device id table for all I2O controllers */
+static struct pci_device_id i2o_pci_ids[] = {
+ {PCI_DEVICE_CLASS(PCI_CLASS_INTELLIGENT_I2O << 8, 0xffff00)},
+ {PCI_DEVICE(PCI_VENDOR_ID_DPT, 0xa511)},
+ {.vendor = PCI_VENDOR_ID_INTEL,.device = 0x1962,
+ .subvendor = PCI_VENDOR_ID_PROMISE,.subdevice = PCI_ANY_ID},
+ {0}
+};
+
+/**
+ * i2o_pci_free - Frees the DMA memory for the I2O controller
+ * @c: I2O controller to free
+ *
+ * Remove all allocated DMA memory and unmap memory IO regions. If MTRR
+ * is enabled, also remove it again.
+ */
+static void i2o_pci_free(struct i2o_controller *c)
+{
+ struct device *dev;
+
+ dev = &c->pdev->dev;
+
+ i2o_dma_free(dev, &c->out_queue);
+ i2o_dma_free(dev, &c->status_block);
+ kfree(c->lct);
+ i2o_dma_free(dev, &c->dlct);
+ i2o_dma_free(dev, &c->hrt);
+ i2o_dma_free(dev, &c->status);
+
+ if (c->raptor && c->in_queue.virt)
+ iounmap(c->in_queue.virt);
+
+ if (c->base.virt)
+ iounmap(c->base.virt);
+
+ pci_release_regions(c->pdev);
+}
+
+/**
+ * i2o_pci_alloc - Allocate DMA memory, map IO memory for I2O controller
+ * @c: I2O controller
+ *
+ * Allocate DMA memory for a PCI (or in theory AGP) I2O controller. All
+ * IO mappings are also done here. If MTRR is enabled, also do add memory
+ * regions here.
+ *
+ * Returns 0 on success or negative error code on failure.
+ */
+static int i2o_pci_alloc(struct i2o_controller *c)
+{
+ struct pci_dev *pdev = c->pdev;
+ struct device *dev = &pdev->dev;
+ int i;
+
+ if (pci_request_regions(pdev, OSM_DESCRIPTION)) {
+ printk(KERN_ERR "%s: device already claimed\n", c->name);
+ return -ENODEV;
+ }
+
+ for (i = 0; i < 6; i++) {
+ /* Skip I/O spaces */
+ if (!(pci_resource_flags(pdev, i) & IORESOURCE_IO)) {
+ if (!c->base.phys) {
+ c->base.phys = pci_resource_start(pdev, i);
+ c->base.len = pci_resource_len(pdev, i);
+
+ /*
+ * If we know what card it is, set the size
+ * correctly. Code is taken from dpt_i2o.c
+ */
+ if (pdev->device == 0xa501) {
+ if (pdev->subsystem_device >= 0xc032 &&
+ pdev->subsystem_device <= 0xc03b) {
+ if (c->base.len > 0x400000)
+ c->base.len = 0x400000;
+ } else {
+ if (c->base.len > 0x100000)
+ c->base.len = 0x100000;
+ }
+ }
+ if (!c->raptor)
+ break;
+ } else {
+ c->in_queue.phys = pci_resource_start(pdev, i);
+ c->in_queue.len = pci_resource_len(pdev, i);
+ break;
+ }
+ }
+ }
+
+ if (i == 6) {
+ printk(KERN_ERR "%s: I2O controller has no memory regions"
+ " defined.\n", c->name);
+ i2o_pci_free(c);
+ return -EINVAL;
+ }
+
+ /* Map the I2O controller */
+ if (c->raptor) {
+ printk(KERN_INFO "%s: PCI I2O controller\n", c->name);
+ printk(KERN_INFO " BAR0 at 0x%08lX size=%ld\n",
+ (unsigned long)c->base.phys, (unsigned long)c->base.len);
+ printk(KERN_INFO " BAR1 at 0x%08lX size=%ld\n",
+ (unsigned long)c->in_queue.phys,
+ (unsigned long)c->in_queue.len);
+ } else
+ printk(KERN_INFO "%s: PCI I2O controller at %08lX size=%ld\n",
+ c->name, (unsigned long)c->base.phys,
+ (unsigned long)c->base.len);
+
+ c->base.virt = ioremap_nocache(c->base.phys, c->base.len);
+ if (!c->base.virt) {
+ printk(KERN_ERR "%s: Unable to map controller.\n", c->name);
+ i2o_pci_free(c);
+ return -ENOMEM;
+ }
+
+ if (c->raptor) {
+ c->in_queue.virt =
+ ioremap_nocache(c->in_queue.phys, c->in_queue.len);
+ if (!c->in_queue.virt) {
+ printk(KERN_ERR "%s: Unable to map controller.\n",
+ c->name);
+ i2o_pci_free(c);
+ return -ENOMEM;
+ }
+ } else
+ c->in_queue = c->base;
+
+ c->irq_status = c->base.virt + I2O_IRQ_STATUS;
+ c->irq_mask = c->base.virt + I2O_IRQ_MASK;
+ c->in_port = c->base.virt + I2O_IN_PORT;
+ c->out_port = c->base.virt + I2O_OUT_PORT;
+
+ /* Motorola/Freescale chip does not follow spec */
+ if (pdev->vendor == PCI_VENDOR_ID_MOTOROLA && pdev->device == 0x18c0) {
+ /* Check if CPU is enabled */
+ if (be32_to_cpu(readl(c->base.virt + 0x10000)) & 0x10000000) {
+ printk(KERN_INFO "%s: MPC82XX needs CPU running to "
+ "service I2O.\n", c->name);
+ i2o_pci_free(c);
+ return -ENODEV;
+ } else {
+ c->irq_status += I2O_MOTOROLA_PORT_OFFSET;
+ c->irq_mask += I2O_MOTOROLA_PORT_OFFSET;
+ c->in_port += I2O_MOTOROLA_PORT_OFFSET;
+ c->out_port += I2O_MOTOROLA_PORT_OFFSET;
+ printk(KERN_INFO "%s: MPC82XX workarounds activated.\n",
+ c->name);
+ }
+ }
+
+ if (i2o_dma_alloc(dev, &c->status, 8)) {
+ i2o_pci_free(c);
+ return -ENOMEM;
+ }
+
+ if (i2o_dma_alloc(dev, &c->hrt, sizeof(i2o_hrt))) {
+ i2o_pci_free(c);
+ return -ENOMEM;
+ }
+
+ if (i2o_dma_alloc(dev, &c->dlct, 8192)) {
+ i2o_pci_free(c);
+ return -ENOMEM;
+ }
+
+ if (i2o_dma_alloc(dev, &c->status_block, sizeof(i2o_status_block))) {
+ i2o_pci_free(c);
+ return -ENOMEM;
+ }
+
+ if (i2o_dma_alloc(dev, &c->out_queue,
+ I2O_MAX_OUTBOUND_MSG_FRAMES * I2O_OUTBOUND_MSG_FRAME_SIZE *
+ sizeof(u32))) {
+ i2o_pci_free(c);
+ return -ENOMEM;
+ }
+
+ pci_set_drvdata(pdev, c);
+
+ return 0;
+}
+
+/**
+ * i2o_pci_interrupt - Interrupt handler for I2O controller
+ * @irq: interrupt line
+ * @dev_id: pointer to the I2O controller
+ *
+ * Handle an interrupt from a PCI based I2O controller. This turns out
+ * to be rather simple. We keep the controller pointer in the cookie.
+ */
+static irqreturn_t i2o_pci_interrupt(int irq, void *dev_id)
+{
+ struct i2o_controller *c = dev_id;
+ u32 m;
+ irqreturn_t rc = IRQ_NONE;
+
+ while (readl(c->irq_status) & I2O_IRQ_OUTBOUND_POST) {
+ m = readl(c->out_port);
+ if (m == I2O_QUEUE_EMPTY) {
+ /*
+ * Old 960 steppings had a bug in the I2O unit that
+ * caused the queue to appear empty when it wasn't.
+ */
+ m = readl(c->out_port);
+ if (unlikely(m == I2O_QUEUE_EMPTY))
+ break;
+ }
+
+ /* dispatch it */
+ if (i2o_driver_dispatch(c, m))
+ /* flush it if result != 0 */
+ i2o_flush_reply(c, m);
+
+ rc = IRQ_HANDLED;
+ }
+
+ return rc;
+}
+
+/**
+ * i2o_pci_irq_enable - Allocate interrupt for I2O controller
+ * @c: i2o_controller that the request is for
+ *
+ * Allocate an interrupt for the I2O controller, and activate interrupts
+ * on the I2O controller.
+ *
+ * Returns 0 on success or negative error code on failure.
+ */
+static int i2o_pci_irq_enable(struct i2o_controller *c)
+{
+ struct pci_dev *pdev = c->pdev;
+ int rc;
+
+ writel(0xffffffff, c->irq_mask);
+
+ if (pdev->irq) {
+ rc = request_irq(pdev->irq, i2o_pci_interrupt, IRQF_SHARED,
+ c->name, c);
+ if (rc < 0) {
+ printk(KERN_ERR "%s: unable to allocate interrupt %d."
+ "\n", c->name, pdev->irq);
+ return rc;
+ }
+ }
+
+ writel(0x00000000, c->irq_mask);
+
+ printk(KERN_INFO "%s: Installed at IRQ %d\n", c->name, pdev->irq);
+
+ return 0;
+}
+
+/**
+ * i2o_pci_irq_disable - Free interrupt for I2O controller
+ * @c: I2O controller
+ *
+ * Disable interrupts in I2O controller and then free interrupt.
+ */
+static void i2o_pci_irq_disable(struct i2o_controller *c)
+{
+ writel(0xffffffff, c->irq_mask);
+
+ if (c->pdev->irq > 0)
+ free_irq(c->pdev->irq, c);
+}
+
+/**
+ * i2o_pci_probe - Probe the PCI device for an I2O controller
+ * @pdev: PCI device to test
+ * @id: id which matched with the PCI device id table
+ *
+ * Probe the PCI device for any device which is a memory of the
+ * Intelligent, I2O class or an Adaptec Zero Channel Controller. We
+ * attempt to set up each such device and register it with the core.
+ *
+ * Returns 0 on success or negative error code on failure.
+ */
+static int i2o_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
+{
+ struct i2o_controller *c;
+ int rc;
+ struct pci_dev *i960 = NULL;
+
+ printk(KERN_INFO "i2o: Checking for PCI I2O controllers...\n");
+
+ if ((pdev->class & 0xff) > 1) {
+ printk(KERN_WARNING "i2o: %s does not support I2O 1.5 "
+ "(skipping).\n", pci_name(pdev));
+ return -ENODEV;
+ }
+
+ rc = pci_enable_device(pdev);
+ if (rc) {
+ printk(KERN_WARNING "i2o: couldn't enable device %s\n",
+ pci_name(pdev));
+ return rc;
+ }
+
+ if (pci_set_dma_mask(pdev, DMA_BIT_MASK(32))) {
+ printk(KERN_WARNING "i2o: no suitable DMA found for %s\n",
+ pci_name(pdev));
+ rc = -ENODEV;
+ goto disable;
+ }
+
+ pci_set_master(pdev);
+
+ c = i2o_iop_alloc();
+ if (IS_ERR(c)) {
+ printk(KERN_ERR "i2o: couldn't allocate memory for %s\n",
+ pci_name(pdev));
+ rc = PTR_ERR(c);
+ goto disable;
+ } else
+ printk(KERN_INFO "%s: controller found (%s)\n", c->name,
+ pci_name(pdev));
+
+ c->pdev = pdev;
+ c->device.parent = &pdev->dev;
+
+ /* Cards that fall apart if you hit them with large I/O loads... */
+ if (pdev->vendor == PCI_VENDOR_ID_NCR && pdev->device == 0x0630) {
+ c->short_req = 1;
+ printk(KERN_INFO "%s: Symbios FC920 workarounds activated.\n",
+ c->name);
+ }
+
+ if (pdev->subsystem_vendor == PCI_VENDOR_ID_PROMISE) {
+ /*
+ * Expose the ship behind i960 for initialization, or it will
+ * failed
+ */
+ i960 = pci_get_slot(c->pdev->bus,
+ PCI_DEVFN(PCI_SLOT(c->pdev->devfn), 0));
+
+ if (i960) {
+ pci_write_config_word(i960, 0x42, 0);
+ pci_dev_put(i960);
+ }
+
+ c->promise = 1;
+ c->limit_sectors = 1;
+ }
+
+ if (pdev->subsystem_vendor == PCI_VENDOR_ID_DPT)
+ c->adaptec = 1;
+
+ /* Cards that go bananas if you quiesce them before you reset them. */
+ if (pdev->vendor == PCI_VENDOR_ID_DPT) {
+ c->no_quiesce = 1;
+ if (pdev->device == 0xa511)
+ c->raptor = 1;
+
+ if (pdev->subsystem_device == 0xc05a) {
+ c->limit_sectors = 1;
+ printk(KERN_INFO
+ "%s: limit sectors per request to %d\n", c->name,
+ I2O_MAX_SECTORS_LIMITED);
+ }
+#ifdef CONFIG_I2O_EXT_ADAPTEC_DMA64
+ if (sizeof(dma_addr_t) > 4) {
+ if (pci_set_dma_mask(pdev, DMA_BIT_MASK(64)))
+ printk(KERN_INFO "%s: 64-bit DMA unavailable\n",
+ c->name);
+ else {
+ c->pae_support = 1;
+ printk(KERN_INFO "%s: using 64-bit DMA\n",
+ c->name);
+ }
+ }
+#endif
+ }
+
+ rc = i2o_pci_alloc(c);
+ if (rc) {
+ printk(KERN_ERR "%s: DMA / IO allocation for I2O controller "
+ "failed\n", c->name);
+ goto free_controller;
+ }
+
+ if (i2o_pci_irq_enable(c)) {
+ printk(KERN_ERR "%s: unable to enable interrupts for I2O "
+ "controller\n", c->name);
+ goto free_pci;
+ }
+
+ rc = i2o_iop_add(c);
+ if (rc)
+ goto uninstall;
+
+ if (i960)
+ pci_write_config_word(i960, 0x42, 0x03ff);
+
+ return 0;
+
+ uninstall:
+ i2o_pci_irq_disable(c);
+
+ free_pci:
+ i2o_pci_free(c);
+
+ free_controller:
+ i2o_iop_free(c);
+
+ disable:
+ pci_disable_device(pdev);
+
+ return rc;
+}
+
+/**
+ * i2o_pci_remove - Removes a I2O controller from the system
+ * @pdev: I2O controller which should be removed
+ *
+ * Reset the I2O controller, disable interrupts and remove all allocated
+ * resources.
+ */
+static void i2o_pci_remove(struct pci_dev *pdev)
+{
+ struct i2o_controller *c;
+ c = pci_get_drvdata(pdev);
+
+ i2o_iop_remove(c);
+ i2o_pci_irq_disable(c);
+ i2o_pci_free(c);
+
+ pci_disable_device(pdev);
+
+ printk(KERN_INFO "%s: Controller removed.\n", c->name);
+
+ put_device(&c->device);
+};
+
+/* PCI driver for I2O controller */
+static struct pci_driver i2o_pci_driver = {
+ .name = "PCI_I2O",
+ .id_table = i2o_pci_ids,
+ .probe = i2o_pci_probe,
+ .remove = i2o_pci_remove,
+};
+
+/**
+ * i2o_pci_init - registers I2O PCI driver in PCI subsystem
+ *
+ * Returns > 0 on success or negative error code on failure.
+ */
+int __init i2o_pci_init(void)
+{
+ return pci_register_driver(&i2o_pci_driver);
+};
+
+/**
+ * i2o_pci_exit - unregisters I2O PCI driver from PCI subsystem
+ */
+void __exit i2o_pci_exit(void)
+{
+ pci_unregister_driver(&i2o_pci_driver);
+};
+
+MODULE_DEVICE_TABLE(pci, i2o_pci_ids);
diff --git a/drivers/staging/iio/Documentation/dac/max517 b/drivers/staging/iio/Documentation/dac/max517
new file mode 100644
index 000000000..e60ec2f91
--- /dev/null
+++ b/drivers/staging/iio/Documentation/dac/max517
@@ -0,0 +1,41 @@
+Kernel driver max517
+====================
+
+Supported chips:
+ * Maxim MAX517, MAX518, MAX519
+ Prefix: 'max517'
+ Datasheet: Publicly available at the Maxim website
+ http://www.maxim-ic.com/
+
+Author:
+ Roland Stigge <stigge@antcom.de>
+
+Description
+-----------
+
+The Maxim MAX517/518/519 is an 8-bit DAC on the I2C bus. The following table
+shows the different feature sets of the variants MAX517, MAX518 and MAX519:
+
+Feature MAX517 MAX518 MAX519
+--------------------------------------------------------------------------
+One output channel X
+Two output channels X X
+Simultaneous output updates X X
+Supply voltage as reference X
+Separate reference input X
+Reference input for each DAC X
+
+Via the iio sysfs interface, there are three attributes available: out1_raw,
+out2_raw and out12_raw. With out1_raw and out2_raw, the current output values
+(0..255) of the DACs can be written to the device. out12_raw can be used to set
+both output channel values simultaneously.
+
+With MAX517, only out1_raw is available.
+
+Via out1_scale (and where appropriate, out2_scale), the current scaling factor
+in mV can be read.
+
+When the operating system goes to a power down state, the Power Down function
+of the chip is activated, reducing the supply current to 4uA.
+
+On power-up, the device is in 0V-output state.
diff --git a/drivers/staging/iio/Documentation/device.txt b/drivers/staging/iio/Documentation/device.txt
new file mode 100644
index 000000000..8be32e5a0
--- /dev/null
+++ b/drivers/staging/iio/Documentation/device.txt
@@ -0,0 +1,79 @@
+IIO Device drivers
+
+This is not intended to provide a comprehensive guide to writing an
+IIO device driver. For further information see the drivers within the
+subsystem.
+
+The crucial structure for device drivers in iio is iio_dev.
+
+First allocate one using:
+
+struct iio_dev *indio_dev = iio_device_alloc(sizeof(struct chip_state));
+where chip_state is a structure of local state data for this instance of
+the chip.
+
+That data can be accessed using iio_priv(struct iio_dev *).
+
+Then fill in the following:
+
+- indio_dev->dev.parent
+ Struct device associated with the underlying hardware.
+- indio_dev->name
+ Name of the device being driven - made available as the name
+ attribute in sysfs.
+
+- indio_dev->info
+ pointer to a structure with elements that tend to be fixed for
+ large sets of different parts supported by a given driver.
+ This contains:
+ * info->driver_module:
+ Set to THIS_MODULE. Used to ensure correct ownership
+ of various resources allocate by the core.
+ * info->event_attrs:
+ Attributes used to enable / disable hardware events.
+ * info->attrs:
+ General device attributes. Typically used for the weird
+ and the wonderful bits not covered by the channel specification.
+ * info->read_raw:
+ Raw data reading function. Used for both raw channel access
+ and for associate parameters such as offsets and scales.
+ * info->write_raw:
+ Raw value writing function. Used for writable device values such
+ as DAC values and calibbias.
+ * info->read_event_config:
+ Typically only set if there are some interrupt lines. This
+ is used to read if an on sensor event detector is enabled.
+ * info->write_event_config:
+ Enable / disable an on sensor event detector.
+ * info->read_event_value:
+ Read value associated with on sensor event detectors. Note that
+ the meaning of the returned value is dependent on the event
+ type.
+ * info->write_event_value:
+ Write the value associated with on sensor event detectors. E.g.
+ a threshold above which an interrupt occurs. Note that the
+ meaning of the value to be set is event type dependant.
+
+- indio_dev->modes:
+ Specify whether direct access and / or ring buffer access is supported.
+- indio_dev->buffer:
+ An optional associated buffer.
+- indio_dev->pollfunc:
+ Poll function related elements. This controls what occurs when a trigger
+ to which this device is attached sends an event.
+- indio_dev->channels:
+ Specification of device channels. Most attributes etc. are built
+ from this spec.
+- indio_dev->num_channels:
+ How many channels are there?
+
+Once these are set up, a call to iio_device_register(indio_dev)
+will register the device with the iio core.
+
+Worth noting here is that, if a ring buffer is to be used, it can be
+allocated prior to registering the device with the iio-core, but must
+be registered afterwards (otherwise the whole parentage of devices
+gets confused)
+
+On remove, iio_device_unregister(indio_dev) will remove the device from
+the core, and iio_device_free(indio_dev) will clean up.
diff --git a/drivers/staging/iio/Documentation/inkernel.txt b/drivers/staging/iio/Documentation/inkernel.txt
new file mode 100644
index 000000000..ab528409b
--- /dev/null
+++ b/drivers/staging/iio/Documentation/inkernel.txt
@@ -0,0 +1,58 @@
+Industrial I/O Subsystem in kernel consumers.
+
+The IIO subsystem can act as a layer under other elements of the kernel
+providing a means of obtaining ADC type readings or of driving DAC type
+signals. The functionality supported will grow as use cases arise.
+
+Describing the channel mapping (iio/machine.h)
+
+Channel associations are described using:
+
+struct iio_map {
+ const char *adc_channel_label;
+ const char *consumer_dev_name;
+ const char *consumer_channel;
+};
+
+adc_channel_label identifies the channel on the IIO device by being
+matched against the datasheet_name field of the iio_chan_spec.
+
+consumer_dev_name allows identification of the consumer device.
+This are then used to find the channel mapping from the consumer device (see
+below).
+
+Finally consumer_channel is a string identifying the channel to the consumer.
+(Perhaps 'battery_voltage' or similar).
+
+An array of these structures is then passed to the IIO driver.
+
+Supporting in kernel interfaces in the driver (driver.h)
+
+The driver must provide datasheet_name values for its channels and
+must pass the iio_map structures and a pointer to its own iio_dev structure
+ on to the core via a call to iio_map_array_register. On removal,
+iio_map_array_unregister reverses this process.
+
+The result of this is that the IIO core now has all the information needed
+to associate a given channel with the consumer requesting it.
+
+Acting as an IIO consumer (consumer.h)
+
+The consumer first has to obtain an iio_channel structure from the core
+by calling iio_channel_get(). The correct channel is identified by:
+
+* matching dev or dev_name against consumer_dev and consumer_dev_name
+* matching consumer_channel against consumer_channel in the map
+
+There are then a number of functions that can be used to get information
+about this channel such as it's current reading.
+
+e.g.
+iio_read_channel_raw() - get a reading
+iio_get_channel_type() - get the type of channel
+
+There is also provision for retrieving all of the channels associated
+with a given consumer. This is useful for generic drivers such as
+iio_hwmon where the number and naming of channels is not known by the
+consumer driver. To do this, use iio_channel_get_all.
+
diff --git a/drivers/staging/iio/Documentation/light/sysfs-bus-iio-light-tsl2583 b/drivers/staging/iio/Documentation/light/sysfs-bus-iio-light-tsl2583
new file mode 100644
index 000000000..470f7ad9c
--- /dev/null
+++ b/drivers/staging/iio/Documentation/light/sysfs-bus-iio-light-tsl2583
@@ -0,0 +1,6 @@
+What: /sys/bus/iio/devices/device[n]/in_illuminance0_calibrate
+KernelVersion: 2.6.37
+Contact: linux-iio@vger.kernel.org
+Description:
+ This property causes an internal calibration of the als gain trim
+ value which is later used in calculating illuminance in lux.
diff --git a/drivers/staging/iio/Documentation/light/sysfs-bus-iio-light-tsl2x7x b/drivers/staging/iio/Documentation/light/sysfs-bus-iio-light-tsl2x7x
new file mode 100644
index 000000000..b2798b258
--- /dev/null
+++ b/drivers/staging/iio/Documentation/light/sysfs-bus-iio-light-tsl2x7x
@@ -0,0 +1,13 @@
+What: /sys/bus/iio/devices/device[n]/in_illuminance0_calibrate
+KernelVersion: 3.3-rc1
+Contact: linux-iio@vger.kernel.org
+Description:
+ Causes an internal calibration of the als gain trim
+ value which is later used in calculating illuminance in lux.
+
+What: /sys/bus/iio/devices/device[n]/in_proximity0_calibrate
+KernelVersion: 3.3-rc1
+Contact: linux-iio@vger.kernel.org
+Description:
+ Causes a recalculation and adjustment to the
+ proximity_thresh_rising_value.
diff --git a/drivers/staging/iio/Documentation/overview.txt b/drivers/staging/iio/Documentation/overview.txt
new file mode 100644
index 000000000..43f92b06b
--- /dev/null
+++ b/drivers/staging/iio/Documentation/overview.txt
@@ -0,0 +1,57 @@
+Overview of IIO
+
+The Industrial I/O subsystem is intended to provide support for devices
+that in some sense are analog to digital converters (ADCs). As many
+actual devices combine some ADCs with digital to analog converters
+(DACs) that functionality is also supported.
+
+The aim is to fill the gap between the somewhat similar hwmon and
+input subsystems. Hwmon is very much directed at low sample rate
+sensors used in applications such as fan speed control and temperature
+measurement. Input is, as its name suggests focused on input
+devices. In some cases there is considerable overlap between these and
+IIO.
+
+A typical device falling into this category would be connected via SPI
+or I2C.
+
+Functionality of IIO
+
+* Basic device registration and handling. This is very similar to
+hwmon with simple polled access to device channels via sysfs.
+
+* Event chrdevs. These are similar to input in that they provide a
+route to user space for hardware triggered events. Such events include
+threshold detectors, free-fall detectors and more complex action
+detection. The events themselves are currently very simple with
+merely an event code and a timestamp. Any data associated with the
+event must be accessed via polling.
+
+Note: A given device may have one or more event channel. These events are
+turned on or off (if possible) via sysfs interfaces.
+
+* Hardware buffer support. Some recent sensors have included
+fifo / ring buffers on the sensor chip. These greatly reduce the load
+on the host CPU by buffering relatively large numbers of data samples
+based on an internal sampling clock. Examples include VTI SCA3000
+series and Analog Device ADXL345 accelerometers. Each buffer supports
+polling to establish when data is available.
+
+* Trigger and software buffer support. In many data analysis
+applications it it useful to be able to capture data based on some
+external signal (trigger). These triggers might be a data ready
+signal, a gpio line connected to some external system or an on
+processor periodic interrupt. A single trigger may initialize data
+capture or reading from a number of sensors. These triggers are
+used in IIO to fill software buffers acting in a very similar
+fashion to the hardware buffers described above.
+
+Other documentation:
+
+device.txt - elements of a typical device driver.
+
+trigger.txt - elements of a typical trigger driver.
+
+ring.txt - additional elements required for buffer support.
+
+sysfs-bus-iio - abi documentation file.
diff --git a/drivers/staging/iio/Documentation/ring.txt b/drivers/staging/iio/Documentation/ring.txt
new file mode 100644
index 000000000..18718fcaf
--- /dev/null
+++ b/drivers/staging/iio/Documentation/ring.txt
@@ -0,0 +1,47 @@
+Buffer support within IIO
+
+This document is intended as a general overview of the functionality
+a buffer may supply and how it is specified within IIO. For more
+specific information on a given buffer implementation, see the
+comments in the source code. Note that some drivers allow buffer
+implementation to be selected at compile time via Kconfig options.
+
+A given buffer implementation typically embeds a struct
+iio_ring_buffer and it is a pointer to this that is provided to the
+IIO core. Access to the embedding structure is typically done via
+container_of functions.
+
+struct iio_ring_buffer contains a struct iio_ring_setup_ops *setup_ops
+which in turn contains the 4 function pointers
+(preenable, postenable, predisable and postdisable).
+These are used to perform device specific steps on either side
+of the core changing its current mode to indicate that the buffer
+is enabled or disabled (along with enabling triggering etc. as appropriate).
+
+Also in struct iio_ring_buffer is a struct iio_ring_access_funcs.
+The function pointers within here are used to allow the core to handle
+as much buffer functionality as possible. Note almost all of these
+are optional.
+
+store_to
+ If possible, push data to the buffer.
+
+read_last
+ If possible, get the most recent scan from the buffer (without removal).
+ This provides polling like functionality whilst the ring buffering is in
+ use without a separate read from the device.
+
+rip_first_n
+ The primary buffer reading function. Note that it may well not return
+ as much data as requested.
+
+request_update
+ If parameters have changed that require reinitialization or configuration of
+ the buffer this will trigger it.
+
+set_bytes_per_datum
+ Set the number of bytes for a complete scan. (All samples + timestamp)
+
+set_length
+ Set the number of complete scans that may be held by the buffer.
+
diff --git a/drivers/staging/iio/Documentation/sysfs-bus-iio-ad7192 b/drivers/staging/iio/Documentation/sysfs-bus-iio-ad7192
new file mode 100644
index 000000000..1c35c507c
--- /dev/null
+++ b/drivers/staging/iio/Documentation/sysfs-bus-iio-ad7192
@@ -0,0 +1,20 @@
+What: /sys/.../iio:deviceX/ac_excitation_en
+KernelVersion: 3.1.0
+Contact: linux-iio@vger.kernel.org
+Description:
+ This attribute, if available, is used to enable the AC
+ excitation mode found on some converters. In ac excitation mode,
+ the polarity of the excitation voltage is reversed on
+ alternate cycles, to eliminate DC errors.
+
+What: /sys/.../iio:deviceX/bridge_switch_en
+KernelVersion: 3.1.0
+Contact: linux-iio@vger.kernel.org
+Description:
+ This attribute, if available, is used to close or open the
+ bridge power down switch found on some converters.
+ In bridge applications, such as strain gauges and load cells,
+ the bridge itself consumes the majority of the current in the
+ system. To minimize the current consumption of the system,
+ the bridge can be disconnected (when it is not being used
+ using the bridge_switch_en attribute.
diff --git a/drivers/staging/iio/Documentation/sysfs-bus-iio-adc-ad7280a b/drivers/staging/iio/Documentation/sysfs-bus-iio-adc-ad7280a
new file mode 100644
index 000000000..863d38567
--- /dev/null
+++ b/drivers/staging/iio/Documentation/sysfs-bus-iio-adc-ad7280a
@@ -0,0 +1,21 @@
+What: /sys/bus/iio/devices/deviceX/inY-inZ_balance_switch_en
+KernelVersion: 3.0.0
+Contact: linux-iio@vger.kernel.org
+Description:
+ Writing 1 enables the cell balance output switch corresponding
+ to input Y. Writing 0 disables it. If the inY-inZ_balance_timer
+ is set to a none zero value, the corresponding switch will
+ enable for the programmed amount of time, before it
+ automatically disables.
+
+What: /sys/bus/iio/devices/deviceX/inY-inZ_balance_timer
+KernelVersion: 3.0.0
+Contact: linux-iio@vger.kernel.org
+Description:
+ The inY-inZ_balance_timer file allows the user to program
+ individual times for each cell balance output. The AD7280A
+ allows the user to set the timer to a value from 0 minutes to
+ 36.9 minutes. The resolution of the timer is 71.5 sec.
+ The value written is the on-time in milliseconds. When the
+ timer value is set 0, the timer is disabled. The cell balance
+ outputs are controlled only by inY-inZ_balance_switch_en.
diff --git a/drivers/staging/iio/Documentation/sysfs-bus-iio-dds b/drivers/staging/iio/Documentation/sysfs-bus-iio-dds
new file mode 100644
index 000000000..ee8c509c6
--- /dev/null
+++ b/drivers/staging/iio/Documentation/sysfs-bus-iio-dds
@@ -0,0 +1,96 @@
+
+What: /sys/bus/iio/devices/.../out_altvoltageX_frequencyY
+KernelVersion: 2.6.37
+Contact: linux-iio@vger.kernel.org
+Description:
+ Stores frequency into tuning word Y.
+ There will be more than one out_altvoltageX_frequencyY file,
+ which allows for pin controlled FSK Frequency Shift Keying
+ (out_altvoltageX_pincontrol_frequency_en is active) or the user
+ can control the desired active tuning word by writing Y to the
+ out_altvoltageX_frequencysymbol file.
+
+What: /sys/bus/iio/devices/.../out_altvoltageX_frequencyY_scale
+KernelVersion: 2.6.37
+Contact: linux-iio@vger.kernel.org
+Description:
+ Scale to be applied to out_altvoltageX_frequencyY in order to
+ obtain the desired value in Hz. If shared across all frequency
+ registers Y is not present. It is also possible X is not present
+ if shared across all channels.
+
+What: /sys/bus/iio/devices/.../out_altvoltageX_frequencysymbol
+KernelVersion: 2.6.37
+Contact: linux-iio@vger.kernel.org
+Description:
+ Specifies the active output frequency tuning word. The value
+ corresponds to the Y in out_altvoltageX_frequencyY.
+ To exit this mode the user can write
+ out_altvoltageX_pincontrol_frequency_en or
+ out_altvoltageX_out_enable file.
+
+What: /sys/bus/iio/devices/.../out_altvoltageX_phaseY
+KernelVersion: 2.6.37
+Contact: linux-iio@vger.kernel.org
+Description:
+ Stores phase into Y.
+ There will be more than one out_altvoltageX_phaseY file, which
+ allows for pin controlled PSK Phase Shift Keying
+ (out_altvoltageX_pincontrol_phase_en is active) or the user can
+ control the desired phase Y which is added to the phase
+ accumulator output by writing Y to the phase_en file.
+
+What: /sys/bus/iio/devices/.../out_altvoltageX_phaseY_scale
+KernelVersion: 2.6.37
+Contact: linux-iio@vger.kernel.org
+Description:
+ Scale to be applied to out_altvoltageX_phaseY in order to obtain
+ the desired value in rad. If shared across all phase registers
+ Y is not present. It is also possible X is not present if
+ shared across all channels.
+
+What: /sys/bus/iio/devices/.../out_altvoltageX_phasesymbol
+KernelVersion: 2.6.37
+Contact: linux-iio@vger.kernel.org
+Description:
+ Specifies the active phase Y which is added to the phase
+ accumulator output. The value corresponds to the Y in
+ out_altvoltageX_phaseY. To exit this mode the user can write
+ out_altvoltageX_pincontrol_phase_en or disable file.
+
+What: /sys/bus/iio/devices/.../out_altvoltageX_pincontrol_en
+What: /sys/bus/iio/devices/.../out_altvoltageX_pincontrol_frequency_en
+What: /sys/bus/iio/devices/.../out_altvoltageX_pincontrol_phase_en
+KernelVersion: 2.6.37
+Contact: linux-iio@vger.kernel.org
+Description:
+ out_altvoltageX_pincontrol_en: Both, the active frequency and
+ phase is controlled by the respective phase and frequency
+ control inputs. In case the device in features independent
+ controls, then there are dedicated files
+ (out_altvoltageX_pincontrol_frequency_en,
+ out_altvoltageX_pincontrol_phase_en).
+
+What: /sys/bus/iio/devices/.../out_altvoltageX_out_enable
+What: /sys/bus/iio/devices/.../out_altvoltageX_outY_enable
+KernelVersion: 2.6.37
+Contact: linux-iio@vger.kernel.org
+Description:
+ out_altvoltageX_outY_enable controls signal generation on
+ output Y of channel X. Y may be suppressed if all channels are
+ controlled together.
+
+What: /sys/bus/iio/devices/.../out_altvoltageX_outY_wavetype
+KernelVersion: 2.6.37
+Contact: linux-iio@vger.kernel.org
+Description:
+ Specifies the output waveform.
+ (sine, triangle, ramp, square, ...)
+ For a list of available output waveform options read
+ available_output_modes.
+
+What: /sys/bus/iio/devices/.../out_altvoltageX_outY_wavetype_available
+KernelVersion: 2.6.37
+Contact: linux-iio@vger.kernel.org
+Description:
+ Lists all available output waveform options.
diff --git a/drivers/staging/iio/Documentation/sysfs-bus-iio-impedance-analyzer-ad5933 b/drivers/staging/iio/Documentation/sysfs-bus-iio-impedance-analyzer-ad5933
new file mode 100644
index 000000000..79c7e88c6
--- /dev/null
+++ b/drivers/staging/iio/Documentation/sysfs-bus-iio-impedance-analyzer-ad5933
@@ -0,0 +1,30 @@
+What: /sys/bus/iio/devices/iio:deviceX/outY_freq_start
+KernelVersion: 3.1.0
+Contact: linux-iio@vger.kernel.org
+Description:
+ Frequency sweep start frequency in Hz.
+
+What: /sys/bus/iio/devices/iio:deviceX/outY_freq_increment
+KernelVersion: 3.1.0
+Contact: linux-iio@vger.kernel.org
+Description:
+ Frequency increment in Hz (step size) between consecutive
+ frequency points along the sweep.
+
+What: /sys/bus/iio/devices/iio:deviceX/outY_freq_points
+KernelVersion: 3.1.0
+Contact: linux-iio@vger.kernel.org
+Description:
+ Number of frequency points (steps) in the frequency sweep.
+ This value, in conjunction with the outY_freq_start and the
+ outY_freq_increment, determines the frequency sweep range
+ for the sweep operation.
+
+What: /sys/bus/iio/devices/iio:deviceX/outY_settling_cycles
+KernelVersion: 3.1.0
+Contact: linux-iio@vger.kernel.org
+Description:
+ Number of output excitation cycles (settling time cycles)
+ that are allowed to pass through the unknown impedance,
+ after each frequency increment, and before the ADC is triggered
+ to perform a conversion sequence of the response signal.
diff --git a/drivers/staging/iio/Documentation/sysfs-bus-iio-light b/drivers/staging/iio/Documentation/sysfs-bus-iio-light
new file mode 100644
index 000000000..17e5c9c51
--- /dev/null
+++ b/drivers/staging/iio/Documentation/sysfs-bus-iio-light
@@ -0,0 +1,107 @@
+
+What: /sys/bus/iio/devices/device[n]/range
+KernelVersion: 2.6.37
+Contact: linux-iio@vger.kernel.org
+Description:
+ Hardware dependent ADC Full Scale Range used for some ambient
+ light sensors in calculating lux.
+
+What: /sys/bus/iio/devices/device[n]/range_available
+KernelVersion: 2.6.37
+Contact: linux-iio@vger.kernel.org
+Description:
+ Hardware dependent supported vales for ADC Full Scale Range.
+
+What: /sys/bus/iio/devices/device[n]/adc_resolution
+KernelVersion: 2.6.37
+Contact: linux-iio@vger.kernel.org
+Description:
+ Hardware dependent ADC resolution of the ambient light sensor
+ used in calculating the lux.
+
+What: /sys/bus/iio/devices/device[n]/adc_resolution_available
+KernelVersion: 2.6.37
+Contact: linux-iio@vger.kernel.org
+Description:
+ Hardware dependent list of possible values supported for the
+ adc_resolution of the given sensor.
+
+What: /sys/bus/iio/devices/device[n]/in_illuminance0[_input|_raw]
+KernelVersion: 2.6.35
+Contact: linux-iio@vger.kernel.org
+Description:
+ This should return the calculated lux from the light sensor. If
+ it comes back in SI units, it should also include _input else it
+ should include _raw to signify it is not in SI units.
+
+What: /sys/.../device[n]/proximity_on_chip_ambient_infrared_suppression
+KernelVersion: 2.6.37
+Contact: linux-iio@vger.kernel.org
+Description:
+ Hardware dependent mode for an ALS device to calculate the value
+ in proximity mode. When this is enabled, then the device should
+ use a infrared sensor reading to remove infrared noise from the
+ proximity reading. If this is not enabled, the driver can still
+ do this calculation manually by reading the infrared sensor
+ value and doing the negation in sw.
+
+What: /sys/bus/iio/devices/device[n]/in_proximity[_input|_raw]
+KernelVersion: 2.6.37
+Contact: linux-iio@vger.kernel.org
+Description:
+ This property is supported by proximity sensors and should be
+ used to return the value of a reading by the sensor. If this
+ value is returned in SI units, it should also include _input
+ but if it is not, then it should include _raw.
+
+What: /sys/bus/iio/devices/device[n]/intensity_infrared[_input|_raw]
+KernelVersion: 2.6.37
+Contact: linux-iio@vger.kernel.org
+Description:
+ This property is supported by sensors that have an infrared
+ sensing mode. This value should be the output from a reading
+ and if expressed in SI units, should include _input. If this
+ value is not in SI units, then it should include _raw.
+
+What: /sys/bus/iio/devices/device[n]/in_illuminance0_target
+KernelVersion: 2.6.37
+Contact: linux-iio@vger.kernel.org
+Description:
+ This property gets/sets the last known external
+ lux measurement used in/for calibration.
+
+What: /sys/bus/iio/devices/device[n]/in_illuminance0_integration_time
+KernelVersion: 2.6.37
+Contact: linux-iio@vger.kernel.org
+Description:
+ This property gets/sets the sensors ADC analog integration time.
+
+What: /sys/bus/iio/devices/device[n]/in_illuminance0_lux_table
+KernelVersion: 2.6.37
+Contact: linux-iio@vger.kernel.org
+Description:
+ This property gets/sets the table of coefficients
+ used in calculating illuminance in lux.
+
+What: /sys/bus/iio/devices/device[n]/in_intensity_clear[_input|_raw]
+What: /sys/bus/iio/devices/device[n]/in_intensity_red[_input|_raw]
+What: /sys/bus/iio/devices/device[n]/in_intensity_green[_input|_raw]
+What: /sys/bus/iio/devices/device[n]/in_intensity_blue[_input|_raw]
+KernelVersion: 3.6.0
+Contact: linux-iio@vger.kernel.org
+Description:
+ This property is supported by sensors that have a RGBC
+ sensing mode. This value should be the output from a reading
+ and if expressed in SI units, should include _input. If this
+ value is not in SI units (irradiance, uW/mm^2), then it should
+ include _raw.
+
+What: /sys/bus/iio/devices/device[n]/in_cct0[_input|_raw]
+KernelVersion: 3.6.0
+Contact: linux-iio@vger.kernel.org
+Description:
+ This should return the correlated color temperature from the
+ light sensor. If it comes back in SI units, it should also
+ include _input else it should include _raw to signify it is not
+ in SI units.
+
diff --git a/drivers/staging/iio/Documentation/sysfs-bus-iio-light-tsl2583 b/drivers/staging/iio/Documentation/sysfs-bus-iio-light-tsl2583
new file mode 100644
index 000000000..660781df4
--- /dev/null
+++ b/drivers/staging/iio/Documentation/sysfs-bus-iio-light-tsl2583
@@ -0,0 +1,20 @@
+What: /sys/bus/iio/devices/device[n]/lux_table
+KernelVersion: 2.6.37
+Contact: linux-iio@vger.kernel.org
+Description:
+ This property gets/sets the table of coefficients
+ used in calculating illuminance in lux.
+
+What: /sys/bus/iio/devices/device[n]/illuminance0_calibrate
+KernelVersion: 2.6.37
+Contact: linux-iio@vger.kernel.org
+Description:
+ This property causes an internal calibration of the als gain trim
+ value which is later used in calculating illuminance in lux.
+
+What: /sys/bus/iio/devices/device[n]/illuminance0_input_target
+KernelVersion: 2.6.37
+Contact: linux-iio@vger.kernel.org
+Description:
+ This property is the known externally illuminance (in lux).
+ It is used in the process of calibrating the device accuracy.
diff --git a/drivers/staging/iio/Documentation/trigger.txt b/drivers/staging/iio/Documentation/trigger.txt
new file mode 100644
index 000000000..7c0e505e4
--- /dev/null
+++ b/drivers/staging/iio/Documentation/trigger.txt
@@ -0,0 +1,35 @@
+IIO trigger drivers.
+
+Many triggers are provided by hardware that will also be registered as
+an IIO device. Whilst this can create device specific complexities
+such triggers are registered with the core in the same way as
+stand-alone triggers.
+
+struct iio_trig *trig = iio_trigger_alloc("<trigger format string>", ...);
+
+allocates a trigger structure. The key elements to then fill in within
+a driver are:
+
+trig->owner
+ Typically set to THIS_MODULE. Used to ensure correct
+ ownership of core allocated resources.
+
+trig->set_trigger_state:
+ Function that enables / disables the underlying source of the trigger.
+
+There is also a
+trig->alloc_list which is useful for drivers that allocate multiple
+triggers to keep track of what they have created.
+
+When these have been set call:
+
+iio_trigger_register(trig);
+
+to register the trigger with the core, making it available to trigger
+consumers.
+
+Trigger Consumers
+
+Currently triggers are only used for the filling of software
+buffers and as such any device supporting INDIO_BUFFER_TRIGGERED has the
+consumer interface automatically created.
diff --git a/drivers/staging/iio/Kconfig b/drivers/staging/iio/Kconfig
new file mode 100644
index 000000000..6d5b38d69
--- /dev/null
+++ b/drivers/staging/iio/Kconfig
@@ -0,0 +1,48 @@
+#
+# Industrial I/O subsystem configuration
+#
+menu "IIO staging drivers"
+ depends on IIO
+
+source "drivers/staging/iio/accel/Kconfig"
+source "drivers/staging/iio/adc/Kconfig"
+source "drivers/staging/iio/addac/Kconfig"
+source "drivers/staging/iio/cdc/Kconfig"
+source "drivers/staging/iio/frequency/Kconfig"
+source "drivers/staging/iio/gyro/Kconfig"
+source "drivers/staging/iio/impedance-analyzer/Kconfig"
+source "drivers/staging/iio/light/Kconfig"
+source "drivers/staging/iio/magnetometer/Kconfig"
+source "drivers/staging/iio/meter/Kconfig"
+source "drivers/staging/iio/resolver/Kconfig"
+source "drivers/staging/iio/trigger/Kconfig"
+
+config IIO_DUMMY_EVGEN
+ tristate
+
+config IIO_SIMPLE_DUMMY
+ tristate "An example driver with no hardware requirements"
+ help
+ Driver intended mainly as documentation for how to write
+ a driver. May also be useful for testing userspace code
+ without hardware.
+
+if IIO_SIMPLE_DUMMY
+
+config IIO_SIMPLE_DUMMY_EVENTS
+ bool "Event generation support"
+ select IIO_DUMMY_EVGEN
+ help
+ Add some dummy events to the simple dummy driver.
+
+config IIO_SIMPLE_DUMMY_BUFFER
+ bool "Buffered capture support"
+ select IIO_BUFFER
+ select IIO_TRIGGER
+ select IIO_KFIFO_BUF
+ help
+ Add buffered data capture to the simple dummy driver.
+
+endif # IIO_SIMPLE_DUMMY
+
+endmenu
diff --git a/drivers/staging/iio/Makefile b/drivers/staging/iio/Makefile
new file mode 100644
index 000000000..d87106135
--- /dev/null
+++ b/drivers/staging/iio/Makefile
@@ -0,0 +1,23 @@
+#
+# Makefile for the industrial I/O core.
+#
+
+obj-$(CONFIG_IIO_SIMPLE_DUMMY) += iio_dummy.o
+iio_dummy-y := iio_simple_dummy.o
+iio_dummy-$(CONFIG_IIO_SIMPLE_DUMMY_EVENTS) += iio_simple_dummy_events.o
+iio_dummy-$(CONFIG_IIO_SIMPLE_DUMMY_BUFFER) += iio_simple_dummy_buffer.o
+
+obj-$(CONFIG_IIO_DUMMY_EVGEN) += iio_dummy_evgen.o
+
+obj-y += accel/
+obj-y += adc/
+obj-y += addac/
+obj-y += cdc/
+obj-y += frequency/
+obj-y += gyro/
+obj-y += impedance-analyzer/
+obj-y += light/
+obj-y += magnetometer/
+obj-y += meter/
+obj-y += resolver/
+obj-y += trigger/
diff --git a/drivers/staging/iio/TODO b/drivers/staging/iio/TODO
new file mode 100644
index 000000000..c22a0edd1
--- /dev/null
+++ b/drivers/staging/iio/TODO
@@ -0,0 +1,84 @@
+2009 8/18
+
+Core:
+1) Get reviews
+2) Additional testing
+3) Ensure all desirable features present by adding more devices.
+ Major changes not expected except in response to comments
+
+Max1363 core:
+1) Possibly add sysfs exports of constant useful to userspace.
+Would be nice
+2) Support hardware generated interrupts
+3) Expand device set. Lots of other maxim adc's have very
+ similar interfaces.
+
+MXS LRADC driver:
+This is a classic MFD device as it combines the following subdevices
+ - touchscreen controller (input subsystem related device)
+ - general purpose ADC channels
+ - battery voltage monitor (power subsystem related device)
+ - die temperature monitor (thermal management)
+
+At least the battery voltage and die temperature feature is required in-kernel
+by a driver of the SoC's battery charging unit to avoid any damage to the
+silicon and the battery.
+
+TSL2561
+Would be nice
+1) Open question of userspace vs kernel space balance when
+converting to useful light measurements from device ones.
+2) Add sysfs elements necessary to allow device agnostic
+unit conversion.
+
+LIS3L02DQ core
+
+LIS3L02DQ ring
+
+KXSD9
+Currently minimal driver, would be nice to add:
+1) Support for all chip generated interrupts (events),
+basically get support up to level of lis3l02dq driver.
+
+Ring buffer core
+
+SCA3000
+Would be nice
+1) Testing on devices other than sca3000-e05
+
+Trigger core support
+1) Discussion of approach. Is it general enough?
+
+Ring Buffer:
+1) Discussion of approach.
+There are probably better ways of doing this. The
+intention is to allow for more than one software ring
+buffer implementation as different users will have
+different requirements. This one suits mid range
+frequencies (100Hz - 4kHz).
+2) Lots of testing
+
+Periodic Timer trigger
+1) Move to a more general hardware periodic timer request
+subsystem. Current approach is abusing purpose of RTC.
+Initial discussions have taken place, but no actual code
+is in place as yet. This topic will be reopened on lkml
+shortly. I don't really envision this patch being merged
+in anything like its current form.
+
+GPIO trigger
+1) Add control over the type of interrupt etc. This will
+necessitate a header that is also visible from arch board
+files. (avoided at the moment to keep the driver set
+contained in staging).
+
+ADI Drivers:
+CC the device-drivers-devel@blackfin.uclinux.org mailing list when
+e-mailing the normal IIO list (see below).
+
+Documentation
+1) Lots of cleanup and expansion.
+2) Some device require individual docs.
+
+Contact: Jonathan Cameron <jic23@kernel.org>.
+Mailing list: linux-iio@vger.kernel.org
diff --git a/drivers/staging/iio/accel/Kconfig b/drivers/staging/iio/accel/Kconfig
new file mode 100644
index 000000000..07b7ffa00
--- /dev/null
+++ b/drivers/staging/iio/accel/Kconfig
@@ -0,0 +1,101 @@
+#
+# Accelerometer drivers
+#
+menu "Accelerometers"
+
+config ADIS16201
+ tristate "Analog Devices ADIS16201 Dual-Axis Digital Inclinometer and Accelerometer"
+ depends on SPI
+ select IIO_ADIS_LIB
+ select IIO_ADIS_LIB_BUFFER if IIO_BUFFER
+ help
+ Say Y here to build support for Analog Devices adis16201 dual-axis
+ digital inclinometer and accelerometer.
+
+ To compile this driver as a module, say M here: the module will
+ be called adis16201.
+
+config ADIS16203
+ tristate "Analog Devices ADIS16203 Programmable 360 Degrees Inclinometer"
+ depends on SPI
+ select IIO_ADIS_LIB
+ select IIO_ADIS_LIB_BUFFER if IIO_BUFFER
+ help
+ Say Y here to build support for Analog Devices adis16203 Programmable
+ 360 Degrees Inclinometer.
+
+ To compile this driver as a module, say M here: the module will be
+ called adis16203.
+
+config ADIS16204
+ tristate "Analog Devices ADIS16204 Programmable High-g Digital Impact Sensor and Recorder"
+ depends on SPI
+ select IIO_ADIS_LIB
+ select IIO_ADIS_LIB_BUFFER if IIO_BUFFER
+ help
+ Say Y here to build support for Analog Devices adis16204 Programmable
+ High-g Digital Impact Sensor and Recorder.
+
+ To compile this driver as a module, say M here: the module will be
+ called adis16204.
+
+config ADIS16209
+ tristate "Analog Devices ADIS16209 Dual-Axis Digital Inclinometer and Accelerometer"
+ depends on SPI
+ select IIO_ADIS_LIB
+ select IIO_ADIS_LIB_BUFFER if IIO_BUFFER
+ help
+ Say Y here to build support for Analog Devices adis16209 dual-axis digital inclinometer
+ and accelerometer.
+
+ To compile this driver as a module, say M here: the module will be
+ called adis16209.
+
+config ADIS16220
+ tristate "Analog Devices ADIS16220 Programmable Digital Vibration Sensor"
+ depends on SPI
+ select IIO_ADIS_LIB
+ help
+ Say Y here to build support for Analog Devices adis16220 programmable
+ digital vibration sensor.
+
+ To compile this driver as a module, say M here: the module will be
+ called adis16220.
+
+config ADIS16240
+ tristate "Analog Devices ADIS16240 Programmable Impact Sensor and Recorder"
+ depends on SPI
+ select IIO_ADIS_LIB
+ select IIO_ADIS_LIB_BUFFER if IIO_BUFFER
+ help
+ Say Y here to build support for Analog Devices adis16240 programmable
+ impact Sensor and recorder.
+
+ To compile this driver as a module, say M here: the module will be
+ called adis16240.
+
+config LIS3L02DQ
+ tristate "ST Microelectronics LIS3L02DQ Accelerometer Driver"
+ depends on SPI
+ select IIO_TRIGGER if IIO_BUFFER
+ depends on !IIO_BUFFER || IIO_KFIFO_BUF
+ depends on GPIOLIB
+ help
+ Say Y here to build SPI support for the ST microelectronics
+ accelerometer. The driver supplies direct access via sysfs files
+ and an event interface via a character device.
+
+ To compile this driver as a module, say M here: the module will be
+ called lis3l02dq.
+
+config SCA3000
+ depends on IIO_BUFFER
+ depends on SPI
+ tristate "VTI SCA3000 series accelerometers"
+ help
+ Say Y here to build support for the VTI SCA3000 series of SPI
+ accelerometers. These devices use a hardware ring buffer.
+
+ To compile this driver as a module, say M here: the module will be
+ called sca3000.
+endmenu
diff --git a/drivers/staging/iio/accel/Makefile b/drivers/staging/iio/accel/Makefile
new file mode 100644
index 000000000..1ed137f1a
--- /dev/null
+++ b/drivers/staging/iio/accel/Makefile
@@ -0,0 +1,28 @@
+#
+# Makefile for industrial I/O accelerometer drivers
+#
+
+adis16201-y := adis16201_core.o
+obj-$(CONFIG_ADIS16201) += adis16201.o
+
+adis16203-y := adis16203_core.o
+obj-$(CONFIG_ADIS16203) += adis16203.o
+
+adis16204-y := adis16204_core.o
+obj-$(CONFIG_ADIS16204) += adis16204.o
+
+adis16209-y := adis16209_core.o
+obj-$(CONFIG_ADIS16209) += adis16209.o
+
+adis16220-y := adis16220_core.o
+obj-$(CONFIG_ADIS16220) += adis16220.o
+
+adis16240-y := adis16240_core.o
+obj-$(CONFIG_ADIS16240) += adis16240.o
+
+lis3l02dq-y := lis3l02dq_core.o
+lis3l02dq-$(CONFIG_IIO_BUFFER) += lis3l02dq_ring.o
+obj-$(CONFIG_LIS3L02DQ) += lis3l02dq.o
+
+sca3000-y := sca3000_core.o sca3000_ring.o
+obj-$(CONFIG_SCA3000) += sca3000.o
diff --git a/drivers/staging/iio/accel/adis16201.h b/drivers/staging/iio/accel/adis16201.h
new file mode 100644
index 000000000..e6b8c9af6
--- /dev/null
+++ b/drivers/staging/iio/accel/adis16201.h
@@ -0,0 +1,66 @@
+#ifndef SPI_ADIS16201_H_
+#define SPI_ADIS16201_H_
+
+#define ADIS16201_STARTUP_DELAY 220 /* ms */
+
+#define ADIS16201_FLASH_CNT 0x00 /* Flash memory write count */
+#define ADIS16201_SUPPLY_OUT 0x02 /* Output, power supply */
+#define ADIS16201_XACCL_OUT 0x04 /* Output, x-axis accelerometer */
+#define ADIS16201_YACCL_OUT 0x06 /* Output, y-axis accelerometer */
+#define ADIS16201_AUX_ADC 0x08 /* Output, auxiliary ADC input */
+#define ADIS16201_TEMP_OUT 0x0A /* Output, temperature */
+#define ADIS16201_XINCL_OUT 0x0C /* Output, x-axis inclination */
+#define ADIS16201_YINCL_OUT 0x0E /* Output, y-axis inclination */
+#define ADIS16201_XACCL_OFFS 0x10 /* Calibration, x-axis acceleration offset */
+#define ADIS16201_YACCL_OFFS 0x12 /* Calibration, y-axis acceleration offset */
+#define ADIS16201_XACCL_SCALE 0x14 /* x-axis acceleration scale factor */
+#define ADIS16201_YACCL_SCALE 0x16 /* y-axis acceleration scale factor */
+#define ADIS16201_XINCL_OFFS 0x18 /* Calibration, x-axis inclination offset */
+#define ADIS16201_YINCL_OFFS 0x1A /* Calibration, y-axis inclination offset */
+#define ADIS16201_XINCL_SCALE 0x1C /* x-axis inclination scale factor */
+#define ADIS16201_YINCL_SCALE 0x1E /* y-axis inclination scale factor */
+#define ADIS16201_ALM_MAG1 0x20 /* Alarm 1 amplitude threshold */
+#define ADIS16201_ALM_MAG2 0x22 /* Alarm 2 amplitude threshold */
+#define ADIS16201_ALM_SMPL1 0x24 /* Alarm 1, sample period */
+#define ADIS16201_ALM_SMPL2 0x26 /* Alarm 2, sample period */
+#define ADIS16201_ALM_CTRL 0x28 /* Alarm control */
+#define ADIS16201_AUX_DAC 0x30 /* Auxiliary DAC data */
+#define ADIS16201_GPIO_CTRL 0x32 /* General-purpose digital input/output control */
+#define ADIS16201_MSC_CTRL 0x34 /* Miscellaneous control */
+#define ADIS16201_SMPL_PRD 0x36 /* Internal sample period (rate) control */
+#define ADIS16201_AVG_CNT 0x38 /* Operation, filter configuration */
+#define ADIS16201_SLP_CNT 0x3A /* Operation, sleep mode control */
+#define ADIS16201_DIAG_STAT 0x3C /* Diagnostics, system status register */
+#define ADIS16201_GLOB_CMD 0x3E /* Operation, system command register */
+
+/* MSC_CTRL */
+#define ADIS16201_MSC_CTRL_SELF_TEST_EN BIT(8) /* Self-test enable */
+#define ADIS16201_MSC_CTRL_DATA_RDY_EN BIT(2) /* Data-ready enable: 1 = enabled, 0 = disabled */
+#define ADIS16201_MSC_CTRL_ACTIVE_HIGH BIT(1) /* Data-ready polarity: 1 = active high, 0 = active low */
+#define ADIS16201_MSC_CTRL_DATA_RDY_DIO1 BIT(0) /* Data-ready line selection: 1 = DIO1, 0 = DIO0 */
+
+/* DIAG_STAT */
+#define ADIS16201_DIAG_STAT_ALARM2 BIT(9) /* Alarm 2 status: 1 = alarm active, 0 = alarm inactive */
+#define ADIS16201_DIAG_STAT_ALARM1 BIT(8) /* Alarm 1 status: 1 = alarm active, 0 = alarm inactive */
+#define ADIS16201_DIAG_STAT_SPI_FAIL_BIT 3 /* SPI communications failure */
+#define ADIS16201_DIAG_STAT_FLASH_UPT_BIT 2 /* Flash update failure */
+#define ADIS16201_DIAG_STAT_POWER_HIGH_BIT 1 /* Power supply above 3.625 V */
+#define ADIS16201_DIAG_STAT_POWER_LOW_BIT 0 /* Power supply below 3.15 V */
+
+/* GLOB_CMD */
+#define ADIS16201_GLOB_CMD_SW_RESET BIT(7)
+#define ADIS16201_GLOB_CMD_FACTORY_CAL BIT(1)
+
+#define ADIS16201_ERROR_ACTIVE BIT(14)
+
+enum adis16201_scan {
+ ADIS16201_SCAN_ACC_X,
+ ADIS16201_SCAN_ACC_Y,
+ ADIS16201_SCAN_INCLI_X,
+ ADIS16201_SCAN_INCLI_Y,
+ ADIS16201_SCAN_SUPPLY,
+ ADIS16201_SCAN_AUX_ADC,
+ ADIS16201_SCAN_TEMP,
+};
+
+#endif /* SPI_ADIS16201_H_ */
diff --git a/drivers/staging/iio/accel/adis16201_core.c b/drivers/staging/iio/accel/adis16201_core.c
new file mode 100644
index 000000000..10db68581
--- /dev/null
+++ b/drivers/staging/iio/accel/adis16201_core.c
@@ -0,0 +1,248 @@
+/*
+ * ADIS16201 Programmable Digital Vibration Sensor driver
+ *
+ * Copyright 2010 Analog Devices Inc.
+ *
+ * Licensed under the GPL-2 or later.
+ */
+
+#include <linux/delay.h>
+#include <linux/mutex.h>
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/spi/spi.h>
+#include <linux/slab.h>
+#include <linux/sysfs.h>
+#include <linux/module.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/imu/adis.h>
+
+#include "adis16201.h"
+
+static const u8 adis16201_addresses[] = {
+ [ADIS16201_SCAN_ACC_X] = ADIS16201_XACCL_OFFS,
+ [ADIS16201_SCAN_ACC_Y] = ADIS16201_YACCL_OFFS,
+ [ADIS16201_SCAN_INCLI_X] = ADIS16201_XINCL_OFFS,
+ [ADIS16201_SCAN_INCLI_Y] = ADIS16201_YINCL_OFFS,
+};
+
+static int adis16201_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2,
+ long mask)
+{
+ struct adis *st = iio_priv(indio_dev);
+ int ret;
+ int bits;
+ u8 addr;
+ s16 val16;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ return adis_single_conversion(indio_dev, chan,
+ ADIS16201_ERROR_ACTIVE, val);
+ case IIO_CHAN_INFO_SCALE:
+ switch (chan->type) {
+ case IIO_VOLTAGE:
+ if (chan->channel == 0) {
+ *val = 1;
+ *val2 = 220000; /* 1.22 mV */
+ } else {
+ *val = 0;
+ *val2 = 610000; /* 0.610 mV */
+ }
+ return IIO_VAL_INT_PLUS_MICRO;
+ case IIO_TEMP:
+ *val = -470; /* 0.47 C */
+ *val2 = 0;
+ return IIO_VAL_INT_PLUS_MICRO;
+ case IIO_ACCEL:
+ *val = 0;
+ *val2 = IIO_G_TO_M_S_2(462400); /* 0.4624 mg */
+ return IIO_VAL_INT_PLUS_NANO;
+ case IIO_INCLI:
+ *val = 0;
+ *val2 = 100000; /* 0.1 degree */
+ return IIO_VAL_INT_PLUS_MICRO;
+ default:
+ return -EINVAL;
+ }
+ break;
+ case IIO_CHAN_INFO_OFFSET:
+ *val = 25000 / -470 - 1278; /* 25 C = 1278 */
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_CALIBBIAS:
+ switch (chan->type) {
+ case IIO_ACCEL:
+ bits = 12;
+ break;
+ case IIO_INCLI:
+ bits = 9;
+ break;
+ default:
+ return -EINVAL;
+ }
+ mutex_lock(&indio_dev->mlock);
+ addr = adis16201_addresses[chan->scan_index];
+ ret = adis_read_reg_16(st, addr, &val16);
+ if (ret) {
+ mutex_unlock(&indio_dev->mlock);
+ return ret;
+ }
+ val16 &= (1 << bits) - 1;
+ val16 = (s16)(val16 << (16 - bits)) >> (16 - bits);
+ *val = val16;
+ mutex_unlock(&indio_dev->mlock);
+ return IIO_VAL_INT;
+ }
+ return -EINVAL;
+}
+
+static int adis16201_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int val,
+ int val2,
+ long mask)
+{
+ struct adis *st = iio_priv(indio_dev);
+ int bits;
+ s16 val16;
+ u8 addr;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_CALIBBIAS:
+ switch (chan->type) {
+ case IIO_ACCEL:
+ bits = 12;
+ break;
+ case IIO_INCLI:
+ bits = 9;
+ break;
+ default:
+ return -EINVAL;
+ }
+ val16 = val & ((1 << bits) - 1);
+ addr = adis16201_addresses[chan->scan_index];
+ return adis_write_reg_16(st, addr, val16);
+ }
+ return -EINVAL;
+}
+
+static const struct iio_chan_spec adis16201_channels[] = {
+ ADIS_SUPPLY_CHAN(ADIS16201_SUPPLY_OUT, ADIS16201_SCAN_SUPPLY, 0, 12),
+ ADIS_TEMP_CHAN(ADIS16201_TEMP_OUT, ADIS16201_SCAN_TEMP, 0, 12),
+ ADIS_ACCEL_CHAN(X, ADIS16201_XACCL_OUT, ADIS16201_SCAN_ACC_X,
+ BIT(IIO_CHAN_INFO_CALIBBIAS), 0, 14),
+ ADIS_ACCEL_CHAN(Y, ADIS16201_YACCL_OUT, ADIS16201_SCAN_ACC_Y,
+ BIT(IIO_CHAN_INFO_CALIBBIAS), 0, 14),
+ ADIS_AUX_ADC_CHAN(ADIS16201_AUX_ADC, ADIS16201_SCAN_AUX_ADC, 0, 12),
+ ADIS_INCLI_CHAN(X, ADIS16201_XINCL_OUT, ADIS16201_SCAN_INCLI_X,
+ BIT(IIO_CHAN_INFO_CALIBBIAS), 0, 14),
+ ADIS_INCLI_CHAN(X, ADIS16201_YINCL_OUT, ADIS16201_SCAN_INCLI_Y,
+ BIT(IIO_CHAN_INFO_CALIBBIAS), 0, 14),
+ IIO_CHAN_SOFT_TIMESTAMP(7)
+};
+
+static const struct iio_info adis16201_info = {
+ .read_raw = &adis16201_read_raw,
+ .write_raw = &adis16201_write_raw,
+ .update_scan_mode = adis_update_scan_mode,
+ .driver_module = THIS_MODULE,
+};
+
+static const char * const adis16201_status_error_msgs[] = {
+ [ADIS16201_DIAG_STAT_SPI_FAIL_BIT] = "SPI failure",
+ [ADIS16201_DIAG_STAT_FLASH_UPT_BIT] = "Flash update failed",
+ [ADIS16201_DIAG_STAT_POWER_HIGH_BIT] = "Power supply above 3.625V",
+ [ADIS16201_DIAG_STAT_POWER_LOW_BIT] = "Power supply below 3.15V",
+};
+
+static const struct adis_data adis16201_data = {
+ .read_delay = 20,
+ .msc_ctrl_reg = ADIS16201_MSC_CTRL,
+ .glob_cmd_reg = ADIS16201_GLOB_CMD,
+ .diag_stat_reg = ADIS16201_DIAG_STAT,
+
+ .self_test_mask = ADIS16201_MSC_CTRL_SELF_TEST_EN,
+ .startup_delay = ADIS16201_STARTUP_DELAY,
+
+ .status_error_msgs = adis16201_status_error_msgs,
+ .status_error_mask = BIT(ADIS16201_DIAG_STAT_SPI_FAIL_BIT) |
+ BIT(ADIS16201_DIAG_STAT_FLASH_UPT_BIT) |
+ BIT(ADIS16201_DIAG_STAT_POWER_HIGH_BIT) |
+ BIT(ADIS16201_DIAG_STAT_POWER_LOW_BIT),
+};
+
+static int adis16201_probe(struct spi_device *spi)
+{
+ int ret;
+ struct adis *st;
+ struct iio_dev *indio_dev;
+
+ /* setup the industrialio driver allocated elements */
+ indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ st = iio_priv(indio_dev);
+ /* this is only used for removal purposes */
+ spi_set_drvdata(spi, indio_dev);
+
+ indio_dev->name = spi->dev.driver->name;
+ indio_dev->dev.parent = &spi->dev;
+ indio_dev->info = &adis16201_info;
+
+ indio_dev->channels = adis16201_channels;
+ indio_dev->num_channels = ARRAY_SIZE(adis16201_channels);
+ indio_dev->modes = INDIO_DIRECT_MODE;
+
+ ret = adis_init(st, indio_dev, spi, &adis16201_data);
+ if (ret)
+ return ret;
+ ret = adis_setup_buffer_and_trigger(st, indio_dev, NULL);
+ if (ret)
+ return ret;
+
+ /* Get the device into a sane initial state */
+ ret = adis_initial_startup(st);
+ if (ret)
+ goto error_cleanup_buffer_trigger;
+
+ ret = iio_device_register(indio_dev);
+ if (ret < 0)
+ goto error_cleanup_buffer_trigger;
+ return 0;
+
+error_cleanup_buffer_trigger:
+ adis_cleanup_buffer_and_trigger(st, indio_dev);
+ return ret;
+}
+
+static int adis16201_remove(struct spi_device *spi)
+{
+ struct iio_dev *indio_dev = spi_get_drvdata(spi);
+ struct adis *st = iio_priv(indio_dev);
+
+ iio_device_unregister(indio_dev);
+ adis_cleanup_buffer_and_trigger(st, indio_dev);
+
+ return 0;
+}
+
+static struct spi_driver adis16201_driver = {
+ .driver = {
+ .name = "adis16201",
+ .owner = THIS_MODULE,
+ },
+ .probe = adis16201_probe,
+ .remove = adis16201_remove,
+};
+module_spi_driver(adis16201_driver);
+
+MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>");
+MODULE_DESCRIPTION("Analog Devices ADIS16201 Programmable Digital Vibration Sensor driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("spi:adis16201");
diff --git a/drivers/staging/iio/accel/adis16203.h b/drivers/staging/iio/accel/adis16203.h
new file mode 100644
index 000000000..6426e38bf
--- /dev/null
+++ b/drivers/staging/iio/accel/adis16203.h
@@ -0,0 +1,59 @@
+#ifndef SPI_ADIS16203_H_
+#define SPI_ADIS16203_H_
+
+#define ADIS16203_STARTUP_DELAY 220 /* ms */
+
+#define ADIS16203_FLASH_CNT 0x00 /* Flash memory write count */
+#define ADIS16203_SUPPLY_OUT 0x02 /* Output, power supply */
+#define ADIS16203_AUX_ADC 0x08 /* Output, auxiliary ADC input */
+#define ADIS16203_TEMP_OUT 0x0A /* Output, temperature */
+#define ADIS16203_XINCL_OUT 0x0C /* Output, x-axis inclination */
+#define ADIS16203_YINCL_OUT 0x0E /* Output, y-axis inclination */
+#define ADIS16203_INCL_NULL 0x18 /* Incline null calibration */
+#define ADIS16203_ALM_MAG1 0x20 /* Alarm 1 amplitude threshold */
+#define ADIS16203_ALM_MAG2 0x22 /* Alarm 2 amplitude threshold */
+#define ADIS16203_ALM_SMPL1 0x24 /* Alarm 1, sample period */
+#define ADIS16203_ALM_SMPL2 0x26 /* Alarm 2, sample period */
+#define ADIS16203_ALM_CTRL 0x28 /* Alarm control */
+#define ADIS16203_AUX_DAC 0x30 /* Auxiliary DAC data */
+#define ADIS16203_GPIO_CTRL 0x32 /* General-purpose digital input/output control */
+#define ADIS16203_MSC_CTRL 0x34 /* Miscellaneous control */
+#define ADIS16203_SMPL_PRD 0x36 /* Internal sample period (rate) control */
+#define ADIS16203_AVG_CNT 0x38 /* Operation, filter configuration */
+#define ADIS16203_SLP_CNT 0x3A /* Operation, sleep mode control */
+#define ADIS16203_DIAG_STAT 0x3C /* Diagnostics, system status register */
+#define ADIS16203_GLOB_CMD 0x3E /* Operation, system command register */
+
+/* MSC_CTRL */
+#define ADIS16203_MSC_CTRL_PWRUP_SELF_TEST BIT(10) /* Self-test at power-on: 1 = disabled, 0 = enabled */
+#define ADIS16203_MSC_CTRL_REVERSE_ROT_EN BIT(9) /* Reverses rotation of both inclination outputs */
+#define ADIS16203_MSC_CTRL_SELF_TEST_EN BIT(8) /* Self-test enable */
+#define ADIS16203_MSC_CTRL_DATA_RDY_EN BIT(2) /* Data-ready enable: 1 = enabled, 0 = disabled */
+#define ADIS16203_MSC_CTRL_ACTIVE_HIGH BIT(1) /* Data-ready polarity: 1 = active high, 0 = active low */
+#define ADIS16203_MSC_CTRL_DATA_RDY_DIO1 BIT(0) /* Data-ready line selection: 1 = DIO1, 0 = DIO0 */
+
+/* DIAG_STAT */
+#define ADIS16203_DIAG_STAT_ALARM2 BIT(9) /* Alarm 2 status: 1 = alarm active, 0 = alarm inactive */
+#define ADIS16203_DIAG_STAT_ALARM1 BIT(8) /* Alarm 1 status: 1 = alarm active, 0 = alarm inactive */
+#define ADIS16203_DIAG_STAT_SELFTEST_FAIL_BIT 5 /* Self-test diagnostic error flag */
+#define ADIS16203_DIAG_STAT_SPI_FAIL_BIT 3 /* SPI communications failure */
+#define ADIS16203_DIAG_STAT_FLASH_UPT_BIT 2 /* Flash update failure */
+#define ADIS16203_DIAG_STAT_POWER_HIGH_BIT 1 /* Power supply above 3.625 V */
+#define ADIS16203_DIAG_STAT_POWER_LOW_BIT 0 /* Power supply below 3.15 V */
+
+/* GLOB_CMD */
+#define ADIS16203_GLOB_CMD_SW_RESET BIT(7)
+#define ADIS16203_GLOB_CMD_CLEAR_STAT BIT(4)
+#define ADIS16203_GLOB_CMD_FACTORY_CAL BIT(1)
+
+#define ADIS16203_ERROR_ACTIVE BIT(14)
+
+enum adis16203_scan {
+ ADIS16203_SCAN_INCLI_X,
+ ADIS16203_SCAN_INCLI_Y,
+ ADIS16203_SCAN_SUPPLY,
+ ADIS16203_SCAN_AUX_ADC,
+ ADIS16203_SCAN_TEMP,
+};
+
+#endif /* SPI_ADIS16203_H_ */
diff --git a/drivers/staging/iio/accel/adis16203_core.c b/drivers/staging/iio/accel/adis16203_core.c
new file mode 100644
index 000000000..fb593d23d
--- /dev/null
+++ b/drivers/staging/iio/accel/adis16203_core.c
@@ -0,0 +1,216 @@
+/*
+ * ADIS16203 Programmable Digital Vibration Sensor driver
+ *
+ * Copyright 2030 Analog Devices Inc.
+ *
+ * Licensed under the GPL-2 or later.
+ */
+
+#include <linux/delay.h>
+#include <linux/mutex.h>
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/spi/spi.h>
+#include <linux/slab.h>
+#include <linux/sysfs.h>
+#include <linux/module.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/imu/adis.h>
+
+#include "adis16203.h"
+
+#define DRIVER_NAME "adis16203"
+
+static const u8 adis16203_addresses[] = {
+ [ADIS16203_SCAN_INCLI_X] = ADIS16203_INCL_NULL,
+};
+
+static int adis16203_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int val,
+ int val2,
+ long mask)
+{
+ struct adis *st = iio_priv(indio_dev);
+ /* currently only one writable parameter which keeps this simple */
+ u8 addr = adis16203_addresses[chan->scan_index];
+
+ return adis_write_reg_16(st, addr, val & 0x3FFF);
+}
+
+static int adis16203_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2,
+ long mask)
+{
+ struct adis *st = iio_priv(indio_dev);
+ int ret;
+ int bits;
+ u8 addr;
+ s16 val16;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ return adis_single_conversion(indio_dev, chan,
+ ADIS16203_ERROR_ACTIVE, val);
+ case IIO_CHAN_INFO_SCALE:
+ switch (chan->type) {
+ case IIO_VOLTAGE:
+ if (chan->channel == 0) {
+ *val = 1;
+ *val2 = 220000; /* 1.22 mV */
+ } else {
+ *val = 0;
+ *val2 = 610000; /* 0.61 mV */
+ }
+ return IIO_VAL_INT_PLUS_MICRO;
+ case IIO_TEMP:
+ *val = -470; /* -0.47 C */
+ *val2 = 0;
+ return IIO_VAL_INT_PLUS_MICRO;
+ case IIO_INCLI:
+ *val = 0;
+ *val2 = 25000; /* 0.025 degree */
+ return IIO_VAL_INT_PLUS_MICRO;
+ default:
+ return -EINVAL;
+ }
+ case IIO_CHAN_INFO_OFFSET:
+ *val = 25000 / -470 - 1278; /* 25 C = 1278 */
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_CALIBBIAS:
+ bits = 14;
+ mutex_lock(&indio_dev->mlock);
+ addr = adis16203_addresses[chan->scan_index];
+ ret = adis_read_reg_16(st, addr, &val16);
+ if (ret) {
+ mutex_unlock(&indio_dev->mlock);
+ return ret;
+ }
+ val16 &= (1 << bits) - 1;
+ val16 = (s16)(val16 << (16 - bits)) >> (16 - bits);
+ *val = val16;
+ mutex_unlock(&indio_dev->mlock);
+ return IIO_VAL_INT;
+ default:
+ return -EINVAL;
+ }
+}
+
+static const struct iio_chan_spec adis16203_channels[] = {
+ ADIS_SUPPLY_CHAN(ADIS16203_SUPPLY_OUT, ADIS16203_SCAN_SUPPLY, 0, 12),
+ ADIS_AUX_ADC_CHAN(ADIS16203_AUX_ADC, ADIS16203_SCAN_AUX_ADC, 0, 12),
+ ADIS_INCLI_CHAN(X, ADIS16203_XINCL_OUT, ADIS16203_SCAN_INCLI_X,
+ BIT(IIO_CHAN_INFO_CALIBBIAS), 0, 14),
+ /* Fixme: Not what it appears to be - see data sheet */
+ ADIS_INCLI_CHAN(Y, ADIS16203_YINCL_OUT, ADIS16203_SCAN_INCLI_Y,
+ 0, 0, 14),
+ ADIS_TEMP_CHAN(ADIS16203_TEMP_OUT, ADIS16203_SCAN_TEMP, 0, 12),
+ IIO_CHAN_SOFT_TIMESTAMP(5),
+};
+
+static const struct iio_info adis16203_info = {
+ .read_raw = &adis16203_read_raw,
+ .write_raw = &adis16203_write_raw,
+ .update_scan_mode = adis_update_scan_mode,
+ .driver_module = THIS_MODULE,
+};
+
+static const char * const adis16203_status_error_msgs[] = {
+ [ADIS16203_DIAG_STAT_SELFTEST_FAIL_BIT] = "Self test failure",
+ [ADIS16203_DIAG_STAT_SPI_FAIL_BIT] = "SPI failure",
+ [ADIS16203_DIAG_STAT_FLASH_UPT_BIT] = "Flash update failed",
+ [ADIS16203_DIAG_STAT_POWER_HIGH_BIT] = "Power supply above 3.625V",
+ [ADIS16203_DIAG_STAT_POWER_LOW_BIT] = "Power supply below 3.15V",
+};
+
+static const struct adis_data adis16203_data = {
+ .read_delay = 20,
+ .msc_ctrl_reg = ADIS16203_MSC_CTRL,
+ .glob_cmd_reg = ADIS16203_GLOB_CMD,
+ .diag_stat_reg = ADIS16203_DIAG_STAT,
+
+ .self_test_mask = ADIS16203_MSC_CTRL_SELF_TEST_EN,
+ .startup_delay = ADIS16203_STARTUP_DELAY,
+
+ .status_error_msgs = adis16203_status_error_msgs,
+ .status_error_mask = BIT(ADIS16203_DIAG_STAT_SELFTEST_FAIL_BIT) |
+ BIT(ADIS16203_DIAG_STAT_SPI_FAIL_BIT) |
+ BIT(ADIS16203_DIAG_STAT_FLASH_UPT_BIT) |
+ BIT(ADIS16203_DIAG_STAT_POWER_HIGH_BIT) |
+ BIT(ADIS16203_DIAG_STAT_POWER_LOW_BIT),
+};
+
+static int adis16203_probe(struct spi_device *spi)
+{
+ int ret;
+ struct iio_dev *indio_dev;
+ struct adis *st;
+
+ /* setup the industrialio driver allocated elements */
+ indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));
+ if (!indio_dev)
+ return -ENOMEM;
+ st = iio_priv(indio_dev);
+ /* this is only used for removal purposes */
+ spi_set_drvdata(spi, indio_dev);
+
+ indio_dev->name = spi->dev.driver->name;
+ indio_dev->dev.parent = &spi->dev;
+ indio_dev->channels = adis16203_channels;
+ indio_dev->num_channels = ARRAY_SIZE(adis16203_channels);
+ indio_dev->info = &adis16203_info;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+
+ ret = adis_init(st, indio_dev, spi, &adis16203_data);
+ if (ret)
+ return ret;
+
+ ret = adis_setup_buffer_and_trigger(st, indio_dev, NULL);
+ if (ret)
+ return ret;
+
+ /* Get the device into a sane initial state */
+ ret = adis_initial_startup(st);
+ if (ret)
+ goto error_cleanup_buffer_trigger;
+
+ ret = iio_device_register(indio_dev);
+ if (ret)
+ goto error_cleanup_buffer_trigger;
+
+ return 0;
+
+error_cleanup_buffer_trigger:
+ adis_cleanup_buffer_and_trigger(st, indio_dev);
+ return ret;
+}
+
+static int adis16203_remove(struct spi_device *spi)
+{
+ struct iio_dev *indio_dev = spi_get_drvdata(spi);
+ struct adis *st = iio_priv(indio_dev);
+
+ iio_device_unregister(indio_dev);
+ adis_cleanup_buffer_and_trigger(st, indio_dev);
+
+ return 0;
+}
+
+static struct spi_driver adis16203_driver = {
+ .driver = {
+ .name = "adis16203",
+ .owner = THIS_MODULE,
+ },
+ .probe = adis16203_probe,
+ .remove = adis16203_remove,
+};
+module_spi_driver(adis16203_driver);
+
+MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>");
+MODULE_DESCRIPTION("Analog Devices ADIS16203 Programmable Digital Vibration Sensor driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("spi:adis16203");
diff --git a/drivers/staging/iio/accel/adis16204.h b/drivers/staging/iio/accel/adis16204.h
new file mode 100644
index 000000000..0b23f0b5c
--- /dev/null
+++ b/drivers/staging/iio/accel/adis16204.h
@@ -0,0 +1,68 @@
+#ifndef SPI_ADIS16204_H_
+#define SPI_ADIS16204_H_
+
+#define ADIS16204_STARTUP_DELAY 220 /* ms */
+
+#define ADIS16204_FLASH_CNT 0x00 /* Flash memory write count */
+#define ADIS16204_SUPPLY_OUT 0x02 /* Output, power supply */
+#define ADIS16204_XACCL_OUT 0x04 /* Output, x-axis accelerometer */
+#define ADIS16204_YACCL_OUT 0x06 /* Output, y-axis accelerometer */
+#define ADIS16204_AUX_ADC 0x08 /* Output, auxiliary ADC input */
+#define ADIS16204_TEMP_OUT 0x0A /* Output, temperature */
+#define ADIS16204_X_PEAK_OUT 0x0C /* Twos complement */
+#define ADIS16204_Y_PEAK_OUT 0x0E /* Twos complement */
+#define ADIS16204_XACCL_NULL 0x10 /* Calibration, x-axis acceleration offset null */
+#define ADIS16204_YACCL_NULL 0x12 /* Calibration, y-axis acceleration offset null */
+#define ADIS16204_XACCL_SCALE 0x14 /* X-axis scale factor calibration register */
+#define ADIS16204_YACCL_SCALE 0x16 /* Y-axis scale factor calibration register */
+#define ADIS16204_XY_RSS_OUT 0x18 /* XY combined acceleration (RSS) */
+#define ADIS16204_XY_PEAK_OUT 0x1A /* Peak, XY combined output (RSS) */
+#define ADIS16204_CAP_BUF_1 0x1C /* Capture buffer output register 1 */
+#define ADIS16204_CAP_BUF_2 0x1E /* Capture buffer output register 2 */
+#define ADIS16204_ALM_MAG1 0x20 /* Alarm 1 amplitude threshold */
+#define ADIS16204_ALM_MAG2 0x22 /* Alarm 2 amplitude threshold */
+#define ADIS16204_ALM_CTRL 0x28 /* Alarm control */
+#define ADIS16204_CAPT_PNTR 0x2A /* Capture register address pointer */
+#define ADIS16204_AUX_DAC 0x30 /* Auxiliary DAC data */
+#define ADIS16204_GPIO_CTRL 0x32 /* General-purpose digital input/output control */
+#define ADIS16204_MSC_CTRL 0x34 /* Miscellaneous control */
+#define ADIS16204_SMPL_PRD 0x36 /* Internal sample period (rate) control */
+#define ADIS16204_AVG_CNT 0x38 /* Operation, filter configuration */
+#define ADIS16204_SLP_CNT 0x3A /* Operation, sleep mode control */
+#define ADIS16204_DIAG_STAT 0x3C /* Diagnostics, system status register */
+#define ADIS16204_GLOB_CMD 0x3E /* Operation, system command register */
+
+/* MSC_CTRL */
+#define ADIS16204_MSC_CTRL_PWRUP_SELF_TEST BIT(10) /* Self-test at power-on: 1 = disabled, 0 = enabled */
+#define ADIS16204_MSC_CTRL_SELF_TEST_EN BIT(8) /* Self-test enable */
+#define ADIS16204_MSC_CTRL_DATA_RDY_EN BIT(2) /* Data-ready enable: 1 = enabled, 0 = disabled */
+#define ADIS16204_MSC_CTRL_ACTIVE_HIGH BIT(1) /* Data-ready polarity: 1 = active high, 0 = active low */
+#define ADIS16204_MSC_CTRL_DATA_RDY_DIO2 BIT(0) /* Data-ready line selection: 1 = DIO2, 0 = DIO1 */
+
+/* DIAG_STAT */
+#define ADIS16204_DIAG_STAT_ALARM2 BIT(9) /* Alarm 2 status: 1 = alarm active, 0 = alarm inactive */
+#define ADIS16204_DIAG_STAT_ALARM1 BIT(8) /* Alarm 1 status: 1 = alarm active, 0 = alarm inactive */
+#define ADIS16204_DIAG_STAT_SELFTEST_FAIL_BIT 5 /* Self-test diagnostic error flag: 1 = error condition,
+ 0 = normal operation */
+#define ADIS16204_DIAG_STAT_SPI_FAIL_BIT 3 /* SPI communications failure */
+#define ADIS16204_DIAG_STAT_FLASH_UPT_BIT 2 /* Flash update failure */
+#define ADIS16204_DIAG_STAT_POWER_HIGH_BIT 1 /* Power supply above 3.625 V */
+#define ADIS16204_DIAG_STAT_POWER_LOW_BIT 0 /* Power supply below 2.975 V */
+
+/* GLOB_CMD */
+#define ADIS16204_GLOB_CMD_SW_RESET BIT(7)
+#define ADIS16204_GLOB_CMD_CLEAR_STAT BIT(4)
+#define ADIS16204_GLOB_CMD_FACTORY_CAL BIT(1)
+
+#define ADIS16204_ERROR_ACTIVE BIT(14)
+
+enum adis16204_scan {
+ ADIS16204_SCAN_ACC_X,
+ ADIS16204_SCAN_ACC_Y,
+ ADIS16204_SCAN_ACC_XY,
+ ADIS16204_SCAN_SUPPLY,
+ ADIS16204_SCAN_AUX_ADC,
+ ADIS16204_SCAN_TEMP,
+};
+
+#endif /* SPI_ADIS16204_H_ */
diff --git a/drivers/staging/iio/accel/adis16204_core.c b/drivers/staging/iio/accel/adis16204_core.c
new file mode 100644
index 000000000..ea0ac2467
--- /dev/null
+++ b/drivers/staging/iio/accel/adis16204_core.c
@@ -0,0 +1,254 @@
+/*
+ * ADIS16204 Programmable High-g Digital Impact Sensor and Recorder
+ *
+ * Copyright 2010 Analog Devices Inc.
+ *
+ * Licensed under the GPL-2 or later.
+ */
+
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/delay.h>
+#include <linux/mutex.h>
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/spi/spi.h>
+#include <linux/slab.h>
+#include <linux/sysfs.h>
+#include <linux/list.h>
+#include <linux/module.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/imu/adis.h>
+
+#include "adis16204.h"
+
+/* Unique to this driver currently */
+
+static const u8 adis16204_addresses[][2] = {
+ [ADIS16204_SCAN_ACC_X] = { ADIS16204_XACCL_NULL, ADIS16204_X_PEAK_OUT },
+ [ADIS16204_SCAN_ACC_Y] = { ADIS16204_YACCL_NULL, ADIS16204_Y_PEAK_OUT },
+ [ADIS16204_SCAN_ACC_XY] = { 0, ADIS16204_XY_PEAK_OUT },
+};
+
+static int adis16204_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2,
+ long mask)
+{
+ struct adis *st = iio_priv(indio_dev);
+ int ret;
+ int bits;
+ u8 addr;
+ s16 val16;
+ int addrind;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ return adis_single_conversion(indio_dev, chan,
+ ADIS16204_ERROR_ACTIVE, val);
+ case IIO_CHAN_INFO_SCALE:
+ switch (chan->type) {
+ case IIO_VOLTAGE:
+ if (chan->channel == 0) {
+ *val = 1;
+ *val2 = 220000; /* 1.22 mV */
+ } else {
+ *val = 0;
+ *val2 = 610000; /* 0.61 mV */
+ }
+ return IIO_VAL_INT_PLUS_MICRO;
+ case IIO_TEMP:
+ *val = -470; /* 0.47 C */
+ *val2 = 0;
+ return IIO_VAL_INT_PLUS_MICRO;
+ case IIO_ACCEL:
+ *val = 0;
+ switch (chan->channel2) {
+ case IIO_MOD_X:
+ case IIO_MOD_ROOT_SUM_SQUARED_X_Y:
+ *val2 = IIO_G_TO_M_S_2(17125); /* 17.125 mg */
+ break;
+ case IIO_MOD_Y:
+ case IIO_MOD_Z:
+ *val2 = IIO_G_TO_M_S_2(8407); /* 8.407 mg */
+ break;
+ }
+ return IIO_VAL_INT_PLUS_MICRO;
+ default:
+ return -EINVAL;
+ }
+ break;
+ case IIO_CHAN_INFO_OFFSET:
+ *val = 25000 / -470 - 1278; /* 25 C = 1278 */
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_CALIBBIAS:
+ case IIO_CHAN_INFO_PEAK:
+ if (mask == IIO_CHAN_INFO_CALIBBIAS) {
+ bits = 12;
+ addrind = 0;
+ } else { /* PEAK_SEPARATE */
+ bits = 14;
+ addrind = 1;
+ }
+ mutex_lock(&indio_dev->mlock);
+ addr = adis16204_addresses[chan->scan_index][addrind];
+ ret = adis_read_reg_16(st, addr, &val16);
+ if (ret) {
+ mutex_unlock(&indio_dev->mlock);
+ return ret;
+ }
+ val16 &= (1 << bits) - 1;
+ val16 = (s16)(val16 << (16 - bits)) >> (16 - bits);
+ *val = val16;
+ mutex_unlock(&indio_dev->mlock);
+ return IIO_VAL_INT;
+ }
+ return -EINVAL;
+}
+
+static int adis16204_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int val,
+ int val2,
+ long mask)
+{
+ struct adis *st = iio_priv(indio_dev);
+ int bits;
+ s16 val16;
+ u8 addr;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_CALIBBIAS:
+ switch (chan->type) {
+ case IIO_ACCEL:
+ bits = 12;
+ break;
+ default:
+ return -EINVAL;
+ }
+ val16 = val & ((1 << bits) - 1);
+ addr = adis16204_addresses[chan->scan_index][1];
+ return adis_write_reg_16(st, addr, val16);
+ }
+ return -EINVAL;
+}
+
+static const struct iio_chan_spec adis16204_channels[] = {
+ ADIS_SUPPLY_CHAN(ADIS16204_SUPPLY_OUT, ADIS16204_SCAN_SUPPLY, 0, 12),
+ ADIS_AUX_ADC_CHAN(ADIS16204_AUX_ADC, ADIS16204_SCAN_AUX_ADC, 0, 12),
+ ADIS_TEMP_CHAN(ADIS16204_TEMP_OUT, ADIS16204_SCAN_TEMP, 0, 12),
+ ADIS_ACCEL_CHAN(X, ADIS16204_XACCL_OUT, ADIS16204_SCAN_ACC_X,
+ BIT(IIO_CHAN_INFO_CALIBBIAS) | BIT(IIO_CHAN_INFO_PEAK),
+ 0, 14),
+ ADIS_ACCEL_CHAN(Y, ADIS16204_YACCL_OUT, ADIS16204_SCAN_ACC_Y,
+ BIT(IIO_CHAN_INFO_CALIBBIAS) | BIT(IIO_CHAN_INFO_PEAK),
+ 0, 14),
+ ADIS_ACCEL_CHAN(ROOT_SUM_SQUARED_X_Y, ADIS16204_XY_RSS_OUT,
+ ADIS16204_SCAN_ACC_XY, BIT(IIO_CHAN_INFO_PEAK), 0, 14),
+ IIO_CHAN_SOFT_TIMESTAMP(5),
+};
+
+static const struct iio_info adis16204_info = {
+ .read_raw = &adis16204_read_raw,
+ .write_raw = &adis16204_write_raw,
+ .update_scan_mode = adis_update_scan_mode,
+ .driver_module = THIS_MODULE,
+};
+
+static const char * const adis16204_status_error_msgs[] = {
+ [ADIS16204_DIAG_STAT_SELFTEST_FAIL_BIT] = "Self test failure",
+ [ADIS16204_DIAG_STAT_SPI_FAIL_BIT] = "SPI failure",
+ [ADIS16204_DIAG_STAT_FLASH_UPT_BIT] = "Flash update failed",
+ [ADIS16204_DIAG_STAT_POWER_HIGH_BIT] = "Power supply above 3.625V",
+ [ADIS16204_DIAG_STAT_POWER_LOW_BIT] = "Power supply below 2.975V",
+};
+
+static const struct adis_data adis16204_data = {
+ .read_delay = 20,
+ .msc_ctrl_reg = ADIS16204_MSC_CTRL,
+ .glob_cmd_reg = ADIS16204_GLOB_CMD,
+ .diag_stat_reg = ADIS16204_DIAG_STAT,
+
+ .self_test_mask = ADIS16204_MSC_CTRL_SELF_TEST_EN,
+ .startup_delay = ADIS16204_STARTUP_DELAY,
+
+ .status_error_msgs = adis16204_status_error_msgs,
+ .status_error_mask = BIT(ADIS16204_DIAG_STAT_SELFTEST_FAIL_BIT) |
+ BIT(ADIS16204_DIAG_STAT_SPI_FAIL_BIT) |
+ BIT(ADIS16204_DIAG_STAT_FLASH_UPT_BIT) |
+ BIT(ADIS16204_DIAG_STAT_POWER_HIGH_BIT) |
+ BIT(ADIS16204_DIAG_STAT_POWER_LOW_BIT),
+};
+
+static int adis16204_probe(struct spi_device *spi)
+{
+ int ret;
+ struct adis *st;
+ struct iio_dev *indio_dev;
+
+ /* setup the industrialio driver allocated elements */
+ indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));
+ if (!indio_dev)
+ return -ENOMEM;
+ st = iio_priv(indio_dev);
+ /* this is only used for removal purposes */
+ spi_set_drvdata(spi, indio_dev);
+
+ indio_dev->name = spi->dev.driver->name;
+ indio_dev->dev.parent = &spi->dev;
+ indio_dev->info = &adis16204_info;
+ indio_dev->channels = adis16204_channels;
+ indio_dev->num_channels = ARRAY_SIZE(adis16204_channels);
+ indio_dev->modes = INDIO_DIRECT_MODE;
+
+ ret = adis_init(st, indio_dev, spi, &adis16204_data);
+ if (ret)
+ return ret;
+
+ ret = adis_setup_buffer_and_trigger(st, indio_dev, NULL);
+ if (ret)
+ return ret;
+
+ /* Get the device into a sane initial state */
+ ret = adis_initial_startup(st);
+ if (ret)
+ goto error_cleanup_buffer_trigger;
+ ret = iio_device_register(indio_dev);
+ if (ret)
+ goto error_cleanup_buffer_trigger;
+
+ return 0;
+
+error_cleanup_buffer_trigger:
+ adis_cleanup_buffer_and_trigger(st, indio_dev);
+ return ret;
+}
+
+static int adis16204_remove(struct spi_device *spi)
+{
+ struct iio_dev *indio_dev = spi_get_drvdata(spi);
+ struct adis *st = iio_priv(indio_dev);
+
+ iio_device_unregister(indio_dev);
+ adis_cleanup_buffer_and_trigger(st, indio_dev);
+
+ return 0;
+}
+
+static struct spi_driver adis16204_driver = {
+ .driver = {
+ .name = "adis16204",
+ .owner = THIS_MODULE,
+ },
+ .probe = adis16204_probe,
+ .remove = adis16204_remove,
+};
+module_spi_driver(adis16204_driver);
+
+MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>");
+MODULE_DESCRIPTION("ADIS16204 High-g Digital Impact Sensor and Recorder");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("spi:adis16204");
diff --git a/drivers/staging/iio/accel/adis16209.h b/drivers/staging/iio/accel/adis16209.h
new file mode 100644
index 000000000..813698d18
--- /dev/null
+++ b/drivers/staging/iio/accel/adis16209.h
@@ -0,0 +1,105 @@
+#ifndef SPI_ADIS16209_H_
+#define SPI_ADIS16209_H_
+
+#define ADIS16209_STARTUP_DELAY 220 /* ms */
+
+/* Flash memory write count */
+#define ADIS16209_FLASH_CNT 0x00
+/* Output, power supply */
+#define ADIS16209_SUPPLY_OUT 0x02
+/* Output, x-axis accelerometer */
+#define ADIS16209_XACCL_OUT 0x04
+/* Output, y-axis accelerometer */
+#define ADIS16209_YACCL_OUT 0x06
+/* Output, auxiliary ADC input */
+#define ADIS16209_AUX_ADC 0x08
+/* Output, temperature */
+#define ADIS16209_TEMP_OUT 0x0A
+/* Output, x-axis inclination */
+#define ADIS16209_XINCL_OUT 0x0C
+/* Output, y-axis inclination */
+#define ADIS16209_YINCL_OUT 0x0E
+/* Output, +/-180 vertical rotational position */
+#define ADIS16209_ROT_OUT 0x10
+/* Calibration, x-axis acceleration offset null */
+#define ADIS16209_XACCL_NULL 0x12
+/* Calibration, y-axis acceleration offset null */
+#define ADIS16209_YACCL_NULL 0x14
+/* Calibration, x-axis inclination offset null */
+#define ADIS16209_XINCL_NULL 0x16
+/* Calibration, y-axis inclination offset null */
+#define ADIS16209_YINCL_NULL 0x18
+/* Calibration, vertical rotation offset null */
+#define ADIS16209_ROT_NULL 0x1A
+/* Alarm 1 amplitude threshold */
+#define ADIS16209_ALM_MAG1 0x20
+/* Alarm 2 amplitude threshold */
+#define ADIS16209_ALM_MAG2 0x22
+/* Alarm 1, sample period */
+#define ADIS16209_ALM_SMPL1 0x24
+/* Alarm 2, sample period */
+#define ADIS16209_ALM_SMPL2 0x26
+/* Alarm control */
+#define ADIS16209_ALM_CTRL 0x28
+/* Auxiliary DAC data */
+#define ADIS16209_AUX_DAC 0x30
+/* General-purpose digital input/output control */
+#define ADIS16209_GPIO_CTRL 0x32
+/* Miscellaneous control */
+#define ADIS16209_MSC_CTRL 0x34
+/* Internal sample period (rate) control */
+#define ADIS16209_SMPL_PRD 0x36
+/* Operation, filter configuration */
+#define ADIS16209_AVG_CNT 0x38
+/* Operation, sleep mode control */
+#define ADIS16209_SLP_CNT 0x3A
+/* Diagnostics, system status register */
+#define ADIS16209_DIAG_STAT 0x3C
+/* Operation, system command register */
+#define ADIS16209_GLOB_CMD 0x3E
+
+/* MSC_CTRL */
+/* Self-test at power-on: 1 = disabled, 0 = enabled */
+#define ADIS16209_MSC_CTRL_PWRUP_SELF_TEST BIT(10)
+/* Self-test enable */
+#define ADIS16209_MSC_CTRL_SELF_TEST_EN BIT(8)
+/* Data-ready enable: 1 = enabled, 0 = disabled */
+#define ADIS16209_MSC_CTRL_DATA_RDY_EN BIT(2)
+/* Data-ready polarity: 1 = active high, 0 = active low */
+#define ADIS16209_MSC_CTRL_ACTIVE_HIGH BIT(1)
+/* Data-ready line selection: 1 = DIO2, 0 = DIO1 */
+#define ADIS16209_MSC_CTRL_DATA_RDY_DIO2 BIT(0)
+
+/* DIAG_STAT */
+/* Alarm 2 status: 1 = alarm active, 0 = alarm inactive */
+#define ADIS16209_DIAG_STAT_ALARM2 BIT(9)
+/* Alarm 1 status: 1 = alarm active, 0 = alarm inactive */
+#define ADIS16209_DIAG_STAT_ALARM1 BIT(8)
+/* Self-test diagnostic error flag: 1 = error condition, 0 = normal operation */
+#define ADIS16209_DIAG_STAT_SELFTEST_FAIL_BIT 5
+/* SPI communications failure */
+#define ADIS16209_DIAG_STAT_SPI_FAIL_BIT 3
+/* Flash update failure */
+#define ADIS16209_DIAG_STAT_FLASH_UPT_BIT 2
+/* Power supply above 3.625 V */
+#define ADIS16209_DIAG_STAT_POWER_HIGH_BIT 1
+/* Power supply below 3.15 V */
+#define ADIS16209_DIAG_STAT_POWER_LOW_BIT 0
+
+/* GLOB_CMD */
+#define ADIS16209_GLOB_CMD_SW_RESET BIT(7)
+#define ADIS16209_GLOB_CMD_CLEAR_STAT BIT(4)
+#define ADIS16209_GLOB_CMD_FACTORY_CAL BIT(1)
+
+#define ADIS16209_ERROR_ACTIVE BIT(14)
+
+#define ADIS16209_SCAN_SUPPLY 0
+#define ADIS16209_SCAN_ACC_X 1
+#define ADIS16209_SCAN_ACC_Y 2
+#define ADIS16209_SCAN_AUX_ADC 3
+#define ADIS16209_SCAN_TEMP 4
+#define ADIS16209_SCAN_INCLI_X 5
+#define ADIS16209_SCAN_INCLI_Y 6
+#define ADIS16209_SCAN_ROT 7
+
+#endif /* SPI_ADIS16209_H_ */
diff --git a/drivers/staging/iio/accel/adis16209_core.c b/drivers/staging/iio/accel/adis16209_core.c
new file mode 100644
index 000000000..d1dc1a3cb
--- /dev/null
+++ b/drivers/staging/iio/accel/adis16209_core.c
@@ -0,0 +1,248 @@
+/*
+ * ADIS16209 Programmable Digital Vibration Sensor driver
+ *
+ * Copyright 2010 Analog Devices Inc.
+ *
+ * Licensed under the GPL-2 or later.
+ */
+
+#include <linux/delay.h>
+#include <linux/mutex.h>
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/spi/spi.h>
+#include <linux/slab.h>
+#include <linux/sysfs.h>
+#include <linux/list.h>
+#include <linux/module.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/imu/adis.h>
+
+#include "adis16209.h"
+
+static const u8 adis16209_addresses[8][1] = {
+ [ADIS16209_SCAN_SUPPLY] = { },
+ [ADIS16209_SCAN_AUX_ADC] = { },
+ [ADIS16209_SCAN_ACC_X] = { ADIS16209_XACCL_NULL },
+ [ADIS16209_SCAN_ACC_Y] = { ADIS16209_YACCL_NULL },
+ [ADIS16209_SCAN_INCLI_X] = { ADIS16209_XINCL_NULL },
+ [ADIS16209_SCAN_INCLI_Y] = { ADIS16209_YINCL_NULL },
+ [ADIS16209_SCAN_ROT] = { },
+ [ADIS16209_SCAN_TEMP] = { },
+};
+
+static int adis16209_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int val,
+ int val2,
+ long mask)
+{
+ struct adis *st = iio_priv(indio_dev);
+ int bits;
+ s16 val16;
+ u8 addr;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_CALIBBIAS:
+ switch (chan->type) {
+ case IIO_ACCEL:
+ case IIO_INCLI:
+ bits = 14;
+ break;
+ default:
+ return -EINVAL;
+ }
+ val16 = val & ((1 << bits) - 1);
+ addr = adis16209_addresses[chan->scan_index][0];
+ return adis_write_reg_16(st, addr, val16);
+ }
+ return -EINVAL;
+}
+
+static int adis16209_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2,
+ long mask)
+{
+ struct adis *st = iio_priv(indio_dev);
+ int ret;
+ int bits;
+ u8 addr;
+ s16 val16;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ return adis_single_conversion(indio_dev, chan,
+ ADIS16209_ERROR_ACTIVE, val);
+ case IIO_CHAN_INFO_SCALE:
+ switch (chan->type) {
+ case IIO_VOLTAGE:
+ *val = 0;
+ if (chan->channel == 0)
+ *val2 = 305180; /* 0.30518 mV */
+ else
+ *val2 = 610500; /* 0.6105 mV */
+ return IIO_VAL_INT_PLUS_MICRO;
+ case IIO_TEMP:
+ *val = -470; /* -0.47 C */
+ *val2 = 0;
+ return IIO_VAL_INT_PLUS_MICRO;
+ case IIO_ACCEL:
+ *val = 0;
+ *val2 = IIO_G_TO_M_S_2(244140); /* 0.244140 mg */
+ return IIO_VAL_INT_PLUS_NANO;
+ case IIO_INCLI:
+ case IIO_ROT:
+ *val = 0;
+ *val2 = 25000; /* 0.025 degree */
+ return IIO_VAL_INT_PLUS_MICRO;
+ default:
+ return -EINVAL;
+ }
+ break;
+ case IIO_CHAN_INFO_OFFSET:
+ *val = 25000 / -470 - 0x4FE; /* 25 C = 0x4FE */
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_CALIBBIAS:
+ switch (chan->type) {
+ case IIO_ACCEL:
+ bits = 14;
+ break;
+ default:
+ return -EINVAL;
+ }
+ mutex_lock(&indio_dev->mlock);
+ addr = adis16209_addresses[chan->scan_index][0];
+ ret = adis_read_reg_16(st, addr, &val16);
+ if (ret) {
+ mutex_unlock(&indio_dev->mlock);
+ return ret;
+ }
+ val16 &= (1 << bits) - 1;
+ val16 = (s16)(val16 << (16 - bits)) >> (16 - bits);
+ *val = val16;
+ mutex_unlock(&indio_dev->mlock);
+ return IIO_VAL_INT;
+ }
+ return -EINVAL;
+}
+
+static const struct iio_chan_spec adis16209_channels[] = {
+ ADIS_SUPPLY_CHAN(ADIS16209_SUPPLY_OUT, ADIS16209_SCAN_SUPPLY, 0, 14),
+ ADIS_TEMP_CHAN(ADIS16209_TEMP_OUT, ADIS16209_SCAN_TEMP, 0, 12),
+ ADIS_ACCEL_CHAN(X, ADIS16209_XACCL_OUT, ADIS16209_SCAN_ACC_X,
+ BIT(IIO_CHAN_INFO_CALIBBIAS), 0, 14),
+ ADIS_ACCEL_CHAN(Y, ADIS16209_YACCL_OUT, ADIS16209_SCAN_ACC_Y,
+ BIT(IIO_CHAN_INFO_CALIBBIAS), 0, 14),
+ ADIS_AUX_ADC_CHAN(ADIS16209_AUX_ADC, ADIS16209_SCAN_AUX_ADC, 0, 12),
+ ADIS_INCLI_CHAN(X, ADIS16209_XINCL_OUT, ADIS16209_SCAN_INCLI_X,
+ 0, 0, 14),
+ ADIS_INCLI_CHAN(Y, ADIS16209_YINCL_OUT, ADIS16209_SCAN_INCLI_Y,
+ 0, 0, 14),
+ ADIS_ROT_CHAN(X, ADIS16209_ROT_OUT, ADIS16209_SCAN_ROT, 0, 0, 14),
+ IIO_CHAN_SOFT_TIMESTAMP(8)
+};
+
+static const struct iio_info adis16209_info = {
+ .read_raw = &adis16209_read_raw,
+ .write_raw = &adis16209_write_raw,
+ .update_scan_mode = adis_update_scan_mode,
+ .driver_module = THIS_MODULE,
+};
+
+static const char * const adis16209_status_error_msgs[] = {
+ [ADIS16209_DIAG_STAT_SELFTEST_FAIL_BIT] = "Self test failure",
+ [ADIS16209_DIAG_STAT_SPI_FAIL_BIT] = "SPI failure",
+ [ADIS16209_DIAG_STAT_FLASH_UPT_BIT] = "Flash update failed",
+ [ADIS16209_DIAG_STAT_POWER_HIGH_BIT] = "Power supply above 3.625V",
+ [ADIS16209_DIAG_STAT_POWER_LOW_BIT] = "Power supply below 3.15V",
+};
+
+static const struct adis_data adis16209_data = {
+ .read_delay = 30,
+ .msc_ctrl_reg = ADIS16209_MSC_CTRL,
+ .glob_cmd_reg = ADIS16209_GLOB_CMD,
+ .diag_stat_reg = ADIS16209_DIAG_STAT,
+
+ .self_test_mask = ADIS16209_MSC_CTRL_SELF_TEST_EN,
+ .startup_delay = ADIS16209_STARTUP_DELAY,
+
+ .status_error_msgs = adis16209_status_error_msgs,
+ .status_error_mask = BIT(ADIS16209_DIAG_STAT_SELFTEST_FAIL_BIT) |
+ BIT(ADIS16209_DIAG_STAT_SPI_FAIL_BIT) |
+ BIT(ADIS16209_DIAG_STAT_FLASH_UPT_BIT) |
+ BIT(ADIS16209_DIAG_STAT_POWER_HIGH_BIT) |
+ BIT(ADIS16209_DIAG_STAT_POWER_LOW_BIT),
+};
+
+static int adis16209_probe(struct spi_device *spi)
+{
+ int ret;
+ struct adis *st;
+ struct iio_dev *indio_dev;
+
+ /* setup the industrialio driver allocated elements */
+ indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));
+ if (!indio_dev)
+ return -ENOMEM;
+ st = iio_priv(indio_dev);
+ /* this is only used for removal purposes */
+ spi_set_drvdata(spi, indio_dev);
+
+ indio_dev->name = spi->dev.driver->name;
+ indio_dev->dev.parent = &spi->dev;
+ indio_dev->info = &adis16209_info;
+ indio_dev->channels = adis16209_channels;
+ indio_dev->num_channels = ARRAY_SIZE(adis16209_channels);
+ indio_dev->modes = INDIO_DIRECT_MODE;
+
+ ret = adis_init(st, indio_dev, spi, &adis16209_data);
+ if (ret)
+ return ret;
+ ret = adis_setup_buffer_and_trigger(st, indio_dev, NULL);
+ if (ret)
+ return ret;
+
+ /* Get the device into a sane initial state */
+ ret = adis_initial_startup(st);
+ if (ret)
+ goto error_cleanup_buffer_trigger;
+ ret = iio_device_register(indio_dev);
+ if (ret)
+ goto error_cleanup_buffer_trigger;
+
+ return 0;
+
+error_cleanup_buffer_trigger:
+ adis_cleanup_buffer_and_trigger(st, indio_dev);
+ return ret;
+}
+
+static int adis16209_remove(struct spi_device *spi)
+{
+ struct iio_dev *indio_dev = spi_get_drvdata(spi);
+ struct adis *st = iio_priv(indio_dev);
+
+ iio_device_unregister(indio_dev);
+ adis_cleanup_buffer_and_trigger(st, indio_dev);
+
+ return 0;
+}
+
+static struct spi_driver adis16209_driver = {
+ .driver = {
+ .name = "adis16209",
+ .owner = THIS_MODULE,
+ },
+ .probe = adis16209_probe,
+ .remove = adis16209_remove,
+};
+module_spi_driver(adis16209_driver);
+
+MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>");
+MODULE_DESCRIPTION("Analog Devices ADIS16209 Digital Vibration Sensor driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("spi:adis16209");
diff --git a/drivers/staging/iio/accel/adis16220.h b/drivers/staging/iio/accel/adis16220.h
new file mode 100644
index 000000000..eab863311
--- /dev/null
+++ b/drivers/staging/iio/accel/adis16220.h
@@ -0,0 +1,140 @@
+#ifndef SPI_ADIS16220_H_
+#define SPI_ADIS16220_H_
+
+#include <linux/iio/imu/adis.h>
+
+#define ADIS16220_STARTUP_DELAY 220 /* ms */
+
+/* Flash memory write count */
+#define ADIS16220_FLASH_CNT 0x00
+/* Control, acceleration offset adjustment control */
+#define ADIS16220_ACCL_NULL 0x02
+/* Control, AIN1 offset adjustment control */
+#define ADIS16220_AIN1_NULL 0x04
+/* Control, AIN2 offset adjustment control */
+#define ADIS16220_AIN2_NULL 0x06
+/* Output, power supply during capture */
+#define ADIS16220_CAPT_SUPPLY 0x0A
+/* Output, temperature during capture */
+#define ADIS16220_CAPT_TEMP 0x0C
+/* Output, peak acceleration during capture */
+#define ADIS16220_CAPT_PEAKA 0x0E
+/* Output, peak AIN1 level during capture */
+#define ADIS16220_CAPT_PEAK1 0x10
+/* Output, peak AIN2 level during capture */
+#define ADIS16220_CAPT_PEAK2 0x12
+/* Output, capture buffer for acceleration */
+#define ADIS16220_CAPT_BUFA 0x14
+/* Output, capture buffer for AIN1 */
+#define ADIS16220_CAPT_BUF1 0x16
+/* Output, capture buffer for AIN2 */
+#define ADIS16220_CAPT_BUF2 0x18
+/* Control, capture buffer address pointer */
+#define ADIS16220_CAPT_PNTR 0x1A
+/* Control, capture control register */
+#define ADIS16220_CAPT_CTRL 0x1C
+/* Control, capture period (automatic mode) */
+#define ADIS16220_CAPT_PRD 0x1E
+/* Control, Alarm A, acceleration peak threshold */
+#define ADIS16220_ALM_MAGA 0x20
+/* Control, Alarm 1, AIN1 peak threshold */
+#define ADIS16220_ALM_MAG1 0x22
+/* Control, Alarm 2, AIN2 peak threshold */
+#define ADIS16220_ALM_MAG2 0x24
+/* Control, Alarm S, peak threshold */
+#define ADIS16220_ALM_MAGS 0x26
+/* Control, alarm configuration register */
+#define ADIS16220_ALM_CTRL 0x28
+/* Control, general I/O configuration */
+#define ADIS16220_GPIO_CTRL 0x32
+/* Control, self-test control, AIN configuration */
+#define ADIS16220_MSC_CTRL 0x34
+/* Control, digital I/O configuration */
+#define ADIS16220_DIO_CTRL 0x36
+/* Control, filter configuration */
+#define ADIS16220_AVG_CNT 0x38
+/* Status, system status */
+#define ADIS16220_DIAG_STAT 0x3C
+/* Control, system commands */
+#define ADIS16220_GLOB_CMD 0x3E
+/* Status, self-test response */
+#define ADIS16220_ST_DELTA 0x40
+/* Lot Identification Code 1 */
+#define ADIS16220_LOT_ID1 0x52
+/* Lot Identification Code 2 */
+#define ADIS16220_LOT_ID2 0x54
+/* Product identifier; convert to decimal = 16220 */
+#define ADIS16220_PROD_ID 0x56
+/* Serial number */
+#define ADIS16220_SERIAL_NUM 0x58
+
+#define ADIS16220_CAPTURE_SIZE 2048
+
+/* MSC_CTRL */
+#define ADIS16220_MSC_CTRL_SELF_TEST_EN BIT(8)
+#define ADIS16220_MSC_CTRL_POWER_SUP_COM_AIN1 BIT(1)
+#define ADIS16220_MSC_CTRL_POWER_SUP_COM_AIN2 BIT(0)
+
+/* DIO_CTRL */
+#define ADIS16220_MSC_CTRL_DIO2_BUSY_IND (BIT(5) | BIT(4))
+#define ADIS16220_MSC_CTRL_DIO1_BUSY_IND (BIT(3) | BIT(2))
+#define ADIS16220_MSC_CTRL_DIO2_ACT_HIGH BIT(1)
+#define ADIS16220_MSC_CTRL_DIO1_ACT_HIGH BIT(0)
+
+/* DIAG_STAT */
+/* AIN2 sample > ALM_MAG2 */
+#define ADIS16220_DIAG_STAT_ALM_MAG2 BIT(14)
+/* AIN1 sample > ALM_MAG1 */
+#define ADIS16220_DIAG_STAT_ALM_MAG1 BIT(13)
+/* Acceleration sample > ALM_MAGA */
+#define ADIS16220_DIAG_STAT_ALM_MAGA BIT(12)
+/* Error condition programmed into ALM_MAGS[11:0] and ALM_CTRL[5:4] is true */
+#define ADIS16220_DIAG_STAT_ALM_MAGS BIT(11)
+/* |Peak value in AIN2 data capture| > ALM_MAG2 */
+#define ADIS16220_DIAG_STAT_PEAK_AIN2 BIT(10)
+/* |Peak value in AIN1 data capture| > ALM_MAG1 */
+#define ADIS16220_DIAG_STAT_PEAK_AIN1 BIT(9)
+/* |Peak value in acceleration data capture| > ALM_MAGA */
+#define ADIS16220_DIAG_STAT_PEAK_ACCEL BIT(8)
+/* Data ready, capture complete */
+#define ADIS16220_DIAG_STAT_DATA_RDY BIT(7)
+#define ADIS16220_DIAG_STAT_FLASH_CHK BIT(6)
+#define ADIS16220_DIAG_STAT_SELF_TEST BIT(5)
+/* Capture period violation/interruption */
+#define ADIS16220_DIAG_STAT_VIOLATION_BIT 4
+/* SPI communications failure */
+#define ADIS16220_DIAG_STAT_SPI_FAIL_BIT 3
+/* Flash update failure */
+#define ADIS16220_DIAG_STAT_FLASH_UPT_BIT 2
+/* Power supply above 3.625 V */
+#define ADIS16220_DIAG_STAT_POWER_HIGH_BIT 1
+/* Power supply below 3.15 V */
+#define ADIS16220_DIAG_STAT_POWER_LOW_BIT 0
+
+/* GLOB_CMD */
+#define ADIS16220_GLOB_CMD_SW_RESET BIT(7)
+#define ADIS16220_GLOB_CMD_SELF_TEST BIT(2)
+#define ADIS16220_GLOB_CMD_PWR_DOWN BIT(1)
+
+#define ADIS16220_MAX_TX 2048
+#define ADIS16220_MAX_RX 2048
+
+#define ADIS16220_SPI_BURST (u32)(1000 * 1000)
+#define ADIS16220_SPI_FAST (u32)(2000 * 1000)
+
+/**
+ * struct adis16220_state - device instance specific data
+ * @adis: adis device
+ * @tx: transmit buffer
+ * @rx: receive buffer
+ * @buf_lock: mutex to protect tx and rx
+ **/
+struct adis16220_state {
+ struct adis adis;
+
+ struct mutex buf_lock;
+ u8 tx[ADIS16220_MAX_TX] ____cacheline_aligned;
+ u8 rx[ADIS16220_MAX_RX];
+};
+
+#endif /* SPI_ADIS16220_H_ */
diff --git a/drivers/staging/iio/accel/adis16220_core.c b/drivers/staging/iio/accel/adis16220_core.c
new file mode 100644
index 000000000..e46a91c69
--- /dev/null
+++ b/drivers/staging/iio/accel/adis16220_core.c
@@ -0,0 +1,495 @@
+/*
+ * ADIS16220 Programmable Digital Vibration Sensor driver
+ *
+ * Copyright 2010 Analog Devices Inc.
+ *
+ * Licensed under the GPL-2 or later.
+ */
+
+#include <linux/delay.h>
+#include <linux/mutex.h>
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/spi/spi.h>
+#include <linux/slab.h>
+#include <linux/sysfs.h>
+#include <linux/module.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+
+#include "adis16220.h"
+
+static ssize_t adis16220_read_16bit(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct adis16220_state *st = iio_priv(indio_dev);
+ ssize_t ret;
+ u16 val;
+
+ /* Take the iio_dev status lock */
+ mutex_lock(&indio_dev->mlock);
+ ret = adis_read_reg_16(&st->adis, this_attr->address, &val);
+ mutex_unlock(&indio_dev->mlock);
+ if (ret)
+ return ret;
+ return sprintf(buf, "%u\n", val);
+}
+
+static ssize_t adis16220_write_16bit(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t len)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
+ struct adis16220_state *st = iio_priv(indio_dev);
+ int ret;
+ u16 val;
+
+ ret = kstrtou16(buf, 10, &val);
+ if (ret)
+ goto error_ret;
+ ret = adis_write_reg_16(&st->adis, this_attr->address, val);
+
+error_ret:
+ return ret ? ret : len;
+}
+
+static int adis16220_capture(struct iio_dev *indio_dev)
+{
+ struct adis16220_state *st = iio_priv(indio_dev);
+ int ret;
+
+ /* initiates a manual data capture */
+ ret = adis_write_reg_16(&st->adis, ADIS16220_GLOB_CMD, 0xBF08);
+ if (ret)
+ dev_err(&indio_dev->dev, "problem beginning capture");
+
+ usleep_range(10000, 11000); /* delay for capture to finish */
+
+ return ret;
+}
+
+static ssize_t adis16220_write_capture(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t len)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ bool val;
+ int ret;
+
+ ret = strtobool(buf, &val);
+ if (ret)
+ return ret;
+ if (!val)
+ return -EINVAL;
+ ret = adis16220_capture(indio_dev);
+ if (ret)
+ return ret;
+
+ return len;
+}
+
+static ssize_t adis16220_capture_buffer_read(struct iio_dev *indio_dev,
+ char *buf,
+ loff_t off,
+ size_t count,
+ int addr)
+{
+ struct adis16220_state *st = iio_priv(indio_dev);
+ struct spi_transfer xfers[] = {
+ {
+ .tx_buf = st->tx,
+ .bits_per_word = 8,
+ .len = 2,
+ .cs_change = 1,
+ .delay_usecs = 25,
+ }, {
+ .tx_buf = st->tx,
+ .rx_buf = st->rx,
+ .bits_per_word = 8,
+ .cs_change = 1,
+ .delay_usecs = 25,
+ },
+ };
+ int ret;
+ int i;
+
+ if (unlikely(!count))
+ return count;
+
+ if ((off >= ADIS16220_CAPTURE_SIZE) || (count & 1) || (off & 1))
+ return -EINVAL;
+
+ if (off + count > ADIS16220_CAPTURE_SIZE)
+ count = ADIS16220_CAPTURE_SIZE - off;
+
+ /* write the begin position of capture buffer */
+ ret = adis_write_reg_16(&st->adis,
+ ADIS16220_CAPT_PNTR,
+ off > 1);
+ if (ret)
+ return -EIO;
+
+ /* read count/2 values from capture buffer */
+ mutex_lock(&st->buf_lock);
+
+ for (i = 0; i < count; i += 2) {
+ st->tx[i] = ADIS_READ_REG(addr);
+ st->tx[i + 1] = 0;
+ }
+ xfers[1].len = count;
+
+ ret = spi_sync_transfer(st->adis.spi, xfers, ARRAY_SIZE(xfers));
+ if (ret) {
+ mutex_unlock(&st->buf_lock);
+ return -EIO;
+ }
+
+ memcpy(buf, st->rx, count);
+
+ mutex_unlock(&st->buf_lock);
+ return count;
+}
+
+static ssize_t adis16220_accel_bin_read(struct file *filp, struct kobject *kobj,
+ struct bin_attribute *attr,
+ char *buf,
+ loff_t off,
+ size_t count)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(kobj_to_dev(kobj));
+
+ return adis16220_capture_buffer_read(indio_dev, buf,
+ off, count,
+ ADIS16220_CAPT_BUFA);
+}
+
+static struct bin_attribute accel_bin = {
+ .attr = {
+ .name = "accel_bin",
+ .mode = S_IRUGO,
+ },
+ .read = adis16220_accel_bin_read,
+ .size = ADIS16220_CAPTURE_SIZE,
+};
+
+static ssize_t adis16220_adc1_bin_read(struct file *filp, struct kobject *kobj,
+ struct bin_attribute *attr,
+ char *buf, loff_t off,
+ size_t count)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(kobj_to_dev(kobj));
+
+ return adis16220_capture_buffer_read(indio_dev, buf,
+ off, count,
+ ADIS16220_CAPT_BUF1);
+}
+
+static struct bin_attribute adc1_bin = {
+ .attr = {
+ .name = "in0_bin",
+ .mode = S_IRUGO,
+ },
+ .read = adis16220_adc1_bin_read,
+ .size = ADIS16220_CAPTURE_SIZE,
+};
+
+static ssize_t adis16220_adc2_bin_read(struct file *filp, struct kobject *kobj,
+ struct bin_attribute *attr,
+ char *buf, loff_t off,
+ size_t count)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(kobj_to_dev(kobj));
+
+ return adis16220_capture_buffer_read(indio_dev, buf,
+ off, count,
+ ADIS16220_CAPT_BUF2);
+}
+
+static struct bin_attribute adc2_bin = {
+ .attr = {
+ .name = "in1_bin",
+ .mode = S_IRUGO,
+ },
+ .read = adis16220_adc2_bin_read,
+ .size = ADIS16220_CAPTURE_SIZE,
+};
+
+#define IIO_DEV_ATTR_CAPTURE(_store) \
+ IIO_DEVICE_ATTR(capture, S_IWUSR, NULL, _store, 0)
+
+static IIO_DEV_ATTR_CAPTURE(adis16220_write_capture);
+
+#define IIO_DEV_ATTR_CAPTURE_COUNT(_mode, _show, _store, _addr) \
+ IIO_DEVICE_ATTR(capture_count, _mode, _show, _store, _addr)
+
+static IIO_DEV_ATTR_CAPTURE_COUNT(S_IWUSR | S_IRUGO,
+ adis16220_read_16bit,
+ adis16220_write_16bit,
+ ADIS16220_CAPT_PNTR);
+
+enum adis16220_channel {
+ in_supply, in_1, in_2, accel, temp
+};
+
+struct adis16220_address_spec {
+ u8 addr;
+ u8 bits;
+ bool sign;
+};
+
+/* Address / bits / signed */
+static const struct adis16220_address_spec adis16220_addresses[][3] = {
+ [in_supply] = { { ADIS16220_CAPT_SUPPLY, 12, 0 }, },
+ [in_1] = { { ADIS16220_CAPT_BUF1, 16, 1 },
+ { ADIS16220_AIN1_NULL, 16, 1 },
+ { ADIS16220_CAPT_PEAK1, 16, 1 }, },
+ [in_2] = { { ADIS16220_CAPT_BUF2, 16, 1 },
+ { ADIS16220_AIN2_NULL, 16, 1 },
+ { ADIS16220_CAPT_PEAK2, 16, 1 }, },
+ [accel] = { { ADIS16220_CAPT_BUFA, 16, 1 },
+ { ADIS16220_ACCL_NULL, 16, 1 },
+ { ADIS16220_CAPT_PEAKA, 16, 1 }, },
+ [temp] = { { ADIS16220_CAPT_TEMP, 12, 0 }, }
+};
+
+static int adis16220_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2,
+ long mask)
+{
+ struct adis16220_state *st = iio_priv(indio_dev);
+ const struct adis16220_address_spec *addr;
+ int ret = -EINVAL;
+ int addrind = 0;
+ u16 uval;
+ s16 sval;
+ u8 bits;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ addrind = 0;
+ break;
+ case IIO_CHAN_INFO_OFFSET:
+ if (chan->type == IIO_TEMP) {
+ *val = 25000 / -470 - 1278; /* 25 C = 1278 */
+ return IIO_VAL_INT;
+ }
+ addrind = 1;
+ break;
+ case IIO_CHAN_INFO_PEAK:
+ addrind = 2;
+ break;
+ case IIO_CHAN_INFO_SCALE:
+ switch (chan->type) {
+ case IIO_TEMP:
+ *val = -470; /* -0.47 C */
+ *val2 = 0;
+ return IIO_VAL_INT_PLUS_MICRO;
+ case IIO_ACCEL:
+ *val2 = IIO_G_TO_M_S_2(19073); /* 19.073 g */
+ return IIO_VAL_INT_PLUS_MICRO;
+ case IIO_VOLTAGE:
+ if (chan->channel == 0) {
+ *val = 1;
+ *val2 = 220700; /* 1.2207 mV */
+ } else {
+ /* Should really be dependent on VDD */
+ *val2 = 305180; /* 305.18 uV */
+ }
+ return IIO_VAL_INT_PLUS_MICRO;
+ default:
+ return -EINVAL;
+ }
+ default:
+ return -EINVAL;
+ }
+ addr = &adis16220_addresses[chan->address][addrind];
+ if (addr->sign) {
+ ret = adis_read_reg_16(&st->adis, addr->addr, &sval);
+ if (ret)
+ return ret;
+ bits = addr->bits;
+ sval &= (1 << bits) - 1;
+ sval = (s16)(sval << (16 - bits)) >> (16 - bits);
+ *val = sval;
+ return IIO_VAL_INT;
+ }
+ ret = adis_read_reg_16(&st->adis, addr->addr, &uval);
+ if (ret)
+ return ret;
+ bits = addr->bits;
+ uval &= (1 << bits) - 1;
+ *val = uval;
+ return IIO_VAL_INT;
+}
+
+static const struct iio_chan_spec adis16220_channels[] = {
+ {
+ .type = IIO_VOLTAGE,
+ .indexed = 1,
+ .channel = 0,
+ .extend_name = "supply",
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+ BIT(IIO_CHAN_INFO_SCALE),
+ .address = in_supply,
+ }, {
+ .type = IIO_ACCEL,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+ BIT(IIO_CHAN_INFO_OFFSET) |
+ BIT(IIO_CHAN_INFO_SCALE) |
+ BIT(IIO_CHAN_INFO_PEAK),
+ .address = accel,
+ }, {
+ .type = IIO_TEMP,
+ .indexed = 1,
+ .channel = 0,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+ BIT(IIO_CHAN_INFO_OFFSET) |
+ BIT(IIO_CHAN_INFO_SCALE),
+ .address = temp,
+ }, {
+ .type = IIO_VOLTAGE,
+ .indexed = 1,
+ .channel = 1,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+ BIT(IIO_CHAN_INFO_OFFSET) |
+ BIT(IIO_CHAN_INFO_SCALE),
+ .address = in_1,
+ }, {
+ .type = IIO_VOLTAGE,
+ .indexed = 1,
+ .channel = 2,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
+ .address = in_2,
+ }
+};
+
+static struct attribute *adis16220_attributes[] = {
+ &iio_dev_attr_capture.dev_attr.attr,
+ &iio_dev_attr_capture_count.dev_attr.attr,
+ NULL
+};
+
+static const struct attribute_group adis16220_attribute_group = {
+ .attrs = adis16220_attributes,
+};
+
+static const struct iio_info adis16220_info = {
+ .attrs = &adis16220_attribute_group,
+ .driver_module = THIS_MODULE,
+ .read_raw = &adis16220_read_raw,
+};
+
+static const char * const adis16220_status_error_msgs[] = {
+ [ADIS16220_DIAG_STAT_VIOLATION_BIT] = "Capture period violation/interruption",
+ [ADIS16220_DIAG_STAT_SPI_FAIL_BIT] = "SPI failure",
+ [ADIS16220_DIAG_STAT_FLASH_UPT_BIT] = "Flash update failed",
+ [ADIS16220_DIAG_STAT_POWER_HIGH_BIT] = "Power supply above 3.625V",
+ [ADIS16220_DIAG_STAT_POWER_LOW_BIT] = "Power supply below 3.15V",
+};
+
+static const struct adis_data adis16220_data = {
+ .read_delay = 35,
+ .write_delay = 35,
+ .msc_ctrl_reg = ADIS16220_MSC_CTRL,
+ .glob_cmd_reg = ADIS16220_GLOB_CMD,
+ .diag_stat_reg = ADIS16220_DIAG_STAT,
+
+ .self_test_mask = ADIS16220_MSC_CTRL_SELF_TEST_EN,
+ .startup_delay = ADIS16220_STARTUP_DELAY,
+
+ .status_error_msgs = adis16220_status_error_msgs,
+ .status_error_mask = BIT(ADIS16220_DIAG_STAT_VIOLATION_BIT) |
+ BIT(ADIS16220_DIAG_STAT_SPI_FAIL_BIT) |
+ BIT(ADIS16220_DIAG_STAT_FLASH_UPT_BIT) |
+ BIT(ADIS16220_DIAG_STAT_POWER_HIGH_BIT) |
+ BIT(ADIS16220_DIAG_STAT_POWER_LOW_BIT),
+};
+
+static int adis16220_probe(struct spi_device *spi)
+{
+ int ret;
+ struct adis16220_state *st;
+ struct iio_dev *indio_dev;
+
+ /* setup the industrialio driver allocated elements */
+ indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ st = iio_priv(indio_dev);
+ /* this is only used for removal purposes */
+ spi_set_drvdata(spi, indio_dev);
+
+ indio_dev->name = spi->dev.driver->name;
+ indio_dev->dev.parent = &spi->dev;
+ indio_dev->info = &adis16220_info;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->channels = adis16220_channels;
+ indio_dev->num_channels = ARRAY_SIZE(adis16220_channels);
+
+ ret = devm_iio_device_register(&spi->dev, indio_dev);
+ if (ret)
+ return ret;
+
+ ret = sysfs_create_bin_file(&indio_dev->dev.kobj, &accel_bin);
+ if (ret)
+ return ret;
+
+ ret = sysfs_create_bin_file(&indio_dev->dev.kobj, &adc1_bin);
+ if (ret)
+ goto error_rm_accel_bin;
+
+ ret = sysfs_create_bin_file(&indio_dev->dev.kobj, &adc2_bin);
+ if (ret)
+ goto error_rm_adc1_bin;
+
+ ret = adis_init(&st->adis, indio_dev, spi, &adis16220_data);
+ if (ret)
+ goto error_rm_adc2_bin;
+ /* Get the device into a sane initial state */
+ ret = adis_initial_startup(&st->adis);
+ if (ret)
+ goto error_rm_adc2_bin;
+ return 0;
+
+error_rm_adc2_bin:
+ sysfs_remove_bin_file(&indio_dev->dev.kobj, &adc2_bin);
+error_rm_adc1_bin:
+ sysfs_remove_bin_file(&indio_dev->dev.kobj, &adc1_bin);
+error_rm_accel_bin:
+ sysfs_remove_bin_file(&indio_dev->dev.kobj, &accel_bin);
+ return ret;
+}
+
+static int adis16220_remove(struct spi_device *spi)
+{
+ struct iio_dev *indio_dev = spi_get_drvdata(spi);
+
+ sysfs_remove_bin_file(&indio_dev->dev.kobj, &adc2_bin);
+ sysfs_remove_bin_file(&indio_dev->dev.kobj, &adc1_bin);
+ sysfs_remove_bin_file(&indio_dev->dev.kobj, &accel_bin);
+
+ return 0;
+}
+
+static struct spi_driver adis16220_driver = {
+ .driver = {
+ .name = "adis16220",
+ .owner = THIS_MODULE,
+ },
+ .probe = adis16220_probe,
+ .remove = adis16220_remove,
+};
+module_spi_driver(adis16220_driver);
+
+MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>");
+MODULE_DESCRIPTION("Analog Devices ADIS16220 Digital Vibration Sensor");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("spi:adis16220");
diff --git a/drivers/staging/iio/accel/adis16240.h b/drivers/staging/iio/accel/adis16240.h
new file mode 100644
index 000000000..66b5ad2f4
--- /dev/null
+++ b/drivers/staging/iio/accel/adis16240.h
@@ -0,0 +1,129 @@
+#ifndef SPI_ADIS16240_H_
+#define SPI_ADIS16240_H_
+
+#define ADIS16240_STARTUP_DELAY 220 /* ms */
+
+/* Flash memory write count */
+#define ADIS16240_FLASH_CNT 0x00
+/* Output, power supply */
+#define ADIS16240_SUPPLY_OUT 0x02
+/* Output, x-axis accelerometer */
+#define ADIS16240_XACCL_OUT 0x04
+/* Output, y-axis accelerometer */
+#define ADIS16240_YACCL_OUT 0x06
+/* Output, z-axis accelerometer */
+#define ADIS16240_ZACCL_OUT 0x08
+/* Output, auxiliary ADC input */
+#define ADIS16240_AUX_ADC 0x0A
+/* Output, temperature */
+#define ADIS16240_TEMP_OUT 0x0C
+/* Output, x-axis acceleration peak */
+#define ADIS16240_XPEAK_OUT 0x0E
+/* Output, y-axis acceleration peak */
+#define ADIS16240_YPEAK_OUT 0x10
+/* Output, z-axis acceleration peak */
+#define ADIS16240_ZPEAK_OUT 0x12
+/* Output, sum-of-squares acceleration peak */
+#define ADIS16240_XYZPEAK_OUT 0x14
+/* Output, Capture Buffer 1, X and Y acceleration */
+#define ADIS16240_CAPT_BUF1 0x16
+/* Output, Capture Buffer 2, Z acceleration */
+#define ADIS16240_CAPT_BUF2 0x18
+/* Diagnostic, error flags */
+#define ADIS16240_DIAG_STAT 0x1A
+/* Diagnostic, event counter */
+#define ADIS16240_EVNT_CNTR 0x1C
+/* Diagnostic, check sum value from firmware test */
+#define ADIS16240_CHK_SUM 0x1E
+/* Calibration, x-axis acceleration offset adjustment */
+#define ADIS16240_XACCL_OFF 0x20
+/* Calibration, y-axis acceleration offset adjustment */
+#define ADIS16240_YACCL_OFF 0x22
+/* Calibration, z-axis acceleration offset adjustment */
+#define ADIS16240_ZACCL_OFF 0x24
+/* Clock, hour and minute */
+#define ADIS16240_CLK_TIME 0x2E
+/* Clock, month and day */
+#define ADIS16240_CLK_DATE 0x30
+/* Clock, year */
+#define ADIS16240_CLK_YEAR 0x32
+/* Wake-up setting, hour and minute */
+#define ADIS16240_WAKE_TIME 0x34
+/* Wake-up setting, month and day */
+#define ADIS16240_WAKE_DATE 0x36
+/* Alarm 1 amplitude threshold */
+#define ADIS16240_ALM_MAG1 0x38
+/* Alarm 2 amplitude threshold */
+#define ADIS16240_ALM_MAG2 0x3A
+/* Alarm control */
+#define ADIS16240_ALM_CTRL 0x3C
+/* Capture, external trigger control */
+#define ADIS16240_XTRIG_CTRL 0x3E
+/* Capture, address pointer */
+#define ADIS16240_CAPT_PNTR 0x40
+/* Capture, configuration and control */
+#define ADIS16240_CAPT_CTRL 0x42
+/* General-purpose digital input/output control */
+#define ADIS16240_GPIO_CTRL 0x44
+/* Miscellaneous control */
+#define ADIS16240_MSC_CTRL 0x46
+/* Internal sample period (rate) control */
+#define ADIS16240_SMPL_PRD 0x48
+/* System command */
+#define ADIS16240_GLOB_CMD 0x4A
+
+/* MSC_CTRL */
+/* Enables sum-of-squares output (XYZPEAK_OUT) */
+#define ADIS16240_MSC_CTRL_XYZPEAK_OUT_EN BIT(15)
+/* Enables peak tracking output (XPEAK_OUT, YPEAK_OUT, and ZPEAK_OUT) */
+#define ADIS16240_MSC_CTRL_X_Y_ZPEAK_OUT_EN BIT(14)
+/* Self-test enable: 1 = apply electrostatic force, 0 = disabled */
+#define ADIS16240_MSC_CTRL_SELF_TEST_EN BIT(8)
+/* Data-ready enable: 1 = enabled, 0 = disabled */
+#define ADIS16240_MSC_CTRL_DATA_RDY_EN BIT(2)
+/* Data-ready polarity: 1 = active high, 0 = active low */
+#define ADIS16240_MSC_CTRL_ACTIVE_HIGH BIT(1)
+/* Data-ready line selection: 1 = DIO2, 0 = DIO1 */
+#define ADIS16240_MSC_CTRL_DATA_RDY_DIO2 BIT(0)
+
+/* DIAG_STAT */
+/* Alarm 2 status: 1 = alarm active, 0 = alarm inactive */
+#define ADIS16240_DIAG_STAT_ALARM2 BIT(9)
+/* Alarm 1 status: 1 = alarm active, 0 = alarm inactive */
+#define ADIS16240_DIAG_STAT_ALARM1 BIT(8)
+/* Capture buffer full: 1 = capture buffer is full */
+#define ADIS16240_DIAG_STAT_CPT_BUF_FUL BIT(7)
+/* Flash test, checksum flag: 1 = mismatch, 0 = match */
+#define ADIS16240_DIAG_STAT_CHKSUM BIT(6)
+/* Power-on, self-test flag: 1 = failure, 0 = pass */
+#define ADIS16240_DIAG_STAT_PWRON_FAIL_BIT 5
+/* Power-on self-test: 1 = in-progress, 0 = complete */
+#define ADIS16240_DIAG_STAT_PWRON_BUSY BIT(4)
+/* SPI communications failure */
+#define ADIS16240_DIAG_STAT_SPI_FAIL_BIT 3
+/* Flash update failure */
+#define ADIS16240_DIAG_STAT_FLASH_UPT_BIT 2
+/* Power supply above 3.625 V */
+#define ADIS16240_DIAG_STAT_POWER_HIGH_BIT 1
+ /* Power supply below 3.15 V */
+#define ADIS16240_DIAG_STAT_POWER_LOW_BIT 0
+
+/* GLOB_CMD */
+#define ADIS16240_GLOB_CMD_RESUME BIT(8)
+#define ADIS16240_GLOB_CMD_SW_RESET BIT(7)
+#define ADIS16240_GLOB_CMD_STANDBY BIT(2)
+
+#define ADIS16240_ERROR_ACTIVE BIT(14)
+
+/* At the moment triggers are only used for ring buffer
+ * filling. This may change!
+ */
+
+#define ADIS16240_SCAN_ACC_X 0
+#define ADIS16240_SCAN_ACC_Y 1
+#define ADIS16240_SCAN_ACC_Z 2
+#define ADIS16240_SCAN_SUPPLY 3
+#define ADIS16240_SCAN_AUX_ADC 4
+#define ADIS16240_SCAN_TEMP 5
+
+#endif /* SPI_ADIS16240_H_ */
diff --git a/drivers/staging/iio/accel/adis16240_core.c b/drivers/staging/iio/accel/adis16240_core.c
new file mode 100644
index 000000000..cb074e864
--- /dev/null
+++ b/drivers/staging/iio/accel/adis16240_core.c
@@ -0,0 +1,301 @@
+/*
+ * ADIS16240 Programmable Impact Sensor and Recorder driver
+ *
+ * Copyright 2010 Analog Devices Inc.
+ *
+ * Licensed under the GPL-2 or later.
+ */
+
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/gpio.h>
+#include <linux/delay.h>
+#include <linux/mutex.h>
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/spi/spi.h>
+#include <linux/slab.h>
+#include <linux/sysfs.h>
+#include <linux/list.h>
+#include <linux/module.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/imu/adis.h>
+
+#include "adis16240.h"
+
+static ssize_t adis16240_spi_read_signed(struct device *dev,
+ struct device_attribute *attr,
+ char *buf,
+ unsigned bits)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct adis *st = iio_priv(indio_dev);
+ int ret;
+ s16 val = 0;
+ unsigned shift = 16 - bits;
+ struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
+
+ ret = adis_read_reg_16(st,
+ this_attr->address, (u16 *)&val);
+ if (ret)
+ return ret;
+
+ if (val & ADIS16240_ERROR_ACTIVE)
+ adis_check_status(st);
+
+ val = (s16)(val << shift) >> shift;
+ return sprintf(buf, "%d\n", val);
+}
+
+static ssize_t adis16240_read_12bit_signed(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ ssize_t ret;
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+
+ /* Take the iio_dev status lock */
+ mutex_lock(&indio_dev->mlock);
+ ret = adis16240_spi_read_signed(dev, attr, buf, 12);
+ mutex_unlock(&indio_dev->mlock);
+
+ return ret;
+}
+
+static IIO_DEVICE_ATTR(in_accel_xyz_squared_peak_raw, S_IRUGO,
+ adis16240_read_12bit_signed, NULL,
+ ADIS16240_XYZPEAK_OUT);
+
+static IIO_CONST_ATTR_SAMP_FREQ_AVAIL("4096");
+
+static const u8 adis16240_addresses[][2] = {
+ [ADIS16240_SCAN_ACC_X] = { ADIS16240_XACCL_OFF, ADIS16240_XPEAK_OUT },
+ [ADIS16240_SCAN_ACC_Y] = { ADIS16240_YACCL_OFF, ADIS16240_YPEAK_OUT },
+ [ADIS16240_SCAN_ACC_Z] = { ADIS16240_ZACCL_OFF, ADIS16240_ZPEAK_OUT },
+};
+
+static int adis16240_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2,
+ long mask)
+{
+ struct adis *st = iio_priv(indio_dev);
+ int ret;
+ int bits;
+ u8 addr;
+ s16 val16;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ return adis_single_conversion(indio_dev, chan,
+ ADIS16240_ERROR_ACTIVE, val);
+ case IIO_CHAN_INFO_SCALE:
+ switch (chan->type) {
+ case IIO_VOLTAGE:
+ if (chan->channel == 0) {
+ *val = 4;
+ *val2 = 880000; /* 4.88 mV */
+ return IIO_VAL_INT_PLUS_MICRO;
+ }
+ return -EINVAL;
+ case IIO_TEMP:
+ *val = 244; /* 0.244 C */
+ *val2 = 0;
+ return IIO_VAL_INT_PLUS_MICRO;
+ case IIO_ACCEL:
+ *val = 0;
+ *val2 = IIO_G_TO_M_S_2(51400); /* 51.4 mg */
+ return IIO_VAL_INT_PLUS_MICRO;
+ default:
+ return -EINVAL;
+ }
+ break;
+ case IIO_CHAN_INFO_PEAK_SCALE:
+ *val = 0;
+ *val2 = IIO_G_TO_M_S_2(51400); /* 51.4 mg */
+ return IIO_VAL_INT_PLUS_MICRO;
+ case IIO_CHAN_INFO_OFFSET:
+ *val = 25000 / 244 - 0x133; /* 25 C = 0x133 */
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_CALIBBIAS:
+ bits = 10;
+ mutex_lock(&indio_dev->mlock);
+ addr = adis16240_addresses[chan->scan_index][0];
+ ret = adis_read_reg_16(st, addr, &val16);
+ if (ret) {
+ mutex_unlock(&indio_dev->mlock);
+ return ret;
+ }
+ val16 &= (1 << bits) - 1;
+ val16 = (s16)(val16 << (16 - bits)) >> (16 - bits);
+ *val = val16;
+ mutex_unlock(&indio_dev->mlock);
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_PEAK:
+ bits = 10;
+ mutex_lock(&indio_dev->mlock);
+ addr = adis16240_addresses[chan->scan_index][1];
+ ret = adis_read_reg_16(st, addr, &val16);
+ if (ret) {
+ mutex_unlock(&indio_dev->mlock);
+ return ret;
+ }
+ val16 &= (1 << bits) - 1;
+ val16 = (s16)(val16 << (16 - bits)) >> (16 - bits);
+ *val = val16;
+ mutex_unlock(&indio_dev->mlock);
+ return IIO_VAL_INT;
+ }
+ return -EINVAL;
+}
+
+static int adis16240_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int val,
+ int val2,
+ long mask)
+{
+ struct adis *st = iio_priv(indio_dev);
+ int bits = 10;
+ s16 val16;
+ u8 addr;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_CALIBBIAS:
+ val16 = val & ((1 << bits) - 1);
+ addr = adis16240_addresses[chan->scan_index][0];
+ return adis_write_reg_16(st, addr, val16);
+ }
+ return -EINVAL;
+}
+
+static const struct iio_chan_spec adis16240_channels[] = {
+ ADIS_SUPPLY_CHAN(ADIS16240_SUPPLY_OUT, ADIS16240_SCAN_SUPPLY, 0, 10),
+ ADIS_AUX_ADC_CHAN(ADIS16240_AUX_ADC, ADIS16240_SCAN_AUX_ADC, 0, 10),
+ ADIS_ACCEL_CHAN(X, ADIS16240_XACCL_OUT, ADIS16240_SCAN_ACC_X,
+ BIT(IIO_CHAN_INFO_CALIBBIAS) | BIT(IIO_CHAN_INFO_PEAK),
+ 0, 10),
+ ADIS_ACCEL_CHAN(Y, ADIS16240_YACCL_OUT, ADIS16240_SCAN_ACC_Y,
+ BIT(IIO_CHAN_INFO_CALIBBIAS) | BIT(IIO_CHAN_INFO_PEAK),
+ 0, 10),
+ ADIS_ACCEL_CHAN(Z, ADIS16240_ZACCL_OUT, ADIS16240_SCAN_ACC_Z,
+ BIT(IIO_CHAN_INFO_CALIBBIAS) | BIT(IIO_CHAN_INFO_PEAK),
+ 0, 10),
+ ADIS_TEMP_CHAN(ADIS16240_TEMP_OUT, ADIS16240_SCAN_TEMP, 0, 10),
+ IIO_CHAN_SOFT_TIMESTAMP(6)
+};
+
+static struct attribute *adis16240_attributes[] = {
+ &iio_dev_attr_in_accel_xyz_squared_peak_raw.dev_attr.attr,
+ &iio_const_attr_sampling_frequency_available.dev_attr.attr,
+ NULL
+};
+
+static const struct attribute_group adis16240_attribute_group = {
+ .attrs = adis16240_attributes,
+};
+
+static const struct iio_info adis16240_info = {
+ .attrs = &adis16240_attribute_group,
+ .read_raw = &adis16240_read_raw,
+ .write_raw = &adis16240_write_raw,
+ .update_scan_mode = adis_update_scan_mode,
+ .driver_module = THIS_MODULE,
+};
+
+static const char * const adis16240_status_error_msgs[] = {
+ [ADIS16240_DIAG_STAT_PWRON_FAIL_BIT] = "Power on, self-test failed",
+ [ADIS16240_DIAG_STAT_SPI_FAIL_BIT] = "SPI failure",
+ [ADIS16240_DIAG_STAT_FLASH_UPT_BIT] = "Flash update failed",
+ [ADIS16240_DIAG_STAT_POWER_HIGH_BIT] = "Power supply above 3.625V",
+ [ADIS16240_DIAG_STAT_POWER_LOW_BIT] = "Power supply below 2.225V",
+};
+
+static const struct adis_data adis16240_data = {
+ .write_delay = 35,
+ .read_delay = 35,
+ .msc_ctrl_reg = ADIS16240_MSC_CTRL,
+ .glob_cmd_reg = ADIS16240_GLOB_CMD,
+ .diag_stat_reg = ADIS16240_DIAG_STAT,
+
+ .self_test_mask = ADIS16240_MSC_CTRL_SELF_TEST_EN,
+ .startup_delay = ADIS16240_STARTUP_DELAY,
+
+ .status_error_msgs = adis16240_status_error_msgs,
+ .status_error_mask = BIT(ADIS16240_DIAG_STAT_PWRON_FAIL_BIT) |
+ BIT(ADIS16240_DIAG_STAT_SPI_FAIL_BIT) |
+ BIT(ADIS16240_DIAG_STAT_FLASH_UPT_BIT) |
+ BIT(ADIS16240_DIAG_STAT_POWER_HIGH_BIT) |
+ BIT(ADIS16240_DIAG_STAT_POWER_LOW_BIT),
+};
+
+static int adis16240_probe(struct spi_device *spi)
+{
+ int ret;
+ struct adis *st;
+ struct iio_dev *indio_dev;
+
+ /* setup the industrialio driver allocated elements */
+ indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));
+ if (!indio_dev)
+ return -ENOMEM;
+ st = iio_priv(indio_dev);
+ /* this is only used for removal purposes */
+ spi_set_drvdata(spi, indio_dev);
+
+ indio_dev->name = spi->dev.driver->name;
+ indio_dev->dev.parent = &spi->dev;
+ indio_dev->info = &adis16240_info;
+ indio_dev->channels = adis16240_channels;
+ indio_dev->num_channels = ARRAY_SIZE(adis16240_channels);
+ indio_dev->modes = INDIO_DIRECT_MODE;
+
+ ret = adis_init(st, indio_dev, spi, &adis16240_data);
+ if (ret)
+ return ret;
+ ret = adis_setup_buffer_and_trigger(st, indio_dev, NULL);
+ if (ret)
+ return ret;
+
+ /* Get the device into a sane initial state */
+ ret = adis_initial_startup(st);
+ if (ret)
+ goto error_cleanup_buffer_trigger;
+ ret = iio_device_register(indio_dev);
+ if (ret)
+ goto error_cleanup_buffer_trigger;
+ return 0;
+
+error_cleanup_buffer_trigger:
+ adis_cleanup_buffer_and_trigger(st, indio_dev);
+ return ret;
+}
+
+static int adis16240_remove(struct spi_device *spi)
+{
+ struct iio_dev *indio_dev = spi_get_drvdata(spi);
+ struct adis *st = iio_priv(indio_dev);
+
+ iio_device_unregister(indio_dev);
+ adis_cleanup_buffer_and_trigger(st, indio_dev);
+
+ return 0;
+}
+
+static struct spi_driver adis16240_driver = {
+ .driver = {
+ .name = "adis16240",
+ .owner = THIS_MODULE,
+ },
+ .probe = adis16240_probe,
+ .remove = adis16240_remove,
+};
+module_spi_driver(adis16240_driver);
+
+MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>");
+MODULE_DESCRIPTION("Analog Devices Programmable Impact Sensor and Recorder");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("spi:adis16240");
diff --git a/drivers/staging/iio/accel/lis3l02dq.h b/drivers/staging/iio/accel/lis3l02dq.h
new file mode 100644
index 000000000..3f24c629b
--- /dev/null
+++ b/drivers/staging/iio/accel/lis3l02dq.h
@@ -0,0 +1,212 @@
+/*
+ * LISL02DQ.h -- support STMicroelectronics LISD02DQ
+ * 3d 2g Linear Accelerometers via SPI
+ *
+ * Copyright (c) 2007 Jonathan Cameron <jic23@kernel.org>
+ *
+ * Loosely based upon tle62x0.c
+ *
+ * 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.
+ */
+
+#ifndef SPI_LIS3L02DQ_H_
+#define SPI_LIS3L02DQ_H_
+#define LIS3L02DQ_READ_REG(a) ((a) | 0x80)
+#define LIS3L02DQ_WRITE_REG(a) a
+
+/* Calibration parameters */
+#define LIS3L02DQ_REG_OFFSET_X_ADDR 0x16
+#define LIS3L02DQ_REG_OFFSET_Y_ADDR 0x17
+#define LIS3L02DQ_REG_OFFSET_Z_ADDR 0x18
+
+#define LIS3L02DQ_REG_GAIN_X_ADDR 0x19
+#define LIS3L02DQ_REG_GAIN_Y_ADDR 0x1A
+#define LIS3L02DQ_REG_GAIN_Z_ADDR 0x1B
+
+/* Control Register (1 of 2) */
+#define LIS3L02DQ_REG_CTRL_1_ADDR 0x20
+/* Power ctrl - either bit set corresponds to on*/
+#define LIS3L02DQ_REG_CTRL_1_PD_ON 0xC0
+
+/* Decimation Factor */
+#define LIS3L02DQ_DEC_MASK 0x30
+#define LIS3L02DQ_REG_CTRL_1_DF_128 0x00
+#define LIS3L02DQ_REG_CTRL_1_DF_64 0x10
+#define LIS3L02DQ_REG_CTRL_1_DF_32 0x20
+#define LIS3L02DQ_REG_CTRL_1_DF_8 (0x10 | 0x20)
+
+/* Self Test Enable */
+#define LIS3L02DQ_REG_CTRL_1_SELF_TEST_ON 0x08
+
+/* Axes enable ctrls */
+#define LIS3L02DQ_REG_CTRL_1_AXES_Z_ENABLE 0x04
+#define LIS3L02DQ_REG_CTRL_1_AXES_Y_ENABLE 0x02
+#define LIS3L02DQ_REG_CTRL_1_AXES_X_ENABLE 0x01
+
+/* Control Register (2 of 2) */
+#define LIS3L02DQ_REG_CTRL_2_ADDR 0x21
+
+/* Block Data Update only after MSB and LSB read */
+#define LIS3L02DQ_REG_CTRL_2_BLOCK_UPDATE 0x40
+
+/* Set to big endian output */
+#define LIS3L02DQ_REG_CTRL_2_BIG_ENDIAN 0x20
+
+/* Reboot memory content */
+#define LIS3L02DQ_REG_CTRL_2_REBOOT_MEMORY 0x10
+
+/* Interrupt Enable - applies data ready to the RDY pad */
+#define LIS3L02DQ_REG_CTRL_2_ENABLE_INTERRUPT 0x08
+
+/* Enable Data Ready Generation - relationship with previous unclear in docs */
+#define LIS3L02DQ_REG_CTRL_2_ENABLE_DATA_READY_GENERATION 0x04
+
+/* SPI 3 wire mode */
+#define LIS3L02DQ_REG_CTRL_2_THREE_WIRE_SPI_MODE 0x02
+
+/* Data alignment, default is 12 bit right justified
+ * - option for 16 bit left justified */
+#define LIS3L02DQ_REG_CTRL_2_DATA_ALIGNMENT_16_BIT_LEFT_JUSTIFIED 0x01
+
+/* Interrupt related stuff */
+#define LIS3L02DQ_REG_WAKE_UP_CFG_ADDR 0x23
+
+/* Switch from or combination of conditions to and */
+#define LIS3L02DQ_REG_WAKE_UP_CFG_BOOLEAN_AND 0x80
+
+/* Latch interrupt request,
+ * if on ack must be given by reading the ack register */
+#define LIS3L02DQ_REG_WAKE_UP_CFG_LATCH_SRC 0x40
+
+/* Z Interrupt on High (above threshold) */
+#define LIS3L02DQ_REG_WAKE_UP_CFG_INTERRUPT_Z_HIGH 0x20
+/* Z Interrupt on Low */
+#define LIS3L02DQ_REG_WAKE_UP_CFG_INTERRUPT_Z_LOW 0x10
+/* Y Interrupt on High */
+#define LIS3L02DQ_REG_WAKE_UP_CFG_INTERRUPT_Y_HIGH 0x08
+/* Y Interrupt on Low */
+#define LIS3L02DQ_REG_WAKE_UP_CFG_INTERRUPT_Y_LOW 0x04
+/* X Interrupt on High */
+#define LIS3L02DQ_REG_WAKE_UP_CFG_INTERRUPT_X_HIGH 0x02
+/* X Interrupt on Low */
+#define LIS3L02DQ_REG_WAKE_UP_CFG_INTERRUPT_X_LOW 0x01
+
+/* Register that gives description of what caused interrupt
+ * - latched if set in CFG_ADDRES */
+#define LIS3L02DQ_REG_WAKE_UP_SRC_ADDR 0x24
+/* top bit ignored */
+/* Interrupt Active */
+#define LIS3L02DQ_REG_WAKE_UP_SRC_INTERRUPT_ACTIVATED 0x40
+/* Interupts that have been triggered */
+#define LIS3L02DQ_REG_WAKE_UP_SRC_INTERRUPT_Z_HIGH 0x20
+#define LIS3L02DQ_REG_WAKE_UP_SRC_INTERRUPT_Z_LOW 0x10
+#define LIS3L02DQ_REG_WAKE_UP_SRC_INTERRUPT_Y_HIGH 0x08
+#define LIS3L02DQ_REG_WAKE_UP_SRC_INTERRUPT_Y_LOW 0x04
+#define LIS3L02DQ_REG_WAKE_UP_SRC_INTERRUPT_X_HIGH 0x02
+#define LIS3L02DQ_REG_WAKE_UP_SRC_INTERRUPT_X_LOW 0x01
+
+#define LIS3L02DQ_REG_WAKE_UP_ACK_ADDR 0x25
+
+/* Status register */
+#define LIS3L02DQ_REG_STATUS_ADDR 0x27
+/* XYZ axis data overrun - first is all overrun? */
+#define LIS3L02DQ_REG_STATUS_XYZ_OVERRUN 0x80
+#define LIS3L02DQ_REG_STATUS_Z_OVERRUN 0x40
+#define LIS3L02DQ_REG_STATUS_Y_OVERRUN 0x20
+#define LIS3L02DQ_REG_STATUS_X_OVERRUN 0x10
+/* XYZ new data available - first is all 3 available? */
+#define LIS3L02DQ_REG_STATUS_XYZ_NEW_DATA 0x08
+#define LIS3L02DQ_REG_STATUS_Z_NEW_DATA 0x04
+#define LIS3L02DQ_REG_STATUS_Y_NEW_DATA 0x02
+#define LIS3L02DQ_REG_STATUS_X_NEW_DATA 0x01
+
+/* The accelerometer readings - low and high bytes.
+ * Form of high byte dependent on justification set in ctrl reg */
+#define LIS3L02DQ_REG_OUT_X_L_ADDR 0x28
+#define LIS3L02DQ_REG_OUT_X_H_ADDR 0x29
+#define LIS3L02DQ_REG_OUT_Y_L_ADDR 0x2A
+#define LIS3L02DQ_REG_OUT_Y_H_ADDR 0x2B
+#define LIS3L02DQ_REG_OUT_Z_L_ADDR 0x2C
+#define LIS3L02DQ_REG_OUT_Z_H_ADDR 0x2D
+
+/* Threshold values for all axes and both above and below thresholds
+ * - i.e. there is only one value */
+#define LIS3L02DQ_REG_THS_L_ADDR 0x2E
+#define LIS3L02DQ_REG_THS_H_ADDR 0x2F
+
+#define LIS3L02DQ_DEFAULT_CTRL1 (LIS3L02DQ_REG_CTRL_1_PD_ON \
+ | LIS3L02DQ_REG_CTRL_1_AXES_Z_ENABLE \
+ | LIS3L02DQ_REG_CTRL_1_AXES_Y_ENABLE \
+ | LIS3L02DQ_REG_CTRL_1_AXES_X_ENABLE \
+ | LIS3L02DQ_REG_CTRL_1_DF_128)
+
+#define LIS3L02DQ_DEFAULT_CTRL2 0
+
+#define LIS3L02DQ_MAX_TX 12
+#define LIS3L02DQ_MAX_RX 12
+/**
+ * struct lis3l02dq_state - device instance specific data
+ * @us: actual spi_device
+ * @trig: data ready trigger registered with iio
+ * @buf_lock: mutex to protect tx and rx
+ * @tx: transmit buffer
+ * @rx: receive buffer
+ **/
+struct lis3l02dq_state {
+ struct spi_device *us;
+ struct iio_trigger *trig;
+ struct mutex buf_lock;
+ int gpio;
+ bool trigger_on;
+
+ u8 tx[LIS3L02DQ_MAX_RX] ____cacheline_aligned;
+ u8 rx[LIS3L02DQ_MAX_RX] ____cacheline_aligned;
+};
+
+int lis3l02dq_spi_read_reg_8(struct iio_dev *indio_dev,
+ u8 reg_address,
+ u8 *val);
+
+int lis3l02dq_spi_write_reg_8(struct iio_dev *indio_dev,
+ u8 reg_address,
+ u8 val);
+
+int lis3l02dq_disable_all_events(struct iio_dev *indio_dev);
+
+#ifdef CONFIG_IIO_BUFFER
+/* At the moment triggers are only used for buffer
+ * filling. This may change!
+ */
+void lis3l02dq_remove_trigger(struct iio_dev *indio_dev);
+int lis3l02dq_probe_trigger(struct iio_dev *indio_dev);
+
+int lis3l02dq_configure_buffer(struct iio_dev *indio_dev);
+void lis3l02dq_unconfigure_buffer(struct iio_dev *indio_dev);
+
+irqreturn_t lis3l02dq_data_rdy_trig_poll(int irq, void *private);
+#define lis3l02dq_th lis3l02dq_data_rdy_trig_poll
+
+#else /* CONFIG_IIO_BUFFER */
+#define lis3l02dq_th lis3l02dq_nobuffer
+
+static inline void lis3l02dq_remove_trigger(struct iio_dev *indio_dev)
+{
+}
+
+static inline int lis3l02dq_probe_trigger(struct iio_dev *indio_dev)
+{
+ return 0;
+}
+
+static int lis3l02dq_configure_buffer(struct iio_dev *indio_dev)
+{
+ return 0;
+}
+
+static inline void lis3l02dq_unconfigure_buffer(struct iio_dev *indio_dev)
+{
+}
+#endif /* CONFIG_IIO_BUFFER */
+#endif /* SPI_LIS3L02DQ_H_ */
diff --git a/drivers/staging/iio/accel/lis3l02dq_core.c b/drivers/staging/iio/accel/lis3l02dq_core.c
new file mode 100644
index 000000000..ebcab56c8
--- /dev/null
+++ b/drivers/staging/iio/accel/lis3l02dq_core.c
@@ -0,0 +1,813 @@
+/*
+ * lis3l02dq.c support STMicroelectronics LISD02DQ
+ * 3d 2g Linear Accelerometers via SPI
+ *
+ * Copyright (c) 2007 Jonathan Cameron <jic23@kernel.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Settings:
+ * 16 bit left justified mode used.
+ */
+
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/gpio.h>
+#include <linux/of_gpio.h>
+#include <linux/mutex.h>
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/spi/spi.h>
+#include <linux/slab.h>
+#include <linux/sysfs.h>
+#include <linux/module.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/iio/events.h>
+#include <linux/iio/buffer.h>
+
+#include "lis3l02dq.h"
+
+/* At the moment the spi framework doesn't allow global setting of cs_change.
+ * It's in the likely to be added comment at the top of spi.h.
+ * This means that use cannot be made of spi_write etc.
+ */
+/* direct copy of the irq_default_primary_handler */
+#ifndef CONFIG_IIO_BUFFER
+static irqreturn_t lis3l02dq_nobuffer(int irq, void *private)
+{
+ return IRQ_WAKE_THREAD;
+}
+#endif
+
+/**
+ * lis3l02dq_spi_read_reg_8() - read single byte from a single register
+ * @indio_dev: iio_dev for this actual device
+ * @reg_address: the address of the register to be read
+ * @val: pass back the resulting value
+ **/
+int lis3l02dq_spi_read_reg_8(struct iio_dev *indio_dev,
+ u8 reg_address, u8 *val)
+{
+ struct lis3l02dq_state *st = iio_priv(indio_dev);
+ int ret;
+ struct spi_transfer xfer = {
+ .tx_buf = st->tx,
+ .rx_buf = st->rx,
+ .bits_per_word = 8,
+ .len = 2,
+ };
+
+ mutex_lock(&st->buf_lock);
+ st->tx[0] = LIS3L02DQ_READ_REG(reg_address);
+ st->tx[1] = 0;
+
+ ret = spi_sync_transfer(st->us, &xfer, 1);
+ *val = st->rx[1];
+ mutex_unlock(&st->buf_lock);
+
+ return ret;
+}
+
+/**
+ * lis3l02dq_spi_write_reg_8() - write single byte to a register
+ * @indio_dev: iio_dev for this device
+ * @reg_address: the address of the register to be written
+ * @val: the value to write
+ **/
+int lis3l02dq_spi_write_reg_8(struct iio_dev *indio_dev,
+ u8 reg_address,
+ u8 val)
+{
+ int ret;
+ struct lis3l02dq_state *st = iio_priv(indio_dev);
+
+ mutex_lock(&st->buf_lock);
+ st->tx[0] = LIS3L02DQ_WRITE_REG(reg_address);
+ st->tx[1] = val;
+ ret = spi_write(st->us, st->tx, 2);
+ mutex_unlock(&st->buf_lock);
+
+ return ret;
+}
+
+/**
+ * lisl302dq_spi_write_reg_s16() - write 2 bytes to a pair of registers
+ * @indio_dev: iio_dev for this device
+ * @lower_reg_address: the address of the lower of the two registers.
+ * Second register is assumed to have address one greater.
+ * @value: value to be written
+ **/
+static int lis3l02dq_spi_write_reg_s16(struct iio_dev *indio_dev,
+ u8 lower_reg_address,
+ s16 value)
+{
+ int ret;
+ struct lis3l02dq_state *st = iio_priv(indio_dev);
+ struct spi_transfer xfers[] = { {
+ .tx_buf = st->tx,
+ .bits_per_word = 8,
+ .len = 2,
+ .cs_change = 1,
+ }, {
+ .tx_buf = st->tx + 2,
+ .bits_per_word = 8,
+ .len = 2,
+ },
+ };
+
+ mutex_lock(&st->buf_lock);
+ st->tx[0] = LIS3L02DQ_WRITE_REG(lower_reg_address);
+ st->tx[1] = value & 0xFF;
+ st->tx[2] = LIS3L02DQ_WRITE_REG(lower_reg_address + 1);
+ st->tx[3] = (value >> 8) & 0xFF;
+
+ ret = spi_sync_transfer(st->us, xfers, ARRAY_SIZE(xfers));
+ mutex_unlock(&st->buf_lock);
+
+ return ret;
+}
+
+static int lis3l02dq_read_reg_s16(struct iio_dev *indio_dev,
+ u8 lower_reg_address,
+ int *val)
+{
+ struct lis3l02dq_state *st = iio_priv(indio_dev);
+ int ret;
+ s16 tempval;
+ struct spi_transfer xfers[] = { {
+ .tx_buf = st->tx,
+ .rx_buf = st->rx,
+ .bits_per_word = 8,
+ .len = 2,
+ .cs_change = 1,
+ }, {
+ .tx_buf = st->tx + 2,
+ .rx_buf = st->rx + 2,
+ .bits_per_word = 8,
+ .len = 2,
+ },
+ };
+
+ mutex_lock(&st->buf_lock);
+ st->tx[0] = LIS3L02DQ_READ_REG(lower_reg_address);
+ st->tx[1] = 0;
+ st->tx[2] = LIS3L02DQ_READ_REG(lower_reg_address + 1);
+ st->tx[3] = 0;
+
+ ret = spi_sync_transfer(st->us, xfers, ARRAY_SIZE(xfers));
+ if (ret) {
+ dev_err(&st->us->dev, "problem when reading 16 bit register");
+ goto error_ret;
+ }
+ tempval = (s16)(st->rx[1]) | ((s16)(st->rx[3]) << 8);
+
+ *val = tempval;
+error_ret:
+ mutex_unlock(&st->buf_lock);
+ return ret;
+}
+
+enum lis3l02dq_rm_ind {
+ LIS3L02DQ_ACCEL,
+ LIS3L02DQ_GAIN,
+ LIS3L02DQ_BIAS,
+};
+
+static u8 lis3l02dq_axis_map[3][3] = {
+ [LIS3L02DQ_ACCEL] = { LIS3L02DQ_REG_OUT_X_L_ADDR,
+ LIS3L02DQ_REG_OUT_Y_L_ADDR,
+ LIS3L02DQ_REG_OUT_Z_L_ADDR },
+ [LIS3L02DQ_GAIN] = { LIS3L02DQ_REG_GAIN_X_ADDR,
+ LIS3L02DQ_REG_GAIN_Y_ADDR,
+ LIS3L02DQ_REG_GAIN_Z_ADDR },
+ [LIS3L02DQ_BIAS] = { LIS3L02DQ_REG_OFFSET_X_ADDR,
+ LIS3L02DQ_REG_OFFSET_Y_ADDR,
+ LIS3L02DQ_REG_OFFSET_Z_ADDR }
+};
+
+static int lis3l02dq_read_thresh(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan,
+ enum iio_event_type type,
+ enum iio_event_direction dir,
+ enum iio_event_info info,
+ int *val, int *val2)
+{
+ int ret;
+
+ ret = lis3l02dq_read_reg_s16(indio_dev, LIS3L02DQ_REG_THS_L_ADDR, val);
+ if (ret)
+ return ret;
+ return IIO_VAL_INT;
+}
+
+static int lis3l02dq_write_thresh(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan,
+ enum iio_event_type type,
+ enum iio_event_direction dir,
+ enum iio_event_info info,
+ int val, int val2)
+{
+ u16 value = val;
+
+ return lis3l02dq_spi_write_reg_s16(indio_dev,
+ LIS3L02DQ_REG_THS_L_ADDR,
+ value);
+}
+
+static int lis3l02dq_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int val,
+ int val2,
+ long mask)
+{
+ int ret = -EINVAL, reg;
+ u8 uval;
+ s8 sval;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_CALIBBIAS:
+ if (val > 255 || val < -256)
+ return -EINVAL;
+ sval = val;
+ reg = lis3l02dq_axis_map[LIS3L02DQ_BIAS][chan->address];
+ ret = lis3l02dq_spi_write_reg_8(indio_dev, reg, sval);
+ break;
+ case IIO_CHAN_INFO_CALIBSCALE:
+ if (val & ~0xFF)
+ return -EINVAL;
+ uval = val;
+ reg = lis3l02dq_axis_map[LIS3L02DQ_GAIN][chan->address];
+ ret = lis3l02dq_spi_write_reg_8(indio_dev, reg, uval);
+ break;
+ }
+ return ret;
+}
+
+static int lis3l02dq_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val,
+ int *val2,
+ long mask)
+{
+ u8 utemp;
+ s8 stemp;
+ ssize_t ret = 0;
+ u8 reg;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ /* Take the iio_dev status lock */
+ mutex_lock(&indio_dev->mlock);
+ if (indio_dev->currentmode == INDIO_BUFFER_TRIGGERED) {
+ ret = -EBUSY;
+ } else {
+ reg = lis3l02dq_axis_map
+ [LIS3L02DQ_ACCEL][chan->address];
+ ret = lis3l02dq_read_reg_s16(indio_dev, reg, val);
+ }
+ mutex_unlock(&indio_dev->mlock);
+ if (ret < 0)
+ goto error_ret;
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_SCALE:
+ *val = 0;
+ *val2 = 9580;
+ return IIO_VAL_INT_PLUS_MICRO;
+ case IIO_CHAN_INFO_CALIBSCALE:
+ reg = lis3l02dq_axis_map[LIS3L02DQ_GAIN][chan->address];
+ ret = lis3l02dq_spi_read_reg_8(indio_dev, reg, &utemp);
+ if (ret)
+ goto error_ret;
+ /* to match with what previous code does */
+ *val = utemp;
+ return IIO_VAL_INT;
+
+ case IIO_CHAN_INFO_CALIBBIAS:
+ reg = lis3l02dq_axis_map[LIS3L02DQ_BIAS][chan->address];
+ ret = lis3l02dq_spi_read_reg_8(indio_dev, reg, (u8 *)&stemp);
+ /* to match with what previous code does */
+ *val = stemp;
+ return IIO_VAL_INT;
+ }
+error_ret:
+ return ret;
+}
+
+static ssize_t lis3l02dq_read_frequency(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ int ret, len = 0;
+ s8 t;
+
+ ret = lis3l02dq_spi_read_reg_8(indio_dev,
+ LIS3L02DQ_REG_CTRL_1_ADDR,
+ (u8 *)&t);
+ if (ret)
+ return ret;
+ t &= LIS3L02DQ_DEC_MASK;
+ switch (t) {
+ case LIS3L02DQ_REG_CTRL_1_DF_128:
+ len = sprintf(buf, "280\n");
+ break;
+ case LIS3L02DQ_REG_CTRL_1_DF_64:
+ len = sprintf(buf, "560\n");
+ break;
+ case LIS3L02DQ_REG_CTRL_1_DF_32:
+ len = sprintf(buf, "1120\n");
+ break;
+ case LIS3L02DQ_REG_CTRL_1_DF_8:
+ len = sprintf(buf, "4480\n");
+ break;
+ }
+ return len;
+}
+
+static ssize_t lis3l02dq_write_frequency(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t len)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ unsigned long val;
+ int ret;
+ u8 t;
+
+ ret = kstrtoul(buf, 10, &val);
+ if (ret)
+ return ret;
+
+ mutex_lock(&indio_dev->mlock);
+ ret = lis3l02dq_spi_read_reg_8(indio_dev,
+ LIS3L02DQ_REG_CTRL_1_ADDR,
+ &t);
+ if (ret)
+ goto error_ret_mutex;
+ /* Wipe the bits clean */
+ t &= ~LIS3L02DQ_DEC_MASK;
+ switch (val) {
+ case 280:
+ t |= LIS3L02DQ_REG_CTRL_1_DF_128;
+ break;
+ case 560:
+ t |= LIS3L02DQ_REG_CTRL_1_DF_64;
+ break;
+ case 1120:
+ t |= LIS3L02DQ_REG_CTRL_1_DF_32;
+ break;
+ case 4480:
+ t |= LIS3L02DQ_REG_CTRL_1_DF_8;
+ break;
+ default:
+ ret = -EINVAL;
+ goto error_ret_mutex;
+ }
+
+ ret = lis3l02dq_spi_write_reg_8(indio_dev,
+ LIS3L02DQ_REG_CTRL_1_ADDR,
+ t);
+
+error_ret_mutex:
+ mutex_unlock(&indio_dev->mlock);
+
+ return ret ? ret : len;
+}
+
+static int lis3l02dq_initial_setup(struct iio_dev *indio_dev)
+{
+ struct lis3l02dq_state *st = iio_priv(indio_dev);
+ int ret;
+ u8 val, valtest;
+
+ st->us->mode = SPI_MODE_3;
+
+ spi_setup(st->us);
+
+ val = LIS3L02DQ_DEFAULT_CTRL1;
+ /* Write suitable defaults to ctrl1 */
+ ret = lis3l02dq_spi_write_reg_8(indio_dev,
+ LIS3L02DQ_REG_CTRL_1_ADDR,
+ val);
+ if (ret) {
+ dev_err(&st->us->dev, "problem with setup control register 1");
+ goto err_ret;
+ }
+ /* Repeat as sometimes doesn't work first time? */
+ ret = lis3l02dq_spi_write_reg_8(indio_dev,
+ LIS3L02DQ_REG_CTRL_1_ADDR,
+ val);
+ if (ret) {
+ dev_err(&st->us->dev, "problem with setup control register 1");
+ goto err_ret;
+ }
+
+ /* Read back to check this has worked acts as loose test of correct
+ * chip */
+ ret = lis3l02dq_spi_read_reg_8(indio_dev,
+ LIS3L02DQ_REG_CTRL_1_ADDR,
+ &valtest);
+ if (ret || (valtest != val)) {
+ dev_err(&indio_dev->dev,
+ "device not playing ball %d %d\n", valtest, val);
+ ret = -EINVAL;
+ goto err_ret;
+ }
+
+ val = LIS3L02DQ_DEFAULT_CTRL2;
+ ret = lis3l02dq_spi_write_reg_8(indio_dev,
+ LIS3L02DQ_REG_CTRL_2_ADDR,
+ val);
+ if (ret) {
+ dev_err(&st->us->dev, "problem with setup control register 2");
+ goto err_ret;
+ }
+
+ val = LIS3L02DQ_REG_WAKE_UP_CFG_LATCH_SRC;
+ ret = lis3l02dq_spi_write_reg_8(indio_dev,
+ LIS3L02DQ_REG_WAKE_UP_CFG_ADDR,
+ val);
+ if (ret)
+ dev_err(&st->us->dev, "problem with interrupt cfg register");
+err_ret:
+
+ return ret;
+}
+
+static IIO_DEV_ATTR_SAMP_FREQ(S_IWUSR | S_IRUGO,
+ lis3l02dq_read_frequency,
+ lis3l02dq_write_frequency);
+
+static IIO_CONST_ATTR_SAMP_FREQ_AVAIL("280 560 1120 4480");
+
+static irqreturn_t lis3l02dq_event_handler(int irq, void *private)
+{
+ struct iio_dev *indio_dev = private;
+ u8 t;
+
+ s64 timestamp = iio_get_time_ns();
+
+ lis3l02dq_spi_read_reg_8(indio_dev,
+ LIS3L02DQ_REG_WAKE_UP_SRC_ADDR,
+ &t);
+
+ if (t & LIS3L02DQ_REG_WAKE_UP_SRC_INTERRUPT_Z_HIGH)
+ iio_push_event(indio_dev,
+ IIO_MOD_EVENT_CODE(IIO_ACCEL,
+ 0,
+ IIO_MOD_Z,
+ IIO_EV_TYPE_THRESH,
+ IIO_EV_DIR_RISING),
+ timestamp);
+
+ if (t & LIS3L02DQ_REG_WAKE_UP_SRC_INTERRUPT_Z_LOW)
+ iio_push_event(indio_dev,
+ IIO_MOD_EVENT_CODE(IIO_ACCEL,
+ 0,
+ IIO_MOD_Z,
+ IIO_EV_TYPE_THRESH,
+ IIO_EV_DIR_FALLING),
+ timestamp);
+
+ if (t & LIS3L02DQ_REG_WAKE_UP_SRC_INTERRUPT_Y_HIGH)
+ iio_push_event(indio_dev,
+ IIO_MOD_EVENT_CODE(IIO_ACCEL,
+ 0,
+ IIO_MOD_Y,
+ IIO_EV_TYPE_THRESH,
+ IIO_EV_DIR_RISING),
+ timestamp);
+
+ if (t & LIS3L02DQ_REG_WAKE_UP_SRC_INTERRUPT_Y_LOW)
+ iio_push_event(indio_dev,
+ IIO_MOD_EVENT_CODE(IIO_ACCEL,
+ 0,
+ IIO_MOD_Y,
+ IIO_EV_TYPE_THRESH,
+ IIO_EV_DIR_FALLING),
+ timestamp);
+
+ if (t & LIS3L02DQ_REG_WAKE_UP_SRC_INTERRUPT_X_HIGH)
+ iio_push_event(indio_dev,
+ IIO_MOD_EVENT_CODE(IIO_ACCEL,
+ 0,
+ IIO_MOD_X,
+ IIO_EV_TYPE_THRESH,
+ IIO_EV_DIR_RISING),
+ timestamp);
+
+ if (t & LIS3L02DQ_REG_WAKE_UP_SRC_INTERRUPT_X_LOW)
+ iio_push_event(indio_dev,
+ IIO_MOD_EVENT_CODE(IIO_ACCEL,
+ 0,
+ IIO_MOD_X,
+ IIO_EV_TYPE_THRESH,
+ IIO_EV_DIR_FALLING),
+ timestamp);
+
+ /* Ack and allow for new interrupts */
+ lis3l02dq_spi_read_reg_8(indio_dev,
+ LIS3L02DQ_REG_WAKE_UP_ACK_ADDR,
+ &t);
+
+ return IRQ_HANDLED;
+}
+
+static const struct iio_event_spec lis3l02dq_event[] = {
+ {
+ .type = IIO_EV_TYPE_THRESH,
+ .dir = IIO_EV_DIR_RISING,
+ .mask_separate = BIT(IIO_EV_INFO_ENABLE),
+ .mask_shared_by_type = BIT(IIO_EV_INFO_VALUE),
+ }, {
+ .type = IIO_EV_TYPE_THRESH,
+ .dir = IIO_EV_DIR_FALLING,
+ .mask_separate = BIT(IIO_EV_INFO_ENABLE),
+ .mask_shared_by_type = BIT(IIO_EV_INFO_VALUE),
+ }
+};
+
+#define LIS3L02DQ_CHAN(index, mod) \
+ { \
+ .type = IIO_ACCEL, \
+ .modified = 1, \
+ .channel2 = mod, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
+ BIT(IIO_CHAN_INFO_CALIBSCALE) | \
+ BIT(IIO_CHAN_INFO_CALIBBIAS), \
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
+ .address = index, \
+ .scan_index = index, \
+ .scan_type = { \
+ .sign = 's', \
+ .realbits = 12, \
+ .storagebits = 16, \
+ }, \
+ .event_spec = lis3l02dq_event, \
+ .num_event_specs = ARRAY_SIZE(lis3l02dq_event), \
+ }
+
+static const struct iio_chan_spec lis3l02dq_channels[] = {
+ LIS3L02DQ_CHAN(0, IIO_MOD_X),
+ LIS3L02DQ_CHAN(1, IIO_MOD_Y),
+ LIS3L02DQ_CHAN(2, IIO_MOD_Z),
+ IIO_CHAN_SOFT_TIMESTAMP(3)
+};
+
+static int lis3l02dq_read_event_config(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan,
+ enum iio_event_type type,
+ enum iio_event_direction dir)
+{
+ u8 val;
+ int ret;
+ u8 mask = (1 << (chan->channel2*2 + (dir == IIO_EV_DIR_RISING)));
+
+ ret = lis3l02dq_spi_read_reg_8(indio_dev,
+ LIS3L02DQ_REG_WAKE_UP_CFG_ADDR,
+ &val);
+ if (ret < 0)
+ return ret;
+
+ return !!(val & mask);
+}
+
+int lis3l02dq_disable_all_events(struct iio_dev *indio_dev)
+{
+ int ret;
+ u8 control, val;
+
+ ret = lis3l02dq_spi_read_reg_8(indio_dev,
+ LIS3L02DQ_REG_CTRL_2_ADDR,
+ &control);
+
+ control &= ~LIS3L02DQ_REG_CTRL_2_ENABLE_INTERRUPT;
+ ret = lis3l02dq_spi_write_reg_8(indio_dev,
+ LIS3L02DQ_REG_CTRL_2_ADDR,
+ control);
+ if (ret)
+ goto error_ret;
+ /* Also for consistency clear the mask */
+ ret = lis3l02dq_spi_read_reg_8(indio_dev,
+ LIS3L02DQ_REG_WAKE_UP_CFG_ADDR,
+ &val);
+ if (ret)
+ goto error_ret;
+ val &= ~0x3f;
+
+ ret = lis3l02dq_spi_write_reg_8(indio_dev,
+ LIS3L02DQ_REG_WAKE_UP_CFG_ADDR,
+ val);
+ if (ret)
+ goto error_ret;
+
+ ret = control;
+error_ret:
+ return ret;
+}
+
+static int lis3l02dq_write_event_config(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan,
+ enum iio_event_type type,
+ enum iio_event_direction dir,
+ int state)
+{
+ int ret = 0;
+ u8 val, control;
+ u8 currentlyset;
+ bool changed = false;
+ u8 mask = (1 << (chan->channel2*2 + (dir == IIO_EV_DIR_RISING)));
+
+ mutex_lock(&indio_dev->mlock);
+ /* read current control */
+ ret = lis3l02dq_spi_read_reg_8(indio_dev,
+ LIS3L02DQ_REG_CTRL_2_ADDR,
+ &control);
+ if (ret)
+ goto error_ret;
+ ret = lis3l02dq_spi_read_reg_8(indio_dev,
+ LIS3L02DQ_REG_WAKE_UP_CFG_ADDR,
+ &val);
+ if (ret < 0)
+ goto error_ret;
+ currentlyset = val & mask;
+
+ if (!currentlyset && state) {
+ changed = true;
+ val |= mask;
+ } else if (currentlyset && !state) {
+ changed = true;
+ val &= ~mask;
+ }
+
+ if (changed) {
+ ret = lis3l02dq_spi_write_reg_8(indio_dev,
+ LIS3L02DQ_REG_WAKE_UP_CFG_ADDR,
+ val);
+ if (ret)
+ goto error_ret;
+ control = val & 0x3f ?
+ (control | LIS3L02DQ_REG_CTRL_2_ENABLE_INTERRUPT) :
+ (control & ~LIS3L02DQ_REG_CTRL_2_ENABLE_INTERRUPT);
+ ret = lis3l02dq_spi_write_reg_8(indio_dev,
+ LIS3L02DQ_REG_CTRL_2_ADDR,
+ control);
+ if (ret)
+ goto error_ret;
+ }
+
+error_ret:
+ mutex_unlock(&indio_dev->mlock);
+ return ret;
+}
+
+static struct attribute *lis3l02dq_attributes[] = {
+ &iio_dev_attr_sampling_frequency.dev_attr.attr,
+ &iio_const_attr_sampling_frequency_available.dev_attr.attr,
+ NULL
+};
+
+static const struct attribute_group lis3l02dq_attribute_group = {
+ .attrs = lis3l02dq_attributes,
+};
+
+static const struct iio_info lis3l02dq_info = {
+ .read_raw = &lis3l02dq_read_raw,
+ .write_raw = &lis3l02dq_write_raw,
+ .read_event_value = &lis3l02dq_read_thresh,
+ .write_event_value = &lis3l02dq_write_thresh,
+ .write_event_config = &lis3l02dq_write_event_config,
+ .read_event_config = &lis3l02dq_read_event_config,
+ .driver_module = THIS_MODULE,
+ .attrs = &lis3l02dq_attribute_group,
+};
+
+static int lis3l02dq_probe(struct spi_device *spi)
+{
+ int ret;
+ struct lis3l02dq_state *st;
+ struct iio_dev *indio_dev;
+
+ indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));
+ if (!indio_dev)
+ return -ENOMEM;
+ st = iio_priv(indio_dev);
+ /* this is only used for removal purposes */
+ spi_set_drvdata(spi, indio_dev);
+
+ st->us = spi;
+ st->gpio = of_get_gpio(spi->dev.of_node, 0);
+ mutex_init(&st->buf_lock);
+ indio_dev->name = spi->dev.driver->name;
+ indio_dev->dev.parent = &spi->dev;
+ indio_dev->info = &lis3l02dq_info;
+ indio_dev->channels = lis3l02dq_channels;
+ indio_dev->num_channels = ARRAY_SIZE(lis3l02dq_channels);
+
+ indio_dev->modes = INDIO_DIRECT_MODE;
+
+ ret = lis3l02dq_configure_buffer(indio_dev);
+ if (ret)
+ return ret;
+
+ if (spi->irq) {
+ ret = request_threaded_irq(st->us->irq,
+ &lis3l02dq_th,
+ &lis3l02dq_event_handler,
+ IRQF_TRIGGER_RISING,
+ "lis3l02dq",
+ indio_dev);
+ if (ret)
+ goto error_unreg_buffer_funcs;
+
+ ret = lis3l02dq_probe_trigger(indio_dev);
+ if (ret)
+ goto error_free_interrupt;
+ }
+
+ /* Get the device into a sane initial state */
+ ret = lis3l02dq_initial_setup(indio_dev);
+ if (ret)
+ goto error_remove_trigger;
+
+ ret = iio_device_register(indio_dev);
+ if (ret)
+ goto error_remove_trigger;
+
+ return 0;
+
+error_remove_trigger:
+ if (spi->irq)
+ lis3l02dq_remove_trigger(indio_dev);
+error_free_interrupt:
+ if (spi->irq)
+ free_irq(st->us->irq, indio_dev);
+error_unreg_buffer_funcs:
+ lis3l02dq_unconfigure_buffer(indio_dev);
+ return ret;
+}
+
+/* Power down the device */
+static int lis3l02dq_stop_device(struct iio_dev *indio_dev)
+{
+ int ret;
+ struct lis3l02dq_state *st = iio_priv(indio_dev);
+ u8 val = 0;
+
+ mutex_lock(&indio_dev->mlock);
+ ret = lis3l02dq_spi_write_reg_8(indio_dev,
+ LIS3L02DQ_REG_CTRL_1_ADDR,
+ val);
+ if (ret) {
+ dev_err(&st->us->dev, "problem with turning device off: ctrl1");
+ goto err_ret;
+ }
+
+ ret = lis3l02dq_spi_write_reg_8(indio_dev,
+ LIS3L02DQ_REG_CTRL_2_ADDR,
+ val);
+ if (ret)
+ dev_err(&st->us->dev, "problem with turning device off: ctrl2");
+err_ret:
+ mutex_unlock(&indio_dev->mlock);
+ return ret;
+}
+
+/* fixme, confirm ordering in this function */
+static int lis3l02dq_remove(struct spi_device *spi)
+{
+ struct iio_dev *indio_dev = spi_get_drvdata(spi);
+ struct lis3l02dq_state *st = iio_priv(indio_dev);
+
+ iio_device_unregister(indio_dev);
+
+ lis3l02dq_disable_all_events(indio_dev);
+ lis3l02dq_stop_device(indio_dev);
+
+ if (spi->irq)
+ free_irq(st->us->irq, indio_dev);
+
+ lis3l02dq_remove_trigger(indio_dev);
+ lis3l02dq_unconfigure_buffer(indio_dev);
+
+ return 0;
+}
+
+static struct spi_driver lis3l02dq_driver = {
+ .driver = {
+ .name = "lis3l02dq",
+ .owner = THIS_MODULE,
+ },
+ .probe = lis3l02dq_probe,
+ .remove = lis3l02dq_remove,
+};
+module_spi_driver(lis3l02dq_driver);
+
+MODULE_AUTHOR("Jonathan Cameron <jic23@kernel.org>");
+MODULE_DESCRIPTION("ST LIS3L02DQ Accelerometer SPI driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("spi:lis3l02dq");
diff --git a/drivers/staging/iio/accel/lis3l02dq_ring.c b/drivers/staging/iio/accel/lis3l02dq_ring.c
new file mode 100644
index 000000000..b892f2cf5
--- /dev/null
+++ b/drivers/staging/iio/accel/lis3l02dq_ring.c
@@ -0,0 +1,426 @@
+#include <linux/interrupt.h>
+#include <linux/gpio.h>
+#include <linux/mutex.h>
+#include <linux/kernel.h>
+#include <linux/spi/spi.h>
+#include <linux/slab.h>
+#include <linux/export.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/kfifo_buf.h>
+#include <linux/iio/trigger.h>
+#include <linux/iio/trigger_consumer.h>
+#include "lis3l02dq.h"
+
+/**
+ * combine_8_to_16() utility function to munge two u8s into u16
+ **/
+static inline u16 combine_8_to_16(u8 lower, u8 upper)
+{
+ u16 _lower = lower;
+ u16 _upper = upper;
+
+ return _lower | (_upper << 8);
+}
+
+/**
+ * lis3l02dq_data_rdy_trig_poll() the event handler for the data rdy trig
+ **/
+irqreturn_t lis3l02dq_data_rdy_trig_poll(int irq, void *private)
+{
+ struct iio_dev *indio_dev = private;
+ struct lis3l02dq_state *st = iio_priv(indio_dev);
+
+ if (st->trigger_on) {
+ iio_trigger_poll(st->trig);
+ return IRQ_HANDLED;
+ }
+
+ return IRQ_WAKE_THREAD;
+}
+
+static const u8 read_all_tx_array[] = {
+ LIS3L02DQ_READ_REG(LIS3L02DQ_REG_OUT_X_L_ADDR), 0,
+ LIS3L02DQ_READ_REG(LIS3L02DQ_REG_OUT_X_H_ADDR), 0,
+ LIS3L02DQ_READ_REG(LIS3L02DQ_REG_OUT_Y_L_ADDR), 0,
+ LIS3L02DQ_READ_REG(LIS3L02DQ_REG_OUT_Y_H_ADDR), 0,
+ LIS3L02DQ_READ_REG(LIS3L02DQ_REG_OUT_Z_L_ADDR), 0,
+ LIS3L02DQ_READ_REG(LIS3L02DQ_REG_OUT_Z_H_ADDR), 0,
+};
+
+/**
+ * lis3l02dq_read_all() Reads all channels currently selected
+ * @indio_dev: IIO device state
+ * @rx_array: (dma capable) receive array, must be at least
+ * 4*number of channels
+ **/
+static int lis3l02dq_read_all(struct iio_dev *indio_dev, u8 *rx_array)
+{
+ struct lis3l02dq_state *st = iio_priv(indio_dev);
+ struct spi_transfer *xfers;
+ struct spi_message msg;
+ int ret, i, j = 0;
+
+ xfers = kcalloc(bitmap_weight(indio_dev->active_scan_mask,
+ indio_dev->masklength) * 2,
+ sizeof(*xfers), GFP_KERNEL);
+ if (!xfers)
+ return -ENOMEM;
+
+ mutex_lock(&st->buf_lock);
+
+ for (i = 0; i < ARRAY_SIZE(read_all_tx_array)/4; i++)
+ if (test_bit(i, indio_dev->active_scan_mask)) {
+ /* lower byte */
+ xfers[j].tx_buf = st->tx + 2*j;
+ st->tx[2*j] = read_all_tx_array[i*4];
+ st->tx[2*j + 1] = 0;
+ if (rx_array)
+ xfers[j].rx_buf = rx_array + j*2;
+ xfers[j].bits_per_word = 8;
+ xfers[j].len = 2;
+ xfers[j].cs_change = 1;
+ j++;
+
+ /* upper byte */
+ xfers[j].tx_buf = st->tx + 2*j;
+ st->tx[2*j] = read_all_tx_array[i*4 + 2];
+ st->tx[2*j + 1] = 0;
+ if (rx_array)
+ xfers[j].rx_buf = rx_array + j*2;
+ xfers[j].bits_per_word = 8;
+ xfers[j].len = 2;
+ xfers[j].cs_change = 1;
+ j++;
+ }
+
+ /* After these are transmitted, the rx_buff should have
+ * values in alternate bytes
+ */
+ spi_message_init(&msg);
+ for (j = 0; j < bitmap_weight(indio_dev->active_scan_mask,
+ indio_dev->masklength) * 2; j++)
+ spi_message_add_tail(&xfers[j], &msg);
+
+ ret = spi_sync(st->us, &msg);
+ mutex_unlock(&st->buf_lock);
+ kfree(xfers);
+
+ return ret;
+}
+
+static int lis3l02dq_get_buffer_element(struct iio_dev *indio_dev,
+ u8 *buf)
+{
+ int ret, i;
+ u8 *rx_array;
+ s16 *data = (s16 *)buf;
+ int scan_count = bitmap_weight(indio_dev->active_scan_mask,
+ indio_dev->masklength);
+
+ rx_array = kcalloc(4, scan_count, GFP_KERNEL);
+ if (!rx_array)
+ return -ENOMEM;
+ ret = lis3l02dq_read_all(indio_dev, rx_array);
+ if (ret < 0) {
+ kfree(rx_array);
+ return ret;
+ }
+ for (i = 0; i < scan_count; i++)
+ data[i] = combine_8_to_16(rx_array[i*4+1],
+ rx_array[i*4+3]);
+ kfree(rx_array);
+
+ return i*sizeof(data[0]);
+}
+
+static irqreturn_t lis3l02dq_trigger_handler(int irq, void *p)
+{
+ struct iio_poll_func *pf = p;
+ struct iio_dev *indio_dev = pf->indio_dev;
+ int len = 0;
+ char *data;
+
+ data = kmalloc(indio_dev->scan_bytes, GFP_KERNEL);
+ if (!data)
+ goto done;
+
+ if (!bitmap_empty(indio_dev->active_scan_mask, indio_dev->masklength))
+ len = lis3l02dq_get_buffer_element(indio_dev, data);
+
+ iio_push_to_buffers_with_timestamp(indio_dev, data, pf->timestamp);
+
+ kfree(data);
+done:
+ iio_trigger_notify_done(indio_dev->trig);
+ return IRQ_HANDLED;
+}
+
+/* Caller responsible for locking as necessary. */
+static int
+__lis3l02dq_write_data_ready_config(struct iio_dev *indio_dev, bool state)
+{
+ int ret;
+ u8 valold;
+ bool currentlyset;
+ struct lis3l02dq_state *st = iio_priv(indio_dev);
+
+ /* Get the current event mask register */
+ ret = lis3l02dq_spi_read_reg_8(indio_dev,
+ LIS3L02DQ_REG_CTRL_2_ADDR,
+ &valold);
+ if (ret)
+ goto error_ret;
+ /* Find out if data ready is already on */
+ currentlyset
+ = valold & LIS3L02DQ_REG_CTRL_2_ENABLE_DATA_READY_GENERATION;
+
+ /* Disable requested */
+ if (!state && currentlyset) {
+ /* Disable the data ready signal */
+ valold &= ~LIS3L02DQ_REG_CTRL_2_ENABLE_DATA_READY_GENERATION;
+
+ /* The double write is to overcome a hardware bug? */
+ ret = lis3l02dq_spi_write_reg_8(indio_dev,
+ LIS3L02DQ_REG_CTRL_2_ADDR,
+ valold);
+ if (ret)
+ goto error_ret;
+ ret = lis3l02dq_spi_write_reg_8(indio_dev,
+ LIS3L02DQ_REG_CTRL_2_ADDR,
+ valold);
+ if (ret)
+ goto error_ret;
+ st->trigger_on = false;
+ /* Enable requested */
+ } else if (state && !currentlyset) {
+ /* If not set, enable requested
+ * first disable all events */
+ ret = lis3l02dq_disable_all_events(indio_dev);
+ if (ret < 0)
+ goto error_ret;
+
+ valold = ret |
+ LIS3L02DQ_REG_CTRL_2_ENABLE_DATA_READY_GENERATION;
+
+ st->trigger_on = true;
+ ret = lis3l02dq_spi_write_reg_8(indio_dev,
+ LIS3L02DQ_REG_CTRL_2_ADDR,
+ valold);
+ if (ret)
+ goto error_ret;
+ }
+
+ return 0;
+error_ret:
+ return ret;
+}
+
+/**
+ * lis3l02dq_data_rdy_trigger_set_state() set datardy interrupt state
+ *
+ * If disabling the interrupt also does a final read to ensure it is clear.
+ * This is only important in some cases where the scan enable elements are
+ * switched before the buffer is reenabled.
+ **/
+static int lis3l02dq_data_rdy_trigger_set_state(struct iio_trigger *trig,
+ bool state)
+{
+ struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig);
+ int ret = 0;
+ u8 t;
+
+ __lis3l02dq_write_data_ready_config(indio_dev, state);
+ if (!state) {
+ /*
+ * A possible quirk with the handler is currently worked around
+ * by ensuring outstanding read events are cleared.
+ */
+ ret = lis3l02dq_read_all(indio_dev, NULL);
+ }
+ lis3l02dq_spi_read_reg_8(indio_dev,
+ LIS3L02DQ_REG_WAKE_UP_SRC_ADDR,
+ &t);
+ return ret;
+}
+
+/**
+ * lis3l02dq_trig_try_reen() try reenabling irq for data rdy trigger
+ * @trig: the datardy trigger
+ */
+static int lis3l02dq_trig_try_reen(struct iio_trigger *trig)
+{
+ struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig);
+ struct lis3l02dq_state *st = iio_priv(indio_dev);
+ int i;
+
+ /* If gpio still high (or high again)
+ * In theory possible we will need to do this several times */
+ for (i = 0; i < 5; i++)
+ if (gpio_get_value(st->gpio))
+ lis3l02dq_read_all(indio_dev, NULL);
+ else
+ break;
+ if (i == 5)
+ pr_info("Failed to clear the interrupt for lis3l02dq\n");
+
+ /* irq reenabled so success! */
+ return 0;
+}
+
+static const struct iio_trigger_ops lis3l02dq_trigger_ops = {
+ .owner = THIS_MODULE,
+ .set_trigger_state = &lis3l02dq_data_rdy_trigger_set_state,
+ .try_reenable = &lis3l02dq_trig_try_reen,
+};
+
+int lis3l02dq_probe_trigger(struct iio_dev *indio_dev)
+{
+ int ret;
+ struct lis3l02dq_state *st = iio_priv(indio_dev);
+
+ st->trig = iio_trigger_alloc("lis3l02dq-dev%d", indio_dev->id);
+ if (!st->trig) {
+ ret = -ENOMEM;
+ goto error_ret;
+ }
+
+ st->trig->dev.parent = &st->us->dev;
+ st->trig->ops = &lis3l02dq_trigger_ops;
+ iio_trigger_set_drvdata(st->trig, indio_dev);
+ ret = iio_trigger_register(st->trig);
+ if (ret)
+ goto error_free_trig;
+
+ return 0;
+
+error_free_trig:
+ iio_trigger_free(st->trig);
+error_ret:
+ return ret;
+}
+
+void lis3l02dq_remove_trigger(struct iio_dev *indio_dev)
+{
+ struct lis3l02dq_state *st = iio_priv(indio_dev);
+
+ iio_trigger_unregister(st->trig);
+ iio_trigger_free(st->trig);
+}
+
+void lis3l02dq_unconfigure_buffer(struct iio_dev *indio_dev)
+{
+ iio_dealloc_pollfunc(indio_dev->pollfunc);
+ iio_kfifo_free(indio_dev->buffer);
+}
+
+static int lis3l02dq_buffer_postenable(struct iio_dev *indio_dev)
+{
+ /* Disable unwanted channels otherwise the interrupt will not clear */
+ u8 t;
+ int ret;
+ bool oneenabled = false;
+
+ ret = lis3l02dq_spi_read_reg_8(indio_dev,
+ LIS3L02DQ_REG_CTRL_1_ADDR,
+ &t);
+ if (ret)
+ goto error_ret;
+
+ if (test_bit(0, indio_dev->active_scan_mask)) {
+ t |= LIS3L02DQ_REG_CTRL_1_AXES_X_ENABLE;
+ oneenabled = true;
+ } else {
+ t &= ~LIS3L02DQ_REG_CTRL_1_AXES_X_ENABLE;
+ }
+ if (test_bit(1, indio_dev->active_scan_mask)) {
+ t |= LIS3L02DQ_REG_CTRL_1_AXES_Y_ENABLE;
+ oneenabled = true;
+ } else {
+ t &= ~LIS3L02DQ_REG_CTRL_1_AXES_Y_ENABLE;
+ }
+ if (test_bit(2, indio_dev->active_scan_mask)) {
+ t |= LIS3L02DQ_REG_CTRL_1_AXES_Z_ENABLE;
+ oneenabled = true;
+ } else {
+ t &= ~LIS3L02DQ_REG_CTRL_1_AXES_Z_ENABLE;
+ }
+ if (!oneenabled) /* what happens in this case is unknown */
+ return -EINVAL;
+ ret = lis3l02dq_spi_write_reg_8(indio_dev,
+ LIS3L02DQ_REG_CTRL_1_ADDR,
+ t);
+ if (ret)
+ goto error_ret;
+
+ return iio_triggered_buffer_postenable(indio_dev);
+error_ret:
+ return ret;
+}
+
+/* Turn all channels on again */
+static int lis3l02dq_buffer_predisable(struct iio_dev *indio_dev)
+{
+ u8 t;
+ int ret;
+
+ ret = iio_triggered_buffer_predisable(indio_dev);
+ if (ret)
+ goto error_ret;
+
+ ret = lis3l02dq_spi_read_reg_8(indio_dev,
+ LIS3L02DQ_REG_CTRL_1_ADDR,
+ &t);
+ if (ret)
+ goto error_ret;
+ t |= LIS3L02DQ_REG_CTRL_1_AXES_X_ENABLE |
+ LIS3L02DQ_REG_CTRL_1_AXES_Y_ENABLE |
+ LIS3L02DQ_REG_CTRL_1_AXES_Z_ENABLE;
+
+ ret = lis3l02dq_spi_write_reg_8(indio_dev,
+ LIS3L02DQ_REG_CTRL_1_ADDR,
+ t);
+
+error_ret:
+ return ret;
+}
+
+static const struct iio_buffer_setup_ops lis3l02dq_buffer_setup_ops = {
+ .postenable = &lis3l02dq_buffer_postenable,
+ .predisable = &lis3l02dq_buffer_predisable,
+};
+
+int lis3l02dq_configure_buffer(struct iio_dev *indio_dev)
+{
+ int ret;
+ struct iio_buffer *buffer;
+
+ buffer = iio_kfifo_allocate();
+ if (!buffer)
+ return -ENOMEM;
+
+ iio_device_attach_buffer(indio_dev, buffer);
+
+ buffer->scan_timestamp = true;
+ indio_dev->setup_ops = &lis3l02dq_buffer_setup_ops;
+
+ /* Functions are NULL as we set handler below */
+ indio_dev->pollfunc = iio_alloc_pollfunc(&iio_pollfunc_store_time,
+ &lis3l02dq_trigger_handler,
+ 0,
+ indio_dev,
+ "lis3l02dq_consumer%d",
+ indio_dev->id);
+
+ if (!indio_dev->pollfunc) {
+ ret = -ENOMEM;
+ goto error_iio_sw_rb_free;
+ }
+
+ indio_dev->modes |= INDIO_BUFFER_TRIGGERED;
+ return 0;
+
+error_iio_sw_rb_free:
+ iio_kfifo_free(indio_dev->buffer);
+ return ret;
+}
diff --git a/drivers/staging/iio/accel/sca3000.h b/drivers/staging/iio/accel/sca3000.h
new file mode 100644
index 000000000..9c8a9587d
--- /dev/null
+++ b/drivers/staging/iio/accel/sca3000.h
@@ -0,0 +1,278 @@
+/*
+ * sca3000.c -- support VTI sca3000 series accelerometers
+ * via SPI
+ *
+ * Copyright (c) 2007 Jonathan Cameron <jic23@kernel.org>
+ *
+ * Partly based upon tle62x0.c
+ *
+ * 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.
+ *
+ * Initial mode is direct measurement.
+ *
+ * Untested things
+ *
+ * Temperature reading (the e05 I'm testing with doesn't have a sensor)
+ *
+ * Free fall detection mode - supported but untested as I'm not droping my
+ * dubious wire rig far enough to test it.
+ *
+ * Unsupported as yet
+ *
+ * Time stamping of data from ring. Various ideas on how to do this but none
+ * are remotely simple. Suggestions welcome.
+ *
+ * Individual enabling disabling of channels going into ring buffer
+ *
+ * Overflow handling (this is signaled for all but 8 bit ring buffer mode.)
+ *
+ * Motion detector using AND combinations of signals.
+ *
+ * Note: Be very careful about not touching an register bytes marked
+ * as reserved on the data sheet. They really mean it as changing convents of
+ * some will cause the device to lock up.
+ *
+ * Known issues - on rare occasions the interrupts lock up. Not sure why as yet.
+ * Can probably alleviate this by reading the interrupt register on start, but
+ * that is really just brushing the problem under the carpet.
+ */
+#ifndef _SCA3000
+#define _SCA3000
+
+#define SCA3000_WRITE_REG(a) (((a) << 2) | 0x02)
+#define SCA3000_READ_REG(a) ((a) << 2)
+
+#define SCA3000_REG_ADDR_REVID 0x00
+#define SCA3000_REVID_MAJOR_MASK 0xf0
+#define SCA3000_REVID_MINOR_MASK 0x0f
+
+#define SCA3000_REG_ADDR_STATUS 0x02
+#define SCA3000_LOCKED 0x20
+#define SCA3000_EEPROM_CS_ERROR 0x02
+#define SCA3000_SPI_FRAME_ERROR 0x01
+
+/* All reads done using register decrement so no need to directly access LSBs */
+#define SCA3000_REG_ADDR_X_MSB 0x05
+#define SCA3000_REG_ADDR_Y_MSB 0x07
+#define SCA3000_REG_ADDR_Z_MSB 0x09
+
+#define SCA3000_REG_ADDR_RING_OUT 0x0f
+
+/* Temp read untested - the e05 doesn't have the sensor */
+#define SCA3000_REG_ADDR_TEMP_MSB 0x13
+
+#define SCA3000_REG_ADDR_MODE 0x14
+#define SCA3000_MODE_PROT_MASK 0x28
+
+#define SCA3000_RING_BUF_ENABLE 0x80
+#define SCA3000_RING_BUF_8BIT 0x40
+/*
+ * Free fall detection triggers an interrupt if the acceleration
+ * is below a threshold for equivalent of 25cm drop
+ */
+#define SCA3000_FREE_FALL_DETECT 0x10
+#define SCA3000_MEAS_MODE_NORMAL 0x00
+#define SCA3000_MEAS_MODE_OP_1 0x01
+#define SCA3000_MEAS_MODE_OP_2 0x02
+
+/*
+ * In motion detection mode the accelerations are band pass filtered
+ * (approx 1 - 25Hz) and then a programmable threshold used to trigger
+ * and interrupt.
+ */
+#define SCA3000_MEAS_MODE_MOT_DET 0x03
+
+#define SCA3000_REG_ADDR_BUF_COUNT 0x15
+
+#define SCA3000_REG_ADDR_INT_STATUS 0x16
+
+#define SCA3000_INT_STATUS_THREE_QUARTERS 0x80
+#define SCA3000_INT_STATUS_HALF 0x40
+
+#define SCA3000_INT_STATUS_FREE_FALL 0x08
+#define SCA3000_INT_STATUS_Y_TRIGGER 0x04
+#define SCA3000_INT_STATUS_X_TRIGGER 0x02
+#define SCA3000_INT_STATUS_Z_TRIGGER 0x01
+
+/* Used to allow access to multiplexed registers */
+#define SCA3000_REG_ADDR_CTRL_SEL 0x18
+/* Only available for SCA3000-D03 and SCA3000-D01 */
+#define SCA3000_REG_CTRL_SEL_I2C_DISABLE 0x01
+#define SCA3000_REG_CTRL_SEL_MD_CTRL 0x02
+#define SCA3000_REG_CTRL_SEL_MD_Y_TH 0x03
+#define SCA3000_REG_CTRL_SEL_MD_X_TH 0x04
+#define SCA3000_REG_CTRL_SEL_MD_Z_TH 0x05
+/*
+ * BE VERY CAREFUL WITH THIS, IF 3 BITS ARE NOT SET the device
+ * will not function
+ */
+#define SCA3000_REG_CTRL_SEL_OUT_CTRL 0x0B
+#define SCA3000_OUT_CTRL_PROT_MASK 0xE0
+#define SCA3000_OUT_CTRL_BUF_X_EN 0x10
+#define SCA3000_OUT_CTRL_BUF_Y_EN 0x08
+#define SCA3000_OUT_CTRL_BUF_Z_EN 0x04
+#define SCA3000_OUT_CTRL_BUF_DIV_4 0x02
+#define SCA3000_OUT_CTRL_BUF_DIV_2 0x01
+
+/*
+ * Control which motion detector interrupts are on.
+ * For now only OR combinations are supported.
+ */
+#define SCA3000_MD_CTRL_PROT_MASK 0xC0
+#define SCA3000_MD_CTRL_OR_Y 0x01
+#define SCA3000_MD_CTRL_OR_X 0x02
+#define SCA3000_MD_CTRL_OR_Z 0x04
+/* Currently unsupported */
+#define SCA3000_MD_CTRL_AND_Y 0x08
+#define SCA3000_MD_CTRL_AND_X 0x10
+#define SAC3000_MD_CTRL_AND_Z 0x20
+
+/*
+ * Some control registers of complex access methods requiring this register to
+ * be used to remove a lock.
+ */
+#define SCA3000_REG_ADDR_UNLOCK 0x1e
+
+#define SCA3000_REG_ADDR_INT_MASK 0x21
+#define SCA3000_INT_MASK_PROT_MASK 0x1C
+
+#define SCA3000_INT_MASK_RING_THREE_QUARTER 0x80
+#define SCA3000_INT_MASK_RING_HALF 0x40
+
+#define SCA3000_INT_MASK_ALL_INTS 0x02
+#define SCA3000_INT_MASK_ACTIVE_HIGH 0x01
+#define SCA3000_INT_MASK_ACTIVE_LOW 0x00
+
+/* Values of multiplexed registers (write to ctrl_data after select) */
+#define SCA3000_REG_ADDR_CTRL_DATA 0x22
+
+/*
+ * Measurement modes available on some sca3000 series chips. Code assumes others
+ * may become available in the future.
+ *
+ * Bypass - Bypass the low-pass filter in the signal channel so as to increase
+ * signal bandwidth.
+ *
+ * Narrow - Narrow low-pass filtering of the signal channel and half output
+ * data rate by decimation.
+ *
+ * Wide - Widen low-pass filtering of signal channel to increase bandwidth
+ */
+#define SCA3000_OP_MODE_BYPASS 0x01
+#define SCA3000_OP_MODE_NARROW 0x02
+#define SCA3000_OP_MODE_WIDE 0x04
+#define SCA3000_MAX_TX 6
+#define SCA3000_MAX_RX 2
+
+/**
+ * struct sca3000_state - device instance state information
+ * @us: the associated spi device
+ * @info: chip variant information
+ * @interrupt_handler_ws: event interrupt handler for all events
+ * @last_timestamp: the timestamp of the last event
+ * @mo_det_use_count: reference counter for the motion detection unit
+ * @lock: lock used to protect elements of sca3000_state
+ * and the underlying device state.
+ * @bpse: number of bits per scan element
+ * @tx: dma-able transmit buffer
+ * @rx: dma-able receive buffer
+ **/
+struct sca3000_state {
+ struct spi_device *us;
+ const struct sca3000_chip_info *info;
+ struct work_struct interrupt_handler_ws;
+ s64 last_timestamp;
+ int mo_det_use_count;
+ struct mutex lock;
+ int bpse;
+ /* Can these share a cacheline ? */
+ u8 rx[2] ____cacheline_aligned;
+ u8 tx[6] ____cacheline_aligned;
+};
+
+/**
+ * struct sca3000_chip_info - model dependent parameters
+ * @scale: scale * 10^-6
+ * @temp_output: some devices have temperature sensors.
+ * @measurement_mode_freq: normal mode sampling frequency
+ * @option_mode_1: first optional mode. Not all models have one
+ * @option_mode_1_freq: option mode 1 sampling frequency
+ * @option_mode_2: second optional mode. Not all chips have one
+ * @option_mode_2_freq: option mode 2 sampling frequency
+ *
+ * This structure is used to hold information about the functionality of a given
+ * sca3000 variant.
+ **/
+struct sca3000_chip_info {
+ unsigned int scale;
+ bool temp_output;
+ int measurement_mode_freq;
+ int option_mode_1;
+ int option_mode_1_freq;
+ int option_mode_2;
+ int option_mode_2_freq;
+ int mot_det_mult_xz[6];
+ int mot_det_mult_y[7];
+};
+
+int sca3000_read_data_short(struct sca3000_state *st,
+ u8 reg_address_high,
+ int len);
+
+/**
+ * sca3000_write_reg() write a single register
+ * @address: address of register on chip
+ * @val: value to be written to register
+ *
+ * The main lock must be held.
+ **/
+int sca3000_write_reg(struct sca3000_state *st, u8 address, u8 val);
+
+#ifdef CONFIG_IIO_BUFFER
+/**
+ * sca3000_register_ring_funcs() setup the ring state change functions
+ **/
+void sca3000_register_ring_funcs(struct iio_dev *indio_dev);
+
+/**
+ * sca3000_configure_ring() - allocate and configure ring buffer
+ * @indio_dev: iio-core device whose ring is to be configured
+ *
+ * The hardware ring buffer needs far fewer ring buffer functions than
+ * a software one as a lot of things are handled automatically.
+ * This function also tells the iio core that our device supports a
+ * hardware ring buffer mode.
+ **/
+int sca3000_configure_ring(struct iio_dev *indio_dev);
+
+/**
+ * sca3000_unconfigure_ring() - deallocate the ring buffer
+ * @indio_dev: iio-core device whose ring we are freeing
+ **/
+void sca3000_unconfigure_ring(struct iio_dev *indio_dev);
+
+/**
+ * sca3000_ring_int_process() handles ring related event pushing and escalation
+ * @val: the event code
+ **/
+void sca3000_ring_int_process(u8 val, struct iio_buffer *ring);
+
+#else
+static inline void sca3000_register_ring_funcs(struct iio_dev *indio_dev)
+{
+}
+
+static inline
+int sca3000_register_ring_access_and_init(struct iio_dev *indio_dev)
+{
+ return 0;
+}
+
+static inline void sca3000_ring_int_process(u8 val, void *ring)
+{
+}
+
+#endif
+#endif /* _SCA3000 */
diff --git a/drivers/staging/iio/accel/sca3000_core.c b/drivers/staging/iio/accel/sca3000_core.c
new file mode 100644
index 000000000..b614f272b
--- /dev/null
+++ b/drivers/staging/iio/accel/sca3000_core.c
@@ -0,0 +1,1209 @@
+/*
+ * sca3000_core.c -- support VTI sca3000 series accelerometers via SPI
+ *
+ * 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.
+ *
+ * Copyright (c) 2009 Jonathan Cameron <jic23@kernel.org>
+ *
+ * See industrialio/accels/sca3000.h for comments.
+ */
+
+#include <linux/interrupt.h>
+#include <linux/fs.h>
+#include <linux/device.h>
+#include <linux/slab.h>
+#include <linux/kernel.h>
+#include <linux/spi/spi.h>
+#include <linux/sysfs.h>
+#include <linux/module.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/iio/events.h>
+#include <linux/iio/buffer.h>
+
+#include "sca3000.h"
+
+enum sca3000_variant {
+ d01,
+ e02,
+ e04,
+ e05,
+};
+
+/*
+ * Note where option modes are not defined, the chip simply does not
+ * support any.
+ * Other chips in the sca3000 series use i2c and are not included here.
+ *
+ * Some of these devices are only listed in the family data sheet and
+ * do not actually appear to be available.
+ */
+static const struct sca3000_chip_info sca3000_spi_chip_info_tbl[] = {
+ [d01] = {
+ .scale = 7357,
+ .temp_output = true,
+ .measurement_mode_freq = 250,
+ .option_mode_1 = SCA3000_OP_MODE_BYPASS,
+ .option_mode_1_freq = 250,
+ .mot_det_mult_xz = {50, 100, 200, 350, 650, 1300},
+ .mot_det_mult_y = {50, 100, 150, 250, 450, 850, 1750},
+ },
+ [e02] = {
+ .scale = 9810,
+ .measurement_mode_freq = 125,
+ .option_mode_1 = SCA3000_OP_MODE_NARROW,
+ .option_mode_1_freq = 63,
+ .mot_det_mult_xz = {100, 150, 300, 550, 1050, 2050},
+ .mot_det_mult_y = {50, 100, 200, 350, 700, 1350, 2700},
+ },
+ [e04] = {
+ .scale = 19620,
+ .measurement_mode_freq = 100,
+ .option_mode_1 = SCA3000_OP_MODE_NARROW,
+ .option_mode_1_freq = 50,
+ .option_mode_2 = SCA3000_OP_MODE_WIDE,
+ .option_mode_2_freq = 400,
+ .mot_det_mult_xz = {200, 300, 600, 1100, 2100, 4100},
+ .mot_det_mult_y = {100, 200, 400, 7000, 1400, 2700, 54000},
+ },
+ [e05] = {
+ .scale = 61313,
+ .measurement_mode_freq = 200,
+ .option_mode_1 = SCA3000_OP_MODE_NARROW,
+ .option_mode_1_freq = 50,
+ .option_mode_2 = SCA3000_OP_MODE_WIDE,
+ .option_mode_2_freq = 400,
+ .mot_det_mult_xz = {600, 900, 1700, 3200, 6100, 11900},
+ .mot_det_mult_y = {300, 600, 1200, 2000, 4100, 7800, 15600},
+ },
+};
+
+int sca3000_write_reg(struct sca3000_state *st, u8 address, u8 val)
+{
+ st->tx[0] = SCA3000_WRITE_REG(address);
+ st->tx[1] = val;
+ return spi_write(st->us, st->tx, 2);
+}
+
+int sca3000_read_data_short(struct sca3000_state *st,
+ uint8_t reg_address_high,
+ int len)
+{
+ struct spi_transfer xfer[2] = {
+ {
+ .len = 1,
+ .tx_buf = st->tx,
+ }, {
+ .len = len,
+ .rx_buf = st->rx,
+ }
+ };
+ st->tx[0] = SCA3000_READ_REG(reg_address_high);
+
+ return spi_sync_transfer(st->us, xfer, ARRAY_SIZE(xfer));
+}
+
+/**
+ * sca3000_reg_lock_on() test if the ctrl register lock is on
+ *
+ * Lock must be held.
+ **/
+static int sca3000_reg_lock_on(struct sca3000_state *st)
+{
+ int ret;
+
+ ret = sca3000_read_data_short(st, SCA3000_REG_ADDR_STATUS, 1);
+ if (ret < 0)
+ return ret;
+
+ return !(st->rx[0] & SCA3000_LOCKED);
+}
+
+/**
+ * __sca3000_unlock_reg_lock() unlock the control registers
+ *
+ * Note the device does not appear to support doing this in a single transfer.
+ * This should only ever be used as part of ctrl reg read.
+ * Lock must be held before calling this
+ **/
+static int __sca3000_unlock_reg_lock(struct sca3000_state *st)
+{
+ struct spi_transfer xfer[3] = {
+ {
+ .len = 2,
+ .cs_change = 1,
+ .tx_buf = st->tx,
+ }, {
+ .len = 2,
+ .cs_change = 1,
+ .tx_buf = st->tx + 2,
+ }, {
+ .len = 2,
+ .tx_buf = st->tx + 4,
+ },
+ };
+ st->tx[0] = SCA3000_WRITE_REG(SCA3000_REG_ADDR_UNLOCK);
+ st->tx[1] = 0x00;
+ st->tx[2] = SCA3000_WRITE_REG(SCA3000_REG_ADDR_UNLOCK);
+ st->tx[3] = 0x50;
+ st->tx[4] = SCA3000_WRITE_REG(SCA3000_REG_ADDR_UNLOCK);
+ st->tx[5] = 0xA0;
+
+ return spi_sync_transfer(st->us, xfer, ARRAY_SIZE(xfer));
+}
+
+/**
+ * sca3000_write_ctrl_reg() write to a lock protect ctrl register
+ * @sel: selects which registers we wish to write to
+ * @val: the value to be written
+ *
+ * Certain control registers are protected against overwriting by the lock
+ * register and use a shared write address. This function allows writing of
+ * these registers.
+ * Lock must be held.
+ **/
+static int sca3000_write_ctrl_reg(struct sca3000_state *st,
+ uint8_t sel,
+ uint8_t val)
+{
+
+ int ret;
+
+ ret = sca3000_reg_lock_on(st);
+ if (ret < 0)
+ goto error_ret;
+ if (ret) {
+ ret = __sca3000_unlock_reg_lock(st);
+ if (ret)
+ goto error_ret;
+ }
+
+ /* Set the control select register */
+ ret = sca3000_write_reg(st, SCA3000_REG_ADDR_CTRL_SEL, sel);
+ if (ret)
+ goto error_ret;
+
+ /* Write the actual value into the register */
+ ret = sca3000_write_reg(st, SCA3000_REG_ADDR_CTRL_DATA, val);
+
+error_ret:
+ return ret;
+}
+
+/**
+ * sca3000_read_ctrl_reg() read from lock protected control register.
+ *
+ * Lock must be held.
+ **/
+static int sca3000_read_ctrl_reg(struct sca3000_state *st,
+ u8 ctrl_reg)
+{
+ int ret;
+
+ ret = sca3000_reg_lock_on(st);
+ if (ret < 0)
+ goto error_ret;
+ if (ret) {
+ ret = __sca3000_unlock_reg_lock(st);
+ if (ret)
+ goto error_ret;
+ }
+ /* Set the control select register */
+ ret = sca3000_write_reg(st, SCA3000_REG_ADDR_CTRL_SEL, ctrl_reg);
+ if (ret)
+ goto error_ret;
+ ret = sca3000_read_data_short(st, SCA3000_REG_ADDR_CTRL_DATA, 1);
+ if (ret)
+ goto error_ret;
+ else
+ return st->rx[0];
+error_ret:
+ return ret;
+}
+
+/**
+ * sca3000_show_rev() - sysfs interface to read the chip revision number
+ **/
+static ssize_t sca3000_show_rev(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ int len = 0, ret;
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct sca3000_state *st = iio_priv(indio_dev);
+
+ mutex_lock(&st->lock);
+ ret = sca3000_read_data_short(st, SCA3000_REG_ADDR_REVID, 1);
+ if (ret < 0)
+ goto error_ret;
+ len += sprintf(buf + len,
+ "major=%d, minor=%d\n",
+ st->rx[0] & SCA3000_REVID_MAJOR_MASK,
+ st->rx[0] & SCA3000_REVID_MINOR_MASK);
+error_ret:
+ mutex_unlock(&st->lock);
+
+ return ret ? ret : len;
+}
+
+/**
+ * sca3000_show_available_measurement_modes() display available modes
+ *
+ * This is all read from chip specific data in the driver. Not all
+ * of the sca3000 series support modes other than normal.
+ **/
+static ssize_t
+sca3000_show_available_measurement_modes(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct sca3000_state *st = iio_priv(indio_dev);
+ int len = 0;
+
+ len += sprintf(buf + len, "0 - normal mode");
+ switch (st->info->option_mode_1) {
+ case SCA3000_OP_MODE_NARROW:
+ len += sprintf(buf + len, ", 1 - narrow mode");
+ break;
+ case SCA3000_OP_MODE_BYPASS:
+ len += sprintf(buf + len, ", 1 - bypass mode");
+ break;
+ }
+ switch (st->info->option_mode_2) {
+ case SCA3000_OP_MODE_WIDE:
+ len += sprintf(buf + len, ", 2 - wide mode");
+ break;
+ }
+ /* always supported */
+ len += sprintf(buf + len, " 3 - motion detection\n");
+
+ return len;
+}
+
+/**
+ * sca3000_show_measurement_mode() sysfs read of current mode
+ **/
+static ssize_t
+sca3000_show_measurement_mode(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct sca3000_state *st = iio_priv(indio_dev);
+ int len = 0, ret;
+
+ mutex_lock(&st->lock);
+ ret = sca3000_read_data_short(st, SCA3000_REG_ADDR_MODE, 1);
+ if (ret)
+ goto error_ret;
+ /* mask bottom 2 bits - only ones that are relevant */
+ st->rx[0] &= 0x03;
+ switch (st->rx[0]) {
+ case SCA3000_MEAS_MODE_NORMAL:
+ len += sprintf(buf + len, "0 - normal mode\n");
+ break;
+ case SCA3000_MEAS_MODE_MOT_DET:
+ len += sprintf(buf + len, "3 - motion detection\n");
+ break;
+ case SCA3000_MEAS_MODE_OP_1:
+ switch (st->info->option_mode_1) {
+ case SCA3000_OP_MODE_NARROW:
+ len += sprintf(buf + len, "1 - narrow mode\n");
+ break;
+ case SCA3000_OP_MODE_BYPASS:
+ len += sprintf(buf + len, "1 - bypass mode\n");
+ break;
+ }
+ break;
+ case SCA3000_MEAS_MODE_OP_2:
+ switch (st->info->option_mode_2) {
+ case SCA3000_OP_MODE_WIDE:
+ len += sprintf(buf + len, "2 - wide mode\n");
+ break;
+ }
+ break;
+ }
+
+error_ret:
+ mutex_unlock(&st->lock);
+
+ return ret ? ret : len;
+}
+
+/**
+ * sca3000_store_measurement_mode() set the current mode
+ **/
+static ssize_t
+sca3000_store_measurement_mode(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t len)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct sca3000_state *st = iio_priv(indio_dev);
+ int ret;
+ u8 mask = 0x03;
+ u8 val;
+
+ mutex_lock(&st->lock);
+ ret = kstrtou8(buf, 10, &val);
+ if (ret)
+ goto error_ret;
+ if (val > 3) {
+ ret = -EINVAL;
+ goto error_ret;
+ }
+ ret = sca3000_read_data_short(st, SCA3000_REG_ADDR_MODE, 1);
+ if (ret)
+ goto error_ret;
+ st->rx[0] &= ~mask;
+ st->rx[0] |= (val & mask);
+ ret = sca3000_write_reg(st, SCA3000_REG_ADDR_MODE, st->rx[0]);
+ if (ret)
+ goto error_ret;
+ mutex_unlock(&st->lock);
+
+ return len;
+
+error_ret:
+ mutex_unlock(&st->lock);
+
+ return ret;
+}
+
+
+/*
+ * Not even vaguely standard attributes so defined here rather than
+ * in the relevant IIO core headers
+ */
+static IIO_DEVICE_ATTR(measurement_mode_available, S_IRUGO,
+ sca3000_show_available_measurement_modes,
+ NULL, 0);
+
+static IIO_DEVICE_ATTR(measurement_mode, S_IRUGO | S_IWUSR,
+ sca3000_show_measurement_mode,
+ sca3000_store_measurement_mode,
+ 0);
+
+/* More standard attributes */
+
+static IIO_DEVICE_ATTR(revision, S_IRUGO, sca3000_show_rev, NULL, 0);
+
+static const struct iio_event_spec sca3000_event = {
+ .type = IIO_EV_TYPE_MAG,
+ .dir = IIO_EV_DIR_RISING,
+ .mask_separate = BIT(IIO_EV_INFO_VALUE) | BIT(IIO_EV_INFO_ENABLE),
+};
+
+#define SCA3000_CHAN(index, mod) \
+ { \
+ .type = IIO_ACCEL, \
+ .modified = 1, \
+ .channel2 = mod, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),\
+ .address = index, \
+ .scan_index = index, \
+ .scan_type = { \
+ .sign = 's', \
+ .realbits = 11, \
+ .storagebits = 16, \
+ .shift = 5, \
+ }, \
+ .event_spec = &sca3000_event, \
+ .num_event_specs = 1, \
+ }
+
+static const struct iio_chan_spec sca3000_channels[] = {
+ SCA3000_CHAN(0, IIO_MOD_X),
+ SCA3000_CHAN(1, IIO_MOD_Y),
+ SCA3000_CHAN(2, IIO_MOD_Z),
+};
+
+static const struct iio_chan_spec sca3000_channels_with_temp[] = {
+ SCA3000_CHAN(0, IIO_MOD_X),
+ SCA3000_CHAN(1, IIO_MOD_Y),
+ SCA3000_CHAN(2, IIO_MOD_Z),
+ {
+ .type = IIO_TEMP,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) |
+ BIT(IIO_CHAN_INFO_OFFSET),
+ /* No buffer support */
+ .scan_index = -1,
+ },
+};
+
+static u8 sca3000_addresses[3][3] = {
+ [0] = {SCA3000_REG_ADDR_X_MSB, SCA3000_REG_CTRL_SEL_MD_X_TH,
+ SCA3000_MD_CTRL_OR_X},
+ [1] = {SCA3000_REG_ADDR_Y_MSB, SCA3000_REG_CTRL_SEL_MD_Y_TH,
+ SCA3000_MD_CTRL_OR_Y},
+ [2] = {SCA3000_REG_ADDR_Z_MSB, SCA3000_REG_CTRL_SEL_MD_Z_TH,
+ SCA3000_MD_CTRL_OR_Z},
+};
+
+static int sca3000_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val,
+ int *val2,
+ long mask)
+{
+ struct sca3000_state *st = iio_priv(indio_dev);
+ int ret;
+ u8 address;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ mutex_lock(&st->lock);
+ if (chan->type == IIO_ACCEL) {
+ if (st->mo_det_use_count) {
+ mutex_unlock(&st->lock);
+ return -EBUSY;
+ }
+ address = sca3000_addresses[chan->address][0];
+ ret = sca3000_read_data_short(st, address, 2);
+ if (ret < 0) {
+ mutex_unlock(&st->lock);
+ return ret;
+ }
+ *val = (be16_to_cpup((__be16 *)st->rx) >> 3) & 0x1FFF;
+ *val = ((*val) << (sizeof(*val)*8 - 13)) >>
+ (sizeof(*val)*8 - 13);
+ } else {
+ /* get the temperature when available */
+ ret = sca3000_read_data_short(st,
+ SCA3000_REG_ADDR_TEMP_MSB, 2);
+ if (ret < 0) {
+ mutex_unlock(&st->lock);
+ return ret;
+ }
+ *val = ((st->rx[0] & 0x3F) << 3) |
+ ((st->rx[1] & 0xE0) >> 5);
+ }
+ mutex_unlock(&st->lock);
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_SCALE:
+ *val = 0;
+ if (chan->type == IIO_ACCEL)
+ *val2 = st->info->scale;
+ else /* temperature */
+ *val2 = 555556;
+ return IIO_VAL_INT_PLUS_MICRO;
+ case IIO_CHAN_INFO_OFFSET:
+ *val = -214;
+ *val2 = 600000;
+ return IIO_VAL_INT_PLUS_MICRO;
+ default:
+ return -EINVAL;
+ }
+}
+
+/**
+ * sca3000_read_av_freq() sysfs function to get available frequencies
+ *
+ * The later modes are only relevant to the ring buffer - and depend on current
+ * mode. Note that data sheet gives rather wide tolerances for these so integer
+ * division will give good enough answer and not all chips have them specified
+ * at all.
+ **/
+static ssize_t sca3000_read_av_freq(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct sca3000_state *st = iio_priv(indio_dev);
+ int len = 0, ret, val;
+
+ mutex_lock(&st->lock);
+ ret = sca3000_read_data_short(st, SCA3000_REG_ADDR_MODE, 1);
+ val = st->rx[0];
+ mutex_unlock(&st->lock);
+ if (ret)
+ goto error_ret;
+
+ switch (val & 0x03) {
+ case SCA3000_MEAS_MODE_NORMAL:
+ len += sprintf(buf + len, "%d %d %d\n",
+ st->info->measurement_mode_freq,
+ st->info->measurement_mode_freq/2,
+ st->info->measurement_mode_freq/4);
+ break;
+ case SCA3000_MEAS_MODE_OP_1:
+ len += sprintf(buf + len, "%d %d %d\n",
+ st->info->option_mode_1_freq,
+ st->info->option_mode_1_freq/2,
+ st->info->option_mode_1_freq/4);
+ break;
+ case SCA3000_MEAS_MODE_OP_2:
+ len += sprintf(buf + len, "%d %d %d\n",
+ st->info->option_mode_2_freq,
+ st->info->option_mode_2_freq/2,
+ st->info->option_mode_2_freq/4);
+ break;
+ }
+ return len;
+error_ret:
+ return ret;
+}
+/**
+ * __sca3000_get_base_freq() obtain mode specific base frequency
+ *
+ * lock must be held
+ **/
+static inline int __sca3000_get_base_freq(struct sca3000_state *st,
+ const struct sca3000_chip_info *info,
+ int *base_freq)
+{
+ int ret;
+
+ ret = sca3000_read_data_short(st, SCA3000_REG_ADDR_MODE, 1);
+ if (ret)
+ goto error_ret;
+ switch (0x03 & st->rx[0]) {
+ case SCA3000_MEAS_MODE_NORMAL:
+ *base_freq = info->measurement_mode_freq;
+ break;
+ case SCA3000_MEAS_MODE_OP_1:
+ *base_freq = info->option_mode_1_freq;
+ break;
+ case SCA3000_MEAS_MODE_OP_2:
+ *base_freq = info->option_mode_2_freq;
+ break;
+ }
+error_ret:
+ return ret;
+}
+
+/**
+ * sca3000_read_frequency() sysfs interface to get the current frequency
+ **/
+static ssize_t sca3000_read_frequency(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct sca3000_state *st = iio_priv(indio_dev);
+ int ret, len = 0, base_freq = 0, val;
+
+ mutex_lock(&st->lock);
+ ret = __sca3000_get_base_freq(st, st->info, &base_freq);
+ if (ret)
+ goto error_ret_mut;
+ ret = sca3000_read_ctrl_reg(st, SCA3000_REG_CTRL_SEL_OUT_CTRL);
+ mutex_unlock(&st->lock);
+ if (ret)
+ goto error_ret;
+ val = ret;
+ if (base_freq > 0)
+ switch (val & 0x03) {
+ case 0x00:
+ case 0x03:
+ len = sprintf(buf, "%d\n", base_freq);
+ break;
+ case 0x01:
+ len = sprintf(buf, "%d\n", base_freq/2);
+ break;
+ case 0x02:
+ len = sprintf(buf, "%d\n", base_freq/4);
+ break;
+ }
+
+ return len;
+error_ret_mut:
+ mutex_unlock(&st->lock);
+error_ret:
+ return ret;
+}
+
+/**
+ * sca3000_set_frequency() sysfs interface to set the current frequency
+ **/
+static ssize_t sca3000_set_frequency(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t len)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct sca3000_state *st = iio_priv(indio_dev);
+ int ret, base_freq = 0;
+ int ctrlval;
+ int val;
+
+ ret = kstrtoint(buf, 10, &val);
+ if (ret)
+ return ret;
+
+ mutex_lock(&st->lock);
+ /* What mode are we in? */
+ ret = __sca3000_get_base_freq(st, st->info, &base_freq);
+ if (ret)
+ goto error_free_lock;
+
+ ret = sca3000_read_ctrl_reg(st, SCA3000_REG_CTRL_SEL_OUT_CTRL);
+ if (ret < 0)
+ goto error_free_lock;
+ ctrlval = ret;
+ /* clear the bits */
+ ctrlval &= ~0x03;
+
+ if (val == base_freq/2) {
+ ctrlval |= SCA3000_OUT_CTRL_BUF_DIV_2;
+ } else if (val == base_freq/4) {
+ ctrlval |= SCA3000_OUT_CTRL_BUF_DIV_4;
+ } else if (val != base_freq) {
+ ret = -EINVAL;
+ goto error_free_lock;
+ }
+ ret = sca3000_write_ctrl_reg(st, SCA3000_REG_CTRL_SEL_OUT_CTRL,
+ ctrlval);
+error_free_lock:
+ mutex_unlock(&st->lock);
+
+ return ret ? ret : len;
+}
+
+/*
+ * Should only really be registered if ring buffer support is compiled in.
+ * Does no harm however and doing it right would add a fair bit of complexity
+ */
+static IIO_DEV_ATTR_SAMP_FREQ_AVAIL(sca3000_read_av_freq);
+
+static IIO_DEV_ATTR_SAMP_FREQ(S_IWUSR | S_IRUGO,
+ sca3000_read_frequency,
+ sca3000_set_frequency);
+
+/**
+ * sca3000_read_thresh() - query of a threshold
+ **/
+static int sca3000_read_thresh(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan,
+ enum iio_event_type type,
+ enum iio_event_direction dir,
+ enum iio_event_info info,
+ int *val, int *val2)
+{
+ int ret, i;
+ struct sca3000_state *st = iio_priv(indio_dev);
+ int num = chan->channel2;
+
+ mutex_lock(&st->lock);
+ ret = sca3000_read_ctrl_reg(st, sca3000_addresses[num][1]);
+ mutex_unlock(&st->lock);
+ if (ret < 0)
+ return ret;
+ *val = 0;
+ if (num == 1)
+ for_each_set_bit(i, (unsigned long *)&ret,
+ ARRAY_SIZE(st->info->mot_det_mult_y))
+ *val += st->info->mot_det_mult_y[i];
+ else
+ for_each_set_bit(i, (unsigned long *)&ret,
+ ARRAY_SIZE(st->info->mot_det_mult_xz))
+ *val += st->info->mot_det_mult_xz[i];
+
+ return IIO_VAL_INT;
+}
+
+/**
+ * sca3000_write_thresh() control of threshold
+ **/
+static int sca3000_write_thresh(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan,
+ enum iio_event_type type,
+ enum iio_event_direction dir,
+ enum iio_event_info info,
+ int val, int val2)
+{
+ struct sca3000_state *st = iio_priv(indio_dev);
+ int num = chan->channel2;
+ int ret;
+ int i;
+ u8 nonlinear = 0;
+
+ if (num == 1) {
+ i = ARRAY_SIZE(st->info->mot_det_mult_y);
+ while (i > 0)
+ if (val >= st->info->mot_det_mult_y[--i]) {
+ nonlinear |= (1 << i);
+ val -= st->info->mot_det_mult_y[i];
+ }
+ } else {
+ i = ARRAY_SIZE(st->info->mot_det_mult_xz);
+ while (i > 0)
+ if (val >= st->info->mot_det_mult_xz[--i]) {
+ nonlinear |= (1 << i);
+ val -= st->info->mot_det_mult_xz[i];
+ }
+ }
+
+ mutex_lock(&st->lock);
+ ret = sca3000_write_ctrl_reg(st, sca3000_addresses[num][1], nonlinear);
+ mutex_unlock(&st->lock);
+
+ return ret;
+}
+
+static struct attribute *sca3000_attributes[] = {
+ &iio_dev_attr_revision.dev_attr.attr,
+ &iio_dev_attr_measurement_mode_available.dev_attr.attr,
+ &iio_dev_attr_measurement_mode.dev_attr.attr,
+ &iio_dev_attr_sampling_frequency_available.dev_attr.attr,
+ &iio_dev_attr_sampling_frequency.dev_attr.attr,
+ NULL,
+};
+
+static const struct attribute_group sca3000_attribute_group = {
+ .attrs = sca3000_attributes,
+};
+
+/**
+ * sca3000_event_handler() - handling ring and non ring events
+ *
+ * Ring related interrupt handler. Depending on event, push to
+ * the ring buffer event chrdev or the event one.
+ *
+ * This function is complicated by the fact that the devices can signify ring
+ * and non ring events via the same interrupt line and they can only
+ * be distinguished via a read of the relevant status register.
+ **/
+static irqreturn_t sca3000_event_handler(int irq, void *private)
+{
+ struct iio_dev *indio_dev = private;
+ struct sca3000_state *st = iio_priv(indio_dev);
+ int ret, val;
+ s64 last_timestamp = iio_get_time_ns();
+
+ /*
+ * Could lead if badly timed to an extra read of status reg,
+ * but ensures no interrupt is missed.
+ */
+ mutex_lock(&st->lock);
+ ret = sca3000_read_data_short(st, SCA3000_REG_ADDR_INT_STATUS, 1);
+ val = st->rx[0];
+ mutex_unlock(&st->lock);
+ if (ret)
+ goto done;
+
+ sca3000_ring_int_process(val, indio_dev->buffer);
+
+ if (val & SCA3000_INT_STATUS_FREE_FALL)
+ iio_push_event(indio_dev,
+ IIO_MOD_EVENT_CODE(IIO_ACCEL,
+ 0,
+ IIO_MOD_X_AND_Y_AND_Z,
+ IIO_EV_TYPE_MAG,
+ IIO_EV_DIR_FALLING),
+ last_timestamp);
+
+ if (val & SCA3000_INT_STATUS_Y_TRIGGER)
+ iio_push_event(indio_dev,
+ IIO_MOD_EVENT_CODE(IIO_ACCEL,
+ 0,
+ IIO_MOD_Y,
+ IIO_EV_TYPE_MAG,
+ IIO_EV_DIR_RISING),
+ last_timestamp);
+
+ if (val & SCA3000_INT_STATUS_X_TRIGGER)
+ iio_push_event(indio_dev,
+ IIO_MOD_EVENT_CODE(IIO_ACCEL,
+ 0,
+ IIO_MOD_X,
+ IIO_EV_TYPE_MAG,
+ IIO_EV_DIR_RISING),
+ last_timestamp);
+
+ if (val & SCA3000_INT_STATUS_Z_TRIGGER)
+ iio_push_event(indio_dev,
+ IIO_MOD_EVENT_CODE(IIO_ACCEL,
+ 0,
+ IIO_MOD_Z,
+ IIO_EV_TYPE_MAG,
+ IIO_EV_DIR_RISING),
+ last_timestamp);
+
+done:
+ return IRQ_HANDLED;
+}
+
+/**
+ * sca3000_read_event_config() what events are enabled
+ **/
+static int sca3000_read_event_config(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan,
+ enum iio_event_type type,
+ enum iio_event_direction dir)
+{
+ struct sca3000_state *st = iio_priv(indio_dev);
+ int ret;
+ u8 protect_mask = 0x03;
+ int num = chan->channel2;
+
+ /* read current value of mode register */
+ mutex_lock(&st->lock);
+ ret = sca3000_read_data_short(st, SCA3000_REG_ADDR_MODE, 1);
+ if (ret)
+ goto error_ret;
+
+ if ((st->rx[0] & protect_mask) != SCA3000_MEAS_MODE_MOT_DET)
+ ret = 0;
+ else {
+ ret = sca3000_read_ctrl_reg(st, SCA3000_REG_CTRL_SEL_MD_CTRL);
+ if (ret < 0)
+ goto error_ret;
+ /* only supporting logical or's for now */
+ ret = !!(ret & sca3000_addresses[num][2]);
+ }
+error_ret:
+ mutex_unlock(&st->lock);
+
+ return ret;
+}
+/**
+ * sca3000_query_free_fall_mode() is free fall mode enabled
+ **/
+static ssize_t sca3000_query_free_fall_mode(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ int ret;
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct sca3000_state *st = iio_priv(indio_dev);
+ int val;
+
+ mutex_lock(&st->lock);
+ ret = sca3000_read_data_short(st, SCA3000_REG_ADDR_MODE, 1);
+ val = st->rx[0];
+ mutex_unlock(&st->lock);
+ if (ret < 0)
+ return ret;
+ return sprintf(buf, "%d\n", !!(val & SCA3000_FREE_FALL_DETECT));
+}
+
+/**
+ * sca3000_set_free_fall_mode() simple on off control for free fall int
+ *
+ * In these chips the free fall detector should send an interrupt if
+ * the device falls more than 25cm. This has not been tested due
+ * to fragile wiring.
+ **/
+static ssize_t sca3000_set_free_fall_mode(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t len)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct sca3000_state *st = iio_priv(indio_dev);
+ u8 val;
+ int ret;
+ u8 protect_mask = SCA3000_FREE_FALL_DETECT;
+
+ mutex_lock(&st->lock);
+ ret = kstrtou8(buf, 10, &val);
+ if (ret)
+ goto error_ret;
+
+ /* read current value of mode register */
+ ret = sca3000_read_data_short(st, SCA3000_REG_ADDR_MODE, 1);
+ if (ret)
+ goto error_ret;
+
+ /* if off and should be on */
+ if (val && !(st->rx[0] & protect_mask))
+ ret = sca3000_write_reg(st, SCA3000_REG_ADDR_MODE,
+ (st->rx[0] | SCA3000_FREE_FALL_DETECT));
+ /* if on and should be off */
+ else if (!val && (st->rx[0] & protect_mask))
+ ret = sca3000_write_reg(st, SCA3000_REG_ADDR_MODE,
+ (st->rx[0] & ~protect_mask));
+error_ret:
+ mutex_unlock(&st->lock);
+
+ return ret ? ret : len;
+}
+
+/**
+ * sca3000_write_event_config() simple on off control for motion detector
+ *
+ * This is a per axis control, but enabling any will result in the
+ * motion detector unit being enabled.
+ * N.B. enabling motion detector stops normal data acquisition.
+ * There is a complexity in knowing which mode to return to when
+ * this mode is disabled. Currently normal mode is assumed.
+ **/
+static int sca3000_write_event_config(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan,
+ enum iio_event_type type,
+ enum iio_event_direction dir,
+ int state)
+{
+ struct sca3000_state *st = iio_priv(indio_dev);
+ int ret, ctrlval;
+ u8 protect_mask = 0x03;
+ int num = chan->channel2;
+
+ mutex_lock(&st->lock);
+ /*
+ * First read the motion detector config to find out if
+ * this axis is on
+ */
+ ret = sca3000_read_ctrl_reg(st, SCA3000_REG_CTRL_SEL_MD_CTRL);
+ if (ret < 0)
+ goto exit_point;
+ ctrlval = ret;
+ /* if off and should be on */
+ if (state && !(ctrlval & sca3000_addresses[num][2])) {
+ ret = sca3000_write_ctrl_reg(st,
+ SCA3000_REG_CTRL_SEL_MD_CTRL,
+ ctrlval |
+ sca3000_addresses[num][2]);
+ if (ret)
+ goto exit_point;
+ st->mo_det_use_count++;
+ } else if (!state && (ctrlval & sca3000_addresses[num][2])) {
+ ret = sca3000_write_ctrl_reg(st,
+ SCA3000_REG_CTRL_SEL_MD_CTRL,
+ ctrlval &
+ ~(sca3000_addresses[num][2]));
+ if (ret)
+ goto exit_point;
+ st->mo_det_use_count--;
+ }
+
+ /* read current value of mode register */
+ ret = sca3000_read_data_short(st, SCA3000_REG_ADDR_MODE, 1);
+ if (ret)
+ goto exit_point;
+ /* if off and should be on */
+ if ((st->mo_det_use_count)
+ && ((st->rx[0] & protect_mask) != SCA3000_MEAS_MODE_MOT_DET))
+ ret = sca3000_write_reg(st, SCA3000_REG_ADDR_MODE,
+ (st->rx[0] & ~protect_mask)
+ | SCA3000_MEAS_MODE_MOT_DET);
+ /* if on and should be off */
+ else if (!(st->mo_det_use_count)
+ && ((st->rx[0] & protect_mask) == SCA3000_MEAS_MODE_MOT_DET))
+ ret = sca3000_write_reg(st, SCA3000_REG_ADDR_MODE,
+ (st->rx[0] & ~protect_mask));
+exit_point:
+ mutex_unlock(&st->lock);
+
+ return ret;
+}
+
+/* Free fall detector related event attribute */
+static IIO_DEVICE_ATTR_NAMED(accel_xayaz_mag_falling_en,
+ in_accel_x&y&z_mag_falling_en,
+ S_IRUGO | S_IWUSR,
+ sca3000_query_free_fall_mode,
+ sca3000_set_free_fall_mode,
+ 0);
+
+static IIO_CONST_ATTR_NAMED(accel_xayaz_mag_falling_period,
+ in_accel_x&y&z_mag_falling_period,
+ "0.226");
+
+static struct attribute *sca3000_event_attributes[] = {
+ &iio_dev_attr_accel_xayaz_mag_falling_en.dev_attr.attr,
+ &iio_const_attr_accel_xayaz_mag_falling_period.dev_attr.attr,
+ NULL,
+};
+
+static struct attribute_group sca3000_event_attribute_group = {
+ .attrs = sca3000_event_attributes,
+ .name = "events",
+};
+
+/**
+ * sca3000_clean_setup() get the device into a predictable state
+ *
+ * Devices use flash memory to store many of the register values
+ * and hence can come up in somewhat unpredictable states.
+ * Hence reset everything on driver load.
+ **/
+static int sca3000_clean_setup(struct sca3000_state *st)
+{
+ int ret;
+
+ mutex_lock(&st->lock);
+ /* Ensure all interrupts have been acknowledged */
+ ret = sca3000_read_data_short(st, SCA3000_REG_ADDR_INT_STATUS, 1);
+ if (ret)
+ goto error_ret;
+
+ /* Turn off all motion detection channels */
+ ret = sca3000_read_ctrl_reg(st, SCA3000_REG_CTRL_SEL_MD_CTRL);
+ if (ret < 0)
+ goto error_ret;
+ ret = sca3000_write_ctrl_reg(st, SCA3000_REG_CTRL_SEL_MD_CTRL,
+ ret & SCA3000_MD_CTRL_PROT_MASK);
+ if (ret)
+ goto error_ret;
+
+ /* Disable ring buffer */
+ ret = sca3000_read_ctrl_reg(st, SCA3000_REG_CTRL_SEL_OUT_CTRL);
+ ret = sca3000_write_ctrl_reg(st, SCA3000_REG_CTRL_SEL_OUT_CTRL,
+ (ret & SCA3000_OUT_CTRL_PROT_MASK)
+ | SCA3000_OUT_CTRL_BUF_X_EN
+ | SCA3000_OUT_CTRL_BUF_Y_EN
+ | SCA3000_OUT_CTRL_BUF_Z_EN
+ | SCA3000_OUT_CTRL_BUF_DIV_4);
+ if (ret)
+ goto error_ret;
+ /* Enable interrupts, relevant to mode and set up as active low */
+ ret = sca3000_read_data_short(st, SCA3000_REG_ADDR_INT_MASK, 1);
+ if (ret)
+ goto error_ret;
+ ret = sca3000_write_reg(st,
+ SCA3000_REG_ADDR_INT_MASK,
+ (ret & SCA3000_INT_MASK_PROT_MASK)
+ | SCA3000_INT_MASK_ACTIVE_LOW);
+ if (ret)
+ goto error_ret;
+ /*
+ * Select normal measurement mode, free fall off, ring off
+ * Ring in 12 bit mode - it is fine to overwrite reserved bits 3,5
+ * as that occurs in one of the example on the datasheet
+ */
+ ret = sca3000_read_data_short(st, SCA3000_REG_ADDR_MODE, 1);
+ if (ret)
+ goto error_ret;
+ ret = sca3000_write_reg(st, SCA3000_REG_ADDR_MODE,
+ (st->rx[0] & SCA3000_MODE_PROT_MASK));
+ st->bpse = 11;
+
+error_ret:
+ mutex_unlock(&st->lock);
+ return ret;
+}
+
+static const struct iio_info sca3000_info = {
+ .attrs = &sca3000_attribute_group,
+ .read_raw = &sca3000_read_raw,
+ .event_attrs = &sca3000_event_attribute_group,
+ .read_event_value = &sca3000_read_thresh,
+ .write_event_value = &sca3000_write_thresh,
+ .read_event_config = &sca3000_read_event_config,
+ .write_event_config = &sca3000_write_event_config,
+ .driver_module = THIS_MODULE,
+};
+
+static int sca3000_probe(struct spi_device *spi)
+{
+ int ret;
+ struct sca3000_state *st;
+ struct iio_dev *indio_dev;
+
+ indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ st = iio_priv(indio_dev);
+ spi_set_drvdata(spi, indio_dev);
+ st->us = spi;
+ mutex_init(&st->lock);
+ st->info = &sca3000_spi_chip_info_tbl[spi_get_device_id(spi)
+ ->driver_data];
+
+ indio_dev->dev.parent = &spi->dev;
+ indio_dev->name = spi_get_device_id(spi)->name;
+ indio_dev->info = &sca3000_info;
+ if (st->info->temp_output) {
+ indio_dev->channels = sca3000_channels_with_temp;
+ indio_dev->num_channels =
+ ARRAY_SIZE(sca3000_channels_with_temp);
+ } else {
+ indio_dev->channels = sca3000_channels;
+ indio_dev->num_channels = ARRAY_SIZE(sca3000_channels);
+ }
+ indio_dev->modes = INDIO_DIRECT_MODE;
+
+ sca3000_configure_ring(indio_dev);
+ ret = iio_device_register(indio_dev);
+ if (ret < 0)
+ return ret;
+
+ if (spi->irq) {
+ ret = request_threaded_irq(spi->irq,
+ NULL,
+ &sca3000_event_handler,
+ IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
+ "sca3000",
+ indio_dev);
+ if (ret)
+ goto error_unregister_dev;
+ }
+ sca3000_register_ring_funcs(indio_dev);
+ ret = sca3000_clean_setup(st);
+ if (ret)
+ goto error_free_irq;
+ return 0;
+
+error_free_irq:
+ if (spi->irq)
+ free_irq(spi->irq, indio_dev);
+error_unregister_dev:
+ iio_device_unregister(indio_dev);
+ return ret;
+}
+
+static int sca3000_stop_all_interrupts(struct sca3000_state *st)
+{
+ int ret;
+
+ mutex_lock(&st->lock);
+ ret = sca3000_read_data_short(st, SCA3000_REG_ADDR_INT_MASK, 1);
+ if (ret)
+ goto error_ret;
+ ret = sca3000_write_reg(st, SCA3000_REG_ADDR_INT_MASK,
+ (st->rx[0] &
+ ~(SCA3000_INT_MASK_RING_THREE_QUARTER |
+ SCA3000_INT_MASK_RING_HALF |
+ SCA3000_INT_MASK_ALL_INTS)));
+error_ret:
+ mutex_unlock(&st->lock);
+ return ret;
+}
+
+static int sca3000_remove(struct spi_device *spi)
+{
+ struct iio_dev *indio_dev = spi_get_drvdata(spi);
+ struct sca3000_state *st = iio_priv(indio_dev);
+
+ /* Must ensure no interrupts can be generated after this! */
+ sca3000_stop_all_interrupts(st);
+ if (spi->irq)
+ free_irq(spi->irq, indio_dev);
+ iio_device_unregister(indio_dev);
+ sca3000_unconfigure_ring(indio_dev);
+
+ return 0;
+}
+
+static const struct spi_device_id sca3000_id[] = {
+ {"sca3000_d01", d01},
+ {"sca3000_e02", e02},
+ {"sca3000_e04", e04},
+ {"sca3000_e05", e05},
+ {}
+};
+MODULE_DEVICE_TABLE(spi, sca3000_id);
+
+static struct spi_driver sca3000_driver = {
+ .driver = {
+ .name = "sca3000",
+ .owner = THIS_MODULE,
+ },
+ .probe = sca3000_probe,
+ .remove = sca3000_remove,
+ .id_table = sca3000_id,
+};
+module_spi_driver(sca3000_driver);
+
+MODULE_AUTHOR("Jonathan Cameron <jic23@kernel.org>");
+MODULE_DESCRIPTION("VTI SCA3000 Series Accelerometers SPI driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/staging/iio/accel/sca3000_ring.c b/drivers/staging/iio/accel/sca3000_ring.c
new file mode 100644
index 000000000..8589eade1
--- /dev/null
+++ b/drivers/staging/iio/accel/sca3000_ring.c
@@ -0,0 +1,348 @@
+/*
+ * sca3000_ring.c -- support VTI sca3000 series accelerometers via SPI
+ *
+ * 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.
+ *
+ * Copyright (c) 2009 Jonathan Cameron <jic23@kernel.org>
+ *
+ */
+
+#include <linux/interrupt.h>
+#include <linux/fs.h>
+#include <linux/slab.h>
+#include <linux/kernel.h>
+#include <linux/spi/spi.h>
+#include <linux/sysfs.h>
+#include <linux/sched.h>
+#include <linux/poll.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/iio/buffer.h>
+#include "../ring_hw.h"
+#include "sca3000.h"
+
+/* RFC / future work
+ *
+ * The internal ring buffer doesn't actually change what it holds depending
+ * on which signals are enabled etc, merely whether you can read them.
+ * As such the scan mode selection is somewhat different than for a software
+ * ring buffer and changing it actually covers any data already in the buffer.
+ * Currently scan elements aren't configured so it doesn't matter.
+ */
+
+static int sca3000_read_data(struct sca3000_state *st,
+ uint8_t reg_address_high,
+ u8 **rx_p,
+ int len)
+{
+ int ret;
+ struct spi_transfer xfer[2] = {
+ {
+ .len = 1,
+ .tx_buf = st->tx,
+ }, {
+ .len = len,
+ }
+ };
+ *rx_p = kmalloc(len, GFP_KERNEL);
+ if (*rx_p == NULL) {
+ ret = -ENOMEM;
+ goto error_ret;
+ }
+ xfer[1].rx_buf = *rx_p;
+ st->tx[0] = SCA3000_READ_REG(reg_address_high);
+ ret = spi_sync_transfer(st->us, xfer, ARRAY_SIZE(xfer));
+ if (ret) {
+ dev_err(get_device(&st->us->dev), "problem reading register");
+ goto error_free_rx;
+ }
+
+ return 0;
+error_free_rx:
+ kfree(*rx_p);
+error_ret:
+ return ret;
+}
+
+/**
+ * sca3000_read_first_n_hw_rb() - main ring access, pulls data from ring
+ * @r: the ring
+ * @count: number of samples to try and pull
+ * @data: output the actual samples pulled from the hw ring
+ *
+ * Currently does not provide timestamps. As the hardware doesn't add them they
+ * can only be inferred approximately from ring buffer events such as 50% full
+ * and knowledge of when buffer was last emptied. This is left to userspace.
+ **/
+static int sca3000_read_first_n_hw_rb(struct iio_buffer *r,
+ size_t count, char __user *buf)
+{
+ struct iio_hw_buffer *hw_ring = iio_to_hw_buf(r);
+ struct iio_dev *indio_dev = hw_ring->private;
+ struct sca3000_state *st = iio_priv(indio_dev);
+ u8 *rx;
+ int ret, i, num_available, num_read = 0;
+ int bytes_per_sample = 1;
+
+ if (st->bpse == 11)
+ bytes_per_sample = 2;
+
+ mutex_lock(&st->lock);
+ if (count % bytes_per_sample) {
+ ret = -EINVAL;
+ goto error_ret;
+ }
+
+ ret = sca3000_read_data_short(st, SCA3000_REG_ADDR_BUF_COUNT, 1);
+ if (ret)
+ goto error_ret;
+ else
+ num_available = st->rx[0];
+ /*
+ * num_available is the total number of samples available
+ * i.e. number of time points * number of channels.
+ */
+ if (count > num_available * bytes_per_sample)
+ num_read = num_available*bytes_per_sample;
+ else
+ num_read = count;
+
+ ret = sca3000_read_data(st,
+ SCA3000_REG_ADDR_RING_OUT,
+ &rx, num_read);
+ if (ret)
+ goto error_ret;
+
+ for (i = 0; i < num_read; i++)
+ *(((u16 *)rx) + i) = be16_to_cpup((__be16 *)rx + i);
+
+ if (copy_to_user(buf, rx, num_read))
+ ret = -EFAULT;
+ kfree(rx);
+ r->stufftoread = 0;
+error_ret:
+ mutex_unlock(&st->lock);
+
+ return ret ? ret : num_read;
+}
+
+static size_t sca3000_ring_buf_data_available(struct iio_buffer *r)
+{
+ return r->stufftoread ? r->watermark : 0;
+}
+
+/**
+ * sca3000_query_ring_int() is the hardware ring status interrupt enabled
+ **/
+static ssize_t sca3000_query_ring_int(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
+ int ret, val;
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct sca3000_state *st = iio_priv(indio_dev);
+
+ mutex_lock(&st->lock);
+ ret = sca3000_read_data_short(st, SCA3000_REG_ADDR_INT_MASK, 1);
+ val = st->rx[0];
+ mutex_unlock(&st->lock);
+ if (ret)
+ return ret;
+
+ return sprintf(buf, "%d\n", !!(val & this_attr->address));
+}
+
+/**
+ * sca3000_set_ring_int() set state of ring status interrupt
+ **/
+static ssize_t sca3000_set_ring_int(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t len)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct sca3000_state *st = iio_priv(indio_dev);
+ struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
+ u8 val;
+ int ret;
+
+ mutex_lock(&st->lock);
+ ret = kstrtou8(buf, 10, &val);
+ if (ret)
+ goto error_ret;
+ ret = sca3000_read_data_short(st, SCA3000_REG_ADDR_INT_MASK, 1);
+ if (ret)
+ goto error_ret;
+ if (val)
+ ret = sca3000_write_reg(st,
+ SCA3000_REG_ADDR_INT_MASK,
+ st->rx[0] | this_attr->address);
+ else
+ ret = sca3000_write_reg(st,
+ SCA3000_REG_ADDR_INT_MASK,
+ st->rx[0] & ~this_attr->address);
+error_ret:
+ mutex_unlock(&st->lock);
+
+ return ret ? ret : len;
+}
+
+static IIO_DEVICE_ATTR(50_percent, S_IRUGO | S_IWUSR,
+ sca3000_query_ring_int,
+ sca3000_set_ring_int,
+ SCA3000_INT_MASK_RING_HALF);
+
+static IIO_DEVICE_ATTR(75_percent, S_IRUGO | S_IWUSR,
+ sca3000_query_ring_int,
+ sca3000_set_ring_int,
+ SCA3000_INT_MASK_RING_THREE_QUARTER);
+
+static ssize_t sca3000_show_buffer_scale(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct sca3000_state *st = iio_priv(indio_dev);
+
+ return sprintf(buf, "0.%06d\n", 4*st->info->scale);
+}
+
+static IIO_DEVICE_ATTR(in_accel_scale,
+ S_IRUGO,
+ sca3000_show_buffer_scale,
+ NULL,
+ 0);
+
+/*
+ * Ring buffer attributes
+ * This device is a bit unusual in that the sampling frequency and bpse
+ * only apply to the ring buffer. At all times full rate and accuracy
+ * is available via direct reading from registers.
+ */
+static const struct attribute *sca3000_ring_attributes[] = {
+ &iio_dev_attr_50_percent.dev_attr.attr,
+ &iio_dev_attr_75_percent.dev_attr.attr,
+ &iio_dev_attr_in_accel_scale.dev_attr.attr,
+ NULL,
+};
+
+static struct iio_buffer *sca3000_rb_allocate(struct iio_dev *indio_dev)
+{
+ struct iio_buffer *buf;
+ struct iio_hw_buffer *ring;
+
+ ring = kzalloc(sizeof(*ring), GFP_KERNEL);
+ if (!ring)
+ return NULL;
+
+ ring->private = indio_dev;
+ buf = &ring->buf;
+ buf->stufftoread = 0;
+ buf->length = 64;
+ buf->attrs = sca3000_ring_attributes;
+ iio_buffer_init(buf);
+
+ return buf;
+}
+
+static void sca3000_ring_release(struct iio_buffer *r)
+{
+ kfree(iio_to_hw_buf(r));
+}
+
+static const struct iio_buffer_access_funcs sca3000_ring_access_funcs = {
+ .read_first_n = &sca3000_read_first_n_hw_rb,
+ .data_available = sca3000_ring_buf_data_available,
+ .release = sca3000_ring_release,
+};
+
+int sca3000_configure_ring(struct iio_dev *indio_dev)
+{
+ struct iio_buffer *buffer;
+
+ buffer = sca3000_rb_allocate(indio_dev);
+ if (buffer == NULL)
+ return -ENOMEM;
+ indio_dev->modes |= INDIO_BUFFER_HARDWARE;
+
+ indio_dev->buffer->access = &sca3000_ring_access_funcs;
+
+ iio_device_attach_buffer(indio_dev, buffer);
+
+ return 0;
+}
+
+void sca3000_unconfigure_ring(struct iio_dev *indio_dev)
+{
+ iio_buffer_put(indio_dev->buffer);
+}
+
+static inline
+int __sca3000_hw_ring_state_set(struct iio_dev *indio_dev, bool state)
+{
+ struct sca3000_state *st = iio_priv(indio_dev);
+ int ret;
+
+ mutex_lock(&st->lock);
+ ret = sca3000_read_data_short(st, SCA3000_REG_ADDR_MODE, 1);
+ if (ret)
+ goto error_ret;
+ if (state) {
+ dev_info(&indio_dev->dev, "supposedly enabling ring buffer\n");
+ ret = sca3000_write_reg(st,
+ SCA3000_REG_ADDR_MODE,
+ (st->rx[0] | SCA3000_RING_BUF_ENABLE));
+ } else
+ ret = sca3000_write_reg(st,
+ SCA3000_REG_ADDR_MODE,
+ (st->rx[0] & ~SCA3000_RING_BUF_ENABLE));
+error_ret:
+ mutex_unlock(&st->lock);
+
+ return ret;
+}
+/**
+ * sca3000_hw_ring_preenable() hw ring buffer preenable function
+ *
+ * Very simple enable function as the chip will allows normal reads
+ * during ring buffer operation so as long as it is indeed running
+ * before we notify the core, the precise ordering does not matter.
+ **/
+static int sca3000_hw_ring_preenable(struct iio_dev *indio_dev)
+{
+ return __sca3000_hw_ring_state_set(indio_dev, 1);
+}
+
+static int sca3000_hw_ring_postdisable(struct iio_dev *indio_dev)
+{
+ return __sca3000_hw_ring_state_set(indio_dev, 0);
+}
+
+static const struct iio_buffer_setup_ops sca3000_ring_setup_ops = {
+ .preenable = &sca3000_hw_ring_preenable,
+ .postdisable = &sca3000_hw_ring_postdisable,
+};
+
+void sca3000_register_ring_funcs(struct iio_dev *indio_dev)
+{
+ indio_dev->setup_ops = &sca3000_ring_setup_ops;
+}
+
+/**
+ * sca3000_ring_int_process() ring specific interrupt handling.
+ *
+ * This is only split from the main interrupt handler so as to
+ * reduce the amount of code if the ring buffer is not enabled.
+ **/
+void sca3000_ring_int_process(u8 val, struct iio_buffer *ring)
+{
+ if (val & (SCA3000_INT_STATUS_THREE_QUARTERS |
+ SCA3000_INT_STATUS_HALF)) {
+ ring->stufftoread = true;
+ wake_up_interruptible(&ring->pollq);
+ }
+}
diff --git a/drivers/staging/iio/adc/Kconfig b/drivers/staging/iio/adc/Kconfig
new file mode 100644
index 000000000..d0016ce6e
--- /dev/null
+++ b/drivers/staging/iio/adc/Kconfig
@@ -0,0 +1,118 @@
+#
+# ADC drivers
+#
+menu "Analog to digital converters"
+
+config AD7606
+ tristate "Analog Devices AD7606 ADC driver"
+ depends on GPIOLIB
+ select IIO_BUFFER
+ select IIO_TRIGGERED_BUFFER
+ help
+ Say yes here to build support for Analog Devices:
+ ad7606, ad7606-6, ad7606-4 analog to digital converters (ADC).
+
+ To compile this driver as a module, choose M here: the
+ module will be called ad7606.
+
+config AD7606_IFACE_PARALLEL
+ tristate "parallel interface support"
+ depends on AD7606
+ help
+ Say yes here to include parallel interface support on the AD7606
+ ADC driver.
+
+ To compile this driver as a module, choose M here: the
+ module will be called ad7606_iface_parallel.
+
+config AD7606_IFACE_SPI
+ tristate "spi interface support"
+ depends on AD7606
+ depends on SPI
+ help
+ Say yes here to include parallel interface support on the AD7606
+ ADC driver.
+
+ To compile this driver as a module, choose M here: the
+ module will be called ad7606_iface_spi.
+
+config AD7780
+ tristate "Analog Devices AD7780 and similar ADCs driver"
+ depends on SPI
+ depends on GPIOLIB
+ select AD_SIGMA_DELTA
+ help
+ Say yes here to build support for Analog Devices AD7170, AD7171,
+ AD7780 and AD7781 SPI analog to digital converters (ADC).
+ If unsure, say N (but it's safe to say "Y").
+
+ To compile this driver as a module, choose M here: the
+ module will be called ad7780.
+
+config AD7816
+ tristate "Analog Devices AD7816/7/8 temperature sensor and ADC driver"
+ depends on SPI
+ depends on GPIOLIB
+ help
+ Say yes here to build support for Analog Devices AD7816/7/8
+ temperature sensors and ADC.
+
+config AD7192
+ tristate "Analog Devices AD7190 AD7192 AD7195 ADC driver"
+ depends on SPI
+ select AD_SIGMA_DELTA
+ help
+ Say yes here to build support for Analog Devices AD7190,
+ AD7192 or AD7195 SPI analog to digital converters (ADC).
+ If unsure, say N (but it's safe to say "Y").
+
+ To compile this driver as a module, choose M here: the
+ module will be called ad7192.
+
+config AD7280
+ tristate "Analog Devices AD7280A Lithium Ion Battery Monitoring System"
+ depends on SPI
+ help
+ Say yes here to build support for Analog Devices AD7280A
+ Lithium Ion Battery Monitoring System.
+
+ To compile this driver as a module, choose M here: the
+ module will be called ad7280a
+
+config LPC32XX_ADC
+ tristate "NXP LPC32XX ADC"
+ depends on ARCH_LPC32XX || COMPILE_TEST
+ depends on HAS_IOMEM
+ help
+ Say yes here to build support for the integrated ADC inside the
+ LPC32XX SoC. Note that this feature uses the same hardware as the
+ touchscreen driver, so you should either select only one of the two
+ drivers (lpc32xx_adc or lpc32xx_ts) or, in the OpenFirmware case,
+ activate only one via device tree selection. Provides direct access
+ via sysfs.
+
+config MXS_LRADC
+ tristate "Freescale i.MX23/i.MX28 LRADC"
+ depends on (ARCH_MXS || COMPILE_TEST) && HAS_IOMEM
+ depends on INPUT
+ select STMP_DEVICE
+ select IIO_BUFFER
+ select IIO_TRIGGERED_BUFFER
+ help
+ Say yes here to build support for i.MX23/i.MX28 LRADC convertor
+ built into these chips.
+
+ To compile this driver as a module, choose M here: the
+ module will be called mxs-lradc.
+
+config SPEAR_ADC
+ tristate "ST SPEAr ADC"
+ depends on PLAT_SPEAR || COMPILE_TEST
+ depends on HAS_IOMEM
+ help
+ Say yes here to build support for the integrated ADC inside the
+ ST SPEAr SoC. Provides direct access via sysfs.
+
+ To compile this driver as a module, choose M here: the
+ module will be called spear_adc.
+endmenu
diff --git a/drivers/staging/iio/adc/Makefile b/drivers/staging/iio/adc/Makefile
new file mode 100644
index 000000000..1c4277dbd
--- /dev/null
+++ b/drivers/staging/iio/adc/Makefile
@@ -0,0 +1,17 @@
+#
+# Makefile for industrial I/O ADC drivers
+#
+
+ad7606-y := ad7606_core.o
+ad7606-$(CONFIG_IIO_BUFFER) += ad7606_ring.o
+ad7606-$(CONFIG_AD7606_IFACE_PARALLEL) += ad7606_par.o
+ad7606-$(CONFIG_AD7606_IFACE_SPI) += ad7606_spi.o
+obj-$(CONFIG_AD7606) += ad7606.o
+
+obj-$(CONFIG_AD7780) += ad7780.o
+obj-$(CONFIG_AD7816) += ad7816.o
+obj-$(CONFIG_AD7192) += ad7192.o
+obj-$(CONFIG_AD7280) += ad7280a.o
+obj-$(CONFIG_LPC32XX_ADC) += lpc32xx_adc.o
+obj-$(CONFIG_MXS_LRADC) += mxs-lradc.o
+obj-$(CONFIG_SPEAR_ADC) += spear_adc.o
diff --git a/drivers/staging/iio/adc/ad7192.c b/drivers/staging/iio/adc/ad7192.c
new file mode 100644
index 000000000..fe56fb6c7
--- /dev/null
+++ b/drivers/staging/iio/adc/ad7192.c
@@ -0,0 +1,720 @@
+/*
+ * AD7190 AD7192 AD7195 SPI ADC driver
+ *
+ * Copyright 2011-2012 Analog Devices Inc.
+ *
+ * Licensed under the GPL-2.
+ */
+
+#include <linux/interrupt.h>
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/sysfs.h>
+#include <linux/spi/spi.h>
+#include <linux/regulator/consumer.h>
+#include <linux/err.h>
+#include <linux/sched.h>
+#include <linux/delay.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/trigger.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/triggered_buffer.h>
+#include <linux/iio/adc/ad_sigma_delta.h>
+
+#include "ad7192.h"
+
+/* Registers */
+#define AD7192_REG_COMM 0 /* Communications Register (WO, 8-bit) */
+#define AD7192_REG_STAT 0 /* Status Register (RO, 8-bit) */
+#define AD7192_REG_MODE 1 /* Mode Register (RW, 24-bit */
+#define AD7192_REG_CONF 2 /* Configuration Register (RW, 24-bit) */
+#define AD7192_REG_DATA 3 /* Data Register (RO, 24/32-bit) */
+#define AD7192_REG_ID 4 /* ID Register (RO, 8-bit) */
+#define AD7192_REG_GPOCON 5 /* GPOCON Register (RO, 8-bit) */
+#define AD7192_REG_OFFSET 6 /* Offset Register (RW, 16-bit
+ * (AD7792)/24-bit (AD7192)) */
+#define AD7192_REG_FULLSALE 7 /* Full-Scale Register
+ * (RW, 16-bit (AD7792)/24-bit (AD7192)) */
+
+/* Communications Register Bit Designations (AD7192_REG_COMM) */
+#define AD7192_COMM_WEN BIT(7) /* Write Enable */
+#define AD7192_COMM_WRITE 0 /* Write Operation */
+#define AD7192_COMM_READ BIT(6) /* Read Operation */
+#define AD7192_COMM_ADDR(x) (((x) & 0x7) << 3) /* Register Address */
+#define AD7192_COMM_CREAD BIT(2) /* Continuous Read of Data Register */
+
+/* Status Register Bit Designations (AD7192_REG_STAT) */
+#define AD7192_STAT_RDY BIT(7) /* Ready */
+#define AD7192_STAT_ERR BIT(6) /* Error (Overrange, Underrange) */
+#define AD7192_STAT_NOREF BIT(5) /* Error no external reference */
+#define AD7192_STAT_PARITY BIT(4) /* Parity */
+#define AD7192_STAT_CH3 BIT(2) /* Channel 3 */
+#define AD7192_STAT_CH2 BIT(1) /* Channel 2 */
+#define AD7192_STAT_CH1 BIT(0) /* Channel 1 */
+
+/* Mode Register Bit Designations (AD7192_REG_MODE) */
+#define AD7192_MODE_SEL(x) (((x) & 0x7) << 21) /* Operation Mode Select */
+#define AD7192_MODE_SEL_MASK (0x7 << 21) /* Operation Mode Select Mask */
+#define AD7192_MODE_DAT_STA BIT(20) /* Status Register transmission */
+#define AD7192_MODE_CLKSRC(x) (((x) & 0x3) << 18) /* Clock Source Select */
+#define AD7192_MODE_SINC3 BIT(15) /* SINC3 Filter Select */
+#define AD7192_MODE_ACX BIT(14) /* AC excitation enable(AD7195 only)*/
+#define AD7192_MODE_ENPAR BIT(13) /* Parity Enable */
+#define AD7192_MODE_CLKDIV BIT(12) /* Clock divide by 2 (AD7190/2 only)*/
+#define AD7192_MODE_SCYCLE BIT(11) /* Single cycle conversion */
+#define AD7192_MODE_REJ60 BIT(10) /* 50/60Hz notch filter */
+#define AD7192_MODE_RATE(x) ((x) & 0x3FF) /* Filter Update Rate Select */
+
+/* Mode Register: AD7192_MODE_SEL options */
+#define AD7192_MODE_CONT 0 /* Continuous Conversion Mode */
+#define AD7192_MODE_SINGLE 1 /* Single Conversion Mode */
+#define AD7192_MODE_IDLE 2 /* Idle Mode */
+#define AD7192_MODE_PWRDN 3 /* Power-Down Mode */
+#define AD7192_MODE_CAL_INT_ZERO 4 /* Internal Zero-Scale Calibration */
+#define AD7192_MODE_CAL_INT_FULL 5 /* Internal Full-Scale Calibration */
+#define AD7192_MODE_CAL_SYS_ZERO 6 /* System Zero-Scale Calibration */
+#define AD7192_MODE_CAL_SYS_FULL 7 /* System Full-Scale Calibration */
+
+/* Mode Register: AD7192_MODE_CLKSRC options */
+#define AD7192_CLK_EXT_MCLK1_2 0 /* External 4.92 MHz Clock connected
+ * from MCLK1 to MCLK2 */
+#define AD7192_CLK_EXT_MCLK2 1 /* External Clock applied to MCLK2 */
+#define AD7192_CLK_INT 2 /* Internal 4.92 MHz Clock not
+ * available at the MCLK2 pin */
+#define AD7192_CLK_INT_CO 3 /* Internal 4.92 MHz Clock available
+ * at the MCLK2 pin */
+
+
+/* Configuration Register Bit Designations (AD7192_REG_CONF) */
+
+#define AD7192_CONF_CHOP BIT(23) /* CHOP enable */
+#define AD7192_CONF_REFSEL BIT(20) /* REFIN1/REFIN2 Reference Select */
+#define AD7192_CONF_CHAN(x) (((1 << (x)) & 0xFF) << 8) /* Channel select */
+#define AD7192_CONF_CHAN_MASK (0xFF << 8) /* Channel select mask */
+#define AD7192_CONF_BURN BIT(7) /* Burnout current enable */
+#define AD7192_CONF_REFDET BIT(6) /* Reference detect enable */
+#define AD7192_CONF_BUF BIT(4) /* Buffered Mode Enable */
+#define AD7192_CONF_UNIPOLAR BIT(3) /* Unipolar/Bipolar Enable */
+#define AD7192_CONF_GAIN(x) ((x) & 0x7) /* Gain Select */
+
+#define AD7192_CH_AIN1P_AIN2M 0 /* AIN1(+) - AIN2(-) */
+#define AD7192_CH_AIN3P_AIN4M 1 /* AIN3(+) - AIN4(-) */
+#define AD7192_CH_TEMP 2 /* Temp Sensor */
+#define AD7192_CH_AIN2P_AIN2M 3 /* AIN2(+) - AIN2(-) */
+#define AD7192_CH_AIN1 4 /* AIN1 - AINCOM */
+#define AD7192_CH_AIN2 5 /* AIN2 - AINCOM */
+#define AD7192_CH_AIN3 6 /* AIN3 - AINCOM */
+#define AD7192_CH_AIN4 7 /* AIN4 - AINCOM */
+
+/* ID Register Bit Designations (AD7192_REG_ID) */
+#define ID_AD7190 0x4
+#define ID_AD7192 0x0
+#define ID_AD7195 0x6
+#define AD7192_ID_MASK 0x0F
+
+/* GPOCON Register Bit Designations (AD7192_REG_GPOCON) */
+#define AD7192_GPOCON_BPDSW BIT(6) /* Bridge power-down switch enable */
+#define AD7192_GPOCON_GP32EN BIT(5) /* Digital Output P3 and P2 enable */
+#define AD7192_GPOCON_GP10EN BIT(4) /* Digital Output P1 and P0 enable */
+#define AD7192_GPOCON_P3DAT BIT(3) /* P3 state */
+#define AD7192_GPOCON_P2DAT BIT(2) /* P2 state */
+#define AD7192_GPOCON_P1DAT BIT(1) /* P1 state */
+#define AD7192_GPOCON_P0DAT BIT(0) /* P0 state */
+
+#define AD7192_INT_FREQ_MHz 4915200
+
+/* NOTE:
+ * The AD7190/2/5 features a dual use data out ready DOUT/RDY output.
+ * In order to avoid contentions on the SPI bus, it's therefore necessary
+ * to use spi bus locking.
+ *
+ * The DOUT/RDY output must also be wired to an interrupt capable GPIO.
+ */
+
+struct ad7192_state {
+ struct regulator *reg;
+ u16 int_vref_mv;
+ u32 mclk;
+ u32 f_order;
+ u32 mode;
+ u32 conf;
+ u32 scale_avail[8][2];
+ u8 gpocon;
+ u8 devid;
+
+ struct ad_sigma_delta sd;
+};
+
+static struct ad7192_state *ad_sigma_delta_to_ad7192(struct ad_sigma_delta *sd)
+{
+ return container_of(sd, struct ad7192_state, sd);
+}
+
+static int ad7192_set_channel(struct ad_sigma_delta *sd, unsigned int channel)
+{
+ struct ad7192_state *st = ad_sigma_delta_to_ad7192(sd);
+
+ st->conf &= ~AD7192_CONF_CHAN_MASK;
+ st->conf |= AD7192_CONF_CHAN(channel);
+
+ return ad_sd_write_reg(&st->sd, AD7192_REG_CONF, 3, st->conf);
+}
+
+static int ad7192_set_mode(struct ad_sigma_delta *sd,
+ enum ad_sigma_delta_mode mode)
+{
+ struct ad7192_state *st = ad_sigma_delta_to_ad7192(sd);
+
+ st->mode &= ~AD7192_MODE_SEL_MASK;
+ st->mode |= AD7192_MODE_SEL(mode);
+
+ return ad_sd_write_reg(&st->sd, AD7192_REG_MODE, 3, st->mode);
+}
+
+static const struct ad_sigma_delta_info ad7192_sigma_delta_info = {
+ .set_channel = ad7192_set_channel,
+ .set_mode = ad7192_set_mode,
+ .has_registers = true,
+ .addr_shift = 3,
+ .read_mask = BIT(6),
+};
+
+static const struct ad_sd_calib_data ad7192_calib_arr[8] = {
+ {AD7192_MODE_CAL_INT_ZERO, AD7192_CH_AIN1},
+ {AD7192_MODE_CAL_INT_FULL, AD7192_CH_AIN1},
+ {AD7192_MODE_CAL_INT_ZERO, AD7192_CH_AIN2},
+ {AD7192_MODE_CAL_INT_FULL, AD7192_CH_AIN2},
+ {AD7192_MODE_CAL_INT_ZERO, AD7192_CH_AIN3},
+ {AD7192_MODE_CAL_INT_FULL, AD7192_CH_AIN3},
+ {AD7192_MODE_CAL_INT_ZERO, AD7192_CH_AIN4},
+ {AD7192_MODE_CAL_INT_FULL, AD7192_CH_AIN4}
+};
+
+static int ad7192_calibrate_all(struct ad7192_state *st)
+{
+ return ad_sd_calibrate_all(&st->sd, ad7192_calib_arr,
+ ARRAY_SIZE(ad7192_calib_arr));
+}
+
+static int ad7192_setup(struct ad7192_state *st,
+ const struct ad7192_platform_data *pdata)
+{
+ struct iio_dev *indio_dev = spi_get_drvdata(st->sd.spi);
+ unsigned long long scale_uv;
+ int i, ret, id;
+ u8 ones[6];
+
+ /* reset the serial interface */
+ memset(&ones, 0xFF, 6);
+ ret = spi_write(st->sd.spi, &ones, 6);
+ if (ret < 0)
+ goto out;
+ usleep_range(500, 1000); /* Wait for at least 500us */
+
+ /* write/read test for device presence */
+ ret = ad_sd_read_reg(&st->sd, AD7192_REG_ID, 1, &id);
+ if (ret)
+ goto out;
+
+ id &= AD7192_ID_MASK;
+
+ if (id != st->devid)
+ dev_warn(&st->sd.spi->dev, "device ID query failed (0x%X)\n",
+ id);
+
+ switch (pdata->clock_source_sel) {
+ case AD7192_CLK_EXT_MCLK1_2:
+ case AD7192_CLK_EXT_MCLK2:
+ st->mclk = AD7192_INT_FREQ_MHz;
+ break;
+ case AD7192_CLK_INT:
+ case AD7192_CLK_INT_CO:
+ if (pdata->ext_clk_Hz)
+ st->mclk = pdata->ext_clk_Hz;
+ else
+ st->mclk = AD7192_INT_FREQ_MHz;
+ break;
+ default:
+ ret = -EINVAL;
+ goto out;
+ }
+
+ st->mode = AD7192_MODE_SEL(AD7192_MODE_IDLE) |
+ AD7192_MODE_CLKSRC(pdata->clock_source_sel) |
+ AD7192_MODE_RATE(480);
+
+ st->conf = AD7192_CONF_GAIN(0);
+
+ if (pdata->rej60_en)
+ st->mode |= AD7192_MODE_REJ60;
+
+ if (pdata->sinc3_en)
+ st->mode |= AD7192_MODE_SINC3;
+
+ if (pdata->refin2_en && (st->devid != ID_AD7195))
+ st->conf |= AD7192_CONF_REFSEL;
+
+ if (pdata->chop_en) {
+ st->conf |= AD7192_CONF_CHOP;
+ if (pdata->sinc3_en)
+ st->f_order = 3; /* SINC 3rd order */
+ else
+ st->f_order = 4; /* SINC 4th order */
+ } else {
+ st->f_order = 1;
+ }
+
+ if (pdata->buf_en)
+ st->conf |= AD7192_CONF_BUF;
+
+ if (pdata->unipolar_en)
+ st->conf |= AD7192_CONF_UNIPOLAR;
+
+ if (pdata->burnout_curr_en)
+ st->conf |= AD7192_CONF_BURN;
+
+ ret = ad_sd_write_reg(&st->sd, AD7192_REG_MODE, 3, st->mode);
+ if (ret)
+ goto out;
+
+ ret = ad_sd_write_reg(&st->sd, AD7192_REG_CONF, 3, st->conf);
+ if (ret)
+ goto out;
+
+ ret = ad7192_calibrate_all(st);
+ if (ret)
+ goto out;
+
+ /* Populate available ADC input ranges */
+ for (i = 0; i < ARRAY_SIZE(st->scale_avail); i++) {
+ scale_uv = ((u64)st->int_vref_mv * 100000000)
+ >> (indio_dev->channels[0].scan_type.realbits -
+ ((st->conf & AD7192_CONF_UNIPOLAR) ? 0 : 1));
+ scale_uv >>= i;
+
+ st->scale_avail[i][1] = do_div(scale_uv, 100000000) * 10;
+ st->scale_avail[i][0] = scale_uv;
+ }
+
+ return 0;
+out:
+ dev_err(&st->sd.spi->dev, "setup failed\n");
+ return ret;
+}
+
+static ssize_t ad7192_read_frequency(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct ad7192_state *st = iio_priv(indio_dev);
+
+ return sprintf(buf, "%d\n", st->mclk /
+ (st->f_order * 1024 * AD7192_MODE_RATE(st->mode)));
+}
+
+static ssize_t ad7192_write_frequency(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t len)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct ad7192_state *st = iio_priv(indio_dev);
+ unsigned long lval;
+ int div, ret;
+
+ ret = kstrtoul(buf, 10, &lval);
+ if (ret)
+ return ret;
+ if (lval == 0)
+ return -EINVAL;
+
+ mutex_lock(&indio_dev->mlock);
+ if (iio_buffer_enabled(indio_dev)) {
+ mutex_unlock(&indio_dev->mlock);
+ return -EBUSY;
+ }
+
+ div = st->mclk / (lval * st->f_order * 1024);
+ if (div < 1 || div > 1023) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ st->mode &= ~AD7192_MODE_RATE(-1);
+ st->mode |= AD7192_MODE_RATE(div);
+ ad_sd_write_reg(&st->sd, AD7192_REG_MODE, 3, st->mode);
+
+out:
+ mutex_unlock(&indio_dev->mlock);
+
+ return ret ? ret : len;
+}
+
+static IIO_DEV_ATTR_SAMP_FREQ(S_IWUSR | S_IRUGO,
+ ad7192_read_frequency,
+ ad7192_write_frequency);
+
+static ssize_t ad7192_show_scale_available(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct ad7192_state *st = iio_priv(indio_dev);
+ int i, len = 0;
+
+ for (i = 0; i < ARRAY_SIZE(st->scale_avail); i++)
+ len += sprintf(buf + len, "%d.%09u ", st->scale_avail[i][0],
+ st->scale_avail[i][1]);
+
+ len += sprintf(buf + len, "\n");
+
+ return len;
+}
+
+static IIO_DEVICE_ATTR_NAMED(in_v_m_v_scale_available,
+ in_voltage-voltage_scale_available,
+ S_IRUGO, ad7192_show_scale_available, NULL, 0);
+
+static IIO_DEVICE_ATTR(in_voltage_scale_available, S_IRUGO,
+ ad7192_show_scale_available, NULL, 0);
+
+static ssize_t ad7192_show_ac_excitation(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct ad7192_state *st = iio_priv(indio_dev);
+
+ return sprintf(buf, "%d\n", !!(st->mode & AD7192_MODE_ACX));
+}
+
+static ssize_t ad7192_show_bridge_switch(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct ad7192_state *st = iio_priv(indio_dev);
+
+ return sprintf(buf, "%d\n", !!(st->gpocon & AD7192_GPOCON_BPDSW));
+}
+
+static ssize_t ad7192_set(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t len)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct ad7192_state *st = iio_priv(indio_dev);
+ struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
+ int ret;
+ bool val;
+
+ ret = strtobool(buf, &val);
+ if (ret < 0)
+ return ret;
+
+ mutex_lock(&indio_dev->mlock);
+ if (iio_buffer_enabled(indio_dev)) {
+ mutex_unlock(&indio_dev->mlock);
+ return -EBUSY;
+ }
+
+ switch ((u32) this_attr->address) {
+ case AD7192_REG_GPOCON:
+ if (val)
+ st->gpocon |= AD7192_GPOCON_BPDSW;
+ else
+ st->gpocon &= ~AD7192_GPOCON_BPDSW;
+
+ ad_sd_write_reg(&st->sd, AD7192_REG_GPOCON, 1, st->gpocon);
+ break;
+ case AD7192_REG_MODE:
+ if (val)
+ st->mode |= AD7192_MODE_ACX;
+ else
+ st->mode &= ~AD7192_MODE_ACX;
+
+ ad_sd_write_reg(&st->sd, AD7192_REG_MODE, 3, st->mode);
+ break;
+ default:
+ ret = -EINVAL;
+ }
+
+ mutex_unlock(&indio_dev->mlock);
+
+ return ret ? ret : len;
+}
+
+static IIO_DEVICE_ATTR(bridge_switch_en, S_IRUGO | S_IWUSR,
+ ad7192_show_bridge_switch, ad7192_set,
+ AD7192_REG_GPOCON);
+
+static IIO_DEVICE_ATTR(ac_excitation_en, S_IRUGO | S_IWUSR,
+ ad7192_show_ac_excitation, ad7192_set,
+ AD7192_REG_MODE);
+
+static struct attribute *ad7192_attributes[] = {
+ &iio_dev_attr_sampling_frequency.dev_attr.attr,
+ &iio_dev_attr_in_v_m_v_scale_available.dev_attr.attr,
+ &iio_dev_attr_in_voltage_scale_available.dev_attr.attr,
+ &iio_dev_attr_bridge_switch_en.dev_attr.attr,
+ &iio_dev_attr_ac_excitation_en.dev_attr.attr,
+ NULL
+};
+
+static const struct attribute_group ad7192_attribute_group = {
+ .attrs = ad7192_attributes,
+};
+
+static struct attribute *ad7195_attributes[] = {
+ &iio_dev_attr_sampling_frequency.dev_attr.attr,
+ &iio_dev_attr_in_v_m_v_scale_available.dev_attr.attr,
+ &iio_dev_attr_in_voltage_scale_available.dev_attr.attr,
+ &iio_dev_attr_bridge_switch_en.dev_attr.attr,
+ NULL
+};
+
+static const struct attribute_group ad7195_attribute_group = {
+ .attrs = ad7195_attributes,
+};
+
+static unsigned int ad7192_get_temp_scale(bool unipolar)
+{
+ return unipolar ? 2815 * 2 : 2815;
+}
+
+static int ad7192_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val,
+ int *val2,
+ long m)
+{
+ struct ad7192_state *st = iio_priv(indio_dev);
+ bool unipolar = !!(st->conf & AD7192_CONF_UNIPOLAR);
+
+ switch (m) {
+ case IIO_CHAN_INFO_RAW:
+ return ad_sigma_delta_single_conversion(indio_dev, chan, val);
+ case IIO_CHAN_INFO_SCALE:
+ switch (chan->type) {
+ case IIO_VOLTAGE:
+ mutex_lock(&indio_dev->mlock);
+ *val = st->scale_avail[AD7192_CONF_GAIN(st->conf)][0];
+ *val2 = st->scale_avail[AD7192_CONF_GAIN(st->conf)][1];
+ mutex_unlock(&indio_dev->mlock);
+ return IIO_VAL_INT_PLUS_NANO;
+ case IIO_TEMP:
+ *val = 0;
+ *val2 = 1000000000 / ad7192_get_temp_scale(unipolar);
+ return IIO_VAL_INT_PLUS_NANO;
+ default:
+ return -EINVAL;
+ }
+ case IIO_CHAN_INFO_OFFSET:
+ if (!unipolar)
+ *val = -(1 << (chan->scan_type.realbits - 1));
+ else
+ *val = 0;
+ /* Kelvin to Celsius */
+ if (chan->type == IIO_TEMP)
+ *val -= 273 * ad7192_get_temp_scale(unipolar);
+ return IIO_VAL_INT;
+ }
+
+ return -EINVAL;
+}
+
+static int ad7192_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int val,
+ int val2,
+ long mask)
+{
+ struct ad7192_state *st = iio_priv(indio_dev);
+ int ret, i;
+ unsigned int tmp;
+
+ mutex_lock(&indio_dev->mlock);
+ if (iio_buffer_enabled(indio_dev)) {
+ mutex_unlock(&indio_dev->mlock);
+ return -EBUSY;
+ }
+
+ switch (mask) {
+ case IIO_CHAN_INFO_SCALE:
+ ret = -EINVAL;
+ for (i = 0; i < ARRAY_SIZE(st->scale_avail); i++)
+ if (val2 == st->scale_avail[i][1]) {
+ ret = 0;
+ tmp = st->conf;
+ st->conf &= ~AD7192_CONF_GAIN(-1);
+ st->conf |= AD7192_CONF_GAIN(i);
+ if (tmp == st->conf)
+ break;
+ ad_sd_write_reg(&st->sd, AD7192_REG_CONF,
+ 3, st->conf);
+ ad7192_calibrate_all(st);
+ break;
+ }
+ break;
+ default:
+ ret = -EINVAL;
+ }
+
+ mutex_unlock(&indio_dev->mlock);
+
+ return ret;
+}
+
+static int ad7192_write_raw_get_fmt(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ long mask)
+{
+ return IIO_VAL_INT_PLUS_NANO;
+}
+
+static const struct iio_info ad7192_info = {
+ .read_raw = &ad7192_read_raw,
+ .write_raw = &ad7192_write_raw,
+ .write_raw_get_fmt = &ad7192_write_raw_get_fmt,
+ .attrs = &ad7192_attribute_group,
+ .validate_trigger = ad_sd_validate_trigger,
+ .driver_module = THIS_MODULE,
+};
+
+static const struct iio_info ad7195_info = {
+ .read_raw = &ad7192_read_raw,
+ .write_raw = &ad7192_write_raw,
+ .write_raw_get_fmt = &ad7192_write_raw_get_fmt,
+ .attrs = &ad7195_attribute_group,
+ .validate_trigger = ad_sd_validate_trigger,
+ .driver_module = THIS_MODULE,
+};
+
+static const struct iio_chan_spec ad7192_channels[] = {
+ AD_SD_DIFF_CHANNEL(0, 1, 2, AD7192_CH_AIN1P_AIN2M, 24, 32, 0),
+ AD_SD_DIFF_CHANNEL(1, 3, 4, AD7192_CH_AIN3P_AIN4M, 24, 32, 0),
+ AD_SD_TEMP_CHANNEL(2, AD7192_CH_TEMP, 24, 32, 0),
+ AD_SD_SHORTED_CHANNEL(3, 2, AD7192_CH_AIN2P_AIN2M, 24, 32, 0),
+ AD_SD_CHANNEL(4, 1, AD7192_CH_AIN1, 24, 32, 0),
+ AD_SD_CHANNEL(5, 2, AD7192_CH_AIN2, 24, 32, 0),
+ AD_SD_CHANNEL(6, 3, AD7192_CH_AIN3, 24, 32, 0),
+ AD_SD_CHANNEL(7, 4, AD7192_CH_AIN4, 24, 32, 0),
+ IIO_CHAN_SOFT_TIMESTAMP(8),
+};
+
+static int ad7192_probe(struct spi_device *spi)
+{
+ const struct ad7192_platform_data *pdata = spi->dev.platform_data;
+ struct ad7192_state *st;
+ struct iio_dev *indio_dev;
+ int ret, voltage_uv = 0;
+
+ if (!pdata) {
+ dev_err(&spi->dev, "no platform data?\n");
+ return -ENODEV;
+ }
+
+ if (!spi->irq) {
+ dev_err(&spi->dev, "no IRQ?\n");
+ return -ENODEV;
+ }
+
+ indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));
+ if (indio_dev == NULL)
+ return -ENOMEM;
+
+ st = iio_priv(indio_dev);
+
+ st->reg = devm_regulator_get(&spi->dev, "vcc");
+ if (!IS_ERR(st->reg)) {
+ ret = regulator_enable(st->reg);
+ if (ret)
+ return ret;
+
+ voltage_uv = regulator_get_voltage(st->reg);
+ }
+
+ if (pdata && pdata->vref_mv)
+ st->int_vref_mv = pdata->vref_mv;
+ else if (voltage_uv)
+ st->int_vref_mv = voltage_uv / 1000;
+ else
+ dev_warn(&spi->dev, "reference voltage undefined\n");
+
+ spi_set_drvdata(spi, indio_dev);
+ st->devid = spi_get_device_id(spi)->driver_data;
+ indio_dev->dev.parent = &spi->dev;
+ indio_dev->name = spi_get_device_id(spi)->name;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->channels = ad7192_channels;
+ indio_dev->num_channels = ARRAY_SIZE(ad7192_channels);
+ if (st->devid == ID_AD7195)
+ indio_dev->info = &ad7195_info;
+ else
+ indio_dev->info = &ad7192_info;
+
+ ad_sd_init(&st->sd, indio_dev, spi, &ad7192_sigma_delta_info);
+
+ ret = ad_sd_setup_buffer_and_trigger(indio_dev);
+ if (ret)
+ goto error_disable_reg;
+
+ ret = ad7192_setup(st, pdata);
+ if (ret)
+ goto error_remove_trigger;
+
+ ret = iio_device_register(indio_dev);
+ if (ret < 0)
+ goto error_remove_trigger;
+ return 0;
+
+error_remove_trigger:
+ ad_sd_cleanup_buffer_and_trigger(indio_dev);
+error_disable_reg:
+ if (!IS_ERR(st->reg))
+ regulator_disable(st->reg);
+
+ return ret;
+}
+
+static int ad7192_remove(struct spi_device *spi)
+{
+ struct iio_dev *indio_dev = spi_get_drvdata(spi);
+ struct ad7192_state *st = iio_priv(indio_dev);
+
+ iio_device_unregister(indio_dev);
+ ad_sd_cleanup_buffer_and_trigger(indio_dev);
+
+ if (!IS_ERR(st->reg))
+ regulator_disable(st->reg);
+
+ return 0;
+}
+
+static const struct spi_device_id ad7192_id[] = {
+ {"ad7190", ID_AD7190},
+ {"ad7192", ID_AD7192},
+ {"ad7195", ID_AD7195},
+ {}
+};
+MODULE_DEVICE_TABLE(spi, ad7192_id);
+
+static struct spi_driver ad7192_driver = {
+ .driver = {
+ .name = "ad7192",
+ .owner = THIS_MODULE,
+ },
+ .probe = ad7192_probe,
+ .remove = ad7192_remove,
+ .id_table = ad7192_id,
+};
+module_spi_driver(ad7192_driver);
+
+MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
+MODULE_DESCRIPTION("Analog Devices AD7190, AD7192, AD7195 ADC");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/staging/iio/adc/ad7192.h b/drivers/staging/iio/adc/ad7192.h
new file mode 100644
index 000000000..a0a5b61a4
--- /dev/null
+++ b/drivers/staging/iio/adc/ad7192.h
@@ -0,0 +1,47 @@
+/*
+ * AD7190 AD7192 AD7195 SPI ADC driver
+ *
+ * Copyright 2011 Analog Devices Inc.
+ *
+ * Licensed under the GPL-2.
+ */
+#ifndef IIO_ADC_AD7192_H_
+#define IIO_ADC_AD7192_H_
+
+/*
+ * TODO: struct ad7192_platform_data needs to go into include/linux/iio
+ */
+
+/**
+ * struct ad7192_platform_data - platform/board specific information
+ * @vref_mv: the external reference voltage in millivolt
+ * @clock_source_sel: [0..3]
+ * 0 External 4.92 MHz clock connected from MCLK1 to MCLK2
+ * 1 External Clock applied to MCLK2
+ * 2 Internal 4.92 MHz Clock not available at the MCLK2 pin
+ * 3 Internal 4.92 MHz Clock available at the MCLK2 pin
+ * @ext_clk_Hz: the external clock frequency in Hz, if not set
+ * the driver uses the internal clock (16.776 MHz)
+ * @refin2_en: REFIN1/REFIN2 Reference Select (AD7190/2 only)
+ * @rej60_en: 50/60Hz notch filter enable
+ * @sinc3_en: SINC3 filter enable (default SINC4)
+ * @chop_en: CHOP mode enable
+ * @buf_en: buffered input mode enable
+ * @unipolar_en: unipolar mode enable
+ * @burnout_curr_en: constant current generators on AIN(+|-) enable
+ */
+
+struct ad7192_platform_data {
+ u16 vref_mv;
+ u8 clock_source_sel;
+ u32 ext_clk_Hz;
+ bool refin2_en;
+ bool rej60_en;
+ bool sinc3_en;
+ bool chop_en;
+ bool buf_en;
+ bool unipolar_en;
+ bool burnout_curr_en;
+};
+
+#endif /* IIO_ADC_AD7192_H_ */
diff --git a/drivers/staging/iio/adc/ad7280a.c b/drivers/staging/iio/adc/ad7280a.c
new file mode 100644
index 000000000..d98e229c4
--- /dev/null
+++ b/drivers/staging/iio/adc/ad7280a.c
@@ -0,0 +1,985 @@
+/*
+ * AD7280A Lithium Ion Battery Monitoring System
+ *
+ * Copyright 2011 Analog Devices Inc.
+ *
+ * Licensed under the GPL-2.
+ */
+
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/sysfs.h>
+#include <linux/spi/spi.h>
+#include <linux/err.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/iio/events.h>
+
+#include "ad7280a.h"
+
+/* Registers */
+#define AD7280A_CELL_VOLTAGE_1 0x0 /* D11 to D0, Read only */
+#define AD7280A_CELL_VOLTAGE_2 0x1 /* D11 to D0, Read only */
+#define AD7280A_CELL_VOLTAGE_3 0x2 /* D11 to D0, Read only */
+#define AD7280A_CELL_VOLTAGE_4 0x3 /* D11 to D0, Read only */
+#define AD7280A_CELL_VOLTAGE_5 0x4 /* D11 to D0, Read only */
+#define AD7280A_CELL_VOLTAGE_6 0x5 /* D11 to D0, Read only */
+#define AD7280A_AUX_ADC_1 0x6 /* D11 to D0, Read only */
+#define AD7280A_AUX_ADC_2 0x7 /* D11 to D0, Read only */
+#define AD7280A_AUX_ADC_3 0x8 /* D11 to D0, Read only */
+#define AD7280A_AUX_ADC_4 0x9 /* D11 to D0, Read only */
+#define AD7280A_AUX_ADC_5 0xA /* D11 to D0, Read only */
+#define AD7280A_AUX_ADC_6 0xB /* D11 to D0, Read only */
+#define AD7280A_SELF_TEST 0xC /* D11 to D0, Read only */
+#define AD7280A_CONTROL_HB 0xD /* D15 to D8, Read/write */
+#define AD7280A_CONTROL_LB 0xE /* D7 to D0, Read/write */
+#define AD7280A_CELL_OVERVOLTAGE 0xF /* D7 to D0, Read/write */
+#define AD7280A_CELL_UNDERVOLTAGE 0x10 /* D7 to D0, Read/write */
+#define AD7280A_AUX_ADC_OVERVOLTAGE 0x11 /* D7 to D0, Read/write */
+#define AD7280A_AUX_ADC_UNDERVOLTAGE 0x12 /* D7 to D0, Read/write */
+#define AD7280A_ALERT 0x13 /* D7 to D0, Read/write */
+#define AD7280A_CELL_BALANCE 0x14 /* D7 to D0, Read/write */
+#define AD7280A_CB1_TIMER 0x15 /* D7 to D0, Read/write */
+#define AD7280A_CB2_TIMER 0x16 /* D7 to D0, Read/write */
+#define AD7280A_CB3_TIMER 0x17 /* D7 to D0, Read/write */
+#define AD7280A_CB4_TIMER 0x18 /* D7 to D0, Read/write */
+#define AD7280A_CB5_TIMER 0x19 /* D7 to D0, Read/write */
+#define AD7280A_CB6_TIMER 0x1A /* D7 to D0, Read/write */
+#define AD7280A_PD_TIMER 0x1B /* D7 to D0, Read/write */
+#define AD7280A_READ 0x1C /* D7 to D0, Read/write */
+#define AD7280A_CNVST_CONTROL 0x1D /* D7 to D0, Read/write */
+
+/* Bits and Masks */
+#define AD7280A_CTRL_HB_CONV_INPUT_ALL 0
+#define AD7280A_CTRL_HB_CONV_INPUT_6CELL_AUX1_3_4 BIT(6)
+#define AD7280A_CTRL_HB_CONV_INPUT_6CELL BIT(7)
+#define AD7280A_CTRL_HB_CONV_INPUT_SELF_TEST (BIT(7) | BIT(6))
+#define AD7280A_CTRL_HB_CONV_RES_READ_ALL 0
+#define AD7280A_CTRL_HB_CONV_RES_READ_6CELL_AUX1_3_4 BIT(4)
+#define AD7280A_CTRL_HB_CONV_RES_READ_6CELL BIT(5)
+#define AD7280A_CTRL_HB_CONV_RES_READ_NO (BIT(5) | BIT(4))
+#define AD7280A_CTRL_HB_CONV_START_CNVST 0
+#define AD7280A_CTRL_HB_CONV_START_CS BIT(3)
+#define AD7280A_CTRL_HB_CONV_AVG_DIS 0
+#define AD7280A_CTRL_HB_CONV_AVG_2 BIT(1)
+#define AD7280A_CTRL_HB_CONV_AVG_4 BIT(2)
+#define AD7280A_CTRL_HB_CONV_AVG_8 (BIT(2) | BIT(1))
+#define AD7280A_CTRL_HB_CONV_AVG(x) ((x) << 1)
+#define AD7280A_CTRL_HB_PWRDN_SW BIT(0)
+
+#define AD7280A_CTRL_LB_SWRST BIT(7)
+#define AD7280A_CTRL_LB_ACQ_TIME_400ns 0
+#define AD7280A_CTRL_LB_ACQ_TIME_800ns BIT(5)
+#define AD7280A_CTRL_LB_ACQ_TIME_1200ns BIT(6)
+#define AD7280A_CTRL_LB_ACQ_TIME_1600ns (BIT(6) | BIT(5))
+#define AD7280A_CTRL_LB_ACQ_TIME(x) ((x) << 5)
+#define AD7280A_CTRL_LB_MUST_SET BIT(4)
+#define AD7280A_CTRL_LB_THERMISTOR_EN BIT(3)
+#define AD7280A_CTRL_LB_LOCK_DEV_ADDR BIT(2)
+#define AD7280A_CTRL_LB_INC_DEV_ADDR BIT(1)
+#define AD7280A_CTRL_LB_DAISY_CHAIN_RB_EN BIT(0)
+
+#define AD7280A_ALERT_GEN_STATIC_HIGH BIT(6)
+#define AD7280A_ALERT_RELAY_SIG_CHAIN_DOWN (BIT(7) | BIT(6))
+
+#define AD7280A_ALL_CELLS (0xAD << 16)
+
+#define AD7280A_MAX_SPI_CLK_Hz 700000 /* < 1MHz */
+#define AD7280A_MAX_CHAIN 8
+#define AD7280A_CELLS_PER_DEV 6
+#define AD7280A_BITS 12
+#define AD7280A_NUM_CH (AD7280A_AUX_ADC_6 - \
+ AD7280A_CELL_VOLTAGE_1 + 1)
+
+#define AD7280A_DEVADDR_MASTER 0
+#define AD7280A_DEVADDR_ALL 0x1F
+/* 5-bit device address is sent LSB first */
+#define AD7280A_DEVADDR(addr) (((addr & 0x1) << 4) | ((addr & 0x2) << 3) | \
+ (addr & 0x4) | ((addr & 0x8) >> 3) | \
+ ((addr & 0x10) >> 4))
+
+/* During a read a valid write is mandatory.
+ * So writing to the highest available address (Address 0x1F)
+ * and setting the address all parts bit to 0 is recommended
+ * So the TXVAL is AD7280A_DEVADDR_ALL + CRC
+ */
+#define AD7280A_READ_TXVAL 0xF800030A
+
+/*
+ * AD7280 CRC
+ *
+ * P(x) = x^8 + x^5 + x^3 + x^2 + x^1 + x^0 = 0b100101111 => 0x2F
+ */
+#define POLYNOM 0x2F
+#define POLYNOM_ORDER 8
+#define HIGHBIT (1 << (POLYNOM_ORDER - 1))
+
+struct ad7280_state {
+ struct spi_device *spi;
+ struct iio_chan_spec *channels;
+ struct iio_dev_attr *iio_attr;
+ int slave_num;
+ int scan_cnt;
+ int readback_delay_us;
+ unsigned char crc_tab[256];
+ unsigned char ctrl_hb;
+ unsigned char ctrl_lb;
+ unsigned char cell_threshhigh;
+ unsigned char cell_threshlow;
+ unsigned char aux_threshhigh;
+ unsigned char aux_threshlow;
+ unsigned char cb_mask[AD7280A_MAX_CHAIN];
+
+ __be32 buf[2] ____cacheline_aligned;
+};
+
+static void ad7280_crc8_build_table(unsigned char *crc_tab)
+{
+ unsigned char bit, crc;
+ int cnt, i;
+
+ for (cnt = 0; cnt < 256; cnt++) {
+ crc = cnt;
+ for (i = 0; i < 8; i++) {
+ bit = crc & HIGHBIT;
+ crc <<= 1;
+ if (bit)
+ crc ^= POLYNOM;
+ }
+ crc_tab[cnt] = crc;
+ }
+}
+
+static unsigned char ad7280_calc_crc8(unsigned char *crc_tab, unsigned val)
+{
+ unsigned char crc;
+
+ crc = crc_tab[val >> 16 & 0xFF];
+ crc = crc_tab[crc ^ (val >> 8 & 0xFF)];
+
+ return crc ^ (val & 0xFF);
+}
+
+static int ad7280_check_crc(struct ad7280_state *st, unsigned val)
+{
+ unsigned char crc = ad7280_calc_crc8(st->crc_tab, val >> 10);
+
+ if (crc != ((val >> 2) & 0xFF))
+ return -EIO;
+
+ return 0;
+}
+
+/* After initiating a conversion sequence we need to wait until the
+ * conversion is done. The delay is typically in the range of 15..30 us
+ * however depending an the number of devices in the daisy chain and the
+ * number of averages taken, conversion delays and acquisition time options
+ * it may take up to 250us, in this case we better sleep instead of busy
+ * wait.
+ */
+
+static void ad7280_delay(struct ad7280_state *st)
+{
+ if (st->readback_delay_us < 50)
+ udelay(st->readback_delay_us);
+ else
+ usleep_range(250, 500);
+}
+
+static int __ad7280_read32(struct ad7280_state *st, unsigned *val)
+{
+ int ret;
+ struct spi_transfer t = {
+ .tx_buf = &st->buf[0],
+ .rx_buf = &st->buf[1],
+ .len = 4,
+ };
+
+ st->buf[0] = cpu_to_be32(AD7280A_READ_TXVAL);
+
+ ret = spi_sync_transfer(st->spi, &t, 1);
+ if (ret)
+ return ret;
+
+ *val = be32_to_cpu(st->buf[1]);
+
+ return 0;
+}
+
+static int ad7280_write(struct ad7280_state *st, unsigned devaddr,
+ unsigned addr, bool all, unsigned val)
+{
+ unsigned reg = (devaddr << 27 | addr << 21 |
+ (val & 0xFF) << 13 | all << 12);
+
+ reg |= ad7280_calc_crc8(st->crc_tab, reg >> 11) << 3 | 0x2;
+ st->buf[0] = cpu_to_be32(reg);
+
+ return spi_write(st->spi, &st->buf[0], 4);
+}
+
+static int ad7280_read(struct ad7280_state *st, unsigned devaddr,
+ unsigned addr)
+{
+ int ret;
+ unsigned tmp;
+
+ /* turns off the read operation on all parts */
+ ret = ad7280_write(st, AD7280A_DEVADDR_MASTER, AD7280A_CONTROL_HB, 1,
+ AD7280A_CTRL_HB_CONV_INPUT_ALL |
+ AD7280A_CTRL_HB_CONV_RES_READ_NO |
+ st->ctrl_hb);
+ if (ret)
+ return ret;
+
+ /* turns on the read operation on the addressed part */
+ ret = ad7280_write(st, devaddr, AD7280A_CONTROL_HB, 0,
+ AD7280A_CTRL_HB_CONV_INPUT_ALL |
+ AD7280A_CTRL_HB_CONV_RES_READ_ALL |
+ st->ctrl_hb);
+ if (ret)
+ return ret;
+
+ /* Set register address on the part to be read from */
+ ret = ad7280_write(st, devaddr, AD7280A_READ, 0, addr << 2);
+ if (ret)
+ return ret;
+
+ __ad7280_read32(st, &tmp);
+
+ if (ad7280_check_crc(st, tmp))
+ return -EIO;
+
+ if (((tmp >> 27) != devaddr) || (((tmp >> 21) & 0x3F) != addr))
+ return -EFAULT;
+
+ return (tmp >> 13) & 0xFF;
+}
+
+static int ad7280_read_channel(struct ad7280_state *st, unsigned devaddr,
+ unsigned addr)
+{
+ int ret;
+ unsigned tmp;
+
+ ret = ad7280_write(st, devaddr, AD7280A_READ, 0, addr << 2);
+ if (ret)
+ return ret;
+
+ ret = ad7280_write(st, AD7280A_DEVADDR_MASTER, AD7280A_CONTROL_HB, 1,
+ AD7280A_CTRL_HB_CONV_INPUT_ALL |
+ AD7280A_CTRL_HB_CONV_RES_READ_NO |
+ st->ctrl_hb);
+ if (ret)
+ return ret;
+
+ ret = ad7280_write(st, devaddr, AD7280A_CONTROL_HB, 0,
+ AD7280A_CTRL_HB_CONV_INPUT_ALL |
+ AD7280A_CTRL_HB_CONV_RES_READ_ALL |
+ AD7280A_CTRL_HB_CONV_START_CS |
+ st->ctrl_hb);
+ if (ret)
+ return ret;
+
+ ad7280_delay(st);
+
+ __ad7280_read32(st, &tmp);
+
+ if (ad7280_check_crc(st, tmp))
+ return -EIO;
+
+ if (((tmp >> 27) != devaddr) || (((tmp >> 23) & 0xF) != addr))
+ return -EFAULT;
+
+ return (tmp >> 11) & 0xFFF;
+}
+
+static int ad7280_read_all_channels(struct ad7280_state *st, unsigned cnt,
+ unsigned *array)
+{
+ int i, ret;
+ unsigned tmp, sum = 0;
+
+ ret = ad7280_write(st, AD7280A_DEVADDR_MASTER, AD7280A_READ, 1,
+ AD7280A_CELL_VOLTAGE_1 << 2);
+ if (ret)
+ return ret;
+
+ ret = ad7280_write(st, AD7280A_DEVADDR_MASTER, AD7280A_CONTROL_HB, 1,
+ AD7280A_CTRL_HB_CONV_INPUT_ALL |
+ AD7280A_CTRL_HB_CONV_RES_READ_ALL |
+ AD7280A_CTRL_HB_CONV_START_CS |
+ st->ctrl_hb);
+ if (ret)
+ return ret;
+
+ ad7280_delay(st);
+
+ for (i = 0; i < cnt; i++) {
+ __ad7280_read32(st, &tmp);
+
+ if (ad7280_check_crc(st, tmp))
+ return -EIO;
+
+ if (array)
+ array[i] = tmp;
+ /* only sum cell voltages */
+ if (((tmp >> 23) & 0xF) <= AD7280A_CELL_VOLTAGE_6)
+ sum += ((tmp >> 11) & 0xFFF);
+ }
+
+ return sum;
+}
+
+static int ad7280_chain_setup(struct ad7280_state *st)
+{
+ unsigned val, n;
+ int ret;
+
+ ret = ad7280_write(st, AD7280A_DEVADDR_MASTER, AD7280A_CONTROL_LB, 1,
+ AD7280A_CTRL_LB_DAISY_CHAIN_RB_EN |
+ AD7280A_CTRL_LB_LOCK_DEV_ADDR |
+ AD7280A_CTRL_LB_MUST_SET |
+ AD7280A_CTRL_LB_SWRST |
+ st->ctrl_lb);
+ if (ret)
+ return ret;
+
+ ret = ad7280_write(st, AD7280A_DEVADDR_MASTER, AD7280A_CONTROL_LB, 1,
+ AD7280A_CTRL_LB_DAISY_CHAIN_RB_EN |
+ AD7280A_CTRL_LB_LOCK_DEV_ADDR |
+ AD7280A_CTRL_LB_MUST_SET |
+ st->ctrl_lb);
+ if (ret)
+ return ret;
+
+ ret = ad7280_write(st, AD7280A_DEVADDR_MASTER, AD7280A_READ, 1,
+ AD7280A_CONTROL_LB << 2);
+ if (ret)
+ return ret;
+
+ for (n = 0; n <= AD7280A_MAX_CHAIN; n++) {
+ __ad7280_read32(st, &val);
+ if (val == 0)
+ return n - 1;
+
+ if (ad7280_check_crc(st, val))
+ return -EIO;
+
+ if (n != AD7280A_DEVADDR(val >> 27))
+ return -EIO;
+ }
+
+ return -EFAULT;
+}
+
+static ssize_t ad7280_show_balance_sw(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct ad7280_state *st = iio_priv(indio_dev);
+ struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
+
+ return sprintf(buf, "%d\n",
+ !!(st->cb_mask[this_attr->address >> 8] &
+ (1 << ((this_attr->address & 0xFF) + 2))));
+}
+
+static ssize_t ad7280_store_balance_sw(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t len)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct ad7280_state *st = iio_priv(indio_dev);
+ struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
+ bool readin;
+ int ret;
+ unsigned devaddr, ch;
+
+ ret = strtobool(buf, &readin);
+ if (ret)
+ return ret;
+
+ devaddr = this_attr->address >> 8;
+ ch = this_attr->address & 0xFF;
+
+ mutex_lock(&indio_dev->mlock);
+ if (readin)
+ st->cb_mask[devaddr] |= 1 << (ch + 2);
+ else
+ st->cb_mask[devaddr] &= ~(1 << (ch + 2));
+
+ ret = ad7280_write(st, devaddr, AD7280A_CELL_BALANCE,
+ 0, st->cb_mask[devaddr]);
+ mutex_unlock(&indio_dev->mlock);
+
+ return ret ? ret : len;
+}
+
+static ssize_t ad7280_show_balance_timer(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct ad7280_state *st = iio_priv(indio_dev);
+ struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
+ int ret;
+ unsigned msecs;
+
+ mutex_lock(&indio_dev->mlock);
+ ret = ad7280_read(st, this_attr->address >> 8,
+ this_attr->address & 0xFF);
+ mutex_unlock(&indio_dev->mlock);
+
+ if (ret < 0)
+ return ret;
+
+ msecs = (ret >> 3) * 71500;
+
+ return sprintf(buf, "%u\n", msecs);
+}
+
+static ssize_t ad7280_store_balance_timer(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t len)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct ad7280_state *st = iio_priv(indio_dev);
+ struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
+ unsigned long val;
+ int ret;
+
+ ret = kstrtoul(buf, 10, &val);
+ if (ret)
+ return ret;
+
+ val /= 71500;
+
+ if (val > 31)
+ return -EINVAL;
+
+ mutex_lock(&indio_dev->mlock);
+ ret = ad7280_write(st, this_attr->address >> 8,
+ this_attr->address & 0xFF,
+ 0, (val & 0x1F) << 3);
+ mutex_unlock(&indio_dev->mlock);
+
+ return ret ? ret : len;
+}
+
+static struct attribute *ad7280_attributes[AD7280A_MAX_CHAIN *
+ AD7280A_CELLS_PER_DEV * 2 + 1];
+
+static struct attribute_group ad7280_attrs_group = {
+ .attrs = ad7280_attributes,
+};
+
+static int ad7280_channel_init(struct ad7280_state *st)
+{
+ int dev, ch, cnt;
+
+ st->channels = kcalloc((st->slave_num + 1) * 12 + 2,
+ sizeof(*st->channels), GFP_KERNEL);
+ if (st->channels == NULL)
+ return -ENOMEM;
+
+ for (dev = 0, cnt = 0; dev <= st->slave_num; dev++)
+ for (ch = AD7280A_CELL_VOLTAGE_1; ch <= AD7280A_AUX_ADC_6; ch++,
+ cnt++) {
+ if (ch < AD7280A_AUX_ADC_1) {
+ st->channels[cnt].type = IIO_VOLTAGE;
+ st->channels[cnt].differential = 1;
+ st->channels[cnt].channel = (dev * 6) + ch;
+ st->channels[cnt].channel2 =
+ st->channels[cnt].channel + 1;
+ } else {
+ st->channels[cnt].type = IIO_TEMP;
+ st->channels[cnt].channel = (dev * 6) + ch - 6;
+ }
+ st->channels[cnt].indexed = 1;
+ st->channels[cnt].info_mask_separate =
+ BIT(IIO_CHAN_INFO_RAW);
+ st->channels[cnt].info_mask_shared_by_type =
+ BIT(IIO_CHAN_INFO_SCALE);
+ st->channels[cnt].address =
+ AD7280A_DEVADDR(dev) << 8 | ch;
+ st->channels[cnt].scan_index = cnt;
+ st->channels[cnt].scan_type.sign = 'u';
+ st->channels[cnt].scan_type.realbits = 12;
+ st->channels[cnt].scan_type.storagebits = 32;
+ st->channels[cnt].scan_type.shift = 0;
+ }
+
+ st->channels[cnt].type = IIO_VOLTAGE;
+ st->channels[cnt].differential = 1;
+ st->channels[cnt].channel = 0;
+ st->channels[cnt].channel2 = dev * 6;
+ st->channels[cnt].address = AD7280A_ALL_CELLS;
+ st->channels[cnt].indexed = 1;
+ st->channels[cnt].info_mask_separate = BIT(IIO_CHAN_INFO_RAW);
+ st->channels[cnt].info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE);
+ st->channels[cnt].scan_index = cnt;
+ st->channels[cnt].scan_type.sign = 'u';
+ st->channels[cnt].scan_type.realbits = 32;
+ st->channels[cnt].scan_type.storagebits = 32;
+ st->channels[cnt].scan_type.shift = 0;
+ cnt++;
+ st->channels[cnt].type = IIO_TIMESTAMP;
+ st->channels[cnt].channel = -1;
+ st->channels[cnt].scan_index = cnt;
+ st->channels[cnt].scan_type.sign = 's';
+ st->channels[cnt].scan_type.realbits = 64;
+ st->channels[cnt].scan_type.storagebits = 64;
+ st->channels[cnt].scan_type.shift = 0;
+
+ return cnt + 1;
+}
+
+static int ad7280_attr_init(struct ad7280_state *st)
+{
+ int dev, ch, cnt;
+
+ st->iio_attr = kcalloc(2, sizeof(*st->iio_attr) *
+ (st->slave_num + 1) * AD7280A_CELLS_PER_DEV,
+ GFP_KERNEL);
+ if (st->iio_attr == NULL)
+ return -ENOMEM;
+
+ for (dev = 0, cnt = 0; dev <= st->slave_num; dev++)
+ for (ch = AD7280A_CELL_VOLTAGE_1; ch <= AD7280A_CELL_VOLTAGE_6;
+ ch++, cnt++) {
+ st->iio_attr[cnt].address =
+ AD7280A_DEVADDR(dev) << 8 | ch;
+ st->iio_attr[cnt].dev_attr.attr.mode =
+ S_IWUSR | S_IRUGO;
+ st->iio_attr[cnt].dev_attr.show =
+ ad7280_show_balance_sw;
+ st->iio_attr[cnt].dev_attr.store =
+ ad7280_store_balance_sw;
+ st->iio_attr[cnt].dev_attr.attr.name =
+ kasprintf(GFP_KERNEL,
+ "in%d-in%d_balance_switch_en",
+ (dev * AD7280A_CELLS_PER_DEV) + ch,
+ (dev * AD7280A_CELLS_PER_DEV) + ch + 1);
+ ad7280_attributes[cnt] =
+ &st->iio_attr[cnt].dev_attr.attr;
+ cnt++;
+ st->iio_attr[cnt].address =
+ AD7280A_DEVADDR(dev) << 8 |
+ (AD7280A_CB1_TIMER + ch);
+ st->iio_attr[cnt].dev_attr.attr.mode =
+ S_IWUSR | S_IRUGO;
+ st->iio_attr[cnt].dev_attr.show =
+ ad7280_show_balance_timer;
+ st->iio_attr[cnt].dev_attr.store =
+ ad7280_store_balance_timer;
+ st->iio_attr[cnt].dev_attr.attr.name =
+ kasprintf(GFP_KERNEL, "in%d-in%d_balance_timer",
+ (dev * AD7280A_CELLS_PER_DEV) + ch,
+ (dev * AD7280A_CELLS_PER_DEV) + ch + 1);
+ ad7280_attributes[cnt] =
+ &st->iio_attr[cnt].dev_attr.attr;
+ }
+
+ ad7280_attributes[cnt] = NULL;
+
+ return 0;
+}
+
+static ssize_t ad7280_read_channel_config(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct ad7280_state *st = iio_priv(indio_dev);
+ struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
+ unsigned val;
+
+ switch ((u32) this_attr->address) {
+ case AD7280A_CELL_OVERVOLTAGE:
+ val = 1000 + (st->cell_threshhigh * 1568) / 100;
+ break;
+ case AD7280A_CELL_UNDERVOLTAGE:
+ val = 1000 + (st->cell_threshlow * 1568) / 100;
+ break;
+ case AD7280A_AUX_ADC_OVERVOLTAGE:
+ val = (st->aux_threshhigh * 196) / 10;
+ break;
+ case AD7280A_AUX_ADC_UNDERVOLTAGE:
+ val = (st->aux_threshlow * 196) / 10;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return sprintf(buf, "%u\n", val);
+}
+
+static ssize_t ad7280_write_channel_config(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t len)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct ad7280_state *st = iio_priv(indio_dev);
+ struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
+
+ long val;
+ int ret;
+
+ ret = kstrtol(buf, 10, &val);
+ if (ret)
+ return ret;
+
+ switch ((u32) this_attr->address) {
+ case AD7280A_CELL_OVERVOLTAGE:
+ case AD7280A_CELL_UNDERVOLTAGE:
+ val = ((val - 1000) * 100) / 1568; /* LSB 15.68mV */
+ break;
+ case AD7280A_AUX_ADC_OVERVOLTAGE:
+ case AD7280A_AUX_ADC_UNDERVOLTAGE:
+ val = (val * 10) / 196; /* LSB 19.6mV */
+ break;
+ default:
+ return -EFAULT;
+ }
+
+ val = clamp(val, 0L, 0xFFL);
+
+ mutex_lock(&indio_dev->mlock);
+ switch ((u32) this_attr->address) {
+ case AD7280A_CELL_OVERVOLTAGE:
+ st->cell_threshhigh = val;
+ break;
+ case AD7280A_CELL_UNDERVOLTAGE:
+ st->cell_threshlow = val;
+ break;
+ case AD7280A_AUX_ADC_OVERVOLTAGE:
+ st->aux_threshhigh = val;
+ break;
+ case AD7280A_AUX_ADC_UNDERVOLTAGE:
+ st->aux_threshlow = val;
+ break;
+ }
+
+ ret = ad7280_write(st, AD7280A_DEVADDR_MASTER,
+ this_attr->address, 1, val);
+
+ mutex_unlock(&indio_dev->mlock);
+
+ return ret ? ret : len;
+}
+
+static irqreturn_t ad7280_event_handler(int irq, void *private)
+{
+ struct iio_dev *indio_dev = private;
+ struct ad7280_state *st = iio_priv(indio_dev);
+ unsigned *channels;
+ int i, ret;
+
+ channels = kcalloc(st->scan_cnt, sizeof(*channels), GFP_KERNEL);
+ if (channels == NULL)
+ return IRQ_HANDLED;
+
+ ret = ad7280_read_all_channels(st, st->scan_cnt, channels);
+ if (ret < 0)
+ goto out;
+
+ for (i = 0; i < st->scan_cnt; i++) {
+ if (((channels[i] >> 23) & 0xF) <= AD7280A_CELL_VOLTAGE_6) {
+ if (((channels[i] >> 11) & 0xFFF) >=
+ st->cell_threshhigh)
+ iio_push_event(indio_dev,
+ IIO_EVENT_CODE(IIO_VOLTAGE,
+ 1,
+ 0,
+ IIO_EV_DIR_RISING,
+ IIO_EV_TYPE_THRESH,
+ 0, 0, 0),
+ iio_get_time_ns());
+ else if (((channels[i] >> 11) & 0xFFF) <=
+ st->cell_threshlow)
+ iio_push_event(indio_dev,
+ IIO_EVENT_CODE(IIO_VOLTAGE,
+ 1,
+ 0,
+ IIO_EV_DIR_FALLING,
+ IIO_EV_TYPE_THRESH,
+ 0, 0, 0),
+ iio_get_time_ns());
+ } else {
+ if (((channels[i] >> 11) & 0xFFF) >= st->aux_threshhigh)
+ iio_push_event(indio_dev,
+ IIO_UNMOD_EVENT_CODE(IIO_TEMP,
+ 0,
+ IIO_EV_TYPE_THRESH,
+ IIO_EV_DIR_RISING),
+ iio_get_time_ns());
+ else if (((channels[i] >> 11) & 0xFFF) <=
+ st->aux_threshlow)
+ iio_push_event(indio_dev,
+ IIO_UNMOD_EVENT_CODE(IIO_TEMP,
+ 0,
+ IIO_EV_TYPE_THRESH,
+ IIO_EV_DIR_FALLING),
+ iio_get_time_ns());
+ }
+ }
+
+out:
+ kfree(channels);
+
+ return IRQ_HANDLED;
+}
+
+static IIO_DEVICE_ATTR_NAMED(in_thresh_low_value,
+ in_voltage-voltage_thresh_low_value,
+ S_IRUGO | S_IWUSR,
+ ad7280_read_channel_config,
+ ad7280_write_channel_config,
+ AD7280A_CELL_UNDERVOLTAGE);
+
+static IIO_DEVICE_ATTR_NAMED(in_thresh_high_value,
+ in_voltage-voltage_thresh_high_value,
+ S_IRUGO | S_IWUSR,
+ ad7280_read_channel_config,
+ ad7280_write_channel_config,
+ AD7280A_CELL_OVERVOLTAGE);
+
+static IIO_DEVICE_ATTR(in_temp_thresh_low_value,
+ S_IRUGO | S_IWUSR,
+ ad7280_read_channel_config,
+ ad7280_write_channel_config,
+ AD7280A_AUX_ADC_UNDERVOLTAGE);
+
+static IIO_DEVICE_ATTR(in_temp_thresh_high_value,
+ S_IRUGO | S_IWUSR,
+ ad7280_read_channel_config,
+ ad7280_write_channel_config,
+ AD7280A_AUX_ADC_OVERVOLTAGE);
+
+
+static struct attribute *ad7280_event_attributes[] = {
+ &iio_dev_attr_in_thresh_low_value.dev_attr.attr,
+ &iio_dev_attr_in_thresh_high_value.dev_attr.attr,
+ &iio_dev_attr_in_temp_thresh_low_value.dev_attr.attr,
+ &iio_dev_attr_in_temp_thresh_high_value.dev_attr.attr,
+ NULL,
+};
+
+static struct attribute_group ad7280_event_attrs_group = {
+ .attrs = ad7280_event_attributes,
+};
+
+static int ad7280_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val,
+ int *val2,
+ long m)
+{
+ struct ad7280_state *st = iio_priv(indio_dev);
+ int ret;
+
+ switch (m) {
+ case IIO_CHAN_INFO_RAW:
+ mutex_lock(&indio_dev->mlock);
+ if (chan->address == AD7280A_ALL_CELLS)
+ ret = ad7280_read_all_channels(st, st->scan_cnt, NULL);
+ else
+ ret = ad7280_read_channel(st, chan->address >> 8,
+ chan->address & 0xFF);
+ mutex_unlock(&indio_dev->mlock);
+
+ if (ret < 0)
+ return ret;
+
+ *val = ret;
+
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_SCALE:
+ if ((chan->address & 0xFF) <= AD7280A_CELL_VOLTAGE_6)
+ *val = 4000;
+ else
+ *val = 5000;
+
+ *val2 = AD7280A_BITS;
+ return IIO_VAL_FRACTIONAL_LOG2;
+ }
+ return -EINVAL;
+}
+
+static const struct iio_info ad7280_info = {
+ .read_raw = &ad7280_read_raw,
+ .event_attrs = &ad7280_event_attrs_group,
+ .attrs = &ad7280_attrs_group,
+ .driver_module = THIS_MODULE,
+};
+
+static const struct ad7280_platform_data ad7793_default_pdata = {
+ .acquisition_time = AD7280A_ACQ_TIME_400ns,
+ .conversion_averaging = AD7280A_CONV_AVG_DIS,
+ .thermistor_term_en = true,
+};
+
+static int ad7280_probe(struct spi_device *spi)
+{
+ const struct ad7280_platform_data *pdata = spi->dev.platform_data;
+ struct ad7280_state *st;
+ int ret;
+ const unsigned short tACQ_ns[4] = {465, 1010, 1460, 1890};
+ const unsigned short nAVG[4] = {1, 2, 4, 8};
+ struct iio_dev *indio_dev;
+
+ indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));
+ if (indio_dev == NULL)
+ return -ENOMEM;
+
+ st = iio_priv(indio_dev);
+ spi_set_drvdata(spi, indio_dev);
+ st->spi = spi;
+
+ if (!pdata)
+ pdata = &ad7793_default_pdata;
+
+ ad7280_crc8_build_table(st->crc_tab);
+
+ st->spi->max_speed_hz = AD7280A_MAX_SPI_CLK_Hz;
+ st->spi->mode = SPI_MODE_1;
+ spi_setup(st->spi);
+
+ st->ctrl_lb = AD7280A_CTRL_LB_ACQ_TIME(pdata->acquisition_time & 0x3);
+ st->ctrl_hb = AD7280A_CTRL_HB_CONV_AVG(pdata->conversion_averaging
+ & 0x3) | (pdata->thermistor_term_en ?
+ AD7280A_CTRL_LB_THERMISTOR_EN : 0);
+
+ ret = ad7280_chain_setup(st);
+ if (ret < 0)
+ return ret;
+
+ st->slave_num = ret;
+ st->scan_cnt = (st->slave_num + 1) * AD7280A_NUM_CH;
+ st->cell_threshhigh = 0xFF;
+ st->aux_threshhigh = 0xFF;
+
+ /*
+ * Total Conversion Time = ((tACQ + tCONV) *
+ * (Number of Conversions per Part)) −
+ * tACQ + ((N - 1) * tDELAY)
+ *
+ * Readback Delay = Total Conversion Time + tWAIT
+ */
+
+ st->readback_delay_us =
+ ((tACQ_ns[pdata->acquisition_time & 0x3] + 695) *
+ (AD7280A_NUM_CH * nAVG[pdata->conversion_averaging & 0x3]))
+ - tACQ_ns[pdata->acquisition_time & 0x3] +
+ st->slave_num * 250;
+
+ /* Convert to usecs */
+ st->readback_delay_us = DIV_ROUND_UP(st->readback_delay_us, 1000);
+ st->readback_delay_us += 5; /* Add tWAIT */
+
+ indio_dev->name = spi_get_device_id(spi)->name;
+ indio_dev->dev.parent = &spi->dev;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+
+ ret = ad7280_channel_init(st);
+ if (ret < 0)
+ return ret;
+
+ indio_dev->num_channels = ret;
+ indio_dev->channels = st->channels;
+ indio_dev->info = &ad7280_info;
+
+ ret = ad7280_attr_init(st);
+ if (ret < 0)
+ goto error_free_channels;
+
+ ret = iio_device_register(indio_dev);
+ if (ret)
+ goto error_free_attr;
+
+ if (spi->irq > 0) {
+ ret = ad7280_write(st, AD7280A_DEVADDR_MASTER,
+ AD7280A_ALERT, 1,
+ AD7280A_ALERT_RELAY_SIG_CHAIN_DOWN);
+ if (ret)
+ goto error_unregister;
+
+ ret = ad7280_write(st, AD7280A_DEVADDR(st->slave_num),
+ AD7280A_ALERT, 0,
+ AD7280A_ALERT_GEN_STATIC_HIGH |
+ (pdata->chain_last_alert_ignore & 0xF));
+ if (ret)
+ goto error_unregister;
+
+ ret = request_threaded_irq(spi->irq,
+ NULL,
+ ad7280_event_handler,
+ IRQF_TRIGGER_FALLING |
+ IRQF_ONESHOT,
+ indio_dev->name,
+ indio_dev);
+ if (ret)
+ goto error_unregister;
+ }
+
+ return 0;
+error_unregister:
+ iio_device_unregister(indio_dev);
+
+error_free_attr:
+ kfree(st->iio_attr);
+
+error_free_channels:
+ kfree(st->channels);
+
+ return ret;
+}
+
+static int ad7280_remove(struct spi_device *spi)
+{
+ struct iio_dev *indio_dev = spi_get_drvdata(spi);
+ struct ad7280_state *st = iio_priv(indio_dev);
+
+ if (spi->irq > 0)
+ free_irq(spi->irq, indio_dev);
+ iio_device_unregister(indio_dev);
+
+ ad7280_write(st, AD7280A_DEVADDR_MASTER, AD7280A_CONTROL_HB, 1,
+ AD7280A_CTRL_HB_PWRDN_SW | st->ctrl_hb);
+
+ kfree(st->channels);
+ kfree(st->iio_attr);
+
+ return 0;
+}
+
+static const struct spi_device_id ad7280_id[] = {
+ {"ad7280a", 0},
+ {}
+};
+MODULE_DEVICE_TABLE(spi, ad7280_id);
+
+static struct spi_driver ad7280_driver = {
+ .driver = {
+ .name = "ad7280",
+ .owner = THIS_MODULE,
+ },
+ .probe = ad7280_probe,
+ .remove = ad7280_remove,
+ .id_table = ad7280_id,
+};
+module_spi_driver(ad7280_driver);
+
+MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
+MODULE_DESCRIPTION("Analog Devices AD7280A");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/staging/iio/adc/ad7280a.h b/drivers/staging/iio/adc/ad7280a.h
new file mode 100644
index 000000000..732347a9b
--- /dev/null
+++ b/drivers/staging/iio/adc/ad7280a.h
@@ -0,0 +1,38 @@
+/*
+ * AD7280A Lithium Ion Battery Monitoring System
+ *
+ * Copyright 2011 Analog Devices Inc.
+ *
+ * Licensed under the GPL-2.
+ */
+
+#ifndef IIO_ADC_AD7280_H_
+#define IIO_ADC_AD7280_H_
+
+/*
+ * TODO: struct ad7280_platform_data needs to go into include/linux/iio
+ */
+
+#define AD7280A_ACQ_TIME_400ns 0
+#define AD7280A_ACQ_TIME_800ns 1
+#define AD7280A_ACQ_TIME_1200ns 2
+#define AD7280A_ACQ_TIME_1600ns 3
+
+#define AD7280A_CONV_AVG_DIS 0
+#define AD7280A_CONV_AVG_2 1
+#define AD7280A_CONV_AVG_4 2
+#define AD7280A_CONV_AVG_8 3
+
+#define AD7280A_ALERT_REMOVE_VIN5 BIT(2)
+#define AD7280A_ALERT_REMOVE_VIN4_VIN5 BIT(3)
+#define AD7280A_ALERT_REMOVE_AUX5 BIT(0)
+#define AD7280A_ALERT_REMOVE_AUX4_AUX5 BIT(1)
+
+struct ad7280_platform_data {
+ unsigned acquisition_time;
+ unsigned conversion_averaging;
+ unsigned chain_last_alert_ignore;
+ bool thermistor_term_en;
+};
+
+#endif /* IIO_ADC_AD7280_H_ */
diff --git a/drivers/staging/iio/adc/ad7606.h b/drivers/staging/iio/adc/ad7606.h
new file mode 100644
index 000000000..ec89d055c
--- /dev/null
+++ b/drivers/staging/iio/adc/ad7606.h
@@ -0,0 +1,104 @@
+/*
+ * AD7606 ADC driver
+ *
+ * Copyright 2011 Analog Devices Inc.
+ *
+ * Licensed under the GPL-2.
+ */
+
+#ifndef IIO_ADC_AD7606_H_
+#define IIO_ADC_AD7606_H_
+
+/*
+ * TODO: struct ad7606_platform_data needs to go into include/linux/iio
+ */
+
+/**
+ * struct ad7606_platform_data - platform/board specific information
+ * @default_os: default oversampling value {0, 2, 4, 8, 16, 32, 64}
+ * @default_range: default range +/-{5000, 10000} mVolt
+ * @gpio_convst: number of gpio connected to the CONVST pin
+ * @gpio_reset: gpio connected to the RESET pin, if not used set to -1
+ * @gpio_range: gpio connected to the RANGE pin, if not used set to -1
+ * @gpio_os0: gpio connected to the OS0 pin, if not used set to -1
+ * @gpio_os1: gpio connected to the OS1 pin, if not used set to -1
+ * @gpio_os2: gpio connected to the OS2 pin, if not used set to -1
+ * @gpio_frstdata: gpio connected to the FRSTDAT pin, if not used set to -1
+ * @gpio_stby: gpio connected to the STBY pin, if not used set to -1
+ */
+
+struct ad7606_platform_data {
+ unsigned default_os;
+ unsigned default_range;
+ unsigned gpio_convst;
+ unsigned gpio_reset;
+ unsigned gpio_range;
+ unsigned gpio_os0;
+ unsigned gpio_os1;
+ unsigned gpio_os2;
+ unsigned gpio_frstdata;
+ unsigned gpio_stby;
+};
+
+/**
+ * struct ad7606_chip_info - chip specific information
+ * @name: identification string for chip
+ * @int_vref_mv: the internal reference voltage
+ * @channels: channel specification
+ * @num_channels: number of channels
+ */
+
+struct ad7606_chip_info {
+ const char *name;
+ u16 int_vref_mv;
+ const struct iio_chan_spec *channels;
+ unsigned num_channels;
+};
+
+/**
+ * struct ad7606_state - driver instance specific data
+ */
+
+struct ad7606_state {
+ struct device *dev;
+ const struct ad7606_chip_info *chip_info;
+ struct ad7606_platform_data *pdata;
+ struct regulator *reg;
+ struct work_struct poll_work;
+ wait_queue_head_t wq_data_avail;
+ const struct ad7606_bus_ops *bops;
+ unsigned range;
+ unsigned oversampling;
+ bool done;
+ void __iomem *base_address;
+
+ /*
+ * DMA (thus cache coherency maintenance) requires the
+ * transfer buffers to live in their own cache lines.
+ */
+
+ unsigned short data[8] ____cacheline_aligned;
+};
+
+struct ad7606_bus_ops {
+ /* more methods added in future? */
+ int (*read_block)(struct device *, int, void *);
+};
+
+void ad7606_suspend(struct iio_dev *indio_dev);
+void ad7606_resume(struct iio_dev *indio_dev);
+struct iio_dev *ad7606_probe(struct device *dev, int irq,
+ void __iomem *base_address, unsigned id,
+ const struct ad7606_bus_ops *bops);
+int ad7606_remove(struct iio_dev *indio_dev, int irq);
+int ad7606_reset(struct ad7606_state *st);
+
+enum ad7606_supported_device_ids {
+ ID_AD7606_8,
+ ID_AD7606_6,
+ ID_AD7606_4
+};
+
+int ad7606_register_ring_funcs_and_init(struct iio_dev *indio_dev);
+void ad7606_ring_cleanup(struct iio_dev *indio_dev);
+#endif /* IIO_ADC_AD7606_H_ */
diff --git a/drivers/staging/iio/adc/ad7606_core.c b/drivers/staging/iio/adc/ad7606_core.c
new file mode 100644
index 000000000..bf2c80131
--- /dev/null
+++ b/drivers/staging/iio/adc/ad7606_core.c
@@ -0,0 +1,603 @@
+/*
+ * AD7606 SPI ADC driver
+ *
+ * Copyright 2011 Analog Devices Inc.
+ *
+ * Licensed under the GPL-2.
+ */
+
+#include <linux/interrupt.h>
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/sysfs.h>
+#include <linux/regulator/consumer.h>
+#include <linux/err.h>
+#include <linux/gpio.h>
+#include <linux/delay.h>
+#include <linux/sched.h>
+#include <linux/module.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/iio/buffer.h>
+
+#include "ad7606.h"
+
+int ad7606_reset(struct ad7606_state *st)
+{
+ if (gpio_is_valid(st->pdata->gpio_reset)) {
+ gpio_set_value(st->pdata->gpio_reset, 1);
+ ndelay(100); /* t_reset >= 100ns */
+ gpio_set_value(st->pdata->gpio_reset, 0);
+ return 0;
+ }
+
+ return -ENODEV;
+}
+
+static int ad7606_scan_direct(struct iio_dev *indio_dev, unsigned ch)
+{
+ struct ad7606_state *st = iio_priv(indio_dev);
+ int ret;
+
+ st->done = false;
+ gpio_set_value(st->pdata->gpio_convst, 1);
+
+ ret = wait_event_interruptible(st->wq_data_avail, st->done);
+ if (ret)
+ goto error_ret;
+
+ if (gpio_is_valid(st->pdata->gpio_frstdata)) {
+ ret = st->bops->read_block(st->dev, 1, st->data);
+ if (ret)
+ goto error_ret;
+ if (!gpio_get_value(st->pdata->gpio_frstdata)) {
+ /* This should never happen */
+ ad7606_reset(st);
+ ret = -EIO;
+ goto error_ret;
+ }
+ ret = st->bops->read_block(st->dev,
+ st->chip_info->num_channels - 1, &st->data[1]);
+ if (ret)
+ goto error_ret;
+ } else {
+ ret = st->bops->read_block(st->dev,
+ st->chip_info->num_channels, st->data);
+ if (ret)
+ goto error_ret;
+ }
+
+ ret = st->data[ch];
+
+error_ret:
+ gpio_set_value(st->pdata->gpio_convst, 0);
+
+ return ret;
+}
+
+static int ad7606_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val,
+ int *val2,
+ long m)
+{
+ int ret;
+ struct ad7606_state *st = iio_priv(indio_dev);
+
+ switch (m) {
+ case IIO_CHAN_INFO_RAW:
+ mutex_lock(&indio_dev->mlock);
+ if (iio_buffer_enabled(indio_dev))
+ ret = -EBUSY;
+ else
+ ret = ad7606_scan_direct(indio_dev, chan->address);
+ mutex_unlock(&indio_dev->mlock);
+
+ if (ret < 0)
+ return ret;
+ *val = (short) ret;
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_SCALE:
+ *val = st->range * 2;
+ *val2 = st->chip_info->channels[0].scan_type.realbits;
+ return IIO_VAL_FRACTIONAL_LOG2;
+ }
+ return -EINVAL;
+}
+
+static ssize_t ad7606_show_range(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct ad7606_state *st = iio_priv(indio_dev);
+
+ return sprintf(buf, "%u\n", st->range);
+}
+
+static ssize_t ad7606_store_range(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct ad7606_state *st = iio_priv(indio_dev);
+ unsigned long lval;
+ int ret;
+
+ ret = kstrtoul(buf, 10, &lval);
+ if (ret)
+ return ret;
+
+ if (!(lval == 5000 || lval == 10000)) {
+ dev_err(dev, "range is not supported\n");
+ return -EINVAL;
+ }
+ mutex_lock(&indio_dev->mlock);
+ gpio_set_value(st->pdata->gpio_range, lval == 10000);
+ st->range = lval;
+ mutex_unlock(&indio_dev->mlock);
+
+ return count;
+}
+
+static IIO_DEVICE_ATTR(in_voltage_range, S_IRUGO | S_IWUSR,
+ ad7606_show_range, ad7606_store_range, 0);
+static IIO_CONST_ATTR(in_voltage_range_available, "5000 10000");
+
+static ssize_t ad7606_show_oversampling_ratio(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct ad7606_state *st = iio_priv(indio_dev);
+
+ return sprintf(buf, "%u\n", st->oversampling);
+}
+
+static int ad7606_oversampling_get_index(unsigned val)
+{
+ unsigned char supported[] = {0, 2, 4, 8, 16, 32, 64};
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(supported); i++)
+ if (val == supported[i])
+ return i;
+
+ return -EINVAL;
+}
+
+static ssize_t ad7606_store_oversampling_ratio(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct ad7606_state *st = iio_priv(indio_dev);
+ unsigned long lval;
+ int ret;
+
+ ret = kstrtoul(buf, 10, &lval);
+ if (ret)
+ return ret;
+
+ ret = ad7606_oversampling_get_index(lval);
+ if (ret < 0) {
+ dev_err(dev, "oversampling %lu is not supported\n", lval);
+ return ret;
+ }
+
+ mutex_lock(&indio_dev->mlock);
+ gpio_set_value(st->pdata->gpio_os0, (ret >> 0) & 1);
+ gpio_set_value(st->pdata->gpio_os1, (ret >> 1) & 1);
+ gpio_set_value(st->pdata->gpio_os1, (ret >> 2) & 1);
+ st->oversampling = lval;
+ mutex_unlock(&indio_dev->mlock);
+
+ return count;
+}
+
+static IIO_DEVICE_ATTR(oversampling_ratio, S_IRUGO | S_IWUSR,
+ ad7606_show_oversampling_ratio,
+ ad7606_store_oversampling_ratio, 0);
+static IIO_CONST_ATTR(oversampling_ratio_available, "0 2 4 8 16 32 64");
+
+static struct attribute *ad7606_attributes_os_and_range[] = {
+ &iio_dev_attr_in_voltage_range.dev_attr.attr,
+ &iio_const_attr_in_voltage_range_available.dev_attr.attr,
+ &iio_dev_attr_oversampling_ratio.dev_attr.attr,
+ &iio_const_attr_oversampling_ratio_available.dev_attr.attr,
+ NULL,
+};
+
+static const struct attribute_group ad7606_attribute_group_os_and_range = {
+ .attrs = ad7606_attributes_os_and_range,
+};
+
+static struct attribute *ad7606_attributes_os[] = {
+ &iio_dev_attr_oversampling_ratio.dev_attr.attr,
+ &iio_const_attr_oversampling_ratio_available.dev_attr.attr,
+ NULL,
+};
+
+static const struct attribute_group ad7606_attribute_group_os = {
+ .attrs = ad7606_attributes_os,
+};
+
+static struct attribute *ad7606_attributes_range[] = {
+ &iio_dev_attr_in_voltage_range.dev_attr.attr,
+ &iio_const_attr_in_voltage_range_available.dev_attr.attr,
+ NULL,
+};
+
+static const struct attribute_group ad7606_attribute_group_range = {
+ .attrs = ad7606_attributes_range,
+};
+
+#define AD7606_CHANNEL(num) \
+ { \
+ .type = IIO_VOLTAGE, \
+ .indexed = 1, \
+ .channel = num, \
+ .address = num, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),\
+ .scan_index = num, \
+ .scan_type = { \
+ .sign = 's', \
+ .realbits = 16, \
+ .storagebits = 16, \
+ .endianness = IIO_CPU, \
+ }, \
+ }
+
+static const struct iio_chan_spec ad7606_8_channels[] = {
+ AD7606_CHANNEL(0),
+ AD7606_CHANNEL(1),
+ AD7606_CHANNEL(2),
+ AD7606_CHANNEL(3),
+ AD7606_CHANNEL(4),
+ AD7606_CHANNEL(5),
+ AD7606_CHANNEL(6),
+ AD7606_CHANNEL(7),
+ IIO_CHAN_SOFT_TIMESTAMP(8),
+};
+
+static const struct iio_chan_spec ad7606_6_channels[] = {
+ AD7606_CHANNEL(0),
+ AD7606_CHANNEL(1),
+ AD7606_CHANNEL(2),
+ AD7606_CHANNEL(3),
+ AD7606_CHANNEL(4),
+ AD7606_CHANNEL(5),
+ IIO_CHAN_SOFT_TIMESTAMP(6),
+};
+
+static const struct iio_chan_spec ad7606_4_channels[] = {
+ AD7606_CHANNEL(0),
+ AD7606_CHANNEL(1),
+ AD7606_CHANNEL(2),
+ AD7606_CHANNEL(3),
+ IIO_CHAN_SOFT_TIMESTAMP(4),
+};
+
+static const struct ad7606_chip_info ad7606_chip_info_tbl[] = {
+ /*
+ * More devices added in future
+ */
+ [ID_AD7606_8] = {
+ .name = "ad7606",
+ .int_vref_mv = 2500,
+ .channels = ad7606_8_channels,
+ .num_channels = 8,
+ },
+ [ID_AD7606_6] = {
+ .name = "ad7606-6",
+ .int_vref_mv = 2500,
+ .channels = ad7606_6_channels,
+ .num_channels = 6,
+ },
+ [ID_AD7606_4] = {
+ .name = "ad7606-4",
+ .int_vref_mv = 2500,
+ .channels = ad7606_4_channels,
+ .num_channels = 4,
+ },
+};
+
+static int ad7606_request_gpios(struct ad7606_state *st)
+{
+ struct gpio gpio_array[3] = {
+ [0] = {
+ .gpio = st->pdata->gpio_os0,
+ .flags = GPIOF_DIR_OUT | ((st->oversampling & 1) ?
+ GPIOF_INIT_HIGH : GPIOF_INIT_LOW),
+ .label = "AD7606_OS0",
+ },
+ [1] = {
+ .gpio = st->pdata->gpio_os1,
+ .flags = GPIOF_DIR_OUT | ((st->oversampling & 2) ?
+ GPIOF_INIT_HIGH : GPIOF_INIT_LOW),
+ .label = "AD7606_OS1",
+ },
+ [2] = {
+ .gpio = st->pdata->gpio_os2,
+ .flags = GPIOF_DIR_OUT | ((st->oversampling & 4) ?
+ GPIOF_INIT_HIGH : GPIOF_INIT_LOW),
+ .label = "AD7606_OS2",
+ },
+ };
+ int ret;
+
+ if (gpio_is_valid(st->pdata->gpio_convst)) {
+ ret = gpio_request_one(st->pdata->gpio_convst,
+ GPIOF_OUT_INIT_LOW,
+ "AD7606_CONVST");
+ if (ret) {
+ dev_err(st->dev, "failed to request GPIO CONVST\n");
+ goto error_ret;
+ }
+ } else {
+ ret = -EIO;
+ goto error_ret;
+ }
+
+ if (gpio_is_valid(st->pdata->gpio_os0) &&
+ gpio_is_valid(st->pdata->gpio_os1) &&
+ gpio_is_valid(st->pdata->gpio_os2)) {
+ ret = gpio_request_array(gpio_array, ARRAY_SIZE(gpio_array));
+ if (ret < 0)
+ goto error_free_convst;
+ }
+
+ if (gpio_is_valid(st->pdata->gpio_reset)) {
+ ret = gpio_request_one(st->pdata->gpio_reset,
+ GPIOF_OUT_INIT_LOW,
+ "AD7606_RESET");
+ if (ret < 0)
+ goto error_free_os;
+ }
+
+ if (gpio_is_valid(st->pdata->gpio_range)) {
+ ret = gpio_request_one(st->pdata->gpio_range, GPIOF_DIR_OUT |
+ ((st->range == 10000) ? GPIOF_INIT_HIGH :
+ GPIOF_INIT_LOW), "AD7606_RANGE");
+ if (ret < 0)
+ goto error_free_reset;
+ }
+ if (gpio_is_valid(st->pdata->gpio_stby)) {
+ ret = gpio_request_one(st->pdata->gpio_stby,
+ GPIOF_OUT_INIT_HIGH,
+ "AD7606_STBY");
+ if (ret < 0)
+ goto error_free_range;
+ }
+
+ if (gpio_is_valid(st->pdata->gpio_frstdata)) {
+ ret = gpio_request_one(st->pdata->gpio_frstdata, GPIOF_IN,
+ "AD7606_FRSTDATA");
+ if (ret < 0)
+ goto error_free_stby;
+ }
+
+ return 0;
+
+error_free_stby:
+ if (gpio_is_valid(st->pdata->gpio_stby))
+ gpio_free(st->pdata->gpio_stby);
+error_free_range:
+ if (gpio_is_valid(st->pdata->gpio_range))
+ gpio_free(st->pdata->gpio_range);
+error_free_reset:
+ if (gpio_is_valid(st->pdata->gpio_reset))
+ gpio_free(st->pdata->gpio_reset);
+error_free_os:
+ if (gpio_is_valid(st->pdata->gpio_os0) &&
+ gpio_is_valid(st->pdata->gpio_os1) &&
+ gpio_is_valid(st->pdata->gpio_os2))
+ gpio_free_array(gpio_array, ARRAY_SIZE(gpio_array));
+error_free_convst:
+ gpio_free(st->pdata->gpio_convst);
+error_ret:
+ return ret;
+}
+
+static void ad7606_free_gpios(struct ad7606_state *st)
+{
+ if (gpio_is_valid(st->pdata->gpio_frstdata))
+ gpio_free(st->pdata->gpio_frstdata);
+ if (gpio_is_valid(st->pdata->gpio_stby))
+ gpio_free(st->pdata->gpio_stby);
+ if (gpio_is_valid(st->pdata->gpio_range))
+ gpio_free(st->pdata->gpio_range);
+ if (gpio_is_valid(st->pdata->gpio_reset))
+ gpio_free(st->pdata->gpio_reset);
+ if (gpio_is_valid(st->pdata->gpio_os0) &&
+ gpio_is_valid(st->pdata->gpio_os1) &&
+ gpio_is_valid(st->pdata->gpio_os2)) {
+ gpio_free(st->pdata->gpio_os2);
+ gpio_free(st->pdata->gpio_os1);
+ gpio_free(st->pdata->gpio_os0);
+ }
+ gpio_free(st->pdata->gpio_convst);
+}
+
+/**
+ * Interrupt handler
+ */
+static irqreturn_t ad7606_interrupt(int irq, void *dev_id)
+{
+ struct iio_dev *indio_dev = dev_id;
+ struct ad7606_state *st = iio_priv(indio_dev);
+
+ if (iio_buffer_enabled(indio_dev)) {
+ schedule_work(&st->poll_work);
+ } else {
+ st->done = true;
+ wake_up_interruptible(&st->wq_data_avail);
+ }
+
+ return IRQ_HANDLED;
+};
+
+static const struct iio_info ad7606_info_no_os_or_range = {
+ .driver_module = THIS_MODULE,
+ .read_raw = &ad7606_read_raw,
+};
+
+static const struct iio_info ad7606_info_os_and_range = {
+ .driver_module = THIS_MODULE,
+ .read_raw = &ad7606_read_raw,
+ .attrs = &ad7606_attribute_group_os_and_range,
+};
+
+static const struct iio_info ad7606_info_os = {
+ .driver_module = THIS_MODULE,
+ .read_raw = &ad7606_read_raw,
+ .attrs = &ad7606_attribute_group_os,
+};
+
+static const struct iio_info ad7606_info_range = {
+ .driver_module = THIS_MODULE,
+ .read_raw = &ad7606_read_raw,
+ .attrs = &ad7606_attribute_group_range,
+};
+
+struct iio_dev *ad7606_probe(struct device *dev, int irq,
+ void __iomem *base_address,
+ unsigned id,
+ const struct ad7606_bus_ops *bops)
+{
+ struct ad7606_platform_data *pdata = dev->platform_data;
+ struct ad7606_state *st;
+ int ret;
+ struct iio_dev *indio_dev;
+
+ indio_dev = devm_iio_device_alloc(dev, sizeof(*st));
+ if (!indio_dev)
+ return ERR_PTR(-ENOMEM);
+
+ st = iio_priv(indio_dev);
+
+ st->dev = dev;
+ st->bops = bops;
+ st->base_address = base_address;
+ st->range = pdata->default_range == 10000 ? 10000 : 5000;
+
+ ret = ad7606_oversampling_get_index(pdata->default_os);
+ if (ret < 0) {
+ dev_warn(dev, "oversampling %d is not supported\n",
+ pdata->default_os);
+ st->oversampling = 0;
+ } else {
+ st->oversampling = pdata->default_os;
+ }
+
+ st->reg = devm_regulator_get(dev, "vcc");
+ if (!IS_ERR(st->reg)) {
+ ret = regulator_enable(st->reg);
+ if (ret)
+ return ERR_PTR(ret);
+ }
+
+ st->pdata = pdata;
+ st->chip_info = &ad7606_chip_info_tbl[id];
+
+ indio_dev->dev.parent = dev;
+ if (gpio_is_valid(st->pdata->gpio_os0) &&
+ gpio_is_valid(st->pdata->gpio_os1) &&
+ gpio_is_valid(st->pdata->gpio_os2)) {
+ if (gpio_is_valid(st->pdata->gpio_range))
+ indio_dev->info = &ad7606_info_os_and_range;
+ else
+ indio_dev->info = &ad7606_info_os;
+ } else {
+ if (gpio_is_valid(st->pdata->gpio_range))
+ indio_dev->info = &ad7606_info_range;
+ else
+ indio_dev->info = &ad7606_info_no_os_or_range;
+ }
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->name = st->chip_info->name;
+ indio_dev->channels = st->chip_info->channels;
+ indio_dev->num_channels = st->chip_info->num_channels;
+
+ init_waitqueue_head(&st->wq_data_avail);
+
+ ret = ad7606_request_gpios(st);
+ if (ret)
+ goto error_disable_reg;
+
+ ret = ad7606_reset(st);
+ if (ret)
+ dev_warn(st->dev, "failed to RESET: no RESET GPIO specified\n");
+
+ ret = request_irq(irq, ad7606_interrupt,
+ IRQF_TRIGGER_FALLING, st->chip_info->name, indio_dev);
+ if (ret)
+ goto error_free_gpios;
+
+ ret = ad7606_register_ring_funcs_and_init(indio_dev);
+ if (ret)
+ goto error_free_irq;
+
+ ret = iio_device_register(indio_dev);
+ if (ret)
+ goto error_unregister_ring;
+
+ return indio_dev;
+error_unregister_ring:
+ ad7606_ring_cleanup(indio_dev);
+
+error_free_irq:
+ free_irq(irq, indio_dev);
+
+error_free_gpios:
+ ad7606_free_gpios(st);
+
+error_disable_reg:
+ if (!IS_ERR(st->reg))
+ regulator_disable(st->reg);
+ return ERR_PTR(ret);
+}
+
+int ad7606_remove(struct iio_dev *indio_dev, int irq)
+{
+ struct ad7606_state *st = iio_priv(indio_dev);
+
+ iio_device_unregister(indio_dev);
+ ad7606_ring_cleanup(indio_dev);
+
+ free_irq(irq, indio_dev);
+ if (!IS_ERR(st->reg))
+ regulator_disable(st->reg);
+
+ ad7606_free_gpios(st);
+
+ return 0;
+}
+
+void ad7606_suspend(struct iio_dev *indio_dev)
+{
+ struct ad7606_state *st = iio_priv(indio_dev);
+
+ if (gpio_is_valid(st->pdata->gpio_stby)) {
+ if (gpio_is_valid(st->pdata->gpio_range))
+ gpio_set_value(st->pdata->gpio_range, 1);
+ gpio_set_value(st->pdata->gpio_stby, 0);
+ }
+}
+
+void ad7606_resume(struct iio_dev *indio_dev)
+{
+ struct ad7606_state *st = iio_priv(indio_dev);
+
+ if (gpio_is_valid(st->pdata->gpio_stby)) {
+ if (gpio_is_valid(st->pdata->gpio_range))
+ gpio_set_value(st->pdata->gpio_range,
+ st->range == 10000);
+
+ gpio_set_value(st->pdata->gpio_stby, 1);
+ ad7606_reset(st);
+ }
+}
+
+MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
+MODULE_DESCRIPTION("Analog Devices AD7606 ADC");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/staging/iio/adc/ad7606_par.c b/drivers/staging/iio/adc/ad7606_par.c
new file mode 100644
index 000000000..9e24b4d44
--- /dev/null
+++ b/drivers/staging/iio/adc/ad7606_par.c
@@ -0,0 +1,152 @@
+/*
+ * AD7606 Parallel Interface ADC driver
+ *
+ * Copyright 2011 Analog Devices Inc.
+ *
+ * Licensed under the GPL-2.
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/types.h>
+#include <linux/err.h>
+#include <linux/io.h>
+
+#include <linux/iio/iio.h>
+#include "ad7606.h"
+
+static int ad7606_par16_read_block(struct device *dev,
+ int count, void *buf)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct iio_dev *indio_dev = platform_get_drvdata(pdev);
+ struct ad7606_state *st = iio_priv(indio_dev);
+
+ insw((unsigned long) st->base_address, buf, count);
+
+ return 0;
+}
+
+static const struct ad7606_bus_ops ad7606_par16_bops = {
+ .read_block = ad7606_par16_read_block,
+};
+
+static int ad7606_par8_read_block(struct device *dev,
+ int count, void *buf)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct iio_dev *indio_dev = platform_get_drvdata(pdev);
+ struct ad7606_state *st = iio_priv(indio_dev);
+
+ insb((unsigned long) st->base_address, buf, count * 2);
+
+ return 0;
+}
+
+static const struct ad7606_bus_ops ad7606_par8_bops = {
+ .read_block = ad7606_par8_read_block,
+};
+
+static int ad7606_par_probe(struct platform_device *pdev)
+{
+ struct resource *res;
+ struct iio_dev *indio_dev;
+ void __iomem *addr;
+ resource_size_t remap_size;
+ int irq;
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0) {
+ dev_err(&pdev->dev, "no irq\n");
+ return -ENODEV;
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ addr = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(addr))
+ return PTR_ERR(addr);
+
+ remap_size = resource_size(res);
+
+ indio_dev = ad7606_probe(&pdev->dev, irq, addr,
+ platform_get_device_id(pdev)->driver_data,
+ remap_size > 1 ? &ad7606_par16_bops :
+ &ad7606_par8_bops);
+
+ if (IS_ERR(indio_dev))
+ return PTR_ERR(indio_dev);
+
+ platform_set_drvdata(pdev, indio_dev);
+
+ return 0;
+}
+
+static int ad7606_par_remove(struct platform_device *pdev)
+{
+ struct iio_dev *indio_dev = platform_get_drvdata(pdev);
+
+ ad7606_remove(indio_dev, platform_get_irq(pdev, 0));
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int ad7606_par_suspend(struct device *dev)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+
+ ad7606_suspend(indio_dev);
+
+ return 0;
+}
+
+static int ad7606_par_resume(struct device *dev)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+
+ ad7606_resume(indio_dev);
+
+ return 0;
+}
+
+static const struct dev_pm_ops ad7606_pm_ops = {
+ .suspend = ad7606_par_suspend,
+ .resume = ad7606_par_resume,
+};
+#define AD7606_PAR_PM_OPS (&ad7606_pm_ops)
+
+#else
+#define AD7606_PAR_PM_OPS NULL
+#endif /* CONFIG_PM */
+
+static struct platform_device_id ad7606_driver_ids[] = {
+ {
+ .name = "ad7606-8",
+ .driver_data = ID_AD7606_8,
+ }, {
+ .name = "ad7606-6",
+ .driver_data = ID_AD7606_6,
+ }, {
+ .name = "ad7606-4",
+ .driver_data = ID_AD7606_4,
+ },
+ { }
+};
+
+MODULE_DEVICE_TABLE(platform, ad7606_driver_ids);
+
+static struct platform_driver ad7606_driver = {
+ .probe = ad7606_par_probe,
+ .remove = ad7606_par_remove,
+ .id_table = ad7606_driver_ids,
+ .driver = {
+ .name = "ad7606",
+ .pm = AD7606_PAR_PM_OPS,
+ },
+};
+
+module_platform_driver(ad7606_driver);
+
+MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
+MODULE_DESCRIPTION("Analog Devices AD7606 ADC");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/staging/iio/adc/ad7606_ring.c b/drivers/staging/iio/adc/ad7606_ring.c
new file mode 100644
index 000000000..a6f8eb112
--- /dev/null
+++ b/drivers/staging/iio/adc/ad7606_ring.c
@@ -0,0 +1,101 @@
+/*
+ * Copyright 2011-2012 Analog Devices Inc.
+ *
+ * Licensed under the GPL-2.
+ *
+ */
+
+#include <linux/interrupt.h>
+#include <linux/gpio.h>
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/triggered_buffer.h>
+
+#include "ad7606.h"
+
+/**
+ * ad7606_trigger_handler_th() th/bh of trigger launched polling to ring buffer
+ *
+ **/
+static irqreturn_t ad7606_trigger_handler_th_bh(int irq, void *p)
+{
+ struct iio_poll_func *pf = p;
+ struct ad7606_state *st = iio_priv(pf->indio_dev);
+
+ gpio_set_value(st->pdata->gpio_convst, 1);
+
+ return IRQ_HANDLED;
+}
+
+/**
+ * ad7606_poll_bh_to_ring() bh of trigger launched polling to ring buffer
+ * @work_s: the work struct through which this was scheduled
+ *
+ * Currently there is no option in this driver to disable the saving of
+ * timestamps within the ring.
+ * I think the one copy of this at a time was to avoid problems if the
+ * trigger was set far too high and the reads then locked up the computer.
+ **/
+static void ad7606_poll_bh_to_ring(struct work_struct *work_s)
+{
+ struct ad7606_state *st = container_of(work_s, struct ad7606_state,
+ poll_work);
+ struct iio_dev *indio_dev = iio_priv_to_dev(st);
+ __u8 *buf;
+ int ret;
+
+ buf = kzalloc(indio_dev->scan_bytes, GFP_KERNEL);
+ if (!buf)
+ return;
+
+ if (gpio_is_valid(st->pdata->gpio_frstdata)) {
+ ret = st->bops->read_block(st->dev, 1, buf);
+ if (ret)
+ goto done;
+ if (!gpio_get_value(st->pdata->gpio_frstdata)) {
+ /* This should never happen. However
+ * some signal glitch caused by bad PCB desgin or
+ * electrostatic discharge, could cause an extra read
+ * or clock. This allows recovery.
+ */
+ ad7606_reset(st);
+ goto done;
+ }
+ ret = st->bops->read_block(st->dev,
+ st->chip_info->num_channels - 1, buf + 2);
+ if (ret)
+ goto done;
+ } else {
+ ret = st->bops->read_block(st->dev,
+ st->chip_info->num_channels, buf);
+ if (ret)
+ goto done;
+ }
+
+ iio_push_to_buffers_with_timestamp(indio_dev, buf, iio_get_time_ns());
+done:
+ gpio_set_value(st->pdata->gpio_convst, 0);
+ iio_trigger_notify_done(indio_dev->trig);
+ kfree(buf);
+}
+
+int ad7606_register_ring_funcs_and_init(struct iio_dev *indio_dev)
+{
+ struct ad7606_state *st = iio_priv(indio_dev);
+
+ INIT_WORK(&st->poll_work, &ad7606_poll_bh_to_ring);
+
+ return iio_triggered_buffer_setup(indio_dev,
+ &ad7606_trigger_handler_th_bh, &ad7606_trigger_handler_th_bh,
+ NULL);
+}
+
+void ad7606_ring_cleanup(struct iio_dev *indio_dev)
+{
+ iio_triggered_buffer_cleanup(indio_dev);
+}
diff --git a/drivers/staging/iio/adc/ad7606_spi.c b/drivers/staging/iio/adc/ad7606_spi.c
new file mode 100644
index 000000000..7303983e6
--- /dev/null
+++ b/drivers/staging/iio/adc/ad7606_spi.c
@@ -0,0 +1,116 @@
+/*
+ * AD7606 SPI ADC driver
+ *
+ * Copyright 2011 Analog Devices Inc.
+ *
+ * Licensed under the GPL-2.
+ */
+
+#include <linux/module.h>
+#include <linux/spi/spi.h>
+#include <linux/types.h>
+#include <linux/err.h>
+
+#include <linux/iio/iio.h>
+#include "ad7606.h"
+
+#define MAX_SPI_FREQ_HZ 23500000 /* VDRIVE above 4.75 V */
+
+static int ad7606_spi_read_block(struct device *dev,
+ int count, void *buf)
+{
+ struct spi_device *spi = to_spi_device(dev);
+ int i, ret;
+ unsigned short *data = buf;
+
+ ret = spi_read(spi, buf, count * 2);
+ if (ret < 0) {
+ dev_err(&spi->dev, "SPI read error\n");
+ return ret;
+ }
+
+ for (i = 0; i < count; i++)
+ data[i] = be16_to_cpu(data[i]);
+
+ return 0;
+}
+
+static const struct ad7606_bus_ops ad7606_spi_bops = {
+ .read_block = ad7606_spi_read_block,
+};
+
+static int ad7606_spi_probe(struct spi_device *spi)
+{
+ struct iio_dev *indio_dev;
+
+ indio_dev = ad7606_probe(&spi->dev, spi->irq, NULL,
+ spi_get_device_id(spi)->driver_data,
+ &ad7606_spi_bops);
+
+ if (IS_ERR(indio_dev))
+ return PTR_ERR(indio_dev);
+
+ spi_set_drvdata(spi, indio_dev);
+
+ return 0;
+}
+
+static int ad7606_spi_remove(struct spi_device *spi)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(&spi->dev);
+
+ return ad7606_remove(indio_dev, spi->irq);
+}
+
+#ifdef CONFIG_PM
+static int ad7606_spi_suspend(struct device *dev)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+
+ ad7606_suspend(indio_dev);
+
+ return 0;
+}
+
+static int ad7606_spi_resume(struct device *dev)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+
+ ad7606_resume(indio_dev);
+
+ return 0;
+}
+
+static const struct dev_pm_ops ad7606_pm_ops = {
+ .suspend = ad7606_spi_suspend,
+ .resume = ad7606_spi_resume,
+};
+#define AD7606_SPI_PM_OPS (&ad7606_pm_ops)
+
+#else
+#define AD7606_SPI_PM_OPS NULL
+#endif
+
+static const struct spi_device_id ad7606_id[] = {
+ {"ad7606-8", ID_AD7606_8},
+ {"ad7606-6", ID_AD7606_6},
+ {"ad7606-4", ID_AD7606_4},
+ {}
+};
+MODULE_DEVICE_TABLE(spi, ad7606_id);
+
+static struct spi_driver ad7606_driver = {
+ .driver = {
+ .name = "ad7606",
+ .owner = THIS_MODULE,
+ .pm = AD7606_SPI_PM_OPS,
+ },
+ .probe = ad7606_spi_probe,
+ .remove = ad7606_spi_remove,
+ .id_table = ad7606_id,
+};
+module_spi_driver(ad7606_driver);
+
+MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
+MODULE_DESCRIPTION("Analog Devices AD7606 ADC");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/staging/iio/adc/ad7780.c b/drivers/staging/iio/adc/ad7780.c
new file mode 100644
index 000000000..9f03fe3ee
--- /dev/null
+++ b/drivers/staging/iio/adc/ad7780.c
@@ -0,0 +1,276 @@
+/*
+ * AD7170/AD7171 and AD7780/AD7781 SPI ADC driver
+ *
+ * Copyright 2011 Analog Devices Inc.
+ *
+ * Licensed under the GPL-2.
+ */
+
+#include <linux/interrupt.h>
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/sysfs.h>
+#include <linux/spi/spi.h>
+#include <linux/regulator/consumer.h>
+#include <linux/err.h>
+#include <linux/sched.h>
+#include <linux/gpio.h>
+#include <linux/module.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/iio/adc/ad_sigma_delta.h>
+
+#include "ad7780.h"
+
+#define AD7780_RDY BIT(7)
+#define AD7780_FILTER BIT(6)
+#define AD7780_ERR BIT(5)
+#define AD7780_ID1 BIT(4)
+#define AD7780_ID0 BIT(3)
+#define AD7780_GAIN BIT(2)
+#define AD7780_PAT1 BIT(1)
+#define AD7780_PAT0 BIT(0)
+
+struct ad7780_chip_info {
+ struct iio_chan_spec channel;
+ unsigned int pattern_mask;
+ unsigned int pattern;
+};
+
+struct ad7780_state {
+ const struct ad7780_chip_info *chip_info;
+ struct regulator *reg;
+ int powerdown_gpio;
+ unsigned int gain;
+ u16 int_vref_mv;
+
+ struct ad_sigma_delta sd;
+};
+
+enum ad7780_supported_device_ids {
+ ID_AD7170,
+ ID_AD7171,
+ ID_AD7780,
+ ID_AD7781,
+};
+
+static struct ad7780_state *ad_sigma_delta_to_ad7780(struct ad_sigma_delta *sd)
+{
+ return container_of(sd, struct ad7780_state, sd);
+}
+
+static int ad7780_set_mode(struct ad_sigma_delta *sigma_delta,
+ enum ad_sigma_delta_mode mode)
+{
+ struct ad7780_state *st = ad_sigma_delta_to_ad7780(sigma_delta);
+ unsigned val;
+
+ switch (mode) {
+ case AD_SD_MODE_SINGLE:
+ case AD_SD_MODE_CONTINUOUS:
+ val = 1;
+ break;
+ default:
+ val = 0;
+ break;
+ }
+
+ if (gpio_is_valid(st->powerdown_gpio))
+ gpio_set_value(st->powerdown_gpio, val);
+
+ return 0;
+}
+
+static int ad7780_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val,
+ int *val2,
+ long m)
+{
+ struct ad7780_state *st = iio_priv(indio_dev);
+
+ switch (m) {
+ case IIO_CHAN_INFO_RAW:
+ return ad_sigma_delta_single_conversion(indio_dev, chan, val);
+ case IIO_CHAN_INFO_SCALE:
+ *val = st->int_vref_mv * st->gain;
+ *val2 = chan->scan_type.realbits - 1;
+ return IIO_VAL_FRACTIONAL_LOG2;
+ case IIO_CHAN_INFO_OFFSET:
+ *val -= (1 << (chan->scan_type.realbits - 1));
+ return IIO_VAL_INT;
+ }
+
+ return -EINVAL;
+}
+
+static int ad7780_postprocess_sample(struct ad_sigma_delta *sigma_delta,
+ unsigned int raw_sample)
+{
+ struct ad7780_state *st = ad_sigma_delta_to_ad7780(sigma_delta);
+ const struct ad7780_chip_info *chip_info = st->chip_info;
+
+ if ((raw_sample & AD7780_ERR) ||
+ ((raw_sample & chip_info->pattern_mask) != chip_info->pattern))
+ return -EIO;
+
+ if (raw_sample & AD7780_GAIN)
+ st->gain = 1;
+ else
+ st->gain = 128;
+
+ return 0;
+}
+
+static const struct ad_sigma_delta_info ad7780_sigma_delta_info = {
+ .set_mode = ad7780_set_mode,
+ .postprocess_sample = ad7780_postprocess_sample,
+ .has_registers = false,
+};
+
+#define AD7780_CHANNEL(bits, wordsize) \
+ AD_SD_CHANNEL(1, 0, 0, bits, 32, wordsize - bits)
+
+static const struct ad7780_chip_info ad7780_chip_info_tbl[] = {
+ [ID_AD7170] = {
+ .channel = AD7780_CHANNEL(12, 24),
+ .pattern = 0x5,
+ .pattern_mask = 0x7,
+ },
+ [ID_AD7171] = {
+ .channel = AD7780_CHANNEL(16, 24),
+ .pattern = 0x5,
+ .pattern_mask = 0x7,
+ },
+ [ID_AD7780] = {
+ .channel = AD7780_CHANNEL(24, 32),
+ .pattern = 0x1,
+ .pattern_mask = 0x3,
+ },
+ [ID_AD7781] = {
+ .channel = AD7780_CHANNEL(20, 32),
+ .pattern = 0x1,
+ .pattern_mask = 0x3,
+ },
+};
+
+static const struct iio_info ad7780_info = {
+ .read_raw = &ad7780_read_raw,
+ .driver_module = THIS_MODULE,
+};
+
+static int ad7780_probe(struct spi_device *spi)
+{
+ struct ad7780_platform_data *pdata = spi->dev.platform_data;
+ struct ad7780_state *st;
+ struct iio_dev *indio_dev;
+ int ret, voltage_uv = 0;
+
+ indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ st = iio_priv(indio_dev);
+ st->gain = 1;
+
+ ad_sd_init(&st->sd, indio_dev, spi, &ad7780_sigma_delta_info);
+
+ st->reg = devm_regulator_get(&spi->dev, "vcc");
+ if (!IS_ERR(st->reg)) {
+ ret = regulator_enable(st->reg);
+ if (ret)
+ return ret;
+
+ voltage_uv = regulator_get_voltage(st->reg);
+ }
+
+ st->chip_info =
+ &ad7780_chip_info_tbl[spi_get_device_id(spi)->driver_data];
+
+ if (pdata && pdata->vref_mv)
+ st->int_vref_mv = pdata->vref_mv;
+ else if (voltage_uv)
+ st->int_vref_mv = voltage_uv / 1000;
+ else
+ dev_warn(&spi->dev, "reference voltage unspecified\n");
+
+ spi_set_drvdata(spi, indio_dev);
+
+ indio_dev->dev.parent = &spi->dev;
+ indio_dev->name = spi_get_device_id(spi)->name;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->channels = &st->chip_info->channel;
+ indio_dev->num_channels = 1;
+ indio_dev->info = &ad7780_info;
+
+ if (pdata && gpio_is_valid(pdata->gpio_pdrst)) {
+
+ ret = devm_gpio_request_one(&spi->dev, pdata->gpio_pdrst,
+ GPIOF_OUT_INIT_LOW, "AD7780 /PDRST");
+ if (ret) {
+ dev_err(&spi->dev, "failed to request GPIO PDRST\n");
+ goto error_disable_reg;
+ }
+ st->powerdown_gpio = pdata->gpio_pdrst;
+ } else {
+ st->powerdown_gpio = -1;
+ }
+
+ ret = ad_sd_setup_buffer_and_trigger(indio_dev);
+ if (ret)
+ goto error_disable_reg;
+
+ ret = iio_device_register(indio_dev);
+ if (ret)
+ goto error_cleanup_buffer_and_trigger;
+
+ return 0;
+
+error_cleanup_buffer_and_trigger:
+ ad_sd_cleanup_buffer_and_trigger(indio_dev);
+error_disable_reg:
+ if (!IS_ERR(st->reg))
+ regulator_disable(st->reg);
+
+ return ret;
+}
+
+static int ad7780_remove(struct spi_device *spi)
+{
+ struct iio_dev *indio_dev = spi_get_drvdata(spi);
+ struct ad7780_state *st = iio_priv(indio_dev);
+
+ iio_device_unregister(indio_dev);
+ ad_sd_cleanup_buffer_and_trigger(indio_dev);
+
+ if (!IS_ERR(st->reg))
+ regulator_disable(st->reg);
+
+ return 0;
+}
+
+static const struct spi_device_id ad7780_id[] = {
+ {"ad7170", ID_AD7170},
+ {"ad7171", ID_AD7171},
+ {"ad7780", ID_AD7780},
+ {"ad7781", ID_AD7781},
+ {}
+};
+MODULE_DEVICE_TABLE(spi, ad7780_id);
+
+static struct spi_driver ad7780_driver = {
+ .driver = {
+ .name = "ad7780",
+ .owner = THIS_MODULE,
+ },
+ .probe = ad7780_probe,
+ .remove = ad7780_remove,
+ .id_table = ad7780_id,
+};
+module_spi_driver(ad7780_driver);
+
+MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
+MODULE_DESCRIPTION("Analog Devices AD7780 and similar ADCs");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/staging/iio/adc/ad7780.h b/drivers/staging/iio/adc/ad7780.h
new file mode 100644
index 000000000..67e511c3d
--- /dev/null
+++ b/drivers/staging/iio/adc/ad7780.h
@@ -0,0 +1,30 @@
+/*
+ * AD7780/AD7781 SPI ADC driver
+ *
+ * Copyright 2011 Analog Devices Inc.
+ *
+ * Licensed under the GPL-2.
+ */
+#ifndef IIO_ADC_AD7780_H_
+#define IIO_ADC_AD7780_H_
+
+/*
+ * TODO: struct ad7780_platform_data needs to go into include/linux/iio
+ */
+
+/* NOTE:
+ * The AD7780 doesn't feature a dedicated SPI chip select, in addition it
+ * features a dual use data out ready DOUT/RDY output.
+ * In order to avoid contentions on the SPI bus, it's therefore necessary
+ * to use spi bus locking combined with a dedicated GPIO to control the
+ * power down reset signal of the AD7780.
+ *
+ * The DOUT/RDY output must also be wired to an interrupt capable GPIO.
+ */
+
+struct ad7780_platform_data {
+ u16 vref_mv;
+ int gpio_pdrst;
+};
+
+#endif /* IIO_ADC_AD7780_H_ */
diff --git a/drivers/staging/iio/adc/ad7816.c b/drivers/staging/iio/adc/ad7816.c
new file mode 100644
index 000000000..48b1c3740
--- /dev/null
+++ b/drivers/staging/iio/adc/ad7816.c
@@ -0,0 +1,446 @@
+/*
+ * AD7816 digital temperature sensor driver supporting AD7816/7/8
+ *
+ * Copyright 2010 Analog Devices Inc.
+ *
+ * Licensed under the GPL-2 or later.
+ */
+
+#include <linux/interrupt.h>
+#include <linux/gpio.h>
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/sysfs.h>
+#include <linux/list.h>
+#include <linux/spi/spi.h>
+#include <linux/module.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/iio/events.h>
+
+/*
+ * AD7816 config masks
+ */
+#define AD7816_FULL 0x1
+#define AD7816_PD 0x2
+#define AD7816_CS_MASK 0x7
+#define AD7816_CS_MAX 0x4
+
+/*
+ * AD7816 temperature masks
+ */
+#define AD7816_VALUE_OFFSET 6
+#define AD7816_BOUND_VALUE_BASE 0x8
+#define AD7816_BOUND_VALUE_MIN -95
+#define AD7816_BOUND_VALUE_MAX 152
+#define AD7816_TEMP_FLOAT_OFFSET 2
+#define AD7816_TEMP_FLOAT_MASK 0x3
+
+
+/*
+ * struct ad7816_chip_info - chip specific information
+ */
+
+struct ad7816_chip_info {
+ struct spi_device *spi_dev;
+ u16 rdwr_pin;
+ u16 convert_pin;
+ u16 busy_pin;
+ u8 oti_data[AD7816_CS_MAX+1];
+ u8 channel_id; /* 0 always be temperature */
+ u8 mode;
+};
+
+/*
+ * ad7816 data access by SPI
+ */
+static int ad7816_spi_read(struct ad7816_chip_info *chip, u16 *data)
+{
+ struct spi_device *spi_dev = chip->spi_dev;
+ int ret = 0;
+
+ gpio_set_value(chip->rdwr_pin, 1);
+ gpio_set_value(chip->rdwr_pin, 0);
+ ret = spi_write(spi_dev, &chip->channel_id, sizeof(chip->channel_id));
+ if (ret < 0) {
+ dev_err(&spi_dev->dev, "SPI channel setting error\n");
+ return ret;
+ }
+ gpio_set_value(chip->rdwr_pin, 1);
+
+
+ if (chip->mode == AD7816_PD) { /* operating mode 2 */
+ gpio_set_value(chip->convert_pin, 1);
+ gpio_set_value(chip->convert_pin, 0);
+ } else { /* operating mode 1 */
+ gpio_set_value(chip->convert_pin, 0);
+ gpio_set_value(chip->convert_pin, 1);
+ }
+
+ while (gpio_get_value(chip->busy_pin))
+ cpu_relax();
+
+ gpio_set_value(chip->rdwr_pin, 0);
+ gpio_set_value(chip->rdwr_pin, 1);
+ ret = spi_read(spi_dev, (u8 *)data, sizeof(*data));
+ if (ret < 0) {
+ dev_err(&spi_dev->dev, "SPI data read error\n");
+ return ret;
+ }
+
+ *data = be16_to_cpu(*data);
+
+ return ret;
+}
+
+static int ad7816_spi_write(struct ad7816_chip_info *chip, u8 data)
+{
+ struct spi_device *spi_dev = chip->spi_dev;
+ int ret = 0;
+
+ gpio_set_value(chip->rdwr_pin, 1);
+ gpio_set_value(chip->rdwr_pin, 0);
+ ret = spi_write(spi_dev, &data, sizeof(data));
+ if (ret < 0)
+ dev_err(&spi_dev->dev, "SPI oti data write error\n");
+
+ return ret;
+}
+
+static ssize_t ad7816_show_mode(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct ad7816_chip_info *chip = iio_priv(indio_dev);
+
+ if (chip->mode)
+ return sprintf(buf, "power-save\n");
+ return sprintf(buf, "full\n");
+}
+
+static ssize_t ad7816_store_mode(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t len)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct ad7816_chip_info *chip = iio_priv(indio_dev);
+
+ if (strcmp(buf, "full")) {
+ gpio_set_value(chip->rdwr_pin, 1);
+ chip->mode = AD7816_FULL;
+ } else {
+ gpio_set_value(chip->rdwr_pin, 0);
+ chip->mode = AD7816_PD;
+ }
+
+ return len;
+}
+
+static IIO_DEVICE_ATTR(mode, S_IRUGO | S_IWUSR,
+ ad7816_show_mode,
+ ad7816_store_mode,
+ 0);
+
+static ssize_t ad7816_show_available_modes(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ return sprintf(buf, "full\npower-save\n");
+}
+
+static IIO_DEVICE_ATTR(available_modes, S_IRUGO, ad7816_show_available_modes,
+ NULL, 0);
+
+static ssize_t ad7816_show_channel(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct ad7816_chip_info *chip = iio_priv(indio_dev);
+
+ return sprintf(buf, "%d\n", chip->channel_id);
+}
+
+static ssize_t ad7816_store_channel(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t len)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct ad7816_chip_info *chip = iio_priv(indio_dev);
+ unsigned long data;
+ int ret;
+
+ ret = kstrtoul(buf, 10, &data);
+ if (ret)
+ return ret;
+
+ if (data > AD7816_CS_MAX && data != AD7816_CS_MASK) {
+ dev_err(&chip->spi_dev->dev, "Invalid channel id %lu for %s.\n",
+ data, indio_dev->name);
+ return -EINVAL;
+ } else if (strcmp(indio_dev->name, "ad7818") == 0 && data > 1) {
+ dev_err(&chip->spi_dev->dev,
+ "Invalid channel id %lu for ad7818.\n", data);
+ return -EINVAL;
+ } else if (strcmp(indio_dev->name, "ad7816") == 0 && data > 0) {
+ dev_err(&chip->spi_dev->dev,
+ "Invalid channel id %lu for ad7816.\n", data);
+ return -EINVAL;
+ }
+
+ chip->channel_id = data;
+
+ return len;
+}
+
+static IIO_DEVICE_ATTR(channel, S_IRUGO | S_IWUSR,
+ ad7816_show_channel,
+ ad7816_store_channel,
+ 0);
+
+
+static ssize_t ad7816_show_value(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct ad7816_chip_info *chip = iio_priv(indio_dev);
+ u16 data;
+ s8 value;
+ int ret;
+
+ ret = ad7816_spi_read(chip, &data);
+ if (ret)
+ return -EIO;
+
+ data >>= AD7816_VALUE_OFFSET;
+
+ if (chip->channel_id == 0) {
+ value = (s8)((data >> AD7816_TEMP_FLOAT_OFFSET) - 103);
+ data &= AD7816_TEMP_FLOAT_MASK;
+ if (value < 0)
+ data = (1 << AD7816_TEMP_FLOAT_OFFSET) - data;
+ return sprintf(buf, "%d.%.2d\n", value, data * 25);
+ }
+ return sprintf(buf, "%u\n", data);
+}
+
+static IIO_DEVICE_ATTR(value, S_IRUGO, ad7816_show_value, NULL, 0);
+
+static struct attribute *ad7816_attributes[] = {
+ &iio_dev_attr_available_modes.dev_attr.attr,
+ &iio_dev_attr_mode.dev_attr.attr,
+ &iio_dev_attr_channel.dev_attr.attr,
+ &iio_dev_attr_value.dev_attr.attr,
+ NULL,
+};
+
+static const struct attribute_group ad7816_attribute_group = {
+ .attrs = ad7816_attributes,
+};
+
+/*
+ * temperature bound events
+ */
+
+#define IIO_EVENT_CODE_AD7816_OTI IIO_UNMOD_EVENT_CODE(IIO_TEMP, \
+ 0, \
+ IIO_EV_TYPE_THRESH, \
+ IIO_EV_DIR_FALLING)
+
+static irqreturn_t ad7816_event_handler(int irq, void *private)
+{
+ iio_push_event(private, IIO_EVENT_CODE_AD7816_OTI, iio_get_time_ns());
+ return IRQ_HANDLED;
+}
+
+static ssize_t ad7816_show_oti(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct ad7816_chip_info *chip = iio_priv(indio_dev);
+ int value;
+
+ if (chip->channel_id > AD7816_CS_MAX) {
+ dev_err(dev, "Invalid oti channel id %d.\n", chip->channel_id);
+ return -EINVAL;
+ } else if (chip->channel_id == 0) {
+ value = AD7816_BOUND_VALUE_MIN +
+ (chip->oti_data[chip->channel_id] -
+ AD7816_BOUND_VALUE_BASE);
+ return sprintf(buf, "%d\n", value);
+ }
+ return sprintf(buf, "%u\n", chip->oti_data[chip->channel_id]);
+}
+
+static inline ssize_t ad7816_set_oti(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t len)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct ad7816_chip_info *chip = iio_priv(indio_dev);
+ long value;
+ u8 data;
+ int ret;
+
+ ret = kstrtol(buf, 10, &value);
+ if (ret)
+ return ret;
+
+ if (chip->channel_id > AD7816_CS_MAX) {
+ dev_err(dev, "Invalid oti channel id %d.\n", chip->channel_id);
+ return -EINVAL;
+ } else if (chip->channel_id == 0) {
+ if (ret || value < AD7816_BOUND_VALUE_MIN ||
+ value > AD7816_BOUND_VALUE_MAX)
+ return -EINVAL;
+
+ data = (u8)(value - AD7816_BOUND_VALUE_MIN +
+ AD7816_BOUND_VALUE_BASE);
+ } else {
+ if (ret || value < AD7816_BOUND_VALUE_BASE || value > 255)
+ return -EINVAL;
+
+ data = (u8)value;
+ }
+
+ ret = ad7816_spi_write(chip, data);
+ if (ret)
+ return -EIO;
+
+ chip->oti_data[chip->channel_id] = data;
+
+ return len;
+}
+
+static IIO_DEVICE_ATTR(oti, S_IRUGO | S_IWUSR,
+ ad7816_show_oti, ad7816_set_oti, 0);
+
+static struct attribute *ad7816_event_attributes[] = {
+ &iio_dev_attr_oti.dev_attr.attr,
+ NULL,
+};
+
+static struct attribute_group ad7816_event_attribute_group = {
+ .attrs = ad7816_event_attributes,
+ .name = "events",
+};
+
+static const struct iio_info ad7816_info = {
+ .attrs = &ad7816_attribute_group,
+ .event_attrs = &ad7816_event_attribute_group,
+ .driver_module = THIS_MODULE,
+};
+
+/*
+ * device probe and remove
+ */
+
+static int ad7816_probe(struct spi_device *spi_dev)
+{
+ struct ad7816_chip_info *chip;
+ struct iio_dev *indio_dev;
+ unsigned short *pins = spi_dev->dev.platform_data;
+ int ret = 0;
+ int i;
+
+ if (!pins) {
+ dev_err(&spi_dev->dev, "No necessary GPIO platform data.\n");
+ return -EINVAL;
+ }
+
+ indio_dev = devm_iio_device_alloc(&spi_dev->dev, sizeof(*chip));
+ if (!indio_dev)
+ return -ENOMEM;
+ chip = iio_priv(indio_dev);
+ /* this is only used for device removal purposes */
+ dev_set_drvdata(&spi_dev->dev, indio_dev);
+
+ chip->spi_dev = spi_dev;
+ for (i = 0; i <= AD7816_CS_MAX; i++)
+ chip->oti_data[i] = 203;
+ chip->rdwr_pin = pins[0];
+ chip->convert_pin = pins[1];
+ chip->busy_pin = pins[2];
+
+ ret = devm_gpio_request(&spi_dev->dev, chip->rdwr_pin,
+ spi_get_device_id(spi_dev)->name);
+ if (ret) {
+ dev_err(&spi_dev->dev, "Fail to request rdwr gpio PIN %d.\n",
+ chip->rdwr_pin);
+ return ret;
+ }
+ gpio_direction_input(chip->rdwr_pin);
+ ret = devm_gpio_request(&spi_dev->dev, chip->convert_pin,
+ spi_get_device_id(spi_dev)->name);
+ if (ret) {
+ dev_err(&spi_dev->dev, "Fail to request convert gpio PIN %d.\n",
+ chip->convert_pin);
+ return ret;
+ }
+ gpio_direction_input(chip->convert_pin);
+ ret = devm_gpio_request(&spi_dev->dev, chip->busy_pin,
+ spi_get_device_id(spi_dev)->name);
+ if (ret) {
+ dev_err(&spi_dev->dev, "Fail to request busy gpio PIN %d.\n",
+ chip->busy_pin);
+ return ret;
+ }
+ gpio_direction_input(chip->busy_pin);
+
+ indio_dev->name = spi_get_device_id(spi_dev)->name;
+ indio_dev->dev.parent = &spi_dev->dev;
+ indio_dev->info = &ad7816_info;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+
+ if (spi_dev->irq) {
+ /* Only low trigger is supported in ad7816/7/8 */
+ ret = devm_request_threaded_irq(&spi_dev->dev, spi_dev->irq,
+ NULL,
+ &ad7816_event_handler,
+ IRQF_TRIGGER_LOW | IRQF_ONESHOT,
+ indio_dev->name,
+ indio_dev);
+ if (ret)
+ return ret;
+ }
+
+ ret = devm_iio_device_register(&spi_dev->dev, indio_dev);
+ if (ret)
+ return ret;
+
+ dev_info(&spi_dev->dev, "%s temperature sensor and ADC registered.\n",
+ indio_dev->name);
+
+ return 0;
+}
+
+static const struct spi_device_id ad7816_id[] = {
+ { "ad7816", 0 },
+ { "ad7817", 0 },
+ { "ad7818", 0 },
+ {}
+};
+
+MODULE_DEVICE_TABLE(spi, ad7816_id);
+
+static struct spi_driver ad7816_driver = {
+ .driver = {
+ .name = "ad7816",
+ .owner = THIS_MODULE,
+ },
+ .probe = ad7816_probe,
+ .id_table = ad7816_id,
+};
+module_spi_driver(ad7816_driver);
+
+MODULE_AUTHOR("Sonic Zhang <sonic.zhang@analog.com>");
+MODULE_DESCRIPTION("Analog Devices AD7816/7/8 digital temperature sensor driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/staging/iio/adc/lpc32xx_adc.c b/drivers/staging/iio/adc/lpc32xx_adc.c
new file mode 100644
index 000000000..5331c442f
--- /dev/null
+++ b/drivers/staging/iio/adc/lpc32xx_adc.c
@@ -0,0 +1,215 @@
+/*
+ * lpc32xx_adc.c - Support for ADC in LPC32XX
+ *
+ * 3-channel, 10-bit ADC
+ *
+ * Copyright (C) 2011, 2012 Roland Stigge <stigge@antcom.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/io.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/completion.h>
+#include <linux/of.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+
+/*
+ * LPC32XX registers definitions
+ */
+#define LPC32XX_ADC_SELECT(x) ((x) + 0x04)
+#define LPC32XX_ADC_CTRL(x) ((x) + 0x08)
+#define LPC32XX_ADC_VALUE(x) ((x) + 0x48)
+
+/* Bit definitions for LPC32XX_ADC_SELECT: */
+#define AD_REFm 0x00000200 /* constant, always write this value! */
+#define AD_REFp 0x00000080 /* constant, always write this value! */
+#define AD_IN 0x00000010 /* multiple of this is the */
+ /* channel number: 0, 1, 2 */
+#define AD_INTERNAL 0x00000004 /* constant, always write this value! */
+
+/* Bit definitions for LPC32XX_ADC_CTRL: */
+#define AD_STROBE 0x00000002
+#define AD_PDN_CTRL 0x00000004
+
+/* Bit definitions for LPC32XX_ADC_VALUE: */
+#define ADC_VALUE_MASK 0x000003FF
+
+#define MOD_NAME "lpc32xx-adc"
+
+struct lpc32xx_adc_info {
+ void __iomem *adc_base;
+ struct clk *clk;
+ struct completion completion;
+
+ u32 value;
+};
+
+static int lpc32xx_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val,
+ int *val2,
+ long mask)
+{
+ struct lpc32xx_adc_info *info = iio_priv(indio_dev);
+
+ if (mask == IIO_CHAN_INFO_RAW) {
+ mutex_lock(&indio_dev->mlock);
+ clk_enable(info->clk);
+ /* Measurement setup */
+ __raw_writel(AD_INTERNAL | (chan->address) | AD_REFp | AD_REFm,
+ LPC32XX_ADC_SELECT(info->adc_base));
+ /* Trigger conversion */
+ __raw_writel(AD_PDN_CTRL | AD_STROBE,
+ LPC32XX_ADC_CTRL(info->adc_base));
+ wait_for_completion(&info->completion); /* set by ISR */
+ clk_disable(info->clk);
+ *val = info->value;
+ mutex_unlock(&indio_dev->mlock);
+
+ return IIO_VAL_INT;
+ }
+
+ return -EINVAL;
+}
+
+static const struct iio_info lpc32xx_adc_iio_info = {
+ .read_raw = &lpc32xx_read_raw,
+ .driver_module = THIS_MODULE,
+};
+
+#define LPC32XX_ADC_CHANNEL(_index) { \
+ .type = IIO_VOLTAGE, \
+ .indexed = 1, \
+ .channel = _index, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
+ .address = AD_IN * _index, \
+ .scan_index = _index, \
+}
+
+static const struct iio_chan_spec lpc32xx_adc_iio_channels[] = {
+ LPC32XX_ADC_CHANNEL(0),
+ LPC32XX_ADC_CHANNEL(1),
+ LPC32XX_ADC_CHANNEL(2),
+};
+
+static irqreturn_t lpc32xx_adc_isr(int irq, void *dev_id)
+{
+ struct lpc32xx_adc_info *info = dev_id;
+
+ /* Read value and clear irq */
+ info->value = __raw_readl(LPC32XX_ADC_VALUE(info->adc_base)) &
+ ADC_VALUE_MASK;
+ complete(&info->completion);
+
+ return IRQ_HANDLED;
+}
+
+static int lpc32xx_adc_probe(struct platform_device *pdev)
+{
+ struct lpc32xx_adc_info *info = NULL;
+ struct resource *res;
+ int retval = -ENODEV;
+ struct iio_dev *iodev = NULL;
+ int irq;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_err(&pdev->dev, "failed to get platform I/O memory\n");
+ return -EBUSY;
+ }
+
+ iodev = devm_iio_device_alloc(&pdev->dev, sizeof(*info));
+ if (!iodev)
+ return -ENOMEM;
+
+ info = iio_priv(iodev);
+
+ info->adc_base = devm_ioremap(&pdev->dev, res->start,
+ resource_size(res));
+ if (!info->adc_base) {
+ dev_err(&pdev->dev, "failed mapping memory\n");
+ return -EBUSY;
+ }
+
+ info->clk = devm_clk_get(&pdev->dev, NULL);
+ if (IS_ERR(info->clk)) {
+ dev_err(&pdev->dev, "failed getting clock\n");
+ return PTR_ERR(info->clk);
+ }
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq <= 0) {
+ dev_err(&pdev->dev, "failed getting interrupt resource\n");
+ return -EINVAL;
+ }
+
+ retval = devm_request_irq(&pdev->dev, irq, lpc32xx_adc_isr, 0,
+ MOD_NAME, info);
+ if (retval < 0) {
+ dev_err(&pdev->dev, "failed requesting interrupt\n");
+ return retval;
+ }
+
+ platform_set_drvdata(pdev, iodev);
+
+ init_completion(&info->completion);
+
+ iodev->name = MOD_NAME;
+ iodev->dev.parent = &pdev->dev;
+ iodev->info = &lpc32xx_adc_iio_info;
+ iodev->modes = INDIO_DIRECT_MODE;
+ iodev->channels = lpc32xx_adc_iio_channels;
+ iodev->num_channels = ARRAY_SIZE(lpc32xx_adc_iio_channels);
+
+ retval = devm_iio_device_register(&pdev->dev, iodev);
+ if (retval)
+ return retval;
+
+ dev_info(&pdev->dev, "LPC32XX ADC driver loaded, IRQ %d\n", irq);
+
+ return 0;
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id lpc32xx_adc_match[] = {
+ { .compatible = "nxp,lpc3220-adc" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, lpc32xx_adc_match);
+#endif
+
+static struct platform_driver lpc32xx_adc_driver = {
+ .probe = lpc32xx_adc_probe,
+ .driver = {
+ .name = MOD_NAME,
+ .of_match_table = of_match_ptr(lpc32xx_adc_match),
+ },
+};
+
+module_platform_driver(lpc32xx_adc_driver);
+
+MODULE_AUTHOR("Roland Stigge <stigge@antcom.de>");
+MODULE_DESCRIPTION("LPC32XX ADC driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/iio/adc/mxs-lradc.c b/drivers/staging/iio/adc/mxs-lradc.c
new file mode 100644
index 000000000..d7c5223f1
--- /dev/null
+++ b/drivers/staging/iio/adc/mxs-lradc.c
@@ -0,0 +1,1712 @@
+/*
+ * Freescale i.MX28 LRADC driver
+ *
+ * Copyright (c) 2012 DENX Software Engineering, GmbH.
+ * Marek Vasut <marex@denx.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.
+ */
+
+#include <linux/err.h>
+#include <linux/interrupt.h>
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/sysfs.h>
+#include <linux/list.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/spinlock.h>
+#include <linux/wait.h>
+#include <linux/sched.h>
+#include <linux/stmp_device.h>
+#include <linux/bitops.h>
+#include <linux/completion.h>
+#include <linux/delay.h>
+#include <linux/input.h>
+#include <linux/clk.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/trigger.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/triggered_buffer.h>
+
+#define DRIVER_NAME "mxs-lradc"
+
+#define LRADC_MAX_DELAY_CHANS 4
+#define LRADC_MAX_MAPPED_CHANS 8
+#define LRADC_MAX_TOTAL_CHANS 16
+
+#define LRADC_DELAY_TIMER_HZ 2000
+
+/*
+ * Make this runtime configurable if necessary. Currently, if the buffered mode
+ * is enabled, the LRADC takes LRADC_DELAY_TIMER_LOOP samples of data before
+ * triggering IRQ. The sampling happens every (LRADC_DELAY_TIMER_PER / 2000)
+ * seconds. The result is that the samples arrive every 500mS.
+ */
+#define LRADC_DELAY_TIMER_PER 200
+#define LRADC_DELAY_TIMER_LOOP 5
+
+/*
+ * Once the pen touches the touchscreen, the touchscreen switches from
+ * IRQ-driven mode to polling mode to prevent interrupt storm. The polling
+ * is realized by worker thread, which is called every 20 or so milliseconds.
+ * This gives the touchscreen enough fluence and does not strain the system
+ * too much.
+ */
+#define LRADC_TS_SAMPLE_DELAY_MS 5
+
+/*
+ * The LRADC reads the following amount of samples from each touchscreen
+ * channel and the driver then computes avarage of these.
+ */
+#define LRADC_TS_SAMPLE_AMOUNT 4
+
+enum mxs_lradc_id {
+ IMX23_LRADC,
+ IMX28_LRADC,
+};
+
+static const char * const mx23_lradc_irq_names[] = {
+ "mxs-lradc-touchscreen",
+ "mxs-lradc-channel0",
+ "mxs-lradc-channel1",
+ "mxs-lradc-channel2",
+ "mxs-lradc-channel3",
+ "mxs-lradc-channel4",
+ "mxs-lradc-channel5",
+ "mxs-lradc-channel6",
+ "mxs-lradc-channel7",
+};
+
+static const char * const mx28_lradc_irq_names[] = {
+ "mxs-lradc-touchscreen",
+ "mxs-lradc-thresh0",
+ "mxs-lradc-thresh1",
+ "mxs-lradc-channel0",
+ "mxs-lradc-channel1",
+ "mxs-lradc-channel2",
+ "mxs-lradc-channel3",
+ "mxs-lradc-channel4",
+ "mxs-lradc-channel5",
+ "mxs-lradc-channel6",
+ "mxs-lradc-channel7",
+ "mxs-lradc-button0",
+ "mxs-lradc-button1",
+};
+
+struct mxs_lradc_of_config {
+ const int irq_count;
+ const char * const *irq_name;
+ const uint32_t *vref_mv;
+};
+
+#define VREF_MV_BASE 1850
+
+static const uint32_t mx23_vref_mv[LRADC_MAX_TOTAL_CHANS] = {
+ VREF_MV_BASE, /* CH0 */
+ VREF_MV_BASE, /* CH1 */
+ VREF_MV_BASE, /* CH2 */
+ VREF_MV_BASE, /* CH3 */
+ VREF_MV_BASE, /* CH4 */
+ VREF_MV_BASE, /* CH5 */
+ VREF_MV_BASE * 2, /* CH6 VDDIO */
+ VREF_MV_BASE * 4, /* CH7 VBATT */
+ VREF_MV_BASE, /* CH8 Temp sense 0 */
+ VREF_MV_BASE, /* CH9 Temp sense 1 */
+ VREF_MV_BASE, /* CH10 */
+ VREF_MV_BASE, /* CH11 */
+ VREF_MV_BASE, /* CH12 USB_DP */
+ VREF_MV_BASE, /* CH13 USB_DN */
+ VREF_MV_BASE, /* CH14 VBG */
+ VREF_MV_BASE * 4, /* CH15 VDD5V */
+};
+
+static const uint32_t mx28_vref_mv[LRADC_MAX_TOTAL_CHANS] = {
+ VREF_MV_BASE, /* CH0 */
+ VREF_MV_BASE, /* CH1 */
+ VREF_MV_BASE, /* CH2 */
+ VREF_MV_BASE, /* CH3 */
+ VREF_MV_BASE, /* CH4 */
+ VREF_MV_BASE, /* CH5 */
+ VREF_MV_BASE, /* CH6 */
+ VREF_MV_BASE * 4, /* CH7 VBATT */
+ VREF_MV_BASE, /* CH8 Temp sense 0 */
+ VREF_MV_BASE, /* CH9 Temp sense 1 */
+ VREF_MV_BASE * 2, /* CH10 VDDIO */
+ VREF_MV_BASE, /* CH11 VTH */
+ VREF_MV_BASE * 2, /* CH12 VDDA */
+ VREF_MV_BASE, /* CH13 VDDD */
+ VREF_MV_BASE, /* CH14 VBG */
+ VREF_MV_BASE * 4, /* CH15 VDD5V */
+};
+
+static const struct mxs_lradc_of_config mxs_lradc_of_config[] = {
+ [IMX23_LRADC] = {
+ .irq_count = ARRAY_SIZE(mx23_lradc_irq_names),
+ .irq_name = mx23_lradc_irq_names,
+ .vref_mv = mx23_vref_mv,
+ },
+ [IMX28_LRADC] = {
+ .irq_count = ARRAY_SIZE(mx28_lradc_irq_names),
+ .irq_name = mx28_lradc_irq_names,
+ .vref_mv = mx28_vref_mv,
+ },
+};
+
+enum mxs_lradc_ts {
+ MXS_LRADC_TOUCHSCREEN_NONE = 0,
+ MXS_LRADC_TOUCHSCREEN_4WIRE,
+ MXS_LRADC_TOUCHSCREEN_5WIRE,
+};
+
+/*
+ * Touchscreen handling
+ */
+enum lradc_ts_plate {
+ LRADC_TOUCH = 0,
+ LRADC_SAMPLE_X,
+ LRADC_SAMPLE_Y,
+ LRADC_SAMPLE_PRESSURE,
+ LRADC_SAMPLE_VALID,
+};
+
+enum mxs_lradc_divbytwo {
+ MXS_LRADC_DIV_DISABLED = 0,
+ MXS_LRADC_DIV_ENABLED,
+};
+
+struct mxs_lradc_scale {
+ unsigned int integer;
+ unsigned int nano;
+};
+
+struct mxs_lradc {
+ struct device *dev;
+ void __iomem *base;
+ int irq[13];
+
+ struct clk *clk;
+
+ uint32_t *buffer;
+ struct iio_trigger *trig;
+
+ struct mutex lock;
+
+ struct completion completion;
+
+ const uint32_t *vref_mv;
+ struct mxs_lradc_scale scale_avail[LRADC_MAX_TOTAL_CHANS][2];
+ unsigned long is_divided;
+
+ /*
+ * When the touchscreen is enabled, we give it two private virtual
+ * channels: #6 and #7. This means that only 6 virtual channels (instead
+ * of 8) will be available for buffered capture.
+ */
+#define TOUCHSCREEN_VCHANNEL1 7
+#define TOUCHSCREEN_VCHANNEL2 6
+#define BUFFER_VCHANS_LIMITED 0x3f
+#define BUFFER_VCHANS_ALL 0xff
+ u8 buffer_vchans;
+
+ /*
+ * Furthermore, certain LRADC channels are shared between touchscreen
+ * and/or touch-buttons and generic LRADC block. Therefore when using
+ * either of these, these channels are not available for the regular
+ * sampling. The shared channels are as follows:
+ *
+ * CH0 -- Touch button #0
+ * CH1 -- Touch button #1
+ * CH2 -- Touch screen XPUL
+ * CH3 -- Touch screen YPLL
+ * CH4 -- Touch screen XNUL
+ * CH5 -- Touch screen YNLR
+ * CH6 -- Touch screen WIPER (5-wire only)
+ *
+ * The bitfields below represents which parts of the LRADC block are
+ * switched into special mode of operation. These channels can not
+ * be sampled as regular LRADC channels. The driver will refuse any
+ * attempt to sample these channels.
+ */
+#define CHAN_MASK_TOUCHBUTTON (BIT(1) | BIT(0))
+#define CHAN_MASK_TOUCHSCREEN_4WIRE (0xf << 2)
+#define CHAN_MASK_TOUCHSCREEN_5WIRE (0x1f << 2)
+ enum mxs_lradc_ts use_touchscreen;
+ bool use_touchbutton;
+
+ struct input_dev *ts_input;
+
+ enum mxs_lradc_id soc;
+ enum lradc_ts_plate cur_plate; /* statemachine */
+ bool ts_valid;
+ unsigned ts_x_pos;
+ unsigned ts_y_pos;
+ unsigned ts_pressure;
+
+ /* handle touchscreen's physical behaviour */
+ /* samples per coordinate */
+ unsigned over_sample_cnt;
+ /* time clocks between samples */
+ unsigned over_sample_delay;
+ /* time in clocks to wait after the plates where switched */
+ unsigned settling_delay;
+};
+
+#define LRADC_CTRL0 0x00
+# define LRADC_CTRL0_MX28_TOUCH_DETECT_ENABLE BIT(23)
+# define LRADC_CTRL0_MX28_TOUCH_SCREEN_TYPE BIT(22)
+# define LRADC_CTRL0_MX28_YNNSW /* YM */ BIT(21)
+# define LRADC_CTRL0_MX28_YPNSW /* YP */ BIT(20)
+# define LRADC_CTRL0_MX28_YPPSW /* YP */ BIT(19)
+# define LRADC_CTRL0_MX28_XNNSW /* XM */ BIT(18)
+# define LRADC_CTRL0_MX28_XNPSW /* XM */ BIT(17)
+# define LRADC_CTRL0_MX28_XPPSW /* XP */ BIT(16)
+
+# define LRADC_CTRL0_MX23_TOUCH_DETECT_ENABLE BIT(20)
+# define LRADC_CTRL0_MX23_YM BIT(19)
+# define LRADC_CTRL0_MX23_XM BIT(18)
+# define LRADC_CTRL0_MX23_YP BIT(17)
+# define LRADC_CTRL0_MX23_XP BIT(16)
+
+# define LRADC_CTRL0_MX28_PLATE_MASK \
+ (LRADC_CTRL0_MX28_TOUCH_DETECT_ENABLE | \
+ LRADC_CTRL0_MX28_YNNSW | LRADC_CTRL0_MX28_YPNSW | \
+ LRADC_CTRL0_MX28_YPPSW | LRADC_CTRL0_MX28_XNNSW | \
+ LRADC_CTRL0_MX28_XNPSW | LRADC_CTRL0_MX28_XPPSW)
+
+# define LRADC_CTRL0_MX23_PLATE_MASK \
+ (LRADC_CTRL0_MX23_TOUCH_DETECT_ENABLE | \
+ LRADC_CTRL0_MX23_YM | LRADC_CTRL0_MX23_XM | \
+ LRADC_CTRL0_MX23_YP | LRADC_CTRL0_MX23_XP)
+
+#define LRADC_CTRL1 0x10
+#define LRADC_CTRL1_TOUCH_DETECT_IRQ_EN BIT(24)
+#define LRADC_CTRL1_LRADC_IRQ_EN(n) (1 << ((n) + 16))
+#define LRADC_CTRL1_MX28_LRADC_IRQ_EN_MASK (0x1fff << 16)
+#define LRADC_CTRL1_MX23_LRADC_IRQ_EN_MASK (0x01ff << 16)
+#define LRADC_CTRL1_LRADC_IRQ_EN_OFFSET 16
+#define LRADC_CTRL1_TOUCH_DETECT_IRQ BIT(8)
+#define LRADC_CTRL1_LRADC_IRQ(n) (1 << (n))
+#define LRADC_CTRL1_MX28_LRADC_IRQ_MASK 0x1fff
+#define LRADC_CTRL1_MX23_LRADC_IRQ_MASK 0x01ff
+#define LRADC_CTRL1_LRADC_IRQ_OFFSET 0
+
+#define LRADC_CTRL2 0x20
+#define LRADC_CTRL2_DIVIDE_BY_TWO_OFFSET 24
+#define LRADC_CTRL2_TEMPSENSE_PWD BIT(15)
+
+#define LRADC_STATUS 0x40
+#define LRADC_STATUS_TOUCH_DETECT_RAW BIT(0)
+
+#define LRADC_CH(n) (0x50 + (0x10 * (n)))
+#define LRADC_CH_ACCUMULATE BIT(29)
+#define LRADC_CH_NUM_SAMPLES_MASK (0x1f << 24)
+#define LRADC_CH_NUM_SAMPLES_OFFSET 24
+#define LRADC_CH_NUM_SAMPLES(x) \
+ ((x) << LRADC_CH_NUM_SAMPLES_OFFSET)
+#define LRADC_CH_VALUE_MASK 0x3ffff
+#define LRADC_CH_VALUE_OFFSET 0
+
+#define LRADC_DELAY(n) (0xd0 + (0x10 * (n)))
+#define LRADC_DELAY_TRIGGER_LRADCS_MASK (0xff << 24)
+#define LRADC_DELAY_TRIGGER_LRADCS_OFFSET 24
+#define LRADC_DELAY_TRIGGER(x) \
+ (((x) << LRADC_DELAY_TRIGGER_LRADCS_OFFSET) & \
+ LRADC_DELAY_TRIGGER_LRADCS_MASK)
+#define LRADC_DELAY_KICK (1 << 20)
+#define LRADC_DELAY_TRIGGER_DELAYS_MASK (0xf << 16)
+#define LRADC_DELAY_TRIGGER_DELAYS_OFFSET 16
+#define LRADC_DELAY_TRIGGER_DELAYS(x) \
+ (((x) << LRADC_DELAY_TRIGGER_DELAYS_OFFSET) & \
+ LRADC_DELAY_TRIGGER_DELAYS_MASK)
+#define LRADC_DELAY_LOOP_COUNT_MASK (0x1f << 11)
+#define LRADC_DELAY_LOOP_COUNT_OFFSET 11
+#define LRADC_DELAY_LOOP(x) \
+ (((x) << LRADC_DELAY_LOOP_COUNT_OFFSET) & \
+ LRADC_DELAY_LOOP_COUNT_MASK)
+#define LRADC_DELAY_DELAY_MASK 0x7ff
+#define LRADC_DELAY_DELAY_OFFSET 0
+#define LRADC_DELAY_DELAY(x) \
+ (((x) << LRADC_DELAY_DELAY_OFFSET) & \
+ LRADC_DELAY_DELAY_MASK)
+
+#define LRADC_CTRL4 0x140
+#define LRADC_CTRL4_LRADCSELECT_MASK(n) (0xf << ((n) * 4))
+#define LRADC_CTRL4_LRADCSELECT_OFFSET(n) ((n) * 4)
+#define LRADC_CTRL4_LRADCSELECT(n, x) \
+ (((x) << LRADC_CTRL4_LRADCSELECT_OFFSET(n)) & \
+ LRADC_CTRL4_LRADCSELECT_MASK(n))
+
+#define LRADC_RESOLUTION 12
+#define LRADC_SINGLE_SAMPLE_MASK ((1 << LRADC_RESOLUTION) - 1)
+
+static void mxs_lradc_reg_set(struct mxs_lradc *lradc, u32 val, u32 reg)
+{
+ writel(val, lradc->base + reg + STMP_OFFSET_REG_SET);
+}
+
+static void mxs_lradc_reg_clear(struct mxs_lradc *lradc, u32 val, u32 reg)
+{
+ writel(val, lradc->base + reg + STMP_OFFSET_REG_CLR);
+}
+
+static void mxs_lradc_reg_wrt(struct mxs_lradc *lradc, u32 val, u32 reg)
+{
+ writel(val, lradc->base + reg);
+}
+
+static u32 mxs_lradc_plate_mask(struct mxs_lradc *lradc)
+{
+ if (lradc->soc == IMX23_LRADC)
+ return LRADC_CTRL0_MX23_PLATE_MASK;
+ return LRADC_CTRL0_MX28_PLATE_MASK;
+}
+
+static u32 mxs_lradc_irq_en_mask(struct mxs_lradc *lradc)
+{
+ if (lradc->soc == IMX23_LRADC)
+ return LRADC_CTRL1_MX23_LRADC_IRQ_EN_MASK;
+ return LRADC_CTRL1_MX28_LRADC_IRQ_EN_MASK;
+}
+
+static u32 mxs_lradc_irq_mask(struct mxs_lradc *lradc)
+{
+ if (lradc->soc == IMX23_LRADC)
+ return LRADC_CTRL1_MX23_LRADC_IRQ_MASK;
+ return LRADC_CTRL1_MX28_LRADC_IRQ_MASK;
+}
+
+static u32 mxs_lradc_touch_detect_bit(struct mxs_lradc *lradc)
+{
+ if (lradc->soc == IMX23_LRADC)
+ return LRADC_CTRL0_MX23_TOUCH_DETECT_ENABLE;
+ return LRADC_CTRL0_MX28_TOUCH_DETECT_ENABLE;
+}
+
+static u32 mxs_lradc_drive_x_plate(struct mxs_lradc *lradc)
+{
+ if (lradc->soc == IMX23_LRADC)
+ return LRADC_CTRL0_MX23_XP | LRADC_CTRL0_MX23_XM;
+ return LRADC_CTRL0_MX28_XPPSW | LRADC_CTRL0_MX28_XNNSW;
+}
+
+static u32 mxs_lradc_drive_y_plate(struct mxs_lradc *lradc)
+{
+ if (lradc->soc == IMX23_LRADC)
+ return LRADC_CTRL0_MX23_YP | LRADC_CTRL0_MX23_YM;
+ return LRADC_CTRL0_MX28_YPPSW | LRADC_CTRL0_MX28_YNNSW;
+}
+
+static u32 mxs_lradc_drive_pressure(struct mxs_lradc *lradc)
+{
+ if (lradc->soc == IMX23_LRADC)
+ return LRADC_CTRL0_MX23_YP | LRADC_CTRL0_MX23_XM;
+ return LRADC_CTRL0_MX28_YPPSW | LRADC_CTRL0_MX28_XNNSW;
+}
+
+static bool mxs_lradc_check_touch_event(struct mxs_lradc *lradc)
+{
+ return !!(readl(lradc->base + LRADC_STATUS) &
+ LRADC_STATUS_TOUCH_DETECT_RAW);
+}
+
+static void mxs_lradc_map_channel(struct mxs_lradc *lradc, unsigned vch,
+ unsigned ch)
+{
+ mxs_lradc_reg_clear(lradc, LRADC_CTRL4_LRADCSELECT_MASK(vch),
+ LRADC_CTRL4);
+ mxs_lradc_reg_set(lradc, LRADC_CTRL4_LRADCSELECT(vch, ch), LRADC_CTRL4);
+}
+
+static void mxs_lradc_setup_ts_channel(struct mxs_lradc *lradc, unsigned ch)
+{
+ /*
+ * prepare for oversampling conversion
+ *
+ * from the datasheet:
+ * "The ACCUMULATE bit in the appropriate channel register
+ * HW_LRADC_CHn must be set to 1 if NUM_SAMPLES is greater then 0;
+ * otherwise, the IRQs will not fire."
+ */
+ mxs_lradc_reg_wrt(lradc, LRADC_CH_ACCUMULATE |
+ LRADC_CH_NUM_SAMPLES(lradc->over_sample_cnt - 1),
+ LRADC_CH(ch));
+
+ /* from the datasheet:
+ * "Software must clear this register in preparation for a
+ * multi-cycle accumulation.
+ */
+ mxs_lradc_reg_clear(lradc, LRADC_CH_VALUE_MASK, LRADC_CH(ch));
+
+ /*
+ * prepare the delay/loop unit according to the oversampling count
+ *
+ * from the datasheet:
+ * "The DELAY fields in HW_LRADC_DELAY0, HW_LRADC_DELAY1,
+ * HW_LRADC_DELAY2, and HW_LRADC_DELAY3 must be non-zero; otherwise,
+ * the LRADC will not trigger the delay group."
+ */
+ mxs_lradc_reg_wrt(lradc, LRADC_DELAY_TRIGGER(1 << ch) |
+ LRADC_DELAY_TRIGGER_DELAYS(0) |
+ LRADC_DELAY_LOOP(lradc->over_sample_cnt - 1) |
+ LRADC_DELAY_DELAY(lradc->over_sample_delay - 1),
+ LRADC_DELAY(3));
+
+ mxs_lradc_reg_clear(lradc, LRADC_CTRL1_LRADC_IRQ(ch), LRADC_CTRL1);
+
+ /*
+ * after changing the touchscreen plates setting
+ * the signals need some initial time to settle. Start the
+ * SoC's delay unit and start the conversion later
+ * and automatically.
+ */
+ mxs_lradc_reg_wrt(lradc,
+ LRADC_DELAY_TRIGGER(0) | /* don't trigger ADC */
+ LRADC_DELAY_TRIGGER_DELAYS(BIT(3)) | /* trigger DELAY unit#3 */
+ LRADC_DELAY_KICK |
+ LRADC_DELAY_DELAY(lradc->settling_delay),
+ LRADC_DELAY(2));
+}
+
+/*
+ * Pressure detection is special:
+ * We want to do both required measurements for the pressure detection in
+ * one turn. Use the hardware features to chain both conversions and let the
+ * hardware report one interrupt if both conversions are done
+ */
+static void mxs_lradc_setup_ts_pressure(struct mxs_lradc *lradc, unsigned ch1,
+ unsigned ch2)
+{
+ u32 reg;
+
+ /*
+ * prepare for oversampling conversion
+ *
+ * from the datasheet:
+ * "The ACCUMULATE bit in the appropriate channel register
+ * HW_LRADC_CHn must be set to 1 if NUM_SAMPLES is greater then 0;
+ * otherwise, the IRQs will not fire."
+ */
+ reg = LRADC_CH_ACCUMULATE |
+ LRADC_CH_NUM_SAMPLES(lradc->over_sample_cnt - 1);
+ mxs_lradc_reg_wrt(lradc, reg, LRADC_CH(ch1));
+ mxs_lradc_reg_wrt(lradc, reg, LRADC_CH(ch2));
+
+ /* from the datasheet:
+ * "Software must clear this register in preparation for a
+ * multi-cycle accumulation.
+ */
+ mxs_lradc_reg_clear(lradc, LRADC_CH_VALUE_MASK, LRADC_CH(ch1));
+ mxs_lradc_reg_clear(lradc, LRADC_CH_VALUE_MASK, LRADC_CH(ch2));
+
+ /* prepare the delay/loop unit according to the oversampling count */
+ mxs_lradc_reg_wrt(lradc, LRADC_DELAY_TRIGGER(1 << ch1) |
+ LRADC_DELAY_TRIGGER(1 << ch2) | /* start both channels */
+ LRADC_DELAY_TRIGGER_DELAYS(0) |
+ LRADC_DELAY_LOOP(lradc->over_sample_cnt - 1) |
+ LRADC_DELAY_DELAY(lradc->over_sample_delay - 1),
+ LRADC_DELAY(3));
+
+ mxs_lradc_reg_clear(lradc, LRADC_CTRL1_LRADC_IRQ(ch2), LRADC_CTRL1);
+
+ /*
+ * after changing the touchscreen plates setting
+ * the signals need some initial time to settle. Start the
+ * SoC's delay unit and start the conversion later
+ * and automatically.
+ */
+ mxs_lradc_reg_wrt(lradc,
+ LRADC_DELAY_TRIGGER(0) | /* don't trigger ADC */
+ LRADC_DELAY_TRIGGER_DELAYS(BIT(3)) | /* trigger DELAY unit#3 */
+ LRADC_DELAY_KICK |
+ LRADC_DELAY_DELAY(lradc->settling_delay), LRADC_DELAY(2));
+}
+
+static unsigned mxs_lradc_read_raw_channel(struct mxs_lradc *lradc,
+ unsigned channel)
+{
+ u32 reg;
+ unsigned num_samples, val;
+
+ reg = readl(lradc->base + LRADC_CH(channel));
+ if (reg & LRADC_CH_ACCUMULATE)
+ num_samples = lradc->over_sample_cnt;
+ else
+ num_samples = 1;
+
+ val = (reg & LRADC_CH_VALUE_MASK) >> LRADC_CH_VALUE_OFFSET;
+ return val / num_samples;
+}
+
+static unsigned mxs_lradc_read_ts_pressure(struct mxs_lradc *lradc,
+ unsigned ch1, unsigned ch2)
+{
+ u32 reg, mask;
+ unsigned pressure, m1, m2;
+
+ mask = LRADC_CTRL1_LRADC_IRQ(ch1) | LRADC_CTRL1_LRADC_IRQ(ch2);
+ reg = readl(lradc->base + LRADC_CTRL1) & mask;
+
+ while (reg != mask) {
+ reg = readl(lradc->base + LRADC_CTRL1) & mask;
+ dev_dbg(lradc->dev, "One channel is still busy: %X\n", reg);
+ }
+
+ m1 = mxs_lradc_read_raw_channel(lradc, ch1);
+ m2 = mxs_lradc_read_raw_channel(lradc, ch2);
+
+ if (m2 == 0) {
+ dev_warn(lradc->dev, "Cannot calculate pressure\n");
+ return 1 << (LRADC_RESOLUTION - 1);
+ }
+
+ /* simply scale the value from 0 ... max ADC resolution */
+ pressure = m1;
+ pressure *= (1 << LRADC_RESOLUTION);
+ pressure /= m2;
+
+ dev_dbg(lradc->dev, "Pressure = %u\n", pressure);
+ return pressure;
+}
+
+#define TS_CH_XP 2
+#define TS_CH_YP 3
+#define TS_CH_XM 4
+#define TS_CH_YM 5
+
+/*
+ * YP(open)--+-------------+
+ * | |--+
+ * | | |
+ * YM(-)--+-------------+ |
+ * +--------------+
+ * | |
+ * XP(weak+) XM(open)
+ *
+ * "weak+" means 200k Ohm VDDIO
+ * (-) means GND
+ */
+static void mxs_lradc_setup_touch_detection(struct mxs_lradc *lradc)
+{
+ /*
+ * In order to detect a touch event the 'touch detect enable' bit
+ * enables:
+ * - a weak pullup to the X+ connector
+ * - a strong ground at the Y- connector
+ */
+ mxs_lradc_reg_clear(lradc, mxs_lradc_plate_mask(lradc), LRADC_CTRL0);
+ mxs_lradc_reg_set(lradc, mxs_lradc_touch_detect_bit(lradc),
+ LRADC_CTRL0);
+}
+
+/*
+ * YP(meas)--+-------------+
+ * | |--+
+ * | | |
+ * YM(open)--+-------------+ |
+ * +--------------+
+ * | |
+ * XP(+) XM(-)
+ *
+ * (+) means here 1.85 V
+ * (-) means here GND
+ */
+static void mxs_lradc_prepare_x_pos(struct mxs_lradc *lradc)
+{
+ mxs_lradc_reg_clear(lradc, mxs_lradc_plate_mask(lradc), LRADC_CTRL0);
+ mxs_lradc_reg_set(lradc, mxs_lradc_drive_x_plate(lradc), LRADC_CTRL0);
+
+ lradc->cur_plate = LRADC_SAMPLE_X;
+ mxs_lradc_map_channel(lradc, TOUCHSCREEN_VCHANNEL1, TS_CH_YP);
+ mxs_lradc_setup_ts_channel(lradc, TOUCHSCREEN_VCHANNEL1);
+}
+
+/*
+ * YP(+)--+-------------+
+ * | |--+
+ * | | |
+ * YM(-)--+-------------+ |
+ * +--------------+
+ * | |
+ * XP(open) XM(meas)
+ *
+ * (+) means here 1.85 V
+ * (-) means here GND
+ */
+static void mxs_lradc_prepare_y_pos(struct mxs_lradc *lradc)
+{
+ mxs_lradc_reg_clear(lradc, mxs_lradc_plate_mask(lradc), LRADC_CTRL0);
+ mxs_lradc_reg_set(lradc, mxs_lradc_drive_y_plate(lradc), LRADC_CTRL0);
+
+ lradc->cur_plate = LRADC_SAMPLE_Y;
+ mxs_lradc_map_channel(lradc, TOUCHSCREEN_VCHANNEL1, TS_CH_XM);
+ mxs_lradc_setup_ts_channel(lradc, TOUCHSCREEN_VCHANNEL1);
+}
+
+/*
+ * YP(+)--+-------------+
+ * | |--+
+ * | | |
+ * YM(meas)--+-------------+ |
+ * +--------------+
+ * | |
+ * XP(meas) XM(-)
+ *
+ * (+) means here 1.85 V
+ * (-) means here GND
+ */
+static void mxs_lradc_prepare_pressure(struct mxs_lradc *lradc)
+{
+ mxs_lradc_reg_clear(lradc, mxs_lradc_plate_mask(lradc), LRADC_CTRL0);
+ mxs_lradc_reg_set(lradc, mxs_lradc_drive_pressure(lradc), LRADC_CTRL0);
+
+ lradc->cur_plate = LRADC_SAMPLE_PRESSURE;
+ mxs_lradc_map_channel(lradc, TOUCHSCREEN_VCHANNEL1, TS_CH_YM);
+ mxs_lradc_map_channel(lradc, TOUCHSCREEN_VCHANNEL2, TS_CH_XP);
+ mxs_lradc_setup_ts_pressure(lradc, TOUCHSCREEN_VCHANNEL2,
+ TOUCHSCREEN_VCHANNEL1);
+}
+
+static void mxs_lradc_enable_touch_detection(struct mxs_lradc *lradc)
+{
+ mxs_lradc_setup_touch_detection(lradc);
+
+ lradc->cur_plate = LRADC_TOUCH;
+ mxs_lradc_reg_clear(lradc, LRADC_CTRL1_TOUCH_DETECT_IRQ |
+ LRADC_CTRL1_TOUCH_DETECT_IRQ_EN, LRADC_CTRL1);
+ mxs_lradc_reg_set(lradc, LRADC_CTRL1_TOUCH_DETECT_IRQ_EN, LRADC_CTRL1);
+}
+
+static void mxs_lradc_start_touch_event(struct mxs_lradc *lradc)
+{
+ mxs_lradc_reg_clear(lradc, LRADC_CTRL1_TOUCH_DETECT_IRQ_EN,
+ LRADC_CTRL1);
+ mxs_lradc_reg_set(lradc,
+ LRADC_CTRL1_LRADC_IRQ_EN(TOUCHSCREEN_VCHANNEL1), LRADC_CTRL1);
+ /*
+ * start with the Y-pos, because it uses nearly the same plate
+ * settings like the touch detection
+ */
+ mxs_lradc_prepare_y_pos(lradc);
+}
+
+static void mxs_lradc_report_ts_event(struct mxs_lradc *lradc)
+{
+ input_report_abs(lradc->ts_input, ABS_X, lradc->ts_x_pos);
+ input_report_abs(lradc->ts_input, ABS_Y, lradc->ts_y_pos);
+ input_report_abs(lradc->ts_input, ABS_PRESSURE, lradc->ts_pressure);
+ input_report_key(lradc->ts_input, BTN_TOUCH, 1);
+ input_sync(lradc->ts_input);
+}
+
+static void mxs_lradc_complete_touch_event(struct mxs_lradc *lradc)
+{
+ mxs_lradc_setup_touch_detection(lradc);
+ lradc->cur_plate = LRADC_SAMPLE_VALID;
+ /*
+ * start a dummy conversion to burn time to settle the signals
+ * note: we are not interested in the conversion's value
+ */
+ mxs_lradc_reg_wrt(lradc, 0, LRADC_CH(TOUCHSCREEN_VCHANNEL1));
+ mxs_lradc_reg_clear(lradc,
+ LRADC_CTRL1_LRADC_IRQ(TOUCHSCREEN_VCHANNEL1) |
+ LRADC_CTRL1_LRADC_IRQ(TOUCHSCREEN_VCHANNEL2), LRADC_CTRL1);
+ mxs_lradc_reg_wrt(lradc,
+ LRADC_DELAY_TRIGGER(1 << TOUCHSCREEN_VCHANNEL1) |
+ LRADC_DELAY_KICK | LRADC_DELAY_DELAY(10), /* waste 5 ms */
+ LRADC_DELAY(2));
+}
+
+/*
+ * in order to avoid false measurements, report only samples where
+ * the surface is still touched after the position measurement
+ */
+static void mxs_lradc_finish_touch_event(struct mxs_lradc *lradc, bool valid)
+{
+ /* if it is still touched, report the sample */
+ if (valid && mxs_lradc_check_touch_event(lradc)) {
+ lradc->ts_valid = true;
+ mxs_lradc_report_ts_event(lradc);
+ }
+
+ /* if it is even still touched, continue with the next measurement */
+ if (mxs_lradc_check_touch_event(lradc)) {
+ mxs_lradc_prepare_y_pos(lradc);
+ return;
+ }
+
+ if (lradc->ts_valid) {
+ /* signal the release */
+ lradc->ts_valid = false;
+ input_report_key(lradc->ts_input, BTN_TOUCH, 0);
+ input_sync(lradc->ts_input);
+ }
+
+ /* if it is released, wait for the next touch via IRQ */
+ lradc->cur_plate = LRADC_TOUCH;
+ mxs_lradc_reg_wrt(lradc, 0, LRADC_DELAY(2));
+ mxs_lradc_reg_wrt(lradc, 0, LRADC_DELAY(3));
+ mxs_lradc_reg_clear(lradc, LRADC_CTRL1_TOUCH_DETECT_IRQ |
+ LRADC_CTRL1_LRADC_IRQ_EN(TOUCHSCREEN_VCHANNEL1) |
+ LRADC_CTRL1_LRADC_IRQ(TOUCHSCREEN_VCHANNEL1), LRADC_CTRL1);
+ mxs_lradc_reg_set(lradc, LRADC_CTRL1_TOUCH_DETECT_IRQ_EN, LRADC_CTRL1);
+}
+
+/* touchscreen's state machine */
+static void mxs_lradc_handle_touch(struct mxs_lradc *lradc)
+{
+ switch (lradc->cur_plate) {
+ case LRADC_TOUCH:
+ if (mxs_lradc_check_touch_event(lradc))
+ mxs_lradc_start_touch_event(lradc);
+ mxs_lradc_reg_clear(lradc, LRADC_CTRL1_TOUCH_DETECT_IRQ,
+ LRADC_CTRL1);
+ return;
+
+ case LRADC_SAMPLE_Y:
+ lradc->ts_y_pos = mxs_lradc_read_raw_channel(lradc,
+ TOUCHSCREEN_VCHANNEL1);
+ mxs_lradc_prepare_x_pos(lradc);
+ return;
+
+ case LRADC_SAMPLE_X:
+ lradc->ts_x_pos = mxs_lradc_read_raw_channel(lradc,
+ TOUCHSCREEN_VCHANNEL1);
+ mxs_lradc_prepare_pressure(lradc);
+ return;
+
+ case LRADC_SAMPLE_PRESSURE:
+ lradc->ts_pressure = mxs_lradc_read_ts_pressure(lradc,
+ TOUCHSCREEN_VCHANNEL2,
+ TOUCHSCREEN_VCHANNEL1);
+ mxs_lradc_complete_touch_event(lradc);
+ return;
+
+ case LRADC_SAMPLE_VALID:
+ mxs_lradc_finish_touch_event(lradc, 1);
+ break;
+ }
+}
+
+/*
+ * Raw I/O operations
+ */
+static int mxs_lradc_read_single(struct iio_dev *iio_dev, int chan, int *val)
+{
+ struct mxs_lradc *lradc = iio_priv(iio_dev);
+ int ret;
+
+ /*
+ * See if there is no buffered operation in progess. If there is, simply
+ * bail out. This can be improved to support both buffered and raw IO at
+ * the same time, yet the code becomes horribly complicated. Therefore I
+ * applied KISS principle here.
+ */
+ ret = mutex_trylock(&lradc->lock);
+ if (!ret)
+ return -EBUSY;
+
+ reinit_completion(&lradc->completion);
+
+ /*
+ * No buffered operation in progress, map the channel and trigger it.
+ * Virtual channel 0 is always used here as the others are always not
+ * used if doing raw sampling.
+ */
+ if (lradc->soc == IMX28_LRADC)
+ mxs_lradc_reg_clear(lradc, LRADC_CTRL1_LRADC_IRQ_EN(0),
+ LRADC_CTRL1);
+ mxs_lradc_reg_clear(lradc, 0x1, LRADC_CTRL0);
+
+ /* Enable / disable the divider per requirement */
+ if (test_bit(chan, &lradc->is_divided))
+ mxs_lradc_reg_set(lradc, 1 << LRADC_CTRL2_DIVIDE_BY_TWO_OFFSET,
+ LRADC_CTRL2);
+ else
+ mxs_lradc_reg_clear(lradc,
+ 1 << LRADC_CTRL2_DIVIDE_BY_TWO_OFFSET, LRADC_CTRL2);
+
+ /* Clean the slot's previous content, then set new one. */
+ mxs_lradc_reg_clear(lradc, LRADC_CTRL4_LRADCSELECT_MASK(0),
+ LRADC_CTRL4);
+ mxs_lradc_reg_set(lradc, chan, LRADC_CTRL4);
+
+ mxs_lradc_reg_wrt(lradc, 0, LRADC_CH(0));
+
+ /* Enable the IRQ and start sampling the channel. */
+ mxs_lradc_reg_set(lradc, LRADC_CTRL1_LRADC_IRQ_EN(0), LRADC_CTRL1);
+ mxs_lradc_reg_set(lradc, BIT(0), LRADC_CTRL0);
+
+ /* Wait for completion on the channel, 1 second max. */
+ ret = wait_for_completion_killable_timeout(&lradc->completion, HZ);
+ if (!ret)
+ ret = -ETIMEDOUT;
+ if (ret < 0)
+ goto err;
+
+ /* Read the data. */
+ *val = readl(lradc->base + LRADC_CH(0)) & LRADC_CH_VALUE_MASK;
+ ret = IIO_VAL_INT;
+
+err:
+ mxs_lradc_reg_clear(lradc, LRADC_CTRL1_LRADC_IRQ_EN(0), LRADC_CTRL1);
+
+ mutex_unlock(&lradc->lock);
+
+ return ret;
+}
+
+static int mxs_lradc_read_temp(struct iio_dev *iio_dev, int *val)
+{
+ int ret, min, max;
+
+ ret = mxs_lradc_read_single(iio_dev, 8, &min);
+ if (ret != IIO_VAL_INT)
+ return ret;
+
+ ret = mxs_lradc_read_single(iio_dev, 9, &max);
+ if (ret != IIO_VAL_INT)
+ return ret;
+
+ *val = max - min;
+
+ return IIO_VAL_INT;
+}
+
+static int mxs_lradc_read_raw(struct iio_dev *iio_dev,
+ const struct iio_chan_spec *chan,
+ int *val, int *val2, long m)
+{
+ struct mxs_lradc *lradc = iio_priv(iio_dev);
+
+ switch (m) {
+ case IIO_CHAN_INFO_RAW:
+ if (chan->type == IIO_TEMP)
+ return mxs_lradc_read_temp(iio_dev, val);
+
+ return mxs_lradc_read_single(iio_dev, chan->channel, val);
+
+ case IIO_CHAN_INFO_SCALE:
+ if (chan->type == IIO_TEMP) {
+ /* From the datasheet, we have to multiply by 1.012 and
+ * divide by 4
+ */
+ *val = 0;
+ *val2 = 253000;
+ return IIO_VAL_INT_PLUS_MICRO;
+ }
+
+ *val = lradc->vref_mv[chan->channel];
+ *val2 = chan->scan_type.realbits -
+ test_bit(chan->channel, &lradc->is_divided);
+ return IIO_VAL_FRACTIONAL_LOG2;
+
+ case IIO_CHAN_INFO_OFFSET:
+ if (chan->type == IIO_TEMP) {
+ /* The calculated value from the ADC is in Kelvin, we
+ * want Celsius for hwmon so the offset is
+ * -272.15 * scale
+ */
+ *val = -1075;
+ *val2 = 691699;
+
+ return IIO_VAL_INT_PLUS_MICRO;
+ }
+
+ return -EINVAL;
+
+ default:
+ break;
+ }
+
+ return -EINVAL;
+}
+
+static int mxs_lradc_write_raw(struct iio_dev *iio_dev,
+ const struct iio_chan_spec *chan,
+ int val, int val2, long m)
+{
+ struct mxs_lradc *lradc = iio_priv(iio_dev);
+ struct mxs_lradc_scale *scale_avail =
+ lradc->scale_avail[chan->channel];
+ int ret;
+
+ ret = mutex_trylock(&lradc->lock);
+ if (!ret)
+ return -EBUSY;
+
+ switch (m) {
+ case IIO_CHAN_INFO_SCALE:
+ ret = -EINVAL;
+ if (val == scale_avail[MXS_LRADC_DIV_DISABLED].integer &&
+ val2 == scale_avail[MXS_LRADC_DIV_DISABLED].nano) {
+ /* divider by two disabled */
+ clear_bit(chan->channel, &lradc->is_divided);
+ ret = 0;
+ } else if (val == scale_avail[MXS_LRADC_DIV_ENABLED].integer &&
+ val2 == scale_avail[MXS_LRADC_DIV_ENABLED].nano) {
+ /* divider by two enabled */
+ set_bit(chan->channel, &lradc->is_divided);
+ ret = 0;
+ }
+
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ mutex_unlock(&lradc->lock);
+
+ return ret;
+}
+
+static int mxs_lradc_write_raw_get_fmt(struct iio_dev *iio_dev,
+ const struct iio_chan_spec *chan,
+ long m)
+{
+ return IIO_VAL_INT_PLUS_NANO;
+}
+
+static ssize_t mxs_lradc_show_scale_available_ch(struct device *dev,
+ struct device_attribute *attr,
+ char *buf,
+ int ch)
+{
+ struct iio_dev *iio = dev_to_iio_dev(dev);
+ struct mxs_lradc *lradc = iio_priv(iio);
+ int i, len = 0;
+
+ for (i = 0; i < ARRAY_SIZE(lradc->scale_avail[ch]); i++)
+ len += sprintf(buf + len, "%u.%09u ",
+ lradc->scale_avail[ch][i].integer,
+ lradc->scale_avail[ch][i].nano);
+
+ len += sprintf(buf + len, "\n");
+
+ return len;
+}
+
+static ssize_t mxs_lradc_show_scale_available(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct iio_dev_attr *iio_attr = to_iio_dev_attr(attr);
+
+ return mxs_lradc_show_scale_available_ch(dev, attr, buf,
+ iio_attr->address);
+}
+
+#define SHOW_SCALE_AVAILABLE_ATTR(ch) \
+static IIO_DEVICE_ATTR(in_voltage##ch##_scale_available, S_IRUGO, \
+ mxs_lradc_show_scale_available, NULL, ch)
+
+SHOW_SCALE_AVAILABLE_ATTR(0);
+SHOW_SCALE_AVAILABLE_ATTR(1);
+SHOW_SCALE_AVAILABLE_ATTR(2);
+SHOW_SCALE_AVAILABLE_ATTR(3);
+SHOW_SCALE_AVAILABLE_ATTR(4);
+SHOW_SCALE_AVAILABLE_ATTR(5);
+SHOW_SCALE_AVAILABLE_ATTR(6);
+SHOW_SCALE_AVAILABLE_ATTR(7);
+SHOW_SCALE_AVAILABLE_ATTR(10);
+SHOW_SCALE_AVAILABLE_ATTR(11);
+SHOW_SCALE_AVAILABLE_ATTR(12);
+SHOW_SCALE_AVAILABLE_ATTR(13);
+SHOW_SCALE_AVAILABLE_ATTR(14);
+SHOW_SCALE_AVAILABLE_ATTR(15);
+
+static struct attribute *mxs_lradc_attributes[] = {
+ &iio_dev_attr_in_voltage0_scale_available.dev_attr.attr,
+ &iio_dev_attr_in_voltage1_scale_available.dev_attr.attr,
+ &iio_dev_attr_in_voltage2_scale_available.dev_attr.attr,
+ &iio_dev_attr_in_voltage3_scale_available.dev_attr.attr,
+ &iio_dev_attr_in_voltage4_scale_available.dev_attr.attr,
+ &iio_dev_attr_in_voltage5_scale_available.dev_attr.attr,
+ &iio_dev_attr_in_voltage6_scale_available.dev_attr.attr,
+ &iio_dev_attr_in_voltage7_scale_available.dev_attr.attr,
+ &iio_dev_attr_in_voltage10_scale_available.dev_attr.attr,
+ &iio_dev_attr_in_voltage11_scale_available.dev_attr.attr,
+ &iio_dev_attr_in_voltage12_scale_available.dev_attr.attr,
+ &iio_dev_attr_in_voltage13_scale_available.dev_attr.attr,
+ &iio_dev_attr_in_voltage14_scale_available.dev_attr.attr,
+ &iio_dev_attr_in_voltage15_scale_available.dev_attr.attr,
+ NULL
+};
+
+static const struct attribute_group mxs_lradc_attribute_group = {
+ .attrs = mxs_lradc_attributes,
+};
+
+static const struct iio_info mxs_lradc_iio_info = {
+ .driver_module = THIS_MODULE,
+ .read_raw = mxs_lradc_read_raw,
+ .write_raw = mxs_lradc_write_raw,
+ .write_raw_get_fmt = mxs_lradc_write_raw_get_fmt,
+ .attrs = &mxs_lradc_attribute_group,
+};
+
+static int mxs_lradc_ts_open(struct input_dev *dev)
+{
+ struct mxs_lradc *lradc = input_get_drvdata(dev);
+
+ /* Enable the touch-detect circuitry. */
+ mxs_lradc_enable_touch_detection(lradc);
+
+ return 0;
+}
+
+static void mxs_lradc_disable_ts(struct mxs_lradc *lradc)
+{
+ /* stop all interrupts from firing */
+ mxs_lradc_reg_clear(lradc, LRADC_CTRL1_TOUCH_DETECT_IRQ_EN |
+ LRADC_CTRL1_LRADC_IRQ_EN(TOUCHSCREEN_VCHANNEL1) |
+ LRADC_CTRL1_LRADC_IRQ_EN(TOUCHSCREEN_VCHANNEL2), LRADC_CTRL1);
+
+ /* Power-down touchscreen touch-detect circuitry. */
+ mxs_lradc_reg_clear(lradc, mxs_lradc_plate_mask(lradc), LRADC_CTRL0);
+}
+
+static void mxs_lradc_ts_close(struct input_dev *dev)
+{
+ struct mxs_lradc *lradc = input_get_drvdata(dev);
+
+ mxs_lradc_disable_ts(lradc);
+}
+
+static int mxs_lradc_ts_register(struct mxs_lradc *lradc)
+{
+ struct input_dev *input;
+ struct device *dev = lradc->dev;
+ int ret;
+
+ if (!lradc->use_touchscreen)
+ return 0;
+
+ input = input_allocate_device();
+ if (!input)
+ return -ENOMEM;
+
+ input->name = DRIVER_NAME;
+ input->id.bustype = BUS_HOST;
+ input->dev.parent = dev;
+ input->open = mxs_lradc_ts_open;
+ input->close = mxs_lradc_ts_close;
+
+ __set_bit(EV_ABS, input->evbit);
+ __set_bit(EV_KEY, input->evbit);
+ __set_bit(BTN_TOUCH, input->keybit);
+ input_set_abs_params(input, ABS_X, 0, LRADC_SINGLE_SAMPLE_MASK, 0, 0);
+ input_set_abs_params(input, ABS_Y, 0, LRADC_SINGLE_SAMPLE_MASK, 0, 0);
+ input_set_abs_params(input, ABS_PRESSURE, 0, LRADC_SINGLE_SAMPLE_MASK,
+ 0, 0);
+
+ lradc->ts_input = input;
+ input_set_drvdata(input, lradc);
+ ret = input_register_device(input);
+ if (ret)
+ input_free_device(lradc->ts_input);
+
+ return ret;
+}
+
+static void mxs_lradc_ts_unregister(struct mxs_lradc *lradc)
+{
+ if (!lradc->use_touchscreen)
+ return;
+
+ mxs_lradc_disable_ts(lradc);
+ input_unregister_device(lradc->ts_input);
+}
+
+/*
+ * IRQ Handling
+ */
+static irqreturn_t mxs_lradc_handle_irq(int irq, void *data)
+{
+ struct iio_dev *iio = data;
+ struct mxs_lradc *lradc = iio_priv(iio);
+ unsigned long reg = readl(lradc->base + LRADC_CTRL1);
+ uint32_t clr_irq = mxs_lradc_irq_mask(lradc);
+ const uint32_t ts_irq_mask =
+ LRADC_CTRL1_TOUCH_DETECT_IRQ |
+ LRADC_CTRL1_LRADC_IRQ(TOUCHSCREEN_VCHANNEL1) |
+ LRADC_CTRL1_LRADC_IRQ(TOUCHSCREEN_VCHANNEL2);
+
+ if (!(reg & mxs_lradc_irq_mask(lradc)))
+ return IRQ_NONE;
+
+ if (lradc->use_touchscreen && (reg & ts_irq_mask)) {
+ mxs_lradc_handle_touch(lradc);
+
+ /* Make sure we don't clear the next conversion's interrupt. */
+ clr_irq &= ~(LRADC_CTRL1_LRADC_IRQ(TOUCHSCREEN_VCHANNEL1) |
+ LRADC_CTRL1_LRADC_IRQ(TOUCHSCREEN_VCHANNEL2));
+ }
+
+ if (iio_buffer_enabled(iio)) {
+ if (reg & lradc->buffer_vchans)
+ iio_trigger_poll(iio->trig);
+ } else if (reg & LRADC_CTRL1_LRADC_IRQ(0)) {
+ complete(&lradc->completion);
+ }
+
+ mxs_lradc_reg_clear(lradc, reg & clr_irq, LRADC_CTRL1);
+
+ return IRQ_HANDLED;
+}
+
+/*
+ * Trigger handling
+ */
+static irqreturn_t mxs_lradc_trigger_handler(int irq, void *p)
+{
+ struct iio_poll_func *pf = p;
+ struct iio_dev *iio = pf->indio_dev;
+ struct mxs_lradc *lradc = iio_priv(iio);
+ const uint32_t chan_value = LRADC_CH_ACCUMULATE |
+ ((LRADC_DELAY_TIMER_LOOP - 1) << LRADC_CH_NUM_SAMPLES_OFFSET);
+ unsigned int i, j = 0;
+
+ for_each_set_bit(i, iio->active_scan_mask, LRADC_MAX_TOTAL_CHANS) {
+ lradc->buffer[j] = readl(lradc->base + LRADC_CH(j));
+ mxs_lradc_reg_wrt(lradc, chan_value, LRADC_CH(j));
+ lradc->buffer[j] &= LRADC_CH_VALUE_MASK;
+ lradc->buffer[j] /= LRADC_DELAY_TIMER_LOOP;
+ j++;
+ }
+
+ iio_push_to_buffers_with_timestamp(iio, lradc->buffer, pf->timestamp);
+
+ iio_trigger_notify_done(iio->trig);
+
+ return IRQ_HANDLED;
+}
+
+static int mxs_lradc_configure_trigger(struct iio_trigger *trig, bool state)
+{
+ struct iio_dev *iio = iio_trigger_get_drvdata(trig);
+ struct mxs_lradc *lradc = iio_priv(iio);
+ const uint32_t st = state ? STMP_OFFSET_REG_SET : STMP_OFFSET_REG_CLR;
+
+ mxs_lradc_reg_wrt(lradc, LRADC_DELAY_KICK, LRADC_DELAY(0) + st);
+
+ return 0;
+}
+
+static const struct iio_trigger_ops mxs_lradc_trigger_ops = {
+ .owner = THIS_MODULE,
+ .set_trigger_state = &mxs_lradc_configure_trigger,
+};
+
+static int mxs_lradc_trigger_init(struct iio_dev *iio)
+{
+ int ret;
+ struct iio_trigger *trig;
+ struct mxs_lradc *lradc = iio_priv(iio);
+
+ trig = iio_trigger_alloc("%s-dev%i", iio->name, iio->id);
+ if (trig == NULL)
+ return -ENOMEM;
+
+ trig->dev.parent = lradc->dev;
+ iio_trigger_set_drvdata(trig, iio);
+ trig->ops = &mxs_lradc_trigger_ops;
+
+ ret = iio_trigger_register(trig);
+ if (ret) {
+ iio_trigger_free(trig);
+ return ret;
+ }
+
+ lradc->trig = trig;
+
+ return 0;
+}
+
+static void mxs_lradc_trigger_remove(struct iio_dev *iio)
+{
+ struct mxs_lradc *lradc = iio_priv(iio);
+
+ iio_trigger_unregister(lradc->trig);
+ iio_trigger_free(lradc->trig);
+}
+
+static int mxs_lradc_buffer_preenable(struct iio_dev *iio)
+{
+ struct mxs_lradc *lradc = iio_priv(iio);
+ int ret = 0, chan, ofs = 0;
+ unsigned long enable = 0;
+ uint32_t ctrl4_set = 0;
+ uint32_t ctrl4_clr = 0;
+ uint32_t ctrl1_irq = 0;
+ const uint32_t chan_value = LRADC_CH_ACCUMULATE |
+ ((LRADC_DELAY_TIMER_LOOP - 1) << LRADC_CH_NUM_SAMPLES_OFFSET);
+ const int len = bitmap_weight(iio->active_scan_mask,
+ LRADC_MAX_TOTAL_CHANS);
+
+ if (!len)
+ return -EINVAL;
+
+ /*
+ * Lock the driver so raw access can not be done during buffered
+ * operation. This simplifies the code a lot.
+ */
+ ret = mutex_trylock(&lradc->lock);
+ if (!ret)
+ return -EBUSY;
+
+ lradc->buffer = kmalloc_array(len, sizeof(*lradc->buffer), GFP_KERNEL);
+ if (!lradc->buffer) {
+ ret = -ENOMEM;
+ goto err_mem;
+ }
+
+ if (lradc->soc == IMX28_LRADC)
+ mxs_lradc_reg_clear(lradc,
+ lradc->buffer_vchans << LRADC_CTRL1_LRADC_IRQ_EN_OFFSET,
+ LRADC_CTRL1);
+ mxs_lradc_reg_clear(lradc, lradc->buffer_vchans, LRADC_CTRL0);
+
+ for_each_set_bit(chan, iio->active_scan_mask, LRADC_MAX_TOTAL_CHANS) {
+ ctrl4_set |= chan << LRADC_CTRL4_LRADCSELECT_OFFSET(ofs);
+ ctrl4_clr |= LRADC_CTRL4_LRADCSELECT_MASK(ofs);
+ ctrl1_irq |= LRADC_CTRL1_LRADC_IRQ_EN(ofs);
+ mxs_lradc_reg_wrt(lradc, chan_value, LRADC_CH(ofs));
+ bitmap_set(&enable, ofs, 1);
+ ofs++;
+ }
+
+ mxs_lradc_reg_clear(lradc, LRADC_DELAY_TRIGGER_LRADCS_MASK |
+ LRADC_DELAY_KICK, LRADC_DELAY(0));
+ mxs_lradc_reg_clear(lradc, ctrl4_clr, LRADC_CTRL4);
+ mxs_lradc_reg_set(lradc, ctrl4_set, LRADC_CTRL4);
+ mxs_lradc_reg_set(lradc, ctrl1_irq, LRADC_CTRL1);
+ mxs_lradc_reg_set(lradc, enable << LRADC_DELAY_TRIGGER_LRADCS_OFFSET,
+ LRADC_DELAY(0));
+
+ return 0;
+
+err_mem:
+ mutex_unlock(&lradc->lock);
+ return ret;
+}
+
+static int mxs_lradc_buffer_postdisable(struct iio_dev *iio)
+{
+ struct mxs_lradc *lradc = iio_priv(iio);
+
+ mxs_lradc_reg_clear(lradc, LRADC_DELAY_TRIGGER_LRADCS_MASK |
+ LRADC_DELAY_KICK, LRADC_DELAY(0));
+
+ mxs_lradc_reg_clear(lradc, lradc->buffer_vchans, LRADC_CTRL0);
+ if (lradc->soc == IMX28_LRADC)
+ mxs_lradc_reg_clear(lradc,
+ lradc->buffer_vchans << LRADC_CTRL1_LRADC_IRQ_EN_OFFSET,
+ LRADC_CTRL1);
+
+ kfree(lradc->buffer);
+ mutex_unlock(&lradc->lock);
+
+ return 0;
+}
+
+static bool mxs_lradc_validate_scan_mask(struct iio_dev *iio,
+ const unsigned long *mask)
+{
+ struct mxs_lradc *lradc = iio_priv(iio);
+ const int map_chans = bitmap_weight(mask, LRADC_MAX_TOTAL_CHANS);
+ int rsvd_chans = 0;
+ unsigned long rsvd_mask = 0;
+
+ if (lradc->use_touchbutton)
+ rsvd_mask |= CHAN_MASK_TOUCHBUTTON;
+ if (lradc->use_touchscreen == MXS_LRADC_TOUCHSCREEN_4WIRE)
+ rsvd_mask |= CHAN_MASK_TOUCHSCREEN_4WIRE;
+ if (lradc->use_touchscreen == MXS_LRADC_TOUCHSCREEN_5WIRE)
+ rsvd_mask |= CHAN_MASK_TOUCHSCREEN_5WIRE;
+
+ if (lradc->use_touchbutton)
+ rsvd_chans++;
+ if (lradc->use_touchscreen)
+ rsvd_chans += 2;
+
+ /* Test for attempts to map channels with special mode of operation. */
+ if (bitmap_intersects(mask, &rsvd_mask, LRADC_MAX_TOTAL_CHANS))
+ return false;
+
+ /* Test for attempts to map more channels then available slots. */
+ if (map_chans + rsvd_chans > LRADC_MAX_MAPPED_CHANS)
+ return false;
+
+ return true;
+}
+
+static const struct iio_buffer_setup_ops mxs_lradc_buffer_ops = {
+ .preenable = &mxs_lradc_buffer_preenable,
+ .postenable = &iio_triggered_buffer_postenable,
+ .predisable = &iio_triggered_buffer_predisable,
+ .postdisable = &mxs_lradc_buffer_postdisable,
+ .validate_scan_mask = &mxs_lradc_validate_scan_mask,
+};
+
+/*
+ * Driver initialization
+ */
+
+#define MXS_ADC_CHAN(idx, chan_type) { \
+ .type = (chan_type), \
+ .indexed = 1, \
+ .scan_index = (idx), \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
+ BIT(IIO_CHAN_INFO_SCALE), \
+ .channel = (idx), \
+ .address = (idx), \
+ .scan_type = { \
+ .sign = 'u', \
+ .realbits = LRADC_RESOLUTION, \
+ .storagebits = 32, \
+ }, \
+}
+
+static const struct iio_chan_spec mxs_lradc_chan_spec[] = {
+ MXS_ADC_CHAN(0, IIO_VOLTAGE),
+ MXS_ADC_CHAN(1, IIO_VOLTAGE),
+ MXS_ADC_CHAN(2, IIO_VOLTAGE),
+ MXS_ADC_CHAN(3, IIO_VOLTAGE),
+ MXS_ADC_CHAN(4, IIO_VOLTAGE),
+ MXS_ADC_CHAN(5, IIO_VOLTAGE),
+ MXS_ADC_CHAN(6, IIO_VOLTAGE),
+ MXS_ADC_CHAN(7, IIO_VOLTAGE), /* VBATT */
+ /* Combined Temperature sensors */
+ {
+ .type = IIO_TEMP,
+ .indexed = 1,
+ .scan_index = 8,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+ BIT(IIO_CHAN_INFO_OFFSET) |
+ BIT(IIO_CHAN_INFO_SCALE),
+ .channel = 8,
+ .scan_type = {.sign = 'u', .realbits = 18, .storagebits = 32,},
+ },
+ /* Hidden channel to keep indexes */
+ {
+ .type = IIO_TEMP,
+ .indexed = 1,
+ .scan_index = -1,
+ .channel = 9,
+ },
+ MXS_ADC_CHAN(10, IIO_VOLTAGE), /* VDDIO */
+ MXS_ADC_CHAN(11, IIO_VOLTAGE), /* VTH */
+ MXS_ADC_CHAN(12, IIO_VOLTAGE), /* VDDA */
+ MXS_ADC_CHAN(13, IIO_VOLTAGE), /* VDDD */
+ MXS_ADC_CHAN(14, IIO_VOLTAGE), /* VBG */
+ MXS_ADC_CHAN(15, IIO_VOLTAGE), /* VDD5V */
+};
+
+static int mxs_lradc_hw_init(struct mxs_lradc *lradc)
+{
+ /* The ADC always uses DELAY CHANNEL 0. */
+ const uint32_t adc_cfg =
+ (1 << (LRADC_DELAY_TRIGGER_DELAYS_OFFSET + 0)) |
+ (LRADC_DELAY_TIMER_PER << LRADC_DELAY_DELAY_OFFSET);
+
+ int ret = stmp_reset_block(lradc->base);
+
+ if (ret)
+ return ret;
+
+ /* Configure DELAY CHANNEL 0 for generic ADC sampling. */
+ mxs_lradc_reg_wrt(lradc, adc_cfg, LRADC_DELAY(0));
+
+ /* Disable remaining DELAY CHANNELs */
+ mxs_lradc_reg_wrt(lradc, 0, LRADC_DELAY(1));
+ mxs_lradc_reg_wrt(lradc, 0, LRADC_DELAY(2));
+ mxs_lradc_reg_wrt(lradc, 0, LRADC_DELAY(3));
+
+ /* Configure the touchscreen type */
+ if (lradc->soc == IMX28_LRADC) {
+ mxs_lradc_reg_clear(lradc, LRADC_CTRL0_MX28_TOUCH_SCREEN_TYPE,
+ LRADC_CTRL0);
+
+ if (lradc->use_touchscreen == MXS_LRADC_TOUCHSCREEN_5WIRE)
+ mxs_lradc_reg_set(lradc, LRADC_CTRL0_MX28_TOUCH_SCREEN_TYPE,
+ LRADC_CTRL0);
+ }
+
+ /* Start internal temperature sensing. */
+ mxs_lradc_reg_wrt(lradc, 0, LRADC_CTRL2);
+
+ return 0;
+}
+
+static void mxs_lradc_hw_stop(struct mxs_lradc *lradc)
+{
+ int i;
+
+ mxs_lradc_reg_clear(lradc, mxs_lradc_irq_en_mask(lradc), LRADC_CTRL1);
+
+ for (i = 0; i < LRADC_MAX_DELAY_CHANS; i++)
+ mxs_lradc_reg_wrt(lradc, 0, LRADC_DELAY(i));
+}
+
+static const struct of_device_id mxs_lradc_dt_ids[] = {
+ { .compatible = "fsl,imx23-lradc", .data = (void *)IMX23_LRADC, },
+ { .compatible = "fsl,imx28-lradc", .data = (void *)IMX28_LRADC, },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, mxs_lradc_dt_ids);
+
+static int mxs_lradc_probe_touchscreen(struct mxs_lradc *lradc,
+ struct device_node *lradc_node)
+{
+ int ret;
+ u32 ts_wires = 0, adapt;
+
+ ret = of_property_read_u32(lradc_node, "fsl,lradc-touchscreen-wires",
+ &ts_wires);
+ if (ret)
+ return -ENODEV; /* touchscreen feature disabled */
+
+ switch (ts_wires) {
+ case 4:
+ lradc->use_touchscreen = MXS_LRADC_TOUCHSCREEN_4WIRE;
+ break;
+ case 5:
+ if (lradc->soc == IMX28_LRADC) {
+ lradc->use_touchscreen = MXS_LRADC_TOUCHSCREEN_5WIRE;
+ break;
+ }
+ /* fall through an error message for i.MX23 */
+ default:
+ dev_err(lradc->dev,
+ "Unsupported number of touchscreen wires (%d)\n",
+ ts_wires);
+ return -EINVAL;
+ }
+
+ if (of_property_read_u32(lradc_node, "fsl,ave-ctrl", &adapt)) {
+ lradc->over_sample_cnt = 4;
+ } else {
+ if (adapt < 1 || adapt > 32) {
+ dev_err(lradc->dev, "Invalid sample count (%u)\n",
+ adapt);
+ return -EINVAL;
+ }
+ lradc->over_sample_cnt = adapt;
+ }
+
+ if (of_property_read_u32(lradc_node, "fsl,ave-delay", &adapt)) {
+ lradc->over_sample_delay = 2;
+ } else {
+ if (adapt < 2 || adapt > LRADC_DELAY_DELAY_MASK + 1) {
+ dev_err(lradc->dev, "Invalid sample delay (%u)\n",
+ adapt);
+ return -EINVAL;
+ }
+ lradc->over_sample_delay = adapt;
+ }
+
+ if (of_property_read_u32(lradc_node, "fsl,settling", &adapt)) {
+ lradc->settling_delay = 10;
+ } else {
+ if (adapt < 1 || adapt > LRADC_DELAY_DELAY_MASK) {
+ dev_err(lradc->dev, "Invalid settling delay (%u)\n",
+ adapt);
+ return -EINVAL;
+ }
+ lradc->settling_delay = adapt;
+ }
+
+ return 0;
+}
+
+static int mxs_lradc_probe(struct platform_device *pdev)
+{
+ const struct of_device_id *of_id =
+ of_match_device(mxs_lradc_dt_ids, &pdev->dev);
+ const struct mxs_lradc_of_config *of_cfg =
+ &mxs_lradc_of_config[(enum mxs_lradc_id)of_id->data];
+ struct device *dev = &pdev->dev;
+ struct device_node *node = dev->of_node;
+ struct mxs_lradc *lradc;
+ struct iio_dev *iio;
+ struct resource *iores;
+ int ret = 0, touch_ret;
+ int i, s;
+ uint64_t scale_uv;
+
+ /* Allocate the IIO device. */
+ iio = devm_iio_device_alloc(dev, sizeof(*lradc));
+ if (!iio) {
+ dev_err(dev, "Failed to allocate IIO device\n");
+ return -ENOMEM;
+ }
+
+ lradc = iio_priv(iio);
+ lradc->soc = (enum mxs_lradc_id)of_id->data;
+
+ /* Grab the memory area */
+ iores = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ lradc->dev = &pdev->dev;
+ lradc->base = devm_ioremap_resource(dev, iores);
+ if (IS_ERR(lradc->base))
+ return PTR_ERR(lradc->base);
+
+ lradc->clk = devm_clk_get(&pdev->dev, NULL);
+ if (IS_ERR(lradc->clk)) {
+ dev_err(dev, "Failed to get the delay unit clock\n");
+ return PTR_ERR(lradc->clk);
+ }
+ ret = clk_prepare_enable(lradc->clk);
+ if (ret != 0) {
+ dev_err(dev, "Failed to enable the delay unit clock\n");
+ return ret;
+ }
+
+ touch_ret = mxs_lradc_probe_touchscreen(lradc, node);
+
+ if (touch_ret == 0)
+ lradc->buffer_vchans = BUFFER_VCHANS_LIMITED;
+ else
+ lradc->buffer_vchans = BUFFER_VCHANS_ALL;
+
+ /* Grab all IRQ sources */
+ for (i = 0; i < of_cfg->irq_count; i++) {
+ lradc->irq[i] = platform_get_irq(pdev, i);
+ if (lradc->irq[i] < 0) {
+ ret = lradc->irq[i];
+ goto err_clk;
+ }
+
+ ret = devm_request_irq(dev, lradc->irq[i],
+ mxs_lradc_handle_irq, 0,
+ of_cfg->irq_name[i], iio);
+ if (ret)
+ goto err_clk;
+ }
+
+ lradc->vref_mv = of_cfg->vref_mv;
+
+ platform_set_drvdata(pdev, iio);
+
+ init_completion(&lradc->completion);
+ mutex_init(&lradc->lock);
+
+ iio->name = pdev->name;
+ iio->dev.parent = &pdev->dev;
+ iio->info = &mxs_lradc_iio_info;
+ iio->modes = INDIO_DIRECT_MODE;
+ iio->channels = mxs_lradc_chan_spec;
+ iio->num_channels = ARRAY_SIZE(mxs_lradc_chan_spec);
+ iio->masklength = LRADC_MAX_TOTAL_CHANS;
+
+ ret = iio_triggered_buffer_setup(iio, &iio_pollfunc_store_time,
+ &mxs_lradc_trigger_handler,
+ &mxs_lradc_buffer_ops);
+ if (ret)
+ goto err_clk;
+
+ ret = mxs_lradc_trigger_init(iio);
+ if (ret)
+ goto err_trig;
+
+ /* Populate available ADC input ranges */
+ for (i = 0; i < LRADC_MAX_TOTAL_CHANS; i++) {
+ for (s = 0; s < ARRAY_SIZE(lradc->scale_avail[i]); s++) {
+ /*
+ * [s=0] = optional divider by two disabled (default)
+ * [s=1] = optional divider by two enabled
+ *
+ * The scale is calculated by doing:
+ * Vref >> (realbits - s)
+ * which multiplies by two on the second component
+ * of the array.
+ */
+ scale_uv = ((u64)lradc->vref_mv[i] * 100000000) >>
+ (LRADC_RESOLUTION - s);
+ lradc->scale_avail[i][s].nano =
+ do_div(scale_uv, 100000000) * 10;
+ lradc->scale_avail[i][s].integer = scale_uv;
+ }
+ }
+
+ /* Configure the hardware. */
+ ret = mxs_lradc_hw_init(lradc);
+ if (ret)
+ goto err_dev;
+
+ /* Register the touchscreen input device. */
+ if (touch_ret == 0) {
+ ret = mxs_lradc_ts_register(lradc);
+ if (ret)
+ goto err_ts_register;
+ }
+
+ /* Register IIO device. */
+ ret = iio_device_register(iio);
+ if (ret) {
+ dev_err(dev, "Failed to register IIO device\n");
+ goto err_ts;
+ }
+
+ return 0;
+
+err_ts:
+ mxs_lradc_ts_unregister(lradc);
+err_ts_register:
+ mxs_lradc_hw_stop(lradc);
+err_dev:
+ mxs_lradc_trigger_remove(iio);
+err_trig:
+ iio_triggered_buffer_cleanup(iio);
+err_clk:
+ clk_disable_unprepare(lradc->clk);
+ return ret;
+}
+
+static int mxs_lradc_remove(struct platform_device *pdev)
+{
+ struct iio_dev *iio = platform_get_drvdata(pdev);
+ struct mxs_lradc *lradc = iio_priv(iio);
+
+ iio_device_unregister(iio);
+ mxs_lradc_ts_unregister(lradc);
+ mxs_lradc_hw_stop(lradc);
+ mxs_lradc_trigger_remove(iio);
+ iio_triggered_buffer_cleanup(iio);
+
+ clk_disable_unprepare(lradc->clk);
+ return 0;
+}
+
+static struct platform_driver mxs_lradc_driver = {
+ .driver = {
+ .name = DRIVER_NAME,
+ .of_match_table = mxs_lradc_dt_ids,
+ },
+ .probe = mxs_lradc_probe,
+ .remove = mxs_lradc_remove,
+};
+
+module_platform_driver(mxs_lradc_driver);
+
+MODULE_AUTHOR("Marek Vasut <marex@denx.de>");
+MODULE_DESCRIPTION("Freescale i.MX28 LRADC driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:" DRIVER_NAME);
diff --git a/drivers/staging/iio/adc/spear_adc.c b/drivers/staging/iio/adc/spear_adc.c
new file mode 100644
index 000000000..c5382374d
--- /dev/null
+++ b/drivers/staging/iio/adc/spear_adc.c
@@ -0,0 +1,400 @@
+/*
+ * ST SPEAr ADC driver
+ *
+ * Copyright 2012 Stefan Roese <sr@denx.de>
+ *
+ * Licensed under the GPL-2.
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/io.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/completion.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+
+/* SPEAR registers definitions */
+#define SPEAR600_ADC_SCAN_RATE_LO(x) ((x) & 0xFFFF)
+#define SPEAR600_ADC_SCAN_RATE_HI(x) (((x) >> 0x10) & 0xFFFF)
+#define SPEAR_ADC_CLK_LOW(x) (((x) & 0xf) << 0)
+#define SPEAR_ADC_CLK_HIGH(x) (((x) & 0xf) << 4)
+
+/* Bit definitions for SPEAR_ADC_STATUS */
+#define SPEAR_ADC_STATUS_START_CONVERSION BIT(0)
+#define SPEAR_ADC_STATUS_CHANNEL_NUM(x) ((x) << 1)
+#define SPEAR_ADC_STATUS_ADC_ENABLE BIT(4)
+#define SPEAR_ADC_STATUS_AVG_SAMPLE(x) ((x) << 5)
+#define SPEAR_ADC_STATUS_VREF_INTERNAL BIT(9)
+
+#define SPEAR_ADC_DATA_MASK 0x03ff
+#define SPEAR_ADC_DATA_BITS 10
+
+#define SPEAR_ADC_MOD_NAME "spear-adc"
+
+#define SPEAR_ADC_CHANNEL_NUM 8
+
+#define SPEAR_ADC_CLK_MIN 2500000
+#define SPEAR_ADC_CLK_MAX 20000000
+
+struct adc_regs_spear3xx {
+ u32 status;
+ u32 average;
+ u32 scan_rate;
+ u32 clk; /* Not avail for 1340 & 1310 */
+ u32 ch_ctrl[SPEAR_ADC_CHANNEL_NUM];
+ u32 ch_data[SPEAR_ADC_CHANNEL_NUM];
+};
+
+struct chan_data {
+ u32 lsb;
+ u32 msb;
+};
+
+struct adc_regs_spear6xx {
+ u32 status;
+ u32 pad[2];
+ u32 clk;
+ u32 ch_ctrl[SPEAR_ADC_CHANNEL_NUM];
+ struct chan_data ch_data[SPEAR_ADC_CHANNEL_NUM];
+ u32 scan_rate_lo;
+ u32 scan_rate_hi;
+ struct chan_data average;
+};
+
+struct spear_adc_state {
+ struct device_node *np;
+ struct adc_regs_spear3xx __iomem *adc_base_spear3xx;
+ struct adc_regs_spear6xx __iomem *adc_base_spear6xx;
+ struct clk *clk;
+ struct completion completion;
+ u32 current_clk;
+ u32 sampling_freq;
+ u32 avg_samples;
+ u32 vref_external;
+ u32 value;
+};
+
+/*
+ * Functions to access some SPEAr ADC register. Abstracted into
+ * static inline functions, because of different register offsets
+ * on different SoC variants (SPEAr300 vs SPEAr600 etc).
+ */
+static void spear_adc_set_status(struct spear_adc_state *st, u32 val)
+{
+ __raw_writel(val, &st->adc_base_spear6xx->status);
+}
+
+static void spear_adc_set_clk(struct spear_adc_state *st, u32 val)
+{
+ u32 clk_high, clk_low, count;
+ u32 apb_clk = clk_get_rate(st->clk);
+
+ count = DIV_ROUND_UP(apb_clk, val);
+ clk_low = count / 2;
+ clk_high = count - clk_low;
+ st->current_clk = apb_clk / count;
+
+ __raw_writel(SPEAR_ADC_CLK_LOW(clk_low) | SPEAR_ADC_CLK_HIGH(clk_high),
+ &st->adc_base_spear6xx->clk);
+}
+
+static void spear_adc_set_ctrl(struct spear_adc_state *st, int n,
+ u32 val)
+{
+ __raw_writel(val, &st->adc_base_spear6xx->ch_ctrl[n]);
+}
+
+static u32 spear_adc_get_average(struct spear_adc_state *st)
+{
+ if (of_device_is_compatible(st->np, "st,spear600-adc")) {
+ return __raw_readl(&st->adc_base_spear6xx->average.msb) &
+ SPEAR_ADC_DATA_MASK;
+ } else {
+ return __raw_readl(&st->adc_base_spear3xx->average) &
+ SPEAR_ADC_DATA_MASK;
+ }
+}
+
+static void spear_adc_set_scanrate(struct spear_adc_state *st, u32 rate)
+{
+ if (of_device_is_compatible(st->np, "st,spear600-adc")) {
+ __raw_writel(SPEAR600_ADC_SCAN_RATE_LO(rate),
+ &st->adc_base_spear6xx->scan_rate_lo);
+ __raw_writel(SPEAR600_ADC_SCAN_RATE_HI(rate),
+ &st->adc_base_spear6xx->scan_rate_hi);
+ } else {
+ __raw_writel(rate, &st->adc_base_spear3xx->scan_rate);
+ }
+}
+
+static int spear_adc_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val,
+ int *val2,
+ long mask)
+{
+ struct spear_adc_state *st = iio_priv(indio_dev);
+ u32 status;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ mutex_lock(&indio_dev->mlock);
+
+ status = SPEAR_ADC_STATUS_CHANNEL_NUM(chan->channel) |
+ SPEAR_ADC_STATUS_AVG_SAMPLE(st->avg_samples) |
+ SPEAR_ADC_STATUS_START_CONVERSION |
+ SPEAR_ADC_STATUS_ADC_ENABLE;
+ if (st->vref_external == 0)
+ status |= SPEAR_ADC_STATUS_VREF_INTERNAL;
+
+ spear_adc_set_status(st, status);
+ wait_for_completion(&st->completion); /* set by ISR */
+ *val = st->value;
+
+ mutex_unlock(&indio_dev->mlock);
+
+ return IIO_VAL_INT;
+
+ case IIO_CHAN_INFO_SCALE:
+ *val = st->vref_external;
+ *val2 = SPEAR_ADC_DATA_BITS;
+ return IIO_VAL_FRACTIONAL_LOG2;
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ *val = st->current_clk;
+ return IIO_VAL_INT;
+ }
+
+ return -EINVAL;
+}
+
+static int spear_adc_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int val,
+ int val2,
+ long mask)
+{
+ struct spear_adc_state *st = iio_priv(indio_dev);
+ int ret = 0;
+
+ if (mask != IIO_CHAN_INFO_SAMP_FREQ)
+ return -EINVAL;
+
+ mutex_lock(&indio_dev->mlock);
+
+ if ((val < SPEAR_ADC_CLK_MIN) ||
+ (val > SPEAR_ADC_CLK_MAX) ||
+ (val2 != 0)) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ spear_adc_set_clk(st, val);
+
+out:
+ mutex_unlock(&indio_dev->mlock);
+ return ret;
+}
+
+#define SPEAR_ADC_CHAN(idx) { \
+ .type = IIO_VOLTAGE, \
+ .indexed = 1, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
+ .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ),\
+ .channel = idx, \
+}
+
+static const struct iio_chan_spec spear_adc_iio_channels[] = {
+ SPEAR_ADC_CHAN(0),
+ SPEAR_ADC_CHAN(1),
+ SPEAR_ADC_CHAN(2),
+ SPEAR_ADC_CHAN(3),
+ SPEAR_ADC_CHAN(4),
+ SPEAR_ADC_CHAN(5),
+ SPEAR_ADC_CHAN(6),
+ SPEAR_ADC_CHAN(7),
+};
+
+static irqreturn_t spear_adc_isr(int irq, void *dev_id)
+{
+ struct spear_adc_state *st = dev_id;
+
+ /* Read value to clear IRQ */
+ st->value = spear_adc_get_average(st);
+ complete(&st->completion);
+
+ return IRQ_HANDLED;
+}
+
+static int spear_adc_configure(struct spear_adc_state *st)
+{
+ int i;
+
+ /* Reset ADC core */
+ spear_adc_set_status(st, 0);
+ __raw_writel(0, &st->adc_base_spear6xx->clk);
+ for (i = 0; i < 8; i++)
+ spear_adc_set_ctrl(st, i, 0);
+ spear_adc_set_scanrate(st, 0);
+
+ spear_adc_set_clk(st, st->sampling_freq);
+
+ return 0;
+}
+
+static const struct iio_info spear_adc_info = {
+ .read_raw = &spear_adc_read_raw,
+ .write_raw = &spear_adc_write_raw,
+ .driver_module = THIS_MODULE,
+};
+
+static int spear_adc_probe(struct platform_device *pdev)
+{
+ struct device_node *np = pdev->dev.of_node;
+ struct device *dev = &pdev->dev;
+ struct spear_adc_state *st;
+ struct iio_dev *indio_dev = NULL;
+ int ret = -ENODEV;
+ int irq;
+
+ indio_dev = devm_iio_device_alloc(dev, sizeof(struct spear_adc_state));
+ if (!indio_dev) {
+ dev_err(dev, "failed allocating iio device\n");
+ return -ENOMEM;
+ }
+
+ st = iio_priv(indio_dev);
+ st->np = np;
+
+ /*
+ * SPEAr600 has a different register layout than other SPEAr SoC's
+ * (e.g. SPEAr3xx). Let's provide two register base addresses
+ * to support multi-arch kernels.
+ */
+ st->adc_base_spear6xx = of_iomap(np, 0);
+ if (!st->adc_base_spear6xx) {
+ dev_err(dev, "failed mapping memory\n");
+ return -ENOMEM;
+ }
+ st->adc_base_spear3xx =
+ (struct adc_regs_spear3xx __iomem *)st->adc_base_spear6xx;
+
+ st->clk = clk_get(dev, NULL);
+ if (IS_ERR(st->clk)) {
+ dev_err(dev, "failed getting clock\n");
+ goto errout1;
+ }
+
+ ret = clk_prepare_enable(st->clk);
+ if (ret) {
+ dev_err(dev, "failed enabling clock\n");
+ goto errout2;
+ }
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq <= 0) {
+ dev_err(dev, "failed getting interrupt resource\n");
+ ret = -EINVAL;
+ goto errout3;
+ }
+
+ ret = devm_request_irq(dev, irq, spear_adc_isr, 0, SPEAR_ADC_MOD_NAME,
+ st);
+ if (ret < 0) {
+ dev_err(dev, "failed requesting interrupt\n");
+ goto errout3;
+ }
+
+ if (of_property_read_u32(np, "sampling-frequency",
+ &st->sampling_freq)) {
+ dev_err(dev, "sampling-frequency missing in DT\n");
+ ret = -EINVAL;
+ goto errout3;
+ }
+
+ /*
+ * Optional avg_samples defaults to 0, resulting in single data
+ * conversion
+ */
+ of_property_read_u32(np, "average-samples", &st->avg_samples);
+
+ /*
+ * Optional vref_external defaults to 0, resulting in internal vref
+ * selection
+ */
+ of_property_read_u32(np, "vref-external", &st->vref_external);
+
+ spear_adc_configure(st);
+
+ platform_set_drvdata(pdev, indio_dev);
+
+ init_completion(&st->completion);
+
+ indio_dev->name = SPEAR_ADC_MOD_NAME;
+ indio_dev->dev.parent = dev;
+ indio_dev->info = &spear_adc_info;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->channels = spear_adc_iio_channels;
+ indio_dev->num_channels = ARRAY_SIZE(spear_adc_iio_channels);
+
+ ret = iio_device_register(indio_dev);
+ if (ret)
+ goto errout3;
+
+ dev_info(dev, "SPEAR ADC driver loaded, IRQ %d\n", irq);
+
+ return 0;
+
+errout3:
+ clk_disable_unprepare(st->clk);
+errout2:
+ clk_put(st->clk);
+errout1:
+ iounmap(st->adc_base_spear6xx);
+ return ret;
+}
+
+static int spear_adc_remove(struct platform_device *pdev)
+{
+ struct iio_dev *indio_dev = platform_get_drvdata(pdev);
+ struct spear_adc_state *st = iio_priv(indio_dev);
+
+ iio_device_unregister(indio_dev);
+ clk_disable_unprepare(st->clk);
+ clk_put(st->clk);
+ iounmap(st->adc_base_spear6xx);
+
+ return 0;
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id spear_adc_dt_ids[] = {
+ { .compatible = "st,spear600-adc", },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, spear_adc_dt_ids);
+#endif
+
+static struct platform_driver spear_adc_driver = {
+ .probe = spear_adc_probe,
+ .remove = spear_adc_remove,
+ .driver = {
+ .name = SPEAR_ADC_MOD_NAME,
+ .of_match_table = of_match_ptr(spear_adc_dt_ids),
+ },
+};
+
+module_platform_driver(spear_adc_driver);
+
+MODULE_AUTHOR("Stefan Roese <sr@denx.de>");
+MODULE_DESCRIPTION("SPEAr ADC driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/iio/addac/Kconfig b/drivers/staging/iio/addac/Kconfig
new file mode 100644
index 000000000..0ed7e13e2
--- /dev/null
+++ b/drivers/staging/iio/addac/Kconfig
@@ -0,0 +1,37 @@
+#
+# ADDAC drivers
+#
+menu "Analog digital bi-direction converters"
+
+config ADT7316
+ tristate "Analog Devices ADT7316/7/8 ADT7516/7/9 temperature sensor, ADC and DAC driver"
+ depends on GPIOLIB
+ help
+ Say yes here to build support for Analog Devices ADT7316, ADT7317, ADT7318
+ and ADT7516, ADT7517, ADT7519 temperature sensors, ADC and DAC.
+
+ To compile this driver as a module, choose M here: the module will
+ be called adt7316.
+
+config ADT7316_SPI
+ tristate "support SPI bus connection"
+ depends on SPI && ADT7316
+ default y
+ help
+ Say yes here to build SPI bus support for Analog Devices ADT7316/7/8
+ and ADT7516/7/9.
+
+ To compile this driver as a module, choose M here: the module will
+ be called adt7316_spi.
+
+config ADT7316_I2C
+ tristate "support I2C bus connection"
+ depends on I2C && ADT7316
+ help
+ Say yes here to build I2C bus support for Analog Devices ADT7316/7/8
+ and ADT7516/7/9.
+
+ To compile this driver as a module, choose M here: the module will
+ be called adt7316_i2c.
+
+endmenu
diff --git a/drivers/staging/iio/addac/Makefile b/drivers/staging/iio/addac/Makefile
new file mode 100644
index 000000000..4c7686133
--- /dev/null
+++ b/drivers/staging/iio/addac/Makefile
@@ -0,0 +1,7 @@
+#
+# Makefile for industrial I/O ADDAC drivers
+#
+
+obj-$(CONFIG_ADT7316) += adt7316.o
+obj-$(CONFIG_ADT7316_SPI) += adt7316-spi.o
+obj-$(CONFIG_ADT7316_I2C) += adt7316-i2c.o
diff --git a/drivers/staging/iio/addac/adt7316-i2c.c b/drivers/staging/iio/addac/adt7316-i2c.c
new file mode 100644
index 000000000..75ddd4f80
--- /dev/null
+++ b/drivers/staging/iio/addac/adt7316-i2c.c
@@ -0,0 +1,136 @@
+/*
+ * I2C bus driver for ADT7316/7/8 ADT7516/7/9 digital temperature
+ * sensor, ADC and DAC
+ *
+ * Copyright 2010 Analog Devices Inc.
+ *
+ * Licensed under the GPL-2 or later.
+ */
+
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+
+#include "adt7316.h"
+
+/*
+ * adt7316 register access by I2C
+ */
+static int adt7316_i2c_read(void *client, u8 reg, u8 *data)
+{
+ struct i2c_client *cl = client;
+ int ret = 0;
+
+ ret = i2c_smbus_write_byte(cl, reg);
+ if (ret < 0) {
+ dev_err(&cl->dev, "I2C fail to select reg\n");
+ return ret;
+ }
+
+ ret = i2c_smbus_read_byte(client);
+ if (ret < 0) {
+ dev_err(&cl->dev, "I2C read error\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static int adt7316_i2c_write(void *client, u8 reg, u8 data)
+{
+ struct i2c_client *cl = client;
+ int ret = 0;
+
+ ret = i2c_smbus_write_byte_data(cl, reg, data);
+ if (ret < 0)
+ dev_err(&cl->dev, "I2C write error\n");
+
+ return ret;
+}
+
+static int adt7316_i2c_multi_read(void *client, u8 reg, u8 count, u8 *data)
+{
+ struct i2c_client *cl = client;
+ int i, ret = 0;
+
+ if (count > ADT7316_REG_MAX_ADDR)
+ count = ADT7316_REG_MAX_ADDR;
+
+ for (i = 0; i < count; i++) {
+ ret = adt7316_i2c_read(cl, reg, &data[i]);
+ if (ret < 0) {
+ dev_err(&cl->dev, "I2C multi read error\n");
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static int adt7316_i2c_multi_write(void *client, u8 reg, u8 count, u8 *data)
+{
+ struct i2c_client *cl = client;
+ int i, ret = 0;
+
+ if (count > ADT7316_REG_MAX_ADDR)
+ count = ADT7316_REG_MAX_ADDR;
+
+ for (i = 0; i < count; i++) {
+ ret = adt7316_i2c_write(cl, reg, data[i]);
+ if (ret < 0) {
+ dev_err(&cl->dev, "I2C multi write error\n");
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * device probe and remove
+ */
+
+static int adt7316_i2c_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct adt7316_bus bus = {
+ .client = client,
+ .irq = client->irq,
+ .irq_flags = IRQF_TRIGGER_LOW,
+ .read = adt7316_i2c_read,
+ .write = adt7316_i2c_write,
+ .multi_read = adt7316_i2c_multi_read,
+ .multi_write = adt7316_i2c_multi_write,
+ };
+
+ return adt7316_probe(&client->dev, &bus, id->name);
+}
+
+static const struct i2c_device_id adt7316_i2c_id[] = {
+ { "adt7316", 0 },
+ { "adt7317", 0 },
+ { "adt7318", 0 },
+ { "adt7516", 0 },
+ { "adt7517", 0 },
+ { "adt7519", 0 },
+ { }
+};
+
+MODULE_DEVICE_TABLE(i2c, adt7316_i2c_id);
+
+static struct i2c_driver adt7316_driver = {
+ .driver = {
+ .name = "adt7316",
+ .pm = ADT7316_PM_OPS,
+ .owner = THIS_MODULE,
+ },
+ .probe = adt7316_i2c_probe,
+ .id_table = adt7316_i2c_id,
+};
+module_i2c_driver(adt7316_driver);
+
+MODULE_AUTHOR("Sonic Zhang <sonic.zhang@analog.com>");
+MODULE_DESCRIPTION("I2C bus driver for Analog Devices ADT7316/7/9 and ADT7516/7/8 digital temperature sensor, ADC and DAC");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/staging/iio/addac/adt7316-spi.c b/drivers/staging/iio/addac/adt7316-spi.c
new file mode 100644
index 000000000..e480abb72
--- /dev/null
+++ b/drivers/staging/iio/addac/adt7316-spi.c
@@ -0,0 +1,144 @@
+/*
+ * API bus driver for ADT7316/7/8 ADT7516/7/9 digital temperature
+ * sensor, ADC and DAC
+ *
+ * Copyright 2010 Analog Devices Inc.
+ *
+ * Licensed under the GPL-2 or later.
+ */
+
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/spi/spi.h>
+
+#include "adt7316.h"
+
+#define ADT7316_SPI_MAX_FREQ_HZ 5000000
+#define ADT7316_SPI_CMD_READ 0x91
+#define ADT7316_SPI_CMD_WRITE 0x90
+
+/*
+ * adt7316 register access by SPI
+ */
+
+static int adt7316_spi_multi_read(void *client, u8 reg, u8 count, u8 *data)
+{
+ struct spi_device *spi_dev = client;
+ u8 cmd[2];
+ int ret = 0;
+
+ if (count > ADT7316_REG_MAX_ADDR)
+ count = ADT7316_REG_MAX_ADDR;
+
+ cmd[0] = ADT7316_SPI_CMD_WRITE;
+ cmd[1] = reg;
+
+ ret = spi_write(spi_dev, cmd, 2);
+ if (ret < 0) {
+ dev_err(&spi_dev->dev, "SPI fail to select reg\n");
+ return ret;
+ }
+
+ cmd[0] = ADT7316_SPI_CMD_READ;
+
+ ret = spi_write_then_read(spi_dev, cmd, 1, data, count);
+ if (ret < 0) {
+ dev_err(&spi_dev->dev, "SPI read data error\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static int adt7316_spi_multi_write(void *client, u8 reg, u8 count, u8 *data)
+{
+ struct spi_device *spi_dev = client;
+ u8 buf[ADT7316_REG_MAX_ADDR + 2];
+ int i, ret = 0;
+
+ if (count > ADT7316_REG_MAX_ADDR)
+ count = ADT7316_REG_MAX_ADDR;
+
+ buf[0] = ADT7316_SPI_CMD_WRITE;
+ buf[1] = reg;
+ for (i = 0; i < count; i++)
+ buf[i + 2] = data[i];
+
+ ret = spi_write(spi_dev, buf, count + 2);
+ if (ret < 0) {
+ dev_err(&spi_dev->dev, "SPI write error\n");
+ return ret;
+ }
+
+ return ret;
+}
+
+static int adt7316_spi_read(void *client, u8 reg, u8 *data)
+{
+ return adt7316_spi_multi_read(client, reg, 1, data);
+}
+
+static int adt7316_spi_write(void *client, u8 reg, u8 val)
+{
+ return adt7316_spi_multi_write(client, reg, 1, &val);
+}
+
+/*
+ * device probe and remove
+ */
+
+static int adt7316_spi_probe(struct spi_device *spi_dev)
+{
+ struct adt7316_bus bus = {
+ .client = spi_dev,
+ .irq = spi_dev->irq,
+ .irq_flags = IRQF_TRIGGER_LOW,
+ .read = adt7316_spi_read,
+ .write = adt7316_spi_write,
+ .multi_read = adt7316_spi_multi_read,
+ .multi_write = adt7316_spi_multi_write,
+ };
+
+ /* don't exceed max specified SPI CLK frequency */
+ if (spi_dev->max_speed_hz > ADT7316_SPI_MAX_FREQ_HZ) {
+ dev_err(&spi_dev->dev, "SPI CLK %d Hz?\n",
+ spi_dev->max_speed_hz);
+ return -EINVAL;
+ }
+
+ /* switch from default I2C protocol to SPI protocol */
+ adt7316_spi_write(spi_dev, 0, 0);
+ adt7316_spi_write(spi_dev, 0, 0);
+ adt7316_spi_write(spi_dev, 0, 0);
+
+ return adt7316_probe(&spi_dev->dev, &bus, spi_dev->modalias);
+}
+
+static const struct spi_device_id adt7316_spi_id[] = {
+ { "adt7316", 0 },
+ { "adt7317", 0 },
+ { "adt7318", 0 },
+ { "adt7516", 0 },
+ { "adt7517", 0 },
+ { "adt7519", 0 },
+ { }
+};
+
+MODULE_DEVICE_TABLE(spi, adt7316_spi_id);
+
+static struct spi_driver adt7316_driver = {
+ .driver = {
+ .name = "adt7316",
+ .pm = ADT7316_PM_OPS,
+ .owner = THIS_MODULE,
+ },
+ .probe = adt7316_spi_probe,
+ .id_table = adt7316_spi_id,
+};
+module_spi_driver(adt7316_driver);
+
+MODULE_AUTHOR("Sonic Zhang <sonic.zhang@analog.com>");
+MODULE_DESCRIPTION("SPI bus driver for Analog Devices ADT7316/7/8 and ADT7516/7/9 digital temperature sensor, ADC and DAC");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/staging/iio/addac/adt7316.c b/drivers/staging/iio/addac/adt7316.c
new file mode 100644
index 000000000..5b11b42c0
--- /dev/null
+++ b/drivers/staging/iio/addac/adt7316.c
@@ -0,0 +1,2188 @@
+/*
+ * ADT7316 digital temperature sensor driver supporting ADT7316/7/8 ADT7516/7/9
+ *
+ *
+ * Copyright 2010 Analog Devices Inc.
+ *
+ * Licensed under the GPL-2 or later.
+ */
+
+#include <linux/interrupt.h>
+#include <linux/gpio.h>
+#include <linux/workqueue.h>
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/sysfs.h>
+#include <linux/list.h>
+#include <linux/i2c.h>
+#include <linux/rtc.h>
+#include <linux/module.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/events.h>
+#include <linux/iio/sysfs.h>
+#include "adt7316.h"
+
+/*
+ * ADT7316 registers definition
+ */
+#define ADT7316_INT_STAT1 0x0
+#define ADT7316_INT_STAT2 0x1
+#define ADT7316_LSB_IN_TEMP_VDD 0x3
+#define ADT7316_LSB_IN_TEMP_MASK 0x3
+#define ADT7316_LSB_VDD_MASK 0xC
+#define ADT7316_LSB_VDD_OFFSET 2
+#define ADT7316_LSB_EX_TEMP_AIN 0x4
+#define ADT7316_LSB_EX_TEMP_MASK 0x3
+#define ADT7516_LSB_AIN_SHIFT 2
+#define ADT7316_AD_MSB_DATA_BASE 0x6
+#define ADT7316_AD_MSB_DATA_REGS 3
+#define ADT7516_AD_MSB_DATA_REGS 6
+#define ADT7316_MSB_VDD 0x6
+#define ADT7316_MSB_IN_TEMP 0x7
+#define ADT7316_MSB_EX_TEMP 0x8
+#define ADT7516_MSB_AIN1 0x8
+#define ADT7516_MSB_AIN2 0x9
+#define ADT7516_MSB_AIN3 0xA
+#define ADT7516_MSB_AIN4 0xB
+#define ADT7316_DA_DATA_BASE 0x10
+#define ADT7316_DA_MSB_DATA_REGS 4
+#define ADT7316_LSB_DAC_A 0x10
+#define ADT7316_MSB_DAC_A 0x11
+#define ADT7316_LSB_DAC_B 0x12
+#define ADT7316_MSB_DAC_B 0x13
+#define ADT7316_LSB_DAC_C 0x14
+#define ADT7316_MSB_DAC_C 0x15
+#define ADT7316_LSB_DAC_D 0x16
+#define ADT7316_MSB_DAC_D 0x17
+#define ADT7316_CONFIG1 0x18
+#define ADT7316_CONFIG2 0x19
+#define ADT7316_CONFIG3 0x1A
+#define ADT7316_LDAC_CONFIG 0x1B
+#define ADT7316_DAC_CONFIG 0x1C
+#define ADT7316_INT_MASK1 0x1D
+#define ADT7316_INT_MASK2 0x1E
+#define ADT7316_IN_TEMP_OFFSET 0x1F
+#define ADT7316_EX_TEMP_OFFSET 0x20
+#define ADT7316_IN_ANALOG_TEMP_OFFSET 0x21
+#define ADT7316_EX_ANALOG_TEMP_OFFSET 0x22
+#define ADT7316_VDD_HIGH 0x23
+#define ADT7316_VDD_LOW 0x24
+#define ADT7316_IN_TEMP_HIGH 0x25
+#define ADT7316_IN_TEMP_LOW 0x26
+#define ADT7316_EX_TEMP_HIGH 0x27
+#define ADT7316_EX_TEMP_LOW 0x28
+#define ADT7516_AIN2_HIGH 0x2B
+#define ADT7516_AIN2_LOW 0x2C
+#define ADT7516_AIN3_HIGH 0x2D
+#define ADT7516_AIN3_LOW 0x2E
+#define ADT7516_AIN4_HIGH 0x2F
+#define ADT7516_AIN4_LOW 0x30
+#define ADT7316_DEVICE_ID 0x4D
+#define ADT7316_MANUFACTURE_ID 0x4E
+#define ADT7316_DEVICE_REV 0x4F
+#define ADT7316_SPI_LOCK_STAT 0x7F
+
+/*
+ * ADT7316 config1
+ */
+#define ADT7316_EN 0x1
+#define ADT7516_SEL_EX_TEMP 0x4
+#define ADT7516_SEL_AIN1_2_EX_TEMP_MASK 0x6
+#define ADT7516_SEL_AIN3 0x8
+#define ADT7316_INT_EN 0x20
+#define ADT7316_INT_POLARITY 0x40
+#define ADT7316_PD 0x80
+
+/*
+ * ADT7316 config2
+ */
+#define ADT7316_AD_SINGLE_CH_MASK 0x3
+#define ADT7516_AD_SINGLE_CH_MASK 0x7
+#define ADT7316_AD_SINGLE_CH_VDD 0
+#define ADT7316_AD_SINGLE_CH_IN 1
+#define ADT7316_AD_SINGLE_CH_EX 2
+#define ADT7516_AD_SINGLE_CH_AIN1 2
+#define ADT7516_AD_SINGLE_CH_AIN2 3
+#define ADT7516_AD_SINGLE_CH_AIN3 4
+#define ADT7516_AD_SINGLE_CH_AIN4 5
+#define ADT7316_AD_SINGLE_CH_MODE 0x10
+#define ADT7316_DISABLE_AVERAGING 0x20
+#define ADT7316_EN_SMBUS_TIMEOUT 0x40
+#define ADT7316_RESET 0x80
+
+/*
+ * ADT7316 config3
+ */
+#define ADT7316_ADCLK_22_5 0x1
+#define ADT7316_DA_HIGH_RESOLUTION 0x2
+#define ADT7316_DA_EN_VIA_DAC_LDCA 0x4
+#define ADT7516_AIN_IN_VREF 0x10
+#define ADT7316_EN_IN_TEMP_PROP_DACA 0x20
+#define ADT7316_EN_EX_TEMP_PROP_DACB 0x40
+
+/*
+ * ADT7316 DAC config
+ */
+#define ADT7316_DA_2VREF_CH_MASK 0xF
+#define ADT7316_DA_EN_MODE_MASK 0x30
+#define ADT7316_DA_EN_MODE_SINGLE 0x00
+#define ADT7316_DA_EN_MODE_AB_CD 0x10
+#define ADT7316_DA_EN_MODE_ABCD 0x20
+#define ADT7316_DA_EN_MODE_LDAC 0x30
+#define ADT7316_VREF_BYPASS_DAC_AB 0x40
+#define ADT7316_VREF_BYPASS_DAC_CD 0x80
+
+/*
+ * ADT7316 LDAC config
+ */
+#define ADT7316_LDAC_EN_DA_MASK 0xF
+#define ADT7316_DAC_IN_VREF 0x10
+#define ADT7516_DAC_AB_IN_VREF 0x10
+#define ADT7516_DAC_CD_IN_VREF 0x20
+#define ADT7516_DAC_IN_VREF_OFFSET 4
+#define ADT7516_DAC_IN_VREF_MASK 0x30
+
+/*
+ * ADT7316 INT_MASK2
+ */
+#define ADT7316_INT_MASK2_VDD 0x10
+
+/*
+ * ADT7316 value masks
+ */
+#define ADT7316_VALUE_MASK 0xfff
+#define ADT7316_T_VALUE_SIGN 0x400
+#define ADT7316_T_VALUE_FLOAT_OFFSET 2
+#define ADT7316_T_VALUE_FLOAT_MASK 0x2
+
+/*
+ * Chip ID
+ */
+#define ID_ADT7316 0x1
+#define ID_ADT7317 0x2
+#define ID_ADT7318 0x3
+#define ID_ADT7516 0x11
+#define ID_ADT7517 0x12
+#define ID_ADT7519 0x14
+
+#define ID_FAMILY_MASK 0xF0
+#define ID_ADT73XX 0x0
+#define ID_ADT75XX 0x10
+
+/*
+ * struct adt7316_chip_info - chip specific information
+ */
+
+struct adt7316_chip_info {
+ struct adt7316_bus bus;
+ u16 ldac_pin;
+ u16 int_mask; /* 0x2f */
+ u8 config1;
+ u8 config2;
+ u8 config3;
+ u8 dac_config; /* DAC config */
+ u8 ldac_config; /* LDAC config */
+ u8 dac_bits; /* 8, 10, 12 */
+ u8 id; /* chip id */
+};
+
+/*
+ * Logic interrupt mask for user application to enable
+ * interrupts.
+ */
+#define ADT7316_IN_TEMP_HIGH_INT_MASK 0x1
+#define ADT7316_IN_TEMP_LOW_INT_MASK 0x2
+#define ADT7316_EX_TEMP_HIGH_INT_MASK 0x4
+#define ADT7316_EX_TEMP_LOW_INT_MASK 0x8
+#define ADT7316_EX_TEMP_FAULT_INT_MASK 0x10
+#define ADT7516_AIN1_INT_MASK 0x4
+#define ADT7516_AIN2_INT_MASK 0x20
+#define ADT7516_AIN3_INT_MASK 0x40
+#define ADT7516_AIN4_INT_MASK 0x80
+#define ADT7316_VDD_INT_MASK 0x100
+#define ADT7316_TEMP_INT_MASK 0x1F
+#define ADT7516_AIN_INT_MASK 0xE0
+#define ADT7316_TEMP_AIN_INT_MASK \
+ (ADT7316_TEMP_INT_MASK)
+
+/*
+ * struct adt7316_chip_info - chip specific information
+ */
+
+struct adt7316_limit_regs {
+ u16 data_high;
+ u16 data_low;
+};
+
+static ssize_t adt7316_show_enabled(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct iio_dev *dev_info = dev_to_iio_dev(dev);
+ struct adt7316_chip_info *chip = iio_priv(dev_info);
+
+ return sprintf(buf, "%d\n", !!(chip->config1 & ADT7316_EN));
+}
+
+static ssize_t _adt7316_store_enabled(struct adt7316_chip_info *chip,
+ int enable)
+{
+ u8 config1;
+ int ret;
+
+ if (enable)
+ config1 = chip->config1 | ADT7316_EN;
+ else
+ config1 = chip->config1 & ~ADT7316_EN;
+
+ ret = chip->bus.write(chip->bus.client, ADT7316_CONFIG1, config1);
+ if (ret)
+ return -EIO;
+
+ chip->config1 = config1;
+
+ return ret;
+
+}
+
+static ssize_t adt7316_store_enabled(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t len)
+{
+ struct iio_dev *dev_info = dev_to_iio_dev(dev);
+ struct adt7316_chip_info *chip = iio_priv(dev_info);
+ int enable;
+
+ if (buf[0] == '1')
+ enable = 1;
+ else
+ enable = 0;
+
+ if (_adt7316_store_enabled(chip, enable) < 0)
+ return -EIO;
+
+ return len;
+}
+
+static IIO_DEVICE_ATTR(enabled, S_IRUGO | S_IWUSR,
+ adt7316_show_enabled,
+ adt7316_store_enabled,
+ 0);
+
+static ssize_t adt7316_show_select_ex_temp(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct iio_dev *dev_info = dev_to_iio_dev(dev);
+ struct adt7316_chip_info *chip = iio_priv(dev_info);
+
+ if ((chip->id & ID_FAMILY_MASK) != ID_ADT75XX)
+ return -EPERM;
+
+ return sprintf(buf, "%d\n", !!(chip->config1 & ADT7516_SEL_EX_TEMP));
+}
+
+static ssize_t adt7316_store_select_ex_temp(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t len)
+{
+ struct iio_dev *dev_info = dev_to_iio_dev(dev);
+ struct adt7316_chip_info *chip = iio_priv(dev_info);
+ u8 config1;
+ int ret;
+
+ if ((chip->id & ID_FAMILY_MASK) != ID_ADT75XX)
+ return -EPERM;
+
+ config1 = chip->config1 & (~ADT7516_SEL_EX_TEMP);
+ if (buf[0] == '1')
+ config1 |= ADT7516_SEL_EX_TEMP;
+
+ ret = chip->bus.write(chip->bus.client, ADT7316_CONFIG1, config1);
+ if (ret)
+ return -EIO;
+
+ chip->config1 = config1;
+
+ return len;
+}
+
+static IIO_DEVICE_ATTR(select_ex_temp, S_IRUGO | S_IWUSR,
+ adt7316_show_select_ex_temp,
+ adt7316_store_select_ex_temp,
+ 0);
+
+static ssize_t adt7316_show_mode(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct iio_dev *dev_info = dev_to_iio_dev(dev);
+ struct adt7316_chip_info *chip = iio_priv(dev_info);
+
+ if (chip->config2 & ADT7316_AD_SINGLE_CH_MODE)
+ return sprintf(buf, "single_channel\n");
+
+ return sprintf(buf, "round_robin\n");
+}
+
+static ssize_t adt7316_store_mode(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t len)
+{
+ struct iio_dev *dev_info = dev_to_iio_dev(dev);
+ struct adt7316_chip_info *chip = iio_priv(dev_info);
+ u8 config2;
+ int ret;
+
+ config2 = chip->config2 & (~ADT7316_AD_SINGLE_CH_MODE);
+ if (!memcmp(buf, "single_channel", 14))
+ config2 |= ADT7316_AD_SINGLE_CH_MODE;
+
+ ret = chip->bus.write(chip->bus.client, ADT7316_CONFIG2, config2);
+ if (ret)
+ return -EIO;
+
+ chip->config2 = config2;
+
+ return len;
+}
+
+static IIO_DEVICE_ATTR(mode, S_IRUGO | S_IWUSR,
+ adt7316_show_mode,
+ adt7316_store_mode,
+ 0);
+
+static ssize_t adt7316_show_all_modes(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ return sprintf(buf, "single_channel\nround_robin\n");
+}
+
+static IIO_DEVICE_ATTR(all_modes, S_IRUGO, adt7316_show_all_modes, NULL, 0);
+
+static ssize_t adt7316_show_ad_channel(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct iio_dev *dev_info = dev_to_iio_dev(dev);
+ struct adt7316_chip_info *chip = iio_priv(dev_info);
+
+ if (!(chip->config2 & ADT7316_AD_SINGLE_CH_MODE))
+ return -EPERM;
+
+ switch (chip->config2 & ADT7516_AD_SINGLE_CH_MASK) {
+ case ADT7316_AD_SINGLE_CH_VDD:
+ return sprintf(buf, "0 - VDD\n");
+ case ADT7316_AD_SINGLE_CH_IN:
+ return sprintf(buf, "1 - Internal Temperature\n");
+ case ADT7316_AD_SINGLE_CH_EX:
+ if (((chip->id & ID_FAMILY_MASK) == ID_ADT75XX) &&
+ (chip->config1 & ADT7516_SEL_AIN1_2_EX_TEMP_MASK) == 0)
+ return sprintf(buf, "2 - AIN1\n");
+
+ return sprintf(buf, "2 - External Temperature\n");
+ case ADT7516_AD_SINGLE_CH_AIN2:
+ if ((chip->config1 & ADT7516_SEL_AIN1_2_EX_TEMP_MASK) == 0)
+ return sprintf(buf, "3 - AIN2\n");
+
+ return sprintf(buf, "N/A\n");
+ case ADT7516_AD_SINGLE_CH_AIN3:
+ if (chip->config1 & ADT7516_SEL_AIN3)
+ return sprintf(buf, "4 - AIN3\n");
+
+ return sprintf(buf, "N/A\n");
+ case ADT7516_AD_SINGLE_CH_AIN4:
+ return sprintf(buf, "5 - AIN4\n");
+ default:
+ return sprintf(buf, "N/A\n");
+ }
+}
+
+static ssize_t adt7316_store_ad_channel(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t len)
+{
+ struct iio_dev *dev_info = dev_to_iio_dev(dev);
+ struct adt7316_chip_info *chip = iio_priv(dev_info);
+ u8 config2;
+ u8 data;
+ int ret;
+
+ if (!(chip->config2 & ADT7316_AD_SINGLE_CH_MODE))
+ return -EPERM;
+
+ ret = kstrtou8(buf, 10, &data);
+ if (ret)
+ return -EINVAL;
+
+ if ((chip->id & ID_FAMILY_MASK) == ID_ADT75XX) {
+ if (data > 5)
+ return -EINVAL;
+
+ config2 = chip->config2 & (~ADT7516_AD_SINGLE_CH_MASK);
+ } else {
+ if (data > 2)
+ return -EINVAL;
+
+ config2 = chip->config2 & (~ADT7316_AD_SINGLE_CH_MASK);
+ }
+
+
+ config2 |= data;
+
+ ret = chip->bus.write(chip->bus.client, ADT7316_CONFIG2, config2);
+ if (ret)
+ return -EIO;
+
+ chip->config2 = config2;
+
+ return len;
+}
+
+static IIO_DEVICE_ATTR(ad_channel, S_IRUGO | S_IWUSR,
+ adt7316_show_ad_channel,
+ adt7316_store_ad_channel,
+ 0);
+
+static ssize_t adt7316_show_all_ad_channels(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct iio_dev *dev_info = dev_to_iio_dev(dev);
+ struct adt7316_chip_info *chip = iio_priv(dev_info);
+
+ if (!(chip->config2 & ADT7316_AD_SINGLE_CH_MODE))
+ return -EPERM;
+
+ if ((chip->id & ID_FAMILY_MASK) == ID_ADT75XX)
+ return sprintf(buf, "0 - VDD\n1 - Internal Temperature\n"
+ "2 - External Temperature or AIN1\n"
+ "3 - AIN2\n4 - AIN3\n5 - AIN4\n");
+ else
+ return sprintf(buf, "0 - VDD\n1 - Internal Temperature\n"
+ "2 - External Temperature\n");
+}
+
+static IIO_DEVICE_ATTR(all_ad_channels, S_IRUGO,
+ adt7316_show_all_ad_channels, NULL, 0);
+
+static ssize_t adt7316_show_disable_averaging(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct iio_dev *dev_info = dev_to_iio_dev(dev);
+ struct adt7316_chip_info *chip = iio_priv(dev_info);
+
+ return sprintf(buf, "%d\n",
+ !!(chip->config2 & ADT7316_DISABLE_AVERAGING));
+}
+
+static ssize_t adt7316_store_disable_averaging(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t len)
+{
+ struct iio_dev *dev_info = dev_to_iio_dev(dev);
+ struct adt7316_chip_info *chip = iio_priv(dev_info);
+ u8 config2;
+ int ret;
+
+ config2 = chip->config2 & (~ADT7316_DISABLE_AVERAGING);
+ if (buf[0] == '1')
+ config2 |= ADT7316_DISABLE_AVERAGING;
+
+ ret = chip->bus.write(chip->bus.client, ADT7316_CONFIG2, config2);
+ if (ret)
+ return -EIO;
+
+ chip->config2 = config2;
+
+ return len;
+}
+
+static IIO_DEVICE_ATTR(disable_averaging, S_IRUGO | S_IWUSR,
+ adt7316_show_disable_averaging,
+ adt7316_store_disable_averaging,
+ 0);
+
+static ssize_t adt7316_show_enable_smbus_timeout(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct iio_dev *dev_info = dev_to_iio_dev(dev);
+ struct adt7316_chip_info *chip = iio_priv(dev_info);
+
+ return sprintf(buf, "%d\n",
+ !!(chip->config2 & ADT7316_EN_SMBUS_TIMEOUT));
+}
+
+static ssize_t adt7316_store_enable_smbus_timeout(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t len)
+{
+ struct iio_dev *dev_info = dev_to_iio_dev(dev);
+ struct adt7316_chip_info *chip = iio_priv(dev_info);
+ u8 config2;
+ int ret;
+
+ config2 = chip->config2 & (~ADT7316_EN_SMBUS_TIMEOUT);
+ if (buf[0] == '1')
+ config2 |= ADT7316_EN_SMBUS_TIMEOUT;
+
+ ret = chip->bus.write(chip->bus.client, ADT7316_CONFIG2, config2);
+ if (ret)
+ return -EIO;
+
+ chip->config2 = config2;
+
+ return len;
+}
+
+static IIO_DEVICE_ATTR(enable_smbus_timeout, S_IRUGO | S_IWUSR,
+ adt7316_show_enable_smbus_timeout,
+ adt7316_store_enable_smbus_timeout,
+ 0);
+
+static ssize_t adt7316_show_powerdown(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct iio_dev *dev_info = dev_to_iio_dev(dev);
+ struct adt7316_chip_info *chip = iio_priv(dev_info);
+
+ return sprintf(buf, "%d\n", !!(chip->config1 & ADT7316_PD));
+}
+
+static ssize_t adt7316_store_powerdown(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t len)
+{
+ struct iio_dev *dev_info = dev_to_iio_dev(dev);
+ struct adt7316_chip_info *chip = iio_priv(dev_info);
+ u8 config1;
+ int ret;
+
+ config1 = chip->config1 & (~ADT7316_PD);
+ if (buf[0] == '1')
+ config1 |= ADT7316_PD;
+
+ ret = chip->bus.write(chip->bus.client, ADT7316_CONFIG1, config1);
+ if (ret)
+ return -EIO;
+
+ chip->config1 = config1;
+
+ return len;
+}
+
+static IIO_DEVICE_ATTR(powerdown, S_IRUGO | S_IWUSR,
+ adt7316_show_powerdown,
+ adt7316_store_powerdown,
+ 0);
+
+static ssize_t adt7316_show_fast_ad_clock(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct iio_dev *dev_info = dev_to_iio_dev(dev);
+ struct adt7316_chip_info *chip = iio_priv(dev_info);
+
+ return sprintf(buf, "%d\n", !!(chip->config3 & ADT7316_ADCLK_22_5));
+}
+
+static ssize_t adt7316_store_fast_ad_clock(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t len)
+{
+ struct iio_dev *dev_info = dev_to_iio_dev(dev);
+ struct adt7316_chip_info *chip = iio_priv(dev_info);
+ u8 config3;
+ int ret;
+
+ config3 = chip->config3 & (~ADT7316_ADCLK_22_5);
+ if (buf[0] == '1')
+ config3 |= ADT7316_ADCLK_22_5;
+
+ ret = chip->bus.write(chip->bus.client, ADT7316_CONFIG3, config3);
+ if (ret)
+ return -EIO;
+
+ chip->config3 = config3;
+
+ return len;
+}
+
+static IIO_DEVICE_ATTR(fast_ad_clock, S_IRUGO | S_IWUSR,
+ adt7316_show_fast_ad_clock,
+ adt7316_store_fast_ad_clock,
+ 0);
+
+static ssize_t adt7316_show_da_high_resolution(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct iio_dev *dev_info = dev_to_iio_dev(dev);
+ struct adt7316_chip_info *chip = iio_priv(dev_info);
+
+ if (chip->config3 & ADT7316_DA_HIGH_RESOLUTION) {
+ if (chip->id == ID_ADT7316 || chip->id == ID_ADT7516)
+ return sprintf(buf, "1 (12 bits)\n");
+ else if (chip->id == ID_ADT7317 || chip->id == ID_ADT7517)
+ return sprintf(buf, "1 (10 bits)\n");
+ }
+
+ return sprintf(buf, "0 (8 bits)\n");
+}
+
+static ssize_t adt7316_store_da_high_resolution(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t len)
+{
+ struct iio_dev *dev_info = dev_to_iio_dev(dev);
+ struct adt7316_chip_info *chip = iio_priv(dev_info);
+ u8 config3;
+ int ret;
+
+ chip->dac_bits = 8;
+
+ if (buf[0] == '1') {
+ config3 = chip->config3 | ADT7316_DA_HIGH_RESOLUTION;
+ if (chip->id == ID_ADT7316 || chip->id == ID_ADT7516)
+ chip->dac_bits = 12;
+ else if (chip->id == ID_ADT7317 || chip->id == ID_ADT7517)
+ chip->dac_bits = 10;
+ } else
+ config3 = chip->config3 & (~ADT7316_DA_HIGH_RESOLUTION);
+
+ ret = chip->bus.write(chip->bus.client, ADT7316_CONFIG3, config3);
+ if (ret)
+ return -EIO;
+
+ chip->config3 = config3;
+
+ return len;
+}
+
+static IIO_DEVICE_ATTR(da_high_resolution, S_IRUGO | S_IWUSR,
+ adt7316_show_da_high_resolution,
+ adt7316_store_da_high_resolution,
+ 0);
+
+static ssize_t adt7316_show_AIN_internal_Vref(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct iio_dev *dev_info = dev_to_iio_dev(dev);
+ struct adt7316_chip_info *chip = iio_priv(dev_info);
+
+ if ((chip->id & ID_FAMILY_MASK) != ID_ADT75XX)
+ return -EPERM;
+
+ return sprintf(buf, "%d\n",
+ !!(chip->config3 & ADT7516_AIN_IN_VREF));
+}
+
+static ssize_t adt7316_store_AIN_internal_Vref(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t len)
+{
+ struct iio_dev *dev_info = dev_to_iio_dev(dev);
+ struct adt7316_chip_info *chip = iio_priv(dev_info);
+ u8 config3;
+ int ret;
+
+ if ((chip->id & ID_FAMILY_MASK) != ID_ADT75XX)
+ return -EPERM;
+
+ if (buf[0] != '1')
+ config3 = chip->config3 & (~ADT7516_AIN_IN_VREF);
+ else
+ config3 = chip->config3 | ADT7516_AIN_IN_VREF;
+
+ ret = chip->bus.write(chip->bus.client, ADT7316_CONFIG3, config3);
+ if (ret)
+ return -EIO;
+
+ chip->config3 = config3;
+
+ return len;
+}
+
+static IIO_DEVICE_ATTR(AIN_internal_Vref, S_IRUGO | S_IWUSR,
+ adt7316_show_AIN_internal_Vref,
+ adt7316_store_AIN_internal_Vref,
+ 0);
+
+
+static ssize_t adt7316_show_enable_prop_DACA(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct iio_dev *dev_info = dev_to_iio_dev(dev);
+ struct adt7316_chip_info *chip = iio_priv(dev_info);
+
+ return sprintf(buf, "%d\n",
+ !!(chip->config3 & ADT7316_EN_IN_TEMP_PROP_DACA));
+}
+
+static ssize_t adt7316_store_enable_prop_DACA(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t len)
+{
+ struct iio_dev *dev_info = dev_to_iio_dev(dev);
+ struct adt7316_chip_info *chip = iio_priv(dev_info);
+ u8 config3;
+ int ret;
+
+ config3 = chip->config3 & (~ADT7316_EN_IN_TEMP_PROP_DACA);
+ if (buf[0] == '1')
+ config3 |= ADT7316_EN_IN_TEMP_PROP_DACA;
+
+ ret = chip->bus.write(chip->bus.client, ADT7316_CONFIG3, config3);
+ if (ret)
+ return -EIO;
+
+ chip->config3 = config3;
+
+ return len;
+}
+
+static IIO_DEVICE_ATTR(enable_proportion_DACA, S_IRUGO | S_IWUSR,
+ adt7316_show_enable_prop_DACA,
+ adt7316_store_enable_prop_DACA,
+ 0);
+
+static ssize_t adt7316_show_enable_prop_DACB(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct iio_dev *dev_info = dev_to_iio_dev(dev);
+ struct adt7316_chip_info *chip = iio_priv(dev_info);
+
+ return sprintf(buf, "%d\n",
+ !!(chip->config3 & ADT7316_EN_EX_TEMP_PROP_DACB));
+}
+
+static ssize_t adt7316_store_enable_prop_DACB(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t len)
+{
+ struct iio_dev *dev_info = dev_to_iio_dev(dev);
+ struct adt7316_chip_info *chip = iio_priv(dev_info);
+ u8 config3;
+ int ret;
+
+ config3 = chip->config3 & (~ADT7316_EN_EX_TEMP_PROP_DACB);
+ if (buf[0] == '1')
+ config3 |= ADT7316_EN_EX_TEMP_PROP_DACB;
+
+ ret = chip->bus.write(chip->bus.client, ADT7316_CONFIG3, config3);
+ if (ret)
+ return -EIO;
+
+ chip->config3 = config3;
+
+ return len;
+}
+
+static IIO_DEVICE_ATTR(enable_proportion_DACB, S_IRUGO | S_IWUSR,
+ adt7316_show_enable_prop_DACB,
+ adt7316_store_enable_prop_DACB,
+ 0);
+
+static ssize_t adt7316_show_DAC_2Vref_ch_mask(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct iio_dev *dev_info = dev_to_iio_dev(dev);
+ struct adt7316_chip_info *chip = iio_priv(dev_info);
+
+ return sprintf(buf, "0x%x\n",
+ chip->dac_config & ADT7316_DA_2VREF_CH_MASK);
+}
+
+static ssize_t adt7316_store_DAC_2Vref_ch_mask(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t len)
+{
+ struct iio_dev *dev_info = dev_to_iio_dev(dev);
+ struct adt7316_chip_info *chip = iio_priv(dev_info);
+ u8 dac_config;
+ u8 data;
+ int ret;
+
+ ret = kstrtou8(buf, 16, &data);
+ if (ret || data > ADT7316_DA_2VREF_CH_MASK)
+ return -EINVAL;
+
+ dac_config = chip->dac_config & (~ADT7316_DA_2VREF_CH_MASK);
+ dac_config |= data;
+
+ ret = chip->bus.write(chip->bus.client, ADT7316_DAC_CONFIG, dac_config);
+ if (ret)
+ return -EIO;
+
+ chip->dac_config = dac_config;
+
+ return len;
+}
+
+static IIO_DEVICE_ATTR(DAC_2Vref_channels_mask, S_IRUGO | S_IWUSR,
+ adt7316_show_DAC_2Vref_ch_mask,
+ adt7316_store_DAC_2Vref_ch_mask,
+ 0);
+
+static ssize_t adt7316_show_DAC_update_mode(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct iio_dev *dev_info = dev_to_iio_dev(dev);
+ struct adt7316_chip_info *chip = iio_priv(dev_info);
+
+ if (!(chip->config3 & ADT7316_DA_EN_VIA_DAC_LDCA))
+ return sprintf(buf, "manual\n");
+
+ switch (chip->dac_config & ADT7316_DA_EN_MODE_MASK) {
+ case ADT7316_DA_EN_MODE_SINGLE:
+ return sprintf(buf,
+ "0 - auto at any MSB DAC writing\n");
+ case ADT7316_DA_EN_MODE_AB_CD:
+ return sprintf(buf,
+ "1 - auto at MSB DAC AB and CD writing\n");
+ case ADT7316_DA_EN_MODE_ABCD:
+ return sprintf(buf,
+ "2 - auto at MSB DAC ABCD writing\n");
+ default: /* ADT7316_DA_EN_MODE_LDAC */
+ return sprintf(buf, "3 - manual\n");
+ }
+}
+
+static ssize_t adt7316_store_DAC_update_mode(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t len)
+{
+ struct iio_dev *dev_info = dev_to_iio_dev(dev);
+ struct adt7316_chip_info *chip = iio_priv(dev_info);
+ u8 dac_config;
+ u8 data;
+ int ret;
+
+ if (!(chip->config3 & ADT7316_DA_EN_VIA_DAC_LDCA))
+ return -EPERM;
+
+ ret = kstrtou8(buf, 10, &data);
+ if (ret || data > ADT7316_DA_EN_MODE_MASK)
+ return -EINVAL;
+
+ dac_config = chip->dac_config & (~ADT7316_DA_EN_MODE_MASK);
+ dac_config |= data;
+
+ ret = chip->bus.write(chip->bus.client, ADT7316_DAC_CONFIG, dac_config);
+ if (ret)
+ return -EIO;
+
+ chip->dac_config = dac_config;
+
+ return len;
+}
+
+static IIO_DEVICE_ATTR(DAC_update_mode, S_IRUGO | S_IWUSR,
+ adt7316_show_DAC_update_mode,
+ adt7316_store_DAC_update_mode,
+ 0);
+
+static ssize_t adt7316_show_all_DAC_update_modes(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct iio_dev *dev_info = dev_to_iio_dev(dev);
+ struct adt7316_chip_info *chip = iio_priv(dev_info);
+
+ if (chip->config3 & ADT7316_DA_EN_VIA_DAC_LDCA)
+ return sprintf(buf, "0 - auto at any MSB DAC writing\n"
+ "1 - auto at MSB DAC AB and CD writing\n"
+ "2 - auto at MSB DAC ABCD writing\n"
+ "3 - manual\n");
+ else
+ return sprintf(buf, "manual\n");
+}
+
+static IIO_DEVICE_ATTR(all_DAC_update_modes, S_IRUGO,
+ adt7316_show_all_DAC_update_modes, NULL, 0);
+
+
+static ssize_t adt7316_store_update_DAC(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t len)
+{
+ struct iio_dev *dev_info = dev_to_iio_dev(dev);
+ struct adt7316_chip_info *chip = iio_priv(dev_info);
+ u8 ldac_config;
+ u8 data;
+ int ret;
+
+ if (chip->config3 & ADT7316_DA_EN_VIA_DAC_LDCA) {
+ if ((chip->dac_config & ADT7316_DA_EN_MODE_MASK) !=
+ ADT7316_DA_EN_MODE_LDAC)
+ return -EPERM;
+
+ ret = kstrtou8(buf, 16, &data);
+ if (ret || data > ADT7316_LDAC_EN_DA_MASK)
+ return -EINVAL;
+
+ ldac_config = chip->ldac_config & (~ADT7316_LDAC_EN_DA_MASK);
+ ldac_config |= data;
+
+ ret = chip->bus.write(chip->bus.client, ADT7316_LDAC_CONFIG,
+ ldac_config);
+ if (ret)
+ return -EIO;
+ } else {
+ gpio_set_value(chip->ldac_pin, 0);
+ gpio_set_value(chip->ldac_pin, 1);
+ }
+
+ return len;
+}
+
+static IIO_DEVICE_ATTR(update_DAC, S_IRUGO | S_IWUSR,
+ NULL,
+ adt7316_store_update_DAC,
+ 0);
+
+static ssize_t adt7316_show_DA_AB_Vref_bypass(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct iio_dev *dev_info = dev_to_iio_dev(dev);
+ struct adt7316_chip_info *chip = iio_priv(dev_info);
+
+ if ((chip->id & ID_FAMILY_MASK) == ID_ADT75XX)
+ return -EPERM;
+
+ return sprintf(buf, "%d\n",
+ !!(chip->dac_config & ADT7316_VREF_BYPASS_DAC_AB));
+}
+
+static ssize_t adt7316_store_DA_AB_Vref_bypass(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t len)
+{
+ struct iio_dev *dev_info = dev_to_iio_dev(dev);
+ struct adt7316_chip_info *chip = iio_priv(dev_info);
+ u8 dac_config;
+ int ret;
+
+ if ((chip->id & ID_FAMILY_MASK) == ID_ADT75XX)
+ return -EPERM;
+
+ dac_config = chip->dac_config & (~ADT7316_VREF_BYPASS_DAC_AB);
+ if (buf[0] == '1')
+ dac_config |= ADT7316_VREF_BYPASS_DAC_AB;
+
+ ret = chip->bus.write(chip->bus.client, ADT7316_DAC_CONFIG, dac_config);
+ if (ret)
+ return -EIO;
+
+ chip->dac_config = dac_config;
+
+ return len;
+}
+
+static IIO_DEVICE_ATTR(DA_AB_Vref_bypass, S_IRUGO | S_IWUSR,
+ adt7316_show_DA_AB_Vref_bypass,
+ adt7316_store_DA_AB_Vref_bypass,
+ 0);
+
+static ssize_t adt7316_show_DA_CD_Vref_bypass(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct iio_dev *dev_info = dev_to_iio_dev(dev);
+ struct adt7316_chip_info *chip = iio_priv(dev_info);
+
+ if ((chip->id & ID_FAMILY_MASK) == ID_ADT75XX)
+ return -EPERM;
+
+ return sprintf(buf, "%d\n",
+ !!(chip->dac_config & ADT7316_VREF_BYPASS_DAC_CD));
+}
+
+static ssize_t adt7316_store_DA_CD_Vref_bypass(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t len)
+{
+ struct iio_dev *dev_info = dev_to_iio_dev(dev);
+ struct adt7316_chip_info *chip = iio_priv(dev_info);
+ u8 dac_config;
+ int ret;
+
+ if ((chip->id & ID_FAMILY_MASK) == ID_ADT75XX)
+ return -EPERM;
+
+ dac_config = chip->dac_config & (~ADT7316_VREF_BYPASS_DAC_CD);
+ if (buf[0] == '1')
+ dac_config |= ADT7316_VREF_BYPASS_DAC_CD;
+
+ ret = chip->bus.write(chip->bus.client, ADT7316_DAC_CONFIG, dac_config);
+ if (ret)
+ return -EIO;
+
+ chip->dac_config = dac_config;
+
+ return len;
+}
+
+static IIO_DEVICE_ATTR(DA_CD_Vref_bypass, S_IRUGO | S_IWUSR,
+ adt7316_show_DA_CD_Vref_bypass,
+ adt7316_store_DA_CD_Vref_bypass,
+ 0);
+
+static ssize_t adt7316_show_DAC_internal_Vref(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct iio_dev *dev_info = dev_to_iio_dev(dev);
+ struct adt7316_chip_info *chip = iio_priv(dev_info);
+
+ if ((chip->id & ID_FAMILY_MASK) == ID_ADT75XX)
+ return sprintf(buf, "0x%x\n",
+ (chip->dac_config & ADT7516_DAC_IN_VREF_MASK) >>
+ ADT7516_DAC_IN_VREF_OFFSET);
+ else
+ return sprintf(buf, "%d\n",
+ !!(chip->dac_config & ADT7316_DAC_IN_VREF));
+}
+
+static ssize_t adt7316_store_DAC_internal_Vref(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t len)
+{
+ struct iio_dev *dev_info = dev_to_iio_dev(dev);
+ struct adt7316_chip_info *chip = iio_priv(dev_info);
+ u8 ldac_config;
+ u8 data;
+ int ret;
+
+ if ((chip->id & ID_FAMILY_MASK) == ID_ADT75XX) {
+ ret = kstrtou8(buf, 16, &data);
+ if (ret || data > 3)
+ return -EINVAL;
+
+ ldac_config = chip->ldac_config & (~ADT7516_DAC_IN_VREF_MASK);
+ if (data & 0x1)
+ ldac_config |= ADT7516_DAC_AB_IN_VREF;
+ else if (data & 0x2)
+ ldac_config |= ADT7516_DAC_CD_IN_VREF;
+ } else {
+ ret = kstrtou8(buf, 16, &data);
+ if (ret)
+ return -EINVAL;
+
+ ldac_config = chip->ldac_config & (~ADT7316_DAC_IN_VREF);
+ if (data)
+ ldac_config = chip->ldac_config | ADT7316_DAC_IN_VREF;
+ }
+
+ ret = chip->bus.write(chip->bus.client, ADT7316_LDAC_CONFIG,
+ ldac_config);
+ if (ret)
+ return -EIO;
+
+ chip->ldac_config = ldac_config;
+
+ return len;
+}
+
+static IIO_DEVICE_ATTR(DAC_internal_Vref, S_IRUGO | S_IWUSR,
+ adt7316_show_DAC_internal_Vref,
+ adt7316_store_DAC_internal_Vref,
+ 0);
+
+static ssize_t adt7316_show_ad(struct adt7316_chip_info *chip,
+ int channel, char *buf)
+{
+ u16 data;
+ u8 msb, lsb;
+ char sign = ' ';
+ int ret;
+
+ if ((chip->config2 & ADT7316_AD_SINGLE_CH_MODE) &&
+ channel != (chip->config2 & ADT7516_AD_SINGLE_CH_MASK))
+ return -EPERM;
+
+ switch (channel) {
+ case ADT7316_AD_SINGLE_CH_IN:
+ ret = chip->bus.read(chip->bus.client,
+ ADT7316_LSB_IN_TEMP_VDD, &lsb);
+ if (ret)
+ return -EIO;
+
+ ret = chip->bus.read(chip->bus.client,
+ ADT7316_AD_MSB_DATA_BASE + channel, &msb);
+ if (ret)
+ return -EIO;
+
+ data = msb << ADT7316_T_VALUE_FLOAT_OFFSET;
+ data |= lsb & ADT7316_LSB_IN_TEMP_MASK;
+ break;
+ case ADT7316_AD_SINGLE_CH_VDD:
+ ret = chip->bus.read(chip->bus.client,
+ ADT7316_LSB_IN_TEMP_VDD, &lsb);
+ if (ret)
+ return -EIO;
+
+ ret = chip->bus.read(chip->bus.client,
+
+ ADT7316_AD_MSB_DATA_BASE + channel, &msb);
+ if (ret)
+ return -EIO;
+
+ data = msb << ADT7316_T_VALUE_FLOAT_OFFSET;
+ data |= (lsb & ADT7316_LSB_VDD_MASK) >> ADT7316_LSB_VDD_OFFSET;
+ return sprintf(buf, "%d\n", data);
+ default: /* ex_temp and ain */
+ ret = chip->bus.read(chip->bus.client,
+ ADT7316_LSB_EX_TEMP_AIN, &lsb);
+ if (ret)
+ return -EIO;
+
+ ret = chip->bus.read(chip->bus.client,
+ ADT7316_AD_MSB_DATA_BASE + channel, &msb);
+ if (ret)
+ return -EIO;
+
+ data = msb << ADT7316_T_VALUE_FLOAT_OFFSET;
+ data |= lsb & (ADT7316_LSB_EX_TEMP_MASK <<
+ (ADT7516_LSB_AIN_SHIFT * (channel -
+ (ADT7316_MSB_EX_TEMP - ADT7316_AD_MSB_DATA_BASE))));
+
+ if ((chip->id & ID_FAMILY_MASK) == ID_ADT75XX)
+ return sprintf(buf, "%d\n", data);
+
+ break;
+ }
+
+ if (data & ADT7316_T_VALUE_SIGN) {
+ /* convert supplement to positive value */
+ data = (ADT7316_T_VALUE_SIGN << 1) - data;
+ sign = '-';
+ }
+
+ return sprintf(buf, "%c%d.%.2d\n", sign,
+ (data >> ADT7316_T_VALUE_FLOAT_OFFSET),
+ (data & ADT7316_T_VALUE_FLOAT_MASK) * 25);
+}
+
+static ssize_t adt7316_show_VDD(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct iio_dev *dev_info = dev_to_iio_dev(dev);
+ struct adt7316_chip_info *chip = iio_priv(dev_info);
+
+ return adt7316_show_ad(chip, ADT7316_AD_SINGLE_CH_VDD, buf);
+}
+static IIO_DEVICE_ATTR(VDD, S_IRUGO, adt7316_show_VDD, NULL, 0);
+
+static ssize_t adt7316_show_in_temp(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct iio_dev *dev_info = dev_to_iio_dev(dev);
+ struct adt7316_chip_info *chip = iio_priv(dev_info);
+
+ return adt7316_show_ad(chip, ADT7316_AD_SINGLE_CH_IN, buf);
+}
+
+static IIO_DEVICE_ATTR(in_temp, S_IRUGO, adt7316_show_in_temp, NULL, 0);
+
+static ssize_t adt7316_show_ex_temp_AIN1(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct iio_dev *dev_info = dev_to_iio_dev(dev);
+ struct adt7316_chip_info *chip = iio_priv(dev_info);
+
+ return adt7316_show_ad(chip, ADT7316_AD_SINGLE_CH_EX, buf);
+}
+
+static IIO_DEVICE_ATTR(ex_temp_AIN1, S_IRUGO, adt7316_show_ex_temp_AIN1,
+ NULL, 0);
+static IIO_DEVICE_ATTR(ex_temp, S_IRUGO, adt7316_show_ex_temp_AIN1, NULL, 0);
+
+static ssize_t adt7316_show_AIN2(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct iio_dev *dev_info = dev_to_iio_dev(dev);
+ struct adt7316_chip_info *chip = iio_priv(dev_info);
+
+ return adt7316_show_ad(chip, ADT7516_AD_SINGLE_CH_AIN2, buf);
+}
+static IIO_DEVICE_ATTR(AIN2, S_IRUGO, adt7316_show_AIN2, NULL, 0);
+
+static ssize_t adt7316_show_AIN3(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct iio_dev *dev_info = dev_to_iio_dev(dev);
+ struct adt7316_chip_info *chip = iio_priv(dev_info);
+
+ return adt7316_show_ad(chip, ADT7516_AD_SINGLE_CH_AIN3, buf);
+}
+static IIO_DEVICE_ATTR(AIN3, S_IRUGO, adt7316_show_AIN3, NULL, 0);
+
+static ssize_t adt7316_show_AIN4(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct iio_dev *dev_info = dev_to_iio_dev(dev);
+ struct adt7316_chip_info *chip = iio_priv(dev_info);
+
+ return adt7316_show_ad(chip, ADT7516_AD_SINGLE_CH_AIN4, buf);
+}
+static IIO_DEVICE_ATTR(AIN4, S_IRUGO, adt7316_show_AIN4, NULL, 0);
+
+static ssize_t adt7316_show_temp_offset(struct adt7316_chip_info *chip,
+ int offset_addr, char *buf)
+{
+ int data;
+ u8 val;
+ int ret;
+
+ ret = chip->bus.read(chip->bus.client, offset_addr, &val);
+ if (ret)
+ return -EIO;
+
+ data = (int)val;
+ if (val & 0x80)
+ data -= 256;
+
+ return sprintf(buf, "%d\n", data);
+}
+
+static ssize_t adt7316_store_temp_offset(struct adt7316_chip_info *chip,
+ int offset_addr, const char *buf, size_t len)
+{
+ int data;
+ u8 val;
+ int ret;
+
+ ret = kstrtoint(buf, 10, &data);
+ if (ret || data > 127 || data < -128)
+ return -EINVAL;
+
+ if (data < 0)
+ data += 256;
+
+ val = (u8)data;
+
+ ret = chip->bus.write(chip->bus.client, offset_addr, val);
+ if (ret)
+ return -EIO;
+
+ return len;
+}
+
+static ssize_t adt7316_show_in_temp_offset(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct iio_dev *dev_info = dev_to_iio_dev(dev);
+ struct adt7316_chip_info *chip = iio_priv(dev_info);
+
+ return adt7316_show_temp_offset(chip, ADT7316_IN_TEMP_OFFSET, buf);
+}
+
+static ssize_t adt7316_store_in_temp_offset(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t len)
+{
+ struct iio_dev *dev_info = dev_to_iio_dev(dev);
+ struct adt7316_chip_info *chip = iio_priv(dev_info);
+
+ return adt7316_store_temp_offset(chip, ADT7316_IN_TEMP_OFFSET, buf,
+ len);
+}
+
+static IIO_DEVICE_ATTR(in_temp_offset, S_IRUGO | S_IWUSR,
+ adt7316_show_in_temp_offset,
+ adt7316_store_in_temp_offset, 0);
+
+static ssize_t adt7316_show_ex_temp_offset(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct iio_dev *dev_info = dev_to_iio_dev(dev);
+ struct adt7316_chip_info *chip = iio_priv(dev_info);
+
+ return adt7316_show_temp_offset(chip, ADT7316_EX_TEMP_OFFSET, buf);
+}
+
+static ssize_t adt7316_store_ex_temp_offset(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t len)
+{
+ struct iio_dev *dev_info = dev_to_iio_dev(dev);
+ struct adt7316_chip_info *chip = iio_priv(dev_info);
+
+ return adt7316_store_temp_offset(chip, ADT7316_EX_TEMP_OFFSET, buf,
+ len);
+}
+
+static IIO_DEVICE_ATTR(ex_temp_offset, S_IRUGO | S_IWUSR,
+ adt7316_show_ex_temp_offset,
+ adt7316_store_ex_temp_offset, 0);
+
+static ssize_t adt7316_show_in_analog_temp_offset(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct iio_dev *dev_info = dev_to_iio_dev(dev);
+ struct adt7316_chip_info *chip = iio_priv(dev_info);
+
+ return adt7316_show_temp_offset(chip,
+ ADT7316_IN_ANALOG_TEMP_OFFSET, buf);
+}
+
+static ssize_t adt7316_store_in_analog_temp_offset(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t len)
+{
+ struct iio_dev *dev_info = dev_to_iio_dev(dev);
+ struct adt7316_chip_info *chip = iio_priv(dev_info);
+
+ return adt7316_store_temp_offset(chip,
+ ADT7316_IN_ANALOG_TEMP_OFFSET, buf, len);
+}
+
+static IIO_DEVICE_ATTR(in_analog_temp_offset, S_IRUGO | S_IWUSR,
+ adt7316_show_in_analog_temp_offset,
+ adt7316_store_in_analog_temp_offset, 0);
+
+static ssize_t adt7316_show_ex_analog_temp_offset(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct iio_dev *dev_info = dev_to_iio_dev(dev);
+ struct adt7316_chip_info *chip = iio_priv(dev_info);
+
+ return adt7316_show_temp_offset(chip,
+ ADT7316_EX_ANALOG_TEMP_OFFSET, buf);
+}
+
+static ssize_t adt7316_store_ex_analog_temp_offset(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t len)
+{
+ struct iio_dev *dev_info = dev_to_iio_dev(dev);
+ struct adt7316_chip_info *chip = iio_priv(dev_info);
+
+ return adt7316_store_temp_offset(chip,
+ ADT7316_EX_ANALOG_TEMP_OFFSET, buf, len);
+}
+
+static IIO_DEVICE_ATTR(ex_analog_temp_offset, S_IRUGO | S_IWUSR,
+ adt7316_show_ex_analog_temp_offset,
+ adt7316_store_ex_analog_temp_offset, 0);
+
+static ssize_t adt7316_show_DAC(struct adt7316_chip_info *chip,
+ int channel, char *buf)
+{
+ u16 data;
+ u8 msb, lsb, offset;
+ int ret;
+
+ if (channel >= ADT7316_DA_MSB_DATA_REGS ||
+ (channel == 0 &&
+ (chip->config3 & ADT7316_EN_IN_TEMP_PROP_DACA)) ||
+ (channel == 1 &&
+ (chip->config3 & ADT7316_EN_EX_TEMP_PROP_DACB)))
+ return -EPERM;
+
+ offset = chip->dac_bits - 8;
+
+ if (chip->dac_bits > 8) {
+ ret = chip->bus.read(chip->bus.client,
+ ADT7316_DA_DATA_BASE + channel * 2, &lsb);
+ if (ret)
+ return -EIO;
+ }
+
+ ret = chip->bus.read(chip->bus.client,
+ ADT7316_DA_DATA_BASE + 1 + channel * 2, &msb);
+ if (ret)
+ return -EIO;
+
+ data = (msb << offset) + (lsb & ((1 << offset) - 1));
+
+ return sprintf(buf, "%d\n", data);
+}
+
+static ssize_t adt7316_store_DAC(struct adt7316_chip_info *chip,
+ int channel, const char *buf, size_t len)
+{
+ u8 msb, lsb, offset;
+ u16 data;
+ int ret;
+
+ if (channel >= ADT7316_DA_MSB_DATA_REGS ||
+ (channel == 0 &&
+ (chip->config3 & ADT7316_EN_IN_TEMP_PROP_DACA)) ||
+ (channel == 1 &&
+ (chip->config3 & ADT7316_EN_EX_TEMP_PROP_DACB)))
+ return -EPERM;
+
+ offset = chip->dac_bits - 8;
+
+ ret = kstrtou16(buf, 10, &data);
+ if (ret || data >= (1 << chip->dac_bits))
+ return -EINVAL;
+
+ if (chip->dac_bits > 8) {
+ lsb = data & (1 << offset);
+ ret = chip->bus.write(chip->bus.client,
+ ADT7316_DA_DATA_BASE + channel * 2, lsb);
+ if (ret)
+ return -EIO;
+ }
+
+ msb = data >> offset;
+ ret = chip->bus.write(chip->bus.client,
+ ADT7316_DA_DATA_BASE + 1 + channel * 2, msb);
+ if (ret)
+ return -EIO;
+
+ return len;
+}
+
+static ssize_t adt7316_show_DAC_A(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct iio_dev *dev_info = dev_to_iio_dev(dev);
+ struct adt7316_chip_info *chip = iio_priv(dev_info);
+
+ return adt7316_show_DAC(chip, 0, buf);
+}
+
+static ssize_t adt7316_store_DAC_A(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t len)
+{
+ struct iio_dev *dev_info = dev_to_iio_dev(dev);
+ struct adt7316_chip_info *chip = iio_priv(dev_info);
+
+ return adt7316_store_DAC(chip, 0, buf, len);
+}
+
+static IIO_DEVICE_ATTR(DAC_A, S_IRUGO | S_IWUSR, adt7316_show_DAC_A,
+ adt7316_store_DAC_A, 0);
+
+static ssize_t adt7316_show_DAC_B(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct iio_dev *dev_info = dev_to_iio_dev(dev);
+ struct adt7316_chip_info *chip = iio_priv(dev_info);
+
+ return adt7316_show_DAC(chip, 1, buf);
+}
+
+static ssize_t adt7316_store_DAC_B(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t len)
+{
+ struct iio_dev *dev_info = dev_to_iio_dev(dev);
+ struct adt7316_chip_info *chip = iio_priv(dev_info);
+
+ return adt7316_store_DAC(chip, 1, buf, len);
+}
+
+static IIO_DEVICE_ATTR(DAC_B, S_IRUGO | S_IWUSR, adt7316_show_DAC_B,
+ adt7316_store_DAC_B, 0);
+
+static ssize_t adt7316_show_DAC_C(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct iio_dev *dev_info = dev_to_iio_dev(dev);
+ struct adt7316_chip_info *chip = iio_priv(dev_info);
+
+ return adt7316_show_DAC(chip, 2, buf);
+}
+
+static ssize_t adt7316_store_DAC_C(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t len)
+{
+ struct iio_dev *dev_info = dev_to_iio_dev(dev);
+ struct adt7316_chip_info *chip = iio_priv(dev_info);
+
+ return adt7316_store_DAC(chip, 2, buf, len);
+}
+
+static IIO_DEVICE_ATTR(DAC_C, S_IRUGO | S_IWUSR, adt7316_show_DAC_C,
+ adt7316_store_DAC_C, 0);
+
+static ssize_t adt7316_show_DAC_D(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct iio_dev *dev_info = dev_to_iio_dev(dev);
+ struct adt7316_chip_info *chip = iio_priv(dev_info);
+
+ return adt7316_show_DAC(chip, 3, buf);
+}
+
+static ssize_t adt7316_store_DAC_D(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t len)
+{
+ struct iio_dev *dev_info = dev_to_iio_dev(dev);
+ struct adt7316_chip_info *chip = iio_priv(dev_info);
+
+ return adt7316_store_DAC(chip, 3, buf, len);
+}
+
+static IIO_DEVICE_ATTR(DAC_D, S_IRUGO | S_IWUSR, adt7316_show_DAC_D,
+ adt7316_store_DAC_D, 0);
+
+static ssize_t adt7316_show_device_id(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct iio_dev *dev_info = dev_to_iio_dev(dev);
+ struct adt7316_chip_info *chip = iio_priv(dev_info);
+ u8 id;
+ int ret;
+
+ ret = chip->bus.read(chip->bus.client, ADT7316_DEVICE_ID, &id);
+ if (ret)
+ return -EIO;
+
+ return sprintf(buf, "%d\n", id);
+}
+
+static IIO_DEVICE_ATTR(device_id, S_IRUGO, adt7316_show_device_id, NULL, 0);
+
+static ssize_t adt7316_show_manufactorer_id(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct iio_dev *dev_info = dev_to_iio_dev(dev);
+ struct adt7316_chip_info *chip = iio_priv(dev_info);
+ u8 id;
+ int ret;
+
+ ret = chip->bus.read(chip->bus.client, ADT7316_MANUFACTURE_ID, &id);
+ if (ret)
+ return -EIO;
+
+ return sprintf(buf, "%d\n", id);
+}
+
+static IIO_DEVICE_ATTR(manufactorer_id, S_IRUGO,
+ adt7316_show_manufactorer_id, NULL, 0);
+
+static ssize_t adt7316_show_device_rev(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct iio_dev *dev_info = dev_to_iio_dev(dev);
+ struct adt7316_chip_info *chip = iio_priv(dev_info);
+ u8 rev;
+ int ret;
+
+ ret = chip->bus.read(chip->bus.client, ADT7316_DEVICE_REV, &rev);
+ if (ret)
+ return -EIO;
+
+ return sprintf(buf, "%d\n", rev);
+}
+
+static IIO_DEVICE_ATTR(device_rev, S_IRUGO, adt7316_show_device_rev, NULL, 0);
+
+static ssize_t adt7316_show_bus_type(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct iio_dev *dev_info = dev_to_iio_dev(dev);
+ struct adt7316_chip_info *chip = iio_priv(dev_info);
+ u8 stat;
+ int ret;
+
+ ret = chip->bus.read(chip->bus.client, ADT7316_SPI_LOCK_STAT, &stat);
+ if (ret)
+ return -EIO;
+
+ if (stat)
+ return sprintf(buf, "spi\n");
+
+ return sprintf(buf, "i2c\n");
+}
+
+static IIO_DEVICE_ATTR(bus_type, S_IRUGO, adt7316_show_bus_type, NULL, 0);
+
+static struct attribute *adt7316_attributes[] = {
+ &iio_dev_attr_all_modes.dev_attr.attr,
+ &iio_dev_attr_mode.dev_attr.attr,
+ &iio_dev_attr_enabled.dev_attr.attr,
+ &iio_dev_attr_ad_channel.dev_attr.attr,
+ &iio_dev_attr_all_ad_channels.dev_attr.attr,
+ &iio_dev_attr_disable_averaging.dev_attr.attr,
+ &iio_dev_attr_enable_smbus_timeout.dev_attr.attr,
+ &iio_dev_attr_powerdown.dev_attr.attr,
+ &iio_dev_attr_fast_ad_clock.dev_attr.attr,
+ &iio_dev_attr_da_high_resolution.dev_attr.attr,
+ &iio_dev_attr_enable_proportion_DACA.dev_attr.attr,
+ &iio_dev_attr_enable_proportion_DACB.dev_attr.attr,
+ &iio_dev_attr_DAC_2Vref_channels_mask.dev_attr.attr,
+ &iio_dev_attr_DAC_update_mode.dev_attr.attr,
+ &iio_dev_attr_all_DAC_update_modes.dev_attr.attr,
+ &iio_dev_attr_update_DAC.dev_attr.attr,
+ &iio_dev_attr_DA_AB_Vref_bypass.dev_attr.attr,
+ &iio_dev_attr_DA_CD_Vref_bypass.dev_attr.attr,
+ &iio_dev_attr_DAC_internal_Vref.dev_attr.attr,
+ &iio_dev_attr_VDD.dev_attr.attr,
+ &iio_dev_attr_in_temp.dev_attr.attr,
+ &iio_dev_attr_ex_temp.dev_attr.attr,
+ &iio_dev_attr_in_temp_offset.dev_attr.attr,
+ &iio_dev_attr_ex_temp_offset.dev_attr.attr,
+ &iio_dev_attr_in_analog_temp_offset.dev_attr.attr,
+ &iio_dev_attr_ex_analog_temp_offset.dev_attr.attr,
+ &iio_dev_attr_DAC_A.dev_attr.attr,
+ &iio_dev_attr_DAC_B.dev_attr.attr,
+ &iio_dev_attr_DAC_C.dev_attr.attr,
+ &iio_dev_attr_DAC_D.dev_attr.attr,
+ &iio_dev_attr_device_id.dev_attr.attr,
+ &iio_dev_attr_manufactorer_id.dev_attr.attr,
+ &iio_dev_attr_device_rev.dev_attr.attr,
+ &iio_dev_attr_bus_type.dev_attr.attr,
+ NULL,
+};
+
+static const struct attribute_group adt7316_attribute_group = {
+ .attrs = adt7316_attributes,
+};
+
+static struct attribute *adt7516_attributes[] = {
+ &iio_dev_attr_all_modes.dev_attr.attr,
+ &iio_dev_attr_mode.dev_attr.attr,
+ &iio_dev_attr_select_ex_temp.dev_attr.attr,
+ &iio_dev_attr_enabled.dev_attr.attr,
+ &iio_dev_attr_ad_channel.dev_attr.attr,
+ &iio_dev_attr_all_ad_channels.dev_attr.attr,
+ &iio_dev_attr_disable_averaging.dev_attr.attr,
+ &iio_dev_attr_enable_smbus_timeout.dev_attr.attr,
+ &iio_dev_attr_powerdown.dev_attr.attr,
+ &iio_dev_attr_fast_ad_clock.dev_attr.attr,
+ &iio_dev_attr_AIN_internal_Vref.dev_attr.attr,
+ &iio_dev_attr_da_high_resolution.dev_attr.attr,
+ &iio_dev_attr_enable_proportion_DACA.dev_attr.attr,
+ &iio_dev_attr_enable_proportion_DACB.dev_attr.attr,
+ &iio_dev_attr_DAC_2Vref_channels_mask.dev_attr.attr,
+ &iio_dev_attr_DAC_update_mode.dev_attr.attr,
+ &iio_dev_attr_all_DAC_update_modes.dev_attr.attr,
+ &iio_dev_attr_update_DAC.dev_attr.attr,
+ &iio_dev_attr_DA_AB_Vref_bypass.dev_attr.attr,
+ &iio_dev_attr_DA_CD_Vref_bypass.dev_attr.attr,
+ &iio_dev_attr_DAC_internal_Vref.dev_attr.attr,
+ &iio_dev_attr_VDD.dev_attr.attr,
+ &iio_dev_attr_in_temp.dev_attr.attr,
+ &iio_dev_attr_ex_temp_AIN1.dev_attr.attr,
+ &iio_dev_attr_AIN2.dev_attr.attr,
+ &iio_dev_attr_AIN3.dev_attr.attr,
+ &iio_dev_attr_AIN4.dev_attr.attr,
+ &iio_dev_attr_in_temp_offset.dev_attr.attr,
+ &iio_dev_attr_ex_temp_offset.dev_attr.attr,
+ &iio_dev_attr_in_analog_temp_offset.dev_attr.attr,
+ &iio_dev_attr_ex_analog_temp_offset.dev_attr.attr,
+ &iio_dev_attr_DAC_A.dev_attr.attr,
+ &iio_dev_attr_DAC_B.dev_attr.attr,
+ &iio_dev_attr_DAC_C.dev_attr.attr,
+ &iio_dev_attr_DAC_D.dev_attr.attr,
+ &iio_dev_attr_device_id.dev_attr.attr,
+ &iio_dev_attr_manufactorer_id.dev_attr.attr,
+ &iio_dev_attr_device_rev.dev_attr.attr,
+ &iio_dev_attr_bus_type.dev_attr.attr,
+ NULL,
+};
+
+static const struct attribute_group adt7516_attribute_group = {
+ .attrs = adt7516_attributes,
+};
+
+static irqreturn_t adt7316_event_handler(int irq, void *private)
+{
+ struct iio_dev *indio_dev = private;
+ struct adt7316_chip_info *chip = iio_priv(indio_dev);
+ u8 stat1, stat2;
+ int ret;
+ s64 time;
+
+ ret = chip->bus.read(chip->bus.client, ADT7316_INT_STAT1, &stat1);
+ if (!ret) {
+ if ((chip->id & ID_FAMILY_MASK) != ID_ADT75XX)
+ stat1 &= 0x1F;
+
+ time = iio_get_time_ns();
+ if (stat1 & (1 << 0))
+ iio_push_event(indio_dev,
+ IIO_UNMOD_EVENT_CODE(IIO_TEMP, 0,
+ IIO_EV_TYPE_THRESH,
+ IIO_EV_DIR_RISING),
+ time);
+ if (stat1 & (1 << 1))
+ iio_push_event(indio_dev,
+ IIO_UNMOD_EVENT_CODE(IIO_TEMP, 0,
+ IIO_EV_TYPE_THRESH,
+ IIO_EV_DIR_FALLING),
+ time);
+ if (stat1 & (1 << 2))
+ iio_push_event(indio_dev,
+ IIO_UNMOD_EVENT_CODE(IIO_TEMP, 1,
+ IIO_EV_TYPE_THRESH,
+ IIO_EV_DIR_RISING),
+ time);
+ if (stat1 & (1 << 3))
+ iio_push_event(indio_dev,
+ IIO_UNMOD_EVENT_CODE(IIO_TEMP, 1,
+ IIO_EV_TYPE_THRESH,
+ IIO_EV_DIR_FALLING),
+ time);
+ if (stat1 & (1 << 5))
+ iio_push_event(indio_dev,
+ IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE, 1,
+ IIO_EV_TYPE_THRESH,
+ IIO_EV_DIR_EITHER),
+ time);
+ if (stat1 & (1 << 6))
+ iio_push_event(indio_dev,
+ IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE, 2,
+ IIO_EV_TYPE_THRESH,
+ IIO_EV_DIR_EITHER),
+ time);
+ if (stat1 & (1 << 7))
+ iio_push_event(indio_dev,
+ IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE, 3,
+ IIO_EV_TYPE_THRESH,
+ IIO_EV_DIR_EITHER),
+ time);
+ }
+ ret = chip->bus.read(chip->bus.client, ADT7316_INT_STAT2, &stat2);
+ if (!ret) {
+ if (stat2 & ADT7316_INT_MASK2_VDD)
+ iio_push_event(indio_dev,
+ IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE,
+ 0,
+ IIO_EV_TYPE_THRESH,
+ IIO_EV_DIR_RISING),
+ iio_get_time_ns());
+ }
+
+ return IRQ_HANDLED;
+}
+
+/*
+ * Show mask of enabled interrupts in Hex.
+ */
+static ssize_t adt7316_show_int_mask(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct iio_dev *dev_info = dev_to_iio_dev(dev);
+ struct adt7316_chip_info *chip = iio_priv(dev_info);
+
+ return sprintf(buf, "0x%x\n", chip->int_mask);
+}
+
+/*
+ * Set 1 to the mask in Hex to enabled interrupts.
+ */
+static ssize_t adt7316_set_int_mask(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t len)
+{
+ struct iio_dev *dev_info = dev_to_iio_dev(dev);
+ struct adt7316_chip_info *chip = iio_priv(dev_info);
+ u16 data;
+ int ret;
+ u8 mask;
+
+ ret = kstrtou16(buf, 16, &data);
+ if (ret || data >= ADT7316_VDD_INT_MASK + 1)
+ return -EINVAL;
+
+ if (data & ADT7316_VDD_INT_MASK)
+ mask = 0; /* enable vdd int */
+ else
+ mask = ADT7316_INT_MASK2_VDD; /* disable vdd int */
+
+ ret = chip->bus.write(chip->bus.client, ADT7316_INT_MASK2, mask);
+ if (!ret) {
+ chip->int_mask &= ~ADT7316_VDD_INT_MASK;
+ chip->int_mask |= data & ADT7316_VDD_INT_MASK;
+ }
+
+ if (data & ADT7316_TEMP_AIN_INT_MASK) {
+ if ((chip->id & ID_FAMILY_MASK) == ID_ADT73XX)
+ /* mask in reg is opposite, set 1 to disable */
+ mask = (~data) & ADT7316_TEMP_INT_MASK;
+ else
+ /* mask in reg is opposite, set 1 to disable */
+ mask = (~data) & ADT7316_TEMP_AIN_INT_MASK;
+ }
+ ret = chip->bus.write(chip->bus.client, ADT7316_INT_MASK1, mask);
+
+ chip->int_mask = mask;
+
+ return len;
+}
+static inline ssize_t adt7316_show_ad_bound(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
+ struct iio_dev *dev_info = dev_to_iio_dev(dev);
+ struct adt7316_chip_info *chip = iio_priv(dev_info);
+ u8 val;
+ int data;
+ int ret;
+
+ if ((chip->id & ID_FAMILY_MASK) == ID_ADT73XX &&
+ this_attr->address > ADT7316_EX_TEMP_LOW)
+ return -EPERM;
+
+ ret = chip->bus.read(chip->bus.client, this_attr->address, &val);
+ if (ret)
+ return -EIO;
+
+ data = (int)val;
+
+ if (!((chip->id & ID_FAMILY_MASK) == ID_ADT75XX &&
+ (chip->config1 & ADT7516_SEL_AIN1_2_EX_TEMP_MASK) == 0)) {
+ if (data & 0x80)
+ data -= 256;
+ }
+
+ return sprintf(buf, "%d\n", data);
+}
+
+static inline ssize_t adt7316_set_ad_bound(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t len)
+{
+ struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
+ struct iio_dev *dev_info = dev_to_iio_dev(dev);
+ struct adt7316_chip_info *chip = iio_priv(dev_info);
+ int data;
+ u8 val;
+ int ret;
+
+ if ((chip->id & ID_FAMILY_MASK) == ID_ADT73XX &&
+ this_attr->address > ADT7316_EX_TEMP_LOW)
+ return -EPERM;
+
+ ret = kstrtoint(buf, 10, &data);
+ if (ret)
+ return -EINVAL;
+
+ if ((chip->id & ID_FAMILY_MASK) == ID_ADT75XX &&
+ (chip->config1 & ADT7516_SEL_AIN1_2_EX_TEMP_MASK) == 0) {
+ if (data > 255 || data < 0)
+ return -EINVAL;
+ } else {
+ if (data > 127 || data < -128)
+ return -EINVAL;
+
+ if (data < 0)
+ data += 256;
+ }
+
+ val = (u8)data;
+
+ ret = chip->bus.write(chip->bus.client, this_attr->address, val);
+ if (ret)
+ return -EIO;
+
+ return len;
+}
+
+static ssize_t adt7316_show_int_enabled(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct iio_dev *dev_info = dev_to_iio_dev(dev);
+ struct adt7316_chip_info *chip = iio_priv(dev_info);
+
+ return sprintf(buf, "%d\n", !!(chip->config1 & ADT7316_INT_EN));
+}
+
+static ssize_t adt7316_set_int_enabled(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t len)
+{
+ struct iio_dev *dev_info = dev_to_iio_dev(dev);
+ struct adt7316_chip_info *chip = iio_priv(dev_info);
+ u8 config1;
+ int ret;
+
+ config1 = chip->config1 & (~ADT7316_INT_EN);
+ if (buf[0] == '1')
+ config1 |= ADT7316_INT_EN;
+
+ ret = chip->bus.write(chip->bus.client, ADT7316_CONFIG1, config1);
+ if (ret)
+ return -EIO;
+
+ chip->config1 = config1;
+
+ return len;
+}
+
+static IIO_DEVICE_ATTR(int_mask,
+ S_IRUGO | S_IWUSR,
+ adt7316_show_int_mask, adt7316_set_int_mask,
+ 0);
+static IIO_DEVICE_ATTR(in_temp_high_value,
+ S_IRUGO | S_IWUSR,
+ adt7316_show_ad_bound, adt7316_set_ad_bound,
+ ADT7316_IN_TEMP_HIGH);
+static IIO_DEVICE_ATTR(in_temp_low_value,
+ S_IRUGO | S_IWUSR,
+ adt7316_show_ad_bound, adt7316_set_ad_bound,
+ ADT7316_IN_TEMP_LOW);
+static IIO_DEVICE_ATTR(ex_temp_high_value,
+ S_IRUGO | S_IWUSR,
+ adt7316_show_ad_bound, adt7316_set_ad_bound,
+ ADT7316_EX_TEMP_HIGH);
+static IIO_DEVICE_ATTR(ex_temp_low_value,
+ S_IRUGO | S_IWUSR,
+ adt7316_show_ad_bound, adt7316_set_ad_bound,
+ ADT7316_EX_TEMP_LOW);
+
+/* NASTY duplication to be fixed */
+static IIO_DEVICE_ATTR(ex_temp_ain1_high_value,
+ S_IRUGO | S_IWUSR,
+ adt7316_show_ad_bound, adt7316_set_ad_bound,
+ ADT7316_EX_TEMP_HIGH);
+static IIO_DEVICE_ATTR(ex_temp_ain1_low_value,
+ S_IRUGO | S_IWUSR,
+ adt7316_show_ad_bound, adt7316_set_ad_bound,
+ ADT7316_EX_TEMP_LOW);
+static IIO_DEVICE_ATTR(ain2_high_value,
+ S_IRUGO | S_IWUSR,
+ adt7316_show_ad_bound, adt7316_set_ad_bound,
+ ADT7516_AIN2_HIGH);
+static IIO_DEVICE_ATTR(ain2_low_value,
+ S_IRUGO | S_IWUSR,
+ adt7316_show_ad_bound, adt7316_set_ad_bound,
+ ADT7516_AIN2_LOW);
+static IIO_DEVICE_ATTR(ain3_high_value,
+ S_IRUGO | S_IWUSR,
+ adt7316_show_ad_bound, adt7316_set_ad_bound,
+ ADT7516_AIN3_HIGH);
+static IIO_DEVICE_ATTR(ain3_low_value,
+ S_IRUGO | S_IWUSR,
+ adt7316_show_ad_bound, adt7316_set_ad_bound,
+ ADT7516_AIN3_LOW);
+static IIO_DEVICE_ATTR(ain4_high_value,
+ S_IRUGO | S_IWUSR,
+ adt7316_show_ad_bound, adt7316_set_ad_bound,
+ ADT7516_AIN4_HIGH);
+static IIO_DEVICE_ATTR(ain4_low_value,
+ S_IRUGO | S_IWUSR,
+ adt7316_show_ad_bound, adt7316_set_ad_bound,
+ ADT7516_AIN4_LOW);
+static IIO_DEVICE_ATTR(int_enabled,
+ S_IRUGO | S_IWUSR,
+ adt7316_show_int_enabled,
+ adt7316_set_int_enabled, 0);
+
+static struct attribute *adt7316_event_attributes[] = {
+ &iio_dev_attr_int_mask.dev_attr.attr,
+ &iio_dev_attr_in_temp_high_value.dev_attr.attr,
+ &iio_dev_attr_in_temp_low_value.dev_attr.attr,
+ &iio_dev_attr_ex_temp_high_value.dev_attr.attr,
+ &iio_dev_attr_ex_temp_low_value.dev_attr.attr,
+ &iio_dev_attr_int_enabled.dev_attr.attr,
+ NULL,
+};
+
+static struct attribute_group adt7316_event_attribute_group = {
+ .attrs = adt7316_event_attributes,
+ .name = "events",
+};
+
+static struct attribute *adt7516_event_attributes[] = {
+ &iio_dev_attr_int_mask.dev_attr.attr,
+ &iio_dev_attr_in_temp_high_value.dev_attr.attr,
+ &iio_dev_attr_in_temp_low_value.dev_attr.attr,
+ &iio_dev_attr_ex_temp_ain1_high_value.dev_attr.attr,
+ &iio_dev_attr_ex_temp_ain1_low_value.dev_attr.attr,
+ &iio_dev_attr_ain2_high_value.dev_attr.attr,
+ &iio_dev_attr_ain2_low_value.dev_attr.attr,
+ &iio_dev_attr_ain3_high_value.dev_attr.attr,
+ &iio_dev_attr_ain3_low_value.dev_attr.attr,
+ &iio_dev_attr_ain4_high_value.dev_attr.attr,
+ &iio_dev_attr_ain4_low_value.dev_attr.attr,
+ &iio_dev_attr_int_enabled.dev_attr.attr,
+ NULL,
+};
+
+static struct attribute_group adt7516_event_attribute_group = {
+ .attrs = adt7516_event_attributes,
+ .name = "events",
+};
+
+#ifdef CONFIG_PM_SLEEP
+static int adt7316_disable(struct device *dev)
+{
+ struct iio_dev *dev_info = dev_get_drvdata(dev);
+ struct adt7316_chip_info *chip = iio_priv(dev_info);
+
+ return _adt7316_store_enabled(chip, 0);
+}
+
+static int adt7316_enable(struct device *dev)
+{
+ struct iio_dev *dev_info = dev_get_drvdata(dev);
+ struct adt7316_chip_info *chip = iio_priv(dev_info);
+
+ return _adt7316_store_enabled(chip, 1);
+}
+
+SIMPLE_DEV_PM_OPS(adt7316_pm_ops, adt7316_disable, adt7316_enable);
+EXPORT_SYMBOL_GPL(adt7316_pm_ops);
+#endif
+
+static const struct iio_info adt7316_info = {
+ .attrs = &adt7316_attribute_group,
+ .event_attrs = &adt7316_event_attribute_group,
+ .driver_module = THIS_MODULE,
+};
+
+static const struct iio_info adt7516_info = {
+ .attrs = &adt7516_attribute_group,
+ .event_attrs = &adt7516_event_attribute_group,
+ .driver_module = THIS_MODULE,
+};
+
+/*
+ * device probe and remove
+ */
+int adt7316_probe(struct device *dev, struct adt7316_bus *bus,
+ const char *name)
+{
+ struct adt7316_chip_info *chip;
+ struct iio_dev *indio_dev;
+ unsigned short *adt7316_platform_data = dev->platform_data;
+ int ret = 0;
+
+ indio_dev = devm_iio_device_alloc(dev, sizeof(*chip));
+ if (!indio_dev)
+ return -ENOMEM;
+ chip = iio_priv(indio_dev);
+ /* this is only used for device removal purposes */
+ dev_set_drvdata(dev, indio_dev);
+
+ chip->bus = *bus;
+
+ if (name[4] == '3')
+ chip->id = ID_ADT7316 + (name[6] - '6');
+ else if (name[4] == '5')
+ chip->id = ID_ADT7516 + (name[6] - '6');
+ else
+ return -ENODEV;
+
+ chip->ldac_pin = adt7316_platform_data[1];
+ if (chip->ldac_pin) {
+ chip->config3 |= ADT7316_DA_EN_VIA_DAC_LDCA;
+ if ((chip->id & ID_FAMILY_MASK) == ID_ADT75XX)
+ chip->config1 |= ADT7516_SEL_AIN3;
+ }
+ chip->int_mask = ADT7316_TEMP_INT_MASK | ADT7316_VDD_INT_MASK;
+ if ((chip->id & ID_FAMILY_MASK) == ID_ADT75XX)
+ chip->int_mask |= ADT7516_AIN_INT_MASK;
+
+ indio_dev->dev.parent = dev;
+ if ((chip->id & ID_FAMILY_MASK) == ID_ADT75XX)
+ indio_dev->info = &adt7516_info;
+ else
+ indio_dev->info = &adt7316_info;
+ indio_dev->name = name;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+
+ if (chip->bus.irq > 0) {
+ if (adt7316_platform_data[0])
+ chip->bus.irq_flags = adt7316_platform_data[0];
+
+ ret = devm_request_threaded_irq(dev, chip->bus.irq,
+ NULL,
+ &adt7316_event_handler,
+ chip->bus.irq_flags |
+ IRQF_ONESHOT,
+ indio_dev->name,
+ indio_dev);
+ if (ret)
+ return ret;
+
+ if (chip->bus.irq_flags & IRQF_TRIGGER_HIGH)
+ chip->config1 |= ADT7316_INT_POLARITY;
+ }
+
+ ret = chip->bus.write(chip->bus.client, ADT7316_CONFIG1, chip->config1);
+ if (ret)
+ return -EIO;
+
+ ret = chip->bus.write(chip->bus.client, ADT7316_CONFIG3, chip->config3);
+ if (ret)
+ return -EIO;
+
+ ret = devm_iio_device_register(dev, indio_dev);
+ if (ret)
+ return ret;
+
+ dev_info(dev, "%s temperature sensor, ADC and DAC registered.\n",
+ indio_dev->name);
+
+ return 0;
+}
+EXPORT_SYMBOL(adt7316_probe);
+
+MODULE_AUTHOR("Sonic Zhang <sonic.zhang@analog.com>");
+MODULE_DESCRIPTION("Analog Devices ADT7316/7/8 and ADT7516/7/9 digital temperature sensor, ADC and DAC driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/staging/iio/addac/adt7316.h b/drivers/staging/iio/addac/adt7316.h
new file mode 100644
index 000000000..ec40fbb69
--- /dev/null
+++ b/drivers/staging/iio/addac/adt7316.h
@@ -0,0 +1,36 @@
+/*
+ * ADT7316 digital temperature sensor driver supporting ADT7316/7/8 ADT7516/7/9
+ *
+ * Copyright 2010 Analog Devices Inc.
+ *
+ * Licensed under the GPL-2 or later.
+ */
+
+#ifndef _ADT7316_H_
+#define _ADT7316_H_
+
+#include <linux/types.h>
+#include <linux/pm.h>
+
+#define ADT7316_REG_MAX_ADDR 0x3F
+
+struct adt7316_bus {
+ void *client;
+ int irq;
+ int irq_flags;
+ int (*read)(void *client, u8 reg, u8 *data);
+ int (*write)(void *client, u8 reg, u8 val);
+ int (*multi_read)(void *client, u8 first_reg, u8 count, u8 *data);
+ int (*multi_write)(void *client, u8 first_reg, u8 count, u8 *data);
+};
+
+#ifdef CONFIG_PM_SLEEP
+extern const struct dev_pm_ops adt7316_pm_ops;
+#define ADT7316_PM_OPS (&adt7316_pm_ops)
+#else
+#define ADT7316_PM_OPS NULL
+#endif
+int adt7316_probe(struct device *dev, struct adt7316_bus *bus,
+ const char *name);
+
+#endif
diff --git a/drivers/staging/iio/cdc/Kconfig b/drivers/staging/iio/cdc/Kconfig
new file mode 100644
index 000000000..80211df8c
--- /dev/null
+++ b/drivers/staging/iio/cdc/Kconfig
@@ -0,0 +1,36 @@
+#
+# CDC drivers
+#
+menu "Capacitance to digital converters"
+
+config AD7150
+ tristate "Analog Devices ad7150/1/6 capacitive sensor driver"
+ depends on I2C
+ help
+ Say yes here to build support for Analog Devices capacitive sensors.
+ (ad7150, ad7151, ad7156) Provides direct access via sysfs.
+
+ To compile this driver as a module, choose M here: the
+ module will be called ad7150.
+
+config AD7152
+ tristate "Analog Devices ad7152/3 capacitive sensor driver"
+ depends on I2C
+ help
+ Say yes here to build support for Analog Devices capacitive sensors.
+ (ad7152, ad7153) Provides direct access via sysfs.
+
+ To compile this driver as a module, choose M here: the
+ module will be called ad7152.
+
+config AD7746
+ tristate "Analog Devices AD7745, AD7746 AD7747 capacitive sensor driver"
+ depends on I2C
+ help
+ Say yes here to build support for Analog Devices capacitive sensors.
+ (AD7745, AD7746, AD7747) Provides direct access via sysfs.
+
+ To compile this driver as a module, choose M here: the
+ module will be called ad7746.
+
+endmenu
diff --git a/drivers/staging/iio/cdc/Makefile b/drivers/staging/iio/cdc/Makefile
new file mode 100644
index 000000000..a5fbabf5c
--- /dev/null
+++ b/drivers/staging/iio/cdc/Makefile
@@ -0,0 +1,7 @@
+#
+# Makefile for industrial I/O DAC drivers
+#
+
+obj-$(CONFIG_AD7150) += ad7150.o
+obj-$(CONFIG_AD7152) += ad7152.o
+obj-$(CONFIG_AD7746) += ad7746.o
diff --git a/drivers/staging/iio/cdc/ad7150.c b/drivers/staging/iio/cdc/ad7150.c
new file mode 100644
index 000000000..a2b7ae332
--- /dev/null
+++ b/drivers/staging/iio/cdc/ad7150.c
@@ -0,0 +1,679 @@
+/*
+ * AD7150 capacitive sensor driver supporting AD7150/1/6
+ *
+ * Copyright 2010-2011 Analog Devices Inc.
+ *
+ * Licensed under the GPL-2 or later.
+ */
+
+#include <linux/interrupt.h>
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/iio/events.h>
+/*
+ * AD7150 registers definition
+ */
+
+#define AD7150_STATUS 0
+#define AD7150_STATUS_OUT1 (1 << 3)
+#define AD7150_STATUS_OUT2 (1 << 5)
+#define AD7150_CH1_DATA_HIGH 1
+#define AD7150_CH2_DATA_HIGH 3
+#define AD7150_CH1_AVG_HIGH 5
+#define AD7150_CH2_AVG_HIGH 7
+#define AD7150_CH1_SENSITIVITY 9
+#define AD7150_CH1_THR_HOLD_H 9
+#define AD7150_CH1_TIMEOUT 10
+#define AD7150_CH1_SETUP 11
+#define AD7150_CH2_SENSITIVITY 12
+#define AD7150_CH2_THR_HOLD_H 12
+#define AD7150_CH2_TIMEOUT 13
+#define AD7150_CH2_SETUP 14
+#define AD7150_CFG 15
+#define AD7150_CFG_FIX (1 << 7)
+#define AD7150_PD_TIMER 16
+#define AD7150_CH1_CAPDAC 17
+#define AD7150_CH2_CAPDAC 18
+#define AD7150_SN3 19
+#define AD7150_SN2 20
+#define AD7150_SN1 21
+#define AD7150_SN0 22
+#define AD7150_ID 23
+
+/**
+ * struct ad7150_chip_info - instance specific chip data
+ * @client: i2c client for this device
+ * @current_event: device always has one type of event enabled.
+ * This element stores the event code of the current one.
+ * @threshold: thresholds for simple capacitance value events
+ * @thresh_sensitivity: threshold for simple capacitance offset
+ * from 'average' value.
+ * @mag_sensitity: threshold for magnitude of capacitance offset from
+ * from 'average' value.
+ * @thresh_timeout: a timeout, in samples from the moment an
+ * adaptive threshold event occurs to when the average
+ * value jumps to current value.
+ * @mag_timeout: a timeout, in sample from the moment an
+ * adaptive magnitude event occurs to when the average
+ * value jumps to the current value.
+ * @old_state: store state from previous event, allowing confirmation
+ * of new condition.
+ * @conversion_mode: the current conversion mode.
+ * @state_lock: ensure consistent state of this structure wrt the
+ * hardware.
+ */
+struct ad7150_chip_info {
+ struct i2c_client *client;
+ u64 current_event;
+ u16 threshold[2][2];
+ u8 thresh_sensitivity[2][2];
+ u8 mag_sensitivity[2][2];
+ u8 thresh_timeout[2][2];
+ u8 mag_timeout[2][2];
+ int old_state;
+ char *conversion_mode;
+ struct mutex state_lock;
+};
+
+/*
+ * sysfs nodes
+ */
+
+static const u8 ad7150_addresses[][6] = {
+ { AD7150_CH1_DATA_HIGH, AD7150_CH1_AVG_HIGH,
+ AD7150_CH1_SETUP, AD7150_CH1_THR_HOLD_H,
+ AD7150_CH1_SENSITIVITY, AD7150_CH1_TIMEOUT },
+ { AD7150_CH2_DATA_HIGH, AD7150_CH2_AVG_HIGH,
+ AD7150_CH2_SETUP, AD7150_CH2_THR_HOLD_H,
+ AD7150_CH2_SENSITIVITY, AD7150_CH2_TIMEOUT },
+};
+
+static int ad7150_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val,
+ int *val2,
+ long mask)
+{
+ int ret;
+ struct ad7150_chip_info *chip = iio_priv(indio_dev);
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ ret = i2c_smbus_read_word_data(chip->client,
+ ad7150_addresses[chan->channel][0]);
+ if (ret < 0)
+ return ret;
+ *val = swab16(ret);
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_AVERAGE_RAW:
+ ret = i2c_smbus_read_word_data(chip->client,
+ ad7150_addresses[chan->channel][1]);
+ if (ret < 0)
+ return ret;
+ *val = swab16(ret);
+ return IIO_VAL_INT;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int ad7150_read_event_config(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan, enum iio_event_type type,
+ enum iio_event_direction dir)
+{
+ int ret;
+ u8 threshtype;
+ bool adaptive;
+ struct ad7150_chip_info *chip = iio_priv(indio_dev);
+
+ ret = i2c_smbus_read_byte_data(chip->client, AD7150_CFG);
+ if (ret < 0)
+ return ret;
+
+ threshtype = (ret >> 5) & 0x03;
+ adaptive = !!(ret & 0x80);
+
+ switch (type) {
+ case IIO_EV_TYPE_MAG_ADAPTIVE:
+ if (dir == IIO_EV_DIR_RISING)
+ return adaptive && (threshtype == 0x1);
+ return adaptive && (threshtype == 0x0);
+ case IIO_EV_TYPE_THRESH_ADAPTIVE:
+ if (dir == IIO_EV_DIR_RISING)
+ return adaptive && (threshtype == 0x3);
+ return adaptive && (threshtype == 0x2);
+ case IIO_EV_TYPE_THRESH:
+ if (dir == IIO_EV_DIR_RISING)
+ return !adaptive && (threshtype == 0x1);
+ return !adaptive && (threshtype == 0x0);
+ default:
+ break;
+ }
+ return -EINVAL;
+}
+
+/* lock should be held */
+static int ad7150_write_event_params(struct iio_dev *indio_dev,
+ unsigned int chan, enum iio_event_type type,
+ enum iio_event_direction dir)
+{
+ int ret;
+ u16 value;
+ u8 sens, timeout;
+ struct ad7150_chip_info *chip = iio_priv(indio_dev);
+ int rising = (dir == IIO_EV_DIR_RISING);
+ u64 event_code;
+
+ event_code = IIO_UNMOD_EVENT_CODE(IIO_CAPACITANCE, chan, type, dir);
+
+ if (event_code != chip->current_event)
+ return 0;
+
+ switch (type) {
+ /* Note completely different from the adaptive versions */
+ case IIO_EV_TYPE_THRESH:
+ value = chip->threshold[rising][chan];
+ ret = i2c_smbus_write_word_data(chip->client,
+ ad7150_addresses[chan][3],
+ swab16(value));
+ if (ret < 0)
+ return ret;
+ return 0;
+ case IIO_EV_TYPE_MAG_ADAPTIVE:
+ sens = chip->mag_sensitivity[rising][chan];
+ timeout = chip->mag_timeout[rising][chan];
+ break;
+ case IIO_EV_TYPE_THRESH_ADAPTIVE:
+ sens = chip->thresh_sensitivity[rising][chan];
+ timeout = chip->thresh_timeout[rising][chan];
+ break;
+ default:
+ return -EINVAL;
+ }
+ ret = i2c_smbus_write_byte_data(chip->client,
+ ad7150_addresses[chan][4],
+ sens);
+ if (ret < 0)
+ return ret;
+
+ ret = i2c_smbus_write_byte_data(chip->client,
+ ad7150_addresses[chan][5],
+ timeout);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static int ad7150_write_event_config(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan, enum iio_event_type type,
+ enum iio_event_direction dir, int state)
+{
+ u8 thresh_type, cfg, adaptive;
+ int ret;
+ struct ad7150_chip_info *chip = iio_priv(indio_dev);
+ int rising = (dir == IIO_EV_DIR_RISING);
+ u64 event_code;
+
+ /* Something must always be turned on */
+ if (state == 0)
+ return -EINVAL;
+
+ event_code = IIO_UNMOD_EVENT_CODE(chan->type, chan->channel, type, dir);
+ if (event_code == chip->current_event)
+ return 0;
+ mutex_lock(&chip->state_lock);
+ ret = i2c_smbus_read_byte_data(chip->client, AD7150_CFG);
+ if (ret < 0)
+ goto error_ret;
+
+ cfg = ret & ~((0x03 << 5) | (0x1 << 7));
+
+ switch (type) {
+ case IIO_EV_TYPE_MAG_ADAPTIVE:
+ adaptive = 1;
+ if (rising)
+ thresh_type = 0x1;
+ else
+ thresh_type = 0x0;
+ break;
+ case IIO_EV_TYPE_THRESH_ADAPTIVE:
+ adaptive = 1;
+ if (rising)
+ thresh_type = 0x3;
+ else
+ thresh_type = 0x2;
+ break;
+ case IIO_EV_TYPE_THRESH:
+ adaptive = 0;
+ if (rising)
+ thresh_type = 0x1;
+ else
+ thresh_type = 0x0;
+ break;
+ default:
+ ret = -EINVAL;
+ goto error_ret;
+ }
+
+ cfg |= (!adaptive << 7) | (thresh_type << 5);
+
+ ret = i2c_smbus_write_byte_data(chip->client, AD7150_CFG, cfg);
+ if (ret < 0)
+ goto error_ret;
+
+ chip->current_event = event_code;
+
+ /* update control attributes */
+ ret = ad7150_write_event_params(indio_dev, chan->channel, type, dir);
+error_ret:
+ mutex_unlock(&chip->state_lock);
+
+ return 0;
+}
+
+static int ad7150_read_event_value(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan,
+ enum iio_event_type type,
+ enum iio_event_direction dir,
+ enum iio_event_info info,
+ int *val, int *val2)
+{
+ struct ad7150_chip_info *chip = iio_priv(indio_dev);
+ int rising = (dir == IIO_EV_DIR_RISING);
+
+ /* Complex register sharing going on here */
+ switch (type) {
+ case IIO_EV_TYPE_MAG_ADAPTIVE:
+ *val = chip->mag_sensitivity[rising][chan->channel];
+ return IIO_VAL_INT;
+ case IIO_EV_TYPE_THRESH_ADAPTIVE:
+ *val = chip->thresh_sensitivity[rising][chan->channel];
+ return IIO_VAL_INT;
+ case IIO_EV_TYPE_THRESH:
+ *val = chip->threshold[rising][chan->channel];
+ return IIO_VAL_INT;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int ad7150_write_event_value(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan,
+ enum iio_event_type type,
+ enum iio_event_direction dir,
+ enum iio_event_info info,
+ int val, int val2)
+{
+ int ret;
+ struct ad7150_chip_info *chip = iio_priv(indio_dev);
+ int rising = (dir == IIO_EV_DIR_RISING);
+
+ mutex_lock(&chip->state_lock);
+ switch (type) {
+ case IIO_EV_TYPE_MAG_ADAPTIVE:
+ chip->mag_sensitivity[rising][chan->channel] = val;
+ break;
+ case IIO_EV_TYPE_THRESH_ADAPTIVE:
+ chip->thresh_sensitivity[rising][chan->channel] = val;
+ break;
+ case IIO_EV_TYPE_THRESH:
+ chip->threshold[rising][chan->channel] = val;
+ break;
+ default:
+ ret = -EINVAL;
+ goto error_ret;
+ }
+
+ /* write back if active */
+ ret = ad7150_write_event_params(indio_dev, chan->channel, type, dir);
+
+error_ret:
+ mutex_unlock(&chip->state_lock);
+ return ret;
+}
+
+static ssize_t ad7150_show_timeout(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct ad7150_chip_info *chip = iio_priv(indio_dev);
+ struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
+ u8 value;
+
+ /* use the event code for consistency reasons */
+ int chan = IIO_EVENT_CODE_EXTRACT_CHAN(this_attr->address);
+ int rising = !!(IIO_EVENT_CODE_EXTRACT_DIR(this_attr->address)
+ == IIO_EV_DIR_RISING);
+
+ switch (IIO_EVENT_CODE_EXTRACT_TYPE(this_attr->address)) {
+ case IIO_EV_TYPE_MAG_ADAPTIVE:
+ value = chip->mag_timeout[rising][chan];
+ break;
+ case IIO_EV_TYPE_THRESH_ADAPTIVE:
+ value = chip->thresh_timeout[rising][chan];
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return sprintf(buf, "%d\n", value);
+}
+
+static ssize_t ad7150_store_timeout(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t len)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct ad7150_chip_info *chip = iio_priv(indio_dev);
+ struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
+ int chan = IIO_EVENT_CODE_EXTRACT_CHAN(this_attr->address);
+ enum iio_event_direction dir;
+ enum iio_event_type type;
+ int rising;
+ u8 data;
+ int ret;
+
+ type = IIO_EVENT_CODE_EXTRACT_TYPE(this_attr->address);
+ dir = IIO_EVENT_CODE_EXTRACT_DIR(this_attr->address);
+ rising = (dir == IIO_EV_DIR_RISING);
+
+ ret = kstrtou8(buf, 10, &data);
+ if (ret < 0)
+ return ret;
+
+ mutex_lock(&chip->state_lock);
+ switch (type) {
+ case IIO_EV_TYPE_MAG_ADAPTIVE:
+ chip->mag_timeout[rising][chan] = data;
+ break;
+ case IIO_EV_TYPE_THRESH_ADAPTIVE:
+ chip->thresh_timeout[rising][chan] = data;
+ break;
+ default:
+ ret = -EINVAL;
+ goto error_ret;
+ }
+
+ ret = ad7150_write_event_params(indio_dev, chan, type, dir);
+error_ret:
+ mutex_unlock(&chip->state_lock);
+
+ if (ret < 0)
+ return ret;
+
+ return len;
+}
+
+#define AD7150_TIMEOUT(chan, type, dir, ev_type, ev_dir) \
+ IIO_DEVICE_ATTR(in_capacitance##chan##_##type##_##dir##_timeout, \
+ S_IRUGO | S_IWUSR, \
+ &ad7150_show_timeout, \
+ &ad7150_store_timeout, \
+ IIO_UNMOD_EVENT_CODE(IIO_CAPACITANCE, \
+ chan, \
+ IIO_EV_TYPE_##ev_type, \
+ IIO_EV_DIR_##ev_dir))
+static AD7150_TIMEOUT(0, mag_adaptive, rising, MAG_ADAPTIVE, RISING);
+static AD7150_TIMEOUT(0, mag_adaptive, falling, MAG_ADAPTIVE, FALLING);
+static AD7150_TIMEOUT(1, mag_adaptive, rising, MAG_ADAPTIVE, RISING);
+static AD7150_TIMEOUT(1, mag_adaptive, falling, MAG_ADAPTIVE, FALLING);
+static AD7150_TIMEOUT(0, thresh_adaptive, rising, THRESH_ADAPTIVE, RISING);
+static AD7150_TIMEOUT(0, thresh_adaptive, falling, THRESH_ADAPTIVE, FALLING);
+static AD7150_TIMEOUT(1, thresh_adaptive, rising, THRESH_ADAPTIVE, RISING);
+static AD7150_TIMEOUT(1, thresh_adaptive, falling, THRESH_ADAPTIVE, FALLING);
+
+static const struct iio_event_spec ad7150_events[] = {
+ {
+ .type = IIO_EV_TYPE_THRESH,
+ .dir = IIO_EV_DIR_RISING,
+ .mask_separate = BIT(IIO_EV_INFO_VALUE) |
+ BIT(IIO_EV_INFO_ENABLE),
+ }, {
+ .type = IIO_EV_TYPE_THRESH,
+ .dir = IIO_EV_DIR_FALLING,
+ .mask_separate = BIT(IIO_EV_INFO_VALUE) |
+ BIT(IIO_EV_INFO_ENABLE),
+ }, {
+ .type = IIO_EV_TYPE_THRESH_ADAPTIVE,
+ .dir = IIO_EV_DIR_RISING,
+ .mask_separate = BIT(IIO_EV_INFO_VALUE) |
+ BIT(IIO_EV_INFO_ENABLE),
+ }, {
+ .type = IIO_EV_TYPE_THRESH_ADAPTIVE,
+ .dir = IIO_EV_DIR_FALLING,
+ .mask_separate = BIT(IIO_EV_INFO_VALUE) |
+ BIT(IIO_EV_INFO_ENABLE),
+ }, {
+ .type = IIO_EV_TYPE_MAG_ADAPTIVE,
+ .dir = IIO_EV_DIR_RISING,
+ .mask_separate = BIT(IIO_EV_INFO_VALUE) |
+ BIT(IIO_EV_INFO_ENABLE),
+ }, {
+ .type = IIO_EV_TYPE_MAG_ADAPTIVE,
+ .dir = IIO_EV_DIR_FALLING,
+ .mask_separate = BIT(IIO_EV_INFO_VALUE) |
+ BIT(IIO_EV_INFO_ENABLE),
+ },
+};
+
+static const struct iio_chan_spec ad7150_channels[] = {
+ {
+ .type = IIO_CAPACITANCE,
+ .indexed = 1,
+ .channel = 0,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+ BIT(IIO_CHAN_INFO_AVERAGE_RAW),
+ .event_spec = ad7150_events,
+ .num_event_specs = ARRAY_SIZE(ad7150_events),
+ }, {
+ .type = IIO_CAPACITANCE,
+ .indexed = 1,
+ .channel = 1,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+ BIT(IIO_CHAN_INFO_AVERAGE_RAW),
+ .event_spec = ad7150_events,
+ .num_event_specs = ARRAY_SIZE(ad7150_events),
+ },
+};
+
+/*
+ * threshold events
+ */
+
+static irqreturn_t ad7150_event_handler(int irq, void *private)
+{
+ struct iio_dev *indio_dev = private;
+ struct ad7150_chip_info *chip = iio_priv(indio_dev);
+ u8 int_status;
+ s64 timestamp = iio_get_time_ns();
+ int ret;
+
+ ret = i2c_smbus_read_byte_data(chip->client, AD7150_STATUS);
+ if (ret < 0)
+ return IRQ_HANDLED;
+
+ int_status = ret;
+
+ if ((int_status & AD7150_STATUS_OUT1) &&
+ !(chip->old_state & AD7150_STATUS_OUT1))
+ iio_push_event(indio_dev,
+ IIO_UNMOD_EVENT_CODE(IIO_CAPACITANCE,
+ 0,
+ IIO_EV_TYPE_THRESH,
+ IIO_EV_DIR_RISING),
+ timestamp);
+ else if ((!(int_status & AD7150_STATUS_OUT1)) &&
+ (chip->old_state & AD7150_STATUS_OUT1))
+ iio_push_event(indio_dev,
+ IIO_UNMOD_EVENT_CODE(IIO_CAPACITANCE,
+ 0,
+ IIO_EV_TYPE_THRESH,
+ IIO_EV_DIR_FALLING),
+ timestamp);
+
+ if ((int_status & AD7150_STATUS_OUT2) &&
+ !(chip->old_state & AD7150_STATUS_OUT2))
+ iio_push_event(indio_dev,
+ IIO_UNMOD_EVENT_CODE(IIO_CAPACITANCE,
+ 1,
+ IIO_EV_TYPE_THRESH,
+ IIO_EV_DIR_RISING),
+ timestamp);
+ else if ((!(int_status & AD7150_STATUS_OUT2)) &&
+ (chip->old_state & AD7150_STATUS_OUT2))
+ iio_push_event(indio_dev,
+ IIO_UNMOD_EVENT_CODE(IIO_CAPACITANCE,
+ 1,
+ IIO_EV_TYPE_THRESH,
+ IIO_EV_DIR_FALLING),
+ timestamp);
+ /* store the status to avoid repushing same events */
+ chip->old_state = int_status;
+
+ return IRQ_HANDLED;
+}
+
+/* Timeouts not currently handled by core */
+static struct attribute *ad7150_event_attributes[] = {
+ &iio_dev_attr_in_capacitance0_mag_adaptive_rising_timeout
+ .dev_attr.attr,
+ &iio_dev_attr_in_capacitance0_mag_adaptive_falling_timeout
+ .dev_attr.attr,
+ &iio_dev_attr_in_capacitance1_mag_adaptive_rising_timeout
+ .dev_attr.attr,
+ &iio_dev_attr_in_capacitance1_mag_adaptive_falling_timeout
+ .dev_attr.attr,
+ &iio_dev_attr_in_capacitance0_thresh_adaptive_rising_timeout
+ .dev_attr.attr,
+ &iio_dev_attr_in_capacitance0_thresh_adaptive_falling_timeout
+ .dev_attr.attr,
+ &iio_dev_attr_in_capacitance1_thresh_adaptive_rising_timeout
+ .dev_attr.attr,
+ &iio_dev_attr_in_capacitance1_thresh_adaptive_falling_timeout
+ .dev_attr.attr,
+ NULL,
+};
+
+static struct attribute_group ad7150_event_attribute_group = {
+ .attrs = ad7150_event_attributes,
+ .name = "events",
+};
+
+static const struct iio_info ad7150_info = {
+ .event_attrs = &ad7150_event_attribute_group,
+ .driver_module = THIS_MODULE,
+ .read_raw = &ad7150_read_raw,
+ .read_event_config = &ad7150_read_event_config,
+ .write_event_config = &ad7150_write_event_config,
+ .read_event_value = &ad7150_read_event_value,
+ .write_event_value = &ad7150_write_event_value,
+};
+
+/*
+ * device probe and remove
+ */
+
+static int ad7150_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ int ret;
+ struct ad7150_chip_info *chip;
+ struct iio_dev *indio_dev;
+
+ indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*chip));
+ if (!indio_dev)
+ return -ENOMEM;
+ chip = iio_priv(indio_dev);
+ mutex_init(&chip->state_lock);
+ /* this is only used for device removal purposes */
+ i2c_set_clientdata(client, indio_dev);
+
+ chip->client = client;
+
+ indio_dev->name = id->name;
+ indio_dev->channels = ad7150_channels;
+ indio_dev->num_channels = ARRAY_SIZE(ad7150_channels);
+ /* Establish that the iio_dev is a child of the i2c device */
+ indio_dev->dev.parent = &client->dev;
+
+ indio_dev->info = &ad7150_info;
+
+ indio_dev->modes = INDIO_DIRECT_MODE;
+
+ if (client->irq) {
+ ret = devm_request_threaded_irq(&client->dev, client->irq,
+ NULL,
+ &ad7150_event_handler,
+ IRQF_TRIGGER_RISING |
+ IRQF_TRIGGER_FALLING |
+ IRQF_ONESHOT,
+ "ad7150_irq1",
+ indio_dev);
+ if (ret)
+ return ret;
+ }
+
+ if (client->dev.platform_data) {
+ ret = devm_request_threaded_irq(&client->dev, *(unsigned int *)
+ client->dev.platform_data,
+ NULL,
+ &ad7150_event_handler,
+ IRQF_TRIGGER_RISING |
+ IRQF_TRIGGER_FALLING |
+ IRQF_ONESHOT,
+ "ad7150_irq2",
+ indio_dev);
+ if (ret)
+ return ret;
+ }
+
+ ret = iio_device_register(indio_dev);
+ if (ret)
+ return ret;
+
+ dev_info(&client->dev, "%s capacitive sensor registered,irq: %d\n",
+ id->name, client->irq);
+
+ return 0;
+}
+
+static int ad7150_remove(struct i2c_client *client)
+{
+ struct iio_dev *indio_dev = i2c_get_clientdata(client);
+
+ iio_device_unregister(indio_dev);
+
+ return 0;
+}
+
+static const struct i2c_device_id ad7150_id[] = {
+ { "ad7150", 0 },
+ { "ad7151", 0 },
+ { "ad7156", 0 },
+ {}
+};
+
+MODULE_DEVICE_TABLE(i2c, ad7150_id);
+
+static struct i2c_driver ad7150_driver = {
+ .driver = {
+ .name = "ad7150",
+ },
+ .probe = ad7150_probe,
+ .remove = ad7150_remove,
+ .id_table = ad7150_id,
+};
+module_i2c_driver(ad7150_driver);
+
+MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>");
+MODULE_DESCRIPTION("Analog Devices AD7150/1/6 capacitive sensor driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/staging/iio/cdc/ad7152.c b/drivers/staging/iio/cdc/ad7152.c
new file mode 100644
index 000000000..87110d940
--- /dev/null
+++ b/drivers/staging/iio/cdc/ad7152.c
@@ -0,0 +1,543 @@
+/*
+ * AD7152 capacitive sensor driver supporting AD7152/3
+ *
+ * Copyright 2010-2011a Analog Devices Inc.
+ *
+ * Licensed under the GPL-2 or later.
+ */
+
+#include <linux/interrupt.h>
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/sysfs.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/delay.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+
+/*
+ * TODO: Check compliance of calibbias with abi (units)
+ */
+/*
+ * AD7152 registers definition
+ */
+
+#define AD7152_REG_STATUS 0
+#define AD7152_REG_CH1_DATA_HIGH 1
+#define AD7152_REG_CH2_DATA_HIGH 3
+#define AD7152_REG_CH1_OFFS_HIGH 5
+#define AD7152_REG_CH2_OFFS_HIGH 7
+#define AD7152_REG_CH1_GAIN_HIGH 9
+#define AD7152_REG_CH1_SETUP 11
+#define AD7152_REG_CH2_GAIN_HIGH 12
+#define AD7152_REG_CH2_SETUP 14
+#define AD7152_REG_CFG 15
+#define AD7152_REG_RESEVERD 16
+#define AD7152_REG_CAPDAC_POS 17
+#define AD7152_REG_CAPDAC_NEG 18
+#define AD7152_REG_CFG2 26
+
+/* Status Register Bit Designations (AD7152_REG_STATUS) */
+#define AD7152_STATUS_RDY1 (1 << 0)
+#define AD7152_STATUS_RDY2 (1 << 1)
+#define AD7152_STATUS_C1C2 (1 << 2)
+#define AD7152_STATUS_PWDN (1 << 7)
+
+/* Setup Register Bit Designations (AD7152_REG_CHx_SETUP) */
+#define AD7152_SETUP_CAPDIFF (1 << 5)
+#define AD7152_SETUP_RANGE_2pF (0 << 6)
+#define AD7152_SETUP_RANGE_0_5pF (1 << 6)
+#define AD7152_SETUP_RANGE_1pF (2 << 6)
+#define AD7152_SETUP_RANGE_4pF (3 << 6)
+#define AD7152_SETUP_RANGE(x) ((x) << 6)
+
+/* Config Register Bit Designations (AD7152_REG_CFG) */
+#define AD7152_CONF_CH2EN (1 << 3)
+#define AD7152_CONF_CH1EN (1 << 4)
+#define AD7152_CONF_MODE_IDLE (0 << 0)
+#define AD7152_CONF_MODE_CONT_CONV (1 << 0)
+#define AD7152_CONF_MODE_SINGLE_CONV (2 << 0)
+#define AD7152_CONF_MODE_OFFS_CAL (5 << 0)
+#define AD7152_CONF_MODE_GAIN_CAL (6 << 0)
+
+/* Capdac Register Bit Designations (AD7152_REG_CAPDAC_XXX) */
+#define AD7152_CAPDAC_DACEN (1 << 7)
+#define AD7152_CAPDAC_DACP(x) ((x) & 0x1F)
+
+/* CFG2 Register Bit Designations (AD7152_REG_CFG2) */
+#define AD7152_CFG2_OSR(x) (((x) & 0x3) << 4)
+
+enum {
+ AD7152_DATA,
+ AD7152_OFFS,
+ AD7152_GAIN,
+ AD7152_SETUP
+};
+
+/*
+ * struct ad7152_chip_info - chip specific information
+ */
+
+struct ad7152_chip_info {
+ struct i2c_client *client;
+ /*
+ * Capacitive channel digital filter setup;
+ * conversion time/update rate setup per channel
+ */
+ u8 filter_rate_setup;
+ u8 setup[2];
+};
+
+static inline ssize_t ad7152_start_calib(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t len,
+ u8 regval)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct ad7152_chip_info *chip = iio_priv(indio_dev);
+ struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
+ bool doit;
+ int ret, timeout = 10;
+
+ ret = strtobool(buf, &doit);
+ if (ret < 0)
+ return ret;
+
+ if (!doit)
+ return 0;
+
+ if (this_attr->address == 0)
+ regval |= AD7152_CONF_CH1EN;
+ else
+ regval |= AD7152_CONF_CH2EN;
+
+ mutex_lock(&indio_dev->mlock);
+ ret = i2c_smbus_write_byte_data(chip->client, AD7152_REG_CFG, regval);
+ if (ret < 0) {
+ mutex_unlock(&indio_dev->mlock);
+ return ret;
+ }
+
+ do {
+ mdelay(20);
+ ret = i2c_smbus_read_byte_data(chip->client, AD7152_REG_CFG);
+ if (ret < 0) {
+ mutex_unlock(&indio_dev->mlock);
+ return ret;
+ }
+ } while ((ret == regval) && timeout--);
+
+ mutex_unlock(&indio_dev->mlock);
+ return len;
+}
+static ssize_t ad7152_start_offset_calib(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t len)
+{
+ return ad7152_start_calib(dev, attr, buf, len,
+ AD7152_CONF_MODE_OFFS_CAL);
+}
+static ssize_t ad7152_start_gain_calib(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t len)
+{
+ return ad7152_start_calib(dev, attr, buf, len,
+ AD7152_CONF_MODE_GAIN_CAL);
+}
+
+static IIO_DEVICE_ATTR(in_capacitance0_calibbias_calibration,
+ S_IWUSR, NULL, ad7152_start_offset_calib, 0);
+static IIO_DEVICE_ATTR(in_capacitance1_calibbias_calibration,
+ S_IWUSR, NULL, ad7152_start_offset_calib, 1);
+static IIO_DEVICE_ATTR(in_capacitance0_calibscale_calibration,
+ S_IWUSR, NULL, ad7152_start_gain_calib, 0);
+static IIO_DEVICE_ATTR(in_capacitance1_calibscale_calibration,
+ S_IWUSR, NULL, ad7152_start_gain_calib, 1);
+
+/* Values are Update Rate (Hz), Conversion Time (ms) + 1*/
+static const unsigned char ad7152_filter_rate_table[][2] = {
+ {200, 5 + 1}, {50, 20 + 1}, {20, 50 + 1}, {17, 60 + 1},
+};
+
+static ssize_t ad7152_show_filter_rate_setup(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct ad7152_chip_info *chip = iio_priv(indio_dev);
+
+ return sprintf(buf, "%d\n",
+ ad7152_filter_rate_table[chip->filter_rate_setup][0]);
+}
+
+static ssize_t ad7152_store_filter_rate_setup(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t len)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct ad7152_chip_info *chip = iio_priv(indio_dev);
+ u8 data;
+ int ret, i;
+
+ ret = kstrtou8(buf, 10, &data);
+ if (ret < 0)
+ return ret;
+
+ for (i = 0; i < ARRAY_SIZE(ad7152_filter_rate_table); i++)
+ if (data >= ad7152_filter_rate_table[i][0])
+ break;
+
+ if (i >= ARRAY_SIZE(ad7152_filter_rate_table))
+ i = ARRAY_SIZE(ad7152_filter_rate_table) - 1;
+
+ mutex_lock(&indio_dev->mlock);
+ ret = i2c_smbus_write_byte_data(chip->client,
+ AD7152_REG_CFG2, AD7152_CFG2_OSR(i));
+ if (ret < 0) {
+ mutex_unlock(&indio_dev->mlock);
+ return ret;
+ }
+
+ chip->filter_rate_setup = i;
+ mutex_unlock(&indio_dev->mlock);
+
+ return len;
+}
+
+static IIO_DEV_ATTR_SAMP_FREQ(S_IRUGO | S_IWUSR,
+ ad7152_show_filter_rate_setup,
+ ad7152_store_filter_rate_setup);
+
+static IIO_CONST_ATTR_SAMP_FREQ_AVAIL("200 50 20 17");
+
+static IIO_CONST_ATTR(in_capacitance_scale_available,
+ "0.000061050 0.000030525 0.000015263 0.000007631");
+
+static struct attribute *ad7152_attributes[] = {
+ &iio_dev_attr_sampling_frequency.dev_attr.attr,
+ &iio_dev_attr_in_capacitance0_calibbias_calibration.dev_attr.attr,
+ &iio_dev_attr_in_capacitance1_calibbias_calibration.dev_attr.attr,
+ &iio_dev_attr_in_capacitance0_calibscale_calibration.dev_attr.attr,
+ &iio_dev_attr_in_capacitance1_calibscale_calibration.dev_attr.attr,
+ &iio_const_attr_in_capacitance_scale_available.dev_attr.attr,
+ &iio_const_attr_sampling_frequency_available.dev_attr.attr,
+ NULL,
+};
+
+static const struct attribute_group ad7152_attribute_group = {
+ .attrs = ad7152_attributes,
+};
+
+static const u8 ad7152_addresses[][4] = {
+ { AD7152_REG_CH1_DATA_HIGH, AD7152_REG_CH1_OFFS_HIGH,
+ AD7152_REG_CH1_GAIN_HIGH, AD7152_REG_CH1_SETUP },
+ { AD7152_REG_CH2_DATA_HIGH, AD7152_REG_CH2_OFFS_HIGH,
+ AD7152_REG_CH2_GAIN_HIGH, AD7152_REG_CH2_SETUP },
+};
+
+/* Values are nano relative to pf base. */
+static const int ad7152_scale_table[] = {
+ 30525, 7631, 15263, 61050
+};
+
+static int ad7152_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int val,
+ int val2,
+ long mask)
+{
+ struct ad7152_chip_info *chip = iio_priv(indio_dev);
+ int ret, i;
+
+ mutex_lock(&indio_dev->mlock);
+
+ switch (mask) {
+ case IIO_CHAN_INFO_CALIBSCALE:
+ if (val != 1) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ val = (val2 * 1024) / 15625;
+
+ ret = i2c_smbus_write_word_data(chip->client,
+ ad7152_addresses[chan->channel][AD7152_GAIN],
+ swab16(val));
+ if (ret < 0)
+ goto out;
+
+ ret = 0;
+ break;
+
+ case IIO_CHAN_INFO_CALIBBIAS:
+ if ((val < 0) | (val > 0xFFFF)) {
+ ret = -EINVAL;
+ goto out;
+ }
+ ret = i2c_smbus_write_word_data(chip->client,
+ ad7152_addresses[chan->channel][AD7152_OFFS],
+ swab16(val));
+ if (ret < 0)
+ goto out;
+
+ ret = 0;
+ break;
+ case IIO_CHAN_INFO_SCALE:
+ if (val != 0) {
+ ret = -EINVAL;
+ goto out;
+ }
+ for (i = 0; i < ARRAY_SIZE(ad7152_scale_table); i++)
+ if (val2 == ad7152_scale_table[i])
+ break;
+
+ chip->setup[chan->channel] &= ~AD7152_SETUP_RANGE_4pF;
+ chip->setup[chan->channel] |= AD7152_SETUP_RANGE(i);
+
+ ret = i2c_smbus_write_byte_data(chip->client,
+ ad7152_addresses[chan->channel][AD7152_SETUP],
+ chip->setup[chan->channel]);
+ if (ret < 0)
+ goto out;
+
+ ret = 0;
+ break;
+ default:
+ ret = -EINVAL;
+ }
+
+out:
+ mutex_unlock(&indio_dev->mlock);
+ return ret;
+}
+static int ad7152_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2,
+ long mask)
+{
+ struct ad7152_chip_info *chip = iio_priv(indio_dev);
+ int ret;
+ u8 regval = 0;
+
+ mutex_lock(&indio_dev->mlock);
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ /* First set whether in differential mode */
+
+ regval = chip->setup[chan->channel];
+
+ if (chan->differential)
+ chip->setup[chan->channel] |= AD7152_SETUP_CAPDIFF;
+ else
+ chip->setup[chan->channel] &= ~AD7152_SETUP_CAPDIFF;
+
+ if (regval != chip->setup[chan->channel]) {
+ ret = i2c_smbus_write_byte_data(chip->client,
+ ad7152_addresses[chan->channel][AD7152_SETUP],
+ chip->setup[chan->channel]);
+ if (ret < 0)
+ goto out;
+ }
+ /* Make sure the channel is enabled */
+ if (chan->channel == 0)
+ regval = AD7152_CONF_CH1EN;
+ else
+ regval = AD7152_CONF_CH2EN;
+
+ /* Trigger a single read */
+ regval |= AD7152_CONF_MODE_SINGLE_CONV;
+ ret = i2c_smbus_write_byte_data(chip->client, AD7152_REG_CFG,
+ regval);
+ if (ret < 0)
+ goto out;
+
+ msleep(ad7152_filter_rate_table[chip->filter_rate_setup][1]);
+ /* Now read the actual register */
+ ret = i2c_smbus_read_word_data(chip->client,
+ ad7152_addresses[chan->channel][AD7152_DATA]);
+ if (ret < 0)
+ goto out;
+ *val = swab16(ret);
+
+ if (chan->differential)
+ *val -= 0x8000;
+
+ ret = IIO_VAL_INT;
+ break;
+ case IIO_CHAN_INFO_CALIBSCALE:
+
+ ret = i2c_smbus_read_word_data(chip->client,
+ ad7152_addresses[chan->channel][AD7152_GAIN]);
+ if (ret < 0)
+ goto out;
+ /* 1 + gain_val / 2^16 */
+ *val = 1;
+ *val2 = (15625 * swab16(ret)) / 1024;
+
+ ret = IIO_VAL_INT_PLUS_MICRO;
+ break;
+ case IIO_CHAN_INFO_CALIBBIAS:
+ ret = i2c_smbus_read_word_data(chip->client,
+ ad7152_addresses[chan->channel][AD7152_OFFS]);
+ if (ret < 0)
+ goto out;
+ *val = swab16(ret);
+
+ ret = IIO_VAL_INT;
+ break;
+ case IIO_CHAN_INFO_SCALE:
+ ret = i2c_smbus_read_byte_data(chip->client,
+ ad7152_addresses[chan->channel][AD7152_SETUP]);
+ if (ret < 0)
+ goto out;
+ *val = 0;
+ *val2 = ad7152_scale_table[ret >> 6];
+
+ ret = IIO_VAL_INT_PLUS_NANO;
+ break;
+ default:
+ ret = -EINVAL;
+ }
+out:
+ mutex_unlock(&indio_dev->mlock);
+ return ret;
+}
+
+static int ad7152_write_raw_get_fmt(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ long mask)
+{
+ switch (mask) {
+ case IIO_CHAN_INFO_SCALE:
+ return IIO_VAL_INT_PLUS_NANO;
+ default:
+ return IIO_VAL_INT_PLUS_MICRO;
+ }
+}
+
+static const struct iio_info ad7152_info = {
+ .attrs = &ad7152_attribute_group,
+ .read_raw = &ad7152_read_raw,
+ .write_raw = &ad7152_write_raw,
+ .write_raw_get_fmt = &ad7152_write_raw_get_fmt,
+ .driver_module = THIS_MODULE,
+};
+
+static const struct iio_chan_spec ad7152_channels[] = {
+ {
+ .type = IIO_CAPACITANCE,
+ .indexed = 1,
+ .channel = 0,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+ BIT(IIO_CHAN_INFO_CALIBSCALE) |
+ BIT(IIO_CHAN_INFO_CALIBBIAS) |
+ BIT(IIO_CHAN_INFO_SCALE),
+ }, {
+ .type = IIO_CAPACITANCE,
+ .differential = 1,
+ .indexed = 1,
+ .channel = 0,
+ .channel2 = 2,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+ BIT(IIO_CHAN_INFO_CALIBSCALE) |
+ BIT(IIO_CHAN_INFO_CALIBBIAS) |
+ BIT(IIO_CHAN_INFO_SCALE),
+ }, {
+ .type = IIO_CAPACITANCE,
+ .indexed = 1,
+ .channel = 1,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+ BIT(IIO_CHAN_INFO_CALIBSCALE) |
+ BIT(IIO_CHAN_INFO_CALIBBIAS) |
+ BIT(IIO_CHAN_INFO_SCALE),
+ }, {
+ .type = IIO_CAPACITANCE,
+ .differential = 1,
+ .indexed = 1,
+ .channel = 1,
+ .channel2 = 3,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+ BIT(IIO_CHAN_INFO_CALIBSCALE) |
+ BIT(IIO_CHAN_INFO_CALIBBIAS) |
+ BIT(IIO_CHAN_INFO_SCALE),
+ }
+};
+/*
+ * device probe and remove
+ */
+
+static int ad7152_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ int ret = 0;
+ struct ad7152_chip_info *chip;
+ struct iio_dev *indio_dev;
+
+ indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*chip));
+ if (!indio_dev)
+ return -ENOMEM;
+ chip = iio_priv(indio_dev);
+ /* this is only used for device removal purposes */
+ i2c_set_clientdata(client, indio_dev);
+
+ chip->client = client;
+
+ /* Establish that the iio_dev is a child of the i2c device */
+ indio_dev->name = id->name;
+ indio_dev->dev.parent = &client->dev;
+ indio_dev->info = &ad7152_info;
+ indio_dev->channels = ad7152_channels;
+ if (id->driver_data == 0)
+ indio_dev->num_channels = ARRAY_SIZE(ad7152_channels);
+ else
+ indio_dev->num_channels = 2;
+ indio_dev->num_channels = ARRAY_SIZE(ad7152_channels);
+ indio_dev->modes = INDIO_DIRECT_MODE;
+
+ ret = iio_device_register(indio_dev);
+ if (ret)
+ return ret;
+
+ dev_err(&client->dev, "%s capacitive sensor registered\n", id->name);
+
+ return 0;
+}
+
+static int ad7152_remove(struct i2c_client *client)
+{
+ struct iio_dev *indio_dev = i2c_get_clientdata(client);
+
+ iio_device_unregister(indio_dev);
+
+ return 0;
+}
+
+static const struct i2c_device_id ad7152_id[] = {
+ { "ad7152", 0 },
+ { "ad7153", 1 },
+ {}
+};
+
+MODULE_DEVICE_TABLE(i2c, ad7152_id);
+
+static struct i2c_driver ad7152_driver = {
+ .driver = {
+ .name = KBUILD_MODNAME,
+ },
+ .probe = ad7152_probe,
+ .remove = ad7152_remove,
+ .id_table = ad7152_id,
+};
+module_i2c_driver(ad7152_driver);
+
+MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>");
+MODULE_DESCRIPTION("Analog Devices AD7152/3 capacitive sensor driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/staging/iio/cdc/ad7746.c b/drivers/staging/iio/cdc/ad7746.c
new file mode 100644
index 000000000..e6e9eaa9e
--- /dev/null
+++ b/drivers/staging/iio/cdc/ad7746.c
@@ -0,0 +1,791 @@
+/*
+ * AD7746 capacitive sensor driver supporting AD7745, AD7746 and AD7747
+ *
+ * Copyright 2011 Analog Devices Inc.
+ *
+ * Licensed under the GPL-2.
+ */
+
+#include <linux/interrupt.h>
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/sysfs.h>
+#include <linux/i2c.h>
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/stat.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+
+#include "ad7746.h"
+
+/*
+ * AD7746 Register Definition
+ */
+
+#define AD7746_REG_STATUS 0
+#define AD7746_REG_CAP_DATA_HIGH 1
+#define AD7746_REG_CAP_DATA_MID 2
+#define AD7746_REG_CAP_DATA_LOW 3
+#define AD7746_REG_VT_DATA_HIGH 4
+#define AD7746_REG_VT_DATA_MID 5
+#define AD7746_REG_VT_DATA_LOW 6
+#define AD7746_REG_CAP_SETUP 7
+#define AD7746_REG_VT_SETUP 8
+#define AD7746_REG_EXC_SETUP 9
+#define AD7746_REG_CFG 10
+#define AD7746_REG_CAPDACA 11
+#define AD7746_REG_CAPDACB 12
+#define AD7746_REG_CAP_OFFH 13
+#define AD7746_REG_CAP_OFFL 14
+#define AD7746_REG_CAP_GAINH 15
+#define AD7746_REG_CAP_GAINL 16
+#define AD7746_REG_VOLT_GAINH 17
+#define AD7746_REG_VOLT_GAINL 18
+
+/* Status Register Bit Designations (AD7746_REG_STATUS) */
+#define AD7746_STATUS_EXCERR (1 << 3)
+#define AD7746_STATUS_RDY (1 << 2)
+#define AD7746_STATUS_RDYVT (1 << 1)
+#define AD7746_STATUS_RDYCAP (1 << 0)
+
+/* Capacitive Channel Setup Register Bit Designations (AD7746_REG_CAP_SETUP) */
+#define AD7746_CAPSETUP_CAPEN (1 << 7)
+#define AD7746_CAPSETUP_CIN2 (1 << 6) /* AD7746 only */
+#define AD7746_CAPSETUP_CAPDIFF (1 << 5)
+#define AD7746_CAPSETUP_CACHOP (1 << 0)
+
+/* Voltage/Temperature Setup Register Bit Designations (AD7746_REG_VT_SETUP) */
+#define AD7746_VTSETUP_VTEN (1 << 7)
+#define AD7746_VTSETUP_VTMD_INT_TEMP (0 << 5)
+#define AD7746_VTSETUP_VTMD_EXT_TEMP (1 << 5)
+#define AD7746_VTSETUP_VTMD_VDD_MON (2 << 5)
+#define AD7746_VTSETUP_VTMD_EXT_VIN (3 << 5)
+#define AD7746_VTSETUP_EXTREF (1 << 4)
+#define AD7746_VTSETUP_VTSHORT (1 << 1)
+#define AD7746_VTSETUP_VTCHOP (1 << 0)
+
+/* Excitation Setup Register Bit Designations (AD7746_REG_EXC_SETUP) */
+#define AD7746_EXCSETUP_CLKCTRL (1 << 7)
+#define AD7746_EXCSETUP_EXCON (1 << 6)
+#define AD7746_EXCSETUP_EXCB (1 << 5)
+#define AD7746_EXCSETUP_NEXCB (1 << 4)
+#define AD7746_EXCSETUP_EXCA (1 << 3)
+#define AD7746_EXCSETUP_NEXCA (1 << 2)
+#define AD7746_EXCSETUP_EXCLVL(x) (((x) & 0x3) << 0)
+
+/* Config Register Bit Designations (AD7746_REG_CFG) */
+#define AD7746_CONF_VTFS(x) ((x) << 6)
+#define AD7746_CONF_CAPFS(x) ((x) << 3)
+#define AD7746_CONF_MODE_IDLE (0 << 0)
+#define AD7746_CONF_MODE_CONT_CONV (1 << 0)
+#define AD7746_CONF_MODE_SINGLE_CONV (2 << 0)
+#define AD7746_CONF_MODE_PWRDN (3 << 0)
+#define AD7746_CONF_MODE_OFFS_CAL (5 << 0)
+#define AD7746_CONF_MODE_GAIN_CAL (6 << 0)
+
+/* CAPDAC Register Bit Designations (AD7746_REG_CAPDACx) */
+#define AD7746_CAPDAC_DACEN (1 << 7)
+#define AD7746_CAPDAC_DACP(x) ((x) & 0x7F)
+
+/*
+ * struct ad7746_chip_info - chip specific information
+ */
+
+struct ad7746_chip_info {
+ struct i2c_client *client;
+ /*
+ * Capacitive channel digital filter setup;
+ * conversion time/update rate setup per channel
+ */
+ u8 config;
+ u8 cap_setup;
+ u8 vt_setup;
+ u8 capdac[2][2];
+ s8 capdac_set;
+
+ union {
+ __be32 d32;
+ u8 d8[4];
+ } data ____cacheline_aligned;
+};
+
+enum ad7746_chan {
+ VIN,
+ VIN_VDD,
+ TEMP_INT,
+ TEMP_EXT,
+ CIN1,
+ CIN1_DIFF,
+ CIN2,
+ CIN2_DIFF,
+};
+
+static const struct iio_chan_spec ad7746_channels[] = {
+ [VIN] = {
+ .type = IIO_VOLTAGE,
+ .indexed = 1,
+ .channel = 0,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),
+ .address = AD7746_REG_VT_DATA_HIGH << 8 |
+ AD7746_VTSETUP_VTMD_EXT_VIN,
+ },
+ [VIN_VDD] = {
+ .type = IIO_VOLTAGE,
+ .indexed = 1,
+ .channel = 1,
+ .extend_name = "supply",
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),
+ .address = AD7746_REG_VT_DATA_HIGH << 8 |
+ AD7746_VTSETUP_VTMD_VDD_MON,
+ },
+ [TEMP_INT] = {
+ .type = IIO_TEMP,
+ .indexed = 1,
+ .channel = 0,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED),
+ .address = AD7746_REG_VT_DATA_HIGH << 8 |
+ AD7746_VTSETUP_VTMD_INT_TEMP,
+ },
+ [TEMP_EXT] = {
+ .type = IIO_TEMP,
+ .indexed = 1,
+ .channel = 1,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED),
+ .address = AD7746_REG_VT_DATA_HIGH << 8 |
+ AD7746_VTSETUP_VTMD_EXT_TEMP,
+ },
+ [CIN1] = {
+ .type = IIO_CAPACITANCE,
+ .indexed = 1,
+ .channel = 0,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+ BIT(IIO_CHAN_INFO_CALIBSCALE) | BIT(IIO_CHAN_INFO_OFFSET),
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_CALIBBIAS) |
+ BIT(IIO_CHAN_INFO_SCALE),
+ .address = AD7746_REG_CAP_DATA_HIGH << 8,
+ },
+ [CIN1_DIFF] = {
+ .type = IIO_CAPACITANCE,
+ .differential = 1,
+ .indexed = 1,
+ .channel = 0,
+ .channel2 = 2,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+ BIT(IIO_CHAN_INFO_CALIBSCALE) | BIT(IIO_CHAN_INFO_OFFSET),
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_CALIBBIAS) |
+ BIT(IIO_CHAN_INFO_SCALE),
+ .address = AD7746_REG_CAP_DATA_HIGH << 8 |
+ AD7746_CAPSETUP_CAPDIFF
+ },
+ [CIN2] = {
+ .type = IIO_CAPACITANCE,
+ .indexed = 1,
+ .channel = 1,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+ BIT(IIO_CHAN_INFO_CALIBSCALE) | BIT(IIO_CHAN_INFO_OFFSET),
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_CALIBBIAS) |
+ BIT(IIO_CHAN_INFO_SCALE),
+ .address = AD7746_REG_CAP_DATA_HIGH << 8 |
+ AD7746_CAPSETUP_CIN2,
+ },
+ [CIN2_DIFF] = {
+ .type = IIO_CAPACITANCE,
+ .differential = 1,
+ .indexed = 1,
+ .channel = 1,
+ .channel2 = 3,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+ BIT(IIO_CHAN_INFO_CALIBSCALE) | BIT(IIO_CHAN_INFO_OFFSET),
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_CALIBBIAS) |
+ BIT(IIO_CHAN_INFO_SCALE),
+ .address = AD7746_REG_CAP_DATA_HIGH << 8 |
+ AD7746_CAPSETUP_CAPDIFF | AD7746_CAPSETUP_CIN2,
+ }
+};
+
+/* Values are Update Rate (Hz), Conversion Time (ms) + 1*/
+static const unsigned char ad7746_vt_filter_rate_table[][2] = {
+ {50, 20 + 1}, {31, 32 + 1}, {16, 62 + 1}, {8, 122 + 1},
+};
+
+static const unsigned char ad7746_cap_filter_rate_table[][2] = {
+ {91, 11 + 1}, {84, 12 + 1}, {50, 20 + 1}, {26, 38 + 1},
+ {16, 62 + 1}, {13, 77 + 1}, {11, 92 + 1}, {9, 110 + 1},
+};
+
+static int ad7746_select_channel(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan)
+{
+ struct ad7746_chip_info *chip = iio_priv(indio_dev);
+ int ret, delay;
+ u8 vt_setup, cap_setup;
+
+ switch (chan->type) {
+ case IIO_CAPACITANCE:
+ cap_setup = (chan->address & 0xFF) | AD7746_CAPSETUP_CAPEN;
+ vt_setup = chip->vt_setup & ~AD7746_VTSETUP_VTEN;
+ delay = ad7746_cap_filter_rate_table[(chip->config >> 3) &
+ 0x7][1];
+
+ if (chip->capdac_set != chan->channel) {
+ ret = i2c_smbus_write_byte_data(chip->client,
+ AD7746_REG_CAPDACA,
+ chip->capdac[chan->channel][0]);
+ if (ret < 0)
+ return ret;
+ ret = i2c_smbus_write_byte_data(chip->client,
+ AD7746_REG_CAPDACB,
+ chip->capdac[chan->channel][1]);
+ if (ret < 0)
+ return ret;
+
+ chip->capdac_set = chan->channel;
+ }
+ break;
+ case IIO_VOLTAGE:
+ case IIO_TEMP:
+ vt_setup = (chan->address & 0xFF) | AD7746_VTSETUP_VTEN;
+ cap_setup = chip->cap_setup & ~AD7746_CAPSETUP_CAPEN;
+ delay = ad7746_cap_filter_rate_table[(chip->config >> 6) &
+ 0x3][1];
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (chip->cap_setup != cap_setup) {
+ ret = i2c_smbus_write_byte_data(chip->client,
+ AD7746_REG_CAP_SETUP,
+ cap_setup);
+ if (ret < 0)
+ return ret;
+
+ chip->cap_setup = cap_setup;
+ }
+
+ if (chip->vt_setup != vt_setup) {
+ ret = i2c_smbus_write_byte_data(chip->client,
+ AD7746_REG_VT_SETUP,
+ vt_setup);
+ if (ret < 0)
+ return ret;
+
+ chip->vt_setup = vt_setup;
+ }
+
+ return delay;
+}
+
+static inline ssize_t ad7746_start_calib(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t len,
+ u8 regval)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct ad7746_chip_info *chip = iio_priv(indio_dev);
+ bool doit;
+ int ret, timeout = 10;
+
+ ret = strtobool(buf, &doit);
+ if (ret < 0)
+ return ret;
+
+ if (!doit)
+ return 0;
+
+ mutex_lock(&indio_dev->mlock);
+ regval |= chip->config;
+ ret = i2c_smbus_write_byte_data(chip->client, AD7746_REG_CFG, regval);
+ if (ret < 0) {
+ mutex_unlock(&indio_dev->mlock);
+ return ret;
+ }
+
+ do {
+ msleep(20);
+ ret = i2c_smbus_read_byte_data(chip->client, AD7746_REG_CFG);
+ if (ret < 0) {
+ mutex_unlock(&indio_dev->mlock);
+ return ret;
+ }
+ } while ((ret == regval) && timeout--);
+
+ mutex_unlock(&indio_dev->mlock);
+
+ return len;
+}
+
+static ssize_t ad7746_start_offset_calib(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t len)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ int ret = ad7746_select_channel(indio_dev,
+ &ad7746_channels[to_iio_dev_attr(attr)->address]);
+ if (ret < 0)
+ return ret;
+
+ return ad7746_start_calib(dev, attr, buf, len,
+ AD7746_CONF_MODE_OFFS_CAL);
+}
+
+static ssize_t ad7746_start_gain_calib(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t len)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ int ret = ad7746_select_channel(indio_dev,
+ &ad7746_channels[to_iio_dev_attr(attr)->address]);
+ if (ret < 0)
+ return ret;
+
+ return ad7746_start_calib(dev, attr, buf, len,
+ AD7746_CONF_MODE_GAIN_CAL);
+}
+
+static IIO_DEVICE_ATTR(in_capacitance0_calibbias_calibration,
+ S_IWUSR, NULL, ad7746_start_offset_calib, CIN1);
+static IIO_DEVICE_ATTR(in_capacitance1_calibbias_calibration,
+ S_IWUSR, NULL, ad7746_start_offset_calib, CIN2);
+static IIO_DEVICE_ATTR(in_capacitance0_calibscale_calibration,
+ S_IWUSR, NULL, ad7746_start_gain_calib, CIN1);
+static IIO_DEVICE_ATTR(in_capacitance1_calibscale_calibration,
+ S_IWUSR, NULL, ad7746_start_gain_calib, CIN2);
+static IIO_DEVICE_ATTR(in_voltage0_calibscale_calibration,
+ S_IWUSR, NULL, ad7746_start_gain_calib, VIN);
+
+static ssize_t ad7746_show_cap_filter_rate_setup(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct ad7746_chip_info *chip = iio_priv(indio_dev);
+
+ return sprintf(buf, "%d\n", ad7746_cap_filter_rate_table[
+ (chip->config >> 3) & 0x7][0]);
+}
+
+static ssize_t ad7746_store_cap_filter_rate_setup(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t len)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct ad7746_chip_info *chip = iio_priv(indio_dev);
+ u8 data;
+ int ret, i;
+
+ ret = kstrtou8(buf, 10, &data);
+ if (ret < 0)
+ return ret;
+
+ for (i = 0; i < ARRAY_SIZE(ad7746_cap_filter_rate_table); i++)
+ if (data >= ad7746_cap_filter_rate_table[i][0])
+ break;
+
+ if (i >= ARRAY_SIZE(ad7746_cap_filter_rate_table))
+ i = ARRAY_SIZE(ad7746_cap_filter_rate_table) - 1;
+
+ mutex_lock(&indio_dev->mlock);
+ chip->config &= ~AD7746_CONF_CAPFS(0x7);
+ chip->config |= AD7746_CONF_CAPFS(i);
+ mutex_unlock(&indio_dev->mlock);
+
+ return len;
+}
+
+static ssize_t ad7746_show_vt_filter_rate_setup(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct ad7746_chip_info *chip = iio_priv(indio_dev);
+
+ return sprintf(buf, "%d\n", ad7746_vt_filter_rate_table[
+ (chip->config >> 6) & 0x3][0]);
+}
+
+static ssize_t ad7746_store_vt_filter_rate_setup(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t len)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct ad7746_chip_info *chip = iio_priv(indio_dev);
+ u8 data;
+ int ret, i;
+
+ ret = kstrtou8(buf, 10, &data);
+ if (ret < 0)
+ return ret;
+
+ for (i = 0; i < ARRAY_SIZE(ad7746_vt_filter_rate_table); i++)
+ if (data >= ad7746_vt_filter_rate_table[i][0])
+ break;
+
+ if (i >= ARRAY_SIZE(ad7746_vt_filter_rate_table))
+ i = ARRAY_SIZE(ad7746_vt_filter_rate_table) - 1;
+
+ mutex_lock(&indio_dev->mlock);
+ chip->config &= ~AD7746_CONF_VTFS(0x3);
+ chip->config |= AD7746_CONF_VTFS(i);
+ mutex_unlock(&indio_dev->mlock);
+
+ return len;
+}
+
+static IIO_DEVICE_ATTR(in_capacitance_sampling_frequency,
+ S_IRUGO | S_IWUSR, ad7746_show_cap_filter_rate_setup,
+ ad7746_store_cap_filter_rate_setup, 0);
+
+static IIO_DEVICE_ATTR(in_voltage_sampling_frequency,
+ S_IRUGO | S_IWUSR, ad7746_show_vt_filter_rate_setup,
+ ad7746_store_vt_filter_rate_setup, 0);
+
+static IIO_CONST_ATTR(in_voltage_sampling_frequency_available, "50 31 16 8");
+static IIO_CONST_ATTR(in_capacitance_sampling_frequency_available,
+ "91 84 50 26 16 13 11 9");
+
+static struct attribute *ad7746_attributes[] = {
+ &iio_dev_attr_in_capacitance_sampling_frequency.dev_attr.attr,
+ &iio_dev_attr_in_voltage_sampling_frequency.dev_attr.attr,
+ &iio_dev_attr_in_capacitance0_calibbias_calibration.dev_attr.attr,
+ &iio_dev_attr_in_capacitance0_calibscale_calibration.dev_attr.attr,
+ &iio_dev_attr_in_capacitance1_calibscale_calibration.dev_attr.attr,
+ &iio_dev_attr_in_capacitance1_calibbias_calibration.dev_attr.attr,
+ &iio_dev_attr_in_voltage0_calibscale_calibration.dev_attr.attr,
+ &iio_const_attr_in_voltage_sampling_frequency_available.dev_attr.attr,
+ &iio_const_attr_in_capacitance_sampling_frequency_available.
+ dev_attr.attr,
+ NULL,
+};
+
+static const struct attribute_group ad7746_attribute_group = {
+ .attrs = ad7746_attributes,
+};
+
+static int ad7746_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int val,
+ int val2,
+ long mask)
+{
+ struct ad7746_chip_info *chip = iio_priv(indio_dev);
+ int ret, reg;
+
+ mutex_lock(&indio_dev->mlock);
+
+ switch (mask) {
+ case IIO_CHAN_INFO_CALIBSCALE:
+ if (val != 1) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ val = (val2 * 1024) / 15625;
+
+ switch (chan->type) {
+ case IIO_CAPACITANCE:
+ reg = AD7746_REG_CAP_GAINH;
+ break;
+ case IIO_VOLTAGE:
+ reg = AD7746_REG_VOLT_GAINH;
+ break;
+ default:
+ ret = -EINVAL;
+ goto out;
+ }
+
+ ret = i2c_smbus_write_word_data(chip->client, reg, swab16(val));
+ if (ret < 0)
+ goto out;
+
+ ret = 0;
+ break;
+ case IIO_CHAN_INFO_CALIBBIAS:
+ if ((val < 0) | (val > 0xFFFF)) {
+ ret = -EINVAL;
+ goto out;
+ }
+ ret = i2c_smbus_write_word_data(chip->client,
+ AD7746_REG_CAP_OFFH, swab16(val));
+ if (ret < 0)
+ goto out;
+
+ ret = 0;
+ break;
+ case IIO_CHAN_INFO_OFFSET:
+ if ((val < 0) | (val > 43008000)) { /* 21pF */
+ ret = -EINVAL;
+ goto out;
+ }
+
+ /* CAPDAC Scale = 21pF_typ / 127
+ * CIN Scale = 8.192pF / 2^24
+ * Offset Scale = CAPDAC Scale / CIN Scale = 338646
+ * */
+
+ val /= 338646;
+
+ chip->capdac[chan->channel][chan->differential] = (val > 0 ?
+ AD7746_CAPDAC_DACP(val) | AD7746_CAPDAC_DACEN : 0);
+
+ ret = i2c_smbus_write_byte_data(chip->client,
+ AD7746_REG_CAPDACA,
+ chip->capdac[chan->channel][0]);
+ if (ret < 0)
+ goto out;
+ ret = i2c_smbus_write_byte_data(chip->client,
+ AD7746_REG_CAPDACB,
+ chip->capdac[chan->channel][1]);
+ if (ret < 0)
+ goto out;
+
+ chip->capdac_set = chan->channel;
+
+ ret = 0;
+ break;
+ default:
+ ret = -EINVAL;
+ }
+
+out:
+ mutex_unlock(&indio_dev->mlock);
+ return ret;
+}
+
+static int ad7746_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2,
+ long mask)
+{
+ struct ad7746_chip_info *chip = iio_priv(indio_dev);
+ int ret, delay;
+ u8 regval, reg;
+
+ mutex_lock(&indio_dev->mlock);
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ case IIO_CHAN_INFO_PROCESSED:
+ ret = ad7746_select_channel(indio_dev, chan);
+ if (ret < 0)
+ goto out;
+ delay = ret;
+
+ regval = chip->config | AD7746_CONF_MODE_SINGLE_CONV;
+ ret = i2c_smbus_write_byte_data(chip->client, AD7746_REG_CFG,
+ regval);
+ if (ret < 0)
+ goto out;
+
+ msleep(delay);
+ /* Now read the actual register */
+
+ ret = i2c_smbus_read_i2c_block_data(chip->client,
+ chan->address >> 8, 3, &chip->data.d8[1]);
+
+ if (ret < 0)
+ goto out;
+
+ *val = (be32_to_cpu(chip->data.d32) & 0xFFFFFF) - 0x800000;
+
+ switch (chan->type) {
+ case IIO_TEMP:
+ /* temperature in milli degrees Celsius
+ * T = ((*val / 2048) - 4096) * 1000
+ */
+ *val = (*val * 125) / 256;
+ break;
+ case IIO_VOLTAGE:
+ if (chan->channel == 1) /* supply_raw*/
+ *val = *val * 6;
+ break;
+ default:
+ break;
+ }
+
+ ret = IIO_VAL_INT;
+ break;
+ case IIO_CHAN_INFO_CALIBSCALE:
+ switch (chan->type) {
+ case IIO_CAPACITANCE:
+ reg = AD7746_REG_CAP_GAINH;
+ break;
+ case IIO_VOLTAGE:
+ reg = AD7746_REG_VOLT_GAINH;
+ break;
+ default:
+ ret = -EINVAL;
+ goto out;
+ }
+
+ ret = i2c_smbus_read_word_data(chip->client, reg);
+ if (ret < 0)
+ goto out;
+ /* 1 + gain_val / 2^16 */
+ *val = 1;
+ *val2 = (15625 * swab16(ret)) / 1024;
+
+ ret = IIO_VAL_INT_PLUS_MICRO;
+ break;
+ case IIO_CHAN_INFO_CALIBBIAS:
+ ret = i2c_smbus_read_word_data(chip->client,
+ AD7746_REG_CAP_OFFH);
+ if (ret < 0)
+ goto out;
+ *val = swab16(ret);
+
+ ret = IIO_VAL_INT;
+ break;
+ case IIO_CHAN_INFO_OFFSET:
+ *val = AD7746_CAPDAC_DACP(chip->capdac[chan->channel]
+ [chan->differential]) * 338646;
+
+ ret = IIO_VAL_INT;
+ break;
+ case IIO_CHAN_INFO_SCALE:
+ switch (chan->type) {
+ case IIO_CAPACITANCE:
+ /* 8.192pf / 2^24 */
+ *val = 0;
+ *val2 = 488;
+ ret = IIO_VAL_INT_PLUS_NANO;
+ break;
+ case IIO_VOLTAGE:
+ /* 1170mV / 2^23 */
+ *val = 1170;
+ *val2 = 23;
+ ret = IIO_VAL_FRACTIONAL_LOG2;
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ break;
+ default:
+ ret = -EINVAL;
+ }
+out:
+ mutex_unlock(&indio_dev->mlock);
+ return ret;
+}
+
+static const struct iio_info ad7746_info = {
+ .attrs = &ad7746_attribute_group,
+ .read_raw = &ad7746_read_raw,
+ .write_raw = &ad7746_write_raw,
+ .driver_module = THIS_MODULE,
+};
+
+/*
+ * device probe and remove
+ */
+
+static int ad7746_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct ad7746_platform_data *pdata = client->dev.platform_data;
+ struct ad7746_chip_info *chip;
+ struct iio_dev *indio_dev;
+ int ret = 0;
+ unsigned char regval = 0;
+
+ indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*chip));
+ if (!indio_dev)
+ return -ENOMEM;
+ chip = iio_priv(indio_dev);
+ /* this is only used for device removal purposes */
+ i2c_set_clientdata(client, indio_dev);
+
+ chip->client = client;
+ chip->capdac_set = -1;
+
+ /* Establish that the iio_dev is a child of the i2c device */
+ indio_dev->name = id->name;
+ indio_dev->dev.parent = &client->dev;
+ indio_dev->info = &ad7746_info;
+ indio_dev->channels = ad7746_channels;
+ if (id->driver_data == 7746)
+ indio_dev->num_channels = ARRAY_SIZE(ad7746_channels);
+ else
+ indio_dev->num_channels = ARRAY_SIZE(ad7746_channels) - 2;
+ indio_dev->num_channels = ARRAY_SIZE(ad7746_channels);
+ indio_dev->modes = INDIO_DIRECT_MODE;
+
+ if (pdata) {
+ if (pdata->exca_en) {
+ if (pdata->exca_inv_en)
+ regval |= AD7746_EXCSETUP_NEXCA;
+ else
+ regval |= AD7746_EXCSETUP_EXCA;
+ }
+
+ if (pdata->excb_en) {
+ if (pdata->excb_inv_en)
+ regval |= AD7746_EXCSETUP_NEXCB;
+ else
+ regval |= AD7746_EXCSETUP_EXCB;
+ }
+
+ regval |= AD7746_EXCSETUP_EXCLVL(pdata->exclvl);
+ } else {
+ dev_warn(&client->dev, "No platform data? using default\n");
+ regval = AD7746_EXCSETUP_EXCA | AD7746_EXCSETUP_EXCB |
+ AD7746_EXCSETUP_EXCLVL(3);
+ }
+
+ ret = i2c_smbus_write_byte_data(chip->client,
+ AD7746_REG_EXC_SETUP, regval);
+ if (ret < 0)
+ return ret;
+
+ ret = iio_device_register(indio_dev);
+ if (ret)
+ return ret;
+
+ dev_info(&client->dev, "%s capacitive sensor registered\n", id->name);
+
+ return 0;
+}
+
+static int ad7746_remove(struct i2c_client *client)
+{
+ struct iio_dev *indio_dev = i2c_get_clientdata(client);
+
+ iio_device_unregister(indio_dev);
+
+ return 0;
+}
+
+static const struct i2c_device_id ad7746_id[] = {
+ { "ad7745", 7745 },
+ { "ad7746", 7746 },
+ { "ad7747", 7747 },
+ {}
+};
+
+MODULE_DEVICE_TABLE(i2c, ad7746_id);
+
+static struct i2c_driver ad7746_driver = {
+ .driver = {
+ .name = KBUILD_MODNAME,
+ },
+ .probe = ad7746_probe,
+ .remove = ad7746_remove,
+ .id_table = ad7746_id,
+};
+module_i2c_driver(ad7746_driver);
+
+MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
+MODULE_DESCRIPTION("Analog Devices AD7746/5/7 capacitive sensor driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/staging/iio/cdc/ad7746.h b/drivers/staging/iio/cdc/ad7746.h
new file mode 100644
index 000000000..ea8572d1d
--- /dev/null
+++ b/drivers/staging/iio/cdc/ad7746.h
@@ -0,0 +1,29 @@
+/*
+ * AD7746 capacitive sensor driver supporting AD7745, AD7746 and AD7747
+ *
+ * Copyright 2011 Analog Devices Inc.
+ *
+ * Licensed under the GPL-2.
+ */
+
+#ifndef IIO_CDC_AD7746_H_
+#define IIO_CDC_AD7746_H_
+
+/*
+ * TODO: struct ad7746_platform_data needs to go into include/linux/iio
+ */
+
+#define AD7466_EXCLVL_0 0 /* +-VDD/8 */
+#define AD7466_EXCLVL_1 1 /* +-VDD/4 */
+#define AD7466_EXCLVL_2 2 /* +-VDD * 3/8 */
+#define AD7466_EXCLVL_3 3 /* +-VDD/2 */
+
+struct ad7746_platform_data {
+ unsigned char exclvl; /*Excitation Voltage Level */
+ bool exca_en; /* enables EXCA pin as the excitation output */
+ bool exca_inv_en; /* enables /EXCA pin as the excitation output */
+ bool excb_en; /* enables EXCB pin as the excitation output */
+ bool excb_inv_en; /* enables /EXCB pin as the excitation output */
+};
+
+#endif /* IIO_CDC_AD7746_H_ */
diff --git a/drivers/staging/iio/frequency/Kconfig b/drivers/staging/iio/frequency/Kconfig
new file mode 100644
index 000000000..fc726d3c6
--- /dev/null
+++ b/drivers/staging/iio/frequency/Kconfig
@@ -0,0 +1,26 @@
+#
+# Direct Digital Synthesis drivers
+#
+menu "Direct Digital Synthesis"
+
+config AD9832
+ tristate "Analog Devices ad9832/5 driver"
+ depends on SPI
+ help
+ Say yes here to build support for Analog Devices DDS chip
+ AD9832 and AD9835, provides direct access via sysfs.
+
+ To compile this driver as a module, choose M here: the
+ module will be called ad9832.
+
+config AD9834
+ tristate "Analog Devices AD9833/4/7/8 driver"
+ depends on SPI
+ help
+ Say yes here to build support for Analog Devices DDS chip
+ AD9833, AD9834, AD9837 and AD9838, provides direct access via sysfs.
+
+ To compile this driver as a module, choose M here: the
+ module will be called ad9834.
+
+endmenu
diff --git a/drivers/staging/iio/frequency/Makefile b/drivers/staging/iio/frequency/Makefile
new file mode 100644
index 000000000..e5dbcfce4
--- /dev/null
+++ b/drivers/staging/iio/frequency/Makefile
@@ -0,0 +1,6 @@
+#
+# Makefile for Direct Digital Synthesis drivers
+#
+
+obj-$(CONFIG_AD9832) += ad9832.o
+obj-$(CONFIG_AD9834) += ad9834.o
diff --git a/drivers/staging/iio/frequency/ad9832.c b/drivers/staging/iio/frequency/ad9832.c
new file mode 100644
index 000000000..a861fe014
--- /dev/null
+++ b/drivers/staging/iio/frequency/ad9832.c
@@ -0,0 +1,352 @@
+/*
+ * AD9832 SPI DDS driver
+ *
+ * Copyright 2011 Analog Devices Inc.
+ *
+ * Licensed under the GPL-2.
+ */
+
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/sysfs.h>
+#include <linux/spi/spi.h>
+#include <linux/regulator/consumer.h>
+#include <linux/err.h>
+#include <linux/module.h>
+#include <asm/div64.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include "dds.h"
+
+#include "ad9832.h"
+
+static unsigned long ad9832_calc_freqreg(unsigned long mclk, unsigned long fout)
+{
+ unsigned long long freqreg = (u64)fout *
+ (u64)((u64)1L << AD9832_FREQ_BITS);
+ do_div(freqreg, mclk);
+ return freqreg;
+}
+
+static int ad9832_write_frequency(struct ad9832_state *st,
+ unsigned addr, unsigned long fout)
+{
+ unsigned long regval;
+
+ if (fout > (st->mclk / 2))
+ return -EINVAL;
+
+ regval = ad9832_calc_freqreg(st->mclk, fout);
+
+ st->freq_data[0] = cpu_to_be16((AD9832_CMD_FRE8BITSW << CMD_SHIFT) |
+ (addr << ADD_SHIFT) |
+ ((regval >> 24) & 0xFF));
+ st->freq_data[1] = cpu_to_be16((AD9832_CMD_FRE16BITSW << CMD_SHIFT) |
+ ((addr - 1) << ADD_SHIFT) |
+ ((regval >> 16) & 0xFF));
+ st->freq_data[2] = cpu_to_be16((AD9832_CMD_FRE8BITSW << CMD_SHIFT) |
+ ((addr - 2) << ADD_SHIFT) |
+ ((regval >> 8) & 0xFF));
+ st->freq_data[3] = cpu_to_be16((AD9832_CMD_FRE16BITSW << CMD_SHIFT) |
+ ((addr - 3) << ADD_SHIFT) |
+ ((regval >> 0) & 0xFF));
+
+ return spi_sync(st->spi, &st->freq_msg);
+}
+
+static int ad9832_write_phase(struct ad9832_state *st,
+ unsigned long addr, unsigned long phase)
+{
+ if (phase > BIT(AD9832_PHASE_BITS))
+ return -EINVAL;
+
+ st->phase_data[0] = cpu_to_be16((AD9832_CMD_PHA8BITSW << CMD_SHIFT) |
+ (addr << ADD_SHIFT) |
+ ((phase >> 8) & 0xFF));
+ st->phase_data[1] = cpu_to_be16((AD9832_CMD_PHA16BITSW << CMD_SHIFT) |
+ ((addr - 1) << ADD_SHIFT) |
+ (phase & 0xFF));
+
+ return spi_sync(st->spi, &st->phase_msg);
+}
+
+static ssize_t ad9832_write(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t len)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct ad9832_state *st = iio_priv(indio_dev);
+ struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
+ int ret;
+ unsigned long val;
+
+ ret = kstrtoul(buf, 10, &val);
+ if (ret)
+ goto error_ret;
+
+ mutex_lock(&indio_dev->mlock);
+ switch ((u32)this_attr->address) {
+ case AD9832_FREQ0HM:
+ case AD9832_FREQ1HM:
+ ret = ad9832_write_frequency(st, this_attr->address, val);
+ break;
+ case AD9832_PHASE0H:
+ case AD9832_PHASE1H:
+ case AD9832_PHASE2H:
+ case AD9832_PHASE3H:
+ ret = ad9832_write_phase(st, this_attr->address, val);
+ break;
+ case AD9832_PINCTRL_EN:
+ if (val)
+ st->ctrl_ss &= ~AD9832_SELSRC;
+ else
+ st->ctrl_ss |= AD9832_SELSRC;
+ st->data = cpu_to_be16((AD9832_CMD_SYNCSELSRC << CMD_SHIFT) |
+ st->ctrl_ss);
+ ret = spi_sync(st->spi, &st->msg);
+ break;
+ case AD9832_FREQ_SYM:
+ if (val == 1) {
+ st->ctrl_fp |= AD9832_FREQ;
+ } else if (val == 0) {
+ st->ctrl_fp &= ~AD9832_FREQ;
+ } else {
+ ret = -EINVAL;
+ break;
+ }
+ st->data = cpu_to_be16((AD9832_CMD_FPSELECT << CMD_SHIFT) |
+ st->ctrl_fp);
+ ret = spi_sync(st->spi, &st->msg);
+ break;
+ case AD9832_PHASE_SYM:
+ if (val > 3) {
+ ret = -EINVAL;
+ break;
+ }
+
+ st->ctrl_fp &= ~AD9832_PHASE(3);
+ st->ctrl_fp |= AD9832_PHASE(val);
+
+ st->data = cpu_to_be16((AD9832_CMD_FPSELECT << CMD_SHIFT) |
+ st->ctrl_fp);
+ ret = spi_sync(st->spi, &st->msg);
+ break;
+ case AD9832_OUTPUT_EN:
+ if (val)
+ st->ctrl_src &= ~(AD9832_RESET | AD9832_SLEEP |
+ AD9832_CLR);
+ else
+ st->ctrl_src |= AD9832_RESET;
+
+ st->data = cpu_to_be16((AD9832_CMD_SLEEPRESCLR << CMD_SHIFT) |
+ st->ctrl_src);
+ ret = spi_sync(st->spi, &st->msg);
+ break;
+ default:
+ ret = -ENODEV;
+ }
+ mutex_unlock(&indio_dev->mlock);
+
+error_ret:
+ return ret ? ret : len;
+}
+
+/**
+ * see dds.h for further information
+ */
+
+static IIO_DEV_ATTR_FREQ(0, 0, S_IWUSR, NULL, ad9832_write, AD9832_FREQ0HM);
+static IIO_DEV_ATTR_FREQ(0, 1, S_IWUSR, NULL, ad9832_write, AD9832_FREQ1HM);
+static IIO_DEV_ATTR_FREQSYMBOL(0, S_IWUSR, NULL, ad9832_write, AD9832_FREQ_SYM);
+static IIO_CONST_ATTR_FREQ_SCALE(0, "1"); /* 1Hz */
+
+static IIO_DEV_ATTR_PHASE(0, 0, S_IWUSR, NULL, ad9832_write, AD9832_PHASE0H);
+static IIO_DEV_ATTR_PHASE(0, 1, S_IWUSR, NULL, ad9832_write, AD9832_PHASE1H);
+static IIO_DEV_ATTR_PHASE(0, 2, S_IWUSR, NULL, ad9832_write, AD9832_PHASE2H);
+static IIO_DEV_ATTR_PHASE(0, 3, S_IWUSR, NULL, ad9832_write, AD9832_PHASE3H);
+static IIO_DEV_ATTR_PHASESYMBOL(0, S_IWUSR, NULL,
+ ad9832_write, AD9832_PHASE_SYM);
+static IIO_CONST_ATTR_PHASE_SCALE(0, "0.0015339808"); /* 2PI/2^12 rad*/
+
+static IIO_DEV_ATTR_PINCONTROL_EN(0, S_IWUSR, NULL,
+ ad9832_write, AD9832_PINCTRL_EN);
+static IIO_DEV_ATTR_OUT_ENABLE(0, S_IWUSR, NULL,
+ ad9832_write, AD9832_OUTPUT_EN);
+
+static struct attribute *ad9832_attributes[] = {
+ &iio_dev_attr_out_altvoltage0_frequency0.dev_attr.attr,
+ &iio_dev_attr_out_altvoltage0_frequency1.dev_attr.attr,
+ &iio_const_attr_out_altvoltage0_frequency_scale.dev_attr.attr,
+ &iio_dev_attr_out_altvoltage0_phase0.dev_attr.attr,
+ &iio_dev_attr_out_altvoltage0_phase1.dev_attr.attr,
+ &iio_dev_attr_out_altvoltage0_phase2.dev_attr.attr,
+ &iio_dev_attr_out_altvoltage0_phase3.dev_attr.attr,
+ &iio_const_attr_out_altvoltage0_phase_scale.dev_attr.attr,
+ &iio_dev_attr_out_altvoltage0_pincontrol_en.dev_attr.attr,
+ &iio_dev_attr_out_altvoltage0_frequencysymbol.dev_attr.attr,
+ &iio_dev_attr_out_altvoltage0_phasesymbol.dev_attr.attr,
+ &iio_dev_attr_out_altvoltage0_out_enable.dev_attr.attr,
+ NULL,
+};
+
+static const struct attribute_group ad9832_attribute_group = {
+ .attrs = ad9832_attributes,
+};
+
+static const struct iio_info ad9832_info = {
+ .attrs = &ad9832_attribute_group,
+ .driver_module = THIS_MODULE,
+};
+
+static int ad9832_probe(struct spi_device *spi)
+{
+ struct ad9832_platform_data *pdata = spi->dev.platform_data;
+ struct iio_dev *indio_dev;
+ struct ad9832_state *st;
+ struct regulator *reg;
+ int ret;
+
+ if (!pdata) {
+ dev_dbg(&spi->dev, "no platform data?\n");
+ return -ENODEV;
+ }
+
+ reg = devm_regulator_get(&spi->dev, "vcc");
+ if (!IS_ERR(reg)) {
+ ret = regulator_enable(reg);
+ if (ret)
+ return ret;
+ }
+
+ indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));
+ if (!indio_dev) {
+ ret = -ENOMEM;
+ goto error_disable_reg;
+ }
+ spi_set_drvdata(spi, indio_dev);
+ st = iio_priv(indio_dev);
+ st->reg = reg;
+ st->mclk = pdata->mclk;
+ st->spi = spi;
+
+ indio_dev->dev.parent = &spi->dev;
+ indio_dev->name = spi_get_device_id(spi)->name;
+ indio_dev->info = &ad9832_info;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+
+ /* Setup default messages */
+
+ st->xfer.tx_buf = &st->data;
+ st->xfer.len = 2;
+
+ spi_message_init(&st->msg);
+ spi_message_add_tail(&st->xfer, &st->msg);
+
+ st->freq_xfer[0].tx_buf = &st->freq_data[0];
+ st->freq_xfer[0].len = 2;
+ st->freq_xfer[0].cs_change = 1;
+ st->freq_xfer[1].tx_buf = &st->freq_data[1];
+ st->freq_xfer[1].len = 2;
+ st->freq_xfer[1].cs_change = 1;
+ st->freq_xfer[2].tx_buf = &st->freq_data[2];
+ st->freq_xfer[2].len = 2;
+ st->freq_xfer[2].cs_change = 1;
+ st->freq_xfer[3].tx_buf = &st->freq_data[3];
+ st->freq_xfer[3].len = 2;
+
+ spi_message_init(&st->freq_msg);
+ spi_message_add_tail(&st->freq_xfer[0], &st->freq_msg);
+ spi_message_add_tail(&st->freq_xfer[1], &st->freq_msg);
+ spi_message_add_tail(&st->freq_xfer[2], &st->freq_msg);
+ spi_message_add_tail(&st->freq_xfer[3], &st->freq_msg);
+
+ st->phase_xfer[0].tx_buf = &st->phase_data[0];
+ st->phase_xfer[0].len = 2;
+ st->phase_xfer[0].cs_change = 1;
+ st->phase_xfer[1].tx_buf = &st->phase_data[1];
+ st->phase_xfer[1].len = 2;
+
+ spi_message_init(&st->phase_msg);
+ spi_message_add_tail(&st->phase_xfer[0], &st->phase_msg);
+ spi_message_add_tail(&st->phase_xfer[1], &st->phase_msg);
+
+ st->ctrl_src = AD9832_SLEEP | AD9832_RESET | AD9832_CLR;
+ st->data = cpu_to_be16((AD9832_CMD_SLEEPRESCLR << CMD_SHIFT) |
+ st->ctrl_src);
+ ret = spi_sync(st->spi, &st->msg);
+ if (ret) {
+ dev_err(&spi->dev, "device init failed\n");
+ goto error_disable_reg;
+ }
+
+ ret = ad9832_write_frequency(st, AD9832_FREQ0HM, pdata->freq0);
+ if (ret)
+ goto error_disable_reg;
+
+ ret = ad9832_write_frequency(st, AD9832_FREQ1HM, pdata->freq1);
+ if (ret)
+ goto error_disable_reg;
+
+ ret = ad9832_write_phase(st, AD9832_PHASE0H, pdata->phase0);
+ if (ret)
+ goto error_disable_reg;
+
+ ret = ad9832_write_phase(st, AD9832_PHASE1H, pdata->phase1);
+ if (ret)
+ goto error_disable_reg;
+
+ ret = ad9832_write_phase(st, AD9832_PHASE2H, pdata->phase2);
+ if (ret)
+ goto error_disable_reg;
+
+ ret = ad9832_write_phase(st, AD9832_PHASE3H, pdata->phase3);
+ if (ret)
+ goto error_disable_reg;
+
+ ret = iio_device_register(indio_dev);
+ if (ret)
+ goto error_disable_reg;
+
+ return 0;
+
+error_disable_reg:
+ if (!IS_ERR(reg))
+ regulator_disable(reg);
+
+ return ret;
+}
+
+static int ad9832_remove(struct spi_device *spi)
+{
+ struct iio_dev *indio_dev = spi_get_drvdata(spi);
+ struct ad9832_state *st = iio_priv(indio_dev);
+
+ iio_device_unregister(indio_dev);
+ if (!IS_ERR(st->reg))
+ regulator_disable(st->reg);
+
+ return 0;
+}
+
+static const struct spi_device_id ad9832_id[] = {
+ {"ad9832", 0},
+ {"ad9835", 0},
+ {}
+};
+MODULE_DEVICE_TABLE(spi, ad9832_id);
+
+static struct spi_driver ad9832_driver = {
+ .driver = {
+ .name = "ad9832",
+ .owner = THIS_MODULE,
+ },
+ .probe = ad9832_probe,
+ .remove = ad9832_remove,
+ .id_table = ad9832_id,
+};
+module_spi_driver(ad9832_driver);
+
+MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
+MODULE_DESCRIPTION("Analog Devices AD9832/AD9835 DDS");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/staging/iio/frequency/ad9832.h b/drivers/staging/iio/frequency/ad9832.h
new file mode 100644
index 000000000..d32323b46
--- /dev/null
+++ b/drivers/staging/iio/frequency/ad9832.h
@@ -0,0 +1,126 @@
+/*
+ * AD9832 SPI DDS driver
+ *
+ * Copyright 2011 Analog Devices Inc.
+ *
+ * Licensed under the GPL-2 or later.
+ */
+#ifndef IIO_DDS_AD9832_H_
+#define IIO_DDS_AD9832_H_
+
+/* Registers */
+
+#define AD9832_FREQ0LL 0x0
+#define AD9832_FREQ0HL 0x1
+#define AD9832_FREQ0LM 0x2
+#define AD9832_FREQ0HM 0x3
+#define AD9832_FREQ1LL 0x4
+#define AD9832_FREQ1HL 0x5
+#define AD9832_FREQ1LM 0x6
+#define AD9832_FREQ1HM 0x7
+#define AD9832_PHASE0L 0x8
+#define AD9832_PHASE0H 0x9
+#define AD9832_PHASE1L 0xA
+#define AD9832_PHASE1H 0xB
+#define AD9832_PHASE2L 0xC
+#define AD9832_PHASE2H 0xD
+#define AD9832_PHASE3L 0xE
+#define AD9832_PHASE3H 0xF
+
+#define AD9832_PHASE_SYM 0x10
+#define AD9832_FREQ_SYM 0x11
+#define AD9832_PINCTRL_EN 0x12
+#define AD9832_OUTPUT_EN 0x13
+
+/* Command Control Bits */
+
+#define AD9832_CMD_PHA8BITSW 0x1
+#define AD9832_CMD_PHA16BITSW 0x0
+#define AD9832_CMD_FRE8BITSW 0x3
+#define AD9832_CMD_FRE16BITSW 0x2
+#define AD9832_CMD_FPSELECT 0x6
+#define AD9832_CMD_SYNCSELSRC 0x8
+#define AD9832_CMD_SLEEPRESCLR 0xC
+
+#define AD9832_FREQ BIT(11)
+#define AD9832_PHASE(x) (((x) & 3) << 9)
+#define AD9832_SYNC BIT(13)
+#define AD9832_SELSRC BIT(12)
+#define AD9832_SLEEP BIT(13)
+#define AD9832_RESET BIT(12)
+#define AD9832_CLR BIT(11)
+#define CMD_SHIFT 12
+#define ADD_SHIFT 8
+#define AD9832_FREQ_BITS 32
+#define AD9832_PHASE_BITS 12
+#define RES_MASK(bits) ((1 << (bits)) - 1)
+
+/**
+ * struct ad9832_state - driver instance specific data
+ * @spi: spi_device
+ * @reg: supply regulator
+ * @mclk: external master clock
+ * @ctrl_fp: cached frequency/phase control word
+ * @ctrl_ss: cached sync/selsrc control word
+ * @ctrl_src: cached sleep/reset/clr word
+ * @xfer: default spi transfer
+ * @msg: default spi message
+ * @freq_xfer: tuning word spi transfer
+ * @freq_msg: tuning word spi message
+ * @phase_xfer: tuning word spi transfer
+ * @phase_msg: tuning word spi message
+ * @data: spi transmit buffer
+ * @phase_data: tuning word spi transmit buffer
+ * @freq_data: tuning word spi transmit buffer
+ */
+
+struct ad9832_state {
+ struct spi_device *spi;
+ struct regulator *reg;
+ unsigned long mclk;
+ unsigned short ctrl_fp;
+ unsigned short ctrl_ss;
+ unsigned short ctrl_src;
+ struct spi_transfer xfer;
+ struct spi_message msg;
+ struct spi_transfer freq_xfer[4];
+ struct spi_message freq_msg;
+ struct spi_transfer phase_xfer[2];
+ struct spi_message phase_msg;
+ /*
+ * DMA (thus cache coherency maintenance) requires the
+ * transfer buffers to live in their own cache lines.
+ */
+ union {
+ __be16 freq_data[4]____cacheline_aligned;
+ __be16 phase_data[2];
+ __be16 data;
+ };
+};
+
+/*
+ * TODO: struct ad9832_platform_data needs to go into include/linux/iio
+ */
+
+/**
+ * struct ad9832_platform_data - platform specific information
+ * @mclk: master clock in Hz
+ * @freq0: power up freq0 tuning word in Hz
+ * @freq1: power up freq1 tuning word in Hz
+ * @phase0: power up phase0 value [0..4095] correlates with 0..2PI
+ * @phase1: power up phase1 value [0..4095] correlates with 0..2PI
+ * @phase2: power up phase2 value [0..4095] correlates with 0..2PI
+ * @phase3: power up phase3 value [0..4095] correlates with 0..2PI
+ */
+
+struct ad9832_platform_data {
+ unsigned long mclk;
+ unsigned long freq0;
+ unsigned long freq1;
+ unsigned short phase0;
+ unsigned short phase1;
+ unsigned short phase2;
+ unsigned short phase3;
+};
+
+#endif /* IIO_DDS_AD9832_H_ */
diff --git a/drivers/staging/iio/frequency/ad9834.c b/drivers/staging/iio/frequency/ad9834.c
new file mode 100644
index 000000000..d02bb44fb
--- /dev/null
+++ b/drivers/staging/iio/frequency/ad9834.c
@@ -0,0 +1,459 @@
+/*
+ * AD9833/AD9834/AD9837/AD9838 SPI DDS driver
+ *
+ * Copyright 2010-2011 Analog Devices Inc.
+ *
+ * Licensed under the GPL-2.
+ */
+
+#include <linux/interrupt.h>
+#include <linux/workqueue.h>
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/sysfs.h>
+#include <linux/list.h>
+#include <linux/spi/spi.h>
+#include <linux/regulator/consumer.h>
+#include <linux/err.h>
+#include <linux/module.h>
+#include <asm/div64.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include "dds.h"
+
+#include "ad9834.h"
+
+static unsigned int ad9834_calc_freqreg(unsigned long mclk, unsigned long fout)
+{
+ unsigned long long freqreg = (u64)fout * (u64)BIT(AD9834_FREQ_BITS);
+
+ do_div(freqreg, mclk);
+ return freqreg;
+}
+
+static int ad9834_write_frequency(struct ad9834_state *st,
+ unsigned long addr, unsigned long fout)
+{
+ unsigned long regval;
+
+ if (fout > (st->mclk / 2))
+ return -EINVAL;
+
+ regval = ad9834_calc_freqreg(st->mclk, fout);
+
+ st->freq_data[0] = cpu_to_be16(addr | (regval &
+ RES_MASK(AD9834_FREQ_BITS / 2)));
+ st->freq_data[1] = cpu_to_be16(addr | ((regval >>
+ (AD9834_FREQ_BITS / 2)) &
+ RES_MASK(AD9834_FREQ_BITS / 2)));
+
+ return spi_sync(st->spi, &st->freq_msg);
+}
+
+static int ad9834_write_phase(struct ad9834_state *st,
+ unsigned long addr, unsigned long phase)
+{
+ if (phase > BIT(AD9834_PHASE_BITS))
+ return -EINVAL;
+ st->data = cpu_to_be16(addr | phase);
+
+ return spi_sync(st->spi, &st->msg);
+}
+
+static ssize_t ad9834_write(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t len)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct ad9834_state *st = iio_priv(indio_dev);
+ struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
+ int ret;
+ unsigned long val;
+
+ ret = kstrtoul(buf, 10, &val);
+ if (ret)
+ goto error_ret;
+
+ mutex_lock(&indio_dev->mlock);
+ switch ((u32)this_attr->address) {
+ case AD9834_REG_FREQ0:
+ case AD9834_REG_FREQ1:
+ ret = ad9834_write_frequency(st, this_attr->address, val);
+ break;
+ case AD9834_REG_PHASE0:
+ case AD9834_REG_PHASE1:
+ ret = ad9834_write_phase(st, this_attr->address, val);
+ break;
+ case AD9834_OPBITEN:
+ if (st->control & AD9834_MODE) {
+ ret = -EINVAL; /* AD9843 reserved mode */
+ break;
+ }
+
+ if (val)
+ st->control |= AD9834_OPBITEN;
+ else
+ st->control &= ~AD9834_OPBITEN;
+
+ st->data = cpu_to_be16(AD9834_REG_CMD | st->control);
+ ret = spi_sync(st->spi, &st->msg);
+ break;
+ case AD9834_PIN_SW:
+ if (val)
+ st->control |= AD9834_PIN_SW;
+ else
+ st->control &= ~AD9834_PIN_SW;
+ st->data = cpu_to_be16(AD9834_REG_CMD | st->control);
+ ret = spi_sync(st->spi, &st->msg);
+ break;
+ case AD9834_FSEL:
+ case AD9834_PSEL:
+ if (val == 0) {
+ st->control &= ~(this_attr->address | AD9834_PIN_SW);
+ } else if (val == 1) {
+ st->control |= this_attr->address;
+ st->control &= ~AD9834_PIN_SW;
+ } else {
+ ret = -EINVAL;
+ break;
+ }
+ st->data = cpu_to_be16(AD9834_REG_CMD | st->control);
+ ret = spi_sync(st->spi, &st->msg);
+ break;
+ case AD9834_RESET:
+ if (val)
+ st->control &= ~AD9834_RESET;
+ else
+ st->control |= AD9834_RESET;
+
+ st->data = cpu_to_be16(AD9834_REG_CMD | st->control);
+ ret = spi_sync(st->spi, &st->msg);
+ break;
+ default:
+ ret = -ENODEV;
+ }
+ mutex_unlock(&indio_dev->mlock);
+
+error_ret:
+ return ret ? ret : len;
+}
+
+static ssize_t ad9834_store_wavetype(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t len)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct ad9834_state *st = iio_priv(indio_dev);
+ struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
+ int ret = 0;
+ bool is_ad9833_7 = (st->devid == ID_AD9833) || (st->devid == ID_AD9837);
+
+ mutex_lock(&indio_dev->mlock);
+
+ switch ((u32)this_attr->address) {
+ case 0:
+ if (sysfs_streq(buf, "sine")) {
+ st->control &= ~AD9834_MODE;
+ if (is_ad9833_7)
+ st->control &= ~AD9834_OPBITEN;
+ } else if (sysfs_streq(buf, "triangle")) {
+ if (is_ad9833_7) {
+ st->control &= ~AD9834_OPBITEN;
+ st->control |= AD9834_MODE;
+ } else if (st->control & AD9834_OPBITEN) {
+ ret = -EINVAL; /* AD9843 reserved mode */
+ } else {
+ st->control |= AD9834_MODE;
+ }
+ } else if (is_ad9833_7 && sysfs_streq(buf, "square")) {
+ st->control &= ~AD9834_MODE;
+ st->control |= AD9834_OPBITEN;
+ } else {
+ ret = -EINVAL;
+ }
+
+ break;
+ case 1:
+ if (sysfs_streq(buf, "square") &&
+ !(st->control & AD9834_MODE)) {
+ st->control &= ~AD9834_MODE;
+ st->control |= AD9834_OPBITEN;
+ } else {
+ ret = -EINVAL;
+ }
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ if (!ret) {
+ st->data = cpu_to_be16(AD9834_REG_CMD | st->control);
+ ret = spi_sync(st->spi, &st->msg);
+ }
+ mutex_unlock(&indio_dev->mlock);
+
+ return ret ? ret : len;
+}
+
+static
+ssize_t ad9834_show_out0_wavetype_available(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct ad9834_state *st = iio_priv(indio_dev);
+ char *str;
+
+ if ((st->devid == ID_AD9833) || (st->devid == ID_AD9837))
+ str = "sine triangle square";
+ else if (st->control & AD9834_OPBITEN)
+ str = "sine";
+ else
+ str = "sine triangle";
+
+ return sprintf(buf, "%s\n", str);
+}
+
+static IIO_DEVICE_ATTR(out_altvoltage0_out0_wavetype_available, S_IRUGO,
+ ad9834_show_out0_wavetype_available, NULL, 0);
+
+static
+ssize_t ad9834_show_out1_wavetype_available(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct ad9834_state *st = iio_priv(indio_dev);
+ char *str;
+
+ if (st->control & AD9834_MODE)
+ str = "";
+ else
+ str = "square";
+
+ return sprintf(buf, "%s\n", str);
+}
+
+static IIO_DEVICE_ATTR(out_altvoltage0_out1_wavetype_available, S_IRUGO,
+ ad9834_show_out1_wavetype_available, NULL, 0);
+
+/**
+ * see dds.h for further information
+ */
+
+static IIO_DEV_ATTR_FREQ(0, 0, S_IWUSR, NULL, ad9834_write, AD9834_REG_FREQ0);
+static IIO_DEV_ATTR_FREQ(0, 1, S_IWUSR, NULL, ad9834_write, AD9834_REG_FREQ1);
+static IIO_DEV_ATTR_FREQSYMBOL(0, S_IWUSR, NULL, ad9834_write, AD9834_FSEL);
+static IIO_CONST_ATTR_FREQ_SCALE(0, "1"); /* 1Hz */
+
+static IIO_DEV_ATTR_PHASE(0, 0, S_IWUSR, NULL, ad9834_write, AD9834_REG_PHASE0);
+static IIO_DEV_ATTR_PHASE(0, 1, S_IWUSR, NULL, ad9834_write, AD9834_REG_PHASE1);
+static IIO_DEV_ATTR_PHASESYMBOL(0, S_IWUSR, NULL, ad9834_write, AD9834_PSEL);
+static IIO_CONST_ATTR_PHASE_SCALE(0, "0.0015339808"); /* 2PI/2^12 rad*/
+
+static IIO_DEV_ATTR_PINCONTROL_EN(0, S_IWUSR, NULL,
+ ad9834_write, AD9834_PIN_SW);
+static IIO_DEV_ATTR_OUT_ENABLE(0, S_IWUSR, NULL, ad9834_write, AD9834_RESET);
+static IIO_DEV_ATTR_OUTY_ENABLE(0, 1, S_IWUSR, NULL,
+ ad9834_write, AD9834_OPBITEN);
+static IIO_DEV_ATTR_OUT_WAVETYPE(0, 0, ad9834_store_wavetype, 0);
+static IIO_DEV_ATTR_OUT_WAVETYPE(0, 1, ad9834_store_wavetype, 1);
+
+static struct attribute *ad9834_attributes[] = {
+ &iio_dev_attr_out_altvoltage0_frequency0.dev_attr.attr,
+ &iio_dev_attr_out_altvoltage0_frequency1.dev_attr.attr,
+ &iio_const_attr_out_altvoltage0_frequency_scale.dev_attr.attr,
+ &iio_dev_attr_out_altvoltage0_phase0.dev_attr.attr,
+ &iio_dev_attr_out_altvoltage0_phase1.dev_attr.attr,
+ &iio_const_attr_out_altvoltage0_phase_scale.dev_attr.attr,
+ &iio_dev_attr_out_altvoltage0_pincontrol_en.dev_attr.attr,
+ &iio_dev_attr_out_altvoltage0_frequencysymbol.dev_attr.attr,
+ &iio_dev_attr_out_altvoltage0_phasesymbol.dev_attr.attr,
+ &iio_dev_attr_out_altvoltage0_out_enable.dev_attr.attr,
+ &iio_dev_attr_out_altvoltage0_out1_enable.dev_attr.attr,
+ &iio_dev_attr_out_altvoltage0_out0_wavetype.dev_attr.attr,
+ &iio_dev_attr_out_altvoltage0_out1_wavetype.dev_attr.attr,
+ &iio_dev_attr_out_altvoltage0_out0_wavetype_available.dev_attr.attr,
+ &iio_dev_attr_out_altvoltage0_out1_wavetype_available.dev_attr.attr,
+ NULL,
+};
+
+static struct attribute *ad9833_attributes[] = {
+ &iio_dev_attr_out_altvoltage0_frequency0.dev_attr.attr,
+ &iio_dev_attr_out_altvoltage0_frequency1.dev_attr.attr,
+ &iio_const_attr_out_altvoltage0_frequency_scale.dev_attr.attr,
+ &iio_dev_attr_out_altvoltage0_phase0.dev_attr.attr,
+ &iio_dev_attr_out_altvoltage0_phase1.dev_attr.attr,
+ &iio_const_attr_out_altvoltage0_phase_scale.dev_attr.attr,
+ &iio_dev_attr_out_altvoltage0_frequencysymbol.dev_attr.attr,
+ &iio_dev_attr_out_altvoltage0_phasesymbol.dev_attr.attr,
+ &iio_dev_attr_out_altvoltage0_out_enable.dev_attr.attr,
+ &iio_dev_attr_out_altvoltage0_out0_wavetype.dev_attr.attr,
+ &iio_dev_attr_out_altvoltage0_out0_wavetype_available.dev_attr.attr,
+ NULL,
+};
+
+static const struct attribute_group ad9834_attribute_group = {
+ .attrs = ad9834_attributes,
+};
+
+static const struct attribute_group ad9833_attribute_group = {
+ .attrs = ad9833_attributes,
+};
+
+static const struct iio_info ad9834_info = {
+ .attrs = &ad9834_attribute_group,
+ .driver_module = THIS_MODULE,
+};
+
+static const struct iio_info ad9833_info = {
+ .attrs = &ad9833_attribute_group,
+ .driver_module = THIS_MODULE,
+};
+
+static int ad9834_probe(struct spi_device *spi)
+{
+ struct ad9834_platform_data *pdata = spi->dev.platform_data;
+ struct ad9834_state *st;
+ struct iio_dev *indio_dev;
+ struct regulator *reg;
+ int ret;
+
+ if (!pdata) {
+ dev_dbg(&spi->dev, "no platform data?\n");
+ return -ENODEV;
+ }
+
+ reg = devm_regulator_get(&spi->dev, "vcc");
+ if (!IS_ERR(reg)) {
+ ret = regulator_enable(reg);
+ if (ret)
+ return ret;
+ }
+
+ indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));
+ if (!indio_dev) {
+ ret = -ENOMEM;
+ goto error_disable_reg;
+ }
+ spi_set_drvdata(spi, indio_dev);
+ st = iio_priv(indio_dev);
+ st->mclk = pdata->mclk;
+ st->spi = spi;
+ st->devid = spi_get_device_id(spi)->driver_data;
+ st->reg = reg;
+ indio_dev->dev.parent = &spi->dev;
+ indio_dev->name = spi_get_device_id(spi)->name;
+ switch (st->devid) {
+ case ID_AD9833:
+ case ID_AD9837:
+ indio_dev->info = &ad9833_info;
+ break;
+ default:
+ indio_dev->info = &ad9834_info;
+ break;
+ }
+ indio_dev->modes = INDIO_DIRECT_MODE;
+
+ /* Setup default messages */
+
+ st->xfer.tx_buf = &st->data;
+ st->xfer.len = 2;
+
+ spi_message_init(&st->msg);
+ spi_message_add_tail(&st->xfer, &st->msg);
+
+ st->freq_xfer[0].tx_buf = &st->freq_data[0];
+ st->freq_xfer[0].len = 2;
+ st->freq_xfer[0].cs_change = 1;
+ st->freq_xfer[1].tx_buf = &st->freq_data[1];
+ st->freq_xfer[1].len = 2;
+
+ spi_message_init(&st->freq_msg);
+ spi_message_add_tail(&st->freq_xfer[0], &st->freq_msg);
+ spi_message_add_tail(&st->freq_xfer[1], &st->freq_msg);
+
+ st->control = AD9834_B28 | AD9834_RESET;
+
+ if (!pdata->en_div2)
+ st->control |= AD9834_DIV2;
+
+ if (!pdata->en_signbit_msb_out && (st->devid == ID_AD9834))
+ st->control |= AD9834_SIGN_PIB;
+
+ st->data = cpu_to_be16(AD9834_REG_CMD | st->control);
+ ret = spi_sync(st->spi, &st->msg);
+ if (ret) {
+ dev_err(&spi->dev, "device init failed\n");
+ goto error_disable_reg;
+ }
+
+ ret = ad9834_write_frequency(st, AD9834_REG_FREQ0, pdata->freq0);
+ if (ret)
+ goto error_disable_reg;
+
+ ret = ad9834_write_frequency(st, AD9834_REG_FREQ1, pdata->freq1);
+ if (ret)
+ goto error_disable_reg;
+
+ ret = ad9834_write_phase(st, AD9834_REG_PHASE0, pdata->phase0);
+ if (ret)
+ goto error_disable_reg;
+
+ ret = ad9834_write_phase(st, AD9834_REG_PHASE1, pdata->phase1);
+ if (ret)
+ goto error_disable_reg;
+
+ ret = iio_device_register(indio_dev);
+ if (ret)
+ goto error_disable_reg;
+
+ return 0;
+
+error_disable_reg:
+ if (!IS_ERR(reg))
+ regulator_disable(reg);
+
+ return ret;
+}
+
+static int ad9834_remove(struct spi_device *spi)
+{
+ struct iio_dev *indio_dev = spi_get_drvdata(spi);
+ struct ad9834_state *st = iio_priv(indio_dev);
+
+ iio_device_unregister(indio_dev);
+ if (!IS_ERR(st->reg))
+ regulator_disable(st->reg);
+
+ return 0;
+}
+
+static const struct spi_device_id ad9834_id[] = {
+ {"ad9833", ID_AD9833},
+ {"ad9834", ID_AD9834},
+ {"ad9837", ID_AD9837},
+ {"ad9838", ID_AD9838},
+ {}
+};
+MODULE_DEVICE_TABLE(spi, ad9834_id);
+
+static struct spi_driver ad9834_driver = {
+ .driver = {
+ .name = "ad9834",
+ .owner = THIS_MODULE,
+ },
+ .probe = ad9834_probe,
+ .remove = ad9834_remove,
+ .id_table = ad9834_id,
+};
+module_spi_driver(ad9834_driver);
+
+MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
+MODULE_DESCRIPTION("Analog Devices AD9833/AD9834/AD9837/AD9838 DDS");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/staging/iio/frequency/ad9834.h b/drivers/staging/iio/frequency/ad9834.h
new file mode 100644
index 000000000..40fdd5da7
--- /dev/null
+++ b/drivers/staging/iio/frequency/ad9834.h
@@ -0,0 +1,111 @@
+/*
+ * AD9833/AD9834/AD9837/AD9838 SPI DDS driver
+ *
+ * Copyright 2010-2011 Analog Devices Inc.
+ *
+ * Licensed under the GPL-2.
+ */
+#ifndef IIO_DDS_AD9834_H_
+#define IIO_DDS_AD9834_H_
+
+/* Registers */
+
+#define AD9834_REG_CMD 0
+#define AD9834_REG_FREQ0 BIT(14)
+#define AD9834_REG_FREQ1 BIT(15)
+#define AD9834_REG_PHASE0 (BIT(15) | BIT(14))
+#define AD9834_REG_PHASE1 (BIT(15) | BIT(14) | BIT(13))
+
+/* Command Control Bits */
+
+#define AD9834_B28 BIT(13)
+#define AD9834_HLB BIT(12)
+#define AD9834_FSEL BIT(11)
+#define AD9834_PSEL BIT(10)
+#define AD9834_PIN_SW BIT(9)
+#define AD9834_RESET BIT(8)
+#define AD9834_SLEEP1 BIT(7)
+#define AD9834_SLEEP12 BIT(6)
+#define AD9834_OPBITEN BIT(5)
+#define AD9834_SIGN_PIB BIT(4)
+#define AD9834_DIV2 BIT(3)
+#define AD9834_MODE BIT(1)
+
+#define AD9834_FREQ_BITS 28
+#define AD9834_PHASE_BITS 12
+
+#define RES_MASK(bits) (BIT(bits) - 1)
+
+/**
+ * struct ad9834_state - driver instance specific data
+ * @spi: spi_device
+ * @reg: supply regulator
+ * @mclk: external master clock
+ * @control: cached control word
+ * @xfer: default spi transfer
+ * @msg: default spi message
+ * @freq_xfer: tuning word spi transfer
+ * @freq_msg: tuning word spi message
+ * @data: spi transmit buffer
+ * @freq_data: tuning word spi transmit buffer
+ */
+
+struct ad9834_state {
+ struct spi_device *spi;
+ struct regulator *reg;
+ unsigned int mclk;
+ unsigned short control;
+ unsigned short devid;
+ struct spi_transfer xfer;
+ struct spi_message msg;
+ struct spi_transfer freq_xfer[2];
+ struct spi_message freq_msg;
+
+ /*
+ * DMA (thus cache coherency maintenance) requires the
+ * transfer buffers to live in their own cache lines.
+ */
+ __be16 data ____cacheline_aligned;
+ __be16 freq_data[2];
+};
+
+/*
+ * TODO: struct ad7887_platform_data needs to go into include/linux/iio
+ */
+
+/**
+ * struct ad9834_platform_data - platform specific information
+ * @mclk: master clock in Hz
+ * @freq0: power up freq0 tuning word in Hz
+ * @freq1: power up freq1 tuning word in Hz
+ * @phase0: power up phase0 value [0..4095] correlates with 0..2PI
+ * @phase1: power up phase1 value [0..4095] correlates with 0..2PI
+ * @en_div2: digital output/2 is passed to the SIGN BIT OUT pin
+ * @en_signbit_msb_out: the MSB (or MSB/2) of the DAC data is connected to the
+ * SIGN BIT OUT pin. en_div2 controls whether it is the MSB
+ * or MSB/2 that is output. if en_signbit_msb_out=false,
+ * the on-board comparator is connected to SIGN BIT OUT
+ */
+
+struct ad9834_platform_data {
+ unsigned int mclk;
+ unsigned int freq0;
+ unsigned int freq1;
+ unsigned short phase0;
+ unsigned short phase1;
+ bool en_div2;
+ bool en_signbit_msb_out;
+};
+
+/**
+ * ad9834_supported_device_ids:
+ */
+
+enum ad9834_supported_device_ids {
+ ID_AD9833,
+ ID_AD9834,
+ ID_AD9837,
+ ID_AD9838,
+};
+
+#endif /* IIO_DDS_AD9834_H_ */
diff --git a/drivers/staging/iio/frequency/dds.h b/drivers/staging/iio/frequency/dds.h
new file mode 100644
index 000000000..fe53e7324
--- /dev/null
+++ b/drivers/staging/iio/frequency/dds.h
@@ -0,0 +1,114 @@
+/*
+ * dds.h - sysfs attributes associated with DDS devices
+ *
+ * Copyright (c) 2010 Analog Devices Inc.
+ *
+ * Licensed under the GPL-2 or later.
+ */
+#ifndef IIO_DDS_H_
+#define IIO_DDS_H_
+
+/**
+ * /sys/bus/iio/devices/.../out_altvoltageX_frequencyY
+ */
+
+#define IIO_DEV_ATTR_FREQ(_channel, _num, _mode, _show, _store, _addr) \
+ IIO_DEVICE_ATTR(out_altvoltage##_channel##_frequency##_num, \
+ _mode, _show, _store, _addr)
+
+/**
+ * /sys/bus/iio/devices/.../out_altvoltageX_frequencyY_scale
+ */
+
+#define IIO_CONST_ATTR_FREQ_SCALE(_channel, _string) \
+ IIO_CONST_ATTR(out_altvoltage##_channel##_frequency_scale, _string)
+
+/**
+ * /sys/bus/iio/devices/.../out_altvoltageX_frequencysymbol
+ */
+
+#define IIO_DEV_ATTR_FREQSYMBOL(_channel, _mode, _show, _store, _addr) \
+ IIO_DEVICE_ATTR(out_altvoltage##_channel##_frequencysymbol, \
+ _mode, _show, _store, _addr)
+
+/**
+ * /sys/bus/iio/devices/.../out_altvoltageX_phaseY
+ */
+
+#define IIO_DEV_ATTR_PHASE(_channel, _num, _mode, _show, _store, _addr) \
+ IIO_DEVICE_ATTR(out_altvoltage##_channel##_phase##_num, \
+ _mode, _show, _store, _addr)
+
+/**
+ * /sys/bus/iio/devices/.../out_altvoltageX_phaseY_scale
+ */
+
+#define IIO_CONST_ATTR_PHASE_SCALE(_channel, _string) \
+ IIO_CONST_ATTR(out_altvoltage##_channel##_phase_scale, _string)
+
+/**
+ * /sys/bus/iio/devices/.../out_altvoltageX_phasesymbol
+ */
+
+#define IIO_DEV_ATTR_PHASESYMBOL(_channel, _mode, _show, _store, _addr) \
+ IIO_DEVICE_ATTR(out_altvoltage##_channel##_phasesymbol, \
+ _mode, _show, _store, _addr)
+
+/**
+ * /sys/bus/iio/devices/.../out_altvoltageX_pincontrol_en
+ */
+
+#define IIO_DEV_ATTR_PINCONTROL_EN(_channel, _mode, _show, _store, _addr)\
+ IIO_DEVICE_ATTR(out_altvoltage##_channel##_pincontrol_en, \
+ _mode, _show, _store, _addr)
+
+/**
+ * /sys/bus/iio/devices/.../out_altvoltageX_pincontrol_frequency_en
+ */
+
+#define IIO_DEV_ATTR_PINCONTROL_FREQ_EN(_channel, _mode, _show, _store, _addr)\
+ IIO_DEVICE_ATTR(out_altvoltage##_channel##_pincontrol_frequency_en,\
+ _mode, _show, _store, _addr)
+
+/**
+ * /sys/bus/iio/devices/.../out_altvoltageX_pincontrol_phase_en
+ */
+
+#define IIO_DEV_ATTR_PINCONTROL_PHASE_EN(_channel, _mode, _show, _store, _addr)\
+ IIO_DEVICE_ATTR(out_altvoltage##_channel##_pincontrol_phase_en, \
+ _mode, _show, _store, _addr)
+
+/**
+ * /sys/bus/iio/devices/.../out_altvoltageX_out_enable
+ */
+
+#define IIO_DEV_ATTR_OUT_ENABLE(_channel, _mode, _show, _store, _addr) \
+ IIO_DEVICE_ATTR(out_altvoltage##_channel##_out_enable, \
+ _mode, _show, _store, _addr)
+
+/**
+ * /sys/bus/iio/devices/.../out_altvoltageX_outY_enable
+ */
+
+#define IIO_DEV_ATTR_OUTY_ENABLE(_channel, _output, \
+ _mode, _show, _store, _addr) \
+ IIO_DEVICE_ATTR(out_altvoltage##_channel##_out##_output##_enable,\
+ _mode, _show, _store, _addr)
+
+/**
+ * /sys/bus/iio/devices/.../out_altvoltageX_outY_wavetype
+ */
+
+#define IIO_DEV_ATTR_OUT_WAVETYPE(_channel, _output, _store, _addr) \
+ IIO_DEVICE_ATTR(out_altvoltage##_channel##_out##_output##_wavetype,\
+ S_IWUSR, NULL, _store, _addr)
+
+/**
+ * /sys/bus/iio/devices/.../out_altvoltageX_outY_wavetype_available
+ */
+
+#define IIO_CONST_ATTR_OUT_WAVETYPES_AVAILABLE(_channel, _output, _modes)\
+ IIO_CONST_ATTR( \
+ out_altvoltage##_channel##_out##_output##_wavetype_available, _modes)
+
+#endif /* IIO_DDS_H_ */
diff --git a/drivers/staging/iio/gyro/Kconfig b/drivers/staging/iio/gyro/Kconfig
new file mode 100644
index 000000000..f62f68fd6
--- /dev/null
+++ b/drivers/staging/iio/gyro/Kconfig
@@ -0,0 +1,16 @@
+#
+# IIO Digital Gyroscope Sensor drivers configuration
+#
+menu "Digital gyroscope sensors"
+
+config ADIS16060
+ tristate "Analog Devices ADIS16060 Yaw Rate Gyroscope with SPI driver"
+ depends on SPI
+ help
+ Say Y (yes) here to build support for Analog Devices adis16060 wide bandwidth
+ yaw rate gyroscope with SPI.
+
+ To compile this driver as a module, say M here: the module will be
+ called adis16060. If unsure, say N.
+
+endmenu
diff --git a/drivers/staging/iio/gyro/Makefile b/drivers/staging/iio/gyro/Makefile
new file mode 100644
index 000000000..cf22d6d55
--- /dev/null
+++ b/drivers/staging/iio/gyro/Makefile
@@ -0,0 +1,6 @@
+#
+# Makefile for digital gyroscope sensor drivers
+#
+
+adis16060-y := adis16060_core.o
+obj-$(CONFIG_ADIS16060) += adis16060.o
diff --git a/drivers/staging/iio/gyro/adis16060_core.c b/drivers/staging/iio/gyro/adis16060_core.c
new file mode 100644
index 000000000..4c5869dd8
--- /dev/null
+++ b/drivers/staging/iio/gyro/adis16060_core.c
@@ -0,0 +1,246 @@
+/*
+ * ADIS16060 Wide Bandwidth Yaw Rate Gyroscope with SPI driver
+ *
+ * Copyright 2010 Analog Devices Inc.
+ *
+ * Licensed under the GPL-2 or later.
+ */
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/mutex.h>
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/spi/spi.h>
+#include <linux/slab.h>
+#include <linux/sysfs.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+
+#define ADIS16060_GYRO 0x20 /* Measure Angular Rate (Gyro) */
+#define ADIS16060_TEMP_OUT 0x10 /* Measure Temperature */
+#define ADIS16060_AIN2 0x80 /* Measure AIN2 */
+#define ADIS16060_AIN1 0x40 /* Measure AIN1 */
+
+/**
+ * struct adis16060_state - device instance specific data
+ * @us_w: actual spi_device to write config
+ * @us_r: actual spi_device to read back data
+ * @buf: transmit or receive buffer
+ * @buf_lock: mutex to protect tx and rx
+ **/
+struct adis16060_state {
+ struct spi_device *us_w;
+ struct spi_device *us_r;
+ struct mutex buf_lock;
+
+ u8 buf[3] ____cacheline_aligned;
+};
+
+static struct iio_dev *adis16060_iio_dev;
+
+static int adis16060_spi_write(struct iio_dev *indio_dev, u8 val)
+{
+ int ret;
+ struct adis16060_state *st = iio_priv(indio_dev);
+
+ mutex_lock(&st->buf_lock);
+ st->buf[2] = val; /* The last 8 bits clocked in are latched */
+ ret = spi_write(st->us_w, st->buf, 3);
+ mutex_unlock(&st->buf_lock);
+
+ return ret;
+}
+
+static int adis16060_spi_read(struct iio_dev *indio_dev, u16 *val)
+{
+ int ret;
+ struct adis16060_state *st = iio_priv(indio_dev);
+
+ mutex_lock(&st->buf_lock);
+
+ ret = spi_read(st->us_r, st->buf, 3);
+
+ /* The internal successive approximation ADC begins the
+ * conversion process on the falling edge of MSEL1 and
+ * starts to place data MSB first on the DOUT line at
+ * the 6th falling edge of SCLK
+ */
+ if (ret == 0)
+ *val = ((st->buf[0] & 0x3) << 12) |
+ (st->buf[1] << 4) |
+ ((st->buf[2] >> 4) & 0xF);
+ mutex_unlock(&st->buf_lock);
+
+ return ret;
+}
+
+static int adis16060_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2,
+ long mask)
+{
+ u16 tval = 0;
+ int ret;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ /* Take the iio_dev status lock */
+ mutex_lock(&indio_dev->mlock);
+ ret = adis16060_spi_write(indio_dev, chan->address);
+ if (ret < 0) {
+ mutex_unlock(&indio_dev->mlock);
+ return ret;
+ }
+ ret = adis16060_spi_read(indio_dev, &tval);
+ mutex_unlock(&indio_dev->mlock);
+ *val = tval;
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_OFFSET:
+ *val = -7;
+ *val2 = 461117;
+ return IIO_VAL_INT_PLUS_MICRO;
+ case IIO_CHAN_INFO_SCALE:
+ *val = 0;
+ *val2 = 34000;
+ return IIO_VAL_INT_PLUS_MICRO;
+ }
+
+ return -EINVAL;
+}
+
+static const struct iio_info adis16060_info = {
+ .read_raw = &adis16060_read_raw,
+ .driver_module = THIS_MODULE,
+};
+
+static const struct iio_chan_spec adis16060_channels[] = {
+ {
+ .type = IIO_ANGL_VEL,
+ .modified = 1,
+ .channel2 = IIO_MOD_Z,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
+ .address = ADIS16060_GYRO,
+ }, {
+ .type = IIO_VOLTAGE,
+ .indexed = 1,
+ .channel = 0,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
+ .address = ADIS16060_AIN1,
+ }, {
+ .type = IIO_VOLTAGE,
+ .indexed = 1,
+ .channel = 1,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
+ .address = ADIS16060_AIN2,
+ }, {
+ .type = IIO_TEMP,
+ .indexed = 1,
+ .channel = 0,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+ BIT(IIO_CHAN_INFO_OFFSET) | BIT(IIO_CHAN_INFO_SCALE),
+ .address = ADIS16060_TEMP_OUT,
+ }
+};
+
+static int adis16060_r_probe(struct spi_device *spi)
+{
+ int ret;
+ struct adis16060_state *st;
+ struct iio_dev *indio_dev;
+
+ /* setup the industrialio driver allocated elements */
+ indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));
+ if (!indio_dev)
+ return -ENOMEM;
+ /* this is only used for removal purposes */
+ spi_set_drvdata(spi, indio_dev);
+ st = iio_priv(indio_dev);
+ st->us_r = spi;
+ mutex_init(&st->buf_lock);
+
+ indio_dev->name = spi->dev.driver->name;
+ indio_dev->dev.parent = &spi->dev;
+ indio_dev->info = &adis16060_info;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->channels = adis16060_channels;
+ indio_dev->num_channels = ARRAY_SIZE(adis16060_channels);
+
+ ret = devm_iio_device_register(&spi->dev, indio_dev);
+ if (ret)
+ return ret;
+
+ adis16060_iio_dev = indio_dev;
+ return 0;
+}
+
+static int adis16060_w_probe(struct spi_device *spi)
+{
+ int ret;
+ struct iio_dev *indio_dev = adis16060_iio_dev;
+ struct adis16060_state *st;
+
+ if (!indio_dev) {
+ ret = -ENODEV;
+ goto error_ret;
+ }
+ st = iio_priv(indio_dev);
+ spi_set_drvdata(spi, indio_dev);
+ st->us_w = spi;
+ return 0;
+
+error_ret:
+ return ret;
+}
+
+static int adis16060_w_remove(struct spi_device *spi)
+{
+ return 0;
+}
+
+static struct spi_driver adis16060_r_driver = {
+ .driver = {
+ .name = "adis16060_r",
+ .owner = THIS_MODULE,
+ },
+ .probe = adis16060_r_probe,
+};
+
+static struct spi_driver adis16060_w_driver = {
+ .driver = {
+ .name = "adis16060_w",
+ .owner = THIS_MODULE,
+ },
+ .probe = adis16060_w_probe,
+ .remove = adis16060_w_remove,
+};
+
+static __init int adis16060_init(void)
+{
+ int ret;
+
+ ret = spi_register_driver(&adis16060_r_driver);
+ if (ret < 0)
+ return ret;
+
+ ret = spi_register_driver(&adis16060_w_driver);
+ if (ret < 0) {
+ spi_unregister_driver(&adis16060_r_driver);
+ return ret;
+ }
+
+ return 0;
+}
+module_init(adis16060_init);
+
+static __exit void adis16060_exit(void)
+{
+ spi_unregister_driver(&adis16060_w_driver);
+ spi_unregister_driver(&adis16060_r_driver);
+}
+module_exit(adis16060_exit);
+
+MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>");
+MODULE_DESCRIPTION("Analog Devices ADIS16060 Yaw Rate Gyroscope Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/staging/iio/iio_dummy_evgen.c b/drivers/staging/iio/iio_dummy_evgen.c
new file mode 100644
index 000000000..0c9c86d7b
--- /dev/null
+++ b/drivers/staging/iio/iio_dummy_evgen.c
@@ -0,0 +1,239 @@
+/**
+ * Copyright (c) 2011 Jonathan Cameron
+ *
+ * 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.
+ *
+ * Companion module to the iio simple dummy example driver.
+ * The purpose of this is to generate 'fake' event interrupts thus
+ * allowing that driver's code to be as close as possible to that of
+ * a normal driver talking to hardware. The approach used here
+ * is not intended to be general and just happens to work for this
+ * particular use case.
+ */
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/mutex.h>
+#include <linux/module.h>
+#include <linux/sysfs.h>
+
+#include "iio_dummy_evgen.h"
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+
+/* Fiddly bit of faking and irq without hardware */
+#define IIO_EVENTGEN_NO 10
+/**
+ * struct iio_dummy_evgen - evgen state
+ * @chip: irq chip we are faking
+ * @base: base of irq range
+ * @enabled: mask of which irqs are enabled
+ * @inuse: mask of which irqs are connected
+ * @regs: irq regs we are faking
+ * @lock: protect the evgen state
+ */
+struct iio_dummy_eventgen {
+ struct irq_chip chip;
+ int base;
+ bool enabled[IIO_EVENTGEN_NO];
+ bool inuse[IIO_EVENTGEN_NO];
+ struct iio_dummy_regs regs[IIO_EVENTGEN_NO];
+ struct mutex lock;
+};
+
+/* We can only ever have one instance of this 'device' */
+static struct iio_dummy_eventgen *iio_evgen;
+static const char *iio_evgen_name = "iio_dummy_evgen";
+
+static void iio_dummy_event_irqmask(struct irq_data *d)
+{
+ struct irq_chip *chip = irq_data_get_irq_chip(d);
+ struct iio_dummy_eventgen *evgen =
+ container_of(chip, struct iio_dummy_eventgen, chip);
+
+ evgen->enabled[d->irq - evgen->base] = false;
+}
+
+static void iio_dummy_event_irqunmask(struct irq_data *d)
+{
+ struct irq_chip *chip = irq_data_get_irq_chip(d);
+ struct iio_dummy_eventgen *evgen =
+ container_of(chip, struct iio_dummy_eventgen, chip);
+
+ evgen->enabled[d->irq - evgen->base] = true;
+}
+
+static int iio_dummy_evgen_create(void)
+{
+ int ret, i;
+
+ iio_evgen = kzalloc(sizeof(*iio_evgen), GFP_KERNEL);
+ if (!iio_evgen)
+ return -ENOMEM;
+
+ iio_evgen->base = irq_alloc_descs(-1, 0, IIO_EVENTGEN_NO, 0);
+ if (iio_evgen->base < 0) {
+ ret = iio_evgen->base;
+ kfree(iio_evgen);
+ return ret;
+ }
+ iio_evgen->chip.name = iio_evgen_name;
+ iio_evgen->chip.irq_mask = &iio_dummy_event_irqmask;
+ iio_evgen->chip.irq_unmask = &iio_dummy_event_irqunmask;
+ for (i = 0; i < IIO_EVENTGEN_NO; i++) {
+ irq_set_chip(iio_evgen->base + i, &iio_evgen->chip);
+ irq_set_handler(iio_evgen->base + i, &handle_simple_irq);
+ irq_modify_status(iio_evgen->base + i,
+ IRQ_NOREQUEST | IRQ_NOAUTOEN,
+ IRQ_NOPROBE);
+ }
+ mutex_init(&iio_evgen->lock);
+ return 0;
+}
+
+/**
+ * iio_dummy_evgen_get_irq() - get an evgen provided irq for a device
+ *
+ * This function will give a free allocated irq to a client device.
+ * That irq can then be caused to 'fire' by using the associated sysfs file.
+ */
+int iio_dummy_evgen_get_irq(void)
+{
+ int i, ret = 0;
+
+ if (!iio_evgen)
+ return -ENODEV;
+
+ mutex_lock(&iio_evgen->lock);
+ for (i = 0; i < IIO_EVENTGEN_NO; i++)
+ if (!iio_evgen->inuse[i]) {
+ ret = iio_evgen->base + i;
+ iio_evgen->inuse[i] = true;
+ break;
+ }
+ mutex_unlock(&iio_evgen->lock);
+ if (i == IIO_EVENTGEN_NO)
+ return -ENOMEM;
+ return ret;
+}
+EXPORT_SYMBOL_GPL(iio_dummy_evgen_get_irq);
+
+/**
+ * iio_dummy_evgen_release_irq() - give the irq back.
+ * @irq: irq being returned to the pool
+ *
+ * Used by client driver instances to give the irqs back when they disconnect
+ */
+int iio_dummy_evgen_release_irq(int irq)
+{
+ mutex_lock(&iio_evgen->lock);
+ iio_evgen->inuse[irq - iio_evgen->base] = false;
+ mutex_unlock(&iio_evgen->lock);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(iio_dummy_evgen_release_irq);
+
+struct iio_dummy_regs *iio_dummy_evgen_get_regs(int irq)
+{
+ return &iio_evgen->regs[irq - iio_evgen->base];
+}
+EXPORT_SYMBOL_GPL(iio_dummy_evgen_get_regs);
+
+static void iio_dummy_evgen_free(void)
+{
+ irq_free_descs(iio_evgen->base, IIO_EVENTGEN_NO);
+ kfree(iio_evgen);
+}
+
+static void iio_evgen_release(struct device *dev)
+{
+ iio_dummy_evgen_free();
+}
+
+static ssize_t iio_evgen_poke(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t len)
+{
+ struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
+ unsigned long event;
+ int ret;
+
+ ret = kstrtoul(buf, 10, &event);
+ if (ret)
+ return ret;
+
+ iio_evgen->regs[this_attr->address].reg_id = this_attr->address;
+ iio_evgen->regs[this_attr->address].reg_data = event;
+
+ if (iio_evgen->enabled[this_attr->address])
+ handle_nested_irq(iio_evgen->base + this_attr->address);
+
+ return len;
+}
+
+static IIO_DEVICE_ATTR(poke_ev0, S_IWUSR, NULL, &iio_evgen_poke, 0);
+static IIO_DEVICE_ATTR(poke_ev1, S_IWUSR, NULL, &iio_evgen_poke, 1);
+static IIO_DEVICE_ATTR(poke_ev2, S_IWUSR, NULL, &iio_evgen_poke, 2);
+static IIO_DEVICE_ATTR(poke_ev3, S_IWUSR, NULL, &iio_evgen_poke, 3);
+static IIO_DEVICE_ATTR(poke_ev4, S_IWUSR, NULL, &iio_evgen_poke, 4);
+static IIO_DEVICE_ATTR(poke_ev5, S_IWUSR, NULL, &iio_evgen_poke, 5);
+static IIO_DEVICE_ATTR(poke_ev6, S_IWUSR, NULL, &iio_evgen_poke, 6);
+static IIO_DEVICE_ATTR(poke_ev7, S_IWUSR, NULL, &iio_evgen_poke, 7);
+static IIO_DEVICE_ATTR(poke_ev8, S_IWUSR, NULL, &iio_evgen_poke, 8);
+static IIO_DEVICE_ATTR(poke_ev9, S_IWUSR, NULL, &iio_evgen_poke, 9);
+
+static struct attribute *iio_evgen_attrs[] = {
+ &iio_dev_attr_poke_ev0.dev_attr.attr,
+ &iio_dev_attr_poke_ev1.dev_attr.attr,
+ &iio_dev_attr_poke_ev2.dev_attr.attr,
+ &iio_dev_attr_poke_ev3.dev_attr.attr,
+ &iio_dev_attr_poke_ev4.dev_attr.attr,
+ &iio_dev_attr_poke_ev5.dev_attr.attr,
+ &iio_dev_attr_poke_ev6.dev_attr.attr,
+ &iio_dev_attr_poke_ev7.dev_attr.attr,
+ &iio_dev_attr_poke_ev8.dev_attr.attr,
+ &iio_dev_attr_poke_ev9.dev_attr.attr,
+ NULL,
+};
+
+static const struct attribute_group iio_evgen_group = {
+ .attrs = iio_evgen_attrs,
+};
+
+static const struct attribute_group *iio_evgen_groups[] = {
+ &iio_evgen_group,
+ NULL
+};
+
+static struct device iio_evgen_dev = {
+ .bus = &iio_bus_type,
+ .groups = iio_evgen_groups,
+ .release = &iio_evgen_release,
+};
+static __init int iio_dummy_evgen_init(void)
+{
+ int ret = iio_dummy_evgen_create();
+
+ if (ret < 0)
+ return ret;
+ device_initialize(&iio_evgen_dev);
+ dev_set_name(&iio_evgen_dev, "iio_evgen");
+ return device_add(&iio_evgen_dev);
+}
+module_init(iio_dummy_evgen_init);
+
+static __exit void iio_dummy_evgen_exit(void)
+{
+ device_unregister(&iio_evgen_dev);
+}
+module_exit(iio_dummy_evgen_exit);
+
+MODULE_AUTHOR("Jonathan Cameron <jic23@kernel.org>");
+MODULE_DESCRIPTION("IIO dummy driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/staging/iio/iio_dummy_evgen.h b/drivers/staging/iio/iio_dummy_evgen.h
new file mode 100644
index 000000000..2ac293ab7
--- /dev/null
+++ b/drivers/staging/iio/iio_dummy_evgen.h
@@ -0,0 +1,13 @@
+#ifndef _IIO_DUMMY_EVGEN_H_
+#define _IIO_DUMMY_EVGEN_H_
+
+struct iio_dummy_regs {
+ u32 reg_id;
+ u32 reg_data;
+};
+
+struct iio_dummy_regs *iio_dummy_evgen_get_regs(int irq);
+int iio_dummy_evgen_get_irq(void);
+int iio_dummy_evgen_release_irq(int irq);
+
+#endif /* _IIO_DUMMY_EVGEN_H_ */
diff --git a/drivers/staging/iio/iio_simple_dummy.c b/drivers/staging/iio/iio_simple_dummy.c
new file mode 100644
index 000000000..b47bf9fb6
--- /dev/null
+++ b/drivers/staging/iio/iio_simple_dummy.c
@@ -0,0 +1,749 @@
+/**
+ * Copyright (c) 2011 Jonathan Cameron
+ *
+ * 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.
+ *
+ * A reference industrial I/O driver to illustrate the functionality available.
+ *
+ * There are numerous real drivers to illustrate the finer points.
+ * The purpose of this driver is to provide a driver with far more comments
+ * and explanatory notes than any 'real' driver would have.
+ * Anyone starting out writing an IIO driver should first make sure they
+ * understand all of this driver except those bits specifically marked
+ * as being present to allow us to 'fake' the presence of hardware.
+ */
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/iio/events.h>
+#include <linux/iio/buffer.h>
+#include "iio_simple_dummy.h"
+
+/*
+ * A few elements needed to fake a bus for this driver
+ * Note instances parameter controls how many of these
+ * dummy devices are registered.
+ */
+static unsigned instances = 1;
+module_param(instances, int, 0);
+
+/* Pointer array used to fake bus elements */
+static struct iio_dev **iio_dummy_devs;
+
+/* Fake a name for the part number, usually obtained from the id table */
+static const char *iio_dummy_part_number = "iio_dummy_part_no";
+
+/**
+ * struct iio_dummy_accel_calibscale - realworld to register mapping
+ * @val: first value in read_raw - here integer part.
+ * @val2: second value in read_raw etc - here micro part.
+ * @regval: register value - magic device specific numbers.
+ */
+struct iio_dummy_accel_calibscale {
+ int val;
+ int val2;
+ int regval; /* what would be written to hardware */
+};
+
+static const struct iio_dummy_accel_calibscale dummy_scales[] = {
+ { 0, 100, 0x8 }, /* 0.000100 */
+ { 0, 133, 0x7 }, /* 0.000133 */
+ { 733, 13, 0x9 }, /* 733.000013 */
+};
+
+#ifdef CONFIG_IIO_SIMPLE_DUMMY_EVENTS
+
+/*
+ * simple event - triggered when value rises above
+ * a threshold
+ */
+static const struct iio_event_spec iio_dummy_event = {
+ .type = IIO_EV_TYPE_THRESH,
+ .dir = IIO_EV_DIR_RISING,
+ .mask_separate = BIT(IIO_EV_INFO_VALUE) | BIT(IIO_EV_INFO_ENABLE),
+};
+
+/*
+ * simple step detect event - triggered when a step is detected
+ */
+static const struct iio_event_spec step_detect_event = {
+ .type = IIO_EV_TYPE_CHANGE,
+ .dir = IIO_EV_DIR_NONE,
+ .mask_separate = BIT(IIO_EV_INFO_ENABLE),
+};
+
+/*
+ * simple transition event - triggered when the reported running confidence
+ * value rises above a threshold value
+ */
+static const struct iio_event_spec iio_running_event = {
+ .type = IIO_EV_TYPE_THRESH,
+ .dir = IIO_EV_DIR_RISING,
+ .mask_separate = BIT(IIO_EV_INFO_VALUE) | BIT(IIO_EV_INFO_ENABLE),
+};
+
+/*
+ * simple transition event - triggered when the reported walking confidence
+ * value falls under a threshold value
+ */
+static const struct iio_event_spec iio_walking_event = {
+ .type = IIO_EV_TYPE_THRESH,
+ .dir = IIO_EV_DIR_FALLING,
+ .mask_separate = BIT(IIO_EV_INFO_VALUE) | BIT(IIO_EV_INFO_ENABLE),
+};
+#endif
+
+/*
+ * iio_dummy_channels - Description of available channels
+ *
+ * This array of structures tells the IIO core about what the device
+ * actually provides for a given channel.
+ */
+static const struct iio_chan_spec iio_dummy_channels[] = {
+ /* indexed ADC channel in_voltage0_raw etc */
+ {
+ .type = IIO_VOLTAGE,
+ /* Channel has a numeric index of 0 */
+ .indexed = 1,
+ .channel = 0,
+ /* What other information is available? */
+ .info_mask_separate =
+ /*
+ * in_voltage0_raw
+ * Raw (unscaled no bias removal etc) measurement
+ * from the device.
+ */
+ BIT(IIO_CHAN_INFO_RAW) |
+ /*
+ * in_voltage0_offset
+ * Offset for userspace to apply prior to scale
+ * when converting to standard units (microvolts)
+ */
+ BIT(IIO_CHAN_INFO_OFFSET) |
+ /*
+ * in_voltage0_scale
+ * Multipler for userspace to apply post offset
+ * when converting to standard units (microvolts)
+ */
+ BIT(IIO_CHAN_INFO_SCALE),
+ /*
+ * sampling_frequency
+ * The frequency in Hz at which the channels are sampled
+ */
+ .info_mask_shared_by_dir = BIT(IIO_CHAN_INFO_SAMP_FREQ),
+ /* The ordering of elements in the buffer via an enum */
+ .scan_index = voltage0,
+ .scan_type = { /* Description of storage in buffer */
+ .sign = 'u', /* unsigned */
+ .realbits = 13, /* 13 bits */
+ .storagebits = 16, /* 16 bits used for storage */
+ .shift = 0, /* zero shift */
+ },
+#ifdef CONFIG_IIO_SIMPLE_DUMMY_EVENTS
+ .event_spec = &iio_dummy_event,
+ .num_event_specs = 1,
+#endif /* CONFIG_IIO_SIMPLE_DUMMY_EVENTS */
+ },
+ /* Differential ADC channel in_voltage1-voltage2_raw etc*/
+ {
+ .type = IIO_VOLTAGE,
+ .differential = 1,
+ /*
+ * Indexing for differential channels uses channel
+ * for the positive part, channel2 for the negative.
+ */
+ .indexed = 1,
+ .channel = 1,
+ .channel2 = 2,
+ /*
+ * in_voltage1-voltage2_raw
+ * Raw (unscaled no bias removal etc) measurement
+ * from the device.
+ */
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
+ /*
+ * in_voltage-voltage_scale
+ * Shared version of scale - shared by differential
+ * input channels of type IIO_VOLTAGE.
+ */
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),
+ /*
+ * sampling_frequency
+ * The frequency in Hz at which the channels are sampled
+ */
+ .scan_index = diffvoltage1m2,
+ .scan_type = { /* Description of storage in buffer */
+ .sign = 's', /* signed */
+ .realbits = 12, /* 12 bits */
+ .storagebits = 16, /* 16 bits used for storage */
+ .shift = 0, /* zero shift */
+ },
+ },
+ /* Differential ADC channel in_voltage3-voltage4_raw etc*/
+ {
+ .type = IIO_VOLTAGE,
+ .differential = 1,
+ .indexed = 1,
+ .channel = 3,
+ .channel2 = 4,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),
+ .info_mask_shared_by_dir = BIT(IIO_CHAN_INFO_SAMP_FREQ),
+ .scan_index = diffvoltage3m4,
+ .scan_type = {
+ .sign = 's',
+ .realbits = 11,
+ .storagebits = 16,
+ .shift = 0,
+ },
+ },
+ /*
+ * 'modified' (i.e. axis specified) acceleration channel
+ * in_accel_z_raw
+ */
+ {
+ .type = IIO_ACCEL,
+ .modified = 1,
+ /* Channel 2 is use for modifiers */
+ .channel2 = IIO_MOD_X,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+ /*
+ * Internal bias and gain correction values. Applied
+ * by the hardware or driver prior to userspace
+ * seeing the readings. Typically part of hardware
+ * calibration.
+ */
+ BIT(IIO_CHAN_INFO_CALIBSCALE) |
+ BIT(IIO_CHAN_INFO_CALIBBIAS),
+ .info_mask_shared_by_dir = BIT(IIO_CHAN_INFO_SAMP_FREQ),
+ .scan_index = accelx,
+ .scan_type = { /* Description of storage in buffer */
+ .sign = 's', /* signed */
+ .realbits = 16, /* 16 bits */
+ .storagebits = 16, /* 16 bits used for storage */
+ .shift = 0, /* zero shift */
+ },
+ },
+ /*
+ * Convenience macro for timestamps. 4 is the index in
+ * the buffer.
+ */
+ IIO_CHAN_SOFT_TIMESTAMP(4),
+ /* DAC channel out_voltage0_raw */
+ {
+ .type = IIO_VOLTAGE,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
+ .scan_index = -1, /* No buffer support */
+ .output = 1,
+ .indexed = 1,
+ .channel = 0,
+ },
+ {
+ .type = IIO_STEPS,
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_ENABLE) |
+ BIT(IIO_CHAN_INFO_CALIBHEIGHT),
+ .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED),
+ .scan_index = -1, /* No buffer support */
+#ifdef CONFIG_IIO_SIMPLE_DUMMY_EVENTS
+ .event_spec = &step_detect_event,
+ .num_event_specs = 1,
+#endif /* CONFIG_IIO_SIMPLE_DUMMY_EVENTS */
+ },
+ {
+ .type = IIO_ACTIVITY,
+ .modified = 1,
+ .channel2 = IIO_MOD_RUNNING,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED),
+ .scan_index = -1, /* No buffer support */
+#ifdef CONFIG_IIO_SIMPLE_DUMMY_EVENTS
+ .event_spec = &iio_running_event,
+ .num_event_specs = 1,
+#endif /* CONFIG_IIO_SIMPLE_DUMMY_EVENTS */
+ },
+ {
+ .type = IIO_ACTIVITY,
+ .modified = 1,
+ .channel2 = IIO_MOD_WALKING,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED),
+ .scan_index = -1, /* No buffer support */
+#ifdef CONFIG_IIO_SIMPLE_DUMMY_EVENTS
+ .event_spec = &iio_walking_event,
+ .num_event_specs = 1,
+#endif /* CONFIG_IIO_SIMPLE_DUMMY_EVENTS */
+ },
+};
+
+/**
+ * iio_dummy_read_raw() - data read function.
+ * @indio_dev: the struct iio_dev associated with this device instance
+ * @chan: the channel whose data is to be read
+ * @val: first element of returned value (typically INT)
+ * @val2: second element of returned value (typically MICRO)
+ * @mask: what we actually want to read as per the info_mask_*
+ * in iio_chan_spec.
+ */
+static int iio_dummy_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val,
+ int *val2,
+ long mask)
+{
+ struct iio_dummy_state *st = iio_priv(indio_dev);
+ int ret = -EINVAL;
+
+ mutex_lock(&st->lock);
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW: /* magic value - channel value read */
+ switch (chan->type) {
+ case IIO_VOLTAGE:
+ if (chan->output) {
+ /* Set integer part to cached value */
+ *val = st->dac_val;
+ ret = IIO_VAL_INT;
+ } else if (chan->differential) {
+ if (chan->channel == 1)
+ *val = st->differential_adc_val[0];
+ else
+ *val = st->differential_adc_val[1];
+ ret = IIO_VAL_INT;
+ } else {
+ *val = st->single_ended_adc_val;
+ ret = IIO_VAL_INT;
+ }
+ break;
+ case IIO_ACCEL:
+ *val = st->accel_val;
+ ret = IIO_VAL_INT;
+ break;
+ default:
+ break;
+ }
+ break;
+ case IIO_CHAN_INFO_PROCESSED:
+ switch (chan->type) {
+ case IIO_STEPS:
+ *val = st->steps;
+ ret = IIO_VAL_INT;
+ break;
+ case IIO_ACTIVITY:
+ switch (chan->channel2) {
+ case IIO_MOD_RUNNING:
+ *val = st->activity_running;
+ ret = IIO_VAL_INT;
+ break;
+ case IIO_MOD_WALKING:
+ *val = st->activity_walking;
+ ret = IIO_VAL_INT;
+ break;
+ default:
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+ break;
+ case IIO_CHAN_INFO_OFFSET:
+ /* only single ended adc -> 7 */
+ *val = 7;
+ ret = IIO_VAL_INT;
+ break;
+ case IIO_CHAN_INFO_SCALE:
+ switch (chan->type) {
+ case IIO_VOLTAGE:
+ switch (chan->differential) {
+ case 0:
+ /* only single ended adc -> 0.001333 */
+ *val = 0;
+ *val2 = 1333;
+ ret = IIO_VAL_INT_PLUS_MICRO;
+ break;
+ case 1:
+ /* all differential adc channels ->
+ * 0.000001344 */
+ *val = 0;
+ *val2 = 1344;
+ ret = IIO_VAL_INT_PLUS_NANO;
+ }
+ break;
+ default:
+ break;
+ }
+ break;
+ case IIO_CHAN_INFO_CALIBBIAS:
+ /* only the acceleration axis - read from cache */
+ *val = st->accel_calibbias;
+ ret = IIO_VAL_INT;
+ break;
+ case IIO_CHAN_INFO_CALIBSCALE:
+ *val = st->accel_calibscale->val;
+ *val2 = st->accel_calibscale->val2;
+ ret = IIO_VAL_INT_PLUS_MICRO;
+ break;
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ *val = 3;
+ *val2 = 33;
+ ret = IIO_VAL_INT_PLUS_NANO;
+ break;
+ case IIO_CHAN_INFO_ENABLE:
+ switch (chan->type) {
+ case IIO_STEPS:
+ *val = st->steps_enabled;
+ ret = IIO_VAL_INT;
+ break;
+ default:
+ break;
+ }
+ break;
+ case IIO_CHAN_INFO_CALIBHEIGHT:
+ switch (chan->type) {
+ case IIO_STEPS:
+ *val = st->height;
+ ret = IIO_VAL_INT;
+ break;
+ default:
+ break;
+ }
+ break;
+
+ default:
+ break;
+ }
+ mutex_unlock(&st->lock);
+ return ret;
+}
+
+/**
+ * iio_dummy_write_raw() - data write function.
+ * @indio_dev: the struct iio_dev associated with this device instance
+ * @chan: the channel whose data is to be written
+ * @val: first element of value to set (typically INT)
+ * @val2: second element of value to set (typically MICRO)
+ * @mask: what we actually want to write as per the info_mask_*
+ * in iio_chan_spec.
+ *
+ * Note that all raw writes are assumed IIO_VAL_INT and info mask elements
+ * are assumed to be IIO_INT_PLUS_MICRO unless the callback write_raw_get_fmt
+ * in struct iio_info is provided by the driver.
+ */
+static int iio_dummy_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int val,
+ int val2,
+ long mask)
+{
+ int i;
+ int ret = 0;
+ struct iio_dummy_state *st = iio_priv(indio_dev);
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ switch (chan->type) {
+ case IIO_VOLTAGE:
+ if (chan->output == 0)
+ return -EINVAL;
+
+ /* Locking not required as writing single value */
+ mutex_lock(&st->lock);
+ st->dac_val = val;
+ mutex_unlock(&st->lock);
+ return 0;
+ default:
+ return -EINVAL;
+ }
+ case IIO_CHAN_INFO_PROCESSED:
+ switch (chan->type) {
+ case IIO_STEPS:
+ mutex_lock(&st->lock);
+ st->steps = val;
+ mutex_unlock(&st->lock);
+ return 0;
+ case IIO_ACTIVITY:
+ if (val < 0)
+ val = 0;
+ if (val > 100)
+ val = 100;
+ switch (chan->channel2) {
+ case IIO_MOD_RUNNING:
+ st->activity_running = val;
+ return 0;
+ case IIO_MOD_WALKING:
+ st->activity_walking = val;
+ return 0;
+ default:
+ return -EINVAL;
+ }
+ break;
+ default:
+ return -EINVAL;
+ }
+ case IIO_CHAN_INFO_CALIBSCALE:
+ mutex_lock(&st->lock);
+ /* Compare against table - hard matching here */
+ for (i = 0; i < ARRAY_SIZE(dummy_scales); i++)
+ if (val == dummy_scales[i].val &&
+ val2 == dummy_scales[i].val2)
+ break;
+ if (i == ARRAY_SIZE(dummy_scales))
+ ret = -EINVAL;
+ else
+ st->accel_calibscale = &dummy_scales[i];
+ mutex_unlock(&st->lock);
+ return ret;
+ case IIO_CHAN_INFO_CALIBBIAS:
+ mutex_lock(&st->lock);
+ st->accel_calibbias = val;
+ mutex_unlock(&st->lock);
+ return 0;
+ case IIO_CHAN_INFO_ENABLE:
+ switch (chan->type) {
+ case IIO_STEPS:
+ mutex_lock(&st->lock);
+ st->steps_enabled = val;
+ mutex_unlock(&st->lock);
+ return 0;
+ default:
+ return -EINVAL;
+ }
+ case IIO_CHAN_INFO_CALIBHEIGHT:
+ switch (chan->type) {
+ case IIO_STEPS:
+ st->height = val;
+ return 0;
+ default:
+ return -EINVAL;
+ }
+
+ default:
+ return -EINVAL;
+ }
+}
+
+/*
+ * Device type specific information.
+ */
+static const struct iio_info iio_dummy_info = {
+ .driver_module = THIS_MODULE,
+ .read_raw = &iio_dummy_read_raw,
+ .write_raw = &iio_dummy_write_raw,
+#ifdef CONFIG_IIO_SIMPLE_DUMMY_EVENTS
+ .read_event_config = &iio_simple_dummy_read_event_config,
+ .write_event_config = &iio_simple_dummy_write_event_config,
+ .read_event_value = &iio_simple_dummy_read_event_value,
+ .write_event_value = &iio_simple_dummy_write_event_value,
+#endif /* CONFIG_IIO_SIMPLE_DUMMY_EVENTS */
+};
+
+/**
+ * iio_dummy_init_device() - device instance specific init
+ * @indio_dev: the iio device structure
+ *
+ * Most drivers have one of these to set up default values,
+ * reset the device to known state etc.
+ */
+static int iio_dummy_init_device(struct iio_dev *indio_dev)
+{
+ struct iio_dummy_state *st = iio_priv(indio_dev);
+
+ st->dac_val = 0;
+ st->single_ended_adc_val = 73;
+ st->differential_adc_val[0] = 33;
+ st->differential_adc_val[1] = -34;
+ st->accel_val = 34;
+ st->accel_calibbias = -7;
+ st->accel_calibscale = &dummy_scales[0];
+ st->steps = 47;
+ st->activity_running = 98;
+ st->activity_walking = 4;
+
+ return 0;
+}
+
+/**
+ * iio_dummy_probe() - device instance probe
+ * @index: an id number for this instance.
+ *
+ * Arguments are bus type specific.
+ * I2C: iio_dummy_probe(struct i2c_client *client,
+ * const struct i2c_device_id *id)
+ * SPI: iio_dummy_probe(struct spi_device *spi)
+ */
+static int iio_dummy_probe(int index)
+{
+ int ret;
+ struct iio_dev *indio_dev;
+ struct iio_dummy_state *st;
+
+ /*
+ * Allocate an IIO device.
+ *
+ * This structure contains all generic state
+ * information about the device instance.
+ * It also has a region (accessed by iio_priv()
+ * for chip specific state information.
+ */
+ indio_dev = iio_device_alloc(sizeof(*st));
+ if (!indio_dev) {
+ ret = -ENOMEM;
+ goto error_ret;
+ }
+
+ st = iio_priv(indio_dev);
+ mutex_init(&st->lock);
+
+ iio_dummy_init_device(indio_dev);
+ /*
+ * With hardware: Set the parent device.
+ * indio_dev->dev.parent = &spi->dev;
+ * indio_dev->dev.parent = &client->dev;
+ */
+
+ /*
+ * Make the iio_dev struct available to remove function.
+ * Bus equivalents
+ * i2c_set_clientdata(client, indio_dev);
+ * spi_set_drvdata(spi, indio_dev);
+ */
+ iio_dummy_devs[index] = indio_dev;
+
+
+ /*
+ * Set the device name.
+ *
+ * This is typically a part number and obtained from the module
+ * id table.
+ * e.g. for i2c and spi:
+ * indio_dev->name = id->name;
+ * indio_dev->name = spi_get_device_id(spi)->name;
+ */
+ indio_dev->name = iio_dummy_part_number;
+
+ /* Provide description of available channels */
+ indio_dev->channels = iio_dummy_channels;
+ indio_dev->num_channels = ARRAY_SIZE(iio_dummy_channels);
+
+ /*
+ * Provide device type specific interface functions and
+ * constant data.
+ */
+ indio_dev->info = &iio_dummy_info;
+
+ /* Specify that device provides sysfs type interfaces */
+ indio_dev->modes = INDIO_DIRECT_MODE;
+
+ ret = iio_simple_dummy_events_register(indio_dev);
+ if (ret < 0)
+ goto error_free_device;
+
+ ret = iio_simple_dummy_configure_buffer(indio_dev);
+ if (ret < 0)
+ goto error_unregister_events;
+
+ ret = iio_device_register(indio_dev);
+ if (ret < 0)
+ goto error_unconfigure_buffer;
+
+ return 0;
+error_unconfigure_buffer:
+ iio_simple_dummy_unconfigure_buffer(indio_dev);
+error_unregister_events:
+ iio_simple_dummy_events_unregister(indio_dev);
+error_free_device:
+ iio_device_free(indio_dev);
+error_ret:
+ return ret;
+}
+
+/**
+ * iio_dummy_remove() - device instance removal function
+ * @index: device index.
+ *
+ * Parameters follow those of iio_dummy_probe for buses.
+ */
+static int iio_dummy_remove(int index)
+{
+ int ret;
+ /*
+ * Get a pointer to the device instance iio_dev structure
+ * from the bus subsystem. E.g.
+ * struct iio_dev *indio_dev = i2c_get_clientdata(client);
+ * struct iio_dev *indio_dev = spi_get_drvdata(spi);
+ */
+ struct iio_dev *indio_dev = iio_dummy_devs[index];
+
+
+ /* Unregister the device */
+ iio_device_unregister(indio_dev);
+
+ /* Device specific code to power down etc */
+
+ /* Buffered capture related cleanup */
+ iio_simple_dummy_unconfigure_buffer(indio_dev);
+
+ ret = iio_simple_dummy_events_unregister(indio_dev);
+ if (ret)
+ goto error_ret;
+
+ /* Free all structures */
+ iio_device_free(indio_dev);
+
+error_ret:
+ return ret;
+}
+
+/**
+ * iio_dummy_init() - device driver registration
+ *
+ * Varies depending on bus type of the device. As there is no device
+ * here, call probe directly. For information on device registration
+ * i2c:
+ * Documentation/i2c/writing-clients
+ * spi:
+ * Documentation/spi/spi-summary
+ */
+static __init int iio_dummy_init(void)
+{
+ int i, ret;
+
+ if (instances > 10) {
+ instances = 1;
+ return -EINVAL;
+ }
+
+ /* Fake a bus */
+ iio_dummy_devs = kcalloc(instances, sizeof(*iio_dummy_devs),
+ GFP_KERNEL);
+ /* Here we have no actual device so call probe */
+ for (i = 0; i < instances; i++) {
+ ret = iio_dummy_probe(i);
+ if (ret < 0)
+ return ret;
+ }
+ return 0;
+}
+module_init(iio_dummy_init);
+
+/**
+ * iio_dummy_exit() - device driver removal
+ *
+ * Varies depending on bus type of the device.
+ * As there is no device here, call remove directly.
+ */
+static __exit void iio_dummy_exit(void)
+{
+ int i;
+
+ for (i = 0; i < instances; i++)
+ iio_dummy_remove(i);
+ kfree(iio_dummy_devs);
+}
+module_exit(iio_dummy_exit);
+
+MODULE_AUTHOR("Jonathan Cameron <jic23@kernel.org>");
+MODULE_DESCRIPTION("IIO dummy driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/staging/iio/iio_simple_dummy.h b/drivers/staging/iio/iio_simple_dummy.h
new file mode 100644
index 000000000..34989bf24
--- /dev/null
+++ b/drivers/staging/iio/iio_simple_dummy.h
@@ -0,0 +1,129 @@
+/**
+ * Copyright (c) 2011 Jonathan Cameron
+ *
+ * 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.
+ *
+ * Join together the various functionality of iio_simple_dummy driver
+ */
+
+#ifndef _IIO_SIMPLE_DUMMY_H_
+#define _IIO_SIMPLE_DUMMY_H_
+#include <linux/kernel.h>
+
+struct iio_dummy_accel_calibscale;
+struct iio_dummy_regs;
+
+/**
+ * struct iio_dummy_state - device instance specific state.
+ * @dac_val: cache for dac value
+ * @single_ended_adc_val: cache for single ended adc value
+ * @differential_adc_val: cache for differential adc value
+ * @accel_val: cache for acceleration value
+ * @accel_calibbias: cache for acceleration calibbias
+ * @accel_calibscale: cache for acceleration calibscale
+ * @lock: lock to ensure state is consistent
+ * @event_irq: irq number for event line (faked)
+ * @event_val: cache for event theshold value
+ * @event_en: cache of whether event is enabled
+ */
+struct iio_dummy_state {
+ int dac_val;
+ int single_ended_adc_val;
+ int differential_adc_val[2];
+ int accel_val;
+ int accel_calibbias;
+ int activity_running;
+ int activity_walking;
+ const struct iio_dummy_accel_calibscale *accel_calibscale;
+ struct mutex lock;
+ struct iio_dummy_regs *regs;
+ int steps_enabled;
+ int steps;
+ int height;
+#ifdef CONFIG_IIO_SIMPLE_DUMMY_EVENTS
+ int event_irq;
+ int event_val;
+ bool event_en;
+#endif /* CONFIG_IIO_SIMPLE_DUMMY_EVENTS */
+};
+
+#ifdef CONFIG_IIO_SIMPLE_DUMMY_EVENTS
+
+struct iio_dev;
+
+int iio_simple_dummy_read_event_config(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan,
+ enum iio_event_type type,
+ enum iio_event_direction dir);
+
+int iio_simple_dummy_write_event_config(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan,
+ enum iio_event_type type,
+ enum iio_event_direction dir,
+ int state);
+
+int iio_simple_dummy_read_event_value(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan,
+ enum iio_event_type type,
+ enum iio_event_direction dir,
+ enum iio_event_info info, int *val,
+ int *val2);
+
+int iio_simple_dummy_write_event_value(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan,
+ enum iio_event_type type,
+ enum iio_event_direction dir,
+ enum iio_event_info info, int val,
+ int val2);
+
+int iio_simple_dummy_events_register(struct iio_dev *indio_dev);
+int iio_simple_dummy_events_unregister(struct iio_dev *indio_dev);
+
+#else /* Stubs for when events are disabled at compile time */
+
+static inline int
+iio_simple_dummy_events_register(struct iio_dev *indio_dev)
+{
+ return 0;
+};
+
+static inline int
+iio_simple_dummy_events_unregister(struct iio_dev *indio_dev)
+{
+ return 0;
+};
+
+#endif /* CONFIG_IIO_SIMPLE_DUMMY_EVENTS*/
+
+/**
+ * enum iio_simple_dummy_scan_elements - scan index enum
+ * @voltage0: the single ended voltage channel
+ * @diffvoltage1m2: first differential channel
+ * @diffvoltage3m4: second differenial channel
+ * @accelx: acceleration channel
+ *
+ * Enum provides convenient numbering for the scan index.
+ */
+enum iio_simple_dummy_scan_elements {
+ voltage0,
+ diffvoltage1m2,
+ diffvoltage3m4,
+ accelx,
+};
+
+#ifdef CONFIG_IIO_SIMPLE_DUMMY_BUFFER
+int iio_simple_dummy_configure_buffer(struct iio_dev *indio_dev);
+void iio_simple_dummy_unconfigure_buffer(struct iio_dev *indio_dev);
+#else
+static inline int iio_simple_dummy_configure_buffer(struct iio_dev *indio_dev)
+{
+ return 0;
+};
+static inline
+void iio_simple_dummy_unconfigure_buffer(struct iio_dev *indio_dev)
+{};
+
+#endif /* CONFIG_IIO_SIMPLE_DUMMY_BUFFER */
+#endif /* _IIO_SIMPLE_DUMMY_H_ */
diff --git a/drivers/staging/iio/iio_simple_dummy_buffer.c b/drivers/staging/iio/iio_simple_dummy_buffer.c
new file mode 100644
index 000000000..a651b8922
--- /dev/null
+++ b/drivers/staging/iio/iio_simple_dummy_buffer.c
@@ -0,0 +1,192 @@
+/**
+ * Copyright (c) 2011 Jonathan Cameron
+ *
+ * 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.
+ *
+ * Buffer handling elements of industrial I/O reference driver.
+ * Uses the kfifo buffer.
+ *
+ * To test without hardware use the sysfs trigger.
+ */
+
+#include <linux/kernel.h>
+#include <linux/export.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/bitmap.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/kfifo_buf.h>
+
+#include "iio_simple_dummy.h"
+
+/* Some fake data */
+
+static const s16 fakedata[] = {
+ [voltage0] = 7,
+ [diffvoltage1m2] = -33,
+ [diffvoltage3m4] = -2,
+ [accelx] = 344,
+};
+/**
+ * iio_simple_dummy_trigger_h() - the trigger handler function
+ * @irq: the interrupt number
+ * @p: private data - always a pointer to the poll func.
+ *
+ * This is the guts of buffered capture. On a trigger event occurring,
+ * if the pollfunc is attached then this handler is called as a threaded
+ * interrupt (and hence may sleep). It is responsible for grabbing data
+ * from the device and pushing it into the associated buffer.
+ */
+static irqreturn_t iio_simple_dummy_trigger_h(int irq, void *p)
+{
+ struct iio_poll_func *pf = p;
+ struct iio_dev *indio_dev = pf->indio_dev;
+ int len = 0;
+ u16 *data;
+
+ data = kmalloc(indio_dev->scan_bytes, GFP_KERNEL);
+ if (!data)
+ goto done;
+
+ if (!bitmap_empty(indio_dev->active_scan_mask, indio_dev->masklength)) {
+ /*
+ * Three common options here:
+ * hardware scans: certain combinations of channels make
+ * up a fast read. The capture will consist of all of them.
+ * Hence we just call the grab data function and fill the
+ * buffer without processing.
+ * software scans: can be considered to be random access
+ * so efficient reading is just a case of minimal bus
+ * transactions.
+ * software culled hardware scans:
+ * occasionally a driver may process the nearest hardware
+ * scan to avoid storing elements that are not desired. This
+ * is the fiddliest option by far.
+ * Here let's pretend we have random access. And the values are
+ * in the constant table fakedata.
+ */
+ int i, j;
+
+ for (i = 0, j = 0;
+ i < bitmap_weight(indio_dev->active_scan_mask,
+ indio_dev->masklength);
+ i++, j++) {
+ j = find_next_bit(indio_dev->active_scan_mask,
+ indio_dev->masklength, j);
+ /* random access read from the 'device' */
+ data[i] = fakedata[j];
+ len += 2;
+ }
+ }
+
+ iio_push_to_buffers_with_timestamp(indio_dev, data, iio_get_time_ns());
+
+ kfree(data);
+
+done:
+ /*
+ * Tell the core we are done with this trigger and ready for the
+ * next one.
+ */
+ iio_trigger_notify_done(indio_dev->trig);
+
+ return IRQ_HANDLED;
+}
+
+static const struct iio_buffer_setup_ops iio_simple_dummy_buffer_setup_ops = {
+ /*
+ * iio_triggered_buffer_postenable:
+ * Generic function that simply attaches the pollfunc to the trigger.
+ * Replace this to mess with hardware state before we attach the
+ * trigger.
+ */
+ .postenable = &iio_triggered_buffer_postenable,
+ /*
+ * iio_triggered_buffer_predisable:
+ * Generic function that simple detaches the pollfunc from the trigger.
+ * Replace this to put hardware state back again after the trigger is
+ * detached but before userspace knows we have disabled the ring.
+ */
+ .predisable = &iio_triggered_buffer_predisable,
+};
+
+int iio_simple_dummy_configure_buffer(struct iio_dev *indio_dev)
+{
+ int ret;
+ struct iio_buffer *buffer;
+
+ /* Allocate a buffer to use - here a kfifo */
+ buffer = iio_kfifo_allocate();
+ if (!buffer) {
+ ret = -ENOMEM;
+ goto error_ret;
+ }
+
+ iio_device_attach_buffer(indio_dev, buffer);
+
+ /* Enable timestamps by default */
+ buffer->scan_timestamp = true;
+
+ /*
+ * Tell the core what device type specific functions should
+ * be run on either side of buffer capture enable / disable.
+ */
+ indio_dev->setup_ops = &iio_simple_dummy_buffer_setup_ops;
+
+ /*
+ * Configure a polling function.
+ * When a trigger event with this polling function connected
+ * occurs, this function is run. Typically this grabs data
+ * from the device.
+ *
+ * NULL for the bottom half. This is normally implemented only if we
+ * either want to ping a capture now pin (no sleeping) or grab
+ * a timestamp as close as possible to a data ready trigger firing.
+ *
+ * IRQF_ONESHOT ensures irqs are masked such that only one instance
+ * of the handler can run at a time.
+ *
+ * "iio_simple_dummy_consumer%d" formatting string for the irq 'name'
+ * as seen under /proc/interrupts. Remaining parameters as per printk.
+ */
+ indio_dev->pollfunc = iio_alloc_pollfunc(NULL,
+ &iio_simple_dummy_trigger_h,
+ IRQF_ONESHOT,
+ indio_dev,
+ "iio_simple_dummy_consumer%d",
+ indio_dev->id);
+
+ if (!indio_dev->pollfunc) {
+ ret = -ENOMEM;
+ goto error_free_buffer;
+ }
+
+ /*
+ * Notify the core that this device is capable of buffered capture
+ * driven by a trigger.
+ */
+ indio_dev->modes |= INDIO_BUFFER_TRIGGERED;
+
+ return 0;
+
+error_free_buffer:
+ iio_kfifo_free(indio_dev->buffer);
+error_ret:
+ return ret;
+
+}
+
+/**
+ * iio_simple_dummy_unconfigure_buffer() - release buffer resources
+ * @indo_dev: device instance state
+ */
+void iio_simple_dummy_unconfigure_buffer(struct iio_dev *indio_dev)
+{
+ iio_dealloc_pollfunc(indio_dev->pollfunc);
+ iio_kfifo_free(indio_dev->buffer);
+}
diff --git a/drivers/staging/iio/iio_simple_dummy_events.c b/drivers/staging/iio/iio_simple_dummy_events.c
new file mode 100644
index 000000000..a5cd3bb21
--- /dev/null
+++ b/drivers/staging/iio/iio_simple_dummy_events.c
@@ -0,0 +1,267 @@
+/**
+ * Copyright (c) 2011 Jonathan Cameron
+ *
+ * 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.
+ *
+ * Event handling elements of industrial I/O reference driver.
+ */
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/iio/events.h>
+#include "iio_simple_dummy.h"
+
+/* Evgen 'fakes' interrupt events for this example */
+#include "iio_dummy_evgen.h"
+
+/**
+ * iio_simple_dummy_read_event_config() - is event enabled?
+ * @indio_dev: the device instance data
+ * @chan: channel for the event whose state is being queried
+ * @type: type of the event whose state is being queried
+ * @dir: direction of the vent whose state is being queried
+ *
+ * This function would normally query the relevant registers or a cache to
+ * discover if the event generation is enabled on the device.
+ */
+int iio_simple_dummy_read_event_config(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan,
+ enum iio_event_type type,
+ enum iio_event_direction dir)
+{
+ struct iio_dummy_state *st = iio_priv(indio_dev);
+
+ return st->event_en;
+}
+
+/**
+ * iio_simple_dummy_write_event_config() - set whether event is enabled
+ * @indio_dev: the device instance data
+ * @chan: channel for the event whose state is being set
+ * @type: type of the event whose state is being set
+ * @dir: direction of the vent whose state is being set
+ * @state: whether to enable or disable the device.
+ *
+ * This function would normally set the relevant registers on the devices
+ * so that it generates the specified event. Here it just sets up a cached
+ * value.
+ */
+int iio_simple_dummy_write_event_config(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan,
+ enum iio_event_type type,
+ enum iio_event_direction dir,
+ int state)
+{
+ struct iio_dummy_state *st = iio_priv(indio_dev);
+
+ /*
+ * Deliberately over the top code splitting to illustrate
+ * how this is done when multiple events exist.
+ */
+ switch (chan->type) {
+ case IIO_VOLTAGE:
+ switch (type) {
+ case IIO_EV_TYPE_THRESH:
+ if (dir == IIO_EV_DIR_RISING)
+ st->event_en = state;
+ else
+ return -EINVAL;
+ default:
+ return -EINVAL;
+ }
+ break;
+ case IIO_ACTIVITY:
+ switch (type) {
+ case IIO_EV_TYPE_THRESH:
+ st->event_en = state;
+ break;
+ default:
+ return -EINVAL;
+ }
+ case IIO_STEPS:
+ switch (type) {
+ case IIO_EV_TYPE_CHANGE:
+ st->event_en = state;
+ break;
+ default:
+ return -EINVAL;
+ }
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/**
+ * iio_simple_dummy_read_event_value() - get value associated with event
+ * @indio_dev: device instance specific data
+ * @chan: channel for the event whose value is being read
+ * @type: type of the event whose value is being read
+ * @dir: direction of the vent whose value is being read
+ * @info: info type of the event whose value is being read
+ * @val: value for the event code.
+ *
+ * Many devices provide a large set of events of which only a subset may
+ * be enabled at a time, with value registers whose meaning changes depending
+ * on the event enabled. This often means that the driver must cache the values
+ * associated with each possible events so that the right value is in place when
+ * the enabled event is changed.
+ */
+int iio_simple_dummy_read_event_value(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan,
+ enum iio_event_type type,
+ enum iio_event_direction dir,
+ enum iio_event_info info,
+ int *val, int *val2)
+{
+ struct iio_dummy_state *st = iio_priv(indio_dev);
+
+ *val = st->event_val;
+
+ return IIO_VAL_INT;
+}
+
+/**
+ * iio_simple_dummy_write_event_value() - set value associate with event
+ * @indio_dev: device instance specific data
+ * @chan: channel for the event whose value is being set
+ * @type: type of the event whose value is being set
+ * @dir: direction of the vent whose value is being set
+ * @info: info type of the event whose value is being set
+ * @val: the value to be set.
+ */
+int iio_simple_dummy_write_event_value(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan,
+ enum iio_event_type type,
+ enum iio_event_direction dir,
+ enum iio_event_info info,
+ int val, int val2)
+{
+ struct iio_dummy_state *st = iio_priv(indio_dev);
+
+ st->event_val = val;
+
+ return 0;
+}
+
+/**
+ * iio_simple_dummy_event_handler() - identify and pass on event
+ * @irq: irq of event line
+ * @private: pointer to device instance state.
+ *
+ * This handler is responsible for querying the device to find out what
+ * event occurred and for then pushing that event towards userspace.
+ * Here only one event occurs so we push that directly on with locally
+ * grabbed timestamp.
+ */
+static irqreturn_t iio_simple_dummy_event_handler(int irq, void *private)
+{
+ struct iio_dev *indio_dev = private;
+ struct iio_dummy_state *st = iio_priv(indio_dev);
+
+ dev_dbg(&indio_dev->dev, "id %x event %x\n",
+ st->regs->reg_id, st->regs->reg_data);
+
+ switch (st->regs->reg_data) {
+ case 0:
+ iio_push_event(indio_dev,
+ IIO_EVENT_CODE(IIO_VOLTAGE, 0, 0,
+ IIO_EV_DIR_RISING,
+ IIO_EV_TYPE_THRESH, 0, 0, 0),
+ iio_get_time_ns());
+ break;
+ case 1:
+ if (st->activity_running > st->event_val)
+ iio_push_event(indio_dev,
+ IIO_EVENT_CODE(IIO_ACTIVITY, 0,
+ IIO_MOD_RUNNING,
+ IIO_EV_DIR_RISING,
+ IIO_EV_TYPE_THRESH,
+ 0, 0, 0),
+ iio_get_time_ns());
+ break;
+ case 2:
+ if (st->activity_walking < st->event_val)
+ iio_push_event(indio_dev,
+ IIO_EVENT_CODE(IIO_ACTIVITY, 0,
+ IIO_MOD_WALKING,
+ IIO_EV_DIR_FALLING,
+ IIO_EV_TYPE_THRESH,
+ 0, 0, 0),
+ iio_get_time_ns());
+ break;
+ case 3:
+ iio_push_event(indio_dev,
+ IIO_EVENT_CODE(IIO_STEPS, 0, IIO_NO_MOD,
+ IIO_EV_DIR_NONE,
+ IIO_EV_TYPE_CHANGE, 0, 0, 0),
+ iio_get_time_ns());
+ break;
+ default:
+ break;
+ }
+
+ return IRQ_HANDLED;
+}
+
+/**
+ * iio_simple_dummy_events_register() - setup interrupt handling for events
+ * @indio_dev: device instance data
+ *
+ * This function requests the threaded interrupt to handle the events.
+ * Normally the irq is a hardware interrupt and the number comes
+ * from board configuration files. Here we get it from a companion
+ * module that fakes the interrupt for us. Note that module in
+ * no way forms part of this example. Just assume that events magically
+ * appear via the provided interrupt.
+ */
+int iio_simple_dummy_events_register(struct iio_dev *indio_dev)
+{
+ struct iio_dummy_state *st = iio_priv(indio_dev);
+ int ret;
+
+ /* Fire up event source - normally not present */
+ st->event_irq = iio_dummy_evgen_get_irq();
+ if (st->event_irq < 0) {
+ ret = st->event_irq;
+ goto error_ret;
+ }
+ st->regs = iio_dummy_evgen_get_regs(st->event_irq);
+
+ ret = request_threaded_irq(st->event_irq,
+ NULL,
+ &iio_simple_dummy_event_handler,
+ IRQF_ONESHOT,
+ "iio_simple_event",
+ indio_dev);
+ if (ret < 0)
+ goto error_free_evgen;
+ return 0;
+
+error_free_evgen:
+ iio_dummy_evgen_release_irq(st->event_irq);
+error_ret:
+ return ret;
+}
+
+/**
+ * iio_simple_dummy_events_unregister() - tidy up interrupt handling on remove
+ * @indio_dev: device instance data
+ */
+int iio_simple_dummy_events_unregister(struct iio_dev *indio_dev)
+{
+ struct iio_dummy_state *st = iio_priv(indio_dev);
+
+ free_irq(st->event_irq, indio_dev);
+ /* Not part of normal driver */
+ iio_dummy_evgen_release_irq(st->event_irq);
+
+ return 0;
+}
diff --git a/drivers/staging/iio/impedance-analyzer/Kconfig b/drivers/staging/iio/impedance-analyzer/Kconfig
new file mode 100644
index 000000000..dd97b6bb3
--- /dev/null
+++ b/drivers/staging/iio/impedance-analyzer/Kconfig
@@ -0,0 +1,18 @@
+#
+# Impedance Converter, Network Analyzer drivers
+#
+menu "Network Analyzer, Impedance Converters"
+
+config AD5933
+ tristate "Analog Devices AD5933, AD5934 driver"
+ depends on I2C
+ select IIO_BUFFER
+ select IIO_KFIFO_BUF
+ help
+ Say yes here to build support for Analog Devices Impedance Converter,
+ Network Analyzer, AD5933/4, provides direct access via sysfs.
+
+ To compile this driver as a module, choose M here: the
+ module will be called ad5933.
+
+endmenu
diff --git a/drivers/staging/iio/impedance-analyzer/Makefile b/drivers/staging/iio/impedance-analyzer/Makefile
new file mode 100644
index 000000000..7604d7865
--- /dev/null
+++ b/drivers/staging/iio/impedance-analyzer/Makefile
@@ -0,0 +1,5 @@
+#
+# Makefile for Impedance Converter, Network Analyzer drivers
+#
+
+obj-$(CONFIG_AD5933) += ad5933.o
diff --git a/drivers/staging/iio/impedance-analyzer/ad5933.c b/drivers/staging/iio/impedance-analyzer/ad5933.c
new file mode 100644
index 000000000..c18109c55
--- /dev/null
+++ b/drivers/staging/iio/impedance-analyzer/ad5933.c
@@ -0,0 +1,804 @@
+/*
+ * AD5933 AD5934 Impedance Converter, Network Analyzer
+ *
+ * Copyright 2011 Analog Devices Inc.
+ *
+ * Licensed under the GPL-2.
+ */
+
+#include <linux/interrupt.h>
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/sysfs.h>
+#include <linux/i2c.h>
+#include <linux/regulator/consumer.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <linux/err.h>
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <asm/div64.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/kfifo_buf.h>
+
+#include "ad5933.h"
+
+/* AD5933/AD5934 Registers */
+#define AD5933_REG_CONTROL_HB 0x80 /* R/W, 2 bytes */
+#define AD5933_REG_CONTROL_LB 0x81 /* R/W, 2 bytes */
+#define AD5933_REG_FREQ_START 0x82 /* R/W, 3 bytes */
+#define AD5933_REG_FREQ_INC 0x85 /* R/W, 3 bytes */
+#define AD5933_REG_INC_NUM 0x88 /* R/W, 2 bytes, 9 bit */
+#define AD5933_REG_SETTLING_CYCLES 0x8A /* R/W, 2 bytes */
+#define AD5933_REG_STATUS 0x8F /* R, 1 byte */
+#define AD5933_REG_TEMP_DATA 0x92 /* R, 2 bytes*/
+#define AD5933_REG_REAL_DATA 0x94 /* R, 2 bytes*/
+#define AD5933_REG_IMAG_DATA 0x96 /* R, 2 bytes*/
+
+/* AD5933_REG_CONTROL_HB Bits */
+#define AD5933_CTRL_INIT_START_FREQ (0x1 << 4)
+#define AD5933_CTRL_START_SWEEP (0x2 << 4)
+#define AD5933_CTRL_INC_FREQ (0x3 << 4)
+#define AD5933_CTRL_REPEAT_FREQ (0x4 << 4)
+#define AD5933_CTRL_MEASURE_TEMP (0x9 << 4)
+#define AD5933_CTRL_POWER_DOWN (0xA << 4)
+#define AD5933_CTRL_STANDBY (0xB << 4)
+
+#define AD5933_CTRL_RANGE_2000mVpp (0x0 << 1)
+#define AD5933_CTRL_RANGE_200mVpp (0x1 << 1)
+#define AD5933_CTRL_RANGE_400mVpp (0x2 << 1)
+#define AD5933_CTRL_RANGE_1000mVpp (0x3 << 1)
+#define AD5933_CTRL_RANGE(x) ((x) << 1)
+
+#define AD5933_CTRL_PGA_GAIN_1 (0x1 << 0)
+#define AD5933_CTRL_PGA_GAIN_5 (0x0 << 0)
+
+/* AD5933_REG_CONTROL_LB Bits */
+#define AD5933_CTRL_RESET (0x1 << 4)
+#define AD5933_CTRL_INT_SYSCLK (0x0 << 3)
+#define AD5933_CTRL_EXT_SYSCLK (0x1 << 3)
+
+/* AD5933_REG_STATUS Bits */
+#define AD5933_STAT_TEMP_VALID (0x1 << 0)
+#define AD5933_STAT_DATA_VALID (0x1 << 1)
+#define AD5933_STAT_SWEEP_DONE (0x1 << 2)
+
+/* I2C Block Commands */
+#define AD5933_I2C_BLOCK_WRITE 0xA0
+#define AD5933_I2C_BLOCK_READ 0xA1
+#define AD5933_I2C_ADDR_POINTER 0xB0
+
+/* Device Specs */
+#define AD5933_INT_OSC_FREQ_Hz 16776000
+#define AD5933_MAX_OUTPUT_FREQ_Hz 100000
+#define AD5933_MAX_RETRIES 100
+
+#define AD5933_OUT_RANGE 1
+#define AD5933_OUT_RANGE_AVAIL 2
+#define AD5933_OUT_SETTLING_CYCLES 3
+#define AD5933_IN_PGA_GAIN 4
+#define AD5933_IN_PGA_GAIN_AVAIL 5
+#define AD5933_FREQ_POINTS 6
+
+#define AD5933_POLL_TIME_ms 10
+#define AD5933_INIT_EXCITATION_TIME_ms 100
+
+struct ad5933_state {
+ struct i2c_client *client;
+ struct regulator *reg;
+ struct delayed_work work;
+ unsigned long mclk_hz;
+ unsigned char ctrl_hb;
+ unsigned char ctrl_lb;
+ unsigned range_avail[4];
+ unsigned short vref_mv;
+ unsigned short settling_cycles;
+ unsigned short freq_points;
+ unsigned freq_start;
+ unsigned freq_inc;
+ unsigned state;
+ unsigned poll_time_jiffies;
+};
+
+static struct ad5933_platform_data ad5933_default_pdata = {
+ .vref_mv = 3300,
+};
+
+static const struct iio_chan_spec ad5933_channels[] = {
+ {
+ .type = IIO_TEMP,
+ .indexed = 1,
+ .channel = 0,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+ BIT(IIO_CHAN_INFO_SCALE),
+ .address = AD5933_REG_TEMP_DATA,
+ .scan_index = -1,
+ .scan_type = {
+ .sign = 's',
+ .realbits = 14,
+ .storagebits = 16,
+ },
+ }, { /* Ring Channels */
+ .type = IIO_VOLTAGE,
+ .indexed = 1,
+ .channel = 0,
+ .extend_name = "real",
+ .address = AD5933_REG_REAL_DATA,
+ .scan_index = 0,
+ .scan_type = {
+ .sign = 's',
+ .realbits = 16,
+ .storagebits = 16,
+ },
+ }, {
+ .type = IIO_VOLTAGE,
+ .indexed = 1,
+ .channel = 0,
+ .extend_name = "imag",
+ .address = AD5933_REG_IMAG_DATA,
+ .scan_index = 1,
+ .scan_type = {
+ .sign = 's',
+ .realbits = 16,
+ .storagebits = 16,
+ },
+ },
+};
+
+static int ad5933_i2c_write(struct i2c_client *client,
+ u8 reg, u8 len, u8 *data)
+{
+ int ret;
+
+ while (len--) {
+ ret = i2c_smbus_write_byte_data(client, reg++, *data++);
+ if (ret < 0) {
+ dev_err(&client->dev, "I2C write error\n");
+ return ret;
+ }
+ }
+ return 0;
+}
+
+static int ad5933_i2c_read(struct i2c_client *client,
+ u8 reg, u8 len, u8 *data)
+{
+ int ret;
+
+ while (len--) {
+ ret = i2c_smbus_read_byte_data(client, reg++);
+ if (ret < 0) {
+ dev_err(&client->dev, "I2C read error\n");
+ return ret;
+ }
+ *data++ = ret;
+ }
+ return 0;
+}
+
+static int ad5933_cmd(struct ad5933_state *st, unsigned char cmd)
+{
+ unsigned char dat = st->ctrl_hb | cmd;
+
+ return ad5933_i2c_write(st->client,
+ AD5933_REG_CONTROL_HB, 1, &dat);
+}
+
+static int ad5933_reset(struct ad5933_state *st)
+{
+ unsigned char dat = st->ctrl_lb | AD5933_CTRL_RESET;
+
+ return ad5933_i2c_write(st->client,
+ AD5933_REG_CONTROL_LB, 1, &dat);
+}
+
+static int ad5933_wait_busy(struct ad5933_state *st, unsigned char event)
+{
+ unsigned char val, timeout = AD5933_MAX_RETRIES;
+ int ret;
+
+ while (timeout--) {
+ ret = ad5933_i2c_read(st->client, AD5933_REG_STATUS, 1, &val);
+ if (ret < 0)
+ return ret;
+ if (val & event)
+ return val;
+ cpu_relax();
+ mdelay(1);
+ }
+
+ return -EAGAIN;
+}
+
+static int ad5933_set_freq(struct ad5933_state *st,
+ unsigned reg, unsigned long freq)
+{
+ unsigned long long freqreg;
+ union {
+ __be32 d32;
+ u8 d8[4];
+ } dat;
+
+ freqreg = (u64) freq * (u64) (1 << 27);
+ do_div(freqreg, st->mclk_hz / 4);
+
+ switch (reg) {
+ case AD5933_REG_FREQ_START:
+ st->freq_start = freq;
+ break;
+ case AD5933_REG_FREQ_INC:
+ st->freq_inc = freq;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ dat.d32 = cpu_to_be32(freqreg);
+ return ad5933_i2c_write(st->client, reg, 3, &dat.d8[1]);
+}
+
+static int ad5933_setup(struct ad5933_state *st)
+{
+ __be16 dat;
+ int ret;
+
+ ret = ad5933_reset(st);
+ if (ret < 0)
+ return ret;
+
+ ret = ad5933_set_freq(st, AD5933_REG_FREQ_START, 10000);
+ if (ret < 0)
+ return ret;
+
+ ret = ad5933_set_freq(st, AD5933_REG_FREQ_INC, 200);
+ if (ret < 0)
+ return ret;
+
+ st->settling_cycles = 10;
+ dat = cpu_to_be16(st->settling_cycles);
+
+ ret = ad5933_i2c_write(st->client,
+ AD5933_REG_SETTLING_CYCLES, 2, (u8 *)&dat);
+ if (ret < 0)
+ return ret;
+
+ st->freq_points = 100;
+ dat = cpu_to_be16(st->freq_points);
+
+ return ad5933_i2c_write(st->client, AD5933_REG_INC_NUM, 2, (u8 *)&dat);
+}
+
+static void ad5933_calc_out_ranges(struct ad5933_state *st)
+{
+ int i;
+ unsigned normalized_3v3[4] = {1980, 198, 383, 970};
+
+ for (i = 0; i < 4; i++)
+ st->range_avail[i] = normalized_3v3[i] * st->vref_mv / 3300;
+
+}
+
+/*
+ * handles: AD5933_REG_FREQ_START and AD5933_REG_FREQ_INC
+ */
+
+static ssize_t ad5933_show_frequency(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct ad5933_state *st = iio_priv(indio_dev);
+ struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
+ int ret;
+ unsigned long long freqreg;
+ union {
+ __be32 d32;
+ u8 d8[4];
+ } dat;
+
+ mutex_lock(&indio_dev->mlock);
+ ret = ad5933_i2c_read(st->client, this_attr->address, 3, &dat.d8[1]);
+ mutex_unlock(&indio_dev->mlock);
+ if (ret < 0)
+ return ret;
+
+ freqreg = be32_to_cpu(dat.d32) & 0xFFFFFF;
+
+ freqreg = (u64) freqreg * (u64) (st->mclk_hz / 4);
+ do_div(freqreg, 1 << 27);
+
+ return sprintf(buf, "%d\n", (int) freqreg);
+}
+
+static ssize_t ad5933_store_frequency(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t len)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct ad5933_state *st = iio_priv(indio_dev);
+ struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
+ unsigned long val;
+ int ret;
+
+ ret = kstrtoul(buf, 10, &val);
+ if (ret)
+ return ret;
+
+ if (val > AD5933_MAX_OUTPUT_FREQ_Hz)
+ return -EINVAL;
+
+ mutex_lock(&indio_dev->mlock);
+ ret = ad5933_set_freq(st, this_attr->address, val);
+ mutex_unlock(&indio_dev->mlock);
+
+ return ret ? ret : len;
+}
+
+static IIO_DEVICE_ATTR(out_voltage0_freq_start, S_IRUGO | S_IWUSR,
+ ad5933_show_frequency,
+ ad5933_store_frequency,
+ AD5933_REG_FREQ_START);
+
+static IIO_DEVICE_ATTR(out_voltage0_freq_increment, S_IRUGO | S_IWUSR,
+ ad5933_show_frequency,
+ ad5933_store_frequency,
+ AD5933_REG_FREQ_INC);
+
+static ssize_t ad5933_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct ad5933_state *st = iio_priv(indio_dev);
+ struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
+ int ret = 0, len = 0;
+
+ mutex_lock(&indio_dev->mlock);
+ switch ((u32) this_attr->address) {
+ case AD5933_OUT_RANGE:
+ len = sprintf(buf, "%u\n",
+ st->range_avail[(st->ctrl_hb >> 1) & 0x3]);
+ break;
+ case AD5933_OUT_RANGE_AVAIL:
+ len = sprintf(buf, "%u %u %u %u\n", st->range_avail[0],
+ st->range_avail[3], st->range_avail[2],
+ st->range_avail[1]);
+ break;
+ case AD5933_OUT_SETTLING_CYCLES:
+ len = sprintf(buf, "%d\n", st->settling_cycles);
+ break;
+ case AD5933_IN_PGA_GAIN:
+ len = sprintf(buf, "%s\n",
+ (st->ctrl_hb & AD5933_CTRL_PGA_GAIN_1) ?
+ "1" : "0.2");
+ break;
+ case AD5933_IN_PGA_GAIN_AVAIL:
+ len = sprintf(buf, "1 0.2\n");
+ break;
+ case AD5933_FREQ_POINTS:
+ len = sprintf(buf, "%d\n", st->freq_points);
+ break;
+ default:
+ ret = -EINVAL;
+ }
+
+ mutex_unlock(&indio_dev->mlock);
+ return ret ? ret : len;
+}
+
+static ssize_t ad5933_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t len)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct ad5933_state *st = iio_priv(indio_dev);
+ struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
+ u16 val;
+ int i, ret = 0;
+ __be16 dat;
+
+ if (this_attr->address != AD5933_IN_PGA_GAIN) {
+ ret = kstrtou16(buf, 10, &val);
+ if (ret)
+ return ret;
+ }
+
+ mutex_lock(&indio_dev->mlock);
+ switch ((u32) this_attr->address) {
+ case AD5933_OUT_RANGE:
+ for (i = 0; i < 4; i++)
+ if (val == st->range_avail[i]) {
+ st->ctrl_hb &= ~AD5933_CTRL_RANGE(0x3);
+ st->ctrl_hb |= AD5933_CTRL_RANGE(i);
+ ret = ad5933_cmd(st, 0);
+ break;
+ }
+ ret = -EINVAL;
+ break;
+ case AD5933_IN_PGA_GAIN:
+ if (sysfs_streq(buf, "1")) {
+ st->ctrl_hb |= AD5933_CTRL_PGA_GAIN_1;
+ } else if (sysfs_streq(buf, "0.2")) {
+ st->ctrl_hb &= ~AD5933_CTRL_PGA_GAIN_1;
+ } else {
+ ret = -EINVAL;
+ break;
+ }
+ ret = ad5933_cmd(st, 0);
+ break;
+ case AD5933_OUT_SETTLING_CYCLES:
+ val = clamp(val, (u16)0, (u16)0x7FF);
+ st->settling_cycles = val;
+
+ /* 2x, 4x handling, see datasheet */
+ if (val > 511)
+ val = (val >> 1) | (1 << 9);
+ else if (val > 1022)
+ val = (val >> 2) | (3 << 9);
+
+ dat = cpu_to_be16(val);
+ ret = ad5933_i2c_write(st->client,
+ AD5933_REG_SETTLING_CYCLES, 2, (u8 *)&dat);
+ break;
+ case AD5933_FREQ_POINTS:
+ val = clamp(val, (u16)0, (u16)511);
+ st->freq_points = val;
+
+ dat = cpu_to_be16(val);
+ ret = ad5933_i2c_write(st->client, AD5933_REG_INC_NUM, 2,
+ (u8 *)&dat);
+ break;
+ default:
+ ret = -EINVAL;
+ }
+
+ mutex_unlock(&indio_dev->mlock);
+ return ret ? ret : len;
+}
+
+static IIO_DEVICE_ATTR(out_voltage0_scale, S_IRUGO | S_IWUSR,
+ ad5933_show,
+ ad5933_store,
+ AD5933_OUT_RANGE);
+
+static IIO_DEVICE_ATTR(out_voltage0_scale_available, S_IRUGO,
+ ad5933_show,
+ NULL,
+ AD5933_OUT_RANGE_AVAIL);
+
+static IIO_DEVICE_ATTR(in_voltage0_scale, S_IRUGO | S_IWUSR,
+ ad5933_show,
+ ad5933_store,
+ AD5933_IN_PGA_GAIN);
+
+static IIO_DEVICE_ATTR(in_voltage0_scale_available, S_IRUGO,
+ ad5933_show,
+ NULL,
+ AD5933_IN_PGA_GAIN_AVAIL);
+
+static IIO_DEVICE_ATTR(out_voltage0_freq_points, S_IRUGO | S_IWUSR,
+ ad5933_show,
+ ad5933_store,
+ AD5933_FREQ_POINTS);
+
+static IIO_DEVICE_ATTR(out_voltage0_settling_cycles, S_IRUGO | S_IWUSR,
+ ad5933_show,
+ ad5933_store,
+ AD5933_OUT_SETTLING_CYCLES);
+
+/* note:
+ * ideally we would handle the scale attributes via the iio_info
+ * (read|write)_raw methods, however this part is a untypical since we
+ * don't create dedicated sysfs channel attributes for out0 and in0.
+ */
+static struct attribute *ad5933_attributes[] = {
+ &iio_dev_attr_out_voltage0_scale.dev_attr.attr,
+ &iio_dev_attr_out_voltage0_scale_available.dev_attr.attr,
+ &iio_dev_attr_out_voltage0_freq_start.dev_attr.attr,
+ &iio_dev_attr_out_voltage0_freq_increment.dev_attr.attr,
+ &iio_dev_attr_out_voltage0_freq_points.dev_attr.attr,
+ &iio_dev_attr_out_voltage0_settling_cycles.dev_attr.attr,
+ &iio_dev_attr_in_voltage0_scale.dev_attr.attr,
+ &iio_dev_attr_in_voltage0_scale_available.dev_attr.attr,
+ NULL
+};
+
+static const struct attribute_group ad5933_attribute_group = {
+ .attrs = ad5933_attributes,
+};
+
+static int ad5933_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val,
+ int *val2,
+ long m)
+{
+ struct ad5933_state *st = iio_priv(indio_dev);
+ __be16 dat;
+ int ret;
+
+ switch (m) {
+ case IIO_CHAN_INFO_RAW:
+ mutex_lock(&indio_dev->mlock);
+ if (iio_buffer_enabled(indio_dev)) {
+ ret = -EBUSY;
+ goto out;
+ }
+ ret = ad5933_cmd(st, AD5933_CTRL_MEASURE_TEMP);
+ if (ret < 0)
+ goto out;
+ ret = ad5933_wait_busy(st, AD5933_STAT_TEMP_VALID);
+ if (ret < 0)
+ goto out;
+
+ ret = ad5933_i2c_read(st->client,
+ AD5933_REG_TEMP_DATA, 2,
+ (u8 *)&dat);
+ if (ret < 0)
+ goto out;
+ mutex_unlock(&indio_dev->mlock);
+ *val = sign_extend32(be16_to_cpu(dat), 13);
+
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_SCALE:
+ *val = 1000;
+ *val2 = 5;
+ return IIO_VAL_FRACTIONAL_LOG2;
+ }
+
+ return -EINVAL;
+out:
+ mutex_unlock(&indio_dev->mlock);
+ return ret;
+}
+
+static const struct iio_info ad5933_info = {
+ .read_raw = &ad5933_read_raw,
+ .attrs = &ad5933_attribute_group,
+ .driver_module = THIS_MODULE,
+};
+
+static int ad5933_ring_preenable(struct iio_dev *indio_dev)
+{
+ struct ad5933_state *st = iio_priv(indio_dev);
+ int ret;
+
+ if (bitmap_empty(indio_dev->active_scan_mask, indio_dev->masklength))
+ return -EINVAL;
+
+ ret = ad5933_reset(st);
+ if (ret < 0)
+ return ret;
+
+ ret = ad5933_cmd(st, AD5933_CTRL_STANDBY);
+ if (ret < 0)
+ return ret;
+
+ ret = ad5933_cmd(st, AD5933_CTRL_INIT_START_FREQ);
+ if (ret < 0)
+ return ret;
+
+ st->state = AD5933_CTRL_INIT_START_FREQ;
+
+ return 0;
+}
+
+static int ad5933_ring_postenable(struct iio_dev *indio_dev)
+{
+ struct ad5933_state *st = iio_priv(indio_dev);
+
+ /* AD5933_CTRL_INIT_START_FREQ:
+ * High Q complex circuits require a long time to reach steady state.
+ * To facilitate the measurement of such impedances, this mode allows
+ * the user full control of the settling time requirement before
+ * entering start frequency sweep mode where the impedance measurement
+ * takes place. In this mode the impedance is excited with the
+ * programmed start frequency (ad5933_ring_preenable),
+ * but no measurement takes place.
+ */
+
+ schedule_delayed_work(&st->work,
+ msecs_to_jiffies(AD5933_INIT_EXCITATION_TIME_ms));
+ return 0;
+}
+
+static int ad5933_ring_postdisable(struct iio_dev *indio_dev)
+{
+ struct ad5933_state *st = iio_priv(indio_dev);
+
+ cancel_delayed_work_sync(&st->work);
+ return ad5933_cmd(st, AD5933_CTRL_POWER_DOWN);
+}
+
+static const struct iio_buffer_setup_ops ad5933_ring_setup_ops = {
+ .preenable = &ad5933_ring_preenable,
+ .postenable = &ad5933_ring_postenable,
+ .postdisable = &ad5933_ring_postdisable,
+};
+
+static int ad5933_register_ring_funcs_and_init(struct iio_dev *indio_dev)
+{
+ struct iio_buffer *buffer;
+
+ buffer = iio_kfifo_allocate();
+ if (!buffer)
+ return -ENOMEM;
+
+ iio_device_attach_buffer(indio_dev, buffer);
+
+ /* Ring buffer functions - here trigger setup related */
+ indio_dev->setup_ops = &ad5933_ring_setup_ops;
+
+ indio_dev->modes |= INDIO_BUFFER_HARDWARE;
+
+ return 0;
+}
+
+static void ad5933_work(struct work_struct *work)
+{
+ struct ad5933_state *st = container_of(work,
+ struct ad5933_state, work.work);
+ struct iio_dev *indio_dev = i2c_get_clientdata(st->client);
+ signed short buf[2];
+ unsigned char status;
+
+ mutex_lock(&indio_dev->mlock);
+ if (st->state == AD5933_CTRL_INIT_START_FREQ) {
+ /* start sweep */
+ ad5933_cmd(st, AD5933_CTRL_START_SWEEP);
+ st->state = AD5933_CTRL_START_SWEEP;
+ schedule_delayed_work(&st->work, st->poll_time_jiffies);
+ mutex_unlock(&indio_dev->mlock);
+ return;
+ }
+
+ ad5933_i2c_read(st->client, AD5933_REG_STATUS, 1, &status);
+
+ if (status & AD5933_STAT_DATA_VALID) {
+ int scan_count = bitmap_weight(indio_dev->active_scan_mask,
+ indio_dev->masklength);
+ ad5933_i2c_read(st->client,
+ test_bit(1, indio_dev->active_scan_mask) ?
+ AD5933_REG_REAL_DATA : AD5933_REG_IMAG_DATA,
+ scan_count * 2, (u8 *)buf);
+
+ if (scan_count == 2) {
+ buf[0] = be16_to_cpu(buf[0]);
+ buf[1] = be16_to_cpu(buf[1]);
+ } else {
+ buf[0] = be16_to_cpu(buf[0]);
+ }
+ iio_push_to_buffers(indio_dev, buf);
+ } else {
+ /* no data available - try again later */
+ schedule_delayed_work(&st->work, st->poll_time_jiffies);
+ mutex_unlock(&indio_dev->mlock);
+ return;
+ }
+
+ if (status & AD5933_STAT_SWEEP_DONE) {
+ /* last sample received - power down do nothing until
+ * the ring enable is toggled */
+ ad5933_cmd(st, AD5933_CTRL_POWER_DOWN);
+ } else {
+ /* we just received a valid datum, move on to the next */
+ ad5933_cmd(st, AD5933_CTRL_INC_FREQ);
+ schedule_delayed_work(&st->work, st->poll_time_jiffies);
+ }
+
+ mutex_unlock(&indio_dev->mlock);
+}
+
+static int ad5933_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ int ret, voltage_uv = 0;
+ struct ad5933_platform_data *pdata = client->dev.platform_data;
+ struct ad5933_state *st;
+ struct iio_dev *indio_dev;
+
+ indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*st));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ st = iio_priv(indio_dev);
+ i2c_set_clientdata(client, indio_dev);
+ st->client = client;
+
+ if (!pdata)
+ pdata = &ad5933_default_pdata;
+
+ st->reg = devm_regulator_get(&client->dev, "vcc");
+ if (!IS_ERR(st->reg)) {
+ ret = regulator_enable(st->reg);
+ if (ret)
+ return ret;
+ voltage_uv = regulator_get_voltage(st->reg);
+ }
+
+ if (voltage_uv)
+ st->vref_mv = voltage_uv / 1000;
+ else
+ st->vref_mv = pdata->vref_mv;
+
+ if (pdata->ext_clk_Hz) {
+ st->mclk_hz = pdata->ext_clk_Hz;
+ st->ctrl_lb = AD5933_CTRL_EXT_SYSCLK;
+ } else {
+ st->mclk_hz = AD5933_INT_OSC_FREQ_Hz;
+ st->ctrl_lb = AD5933_CTRL_INT_SYSCLK;
+ }
+
+ ad5933_calc_out_ranges(st);
+ INIT_DELAYED_WORK(&st->work, ad5933_work);
+ st->poll_time_jiffies = msecs_to_jiffies(AD5933_POLL_TIME_ms);
+
+ indio_dev->dev.parent = &client->dev;
+ indio_dev->info = &ad5933_info;
+ indio_dev->name = id->name;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->channels = ad5933_channels;
+ indio_dev->num_channels = ARRAY_SIZE(ad5933_channels);
+
+ ret = ad5933_register_ring_funcs_and_init(indio_dev);
+ if (ret)
+ goto error_disable_reg;
+
+ ret = ad5933_setup(st);
+ if (ret)
+ goto error_unreg_ring;
+
+ ret = iio_device_register(indio_dev);
+ if (ret)
+ goto error_unreg_ring;
+
+ return 0;
+
+error_unreg_ring:
+ iio_kfifo_free(indio_dev->buffer);
+error_disable_reg:
+ if (!IS_ERR(st->reg))
+ regulator_disable(st->reg);
+
+ return ret;
+}
+
+static int ad5933_remove(struct i2c_client *client)
+{
+ struct iio_dev *indio_dev = i2c_get_clientdata(client);
+ struct ad5933_state *st = iio_priv(indio_dev);
+
+ iio_device_unregister(indio_dev);
+ iio_kfifo_free(indio_dev->buffer);
+ if (!IS_ERR(st->reg))
+ regulator_disable(st->reg);
+
+ return 0;
+}
+
+static const struct i2c_device_id ad5933_id[] = {
+ { "ad5933", 0 },
+ { "ad5934", 0 },
+ {}
+};
+
+MODULE_DEVICE_TABLE(i2c, ad5933_id);
+
+static struct i2c_driver ad5933_driver = {
+ .driver = {
+ .name = "ad5933",
+ },
+ .probe = ad5933_probe,
+ .remove = ad5933_remove,
+ .id_table = ad5933_id,
+};
+module_i2c_driver(ad5933_driver);
+
+MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
+MODULE_DESCRIPTION("Analog Devices AD5933 Impedance Conv. Network Analyzer");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/staging/iio/impedance-analyzer/ad5933.h b/drivers/staging/iio/impedance-analyzer/ad5933.h
new file mode 100644
index 000000000..b140e42d6
--- /dev/null
+++ b/drivers/staging/iio/impedance-analyzer/ad5933.h
@@ -0,0 +1,28 @@
+/*
+ * AD5933 AD5934 Impedance Converter, Network Analyzer
+ *
+ * Copyright 2011 Analog Devices Inc.
+ *
+ * Licensed under the GPL-2.
+ */
+
+#ifndef IIO_ADC_AD5933_H_
+#define IIO_ADC_AD5933_H_
+
+/*
+ * TODO: struct ad5933_platform_data needs to go into include/linux/iio
+ */
+
+/**
+ * struct ad5933_platform_data - platform specific data
+ * @ext_clk_Hz: the external clock frequency in Hz, if not set
+ * the driver uses the internal clock (16.776 MHz)
+ * @vref_mv: the external reference voltage in millivolt
+ */
+
+struct ad5933_platform_data {
+ unsigned long ext_clk_Hz;
+ unsigned short vref_mv;
+};
+
+#endif /* IIO_ADC_AD5933_H_ */
diff --git a/drivers/staging/iio/light/Kconfig b/drivers/staging/iio/light/Kconfig
new file mode 100644
index 000000000..ca8d6e66c
--- /dev/null
+++ b/drivers/staging/iio/light/Kconfig
@@ -0,0 +1,43 @@
+#
+# Light sensors
+#
+menu "Light sensors"
+
+config SENSORS_ISL29018
+ tristate "ISL 29018 light and proximity sensor"
+ depends on I2C
+ select REGMAP_I2C
+ default n
+ help
+ If you say yes here you get support for ambient light sensing and
+ proximity infrared sensing from Intersil ISL29018.
+ This driver will provide the measurements of ambient light intensity
+ in lux, proximity infrared sensing and normal infrared sensing.
+ Data from sensor is accessible via sysfs.
+
+config SENSORS_ISL29028
+ tristate "Intersil ISL29028 Concurrent Light and Proximity Sensor"
+ depends on I2C
+ select REGMAP_I2C
+ help
+ Provides driver for the Intersil's ISL29028 device.
+ This driver supports the sysfs interface to get the ALS, IR intensity,
+ Proximity value via iio. The ISL29028 provides the concurrent sensing
+ of ambient light and proximity.
+
+config TSL2583
+ tristate "TAOS TSL2580, TSL2581 and TSL2583 light-to-digital converters"
+ depends on I2C
+ help
+ Provides support for the TAOS tsl2580, tsl2581 and tsl2583 devices.
+ Access ALS data via iio, sysfs.
+
+config TSL2x7x
+ tristate "TAOS TSL/TMD2x71 and TSL/TMD2x72 Family of light and proximity sensors"
+ depends on I2C
+ help
+ Support for: tsl2571, tsl2671, tmd2671, tsl2771, tmd2771, tsl2572, tsl2672,
+ tmd2672, tsl2772, tmd2772 devices.
+ Provides iio_events and direct access via sysfs.
+
+endmenu
diff --git a/drivers/staging/iio/light/Makefile b/drivers/staging/iio/light/Makefile
new file mode 100644
index 000000000..9960fdf7c
--- /dev/null
+++ b/drivers/staging/iio/light/Makefile
@@ -0,0 +1,8 @@
+#
+# Makefile for industrial I/O Light sensors
+#
+
+obj-$(CONFIG_SENSORS_ISL29018) += isl29018.o
+obj-$(CONFIG_SENSORS_ISL29028) += isl29028.o
+obj-$(CONFIG_TSL2583) += tsl2583.o
+obj-$(CONFIG_TSL2x7x) += tsl2x7x_core.o
diff --git a/drivers/staging/iio/light/isl29018.c b/drivers/staging/iio/light/isl29018.c
new file mode 100644
index 000000000..a3489187a
--- /dev/null
+++ b/drivers/staging/iio/light/isl29018.c
@@ -0,0 +1,815 @@
+/*
+ * A iio driver for the light sensor ISL 29018/29023/29035.
+ *
+ * IIO driver for monitoring ambient light intensity in luxi, proximity
+ * sensing and infrared sensing.
+ *
+ * Copyright (c) 2010, NVIDIA Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/err.h>
+#include <linux/mutex.h>
+#include <linux/delay.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/acpi.h>
+
+#define CONVERSION_TIME_MS 100
+
+#define ISL29018_REG_ADD_COMMAND1 0x00
+#define COMMMAND1_OPMODE_SHIFT 5
+#define COMMMAND1_OPMODE_MASK (7 << COMMMAND1_OPMODE_SHIFT)
+#define COMMMAND1_OPMODE_POWER_DOWN 0
+#define COMMMAND1_OPMODE_ALS_ONCE 1
+#define COMMMAND1_OPMODE_IR_ONCE 2
+#define COMMMAND1_OPMODE_PROX_ONCE 3
+
+#define ISL29018_REG_ADD_COMMANDII 0x01
+#define COMMANDII_RESOLUTION_SHIFT 2
+#define COMMANDII_RESOLUTION_MASK (0x3 << COMMANDII_RESOLUTION_SHIFT)
+
+#define COMMANDII_RANGE_SHIFT 0
+#define COMMANDII_RANGE_MASK (0x3 << COMMANDII_RANGE_SHIFT)
+
+#define COMMANDII_SCHEME_SHIFT 7
+#define COMMANDII_SCHEME_MASK (0x1 << COMMANDII_SCHEME_SHIFT)
+
+#define ISL29018_REG_ADD_DATA_LSB 0x02
+#define ISL29018_REG_ADD_DATA_MSB 0x03
+
+#define ISL29018_REG_TEST 0x08
+#define ISL29018_TEST_SHIFT 0
+#define ISL29018_TEST_MASK (0xFF << ISL29018_TEST_SHIFT)
+
+#define ISL29035_REG_DEVICE_ID 0x0F
+#define ISL29035_DEVICE_ID_SHIFT 0x03
+#define ISL29035_DEVICE_ID_MASK (0x7 << ISL29035_DEVICE_ID_SHIFT)
+#define ISL29035_DEVICE_ID 0x5
+#define ISL29035_BOUT_SHIFT 0x07
+#define ISL29035_BOUT_MASK (0x01 << ISL29035_BOUT_SHIFT)
+
+struct isl29018_chip {
+ struct device *dev;
+ struct regmap *regmap;
+ struct mutex lock;
+ int type;
+ unsigned int lux_scale;
+ unsigned int lux_uscale;
+ unsigned int range;
+ unsigned int adc_bit;
+ int prox_scheme;
+ bool suspended;
+};
+
+static int isl29018_set_range(struct isl29018_chip *chip, unsigned long range,
+ unsigned int *new_range)
+{
+ static const unsigned long supp_ranges[] = {1000, 4000, 16000, 64000};
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(supp_ranges); ++i) {
+ if (range <= supp_ranges[i]) {
+ *new_range = (unsigned int)supp_ranges[i];
+ break;
+ }
+ }
+
+ if (i >= ARRAY_SIZE(supp_ranges))
+ return -EINVAL;
+
+ return regmap_update_bits(chip->regmap, ISL29018_REG_ADD_COMMANDII,
+ COMMANDII_RANGE_MASK, i << COMMANDII_RANGE_SHIFT);
+}
+
+static int isl29018_set_resolution(struct isl29018_chip *chip,
+ unsigned long adcbit, unsigned int *conf_adc_bit)
+{
+ static const unsigned long supp_adcbit[] = {16, 12, 8, 4};
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(supp_adcbit); ++i) {
+ if (adcbit >= supp_adcbit[i]) {
+ *conf_adc_bit = (unsigned int)supp_adcbit[i];
+ break;
+ }
+ }
+
+ if (i >= ARRAY_SIZE(supp_adcbit))
+ return -EINVAL;
+
+ return regmap_update_bits(chip->regmap, ISL29018_REG_ADD_COMMANDII,
+ COMMANDII_RESOLUTION_MASK,
+ i << COMMANDII_RESOLUTION_SHIFT);
+}
+
+static int isl29018_read_sensor_input(struct isl29018_chip *chip, int mode)
+{
+ int status;
+ unsigned int lsb;
+ unsigned int msb;
+
+ /* Set mode */
+ status = regmap_write(chip->regmap, ISL29018_REG_ADD_COMMAND1,
+ mode << COMMMAND1_OPMODE_SHIFT);
+ if (status) {
+ dev_err(chip->dev,
+ "Error in setting operating mode err %d\n", status);
+ return status;
+ }
+ msleep(CONVERSION_TIME_MS);
+ status = regmap_read(chip->regmap, ISL29018_REG_ADD_DATA_LSB, &lsb);
+ if (status < 0) {
+ dev_err(chip->dev,
+ "Error in reading LSB DATA with err %d\n", status);
+ return status;
+ }
+
+ status = regmap_read(chip->regmap, ISL29018_REG_ADD_DATA_MSB, &msb);
+ if (status < 0) {
+ dev_err(chip->dev,
+ "Error in reading MSB DATA with error %d\n", status);
+ return status;
+ }
+ dev_vdbg(chip->dev, "MSB 0x%x and LSB 0x%x\n", msb, lsb);
+
+ return (msb << 8) | lsb;
+}
+
+static int isl29018_read_lux(struct isl29018_chip *chip, int *lux)
+{
+ int lux_data;
+ unsigned int data_x_range, lux_unshifted;
+
+ lux_data = isl29018_read_sensor_input(chip, COMMMAND1_OPMODE_ALS_ONCE);
+
+ if (lux_data < 0)
+ return lux_data;
+
+ /* To support fractional scaling, separate the unshifted lux
+ * into two calculations: int scaling and micro-scaling.
+ * lux_uscale ranges from 0-999999, so about 20 bits. Split
+ * the /1,000,000 in two to reduce the risk of over/underflow.
+ */
+ data_x_range = lux_data * chip->range;
+ lux_unshifted = data_x_range * chip->lux_scale;
+ lux_unshifted += data_x_range / 1000 * chip->lux_uscale / 1000;
+ *lux = lux_unshifted >> chip->adc_bit;
+
+ return 0;
+}
+
+static int isl29018_read_ir(struct isl29018_chip *chip, int *ir)
+{
+ int ir_data;
+
+ ir_data = isl29018_read_sensor_input(chip, COMMMAND1_OPMODE_IR_ONCE);
+
+ if (ir_data < 0)
+ return ir_data;
+
+ *ir = ir_data;
+
+ return 0;
+}
+
+static int isl29018_read_proximity_ir(struct isl29018_chip *chip, int scheme,
+ int *near_ir)
+{
+ int status;
+ int prox_data = -1;
+ int ir_data = -1;
+
+ /* Do proximity sensing with required scheme */
+ status = regmap_update_bits(chip->regmap, ISL29018_REG_ADD_COMMANDII,
+ COMMANDII_SCHEME_MASK,
+ scheme << COMMANDII_SCHEME_SHIFT);
+ if (status) {
+ dev_err(chip->dev, "Error in setting operating mode\n");
+ return status;
+ }
+
+ prox_data = isl29018_read_sensor_input(chip,
+ COMMMAND1_OPMODE_PROX_ONCE);
+ if (prox_data < 0)
+ return prox_data;
+
+ if (scheme == 1) {
+ *near_ir = prox_data;
+ return 0;
+ }
+
+ ir_data = isl29018_read_sensor_input(chip, COMMMAND1_OPMODE_IR_ONCE);
+
+ if (ir_data < 0)
+ return ir_data;
+
+ if (prox_data >= ir_data)
+ *near_ir = prox_data - ir_data;
+ else
+ *near_ir = 0;
+
+ return 0;
+}
+
+/* Sysfs interface */
+/* range */
+static ssize_t show_range(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct isl29018_chip *chip = iio_priv(indio_dev);
+
+ return sprintf(buf, "%u\n", chip->range);
+}
+
+static ssize_t store_range(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct isl29018_chip *chip = iio_priv(indio_dev);
+ int status;
+ unsigned long lval;
+ unsigned int new_range;
+
+ if (kstrtoul(buf, 10, &lval))
+ return -EINVAL;
+
+ if (!(lval == 1000UL || lval == 4000UL ||
+ lval == 16000UL || lval == 64000UL)) {
+ dev_err(dev, "The range is not supported\n");
+ return -EINVAL;
+ }
+
+ mutex_lock(&chip->lock);
+ status = isl29018_set_range(chip, lval, &new_range);
+ if (status < 0) {
+ mutex_unlock(&chip->lock);
+ dev_err(dev,
+ "Error in setting max range with err %d\n", status);
+ return status;
+ }
+ chip->range = new_range;
+ mutex_unlock(&chip->lock);
+
+ return count;
+}
+
+/* resolution */
+static ssize_t show_resolution(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct isl29018_chip *chip = iio_priv(indio_dev);
+
+ return sprintf(buf, "%u\n", chip->adc_bit);
+}
+
+static ssize_t store_resolution(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct isl29018_chip *chip = iio_priv(indio_dev);
+ int status;
+ unsigned int val;
+ unsigned int new_adc_bit;
+
+ if (kstrtouint(buf, 10, &val))
+ return -EINVAL;
+ if (!(val == 4 || val == 8 || val == 12 || val == 16)) {
+ dev_err(dev, "The resolution is not supported\n");
+ return -EINVAL;
+ }
+
+ mutex_lock(&chip->lock);
+ status = isl29018_set_resolution(chip, val, &new_adc_bit);
+ if (status < 0) {
+ mutex_unlock(&chip->lock);
+ dev_err(dev, "Error in setting resolution\n");
+ return status;
+ }
+ chip->adc_bit = new_adc_bit;
+ mutex_unlock(&chip->lock);
+
+ return count;
+}
+
+/* proximity scheme */
+static ssize_t show_prox_infrared_suppression(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct isl29018_chip *chip = iio_priv(indio_dev);
+
+ /* return the "proximity scheme" i.e. if the chip does on chip
+ infrared suppression (1 means perform on chip suppression) */
+ return sprintf(buf, "%d\n", chip->prox_scheme);
+}
+
+static ssize_t store_prox_infrared_suppression(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct isl29018_chip *chip = iio_priv(indio_dev);
+ int val;
+
+ if (kstrtoint(buf, 10, &val))
+ return -EINVAL;
+ if (!(val == 0 || val == 1)) {
+ dev_err(dev, "The mode is not supported\n");
+ return -EINVAL;
+ }
+
+ /* get the "proximity scheme" i.e. if the chip does on chip
+ infrared suppression (1 means perform on chip suppression) */
+ mutex_lock(&chip->lock);
+ chip->prox_scheme = val;
+ mutex_unlock(&chip->lock);
+
+ return count;
+}
+
+/* Channel IO */
+static int isl29018_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int val,
+ int val2,
+ long mask)
+{
+ struct isl29018_chip *chip = iio_priv(indio_dev);
+ int ret = -EINVAL;
+
+ mutex_lock(&chip->lock);
+ if (mask == IIO_CHAN_INFO_CALIBSCALE && chan->type == IIO_LIGHT) {
+ chip->lux_scale = val;
+ /* With no write_raw_get_fmt(), val2 is a MICRO fraction. */
+ chip->lux_uscale = val2;
+ ret = 0;
+ }
+ mutex_unlock(&chip->lock);
+
+ return ret;
+}
+
+static int isl29018_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val,
+ int *val2,
+ long mask)
+{
+ int ret = -EINVAL;
+ struct isl29018_chip *chip = iio_priv(indio_dev);
+
+ mutex_lock(&chip->lock);
+ if (chip->suspended) {
+ mutex_unlock(&chip->lock);
+ return -EBUSY;
+ }
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ case IIO_CHAN_INFO_PROCESSED:
+ switch (chan->type) {
+ case IIO_LIGHT:
+ ret = isl29018_read_lux(chip, val);
+ break;
+ case IIO_INTENSITY:
+ ret = isl29018_read_ir(chip, val);
+ break;
+ case IIO_PROXIMITY:
+ ret = isl29018_read_proximity_ir(chip,
+ chip->prox_scheme, val);
+ break;
+ default:
+ break;
+ }
+ if (!ret)
+ ret = IIO_VAL_INT;
+ break;
+ case IIO_CHAN_INFO_CALIBSCALE:
+ if (chan->type == IIO_LIGHT) {
+ *val = chip->lux_scale;
+ *val2 = chip->lux_uscale;
+ ret = IIO_VAL_INT_PLUS_MICRO;
+ }
+ break;
+ default:
+ break;
+ }
+ mutex_unlock(&chip->lock);
+ return ret;
+}
+
+#define ISL29018_LIGHT_CHANNEL { \
+ .type = IIO_LIGHT, \
+ .indexed = 1, \
+ .channel = 0, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED) | \
+ BIT(IIO_CHAN_INFO_CALIBSCALE), \
+}
+
+#define ISL29018_IR_CHANNEL { \
+ .type = IIO_INTENSITY, \
+ .modified = 1, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
+ .channel2 = IIO_MOD_LIGHT_IR, \
+}
+
+#define ISL29018_PROXIMITY_CHANNEL { \
+ .type = IIO_PROXIMITY, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
+}
+
+static const struct iio_chan_spec isl29018_channels[] = {
+ ISL29018_LIGHT_CHANNEL,
+ ISL29018_IR_CHANNEL,
+ ISL29018_PROXIMITY_CHANNEL,
+};
+
+static const struct iio_chan_spec isl29023_channels[] = {
+ ISL29018_LIGHT_CHANNEL,
+ ISL29018_IR_CHANNEL,
+};
+
+static IIO_DEVICE_ATTR(range, S_IRUGO | S_IWUSR, show_range, store_range, 0);
+static IIO_CONST_ATTR(range_available, "1000 4000 16000 64000");
+static IIO_CONST_ATTR(adc_resolution_available, "4 8 12 16");
+static IIO_DEVICE_ATTR(adc_resolution, S_IRUGO | S_IWUSR,
+ show_resolution, store_resolution, 0);
+static IIO_DEVICE_ATTR(proximity_on_chip_ambient_infrared_suppression,
+ S_IRUGO | S_IWUSR,
+ show_prox_infrared_suppression,
+ store_prox_infrared_suppression, 0);
+
+#define ISL29018_DEV_ATTR(name) (&iio_dev_attr_##name.dev_attr.attr)
+#define ISL29018_CONST_ATTR(name) (&iio_const_attr_##name.dev_attr.attr)
+static struct attribute *isl29018_attributes[] = {
+ ISL29018_DEV_ATTR(range),
+ ISL29018_CONST_ATTR(range_available),
+ ISL29018_DEV_ATTR(adc_resolution),
+ ISL29018_CONST_ATTR(adc_resolution_available),
+ ISL29018_DEV_ATTR(proximity_on_chip_ambient_infrared_suppression),
+ NULL
+};
+
+static struct attribute *isl29023_attributes[] = {
+ ISL29018_DEV_ATTR(range),
+ ISL29018_CONST_ATTR(range_available),
+ ISL29018_DEV_ATTR(adc_resolution),
+ ISL29018_CONST_ATTR(adc_resolution_available),
+ NULL
+};
+
+static const struct attribute_group isl29018_group = {
+ .attrs = isl29018_attributes,
+};
+
+static const struct attribute_group isl29023_group = {
+ .attrs = isl29023_attributes,
+};
+
+static int isl29035_detect(struct isl29018_chip *chip)
+{
+ int status;
+ unsigned int id;
+
+ status = regmap_read(chip->regmap, ISL29035_REG_DEVICE_ID, &id);
+ if (status < 0) {
+ dev_err(chip->dev,
+ "Error reading ID register with error %d\n",
+ status);
+ return status;
+ }
+
+ id = (id & ISL29035_DEVICE_ID_MASK) >> ISL29035_DEVICE_ID_SHIFT;
+
+ if (id != ISL29035_DEVICE_ID)
+ return -ENODEV;
+
+ /* clear out brownout bit */
+ return regmap_update_bits(chip->regmap, ISL29035_REG_DEVICE_ID,
+ ISL29035_BOUT_MASK, 0);
+}
+
+enum {
+ isl29018,
+ isl29023,
+ isl29035,
+};
+
+static int isl29018_chip_init(struct isl29018_chip *chip)
+{
+ int status;
+ unsigned int new_adc_bit;
+ unsigned int new_range;
+
+ if (chip->type == isl29035) {
+ status = isl29035_detect(chip);
+ if (status < 0)
+ return status;
+ }
+
+ /* Code added per Intersil Application Note 1534:
+ * When VDD sinks to approximately 1.8V or below, some of
+ * the part's registers may change their state. When VDD
+ * recovers to 2.25V (or greater), the part may thus be in an
+ * unknown mode of operation. The user can return the part to
+ * a known mode of operation either by (a) setting VDD = 0V for
+ * 1 second or more and then powering back up with a slew rate
+ * of 0.5V/ms or greater, or (b) via I2C disable all ALS/PROX
+ * conversions, clear the test registers, and then rewrite all
+ * registers to the desired values.
+ * ...
+ * FOR ISL29011, ISL29018, ISL29021, ISL29023
+ * 1. Write 0x00 to register 0x08 (TEST)
+ * 2. Write 0x00 to register 0x00 (CMD1)
+ * 3. Rewrite all registers to the desired values
+ *
+ * ISL29018 Data Sheet (FN6619.1, Feb 11, 2010) essentially says
+ * the same thing EXCEPT the data sheet asks for a 1ms delay after
+ * writing the CMD1 register.
+ */
+ status = regmap_write(chip->regmap, ISL29018_REG_TEST, 0x0);
+ if (status < 0) {
+ dev_err(chip->dev, "Failed to clear isl29018 TEST reg.(%d)\n",
+ status);
+ return status;
+ }
+
+ /* See Intersil AN1534 comments above.
+ * "Operating Mode" (COMMAND1) register is reprogrammed when
+ * data is read from the device.
+ */
+ status = regmap_write(chip->regmap, ISL29018_REG_ADD_COMMAND1, 0);
+ if (status < 0) {
+ dev_err(chip->dev, "Failed to clear isl29018 CMD1 reg.(%d)\n",
+ status);
+ return status;
+ }
+
+ usleep_range(1000, 2000); /* per data sheet, page 10 */
+
+ /* set defaults */
+ status = isl29018_set_range(chip, chip->range, &new_range);
+ if (status < 0) {
+ dev_err(chip->dev, "Init of isl29018 fails\n");
+ return status;
+ }
+
+ status = isl29018_set_resolution(chip, chip->adc_bit,
+ &new_adc_bit);
+
+ return 0;
+}
+
+static const struct iio_info isl29018_info = {
+ .attrs = &isl29018_group,
+ .driver_module = THIS_MODULE,
+ .read_raw = &isl29018_read_raw,
+ .write_raw = &isl29018_write_raw,
+};
+
+static const struct iio_info isl29023_info = {
+ .attrs = &isl29023_group,
+ .driver_module = THIS_MODULE,
+ .read_raw = &isl29018_read_raw,
+ .write_raw = &isl29018_write_raw,
+};
+
+static bool is_volatile_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case ISL29018_REG_ADD_DATA_LSB:
+ case ISL29018_REG_ADD_DATA_MSB:
+ case ISL29018_REG_ADD_COMMAND1:
+ case ISL29018_REG_TEST:
+ case ISL29035_REG_DEVICE_ID:
+ return true;
+ default:
+ return false;
+ }
+}
+
+/*
+ * isl29018_regmap_config: regmap configuration.
+ * Use RBTREE mechanism for caching.
+ */
+static const struct regmap_config isl29018_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .volatile_reg = is_volatile_reg,
+ .max_register = ISL29018_REG_TEST,
+ .num_reg_defaults_raw = ISL29018_REG_TEST + 1,
+ .cache_type = REGCACHE_RBTREE,
+};
+
+/* isl29035_regmap_config: regmap configuration for ISL29035 */
+static const struct regmap_config isl29035_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .volatile_reg = is_volatile_reg,
+ .max_register = ISL29035_REG_DEVICE_ID,
+ .num_reg_defaults_raw = ISL29035_REG_DEVICE_ID + 1,
+ .cache_type = REGCACHE_RBTREE,
+};
+
+struct chip_info {
+ const struct iio_chan_spec *channels;
+ int num_channels;
+ const struct iio_info *indio_info;
+ const struct regmap_config *regmap_cfg;
+};
+
+static const struct chip_info chip_info_tbl[] = {
+ [isl29018] = {
+ .channels = isl29018_channels,
+ .num_channels = ARRAY_SIZE(isl29018_channels),
+ .indio_info = &isl29018_info,
+ .regmap_cfg = &isl29018_regmap_config,
+ },
+ [isl29023] = {
+ .channels = isl29023_channels,
+ .num_channels = ARRAY_SIZE(isl29023_channels),
+ .indio_info = &isl29023_info,
+ .regmap_cfg = &isl29018_regmap_config,
+ },
+ [isl29035] = {
+ .channels = isl29023_channels,
+ .num_channels = ARRAY_SIZE(isl29023_channels),
+ .indio_info = &isl29023_info,
+ .regmap_cfg = &isl29035_regmap_config,
+ },
+};
+
+static const char *isl29018_match_acpi_device(struct device *dev, int *data)
+{
+ const struct acpi_device_id *id;
+
+ id = acpi_match_device(dev->driver->acpi_match_table, dev);
+
+ if (!id)
+ return NULL;
+
+ *data = (int) id->driver_data;
+
+ return dev_name(dev);
+}
+
+static int isl29018_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct isl29018_chip *chip;
+ struct iio_dev *indio_dev;
+ int err;
+ const char *name = NULL;
+ int dev_id = 0;
+
+ indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*chip));
+ if (indio_dev == NULL) {
+ dev_err(&client->dev, "iio allocation fails\n");
+ return -ENOMEM;
+ }
+ chip = iio_priv(indio_dev);
+
+ i2c_set_clientdata(client, indio_dev);
+ chip->dev = &client->dev;
+
+ if (id) {
+ name = id->name;
+ dev_id = id->driver_data;
+ }
+
+ if (ACPI_HANDLE(&client->dev))
+ name = isl29018_match_acpi_device(&client->dev, &dev_id);
+
+ mutex_init(&chip->lock);
+
+ chip->type = dev_id;
+ chip->lux_scale = 1;
+ chip->lux_uscale = 0;
+ chip->range = 1000;
+ chip->adc_bit = 16;
+ chip->suspended = false;
+
+ chip->regmap = devm_regmap_init_i2c(client,
+ chip_info_tbl[dev_id].regmap_cfg);
+ if (IS_ERR(chip->regmap)) {
+ err = PTR_ERR(chip->regmap);
+ dev_err(chip->dev, "regmap initialization failed: %d\n", err);
+ return err;
+ }
+
+ err = isl29018_chip_init(chip);
+ if (err)
+ return err;
+
+ indio_dev->info = chip_info_tbl[dev_id].indio_info;
+ indio_dev->channels = chip_info_tbl[dev_id].channels;
+ indio_dev->num_channels = chip_info_tbl[dev_id].num_channels;
+ indio_dev->name = name;
+ indio_dev->dev.parent = &client->dev;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ err = devm_iio_device_register(&client->dev, indio_dev);
+ if (err) {
+ dev_err(&client->dev, "iio registration fails\n");
+ return err;
+ }
+
+ return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int isl29018_suspend(struct device *dev)
+{
+ struct isl29018_chip *chip = iio_priv(dev_get_drvdata(dev));
+
+ mutex_lock(&chip->lock);
+
+ /* Since this driver uses only polling commands, we are by default in
+ * auto shutdown (ie, power-down) mode.
+ * So we do not have much to do here.
+ */
+ chip->suspended = true;
+
+ mutex_unlock(&chip->lock);
+ return 0;
+}
+
+static int isl29018_resume(struct device *dev)
+{
+ struct isl29018_chip *chip = iio_priv(dev_get_drvdata(dev));
+ int err;
+
+ mutex_lock(&chip->lock);
+
+ err = isl29018_chip_init(chip);
+ if (!err)
+ chip->suspended = false;
+
+ mutex_unlock(&chip->lock);
+ return err;
+}
+
+static SIMPLE_DEV_PM_OPS(isl29018_pm_ops, isl29018_suspend, isl29018_resume);
+#define ISL29018_PM_OPS (&isl29018_pm_ops)
+#else
+#define ISL29018_PM_OPS NULL
+#endif
+
+static const struct acpi_device_id isl29018_acpi_match[] = {
+ {"ISL29018", isl29018},
+ {"ISL29023", isl29023},
+ {"ISL29035", isl29035},
+ {},
+};
+MODULE_DEVICE_TABLE(acpi, isl29018_acpi_match);
+
+static const struct i2c_device_id isl29018_id[] = {
+ {"isl29018", isl29018},
+ {"isl29023", isl29023},
+ {"isl29035", isl29035},
+ {}
+};
+
+MODULE_DEVICE_TABLE(i2c, isl29018_id);
+
+static const struct of_device_id isl29018_of_match[] = {
+ { .compatible = "isil,isl29018", },
+ { .compatible = "isil,isl29023", },
+ { .compatible = "isil,isl29035", },
+ { },
+};
+MODULE_DEVICE_TABLE(of, isl29018_of_match);
+
+static struct i2c_driver isl29018_driver = {
+ .class = I2C_CLASS_HWMON,
+ .driver = {
+ .name = "isl29018",
+ .acpi_match_table = ACPI_PTR(isl29018_acpi_match),
+ .pm = ISL29018_PM_OPS,
+ .owner = THIS_MODULE,
+ .of_match_table = isl29018_of_match,
+ },
+ .probe = isl29018_probe,
+ .id_table = isl29018_id,
+};
+module_i2c_driver(isl29018_driver);
+
+MODULE_DESCRIPTION("ISL29018 Ambient Light Sensor driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/iio/light/isl29028.c b/drivers/staging/iio/light/isl29028.c
new file mode 100644
index 000000000..e5b2fdc23
--- /dev/null
+++ b/drivers/staging/iio/light/isl29028.c
@@ -0,0 +1,562 @@
+/*
+ * IIO driver for the light sensor ISL29028.
+ * ISL29028 is Concurrent Ambient Light and Proximity Sensor
+ *
+ * Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/err.h>
+#include <linux/mutex.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/regmap.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+
+#define CONVERSION_TIME_MS 100
+
+#define ISL29028_REG_CONFIGURE 0x01
+
+#define CONFIGURE_ALS_IR_MODE_ALS 0
+#define CONFIGURE_ALS_IR_MODE_IR BIT(0)
+#define CONFIGURE_ALS_IR_MODE_MASK BIT(0)
+
+#define CONFIGURE_ALS_RANGE_LOW_LUX 0
+#define CONFIGURE_ALS_RANGE_HIGH_LUX BIT(1)
+#define CONFIGURE_ALS_RANGE_MASK BIT(1)
+
+#define CONFIGURE_ALS_DIS 0
+#define CONFIGURE_ALS_EN BIT(2)
+#define CONFIGURE_ALS_EN_MASK BIT(2)
+
+#define CONFIGURE_PROX_DRIVE BIT(3)
+
+#define CONFIGURE_PROX_SLP_SH 4
+#define CONFIGURE_PROX_SLP_MASK (7 << CONFIGURE_PROX_SLP_SH)
+
+#define CONFIGURE_PROX_EN BIT(7)
+#define CONFIGURE_PROX_EN_MASK BIT(7)
+
+#define ISL29028_REG_INTERRUPT 0x02
+
+#define ISL29028_REG_PROX_DATA 0x08
+#define ISL29028_REG_ALSIR_L 0x09
+#define ISL29028_REG_ALSIR_U 0x0A
+
+#define ISL29028_REG_TEST1_MODE 0x0E
+#define ISL29028_REG_TEST2_MODE 0x0F
+
+#define ISL29028_NUM_REGS (ISL29028_REG_TEST2_MODE + 1)
+
+enum als_ir_mode {
+ MODE_NONE = 0,
+ MODE_ALS,
+ MODE_IR
+};
+
+struct isl29028_chip {
+ struct device *dev;
+ struct mutex lock;
+ struct regmap *regmap;
+
+ unsigned int prox_sampling;
+ bool enable_prox;
+
+ int lux_scale;
+ int als_ir_mode;
+};
+
+static int isl29028_set_proxim_sampling(struct isl29028_chip *chip,
+ unsigned int sampling)
+{
+ static unsigned int prox_period[] = {800, 400, 200, 100, 75, 50, 12, 0};
+ int sel;
+ unsigned int period = DIV_ROUND_UP(1000, sampling);
+
+ for (sel = 0; sel < ARRAY_SIZE(prox_period); ++sel) {
+ if (period >= prox_period[sel])
+ break;
+ }
+ return regmap_update_bits(chip->regmap, ISL29028_REG_CONFIGURE,
+ CONFIGURE_PROX_SLP_MASK, sel << CONFIGURE_PROX_SLP_SH);
+}
+
+static int isl29028_enable_proximity(struct isl29028_chip *chip, bool enable)
+{
+ int ret;
+ int val = 0;
+
+ if (enable)
+ val = CONFIGURE_PROX_EN;
+ ret = regmap_update_bits(chip->regmap, ISL29028_REG_CONFIGURE,
+ CONFIGURE_PROX_EN_MASK, val);
+ if (ret < 0)
+ return ret;
+
+ /* Wait for conversion to be complete for first sample */
+ mdelay(DIV_ROUND_UP(1000, chip->prox_sampling));
+ return 0;
+}
+
+static int isl29028_set_als_scale(struct isl29028_chip *chip, int lux_scale)
+{
+ int val = (lux_scale == 2000) ? CONFIGURE_ALS_RANGE_HIGH_LUX :
+ CONFIGURE_ALS_RANGE_LOW_LUX;
+
+ return regmap_update_bits(chip->regmap, ISL29028_REG_CONFIGURE,
+ CONFIGURE_ALS_RANGE_MASK, val);
+}
+
+static int isl29028_set_als_ir_mode(struct isl29028_chip *chip,
+ enum als_ir_mode mode)
+{
+ int ret = 0;
+
+ switch (mode) {
+ case MODE_ALS:
+ ret = regmap_update_bits(chip->regmap, ISL29028_REG_CONFIGURE,
+ CONFIGURE_ALS_IR_MODE_MASK, CONFIGURE_ALS_IR_MODE_ALS);
+ if (ret < 0)
+ return ret;
+
+ ret = regmap_update_bits(chip->regmap, ISL29028_REG_CONFIGURE,
+ CONFIGURE_ALS_RANGE_MASK, CONFIGURE_ALS_RANGE_HIGH_LUX);
+ break;
+
+ case MODE_IR:
+ ret = regmap_update_bits(chip->regmap, ISL29028_REG_CONFIGURE,
+ CONFIGURE_ALS_IR_MODE_MASK, CONFIGURE_ALS_IR_MODE_IR);
+ break;
+
+ case MODE_NONE:
+ return regmap_update_bits(chip->regmap, ISL29028_REG_CONFIGURE,
+ CONFIGURE_ALS_EN_MASK, CONFIGURE_ALS_DIS);
+ }
+
+ if (ret < 0)
+ return ret;
+
+ /* Enable the ALS/IR */
+ ret = regmap_update_bits(chip->regmap, ISL29028_REG_CONFIGURE,
+ CONFIGURE_ALS_EN_MASK, CONFIGURE_ALS_EN);
+ if (ret < 0)
+ return ret;
+
+ /* Need to wait for conversion time if ALS/IR mode enabled */
+ mdelay(CONVERSION_TIME_MS);
+ return 0;
+}
+
+static int isl29028_read_als_ir(struct isl29028_chip *chip, int *als_ir)
+{
+ unsigned int lsb;
+ unsigned int msb;
+ int ret;
+
+ ret = regmap_read(chip->regmap, ISL29028_REG_ALSIR_L, &lsb);
+ if (ret < 0) {
+ dev_err(chip->dev,
+ "Error in reading register ALSIR_L err %d\n", ret);
+ return ret;
+ }
+
+ ret = regmap_read(chip->regmap, ISL29028_REG_ALSIR_U, &msb);
+ if (ret < 0) {
+ dev_err(chip->dev,
+ "Error in reading register ALSIR_U err %d\n", ret);
+ return ret;
+ }
+
+ *als_ir = ((msb & 0xF) << 8) | (lsb & 0xFF);
+ return 0;
+}
+
+static int isl29028_read_proxim(struct isl29028_chip *chip, int *prox)
+{
+ unsigned int data;
+ int ret;
+
+ ret = regmap_read(chip->regmap, ISL29028_REG_PROX_DATA, &data);
+ if (ret < 0) {
+ dev_err(chip->dev, "Error in reading register %d, error %d\n",
+ ISL29028_REG_PROX_DATA, ret);
+ return ret;
+ }
+ *prox = data;
+ return 0;
+}
+
+static int isl29028_proxim_get(struct isl29028_chip *chip, int *prox_data)
+{
+ int ret;
+
+ if (!chip->enable_prox) {
+ ret = isl29028_enable_proximity(chip, true);
+ if (ret < 0)
+ return ret;
+ chip->enable_prox = true;
+ }
+ return isl29028_read_proxim(chip, prox_data);
+}
+
+static int isl29028_als_get(struct isl29028_chip *chip, int *als_data)
+{
+ int ret;
+ int als_ir_data;
+
+ if (chip->als_ir_mode != MODE_ALS) {
+ ret = isl29028_set_als_ir_mode(chip, MODE_ALS);
+ if (ret < 0) {
+ dev_err(chip->dev,
+ "Error in enabling ALS mode err %d\n", ret);
+ return ret;
+ }
+ chip->als_ir_mode = MODE_ALS;
+ }
+
+ ret = isl29028_read_als_ir(chip, &als_ir_data);
+ if (ret < 0)
+ return ret;
+
+ /*
+ * convert als data count to lux.
+ * if lux_scale = 125, lux = count * 0.031
+ * if lux_scale = 2000, lux = count * 0.49
+ */
+ if (chip->lux_scale == 125)
+ als_ir_data = (als_ir_data * 31) / 1000;
+ else
+ als_ir_data = (als_ir_data * 49) / 100;
+
+ *als_data = als_ir_data;
+ return 0;
+}
+
+static int isl29028_ir_get(struct isl29028_chip *chip, int *ir_data)
+{
+ int ret;
+
+ if (chip->als_ir_mode != MODE_IR) {
+ ret = isl29028_set_als_ir_mode(chip, MODE_IR);
+ if (ret < 0) {
+ dev_err(chip->dev,
+ "Error in enabling IR mode err %d\n", ret);
+ return ret;
+ }
+ chip->als_ir_mode = MODE_IR;
+ }
+ return isl29028_read_als_ir(chip, ir_data);
+}
+
+/* Channel IO */
+static int isl29028_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan, int val, int val2, long mask)
+{
+ struct isl29028_chip *chip = iio_priv(indio_dev);
+ int ret = -EINVAL;
+
+ mutex_lock(&chip->lock);
+ switch (chan->type) {
+ case IIO_PROXIMITY:
+ if (mask != IIO_CHAN_INFO_SAMP_FREQ) {
+ dev_err(chip->dev,
+ "proximity: mask value 0x%08lx not supported\n",
+ mask);
+ break;
+ }
+ if (val < 1 || val > 100) {
+ dev_err(chip->dev,
+ "Samp_freq %d is not in range[1:100]\n", val);
+ break;
+ }
+ ret = isl29028_set_proxim_sampling(chip, val);
+ if (ret < 0) {
+ dev_err(chip->dev,
+ "Setting proximity samp_freq fail, err %d\n",
+ ret);
+ break;
+ }
+ chip->prox_sampling = val;
+ break;
+
+ case IIO_LIGHT:
+ if (mask != IIO_CHAN_INFO_SCALE) {
+ dev_err(chip->dev,
+ "light: mask value 0x%08lx not supported\n",
+ mask);
+ break;
+ }
+ if ((val != 125) && (val != 2000)) {
+ dev_err(chip->dev,
+ "lux scale %d is invalid [125, 2000]\n", val);
+ break;
+ }
+ ret = isl29028_set_als_scale(chip, val);
+ if (ret < 0) {
+ dev_err(chip->dev,
+ "Setting lux scale fail with error %d\n", ret);
+ break;
+ }
+ chip->lux_scale = val;
+ break;
+
+ default:
+ dev_err(chip->dev, "Unsupported channel type\n");
+ break;
+ }
+ mutex_unlock(&chip->lock);
+ return ret;
+}
+
+static int isl29028_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan, int *val, int *val2, long mask)
+{
+ struct isl29028_chip *chip = iio_priv(indio_dev);
+ int ret = -EINVAL;
+
+ mutex_lock(&chip->lock);
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ case IIO_CHAN_INFO_PROCESSED:
+ switch (chan->type) {
+ case IIO_LIGHT:
+ ret = isl29028_als_get(chip, val);
+ break;
+ case IIO_INTENSITY:
+ ret = isl29028_ir_get(chip, val);
+ break;
+ case IIO_PROXIMITY:
+ ret = isl29028_proxim_get(chip, val);
+ break;
+ default:
+ break;
+ }
+ if (ret < 0)
+ break;
+ ret = IIO_VAL_INT;
+ break;
+
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ if (chan->type != IIO_PROXIMITY)
+ break;
+ *val = chip->prox_sampling;
+ ret = IIO_VAL_INT;
+ break;
+
+ case IIO_CHAN_INFO_SCALE:
+ if (chan->type != IIO_LIGHT)
+ break;
+ *val = chip->lux_scale;
+ ret = IIO_VAL_INT;
+ break;
+
+ default:
+ dev_err(chip->dev, "mask value 0x%08lx not supported\n", mask);
+ break;
+ }
+ mutex_unlock(&chip->lock);
+ return ret;
+}
+
+static IIO_CONST_ATTR(in_proximity_sampling_frequency_available,
+ "1, 3, 5, 10, 13, 20, 83, 100");
+static IIO_CONST_ATTR(in_illuminance_scale_available, "125, 2000");
+
+#define ISL29028_DEV_ATTR(name) (&iio_dev_attr_##name.dev_attr.attr)
+#define ISL29028_CONST_ATTR(name) (&iio_const_attr_##name.dev_attr.attr)
+static struct attribute *isl29028_attributes[] = {
+ ISL29028_CONST_ATTR(in_proximity_sampling_frequency_available),
+ ISL29028_CONST_ATTR(in_illuminance_scale_available),
+ NULL,
+};
+
+static const struct attribute_group isl29108_group = {
+ .attrs = isl29028_attributes,
+};
+
+static const struct iio_chan_spec isl29028_channels[] = {
+ {
+ .type = IIO_LIGHT,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED) |
+ BIT(IIO_CHAN_INFO_SCALE),
+ }, {
+ .type = IIO_INTENSITY,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
+ }, {
+ .type = IIO_PROXIMITY,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+ BIT(IIO_CHAN_INFO_SAMP_FREQ),
+ }
+};
+
+static const struct iio_info isl29028_info = {
+ .attrs = &isl29108_group,
+ .driver_module = THIS_MODULE,
+ .read_raw = &isl29028_read_raw,
+ .write_raw = &isl29028_write_raw,
+};
+
+static int isl29028_chip_init(struct isl29028_chip *chip)
+{
+ int ret;
+
+ chip->enable_prox = false;
+ chip->prox_sampling = 20;
+ chip->lux_scale = 2000;
+ chip->als_ir_mode = MODE_NONE;
+
+ ret = regmap_write(chip->regmap, ISL29028_REG_TEST1_MODE, 0x0);
+ if (ret < 0) {
+ dev_err(chip->dev, "%s(): write to reg %d failed, err = %d\n",
+ __func__, ISL29028_REG_TEST1_MODE, ret);
+ return ret;
+ }
+ ret = regmap_write(chip->regmap, ISL29028_REG_TEST2_MODE, 0x0);
+ if (ret < 0) {
+ dev_err(chip->dev, "%s(): write to reg %d failed, err = %d\n",
+ __func__, ISL29028_REG_TEST2_MODE, ret);
+ return ret;
+ }
+
+ ret = regmap_write(chip->regmap, ISL29028_REG_CONFIGURE, 0x0);
+ if (ret < 0) {
+ dev_err(chip->dev, "%s(): write to reg %d failed, err = %d\n",
+ __func__, ISL29028_REG_CONFIGURE, ret);
+ return ret;
+ }
+
+ ret = isl29028_set_proxim_sampling(chip, chip->prox_sampling);
+ if (ret < 0) {
+ dev_err(chip->dev, "setting the proximity, err = %d\n",
+ ret);
+ return ret;
+ }
+
+ ret = isl29028_set_als_scale(chip, chip->lux_scale);
+ if (ret < 0)
+ dev_err(chip->dev,
+ "setting als scale failed, err = %d\n", ret);
+ return ret;
+}
+
+static bool is_volatile_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case ISL29028_REG_INTERRUPT:
+ case ISL29028_REG_PROX_DATA:
+ case ISL29028_REG_ALSIR_L:
+ case ISL29028_REG_ALSIR_U:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static const struct regmap_config isl29028_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .volatile_reg = is_volatile_reg,
+ .max_register = ISL29028_NUM_REGS - 1,
+ .num_reg_defaults_raw = ISL29028_NUM_REGS,
+ .cache_type = REGCACHE_RBTREE,
+};
+
+static int isl29028_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct isl29028_chip *chip;
+ struct iio_dev *indio_dev;
+ int ret;
+
+ indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*chip));
+ if (!indio_dev) {
+ dev_err(&client->dev, "iio allocation fails\n");
+ return -ENOMEM;
+ }
+
+ chip = iio_priv(indio_dev);
+
+ i2c_set_clientdata(client, indio_dev);
+ chip->dev = &client->dev;
+ mutex_init(&chip->lock);
+
+ chip->regmap = devm_regmap_init_i2c(client, &isl29028_regmap_config);
+ if (IS_ERR(chip->regmap)) {
+ ret = PTR_ERR(chip->regmap);
+ dev_err(chip->dev, "regmap initialization failed: %d\n", ret);
+ return ret;
+ }
+
+ ret = isl29028_chip_init(chip);
+ if (ret < 0) {
+ dev_err(chip->dev, "chip initialization failed: %d\n", ret);
+ return ret;
+ }
+
+ indio_dev->info = &isl29028_info;
+ indio_dev->channels = isl29028_channels;
+ indio_dev->num_channels = ARRAY_SIZE(isl29028_channels);
+ indio_dev->name = id->name;
+ indio_dev->dev.parent = &client->dev;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ ret = iio_device_register(indio_dev);
+ if (ret < 0) {
+ dev_err(chip->dev, "iio registration fails with error %d\n",
+ ret);
+ return ret;
+ }
+ return 0;
+}
+
+static int isl29028_remove(struct i2c_client *client)
+{
+ struct iio_dev *indio_dev = i2c_get_clientdata(client);
+
+ iio_device_unregister(indio_dev);
+ return 0;
+}
+
+static const struct i2c_device_id isl29028_id[] = {
+ {"isl29028", 0},
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, isl29028_id);
+
+static const struct of_device_id isl29028_of_match[] = {
+ { .compatible = "isl,isl29028", }, /* for backward compat., don't use */
+ { .compatible = "isil,isl29028", },
+ { },
+};
+MODULE_DEVICE_TABLE(of, isl29028_of_match);
+
+static struct i2c_driver isl29028_driver = {
+ .class = I2C_CLASS_HWMON,
+ .driver = {
+ .name = "isl29028",
+ .owner = THIS_MODULE,
+ .of_match_table = isl29028_of_match,
+ },
+ .probe = isl29028_probe,
+ .remove = isl29028_remove,
+ .id_table = isl29028_id,
+};
+
+module_i2c_driver(isl29028_driver);
+
+MODULE_DESCRIPTION("ISL29028 Ambient Light and Proximity Sensor driver");
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Laxman Dewangan <ldewangan@nvidia.com>");
diff --git a/drivers/staging/iio/light/tsl2583.c b/drivers/staging/iio/light/tsl2583.c
new file mode 100644
index 000000000..b5e1b8b0a
--- /dev/null
+++ b/drivers/staging/iio/light/tsl2583.c
@@ -0,0 +1,949 @@
+/*
+ * Device driver for monitoring ambient light intensity (lux)
+ * within the TAOS tsl258x family of devices (tsl2580, tsl2581).
+ *
+ * Copyright (c) 2011, TAOS Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include <linux/kernel.h>
+#include <linux/i2c.h>
+#include <linux/errno.h>
+#include <linux/delay.h>
+#include <linux/string.h>
+#include <linux/mutex.h>
+#include <linux/unistd.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/iio/iio.h>
+
+#define TSL258X_MAX_DEVICE_REGS 32
+
+/* Triton register offsets */
+#define TSL258X_REG_MAX 8
+
+/* Device Registers and Masks */
+#define TSL258X_CNTRL 0x00
+#define TSL258X_ALS_TIME 0X01
+#define TSL258X_INTERRUPT 0x02
+#define TSL258X_GAIN 0x07
+#define TSL258X_REVID 0x11
+#define TSL258X_CHIPID 0x12
+#define TSL258X_ALS_CHAN0LO 0x14
+#define TSL258X_ALS_CHAN0HI 0x15
+#define TSL258X_ALS_CHAN1LO 0x16
+#define TSL258X_ALS_CHAN1HI 0x17
+#define TSL258X_TMR_LO 0x18
+#define TSL258X_TMR_HI 0x19
+
+/* tsl2583 cmd reg masks */
+#define TSL258X_CMD_REG 0x80
+#define TSL258X_CMD_SPL_FN 0x60
+#define TSL258X_CMD_ALS_INT_CLR 0X01
+
+/* tsl2583 cntrl reg masks */
+#define TSL258X_CNTL_ADC_ENBL 0x02
+#define TSL258X_CNTL_PWR_ON 0x01
+
+/* tsl2583 status reg masks */
+#define TSL258X_STA_ADC_VALID 0x01
+#define TSL258X_STA_ADC_INTR 0x10
+
+/* Lux calculation constants */
+#define TSL258X_LUX_CALC_OVER_FLOW 65535
+
+enum {
+ TSL258X_CHIP_UNKNOWN = 0,
+ TSL258X_CHIP_WORKING = 1,
+ TSL258X_CHIP_SUSPENDED = 2
+};
+
+/* Per-device data */
+struct taos_als_info {
+ u16 als_ch0;
+ u16 als_ch1;
+ u16 lux;
+};
+
+struct taos_settings {
+ int als_time;
+ int als_gain;
+ int als_gain_trim;
+ int als_cal_target;
+};
+
+struct tsl2583_chip {
+ struct mutex als_mutex;
+ struct i2c_client *client;
+ struct taos_als_info als_cur_info;
+ struct taos_settings taos_settings;
+ int als_time_scale;
+ int als_saturation;
+ int taos_chip_status;
+ u8 taos_config[8];
+};
+
+/*
+ * Initial values for device - this values can/will be changed by driver.
+ * and applications as needed.
+ * These values are dynamic.
+ */
+static const u8 taos_config[8] = {
+ 0x00, 0xee, 0x00, 0x03, 0x00, 0xFF, 0xFF, 0x00
+}; /* cntrl atime intC Athl0 Athl1 Athh0 Athh1 gain */
+
+struct taos_lux {
+ unsigned int ratio;
+ unsigned int ch0;
+ unsigned int ch1;
+};
+
+/* This structure is intentionally large to accommodate updates via sysfs. */
+/* Sized to 11 = max 10 segments + 1 termination segment */
+/* Assumption is one and only one type of glass used */
+static struct taos_lux taos_device_lux[11] = {
+ { 9830, 8520, 15729 },
+ { 12452, 10807, 23344 },
+ { 14746, 6383, 11705 },
+ { 17695, 4063, 6554 },
+};
+
+struct gainadj {
+ s16 ch0;
+ s16 ch1;
+};
+
+/* Index = (0 - 3) Used to validate the gain selection index */
+static const struct gainadj gainadj[] = {
+ { 1, 1 },
+ { 8, 8 },
+ { 16, 16 },
+ { 107, 115 }
+};
+
+/*
+ * Provides initial operational parameter defaults.
+ * These defaults may be changed through the device's sysfs files.
+ */
+static void taos_defaults(struct tsl2583_chip *chip)
+{
+ /* Operational parameters */
+ chip->taos_settings.als_time = 100;
+ /* must be a multiple of 50mS */
+ chip->taos_settings.als_gain = 0;
+ /* this is actually an index into the gain table */
+ /* assume clear glass as default */
+ chip->taos_settings.als_gain_trim = 1000;
+ /* default gain trim to account for aperture effects */
+ chip->taos_settings.als_cal_target = 130;
+ /* Known external ALS reading used for calibration */
+}
+
+/*
+ * Read a number of bytes starting at register (reg) location.
+ * Return 0, or i2c_smbus_write_byte ERROR code.
+ */
+static int
+taos_i2c_read(struct i2c_client *client, u8 reg, u8 *val, unsigned int len)
+{
+ int i, ret;
+
+ for (i = 0; i < len; i++) {
+ /* select register to write */
+ ret = i2c_smbus_write_byte(client, (TSL258X_CMD_REG | reg));
+ if (ret < 0) {
+ dev_err(&client->dev,
+ "taos_i2c_read failed to write register %x\n",
+ reg);
+ return ret;
+ }
+ /* read the data */
+ *val = i2c_smbus_read_byte(client);
+ val++;
+ reg++;
+ }
+ return 0;
+}
+
+/*
+ * Reads and calculates current lux value.
+ * The raw ch0 and ch1 values of the ambient light sensed in the last
+ * integration cycle are read from the device.
+ * Time scale factor array values are adjusted based on the integration time.
+ * The raw values are multiplied by a scale factor, and device gain is obtained
+ * using gain index. Limit checks are done next, then the ratio of a multiple
+ * of ch1 value, to the ch0 value, is calculated. The array taos_device_lux[]
+ * declared above is then scanned to find the first ratio value that is just
+ * above the ratio we just calculated. The ch0 and ch1 multiplier constants in
+ * the array are then used along with the time scale factor array values, to
+ * calculate the lux.
+ */
+static int taos_get_lux(struct iio_dev *indio_dev)
+{
+ u16 ch0, ch1; /* separated ch0/ch1 data from device */
+ u32 lux; /* raw lux calculated from device data */
+ u64 lux64;
+ u32 ratio;
+ u8 buf[5];
+ struct taos_lux *p;
+ struct tsl2583_chip *chip = iio_priv(indio_dev);
+ int i, ret;
+ u32 ch0lux = 0;
+ u32 ch1lux = 0;
+
+ if (mutex_trylock(&chip->als_mutex) == 0) {
+ dev_info(&chip->client->dev, "taos_get_lux device is busy\n");
+ return chip->als_cur_info.lux; /* busy, so return LAST VALUE */
+ }
+
+ if (chip->taos_chip_status != TSL258X_CHIP_WORKING) {
+ /* device is not enabled */
+ dev_err(&chip->client->dev, "taos_get_lux device is not enabled\n");
+ ret = -EBUSY;
+ goto out_unlock;
+ }
+
+ ret = taos_i2c_read(chip->client, (TSL258X_CMD_REG), &buf[0], 1);
+ if (ret < 0) {
+ dev_err(&chip->client->dev, "taos_get_lux failed to read CMD_REG\n");
+ goto out_unlock;
+ }
+ /* is data new & valid */
+ if (!(buf[0] & TSL258X_STA_ADC_INTR)) {
+ dev_err(&chip->client->dev, "taos_get_lux data not valid\n");
+ ret = chip->als_cur_info.lux; /* return LAST VALUE */
+ goto out_unlock;
+ }
+
+ for (i = 0; i < 4; i++) {
+ int reg = TSL258X_CMD_REG | (TSL258X_ALS_CHAN0LO + i);
+
+ ret = taos_i2c_read(chip->client, reg, &buf[i], 1);
+ if (ret < 0) {
+ dev_err(&chip->client->dev,
+ "taos_get_lux failed to read register %x\n",
+ reg);
+ goto out_unlock;
+ }
+ }
+
+ /* clear status, really interrupt status (interrupts are off), but
+ * we use the bit anyway - don't forget 0x80 - this is a command*/
+ ret = i2c_smbus_write_byte(chip->client,
+ (TSL258X_CMD_REG | TSL258X_CMD_SPL_FN |
+ TSL258X_CMD_ALS_INT_CLR));
+
+ if (ret < 0) {
+ dev_err(&chip->client->dev,
+ "taos_i2c_write_command failed in taos_get_lux, err = %d\n",
+ ret);
+ goto out_unlock; /* have no data, so return failure */
+ }
+
+ /* extract ALS/lux data */
+ ch0 = le16_to_cpup((const __le16 *)&buf[0]);
+ ch1 = le16_to_cpup((const __le16 *)&buf[2]);
+
+ chip->als_cur_info.als_ch0 = ch0;
+ chip->als_cur_info.als_ch1 = ch1;
+
+ if ((ch0 >= chip->als_saturation) || (ch1 >= chip->als_saturation))
+ goto return_max;
+
+ if (ch0 == 0) {
+ /* have no data, so return LAST VALUE */
+ ret = chip->als_cur_info.lux = 0;
+ goto out_unlock;
+ }
+ /* calculate ratio */
+ ratio = (ch1 << 15) / ch0;
+ /* convert to unscaled lux using the pointer to the table */
+ for (p = (struct taos_lux *) taos_device_lux;
+ p->ratio != 0 && p->ratio < ratio; p++)
+ ;
+
+ if (p->ratio == 0) {
+ lux = 0;
+ } else {
+ ch0lux = ((ch0 * p->ch0) +
+ (gainadj[chip->taos_settings.als_gain].ch0 >> 1))
+ / gainadj[chip->taos_settings.als_gain].ch0;
+ ch1lux = ((ch1 * p->ch1) +
+ (gainadj[chip->taos_settings.als_gain].ch1 >> 1))
+ / gainadj[chip->taos_settings.als_gain].ch1;
+ lux = ch0lux - ch1lux;
+ }
+
+ /* note: lux is 31 bit max at this point */
+ if (ch1lux > ch0lux) {
+ dev_dbg(&chip->client->dev, "No Data - Return last value\n");
+ ret = chip->als_cur_info.lux = 0;
+ goto out_unlock;
+ }
+
+ /* adjust for active time scale */
+ if (chip->als_time_scale == 0)
+ lux = 0;
+ else
+ lux = (lux + (chip->als_time_scale >> 1)) /
+ chip->als_time_scale;
+
+ /* Adjust for active gain scale.
+ * The taos_device_lux tables above have a factor of 8192 built in,
+ * so we need to shift right.
+ * User-specified gain provides a multiplier.
+ * Apply user-specified gain before shifting right to retain precision.
+ * Use 64 bits to avoid overflow on multiplication.
+ * Then go back to 32 bits before division to avoid using div_u64().
+ */
+ lux64 = lux;
+ lux64 = lux64 * chip->taos_settings.als_gain_trim;
+ lux64 >>= 13;
+ lux = lux64;
+ lux = (lux + 500) / 1000;
+ if (lux > TSL258X_LUX_CALC_OVER_FLOW) { /* check for overflow */
+return_max:
+ lux = TSL258X_LUX_CALC_OVER_FLOW;
+ }
+
+ /* Update the structure with the latest VALID lux. */
+ chip->als_cur_info.lux = lux;
+ ret = lux;
+
+out_unlock:
+ mutex_unlock(&chip->als_mutex);
+ return ret;
+}
+
+/*
+ * Obtain single reading and calculate the als_gain_trim (later used
+ * to derive actual lux).
+ * Return updated gain_trim value.
+ */
+static int taos_als_calibrate(struct iio_dev *indio_dev)
+{
+ struct tsl2583_chip *chip = iio_priv(indio_dev);
+ u8 reg_val;
+ unsigned int gain_trim_val;
+ int ret;
+ int lux_val;
+
+ ret = i2c_smbus_write_byte(chip->client,
+ (TSL258X_CMD_REG | TSL258X_CNTRL));
+ if (ret < 0) {
+ dev_err(&chip->client->dev,
+ "taos_als_calibrate failed to reach the CNTRL register, ret=%d\n",
+ ret);
+ return ret;
+ }
+
+ reg_val = i2c_smbus_read_byte(chip->client);
+ if ((reg_val & (TSL258X_CNTL_ADC_ENBL | TSL258X_CNTL_PWR_ON))
+ != (TSL258X_CNTL_ADC_ENBL | TSL258X_CNTL_PWR_ON)) {
+ dev_err(&chip->client->dev,
+ "taos_als_calibrate failed: device not powered on with ADC enabled\n");
+ return -1;
+ }
+
+ ret = i2c_smbus_write_byte(chip->client,
+ (TSL258X_CMD_REG | TSL258X_CNTRL));
+ if (ret < 0) {
+ dev_err(&chip->client->dev,
+ "taos_als_calibrate failed to reach the STATUS register, ret=%d\n",
+ ret);
+ return ret;
+ }
+ reg_val = i2c_smbus_read_byte(chip->client);
+
+ if ((reg_val & TSL258X_STA_ADC_VALID) != TSL258X_STA_ADC_VALID) {
+ dev_err(&chip->client->dev,
+ "taos_als_calibrate failed: STATUS - ADC not valid.\n");
+ return -ENODATA;
+ }
+ lux_val = taos_get_lux(indio_dev);
+ if (lux_val < 0) {
+ dev_err(&chip->client->dev, "taos_als_calibrate failed to get lux\n");
+ return lux_val;
+ }
+ gain_trim_val = (unsigned int) (((chip->taos_settings.als_cal_target)
+ * chip->taos_settings.als_gain_trim) / lux_val);
+
+ if ((gain_trim_val < 250) || (gain_trim_val > 4000)) {
+ dev_err(&chip->client->dev,
+ "taos_als_calibrate failed: trim_val of %d is out of range\n",
+ gain_trim_val);
+ return -ENODATA;
+ }
+ chip->taos_settings.als_gain_trim = (int) gain_trim_val;
+
+ return (int) gain_trim_val;
+}
+
+/*
+ * Turn the device on.
+ * Configuration must be set before calling this function.
+ */
+static int taos_chip_on(struct iio_dev *indio_dev)
+{
+ int i;
+ int ret;
+ u8 *uP;
+ u8 utmp;
+ int als_count;
+ int als_time;
+ struct tsl2583_chip *chip = iio_priv(indio_dev);
+
+ /* and make sure we're not already on */
+ if (chip->taos_chip_status == TSL258X_CHIP_WORKING) {
+ /* if forcing a register update - turn off, then on */
+ dev_info(&chip->client->dev, "device is already enabled\n");
+ return -EINVAL;
+ }
+
+ /* determine als integration register */
+ als_count = (chip->taos_settings.als_time * 100 + 135) / 270;
+ if (als_count == 0)
+ als_count = 1; /* ensure at least one cycle */
+
+ /* convert back to time (encompasses overrides) */
+ als_time = (als_count * 27 + 5) / 10;
+ chip->taos_config[TSL258X_ALS_TIME] = 256 - als_count;
+
+ /* Set the gain based on taos_settings struct */
+ chip->taos_config[TSL258X_GAIN] = chip->taos_settings.als_gain;
+
+ /* set chip struct re scaling and saturation */
+ chip->als_saturation = als_count * 922; /* 90% of full scale */
+ chip->als_time_scale = (als_time + 25) / 50;
+
+ /* TSL258x Specific power-on / adc enable sequence
+ * Power on the device 1st. */
+ utmp = TSL258X_CNTL_PWR_ON;
+ ret = i2c_smbus_write_byte_data(chip->client,
+ TSL258X_CMD_REG | TSL258X_CNTRL, utmp);
+ if (ret < 0) {
+ dev_err(&chip->client->dev, "taos_chip_on failed on CNTRL reg.\n");
+ return ret;
+ }
+
+ /* Use the following shadow copy for our delay before enabling ADC.
+ * Write all the registers. */
+ for (i = 0, uP = chip->taos_config; i < TSL258X_REG_MAX; i++) {
+ ret = i2c_smbus_write_byte_data(chip->client,
+ TSL258X_CMD_REG + i,
+ *uP++);
+ if (ret < 0) {
+ dev_err(&chip->client->dev,
+ "taos_chip_on failed on reg %d.\n", i);
+ return ret;
+ }
+ }
+
+ usleep_range(3000, 3500);
+ /* NOW enable the ADC
+ * initialize the desired mode of operation */
+ utmp = TSL258X_CNTL_PWR_ON | TSL258X_CNTL_ADC_ENBL;
+ ret = i2c_smbus_write_byte_data(chip->client,
+ TSL258X_CMD_REG | TSL258X_CNTRL,
+ utmp);
+ if (ret < 0) {
+ dev_err(&chip->client->dev, "taos_chip_on failed on 2nd CTRL reg.\n");
+ return ret;
+ }
+ chip->taos_chip_status = TSL258X_CHIP_WORKING;
+
+ return ret;
+}
+
+static int taos_chip_off(struct iio_dev *indio_dev)
+{
+ struct tsl2583_chip *chip = iio_priv(indio_dev);
+
+ /* turn device off */
+ chip->taos_chip_status = TSL258X_CHIP_SUSPENDED;
+ return i2c_smbus_write_byte_data(chip->client,
+ TSL258X_CMD_REG | TSL258X_CNTRL,
+ 0x00);
+}
+
+/* Sysfs Interface Functions */
+
+static ssize_t taos_power_state_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct tsl2583_chip *chip = iio_priv(indio_dev);
+
+ return sprintf(buf, "%d\n", chip->taos_chip_status);
+}
+
+static ssize_t taos_power_state_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t len)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ int value;
+
+ if (kstrtoint(buf, 0, &value))
+ return -EINVAL;
+
+ if (value == 0)
+ taos_chip_off(indio_dev);
+ else
+ taos_chip_on(indio_dev);
+
+ return len;
+}
+
+static ssize_t taos_gain_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct tsl2583_chip *chip = iio_priv(indio_dev);
+ char gain[4] = {0};
+
+ switch (chip->taos_settings.als_gain) {
+ case 0:
+ strcpy(gain, "001");
+ break;
+ case 1:
+ strcpy(gain, "008");
+ break;
+ case 2:
+ strcpy(gain, "016");
+ break;
+ case 3:
+ strcpy(gain, "111");
+ break;
+ }
+
+ return sprintf(buf, "%s\n", gain);
+}
+
+static ssize_t taos_gain_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t len)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct tsl2583_chip *chip = iio_priv(indio_dev);
+ int value;
+
+ if (kstrtoint(buf, 0, &value))
+ return -EINVAL;
+
+ switch (value) {
+ case 1:
+ chip->taos_settings.als_gain = 0;
+ break;
+ case 8:
+ chip->taos_settings.als_gain = 1;
+ break;
+ case 16:
+ chip->taos_settings.als_gain = 2;
+ break;
+ case 111:
+ chip->taos_settings.als_gain = 3;
+ break;
+ default:
+ dev_err(dev, "Invalid Gain Index (must be 1,8,16,111)\n");
+ return -1;
+ }
+
+ return len;
+}
+
+static ssize_t taos_gain_available_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return sprintf(buf, "%s\n", "1 8 16 111");
+}
+
+static ssize_t taos_als_time_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct tsl2583_chip *chip = iio_priv(indio_dev);
+
+ return sprintf(buf, "%d\n", chip->taos_settings.als_time);
+}
+
+static ssize_t taos_als_time_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t len)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct tsl2583_chip *chip = iio_priv(indio_dev);
+ int value;
+
+ if (kstrtoint(buf, 0, &value))
+ return -EINVAL;
+
+ if ((value < 50) || (value > 650))
+ return -EINVAL;
+
+ if (value % 50)
+ return -EINVAL;
+
+ chip->taos_settings.als_time = value;
+
+ return len;
+}
+
+static ssize_t taos_als_time_available_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return sprintf(buf, "%s\n",
+ "50 100 150 200 250 300 350 400 450 500 550 600 650");
+}
+
+static ssize_t taos_als_trim_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct tsl2583_chip *chip = iio_priv(indio_dev);
+
+ return sprintf(buf, "%d\n", chip->taos_settings.als_gain_trim);
+}
+
+static ssize_t taos_als_trim_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t len)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct tsl2583_chip *chip = iio_priv(indio_dev);
+ int value;
+
+ if (kstrtoint(buf, 0, &value))
+ return -EINVAL;
+
+ if (value)
+ chip->taos_settings.als_gain_trim = value;
+
+ return len;
+}
+
+static ssize_t taos_als_cal_target_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct tsl2583_chip *chip = iio_priv(indio_dev);
+
+ return sprintf(buf, "%d\n", chip->taos_settings.als_cal_target);
+}
+
+static ssize_t taos_als_cal_target_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t len)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct tsl2583_chip *chip = iio_priv(indio_dev);
+ int value;
+
+ if (kstrtoint(buf, 0, &value))
+ return -EINVAL;
+
+ if (value)
+ chip->taos_settings.als_cal_target = value;
+
+ return len;
+}
+
+static ssize_t taos_lux_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ int ret;
+
+ ret = taos_get_lux(dev_to_iio_dev(dev));
+ if (ret < 0)
+ return ret;
+
+ return sprintf(buf, "%d\n", ret);
+}
+
+static ssize_t taos_do_calibrate(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t len)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ int value;
+
+ if (kstrtoint(buf, 0, &value))
+ return -EINVAL;
+
+ if (value == 1)
+ taos_als_calibrate(indio_dev);
+
+ return len;
+}
+
+static ssize_t taos_luxtable_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int i;
+ int offset = 0;
+
+ for (i = 0; i < ARRAY_SIZE(taos_device_lux); i++) {
+ offset += sprintf(buf + offset, "%u,%u,%u,",
+ taos_device_lux[i].ratio,
+ taos_device_lux[i].ch0,
+ taos_device_lux[i].ch1);
+ if (taos_device_lux[i].ratio == 0) {
+ /* We just printed the first "0" entry.
+ * Now get rid of the extra "," and break. */
+ offset--;
+ break;
+ }
+ }
+
+ offset += sprintf(buf + offset, "\n");
+ return offset;
+}
+
+static ssize_t taos_luxtable_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t len)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct tsl2583_chip *chip = iio_priv(indio_dev);
+ int value[ARRAY_SIZE(taos_device_lux)*3 + 1];
+ int n;
+
+ get_options(buf, ARRAY_SIZE(value), value);
+
+ /* We now have an array of ints starting at value[1], and
+ * enumerated by value[0].
+ * We expect each group of three ints is one table entry,
+ * and the last table entry is all 0.
+ */
+ n = value[0];
+ if ((n % 3) || n < 6 || n > ((ARRAY_SIZE(taos_device_lux) - 1) * 3)) {
+ dev_info(dev, "LUX TABLE INPUT ERROR 1 Value[0]=%d\n", n);
+ return -EINVAL;
+ }
+ if ((value[(n - 2)] | value[(n - 1)] | value[n]) != 0) {
+ dev_info(dev, "LUX TABLE INPUT ERROR 2 Value[0]=%d\n", n);
+ return -EINVAL;
+ }
+
+ if (chip->taos_chip_status == TSL258X_CHIP_WORKING)
+ taos_chip_off(indio_dev);
+
+ /* Zero out the table */
+ memset(taos_device_lux, 0, sizeof(taos_device_lux));
+ memcpy(taos_device_lux, &value[1], (value[0] * 4));
+
+ taos_chip_on(indio_dev);
+
+ return len;
+}
+
+static DEVICE_ATTR(power_state, S_IRUGO | S_IWUSR,
+ taos_power_state_show, taos_power_state_store);
+
+static DEVICE_ATTR(illuminance0_calibscale, S_IRUGO | S_IWUSR,
+ taos_gain_show, taos_gain_store);
+static DEVICE_ATTR(illuminance0_calibscale_available, S_IRUGO,
+ taos_gain_available_show, NULL);
+
+static DEVICE_ATTR(illuminance0_integration_time, S_IRUGO | S_IWUSR,
+ taos_als_time_show, taos_als_time_store);
+static DEVICE_ATTR(illuminance0_integration_time_available, S_IRUGO,
+ taos_als_time_available_show, NULL);
+
+static DEVICE_ATTR(illuminance0_calibbias, S_IRUGO | S_IWUSR,
+ taos_als_trim_show, taos_als_trim_store);
+
+static DEVICE_ATTR(illuminance0_input_target, S_IRUGO | S_IWUSR,
+ taos_als_cal_target_show, taos_als_cal_target_store);
+
+static DEVICE_ATTR(illuminance0_input, S_IRUGO, taos_lux_show, NULL);
+static DEVICE_ATTR(illuminance0_calibrate, S_IWUSR, NULL, taos_do_calibrate);
+static DEVICE_ATTR(illuminance0_lux_table, S_IRUGO | S_IWUSR,
+ taos_luxtable_show, taos_luxtable_store);
+
+static struct attribute *sysfs_attrs_ctrl[] = {
+ &dev_attr_power_state.attr,
+ &dev_attr_illuminance0_calibscale.attr, /* Gain */
+ &dev_attr_illuminance0_calibscale_available.attr,
+ &dev_attr_illuminance0_integration_time.attr, /* I time*/
+ &dev_attr_illuminance0_integration_time_available.attr,
+ &dev_attr_illuminance0_calibbias.attr, /* trim */
+ &dev_attr_illuminance0_input_target.attr,
+ &dev_attr_illuminance0_input.attr,
+ &dev_attr_illuminance0_calibrate.attr,
+ &dev_attr_illuminance0_lux_table.attr,
+ NULL
+};
+
+static struct attribute_group tsl2583_attribute_group = {
+ .attrs = sysfs_attrs_ctrl,
+};
+
+/* Use the default register values to identify the Taos device */
+static int taos_tsl258x_device(unsigned char *bufp)
+{
+ return ((bufp[TSL258X_CHIPID] & 0xf0) == 0x90);
+}
+
+static const struct iio_info tsl2583_info = {
+ .attrs = &tsl2583_attribute_group,
+ .driver_module = THIS_MODULE,
+};
+
+/*
+ * Client probe function - When a valid device is found, the driver's device
+ * data structure is updated, and initialization completes successfully.
+ */
+static int taos_probe(struct i2c_client *clientp,
+ const struct i2c_device_id *idp)
+{
+ int i, ret;
+ unsigned char buf[TSL258X_MAX_DEVICE_REGS];
+ struct tsl2583_chip *chip;
+ struct iio_dev *indio_dev;
+
+ if (!i2c_check_functionality(clientp->adapter,
+ I2C_FUNC_SMBUS_BYTE_DATA)) {
+ dev_err(&clientp->dev, "taos_probe() - i2c smbus byte data func unsupported\n");
+ return -EOPNOTSUPP;
+ }
+
+ indio_dev = devm_iio_device_alloc(&clientp->dev, sizeof(*chip));
+ if (!indio_dev)
+ return -ENOMEM;
+ chip = iio_priv(indio_dev);
+ chip->client = clientp;
+ i2c_set_clientdata(clientp, indio_dev);
+
+ mutex_init(&chip->als_mutex);
+ chip->taos_chip_status = TSL258X_CHIP_UNKNOWN;
+ memcpy(chip->taos_config, taos_config, sizeof(chip->taos_config));
+
+ for (i = 0; i < TSL258X_MAX_DEVICE_REGS; i++) {
+ ret = i2c_smbus_write_byte(clientp,
+ (TSL258X_CMD_REG | (TSL258X_CNTRL + i)));
+ if (ret < 0) {
+ dev_err(&clientp->dev,
+ "i2c_smbus_write_byte to cmd reg failed in taos_probe(), err = %d\n",
+ ret);
+ return ret;
+ }
+ ret = i2c_smbus_read_byte(clientp);
+ if (ret < 0) {
+ dev_err(&clientp->dev,
+ "i2c_smbus_read_byte from reg failed in taos_probe(), err = %d\n",
+ ret);
+ return ret;
+ }
+ buf[i] = ret;
+ }
+
+ if (!taos_tsl258x_device(buf)) {
+ dev_info(&clientp->dev,
+ "i2c device found but does not match expected id in taos_probe()\n");
+ return -EINVAL;
+ }
+
+ ret = i2c_smbus_write_byte(clientp, (TSL258X_CMD_REG | TSL258X_CNTRL));
+ if (ret < 0) {
+ dev_err(&clientp->dev,
+ "i2c_smbus_write_byte() to cmd reg failed in taos_probe(), err = %d\n",
+ ret);
+ return ret;
+ }
+
+ indio_dev->info = &tsl2583_info;
+ indio_dev->dev.parent = &clientp->dev;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->name = chip->client->name;
+ ret = iio_device_register(indio_dev);
+ if (ret) {
+ dev_err(&clientp->dev, "iio registration failed\n");
+ return ret;
+ }
+
+ /* Load up the V2 defaults (these are hard coded defaults for now) */
+ taos_defaults(chip);
+
+ /* Make sure the chip is on */
+ taos_chip_on(indio_dev);
+
+ dev_info(&clientp->dev, "Light sensor found.\n");
+ return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int taos_suspend(struct device *dev)
+{
+ struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
+ struct tsl2583_chip *chip = iio_priv(indio_dev);
+ int ret = 0;
+
+ mutex_lock(&chip->als_mutex);
+
+ if (chip->taos_chip_status == TSL258X_CHIP_WORKING) {
+ ret = taos_chip_off(indio_dev);
+ chip->taos_chip_status = TSL258X_CHIP_SUSPENDED;
+ }
+
+ mutex_unlock(&chip->als_mutex);
+ return ret;
+}
+
+static int taos_resume(struct device *dev)
+{
+ struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
+ struct tsl2583_chip *chip = iio_priv(indio_dev);
+ int ret = 0;
+
+ mutex_lock(&chip->als_mutex);
+
+ if (chip->taos_chip_status == TSL258X_CHIP_SUSPENDED)
+ ret = taos_chip_on(indio_dev);
+
+ mutex_unlock(&chip->als_mutex);
+ return ret;
+}
+
+static SIMPLE_DEV_PM_OPS(taos_pm_ops, taos_suspend, taos_resume);
+#define TAOS_PM_OPS (&taos_pm_ops)
+#else
+#define TAOS_PM_OPS NULL
+#endif
+
+static int taos_remove(struct i2c_client *client)
+{
+ iio_device_unregister(i2c_get_clientdata(client));
+
+ return 0;
+}
+
+static struct i2c_device_id taos_idtable[] = {
+ { "tsl2580", 0 },
+ { "tsl2581", 1 },
+ { "tsl2583", 2 },
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, taos_idtable);
+
+/* Driver definition */
+static struct i2c_driver taos_driver = {
+ .driver = {
+ .name = "tsl2583",
+ .pm = TAOS_PM_OPS,
+ },
+ .id_table = taos_idtable,
+ .probe = taos_probe,
+ .remove = taos_remove,
+};
+module_i2c_driver(taos_driver);
+
+MODULE_AUTHOR("J. August Brenner<jbrenner@taosinc.com>");
+MODULE_DESCRIPTION("TAOS tsl2583 ambient light sensor driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/iio/light/tsl2x7x.h b/drivers/staging/iio/light/tsl2x7x.h
new file mode 100644
index 000000000..ecae92211
--- /dev/null
+++ b/drivers/staging/iio/light/tsl2x7x.h
@@ -0,0 +1,100 @@
+/*
+ * Device driver for monitoring ambient light intensity (lux)
+ * and proximity (prox) within the TAOS TSL2X7X family of devices.
+ *
+ * Copyright (c) 2012, TAOS Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#ifndef __TSL2X7X_H
+#define __TSL2X7X_H
+#include <linux/pm.h>
+
+/* Max number of segments allowable in LUX table */
+#define TSL2X7X_MAX_LUX_TABLE_SIZE 9
+#define MAX_DEFAULT_TABLE_BYTES (sizeof(int) * TSL2X7X_MAX_LUX_TABLE_SIZE)
+
+struct iio_dev;
+
+struct tsl2x7x_lux {
+ unsigned int ratio;
+ unsigned int ch0;
+ unsigned int ch1;
+};
+
+/**
+ * struct tsl2x7x_default_settings - power on defaults unless
+ * overridden by platform data.
+ * @als_time: ALS Integration time - multiple of 50mS
+ * @als_gain: Index into the ALS gain table.
+ * @als_gain_trim: default gain trim to account for
+ * aperture effects.
+ * @wait_time: Time between PRX and ALS cycles
+ * in 2.7 periods
+ * @prx_time: 5.2ms prox integration time -
+ * decrease in 2.7ms periods
+ * @prx_gain: Proximity gain index
+ * @prox_config: Prox configuration filters.
+ * @als_cal_target: Known external ALS reading for
+ * calibration.
+ * @interrupts_en: Enable/Disable - 0x00 = none, 0x10 = als,
+ * 0x20 = prx, 0x30 = bth
+ * @persistence: H/W Filters, Number of 'out of limits'
+ * ADC readings PRX/ALS.
+ * @als_thresh_low: CH0 'low' count to trigger interrupt.
+ * @als_thresh_high: CH0 'high' count to trigger interrupt.
+ * @prox_thres_low: Low threshold proximity detection.
+ * @prox_thres_high: High threshold proximity detection
+ * @prox_pulse_count: Number if proximity emitter pulses
+ * @prox_max_samples_cal: Used for prox cal.
+ */
+struct tsl2x7x_settings {
+ int als_time;
+ int als_gain;
+ int als_gain_trim;
+ int wait_time;
+ int prx_time;
+ int prox_gain;
+ int prox_config;
+ int als_cal_target;
+ u8 interrupts_en;
+ u8 persistence;
+ int als_thresh_low;
+ int als_thresh_high;
+ int prox_thres_low;
+ int prox_thres_high;
+ int prox_pulse_count;
+ int prox_max_samples_cal;
+};
+
+/**
+ * struct tsl2X7X_platform_data - Platform callback, glass and defaults
+ * @platform_power: Suspend/resume platform callback
+ * @power_on: Power on callback
+ * @power_off: Power off callback
+ * @platform_lux_table: Device specific glass coefficents
+ * @platform_default_settings: Device specific power on defaults
+ *
+ */
+struct tsl2X7X_platform_data {
+ int (*platform_power)(struct device *dev, pm_message_t);
+ int (*power_on)(struct iio_dev *indio_dev);
+ int (*power_off)(struct i2c_client *dev);
+ struct tsl2x7x_lux platform_lux_table[TSL2X7X_MAX_LUX_TABLE_SIZE];
+ struct tsl2x7x_settings *platform_default_settings;
+};
+
+#endif /* __TSL2X7X_H */
diff --git a/drivers/staging/iio/light/tsl2x7x_core.c b/drivers/staging/iio/light/tsl2x7x_core.c
new file mode 100644
index 000000000..010e607dd
--- /dev/null
+++ b/drivers/staging/iio/light/tsl2x7x_core.c
@@ -0,0 +1,2030 @@
+/*
+ * Device driver for monitoring ambient light intensity in (lux)
+ * and proximity detection (prox) within the TAOS TSL2X7X family of devices.
+ *
+ * Copyright (c) 2012, TAOS Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include <linux/kernel.h>
+#include <linux/i2c.h>
+#include <linux/errno.h>
+#include <linux/delay.h>
+#include <linux/mutex.h>
+#include <linux/interrupt.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/iio/events.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include "tsl2x7x.h"
+
+/* Cal defs*/
+#define PROX_STAT_CAL 0
+#define PROX_STAT_SAMP 1
+#define MAX_SAMPLES_CAL 200
+
+/* TSL2X7X Device ID */
+#define TRITON_ID 0x00
+#define SWORDFISH_ID 0x30
+#define HALIBUT_ID 0x20
+
+/* Lux calculation constants */
+#define TSL2X7X_LUX_CALC_OVER_FLOW 65535
+
+/* TAOS Register definitions - note:
+ * depending on device, some of these register are not used and the
+ * register address is benign.
+ */
+/* 2X7X register offsets */
+#define TSL2X7X_MAX_CONFIG_REG 16
+
+/* Device Registers and Masks */
+#define TSL2X7X_CNTRL 0x00
+#define TSL2X7X_ALS_TIME 0X01
+#define TSL2X7X_PRX_TIME 0x02
+#define TSL2X7X_WAIT_TIME 0x03
+#define TSL2X7X_ALS_MINTHRESHLO 0X04
+#define TSL2X7X_ALS_MINTHRESHHI 0X05
+#define TSL2X7X_ALS_MAXTHRESHLO 0X06
+#define TSL2X7X_ALS_MAXTHRESHHI 0X07
+#define TSL2X7X_PRX_MINTHRESHLO 0X08
+#define TSL2X7X_PRX_MINTHRESHHI 0X09
+#define TSL2X7X_PRX_MAXTHRESHLO 0X0A
+#define TSL2X7X_PRX_MAXTHRESHHI 0X0B
+#define TSL2X7X_PERSISTENCE 0x0C
+#define TSL2X7X_PRX_CONFIG 0x0D
+#define TSL2X7X_PRX_COUNT 0x0E
+#define TSL2X7X_GAIN 0x0F
+#define TSL2X7X_NOTUSED 0x10
+#define TSL2X7X_REVID 0x11
+#define TSL2X7X_CHIPID 0x12
+#define TSL2X7X_STATUS 0x13
+#define TSL2X7X_ALS_CHAN0LO 0x14
+#define TSL2X7X_ALS_CHAN0HI 0x15
+#define TSL2X7X_ALS_CHAN1LO 0x16
+#define TSL2X7X_ALS_CHAN1HI 0x17
+#define TSL2X7X_PRX_LO 0x18
+#define TSL2X7X_PRX_HI 0x19
+
+/* tsl2X7X cmd reg masks */
+#define TSL2X7X_CMD_REG 0x80
+#define TSL2X7X_CMD_SPL_FN 0x60
+
+#define TSL2X7X_CMD_PROX_INT_CLR 0X05
+#define TSL2X7X_CMD_ALS_INT_CLR 0x06
+#define TSL2X7X_CMD_PROXALS_INT_CLR 0X07
+
+/* tsl2X7X cntrl reg masks */
+#define TSL2X7X_CNTL_ADC_ENBL 0x02
+#define TSL2X7X_CNTL_PWR_ON 0x01
+
+/* tsl2X7X status reg masks */
+#define TSL2X7X_STA_ADC_VALID 0x01
+#define TSL2X7X_STA_PRX_VALID 0x02
+#define TSL2X7X_STA_ADC_PRX_VALID (TSL2X7X_STA_ADC_VALID |\
+ TSL2X7X_STA_PRX_VALID)
+#define TSL2X7X_STA_ALS_INTR 0x10
+#define TSL2X7X_STA_PRX_INTR 0x20
+
+/* tsl2X7X cntrl reg masks */
+#define TSL2X7X_CNTL_REG_CLEAR 0x00
+#define TSL2X7X_CNTL_PROX_INT_ENBL 0X20
+#define TSL2X7X_CNTL_ALS_INT_ENBL 0X10
+#define TSL2X7X_CNTL_WAIT_TMR_ENBL 0X08
+#define TSL2X7X_CNTL_PROX_DET_ENBL 0X04
+#define TSL2X7X_CNTL_PWRON 0x01
+#define TSL2X7X_CNTL_ALSPON_ENBL 0x03
+#define TSL2X7X_CNTL_INTALSPON_ENBL 0x13
+#define TSL2X7X_CNTL_PROXPON_ENBL 0x0F
+#define TSL2X7X_CNTL_INTPROXPON_ENBL 0x2F
+
+/*Prox diode to use */
+#define TSL2X7X_DIODE0 0x10
+#define TSL2X7X_DIODE1 0x20
+#define TSL2X7X_DIODE_BOTH 0x30
+
+/* LED Power */
+#define TSL2X7X_mA100 0x00
+#define TSL2X7X_mA50 0x40
+#define TSL2X7X_mA25 0x80
+#define TSL2X7X_mA13 0xD0
+#define TSL2X7X_MAX_TIMER_CNT (0xFF)
+
+#define TSL2X7X_MIN_ITIME 3
+
+/* TAOS txx2x7x Device family members */
+enum {
+ tsl2571,
+ tsl2671,
+ tmd2671,
+ tsl2771,
+ tmd2771,
+ tsl2572,
+ tsl2672,
+ tmd2672,
+ tsl2772,
+ tmd2772
+};
+
+enum {
+ TSL2X7X_CHIP_UNKNOWN = 0,
+ TSL2X7X_CHIP_WORKING = 1,
+ TSL2X7X_CHIP_SUSPENDED = 2
+};
+
+struct tsl2x7x_parse_result {
+ int integer;
+ int fract;
+};
+
+/* Per-device data */
+struct tsl2x7x_als_info {
+ u16 als_ch0;
+ u16 als_ch1;
+ u16 lux;
+};
+
+struct tsl2x7x_prox_stat {
+ int min;
+ int max;
+ int mean;
+ unsigned long stddev;
+};
+
+struct tsl2x7x_chip_info {
+ int chan_table_elements;
+ struct iio_chan_spec channel[4];
+ const struct iio_info *info;
+};
+
+struct tsl2X7X_chip {
+ kernel_ulong_t id;
+ struct mutex prox_mutex;
+ struct mutex als_mutex;
+ struct i2c_client *client;
+ u16 prox_data;
+ struct tsl2x7x_als_info als_cur_info;
+ struct tsl2x7x_settings tsl2x7x_settings;
+ struct tsl2X7X_platform_data *pdata;
+ int als_time_scale;
+ int als_saturation;
+ int tsl2x7x_chip_status;
+ u8 tsl2x7x_config[TSL2X7X_MAX_CONFIG_REG];
+ const struct tsl2x7x_chip_info *chip_info;
+ const struct iio_info *info;
+ s64 event_timestamp;
+ /* This structure is intentionally large to accommodate
+ * updates via sysfs. */
+ /* Sized to 9 = max 8 segments + 1 termination segment */
+ struct tsl2x7x_lux tsl2x7x_device_lux[TSL2X7X_MAX_LUX_TABLE_SIZE];
+};
+
+/* Different devices require different coefficents */
+static const struct tsl2x7x_lux tsl2x71_lux_table[] = {
+ { 14461, 611, 1211 },
+ { 18540, 352, 623 },
+ { 0, 0, 0 },
+};
+
+static const struct tsl2x7x_lux tmd2x71_lux_table[] = {
+ { 11635, 115, 256 },
+ { 15536, 87, 179 },
+ { 0, 0, 0 },
+};
+
+static const struct tsl2x7x_lux tsl2x72_lux_table[] = {
+ { 14013, 466, 917 },
+ { 18222, 310, 552 },
+ { 0, 0, 0 },
+};
+
+static const struct tsl2x7x_lux tmd2x72_lux_table[] = {
+ { 13218, 130, 262 },
+ { 17592, 92, 169 },
+ { 0, 0, 0 },
+};
+
+static const struct tsl2x7x_lux *tsl2x7x_default_lux_table_group[] = {
+ [tsl2571] = tsl2x71_lux_table,
+ [tsl2671] = tsl2x71_lux_table,
+ [tmd2671] = tmd2x71_lux_table,
+ [tsl2771] = tsl2x71_lux_table,
+ [tmd2771] = tmd2x71_lux_table,
+ [tsl2572] = tsl2x72_lux_table,
+ [tsl2672] = tsl2x72_lux_table,
+ [tmd2672] = tmd2x72_lux_table,
+ [tsl2772] = tsl2x72_lux_table,
+ [tmd2772] = tmd2x72_lux_table,
+};
+
+static const struct tsl2x7x_settings tsl2x7x_default_settings = {
+ .als_time = 219, /* 101 ms */
+ .als_gain = 0,
+ .prx_time = 254, /* 5.4 ms */
+ .prox_gain = 1,
+ .wait_time = 245,
+ .prox_config = 0,
+ .als_gain_trim = 1000,
+ .als_cal_target = 150,
+ .als_thresh_low = 200,
+ .als_thresh_high = 256,
+ .persistence = 255,
+ .interrupts_en = 0,
+ .prox_thres_low = 0,
+ .prox_thres_high = 512,
+ .prox_max_samples_cal = 30,
+ .prox_pulse_count = 8
+};
+
+static const s16 tsl2X7X_als_gainadj[] = {
+ 1,
+ 8,
+ 16,
+ 120
+};
+
+static const s16 tsl2X7X_prx_gainadj[] = {
+ 1,
+ 2,
+ 4,
+ 8
+};
+
+/* Channel variations */
+enum {
+ ALS,
+ PRX,
+ ALSPRX,
+ PRX2,
+ ALSPRX2,
+};
+
+static const u8 device_channel_config[] = {
+ ALS,
+ PRX,
+ PRX,
+ ALSPRX,
+ ALSPRX,
+ ALS,
+ PRX2,
+ PRX2,
+ ALSPRX2,
+ ALSPRX2
+};
+
+/**
+ * tsl2x7x_i2c_read() - Read a byte from a register.
+ * @client: i2c client
+ * @reg: device register to read from
+ * @*val: pointer to location to store register contents.
+ *
+ */
+static int
+tsl2x7x_i2c_read(struct i2c_client *client, u8 reg, u8 *val)
+{
+ int ret = 0;
+
+ /* select register to write */
+ ret = i2c_smbus_write_byte(client, (TSL2X7X_CMD_REG | reg));
+ if (ret < 0) {
+ dev_err(&client->dev, "failed to write register %x\n", reg);
+ return ret;
+ }
+
+ /* read the data */
+ ret = i2c_smbus_read_byte(client);
+ if (ret >= 0)
+ *val = (u8)ret;
+ else
+ dev_err(&client->dev, "failed to read register %x\n", reg);
+
+ return ret;
+}
+
+/**
+ * tsl2x7x_get_lux() - Reads and calculates current lux value.
+ * @indio_dev: pointer to IIO device
+ *
+ * The raw ch0 and ch1 values of the ambient light sensed in the last
+ * integration cycle are read from the device.
+ * Time scale factor array values are adjusted based on the integration time.
+ * The raw values are multiplied by a scale factor, and device gain is obtained
+ * using gain index. Limit checks are done next, then the ratio of a multiple
+ * of ch1 value, to the ch0 value, is calculated. Array tsl2x7x_device_lux[]
+ * is then scanned to find the first ratio value that is just above the ratio
+ * we just calculated. The ch0 and ch1 multiplier constants in the array are
+ * then used along with the time scale factor array values, to calculate the
+ * lux.
+ */
+static int tsl2x7x_get_lux(struct iio_dev *indio_dev)
+{
+ u16 ch0, ch1; /* separated ch0/ch1 data from device */
+ u32 lux; /* raw lux calculated from device data */
+ u64 lux64;
+ u32 ratio;
+ u8 buf[4];
+ struct tsl2x7x_lux *p;
+ struct tsl2X7X_chip *chip = iio_priv(indio_dev);
+ int i, ret;
+ u32 ch0lux = 0;
+ u32 ch1lux = 0;
+
+ if (mutex_trylock(&chip->als_mutex) == 0)
+ return chip->als_cur_info.lux; /* busy, so return LAST VALUE */
+
+ if (chip->tsl2x7x_chip_status != TSL2X7X_CHIP_WORKING) {
+ /* device is not enabled */
+ dev_err(&chip->client->dev, "%s: device is not enabled\n",
+ __func__);
+ ret = -EBUSY;
+ goto out_unlock;
+ }
+
+ ret = tsl2x7x_i2c_read(chip->client,
+ (TSL2X7X_CMD_REG | TSL2X7X_STATUS), &buf[0]);
+ if (ret < 0) {
+ dev_err(&chip->client->dev,
+ "%s: Failed to read STATUS Reg\n", __func__);
+ goto out_unlock;
+ }
+ /* is data new & valid */
+ if (!(buf[0] & TSL2X7X_STA_ADC_VALID)) {
+ dev_err(&chip->client->dev,
+ "%s: data not valid yet\n", __func__);
+ ret = chip->als_cur_info.lux; /* return LAST VALUE */
+ goto out_unlock;
+ }
+
+ for (i = 0; i < 4; i++) {
+ ret = tsl2x7x_i2c_read(chip->client,
+ (TSL2X7X_CMD_REG | (TSL2X7X_ALS_CHAN0LO + i)),
+ &buf[i]);
+ if (ret < 0) {
+ dev_err(&chip->client->dev,
+ "failed to read. err=%x\n", ret);
+ goto out_unlock;
+ }
+ }
+
+ /* clear any existing interrupt status */
+ ret = i2c_smbus_write_byte(chip->client,
+ (TSL2X7X_CMD_REG |
+ TSL2X7X_CMD_SPL_FN |
+ TSL2X7X_CMD_ALS_INT_CLR));
+ if (ret < 0) {
+ dev_err(&chip->client->dev,
+ "i2c_write_command failed - err = %d\n", ret);
+ goto out_unlock; /* have no data, so return failure */
+ }
+
+ /* extract ALS/lux data */
+ ch0 = le16_to_cpup((const __le16 *)&buf[0]);
+ ch1 = le16_to_cpup((const __le16 *)&buf[2]);
+
+ chip->als_cur_info.als_ch0 = ch0;
+ chip->als_cur_info.als_ch1 = ch1;
+
+ if ((ch0 >= chip->als_saturation) || (ch1 >= chip->als_saturation)) {
+ lux = TSL2X7X_LUX_CALC_OVER_FLOW;
+ goto return_max;
+ }
+
+ if (ch0 == 0) {
+ /* have no data, so return LAST VALUE */
+ ret = chip->als_cur_info.lux;
+ goto out_unlock;
+ }
+ /* calculate ratio */
+ ratio = (ch1 << 15) / ch0;
+ /* convert to unscaled lux using the pointer to the table */
+ p = (struct tsl2x7x_lux *) chip->tsl2x7x_device_lux;
+ while (p->ratio != 0 && p->ratio < ratio)
+ p++;
+
+ if (p->ratio == 0) {
+ lux = 0;
+ } else {
+ ch0lux = DIV_ROUND_UP((ch0 * p->ch0),
+ tsl2X7X_als_gainadj[chip->tsl2x7x_settings.als_gain]);
+ ch1lux = DIV_ROUND_UP((ch1 * p->ch1),
+ tsl2X7X_als_gainadj[chip->tsl2x7x_settings.als_gain]);
+ lux = ch0lux - ch1lux;
+ }
+
+ /* note: lux is 31 bit max at this point */
+ if (ch1lux > ch0lux) {
+ dev_dbg(&chip->client->dev, "ch1lux > ch0lux-return last value\n");
+ ret = chip->als_cur_info.lux;
+ goto out_unlock;
+ }
+
+ /* adjust for active time scale */
+ if (chip->als_time_scale == 0)
+ lux = 0;
+ else
+ lux = (lux + (chip->als_time_scale >> 1)) /
+ chip->als_time_scale;
+
+ /* adjust for active gain scale
+ * The tsl2x7x_device_lux tables have a factor of 256 built-in.
+ * User-specified gain provides a multiplier.
+ * Apply user-specified gain before shifting right to retain precision.
+ * Use 64 bits to avoid overflow on multiplication.
+ * Then go back to 32 bits before division to avoid using div_u64().
+ */
+
+ lux64 = lux;
+ lux64 = lux64 * chip->tsl2x7x_settings.als_gain_trim;
+ lux64 >>= 8;
+ lux = lux64;
+ lux = (lux + 500) / 1000;
+
+ if (lux > TSL2X7X_LUX_CALC_OVER_FLOW) /* check for overflow */
+ lux = TSL2X7X_LUX_CALC_OVER_FLOW;
+
+ /* Update the structure with the latest lux. */
+return_max:
+ chip->als_cur_info.lux = lux;
+ ret = lux;
+
+out_unlock:
+ mutex_unlock(&chip->als_mutex);
+
+ return ret;
+}
+
+/**
+ * tsl2x7x_get_prox() - Reads proximity data registers and updates
+ * chip->prox_data.
+ *
+ * @indio_dev: pointer to IIO device
+ */
+static int tsl2x7x_get_prox(struct iio_dev *indio_dev)
+{
+ int i;
+ int ret;
+ u8 status;
+ u8 chdata[2];
+ struct tsl2X7X_chip *chip = iio_priv(indio_dev);
+
+ if (mutex_trylock(&chip->prox_mutex) == 0) {
+ dev_err(&chip->client->dev,
+ "%s: Can't get prox mutex\n", __func__);
+ return -EBUSY;
+ }
+
+ ret = tsl2x7x_i2c_read(chip->client,
+ (TSL2X7X_CMD_REG | TSL2X7X_STATUS), &status);
+ if (ret < 0) {
+ dev_err(&chip->client->dev, "i2c err=%d\n", ret);
+ goto prox_poll_err;
+ }
+
+ switch (chip->id) {
+ case tsl2571:
+ case tsl2671:
+ case tmd2671:
+ case tsl2771:
+ case tmd2771:
+ if (!(status & TSL2X7X_STA_ADC_VALID))
+ goto prox_poll_err;
+ break;
+ case tsl2572:
+ case tsl2672:
+ case tmd2672:
+ case tsl2772:
+ case tmd2772:
+ if (!(status & TSL2X7X_STA_PRX_VALID))
+ goto prox_poll_err;
+ break;
+ }
+
+ for (i = 0; i < 2; i++) {
+ ret = tsl2x7x_i2c_read(chip->client,
+ (TSL2X7X_CMD_REG |
+ (TSL2X7X_PRX_LO + i)), &chdata[i]);
+ if (ret < 0)
+ goto prox_poll_err;
+ }
+
+ chip->prox_data =
+ le16_to_cpup((const __le16 *)&chdata[0]);
+
+prox_poll_err:
+
+ mutex_unlock(&chip->prox_mutex);
+
+ return chip->prox_data;
+}
+
+/**
+ * tsl2x7x_defaults() - Populates the device nominal operating parameters
+ * with those provided by a 'platform' data struct or
+ * with prefined defaults.
+ *
+ * @chip: pointer to device structure.
+ */
+static void tsl2x7x_defaults(struct tsl2X7X_chip *chip)
+{
+ /* If Operational settings defined elsewhere.. */
+ if (chip->pdata && chip->pdata->platform_default_settings)
+ memcpy(&(chip->tsl2x7x_settings),
+ chip->pdata->platform_default_settings,
+ sizeof(tsl2x7x_default_settings));
+ else
+ memcpy(&(chip->tsl2x7x_settings),
+ &tsl2x7x_default_settings,
+ sizeof(tsl2x7x_default_settings));
+
+ /* Load up the proper lux table. */
+ if (chip->pdata && chip->pdata->platform_lux_table[0].ratio != 0)
+ memcpy(chip->tsl2x7x_device_lux,
+ chip->pdata->platform_lux_table,
+ sizeof(chip->pdata->platform_lux_table));
+ else
+ memcpy(chip->tsl2x7x_device_lux,
+ (struct tsl2x7x_lux *)tsl2x7x_default_lux_table_group[chip->id],
+ MAX_DEFAULT_TABLE_BYTES);
+}
+
+/**
+ * tsl2x7x_als_calibrate() - Obtain single reading and calculate
+ * the als_gain_trim.
+ *
+ * @indio_dev: pointer to IIO device
+ */
+static int tsl2x7x_als_calibrate(struct iio_dev *indio_dev)
+{
+ struct tsl2X7X_chip *chip = iio_priv(indio_dev);
+ u8 reg_val;
+ int gain_trim_val;
+ int ret;
+ int lux_val;
+
+ ret = i2c_smbus_write_byte(chip->client,
+ (TSL2X7X_CMD_REG | TSL2X7X_CNTRL));
+ if (ret < 0) {
+ dev_err(&chip->client->dev,
+ "failed to write CNTRL register, ret=%d\n", ret);
+ return ret;
+ }
+
+ reg_val = i2c_smbus_read_byte(chip->client);
+ if ((reg_val & (TSL2X7X_CNTL_ADC_ENBL | TSL2X7X_CNTL_PWR_ON))
+ != (TSL2X7X_CNTL_ADC_ENBL | TSL2X7X_CNTL_PWR_ON)) {
+ dev_err(&chip->client->dev,
+ "%s: failed: ADC not enabled\n", __func__);
+ return -1;
+ }
+
+ ret = i2c_smbus_write_byte(chip->client,
+ (TSL2X7X_CMD_REG | TSL2X7X_CNTRL));
+ if (ret < 0) {
+ dev_err(&chip->client->dev,
+ "failed to write ctrl reg: ret=%d\n", ret);
+ return ret;
+ }
+
+ reg_val = i2c_smbus_read_byte(chip->client);
+ if ((reg_val & TSL2X7X_STA_ADC_VALID) != TSL2X7X_STA_ADC_VALID) {
+ dev_err(&chip->client->dev,
+ "%s: failed: STATUS - ADC not valid.\n", __func__);
+ return -ENODATA;
+ }
+
+ lux_val = tsl2x7x_get_lux(indio_dev);
+ if (lux_val < 0) {
+ dev_err(&chip->client->dev,
+ "%s: failed to get lux\n", __func__);
+ return lux_val;
+ }
+
+ gain_trim_val = ((chip->tsl2x7x_settings.als_cal_target)
+ * chip->tsl2x7x_settings.als_gain_trim) / lux_val;
+ if ((gain_trim_val < 250) || (gain_trim_val > 4000))
+ return -ERANGE;
+
+ chip->tsl2x7x_settings.als_gain_trim = gain_trim_val;
+ dev_info(&chip->client->dev,
+ "%s als_calibrate completed\n", chip->client->name);
+
+ return (int) gain_trim_val;
+}
+
+static int tsl2x7x_chip_on(struct iio_dev *indio_dev)
+{
+ int i;
+ int ret = 0;
+ u8 *dev_reg;
+ u8 utmp;
+ int als_count;
+ int als_time;
+ struct tsl2X7X_chip *chip = iio_priv(indio_dev);
+ u8 reg_val = 0;
+
+ if (chip->pdata && chip->pdata->power_on)
+ chip->pdata->power_on(indio_dev);
+
+ /* Non calculated parameters */
+ chip->tsl2x7x_config[TSL2X7X_PRX_TIME] =
+ chip->tsl2x7x_settings.prx_time;
+ chip->tsl2x7x_config[TSL2X7X_WAIT_TIME] =
+ chip->tsl2x7x_settings.wait_time;
+ chip->tsl2x7x_config[TSL2X7X_PRX_CONFIG] =
+ chip->tsl2x7x_settings.prox_config;
+
+ chip->tsl2x7x_config[TSL2X7X_ALS_MINTHRESHLO] =
+ (chip->tsl2x7x_settings.als_thresh_low) & 0xFF;
+ chip->tsl2x7x_config[TSL2X7X_ALS_MINTHRESHHI] =
+ (chip->tsl2x7x_settings.als_thresh_low >> 8) & 0xFF;
+ chip->tsl2x7x_config[TSL2X7X_ALS_MAXTHRESHLO] =
+ (chip->tsl2x7x_settings.als_thresh_high) & 0xFF;
+ chip->tsl2x7x_config[TSL2X7X_ALS_MAXTHRESHHI] =
+ (chip->tsl2x7x_settings.als_thresh_high >> 8) & 0xFF;
+ chip->tsl2x7x_config[TSL2X7X_PERSISTENCE] =
+ chip->tsl2x7x_settings.persistence;
+
+ chip->tsl2x7x_config[TSL2X7X_PRX_COUNT] =
+ chip->tsl2x7x_settings.prox_pulse_count;
+ chip->tsl2x7x_config[TSL2X7X_PRX_MINTHRESHLO] =
+ (chip->tsl2x7x_settings.prox_thres_low) & 0xFF;
+ chip->tsl2x7x_config[TSL2X7X_PRX_MINTHRESHHI] =
+ (chip->tsl2x7x_settings.prox_thres_low >> 8) & 0xFF;
+ chip->tsl2x7x_config[TSL2X7X_PRX_MAXTHRESHLO] =
+ (chip->tsl2x7x_settings.prox_thres_high) & 0xFF;
+ chip->tsl2x7x_config[TSL2X7X_PRX_MAXTHRESHHI] =
+ (chip->tsl2x7x_settings.prox_thres_high >> 8) & 0xFF;
+
+ /* and make sure we're not already on */
+ if (chip->tsl2x7x_chip_status == TSL2X7X_CHIP_WORKING) {
+ /* if forcing a register update - turn off, then on */
+ dev_info(&chip->client->dev, "device is already enabled\n");
+ return -EINVAL;
+ }
+
+ /* determine als integration register */
+ als_count = (chip->tsl2x7x_settings.als_time * 100 + 135) / 270;
+ if (als_count == 0)
+ als_count = 1; /* ensure at least one cycle */
+
+ /* convert back to time (encompasses overrides) */
+ als_time = (als_count * 27 + 5) / 10;
+ chip->tsl2x7x_config[TSL2X7X_ALS_TIME] = 256 - als_count;
+
+ /* Set the gain based on tsl2x7x_settings struct */
+ chip->tsl2x7x_config[TSL2X7X_GAIN] =
+ (chip->tsl2x7x_settings.als_gain |
+ (TSL2X7X_mA100 | TSL2X7X_DIODE1)
+ | ((chip->tsl2x7x_settings.prox_gain) << 2));
+
+ /* set chip struct re scaling and saturation */
+ chip->als_saturation = als_count * 922; /* 90% of full scale */
+ chip->als_time_scale = (als_time + 25) / 50;
+
+ /* TSL2X7X Specific power-on / adc enable sequence
+ * Power on the device 1st. */
+ utmp = TSL2X7X_CNTL_PWR_ON;
+ ret = i2c_smbus_write_byte_data(chip->client,
+ TSL2X7X_CMD_REG | TSL2X7X_CNTRL, utmp);
+ if (ret < 0) {
+ dev_err(&chip->client->dev,
+ "%s: failed on CNTRL reg.\n", __func__);
+ return ret;
+ }
+
+ /* Use the following shadow copy for our delay before enabling ADC.
+ * Write all the registers. */
+ for (i = 0, dev_reg = chip->tsl2x7x_config;
+ i < TSL2X7X_MAX_CONFIG_REG; i++) {
+ ret = i2c_smbus_write_byte_data(chip->client,
+ TSL2X7X_CMD_REG + i, *dev_reg++);
+ if (ret < 0) {
+ dev_err(&chip->client->dev,
+ "failed on write to reg %d.\n", i);
+ return ret;
+ }
+ }
+
+ mdelay(3); /* Power-on settling time */
+
+ /* NOW enable the ADC
+ * initialize the desired mode of operation */
+ utmp = TSL2X7X_CNTL_PWR_ON |
+ TSL2X7X_CNTL_ADC_ENBL |
+ TSL2X7X_CNTL_PROX_DET_ENBL;
+ ret = i2c_smbus_write_byte_data(chip->client,
+ TSL2X7X_CMD_REG | TSL2X7X_CNTRL, utmp);
+ if (ret < 0) {
+ dev_err(&chip->client->dev,
+ "%s: failed on 2nd CTRL reg.\n", __func__);
+ return ret;
+ }
+
+ chip->tsl2x7x_chip_status = TSL2X7X_CHIP_WORKING;
+
+ if (chip->tsl2x7x_settings.interrupts_en != 0) {
+ dev_info(&chip->client->dev, "Setting Up Interrupt(s)\n");
+
+ reg_val = TSL2X7X_CNTL_PWR_ON | TSL2X7X_CNTL_ADC_ENBL;
+ if ((chip->tsl2x7x_settings.interrupts_en == 0x20) ||
+ (chip->tsl2x7x_settings.interrupts_en == 0x30))
+ reg_val |= TSL2X7X_CNTL_PROX_DET_ENBL;
+
+ reg_val |= chip->tsl2x7x_settings.interrupts_en;
+ ret = i2c_smbus_write_byte_data(chip->client,
+ (TSL2X7X_CMD_REG | TSL2X7X_CNTRL), reg_val);
+ if (ret < 0)
+ dev_err(&chip->client->dev,
+ "%s: failed in tsl2x7x_IOCTL_INT_SET.\n",
+ __func__);
+
+ /* Clear out any initial interrupts */
+ ret = i2c_smbus_write_byte(chip->client,
+ TSL2X7X_CMD_REG | TSL2X7X_CMD_SPL_FN |
+ TSL2X7X_CMD_PROXALS_INT_CLR);
+ if (ret < 0) {
+ dev_err(&chip->client->dev,
+ "%s: Failed to clear Int status\n",
+ __func__);
+ return ret;
+ }
+ }
+
+ return ret;
+}
+
+static int tsl2x7x_chip_off(struct iio_dev *indio_dev)
+{
+ int ret;
+ struct tsl2X7X_chip *chip = iio_priv(indio_dev);
+
+ /* turn device off */
+ chip->tsl2x7x_chip_status = TSL2X7X_CHIP_SUSPENDED;
+
+ ret = i2c_smbus_write_byte_data(chip->client,
+ TSL2X7X_CMD_REG | TSL2X7X_CNTRL, 0x00);
+
+ if (chip->pdata && chip->pdata->power_off)
+ chip->pdata->power_off(chip->client);
+
+ return ret;
+}
+
+/**
+ * tsl2x7x_invoke_change
+ * @indio_dev: pointer to IIO device
+ *
+ * Obtain and lock both ALS and PROX resources,
+ * determine and save device state (On/Off),
+ * cycle device to implement updated parameter,
+ * put device back into proper state, and unlock
+ * resource.
+ */
+static
+int tsl2x7x_invoke_change(struct iio_dev *indio_dev)
+{
+ struct tsl2X7X_chip *chip = iio_priv(indio_dev);
+ int device_status = chip->tsl2x7x_chip_status;
+
+ mutex_lock(&chip->als_mutex);
+ mutex_lock(&chip->prox_mutex);
+
+ if (device_status == TSL2X7X_CHIP_WORKING)
+ tsl2x7x_chip_off(indio_dev);
+
+ tsl2x7x_chip_on(indio_dev);
+
+ if (device_status != TSL2X7X_CHIP_WORKING)
+ tsl2x7x_chip_off(indio_dev);
+
+ mutex_unlock(&chip->prox_mutex);
+ mutex_unlock(&chip->als_mutex);
+
+ return 0;
+}
+
+static
+void tsl2x7x_prox_calculate(int *data, int length,
+ struct tsl2x7x_prox_stat *statP)
+{
+ int i;
+ int sample_sum;
+ int tmp;
+
+ if (length == 0)
+ length = 1;
+
+ sample_sum = 0;
+ statP->min = INT_MAX;
+ statP->max = INT_MIN;
+ for (i = 0; i < length; i++) {
+ sample_sum += data[i];
+ statP->min = min(statP->min, data[i]);
+ statP->max = max(statP->max, data[i]);
+ }
+
+ statP->mean = sample_sum / length;
+ sample_sum = 0;
+ for (i = 0; i < length; i++) {
+ tmp = data[i] - statP->mean;
+ sample_sum += tmp * tmp;
+ }
+ statP->stddev = int_sqrt((long)sample_sum)/length;
+}
+
+/**
+ * tsl2x7x_prox_cal() - Calculates std. and sets thresholds.
+ * @indio_dev: pointer to IIO device
+ *
+ * Calculates a standard deviation based on the samples,
+ * and sets the threshold accordingly.
+ */
+static void tsl2x7x_prox_cal(struct iio_dev *indio_dev)
+{
+ int prox_history[MAX_SAMPLES_CAL + 1];
+ int i;
+ struct tsl2x7x_prox_stat prox_stat_data[2];
+ struct tsl2x7x_prox_stat *calP;
+ struct tsl2X7X_chip *chip = iio_priv(indio_dev);
+ u8 tmp_irq_settings;
+ u8 current_state = chip->tsl2x7x_chip_status;
+
+ if (chip->tsl2x7x_settings.prox_max_samples_cal > MAX_SAMPLES_CAL) {
+ dev_err(&chip->client->dev,
+ "max prox samples cal is too big: %d\n",
+ chip->tsl2x7x_settings.prox_max_samples_cal);
+ chip->tsl2x7x_settings.prox_max_samples_cal = MAX_SAMPLES_CAL;
+ }
+
+ /* have to stop to change settings */
+ tsl2x7x_chip_off(indio_dev);
+
+ /* Enable proximity detection save just in case prox not wanted yet*/
+ tmp_irq_settings = chip->tsl2x7x_settings.interrupts_en;
+ chip->tsl2x7x_settings.interrupts_en |= TSL2X7X_CNTL_PROX_INT_ENBL;
+
+ /*turn on device if not already on*/
+ tsl2x7x_chip_on(indio_dev);
+
+ /*gather the samples*/
+ for (i = 0; i < chip->tsl2x7x_settings.prox_max_samples_cal; i++) {
+ mdelay(15);
+ tsl2x7x_get_prox(indio_dev);
+ prox_history[i] = chip->prox_data;
+ dev_info(&chip->client->dev, "2 i=%d prox data= %d\n",
+ i, chip->prox_data);
+ }
+
+ tsl2x7x_chip_off(indio_dev);
+ calP = &prox_stat_data[PROX_STAT_CAL];
+ tsl2x7x_prox_calculate(prox_history,
+ chip->tsl2x7x_settings.prox_max_samples_cal, calP);
+ chip->tsl2x7x_settings.prox_thres_high = (calP->max << 1) - calP->mean;
+
+ dev_info(&chip->client->dev, " cal min=%d mean=%d max=%d\n",
+ calP->min, calP->mean, calP->max);
+ dev_info(&chip->client->dev,
+ "%s proximity threshold set to %d\n",
+ chip->client->name, chip->tsl2x7x_settings.prox_thres_high);
+
+ /* back to the way they were */
+ chip->tsl2x7x_settings.interrupts_en = tmp_irq_settings;
+ if (current_state == TSL2X7X_CHIP_WORKING)
+ tsl2x7x_chip_on(indio_dev);
+}
+
+static ssize_t tsl2x7x_power_state_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct tsl2X7X_chip *chip = iio_priv(dev_to_iio_dev(dev));
+
+ return snprintf(buf, PAGE_SIZE, "%d\n", chip->tsl2x7x_chip_status);
+}
+
+static ssize_t tsl2x7x_power_state_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t len)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ bool value;
+
+ if (strtobool(buf, &value))
+ return -EINVAL;
+
+ if (value)
+ tsl2x7x_chip_on(indio_dev);
+ else
+ tsl2x7x_chip_off(indio_dev);
+
+ return len;
+}
+
+static ssize_t tsl2x7x_gain_available_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct tsl2X7X_chip *chip = iio_priv(dev_to_iio_dev(dev));
+
+ switch (chip->id) {
+ case tsl2571:
+ case tsl2671:
+ case tmd2671:
+ case tsl2771:
+ case tmd2771:
+ return snprintf(buf, PAGE_SIZE, "%s\n", "1 8 16 128");
+ }
+
+ return snprintf(buf, PAGE_SIZE, "%s\n", "1 8 16 120");
+}
+
+static ssize_t tsl2x7x_prox_gain_available_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return snprintf(buf, PAGE_SIZE, "%s\n", "1 2 4 8");
+}
+
+static ssize_t tsl2x7x_als_time_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct tsl2X7X_chip *chip = iio_priv(dev_to_iio_dev(dev));
+ int y, z;
+
+ y = (TSL2X7X_MAX_TIMER_CNT - (u8)chip->tsl2x7x_settings.als_time) + 1;
+ z = y * TSL2X7X_MIN_ITIME;
+ y /= 1000;
+ z %= 1000;
+
+ return snprintf(buf, PAGE_SIZE, "%d.%03d\n", y, z);
+}
+
+static ssize_t tsl2x7x_als_time_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t len)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct tsl2X7X_chip *chip = iio_priv(indio_dev);
+ struct tsl2x7x_parse_result result;
+ int ret;
+
+ ret = iio_str_to_fixpoint(buf, 100, &result.integer, &result.fract);
+ if (ret)
+ return ret;
+
+ result.fract /= 3;
+ chip->tsl2x7x_settings.als_time =
+ (TSL2X7X_MAX_TIMER_CNT - (u8)result.fract);
+
+ dev_info(&chip->client->dev, "%s: als time = %d",
+ __func__, chip->tsl2x7x_settings.als_time);
+
+ tsl2x7x_invoke_change(indio_dev);
+
+ return IIO_VAL_INT_PLUS_MICRO;
+}
+
+static IIO_CONST_ATTR(in_illuminance0_integration_time_available,
+ ".00272 - .696");
+
+static ssize_t tsl2x7x_als_cal_target_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct tsl2X7X_chip *chip = iio_priv(dev_to_iio_dev(dev));
+
+ return snprintf(buf, PAGE_SIZE, "%d\n",
+ chip->tsl2x7x_settings.als_cal_target);
+}
+
+static ssize_t tsl2x7x_als_cal_target_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t len)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct tsl2X7X_chip *chip = iio_priv(indio_dev);
+ unsigned long value;
+
+ if (kstrtoul(buf, 0, &value))
+ return -EINVAL;
+
+ if (value)
+ chip->tsl2x7x_settings.als_cal_target = value;
+
+ tsl2x7x_invoke_change(indio_dev);
+
+ return len;
+}
+
+/* persistence settings */
+static ssize_t tsl2x7x_als_persistence_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct tsl2X7X_chip *chip = iio_priv(dev_to_iio_dev(dev));
+ int y, z, filter_delay;
+
+ /* Determine integration time */
+ y = (TSL2X7X_MAX_TIMER_CNT - (u8)chip->tsl2x7x_settings.als_time) + 1;
+ z = y * TSL2X7X_MIN_ITIME;
+ filter_delay = z * (chip->tsl2x7x_settings.persistence & 0x0F);
+ y = filter_delay / 1000;
+ z = filter_delay % 1000;
+
+ return snprintf(buf, PAGE_SIZE, "%d.%03d\n", y, z);
+}
+
+static ssize_t tsl2x7x_als_persistence_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t len)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct tsl2X7X_chip *chip = iio_priv(indio_dev);
+ struct tsl2x7x_parse_result result;
+ int y, z, filter_delay;
+ int ret;
+
+ ret = iio_str_to_fixpoint(buf, 100, &result.integer, &result.fract);
+ if (ret)
+ return ret;
+
+ y = (TSL2X7X_MAX_TIMER_CNT - (u8)chip->tsl2x7x_settings.als_time) + 1;
+ z = y * TSL2X7X_MIN_ITIME;
+
+ filter_delay =
+ DIV_ROUND_UP(((result.integer * 1000) + result.fract), z);
+
+ chip->tsl2x7x_settings.persistence &= 0xF0;
+ chip->tsl2x7x_settings.persistence |= (filter_delay & 0x0F);
+
+ dev_info(&chip->client->dev, "%s: als persistence = %d",
+ __func__, filter_delay);
+
+ tsl2x7x_invoke_change(indio_dev);
+
+ return IIO_VAL_INT_PLUS_MICRO;
+}
+
+static ssize_t tsl2x7x_prox_persistence_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct tsl2X7X_chip *chip = iio_priv(dev_to_iio_dev(dev));
+ int y, z, filter_delay;
+
+ /* Determine integration time */
+ y = (TSL2X7X_MAX_TIMER_CNT - (u8)chip->tsl2x7x_settings.prx_time) + 1;
+ z = y * TSL2X7X_MIN_ITIME;
+ filter_delay = z * ((chip->tsl2x7x_settings.persistence & 0xF0) >> 4);
+ y = filter_delay / 1000;
+ z = filter_delay % 1000;
+
+ return snprintf(buf, PAGE_SIZE, "%d.%03d\n", y, z);
+}
+
+static ssize_t tsl2x7x_prox_persistence_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t len)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct tsl2X7X_chip *chip = iio_priv(indio_dev);
+ struct tsl2x7x_parse_result result;
+ int y, z, filter_delay;
+ int ret;
+
+ ret = iio_str_to_fixpoint(buf, 100, &result.integer, &result.fract);
+ if (ret)
+ return ret;
+
+ y = (TSL2X7X_MAX_TIMER_CNT - (u8)chip->tsl2x7x_settings.prx_time) + 1;
+ z = y * TSL2X7X_MIN_ITIME;
+
+ filter_delay =
+ DIV_ROUND_UP(((result.integer * 1000) + result.fract), z);
+
+ chip->tsl2x7x_settings.persistence &= 0x0F;
+ chip->tsl2x7x_settings.persistence |= ((filter_delay << 4) & 0xF0);
+
+ dev_info(&chip->client->dev, "%s: prox persistence = %d",
+ __func__, filter_delay);
+
+ tsl2x7x_invoke_change(indio_dev);
+
+ return IIO_VAL_INT_PLUS_MICRO;
+}
+
+static ssize_t tsl2x7x_do_calibrate(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t len)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ bool value;
+
+ if (strtobool(buf, &value))
+ return -EINVAL;
+
+ if (value)
+ tsl2x7x_als_calibrate(indio_dev);
+
+ tsl2x7x_invoke_change(indio_dev);
+
+ return len;
+}
+
+static ssize_t tsl2x7x_luxtable_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct tsl2X7X_chip *chip = iio_priv(dev_to_iio_dev(dev));
+ int i = 0;
+ int offset = 0;
+
+ while (i < (TSL2X7X_MAX_LUX_TABLE_SIZE * 3)) {
+ offset += snprintf(buf + offset, PAGE_SIZE, "%u,%u,%u,",
+ chip->tsl2x7x_device_lux[i].ratio,
+ chip->tsl2x7x_device_lux[i].ch0,
+ chip->tsl2x7x_device_lux[i].ch1);
+ if (chip->tsl2x7x_device_lux[i].ratio == 0) {
+ /* We just printed the first "0" entry.
+ * Now get rid of the extra "," and break. */
+ offset--;
+ break;
+ }
+ i++;
+ }
+
+ offset += snprintf(buf + offset, PAGE_SIZE, "\n");
+ return offset;
+}
+
+static ssize_t tsl2x7x_luxtable_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t len)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct tsl2X7X_chip *chip = iio_priv(indio_dev);
+ int value[ARRAY_SIZE(chip->tsl2x7x_device_lux)*3 + 1];
+ int n;
+
+ get_options(buf, ARRAY_SIZE(value), value);
+
+ /* We now have an array of ints starting at value[1], and
+ * enumerated by value[0].
+ * We expect each group of three ints is one table entry,
+ * and the last table entry is all 0.
+ */
+ n = value[0];
+ if ((n % 3) || n < 6 ||
+ n > ((ARRAY_SIZE(chip->tsl2x7x_device_lux) - 1) * 3)) {
+ dev_info(dev, "LUX TABLE INPUT ERROR 1 Value[0]=%d\n", n);
+ return -EINVAL;
+ }
+
+ if ((value[(n - 2)] | value[(n - 1)] | value[n]) != 0) {
+ dev_info(dev, "LUX TABLE INPUT ERROR 2 Value[0]=%d\n", n);
+ return -EINVAL;
+ }
+
+ if (chip->tsl2x7x_chip_status == TSL2X7X_CHIP_WORKING)
+ tsl2x7x_chip_off(indio_dev);
+
+ /* Zero out the table */
+ memset(chip->tsl2x7x_device_lux, 0, sizeof(chip->tsl2x7x_device_lux));
+ memcpy(chip->tsl2x7x_device_lux, &value[1], (value[0] * 4));
+
+ tsl2x7x_invoke_change(indio_dev);
+
+ return len;
+}
+
+static ssize_t tsl2x7x_do_prox_calibrate(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t len)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ bool value;
+
+ if (strtobool(buf, &value))
+ return -EINVAL;
+
+ if (value)
+ tsl2x7x_prox_cal(indio_dev);
+
+ tsl2x7x_invoke_change(indio_dev);
+
+ return len;
+}
+
+static int tsl2x7x_read_interrupt_config(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan,
+ enum iio_event_type type,
+ enum iio_event_direction dir)
+{
+ struct tsl2X7X_chip *chip = iio_priv(indio_dev);
+ int ret;
+
+ if (chan->type == IIO_INTENSITY)
+ ret = !!(chip->tsl2x7x_settings.interrupts_en & 0x10);
+ else
+ ret = !!(chip->tsl2x7x_settings.interrupts_en & 0x20);
+
+ return ret;
+}
+
+static int tsl2x7x_write_interrupt_config(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan,
+ enum iio_event_type type,
+ enum iio_event_direction dir,
+ int val)
+{
+ struct tsl2X7X_chip *chip = iio_priv(indio_dev);
+
+ if (chan->type == IIO_INTENSITY) {
+ if (val)
+ chip->tsl2x7x_settings.interrupts_en |= 0x10;
+ else
+ chip->tsl2x7x_settings.interrupts_en &= 0x20;
+ } else {
+ if (val)
+ chip->tsl2x7x_settings.interrupts_en |= 0x20;
+ else
+ chip->tsl2x7x_settings.interrupts_en &= 0x10;
+ }
+
+ tsl2x7x_invoke_change(indio_dev);
+
+ return 0;
+}
+
+static int tsl2x7x_write_thresh(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan,
+ enum iio_event_type type,
+ enum iio_event_direction dir,
+ enum iio_event_info info,
+ int val, int val2)
+{
+ struct tsl2X7X_chip *chip = iio_priv(indio_dev);
+
+ if (chan->type == IIO_INTENSITY) {
+ switch (dir) {
+ case IIO_EV_DIR_RISING:
+ chip->tsl2x7x_settings.als_thresh_high = val;
+ break;
+ case IIO_EV_DIR_FALLING:
+ chip->tsl2x7x_settings.als_thresh_low = val;
+ break;
+ default:
+ return -EINVAL;
+ }
+ } else {
+ switch (dir) {
+ case IIO_EV_DIR_RISING:
+ chip->tsl2x7x_settings.prox_thres_high = val;
+ break;
+ case IIO_EV_DIR_FALLING:
+ chip->tsl2x7x_settings.prox_thres_low = val;
+ break;
+ default:
+ return -EINVAL;
+ }
+ }
+
+ tsl2x7x_invoke_change(indio_dev);
+
+ return 0;
+}
+
+static int tsl2x7x_read_thresh(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan,
+ enum iio_event_type type,
+ enum iio_event_direction dir,
+ enum iio_event_info info,
+ int *val, int *val2)
+{
+ struct tsl2X7X_chip *chip = iio_priv(indio_dev);
+
+ if (chan->type == IIO_INTENSITY) {
+ switch (dir) {
+ case IIO_EV_DIR_RISING:
+ *val = chip->tsl2x7x_settings.als_thresh_high;
+ break;
+ case IIO_EV_DIR_FALLING:
+ *val = chip->tsl2x7x_settings.als_thresh_low;
+ break;
+ default:
+ return -EINVAL;
+ }
+ } else {
+ switch (dir) {
+ case IIO_EV_DIR_RISING:
+ *val = chip->tsl2x7x_settings.prox_thres_high;
+ break;
+ case IIO_EV_DIR_FALLING:
+ *val = chip->tsl2x7x_settings.prox_thres_low;
+ break;
+ default:
+ return -EINVAL;
+ }
+ }
+
+ return IIO_VAL_INT;
+}
+
+static int tsl2x7x_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val,
+ int *val2,
+ long mask)
+{
+ int ret = -EINVAL;
+ struct tsl2X7X_chip *chip = iio_priv(indio_dev);
+
+ switch (mask) {
+ case IIO_CHAN_INFO_PROCESSED:
+ switch (chan->type) {
+ case IIO_LIGHT:
+ tsl2x7x_get_lux(indio_dev);
+ *val = chip->als_cur_info.lux;
+ ret = IIO_VAL_INT;
+ break;
+ default:
+ return -EINVAL;
+ }
+ break;
+ case IIO_CHAN_INFO_RAW:
+ switch (chan->type) {
+ case IIO_INTENSITY:
+ tsl2x7x_get_lux(indio_dev);
+ if (chan->channel == 0)
+ *val = chip->als_cur_info.als_ch0;
+ else
+ *val = chip->als_cur_info.als_ch1;
+ ret = IIO_VAL_INT;
+ break;
+ case IIO_PROXIMITY:
+ tsl2x7x_get_prox(indio_dev);
+ *val = chip->prox_data;
+ ret = IIO_VAL_INT;
+ break;
+ default:
+ return -EINVAL;
+ }
+ break;
+ case IIO_CHAN_INFO_CALIBSCALE:
+ if (chan->type == IIO_LIGHT)
+ *val =
+ tsl2X7X_als_gainadj[chip->tsl2x7x_settings.als_gain];
+ else
+ *val =
+ tsl2X7X_prx_gainadj[chip->tsl2x7x_settings.prox_gain];
+ ret = IIO_VAL_INT;
+ break;
+ case IIO_CHAN_INFO_CALIBBIAS:
+ *val = chip->tsl2x7x_settings.als_gain_trim;
+ ret = IIO_VAL_INT;
+ break;
+
+ default:
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+static int tsl2x7x_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int val,
+ int val2,
+ long mask)
+{
+ struct tsl2X7X_chip *chip = iio_priv(indio_dev);
+
+ switch (mask) {
+ case IIO_CHAN_INFO_CALIBSCALE:
+ if (chan->type == IIO_INTENSITY) {
+ switch (val) {
+ case 1:
+ chip->tsl2x7x_settings.als_gain = 0;
+ break;
+ case 8:
+ chip->tsl2x7x_settings.als_gain = 1;
+ break;
+ case 16:
+ chip->tsl2x7x_settings.als_gain = 2;
+ break;
+ case 120:
+ switch (chip->id) {
+ case tsl2572:
+ case tsl2672:
+ case tmd2672:
+ case tsl2772:
+ case tmd2772:
+ return -EINVAL;
+ }
+ chip->tsl2x7x_settings.als_gain = 3;
+ break;
+ case 128:
+ switch (chip->id) {
+ case tsl2571:
+ case tsl2671:
+ case tmd2671:
+ case tsl2771:
+ case tmd2771:
+ return -EINVAL;
+ }
+ chip->tsl2x7x_settings.als_gain = 3;
+ break;
+ default:
+ return -EINVAL;
+ }
+ } else {
+ switch (val) {
+ case 1:
+ chip->tsl2x7x_settings.prox_gain = 0;
+ break;
+ case 2:
+ chip->tsl2x7x_settings.prox_gain = 1;
+ break;
+ case 4:
+ chip->tsl2x7x_settings.prox_gain = 2;
+ break;
+ case 8:
+ chip->tsl2x7x_settings.prox_gain = 3;
+ break;
+ default:
+ return -EINVAL;
+ }
+ }
+ break;
+ case IIO_CHAN_INFO_CALIBBIAS:
+ chip->tsl2x7x_settings.als_gain_trim = val;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ tsl2x7x_invoke_change(indio_dev);
+
+ return 0;
+}
+
+static DEVICE_ATTR(power_state, S_IRUGO | S_IWUSR,
+ tsl2x7x_power_state_show, tsl2x7x_power_state_store);
+
+static DEVICE_ATTR(in_proximity0_calibscale_available, S_IRUGO,
+ tsl2x7x_prox_gain_available_show, NULL);
+
+static DEVICE_ATTR(in_illuminance0_calibscale_available, S_IRUGO,
+ tsl2x7x_gain_available_show, NULL);
+
+static DEVICE_ATTR(in_illuminance0_integration_time, S_IRUGO | S_IWUSR,
+ tsl2x7x_als_time_show, tsl2x7x_als_time_store);
+
+static DEVICE_ATTR(in_illuminance0_target_input, S_IRUGO | S_IWUSR,
+ tsl2x7x_als_cal_target_show, tsl2x7x_als_cal_target_store);
+
+static DEVICE_ATTR(in_illuminance0_calibrate, S_IWUSR, NULL,
+ tsl2x7x_do_calibrate);
+
+static DEVICE_ATTR(in_proximity0_calibrate, S_IWUSR, NULL,
+ tsl2x7x_do_prox_calibrate);
+
+static DEVICE_ATTR(in_illuminance0_lux_table, S_IRUGO | S_IWUSR,
+ tsl2x7x_luxtable_show, tsl2x7x_luxtable_store);
+
+static DEVICE_ATTR(in_intensity0_thresh_period, S_IRUGO | S_IWUSR,
+ tsl2x7x_als_persistence_show, tsl2x7x_als_persistence_store);
+
+static DEVICE_ATTR(in_proximity0_thresh_period, S_IRUGO | S_IWUSR,
+ tsl2x7x_prox_persistence_show, tsl2x7x_prox_persistence_store);
+
+/* Use the default register values to identify the Taos device */
+static int tsl2x7x_device_id(unsigned char *id, int target)
+{
+ switch (target) {
+ case tsl2571:
+ case tsl2671:
+ case tsl2771:
+ return (*id & 0xf0) == TRITON_ID;
+ case tmd2671:
+ case tmd2771:
+ return (*id & 0xf0) == HALIBUT_ID;
+ case tsl2572:
+ case tsl2672:
+ case tmd2672:
+ case tsl2772:
+ case tmd2772:
+ return (*id & 0xf0) == SWORDFISH_ID;
+ }
+
+ return -EINVAL;
+}
+
+static irqreturn_t tsl2x7x_event_handler(int irq, void *private)
+{
+ struct iio_dev *indio_dev = private;
+ struct tsl2X7X_chip *chip = iio_priv(indio_dev);
+ s64 timestamp = iio_get_time_ns();
+ int ret;
+ u8 value;
+
+ value = i2c_smbus_read_byte_data(chip->client,
+ TSL2X7X_CMD_REG | TSL2X7X_STATUS);
+
+ /* What type of interrupt do we need to process */
+ if (value & TSL2X7X_STA_PRX_INTR) {
+ tsl2x7x_get_prox(indio_dev); /* freshen data for ABI */
+ iio_push_event(indio_dev,
+ IIO_UNMOD_EVENT_CODE(IIO_PROXIMITY,
+ 0,
+ IIO_EV_TYPE_THRESH,
+ IIO_EV_DIR_EITHER),
+ timestamp);
+ }
+
+ if (value & TSL2X7X_STA_ALS_INTR) {
+ tsl2x7x_get_lux(indio_dev); /* freshen data for ABI */
+ iio_push_event(indio_dev,
+ IIO_UNMOD_EVENT_CODE(IIO_LIGHT,
+ 0,
+ IIO_EV_TYPE_THRESH,
+ IIO_EV_DIR_EITHER),
+ timestamp);
+ }
+ /* Clear interrupt now that we have handled it. */
+ ret = i2c_smbus_write_byte(chip->client,
+ TSL2X7X_CMD_REG | TSL2X7X_CMD_SPL_FN |
+ TSL2X7X_CMD_PROXALS_INT_CLR);
+ if (ret < 0)
+ dev_err(&chip->client->dev,
+ "Failed to clear irq from event handler. err = %d\n",
+ ret);
+
+ return IRQ_HANDLED;
+}
+
+static struct attribute *tsl2x7x_ALS_device_attrs[] = {
+ &dev_attr_power_state.attr,
+ &dev_attr_in_illuminance0_calibscale_available.attr,
+ &dev_attr_in_illuminance0_integration_time.attr,
+ &iio_const_attr_in_illuminance0_integration_time_available.dev_attr.attr,
+ &dev_attr_in_illuminance0_target_input.attr,
+ &dev_attr_in_illuminance0_calibrate.attr,
+ &dev_attr_in_illuminance0_lux_table.attr,
+ NULL
+};
+
+static struct attribute *tsl2x7x_PRX_device_attrs[] = {
+ &dev_attr_power_state.attr,
+ &dev_attr_in_proximity0_calibrate.attr,
+ NULL
+};
+
+static struct attribute *tsl2x7x_ALSPRX_device_attrs[] = {
+ &dev_attr_power_state.attr,
+ &dev_attr_in_illuminance0_calibscale_available.attr,
+ &dev_attr_in_illuminance0_integration_time.attr,
+ &iio_const_attr_in_illuminance0_integration_time_available.dev_attr.attr,
+ &dev_attr_in_illuminance0_target_input.attr,
+ &dev_attr_in_illuminance0_calibrate.attr,
+ &dev_attr_in_illuminance0_lux_table.attr,
+ &dev_attr_in_proximity0_calibrate.attr,
+ NULL
+};
+
+static struct attribute *tsl2x7x_PRX2_device_attrs[] = {
+ &dev_attr_power_state.attr,
+ &dev_attr_in_proximity0_calibrate.attr,
+ &dev_attr_in_proximity0_calibscale_available.attr,
+ NULL
+};
+
+static struct attribute *tsl2x7x_ALSPRX2_device_attrs[] = {
+ &dev_attr_power_state.attr,
+ &dev_attr_in_illuminance0_calibscale_available.attr,
+ &dev_attr_in_illuminance0_integration_time.attr,
+ &iio_const_attr_in_illuminance0_integration_time_available.dev_attr.attr,
+ &dev_attr_in_illuminance0_target_input.attr,
+ &dev_attr_in_illuminance0_calibrate.attr,
+ &dev_attr_in_illuminance0_lux_table.attr,
+ &dev_attr_in_proximity0_calibrate.attr,
+ &dev_attr_in_proximity0_calibscale_available.attr,
+ NULL
+};
+
+static struct attribute *tsl2X7X_ALS_event_attrs[] = {
+ &dev_attr_in_intensity0_thresh_period.attr,
+ NULL,
+};
+static struct attribute *tsl2X7X_PRX_event_attrs[] = {
+ &dev_attr_in_proximity0_thresh_period.attr,
+ NULL,
+};
+
+static struct attribute *tsl2X7X_ALSPRX_event_attrs[] = {
+ &dev_attr_in_intensity0_thresh_period.attr,
+ &dev_attr_in_proximity0_thresh_period.attr,
+ NULL,
+};
+
+static const struct attribute_group tsl2X7X_device_attr_group_tbl[] = {
+ [ALS] = {
+ .attrs = tsl2x7x_ALS_device_attrs,
+ },
+ [PRX] = {
+ .attrs = tsl2x7x_PRX_device_attrs,
+ },
+ [ALSPRX] = {
+ .attrs = tsl2x7x_ALSPRX_device_attrs,
+ },
+ [PRX2] = {
+ .attrs = tsl2x7x_PRX2_device_attrs,
+ },
+ [ALSPRX2] = {
+ .attrs = tsl2x7x_ALSPRX2_device_attrs,
+ },
+};
+
+static struct attribute_group tsl2X7X_event_attr_group_tbl[] = {
+ [ALS] = {
+ .attrs = tsl2X7X_ALS_event_attrs,
+ .name = "events",
+ },
+ [PRX] = {
+ .attrs = tsl2X7X_PRX_event_attrs,
+ .name = "events",
+ },
+ [ALSPRX] = {
+ .attrs = tsl2X7X_ALSPRX_event_attrs,
+ .name = "events",
+ },
+};
+
+static const struct iio_info tsl2X7X_device_info[] = {
+ [ALS] = {
+ .attrs = &tsl2X7X_device_attr_group_tbl[ALS],
+ .event_attrs = &tsl2X7X_event_attr_group_tbl[ALS],
+ .driver_module = THIS_MODULE,
+ .read_raw = &tsl2x7x_read_raw,
+ .write_raw = &tsl2x7x_write_raw,
+ .read_event_value = &tsl2x7x_read_thresh,
+ .write_event_value = &tsl2x7x_write_thresh,
+ .read_event_config = &tsl2x7x_read_interrupt_config,
+ .write_event_config = &tsl2x7x_write_interrupt_config,
+ },
+ [PRX] = {
+ .attrs = &tsl2X7X_device_attr_group_tbl[PRX],
+ .event_attrs = &tsl2X7X_event_attr_group_tbl[PRX],
+ .driver_module = THIS_MODULE,
+ .read_raw = &tsl2x7x_read_raw,
+ .write_raw = &tsl2x7x_write_raw,
+ .read_event_value = &tsl2x7x_read_thresh,
+ .write_event_value = &tsl2x7x_write_thresh,
+ .read_event_config = &tsl2x7x_read_interrupt_config,
+ .write_event_config = &tsl2x7x_write_interrupt_config,
+ },
+ [ALSPRX] = {
+ .attrs = &tsl2X7X_device_attr_group_tbl[ALSPRX],
+ .event_attrs = &tsl2X7X_event_attr_group_tbl[ALSPRX],
+ .driver_module = THIS_MODULE,
+ .read_raw = &tsl2x7x_read_raw,
+ .write_raw = &tsl2x7x_write_raw,
+ .read_event_value = &tsl2x7x_read_thresh,
+ .write_event_value = &tsl2x7x_write_thresh,
+ .read_event_config = &tsl2x7x_read_interrupt_config,
+ .write_event_config = &tsl2x7x_write_interrupt_config,
+ },
+ [PRX2] = {
+ .attrs = &tsl2X7X_device_attr_group_tbl[PRX2],
+ .event_attrs = &tsl2X7X_event_attr_group_tbl[PRX],
+ .driver_module = THIS_MODULE,
+ .read_raw = &tsl2x7x_read_raw,
+ .write_raw = &tsl2x7x_write_raw,
+ .read_event_value = &tsl2x7x_read_thresh,
+ .write_event_value = &tsl2x7x_write_thresh,
+ .read_event_config = &tsl2x7x_read_interrupt_config,
+ .write_event_config = &tsl2x7x_write_interrupt_config,
+ },
+ [ALSPRX2] = {
+ .attrs = &tsl2X7X_device_attr_group_tbl[ALSPRX2],
+ .event_attrs = &tsl2X7X_event_attr_group_tbl[ALSPRX],
+ .driver_module = THIS_MODULE,
+ .read_raw = &tsl2x7x_read_raw,
+ .write_raw = &tsl2x7x_write_raw,
+ .read_event_value = &tsl2x7x_read_thresh,
+ .write_event_value = &tsl2x7x_write_thresh,
+ .read_event_config = &tsl2x7x_read_interrupt_config,
+ .write_event_config = &tsl2x7x_write_interrupt_config,
+ },
+};
+
+static const struct iio_event_spec tsl2x7x_events[] = {
+ {
+ .type = IIO_EV_TYPE_THRESH,
+ .dir = IIO_EV_DIR_RISING,
+ .mask_separate = BIT(IIO_EV_INFO_VALUE) |
+ BIT(IIO_EV_INFO_ENABLE),
+ }, {
+ .type = IIO_EV_TYPE_THRESH,
+ .dir = IIO_EV_DIR_FALLING,
+ .mask_separate = BIT(IIO_EV_INFO_VALUE) |
+ BIT(IIO_EV_INFO_ENABLE),
+ },
+};
+
+static const struct tsl2x7x_chip_info tsl2x7x_chip_info_tbl[] = {
+ [ALS] = {
+ .channel = {
+ {
+ .type = IIO_LIGHT,
+ .indexed = 1,
+ .channel = 0,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED),
+ }, {
+ .type = IIO_INTENSITY,
+ .indexed = 1,
+ .channel = 0,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+ BIT(IIO_CHAN_INFO_CALIBSCALE) |
+ BIT(IIO_CHAN_INFO_CALIBBIAS),
+ .event_spec = tsl2x7x_events,
+ .num_event_specs = ARRAY_SIZE(tsl2x7x_events),
+ }, {
+ .type = IIO_INTENSITY,
+ .indexed = 1,
+ .channel = 1,
+ },
+ },
+ .chan_table_elements = 3,
+ .info = &tsl2X7X_device_info[ALS],
+ },
+ [PRX] = {
+ .channel = {
+ {
+ .type = IIO_PROXIMITY,
+ .indexed = 1,
+ .channel = 0,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
+ .event_spec = tsl2x7x_events,
+ .num_event_specs = ARRAY_SIZE(tsl2x7x_events),
+ },
+ },
+ .chan_table_elements = 1,
+ .info = &tsl2X7X_device_info[PRX],
+ },
+ [ALSPRX] = {
+ .channel = {
+ {
+ .type = IIO_LIGHT,
+ .indexed = 1,
+ .channel = 0,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED)
+ }, {
+ .type = IIO_INTENSITY,
+ .indexed = 1,
+ .channel = 0,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+ BIT(IIO_CHAN_INFO_CALIBSCALE) |
+ BIT(IIO_CHAN_INFO_CALIBBIAS),
+ .event_spec = tsl2x7x_events,
+ .num_event_specs = ARRAY_SIZE(tsl2x7x_events),
+ }, {
+ .type = IIO_INTENSITY,
+ .indexed = 1,
+ .channel = 1,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
+ }, {
+ .type = IIO_PROXIMITY,
+ .indexed = 1,
+ .channel = 0,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
+ .event_spec = tsl2x7x_events,
+ .num_event_specs = ARRAY_SIZE(tsl2x7x_events),
+ },
+ },
+ .chan_table_elements = 4,
+ .info = &tsl2X7X_device_info[ALSPRX],
+ },
+ [PRX2] = {
+ .channel = {
+ {
+ .type = IIO_PROXIMITY,
+ .indexed = 1,
+ .channel = 0,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+ BIT(IIO_CHAN_INFO_CALIBSCALE),
+ .event_spec = tsl2x7x_events,
+ .num_event_specs = ARRAY_SIZE(tsl2x7x_events),
+ },
+ },
+ .chan_table_elements = 1,
+ .info = &tsl2X7X_device_info[PRX2],
+ },
+ [ALSPRX2] = {
+ .channel = {
+ {
+ .type = IIO_LIGHT,
+ .indexed = 1,
+ .channel = 0,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED),
+ }, {
+ .type = IIO_INTENSITY,
+ .indexed = 1,
+ .channel = 0,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+ BIT(IIO_CHAN_INFO_CALIBSCALE) |
+ BIT(IIO_CHAN_INFO_CALIBBIAS),
+ .event_spec = tsl2x7x_events,
+ .num_event_specs = ARRAY_SIZE(tsl2x7x_events),
+ }, {
+ .type = IIO_INTENSITY,
+ .indexed = 1,
+ .channel = 1,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
+ }, {
+ .type = IIO_PROXIMITY,
+ .indexed = 1,
+ .channel = 0,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+ BIT(IIO_CHAN_INFO_CALIBSCALE),
+ .event_spec = tsl2x7x_events,
+ .num_event_specs = ARRAY_SIZE(tsl2x7x_events),
+ },
+ },
+ .chan_table_elements = 4,
+ .info = &tsl2X7X_device_info[ALSPRX2],
+ },
+};
+
+static int tsl2x7x_probe(struct i2c_client *clientp,
+ const struct i2c_device_id *id)
+{
+ int ret;
+ unsigned char device_id;
+ struct iio_dev *indio_dev;
+ struct tsl2X7X_chip *chip;
+
+ indio_dev = devm_iio_device_alloc(&clientp->dev, sizeof(*chip));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ chip = iio_priv(indio_dev);
+ chip->client = clientp;
+ i2c_set_clientdata(clientp, indio_dev);
+
+ ret = tsl2x7x_i2c_read(chip->client,
+ TSL2X7X_CHIPID, &device_id);
+ if (ret < 0)
+ return ret;
+
+ if ((!tsl2x7x_device_id(&device_id, id->driver_data)) ||
+ (tsl2x7x_device_id(&device_id, id->driver_data) == -EINVAL)) {
+ dev_info(&chip->client->dev,
+ "%s: i2c device found does not match expected id\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ ret = i2c_smbus_write_byte(clientp, (TSL2X7X_CMD_REG | TSL2X7X_CNTRL));
+ if (ret < 0) {
+ dev_err(&clientp->dev, "write to cmd reg failed. err = %d\n",
+ ret);
+ return ret;
+ }
+
+ /* ALS and PROX functions can be invoked via user space poll
+ * or H/W interrupt. If busy return last sample. */
+ mutex_init(&chip->als_mutex);
+ mutex_init(&chip->prox_mutex);
+
+ chip->tsl2x7x_chip_status = TSL2X7X_CHIP_UNKNOWN;
+ chip->pdata = clientp->dev.platform_data;
+ chip->id = id->driver_data;
+ chip->chip_info =
+ &tsl2x7x_chip_info_tbl[device_channel_config[id->driver_data]];
+
+ indio_dev->info = chip->chip_info->info;
+ indio_dev->dev.parent = &clientp->dev;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->name = chip->client->name;
+ indio_dev->channels = chip->chip_info->channel;
+ indio_dev->num_channels = chip->chip_info->chan_table_elements;
+
+ if (clientp->irq) {
+ ret = devm_request_threaded_irq(&clientp->dev, clientp->irq,
+ NULL,
+ &tsl2x7x_event_handler,
+ IRQF_TRIGGER_RISING |
+ IRQF_ONESHOT,
+ "TSL2X7X_event",
+ indio_dev);
+ if (ret) {
+ dev_err(&clientp->dev,
+ "%s: irq request failed", __func__);
+ return ret;
+ }
+ }
+
+ /* Load up the defaults */
+ tsl2x7x_defaults(chip);
+ /* Make sure the chip is on */
+ tsl2x7x_chip_on(indio_dev);
+
+ ret = iio_device_register(indio_dev);
+ if (ret) {
+ dev_err(&clientp->dev,
+ "%s: iio registration failed\n", __func__);
+ return ret;
+ }
+
+ dev_info(&clientp->dev, "%s Light sensor found.\n", id->name);
+
+ return 0;
+}
+
+static int tsl2x7x_suspend(struct device *dev)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct tsl2X7X_chip *chip = iio_priv(indio_dev);
+ int ret = 0;
+
+ if (chip->tsl2x7x_chip_status == TSL2X7X_CHIP_WORKING) {
+ ret = tsl2x7x_chip_off(indio_dev);
+ chip->tsl2x7x_chip_status = TSL2X7X_CHIP_SUSPENDED;
+ }
+
+ if (chip->pdata && chip->pdata->platform_power) {
+ pm_message_t pmm = {PM_EVENT_SUSPEND};
+
+ chip->pdata->platform_power(dev, pmm);
+ }
+
+ return ret;
+}
+
+static int tsl2x7x_resume(struct device *dev)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct tsl2X7X_chip *chip = iio_priv(indio_dev);
+ int ret = 0;
+
+ if (chip->pdata && chip->pdata->platform_power) {
+ pm_message_t pmm = {PM_EVENT_RESUME};
+
+ chip->pdata->platform_power(dev, pmm);
+ }
+
+ if (chip->tsl2x7x_chip_status == TSL2X7X_CHIP_SUSPENDED)
+ ret = tsl2x7x_chip_on(indio_dev);
+
+ return ret;
+}
+
+static int tsl2x7x_remove(struct i2c_client *client)
+{
+ struct iio_dev *indio_dev = i2c_get_clientdata(client);
+
+ tsl2x7x_chip_off(indio_dev);
+
+ iio_device_unregister(indio_dev);
+
+ return 0;
+}
+
+static struct i2c_device_id tsl2x7x_idtable[] = {
+ { "tsl2571", tsl2571 },
+ { "tsl2671", tsl2671 },
+ { "tmd2671", tmd2671 },
+ { "tsl2771", tsl2771 },
+ { "tmd2771", tmd2771 },
+ { "tsl2572", tsl2572 },
+ { "tsl2672", tsl2672 },
+ { "tmd2672", tmd2672 },
+ { "tsl2772", tsl2772 },
+ { "tmd2772", tmd2772 },
+ {}
+};
+
+MODULE_DEVICE_TABLE(i2c, tsl2x7x_idtable);
+
+static const struct dev_pm_ops tsl2x7x_pm_ops = {
+ .suspend = tsl2x7x_suspend,
+ .resume = tsl2x7x_resume,
+};
+
+/* Driver definition */
+static struct i2c_driver tsl2x7x_driver = {
+ .driver = {
+ .name = "tsl2x7x",
+ .pm = &tsl2x7x_pm_ops,
+ },
+ .id_table = tsl2x7x_idtable,
+ .probe = tsl2x7x_probe,
+ .remove = tsl2x7x_remove,
+};
+
+module_i2c_driver(tsl2x7x_driver);
+
+MODULE_AUTHOR("J. August Brenner<jbrenner@taosinc.com>");
+MODULE_DESCRIPTION("TAOS tsl2x7x ambient and proximity light sensor driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/iio/magnetometer/Kconfig b/drivers/staging/iio/magnetometer/Kconfig
new file mode 100644
index 000000000..dec814a7a
--- /dev/null
+++ b/drivers/staging/iio/magnetometer/Kconfig
@@ -0,0 +1,40 @@
+#
+# Magnetometer sensors
+#
+menu "Magnetometer sensors"
+
+config SENSORS_HMC5843
+ tristate
+ select IIO_BUFFER
+ select IIO_TRIGGERED_BUFFER
+
+config SENSORS_HMC5843_I2C
+ tristate "Honeywell HMC5843/5883/5883L 3-Axis Magnetometer (I2C)"
+ depends on I2C
+ select SENSORS_HMC5843
+ select REGMAP_I2C
+ help
+ Say Y here to add support for the Honeywell HMC5843, HMC5883 and
+ HMC5883L 3-Axis Magnetometer (digital compass).
+
+ This driver can also be compiled as a set of modules.
+ If so, these modules will be created:
+ - hmc5843_core (core functions)
+ - hmc5843_i2c (support for HMC5843, HMC5883, HMC5883L and HMC5983)
+
+config SENSORS_HMC5843_SPI
+ tristate "Honeywell HMC5983 3-Axis Magnetometer (SPI)"
+ depends on SPI_MASTER
+ select SENSORS_HMC5843
+ select REGMAP_SPI
+ help
+ Say Y here to add support for the Honeywell HMC5983 3-Axis Magnetometer
+ (digital compass).
+
+ This driver can also be compiled as a set of modules.
+ If so, these modules will be created:
+ - hmc5843_core (core functions)
+ - hmc5843_spi (support for HMC5983)
+
+
+endmenu
diff --git a/drivers/staging/iio/magnetometer/Makefile b/drivers/staging/iio/magnetometer/Makefile
new file mode 100644
index 000000000..33761a19a
--- /dev/null
+++ b/drivers/staging/iio/magnetometer/Makefile
@@ -0,0 +1,7 @@
+#
+# Makefile for industrial I/O Magnetometer sensors
+#
+
+obj-$(CONFIG_SENSORS_HMC5843) += hmc5843_core.o
+obj-$(CONFIG_SENSORS_HMC5843_I2C) += hmc5843_i2c.o
+obj-$(CONFIG_SENSORS_HMC5843_SPI) += hmc5843_spi.o
diff --git a/drivers/staging/iio/magnetometer/hmc5843.h b/drivers/staging/iio/magnetometer/hmc5843.h
new file mode 100644
index 000000000..f3d0da2fe
--- /dev/null
+++ b/drivers/staging/iio/magnetometer/hmc5843.h
@@ -0,0 +1,66 @@
+/*
+ * Header file for hmc5843 driver
+ *
+ * Split from hmc5843.c
+ * Copyright (C) Josef Gajdusek <atx@atx.name>
+ *
+ * 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.
+ *
+ * */
+
+#ifndef HMC5843_CORE_H
+#define HMC5843_CORE_H
+
+#include <linux/regmap.h>
+#include <linux/iio/iio.h>
+
+#define HMC5843_CONFIG_REG_A 0x00
+#define HMC5843_CONFIG_REG_B 0x01
+#define HMC5843_MODE_REG 0x02
+#define HMC5843_DATA_OUT_MSB_REGS 0x03
+#define HMC5843_STATUS_REG 0x09
+#define HMC5843_ID_REG 0x0a
+#define HMC5843_ID_END 0x0c
+
+enum hmc5843_ids {
+ HMC5843_ID,
+ HMC5883_ID,
+ HMC5883L_ID,
+ HMC5983_ID,
+};
+
+/**
+ * struct hcm5843_data - device specific data
+ * @dev: actual device
+ * @lock: update and read regmap data
+ * @regmap: hardware access register maps
+ * @variant: describe chip variants
+ * @buffer: 3x 16-bit channels + padding + 64-bit timestamp
+ **/
+struct hmc5843_data {
+ struct device *dev;
+ struct mutex lock;
+ struct regmap *regmap;
+ const struct hmc5843_chip_info *variant;
+ __be16 buffer[8];
+};
+
+int hmc5843_common_probe(struct device *dev, struct regmap *regmap,
+ enum hmc5843_ids id);
+int hmc5843_common_remove(struct device *dev);
+
+int hmc5843_common_suspend(struct device *dev);
+int hmc5843_common_resume(struct device *dev);
+
+#ifdef CONFIG_PM_SLEEP
+static SIMPLE_DEV_PM_OPS(hmc5843_pm_ops,
+ hmc5843_common_suspend,
+ hmc5843_common_resume);
+#define HMC5843_PM_OPS (&hmc5843_pm_ops)
+#else
+#define HMC5843_PM_OPS NULL
+#endif
+
+#endif /* HMC5843_CORE_H */
diff --git a/drivers/staging/iio/magnetometer/hmc5843_core.c b/drivers/staging/iio/magnetometer/hmc5843_core.c
new file mode 100644
index 000000000..fffca3a9f
--- /dev/null
+++ b/drivers/staging/iio/magnetometer/hmc5843_core.c
@@ -0,0 +1,646 @@
+/*
+ * Device driver for the the HMC5843 multi-chip module designed
+ * for low field magnetic sensing.
+ *
+ * Copyright (C) 2010 Texas Instruments
+ *
+ * Author: Shubhrajyoti Datta <shubhrajyoti@ti.com>
+ * Acknowledgment: Jonathan Cameron <jic23@kernel.org> for valuable inputs.
+ * Support for HMC5883 and HMC5883L by Peter Meerwald <pmeerw@pmeerw.net>.
+ * Split to multiple files by Josef Gajdusek <atx@atx.name> - 2014
+ *
+ * This program is free software; you can redistribute it and/or 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.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/triggered_buffer.h>
+#include <linux/delay.h>
+
+#include "hmc5843.h"
+
+/*
+ * Range gain settings in (+-)Ga
+ * Beware: HMC5843 and HMC5883 have different recommended sensor field
+ * ranges; default corresponds to +-1.0 Ga and +-1.3 Ga, respectively
+ */
+#define HMC5843_RANGE_GAIN_OFFSET 0x05
+#define HMC5843_RANGE_GAIN_DEFAULT 0x01
+#define HMC5843_RANGE_GAIN_MASK 0xe0
+
+/* Device status */
+#define HMC5843_DATA_READY 0x01
+#define HMC5843_DATA_OUTPUT_LOCK 0x02
+
+/* Mode register configuration */
+#define HMC5843_MODE_CONVERSION_CONTINUOUS 0x00
+#define HMC5843_MODE_CONVERSION_SINGLE 0x01
+#define HMC5843_MODE_IDLE 0x02
+#define HMC5843_MODE_SLEEP 0x03
+#define HMC5843_MODE_MASK 0x03
+
+/*
+ * HMC5843: Minimum data output rate
+ * HMC5883: Typical data output rate
+ */
+#define HMC5843_RATE_OFFSET 0x02
+#define HMC5843_RATE_DEFAULT 0x04
+#define HMC5843_RATE_MASK 0x1c
+
+/* Device measurement configuration */
+#define HMC5843_MEAS_CONF_NORMAL 0x00
+#define HMC5843_MEAS_CONF_POSITIVE_BIAS 0x01
+#define HMC5843_MEAS_CONF_NEGATIVE_BIAS 0x02
+#define HMC5843_MEAS_CONF_MASK 0x03
+
+/* Scaling factors: 10000000/Gain */
+static const int hmc5843_regval_to_nanoscale[] = {
+ 6173, 7692, 10309, 12821, 18868, 21739, 25641, 35714
+};
+
+static const int hmc5883_regval_to_nanoscale[] = {
+ 7812, 9766, 13021, 16287, 24096, 27701, 32573, 45662
+};
+
+static const int hmc5883l_regval_to_nanoscale[] = {
+ 7299, 9174, 12195, 15152, 22727, 25641, 30303, 43478
+};
+
+/*
+ * From the datasheet:
+ * Value | HMC5843 | HMC5883/HMC5883L
+ * | Data output rate (Hz) | Data output rate (Hz)
+ * 0 | 0.5 | 0.75
+ * 1 | 1 | 1.5
+ * 2 | 2 | 3
+ * 3 | 5 | 7.5
+ * 4 | 10 (default) | 15
+ * 5 | 20 | 30
+ * 6 | 50 | 75
+ * 7 | Not used | Not used
+ */
+static const int hmc5843_regval_to_samp_freq[][2] = {
+ {0, 500000}, {1, 0}, {2, 0}, {5, 0}, {10, 0}, {20, 0}, {50, 0}
+};
+
+static const int hmc5883_regval_to_samp_freq[][2] = {
+ {0, 750000}, {1, 500000}, {3, 0}, {7, 500000}, {15, 0}, {30, 0},
+ {75, 0}
+};
+
+static const int hmc5983_regval_to_samp_freq[][2] = {
+ {0, 750000}, {1, 500000}, {3, 0}, {7, 500000}, {15, 0}, {30, 0},
+ {75, 0}, {220, 0}
+};
+
+/* Describe chip variants */
+struct hmc5843_chip_info {
+ const struct iio_chan_spec *channels;
+ const int (*regval_to_samp_freq)[2];
+ const int n_regval_to_samp_freq;
+ const int *regval_to_nanoscale;
+ const int n_regval_to_nanoscale;
+};
+
+/* The lower two bits contain the current conversion mode */
+static s32 hmc5843_set_mode(struct hmc5843_data *data, u8 operating_mode)
+{
+ int ret;
+
+ mutex_lock(&data->lock);
+ ret = regmap_update_bits(data->regmap, HMC5843_MODE_REG,
+ HMC5843_MODE_MASK, operating_mode);
+ mutex_unlock(&data->lock);
+
+ return ret;
+}
+
+static int hmc5843_wait_measurement(struct hmc5843_data *data)
+{
+ int tries = 150;
+ unsigned int val;
+ int ret;
+
+ while (tries-- > 0) {
+ ret = regmap_read(data->regmap, HMC5843_STATUS_REG, &val);
+ if (ret < 0)
+ return ret;
+ if (val & HMC5843_DATA_READY)
+ break;
+ msleep(20);
+ }
+
+ if (tries < 0) {
+ dev_err(data->dev, "data not ready\n");
+ return -EIO;
+ }
+
+ return 0;
+}
+
+/* Return the measurement value from the specified channel */
+static int hmc5843_read_measurement(struct hmc5843_data *data,
+ int idx, int *val)
+{
+ __be16 values[3];
+ int ret;
+
+ mutex_lock(&data->lock);
+ ret = hmc5843_wait_measurement(data);
+ if (ret < 0) {
+ mutex_unlock(&data->lock);
+ return ret;
+ }
+ ret = regmap_bulk_read(data->regmap, HMC5843_DATA_OUT_MSB_REGS,
+ values, sizeof(values));
+ mutex_unlock(&data->lock);
+ if (ret < 0)
+ return ret;
+
+ *val = sign_extend32(be16_to_cpu(values[idx]), 15);
+ return IIO_VAL_INT;
+}
+
+/*
+ * API for setting the measurement configuration to
+ * Normal, Positive bias and Negative bias
+ *
+ * From the datasheet:
+ * 0 - Normal measurement configuration (default): In normal measurement
+ * configuration the device follows normal measurement flow. Pins BP
+ * and BN are left floating and high impedance.
+ *
+ * 1 - Positive bias configuration: In positive bias configuration, a
+ * positive current is forced across the resistive load on pins BP
+ * and BN.
+ *
+ * 2 - Negative bias configuration. In negative bias configuration, a
+ * negative current is forced across the resistive load on pins BP
+ * and BN.
+ *
+ */
+static int hmc5843_set_meas_conf(struct hmc5843_data *data, u8 meas_conf)
+{
+ int ret;
+
+ mutex_lock(&data->lock);
+ ret = regmap_update_bits(data->regmap, HMC5843_CONFIG_REG_A,
+ HMC5843_MEAS_CONF_MASK, meas_conf);
+ mutex_unlock(&data->lock);
+
+ return ret;
+}
+
+static
+ssize_t hmc5843_show_measurement_configuration(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct hmc5843_data *data = iio_priv(dev_to_iio_dev(dev));
+ unsigned int val;
+ int ret;
+
+ ret = regmap_read(data->regmap, HMC5843_CONFIG_REG_A, &val);
+ if (ret)
+ return ret;
+ val &= HMC5843_MEAS_CONF_MASK;
+
+ return sprintf(buf, "%d\n", val);
+}
+
+static
+ssize_t hmc5843_set_measurement_configuration(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t count)
+{
+ struct hmc5843_data *data = iio_priv(dev_to_iio_dev(dev));
+ unsigned long meas_conf = 0;
+ int ret;
+
+ ret = kstrtoul(buf, 10, &meas_conf);
+ if (ret)
+ return ret;
+ if (meas_conf >= HMC5843_MEAS_CONF_MASK)
+ return -EINVAL;
+
+ ret = hmc5843_set_meas_conf(data, meas_conf);
+
+ return (ret < 0) ? ret : count;
+}
+
+static IIO_DEVICE_ATTR(meas_conf,
+ S_IWUSR | S_IRUGO,
+ hmc5843_show_measurement_configuration,
+ hmc5843_set_measurement_configuration,
+ 0);
+
+static
+ssize_t hmc5843_show_samp_freq_avail(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct hmc5843_data *data = iio_priv(dev_to_iio_dev(dev));
+ size_t len = 0;
+ int i;
+
+ for (i = 0; i < data->variant->n_regval_to_samp_freq; i++)
+ len += scnprintf(buf + len, PAGE_SIZE - len,
+ "%d.%d ", data->variant->regval_to_samp_freq[i][0],
+ data->variant->regval_to_samp_freq[i][1]);
+
+ /* replace trailing space by newline */
+ buf[len - 1] = '\n';
+
+ return len;
+}
+
+static IIO_DEV_ATTR_SAMP_FREQ_AVAIL(hmc5843_show_samp_freq_avail);
+
+static int hmc5843_set_samp_freq(struct hmc5843_data *data, u8 rate)
+{
+ int ret;
+
+ mutex_lock(&data->lock);
+ ret = regmap_update_bits(data->regmap, HMC5843_CONFIG_REG_A,
+ HMC5843_RATE_MASK,
+ rate << HMC5843_RATE_OFFSET);
+ mutex_unlock(&data->lock);
+
+ return ret;
+}
+
+static int hmc5843_get_samp_freq_index(struct hmc5843_data *data,
+ int val, int val2)
+{
+ int i;
+
+ for (i = 0; i < data->variant->n_regval_to_samp_freq; i++)
+ if (val == data->variant->regval_to_samp_freq[i][0] &&
+ val2 == data->variant->regval_to_samp_freq[i][1])
+ return i;
+
+ return -EINVAL;
+}
+
+static int hmc5843_set_range_gain(struct hmc5843_data *data, u8 range)
+{
+ int ret;
+
+ mutex_lock(&data->lock);
+ ret = regmap_update_bits(data->regmap, HMC5843_CONFIG_REG_B,
+ HMC5843_RANGE_GAIN_MASK,
+ range << HMC5843_RANGE_GAIN_OFFSET);
+ mutex_unlock(&data->lock);
+
+ return ret;
+}
+
+static ssize_t hmc5843_show_scale_avail(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct hmc5843_data *data = iio_priv(dev_to_iio_dev(dev));
+
+ size_t len = 0;
+ int i;
+
+ for (i = 0; i < data->variant->n_regval_to_nanoscale; i++)
+ len += scnprintf(buf + len, PAGE_SIZE - len,
+ "0.%09d ", data->variant->regval_to_nanoscale[i]);
+
+ /* replace trailing space by newline */
+ buf[len - 1] = '\n';
+
+ return len;
+}
+
+static IIO_DEVICE_ATTR(scale_available, S_IRUGO,
+ hmc5843_show_scale_avail, NULL, 0);
+
+static int hmc5843_get_scale_index(struct hmc5843_data *data, int val, int val2)
+{
+ int i;
+
+ if (val != 0)
+ return -EINVAL;
+
+ for (i = 0; i < data->variant->n_regval_to_nanoscale; i++)
+ if (val2 == data->variant->regval_to_nanoscale[i])
+ return i;
+
+ return -EINVAL;
+}
+
+static int hmc5843_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask)
+{
+ struct hmc5843_data *data = iio_priv(indio_dev);
+ unsigned int rval;
+ int ret;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ return hmc5843_read_measurement(data, chan->scan_index, val);
+ case IIO_CHAN_INFO_SCALE:
+ ret = regmap_read(data->regmap, HMC5843_CONFIG_REG_B, &rval);
+ if (ret < 0)
+ return ret;
+ rval >>= HMC5843_RANGE_GAIN_OFFSET;
+ *val = 0;
+ *val2 = data->variant->regval_to_nanoscale[rval];
+ return IIO_VAL_INT_PLUS_NANO;
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ ret = regmap_read(data->regmap, HMC5843_CONFIG_REG_A, &rval);
+ if (ret < 0)
+ return ret;
+ rval >>= HMC5843_RATE_OFFSET;
+ *val = data->variant->regval_to_samp_freq[rval][0];
+ *val2 = data->variant->regval_to_samp_freq[rval][1];
+ return IIO_VAL_INT_PLUS_MICRO;
+ }
+ return -EINVAL;
+}
+
+static int hmc5843_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int val, int val2, long mask)
+{
+ struct hmc5843_data *data = iio_priv(indio_dev);
+ int rate, range;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ rate = hmc5843_get_samp_freq_index(data, val, val2);
+ if (rate < 0)
+ return -EINVAL;
+
+ return hmc5843_set_samp_freq(data, rate);
+ case IIO_CHAN_INFO_SCALE:
+ range = hmc5843_get_scale_index(data, val, val2);
+ if (range < 0)
+ return -EINVAL;
+
+ return hmc5843_set_range_gain(data, range);
+ default:
+ return -EINVAL;
+ }
+}
+
+static int hmc5843_write_raw_get_fmt(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ long mask)
+{
+ switch (mask) {
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ return IIO_VAL_INT_PLUS_MICRO;
+ case IIO_CHAN_INFO_SCALE:
+ return IIO_VAL_INT_PLUS_NANO;
+ default:
+ return -EINVAL;
+ }
+}
+
+static irqreturn_t hmc5843_trigger_handler(int irq, void *p)
+{
+ struct iio_poll_func *pf = p;
+ struct iio_dev *indio_dev = pf->indio_dev;
+ struct hmc5843_data *data = iio_priv(indio_dev);
+ int ret;
+
+ mutex_lock(&data->lock);
+ ret = hmc5843_wait_measurement(data);
+ if (ret < 0) {
+ mutex_unlock(&data->lock);
+ goto done;
+ }
+
+ ret = regmap_bulk_read(data->regmap, HMC5843_DATA_OUT_MSB_REGS,
+ data->buffer, 3 * sizeof(__be16));
+
+ mutex_unlock(&data->lock);
+ if (ret < 0)
+ goto done;
+
+ iio_push_to_buffers_with_timestamp(indio_dev, data->buffer,
+ iio_get_time_ns());
+
+done:
+ iio_trigger_notify_done(indio_dev->trig);
+
+ return IRQ_HANDLED;
+}
+
+#define HMC5843_CHANNEL(axis, idx) \
+ { \
+ .type = IIO_MAGN, \
+ .modified = 1, \
+ .channel2 = IIO_MOD_##axis, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \
+ BIT(IIO_CHAN_INFO_SAMP_FREQ), \
+ .scan_index = idx, \
+ .scan_type = { \
+ .sign = 's', \
+ .realbits = 16, \
+ .storagebits = 16, \
+ .endianness = IIO_BE, \
+ }, \
+ }
+
+static const struct iio_chan_spec hmc5843_channels[] = {
+ HMC5843_CHANNEL(X, 0),
+ HMC5843_CHANNEL(Y, 1),
+ HMC5843_CHANNEL(Z, 2),
+ IIO_CHAN_SOFT_TIMESTAMP(3),
+};
+
+/* Beware: Y and Z are exchanged on HMC5883 and 5983 */
+static const struct iio_chan_spec hmc5883_channels[] = {
+ HMC5843_CHANNEL(X, 0),
+ HMC5843_CHANNEL(Z, 1),
+ HMC5843_CHANNEL(Y, 2),
+ IIO_CHAN_SOFT_TIMESTAMP(3),
+};
+
+static struct attribute *hmc5843_attributes[] = {
+ &iio_dev_attr_meas_conf.dev_attr.attr,
+ &iio_dev_attr_scale_available.dev_attr.attr,
+ &iio_dev_attr_sampling_frequency_available.dev_attr.attr,
+ NULL
+};
+
+static const struct attribute_group hmc5843_group = {
+ .attrs = hmc5843_attributes,
+};
+
+static const struct hmc5843_chip_info hmc5843_chip_info_tbl[] = {
+ [HMC5843_ID] = {
+ .channels = hmc5843_channels,
+ .regval_to_samp_freq = hmc5843_regval_to_samp_freq,
+ .n_regval_to_samp_freq =
+ ARRAY_SIZE(hmc5843_regval_to_samp_freq),
+ .regval_to_nanoscale = hmc5843_regval_to_nanoscale,
+ .n_regval_to_nanoscale =
+ ARRAY_SIZE(hmc5843_regval_to_nanoscale),
+ },
+ [HMC5883_ID] = {
+ .channels = hmc5883_channels,
+ .regval_to_samp_freq = hmc5883_regval_to_samp_freq,
+ .n_regval_to_samp_freq =
+ ARRAY_SIZE(hmc5883_regval_to_samp_freq),
+ .regval_to_nanoscale = hmc5883_regval_to_nanoscale,
+ .n_regval_to_nanoscale =
+ ARRAY_SIZE(hmc5883_regval_to_nanoscale),
+ },
+ [HMC5883L_ID] = {
+ .channels = hmc5883_channels,
+ .regval_to_samp_freq = hmc5883_regval_to_samp_freq,
+ .n_regval_to_samp_freq =
+ ARRAY_SIZE(hmc5883_regval_to_samp_freq),
+ .regval_to_nanoscale = hmc5883l_regval_to_nanoscale,
+ .n_regval_to_nanoscale =
+ ARRAY_SIZE(hmc5883l_regval_to_nanoscale),
+ },
+ [HMC5983_ID] = {
+ .channels = hmc5883_channels,
+ .regval_to_samp_freq = hmc5983_regval_to_samp_freq,
+ .n_regval_to_samp_freq =
+ ARRAY_SIZE(hmc5983_regval_to_samp_freq),
+ .regval_to_nanoscale = hmc5883l_regval_to_nanoscale,
+ .n_regval_to_nanoscale =
+ ARRAY_SIZE(hmc5883l_regval_to_nanoscale),
+ }
+};
+
+static int hmc5843_init(struct hmc5843_data *data)
+{
+ int ret;
+ u8 id[3];
+
+ ret = regmap_bulk_read(data->regmap, HMC5843_ID_REG,
+ id, ARRAY_SIZE(id));
+ if (ret < 0)
+ return ret;
+ if (id[0] != 'H' || id[1] != '4' || id[2] != '3') {
+ dev_err(data->dev, "no HMC5843/5883/5883L/5983 sensor\n");
+ return -ENODEV;
+ }
+
+ ret = hmc5843_set_meas_conf(data, HMC5843_MEAS_CONF_NORMAL);
+ if (ret < 0)
+ return ret;
+ ret = hmc5843_set_samp_freq(data, HMC5843_RATE_DEFAULT);
+ if (ret < 0)
+ return ret;
+ ret = hmc5843_set_range_gain(data, HMC5843_RANGE_GAIN_DEFAULT);
+ if (ret < 0)
+ return ret;
+ return hmc5843_set_mode(data, HMC5843_MODE_CONVERSION_CONTINUOUS);
+}
+
+static const struct iio_info hmc5843_info = {
+ .attrs = &hmc5843_group,
+ .read_raw = &hmc5843_read_raw,
+ .write_raw = &hmc5843_write_raw,
+ .write_raw_get_fmt = &hmc5843_write_raw_get_fmt,
+ .driver_module = THIS_MODULE,
+};
+
+static const unsigned long hmc5843_scan_masks[] = {0x7, 0};
+
+int hmc5843_common_suspend(struct device *dev)
+{
+ return hmc5843_set_mode(iio_priv(dev_get_drvdata(dev)),
+ HMC5843_MODE_CONVERSION_CONTINUOUS);
+}
+EXPORT_SYMBOL(hmc5843_common_suspend);
+
+int hmc5843_common_resume(struct device *dev)
+{
+ return hmc5843_set_mode(iio_priv(dev_get_drvdata(dev)),
+ HMC5843_MODE_SLEEP);
+}
+EXPORT_SYMBOL(hmc5843_common_resume);
+
+int hmc5843_common_probe(struct device *dev, struct regmap *regmap,
+ enum hmc5843_ids id)
+{
+ struct hmc5843_data *data;
+ struct iio_dev *indio_dev;
+ int ret;
+
+ indio_dev = devm_iio_device_alloc(dev, sizeof(*data));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ dev_set_drvdata(dev, indio_dev);
+
+ /* default settings at probe */
+ data = iio_priv(indio_dev);
+ data->dev = dev;
+ data->regmap = regmap;
+ data->variant = &hmc5843_chip_info_tbl[id];
+ mutex_init(&data->lock);
+
+ indio_dev->dev.parent = dev;
+ indio_dev->name = dev->driver->name;
+ indio_dev->info = &hmc5843_info;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->channels = data->variant->channels;
+ indio_dev->num_channels = 4;
+ indio_dev->available_scan_masks = hmc5843_scan_masks;
+
+ ret = hmc5843_init(data);
+ if (ret < 0)
+ return ret;
+
+ ret = iio_triggered_buffer_setup(indio_dev, NULL,
+ hmc5843_trigger_handler, NULL);
+ if (ret < 0)
+ goto buffer_setup_err;
+
+ ret = iio_device_register(indio_dev);
+ if (ret < 0)
+ goto buffer_cleanup;
+
+ return 0;
+
+buffer_cleanup:
+ iio_triggered_buffer_cleanup(indio_dev);
+buffer_setup_err:
+ hmc5843_set_mode(iio_priv(indio_dev), HMC5843_MODE_SLEEP);
+ return ret;
+}
+EXPORT_SYMBOL(hmc5843_common_probe);
+
+int hmc5843_common_remove(struct device *dev)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+
+ iio_device_unregister(indio_dev);
+ iio_triggered_buffer_cleanup(indio_dev);
+
+ /* sleep mode to save power */
+ hmc5843_set_mode(iio_priv(indio_dev), HMC5843_MODE_SLEEP);
+
+ return 0;
+}
+EXPORT_SYMBOL(hmc5843_common_remove);
+
+MODULE_AUTHOR("Shubhrajyoti Datta <shubhrajyoti@ti.com>");
+MODULE_DESCRIPTION("HMC5843/5883/5883L/5983 core driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/iio/magnetometer/hmc5843_i2c.c b/drivers/staging/iio/magnetometer/hmc5843_i2c.c
new file mode 100644
index 000000000..ff08667fa
--- /dev/null
+++ b/drivers/staging/iio/magnetometer/hmc5843_i2c.c
@@ -0,0 +1,104 @@
+/*
+ * i2c driver for hmc5843/5843/5883/5883l/5983
+ *
+ * Split from hmc5843.c
+ * Copyright (C) Josef Gajdusek <atx@atx.name>
+ *
+ * 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/module.h>
+#include <linux/i2c.h>
+#include <linux/regmap.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/triggered_buffer.h>
+
+#include "hmc5843.h"
+
+static const struct regmap_range hmc5843_readable_ranges[] = {
+ regmap_reg_range(0, HMC5843_ID_END),
+};
+
+static const struct regmap_access_table hmc5843_readable_table = {
+ .yes_ranges = hmc5843_readable_ranges,
+ .n_yes_ranges = ARRAY_SIZE(hmc5843_readable_ranges),
+};
+
+static const struct regmap_range hmc5843_writable_ranges[] = {
+ regmap_reg_range(0, HMC5843_MODE_REG),
+};
+
+static const struct regmap_access_table hmc5843_writable_table = {
+ .yes_ranges = hmc5843_writable_ranges,
+ .n_yes_ranges = ARRAY_SIZE(hmc5843_writable_ranges),
+};
+
+static const struct regmap_range hmc5843_volatile_ranges[] = {
+ regmap_reg_range(HMC5843_DATA_OUT_MSB_REGS, HMC5843_STATUS_REG),
+};
+
+static const struct regmap_access_table hmc5843_volatile_table = {
+ .yes_ranges = hmc5843_volatile_ranges,
+ .n_yes_ranges = ARRAY_SIZE(hmc5843_volatile_ranges),
+};
+
+static const struct regmap_config hmc5843_i2c_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+
+ .rd_table = &hmc5843_readable_table,
+ .wr_table = &hmc5843_writable_table,
+ .volatile_table = &hmc5843_volatile_table,
+
+ .cache_type = REGCACHE_RBTREE,
+};
+
+static int hmc5843_i2c_probe(struct i2c_client *cli,
+ const struct i2c_device_id *id)
+{
+ return hmc5843_common_probe(&cli->dev,
+ devm_regmap_init_i2c(cli, &hmc5843_i2c_regmap_config),
+ id->driver_data);
+}
+
+static int hmc5843_i2c_remove(struct i2c_client *client)
+{
+ return hmc5843_common_remove(&client->dev);
+}
+
+static const struct i2c_device_id hmc5843_id[] = {
+ { "hmc5843", HMC5843_ID },
+ { "hmc5883", HMC5883_ID },
+ { "hmc5883l", HMC5883L_ID },
+ { "hmc5983", HMC5983_ID },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, hmc5843_id);
+
+static const struct of_device_id hmc5843_of_match[] = {
+ { .compatible = "honeywell,hmc5843", .data = (void *)HMC5843_ID },
+ { .compatible = "honeywell,hmc5883", .data = (void *)HMC5883_ID },
+ { .compatible = "honeywell,hmc5883l", .data = (void *)HMC5883L_ID },
+ { .compatible = "honeywell,hmc5983", .data = (void *)HMC5983_ID },
+ {}
+};
+MODULE_DEVICE_TABLE(of, hmc5843_of_match);
+
+static struct i2c_driver hmc5843_driver = {
+ .driver = {
+ .name = "hmc5843",
+ .pm = HMC5843_PM_OPS,
+ .of_match_table = hmc5843_of_match,
+ },
+ .id_table = hmc5843_id,
+ .probe = hmc5843_i2c_probe,
+ .remove = hmc5843_i2c_remove,
+};
+module_i2c_driver(hmc5843_driver);
+
+MODULE_AUTHOR("Josef Gajdusek <atx@atx.name>");
+MODULE_DESCRIPTION("HMC5843/5883/5883L/5983 i2c driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/iio/magnetometer/hmc5843_spi.c b/drivers/staging/iio/magnetometer/hmc5843_spi.c
new file mode 100644
index 000000000..8e658f736
--- /dev/null
+++ b/drivers/staging/iio/magnetometer/hmc5843_spi.c
@@ -0,0 +1,100 @@
+/*
+ * SPI driver for hmc5983
+ *
+ * Copyright (C) Josef Gajdusek <atx@atx.name>
+ *
+ * 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/module.h>
+#include <linux/spi/spi.h>
+#include <linux/iio/iio.h>
+
+#include "hmc5843.h"
+
+static const struct regmap_range hmc5843_readable_ranges[] = {
+ regmap_reg_range(0, HMC5843_ID_END),
+};
+
+static const struct regmap_access_table hmc5843_readable_table = {
+ .yes_ranges = hmc5843_readable_ranges,
+ .n_yes_ranges = ARRAY_SIZE(hmc5843_readable_ranges),
+};
+
+static const struct regmap_range hmc5843_writable_ranges[] = {
+ regmap_reg_range(0, HMC5843_MODE_REG),
+};
+
+static const struct regmap_access_table hmc5843_writable_table = {
+ .yes_ranges = hmc5843_writable_ranges,
+ .n_yes_ranges = ARRAY_SIZE(hmc5843_writable_ranges),
+};
+
+static const struct regmap_range hmc5843_volatile_ranges[] = {
+ regmap_reg_range(HMC5843_DATA_OUT_MSB_REGS, HMC5843_STATUS_REG),
+};
+
+static const struct regmap_access_table hmc5843_volatile_table = {
+ .yes_ranges = hmc5843_volatile_ranges,
+ .n_yes_ranges = ARRAY_SIZE(hmc5843_volatile_ranges),
+};
+
+static const struct regmap_config hmc5843_spi_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+
+ .rd_table = &hmc5843_readable_table,
+ .wr_table = &hmc5843_writable_table,
+ .volatile_table = &hmc5843_volatile_table,
+
+ /* Autoincrement address pointer */
+ .read_flag_mask = 0xc0,
+
+ .cache_type = REGCACHE_RBTREE,
+};
+
+static int hmc5843_spi_probe(struct spi_device *spi)
+{
+ int ret;
+
+ spi->mode = SPI_MODE_3;
+ spi->max_speed_hz = 8000000;
+ spi->bits_per_word = 8;
+ ret = spi_setup(spi);
+ if (ret)
+ return ret;
+
+ return hmc5843_common_probe(&spi->dev,
+ devm_regmap_init_spi(spi, &hmc5843_spi_regmap_config),
+ HMC5983_ID);
+}
+
+static int hmc5843_spi_remove(struct spi_device *spi)
+{
+ return hmc5843_common_remove(&spi->dev);
+}
+
+static const struct spi_device_id hmc5843_id[] = {
+ { "hmc5983", HMC5983_ID },
+ { }
+};
+
+static struct spi_driver hmc5843_driver = {
+ .driver = {
+ .name = "hmc5843",
+ .pm = HMC5843_PM_OPS,
+ .owner = THIS_MODULE,
+ },
+ .id_table = hmc5843_id,
+ .probe = hmc5843_spi_probe,
+ .remove = hmc5843_spi_remove,
+};
+
+module_spi_driver(hmc5843_driver);
+
+MODULE_AUTHOR("Josef Gajdusek <atx@atx.name>");
+MODULE_DESCRIPTION("HMC5983 SPI driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/iio/meter/Kconfig b/drivers/staging/iio/meter/Kconfig
new file mode 100644
index 000000000..64cd3704e
--- /dev/null
+++ b/drivers/staging/iio/meter/Kconfig
@@ -0,0 +1,78 @@
+#
+# IIO meter drivers configuration
+#
+menu "Active energy metering IC"
+
+config ADE7753
+ tristate "Analog Devices ADE7753/6 Single-Phase Multifunction Metering IC Driver"
+ depends on SPI
+ help
+ Say yes here to build support for Analog Devices ADE7753 Single-Phase Multifunction
+ Metering IC with di/dt Sensor Interface.
+
+ To compile this driver as a module, choose M here: the
+ module will be called ade7753.
+
+config ADE7754
+ tristate "Analog Devices ADE7754 Polyphase Multifunction Energy Metering IC Driver"
+ depends on SPI
+ help
+ Say yes here to build support for Analog Devices ADE7754 Polyphase
+ Multifunction Energy Metering IC Driver.
+
+ To compile this driver as a module, choose M here: the
+ module will be called ade7754.
+
+config ADE7758
+ tristate "Analog Devices ADE7758 Poly Phase Multifunction Energy Metering IC Driver"
+ depends on SPI
+ select IIO_TRIGGER if IIO_BUFFER
+ select IIO_KFIFO_BUF if IIO_BUFFER
+ help
+ Say yes here to build support for Analog Devices ADE7758 Polyphase
+ Multifunction Energy Metering IC with Per Phase Information Driver.
+
+ To compile this driver as a module, choose M here: the
+ module will be called ade7758.
+
+config ADE7759
+ tristate "Analog Devices ADE7759 Active Energy Metering IC Driver"
+ depends on SPI
+ help
+ Say yes here to build support for Analog Devices ADE7758 Active Energy
+ Metering IC with di/dt Sensor Interface.
+
+ To compile this driver as a module, choose M here: the
+ module will be called ade7759.
+
+config ADE7854
+ tristate "Analog Devices ADE7854/58/68/78 Polyphase Multifunction Energy Metering IC Driver"
+ depends on SPI || I2C
+ help
+ Say yes here to build support for Analog Devices ADE7854/58/68/78 Polyphase
+ Multifunction Energy Metering IC Driver.
+
+ To compile this driver as a module, choose M here: the
+ module will be called ade7854.
+
+config ADE7854_I2C
+ tristate "support I2C bus connection"
+ depends on ADE7854 && I2C
+ default y
+ help
+ Say Y here if you have ADE7854/58/68/78 hooked to an I2C bus.
+
+ To compile this driver as a module, choose M here: the
+ module will be called ade7854-i2c.
+
+config ADE7854_SPI
+ tristate "support SPI bus connection"
+ depends on ADE7854 && SPI
+ default y
+ help
+ Say Y here if you have ADE7854/58/68/78 hooked to a SPI bus.
+
+ To compile this driver as a module, choose M here: the
+ module will be called ade7854-spi.
+
+endmenu
diff --git a/drivers/staging/iio/meter/Makefile b/drivers/staging/iio/meter/Makefile
new file mode 100644
index 000000000..de3863d6b
--- /dev/null
+++ b/drivers/staging/iio/meter/Makefile
@@ -0,0 +1,15 @@
+#
+# Makefile for metering ic drivers
+#
+
+obj-$(CONFIG_ADE7753) += ade7753.o
+obj-$(CONFIG_ADE7754) += ade7754.o
+
+ade7758-y := ade7758_core.o
+ade7758-$(CONFIG_IIO_BUFFER) += ade7758_ring.o ade7758_trigger.o
+obj-$(CONFIG_ADE7758) += ade7758.o
+
+obj-$(CONFIG_ADE7759) += ade7759.o
+obj-$(CONFIG_ADE7854) += ade7854.o
+obj-$(CONFIG_ADE7854_I2C) += ade7854-i2c.o
+obj-$(CONFIG_ADE7854_SPI) += ade7854-spi.o
diff --git a/drivers/staging/iio/meter/ade7753.c b/drivers/staging/iio/meter/ade7753.c
new file mode 100644
index 000000000..ffc7f0ddf
--- /dev/null
+++ b/drivers/staging/iio/meter/ade7753.c
@@ -0,0 +1,547 @@
+/*
+ * ADE7753 Single-Phase Multifunction Metering IC with di/dt Sensor Interface
+ *
+ * Copyright 2010 Analog Devices Inc.
+ *
+ * Licensed under the GPL-2 or later.
+ */
+
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/delay.h>
+#include <linux/mutex.h>
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/spi/spi.h>
+#include <linux/slab.h>
+#include <linux/sysfs.h>
+#include <linux/list.h>
+#include <linux/module.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include "meter.h"
+#include "ade7753.h"
+
+static int ade7753_spi_write_reg_8(struct device *dev,
+ u8 reg_address,
+ u8 val)
+{
+ int ret;
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct ade7753_state *st = iio_priv(indio_dev);
+
+ mutex_lock(&st->buf_lock);
+ st->tx[0] = ADE7753_WRITE_REG(reg_address);
+ st->tx[1] = val;
+
+ ret = spi_write(st->us, st->tx, 2);
+ mutex_unlock(&st->buf_lock);
+
+ return ret;
+}
+
+static int ade7753_spi_write_reg_16(struct device *dev,
+ u8 reg_address,
+ u16 value)
+{
+ int ret;
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct ade7753_state *st = iio_priv(indio_dev);
+
+ mutex_lock(&st->buf_lock);
+ st->tx[0] = ADE7753_WRITE_REG(reg_address);
+ st->tx[1] = (value >> 8) & 0xFF;
+ st->tx[2] = value & 0xFF;
+ ret = spi_write(st->us, st->tx, 3);
+ mutex_unlock(&st->buf_lock);
+
+ return ret;
+}
+
+static int ade7753_spi_read_reg_8(struct device *dev,
+ u8 reg_address,
+ u8 *val)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct ade7753_state *st = iio_priv(indio_dev);
+ ssize_t ret;
+
+ ret = spi_w8r8(st->us, ADE7753_READ_REG(reg_address));
+ if (ret < 0) {
+ dev_err(&st->us->dev, "problem when reading 8 bit register 0x%02X",
+ reg_address);
+ return ret;
+ }
+ *val = ret;
+
+ return 0;
+}
+
+static int ade7753_spi_read_reg_16(struct device *dev,
+ u8 reg_address,
+ u16 *val)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct ade7753_state *st = iio_priv(indio_dev);
+ ssize_t ret;
+
+ ret = spi_w8r16be(st->us, ADE7753_READ_REG(reg_address));
+ if (ret < 0) {
+ dev_err(&st->us->dev, "problem when reading 16 bit register 0x%02X",
+ reg_address);
+ return ret;
+ }
+
+ *val = ret;
+
+ return 0;
+}
+
+static int ade7753_spi_read_reg_24(struct device *dev,
+ u8 reg_address,
+ u32 *val)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct ade7753_state *st = iio_priv(indio_dev);
+ int ret;
+ struct spi_transfer xfers[] = {
+ {
+ .tx_buf = st->tx,
+ .bits_per_word = 8,
+ .len = 1,
+ }, {
+ .rx_buf = st->tx,
+ .bits_per_word = 8,
+ .len = 3,
+ }
+ };
+
+ mutex_lock(&st->buf_lock);
+ st->tx[0] = ADE7753_READ_REG(reg_address);
+
+ ret = spi_sync_transfer(st->us, xfers, ARRAY_SIZE(xfers));
+ if (ret) {
+ dev_err(&st->us->dev, "problem when reading 24 bit register 0x%02X",
+ reg_address);
+ goto error_ret;
+ }
+ *val = (st->rx[0] << 16) | (st->rx[1] << 8) | st->rx[2];
+
+error_ret:
+ mutex_unlock(&st->buf_lock);
+ return ret;
+}
+
+static ssize_t ade7753_read_8bit(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ int ret;
+ u8 val;
+ struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
+
+ ret = ade7753_spi_read_reg_8(dev, this_attr->address, &val);
+ if (ret)
+ return ret;
+
+ return sprintf(buf, "%u\n", val);
+}
+
+static ssize_t ade7753_read_16bit(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ int ret;
+ u16 val;
+ struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
+
+ ret = ade7753_spi_read_reg_16(dev, this_attr->address, &val);
+ if (ret)
+ return ret;
+
+ return sprintf(buf, "%u\n", val);
+}
+
+static ssize_t ade7753_read_24bit(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ int ret;
+ u32 val;
+ struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
+
+ ret = ade7753_spi_read_reg_24(dev, this_attr->address, &val);
+ if (ret)
+ return ret;
+
+ return sprintf(buf, "%u\n", val);
+}
+
+static ssize_t ade7753_write_8bit(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t len)
+{
+ struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
+ int ret;
+ u8 val;
+
+ ret = kstrtou8(buf, 10, &val);
+ if (ret)
+ goto error_ret;
+ ret = ade7753_spi_write_reg_8(dev, this_attr->address, val);
+
+error_ret:
+ return ret ? ret : len;
+}
+
+static ssize_t ade7753_write_16bit(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t len)
+{
+ struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
+ int ret;
+ u16 val;
+
+ ret = kstrtou16(buf, 10, &val);
+ if (ret)
+ goto error_ret;
+ ret = ade7753_spi_write_reg_16(dev, this_attr->address, val);
+
+error_ret:
+ return ret ? ret : len;
+}
+
+static int ade7753_reset(struct device *dev)
+{
+ u16 val;
+
+ ade7753_spi_read_reg_16(dev, ADE7753_MODE, &val);
+ val |= 1 << 6; /* Software Chip Reset */
+
+ return ade7753_spi_write_reg_16(dev, ADE7753_MODE, val);
+}
+
+static IIO_DEV_ATTR_AENERGY(ade7753_read_24bit, ADE7753_AENERGY);
+static IIO_DEV_ATTR_LAENERGY(ade7753_read_24bit, ADE7753_LAENERGY);
+static IIO_DEV_ATTR_VAENERGY(ade7753_read_24bit, ADE7753_VAENERGY);
+static IIO_DEV_ATTR_LVAENERGY(ade7753_read_24bit, ADE7753_LVAENERGY);
+static IIO_DEV_ATTR_CFDEN(S_IWUSR | S_IRUGO,
+ ade7753_read_16bit,
+ ade7753_write_16bit,
+ ADE7753_CFDEN);
+static IIO_DEV_ATTR_CFNUM(S_IWUSR | S_IRUGO,
+ ade7753_read_8bit,
+ ade7753_write_8bit,
+ ADE7753_CFNUM);
+static IIO_DEV_ATTR_CHKSUM(ade7753_read_8bit, ADE7753_CHKSUM);
+static IIO_DEV_ATTR_PHCAL(S_IWUSR | S_IRUGO,
+ ade7753_read_16bit,
+ ade7753_write_16bit,
+ ADE7753_PHCAL);
+static IIO_DEV_ATTR_APOS(S_IWUSR | S_IRUGO,
+ ade7753_read_16bit,
+ ade7753_write_16bit,
+ ADE7753_APOS);
+static IIO_DEV_ATTR_SAGCYC(S_IWUSR | S_IRUGO,
+ ade7753_read_8bit,
+ ade7753_write_8bit,
+ ADE7753_SAGCYC);
+static IIO_DEV_ATTR_SAGLVL(S_IWUSR | S_IRUGO,
+ ade7753_read_8bit,
+ ade7753_write_8bit,
+ ADE7753_SAGLVL);
+static IIO_DEV_ATTR_LINECYC(S_IWUSR | S_IRUGO,
+ ade7753_read_8bit,
+ ade7753_write_8bit,
+ ADE7753_LINECYC);
+static IIO_DEV_ATTR_WDIV(S_IWUSR | S_IRUGO,
+ ade7753_read_8bit,
+ ade7753_write_8bit,
+ ADE7753_WDIV);
+static IIO_DEV_ATTR_IRMS(S_IWUSR | S_IRUGO,
+ ade7753_read_24bit,
+ NULL,
+ ADE7753_IRMS);
+static IIO_DEV_ATTR_VRMS(S_IRUGO,
+ ade7753_read_24bit,
+ NULL,
+ ADE7753_VRMS);
+static IIO_DEV_ATTR_IRMSOS(S_IWUSR | S_IRUGO,
+ ade7753_read_16bit,
+ ade7753_write_16bit,
+ ADE7753_IRMSOS);
+static IIO_DEV_ATTR_VRMSOS(S_IWUSR | S_IRUGO,
+ ade7753_read_16bit,
+ ade7753_write_16bit,
+ ADE7753_VRMSOS);
+static IIO_DEV_ATTR_WGAIN(S_IWUSR | S_IRUGO,
+ ade7753_read_16bit,
+ ade7753_write_16bit,
+ ADE7753_WGAIN);
+static IIO_DEV_ATTR_VAGAIN(S_IWUSR | S_IRUGO,
+ ade7753_read_16bit,
+ ade7753_write_16bit,
+ ADE7753_VAGAIN);
+static IIO_DEV_ATTR_PGA_GAIN(S_IWUSR | S_IRUGO,
+ ade7753_read_16bit,
+ ade7753_write_16bit,
+ ADE7753_GAIN);
+static IIO_DEV_ATTR_IPKLVL(S_IWUSR | S_IRUGO,
+ ade7753_read_8bit,
+ ade7753_write_8bit,
+ ADE7753_IPKLVL);
+static IIO_DEV_ATTR_VPKLVL(S_IWUSR | S_IRUGO,
+ ade7753_read_8bit,
+ ade7753_write_8bit,
+ ADE7753_VPKLVL);
+static IIO_DEV_ATTR_IPEAK(S_IRUGO,
+ ade7753_read_24bit,
+ NULL,
+ ADE7753_IPEAK);
+static IIO_DEV_ATTR_VPEAK(S_IRUGO,
+ ade7753_read_24bit,
+ NULL,
+ ADE7753_VPEAK);
+static IIO_DEV_ATTR_VPERIOD(S_IRUGO,
+ ade7753_read_16bit,
+ NULL,
+ ADE7753_PERIOD);
+static IIO_DEV_ATTR_CH_OFF(1, S_IWUSR | S_IRUGO,
+ ade7753_read_8bit,
+ ade7753_write_8bit,
+ ADE7753_CH1OS);
+static IIO_DEV_ATTR_CH_OFF(2, S_IWUSR | S_IRUGO,
+ ade7753_read_8bit,
+ ade7753_write_8bit,
+ ADE7753_CH2OS);
+
+static int ade7753_set_irq(struct device *dev, bool enable)
+{
+ int ret;
+ u8 irqen;
+
+ ret = ade7753_spi_read_reg_8(dev, ADE7753_IRQEN, &irqen);
+ if (ret)
+ goto error_ret;
+
+ if (enable)
+ irqen |= 1 << 3; /* Enables an interrupt when a data is
+ present in the waveform register */
+ else
+ irqen &= ~(1 << 3);
+
+ ret = ade7753_spi_write_reg_8(dev, ADE7753_IRQEN, irqen);
+
+error_ret:
+ return ret;
+}
+
+/* Power down the device */
+static int ade7753_stop_device(struct device *dev)
+{
+ u16 val;
+
+ ade7753_spi_read_reg_16(dev, ADE7753_MODE, &val);
+ val |= 1 << 4; /* AD converters can be turned off */
+
+ return ade7753_spi_write_reg_16(dev, ADE7753_MODE, val);
+}
+
+static int ade7753_initial_setup(struct iio_dev *indio_dev)
+{
+ int ret;
+ struct device *dev = &indio_dev->dev;
+ struct ade7753_state *st = iio_priv(indio_dev);
+
+ /* use low spi speed for init */
+ st->us->mode = SPI_MODE_3;
+ spi_setup(st->us);
+
+ /* Disable IRQ */
+ ret = ade7753_set_irq(dev, false);
+ if (ret) {
+ dev_err(dev, "disable irq failed");
+ goto err_ret;
+ }
+
+ ade7753_reset(dev);
+ msleep(ADE7753_STARTUP_DELAY);
+
+err_ret:
+ return ret;
+}
+
+static ssize_t ade7753_read_frequency(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ int ret;
+ u16 t;
+ int sps;
+
+ ret = ade7753_spi_read_reg_16(dev, ADE7753_MODE, &t);
+ if (ret)
+ return ret;
+
+ t = (t >> 11) & 0x3;
+ sps = 27900 / (1 + t);
+
+ return sprintf(buf, "%d\n", sps);
+}
+
+static ssize_t ade7753_write_frequency(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t len)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct ade7753_state *st = iio_priv(indio_dev);
+ u16 val;
+ int ret;
+ u16 reg, t;
+
+ ret = kstrtou16(buf, 10, &val);
+ if (ret)
+ return ret;
+ if (val == 0)
+ return -EINVAL;
+
+ mutex_lock(&indio_dev->mlock);
+
+ t = 27900 / val;
+ if (t > 0)
+ t--;
+
+ if (t > 1)
+ st->us->max_speed_hz = ADE7753_SPI_SLOW;
+ else
+ st->us->max_speed_hz = ADE7753_SPI_FAST;
+
+ ret = ade7753_spi_read_reg_16(dev, ADE7753_MODE, &reg);
+ if (ret)
+ goto out;
+
+ reg &= ~(3 << 11);
+ reg |= t << 11;
+
+ ret = ade7753_spi_write_reg_16(dev, ADE7753_MODE, reg);
+
+out:
+ mutex_unlock(&indio_dev->mlock);
+
+ return ret ? ret : len;
+}
+
+static IIO_DEV_ATTR_TEMP_RAW(ade7753_read_8bit);
+static IIO_CONST_ATTR(in_temp_offset, "-25 C");
+static IIO_CONST_ATTR(in_temp_scale, "0.67 C");
+
+static IIO_DEV_ATTR_SAMP_FREQ(S_IWUSR | S_IRUGO,
+ ade7753_read_frequency,
+ ade7753_write_frequency);
+
+static IIO_CONST_ATTR_SAMP_FREQ_AVAIL("27900 14000 7000 3500");
+
+static struct attribute *ade7753_attributes[] = {
+ &iio_dev_attr_in_temp_raw.dev_attr.attr,
+ &iio_const_attr_in_temp_offset.dev_attr.attr,
+ &iio_const_attr_in_temp_scale.dev_attr.attr,
+ &iio_dev_attr_sampling_frequency.dev_attr.attr,
+ &iio_const_attr_sampling_frequency_available.dev_attr.attr,
+ &iio_dev_attr_phcal.dev_attr.attr,
+ &iio_dev_attr_cfden.dev_attr.attr,
+ &iio_dev_attr_aenergy.dev_attr.attr,
+ &iio_dev_attr_laenergy.dev_attr.attr,
+ &iio_dev_attr_vaenergy.dev_attr.attr,
+ &iio_dev_attr_lvaenergy.dev_attr.attr,
+ &iio_dev_attr_cfnum.dev_attr.attr,
+ &iio_dev_attr_apos.dev_attr.attr,
+ &iio_dev_attr_sagcyc.dev_attr.attr,
+ &iio_dev_attr_saglvl.dev_attr.attr,
+ &iio_dev_attr_linecyc.dev_attr.attr,
+ &iio_dev_attr_chksum.dev_attr.attr,
+ &iio_dev_attr_pga_gain.dev_attr.attr,
+ &iio_dev_attr_wgain.dev_attr.attr,
+ &iio_dev_attr_choff_1.dev_attr.attr,
+ &iio_dev_attr_choff_2.dev_attr.attr,
+ &iio_dev_attr_wdiv.dev_attr.attr,
+ &iio_dev_attr_irms.dev_attr.attr,
+ &iio_dev_attr_vrms.dev_attr.attr,
+ &iio_dev_attr_irmsos.dev_attr.attr,
+ &iio_dev_attr_vrmsos.dev_attr.attr,
+ &iio_dev_attr_vagain.dev_attr.attr,
+ &iio_dev_attr_ipklvl.dev_attr.attr,
+ &iio_dev_attr_vpklvl.dev_attr.attr,
+ &iio_dev_attr_ipeak.dev_attr.attr,
+ &iio_dev_attr_vpeak.dev_attr.attr,
+ &iio_dev_attr_vperiod.dev_attr.attr,
+ NULL,
+};
+
+static const struct attribute_group ade7753_attribute_group = {
+ .attrs = ade7753_attributes,
+};
+
+static const struct iio_info ade7753_info = {
+ .attrs = &ade7753_attribute_group,
+ .driver_module = THIS_MODULE,
+};
+
+static int ade7753_probe(struct spi_device *spi)
+{
+ int ret;
+ struct ade7753_state *st;
+ struct iio_dev *indio_dev;
+
+ /* setup the industrialio driver allocated elements */
+ indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));
+ if (!indio_dev)
+ return -ENOMEM;
+ /* this is only used for removal purposes */
+ spi_set_drvdata(spi, indio_dev);
+
+ st = iio_priv(indio_dev);
+ st->us = spi;
+ mutex_init(&st->buf_lock);
+
+ indio_dev->name = spi->dev.driver->name;
+ indio_dev->dev.parent = &spi->dev;
+ indio_dev->info = &ade7753_info;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+
+ /* Get the device into a sane initial state */
+ ret = ade7753_initial_setup(indio_dev);
+ if (ret)
+ return ret;
+
+ return iio_device_register(indio_dev);
+}
+
+/* fixme, confirm ordering in this function */
+static int ade7753_remove(struct spi_device *spi)
+{
+ struct iio_dev *indio_dev = spi_get_drvdata(spi);
+
+ iio_device_unregister(indio_dev);
+ ade7753_stop_device(&indio_dev->dev);
+
+ return 0;
+}
+
+static struct spi_driver ade7753_driver = {
+ .driver = {
+ .name = "ade7753",
+ .owner = THIS_MODULE,
+ },
+ .probe = ade7753_probe,
+ .remove = ade7753_remove,
+};
+module_spi_driver(ade7753_driver);
+
+MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>");
+MODULE_DESCRIPTION("Analog Devices ADE7753/6 Single-Phase Multifunction Meter");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("spi:ade7753");
diff --git a/drivers/staging/iio/meter/ade7753.h b/drivers/staging/iio/meter/ade7753.h
new file mode 100644
index 000000000..a9d93cc1c
--- /dev/null
+++ b/drivers/staging/iio/meter/ade7753.h
@@ -0,0 +1,72 @@
+#ifndef _ADE7753_H
+#define _ADE7753_H
+
+#define ADE7753_WAVEFORM 0x01
+#define ADE7753_AENERGY 0x02
+#define ADE7753_RAENERGY 0x03
+#define ADE7753_LAENERGY 0x04
+#define ADE7753_VAENERGY 0x05
+#define ADE7753_RVAENERGY 0x06
+#define ADE7753_LVAENERGY 0x07
+#define ADE7753_LVARENERGY 0x08
+#define ADE7753_MODE 0x09
+#define ADE7753_IRQEN 0x0A
+#define ADE7753_STATUS 0x0B
+#define ADE7753_RSTSTATUS 0x0C
+#define ADE7753_CH1OS 0x0D
+#define ADE7753_CH2OS 0x0E
+#define ADE7753_GAIN 0x0F
+#define ADE7753_PHCAL 0x10
+#define ADE7753_APOS 0x11
+#define ADE7753_WGAIN 0x12
+#define ADE7753_WDIV 0x13
+#define ADE7753_CFNUM 0x14
+#define ADE7753_CFDEN 0x15
+#define ADE7753_IRMS 0x16
+#define ADE7753_VRMS 0x17
+#define ADE7753_IRMSOS 0x18
+#define ADE7753_VRMSOS 0x19
+#define ADE7753_VAGAIN 0x1A
+#define ADE7753_VADIV 0x1B
+#define ADE7753_LINECYC 0x1C
+#define ADE7753_ZXTOUT 0x1D
+#define ADE7753_SAGCYC 0x1E
+#define ADE7753_SAGLVL 0x1F
+#define ADE7753_IPKLVL 0x20
+#define ADE7753_VPKLVL 0x21
+#define ADE7753_IPEAK 0x22
+#define ADE7753_RSTIPEAK 0x23
+#define ADE7753_VPEAK 0x24
+#define ADE7753_RSTVPEAK 0x25
+#define ADE7753_TEMP 0x26
+#define ADE7753_PERIOD 0x27
+#define ADE7753_TMODE 0x3D
+#define ADE7753_CHKSUM 0x3E
+#define ADE7753_DIEREV 0x3F
+
+#define ADE7753_READ_REG(a) a
+#define ADE7753_WRITE_REG(a) ((a) | 0x80)
+
+#define ADE7753_MAX_TX 4
+#define ADE7753_MAX_RX 4
+#define ADE7753_STARTUP_DELAY 1
+
+#define ADE7753_SPI_SLOW (u32)(300 * 1000)
+#define ADE7753_SPI_BURST (u32)(1000 * 1000)
+#define ADE7753_SPI_FAST (u32)(2000 * 1000)
+
+/**
+ * struct ade7753_state - device instance specific data
+ * @us: actual spi_device
+ * @tx: transmit buffer
+ * @rx: receive buffer
+ * @buf_lock: mutex to protect tx and rx
+ **/
+struct ade7753_state {
+ struct spi_device *us;
+ struct mutex buf_lock;
+ u8 tx[ADE7753_MAX_TX] ____cacheline_aligned;
+ u8 rx[ADE7753_MAX_RX];
+};
+
+#endif
diff --git a/drivers/staging/iio/meter/ade7754.c b/drivers/staging/iio/meter/ade7754.c
new file mode 100644
index 000000000..f12b2e503
--- /dev/null
+++ b/drivers/staging/iio/meter/ade7754.c
@@ -0,0 +1,588 @@
+/*
+ * ADE7754 Polyphase Multifunction Energy Metering IC Driver
+ *
+ * Copyright 2010 Analog Devices Inc.
+ *
+ * Licensed under the GPL-2 or later.
+ */
+
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/delay.h>
+#include <linux/mutex.h>
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/spi/spi.h>
+#include <linux/slab.h>
+#include <linux/sysfs.h>
+#include <linux/list.h>
+#include <linux/module.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include "meter.h"
+#include "ade7754.h"
+
+static int ade7754_spi_write_reg_8(struct device *dev,
+ u8 reg_address,
+ u8 val)
+{
+ int ret;
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct ade7754_state *st = iio_priv(indio_dev);
+
+ mutex_lock(&st->buf_lock);
+ st->tx[0] = ADE7754_WRITE_REG(reg_address);
+ st->tx[1] = val;
+
+ ret = spi_write(st->us, st->tx, 2);
+ mutex_unlock(&st->buf_lock);
+
+ return ret;
+}
+
+static int ade7754_spi_write_reg_16(struct device *dev,
+ u8 reg_address,
+ u16 value)
+{
+ int ret;
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct ade7754_state *st = iio_priv(indio_dev);
+
+ mutex_lock(&st->buf_lock);
+ st->tx[0] = ADE7754_WRITE_REG(reg_address);
+ st->tx[1] = (value >> 8) & 0xFF;
+ st->tx[2] = value & 0xFF;
+ ret = spi_write(st->us, st->tx, 3);
+ mutex_unlock(&st->buf_lock);
+
+ return ret;
+}
+
+static int ade7754_spi_read_reg_8(struct device *dev,
+ u8 reg_address,
+ u8 *val)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct ade7754_state *st = iio_priv(indio_dev);
+ int ret;
+
+ ret = spi_w8r8(st->us, ADE7754_READ_REG(reg_address));
+ if (ret < 0) {
+ dev_err(&st->us->dev, "problem when reading 8 bit register 0x%02X",
+ reg_address);
+ return ret;
+ }
+ *val = ret;
+
+ return 0;
+}
+
+static int ade7754_spi_read_reg_16(struct device *dev,
+ u8 reg_address,
+ u16 *val)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct ade7754_state *st = iio_priv(indio_dev);
+ int ret;
+
+ ret = spi_w8r16be(st->us, ADE7754_READ_REG(reg_address));
+ if (ret < 0) {
+ dev_err(&st->us->dev, "problem when reading 16 bit register 0x%02X",
+ reg_address);
+ return ret;
+ }
+
+ *val = ret;
+
+ return 0;
+}
+
+static int ade7754_spi_read_reg_24(struct device *dev,
+ u8 reg_address,
+ u32 *val)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct ade7754_state *st = iio_priv(indio_dev);
+ int ret;
+ struct spi_transfer xfers[] = {
+ {
+ .tx_buf = st->tx,
+ .rx_buf = st->rx,
+ .bits_per_word = 8,
+ .len = 4,
+ },
+ };
+
+ mutex_lock(&st->buf_lock);
+ st->tx[0] = ADE7754_READ_REG(reg_address);
+ st->tx[1] = 0;
+ st->tx[2] = 0;
+ st->tx[3] = 0;
+
+ ret = spi_sync_transfer(st->us, xfers, ARRAY_SIZE(xfers));
+ if (ret) {
+ dev_err(&st->us->dev, "problem when reading 24 bit register 0x%02X",
+ reg_address);
+ goto error_ret;
+ }
+ *val = (st->rx[1] << 16) | (st->rx[2] << 8) | st->rx[3];
+
+error_ret:
+ mutex_unlock(&st->buf_lock);
+ return ret;
+}
+
+static ssize_t ade7754_read_8bit(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ int ret;
+ u8 val = 0;
+ struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
+
+ ret = ade7754_spi_read_reg_8(dev, this_attr->address, &val);
+ if (ret)
+ return ret;
+
+ return sprintf(buf, "%u\n", val);
+}
+
+static ssize_t ade7754_read_16bit(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ int ret;
+ u16 val = 0;
+ struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
+
+ ret = ade7754_spi_read_reg_16(dev, this_attr->address, &val);
+ if (ret)
+ return ret;
+
+ return sprintf(buf, "%u\n", val);
+}
+
+static ssize_t ade7754_read_24bit(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ int ret;
+ u32 val = 0;
+ struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
+
+ ret = ade7754_spi_read_reg_24(dev, this_attr->address, &val);
+ if (ret)
+ return ret;
+
+ return sprintf(buf, "%u\n", val & 0xFFFFFF);
+}
+
+static ssize_t ade7754_write_8bit(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t len)
+{
+ struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
+ int ret;
+ u8 val;
+
+ ret = kstrtou8(buf, 10, &val);
+ if (ret)
+ goto error_ret;
+ ret = ade7754_spi_write_reg_8(dev, this_attr->address, val);
+
+error_ret:
+ return ret ? ret : len;
+}
+
+static ssize_t ade7754_write_16bit(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t len)
+{
+ struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
+ int ret;
+ u16 val;
+
+ ret = kstrtou16(buf, 10, &val);
+ if (ret)
+ goto error_ret;
+ ret = ade7754_spi_write_reg_16(dev, this_attr->address, val);
+
+error_ret:
+ return ret ? ret : len;
+}
+
+static int ade7754_reset(struct device *dev)
+{
+ int ret;
+ u8 val;
+
+ ret = ade7754_spi_read_reg_8(dev, ADE7754_OPMODE, &val);
+ if (ret < 0)
+ return ret;
+
+ val |= 1 << 6; /* Software Chip Reset */
+ return ade7754_spi_write_reg_8(dev, ADE7754_OPMODE, val);
+}
+
+static IIO_DEV_ATTR_AENERGY(ade7754_read_24bit, ADE7754_AENERGY);
+static IIO_DEV_ATTR_LAENERGY(ade7754_read_24bit, ADE7754_LAENERGY);
+static IIO_DEV_ATTR_VAENERGY(ade7754_read_24bit, ADE7754_VAENERGY);
+static IIO_DEV_ATTR_LVAENERGY(ade7754_read_24bit, ADE7754_LVAENERGY);
+static IIO_DEV_ATTR_VPEAK(S_IWUSR | S_IRUGO,
+ ade7754_read_8bit,
+ ade7754_write_8bit,
+ ADE7754_VPEAK);
+static IIO_DEV_ATTR_IPEAK(S_IWUSR | S_IRUGO,
+ ade7754_read_8bit,
+ ade7754_write_8bit,
+ ADE7754_VPEAK);
+static IIO_DEV_ATTR_APHCAL(S_IWUSR | S_IRUGO,
+ ade7754_read_8bit,
+ ade7754_write_8bit,
+ ADE7754_APHCAL);
+static IIO_DEV_ATTR_BPHCAL(S_IWUSR | S_IRUGO,
+ ade7754_read_8bit,
+ ade7754_write_8bit,
+ ADE7754_BPHCAL);
+static IIO_DEV_ATTR_CPHCAL(S_IWUSR | S_IRUGO,
+ ade7754_read_8bit,
+ ade7754_write_8bit,
+ ADE7754_CPHCAL);
+static IIO_DEV_ATTR_AAPOS(S_IWUSR | S_IRUGO,
+ ade7754_read_16bit,
+ ade7754_write_16bit,
+ ADE7754_AAPOS);
+static IIO_DEV_ATTR_BAPOS(S_IWUSR | S_IRUGO,
+ ade7754_read_16bit,
+ ade7754_write_16bit,
+ ADE7754_BAPOS);
+static IIO_DEV_ATTR_CAPOS(S_IWUSR | S_IRUGO,
+ ade7754_read_16bit,
+ ade7754_write_16bit,
+ ADE7754_CAPOS);
+static IIO_DEV_ATTR_WDIV(S_IWUSR | S_IRUGO,
+ ade7754_read_8bit,
+ ade7754_write_8bit,
+ ADE7754_WDIV);
+static IIO_DEV_ATTR_VADIV(S_IWUSR | S_IRUGO,
+ ade7754_read_8bit,
+ ade7754_write_8bit,
+ ADE7754_VADIV);
+static IIO_DEV_ATTR_CFNUM(S_IWUSR | S_IRUGO,
+ ade7754_read_16bit,
+ ade7754_write_16bit,
+ ADE7754_CFNUM);
+static IIO_DEV_ATTR_CFDEN(S_IWUSR | S_IRUGO,
+ ade7754_read_16bit,
+ ade7754_write_16bit,
+ ADE7754_CFDEN);
+static IIO_DEV_ATTR_ACTIVE_POWER_A_GAIN(S_IWUSR | S_IRUGO,
+ ade7754_read_16bit,
+ ade7754_write_16bit,
+ ADE7754_AAPGAIN);
+static IIO_DEV_ATTR_ACTIVE_POWER_B_GAIN(S_IWUSR | S_IRUGO,
+ ade7754_read_16bit,
+ ade7754_write_16bit,
+ ADE7754_BAPGAIN);
+static IIO_DEV_ATTR_ACTIVE_POWER_C_GAIN(S_IWUSR | S_IRUGO,
+ ade7754_read_16bit,
+ ade7754_write_16bit,
+ ADE7754_CAPGAIN);
+static IIO_DEV_ATTR_AIRMS(S_IRUGO,
+ ade7754_read_24bit,
+ NULL,
+ ADE7754_AIRMS);
+static IIO_DEV_ATTR_BIRMS(S_IRUGO,
+ ade7754_read_24bit,
+ NULL,
+ ADE7754_BIRMS);
+static IIO_DEV_ATTR_CIRMS(S_IRUGO,
+ ade7754_read_24bit,
+ NULL,
+ ADE7754_CIRMS);
+static IIO_DEV_ATTR_AVRMS(S_IRUGO,
+ ade7754_read_24bit,
+ NULL,
+ ADE7754_AVRMS);
+static IIO_DEV_ATTR_BVRMS(S_IRUGO,
+ ade7754_read_24bit,
+ NULL,
+ ADE7754_BVRMS);
+static IIO_DEV_ATTR_CVRMS(S_IRUGO,
+ ade7754_read_24bit,
+ NULL,
+ ADE7754_CVRMS);
+static IIO_DEV_ATTR_AIRMSOS(S_IRUGO,
+ ade7754_read_16bit,
+ ade7754_write_16bit,
+ ADE7754_AIRMSOS);
+static IIO_DEV_ATTR_BIRMSOS(S_IRUGO,
+ ade7754_read_16bit,
+ ade7754_write_16bit,
+ ADE7754_BIRMSOS);
+static IIO_DEV_ATTR_CIRMSOS(S_IRUGO,
+ ade7754_read_16bit,
+ ade7754_write_16bit,
+ ADE7754_CIRMSOS);
+static IIO_DEV_ATTR_AVRMSOS(S_IRUGO,
+ ade7754_read_16bit,
+ ade7754_write_16bit,
+ ADE7754_AVRMSOS);
+static IIO_DEV_ATTR_BVRMSOS(S_IRUGO,
+ ade7754_read_16bit,
+ ade7754_write_16bit,
+ ADE7754_BVRMSOS);
+static IIO_DEV_ATTR_CVRMSOS(S_IRUGO,
+ ade7754_read_16bit,
+ ade7754_write_16bit,
+ ADE7754_CVRMSOS);
+
+static int ade7754_set_irq(struct device *dev, bool enable)
+{
+ int ret;
+ u16 irqen;
+
+ ret = ade7754_spi_read_reg_16(dev, ADE7754_IRQEN, &irqen);
+ if (ret)
+ goto error_ret;
+
+ if (enable)
+ irqen |= 1 << 14; /* Enables an interrupt when a data is
+ present in the waveform register */
+ else
+ irqen &= ~(1 << 14);
+
+ ret = ade7754_spi_write_reg_16(dev, ADE7754_IRQEN, irqen);
+ if (ret)
+ goto error_ret;
+
+error_ret:
+ return ret;
+}
+
+/* Power down the device */
+static int ade7754_stop_device(struct device *dev)
+{
+ int ret;
+ u8 val;
+
+ ret = ade7754_spi_read_reg_8(dev, ADE7754_OPMODE, &val);
+ if (ret < 0) {
+ dev_err(dev, "unable to power down the device, error: %d",
+ ret);
+ return ret;
+ }
+
+ val |= 7 << 3; /* ADE7754 powered down */
+ return ade7754_spi_write_reg_8(dev, ADE7754_OPMODE, val);
+}
+
+static int ade7754_initial_setup(struct iio_dev *indio_dev)
+{
+ int ret;
+ struct ade7754_state *st = iio_priv(indio_dev);
+ struct device *dev = &indio_dev->dev;
+
+ /* use low spi speed for init */
+ st->us->mode = SPI_MODE_3;
+ spi_setup(st->us);
+
+ /* Disable IRQ */
+ ret = ade7754_set_irq(dev, false);
+ if (ret) {
+ dev_err(dev, "disable irq failed");
+ goto err_ret;
+ }
+
+ ade7754_reset(dev);
+ msleep(ADE7754_STARTUP_DELAY);
+
+err_ret:
+ return ret;
+}
+
+static ssize_t ade7754_read_frequency(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ int ret;
+ u8 t;
+ int sps;
+
+ ret = ade7754_spi_read_reg_8(dev,
+ ADE7754_WAVMODE,
+ &t);
+ if (ret)
+ return ret;
+
+ t = (t >> 3) & 0x3;
+ sps = 26000 / (1 + t);
+
+ return sprintf(buf, "%d\n", sps);
+}
+
+static ssize_t ade7754_write_frequency(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t len)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct ade7754_state *st = iio_priv(indio_dev);
+ u16 val;
+ int ret;
+ u8 reg, t;
+
+ ret = kstrtou16(buf, 10, &val);
+ if (ret)
+ return ret;
+ if (val == 0)
+ return -EINVAL;
+
+ mutex_lock(&indio_dev->mlock);
+
+ t = 26000 / val;
+ if (t > 0)
+ t--;
+
+ if (t > 1)
+ st->us->max_speed_hz = ADE7754_SPI_SLOW;
+ else
+ st->us->max_speed_hz = ADE7754_SPI_FAST;
+
+ ret = ade7754_spi_read_reg_8(dev, ADE7754_WAVMODE, &reg);
+ if (ret)
+ goto out;
+
+ reg &= ~(3 << 3);
+ reg |= t << 3;
+
+ ret = ade7754_spi_write_reg_8(dev, ADE7754_WAVMODE, reg);
+
+out:
+ mutex_unlock(&indio_dev->mlock);
+
+ return ret ? ret : len;
+}
+static IIO_DEV_ATTR_TEMP_RAW(ade7754_read_8bit);
+static IIO_CONST_ATTR(in_temp_offset, "129 C");
+static IIO_CONST_ATTR(in_temp_scale, "4 C");
+
+static IIO_DEV_ATTR_SAMP_FREQ(S_IWUSR | S_IRUGO,
+ ade7754_read_frequency,
+ ade7754_write_frequency);
+
+static IIO_CONST_ATTR_SAMP_FREQ_AVAIL("26000 13000 65000 33000");
+
+static struct attribute *ade7754_attributes[] = {
+ &iio_dev_attr_in_temp_raw.dev_attr.attr,
+ &iio_const_attr_in_temp_offset.dev_attr.attr,
+ &iio_const_attr_in_temp_scale.dev_attr.attr,
+ &iio_dev_attr_sampling_frequency.dev_attr.attr,
+ &iio_const_attr_sampling_frequency_available.dev_attr.attr,
+ &iio_dev_attr_aenergy.dev_attr.attr,
+ &iio_dev_attr_laenergy.dev_attr.attr,
+ &iio_dev_attr_vaenergy.dev_attr.attr,
+ &iio_dev_attr_lvaenergy.dev_attr.attr,
+ &iio_dev_attr_vpeak.dev_attr.attr,
+ &iio_dev_attr_ipeak.dev_attr.attr,
+ &iio_dev_attr_aphcal.dev_attr.attr,
+ &iio_dev_attr_bphcal.dev_attr.attr,
+ &iio_dev_attr_cphcal.dev_attr.attr,
+ &iio_dev_attr_aapos.dev_attr.attr,
+ &iio_dev_attr_bapos.dev_attr.attr,
+ &iio_dev_attr_capos.dev_attr.attr,
+ &iio_dev_attr_wdiv.dev_attr.attr,
+ &iio_dev_attr_vadiv.dev_attr.attr,
+ &iio_dev_attr_cfnum.dev_attr.attr,
+ &iio_dev_attr_cfden.dev_attr.attr,
+ &iio_dev_attr_active_power_a_gain.dev_attr.attr,
+ &iio_dev_attr_active_power_b_gain.dev_attr.attr,
+ &iio_dev_attr_active_power_c_gain.dev_attr.attr,
+ &iio_dev_attr_airms.dev_attr.attr,
+ &iio_dev_attr_birms.dev_attr.attr,
+ &iio_dev_attr_cirms.dev_attr.attr,
+ &iio_dev_attr_avrms.dev_attr.attr,
+ &iio_dev_attr_bvrms.dev_attr.attr,
+ &iio_dev_attr_cvrms.dev_attr.attr,
+ &iio_dev_attr_airmsos.dev_attr.attr,
+ &iio_dev_attr_birmsos.dev_attr.attr,
+ &iio_dev_attr_cirmsos.dev_attr.attr,
+ &iio_dev_attr_avrmsos.dev_attr.attr,
+ &iio_dev_attr_bvrmsos.dev_attr.attr,
+ &iio_dev_attr_cvrmsos.dev_attr.attr,
+ NULL,
+};
+
+static const struct attribute_group ade7754_attribute_group = {
+ .attrs = ade7754_attributes,
+};
+
+static const struct iio_info ade7754_info = {
+ .attrs = &ade7754_attribute_group,
+ .driver_module = THIS_MODULE,
+};
+
+static int ade7754_probe(struct spi_device *spi)
+{
+ int ret;
+ struct ade7754_state *st;
+ struct iio_dev *indio_dev;
+
+ /* setup the industrialio driver allocated elements */
+ indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));
+ if (!indio_dev)
+ return -ENOMEM;
+ /* this is only used for removal purposes */
+ spi_set_drvdata(spi, indio_dev);
+
+ st = iio_priv(indio_dev);
+ st->us = spi;
+ mutex_init(&st->buf_lock);
+
+ indio_dev->name = spi->dev.driver->name;
+ indio_dev->dev.parent = &spi->dev;
+ indio_dev->info = &ade7754_info;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+
+ /* Get the device into a sane initial state */
+ ret = ade7754_initial_setup(indio_dev);
+ if (ret)
+ goto powerdown_on_error;
+ ret = iio_device_register(indio_dev);
+ if (ret)
+ goto powerdown_on_error;
+ return ret;
+
+powerdown_on_error:
+ ade7754_stop_device(&indio_dev->dev);
+ return ret;
+}
+
+/* fixme, confirm ordering in this function */
+static int ade7754_remove(struct spi_device *spi)
+{
+ struct iio_dev *indio_dev = spi_get_drvdata(spi);
+
+ iio_device_unregister(indio_dev);
+ ade7754_stop_device(&indio_dev->dev);
+
+ return 0;
+}
+
+static struct spi_driver ade7754_driver = {
+ .driver = {
+ .name = "ade7754",
+ .owner = THIS_MODULE,
+ },
+ .probe = ade7754_probe,
+ .remove = ade7754_remove,
+};
+module_spi_driver(ade7754_driver);
+
+MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>");
+MODULE_DESCRIPTION("Analog Devices ADE7754 Polyphase Multifunction Energy Metering IC Driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("spi:ad7754");
diff --git a/drivers/staging/iio/meter/ade7754.h b/drivers/staging/iio/meter/ade7754.h
new file mode 100644
index 000000000..e42ffc387
--- /dev/null
+++ b/drivers/staging/iio/meter/ade7754.h
@@ -0,0 +1,90 @@
+#ifndef _ADE7754_H
+#define _ADE7754_H
+
+#define ADE7754_AENERGY 0x01
+#define ADE7754_RAENERGY 0x02
+#define ADE7754_LAENERGY 0x03
+#define ADE7754_VAENERGY 0x04
+#define ADE7754_RVAENERGY 0x05
+#define ADE7754_LVAENERGY 0x06
+#define ADE7754_PERIOD 0x07
+#define ADE7754_TEMP 0x08
+#define ADE7754_WFORM 0x09
+#define ADE7754_OPMODE 0x0A
+#define ADE7754_MMODE 0x0B
+#define ADE7754_WAVMODE 0x0C
+#define ADE7754_WATMODE 0x0D
+#define ADE7754_VAMODE 0x0E
+#define ADE7754_IRQEN 0x0F
+#define ADE7754_STATUS 0x10
+#define ADE7754_RSTATUS 0x11
+#define ADE7754_ZXTOUT 0x12
+#define ADE7754_LINCYC 0x13
+#define ADE7754_SAGCYC 0x14
+#define ADE7754_SAGLVL 0x15
+#define ADE7754_VPEAK 0x16
+#define ADE7754_IPEAK 0x17
+#define ADE7754_GAIN 0x18
+#define ADE7754_AWG 0x19
+#define ADE7754_BWG 0x1A
+#define ADE7754_CWG 0x1B
+#define ADE7754_AVAG 0x1C
+#define ADE7754_BVAG 0x1D
+#define ADE7754_CVAG 0x1E
+#define ADE7754_APHCAL 0x1F
+#define ADE7754_BPHCAL 0x20
+#define ADE7754_CPHCAL 0x21
+#define ADE7754_AAPOS 0x22
+#define ADE7754_BAPOS 0x23
+#define ADE7754_CAPOS 0x24
+#define ADE7754_CFNUM 0x25
+#define ADE7754_CFDEN 0x26
+#define ADE7754_WDIV 0x27
+#define ADE7754_VADIV 0x28
+#define ADE7754_AIRMS 0x29
+#define ADE7754_BIRMS 0x2A
+#define ADE7754_CIRMS 0x2B
+#define ADE7754_AVRMS 0x2C
+#define ADE7754_BVRMS 0x2D
+#define ADE7754_CVRMS 0x2E
+#define ADE7754_AIRMSOS 0x2F
+#define ADE7754_BIRMSOS 0x30
+#define ADE7754_CIRMSOS 0x31
+#define ADE7754_AVRMSOS 0x32
+#define ADE7754_BVRMSOS 0x33
+#define ADE7754_CVRMSOS 0x34
+#define ADE7754_AAPGAIN 0x35
+#define ADE7754_BAPGAIN 0x36
+#define ADE7754_CAPGAIN 0x37
+#define ADE7754_AVGAIN 0x38
+#define ADE7754_BVGAIN 0x39
+#define ADE7754_CVGAIN 0x3A
+#define ADE7754_CHKSUM 0x3E
+#define ADE7754_VERSION 0x3F
+
+#define ADE7754_READ_REG(a) a
+#define ADE7754_WRITE_REG(a) ((a) | 0x80)
+
+#define ADE7754_MAX_TX 4
+#define ADE7754_MAX_RX 4
+#define ADE7754_STARTUP_DELAY 1
+
+#define ADE7754_SPI_SLOW (u32)(300 * 1000)
+#define ADE7754_SPI_BURST (u32)(1000 * 1000)
+#define ADE7754_SPI_FAST (u32)(2000 * 1000)
+
+/**
+ * struct ade7754_state - device instance specific data
+ * @us: actual spi_device
+ * @buf_lock: mutex to protect tx and rx
+ * @tx: transmit buffer
+ * @rx: receive buffer
+ **/
+struct ade7754_state {
+ struct spi_device *us;
+ struct mutex buf_lock;
+ u8 tx[ADE7754_MAX_TX] ____cacheline_aligned;
+ u8 rx[ADE7754_MAX_RX];
+};
+
+#endif
diff --git a/drivers/staging/iio/meter/ade7758.h b/drivers/staging/iio/meter/ade7758.h
new file mode 100644
index 000000000..f6739e2c2
--- /dev/null
+++ b/drivers/staging/iio/meter/ade7758.h
@@ -0,0 +1,181 @@
+/*
+ * ADE7758 Poly Phase Multifunction Energy Metering IC driver
+ *
+ * Copyright 2010-2011 Analog Devices Inc.
+ *
+ * Licensed under the GPL-2.
+ */
+
+#ifndef _ADE7758_H
+#define _ADE7758_H
+
+#define ADE7758_AWATTHR 0x01
+#define ADE7758_BWATTHR 0x02
+#define ADE7758_CWATTHR 0x03
+#define ADE7758_AVARHR 0x04
+#define ADE7758_BVARHR 0x05
+#define ADE7758_CVARHR 0x06
+#define ADE7758_AVAHR 0x07
+#define ADE7758_BVAHR 0x08
+#define ADE7758_CVAHR 0x09
+#define ADE7758_AIRMS 0x0A
+#define ADE7758_BIRMS 0x0B
+#define ADE7758_CIRMS 0x0C
+#define ADE7758_AVRMS 0x0D
+#define ADE7758_BVRMS 0x0E
+#define ADE7758_CVRMS 0x0F
+#define ADE7758_FREQ 0x10
+#define ADE7758_TEMP 0x11
+#define ADE7758_WFORM 0x12
+#define ADE7758_OPMODE 0x13
+#define ADE7758_MMODE 0x14
+#define ADE7758_WAVMODE 0x15
+#define ADE7758_COMPMODE 0x16
+#define ADE7758_LCYCMODE 0x17
+#define ADE7758_MASK 0x18
+#define ADE7758_STATUS 0x19
+#define ADE7758_RSTATUS 0x1A
+#define ADE7758_ZXTOUT 0x1B
+#define ADE7758_LINECYC 0x1C
+#define ADE7758_SAGCYC 0x1D
+#define ADE7758_SAGLVL 0x1E
+#define ADE7758_VPINTLVL 0x1F
+#define ADE7758_IPINTLVL 0x20
+#define ADE7758_VPEAK 0x21
+#define ADE7758_IPEAK 0x22
+#define ADE7758_GAIN 0x23
+#define ADE7758_AVRMSGAIN 0x24
+#define ADE7758_BVRMSGAIN 0x25
+#define ADE7758_CVRMSGAIN 0x26
+#define ADE7758_AIGAIN 0x27
+#define ADE7758_BIGAIN 0x28
+#define ADE7758_CIGAIN 0x29
+#define ADE7758_AWG 0x2A
+#define ADE7758_BWG 0x2B
+#define ADE7758_CWG 0x2C
+#define ADE7758_AVARG 0x2D
+#define ADE7758_BVARG 0x2E
+#define ADE7758_CVARG 0x2F
+#define ADE7758_AVAG 0x30
+#define ADE7758_BVAG 0x31
+#define ADE7758_CVAG 0x32
+#define ADE7758_AVRMSOS 0x33
+#define ADE7758_BVRMSOS 0x34
+#define ADE7758_CVRMSOS 0x35
+#define ADE7758_AIRMSOS 0x36
+#define ADE7758_BIRMSOS 0x37
+#define ADE7758_CIRMSOS 0x38
+#define ADE7758_AWAITOS 0x39
+#define ADE7758_BWAITOS 0x3A
+#define ADE7758_CWAITOS 0x3B
+#define ADE7758_AVAROS 0x3C
+#define ADE7758_BVAROS 0x3D
+#define ADE7758_CVAROS 0x3E
+#define ADE7758_APHCAL 0x3F
+#define ADE7758_BPHCAL 0x40
+#define ADE7758_CPHCAL 0x41
+#define ADE7758_WDIV 0x42
+#define ADE7758_VADIV 0x44
+#define ADE7758_VARDIV 0x43
+#define ADE7758_APCFNUM 0x45
+#define ADE7758_APCFDEN 0x46
+#define ADE7758_VARCFNUM 0x47
+#define ADE7758_VARCFDEN 0x48
+#define ADE7758_CHKSUM 0x7E
+#define ADE7758_VERSION 0x7F
+
+#define ADE7758_READ_REG(a) a
+#define ADE7758_WRITE_REG(a) ((a) | 0x80)
+
+#define ADE7758_MAX_TX 8
+#define ADE7758_MAX_RX 4
+#define ADE7758_STARTUP_DELAY 1
+
+#define AD7758_NUM_WAVSEL 5
+#define AD7758_NUM_PHSEL 3
+#define AD7758_NUM_WAVESRC (AD7758_NUM_WAVSEL * AD7758_NUM_PHSEL)
+
+#define AD7758_PHASE_A 0
+#define AD7758_PHASE_B 1
+#define AD7758_PHASE_C 2
+#define AD7758_CURRENT 0
+#define AD7758_VOLTAGE 1
+#define AD7758_ACT_PWR 2
+#define AD7758_REACT_PWR 3
+#define AD7758_APP_PWR 4
+#define AD7758_WT(p, w) (((w) << 2) | (p))
+
+/**
+ * struct ade7758_state - device instance specific data
+ * @us: actual spi_device
+ * @trig: data ready trigger registered with iio
+ * @tx: transmit buffer
+ * @rx: receive buffer
+ * @buf_lock: mutex to protect tx and rx
+ **/
+struct ade7758_state {
+ struct spi_device *us;
+ struct iio_trigger *trig;
+ u8 *tx;
+ u8 *rx;
+ struct mutex buf_lock;
+ struct spi_transfer ring_xfer[4];
+ struct spi_message ring_msg;
+ /*
+ * DMA (thus cache coherency maintenance) requires the
+ * transfer buffers to live in their own cache lines.
+ */
+ unsigned char rx_buf[8] ____cacheline_aligned;
+ unsigned char tx_buf[8];
+
+};
+#ifdef CONFIG_IIO_BUFFER
+/* At the moment triggers are only used for ring buffer
+ * filling. This may change!
+ */
+
+void ade7758_remove_trigger(struct iio_dev *indio_dev);
+int ade7758_probe_trigger(struct iio_dev *indio_dev);
+
+ssize_t ade7758_read_data_from_ring(struct device *dev,
+ struct device_attribute *attr,
+ char *buf);
+
+
+int ade7758_configure_ring(struct iio_dev *indio_dev);
+void ade7758_unconfigure_ring(struct iio_dev *indio_dev);
+
+int ade7758_set_irq(struct device *dev, bool enable);
+
+int ade7758_spi_write_reg_8(struct device *dev,
+ u8 reg_address, u8 val);
+int ade7758_spi_read_reg_8(struct device *dev,
+ u8 reg_address, u8 *val);
+
+#else /* CONFIG_IIO_BUFFER */
+
+static inline void ade7758_remove_trigger(struct iio_dev *indio_dev)
+{
+}
+static inline int ade7758_probe_trigger(struct iio_dev *indio_dev)
+{
+ return 0;
+}
+
+static int ade7758_configure_ring(struct iio_dev *indio_dev)
+{
+ return 0;
+}
+static inline void ade7758_unconfigure_ring(struct iio_dev *indio_dev)
+{
+}
+static inline int ade7758_initialize_ring(struct iio_ring_buffer *ring)
+{
+ return 0;
+}
+static inline void ade7758_uninitialize_ring(struct iio_dev *indio_dev)
+{
+}
+#endif /* CONFIG_IIO_BUFFER */
+
+#endif
diff --git a/drivers/staging/iio/meter/ade7758_core.c b/drivers/staging/iio/meter/ade7758_core.c
new file mode 100644
index 000000000..77141ae13
--- /dev/null
+++ b/drivers/staging/iio/meter/ade7758_core.c
@@ -0,0 +1,917 @@
+/*
+ * ADE7758 Poly Phase Multifunction Energy Metering IC driver
+ *
+ * Copyright 2010-2011 Analog Devices Inc.
+ *
+ * Licensed under the GPL-2.
+ */
+
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/delay.h>
+#include <linux/mutex.h>
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/spi/spi.h>
+#include <linux/slab.h>
+#include <linux/sysfs.h>
+#include <linux/list.h>
+#include <linux/module.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/iio/buffer.h>
+#include "meter.h"
+#include "ade7758.h"
+
+int ade7758_spi_write_reg_8(struct device *dev,
+ u8 reg_address,
+ u8 val)
+{
+ int ret;
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct ade7758_state *st = iio_priv(indio_dev);
+
+ mutex_lock(&st->buf_lock);
+ st->tx[0] = ADE7758_WRITE_REG(reg_address);
+ st->tx[1] = val;
+
+ ret = spi_write(st->us, st->tx, 2);
+ mutex_unlock(&st->buf_lock);
+
+ return ret;
+}
+
+static int ade7758_spi_write_reg_16(struct device *dev,
+ u8 reg_address,
+ u16 value)
+{
+ int ret;
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct ade7758_state *st = iio_priv(indio_dev);
+ struct spi_transfer xfers[] = {
+ {
+ .tx_buf = st->tx,
+ .bits_per_word = 8,
+ .len = 3,
+ }
+ };
+
+ mutex_lock(&st->buf_lock);
+ st->tx[0] = ADE7758_WRITE_REG(reg_address);
+ st->tx[1] = (value >> 8) & 0xFF;
+ st->tx[2] = value & 0xFF;
+
+ ret = spi_sync_transfer(st->us, xfers, ARRAY_SIZE(xfers));
+ mutex_unlock(&st->buf_lock);
+
+ return ret;
+}
+
+static int ade7758_spi_write_reg_24(struct device *dev,
+ u8 reg_address,
+ u32 value)
+{
+ int ret;
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct ade7758_state *st = iio_priv(indio_dev);
+ struct spi_transfer xfers[] = {
+ {
+ .tx_buf = st->tx,
+ .bits_per_word = 8,
+ .len = 4,
+ }
+ };
+
+ mutex_lock(&st->buf_lock);
+ st->tx[0] = ADE7758_WRITE_REG(reg_address);
+ st->tx[1] = (value >> 16) & 0xFF;
+ st->tx[2] = (value >> 8) & 0xFF;
+ st->tx[3] = value & 0xFF;
+
+ ret = spi_sync_transfer(st->us, xfers, ARRAY_SIZE(xfers));
+ mutex_unlock(&st->buf_lock);
+
+ return ret;
+}
+
+int ade7758_spi_read_reg_8(struct device *dev,
+ u8 reg_address,
+ u8 *val)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct ade7758_state *st = iio_priv(indio_dev);
+ int ret;
+ struct spi_transfer xfers[] = {
+ {
+ .tx_buf = st->tx,
+ .bits_per_word = 8,
+ .len = 1,
+ .delay_usecs = 4,
+ },
+ {
+ .tx_buf = &st->tx[1],
+ .rx_buf = st->rx,
+ .bits_per_word = 8,
+ .len = 1,
+ },
+ };
+
+ mutex_lock(&st->buf_lock);
+ st->tx[0] = ADE7758_READ_REG(reg_address);
+ st->tx[1] = 0;
+
+ ret = spi_sync_transfer(st->us, xfers, ARRAY_SIZE(xfers));
+ if (ret) {
+ dev_err(&st->us->dev, "problem when reading 8 bit register 0x%02X",
+ reg_address);
+ goto error_ret;
+ }
+ *val = st->rx[0];
+
+error_ret:
+ mutex_unlock(&st->buf_lock);
+ return ret;
+}
+
+static int ade7758_spi_read_reg_16(struct device *dev,
+ u8 reg_address,
+ u16 *val)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct ade7758_state *st = iio_priv(indio_dev);
+ int ret;
+ struct spi_transfer xfers[] = {
+ {
+ .tx_buf = st->tx,
+ .bits_per_word = 8,
+ .len = 1,
+ .delay_usecs = 4,
+ },
+ {
+ .tx_buf = &st->tx[1],
+ .rx_buf = st->rx,
+ .bits_per_word = 8,
+ .len = 2,
+ },
+ };
+
+
+ mutex_lock(&st->buf_lock);
+ st->tx[0] = ADE7758_READ_REG(reg_address);
+ st->tx[1] = 0;
+ st->tx[2] = 0;
+
+ ret = spi_sync_transfer(st->us, xfers, ARRAY_SIZE(xfers));
+ if (ret) {
+ dev_err(&st->us->dev, "problem when reading 16 bit register 0x%02X",
+ reg_address);
+ goto error_ret;
+ }
+
+ *val = (st->rx[0] << 8) | st->rx[1];
+
+error_ret:
+ mutex_unlock(&st->buf_lock);
+ return ret;
+}
+
+static int ade7758_spi_read_reg_24(struct device *dev,
+ u8 reg_address,
+ u32 *val)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct ade7758_state *st = iio_priv(indio_dev);
+ int ret;
+ struct spi_transfer xfers[] = {
+ {
+ .tx_buf = st->tx,
+ .bits_per_word = 8,
+ .len = 1,
+ .delay_usecs = 4,
+ },
+ {
+ .tx_buf = &st->tx[1],
+ .rx_buf = st->rx,
+ .bits_per_word = 8,
+ .len = 3,
+ },
+ };
+
+ mutex_lock(&st->buf_lock);
+ st->tx[0] = ADE7758_READ_REG(reg_address);
+ st->tx[1] = 0;
+ st->tx[2] = 0;
+ st->tx[3] = 0;
+
+ ret = spi_sync_transfer(st->us, xfers, ARRAY_SIZE(xfers));
+ if (ret) {
+ dev_err(&st->us->dev, "problem when reading 24 bit register 0x%02X",
+ reg_address);
+ goto error_ret;
+ }
+ *val = (st->rx[0] << 16) | (st->rx[1] << 8) | st->rx[2];
+
+error_ret:
+ mutex_unlock(&st->buf_lock);
+ return ret;
+}
+
+static ssize_t ade7758_read_8bit(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ int ret;
+ u8 val = 0;
+ struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
+
+ ret = ade7758_spi_read_reg_8(dev, this_attr->address, &val);
+ if (ret)
+ return ret;
+
+ return sprintf(buf, "%u\n", val);
+}
+
+static ssize_t ade7758_read_16bit(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ int ret;
+ u16 val = 0;
+ struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
+
+ ret = ade7758_spi_read_reg_16(dev, this_attr->address, &val);
+ if (ret)
+ return ret;
+
+ return sprintf(buf, "%u\n", val);
+}
+
+static ssize_t ade7758_read_24bit(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ int ret;
+ u32 val = 0;
+ struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
+
+ ret = ade7758_spi_read_reg_24(dev, this_attr->address, &val);
+ if (ret)
+ return ret;
+
+ return sprintf(buf, "%u\n", val & 0xFFFFFF);
+}
+
+static ssize_t ade7758_write_8bit(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t len)
+{
+ struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
+ int ret;
+ u8 val;
+
+ ret = kstrtou8(buf, 10, &val);
+ if (ret)
+ goto error_ret;
+ ret = ade7758_spi_write_reg_8(dev, this_attr->address, val);
+
+error_ret:
+ return ret ? ret : len;
+}
+
+static ssize_t ade7758_write_16bit(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t len)
+{
+ struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
+ int ret;
+ u16 val;
+
+ ret = kstrtou16(buf, 10, &val);
+ if (ret)
+ goto error_ret;
+ ret = ade7758_spi_write_reg_16(dev, this_attr->address, val);
+
+error_ret:
+ return ret ? ret : len;
+}
+
+static int ade7758_reset(struct device *dev)
+{
+ int ret;
+ u8 val;
+
+ ret = ade7758_spi_read_reg_8(dev, ADE7758_OPMODE, &val);
+ if (ret < 0) {
+ dev_err(dev, "Failed to read opmode reg\n");
+ return ret;
+ }
+ val |= 1 << 6; /* Software Chip Reset */
+ ret = ade7758_spi_write_reg_8(dev, ADE7758_OPMODE, val);
+ if (ret < 0)
+ dev_err(dev, "Failed to write opmode reg\n");
+ return ret;
+}
+
+static IIO_DEV_ATTR_VPEAK(S_IWUSR | S_IRUGO,
+ ade7758_read_8bit,
+ ade7758_write_8bit,
+ ADE7758_VPEAK);
+static IIO_DEV_ATTR_IPEAK(S_IWUSR | S_IRUGO,
+ ade7758_read_8bit,
+ ade7758_write_8bit,
+ ADE7758_VPEAK);
+static IIO_DEV_ATTR_APHCAL(S_IWUSR | S_IRUGO,
+ ade7758_read_8bit,
+ ade7758_write_8bit,
+ ADE7758_APHCAL);
+static IIO_DEV_ATTR_BPHCAL(S_IWUSR | S_IRUGO,
+ ade7758_read_8bit,
+ ade7758_write_8bit,
+ ADE7758_BPHCAL);
+static IIO_DEV_ATTR_CPHCAL(S_IWUSR | S_IRUGO,
+ ade7758_read_8bit,
+ ade7758_write_8bit,
+ ADE7758_CPHCAL);
+static IIO_DEV_ATTR_WDIV(S_IWUSR | S_IRUGO,
+ ade7758_read_8bit,
+ ade7758_write_8bit,
+ ADE7758_WDIV);
+static IIO_DEV_ATTR_VADIV(S_IWUSR | S_IRUGO,
+ ade7758_read_8bit,
+ ade7758_write_8bit,
+ ADE7758_VADIV);
+static IIO_DEV_ATTR_AIRMS(S_IRUGO,
+ ade7758_read_24bit,
+ NULL,
+ ADE7758_AIRMS);
+static IIO_DEV_ATTR_BIRMS(S_IRUGO,
+ ade7758_read_24bit,
+ NULL,
+ ADE7758_BIRMS);
+static IIO_DEV_ATTR_CIRMS(S_IRUGO,
+ ade7758_read_24bit,
+ NULL,
+ ADE7758_CIRMS);
+static IIO_DEV_ATTR_AVRMS(S_IRUGO,
+ ade7758_read_24bit,
+ NULL,
+ ADE7758_AVRMS);
+static IIO_DEV_ATTR_BVRMS(S_IRUGO,
+ ade7758_read_24bit,
+ NULL,
+ ADE7758_BVRMS);
+static IIO_DEV_ATTR_CVRMS(S_IRUGO,
+ ade7758_read_24bit,
+ NULL,
+ ADE7758_CVRMS);
+static IIO_DEV_ATTR_AIRMSOS(S_IWUSR | S_IRUGO,
+ ade7758_read_16bit,
+ ade7758_write_16bit,
+ ADE7758_AIRMSOS);
+static IIO_DEV_ATTR_BIRMSOS(S_IWUSR | S_IRUGO,
+ ade7758_read_16bit,
+ ade7758_write_16bit,
+ ADE7758_BIRMSOS);
+static IIO_DEV_ATTR_CIRMSOS(S_IWUSR | S_IRUGO,
+ ade7758_read_16bit,
+ ade7758_write_16bit,
+ ADE7758_CIRMSOS);
+static IIO_DEV_ATTR_AVRMSOS(S_IWUSR | S_IRUGO,
+ ade7758_read_16bit,
+ ade7758_write_16bit,
+ ADE7758_AVRMSOS);
+static IIO_DEV_ATTR_BVRMSOS(S_IWUSR | S_IRUGO,
+ ade7758_read_16bit,
+ ade7758_write_16bit,
+ ADE7758_BVRMSOS);
+static IIO_DEV_ATTR_CVRMSOS(S_IWUSR | S_IRUGO,
+ ade7758_read_16bit,
+ ade7758_write_16bit,
+ ADE7758_CVRMSOS);
+static IIO_DEV_ATTR_AIGAIN(S_IWUSR | S_IRUGO,
+ ade7758_read_16bit,
+ ade7758_write_16bit,
+ ADE7758_AIGAIN);
+static IIO_DEV_ATTR_BIGAIN(S_IWUSR | S_IRUGO,
+ ade7758_read_16bit,
+ ade7758_write_16bit,
+ ADE7758_BIGAIN);
+static IIO_DEV_ATTR_CIGAIN(S_IWUSR | S_IRUGO,
+ ade7758_read_16bit,
+ ade7758_write_16bit,
+ ADE7758_CIGAIN);
+static IIO_DEV_ATTR_AVRMSGAIN(S_IWUSR | S_IRUGO,
+ ade7758_read_16bit,
+ ade7758_write_16bit,
+ ADE7758_AVRMSGAIN);
+static IIO_DEV_ATTR_BVRMSGAIN(S_IWUSR | S_IRUGO,
+ ade7758_read_16bit,
+ ade7758_write_16bit,
+ ADE7758_BVRMSGAIN);
+static IIO_DEV_ATTR_CVRMSGAIN(S_IWUSR | S_IRUGO,
+ ade7758_read_16bit,
+ ade7758_write_16bit,
+ ADE7758_CVRMSGAIN);
+
+int ade7758_set_irq(struct device *dev, bool enable)
+{
+ int ret;
+ u32 irqen;
+
+ ret = ade7758_spi_read_reg_24(dev, ADE7758_MASK, &irqen);
+ if (ret)
+ goto error_ret;
+
+ if (enable)
+ irqen |= 1 << 16; /* Enables an interrupt when a data is
+ present in the waveform register */
+ else
+ irqen &= ~(1 << 16);
+
+ ret = ade7758_spi_write_reg_24(dev, ADE7758_MASK, irqen);
+ if (ret)
+ goto error_ret;
+
+error_ret:
+ return ret;
+}
+
+/* Power down the device */
+static int ade7758_stop_device(struct device *dev)
+{
+ int ret;
+ u8 val;
+
+ ret = ade7758_spi_read_reg_8(dev, ADE7758_OPMODE, &val);
+ if (ret < 0) {
+ dev_err(dev, "Failed to read opmode reg\n");
+ return ret;
+ }
+ val |= 7 << 3; /* ADE7758 powered down */
+ ret = ade7758_spi_write_reg_8(dev, ADE7758_OPMODE, val);
+ if (ret < 0)
+ dev_err(dev, "Failed to write opmode reg\n");
+ return ret;
+}
+
+static int ade7758_initial_setup(struct iio_dev *indio_dev)
+{
+ struct ade7758_state *st = iio_priv(indio_dev);
+ struct device *dev = &indio_dev->dev;
+ int ret;
+
+ /* use low spi speed for init */
+ st->us->mode = SPI_MODE_1;
+ spi_setup(st->us);
+
+ /* Disable IRQ */
+ ret = ade7758_set_irq(dev, false);
+ if (ret) {
+ dev_err(dev, "disable irq failed");
+ goto err_ret;
+ }
+
+ ade7758_reset(dev);
+ msleep(ADE7758_STARTUP_DELAY);
+
+err_ret:
+ return ret;
+}
+
+static ssize_t ade7758_read_frequency(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ int ret;
+ u8 t;
+ int sps;
+
+ ret = ade7758_spi_read_reg_8(dev,
+ ADE7758_WAVMODE,
+ &t);
+ if (ret)
+ return ret;
+
+ t = (t >> 5) & 0x3;
+ sps = 26040 / (1 << t);
+
+ return sprintf(buf, "%d SPS\n", sps);
+}
+
+static ssize_t ade7758_write_frequency(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t len)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ u16 val;
+ int ret;
+ u8 reg, t;
+
+ ret = kstrtou16(buf, 10, &val);
+ if (ret)
+ return ret;
+
+ mutex_lock(&indio_dev->mlock);
+
+ switch (val) {
+ case 26040:
+ t = 0;
+ break;
+ case 13020:
+ t = 1;
+ break;
+ case 6510:
+ t = 2;
+ break;
+ case 3255:
+ t = 3;
+ break;
+ default:
+ ret = -EINVAL;
+ goto out;
+ }
+
+ ret = ade7758_spi_read_reg_8(dev,
+ ADE7758_WAVMODE,
+ &reg);
+ if (ret)
+ goto out;
+
+ reg &= ~(5 << 3);
+ reg |= t << 5;
+
+ ret = ade7758_spi_write_reg_8(dev,
+ ADE7758_WAVMODE,
+ reg);
+
+out:
+ mutex_unlock(&indio_dev->mlock);
+
+ return ret ? ret : len;
+}
+
+static IIO_DEV_ATTR_TEMP_RAW(ade7758_read_8bit);
+static IIO_CONST_ATTR(in_temp_offset, "129 C");
+static IIO_CONST_ATTR(in_temp_scale, "4 C");
+
+static IIO_DEV_ATTR_AWATTHR(ade7758_read_16bit,
+ ADE7758_AWATTHR);
+static IIO_DEV_ATTR_BWATTHR(ade7758_read_16bit,
+ ADE7758_BWATTHR);
+static IIO_DEV_ATTR_CWATTHR(ade7758_read_16bit,
+ ADE7758_CWATTHR);
+static IIO_DEV_ATTR_AVARHR(ade7758_read_16bit,
+ ADE7758_AVARHR);
+static IIO_DEV_ATTR_BVARHR(ade7758_read_16bit,
+ ADE7758_BVARHR);
+static IIO_DEV_ATTR_CVARHR(ade7758_read_16bit,
+ ADE7758_CVARHR);
+static IIO_DEV_ATTR_AVAHR(ade7758_read_16bit,
+ ADE7758_AVAHR);
+static IIO_DEV_ATTR_BVAHR(ade7758_read_16bit,
+ ADE7758_BVAHR);
+static IIO_DEV_ATTR_CVAHR(ade7758_read_16bit,
+ ADE7758_CVAHR);
+
+static IIO_DEV_ATTR_SAMP_FREQ(S_IWUSR | S_IRUGO,
+ ade7758_read_frequency,
+ ade7758_write_frequency);
+
+static IIO_CONST_ATTR_SAMP_FREQ_AVAIL("26040 13020 6510 3255");
+
+static struct attribute *ade7758_attributes[] = {
+ &iio_dev_attr_in_temp_raw.dev_attr.attr,
+ &iio_const_attr_in_temp_offset.dev_attr.attr,
+ &iio_const_attr_in_temp_scale.dev_attr.attr,
+ &iio_dev_attr_sampling_frequency.dev_attr.attr,
+ &iio_const_attr_sampling_frequency_available.dev_attr.attr,
+ &iio_dev_attr_awatthr.dev_attr.attr,
+ &iio_dev_attr_bwatthr.dev_attr.attr,
+ &iio_dev_attr_cwatthr.dev_attr.attr,
+ &iio_dev_attr_avarhr.dev_attr.attr,
+ &iio_dev_attr_bvarhr.dev_attr.attr,
+ &iio_dev_attr_cvarhr.dev_attr.attr,
+ &iio_dev_attr_avahr.dev_attr.attr,
+ &iio_dev_attr_bvahr.dev_attr.attr,
+ &iio_dev_attr_cvahr.dev_attr.attr,
+ &iio_dev_attr_vpeak.dev_attr.attr,
+ &iio_dev_attr_ipeak.dev_attr.attr,
+ &iio_dev_attr_aphcal.dev_attr.attr,
+ &iio_dev_attr_bphcal.dev_attr.attr,
+ &iio_dev_attr_cphcal.dev_attr.attr,
+ &iio_dev_attr_wdiv.dev_attr.attr,
+ &iio_dev_attr_vadiv.dev_attr.attr,
+ &iio_dev_attr_airms.dev_attr.attr,
+ &iio_dev_attr_birms.dev_attr.attr,
+ &iio_dev_attr_cirms.dev_attr.attr,
+ &iio_dev_attr_avrms.dev_attr.attr,
+ &iio_dev_attr_bvrms.dev_attr.attr,
+ &iio_dev_attr_cvrms.dev_attr.attr,
+ &iio_dev_attr_aigain.dev_attr.attr,
+ &iio_dev_attr_bigain.dev_attr.attr,
+ &iio_dev_attr_cigain.dev_attr.attr,
+ &iio_dev_attr_avrmsgain.dev_attr.attr,
+ &iio_dev_attr_bvrmsgain.dev_attr.attr,
+ &iio_dev_attr_cvrmsgain.dev_attr.attr,
+ &iio_dev_attr_airmsos.dev_attr.attr,
+ &iio_dev_attr_birmsos.dev_attr.attr,
+ &iio_dev_attr_cirmsos.dev_attr.attr,
+ &iio_dev_attr_avrmsos.dev_attr.attr,
+ &iio_dev_attr_bvrmsos.dev_attr.attr,
+ &iio_dev_attr_cvrmsos.dev_attr.attr,
+ NULL,
+};
+
+static const struct attribute_group ade7758_attribute_group = {
+ .attrs = ade7758_attributes,
+};
+
+static const struct iio_chan_spec ade7758_channels[] = {
+ {
+ .type = IIO_VOLTAGE,
+ .indexed = 1,
+ .channel = 0,
+ .address = AD7758_WT(AD7758_PHASE_A, AD7758_VOLTAGE),
+ .scan_index = 0,
+ .scan_type = {
+ .sign = 's',
+ .realbits = 24,
+ .storagebits = 32,
+ },
+ }, {
+ .type = IIO_CURRENT,
+ .indexed = 1,
+ .channel = 0,
+ .address = AD7758_WT(AD7758_PHASE_A, AD7758_CURRENT),
+ .scan_index = 1,
+ .scan_type = {
+ .sign = 's',
+ .realbits = 24,
+ .storagebits = 32,
+ },
+ }, {
+ .type = IIO_POWER,
+ .indexed = 1,
+ .channel = 0,
+ .extend_name = "apparent",
+ .address = AD7758_WT(AD7758_PHASE_A, AD7758_APP_PWR),
+ .scan_index = 2,
+ .scan_type = {
+ .sign = 's',
+ .realbits = 24,
+ .storagebits = 32,
+ },
+ }, {
+ .type = IIO_POWER,
+ .indexed = 1,
+ .channel = 0,
+ .extend_name = "active",
+ .address = AD7758_WT(AD7758_PHASE_A, AD7758_ACT_PWR),
+ .scan_index = 3,
+ .scan_type = {
+ .sign = 's',
+ .realbits = 24,
+ .storagebits = 32,
+ },
+ }, {
+ .type = IIO_POWER,
+ .indexed = 1,
+ .channel = 0,
+ .extend_name = "reactive",
+ .address = AD7758_WT(AD7758_PHASE_A, AD7758_REACT_PWR),
+ .scan_index = 4,
+ .scan_type = {
+ .sign = 's',
+ .realbits = 24,
+ .storagebits = 32,
+ },
+ }, {
+ .type = IIO_VOLTAGE,
+ .indexed = 1,
+ .channel = 1,
+ .address = AD7758_WT(AD7758_PHASE_B, AD7758_VOLTAGE),
+ .scan_index = 5,
+ .scan_type = {
+ .sign = 's',
+ .realbits = 24,
+ .storagebits = 32,
+ },
+ }, {
+ .type = IIO_CURRENT,
+ .indexed = 1,
+ .channel = 1,
+ .address = AD7758_WT(AD7758_PHASE_B, AD7758_CURRENT),
+ .scan_index = 6,
+ .scan_type = {
+ .sign = 's',
+ .realbits = 24,
+ .storagebits = 32,
+ },
+ }, {
+ .type = IIO_POWER,
+ .indexed = 1,
+ .channel = 1,
+ .extend_name = "apparent",
+ .address = AD7758_WT(AD7758_PHASE_B, AD7758_APP_PWR),
+ .scan_index = 7,
+ .scan_type = {
+ .sign = 's',
+ .realbits = 24,
+ .storagebits = 32,
+ },
+ }, {
+ .type = IIO_POWER,
+ .indexed = 1,
+ .channel = 1,
+ .extend_name = "active",
+ .address = AD7758_WT(AD7758_PHASE_B, AD7758_ACT_PWR),
+ .scan_index = 8,
+ .scan_type = {
+ .sign = 's',
+ .realbits = 24,
+ .storagebits = 32,
+ },
+ }, {
+ .type = IIO_POWER,
+ .indexed = 1,
+ .channel = 1,
+ .extend_name = "reactive",
+ .address = AD7758_WT(AD7758_PHASE_B, AD7758_REACT_PWR),
+ .scan_index = 9,
+ .scan_type = {
+ .sign = 's',
+ .realbits = 24,
+ .storagebits = 32,
+ },
+ }, {
+ .type = IIO_VOLTAGE,
+ .indexed = 1,
+ .channel = 2,
+ .address = AD7758_WT(AD7758_PHASE_C, AD7758_VOLTAGE),
+ .scan_index = 10,
+ .scan_type = {
+ .sign = 's',
+ .realbits = 24,
+ .storagebits = 32,
+ },
+ }, {
+ .type = IIO_CURRENT,
+ .indexed = 1,
+ .channel = 2,
+ .address = AD7758_WT(AD7758_PHASE_C, AD7758_CURRENT),
+ .scan_index = 11,
+ .scan_type = {
+ .sign = 's',
+ .realbits = 24,
+ .storagebits = 32,
+ },
+ }, {
+ .type = IIO_POWER,
+ .indexed = 1,
+ .channel = 2,
+ .extend_name = "apparent",
+ .address = AD7758_WT(AD7758_PHASE_C, AD7758_APP_PWR),
+ .scan_index = 12,
+ .scan_type = {
+ .sign = 's',
+ .realbits = 24,
+ .storagebits = 32,
+ },
+ }, {
+ .type = IIO_POWER,
+ .indexed = 1,
+ .channel = 2,
+ .extend_name = "active",
+ .address = AD7758_WT(AD7758_PHASE_C, AD7758_ACT_PWR),
+ .scan_index = 13,
+ .scan_type = {
+ .sign = 's',
+ .realbits = 24,
+ .storagebits = 32,
+ },
+ }, {
+ .type = IIO_POWER,
+ .indexed = 1,
+ .channel = 2,
+ .extend_name = "reactive",
+ .address = AD7758_WT(AD7758_PHASE_C, AD7758_REACT_PWR),
+ .scan_index = 14,
+ .scan_type = {
+ .sign = 's',
+ .realbits = 24,
+ .storagebits = 32,
+ },
+ },
+ IIO_CHAN_SOFT_TIMESTAMP(15),
+};
+
+static const struct iio_info ade7758_info = {
+ .attrs = &ade7758_attribute_group,
+ .driver_module = THIS_MODULE,
+};
+
+static int ade7758_probe(struct spi_device *spi)
+{
+ int ret;
+ struct ade7758_state *st;
+ struct iio_dev *indio_dev;
+
+ indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ st = iio_priv(indio_dev);
+ /* this is only used for removal purposes */
+ spi_set_drvdata(spi, indio_dev);
+
+ /* Allocate the comms buffers */
+ st->rx = kcalloc(ADE7758_MAX_RX, sizeof(*st->rx), GFP_KERNEL);
+ if (!st->rx)
+ return -ENOMEM;
+ st->tx = kcalloc(ADE7758_MAX_TX, sizeof(*st->tx), GFP_KERNEL);
+ if (!st->tx) {
+ ret = -ENOMEM;
+ goto error_free_rx;
+ }
+ st->us = spi;
+ mutex_init(&st->buf_lock);
+
+ indio_dev->name = spi->dev.driver->name;
+ indio_dev->dev.parent = &spi->dev;
+ indio_dev->info = &ade7758_info;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->channels = ade7758_channels;
+ indio_dev->num_channels = ARRAY_SIZE(ade7758_channels);
+
+ ret = ade7758_configure_ring(indio_dev);
+ if (ret)
+ goto error_free_tx;
+
+ /* Get the device into a sane initial state */
+ ret = ade7758_initial_setup(indio_dev);
+ if (ret)
+ goto error_unreg_ring_funcs;
+
+ if (spi->irq) {
+ ret = ade7758_probe_trigger(indio_dev);
+ if (ret)
+ goto error_unreg_ring_funcs;
+ }
+
+ ret = iio_device_register(indio_dev);
+ if (ret)
+ goto error_remove_trigger;
+
+ return 0;
+
+error_remove_trigger:
+ if (spi->irq)
+ ade7758_remove_trigger(indio_dev);
+error_unreg_ring_funcs:
+ ade7758_unconfigure_ring(indio_dev);
+error_free_tx:
+ kfree(st->tx);
+error_free_rx:
+ kfree(st->rx);
+ return ret;
+}
+
+static int ade7758_remove(struct spi_device *spi)
+{
+ struct iio_dev *indio_dev = spi_get_drvdata(spi);
+ struct ade7758_state *st = iio_priv(indio_dev);
+
+ iio_device_unregister(indio_dev);
+ ade7758_stop_device(&indio_dev->dev);
+ ade7758_remove_trigger(indio_dev);
+ ade7758_unconfigure_ring(indio_dev);
+ kfree(st->tx);
+ kfree(st->rx);
+
+ return 0;
+}
+
+static const struct spi_device_id ade7758_id[] = {
+ {"ade7758", 0},
+ {}
+};
+MODULE_DEVICE_TABLE(spi, ade7758_id);
+
+static struct spi_driver ade7758_driver = {
+ .driver = {
+ .name = "ade7758",
+ .owner = THIS_MODULE,
+ },
+ .probe = ade7758_probe,
+ .remove = ade7758_remove,
+ .id_table = ade7758_id,
+};
+module_spi_driver(ade7758_driver);
+
+MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>");
+MODULE_DESCRIPTION("Analog Devices ADE7758 Polyphase Multifunction Energy Metering IC Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/staging/iio/meter/ade7758_ring.c b/drivers/staging/iio/meter/ade7758_ring.c
new file mode 100644
index 000000000..9a24e0226
--- /dev/null
+++ b/drivers/staging/iio/meter/ade7758_ring.c
@@ -0,0 +1,180 @@
+/*
+ * ADE7758 Poly Phase Multifunction Energy Metering IC driver
+ *
+ * Copyright 2010-2011 Analog Devices Inc.
+ *
+ * Licensed under the GPL-2.
+ */
+#include <linux/export.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/spi/spi.h>
+#include <linux/slab.h>
+#include <asm/unaligned.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/kfifo_buf.h>
+#include <linux/iio/trigger_consumer.h>
+#include "ade7758.h"
+
+/**
+ * ade7758_spi_read_burst() - read data registers
+ * @indio_dev: the IIO device
+ **/
+static int ade7758_spi_read_burst(struct iio_dev *indio_dev)
+{
+ struct ade7758_state *st = iio_priv(indio_dev);
+ int ret;
+
+ ret = spi_sync(st->us, &st->ring_msg);
+ if (ret)
+ dev_err(&st->us->dev, "problem when reading WFORM value\n");
+
+ return ret;
+}
+
+static int ade7758_write_waveform_type(struct device *dev, unsigned type)
+{
+ int ret;
+ u8 reg;
+
+ ret = ade7758_spi_read_reg_8(dev,
+ ADE7758_WAVMODE,
+ &reg);
+ if (ret)
+ goto out;
+
+ reg &= ~0x1F;
+ reg |= type & 0x1F;
+
+ ret = ade7758_spi_write_reg_8(dev,
+ ADE7758_WAVMODE,
+ reg);
+out:
+ return ret;
+}
+
+/* Whilst this makes a lot of calls to iio_sw_ring functions - it is too device
+ * specific to be rolled into the core.
+ */
+static irqreturn_t ade7758_trigger_handler(int irq, void *p)
+{
+ struct iio_poll_func *pf = p;
+ struct iio_dev *indio_dev = pf->indio_dev;
+ struct ade7758_state *st = iio_priv(indio_dev);
+ s64 dat64[2];
+ u32 *dat32 = (u32 *)dat64;
+
+ if (!bitmap_empty(indio_dev->active_scan_mask, indio_dev->masklength))
+ if (ade7758_spi_read_burst(indio_dev) >= 0)
+ *dat32 = get_unaligned_be32(&st->rx_buf[5]) & 0xFFFFFF;
+
+ iio_push_to_buffers_with_timestamp(indio_dev, dat64, pf->timestamp);
+
+ iio_trigger_notify_done(indio_dev->trig);
+
+ return IRQ_HANDLED;
+}
+
+/**
+ * ade7758_ring_preenable() setup the parameters of the ring before enabling
+ *
+ * The complex nature of the setting of the number of bytes per datum is due
+ * to this driver currently ensuring that the timestamp is stored at an 8
+ * byte boundary.
+ **/
+static int ade7758_ring_preenable(struct iio_dev *indio_dev)
+{
+ unsigned channel;
+
+ if (bitmap_empty(indio_dev->active_scan_mask, indio_dev->masklength))
+ return -EINVAL;
+
+ channel = find_first_bit(indio_dev->active_scan_mask,
+ indio_dev->masklength);
+
+ ade7758_write_waveform_type(&indio_dev->dev,
+ indio_dev->channels[channel].address);
+
+ return 0;
+}
+
+static const struct iio_buffer_setup_ops ade7758_ring_setup_ops = {
+ .preenable = &ade7758_ring_preenable,
+ .postenable = &iio_triggered_buffer_postenable,
+ .predisable = &iio_triggered_buffer_predisable,
+ .validate_scan_mask = &iio_validate_scan_mask_onehot,
+};
+
+void ade7758_unconfigure_ring(struct iio_dev *indio_dev)
+{
+ iio_dealloc_pollfunc(indio_dev->pollfunc);
+ iio_kfifo_free(indio_dev->buffer);
+}
+
+int ade7758_configure_ring(struct iio_dev *indio_dev)
+{
+ struct ade7758_state *st = iio_priv(indio_dev);
+ struct iio_buffer *buffer;
+ int ret = 0;
+
+ buffer = iio_kfifo_allocate();
+ if (!buffer)
+ return -ENOMEM;
+
+ iio_device_attach_buffer(indio_dev, buffer);
+
+ indio_dev->setup_ops = &ade7758_ring_setup_ops;
+
+ indio_dev->pollfunc = iio_alloc_pollfunc(&iio_pollfunc_store_time,
+ &ade7758_trigger_handler,
+ 0,
+ indio_dev,
+ "ade7759_consumer%d",
+ indio_dev->id);
+ if (!indio_dev->pollfunc) {
+ ret = -ENOMEM;
+ goto error_iio_kfifo_free;
+ }
+
+ indio_dev->modes |= INDIO_BUFFER_TRIGGERED;
+
+ st->tx_buf[0] = ADE7758_READ_REG(ADE7758_RSTATUS);
+ st->tx_buf[1] = 0;
+ st->tx_buf[2] = 0;
+ st->tx_buf[3] = 0;
+ st->tx_buf[4] = ADE7758_READ_REG(ADE7758_WFORM);
+ st->tx_buf[5] = 0;
+ st->tx_buf[6] = 0;
+ st->tx_buf[7] = 0;
+
+ /* build spi ring message */
+ st->ring_xfer[0].tx_buf = &st->tx_buf[0];
+ st->ring_xfer[0].len = 1;
+ st->ring_xfer[0].bits_per_word = 8;
+ st->ring_xfer[0].delay_usecs = 4;
+ st->ring_xfer[1].rx_buf = &st->rx_buf[1];
+ st->ring_xfer[1].len = 3;
+ st->ring_xfer[1].bits_per_word = 8;
+ st->ring_xfer[1].cs_change = 1;
+
+ st->ring_xfer[2].tx_buf = &st->tx_buf[4];
+ st->ring_xfer[2].len = 1;
+ st->ring_xfer[2].bits_per_word = 8;
+ st->ring_xfer[2].delay_usecs = 1;
+ st->ring_xfer[3].rx_buf = &st->rx_buf[5];
+ st->ring_xfer[3].len = 3;
+ st->ring_xfer[3].bits_per_word = 8;
+
+ spi_message_init(&st->ring_msg);
+ spi_message_add_tail(&st->ring_xfer[0], &st->ring_msg);
+ spi_message_add_tail(&st->ring_xfer[1], &st->ring_msg);
+ spi_message_add_tail(&st->ring_xfer[2], &st->ring_msg);
+ spi_message_add_tail(&st->ring_xfer[3], &st->ring_msg);
+
+ return 0;
+
+error_iio_kfifo_free:
+ iio_kfifo_free(indio_dev->buffer);
+ return ret;
+}
diff --git a/drivers/staging/iio/meter/ade7758_trigger.c b/drivers/staging/iio/meter/ade7758_trigger.c
new file mode 100644
index 000000000..5b35a7f08
--- /dev/null
+++ b/drivers/staging/iio/meter/ade7758_trigger.c
@@ -0,0 +1,109 @@
+/*
+ * ADE7758 Poly Phase Multifunction Energy Metering IC driver
+ *
+ * Copyright 2010-2011 Analog Devices Inc.
+ *
+ * Licensed under the GPL-2.
+ */
+
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/spi/spi.h>
+#include <linux/export.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/trigger.h>
+#include "ade7758.h"
+
+/**
+ * ade7758_data_rdy_trig_poll() the event handler for the data rdy trig
+ **/
+static irqreturn_t ade7758_data_rdy_trig_poll(int irq, void *private)
+{
+ disable_irq_nosync(irq);
+ iio_trigger_poll(private);
+
+ return IRQ_HANDLED;
+}
+
+/**
+ * ade7758_data_rdy_trigger_set_state() set datardy interrupt state
+ **/
+static int ade7758_data_rdy_trigger_set_state(struct iio_trigger *trig,
+ bool state)
+{
+ struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig);
+
+ dev_dbg(&indio_dev->dev, "%s (%d)\n", __func__, state);
+ return ade7758_set_irq(&indio_dev->dev, state);
+}
+
+/**
+ * ade7758_trig_try_reen() try renabling irq for data rdy trigger
+ * @trig: the datardy trigger
+ **/
+static int ade7758_trig_try_reen(struct iio_trigger *trig)
+{
+ struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig);
+ struct ade7758_state *st = iio_priv(indio_dev);
+
+ enable_irq(st->us->irq);
+ /* irq reenabled so success! */
+ return 0;
+}
+
+static const struct iio_trigger_ops ade7758_trigger_ops = {
+ .owner = THIS_MODULE,
+ .set_trigger_state = &ade7758_data_rdy_trigger_set_state,
+ .try_reenable = &ade7758_trig_try_reen,
+};
+
+int ade7758_probe_trigger(struct iio_dev *indio_dev)
+{
+ struct ade7758_state *st = iio_priv(indio_dev);
+ int ret;
+
+ st->trig = iio_trigger_alloc("%s-dev%d",
+ spi_get_device_id(st->us)->name,
+ indio_dev->id);
+ if (!st->trig) {
+ ret = -ENOMEM;
+ goto error_ret;
+ }
+
+ ret = request_irq(st->us->irq,
+ ade7758_data_rdy_trig_poll,
+ IRQF_TRIGGER_LOW,
+ spi_get_device_id(st->us)->name,
+ st->trig);
+ if (ret)
+ goto error_free_trig;
+
+ st->trig->dev.parent = &st->us->dev;
+ st->trig->ops = &ade7758_trigger_ops;
+ iio_trigger_set_drvdata(st->trig, indio_dev);
+ ret = iio_trigger_register(st->trig);
+
+ /* select default trigger */
+ indio_dev->trig = iio_trigger_get(st->trig);
+ if (ret)
+ goto error_free_irq;
+
+ return 0;
+
+error_free_irq:
+ free_irq(st->us->irq, st->trig);
+error_free_trig:
+ iio_trigger_free(st->trig);
+error_ret:
+ return ret;
+}
+
+void ade7758_remove_trigger(struct iio_dev *indio_dev)
+{
+ struct ade7758_state *st = iio_priv(indio_dev);
+
+ iio_trigger_unregister(st->trig);
+ free_irq(st->us->irq, st->trig);
+ iio_trigger_free(st->trig);
+}
diff --git a/drivers/staging/iio/meter/ade7759.c b/drivers/staging/iio/meter/ade7759.c
new file mode 100644
index 000000000..dbceda1e6
--- /dev/null
+++ b/drivers/staging/iio/meter/ade7759.c
@@ -0,0 +1,503 @@
+/*
+ * ADE7759 Active Energy Metering IC with di/dt Sensor Interface Driver
+ *
+ * Copyright 2010 Analog Devices Inc.
+ *
+ * Licensed under the GPL-2 or later.
+ */
+
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/delay.h>
+#include <linux/mutex.h>
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/spi/spi.h>
+#include <linux/slab.h>
+#include <linux/sysfs.h>
+#include <linux/list.h>
+#include <linux/module.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include "meter.h"
+#include "ade7759.h"
+
+static int ade7759_spi_write_reg_8(struct device *dev,
+ u8 reg_address,
+ u8 val)
+{
+ int ret;
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct ade7759_state *st = iio_priv(indio_dev);
+
+ mutex_lock(&st->buf_lock);
+ st->tx[0] = ADE7759_WRITE_REG(reg_address);
+ st->tx[1] = val;
+
+ ret = spi_write(st->us, st->tx, 2);
+ mutex_unlock(&st->buf_lock);
+
+ return ret;
+}
+
+static int ade7759_spi_write_reg_16(struct device *dev,
+ u8 reg_address,
+ u16 value)
+{
+ int ret;
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct ade7759_state *st = iio_priv(indio_dev);
+
+ mutex_lock(&st->buf_lock);
+ st->tx[0] = ADE7759_WRITE_REG(reg_address);
+ st->tx[1] = (value >> 8) & 0xFF;
+ st->tx[2] = value & 0xFF;
+ ret = spi_write(st->us, st->tx, 3);
+ mutex_unlock(&st->buf_lock);
+
+ return ret;
+}
+
+static int ade7759_spi_read_reg_8(struct device *dev,
+ u8 reg_address,
+ u8 *val)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct ade7759_state *st = iio_priv(indio_dev);
+ int ret;
+
+ ret = spi_w8r8(st->us, ADE7759_READ_REG(reg_address));
+ if (ret < 0) {
+ dev_err(&st->us->dev, "problem when reading 8 bit register 0x%02X",
+ reg_address);
+ return ret;
+ }
+ *val = ret;
+
+ return 0;
+}
+
+static int ade7759_spi_read_reg_16(struct device *dev,
+ u8 reg_address,
+ u16 *val)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct ade7759_state *st = iio_priv(indio_dev);
+ int ret;
+
+ ret = spi_w8r16be(st->us, ADE7759_READ_REG(reg_address));
+ if (ret < 0) {
+ dev_err(&st->us->dev, "problem when reading 16 bit register 0x%02X",
+ reg_address);
+ return ret;
+ }
+
+ *val = ret;
+
+ return 0;
+}
+
+static int ade7759_spi_read_reg_40(struct device *dev,
+ u8 reg_address,
+ u64 *val)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct ade7759_state *st = iio_priv(indio_dev);
+ int ret;
+ struct spi_transfer xfers[] = {
+ {
+ .tx_buf = st->tx,
+ .rx_buf = st->rx,
+ .bits_per_word = 8,
+ .len = 6,
+ },
+ };
+
+ mutex_lock(&st->buf_lock);
+ st->tx[0] = ADE7759_READ_REG(reg_address);
+ memset(&st->tx[1], 0, 5);
+
+ ret = spi_sync_transfer(st->us, xfers, ARRAY_SIZE(xfers));
+ if (ret) {
+ dev_err(&st->us->dev, "problem when reading 40 bit register 0x%02X",
+ reg_address);
+ goto error_ret;
+ }
+ *val = ((u64)st->rx[1] << 32) | (st->rx[2] << 24) |
+ (st->rx[3] << 16) | (st->rx[4] << 8) | st->rx[5];
+
+error_ret:
+ mutex_unlock(&st->buf_lock);
+ return ret;
+}
+
+static ssize_t ade7759_read_8bit(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ int ret;
+ u8 val = 0;
+ struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
+
+ ret = ade7759_spi_read_reg_8(dev, this_attr->address, &val);
+ if (ret)
+ return ret;
+
+ return sprintf(buf, "%u\n", val);
+}
+
+static ssize_t ade7759_read_16bit(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ int ret;
+ u16 val = 0;
+ struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
+
+ ret = ade7759_spi_read_reg_16(dev, this_attr->address, &val);
+ if (ret)
+ return ret;
+
+ return sprintf(buf, "%u\n", val);
+}
+
+static ssize_t ade7759_read_40bit(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ int ret;
+ u64 val = 0;
+ struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
+
+ ret = ade7759_spi_read_reg_40(dev, this_attr->address, &val);
+ if (ret)
+ return ret;
+
+ return sprintf(buf, "%llu\n", val);
+}
+
+static ssize_t ade7759_write_8bit(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t len)
+{
+ struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
+ int ret;
+ u8 val;
+
+ ret = kstrtou8(buf, 10, &val);
+ if (ret)
+ goto error_ret;
+ ret = ade7759_spi_write_reg_8(dev, this_attr->address, val);
+
+error_ret:
+ return ret ? ret : len;
+}
+
+static ssize_t ade7759_write_16bit(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t len)
+{
+ struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
+ int ret;
+ u16 val;
+
+ ret = kstrtou16(buf, 10, &val);
+ if (ret)
+ goto error_ret;
+ ret = ade7759_spi_write_reg_16(dev, this_attr->address, val);
+
+error_ret:
+ return ret ? ret : len;
+}
+
+static int ade7759_reset(struct device *dev)
+{
+ int ret;
+ u16 val;
+
+ ret = ade7759_spi_read_reg_16(dev,
+ ADE7759_MODE,
+ &val);
+ if (ret < 0)
+ return ret;
+
+ val |= 1 << 6; /* Software Chip Reset */
+ return ade7759_spi_write_reg_16(dev,
+ ADE7759_MODE,
+ val);
+}
+
+static IIO_DEV_ATTR_AENERGY(ade7759_read_40bit, ADE7759_AENERGY);
+static IIO_DEV_ATTR_CFDEN(S_IWUSR | S_IRUGO,
+ ade7759_read_16bit,
+ ade7759_write_16bit,
+ ADE7759_CFDEN);
+static IIO_DEV_ATTR_CFNUM(S_IWUSR | S_IRUGO,
+ ade7759_read_8bit,
+ ade7759_write_8bit,
+ ADE7759_CFNUM);
+static IIO_DEV_ATTR_CHKSUM(ade7759_read_8bit, ADE7759_CHKSUM);
+static IIO_DEV_ATTR_PHCAL(S_IWUSR | S_IRUGO,
+ ade7759_read_16bit,
+ ade7759_write_16bit,
+ ADE7759_PHCAL);
+static IIO_DEV_ATTR_APOS(S_IWUSR | S_IRUGO,
+ ade7759_read_16bit,
+ ade7759_write_16bit,
+ ADE7759_APOS);
+static IIO_DEV_ATTR_SAGCYC(S_IWUSR | S_IRUGO,
+ ade7759_read_8bit,
+ ade7759_write_8bit,
+ ADE7759_SAGCYC);
+static IIO_DEV_ATTR_SAGLVL(S_IWUSR | S_IRUGO,
+ ade7759_read_8bit,
+ ade7759_write_8bit,
+ ADE7759_SAGLVL);
+static IIO_DEV_ATTR_LINECYC(S_IWUSR | S_IRUGO,
+ ade7759_read_8bit,
+ ade7759_write_8bit,
+ ADE7759_LINECYC);
+static IIO_DEV_ATTR_LENERGY(ade7759_read_40bit, ADE7759_LENERGY);
+static IIO_DEV_ATTR_PGA_GAIN(S_IWUSR | S_IRUGO,
+ ade7759_read_8bit,
+ ade7759_write_8bit,
+ ADE7759_GAIN);
+static IIO_DEV_ATTR_ACTIVE_POWER_GAIN(S_IWUSR | S_IRUGO,
+ ade7759_read_16bit,
+ ade7759_write_16bit,
+ ADE7759_APGAIN);
+static IIO_DEV_ATTR_CH_OFF(1, S_IWUSR | S_IRUGO,
+ ade7759_read_8bit,
+ ade7759_write_8bit,
+ ADE7759_CH1OS);
+static IIO_DEV_ATTR_CH_OFF(2, S_IWUSR | S_IRUGO,
+ ade7759_read_8bit,
+ ade7759_write_8bit,
+ ADE7759_CH2OS);
+
+static int ade7759_set_irq(struct device *dev, bool enable)
+{
+ int ret;
+ u8 irqen;
+
+ ret = ade7759_spi_read_reg_8(dev, ADE7759_IRQEN, &irqen);
+ if (ret)
+ goto error_ret;
+
+ if (enable)
+ irqen |= 1 << 3; /* Enables an interrupt when a data is
+ present in the waveform register */
+ else
+ irqen &= ~(1 << 3);
+
+ ret = ade7759_spi_write_reg_8(dev, ADE7759_IRQEN, irqen);
+
+error_ret:
+ return ret;
+}
+
+/* Power down the device */
+static int ade7759_stop_device(struct device *dev)
+{
+ int ret;
+ u16 val;
+
+ ret = ade7759_spi_read_reg_16(dev,
+ ADE7759_MODE,
+ &val);
+ if (ret < 0) {
+ dev_err(dev, "unable to power down the device, error: %d\n",
+ ret);
+ return ret;
+ }
+
+ val |= 1 << 4; /* AD converters can be turned off */
+
+ return ade7759_spi_write_reg_16(dev, ADE7759_MODE, val);
+}
+
+static int ade7759_initial_setup(struct iio_dev *indio_dev)
+{
+ int ret;
+ struct ade7759_state *st = iio_priv(indio_dev);
+ struct device *dev = &indio_dev->dev;
+
+ /* use low spi speed for init */
+ st->us->mode = SPI_MODE_3;
+ spi_setup(st->us);
+
+ /* Disable IRQ */
+ ret = ade7759_set_irq(dev, false);
+ if (ret) {
+ dev_err(dev, "disable irq failed");
+ goto err_ret;
+ }
+
+ ade7759_reset(dev);
+ msleep(ADE7759_STARTUP_DELAY);
+
+err_ret:
+ return ret;
+}
+
+static ssize_t ade7759_read_frequency(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ int ret;
+ u16 t;
+ int sps;
+
+ ret = ade7759_spi_read_reg_16(dev,
+ ADE7759_MODE,
+ &t);
+ if (ret)
+ return ret;
+
+ t = (t >> 3) & 0x3;
+ sps = 27900 / (1 + t);
+
+ return sprintf(buf, "%d\n", sps);
+}
+
+static ssize_t ade7759_write_frequency(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t len)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct ade7759_state *st = iio_priv(indio_dev);
+ u16 val;
+ int ret;
+ u16 reg, t;
+
+ ret = kstrtou16(buf, 10, &val);
+ if (ret)
+ return ret;
+ if (val == 0)
+ return -EINVAL;
+
+ mutex_lock(&indio_dev->mlock);
+
+ t = 27900 / val;
+ if (t > 0)
+ t--;
+
+ if (t > 1)
+ st->us->max_speed_hz = ADE7759_SPI_SLOW;
+ else
+ st->us->max_speed_hz = ADE7759_SPI_FAST;
+
+ ret = ade7759_spi_read_reg_16(dev, ADE7759_MODE, &reg);
+ if (ret)
+ goto out;
+
+ reg &= ~(3 << 13);
+ reg |= t << 13;
+
+ ret = ade7759_spi_write_reg_16(dev, ADE7759_MODE, reg);
+
+out:
+ mutex_unlock(&indio_dev->mlock);
+
+ return ret ? ret : len;
+}
+static IIO_DEV_ATTR_TEMP_RAW(ade7759_read_8bit);
+static IIO_CONST_ATTR(in_temp_offset, "70 C");
+static IIO_CONST_ATTR(in_temp_scale, "1 C");
+
+static IIO_DEV_ATTR_SAMP_FREQ(S_IWUSR | S_IRUGO,
+ ade7759_read_frequency,
+ ade7759_write_frequency);
+
+static IIO_CONST_ATTR_SAMP_FREQ_AVAIL("27900 14000 7000 3500");
+
+static struct attribute *ade7759_attributes[] = {
+ &iio_dev_attr_in_temp_raw.dev_attr.attr,
+ &iio_const_attr_in_temp_offset.dev_attr.attr,
+ &iio_const_attr_in_temp_scale.dev_attr.attr,
+ &iio_dev_attr_sampling_frequency.dev_attr.attr,
+ &iio_const_attr_sampling_frequency_available.dev_attr.attr,
+ &iio_dev_attr_phcal.dev_attr.attr,
+ &iio_dev_attr_cfden.dev_attr.attr,
+ &iio_dev_attr_aenergy.dev_attr.attr,
+ &iio_dev_attr_cfnum.dev_attr.attr,
+ &iio_dev_attr_apos.dev_attr.attr,
+ &iio_dev_attr_sagcyc.dev_attr.attr,
+ &iio_dev_attr_saglvl.dev_attr.attr,
+ &iio_dev_attr_linecyc.dev_attr.attr,
+ &iio_dev_attr_lenergy.dev_attr.attr,
+ &iio_dev_attr_chksum.dev_attr.attr,
+ &iio_dev_attr_pga_gain.dev_attr.attr,
+ &iio_dev_attr_active_power_gain.dev_attr.attr,
+ &iio_dev_attr_choff_1.dev_attr.attr,
+ &iio_dev_attr_choff_2.dev_attr.attr,
+ NULL,
+};
+
+static const struct attribute_group ade7759_attribute_group = {
+ .attrs = ade7759_attributes,
+};
+
+static const struct iio_info ade7759_info = {
+ .attrs = &ade7759_attribute_group,
+ .driver_module = THIS_MODULE,
+};
+
+static int ade7759_probe(struct spi_device *spi)
+{
+ int ret;
+ struct ade7759_state *st;
+ struct iio_dev *indio_dev;
+
+ /* setup the industrialio driver allocated elements */
+ indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));
+ if (!indio_dev)
+ return -ENOMEM;
+ /* this is only used for removal purposes */
+ spi_set_drvdata(spi, indio_dev);
+
+ st = iio_priv(indio_dev);
+ st->us = spi;
+ mutex_init(&st->buf_lock);
+ indio_dev->name = spi->dev.driver->name;
+ indio_dev->dev.parent = &spi->dev;
+ indio_dev->info = &ade7759_info;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+
+ /* Get the device into a sane initial state */
+ ret = ade7759_initial_setup(indio_dev);
+ if (ret)
+ return ret;
+
+ return iio_device_register(indio_dev);
+}
+
+/* fixme, confirm ordering in this function */
+static int ade7759_remove(struct spi_device *spi)
+{
+ struct iio_dev *indio_dev = spi_get_drvdata(spi);
+
+ iio_device_unregister(indio_dev);
+ ade7759_stop_device(&indio_dev->dev);
+
+ return 0;
+}
+
+static struct spi_driver ade7759_driver = {
+ .driver = {
+ .name = "ade7759",
+ .owner = THIS_MODULE,
+ },
+ .probe = ade7759_probe,
+ .remove = ade7759_remove,
+};
+module_spi_driver(ade7759_driver);
+
+MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>");
+MODULE_DESCRIPTION("Analog Devices ADE7759 Active Energy Metering IC Driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("spi:ad7759");
diff --git a/drivers/staging/iio/meter/ade7759.h b/drivers/staging/iio/meter/ade7759.h
new file mode 100644
index 000000000..f9ff1f8e7
--- /dev/null
+++ b/drivers/staging/iio/meter/ade7759.h
@@ -0,0 +1,53 @@
+#ifndef _ADE7759_H
+#define _ADE7759_H
+
+#define ADE7759_WAVEFORM 0x01
+#define ADE7759_AENERGY 0x02
+#define ADE7759_RSTENERGY 0x03
+#define ADE7759_STATUS 0x04
+#define ADE7759_RSTSTATUS 0x05
+#define ADE7759_MODE 0x06
+#define ADE7759_CFDEN 0x07
+#define ADE7759_CH1OS 0x08
+#define ADE7759_CH2OS 0x09
+#define ADE7759_GAIN 0x0A
+#define ADE7759_APGAIN 0x0B
+#define ADE7759_PHCAL 0x0C
+#define ADE7759_APOS 0x0D
+#define ADE7759_ZXTOUT 0x0E
+#define ADE7759_SAGCYC 0x0F
+#define ADE7759_IRQEN 0x10
+#define ADE7759_SAGLVL 0x11
+#define ADE7759_TEMP 0x12
+#define ADE7759_LINECYC 0x13
+#define ADE7759_LENERGY 0x14
+#define ADE7759_CFNUM 0x15
+#define ADE7759_CHKSUM 0x1E
+#define ADE7759_DIEREV 0x1F
+
+#define ADE7759_READ_REG(a) a
+#define ADE7759_WRITE_REG(a) ((a) | 0x80)
+
+#define ADE7759_MAX_TX 6
+#define ADE7759_MAX_RX 6
+#define ADE7759_STARTUP_DELAY 1
+
+#define ADE7759_SPI_SLOW (u32)(300 * 1000)
+#define ADE7759_SPI_BURST (u32)(1000 * 1000)
+#define ADE7759_SPI_FAST (u32)(2000 * 1000)
+
+/**
+ * struct ade7759_state - device instance specific data
+ * @us: actual spi_device
+ * @buf_lock: mutex to protect tx and rx
+ * @tx: transmit buffer
+ * @rx: receive buffer
+ **/
+struct ade7759_state {
+ struct spi_device *us;
+ struct mutex buf_lock;
+ u8 tx[ADE7759_MAX_TX] ____cacheline_aligned;
+ u8 rx[ADE7759_MAX_RX];
+};
+
+#endif
diff --git a/drivers/staging/iio/meter/ade7854-i2c.c b/drivers/staging/iio/meter/ade7854-i2c.c
new file mode 100644
index 000000000..07cfe28b2
--- /dev/null
+++ b/drivers/staging/iio/meter/ade7854-i2c.c
@@ -0,0 +1,256 @@
+/*
+ * ADE7854/58/68/78 Polyphase Multifunction Energy Metering IC Driver (I2C Bus)
+ *
+ * Copyright 2010 Analog Devices Inc.
+ *
+ * Licensed under the GPL-2 or later.
+ */
+
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/i2c.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+
+#include <linux/iio/iio.h>
+#include "ade7854.h"
+
+static int ade7854_i2c_write_reg_8(struct device *dev,
+ u16 reg_address,
+ u8 value)
+{
+ int ret;
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct ade7854_state *st = iio_priv(indio_dev);
+
+ mutex_lock(&st->buf_lock);
+ st->tx[0] = (reg_address >> 8) & 0xFF;
+ st->tx[1] = reg_address & 0xFF;
+ st->tx[2] = value;
+
+ ret = i2c_master_send(st->i2c, st->tx, 3);
+ mutex_unlock(&st->buf_lock);
+
+ return ret;
+}
+
+static int ade7854_i2c_write_reg_16(struct device *dev,
+ u16 reg_address,
+ u16 value)
+{
+ int ret;
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct ade7854_state *st = iio_priv(indio_dev);
+
+ mutex_lock(&st->buf_lock);
+ st->tx[0] = (reg_address >> 8) & 0xFF;
+ st->tx[1] = reg_address & 0xFF;
+ st->tx[2] = (value >> 8) & 0xFF;
+ st->tx[3] = value & 0xFF;
+
+ ret = i2c_master_send(st->i2c, st->tx, 4);
+ mutex_unlock(&st->buf_lock);
+
+ return ret;
+}
+
+static int ade7854_i2c_write_reg_24(struct device *dev,
+ u16 reg_address,
+ u32 value)
+{
+ int ret;
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct ade7854_state *st = iio_priv(indio_dev);
+
+ mutex_lock(&st->buf_lock);
+ st->tx[0] = (reg_address >> 8) & 0xFF;
+ st->tx[1] = reg_address & 0xFF;
+ st->tx[2] = (value >> 16) & 0xFF;
+ st->tx[3] = (value >> 8) & 0xFF;
+ st->tx[4] = value & 0xFF;
+
+ ret = i2c_master_send(st->i2c, st->tx, 5);
+ mutex_unlock(&st->buf_lock);
+
+ return ret;
+}
+
+static int ade7854_i2c_write_reg_32(struct device *dev,
+ u16 reg_address,
+ u32 value)
+{
+ int ret;
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct ade7854_state *st = iio_priv(indio_dev);
+
+ mutex_lock(&st->buf_lock);
+ st->tx[0] = (reg_address >> 8) & 0xFF;
+ st->tx[1] = reg_address & 0xFF;
+ st->tx[2] = (value >> 24) & 0xFF;
+ st->tx[3] = (value >> 16) & 0xFF;
+ st->tx[4] = (value >> 8) & 0xFF;
+ st->tx[5] = value & 0xFF;
+
+ ret = i2c_master_send(st->i2c, st->tx, 6);
+ mutex_unlock(&st->buf_lock);
+
+ return ret;
+}
+
+static int ade7854_i2c_read_reg_8(struct device *dev,
+ u16 reg_address,
+ u8 *val)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct ade7854_state *st = iio_priv(indio_dev);
+ int ret;
+
+ mutex_lock(&st->buf_lock);
+ st->tx[0] = (reg_address >> 8) & 0xFF;
+ st->tx[1] = reg_address & 0xFF;
+
+ ret = i2c_master_send(st->i2c, st->tx, 2);
+ if (ret)
+ goto out;
+
+ ret = i2c_master_recv(st->i2c, st->rx, 1);
+ if (ret)
+ goto out;
+
+ *val = st->rx[0];
+out:
+ mutex_unlock(&st->buf_lock);
+ return ret;
+}
+
+static int ade7854_i2c_read_reg_16(struct device *dev,
+ u16 reg_address,
+ u16 *val)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct ade7854_state *st = iio_priv(indio_dev);
+ int ret;
+
+ mutex_lock(&st->buf_lock);
+ st->tx[0] = (reg_address >> 8) & 0xFF;
+ st->tx[1] = reg_address & 0xFF;
+
+ ret = i2c_master_send(st->i2c, st->tx, 2);
+ if (ret)
+ goto out;
+
+ ret = i2c_master_recv(st->i2c, st->rx, 2);
+ if (ret)
+ goto out;
+
+ *val = (st->rx[0] << 8) | st->rx[1];
+out:
+ mutex_unlock(&st->buf_lock);
+ return ret;
+}
+
+static int ade7854_i2c_read_reg_24(struct device *dev,
+ u16 reg_address,
+ u32 *val)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct ade7854_state *st = iio_priv(indio_dev);
+ int ret;
+
+ mutex_lock(&st->buf_lock);
+ st->tx[0] = (reg_address >> 8) & 0xFF;
+ st->tx[1] = reg_address & 0xFF;
+
+ ret = i2c_master_send(st->i2c, st->tx, 2);
+ if (ret)
+ goto out;
+
+ ret = i2c_master_recv(st->i2c, st->rx, 3);
+ if (ret)
+ goto out;
+
+ *val = (st->rx[0] << 16) | (st->rx[1] << 8) | st->rx[2];
+out:
+ mutex_unlock(&st->buf_lock);
+ return ret;
+}
+
+static int ade7854_i2c_read_reg_32(struct device *dev,
+ u16 reg_address,
+ u32 *val)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct ade7854_state *st = iio_priv(indio_dev);
+ int ret;
+
+ mutex_lock(&st->buf_lock);
+ st->tx[0] = (reg_address >> 8) & 0xFF;
+ st->tx[1] = reg_address & 0xFF;
+
+ ret = i2c_master_send(st->i2c, st->tx, 2);
+ if (ret)
+ goto out;
+
+ ret = i2c_master_recv(st->i2c, st->rx, 3);
+ if (ret)
+ goto out;
+
+ *val = (st->rx[0] << 24) | (st->rx[1] << 16) |
+ (st->rx[2] << 8) | st->rx[3];
+out:
+ mutex_unlock(&st->buf_lock);
+ return ret;
+}
+
+static int ade7854_i2c_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct ade7854_state *st;
+ struct iio_dev *indio_dev;
+
+ indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*st));
+ if (!indio_dev)
+ return -ENOMEM;
+ st = iio_priv(indio_dev);
+ i2c_set_clientdata(client, indio_dev);
+ st->read_reg_8 = ade7854_i2c_read_reg_8;
+ st->read_reg_16 = ade7854_i2c_read_reg_16;
+ st->read_reg_24 = ade7854_i2c_read_reg_24;
+ st->read_reg_32 = ade7854_i2c_read_reg_32;
+ st->write_reg_8 = ade7854_i2c_write_reg_8;
+ st->write_reg_16 = ade7854_i2c_write_reg_16;
+ st->write_reg_24 = ade7854_i2c_write_reg_24;
+ st->write_reg_32 = ade7854_i2c_write_reg_32;
+ st->i2c = client;
+ st->irq = client->irq;
+
+ return ade7854_probe(indio_dev, &client->dev);
+}
+
+static int ade7854_i2c_remove(struct i2c_client *client)
+{
+ return ade7854_remove(i2c_get_clientdata(client));
+}
+
+static const struct i2c_device_id ade7854_id[] = {
+ { "ade7854", 0 },
+ { "ade7858", 0 },
+ { "ade7868", 0 },
+ { "ade7878", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, ade7854_id);
+
+static struct i2c_driver ade7854_i2c_driver = {
+ .driver = {
+ .name = "ade7854",
+ },
+ .probe = ade7854_i2c_probe,
+ .remove = ade7854_i2c_remove,
+ .id_table = ade7854_id,
+};
+module_i2c_driver(ade7854_i2c_driver);
+
+MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>");
+MODULE_DESCRIPTION("Analog Devices ADE7854/58/68/78 Polyphase Multifunction Energy Metering IC I2C Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/staging/iio/meter/ade7854-spi.c b/drivers/staging/iio/meter/ade7854-spi.c
new file mode 100644
index 000000000..9b255a5f6
--- /dev/null
+++ b/drivers/staging/iio/meter/ade7854-spi.c
@@ -0,0 +1,327 @@
+/*
+ * ADE7854/58/68/78 Polyphase Multifunction Energy Metering IC Driver (SPI Bus)
+ *
+ * Copyright 2010 Analog Devices Inc.
+ *
+ * Licensed under the GPL-2 or later.
+ */
+
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/spi/spi.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+
+#include <linux/iio/iio.h>
+#include "ade7854.h"
+
+static int ade7854_spi_write_reg_8(struct device *dev,
+ u16 reg_address,
+ u8 value)
+{
+ int ret;
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct ade7854_state *st = iio_priv(indio_dev);
+ struct spi_transfer xfer = {
+ .tx_buf = st->tx,
+ .bits_per_word = 8,
+ .len = 4,
+ };
+
+ mutex_lock(&st->buf_lock);
+ st->tx[0] = ADE7854_WRITE_REG;
+ st->tx[1] = (reg_address >> 8) & 0xFF;
+ st->tx[2] = reg_address & 0xFF;
+ st->tx[3] = value & 0xFF;
+
+ ret = spi_sync_transfer(st->spi, &xfer, 1);
+ mutex_unlock(&st->buf_lock);
+
+ return ret;
+}
+
+static int ade7854_spi_write_reg_16(struct device *dev,
+ u16 reg_address,
+ u16 value)
+{
+ int ret;
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct ade7854_state *st = iio_priv(indio_dev);
+ struct spi_transfer xfer = {
+ .tx_buf = st->tx,
+ .bits_per_word = 8,
+ .len = 5,
+ };
+
+ mutex_lock(&st->buf_lock);
+ st->tx[0] = ADE7854_WRITE_REG;
+ st->tx[1] = (reg_address >> 8) & 0xFF;
+ st->tx[2] = reg_address & 0xFF;
+ st->tx[3] = (value >> 8) & 0xFF;
+ st->tx[4] = value & 0xFF;
+
+ ret = spi_sync_transfer(st->spi, &xfer, 1);
+ mutex_unlock(&st->buf_lock);
+
+ return ret;
+}
+
+static int ade7854_spi_write_reg_24(struct device *dev,
+ u16 reg_address,
+ u32 value)
+{
+ int ret;
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct ade7854_state *st = iio_priv(indio_dev);
+ struct spi_transfer xfer = {
+ .tx_buf = st->tx,
+ .bits_per_word = 8,
+ .len = 6,
+ };
+
+ mutex_lock(&st->buf_lock);
+ st->tx[0] = ADE7854_WRITE_REG;
+ st->tx[1] = (reg_address >> 8) & 0xFF;
+ st->tx[2] = reg_address & 0xFF;
+ st->tx[3] = (value >> 16) & 0xFF;
+ st->tx[4] = (value >> 8) & 0xFF;
+ st->tx[5] = value & 0xFF;
+
+ ret = spi_sync_transfer(st->spi, &xfer, 1);
+ mutex_unlock(&st->buf_lock);
+
+ return ret;
+}
+
+static int ade7854_spi_write_reg_32(struct device *dev,
+ u16 reg_address,
+ u32 value)
+{
+ int ret;
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct ade7854_state *st = iio_priv(indio_dev);
+ struct spi_transfer xfer = {
+ .tx_buf = st->tx,
+ .bits_per_word = 8,
+ .len = 7,
+ };
+
+ mutex_lock(&st->buf_lock);
+ st->tx[0] = ADE7854_WRITE_REG;
+ st->tx[1] = (reg_address >> 8) & 0xFF;
+ st->tx[2] = reg_address & 0xFF;
+ st->tx[3] = (value >> 24) & 0xFF;
+ st->tx[4] = (value >> 16) & 0xFF;
+ st->tx[5] = (value >> 8) & 0xFF;
+ st->tx[6] = value & 0xFF;
+
+ ret = spi_sync_transfer(st->spi, &xfer, 1);
+ mutex_unlock(&st->buf_lock);
+
+ return ret;
+}
+
+static int ade7854_spi_read_reg_8(struct device *dev,
+ u16 reg_address,
+ u8 *val)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct ade7854_state *st = iio_priv(indio_dev);
+ int ret;
+ struct spi_transfer xfers[] = {
+ {
+ .tx_buf = st->tx,
+ .bits_per_word = 8,
+ .len = 3,
+ }, {
+ .rx_buf = st->rx,
+ .bits_per_word = 8,
+ .len = 1,
+ }
+ };
+
+ mutex_lock(&st->buf_lock);
+
+ st->tx[0] = ADE7854_READ_REG;
+ st->tx[1] = (reg_address >> 8) & 0xFF;
+ st->tx[2] = reg_address & 0xFF;
+
+ ret = spi_sync_transfer(st->spi, xfers, ARRAY_SIZE(xfers));
+ if (ret) {
+ dev_err(&st->spi->dev, "problem when reading 8 bit register 0x%02X",
+ reg_address);
+ goto error_ret;
+ }
+ *val = st->rx[0];
+
+error_ret:
+ mutex_unlock(&st->buf_lock);
+ return ret;
+}
+
+static int ade7854_spi_read_reg_16(struct device *dev,
+ u16 reg_address,
+ u16 *val)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct ade7854_state *st = iio_priv(indio_dev);
+ int ret;
+ struct spi_transfer xfers[] = {
+ {
+ .tx_buf = st->tx,
+ .bits_per_word = 8,
+ .len = 3,
+ }, {
+ .rx_buf = st->rx,
+ .bits_per_word = 8,
+ .len = 2,
+ }
+ };
+
+ mutex_lock(&st->buf_lock);
+ st->tx[0] = ADE7854_READ_REG;
+ st->tx[1] = (reg_address >> 8) & 0xFF;
+ st->tx[2] = reg_address & 0xFF;
+
+ ret = spi_sync_transfer(st->spi, xfers, ARRAY_SIZE(xfers));
+ if (ret) {
+ dev_err(&st->spi->dev, "problem when reading 16 bit register 0x%02X",
+ reg_address);
+ goto error_ret;
+ }
+ *val = be16_to_cpup((const __be16 *)st->rx);
+
+error_ret:
+ mutex_unlock(&st->buf_lock);
+ return ret;
+}
+
+static int ade7854_spi_read_reg_24(struct device *dev,
+ u16 reg_address,
+ u32 *val)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct ade7854_state *st = iio_priv(indio_dev);
+ int ret;
+ struct spi_transfer xfers[] = {
+ {
+ .tx_buf = st->tx,
+ .bits_per_word = 8,
+ .len = 3,
+ }, {
+ .rx_buf = st->rx,
+ .bits_per_word = 8,
+ .len = 3,
+ }
+ };
+
+ mutex_lock(&st->buf_lock);
+
+ st->tx[0] = ADE7854_READ_REG;
+ st->tx[1] = (reg_address >> 8) & 0xFF;
+ st->tx[2] = reg_address & 0xFF;
+
+ ret = spi_sync_transfer(st->spi, xfers, ARRAY_SIZE(xfers));
+ if (ret) {
+ dev_err(&st->spi->dev, "problem when reading 24 bit register 0x%02X",
+ reg_address);
+ goto error_ret;
+ }
+ *val = (st->rx[0] << 16) | (st->rx[1] << 8) | st->rx[2];
+
+error_ret:
+ mutex_unlock(&st->buf_lock);
+ return ret;
+}
+
+static int ade7854_spi_read_reg_32(struct device *dev,
+ u16 reg_address,
+ u32 *val)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct ade7854_state *st = iio_priv(indio_dev);
+ int ret;
+ struct spi_transfer xfers[] = {
+ {
+ .tx_buf = st->tx,
+ .bits_per_word = 8,
+ .len = 3,
+ }, {
+ .rx_buf = st->rx,
+ .bits_per_word = 8,
+ .len = 4,
+ }
+ };
+
+ mutex_lock(&st->buf_lock);
+
+ st->tx[0] = ADE7854_READ_REG;
+ st->tx[1] = (reg_address >> 8) & 0xFF;
+ st->tx[2] = reg_address & 0xFF;
+
+ ret = spi_sync_transfer(st->spi, xfers, ARRAY_SIZE(xfers));
+ if (ret) {
+ dev_err(&st->spi->dev, "problem when reading 32 bit register 0x%02X",
+ reg_address);
+ goto error_ret;
+ }
+ *val = be32_to_cpup((const __be32 *)st->rx);
+
+error_ret:
+ mutex_unlock(&st->buf_lock);
+ return ret;
+}
+
+static int ade7854_spi_probe(struct spi_device *spi)
+{
+ struct ade7854_state *st;
+ struct iio_dev *indio_dev;
+
+ indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));
+ if (indio_dev == NULL)
+ return -ENOMEM;
+ st = iio_priv(indio_dev);
+ spi_set_drvdata(spi, indio_dev);
+ st->read_reg_8 = ade7854_spi_read_reg_8;
+ st->read_reg_16 = ade7854_spi_read_reg_16;
+ st->read_reg_24 = ade7854_spi_read_reg_24;
+ st->read_reg_32 = ade7854_spi_read_reg_32;
+ st->write_reg_8 = ade7854_spi_write_reg_8;
+ st->write_reg_16 = ade7854_spi_write_reg_16;
+ st->write_reg_24 = ade7854_spi_write_reg_24;
+ st->write_reg_32 = ade7854_spi_write_reg_32;
+ st->irq = spi->irq;
+ st->spi = spi;
+
+ return ade7854_probe(indio_dev, &spi->dev);
+}
+
+static int ade7854_spi_remove(struct spi_device *spi)
+{
+ ade7854_remove(spi_get_drvdata(spi));
+
+ return 0;
+}
+static const struct spi_device_id ade7854_id[] = {
+ { "ade7854", 0 },
+ { "ade7858", 0 },
+ { "ade7868", 0 },
+ { "ade7878", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(spi, ade7854_id);
+
+static struct spi_driver ade7854_driver = {
+ .driver = {
+ .name = "ade7854",
+ .owner = THIS_MODULE,
+ },
+ .probe = ade7854_spi_probe,
+ .remove = ade7854_spi_remove,
+ .id_table = ade7854_id,
+};
+module_spi_driver(ade7854_driver);
+
+MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>");
+MODULE_DESCRIPTION("Analog Devices ADE7854/58/68/78 SPI Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/staging/iio/meter/ade7854.c b/drivers/staging/iio/meter/ade7854.c
new file mode 100644
index 000000000..d620bbd60
--- /dev/null
+++ b/drivers/staging/iio/meter/ade7854.c
@@ -0,0 +1,578 @@
+/*
+ * ADE7854/58/68/78 Polyphase Multifunction Energy Metering IC Driver
+ *
+ * Copyright 2010 Analog Devices Inc.
+ *
+ * Licensed under the GPL-2 or later.
+ */
+
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/delay.h>
+#include <linux/mutex.h>
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/sysfs.h>
+#include <linux/list.h>
+#include <linux/module.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include "meter.h"
+#include "ade7854.h"
+
+static ssize_t ade7854_read_8bit(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ int ret;
+ u8 val = 0;
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct ade7854_state *st = iio_priv(indio_dev);
+ struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
+
+ ret = st->read_reg_8(dev, this_attr->address, &val);
+ if (ret)
+ return ret;
+
+ return sprintf(buf, "%u\n", val);
+}
+
+static ssize_t ade7854_read_16bit(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ int ret;
+ u16 val = 0;
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct ade7854_state *st = iio_priv(indio_dev);
+ struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
+
+ ret = st->read_reg_16(dev, this_attr->address, &val);
+ if (ret)
+ return ret;
+
+ return sprintf(buf, "%u\n", val);
+}
+
+static ssize_t ade7854_read_24bit(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ int ret;
+ u32 val;
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct ade7854_state *st = iio_priv(indio_dev);
+ struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
+
+ ret = st->read_reg_24(dev, this_attr->address, &val);
+ if (ret)
+ return ret;
+
+ return sprintf(buf, "%u\n", val);
+}
+
+static ssize_t ade7854_read_32bit(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ int ret;
+ u32 val = 0;
+ struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct ade7854_state *st = iio_priv(indio_dev);
+
+ ret = st->read_reg_32(dev, this_attr->address, &val);
+ if (ret)
+ return ret;
+
+ return sprintf(buf, "%u\n", val);
+}
+
+static ssize_t ade7854_write_8bit(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t len)
+{
+ struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct ade7854_state *st = iio_priv(indio_dev);
+
+ int ret;
+ u8 val;
+
+ ret = kstrtou8(buf, 10, &val);
+ if (ret)
+ goto error_ret;
+ ret = st->write_reg_8(dev, this_attr->address, val);
+
+error_ret:
+ return ret ? ret : len;
+}
+
+static ssize_t ade7854_write_16bit(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t len)
+{
+ struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct ade7854_state *st = iio_priv(indio_dev);
+
+ int ret;
+ u16 val;
+
+ ret = kstrtou16(buf, 10, &val);
+ if (ret)
+ goto error_ret;
+ ret = st->write_reg_16(dev, this_attr->address, val);
+
+error_ret:
+ return ret ? ret : len;
+}
+
+static ssize_t ade7854_write_24bit(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t len)
+{
+ struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct ade7854_state *st = iio_priv(indio_dev);
+
+ int ret;
+ u32 val;
+
+ ret = kstrtou32(buf, 10, &val);
+ if (ret)
+ goto error_ret;
+ ret = st->write_reg_24(dev, this_attr->address, val);
+
+error_ret:
+ return ret ? ret : len;
+}
+
+static ssize_t ade7854_write_32bit(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t len)
+{
+ struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct ade7854_state *st = iio_priv(indio_dev);
+
+ int ret;
+ u32 val;
+
+ ret = kstrtou32(buf, 10, &val);
+ if (ret)
+ goto error_ret;
+ ret = st->write_reg_32(dev, this_attr->address, val);
+
+error_ret:
+ return ret ? ret : len;
+}
+
+static int ade7854_reset(struct device *dev)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct ade7854_state *st = iio_priv(indio_dev);
+ u16 val;
+
+ st->read_reg_16(dev, ADE7854_CONFIG, &val);
+ val |= 1 << 7; /* Software Chip Reset */
+
+ return st->write_reg_16(dev, ADE7854_CONFIG, val);
+}
+
+static IIO_DEV_ATTR_AIGAIN(S_IWUSR | S_IRUGO,
+ ade7854_read_24bit,
+ ade7854_write_24bit,
+ ADE7854_AIGAIN);
+static IIO_DEV_ATTR_BIGAIN(S_IWUSR | S_IRUGO,
+ ade7854_read_24bit,
+ ade7854_write_24bit,
+ ADE7854_BIGAIN);
+static IIO_DEV_ATTR_CIGAIN(S_IWUSR | S_IRUGO,
+ ade7854_read_24bit,
+ ade7854_write_24bit,
+ ADE7854_CIGAIN);
+static IIO_DEV_ATTR_NIGAIN(S_IWUSR | S_IRUGO,
+ ade7854_read_24bit,
+ ade7854_write_24bit,
+ ADE7854_NIGAIN);
+static IIO_DEV_ATTR_AVGAIN(S_IWUSR | S_IRUGO,
+ ade7854_read_24bit,
+ ade7854_write_24bit,
+ ADE7854_AVGAIN);
+static IIO_DEV_ATTR_BVGAIN(S_IWUSR | S_IRUGO,
+ ade7854_read_24bit,
+ ade7854_write_24bit,
+ ADE7854_BVGAIN);
+static IIO_DEV_ATTR_CVGAIN(S_IWUSR | S_IRUGO,
+ ade7854_read_24bit,
+ ade7854_write_24bit,
+ ADE7854_CVGAIN);
+static IIO_DEV_ATTR_APPARENT_POWER_A_GAIN(S_IWUSR | S_IRUGO,
+ ade7854_read_24bit,
+ ade7854_write_24bit,
+ ADE7854_AVAGAIN);
+static IIO_DEV_ATTR_APPARENT_POWER_B_GAIN(S_IWUSR | S_IRUGO,
+ ade7854_read_24bit,
+ ade7854_write_24bit,
+ ADE7854_BVAGAIN);
+static IIO_DEV_ATTR_APPARENT_POWER_C_GAIN(S_IWUSR | S_IRUGO,
+ ade7854_read_24bit,
+ ade7854_write_24bit,
+ ADE7854_CVAGAIN);
+static IIO_DEV_ATTR_ACTIVE_POWER_A_OFFSET(S_IWUSR | S_IRUGO,
+ ade7854_read_24bit,
+ ade7854_write_24bit,
+ ADE7854_AWATTOS);
+static IIO_DEV_ATTR_ACTIVE_POWER_B_OFFSET(S_IWUSR | S_IRUGO,
+ ade7854_read_24bit,
+ ade7854_write_24bit,
+ ADE7854_BWATTOS);
+static IIO_DEV_ATTR_ACTIVE_POWER_C_OFFSET(S_IWUSR | S_IRUGO,
+ ade7854_read_24bit,
+ ade7854_write_24bit,
+ ADE7854_CWATTOS);
+static IIO_DEV_ATTR_REACTIVE_POWER_A_GAIN(S_IWUSR | S_IRUGO,
+ ade7854_read_24bit,
+ ade7854_write_24bit,
+ ADE7854_AVARGAIN);
+static IIO_DEV_ATTR_REACTIVE_POWER_B_GAIN(S_IWUSR | S_IRUGO,
+ ade7854_read_24bit,
+ ade7854_write_24bit,
+ ADE7854_BVARGAIN);
+static IIO_DEV_ATTR_REACTIVE_POWER_C_GAIN(S_IWUSR | S_IRUGO,
+ ade7854_read_24bit,
+ ade7854_write_24bit,
+ ADE7854_CVARGAIN);
+static IIO_DEV_ATTR_REACTIVE_POWER_A_OFFSET(S_IWUSR | S_IRUGO,
+ ade7854_read_24bit,
+ ade7854_write_24bit,
+ ADE7854_AVAROS);
+static IIO_DEV_ATTR_REACTIVE_POWER_B_OFFSET(S_IWUSR | S_IRUGO,
+ ade7854_read_24bit,
+ ade7854_write_24bit,
+ ADE7854_BVAROS);
+static IIO_DEV_ATTR_REACTIVE_POWER_C_OFFSET(S_IWUSR | S_IRUGO,
+ ade7854_read_24bit,
+ ade7854_write_24bit,
+ ADE7854_CVAROS);
+static IIO_DEV_ATTR_VPEAK(S_IWUSR | S_IRUGO,
+ ade7854_read_32bit,
+ ade7854_write_32bit,
+ ADE7854_VPEAK);
+static IIO_DEV_ATTR_IPEAK(S_IWUSR | S_IRUGO,
+ ade7854_read_32bit,
+ ade7854_write_32bit,
+ ADE7854_VPEAK);
+static IIO_DEV_ATTR_APHCAL(S_IWUSR | S_IRUGO,
+ ade7854_read_16bit,
+ ade7854_write_16bit,
+ ADE7854_APHCAL);
+static IIO_DEV_ATTR_BPHCAL(S_IWUSR | S_IRUGO,
+ ade7854_read_16bit,
+ ade7854_write_16bit,
+ ADE7854_BPHCAL);
+static IIO_DEV_ATTR_CPHCAL(S_IWUSR | S_IRUGO,
+ ade7854_read_16bit,
+ ade7854_write_16bit,
+ ADE7854_CPHCAL);
+static IIO_DEV_ATTR_CF1DEN(S_IWUSR | S_IRUGO,
+ ade7854_read_16bit,
+ ade7854_write_16bit,
+ ADE7854_CF1DEN);
+static IIO_DEV_ATTR_CF2DEN(S_IWUSR | S_IRUGO,
+ ade7854_read_16bit,
+ ade7854_write_16bit,
+ ADE7854_CF2DEN);
+static IIO_DEV_ATTR_CF3DEN(S_IWUSR | S_IRUGO,
+ ade7854_read_16bit,
+ ade7854_write_16bit,
+ ADE7854_CF3DEN);
+static IIO_DEV_ATTR_LINECYC(S_IWUSR | S_IRUGO,
+ ade7854_read_16bit,
+ ade7854_write_16bit,
+ ADE7854_LINECYC);
+static IIO_DEV_ATTR_SAGCYC(S_IWUSR | S_IRUGO,
+ ade7854_read_8bit,
+ ade7854_write_8bit,
+ ADE7854_SAGCYC);
+static IIO_DEV_ATTR_CFCYC(S_IWUSR | S_IRUGO,
+ ade7854_read_8bit,
+ ade7854_write_8bit,
+ ADE7854_CFCYC);
+static IIO_DEV_ATTR_PEAKCYC(S_IWUSR | S_IRUGO,
+ ade7854_read_8bit,
+ ade7854_write_8bit,
+ ADE7854_PEAKCYC);
+static IIO_DEV_ATTR_CHKSUM(ade7854_read_24bit,
+ ADE7854_CHECKSUM);
+static IIO_DEV_ATTR_ANGLE0(ade7854_read_24bit,
+ ADE7854_ANGLE0);
+static IIO_DEV_ATTR_ANGLE1(ade7854_read_24bit,
+ ADE7854_ANGLE1);
+static IIO_DEV_ATTR_ANGLE2(ade7854_read_24bit,
+ ADE7854_ANGLE2);
+static IIO_DEV_ATTR_AIRMS(S_IRUGO,
+ ade7854_read_24bit,
+ NULL,
+ ADE7854_AIRMS);
+static IIO_DEV_ATTR_BIRMS(S_IRUGO,
+ ade7854_read_24bit,
+ NULL,
+ ADE7854_BIRMS);
+static IIO_DEV_ATTR_CIRMS(S_IRUGO,
+ ade7854_read_24bit,
+ NULL,
+ ADE7854_CIRMS);
+static IIO_DEV_ATTR_NIRMS(S_IRUGO,
+ ade7854_read_24bit,
+ NULL,
+ ADE7854_NIRMS);
+static IIO_DEV_ATTR_AVRMS(S_IRUGO,
+ ade7854_read_24bit,
+ NULL,
+ ADE7854_AVRMS);
+static IIO_DEV_ATTR_BVRMS(S_IRUGO,
+ ade7854_read_24bit,
+ NULL,
+ ADE7854_BVRMS);
+static IIO_DEV_ATTR_CVRMS(S_IRUGO,
+ ade7854_read_24bit,
+ NULL,
+ ADE7854_CVRMS);
+static IIO_DEV_ATTR_AIRMSOS(S_IRUGO,
+ ade7854_read_16bit,
+ ade7854_write_16bit,
+ ADE7854_AIRMSOS);
+static IIO_DEV_ATTR_BIRMSOS(S_IRUGO,
+ ade7854_read_16bit,
+ ade7854_write_16bit,
+ ADE7854_BIRMSOS);
+static IIO_DEV_ATTR_CIRMSOS(S_IRUGO,
+ ade7854_read_16bit,
+ ade7854_write_16bit,
+ ADE7854_CIRMSOS);
+static IIO_DEV_ATTR_AVRMSOS(S_IRUGO,
+ ade7854_read_16bit,
+ ade7854_write_16bit,
+ ADE7854_AVRMSOS);
+static IIO_DEV_ATTR_BVRMSOS(S_IRUGO,
+ ade7854_read_16bit,
+ ade7854_write_16bit,
+ ADE7854_BVRMSOS);
+static IIO_DEV_ATTR_CVRMSOS(S_IRUGO,
+ ade7854_read_16bit,
+ ade7854_write_16bit,
+ ADE7854_CVRMSOS);
+static IIO_DEV_ATTR_VOLT_A(ade7854_read_24bit,
+ ADE7854_VAWV);
+static IIO_DEV_ATTR_VOLT_B(ade7854_read_24bit,
+ ADE7854_VBWV);
+static IIO_DEV_ATTR_VOLT_C(ade7854_read_24bit,
+ ADE7854_VCWV);
+static IIO_DEV_ATTR_CURRENT_A(ade7854_read_24bit,
+ ADE7854_IAWV);
+static IIO_DEV_ATTR_CURRENT_B(ade7854_read_24bit,
+ ADE7854_IBWV);
+static IIO_DEV_ATTR_CURRENT_C(ade7854_read_24bit,
+ ADE7854_ICWV);
+static IIO_DEV_ATTR_AWATTHR(ade7854_read_32bit,
+ ADE7854_AWATTHR);
+static IIO_DEV_ATTR_BWATTHR(ade7854_read_32bit,
+ ADE7854_BWATTHR);
+static IIO_DEV_ATTR_CWATTHR(ade7854_read_32bit,
+ ADE7854_CWATTHR);
+static IIO_DEV_ATTR_AFWATTHR(ade7854_read_32bit,
+ ADE7854_AFWATTHR);
+static IIO_DEV_ATTR_BFWATTHR(ade7854_read_32bit,
+ ADE7854_BFWATTHR);
+static IIO_DEV_ATTR_CFWATTHR(ade7854_read_32bit,
+ ADE7854_CFWATTHR);
+static IIO_DEV_ATTR_AVARHR(ade7854_read_32bit,
+ ADE7854_AVARHR);
+static IIO_DEV_ATTR_BVARHR(ade7854_read_32bit,
+ ADE7854_BVARHR);
+static IIO_DEV_ATTR_CVARHR(ade7854_read_32bit,
+ ADE7854_CVARHR);
+static IIO_DEV_ATTR_AVAHR(ade7854_read_32bit,
+ ADE7854_AVAHR);
+static IIO_DEV_ATTR_BVAHR(ade7854_read_32bit,
+ ADE7854_BVAHR);
+static IIO_DEV_ATTR_CVAHR(ade7854_read_32bit,
+ ADE7854_CVAHR);
+
+static int ade7854_set_irq(struct device *dev, bool enable)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct ade7854_state *st = iio_priv(indio_dev);
+
+ int ret;
+ u32 irqen;
+
+ ret = st->read_reg_32(dev, ADE7854_MASK0, &irqen);
+ if (ret)
+ goto error_ret;
+
+ if (enable)
+ irqen |= 1 << 17; /* 1: interrupt enabled when all periodical
+ (at 8 kHz rate) DSP computations finish. */
+ else
+ irqen &= ~(1 << 17);
+
+ ret = st->write_reg_32(dev, ADE7854_MASK0, irqen);
+ if (ret)
+ goto error_ret;
+
+error_ret:
+ return ret;
+}
+
+static int ade7854_initial_setup(struct iio_dev *indio_dev)
+{
+ int ret;
+ struct device *dev = &indio_dev->dev;
+
+ /* Disable IRQ */
+ ret = ade7854_set_irq(dev, false);
+ if (ret) {
+ dev_err(dev, "disable irq failed");
+ goto err_ret;
+ }
+
+ ade7854_reset(dev);
+ msleep(ADE7854_STARTUP_DELAY);
+
+err_ret:
+ return ret;
+}
+
+static IIO_CONST_ATTR_SAMP_FREQ_AVAIL("8000");
+
+static IIO_CONST_ATTR(name, "ade7854");
+
+static struct attribute *ade7854_attributes[] = {
+ &iio_dev_attr_aigain.dev_attr.attr,
+ &iio_dev_attr_bigain.dev_attr.attr,
+ &iio_dev_attr_cigain.dev_attr.attr,
+ &iio_dev_attr_nigain.dev_attr.attr,
+ &iio_dev_attr_avgain.dev_attr.attr,
+ &iio_dev_attr_bvgain.dev_attr.attr,
+ &iio_dev_attr_cvgain.dev_attr.attr,
+ &iio_dev_attr_linecyc.dev_attr.attr,
+ &iio_dev_attr_sagcyc.dev_attr.attr,
+ &iio_dev_attr_cfcyc.dev_attr.attr,
+ &iio_dev_attr_peakcyc.dev_attr.attr,
+ &iio_dev_attr_chksum.dev_attr.attr,
+ &iio_dev_attr_apparent_power_a_gain.dev_attr.attr,
+ &iio_dev_attr_apparent_power_b_gain.dev_attr.attr,
+ &iio_dev_attr_apparent_power_c_gain.dev_attr.attr,
+ &iio_dev_attr_active_power_a_offset.dev_attr.attr,
+ &iio_dev_attr_active_power_b_offset.dev_attr.attr,
+ &iio_dev_attr_active_power_c_offset.dev_attr.attr,
+ &iio_dev_attr_reactive_power_a_gain.dev_attr.attr,
+ &iio_dev_attr_reactive_power_b_gain.dev_attr.attr,
+ &iio_dev_attr_reactive_power_c_gain.dev_attr.attr,
+ &iio_dev_attr_reactive_power_a_offset.dev_attr.attr,
+ &iio_dev_attr_reactive_power_b_offset.dev_attr.attr,
+ &iio_dev_attr_reactive_power_c_offset.dev_attr.attr,
+ &iio_dev_attr_awatthr.dev_attr.attr,
+ &iio_dev_attr_bwatthr.dev_attr.attr,
+ &iio_dev_attr_cwatthr.dev_attr.attr,
+ &iio_dev_attr_afwatthr.dev_attr.attr,
+ &iio_dev_attr_bfwatthr.dev_attr.attr,
+ &iio_dev_attr_cfwatthr.dev_attr.attr,
+ &iio_dev_attr_avarhr.dev_attr.attr,
+ &iio_dev_attr_bvarhr.dev_attr.attr,
+ &iio_dev_attr_cvarhr.dev_attr.attr,
+ &iio_dev_attr_angle0.dev_attr.attr,
+ &iio_dev_attr_angle1.dev_attr.attr,
+ &iio_dev_attr_angle2.dev_attr.attr,
+ &iio_dev_attr_avahr.dev_attr.attr,
+ &iio_dev_attr_bvahr.dev_attr.attr,
+ &iio_dev_attr_cvahr.dev_attr.attr,
+ &iio_const_attr_sampling_frequency_available.dev_attr.attr,
+ &iio_const_attr_name.dev_attr.attr,
+ &iio_dev_attr_vpeak.dev_attr.attr,
+ &iio_dev_attr_ipeak.dev_attr.attr,
+ &iio_dev_attr_aphcal.dev_attr.attr,
+ &iio_dev_attr_bphcal.dev_attr.attr,
+ &iio_dev_attr_cphcal.dev_attr.attr,
+ &iio_dev_attr_cf1den.dev_attr.attr,
+ &iio_dev_attr_cf2den.dev_attr.attr,
+ &iio_dev_attr_cf3den.dev_attr.attr,
+ &iio_dev_attr_airms.dev_attr.attr,
+ &iio_dev_attr_birms.dev_attr.attr,
+ &iio_dev_attr_cirms.dev_attr.attr,
+ &iio_dev_attr_nirms.dev_attr.attr,
+ &iio_dev_attr_avrms.dev_attr.attr,
+ &iio_dev_attr_bvrms.dev_attr.attr,
+ &iio_dev_attr_cvrms.dev_attr.attr,
+ &iio_dev_attr_airmsos.dev_attr.attr,
+ &iio_dev_attr_birmsos.dev_attr.attr,
+ &iio_dev_attr_cirmsos.dev_attr.attr,
+ &iio_dev_attr_avrmsos.dev_attr.attr,
+ &iio_dev_attr_bvrmsos.dev_attr.attr,
+ &iio_dev_attr_cvrmsos.dev_attr.attr,
+ &iio_dev_attr_volt_a.dev_attr.attr,
+ &iio_dev_attr_volt_b.dev_attr.attr,
+ &iio_dev_attr_volt_c.dev_attr.attr,
+ &iio_dev_attr_current_a.dev_attr.attr,
+ &iio_dev_attr_current_b.dev_attr.attr,
+ &iio_dev_attr_current_c.dev_attr.attr,
+ NULL,
+};
+
+static const struct attribute_group ade7854_attribute_group = {
+ .attrs = ade7854_attributes,
+};
+
+static const struct iio_info ade7854_info = {
+ .attrs = &ade7854_attribute_group,
+ .driver_module = THIS_MODULE,
+};
+
+int ade7854_probe(struct iio_dev *indio_dev, struct device *dev)
+{
+ int ret;
+ struct ade7854_state *st = iio_priv(indio_dev);
+ /* setup the industrialio driver allocated elements */
+ mutex_init(&st->buf_lock);
+
+ indio_dev->dev.parent = dev;
+ indio_dev->info = &ade7854_info;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+
+ ret = iio_device_register(indio_dev);
+ if (ret)
+ return ret;
+
+ /* Get the device into a sane initial state */
+ ret = ade7854_initial_setup(indio_dev);
+ if (ret)
+ goto error_unreg_dev;
+
+ return 0;
+
+error_unreg_dev:
+ iio_device_unregister(indio_dev);
+ return ret;
+}
+EXPORT_SYMBOL(ade7854_probe);
+
+int ade7854_remove(struct iio_dev *indio_dev)
+{
+ iio_device_unregister(indio_dev);
+
+ return 0;
+}
+EXPORT_SYMBOL(ade7854_remove);
+
+MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>");
+MODULE_DESCRIPTION("Analog Devices ADE7854/58/68/78 Polyphase Energy Meter");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/staging/iio/meter/ade7854.h b/drivers/staging/iio/meter/ade7854.h
new file mode 100644
index 000000000..52ca5412a
--- /dev/null
+++ b/drivers/staging/iio/meter/ade7854.h
@@ -0,0 +1,174 @@
+#ifndef _ADE7854_H
+#define _ADE7854_H
+
+#define ADE7854_AIGAIN 0x4380
+#define ADE7854_AVGAIN 0x4381
+#define ADE7854_BIGAIN 0x4382
+#define ADE7854_BVGAIN 0x4383
+#define ADE7854_CIGAIN 0x4384
+#define ADE7854_CVGAIN 0x4385
+#define ADE7854_NIGAIN 0x4386
+#define ADE7854_AIRMSOS 0x4387
+#define ADE7854_AVRMSOS 0x4388
+#define ADE7854_BIRMSOS 0x4389
+#define ADE7854_BVRMSOS 0x438A
+#define ADE7854_CIRMSOS 0x438B
+#define ADE7854_CVRMSOS 0x438C
+#define ADE7854_NIRMSOS 0x438D
+#define ADE7854_AVAGAIN 0x438E
+#define ADE7854_BVAGAIN 0x438F
+#define ADE7854_CVAGAIN 0x4390
+#define ADE7854_AWGAIN 0x4391
+#define ADE7854_AWATTOS 0x4392
+#define ADE7854_BWGAIN 0x4393
+#define ADE7854_BWATTOS 0x4394
+#define ADE7854_CWGAIN 0x4395
+#define ADE7854_CWATTOS 0x4396
+#define ADE7854_AVARGAIN 0x4397
+#define ADE7854_AVAROS 0x4398
+#define ADE7854_BVARGAIN 0x4399
+#define ADE7854_BVAROS 0x439A
+#define ADE7854_CVARGAIN 0x439B
+#define ADE7854_CVAROS 0x439C
+#define ADE7854_AFWGAIN 0x439D
+#define ADE7854_AFWATTOS 0x439E
+#define ADE7854_BFWGAIN 0x439F
+#define ADE7854_BFWATTOS 0x43A0
+#define ADE7854_CFWGAIN 0x43A1
+#define ADE7854_CFWATTOS 0x43A2
+#define ADE7854_AFVARGAIN 0x43A3
+#define ADE7854_AFVAROS 0x43A4
+#define ADE7854_BFVARGAIN 0x43A5
+#define ADE7854_BFVAROS 0x43A6
+#define ADE7854_CFVARGAIN 0x43A7
+#define ADE7854_CFVAROS 0x43A8
+#define ADE7854_VATHR1 0x43A9
+#define ADE7854_VATHR0 0x43AA
+#define ADE7854_WTHR1 0x43AB
+#define ADE7854_WTHR0 0x43AC
+#define ADE7854_VARTHR1 0x43AD
+#define ADE7854_VARTHR0 0x43AE
+#define ADE7854_RSV 0x43AF
+#define ADE7854_VANOLOAD 0x43B0
+#define ADE7854_APNOLOAD 0x43B1
+#define ADE7854_VARNOLOAD 0x43B2
+#define ADE7854_VLEVEL 0x43B3
+#define ADE7854_DICOEFF 0x43B5
+#define ADE7854_HPFDIS 0x43B6
+#define ADE7854_ISUMLVL 0x43B8
+#define ADE7854_ISUM 0x43BF
+#define ADE7854_AIRMS 0x43C0
+#define ADE7854_AVRMS 0x43C1
+#define ADE7854_BIRMS 0x43C2
+#define ADE7854_BVRMS 0x43C3
+#define ADE7854_CIRMS 0x43C4
+#define ADE7854_CVRMS 0x43C5
+#define ADE7854_NIRMS 0x43C6
+#define ADE7854_RUN 0xE228
+#define ADE7854_AWATTHR 0xE400
+#define ADE7854_BWATTHR 0xE401
+#define ADE7854_CWATTHR 0xE402
+#define ADE7854_AFWATTHR 0xE403
+#define ADE7854_BFWATTHR 0xE404
+#define ADE7854_CFWATTHR 0xE405
+#define ADE7854_AVARHR 0xE406
+#define ADE7854_BVARHR 0xE407
+#define ADE7854_CVARHR 0xE408
+#define ADE7854_AFVARHR 0xE409
+#define ADE7854_BFVARHR 0xE40A
+#define ADE7854_CFVARHR 0xE40B
+#define ADE7854_AVAHR 0xE40C
+#define ADE7854_BVAHR 0xE40D
+#define ADE7854_CVAHR 0xE40E
+#define ADE7854_IPEAK 0xE500
+#define ADE7854_VPEAK 0xE501
+#define ADE7854_STATUS0 0xE502
+#define ADE7854_STATUS1 0xE503
+#define ADE7854_OILVL 0xE507
+#define ADE7854_OVLVL 0xE508
+#define ADE7854_SAGLVL 0xE509
+#define ADE7854_MASK0 0xE50A
+#define ADE7854_MASK1 0xE50B
+#define ADE7854_IAWV 0xE50C
+#define ADE7854_IBWV 0xE50D
+#define ADE7854_ICWV 0xE50E
+#define ADE7854_VAWV 0xE510
+#define ADE7854_VBWV 0xE511
+#define ADE7854_VCWV 0xE512
+#define ADE7854_AWATT 0xE513
+#define ADE7854_BWATT 0xE514
+#define ADE7854_CWATT 0xE515
+#define ADE7854_AVA 0xE519
+#define ADE7854_BVA 0xE51A
+#define ADE7854_CVA 0xE51B
+#define ADE7854_CHECKSUM 0xE51F
+#define ADE7854_VNOM 0xE520
+#define ADE7854_PHSTATUS 0xE600
+#define ADE7854_ANGLE0 0xE601
+#define ADE7854_ANGLE1 0xE602
+#define ADE7854_ANGLE2 0xE603
+#define ADE7854_PERIOD 0xE607
+#define ADE7854_PHNOLOAD 0xE608
+#define ADE7854_LINECYC 0xE60C
+#define ADE7854_ZXTOUT 0xE60D
+#define ADE7854_COMPMODE 0xE60E
+#define ADE7854_GAIN 0xE60F
+#define ADE7854_CFMODE 0xE610
+#define ADE7854_CF1DEN 0xE611
+#define ADE7854_CF2DEN 0xE612
+#define ADE7854_CF3DEN 0xE613
+#define ADE7854_APHCAL 0xE614
+#define ADE7854_BPHCAL 0xE615
+#define ADE7854_CPHCAL 0xE616
+#define ADE7854_PHSIGN 0xE617
+#define ADE7854_CONFIG 0xE618
+#define ADE7854_MMODE 0xE700
+#define ADE7854_ACCMODE 0xE701
+#define ADE7854_LCYCMODE 0xE702
+#define ADE7854_PEAKCYC 0xE703
+#define ADE7854_SAGCYC 0xE704
+#define ADE7854_CFCYC 0xE705
+#define ADE7854_HSDC_CFG 0xE706
+#define ADE7854_CONFIG2 0xEC01
+
+#define ADE7854_READ_REG 0x1
+#define ADE7854_WRITE_REG 0x0
+
+#define ADE7854_MAX_TX 7
+#define ADE7854_MAX_RX 7
+#define ADE7854_STARTUP_DELAY 1
+
+#define ADE7854_SPI_SLOW (u32)(300 * 1000)
+#define ADE7854_SPI_BURST (u32)(1000 * 1000)
+#define ADE7854_SPI_FAST (u32)(2000 * 1000)
+
+/**
+ * struct ade7854_state - device instance specific data
+ * @spi: actual spi_device
+ * @indio_dev: industrial I/O device structure
+ * @buf_lock: mutex to protect tx and rx
+ * @tx: transmit buffer
+ * @rx: receive buffer
+ **/
+struct ade7854_state {
+ struct spi_device *spi;
+ struct i2c_client *i2c;
+ int (*read_reg_8)(struct device *, u16, u8 *);
+ int (*read_reg_16)(struct device *, u16, u16 *);
+ int (*read_reg_24)(struct device *, u16, u32 *);
+ int (*read_reg_32)(struct device *, u16, u32 *);
+ int (*write_reg_8)(struct device *, u16, u8);
+ int (*write_reg_16)(struct device *, u16, u16);
+ int (*write_reg_24)(struct device *, u16, u32);
+ int (*write_reg_32)(struct device *, u16, u32);
+ int irq;
+ struct mutex buf_lock;
+ u8 tx[ADE7854_MAX_TX] ____cacheline_aligned;
+ u8 rx[ADE7854_MAX_RX];
+
+};
+
+extern int ade7854_probe(struct iio_dev *indio_dev, struct device *dev);
+extern int ade7854_remove(struct iio_dev *indio_dev);
+
+#endif
diff --git a/drivers/staging/iio/meter/meter.h b/drivers/staging/iio/meter/meter.h
new file mode 100644
index 000000000..dfba510f2
--- /dev/null
+++ b/drivers/staging/iio/meter/meter.h
@@ -0,0 +1,400 @@
+#ifndef _METER_H
+#define _METER_H
+
+#include <linux/iio/sysfs.h>
+
+/* metering ic types of attribute */
+
+#define IIO_DEV_ATTR_CURRENT_A_OFFSET(_mode, _show, _store, _addr) \
+ IIO_DEVICE_ATTR(current_a_offset, _mode, _show, _store, _addr)
+
+#define IIO_DEV_ATTR_CURRENT_B_OFFSET(_mode, _show, _store, _addr) \
+ IIO_DEVICE_ATTR(current_b_offset, _mode, _show, _store, _addr)
+
+#define IIO_DEV_ATTR_CURRENT_C_OFFSET(_mode, _show, _store, _addr) \
+ IIO_DEVICE_ATTR(current_c_offset, _mode, _show, _store, _addr)
+
+#define IIO_DEV_ATTR_VOLT_A_OFFSET(_mode, _show, _store, _addr) \
+ IIO_DEVICE_ATTR(volt_a_offset, _mode, _show, _store, _addr)
+
+#define IIO_DEV_ATTR_VOLT_B_OFFSET(_mode, _show, _store, _addr) \
+ IIO_DEVICE_ATTR(volt_b_offset, _mode, _show, _store, _addr)
+
+#define IIO_DEV_ATTR_VOLT_C_OFFSET(_mode, _show, _store, _addr) \
+ IIO_DEVICE_ATTR(volt_c_offset, _mode, _show, _store, _addr)
+
+#define IIO_DEV_ATTR_REACTIVE_POWER_A_OFFSET(_mode, _show, _store, _addr) \
+ IIO_DEVICE_ATTR(reactive_power_a_offset, _mode, _show, _store, _addr)
+
+#define IIO_DEV_ATTR_REACTIVE_POWER_B_OFFSET(_mode, _show, _store, _addr) \
+ IIO_DEVICE_ATTR(reactive_power_b_offset, _mode, _show, _store, _addr)
+
+#define IIO_DEV_ATTR_REACTIVE_POWER_C_OFFSET(_mode, _show, _store, _addr) \
+ IIO_DEVICE_ATTR(reactive_power_c_offset, _mode, _show, _store, _addr)
+
+#define IIO_DEV_ATTR_ACTIVE_POWER_A_OFFSET(_mode, _show, _store, _addr) \
+ IIO_DEVICE_ATTR(active_power_a_offset, _mode, _show, _store, _addr)
+
+#define IIO_DEV_ATTR_ACTIVE_POWER_B_OFFSET(_mode, _show, _store, _addr) \
+ IIO_DEVICE_ATTR(active_power_b_offset, _mode, _show, _store, _addr)
+
+#define IIO_DEV_ATTR_ACTIVE_POWER_C_OFFSET(_mode, _show, _store, _addr) \
+ IIO_DEVICE_ATTR(active_power_c_offset, _mode, _show, _store, _addr)
+
+#define IIO_DEV_ATTR_CURRENT_A_GAIN(_mode, _show, _store, _addr) \
+ IIO_DEVICE_ATTR(current_a_gain, _mode, _show, _store, _addr)
+
+#define IIO_DEV_ATTR_CURRENT_B_GAIN(_mode, _show, _store, _addr) \
+ IIO_DEVICE_ATTR(current_b_gain, _mode, _show, _store, _addr)
+
+#define IIO_DEV_ATTR_CURRENT_C_GAIN(_mode, _show, _store, _addr) \
+ IIO_DEVICE_ATTR(current_c_gain, _mode, _show, _store, _addr)
+
+#define IIO_DEV_ATTR_APPARENT_POWER_A_GAIN(_mode, _show, _store, _addr) \
+ IIO_DEVICE_ATTR(apparent_power_a_gain, _mode, _show, _store, _addr)
+
+#define IIO_DEV_ATTR_APPARENT_POWER_B_GAIN(_mode, _show, _store, _addr) \
+ IIO_DEVICE_ATTR(apparent_power_b_gain, _mode, _show, _store, _addr)
+
+#define IIO_DEV_ATTR_APPARENT_POWER_C_GAIN(_mode, _show, _store, _addr) \
+ IIO_DEVICE_ATTR(apparent_power_c_gain, _mode, _show, _store, _addr)
+
+#define IIO_DEV_ATTR_ACTIVE_POWER_GAIN(_mode, _show, _store, _addr) \
+ IIO_DEVICE_ATTR(active_power_gain, _mode, _show, _store, _addr)
+
+#define IIO_DEV_ATTR_ACTIVE_POWER_A_GAIN(_mode, _show, _store, _addr) \
+ IIO_DEVICE_ATTR(active_power_a_gain, _mode, _show, _store, _addr)
+
+#define IIO_DEV_ATTR_ACTIVE_POWER_B_GAIN(_mode, _show, _store, _addr) \
+ IIO_DEVICE_ATTR(active_power_b_gain, _mode, _show, _store, _addr)
+
+#define IIO_DEV_ATTR_ACTIVE_POWER_C_GAIN(_mode, _show, _store, _addr) \
+ IIO_DEVICE_ATTR(active_power_c_gain, _mode, _show, _store, _addr)
+
+#define IIO_DEV_ATTR_REACTIVE_POWER_A_GAIN(_mode, _show, _store, _addr) \
+ IIO_DEVICE_ATTR(reactive_power_a_gain, _mode, _show, _store, _addr)
+
+#define IIO_DEV_ATTR_REACTIVE_POWER_B_GAIN(_mode, _show, _store, _addr) \
+ IIO_DEVICE_ATTR(reactive_power_b_gain, _mode, _show, _store, _addr)
+
+#define IIO_DEV_ATTR_REACTIVE_POWER_C_GAIN(_mode, _show, _store, _addr) \
+ IIO_DEVICE_ATTR(reactive_power_c_gain, _mode, _show, _store, _addr)
+
+#define IIO_DEV_ATTR_CURRENT_A(_show, _addr) \
+ IIO_DEVICE_ATTR(current_a, S_IRUGO, _show, NULL, _addr)
+
+#define IIO_DEV_ATTR_CURRENT_B(_show, _addr) \
+ IIO_DEVICE_ATTR(current_b, S_IRUGO, _show, NULL, _addr)
+
+#define IIO_DEV_ATTR_CURRENT_C(_show, _addr) \
+ IIO_DEVICE_ATTR(current_c, S_IRUGO, _show, NULL, _addr)
+
+#define IIO_DEV_ATTR_VOLT_A(_show, _addr) \
+ IIO_DEVICE_ATTR(volt_a, S_IRUGO, _show, NULL, _addr)
+
+#define IIO_DEV_ATTR_VOLT_B(_show, _addr) \
+ IIO_DEVICE_ATTR(volt_b, S_IRUGO, _show, NULL, _addr)
+
+#define IIO_DEV_ATTR_VOLT_C(_show, _addr) \
+ IIO_DEVICE_ATTR(volt_c, S_IRUGO, _show, NULL, _addr)
+
+#define IIO_DEV_ATTR_AENERGY(_show, _addr) \
+ IIO_DEVICE_ATTR(aenergy, S_IRUGO, _show, NULL, _addr)
+
+#define IIO_DEV_ATTR_LENERGY(_show, _addr) \
+ IIO_DEVICE_ATTR(lenergy, S_IRUGO, _show, NULL, _addr)
+
+#define IIO_DEV_ATTR_RAENERGY(_show, _addr) \
+ IIO_DEVICE_ATTR(raenergy, S_IRUGO, _show, NULL, _addr)
+
+#define IIO_DEV_ATTR_LAENERGY(_show, _addr) \
+ IIO_DEVICE_ATTR(laenergy, S_IRUGO, _show, NULL, _addr)
+
+#define IIO_DEV_ATTR_VAENERGY(_show, _addr) \
+ IIO_DEVICE_ATTR(vaenergy, S_IRUGO, _show, NULL, _addr)
+
+#define IIO_DEV_ATTR_LVAENERGY(_show, _addr) \
+ IIO_DEVICE_ATTR(lvaenergy, S_IRUGO, _show, NULL, _addr)
+
+#define IIO_DEV_ATTR_RVAENERGY(_show, _addr) \
+ IIO_DEVICE_ATTR(rvaenergy, S_IRUGO, _show, NULL, _addr)
+
+#define IIO_DEV_ATTR_LVARENERGY(_show, _addr) \
+ IIO_DEVICE_ATTR(lvarenergy, S_IRUGO, _show, NULL, _addr)
+
+#define IIO_DEV_ATTR_CHKSUM(_show, _addr) \
+ IIO_DEVICE_ATTR(chksum, S_IRUGO, _show, NULL, _addr)
+
+#define IIO_DEV_ATTR_ANGLE0(_show, _addr) \
+ IIO_DEVICE_ATTR(angle0, S_IRUGO, _show, NULL, _addr)
+
+#define IIO_DEV_ATTR_ANGLE1(_show, _addr) \
+ IIO_DEVICE_ATTR(angle1, S_IRUGO, _show, NULL, _addr)
+
+#define IIO_DEV_ATTR_ANGLE2(_show, _addr) \
+ IIO_DEVICE_ATTR(angle2, S_IRUGO, _show, NULL, _addr)
+
+#define IIO_DEV_ATTR_AWATTHR(_show, _addr) \
+ IIO_DEVICE_ATTR(awatthr, S_IRUGO, _show, NULL, _addr)
+
+#define IIO_DEV_ATTR_BWATTHR(_show, _addr) \
+ IIO_DEVICE_ATTR(bwatthr, S_IRUGO, _show, NULL, _addr)
+
+#define IIO_DEV_ATTR_CWATTHR(_show, _addr) \
+ IIO_DEVICE_ATTR(cwatthr, S_IRUGO, _show, NULL, _addr)
+
+#define IIO_DEV_ATTR_AFWATTHR(_show, _addr) \
+ IIO_DEVICE_ATTR(afwatthr, S_IRUGO, _show, NULL, _addr)
+
+#define IIO_DEV_ATTR_BFWATTHR(_show, _addr) \
+ IIO_DEVICE_ATTR(bfwatthr, S_IRUGO, _show, NULL, _addr)
+
+#define IIO_DEV_ATTR_CFWATTHR(_show, _addr) \
+ IIO_DEVICE_ATTR(cfwatthr, S_IRUGO, _show, NULL, _addr)
+
+#define IIO_DEV_ATTR_AVARHR(_show, _addr) \
+ IIO_DEVICE_ATTR(avarhr, S_IRUGO, _show, NULL, _addr)
+
+#define IIO_DEV_ATTR_BVARHR(_show, _addr) \
+ IIO_DEVICE_ATTR(bvarhr, S_IRUGO, _show, NULL, _addr)
+
+#define IIO_DEV_ATTR_CVARHR(_show, _addr) \
+ IIO_DEVICE_ATTR(cvarhr, S_IRUGO, _show, NULL, _addr)
+
+#define IIO_DEV_ATTR_AVAHR(_show, _addr) \
+ IIO_DEVICE_ATTR(avahr, S_IRUGO, _show, NULL, _addr)
+
+#define IIO_DEV_ATTR_BVAHR(_show, _addr) \
+ IIO_DEVICE_ATTR(bvahr, S_IRUGO, _show, NULL, _addr)
+
+#define IIO_DEV_ATTR_CVAHR(_show, _addr) \
+ IIO_DEVICE_ATTR(cvahr, S_IRUGO, _show, NULL, _addr)
+
+#define IIO_DEV_ATTR_IOS(_mode, _show, _store, _addr) \
+ IIO_DEVICE_ATTR(ios, _mode, _show, _store, _addr)
+
+#define IIO_DEV_ATTR_VOS(_mode, _show, _store, _addr) \
+ IIO_DEVICE_ATTR(vos, _mode, _show, _store, _addr)
+
+#define IIO_DEV_ATTR_PHCAL(_mode, _show, _store, _addr) \
+ IIO_DEVICE_ATTR(phcal, _mode, _show, _store, _addr)
+
+#define IIO_DEV_ATTR_APHCAL(_mode, _show, _store, _addr) \
+ IIO_DEVICE_ATTR(aphcal, _mode, _show, _store, _addr)
+
+#define IIO_DEV_ATTR_BPHCAL(_mode, _show, _store, _addr) \
+ IIO_DEVICE_ATTR(bphcal, _mode, _show, _store, _addr)
+
+#define IIO_DEV_ATTR_CPHCAL(_mode, _show, _store, _addr) \
+ IIO_DEVICE_ATTR(cphcal, _mode, _show, _store, _addr)
+
+#define IIO_DEV_ATTR_APOS(_mode, _show, _store, _addr) \
+ IIO_DEVICE_ATTR(apos, _mode, _show, _store, _addr)
+
+#define IIO_DEV_ATTR_AAPOS(_mode, _show, _store, _addr) \
+ IIO_DEVICE_ATTR(aapos, _mode, _show, _store, _addr)
+
+#define IIO_DEV_ATTR_BAPOS(_mode, _show, _store, _addr) \
+ IIO_DEVICE_ATTR(bapos, _mode, _show, _store, _addr)
+
+#define IIO_DEV_ATTR_CAPOS(_mode, _show, _store, _addr) \
+ IIO_DEVICE_ATTR(capos, _mode, _show, _store, _addr)
+
+#define IIO_DEV_ATTR_AVRMSGAIN(_mode, _show, _store, _addr) \
+ IIO_DEVICE_ATTR(avrmsgain, _mode, _show, _store, _addr)
+
+#define IIO_DEV_ATTR_BVRMSGAIN(_mode, _show, _store, _addr) \
+ IIO_DEVICE_ATTR(bvrmsgain, _mode, _show, _store, _addr)
+
+#define IIO_DEV_ATTR_CVRMSGAIN(_mode, _show, _store, _addr) \
+ IIO_DEVICE_ATTR(cvrmsgain, _mode, _show, _store, _addr)
+
+#define IIO_DEV_ATTR_AIGAIN(_mode, _show, _store, _addr) \
+ IIO_DEVICE_ATTR(aigain, _mode, _show, _store, _addr)
+
+#define IIO_DEV_ATTR_BIGAIN(_mode, _show, _store, _addr) \
+ IIO_DEVICE_ATTR(bigain, _mode, _show, _store, _addr)
+
+#define IIO_DEV_ATTR_CIGAIN(_mode, _show, _store, _addr) \
+ IIO_DEVICE_ATTR(cigain, _mode, _show, _store, _addr)
+
+#define IIO_DEV_ATTR_NIGAIN(_mode, _show, _store, _addr) \
+ IIO_DEVICE_ATTR(nigain, _mode, _show, _store, _addr)
+
+#define IIO_DEV_ATTR_AVGAIN(_mode, _show, _store, _addr) \
+ IIO_DEVICE_ATTR(avgain, _mode, _show, _store, _addr)
+
+#define IIO_DEV_ATTR_BVGAIN(_mode, _show, _store, _addr) \
+ IIO_DEVICE_ATTR(bvgain, _mode, _show, _store, _addr)
+
+#define IIO_DEV_ATTR_CVGAIN(_mode, _show, _store, _addr) \
+ IIO_DEVICE_ATTR(cvgain, _mode, _show, _store, _addr)
+
+#define IIO_DEV_ATTR_WGAIN(_mode, _show, _store, _addr) \
+ IIO_DEVICE_ATTR(wgain, _mode, _show, _store, _addr)
+
+#define IIO_DEV_ATTR_WDIV(_mode, _show, _store, _addr) \
+ IIO_DEVICE_ATTR(wdiv, _mode, _show, _store, _addr)
+
+#define IIO_DEV_ATTR_CFNUM(_mode, _show, _store, _addr) \
+ IIO_DEVICE_ATTR(cfnum, _mode, _show, _store, _addr)
+
+#define IIO_DEV_ATTR_CFDEN(_mode, _show, _store, _addr) \
+ IIO_DEVICE_ATTR(cfden, _mode, _show, _store, _addr)
+
+#define IIO_DEV_ATTR_CF1DEN(_mode, _show, _store, _addr) \
+ IIO_DEVICE_ATTR(cf1den, _mode, _show, _store, _addr)
+
+#define IIO_DEV_ATTR_CF2DEN(_mode, _show, _store, _addr) \
+ IIO_DEVICE_ATTR(cf2den, _mode, _show, _store, _addr)
+
+#define IIO_DEV_ATTR_CF3DEN(_mode, _show, _store, _addr) \
+ IIO_DEVICE_ATTR(cf3den, _mode, _show, _store, _addr)
+
+#define IIO_DEV_ATTR_IRMS(_mode, _show, _store, _addr) \
+ IIO_DEVICE_ATTR(irms, _mode, _show, _store, _addr)
+
+#define IIO_DEV_ATTR_VRMS(_mode, _show, _store, _addr) \
+ IIO_DEVICE_ATTR(vrms, _mode, _show, _store, _addr)
+
+#define IIO_DEV_ATTR_AIRMS(_mode, _show, _store, _addr) \
+ IIO_DEVICE_ATTR(airms, _mode, _show, _store, _addr)
+
+#define IIO_DEV_ATTR_BIRMS(_mode, _show, _store, _addr) \
+ IIO_DEVICE_ATTR(birms, _mode, _show, _store, _addr)
+
+#define IIO_DEV_ATTR_CIRMS(_mode, _show, _store, _addr) \
+ IIO_DEVICE_ATTR(cirms, _mode, _show, _store, _addr)
+
+#define IIO_DEV_ATTR_NIRMS(_mode, _show, _store, _addr) \
+ IIO_DEVICE_ATTR(nirms, _mode, _show, _store, _addr)
+
+#define IIO_DEV_ATTR_AVRMS(_mode, _show, _store, _addr) \
+ IIO_DEVICE_ATTR(avrms, _mode, _show, _store, _addr)
+
+#define IIO_DEV_ATTR_BVRMS(_mode, _show, _store, _addr) \
+ IIO_DEVICE_ATTR(bvrms, _mode, _show, _store, _addr)
+
+#define IIO_DEV_ATTR_CVRMS(_mode, _show, _store, _addr) \
+ IIO_DEVICE_ATTR(cvrms, _mode, _show, _store, _addr)
+
+#define IIO_DEV_ATTR_IRMSOS(_mode, _show, _store, _addr) \
+ IIO_DEVICE_ATTR(irmsos, _mode, _show, _store, _addr)
+
+#define IIO_DEV_ATTR_VRMSOS(_mode, _show, _store, _addr) \
+ IIO_DEVICE_ATTR(vrmsos, _mode, _show, _store, _addr)
+
+#define IIO_DEV_ATTR_AIRMSOS(_mode, _show, _store, _addr) \
+ IIO_DEVICE_ATTR(airmsos, _mode, _show, _store, _addr)
+
+#define IIO_DEV_ATTR_BIRMSOS(_mode, _show, _store, _addr) \
+ IIO_DEVICE_ATTR(birmsos, _mode, _show, _store, _addr)
+
+#define IIO_DEV_ATTR_CIRMSOS(_mode, _show, _store, _addr) \
+ IIO_DEVICE_ATTR(cirmsos, _mode, _show, _store, _addr)
+
+#define IIO_DEV_ATTR_AVRMSOS(_mode, _show, _store, _addr) \
+ IIO_DEVICE_ATTR(avrmsos, _mode, _show, _store, _addr)
+
+#define IIO_DEV_ATTR_BVRMSOS(_mode, _show, _store, _addr) \
+ IIO_DEVICE_ATTR(bvrmsos, _mode, _show, _store, _addr)
+
+#define IIO_DEV_ATTR_CVRMSOS(_mode, _show, _store, _addr) \
+ IIO_DEVICE_ATTR(cvrmsos, _mode, _show, _store, _addr)
+
+#define IIO_DEV_ATTR_VAGAIN(_mode, _show, _store, _addr) \
+ IIO_DEVICE_ATTR(vagain, _mode, _show, _store, _addr)
+
+#define IIO_DEV_ATTR_PGA_GAIN(_mode, _show, _store, _addr) \
+ IIO_DEVICE_ATTR(pga_gain, _mode, _show, _store, _addr)
+
+#define IIO_DEV_ATTR_VADIV(_mode, _show, _store, _addr) \
+ IIO_DEVICE_ATTR(vadiv, _mode, _show, _store, _addr)
+
+#define IIO_DEV_ATTR_LINECYC(_mode, _show, _store, _addr) \
+ IIO_DEVICE_ATTR(linecyc, _mode, _show, _store, _addr)
+
+#define IIO_DEV_ATTR_SAGCYC(_mode, _show, _store, _addr) \
+ IIO_DEVICE_ATTR(sagcyc, _mode, _show, _store, _addr)
+
+#define IIO_DEV_ATTR_CFCYC(_mode, _show, _store, _addr) \
+ IIO_DEVICE_ATTR(cfcyc, _mode, _show, _store, _addr)
+
+#define IIO_DEV_ATTR_PEAKCYC(_mode, _show, _store, _addr) \
+ IIO_DEVICE_ATTR(peakcyc, _mode, _show, _store, _addr)
+
+#define IIO_DEV_ATTR_SAGLVL(_mode, _show, _store, _addr) \
+ IIO_DEVICE_ATTR(saglvl, _mode, _show, _store, _addr)
+
+#define IIO_DEV_ATTR_IPKLVL(_mode, _show, _store, _addr) \
+ IIO_DEVICE_ATTR(ipklvl, _mode, _show, _store, _addr)
+
+#define IIO_DEV_ATTR_VPKLVL(_mode, _show, _store, _addr) \
+ IIO_DEVICE_ATTR(vpklvl, _mode, _show, _store, _addr)
+
+#define IIO_DEV_ATTR_IPEAK(_mode, _show, _store, _addr) \
+ IIO_DEVICE_ATTR(ipeak, _mode, _show, _store, _addr)
+
+#define IIO_DEV_ATTR_RIPEAK(_mode, _show, _store, _addr) \
+ IIO_DEVICE_ATTR(ripeak, _mode, _show, _store, _addr)
+
+#define IIO_DEV_ATTR_VPEAK(_mode, _show, _store, _addr) \
+ IIO_DEVICE_ATTR(vpeak, _mode, _show, _store, _addr)
+
+#define IIO_DEV_ATTR_RVPEAK(_mode, _show, _store, _addr) \
+ IIO_DEVICE_ATTR(rvpeak, _mode, _show, _store, _addr)
+
+#define IIO_DEV_ATTR_VPERIOD(_mode, _show, _store, _addr) \
+ IIO_DEVICE_ATTR(vperiod, _mode, _show, _store, _addr)
+
+#define IIO_DEV_ATTR_CH_OFF(_num, _mode, _show, _store, _addr) \
+ IIO_DEVICE_ATTR(choff_##_num, _mode, _show, _store, _addr)
+
+/* active energy register, AENERGY, is more than half full */
+#define IIO_EVENT_ATTR_AENERGY_HALF_FULL(_evlist, _show, _store, _mask) \
+ IIO_EVENT_ATTR_SH(aenergy_half_full, _evlist, _show, _store, _mask)
+
+/* a SAG on the line voltage */
+#define IIO_EVENT_ATTR_LINE_VOLT_SAG(_evlist, _show, _store, _mask) \
+ IIO_EVENT_ATTR_SH(line_volt_sag, _evlist, _show, _store, _mask)
+
+/*
+ * Indicates the end of energy accumulation over an integer number
+ * of half line cycles
+ */
+#define IIO_EVENT_ATTR_CYCEND(_evlist, _show, _store, _mask) \
+ IIO_EVENT_ATTR_SH(cycend, _evlist, _show, _store, _mask)
+
+/* on the rising and falling edge of the voltage waveform */
+#define IIO_EVENT_ATTR_ZERO_CROSS(_evlist, _show, _store, _mask) \
+ IIO_EVENT_ATTR_SH(zero_cross, _evlist, _show, _store, _mask)
+
+/* the active energy register has overflowed */
+#define IIO_EVENT_ATTR_AENERGY_OVERFLOW(_evlist, _show, _store, _mask) \
+ IIO_EVENT_ATTR_SH(aenergy_overflow, _evlist, _show, _store, _mask)
+
+/* the apparent energy register has overflowed */
+#define IIO_EVENT_ATTR_VAENERGY_OVERFLOW(_evlist, _show, _store, _mask) \
+ IIO_EVENT_ATTR_SH(vaenergy_overflow, _evlist, _show, _store, _mask)
+
+/* the active energy register, VAENERGY, is more than half full */
+#define IIO_EVENT_ATTR_VAENERGY_HALF_FULL(_evlist, _show, _store, _mask) \
+ IIO_EVENT_ATTR_SH(vaenergy_half_full, _evlist, _show, _store, _mask)
+
+/* the power has gone from negative to positive */
+#define IIO_EVENT_ATTR_PPOS(_evlist, _show, _store, _mask) \
+ IIO_EVENT_ATTR_SH(ppos, _evlist, _show, _store, _mask)
+
+/* the power has gone from positive to negative */
+#define IIO_EVENT_ATTR_PNEG(_evlist, _show, _store, _mask) \
+ IIO_EVENT_ATTR_SH(pneg, _evlist, _show, _store, _mask)
+
+/* waveform sample from Channel 1 has exceeded the IPKLVL value */
+#define IIO_EVENT_ATTR_IPKLVL_EXC(_evlist, _show, _store, _mask) \
+ IIO_EVENT_ATTR_SH(ipklvl_exc, _evlist, _show, _store, _mask)
+
+/* waveform sample from Channel 2 has exceeded the VPKLVL value */
+#define IIO_EVENT_ATTR_VPKLVL_EXC(_evlist, _show, _store, _mask) \
+ IIO_EVENT_ATTR_SH(vpklvl_exc, _evlist, _show, _store, _mask)
+
+#endif /* _METER_H */
diff --git a/drivers/staging/iio/resolver/Kconfig b/drivers/staging/iio/resolver/Kconfig
new file mode 100644
index 000000000..c7a742ec1
--- /dev/null
+++ b/drivers/staging/iio/resolver/Kconfig
@@ -0,0 +1,39 @@
+#
+# Resolver/Synchro drivers
+#
+menu "Resolver to digital converters"
+
+config AD2S90
+ tristate "Analog Devices ad2s90 driver"
+ depends on SPI
+ help
+ Say yes here to build support for Analog Devices spi resolver
+ to digital converters, ad2s90, provides direct access via sysfs.
+
+ To compile this driver as a module, choose M here: the
+ module will be called ad2s90.
+
+config AD2S1200
+ tristate "Analog Devices ad2s1200/ad2s1205 driver"
+ depends on SPI
+ depends on GPIOLIB
+ help
+ Say yes here to build support for Analog Devices spi resolver
+ to digital converters, ad2s1200 and ad2s1205, provides direct access
+ via sysfs.
+
+ To compile this driver as a module, choose M here: the
+ module will be called ad2s1200.
+
+config AD2S1210
+ tristate "Analog Devices ad2s1210 driver"
+ depends on SPI
+ depends on GPIOLIB
+ help
+ Say yes here to build support for Analog Devices spi resolver
+ to digital converters, ad2s1210, provides direct access via sysfs.
+
+ To compile this driver as a module, choose M here: the
+ module will be called ad2s1210.
+
+endmenu
diff --git a/drivers/staging/iio/resolver/Makefile b/drivers/staging/iio/resolver/Makefile
new file mode 100644
index 000000000..14375e444
--- /dev/null
+++ b/drivers/staging/iio/resolver/Makefile
@@ -0,0 +1,7 @@
+#
+# Makefile for Resolver/Synchro drivers
+#
+
+obj-$(CONFIG_AD2S90) += ad2s90.o
+obj-$(CONFIG_AD2S1200) += ad2s1200.o
+obj-$(CONFIG_AD2S1210) += ad2s1210.o
diff --git a/drivers/staging/iio/resolver/ad2s1200.c b/drivers/staging/iio/resolver/ad2s1200.c
new file mode 100644
index 000000000..c17893b49
--- /dev/null
+++ b/drivers/staging/iio/resolver/ad2s1200.c
@@ -0,0 +1,167 @@
+/*
+ * ad2s1200.c simple support for the ADI Resolver to Digital Converters:
+ * AD2S1200/1205
+ *
+ * Copyright (c) 2010-2010 Analog Devices Inc.
+ *
+ * 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/types.h>
+#include <linux/mutex.h>
+#include <linux/device.h>
+#include <linux/spi/spi.h>
+#include <linux/slab.h>
+#include <linux/sysfs.h>
+#include <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/module.h>
+#include <linux/bitops.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+
+#define DRV_NAME "ad2s1200"
+
+/* input pin sample and rdvel is controlled by driver */
+#define AD2S1200_PN 2
+
+/* input clock on serial interface */
+#define AD2S1200_HZ 8192000
+/* clock period in nano second */
+#define AD2S1200_TSCLK (1000000000/AD2S1200_HZ)
+
+struct ad2s1200_state {
+ struct mutex lock;
+ struct spi_device *sdev;
+ int sample;
+ int rdvel;
+ u8 rx[2] ____cacheline_aligned;
+};
+
+static int ad2s1200_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val,
+ int *val2,
+ long m)
+{
+ int ret = 0;
+ s16 vel;
+ struct ad2s1200_state *st = iio_priv(indio_dev);
+
+ mutex_lock(&st->lock);
+ gpio_set_value(st->sample, 0);
+ /* delay (6 * AD2S1200_TSCLK + 20) nano seconds */
+ udelay(1);
+ gpio_set_value(st->sample, 1);
+ gpio_set_value(st->rdvel, !!(chan->type == IIO_ANGL));
+ ret = spi_read(st->sdev, st->rx, 2);
+ if (ret < 0) {
+ mutex_unlock(&st->lock);
+ return ret;
+ }
+
+ switch (chan->type) {
+ case IIO_ANGL:
+ *val = (((u16)(st->rx[0])) << 4) | ((st->rx[1] & 0xF0) >> 4);
+ break;
+ case IIO_ANGL_VEL:
+ vel = (((s16)(st->rx[0])) << 4) | ((st->rx[1] & 0xF0) >> 4);
+ vel = sign_extend32(vel, 11);
+ *val = vel;
+ break;
+ default:
+ mutex_unlock(&st->lock);
+ return -EINVAL;
+ }
+ /* delay (2 * AD2S1200_TSCLK + 20) ns for sample pulse */
+ udelay(1);
+ mutex_unlock(&st->lock);
+ return IIO_VAL_INT;
+}
+
+static const struct iio_chan_spec ad2s1200_channels[] = {
+ {
+ .type = IIO_ANGL,
+ .indexed = 1,
+ .channel = 0,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
+ }, {
+ .type = IIO_ANGL_VEL,
+ .indexed = 1,
+ .channel = 0,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
+ }
+};
+
+static const struct iio_info ad2s1200_info = {
+ .read_raw = &ad2s1200_read_raw,
+ .driver_module = THIS_MODULE,
+};
+
+static int ad2s1200_probe(struct spi_device *spi)
+{
+ struct ad2s1200_state *st;
+ struct iio_dev *indio_dev;
+ int pn, ret = 0;
+ unsigned short *pins = spi->dev.platform_data;
+
+ for (pn = 0; pn < AD2S1200_PN; pn++) {
+ ret = devm_gpio_request_one(&spi->dev, pins[pn], GPIOF_DIR_OUT,
+ DRV_NAME);
+ if (ret) {
+ dev_err(&spi->dev, "request gpio pin %d failed\n",
+ pins[pn]);
+ return ret;
+ }
+ }
+ indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));
+ if (!indio_dev)
+ return -ENOMEM;
+ spi_set_drvdata(spi, indio_dev);
+ st = iio_priv(indio_dev);
+ mutex_init(&st->lock);
+ st->sdev = spi;
+ st->sample = pins[0];
+ st->rdvel = pins[1];
+
+ indio_dev->dev.parent = &spi->dev;
+ indio_dev->info = &ad2s1200_info;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->channels = ad2s1200_channels;
+ indio_dev->num_channels = ARRAY_SIZE(ad2s1200_channels);
+ indio_dev->name = spi_get_device_id(spi)->name;
+
+ ret = devm_iio_device_register(&spi->dev, indio_dev);
+ if (ret)
+ return ret;
+
+ spi->max_speed_hz = AD2S1200_HZ;
+ spi->mode = SPI_MODE_3;
+ spi_setup(spi);
+
+ return 0;
+}
+
+static const struct spi_device_id ad2s1200_id[] = {
+ { "ad2s1200" },
+ { "ad2s1205" },
+ {}
+};
+MODULE_DEVICE_TABLE(spi, ad2s1200_id);
+
+static struct spi_driver ad2s1200_driver = {
+ .driver = {
+ .name = DRV_NAME,
+ .owner = THIS_MODULE,
+ },
+ .probe = ad2s1200_probe,
+ .id_table = ad2s1200_id,
+};
+module_spi_driver(ad2s1200_driver);
+
+MODULE_AUTHOR("Graff Yang <graff.yang@gmail.com>");
+MODULE_DESCRIPTION("Analog Devices AD2S1200/1205 Resolver to Digital SPI driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/staging/iio/resolver/ad2s1210.c b/drivers/staging/iio/resolver/ad2s1210.c
new file mode 100644
index 000000000..7bc3e4a73
--- /dev/null
+++ b/drivers/staging/iio/resolver/ad2s1210.c
@@ -0,0 +1,748 @@
+/*
+ * ad2s1210.c support for the ADI Resolver to Digital Converters: AD2S1210
+ *
+ * Copyright (c) 2010-2010 Analog Devices Inc.
+ *
+ * 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/types.h>
+#include <linux/mutex.h>
+#include <linux/device.h>
+#include <linux/spi/spi.h>
+#include <linux/slab.h>
+#include <linux/sysfs.h>
+#include <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/module.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include "ad2s1210.h"
+
+#define DRV_NAME "ad2s1210"
+
+#define AD2S1210_DEF_CONTROL 0x7E
+
+#define AD2S1210_MSB_IS_HIGH 0x80
+#define AD2S1210_MSB_IS_LOW 0x7F
+#define AD2S1210_PHASE_LOCK_RANGE_44 0x20
+#define AD2S1210_ENABLE_HYSTERESIS 0x10
+#define AD2S1210_SET_ENRES1 0x08
+#define AD2S1210_SET_ENRES0 0x04
+#define AD2S1210_SET_RES1 0x02
+#define AD2S1210_SET_RES0 0x01
+
+#define AD2S1210_SET_ENRESOLUTION (AD2S1210_SET_ENRES1 | \
+ AD2S1210_SET_ENRES0)
+#define AD2S1210_SET_RESOLUTION (AD2S1210_SET_RES1 | AD2S1210_SET_RES0)
+
+#define AD2S1210_REG_POSITION 0x80
+#define AD2S1210_REG_VELOCITY 0x82
+#define AD2S1210_REG_LOS_THRD 0x88
+#define AD2S1210_REG_DOS_OVR_THRD 0x89
+#define AD2S1210_REG_DOS_MIS_THRD 0x8A
+#define AD2S1210_REG_DOS_RST_MAX_THRD 0x8B
+#define AD2S1210_REG_DOS_RST_MIN_THRD 0x8C
+#define AD2S1210_REG_LOT_HIGH_THRD 0x8D
+#define AD2S1210_REG_LOT_LOW_THRD 0x8E
+#define AD2S1210_REG_EXCIT_FREQ 0x91
+#define AD2S1210_REG_CONTROL 0x92
+#define AD2S1210_REG_SOFT_RESET 0xF0
+#define AD2S1210_REG_FAULT 0xFF
+
+/* pin SAMPLE, A0, A1, RES0, RES1, is controlled by driver */
+#define AD2S1210_SAA 3
+#define AD2S1210_PN (AD2S1210_SAA + AD2S1210_RES)
+
+#define AD2S1210_MIN_CLKIN 6144000
+#define AD2S1210_MAX_CLKIN 10240000
+#define AD2S1210_MIN_EXCIT 2000
+#define AD2S1210_MAX_EXCIT 20000
+#define AD2S1210_MIN_FCW 0x4
+#define AD2S1210_MAX_FCW 0x50
+
+/* default input clock on serial interface */
+#define AD2S1210_DEF_CLKIN 8192000
+/* clock period in nano second */
+#define AD2S1210_DEF_TCK (1000000000/AD2S1210_DEF_CLKIN)
+#define AD2S1210_DEF_EXCIT 10000
+
+enum ad2s1210_mode {
+ MOD_POS = 0,
+ MOD_VEL,
+ MOD_CONFIG,
+ MOD_RESERVED,
+};
+
+static const unsigned int ad2s1210_resolution_value[] = { 10, 12, 14, 16 };
+
+struct ad2s1210_state {
+ const struct ad2s1210_platform_data *pdata;
+ struct mutex lock;
+ struct spi_device *sdev;
+ unsigned int fclkin;
+ unsigned int fexcit;
+ bool hysteresis;
+ bool old_data;
+ u8 resolution;
+ enum ad2s1210_mode mode;
+ u8 rx[2] ____cacheline_aligned;
+ u8 tx[2] ____cacheline_aligned;
+};
+
+static const int ad2s1210_mode_vals[4][2] = {
+ [MOD_POS] = { 0, 0 },
+ [MOD_VEL] = { 0, 1 },
+ [MOD_CONFIG] = { 1, 0 },
+};
+static inline void ad2s1210_set_mode(enum ad2s1210_mode mode,
+ struct ad2s1210_state *st)
+{
+ gpio_set_value(st->pdata->a[0], ad2s1210_mode_vals[mode][0]);
+ gpio_set_value(st->pdata->a[1], ad2s1210_mode_vals[mode][1]);
+ st->mode = mode;
+}
+
+/* write 1 bytes (address or data) to the chip */
+static int ad2s1210_config_write(struct ad2s1210_state *st, u8 data)
+{
+ int ret;
+
+ ad2s1210_set_mode(MOD_CONFIG, st);
+ st->tx[0] = data;
+ ret = spi_write(st->sdev, st->tx, 1);
+ if (ret < 0)
+ return ret;
+ st->old_data = true;
+
+ return 0;
+}
+
+/* read value from one of the registers */
+static int ad2s1210_config_read(struct ad2s1210_state *st,
+ unsigned char address)
+{
+ struct spi_transfer xfer = {
+ .len = 2,
+ .rx_buf = st->rx,
+ .tx_buf = st->tx,
+ };
+ int ret = 0;
+
+ ad2s1210_set_mode(MOD_CONFIG, st);
+ st->tx[0] = address | AD2S1210_MSB_IS_HIGH;
+ st->tx[1] = AD2S1210_REG_FAULT;
+ ret = spi_sync_transfer(st->sdev, &xfer, 1);
+ if (ret < 0)
+ return ret;
+ st->old_data = true;
+
+ return st->rx[1];
+}
+
+static inline
+int ad2s1210_update_frequency_control_word(struct ad2s1210_state *st)
+{
+ int ret;
+ unsigned char fcw;
+
+ fcw = (unsigned char)(st->fexcit * (1 << 15) / st->fclkin);
+ if (fcw < AD2S1210_MIN_FCW || fcw > AD2S1210_MAX_FCW) {
+ dev_err(&st->sdev->dev, "ad2s1210: FCW out of range\n");
+ return -ERANGE;
+ }
+
+ ret = ad2s1210_config_write(st, AD2S1210_REG_EXCIT_FREQ);
+ if (ret < 0)
+ return ret;
+
+ return ad2s1210_config_write(st, fcw);
+}
+
+static unsigned char ad2s1210_read_resolution_pin(struct ad2s1210_state *st)
+{
+ return ad2s1210_resolution_value[
+ (gpio_get_value(st->pdata->res[0]) << 1) |
+ gpio_get_value(st->pdata->res[1])];
+}
+
+static const int ad2s1210_res_pins[4][2] = {
+ { 0, 0 }, {0, 1}, {1, 0}, {1, 1}
+};
+
+static inline void ad2s1210_set_resolution_pin(struct ad2s1210_state *st)
+{
+ gpio_set_value(st->pdata->res[0],
+ ad2s1210_res_pins[(st->resolution - 10)/2][0]);
+ gpio_set_value(st->pdata->res[1],
+ ad2s1210_res_pins[(st->resolution - 10)/2][1]);
+}
+
+static inline int ad2s1210_soft_reset(struct ad2s1210_state *st)
+{
+ int ret;
+
+ ret = ad2s1210_config_write(st, AD2S1210_REG_SOFT_RESET);
+ if (ret < 0)
+ return ret;
+
+ return ad2s1210_config_write(st, 0x0);
+}
+
+static ssize_t ad2s1210_show_fclkin(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct ad2s1210_state *st = iio_priv(dev_to_iio_dev(dev));
+
+ return sprintf(buf, "%u\n", st->fclkin);
+}
+
+static ssize_t ad2s1210_store_fclkin(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t len)
+{
+ struct ad2s1210_state *st = iio_priv(dev_to_iio_dev(dev));
+ unsigned int fclkin;
+ int ret;
+
+ ret = kstrtouint(buf, 10, &fclkin);
+ if (ret)
+ return ret;
+ if (fclkin < AD2S1210_MIN_CLKIN || fclkin > AD2S1210_MAX_CLKIN) {
+ dev_err(dev, "ad2s1210: fclkin out of range\n");
+ return -EINVAL;
+ }
+
+ mutex_lock(&st->lock);
+ st->fclkin = fclkin;
+
+ ret = ad2s1210_update_frequency_control_word(st);
+ if (ret < 0)
+ goto error_ret;
+ ret = ad2s1210_soft_reset(st);
+error_ret:
+ mutex_unlock(&st->lock);
+
+ return ret < 0 ? ret : len;
+}
+
+static ssize_t ad2s1210_show_fexcit(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct ad2s1210_state *st = iio_priv(dev_to_iio_dev(dev));
+
+ return sprintf(buf, "%u\n", st->fexcit);
+}
+
+static ssize_t ad2s1210_store_fexcit(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t len)
+{
+ struct ad2s1210_state *st = iio_priv(dev_to_iio_dev(dev));
+ unsigned int fexcit;
+ int ret;
+
+ ret = kstrtouint(buf, 10, &fexcit);
+ if (ret < 0)
+ return ret;
+ if (fexcit < AD2S1210_MIN_EXCIT || fexcit > AD2S1210_MAX_EXCIT) {
+ dev_err(dev,
+ "ad2s1210: excitation frequency out of range\n");
+ return -EINVAL;
+ }
+ mutex_lock(&st->lock);
+ st->fexcit = fexcit;
+ ret = ad2s1210_update_frequency_control_word(st);
+ if (ret < 0)
+ goto error_ret;
+ ret = ad2s1210_soft_reset(st);
+error_ret:
+ mutex_unlock(&st->lock);
+
+ return ret < 0 ? ret : len;
+}
+
+static ssize_t ad2s1210_show_control(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct ad2s1210_state *st = iio_priv(dev_to_iio_dev(dev));
+ int ret;
+
+ mutex_lock(&st->lock);
+ ret = ad2s1210_config_read(st, AD2S1210_REG_CONTROL);
+ mutex_unlock(&st->lock);
+ return ret < 0 ? ret : sprintf(buf, "0x%x\n", ret);
+}
+
+static ssize_t ad2s1210_store_control(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t len)
+{
+ struct ad2s1210_state *st = iio_priv(dev_to_iio_dev(dev));
+ unsigned char udata;
+ unsigned char data;
+ int ret;
+
+ ret = kstrtou8(buf, 16, &udata);
+ if (ret)
+ return -EINVAL;
+
+ mutex_lock(&st->lock);
+ ret = ad2s1210_config_write(st, AD2S1210_REG_CONTROL);
+ if (ret < 0)
+ goto error_ret;
+ data = udata & AD2S1210_MSB_IS_LOW;
+ ret = ad2s1210_config_write(st, data);
+ if (ret < 0)
+ goto error_ret;
+
+ ret = ad2s1210_config_read(st, AD2S1210_REG_CONTROL);
+ if (ret < 0)
+ goto error_ret;
+ if (ret & AD2S1210_MSB_IS_HIGH) {
+ ret = -EIO;
+ dev_err(dev,
+ "ad2s1210: write control register fail\n");
+ goto error_ret;
+ }
+ st->resolution
+ = ad2s1210_resolution_value[data & AD2S1210_SET_RESOLUTION];
+ if (st->pdata->gpioin) {
+ data = ad2s1210_read_resolution_pin(st);
+ if (data != st->resolution)
+ dev_warn(dev, "ad2s1210: resolution settings not match\n");
+ } else
+ ad2s1210_set_resolution_pin(st);
+
+ ret = len;
+ st->hysteresis = !!(data & AD2S1210_ENABLE_HYSTERESIS);
+
+error_ret:
+ mutex_unlock(&st->lock);
+ return ret;
+}
+
+static ssize_t ad2s1210_show_resolution(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct ad2s1210_state *st = iio_priv(dev_to_iio_dev(dev));
+
+ return sprintf(buf, "%d\n", st->resolution);
+}
+
+static ssize_t ad2s1210_store_resolution(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t len)
+{
+ struct ad2s1210_state *st = iio_priv(dev_to_iio_dev(dev));
+ unsigned char data;
+ unsigned char udata;
+ int ret;
+
+ ret = kstrtou8(buf, 10, &udata);
+ if (ret || udata < 10 || udata > 16) {
+ dev_err(dev, "ad2s1210: resolution out of range\n");
+ return -EINVAL;
+ }
+ mutex_lock(&st->lock);
+ ret = ad2s1210_config_read(st, AD2S1210_REG_CONTROL);
+ if (ret < 0)
+ goto error_ret;
+ data = ret;
+ data &= ~AD2S1210_SET_RESOLUTION;
+ data |= (udata - 10) >> 1;
+ ret = ad2s1210_config_write(st, AD2S1210_REG_CONTROL);
+ if (ret < 0)
+ goto error_ret;
+ ret = ad2s1210_config_write(st, data & AD2S1210_MSB_IS_LOW);
+ if (ret < 0)
+ goto error_ret;
+ ret = ad2s1210_config_read(st, AD2S1210_REG_CONTROL);
+ if (ret < 0)
+ goto error_ret;
+ data = ret;
+ if (data & AD2S1210_MSB_IS_HIGH) {
+ ret = -EIO;
+ dev_err(dev, "ad2s1210: setting resolution fail\n");
+ goto error_ret;
+ }
+ st->resolution
+ = ad2s1210_resolution_value[data & AD2S1210_SET_RESOLUTION];
+ if (st->pdata->gpioin) {
+ data = ad2s1210_read_resolution_pin(st);
+ if (data != st->resolution)
+ dev_warn(dev, "ad2s1210: resolution settings not match\n");
+ } else
+ ad2s1210_set_resolution_pin(st);
+ ret = len;
+error_ret:
+ mutex_unlock(&st->lock);
+ return ret;
+}
+
+/* read the fault register since last sample */
+static ssize_t ad2s1210_show_fault(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct ad2s1210_state *st = iio_priv(dev_to_iio_dev(dev));
+ int ret;
+
+ mutex_lock(&st->lock);
+ ret = ad2s1210_config_read(st, AD2S1210_REG_FAULT);
+ mutex_unlock(&st->lock);
+
+ return ret ? ret : sprintf(buf, "0x%x\n", ret);
+}
+
+static ssize_t ad2s1210_clear_fault(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t len)
+{
+ struct ad2s1210_state *st = iio_priv(dev_to_iio_dev(dev));
+ int ret;
+
+ mutex_lock(&st->lock);
+ gpio_set_value(st->pdata->sample, 0);
+ /* delay (2 * tck + 20) nano seconds */
+ udelay(1);
+ gpio_set_value(st->pdata->sample, 1);
+ ret = ad2s1210_config_read(st, AD2S1210_REG_FAULT);
+ if (ret < 0)
+ goto error_ret;
+ gpio_set_value(st->pdata->sample, 0);
+ gpio_set_value(st->pdata->sample, 1);
+error_ret:
+ mutex_unlock(&st->lock);
+
+ return ret < 0 ? ret : len;
+}
+
+static ssize_t ad2s1210_show_reg(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct ad2s1210_state *st = iio_priv(dev_to_iio_dev(dev));
+ struct iio_dev_attr *iattr = to_iio_dev_attr(attr);
+ int ret;
+
+ mutex_lock(&st->lock);
+ ret = ad2s1210_config_read(st, iattr->address);
+ mutex_unlock(&st->lock);
+
+ return ret < 0 ? ret : sprintf(buf, "%d\n", ret);
+}
+
+static ssize_t ad2s1210_store_reg(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t len)
+{
+ struct ad2s1210_state *st = iio_priv(dev_to_iio_dev(dev));
+ unsigned char data;
+ int ret;
+ struct iio_dev_attr *iattr = to_iio_dev_attr(attr);
+
+ ret = kstrtou8(buf, 10, &data);
+ if (ret)
+ return -EINVAL;
+ mutex_lock(&st->lock);
+ ret = ad2s1210_config_write(st, iattr->address);
+ if (ret < 0)
+ goto error_ret;
+ ret = ad2s1210_config_write(st, data & AD2S1210_MSB_IS_LOW);
+error_ret:
+ mutex_unlock(&st->lock);
+ return ret < 0 ? ret : len;
+}
+
+static int ad2s1210_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val,
+ int *val2,
+ long m)
+{
+ struct ad2s1210_state *st = iio_priv(indio_dev);
+ bool negative;
+ int ret = 0;
+ u16 pos;
+ s16 vel;
+
+ mutex_lock(&st->lock);
+ gpio_set_value(st->pdata->sample, 0);
+ /* delay (6 * tck + 20) nano seconds */
+ udelay(1);
+
+ switch (chan->type) {
+ case IIO_ANGL:
+ ad2s1210_set_mode(MOD_POS, st);
+ break;
+ case IIO_ANGL_VEL:
+ ad2s1210_set_mode(MOD_VEL, st);
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+ if (ret < 0)
+ goto error_ret;
+ ret = spi_read(st->sdev, st->rx, 2);
+ if (ret < 0)
+ goto error_ret;
+
+ switch (chan->type) {
+ case IIO_ANGL:
+ pos = be16_to_cpup((__be16 *) st->rx);
+ if (st->hysteresis)
+ pos >>= 16 - st->resolution;
+ *val = pos;
+ ret = IIO_VAL_INT;
+ break;
+ case IIO_ANGL_VEL:
+ negative = st->rx[0] & 0x80;
+ vel = be16_to_cpup((__be16 *) st->rx);
+ vel >>= 16 - st->resolution;
+ if (vel & 0x8000) {
+ negative = (0xffff >> st->resolution) << st->resolution;
+ vel |= negative;
+ }
+ *val = vel;
+ ret = IIO_VAL_INT;
+ break;
+ default:
+ mutex_unlock(&st->lock);
+ return -EINVAL;
+ }
+
+error_ret:
+ gpio_set_value(st->pdata->sample, 1);
+ /* delay (2 * tck + 20) nano seconds */
+ udelay(1);
+ mutex_unlock(&st->lock);
+ return ret;
+}
+
+static IIO_DEVICE_ATTR(fclkin, S_IRUGO | S_IWUSR,
+ ad2s1210_show_fclkin, ad2s1210_store_fclkin, 0);
+static IIO_DEVICE_ATTR(fexcit, S_IRUGO | S_IWUSR,
+ ad2s1210_show_fexcit, ad2s1210_store_fexcit, 0);
+static IIO_DEVICE_ATTR(control, S_IRUGO | S_IWUSR,
+ ad2s1210_show_control, ad2s1210_store_control, 0);
+static IIO_DEVICE_ATTR(bits, S_IRUGO | S_IWUSR,
+ ad2s1210_show_resolution, ad2s1210_store_resolution, 0);
+static IIO_DEVICE_ATTR(fault, S_IRUGO | S_IWUSR,
+ ad2s1210_show_fault, ad2s1210_clear_fault, 0);
+
+static IIO_DEVICE_ATTR(los_thrd, S_IRUGO | S_IWUSR,
+ ad2s1210_show_reg, ad2s1210_store_reg,
+ AD2S1210_REG_LOS_THRD);
+static IIO_DEVICE_ATTR(dos_ovr_thrd, S_IRUGO | S_IWUSR,
+ ad2s1210_show_reg, ad2s1210_store_reg,
+ AD2S1210_REG_DOS_OVR_THRD);
+static IIO_DEVICE_ATTR(dos_mis_thrd, S_IRUGO | S_IWUSR,
+ ad2s1210_show_reg, ad2s1210_store_reg,
+ AD2S1210_REG_DOS_MIS_THRD);
+static IIO_DEVICE_ATTR(dos_rst_max_thrd, S_IRUGO | S_IWUSR,
+ ad2s1210_show_reg, ad2s1210_store_reg,
+ AD2S1210_REG_DOS_RST_MAX_THRD);
+static IIO_DEVICE_ATTR(dos_rst_min_thrd, S_IRUGO | S_IWUSR,
+ ad2s1210_show_reg, ad2s1210_store_reg,
+ AD2S1210_REG_DOS_RST_MIN_THRD);
+static IIO_DEVICE_ATTR(lot_high_thrd, S_IRUGO | S_IWUSR,
+ ad2s1210_show_reg, ad2s1210_store_reg,
+ AD2S1210_REG_LOT_HIGH_THRD);
+static IIO_DEVICE_ATTR(lot_low_thrd, S_IRUGO | S_IWUSR,
+ ad2s1210_show_reg, ad2s1210_store_reg,
+ AD2S1210_REG_LOT_LOW_THRD);
+
+
+static const struct iio_chan_spec ad2s1210_channels[] = {
+ {
+ .type = IIO_ANGL,
+ .indexed = 1,
+ .channel = 0,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
+ }, {
+ .type = IIO_ANGL_VEL,
+ .indexed = 1,
+ .channel = 0,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
+ }
+};
+
+static struct attribute *ad2s1210_attributes[] = {
+ &iio_dev_attr_fclkin.dev_attr.attr,
+ &iio_dev_attr_fexcit.dev_attr.attr,
+ &iio_dev_attr_control.dev_attr.attr,
+ &iio_dev_attr_bits.dev_attr.attr,
+ &iio_dev_attr_fault.dev_attr.attr,
+ &iio_dev_attr_los_thrd.dev_attr.attr,
+ &iio_dev_attr_dos_ovr_thrd.dev_attr.attr,
+ &iio_dev_attr_dos_mis_thrd.dev_attr.attr,
+ &iio_dev_attr_dos_rst_max_thrd.dev_attr.attr,
+ &iio_dev_attr_dos_rst_min_thrd.dev_attr.attr,
+ &iio_dev_attr_lot_high_thrd.dev_attr.attr,
+ &iio_dev_attr_lot_low_thrd.dev_attr.attr,
+ NULL,
+};
+
+static const struct attribute_group ad2s1210_attribute_group = {
+ .attrs = ad2s1210_attributes,
+};
+
+static int ad2s1210_initial(struct ad2s1210_state *st)
+{
+ unsigned char data;
+ int ret;
+
+ mutex_lock(&st->lock);
+ if (st->pdata->gpioin)
+ st->resolution = ad2s1210_read_resolution_pin(st);
+ else
+ ad2s1210_set_resolution_pin(st);
+
+ ret = ad2s1210_config_write(st, AD2S1210_REG_CONTROL);
+ if (ret < 0)
+ goto error_ret;
+ data = AD2S1210_DEF_CONTROL & ~(AD2S1210_SET_RESOLUTION);
+ data |= (st->resolution - 10) >> 1;
+ ret = ad2s1210_config_write(st, data);
+ if (ret < 0)
+ goto error_ret;
+ ret = ad2s1210_config_read(st, AD2S1210_REG_CONTROL);
+ if (ret < 0)
+ goto error_ret;
+
+ if (ret & AD2S1210_MSB_IS_HIGH) {
+ ret = -EIO;
+ goto error_ret;
+ }
+
+ ret = ad2s1210_update_frequency_control_word(st);
+ if (ret < 0)
+ goto error_ret;
+ ret = ad2s1210_soft_reset(st);
+error_ret:
+ mutex_unlock(&st->lock);
+ return ret;
+}
+
+static const struct iio_info ad2s1210_info = {
+ .read_raw = &ad2s1210_read_raw,
+ .attrs = &ad2s1210_attribute_group,
+ .driver_module = THIS_MODULE,
+};
+
+static int ad2s1210_setup_gpios(struct ad2s1210_state *st)
+{
+ unsigned long flags = st->pdata->gpioin ? GPIOF_DIR_IN : GPIOF_DIR_OUT;
+ struct gpio ad2s1210_gpios[] = {
+ { st->pdata->sample, GPIOF_DIR_IN, "sample" },
+ { st->pdata->a[0], flags, "a0" },
+ { st->pdata->a[1], flags, "a1" },
+ { st->pdata->res[0], flags, "res0" },
+ { st->pdata->res[0], flags, "res1" },
+ };
+
+ return gpio_request_array(ad2s1210_gpios, ARRAY_SIZE(ad2s1210_gpios));
+}
+
+static void ad2s1210_free_gpios(struct ad2s1210_state *st)
+{
+ unsigned long flags = st->pdata->gpioin ? GPIOF_DIR_IN : GPIOF_DIR_OUT;
+ struct gpio ad2s1210_gpios[] = {
+ { st->pdata->sample, GPIOF_DIR_IN, "sample" },
+ { st->pdata->a[0], flags, "a0" },
+ { st->pdata->a[1], flags, "a1" },
+ { st->pdata->res[0], flags, "res0" },
+ { st->pdata->res[0], flags, "res1" },
+ };
+
+ gpio_free_array(ad2s1210_gpios, ARRAY_SIZE(ad2s1210_gpios));
+}
+
+static int ad2s1210_probe(struct spi_device *spi)
+{
+ struct iio_dev *indio_dev;
+ struct ad2s1210_state *st;
+ int ret;
+
+ if (spi->dev.platform_data == NULL)
+ return -EINVAL;
+
+ indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));
+ if (!indio_dev)
+ return -ENOMEM;
+ st = iio_priv(indio_dev);
+ st->pdata = spi->dev.platform_data;
+ ret = ad2s1210_setup_gpios(st);
+ if (ret < 0)
+ return ret;
+
+ spi_set_drvdata(spi, indio_dev);
+
+ mutex_init(&st->lock);
+ st->sdev = spi;
+ st->hysteresis = true;
+ st->mode = MOD_CONFIG;
+ st->resolution = 12;
+ st->fexcit = AD2S1210_DEF_EXCIT;
+
+ indio_dev->dev.parent = &spi->dev;
+ indio_dev->info = &ad2s1210_info;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->channels = ad2s1210_channels;
+ indio_dev->num_channels = ARRAY_SIZE(ad2s1210_channels);
+ indio_dev->name = spi_get_device_id(spi)->name;
+
+ ret = iio_device_register(indio_dev);
+ if (ret)
+ goto error_free_gpios;
+
+ st->fclkin = spi->max_speed_hz;
+ spi->mode = SPI_MODE_3;
+ spi_setup(spi);
+ ad2s1210_initial(st);
+
+ return 0;
+
+error_free_gpios:
+ ad2s1210_free_gpios(st);
+ return ret;
+}
+
+static int ad2s1210_remove(struct spi_device *spi)
+{
+ struct iio_dev *indio_dev = spi_get_drvdata(spi);
+
+ iio_device_unregister(indio_dev);
+ ad2s1210_free_gpios(iio_priv(indio_dev));
+
+ return 0;
+}
+
+static const struct spi_device_id ad2s1210_id[] = {
+ { "ad2s1210" },
+ {}
+};
+MODULE_DEVICE_TABLE(spi, ad2s1210_id);
+
+static struct spi_driver ad2s1210_driver = {
+ .driver = {
+ .name = DRV_NAME,
+ .owner = THIS_MODULE,
+ },
+ .probe = ad2s1210_probe,
+ .remove = ad2s1210_remove,
+ .id_table = ad2s1210_id,
+};
+module_spi_driver(ad2s1210_driver);
+
+MODULE_AUTHOR("Graff Yang <graff.yang@gmail.com>");
+MODULE_DESCRIPTION("Analog Devices AD2S1210 Resolver to Digital SPI driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/staging/iio/resolver/ad2s1210.h b/drivers/staging/iio/resolver/ad2s1210.h
new file mode 100644
index 000000000..c7158f6e6
--- /dev/null
+++ b/drivers/staging/iio/resolver/ad2s1210.h
@@ -0,0 +1,20 @@
+/*
+ * ad2s1210.h plaform data for the ADI Resolver to Digital Converters:
+ * AD2S1210
+ *
+ * Copyright (c) 2010-2010 Analog Devices Inc.
+ *
+ * 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.
+ */
+#ifndef _AD2S1210_H
+#define _AD2S1210_H
+
+struct ad2s1210_platform_data {
+ unsigned sample;
+ unsigned a[2];
+ unsigned res[2];
+ bool gpioin;
+};
+#endif /* _AD2S1210_H */
diff --git a/drivers/staging/iio/resolver/ad2s90.c b/drivers/staging/iio/resolver/ad2s90.c
new file mode 100644
index 000000000..e24c58906
--- /dev/null
+++ b/drivers/staging/iio/resolver/ad2s90.c
@@ -0,0 +1,120 @@
+/*
+ * ad2s90.c simple support for the ADI Resolver to Digital Converters: AD2S90
+ *
+ * Copyright (c) 2010-2010 Analog Devices Inc.
+ *
+ * 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/types.h>
+#include <linux/mutex.h>
+#include <linux/device.h>
+#include <linux/spi/spi.h>
+#include <linux/slab.h>
+#include <linux/sysfs.h>
+#include <linux/module.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+
+struct ad2s90_state {
+ struct mutex lock;
+ struct spi_device *sdev;
+ u8 rx[2] ____cacheline_aligned;
+};
+
+static int ad2s90_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val,
+ int *val2,
+ long m)
+{
+ int ret;
+ struct ad2s90_state *st = iio_priv(indio_dev);
+
+ mutex_lock(&st->lock);
+ ret = spi_read(st->sdev, st->rx, 2);
+ if (ret)
+ goto error_ret;
+ *val = (((u16)(st->rx[0])) << 4) | ((st->rx[1] & 0xF0) >> 4);
+
+error_ret:
+ mutex_unlock(&st->lock);
+
+ return IIO_VAL_INT;
+}
+
+static const struct iio_info ad2s90_info = {
+ .read_raw = &ad2s90_read_raw,
+ .driver_module = THIS_MODULE,
+};
+
+static const struct iio_chan_spec ad2s90_chan = {
+ .type = IIO_ANGL,
+ .indexed = 1,
+ .channel = 0,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
+};
+
+static int ad2s90_probe(struct spi_device *spi)
+{
+ struct iio_dev *indio_dev;
+ struct ad2s90_state *st;
+ int ret = 0;
+
+ indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));
+ if (!indio_dev)
+ return -ENOMEM;
+ st = iio_priv(indio_dev);
+ spi_set_drvdata(spi, indio_dev);
+
+ mutex_init(&st->lock);
+ st->sdev = spi;
+ indio_dev->dev.parent = &spi->dev;
+ indio_dev->info = &ad2s90_info;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->channels = &ad2s90_chan;
+ indio_dev->num_channels = 1;
+ indio_dev->name = spi_get_device_id(spi)->name;
+
+ ret = iio_device_register(indio_dev);
+ if (ret)
+ return ret;
+
+ /* need 600ns between CS and the first falling edge of SCLK */
+ spi->max_speed_hz = 830000;
+ spi->mode = SPI_MODE_3;
+ spi_setup(spi);
+
+ return 0;
+}
+
+static int ad2s90_remove(struct spi_device *spi)
+{
+ iio_device_unregister(spi_get_drvdata(spi));
+
+ return 0;
+}
+
+static const struct spi_device_id ad2s90_id[] = {
+ { "ad2s90" },
+ {}
+};
+MODULE_DEVICE_TABLE(spi, ad2s90_id);
+
+static struct spi_driver ad2s90_driver = {
+ .driver = {
+ .name = "ad2s90",
+ .owner = THIS_MODULE,
+ },
+ .probe = ad2s90_probe,
+ .remove = ad2s90_remove,
+ .id_table = ad2s90_id,
+};
+module_spi_driver(ad2s90_driver);
+
+MODULE_AUTHOR("Graff Yang <graff.yang@gmail.com>");
+MODULE_DESCRIPTION("Analog Devices AD2S90 Resolver to Digital SPI driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/staging/iio/ring_hw.h b/drivers/staging/iio/ring_hw.h
new file mode 100644
index 000000000..75bf47bfe
--- /dev/null
+++ b/drivers/staging/iio/ring_hw.h
@@ -0,0 +1,27 @@
+/*
+ * ring_hw.h - common functionality for iio hardware ring buffers
+ *
+ * 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.
+ *
+ * Copyright (c) 2009 Jonathan Cameron <jic23@kernel.org>
+ *
+ */
+
+#ifndef _RING_HW_H_
+#define _RING_HW_H_
+
+/**
+ * struct iio_hw_ring_buffer- hardware ring buffer
+ * @buf: generic ring buffer elements
+ * @private: device specific data
+ */
+struct iio_hw_buffer {
+ struct iio_buffer buf;
+ void *private;
+};
+
+#define iio_to_hw_buf(r) container_of(r, struct iio_hw_buffer, buf)
+
+#endif /* _RING_HW_H_ */
diff --git a/drivers/staging/iio/trigger/Kconfig b/drivers/staging/iio/trigger/Kconfig
new file mode 100644
index 000000000..710a2f3e7
--- /dev/null
+++ b/drivers/staging/iio/trigger/Kconfig
@@ -0,0 +1,29 @@
+ #
+# Industrial I/O standalone triggers
+#
+comment "Triggers - standalone"
+
+if IIO_TRIGGER
+
+config IIO_PERIODIC_RTC_TRIGGER
+ tristate "Periodic RTC triggers"
+ depends on RTC_CLASS
+ help
+ Provides support for using periodic capable real time
+ clocks as IIO triggers.
+
+ To compile this driver as a module, choose M here: the
+ module will be called iio-trig-periodic-rtc.
+
+config IIO_BFIN_TMR_TRIGGER
+ tristate "Blackfin TIMER trigger"
+ depends on BLACKFIN
+ select BFIN_GPTIMERS
+ help
+ Provides support for using a Blackfin timer as IIO triggers.
+ If unsure, say N (but it's safe to say "Y").
+
+ To compile this driver as a module, choose M here: the
+ module will be called iio-trig-bfin-timer.
+
+endif # IIO_TRIGGER
diff --git a/drivers/staging/iio/trigger/Makefile b/drivers/staging/iio/trigger/Makefile
new file mode 100644
index 000000000..238481b78
--- /dev/null
+++ b/drivers/staging/iio/trigger/Makefile
@@ -0,0 +1,6 @@
+#
+# Makefile for triggers not associated with iio-devices
+#
+
+obj-$(CONFIG_IIO_PERIODIC_RTC_TRIGGER) += iio-trig-periodic-rtc.o
+obj-$(CONFIG_IIO_BFIN_TMR_TRIGGER) += iio-trig-bfin-timer.o
diff --git a/drivers/staging/iio/trigger/iio-trig-bfin-timer.c b/drivers/staging/iio/trigger/iio-trig-bfin-timer.c
new file mode 100644
index 000000000..3c1c8c6c4
--- /dev/null
+++ b/drivers/staging/iio/trigger/iio-trig-bfin-timer.c
@@ -0,0 +1,292 @@
+/*
+ * Copyright 2011 Analog Devices Inc.
+ *
+ * Licensed under the GPL-2.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/delay.h>
+
+#include <asm/gptimers.h>
+#include <asm/portmux.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/trigger.h>
+
+#include "iio-trig-bfin-timer.h"
+
+struct bfin_timer {
+ unsigned short id, bit;
+ unsigned long irqbit;
+ int irq;
+ int pin;
+};
+
+/*
+ * this covers all hardware timer configurations on
+ * all Blackfin derivatives out there today
+ */
+
+static struct bfin_timer iio_bfin_timer_code[MAX_BLACKFIN_GPTIMERS] = {
+ {TIMER0_id, TIMER0bit, TIMER_STATUS_TIMIL0, IRQ_TIMER0, P_TMR0},
+ {TIMER1_id, TIMER1bit, TIMER_STATUS_TIMIL1, IRQ_TIMER1, P_TMR1},
+ {TIMER2_id, TIMER2bit, TIMER_STATUS_TIMIL2, IRQ_TIMER2, P_TMR2},
+#if (MAX_BLACKFIN_GPTIMERS > 3)
+ {TIMER3_id, TIMER3bit, TIMER_STATUS_TIMIL3, IRQ_TIMER3, P_TMR3},
+ {TIMER4_id, TIMER4bit, TIMER_STATUS_TIMIL4, IRQ_TIMER4, P_TMR4},
+ {TIMER5_id, TIMER5bit, TIMER_STATUS_TIMIL5, IRQ_TIMER5, P_TMR5},
+ {TIMER6_id, TIMER6bit, TIMER_STATUS_TIMIL6, IRQ_TIMER6, P_TMR6},
+ {TIMER7_id, TIMER7bit, TIMER_STATUS_TIMIL7, IRQ_TIMER7, P_TMR7},
+#endif
+#if (MAX_BLACKFIN_GPTIMERS > 8)
+ {TIMER8_id, TIMER8bit, TIMER_STATUS_TIMIL8, IRQ_TIMER8, P_TMR8},
+ {TIMER9_id, TIMER9bit, TIMER_STATUS_TIMIL9, IRQ_TIMER9, P_TMR9},
+ {TIMER10_id, TIMER10bit, TIMER_STATUS_TIMIL10, IRQ_TIMER10, P_TMR10},
+#if (MAX_BLACKFIN_GPTIMERS > 11)
+ {TIMER11_id, TIMER11bit, TIMER_STATUS_TIMIL11, IRQ_TIMER11, P_TMR11},
+#endif
+#endif
+};
+
+struct bfin_tmr_state {
+ struct iio_trigger *trig;
+ struct bfin_timer *t;
+ unsigned timer_num;
+ bool output_enable;
+ unsigned int duty;
+ int irq;
+};
+
+static int iio_bfin_tmr_set_state(struct iio_trigger *trig, bool state)
+{
+ struct bfin_tmr_state *st = iio_trigger_get_drvdata(trig);
+
+ if (get_gptimer_period(st->t->id) == 0)
+ return -EINVAL;
+
+ if (state)
+ enable_gptimers(st->t->bit);
+ else
+ disable_gptimers(st->t->bit);
+
+ return 0;
+}
+
+static ssize_t iio_bfin_tmr_frequency_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct iio_trigger *trig = to_iio_trigger(dev);
+ struct bfin_tmr_state *st = iio_trigger_get_drvdata(trig);
+ unsigned int val;
+ bool enabled;
+ int ret;
+
+ ret = kstrtouint(buf, 10, &val);
+ if (ret)
+ return ret;
+
+ if (val > 100000)
+ return -EINVAL;
+
+ enabled = get_enabled_gptimers() & st->t->bit;
+
+ if (enabled)
+ disable_gptimers(st->t->bit);
+
+ if (val == 0)
+ return count;
+
+ val = get_sclk() / val;
+ if (val <= 4 || val <= st->duty)
+ return -EINVAL;
+
+ set_gptimer_period(st->t->id, val);
+ set_gptimer_pwidth(st->t->id, val - st->duty);
+
+ if (enabled)
+ enable_gptimers(st->t->bit);
+
+ return count;
+}
+
+static ssize_t iio_bfin_tmr_frequency_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct iio_trigger *trig = to_iio_trigger(dev);
+ struct bfin_tmr_state *st = iio_trigger_get_drvdata(trig);
+ unsigned int period = get_gptimer_period(st->t->id);
+ unsigned long val;
+
+ if (period == 0)
+ val = 0;
+ else
+ val = get_sclk() / get_gptimer_period(st->t->id);
+
+ return sprintf(buf, "%lu\n", val);
+}
+
+static DEVICE_ATTR(frequency, S_IRUGO | S_IWUSR, iio_bfin_tmr_frequency_show,
+ iio_bfin_tmr_frequency_store);
+
+static struct attribute *iio_bfin_tmr_trigger_attrs[] = {
+ &dev_attr_frequency.attr,
+ NULL,
+};
+
+static const struct attribute_group iio_bfin_tmr_trigger_attr_group = {
+ .attrs = iio_bfin_tmr_trigger_attrs,
+};
+
+static const struct attribute_group *iio_bfin_tmr_trigger_attr_groups[] = {
+ &iio_bfin_tmr_trigger_attr_group,
+ NULL
+};
+
+static irqreturn_t iio_bfin_tmr_trigger_isr(int irq, void *devid)
+{
+ struct bfin_tmr_state *st = devid;
+
+ clear_gptimer_intr(st->t->id);
+ iio_trigger_poll(st->trig);
+
+ return IRQ_HANDLED;
+}
+
+static int iio_bfin_tmr_get_number(int irq)
+{
+ int i;
+
+ for (i = 0; i < MAX_BLACKFIN_GPTIMERS; i++)
+ if (iio_bfin_timer_code[i].irq == irq)
+ return i;
+
+ return -ENODEV;
+}
+
+static const struct iio_trigger_ops iio_bfin_tmr_trigger_ops = {
+ .owner = THIS_MODULE,
+ .set_trigger_state = iio_bfin_tmr_set_state,
+};
+
+static int iio_bfin_tmr_trigger_probe(struct platform_device *pdev)
+{
+ struct iio_bfin_timer_trigger_pdata *pdata = pdev->dev.platform_data;
+ struct bfin_tmr_state *st;
+ unsigned int config;
+ int ret;
+
+ st = devm_kzalloc(&pdev->dev, sizeof(*st), GFP_KERNEL);
+ if (!st)
+ return -ENOMEM;
+
+ st->irq = platform_get_irq(pdev, 0);
+ if (!st->irq) {
+ dev_err(&pdev->dev, "No IRQs specified");
+ return -ENODEV;
+ }
+
+ ret = iio_bfin_tmr_get_number(st->irq);
+ if (ret < 0)
+ return ret;
+
+ st->timer_num = ret;
+ st->t = &iio_bfin_timer_code[st->timer_num];
+
+ st->trig = iio_trigger_alloc("bfintmr%d", st->timer_num);
+ if (!st->trig)
+ return -ENOMEM;
+
+ st->trig->ops = &iio_bfin_tmr_trigger_ops;
+ st->trig->dev.groups = iio_bfin_tmr_trigger_attr_groups;
+ iio_trigger_set_drvdata(st->trig, st);
+ ret = iio_trigger_register(st->trig);
+ if (ret)
+ goto out;
+
+ ret = request_irq(st->irq, iio_bfin_tmr_trigger_isr,
+ 0, st->trig->name, st);
+ if (ret) {
+ dev_err(&pdev->dev,
+ "request IRQ-%d failed", st->irq);
+ goto out1;
+ }
+
+ config = PWM_OUT | PERIOD_CNT | IRQ_ENA;
+
+ if (pdata && pdata->output_enable) {
+ unsigned long long val;
+
+ st->output_enable = true;
+
+ ret = peripheral_request(st->t->pin, st->trig->name);
+ if (ret)
+ goto out_free_irq;
+
+ val = (unsigned long long)get_sclk() * pdata->duty_ns;
+ do_div(val, NSEC_PER_SEC);
+ st->duty = val;
+
+ /**
+ * The interrupt will be generated at the end of the period,
+ * since we want the interrupt to be generated at end of the
+ * pulse we invert both polarity and duty cycle, so that the
+ * pulse will be generated directly before the interrupt.
+ */
+ if (pdata->active_low)
+ config |= PULSE_HI;
+ } else {
+ st->duty = 1;
+ config |= OUT_DIS;
+ }
+
+ set_gptimer_config(st->t->id, config);
+
+ dev_info(&pdev->dev, "iio trigger Blackfin TMR%d, IRQ-%d",
+ st->timer_num, st->irq);
+ platform_set_drvdata(pdev, st);
+
+ return 0;
+out_free_irq:
+ free_irq(st->irq, st);
+out1:
+ iio_trigger_unregister(st->trig);
+out:
+ iio_trigger_put(st->trig);
+ return ret;
+}
+
+static int iio_bfin_tmr_trigger_remove(struct platform_device *pdev)
+{
+ struct bfin_tmr_state *st = platform_get_drvdata(pdev);
+
+ disable_gptimers(st->t->bit);
+ if (st->output_enable)
+ peripheral_free(st->t->pin);
+ free_irq(st->irq, st);
+ iio_trigger_unregister(st->trig);
+ iio_trigger_put(st->trig);
+
+ return 0;
+}
+
+static struct platform_driver iio_bfin_tmr_trigger_driver = {
+ .driver = {
+ .name = "iio_bfin_tmr_trigger",
+ },
+ .probe = iio_bfin_tmr_trigger_probe,
+ .remove = iio_bfin_tmr_trigger_remove,
+};
+
+module_platform_driver(iio_bfin_tmr_trigger_driver);
+
+MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
+MODULE_DESCRIPTION("Blackfin system timer based trigger for the iio subsystem");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:iio-trig-bfin-timer");
diff --git a/drivers/staging/iio/trigger/iio-trig-bfin-timer.h b/drivers/staging/iio/trigger/iio-trig-bfin-timer.h
new file mode 100644
index 000000000..c07321f8d
--- /dev/null
+++ b/drivers/staging/iio/trigger/iio-trig-bfin-timer.h
@@ -0,0 +1,24 @@
+#ifndef __IIO_BFIN_TIMER_TRIGGER_H__
+#define __IIO_BFIN_TIMER_TRIGGER_H__
+
+/**
+ * struct iio_bfin_timer_trigger_pdata - timer trigger platform data
+ * @output_enable: Enable external trigger pulse generation.
+ * @active_low: Whether the trigger pulse is active low.
+ * @duty_ns: Length of the trigger pulse in nanoseconds.
+ *
+ * This struct is used to configure the output pulse generation of the blackfin
+ * timer trigger. If output_enable is set to true an external trigger signal
+ * will generated on the pin corresponding to the timer. This is useful for
+ * converters which needs an external signal to start conversion. active_low and
+ * duty_ns are used to configure the type of the trigger pulse. If output_enable
+ * is set to false no external trigger pulse will be generated and active_low
+ * and duty_ns are ignored.
+ **/
+struct iio_bfin_timer_trigger_pdata {
+ bool output_enable;
+ bool active_low;
+ unsigned int duty_ns;
+};
+
+#endif
diff --git a/drivers/staging/iio/trigger/iio-trig-periodic-rtc.c b/drivers/staging/iio/trigger/iio-trig-periodic-rtc.c
new file mode 100644
index 000000000..0c1976dde
--- /dev/null
+++ b/drivers/staging/iio/trigger/iio-trig-periodic-rtc.c
@@ -0,0 +1,215 @@
+/* The industrial I/O periodic RTC trigger driver
+ *
+ * Copyright (c) 2008 Jonathan Cameron
+ *
+ * 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.
+ *
+ * This is a heavily rewritten version of the periodic timer system in
+ * earlier version of industrialio. It supplies the same functionality
+ * but via a trigger rather than a specific periodic timer system.
+ */
+
+#include <linux/platform_device.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/rtc.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/trigger.h>
+
+static LIST_HEAD(iio_prtc_trigger_list);
+static DEFINE_MUTEX(iio_prtc_trigger_list_lock);
+
+struct iio_prtc_trigger_info {
+ struct rtc_device *rtc;
+ unsigned int frequency;
+ struct rtc_task task;
+ bool state;
+};
+
+static int iio_trig_periodic_rtc_set_state(struct iio_trigger *trig, bool state)
+{
+ struct iio_prtc_trigger_info *trig_info = iio_trigger_get_drvdata(trig);
+ int ret;
+
+ if (trig_info->frequency == 0 && state)
+ return -EINVAL;
+ dev_dbg(&trig_info->rtc->dev, "trigger frequency is %u\n",
+ trig_info->frequency);
+ ret = rtc_irq_set_state(trig_info->rtc, &trig_info->task, state);
+ if (ret == 0)
+ trig_info->state = state;
+
+ return ret;
+}
+
+static ssize_t iio_trig_periodic_read_freq(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct iio_trigger *trig = to_iio_trigger(dev);
+ struct iio_prtc_trigger_info *trig_info = iio_trigger_get_drvdata(trig);
+
+ return sprintf(buf, "%u\n", trig_info->frequency);
+}
+
+static ssize_t iio_trig_periodic_write_freq(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t len)
+{
+ struct iio_trigger *trig = to_iio_trigger(dev);
+ struct iio_prtc_trigger_info *trig_info = iio_trigger_get_drvdata(trig);
+ unsigned int val;
+ int ret;
+
+ ret = kstrtouint(buf, 10, &val);
+ if (ret)
+ goto error_ret;
+
+ if (val > 0) {
+ ret = rtc_irq_set_freq(trig_info->rtc, &trig_info->task, val);
+ if (ret == 0 && trig_info->state && trig_info->frequency == 0)
+ ret = rtc_irq_set_state(trig_info->rtc,
+ &trig_info->task, 1);
+ } else
+ ret = rtc_irq_set_state(trig_info->rtc, &trig_info->task, 0);
+ if (ret)
+ goto error_ret;
+
+ trig_info->frequency = val;
+
+ return len;
+
+error_ret:
+ return ret;
+}
+
+static DEVICE_ATTR(frequency, S_IRUGO | S_IWUSR,
+ iio_trig_periodic_read_freq,
+ iio_trig_periodic_write_freq);
+
+static struct attribute *iio_trig_prtc_attrs[] = {
+ &dev_attr_frequency.attr,
+ NULL,
+};
+
+static const struct attribute_group iio_trig_prtc_attr_group = {
+ .attrs = iio_trig_prtc_attrs,
+};
+
+static const struct attribute_group *iio_trig_prtc_attr_groups[] = {
+ &iio_trig_prtc_attr_group,
+ NULL
+};
+
+static void iio_prtc_trigger_poll(void *private_data)
+{
+ iio_trigger_poll(private_data);
+}
+
+static const struct iio_trigger_ops iio_prtc_trigger_ops = {
+ .owner = THIS_MODULE,
+ .set_trigger_state = &iio_trig_periodic_rtc_set_state,
+};
+
+static int iio_trig_periodic_rtc_probe(struct platform_device *dev)
+{
+ char **pdata = dev->dev.platform_data;
+ struct iio_prtc_trigger_info *trig_info;
+ struct iio_trigger *trig, *trig2;
+
+ int i, ret;
+
+ for (i = 0;; i++) {
+ if (!pdata[i])
+ break;
+ trig = iio_trigger_alloc("periodic%s", pdata[i]);
+ if (!trig) {
+ ret = -ENOMEM;
+ goto error_free_completed_registrations;
+ }
+ list_add(&trig->alloc_list, &iio_prtc_trigger_list);
+
+ trig_info = kzalloc(sizeof(*trig_info), GFP_KERNEL);
+ if (!trig_info) {
+ ret = -ENOMEM;
+ goto error_put_trigger_and_remove_from_list;
+ }
+ iio_trigger_set_drvdata(trig, trig_info);
+ trig->ops = &iio_prtc_trigger_ops;
+ /* RTC access */
+ trig_info->rtc = rtc_class_open(pdata[i]);
+ if (!trig_info->rtc) {
+ ret = -EINVAL;
+ goto error_free_trig_info;
+ }
+ trig_info->task.func = iio_prtc_trigger_poll;
+ trig_info->task.private_data = trig;
+ ret = rtc_irq_register(trig_info->rtc, &trig_info->task);
+ if (ret)
+ goto error_close_rtc;
+ trig->dev.groups = iio_trig_prtc_attr_groups;
+ ret = iio_trigger_register(trig);
+ if (ret)
+ goto error_unregister_rtc_irq;
+ }
+ return 0;
+error_unregister_rtc_irq:
+ rtc_irq_unregister(trig_info->rtc, &trig_info->task);
+error_close_rtc:
+ rtc_class_close(trig_info->rtc);
+error_free_trig_info:
+ kfree(trig_info);
+error_put_trigger_and_remove_from_list:
+ list_del(&trig->alloc_list);
+ iio_trigger_put(trig);
+error_free_completed_registrations:
+ list_for_each_entry_safe(trig,
+ trig2,
+ &iio_prtc_trigger_list,
+ alloc_list) {
+ trig_info = iio_trigger_get_drvdata(trig);
+ rtc_irq_unregister(trig_info->rtc, &trig_info->task);
+ rtc_class_close(trig_info->rtc);
+ kfree(trig_info);
+ iio_trigger_unregister(trig);
+ }
+ return ret;
+}
+
+static int iio_trig_periodic_rtc_remove(struct platform_device *dev)
+{
+ struct iio_trigger *trig, *trig2;
+ struct iio_prtc_trigger_info *trig_info;
+
+ mutex_lock(&iio_prtc_trigger_list_lock);
+ list_for_each_entry_safe(trig,
+ trig2,
+ &iio_prtc_trigger_list,
+ alloc_list) {
+ trig_info = iio_trigger_get_drvdata(trig);
+ rtc_irq_unregister(trig_info->rtc, &trig_info->task);
+ rtc_class_close(trig_info->rtc);
+ kfree(trig_info);
+ iio_trigger_unregister(trig);
+ }
+ mutex_unlock(&iio_prtc_trigger_list_lock);
+ return 0;
+}
+
+static struct platform_driver iio_trig_periodic_rtc_driver = {
+ .probe = iio_trig_periodic_rtc_probe,
+ .remove = iio_trig_periodic_rtc_remove,
+ .driver = {
+ .name = "iio_prtc_trigger",
+ },
+};
+
+module_platform_driver(iio_trig_periodic_rtc_driver);
+
+MODULE_AUTHOR("Jonathan Cameron <jic23@kernel.org>");
+MODULE_DESCRIPTION("Periodic realtime clock trigger for the iio subsystem");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/staging/lustre/Kconfig b/drivers/staging/lustre/Kconfig
new file mode 100644
index 000000000..a224d88bf
--- /dev/null
+++ b/drivers/staging/lustre/Kconfig
@@ -0,0 +1,3 @@
+source "drivers/staging/lustre/lustre/Kconfig"
+
+source "drivers/staging/lustre/lnet/Kconfig"
diff --git a/drivers/staging/lustre/Makefile b/drivers/staging/lustre/Makefile
new file mode 100644
index 000000000..95ffe337a
--- /dev/null
+++ b/drivers/staging/lustre/Makefile
@@ -0,0 +1,2 @@
+obj-$(CONFIG_LNET) += lnet/
+obj-$(CONFIG_LUSTRE_FS) += lustre/
diff --git a/drivers/staging/lustre/README.txt b/drivers/staging/lustre/README.txt
new file mode 100644
index 000000000..cf0ca50ff
--- /dev/null
+++ b/drivers/staging/lustre/README.txt
@@ -0,0 +1,87 @@
+Lustre Parallel Filesystem Client
+=================================
+
+The Lustre file system is an open-source, parallel file system
+that supports many requirements of leadership class HPC simulation
+environments.
+Born from from a research project at Carnegie Mellon University,
+the Lustre file system is a widely-used option in HPC.
+The Lustre file system provides a POSIX compliant file system interface,
+can scale to thousands of clients, petabytes of storage and
+hundreds of gigabytes per second of I/O bandwidth.
+
+Unlike shared disk storage cluster filesystems (e.g. OCFS2, GFS, GPFS),
+Lustre has independent Metadata and Data servers that clients can access
+in parallel to maximize performance.
+
+In order to use Lustre client you will need to download lustre client
+tools from
+https://downloads.hpdd.intel.com/public/lustre/latest-feature-release/
+the package name is lustre-client.
+
+You will need to install and configure your Lustre servers separately.
+
+Mount Syntax
+============
+After you installed the lustre-client tools including mount.lustre binary
+you can mount your Lustre filesystem with:
+
+mount -t lustre mgs:/fsname mnt
+
+where mgs is the host name or ip address of your Lustre MGS(management service)
+fsname is the name of the filesystem you would like to mount.
+
+
+Mount Options
+=============
+
+ noflock
+ Disable posix file locking (Applications trying to use
+ the functionality will get ENOSYS)
+
+ localflock
+ Enable local flock support, using only client-local flock
+ (faster, for applications that require flock but do not run
+ on multiple nodes).
+
+ flock
+ Enable cluster-global posix file locking coherent across all
+ client nodes.
+
+ user_xattr, nouser_xattr
+ Support "user." extended attributes (or not)
+
+ user_fid2path, nouser_fid2path
+ Enable FID to path translation by regular users (or not)
+
+ checksum, nochecksum
+ Verify data consistency on the wire and in memory as it passes
+ between the layers (or not).
+
+ lruresize, nolruresize
+ Allow lock LRU to be controlled by memory pressure on the server
+ (or only 100 (default, controlled by lru_size proc parameter) locks
+ per CPU per server on this client).
+
+ lazystatfs, nolazystatfs
+ Do not block in statfs() if some of the servers are down.
+
+ 32bitapi
+ Shrink inode numbers to fit into 32 bits. This is necessary
+ if you plan to reexport Lustre filesystem from this client via
+ NFSv4.
+
+ verbose, noverbose
+ Enable mount/umount console messages (or not)
+
+More Information
+================
+You can get more information at
+OpenSFS website: http://lustre.opensfs.org/about/
+Intel HPDD wiki: https://wiki.hpdd.intel.com
+
+Out of tree Lustre client and server code is available at:
+http://git.whamcloud.com/fs/lustre-release.git
+
+Latest binary packages:
+http://lustre.opensfs.org/download-lustre/
diff --git a/drivers/staging/lustre/TODO b/drivers/staging/lustre/TODO
new file mode 100644
index 000000000..0512594b5
--- /dev/null
+++ b/drivers/staging/lustre/TODO
@@ -0,0 +1,12 @@
+* Possible remaining coding style fix.
+* Remove deadcode.
+* Seperate client/server functionality. Functions only used by server can be
+ removed from client.
+* Clean up libcfs layer. Ideally we can remove include/linux/libcfs entirely.
+* Clean up CLIO layer. Lustre client readahead/writeback control needs to better
+ suit kernel providings.
+* Add documents in Documentation.
+* Other minor misc cleanups...
+
+Please send any patches to Greg Kroah-Hartman <greg@kroah.com>, Andreas Dilger
+<andreas.dilger@intel.com>, and Oleg Drokin <oleg.drokin@intel.com>.
diff --git a/drivers/staging/lustre/include/linux/libcfs/curproc.h b/drivers/staging/lustre/include/linux/libcfs/curproc.h
new file mode 100644
index 000000000..1edfca58c
--- /dev/null
+++ b/drivers/staging/lustre/include/linux/libcfs/curproc.h
@@ -0,0 +1,97 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2011, 2012, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ *
+ * libcfs/include/libcfs/curproc.h
+ *
+ * Lustre curproc API declaration
+ *
+ * Author: Nikita Danilov <nikita@clusterfs.com>
+ */
+
+#ifndef __LIBCFS_CURPROC_H__
+#define __LIBCFS_CURPROC_H__
+
+/*
+ * Plus, platform-specific constant
+ *
+ * CFS_CURPROC_COMM_MAX,
+ *
+ * and opaque scalar type
+ *
+ * kernel_cap_t
+ */
+
+/* check if task is running in compat mode.*/
+#define current_pid() (current->pid)
+#define current_comm() (current->comm)
+
+typedef __u32 cfs_cap_t;
+
+#define CFS_CAP_CHOWN 0
+#define CFS_CAP_DAC_OVERRIDE 1
+#define CFS_CAP_DAC_READ_SEARCH 2
+#define CFS_CAP_FOWNER 3
+#define CFS_CAP_FSETID 4
+#define CFS_CAP_LINUX_IMMUTABLE 9
+#define CFS_CAP_SYS_ADMIN 21
+#define CFS_CAP_SYS_BOOT 23
+#define CFS_CAP_SYS_RESOURCE 24
+
+#define CFS_CAP_FS_MASK ((1 << CFS_CAP_CHOWN) | \
+ (1 << CFS_CAP_DAC_OVERRIDE) | \
+ (1 << CFS_CAP_DAC_READ_SEARCH) | \
+ (1 << CFS_CAP_FOWNER) | \
+ (1 << CFS_CAP_FSETID) | \
+ (1 << CFS_CAP_LINUX_IMMUTABLE) | \
+ (1 << CFS_CAP_SYS_ADMIN) | \
+ (1 << CFS_CAP_SYS_BOOT) | \
+ (1 << CFS_CAP_SYS_RESOURCE))
+
+void cfs_cap_raise(cfs_cap_t cap);
+void cfs_cap_lower(cfs_cap_t cap);
+int cfs_cap_raised(cfs_cap_t cap);
+cfs_cap_t cfs_curproc_cap_pack(void);
+
+/* __LIBCFS_CURPROC_H__ */
+#endif
+/*
+ * Local variables:
+ * c-indentation-style: "K&R"
+ * c-basic-offset: 8
+ * tab-width: 8
+ * fill-column: 80
+ * scroll-step: 1
+ * End:
+ */
diff --git a/drivers/staging/lustre/include/linux/libcfs/libcfs.h b/drivers/staging/lustre/include/linux/libcfs/libcfs.h
new file mode 100644
index 000000000..4410d7fdc
--- /dev/null
+++ b/drivers/staging/lustre/include/linux/libcfs/libcfs.h
@@ -0,0 +1,187 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2011, 2012, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ */
+
+#ifndef __LIBCFS_LIBCFS_H__
+#define __LIBCFS_LIBCFS_H__
+
+#if !__GNUC__
+#define __attribute__(x)
+#endif
+
+#include "linux/libcfs.h"
+#include <linux/gfp.h>
+
+#include "curproc.h"
+
+#ifndef offsetof
+# define offsetof(typ, memb) ((long)(long_ptr_t)((char *)&(((typ *)0)->memb)))
+#endif
+
+#ifndef ARRAY_SIZE
+#define ARRAY_SIZE(a) ((sizeof(a)) / (sizeof((a)[0])))
+#endif
+
+#if !defined(swap)
+#define swap(x, y) do { typeof(x) z = x; x = y; y = z; } while (0)
+#endif
+
+#if !defined(container_of)
+/* given a pointer @ptr to the field @member embedded into type (usually
+ * struct) @type, return pointer to the embedding instance of @type. */
+#define container_of(ptr, type, member) \
+ ((type *)((char *)(ptr)-(char *)(&((type *)0)->member)))
+#endif
+
+static inline int __is_po2(unsigned long long val)
+{
+ return !(val & (val - 1));
+}
+
+#define IS_PO2(val) __is_po2((unsigned long long)(val))
+
+#define LOWEST_BIT_SET(x) ((x) & ~((x) - 1))
+
+/*
+ * Lustre Error Checksum: calculates checksum
+ * of Hex number by XORing each bit.
+ */
+#define LERRCHKSUM(hexnum) (((hexnum) & 0xf) ^ ((hexnum) >> 4 & 0xf) ^ \
+ ((hexnum) >> 8 & 0xf))
+
+#define LUSTRE_SRV_LNET_PID LUSTRE_LNET_PID
+
+#include <linux/list.h>
+
+int libcfs_arch_init(void);
+void libcfs_arch_cleanup(void);
+
+/* libcfs tcpip */
+int libcfs_ipif_query(char *name, int *up, __u32 *ip, __u32 *mask);
+int libcfs_ipif_enumerate(char ***names);
+void libcfs_ipif_free_enumeration(char **names, int n);
+int libcfs_sock_listen(struct socket **sockp, __u32 ip, int port, int backlog);
+int libcfs_sock_accept(struct socket **newsockp, struct socket *sock);
+void libcfs_sock_abort_accept(struct socket *sock);
+int libcfs_sock_connect(struct socket **sockp, int *fatal,
+ __u32 local_ip, int local_port,
+ __u32 peer_ip, int peer_port);
+int libcfs_sock_setbuf(struct socket *socket, int txbufsize, int rxbufsize);
+int libcfs_sock_getbuf(struct socket *socket, int *txbufsize, int *rxbufsize);
+int libcfs_sock_getaddr(struct socket *socket, int remote, __u32 *ip, int *port);
+int libcfs_sock_write(struct socket *sock, void *buffer, int nob, int timeout);
+int libcfs_sock_read(struct socket *sock, void *buffer, int nob, int timeout);
+void libcfs_sock_release(struct socket *sock);
+
+/* need both kernel and user-land acceptor */
+#define LNET_ACCEPTOR_MIN_RESERVED_PORT 512
+#define LNET_ACCEPTOR_MAX_RESERVED_PORT 1023
+
+/*
+ * libcfs pseudo device operations
+ *
+ * It's just draft now.
+ */
+
+struct cfs_psdev_file {
+ unsigned long off;
+ void *private_data;
+ unsigned long reserved1;
+ unsigned long reserved2;
+};
+
+struct cfs_psdev_ops {
+ int (*p_open)(unsigned long, void *);
+ int (*p_close)(unsigned long, void *);
+ int (*p_read)(struct cfs_psdev_file *, char *, unsigned long);
+ int (*p_write)(struct cfs_psdev_file *, char *, unsigned long);
+ int (*p_ioctl)(struct cfs_psdev_file *, unsigned long, void *);
+};
+
+/*
+ * Drop into debugger, if possible. Implementation is provided by platform.
+ */
+
+void cfs_enter_debugger(void);
+
+/*
+ * Defined by platform
+ */
+int unshare_fs_struct(void);
+sigset_t cfs_get_blocked_sigs(void);
+sigset_t cfs_block_allsigs(void);
+sigset_t cfs_block_sigs(unsigned long sigs);
+sigset_t cfs_block_sigsinv(unsigned long sigs);
+void cfs_restore_sigs(sigset_t);
+int cfs_signal_pending(void);
+void cfs_clear_sigpending(void);
+
+/*
+ * Random number handling
+ */
+
+/* returns a random 32-bit integer */
+unsigned int cfs_rand(void);
+/* seed the generator */
+void cfs_srand(unsigned int, unsigned int);
+void cfs_get_random_bytes(void *buf, int size);
+
+#include "libcfs_debug.h"
+#include "libcfs_cpu.h"
+#include "libcfs_private.h"
+#include "libcfs_ioctl.h"
+#include "libcfs_prim.h"
+#include "libcfs_time.h"
+#include "libcfs_string.h"
+#include "libcfs_kernelcomm.h"
+#include "libcfs_workitem.h"
+#include "libcfs_hash.h"
+#include "libcfs_fail.h"
+#include "libcfs_crypto.h"
+
+/* container_of depends on "likely" which is defined in libcfs_private.h */
+static inline void *__container_of(void *ptr, unsigned long shift)
+{
+ if (unlikely(IS_ERR(ptr) || ptr == NULL))
+ return ptr;
+ return (char *)ptr - shift;
+}
+
+#define container_of0(ptr, type, member) \
+ ((type *)__container_of((void *)(ptr), offsetof(type, member)))
+
+#define _LIBCFS_H
+
+#endif /* _LIBCFS_H */
diff --git a/drivers/staging/lustre/include/linux/libcfs/libcfs_cpu.h b/drivers/staging/lustre/include/linux/libcfs/libcfs_cpu.h
new file mode 100644
index 000000000..787867847
--- /dev/null
+++ b/drivers/staging/lustre/include/linux/libcfs/libcfs_cpu.h
@@ -0,0 +1,219 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 021110-1307, USA
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ *
+ * libcfs/include/libcfs/libcfs_cpu.h
+ *
+ * CPU partition
+ * . CPU partition is virtual processing unit
+ *
+ * . CPU partition can present 1-N cores, or 1-N NUMA nodes,
+ * in other words, CPU partition is a processors pool.
+ *
+ * CPU Partition Table (CPT)
+ * . a set of CPU partitions
+ *
+ * . There are two modes for CPT: CFS_CPU_MODE_NUMA and CFS_CPU_MODE_SMP
+ *
+ * . User can specify total number of CPU partitions while creating a
+ * CPT, ID of CPU partition is always start from 0.
+ *
+ * Example: if there are 8 cores on the system, while creating a CPT
+ * with cpu_npartitions=4:
+ * core[0, 1] = partition[0], core[2, 3] = partition[1]
+ * core[4, 5] = partition[2], core[6, 7] = partition[3]
+ *
+ * cpu_npartitions=1:
+ * core[0, 1, ... 7] = partition[0]
+ *
+ * . User can also specify CPU partitions by string pattern
+ *
+ * Examples: cpu_partitions="0[0,1], 1[2,3]"
+ * cpu_partitions="N 0[0-3], 1[4-8]"
+ *
+ * The first character "N" means following numbers are numa ID
+ *
+ * . NUMA allocators, CPU affinity threads are built over CPU partitions,
+ * instead of HW CPUs or HW nodes.
+ *
+ * . By default, Lustre modules should refer to the global cfs_cpt_table,
+ * instead of accessing HW CPUs directly, so concurrency of Lustre can be
+ * configured by cpu_npartitions of the global cfs_cpt_table
+ *
+ * . If cpu_npartitions=1(all CPUs in one pool), lustre should work the
+ * same way as 2.2 or earlier versions
+ *
+ * Author: liang@whamcloud.com
+ */
+
+#ifndef __LIBCFS_CPU_H__
+#define __LIBCFS_CPU_H__
+
+/* any CPU partition */
+#define CFS_CPT_ANY (-1)
+
+#ifdef CONFIG_SMP
+/**
+ * return cpumask of CPU partition \a cpt
+ */
+cpumask_t *cfs_cpt_cpumask(struct cfs_cpt_table *cptab, int cpt);
+/**
+ * print string information of cpt-table
+ */
+int cfs_cpt_table_print(struct cfs_cpt_table *cptab, char *buf, int len);
+#else /* !CONFIG_SMP */
+struct cfs_cpt_table {
+ /* # of CPU partitions */
+ int ctb_nparts;
+ /* cpu mask */
+ cpumask_t ctb_mask;
+ /* node mask */
+ nodemask_t ctb_nodemask;
+ /* version */
+ __u64 ctb_version;
+};
+
+static inline cpumask_t *
+cfs_cpt_cpumask(struct cfs_cpt_table *cptab, int cpt)
+{
+ return NULL;
+}
+
+static inline int
+cfs_cpt_table_print(struct cfs_cpt_table *cptab, char *buf, int len)
+{
+ return 0;
+}
+#endif /* CONFIG_SMP */
+
+extern struct cfs_cpt_table *cfs_cpt_table;
+
+/**
+ * destroy a CPU partition table
+ */
+void cfs_cpt_table_free(struct cfs_cpt_table *cptab);
+/**
+ * create a cfs_cpt_table with \a ncpt number of partitions
+ */
+struct cfs_cpt_table *cfs_cpt_table_alloc(unsigned int ncpt);
+/**
+ * return total number of CPU partitions in \a cptab
+ */
+int
+cfs_cpt_number(struct cfs_cpt_table *cptab);
+/**
+ * return number of HW cores or hyper-threadings in a CPU partition \a cpt
+ */
+int cfs_cpt_weight(struct cfs_cpt_table *cptab, int cpt);
+/**
+ * is there any online CPU in CPU partition \a cpt
+ */
+int cfs_cpt_online(struct cfs_cpt_table *cptab, int cpt);
+/**
+ * return nodemask of CPU partition \a cpt
+ */
+nodemask_t *cfs_cpt_nodemask(struct cfs_cpt_table *cptab, int cpt);
+/**
+ * shadow current HW processor ID to CPU-partition ID of \a cptab
+ */
+int cfs_cpt_current(struct cfs_cpt_table *cptab, int remap);
+/**
+ * shadow HW processor ID \a CPU to CPU-partition ID by \a cptab
+ */
+int cfs_cpt_of_cpu(struct cfs_cpt_table *cptab, int cpu);
+/**
+ * bind current thread on a CPU-partition \a cpt of \a cptab
+ */
+int cfs_cpt_bind(struct cfs_cpt_table *cptab, int cpt);
+/**
+ * add \a cpu to CPU partition @cpt of \a cptab, return 1 for success,
+ * otherwise 0 is returned
+ */
+int cfs_cpt_set_cpu(struct cfs_cpt_table *cptab, int cpt, int cpu);
+/**
+ * remove \a cpu from CPU partition \a cpt of \a cptab
+ */
+void cfs_cpt_unset_cpu(struct cfs_cpt_table *cptab, int cpt, int cpu);
+/**
+ * add all cpus in \a mask to CPU partition \a cpt
+ * return 1 if successfully set all CPUs, otherwise return 0
+ */
+int cfs_cpt_set_cpumask(struct cfs_cpt_table *cptab,
+ int cpt, cpumask_t *mask);
+/**
+ * remove all cpus in \a mask from CPU partition \a cpt
+ */
+void cfs_cpt_unset_cpumask(struct cfs_cpt_table *cptab,
+ int cpt, cpumask_t *mask);
+/**
+ * add all cpus in NUMA node \a node to CPU partition \a cpt
+ * return 1 if successfully set all CPUs, otherwise return 0
+ */
+int cfs_cpt_set_node(struct cfs_cpt_table *cptab, int cpt, int node);
+/**
+ * remove all cpus in NUMA node \a node from CPU partition \a cpt
+ */
+void cfs_cpt_unset_node(struct cfs_cpt_table *cptab, int cpt, int node);
+
+/**
+ * add all cpus in node mask \a mask to CPU partition \a cpt
+ * return 1 if successfully set all CPUs, otherwise return 0
+ */
+int cfs_cpt_set_nodemask(struct cfs_cpt_table *cptab,
+ int cpt, nodemask_t *mask);
+/**
+ * remove all cpus in node mask \a mask from CPU partition \a cpt
+ */
+void cfs_cpt_unset_nodemask(struct cfs_cpt_table *cptab,
+ int cpt, nodemask_t *mask);
+/**
+ * unset all cpus for CPU partition \a cpt
+ */
+void cfs_cpt_clear(struct cfs_cpt_table *cptab, int cpt);
+/**
+ * convert partition id \a cpt to numa node id, if there are more than one
+ * nodes in this partition, it might return a different node id each time.
+ */
+int cfs_cpt_spread_node(struct cfs_cpt_table *cptab, int cpt);
+
+/**
+ * return number of HTs in the same core of \a cpu
+ */
+int cfs_cpu_ht_nsiblings(int cpu);
+
+/**
+ * iterate over all CPU partitions in \a cptab
+ */
+#define cfs_cpt_for_each(i, cptab) \
+ for (i = 0; i < cfs_cpt_number(cptab); i++)
+
+int cfs_cpu_init(void);
+void cfs_cpu_fini(void);
+
+#endif /* __LIBCFS_CPU_H__ */
diff --git a/drivers/staging/lustre/include/linux/libcfs/libcfs_crypto.h b/drivers/staging/lustre/include/linux/libcfs/libcfs_crypto.h
new file mode 100644
index 000000000..e8663697e
--- /dev/null
+++ b/drivers/staging/lustre/include/linux/libcfs/libcfs_crypto.h
@@ -0,0 +1,199 @@
+/* GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see http://www.gnu.org/licenses
+ *
+ * Please visit http://www.xyratex.com/contact if you need additional
+ * information or have any questions.
+ *
+ * GPL HEADER END
+ */
+
+/*
+ * Copyright 2012 Xyratex Technology Limited
+ */
+
+#ifndef _LIBCFS_CRYPTO_H
+#define _LIBCFS_CRYPTO_H
+
+struct cfs_crypto_hash_type {
+ char *cht_name; /**< hash algorithm name, equal to
+ * format name for crypto api */
+ unsigned int cht_key; /**< init key by default (valid for
+ * 4 bytes context like crc32, adler */
+ unsigned int cht_size; /**< hash digest size */
+};
+
+enum cfs_crypto_hash_alg {
+ CFS_HASH_ALG_NULL = 0,
+ CFS_HASH_ALG_ADLER32,
+ CFS_HASH_ALG_CRC32,
+ CFS_HASH_ALG_MD5,
+ CFS_HASH_ALG_SHA1,
+ CFS_HASH_ALG_SHA256,
+ CFS_HASH_ALG_SHA384,
+ CFS_HASH_ALG_SHA512,
+ CFS_HASH_ALG_CRC32C,
+ CFS_HASH_ALG_MAX
+};
+
+static struct cfs_crypto_hash_type hash_types[] = {
+ [CFS_HASH_ALG_NULL] = { "null", 0, 0 },
+ [CFS_HASH_ALG_ADLER32] = { "adler32", 1, 4 },
+ [CFS_HASH_ALG_CRC32] = { "crc32", ~0, 4 },
+ [CFS_HASH_ALG_CRC32C] = { "crc32c", ~0, 4 },
+ [CFS_HASH_ALG_MD5] = { "md5", 0, 16 },
+ [CFS_HASH_ALG_SHA1] = { "sha1", 0, 20 },
+ [CFS_HASH_ALG_SHA256] = { "sha256", 0, 32 },
+ [CFS_HASH_ALG_SHA384] = { "sha384", 0, 48 },
+ [CFS_HASH_ALG_SHA512] = { "sha512", 0, 64 },
+};
+
+/** Return pointer to type of hash for valid hash algorithm identifier */
+static inline const struct cfs_crypto_hash_type *
+ cfs_crypto_hash_type(unsigned char hash_alg)
+{
+ struct cfs_crypto_hash_type *ht;
+
+ if (hash_alg < CFS_HASH_ALG_MAX) {
+ ht = &hash_types[hash_alg];
+ if (ht->cht_name)
+ return ht;
+ }
+ return NULL;
+}
+
+/** Return hash name for valid hash algorithm identifier or "unknown" */
+static inline const char *cfs_crypto_hash_name(unsigned char hash_alg)
+{
+ const struct cfs_crypto_hash_type *ht;
+
+ ht = cfs_crypto_hash_type(hash_alg);
+ if (ht)
+ return ht->cht_name;
+ return "unknown";
+}
+
+/** Return digest size for valid algorithm identifier or 0 */
+static inline int cfs_crypto_hash_digestsize(unsigned char hash_alg)
+{
+ const struct cfs_crypto_hash_type *ht;
+
+ ht = cfs_crypto_hash_type(hash_alg);
+ if (ht)
+ return ht->cht_size;
+ return 0;
+}
+
+/** Return hash identifier for valid hash algorithm name or 0xFF */
+static inline unsigned char cfs_crypto_hash_alg(const char *algname)
+{
+ unsigned char i;
+
+ for (i = 0; i < CFS_HASH_ALG_MAX; i++)
+ if (!strcmp(hash_types[i].cht_name, algname))
+ break;
+ return (i == CFS_HASH_ALG_MAX ? 0xFF : i);
+}
+
+/** Calculate hash digest for buffer.
+ * @param alg id of hash algorithm
+ * @param buf buffer of data
+ * @param buf_len buffer len
+ * @param key initial value for algorithm, if it is NULL,
+ * default initial value should be used.
+ * @param key_len len of initial value
+ * @param hash [out] pointer to hash, if it is NULL, hash_len is
+ * set to valid digest size in bytes, retval -ENOSPC.
+ * @param hash_len [in,out] size of hash buffer
+ * @returns status of operation
+ * @retval -EINVAL if buf, buf_len, hash_len or alg_id is invalid
+ * @retval -ENODEV if this algorithm is unsupported
+ * @retval -ENOSPC if pointer to hash is NULL, or hash_len less than
+ * digest size
+ * @retval 0 for success
+ * @retval < 0 other errors from lower layers.
+ */
+int cfs_crypto_hash_digest(unsigned char alg,
+ const void *buf, unsigned int buf_len,
+ unsigned char *key, unsigned int key_len,
+ unsigned char *hash, unsigned int *hash_len);
+
+/* cfs crypto hash descriptor */
+struct cfs_crypto_hash_desc;
+
+/** Allocate and initialize descriptor for hash algorithm.
+ * @param alg algorithm id
+ * @param key initial value for algorithm, if it is NULL,
+ * default initial value should be used.
+ * @param key_len len of initial value
+ * @returns pointer to descriptor of hash instance
+ * @retval ERR_PTR(error) when errors occurred.
+ */
+struct cfs_crypto_hash_desc*
+ cfs_crypto_hash_init(unsigned char alg,
+ unsigned char *key, unsigned int key_len);
+
+/** Update digest by part of data.
+ * @param desc hash descriptor
+ * @param page data page
+ * @param offset data offset
+ * @param len data len
+ * @returns status of operation
+ * @retval 0 for success.
+ */
+int cfs_crypto_hash_update_page(struct cfs_crypto_hash_desc *desc,
+ struct page *page, unsigned int offset,
+ unsigned int len);
+
+/** Update digest by part of data.
+ * @param desc hash descriptor
+ * @param buf pointer to data buffer
+ * @param buf_len size of data at buffer
+ * @returns status of operation
+ * @retval 0 for success.
+ */
+int cfs_crypto_hash_update(struct cfs_crypto_hash_desc *desc, const void *buf,
+ unsigned int buf_len);
+
+/** Finalize hash calculation, copy hash digest to buffer, destroy hash
+ * descriptor.
+ * @param desc hash descriptor
+ * @param hash buffer pointer to store hash digest
+ * @param hash_len pointer to hash buffer size, if NULL
+ * destroy hash descriptor
+ * @returns status of operation
+ * @retval -ENOSPC if hash is NULL, or *hash_len less than
+ * digest size
+ * @retval 0 for success
+ * @retval < 0 other errors from lower layers.
+ */
+int cfs_crypto_hash_final(struct cfs_crypto_hash_desc *desc,
+ unsigned char *hash, unsigned int *hash_len);
+/**
+ * Register crypto hash algorithms
+ */
+int cfs_crypto_register(void);
+
+/**
+ * Unregister
+ */
+void cfs_crypto_unregister(void);
+
+/** Return hash speed in Mbytes per second for valid hash algorithm
+ * identifier. If test was unsuccessful -1 would be returned.
+ */
+int cfs_crypto_hash_speed(unsigned char hash_alg);
+#endif
diff --git a/drivers/staging/lustre/include/linux/libcfs/libcfs_debug.h b/drivers/staging/lustre/include/linux/libcfs/libcfs_debug.h
new file mode 100644
index 000000000..8251ac932
--- /dev/null
+++ b/drivers/staging/lustre/include/linux/libcfs/libcfs_debug.h
@@ -0,0 +1,262 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2012, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ *
+ * libcfs/include/libcfs/libcfs_debug.h
+ *
+ * Debug messages and assertions
+ *
+ */
+
+#ifndef __LIBCFS_DEBUG_H__
+#define __LIBCFS_DEBUG_H__
+
+/*
+ * Debugging
+ */
+extern unsigned int libcfs_subsystem_debug;
+extern unsigned int libcfs_stack;
+extern unsigned int libcfs_debug;
+extern unsigned int libcfs_printk;
+extern unsigned int libcfs_console_ratelimit;
+extern unsigned int libcfs_watchdog_ratelimit;
+extern unsigned int libcfs_console_max_delay;
+extern unsigned int libcfs_console_min_delay;
+extern unsigned int libcfs_console_backoff;
+extern unsigned int libcfs_debug_binary;
+extern char libcfs_debug_file_path_arr[PATH_MAX];
+
+int libcfs_debug_mask2str(char *str, int size, int mask, int is_subsys);
+int libcfs_debug_str2mask(int *mask, const char *str, int is_subsys);
+
+/* Has there been an LBUG? */
+extern unsigned int libcfs_catastrophe;
+extern unsigned int libcfs_panic_on_lbug;
+
+/**
+ * Format for debug message headers
+ */
+struct ptldebug_header {
+ __u32 ph_len;
+ __u32 ph_flags;
+ __u32 ph_subsys;
+ __u32 ph_mask;
+ __u16 ph_cpu_id;
+ __u16 ph_type;
+ __u32 ph_sec;
+ __u64 ph_usec;
+ __u32 ph_stack;
+ __u32 ph_pid;
+ __u32 ph_extern_pid;
+ __u32 ph_line_num;
+} __packed;
+
+#define PH_FLAG_FIRST_RECORD 1
+
+/* Debugging subsystems (32 bits, non-overlapping) */
+/* keep these in sync with lnet/utils/debug.c and lnet/libcfs/debug.c */
+#define S_UNDEFINED 0x00000001
+#define S_MDC 0x00000002
+#define S_MDS 0x00000004
+#define S_OSC 0x00000008
+#define S_OST 0x00000010
+#define S_CLASS 0x00000020
+#define S_LOG 0x00000040
+#define S_LLITE 0x00000080
+#define S_RPC 0x00000100
+#define S_MGMT 0x00000200
+#define S_LNET 0x00000400
+#define S_LND 0x00000800 /* ALL LNDs */
+#define S_PINGER 0x00001000
+#define S_FILTER 0x00002000
+/* unused */
+#define S_ECHO 0x00008000
+#define S_LDLM 0x00010000
+#define S_LOV 0x00020000
+#define S_LQUOTA 0x00040000
+#define S_OSD 0x00080000
+/* unused */
+/* unused */
+/* unused */
+#define S_LMV 0x00800000 /* b_new_cmd */
+/* unused */
+#define S_SEC 0x02000000 /* upcall cache */
+#define S_GSS 0x04000000 /* b_new_cmd */
+/* unused */
+#define S_MGC 0x10000000
+#define S_MGS 0x20000000
+#define S_FID 0x40000000 /* b_new_cmd */
+#define S_FLD 0x80000000 /* b_new_cmd */
+/* keep these in sync with lnet/utils/debug.c and lnet/libcfs/debug.c */
+
+/* Debugging masks (32 bits, non-overlapping) */
+/* keep these in sync with lnet/utils/debug.c and lnet/libcfs/debug.c */
+#define D_TRACE 0x00000001 /* ENTRY/EXIT markers */
+#define D_INODE 0x00000002
+#define D_SUPER 0x00000004
+#define D_EXT2 0x00000008 /* anything from ext2_debug */
+#define D_MALLOC 0x00000010 /* print malloc, free information */
+#define D_CACHE 0x00000020 /* cache-related items */
+#define D_INFO 0x00000040 /* general information */
+#define D_IOCTL 0x00000080 /* ioctl related information */
+#define D_NETERROR 0x00000100 /* network errors */
+#define D_NET 0x00000200 /* network communications */
+#define D_WARNING 0x00000400 /* CWARN(...) == CDEBUG (D_WARNING, ...) */
+#define D_BUFFS 0x00000800
+#define D_OTHER 0x00001000
+#define D_DENTRY 0x00002000
+#define D_NETTRACE 0x00004000
+#define D_PAGE 0x00008000 /* bulk page handling */
+#define D_DLMTRACE 0x00010000
+#define D_ERROR 0x00020000 /* CERROR(...) == CDEBUG (D_ERROR, ...) */
+#define D_EMERG 0x00040000 /* CEMERG(...) == CDEBUG (D_EMERG, ...) */
+#define D_HA 0x00080000 /* recovery and failover */
+#define D_RPCTRACE 0x00100000 /* for distributed debugging */
+#define D_VFSTRACE 0x00200000
+#define D_READA 0x00400000 /* read-ahead */
+#define D_MMAP 0x00800000
+#define D_CONFIG 0x01000000
+#define D_CONSOLE 0x02000000
+#define D_QUOTA 0x04000000
+#define D_SEC 0x08000000
+#define D_LFSCK 0x10000000 /* For both OI scrub and LFSCK */
+/* keep these in sync with lnet/{utils,libcfs}/debug.c */
+
+#define D_HSM D_TRACE
+
+#define D_CANTMASK (D_ERROR | D_EMERG | D_WARNING | D_CONSOLE)
+
+#ifndef DEBUG_SUBSYSTEM
+# define DEBUG_SUBSYSTEM S_UNDEFINED
+#endif
+
+#define CDEBUG_DEFAULT_MAX_DELAY (cfs_time_seconds(600)) /* jiffies */
+#define CDEBUG_DEFAULT_MIN_DELAY ((cfs_time_seconds(1) + 1) / 2) /* jiffies */
+#define CDEBUG_DEFAULT_BACKOFF 2
+struct cfs_debug_limit_state {
+ unsigned long cdls_next;
+ unsigned int cdls_delay;
+ int cdls_count;
+};
+
+struct libcfs_debug_msg_data {
+ const char *msg_file;
+ const char *msg_fn;
+ int msg_subsys;
+ int msg_line;
+ int msg_mask;
+ struct cfs_debug_limit_state *msg_cdls;
+};
+
+#define LIBCFS_DEBUG_MSG_DATA_INIT(data, mask, cdls) \
+do { \
+ (data)->msg_subsys = DEBUG_SUBSYSTEM; \
+ (data)->msg_file = __FILE__; \
+ (data)->msg_fn = __func__; \
+ (data)->msg_line = __LINE__; \
+ (data)->msg_cdls = (cdls); \
+ (data)->msg_mask = (mask); \
+} while (0)
+
+#define LIBCFS_DEBUG_MSG_DATA_DECL(dataname, mask, cdls) \
+ static struct libcfs_debug_msg_data dataname = { \
+ .msg_subsys = DEBUG_SUBSYSTEM, \
+ .msg_file = __FILE__, \
+ .msg_fn = __func__, \
+ .msg_line = __LINE__, \
+ .msg_cdls = (cdls) }; \
+ dataname.msg_mask = (mask)
+
+/**
+ * Filters out logging messages based on mask and subsystem.
+ */
+static inline int cfs_cdebug_show(unsigned int mask, unsigned int subsystem)
+{
+ return mask & D_CANTMASK ||
+ ((libcfs_debug & mask) && (libcfs_subsystem_debug & subsystem));
+}
+
+#define __CDEBUG(cdls, mask, format, ...) \
+do { \
+ static struct libcfs_debug_msg_data msgdata; \
+ \
+ CFS_CHECK_STACK(&msgdata, mask, cdls); \
+ \
+ if (cfs_cdebug_show(mask, DEBUG_SUBSYSTEM)) { \
+ LIBCFS_DEBUG_MSG_DATA_INIT(&msgdata, mask, cdls); \
+ libcfs_debug_msg(&msgdata, format, ## __VA_ARGS__); \
+ } \
+} while (0)
+
+#define CDEBUG(mask, format, ...) __CDEBUG(NULL, mask, format, ## __VA_ARGS__)
+
+#define CDEBUG_LIMIT(mask, format, ...) \
+do { \
+ static struct cfs_debug_limit_state cdls; \
+ \
+ __CDEBUG(&cdls, mask, format, ## __VA_ARGS__); \
+} while (0)
+
+#define CWARN(format, ...) CDEBUG_LIMIT(D_WARNING, format, ## __VA_ARGS__)
+#define CERROR(format, ...) CDEBUG_LIMIT(D_ERROR, format, ## __VA_ARGS__)
+#define CNETERR(format, a...) CDEBUG_LIMIT(D_NETERROR, format, ## a)
+#define CEMERG(format, ...) CDEBUG_LIMIT(D_EMERG, format, ## __VA_ARGS__)
+
+#define LCONSOLE(mask, format, ...) CDEBUG(D_CONSOLE | (mask), format, ## __VA_ARGS__)
+#define LCONSOLE_INFO(format, ...) CDEBUG_LIMIT(D_CONSOLE, format, ## __VA_ARGS__)
+#define LCONSOLE_WARN(format, ...) CDEBUG_LIMIT(D_CONSOLE | D_WARNING, format, ## __VA_ARGS__)
+#define LCONSOLE_ERROR_MSG(errnum, format, ...) CDEBUG_LIMIT(D_CONSOLE | D_ERROR, \
+ "%x-%x: " format, errnum, LERRCHKSUM(errnum), ## __VA_ARGS__)
+#define LCONSOLE_ERROR(format, ...) LCONSOLE_ERROR_MSG(0x00, format, ## __VA_ARGS__)
+
+#define LCONSOLE_EMERG(format, ...) CDEBUG(D_CONSOLE | D_EMERG, format, ## __VA_ARGS__)
+
+int libcfs_debug_msg(struct libcfs_debug_msg_data *msgdata,
+ const char *format1, ...)
+ __printf(2, 3);
+
+int libcfs_debug_vmsg2(struct libcfs_debug_msg_data *msgdata,
+ const char *format1,
+ va_list args, const char *format2, ...)
+ __printf(4, 5);
+
+/* other external symbols that tracefile provides: */
+int cfs_trace_copyin_string(char *knl_buffer, int knl_buffer_nob,
+ const char __user *usr_buffer, int usr_buffer_nob);
+int cfs_trace_copyout_string(char __user *usr_buffer, int usr_buffer_nob,
+ const char *knl_buffer, char *append);
+
+#define LIBCFS_DEBUG_FILE_PATH_DEFAULT "/tmp/lustre-log"
+
+#endif /* __LIBCFS_DEBUG_H__ */
diff --git a/drivers/staging/lustre/include/linux/libcfs/libcfs_fail.h b/drivers/staging/lustre/include/linux/libcfs/libcfs_fail.h
new file mode 100644
index 000000000..eea55d94e
--- /dev/null
+++ b/drivers/staging/lustre/include/linux/libcfs/libcfs_fail.h
@@ -0,0 +1,171 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see http://www.gnu.org/licenses
+ *
+ * Please contact Oracle Corporation, Inc., 500 Oracle Parkway, Redwood Shores,
+ * CA 94065 USA or visit www.oracle.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2011, 2012, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Oracle Corporation, Inc.
+ */
+
+#ifndef _LIBCFS_FAIL_H
+#define _LIBCFS_FAIL_H
+
+extern unsigned long cfs_fail_loc;
+extern unsigned int cfs_fail_val;
+
+extern wait_queue_head_t cfs_race_waitq;
+extern int cfs_race_state;
+
+int __cfs_fail_check_set(__u32 id, __u32 value, int set);
+int __cfs_fail_timeout_set(__u32 id, __u32 value, int ms, int set);
+
+enum {
+ CFS_FAIL_LOC_NOSET = 0,
+ CFS_FAIL_LOC_ORSET = 1,
+ CFS_FAIL_LOC_RESET = 2,
+ CFS_FAIL_LOC_VALUE = 3
+};
+
+/* Failure injection control */
+#define CFS_FAIL_MASK_SYS 0x0000FF00
+#define CFS_FAIL_MASK_LOC (0x000000FF | CFS_FAIL_MASK_SYS)
+
+#define CFS_FAILED_BIT 30
+/* CFS_FAILED is 0x40000000 */
+#define CFS_FAILED (1 << CFS_FAILED_BIT)
+
+#define CFS_FAIL_ONCE_BIT 31
+/* CFS_FAIL_ONCE is 0x80000000 */
+#define CFS_FAIL_ONCE (1 << CFS_FAIL_ONCE_BIT)
+
+/* The following flags aren't made to be combined */
+#define CFS_FAIL_SKIP 0x20000000 /* skip N times then fail */
+#define CFS_FAIL_SOME 0x10000000 /* only fail N times */
+#define CFS_FAIL_RAND 0x08000000 /* fail 1/N of the times */
+#define CFS_FAIL_USR1 0x04000000 /* user flag */
+
+#define CFS_FAIL_PRECHECK(id) (cfs_fail_loc && \
+ (cfs_fail_loc & CFS_FAIL_MASK_LOC) == \
+ ((id) & CFS_FAIL_MASK_LOC))
+
+static inline int cfs_fail_check_set(__u32 id, __u32 value,
+ int set, int quiet)
+{
+ int ret = 0;
+
+ if (unlikely(CFS_FAIL_PRECHECK(id) &&
+ (ret = __cfs_fail_check_set(id, value, set)))) {
+ if (quiet) {
+ CDEBUG(D_INFO, "*** cfs_fail_loc=%x, val=%u***\n",
+ id, value);
+ } else {
+ LCONSOLE_INFO("*** cfs_fail_loc=%x, val=%u***\n",
+ id, value);
+ }
+ }
+
+ return ret;
+}
+
+/* If id hit cfs_fail_loc, return 1, otherwise return 0 */
+#define CFS_FAIL_CHECK(id) \
+ cfs_fail_check_set(id, 0, CFS_FAIL_LOC_NOSET, 0)
+#define CFS_FAIL_CHECK_QUIET(id) \
+ cfs_fail_check_set(id, 0, CFS_FAIL_LOC_NOSET, 1)
+
+/* If id hit cfs_fail_loc and cfs_fail_val == (-1 or value) return 1,
+ * otherwise return 0 */
+#define CFS_FAIL_CHECK_VALUE(id, value) \
+ cfs_fail_check_set(id, value, CFS_FAIL_LOC_VALUE, 0)
+#define CFS_FAIL_CHECK_VALUE_QUIET(id, value) \
+ cfs_fail_check_set(id, value, CFS_FAIL_LOC_VALUE, 1)
+
+/* If id hit cfs_fail_loc, cfs_fail_loc |= value and return 1,
+ * otherwise return 0 */
+#define CFS_FAIL_CHECK_ORSET(id, value) \
+ cfs_fail_check_set(id, value, CFS_FAIL_LOC_ORSET, 0)
+#define CFS_FAIL_CHECK_ORSET_QUIET(id, value) \
+ cfs_fail_check_set(id, value, CFS_FAIL_LOC_ORSET, 1)
+
+/* If id hit cfs_fail_loc, cfs_fail_loc = value and return 1,
+ * otherwise return 0 */
+#define CFS_FAIL_CHECK_RESET(id, value) \
+ cfs_fail_check_set(id, value, CFS_FAIL_LOC_RESET, 0)
+#define CFS_FAIL_CHECK_RESET_QUIET(id, value) \
+ cfs_fail_check_set(id, value, CFS_FAIL_LOC_RESET, 1)
+
+static inline int cfs_fail_timeout_set(__u32 id, __u32 value, int ms, int set)
+{
+ if (unlikely(CFS_FAIL_PRECHECK(id)))
+ return __cfs_fail_timeout_set(id, value, ms, set);
+ return 0;
+}
+
+/* If id hit cfs_fail_loc, sleep for seconds or milliseconds */
+#define CFS_FAIL_TIMEOUT(id, secs) \
+ cfs_fail_timeout_set(id, 0, secs * 1000, CFS_FAIL_LOC_NOSET)
+
+#define CFS_FAIL_TIMEOUT_MS(id, ms) \
+ cfs_fail_timeout_set(id, 0, ms, CFS_FAIL_LOC_NOSET)
+
+/* If id hit cfs_fail_loc, cfs_fail_loc |= value and
+ * sleep seconds or milliseconds */
+#define CFS_FAIL_TIMEOUT_ORSET(id, value, secs) \
+ cfs_fail_timeout_set(id, value, secs * 1000, CFS_FAIL_LOC_ORSET)
+
+#define CFS_FAIL_TIMEOUT_MS_ORSET(id, value, ms) \
+ cfs_fail_timeout_set(id, value, ms, CFS_FAIL_LOC_ORSET)
+
+/* The idea here is to synchronise two threads to force a race. The
+ * first thread that calls this with a matching fail_loc is put to
+ * sleep. The next thread that calls with the same fail_loc wakes up
+ * the first and continues. */
+static inline void cfs_race(__u32 id)
+{
+
+ if (CFS_FAIL_PRECHECK(id)) {
+ if (unlikely(__cfs_fail_check_set(id, 0, CFS_FAIL_LOC_NOSET))) {
+ int rc;
+
+ cfs_race_state = 0;
+ CERROR("cfs_race id %x sleeping\n", id);
+ rc = wait_event_interruptible(cfs_race_waitq,
+ cfs_race_state != 0);
+ CERROR("cfs_fail_race id %x awake, rc=%d\n", id, rc);
+ } else {
+ CERROR("cfs_fail_race id %x waking\n", id);
+ cfs_race_state = 1;
+ wake_up(&cfs_race_waitq);
+ }
+ }
+}
+
+#define CFS_RACE(id) cfs_race(id)
+
+#endif /* _LIBCFS_FAIL_H */
diff --git a/drivers/staging/lustre/include/linux/libcfs/libcfs_hash.h b/drivers/staging/lustre/include/linux/libcfs/libcfs_hash.h
new file mode 100644
index 000000000..c40814591
--- /dev/null
+++ b/drivers/staging/lustre/include/linux/libcfs/libcfs_hash.h
@@ -0,0 +1,843 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2012, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ *
+ * libcfs/include/libcfs/libcfs_hash.h
+ *
+ * Hashing routines
+ *
+ */
+
+#ifndef __LIBCFS_HASH_H__
+#define __LIBCFS_HASH_H__
+/*
+ * Knuth recommends primes in approximately golden ratio to the maximum
+ * integer representable by a machine word for multiplicative hashing.
+ * Chuck Lever verified the effectiveness of this technique:
+ * http://www.citi.umich.edu/techreports/reports/citi-tr-00-1.pdf
+ *
+ * These primes are chosen to be bit-sparse, that is operations on
+ * them can use shifts and additions instead of multiplications for
+ * machines where multiplications are slow.
+ */
+/* 2^31 + 2^29 - 2^25 + 2^22 - 2^19 - 2^16 + 1 */
+#define CFS_GOLDEN_RATIO_PRIME_32 0x9e370001UL
+/* 2^63 + 2^61 - 2^57 + 2^54 - 2^51 - 2^18 + 1 */
+#define CFS_GOLDEN_RATIO_PRIME_64 0x9e37fffffffc0001ULL
+
+/*
+ * Ideally we would use HAVE_HASH_LONG for this, but on linux we configure
+ * the linux kernel and user space at the same time, so we need to differentiate
+ * between them explicitly. If this is not needed on other architectures, then
+ * we'll need to move the functions to architecture specific headers.
+ */
+
+#include <linux/hash.h>
+
+/** disable debug */
+#define CFS_HASH_DEBUG_NONE 0
+/** record hash depth and output to console when it's too deep,
+ * computing overhead is low but consume more memory */
+#define CFS_HASH_DEBUG_1 1
+/** expensive, check key validation */
+#define CFS_HASH_DEBUG_2 2
+
+#define CFS_HASH_DEBUG_LEVEL CFS_HASH_DEBUG_NONE
+
+struct cfs_hash_ops;
+struct cfs_hash_lock_ops;
+struct cfs_hash_hlist_ops;
+
+union cfs_hash_lock {
+ rwlock_t rw; /**< rwlock */
+ spinlock_t spin; /**< spinlock */
+};
+
+/**
+ * cfs_hash_bucket is a container of:
+ * - lock, counter ...
+ * - array of hash-head starting from hsb_head[0], hash-head can be one of
+ * . cfs_hash_head_t
+ * . cfs_hash_head_dep_t
+ * . cfs_hash_dhead_t
+ * . cfs_hash_dhead_dep_t
+ * which depends on requirement of user
+ * - some extra bytes (caller can require it while creating hash)
+ */
+struct cfs_hash_bucket {
+ union cfs_hash_lock hsb_lock; /**< bucket lock */
+ __u32 hsb_count; /**< current entries */
+ __u32 hsb_version; /**< change version */
+ unsigned int hsb_index; /**< index of bucket */
+ int hsb_depmax; /**< max depth on bucket */
+ long hsb_head[0]; /**< hash-head array */
+};
+
+/**
+ * cfs_hash bucket descriptor, it's normally in stack of caller
+ */
+struct cfs_hash_bd {
+ struct cfs_hash_bucket *bd_bucket; /**< address of bucket */
+ unsigned int bd_offset; /**< offset in bucket */
+};
+
+#define CFS_HASH_NAME_LEN 16 /**< default name length */
+#define CFS_HASH_BIGNAME_LEN 64 /**< bigname for param tree */
+
+#define CFS_HASH_BKT_BITS 3 /**< default bits of bucket */
+#define CFS_HASH_BITS_MAX 30 /**< max bits of bucket */
+#define CFS_HASH_BITS_MIN CFS_HASH_BKT_BITS
+
+/**
+ * common hash attributes.
+ */
+enum cfs_hash_tag {
+ /**
+ * don't need any lock, caller will protect operations with it's
+ * own lock. With this flag:
+ * . CFS_HASH_NO_BKTLOCK, CFS_HASH_RW_BKTLOCK, CFS_HASH_SPIN_BKTLOCK
+ * will be ignored.
+ * . Some functions will be disabled with this flag, i.e:
+ * cfs_hash_for_each_empty, cfs_hash_rehash
+ */
+ CFS_HASH_NO_LOCK = 1 << 0,
+ /** no bucket lock, use one spinlock to protect the whole hash */
+ CFS_HASH_NO_BKTLOCK = 1 << 1,
+ /** rwlock to protect bucket */
+ CFS_HASH_RW_BKTLOCK = 1 << 2,
+ /** spinlock to protect bucket */
+ CFS_HASH_SPIN_BKTLOCK = 1 << 3,
+ /** always add new item to tail */
+ CFS_HASH_ADD_TAIL = 1 << 4,
+ /** hash-table doesn't have refcount on item */
+ CFS_HASH_NO_ITEMREF = 1 << 5,
+ /** big name for param-tree */
+ CFS_HASH_BIGNAME = 1 << 6,
+ /** track global count */
+ CFS_HASH_COUNTER = 1 << 7,
+ /** rehash item by new key */
+ CFS_HASH_REHASH_KEY = 1 << 8,
+ /** Enable dynamic hash resizing */
+ CFS_HASH_REHASH = 1 << 9,
+ /** can shrink hash-size */
+ CFS_HASH_SHRINK = 1 << 10,
+ /** assert hash is empty on exit */
+ CFS_HASH_ASSERT_EMPTY = 1 << 11,
+ /** record hlist depth */
+ CFS_HASH_DEPTH = 1 << 12,
+ /**
+ * rehash is always scheduled in a different thread, so current
+ * change on hash table is non-blocking
+ */
+ CFS_HASH_NBLK_CHANGE = 1 << 13,
+ /** NB, we typed hs_flags as __u16, please change it
+ * if you need to extend >=16 flags */
+};
+
+/** most used attributes */
+#define CFS_HASH_DEFAULT (CFS_HASH_RW_BKTLOCK | \
+ CFS_HASH_COUNTER | CFS_HASH_REHASH)
+
+/**
+ * cfs_hash is a hash-table implementation for general purpose, it can support:
+ * . two refcount modes
+ * hash-table with & without refcount
+ * . four lock modes
+ * nolock, one-spinlock, rw-bucket-lock, spin-bucket-lock
+ * . general operations
+ * lookup, add(add_tail or add_head), delete
+ * . rehash
+ * grows or shrink
+ * . iteration
+ * locked iteration and unlocked iteration
+ * . bigname
+ * support long name hash
+ * . debug
+ * trace max searching depth
+ *
+ * Rehash:
+ * When the htable grows or shrinks, a separate task (cfs_hash_rehash_worker)
+ * is spawned to handle the rehash in the background, it's possible that other
+ * processes can concurrently perform additions, deletions, and lookups
+ * without being blocked on rehash completion, because rehash will release
+ * the global wrlock for each bucket.
+ *
+ * rehash and iteration can't run at the same time because it's too tricky
+ * to keep both of them safe and correct.
+ * As they are relatively rare operations, so:
+ * . if iteration is in progress while we try to launch rehash, then
+ * it just giveup, iterator will launch rehash at the end.
+ * . if rehash is in progress while we try to iterate the hash table,
+ * then we just wait (shouldn't be very long time), anyway, nobody
+ * should expect iteration of whole hash-table to be non-blocking.
+ *
+ * During rehashing, a (key,object) pair may be in one of two buckets,
+ * depending on whether the worker task has yet to transfer the object
+ * to its new location in the table. Lookups and deletions need to search both
+ * locations; additions must take care to only insert into the new bucket.
+ */
+
+struct cfs_hash {
+ /** serialize with rehash, or serialize all operations if
+ * the hash-table has CFS_HASH_NO_BKTLOCK */
+ union cfs_hash_lock hs_lock;
+ /** hash operations */
+ struct cfs_hash_ops *hs_ops;
+ /** hash lock operations */
+ struct cfs_hash_lock_ops *hs_lops;
+ /** hash list operations */
+ struct cfs_hash_hlist_ops *hs_hops;
+ /** hash buckets-table */
+ struct cfs_hash_bucket **hs_buckets;
+ /** total number of items on this hash-table */
+ atomic_t hs_count;
+ /** hash flags, see cfs_hash_tag for detail */
+ __u16 hs_flags;
+ /** # of extra-bytes for bucket, for user saving extended attributes */
+ __u16 hs_extra_bytes;
+ /** wants to iterate */
+ __u8 hs_iterating;
+ /** hash-table is dying */
+ __u8 hs_exiting;
+ /** current hash bits */
+ __u8 hs_cur_bits;
+ /** min hash bits */
+ __u8 hs_min_bits;
+ /** max hash bits */
+ __u8 hs_max_bits;
+ /** bits for rehash */
+ __u8 hs_rehash_bits;
+ /** bits for each bucket */
+ __u8 hs_bkt_bits;
+ /** resize min threshold */
+ __u16 hs_min_theta;
+ /** resize max threshold */
+ __u16 hs_max_theta;
+ /** resize count */
+ __u32 hs_rehash_count;
+ /** # of iterators (caller of cfs_hash_for_each_*) */
+ __u32 hs_iterators;
+ /** rehash workitem */
+ cfs_workitem_t hs_rehash_wi;
+ /** refcount on this hash table */
+ atomic_t hs_refcount;
+ /** rehash buckets-table */
+ struct cfs_hash_bucket **hs_rehash_buckets;
+#if CFS_HASH_DEBUG_LEVEL >= CFS_HASH_DEBUG_1
+ /** serialize debug members */
+ spinlock_t hs_dep_lock;
+ /** max depth */
+ unsigned int hs_dep_max;
+ /** id of the deepest bucket */
+ unsigned int hs_dep_bkt;
+ /** offset in the deepest bucket */
+ unsigned int hs_dep_off;
+ /** bits when we found the max depth */
+ unsigned int hs_dep_bits;
+ /** workitem to output max depth */
+ cfs_workitem_t hs_dep_wi;
+#endif
+ /** name of htable */
+ char hs_name[0];
+};
+
+typedef struct cfs_hash_lock_ops {
+ /** lock the hash table */
+ void (*hs_lock)(union cfs_hash_lock *lock, int exclusive);
+ /** unlock the hash table */
+ void (*hs_unlock)(union cfs_hash_lock *lock, int exclusive);
+ /** lock the hash bucket */
+ void (*hs_bkt_lock)(union cfs_hash_lock *lock, int exclusive);
+ /** unlock the hash bucket */
+ void (*hs_bkt_unlock)(union cfs_hash_lock *lock, int exclusive);
+} cfs_hash_lock_ops_t;
+
+typedef struct cfs_hash_hlist_ops {
+ /** return hlist_head of hash-head of @bd */
+ struct hlist_head *(*hop_hhead)(struct cfs_hash *hs, struct cfs_hash_bd *bd);
+ /** return hash-head size */
+ int (*hop_hhead_size)(struct cfs_hash *hs);
+ /** add @hnode to hash-head of @bd */
+ int (*hop_hnode_add)(struct cfs_hash *hs,
+ struct cfs_hash_bd *bd, struct hlist_node *hnode);
+ /** remove @hnode from hash-head of @bd */
+ int (*hop_hnode_del)(struct cfs_hash *hs,
+ struct cfs_hash_bd *bd, struct hlist_node *hnode);
+} cfs_hash_hlist_ops_t;
+
+typedef struct cfs_hash_ops {
+ /** return hashed value from @key */
+ unsigned (*hs_hash)(struct cfs_hash *hs, const void *key, unsigned mask);
+ /** return key address of @hnode */
+ void * (*hs_key)(struct hlist_node *hnode);
+ /** copy key from @hnode to @key */
+ void (*hs_keycpy)(struct hlist_node *hnode, void *key);
+ /**
+ * compare @key with key of @hnode
+ * returns 1 on a match
+ */
+ int (*hs_keycmp)(const void *key, struct hlist_node *hnode);
+ /** return object address of @hnode, i.e: container_of(...hnode) */
+ void * (*hs_object)(struct hlist_node *hnode);
+ /** get refcount of item, always called with holding bucket-lock */
+ void (*hs_get)(struct cfs_hash *hs, struct hlist_node *hnode);
+ /** release refcount of item */
+ void (*hs_put)(struct cfs_hash *hs, struct hlist_node *hnode);
+ /** release refcount of item, always called with holding bucket-lock */
+ void (*hs_put_locked)(struct cfs_hash *hs, struct hlist_node *hnode);
+ /** it's called before removing of @hnode */
+ void (*hs_exit)(struct cfs_hash *hs, struct hlist_node *hnode);
+} cfs_hash_ops_t;
+
+/** total number of buckets in @hs */
+#define CFS_HASH_NBKT(hs) \
+ (1U << ((hs)->hs_cur_bits - (hs)->hs_bkt_bits))
+
+/** total number of buckets in @hs while rehashing */
+#define CFS_HASH_RH_NBKT(hs) \
+ (1U << ((hs)->hs_rehash_bits - (hs)->hs_bkt_bits))
+
+/** number of hlist for in bucket */
+#define CFS_HASH_BKT_NHLIST(hs) (1U << (hs)->hs_bkt_bits)
+
+/** total number of hlist in @hs */
+#define CFS_HASH_NHLIST(hs) (1U << (hs)->hs_cur_bits)
+
+/** total number of hlist in @hs while rehashing */
+#define CFS_HASH_RH_NHLIST(hs) (1U << (hs)->hs_rehash_bits)
+
+static inline int
+cfs_hash_with_no_lock(struct cfs_hash *hs)
+{
+ /* caller will serialize all operations for this hash-table */
+ return (hs->hs_flags & CFS_HASH_NO_LOCK) != 0;
+}
+
+static inline int
+cfs_hash_with_no_bktlock(struct cfs_hash *hs)
+{
+ /* no bucket lock, one single lock to protect the hash-table */
+ return (hs->hs_flags & CFS_HASH_NO_BKTLOCK) != 0;
+}
+
+static inline int
+cfs_hash_with_rw_bktlock(struct cfs_hash *hs)
+{
+ /* rwlock to protect hash bucket */
+ return (hs->hs_flags & CFS_HASH_RW_BKTLOCK) != 0;
+}
+
+static inline int
+cfs_hash_with_spin_bktlock(struct cfs_hash *hs)
+{
+ /* spinlock to protect hash bucket */
+ return (hs->hs_flags & CFS_HASH_SPIN_BKTLOCK) != 0;
+}
+
+static inline int
+cfs_hash_with_add_tail(struct cfs_hash *hs)
+{
+ return (hs->hs_flags & CFS_HASH_ADD_TAIL) != 0;
+}
+
+static inline int
+cfs_hash_with_no_itemref(struct cfs_hash *hs)
+{
+ /* hash-table doesn't keep refcount on item,
+ * item can't be removed from hash unless it's
+ * ZERO refcount */
+ return (hs->hs_flags & CFS_HASH_NO_ITEMREF) != 0;
+}
+
+static inline int
+cfs_hash_with_bigname(struct cfs_hash *hs)
+{
+ return (hs->hs_flags & CFS_HASH_BIGNAME) != 0;
+}
+
+static inline int
+cfs_hash_with_counter(struct cfs_hash *hs)
+{
+ return (hs->hs_flags & CFS_HASH_COUNTER) != 0;
+}
+
+static inline int
+cfs_hash_with_rehash(struct cfs_hash *hs)
+{
+ return (hs->hs_flags & CFS_HASH_REHASH) != 0;
+}
+
+static inline int
+cfs_hash_with_rehash_key(struct cfs_hash *hs)
+{
+ return (hs->hs_flags & CFS_HASH_REHASH_KEY) != 0;
+}
+
+static inline int
+cfs_hash_with_shrink(struct cfs_hash *hs)
+{
+ return (hs->hs_flags & CFS_HASH_SHRINK) != 0;
+}
+
+static inline int
+cfs_hash_with_assert_empty(struct cfs_hash *hs)
+{
+ return (hs->hs_flags & CFS_HASH_ASSERT_EMPTY) != 0;
+}
+
+static inline int
+cfs_hash_with_depth(struct cfs_hash *hs)
+{
+ return (hs->hs_flags & CFS_HASH_DEPTH) != 0;
+}
+
+static inline int
+cfs_hash_with_nblk_change(struct cfs_hash *hs)
+{
+ return (hs->hs_flags & CFS_HASH_NBLK_CHANGE) != 0;
+}
+
+static inline int
+cfs_hash_is_exiting(struct cfs_hash *hs)
+{ /* cfs_hash_destroy is called */
+ return hs->hs_exiting;
+}
+
+static inline int
+cfs_hash_is_rehashing(struct cfs_hash *hs)
+{ /* rehash is launched */
+ return hs->hs_rehash_bits != 0;
+}
+
+static inline int
+cfs_hash_is_iterating(struct cfs_hash *hs)
+{ /* someone is calling cfs_hash_for_each_* */
+ return hs->hs_iterating || hs->hs_iterators != 0;
+}
+
+static inline int
+cfs_hash_bkt_size(struct cfs_hash *hs)
+{
+ return offsetof(struct cfs_hash_bucket, hsb_head[0]) +
+ hs->hs_hops->hop_hhead_size(hs) * CFS_HASH_BKT_NHLIST(hs) +
+ hs->hs_extra_bytes;
+}
+
+static inline unsigned
+cfs_hash_id(struct cfs_hash *hs, const void *key, unsigned mask)
+{
+ return hs->hs_ops->hs_hash(hs, key, mask);
+}
+
+static inline void *
+cfs_hash_key(struct cfs_hash *hs, struct hlist_node *hnode)
+{
+ return hs->hs_ops->hs_key(hnode);
+}
+
+static inline void
+cfs_hash_keycpy(struct cfs_hash *hs, struct hlist_node *hnode, void *key)
+{
+ if (hs->hs_ops->hs_keycpy)
+ hs->hs_ops->hs_keycpy(hnode, key);
+}
+
+/**
+ * Returns 1 on a match,
+ */
+static inline int
+cfs_hash_keycmp(struct cfs_hash *hs, const void *key, struct hlist_node *hnode)
+{
+ return hs->hs_ops->hs_keycmp(key, hnode);
+}
+
+static inline void *
+cfs_hash_object(struct cfs_hash *hs, struct hlist_node *hnode)
+{
+ return hs->hs_ops->hs_object(hnode);
+}
+
+static inline void
+cfs_hash_get(struct cfs_hash *hs, struct hlist_node *hnode)
+{
+ return hs->hs_ops->hs_get(hs, hnode);
+}
+
+static inline void
+cfs_hash_put_locked(struct cfs_hash *hs, struct hlist_node *hnode)
+{
+ return hs->hs_ops->hs_put_locked(hs, hnode);
+}
+
+static inline void
+cfs_hash_put(struct cfs_hash *hs, struct hlist_node *hnode)
+{
+ return hs->hs_ops->hs_put(hs, hnode);
+}
+
+static inline void
+cfs_hash_exit(struct cfs_hash *hs, struct hlist_node *hnode)
+{
+ if (hs->hs_ops->hs_exit)
+ hs->hs_ops->hs_exit(hs, hnode);
+}
+
+static inline void cfs_hash_lock(struct cfs_hash *hs, int excl)
+{
+ hs->hs_lops->hs_lock(&hs->hs_lock, excl);
+}
+
+static inline void cfs_hash_unlock(struct cfs_hash *hs, int excl)
+{
+ hs->hs_lops->hs_unlock(&hs->hs_lock, excl);
+}
+
+static inline int cfs_hash_dec_and_lock(struct cfs_hash *hs,
+ atomic_t *condition)
+{
+ LASSERT(cfs_hash_with_no_bktlock(hs));
+ return atomic_dec_and_lock(condition, &hs->hs_lock.spin);
+}
+
+static inline void cfs_hash_bd_lock(struct cfs_hash *hs,
+ struct cfs_hash_bd *bd, int excl)
+{
+ hs->hs_lops->hs_bkt_lock(&bd->bd_bucket->hsb_lock, excl);
+}
+
+static inline void cfs_hash_bd_unlock(struct cfs_hash *hs,
+ struct cfs_hash_bd *bd, int excl)
+{
+ hs->hs_lops->hs_bkt_unlock(&bd->bd_bucket->hsb_lock, excl);
+}
+
+/**
+ * operations on cfs_hash bucket (bd: bucket descriptor),
+ * they are normally for hash-table without rehash
+ */
+void cfs_hash_bd_get(struct cfs_hash *hs, const void *key, struct cfs_hash_bd *bd);
+
+static inline void cfs_hash_bd_get_and_lock(struct cfs_hash *hs, const void *key,
+ struct cfs_hash_bd *bd, int excl)
+{
+ cfs_hash_bd_get(hs, key, bd);
+ cfs_hash_bd_lock(hs, bd, excl);
+}
+
+static inline unsigned cfs_hash_bd_index_get(struct cfs_hash *hs, struct cfs_hash_bd *bd)
+{
+ return bd->bd_offset | (bd->bd_bucket->hsb_index << hs->hs_bkt_bits);
+}
+
+static inline void cfs_hash_bd_index_set(struct cfs_hash *hs,
+ unsigned index, struct cfs_hash_bd *bd)
+{
+ bd->bd_bucket = hs->hs_buckets[index >> hs->hs_bkt_bits];
+ bd->bd_offset = index & (CFS_HASH_BKT_NHLIST(hs) - 1U);
+}
+
+static inline void *
+cfs_hash_bd_extra_get(struct cfs_hash *hs, struct cfs_hash_bd *bd)
+{
+ return (void *)bd->bd_bucket +
+ cfs_hash_bkt_size(hs) - hs->hs_extra_bytes;
+}
+
+static inline __u32
+cfs_hash_bd_version_get(struct cfs_hash_bd *bd)
+{
+ /* need hold cfs_hash_bd_lock */
+ return bd->bd_bucket->hsb_version;
+}
+
+static inline __u32
+cfs_hash_bd_count_get(struct cfs_hash_bd *bd)
+{
+ /* need hold cfs_hash_bd_lock */
+ return bd->bd_bucket->hsb_count;
+}
+
+static inline int
+cfs_hash_bd_depmax_get(struct cfs_hash_bd *bd)
+{
+ return bd->bd_bucket->hsb_depmax;
+}
+
+static inline int
+cfs_hash_bd_compare(struct cfs_hash_bd *bd1, struct cfs_hash_bd *bd2)
+{
+ if (bd1->bd_bucket->hsb_index != bd2->bd_bucket->hsb_index)
+ return bd1->bd_bucket->hsb_index - bd2->bd_bucket->hsb_index;
+
+ if (bd1->bd_offset != bd2->bd_offset)
+ return bd1->bd_offset - bd2->bd_offset;
+
+ return 0;
+}
+
+void cfs_hash_bd_add_locked(struct cfs_hash *hs, struct cfs_hash_bd *bd,
+ struct hlist_node *hnode);
+void cfs_hash_bd_del_locked(struct cfs_hash *hs, struct cfs_hash_bd *bd,
+ struct hlist_node *hnode);
+void cfs_hash_bd_move_locked(struct cfs_hash *hs, struct cfs_hash_bd *bd_old,
+ struct cfs_hash_bd *bd_new, struct hlist_node *hnode);
+
+static inline int cfs_hash_bd_dec_and_lock(struct cfs_hash *hs, struct cfs_hash_bd *bd,
+ atomic_t *condition)
+{
+ LASSERT(cfs_hash_with_spin_bktlock(hs));
+ return atomic_dec_and_lock(condition,
+ &bd->bd_bucket->hsb_lock.spin);
+}
+
+static inline struct hlist_head *cfs_hash_bd_hhead(struct cfs_hash *hs,
+ struct cfs_hash_bd *bd)
+{
+ return hs->hs_hops->hop_hhead(hs, bd);
+}
+
+struct hlist_node *cfs_hash_bd_lookup_locked(struct cfs_hash *hs,
+ struct cfs_hash_bd *bd, const void *key);
+struct hlist_node *cfs_hash_bd_peek_locked(struct cfs_hash *hs,
+ struct cfs_hash_bd *bd, const void *key);
+struct hlist_node *cfs_hash_bd_findadd_locked(struct cfs_hash *hs,
+ struct cfs_hash_bd *bd, const void *key,
+ struct hlist_node *hnode,
+ int insist_add);
+struct hlist_node *cfs_hash_bd_finddel_locked(struct cfs_hash *hs,
+ struct cfs_hash_bd *bd, const void *key,
+ struct hlist_node *hnode);
+
+/**
+ * operations on cfs_hash bucket (bd: bucket descriptor),
+ * they are safe for hash-table with rehash
+ */
+void cfs_hash_dual_bd_get(struct cfs_hash *hs, const void *key, struct cfs_hash_bd *bds);
+void cfs_hash_dual_bd_lock(struct cfs_hash *hs, struct cfs_hash_bd *bds, int excl);
+void cfs_hash_dual_bd_unlock(struct cfs_hash *hs, struct cfs_hash_bd *bds, int excl);
+
+static inline void cfs_hash_dual_bd_get_and_lock(struct cfs_hash *hs, const void *key,
+ struct cfs_hash_bd *bds, int excl)
+{
+ cfs_hash_dual_bd_get(hs, key, bds);
+ cfs_hash_dual_bd_lock(hs, bds, excl);
+}
+
+struct hlist_node *cfs_hash_dual_bd_lookup_locked(struct cfs_hash *hs,
+ struct cfs_hash_bd *bds,
+ const void *key);
+struct hlist_node *cfs_hash_dual_bd_findadd_locked(struct cfs_hash *hs,
+ struct cfs_hash_bd *bds,
+ const void *key,
+ struct hlist_node *hnode,
+ int insist_add);
+struct hlist_node *cfs_hash_dual_bd_finddel_locked(struct cfs_hash *hs,
+ struct cfs_hash_bd *bds,
+ const void *key,
+ struct hlist_node *hnode);
+
+/* Hash init/cleanup functions */
+struct cfs_hash *cfs_hash_create(char *name, unsigned cur_bits, unsigned max_bits,
+ unsigned bkt_bits, unsigned extra_bytes,
+ unsigned min_theta, unsigned max_theta,
+ cfs_hash_ops_t *ops, unsigned flags);
+
+struct cfs_hash *cfs_hash_getref(struct cfs_hash *hs);
+void cfs_hash_putref(struct cfs_hash *hs);
+
+/* Hash addition functions */
+void cfs_hash_add(struct cfs_hash *hs, const void *key,
+ struct hlist_node *hnode);
+int cfs_hash_add_unique(struct cfs_hash *hs, const void *key,
+ struct hlist_node *hnode);
+void *cfs_hash_findadd_unique(struct cfs_hash *hs, const void *key,
+ struct hlist_node *hnode);
+
+/* Hash deletion functions */
+void *cfs_hash_del(struct cfs_hash *hs, const void *key, struct hlist_node *hnode);
+void *cfs_hash_del_key(struct cfs_hash *hs, const void *key);
+
+/* Hash lookup/for_each functions */
+#define CFS_HASH_LOOP_HOG 1024
+
+typedef int (*cfs_hash_for_each_cb_t)(struct cfs_hash *hs, struct cfs_hash_bd *bd,
+ struct hlist_node *node, void *data);
+void *cfs_hash_lookup(struct cfs_hash *hs, const void *key);
+void cfs_hash_for_each(struct cfs_hash *hs, cfs_hash_for_each_cb_t, void *data);
+void cfs_hash_for_each_safe(struct cfs_hash *hs, cfs_hash_for_each_cb_t, void *data);
+int cfs_hash_for_each_nolock(struct cfs_hash *hs,
+ cfs_hash_for_each_cb_t, void *data);
+int cfs_hash_for_each_empty(struct cfs_hash *hs,
+ cfs_hash_for_each_cb_t, void *data);
+void cfs_hash_for_each_key(struct cfs_hash *hs, const void *key,
+ cfs_hash_for_each_cb_t, void *data);
+typedef int (*cfs_hash_cond_opt_cb_t)(void *obj, void *data);
+void cfs_hash_cond_del(struct cfs_hash *hs, cfs_hash_cond_opt_cb_t, void *data);
+
+void cfs_hash_hlist_for_each(struct cfs_hash *hs, unsigned hindex,
+ cfs_hash_for_each_cb_t, void *data);
+int cfs_hash_is_empty(struct cfs_hash *hs);
+__u64 cfs_hash_size_get(struct cfs_hash *hs);
+
+/*
+ * Rehash - Theta is calculated to be the average chained
+ * hash depth assuming a perfectly uniform hash function.
+ */
+void cfs_hash_rehash_cancel_locked(struct cfs_hash *hs);
+void cfs_hash_rehash_cancel(struct cfs_hash *hs);
+int cfs_hash_rehash(struct cfs_hash *hs, int do_rehash);
+void cfs_hash_rehash_key(struct cfs_hash *hs, const void *old_key,
+ void *new_key, struct hlist_node *hnode);
+
+#if CFS_HASH_DEBUG_LEVEL > CFS_HASH_DEBUG_1
+/* Validate hnode references the correct key */
+static inline void
+cfs_hash_key_validate(struct cfs_hash *hs, const void *key,
+ struct hlist_node *hnode)
+{
+ LASSERT(cfs_hash_keycmp(hs, key, hnode));
+}
+
+/* Validate hnode is in the correct bucket */
+static inline void
+cfs_hash_bucket_validate(struct cfs_hash *hs, struct cfs_hash_bd *bd,
+ struct hlist_node *hnode)
+{
+ struct cfs_hash_bd bds[2];
+
+ cfs_hash_dual_bd_get(hs, cfs_hash_key(hs, hnode), bds);
+ LASSERT(bds[0].bd_bucket == bd->bd_bucket ||
+ bds[1].bd_bucket == bd->bd_bucket);
+}
+
+#else /* CFS_HASH_DEBUG_LEVEL > CFS_HASH_DEBUG_1 */
+
+static inline void
+cfs_hash_key_validate(struct cfs_hash *hs, const void *key,
+ struct hlist_node *hnode) {}
+
+static inline void
+cfs_hash_bucket_validate(struct cfs_hash *hs, struct cfs_hash_bd *bd,
+ struct hlist_node *hnode) {}
+
+#endif /* CFS_HASH_DEBUG_LEVEL */
+
+#define CFS_HASH_THETA_BITS 10
+#define CFS_HASH_MIN_THETA (1U << (CFS_HASH_THETA_BITS - 1))
+#define CFS_HASH_MAX_THETA (1U << (CFS_HASH_THETA_BITS + 1))
+
+/* Return integer component of theta */
+static inline int __cfs_hash_theta_int(int theta)
+{
+ return (theta >> CFS_HASH_THETA_BITS);
+}
+
+/* Return a fractional value between 0 and 999 */
+static inline int __cfs_hash_theta_frac(int theta)
+{
+ return ((theta * 1000) >> CFS_HASH_THETA_BITS) -
+ (__cfs_hash_theta_int(theta) * 1000);
+}
+
+static inline int __cfs_hash_theta(struct cfs_hash *hs)
+{
+ return (atomic_read(&hs->hs_count) <<
+ CFS_HASH_THETA_BITS) >> hs->hs_cur_bits;
+}
+
+static inline void __cfs_hash_set_theta(struct cfs_hash *hs, int min, int max)
+{
+ LASSERT(min < max);
+ hs->hs_min_theta = (__u16)min;
+ hs->hs_max_theta = (__u16)max;
+}
+
+/* Generic debug formatting routines mainly for proc handler */
+struct seq_file;
+void cfs_hash_debug_header(struct seq_file *m);
+void cfs_hash_debug_str(struct cfs_hash *hs, struct seq_file *m);
+
+/*
+ * Generic djb2 hash algorithm for character arrays.
+ */
+static inline unsigned
+cfs_hash_djb2_hash(const void *key, size_t size, unsigned mask)
+{
+ unsigned i, hash = 5381;
+
+ LASSERT(key != NULL);
+
+ for (i = 0; i < size; i++)
+ hash = hash * 33 + ((char *)key)[i];
+
+ return (hash & mask);
+}
+
+/*
+ * Generic u32 hash algorithm.
+ */
+static inline unsigned
+cfs_hash_u32_hash(const __u32 key, unsigned mask)
+{
+ return ((key * CFS_GOLDEN_RATIO_PRIME_32) & mask);
+}
+
+/*
+ * Generic u64 hash algorithm.
+ */
+static inline unsigned
+cfs_hash_u64_hash(const __u64 key, unsigned mask)
+{
+ return ((unsigned)(key * CFS_GOLDEN_RATIO_PRIME_64) & mask);
+}
+
+/** iterate over all buckets in @bds (array of struct cfs_hash_bd) */
+#define cfs_hash_for_each_bd(bds, n, i) \
+ for (i = 0; i < n && (bds)[i].bd_bucket != NULL; i++)
+
+/** iterate over all buckets of @hs */
+#define cfs_hash_for_each_bucket(hs, bd, pos) \
+ for (pos = 0; \
+ pos < CFS_HASH_NBKT(hs) && \
+ ((bd)->bd_bucket = (hs)->hs_buckets[pos]) != NULL; pos++)
+
+/** iterate over all hlist of bucket @bd */
+#define cfs_hash_bd_for_each_hlist(hs, bd, hlist) \
+ for ((bd)->bd_offset = 0; \
+ (bd)->bd_offset < CFS_HASH_BKT_NHLIST(hs) && \
+ (hlist = cfs_hash_bd_hhead(hs, bd)) != NULL; \
+ (bd)->bd_offset++)
+
+/* !__LIBCFS__HASH_H__ */
+#endif
diff --git a/drivers/staging/lustre/include/linux/libcfs/libcfs_ioctl.h b/drivers/staging/lustre/include/linux/libcfs/libcfs_ioctl.h
new file mode 100644
index 000000000..3ee38782a
--- /dev/null
+++ b/drivers/staging/lustre/include/linux/libcfs/libcfs_ioctl.h
@@ -0,0 +1,214 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ *
+ * libcfs/include/libcfs/libcfs_ioctl.h
+ *
+ * Low-level ioctl data structures. Kernel ioctl functions declared here,
+ * and user space functions are in libcfsutil_ioctl.h.
+ *
+ */
+
+#ifndef __LIBCFS_IOCTL_H__
+#define __LIBCFS_IOCTL_H__
+
+#define LIBCFS_IOCTL_VERSION 0x0001000a
+
+struct libcfs_ioctl_data {
+ __u32 ioc_len;
+ __u32 ioc_version;
+
+ __u64 ioc_nid;
+ __u64 ioc_u64[1];
+
+ __u32 ioc_flags;
+ __u32 ioc_count;
+ __u32 ioc_net;
+ __u32 ioc_u32[7];
+
+ __u32 ioc_inllen1;
+ char *ioc_inlbuf1;
+ __u32 ioc_inllen2;
+ char *ioc_inlbuf2;
+
+ __u32 ioc_plen1; /* buffers in userspace */
+ char *ioc_pbuf1;
+ __u32 ioc_plen2; /* buffers in userspace */
+ char *ioc_pbuf2;
+
+ char ioc_bulk[0];
+};
+
+#define ioc_priority ioc_u32[0]
+
+struct libcfs_ioctl_hdr {
+ __u32 ioc_len;
+ __u32 ioc_version;
+};
+
+struct libcfs_debug_ioctl_data {
+ struct libcfs_ioctl_hdr hdr;
+ unsigned int subs;
+ unsigned int debug;
+};
+
+#define LIBCFS_IOC_INIT(data) \
+do { \
+ memset(&data, 0, sizeof(data)); \
+ data.ioc_version = LIBCFS_IOCTL_VERSION; \
+ data.ioc_len = sizeof(data); \
+} while (0)
+
+struct libcfs_ioctl_handler {
+ struct list_head item;
+ int (*handle_ioctl)(unsigned int cmd, struct libcfs_ioctl_data *data);
+};
+
+#define DECLARE_IOCTL_HANDLER(ident, func) \
+ struct libcfs_ioctl_handler ident = { \
+ /* .item = */ LIST_HEAD_INIT(ident.item), \
+ /* .handle_ioctl = */ func \
+ }
+
+/* FIXME check conflict with lustre_lib.h */
+#define LIBCFS_IOC_DEBUG_MASK _IOWR('f', 250, long)
+
+/* ioctls for manipulating snapshots 30- */
+#define IOC_LIBCFS_TYPE 'e'
+#define IOC_LIBCFS_MIN_NR 30
+/* libcfs ioctls */
+#define IOC_LIBCFS_PANIC _IOWR('e', 30, long)
+#define IOC_LIBCFS_CLEAR_DEBUG _IOWR('e', 31, long)
+#define IOC_LIBCFS_MARK_DEBUG _IOWR('e', 32, long)
+#define IOC_LIBCFS_MEMHOG _IOWR('e', 36, long)
+#define IOC_LIBCFS_PING_TEST _IOWR('e', 37, long)
+/* lnet ioctls */
+#define IOC_LIBCFS_GET_NI _IOWR('e', 50, long)
+#define IOC_LIBCFS_FAIL_NID _IOWR('e', 51, long)
+#define IOC_LIBCFS_ADD_ROUTE _IOWR('e', 52, long)
+#define IOC_LIBCFS_DEL_ROUTE _IOWR('e', 53, long)
+#define IOC_LIBCFS_GET_ROUTE _IOWR('e', 54, long)
+#define IOC_LIBCFS_NOTIFY_ROUTER _IOWR('e', 55, long)
+#define IOC_LIBCFS_UNCONFIGURE _IOWR('e', 56, long)
+#define IOC_LIBCFS_PORTALS_COMPATIBILITY _IOWR('e', 57, long)
+#define IOC_LIBCFS_LNET_DIST _IOWR('e', 58, long)
+#define IOC_LIBCFS_CONFIGURE _IOWR('e', 59, long)
+#define IOC_LIBCFS_TESTPROTOCOMPAT _IOWR('e', 60, long)
+#define IOC_LIBCFS_PING _IOWR('e', 61, long)
+#define IOC_LIBCFS_DEBUG_PEER _IOWR('e', 62, long)
+#define IOC_LIBCFS_LNETST _IOWR('e', 63, long)
+/* lnd ioctls */
+#define IOC_LIBCFS_REGISTER_MYNID _IOWR('e', 70, long)
+#define IOC_LIBCFS_CLOSE_CONNECTION _IOWR('e', 71, long)
+#define IOC_LIBCFS_PUSH_CONNECTION _IOWR('e', 72, long)
+#define IOC_LIBCFS_GET_CONN _IOWR('e', 73, long)
+#define IOC_LIBCFS_DEL_PEER _IOWR('e', 74, long)
+#define IOC_LIBCFS_ADD_PEER _IOWR('e', 75, long)
+#define IOC_LIBCFS_GET_PEER _IOWR('e', 76, long)
+#define IOC_LIBCFS_GET_TXDESC _IOWR('e', 77, long)
+#define IOC_LIBCFS_ADD_INTERFACE _IOWR('e', 78, long)
+#define IOC_LIBCFS_DEL_INTERFACE _IOWR('e', 79, long)
+#define IOC_LIBCFS_GET_INTERFACE _IOWR('e', 80, long)
+
+#define IOC_LIBCFS_MAX_NR 80
+
+static inline int libcfs_ioctl_packlen(struct libcfs_ioctl_data *data)
+{
+ int len = sizeof(*data);
+
+ len += cfs_size_round(data->ioc_inllen1);
+ len += cfs_size_round(data->ioc_inllen2);
+ return len;
+}
+
+static inline int libcfs_ioctl_is_invalid(struct libcfs_ioctl_data *data)
+{
+ if (data->ioc_len > (1<<30)) {
+ CERROR("LIBCFS ioctl: ioc_len larger than 1<<30\n");
+ return 1;
+ }
+ if (data->ioc_inllen1 > (1<<30)) {
+ CERROR("LIBCFS ioctl: ioc_inllen1 larger than 1<<30\n");
+ return 1;
+ }
+ if (data->ioc_inllen2 > (1<<30)) {
+ CERROR("LIBCFS ioctl: ioc_inllen2 larger than 1<<30\n");
+ return 1;
+ }
+ if (data->ioc_inlbuf1 && !data->ioc_inllen1) {
+ CERROR("LIBCFS ioctl: inlbuf1 pointer but 0 length\n");
+ return 1;
+ }
+ if (data->ioc_inlbuf2 && !data->ioc_inllen2) {
+ CERROR("LIBCFS ioctl: inlbuf2 pointer but 0 length\n");
+ return 1;
+ }
+ if (data->ioc_pbuf1 && !data->ioc_plen1) {
+ CERROR("LIBCFS ioctl: pbuf1 pointer but 0 length\n");
+ return 1;
+ }
+ if (data->ioc_pbuf2 && !data->ioc_plen2) {
+ CERROR("LIBCFS ioctl: pbuf2 pointer but 0 length\n");
+ return 1;
+ }
+ if (data->ioc_plen1 && !data->ioc_pbuf1) {
+ CERROR("LIBCFS ioctl: plen1 nonzero but no pbuf1 pointer\n");
+ return 1;
+ }
+ if (data->ioc_plen2 && !data->ioc_pbuf2) {
+ CERROR("LIBCFS ioctl: plen2 nonzero but no pbuf2 pointer\n");
+ return 1;
+ }
+ if ((__u32)libcfs_ioctl_packlen(data) != data->ioc_len) {
+ CERROR("LIBCFS ioctl: packlen != ioc_len\n");
+ return 1;
+ }
+ if (data->ioc_inllen1 &&
+ data->ioc_bulk[data->ioc_inllen1 - 1] != '\0') {
+ CERROR("LIBCFS ioctl: inlbuf1 not 0 terminated\n");
+ return 1;
+ }
+ if (data->ioc_inllen2 &&
+ data->ioc_bulk[cfs_size_round(data->ioc_inllen1) +
+ data->ioc_inllen2 - 1] != '\0') {
+ CERROR("LIBCFS ioctl: inlbuf2 not 0 terminated\n");
+ return 1;
+ }
+ return 0;
+}
+
+int libcfs_register_ioctl(struct libcfs_ioctl_handler *hand);
+int libcfs_deregister_ioctl(struct libcfs_ioctl_handler *hand);
+int libcfs_ioctl_getdata(char *buf, char *end, void *arg);
+int libcfs_ioctl_popdata(void *arg, void *buf, int size);
+
+#endif /* __LIBCFS_IOCTL_H__ */
diff --git a/drivers/staging/lustre/include/linux/libcfs/libcfs_kernelcomm.h b/drivers/staging/lustre/include/linux/libcfs/libcfs_kernelcomm.h
new file mode 100644
index 000000000..a989d2666
--- /dev/null
+++ b/drivers/staging/lustre/include/linux/libcfs/libcfs_kernelcomm.h
@@ -0,0 +1,118 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ *
+ * Author: Nathan Rutman <nathan.rutman@sun.com>
+ *
+ * libcfs/include/libcfs/libcfs_kernelcomm.h
+ *
+ * Kernel <-> userspace communication routines.
+ * The definitions below are used in the kernel and userspace.
+ *
+ */
+
+#ifndef __LIBCFS_KERNELCOMM_H__
+#define __LIBCFS_KERNELCOMM_H__
+
+#ifndef __LIBCFS_LIBCFS_H__
+#error Do not #include this file directly. #include <linux/libcfs/libcfs.h> instead
+#endif
+
+/* KUC message header.
+ * All current and future KUC messages should use this header.
+ * To avoid having to include Lustre headers from libcfs, define this here.
+ */
+struct kuc_hdr {
+ __u16 kuc_magic;
+ __u8 kuc_transport; /* Each new Lustre feature should use a different
+ transport */
+ __u8 kuc_flags;
+ __u16 kuc_msgtype; /* Message type or opcode, transport-specific */
+ __u16 kuc_msglen; /* Including header */
+} __aligned(sizeof(__u64));
+
+#define KUC_CHANGELOG_MSG_MAXSIZE (sizeof(struct kuc_hdr)+CR_MAXSIZE)
+
+#define KUC_MAGIC 0x191C /*Lustre9etLinC */
+#define KUC_FL_BLOCK 0x01 /* Wait for send */
+
+/* kuc_msgtype values are defined in each transport */
+enum kuc_transport_type {
+ KUC_TRANSPORT_GENERIC = 1,
+ KUC_TRANSPORT_HSM = 2,
+ KUC_TRANSPORT_CHANGELOG = 3,
+};
+
+enum kuc_generic_message_type {
+ KUC_MSG_SHUTDOWN = 1,
+};
+
+/* prototype for callback function on kuc groups */
+typedef int (*libcfs_kkuc_cb_t)(__u32 data, void *cb_arg);
+
+/* KUC Broadcast Groups. This determines which userspace process hears which
+ * messages. Mutliple transports may be used within a group, or multiple
+ * groups may use the same transport. Broadcast
+ * groups need not be used if e.g. a UID is specified instead;
+ * use group 0 to signify unicast.
+ */
+#define KUC_GRP_HSM 0x02
+#define KUC_GRP_MAX KUC_GRP_HSM
+
+/* Kernel methods */
+int libcfs_kkuc_msg_put(struct file *fp, void *payload);
+int libcfs_kkuc_group_put(int group, void *payload);
+int libcfs_kkuc_group_add(struct file *fp, int uid, int group,
+ __u32 data);
+int libcfs_kkuc_group_rem(int uid, int group);
+int libcfs_kkuc_group_foreach(int group, libcfs_kkuc_cb_t cb_func,
+ void *cb_arg);
+
+#define LK_FLG_STOP 0x01
+
+/* kernelcomm control structure, passed from userspace to kernel */
+typedef struct lustre_kernelcomm {
+ __u32 lk_wfd;
+ __u32 lk_rfd;
+ __u32 lk_uid;
+ __u32 lk_group;
+ __u32 lk_data;
+ __u32 lk_flags;
+} __packed lustre_kernelcomm;
+
+/* Userspace methods */
+int libcfs_ukuc_start(lustre_kernelcomm *l, int groups);
+int libcfs_ukuc_stop(lustre_kernelcomm *l);
+int libcfs_ukuc_msg_get(lustre_kernelcomm *l, char *buf, int maxsize,
+ int transport);
+
+#endif /* __LIBCFS_KERNELCOMM_H__ */
diff --git a/drivers/staging/lustre/include/linux/libcfs/libcfs_prim.h b/drivers/staging/lustre/include/linux/libcfs/libcfs_prim.h
new file mode 100644
index 000000000..978d3e2f1
--- /dev/null
+++ b/drivers/staging/lustre/include/linux/libcfs/libcfs_prim.h
@@ -0,0 +1,87 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ *
+ * libcfs/include/libcfs/libcfs_prim.h
+ *
+ * General primitives.
+ *
+ */
+
+#ifndef __LIBCFS_PRIM_H__
+#define __LIBCFS_PRIM_H__
+
+/*
+ * Timer
+ */
+typedef void (cfs_timer_func_t)(ulong_ptr_t);
+
+void add_wait_queue_exclusive_head(wait_queue_head_t *, wait_queue_t *);
+
+void cfs_init_timer(struct timer_list *t);
+void cfs_timer_init(struct timer_list *t, cfs_timer_func_t *func, void *arg);
+void cfs_timer_done(struct timer_list *t);
+void cfs_timer_arm(struct timer_list *t, unsigned long deadline);
+void cfs_timer_disarm(struct timer_list *t);
+int cfs_timer_is_armed(struct timer_list *t);
+unsigned long cfs_timer_deadline(struct timer_list *t);
+
+/*
+ * Memory
+ */
+#ifndef memory_pressure_get
+#define memory_pressure_get() (0)
+#endif
+#ifndef memory_pressure_set
+#define memory_pressure_set() do {} while (0)
+#endif
+#ifndef memory_pressure_clr
+#define memory_pressure_clr() do {} while (0)
+#endif
+
+static inline int cfs_memory_pressure_get_and_set(void)
+{
+ int old = memory_pressure_get();
+
+ if (!old)
+ memory_pressure_set();
+ return old;
+}
+
+static inline void cfs_memory_pressure_restore(int old)
+{
+ if (old)
+ memory_pressure_set();
+ else
+ memory_pressure_clr();
+}
+#endif
diff --git a/drivers/staging/lustre/include/linux/libcfs/libcfs_private.h b/drivers/staging/lustre/include/linux/libcfs/libcfs_private.h
new file mode 100644
index 000000000..fef882530
--- /dev/null
+++ b/drivers/staging/lustre/include/linux/libcfs/libcfs_private.h
@@ -0,0 +1,556 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2011, 2012, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ *
+ * libcfs/include/libcfs/libcfs_private.h
+ *
+ * Various defines for libcfs.
+ *
+ */
+
+#ifndef __LIBCFS_PRIVATE_H__
+#define __LIBCFS_PRIVATE_H__
+
+/* XXX this layering violation is for nidstrings */
+#include "../lnet/types.h"
+
+#ifndef DEBUG_SUBSYSTEM
+# define DEBUG_SUBSYSTEM S_UNDEFINED
+#endif
+
+
+/*
+ * When this is on, LASSERT macro includes check for assignment used instead
+ * of equality check, but doesn't have unlikely(). Turn this on from time to
+ * time to make test-builds. This shouldn't be on for production release.
+ */
+#define LASSERT_CHECKED (0)
+
+#define LASSERTF(cond, fmt, ...) \
+do { \
+ if (unlikely(!(cond))) { \
+ LIBCFS_DEBUG_MSG_DATA_DECL(__msg_data, D_EMERG, NULL); \
+ libcfs_debug_msg(&__msg_data, \
+ "ASSERTION( %s ) failed: " fmt, #cond, \
+ ## __VA_ARGS__); \
+ lbug_with_loc(&__msg_data); \
+ } \
+} while (0)
+
+#define LASSERT(cond) LASSERTF(cond, "\n")
+
+#ifdef CONFIG_LUSTRE_DEBUG_EXPENSIVE_CHECK
+/**
+ * This is for more expensive checks that one doesn't want to be enabled all
+ * the time. LINVRNT() has to be explicitly enabled by
+ * CONFIG_LUSTRE_DEBUG_EXPENSIVE_CHECK option.
+ */
+# define LINVRNT(exp) LASSERT(exp)
+#else
+# define LINVRNT(exp) ((void)sizeof !!(exp))
+#endif
+
+#define KLASSERT(e) LASSERT(e)
+
+void lbug_with_loc(struct libcfs_debug_msg_data *)__attribute__((noreturn));
+
+#define LBUG() \
+do { \
+ LIBCFS_DEBUG_MSG_DATA_DECL(msgdata, D_EMERG, NULL); \
+ lbug_with_loc(&msgdata); \
+} while (0)
+
+extern atomic_t libcfs_kmemory;
+/*
+ * Memory
+ */
+
+# define libcfs_kmem_inc(ptr, size) \
+do { \
+ atomic_add(size, &libcfs_kmemory); \
+} while (0)
+
+# define libcfs_kmem_dec(ptr, size) \
+do { \
+ atomic_sub(size, &libcfs_kmemory); \
+} while (0)
+
+# define libcfs_kmem_read() \
+ atomic_read(&libcfs_kmemory)
+
+#ifndef LIBCFS_VMALLOC_SIZE
+#define LIBCFS_VMALLOC_SIZE (2 << PAGE_CACHE_SHIFT) /* 2 pages */
+#endif
+
+#define LIBCFS_ALLOC_PRE(size, mask) \
+do { \
+ LASSERT(!in_interrupt() || \
+ ((size) <= LIBCFS_VMALLOC_SIZE && \
+ ((mask) & __GFP_WAIT) == 0)); \
+} while (0)
+
+#define LIBCFS_ALLOC_POST(ptr, size) \
+do { \
+ if (unlikely((ptr) == NULL)) { \
+ CERROR("LNET: out of memory at %s:%d (tried to alloc '" \
+ #ptr "' = %d)\n", __FILE__, __LINE__, (int)(size)); \
+ CERROR("LNET: %d total bytes allocated by lnet\n", \
+ libcfs_kmem_read()); \
+ } else { \
+ memset((ptr), 0, (size)); \
+ libcfs_kmem_inc((ptr), (size)); \
+ CDEBUG(D_MALLOC, "alloc '" #ptr "': %d at %p (tot %d).\n", \
+ (int)(size), (ptr), libcfs_kmem_read()); \
+ } \
+} while (0)
+
+/**
+ * allocate memory with GFP flags @mask
+ */
+#define LIBCFS_ALLOC_GFP(ptr, size, mask) \
+do { \
+ LIBCFS_ALLOC_PRE((size), (mask)); \
+ (ptr) = (size) <= LIBCFS_VMALLOC_SIZE ? \
+ kmalloc((size), (mask)) : vmalloc(size); \
+ LIBCFS_ALLOC_POST((ptr), (size)); \
+} while (0)
+
+/**
+ * default allocator
+ */
+#define LIBCFS_ALLOC(ptr, size) \
+ LIBCFS_ALLOC_GFP(ptr, size, GFP_NOFS)
+
+/**
+ * non-sleeping allocator
+ */
+#define LIBCFS_ALLOC_ATOMIC(ptr, size) \
+ LIBCFS_ALLOC_GFP(ptr, size, GFP_ATOMIC)
+
+/**
+ * allocate memory for specified CPU partition
+ * \a cptab != NULL, \a cpt is CPU partition id of \a cptab
+ * \a cptab == NULL, \a cpt is HW NUMA node id
+ */
+#define LIBCFS_CPT_ALLOC_GFP(ptr, cptab, cpt, size, mask) \
+do { \
+ LIBCFS_ALLOC_PRE((size), (mask)); \
+ (ptr) = (size) <= LIBCFS_VMALLOC_SIZE ? \
+ kmalloc_node((size), (mask), cfs_cpt_spread_node(cptab, cpt)) :\
+ vmalloc_node(size, cfs_cpt_spread_node(cptab, cpt)); \
+ LIBCFS_ALLOC_POST((ptr), (size)); \
+} while (0)
+
+/** default numa allocator */
+#define LIBCFS_CPT_ALLOC(ptr, cptab, cpt, size) \
+ LIBCFS_CPT_ALLOC_GFP(ptr, cptab, cpt, size, GFP_NOFS)
+
+#define LIBCFS_FREE(ptr, size) \
+do { \
+ int s = (size); \
+ if (unlikely((ptr) == NULL)) { \
+ CERROR("LIBCFS: free NULL '" #ptr "' (%d bytes) at " \
+ "%s:%d\n", s, __FILE__, __LINE__); \
+ break; \
+ } \
+ libcfs_kmem_dec((ptr), s); \
+ CDEBUG(D_MALLOC, "kfreed '" #ptr "': %d at %p (tot %d).\n", \
+ s, (ptr), libcfs_kmem_read()); \
+ if (unlikely(s > LIBCFS_VMALLOC_SIZE)) \
+ vfree(ptr); \
+ else \
+ kfree(ptr); \
+} while (0)
+
+/******************************************************************************/
+
+/* htonl hack - either this, or compile with -O2. Stupid byteorder/generic.h */
+#if defined(__GNUC__) && (__GNUC__ >= 2) && !defined(__OPTIMIZE__)
+#define ___htonl(x) __cpu_to_be32(x)
+#define ___htons(x) __cpu_to_be16(x)
+#define ___ntohl(x) __be32_to_cpu(x)
+#define ___ntohs(x) __be16_to_cpu(x)
+#define htonl(x) ___htonl(x)
+#define ntohl(x) ___ntohl(x)
+#define htons(x) ___htons(x)
+#define ntohs(x) ___ntohs(x)
+#endif
+
+void libcfs_run_upcall(char **argv);
+void libcfs_run_lbug_upcall(struct libcfs_debug_msg_data *);
+void libcfs_debug_dumplog(void);
+int libcfs_debug_init(unsigned long bufsize);
+int libcfs_debug_cleanup(void);
+int libcfs_debug_clear_buffer(void);
+int libcfs_debug_mark_buffer(const char *text);
+
+void libcfs_debug_set_level(unsigned int debug_level);
+
+/*
+ * allocate per-cpu-partition data, returned value is an array of pointers,
+ * variable can be indexed by CPU ID.
+ * cptable != NULL: size of array is number of CPU partitions
+ * cptable == NULL: size of array is number of HW cores
+ */
+void *cfs_percpt_alloc(struct cfs_cpt_table *cptab, unsigned int size);
+/*
+ * destroy per-cpu-partition variable
+ */
+void cfs_percpt_free(void *vars);
+int cfs_percpt_number(void *vars);
+void *cfs_percpt_current(void *vars);
+void *cfs_percpt_index(void *vars, int idx);
+
+#define cfs_percpt_for_each(var, i, vars) \
+ for (i = 0; i < cfs_percpt_number(vars) && \
+ ((var) = (vars)[i]) != NULL; i++)
+
+/*
+ * allocate a variable array, returned value is an array of pointers.
+ * Caller can specify length of array by count.
+ */
+void *cfs_array_alloc(int count, unsigned int size);
+void cfs_array_free(void *vars);
+
+#define LASSERT_ATOMIC_ENABLED (1)
+
+#if LASSERT_ATOMIC_ENABLED
+
+/** assert value of @a is equal to @v */
+#define LASSERT_ATOMIC_EQ(a, v) \
+do { \
+ LASSERTF(atomic_read(a) == v, \
+ "value: %d\n", atomic_read((a))); \
+} while (0)
+
+/** assert value of @a is unequal to @v */
+#define LASSERT_ATOMIC_NE(a, v) \
+do { \
+ LASSERTF(atomic_read(a) != v, \
+ "value: %d\n", atomic_read((a))); \
+} while (0)
+
+/** assert value of @a is little than @v */
+#define LASSERT_ATOMIC_LT(a, v) \
+do { \
+ LASSERTF(atomic_read(a) < v, \
+ "value: %d\n", atomic_read((a))); \
+} while (0)
+
+/** assert value of @a is little/equal to @v */
+#define LASSERT_ATOMIC_LE(a, v) \
+do { \
+ LASSERTF(atomic_read(a) <= v, \
+ "value: %d\n", atomic_read((a))); \
+} while (0)
+
+/** assert value of @a is great than @v */
+#define LASSERT_ATOMIC_GT(a, v) \
+do { \
+ LASSERTF(atomic_read(a) > v, \
+ "value: %d\n", atomic_read((a))); \
+} while (0)
+
+/** assert value of @a is great/equal to @v */
+#define LASSERT_ATOMIC_GE(a, v) \
+do { \
+ LASSERTF(atomic_read(a) >= v, \
+ "value: %d\n", atomic_read((a))); \
+} while (0)
+
+/** assert value of @a is great than @v1 and little than @v2 */
+#define LASSERT_ATOMIC_GT_LT(a, v1, v2) \
+do { \
+ int __v = atomic_read(a); \
+ LASSERTF(__v > v1 && __v < v2, "value: %d\n", __v); \
+} while (0)
+
+/** assert value of @a is great than @v1 and little/equal to @v2 */
+#define LASSERT_ATOMIC_GT_LE(a, v1, v2) \
+do { \
+ int __v = atomic_read(a); \
+ LASSERTF(__v > v1 && __v <= v2, "value: %d\n", __v); \
+} while (0)
+
+/** assert value of @a is great/equal to @v1 and little than @v2 */
+#define LASSERT_ATOMIC_GE_LT(a, v1, v2) \
+do { \
+ int __v = atomic_read(a); \
+ LASSERTF(__v >= v1 && __v < v2, "value: %d\n", __v); \
+} while (0)
+
+/** assert value of @a is great/equal to @v1 and little/equal to @v2 */
+#define LASSERT_ATOMIC_GE_LE(a, v1, v2) \
+do { \
+ int __v = atomic_read(a); \
+ LASSERTF(__v >= v1 && __v <= v2, "value: %d\n", __v); \
+} while (0)
+
+#else /* !LASSERT_ATOMIC_ENABLED */
+
+#define LASSERT_ATOMIC_EQ(a, v) do {} while (0)
+#define LASSERT_ATOMIC_NE(a, v) do {} while (0)
+#define LASSERT_ATOMIC_LT(a, v) do {} while (0)
+#define LASSERT_ATOMIC_LE(a, v) do {} while (0)
+#define LASSERT_ATOMIC_GT(a, v) do {} while (0)
+#define LASSERT_ATOMIC_GE(a, v) do {} while (0)
+#define LASSERT_ATOMIC_GT_LT(a, v1, v2) do {} while (0)
+#define LASSERT_ATOMIC_GT_LE(a, v1, v2) do {} while (0)
+#define LASSERT_ATOMIC_GE_LT(a, v1, v2) do {} while (0)
+#define LASSERT_ATOMIC_GE_LE(a, v1, v2) do {} while (0)
+
+#endif /* LASSERT_ATOMIC_ENABLED */
+
+#define LASSERT_ATOMIC_ZERO(a) LASSERT_ATOMIC_EQ(a, 0)
+#define LASSERT_ATOMIC_POS(a) LASSERT_ATOMIC_GT(a, 0)
+
+#define CFS_ALLOC_PTR(ptr) LIBCFS_ALLOC(ptr, sizeof(*(ptr)))
+#define CFS_FREE_PTR(ptr) LIBCFS_FREE(ptr, sizeof(*(ptr)))
+
+/*
+ * percpu partition lock
+ *
+ * There are some use-cases like this in Lustre:
+ * . each CPU partition has it's own private data which is frequently changed,
+ * and mostly by the local CPU partition.
+ * . all CPU partitions share some global data, these data are rarely changed.
+ *
+ * LNet is typical example.
+ * CPU partition lock is designed for this kind of use-cases:
+ * . each CPU partition has it's own private lock
+ * . change on private data just needs to take the private lock
+ * . read on shared data just needs to take _any_ of private locks
+ * . change on shared data needs to take _all_ private locks,
+ * which is slow and should be really rare.
+ */
+
+enum {
+ CFS_PERCPT_LOCK_EX = -1, /* negative */
+};
+
+struct cfs_percpt_lock {
+ /* cpu-partition-table for this lock */
+ struct cfs_cpt_table *pcl_cptab;
+ /* exclusively locked */
+ unsigned int pcl_locked;
+ /* private lock table */
+ spinlock_t **pcl_locks;
+};
+
+/* return number of private locks */
+static inline int
+cfs_percpt_lock_num(struct cfs_percpt_lock *pcl)
+{
+ return cfs_cpt_number(pcl->pcl_cptab);
+}
+
+/*
+ * create a cpu-partition lock based on CPU partition table \a cptab,
+ * each private lock has extra \a psize bytes padding data
+ */
+struct cfs_percpt_lock *cfs_percpt_lock_alloc(struct cfs_cpt_table *cptab);
+/* destroy a cpu-partition lock */
+void cfs_percpt_lock_free(struct cfs_percpt_lock *pcl);
+
+/* lock private lock \a index of \a pcl */
+void cfs_percpt_lock(struct cfs_percpt_lock *pcl, int index);
+/* unlock private lock \a index of \a pcl */
+void cfs_percpt_unlock(struct cfs_percpt_lock *pcl, int index);
+/* create percpt (atomic) refcount based on @cptab */
+atomic_t **cfs_percpt_atomic_alloc(struct cfs_cpt_table *cptab, int val);
+/* destroy percpt refcount */
+void cfs_percpt_atomic_free(atomic_t **refs);
+/* return sum of all percpu refs */
+int cfs_percpt_atomic_summary(atomic_t **refs);
+
+/** Compile-time assertion.
+
+ * Check an invariant described by a constant expression at compile time by
+ * forcing a compiler error if it does not hold. \a cond must be a constant
+ * expression as defined by the ISO C Standard:
+ *
+ * 6.8.4.2 The switch statement
+ * ....
+ * [#3] The expression of each case label shall be an integer
+ * constant expression and no two of the case constant
+ * expressions in the same switch statement shall have the same
+ * value after conversion...
+ *
+ */
+#define CLASSERT(cond) do {switch (42) {case (cond): case 0: break; } } while (0)
+
+/* support decl needed both by kernel and liblustre */
+int libcfs_isknown_lnd(int type);
+char *libcfs_lnd2modname(int type);
+char *libcfs_lnd2str(int type);
+int libcfs_str2lnd(const char *str);
+char *libcfs_net2str(__u32 net);
+char *libcfs_nid2str(lnet_nid_t nid);
+__u32 libcfs_str2net(const char *str);
+lnet_nid_t libcfs_str2nid(const char *str);
+int libcfs_str2anynid(lnet_nid_t *nid, const char *str);
+char *libcfs_id2str(lnet_process_id_t id);
+void cfs_free_nidlist(struct list_head *list);
+int cfs_parse_nidlist(char *str, int len, struct list_head *list);
+int cfs_match_nid(lnet_nid_t nid, struct list_head *list);
+
+/** \addtogroup lnet_addr
+ * @{ */
+/* how an LNET NID encodes net:address */
+/** extract the address part of an lnet_nid_t */
+#define LNET_NIDADDR(nid) ((__u32)((nid) & 0xffffffff))
+/** extract the network part of an lnet_nid_t */
+#define LNET_NIDNET(nid) ((__u32)(((nid) >> 32)) & 0xffffffff)
+/** make an lnet_nid_t from a network part and an address part */
+#define LNET_MKNID(net, addr) ((((__u64)(net))<<32)|((__u64)(addr)))
+/* how net encodes type:number */
+#define LNET_NETNUM(net) ((net) & 0xffff)
+#define LNET_NETTYP(net) (((net) >> 16) & 0xffff)
+#define LNET_MKNET(typ, num) ((((__u32)(typ))<<16)|((__u32)(num)))
+/** @} lnet_addr */
+
+/* max value for numeric network address */
+#define MAX_NUMERIC_VALUE 0xffffffff
+
+/* implication */
+#define ergo(a, b) (!(a) || (b))
+/* logical equivalence */
+#define equi(a, b) (!!(a) == !!(b))
+
+/* --------------------------------------------------------------------
+ * Light-weight trace
+ * Support for temporary event tracing with minimal Heisenberg effect.
+ * -------------------------------------------------------------------- */
+
+struct libcfs_device_userstate {
+ int ldu_memhog_pages;
+ struct page *ldu_memhog_root_page;
+};
+
+#define MKSTR(ptr) ((ptr)) ? (ptr) : ""
+
+static inline int cfs_size_round4(int val)
+{
+ return (val + 3) & (~0x3);
+}
+
+#ifndef HAVE_CFS_SIZE_ROUND
+static inline int cfs_size_round(int val)
+{
+ return (val + 7) & (~0x7);
+}
+
+#define HAVE_CFS_SIZE_ROUND
+#endif
+
+static inline int cfs_size_round16(int val)
+{
+ return (val + 0xf) & (~0xf);
+}
+
+static inline int cfs_size_round32(int val)
+{
+ return (val + 0x1f) & (~0x1f);
+}
+
+static inline int cfs_size_round0(int val)
+{
+ if (!val)
+ return 0;
+ return (val + 1 + 7) & (~0x7);
+}
+
+static inline size_t cfs_round_strlen(char *fset)
+{
+ return (size_t)cfs_size_round((int)strlen(fset) + 1);
+}
+
+/* roundup \a val to power2 */
+static inline unsigned int cfs_power2_roundup(unsigned int val)
+{
+ if (val != LOWEST_BIT_SET(val)) { /* not a power of 2 already */
+ do {
+ val &= ~LOWEST_BIT_SET(val);
+ } while (val != LOWEST_BIT_SET(val));
+ /* ...and round up */
+ val <<= 1;
+ }
+ return val;
+}
+
+#define LOGL(var, len, ptr) \
+do { \
+ if (var) \
+ memcpy((char *)ptr, (const char *)var, len); \
+ ptr += cfs_size_round(len); \
+} while (0)
+
+#define LOGU(var, len, ptr) \
+do { \
+ if (var) \
+ memcpy((char *)var, (const char *)ptr, len); \
+ ptr += cfs_size_round(len); \
+} while (0)
+
+#define LOGL0(var, len, ptr) \
+do { \
+ if (!len) \
+ break; \
+ memcpy((char *)ptr, (const char *)var, len); \
+ *((char *)(ptr) + len) = 0; \
+ ptr += cfs_size_round(len + 1); \
+} while (0)
+
+/**
+ * Lustre Network Driver types.
+ */
+enum {
+ /* Only add to these values (i.e. don't ever change or redefine them):
+ * network addresses depend on them... */
+ QSWLND = 1,
+ SOCKLND = 2,
+ GMLND = 3, /* obsolete, keep it so that libcfs_nid2str works */
+ PTLLND = 4,
+ O2IBLND = 5,
+ CIBLND = 6,
+ OPENIBLND = 7,
+ IIBLND = 8,
+ LOLND = 9,
+ RALND = 10,
+ VIBLND = 11,
+ MXLND = 12,
+ GNILND = 13,
+};
+
+#endif
diff --git a/drivers/staging/lustre/include/linux/libcfs/libcfs_string.h b/drivers/staging/lustre/include/linux/libcfs/libcfs_string.h
new file mode 100644
index 000000000..509dc1e5c
--- /dev/null
+++ b/drivers/staging/lustre/include/linux/libcfs/libcfs_string.h
@@ -0,0 +1,107 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2012, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ *
+ * libcfs/include/libcfs/libcfs_string.h
+ *
+ * Generic string manipulation functions.
+ *
+ * Author: Nathan Rutman <nathan.rutman@sun.com>
+ */
+
+#ifndef __LIBCFS_STRING_H__
+#define __LIBCFS_STRING_H__
+
+/* libcfs_string.c */
+/* string comparison ignoring case */
+int cfs_strncasecmp(const char *s1, const char *s2, size_t n);
+/* Convert a text string to a bitmask */
+int cfs_str2mask(const char *str, const char *(*bit2str)(int bit),
+ int *oldmask, int minmask, int allmask);
+/* trim leading and trailing space characters */
+char *cfs_firststr(char *str, size_t size);
+
+/**
+ * Structure to represent NULL-less strings.
+ */
+struct cfs_lstr {
+ char *ls_str;
+ int ls_len;
+};
+
+/*
+ * Structure to represent \<range_expr\> token of the syntax.
+ */
+struct cfs_range_expr {
+ /*
+ * Link to cfs_expr_list::el_exprs.
+ */
+ struct list_head re_link;
+ __u32 re_lo;
+ __u32 re_hi;
+ __u32 re_stride;
+};
+
+struct cfs_expr_list {
+ struct list_head el_link;
+ struct list_head el_exprs;
+};
+
+char *cfs_trimwhite(char *str);
+int cfs_gettok(struct cfs_lstr *next, char delim, struct cfs_lstr *res);
+int cfs_str2num_check(char *str, int nob, unsigned *num,
+ unsigned min, unsigned max);
+int cfs_expr_list_match(__u32 value, struct cfs_expr_list *expr_list);
+int cfs_expr_list_values(struct cfs_expr_list *expr_list,
+ int max, __u32 **values);
+static inline void
+cfs_expr_list_values_free(__u32 *values, int num)
+{
+ /* This array is allocated by LIBCFS_ALLOC(), so it shouldn't be freed
+ * by OBD_FREE() if it's called by module other than libcfs & LNet,
+ * otherwise we will see fake memory leak */
+ LIBCFS_FREE(values, num * sizeof(values[0]));
+}
+
+void cfs_expr_list_free(struct cfs_expr_list *expr_list);
+int cfs_expr_list_parse(char *str, int len, unsigned min, unsigned max,
+ struct cfs_expr_list **elpp);
+void cfs_expr_list_free_list(struct list_head *list);
+int cfs_ip_addr_parse(char *str, int len, struct list_head *list);
+int cfs_ip_addr_match(__u32 addr, struct list_head *list);
+void cfs_ip_addr_free(struct list_head *list);
+
+#define strtoul(str, endp, base) simple_strtoul(str, endp, base)
+
+#endif
diff --git a/drivers/staging/lustre/include/linux/libcfs/libcfs_time.h b/drivers/staging/lustre/include/linux/libcfs/libcfs_time.h
new file mode 100644
index 000000000..5de6da085
--- /dev/null
+++ b/drivers/staging/lustre/include/linux/libcfs/libcfs_time.h
@@ -0,0 +1,131 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ *
+ * libcfs/include/libcfs/libcfs_time.h
+ *
+ * Time functions.
+ *
+ */
+
+#ifndef __LIBCFS_TIME_H__
+#define __LIBCFS_TIME_H__
+/*
+ * generic time manipulation functions.
+ */
+
+static inline unsigned long cfs_time_add(unsigned long t, long d)
+{
+ return (unsigned long)(t + d);
+}
+
+static inline unsigned long cfs_time_sub(unsigned long t1, unsigned long t2)
+{
+ return (unsigned long)(t1 - t2);
+}
+
+static inline int cfs_time_after(unsigned long t1, unsigned long t2)
+{
+ return time_before(t2, t1);
+}
+
+static inline int cfs_time_aftereq(unsigned long t1, unsigned long t2)
+{
+ return time_before_eq(t2, t1);
+}
+
+static inline unsigned long cfs_time_shift(int seconds)
+{
+ return cfs_time_add(cfs_time_current(), cfs_time_seconds(seconds));
+}
+
+static inline long cfs_timeval_sub(struct timeval *large, struct timeval *small,
+ struct timeval *result)
+{
+ long r = (long)(
+ (large->tv_sec - small->tv_sec) * ONE_MILLION +
+ (large->tv_usec - small->tv_usec));
+ if (result != NULL) {
+ result->tv_usec = r % ONE_MILLION;
+ result->tv_sec = r / ONE_MILLION;
+ }
+ return r;
+}
+
+static inline void cfs_slow_warning(unsigned long now, int seconds, char *msg)
+{
+ if (cfs_time_after(cfs_time_current(),
+ cfs_time_add(now, cfs_time_seconds(15))))
+ CERROR("slow %s "CFS_TIME_T" sec\n", msg,
+ cfs_duration_sec(cfs_time_sub(cfs_time_current(), now)));
+}
+
+#define CFS_RATELIMIT(seconds) \
+({ \
+ /* \
+ * XXX nikita: non-portable initializer \
+ */ \
+ static time_t __next_message; \
+ int result; \
+ \
+ if (cfs_time_after(cfs_time_current(), __next_message)) \
+ result = 1; \
+ else { \
+ __next_message = cfs_time_shift(seconds); \
+ result = 0; \
+ } \
+ result; \
+})
+
+/*
+ * helper function similar to do_gettimeofday() of Linux kernel
+ */
+static inline void cfs_fs_timeval(struct timeval *tv)
+{
+ struct timespec time;
+
+ cfs_fs_time_current(&time);
+ cfs_fs_time_usec(&time, tv);
+}
+
+/*
+ * return valid time-out based on user supplied one. Currently we only check
+ * that time-out is not shorted than allowed.
+ */
+static inline long cfs_timeout_cap(long timeout)
+{
+ if (timeout < CFS_TICK)
+ timeout = CFS_TICK;
+ return timeout;
+}
+
+#endif
diff --git a/drivers/staging/lustre/include/linux/libcfs/libcfs_workitem.h b/drivers/staging/lustre/include/linux/libcfs/libcfs_workitem.h
new file mode 100644
index 000000000..5cc64f327
--- /dev/null
+++ b/drivers/staging/lustre/include/linux/libcfs/libcfs_workitem.h
@@ -0,0 +1,110 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2012, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ *
+ * libcfs/include/libcfs/libcfs_workitem.h
+ *
+ * Author: Isaac Huang <he.h.huang@oracle.com>
+ * Liang Zhen <zhen.liang@sun.com>
+ *
+ * A workitems is deferred work with these semantics:
+ * - a workitem always runs in thread context.
+ * - a workitem can be concurrent with other workitems but is strictly
+ * serialized with respect to itself.
+ * - no CPU affinity, a workitem does not necessarily run on the same CPU
+ * that schedules it. However, this might change in the future.
+ * - if a workitem is scheduled again before it has a chance to run, it
+ * runs only once.
+ * - if a workitem is scheduled while it runs, it runs again after it
+ * completes; this ensures that events occurring while other events are
+ * being processed receive due attention. This behavior also allows a
+ * workitem to reschedule itself.
+ *
+ * Usage notes:
+ * - a workitem can sleep but it should be aware of how that sleep might
+ * affect others.
+ * - a workitem runs inside a kernel thread so there's no user space to access.
+ * - do not use a workitem if the scheduling latency can't be tolerated.
+ *
+ * When wi_action returns non-zero, it means the workitem has either been
+ * freed or reused and workitem scheduler won't touch it any more.
+ */
+
+#ifndef __LIBCFS_WORKITEM_H__
+#define __LIBCFS_WORKITEM_H__
+
+struct cfs_wi_sched;
+
+void cfs_wi_sched_destroy(struct cfs_wi_sched *);
+int cfs_wi_sched_create(char *name, struct cfs_cpt_table *cptab, int cpt,
+ int nthrs, struct cfs_wi_sched **);
+
+struct cfs_workitem;
+
+typedef int (*cfs_wi_action_t) (struct cfs_workitem *);
+typedef struct cfs_workitem {
+ /** chain on runq or rerunq */
+ struct list_head wi_list;
+ /** working function */
+ cfs_wi_action_t wi_action;
+ /** arg for working function */
+ void *wi_data;
+ /** in running */
+ unsigned short wi_running:1;
+ /** scheduled */
+ unsigned short wi_scheduled:1;
+} cfs_workitem_t;
+
+static inline void
+cfs_wi_init(cfs_workitem_t *wi, void *data, cfs_wi_action_t action)
+{
+ INIT_LIST_HEAD(&wi->wi_list);
+
+ wi->wi_running = 0;
+ wi->wi_scheduled = 0;
+ wi->wi_data = data;
+ wi->wi_action = action;
+}
+
+void cfs_wi_schedule(struct cfs_wi_sched *sched, cfs_workitem_t *wi);
+int cfs_wi_deschedule(struct cfs_wi_sched *sched, cfs_workitem_t *wi);
+void cfs_wi_exit(struct cfs_wi_sched *sched, cfs_workitem_t *wi);
+
+int cfs_wi_startup(void);
+void cfs_wi_shutdown(void);
+
+/** # workitem scheduler loops before reschedule */
+#define CFS_WI_RESCHED 128
+
+#endif /* __LIBCFS_WORKITEM_H__ */
diff --git a/drivers/staging/lustre/include/linux/libcfs/linux/libcfs.h b/drivers/staging/lustre/include/linux/libcfs/linux/libcfs.h
new file mode 100644
index 000000000..4fe50841e
--- /dev/null
+++ b/drivers/staging/lustre/include/linux/libcfs/linux/libcfs.h
@@ -0,0 +1,147 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2012, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ */
+
+#ifndef __LIBCFS_LINUX_LIBCFS_H__
+#define __LIBCFS_LINUX_LIBCFS_H__
+
+#ifndef __LIBCFS_LIBCFS_H__
+#error Do not #include this file directly. #include <linux/libcfs/libcfs.h> instead
+#endif
+
+
+#include <linux/bitops.h>
+#include <linux/compiler.h>
+#include <linux/ctype.h>
+#include <linux/errno.h>
+#include <linux/file.h>
+#include <linux/fs.h>
+#include <linux/highmem.h>
+#include <linux/interrupt.h>
+#include <linux/kallsyms.h>
+#include <linux/kernel.h>
+#include <linux/kmod.h>
+#include <linux/kthread.h>
+#include <linux/miscdevice.h>
+#include <linux/mm.h>
+#include <linux/mm_inline.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/mutex.h>
+#include <linux/notifier.h>
+#include <linux/proc_fs.h>
+#include <linux/random.h>
+#include <linux/rbtree.h>
+#include <linux/rwsem.h>
+#include <linux/scatterlist.h>
+#include <linux/sched.h>
+#include <linux/signal.h>
+#include <linux/slab.h>
+#include <linux/smp.h>
+#include <linux/stat.h>
+#include <linux/string.h>
+#include <linux/time.h>
+#include <linux/timer.h>
+#include <linux/types.h>
+#include <linux/unistd.h>
+#include <linux/vmalloc.h>
+#include <net/sock.h>
+#include <linux/atomic.h>
+#include <asm/div64.h>
+#include <linux/timex.h>
+#include <linux/uaccess.h>
+#include <stdarg.h>
+#include "linux-cpu.h"
+#include "linux-time.h"
+#include "linux-mem.h"
+
+
+#define LUSTRE_TRACE_SIZE (THREAD_SIZE >> 5)
+
+#if !defined(__x86_64__)
+# ifdef __ia64__
+# define CDEBUG_STACK() (THREAD_SIZE - \
+ ((unsigned long)__builtin_dwarf_cfa() & \
+ (THREAD_SIZE - 1)))
+# else
+# define CDEBUG_STACK() (THREAD_SIZE - \
+ ((unsigned long)__builtin_frame_address(0) & \
+ (THREAD_SIZE - 1)))
+# endif /* __ia64__ */
+
+#define __CHECK_STACK(msgdata, mask, cdls) \
+do { \
+ if (unlikely(CDEBUG_STACK() > libcfs_stack)) { \
+ LIBCFS_DEBUG_MSG_DATA_INIT(msgdata, D_WARNING, NULL); \
+ libcfs_stack = CDEBUG_STACK(); \
+ libcfs_debug_msg(msgdata, \
+ "maximum lustre stack %lu\n", \
+ CDEBUG_STACK()); \
+ (msgdata)->msg_mask = mask; \
+ (msgdata)->msg_cdls = cdls; \
+ dump_stack(); \
+ /*panic("LBUG");*/ \
+ } \
+} while (0)
+#define CFS_CHECK_STACK(msgdata, mask, cdls) __CHECK_STACK(msgdata, mask, cdls)
+#else /* __x86_64__ */
+#define CFS_CHECK_STACK(msgdata, mask, cdls) do {} while (0)
+#define CDEBUG_STACK() (0L)
+#endif /* __x86_64__ */
+
+/* initial pid */
+#define LUSTRE_LNET_PID 12345
+
+#define __current_nesting_level() (0)
+
+/**
+ * Platform specific declarations for cfs_curproc API (libcfs/curproc.h)
+ *
+ * Implementation is in linux-curproc.c
+ */
+#define CFS_CURPROC_COMM_MAX (sizeof((struct task_struct *)0)->comm)
+
+#include <linux/capability.h>
+
+/* long integer with size equal to pointer */
+typedef unsigned long ulong_ptr_t;
+typedef long long_ptr_t;
+
+#ifndef WITH_WATCHDOG
+#define WITH_WATCHDOG
+#endif
+
+
+#endif /* _LINUX_LIBCFS_H */
diff --git a/drivers/staging/lustre/include/linux/libcfs/linux/linux-cpu.h b/drivers/staging/lustre/include/linux/libcfs/linux/linux-cpu.h
new file mode 100644
index 000000000..520209f17
--- /dev/null
+++ b/drivers/staging/lustre/include/linux/libcfs/linux/linux-cpu.h
@@ -0,0 +1,82 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 021110-1307, USA
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ *
+ * libcfs/include/libcfs/linux/linux-mem.h
+ *
+ * Basic library routines.
+ *
+ * Author: liang@whamcloud.com
+ */
+
+#ifndef __LIBCFS_LINUX_CPU_H__
+#define __LIBCFS_LINUX_CPU_H__
+
+#ifndef __LIBCFS_LIBCFS_H__
+#error Do not #include this file directly. #include <linux/libcfs/libcfs.h> instead
+#endif
+
+#include <linux/cpu.h>
+#include <linux/cpuset.h>
+#include <linux/topology.h>
+
+#ifdef CONFIG_SMP
+
+#define HAVE_LIBCFS_CPT
+
+/** virtual processing unit */
+struct cfs_cpu_partition {
+ /* CPUs mask for this partition */
+ cpumask_t *cpt_cpumask;
+ /* nodes mask for this partition */
+ nodemask_t *cpt_nodemask;
+ /* spread rotor for NUMA allocator */
+ unsigned cpt_spread_rotor;
+};
+
+/** descriptor for CPU partitions */
+struct cfs_cpt_table {
+ /* version, reserved for hotplug */
+ unsigned ctb_version;
+ /* spread rotor for NUMA allocator */
+ unsigned ctb_spread_rotor;
+ /* # of CPU partitions */
+ unsigned ctb_nparts;
+ /* partitions tables */
+ struct cfs_cpu_partition *ctb_parts;
+ /* shadow HW CPU to CPU partition ID */
+ int *ctb_cpu2cpt;
+ /* all cpus in this partition table */
+ cpumask_t *ctb_cpumask;
+ /* all nodes in this partition table */
+ nodemask_t *ctb_nodemask;
+};
+
+#endif /* CONFIG_SMP */
+#endif /* __LIBCFS_LINUX_CPU_H__ */
diff --git a/drivers/staging/lustre/include/linux/libcfs/linux/linux-mem.h b/drivers/staging/lustre/include/linux/libcfs/linux/linux-mem.h
new file mode 100644
index 000000000..0f2fd79e5
--- /dev/null
+++ b/drivers/staging/lustre/include/linux/libcfs/linux/linux-mem.h
@@ -0,0 +1,80 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2011, 2012, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ *
+ * libcfs/include/libcfs/linux/linux-mem.h
+ *
+ * Basic library routines.
+ */
+
+#ifndef __LIBCFS_LINUX_CFS_MEM_H__
+#define __LIBCFS_LINUX_CFS_MEM_H__
+
+#ifndef __LIBCFS_LIBCFS_H__
+#error Do not #include this file directly. #include <linux/libcfs/libcfs.h> instead
+#endif
+
+#include <linux/mm.h>
+#include <linux/vmalloc.h>
+#include <linux/pagemap.h>
+#include <linux/slab.h>
+#include <linux/memcontrol.h>
+#include <linux/mm_inline.h>
+
+#ifndef HAVE_LIBCFS_CPT
+/* Need this for cfs_cpt_table */
+#include "../libcfs_cpu.h"
+#endif
+
+#define CFS_PAGE_MASK (~((__u64)PAGE_CACHE_SIZE-1))
+#define page_index(p) ((p)->index)
+
+#define memory_pressure_get() (current->flags & PF_MEMALLOC)
+#define memory_pressure_set() do { current->flags |= PF_MEMALLOC; } while (0)
+#define memory_pressure_clr() do { current->flags &= ~PF_MEMALLOC; } while (0)
+
+#if BITS_PER_LONG == 32
+/* limit to lowmem on 32-bit systems */
+#define NUM_CACHEPAGES \
+ min(totalram_pages, 1UL << (30 - PAGE_CACHE_SHIFT) * 3 / 4)
+#else
+#define NUM_CACHEPAGES totalram_pages
+#endif
+
+#define DECL_MMSPACE mm_segment_t __oldfs
+#define MMSPACE_OPEN \
+ do { __oldfs = get_fs(); set_fs(get_ds()); } while (0)
+#define MMSPACE_CLOSE set_fs(__oldfs)
+
+#endif /* __LINUX_CFS_MEM_H__ */
diff --git a/drivers/staging/lustre/include/linux/libcfs/linux/linux-time.h b/drivers/staging/lustre/include/linux/libcfs/linux/linux-time.h
new file mode 100644
index 000000000..0fc490bac
--- /dev/null
+++ b/drivers/staging/lustre/include/linux/libcfs/linux/linux-time.h
@@ -0,0 +1,144 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ *
+ * libcfs/include/libcfs/linux/linux-time.h
+ *
+ * Implementation of portable time API for Linux (kernel and user-level).
+ *
+ * Author: Nikita Danilov <nikita@clusterfs.com>
+ */
+
+#ifndef __LIBCFS_LINUX_LINUX_TIME_H__
+#define __LIBCFS_LINUX_LINUX_TIME_H__
+
+#ifndef __LIBCFS_LIBCFS_H__
+#error Do not #include this file directly. #include <linux/libcfs/libcfs.h> instead
+#endif
+
+#define ONE_BILLION ((u_int64_t)1000000000)
+#define ONE_MILLION 1000000
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/time.h>
+#include <asm/div64.h>
+
+/*
+ * post 2.5 kernels.
+ */
+
+#include <linux/jiffies.h>
+
+
+static inline void cfs_fs_time_usec(struct timespec *t, struct timeval *v)
+{
+ v->tv_sec = t->tv_sec;
+ v->tv_usec = t->tv_nsec / 1000;
+}
+
+/*
+ * Generic kernel stuff
+ */
+
+static inline unsigned long cfs_time_current(void)
+{
+ return jiffies;
+}
+
+static inline void cfs_fs_time_current(struct timespec *t)
+{
+ *t = CURRENT_TIME;
+}
+
+static inline time_t cfs_fs_time_sec(struct timespec *t)
+{
+ return t->tv_sec;
+}
+
+static inline long cfs_time_seconds(int seconds)
+{
+ return ((long)seconds) * HZ;
+}
+
+static inline time_t cfs_duration_sec(long d)
+{
+ return d / HZ;
+}
+
+static inline void cfs_duration_usec(long d, struct timeval *s)
+{
+#if (BITS_PER_LONG == 32) && (HZ > 4096)
+ __u64 t;
+
+ s->tv_sec = d / HZ;
+ t = (d - (long)s->tv_sec * HZ) * ONE_MILLION;
+ do_div(t, HZ);
+ s->tv_usec = t;
+#else
+ s->tv_sec = d / HZ;
+ s->tv_usec = ((d - (long)s->tv_sec * HZ) * ONE_MILLION) / HZ;
+#endif
+}
+
+#define cfs_time_current_64 get_jiffies_64
+
+static inline __u64 cfs_time_add_64(__u64 t, __u64 d)
+{
+ return t + d;
+}
+
+static inline __u64 cfs_time_shift_64(int seconds)
+{
+ return cfs_time_add_64(cfs_time_current_64(),
+ cfs_time_seconds(seconds));
+}
+
+static inline int cfs_time_before_64(__u64 t1, __u64 t2)
+{
+ return (__s64)t2 - (__s64)t1 > 0;
+}
+
+static inline int cfs_time_beforeq_64(__u64 t1, __u64 t2)
+{
+ return (__s64)t2 - (__s64)t1 >= 0;
+}
+
+/*
+ * One jiffy
+ */
+#define CFS_TICK (1)
+
+#define CFS_TIME_T "%lu"
+#define CFS_DURATION_T "%ld"
+
+#endif /* __LIBCFS_LINUX_LINUX_TIME_H__ */
diff --git a/drivers/staging/lustre/include/linux/lnet/api-support.h b/drivers/staging/lustre/include/linux/lnet/api-support.h
new file mode 100644
index 000000000..8f7fa28b5
--- /dev/null
+++ b/drivers/staging/lustre/include/linux/lnet/api-support.h
@@ -0,0 +1,44 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ */
+
+#ifndef __LNET_API_SUPPORT_H__
+#define __LNET_API_SUPPORT_H__
+
+#include "linux/api-support.h"
+
+#include "../libcfs/libcfs.h"
+#include "types.h"
+#include "lnet.h"
+
+#endif
diff --git a/drivers/staging/lustre/include/linux/lnet/api.h b/drivers/staging/lustre/include/linux/lnet/api.h
new file mode 100644
index 000000000..cd8651757
--- /dev/null
+++ b/drivers/staging/lustre/include/linux/lnet/api.h
@@ -0,0 +1,217 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ */
+
+#ifndef __LNET_API_H__
+#define __LNET_API_H__
+
+/** \defgroup lnet LNet
+ *
+ * The Lustre Networking subsystem.
+ *
+ * LNet is an asynchronous message-passing API, which provides an unreliable
+ * connectionless service that can't guarantee any order. It supports OFA IB,
+ * TCP/IP, and Cray Portals, and routes between heterogeneous networks.
+ *
+ * LNet can run both in OS kernel space and in userspace as a library.
+ * @{
+ */
+
+#include "../lnet/types.h"
+
+/** \defgroup lnet_init_fini Initialization and cleanup
+ * The LNet must be properly initialized before any LNet calls can be made.
+ * @{ */
+int LNetInit(void);
+void LNetFini(void);
+
+int LNetNIInit(lnet_pid_t requested_pid);
+int LNetNIFini(void);
+/** @} lnet_init_fini */
+
+/** \defgroup lnet_addr LNet addressing and basic types
+ *
+ * Addressing scheme and basic data types of LNet.
+ *
+ * The LNet API is memory-oriented, so LNet must be able to address not only
+ * end-points but also memory region within a process address space.
+ * An ::lnet_nid_t addresses an end-point. An ::lnet_pid_t identifies a process
+ * in a node. A portal represents an opening in the address space of a
+ * process. Match bits is criteria to identify a region of memory inside a
+ * portal, and offset specifies an offset within the memory region.
+ *
+ * LNet creates a table of portals for each process during initialization.
+ * This table has MAX_PORTALS entries and its size can't be dynamically
+ * changed. A portal stays empty until the owning process starts to add
+ * memory regions to it. A portal is sometimes called an index because
+ * it's an entry in the portals table of a process.
+ *
+ * \see LNetMEAttach
+ * @{ */
+int LNetGetId(unsigned int index, lnet_process_id_t *id);
+int LNetDist(lnet_nid_t nid, lnet_nid_t *srcnid, __u32 *order);
+void LNetSnprintHandle(char *str, int str_len, lnet_handle_any_t handle);
+
+/** @} lnet_addr */
+
+/** \defgroup lnet_me Match entries
+ *
+ * A match entry (abbreviated as ME) describes a set of criteria to accept
+ * incoming requests.
+ *
+ * A portal is essentially a match list plus a set of attributes. A match
+ * list is a chain of MEs. Each ME includes a pointer to a memory descriptor
+ * and a set of match criteria. The match criteria can be used to reject
+ * incoming requests based on process ID or the match bits provided in the
+ * request. MEs can be dynamically inserted into a match list by LNetMEAttach()
+ * and LNetMEInsert(), and removed from its list by LNetMEUnlink().
+ * @{ */
+int LNetMEAttach(unsigned int portal,
+ lnet_process_id_t match_id_in,
+ __u64 match_bits_in,
+ __u64 ignore_bits_in,
+ lnet_unlink_t unlink_in,
+ lnet_ins_pos_t pos_in,
+ lnet_handle_me_t *handle_out);
+
+int LNetMEInsert(lnet_handle_me_t current_in,
+ lnet_process_id_t match_id_in,
+ __u64 match_bits_in,
+ __u64 ignore_bits_in,
+ lnet_unlink_t unlink_in,
+ lnet_ins_pos_t position_in,
+ lnet_handle_me_t *handle_out);
+
+int LNetMEUnlink(lnet_handle_me_t current_in);
+/** @} lnet_me */
+
+/** \defgroup lnet_md Memory descriptors
+ *
+ * A memory descriptor contains information about a region of a user's
+ * memory (either in kernel or user space) and optionally points to an
+ * event queue where information about the operations performed on the
+ * memory descriptor are recorded. Memory descriptor is abbreviated as
+ * MD and can be used interchangeably with the memory region it describes.
+ *
+ * The LNet API provides two operations to create MDs: LNetMDAttach()
+ * and LNetMDBind(); one operation to unlink and release the resources
+ * associated with a MD: LNetMDUnlink().
+ * @{ */
+int LNetMDAttach(lnet_handle_me_t current_in,
+ lnet_md_t md_in,
+ lnet_unlink_t unlink_in,
+ lnet_handle_md_t *handle_out);
+
+int LNetMDBind(lnet_md_t md_in,
+ lnet_unlink_t unlink_in,
+ lnet_handle_md_t *handle_out);
+
+int LNetMDUnlink(lnet_handle_md_t md_in);
+/** @} lnet_md */
+
+/** \defgroup lnet_eq Events and event queues
+ *
+ * Event queues (abbreviated as EQ) are used to log operations performed on
+ * local MDs. In particular, they signal the completion of a data transmission
+ * into or out of a MD. They can also be used to hold acknowledgments for
+ * completed PUT operations and indicate when a MD has been unlinked. Multiple
+ * MDs can share a single EQ. An EQ may have an optional event handler
+ * associated with it. If an event handler exists, it will be run for each
+ * event that is deposited into the EQ.
+ *
+ * In addition to the lnet_handle_eq_t, the LNet API defines two types
+ * associated with events: The ::lnet_event_kind_t defines the kinds of events
+ * that can be stored in an EQ. The lnet_event_t defines a structure that
+ * holds the information about with an event.
+ *
+ * There are five functions for dealing with EQs: LNetEQAlloc() is used to
+ * create an EQ and allocate the resources needed, while LNetEQFree()
+ * releases these resources and free the EQ. LNetEQGet() retrieves the next
+ * event from an EQ, and LNetEQWait() can be used to block a process until
+ * an EQ has at least one event. LNetEQPoll() can be used to test or wait
+ * on multiple EQs.
+ * @{ */
+int LNetEQAlloc(unsigned int count_in,
+ lnet_eq_handler_t handler,
+ lnet_handle_eq_t *handle_out);
+
+int LNetEQFree(lnet_handle_eq_t eventq_in);
+
+int LNetEQGet(lnet_handle_eq_t eventq_in,
+ lnet_event_t *event_out);
+
+int LNetEQWait(lnet_handle_eq_t eventq_in,
+ lnet_event_t *event_out);
+
+int LNetEQPoll(lnet_handle_eq_t *eventqs_in,
+ int neq_in,
+ int timeout_ms,
+ lnet_event_t *event_out,
+ int *which_eq_out);
+/** @} lnet_eq */
+
+/** \defgroup lnet_data Data movement operations
+ *
+ * The LNet API provides two data movement operations: LNetPut()
+ * and LNetGet().
+ * @{ */
+int LNetPut(lnet_nid_t self,
+ lnet_handle_md_t md_in,
+ lnet_ack_req_t ack_req_in,
+ lnet_process_id_t target_in,
+ unsigned int portal_in,
+ __u64 match_bits_in,
+ unsigned int offset_in,
+ __u64 hdr_data_in);
+
+int LNetGet(lnet_nid_t self,
+ lnet_handle_md_t md_in,
+ lnet_process_id_t target_in,
+ unsigned int portal_in,
+ __u64 match_bits_in,
+ unsigned int offset_in);
+/** @} lnet_data */
+
+/** \defgroup lnet_misc Miscellaneous operations.
+ * Miscellaneous operations.
+ * @{ */
+
+int LNetSetLazyPortal(int portal);
+int LNetClearLazyPortal(int portal);
+int LNetCtl(unsigned int cmd, void *arg);
+int LNetSetAsync(lnet_process_id_t id, int nasync);
+
+/** @} lnet_misc */
+
+/** @} lnet */
+#endif
diff --git a/drivers/staging/lustre/include/linux/lnet/lib-lnet.h b/drivers/staging/lustre/include/linux/lnet/lib-lnet.h
new file mode 100644
index 000000000..0038d29a3
--- /dev/null
+++ b/drivers/staging/lustre/include/linux/lnet/lib-lnet.h
@@ -0,0 +1,883 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2012, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ *
+ * lnet/include/lnet/lib-lnet.h
+ *
+ * Top level include for library side routines
+ */
+
+#ifndef __LNET_LIB_LNET_H__
+#define __LNET_LIB_LNET_H__
+
+#include "linux/lib-lnet.h"
+#include "../libcfs/libcfs.h"
+#include "types.h"
+#include "lnet.h"
+#include "lib-types.h"
+
+extern lnet_t the_lnet; /* THE network */
+
+#if defined(LNET_USE_LIB_FREELIST)
+/* 1 CPT, simplify implementation... */
+# define LNET_CPT_MAX_BITS 0
+
+#else /* KERNEL and no freelist */
+
+# if (BITS_PER_LONG == 32)
+/* 2 CPTs, allowing more CPTs might make us under memory pressure */
+# define LNET_CPT_MAX_BITS 1
+
+# else /* 64-bit system */
+/*
+ * 256 CPTs for thousands of CPUs, allowing more CPTs might make us
+ * under risk of consuming all lh_cookie.
+ */
+# define LNET_CPT_MAX_BITS 8
+# endif /* BITS_PER_LONG == 32 */
+#endif
+
+/* max allowed CPT number */
+#define LNET_CPT_MAX (1 << LNET_CPT_MAX_BITS)
+
+#define LNET_CPT_NUMBER (the_lnet.ln_cpt_number)
+#define LNET_CPT_BITS (the_lnet.ln_cpt_bits)
+#define LNET_CPT_MASK ((1ULL << LNET_CPT_BITS) - 1)
+
+/** exclusive lock */
+#define LNET_LOCK_EX CFS_PERCPT_LOCK_EX
+
+static inline int lnet_is_wire_handle_none(lnet_handle_wire_t *wh)
+{
+ return (wh->wh_interface_cookie == LNET_WIRE_HANDLE_COOKIE_NONE &&
+ wh->wh_object_cookie == LNET_WIRE_HANDLE_COOKIE_NONE);
+}
+
+static inline int lnet_md_exhausted(lnet_libmd_t *md)
+{
+ return (md->md_threshold == 0 ||
+ ((md->md_options & LNET_MD_MAX_SIZE) != 0 &&
+ md->md_offset + md->md_max_size > md->md_length));
+}
+
+static inline int lnet_md_unlinkable(lnet_libmd_t *md)
+{
+ /* Should unlink md when its refcount is 0 and either:
+ * - md has been flagged for deletion (by auto unlink or
+ * LNetM[DE]Unlink, in the latter case md may not be exhausted).
+ * - auto unlink is on and md is exhausted.
+ */
+ if (md->md_refcount != 0)
+ return 0;
+
+ if ((md->md_flags & LNET_MD_FLAG_ZOMBIE) != 0)
+ return 1;
+
+ return ((md->md_flags & LNET_MD_FLAG_AUTO_UNLINK) != 0 &&
+ lnet_md_exhausted(md));
+}
+
+#define lnet_cpt_table() (the_lnet.ln_cpt_table)
+#define lnet_cpt_current() cfs_cpt_current(the_lnet.ln_cpt_table, 1)
+
+static inline int
+lnet_cpt_of_cookie(__u64 cookie)
+{
+ unsigned int cpt = (cookie >> LNET_COOKIE_TYPE_BITS) & LNET_CPT_MASK;
+
+ /* LNET_CPT_NUMBER doesn't have to be power2, which means we can
+ * get illegal cpt from it's invalid cookie */
+ return cpt < LNET_CPT_NUMBER ? cpt : cpt % LNET_CPT_NUMBER;
+}
+
+static inline void
+lnet_res_lock(int cpt)
+{
+ cfs_percpt_lock(the_lnet.ln_res_lock, cpt);
+}
+
+static inline void
+lnet_res_unlock(int cpt)
+{
+ cfs_percpt_unlock(the_lnet.ln_res_lock, cpt);
+}
+
+static inline int
+lnet_res_lock_current(void)
+{
+ int cpt = lnet_cpt_current();
+
+ lnet_res_lock(cpt);
+ return cpt;
+}
+
+static inline void
+lnet_net_lock(int cpt)
+{
+ cfs_percpt_lock(the_lnet.ln_net_lock, cpt);
+}
+
+static inline void
+lnet_net_unlock(int cpt)
+{
+ cfs_percpt_unlock(the_lnet.ln_net_lock, cpt);
+}
+
+static inline int
+lnet_net_lock_current(void)
+{
+ int cpt = lnet_cpt_current();
+
+ lnet_net_lock(cpt);
+ return cpt;
+}
+
+#define LNET_LOCK() lnet_net_lock(LNET_LOCK_EX)
+#define LNET_UNLOCK() lnet_net_unlock(LNET_LOCK_EX)
+
+#define lnet_ptl_lock(ptl) spin_lock(&(ptl)->ptl_lock)
+#define lnet_ptl_unlock(ptl) spin_unlock(&(ptl)->ptl_lock)
+#define lnet_eq_wait_lock() spin_lock(&the_lnet.ln_eq_wait_lock)
+#define lnet_eq_wait_unlock() spin_unlock(&the_lnet.ln_eq_wait_lock)
+#define lnet_ni_lock(ni) spin_lock(&(ni)->ni_lock)
+#define lnet_ni_unlock(ni) spin_unlock(&(ni)->ni_lock)
+#define LNET_MUTEX_LOCK(m) mutex_lock(m)
+#define LNET_MUTEX_UNLOCK(m) mutex_unlock(m)
+
+#define MAX_PORTALS 64
+
+/* these are only used by code with LNET_USE_LIB_FREELIST, but we still
+ * exported them to !LNET_USE_LIB_FREELIST for easy implementation */
+#define LNET_FL_MAX_MES 2048
+#define LNET_FL_MAX_MDS 2048
+#define LNET_FL_MAX_EQS 512
+#define LNET_FL_MAX_MSGS 2048 /* Outstanding messages */
+
+#ifdef LNET_USE_LIB_FREELIST
+
+int lnet_freelist_init(lnet_freelist_t *fl, int n, int size);
+void lnet_freelist_fini(lnet_freelist_t *fl);
+
+static inline void *
+lnet_freelist_alloc(lnet_freelist_t *fl)
+{
+ /* ALWAYS called with liblock held */
+ lnet_freeobj_t *o;
+
+ if (list_empty(&fl->fl_list))
+ return NULL;
+
+ o = list_entry(fl->fl_list.next, lnet_freeobj_t, fo_list);
+ list_del(&o->fo_list);
+ return (void *)&o->fo_contents;
+}
+
+static inline void
+lnet_freelist_free(lnet_freelist_t *fl, void *obj)
+{
+ /* ALWAYS called with liblock held */
+ lnet_freeobj_t *o = list_entry(obj, lnet_freeobj_t, fo_contents);
+
+ list_add(&o->fo_list, &fl->fl_list);
+}
+
+static inline lnet_eq_t *
+lnet_eq_alloc(void)
+{
+ /* NEVER called with resource lock held */
+ struct lnet_res_container *rec = &the_lnet.ln_eq_container;
+ lnet_eq_t *eq;
+
+ LASSERT(LNET_CPT_NUMBER == 1);
+
+ lnet_res_lock(0);
+ eq = (lnet_eq_t *)lnet_freelist_alloc(&rec->rec_freelist);
+ lnet_res_unlock(0);
+
+ return eq;
+}
+
+static inline void
+lnet_eq_free_locked(lnet_eq_t *eq)
+{
+ /* ALWAYS called with resource lock held */
+ struct lnet_res_container *rec = &the_lnet.ln_eq_container;
+
+ LASSERT(LNET_CPT_NUMBER == 1);
+ lnet_freelist_free(&rec->rec_freelist, eq);
+}
+
+static inline void
+lnet_eq_free(lnet_eq_t *eq)
+{
+ lnet_res_lock(0);
+ lnet_eq_free_locked(eq);
+ lnet_res_unlock(0);
+}
+
+static inline lnet_libmd_t *
+lnet_md_alloc(lnet_md_t *umd)
+{
+ /* NEVER called with resource lock held */
+ struct lnet_res_container *rec = the_lnet.ln_md_containers[0];
+ lnet_libmd_t *md;
+
+ LASSERT(LNET_CPT_NUMBER == 1);
+
+ lnet_res_lock(0);
+ md = (lnet_libmd_t *)lnet_freelist_alloc(&rec->rec_freelist);
+ lnet_res_unlock(0);
+
+ if (md != NULL)
+ INIT_LIST_HEAD(&md->md_list);
+
+ return md;
+}
+
+static inline void
+lnet_md_free_locked(lnet_libmd_t *md)
+{
+ /* ALWAYS called with resource lock held */
+ struct lnet_res_container *rec = the_lnet.ln_md_containers[0];
+
+ LASSERT(LNET_CPT_NUMBER == 1);
+ lnet_freelist_free(&rec->rec_freelist, md);
+}
+
+static inline void
+lnet_md_free(lnet_libmd_t *md)
+{
+ lnet_res_lock(0);
+ lnet_md_free_locked(md);
+ lnet_res_unlock(0);
+}
+
+static inline lnet_me_t *
+lnet_me_alloc(void)
+{
+ /* NEVER called with resource lock held */
+ struct lnet_res_container *rec = the_lnet.ln_me_containers[0];
+ lnet_me_t *me;
+
+ LASSERT(LNET_CPT_NUMBER == 1);
+
+ lnet_res_lock(0);
+ me = (lnet_me_t *)lnet_freelist_alloc(&rec->rec_freelist);
+ lnet_res_unlock(0);
+
+ return me;
+}
+
+static inline void
+lnet_me_free_locked(lnet_me_t *me)
+{
+ /* ALWAYS called with resource lock held */
+ struct lnet_res_container *rec = the_lnet.ln_me_containers[0];
+
+ LASSERT(LNET_CPT_NUMBER == 1);
+ lnet_freelist_free(&rec->rec_freelist, me);
+}
+
+static inline void
+lnet_me_free(lnet_me_t *me)
+{
+ lnet_res_lock(0);
+ lnet_me_free_locked(me);
+ lnet_res_unlock(0);
+}
+
+static inline lnet_msg_t *
+lnet_msg_alloc(void)
+{
+ /* NEVER called with network lock held */
+ struct lnet_msg_container *msc = the_lnet.ln_msg_containers[0];
+ lnet_msg_t *msg;
+
+ LASSERT(LNET_CPT_NUMBER == 1);
+
+ lnet_net_lock(0);
+ msg = (lnet_msg_t *)lnet_freelist_alloc(&msc->msc_freelist);
+ lnet_net_unlock(0);
+
+ if (msg != NULL) {
+ /* NULL pointers, clear flags etc */
+ memset(msg, 0, sizeof(*msg));
+ }
+ return msg;
+}
+
+static inline void
+lnet_msg_free_locked(lnet_msg_t *msg)
+{
+ /* ALWAYS called with network lock held */
+ struct lnet_msg_container *msc = the_lnet.ln_msg_containers[0];
+
+ LASSERT(LNET_CPT_NUMBER == 1);
+ LASSERT(!msg->msg_onactivelist);
+ lnet_freelist_free(&msc->msc_freelist, msg);
+}
+
+static inline void
+lnet_msg_free(lnet_msg_t *msg)
+{
+ lnet_net_lock(0);
+ lnet_msg_free_locked(msg);
+ lnet_net_unlock(0);
+}
+
+#else /* !LNET_USE_LIB_FREELIST */
+
+static inline lnet_eq_t *
+lnet_eq_alloc(void)
+{
+ /* NEVER called with liblock held */
+ lnet_eq_t *eq;
+
+ LIBCFS_ALLOC(eq, sizeof(*eq));
+ return eq;
+}
+
+static inline void
+lnet_eq_free(lnet_eq_t *eq)
+{
+ /* ALWAYS called with resource lock held */
+ LIBCFS_FREE(eq, sizeof(*eq));
+}
+
+static inline lnet_libmd_t *
+lnet_md_alloc(lnet_md_t *umd)
+{
+ /* NEVER called with liblock held */
+ lnet_libmd_t *md;
+ unsigned int size;
+ unsigned int niov;
+
+ if ((umd->options & LNET_MD_KIOV) != 0) {
+ niov = umd->length;
+ size = offsetof(lnet_libmd_t, md_iov.kiov[niov]);
+ } else {
+ niov = ((umd->options & LNET_MD_IOVEC) != 0) ?
+ umd->length : 1;
+ size = offsetof(lnet_libmd_t, md_iov.iov[niov]);
+ }
+
+ LIBCFS_ALLOC(md, size);
+
+ if (md != NULL) {
+ /* Set here in case of early free */
+ md->md_options = umd->options;
+ md->md_niov = niov;
+ INIT_LIST_HEAD(&md->md_list);
+ }
+
+ return md;
+}
+
+static inline void
+lnet_md_free(lnet_libmd_t *md)
+{
+ /* ALWAYS called with resource lock held */
+ unsigned int size;
+
+ if ((md->md_options & LNET_MD_KIOV) != 0)
+ size = offsetof(lnet_libmd_t, md_iov.kiov[md->md_niov]);
+ else
+ size = offsetof(lnet_libmd_t, md_iov.iov[md->md_niov]);
+
+ LIBCFS_FREE(md, size);
+}
+
+static inline lnet_me_t *
+lnet_me_alloc(void)
+{
+ /* NEVER called with liblock held */
+ lnet_me_t *me;
+
+ LIBCFS_ALLOC(me, sizeof(*me));
+ return me;
+}
+
+static inline void
+lnet_me_free(lnet_me_t *me)
+{
+ /* ALWAYS called with resource lock held */
+ LIBCFS_FREE(me, sizeof(*me));
+}
+
+static inline lnet_msg_t *
+lnet_msg_alloc(void)
+{
+ /* NEVER called with liblock held */
+ lnet_msg_t *msg;
+
+ LIBCFS_ALLOC(msg, sizeof(*msg));
+
+ /* no need to zero, LIBCFS_ALLOC does for us */
+ return msg;
+}
+
+static inline void
+lnet_msg_free(lnet_msg_t *msg)
+{
+ /* ALWAYS called with network lock held */
+ LASSERT(!msg->msg_onactivelist);
+ LIBCFS_FREE(msg, sizeof(*msg));
+}
+
+#define lnet_eq_free_locked(eq) lnet_eq_free(eq)
+#define lnet_md_free_locked(md) lnet_md_free(md)
+#define lnet_me_free_locked(me) lnet_me_free(me)
+#define lnet_msg_free_locked(msg) lnet_msg_free(msg)
+
+#endif /* LNET_USE_LIB_FREELIST */
+
+lnet_libhandle_t *lnet_res_lh_lookup(struct lnet_res_container *rec,
+ __u64 cookie);
+void lnet_res_lh_initialize(struct lnet_res_container *rec,
+ lnet_libhandle_t *lh);
+static inline void
+lnet_res_lh_invalidate(lnet_libhandle_t *lh)
+{
+ /* ALWAYS called with resource lock held */
+ /* NB: cookie is still useful, don't reset it */
+ list_del(&lh->lh_hash_chain);
+}
+
+static inline void
+lnet_eq2handle(lnet_handle_eq_t *handle, lnet_eq_t *eq)
+{
+ if (eq == NULL) {
+ LNetInvalidateHandle(handle);
+ return;
+ }
+
+ handle->cookie = eq->eq_lh.lh_cookie;
+}
+
+static inline lnet_eq_t *
+lnet_handle2eq(lnet_handle_eq_t *handle)
+{
+ /* ALWAYS called with resource lock held */
+ lnet_libhandle_t *lh;
+
+ lh = lnet_res_lh_lookup(&the_lnet.ln_eq_container, handle->cookie);
+ if (lh == NULL)
+ return NULL;
+
+ return lh_entry(lh, lnet_eq_t, eq_lh);
+}
+
+static inline void
+lnet_md2handle(lnet_handle_md_t *handle, lnet_libmd_t *md)
+{
+ handle->cookie = md->md_lh.lh_cookie;
+}
+
+static inline lnet_libmd_t *
+lnet_handle2md(lnet_handle_md_t *handle)
+{
+ /* ALWAYS called with resource lock held */
+ lnet_libhandle_t *lh;
+ int cpt;
+
+ cpt = lnet_cpt_of_cookie(handle->cookie);
+ lh = lnet_res_lh_lookup(the_lnet.ln_md_containers[cpt],
+ handle->cookie);
+ if (lh == NULL)
+ return NULL;
+
+ return lh_entry(lh, lnet_libmd_t, md_lh);
+}
+
+static inline lnet_libmd_t *
+lnet_wire_handle2md(lnet_handle_wire_t *wh)
+{
+ /* ALWAYS called with resource lock held */
+ lnet_libhandle_t *lh;
+ int cpt;
+
+ if (wh->wh_interface_cookie != the_lnet.ln_interface_cookie)
+ return NULL;
+
+ cpt = lnet_cpt_of_cookie(wh->wh_object_cookie);
+ lh = lnet_res_lh_lookup(the_lnet.ln_md_containers[cpt],
+ wh->wh_object_cookie);
+ if (lh == NULL)
+ return NULL;
+
+ return lh_entry(lh, lnet_libmd_t, md_lh);
+}
+
+static inline void
+lnet_me2handle(lnet_handle_me_t *handle, lnet_me_t *me)
+{
+ handle->cookie = me->me_lh.lh_cookie;
+}
+
+static inline lnet_me_t *
+lnet_handle2me(lnet_handle_me_t *handle)
+{
+ /* ALWAYS called with resource lock held */
+ lnet_libhandle_t *lh;
+ int cpt;
+
+ cpt = lnet_cpt_of_cookie(handle->cookie);
+ lh = lnet_res_lh_lookup(the_lnet.ln_me_containers[cpt],
+ handle->cookie);
+ if (lh == NULL)
+ return NULL;
+
+ return lh_entry(lh, lnet_me_t, me_lh);
+}
+
+static inline void
+lnet_peer_addref_locked(lnet_peer_t *lp)
+{
+ LASSERT(lp->lp_refcount > 0);
+ lp->lp_refcount++;
+}
+
+void lnet_destroy_peer_locked(lnet_peer_t *lp);
+
+static inline void
+lnet_peer_decref_locked(lnet_peer_t *lp)
+{
+ LASSERT(lp->lp_refcount > 0);
+ lp->lp_refcount--;
+ if (lp->lp_refcount == 0)
+ lnet_destroy_peer_locked(lp);
+}
+
+static inline int
+lnet_isrouter(lnet_peer_t *lp)
+{
+ return lp->lp_rtr_refcount != 0;
+}
+
+static inline void
+lnet_ni_addref_locked(lnet_ni_t *ni, int cpt)
+{
+ LASSERT(cpt >= 0 && cpt < LNET_CPT_NUMBER);
+ LASSERT(*ni->ni_refs[cpt] >= 0);
+
+ (*ni->ni_refs[cpt])++;
+}
+
+static inline void
+lnet_ni_addref(lnet_ni_t *ni)
+{
+ lnet_net_lock(0);
+ lnet_ni_addref_locked(ni, 0);
+ lnet_net_unlock(0);
+}
+
+static inline void
+lnet_ni_decref_locked(lnet_ni_t *ni, int cpt)
+{
+ LASSERT(cpt >= 0 && cpt < LNET_CPT_NUMBER);
+ LASSERT(*ni->ni_refs[cpt] > 0);
+
+ (*ni->ni_refs[cpt])--;
+}
+
+static inline void
+lnet_ni_decref(lnet_ni_t *ni)
+{
+ lnet_net_lock(0);
+ lnet_ni_decref_locked(ni, 0);
+ lnet_net_unlock(0);
+}
+
+void lnet_ni_free(lnet_ni_t *ni);
+
+static inline int
+lnet_nid2peerhash(lnet_nid_t nid)
+{
+ return hash_long(nid, LNET_PEER_HASH_BITS);
+}
+
+static inline struct list_head *
+lnet_net2rnethash(__u32 net)
+{
+ return &the_lnet.ln_remote_nets_hash[(LNET_NETNUM(net) +
+ LNET_NETTYP(net)) &
+ ((1U << the_lnet.ln_remote_nets_hbits) - 1)];
+}
+
+extern lnd_t the_lolnd;
+extern int avoid_asym_router_failure;
+
+int lnet_cpt_of_nid_locked(lnet_nid_t nid);
+int lnet_cpt_of_nid(lnet_nid_t nid);
+lnet_ni_t *lnet_nid2ni_locked(lnet_nid_t nid, int cpt);
+lnet_ni_t *lnet_net2ni_locked(__u32 net, int cpt);
+lnet_ni_t *lnet_net2ni(__u32 net);
+
+int lnet_notify(lnet_ni_t *ni, lnet_nid_t peer, int alive, unsigned long when);
+void lnet_notify_locked(lnet_peer_t *lp, int notifylnd, int alive,
+ unsigned long when);
+int lnet_add_route(__u32 net, unsigned int hops, lnet_nid_t gateway_nid,
+ unsigned int priority);
+int lnet_check_routes(void);
+int lnet_del_route(__u32 net, lnet_nid_t gw_nid);
+void lnet_destroy_routes(void);
+int lnet_get_route(int idx, __u32 *net, __u32 *hops,
+ lnet_nid_t *gateway, __u32 *alive, __u32 *priority);
+void lnet_proc_init(void);
+void lnet_proc_fini(void);
+int lnet_rtrpools_alloc(int im_a_router);
+void lnet_rtrpools_free(void);
+lnet_remotenet_t *lnet_find_net_locked(__u32 net);
+
+int lnet_islocalnid(lnet_nid_t nid);
+int lnet_islocalnet(__u32 net);
+
+void lnet_msg_attach_md(lnet_msg_t *msg, lnet_libmd_t *md,
+ unsigned int offset, unsigned int mlen);
+void lnet_msg_detach_md(lnet_msg_t *msg, int status);
+void lnet_build_unlink_event(lnet_libmd_t *md, lnet_event_t *ev);
+void lnet_build_msg_event(lnet_msg_t *msg, lnet_event_kind_t ev_type);
+void lnet_msg_commit(lnet_msg_t *msg, int cpt);
+void lnet_msg_decommit(lnet_msg_t *msg, int cpt, int status);
+
+void lnet_eq_enqueue_event(lnet_eq_t *eq, lnet_event_t *ev);
+void lnet_prep_send(lnet_msg_t *msg, int type, lnet_process_id_t target,
+ unsigned int offset, unsigned int len);
+int lnet_send(lnet_nid_t nid, lnet_msg_t *msg, lnet_nid_t rtr_nid);
+void lnet_return_tx_credits_locked(lnet_msg_t *msg);
+void lnet_return_rx_credits_locked(lnet_msg_t *msg);
+
+/* portals functions */
+/* portals attributes */
+static inline int
+lnet_ptl_is_lazy(lnet_portal_t *ptl)
+{
+ return !!(ptl->ptl_options & LNET_PTL_LAZY);
+}
+
+static inline int
+lnet_ptl_is_unique(lnet_portal_t *ptl)
+{
+ return !!(ptl->ptl_options & LNET_PTL_MATCH_UNIQUE);
+}
+
+static inline int
+lnet_ptl_is_wildcard(lnet_portal_t *ptl)
+{
+ return !!(ptl->ptl_options & LNET_PTL_MATCH_WILDCARD);
+}
+
+static inline void
+lnet_ptl_setopt(lnet_portal_t *ptl, int opt)
+{
+ ptl->ptl_options |= opt;
+}
+
+static inline void
+lnet_ptl_unsetopt(lnet_portal_t *ptl, int opt)
+{
+ ptl->ptl_options &= ~opt;
+}
+
+/* match-table functions */
+struct list_head *lnet_mt_match_head(struct lnet_match_table *mtable,
+ lnet_process_id_t id, __u64 mbits);
+struct lnet_match_table *lnet_mt_of_attach(unsigned int index,
+ lnet_process_id_t id, __u64 mbits,
+ __u64 ignore_bits,
+ lnet_ins_pos_t pos);
+int lnet_mt_match_md(struct lnet_match_table *mtable,
+ struct lnet_match_info *info, struct lnet_msg *msg);
+
+/* portals match/attach functions */
+void lnet_ptl_attach_md(lnet_me_t *me, lnet_libmd_t *md,
+ struct list_head *matches, struct list_head *drops);
+void lnet_ptl_detach_md(lnet_me_t *me, lnet_libmd_t *md);
+int lnet_ptl_match_md(struct lnet_match_info *info, struct lnet_msg *msg);
+
+/* initialized and finalize portals */
+int lnet_portals_create(void);
+void lnet_portals_destroy(void);
+
+/* message functions */
+int lnet_parse(lnet_ni_t *ni, lnet_hdr_t *hdr,
+ lnet_nid_t fromnid, void *private, int rdma_req);
+void lnet_recv(lnet_ni_t *ni, void *private, lnet_msg_t *msg, int delayed,
+ unsigned int offset, unsigned int mlen, unsigned int rlen);
+lnet_msg_t *lnet_create_reply_msg(lnet_ni_t *ni, lnet_msg_t *get_msg);
+void lnet_set_reply_msg_len(lnet_ni_t *ni, lnet_msg_t *msg, unsigned int len);
+void lnet_finalize(lnet_ni_t *ni, lnet_msg_t *msg, int rc);
+void lnet_drop_delayed_msg_list(struct list_head *head, char *reason);
+void lnet_recv_delayed_msg_list(struct list_head *head);
+
+int lnet_msg_container_setup(struct lnet_msg_container *container, int cpt);
+void lnet_msg_container_cleanup(struct lnet_msg_container *container);
+void lnet_msg_containers_destroy(void);
+int lnet_msg_containers_create(void);
+
+char *lnet_msgtyp2str(int type);
+void lnet_print_hdr(lnet_hdr_t *hdr);
+int lnet_fail_nid(lnet_nid_t nid, unsigned int threshold);
+
+void lnet_counters_get(lnet_counters_t *counters);
+void lnet_counters_reset(void);
+
+unsigned int lnet_iov_nob(unsigned int niov, struct kvec *iov);
+int lnet_extract_iov(int dst_niov, struct kvec *dst,
+ int src_niov, struct kvec *src,
+ unsigned int offset, unsigned int len);
+
+unsigned int lnet_kiov_nob(unsigned int niov, lnet_kiov_t *iov);
+int lnet_extract_kiov(int dst_niov, lnet_kiov_t *dst,
+ int src_niov, lnet_kiov_t *src,
+ unsigned int offset, unsigned int len);
+
+void lnet_copy_iov2iov(unsigned int ndiov, struct kvec *diov,
+ unsigned int doffset,
+ unsigned int nsiov, struct kvec *siov,
+ unsigned int soffset, unsigned int nob);
+void lnet_copy_kiov2iov(unsigned int niov, struct kvec *iov,
+ unsigned int iovoffset,
+ unsigned int nkiov, lnet_kiov_t *kiov,
+ unsigned int kiovoffset, unsigned int nob);
+void lnet_copy_iov2kiov(unsigned int nkiov, lnet_kiov_t *kiov,
+ unsigned int kiovoffset,
+ unsigned int niov, struct kvec *iov,
+ unsigned int iovoffset, unsigned int nob);
+void lnet_copy_kiov2kiov(unsigned int ndkiov, lnet_kiov_t *dkiov,
+ unsigned int doffset,
+ unsigned int nskiov, lnet_kiov_t *skiov,
+ unsigned int soffset, unsigned int nob);
+
+static inline void
+lnet_copy_iov2flat(int dlen, void *dest, unsigned int doffset,
+ unsigned int nsiov, struct kvec *siov, unsigned int soffset,
+ unsigned int nob)
+{
+ struct kvec diov = {/*.iov_base = */ dest, /*.iov_len = */ dlen};
+
+ lnet_copy_iov2iov(1, &diov, doffset,
+ nsiov, siov, soffset, nob);
+}
+
+static inline void
+lnet_copy_kiov2flat(int dlen, void *dest, unsigned int doffset,
+ unsigned int nsiov, lnet_kiov_t *skiov,
+ unsigned int soffset, unsigned int nob)
+{
+ struct kvec diov = {/* .iov_base = */ dest, /* .iov_len = */ dlen};
+
+ lnet_copy_kiov2iov(1, &diov, doffset,
+ nsiov, skiov, soffset, nob);
+}
+
+static inline void
+lnet_copy_flat2iov(unsigned int ndiov, struct kvec *diov, unsigned int doffset,
+ int slen, void *src, unsigned int soffset, unsigned int nob)
+{
+ struct kvec siov = {/*.iov_base = */ src, /*.iov_len = */slen};
+
+ lnet_copy_iov2iov(ndiov, diov, doffset,
+ 1, &siov, soffset, nob);
+}
+
+static inline void
+lnet_copy_flat2kiov(unsigned int ndiov, lnet_kiov_t *dkiov,
+ unsigned int doffset, int slen, void *src,
+ unsigned int soffset, unsigned int nob)
+{
+ struct kvec siov = {/* .iov_base = */ src, /* .iov_len = */ slen};
+
+ lnet_copy_iov2kiov(ndiov, dkiov, doffset,
+ 1, &siov, soffset, nob);
+}
+
+void lnet_me_unlink(lnet_me_t *me);
+
+void lnet_md_unlink(lnet_libmd_t *md);
+void lnet_md_deconstruct(lnet_libmd_t *lmd, lnet_md_t *umd);
+
+void lnet_register_lnd(lnd_t *lnd);
+void lnet_unregister_lnd(lnd_t *lnd);
+int lnet_set_ip_niaddr(lnet_ni_t *ni);
+
+int lnet_connect(struct socket **sockp, lnet_nid_t peer_nid,
+ __u32 local_ip, __u32 peer_ip, int peer_port);
+void lnet_connect_console_error(int rc, lnet_nid_t peer_nid,
+ __u32 peer_ip, int port);
+int lnet_count_acceptor_nis(void);
+int lnet_acceptor_timeout(void);
+int lnet_acceptor_port(void);
+
+int lnet_count_acceptor_nis(void);
+int lnet_acceptor_port(void);
+
+int lnet_acceptor_start(void);
+void lnet_acceptor_stop(void);
+
+void lnet_get_tunables(void);
+int lnet_peers_start_down(void);
+int lnet_peer_buffer_credits(lnet_ni_t *ni);
+
+int lnet_router_checker_start(void);
+void lnet_router_checker_stop(void);
+void lnet_router_ni_update_locked(lnet_peer_t *gw, __u32 net);
+void lnet_swap_pinginfo(lnet_ping_info_t *info);
+
+int lnet_ping_target_init(void);
+void lnet_ping_target_fini(void);
+int lnet_ping(lnet_process_id_t id, int timeout_ms,
+ lnet_process_id_t *ids, int n_ids);
+
+int lnet_parse_ip2nets(char **networksp, char *ip2nets);
+int lnet_parse_routes(char *route_str, int *im_a_router);
+int lnet_parse_networks(struct list_head *nilist, char *networks);
+
+int lnet_nid2peer_locked(lnet_peer_t **lpp, lnet_nid_t nid, int cpt);
+lnet_peer_t *lnet_find_peer_locked(struct lnet_peer_table *ptable,
+ lnet_nid_t nid);
+void lnet_peer_tables_cleanup(void);
+void lnet_peer_tables_destroy(void);
+int lnet_peer_tables_create(void);
+void lnet_debug_peer(lnet_nid_t nid);
+
+static inline void lnet_peer_set_alive(lnet_peer_t *lp)
+{
+ lp->lp_last_alive = lp->lp_last_query = get_seconds();
+ if (!lp->lp_alive)
+ lnet_notify_locked(lp, 0, 1, lp->lp_last_alive);
+}
+
+
+#endif
diff --git a/drivers/staging/lustre/include/linux/lnet/lib-types.h b/drivers/staging/lustre/include/linux/lnet/lib-types.h
new file mode 100644
index 000000000..50537668f
--- /dev/null
+++ b/drivers/staging/lustre/include/linux/lnet/lib-types.h
@@ -0,0 +1,760 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2012, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ *
+ * lnet/include/lnet/lib-types.h
+ *
+ * Types used by the library side routines that do not need to be
+ * exposed to the user application
+ */
+
+#ifndef __LNET_LIB_TYPES_H__
+#define __LNET_LIB_TYPES_H__
+
+#include "linux/lib-types.h"
+
+#include "../libcfs/libcfs.h"
+#include <linux/list.h>
+#include "types.h"
+
+#define WIRE_ATTR __attribute__((packed))
+
+/* Packed version of lnet_process_id_t to transfer via network */
+typedef struct {
+ lnet_nid_t nid;
+ lnet_pid_t pid; /* node id / process id */
+} WIRE_ATTR lnet_process_id_packed_t;
+
+/* The wire handle's interface cookie only matches one network interface in
+ * one epoch (i.e. new cookie when the interface restarts or the node
+ * reboots). The object cookie only matches one object on that interface
+ * during that object's lifetime (i.e. no cookie re-use). */
+typedef struct {
+ __u64 wh_interface_cookie;
+ __u64 wh_object_cookie;
+} WIRE_ATTR lnet_handle_wire_t;
+
+typedef enum {
+ LNET_MSG_ACK = 0,
+ LNET_MSG_PUT,
+ LNET_MSG_GET,
+ LNET_MSG_REPLY,
+ LNET_MSG_HELLO,
+} lnet_msg_type_t;
+
+/* The variant fields of the portals message header are aligned on an 8
+ * byte boundary in the message header. Note that all types used in these
+ * wire structs MUST be fixed size and the smaller types are placed at the
+ * end. */
+typedef struct lnet_ack {
+ lnet_handle_wire_t dst_wmd;
+ __u64 match_bits;
+ __u32 mlength;
+} WIRE_ATTR lnet_ack_t;
+
+typedef struct lnet_put {
+ lnet_handle_wire_t ack_wmd;
+ __u64 match_bits;
+ __u64 hdr_data;
+ __u32 ptl_index;
+ __u32 offset;
+} WIRE_ATTR lnet_put_t;
+
+typedef struct lnet_get {
+ lnet_handle_wire_t return_wmd;
+ __u64 match_bits;
+ __u32 ptl_index;
+ __u32 src_offset;
+ __u32 sink_length;
+} WIRE_ATTR lnet_get_t;
+
+typedef struct lnet_reply {
+ lnet_handle_wire_t dst_wmd;
+} WIRE_ATTR lnet_reply_t;
+
+typedef struct lnet_hello {
+ __u64 incarnation;
+ __u32 type;
+} WIRE_ATTR lnet_hello_t;
+
+typedef struct {
+ lnet_nid_t dest_nid;
+ lnet_nid_t src_nid;
+ lnet_pid_t dest_pid;
+ lnet_pid_t src_pid;
+ __u32 type; /* lnet_msg_type_t */
+ __u32 payload_length; /* payload data to follow */
+ /*<------__u64 aligned------->*/
+ union {
+ lnet_ack_t ack;
+ lnet_put_t put;
+ lnet_get_t get;
+ lnet_reply_t reply;
+ lnet_hello_t hello;
+ } msg;
+} WIRE_ATTR lnet_hdr_t;
+
+/* A HELLO message contains a magic number and protocol version
+ * code in the header's dest_nid, the peer's NID in the src_nid, and
+ * LNET_MSG_HELLO in the type field. All other common fields are zero
+ * (including payload_size; i.e. no payload).
+ * This is for use by byte-stream LNDs (e.g. TCP/IP) to check the peer is
+ * running the same protocol and to find out its NID. These LNDs should
+ * exchange HELLO messages when a connection is first established. Individual
+ * LNDs can put whatever else they fancy in lnet_hdr_t::msg.
+ */
+typedef struct {
+ __u32 magic; /* LNET_PROTO_TCP_MAGIC */
+ __u16 version_major; /* increment on incompatible change */
+ __u16 version_minor; /* increment on compatible change */
+} WIRE_ATTR lnet_magicversion_t;
+
+/* PROTO MAGIC for LNDs */
+#define LNET_PROTO_IB_MAGIC 0x0be91b91
+#define LNET_PROTO_RA_MAGIC 0x0be91b92
+#define LNET_PROTO_QSW_MAGIC 0x0be91b93
+#define LNET_PROTO_GNI_MAGIC 0xb00fbabe /* ask Kim */
+#define LNET_PROTO_TCP_MAGIC 0xeebc0ded
+#define LNET_PROTO_PTL_MAGIC 0x50746C4E /* 'PtlN' unique magic */
+#define LNET_PROTO_MX_MAGIC 0x4d583130 /* 'MX10'! */
+#define LNET_PROTO_ACCEPTOR_MAGIC 0xacce7100
+#define LNET_PROTO_PING_MAGIC 0x70696E67 /* 'ping' */
+
+/* Placeholder for a future "unified" protocol across all LNDs */
+/* Current LNDs that receive a request with this magic will respond with a
+ * "stub" reply using their current protocol */
+#define LNET_PROTO_MAGIC 0x45726963 /* ! */
+
+#define LNET_PROTO_TCP_VERSION_MAJOR 1
+#define LNET_PROTO_TCP_VERSION_MINOR 0
+
+/* Acceptor connection request */
+typedef struct {
+ __u32 acr_magic; /* PTL_ACCEPTOR_PROTO_MAGIC */
+ __u32 acr_version; /* protocol version */
+ __u64 acr_nid; /* target NID */
+} WIRE_ATTR lnet_acceptor_connreq_t;
+
+#define LNET_PROTO_ACCEPTOR_VERSION 1
+
+/* forward refs */
+struct lnet_libmd;
+
+typedef struct lnet_msg {
+ struct list_head msg_activelist;
+ struct list_head msg_list; /* Q for credits/MD */
+
+ lnet_process_id_t msg_target;
+ /* where is it from, it's only for building event */
+ lnet_nid_t msg_from;
+ __u32 msg_type;
+
+ /* committed for sending */
+ unsigned int msg_tx_committed:1;
+ /* CPT # this message committed for sending */
+ unsigned int msg_tx_cpt:15;
+ /* committed for receiving */
+ unsigned int msg_rx_committed:1;
+ /* CPT # this message committed for receiving */
+ unsigned int msg_rx_cpt:15;
+ /* queued for tx credit */
+ unsigned int msg_tx_delayed:1;
+ /* queued for RX buffer */
+ unsigned int msg_rx_delayed:1;
+ /* ready for pending on RX delay list */
+ unsigned int msg_rx_ready_delay:1;
+
+ unsigned int msg_vmflush:1; /* VM trying to free memory */
+ unsigned int msg_target_is_router:1; /* sending to a router */
+ unsigned int msg_routing:1; /* being forwarded */
+ unsigned int msg_ack:1; /* ack on finalize (PUT) */
+ unsigned int msg_sending:1; /* outgoing message */
+ unsigned int msg_receiving:1; /* being received */
+ unsigned int msg_txcredit:1; /* taken an NI send credit */
+ unsigned int msg_peertxcredit:1; /* taken a peer send credit */
+ unsigned int msg_rtrcredit:1; /* taken a global router credit */
+ unsigned int msg_peerrtrcredit:1; /* taken a peer router credit */
+ unsigned int msg_onactivelist:1; /* on the activelist */
+
+ struct lnet_peer *msg_txpeer; /* peer I'm sending to */
+ struct lnet_peer *msg_rxpeer; /* peer I received from */
+
+ void *msg_private;
+ struct lnet_libmd *msg_md;
+
+ unsigned int msg_len;
+ unsigned int msg_wanted;
+ unsigned int msg_offset;
+ unsigned int msg_niov;
+ struct kvec *msg_iov;
+ lnet_kiov_t *msg_kiov;
+
+ lnet_event_t msg_ev;
+ lnet_hdr_t msg_hdr;
+} lnet_msg_t;
+
+typedef struct lnet_libhandle {
+ struct list_head lh_hash_chain;
+ __u64 lh_cookie;
+} lnet_libhandle_t;
+
+#define lh_entry(ptr, type, member) \
+ ((type *)((char *)(ptr)-(char *)(&((type *)0)->member)))
+
+typedef struct lnet_eq {
+ struct list_head eq_list;
+ lnet_libhandle_t eq_lh;
+ lnet_seq_t eq_enq_seq;
+ lnet_seq_t eq_deq_seq;
+ unsigned int eq_size;
+ lnet_eq_handler_t eq_callback;
+ lnet_event_t *eq_events;
+ int **eq_refs; /* percpt refcount for EQ */
+} lnet_eq_t;
+
+typedef struct lnet_me {
+ struct list_head me_list;
+ lnet_libhandle_t me_lh;
+ lnet_process_id_t me_match_id;
+ unsigned int me_portal;
+ unsigned int me_pos; /* hash offset in mt_hash */
+ __u64 me_match_bits;
+ __u64 me_ignore_bits;
+ lnet_unlink_t me_unlink;
+ struct lnet_libmd *me_md;
+} lnet_me_t;
+
+typedef struct lnet_libmd {
+ struct list_head md_list;
+ lnet_libhandle_t md_lh;
+ lnet_me_t *md_me;
+ char *md_start;
+ unsigned int md_offset;
+ unsigned int md_length;
+ unsigned int md_max_size;
+ int md_threshold;
+ int md_refcount;
+ unsigned int md_options;
+ unsigned int md_flags;
+ void *md_user_ptr;
+ lnet_eq_t *md_eq;
+ unsigned int md_niov; /* # frags */
+ union {
+ struct kvec iov[LNET_MAX_IOV];
+ lnet_kiov_t kiov[LNET_MAX_IOV];
+ } md_iov;
+} lnet_libmd_t;
+
+#define LNET_MD_FLAG_ZOMBIE (1 << 0)
+#define LNET_MD_FLAG_AUTO_UNLINK (1 << 1)
+#define LNET_MD_FLAG_ABORTED (1 << 2)
+
+#ifdef LNET_USE_LIB_FREELIST
+typedef struct {
+ void *fl_objs; /* single contiguous array of objects */
+ int fl_nobjs; /* the number of them */
+ int fl_objsize; /* the size (including overhead) of each of them */
+ struct list_head fl_list; /* where they are enqueued */
+} lnet_freelist_t;
+
+typedef struct {
+ struct list_head fo_list; /* enqueue on fl_list */
+ void *fo_contents; /* aligned contents */
+} lnet_freeobj_t;
+#endif
+
+typedef struct {
+ /* info about peers we are trying to fail */
+ struct list_head tp_list; /* ln_test_peers */
+ lnet_nid_t tp_nid; /* matching nid */
+ unsigned int tp_threshold; /* # failures to simulate */
+} lnet_test_peer_t;
+
+#define LNET_COOKIE_TYPE_MD 1
+#define LNET_COOKIE_TYPE_ME 2
+#define LNET_COOKIE_TYPE_EQ 3
+#define LNET_COOKIE_TYPE_BITS 2
+#define LNET_COOKIE_MASK ((1ULL << LNET_COOKIE_TYPE_BITS) - 1ULL)
+
+struct lnet_ni; /* forward ref */
+
+typedef struct lnet_lnd {
+ /* fields managed by portals */
+ struct list_head lnd_list; /* stash in the LND table */
+ int lnd_refcount; /* # active instances */
+
+ /* fields initialised by the LND */
+ unsigned int lnd_type;
+
+ int (*lnd_startup)(struct lnet_ni *ni);
+ void (*lnd_shutdown)(struct lnet_ni *ni);
+ int (*lnd_ctl)(struct lnet_ni *ni, unsigned int cmd, void *arg);
+
+ /* In data movement APIs below, payload buffers are described as a set
+ * of 'niov' fragments which are...
+ * EITHER
+ * in virtual memory (struct iovec *iov != NULL)
+ * OR
+ * in pages (kernel only: plt_kiov_t *kiov != NULL).
+ * The LND may NOT overwrite these fragment descriptors.
+ * An 'offset' and may specify a byte offset within the set of
+ * fragments to start from
+ */
+
+ /* Start sending a preformatted message. 'private' is NULL for PUT and
+ * GET messages; otherwise this is a response to an incoming message
+ * and 'private' is the 'private' passed to lnet_parse(). Return
+ * non-zero for immediate failure, otherwise complete later with
+ * lnet_finalize() */
+ int (*lnd_send)(struct lnet_ni *ni, void *private, lnet_msg_t *msg);
+
+ /* Start receiving 'mlen' bytes of payload data, skipping the following
+ * 'rlen' - 'mlen' bytes. 'private' is the 'private' passed to
+ * lnet_parse(). Return non-zero for immediate failure, otherwise
+ * complete later with lnet_finalize(). This also gives back a receive
+ * credit if the LND does flow control. */
+ int (*lnd_recv)(struct lnet_ni *ni, void *private, lnet_msg_t *msg,
+ int delayed, unsigned int niov,
+ struct kvec *iov, lnet_kiov_t *kiov,
+ unsigned int offset, unsigned int mlen, unsigned int rlen);
+
+ /* lnet_parse() has had to delay processing of this message
+ * (e.g. waiting for a forwarding buffer or send credits). Give the
+ * LND a chance to free urgently needed resources. If called, return 0
+ * for success and do NOT give back a receive credit; that has to wait
+ * until lnd_recv() gets called. On failure return < 0 and
+ * release resources; lnd_recv() will not be called. */
+ int (*lnd_eager_recv)(struct lnet_ni *ni, void *private, lnet_msg_t *msg,
+ void **new_privatep);
+
+ /* notification of peer health */
+ void (*lnd_notify)(struct lnet_ni *ni, lnet_nid_t peer, int alive);
+
+ /* query of peer aliveness */
+ void (*lnd_query)(struct lnet_ni *ni, lnet_nid_t peer, unsigned long *when);
+
+ /* accept a new connection */
+ int (*lnd_accept)(struct lnet_ni *ni, struct socket *sock);
+
+} lnd_t;
+
+#define LNET_NI_STATUS_UP 0x15aac0de
+#define LNET_NI_STATUS_DOWN 0xdeadface
+#define LNET_NI_STATUS_INVALID 0x00000000
+typedef struct {
+ lnet_nid_t ns_nid;
+ __u32 ns_status;
+ __u32 ns_unused;
+} WIRE_ATTR lnet_ni_status_t;
+
+struct lnet_tx_queue {
+ int tq_credits; /* # tx credits free */
+ int tq_credits_min; /* lowest it's been */
+ int tq_credits_max; /* total # tx credits */
+ struct list_head tq_delayed; /* delayed TXs */
+};
+
+#define LNET_MAX_INTERFACES 16
+
+typedef struct lnet_ni {
+ spinlock_t ni_lock;
+ struct list_head ni_list; /* chain on ln_nis */
+ struct list_head ni_cptlist; /* chain on ln_nis_cpt */
+ int ni_maxtxcredits; /* # tx credits */
+ /* # per-peer send credits */
+ int ni_peertxcredits;
+ /* # per-peer router buffer credits */
+ int ni_peerrtrcredits;
+ /* seconds to consider peer dead */
+ int ni_peertimeout;
+ int ni_ncpts; /* number of CPTs */
+ __u32 *ni_cpts; /* bond NI on some CPTs */
+ lnet_nid_t ni_nid; /* interface's NID */
+ void *ni_data; /* instance-specific data */
+ lnd_t *ni_lnd; /* procedural interface */
+ struct lnet_tx_queue **ni_tx_queues; /* percpt TX queues */
+ int **ni_refs; /* percpt reference count */
+ long ni_last_alive; /* when I was last alive */
+ lnet_ni_status_t *ni_status; /* my health status */
+ /* equivalent interfaces to use */
+ char *ni_interfaces[LNET_MAX_INTERFACES];
+} lnet_ni_t;
+
+#define LNET_PROTO_PING_MATCHBITS 0x8000000000000000LL
+
+/* NB: value of these features equal to LNET_PROTO_PING_VERSION_x
+ * of old LNet, so there shouldn't be any compatibility issue */
+#define LNET_PING_FEAT_INVAL (0) /* no feature */
+#define LNET_PING_FEAT_BASE (1 << 0) /* just a ping */
+#define LNET_PING_FEAT_NI_STATUS (1 << 1) /* return NI status */
+
+#define LNET_PING_FEAT_MASK (LNET_PING_FEAT_BASE | \
+ LNET_PING_FEAT_NI_STATUS)
+
+typedef struct {
+ __u32 pi_magic;
+ __u32 pi_features;
+ lnet_pid_t pi_pid;
+ __u32 pi_nnis;
+ lnet_ni_status_t pi_ni[0];
+} WIRE_ATTR lnet_ping_info_t;
+
+/* router checker data, per router */
+#define LNET_MAX_RTR_NIS 16
+#define LNET_PINGINFO_SIZE offsetof(lnet_ping_info_t, pi_ni[LNET_MAX_RTR_NIS])
+typedef struct {
+ /* chain on the_lnet.ln_zombie_rcd or ln_deathrow_rcd */
+ struct list_head rcd_list;
+ lnet_handle_md_t rcd_mdh; /* ping buffer MD */
+ struct lnet_peer *rcd_gateway; /* reference to gateway */
+ lnet_ping_info_t *rcd_pinginfo; /* ping buffer */
+} lnet_rc_data_t;
+
+typedef struct lnet_peer {
+ struct list_head lp_hashlist; /* chain on peer hash */
+ struct list_head lp_txq; /* messages blocking for tx credits */
+ struct list_head lp_rtrq; /* messages blocking for router credits */
+ struct list_head lp_rtr_list; /* chain on router list */
+ int lp_txcredits; /* # tx credits available */
+ int lp_mintxcredits; /* low water mark */
+ int lp_rtrcredits; /* # router credits */
+ int lp_minrtrcredits; /* low water mark */
+ unsigned int lp_alive:1; /* alive/dead? */
+ unsigned int lp_notify:1; /* notification outstanding? */
+ unsigned int lp_notifylnd:1; /* outstanding notification for LND? */
+ unsigned int lp_notifying:1; /* some thread is handling notification */
+ unsigned int lp_ping_notsent; /* SEND event outstanding from ping */
+ int lp_alive_count; /* # times router went dead<->alive */
+ long lp_txqnob; /* bytes queued for sending */
+ unsigned long lp_timestamp; /* time of last aliveness news */
+ unsigned long lp_ping_timestamp; /* time of last ping attempt */
+ unsigned long lp_ping_deadline; /* != 0 if ping reply expected */
+ unsigned long lp_last_alive; /* when I was last alive */
+ unsigned long lp_last_query; /* when lp_ni was queried last time */
+ lnet_ni_t *lp_ni; /* interface peer is on */
+ lnet_nid_t lp_nid; /* peer's NID */
+ int lp_refcount; /* # refs */
+ int lp_cpt; /* CPT this peer attached on */
+ /* # refs from lnet_route_t::lr_gateway */
+ int lp_rtr_refcount;
+ /* returned RC ping features */
+ unsigned int lp_ping_feats;
+ struct list_head lp_routes; /* routers on this peer */
+ lnet_rc_data_t *lp_rcd; /* router checker state */
+} lnet_peer_t;
+
+/* peer hash size */
+#define LNET_PEER_HASH_BITS 9
+#define LNET_PEER_HASH_SIZE (1 << LNET_PEER_HASH_BITS)
+
+/* peer hash table */
+struct lnet_peer_table {
+ int pt_version; /* /proc validity stamp */
+ int pt_number; /* # peers extant */
+ struct list_head pt_deathrow; /* zombie peers */
+ struct list_head *pt_hash; /* NID->peer hash */
+};
+
+/* peer aliveness is enabled only on routers for peers in a network where the
+ * lnet_ni_t::ni_peertimeout has been set to a positive value */
+#define lnet_peer_aliveness_enabled(lp) (the_lnet.ln_routing != 0 && \
+ (lp)->lp_ni->ni_peertimeout > 0)
+
+typedef struct {
+ struct list_head lr_list; /* chain on net */
+ struct list_head lr_gwlist; /* chain on gateway */
+ lnet_peer_t *lr_gateway; /* router node */
+ __u32 lr_net; /* remote network number */
+ int lr_seq; /* sequence for round-robin */
+ unsigned int lr_downis; /* number of down NIs */
+ unsigned int lr_hops; /* how far I am */
+ unsigned int lr_priority; /* route priority */
+} lnet_route_t;
+
+#define LNET_REMOTE_NETS_HASH_DEFAULT (1U << 7)
+#define LNET_REMOTE_NETS_HASH_MAX (1U << 16)
+#define LNET_REMOTE_NETS_HASH_SIZE (1 << the_lnet.ln_remote_nets_hbits)
+
+typedef struct {
+ struct list_head lrn_list; /* chain on ln_remote_nets_hash */
+ struct list_head lrn_routes; /* routes to me */
+ __u32 lrn_net; /* my net number */
+} lnet_remotenet_t;
+
+typedef struct {
+ struct list_head rbp_bufs; /* my free buffer pool */
+ struct list_head rbp_msgs; /* messages blocking for a buffer */
+ int rbp_npages; /* # pages in each buffer */
+ int rbp_nbuffers; /* # buffers */
+ int rbp_credits; /* # free buffers / blocked messages */
+ int rbp_mincredits; /* low water mark */
+} lnet_rtrbufpool_t;
+
+typedef struct {
+ struct list_head rb_list; /* chain on rbp_bufs */
+ lnet_rtrbufpool_t *rb_pool; /* owning pool */
+ lnet_kiov_t rb_kiov[0]; /* the buffer space */
+} lnet_rtrbuf_t;
+
+typedef struct {
+ __u32 msgs_alloc;
+ __u32 msgs_max;
+ __u32 errors;
+ __u32 send_count;
+ __u32 recv_count;
+ __u32 route_count;
+ __u32 drop_count;
+ __u64 send_length;
+ __u64 recv_length;
+ __u64 route_length;
+ __u64 drop_length;
+} WIRE_ATTR lnet_counters_t;
+
+#define LNET_PEER_HASHSIZE 503 /* prime! */
+
+#define LNET_NRBPOOLS 3 /* # different router buffer pools */
+
+enum {
+ /* Didn't match anything */
+ LNET_MATCHMD_NONE = (1 << 0),
+ /* Matched OK */
+ LNET_MATCHMD_OK = (1 << 1),
+ /* Must be discarded */
+ LNET_MATCHMD_DROP = (1 << 2),
+ /* match and buffer is exhausted */
+ LNET_MATCHMD_EXHAUSTED = (1 << 3),
+ /* match or drop */
+ LNET_MATCHMD_FINISH = (LNET_MATCHMD_OK | LNET_MATCHMD_DROP),
+};
+
+/* Options for lnet_portal_t::ptl_options */
+#define LNET_PTL_LAZY (1 << 0)
+#define LNET_PTL_MATCH_UNIQUE (1 << 1) /* unique match, for RDMA */
+#define LNET_PTL_MATCH_WILDCARD (1 << 2) /* wildcard match, request portal */
+
+/* parameter for matching operations (GET, PUT) */
+struct lnet_match_info {
+ __u64 mi_mbits;
+ lnet_process_id_t mi_id;
+ unsigned int mi_opc;
+ unsigned int mi_portal;
+ unsigned int mi_rlength;
+ unsigned int mi_roffset;
+};
+
+/* ME hash of RDMA portal */
+#define LNET_MT_HASH_BITS 8
+#define LNET_MT_HASH_SIZE (1 << LNET_MT_HASH_BITS)
+#define LNET_MT_HASH_MASK (LNET_MT_HASH_SIZE - 1)
+/* we allocate (LNET_MT_HASH_SIZE + 1) entries for lnet_match_table::mt_hash,
+ * the last entry is reserved for MEs with ignore-bits */
+#define LNET_MT_HASH_IGNORE LNET_MT_HASH_SIZE
+/* __u64 has 2^6 bits, so need 2^(LNET_MT_HASH_BITS - LNET_MT_BITS_U64) which
+ * is 4 __u64s as bit-map, and add an extra __u64 (only use one bit) for the
+ * ME-list with ignore-bits, which is mtable::mt_hash[LNET_MT_HASH_IGNORE] */
+#define LNET_MT_BITS_U64 6 /* 2^6 bits */
+#define LNET_MT_EXHAUSTED_BITS (LNET_MT_HASH_BITS - LNET_MT_BITS_U64)
+#define LNET_MT_EXHAUSTED_BMAP ((1 << LNET_MT_EXHAUSTED_BITS) + 1)
+
+/* portal match table */
+struct lnet_match_table {
+ /* reserved for upcoming patches, CPU partition ID */
+ unsigned int mt_cpt;
+ unsigned int mt_portal; /* portal index */
+ /* match table is set as "enabled" if there's non-exhausted MD
+ * attached on mt_mhash, it's only valid for wildcard portal */
+ unsigned int mt_enabled;
+ /* bitmap to flag whether MEs on mt_hash are exhausted or not */
+ __u64 mt_exhausted[LNET_MT_EXHAUSTED_BMAP];
+ struct list_head *mt_mhash; /* matching hash */
+};
+
+/* these are only useful for wildcard portal */
+/* Turn off message rotor for wildcard portals */
+#define LNET_PTL_ROTOR_OFF 0
+/* round-robin dispatch all PUT messages for wildcard portals */
+#define LNET_PTL_ROTOR_ON 1
+/* round-robin dispatch routed PUT message for wildcard portals */
+#define LNET_PTL_ROTOR_RR_RT 2
+/* dispatch routed PUT message by hashing source NID for wildcard portals */
+#define LNET_PTL_ROTOR_HASH_RT 3
+
+typedef struct lnet_portal {
+ spinlock_t ptl_lock;
+ unsigned int ptl_index; /* portal ID, reserved */
+ /* flags on this portal: lazy, unique... */
+ unsigned int ptl_options;
+ /* list of messages which are stealing buffer */
+ struct list_head ptl_msg_stealing;
+ /* messages blocking for MD */
+ struct list_head ptl_msg_delayed;
+ /* Match table for each CPT */
+ struct lnet_match_table **ptl_mtables;
+ /* spread rotor of incoming "PUT" */
+ unsigned int ptl_rotor;
+ /* # active entries for this portal */
+ int ptl_mt_nmaps;
+ /* array of active entries' cpu-partition-id */
+ int ptl_mt_maps[0];
+} lnet_portal_t;
+
+#define LNET_LH_HASH_BITS 12
+#define LNET_LH_HASH_SIZE (1ULL << LNET_LH_HASH_BITS)
+#define LNET_LH_HASH_MASK (LNET_LH_HASH_SIZE - 1)
+
+/* resource container (ME, MD, EQ) */
+struct lnet_res_container {
+ unsigned int rec_type; /* container type */
+ __u64 rec_lh_cookie; /* cookie generator */
+ struct list_head rec_active; /* active resource list */
+ struct list_head *rec_lh_hash; /* handle hash */
+#ifdef LNET_USE_LIB_FREELIST
+ lnet_freelist_t rec_freelist; /* freelist for resources */
+#endif
+};
+
+/* message container */
+struct lnet_msg_container {
+ int msc_init; /* initialized or not */
+ /* max # threads finalizing */
+ int msc_nfinalizers;
+ /* msgs waiting to complete finalizing */
+ struct list_head msc_finalizing;
+ struct list_head msc_active; /* active message list */
+ /* threads doing finalization */
+ void **msc_finalizers;
+#ifdef LNET_USE_LIB_FREELIST
+ lnet_freelist_t msc_freelist; /* freelist for messages */
+#endif
+};
+
+/* Router Checker states */
+#define LNET_RC_STATE_SHUTDOWN 0 /* not started */
+#define LNET_RC_STATE_RUNNING 1 /* started up OK */
+#define LNET_RC_STATE_STOPPING 2 /* telling thread to stop */
+
+typedef struct {
+ /* CPU partition table of LNet */
+ struct cfs_cpt_table *ln_cpt_table;
+ /* number of CPTs in ln_cpt_table */
+ unsigned int ln_cpt_number;
+ unsigned int ln_cpt_bits;
+
+ /* protect LNet resources (ME/MD/EQ) */
+ struct cfs_percpt_lock *ln_res_lock;
+ /* # portals */
+ int ln_nportals;
+ /* the vector of portals */
+ lnet_portal_t **ln_portals;
+ /* percpt ME containers */
+ struct lnet_res_container **ln_me_containers;
+ /* percpt MD container */
+ struct lnet_res_container **ln_md_containers;
+
+ /* Event Queue container */
+ struct lnet_res_container ln_eq_container;
+ wait_queue_head_t ln_eq_waitq;
+ spinlock_t ln_eq_wait_lock;
+ unsigned int ln_remote_nets_hbits;
+
+ /* protect NI, peer table, credits, routers, rtrbuf... */
+ struct cfs_percpt_lock *ln_net_lock;
+ /* percpt message containers for active/finalizing/freed message */
+ struct lnet_msg_container **ln_msg_containers;
+ lnet_counters_t **ln_counters;
+ struct lnet_peer_table **ln_peer_tables;
+ /* failure simulation */
+ struct list_head ln_test_peers;
+
+ struct list_head ln_nis; /* LND instances */
+ /* NIs bond on specific CPT(s) */
+ struct list_head ln_nis_cpt;
+ /* dying LND instances */
+ struct list_head ln_nis_zombie;
+ lnet_ni_t *ln_loni; /* the loopback NI */
+ /* NI to wait for events in */
+ lnet_ni_t *ln_eq_waitni;
+
+ /* remote networks with routes to them */
+ struct list_head *ln_remote_nets_hash;
+ /* validity stamp */
+ __u64 ln_remote_nets_version;
+ /* list of all known routers */
+ struct list_head ln_routers;
+ /* validity stamp */
+ __u64 ln_routers_version;
+ /* percpt router buffer pools */
+ lnet_rtrbufpool_t **ln_rtrpools;
+
+ lnet_handle_md_t ln_ping_target_md;
+ lnet_handle_eq_t ln_ping_target_eq;
+ lnet_ping_info_t *ln_ping_info;
+
+ /* router checker startup/shutdown state */
+ int ln_rc_state;
+ /* router checker's event queue */
+ lnet_handle_eq_t ln_rc_eqh;
+ /* rcd still pending on net */
+ struct list_head ln_rcd_deathrow;
+ /* rcd ready for free */
+ struct list_head ln_rcd_zombie;
+ /* serialise startup/shutdown */
+ struct semaphore ln_rc_signal;
+
+ struct mutex ln_api_mutex;
+ struct mutex ln_lnd_mutex;
+ int ln_init; /* LNetInit() called? */
+ /* Have I called LNetNIInit myself? */
+ int ln_niinit_self;
+ /* LNetNIInit/LNetNIFini counter */
+ int ln_refcount;
+ /* shutdown in progress */
+ int ln_shutdown;
+
+ int ln_routing; /* am I a router? */
+ lnet_pid_t ln_pid; /* requested pid */
+ /* uniquely identifies this ni in this epoch */
+ __u64 ln_interface_cookie;
+ /* registered LNDs */
+ struct list_head ln_lnds;
+
+ /* space for network names */
+ char *ln_network_tokens;
+ int ln_network_tokens_nob;
+ /* test protocol compatibility flags */
+ int ln_testprotocompat;
+
+} lnet_t;
+
+#endif
diff --git a/drivers/staging/lustre/include/linux/lnet/linux/api-support.h b/drivers/staging/lustre/include/linux/lnet/linux/api-support.h
new file mode 100644
index 000000000..e237ad6af
--- /dev/null
+++ b/drivers/staging/lustre/include/linux/lnet/linux/api-support.h
@@ -0,0 +1,42 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ */
+
+#ifndef __LINUX_API_SUPPORT_H__
+#define __LINUX_API_SUPPORT_H__
+
+#ifndef __LNET_API_SUPPORT_H__
+#error Do not #include this file directly. #include <lnet /api-support.h> instead
+#endif
+
+#endif
diff --git a/drivers/staging/lustre/include/linux/lnet/linux/lib-lnet.h b/drivers/staging/lustre/include/linux/lnet/linux/lib-lnet.h
new file mode 100644
index 000000000..0f8f04d1e
--- /dev/null
+++ b/drivers/staging/lustre/include/linux/lnet/linux/lib-lnet.h
@@ -0,0 +1,71 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ */
+
+#ifndef __LNET_LINUX_LIB_LNET_H__
+#define __LNET_LINUX_LIB_LNET_H__
+
+#ifndef __LNET_LIB_LNET_H__
+#error Do not #include this file directly. #include <linux/lnet/lib-lnet.h> instead
+#endif
+
+# include <asm/page.h>
+# include <linux/string.h>
+# include <asm/io.h>
+#include "../../libcfs/libcfs.h"
+
+static inline __u64
+lnet_page2phys(struct page *p)
+{
+ /* compiler optimizer will elide unused branches */
+
+ switch (sizeof(typeof(page_to_phys(p)))) {
+ case 4:
+ /* page_to_phys returns a 32 bit physical address. This must
+ * be a 32 bit machine with <= 4G memory and we must ensure we
+ * don't sign extend when converting to 64 bits. */
+ return (unsigned long)page_to_phys(p);
+
+ case 8:
+ /* page_to_phys returns a 64 bit physical address :) */
+ return page_to_phys(p);
+
+ default:
+ LBUG();
+ return 0;
+ }
+}
+
+#define LNET_ROUTER
+
+#endif /* __LNET_LINUX_LIB_LNET_H__ */
diff --git a/drivers/staging/lustre/include/linux/lnet/linux/lib-types.h b/drivers/staging/lustre/include/linux/lnet/linux/lib-types.h
new file mode 100644
index 000000000..669e8c038
--- /dev/null
+++ b/drivers/staging/lustre/include/linux/lnet/linux/lib-types.h
@@ -0,0 +1,45 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ */
+
+#ifndef __LNET_LINUX_LIB_TYPES_H__
+#define __LNET_LINUX_LIB_TYPES_H__
+
+#ifndef __LNET_LIB_TYPES_H__
+#error Do not #include this file directly. #include <linux/lnet/lib-types.h> instead
+#endif
+
+# include <linux/uio.h>
+# include <linux/types.h>
+
+#endif
diff --git a/drivers/staging/lustre/include/linux/lnet/linux/lnet.h b/drivers/staging/lustre/include/linux/lnet/linux/lnet.h
new file mode 100644
index 000000000..1e888f1ef
--- /dev/null
+++ b/drivers/staging/lustre/include/linux/lnet/linux/lnet.h
@@ -0,0 +1,56 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2011, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ */
+
+#ifndef __LNET_LINUX_LNET_H__
+#define __LNET_LINUX_LNET_H__
+
+#ifndef __LNET_H__
+#error Do not #include this file directly. #include <linux/lnet/lnet.h> instead
+#endif
+
+/*
+ * lnet.h
+ *
+ * User application interface file
+ */
+
+#include <linux/uio.h>
+#include <linux/types.h>
+
+#define cfs_tcp_sendpage(sk, page, offset, size, flags) \
+ tcp_sendpage(sk, page, offset, size, flags)
+
+#endif
diff --git a/drivers/staging/lustre/include/linux/lnet/lnet-sysctl.h b/drivers/staging/lustre/include/linux/lnet/lnet-sysctl.h
new file mode 100644
index 000000000..2dee1b97f
--- /dev/null
+++ b/drivers/staging/lustre/include/linux/lnet/lnet-sysctl.h
@@ -0,0 +1,49 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ */
+
+#ifndef __LNET_SYSCTL_H__
+#define __LNET_SYSCTL_H__
+
+#if defined(CONFIG_SYSCTL)
+
+#define CTL_KRANAL 201
+#define CTL_O2IBLND 205
+#define CTL_PTLLND 206
+#define CTL_QSWNAL 207
+#define CTL_SOCKLND 208
+#define CTL_GNILND 210
+
+#endif
+
+#endif
diff --git a/drivers/staging/lustre/include/linux/lnet/lnet.h b/drivers/staging/lustre/include/linux/lnet/lnet.h
new file mode 100644
index 000000000..75c0ab919
--- /dev/null
+++ b/drivers/staging/lustre/include/linux/lnet/lnet.h
@@ -0,0 +1,51 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ */
+
+#ifndef __LNET_H__
+#define __LNET_H__
+
+/*
+ * lnet.h
+ *
+ * User application interface file
+ */
+#include "linux/lnet.h"
+
+#include "types.h"
+#include "api.h"
+
+#define LNET_NIDSTR_COUNT 1024 /* # of nidstrings */
+#define LNET_NIDSTR_SIZE 32 /* size of each one (see below for usage) */
+
+#endif
diff --git a/drivers/staging/lustre/include/linux/lnet/lnetctl.h b/drivers/staging/lustre/include/linux/lnet/lnetctl.h
new file mode 100644
index 000000000..98181d389
--- /dev/null
+++ b/drivers/staging/lustre/include/linux/lnet/lnetctl.h
@@ -0,0 +1,80 @@
+/*
+ * This file is part of Portals, http://www.sf.net/projects/lustre/
+ *
+ * Portals is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * Portals 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 Portals; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * header for libptlctl.a
+ */
+#ifndef _PTLCTL_H_
+#define _PTLCTL_H_
+
+#include "../libcfs/libcfs.h"
+#include "types.h"
+
+#define LNET_DEV_ID 0
+#define LNET_DEV_PATH "/dev/lnet"
+#define LNET_DEV_MAJOR 10
+#define LNET_DEV_MINOR 240
+#define OBD_DEV_ID 1
+#define OBD_DEV_NAME "obd"
+#define OBD_DEV_PATH "/dev/" OBD_DEV_NAME
+#define OBD_DEV_MAJOR 10
+#define OBD_DEV_MINOR 241
+#define SMFS_DEV_ID 2
+#define SMFS_DEV_PATH "/dev/snapdev"
+#define SMFS_DEV_MAJOR 10
+#define SMFS_DEV_MINOR 242
+
+int ptl_initialize(int argc, char **argv);
+int jt_ptl_network(int argc, char **argv);
+int jt_ptl_list_nids(int argc, char **argv);
+int jt_ptl_which_nid(int argc, char **argv);
+int jt_ptl_print_interfaces(int argc, char **argv);
+int jt_ptl_add_interface(int argc, char **argv);
+int jt_ptl_del_interface(int argc, char **argv);
+int jt_ptl_print_peers(int argc, char **argv);
+int jt_ptl_add_peer(int argc, char **argv);
+int jt_ptl_del_peer(int argc, char **argv);
+int jt_ptl_print_connections(int argc, char **argv);
+int jt_ptl_disconnect(int argc, char **argv);
+int jt_ptl_push_connection(int argc, char **argv);
+int jt_ptl_print_active_txs(int argc, char **argv);
+int jt_ptl_ping(int argc, char **argv);
+int jt_ptl_mynid(int argc, char **argv);
+int jt_ptl_add_uuid(int argc, char **argv);
+int jt_ptl_add_uuid_old(int argc, char **argv); /* backwards compatibility */
+int jt_ptl_close_uuid(int argc, char **argv);
+int jt_ptl_del_uuid(int argc, char **argv);
+int jt_ptl_add_route(int argc, char **argv);
+int jt_ptl_del_route(int argc, char **argv);
+int jt_ptl_notify_router(int argc, char **argv);
+int jt_ptl_print_routes(int argc, char **argv);
+int jt_ptl_fail_nid(int argc, char **argv);
+int jt_ptl_lwt(int argc, char **argv);
+int jt_ptl_testprotocompat(int argc, char **argv);
+int jt_ptl_memhog(int argc, char **argv);
+
+int dbg_initialize(int argc, char **argv);
+int jt_dbg_filter(int argc, char **argv);
+int jt_dbg_show(int argc, char **argv);
+int jt_dbg_list(int argc, char **argv);
+int jt_dbg_debug_kernel(int argc, char **argv);
+int jt_dbg_debug_daemon(int argc, char **argv);
+int jt_dbg_debug_file(int argc, char **argv);
+int jt_dbg_clear_debug_buf(int argc, char **argv);
+int jt_dbg_mark_debug_buf(int argc, char **argv);
+int jt_dbg_modules(int argc, char **argv);
+int jt_dbg_panic(int argc, char **argv);
+
+#endif
diff --git a/drivers/staging/lustre/include/linux/lnet/lnetst.h b/drivers/staging/lustre/include/linux/lnet/lnetst.h
new file mode 100644
index 000000000..885f708d4
--- /dev/null
+++ b/drivers/staging/lustre/include/linux/lnet/lnetst.h
@@ -0,0 +1,491 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2011, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ *
+ * lnet/include/lnet/lnetst.h
+ *
+ * Author: Liang Zhen <liangzhen@clusterfs.com>
+ */
+
+#ifndef __LNET_ST_H__
+#define __LNET_ST_H__
+
+#include "../libcfs/libcfs.h"
+#include "lnet.h"
+#include "lib-types.h"
+
+#define LST_FEAT_NONE (0)
+#define LST_FEAT_BULK_LEN (1 << 0) /* enable variable page size */
+
+#define LST_FEATS_EMPTY (LST_FEAT_NONE)
+#define LST_FEATS_MASK (LST_FEAT_NONE | LST_FEAT_BULK_LEN)
+
+#define LST_NAME_SIZE 32 /* max name buffer length */
+
+#define LSTIO_DEBUG 0xC00 /* debug */
+#define LSTIO_SESSION_NEW 0xC01 /* create session */
+#define LSTIO_SESSION_END 0xC02 /* end session */
+#define LSTIO_SESSION_INFO 0xC03 /* query session */
+#define LSTIO_GROUP_ADD 0xC10 /* add group */
+#define LSTIO_GROUP_LIST 0xC11 /* list all groups in session */
+#define LSTIO_GROUP_INFO 0xC12 /* query default information of specified group */
+#define LSTIO_GROUP_DEL 0xC13 /* delete group */
+#define LSTIO_NODES_ADD 0xC14 /* add nodes to specified group */
+#define LSTIO_GROUP_UPDATE 0xC15 /* update group */
+#define LSTIO_BATCH_ADD 0xC20 /* add batch */
+#define LSTIO_BATCH_START 0xC21 /* start batch */
+#define LSTIO_BATCH_STOP 0xC22 /* stop batch */
+#define LSTIO_BATCH_DEL 0xC23 /* delete batch */
+#define LSTIO_BATCH_LIST 0xC24 /* show all batches in the session */
+#define LSTIO_BATCH_INFO 0xC25 /* show defail of specified batch */
+#define LSTIO_TEST_ADD 0xC26 /* add test (to batch) */
+#define LSTIO_BATCH_QUERY 0xC27 /* query batch status */
+#define LSTIO_STAT_QUERY 0xC30 /* get stats */
+
+typedef struct {
+ lnet_nid_t ses_nid; /* nid of console node */
+ __u64 ses_stamp; /* time stamp */
+} lst_sid_t; /*** session id */
+
+extern lst_sid_t LST_INVALID_SID;
+
+typedef struct {
+ __u64 bat_id; /* unique id in session */
+} lst_bid_t; /*** batch id (group of tests) */
+
+/* Status of test node */
+#define LST_NODE_ACTIVE 0x1 /* node in this session */
+#define LST_NODE_BUSY 0x2 /* node is taken by other session */
+#define LST_NODE_DOWN 0x4 /* node is down */
+#define LST_NODE_UNKNOWN 0x8 /* node not in session */
+
+typedef struct {
+ lnet_process_id_t nde_id; /* id of node */
+ int nde_state; /* state of node */
+} lstcon_node_ent_t; /*** node entry, for list_group command */
+
+typedef struct {
+ int nle_nnode; /* # of nodes */
+ int nle_nactive; /* # of active nodes */
+ int nle_nbusy; /* # of busy nodes */
+ int nle_ndown; /* # of down nodes */
+ int nle_nunknown; /* # of unknown nodes */
+} lstcon_ndlist_ent_t; /*** node_list entry, for list_batch command */
+
+typedef struct {
+ int tse_type; /* test type */
+ int tse_loop; /* loop count */
+ int tse_concur; /* concurrency of test */
+} lstcon_test_ent_t; /*** test summary entry, for list_batch command */
+
+typedef struct {
+ int bae_state; /* batch status */
+ int bae_timeout; /* batch timeout */
+ int bae_ntest; /* # of tests in the batch */
+} lstcon_batch_ent_t; /*** batch summary entry, for list_batch command */
+
+typedef struct {
+ lstcon_ndlist_ent_t tbe_cli_nle; /* client (group) node_list entry */
+ lstcon_ndlist_ent_t tbe_srv_nle; /* server (group) node_list entry */
+ union {
+ lstcon_test_ent_t tbe_test; /* test entry */
+ lstcon_batch_ent_t tbe_batch; /* batch entry */
+ } u;
+} lstcon_test_batch_ent_t; /*** test/batch verbose information entry,
+ *** for list_batch command */
+
+typedef struct {
+ struct list_head rpe_link; /* link chain */
+ lnet_process_id_t rpe_peer; /* peer's id */
+ struct timeval rpe_stamp; /* time stamp of RPC */
+ int rpe_state; /* peer's state */
+ int rpe_rpc_errno; /* RPC errno */
+
+ lst_sid_t rpe_sid; /* peer's session id */
+ int rpe_fwk_errno; /* framework errno */
+ int rpe_priv[4]; /* private data */
+ char rpe_payload[0]; /* private reply payload */
+} lstcon_rpc_ent_t;
+
+typedef struct {
+ int trs_rpc_stat[4]; /* RPCs stat (0: total, 1: failed, 2: finished, 4: reserved */
+ int trs_rpc_errno; /* RPC errno */
+ int trs_fwk_stat[8]; /* framework stat */
+ int trs_fwk_errno; /* errno of the first remote error */
+ void *trs_fwk_private; /* private framework stat */
+} lstcon_trans_stat_t;
+
+static inline int
+lstcon_rpc_stat_total(lstcon_trans_stat_t *stat, int inc)
+{
+ return inc ? ++stat->trs_rpc_stat[0] : stat->trs_rpc_stat[0];
+}
+
+static inline int
+lstcon_rpc_stat_success(lstcon_trans_stat_t *stat, int inc)
+{
+ return inc ? ++stat->trs_rpc_stat[1] : stat->trs_rpc_stat[1];
+}
+
+static inline int
+lstcon_rpc_stat_failure(lstcon_trans_stat_t *stat, int inc)
+{
+ return inc ? ++stat->trs_rpc_stat[2] : stat->trs_rpc_stat[2];
+}
+
+static inline int
+lstcon_sesop_stat_success(lstcon_trans_stat_t *stat, int inc)
+{
+ return inc ? ++stat->trs_fwk_stat[0] : stat->trs_fwk_stat[0];
+}
+
+static inline int
+lstcon_sesop_stat_failure(lstcon_trans_stat_t *stat, int inc)
+{
+ return inc ? ++stat->trs_fwk_stat[1] : stat->trs_fwk_stat[1];
+}
+
+static inline int
+lstcon_sesqry_stat_active(lstcon_trans_stat_t *stat, int inc)
+{
+ return inc ? ++stat->trs_fwk_stat[0] : stat->trs_fwk_stat[0];
+}
+
+static inline int
+lstcon_sesqry_stat_busy(lstcon_trans_stat_t *stat, int inc)
+{
+ return inc ? ++stat->trs_fwk_stat[1] : stat->trs_fwk_stat[1];
+}
+
+static inline int
+lstcon_sesqry_stat_unknown(lstcon_trans_stat_t *stat, int inc)
+{
+ return inc ? ++stat->trs_fwk_stat[2] : stat->trs_fwk_stat[2];
+}
+
+static inline int
+lstcon_tsbop_stat_success(lstcon_trans_stat_t *stat, int inc)
+{
+ return inc ? ++stat->trs_fwk_stat[0] : stat->trs_fwk_stat[0];
+}
+
+static inline int
+lstcon_tsbop_stat_failure(lstcon_trans_stat_t *stat, int inc)
+{
+ return inc ? ++stat->trs_fwk_stat[1] : stat->trs_fwk_stat[1];
+}
+
+static inline int
+lstcon_tsbqry_stat_idle(lstcon_trans_stat_t *stat, int inc)
+{
+ return inc ? ++stat->trs_fwk_stat[0] : stat->trs_fwk_stat[0];
+}
+
+static inline int
+lstcon_tsbqry_stat_run(lstcon_trans_stat_t *stat, int inc)
+{
+ return inc ? ++stat->trs_fwk_stat[1] : stat->trs_fwk_stat[1];
+}
+
+static inline int
+lstcon_tsbqry_stat_failure(lstcon_trans_stat_t *stat, int inc)
+{
+ return inc ? ++stat->trs_fwk_stat[2] : stat->trs_fwk_stat[2];
+}
+
+static inline int
+lstcon_statqry_stat_success(lstcon_trans_stat_t *stat, int inc)
+{
+ return inc ? ++stat->trs_fwk_stat[0] : stat->trs_fwk_stat[0];
+}
+
+static inline int
+lstcon_statqry_stat_failure(lstcon_trans_stat_t *stat, int inc)
+{
+ return inc ? ++stat->trs_fwk_stat[1] : stat->trs_fwk_stat[1];
+}
+
+/* create a session */
+typedef struct {
+ int lstio_ses_key; /* IN: local key */
+ int lstio_ses_timeout; /* IN: session timeout */
+ int lstio_ses_force; /* IN: force create ? */
+ /** IN: session features */
+ unsigned lstio_ses_feats;
+ lst_sid_t *lstio_ses_idp; /* OUT: session id */
+ int lstio_ses_nmlen; /* IN: name length */
+ char *lstio_ses_namep; /* IN: session name */
+} lstio_session_new_args_t;
+
+/* query current session */
+typedef struct {
+ lst_sid_t *lstio_ses_idp; /* OUT: session id */
+ int *lstio_ses_keyp; /* OUT: local key */
+ /** OUT: session features */
+ unsigned *lstio_ses_featp;
+ lstcon_ndlist_ent_t *lstio_ses_ndinfo; /* OUT: */
+ int lstio_ses_nmlen; /* IN: name length */
+ char *lstio_ses_namep; /* OUT: session name */
+} lstio_session_info_args_t;
+
+/* delete a session */
+typedef struct {
+ int lstio_ses_key; /* IN: session key */
+} lstio_session_end_args_t;
+
+#define LST_OPC_SESSION 1
+#define LST_OPC_GROUP 2
+#define LST_OPC_NODES 3
+#define LST_OPC_BATCHCLI 4
+#define LST_OPC_BATCHSRV 5
+
+typedef struct {
+ int lstio_dbg_key; /* IN: session key */
+ int lstio_dbg_type; /* IN: debug sessin|batch|group|nodes list */
+ int lstio_dbg_flags; /* IN: reserved debug flags */
+ int lstio_dbg_timeout; /* IN: timeout of debug */
+
+ int lstio_dbg_nmlen; /* IN: len of name */
+ char *lstio_dbg_namep; /* IN: name of group|batch */
+ int lstio_dbg_count; /* IN: # of test nodes to debug */
+ lnet_process_id_t *lstio_dbg_idsp; /* IN: id of test nodes */
+ struct list_head *lstio_dbg_resultp; /* OUT: list head of result buffer */
+} lstio_debug_args_t;
+
+typedef struct {
+ int lstio_grp_key; /* IN: session key */
+ int lstio_grp_nmlen; /* IN: name length */
+ char *lstio_grp_namep; /* IN: group name */
+} lstio_group_add_args_t;
+
+typedef struct {
+ int lstio_grp_key; /* IN: session key */
+ int lstio_grp_nmlen; /* IN: name length */
+ char *lstio_grp_namep; /* IN: group name */
+} lstio_group_del_args_t;
+
+#define LST_GROUP_CLEAN 1 /* remove inactive nodes in the group */
+#define LST_GROUP_REFRESH 2 /* refresh inactive nodes in the group */
+#define LST_GROUP_RMND 3 /* delete nodes from the group */
+
+typedef struct {
+ int lstio_grp_key; /* IN: session key */
+ int lstio_grp_opc; /* IN: OPC */
+ int lstio_grp_args; /* IN: arguments */
+ int lstio_grp_nmlen; /* IN: name length */
+ char *lstio_grp_namep; /* IN: group name */
+ int lstio_grp_count; /* IN: # of nodes id */
+ lnet_process_id_t *lstio_grp_idsp; /* IN: array of nodes */
+ struct list_head *lstio_grp_resultp; /* OUT: list head of result buffer */
+} lstio_group_update_args_t;
+
+typedef struct {
+ int lstio_grp_key; /* IN: session key */
+ int lstio_grp_nmlen; /* IN: name length */
+ char *lstio_grp_namep; /* IN: group name */
+ int lstio_grp_count; /* IN: # of nodes */
+ /** OUT: session features */
+ unsigned *lstio_grp_featp;
+ lnet_process_id_t *lstio_grp_idsp; /* IN: nodes */
+ struct list_head *lstio_grp_resultp; /* OUT: list head of result buffer */
+} lstio_group_nodes_args_t;
+
+typedef struct {
+ int lstio_grp_key; /* IN: session key */
+ int lstio_grp_idx; /* IN: group idx */
+ int lstio_grp_nmlen; /* IN: name len */
+ char *lstio_grp_namep; /* OUT: name */
+} lstio_group_list_args_t;
+
+typedef struct {
+ int lstio_grp_key; /* IN: session key */
+ int lstio_grp_nmlen; /* IN: name len */
+ char *lstio_grp_namep; /* IN: name */
+ lstcon_ndlist_ent_t *lstio_grp_entp; /* OUT: description of group */
+
+ int *lstio_grp_idxp; /* IN/OUT: node index */
+ int *lstio_grp_ndentp; /* IN/OUT: # of nodent */
+ lstcon_node_ent_t *lstio_grp_dentsp; /* OUT: nodent array */
+} lstio_group_info_args_t;
+
+#define LST_DEFAULT_BATCH "batch" /* default batch name */
+
+typedef struct {
+ int lstio_bat_key; /* IN: session key */
+ int lstio_bat_nmlen; /* IN: name length */
+ char *lstio_bat_namep; /* IN: batch name */
+} lstio_batch_add_args_t;
+
+typedef struct {
+ int lstio_bat_key; /* IN: session key */
+ int lstio_bat_nmlen; /* IN: name length */
+ char *lstio_bat_namep; /* IN: batch name */
+} lstio_batch_del_args_t;
+
+typedef struct {
+ int lstio_bat_key; /* IN: session key */
+ int lstio_bat_timeout; /* IN: timeout for the batch */
+ int lstio_bat_nmlen; /* IN: name length */
+ char *lstio_bat_namep; /* IN: batch name */
+ struct list_head *lstio_bat_resultp; /* OUT: list head of result buffer */
+} lstio_batch_run_args_t;
+
+typedef struct {
+ int lstio_bat_key; /* IN: session key */
+ int lstio_bat_force; /* IN: abort unfinished test RPC */
+ int lstio_bat_nmlen; /* IN: name length */
+ char *lstio_bat_namep; /* IN: batch name */
+ struct list_head *lstio_bat_resultp; /* OUT: list head of result buffer */
+} lstio_batch_stop_args_t;
+
+typedef struct {
+ int lstio_bat_key; /* IN: session key */
+ int lstio_bat_testidx; /* IN: test index */
+ int lstio_bat_client; /* IN: is test client? */
+ int lstio_bat_timeout; /* IN: timeout for waiting */
+ int lstio_bat_nmlen; /* IN: name length */
+ char *lstio_bat_namep; /* IN: batch name */
+ struct list_head *lstio_bat_resultp; /* OUT: list head of result buffer */
+} lstio_batch_query_args_t;
+
+typedef struct {
+ int lstio_bat_key; /* IN: session key */
+ int lstio_bat_idx; /* IN: index */
+ int lstio_bat_nmlen; /* IN: name length */
+ char *lstio_bat_namep; /* IN: batch name */
+} lstio_batch_list_args_t;
+
+typedef struct {
+ int lstio_bat_key; /* IN: session key */
+ int lstio_bat_nmlen; /* IN: name length */
+ char *lstio_bat_namep; /* IN: name */
+ int lstio_bat_server; /* IN: query server or not */
+ int lstio_bat_testidx; /* IN: test index */
+ lstcon_test_batch_ent_t *lstio_bat_entp; /* OUT: batch ent */
+
+ int *lstio_bat_idxp; /* IN/OUT: index of node */
+ int *lstio_bat_ndentp; /* IN/OUT: # of nodent */
+ lstcon_node_ent_t *lstio_bat_dentsp; /* array of nodent */
+} lstio_batch_info_args_t;
+
+/* add stat in session */
+typedef struct {
+ int lstio_sta_key; /* IN: session key */
+ int lstio_sta_timeout; /* IN: timeout for stat request */
+ int lstio_sta_nmlen; /* IN: group name length */
+ char *lstio_sta_namep; /* IN: group name */
+ int lstio_sta_count; /* IN: # of pid */
+ lnet_process_id_t *lstio_sta_idsp; /* IN: pid */
+ struct list_head *lstio_sta_resultp; /* OUT: list head of result buffer */
+} lstio_stat_args_t;
+
+typedef enum {
+ LST_TEST_BULK = 1,
+ LST_TEST_PING = 2
+} lst_test_type_t;
+
+/* create a test in a batch */
+#define LST_MAX_CONCUR 1024 /* Max concurrency of test */
+
+typedef struct {
+ int lstio_tes_key; /* IN: session key */
+ int lstio_tes_bat_nmlen; /* IN: batch name len */
+ char *lstio_tes_bat_name; /* IN: batch name */
+ int lstio_tes_type; /* IN: test type */
+ int lstio_tes_oneside; /* IN: one sided test */
+ int lstio_tes_loop; /* IN: loop count */
+ int lstio_tes_concur; /* IN: concurrency */
+
+ int lstio_tes_dist; /* IN: node distribution in destination groups */
+ int lstio_tes_span; /* IN: node span in destination groups */
+ int lstio_tes_sgrp_nmlen; /* IN: source group name length */
+ char *lstio_tes_sgrp_name; /* IN: group name */
+ int lstio_tes_dgrp_nmlen; /* IN: destination group name length */
+ char *lstio_tes_dgrp_name; /* IN: group name */
+
+ int lstio_tes_param_len; /* IN: param buffer len */
+ void *lstio_tes_param; /* IN: parameter for specified test:
+ lstio_bulk_param_t,
+ lstio_ping_param_t,
+ ... more */
+ int *lstio_tes_retp; /* OUT: private returned value */
+ struct list_head *lstio_tes_resultp; /* OUT: list head of result buffer */
+} lstio_test_args_t;
+
+typedef enum {
+ LST_BRW_READ = 1,
+ LST_BRW_WRITE = 2
+} lst_brw_type_t;
+
+typedef enum {
+ LST_BRW_CHECK_NONE = 1,
+ LST_BRW_CHECK_SIMPLE = 2,
+ LST_BRW_CHECK_FULL = 3
+} lst_brw_flags_t;
+
+typedef struct {
+ int blk_opc; /* bulk operation code */
+ int blk_size; /* size (bytes) */
+ int blk_time; /* time of running the test*/
+ int blk_flags; /* reserved flags */
+} lst_test_bulk_param_t;
+
+typedef struct {
+ int png_size; /* size of ping message */
+ int png_time; /* time */
+ int png_loop; /* loop */
+ int png_flags; /* reserved flags */
+} lst_test_ping_param_t;
+
+/* more tests */
+typedef struct {
+ __u32 errors;
+ __u32 rpcs_sent;
+ __u32 rpcs_rcvd;
+ __u32 rpcs_dropped;
+ __u32 rpcs_expired;
+ __u64 bulk_get;
+ __u64 bulk_put;
+} WIRE_ATTR srpc_counters_t;
+
+typedef struct {
+ /** milliseconds since current session started */
+ __u32 running_ms;
+ __u32 active_batches;
+ __u32 zombie_sessions;
+ __u32 brw_errors;
+ __u32 ping_errors;
+} WIRE_ATTR sfw_counters_t;
+
+#endif
diff --git a/drivers/staging/lustre/include/linux/lnet/ptllnd.h b/drivers/staging/lustre/include/linux/lnet/ptllnd.h
new file mode 100644
index 000000000..c91d65329
--- /dev/null
+++ b/drivers/staging/lustre/include/linux/lnet/ptllnd.h
@@ -0,0 +1,93 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ *
+ * lnet/include/lnet/ptllnd.h
+ *
+ * Author: PJ Kirner <pjkirner@clusterfs.com>
+ */
+
+/*
+ * The PTLLND was designed to support Portals with
+ * Lustre and non-lustre UNLINK semantics.
+ * However for now the two targets are Cray Portals
+ * on the XT3 and Lustre Portals (for testing) both
+ * have Lustre UNLINK semantics, so this is defined
+ * by default.
+ */
+#define LUSTRE_PORTALS_UNLINK_SEMANTICS
+
+#ifdef _USING_LUSTRE_PORTALS_
+
+/* NIDs are 64-bits on Lustre Portals */
+#define FMT_NID "%llu"
+#define FMT_PID "%d"
+
+/* When using Lustre Portals Lustre completion semantics are imlicit*/
+#define PTL_MD_LUSTRE_COMPLETION_SEMANTICS 0
+
+#else /* _USING_CRAY_PORTALS_ */
+
+/* NIDs are integers on Cray Portals */
+#define FMT_NID "%u"
+#define FMT_PID "%d"
+
+/* When using Cray Portals this is defined in the Cray Portals Header*/
+/*#define PTL_MD_LUSTRE_COMPLETION_SEMANTICS */
+
+/* Can compare handles directly on Cray Portals */
+#define PtlHandleIsEqual(a, b) ((a) == (b))
+
+/* Different error types on Cray Portals*/
+#define ptl_err_t ptl_ni_fail_t
+
+/*
+ * The Cray Portals has no maximum number of IOVs. The
+ * maximum is limited only by memory and size of the
+ * int parameters (2^31-1).
+ * Lustre only really require that the underyling
+ * implementation to support at least LNET_MAX_IOV,
+ * so for Cray portals we can safely just use that
+ * value here.
+ *
+ */
+#define PTL_MD_MAX_IOV LNET_MAX_IOV
+
+#endif
+
+#define FMT_PTLID "ptlid:"FMT_PID"-"FMT_NID
+
+/* Align incoming small request messages to an 8 byte boundary if this is
+ * supported to avoid alignment issues on some architectures */
+#ifndef PTL_MD_LOCAL_ALIGN8
+# define PTL_MD_LOCAL_ALIGN8 0
+#endif
diff --git a/drivers/staging/lustre/include/linux/lnet/ptllnd_wire.h b/drivers/staging/lustre/include/linux/lnet/ptllnd_wire.h
new file mode 100644
index 000000000..808f37b64
--- /dev/null
+++ b/drivers/staging/lustre/include/linux/lnet/ptllnd_wire.h
@@ -0,0 +1,119 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ *
+ * lnet/include/lnet/ptllnd_wire.h
+ *
+ * Author: PJ Kirner <pjkirner@clusterfs.com>
+ */
+
+/* Minimum buffer size that any peer will post to receive ptllnd messages */
+#define PTLLND_MIN_BUFFER_SIZE 256
+
+/************************************************************************
+ * Tunable defaults that {u,k}lnds/ptllnd should have in common.
+ */
+
+#define PTLLND_PORTAL 9 /* The same portal PTLPRC used when talking to cray portals */
+#define PTLLND_PID 9 /* The Portals PID */
+#define PTLLND_PEERCREDITS 8 /* concurrent sends to 1 peer */
+
+/* Default buffer size for kernel ptllnds (guaranteed eager) */
+#define PTLLND_MAX_KLND_MSG_SIZE 512
+
+/* Default buffer size for catamount ptllnds (not guaranteed eager) - large
+ * enough to avoid RDMA for anything sent while control is not in liblustre */
+#define PTLLND_MAX_ULND_MSG_SIZE 512
+
+/************************************************************************
+ * Portals LND Wire message format.
+ * These are sent in sender's byte order (i.e. receiver flips).
+ */
+
+#define PTL_RESERVED_MATCHBITS 0x100 /* below this value is reserved
+ * above is for bulk data transfer */
+#define LNET_MSG_MATCHBITS 0 /* the value for the message channel */
+
+typedef struct {
+ lnet_hdr_t kptlim_hdr; /* portals header */
+ char kptlim_payload[0]; /* piggy-backed payload */
+} WIRE_ATTR kptl_immediate_msg_t;
+
+typedef struct {
+ lnet_hdr_t kptlrm_hdr; /* portals header */
+ __u64 kptlrm_matchbits; /* matchbits */
+} WIRE_ATTR kptl_rdma_msg_t;
+
+typedef struct {
+ __u64 kptlhm_matchbits; /* matchbits */
+ __u32 kptlhm_max_msg_size; /* max message size */
+} WIRE_ATTR kptl_hello_msg_t;
+
+typedef struct {
+ /* First 2 fields fixed FOR ALL TIME */
+ __u32 ptlm_magic; /* I'm a Portals LND message */
+ __u16 ptlm_version; /* this is my version number */
+ __u8 ptlm_type; /* the message type */
+ __u8 ptlm_credits; /* returned credits */
+ __u32 ptlm_nob; /* # bytes in whole message */
+ __u32 ptlm_cksum; /* checksum (0 == no checksum) */
+ __u64 ptlm_srcnid; /* sender's NID */
+ __u64 ptlm_srcstamp; /* sender's incarnation */
+ __u64 ptlm_dstnid; /* destination's NID */
+ __u64 ptlm_dststamp; /* destination's incarnation */
+ __u32 ptlm_srcpid; /* sender's PID */
+ __u32 ptlm_dstpid; /* destination's PID */
+
+ union {
+ kptl_immediate_msg_t immediate;
+ kptl_rdma_msg_t rdma;
+ kptl_hello_msg_t hello;
+ } WIRE_ATTR ptlm_u;
+
+} kptl_msg_t;
+
+/* kptl_msg_t::ptlm_credits is only a __u8 */
+#define PTLLND_MSG_MAX_CREDITS ((typeof(((kptl_msg_t *)0)->ptlm_credits)) - 1)
+
+#define PTLLND_MSG_MAGIC LNET_PROTO_PTL_MAGIC
+#define PTLLND_MSG_VERSION 0x04
+
+#define PTLLND_RDMA_OK 0x00
+#define PTLLND_RDMA_FAIL 0x01
+
+#define PTLLND_MSG_TYPE_INVALID 0x00
+#define PTLLND_MSG_TYPE_PUT 0x01
+#define PTLLND_MSG_TYPE_GET 0x02
+#define PTLLND_MSG_TYPE_IMMEDIATE 0x03 /* No bulk data xfer*/
+#define PTLLND_MSG_TYPE_NOOP 0x04
+#define PTLLND_MSG_TYPE_HELLO 0x05
+#define PTLLND_MSG_TYPE_NAK 0x06
diff --git a/drivers/staging/lustre/include/linux/lnet/socklnd.h b/drivers/staging/lustre/include/linux/lnet/socklnd.h
new file mode 100644
index 000000000..389038b12
--- /dev/null
+++ b/drivers/staging/lustre/include/linux/lnet/socklnd.h
@@ -0,0 +1,103 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ *
+ * lnet/include/lnet/socklnd.h
+ *
+ * #defines shared between socknal implementation and utilities
+ */
+#ifndef __LNET_LNET_SOCKLND_H__
+#define __LNET_LNET_SOCKLND_H__
+
+#include "types.h"
+#include "lib-types.h"
+
+#define SOCKLND_CONN_NONE (-1)
+#define SOCKLND_CONN_ANY 0
+#define SOCKLND_CONN_CONTROL 1
+#define SOCKLND_CONN_BULK_IN 2
+#define SOCKLND_CONN_BULK_OUT 3
+#define SOCKLND_CONN_NTYPES 4
+
+#define SOCKLND_CONN_ACK SOCKLND_CONN_BULK_IN
+
+typedef struct {
+ __u32 kshm_magic; /* magic number of socklnd message */
+ __u32 kshm_version; /* version of socklnd message */
+ lnet_nid_t kshm_src_nid; /* sender's nid */
+ lnet_nid_t kshm_dst_nid; /* destination nid */
+ lnet_pid_t kshm_src_pid; /* sender's pid */
+ lnet_pid_t kshm_dst_pid; /* destination pid */
+ __u64 kshm_src_incarnation; /* sender's incarnation */
+ __u64 kshm_dst_incarnation; /* destination's incarnation */
+ __u32 kshm_ctype; /* connection type */
+ __u32 kshm_nips; /* # IP addrs */
+ __u32 kshm_ips[0]; /* IP addrs */
+} WIRE_ATTR ksock_hello_msg_t;
+
+typedef struct {
+ lnet_hdr_t ksnm_hdr; /* lnet hdr */
+
+ /*
+ * ksnm_payload is removed because of winnt compiler's limitation:
+ * zero-sized array can only be placed at the tail of [nested]
+ * structure definitions. lnet payload will be stored just after
+ * the body of structure ksock_lnet_msg_t
+ */
+} WIRE_ATTR ksock_lnet_msg_t;
+
+typedef struct {
+ __u32 ksm_type; /* type of socklnd message */
+ __u32 ksm_csum; /* checksum if != 0 */
+ __u64 ksm_zc_cookies[2]; /* Zero-Copy request/ACK cookie */
+ union {
+ ksock_lnet_msg_t lnetmsg; /* lnet message, it's empty if it's NOOP */
+ } WIRE_ATTR ksm_u;
+} WIRE_ATTR ksock_msg_t;
+
+static inline void
+socklnd_init_msg(ksock_msg_t *msg, int type)
+{
+ msg->ksm_csum = 0;
+ msg->ksm_type = type;
+ msg->ksm_zc_cookies[0] = msg->ksm_zc_cookies[1] = 0;
+}
+
+#define KSOCK_MSG_NOOP 0xc0 /* ksm_u empty */
+#define KSOCK_MSG_LNET 0xc1 /* lnet msg */
+
+/* We need to know this number to parse hello msg from ksocklnd in
+ * other LND (usocklnd, for example) */
+#define KSOCK_PROTO_V2 2
+#define KSOCK_PROTO_V3 3
+
+#endif
diff --git a/drivers/staging/lustre/include/linux/lnet/types.h b/drivers/staging/lustre/include/linux/lnet/types.h
new file mode 100644
index 000000000..68d8139a2
--- /dev/null
+++ b/drivers/staging/lustre/include/linux/lnet/types.h
@@ -0,0 +1,492 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2012, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ */
+
+#ifndef __LNET_TYPES_H__
+#define __LNET_TYPES_H__
+
+/** \addtogroup lnet
+ * @{ */
+
+#include "../libcfs/libcfs.h"
+
+/** \addtogroup lnet_addr
+ * @{ */
+
+/** Portal reserved for LNet's own use.
+ * \see lustre/include/lustre/lustre_idl.h for Lustre portal assignments.
+ */
+#define LNET_RESERVED_PORTAL 0
+
+/**
+ * Address of an end-point in an LNet network.
+ *
+ * A node can have multiple end-points and hence multiple addresses.
+ * An LNet network can be a simple network (e.g. tcp0) or a network of
+ * LNet networks connected by LNet routers. Therefore an end-point address
+ * has two parts: network ID, and address within a network.
+ *
+ * \see LNET_NIDNET, LNET_NIDADDR, and LNET_MKNID.
+ */
+typedef __u64 lnet_nid_t;
+/**
+ * ID of a process in a node. Shortened as PID to distinguish from
+ * lnet_process_id_t, the global process ID.
+ */
+typedef __u32 lnet_pid_t;
+
+/** wildcard NID that matches any end-point address */
+#define LNET_NID_ANY ((lnet_nid_t) -1)
+/** wildcard PID that matches any lnet_pid_t */
+#define LNET_PID_ANY ((lnet_pid_t) -1)
+
+#define LNET_PID_RESERVED 0xf0000000 /* reserved bits in PID */
+#define LNET_PID_USERFLAG 0x80000000 /* set in userspace peers */
+
+#define LNET_TIME_FOREVER (-1)
+
+/**
+ * Objects maintained by the LNet are accessed through handles. Handle types
+ * have names of the form lnet_handle_xx_t, where xx is one of the two letter
+ * object type codes ('eq' for event queue, 'md' for memory descriptor, and
+ * 'me' for match entry).
+ * Each type of object is given a unique handle type to enhance type checking.
+ * The type lnet_handle_any_t can be used when a generic handle is needed.
+ * Every handle value can be converted into a value of type lnet_handle_any_t
+ * without loss of information.
+ */
+typedef struct {
+ __u64 cookie;
+} lnet_handle_any_t;
+
+typedef lnet_handle_any_t lnet_handle_eq_t;
+typedef lnet_handle_any_t lnet_handle_md_t;
+typedef lnet_handle_any_t lnet_handle_me_t;
+
+#define LNET_WIRE_HANDLE_COOKIE_NONE (-1)
+
+/**
+ * Invalidate handle \a h.
+ */
+static inline void LNetInvalidateHandle(lnet_handle_any_t *h)
+{
+ h->cookie = LNET_WIRE_HANDLE_COOKIE_NONE;
+}
+
+/**
+ * Compare handles \a h1 and \a h2.
+ *
+ * \return 1 if handles are equal, 0 if otherwise.
+ */
+static inline int LNetHandleIsEqual(lnet_handle_any_t h1, lnet_handle_any_t h2)
+{
+ return h1.cookie == h2.cookie;
+}
+
+/**
+ * Check whether handle \a h is invalid.
+ *
+ * \return 1 if handle is invalid, 0 if valid.
+ */
+static inline int LNetHandleIsInvalid(lnet_handle_any_t h)
+{
+ return LNET_WIRE_HANDLE_COOKIE_NONE == h.cookie;
+}
+
+/**
+ * Global process ID.
+ */
+typedef struct {
+ /** node id */
+ lnet_nid_t nid;
+ /** process id */
+ lnet_pid_t pid;
+} lnet_process_id_t;
+/** @} lnet_addr */
+
+/** \addtogroup lnet_me
+ * @{ */
+
+/**
+ * Specifies whether the match entry or memory descriptor should be unlinked
+ * automatically (LNET_UNLINK) or not (LNET_RETAIN).
+ */
+typedef enum {
+ LNET_RETAIN = 0,
+ LNET_UNLINK
+} lnet_unlink_t;
+
+/**
+ * Values of the type lnet_ins_pos_t are used to control where a new match
+ * entry is inserted. The value LNET_INS_BEFORE is used to insert the new
+ * entry before the current entry or before the head of the list. The value
+ * LNET_INS_AFTER is used to insert the new entry after the current entry
+ * or after the last item in the list.
+ */
+typedef enum {
+ /** insert ME before current position or head of the list */
+ LNET_INS_BEFORE,
+ /** insert ME after current position or tail of the list */
+ LNET_INS_AFTER,
+ /** attach ME at tail of local CPU partition ME list */
+ LNET_INS_LOCAL
+} lnet_ins_pos_t;
+
+/** @} lnet_me */
+
+/** \addtogroup lnet_md
+ * @{ */
+
+/**
+ * Defines the visible parts of a memory descriptor. Values of this type
+ * are used to initialize memory descriptors.
+ */
+typedef struct {
+ /**
+ * Specify the memory region associated with the memory descriptor.
+ * If the options field has:
+ * - LNET_MD_KIOV bit set: The start field points to the starting
+ * address of an array of lnet_kiov_t and the length field specifies
+ * the number of entries in the array. The length can't be bigger
+ * than LNET_MAX_IOV. The lnet_kiov_t is used to describe page-based
+ * fragments that are not necessarily mapped in virtual memory.
+ * - LNET_MD_IOVEC bit set: The start field points to the starting
+ * address of an array of struct iovec and the length field specifies
+ * the number of entries in the array. The length can't be bigger
+ * than LNET_MAX_IOV. The struct iovec is used to describe fragments
+ * that have virtual addresses.
+ * - Otherwise: The memory region is contiguous. The start field
+ * specifies the starting address for the memory region and the
+ * length field specifies its length.
+ *
+ * When the memory region is fragmented, all fragments but the first
+ * one must start on page boundary, and all but the last must end on
+ * page boundary.
+ */
+ void *start;
+ unsigned int length;
+ /**
+ * Specifies the maximum number of operations that can be performed
+ * on the memory descriptor. An operation is any action that could
+ * possibly generate an event. In the usual case, the threshold value
+ * is decremented for each operation on the MD. When the threshold
+ * drops to zero, the MD becomes inactive and does not respond to
+ * operations. A threshold value of LNET_MD_THRESH_INF indicates that
+ * there is no bound on the number of operations that may be applied
+ * to a MD.
+ */
+ int threshold;
+ /**
+ * Specifies the largest incoming request that the memory descriptor
+ * should respond to. When the unused portion of a MD (length -
+ * local offset) falls below this value, the MD becomes inactive and
+ * does not respond to further operations. This value is only used
+ * if the LNET_MD_MAX_SIZE option is set.
+ */
+ int max_size;
+ /**
+ * Specifies the behavior of the memory descriptor. A bitwise OR
+ * of the following values can be used:
+ * - LNET_MD_OP_PUT: The LNet PUT operation is allowed on this MD.
+ * - LNET_MD_OP_GET: The LNet GET operation is allowed on this MD.
+ * - LNET_MD_MANAGE_REMOTE: The offset used in accessing the memory
+ * region is provided by the incoming request. By default, the
+ * offset is maintained locally. When maintained locally, the
+ * offset is incremented by the length of the request so that
+ * the next operation (PUT or GET) will access the next part of
+ * the memory region. Note that only one offset variable exists
+ * per memory descriptor. If both PUT and GET operations are
+ * performed on a memory descriptor, the offset is updated each time.
+ * - LNET_MD_TRUNCATE: The length provided in the incoming request can
+ * be reduced to match the memory available in the region (determined
+ * by subtracting the offset from the length of the memory region).
+ * By default, if the length in the incoming operation is greater
+ * than the amount of memory available, the operation is rejected.
+ * - LNET_MD_ACK_DISABLE: An acknowledgment should not be sent for
+ * incoming PUT operations, even if requested. By default,
+ * acknowledgments are sent for PUT operations that request an
+ * acknowledgment. Acknowledgments are never sent for GET operations.
+ * The data sent in the REPLY serves as an implicit acknowledgment.
+ * - LNET_MD_KIOV: The start and length fields specify an array of
+ * lnet_kiov_t.
+ * - LNET_MD_IOVEC: The start and length fields specify an array of
+ * struct iovec.
+ * - LNET_MD_MAX_SIZE: The max_size field is valid.
+ *
+ * Note:
+ * - LNET_MD_KIOV or LNET_MD_IOVEC allows for a scatter/gather
+ * capability for memory descriptors. They can't be both set.
+ * - When LNET_MD_MAX_SIZE is set, the total length of the memory
+ * region (i.e. sum of all fragment lengths) must not be less than
+ * \a max_size.
+ */
+ unsigned int options;
+ /**
+ * A user-specified value that is associated with the memory
+ * descriptor. The value does not need to be a pointer, but must fit
+ * in the space used by a pointer. This value is recorded in events
+ * associated with operations on this MD.
+ */
+ void *user_ptr;
+ /**
+ * A handle for the event queue used to log the operations performed on
+ * the memory region. If this argument is a NULL handle (i.e. nullified
+ * by LNetInvalidateHandle()), operations performed on this memory
+ * descriptor are not logged.
+ */
+ lnet_handle_eq_t eq_handle;
+} lnet_md_t;
+
+/* Max Transfer Unit (minimum supported everywhere).
+ * CAVEAT EMPTOR, with multinet (i.e. routers forwarding between networks)
+ * these limits are system wide and not interface-local. */
+#define LNET_MTU_BITS 20
+#define LNET_MTU (1 << LNET_MTU_BITS)
+
+/** limit on the number of fragments in discontiguous MDs */
+#define LNET_MAX_IOV 256
+
+/* Max payload size */
+# define LNET_MAX_PAYLOAD CONFIG_LNET_MAX_PAYLOAD
+# if (LNET_MAX_PAYLOAD < LNET_MTU)
+# error "LNET_MAX_PAYLOAD too small - error in configure --with-max-payload-mb"
+# else
+# if (LNET_MAX_PAYLOAD > (PAGE_SIZE * LNET_MAX_IOV))
+/* PAGE_SIZE is a constant: check with cpp! */
+# error "LNET_MAX_PAYLOAD too large - error in configure --with-max-payload-mb"
+# endif
+# endif
+
+/**
+ * Options for the MD structure. See lnet_md_t::options.
+ */
+#define LNET_MD_OP_PUT (1 << 0)
+/** See lnet_md_t::options. */
+#define LNET_MD_OP_GET (1 << 1)
+/** See lnet_md_t::options. */
+#define LNET_MD_MANAGE_REMOTE (1 << 2)
+/* unused (1 << 3) */
+/** See lnet_md_t::options. */
+#define LNET_MD_TRUNCATE (1 << 4)
+/** See lnet_md_t::options. */
+#define LNET_MD_ACK_DISABLE (1 << 5)
+/** See lnet_md_t::options. */
+#define LNET_MD_IOVEC (1 << 6)
+/** See lnet_md_t::options. */
+#define LNET_MD_MAX_SIZE (1 << 7)
+/** See lnet_md_t::options. */
+#define LNET_MD_KIOV (1 << 8)
+
+/* For compatibility with Cray Portals */
+#define LNET_MD_PHYS 0
+
+/** Infinite threshold on MD operations. See lnet_md_t::threshold */
+#define LNET_MD_THRESH_INF (-1)
+
+/* NB lustre portals uses struct iovec internally! */
+typedef struct iovec lnet_md_iovec_t;
+
+/**
+ * A page-based fragment of a MD.
+ */
+typedef struct {
+ /** Pointer to the page where the fragment resides */
+ struct page *kiov_page;
+ /** Length in bytes of the fragment */
+ unsigned int kiov_len;
+ /**
+ * Starting offset of the fragment within the page. Note that the
+ * end of the fragment must not pass the end of the page; i.e.,
+ * kiov_len + kiov_offset <= PAGE_CACHE_SIZE.
+ */
+ unsigned int kiov_offset;
+} lnet_kiov_t;
+/** @} lnet_md */
+
+/** \addtogroup lnet_eq
+ * @{ */
+
+/**
+ * Six types of events can be logged in an event queue.
+ */
+typedef enum {
+ /** An incoming GET operation has completed on the MD. */
+ LNET_EVENT_GET = 1,
+ /**
+ * An incoming PUT operation has completed on the MD. The
+ * underlying layers will not alter the memory (on behalf of this
+ * operation) once this event has been logged.
+ */
+ LNET_EVENT_PUT,
+ /**
+ * A REPLY operation has completed. This event is logged after the
+ * data (if any) from the REPLY has been written into the MD.
+ */
+ LNET_EVENT_REPLY,
+ /** An acknowledgment has been received. */
+ LNET_EVENT_ACK,
+ /**
+ * An outgoing send (PUT or GET) operation has completed. This event
+ * is logged after the entire buffer has been sent and it is safe for
+ * the caller to reuse the buffer.
+ *
+ * Note:
+ * - The LNET_EVENT_SEND doesn't guarantee message delivery. It can
+ * happen even when the message has not yet been put out on wire.
+ * - It's unsafe to assume that in an outgoing GET operation
+ * the LNET_EVENT_SEND event would happen before the
+ * LNET_EVENT_REPLY event. The same holds for LNET_EVENT_SEND and
+ * LNET_EVENT_ACK events in an outgoing PUT operation.
+ */
+ LNET_EVENT_SEND,
+ /**
+ * A MD has been unlinked. Note that LNetMDUnlink() does not
+ * necessarily trigger an LNET_EVENT_UNLINK event.
+ * \see LNetMDUnlink
+ */
+ LNET_EVENT_UNLINK,
+} lnet_event_kind_t;
+
+#define LNET_SEQ_BASETYPE long
+typedef unsigned LNET_SEQ_BASETYPE lnet_seq_t;
+#define LNET_SEQ_GT(a, b) (((signed LNET_SEQ_BASETYPE)((a) - (b))) > 0)
+
+/**
+ * Information about an event on a MD.
+ */
+typedef struct {
+ /** The identifier (nid, pid) of the target. */
+ lnet_process_id_t target;
+ /** The identifier (nid, pid) of the initiator. */
+ lnet_process_id_t initiator;
+ /**
+ * The NID of the immediate sender. If the request has been forwarded
+ * by routers, this is the NID of the last hop; otherwise it's the
+ * same as the initiator.
+ */
+ lnet_nid_t sender;
+ /** Indicates the type of the event. */
+ lnet_event_kind_t type;
+ /** The portal table index specified in the request */
+ unsigned int pt_index;
+ /** A copy of the match bits specified in the request. */
+ __u64 match_bits;
+ /** The length (in bytes) specified in the request. */
+ unsigned int rlength;
+ /**
+ * The length (in bytes) of the data that was manipulated by the
+ * operation. For truncated operations, the manipulated length will be
+ * the number of bytes specified by the MD (possibly with an offset,
+ * see lnet_md_t). For all other operations, the manipulated length
+ * will be the length of the requested operation, i.e. rlength.
+ */
+ unsigned int mlength;
+ /**
+ * The handle to the MD associated with the event. The handle may be
+ * invalid if the MD has been unlinked.
+ */
+ lnet_handle_md_t md_handle;
+ /**
+ * A snapshot of the state of the MD immediately after the event has
+ * been processed. In particular, the threshold field in md will
+ * reflect the value of the threshold after the operation occurred.
+ */
+ lnet_md_t md;
+ /**
+ * 64 bits of out-of-band user data. Only valid for LNET_EVENT_PUT.
+ * \see LNetPut
+ */
+ __u64 hdr_data;
+ /**
+ * Indicates the completion status of the operation. It's 0 for
+ * successful operations, otherwise it's an error code.
+ */
+ int status;
+ /**
+ * Indicates whether the MD has been unlinked. Note that:
+ * - An event with unlinked set is the last event on the MD.
+ * - This field is also set for an explicit LNET_EVENT_UNLINK event.
+ * \see LNetMDUnlink
+ */
+ int unlinked;
+ /**
+ * The displacement (in bytes) into the memory region that the
+ * operation used. The offset can be determined by the operation for
+ * a remote managed MD or by the local MD.
+ * \see lnet_md_t::options
+ */
+ unsigned int offset;
+ /**
+ * The sequence number for this event. Sequence numbers are unique
+ * to each event.
+ */
+ volatile lnet_seq_t sequence;
+} lnet_event_t;
+
+/**
+ * Event queue handler function type.
+ *
+ * The EQ handler runs for each event that is deposited into the EQ. The
+ * handler is supplied with a pointer to the event that triggered the
+ * handler invocation.
+ *
+ * The handler must not block, must be reentrant, and must not call any LNet
+ * API functions. It should return as quickly as possible.
+ */
+typedef void (*lnet_eq_handler_t)(lnet_event_t *event);
+#define LNET_EQ_HANDLER_NONE NULL
+/** @} lnet_eq */
+
+/** \addtogroup lnet_data
+ * @{ */
+
+/**
+ * Specify whether an acknowledgment should be sent by target when the PUT
+ * operation completes (i.e., when the data has been written to a MD of the
+ * target process).
+ *
+ * \see lnet_md_t::options for the discussion on LNET_MD_ACK_DISABLE by which
+ * acknowledgments can be disabled for a MD.
+ */
+typedef enum {
+ /** Request an acknowledgment */
+ LNET_ACK_REQ,
+ /** Request that no acknowledgment should be generated. */
+ LNET_NOACK_REQ
+} lnet_ack_req_t;
+/** @} lnet_data */
+
+/** @} lnet */
+#endif
diff --git a/drivers/staging/lustre/lnet/Kconfig b/drivers/staging/lustre/lnet/Kconfig
new file mode 100644
index 000000000..00850eeb6
--- /dev/null
+++ b/drivers/staging/lustre/lnet/Kconfig
@@ -0,0 +1,40 @@
+config LNET
+ tristate "Lustre networking subsystem"
+ depends on LUSTRE_FS
+
+config LNET_MAX_PAYLOAD
+ int "Lustre lnet max transfer payload (default 2MB)"
+ depends on LUSTRE_FS
+ default "1048576"
+ help
+ This option defines the maximum size of payload in bytes that lnet
+ can put into its transport.
+
+ If unsure, use default.
+
+config LNET_SELFTEST
+ tristate "Lustre networking self testing"
+ depends on LNET
+ help
+ Choose Y here if you want to do lnet self testing. To compile this
+ as a module, choose M here: the module will be called lnet_selftest.
+
+ To compile this as a kernel modules, choose M here and it will be
+ called lnet_selftest.
+
+ If unsure, say N.
+
+ See also http://wiki.lustre.org/
+
+config LNET_XPRT_IB
+ tristate "LNET infiniband support"
+ depends on LNET && INFINIBAND && INFINIBAND_ADDR_TRANS
+ default LNET && INFINIBAND
+ help
+ This option allows the LNET users to use infiniband as an
+ RDMA-enabled transport.
+
+ To compile this as a kernel module, choose M here and it will be
+ called ko2iblnd.
+
+ If unsure, say N.
diff --git a/drivers/staging/lustre/lnet/Makefile b/drivers/staging/lustre/lnet/Makefile
new file mode 100644
index 000000000..f6f03e304
--- /dev/null
+++ b/drivers/staging/lustre/lnet/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_LNET) += lnet/ klnds/ selftest/
diff --git a/drivers/staging/lustre/lnet/klnds/Makefile b/drivers/staging/lustre/lnet/klnds/Makefile
new file mode 100644
index 000000000..c23e4f67f
--- /dev/null
+++ b/drivers/staging/lustre/lnet/klnds/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_LNET) += o2iblnd/ socklnd/
diff --git a/drivers/staging/lustre/lnet/klnds/o2iblnd/Makefile b/drivers/staging/lustre/lnet/klnds/o2iblnd/Makefile
new file mode 100644
index 000000000..e0a7aa72b
--- /dev/null
+++ b/drivers/staging/lustre/lnet/klnds/o2iblnd/Makefile
@@ -0,0 +1,2 @@
+obj-$(CONFIG_LNET_XPRT_IB) += ko2iblnd.o
+ko2iblnd-y := o2iblnd.o o2iblnd_cb.o o2iblnd_modparams.o
diff --git a/drivers/staging/lustre/lnet/klnds/o2iblnd/o2iblnd.c b/drivers/staging/lustre/lnet/klnds/o2iblnd/o2iblnd.c
new file mode 100644
index 000000000..3bad441de
--- /dev/null
+++ b/drivers/staging/lustre/lnet/klnds/o2iblnd/o2iblnd.c
@@ -0,0 +1,3118 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2011, 2012, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ *
+ * lnet/klnds/o2iblnd/o2iblnd.c
+ *
+ * Author: Eric Barton <eric@bartonsoftware.com>
+ */
+
+#include "o2iblnd.h"
+#include <asm/div64.h>
+
+static lnd_t the_o2iblnd = {
+ .lnd_type = O2IBLND,
+ .lnd_startup = kiblnd_startup,
+ .lnd_shutdown = kiblnd_shutdown,
+ .lnd_ctl = kiblnd_ctl,
+ .lnd_query = kiblnd_query,
+ .lnd_send = kiblnd_send,
+ .lnd_recv = kiblnd_recv,
+};
+
+kib_data_t kiblnd_data;
+
+static __u32 kiblnd_cksum(void *ptr, int nob)
+{
+ char *c = ptr;
+ __u32 sum = 0;
+
+ while (nob-- > 0)
+ sum = ((sum << 1) | (sum >> 31)) + *c++;
+
+ /* ensure I don't return 0 (== no checksum) */
+ return (sum == 0) ? 1 : sum;
+}
+
+static char *kiblnd_msgtype2str(int type)
+{
+ switch (type) {
+ case IBLND_MSG_CONNREQ:
+ return "CONNREQ";
+
+ case IBLND_MSG_CONNACK:
+ return "CONNACK";
+
+ case IBLND_MSG_NOOP:
+ return "NOOP";
+
+ case IBLND_MSG_IMMEDIATE:
+ return "IMMEDIATE";
+
+ case IBLND_MSG_PUT_REQ:
+ return "PUT_REQ";
+
+ case IBLND_MSG_PUT_NAK:
+ return "PUT_NAK";
+
+ case IBLND_MSG_PUT_ACK:
+ return "PUT_ACK";
+
+ case IBLND_MSG_PUT_DONE:
+ return "PUT_DONE";
+
+ case IBLND_MSG_GET_REQ:
+ return "GET_REQ";
+
+ case IBLND_MSG_GET_DONE:
+ return "GET_DONE";
+
+ default:
+ return "???";
+ }
+}
+
+static int kiblnd_msgtype2size(int type)
+{
+ const int hdr_size = offsetof(kib_msg_t, ibm_u);
+
+ switch (type) {
+ case IBLND_MSG_CONNREQ:
+ case IBLND_MSG_CONNACK:
+ return hdr_size + sizeof(kib_connparams_t);
+
+ case IBLND_MSG_NOOP:
+ return hdr_size;
+
+ case IBLND_MSG_IMMEDIATE:
+ return offsetof(kib_msg_t, ibm_u.immediate.ibim_payload[0]);
+
+ case IBLND_MSG_PUT_REQ:
+ return hdr_size + sizeof(kib_putreq_msg_t);
+
+ case IBLND_MSG_PUT_ACK:
+ return hdr_size + sizeof(kib_putack_msg_t);
+
+ case IBLND_MSG_GET_REQ:
+ return hdr_size + sizeof(kib_get_msg_t);
+
+ case IBLND_MSG_PUT_NAK:
+ case IBLND_MSG_PUT_DONE:
+ case IBLND_MSG_GET_DONE:
+ return hdr_size + sizeof(kib_completion_msg_t);
+ default:
+ return -1;
+ }
+}
+
+static int kiblnd_unpack_rd(kib_msg_t *msg, int flip)
+{
+ kib_rdma_desc_t *rd;
+ int nob;
+ int n;
+ int i;
+
+ LASSERT(msg->ibm_type == IBLND_MSG_GET_REQ ||
+ msg->ibm_type == IBLND_MSG_PUT_ACK);
+
+ rd = msg->ibm_type == IBLND_MSG_GET_REQ ?
+ &msg->ibm_u.get.ibgm_rd :
+ &msg->ibm_u.putack.ibpam_rd;
+
+ if (flip) {
+ __swab32s(&rd->rd_key);
+ __swab32s(&rd->rd_nfrags);
+ }
+
+ n = rd->rd_nfrags;
+
+ if (n <= 0 || n > IBLND_MAX_RDMA_FRAGS) {
+ CERROR("Bad nfrags: %d, should be 0 < n <= %d\n",
+ n, IBLND_MAX_RDMA_FRAGS);
+ return 1;
+ }
+
+ nob = offsetof(kib_msg_t, ibm_u) +
+ kiblnd_rd_msg_size(rd, msg->ibm_type, n);
+
+ if (msg->ibm_nob < nob) {
+ CERROR("Short %s: %d(%d)\n",
+ kiblnd_msgtype2str(msg->ibm_type), msg->ibm_nob, nob);
+ return 1;
+ }
+
+ if (!flip)
+ return 0;
+
+ for (i = 0; i < n; i++) {
+ __swab32s(&rd->rd_frags[i].rf_nob);
+ __swab64s(&rd->rd_frags[i].rf_addr);
+ }
+
+ return 0;
+}
+
+void kiblnd_pack_msg(lnet_ni_t *ni, kib_msg_t *msg, int version,
+ int credits, lnet_nid_t dstnid, __u64 dststamp)
+{
+ kib_net_t *net = ni->ni_data;
+
+ /* CAVEAT EMPTOR! all message fields not set here should have been
+ * initialised previously. */
+ msg->ibm_magic = IBLND_MSG_MAGIC;
+ msg->ibm_version = version;
+ /* ibm_type */
+ msg->ibm_credits = credits;
+ /* ibm_nob */
+ msg->ibm_cksum = 0;
+ msg->ibm_srcnid = ni->ni_nid;
+ msg->ibm_srcstamp = net->ibn_incarnation;
+ msg->ibm_dstnid = dstnid;
+ msg->ibm_dststamp = dststamp;
+
+ if (*kiblnd_tunables.kib_cksum) {
+ /* NB ibm_cksum zero while computing cksum */
+ msg->ibm_cksum = kiblnd_cksum(msg, msg->ibm_nob);
+ }
+}
+
+int kiblnd_unpack_msg(kib_msg_t *msg, int nob)
+{
+ const int hdr_size = offsetof(kib_msg_t, ibm_u);
+ __u32 msg_cksum;
+ __u16 version;
+ int msg_nob;
+ int flip;
+
+ /* 6 bytes are enough to have received magic + version */
+ if (nob < 6) {
+ CERROR("Short message: %d\n", nob);
+ return -EPROTO;
+ }
+
+ if (msg->ibm_magic == IBLND_MSG_MAGIC) {
+ flip = 0;
+ } else if (msg->ibm_magic == __swab32(IBLND_MSG_MAGIC)) {
+ flip = 1;
+ } else {
+ CERROR("Bad magic: %08x\n", msg->ibm_magic);
+ return -EPROTO;
+ }
+
+ version = flip ? __swab16(msg->ibm_version) : msg->ibm_version;
+ if (version != IBLND_MSG_VERSION &&
+ version != IBLND_MSG_VERSION_1) {
+ CERROR("Bad version: %x\n", version);
+ return -EPROTO;
+ }
+
+ if (nob < hdr_size) {
+ CERROR("Short message: %d\n", nob);
+ return -EPROTO;
+ }
+
+ msg_nob = flip ? __swab32(msg->ibm_nob) : msg->ibm_nob;
+ if (msg_nob > nob) {
+ CERROR("Short message: got %d, wanted %d\n", nob, msg_nob);
+ return -EPROTO;
+ }
+
+ /* checksum must be computed with ibm_cksum zero and BEFORE anything
+ * gets flipped */
+ msg_cksum = flip ? __swab32(msg->ibm_cksum) : msg->ibm_cksum;
+ msg->ibm_cksum = 0;
+ if (msg_cksum != 0 &&
+ msg_cksum != kiblnd_cksum(msg, msg_nob)) {
+ CERROR("Bad checksum\n");
+ return -EPROTO;
+ }
+
+ msg->ibm_cksum = msg_cksum;
+
+ if (flip) {
+ /* leave magic unflipped as a clue to peer endianness */
+ msg->ibm_version = version;
+ CLASSERT(sizeof(msg->ibm_type) == 1);
+ CLASSERT(sizeof(msg->ibm_credits) == 1);
+ msg->ibm_nob = msg_nob;
+ __swab64s(&msg->ibm_srcnid);
+ __swab64s(&msg->ibm_srcstamp);
+ __swab64s(&msg->ibm_dstnid);
+ __swab64s(&msg->ibm_dststamp);
+ }
+
+ if (msg->ibm_srcnid == LNET_NID_ANY) {
+ CERROR("Bad src nid: %s\n", libcfs_nid2str(msg->ibm_srcnid));
+ return -EPROTO;
+ }
+
+ if (msg_nob < kiblnd_msgtype2size(msg->ibm_type)) {
+ CERROR("Short %s: %d(%d)\n", kiblnd_msgtype2str(msg->ibm_type),
+ msg_nob, kiblnd_msgtype2size(msg->ibm_type));
+ return -EPROTO;
+ }
+
+ switch (msg->ibm_type) {
+ default:
+ CERROR("Unknown message type %x\n", msg->ibm_type);
+ return -EPROTO;
+
+ case IBLND_MSG_NOOP:
+ case IBLND_MSG_IMMEDIATE:
+ case IBLND_MSG_PUT_REQ:
+ break;
+
+ case IBLND_MSG_PUT_ACK:
+ case IBLND_MSG_GET_REQ:
+ if (kiblnd_unpack_rd(msg, flip))
+ return -EPROTO;
+ break;
+
+ case IBLND_MSG_PUT_NAK:
+ case IBLND_MSG_PUT_DONE:
+ case IBLND_MSG_GET_DONE:
+ if (flip)
+ __swab32s(&msg->ibm_u.completion.ibcm_status);
+ break;
+
+ case IBLND_MSG_CONNREQ:
+ case IBLND_MSG_CONNACK:
+ if (flip) {
+ __swab16s(&msg->ibm_u.connparams.ibcp_queue_depth);
+ __swab16s(&msg->ibm_u.connparams.ibcp_max_frags);
+ __swab32s(&msg->ibm_u.connparams.ibcp_max_msg_size);
+ }
+ break;
+ }
+ return 0;
+}
+
+int kiblnd_create_peer(lnet_ni_t *ni, kib_peer_t **peerp, lnet_nid_t nid)
+{
+ kib_peer_t *peer;
+ kib_net_t *net = ni->ni_data;
+ int cpt = lnet_cpt_of_nid(nid);
+ unsigned long flags;
+
+ LASSERT(net != NULL);
+ LASSERT(nid != LNET_NID_ANY);
+
+ LIBCFS_CPT_ALLOC(peer, lnet_cpt_table(), cpt, sizeof(*peer));
+ if (peer == NULL) {
+ CERROR("Cannot allocate peer\n");
+ return -ENOMEM;
+ }
+
+ memset(peer, 0, sizeof(*peer)); /* zero flags etc */
+
+ peer->ibp_ni = ni;
+ peer->ibp_nid = nid;
+ peer->ibp_error = 0;
+ peer->ibp_last_alive = 0;
+ atomic_set(&peer->ibp_refcount, 1); /* 1 ref for caller */
+
+ INIT_LIST_HEAD(&peer->ibp_list); /* not in the peer table yet */
+ INIT_LIST_HEAD(&peer->ibp_conns);
+ INIT_LIST_HEAD(&peer->ibp_tx_queue);
+
+ write_lock_irqsave(&kiblnd_data.kib_global_lock, flags);
+
+ /* always called with a ref on ni, which prevents ni being shutdown */
+ LASSERT(net->ibn_shutdown == 0);
+
+ /* npeers only grows with the global lock held */
+ atomic_inc(&net->ibn_npeers);
+
+ write_unlock_irqrestore(&kiblnd_data.kib_global_lock, flags);
+
+ *peerp = peer;
+ return 0;
+}
+
+void kiblnd_destroy_peer(kib_peer_t *peer)
+{
+ kib_net_t *net = peer->ibp_ni->ni_data;
+
+ LASSERT(net != NULL);
+ LASSERT(atomic_read(&peer->ibp_refcount) == 0);
+ LASSERT(!kiblnd_peer_active(peer));
+ LASSERT(peer->ibp_connecting == 0);
+ LASSERT(peer->ibp_accepting == 0);
+ LASSERT(list_empty(&peer->ibp_conns));
+ LASSERT(list_empty(&peer->ibp_tx_queue));
+
+ LIBCFS_FREE(peer, sizeof(*peer));
+
+ /* NB a peer's connections keep a reference on their peer until
+ * they are destroyed, so we can be assured that _all_ state to do
+ * with this peer has been cleaned up when its refcount drops to
+ * zero. */
+ atomic_dec(&net->ibn_npeers);
+}
+
+kib_peer_t *kiblnd_find_peer_locked(lnet_nid_t nid)
+{
+ /* the caller is responsible for accounting the additional reference
+ * that this creates */
+ struct list_head *peer_list = kiblnd_nid2peerlist(nid);
+ struct list_head *tmp;
+ kib_peer_t *peer;
+
+ list_for_each(tmp, peer_list) {
+
+ peer = list_entry(tmp, kib_peer_t, ibp_list);
+
+ LASSERT(peer->ibp_connecting > 0 || /* creating conns */
+ peer->ibp_accepting > 0 ||
+ !list_empty(&peer->ibp_conns)); /* active conn */
+
+ if (peer->ibp_nid != nid)
+ continue;
+
+ CDEBUG(D_NET, "got peer [%p] -> %s (%d) version: %x\n",
+ peer, libcfs_nid2str(nid),
+ atomic_read(&peer->ibp_refcount),
+ peer->ibp_version);
+ return peer;
+ }
+ return NULL;
+}
+
+void kiblnd_unlink_peer_locked(kib_peer_t *peer)
+{
+ LASSERT(list_empty(&peer->ibp_conns));
+
+ LASSERT(kiblnd_peer_active(peer));
+ list_del_init(&peer->ibp_list);
+ /* lose peerlist's ref */
+ kiblnd_peer_decref(peer);
+}
+
+static int kiblnd_get_peer_info(lnet_ni_t *ni, int index,
+ lnet_nid_t *nidp, int *count)
+{
+ kib_peer_t *peer;
+ struct list_head *ptmp;
+ int i;
+ unsigned long flags;
+
+ read_lock_irqsave(&kiblnd_data.kib_global_lock, flags);
+
+ for (i = 0; i < kiblnd_data.kib_peer_hash_size; i++) {
+
+ list_for_each(ptmp, &kiblnd_data.kib_peers[i]) {
+
+ peer = list_entry(ptmp, kib_peer_t, ibp_list);
+ LASSERT(peer->ibp_connecting > 0 ||
+ peer->ibp_accepting > 0 ||
+ !list_empty(&peer->ibp_conns));
+
+ if (peer->ibp_ni != ni)
+ continue;
+
+ if (index-- > 0)
+ continue;
+
+ *nidp = peer->ibp_nid;
+ *count = atomic_read(&peer->ibp_refcount);
+
+ read_unlock_irqrestore(&kiblnd_data.kib_global_lock,
+ flags);
+ return 0;
+ }
+ }
+
+ read_unlock_irqrestore(&kiblnd_data.kib_global_lock, flags);
+ return -ENOENT;
+}
+
+static void kiblnd_del_peer_locked(kib_peer_t *peer)
+{
+ struct list_head *ctmp;
+ struct list_head *cnxt;
+ kib_conn_t *conn;
+
+ if (list_empty(&peer->ibp_conns)) {
+ kiblnd_unlink_peer_locked(peer);
+ } else {
+ list_for_each_safe(ctmp, cnxt, &peer->ibp_conns) {
+ conn = list_entry(ctmp, kib_conn_t, ibc_list);
+
+ kiblnd_close_conn_locked(conn, 0);
+ }
+ /* NB closing peer's last conn unlinked it. */
+ }
+ /* NB peer now unlinked; might even be freed if the peer table had the
+ * last ref on it. */
+}
+
+static int kiblnd_del_peer(lnet_ni_t *ni, lnet_nid_t nid)
+{
+ LIST_HEAD(zombies);
+ struct list_head *ptmp;
+ struct list_head *pnxt;
+ kib_peer_t *peer;
+ int lo;
+ int hi;
+ int i;
+ unsigned long flags;
+ int rc = -ENOENT;
+
+ write_lock_irqsave(&kiblnd_data.kib_global_lock, flags);
+
+ if (nid != LNET_NID_ANY) {
+ lo = hi = kiblnd_nid2peerlist(nid) - kiblnd_data.kib_peers;
+ } else {
+ lo = 0;
+ hi = kiblnd_data.kib_peer_hash_size - 1;
+ }
+
+ for (i = lo; i <= hi; i++) {
+ list_for_each_safe(ptmp, pnxt, &kiblnd_data.kib_peers[i]) {
+ peer = list_entry(ptmp, kib_peer_t, ibp_list);
+ LASSERT(peer->ibp_connecting > 0 ||
+ peer->ibp_accepting > 0 ||
+ !list_empty(&peer->ibp_conns));
+
+ if (peer->ibp_ni != ni)
+ continue;
+
+ if (!(nid == LNET_NID_ANY || peer->ibp_nid == nid))
+ continue;
+
+ if (!list_empty(&peer->ibp_tx_queue)) {
+ LASSERT(list_empty(&peer->ibp_conns));
+
+ list_splice_init(&peer->ibp_tx_queue,
+ &zombies);
+ }
+
+ kiblnd_del_peer_locked(peer);
+ rc = 0; /* matched something */
+ }
+ }
+
+ write_unlock_irqrestore(&kiblnd_data.kib_global_lock, flags);
+
+ kiblnd_txlist_done(ni, &zombies, -EIO);
+
+ return rc;
+}
+
+static kib_conn_t *kiblnd_get_conn_by_idx(lnet_ni_t *ni, int index)
+{
+ kib_peer_t *peer;
+ struct list_head *ptmp;
+ kib_conn_t *conn;
+ struct list_head *ctmp;
+ int i;
+ unsigned long flags;
+
+ read_lock_irqsave(&kiblnd_data.kib_global_lock, flags);
+
+ for (i = 0; i < kiblnd_data.kib_peer_hash_size; i++) {
+ list_for_each(ptmp, &kiblnd_data.kib_peers[i]) {
+
+ peer = list_entry(ptmp, kib_peer_t, ibp_list);
+ LASSERT(peer->ibp_connecting > 0 ||
+ peer->ibp_accepting > 0 ||
+ !list_empty(&peer->ibp_conns));
+
+ if (peer->ibp_ni != ni)
+ continue;
+
+ list_for_each(ctmp, &peer->ibp_conns) {
+ if (index-- > 0)
+ continue;
+
+ conn = list_entry(ctmp, kib_conn_t,
+ ibc_list);
+ kiblnd_conn_addref(conn);
+ read_unlock_irqrestore(
+ &kiblnd_data.kib_global_lock,
+ flags);
+ return conn;
+ }
+ }
+ }
+
+ read_unlock_irqrestore(&kiblnd_data.kib_global_lock, flags);
+ return NULL;
+}
+
+int kiblnd_translate_mtu(int value)
+{
+ switch (value) {
+ default:
+ return -1;
+ case 0:
+ return 0;
+ case 256:
+ return IB_MTU_256;
+ case 512:
+ return IB_MTU_512;
+ case 1024:
+ return IB_MTU_1024;
+ case 2048:
+ return IB_MTU_2048;
+ case 4096:
+ return IB_MTU_4096;
+ }
+}
+
+static void kiblnd_setup_mtu_locked(struct rdma_cm_id *cmid)
+{
+ int mtu;
+
+ /* XXX There is no path record for iWARP, set by netdev->change_mtu? */
+ if (cmid->route.path_rec == NULL)
+ return;
+
+ mtu = kiblnd_translate_mtu(*kiblnd_tunables.kib_ib_mtu);
+ LASSERT(mtu >= 0);
+ if (mtu != 0)
+ cmid->route.path_rec->mtu = mtu;
+}
+
+static int kiblnd_get_completion_vector(kib_conn_t *conn, int cpt)
+{
+ cpumask_t *mask;
+ int vectors;
+ int off;
+ int i;
+ lnet_nid_t nid = conn->ibc_peer->ibp_nid;
+
+ vectors = conn->ibc_cmid->device->num_comp_vectors;
+ if (vectors <= 1)
+ return 0;
+
+ mask = cfs_cpt_cpumask(lnet_cpt_table(), cpt);
+ if (mask == NULL)
+ return 0;
+
+ /* hash NID to CPU id in this partition... */
+ off = do_div(nid, cpumask_weight(mask));
+ for_each_cpu(i, mask) {
+ if (off-- == 0)
+ return i % vectors;
+ }
+
+ LBUG();
+ return 1;
+}
+
+kib_conn_t *kiblnd_create_conn(kib_peer_t *peer, struct rdma_cm_id *cmid,
+ int state, int version)
+{
+ /* CAVEAT EMPTOR:
+ * If the new conn is created successfully it takes over the caller's
+ * ref on 'peer'. It also "owns" 'cmid' and destroys it when it itself
+ * is destroyed. On failure, the caller's ref on 'peer' remains and
+ * she must dispose of 'cmid'. (Actually I'd block forever if I tried
+ * to destroy 'cmid' here since I'm called from the CM which still has
+ * its ref on 'cmid'). */
+ rwlock_t *glock = &kiblnd_data.kib_global_lock;
+ kib_net_t *net = peer->ibp_ni->ni_data;
+ kib_dev_t *dev;
+ struct ib_qp_init_attr *init_qp_attr;
+ struct kib_sched_info *sched;
+ kib_conn_t *conn;
+ struct ib_cq *cq;
+ unsigned long flags;
+ int cpt;
+ int rc;
+ int i;
+
+ LASSERT(net != NULL);
+ LASSERT(!in_interrupt());
+
+ dev = net->ibn_dev;
+
+ cpt = lnet_cpt_of_nid(peer->ibp_nid);
+ sched = kiblnd_data.kib_scheds[cpt];
+
+ LASSERT(sched->ibs_nthreads > 0);
+
+ LIBCFS_CPT_ALLOC(init_qp_attr, lnet_cpt_table(), cpt,
+ sizeof(*init_qp_attr));
+ if (init_qp_attr == NULL) {
+ CERROR("Can't allocate qp_attr for %s\n",
+ libcfs_nid2str(peer->ibp_nid));
+ goto failed_0;
+ }
+
+ LIBCFS_CPT_ALLOC(conn, lnet_cpt_table(), cpt, sizeof(*conn));
+ if (conn == NULL) {
+ CERROR("Can't allocate connection for %s\n",
+ libcfs_nid2str(peer->ibp_nid));
+ goto failed_1;
+ }
+
+ conn->ibc_state = IBLND_CONN_INIT;
+ conn->ibc_version = version;
+ conn->ibc_peer = peer; /* I take the caller's ref */
+ cmid->context = conn; /* for future CM callbacks */
+ conn->ibc_cmid = cmid;
+
+ INIT_LIST_HEAD(&conn->ibc_early_rxs);
+ INIT_LIST_HEAD(&conn->ibc_tx_noops);
+ INIT_LIST_HEAD(&conn->ibc_tx_queue);
+ INIT_LIST_HEAD(&conn->ibc_tx_queue_rsrvd);
+ INIT_LIST_HEAD(&conn->ibc_tx_queue_nocred);
+ INIT_LIST_HEAD(&conn->ibc_active_txs);
+ spin_lock_init(&conn->ibc_lock);
+
+ LIBCFS_CPT_ALLOC(conn->ibc_connvars, lnet_cpt_table(), cpt,
+ sizeof(*conn->ibc_connvars));
+ if (conn->ibc_connvars == NULL) {
+ CERROR("Can't allocate in-progress connection state\n");
+ goto failed_2;
+ }
+
+ write_lock_irqsave(glock, flags);
+ if (dev->ibd_failover) {
+ write_unlock_irqrestore(glock, flags);
+ CERROR("%s: failover in progress\n", dev->ibd_ifname);
+ goto failed_2;
+ }
+
+ if (dev->ibd_hdev->ibh_ibdev != cmid->device) {
+ /* wakeup failover thread and teardown connection */
+ if (kiblnd_dev_can_failover(dev)) {
+ list_add_tail(&dev->ibd_fail_list,
+ &kiblnd_data.kib_failed_devs);
+ wake_up(&kiblnd_data.kib_failover_waitq);
+ }
+
+ write_unlock_irqrestore(glock, flags);
+ CERROR("cmid HCA(%s), kib_dev(%s) need failover\n",
+ cmid->device->name, dev->ibd_ifname);
+ goto failed_2;
+ }
+
+ kiblnd_hdev_addref_locked(dev->ibd_hdev);
+ conn->ibc_hdev = dev->ibd_hdev;
+
+ kiblnd_setup_mtu_locked(cmid);
+
+ write_unlock_irqrestore(glock, flags);
+
+ LIBCFS_CPT_ALLOC(conn->ibc_rxs, lnet_cpt_table(), cpt,
+ IBLND_RX_MSGS(version) * sizeof(kib_rx_t));
+ if (conn->ibc_rxs == NULL) {
+ CERROR("Cannot allocate RX buffers\n");
+ goto failed_2;
+ }
+
+ rc = kiblnd_alloc_pages(&conn->ibc_rx_pages, cpt,
+ IBLND_RX_MSG_PAGES(version));
+ if (rc != 0)
+ goto failed_2;
+
+ kiblnd_map_rx_descs(conn);
+
+ cq = ib_create_cq(cmid->device,
+ kiblnd_cq_completion, kiblnd_cq_event, conn,
+ IBLND_CQ_ENTRIES(version),
+ kiblnd_get_completion_vector(conn, cpt));
+ if (IS_ERR(cq)) {
+ CERROR("Can't create CQ: %ld, cqe: %d\n",
+ PTR_ERR(cq), IBLND_CQ_ENTRIES(version));
+ goto failed_2;
+ }
+
+ conn->ibc_cq = cq;
+
+ rc = ib_req_notify_cq(cq, IB_CQ_NEXT_COMP);
+ if (rc != 0) {
+ CERROR("Can't request completion notificiation: %d\n", rc);
+ goto failed_2;
+ }
+
+ init_qp_attr->event_handler = kiblnd_qp_event;
+ init_qp_attr->qp_context = conn;
+ init_qp_attr->cap.max_send_wr = IBLND_SEND_WRS(version);
+ init_qp_attr->cap.max_recv_wr = IBLND_RECV_WRS(version);
+ init_qp_attr->cap.max_send_sge = 1;
+ init_qp_attr->cap.max_recv_sge = 1;
+ init_qp_attr->sq_sig_type = IB_SIGNAL_REQ_WR;
+ init_qp_attr->qp_type = IB_QPT_RC;
+ init_qp_attr->send_cq = cq;
+ init_qp_attr->recv_cq = cq;
+
+ conn->ibc_sched = sched;
+
+ rc = rdma_create_qp(cmid, conn->ibc_hdev->ibh_pd, init_qp_attr);
+ if (rc != 0) {
+ CERROR("Can't create QP: %d, send_wr: %d, recv_wr: %d\n",
+ rc, init_qp_attr->cap.max_send_wr,
+ init_qp_attr->cap.max_recv_wr);
+ goto failed_2;
+ }
+
+ LIBCFS_FREE(init_qp_attr, sizeof(*init_qp_attr));
+
+ /* 1 ref for caller and each rxmsg */
+ atomic_set(&conn->ibc_refcount, 1 + IBLND_RX_MSGS(version));
+ conn->ibc_nrx = IBLND_RX_MSGS(version);
+
+ /* post receives */
+ for (i = 0; i < IBLND_RX_MSGS(version); i++) {
+ rc = kiblnd_post_rx(&conn->ibc_rxs[i],
+ IBLND_POSTRX_NO_CREDIT);
+ if (rc != 0) {
+ CERROR("Can't post rxmsg: %d\n", rc);
+
+ /* Make posted receives complete */
+ kiblnd_abort_receives(conn);
+
+ /* correct # of posted buffers
+ * NB locking needed now I'm racing with completion */
+ spin_lock_irqsave(&sched->ibs_lock, flags);
+ conn->ibc_nrx -= IBLND_RX_MSGS(version) - i;
+ spin_unlock_irqrestore(&sched->ibs_lock, flags);
+
+ /* cmid will be destroyed by CM(ofed) after cm_callback
+ * returned, so we can't refer it anymore
+ * (by kiblnd_connd()->kiblnd_destroy_conn) */
+ rdma_destroy_qp(conn->ibc_cmid);
+ conn->ibc_cmid = NULL;
+
+ /* Drop my own and unused rxbuffer refcounts */
+ while (i++ <= IBLND_RX_MSGS(version))
+ kiblnd_conn_decref(conn);
+
+ return NULL;
+ }
+ }
+
+ /* Init successful! */
+ LASSERT(state == IBLND_CONN_ACTIVE_CONNECT ||
+ state == IBLND_CONN_PASSIVE_WAIT);
+ conn->ibc_state = state;
+
+ /* 1 more conn */
+ atomic_inc(&net->ibn_nconns);
+ return conn;
+
+ failed_2:
+ kiblnd_destroy_conn(conn);
+ failed_1:
+ LIBCFS_FREE(init_qp_attr, sizeof(*init_qp_attr));
+ failed_0:
+ return NULL;
+}
+
+void kiblnd_destroy_conn(kib_conn_t *conn)
+{
+ struct rdma_cm_id *cmid = conn->ibc_cmid;
+ kib_peer_t *peer = conn->ibc_peer;
+ int rc;
+
+ LASSERT(!in_interrupt());
+ LASSERT(atomic_read(&conn->ibc_refcount) == 0);
+ LASSERT(list_empty(&conn->ibc_early_rxs));
+ LASSERT(list_empty(&conn->ibc_tx_noops));
+ LASSERT(list_empty(&conn->ibc_tx_queue));
+ LASSERT(list_empty(&conn->ibc_tx_queue_rsrvd));
+ LASSERT(list_empty(&conn->ibc_tx_queue_nocred));
+ LASSERT(list_empty(&conn->ibc_active_txs));
+ LASSERT(conn->ibc_noops_posted == 0);
+ LASSERT(conn->ibc_nsends_posted == 0);
+
+ switch (conn->ibc_state) {
+ default:
+ /* conn must be completely disengaged from the network */
+ LBUG();
+
+ case IBLND_CONN_DISCONNECTED:
+ /* connvars should have been freed already */
+ LASSERT(conn->ibc_connvars == NULL);
+ break;
+
+ case IBLND_CONN_INIT:
+ break;
+ }
+
+ /* conn->ibc_cmid might be destroyed by CM already */
+ if (cmid != NULL && cmid->qp != NULL)
+ rdma_destroy_qp(cmid);
+
+ if (conn->ibc_cq != NULL) {
+ rc = ib_destroy_cq(conn->ibc_cq);
+ if (rc != 0)
+ CWARN("Error destroying CQ: %d\n", rc);
+ }
+
+ if (conn->ibc_rx_pages != NULL)
+ kiblnd_unmap_rx_descs(conn);
+
+ if (conn->ibc_rxs != NULL) {
+ LIBCFS_FREE(conn->ibc_rxs,
+ IBLND_RX_MSGS(conn->ibc_version)
+ * sizeof(kib_rx_t));
+ }
+
+ if (conn->ibc_connvars != NULL)
+ LIBCFS_FREE(conn->ibc_connvars, sizeof(*conn->ibc_connvars));
+
+ if (conn->ibc_hdev != NULL)
+ kiblnd_hdev_decref(conn->ibc_hdev);
+
+ /* See CAVEAT EMPTOR above in kiblnd_create_conn */
+ if (conn->ibc_state != IBLND_CONN_INIT) {
+ kib_net_t *net = peer->ibp_ni->ni_data;
+
+ kiblnd_peer_decref(peer);
+ rdma_destroy_id(cmid);
+ atomic_dec(&net->ibn_nconns);
+ }
+
+ LIBCFS_FREE(conn, sizeof(*conn));
+}
+
+int kiblnd_close_peer_conns_locked(kib_peer_t *peer, int why)
+{
+ kib_conn_t *conn;
+ struct list_head *ctmp;
+ struct list_head *cnxt;
+ int count = 0;
+
+ list_for_each_safe(ctmp, cnxt, &peer->ibp_conns) {
+ conn = list_entry(ctmp, kib_conn_t, ibc_list);
+
+ CDEBUG(D_NET, "Closing conn -> %s, version: %x, reason: %d\n",
+ libcfs_nid2str(peer->ibp_nid),
+ conn->ibc_version, why);
+
+ kiblnd_close_conn_locked(conn, why);
+ count++;
+ }
+
+ return count;
+}
+
+int kiblnd_close_stale_conns_locked(kib_peer_t *peer,
+ int version, __u64 incarnation)
+{
+ kib_conn_t *conn;
+ struct list_head *ctmp;
+ struct list_head *cnxt;
+ int count = 0;
+
+ list_for_each_safe(ctmp, cnxt, &peer->ibp_conns) {
+ conn = list_entry(ctmp, kib_conn_t, ibc_list);
+
+ if (conn->ibc_version == version &&
+ conn->ibc_incarnation == incarnation)
+ continue;
+
+ CDEBUG(D_NET,
+ "Closing stale conn -> %s version: %x, incarnation:%#llx(%x, %#llx)\n",
+ libcfs_nid2str(peer->ibp_nid),
+ conn->ibc_version, conn->ibc_incarnation,
+ version, incarnation);
+
+ kiblnd_close_conn_locked(conn, -ESTALE);
+ count++;
+ }
+
+ return count;
+}
+
+static int kiblnd_close_matching_conns(lnet_ni_t *ni, lnet_nid_t nid)
+{
+ kib_peer_t *peer;
+ struct list_head *ptmp;
+ struct list_head *pnxt;
+ int lo;
+ int hi;
+ int i;
+ unsigned long flags;
+ int count = 0;
+
+ write_lock_irqsave(&kiblnd_data.kib_global_lock, flags);
+
+ if (nid != LNET_NID_ANY)
+ lo = hi = kiblnd_nid2peerlist(nid) - kiblnd_data.kib_peers;
+ else {
+ lo = 0;
+ hi = kiblnd_data.kib_peer_hash_size - 1;
+ }
+
+ for (i = lo; i <= hi; i++) {
+ list_for_each_safe(ptmp, pnxt, &kiblnd_data.kib_peers[i]) {
+
+ peer = list_entry(ptmp, kib_peer_t, ibp_list);
+ LASSERT(peer->ibp_connecting > 0 ||
+ peer->ibp_accepting > 0 ||
+ !list_empty(&peer->ibp_conns));
+
+ if (peer->ibp_ni != ni)
+ continue;
+
+ if (!(nid == LNET_NID_ANY || nid == peer->ibp_nid))
+ continue;
+
+ count += kiblnd_close_peer_conns_locked(peer, 0);
+ }
+ }
+
+ write_unlock_irqrestore(&kiblnd_data.kib_global_lock, flags);
+
+ /* wildcards always succeed */
+ if (nid == LNET_NID_ANY)
+ return 0;
+
+ return (count == 0) ? -ENOENT : 0;
+}
+
+int kiblnd_ctl(lnet_ni_t *ni, unsigned int cmd, void *arg)
+{
+ struct libcfs_ioctl_data *data = arg;
+ int rc = -EINVAL;
+
+ switch (cmd) {
+ case IOC_LIBCFS_GET_PEER: {
+ lnet_nid_t nid = 0;
+ int count = 0;
+
+ rc = kiblnd_get_peer_info(ni, data->ioc_count,
+ &nid, &count);
+ data->ioc_nid = nid;
+ data->ioc_count = count;
+ break;
+ }
+
+ case IOC_LIBCFS_DEL_PEER: {
+ rc = kiblnd_del_peer(ni, data->ioc_nid);
+ break;
+ }
+ case IOC_LIBCFS_GET_CONN: {
+ kib_conn_t *conn;
+
+ rc = 0;
+ conn = kiblnd_get_conn_by_idx(ni, data->ioc_count);
+ if (conn == NULL) {
+ rc = -ENOENT;
+ break;
+ }
+
+ LASSERT(conn->ibc_cmid != NULL);
+ data->ioc_nid = conn->ibc_peer->ibp_nid;
+ if (conn->ibc_cmid->route.path_rec == NULL)
+ data->ioc_u32[0] = 0; /* iWarp has no path MTU */
+ else
+ data->ioc_u32[0] =
+ ib_mtu_enum_to_int(conn->ibc_cmid->route.path_rec->mtu);
+ kiblnd_conn_decref(conn);
+ break;
+ }
+ case IOC_LIBCFS_CLOSE_CONNECTION: {
+ rc = kiblnd_close_matching_conns(ni, data->ioc_nid);
+ break;
+ }
+
+ default:
+ break;
+ }
+
+ return rc;
+}
+
+void kiblnd_query(lnet_ni_t *ni, lnet_nid_t nid, unsigned long *when)
+{
+ unsigned long last_alive = 0;
+ unsigned long now = cfs_time_current();
+ rwlock_t *glock = &kiblnd_data.kib_global_lock;
+ kib_peer_t *peer;
+ unsigned long flags;
+
+ read_lock_irqsave(glock, flags);
+
+ peer = kiblnd_find_peer_locked(nid);
+ if (peer != NULL) {
+ LASSERT(peer->ibp_connecting > 0 || /* creating conns */
+ peer->ibp_accepting > 0 ||
+ !list_empty(&peer->ibp_conns)); /* active conn */
+ last_alive = peer->ibp_last_alive;
+ }
+
+ read_unlock_irqrestore(glock, flags);
+
+ if (last_alive != 0)
+ *when = last_alive;
+
+ /* peer is not persistent in hash, trigger peer creation
+ * and connection establishment with a NULL tx */
+ if (peer == NULL)
+ kiblnd_launch_tx(ni, NULL, nid);
+
+ CDEBUG(D_NET, "Peer %s %p, alive %ld secs ago\n",
+ libcfs_nid2str(nid), peer,
+ last_alive ? cfs_duration_sec(now - last_alive) : -1);
+}
+
+void kiblnd_free_pages(kib_pages_t *p)
+{
+ int npages = p->ibp_npages;
+ int i;
+
+ for (i = 0; i < npages; i++) {
+ if (p->ibp_pages[i] != NULL)
+ __free_page(p->ibp_pages[i]);
+ }
+
+ LIBCFS_FREE(p, offsetof(kib_pages_t, ibp_pages[npages]));
+}
+
+int kiblnd_alloc_pages(kib_pages_t **pp, int cpt, int npages)
+{
+ kib_pages_t *p;
+ int i;
+
+ LIBCFS_CPT_ALLOC(p, lnet_cpt_table(), cpt,
+ offsetof(kib_pages_t, ibp_pages[npages]));
+ if (p == NULL) {
+ CERROR("Can't allocate descriptor for %d pages\n", npages);
+ return -ENOMEM;
+ }
+
+ memset(p, 0, offsetof(kib_pages_t, ibp_pages[npages]));
+ p->ibp_npages = npages;
+
+ for (i = 0; i < npages; i++) {
+ p->ibp_pages[i] = alloc_pages_node(
+ cfs_cpt_spread_node(lnet_cpt_table(), cpt),
+ GFP_NOFS, 0);
+ if (p->ibp_pages[i] == NULL) {
+ CERROR("Can't allocate page %d of %d\n", i, npages);
+ kiblnd_free_pages(p);
+ return -ENOMEM;
+ }
+ }
+
+ *pp = p;
+ return 0;
+}
+
+void kiblnd_unmap_rx_descs(kib_conn_t *conn)
+{
+ kib_rx_t *rx;
+ int i;
+
+ LASSERT(conn->ibc_rxs != NULL);
+ LASSERT(conn->ibc_hdev != NULL);
+
+ for (i = 0; i < IBLND_RX_MSGS(conn->ibc_version); i++) {
+ rx = &conn->ibc_rxs[i];
+
+ LASSERT(rx->rx_nob >= 0); /* not posted */
+
+ kiblnd_dma_unmap_single(conn->ibc_hdev->ibh_ibdev,
+ KIBLND_UNMAP_ADDR(rx, rx_msgunmap,
+ rx->rx_msgaddr),
+ IBLND_MSG_SIZE, DMA_FROM_DEVICE);
+ }
+
+ kiblnd_free_pages(conn->ibc_rx_pages);
+
+ conn->ibc_rx_pages = NULL;
+}
+
+void kiblnd_map_rx_descs(kib_conn_t *conn)
+{
+ kib_rx_t *rx;
+ struct page *pg;
+ int pg_off;
+ int ipg;
+ int i;
+
+ for (pg_off = ipg = i = 0;
+ i < IBLND_RX_MSGS(conn->ibc_version); i++) {
+ pg = conn->ibc_rx_pages->ibp_pages[ipg];
+ rx = &conn->ibc_rxs[i];
+
+ rx->rx_conn = conn;
+ rx->rx_msg = (kib_msg_t *)(((char *)page_address(pg)) + pg_off);
+
+ rx->rx_msgaddr = kiblnd_dma_map_single(conn->ibc_hdev->ibh_ibdev,
+ rx->rx_msg,
+ IBLND_MSG_SIZE,
+ DMA_FROM_DEVICE);
+ LASSERT(!kiblnd_dma_mapping_error(conn->ibc_hdev->ibh_ibdev,
+ rx->rx_msgaddr));
+ KIBLND_UNMAP_ADDR_SET(rx, rx_msgunmap, rx->rx_msgaddr);
+
+ CDEBUG(D_NET, "rx %d: %p %#llx(%#llx)\n",
+ i, rx->rx_msg, rx->rx_msgaddr,
+ lnet_page2phys(pg) + pg_off);
+
+ pg_off += IBLND_MSG_SIZE;
+ LASSERT(pg_off <= PAGE_SIZE);
+
+ if (pg_off == PAGE_SIZE) {
+ pg_off = 0;
+ ipg++;
+ LASSERT(ipg <= IBLND_RX_MSG_PAGES(conn->ibc_version));
+ }
+ }
+}
+
+static void kiblnd_unmap_tx_pool(kib_tx_pool_t *tpo)
+{
+ kib_hca_dev_t *hdev = tpo->tpo_hdev;
+ kib_tx_t *tx;
+ int i;
+
+ LASSERT(tpo->tpo_pool.po_allocated == 0);
+
+ if (hdev == NULL)
+ return;
+
+ for (i = 0; i < tpo->tpo_pool.po_size; i++) {
+ tx = &tpo->tpo_tx_descs[i];
+ kiblnd_dma_unmap_single(hdev->ibh_ibdev,
+ KIBLND_UNMAP_ADDR(tx, tx_msgunmap,
+ tx->tx_msgaddr),
+ IBLND_MSG_SIZE, DMA_TO_DEVICE);
+ }
+
+ kiblnd_hdev_decref(hdev);
+ tpo->tpo_hdev = NULL;
+}
+
+static kib_hca_dev_t *kiblnd_current_hdev(kib_dev_t *dev)
+{
+ kib_hca_dev_t *hdev;
+ unsigned long flags;
+ int i = 0;
+
+ read_lock_irqsave(&kiblnd_data.kib_global_lock, flags);
+ while (dev->ibd_failover) {
+ read_unlock_irqrestore(&kiblnd_data.kib_global_lock, flags);
+ if (i++ % 50 == 0)
+ CDEBUG(D_NET, "%s: Wait for failover\n",
+ dev->ibd_ifname);
+ schedule_timeout(cfs_time_seconds(1) / 100);
+
+ read_lock_irqsave(&kiblnd_data.kib_global_lock, flags);
+ }
+
+ kiblnd_hdev_addref_locked(dev->ibd_hdev);
+ hdev = dev->ibd_hdev;
+
+ read_unlock_irqrestore(&kiblnd_data.kib_global_lock, flags);
+
+ return hdev;
+}
+
+static void kiblnd_map_tx_pool(kib_tx_pool_t *tpo)
+{
+ kib_pages_t *txpgs = tpo->tpo_tx_pages;
+ kib_pool_t *pool = &tpo->tpo_pool;
+ kib_net_t *net = pool->po_owner->ps_net;
+ kib_dev_t *dev;
+ struct page *page;
+ kib_tx_t *tx;
+ int page_offset;
+ int ipage;
+ int i;
+
+ LASSERT(net != NULL);
+
+ dev = net->ibn_dev;
+
+ /* pre-mapped messages are not bigger than 1 page */
+ CLASSERT(IBLND_MSG_SIZE <= PAGE_SIZE);
+
+ /* No fancy arithmetic when we do the buffer calculations */
+ CLASSERT(PAGE_SIZE % IBLND_MSG_SIZE == 0);
+
+ tpo->tpo_hdev = kiblnd_current_hdev(dev);
+
+ for (ipage = page_offset = i = 0; i < pool->po_size; i++) {
+ page = txpgs->ibp_pages[ipage];
+ tx = &tpo->tpo_tx_descs[i];
+
+ tx->tx_msg = (kib_msg_t *)(((char *)page_address(page)) +
+ page_offset);
+
+ tx->tx_msgaddr = kiblnd_dma_map_single(
+ tpo->tpo_hdev->ibh_ibdev, tx->tx_msg,
+ IBLND_MSG_SIZE, DMA_TO_DEVICE);
+ LASSERT(!kiblnd_dma_mapping_error(tpo->tpo_hdev->ibh_ibdev,
+ tx->tx_msgaddr));
+ KIBLND_UNMAP_ADDR_SET(tx, tx_msgunmap, tx->tx_msgaddr);
+
+ list_add(&tx->tx_list, &pool->po_free_list);
+
+ page_offset += IBLND_MSG_SIZE;
+ LASSERT(page_offset <= PAGE_SIZE);
+
+ if (page_offset == PAGE_SIZE) {
+ page_offset = 0;
+ ipage++;
+ LASSERT(ipage <= txpgs->ibp_npages);
+ }
+ }
+}
+
+struct ib_mr *kiblnd_find_dma_mr(kib_hca_dev_t *hdev, __u64 addr, __u64 size)
+{
+ __u64 index;
+
+ LASSERT(hdev->ibh_mrs[0] != NULL);
+
+ if (hdev->ibh_nmrs == 1)
+ return hdev->ibh_mrs[0];
+
+ index = addr >> hdev->ibh_mr_shift;
+
+ if (index < hdev->ibh_nmrs &&
+ index == ((addr + size - 1) >> hdev->ibh_mr_shift))
+ return hdev->ibh_mrs[index];
+
+ return NULL;
+}
+
+struct ib_mr *kiblnd_find_rd_dma_mr(kib_hca_dev_t *hdev, kib_rdma_desc_t *rd)
+{
+ struct ib_mr *prev_mr;
+ struct ib_mr *mr;
+ int i;
+
+ LASSERT(hdev->ibh_mrs[0] != NULL);
+
+ if (*kiblnd_tunables.kib_map_on_demand > 0 &&
+ *kiblnd_tunables.kib_map_on_demand <= rd->rd_nfrags)
+ return NULL;
+
+ if (hdev->ibh_nmrs == 1)
+ return hdev->ibh_mrs[0];
+
+ for (i = 0, mr = prev_mr = NULL;
+ i < rd->rd_nfrags; i++) {
+ mr = kiblnd_find_dma_mr(hdev,
+ rd->rd_frags[i].rf_addr,
+ rd->rd_frags[i].rf_nob);
+ if (prev_mr == NULL)
+ prev_mr = mr;
+
+ if (mr == NULL || prev_mr != mr) {
+ /* Can't covered by one single MR */
+ mr = NULL;
+ break;
+ }
+ }
+
+ return mr;
+}
+
+static void kiblnd_destroy_fmr_pool(kib_fmr_pool_t *pool)
+{
+ LASSERT(pool->fpo_map_count == 0);
+
+ if (pool->fpo_fmr_pool != NULL)
+ ib_destroy_fmr_pool(pool->fpo_fmr_pool);
+
+ if (pool->fpo_hdev != NULL)
+ kiblnd_hdev_decref(pool->fpo_hdev);
+
+ LIBCFS_FREE(pool, sizeof(kib_fmr_pool_t));
+}
+
+static void kiblnd_destroy_fmr_pool_list(struct list_head *head)
+{
+ kib_fmr_pool_t *pool;
+
+ while (!list_empty(head)) {
+ pool = list_entry(head->next, kib_fmr_pool_t, fpo_list);
+ list_del(&pool->fpo_list);
+ kiblnd_destroy_fmr_pool(pool);
+ }
+}
+
+static int kiblnd_fmr_pool_size(int ncpts)
+{
+ int size = *kiblnd_tunables.kib_fmr_pool_size / ncpts;
+
+ return max(IBLND_FMR_POOL, size);
+}
+
+static int kiblnd_fmr_flush_trigger(int ncpts)
+{
+ int size = *kiblnd_tunables.kib_fmr_flush_trigger / ncpts;
+
+ return max(IBLND_FMR_POOL_FLUSH, size);
+}
+
+static int kiblnd_create_fmr_pool(kib_fmr_poolset_t *fps,
+ kib_fmr_pool_t **pp_fpo)
+{
+ /* FMR pool for RDMA */
+ kib_dev_t *dev = fps->fps_net->ibn_dev;
+ kib_fmr_pool_t *fpo;
+ struct ib_fmr_pool_param param = {
+ .max_pages_per_fmr = LNET_MAX_PAYLOAD/PAGE_SIZE,
+ .page_shift = PAGE_SHIFT,
+ .access = (IB_ACCESS_LOCAL_WRITE |
+ IB_ACCESS_REMOTE_WRITE),
+ .pool_size = fps->fps_pool_size,
+ .dirty_watermark = fps->fps_flush_trigger,
+ .flush_function = NULL,
+ .flush_arg = NULL,
+ .cache = !!*kiblnd_tunables.kib_fmr_cache};
+ int rc;
+
+ LIBCFS_CPT_ALLOC(fpo, lnet_cpt_table(), fps->fps_cpt, sizeof(*fpo));
+ if (fpo == NULL)
+ return -ENOMEM;
+
+ fpo->fpo_hdev = kiblnd_current_hdev(dev);
+
+ fpo->fpo_fmr_pool = ib_create_fmr_pool(fpo->fpo_hdev->ibh_pd, &param);
+ if (IS_ERR(fpo->fpo_fmr_pool)) {
+ rc = PTR_ERR(fpo->fpo_fmr_pool);
+ CERROR("Failed to create FMR pool: %d\n", rc);
+
+ kiblnd_hdev_decref(fpo->fpo_hdev);
+ LIBCFS_FREE(fpo, sizeof(kib_fmr_pool_t));
+ return rc;
+ }
+
+ fpo->fpo_deadline = cfs_time_shift(IBLND_POOL_DEADLINE);
+ fpo->fpo_owner = fps;
+ *pp_fpo = fpo;
+
+ return 0;
+}
+
+static void kiblnd_fail_fmr_poolset(kib_fmr_poolset_t *fps,
+ struct list_head *zombies)
+{
+ if (fps->fps_net == NULL) /* intialized? */
+ return;
+
+ spin_lock(&fps->fps_lock);
+
+ while (!list_empty(&fps->fps_pool_list)) {
+ kib_fmr_pool_t *fpo = list_entry(fps->fps_pool_list.next,
+ kib_fmr_pool_t, fpo_list);
+ fpo->fpo_failed = 1;
+ list_del(&fpo->fpo_list);
+ if (fpo->fpo_map_count == 0)
+ list_add(&fpo->fpo_list, zombies);
+ else
+ list_add(&fpo->fpo_list, &fps->fps_failed_pool_list);
+ }
+
+ spin_unlock(&fps->fps_lock);
+}
+
+static void kiblnd_fini_fmr_poolset(kib_fmr_poolset_t *fps)
+{
+ if (fps->fps_net != NULL) { /* initialized? */
+ kiblnd_destroy_fmr_pool_list(&fps->fps_failed_pool_list);
+ kiblnd_destroy_fmr_pool_list(&fps->fps_pool_list);
+ }
+}
+
+static int kiblnd_init_fmr_poolset(kib_fmr_poolset_t *fps, int cpt,
+ kib_net_t *net, int pool_size,
+ int flush_trigger)
+{
+ kib_fmr_pool_t *fpo;
+ int rc;
+
+ memset(fps, 0, sizeof(kib_fmr_poolset_t));
+
+ fps->fps_net = net;
+ fps->fps_cpt = cpt;
+ fps->fps_pool_size = pool_size;
+ fps->fps_flush_trigger = flush_trigger;
+ spin_lock_init(&fps->fps_lock);
+ INIT_LIST_HEAD(&fps->fps_pool_list);
+ INIT_LIST_HEAD(&fps->fps_failed_pool_list);
+
+ rc = kiblnd_create_fmr_pool(fps, &fpo);
+ if (rc == 0)
+ list_add_tail(&fpo->fpo_list, &fps->fps_pool_list);
+
+ return rc;
+}
+
+static int kiblnd_fmr_pool_is_idle(kib_fmr_pool_t *fpo, unsigned long now)
+{
+ if (fpo->fpo_map_count != 0) /* still in use */
+ return 0;
+ if (fpo->fpo_failed)
+ return 1;
+ return cfs_time_aftereq(now, fpo->fpo_deadline);
+}
+
+void kiblnd_fmr_pool_unmap(kib_fmr_t *fmr, int status)
+{
+ LIST_HEAD(zombies);
+ kib_fmr_pool_t *fpo = fmr->fmr_pool;
+ kib_fmr_poolset_t *fps = fpo->fpo_owner;
+ unsigned long now = cfs_time_current();
+ kib_fmr_pool_t *tmp;
+ int rc;
+
+ rc = ib_fmr_pool_unmap(fmr->fmr_pfmr);
+ LASSERT(rc == 0);
+
+ if (status != 0) {
+ rc = ib_flush_fmr_pool(fpo->fpo_fmr_pool);
+ LASSERT(rc == 0);
+ }
+
+ fmr->fmr_pool = NULL;
+ fmr->fmr_pfmr = NULL;
+
+ spin_lock(&fps->fps_lock);
+ fpo->fpo_map_count--; /* decref the pool */
+
+ list_for_each_entry_safe(fpo, tmp, &fps->fps_pool_list, fpo_list) {
+ /* the first pool is persistent */
+ if (fps->fps_pool_list.next == &fpo->fpo_list)
+ continue;
+
+ if (kiblnd_fmr_pool_is_idle(fpo, now)) {
+ list_move(&fpo->fpo_list, &zombies);
+ fps->fps_version++;
+ }
+ }
+ spin_unlock(&fps->fps_lock);
+
+ if (!list_empty(&zombies))
+ kiblnd_destroy_fmr_pool_list(&zombies);
+}
+
+int kiblnd_fmr_pool_map(kib_fmr_poolset_t *fps, __u64 *pages, int npages,
+ __u64 iov, kib_fmr_t *fmr)
+{
+ struct ib_pool_fmr *pfmr;
+ kib_fmr_pool_t *fpo;
+ __u64 version;
+ int rc;
+
+ again:
+ spin_lock(&fps->fps_lock);
+ version = fps->fps_version;
+ list_for_each_entry(fpo, &fps->fps_pool_list, fpo_list) {
+ fpo->fpo_deadline = cfs_time_shift(IBLND_POOL_DEADLINE);
+ fpo->fpo_map_count++;
+ spin_unlock(&fps->fps_lock);
+
+ pfmr = ib_fmr_pool_map_phys(fpo->fpo_fmr_pool,
+ pages, npages, iov);
+ if (likely(!IS_ERR(pfmr))) {
+ fmr->fmr_pool = fpo;
+ fmr->fmr_pfmr = pfmr;
+ return 0;
+ }
+
+ spin_lock(&fps->fps_lock);
+ fpo->fpo_map_count--;
+ if (PTR_ERR(pfmr) != -EAGAIN) {
+ spin_unlock(&fps->fps_lock);
+ return PTR_ERR(pfmr);
+ }
+
+ /* EAGAIN and ... */
+ if (version != fps->fps_version) {
+ spin_unlock(&fps->fps_lock);
+ goto again;
+ }
+ }
+
+ if (fps->fps_increasing) {
+ spin_unlock(&fps->fps_lock);
+ CDEBUG(D_NET,
+ "Another thread is allocating new FMR pool, waiting for her to complete\n");
+ schedule();
+ goto again;
+
+ }
+
+ if (time_before(cfs_time_current(), fps->fps_next_retry)) {
+ /* someone failed recently */
+ spin_unlock(&fps->fps_lock);
+ return -EAGAIN;
+ }
+
+ fps->fps_increasing = 1;
+ spin_unlock(&fps->fps_lock);
+
+ CDEBUG(D_NET, "Allocate new FMR pool\n");
+ rc = kiblnd_create_fmr_pool(fps, &fpo);
+ spin_lock(&fps->fps_lock);
+ fps->fps_increasing = 0;
+ if (rc == 0) {
+ fps->fps_version++;
+ list_add_tail(&fpo->fpo_list, &fps->fps_pool_list);
+ } else {
+ fps->fps_next_retry = cfs_time_shift(IBLND_POOL_RETRY);
+ }
+ spin_unlock(&fps->fps_lock);
+
+ goto again;
+}
+
+static void kiblnd_fini_pool(kib_pool_t *pool)
+{
+ LASSERT(list_empty(&pool->po_free_list));
+ LASSERT(pool->po_allocated == 0);
+
+ CDEBUG(D_NET, "Finalize %s pool\n", pool->po_owner->ps_name);
+}
+
+static void kiblnd_init_pool(kib_poolset_t *ps, kib_pool_t *pool, int size)
+{
+ CDEBUG(D_NET, "Initialize %s pool\n", ps->ps_name);
+
+ memset(pool, 0, sizeof(kib_pool_t));
+ INIT_LIST_HEAD(&pool->po_free_list);
+ pool->po_deadline = cfs_time_shift(IBLND_POOL_DEADLINE);
+ pool->po_owner = ps;
+ pool->po_size = size;
+}
+
+static void kiblnd_destroy_pool_list(struct list_head *head)
+{
+ kib_pool_t *pool;
+
+ while (!list_empty(head)) {
+ pool = list_entry(head->next, kib_pool_t, po_list);
+ list_del(&pool->po_list);
+
+ LASSERT(pool->po_owner != NULL);
+ pool->po_owner->ps_pool_destroy(pool);
+ }
+}
+
+static void kiblnd_fail_poolset(kib_poolset_t *ps, struct list_head *zombies)
+{
+ if (ps->ps_net == NULL) /* intialized? */
+ return;
+
+ spin_lock(&ps->ps_lock);
+ while (!list_empty(&ps->ps_pool_list)) {
+ kib_pool_t *po = list_entry(ps->ps_pool_list.next,
+ kib_pool_t, po_list);
+ po->po_failed = 1;
+ list_del(&po->po_list);
+ if (po->po_allocated == 0)
+ list_add(&po->po_list, zombies);
+ else
+ list_add(&po->po_list, &ps->ps_failed_pool_list);
+ }
+ spin_unlock(&ps->ps_lock);
+}
+
+static void kiblnd_fini_poolset(kib_poolset_t *ps)
+{
+ if (ps->ps_net != NULL) { /* initialized? */
+ kiblnd_destroy_pool_list(&ps->ps_failed_pool_list);
+ kiblnd_destroy_pool_list(&ps->ps_pool_list);
+ }
+}
+
+static int kiblnd_init_poolset(kib_poolset_t *ps, int cpt,
+ kib_net_t *net, char *name, int size,
+ kib_ps_pool_create_t po_create,
+ kib_ps_pool_destroy_t po_destroy,
+ kib_ps_node_init_t nd_init,
+ kib_ps_node_fini_t nd_fini)
+{
+ kib_pool_t *pool;
+ int rc;
+
+ memset(ps, 0, sizeof(kib_poolset_t));
+
+ ps->ps_cpt = cpt;
+ ps->ps_net = net;
+ ps->ps_pool_create = po_create;
+ ps->ps_pool_destroy = po_destroy;
+ ps->ps_node_init = nd_init;
+ ps->ps_node_fini = nd_fini;
+ ps->ps_pool_size = size;
+ if (strlcpy(ps->ps_name, name, sizeof(ps->ps_name))
+ >= sizeof(ps->ps_name))
+ return -E2BIG;
+ spin_lock_init(&ps->ps_lock);
+ INIT_LIST_HEAD(&ps->ps_pool_list);
+ INIT_LIST_HEAD(&ps->ps_failed_pool_list);
+
+ rc = ps->ps_pool_create(ps, size, &pool);
+ if (rc == 0)
+ list_add(&pool->po_list, &ps->ps_pool_list);
+ else
+ CERROR("Failed to create the first pool for %s\n", ps->ps_name);
+
+ return rc;
+}
+
+static int kiblnd_pool_is_idle(kib_pool_t *pool, unsigned long now)
+{
+ if (pool->po_allocated != 0) /* still in use */
+ return 0;
+ if (pool->po_failed)
+ return 1;
+ return cfs_time_aftereq(now, pool->po_deadline);
+}
+
+void kiblnd_pool_free_node(kib_pool_t *pool, struct list_head *node)
+{
+ LIST_HEAD(zombies);
+ kib_poolset_t *ps = pool->po_owner;
+ kib_pool_t *tmp;
+ unsigned long now = cfs_time_current();
+
+ spin_lock(&ps->ps_lock);
+
+ if (ps->ps_node_fini != NULL)
+ ps->ps_node_fini(pool, node);
+
+ LASSERT(pool->po_allocated > 0);
+ list_add(node, &pool->po_free_list);
+ pool->po_allocated--;
+
+ list_for_each_entry_safe(pool, tmp, &ps->ps_pool_list, po_list) {
+ /* the first pool is persistent */
+ if (ps->ps_pool_list.next == &pool->po_list)
+ continue;
+
+ if (kiblnd_pool_is_idle(pool, now))
+ list_move(&pool->po_list, &zombies);
+ }
+ spin_unlock(&ps->ps_lock);
+
+ if (!list_empty(&zombies))
+ kiblnd_destroy_pool_list(&zombies);
+}
+
+struct list_head *kiblnd_pool_alloc_node(kib_poolset_t *ps)
+{
+ struct list_head *node;
+ kib_pool_t *pool;
+ int rc;
+
+ again:
+ spin_lock(&ps->ps_lock);
+ list_for_each_entry(pool, &ps->ps_pool_list, po_list) {
+ if (list_empty(&pool->po_free_list))
+ continue;
+
+ pool->po_allocated++;
+ pool->po_deadline = cfs_time_shift(IBLND_POOL_DEADLINE);
+ node = pool->po_free_list.next;
+ list_del(node);
+
+ if (ps->ps_node_init != NULL) {
+ /* still hold the lock */
+ ps->ps_node_init(pool, node);
+ }
+ spin_unlock(&ps->ps_lock);
+ return node;
+ }
+
+ /* no available tx pool and ... */
+ if (ps->ps_increasing) {
+ /* another thread is allocating a new pool */
+ spin_unlock(&ps->ps_lock);
+ CDEBUG(D_NET, "Another thread is allocating new %s pool, waiting for her to complete\n",
+ ps->ps_name);
+ schedule();
+ goto again;
+ }
+
+ if (time_before(cfs_time_current(), ps->ps_next_retry)) {
+ /* someone failed recently */
+ spin_unlock(&ps->ps_lock);
+ return NULL;
+ }
+
+ ps->ps_increasing = 1;
+ spin_unlock(&ps->ps_lock);
+
+ CDEBUG(D_NET, "%s pool exhausted, allocate new pool\n", ps->ps_name);
+
+ rc = ps->ps_pool_create(ps, ps->ps_pool_size, &pool);
+
+ spin_lock(&ps->ps_lock);
+ ps->ps_increasing = 0;
+ if (rc == 0) {
+ list_add_tail(&pool->po_list, &ps->ps_pool_list);
+ } else {
+ ps->ps_next_retry = cfs_time_shift(IBLND_POOL_RETRY);
+ CERROR("Can't allocate new %s pool because out of memory\n",
+ ps->ps_name);
+ }
+ spin_unlock(&ps->ps_lock);
+
+ goto again;
+}
+
+void kiblnd_pmr_pool_unmap(kib_phys_mr_t *pmr)
+{
+ kib_pmr_pool_t *ppo = pmr->pmr_pool;
+ struct ib_mr *mr = pmr->pmr_mr;
+
+ pmr->pmr_mr = NULL;
+ kiblnd_pool_free_node(&ppo->ppo_pool, &pmr->pmr_list);
+ if (mr != NULL)
+ ib_dereg_mr(mr);
+}
+
+int kiblnd_pmr_pool_map(kib_pmr_poolset_t *pps, kib_hca_dev_t *hdev,
+ kib_rdma_desc_t *rd, __u64 *iova, kib_phys_mr_t **pp_pmr)
+{
+ kib_phys_mr_t *pmr;
+ struct list_head *node;
+ int rc;
+ int i;
+
+ node = kiblnd_pool_alloc_node(&pps->pps_poolset);
+ if (node == NULL) {
+ CERROR("Failed to allocate PMR descriptor\n");
+ return -ENOMEM;
+ }
+
+ pmr = container_of(node, kib_phys_mr_t, pmr_list);
+ if (pmr->pmr_pool->ppo_hdev != hdev) {
+ kiblnd_pool_free_node(&pmr->pmr_pool->ppo_pool, node);
+ return -EAGAIN;
+ }
+
+ for (i = 0; i < rd->rd_nfrags; i++) {
+ pmr->pmr_ipb[i].addr = rd->rd_frags[i].rf_addr;
+ pmr->pmr_ipb[i].size = rd->rd_frags[i].rf_nob;
+ }
+
+ pmr->pmr_mr = ib_reg_phys_mr(hdev->ibh_pd,
+ pmr->pmr_ipb, rd->rd_nfrags,
+ IB_ACCESS_LOCAL_WRITE |
+ IB_ACCESS_REMOTE_WRITE,
+ iova);
+ if (!IS_ERR(pmr->pmr_mr)) {
+ pmr->pmr_iova = *iova;
+ *pp_pmr = pmr;
+ return 0;
+ }
+
+ rc = PTR_ERR(pmr->pmr_mr);
+ CERROR("Failed ib_reg_phys_mr: %d\n", rc);
+
+ pmr->pmr_mr = NULL;
+ kiblnd_pool_free_node(&pmr->pmr_pool->ppo_pool, node);
+
+ return rc;
+}
+
+static void kiblnd_destroy_pmr_pool(kib_pool_t *pool)
+{
+ kib_pmr_pool_t *ppo = container_of(pool, kib_pmr_pool_t, ppo_pool);
+ kib_phys_mr_t *pmr;
+ kib_phys_mr_t *tmp;
+
+ LASSERT(pool->po_allocated == 0);
+
+ list_for_each_entry_safe(pmr, tmp, &pool->po_free_list, pmr_list) {
+ LASSERT(pmr->pmr_mr == NULL);
+ list_del(&pmr->pmr_list);
+
+ if (pmr->pmr_ipb != NULL) {
+ LIBCFS_FREE(pmr->pmr_ipb,
+ IBLND_MAX_RDMA_FRAGS *
+ sizeof(struct ib_phys_buf));
+ }
+
+ LIBCFS_FREE(pmr, sizeof(kib_phys_mr_t));
+ }
+
+ kiblnd_fini_pool(pool);
+ if (ppo->ppo_hdev != NULL)
+ kiblnd_hdev_decref(ppo->ppo_hdev);
+
+ LIBCFS_FREE(ppo, sizeof(kib_pmr_pool_t));
+}
+
+static inline int kiblnd_pmr_pool_size(int ncpts)
+{
+ int size = *kiblnd_tunables.kib_pmr_pool_size / ncpts;
+
+ return max(IBLND_PMR_POOL, size);
+}
+
+static int kiblnd_create_pmr_pool(kib_poolset_t *ps, int size,
+ kib_pool_t **pp_po)
+{
+ struct kib_pmr_pool *ppo;
+ struct kib_pool *pool;
+ kib_phys_mr_t *pmr;
+ int i;
+
+ LIBCFS_CPT_ALLOC(ppo, lnet_cpt_table(),
+ ps->ps_cpt, sizeof(kib_pmr_pool_t));
+ if (ppo == NULL) {
+ CERROR("Failed to allocate PMR pool\n");
+ return -ENOMEM;
+ }
+
+ pool = &ppo->ppo_pool;
+ kiblnd_init_pool(ps, pool, size);
+
+ for (i = 0; i < size; i++) {
+ LIBCFS_CPT_ALLOC(pmr, lnet_cpt_table(),
+ ps->ps_cpt, sizeof(kib_phys_mr_t));
+ if (pmr == NULL)
+ break;
+
+ pmr->pmr_pool = ppo;
+ LIBCFS_CPT_ALLOC(pmr->pmr_ipb, lnet_cpt_table(), ps->ps_cpt,
+ IBLND_MAX_RDMA_FRAGS * sizeof(*pmr->pmr_ipb));
+ if (pmr->pmr_ipb == NULL)
+ break;
+
+ list_add(&pmr->pmr_list, &pool->po_free_list);
+ }
+
+ if (i < size) {
+ ps->ps_pool_destroy(pool);
+ return -ENOMEM;
+ }
+
+ ppo->ppo_hdev = kiblnd_current_hdev(ps->ps_net->ibn_dev);
+ *pp_po = pool;
+ return 0;
+}
+
+static void kiblnd_destroy_tx_pool(kib_pool_t *pool)
+{
+ kib_tx_pool_t *tpo = container_of(pool, kib_tx_pool_t, tpo_pool);
+ int i;
+
+ LASSERT(pool->po_allocated == 0);
+
+ if (tpo->tpo_tx_pages != NULL) {
+ kiblnd_unmap_tx_pool(tpo);
+ kiblnd_free_pages(tpo->tpo_tx_pages);
+ }
+
+ if (tpo->tpo_tx_descs == NULL)
+ goto out;
+
+ for (i = 0; i < pool->po_size; i++) {
+ kib_tx_t *tx = &tpo->tpo_tx_descs[i];
+
+ list_del(&tx->tx_list);
+ if (tx->tx_pages != NULL)
+ LIBCFS_FREE(tx->tx_pages,
+ LNET_MAX_IOV *
+ sizeof(*tx->tx_pages));
+ if (tx->tx_frags != NULL)
+ LIBCFS_FREE(tx->tx_frags,
+ IBLND_MAX_RDMA_FRAGS *
+ sizeof(*tx->tx_frags));
+ if (tx->tx_wrq != NULL)
+ LIBCFS_FREE(tx->tx_wrq,
+ (1 + IBLND_MAX_RDMA_FRAGS) *
+ sizeof(*tx->tx_wrq));
+ if (tx->tx_sge != NULL)
+ LIBCFS_FREE(tx->tx_sge,
+ (1 + IBLND_MAX_RDMA_FRAGS) *
+ sizeof(*tx->tx_sge));
+ if (tx->tx_rd != NULL)
+ LIBCFS_FREE(tx->tx_rd,
+ offsetof(kib_rdma_desc_t,
+ rd_frags[IBLND_MAX_RDMA_FRAGS]));
+ }
+
+ LIBCFS_FREE(tpo->tpo_tx_descs,
+ pool->po_size * sizeof(kib_tx_t));
+out:
+ kiblnd_fini_pool(pool);
+ LIBCFS_FREE(tpo, sizeof(kib_tx_pool_t));
+}
+
+static int kiblnd_tx_pool_size(int ncpts)
+{
+ int ntx = *kiblnd_tunables.kib_ntx / ncpts;
+
+ return max(IBLND_TX_POOL, ntx);
+}
+
+static int kiblnd_create_tx_pool(kib_poolset_t *ps, int size,
+ kib_pool_t **pp_po)
+{
+ int i;
+ int npg;
+ kib_pool_t *pool;
+ kib_tx_pool_t *tpo;
+
+ LIBCFS_CPT_ALLOC(tpo, lnet_cpt_table(), ps->ps_cpt, sizeof(*tpo));
+ if (tpo == NULL) {
+ CERROR("Failed to allocate TX pool\n");
+ return -ENOMEM;
+ }
+
+ pool = &tpo->tpo_pool;
+ kiblnd_init_pool(ps, pool, size);
+ tpo->tpo_tx_descs = NULL;
+ tpo->tpo_tx_pages = NULL;
+
+ npg = (size * IBLND_MSG_SIZE + PAGE_SIZE - 1) / PAGE_SIZE;
+ if (kiblnd_alloc_pages(&tpo->tpo_tx_pages, ps->ps_cpt, npg) != 0) {
+ CERROR("Can't allocate tx pages: %d\n", npg);
+ LIBCFS_FREE(tpo, sizeof(kib_tx_pool_t));
+ return -ENOMEM;
+ }
+
+ LIBCFS_CPT_ALLOC(tpo->tpo_tx_descs, lnet_cpt_table(), ps->ps_cpt,
+ size * sizeof(kib_tx_t));
+ if (tpo->tpo_tx_descs == NULL) {
+ CERROR("Can't allocate %d tx descriptors\n", size);
+ ps->ps_pool_destroy(pool);
+ return -ENOMEM;
+ }
+
+ memset(tpo->tpo_tx_descs, 0, size * sizeof(kib_tx_t));
+
+ for (i = 0; i < size; i++) {
+ kib_tx_t *tx = &tpo->tpo_tx_descs[i];
+
+ tx->tx_pool = tpo;
+ if (ps->ps_net->ibn_fmr_ps != NULL) {
+ LIBCFS_CPT_ALLOC(tx->tx_pages,
+ lnet_cpt_table(), ps->ps_cpt,
+ LNET_MAX_IOV * sizeof(*tx->tx_pages));
+ if (tx->tx_pages == NULL)
+ break;
+ }
+
+ LIBCFS_CPT_ALLOC(tx->tx_frags, lnet_cpt_table(), ps->ps_cpt,
+ IBLND_MAX_RDMA_FRAGS * sizeof(*tx->tx_frags));
+ if (tx->tx_frags == NULL)
+ break;
+
+ sg_init_table(tx->tx_frags, IBLND_MAX_RDMA_FRAGS);
+
+ LIBCFS_CPT_ALLOC(tx->tx_wrq, lnet_cpt_table(), ps->ps_cpt,
+ (1 + IBLND_MAX_RDMA_FRAGS) *
+ sizeof(*tx->tx_wrq));
+ if (tx->tx_wrq == NULL)
+ break;
+
+ LIBCFS_CPT_ALLOC(tx->tx_sge, lnet_cpt_table(), ps->ps_cpt,
+ (1 + IBLND_MAX_RDMA_FRAGS) *
+ sizeof(*tx->tx_sge));
+ if (tx->tx_sge == NULL)
+ break;
+
+ LIBCFS_CPT_ALLOC(tx->tx_rd, lnet_cpt_table(), ps->ps_cpt,
+ offsetof(kib_rdma_desc_t,
+ rd_frags[IBLND_MAX_RDMA_FRAGS]));
+ if (tx->tx_rd == NULL)
+ break;
+ }
+
+ if (i == size) {
+ kiblnd_map_tx_pool(tpo);
+ *pp_po = pool;
+ return 0;
+ }
+
+ ps->ps_pool_destroy(pool);
+ return -ENOMEM;
+}
+
+static void kiblnd_tx_init(kib_pool_t *pool, struct list_head *node)
+{
+ kib_tx_poolset_t *tps = container_of(pool->po_owner, kib_tx_poolset_t,
+ tps_poolset);
+ kib_tx_t *tx = list_entry(node, kib_tx_t, tx_list);
+
+ tx->tx_cookie = tps->tps_next_tx_cookie++;
+}
+
+static void kiblnd_net_fini_pools(kib_net_t *net)
+{
+ int i;
+
+ cfs_cpt_for_each(i, lnet_cpt_table()) {
+ kib_tx_poolset_t *tps;
+ kib_fmr_poolset_t *fps;
+ kib_pmr_poolset_t *pps;
+
+ if (net->ibn_tx_ps != NULL) {
+ tps = net->ibn_tx_ps[i];
+ kiblnd_fini_poolset(&tps->tps_poolset);
+ }
+
+ if (net->ibn_fmr_ps != NULL) {
+ fps = net->ibn_fmr_ps[i];
+ kiblnd_fini_fmr_poolset(fps);
+ }
+
+ if (net->ibn_pmr_ps != NULL) {
+ pps = net->ibn_pmr_ps[i];
+ kiblnd_fini_poolset(&pps->pps_poolset);
+ }
+ }
+
+ if (net->ibn_tx_ps != NULL) {
+ cfs_percpt_free(net->ibn_tx_ps);
+ net->ibn_tx_ps = NULL;
+ }
+
+ if (net->ibn_fmr_ps != NULL) {
+ cfs_percpt_free(net->ibn_fmr_ps);
+ net->ibn_fmr_ps = NULL;
+ }
+
+ if (net->ibn_pmr_ps != NULL) {
+ cfs_percpt_free(net->ibn_pmr_ps);
+ net->ibn_pmr_ps = NULL;
+ }
+}
+
+static int kiblnd_net_init_pools(kib_net_t *net, __u32 *cpts, int ncpts)
+{
+ unsigned long flags;
+ int cpt;
+ int rc;
+ int i;
+
+ read_lock_irqsave(&kiblnd_data.kib_global_lock, flags);
+ if (*kiblnd_tunables.kib_map_on_demand == 0 &&
+ net->ibn_dev->ibd_hdev->ibh_nmrs == 1) {
+ read_unlock_irqrestore(&kiblnd_data.kib_global_lock,
+ flags);
+ goto create_tx_pool;
+ }
+
+ read_unlock_irqrestore(&kiblnd_data.kib_global_lock, flags);
+
+ if (*kiblnd_tunables.kib_fmr_pool_size <
+ *kiblnd_tunables.kib_ntx / 4) {
+ CERROR("Can't set fmr pool size (%d) < ntx / 4(%d)\n",
+ *kiblnd_tunables.kib_fmr_pool_size,
+ *kiblnd_tunables.kib_ntx / 4);
+ rc = -EINVAL;
+ goto failed;
+ }
+
+ /* TX pool must be created later than FMR/PMR, see LU-2268
+ * for details */
+ LASSERT(net->ibn_tx_ps == NULL);
+
+ /* premapping can fail if ibd_nmr > 1, so we always create
+ * FMR/PMR pool and map-on-demand if premapping failed */
+
+ net->ibn_fmr_ps = cfs_percpt_alloc(lnet_cpt_table(),
+ sizeof(kib_fmr_poolset_t));
+ if (net->ibn_fmr_ps == NULL) {
+ CERROR("Failed to allocate FMR pool array\n");
+ rc = -ENOMEM;
+ goto failed;
+ }
+
+ for (i = 0; i < ncpts; i++) {
+ cpt = (cpts == NULL) ? i : cpts[i];
+ rc = kiblnd_init_fmr_poolset(net->ibn_fmr_ps[cpt], cpt, net,
+ kiblnd_fmr_pool_size(ncpts),
+ kiblnd_fmr_flush_trigger(ncpts));
+ if (rc == -ENOSYS && i == 0) /* no FMR */
+ break; /* create PMR pool */
+
+ if (rc != 0) { /* a real error */
+ CERROR("Can't initialize FMR pool for CPT %d: %d\n",
+ cpt, rc);
+ goto failed;
+ }
+ }
+
+ if (i > 0) {
+ LASSERT(i == ncpts);
+ goto create_tx_pool;
+ }
+
+ cfs_percpt_free(net->ibn_fmr_ps);
+ net->ibn_fmr_ps = NULL;
+
+ CWARN("Device does not support FMR, failing back to PMR\n");
+
+ if (*kiblnd_tunables.kib_pmr_pool_size <
+ *kiblnd_tunables.kib_ntx / 4) {
+ CERROR("Can't set pmr pool size (%d) < ntx / 4(%d)\n",
+ *kiblnd_tunables.kib_pmr_pool_size,
+ *kiblnd_tunables.kib_ntx / 4);
+ rc = -EINVAL;
+ goto failed;
+ }
+
+ net->ibn_pmr_ps = cfs_percpt_alloc(lnet_cpt_table(),
+ sizeof(kib_pmr_poolset_t));
+ if (net->ibn_pmr_ps == NULL) {
+ CERROR("Failed to allocate PMR pool array\n");
+ rc = -ENOMEM;
+ goto failed;
+ }
+
+ for (i = 0; i < ncpts; i++) {
+ cpt = (cpts == NULL) ? i : cpts[i];
+ rc = kiblnd_init_poolset(&net->ibn_pmr_ps[cpt]->pps_poolset,
+ cpt, net, "PMR",
+ kiblnd_pmr_pool_size(ncpts),
+ kiblnd_create_pmr_pool,
+ kiblnd_destroy_pmr_pool, NULL, NULL);
+ if (rc != 0) {
+ CERROR("Can't initialize PMR pool for CPT %d: %d\n",
+ cpt, rc);
+ goto failed;
+ }
+ }
+
+ create_tx_pool:
+ net->ibn_tx_ps = cfs_percpt_alloc(lnet_cpt_table(),
+ sizeof(kib_tx_poolset_t));
+ if (net->ibn_tx_ps == NULL) {
+ CERROR("Failed to allocate tx pool array\n");
+ rc = -ENOMEM;
+ goto failed;
+ }
+
+ for (i = 0; i < ncpts; i++) {
+ cpt = (cpts == NULL) ? i : cpts[i];
+ rc = kiblnd_init_poolset(&net->ibn_tx_ps[cpt]->tps_poolset,
+ cpt, net, "TX",
+ kiblnd_tx_pool_size(ncpts),
+ kiblnd_create_tx_pool,
+ kiblnd_destroy_tx_pool,
+ kiblnd_tx_init, NULL);
+ if (rc != 0) {
+ CERROR("Can't initialize TX pool for CPT %d: %d\n",
+ cpt, rc);
+ goto failed;
+ }
+ }
+
+ return 0;
+ failed:
+ kiblnd_net_fini_pools(net);
+ LASSERT(rc != 0);
+ return rc;
+}
+
+static int kiblnd_hdev_get_attr(kib_hca_dev_t *hdev)
+{
+ struct ib_device_attr *attr;
+ int rc;
+
+ /* It's safe to assume a HCA can handle a page size
+ * matching that of the native system */
+ hdev->ibh_page_shift = PAGE_SHIFT;
+ hdev->ibh_page_size = 1 << PAGE_SHIFT;
+ hdev->ibh_page_mask = ~((__u64)hdev->ibh_page_size - 1);
+
+ LIBCFS_ALLOC(attr, sizeof(*attr));
+ if (attr == NULL) {
+ CERROR("Out of memory\n");
+ return -ENOMEM;
+ }
+
+ rc = ib_query_device(hdev->ibh_ibdev, attr);
+ if (rc == 0)
+ hdev->ibh_mr_size = attr->max_mr_size;
+
+ LIBCFS_FREE(attr, sizeof(*attr));
+
+ if (rc != 0) {
+ CERROR("Failed to query IB device: %d\n", rc);
+ return rc;
+ }
+
+ if (hdev->ibh_mr_size == ~0ULL) {
+ hdev->ibh_mr_shift = 64;
+ return 0;
+ }
+
+ for (hdev->ibh_mr_shift = 0;
+ hdev->ibh_mr_shift < 64; hdev->ibh_mr_shift++) {
+ if (hdev->ibh_mr_size == (1ULL << hdev->ibh_mr_shift) ||
+ hdev->ibh_mr_size == (1ULL << hdev->ibh_mr_shift) - 1)
+ return 0;
+ }
+
+ CERROR("Invalid mr size: %#llx\n", hdev->ibh_mr_size);
+ return -EINVAL;
+}
+
+static void kiblnd_hdev_cleanup_mrs(kib_hca_dev_t *hdev)
+{
+ int i;
+
+ if (hdev->ibh_nmrs == 0 || hdev->ibh_mrs == NULL)
+ return;
+
+ for (i = 0; i < hdev->ibh_nmrs; i++) {
+ if (hdev->ibh_mrs[i] == NULL)
+ break;
+
+ ib_dereg_mr(hdev->ibh_mrs[i]);
+ }
+
+ LIBCFS_FREE(hdev->ibh_mrs, sizeof(*hdev->ibh_mrs) * hdev->ibh_nmrs);
+ hdev->ibh_mrs = NULL;
+ hdev->ibh_nmrs = 0;
+}
+
+void kiblnd_hdev_destroy(kib_hca_dev_t *hdev)
+{
+ kiblnd_hdev_cleanup_mrs(hdev);
+
+ if (hdev->ibh_pd != NULL)
+ ib_dealloc_pd(hdev->ibh_pd);
+
+ if (hdev->ibh_cmid != NULL)
+ rdma_destroy_id(hdev->ibh_cmid);
+
+ LIBCFS_FREE(hdev, sizeof(*hdev));
+}
+
+static int kiblnd_hdev_setup_mrs(kib_hca_dev_t *hdev)
+{
+ struct ib_mr *mr;
+ int i;
+ int rc;
+ __u64 mm_size;
+ __u64 mr_size;
+ int acflags = IB_ACCESS_LOCAL_WRITE |
+ IB_ACCESS_REMOTE_WRITE;
+
+ rc = kiblnd_hdev_get_attr(hdev);
+ if (rc != 0)
+ return rc;
+
+ if (hdev->ibh_mr_shift == 64) {
+ LIBCFS_ALLOC(hdev->ibh_mrs, 1 * sizeof(*hdev->ibh_mrs));
+ if (hdev->ibh_mrs == NULL) {
+ CERROR("Failed to allocate MRs table\n");
+ return -ENOMEM;
+ }
+
+ hdev->ibh_mrs[0] = NULL;
+ hdev->ibh_nmrs = 1;
+
+ mr = ib_get_dma_mr(hdev->ibh_pd, acflags);
+ if (IS_ERR(mr)) {
+ CERROR("Failed ib_get_dma_mr : %ld\n", PTR_ERR(mr));
+ kiblnd_hdev_cleanup_mrs(hdev);
+ return PTR_ERR(mr);
+ }
+
+ hdev->ibh_mrs[0] = mr;
+
+ goto out;
+ }
+
+ mr_size = 1ULL << hdev->ibh_mr_shift;
+ mm_size = (unsigned long)high_memory - PAGE_OFFSET;
+
+ hdev->ibh_nmrs = (int)((mm_size + mr_size - 1) >> hdev->ibh_mr_shift);
+
+ if (hdev->ibh_mr_shift < 32 || hdev->ibh_nmrs > 1024) {
+ /* it's 4T..., assume we will re-code at that time */
+ CERROR("Can't support memory size: x%#llx with MR size: x%#llx\n",
+ mm_size, mr_size);
+ return -EINVAL;
+ }
+
+ /* create an array of MRs to cover all memory */
+ LIBCFS_ALLOC(hdev->ibh_mrs, sizeof(*hdev->ibh_mrs) * hdev->ibh_nmrs);
+ if (hdev->ibh_mrs == NULL) {
+ CERROR("Failed to allocate MRs' table\n");
+ return -ENOMEM;
+ }
+
+ for (i = 0; i < hdev->ibh_nmrs; i++) {
+ struct ib_phys_buf ipb;
+ __u64 iova;
+
+ ipb.size = hdev->ibh_mr_size;
+ ipb.addr = i * mr_size;
+ iova = ipb.addr;
+
+ mr = ib_reg_phys_mr(hdev->ibh_pd, &ipb, 1, acflags, &iova);
+ if (IS_ERR(mr)) {
+ CERROR("Failed ib_reg_phys_mr addr %#llx size %#llx : %ld\n",
+ ipb.addr, ipb.size, PTR_ERR(mr));
+ kiblnd_hdev_cleanup_mrs(hdev);
+ return PTR_ERR(mr);
+ }
+
+ LASSERT(iova == ipb.addr);
+
+ hdev->ibh_mrs[i] = mr;
+ }
+
+out:
+ if (hdev->ibh_mr_size != ~0ULL || hdev->ibh_nmrs != 1)
+ LCONSOLE_INFO("Register global MR array, MR size: %#llx, array size: %d\n",
+ hdev->ibh_mr_size, hdev->ibh_nmrs);
+ return 0;
+}
+
+/* DUMMY */
+static int kiblnd_dummy_callback(struct rdma_cm_id *cmid,
+ struct rdma_cm_event *event)
+{
+ return 0;
+}
+
+static int kiblnd_dev_need_failover(kib_dev_t *dev)
+{
+ struct rdma_cm_id *cmid;
+ struct sockaddr_in srcaddr;
+ struct sockaddr_in dstaddr;
+ int rc;
+
+ if (dev->ibd_hdev == NULL || /* initializing */
+ dev->ibd_hdev->ibh_cmid == NULL || /* listener is dead */
+ *kiblnd_tunables.kib_dev_failover > 1) /* debugging */
+ return 1;
+
+ /* XXX: it's UGLY, but I don't have better way to find
+ * ib-bonding HCA failover because:
+ *
+ * a. no reliable CM event for HCA failover...
+ * b. no OFED API to get ib_device for current net_device...
+ *
+ * We have only two choices at this point:
+ *
+ * a. rdma_bind_addr(), it will conflict with listener cmid
+ * b. rdma_resolve_addr() to zero addr */
+ cmid = kiblnd_rdma_create_id(kiblnd_dummy_callback, dev, RDMA_PS_TCP,
+ IB_QPT_RC);
+ if (IS_ERR(cmid)) {
+ rc = PTR_ERR(cmid);
+ CERROR("Failed to create cmid for failover: %d\n", rc);
+ return rc;
+ }
+
+ memset(&srcaddr, 0, sizeof(srcaddr));
+ srcaddr.sin_family = AF_INET;
+ srcaddr.sin_addr.s_addr = (__force u32)htonl(dev->ibd_ifip);
+
+ memset(&dstaddr, 0, sizeof(dstaddr));
+ dstaddr.sin_family = AF_INET;
+ rc = rdma_resolve_addr(cmid, (struct sockaddr *)&srcaddr,
+ (struct sockaddr *)&dstaddr, 1);
+ if (rc != 0 || cmid->device == NULL) {
+ CERROR("Failed to bind %s:%pI4h to device(%p): %d\n",
+ dev->ibd_ifname, &dev->ibd_ifip,
+ cmid->device, rc);
+ rdma_destroy_id(cmid);
+ return rc;
+ }
+
+ if (dev->ibd_hdev->ibh_ibdev == cmid->device) {
+ /* don't need device failover */
+ rdma_destroy_id(cmid);
+ return 0;
+ }
+
+ return 1;
+}
+
+int kiblnd_dev_failover(kib_dev_t *dev)
+{
+ LIST_HEAD(zombie_tpo);
+ LIST_HEAD(zombie_ppo);
+ LIST_HEAD(zombie_fpo);
+ struct rdma_cm_id *cmid = NULL;
+ kib_hca_dev_t *hdev = NULL;
+ kib_hca_dev_t *old;
+ struct ib_pd *pd;
+ kib_net_t *net;
+ struct sockaddr_in addr;
+ unsigned long flags;
+ int rc = 0;
+ int i;
+
+ LASSERT(*kiblnd_tunables.kib_dev_failover > 1 ||
+ dev->ibd_can_failover ||
+ dev->ibd_hdev == NULL);
+
+ rc = kiblnd_dev_need_failover(dev);
+ if (rc <= 0)
+ goto out;
+
+ if (dev->ibd_hdev != NULL &&
+ dev->ibd_hdev->ibh_cmid != NULL) {
+ /* XXX it's not good to close old listener at here,
+ * because we can fail to create new listener.
+ * But we have to close it now, otherwise rdma_bind_addr
+ * will return EADDRINUSE... How crap! */
+ write_lock_irqsave(&kiblnd_data.kib_global_lock, flags);
+
+ cmid = dev->ibd_hdev->ibh_cmid;
+ /* make next schedule of kiblnd_dev_need_failover()
+ * return 1 for me */
+ dev->ibd_hdev->ibh_cmid = NULL;
+ write_unlock_irqrestore(&kiblnd_data.kib_global_lock, flags);
+
+ rdma_destroy_id(cmid);
+ }
+
+ cmid = kiblnd_rdma_create_id(kiblnd_cm_callback, dev, RDMA_PS_TCP,
+ IB_QPT_RC);
+ if (IS_ERR(cmid)) {
+ rc = PTR_ERR(cmid);
+ CERROR("Failed to create cmid for failover: %d\n", rc);
+ goto out;
+ }
+
+ memset(&addr, 0, sizeof(addr));
+ addr.sin_family = AF_INET;
+ addr.sin_addr.s_addr = (__force u32)htonl(dev->ibd_ifip);
+ addr.sin_port = htons(*kiblnd_tunables.kib_service);
+
+ /* Bind to failover device or port */
+ rc = rdma_bind_addr(cmid, (struct sockaddr *)&addr);
+ if (rc != 0 || cmid->device == NULL) {
+ CERROR("Failed to bind %s:%pI4h to device(%p): %d\n",
+ dev->ibd_ifname, &dev->ibd_ifip,
+ cmid->device, rc);
+ rdma_destroy_id(cmid);
+ goto out;
+ }
+
+ LIBCFS_ALLOC(hdev, sizeof(*hdev));
+ if (hdev == NULL) {
+ CERROR("Failed to allocate kib_hca_dev\n");
+ rdma_destroy_id(cmid);
+ rc = -ENOMEM;
+ goto out;
+ }
+
+ atomic_set(&hdev->ibh_ref, 1);
+ hdev->ibh_dev = dev;
+ hdev->ibh_cmid = cmid;
+ hdev->ibh_ibdev = cmid->device;
+
+ pd = ib_alloc_pd(cmid->device);
+ if (IS_ERR(pd)) {
+ rc = PTR_ERR(pd);
+ CERROR("Can't allocate PD: %d\n", rc);
+ goto out;
+ }
+
+ hdev->ibh_pd = pd;
+
+ rc = rdma_listen(cmid, 0);
+ if (rc != 0) {
+ CERROR("Can't start new listener: %d\n", rc);
+ goto out;
+ }
+
+ rc = kiblnd_hdev_setup_mrs(hdev);
+ if (rc != 0) {
+ CERROR("Can't setup device: %d\n", rc);
+ goto out;
+ }
+
+ write_lock_irqsave(&kiblnd_data.kib_global_lock, flags);
+
+ old = dev->ibd_hdev;
+ dev->ibd_hdev = hdev; /* take over the refcount */
+ hdev = old;
+
+ list_for_each_entry(net, &dev->ibd_nets, ibn_list) {
+ cfs_cpt_for_each(i, lnet_cpt_table()) {
+ kiblnd_fail_poolset(&net->ibn_tx_ps[i]->tps_poolset,
+ &zombie_tpo);
+
+ if (net->ibn_fmr_ps != NULL) {
+ kiblnd_fail_fmr_poolset(net->ibn_fmr_ps[i],
+ &zombie_fpo);
+
+ } else if (net->ibn_pmr_ps != NULL) {
+ kiblnd_fail_poolset(&net->ibn_pmr_ps[i]->
+ pps_poolset, &zombie_ppo);
+ }
+ }
+ }
+
+ write_unlock_irqrestore(&kiblnd_data.kib_global_lock, flags);
+ out:
+ if (!list_empty(&zombie_tpo))
+ kiblnd_destroy_pool_list(&zombie_tpo);
+ if (!list_empty(&zombie_ppo))
+ kiblnd_destroy_pool_list(&zombie_ppo);
+ if (!list_empty(&zombie_fpo))
+ kiblnd_destroy_fmr_pool_list(&zombie_fpo);
+ if (hdev != NULL)
+ kiblnd_hdev_decref(hdev);
+
+ if (rc != 0)
+ dev->ibd_failed_failover++;
+ else
+ dev->ibd_failed_failover = 0;
+
+ return rc;
+}
+
+void kiblnd_destroy_dev(kib_dev_t *dev)
+{
+ LASSERT(dev->ibd_nnets == 0);
+ LASSERT(list_empty(&dev->ibd_nets));
+
+ list_del(&dev->ibd_fail_list);
+ list_del(&dev->ibd_list);
+
+ if (dev->ibd_hdev != NULL)
+ kiblnd_hdev_decref(dev->ibd_hdev);
+
+ LIBCFS_FREE(dev, sizeof(*dev));
+}
+
+static kib_dev_t *kiblnd_create_dev(char *ifname)
+{
+ struct net_device *netdev;
+ kib_dev_t *dev;
+ __u32 netmask;
+ __u32 ip;
+ int up;
+ int rc;
+
+ rc = libcfs_ipif_query(ifname, &up, &ip, &netmask);
+ if (rc != 0) {
+ CERROR("Can't query IPoIB interface %s: %d\n",
+ ifname, rc);
+ return NULL;
+ }
+
+ if (!up) {
+ CERROR("Can't query IPoIB interface %s: it's down\n", ifname);
+ return NULL;
+ }
+
+ LIBCFS_ALLOC(dev, sizeof(*dev));
+ if (dev == NULL)
+ return NULL;
+
+ netdev = dev_get_by_name(&init_net, ifname);
+ if (netdev == NULL) {
+ dev->ibd_can_failover = 0;
+ } else {
+ dev->ibd_can_failover = !!(netdev->flags & IFF_MASTER);
+ dev_put(netdev);
+ }
+
+ INIT_LIST_HEAD(&dev->ibd_nets);
+ INIT_LIST_HEAD(&dev->ibd_list); /* not yet in kib_devs */
+ INIT_LIST_HEAD(&dev->ibd_fail_list);
+ dev->ibd_ifip = ip;
+ strcpy(&dev->ibd_ifname[0], ifname);
+
+ /* initialize the device */
+ rc = kiblnd_dev_failover(dev);
+ if (rc != 0) {
+ CERROR("Can't initialize device: %d\n", rc);
+ LIBCFS_FREE(dev, sizeof(*dev));
+ return NULL;
+ }
+
+ list_add_tail(&dev->ibd_list,
+ &kiblnd_data.kib_devs);
+ return dev;
+}
+
+static void kiblnd_base_shutdown(void)
+{
+ struct kib_sched_info *sched;
+ int i;
+
+ LASSERT(list_empty(&kiblnd_data.kib_devs));
+
+ CDEBUG(D_MALLOC, "before LND base cleanup: kmem %d\n",
+ atomic_read(&libcfs_kmemory));
+
+ switch (kiblnd_data.kib_init) {
+ default:
+ LBUG();
+
+ case IBLND_INIT_ALL:
+ case IBLND_INIT_DATA:
+ LASSERT(kiblnd_data.kib_peers != NULL);
+ for (i = 0; i < kiblnd_data.kib_peer_hash_size; i++)
+ LASSERT(list_empty(&kiblnd_data.kib_peers[i]));
+ LASSERT(list_empty(&kiblnd_data.kib_connd_zombies));
+ LASSERT(list_empty(&kiblnd_data.kib_connd_conns));
+
+ /* flag threads to terminate; wake and wait for them to die */
+ kiblnd_data.kib_shutdown = 1;
+
+ /* NB: we really want to stop scheduler threads net by net
+ * instead of the whole module, this should be improved
+ * with dynamic configuration LNet */
+ cfs_percpt_for_each(sched, i, kiblnd_data.kib_scheds)
+ wake_up_all(&sched->ibs_waitq);
+
+ wake_up_all(&kiblnd_data.kib_connd_waitq);
+ wake_up_all(&kiblnd_data.kib_failover_waitq);
+
+ i = 2;
+ while (atomic_read(&kiblnd_data.kib_nthreads) != 0) {
+ i++;
+ /* power of 2 ? */
+ CDEBUG(((i & (-i)) == i) ? D_WARNING : D_NET,
+ "Waiting for %d threads to terminate\n",
+ atomic_read(&kiblnd_data.kib_nthreads));
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ schedule_timeout(cfs_time_seconds(1));
+ }
+
+ /* fall through */
+
+ case IBLND_INIT_NOTHING:
+ break;
+ }
+
+ if (kiblnd_data.kib_peers != NULL) {
+ LIBCFS_FREE(kiblnd_data.kib_peers,
+ sizeof(struct list_head) *
+ kiblnd_data.kib_peer_hash_size);
+ }
+
+ if (kiblnd_data.kib_scheds != NULL)
+ cfs_percpt_free(kiblnd_data.kib_scheds);
+
+ CDEBUG(D_MALLOC, "after LND base cleanup: kmem %d\n",
+ atomic_read(&libcfs_kmemory));
+
+ kiblnd_data.kib_init = IBLND_INIT_NOTHING;
+ module_put(THIS_MODULE);
+}
+
+void kiblnd_shutdown(lnet_ni_t *ni)
+{
+ kib_net_t *net = ni->ni_data;
+ rwlock_t *g_lock = &kiblnd_data.kib_global_lock;
+ int i;
+ unsigned long flags;
+
+ LASSERT(kiblnd_data.kib_init == IBLND_INIT_ALL);
+
+ if (net == NULL)
+ goto out;
+
+ CDEBUG(D_MALLOC, "before LND net cleanup: kmem %d\n",
+ atomic_read(&libcfs_kmemory));
+
+ write_lock_irqsave(g_lock, flags);
+ net->ibn_shutdown = 1;
+ write_unlock_irqrestore(g_lock, flags);
+
+ switch (net->ibn_init) {
+ default:
+ LBUG();
+
+ case IBLND_INIT_ALL:
+ /* nuke all existing peers within this net */
+ kiblnd_del_peer(ni, LNET_NID_ANY);
+
+ /* Wait for all peer state to clean up */
+ i = 2;
+ while (atomic_read(&net->ibn_npeers) != 0) {
+ i++;
+ CDEBUG(((i & (-i)) == i) ? D_WARNING : D_NET, /* 2**n? */
+ "%s: waiting for %d peers to disconnect\n",
+ libcfs_nid2str(ni->ni_nid),
+ atomic_read(&net->ibn_npeers));
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ schedule_timeout(cfs_time_seconds(1));
+ }
+
+ kiblnd_net_fini_pools(net);
+
+ write_lock_irqsave(g_lock, flags);
+ LASSERT(net->ibn_dev->ibd_nnets > 0);
+ net->ibn_dev->ibd_nnets--;
+ list_del(&net->ibn_list);
+ write_unlock_irqrestore(g_lock, flags);
+
+ /* fall through */
+
+ case IBLND_INIT_NOTHING:
+ LASSERT(atomic_read(&net->ibn_nconns) == 0);
+
+ if (net->ibn_dev != NULL &&
+ net->ibn_dev->ibd_nnets == 0)
+ kiblnd_destroy_dev(net->ibn_dev);
+
+ break;
+ }
+
+ CDEBUG(D_MALLOC, "after LND net cleanup: kmem %d\n",
+ atomic_read(&libcfs_kmemory));
+
+ net->ibn_init = IBLND_INIT_NOTHING;
+ ni->ni_data = NULL;
+
+ LIBCFS_FREE(net, sizeof(*net));
+
+out:
+ if (list_empty(&kiblnd_data.kib_devs))
+ kiblnd_base_shutdown();
+}
+
+static int kiblnd_base_startup(void)
+{
+ struct kib_sched_info *sched;
+ int rc;
+ int i;
+
+ LASSERT(kiblnd_data.kib_init == IBLND_INIT_NOTHING);
+
+ try_module_get(THIS_MODULE);
+ /* zero pointers, flags etc */
+ memset(&kiblnd_data, 0, sizeof(kiblnd_data));
+
+ rwlock_init(&kiblnd_data.kib_global_lock);
+
+ INIT_LIST_HEAD(&kiblnd_data.kib_devs);
+ INIT_LIST_HEAD(&kiblnd_data.kib_failed_devs);
+
+ kiblnd_data.kib_peer_hash_size = IBLND_PEER_HASH_SIZE;
+ LIBCFS_ALLOC(kiblnd_data.kib_peers,
+ sizeof(struct list_head) *
+ kiblnd_data.kib_peer_hash_size);
+ if (kiblnd_data.kib_peers == NULL)
+ goto failed;
+ for (i = 0; i < kiblnd_data.kib_peer_hash_size; i++)
+ INIT_LIST_HEAD(&kiblnd_data.kib_peers[i]);
+
+ spin_lock_init(&kiblnd_data.kib_connd_lock);
+ INIT_LIST_HEAD(&kiblnd_data.kib_connd_conns);
+ INIT_LIST_HEAD(&kiblnd_data.kib_connd_zombies);
+ init_waitqueue_head(&kiblnd_data.kib_connd_waitq);
+ init_waitqueue_head(&kiblnd_data.kib_failover_waitq);
+
+ kiblnd_data.kib_scheds = cfs_percpt_alloc(lnet_cpt_table(),
+ sizeof(*sched));
+ if (kiblnd_data.kib_scheds == NULL)
+ goto failed;
+
+ cfs_percpt_for_each(sched, i, kiblnd_data.kib_scheds) {
+ int nthrs;
+
+ spin_lock_init(&sched->ibs_lock);
+ INIT_LIST_HEAD(&sched->ibs_conns);
+ init_waitqueue_head(&sched->ibs_waitq);
+
+ nthrs = cfs_cpt_weight(lnet_cpt_table(), i);
+ if (*kiblnd_tunables.kib_nscheds > 0) {
+ nthrs = min(nthrs, *kiblnd_tunables.kib_nscheds);
+ } else {
+ /* max to half of CPUs, another half is reserved for
+ * upper layer modules */
+ nthrs = min(max(IBLND_N_SCHED, nthrs >> 1), nthrs);
+ }
+
+ sched->ibs_nthreads_max = nthrs;
+ sched->ibs_cpt = i;
+ }
+
+ kiblnd_data.kib_error_qpa.qp_state = IB_QPS_ERR;
+
+ /* lists/ptrs/locks initialised */
+ kiblnd_data.kib_init = IBLND_INIT_DATA;
+ /*****************************************************/
+
+ rc = kiblnd_thread_start(kiblnd_connd, NULL, "kiblnd_connd");
+ if (rc != 0) {
+ CERROR("Can't spawn o2iblnd connd: %d\n", rc);
+ goto failed;
+ }
+
+ if (*kiblnd_tunables.kib_dev_failover != 0)
+ rc = kiblnd_thread_start(kiblnd_failover_thread, NULL,
+ "kiblnd_failover");
+
+ if (rc != 0) {
+ CERROR("Can't spawn o2iblnd failover thread: %d\n", rc);
+ goto failed;
+ }
+
+ /* flag everything initialised */
+ kiblnd_data.kib_init = IBLND_INIT_ALL;
+ /*****************************************************/
+
+ return 0;
+
+ failed:
+ kiblnd_base_shutdown();
+ return -ENETDOWN;
+}
+
+static int kiblnd_start_schedulers(struct kib_sched_info *sched)
+{
+ int rc = 0;
+ int nthrs;
+ int i;
+
+ if (sched->ibs_nthreads == 0) {
+ if (*kiblnd_tunables.kib_nscheds > 0) {
+ nthrs = sched->ibs_nthreads_max;
+ } else {
+ nthrs = cfs_cpt_weight(lnet_cpt_table(),
+ sched->ibs_cpt);
+ nthrs = min(max(IBLND_N_SCHED, nthrs >> 1), nthrs);
+ nthrs = min(IBLND_N_SCHED_HIGH, nthrs);
+ }
+ } else {
+ LASSERT(sched->ibs_nthreads <= sched->ibs_nthreads_max);
+ /* increase one thread if there is new interface */
+ nthrs = sched->ibs_nthreads < sched->ibs_nthreads_max;
+ }
+
+ for (i = 0; i < nthrs; i++) {
+ long id;
+ char name[20];
+
+ id = KIB_THREAD_ID(sched->ibs_cpt, sched->ibs_nthreads + i);
+ snprintf(name, sizeof(name), "kiblnd_sd_%02ld_%02ld",
+ KIB_THREAD_CPT(id), KIB_THREAD_TID(id));
+ rc = kiblnd_thread_start(kiblnd_scheduler, (void *)id, name);
+ if (rc == 0)
+ continue;
+
+ CERROR("Can't spawn thread %d for scheduler[%d]: %d\n",
+ sched->ibs_cpt, sched->ibs_nthreads + i, rc);
+ break;
+ }
+
+ sched->ibs_nthreads += i;
+ return rc;
+}
+
+static int kiblnd_dev_start_threads(kib_dev_t *dev, int newdev, __u32 *cpts,
+ int ncpts)
+{
+ int cpt;
+ int rc;
+ int i;
+
+ for (i = 0; i < ncpts; i++) {
+ struct kib_sched_info *sched;
+
+ cpt = (cpts == NULL) ? i : cpts[i];
+ sched = kiblnd_data.kib_scheds[cpt];
+
+ if (!newdev && sched->ibs_nthreads > 0)
+ continue;
+
+ rc = kiblnd_start_schedulers(kiblnd_data.kib_scheds[cpt]);
+ if (rc != 0) {
+ CERROR("Failed to start scheduler threads for %s\n",
+ dev->ibd_ifname);
+ return rc;
+ }
+ }
+ return 0;
+}
+
+static kib_dev_t *kiblnd_dev_search(char *ifname)
+{
+ kib_dev_t *alias = NULL;
+ kib_dev_t *dev;
+ char *colon;
+ char *colon2;
+
+ colon = strchr(ifname, ':');
+ list_for_each_entry(dev, &kiblnd_data.kib_devs, ibd_list) {
+ if (strcmp(&dev->ibd_ifname[0], ifname) == 0)
+ return dev;
+
+ if (alias != NULL)
+ continue;
+
+ colon2 = strchr(dev->ibd_ifname, ':');
+ if (colon != NULL)
+ *colon = 0;
+ if (colon2 != NULL)
+ *colon2 = 0;
+
+ if (strcmp(&dev->ibd_ifname[0], ifname) == 0)
+ alias = dev;
+
+ if (colon != NULL)
+ *colon = ':';
+ if (colon2 != NULL)
+ *colon2 = ':';
+ }
+ return alias;
+}
+
+int kiblnd_startup(lnet_ni_t *ni)
+{
+ char *ifname;
+ kib_dev_t *ibdev = NULL;
+ kib_net_t *net;
+ struct timeval tv;
+ unsigned long flags;
+ int rc;
+ int newdev;
+
+ LASSERT(ni->ni_lnd == &the_o2iblnd);
+
+ if (kiblnd_data.kib_init == IBLND_INIT_NOTHING) {
+ rc = kiblnd_base_startup();
+ if (rc != 0)
+ return rc;
+ }
+
+ LIBCFS_ALLOC(net, sizeof(*net));
+ ni->ni_data = net;
+ if (net == NULL)
+ goto net_failed;
+
+ do_gettimeofday(&tv);
+ net->ibn_incarnation = (((__u64)tv.tv_sec) * 1000000) + tv.tv_usec;
+
+ ni->ni_peertimeout = *kiblnd_tunables.kib_peertimeout;
+ ni->ni_maxtxcredits = *kiblnd_tunables.kib_credits;
+ ni->ni_peertxcredits = *kiblnd_tunables.kib_peertxcredits;
+ ni->ni_peerrtrcredits = *kiblnd_tunables.kib_peerrtrcredits;
+
+ if (ni->ni_interfaces[0] != NULL) {
+ /* Use the IPoIB interface specified in 'networks=' */
+
+ CLASSERT(LNET_MAX_INTERFACES > 1);
+ if (ni->ni_interfaces[1] != NULL) {
+ CERROR("Multiple interfaces not supported\n");
+ goto failed;
+ }
+
+ ifname = ni->ni_interfaces[0];
+ } else {
+ ifname = *kiblnd_tunables.kib_default_ipif;
+ }
+
+ if (strlen(ifname) >= sizeof(ibdev->ibd_ifname)) {
+ CERROR("IPoIB interface name too long: %s\n", ifname);
+ goto failed;
+ }
+
+ ibdev = kiblnd_dev_search(ifname);
+
+ newdev = ibdev == NULL;
+ /* hmm...create kib_dev even for alias */
+ if (ibdev == NULL || strcmp(&ibdev->ibd_ifname[0], ifname) != 0)
+ ibdev = kiblnd_create_dev(ifname);
+
+ if (ibdev == NULL)
+ goto failed;
+
+ net->ibn_dev = ibdev;
+ ni->ni_nid = LNET_MKNID(LNET_NIDNET(ni->ni_nid), ibdev->ibd_ifip);
+
+ rc = kiblnd_dev_start_threads(ibdev, newdev,
+ ni->ni_cpts, ni->ni_ncpts);
+ if (rc != 0)
+ goto failed;
+
+ rc = kiblnd_net_init_pools(net, ni->ni_cpts, ni->ni_ncpts);
+ if (rc != 0) {
+ CERROR("Failed to initialize NI pools: %d\n", rc);
+ goto failed;
+ }
+
+ write_lock_irqsave(&kiblnd_data.kib_global_lock, flags);
+ ibdev->ibd_nnets++;
+ list_add_tail(&net->ibn_list, &ibdev->ibd_nets);
+ write_unlock_irqrestore(&kiblnd_data.kib_global_lock, flags);
+
+ net->ibn_init = IBLND_INIT_ALL;
+
+ return 0;
+
+failed:
+ if (net->ibn_dev == NULL && ibdev != NULL)
+ kiblnd_destroy_dev(ibdev);
+
+net_failed:
+ kiblnd_shutdown(ni);
+
+ CDEBUG(D_NET, "kiblnd_startup failed\n");
+ return -ENETDOWN;
+}
+
+static void __exit kiblnd_module_fini(void)
+{
+ lnet_unregister_lnd(&the_o2iblnd);
+}
+
+static int __init kiblnd_module_init(void)
+{
+ int rc;
+
+ CLASSERT(sizeof(kib_msg_t) <= IBLND_MSG_SIZE);
+ CLASSERT(offsetof(kib_msg_t,
+ ibm_u.get.ibgm_rd.rd_frags[IBLND_MAX_RDMA_FRAGS])
+ <= IBLND_MSG_SIZE);
+ CLASSERT(offsetof(kib_msg_t,
+ ibm_u.putack.ibpam_rd.rd_frags[IBLND_MAX_RDMA_FRAGS])
+ <= IBLND_MSG_SIZE);
+
+ rc = kiblnd_tunables_init();
+ if (rc != 0)
+ return rc;
+
+ lnet_register_lnd(&the_o2iblnd);
+
+ return 0;
+}
+
+MODULE_AUTHOR("Sun Microsystems, Inc. <http://www.lustre.org/>");
+MODULE_DESCRIPTION("Kernel OpenIB gen2 LND v2.00");
+MODULE_LICENSE("GPL");
+
+module_init(kiblnd_module_init);
+module_exit(kiblnd_module_fini);
diff --git a/drivers/staging/lustre/lnet/klnds/o2iblnd/o2iblnd.h b/drivers/staging/lustre/lnet/klnds/o2iblnd/o2iblnd.h
new file mode 100644
index 000000000..cd664d025
--- /dev/null
+++ b/drivers/staging/lustre/lnet/klnds/o2iblnd/o2iblnd.h
@@ -0,0 +1,1030 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2011, 2012, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ *
+ * lnet/klnds/o2iblnd/o2iblnd.h
+ *
+ * Author: Eric Barton <eric@bartonsoftware.com>
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/string.h>
+#include <linux/stat.h>
+#include <linux/errno.h>
+#include <linux/unistd.h>
+#include <linux/uio.h>
+#include <linux/uaccess.h>
+
+#include <asm/io.h>
+
+#include <linux/fs.h>
+#include <linux/file.h>
+#include <linux/list.h>
+#include <linux/kmod.h>
+#include <linux/sysctl.h>
+#include <linux/pci.h>
+
+#include <net/sock.h>
+#include <linux/in.h>
+
+#define DEBUG_SUBSYSTEM S_LND
+
+#include "../../../include/linux/libcfs/libcfs.h"
+#include "../../../include/linux/lnet/lnet.h"
+#include "../../../include/linux/lnet/lib-lnet.h"
+#include "../../../include/linux/lnet/lnet-sysctl.h"
+
+#include <rdma/rdma_cm.h>
+#include <rdma/ib_cm.h>
+#include <rdma/ib_verbs.h>
+#include <rdma/ib_fmr_pool.h>
+
+#define IBLND_PEER_HASH_SIZE 101 /* # peer lists */
+/* # scheduler loops before reschedule */
+#define IBLND_RESCHED 100
+
+#define IBLND_N_SCHED 2
+#define IBLND_N_SCHED_HIGH 4
+
+typedef struct {
+ int *kib_dev_failover; /* HCA failover */
+ unsigned int *kib_service; /* IB service number */
+ int *kib_min_reconnect_interval; /* first failed connection retry... */
+ int *kib_max_reconnect_interval; /* ...exponentially increasing to this */
+ int *kib_cksum; /* checksum kib_msg_t? */
+ int *kib_timeout; /* comms timeout (seconds) */
+ int *kib_keepalive; /* keepalive timeout (seconds) */
+ int *kib_ntx; /* # tx descs */
+ int *kib_credits; /* # concurrent sends */
+ int *kib_peertxcredits; /* # concurrent sends to 1 peer */
+ int *kib_peerrtrcredits; /* # per-peer router buffer credits */
+ int *kib_peercredits_hiw; /* # when eagerly to return credits */
+ int *kib_peertimeout; /* seconds to consider peer dead */
+ char **kib_default_ipif; /* default IPoIB interface */
+ int *kib_retry_count;
+ int *kib_rnr_retry_count;
+ int *kib_concurrent_sends; /* send work queue sizing */
+ int *kib_ib_mtu; /* IB MTU */
+ int *kib_map_on_demand; /* map-on-demand if RD has more fragments
+ * than this value, 0 disable map-on-demand */
+ int *kib_pmr_pool_size; /* # physical MR in pool */
+ int *kib_fmr_pool_size; /* # FMRs in pool */
+ int *kib_fmr_flush_trigger; /* When to trigger FMR flush */
+ int *kib_fmr_cache; /* enable FMR pool cache? */
+ int *kib_require_priv_port;/* accept only privileged ports */
+ int *kib_use_priv_port; /* use privileged port for active connect */
+ /* # threads on each CPT */
+ int *kib_nscheds;
+} kib_tunables_t;
+
+extern kib_tunables_t kiblnd_tunables;
+
+#define IBLND_MSG_QUEUE_SIZE_V1 8 /* V1 only : # messages/RDMAs in-flight */
+#define IBLND_CREDIT_HIGHWATER_V1 7 /* V1 only : when eagerly to return credits */
+
+#define IBLND_CREDITS_DEFAULT 8 /* default # of peer credits */
+#define IBLND_CREDITS_MAX ((typeof(((kib_msg_t*) 0)->ibm_credits)) - 1) /* Max # of peer credits */
+
+#define IBLND_MSG_QUEUE_SIZE(v) ((v) == IBLND_MSG_VERSION_1 ? \
+ IBLND_MSG_QUEUE_SIZE_V1 : \
+ *kiblnd_tunables.kib_peertxcredits) /* # messages/RDMAs in-flight */
+#define IBLND_CREDITS_HIGHWATER(v) ((v) == IBLND_MSG_VERSION_1 ? \
+ IBLND_CREDIT_HIGHWATER_V1 : \
+ *kiblnd_tunables.kib_peercredits_hiw) /* when eagerly to return credits */
+
+#define kiblnd_rdma_create_id(cb, dev, ps, qpt) rdma_create_id(cb, dev, ps, qpt)
+
+static inline int
+kiblnd_concurrent_sends_v1(void)
+{
+ if (*kiblnd_tunables.kib_concurrent_sends > IBLND_MSG_QUEUE_SIZE_V1 * 2)
+ return IBLND_MSG_QUEUE_SIZE_V1 * 2;
+
+ if (*kiblnd_tunables.kib_concurrent_sends < IBLND_MSG_QUEUE_SIZE_V1 / 2)
+ return IBLND_MSG_QUEUE_SIZE_V1 / 2;
+
+ return *kiblnd_tunables.kib_concurrent_sends;
+}
+
+#define IBLND_CONCURRENT_SENDS(v) ((v) == IBLND_MSG_VERSION_1 ? \
+ kiblnd_concurrent_sends_v1() : \
+ *kiblnd_tunables.kib_concurrent_sends)
+/* 2 OOB shall suffice for 1 keepalive and 1 returning credits */
+#define IBLND_OOB_CAPABLE(v) ((v) != IBLND_MSG_VERSION_1)
+#define IBLND_OOB_MSGS(v) (IBLND_OOB_CAPABLE(v) ? 2 : 0)
+
+#define IBLND_MSG_SIZE (4<<10) /* max size of queued messages (inc hdr) */
+#define IBLND_MAX_RDMA_FRAGS LNET_MAX_IOV /* max # of fragments supported */
+#define IBLND_CFG_RDMA_FRAGS (*kiblnd_tunables.kib_map_on_demand != 0 ? \
+ *kiblnd_tunables.kib_map_on_demand : \
+ IBLND_MAX_RDMA_FRAGS) /* max # of fragments configured by user */
+#define IBLND_RDMA_FRAGS(v) ((v) == IBLND_MSG_VERSION_1 ? \
+ IBLND_MAX_RDMA_FRAGS : IBLND_CFG_RDMA_FRAGS)
+
+/************************/
+/* derived constants... */
+/* Pools (shared by connections on each CPT) */
+/* These pools can grow at runtime, so don't need give a very large value */
+#define IBLND_TX_POOL 256
+#define IBLND_PMR_POOL 256
+#define IBLND_FMR_POOL 256
+#define IBLND_FMR_POOL_FLUSH 192
+
+/* TX messages (shared by all connections) */
+#define IBLND_TX_MSGS() (*kiblnd_tunables.kib_ntx)
+
+/* RX messages (per connection) */
+#define IBLND_RX_MSGS(v) (IBLND_MSG_QUEUE_SIZE(v) * 2 + IBLND_OOB_MSGS(v))
+#define IBLND_RX_MSG_BYTES(v) (IBLND_RX_MSGS(v) * IBLND_MSG_SIZE)
+#define IBLND_RX_MSG_PAGES(v) ((IBLND_RX_MSG_BYTES(v) + PAGE_SIZE - 1) / PAGE_SIZE)
+
+/* WRs and CQEs (per connection) */
+#define IBLND_RECV_WRS(v) IBLND_RX_MSGS(v)
+#define IBLND_SEND_WRS(v) ((IBLND_RDMA_FRAGS(v) + 1) * IBLND_CONCURRENT_SENDS(v))
+#define IBLND_CQ_ENTRIES(v) (IBLND_RECV_WRS(v) + IBLND_SEND_WRS(v))
+
+struct kib_hca_dev;
+
+/* o2iblnd can run over aliased interface */
+#ifdef IFALIASZ
+#define KIB_IFNAME_SIZE IFALIASZ
+#else
+#define KIB_IFNAME_SIZE 256
+#endif
+
+typedef struct {
+ struct list_head ibd_list; /* chain on kib_devs */
+ struct list_head ibd_fail_list; /* chain on kib_failed_devs */
+ __u32 ibd_ifip; /* IPoIB interface IP */
+ /** IPoIB interface name */
+ char ibd_ifname[KIB_IFNAME_SIZE];
+ int ibd_nnets; /* # nets extant */
+
+ unsigned long ibd_next_failover;
+ int ibd_failed_failover; /* # failover failures */
+ unsigned int ibd_failover; /* failover in progress */
+ unsigned int ibd_can_failover; /* IPoIB interface is a bonding master */
+ struct list_head ibd_nets;
+ struct kib_hca_dev *ibd_hdev;
+} kib_dev_t;
+
+typedef struct kib_hca_dev {
+ struct rdma_cm_id *ibh_cmid; /* listener cmid */
+ struct ib_device *ibh_ibdev; /* IB device */
+ int ibh_page_shift; /* page shift of current HCA */
+ int ibh_page_size; /* page size of current HCA */
+ __u64 ibh_page_mask; /* page mask of current HCA */
+ int ibh_mr_shift; /* bits shift of max MR size */
+ __u64 ibh_mr_size; /* size of MR */
+ int ibh_nmrs; /* # of global MRs */
+ struct ib_mr **ibh_mrs; /* global MR */
+ struct ib_pd *ibh_pd; /* PD */
+ kib_dev_t *ibh_dev; /* owner */
+ atomic_t ibh_ref; /* refcount */
+} kib_hca_dev_t;
+
+/** # of seconds to keep pool alive */
+#define IBLND_POOL_DEADLINE 300
+/** # of seconds to retry if allocation failed */
+#define IBLND_POOL_RETRY 1
+
+typedef struct {
+ int ibp_npages; /* # pages */
+ struct page *ibp_pages[0]; /* page array */
+} kib_pages_t;
+
+struct kib_pmr_pool;
+
+typedef struct {
+ struct list_head pmr_list; /* chain node */
+ struct ib_phys_buf *pmr_ipb; /* physical buffer */
+ struct ib_mr *pmr_mr; /* IB MR */
+ struct kib_pmr_pool *pmr_pool; /* owner of this MR */
+ __u64 pmr_iova; /* Virtual I/O address */
+ int pmr_refcount; /* reference count */
+} kib_phys_mr_t;
+
+struct kib_pool;
+struct kib_poolset;
+
+typedef int (*kib_ps_pool_create_t)(struct kib_poolset *ps,
+ int inc, struct kib_pool **pp_po);
+typedef void (*kib_ps_pool_destroy_t)(struct kib_pool *po);
+typedef void (*kib_ps_node_init_t)(struct kib_pool *po, struct list_head *node);
+typedef void (*kib_ps_node_fini_t)(struct kib_pool *po, struct list_head *node);
+
+struct kib_net;
+
+#define IBLND_POOL_NAME_LEN 32
+
+typedef struct kib_poolset {
+ spinlock_t ps_lock; /* serialize */
+ struct kib_net *ps_net; /* network it belongs to */
+ char ps_name[IBLND_POOL_NAME_LEN]; /* pool set name */
+ struct list_head ps_pool_list; /* list of pools */
+ struct list_head ps_failed_pool_list; /* failed pool list */
+ unsigned long ps_next_retry; /* time stamp for retry if failed to allocate */
+ int ps_increasing; /* is allocating new pool */
+ int ps_pool_size; /* new pool size */
+ int ps_cpt; /* CPT id */
+
+ kib_ps_pool_create_t ps_pool_create; /* create a new pool */
+ kib_ps_pool_destroy_t ps_pool_destroy; /* destroy a pool */
+ kib_ps_node_init_t ps_node_init; /* initialize new allocated node */
+ kib_ps_node_fini_t ps_node_fini; /* finalize node */
+} kib_poolset_t;
+
+typedef struct kib_pool {
+ struct list_head po_list; /* chain on pool list */
+ struct list_head po_free_list; /* pre-allocated node */
+ kib_poolset_t *po_owner; /* pool_set of this pool */
+ unsigned long po_deadline; /* deadline of this pool */
+ int po_allocated; /* # of elements in use */
+ int po_failed; /* pool is created on failed HCA */
+ int po_size; /* # of pre-allocated elements */
+} kib_pool_t;
+
+typedef struct {
+ kib_poolset_t tps_poolset; /* pool-set */
+ __u64 tps_next_tx_cookie; /* cookie of TX */
+} kib_tx_poolset_t;
+
+typedef struct {
+ kib_pool_t tpo_pool; /* pool */
+ struct kib_hca_dev *tpo_hdev; /* device for this pool */
+ struct kib_tx *tpo_tx_descs; /* all the tx descriptors */
+ kib_pages_t *tpo_tx_pages; /* premapped tx msg pages */
+} kib_tx_pool_t;
+
+typedef struct {
+ kib_poolset_t pps_poolset; /* pool-set */
+} kib_pmr_poolset_t;
+
+typedef struct kib_pmr_pool {
+ struct kib_hca_dev *ppo_hdev; /* device for this pool */
+ kib_pool_t ppo_pool; /* pool */
+} kib_pmr_pool_t;
+
+typedef struct {
+ spinlock_t fps_lock; /* serialize */
+ struct kib_net *fps_net; /* IB network */
+ struct list_head fps_pool_list; /* FMR pool list */
+ struct list_head fps_failed_pool_list; /* FMR pool list */
+ __u64 fps_version; /* validity stamp */
+ int fps_cpt; /* CPT id */
+ int fps_pool_size;
+ int fps_flush_trigger;
+ /* is allocating new pool */
+ int fps_increasing;
+ /* time stamp for retry if failed to allocate */
+ unsigned long fps_next_retry;
+} kib_fmr_poolset_t;
+
+typedef struct {
+ struct list_head fpo_list; /* chain on pool list */
+ struct kib_hca_dev *fpo_hdev; /* device for this pool */
+ kib_fmr_poolset_t *fpo_owner; /* owner of this pool */
+ struct ib_fmr_pool *fpo_fmr_pool; /* IB FMR pool */
+ unsigned long fpo_deadline; /* deadline of this pool */
+ int fpo_failed; /* fmr pool is failed */
+ int fpo_map_count; /* # of mapped FMR */
+} kib_fmr_pool_t;
+
+typedef struct {
+ struct ib_pool_fmr *fmr_pfmr; /* IB pool fmr */
+ kib_fmr_pool_t *fmr_pool; /* pool of FMR */
+} kib_fmr_t;
+
+typedef struct kib_net {
+ struct list_head ibn_list; /* chain on kib_dev_t::ibd_nets */
+ __u64 ibn_incarnation; /* my epoch */
+ int ibn_init; /* initialisation state */
+ int ibn_shutdown; /* shutting down? */
+
+ atomic_t ibn_npeers; /* # peers extant */
+ atomic_t ibn_nconns; /* # connections extant */
+
+ kib_tx_poolset_t **ibn_tx_ps; /* tx pool-set */
+ kib_fmr_poolset_t **ibn_fmr_ps; /* fmr pool-set */
+ kib_pmr_poolset_t **ibn_pmr_ps; /* pmr pool-set */
+
+ kib_dev_t *ibn_dev; /* underlying IB device */
+} kib_net_t;
+
+#define KIB_THREAD_SHIFT 16
+#define KIB_THREAD_ID(cpt, tid) ((cpt) << KIB_THREAD_SHIFT | (tid))
+#define KIB_THREAD_CPT(id) ((id) >> KIB_THREAD_SHIFT)
+#define KIB_THREAD_TID(id) ((id) & ((1UL << KIB_THREAD_SHIFT) - 1))
+
+struct kib_sched_info {
+ /* serialise */
+ spinlock_t ibs_lock;
+ /* schedulers sleep here */
+ wait_queue_head_t ibs_waitq;
+ /* conns to check for rx completions */
+ struct list_head ibs_conns;
+ /* number of scheduler threads */
+ int ibs_nthreads;
+ /* max allowed scheduler threads */
+ int ibs_nthreads_max;
+ int ibs_cpt; /* CPT id */
+};
+
+typedef struct {
+ int kib_init; /* initialisation state */
+ int kib_shutdown; /* shut down? */
+ struct list_head kib_devs; /* IB devices extant */
+ /* list head of failed devices */
+ struct list_head kib_failed_devs;
+ /* schedulers sleep here */
+ wait_queue_head_t kib_failover_waitq;
+ atomic_t kib_nthreads; /* # live threads */
+ /* stabilize net/dev/peer/conn ops */
+ rwlock_t kib_global_lock;
+ /* hash table of all my known peers */
+ struct list_head *kib_peers;
+ /* size of kib_peers */
+ int kib_peer_hash_size;
+ /* the connd task (serialisation assertions) */
+ void *kib_connd;
+ /* connections to setup/teardown */
+ struct list_head kib_connd_conns;
+ /* connections with zero refcount */
+ struct list_head kib_connd_zombies;
+ /* connection daemon sleeps here */
+ wait_queue_head_t kib_connd_waitq;
+ spinlock_t kib_connd_lock; /* serialise */
+ struct ib_qp_attr kib_error_qpa; /* QP->ERROR */
+ /* percpt data for schedulers */
+ struct kib_sched_info **kib_scheds;
+} kib_data_t;
+
+#define IBLND_INIT_NOTHING 0
+#define IBLND_INIT_DATA 1
+#define IBLND_INIT_ALL 2
+
+/************************************************************************
+ * IB Wire message format.
+ * These are sent in sender's byte order (i.e. receiver flips).
+ */
+
+typedef struct kib_connparams {
+ __u16 ibcp_queue_depth;
+ __u16 ibcp_max_frags;
+ __u32 ibcp_max_msg_size;
+} WIRE_ATTR kib_connparams_t;
+
+typedef struct {
+ lnet_hdr_t ibim_hdr; /* portals header */
+ char ibim_payload[0]; /* piggy-backed payload */
+} WIRE_ATTR kib_immediate_msg_t;
+
+typedef struct {
+ __u32 rf_nob; /* # bytes this frag */
+ __u64 rf_addr; /* CAVEAT EMPTOR: misaligned!! */
+} WIRE_ATTR kib_rdma_frag_t;
+
+typedef struct {
+ __u32 rd_key; /* local/remote key */
+ __u32 rd_nfrags; /* # fragments */
+ kib_rdma_frag_t rd_frags[0]; /* buffer frags */
+} WIRE_ATTR kib_rdma_desc_t;
+
+typedef struct {
+ lnet_hdr_t ibprm_hdr; /* portals header */
+ __u64 ibprm_cookie; /* opaque completion cookie */
+} WIRE_ATTR kib_putreq_msg_t;
+
+typedef struct {
+ __u64 ibpam_src_cookie; /* reflected completion cookie */
+ __u64 ibpam_dst_cookie; /* opaque completion cookie */
+ kib_rdma_desc_t ibpam_rd; /* sender's sink buffer */
+} WIRE_ATTR kib_putack_msg_t;
+
+typedef struct {
+ lnet_hdr_t ibgm_hdr; /* portals header */
+ __u64 ibgm_cookie; /* opaque completion cookie */
+ kib_rdma_desc_t ibgm_rd; /* rdma descriptor */
+} WIRE_ATTR kib_get_msg_t;
+
+typedef struct {
+ __u64 ibcm_cookie; /* opaque completion cookie */
+ __s32 ibcm_status; /* < 0 failure: >= 0 length */
+} WIRE_ATTR kib_completion_msg_t;
+
+typedef struct {
+ /* First 2 fields fixed FOR ALL TIME */
+ __u32 ibm_magic; /* I'm an ibnal message */
+ __u16 ibm_version; /* this is my version number */
+
+ __u8 ibm_type; /* msg type */
+ __u8 ibm_credits; /* returned credits */
+ __u32 ibm_nob; /* # bytes in whole message */
+ __u32 ibm_cksum; /* checksum (0 == no checksum) */
+ __u64 ibm_srcnid; /* sender's NID */
+ __u64 ibm_srcstamp; /* sender's incarnation */
+ __u64 ibm_dstnid; /* destination's NID */
+ __u64 ibm_dststamp; /* destination's incarnation */
+
+ union {
+ kib_connparams_t connparams;
+ kib_immediate_msg_t immediate;
+ kib_putreq_msg_t putreq;
+ kib_putack_msg_t putack;
+ kib_get_msg_t get;
+ kib_completion_msg_t completion;
+ } WIRE_ATTR ibm_u;
+} WIRE_ATTR kib_msg_t;
+
+#define IBLND_MSG_MAGIC LNET_PROTO_IB_MAGIC /* unique magic */
+
+#define IBLND_MSG_VERSION_1 0x11
+#define IBLND_MSG_VERSION_2 0x12
+#define IBLND_MSG_VERSION IBLND_MSG_VERSION_2
+
+#define IBLND_MSG_CONNREQ 0xc0 /* connection request */
+#define IBLND_MSG_CONNACK 0xc1 /* connection acknowledge */
+#define IBLND_MSG_NOOP 0xd0 /* nothing (just credits) */
+#define IBLND_MSG_IMMEDIATE 0xd1 /* immediate */
+#define IBLND_MSG_PUT_REQ 0xd2 /* putreq (src->sink) */
+#define IBLND_MSG_PUT_NAK 0xd3 /* completion (sink->src) */
+#define IBLND_MSG_PUT_ACK 0xd4 /* putack (sink->src) */
+#define IBLND_MSG_PUT_DONE 0xd5 /* completion (src->sink) */
+#define IBLND_MSG_GET_REQ 0xd6 /* getreq (sink->src) */
+#define IBLND_MSG_GET_DONE 0xd7 /* completion (src->sink: all OK) */
+
+typedef struct {
+ __u32 ibr_magic; /* sender's magic */
+ __u16 ibr_version; /* sender's version */
+ __u8 ibr_why; /* reject reason */
+ __u8 ibr_padding; /* padding */
+ __u64 ibr_incarnation; /* incarnation of peer */
+ kib_connparams_t ibr_cp; /* connection parameters */
+} WIRE_ATTR kib_rej_t;
+
+/* connection rejection reasons */
+#define IBLND_REJECT_CONN_RACE 1 /* You lost connection race */
+#define IBLND_REJECT_NO_RESOURCES 2 /* Out of memory/conns etc */
+#define IBLND_REJECT_FATAL 3 /* Anything else */
+
+#define IBLND_REJECT_CONN_UNCOMPAT 4 /* incompatible version peer */
+#define IBLND_REJECT_CONN_STALE 5 /* stale peer */
+
+#define IBLND_REJECT_RDMA_FRAGS 6 /* Fatal: peer's rdma frags can't match mine */
+#define IBLND_REJECT_MSG_QUEUE_SIZE 7 /* Fatal: peer's msg queue size can't match mine */
+
+/***********************************************************************/
+
+typedef struct kib_rx /* receive message */
+{
+ struct list_head rx_list; /* queue for attention */
+ struct kib_conn *rx_conn; /* owning conn */
+ int rx_nob; /* # bytes received (-1 while posted) */
+ enum ib_wc_status rx_status; /* completion status */
+ kib_msg_t *rx_msg; /* message buffer (host vaddr) */
+ __u64 rx_msgaddr; /* message buffer (I/O addr) */
+ DECLARE_PCI_UNMAP_ADDR (rx_msgunmap); /* for dma_unmap_single() */
+ struct ib_recv_wr rx_wrq; /* receive work item... */
+ struct ib_sge rx_sge; /* ...and its memory */
+} kib_rx_t;
+
+#define IBLND_POSTRX_DONT_POST 0 /* don't post */
+#define IBLND_POSTRX_NO_CREDIT 1 /* post: no credits */
+#define IBLND_POSTRX_PEER_CREDIT 2 /* post: give peer back 1 credit */
+#define IBLND_POSTRX_RSRVD_CREDIT 3 /* post: give myself back 1 reserved credit */
+
+typedef struct kib_tx /* transmit message */
+{
+ struct list_head tx_list; /* queue on idle_txs ibc_tx_queue etc. */
+ kib_tx_pool_t *tx_pool; /* pool I'm from */
+ struct kib_conn *tx_conn; /* owning conn */
+ short tx_sending; /* # tx callbacks outstanding */
+ short tx_queued; /* queued for sending */
+ short tx_waiting; /* waiting for peer */
+ int tx_status; /* LNET completion status */
+ unsigned long tx_deadline; /* completion deadline */
+ __u64 tx_cookie; /* completion cookie */
+ lnet_msg_t *tx_lntmsg[2]; /* lnet msgs to finalize on completion */
+ kib_msg_t *tx_msg; /* message buffer (host vaddr) */
+ __u64 tx_msgaddr; /* message buffer (I/O addr) */
+ DECLARE_PCI_UNMAP_ADDR (tx_msgunmap); /* for dma_unmap_single() */
+ int tx_nwrq; /* # send work items */
+ struct ib_send_wr *tx_wrq; /* send work items... */
+ struct ib_sge *tx_sge; /* ...and their memory */
+ kib_rdma_desc_t *tx_rd; /* rdma descriptor */
+ int tx_nfrags; /* # entries in... */
+ struct scatterlist *tx_frags; /* dma_map_sg descriptor */
+ __u64 *tx_pages; /* rdma phys page addrs */
+ union {
+ kib_phys_mr_t *pmr; /* MR for physical buffer */
+ kib_fmr_t fmr; /* FMR */
+ } tx_u;
+ int tx_dmadir; /* dma direction */
+} kib_tx_t;
+
+typedef struct kib_connvars {
+ /* connection-in-progress variables */
+ kib_msg_t cv_msg;
+} kib_connvars_t;
+
+typedef struct kib_conn {
+ struct kib_sched_info *ibc_sched; /* scheduler information */
+ struct kib_peer *ibc_peer; /* owning peer */
+ kib_hca_dev_t *ibc_hdev; /* HCA bound on */
+ struct list_head ibc_list; /* stash on peer's conn list */
+ struct list_head ibc_sched_list; /* schedule for attention */
+ __u16 ibc_version; /* version of connection */
+ __u64 ibc_incarnation; /* which instance of the peer */
+ atomic_t ibc_refcount; /* # users */
+ int ibc_state; /* what's happening */
+ int ibc_nsends_posted; /* # uncompleted sends */
+ int ibc_noops_posted; /* # uncompleted NOOPs */
+ int ibc_credits; /* # credits I have */
+ int ibc_outstanding_credits; /* # credits to return */
+ int ibc_reserved_credits;/* # ACK/DONE msg credits */
+ int ibc_comms_error; /* set on comms error */
+ unsigned int ibc_nrx:16; /* receive buffers owned */
+ unsigned int ibc_scheduled:1; /* scheduled for attention */
+ unsigned int ibc_ready:1; /* CQ callback fired */
+ /* time of last send */
+ unsigned long ibc_last_send;
+ /** link chain for kiblnd_check_conns only */
+ struct list_head ibc_connd_list;
+ /** rxs completed before ESTABLISHED */
+ struct list_head ibc_early_rxs;
+ /** IBLND_MSG_NOOPs for IBLND_MSG_VERSION_1 */
+ struct list_head ibc_tx_noops;
+ struct list_head ibc_tx_queue; /* sends that need a credit */
+ struct list_head ibc_tx_queue_nocred;/* sends that don't need a credit */
+ struct list_head ibc_tx_queue_rsrvd; /* sends that need to reserve an ACK/DONE msg */
+ struct list_head ibc_active_txs; /* active tx awaiting completion */
+ spinlock_t ibc_lock; /* serialise */
+ kib_rx_t *ibc_rxs; /* the rx descs */
+ kib_pages_t *ibc_rx_pages; /* premapped rx msg pages */
+
+ struct rdma_cm_id *ibc_cmid; /* CM id */
+ struct ib_cq *ibc_cq; /* completion queue */
+
+ kib_connvars_t *ibc_connvars; /* in-progress connection state */
+} kib_conn_t;
+
+#define IBLND_CONN_INIT 0 /* being initialised */
+#define IBLND_CONN_ACTIVE_CONNECT 1 /* active sending req */
+#define IBLND_CONN_PASSIVE_WAIT 2 /* passive waiting for rtu */
+#define IBLND_CONN_ESTABLISHED 3 /* connection established */
+#define IBLND_CONN_CLOSING 4 /* being closed */
+#define IBLND_CONN_DISCONNECTED 5 /* disconnected */
+
+typedef struct kib_peer {
+ struct list_head ibp_list; /* stash on global peer list */
+ lnet_nid_t ibp_nid; /* who's on the other end(s) */
+ lnet_ni_t *ibp_ni; /* LNet interface */
+ atomic_t ibp_refcount; /* # users */
+ struct list_head ibp_conns; /* all active connections */
+ struct list_head ibp_tx_queue; /* msgs waiting for a conn */
+ __u16 ibp_version; /* version of peer */
+ __u64 ibp_incarnation; /* incarnation of peer */
+ int ibp_connecting; /* current active connection attempts */
+ int ibp_accepting; /* current passive connection attempts */
+ int ibp_error; /* errno on closing this peer */
+ unsigned long ibp_last_alive; /* when (in jiffies) I was last alive */
+} kib_peer_t;
+
+extern kib_data_t kiblnd_data;
+
+extern void kiblnd_hdev_destroy(kib_hca_dev_t *hdev);
+
+static inline void
+kiblnd_hdev_addref_locked(kib_hca_dev_t *hdev)
+{
+ LASSERT (atomic_read(&hdev->ibh_ref) > 0);
+ atomic_inc(&hdev->ibh_ref);
+}
+
+static inline void
+kiblnd_hdev_decref(kib_hca_dev_t *hdev)
+{
+ LASSERT (atomic_read(&hdev->ibh_ref) > 0);
+ if (atomic_dec_and_test(&hdev->ibh_ref))
+ kiblnd_hdev_destroy(hdev);
+}
+
+static inline int
+kiblnd_dev_can_failover(kib_dev_t *dev)
+{
+ if (!list_empty(&dev->ibd_fail_list)) /* already scheduled */
+ return 0;
+
+ if (*kiblnd_tunables.kib_dev_failover == 0) /* disabled */
+ return 0;
+
+ if (*kiblnd_tunables.kib_dev_failover > 1) /* force failover */
+ return 1;
+
+ return dev->ibd_can_failover;
+}
+
+#define kiblnd_conn_addref(conn) \
+do { \
+ CDEBUG(D_NET, "conn[%p] (%d)++\n", \
+ (conn), atomic_read(&(conn)->ibc_refcount)); \
+ atomic_inc(&(conn)->ibc_refcount); \
+} while (0)
+
+#define kiblnd_conn_decref(conn) \
+do { \
+ unsigned long flags; \
+ \
+ CDEBUG(D_NET, "conn[%p] (%d)--\n", \
+ (conn), atomic_read(&(conn)->ibc_refcount)); \
+ LASSERT_ATOMIC_POS(&(conn)->ibc_refcount); \
+ if (atomic_dec_and_test(&(conn)->ibc_refcount)) { \
+ spin_lock_irqsave(&kiblnd_data.kib_connd_lock, flags); \
+ list_add_tail(&(conn)->ibc_list, \
+ &kiblnd_data.kib_connd_zombies); \
+ wake_up(&kiblnd_data.kib_connd_waitq); \
+ spin_unlock_irqrestore(&kiblnd_data.kib_connd_lock, flags);\
+ } \
+} while (0)
+
+#define kiblnd_peer_addref(peer) \
+do { \
+ CDEBUG(D_NET, "peer[%p] -> %s (%d)++\n", \
+ (peer), libcfs_nid2str((peer)->ibp_nid), \
+ atomic_read (&(peer)->ibp_refcount)); \
+ atomic_inc(&(peer)->ibp_refcount); \
+} while (0)
+
+#define kiblnd_peer_decref(peer) \
+do { \
+ CDEBUG(D_NET, "peer[%p] -> %s (%d)--\n", \
+ (peer), libcfs_nid2str((peer)->ibp_nid), \
+ atomic_read (&(peer)->ibp_refcount)); \
+ LASSERT_ATOMIC_POS(&(peer)->ibp_refcount); \
+ if (atomic_dec_and_test(&(peer)->ibp_refcount)) \
+ kiblnd_destroy_peer(peer); \
+} while (0)
+
+static inline struct list_head *
+kiblnd_nid2peerlist (lnet_nid_t nid)
+{
+ unsigned int hash =
+ ((unsigned int)nid) % kiblnd_data.kib_peer_hash_size;
+
+ return (&kiblnd_data.kib_peers [hash]);
+}
+
+static inline int
+kiblnd_peer_active (kib_peer_t *peer)
+{
+ /* Am I in the peer hash table? */
+ return (!list_empty(&peer->ibp_list));
+}
+
+static inline kib_conn_t *
+kiblnd_get_conn_locked (kib_peer_t *peer)
+{
+ LASSERT (!list_empty(&peer->ibp_conns));
+
+ /* just return the first connection */
+ return list_entry(peer->ibp_conns.next, kib_conn_t, ibc_list);
+}
+
+static inline int
+kiblnd_send_keepalive(kib_conn_t *conn)
+{
+ return (*kiblnd_tunables.kib_keepalive > 0) &&
+ cfs_time_after(jiffies, conn->ibc_last_send +
+ *kiblnd_tunables.kib_keepalive*HZ);
+}
+
+static inline int
+kiblnd_need_noop(kib_conn_t *conn)
+{
+ LASSERT (conn->ibc_state >= IBLND_CONN_ESTABLISHED);
+
+ if (conn->ibc_outstanding_credits <
+ IBLND_CREDITS_HIGHWATER(conn->ibc_version) &&
+ !kiblnd_send_keepalive(conn))
+ return 0; /* No need to send NOOP */
+
+ if (IBLND_OOB_CAPABLE(conn->ibc_version)) {
+ if (!list_empty(&conn->ibc_tx_queue_nocred))
+ return 0; /* NOOP can be piggybacked */
+
+ /* No tx to piggyback NOOP onto or no credit to send a tx */
+ return (list_empty(&conn->ibc_tx_queue) ||
+ conn->ibc_credits == 0);
+ }
+
+ if (!list_empty(&conn->ibc_tx_noops) || /* NOOP already queued */
+ !list_empty(&conn->ibc_tx_queue_nocred) || /* piggyback NOOP */
+ conn->ibc_credits == 0) /* no credit */
+ return 0;
+
+ if (conn->ibc_credits == 1 && /* last credit reserved for */
+ conn->ibc_outstanding_credits == 0) /* giving back credits */
+ return 0;
+
+ /* No tx to piggyback NOOP onto or no credit to send a tx */
+ return (list_empty(&conn->ibc_tx_queue) || conn->ibc_credits == 1);
+}
+
+static inline void
+kiblnd_abort_receives(kib_conn_t *conn)
+{
+ ib_modify_qp(conn->ibc_cmid->qp,
+ &kiblnd_data.kib_error_qpa, IB_QP_STATE);
+}
+
+static inline const char *
+kiblnd_queue2str (kib_conn_t *conn, struct list_head *q)
+{
+ if (q == &conn->ibc_tx_queue)
+ return "tx_queue";
+
+ if (q == &conn->ibc_tx_queue_rsrvd)
+ return "tx_queue_rsrvd";
+
+ if (q == &conn->ibc_tx_queue_nocred)
+ return "tx_queue_nocred";
+
+ if (q == &conn->ibc_active_txs)
+ return "active_txs";
+
+ LBUG();
+ return NULL;
+}
+
+/* CAVEAT EMPTOR: We rely on descriptor alignment to allow us to use the
+ * lowest bits of the work request id to stash the work item type. */
+
+#define IBLND_WID_TX 0
+#define IBLND_WID_RDMA 1
+#define IBLND_WID_RX 2
+#define IBLND_WID_MASK 3UL
+
+static inline __u64
+kiblnd_ptr2wreqid (void *ptr, int type)
+{
+ unsigned long lptr = (unsigned long)ptr;
+
+ LASSERT ((lptr & IBLND_WID_MASK) == 0);
+ LASSERT ((type & ~IBLND_WID_MASK) == 0);
+ return (__u64)(lptr | type);
+}
+
+static inline void *
+kiblnd_wreqid2ptr (__u64 wreqid)
+{
+ return (void *)(((unsigned long)wreqid) & ~IBLND_WID_MASK);
+}
+
+static inline int
+kiblnd_wreqid2type (__u64 wreqid)
+{
+ return (wreqid & IBLND_WID_MASK);
+}
+
+static inline void
+kiblnd_set_conn_state (kib_conn_t *conn, int state)
+{
+ conn->ibc_state = state;
+ mb();
+}
+
+static inline void
+kiblnd_init_msg (kib_msg_t *msg, int type, int body_nob)
+{
+ msg->ibm_type = type;
+ msg->ibm_nob = offsetof(kib_msg_t, ibm_u) + body_nob;
+}
+
+static inline int
+kiblnd_rd_size (kib_rdma_desc_t *rd)
+{
+ int i;
+ int size;
+
+ for (i = size = 0; i < rd->rd_nfrags; i++)
+ size += rd->rd_frags[i].rf_nob;
+
+ return size;
+}
+
+static inline __u64
+kiblnd_rd_frag_addr(kib_rdma_desc_t *rd, int index)
+{
+ return rd->rd_frags[index].rf_addr;
+}
+
+static inline __u32
+kiblnd_rd_frag_size(kib_rdma_desc_t *rd, int index)
+{
+ return rd->rd_frags[index].rf_nob;
+}
+
+static inline __u32
+kiblnd_rd_frag_key(kib_rdma_desc_t *rd, int index)
+{
+ return rd->rd_key;
+}
+
+static inline int
+kiblnd_rd_consume_frag(kib_rdma_desc_t *rd, int index, __u32 nob)
+{
+ if (nob < rd->rd_frags[index].rf_nob) {
+ rd->rd_frags[index].rf_addr += nob;
+ rd->rd_frags[index].rf_nob -= nob;
+ } else {
+ index ++;
+ }
+
+ return index;
+}
+
+static inline int
+kiblnd_rd_msg_size(kib_rdma_desc_t *rd, int msgtype, int n)
+{
+ LASSERT (msgtype == IBLND_MSG_GET_REQ ||
+ msgtype == IBLND_MSG_PUT_ACK);
+
+ return msgtype == IBLND_MSG_GET_REQ ?
+ offsetof(kib_get_msg_t, ibgm_rd.rd_frags[n]) :
+ offsetof(kib_putack_msg_t, ibpam_rd.rd_frags[n]);
+}
+
+
+static inline __u64
+kiblnd_dma_mapping_error(struct ib_device *dev, u64 dma_addr)
+{
+ return ib_dma_mapping_error(dev, dma_addr);
+}
+
+static inline __u64 kiblnd_dma_map_single(struct ib_device *dev,
+ void *msg, size_t size,
+ enum dma_data_direction direction)
+{
+ return ib_dma_map_single(dev, msg, size, direction);
+}
+
+static inline void kiblnd_dma_unmap_single(struct ib_device *dev,
+ __u64 addr, size_t size,
+ enum dma_data_direction direction)
+{
+ ib_dma_unmap_single(dev, addr, size, direction);
+}
+
+#define KIBLND_UNMAP_ADDR_SET(p, m, a) do {} while (0)
+#define KIBLND_UNMAP_ADDR(p, m, a) (a)
+
+static inline int kiblnd_dma_map_sg(struct ib_device *dev,
+ struct scatterlist *sg, int nents,
+ enum dma_data_direction direction)
+{
+ return ib_dma_map_sg(dev, sg, nents, direction);
+}
+
+static inline void kiblnd_dma_unmap_sg(struct ib_device *dev,
+ struct scatterlist *sg, int nents,
+ enum dma_data_direction direction)
+{
+ ib_dma_unmap_sg(dev, sg, nents, direction);
+}
+
+static inline __u64 kiblnd_sg_dma_address(struct ib_device *dev,
+ struct scatterlist *sg)
+{
+ return ib_sg_dma_address(dev, sg);
+}
+
+static inline unsigned int kiblnd_sg_dma_len(struct ib_device *dev,
+ struct scatterlist *sg)
+{
+ return ib_sg_dma_len(dev, sg);
+}
+
+/* XXX We use KIBLND_CONN_PARAM(e) as writable buffer, it's not strictly
+ * right because OFED1.2 defines it as const, to use it we have to add
+ * (void *) cast to overcome "const" */
+
+#define KIBLND_CONN_PARAM(e) ((e)->param.conn.private_data)
+#define KIBLND_CONN_PARAM_LEN(e) ((e)->param.conn.private_data_len)
+
+
+struct ib_mr *kiblnd_find_rd_dma_mr(kib_hca_dev_t *hdev,
+ kib_rdma_desc_t *rd);
+struct ib_mr *kiblnd_find_dma_mr(kib_hca_dev_t *hdev,
+ __u64 addr, __u64 size);
+void kiblnd_map_rx_descs(kib_conn_t *conn);
+void kiblnd_unmap_rx_descs(kib_conn_t *conn);
+int kiblnd_map_tx(lnet_ni_t *ni, kib_tx_t *tx,
+ kib_rdma_desc_t *rd, int nfrags);
+void kiblnd_unmap_tx(lnet_ni_t *ni, kib_tx_t *tx);
+void kiblnd_pool_free_node(kib_pool_t *pool, struct list_head *node);
+struct list_head *kiblnd_pool_alloc_node(kib_poolset_t *ps);
+
+int kiblnd_fmr_pool_map(kib_fmr_poolset_t *fps, __u64 *pages,
+ int npages, __u64 iov, kib_fmr_t *fmr);
+void kiblnd_fmr_pool_unmap(kib_fmr_t *fmr, int status);
+
+int kiblnd_pmr_pool_map(kib_pmr_poolset_t *pps, kib_hca_dev_t *hdev,
+ kib_rdma_desc_t *rd, __u64 *iova, kib_phys_mr_t **pp_pmr);
+void kiblnd_pmr_pool_unmap(kib_phys_mr_t *pmr);
+
+int kiblnd_startup (lnet_ni_t *ni);
+void kiblnd_shutdown (lnet_ni_t *ni);
+int kiblnd_ctl (lnet_ni_t *ni, unsigned int cmd, void *arg);
+void kiblnd_query (struct lnet_ni *ni, lnet_nid_t nid, unsigned long *when);
+
+int kiblnd_tunables_init(void);
+void kiblnd_tunables_fini(void);
+
+int kiblnd_connd (void *arg);
+int kiblnd_scheduler(void *arg);
+int kiblnd_thread_start(int (*fn)(void *arg), void *arg, char *name);
+int kiblnd_failover_thread (void *arg);
+
+int kiblnd_alloc_pages(kib_pages_t **pp, int cpt, int npages);
+void kiblnd_free_pages (kib_pages_t *p);
+
+int kiblnd_cm_callback(struct rdma_cm_id *cmid,
+ struct rdma_cm_event *event);
+int kiblnd_translate_mtu(int value);
+
+int kiblnd_dev_failover(kib_dev_t *dev);
+int kiblnd_create_peer (lnet_ni_t *ni, kib_peer_t **peerp, lnet_nid_t nid);
+void kiblnd_destroy_peer (kib_peer_t *peer);
+void kiblnd_destroy_dev (kib_dev_t *dev);
+void kiblnd_unlink_peer_locked (kib_peer_t *peer);
+void kiblnd_peer_alive (kib_peer_t *peer);
+kib_peer_t *kiblnd_find_peer_locked (lnet_nid_t nid);
+void kiblnd_peer_connect_failed (kib_peer_t *peer, int active, int error);
+int kiblnd_close_stale_conns_locked (kib_peer_t *peer,
+ int version, __u64 incarnation);
+int kiblnd_close_peer_conns_locked (kib_peer_t *peer, int why);
+
+void kiblnd_connreq_done(kib_conn_t *conn, int status);
+kib_conn_t *kiblnd_create_conn (kib_peer_t *peer, struct rdma_cm_id *cmid,
+ int state, int version);
+void kiblnd_destroy_conn (kib_conn_t *conn);
+void kiblnd_close_conn (kib_conn_t *conn, int error);
+void kiblnd_close_conn_locked (kib_conn_t *conn, int error);
+
+int kiblnd_init_rdma (kib_conn_t *conn, kib_tx_t *tx, int type,
+ int nob, kib_rdma_desc_t *dstrd, __u64 dstcookie);
+
+void kiblnd_launch_tx (lnet_ni_t *ni, kib_tx_t *tx, lnet_nid_t nid);
+void kiblnd_queue_tx_locked (kib_tx_t *tx, kib_conn_t *conn);
+void kiblnd_queue_tx (kib_tx_t *tx, kib_conn_t *conn);
+void kiblnd_init_tx_msg (lnet_ni_t *ni, kib_tx_t *tx, int type, int body_nob);
+void kiblnd_txlist_done (lnet_ni_t *ni, struct list_head *txlist,
+ int status);
+void kiblnd_check_sends (kib_conn_t *conn);
+
+void kiblnd_qp_event(struct ib_event *event, void *arg);
+void kiblnd_cq_event(struct ib_event *event, void *arg);
+void kiblnd_cq_completion(struct ib_cq *cq, void *arg);
+
+void kiblnd_pack_msg (lnet_ni_t *ni, kib_msg_t *msg, int version,
+ int credits, lnet_nid_t dstnid, __u64 dststamp);
+int kiblnd_unpack_msg(kib_msg_t *msg, int nob);
+int kiblnd_post_rx (kib_rx_t *rx, int credit);
+
+int kiblnd_send(lnet_ni_t *ni, void *private, lnet_msg_t *lntmsg);
+int kiblnd_recv(lnet_ni_t *ni, void *private, lnet_msg_t *lntmsg, int delayed,
+ unsigned int niov, struct kvec *iov, lnet_kiov_t *kiov,
+ unsigned int offset, unsigned int mlen, unsigned int rlen);
diff --git a/drivers/staging/lustre/lnet/klnds/o2iblnd/o2iblnd_cb.c b/drivers/staging/lustre/lnet/klnds/o2iblnd/o2iblnd_cb.c
new file mode 100644
index 000000000..dbf374983
--- /dev/null
+++ b/drivers/staging/lustre/lnet/klnds/o2iblnd/o2iblnd_cb.c
@@ -0,0 +1,3519 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2012, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ *
+ * lnet/klnds/o2iblnd/o2iblnd_cb.c
+ *
+ * Author: Eric Barton <eric@bartonsoftware.com>
+ */
+
+#include "o2iblnd.h"
+
+static void
+kiblnd_tx_done(lnet_ni_t *ni, kib_tx_t *tx)
+{
+ lnet_msg_t *lntmsg[2];
+ kib_net_t *net = ni->ni_data;
+ int rc;
+ int i;
+
+ LASSERT(net != NULL);
+ LASSERT(!in_interrupt());
+ LASSERT(!tx->tx_queued); /* mustn't be queued for sending */
+ LASSERT(tx->tx_sending == 0); /* mustn't be awaiting sent callback */
+ LASSERT(!tx->tx_waiting); /* mustn't be awaiting peer response */
+ LASSERT(tx->tx_pool != NULL);
+
+ kiblnd_unmap_tx(ni, tx);
+
+ /* tx may have up to 2 lnet msgs to finalise */
+ lntmsg[0] = tx->tx_lntmsg[0]; tx->tx_lntmsg[0] = NULL;
+ lntmsg[1] = tx->tx_lntmsg[1]; tx->tx_lntmsg[1] = NULL;
+ rc = tx->tx_status;
+
+ if (tx->tx_conn != NULL) {
+ LASSERT(ni == tx->tx_conn->ibc_peer->ibp_ni);
+
+ kiblnd_conn_decref(tx->tx_conn);
+ tx->tx_conn = NULL;
+ }
+
+ tx->tx_nwrq = 0;
+ tx->tx_status = 0;
+
+ kiblnd_pool_free_node(&tx->tx_pool->tpo_pool, &tx->tx_list);
+
+ /* delay finalize until my descs have been freed */
+ for (i = 0; i < 2; i++) {
+ if (lntmsg[i] == NULL)
+ continue;
+
+ lnet_finalize(ni, lntmsg[i], rc);
+ }
+}
+
+void
+kiblnd_txlist_done(lnet_ni_t *ni, struct list_head *txlist, int status)
+{
+ kib_tx_t *tx;
+
+ while (!list_empty(txlist)) {
+ tx = list_entry(txlist->next, kib_tx_t, tx_list);
+
+ list_del(&tx->tx_list);
+ /* complete now */
+ tx->tx_waiting = 0;
+ tx->tx_status = status;
+ kiblnd_tx_done(ni, tx);
+ }
+}
+
+static kib_tx_t *
+kiblnd_get_idle_tx(lnet_ni_t *ni, lnet_nid_t target)
+{
+ kib_net_t *net = (kib_net_t *)ni->ni_data;
+ struct list_head *node;
+ kib_tx_t *tx;
+ kib_tx_poolset_t *tps;
+
+ tps = net->ibn_tx_ps[lnet_cpt_of_nid(target)];
+ node = kiblnd_pool_alloc_node(&tps->tps_poolset);
+ if (node == NULL)
+ return NULL;
+ tx = container_of(node, kib_tx_t, tx_list);
+
+ LASSERT(tx->tx_nwrq == 0);
+ LASSERT(!tx->tx_queued);
+ LASSERT(tx->tx_sending == 0);
+ LASSERT(!tx->tx_waiting);
+ LASSERT(tx->tx_status == 0);
+ LASSERT(tx->tx_conn == NULL);
+ LASSERT(tx->tx_lntmsg[0] == NULL);
+ LASSERT(tx->tx_lntmsg[1] == NULL);
+ LASSERT(tx->tx_u.pmr == NULL);
+ LASSERT(tx->tx_nfrags == 0);
+
+ return tx;
+}
+
+static void
+kiblnd_drop_rx(kib_rx_t *rx)
+{
+ kib_conn_t *conn = rx->rx_conn;
+ struct kib_sched_info *sched = conn->ibc_sched;
+ unsigned long flags;
+
+ spin_lock_irqsave(&sched->ibs_lock, flags);
+ LASSERT(conn->ibc_nrx > 0);
+ conn->ibc_nrx--;
+ spin_unlock_irqrestore(&sched->ibs_lock, flags);
+
+ kiblnd_conn_decref(conn);
+}
+
+int
+kiblnd_post_rx(kib_rx_t *rx, int credit)
+{
+ kib_conn_t *conn = rx->rx_conn;
+ kib_net_t *net = conn->ibc_peer->ibp_ni->ni_data;
+ struct ib_recv_wr *bad_wrq = NULL;
+ struct ib_mr *mr;
+ int rc;
+
+ LASSERT(net != NULL);
+ LASSERT(!in_interrupt());
+ LASSERT(credit == IBLND_POSTRX_NO_CREDIT ||
+ credit == IBLND_POSTRX_PEER_CREDIT ||
+ credit == IBLND_POSTRX_RSRVD_CREDIT);
+
+ mr = kiblnd_find_dma_mr(conn->ibc_hdev, rx->rx_msgaddr, IBLND_MSG_SIZE);
+ LASSERT(mr != NULL);
+
+ rx->rx_sge.lkey = mr->lkey;
+ rx->rx_sge.addr = rx->rx_msgaddr;
+ rx->rx_sge.length = IBLND_MSG_SIZE;
+
+ rx->rx_wrq.next = NULL;
+ rx->rx_wrq.sg_list = &rx->rx_sge;
+ rx->rx_wrq.num_sge = 1;
+ rx->rx_wrq.wr_id = kiblnd_ptr2wreqid(rx, IBLND_WID_RX);
+
+ LASSERT(conn->ibc_state >= IBLND_CONN_INIT);
+ LASSERT(rx->rx_nob >= 0); /* not posted */
+
+ if (conn->ibc_state > IBLND_CONN_ESTABLISHED) {
+ kiblnd_drop_rx(rx); /* No more posts for this rx */
+ return 0;
+ }
+
+ rx->rx_nob = -1; /* flag posted */
+
+ rc = ib_post_recv(conn->ibc_cmid->qp, &rx->rx_wrq, &bad_wrq);
+ if (rc != 0) {
+ CERROR("Can't post rx for %s: %d, bad_wrq: %p\n",
+ libcfs_nid2str(conn->ibc_peer->ibp_nid), rc, bad_wrq);
+ rx->rx_nob = 0;
+ }
+
+ if (conn->ibc_state < IBLND_CONN_ESTABLISHED) /* Initial post */
+ return rc;
+
+ if (rc != 0) {
+ kiblnd_close_conn(conn, rc);
+ kiblnd_drop_rx(rx); /* No more posts for this rx */
+ return rc;
+ }
+
+ if (credit == IBLND_POSTRX_NO_CREDIT)
+ return 0;
+
+ spin_lock(&conn->ibc_lock);
+ if (credit == IBLND_POSTRX_PEER_CREDIT)
+ conn->ibc_outstanding_credits++;
+ else
+ conn->ibc_reserved_credits++;
+ spin_unlock(&conn->ibc_lock);
+
+ kiblnd_check_sends(conn);
+ return 0;
+}
+
+static kib_tx_t *
+kiblnd_find_waiting_tx_locked(kib_conn_t *conn, int txtype, __u64 cookie)
+{
+ struct list_head *tmp;
+
+ list_for_each(tmp, &conn->ibc_active_txs) {
+ kib_tx_t *tx = list_entry(tmp, kib_tx_t, tx_list);
+
+ LASSERT(!tx->tx_queued);
+ LASSERT(tx->tx_sending != 0 || tx->tx_waiting);
+
+ if (tx->tx_cookie != cookie)
+ continue;
+
+ if (tx->tx_waiting &&
+ tx->tx_msg->ibm_type == txtype)
+ return tx;
+
+ CWARN("Bad completion: %swaiting, type %x (wanted %x)\n",
+ tx->tx_waiting ? "" : "NOT ",
+ tx->tx_msg->ibm_type, txtype);
+ }
+ return NULL;
+}
+
+static void
+kiblnd_handle_completion(kib_conn_t *conn, int txtype, int status, __u64 cookie)
+{
+ kib_tx_t *tx;
+ lnet_ni_t *ni = conn->ibc_peer->ibp_ni;
+ int idle;
+
+ spin_lock(&conn->ibc_lock);
+
+ tx = kiblnd_find_waiting_tx_locked(conn, txtype, cookie);
+ if (tx == NULL) {
+ spin_unlock(&conn->ibc_lock);
+
+ CWARN("Unmatched completion type %x cookie %#llx from %s\n",
+ txtype, cookie, libcfs_nid2str(conn->ibc_peer->ibp_nid));
+ kiblnd_close_conn(conn, -EPROTO);
+ return;
+ }
+
+ if (tx->tx_status == 0) { /* success so far */
+ if (status < 0) { /* failed? */
+ tx->tx_status = status;
+ } else if (txtype == IBLND_MSG_GET_REQ) {
+ lnet_set_reply_msg_len(ni, tx->tx_lntmsg[1], status);
+ }
+ }
+
+ tx->tx_waiting = 0;
+
+ idle = !tx->tx_queued && (tx->tx_sending == 0);
+ if (idle)
+ list_del(&tx->tx_list);
+
+ spin_unlock(&conn->ibc_lock);
+
+ if (idle)
+ kiblnd_tx_done(ni, tx);
+}
+
+static void
+kiblnd_send_completion(kib_conn_t *conn, int type, int status, __u64 cookie)
+{
+ lnet_ni_t *ni = conn->ibc_peer->ibp_ni;
+ kib_tx_t *tx = kiblnd_get_idle_tx(ni, conn->ibc_peer->ibp_nid);
+
+ if (tx == NULL) {
+ CERROR("Can't get tx for completion %x for %s\n",
+ type, libcfs_nid2str(conn->ibc_peer->ibp_nid));
+ return;
+ }
+
+ tx->tx_msg->ibm_u.completion.ibcm_status = status;
+ tx->tx_msg->ibm_u.completion.ibcm_cookie = cookie;
+ kiblnd_init_tx_msg(ni, tx, type, sizeof(kib_completion_msg_t));
+
+ kiblnd_queue_tx(tx, conn);
+}
+
+static void
+kiblnd_handle_rx(kib_rx_t *rx)
+{
+ kib_msg_t *msg = rx->rx_msg;
+ kib_conn_t *conn = rx->rx_conn;
+ lnet_ni_t *ni = conn->ibc_peer->ibp_ni;
+ int credits = msg->ibm_credits;
+ kib_tx_t *tx;
+ int rc = 0;
+ int rc2;
+ int post_credit;
+
+ LASSERT(conn->ibc_state >= IBLND_CONN_ESTABLISHED);
+
+ CDEBUG(D_NET, "Received %x[%d] from %s\n",
+ msg->ibm_type, credits,
+ libcfs_nid2str(conn->ibc_peer->ibp_nid));
+
+ if (credits != 0) {
+ /* Have I received credits that will let me send? */
+ spin_lock(&conn->ibc_lock);
+
+ if (conn->ibc_credits + credits >
+ IBLND_MSG_QUEUE_SIZE(conn->ibc_version)) {
+ rc2 = conn->ibc_credits;
+ spin_unlock(&conn->ibc_lock);
+
+ CERROR("Bad credits from %s: %d + %d > %d\n",
+ libcfs_nid2str(conn->ibc_peer->ibp_nid),
+ rc2, credits,
+ IBLND_MSG_QUEUE_SIZE(conn->ibc_version));
+
+ kiblnd_close_conn(conn, -EPROTO);
+ kiblnd_post_rx(rx, IBLND_POSTRX_NO_CREDIT);
+ return;
+ }
+
+ conn->ibc_credits += credits;
+
+ /* This ensures the credit taken by NOOP can be returned */
+ if (msg->ibm_type == IBLND_MSG_NOOP &&
+ !IBLND_OOB_CAPABLE(conn->ibc_version)) /* v1 only */
+ conn->ibc_outstanding_credits++;
+
+ spin_unlock(&conn->ibc_lock);
+ kiblnd_check_sends(conn);
+ }
+
+ switch (msg->ibm_type) {
+ default:
+ CERROR("Bad IBLND message type %x from %s\n",
+ msg->ibm_type, libcfs_nid2str(conn->ibc_peer->ibp_nid));
+ post_credit = IBLND_POSTRX_NO_CREDIT;
+ rc = -EPROTO;
+ break;
+
+ case IBLND_MSG_NOOP:
+ if (IBLND_OOB_CAPABLE(conn->ibc_version)) {
+ post_credit = IBLND_POSTRX_NO_CREDIT;
+ break;
+ }
+
+ if (credits != 0) /* credit already posted */
+ post_credit = IBLND_POSTRX_NO_CREDIT;
+ else /* a keepalive NOOP */
+ post_credit = IBLND_POSTRX_PEER_CREDIT;
+ break;
+
+ case IBLND_MSG_IMMEDIATE:
+ post_credit = IBLND_POSTRX_DONT_POST;
+ rc = lnet_parse(ni, &msg->ibm_u.immediate.ibim_hdr,
+ msg->ibm_srcnid, rx, 0);
+ if (rc < 0) /* repost on error */
+ post_credit = IBLND_POSTRX_PEER_CREDIT;
+ break;
+
+ case IBLND_MSG_PUT_REQ:
+ post_credit = IBLND_POSTRX_DONT_POST;
+ rc = lnet_parse(ni, &msg->ibm_u.putreq.ibprm_hdr,
+ msg->ibm_srcnid, rx, 1);
+ if (rc < 0) /* repost on error */
+ post_credit = IBLND_POSTRX_PEER_CREDIT;
+ break;
+
+ case IBLND_MSG_PUT_NAK:
+ CWARN("PUT_NACK from %s\n",
+ libcfs_nid2str(conn->ibc_peer->ibp_nid));
+ post_credit = IBLND_POSTRX_RSRVD_CREDIT;
+ kiblnd_handle_completion(conn, IBLND_MSG_PUT_REQ,
+ msg->ibm_u.completion.ibcm_status,
+ msg->ibm_u.completion.ibcm_cookie);
+ break;
+
+ case IBLND_MSG_PUT_ACK:
+ post_credit = IBLND_POSTRX_RSRVD_CREDIT;
+
+ spin_lock(&conn->ibc_lock);
+ tx = kiblnd_find_waiting_tx_locked(conn, IBLND_MSG_PUT_REQ,
+ msg->ibm_u.putack.ibpam_src_cookie);
+ if (tx != NULL)
+ list_del(&tx->tx_list);
+ spin_unlock(&conn->ibc_lock);
+
+ if (tx == NULL) {
+ CERROR("Unmatched PUT_ACK from %s\n",
+ libcfs_nid2str(conn->ibc_peer->ibp_nid));
+ rc = -EPROTO;
+ break;
+ }
+
+ LASSERT(tx->tx_waiting);
+ /* CAVEAT EMPTOR: I could be racing with tx_complete, but...
+ * (a) I can overwrite tx_msg since my peer has received it!
+ * (b) tx_waiting set tells tx_complete() it's not done. */
+
+ tx->tx_nwrq = 0; /* overwrite PUT_REQ */
+
+ rc2 = kiblnd_init_rdma(conn, tx, IBLND_MSG_PUT_DONE,
+ kiblnd_rd_size(&msg->ibm_u.putack.ibpam_rd),
+ &msg->ibm_u.putack.ibpam_rd,
+ msg->ibm_u.putack.ibpam_dst_cookie);
+ if (rc2 < 0)
+ CERROR("Can't setup rdma for PUT to %s: %d\n",
+ libcfs_nid2str(conn->ibc_peer->ibp_nid), rc2);
+
+ spin_lock(&conn->ibc_lock);
+ tx->tx_waiting = 0; /* clear waiting and queue atomically */
+ kiblnd_queue_tx_locked(tx, conn);
+ spin_unlock(&conn->ibc_lock);
+ break;
+
+ case IBLND_MSG_PUT_DONE:
+ post_credit = IBLND_POSTRX_PEER_CREDIT;
+ kiblnd_handle_completion(conn, IBLND_MSG_PUT_ACK,
+ msg->ibm_u.completion.ibcm_status,
+ msg->ibm_u.completion.ibcm_cookie);
+ break;
+
+ case IBLND_MSG_GET_REQ:
+ post_credit = IBLND_POSTRX_DONT_POST;
+ rc = lnet_parse(ni, &msg->ibm_u.get.ibgm_hdr,
+ msg->ibm_srcnid, rx, 1);
+ if (rc < 0) /* repost on error */
+ post_credit = IBLND_POSTRX_PEER_CREDIT;
+ break;
+
+ case IBLND_MSG_GET_DONE:
+ post_credit = IBLND_POSTRX_RSRVD_CREDIT;
+ kiblnd_handle_completion(conn, IBLND_MSG_GET_REQ,
+ msg->ibm_u.completion.ibcm_status,
+ msg->ibm_u.completion.ibcm_cookie);
+ break;
+ }
+
+ if (rc < 0) /* protocol error */
+ kiblnd_close_conn(conn, rc);
+
+ if (post_credit != IBLND_POSTRX_DONT_POST)
+ kiblnd_post_rx(rx, post_credit);
+}
+
+static void
+kiblnd_rx_complete(kib_rx_t *rx, int status, int nob)
+{
+ kib_msg_t *msg = rx->rx_msg;
+ kib_conn_t *conn = rx->rx_conn;
+ lnet_ni_t *ni = conn->ibc_peer->ibp_ni;
+ kib_net_t *net = ni->ni_data;
+ int rc;
+ int err = -EIO;
+
+ LASSERT(net != NULL);
+ LASSERT(rx->rx_nob < 0); /* was posted */
+ rx->rx_nob = 0; /* isn't now */
+
+ if (conn->ibc_state > IBLND_CONN_ESTABLISHED)
+ goto ignore;
+
+ if (status != IB_WC_SUCCESS) {
+ CNETERR("Rx from %s failed: %d\n",
+ libcfs_nid2str(conn->ibc_peer->ibp_nid), status);
+ goto failed;
+ }
+
+ LASSERT(nob >= 0);
+ rx->rx_nob = nob;
+
+ rc = kiblnd_unpack_msg(msg, rx->rx_nob);
+ if (rc != 0) {
+ CERROR("Error %d unpacking rx from %s\n",
+ rc, libcfs_nid2str(conn->ibc_peer->ibp_nid));
+ goto failed;
+ }
+
+ if (msg->ibm_srcnid != conn->ibc_peer->ibp_nid ||
+ msg->ibm_dstnid != ni->ni_nid ||
+ msg->ibm_srcstamp != conn->ibc_incarnation ||
+ msg->ibm_dststamp != net->ibn_incarnation) {
+ CERROR("Stale rx from %s\n",
+ libcfs_nid2str(conn->ibc_peer->ibp_nid));
+ err = -ESTALE;
+ goto failed;
+ }
+
+ /* set time last known alive */
+ kiblnd_peer_alive(conn->ibc_peer);
+
+ /* racing with connection establishment/teardown! */
+
+ if (conn->ibc_state < IBLND_CONN_ESTABLISHED) {
+ rwlock_t *g_lock = &kiblnd_data.kib_global_lock;
+ unsigned long flags;
+
+ write_lock_irqsave(g_lock, flags);
+ /* must check holding global lock to eliminate race */
+ if (conn->ibc_state < IBLND_CONN_ESTABLISHED) {
+ list_add_tail(&rx->rx_list, &conn->ibc_early_rxs);
+ write_unlock_irqrestore(g_lock, flags);
+ return;
+ }
+ write_unlock_irqrestore(g_lock, flags);
+ }
+ kiblnd_handle_rx(rx);
+ return;
+
+ failed:
+ CDEBUG(D_NET, "rx %p conn %p\n", rx, conn);
+ kiblnd_close_conn(conn, err);
+ ignore:
+ kiblnd_drop_rx(rx); /* Don't re-post rx. */
+}
+
+static struct page *
+kiblnd_kvaddr_to_page(unsigned long vaddr)
+{
+ struct page *page;
+
+ if (is_vmalloc_addr((void *)vaddr)) {
+ page = vmalloc_to_page((void *)vaddr);
+ LASSERT(page != NULL);
+ return page;
+ }
+#ifdef CONFIG_HIGHMEM
+ if (vaddr >= PKMAP_BASE &&
+ vaddr < (PKMAP_BASE + LAST_PKMAP * PAGE_SIZE)) {
+ /* No highmem pages only used for bulk (kiov) I/O */
+ CERROR("find page for address in highmem\n");
+ LBUG();
+ }
+#endif
+ page = virt_to_page(vaddr);
+ LASSERT(page != NULL);
+ return page;
+}
+
+static int
+kiblnd_fmr_map_tx(kib_net_t *net, kib_tx_t *tx, kib_rdma_desc_t *rd, int nob)
+{
+ kib_hca_dev_t *hdev;
+ __u64 *pages = tx->tx_pages;
+ kib_fmr_poolset_t *fps;
+ int npages;
+ int size;
+ int cpt;
+ int rc;
+ int i;
+
+ LASSERT(tx->tx_pool != NULL);
+ LASSERT(tx->tx_pool->tpo_pool.po_owner != NULL);
+
+ hdev = tx->tx_pool->tpo_hdev;
+
+ for (i = 0, npages = 0; i < rd->rd_nfrags; i++) {
+ for (size = 0; size < rd->rd_frags[i].rf_nob;
+ size += hdev->ibh_page_size) {
+ pages[npages++] = (rd->rd_frags[i].rf_addr &
+ hdev->ibh_page_mask) + size;
+ }
+ }
+
+ cpt = tx->tx_pool->tpo_pool.po_owner->ps_cpt;
+
+ fps = net->ibn_fmr_ps[cpt];
+ rc = kiblnd_fmr_pool_map(fps, pages, npages, 0, &tx->tx_u.fmr);
+ if (rc != 0) {
+ CERROR("Can't map %d pages: %d\n", npages, rc);
+ return rc;
+ }
+
+ /* If rd is not tx_rd, it's going to get sent to a peer, who will need
+ * the rkey */
+ rd->rd_key = (rd != tx->tx_rd) ? tx->tx_u.fmr.fmr_pfmr->fmr->rkey :
+ tx->tx_u.fmr.fmr_pfmr->fmr->lkey;
+ rd->rd_frags[0].rf_addr &= ~hdev->ibh_page_mask;
+ rd->rd_frags[0].rf_nob = nob;
+ rd->rd_nfrags = 1;
+
+ return 0;
+}
+
+static int
+kiblnd_pmr_map_tx(kib_net_t *net, kib_tx_t *tx, kib_rdma_desc_t *rd, int nob)
+{
+ kib_hca_dev_t *hdev;
+ kib_pmr_poolset_t *pps;
+ __u64 iova;
+ int cpt;
+ int rc;
+
+ LASSERT(tx->tx_pool != NULL);
+ LASSERT(tx->tx_pool->tpo_pool.po_owner != NULL);
+
+ hdev = tx->tx_pool->tpo_hdev;
+
+ iova = rd->rd_frags[0].rf_addr & ~hdev->ibh_page_mask;
+
+ cpt = tx->tx_pool->tpo_pool.po_owner->ps_cpt;
+
+ pps = net->ibn_pmr_ps[cpt];
+ rc = kiblnd_pmr_pool_map(pps, hdev, rd, &iova, &tx->tx_u.pmr);
+ if (rc != 0) {
+ CERROR("Failed to create MR by phybuf: %d\n", rc);
+ return rc;
+ }
+
+ /* If rd is not tx_rd, it's going to get sent to a peer, who will need
+ * the rkey */
+ rd->rd_key = (rd != tx->tx_rd) ? tx->tx_u.pmr->pmr_mr->rkey :
+ tx->tx_u.pmr->pmr_mr->lkey;
+ rd->rd_nfrags = 1;
+ rd->rd_frags[0].rf_addr = iova;
+ rd->rd_frags[0].rf_nob = nob;
+
+ return 0;
+}
+
+void
+kiblnd_unmap_tx(lnet_ni_t *ni, kib_tx_t *tx)
+{
+ kib_net_t *net = ni->ni_data;
+
+ LASSERT(net != NULL);
+
+ if (net->ibn_fmr_ps != NULL && tx->tx_u.fmr.fmr_pfmr != NULL) {
+ kiblnd_fmr_pool_unmap(&tx->tx_u.fmr, tx->tx_status);
+ tx->tx_u.fmr.fmr_pfmr = NULL;
+
+ } else if (net->ibn_pmr_ps != NULL && tx->tx_u.pmr != NULL) {
+ kiblnd_pmr_pool_unmap(tx->tx_u.pmr);
+ tx->tx_u.pmr = NULL;
+ }
+
+ if (tx->tx_nfrags != 0) {
+ kiblnd_dma_unmap_sg(tx->tx_pool->tpo_hdev->ibh_ibdev,
+ tx->tx_frags, tx->tx_nfrags, tx->tx_dmadir);
+ tx->tx_nfrags = 0;
+ }
+}
+
+int
+kiblnd_map_tx(lnet_ni_t *ni, kib_tx_t *tx,
+ kib_rdma_desc_t *rd, int nfrags)
+{
+ kib_hca_dev_t *hdev = tx->tx_pool->tpo_hdev;
+ kib_net_t *net = ni->ni_data;
+ struct ib_mr *mr = NULL;
+ __u32 nob;
+ int i;
+
+ /* If rd is not tx_rd, it's going to get sent to a peer and I'm the
+ * RDMA sink */
+ tx->tx_dmadir = (rd != tx->tx_rd) ? DMA_FROM_DEVICE : DMA_TO_DEVICE;
+ tx->tx_nfrags = nfrags;
+
+ rd->rd_nfrags =
+ kiblnd_dma_map_sg(hdev->ibh_ibdev,
+ tx->tx_frags, tx->tx_nfrags, tx->tx_dmadir);
+
+ for (i = 0, nob = 0; i < rd->rd_nfrags; i++) {
+ rd->rd_frags[i].rf_nob = kiblnd_sg_dma_len(
+ hdev->ibh_ibdev, &tx->tx_frags[i]);
+ rd->rd_frags[i].rf_addr = kiblnd_sg_dma_address(
+ hdev->ibh_ibdev, &tx->tx_frags[i]);
+ nob += rd->rd_frags[i].rf_nob;
+ }
+
+ /* looking for pre-mapping MR */
+ mr = kiblnd_find_rd_dma_mr(hdev, rd);
+ if (mr != NULL) {
+ /* found pre-mapping MR */
+ rd->rd_key = (rd != tx->tx_rd) ? mr->rkey : mr->lkey;
+ return 0;
+ }
+
+ if (net->ibn_fmr_ps != NULL)
+ return kiblnd_fmr_map_tx(net, tx, rd, nob);
+ else if (net->ibn_pmr_ps != NULL)
+ return kiblnd_pmr_map_tx(net, tx, rd, nob);
+
+ return -EINVAL;
+}
+
+
+static int
+kiblnd_setup_rd_iov(lnet_ni_t *ni, kib_tx_t *tx, kib_rdma_desc_t *rd,
+ unsigned int niov, struct kvec *iov, int offset, int nob)
+{
+ kib_net_t *net = ni->ni_data;
+ struct page *page;
+ struct scatterlist *sg;
+ unsigned long vaddr;
+ int fragnob;
+ int page_offset;
+
+ LASSERT(nob > 0);
+ LASSERT(niov > 0);
+ LASSERT(net != NULL);
+
+ while (offset >= iov->iov_len) {
+ offset -= iov->iov_len;
+ niov--;
+ iov++;
+ LASSERT(niov > 0);
+ }
+
+ sg = tx->tx_frags;
+ do {
+ LASSERT(niov > 0);
+
+ vaddr = ((unsigned long)iov->iov_base) + offset;
+ page_offset = vaddr & (PAGE_SIZE - 1);
+ page = kiblnd_kvaddr_to_page(vaddr);
+ if (page == NULL) {
+ CERROR("Can't find page\n");
+ return -EFAULT;
+ }
+
+ fragnob = min((int)(iov->iov_len - offset), nob);
+ fragnob = min(fragnob, (int)PAGE_SIZE - page_offset);
+
+ sg_set_page(sg, page, fragnob, page_offset);
+ sg++;
+
+ if (offset + fragnob < iov->iov_len) {
+ offset += fragnob;
+ } else {
+ offset = 0;
+ iov++;
+ niov--;
+ }
+ nob -= fragnob;
+ } while (nob > 0);
+
+ return kiblnd_map_tx(ni, tx, rd, sg - tx->tx_frags);
+}
+
+static int
+kiblnd_setup_rd_kiov(lnet_ni_t *ni, kib_tx_t *tx, kib_rdma_desc_t *rd,
+ int nkiov, lnet_kiov_t *kiov, int offset, int nob)
+{
+ kib_net_t *net = ni->ni_data;
+ struct scatterlist *sg;
+ int fragnob;
+
+ CDEBUG(D_NET, "niov %d offset %d nob %d\n", nkiov, offset, nob);
+
+ LASSERT(nob > 0);
+ LASSERT(nkiov > 0);
+ LASSERT(net != NULL);
+
+ while (offset >= kiov->kiov_len) {
+ offset -= kiov->kiov_len;
+ nkiov--;
+ kiov++;
+ LASSERT(nkiov > 0);
+ }
+
+ sg = tx->tx_frags;
+ do {
+ LASSERT(nkiov > 0);
+
+ fragnob = min((int)(kiov->kiov_len - offset), nob);
+
+ sg_set_page(sg, kiov->kiov_page, fragnob,
+ kiov->kiov_offset + offset);
+ sg++;
+
+ offset = 0;
+ kiov++;
+ nkiov--;
+ nob -= fragnob;
+ } while (nob > 0);
+
+ return kiblnd_map_tx(ni, tx, rd, sg - tx->tx_frags);
+}
+
+static int
+kiblnd_post_tx_locked(kib_conn_t *conn, kib_tx_t *tx, int credit)
+ __releases(conn->ibc_lock)
+ __acquires(conn->ibc_lock)
+{
+ kib_msg_t *msg = tx->tx_msg;
+ kib_peer_t *peer = conn->ibc_peer;
+ int ver = conn->ibc_version;
+ int rc;
+ int done;
+ struct ib_send_wr *bad_wrq;
+
+ LASSERT(tx->tx_queued);
+ /* We rely on this for QP sizing */
+ LASSERT(tx->tx_nwrq > 0);
+ LASSERT(tx->tx_nwrq <= 1 + IBLND_RDMA_FRAGS(ver));
+
+ LASSERT(credit == 0 || credit == 1);
+ LASSERT(conn->ibc_outstanding_credits >= 0);
+ LASSERT(conn->ibc_outstanding_credits <= IBLND_MSG_QUEUE_SIZE(ver));
+ LASSERT(conn->ibc_credits >= 0);
+ LASSERT(conn->ibc_credits <= IBLND_MSG_QUEUE_SIZE(ver));
+
+ if (conn->ibc_nsends_posted == IBLND_CONCURRENT_SENDS(ver)) {
+ /* tx completions outstanding... */
+ CDEBUG(D_NET, "%s: posted enough\n",
+ libcfs_nid2str(peer->ibp_nid));
+ return -EAGAIN;
+ }
+
+ if (credit != 0 && conn->ibc_credits == 0) { /* no credits */
+ CDEBUG(D_NET, "%s: no credits\n",
+ libcfs_nid2str(peer->ibp_nid));
+ return -EAGAIN;
+ }
+
+ if (credit != 0 && !IBLND_OOB_CAPABLE(ver) &&
+ conn->ibc_credits == 1 && /* last credit reserved */
+ msg->ibm_type != IBLND_MSG_NOOP) { /* for NOOP */
+ CDEBUG(D_NET, "%s: not using last credit\n",
+ libcfs_nid2str(peer->ibp_nid));
+ return -EAGAIN;
+ }
+
+ /* NB don't drop ibc_lock before bumping tx_sending */
+ list_del(&tx->tx_list);
+ tx->tx_queued = 0;
+
+ if (msg->ibm_type == IBLND_MSG_NOOP &&
+ (!kiblnd_need_noop(conn) || /* redundant NOOP */
+ (IBLND_OOB_CAPABLE(ver) && /* posted enough NOOP */
+ conn->ibc_noops_posted == IBLND_OOB_MSGS(ver)))) {
+ /* OK to drop when posted enough NOOPs, since
+ * kiblnd_check_sends will queue NOOP again when
+ * posted NOOPs complete */
+ spin_unlock(&conn->ibc_lock);
+ kiblnd_tx_done(peer->ibp_ni, tx);
+ spin_lock(&conn->ibc_lock);
+ CDEBUG(D_NET, "%s(%d): redundant or enough NOOP\n",
+ libcfs_nid2str(peer->ibp_nid),
+ conn->ibc_noops_posted);
+ return 0;
+ }
+
+ kiblnd_pack_msg(peer->ibp_ni, msg, ver, conn->ibc_outstanding_credits,
+ peer->ibp_nid, conn->ibc_incarnation);
+
+ conn->ibc_credits -= credit;
+ conn->ibc_outstanding_credits = 0;
+ conn->ibc_nsends_posted++;
+ if (msg->ibm_type == IBLND_MSG_NOOP)
+ conn->ibc_noops_posted++;
+
+ /* CAVEAT EMPTOR! This tx could be the PUT_DONE of an RDMA
+ * PUT. If so, it was first queued here as a PUT_REQ, sent and
+ * stashed on ibc_active_txs, matched by an incoming PUT_ACK,
+ * and then re-queued here. It's (just) possible that
+ * tx_sending is non-zero if we've not done the tx_complete()
+ * from the first send; hence the ++ rather than = below. */
+ tx->tx_sending++;
+ list_add(&tx->tx_list, &conn->ibc_active_txs);
+
+ /* I'm still holding ibc_lock! */
+ if (conn->ibc_state != IBLND_CONN_ESTABLISHED) {
+ rc = -ECONNABORTED;
+ } else if (tx->tx_pool->tpo_pool.po_failed ||
+ conn->ibc_hdev != tx->tx_pool->tpo_hdev) {
+ /* close_conn will launch failover */
+ rc = -ENETDOWN;
+ } else {
+ rc = ib_post_send(conn->ibc_cmid->qp,
+ tx->tx_wrq, &bad_wrq);
+ }
+
+ conn->ibc_last_send = jiffies;
+
+ if (rc == 0)
+ return 0;
+
+ /* NB credits are transferred in the actual
+ * message, which can only be the last work item */
+ conn->ibc_credits += credit;
+ conn->ibc_outstanding_credits += msg->ibm_credits;
+ conn->ibc_nsends_posted--;
+ if (msg->ibm_type == IBLND_MSG_NOOP)
+ conn->ibc_noops_posted--;
+
+ tx->tx_status = rc;
+ tx->tx_waiting = 0;
+ tx->tx_sending--;
+
+ done = (tx->tx_sending == 0);
+ if (done)
+ list_del(&tx->tx_list);
+
+ spin_unlock(&conn->ibc_lock);
+
+ if (conn->ibc_state == IBLND_CONN_ESTABLISHED)
+ CERROR("Error %d posting transmit to %s\n",
+ rc, libcfs_nid2str(peer->ibp_nid));
+ else
+ CDEBUG(D_NET, "Error %d posting transmit to %s\n",
+ rc, libcfs_nid2str(peer->ibp_nid));
+
+ kiblnd_close_conn(conn, rc);
+
+ if (done)
+ kiblnd_tx_done(peer->ibp_ni, tx);
+
+ spin_lock(&conn->ibc_lock);
+
+ return -EIO;
+}
+
+void
+kiblnd_check_sends(kib_conn_t *conn)
+{
+ int ver = conn->ibc_version;
+ lnet_ni_t *ni = conn->ibc_peer->ibp_ni;
+ kib_tx_t *tx;
+
+ /* Don't send anything until after the connection is established */
+ if (conn->ibc_state < IBLND_CONN_ESTABLISHED) {
+ CDEBUG(D_NET, "%s too soon\n",
+ libcfs_nid2str(conn->ibc_peer->ibp_nid));
+ return;
+ }
+
+ spin_lock(&conn->ibc_lock);
+
+ LASSERT(conn->ibc_nsends_posted <= IBLND_CONCURRENT_SENDS(ver));
+ LASSERT(!IBLND_OOB_CAPABLE(ver) ||
+ conn->ibc_noops_posted <= IBLND_OOB_MSGS(ver));
+ LASSERT(conn->ibc_reserved_credits >= 0);
+
+ while (conn->ibc_reserved_credits > 0 &&
+ !list_empty(&conn->ibc_tx_queue_rsrvd)) {
+ tx = list_entry(conn->ibc_tx_queue_rsrvd.next,
+ kib_tx_t, tx_list);
+ list_del(&tx->tx_list);
+ list_add_tail(&tx->tx_list, &conn->ibc_tx_queue);
+ conn->ibc_reserved_credits--;
+ }
+
+ if (kiblnd_need_noop(conn)) {
+ spin_unlock(&conn->ibc_lock);
+
+ tx = kiblnd_get_idle_tx(ni, conn->ibc_peer->ibp_nid);
+ if (tx != NULL)
+ kiblnd_init_tx_msg(ni, tx, IBLND_MSG_NOOP, 0);
+
+ spin_lock(&conn->ibc_lock);
+ if (tx != NULL)
+ kiblnd_queue_tx_locked(tx, conn);
+ }
+
+ kiblnd_conn_addref(conn); /* 1 ref for me.... (see b21911) */
+
+ for (;;) {
+ int credit;
+
+ if (!list_empty(&conn->ibc_tx_queue_nocred)) {
+ credit = 0;
+ tx = list_entry(conn->ibc_tx_queue_nocred.next,
+ kib_tx_t, tx_list);
+ } else if (!list_empty(&conn->ibc_tx_noops)) {
+ LASSERT(!IBLND_OOB_CAPABLE(ver));
+ credit = 1;
+ tx = list_entry(conn->ibc_tx_noops.next,
+ kib_tx_t, tx_list);
+ } else if (!list_empty(&conn->ibc_tx_queue)) {
+ credit = 1;
+ tx = list_entry(conn->ibc_tx_queue.next,
+ kib_tx_t, tx_list);
+ } else
+ break;
+
+ if (kiblnd_post_tx_locked(conn, tx, credit) != 0)
+ break;
+ }
+
+ spin_unlock(&conn->ibc_lock);
+
+ kiblnd_conn_decref(conn); /* ...until here */
+}
+
+static void
+kiblnd_tx_complete(kib_tx_t *tx, int status)
+{
+ int failed = (status != IB_WC_SUCCESS);
+ kib_conn_t *conn = tx->tx_conn;
+ int idle;
+
+ LASSERT(tx->tx_sending > 0);
+
+ if (failed) {
+ if (conn->ibc_state == IBLND_CONN_ESTABLISHED)
+ CNETERR("Tx -> %s cookie %#llx sending %d waiting %d: failed %d\n",
+ libcfs_nid2str(conn->ibc_peer->ibp_nid),
+ tx->tx_cookie, tx->tx_sending, tx->tx_waiting,
+ status);
+
+ kiblnd_close_conn(conn, -EIO);
+ } else {
+ kiblnd_peer_alive(conn->ibc_peer);
+ }
+
+ spin_lock(&conn->ibc_lock);
+
+ /* I could be racing with rdma completion. Whoever makes 'tx' idle
+ * gets to free it, which also drops its ref on 'conn'. */
+
+ tx->tx_sending--;
+ conn->ibc_nsends_posted--;
+ if (tx->tx_msg->ibm_type == IBLND_MSG_NOOP)
+ conn->ibc_noops_posted--;
+
+ if (failed) {
+ tx->tx_waiting = 0; /* don't wait for peer */
+ tx->tx_status = -EIO;
+ }
+
+ idle = (tx->tx_sending == 0) && /* This is the final callback */
+ !tx->tx_waiting && /* Not waiting for peer */
+ !tx->tx_queued; /* Not re-queued (PUT_DONE) */
+ if (idle)
+ list_del(&tx->tx_list);
+
+ kiblnd_conn_addref(conn); /* 1 ref for me.... */
+
+ spin_unlock(&conn->ibc_lock);
+
+ if (idle)
+ kiblnd_tx_done(conn->ibc_peer->ibp_ni, tx);
+
+ kiblnd_check_sends(conn);
+
+ kiblnd_conn_decref(conn); /* ...until here */
+}
+
+void
+kiblnd_init_tx_msg(lnet_ni_t *ni, kib_tx_t *tx, int type, int body_nob)
+{
+ kib_hca_dev_t *hdev = tx->tx_pool->tpo_hdev;
+ struct ib_sge *sge = &tx->tx_sge[tx->tx_nwrq];
+ struct ib_send_wr *wrq = &tx->tx_wrq[tx->tx_nwrq];
+ int nob = offsetof(kib_msg_t, ibm_u) + body_nob;
+ struct ib_mr *mr;
+
+ LASSERT(tx->tx_nwrq >= 0);
+ LASSERT(tx->tx_nwrq < IBLND_MAX_RDMA_FRAGS + 1);
+ LASSERT(nob <= IBLND_MSG_SIZE);
+
+ kiblnd_init_msg(tx->tx_msg, type, body_nob);
+
+ mr = kiblnd_find_dma_mr(hdev, tx->tx_msgaddr, nob);
+ LASSERT(mr != NULL);
+
+ sge->lkey = mr->lkey;
+ sge->addr = tx->tx_msgaddr;
+ sge->length = nob;
+
+ memset(wrq, 0, sizeof(*wrq));
+
+ wrq->next = NULL;
+ wrq->wr_id = kiblnd_ptr2wreqid(tx, IBLND_WID_TX);
+ wrq->sg_list = sge;
+ wrq->num_sge = 1;
+ wrq->opcode = IB_WR_SEND;
+ wrq->send_flags = IB_SEND_SIGNALED;
+
+ tx->tx_nwrq++;
+}
+
+int
+kiblnd_init_rdma(kib_conn_t *conn, kib_tx_t *tx, int type,
+ int resid, kib_rdma_desc_t *dstrd, __u64 dstcookie)
+{
+ kib_msg_t *ibmsg = tx->tx_msg;
+ kib_rdma_desc_t *srcrd = tx->tx_rd;
+ struct ib_sge *sge = &tx->tx_sge[0];
+ struct ib_send_wr *wrq = &tx->tx_wrq[0];
+ int rc = resid;
+ int srcidx;
+ int dstidx;
+ int wrknob;
+
+ LASSERT(!in_interrupt());
+ LASSERT(tx->tx_nwrq == 0);
+ LASSERT(type == IBLND_MSG_GET_DONE ||
+ type == IBLND_MSG_PUT_DONE);
+
+ srcidx = dstidx = 0;
+
+ while (resid > 0) {
+ if (srcidx >= srcrd->rd_nfrags) {
+ CERROR("Src buffer exhausted: %d frags\n", srcidx);
+ rc = -EPROTO;
+ break;
+ }
+
+ if (dstidx == dstrd->rd_nfrags) {
+ CERROR("Dst buffer exhausted: %d frags\n", dstidx);
+ rc = -EPROTO;
+ break;
+ }
+
+ if (tx->tx_nwrq == IBLND_RDMA_FRAGS(conn->ibc_version)) {
+ CERROR("RDMA too fragmented for %s (%d): %d/%d src %d/%d dst frags\n",
+ libcfs_nid2str(conn->ibc_peer->ibp_nid),
+ IBLND_RDMA_FRAGS(conn->ibc_version),
+ srcidx, srcrd->rd_nfrags,
+ dstidx, dstrd->rd_nfrags);
+ rc = -EMSGSIZE;
+ break;
+ }
+
+ wrknob = min(min(kiblnd_rd_frag_size(srcrd, srcidx),
+ kiblnd_rd_frag_size(dstrd, dstidx)),
+ (__u32) resid);
+
+ sge = &tx->tx_sge[tx->tx_nwrq];
+ sge->addr = kiblnd_rd_frag_addr(srcrd, srcidx);
+ sge->lkey = kiblnd_rd_frag_key(srcrd, srcidx);
+ sge->length = wrknob;
+
+ wrq = &tx->tx_wrq[tx->tx_nwrq];
+
+ wrq->next = wrq + 1;
+ wrq->wr_id = kiblnd_ptr2wreqid(tx, IBLND_WID_RDMA);
+ wrq->sg_list = sge;
+ wrq->num_sge = 1;
+ wrq->opcode = IB_WR_RDMA_WRITE;
+ wrq->send_flags = 0;
+
+ wrq->wr.rdma.remote_addr = kiblnd_rd_frag_addr(dstrd, dstidx);
+ wrq->wr.rdma.rkey = kiblnd_rd_frag_key(dstrd, dstidx);
+
+ srcidx = kiblnd_rd_consume_frag(srcrd, srcidx, wrknob);
+ dstidx = kiblnd_rd_consume_frag(dstrd, dstidx, wrknob);
+
+ resid -= wrknob;
+
+ tx->tx_nwrq++;
+ wrq++;
+ sge++;
+ }
+
+ if (rc < 0) /* no RDMA if completing with failure */
+ tx->tx_nwrq = 0;
+
+ ibmsg->ibm_u.completion.ibcm_status = rc;
+ ibmsg->ibm_u.completion.ibcm_cookie = dstcookie;
+ kiblnd_init_tx_msg(conn->ibc_peer->ibp_ni, tx,
+ type, sizeof(kib_completion_msg_t));
+
+ return rc;
+}
+
+void
+kiblnd_queue_tx_locked(kib_tx_t *tx, kib_conn_t *conn)
+{
+ struct list_head *q;
+
+ LASSERT(tx->tx_nwrq > 0); /* work items set up */
+ LASSERT(!tx->tx_queued); /* not queued for sending already */
+ LASSERT(conn->ibc_state >= IBLND_CONN_ESTABLISHED);
+
+ tx->tx_queued = 1;
+ tx->tx_deadline = jiffies + (*kiblnd_tunables.kib_timeout * HZ);
+
+ if (tx->tx_conn == NULL) {
+ kiblnd_conn_addref(conn);
+ tx->tx_conn = conn;
+ LASSERT(tx->tx_msg->ibm_type != IBLND_MSG_PUT_DONE);
+ } else {
+ /* PUT_DONE first attached to conn as a PUT_REQ */
+ LASSERT(tx->tx_conn == conn);
+ LASSERT(tx->tx_msg->ibm_type == IBLND_MSG_PUT_DONE);
+ }
+
+ switch (tx->tx_msg->ibm_type) {
+ default:
+ LBUG();
+
+ case IBLND_MSG_PUT_REQ:
+ case IBLND_MSG_GET_REQ:
+ q = &conn->ibc_tx_queue_rsrvd;
+ break;
+
+ case IBLND_MSG_PUT_NAK:
+ case IBLND_MSG_PUT_ACK:
+ case IBLND_MSG_PUT_DONE:
+ case IBLND_MSG_GET_DONE:
+ q = &conn->ibc_tx_queue_nocred;
+ break;
+
+ case IBLND_MSG_NOOP:
+ if (IBLND_OOB_CAPABLE(conn->ibc_version))
+ q = &conn->ibc_tx_queue_nocred;
+ else
+ q = &conn->ibc_tx_noops;
+ break;
+
+ case IBLND_MSG_IMMEDIATE:
+ q = &conn->ibc_tx_queue;
+ break;
+ }
+
+ list_add_tail(&tx->tx_list, q);
+}
+
+void
+kiblnd_queue_tx(kib_tx_t *tx, kib_conn_t *conn)
+{
+ spin_lock(&conn->ibc_lock);
+ kiblnd_queue_tx_locked(tx, conn);
+ spin_unlock(&conn->ibc_lock);
+
+ kiblnd_check_sends(conn);
+}
+
+static int kiblnd_resolve_addr(struct rdma_cm_id *cmid,
+ struct sockaddr_in *srcaddr,
+ struct sockaddr_in *dstaddr,
+ int timeout_ms)
+{
+ unsigned short port;
+ int rc;
+
+ /* allow the port to be reused */
+ rc = rdma_set_reuseaddr(cmid, 1);
+ if (rc != 0) {
+ CERROR("Unable to set reuse on cmid: %d\n", rc);
+ return rc;
+ }
+
+ /* look for a free privileged port */
+ for (port = PROT_SOCK-1; port > 0; port--) {
+ srcaddr->sin_port = htons(port);
+ rc = rdma_resolve_addr(cmid,
+ (struct sockaddr *)srcaddr,
+ (struct sockaddr *)dstaddr,
+ timeout_ms);
+ if (rc == 0) {
+ CDEBUG(D_NET, "bound to port %hu\n", port);
+ return 0;
+ } else if (rc == -EADDRINUSE || rc == -EADDRNOTAVAIL) {
+ CDEBUG(D_NET, "bind to port %hu failed: %d\n",
+ port, rc);
+ } else {
+ return rc;
+ }
+ }
+
+ CERROR("Failed to bind to a free privileged port\n");
+ return rc;
+}
+
+static void
+kiblnd_connect_peer(kib_peer_t *peer)
+{
+ struct rdma_cm_id *cmid;
+ kib_dev_t *dev;
+ kib_net_t *net = peer->ibp_ni->ni_data;
+ struct sockaddr_in srcaddr;
+ struct sockaddr_in dstaddr;
+ int rc;
+
+ LASSERT(net != NULL);
+ LASSERT(peer->ibp_connecting > 0);
+
+ cmid = kiblnd_rdma_create_id(kiblnd_cm_callback, peer, RDMA_PS_TCP,
+ IB_QPT_RC);
+
+ if (IS_ERR(cmid)) {
+ CERROR("Can't create CMID for %s: %ld\n",
+ libcfs_nid2str(peer->ibp_nid), PTR_ERR(cmid));
+ rc = PTR_ERR(cmid);
+ goto failed;
+ }
+
+ dev = net->ibn_dev;
+ memset(&srcaddr, 0, sizeof(srcaddr));
+ srcaddr.sin_family = AF_INET;
+ srcaddr.sin_addr.s_addr = htonl(dev->ibd_ifip);
+
+ memset(&dstaddr, 0, sizeof(dstaddr));
+ dstaddr.sin_family = AF_INET;
+ dstaddr.sin_port = htons(*kiblnd_tunables.kib_service);
+ dstaddr.sin_addr.s_addr = htonl(LNET_NIDADDR(peer->ibp_nid));
+
+ kiblnd_peer_addref(peer); /* cmid's ref */
+
+ if (*kiblnd_tunables.kib_use_priv_port) {
+ rc = kiblnd_resolve_addr(cmid, &srcaddr, &dstaddr,
+ *kiblnd_tunables.kib_timeout * 1000);
+ } else {
+ rc = rdma_resolve_addr(cmid,
+ (struct sockaddr *)&srcaddr,
+ (struct sockaddr *)&dstaddr,
+ *kiblnd_tunables.kib_timeout * 1000);
+ }
+ if (rc != 0) {
+ /* Can't initiate address resolution: */
+ CERROR("Can't resolve addr for %s: %d\n",
+ libcfs_nid2str(peer->ibp_nid), rc);
+ goto failed2;
+ }
+
+ LASSERT(cmid->device != NULL);
+ CDEBUG(D_NET, "%s: connection bound to %s:%pI4h:%s\n",
+ libcfs_nid2str(peer->ibp_nid), dev->ibd_ifname,
+ &dev->ibd_ifip, cmid->device->name);
+
+ return;
+
+ failed2:
+ kiblnd_peer_decref(peer); /* cmid's ref */
+ rdma_destroy_id(cmid);
+ failed:
+ kiblnd_peer_connect_failed(peer, 1, rc);
+}
+
+void
+kiblnd_launch_tx(lnet_ni_t *ni, kib_tx_t *tx, lnet_nid_t nid)
+{
+ kib_peer_t *peer;
+ kib_peer_t *peer2;
+ kib_conn_t *conn;
+ rwlock_t *g_lock = &kiblnd_data.kib_global_lock;
+ unsigned long flags;
+ int rc;
+
+ /* If I get here, I've committed to send, so I complete the tx with
+ * failure on any problems */
+
+ LASSERT(tx == NULL || tx->tx_conn == NULL); /* only set when assigned a conn */
+ LASSERT(tx == NULL || tx->tx_nwrq > 0); /* work items have been set up */
+
+ /* First time, just use a read lock since I expect to find my peer
+ * connected */
+ read_lock_irqsave(g_lock, flags);
+
+ peer = kiblnd_find_peer_locked(nid);
+ if (peer != NULL && !list_empty(&peer->ibp_conns)) {
+ /* Found a peer with an established connection */
+ conn = kiblnd_get_conn_locked(peer);
+ kiblnd_conn_addref(conn); /* 1 ref for me... */
+
+ read_unlock_irqrestore(g_lock, flags);
+
+ if (tx != NULL)
+ kiblnd_queue_tx(tx, conn);
+ kiblnd_conn_decref(conn); /* ...to here */
+ return;
+ }
+
+ read_unlock(g_lock);
+ /* Re-try with a write lock */
+ write_lock(g_lock);
+
+ peer = kiblnd_find_peer_locked(nid);
+ if (peer != NULL) {
+ if (list_empty(&peer->ibp_conns)) {
+ /* found a peer, but it's still connecting... */
+ LASSERT(peer->ibp_connecting != 0 ||
+ peer->ibp_accepting != 0);
+ if (tx != NULL)
+ list_add_tail(&tx->tx_list,
+ &peer->ibp_tx_queue);
+ write_unlock_irqrestore(g_lock, flags);
+ } else {
+ conn = kiblnd_get_conn_locked(peer);
+ kiblnd_conn_addref(conn); /* 1 ref for me... */
+
+ write_unlock_irqrestore(g_lock, flags);
+
+ if (tx != NULL)
+ kiblnd_queue_tx(tx, conn);
+ kiblnd_conn_decref(conn); /* ...to here */
+ }
+ return;
+ }
+
+ write_unlock_irqrestore(g_lock, flags);
+
+ /* Allocate a peer ready to add to the peer table and retry */
+ rc = kiblnd_create_peer(ni, &peer, nid);
+ if (rc != 0) {
+ CERROR("Can't create peer %s\n", libcfs_nid2str(nid));
+ if (tx != NULL) {
+ tx->tx_status = -EHOSTUNREACH;
+ tx->tx_waiting = 0;
+ kiblnd_tx_done(ni, tx);
+ }
+ return;
+ }
+
+ write_lock_irqsave(g_lock, flags);
+
+ peer2 = kiblnd_find_peer_locked(nid);
+ if (peer2 != NULL) {
+ if (list_empty(&peer2->ibp_conns)) {
+ /* found a peer, but it's still connecting... */
+ LASSERT(peer2->ibp_connecting != 0 ||
+ peer2->ibp_accepting != 0);
+ if (tx != NULL)
+ list_add_tail(&tx->tx_list,
+ &peer2->ibp_tx_queue);
+ write_unlock_irqrestore(g_lock, flags);
+ } else {
+ conn = kiblnd_get_conn_locked(peer2);
+ kiblnd_conn_addref(conn); /* 1 ref for me... */
+
+ write_unlock_irqrestore(g_lock, flags);
+
+ if (tx != NULL)
+ kiblnd_queue_tx(tx, conn);
+ kiblnd_conn_decref(conn); /* ...to here */
+ }
+
+ kiblnd_peer_decref(peer);
+ return;
+ }
+
+ /* Brand new peer */
+ LASSERT(peer->ibp_connecting == 0);
+ peer->ibp_connecting = 1;
+
+ /* always called with a ref on ni, which prevents ni being shutdown */
+ LASSERT(((kib_net_t *)ni->ni_data)->ibn_shutdown == 0);
+
+ if (tx != NULL)
+ list_add_tail(&tx->tx_list, &peer->ibp_tx_queue);
+
+ kiblnd_peer_addref(peer);
+ list_add_tail(&peer->ibp_list, kiblnd_nid2peerlist(nid));
+
+ write_unlock_irqrestore(g_lock, flags);
+
+ kiblnd_connect_peer(peer);
+ kiblnd_peer_decref(peer);
+}
+
+int
+kiblnd_send(lnet_ni_t *ni, void *private, lnet_msg_t *lntmsg)
+{
+ lnet_hdr_t *hdr = &lntmsg->msg_hdr;
+ int type = lntmsg->msg_type;
+ lnet_process_id_t target = lntmsg->msg_target;
+ int target_is_router = lntmsg->msg_target_is_router;
+ int routing = lntmsg->msg_routing;
+ unsigned int payload_niov = lntmsg->msg_niov;
+ struct kvec *payload_iov = lntmsg->msg_iov;
+ lnet_kiov_t *payload_kiov = lntmsg->msg_kiov;
+ unsigned int payload_offset = lntmsg->msg_offset;
+ unsigned int payload_nob = lntmsg->msg_len;
+ kib_msg_t *ibmsg;
+ kib_tx_t *tx;
+ int nob;
+ int rc;
+
+ /* NB 'private' is different depending on what we're sending.... */
+
+ CDEBUG(D_NET, "sending %d bytes in %d frags to %s\n",
+ payload_nob, payload_niov, libcfs_id2str(target));
+
+ LASSERT(payload_nob == 0 || payload_niov > 0);
+ LASSERT(payload_niov <= LNET_MAX_IOV);
+
+ /* Thread context */
+ LASSERT(!in_interrupt());
+ /* payload is either all vaddrs or all pages */
+ LASSERT(!(payload_kiov != NULL && payload_iov != NULL));
+
+ switch (type) {
+ default:
+ LBUG();
+ return -EIO;
+
+ case LNET_MSG_ACK:
+ LASSERT(payload_nob == 0);
+ break;
+
+ case LNET_MSG_GET:
+ if (routing || target_is_router)
+ break; /* send IMMEDIATE */
+
+ /* is the REPLY message too small for RDMA? */
+ nob = offsetof(kib_msg_t, ibm_u.immediate.ibim_payload[lntmsg->msg_md->md_length]);
+ if (nob <= IBLND_MSG_SIZE)
+ break; /* send IMMEDIATE */
+
+ tx = kiblnd_get_idle_tx(ni, target.nid);
+ if (tx == NULL) {
+ CERROR("Can't allocate txd for GET to %s\n",
+ libcfs_nid2str(target.nid));
+ return -ENOMEM;
+ }
+
+ ibmsg = tx->tx_msg;
+
+ if ((lntmsg->msg_md->md_options & LNET_MD_KIOV) == 0)
+ rc = kiblnd_setup_rd_iov(ni, tx,
+ &ibmsg->ibm_u.get.ibgm_rd,
+ lntmsg->msg_md->md_niov,
+ lntmsg->msg_md->md_iov.iov,
+ 0, lntmsg->msg_md->md_length);
+ else
+ rc = kiblnd_setup_rd_kiov(ni, tx,
+ &ibmsg->ibm_u.get.ibgm_rd,
+ lntmsg->msg_md->md_niov,
+ lntmsg->msg_md->md_iov.kiov,
+ 0, lntmsg->msg_md->md_length);
+ if (rc != 0) {
+ CERROR("Can't setup GET sink for %s: %d\n",
+ libcfs_nid2str(target.nid), rc);
+ kiblnd_tx_done(ni, tx);
+ return -EIO;
+ }
+
+ nob = offsetof(kib_get_msg_t, ibgm_rd.rd_frags[tx->tx_nfrags]);
+ ibmsg->ibm_u.get.ibgm_cookie = tx->tx_cookie;
+ ibmsg->ibm_u.get.ibgm_hdr = *hdr;
+
+ kiblnd_init_tx_msg(ni, tx, IBLND_MSG_GET_REQ, nob);
+
+ tx->tx_lntmsg[1] = lnet_create_reply_msg(ni, lntmsg);
+ if (tx->tx_lntmsg[1] == NULL) {
+ CERROR("Can't create reply for GET -> %s\n",
+ libcfs_nid2str(target.nid));
+ kiblnd_tx_done(ni, tx);
+ return -EIO;
+ }
+
+ tx->tx_lntmsg[0] = lntmsg; /* finalise lntmsg[0,1] on completion */
+ tx->tx_waiting = 1; /* waiting for GET_DONE */
+ kiblnd_launch_tx(ni, tx, target.nid);
+ return 0;
+
+ case LNET_MSG_REPLY:
+ case LNET_MSG_PUT:
+ /* Is the payload small enough not to need RDMA? */
+ nob = offsetof(kib_msg_t, ibm_u.immediate.ibim_payload[payload_nob]);
+ if (nob <= IBLND_MSG_SIZE)
+ break; /* send IMMEDIATE */
+
+ tx = kiblnd_get_idle_tx(ni, target.nid);
+ if (tx == NULL) {
+ CERROR("Can't allocate %s txd for %s\n",
+ type == LNET_MSG_PUT ? "PUT" : "REPLY",
+ libcfs_nid2str(target.nid));
+ return -ENOMEM;
+ }
+
+ if (payload_kiov == NULL)
+ rc = kiblnd_setup_rd_iov(ni, tx, tx->tx_rd,
+ payload_niov, payload_iov,
+ payload_offset, payload_nob);
+ else
+ rc = kiblnd_setup_rd_kiov(ni, tx, tx->tx_rd,
+ payload_niov, payload_kiov,
+ payload_offset, payload_nob);
+ if (rc != 0) {
+ CERROR("Can't setup PUT src for %s: %d\n",
+ libcfs_nid2str(target.nid), rc);
+ kiblnd_tx_done(ni, tx);
+ return -EIO;
+ }
+
+ ibmsg = tx->tx_msg;
+ ibmsg->ibm_u.putreq.ibprm_hdr = *hdr;
+ ibmsg->ibm_u.putreq.ibprm_cookie = tx->tx_cookie;
+ kiblnd_init_tx_msg(ni, tx, IBLND_MSG_PUT_REQ, sizeof(kib_putreq_msg_t));
+
+ tx->tx_lntmsg[0] = lntmsg; /* finalise lntmsg on completion */
+ tx->tx_waiting = 1; /* waiting for PUT_{ACK,NAK} */
+ kiblnd_launch_tx(ni, tx, target.nid);
+ return 0;
+ }
+
+ /* send IMMEDIATE */
+
+ LASSERT(offsetof(kib_msg_t, ibm_u.immediate.ibim_payload[payload_nob])
+ <= IBLND_MSG_SIZE);
+
+ tx = kiblnd_get_idle_tx(ni, target.nid);
+ if (tx == NULL) {
+ CERROR("Can't send %d to %s: tx descs exhausted\n",
+ type, libcfs_nid2str(target.nid));
+ return -ENOMEM;
+ }
+
+ ibmsg = tx->tx_msg;
+ ibmsg->ibm_u.immediate.ibim_hdr = *hdr;
+
+ if (payload_kiov != NULL)
+ lnet_copy_kiov2flat(IBLND_MSG_SIZE, ibmsg,
+ offsetof(kib_msg_t, ibm_u.immediate.ibim_payload),
+ payload_niov, payload_kiov,
+ payload_offset, payload_nob);
+ else
+ lnet_copy_iov2flat(IBLND_MSG_SIZE, ibmsg,
+ offsetof(kib_msg_t, ibm_u.immediate.ibim_payload),
+ payload_niov, payload_iov,
+ payload_offset, payload_nob);
+
+ nob = offsetof(kib_immediate_msg_t, ibim_payload[payload_nob]);
+ kiblnd_init_tx_msg(ni, tx, IBLND_MSG_IMMEDIATE, nob);
+
+ tx->tx_lntmsg[0] = lntmsg; /* finalise lntmsg on completion */
+ kiblnd_launch_tx(ni, tx, target.nid);
+ return 0;
+}
+
+static void
+kiblnd_reply(lnet_ni_t *ni, kib_rx_t *rx, lnet_msg_t *lntmsg)
+{
+ lnet_process_id_t target = lntmsg->msg_target;
+ unsigned int niov = lntmsg->msg_niov;
+ struct kvec *iov = lntmsg->msg_iov;
+ lnet_kiov_t *kiov = lntmsg->msg_kiov;
+ unsigned int offset = lntmsg->msg_offset;
+ unsigned int nob = lntmsg->msg_len;
+ kib_tx_t *tx;
+ int rc;
+
+ tx = kiblnd_get_idle_tx(ni, rx->rx_conn->ibc_peer->ibp_nid);
+ if (tx == NULL) {
+ CERROR("Can't get tx for REPLY to %s\n",
+ libcfs_nid2str(target.nid));
+ goto failed_0;
+ }
+
+ if (nob == 0)
+ rc = 0;
+ else if (kiov == NULL)
+ rc = kiblnd_setup_rd_iov(ni, tx, tx->tx_rd,
+ niov, iov, offset, nob);
+ else
+ rc = kiblnd_setup_rd_kiov(ni, tx, tx->tx_rd,
+ niov, kiov, offset, nob);
+
+ if (rc != 0) {
+ CERROR("Can't setup GET src for %s: %d\n",
+ libcfs_nid2str(target.nid), rc);
+ goto failed_1;
+ }
+
+ rc = kiblnd_init_rdma(rx->rx_conn, tx,
+ IBLND_MSG_GET_DONE, nob,
+ &rx->rx_msg->ibm_u.get.ibgm_rd,
+ rx->rx_msg->ibm_u.get.ibgm_cookie);
+ if (rc < 0) {
+ CERROR("Can't setup rdma for GET from %s: %d\n",
+ libcfs_nid2str(target.nid), rc);
+ goto failed_1;
+ }
+
+ if (nob == 0) {
+ /* No RDMA: local completion may happen now! */
+ lnet_finalize(ni, lntmsg, 0);
+ } else {
+ /* RDMA: lnet_finalize(lntmsg) when it
+ * completes */
+ tx->tx_lntmsg[0] = lntmsg;
+ }
+
+ kiblnd_queue_tx(tx, rx->rx_conn);
+ return;
+
+ failed_1:
+ kiblnd_tx_done(ni, tx);
+ failed_0:
+ lnet_finalize(ni, lntmsg, -EIO);
+}
+
+int
+kiblnd_recv(lnet_ni_t *ni, void *private, lnet_msg_t *lntmsg, int delayed,
+ unsigned int niov, struct kvec *iov, lnet_kiov_t *kiov,
+ unsigned int offset, unsigned int mlen, unsigned int rlen)
+{
+ kib_rx_t *rx = private;
+ kib_msg_t *rxmsg = rx->rx_msg;
+ kib_conn_t *conn = rx->rx_conn;
+ kib_tx_t *tx;
+ kib_msg_t *txmsg;
+ int nob;
+ int post_credit = IBLND_POSTRX_PEER_CREDIT;
+ int rc = 0;
+
+ LASSERT(mlen <= rlen);
+ LASSERT(!in_interrupt());
+ /* Either all pages or all vaddrs */
+ LASSERT(!(kiov != NULL && iov != NULL));
+
+ switch (rxmsg->ibm_type) {
+ default:
+ LBUG();
+
+ case IBLND_MSG_IMMEDIATE:
+ nob = offsetof(kib_msg_t, ibm_u.immediate.ibim_payload[rlen]);
+ if (nob > rx->rx_nob) {
+ CERROR("Immediate message from %s too big: %d(%d)\n",
+ libcfs_nid2str(rxmsg->ibm_u.immediate.ibim_hdr.src_nid),
+ nob, rx->rx_nob);
+ rc = -EPROTO;
+ break;
+ }
+
+ if (kiov != NULL)
+ lnet_copy_flat2kiov(niov, kiov, offset,
+ IBLND_MSG_SIZE, rxmsg,
+ offsetof(kib_msg_t, ibm_u.immediate.ibim_payload),
+ mlen);
+ else
+ lnet_copy_flat2iov(niov, iov, offset,
+ IBLND_MSG_SIZE, rxmsg,
+ offsetof(kib_msg_t, ibm_u.immediate.ibim_payload),
+ mlen);
+ lnet_finalize(ni, lntmsg, 0);
+ break;
+
+ case IBLND_MSG_PUT_REQ:
+ if (mlen == 0) {
+ lnet_finalize(ni, lntmsg, 0);
+ kiblnd_send_completion(rx->rx_conn, IBLND_MSG_PUT_NAK, 0,
+ rxmsg->ibm_u.putreq.ibprm_cookie);
+ break;
+ }
+
+ tx = kiblnd_get_idle_tx(ni, conn->ibc_peer->ibp_nid);
+ if (tx == NULL) {
+ CERROR("Can't allocate tx for %s\n",
+ libcfs_nid2str(conn->ibc_peer->ibp_nid));
+ /* Not replying will break the connection */
+ rc = -ENOMEM;
+ break;
+ }
+
+ txmsg = tx->tx_msg;
+ if (kiov == NULL)
+ rc = kiblnd_setup_rd_iov(ni, tx,
+ &txmsg->ibm_u.putack.ibpam_rd,
+ niov, iov, offset, mlen);
+ else
+ rc = kiblnd_setup_rd_kiov(ni, tx,
+ &txmsg->ibm_u.putack.ibpam_rd,
+ niov, kiov, offset, mlen);
+ if (rc != 0) {
+ CERROR("Can't setup PUT sink for %s: %d\n",
+ libcfs_nid2str(conn->ibc_peer->ibp_nid), rc);
+ kiblnd_tx_done(ni, tx);
+ /* tell peer it's over */
+ kiblnd_send_completion(rx->rx_conn, IBLND_MSG_PUT_NAK, rc,
+ rxmsg->ibm_u.putreq.ibprm_cookie);
+ break;
+ }
+
+ nob = offsetof(kib_putack_msg_t, ibpam_rd.rd_frags[tx->tx_nfrags]);
+ txmsg->ibm_u.putack.ibpam_src_cookie = rxmsg->ibm_u.putreq.ibprm_cookie;
+ txmsg->ibm_u.putack.ibpam_dst_cookie = tx->tx_cookie;
+
+ kiblnd_init_tx_msg(ni, tx, IBLND_MSG_PUT_ACK, nob);
+
+ tx->tx_lntmsg[0] = lntmsg; /* finalise lntmsg on completion */
+ tx->tx_waiting = 1; /* waiting for PUT_DONE */
+ kiblnd_queue_tx(tx, conn);
+
+ /* reposted buffer reserved for PUT_DONE */
+ post_credit = IBLND_POSTRX_NO_CREDIT;
+ break;
+
+ case IBLND_MSG_GET_REQ:
+ if (lntmsg != NULL) {
+ /* Optimized GET; RDMA lntmsg's payload */
+ kiblnd_reply(ni, rx, lntmsg);
+ } else {
+ /* GET didn't match anything */
+ kiblnd_send_completion(rx->rx_conn, IBLND_MSG_GET_DONE,
+ -ENODATA,
+ rxmsg->ibm_u.get.ibgm_cookie);
+ }
+ break;
+ }
+
+ kiblnd_post_rx(rx, post_credit);
+ return rc;
+}
+
+int
+kiblnd_thread_start(int (*fn)(void *arg), void *arg, char *name)
+{
+ struct task_struct *task = kthread_run(fn, arg, "%s", name);
+
+ if (IS_ERR(task))
+ return PTR_ERR(task);
+
+ atomic_inc(&kiblnd_data.kib_nthreads);
+ return 0;
+}
+
+static void
+kiblnd_thread_fini(void)
+{
+ atomic_dec(&kiblnd_data.kib_nthreads);
+}
+
+void
+kiblnd_peer_alive(kib_peer_t *peer)
+{
+ /* This is racy, but everyone's only writing cfs_time_current() */
+ peer->ibp_last_alive = cfs_time_current();
+ mb();
+}
+
+static void
+kiblnd_peer_notify(kib_peer_t *peer)
+{
+ int error = 0;
+ unsigned long last_alive = 0;
+ unsigned long flags;
+
+ read_lock_irqsave(&kiblnd_data.kib_global_lock, flags);
+
+ if (list_empty(&peer->ibp_conns) &&
+ peer->ibp_accepting == 0 &&
+ peer->ibp_connecting == 0 &&
+ peer->ibp_error != 0) {
+ error = peer->ibp_error;
+ peer->ibp_error = 0;
+
+ last_alive = peer->ibp_last_alive;
+ }
+
+ read_unlock_irqrestore(&kiblnd_data.kib_global_lock, flags);
+
+ if (error != 0)
+ lnet_notify(peer->ibp_ni,
+ peer->ibp_nid, 0, last_alive);
+}
+
+void
+kiblnd_close_conn_locked(kib_conn_t *conn, int error)
+{
+ /* This just does the immediate housekeeping. 'error' is zero for a
+ * normal shutdown which can happen only after the connection has been
+ * established. If the connection is established, schedule the
+ * connection to be finished off by the connd. Otherwise the connd is
+ * already dealing with it (either to set it up or tear it down).
+ * Caller holds kib_global_lock exclusively in irq context */
+ kib_peer_t *peer = conn->ibc_peer;
+ kib_dev_t *dev;
+ unsigned long flags;
+
+ LASSERT(error != 0 || conn->ibc_state >= IBLND_CONN_ESTABLISHED);
+
+ if (error != 0 && conn->ibc_comms_error == 0)
+ conn->ibc_comms_error = error;
+
+ if (conn->ibc_state != IBLND_CONN_ESTABLISHED)
+ return; /* already being handled */
+
+ if (error == 0 &&
+ list_empty(&conn->ibc_tx_noops) &&
+ list_empty(&conn->ibc_tx_queue) &&
+ list_empty(&conn->ibc_tx_queue_rsrvd) &&
+ list_empty(&conn->ibc_tx_queue_nocred) &&
+ list_empty(&conn->ibc_active_txs)) {
+ CDEBUG(D_NET, "closing conn to %s\n",
+ libcfs_nid2str(peer->ibp_nid));
+ } else {
+ CNETERR("Closing conn to %s: error %d%s%s%s%s%s\n",
+ libcfs_nid2str(peer->ibp_nid), error,
+ list_empty(&conn->ibc_tx_queue) ? "" : "(sending)",
+ list_empty(&conn->ibc_tx_noops) ? "" : "(sending_noops)",
+ list_empty(&conn->ibc_tx_queue_rsrvd) ? "" : "(sending_rsrvd)",
+ list_empty(&conn->ibc_tx_queue_nocred) ? "" : "(sending_nocred)",
+ list_empty(&conn->ibc_active_txs) ? "" : "(waiting)");
+ }
+
+ dev = ((kib_net_t *)peer->ibp_ni->ni_data)->ibn_dev;
+ list_del(&conn->ibc_list);
+ /* connd (see below) takes over ibc_list's ref */
+
+ if (list_empty(&peer->ibp_conns) && /* no more conns */
+ kiblnd_peer_active(peer)) { /* still in peer table */
+ kiblnd_unlink_peer_locked(peer);
+
+ /* set/clear error on last conn */
+ peer->ibp_error = conn->ibc_comms_error;
+ }
+
+ kiblnd_set_conn_state(conn, IBLND_CONN_CLOSING);
+
+ if (error != 0 &&
+ kiblnd_dev_can_failover(dev)) {
+ list_add_tail(&dev->ibd_fail_list,
+ &kiblnd_data.kib_failed_devs);
+ wake_up(&kiblnd_data.kib_failover_waitq);
+ }
+
+ spin_lock_irqsave(&kiblnd_data.kib_connd_lock, flags);
+
+ list_add_tail(&conn->ibc_list, &kiblnd_data.kib_connd_conns);
+ wake_up(&kiblnd_data.kib_connd_waitq);
+
+ spin_unlock_irqrestore(&kiblnd_data.kib_connd_lock, flags);
+}
+
+void
+kiblnd_close_conn(kib_conn_t *conn, int error)
+{
+ unsigned long flags;
+
+ write_lock_irqsave(&kiblnd_data.kib_global_lock, flags);
+
+ kiblnd_close_conn_locked(conn, error);
+
+ write_unlock_irqrestore(&kiblnd_data.kib_global_lock, flags);
+}
+
+static void
+kiblnd_handle_early_rxs(kib_conn_t *conn)
+{
+ unsigned long flags;
+ kib_rx_t *rx;
+ kib_rx_t *tmp;
+
+ LASSERT(!in_interrupt());
+ LASSERT(conn->ibc_state >= IBLND_CONN_ESTABLISHED);
+
+ write_lock_irqsave(&kiblnd_data.kib_global_lock, flags);
+ list_for_each_entry_safe(rx, tmp, &conn->ibc_early_rxs, rx_list) {
+ list_del(&rx->rx_list);
+ write_unlock_irqrestore(&kiblnd_data.kib_global_lock, flags);
+
+ kiblnd_handle_rx(rx);
+
+ write_lock_irqsave(&kiblnd_data.kib_global_lock, flags);
+ }
+ write_unlock_irqrestore(&kiblnd_data.kib_global_lock, flags);
+}
+
+static void
+kiblnd_abort_txs(kib_conn_t *conn, struct list_head *txs)
+{
+ LIST_HEAD(zombies);
+ struct list_head *tmp;
+ struct list_head *nxt;
+ kib_tx_t *tx;
+
+ spin_lock(&conn->ibc_lock);
+
+ list_for_each_safe(tmp, nxt, txs) {
+ tx = list_entry(tmp, kib_tx_t, tx_list);
+
+ if (txs == &conn->ibc_active_txs) {
+ LASSERT(!tx->tx_queued);
+ LASSERT(tx->tx_waiting ||
+ tx->tx_sending != 0);
+ } else {
+ LASSERT(tx->tx_queued);
+ }
+
+ tx->tx_status = -ECONNABORTED;
+ tx->tx_waiting = 0;
+
+ if (tx->tx_sending == 0) {
+ tx->tx_queued = 0;
+ list_del(&tx->tx_list);
+ list_add(&tx->tx_list, &zombies);
+ }
+ }
+
+ spin_unlock(&conn->ibc_lock);
+
+ kiblnd_txlist_done(conn->ibc_peer->ibp_ni, &zombies, -ECONNABORTED);
+}
+
+static void
+kiblnd_finalise_conn(kib_conn_t *conn)
+{
+ LASSERT(!in_interrupt());
+ LASSERT(conn->ibc_state > IBLND_CONN_INIT);
+
+ kiblnd_set_conn_state(conn, IBLND_CONN_DISCONNECTED);
+
+ /* abort_receives moves QP state to IB_QPS_ERR. This is only required
+ * for connections that didn't get as far as being connected, because
+ * rdma_disconnect() does this for free. */
+ kiblnd_abort_receives(conn);
+
+ /* Complete all tx descs not waiting for sends to complete.
+ * NB we should be safe from RDMA now that the QP has changed state */
+
+ kiblnd_abort_txs(conn, &conn->ibc_tx_noops);
+ kiblnd_abort_txs(conn, &conn->ibc_tx_queue);
+ kiblnd_abort_txs(conn, &conn->ibc_tx_queue_rsrvd);
+ kiblnd_abort_txs(conn, &conn->ibc_tx_queue_nocred);
+ kiblnd_abort_txs(conn, &conn->ibc_active_txs);
+
+ kiblnd_handle_early_rxs(conn);
+}
+
+void
+kiblnd_peer_connect_failed(kib_peer_t *peer, int active, int error)
+{
+ LIST_HEAD(zombies);
+ unsigned long flags;
+
+ LASSERT(error != 0);
+ LASSERT(!in_interrupt());
+
+ write_lock_irqsave(&kiblnd_data.kib_global_lock, flags);
+
+ if (active) {
+ LASSERT(peer->ibp_connecting > 0);
+ peer->ibp_connecting--;
+ } else {
+ LASSERT(peer->ibp_accepting > 0);
+ peer->ibp_accepting--;
+ }
+
+ if (peer->ibp_connecting != 0 ||
+ peer->ibp_accepting != 0) {
+ /* another connection attempt under way... */
+ write_unlock_irqrestore(&kiblnd_data.kib_global_lock,
+ flags);
+ return;
+ }
+
+ if (list_empty(&peer->ibp_conns)) {
+ /* Take peer's blocked transmits to complete with error */
+ list_add(&zombies, &peer->ibp_tx_queue);
+ list_del_init(&peer->ibp_tx_queue);
+
+ if (kiblnd_peer_active(peer))
+ kiblnd_unlink_peer_locked(peer);
+
+ peer->ibp_error = error;
+ } else {
+ /* Can't have blocked transmits if there are connections */
+ LASSERT(list_empty(&peer->ibp_tx_queue));
+ }
+
+ write_unlock_irqrestore(&kiblnd_data.kib_global_lock, flags);
+
+ kiblnd_peer_notify(peer);
+
+ if (list_empty(&zombies))
+ return;
+
+ CNETERR("Deleting messages for %s: connection failed\n",
+ libcfs_nid2str(peer->ibp_nid));
+
+ kiblnd_txlist_done(peer->ibp_ni, &zombies, -EHOSTUNREACH);
+}
+
+void
+kiblnd_connreq_done(kib_conn_t *conn, int status)
+{
+ kib_peer_t *peer = conn->ibc_peer;
+ kib_tx_t *tx;
+ kib_tx_t *tmp;
+ struct list_head txs;
+ unsigned long flags;
+ int active;
+
+ active = (conn->ibc_state == IBLND_CONN_ACTIVE_CONNECT);
+
+ CDEBUG(D_NET, "%s: active(%d), version(%x), status(%d)\n",
+ libcfs_nid2str(peer->ibp_nid), active,
+ conn->ibc_version, status);
+
+ LASSERT(!in_interrupt());
+ LASSERT((conn->ibc_state == IBLND_CONN_ACTIVE_CONNECT &&
+ peer->ibp_connecting > 0) ||
+ (conn->ibc_state == IBLND_CONN_PASSIVE_WAIT &&
+ peer->ibp_accepting > 0));
+
+ LIBCFS_FREE(conn->ibc_connvars, sizeof(*conn->ibc_connvars));
+ conn->ibc_connvars = NULL;
+
+ if (status != 0) {
+ /* failed to establish connection */
+ kiblnd_peer_connect_failed(peer, active, status);
+ kiblnd_finalise_conn(conn);
+ return;
+ }
+
+ /* connection established */
+ write_lock_irqsave(&kiblnd_data.kib_global_lock, flags);
+
+ conn->ibc_last_send = jiffies;
+ kiblnd_set_conn_state(conn, IBLND_CONN_ESTABLISHED);
+ kiblnd_peer_alive(peer);
+
+ /* Add conn to peer's list and nuke any dangling conns from a different
+ * peer instance... */
+ kiblnd_conn_addref(conn); /* +1 ref for ibc_list */
+ list_add(&conn->ibc_list, &peer->ibp_conns);
+ if (active)
+ peer->ibp_connecting--;
+ else
+ peer->ibp_accepting--;
+
+ if (peer->ibp_version == 0) {
+ peer->ibp_version = conn->ibc_version;
+ peer->ibp_incarnation = conn->ibc_incarnation;
+ }
+
+ if (peer->ibp_version != conn->ibc_version ||
+ peer->ibp_incarnation != conn->ibc_incarnation) {
+ kiblnd_close_stale_conns_locked(peer, conn->ibc_version,
+ conn->ibc_incarnation);
+ peer->ibp_version = conn->ibc_version;
+ peer->ibp_incarnation = conn->ibc_incarnation;
+ }
+
+ /* grab pending txs while I have the lock */
+ list_add(&txs, &peer->ibp_tx_queue);
+ list_del_init(&peer->ibp_tx_queue);
+
+ if (!kiblnd_peer_active(peer) || /* peer has been deleted */
+ conn->ibc_comms_error != 0) { /* error has happened already */
+ lnet_ni_t *ni = peer->ibp_ni;
+
+ /* start to shut down connection */
+ kiblnd_close_conn_locked(conn, -ECONNABORTED);
+ write_unlock_irqrestore(&kiblnd_data.kib_global_lock, flags);
+
+ kiblnd_txlist_done(ni, &txs, -ECONNABORTED);
+
+ return;
+ }
+
+ write_unlock_irqrestore(&kiblnd_data.kib_global_lock, flags);
+
+ /* Schedule blocked txs */
+ spin_lock(&conn->ibc_lock);
+ list_for_each_entry_safe(tx, tmp, &txs, tx_list) {
+ list_del(&tx->tx_list);
+
+ kiblnd_queue_tx_locked(tx, conn);
+ }
+ spin_unlock(&conn->ibc_lock);
+
+ kiblnd_check_sends(conn);
+
+ /* schedule blocked rxs */
+ kiblnd_handle_early_rxs(conn);
+}
+
+static void
+kiblnd_reject(struct rdma_cm_id *cmid, kib_rej_t *rej)
+{
+ int rc;
+
+ rc = rdma_reject(cmid, rej, sizeof(*rej));
+
+ if (rc != 0)
+ CWARN("Error %d sending reject\n", rc);
+}
+
+static int
+kiblnd_passive_connect(struct rdma_cm_id *cmid, void *priv, int priv_nob)
+{
+ rwlock_t *g_lock = &kiblnd_data.kib_global_lock;
+ kib_msg_t *reqmsg = priv;
+ kib_msg_t *ackmsg;
+ kib_dev_t *ibdev;
+ kib_peer_t *peer;
+ kib_peer_t *peer2;
+ kib_conn_t *conn;
+ lnet_ni_t *ni = NULL;
+ kib_net_t *net = NULL;
+ lnet_nid_t nid;
+ struct rdma_conn_param cp;
+ kib_rej_t rej;
+ int version = IBLND_MSG_VERSION;
+ unsigned long flags;
+ int rc;
+ struct sockaddr_in *peer_addr;
+ LASSERT(!in_interrupt());
+
+ /* cmid inherits 'context' from the corresponding listener id */
+ ibdev = (kib_dev_t *)cmid->context;
+ LASSERT(ibdev != NULL);
+
+ memset(&rej, 0, sizeof(rej));
+ rej.ibr_magic = IBLND_MSG_MAGIC;
+ rej.ibr_why = IBLND_REJECT_FATAL;
+ rej.ibr_cp.ibcp_max_msg_size = IBLND_MSG_SIZE;
+
+ peer_addr = (struct sockaddr_in *)&(cmid->route.addr.dst_addr);
+ if (*kiblnd_tunables.kib_require_priv_port &&
+ ntohs(peer_addr->sin_port) >= PROT_SOCK) {
+ __u32 ip = ntohl(peer_addr->sin_addr.s_addr);
+ CERROR("Peer's port (%pI4h:%hu) is not privileged\n",
+ &ip, ntohs(peer_addr->sin_port));
+ goto failed;
+ }
+
+ if (priv_nob < offsetof(kib_msg_t, ibm_type)) {
+ CERROR("Short connection request\n");
+ goto failed;
+ }
+
+ /* Future protocol version compatibility support! If the
+ * o2iblnd-specific protocol changes, or when LNET unifies
+ * protocols over all LNDs, the initial connection will
+ * negotiate a protocol version. I trap this here to avoid
+ * console errors; the reject tells the peer which protocol I
+ * speak. */
+ if (reqmsg->ibm_magic == LNET_PROTO_MAGIC ||
+ reqmsg->ibm_magic == __swab32(LNET_PROTO_MAGIC))
+ goto failed;
+ if (reqmsg->ibm_magic == IBLND_MSG_MAGIC &&
+ reqmsg->ibm_version != IBLND_MSG_VERSION &&
+ reqmsg->ibm_version != IBLND_MSG_VERSION_1)
+ goto failed;
+ if (reqmsg->ibm_magic == __swab32(IBLND_MSG_MAGIC) &&
+ reqmsg->ibm_version != __swab16(IBLND_MSG_VERSION) &&
+ reqmsg->ibm_version != __swab16(IBLND_MSG_VERSION_1))
+ goto failed;
+
+ rc = kiblnd_unpack_msg(reqmsg, priv_nob);
+ if (rc != 0) {
+ CERROR("Can't parse connection request: %d\n", rc);
+ goto failed;
+ }
+
+ nid = reqmsg->ibm_srcnid;
+ ni = lnet_net2ni(LNET_NIDNET(reqmsg->ibm_dstnid));
+
+ if (ni != NULL) {
+ net = (kib_net_t *)ni->ni_data;
+ rej.ibr_incarnation = net->ibn_incarnation;
+ }
+
+ if (ni == NULL || /* no matching net */
+ ni->ni_nid != reqmsg->ibm_dstnid || /* right NET, wrong NID! */
+ net->ibn_dev != ibdev) { /* wrong device */
+ CERROR("Can't accept %s on %s (%s:%d:%pI4h): bad dst nid %s\n",
+ libcfs_nid2str(nid),
+ ni == NULL ? "NA" : libcfs_nid2str(ni->ni_nid),
+ ibdev->ibd_ifname, ibdev->ibd_nnets,
+ &ibdev->ibd_ifip,
+ libcfs_nid2str(reqmsg->ibm_dstnid));
+
+ goto failed;
+ }
+
+ /* check time stamp as soon as possible */
+ if (reqmsg->ibm_dststamp != 0 &&
+ reqmsg->ibm_dststamp != net->ibn_incarnation) {
+ CWARN("Stale connection request\n");
+ rej.ibr_why = IBLND_REJECT_CONN_STALE;
+ goto failed;
+ }
+
+ /* I can accept peer's version */
+ version = reqmsg->ibm_version;
+
+ if (reqmsg->ibm_type != IBLND_MSG_CONNREQ) {
+ CERROR("Unexpected connreq msg type: %x from %s\n",
+ reqmsg->ibm_type, libcfs_nid2str(nid));
+ goto failed;
+ }
+
+ if (reqmsg->ibm_u.connparams.ibcp_queue_depth !=
+ IBLND_MSG_QUEUE_SIZE(version)) {
+ CERROR("Can't accept %s: incompatible queue depth %d (%d wanted)\n",
+ libcfs_nid2str(nid), reqmsg->ibm_u.connparams.ibcp_queue_depth,
+ IBLND_MSG_QUEUE_SIZE(version));
+
+ if (version == IBLND_MSG_VERSION)
+ rej.ibr_why = IBLND_REJECT_MSG_QUEUE_SIZE;
+
+ goto failed;
+ }
+
+ if (reqmsg->ibm_u.connparams.ibcp_max_frags !=
+ IBLND_RDMA_FRAGS(version)) {
+ CERROR("Can't accept %s(version %x): incompatible max_frags %d (%d wanted)\n",
+ libcfs_nid2str(nid), version,
+ reqmsg->ibm_u.connparams.ibcp_max_frags,
+ IBLND_RDMA_FRAGS(version));
+
+ if (version == IBLND_MSG_VERSION)
+ rej.ibr_why = IBLND_REJECT_RDMA_FRAGS;
+
+ goto failed;
+
+ }
+
+ if (reqmsg->ibm_u.connparams.ibcp_max_msg_size > IBLND_MSG_SIZE) {
+ CERROR("Can't accept %s: message size %d too big (%d max)\n",
+ libcfs_nid2str(nid),
+ reqmsg->ibm_u.connparams.ibcp_max_msg_size,
+ IBLND_MSG_SIZE);
+ goto failed;
+ }
+
+ /* assume 'nid' is a new peer; create */
+ rc = kiblnd_create_peer(ni, &peer, nid);
+ if (rc != 0) {
+ CERROR("Can't create peer for %s\n", libcfs_nid2str(nid));
+ rej.ibr_why = IBLND_REJECT_NO_RESOURCES;
+ goto failed;
+ }
+
+ write_lock_irqsave(g_lock, flags);
+
+ peer2 = kiblnd_find_peer_locked(nid);
+ if (peer2 != NULL) {
+ if (peer2->ibp_version == 0) {
+ peer2->ibp_version = version;
+ peer2->ibp_incarnation = reqmsg->ibm_srcstamp;
+ }
+
+ /* not the guy I've talked with */
+ if (peer2->ibp_incarnation != reqmsg->ibm_srcstamp ||
+ peer2->ibp_version != version) {
+ kiblnd_close_peer_conns_locked(peer2, -ESTALE);
+ write_unlock_irqrestore(g_lock, flags);
+
+ CWARN("Conn stale %s [old ver: %x, new ver: %x]\n",
+ libcfs_nid2str(nid), peer2->ibp_version, version);
+
+ kiblnd_peer_decref(peer);
+ rej.ibr_why = IBLND_REJECT_CONN_STALE;
+ goto failed;
+ }
+
+ /* tie-break connection race in favour of the higher NID */
+ if (peer2->ibp_connecting != 0 &&
+ nid < ni->ni_nid) {
+ write_unlock_irqrestore(g_lock, flags);
+
+ CWARN("Conn race %s\n", libcfs_nid2str(peer2->ibp_nid));
+
+ kiblnd_peer_decref(peer);
+ rej.ibr_why = IBLND_REJECT_CONN_RACE;
+ goto failed;
+ }
+
+ peer2->ibp_accepting++;
+ kiblnd_peer_addref(peer2);
+
+ write_unlock_irqrestore(g_lock, flags);
+ kiblnd_peer_decref(peer);
+ peer = peer2;
+ } else {
+ /* Brand new peer */
+ LASSERT(peer->ibp_accepting == 0);
+ LASSERT(peer->ibp_version == 0 &&
+ peer->ibp_incarnation == 0);
+
+ peer->ibp_accepting = 1;
+ peer->ibp_version = version;
+ peer->ibp_incarnation = reqmsg->ibm_srcstamp;
+
+ /* I have a ref on ni that prevents it being shutdown */
+ LASSERT(net->ibn_shutdown == 0);
+
+ kiblnd_peer_addref(peer);
+ list_add_tail(&peer->ibp_list, kiblnd_nid2peerlist(nid));
+
+ write_unlock_irqrestore(g_lock, flags);
+ }
+
+ conn = kiblnd_create_conn(peer, cmid, IBLND_CONN_PASSIVE_WAIT, version);
+ if (conn == NULL) {
+ kiblnd_peer_connect_failed(peer, 0, -ENOMEM);
+ kiblnd_peer_decref(peer);
+ rej.ibr_why = IBLND_REJECT_NO_RESOURCES;
+ goto failed;
+ }
+
+ /* conn now "owns" cmid, so I return success from here on to ensure the
+ * CM callback doesn't destroy cmid. */
+
+ conn->ibc_incarnation = reqmsg->ibm_srcstamp;
+ conn->ibc_credits = IBLND_MSG_QUEUE_SIZE(version);
+ conn->ibc_reserved_credits = IBLND_MSG_QUEUE_SIZE(version);
+ LASSERT(conn->ibc_credits + conn->ibc_reserved_credits + IBLND_OOB_MSGS(version)
+ <= IBLND_RX_MSGS(version));
+
+ ackmsg = &conn->ibc_connvars->cv_msg;
+ memset(ackmsg, 0, sizeof(*ackmsg));
+
+ kiblnd_init_msg(ackmsg, IBLND_MSG_CONNACK,
+ sizeof(ackmsg->ibm_u.connparams));
+ ackmsg->ibm_u.connparams.ibcp_queue_depth = IBLND_MSG_QUEUE_SIZE(version);
+ ackmsg->ibm_u.connparams.ibcp_max_msg_size = IBLND_MSG_SIZE;
+ ackmsg->ibm_u.connparams.ibcp_max_frags = IBLND_RDMA_FRAGS(version);
+
+ kiblnd_pack_msg(ni, ackmsg, version, 0, nid, reqmsg->ibm_srcstamp);
+
+ memset(&cp, 0, sizeof(cp));
+ cp.private_data = ackmsg;
+ cp.private_data_len = ackmsg->ibm_nob;
+ cp.responder_resources = 0; /* No atomic ops or RDMA reads */
+ cp.initiator_depth = 0;
+ cp.flow_control = 1;
+ cp.retry_count = *kiblnd_tunables.kib_retry_count;
+ cp.rnr_retry_count = *kiblnd_tunables.kib_rnr_retry_count;
+
+ CDEBUG(D_NET, "Accept %s\n", libcfs_nid2str(nid));
+
+ rc = rdma_accept(cmid, &cp);
+ if (rc != 0) {
+ CERROR("Can't accept %s: %d\n", libcfs_nid2str(nid), rc);
+ rej.ibr_version = version;
+ rej.ibr_why = IBLND_REJECT_FATAL;
+
+ kiblnd_reject(cmid, &rej);
+ kiblnd_connreq_done(conn, rc);
+ kiblnd_conn_decref(conn);
+ }
+
+ lnet_ni_decref(ni);
+ return 0;
+
+ failed:
+ if (ni != NULL)
+ lnet_ni_decref(ni);
+
+ rej.ibr_version = version;
+ rej.ibr_cp.ibcp_queue_depth = IBLND_MSG_QUEUE_SIZE(version);
+ rej.ibr_cp.ibcp_max_frags = IBLND_RDMA_FRAGS(version);
+ kiblnd_reject(cmid, &rej);
+
+ return -ECONNREFUSED;
+}
+
+static void
+kiblnd_reconnect(kib_conn_t *conn, int version,
+ __u64 incarnation, int why, kib_connparams_t *cp)
+{
+ kib_peer_t *peer = conn->ibc_peer;
+ char *reason;
+ int retry = 0;
+ unsigned long flags;
+
+ LASSERT(conn->ibc_state == IBLND_CONN_ACTIVE_CONNECT);
+ LASSERT(peer->ibp_connecting > 0); /* 'conn' at least */
+
+ write_lock_irqsave(&kiblnd_data.kib_global_lock, flags);
+
+ /* retry connection if it's still needed and no other connection
+ * attempts (active or passive) are in progress
+ * NB: reconnect is still needed even when ibp_tx_queue is
+ * empty if ibp_version != version because reconnect may be
+ * initiated by kiblnd_query() */
+ if ((!list_empty(&peer->ibp_tx_queue) ||
+ peer->ibp_version != version) &&
+ peer->ibp_connecting == 1 &&
+ peer->ibp_accepting == 0) {
+ retry = 1;
+ peer->ibp_connecting++;
+
+ peer->ibp_version = version;
+ peer->ibp_incarnation = incarnation;
+ }
+
+ write_unlock_irqrestore(&kiblnd_data.kib_global_lock, flags);
+
+ if (!retry)
+ return;
+
+ switch (why) {
+ default:
+ reason = "Unknown";
+ break;
+
+ case IBLND_REJECT_CONN_STALE:
+ reason = "stale";
+ break;
+
+ case IBLND_REJECT_CONN_RACE:
+ reason = "conn race";
+ break;
+
+ case IBLND_REJECT_CONN_UNCOMPAT:
+ reason = "version negotiation";
+ break;
+ }
+
+ CNETERR("%s: retrying (%s), %x, %x, queue_dep: %d, max_frag: %d, msg_size: %d\n",
+ libcfs_nid2str(peer->ibp_nid),
+ reason, IBLND_MSG_VERSION, version,
+ cp != NULL ? cp->ibcp_queue_depth : IBLND_MSG_QUEUE_SIZE(version),
+ cp != NULL ? cp->ibcp_max_frags : IBLND_RDMA_FRAGS(version),
+ cp != NULL ? cp->ibcp_max_msg_size : IBLND_MSG_SIZE);
+
+ kiblnd_connect_peer(peer);
+}
+
+static void
+kiblnd_rejected(kib_conn_t *conn, int reason, void *priv, int priv_nob)
+{
+ kib_peer_t *peer = conn->ibc_peer;
+
+ LASSERT(!in_interrupt());
+ LASSERT(conn->ibc_state == IBLND_CONN_ACTIVE_CONNECT);
+
+ switch (reason) {
+ case IB_CM_REJ_STALE_CONN:
+ kiblnd_reconnect(conn, IBLND_MSG_VERSION, 0,
+ IBLND_REJECT_CONN_STALE, NULL);
+ break;
+
+ case IB_CM_REJ_INVALID_SERVICE_ID:
+ CNETERR("%s rejected: no listener at %d\n",
+ libcfs_nid2str(peer->ibp_nid),
+ *kiblnd_tunables.kib_service);
+ break;
+
+ case IB_CM_REJ_CONSUMER_DEFINED:
+ if (priv_nob >= offsetof(kib_rej_t, ibr_padding)) {
+ kib_rej_t *rej = priv;
+ kib_connparams_t *cp = NULL;
+ int flip = 0;
+ __u64 incarnation = -1;
+
+ /* NB. default incarnation is -1 because:
+ * a) V1 will ignore dst incarnation in connreq.
+ * b) V2 will provide incarnation while rejecting me,
+ * -1 will be overwrote.
+ *
+ * if I try to connect to a V1 peer with V2 protocol,
+ * it rejected me then upgrade to V2, I have no idea
+ * about the upgrading and try to reconnect with V1,
+ * in this case upgraded V2 can find out I'm trying to
+ * talk to the old guy and reject me(incarnation is -1).
+ */
+
+ if (rej->ibr_magic == __swab32(IBLND_MSG_MAGIC) ||
+ rej->ibr_magic == __swab32(LNET_PROTO_MAGIC)) {
+ __swab32s(&rej->ibr_magic);
+ __swab16s(&rej->ibr_version);
+ flip = 1;
+ }
+
+ if (priv_nob >= sizeof(kib_rej_t) &&
+ rej->ibr_version > IBLND_MSG_VERSION_1) {
+ /* priv_nob is always 148 in current version
+ * of OFED, so we still need to check version.
+ * (define of IB_CM_REJ_PRIVATE_DATA_SIZE) */
+ cp = &rej->ibr_cp;
+
+ if (flip) {
+ __swab64s(&rej->ibr_incarnation);
+ __swab16s(&cp->ibcp_queue_depth);
+ __swab16s(&cp->ibcp_max_frags);
+ __swab32s(&cp->ibcp_max_msg_size);
+ }
+
+ incarnation = rej->ibr_incarnation;
+ }
+
+ if (rej->ibr_magic != IBLND_MSG_MAGIC &&
+ rej->ibr_magic != LNET_PROTO_MAGIC) {
+ CERROR("%s rejected: consumer defined fatal error\n",
+ libcfs_nid2str(peer->ibp_nid));
+ break;
+ }
+
+ if (rej->ibr_version != IBLND_MSG_VERSION &&
+ rej->ibr_version != IBLND_MSG_VERSION_1) {
+ CERROR("%s rejected: o2iblnd version %x error\n",
+ libcfs_nid2str(peer->ibp_nid),
+ rej->ibr_version);
+ break;
+ }
+
+ if (rej->ibr_why == IBLND_REJECT_FATAL &&
+ rej->ibr_version == IBLND_MSG_VERSION_1) {
+ CDEBUG(D_NET, "rejected by old version peer %s: %x\n",
+ libcfs_nid2str(peer->ibp_nid), rej->ibr_version);
+
+ if (conn->ibc_version != IBLND_MSG_VERSION_1)
+ rej->ibr_why = IBLND_REJECT_CONN_UNCOMPAT;
+ }
+
+ switch (rej->ibr_why) {
+ case IBLND_REJECT_CONN_RACE:
+ case IBLND_REJECT_CONN_STALE:
+ case IBLND_REJECT_CONN_UNCOMPAT:
+ kiblnd_reconnect(conn, rej->ibr_version,
+ incarnation, rej->ibr_why, cp);
+ break;
+
+ case IBLND_REJECT_MSG_QUEUE_SIZE:
+ CERROR("%s rejected: incompatible message queue depth %d, %d\n",
+ libcfs_nid2str(peer->ibp_nid),
+ cp != NULL ? cp->ibcp_queue_depth :
+ IBLND_MSG_QUEUE_SIZE(rej->ibr_version),
+ IBLND_MSG_QUEUE_SIZE(conn->ibc_version));
+ break;
+
+ case IBLND_REJECT_RDMA_FRAGS:
+ CERROR("%s rejected: incompatible # of RDMA fragments %d, %d\n",
+ libcfs_nid2str(peer->ibp_nid),
+ cp != NULL ? cp->ibcp_max_frags :
+ IBLND_RDMA_FRAGS(rej->ibr_version),
+ IBLND_RDMA_FRAGS(conn->ibc_version));
+ break;
+
+ case IBLND_REJECT_NO_RESOURCES:
+ CERROR("%s rejected: o2iblnd no resources\n",
+ libcfs_nid2str(peer->ibp_nid));
+ break;
+
+ case IBLND_REJECT_FATAL:
+ CERROR("%s rejected: o2iblnd fatal error\n",
+ libcfs_nid2str(peer->ibp_nid));
+ break;
+
+ default:
+ CERROR("%s rejected: o2iblnd reason %d\n",
+ libcfs_nid2str(peer->ibp_nid),
+ rej->ibr_why);
+ break;
+ }
+ break;
+ }
+ /* fall through */
+ default:
+ CNETERR("%s rejected: reason %d, size %d\n",
+ libcfs_nid2str(peer->ibp_nid), reason, priv_nob);
+ break;
+ }
+
+ kiblnd_connreq_done(conn, -ECONNREFUSED);
+}
+
+static void
+kiblnd_check_connreply(kib_conn_t *conn, void *priv, int priv_nob)
+{
+ kib_peer_t *peer = conn->ibc_peer;
+ lnet_ni_t *ni = peer->ibp_ni;
+ kib_net_t *net = ni->ni_data;
+ kib_msg_t *msg = priv;
+ int ver = conn->ibc_version;
+ int rc = kiblnd_unpack_msg(msg, priv_nob);
+ unsigned long flags;
+
+ LASSERT(net != NULL);
+
+ if (rc != 0) {
+ CERROR("Can't unpack connack from %s: %d\n",
+ libcfs_nid2str(peer->ibp_nid), rc);
+ goto failed;
+ }
+
+ if (msg->ibm_type != IBLND_MSG_CONNACK) {
+ CERROR("Unexpected message %d from %s\n",
+ msg->ibm_type, libcfs_nid2str(peer->ibp_nid));
+ rc = -EPROTO;
+ goto failed;
+ }
+
+ if (ver != msg->ibm_version) {
+ CERROR("%s replied version %x is different with requested version %x\n",
+ libcfs_nid2str(peer->ibp_nid), msg->ibm_version, ver);
+ rc = -EPROTO;
+ goto failed;
+ }
+
+ if (msg->ibm_u.connparams.ibcp_queue_depth !=
+ IBLND_MSG_QUEUE_SIZE(ver)) {
+ CERROR("%s has incompatible queue depth %d(%d wanted)\n",
+ libcfs_nid2str(peer->ibp_nid),
+ msg->ibm_u.connparams.ibcp_queue_depth,
+ IBLND_MSG_QUEUE_SIZE(ver));
+ rc = -EPROTO;
+ goto failed;
+ }
+
+ if (msg->ibm_u.connparams.ibcp_max_frags !=
+ IBLND_RDMA_FRAGS(ver)) {
+ CERROR("%s has incompatible max_frags %d (%d wanted)\n",
+ libcfs_nid2str(peer->ibp_nid),
+ msg->ibm_u.connparams.ibcp_max_frags,
+ IBLND_RDMA_FRAGS(ver));
+ rc = -EPROTO;
+ goto failed;
+ }
+
+ if (msg->ibm_u.connparams.ibcp_max_msg_size > IBLND_MSG_SIZE) {
+ CERROR("%s max message size %d too big (%d max)\n",
+ libcfs_nid2str(peer->ibp_nid),
+ msg->ibm_u.connparams.ibcp_max_msg_size,
+ IBLND_MSG_SIZE);
+ rc = -EPROTO;
+ goto failed;
+ }
+
+ read_lock_irqsave(&kiblnd_data.kib_global_lock, flags);
+ if (msg->ibm_dstnid == ni->ni_nid &&
+ msg->ibm_dststamp == net->ibn_incarnation)
+ rc = 0;
+ else
+ rc = -ESTALE;
+ read_unlock_irqrestore(&kiblnd_data.kib_global_lock, flags);
+
+ if (rc != 0) {
+ CERROR("Bad connection reply from %s, rc = %d, version: %x max_frags: %d\n",
+ libcfs_nid2str(peer->ibp_nid), rc,
+ msg->ibm_version, msg->ibm_u.connparams.ibcp_max_frags);
+ goto failed;
+ }
+
+ conn->ibc_incarnation = msg->ibm_srcstamp;
+ conn->ibc_credits =
+ conn->ibc_reserved_credits = IBLND_MSG_QUEUE_SIZE(ver);
+ LASSERT(conn->ibc_credits + conn->ibc_reserved_credits + IBLND_OOB_MSGS(ver)
+ <= IBLND_RX_MSGS(ver));
+
+ kiblnd_connreq_done(conn, 0);
+ return;
+
+ failed:
+ /* NB My QP has already established itself, so I handle anything going
+ * wrong here by setting ibc_comms_error.
+ * kiblnd_connreq_done(0) moves the conn state to ESTABLISHED, but then
+ * immediately tears it down. */
+
+ LASSERT(rc != 0);
+ conn->ibc_comms_error = rc;
+ kiblnd_connreq_done(conn, 0);
+}
+
+static int
+kiblnd_active_connect(struct rdma_cm_id *cmid)
+{
+ kib_peer_t *peer = (kib_peer_t *)cmid->context;
+ kib_conn_t *conn;
+ kib_msg_t *msg;
+ struct rdma_conn_param cp;
+ int version;
+ __u64 incarnation;
+ unsigned long flags;
+ int rc;
+
+ read_lock_irqsave(&kiblnd_data.kib_global_lock, flags);
+
+ incarnation = peer->ibp_incarnation;
+ version = (peer->ibp_version == 0) ? IBLND_MSG_VERSION :
+ peer->ibp_version;
+
+ read_unlock_irqrestore(&kiblnd_data.kib_global_lock, flags);
+
+ conn = kiblnd_create_conn(peer, cmid, IBLND_CONN_ACTIVE_CONNECT, version);
+ if (conn == NULL) {
+ kiblnd_peer_connect_failed(peer, 1, -ENOMEM);
+ kiblnd_peer_decref(peer); /* lose cmid's ref */
+ return -ENOMEM;
+ }
+
+ /* conn "owns" cmid now, so I return success from here on to ensure the
+ * CM callback doesn't destroy cmid. conn also takes over cmid's ref
+ * on peer */
+
+ msg = &conn->ibc_connvars->cv_msg;
+
+ memset(msg, 0, sizeof(*msg));
+ kiblnd_init_msg(msg, IBLND_MSG_CONNREQ, sizeof(msg->ibm_u.connparams));
+ msg->ibm_u.connparams.ibcp_queue_depth = IBLND_MSG_QUEUE_SIZE(version);
+ msg->ibm_u.connparams.ibcp_max_frags = IBLND_RDMA_FRAGS(version);
+ msg->ibm_u.connparams.ibcp_max_msg_size = IBLND_MSG_SIZE;
+
+ kiblnd_pack_msg(peer->ibp_ni, msg, version,
+ 0, peer->ibp_nid, incarnation);
+
+ memset(&cp, 0, sizeof(cp));
+ cp.private_data = msg;
+ cp.private_data_len = msg->ibm_nob;
+ cp.responder_resources = 0; /* No atomic ops or RDMA reads */
+ cp.initiator_depth = 0;
+ cp.flow_control = 1;
+ cp.retry_count = *kiblnd_tunables.kib_retry_count;
+ cp.rnr_retry_count = *kiblnd_tunables.kib_rnr_retry_count;
+
+ LASSERT(cmid->context == (void *)conn);
+ LASSERT(conn->ibc_cmid == cmid);
+
+ rc = rdma_connect(cmid, &cp);
+ if (rc != 0) {
+ CERROR("Can't connect to %s: %d\n",
+ libcfs_nid2str(peer->ibp_nid), rc);
+ kiblnd_connreq_done(conn, rc);
+ kiblnd_conn_decref(conn);
+ }
+
+ return 0;
+}
+
+int
+kiblnd_cm_callback(struct rdma_cm_id *cmid, struct rdma_cm_event *event)
+{
+ kib_peer_t *peer;
+ kib_conn_t *conn;
+ int rc;
+
+ switch (event->event) {
+ default:
+ CERROR("Unexpected event: %d, status: %d\n",
+ event->event, event->status);
+ LBUG();
+
+ case RDMA_CM_EVENT_CONNECT_REQUEST:
+ /* destroy cmid on failure */
+ rc = kiblnd_passive_connect(cmid,
+ (void *)KIBLND_CONN_PARAM(event),
+ KIBLND_CONN_PARAM_LEN(event));
+ CDEBUG(D_NET, "connreq: %d\n", rc);
+ return rc;
+
+ case RDMA_CM_EVENT_ADDR_ERROR:
+ peer = (kib_peer_t *)cmid->context;
+ CNETERR("%s: ADDR ERROR %d\n",
+ libcfs_nid2str(peer->ibp_nid), event->status);
+ kiblnd_peer_connect_failed(peer, 1, -EHOSTUNREACH);
+ kiblnd_peer_decref(peer);
+ return -EHOSTUNREACH; /* rc != 0 destroys cmid */
+
+ case RDMA_CM_EVENT_ADDR_RESOLVED:
+ peer = (kib_peer_t *)cmid->context;
+
+ CDEBUG(D_NET, "%s Addr resolved: %d\n",
+ libcfs_nid2str(peer->ibp_nid), event->status);
+
+ if (event->status != 0) {
+ CNETERR("Can't resolve address for %s: %d\n",
+ libcfs_nid2str(peer->ibp_nid), event->status);
+ rc = event->status;
+ } else {
+ rc = rdma_resolve_route(
+ cmid, *kiblnd_tunables.kib_timeout * 1000);
+ if (rc == 0)
+ return 0;
+ /* Can't initiate route resolution */
+ CERROR("Can't resolve route for %s: %d\n",
+ libcfs_nid2str(peer->ibp_nid), rc);
+ }
+ kiblnd_peer_connect_failed(peer, 1, rc);
+ kiblnd_peer_decref(peer);
+ return rc; /* rc != 0 destroys cmid */
+
+ case RDMA_CM_EVENT_ROUTE_ERROR:
+ peer = (kib_peer_t *)cmid->context;
+ CNETERR("%s: ROUTE ERROR %d\n",
+ libcfs_nid2str(peer->ibp_nid), event->status);
+ kiblnd_peer_connect_failed(peer, 1, -EHOSTUNREACH);
+ kiblnd_peer_decref(peer);
+ return -EHOSTUNREACH; /* rc != 0 destroys cmid */
+
+ case RDMA_CM_EVENT_ROUTE_RESOLVED:
+ peer = (kib_peer_t *)cmid->context;
+ CDEBUG(D_NET, "%s Route resolved: %d\n",
+ libcfs_nid2str(peer->ibp_nid), event->status);
+
+ if (event->status == 0)
+ return kiblnd_active_connect(cmid);
+
+ CNETERR("Can't resolve route for %s: %d\n",
+ libcfs_nid2str(peer->ibp_nid), event->status);
+ kiblnd_peer_connect_failed(peer, 1, event->status);
+ kiblnd_peer_decref(peer);
+ return event->status; /* rc != 0 destroys cmid */
+
+ case RDMA_CM_EVENT_UNREACHABLE:
+ conn = (kib_conn_t *)cmid->context;
+ LASSERT(conn->ibc_state == IBLND_CONN_ACTIVE_CONNECT ||
+ conn->ibc_state == IBLND_CONN_PASSIVE_WAIT);
+ CNETERR("%s: UNREACHABLE %d\n",
+ libcfs_nid2str(conn->ibc_peer->ibp_nid), event->status);
+ kiblnd_connreq_done(conn, -ENETDOWN);
+ kiblnd_conn_decref(conn);
+ return 0;
+
+ case RDMA_CM_EVENT_CONNECT_ERROR:
+ conn = (kib_conn_t *)cmid->context;
+ LASSERT(conn->ibc_state == IBLND_CONN_ACTIVE_CONNECT ||
+ conn->ibc_state == IBLND_CONN_PASSIVE_WAIT);
+ CNETERR("%s: CONNECT ERROR %d\n",
+ libcfs_nid2str(conn->ibc_peer->ibp_nid), event->status);
+ kiblnd_connreq_done(conn, -ENOTCONN);
+ kiblnd_conn_decref(conn);
+ return 0;
+
+ case RDMA_CM_EVENT_REJECTED:
+ conn = (kib_conn_t *)cmid->context;
+ switch (conn->ibc_state) {
+ default:
+ LBUG();
+
+ case IBLND_CONN_PASSIVE_WAIT:
+ CERROR("%s: REJECTED %d\n",
+ libcfs_nid2str(conn->ibc_peer->ibp_nid),
+ event->status);
+ kiblnd_connreq_done(conn, -ECONNRESET);
+ break;
+
+ case IBLND_CONN_ACTIVE_CONNECT:
+ kiblnd_rejected(conn, event->status,
+ (void *)KIBLND_CONN_PARAM(event),
+ KIBLND_CONN_PARAM_LEN(event));
+ break;
+ }
+ kiblnd_conn_decref(conn);
+ return 0;
+
+ case RDMA_CM_EVENT_ESTABLISHED:
+ conn = (kib_conn_t *)cmid->context;
+ switch (conn->ibc_state) {
+ default:
+ LBUG();
+
+ case IBLND_CONN_PASSIVE_WAIT:
+ CDEBUG(D_NET, "ESTABLISHED (passive): %s\n",
+ libcfs_nid2str(conn->ibc_peer->ibp_nid));
+ kiblnd_connreq_done(conn, 0);
+ break;
+
+ case IBLND_CONN_ACTIVE_CONNECT:
+ CDEBUG(D_NET, "ESTABLISHED(active): %s\n",
+ libcfs_nid2str(conn->ibc_peer->ibp_nid));
+ kiblnd_check_connreply(conn,
+ (void *)KIBLND_CONN_PARAM(event),
+ KIBLND_CONN_PARAM_LEN(event));
+ break;
+ }
+ /* net keeps its ref on conn! */
+ return 0;
+
+ case RDMA_CM_EVENT_TIMEWAIT_EXIT:
+ CDEBUG(D_NET, "Ignore TIMEWAIT_EXIT event\n");
+ return 0;
+ case RDMA_CM_EVENT_DISCONNECTED:
+ conn = (kib_conn_t *)cmid->context;
+ if (conn->ibc_state < IBLND_CONN_ESTABLISHED) {
+ CERROR("%s DISCONNECTED\n",
+ libcfs_nid2str(conn->ibc_peer->ibp_nid));
+ kiblnd_connreq_done(conn, -ECONNRESET);
+ } else {
+ kiblnd_close_conn(conn, 0);
+ }
+ kiblnd_conn_decref(conn);
+ cmid->context = NULL;
+ return 0;
+
+ case RDMA_CM_EVENT_DEVICE_REMOVAL:
+ LCONSOLE_ERROR_MSG(0x131,
+ "Received notification of device removal\n"
+ "Please shutdown LNET to allow this to proceed\n");
+ /* Can't remove network from underneath LNET for now, so I have
+ * to ignore this */
+ return 0;
+
+ case RDMA_CM_EVENT_ADDR_CHANGE:
+ LCONSOLE_INFO("Physical link changed (eg hca/port)\n");
+ return 0;
+ }
+}
+
+static int
+kiblnd_check_txs_locked(kib_conn_t *conn, struct list_head *txs)
+{
+ kib_tx_t *tx;
+ struct list_head *ttmp;
+
+ list_for_each(ttmp, txs) {
+ tx = list_entry(ttmp, kib_tx_t, tx_list);
+
+ if (txs != &conn->ibc_active_txs) {
+ LASSERT(tx->tx_queued);
+ } else {
+ LASSERT(!tx->tx_queued);
+ LASSERT(tx->tx_waiting || tx->tx_sending != 0);
+ }
+
+ if (cfs_time_aftereq(jiffies, tx->tx_deadline)) {
+ CERROR("Timed out tx: %s, %lu seconds\n",
+ kiblnd_queue2str(conn, txs),
+ cfs_duration_sec(jiffies - tx->tx_deadline));
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+static int
+kiblnd_conn_timed_out_locked(kib_conn_t *conn)
+{
+ return kiblnd_check_txs_locked(conn, &conn->ibc_tx_queue) ||
+ kiblnd_check_txs_locked(conn, &conn->ibc_tx_noops) ||
+ kiblnd_check_txs_locked(conn, &conn->ibc_tx_queue_rsrvd) ||
+ kiblnd_check_txs_locked(conn, &conn->ibc_tx_queue_nocred) ||
+ kiblnd_check_txs_locked(conn, &conn->ibc_active_txs);
+}
+
+static void
+kiblnd_check_conns(int idx)
+{
+ LIST_HEAD(closes);
+ LIST_HEAD(checksends);
+ struct list_head *peers = &kiblnd_data.kib_peers[idx];
+ struct list_head *ptmp;
+ kib_peer_t *peer;
+ kib_conn_t *conn;
+ kib_conn_t *tmp;
+ struct list_head *ctmp;
+ unsigned long flags;
+
+ /* NB. We expect to have a look at all the peers and not find any
+ * RDMAs to time out, so we just use a shared lock while we
+ * take a look... */
+ read_lock_irqsave(&kiblnd_data.kib_global_lock, flags);
+
+ list_for_each(ptmp, peers) {
+ peer = list_entry(ptmp, kib_peer_t, ibp_list);
+
+ list_for_each(ctmp, &peer->ibp_conns) {
+ int timedout;
+ int sendnoop;
+
+ conn = list_entry(ctmp, kib_conn_t, ibc_list);
+
+ LASSERT(conn->ibc_state == IBLND_CONN_ESTABLISHED);
+
+ spin_lock(&conn->ibc_lock);
+
+ sendnoop = kiblnd_need_noop(conn);
+ timedout = kiblnd_conn_timed_out_locked(conn);
+ if (!sendnoop && !timedout) {
+ spin_unlock(&conn->ibc_lock);
+ continue;
+ }
+
+ if (timedout) {
+ CERROR("Timed out RDMA with %s (%lu): c: %u, oc: %u, rc: %u\n",
+ libcfs_nid2str(peer->ibp_nid),
+ cfs_duration_sec(cfs_time_current() -
+ peer->ibp_last_alive),
+ conn->ibc_credits,
+ conn->ibc_outstanding_credits,
+ conn->ibc_reserved_credits);
+ list_add(&conn->ibc_connd_list, &closes);
+ } else {
+ list_add(&conn->ibc_connd_list,
+ &checksends);
+ }
+ /* +ref for 'closes' or 'checksends' */
+ kiblnd_conn_addref(conn);
+
+ spin_unlock(&conn->ibc_lock);
+ }
+ }
+
+ read_unlock_irqrestore(&kiblnd_data.kib_global_lock, flags);
+
+ /* Handle timeout by closing the whole
+ * connection. We can only be sure RDMA activity
+ * has ceased once the QP has been modified. */
+ list_for_each_entry_safe(conn, tmp, &closes, ibc_connd_list) {
+ list_del(&conn->ibc_connd_list);
+ kiblnd_close_conn(conn, -ETIMEDOUT);
+ kiblnd_conn_decref(conn);
+ }
+
+ /* In case we have enough credits to return via a
+ * NOOP, but there were no non-blocking tx descs
+ * free to do it last time... */
+ while (!list_empty(&checksends)) {
+ conn = list_entry(checksends.next,
+ kib_conn_t, ibc_connd_list);
+ list_del(&conn->ibc_connd_list);
+ kiblnd_check_sends(conn);
+ kiblnd_conn_decref(conn);
+ }
+}
+
+static void
+kiblnd_disconnect_conn(kib_conn_t *conn)
+{
+ LASSERT(!in_interrupt());
+ LASSERT(current == kiblnd_data.kib_connd);
+ LASSERT(conn->ibc_state == IBLND_CONN_CLOSING);
+
+ rdma_disconnect(conn->ibc_cmid);
+ kiblnd_finalise_conn(conn);
+
+ kiblnd_peer_notify(conn->ibc_peer);
+}
+
+int
+kiblnd_connd(void *arg)
+{
+ wait_queue_t wait;
+ unsigned long flags;
+ kib_conn_t *conn;
+ int timeout;
+ int i;
+ int dropped_lock;
+ int peer_index = 0;
+ unsigned long deadline = jiffies;
+
+ cfs_block_allsigs();
+
+ init_waitqueue_entry(&wait, current);
+ kiblnd_data.kib_connd = current;
+
+ spin_lock_irqsave(&kiblnd_data.kib_connd_lock, flags);
+
+ while (!kiblnd_data.kib_shutdown) {
+
+ dropped_lock = 0;
+
+ if (!list_empty(&kiblnd_data.kib_connd_zombies)) {
+ conn = list_entry(kiblnd_data. \
+ kib_connd_zombies.next,
+ kib_conn_t, ibc_list);
+ list_del(&conn->ibc_list);
+
+ spin_unlock_irqrestore(&kiblnd_data.kib_connd_lock,
+ flags);
+ dropped_lock = 1;
+
+ kiblnd_destroy_conn(conn);
+
+ spin_lock_irqsave(&kiblnd_data.kib_connd_lock, flags);
+ }
+
+ if (!list_empty(&kiblnd_data.kib_connd_conns)) {
+ conn = list_entry(kiblnd_data.kib_connd_conns.next,
+ kib_conn_t, ibc_list);
+ list_del(&conn->ibc_list);
+
+ spin_unlock_irqrestore(&kiblnd_data.kib_connd_lock,
+ flags);
+ dropped_lock = 1;
+
+ kiblnd_disconnect_conn(conn);
+ kiblnd_conn_decref(conn);
+
+ spin_lock_irqsave(&kiblnd_data.kib_connd_lock, flags);
+ }
+
+ /* careful with the jiffy wrap... */
+ timeout = (int)(deadline - jiffies);
+ if (timeout <= 0) {
+ const int n = 4;
+ const int p = 1;
+ int chunk = kiblnd_data.kib_peer_hash_size;
+
+ spin_unlock_irqrestore(&kiblnd_data.kib_connd_lock, flags);
+ dropped_lock = 1;
+
+ /* Time to check for RDMA timeouts on a few more
+ * peers: I do checks every 'p' seconds on a
+ * proportion of the peer table and I need to check
+ * every connection 'n' times within a timeout
+ * interval, to ensure I detect a timeout on any
+ * connection within (n+1)/n times the timeout
+ * interval. */
+
+ if (*kiblnd_tunables.kib_timeout > n * p)
+ chunk = (chunk * n * p) /
+ *kiblnd_tunables.kib_timeout;
+ if (chunk == 0)
+ chunk = 1;
+
+ for (i = 0; i < chunk; i++) {
+ kiblnd_check_conns(peer_index);
+ peer_index = (peer_index + 1) %
+ kiblnd_data.kib_peer_hash_size;
+ }
+
+ deadline += p * HZ;
+ spin_lock_irqsave(&kiblnd_data.kib_connd_lock, flags);
+ }
+
+ if (dropped_lock)
+ continue;
+
+ /* Nothing to do for 'timeout' */
+ set_current_state(TASK_INTERRUPTIBLE);
+ add_wait_queue(&kiblnd_data.kib_connd_waitq, &wait);
+ spin_unlock_irqrestore(&kiblnd_data.kib_connd_lock, flags);
+
+ schedule_timeout(timeout);
+
+ remove_wait_queue(&kiblnd_data.kib_connd_waitq, &wait);
+ spin_lock_irqsave(&kiblnd_data.kib_connd_lock, flags);
+ }
+
+ spin_unlock_irqrestore(&kiblnd_data.kib_connd_lock, flags);
+
+ kiblnd_thread_fini();
+ return 0;
+}
+
+void
+kiblnd_qp_event(struct ib_event *event, void *arg)
+{
+ kib_conn_t *conn = arg;
+
+ switch (event->event) {
+ case IB_EVENT_COMM_EST:
+ CDEBUG(D_NET, "%s established\n",
+ libcfs_nid2str(conn->ibc_peer->ibp_nid));
+ return;
+
+ default:
+ CERROR("%s: Async QP event type %d\n",
+ libcfs_nid2str(conn->ibc_peer->ibp_nid), event->event);
+ return;
+ }
+}
+
+static void
+kiblnd_complete(struct ib_wc *wc)
+{
+ switch (kiblnd_wreqid2type(wc->wr_id)) {
+ default:
+ LBUG();
+
+ case IBLND_WID_RDMA:
+ /* We only get RDMA completion notification if it fails. All
+ * subsequent work items, including the final SEND will fail
+ * too. However we can't print out any more info about the
+ * failing RDMA because 'tx' might be back on the idle list or
+ * even reused already if we didn't manage to post all our work
+ * items */
+ CNETERR("RDMA (tx: %p) failed: %d\n",
+ kiblnd_wreqid2ptr(wc->wr_id), wc->status);
+ return;
+
+ case IBLND_WID_TX:
+ kiblnd_tx_complete(kiblnd_wreqid2ptr(wc->wr_id), wc->status);
+ return;
+
+ case IBLND_WID_RX:
+ kiblnd_rx_complete(kiblnd_wreqid2ptr(wc->wr_id), wc->status,
+ wc->byte_len);
+ return;
+ }
+}
+
+void
+kiblnd_cq_completion(struct ib_cq *cq, void *arg)
+{
+ /* NB I'm not allowed to schedule this conn once its refcount has
+ * reached 0. Since fundamentally I'm racing with scheduler threads
+ * consuming my CQ I could be called after all completions have
+ * occurred. But in this case, ibc_nrx == 0 && ibc_nsends_posted == 0
+ * and this CQ is about to be destroyed so I NOOP. */
+ kib_conn_t *conn = (kib_conn_t *)arg;
+ struct kib_sched_info *sched = conn->ibc_sched;
+ unsigned long flags;
+
+ LASSERT(cq == conn->ibc_cq);
+
+ spin_lock_irqsave(&sched->ibs_lock, flags);
+
+ conn->ibc_ready = 1;
+
+ if (!conn->ibc_scheduled &&
+ (conn->ibc_nrx > 0 ||
+ conn->ibc_nsends_posted > 0)) {
+ kiblnd_conn_addref(conn); /* +1 ref for sched_conns */
+ conn->ibc_scheduled = 1;
+ list_add_tail(&conn->ibc_sched_list, &sched->ibs_conns);
+
+ if (waitqueue_active(&sched->ibs_waitq))
+ wake_up(&sched->ibs_waitq);
+ }
+
+ spin_unlock_irqrestore(&sched->ibs_lock, flags);
+}
+
+void
+kiblnd_cq_event(struct ib_event *event, void *arg)
+{
+ kib_conn_t *conn = arg;
+
+ CERROR("%s: async CQ event type %d\n",
+ libcfs_nid2str(conn->ibc_peer->ibp_nid), event->event);
+}
+
+int
+kiblnd_scheduler(void *arg)
+{
+ long id = (long)arg;
+ struct kib_sched_info *sched;
+ kib_conn_t *conn;
+ wait_queue_t wait;
+ unsigned long flags;
+ struct ib_wc wc;
+ int did_something;
+ int busy_loops = 0;
+ int rc;
+
+ cfs_block_allsigs();
+
+ init_waitqueue_entry(&wait, current);
+
+ sched = kiblnd_data.kib_scheds[KIB_THREAD_CPT(id)];
+
+ rc = cfs_cpt_bind(lnet_cpt_table(), sched->ibs_cpt);
+ if (rc != 0) {
+ CWARN("Failed to bind on CPT %d, please verify whether all CPUs are healthy and reload modules if necessary, otherwise your system might under risk of low performance\n",
+ sched->ibs_cpt);
+ }
+
+ spin_lock_irqsave(&sched->ibs_lock, flags);
+
+ while (!kiblnd_data.kib_shutdown) {
+ if (busy_loops++ >= IBLND_RESCHED) {
+ spin_unlock_irqrestore(&sched->ibs_lock, flags);
+
+ cond_resched();
+ busy_loops = 0;
+
+ spin_lock_irqsave(&sched->ibs_lock, flags);
+ }
+
+ did_something = 0;
+
+ if (!list_empty(&sched->ibs_conns)) {
+ conn = list_entry(sched->ibs_conns.next,
+ kib_conn_t, ibc_sched_list);
+ /* take over kib_sched_conns' ref on conn... */
+ LASSERT(conn->ibc_scheduled);
+ list_del(&conn->ibc_sched_list);
+ conn->ibc_ready = 0;
+
+ spin_unlock_irqrestore(&sched->ibs_lock, flags);
+
+ rc = ib_poll_cq(conn->ibc_cq, 1, &wc);
+ if (rc == 0) {
+ rc = ib_req_notify_cq(conn->ibc_cq,
+ IB_CQ_NEXT_COMP);
+ if (rc < 0) {
+ CWARN("%s: ib_req_notify_cq failed: %d, closing connection\n",
+ libcfs_nid2str(conn->ibc_peer->ibp_nid), rc);
+ kiblnd_close_conn(conn, -EIO);
+ kiblnd_conn_decref(conn);
+ spin_lock_irqsave(&sched->ibs_lock,
+ flags);
+ continue;
+ }
+
+ rc = ib_poll_cq(conn->ibc_cq, 1, &wc);
+ }
+
+ if (rc < 0) {
+ CWARN("%s: ib_poll_cq failed: %d, closing connection\n",
+ libcfs_nid2str(conn->ibc_peer->ibp_nid),
+ rc);
+ kiblnd_close_conn(conn, -EIO);
+ kiblnd_conn_decref(conn);
+ spin_lock_irqsave(&sched->ibs_lock, flags);
+ continue;
+ }
+
+ spin_lock_irqsave(&sched->ibs_lock, flags);
+
+ if (rc != 0 || conn->ibc_ready) {
+ /* There may be another completion waiting; get
+ * another scheduler to check while I handle
+ * this one... */
+ /* +1 ref for sched_conns */
+ kiblnd_conn_addref(conn);
+ list_add_tail(&conn->ibc_sched_list,
+ &sched->ibs_conns);
+ if (waitqueue_active(&sched->ibs_waitq))
+ wake_up(&sched->ibs_waitq);
+ } else {
+ conn->ibc_scheduled = 0;
+ }
+
+ if (rc != 0) {
+ spin_unlock_irqrestore(&sched->ibs_lock, flags);
+ kiblnd_complete(&wc);
+
+ spin_lock_irqsave(&sched->ibs_lock, flags);
+ }
+
+ kiblnd_conn_decref(conn); /* ...drop my ref from above */
+ did_something = 1;
+ }
+
+ if (did_something)
+ continue;
+
+ set_current_state(TASK_INTERRUPTIBLE);
+ add_wait_queue_exclusive(&sched->ibs_waitq, &wait);
+ spin_unlock_irqrestore(&sched->ibs_lock, flags);
+
+ schedule();
+ busy_loops = 0;
+
+ remove_wait_queue(&sched->ibs_waitq, &wait);
+ spin_lock_irqsave(&sched->ibs_lock, flags);
+ }
+
+ spin_unlock_irqrestore(&sched->ibs_lock, flags);
+
+ kiblnd_thread_fini();
+ return 0;
+}
+
+int
+kiblnd_failover_thread(void *arg)
+{
+ rwlock_t *glock = &kiblnd_data.kib_global_lock;
+ kib_dev_t *dev;
+ wait_queue_t wait;
+ unsigned long flags;
+ int rc;
+
+ LASSERT(*kiblnd_tunables.kib_dev_failover != 0);
+
+ cfs_block_allsigs();
+
+ init_waitqueue_entry(&wait, current);
+ write_lock_irqsave(glock, flags);
+
+ while (!kiblnd_data.kib_shutdown) {
+ int do_failover = 0;
+ int long_sleep;
+
+ list_for_each_entry(dev, &kiblnd_data.kib_failed_devs,
+ ibd_fail_list) {
+ if (time_before(cfs_time_current(),
+ dev->ibd_next_failover))
+ continue;
+ do_failover = 1;
+ break;
+ }
+
+ if (do_failover) {
+ list_del_init(&dev->ibd_fail_list);
+ dev->ibd_failover = 1;
+ write_unlock_irqrestore(glock, flags);
+
+ rc = kiblnd_dev_failover(dev);
+
+ write_lock_irqsave(glock, flags);
+
+ LASSERT(dev->ibd_failover);
+ dev->ibd_failover = 0;
+ if (rc >= 0) { /* Device is OK or failover succeed */
+ dev->ibd_next_failover = cfs_time_shift(3);
+ continue;
+ }
+
+ /* failed to failover, retry later */
+ dev->ibd_next_failover =
+ cfs_time_shift(min(dev->ibd_failed_failover, 10));
+ if (kiblnd_dev_can_failover(dev)) {
+ list_add_tail(&dev->ibd_fail_list,
+ &kiblnd_data.kib_failed_devs);
+ }
+
+ continue;
+ }
+
+ /* long sleep if no more pending failover */
+ long_sleep = list_empty(&kiblnd_data.kib_failed_devs);
+
+ set_current_state(TASK_INTERRUPTIBLE);
+ add_wait_queue(&kiblnd_data.kib_failover_waitq, &wait);
+ write_unlock_irqrestore(glock, flags);
+
+ rc = schedule_timeout(long_sleep ? cfs_time_seconds(10) :
+ cfs_time_seconds(1));
+ remove_wait_queue(&kiblnd_data.kib_failover_waitq, &wait);
+ write_lock_irqsave(glock, flags);
+
+ if (!long_sleep || rc != 0)
+ continue;
+
+ /* have a long sleep, routine check all active devices,
+ * we need checking like this because if there is not active
+ * connection on the dev and no SEND from local, we may listen
+ * on wrong HCA for ever while there is a bonding failover */
+ list_for_each_entry(dev, &kiblnd_data.kib_devs, ibd_list) {
+ if (kiblnd_dev_can_failover(dev)) {
+ list_add_tail(&dev->ibd_fail_list,
+ &kiblnd_data.kib_failed_devs);
+ }
+ }
+ }
+
+ write_unlock_irqrestore(glock, flags);
+
+ kiblnd_thread_fini();
+ return 0;
+}
diff --git a/drivers/staging/lustre/lnet/klnds/o2iblnd/o2iblnd_modparams.c b/drivers/staging/lustre/lnet/klnds/o2iblnd/o2iblnd_modparams.c
new file mode 100644
index 000000000..eedf01afd
--- /dev/null
+++ b/drivers/staging/lustre/lnet/klnds/o2iblnd/o2iblnd_modparams.c
@@ -0,0 +1,230 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2012, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ *
+ * lnet/klnds/o2iblnd/o2iblnd_modparams.c
+ *
+ * Author: Eric Barton <eric@bartonsoftware.com>
+ */
+
+#include "o2iblnd.h"
+
+static int service = 987;
+module_param(service, int, 0444);
+MODULE_PARM_DESC(service, "service number (within RDMA_PS_TCP)");
+
+static int cksum;
+module_param(cksum, int, 0644);
+MODULE_PARM_DESC(cksum, "set non-zero to enable message (not RDMA) checksums");
+
+static int timeout = 50;
+module_param(timeout, int, 0644);
+MODULE_PARM_DESC(timeout, "timeout (seconds)");
+
+/* Number of threads in each scheduler pool which is percpt,
+ * we will estimate reasonable value based on CPUs if it's set to zero. */
+static int nscheds;
+module_param(nscheds, int, 0444);
+MODULE_PARM_DESC(nscheds, "number of threads in each scheduler pool");
+
+/* NB: this value is shared by all CPTs, it can grow at runtime */
+static int ntx = 512;
+module_param(ntx, int, 0444);
+MODULE_PARM_DESC(ntx, "# of message descriptors allocated for each pool");
+
+/* NB: this value is shared by all CPTs */
+static int credits = 256;
+module_param(credits, int, 0444);
+MODULE_PARM_DESC(credits, "# concurrent sends");
+
+static int peer_credits = 8;
+module_param(peer_credits, int, 0444);
+MODULE_PARM_DESC(peer_credits, "# concurrent sends to 1 peer");
+
+static int peer_credits_hiw;
+module_param(peer_credits_hiw, int, 0444);
+MODULE_PARM_DESC(peer_credits_hiw, "when eagerly to return credits");
+
+static int peer_buffer_credits;
+module_param(peer_buffer_credits, int, 0444);
+MODULE_PARM_DESC(peer_buffer_credits, "# per-peer router buffer credits");
+
+static int peer_timeout = 180;
+module_param(peer_timeout, int, 0444);
+MODULE_PARM_DESC(peer_timeout, "Seconds without aliveness news to declare peer dead (<=0 to disable)");
+
+static char *ipif_name = "ib0";
+module_param(ipif_name, charp, 0444);
+MODULE_PARM_DESC(ipif_name, "IPoIB interface name");
+
+static int retry_count = 5;
+module_param(retry_count, int, 0644);
+MODULE_PARM_DESC(retry_count, "Retransmissions when no ACK received");
+
+static int rnr_retry_count = 6;
+module_param(rnr_retry_count, int, 0644);
+MODULE_PARM_DESC(rnr_retry_count, "RNR retransmissions");
+
+static int keepalive = 100;
+module_param(keepalive, int, 0644);
+MODULE_PARM_DESC(keepalive, "Idle time in seconds before sending a keepalive");
+
+static int ib_mtu;
+module_param(ib_mtu, int, 0444);
+MODULE_PARM_DESC(ib_mtu, "IB MTU 256/512/1024/2048/4096");
+
+static int concurrent_sends;
+module_param(concurrent_sends, int, 0444);
+MODULE_PARM_DESC(concurrent_sends, "send work-queue sizing");
+
+static int map_on_demand;
+module_param(map_on_demand, int, 0444);
+MODULE_PARM_DESC(map_on_demand, "map on demand");
+
+/* NB: this value is shared by all CPTs, it can grow at runtime */
+static int fmr_pool_size = 512;
+module_param(fmr_pool_size, int, 0444);
+MODULE_PARM_DESC(fmr_pool_size, "size of fmr pool on each CPT (>= ntx / 4)");
+
+/* NB: this value is shared by all CPTs, it can grow at runtime */
+static int fmr_flush_trigger = 384;
+module_param(fmr_flush_trigger, int, 0444);
+MODULE_PARM_DESC(fmr_flush_trigger, "# dirty FMRs that triggers pool flush");
+
+static int fmr_cache = 1;
+module_param(fmr_cache, int, 0444);
+MODULE_PARM_DESC(fmr_cache, "non-zero to enable FMR caching");
+
+/* NB: this value is shared by all CPTs, it can grow at runtime */
+static int pmr_pool_size = 512;
+module_param(pmr_pool_size, int, 0444);
+MODULE_PARM_DESC(pmr_pool_size, "size of MR cache pmr pool on each CPT");
+
+/*
+ * 0: disable failover
+ * 1: enable failover if necessary
+ * 2: force to failover (for debug)
+ */
+static int dev_failover;
+module_param(dev_failover, int, 0444);
+MODULE_PARM_DESC(dev_failover, "HCA failover for bonding (0 off, 1 on, other values reserved)");
+
+
+static int require_privileged_port;
+module_param(require_privileged_port, int, 0644);
+MODULE_PARM_DESC(require_privileged_port, "require privileged port when accepting connection");
+
+static int use_privileged_port = 1;
+module_param(use_privileged_port, int, 0644);
+MODULE_PARM_DESC(use_privileged_port, "use privileged port when initiating connection");
+
+kib_tunables_t kiblnd_tunables = {
+ .kib_dev_failover = &dev_failover,
+ .kib_service = &service,
+ .kib_cksum = &cksum,
+ .kib_timeout = &timeout,
+ .kib_keepalive = &keepalive,
+ .kib_ntx = &ntx,
+ .kib_credits = &credits,
+ .kib_peertxcredits = &peer_credits,
+ .kib_peercredits_hiw = &peer_credits_hiw,
+ .kib_peerrtrcredits = &peer_buffer_credits,
+ .kib_peertimeout = &peer_timeout,
+ .kib_default_ipif = &ipif_name,
+ .kib_retry_count = &retry_count,
+ .kib_rnr_retry_count = &rnr_retry_count,
+ .kib_concurrent_sends = &concurrent_sends,
+ .kib_ib_mtu = &ib_mtu,
+ .kib_map_on_demand = &map_on_demand,
+ .kib_fmr_pool_size = &fmr_pool_size,
+ .kib_fmr_flush_trigger = &fmr_flush_trigger,
+ .kib_fmr_cache = &fmr_cache,
+ .kib_pmr_pool_size = &pmr_pool_size,
+ .kib_require_priv_port = &require_privileged_port,
+ .kib_use_priv_port = &use_privileged_port,
+ .kib_nscheds = &nscheds
+};
+
+int
+kiblnd_tunables_init(void)
+{
+ if (kiblnd_translate_mtu(*kiblnd_tunables.kib_ib_mtu) < 0) {
+ CERROR("Invalid ib_mtu %d, expected 256/512/1024/2048/4096\n",
+ *kiblnd_tunables.kib_ib_mtu);
+ return -EINVAL;
+ }
+
+ if (*kiblnd_tunables.kib_peertxcredits < IBLND_CREDITS_DEFAULT)
+ *kiblnd_tunables.kib_peertxcredits = IBLND_CREDITS_DEFAULT;
+
+ if (*kiblnd_tunables.kib_peertxcredits > IBLND_CREDITS_MAX)
+ *kiblnd_tunables.kib_peertxcredits = IBLND_CREDITS_MAX;
+
+ if (*kiblnd_tunables.kib_peertxcredits > *kiblnd_tunables.kib_credits)
+ *kiblnd_tunables.kib_peertxcredits = *kiblnd_tunables.kib_credits;
+
+ if (*kiblnd_tunables.kib_peercredits_hiw < *kiblnd_tunables.kib_peertxcredits / 2)
+ *kiblnd_tunables.kib_peercredits_hiw = *kiblnd_tunables.kib_peertxcredits / 2;
+
+ if (*kiblnd_tunables.kib_peercredits_hiw >= *kiblnd_tunables.kib_peertxcredits)
+ *kiblnd_tunables.kib_peercredits_hiw = *kiblnd_tunables.kib_peertxcredits - 1;
+
+ if (*kiblnd_tunables.kib_map_on_demand < 0 ||
+ *kiblnd_tunables.kib_map_on_demand > IBLND_MAX_RDMA_FRAGS)
+ *kiblnd_tunables.kib_map_on_demand = 0; /* disable map-on-demand */
+
+ if (*kiblnd_tunables.kib_map_on_demand == 1)
+ *kiblnd_tunables.kib_map_on_demand = 2; /* don't make sense to create map if only one fragment */
+
+ if (*kiblnd_tunables.kib_concurrent_sends == 0) {
+ if (*kiblnd_tunables.kib_map_on_demand > 0 &&
+ *kiblnd_tunables.kib_map_on_demand <= IBLND_MAX_RDMA_FRAGS / 8)
+ *kiblnd_tunables.kib_concurrent_sends = (*kiblnd_tunables.kib_peertxcredits) * 2;
+ else
+ *kiblnd_tunables.kib_concurrent_sends = (*kiblnd_tunables.kib_peertxcredits);
+ }
+
+ if (*kiblnd_tunables.kib_concurrent_sends > *kiblnd_tunables.kib_peertxcredits * 2)
+ *kiblnd_tunables.kib_concurrent_sends = *kiblnd_tunables.kib_peertxcredits * 2;
+
+ if (*kiblnd_tunables.kib_concurrent_sends < *kiblnd_tunables.kib_peertxcredits / 2)
+ *kiblnd_tunables.kib_concurrent_sends = *kiblnd_tunables.kib_peertxcredits / 2;
+
+ if (*kiblnd_tunables.kib_concurrent_sends < *kiblnd_tunables.kib_peertxcredits) {
+ CWARN("Concurrent sends %d is lower than message queue size: %d, performance may drop slightly.\n",
+ *kiblnd_tunables.kib_concurrent_sends, *kiblnd_tunables.kib_peertxcredits);
+ }
+
+ return 0;
+}
diff --git a/drivers/staging/lustre/lnet/klnds/socklnd/Makefile b/drivers/staging/lustre/lnet/klnds/socklnd/Makefile
new file mode 100644
index 000000000..f3fb8778c
--- /dev/null
+++ b/drivers/staging/lustre/lnet/klnds/socklnd/Makefile
@@ -0,0 +1,3 @@
+obj-$(CONFIG_LNET) += ksocklnd.o
+
+ksocklnd-y := socklnd.o socklnd_cb.o socklnd_proto.o socklnd_modparams.o socklnd_lib-linux.o
diff --git a/drivers/staging/lustre/lnet/klnds/socklnd/socklnd.c b/drivers/staging/lustre/lnet/klnds/socklnd/socklnd.c
new file mode 100644
index 000000000..7586b7e40
--- /dev/null
+++ b/drivers/staging/lustre/lnet/klnds/socklnd/socklnd.c
@@ -0,0 +1,2886 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2011, 2012, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ *
+ * lnet/klnds/socklnd/socklnd.c
+ *
+ * Author: Zach Brown <zab@zabbo.net>
+ * Author: Peter J. Braam <braam@clusterfs.com>
+ * Author: Phil Schwan <phil@clusterfs.com>
+ * Author: Eric Barton <eric@bartonsoftware.com>
+ */
+
+#include "socklnd.h"
+
+static lnd_t the_ksocklnd;
+ksock_nal_data_t ksocknal_data;
+
+static ksock_interface_t *
+ksocknal_ip2iface(lnet_ni_t *ni, __u32 ip)
+{
+ ksock_net_t *net = ni->ni_data;
+ int i;
+ ksock_interface_t *iface;
+
+ for (i = 0; i < net->ksnn_ninterfaces; i++) {
+ LASSERT(i < LNET_MAX_INTERFACES);
+ iface = &net->ksnn_interfaces[i];
+
+ if (iface->ksni_ipaddr == ip)
+ return iface;
+ }
+
+ return NULL;
+}
+
+static ksock_route_t *
+ksocknal_create_route(__u32 ipaddr, int port)
+{
+ ksock_route_t *route;
+
+ LIBCFS_ALLOC(route, sizeof(*route));
+ if (route == NULL)
+ return NULL;
+
+ atomic_set(&route->ksnr_refcount, 1);
+ route->ksnr_peer = NULL;
+ route->ksnr_retry_interval = 0; /* OK to connect at any time */
+ route->ksnr_ipaddr = ipaddr;
+ route->ksnr_port = port;
+ route->ksnr_scheduled = 0;
+ route->ksnr_connecting = 0;
+ route->ksnr_connected = 0;
+ route->ksnr_deleted = 0;
+ route->ksnr_conn_count = 0;
+ route->ksnr_share_count = 0;
+
+ return route;
+}
+
+void
+ksocknal_destroy_route(ksock_route_t *route)
+{
+ LASSERT(atomic_read(&route->ksnr_refcount) == 0);
+
+ if (route->ksnr_peer != NULL)
+ ksocknal_peer_decref(route->ksnr_peer);
+
+ LIBCFS_FREE(route, sizeof(*route));
+}
+
+static int
+ksocknal_create_peer(ksock_peer_t **peerp, lnet_ni_t *ni, lnet_process_id_t id)
+{
+ ksock_net_t *net = ni->ni_data;
+ ksock_peer_t *peer;
+
+ LASSERT(id.nid != LNET_NID_ANY);
+ LASSERT(id.pid != LNET_PID_ANY);
+ LASSERT(!in_interrupt());
+
+ LIBCFS_ALLOC(peer, sizeof(*peer));
+ if (peer == NULL)
+ return -ENOMEM;
+
+ peer->ksnp_ni = ni;
+ peer->ksnp_id = id;
+ atomic_set(&peer->ksnp_refcount, 1); /* 1 ref for caller */
+ peer->ksnp_closing = 0;
+ peer->ksnp_accepting = 0;
+ peer->ksnp_proto = NULL;
+ peer->ksnp_last_alive = 0;
+ peer->ksnp_zc_next_cookie = SOCKNAL_KEEPALIVE_PING + 1;
+
+ INIT_LIST_HEAD(&peer->ksnp_conns);
+ INIT_LIST_HEAD(&peer->ksnp_routes);
+ INIT_LIST_HEAD(&peer->ksnp_tx_queue);
+ INIT_LIST_HEAD(&peer->ksnp_zc_req_list);
+ spin_lock_init(&peer->ksnp_lock);
+
+ spin_lock_bh(&net->ksnn_lock);
+
+ if (net->ksnn_shutdown) {
+ spin_unlock_bh(&net->ksnn_lock);
+
+ LIBCFS_FREE(peer, sizeof(*peer));
+ CERROR("Can't create peer: network shutdown\n");
+ return -ESHUTDOWN;
+ }
+
+ net->ksnn_npeers++;
+
+ spin_unlock_bh(&net->ksnn_lock);
+
+ *peerp = peer;
+ return 0;
+}
+
+void
+ksocknal_destroy_peer(ksock_peer_t *peer)
+{
+ ksock_net_t *net = peer->ksnp_ni->ni_data;
+
+ CDEBUG(D_NET, "peer %s %p deleted\n",
+ libcfs_id2str(peer->ksnp_id), peer);
+
+ LASSERT(atomic_read(&peer->ksnp_refcount) == 0);
+ LASSERT(peer->ksnp_accepting == 0);
+ LASSERT(list_empty(&peer->ksnp_conns));
+ LASSERT(list_empty(&peer->ksnp_routes));
+ LASSERT(list_empty(&peer->ksnp_tx_queue));
+ LASSERT(list_empty(&peer->ksnp_zc_req_list));
+
+ LIBCFS_FREE(peer, sizeof(*peer));
+
+ /* NB a peer's connections and routes keep a reference on their peer
+ * until they are destroyed, so we can be assured that _all_ state to
+ * do with this peer has been cleaned up when its refcount drops to
+ * zero. */
+ spin_lock_bh(&net->ksnn_lock);
+ net->ksnn_npeers--;
+ spin_unlock_bh(&net->ksnn_lock);
+}
+
+ksock_peer_t *
+ksocknal_find_peer_locked(lnet_ni_t *ni, lnet_process_id_t id)
+{
+ struct list_head *peer_list = ksocknal_nid2peerlist(id.nid);
+ struct list_head *tmp;
+ ksock_peer_t *peer;
+
+ list_for_each(tmp, peer_list) {
+
+ peer = list_entry(tmp, ksock_peer_t, ksnp_list);
+
+ LASSERT(!peer->ksnp_closing);
+
+ if (peer->ksnp_ni != ni)
+ continue;
+
+ if (peer->ksnp_id.nid != id.nid ||
+ peer->ksnp_id.pid != id.pid)
+ continue;
+
+ CDEBUG(D_NET, "got peer [%p] -> %s (%d)\n",
+ peer, libcfs_id2str(id),
+ atomic_read(&peer->ksnp_refcount));
+ return peer;
+ }
+ return NULL;
+}
+
+ksock_peer_t *
+ksocknal_find_peer(lnet_ni_t *ni, lnet_process_id_t id)
+{
+ ksock_peer_t *peer;
+
+ read_lock(&ksocknal_data.ksnd_global_lock);
+ peer = ksocknal_find_peer_locked(ni, id);
+ if (peer != NULL) /* +1 ref for caller? */
+ ksocknal_peer_addref(peer);
+ read_unlock(&ksocknal_data.ksnd_global_lock);
+
+ return peer;
+}
+
+static void
+ksocknal_unlink_peer_locked(ksock_peer_t *peer)
+{
+ int i;
+ __u32 ip;
+ ksock_interface_t *iface;
+
+ for (i = 0; i < peer->ksnp_n_passive_ips; i++) {
+ LASSERT(i < LNET_MAX_INTERFACES);
+ ip = peer->ksnp_passive_ips[i];
+
+ iface = ksocknal_ip2iface(peer->ksnp_ni, ip);
+ /* All IPs in peer->ksnp_passive_ips[] come from the
+ * interface list, therefore the call must succeed. */
+ LASSERT(iface != NULL);
+
+ CDEBUG(D_NET, "peer=%p iface=%p ksni_nroutes=%d\n",
+ peer, iface, iface->ksni_nroutes);
+ iface->ksni_npeers--;
+ }
+
+ LASSERT(list_empty(&peer->ksnp_conns));
+ LASSERT(list_empty(&peer->ksnp_routes));
+ LASSERT(!peer->ksnp_closing);
+ peer->ksnp_closing = 1;
+ list_del(&peer->ksnp_list);
+ /* lose peerlist's ref */
+ ksocknal_peer_decref(peer);
+}
+
+static int
+ksocknal_get_peer_info(lnet_ni_t *ni, int index,
+ lnet_process_id_t *id, __u32 *myip, __u32 *peer_ip,
+ int *port, int *conn_count, int *share_count)
+{
+ ksock_peer_t *peer;
+ struct list_head *ptmp;
+ ksock_route_t *route;
+ struct list_head *rtmp;
+ int i;
+ int j;
+ int rc = -ENOENT;
+
+ read_lock(&ksocknal_data.ksnd_global_lock);
+
+ for (i = 0; i < ksocknal_data.ksnd_peer_hash_size; i++) {
+
+ list_for_each(ptmp, &ksocknal_data.ksnd_peers[i]) {
+ peer = list_entry(ptmp, ksock_peer_t, ksnp_list);
+
+ if (peer->ksnp_ni != ni)
+ continue;
+
+ if (peer->ksnp_n_passive_ips == 0 &&
+ list_empty(&peer->ksnp_routes)) {
+ if (index-- > 0)
+ continue;
+
+ *id = peer->ksnp_id;
+ *myip = 0;
+ *peer_ip = 0;
+ *port = 0;
+ *conn_count = 0;
+ *share_count = 0;
+ rc = 0;
+ goto out;
+ }
+
+ for (j = 0; j < peer->ksnp_n_passive_ips; j++) {
+ if (index-- > 0)
+ continue;
+
+ *id = peer->ksnp_id;
+ *myip = peer->ksnp_passive_ips[j];
+ *peer_ip = 0;
+ *port = 0;
+ *conn_count = 0;
+ *share_count = 0;
+ rc = 0;
+ goto out;
+ }
+
+ list_for_each(rtmp, &peer->ksnp_routes) {
+ if (index-- > 0)
+ continue;
+
+ route = list_entry(rtmp, ksock_route_t,
+ ksnr_list);
+
+ *id = peer->ksnp_id;
+ *myip = route->ksnr_myipaddr;
+ *peer_ip = route->ksnr_ipaddr;
+ *port = route->ksnr_port;
+ *conn_count = route->ksnr_conn_count;
+ *share_count = route->ksnr_share_count;
+ rc = 0;
+ goto out;
+ }
+ }
+ }
+ out:
+ read_unlock(&ksocknal_data.ksnd_global_lock);
+ return rc;
+}
+
+static void
+ksocknal_associate_route_conn_locked(ksock_route_t *route, ksock_conn_t *conn)
+{
+ ksock_peer_t *peer = route->ksnr_peer;
+ int type = conn->ksnc_type;
+ ksock_interface_t *iface;
+
+ conn->ksnc_route = route;
+ ksocknal_route_addref(route);
+
+ if (route->ksnr_myipaddr != conn->ksnc_myipaddr) {
+ if (route->ksnr_myipaddr == 0) {
+ /* route wasn't bound locally yet (the initial route) */
+ CDEBUG(D_NET, "Binding %s %pI4h to %pI4h\n",
+ libcfs_id2str(peer->ksnp_id),
+ &route->ksnr_ipaddr,
+ &conn->ksnc_myipaddr);
+ } else {
+ CDEBUG(D_NET, "Rebinding %s %pI4h from %pI4h to %pI4h\n",
+ libcfs_id2str(peer->ksnp_id),
+ &route->ksnr_ipaddr,
+ &route->ksnr_myipaddr,
+ &conn->ksnc_myipaddr);
+
+ iface = ksocknal_ip2iface(route->ksnr_peer->ksnp_ni,
+ route->ksnr_myipaddr);
+ if (iface != NULL)
+ iface->ksni_nroutes--;
+ }
+ route->ksnr_myipaddr = conn->ksnc_myipaddr;
+ iface = ksocknal_ip2iface(route->ksnr_peer->ksnp_ni,
+ route->ksnr_myipaddr);
+ if (iface != NULL)
+ iface->ksni_nroutes++;
+ }
+
+ route->ksnr_connected |= (1<<type);
+ route->ksnr_conn_count++;
+
+ /* Successful connection => further attempts can
+ * proceed immediately */
+ route->ksnr_retry_interval = 0;
+}
+
+static void
+ksocknal_add_route_locked(ksock_peer_t *peer, ksock_route_t *route)
+{
+ struct list_head *tmp;
+ ksock_conn_t *conn;
+ ksock_route_t *route2;
+
+ LASSERT(!peer->ksnp_closing);
+ LASSERT(route->ksnr_peer == NULL);
+ LASSERT(!route->ksnr_scheduled);
+ LASSERT(!route->ksnr_connecting);
+ LASSERT(route->ksnr_connected == 0);
+
+ /* LASSERT(unique) */
+ list_for_each(tmp, &peer->ksnp_routes) {
+ route2 = list_entry(tmp, ksock_route_t, ksnr_list);
+
+ if (route2->ksnr_ipaddr == route->ksnr_ipaddr) {
+ CERROR("Duplicate route %s %pI4h\n",
+ libcfs_id2str(peer->ksnp_id),
+ &route->ksnr_ipaddr);
+ LBUG();
+ }
+ }
+
+ route->ksnr_peer = peer;
+ ksocknal_peer_addref(peer);
+ /* peer's routelist takes over my ref on 'route' */
+ list_add_tail(&route->ksnr_list, &peer->ksnp_routes);
+
+ list_for_each(tmp, &peer->ksnp_conns) {
+ conn = list_entry(tmp, ksock_conn_t, ksnc_list);
+
+ if (conn->ksnc_ipaddr != route->ksnr_ipaddr)
+ continue;
+
+ ksocknal_associate_route_conn_locked(route, conn);
+ /* keep going (typed routes) */
+ }
+}
+
+static void
+ksocknal_del_route_locked(ksock_route_t *route)
+{
+ ksock_peer_t *peer = route->ksnr_peer;
+ ksock_interface_t *iface;
+ ksock_conn_t *conn;
+ struct list_head *ctmp;
+ struct list_head *cnxt;
+
+ LASSERT(!route->ksnr_deleted);
+
+ /* Close associated conns */
+ list_for_each_safe(ctmp, cnxt, &peer->ksnp_conns) {
+ conn = list_entry(ctmp, ksock_conn_t, ksnc_list);
+
+ if (conn->ksnc_route != route)
+ continue;
+
+ ksocknal_close_conn_locked(conn, 0);
+ }
+
+ if (route->ksnr_myipaddr != 0) {
+ iface = ksocknal_ip2iface(route->ksnr_peer->ksnp_ni,
+ route->ksnr_myipaddr);
+ if (iface != NULL)
+ iface->ksni_nroutes--;
+ }
+
+ route->ksnr_deleted = 1;
+ list_del(&route->ksnr_list);
+ ksocknal_route_decref(route); /* drop peer's ref */
+
+ if (list_empty(&peer->ksnp_routes) &&
+ list_empty(&peer->ksnp_conns)) {
+ /* I've just removed the last route to a peer with no active
+ * connections */
+ ksocknal_unlink_peer_locked(peer);
+ }
+}
+
+int
+ksocknal_add_peer(lnet_ni_t *ni, lnet_process_id_t id, __u32 ipaddr, int port)
+{
+ struct list_head *tmp;
+ ksock_peer_t *peer;
+ ksock_peer_t *peer2;
+ ksock_route_t *route;
+ ksock_route_t *route2;
+ int rc;
+
+ if (id.nid == LNET_NID_ANY ||
+ id.pid == LNET_PID_ANY)
+ return -EINVAL;
+
+ /* Have a brand new peer ready... */
+ rc = ksocknal_create_peer(&peer, ni, id);
+ if (rc != 0)
+ return rc;
+
+ route = ksocknal_create_route(ipaddr, port);
+ if (route == NULL) {
+ ksocknal_peer_decref(peer);
+ return -ENOMEM;
+ }
+
+ write_lock_bh(&ksocknal_data.ksnd_global_lock);
+
+ /* always called with a ref on ni, so shutdown can't have started */
+ LASSERT(((ksock_net_t *) ni->ni_data)->ksnn_shutdown == 0);
+
+ peer2 = ksocknal_find_peer_locked(ni, id);
+ if (peer2 != NULL) {
+ ksocknal_peer_decref(peer);
+ peer = peer2;
+ } else {
+ /* peer table takes my ref on peer */
+ list_add_tail(&peer->ksnp_list,
+ ksocknal_nid2peerlist(id.nid));
+ }
+
+ route2 = NULL;
+ list_for_each(tmp, &peer->ksnp_routes) {
+ route2 = list_entry(tmp, ksock_route_t, ksnr_list);
+
+ if (route2->ksnr_ipaddr == ipaddr)
+ break;
+
+ route2 = NULL;
+ }
+ if (route2 == NULL) {
+ ksocknal_add_route_locked(peer, route);
+ route->ksnr_share_count++;
+ } else {
+ ksocknal_route_decref(route);
+ route2->ksnr_share_count++;
+ }
+
+ write_unlock_bh(&ksocknal_data.ksnd_global_lock);
+
+ return 0;
+}
+
+static void
+ksocknal_del_peer_locked(ksock_peer_t *peer, __u32 ip)
+{
+ ksock_conn_t *conn;
+ ksock_route_t *route;
+ struct list_head *tmp;
+ struct list_head *nxt;
+ int nshared;
+
+ LASSERT(!peer->ksnp_closing);
+
+ /* Extra ref prevents peer disappearing until I'm done with it */
+ ksocknal_peer_addref(peer);
+
+ list_for_each_safe(tmp, nxt, &peer->ksnp_routes) {
+ route = list_entry(tmp, ksock_route_t, ksnr_list);
+
+ /* no match */
+ if (!(ip == 0 || route->ksnr_ipaddr == ip))
+ continue;
+
+ route->ksnr_share_count = 0;
+ /* This deletes associated conns too */
+ ksocknal_del_route_locked(route);
+ }
+
+ nshared = 0;
+ list_for_each_safe(tmp, nxt, &peer->ksnp_routes) {
+ route = list_entry(tmp, ksock_route_t, ksnr_list);
+ nshared += route->ksnr_share_count;
+ }
+
+ if (nshared == 0) {
+ /* remove everything else if there are no explicit entries
+ * left */
+
+ list_for_each_safe(tmp, nxt, &peer->ksnp_routes) {
+ route = list_entry(tmp, ksock_route_t, ksnr_list);
+
+ /* we should only be removing auto-entries */
+ LASSERT(route->ksnr_share_count == 0);
+ ksocknal_del_route_locked(route);
+ }
+
+ list_for_each_safe(tmp, nxt, &peer->ksnp_conns) {
+ conn = list_entry(tmp, ksock_conn_t, ksnc_list);
+
+ ksocknal_close_conn_locked(conn, 0);
+ }
+ }
+
+ ksocknal_peer_decref(peer);
+ /* NB peer unlinks itself when last conn/route is removed */
+}
+
+static int
+ksocknal_del_peer(lnet_ni_t *ni, lnet_process_id_t id, __u32 ip)
+{
+ LIST_HEAD(zombies);
+ struct list_head *ptmp;
+ struct list_head *pnxt;
+ ksock_peer_t *peer;
+ int lo;
+ int hi;
+ int i;
+ int rc = -ENOENT;
+
+ write_lock_bh(&ksocknal_data.ksnd_global_lock);
+
+ if (id.nid != LNET_NID_ANY)
+ lo = hi = (int)(ksocknal_nid2peerlist(id.nid) - ksocknal_data.ksnd_peers);
+ else {
+ lo = 0;
+ hi = ksocknal_data.ksnd_peer_hash_size - 1;
+ }
+
+ for (i = lo; i <= hi; i++) {
+ list_for_each_safe(ptmp, pnxt,
+ &ksocknal_data.ksnd_peers[i]) {
+ peer = list_entry(ptmp, ksock_peer_t, ksnp_list);
+
+ if (peer->ksnp_ni != ni)
+ continue;
+
+ if (!((id.nid == LNET_NID_ANY || peer->ksnp_id.nid == id.nid) &&
+ (id.pid == LNET_PID_ANY || peer->ksnp_id.pid == id.pid)))
+ continue;
+
+ ksocknal_peer_addref(peer); /* a ref for me... */
+
+ ksocknal_del_peer_locked(peer, ip);
+
+ if (peer->ksnp_closing &&
+ !list_empty(&peer->ksnp_tx_queue)) {
+ LASSERT(list_empty(&peer->ksnp_conns));
+ LASSERT(list_empty(&peer->ksnp_routes));
+
+ list_splice_init(&peer->ksnp_tx_queue,
+ &zombies);
+ }
+
+ ksocknal_peer_decref(peer); /* ...till here */
+
+ rc = 0; /* matched! */
+ }
+ }
+
+ write_unlock_bh(&ksocknal_data.ksnd_global_lock);
+
+ ksocknal_txlist_done(ni, &zombies, 1);
+
+ return rc;
+}
+
+static ksock_conn_t *
+ksocknal_get_conn_by_idx(lnet_ni_t *ni, int index)
+{
+ ksock_peer_t *peer;
+ struct list_head *ptmp;
+ ksock_conn_t *conn;
+ struct list_head *ctmp;
+ int i;
+
+ read_lock(&ksocknal_data.ksnd_global_lock);
+
+ for (i = 0; i < ksocknal_data.ksnd_peer_hash_size; i++) {
+ list_for_each(ptmp, &ksocknal_data.ksnd_peers[i]) {
+ peer = list_entry(ptmp, ksock_peer_t, ksnp_list);
+
+ LASSERT(!peer->ksnp_closing);
+
+ if (peer->ksnp_ni != ni)
+ continue;
+
+ list_for_each(ctmp, &peer->ksnp_conns) {
+ if (index-- > 0)
+ continue;
+
+ conn = list_entry(ctmp, ksock_conn_t,
+ ksnc_list);
+ ksocknal_conn_addref(conn);
+ read_unlock(&ksocknal_data.ksnd_global_lock);
+ return conn;
+ }
+ }
+ }
+
+ read_unlock(&ksocknal_data.ksnd_global_lock);
+ return NULL;
+}
+
+static ksock_sched_t *
+ksocknal_choose_scheduler_locked(unsigned int cpt)
+{
+ struct ksock_sched_info *info = ksocknal_data.ksnd_sched_info[cpt];
+ ksock_sched_t *sched;
+ int i;
+
+ LASSERT(info->ksi_nthreads > 0);
+
+ sched = &info->ksi_scheds[0];
+ /*
+ * NB: it's safe so far, but info->ksi_nthreads could be changed
+ * at runtime when we have dynamic LNet configuration, then we
+ * need to take care of this.
+ */
+ for (i = 1; i < info->ksi_nthreads; i++) {
+ if (sched->kss_nconns > info->ksi_scheds[i].kss_nconns)
+ sched = &info->ksi_scheds[i];
+ }
+
+ return sched;
+}
+
+static int
+ksocknal_local_ipvec(lnet_ni_t *ni, __u32 *ipaddrs)
+{
+ ksock_net_t *net = ni->ni_data;
+ int i;
+ int nip;
+
+ read_lock(&ksocknal_data.ksnd_global_lock);
+
+ nip = net->ksnn_ninterfaces;
+ LASSERT(nip <= LNET_MAX_INTERFACES);
+
+ /* Only offer interfaces for additional connections if I have
+ * more than one. */
+ if (nip < 2) {
+ read_unlock(&ksocknal_data.ksnd_global_lock);
+ return 0;
+ }
+
+ for (i = 0; i < nip; i++) {
+ ipaddrs[i] = net->ksnn_interfaces[i].ksni_ipaddr;
+ LASSERT(ipaddrs[i] != 0);
+ }
+
+ read_unlock(&ksocknal_data.ksnd_global_lock);
+ return nip;
+}
+
+static int
+ksocknal_match_peerip(ksock_interface_t *iface, __u32 *ips, int nips)
+{
+ int best_netmatch = 0;
+ int best_xor = 0;
+ int best = -1;
+ int this_xor;
+ int this_netmatch;
+ int i;
+
+ for (i = 0; i < nips; i++) {
+ if (ips[i] == 0)
+ continue;
+
+ this_xor = ips[i] ^ iface->ksni_ipaddr;
+ this_netmatch = ((this_xor & iface->ksni_netmask) == 0) ? 1 : 0;
+
+ if (!(best < 0 ||
+ best_netmatch < this_netmatch ||
+ (best_netmatch == this_netmatch &&
+ best_xor > this_xor)))
+ continue;
+
+ best = i;
+ best_netmatch = this_netmatch;
+ best_xor = this_xor;
+ }
+
+ LASSERT(best >= 0);
+ return best;
+}
+
+static int
+ksocknal_select_ips(ksock_peer_t *peer, __u32 *peerips, int n_peerips)
+{
+ rwlock_t *global_lock = &ksocknal_data.ksnd_global_lock;
+ ksock_net_t *net = peer->ksnp_ni->ni_data;
+ ksock_interface_t *iface;
+ ksock_interface_t *best_iface;
+ int n_ips;
+ int i;
+ int j;
+ int k;
+ __u32 ip;
+ __u32 xor;
+ int this_netmatch;
+ int best_netmatch;
+ int best_npeers;
+
+ /* CAVEAT EMPTOR: We do all our interface matching with an
+ * exclusive hold of global lock at IRQ priority. We're only
+ * expecting to be dealing with small numbers of interfaces, so the
+ * O(n**3)-ness shouldn't matter */
+
+ /* Also note that I'm not going to return more than n_peerips
+ * interfaces, even if I have more myself */
+
+ write_lock_bh(global_lock);
+
+ LASSERT(n_peerips <= LNET_MAX_INTERFACES);
+ LASSERT(net->ksnn_ninterfaces <= LNET_MAX_INTERFACES);
+
+ /* Only match interfaces for additional connections
+ * if I have > 1 interface */
+ n_ips = (net->ksnn_ninterfaces < 2) ? 0 :
+ min(n_peerips, net->ksnn_ninterfaces);
+
+ for (i = 0; peer->ksnp_n_passive_ips < n_ips; i++) {
+ /* ^ yes really... */
+
+ /* If we have any new interfaces, first tick off all the
+ * peer IPs that match old interfaces, then choose new
+ * interfaces to match the remaining peer IPS.
+ * We don't forget interfaces we've stopped using; we might
+ * start using them again... */
+
+ if (i < peer->ksnp_n_passive_ips) {
+ /* Old interface. */
+ ip = peer->ksnp_passive_ips[i];
+ best_iface = ksocknal_ip2iface(peer->ksnp_ni, ip);
+
+ } else {
+ /* choose a new interface */
+ LASSERT(i == peer->ksnp_n_passive_ips);
+
+ best_iface = NULL;
+ best_netmatch = 0;
+ best_npeers = 0;
+
+ for (j = 0; j < net->ksnn_ninterfaces; j++) {
+ iface = &net->ksnn_interfaces[j];
+ ip = iface->ksni_ipaddr;
+
+ for (k = 0; k < peer->ksnp_n_passive_ips; k++)
+ if (peer->ksnp_passive_ips[k] == ip)
+ break;
+
+ if (k < peer->ksnp_n_passive_ips) /* using it already */
+ continue;
+
+ k = ksocknal_match_peerip(iface, peerips, n_peerips);
+ xor = ip ^ peerips[k];
+ this_netmatch = ((xor & iface->ksni_netmask) == 0) ? 1 : 0;
+
+ if (!(best_iface == NULL ||
+ best_netmatch < this_netmatch ||
+ (best_netmatch == this_netmatch &&
+ best_npeers > iface->ksni_npeers)))
+ continue;
+
+ best_iface = iface;
+ best_netmatch = this_netmatch;
+ best_npeers = iface->ksni_npeers;
+ }
+
+ best_iface->ksni_npeers++;
+ ip = best_iface->ksni_ipaddr;
+ peer->ksnp_passive_ips[i] = ip;
+ peer->ksnp_n_passive_ips = i+1;
+ }
+
+ /* mark the best matching peer IP used */
+ j = ksocknal_match_peerip(best_iface, peerips, n_peerips);
+ peerips[j] = 0;
+ }
+
+ /* Overwrite input peer IP addresses */
+ memcpy(peerips, peer->ksnp_passive_ips, n_ips * sizeof(*peerips));
+
+ write_unlock_bh(global_lock);
+
+ return n_ips;
+}
+
+static void
+ksocknal_create_routes(ksock_peer_t *peer, int port,
+ __u32 *peer_ipaddrs, int npeer_ipaddrs)
+{
+ ksock_route_t *newroute = NULL;
+ rwlock_t *global_lock = &ksocknal_data.ksnd_global_lock;
+ lnet_ni_t *ni = peer->ksnp_ni;
+ ksock_net_t *net = ni->ni_data;
+ struct list_head *rtmp;
+ ksock_route_t *route;
+ ksock_interface_t *iface;
+ ksock_interface_t *best_iface;
+ int best_netmatch;
+ int this_netmatch;
+ int best_nroutes;
+ int i;
+ int j;
+
+ /* CAVEAT EMPTOR: We do all our interface matching with an
+ * exclusive hold of global lock at IRQ priority. We're only
+ * expecting to be dealing with small numbers of interfaces, so the
+ * O(n**3)-ness here shouldn't matter */
+
+ write_lock_bh(global_lock);
+
+ if (net->ksnn_ninterfaces < 2) {
+ /* Only create additional connections
+ * if I have > 1 interface */
+ write_unlock_bh(global_lock);
+ return;
+ }
+
+ LASSERT(npeer_ipaddrs <= LNET_MAX_INTERFACES);
+
+ for (i = 0; i < npeer_ipaddrs; i++) {
+ if (newroute != NULL) {
+ newroute->ksnr_ipaddr = peer_ipaddrs[i];
+ } else {
+ write_unlock_bh(global_lock);
+
+ newroute = ksocknal_create_route(peer_ipaddrs[i], port);
+ if (newroute == NULL)
+ return;
+
+ write_lock_bh(global_lock);
+ }
+
+ if (peer->ksnp_closing) {
+ /* peer got closed under me */
+ break;
+ }
+
+ /* Already got a route? */
+ route = NULL;
+ list_for_each(rtmp, &peer->ksnp_routes) {
+ route = list_entry(rtmp, ksock_route_t, ksnr_list);
+
+ if (route->ksnr_ipaddr == newroute->ksnr_ipaddr)
+ break;
+
+ route = NULL;
+ }
+ if (route != NULL)
+ continue;
+
+ best_iface = NULL;
+ best_nroutes = 0;
+ best_netmatch = 0;
+
+ LASSERT(net->ksnn_ninterfaces <= LNET_MAX_INTERFACES);
+
+ /* Select interface to connect from */
+ for (j = 0; j < net->ksnn_ninterfaces; j++) {
+ iface = &net->ksnn_interfaces[j];
+
+ /* Using this interface already? */
+ list_for_each(rtmp, &peer->ksnp_routes) {
+ route = list_entry(rtmp, ksock_route_t,
+ ksnr_list);
+
+ if (route->ksnr_myipaddr == iface->ksni_ipaddr)
+ break;
+
+ route = NULL;
+ }
+ if (route != NULL)
+ continue;
+
+ this_netmatch = (((iface->ksni_ipaddr ^
+ newroute->ksnr_ipaddr) &
+ iface->ksni_netmask) == 0) ? 1 : 0;
+
+ if (!(best_iface == NULL ||
+ best_netmatch < this_netmatch ||
+ (best_netmatch == this_netmatch &&
+ best_nroutes > iface->ksni_nroutes)))
+ continue;
+
+ best_iface = iface;
+ best_netmatch = this_netmatch;
+ best_nroutes = iface->ksni_nroutes;
+ }
+
+ if (best_iface == NULL)
+ continue;
+
+ newroute->ksnr_myipaddr = best_iface->ksni_ipaddr;
+ best_iface->ksni_nroutes++;
+
+ ksocknal_add_route_locked(peer, newroute);
+ newroute = NULL;
+ }
+
+ write_unlock_bh(global_lock);
+ if (newroute != NULL)
+ ksocknal_route_decref(newroute);
+}
+
+int
+ksocknal_accept(lnet_ni_t *ni, struct socket *sock)
+{
+ ksock_connreq_t *cr;
+ int rc;
+ __u32 peer_ip;
+ int peer_port;
+
+ rc = libcfs_sock_getaddr(sock, 1, &peer_ip, &peer_port);
+ LASSERT(rc == 0); /* we succeeded before */
+
+ LIBCFS_ALLOC(cr, sizeof(*cr));
+ if (cr == NULL) {
+ LCONSOLE_ERROR_MSG(0x12f, "Dropping connection request from %pI4h: memory exhausted\n",
+ &peer_ip);
+ return -ENOMEM;
+ }
+
+ lnet_ni_addref(ni);
+ cr->ksncr_ni = ni;
+ cr->ksncr_sock = sock;
+
+ spin_lock_bh(&ksocknal_data.ksnd_connd_lock);
+
+ list_add_tail(&cr->ksncr_list, &ksocknal_data.ksnd_connd_connreqs);
+ wake_up(&ksocknal_data.ksnd_connd_waitq);
+
+ spin_unlock_bh(&ksocknal_data.ksnd_connd_lock);
+ return 0;
+}
+
+static int
+ksocknal_connecting(ksock_peer_t *peer, __u32 ipaddr)
+{
+ ksock_route_t *route;
+
+ list_for_each_entry(route, &peer->ksnp_routes, ksnr_list) {
+
+ if (route->ksnr_ipaddr == ipaddr)
+ return route->ksnr_connecting;
+ }
+ return 0;
+}
+
+int
+ksocknal_create_conn(lnet_ni_t *ni, ksock_route_t *route,
+ struct socket *sock, int type)
+{
+ rwlock_t *global_lock = &ksocknal_data.ksnd_global_lock;
+ LIST_HEAD(zombies);
+ lnet_process_id_t peerid;
+ struct list_head *tmp;
+ __u64 incarnation;
+ ksock_conn_t *conn;
+ ksock_conn_t *conn2;
+ ksock_peer_t *peer = NULL;
+ ksock_peer_t *peer2;
+ ksock_sched_t *sched;
+ ksock_hello_msg_t *hello;
+ int cpt;
+ ksock_tx_t *tx;
+ ksock_tx_t *txtmp;
+ int rc;
+ int active;
+ char *warn = NULL;
+
+ active = (route != NULL);
+
+ LASSERT(active == (type != SOCKLND_CONN_NONE));
+
+ LIBCFS_ALLOC(conn, sizeof(*conn));
+ if (conn == NULL) {
+ rc = -ENOMEM;
+ goto failed_0;
+ }
+
+ conn->ksnc_peer = NULL;
+ conn->ksnc_route = NULL;
+ conn->ksnc_sock = sock;
+ /* 2 ref, 1 for conn, another extra ref prevents socket
+ * being closed before establishment of connection */
+ atomic_set(&conn->ksnc_sock_refcount, 2);
+ conn->ksnc_type = type;
+ ksocknal_lib_save_callback(sock, conn);
+ atomic_set(&conn->ksnc_conn_refcount, 1); /* 1 ref for me */
+
+ conn->ksnc_rx_ready = 0;
+ conn->ksnc_rx_scheduled = 0;
+
+ INIT_LIST_HEAD(&conn->ksnc_tx_queue);
+ conn->ksnc_tx_ready = 0;
+ conn->ksnc_tx_scheduled = 0;
+ conn->ksnc_tx_carrier = NULL;
+ atomic_set(&conn->ksnc_tx_nob, 0);
+
+ LIBCFS_ALLOC(hello, offsetof(ksock_hello_msg_t,
+ kshm_ips[LNET_MAX_INTERFACES]));
+ if (hello == NULL) {
+ rc = -ENOMEM;
+ goto failed_1;
+ }
+
+ /* stash conn's local and remote addrs */
+ rc = ksocknal_lib_get_conn_addrs(conn);
+ if (rc != 0)
+ goto failed_1;
+
+ /* Find out/confirm peer's NID and connection type and get the
+ * vector of interfaces she's willing to let me connect to.
+ * Passive connections use the listener timeout since the peer sends
+ * eagerly */
+
+ if (active) {
+ peer = route->ksnr_peer;
+ LASSERT(ni == peer->ksnp_ni);
+
+ /* Active connection sends HELLO eagerly */
+ hello->kshm_nips = ksocknal_local_ipvec(ni, hello->kshm_ips);
+ peerid = peer->ksnp_id;
+
+ write_lock_bh(global_lock);
+ conn->ksnc_proto = peer->ksnp_proto;
+ write_unlock_bh(global_lock);
+
+ if (conn->ksnc_proto == NULL) {
+ conn->ksnc_proto = &ksocknal_protocol_v3x;
+#if SOCKNAL_VERSION_DEBUG
+ if (*ksocknal_tunables.ksnd_protocol == 2)
+ conn->ksnc_proto = &ksocknal_protocol_v2x;
+ else if (*ksocknal_tunables.ksnd_protocol == 1)
+ conn->ksnc_proto = &ksocknal_protocol_v1x;
+#endif
+ }
+
+ rc = ksocknal_send_hello(ni, conn, peerid.nid, hello);
+ if (rc != 0)
+ goto failed_1;
+ } else {
+ peerid.nid = LNET_NID_ANY;
+ peerid.pid = LNET_PID_ANY;
+
+ /* Passive, get protocol from peer */
+ conn->ksnc_proto = NULL;
+ }
+
+ rc = ksocknal_recv_hello(ni, conn, hello, &peerid, &incarnation);
+ if (rc < 0)
+ goto failed_1;
+
+ LASSERT(rc == 0 || active);
+ LASSERT(conn->ksnc_proto != NULL);
+ LASSERT(peerid.nid != LNET_NID_ANY);
+
+ cpt = lnet_cpt_of_nid(peerid.nid);
+
+ if (active) {
+ ksocknal_peer_addref(peer);
+ write_lock_bh(global_lock);
+ } else {
+ rc = ksocknal_create_peer(&peer, ni, peerid);
+ if (rc != 0)
+ goto failed_1;
+
+ write_lock_bh(global_lock);
+
+ /* called with a ref on ni, so shutdown can't have started */
+ LASSERT(((ksock_net_t *) ni->ni_data)->ksnn_shutdown == 0);
+
+ peer2 = ksocknal_find_peer_locked(ni, peerid);
+ if (peer2 == NULL) {
+ /* NB this puts an "empty" peer in the peer
+ * table (which takes my ref) */
+ list_add_tail(&peer->ksnp_list,
+ ksocknal_nid2peerlist(peerid.nid));
+ } else {
+ ksocknal_peer_decref(peer);
+ peer = peer2;
+ }
+
+ /* +1 ref for me */
+ ksocknal_peer_addref(peer);
+ peer->ksnp_accepting++;
+
+ /* Am I already connecting to this guy? Resolve in
+ * favour of higher NID... */
+ if (peerid.nid < ni->ni_nid &&
+ ksocknal_connecting(peer, conn->ksnc_ipaddr)) {
+ rc = EALREADY;
+ warn = "connection race resolution";
+ goto failed_2;
+ }
+ }
+
+ if (peer->ksnp_closing ||
+ (active && route->ksnr_deleted)) {
+ /* peer/route got closed under me */
+ rc = -ESTALE;
+ warn = "peer/route removed";
+ goto failed_2;
+ }
+
+ if (peer->ksnp_proto == NULL) {
+ /* Never connected before.
+ * NB recv_hello may have returned EPROTO to signal my peer
+ * wants a different protocol than the one I asked for.
+ */
+ LASSERT(list_empty(&peer->ksnp_conns));
+
+ peer->ksnp_proto = conn->ksnc_proto;
+ peer->ksnp_incarnation = incarnation;
+ }
+
+ if (peer->ksnp_proto != conn->ksnc_proto ||
+ peer->ksnp_incarnation != incarnation) {
+ /* Peer rebooted or I've got the wrong protocol version */
+ ksocknal_close_peer_conns_locked(peer, 0, 0);
+
+ peer->ksnp_proto = NULL;
+ rc = ESTALE;
+ warn = peer->ksnp_incarnation != incarnation ?
+ "peer rebooted" :
+ "wrong proto version";
+ goto failed_2;
+ }
+
+ switch (rc) {
+ default:
+ LBUG();
+ case 0:
+ break;
+ case EALREADY:
+ warn = "lost conn race";
+ goto failed_2;
+ case EPROTO:
+ warn = "retry with different protocol version";
+ goto failed_2;
+ }
+
+ /* Refuse to duplicate an existing connection, unless this is a
+ * loopback connection */
+ if (conn->ksnc_ipaddr != conn->ksnc_myipaddr) {
+ list_for_each(tmp, &peer->ksnp_conns) {
+ conn2 = list_entry(tmp, ksock_conn_t, ksnc_list);
+
+ if (conn2->ksnc_ipaddr != conn->ksnc_ipaddr ||
+ conn2->ksnc_myipaddr != conn->ksnc_myipaddr ||
+ conn2->ksnc_type != conn->ksnc_type)
+ continue;
+
+ /* Reply on a passive connection attempt so the peer
+ * realises we're connected. */
+ LASSERT(rc == 0);
+ if (!active)
+ rc = EALREADY;
+
+ warn = "duplicate";
+ goto failed_2;
+ }
+ }
+
+ /* If the connection created by this route didn't bind to the IP
+ * address the route connected to, the connection/route matching
+ * code below probably isn't going to work. */
+ if (active &&
+ route->ksnr_ipaddr != conn->ksnc_ipaddr) {
+ CERROR("Route %s %pI4h connected to %pI4h\n",
+ libcfs_id2str(peer->ksnp_id),
+ &route->ksnr_ipaddr,
+ &conn->ksnc_ipaddr);
+ }
+
+ /* Search for a route corresponding to the new connection and
+ * create an association. This allows incoming connections created
+ * by routes in my peer to match my own route entries so I don't
+ * continually create duplicate routes. */
+ list_for_each(tmp, &peer->ksnp_routes) {
+ route = list_entry(tmp, ksock_route_t, ksnr_list);
+
+ if (route->ksnr_ipaddr != conn->ksnc_ipaddr)
+ continue;
+
+ ksocknal_associate_route_conn_locked(route, conn);
+ break;
+ }
+
+ conn->ksnc_peer = peer; /* conn takes my ref on peer */
+ peer->ksnp_last_alive = cfs_time_current();
+ peer->ksnp_send_keepalive = 0;
+ peer->ksnp_error = 0;
+
+ sched = ksocknal_choose_scheduler_locked(cpt);
+ sched->kss_nconns++;
+ conn->ksnc_scheduler = sched;
+
+ conn->ksnc_tx_last_post = cfs_time_current();
+ /* Set the deadline for the outgoing HELLO to drain */
+ conn->ksnc_tx_bufnob = sock->sk->sk_wmem_queued;
+ conn->ksnc_tx_deadline = cfs_time_shift(*ksocknal_tunables.ksnd_timeout);
+ mb(); /* order with adding to peer's conn list */
+
+ list_add(&conn->ksnc_list, &peer->ksnp_conns);
+ ksocknal_conn_addref(conn);
+
+ ksocknal_new_packet(conn, 0);
+
+ conn->ksnc_zc_capable = ksocknal_lib_zc_capable(conn);
+
+ /* Take packets blocking for this connection. */
+ list_for_each_entry_safe(tx, txtmp, &peer->ksnp_tx_queue, tx_list) {
+ if (conn->ksnc_proto->pro_match_tx(conn, tx, tx->tx_nonblk) == SOCKNAL_MATCH_NO)
+ continue;
+
+ list_del(&tx->tx_list);
+ ksocknal_queue_tx_locked(tx, conn);
+ }
+
+ write_unlock_bh(global_lock);
+
+ /* We've now got a new connection. Any errors from here on are just
+ * like "normal" comms errors and we close the connection normally.
+ * NB (a) we still have to send the reply HELLO for passive
+ * connections,
+ * (b) normal I/O on the conn is blocked until I setup and call the
+ * socket callbacks.
+ */
+
+ CDEBUG(D_NET, "New conn %s p %d.x %pI4h -> %pI4h/%d incarnation:%lld sched[%d:%d]\n",
+ libcfs_id2str(peerid), conn->ksnc_proto->pro_version,
+ &conn->ksnc_myipaddr, &conn->ksnc_ipaddr,
+ conn->ksnc_port, incarnation, cpt,
+ (int)(sched - &sched->kss_info->ksi_scheds[0]));
+
+ if (active) {
+ /* additional routes after interface exchange? */
+ ksocknal_create_routes(peer, conn->ksnc_port,
+ hello->kshm_ips, hello->kshm_nips);
+ } else {
+ hello->kshm_nips = ksocknal_select_ips(peer, hello->kshm_ips,
+ hello->kshm_nips);
+ rc = ksocknal_send_hello(ni, conn, peerid.nid, hello);
+ }
+
+ LIBCFS_FREE(hello, offsetof(ksock_hello_msg_t,
+ kshm_ips[LNET_MAX_INTERFACES]));
+
+ /* setup the socket AFTER I've received hello (it disables
+ * SO_LINGER). I might call back to the acceptor who may want
+ * to send a protocol version response and then close the
+ * socket; this ensures the socket only tears down after the
+ * response has been sent. */
+ if (rc == 0)
+ rc = ksocknal_lib_setup_sock(sock);
+
+ write_lock_bh(global_lock);
+
+ /* NB my callbacks block while I hold ksnd_global_lock */
+ ksocknal_lib_set_callback(sock, conn);
+
+ if (!active)
+ peer->ksnp_accepting--;
+
+ write_unlock_bh(global_lock);
+
+ if (rc != 0) {
+ write_lock_bh(global_lock);
+ if (!conn->ksnc_closing) {
+ /* could be closed by another thread */
+ ksocknal_close_conn_locked(conn, rc);
+ }
+ write_unlock_bh(global_lock);
+ } else if (ksocknal_connsock_addref(conn) == 0) {
+ /* Allow I/O to proceed. */
+ ksocknal_read_callback(conn);
+ ksocknal_write_callback(conn);
+ ksocknal_connsock_decref(conn);
+ }
+
+ ksocknal_connsock_decref(conn);
+ ksocknal_conn_decref(conn);
+ return rc;
+
+ failed_2:
+ if (!peer->ksnp_closing &&
+ list_empty(&peer->ksnp_conns) &&
+ list_empty(&peer->ksnp_routes)) {
+ list_add(&zombies, &peer->ksnp_tx_queue);
+ list_del_init(&peer->ksnp_tx_queue);
+ ksocknal_unlink_peer_locked(peer);
+ }
+
+ write_unlock_bh(global_lock);
+
+ if (warn != NULL) {
+ if (rc < 0)
+ CERROR("Not creating conn %s type %d: %s\n",
+ libcfs_id2str(peerid), conn->ksnc_type, warn);
+ else
+ CDEBUG(D_NET, "Not creating conn %s type %d: %s\n",
+ libcfs_id2str(peerid), conn->ksnc_type, warn);
+ }
+
+ if (!active) {
+ if (rc > 0) {
+ /* Request retry by replying with CONN_NONE
+ * ksnc_proto has been set already */
+ conn->ksnc_type = SOCKLND_CONN_NONE;
+ hello->kshm_nips = 0;
+ ksocknal_send_hello(ni, conn, peerid.nid, hello);
+ }
+
+ write_lock_bh(global_lock);
+ peer->ksnp_accepting--;
+ write_unlock_bh(global_lock);
+ }
+
+ ksocknal_txlist_done(ni, &zombies, 1);
+ ksocknal_peer_decref(peer);
+
+ failed_1:
+ if (hello != NULL)
+ LIBCFS_FREE(hello, offsetof(ksock_hello_msg_t,
+ kshm_ips[LNET_MAX_INTERFACES]));
+
+ LIBCFS_FREE(conn, sizeof(*conn));
+
+ failed_0:
+ libcfs_sock_release(sock);
+ return rc;
+}
+
+void
+ksocknal_close_conn_locked(ksock_conn_t *conn, int error)
+{
+ /* This just does the immmediate housekeeping, and queues the
+ * connection for the reaper to terminate.
+ * Caller holds ksnd_global_lock exclusively in irq context */
+ ksock_peer_t *peer = conn->ksnc_peer;
+ ksock_route_t *route;
+ ksock_conn_t *conn2;
+ struct list_head *tmp;
+
+ LASSERT(peer->ksnp_error == 0);
+ LASSERT(!conn->ksnc_closing);
+ conn->ksnc_closing = 1;
+
+ /* ksnd_deathrow_conns takes over peer's ref */
+ list_del(&conn->ksnc_list);
+
+ route = conn->ksnc_route;
+ if (route != NULL) {
+ /* dissociate conn from route... */
+ LASSERT(!route->ksnr_deleted);
+ LASSERT((route->ksnr_connected & (1 << conn->ksnc_type)) != 0);
+
+ conn2 = NULL;
+ list_for_each(tmp, &peer->ksnp_conns) {
+ conn2 = list_entry(tmp, ksock_conn_t, ksnc_list);
+
+ if (conn2->ksnc_route == route &&
+ conn2->ksnc_type == conn->ksnc_type)
+ break;
+
+ conn2 = NULL;
+ }
+ if (conn2 == NULL)
+ route->ksnr_connected &= ~(1 << conn->ksnc_type);
+
+ conn->ksnc_route = NULL;
+
+#if 0 /* irrelevant with only eager routes */
+ /* make route least favourite */
+ list_del(&route->ksnr_list);
+ list_add_tail(&route->ksnr_list, &peer->ksnp_routes);
+#endif
+ ksocknal_route_decref(route); /* drop conn's ref on route */
+ }
+
+ if (list_empty(&peer->ksnp_conns)) {
+ /* No more connections to this peer */
+
+ if (!list_empty(&peer->ksnp_tx_queue)) {
+ ksock_tx_t *tx;
+
+ LASSERT(conn->ksnc_proto == &ksocknal_protocol_v3x);
+
+ /* throw them to the last connection...,
+ * these TXs will be send to /dev/null by scheduler */
+ list_for_each_entry(tx, &peer->ksnp_tx_queue,
+ tx_list)
+ ksocknal_tx_prep(conn, tx);
+
+ spin_lock_bh(&conn->ksnc_scheduler->kss_lock);
+ list_splice_init(&peer->ksnp_tx_queue,
+ &conn->ksnc_tx_queue);
+ spin_unlock_bh(&conn->ksnc_scheduler->kss_lock);
+ }
+
+ peer->ksnp_proto = NULL; /* renegotiate protocol version */
+ peer->ksnp_error = error; /* stash last conn close reason */
+
+ if (list_empty(&peer->ksnp_routes)) {
+ /* I've just closed last conn belonging to a
+ * peer with no routes to it */
+ ksocknal_unlink_peer_locked(peer);
+ }
+ }
+
+ spin_lock_bh(&ksocknal_data.ksnd_reaper_lock);
+
+ list_add_tail(&conn->ksnc_list,
+ &ksocknal_data.ksnd_deathrow_conns);
+ wake_up(&ksocknal_data.ksnd_reaper_waitq);
+
+ spin_unlock_bh(&ksocknal_data.ksnd_reaper_lock);
+}
+
+void
+ksocknal_peer_failed(ksock_peer_t *peer)
+{
+ int notify = 0;
+ unsigned long last_alive = 0;
+
+ /* There has been a connection failure or comms error; but I'll only
+ * tell LNET I think the peer is dead if it's to another kernel and
+ * there are no connections or connection attempts in existence. */
+
+ read_lock(&ksocknal_data.ksnd_global_lock);
+
+ if ((peer->ksnp_id.pid & LNET_PID_USERFLAG) == 0 &&
+ list_empty(&peer->ksnp_conns) &&
+ peer->ksnp_accepting == 0 &&
+ ksocknal_find_connecting_route_locked(peer) == NULL) {
+ notify = 1;
+ last_alive = peer->ksnp_last_alive;
+ }
+
+ read_unlock(&ksocknal_data.ksnd_global_lock);
+
+ if (notify)
+ lnet_notify(peer->ksnp_ni, peer->ksnp_id.nid, 0,
+ last_alive);
+}
+
+void
+ksocknal_finalize_zcreq(ksock_conn_t *conn)
+{
+ ksock_peer_t *peer = conn->ksnc_peer;
+ ksock_tx_t *tx;
+ ksock_tx_t *tmp;
+ LIST_HEAD(zlist);
+
+ /* NB safe to finalize TXs because closing of socket will
+ * abort all buffered data */
+ LASSERT(conn->ksnc_sock == NULL);
+
+ spin_lock(&peer->ksnp_lock);
+
+ list_for_each_entry_safe(tx, tmp, &peer->ksnp_zc_req_list, tx_zc_list) {
+ if (tx->tx_conn != conn)
+ continue;
+
+ LASSERT(tx->tx_msg.ksm_zc_cookies[0] != 0);
+
+ tx->tx_msg.ksm_zc_cookies[0] = 0;
+ tx->tx_zc_aborted = 1; /* mark it as not-acked */
+ list_del(&tx->tx_zc_list);
+ list_add(&tx->tx_zc_list, &zlist);
+ }
+
+ spin_unlock(&peer->ksnp_lock);
+
+ while (!list_empty(&zlist)) {
+ tx = list_entry(zlist.next, ksock_tx_t, tx_zc_list);
+
+ list_del(&tx->tx_zc_list);
+ ksocknal_tx_decref(tx);
+ }
+}
+
+void
+ksocknal_terminate_conn(ksock_conn_t *conn)
+{
+ /* This gets called by the reaper (guaranteed thread context) to
+ * disengage the socket from its callbacks and close it.
+ * ksnc_refcount will eventually hit zero, and then the reaper will
+ * destroy it. */
+ ksock_peer_t *peer = conn->ksnc_peer;
+ ksock_sched_t *sched = conn->ksnc_scheduler;
+ int failed = 0;
+
+ LASSERT(conn->ksnc_closing);
+
+ /* wake up the scheduler to "send" all remaining packets to /dev/null */
+ spin_lock_bh(&sched->kss_lock);
+
+ /* a closing conn is always ready to tx */
+ conn->ksnc_tx_ready = 1;
+
+ if (!conn->ksnc_tx_scheduled &&
+ !list_empty(&conn->ksnc_tx_queue)) {
+ list_add_tail(&conn->ksnc_tx_list,
+ &sched->kss_tx_conns);
+ conn->ksnc_tx_scheduled = 1;
+ /* extra ref for scheduler */
+ ksocknal_conn_addref(conn);
+
+ wake_up(&sched->kss_waitq);
+ }
+
+ spin_unlock_bh(&sched->kss_lock);
+
+ /* serialise with callbacks */
+ write_lock_bh(&ksocknal_data.ksnd_global_lock);
+
+ ksocknal_lib_reset_callback(conn->ksnc_sock, conn);
+
+ /* OK, so this conn may not be completely disengaged from its
+ * scheduler yet, but it _has_ committed to terminate... */
+ conn->ksnc_scheduler->kss_nconns--;
+
+ if (peer->ksnp_error != 0) {
+ /* peer's last conn closed in error */
+ LASSERT(list_empty(&peer->ksnp_conns));
+ failed = 1;
+ peer->ksnp_error = 0; /* avoid multiple notifications */
+ }
+
+ write_unlock_bh(&ksocknal_data.ksnd_global_lock);
+
+ if (failed)
+ ksocknal_peer_failed(peer);
+
+ /* The socket is closed on the final put; either here, or in
+ * ksocknal_{send,recv}msg(). Since we set up the linger2 option
+ * when the connection was established, this will close the socket
+ * immediately, aborting anything buffered in it. Any hung
+ * zero-copy transmits will therefore complete in finite time. */
+ ksocknal_connsock_decref(conn);
+}
+
+void
+ksocknal_queue_zombie_conn(ksock_conn_t *conn)
+{
+ /* Queue the conn for the reaper to destroy */
+
+ LASSERT(atomic_read(&conn->ksnc_conn_refcount) == 0);
+ spin_lock_bh(&ksocknal_data.ksnd_reaper_lock);
+
+ list_add_tail(&conn->ksnc_list, &ksocknal_data.ksnd_zombie_conns);
+ wake_up(&ksocknal_data.ksnd_reaper_waitq);
+
+ spin_unlock_bh(&ksocknal_data.ksnd_reaper_lock);
+}
+
+void
+ksocknal_destroy_conn(ksock_conn_t *conn)
+{
+ unsigned long last_rcv;
+
+ /* Final coup-de-grace of the reaper */
+ CDEBUG(D_NET, "connection %p\n", conn);
+
+ LASSERT(atomic_read(&conn->ksnc_conn_refcount) == 0);
+ LASSERT(atomic_read(&conn->ksnc_sock_refcount) == 0);
+ LASSERT(conn->ksnc_sock == NULL);
+ LASSERT(conn->ksnc_route == NULL);
+ LASSERT(!conn->ksnc_tx_scheduled);
+ LASSERT(!conn->ksnc_rx_scheduled);
+ LASSERT(list_empty(&conn->ksnc_tx_queue));
+
+ /* complete current receive if any */
+ switch (conn->ksnc_rx_state) {
+ case SOCKNAL_RX_LNET_PAYLOAD:
+ last_rcv = conn->ksnc_rx_deadline -
+ cfs_time_seconds(*ksocknal_tunables.ksnd_timeout);
+ CERROR("Completing partial receive from %s[%d], ip %pI4h:%d, with error, wanted: %d, left: %d, last alive is %ld secs ago\n",
+ libcfs_id2str(conn->ksnc_peer->ksnp_id), conn->ksnc_type,
+ &conn->ksnc_ipaddr, conn->ksnc_port,
+ conn->ksnc_rx_nob_wanted, conn->ksnc_rx_nob_left,
+ cfs_duration_sec(cfs_time_sub(cfs_time_current(),
+ last_rcv)));
+ lnet_finalize(conn->ksnc_peer->ksnp_ni,
+ conn->ksnc_cookie, -EIO);
+ break;
+ case SOCKNAL_RX_LNET_HEADER:
+ if (conn->ksnc_rx_started)
+ CERROR("Incomplete receive of lnet header from %s, ip %pI4h:%d, with error, protocol: %d.x.\n",
+ libcfs_id2str(conn->ksnc_peer->ksnp_id),
+ &conn->ksnc_ipaddr, conn->ksnc_port,
+ conn->ksnc_proto->pro_version);
+ break;
+ case SOCKNAL_RX_KSM_HEADER:
+ if (conn->ksnc_rx_started)
+ CERROR("Incomplete receive of ksock message from %s, ip %pI4h:%d, with error, protocol: %d.x.\n",
+ libcfs_id2str(conn->ksnc_peer->ksnp_id),
+ &conn->ksnc_ipaddr, conn->ksnc_port,
+ conn->ksnc_proto->pro_version);
+ break;
+ case SOCKNAL_RX_SLOP:
+ if (conn->ksnc_rx_started)
+ CERROR("Incomplete receive of slops from %s, ip %pI4h:%d, with error\n",
+ libcfs_id2str(conn->ksnc_peer->ksnp_id),
+ &conn->ksnc_ipaddr, conn->ksnc_port);
+ break;
+ default:
+ LBUG();
+ break;
+ }
+
+ ksocknal_peer_decref(conn->ksnc_peer);
+
+ LIBCFS_FREE(conn, sizeof(*conn));
+}
+
+int
+ksocknal_close_peer_conns_locked(ksock_peer_t *peer, __u32 ipaddr, int why)
+{
+ ksock_conn_t *conn;
+ struct list_head *ctmp;
+ struct list_head *cnxt;
+ int count = 0;
+
+ list_for_each_safe(ctmp, cnxt, &peer->ksnp_conns) {
+ conn = list_entry(ctmp, ksock_conn_t, ksnc_list);
+
+ if (ipaddr == 0 ||
+ conn->ksnc_ipaddr == ipaddr) {
+ count++;
+ ksocknal_close_conn_locked(conn, why);
+ }
+ }
+
+ return count;
+}
+
+int
+ksocknal_close_conn_and_siblings(ksock_conn_t *conn, int why)
+{
+ ksock_peer_t *peer = conn->ksnc_peer;
+ __u32 ipaddr = conn->ksnc_ipaddr;
+ int count;
+
+ write_lock_bh(&ksocknal_data.ksnd_global_lock);
+
+ count = ksocknal_close_peer_conns_locked(peer, ipaddr, why);
+
+ write_unlock_bh(&ksocknal_data.ksnd_global_lock);
+
+ return count;
+}
+
+int
+ksocknal_close_matching_conns(lnet_process_id_t id, __u32 ipaddr)
+{
+ ksock_peer_t *peer;
+ struct list_head *ptmp;
+ struct list_head *pnxt;
+ int lo;
+ int hi;
+ int i;
+ int count = 0;
+
+ write_lock_bh(&ksocknal_data.ksnd_global_lock);
+
+ if (id.nid != LNET_NID_ANY)
+ lo = hi = (int)(ksocknal_nid2peerlist(id.nid) - ksocknal_data.ksnd_peers);
+ else {
+ lo = 0;
+ hi = ksocknal_data.ksnd_peer_hash_size - 1;
+ }
+
+ for (i = lo; i <= hi; i++) {
+ list_for_each_safe(ptmp, pnxt,
+ &ksocknal_data.ksnd_peers[i]) {
+
+ peer = list_entry(ptmp, ksock_peer_t, ksnp_list);
+
+ if (!((id.nid == LNET_NID_ANY || id.nid == peer->ksnp_id.nid) &&
+ (id.pid == LNET_PID_ANY || id.pid == peer->ksnp_id.pid)))
+ continue;
+
+ count += ksocknal_close_peer_conns_locked(peer, ipaddr, 0);
+ }
+ }
+
+ write_unlock_bh(&ksocknal_data.ksnd_global_lock);
+
+ /* wildcards always succeed */
+ if (id.nid == LNET_NID_ANY || id.pid == LNET_PID_ANY || ipaddr == 0)
+ return 0;
+
+ if (count == 0)
+ return -ENOENT;
+ else
+ return 0;
+}
+
+void
+ksocknal_notify(lnet_ni_t *ni, lnet_nid_t gw_nid, int alive)
+{
+ /* The router is telling me she's been notified of a change in
+ * gateway state.... */
+ lnet_process_id_t id = {0};
+
+ id.nid = gw_nid;
+ id.pid = LNET_PID_ANY;
+
+ CDEBUG(D_NET, "gw %s %s\n", libcfs_nid2str(gw_nid),
+ alive ? "up" : "down");
+
+ if (!alive) {
+ /* If the gateway crashed, close all open connections... */
+ ksocknal_close_matching_conns(id, 0);
+ return;
+ }
+
+ /* ...otherwise do nothing. We can only establish new connections
+ * if we have autroutes, and these connect on demand. */
+}
+
+void
+ksocknal_query(lnet_ni_t *ni, lnet_nid_t nid, unsigned long *when)
+{
+ int connect = 1;
+ unsigned long last_alive = 0;
+ unsigned long now = cfs_time_current();
+ ksock_peer_t *peer = NULL;
+ rwlock_t *glock = &ksocknal_data.ksnd_global_lock;
+ lnet_process_id_t id = {.nid = nid, .pid = LUSTRE_SRV_LNET_PID};
+
+ read_lock(glock);
+
+ peer = ksocknal_find_peer_locked(ni, id);
+ if (peer != NULL) {
+ struct list_head *tmp;
+ ksock_conn_t *conn;
+ int bufnob;
+
+ list_for_each(tmp, &peer->ksnp_conns) {
+ conn = list_entry(tmp, ksock_conn_t, ksnc_list);
+ bufnob = conn->ksnc_sock->sk->sk_wmem_queued;
+
+ if (bufnob < conn->ksnc_tx_bufnob) {
+ /* something got ACKed */
+ conn->ksnc_tx_deadline =
+ cfs_time_shift(*ksocknal_tunables.ksnd_timeout);
+ peer->ksnp_last_alive = now;
+ conn->ksnc_tx_bufnob = bufnob;
+ }
+ }
+
+ last_alive = peer->ksnp_last_alive;
+ if (ksocknal_find_connectable_route_locked(peer) == NULL)
+ connect = 0;
+ }
+
+ read_unlock(glock);
+
+ if (last_alive != 0)
+ *when = last_alive;
+
+ CDEBUG(D_NET, "Peer %s %p, alive %ld secs ago, connect %d\n",
+ libcfs_nid2str(nid), peer,
+ last_alive ? cfs_duration_sec(now - last_alive) : -1,
+ connect);
+
+ if (!connect)
+ return;
+
+ ksocknal_add_peer(ni, id, LNET_NIDADDR(nid), lnet_acceptor_port());
+
+ write_lock_bh(glock);
+
+ peer = ksocknal_find_peer_locked(ni, id);
+ if (peer != NULL)
+ ksocknal_launch_all_connections_locked(peer);
+
+ write_unlock_bh(glock);
+ return;
+}
+
+static void
+ksocknal_push_peer(ksock_peer_t *peer)
+{
+ int index;
+ int i;
+ struct list_head *tmp;
+ ksock_conn_t *conn;
+
+ for (index = 0; ; index++) {
+ read_lock(&ksocknal_data.ksnd_global_lock);
+
+ i = 0;
+ conn = NULL;
+
+ list_for_each(tmp, &peer->ksnp_conns) {
+ if (i++ == index) {
+ conn = list_entry(tmp, ksock_conn_t,
+ ksnc_list);
+ ksocknal_conn_addref(conn);
+ break;
+ }
+ }
+
+ read_unlock(&ksocknal_data.ksnd_global_lock);
+
+ if (conn == NULL)
+ break;
+
+ ksocknal_lib_push_conn(conn);
+ ksocknal_conn_decref(conn);
+ }
+}
+
+static int
+ksocknal_push(lnet_ni_t *ni, lnet_process_id_t id)
+{
+ ksock_peer_t *peer;
+ struct list_head *tmp;
+ int index;
+ int i;
+ int j;
+ int rc = -ENOENT;
+
+ for (i = 0; i < ksocknal_data.ksnd_peer_hash_size; i++) {
+ for (j = 0; ; j++) {
+ read_lock(&ksocknal_data.ksnd_global_lock);
+
+ index = 0;
+ peer = NULL;
+
+ list_for_each(tmp, &ksocknal_data.ksnd_peers[i]) {
+ peer = list_entry(tmp, ksock_peer_t,
+ ksnp_list);
+
+ if (!((id.nid == LNET_NID_ANY ||
+ id.nid == peer->ksnp_id.nid) &&
+ (id.pid == LNET_PID_ANY ||
+ id.pid == peer->ksnp_id.pid))) {
+ peer = NULL;
+ continue;
+ }
+
+ if (index++ == j) {
+ ksocknal_peer_addref(peer);
+ break;
+ }
+ }
+
+ read_unlock(&ksocknal_data.ksnd_global_lock);
+
+ if (peer != NULL) {
+ rc = 0;
+ ksocknal_push_peer(peer);
+ ksocknal_peer_decref(peer);
+ }
+ }
+
+ }
+
+ return rc;
+}
+
+static int
+ksocknal_add_interface(lnet_ni_t *ni, __u32 ipaddress, __u32 netmask)
+{
+ ksock_net_t *net = ni->ni_data;
+ ksock_interface_t *iface;
+ int rc;
+ int i;
+ int j;
+ struct list_head *ptmp;
+ ksock_peer_t *peer;
+ struct list_head *rtmp;
+ ksock_route_t *route;
+
+ if (ipaddress == 0 ||
+ netmask == 0)
+ return -EINVAL;
+
+ write_lock_bh(&ksocknal_data.ksnd_global_lock);
+
+ iface = ksocknal_ip2iface(ni, ipaddress);
+ if (iface != NULL) {
+ /* silently ignore dups */
+ rc = 0;
+ } else if (net->ksnn_ninterfaces == LNET_MAX_INTERFACES) {
+ rc = -ENOSPC;
+ } else {
+ iface = &net->ksnn_interfaces[net->ksnn_ninterfaces++];
+
+ iface->ksni_ipaddr = ipaddress;
+ iface->ksni_netmask = netmask;
+ iface->ksni_nroutes = 0;
+ iface->ksni_npeers = 0;
+
+ for (i = 0; i < ksocknal_data.ksnd_peer_hash_size; i++) {
+ list_for_each(ptmp, &ksocknal_data.ksnd_peers[i]) {
+ peer = list_entry(ptmp, ksock_peer_t,
+ ksnp_list);
+
+ for (j = 0; j < peer->ksnp_n_passive_ips; j++)
+ if (peer->ksnp_passive_ips[j] == ipaddress)
+ iface->ksni_npeers++;
+
+ list_for_each(rtmp, &peer->ksnp_routes) {
+ route = list_entry(rtmp,
+ ksock_route_t,
+ ksnr_list);
+
+ if (route->ksnr_myipaddr == ipaddress)
+ iface->ksni_nroutes++;
+ }
+ }
+ }
+
+ rc = 0;
+ /* NB only new connections will pay attention to the new interface! */
+ }
+
+ write_unlock_bh(&ksocknal_data.ksnd_global_lock);
+
+ return rc;
+}
+
+static void
+ksocknal_peer_del_interface_locked(ksock_peer_t *peer, __u32 ipaddr)
+{
+ struct list_head *tmp;
+ struct list_head *nxt;
+ ksock_route_t *route;
+ ksock_conn_t *conn;
+ int i;
+ int j;
+
+ for (i = 0; i < peer->ksnp_n_passive_ips; i++)
+ if (peer->ksnp_passive_ips[i] == ipaddr) {
+ for (j = i+1; j < peer->ksnp_n_passive_ips; j++)
+ peer->ksnp_passive_ips[j-1] =
+ peer->ksnp_passive_ips[j];
+ peer->ksnp_n_passive_ips--;
+ break;
+ }
+
+ list_for_each_safe(tmp, nxt, &peer->ksnp_routes) {
+ route = list_entry(tmp, ksock_route_t, ksnr_list);
+
+ if (route->ksnr_myipaddr != ipaddr)
+ continue;
+
+ if (route->ksnr_share_count != 0) {
+ /* Manually created; keep, but unbind */
+ route->ksnr_myipaddr = 0;
+ } else {
+ ksocknal_del_route_locked(route);
+ }
+ }
+
+ list_for_each_safe(tmp, nxt, &peer->ksnp_conns) {
+ conn = list_entry(tmp, ksock_conn_t, ksnc_list);
+
+ if (conn->ksnc_myipaddr == ipaddr)
+ ksocknal_close_conn_locked(conn, 0);
+ }
+}
+
+static int
+ksocknal_del_interface(lnet_ni_t *ni, __u32 ipaddress)
+{
+ ksock_net_t *net = ni->ni_data;
+ int rc = -ENOENT;
+ struct list_head *tmp;
+ struct list_head *nxt;
+ ksock_peer_t *peer;
+ __u32 this_ip;
+ int i;
+ int j;
+
+ write_lock_bh(&ksocknal_data.ksnd_global_lock);
+
+ for (i = 0; i < net->ksnn_ninterfaces; i++) {
+ this_ip = net->ksnn_interfaces[i].ksni_ipaddr;
+
+ if (!(ipaddress == 0 ||
+ ipaddress == this_ip))
+ continue;
+
+ rc = 0;
+
+ for (j = i+1; j < net->ksnn_ninterfaces; j++)
+ net->ksnn_interfaces[j-1] =
+ net->ksnn_interfaces[j];
+
+ net->ksnn_ninterfaces--;
+
+ for (j = 0; j < ksocknal_data.ksnd_peer_hash_size; j++) {
+ list_for_each_safe(tmp, nxt,
+ &ksocknal_data.ksnd_peers[j]) {
+ peer = list_entry(tmp, ksock_peer_t,
+ ksnp_list);
+
+ if (peer->ksnp_ni != ni)
+ continue;
+
+ ksocknal_peer_del_interface_locked(peer, this_ip);
+ }
+ }
+ }
+
+ write_unlock_bh(&ksocknal_data.ksnd_global_lock);
+
+ return rc;
+}
+
+int
+ksocknal_ctl(lnet_ni_t *ni, unsigned int cmd, void *arg)
+{
+ lnet_process_id_t id = {0};
+ struct libcfs_ioctl_data *data = arg;
+ int rc;
+
+ switch (cmd) {
+ case IOC_LIBCFS_GET_INTERFACE: {
+ ksock_net_t *net = ni->ni_data;
+ ksock_interface_t *iface;
+
+ read_lock(&ksocknal_data.ksnd_global_lock);
+
+ if (data->ioc_count >= (__u32)net->ksnn_ninterfaces) {
+ rc = -ENOENT;
+ } else {
+ rc = 0;
+ iface = &net->ksnn_interfaces[data->ioc_count];
+
+ data->ioc_u32[0] = iface->ksni_ipaddr;
+ data->ioc_u32[1] = iface->ksni_netmask;
+ data->ioc_u32[2] = iface->ksni_npeers;
+ data->ioc_u32[3] = iface->ksni_nroutes;
+ }
+
+ read_unlock(&ksocknal_data.ksnd_global_lock);
+ return rc;
+ }
+
+ case IOC_LIBCFS_ADD_INTERFACE:
+ return ksocknal_add_interface(ni,
+ data->ioc_u32[0], /* IP address */
+ data->ioc_u32[1]); /* net mask */
+
+ case IOC_LIBCFS_DEL_INTERFACE:
+ return ksocknal_del_interface(ni,
+ data->ioc_u32[0]); /* IP address */
+
+ case IOC_LIBCFS_GET_PEER: {
+ __u32 myip = 0;
+ __u32 ip = 0;
+ int port = 0;
+ int conn_count = 0;
+ int share_count = 0;
+
+ rc = ksocknal_get_peer_info(ni, data->ioc_count,
+ &id, &myip, &ip, &port,
+ &conn_count, &share_count);
+ if (rc != 0)
+ return rc;
+
+ data->ioc_nid = id.nid;
+ data->ioc_count = share_count;
+ data->ioc_u32[0] = ip;
+ data->ioc_u32[1] = port;
+ data->ioc_u32[2] = myip;
+ data->ioc_u32[3] = conn_count;
+ data->ioc_u32[4] = id.pid;
+ return 0;
+ }
+
+ case IOC_LIBCFS_ADD_PEER:
+ id.nid = data->ioc_nid;
+ id.pid = LUSTRE_SRV_LNET_PID;
+ return ksocknal_add_peer(ni, id,
+ data->ioc_u32[0], /* IP */
+ data->ioc_u32[1]); /* port */
+
+ case IOC_LIBCFS_DEL_PEER:
+ id.nid = data->ioc_nid;
+ id.pid = LNET_PID_ANY;
+ return ksocknal_del_peer(ni, id,
+ data->ioc_u32[0]); /* IP */
+
+ case IOC_LIBCFS_GET_CONN: {
+ int txmem;
+ int rxmem;
+ int nagle;
+ ksock_conn_t *conn = ksocknal_get_conn_by_idx(ni, data->ioc_count);
+
+ if (conn == NULL)
+ return -ENOENT;
+
+ ksocknal_lib_get_conn_tunables(conn, &txmem, &rxmem, &nagle);
+
+ data->ioc_count = txmem;
+ data->ioc_nid = conn->ksnc_peer->ksnp_id.nid;
+ data->ioc_flags = nagle;
+ data->ioc_u32[0] = conn->ksnc_ipaddr;
+ data->ioc_u32[1] = conn->ksnc_port;
+ data->ioc_u32[2] = conn->ksnc_myipaddr;
+ data->ioc_u32[3] = conn->ksnc_type;
+ data->ioc_u32[4] = conn->ksnc_scheduler->kss_info->ksi_cpt;
+ data->ioc_u32[5] = rxmem;
+ data->ioc_u32[6] = conn->ksnc_peer->ksnp_id.pid;
+ ksocknal_conn_decref(conn);
+ return 0;
+ }
+
+ case IOC_LIBCFS_CLOSE_CONNECTION:
+ id.nid = data->ioc_nid;
+ id.pid = LNET_PID_ANY;
+ return ksocknal_close_matching_conns(id,
+ data->ioc_u32[0]);
+
+ case IOC_LIBCFS_REGISTER_MYNID:
+ /* Ignore if this is a noop */
+ if (data->ioc_nid == ni->ni_nid)
+ return 0;
+
+ CERROR("obsolete IOC_LIBCFS_REGISTER_MYNID: %s(%s)\n",
+ libcfs_nid2str(data->ioc_nid),
+ libcfs_nid2str(ni->ni_nid));
+ return -EINVAL;
+
+ case IOC_LIBCFS_PUSH_CONNECTION:
+ id.nid = data->ioc_nid;
+ id.pid = LNET_PID_ANY;
+ return ksocknal_push(ni, id);
+
+ default:
+ return -EINVAL;
+ }
+ /* not reached */
+}
+
+static void
+ksocknal_free_buffers(void)
+{
+ LASSERT(atomic_read(&ksocknal_data.ksnd_nactive_txs) == 0);
+
+ if (ksocknal_data.ksnd_sched_info != NULL) {
+ struct ksock_sched_info *info;
+ int i;
+
+ cfs_percpt_for_each(info, i, ksocknal_data.ksnd_sched_info) {
+ if (info->ksi_scheds != NULL) {
+ LIBCFS_FREE(info->ksi_scheds,
+ info->ksi_nthreads_max *
+ sizeof(info->ksi_scheds[0]));
+ }
+ }
+ cfs_percpt_free(ksocknal_data.ksnd_sched_info);
+ }
+
+ LIBCFS_FREE(ksocknal_data.ksnd_peers,
+ sizeof(struct list_head) *
+ ksocknal_data.ksnd_peer_hash_size);
+
+ spin_lock(&ksocknal_data.ksnd_tx_lock);
+
+ if (!list_empty(&ksocknal_data.ksnd_idle_noop_txs)) {
+ struct list_head zlist;
+ ksock_tx_t *tx;
+
+ list_add(&zlist, &ksocknal_data.ksnd_idle_noop_txs);
+ list_del_init(&ksocknal_data.ksnd_idle_noop_txs);
+ spin_unlock(&ksocknal_data.ksnd_tx_lock);
+
+ while (!list_empty(&zlist)) {
+ tx = list_entry(zlist.next, ksock_tx_t, tx_list);
+ list_del(&tx->tx_list);
+ LIBCFS_FREE(tx, tx->tx_desc_size);
+ }
+ } else {
+ spin_unlock(&ksocknal_data.ksnd_tx_lock);
+ }
+}
+
+static void
+ksocknal_base_shutdown(void)
+{
+ struct ksock_sched_info *info;
+ ksock_sched_t *sched;
+ int i;
+ int j;
+
+ CDEBUG(D_MALLOC, "before NAL cleanup: kmem %d\n",
+ atomic_read(&libcfs_kmemory));
+ LASSERT(ksocknal_data.ksnd_nnets == 0);
+
+ switch (ksocknal_data.ksnd_init) {
+ default:
+ LASSERT(0);
+
+ case SOCKNAL_INIT_ALL:
+ case SOCKNAL_INIT_DATA:
+ LASSERT(ksocknal_data.ksnd_peers != NULL);
+ for (i = 0; i < ksocknal_data.ksnd_peer_hash_size; i++) {
+ LASSERT(list_empty(&ksocknal_data.ksnd_peers[i]));
+ }
+
+ LASSERT(list_empty(&ksocknal_data.ksnd_nets));
+ LASSERT(list_empty(&ksocknal_data.ksnd_enomem_conns));
+ LASSERT(list_empty(&ksocknal_data.ksnd_zombie_conns));
+ LASSERT(list_empty(&ksocknal_data.ksnd_connd_connreqs));
+ LASSERT(list_empty(&ksocknal_data.ksnd_connd_routes));
+
+ if (ksocknal_data.ksnd_sched_info != NULL) {
+ cfs_percpt_for_each(info, i,
+ ksocknal_data.ksnd_sched_info) {
+ if (info->ksi_scheds == NULL)
+ continue;
+
+ for (j = 0; j < info->ksi_nthreads_max; j++) {
+
+ sched = &info->ksi_scheds[j];
+ LASSERT(list_empty(
+ &sched->kss_tx_conns));
+ LASSERT(list_empty(
+ &sched->kss_rx_conns));
+ LASSERT(list_empty(
+ &sched->kss_zombie_noop_txs));
+ LASSERT(sched->kss_nconns == 0);
+ }
+ }
+ }
+
+ /* flag threads to terminate; wake and wait for them to die */
+ ksocknal_data.ksnd_shuttingdown = 1;
+ wake_up_all(&ksocknal_data.ksnd_connd_waitq);
+ wake_up_all(&ksocknal_data.ksnd_reaper_waitq);
+
+ if (ksocknal_data.ksnd_sched_info != NULL) {
+ cfs_percpt_for_each(info, i,
+ ksocknal_data.ksnd_sched_info) {
+ if (info->ksi_scheds == NULL)
+ continue;
+
+ for (j = 0; j < info->ksi_nthreads_max; j++) {
+ sched = &info->ksi_scheds[j];
+ wake_up_all(&sched->kss_waitq);
+ }
+ }
+ }
+
+ i = 4;
+ read_lock(&ksocknal_data.ksnd_global_lock);
+ while (ksocknal_data.ksnd_nthreads != 0) {
+ i++;
+ CDEBUG(((i & (-i)) == i) ? D_WARNING : D_NET, /* power of 2? */
+ "waiting for %d threads to terminate\n",
+ ksocknal_data.ksnd_nthreads);
+ read_unlock(&ksocknal_data.ksnd_global_lock);
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ schedule_timeout(cfs_time_seconds(1));
+ read_lock(&ksocknal_data.ksnd_global_lock);
+ }
+ read_unlock(&ksocknal_data.ksnd_global_lock);
+
+ ksocknal_free_buffers();
+
+ ksocknal_data.ksnd_init = SOCKNAL_INIT_NOTHING;
+ break;
+ }
+
+ CDEBUG(D_MALLOC, "after NAL cleanup: kmem %d\n",
+ atomic_read(&libcfs_kmemory));
+
+ module_put(THIS_MODULE);
+}
+
+static __u64
+ksocknal_new_incarnation(void)
+{
+
+ /* The incarnation number is the time this module loaded and it
+ * identifies this particular instance of the socknal.
+ */
+ return ktime_get_ns();
+}
+
+static int
+ksocknal_base_startup(void)
+{
+ struct ksock_sched_info *info;
+ int rc;
+ int i;
+
+ LASSERT(ksocknal_data.ksnd_init == SOCKNAL_INIT_NOTHING);
+ LASSERT(ksocknal_data.ksnd_nnets == 0);
+
+ memset(&ksocknal_data, 0, sizeof(ksocknal_data)); /* zero pointers */
+
+ ksocknal_data.ksnd_peer_hash_size = SOCKNAL_PEER_HASH_SIZE;
+ LIBCFS_ALLOC(ksocknal_data.ksnd_peers,
+ sizeof(struct list_head) *
+ ksocknal_data.ksnd_peer_hash_size);
+ if (ksocknal_data.ksnd_peers == NULL)
+ return -ENOMEM;
+
+ for (i = 0; i < ksocknal_data.ksnd_peer_hash_size; i++)
+ INIT_LIST_HEAD(&ksocknal_data.ksnd_peers[i]);
+
+ rwlock_init(&ksocknal_data.ksnd_global_lock);
+ INIT_LIST_HEAD(&ksocknal_data.ksnd_nets);
+
+ spin_lock_init(&ksocknal_data.ksnd_reaper_lock);
+ INIT_LIST_HEAD(&ksocknal_data.ksnd_enomem_conns);
+ INIT_LIST_HEAD(&ksocknal_data.ksnd_zombie_conns);
+ INIT_LIST_HEAD(&ksocknal_data.ksnd_deathrow_conns);
+ init_waitqueue_head(&ksocknal_data.ksnd_reaper_waitq);
+
+ spin_lock_init(&ksocknal_data.ksnd_connd_lock);
+ INIT_LIST_HEAD(&ksocknal_data.ksnd_connd_connreqs);
+ INIT_LIST_HEAD(&ksocknal_data.ksnd_connd_routes);
+ init_waitqueue_head(&ksocknal_data.ksnd_connd_waitq);
+
+ spin_lock_init(&ksocknal_data.ksnd_tx_lock);
+ INIT_LIST_HEAD(&ksocknal_data.ksnd_idle_noop_txs);
+
+ /* NB memset above zeros whole of ksocknal_data */
+
+ /* flag lists/ptrs/locks initialised */
+ ksocknal_data.ksnd_init = SOCKNAL_INIT_DATA;
+ try_module_get(THIS_MODULE);
+
+ ksocknal_data.ksnd_sched_info = cfs_percpt_alloc(lnet_cpt_table(),
+ sizeof(*info));
+ if (ksocknal_data.ksnd_sched_info == NULL)
+ goto failed;
+
+ cfs_percpt_for_each(info, i, ksocknal_data.ksnd_sched_info) {
+ ksock_sched_t *sched;
+ int nthrs;
+
+ nthrs = cfs_cpt_weight(lnet_cpt_table(), i);
+ if (*ksocknal_tunables.ksnd_nscheds > 0) {
+ nthrs = min(nthrs, *ksocknal_tunables.ksnd_nscheds);
+ } else {
+ /* max to half of CPUs, assume another half should be
+ * reserved for upper layer modules */
+ nthrs = min(max(SOCKNAL_NSCHEDS, nthrs >> 1), nthrs);
+ }
+
+ info->ksi_nthreads_max = nthrs;
+ info->ksi_cpt = i;
+
+ LIBCFS_CPT_ALLOC(info->ksi_scheds, lnet_cpt_table(), i,
+ info->ksi_nthreads_max * sizeof(*sched));
+ if (info->ksi_scheds == NULL)
+ goto failed;
+
+ for (; nthrs > 0; nthrs--) {
+ sched = &info->ksi_scheds[nthrs - 1];
+
+ sched->kss_info = info;
+ spin_lock_init(&sched->kss_lock);
+ INIT_LIST_HEAD(&sched->kss_rx_conns);
+ INIT_LIST_HEAD(&sched->kss_tx_conns);
+ INIT_LIST_HEAD(&sched->kss_zombie_noop_txs);
+ init_waitqueue_head(&sched->kss_waitq);
+ }
+ }
+
+ ksocknal_data.ksnd_connd_starting = 0;
+ ksocknal_data.ksnd_connd_failed_stamp = 0;
+ ksocknal_data.ksnd_connd_starting_stamp = get_seconds();
+ /* must have at least 2 connds to remain responsive to accepts while
+ * connecting */
+ if (*ksocknal_tunables.ksnd_nconnds < SOCKNAL_CONND_RESV + 1)
+ *ksocknal_tunables.ksnd_nconnds = SOCKNAL_CONND_RESV + 1;
+
+ if (*ksocknal_tunables.ksnd_nconnds_max <
+ *ksocknal_tunables.ksnd_nconnds) {
+ ksocknal_tunables.ksnd_nconnds_max =
+ ksocknal_tunables.ksnd_nconnds;
+ }
+
+ for (i = 0; i < *ksocknal_tunables.ksnd_nconnds; i++) {
+ char name[16];
+ spin_lock_bh(&ksocknal_data.ksnd_connd_lock);
+ ksocknal_data.ksnd_connd_starting++;
+ spin_unlock_bh(&ksocknal_data.ksnd_connd_lock);
+
+
+ snprintf(name, sizeof(name), "socknal_cd%02d", i);
+ rc = ksocknal_thread_start(ksocknal_connd,
+ (void *)((ulong_ptr_t)i), name);
+ if (rc != 0) {
+ spin_lock_bh(&ksocknal_data.ksnd_connd_lock);
+ ksocknal_data.ksnd_connd_starting--;
+ spin_unlock_bh(&ksocknal_data.ksnd_connd_lock);
+ CERROR("Can't spawn socknal connd: %d\n", rc);
+ goto failed;
+ }
+ }
+
+ rc = ksocknal_thread_start(ksocknal_reaper, NULL, "socknal_reaper");
+ if (rc != 0) {
+ CERROR("Can't spawn socknal reaper: %d\n", rc);
+ goto failed;
+ }
+
+ /* flag everything initialised */
+ ksocknal_data.ksnd_init = SOCKNAL_INIT_ALL;
+
+ return 0;
+
+ failed:
+ ksocknal_base_shutdown();
+ return -ENETDOWN;
+}
+
+static void
+ksocknal_debug_peerhash(lnet_ni_t *ni)
+{
+ ksock_peer_t *peer = NULL;
+ struct list_head *tmp;
+ int i;
+
+ read_lock(&ksocknal_data.ksnd_global_lock);
+
+ for (i = 0; i < ksocknal_data.ksnd_peer_hash_size; i++) {
+ list_for_each(tmp, &ksocknal_data.ksnd_peers[i]) {
+ peer = list_entry(tmp, ksock_peer_t, ksnp_list);
+
+ if (peer->ksnp_ni == ni)
+ break;
+
+ peer = NULL;
+ }
+ }
+
+ if (peer != NULL) {
+ ksock_route_t *route;
+ ksock_conn_t *conn;
+
+ CWARN("Active peer on shutdown: %s, ref %d, scnt %d, closing %d, accepting %d, err %d, zcookie %llu, txq %d, zc_req %d\n",
+ libcfs_id2str(peer->ksnp_id),
+ atomic_read(&peer->ksnp_refcount),
+ peer->ksnp_sharecount, peer->ksnp_closing,
+ peer->ksnp_accepting, peer->ksnp_error,
+ peer->ksnp_zc_next_cookie,
+ !list_empty(&peer->ksnp_tx_queue),
+ !list_empty(&peer->ksnp_zc_req_list));
+
+ list_for_each(tmp, &peer->ksnp_routes) {
+ route = list_entry(tmp, ksock_route_t, ksnr_list);
+ CWARN("Route: ref %d, schd %d, conn %d, cnted %d, del %d\n",
+ atomic_read(&route->ksnr_refcount),
+ route->ksnr_scheduled, route->ksnr_connecting,
+ route->ksnr_connected, route->ksnr_deleted);
+ }
+
+ list_for_each(tmp, &peer->ksnp_conns) {
+ conn = list_entry(tmp, ksock_conn_t, ksnc_list);
+ CWARN("Conn: ref %d, sref %d, t %d, c %d\n",
+ atomic_read(&conn->ksnc_conn_refcount),
+ atomic_read(&conn->ksnc_sock_refcount),
+ conn->ksnc_type, conn->ksnc_closing);
+ }
+ }
+
+ read_unlock(&ksocknal_data.ksnd_global_lock);
+ return;
+}
+
+void
+ksocknal_shutdown(lnet_ni_t *ni)
+{
+ ksock_net_t *net = ni->ni_data;
+ int i;
+ lnet_process_id_t anyid = {0};
+
+ anyid.nid = LNET_NID_ANY;
+ anyid.pid = LNET_PID_ANY;
+
+ LASSERT(ksocknal_data.ksnd_init == SOCKNAL_INIT_ALL);
+ LASSERT(ksocknal_data.ksnd_nnets > 0);
+
+ spin_lock_bh(&net->ksnn_lock);
+ net->ksnn_shutdown = 1; /* prevent new peers */
+ spin_unlock_bh(&net->ksnn_lock);
+
+ /* Delete all peers */
+ ksocknal_del_peer(ni, anyid, 0);
+
+ /* Wait for all peer state to clean up */
+ i = 2;
+ spin_lock_bh(&net->ksnn_lock);
+ while (net->ksnn_npeers != 0) {
+ spin_unlock_bh(&net->ksnn_lock);
+
+ i++;
+ CDEBUG(((i & (-i)) == i) ? D_WARNING : D_NET, /* power of 2? */
+ "waiting for %d peers to disconnect\n",
+ net->ksnn_npeers);
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ schedule_timeout(cfs_time_seconds(1));
+
+ ksocknal_debug_peerhash(ni);
+
+ spin_lock_bh(&net->ksnn_lock);
+ }
+ spin_unlock_bh(&net->ksnn_lock);
+
+ for (i = 0; i < net->ksnn_ninterfaces; i++) {
+ LASSERT(net->ksnn_interfaces[i].ksni_npeers == 0);
+ LASSERT(net->ksnn_interfaces[i].ksni_nroutes == 0);
+ }
+
+ list_del(&net->ksnn_list);
+ LIBCFS_FREE(net, sizeof(*net));
+
+ ksocknal_data.ksnd_nnets--;
+ if (ksocknal_data.ksnd_nnets == 0)
+ ksocknal_base_shutdown();
+}
+
+static int
+ksocknal_enumerate_interfaces(ksock_net_t *net)
+{
+ char **names;
+ int i;
+ int j;
+ int rc;
+ int n;
+
+ n = libcfs_ipif_enumerate(&names);
+ if (n <= 0) {
+ CERROR("Can't enumerate interfaces: %d\n", n);
+ return n;
+ }
+
+ for (i = j = 0; i < n; i++) {
+ int up;
+ __u32 ip;
+ __u32 mask;
+
+ if (!strcmp(names[i], "lo")) /* skip the loopback IF */
+ continue;
+
+ rc = libcfs_ipif_query(names[i], &up, &ip, &mask);
+ if (rc != 0) {
+ CWARN("Can't get interface %s info: %d\n",
+ names[i], rc);
+ continue;
+ }
+
+ if (!up) {
+ CWARN("Ignoring interface %s (down)\n",
+ names[i]);
+ continue;
+ }
+
+ if (j == LNET_MAX_INTERFACES) {
+ CWARN("Ignoring interface %s (too many interfaces)\n",
+ names[i]);
+ continue;
+ }
+
+ net->ksnn_interfaces[j].ksni_ipaddr = ip;
+ net->ksnn_interfaces[j].ksni_netmask = mask;
+ strncpy(&net->ksnn_interfaces[j].ksni_name[0],
+ names[i], IFNAMSIZ);
+ j++;
+ }
+
+ libcfs_ipif_free_enumeration(names, n);
+
+ if (j == 0)
+ CERROR("Can't find any usable interfaces\n");
+
+ return j;
+}
+
+static int
+ksocknal_search_new_ipif(ksock_net_t *net)
+{
+ int new_ipif = 0;
+ int i;
+
+ for (i = 0; i < net->ksnn_ninterfaces; i++) {
+ char *ifnam = &net->ksnn_interfaces[i].ksni_name[0];
+ char *colon = strchr(ifnam, ':');
+ int found = 0;
+ ksock_net_t *tmp;
+ int j;
+
+ if (colon != NULL) /* ignore alias device */
+ *colon = 0;
+
+ list_for_each_entry(tmp, &ksocknal_data.ksnd_nets,
+ ksnn_list) {
+ for (j = 0; !found && j < tmp->ksnn_ninterfaces; j++) {
+ char *ifnam2 =
+ &tmp->ksnn_interfaces[j].ksni_name[0];
+ char *colon2 = strchr(ifnam2, ':');
+
+ if (colon2 != NULL)
+ *colon2 = 0;
+
+ found = strcmp(ifnam, ifnam2) == 0;
+ if (colon2 != NULL)
+ *colon2 = ':';
+ }
+ if (found)
+ break;
+ }
+
+ new_ipif += !found;
+ if (colon != NULL)
+ *colon = ':';
+ }
+
+ return new_ipif;
+}
+
+static int
+ksocknal_start_schedulers(struct ksock_sched_info *info)
+{
+ int nthrs;
+ int rc = 0;
+ int i;
+
+ if (info->ksi_nthreads == 0) {
+ if (*ksocknal_tunables.ksnd_nscheds > 0) {
+ nthrs = info->ksi_nthreads_max;
+ } else {
+ nthrs = cfs_cpt_weight(lnet_cpt_table(),
+ info->ksi_cpt);
+ nthrs = min(max(SOCKNAL_NSCHEDS, nthrs >> 1), nthrs);
+ nthrs = min(SOCKNAL_NSCHEDS_HIGH, nthrs);
+ }
+ nthrs = min(nthrs, info->ksi_nthreads_max);
+ } else {
+ LASSERT(info->ksi_nthreads <= info->ksi_nthreads_max);
+ /* increase two threads if there is new interface */
+ nthrs = min(2, info->ksi_nthreads_max - info->ksi_nthreads);
+ }
+
+ for (i = 0; i < nthrs; i++) {
+ long id;
+ char name[20];
+ ksock_sched_t *sched;
+ id = KSOCK_THREAD_ID(info->ksi_cpt, info->ksi_nthreads + i);
+ sched = &info->ksi_scheds[KSOCK_THREAD_SID(id)];
+ snprintf(name, sizeof(name), "socknal_sd%02d_%02d",
+ info->ksi_cpt, (int)(sched - &info->ksi_scheds[0]));
+
+ rc = ksocknal_thread_start(ksocknal_scheduler,
+ (void *)id, name);
+ if (rc == 0)
+ continue;
+
+ CERROR("Can't spawn thread %d for scheduler[%d]: %d\n",
+ info->ksi_cpt, info->ksi_nthreads + i, rc);
+ break;
+ }
+
+ info->ksi_nthreads += i;
+ return rc;
+}
+
+static int
+ksocknal_net_start_threads(ksock_net_t *net, __u32 *cpts, int ncpts)
+{
+ int newif = ksocknal_search_new_ipif(net);
+ int rc;
+ int i;
+
+ LASSERT(ncpts > 0 && ncpts <= cfs_cpt_number(lnet_cpt_table()));
+
+ for (i = 0; i < ncpts; i++) {
+ struct ksock_sched_info *info;
+ int cpt = (cpts == NULL) ? i : cpts[i];
+
+ LASSERT(cpt < cfs_cpt_number(lnet_cpt_table()));
+ info = ksocknal_data.ksnd_sched_info[cpt];
+
+ if (!newif && info->ksi_nthreads > 0)
+ continue;
+
+ rc = ksocknal_start_schedulers(info);
+ if (rc != 0)
+ return rc;
+ }
+ return 0;
+}
+
+int
+ksocknal_startup(lnet_ni_t *ni)
+{
+ ksock_net_t *net;
+ int rc;
+ int i;
+
+ LASSERT(ni->ni_lnd == &the_ksocklnd);
+
+ if (ksocknal_data.ksnd_init == SOCKNAL_INIT_NOTHING) {
+ rc = ksocknal_base_startup();
+ if (rc != 0)
+ return rc;
+ }
+
+ LIBCFS_ALLOC(net, sizeof(*net));
+ if (net == NULL)
+ goto fail_0;
+
+ spin_lock_init(&net->ksnn_lock);
+ net->ksnn_incarnation = ksocknal_new_incarnation();
+ ni->ni_data = net;
+ ni->ni_peertimeout = *ksocknal_tunables.ksnd_peertimeout;
+ ni->ni_maxtxcredits = *ksocknal_tunables.ksnd_credits;
+ ni->ni_peertxcredits = *ksocknal_tunables.ksnd_peertxcredits;
+ ni->ni_peerrtrcredits = *ksocknal_tunables.ksnd_peerrtrcredits;
+
+ if (ni->ni_interfaces[0] == NULL) {
+ rc = ksocknal_enumerate_interfaces(net);
+ if (rc <= 0)
+ goto fail_1;
+
+ net->ksnn_ninterfaces = 1;
+ } else {
+ for (i = 0; i < LNET_MAX_INTERFACES; i++) {
+ int up;
+
+ if (ni->ni_interfaces[i] == NULL)
+ break;
+
+ rc = libcfs_ipif_query(
+ ni->ni_interfaces[i], &up,
+ &net->ksnn_interfaces[i].ksni_ipaddr,
+ &net->ksnn_interfaces[i].ksni_netmask);
+
+ if (rc != 0) {
+ CERROR("Can't get interface %s info: %d\n",
+ ni->ni_interfaces[i], rc);
+ goto fail_1;
+ }
+
+ if (!up) {
+ CERROR("Interface %s is down\n",
+ ni->ni_interfaces[i]);
+ goto fail_1;
+ }
+
+ strncpy(&net->ksnn_interfaces[i].ksni_name[0],
+ ni->ni_interfaces[i], IFNAMSIZ);
+ }
+ net->ksnn_ninterfaces = i;
+ }
+
+ /* call it before add it to ksocknal_data.ksnd_nets */
+ rc = ksocknal_net_start_threads(net, ni->ni_cpts, ni->ni_ncpts);
+ if (rc != 0)
+ goto fail_1;
+
+ ni->ni_nid = LNET_MKNID(LNET_NIDNET(ni->ni_nid),
+ net->ksnn_interfaces[0].ksni_ipaddr);
+ list_add(&net->ksnn_list, &ksocknal_data.ksnd_nets);
+
+ ksocknal_data.ksnd_nnets++;
+
+ return 0;
+
+ fail_1:
+ LIBCFS_FREE(net, sizeof(*net));
+ fail_0:
+ if (ksocknal_data.ksnd_nnets == 0)
+ ksocknal_base_shutdown();
+
+ return -ENETDOWN;
+}
+
+
+static void __exit
+ksocknal_module_fini(void)
+{
+ lnet_unregister_lnd(&the_ksocklnd);
+}
+
+static int __init
+ksocknal_module_init(void)
+{
+ int rc;
+
+ /* check ksnr_connected/connecting field large enough */
+ CLASSERT(SOCKLND_CONN_NTYPES <= 4);
+ CLASSERT(SOCKLND_CONN_ACK == SOCKLND_CONN_BULK_IN);
+
+ /* initialize the_ksocklnd */
+ the_ksocklnd.lnd_type = SOCKLND;
+ the_ksocklnd.lnd_startup = ksocknal_startup;
+ the_ksocklnd.lnd_shutdown = ksocknal_shutdown;
+ the_ksocklnd.lnd_ctl = ksocknal_ctl;
+ the_ksocklnd.lnd_send = ksocknal_send;
+ the_ksocklnd.lnd_recv = ksocknal_recv;
+ the_ksocklnd.lnd_notify = ksocknal_notify;
+ the_ksocklnd.lnd_query = ksocknal_query;
+ the_ksocklnd.lnd_accept = ksocknal_accept;
+
+ rc = ksocknal_tunables_init();
+ if (rc != 0)
+ return rc;
+
+ lnet_register_lnd(&the_ksocklnd);
+
+ return 0;
+}
+
+MODULE_AUTHOR("Sun Microsystems, Inc. <http://www.lustre.org/>");
+MODULE_DESCRIPTION("Kernel TCP Socket LND v3.0.0");
+MODULE_LICENSE("GPL");
+MODULE_VERSION("3.0.0");
+
+module_init(ksocknal_module_init);
+module_exit(ksocknal_module_fini);
diff --git a/drivers/staging/lustre/lnet/klnds/socklnd/socklnd.h b/drivers/staging/lustre/lnet/klnds/socklnd/socklnd.h
new file mode 100644
index 000000000..c54c99551
--- /dev/null
+++ b/drivers/staging/lustre/lnet/klnds/socklnd/socklnd.h
@@ -0,0 +1,588 @@
+/*
+ * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
+ *
+ * Copyright (c) 2011, 2012, Intel Corporation.
+ *
+ * Author: Zach Brown <zab@zabbo.net>
+ * Author: Peter J. Braam <braam@clusterfs.com>
+ * Author: Phil Schwan <phil@clusterfs.com>
+ * Author: Eric Barton <eric@bartonsoftware.com>
+ *
+ * This file is part of Lustre, http://www.lustre.org
+ *
+ * Portals is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * Portals 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 Portals; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#define DEBUG_PORTAL_ALLOC
+#define DEBUG_SUBSYSTEM S_LND
+
+#include "socklnd_lib-linux.h"
+
+#include "../../../include/linux/libcfs/libcfs.h"
+#include "../../../include/linux/lnet/lnet.h"
+#include "../../../include/linux/lnet/lib-lnet.h"
+#include "../../../include/linux/lnet/socklnd.h"
+#include "../../../include/linux/lnet/lnet-sysctl.h"
+
+#define SOCKNAL_PEER_HASH_SIZE 101 /* # peer lists */
+#define SOCKNAL_RESCHED 100 /* # scheduler loops before reschedule */
+#define SOCKNAL_INSANITY_RECONN 5000 /* connd is trying on reconn infinitely */
+#define SOCKNAL_ENOMEM_RETRY CFS_TICK /* jiffies between retries */
+
+#define SOCKNAL_SINGLE_FRAG_TX 0 /* disable multi-fragment sends */
+#define SOCKNAL_SINGLE_FRAG_RX 0 /* disable multi-fragment receives */
+
+#define SOCKNAL_VERSION_DEBUG 0 /* enable protocol version debugging */
+
+/* risk kmap deadlock on multi-frag I/O (backs off to single-frag if disabled).
+ * no risk if we're not running on a CONFIG_HIGHMEM platform. */
+#ifdef CONFIG_HIGHMEM
+# define SOCKNAL_RISK_KMAP_DEADLOCK 0
+#else
+# define SOCKNAL_RISK_KMAP_DEADLOCK 1
+#endif
+
+struct ksock_sched_info;
+
+typedef struct /* per scheduler state */
+{
+ spinlock_t kss_lock; /* serialise */
+ struct list_head kss_rx_conns; /* conn waiting to be read */
+ /* conn waiting to be written */
+ struct list_head kss_tx_conns;
+ /* zombie noop tx list */
+ struct list_head kss_zombie_noop_txs;
+ wait_queue_head_t kss_waitq; /* where scheduler sleeps */
+ /* # connections assigned to this scheduler */
+ int kss_nconns;
+ struct ksock_sched_info *kss_info; /* owner of it */
+ struct page *kss_rx_scratch_pgs[LNET_MAX_IOV];
+ struct kvec kss_scratch_iov[LNET_MAX_IOV];
+} ksock_sched_t;
+
+struct ksock_sched_info {
+ int ksi_nthreads_max; /* max allowed threads */
+ int ksi_nthreads; /* number of threads */
+ int ksi_cpt; /* CPT id */
+ ksock_sched_t *ksi_scheds; /* array of schedulers */
+};
+
+#define KSOCK_CPT_SHIFT 16
+#define KSOCK_THREAD_ID(cpt, sid) (((cpt) << KSOCK_CPT_SHIFT) | (sid))
+#define KSOCK_THREAD_CPT(id) ((id) >> KSOCK_CPT_SHIFT)
+#define KSOCK_THREAD_SID(id) ((id) & ((1UL << KSOCK_CPT_SHIFT) - 1))
+
+typedef struct /* in-use interface */
+{
+ __u32 ksni_ipaddr; /* interface's IP address */
+ __u32 ksni_netmask; /* interface's network mask */
+ int ksni_nroutes; /* # routes using (active) */
+ int ksni_npeers; /* # peers using (passive) */
+ char ksni_name[IFNAMSIZ]; /* interface name */
+} ksock_interface_t;
+
+typedef struct {
+ /* "stuck" socket timeout (seconds) */
+ int *ksnd_timeout;
+ /* # scheduler threads in each pool while starting */
+ int *ksnd_nscheds;
+ int *ksnd_nconnds; /* # connection daemons */
+ int *ksnd_nconnds_max; /* max # connection daemons */
+ int *ksnd_min_reconnectms; /* first connection retry after (ms)... */
+ int *ksnd_max_reconnectms; /* ...exponentially increasing to this */
+ int *ksnd_eager_ack; /* make TCP ack eagerly? */
+ int *ksnd_typed_conns; /* drive sockets by type? */
+ int *ksnd_min_bulk; /* smallest "large" message */
+ int *ksnd_tx_buffer_size; /* socket tx buffer size */
+ int *ksnd_rx_buffer_size; /* socket rx buffer size */
+ int *ksnd_nagle; /* enable NAGLE? */
+ int *ksnd_round_robin; /* round robin for multiple interfaces */
+ int *ksnd_keepalive; /* # secs for sending keepalive NOOP */
+ int *ksnd_keepalive_idle; /* # idle secs before 1st probe */
+ int *ksnd_keepalive_count; /* # probes */
+ int *ksnd_keepalive_intvl; /* time between probes */
+ int *ksnd_credits; /* # concurrent sends */
+ int *ksnd_peertxcredits; /* # concurrent sends to 1 peer */
+ int *ksnd_peerrtrcredits; /* # per-peer router buffer credits */
+ int *ksnd_peertimeout; /* seconds to consider peer dead */
+ int *ksnd_enable_csum; /* enable check sum */
+ int *ksnd_inject_csum_error; /* set non-zero to inject checksum error */
+ int *ksnd_nonblk_zcack; /* always send zc-ack on non-blocking connection */
+ unsigned int *ksnd_zc_min_payload; /* minimum zero copy payload size */
+ int *ksnd_zc_recv; /* enable ZC receive (for Chelsio TOE) */
+ int *ksnd_zc_recv_min_nfrags; /* minimum # of fragments to enable ZC receive */
+} ksock_tunables_t;
+
+typedef struct {
+ __u64 ksnn_incarnation; /* my epoch */
+ spinlock_t ksnn_lock; /* serialise */
+ struct list_head ksnn_list; /* chain on global list */
+ int ksnn_npeers; /* # peers */
+ int ksnn_shutdown; /* shutting down? */
+ int ksnn_ninterfaces; /* IP interfaces */
+ ksock_interface_t ksnn_interfaces[LNET_MAX_INTERFACES];
+} ksock_net_t;
+
+/** connd timeout */
+#define SOCKNAL_CONND_TIMEOUT 120
+/** reserved thread for accepting & creating new connd */
+#define SOCKNAL_CONND_RESV 1
+
+typedef struct {
+ int ksnd_init; /* initialisation state */
+ int ksnd_nnets; /* # networks set up */
+ struct list_head ksnd_nets; /* list of nets */
+ /* stabilize peer/conn ops */
+ rwlock_t ksnd_global_lock;
+ /* hash table of all my known peers */
+ struct list_head *ksnd_peers;
+ int ksnd_peer_hash_size; /* size of ksnd_peers */
+
+ int ksnd_nthreads; /* # live threads */
+ int ksnd_shuttingdown; /* tell threads to exit */
+ /* schedulers information */
+ struct ksock_sched_info **ksnd_sched_info;
+
+ atomic_t ksnd_nactive_txs; /* #active txs */
+
+ struct list_head ksnd_deathrow_conns; /* conns to close: reaper_lock*/
+ struct list_head ksnd_zombie_conns; /* conns to free: reaper_lock */
+ struct list_head ksnd_enomem_conns; /* conns to retry: reaper_lock*/
+ wait_queue_head_t ksnd_reaper_waitq; /* reaper sleeps here */
+ unsigned long ksnd_reaper_waketime;/* when reaper will wake */
+ spinlock_t ksnd_reaper_lock; /* serialise */
+
+ int ksnd_enomem_tx; /* test ENOMEM sender */
+ int ksnd_stall_tx; /* test sluggish sender */
+ int ksnd_stall_rx; /* test sluggish receiver */
+
+ struct list_head ksnd_connd_connreqs; /* incoming connection requests */
+ struct list_head ksnd_connd_routes; /* routes waiting to be connected */
+ wait_queue_head_t ksnd_connd_waitq; /* connds sleep here */
+ int ksnd_connd_connecting;/* # connds connecting */
+ /** time stamp of the last failed connecting attempt */
+ long ksnd_connd_failed_stamp;
+ /** # starting connd */
+ unsigned ksnd_connd_starting;
+ /** time stamp of the last starting connd */
+ long ksnd_connd_starting_stamp;
+ /** # running connd */
+ unsigned ksnd_connd_running;
+ spinlock_t ksnd_connd_lock; /* serialise */
+
+ struct list_head ksnd_idle_noop_txs; /* list head for freed noop tx */
+ spinlock_t ksnd_tx_lock; /* serialise, g_lock unsafe */
+
+} ksock_nal_data_t;
+
+#define SOCKNAL_INIT_NOTHING 0
+#define SOCKNAL_INIT_DATA 1
+#define SOCKNAL_INIT_ALL 2
+
+/* A packet just assembled for transmission is represented by 1 or more
+ * struct iovec fragments (the first frag contains the portals header),
+ * followed by 0 or more lnet_kiov_t fragments.
+ *
+ * On the receive side, initially 1 struct iovec fragment is posted for
+ * receive (the header). Once the header has been received, the payload is
+ * received into either struct iovec or lnet_kiov_t fragments, depending on
+ * what the header matched or whether the message needs forwarding. */
+
+struct ksock_conn; /* forward ref */
+struct ksock_peer; /* forward ref */
+struct ksock_route; /* forward ref */
+struct ksock_proto; /* forward ref */
+
+typedef struct /* transmit packet */
+{
+ struct list_head tx_list; /* queue on conn for transmission etc */
+ struct list_head tx_zc_list; /* queue on peer for ZC request */
+ atomic_t tx_refcount; /* tx reference count */
+ int tx_nob; /* # packet bytes */
+ int tx_resid; /* residual bytes */
+ int tx_niov; /* # packet iovec frags */
+ struct kvec *tx_iov; /* packet iovec frags */
+ int tx_nkiov; /* # packet page frags */
+ unsigned short tx_zc_aborted; /* aborted ZC request */
+ unsigned short tx_zc_capable:1; /* payload is large enough for ZC */
+ unsigned short tx_zc_checked:1; /* Have I checked if I should ZC? */
+ unsigned short tx_nonblk:1; /* it's a non-blocking ACK */
+ lnet_kiov_t *tx_kiov; /* packet page frags */
+ struct ksock_conn *tx_conn; /* owning conn */
+ lnet_msg_t *tx_lnetmsg; /* lnet message for lnet_finalize() */
+ unsigned long tx_deadline; /* when (in jiffies) tx times out */
+ ksock_msg_t tx_msg; /* socklnd message buffer */
+ int tx_desc_size; /* size of this descriptor */
+ union {
+ struct {
+ struct kvec iov; /* virt hdr */
+ lnet_kiov_t kiov[0]; /* paged payload */
+ } paged;
+ struct {
+ struct kvec iov[1]; /* virt hdr + payload */
+ } virt;
+ } tx_frags;
+} ksock_tx_t;
+
+#define KSOCK_NOOP_TX_SIZE ((int)offsetof(ksock_tx_t, tx_frags.paged.kiov[0]))
+
+/* network zero copy callback descriptor embedded in ksock_tx_t */
+
+/* space for the rx frag descriptors; we either read a single contiguous
+ * header, or up to LNET_MAX_IOV frags of payload of either type. */
+typedef union {
+ struct kvec iov[LNET_MAX_IOV];
+ lnet_kiov_t kiov[LNET_MAX_IOV];
+} ksock_rxiovspace_t;
+
+#define SOCKNAL_RX_KSM_HEADER 1 /* reading ksock message header */
+#define SOCKNAL_RX_LNET_HEADER 2 /* reading lnet message header */
+#define SOCKNAL_RX_PARSE 3 /* Calling lnet_parse() */
+#define SOCKNAL_RX_PARSE_WAIT 4 /* waiting to be told to read the body */
+#define SOCKNAL_RX_LNET_PAYLOAD 5 /* reading lnet payload (to deliver here) */
+#define SOCKNAL_RX_SLOP 6 /* skipping body */
+
+typedef struct ksock_conn {
+ struct ksock_peer *ksnc_peer; /* owning peer */
+ struct ksock_route *ksnc_route; /* owning route */
+ struct list_head ksnc_list; /* stash on peer's conn list */
+ struct socket *ksnc_sock; /* actual socket */
+ void *ksnc_saved_data_ready; /* socket's original data_ready() callback */
+ void *ksnc_saved_write_space; /* socket's original write_space() callback */
+ atomic_t ksnc_conn_refcount; /* conn refcount */
+ atomic_t ksnc_sock_refcount; /* sock refcount */
+ ksock_sched_t *ksnc_scheduler; /* who schedules this connection */
+ __u32 ksnc_myipaddr; /* my IP */
+ __u32 ksnc_ipaddr; /* peer's IP */
+ int ksnc_port; /* peer's port */
+ signed int ksnc_type:3; /* type of connection,
+ * should be signed value */
+ unsigned int ksnc_closing:1; /* being shut down */
+ unsigned int ksnc_flip:1; /* flip or not, only for V2.x */
+ unsigned int ksnc_zc_capable:1; /* enable to ZC */
+ struct ksock_proto *ksnc_proto; /* protocol for the connection */
+
+ /* reader */
+ struct list_head ksnc_rx_list; /* where I enq waiting input or a forwarding descriptor */
+ unsigned long ksnc_rx_deadline; /* when (in jiffies) receive times out */
+ __u8 ksnc_rx_started; /* started receiving a message */
+ __u8 ksnc_rx_ready; /* data ready to read */
+ __u8 ksnc_rx_scheduled;/* being progressed */
+ __u8 ksnc_rx_state; /* what is being read */
+ int ksnc_rx_nob_left; /* # bytes to next hdr/body */
+ int ksnc_rx_nob_wanted; /* bytes actually wanted */
+ int ksnc_rx_niov; /* # iovec frags */
+ struct kvec *ksnc_rx_iov; /* the iovec frags */
+ int ksnc_rx_nkiov; /* # page frags */
+ lnet_kiov_t *ksnc_rx_kiov; /* the page frags */
+ ksock_rxiovspace_t ksnc_rx_iov_space;/* space for frag descriptors */
+ __u32 ksnc_rx_csum; /* partial checksum for incoming data */
+ void *ksnc_cookie; /* rx lnet_finalize passthru arg */
+ ksock_msg_t ksnc_msg; /* incoming message buffer:
+ * V2.x message takes the
+ * whole struct
+ * V1.x message is a bare
+ * lnet_hdr_t, it's stored in
+ * ksnc_msg.ksm_u.lnetmsg */
+
+ /* WRITER */
+ struct list_head ksnc_tx_list; /* where I enq waiting for output space */
+ struct list_head ksnc_tx_queue; /* packets waiting to be sent */
+ ksock_tx_t *ksnc_tx_carrier; /* next TX that can carry a LNet message or ZC-ACK */
+ unsigned long ksnc_tx_deadline; /* when (in jiffies) tx times out */
+ int ksnc_tx_bufnob; /* send buffer marker */
+ atomic_t ksnc_tx_nob; /* # bytes queued */
+ int ksnc_tx_ready; /* write space */
+ int ksnc_tx_scheduled; /* being progressed */
+ unsigned long ksnc_tx_last_post; /* time stamp of the last posted TX */
+} ksock_conn_t;
+
+typedef struct ksock_route {
+ struct list_head ksnr_list; /* chain on peer route list */
+ struct list_head ksnr_connd_list; /* chain on ksnr_connd_routes */
+ struct ksock_peer *ksnr_peer; /* owning peer */
+ atomic_t ksnr_refcount; /* # users */
+ unsigned long ksnr_timeout; /* when (in jiffies) reconnection can happen next */
+ long ksnr_retry_interval; /* how long between retries */
+ __u32 ksnr_myipaddr; /* my IP */
+ __u32 ksnr_ipaddr; /* IP address to connect to */
+ int ksnr_port; /* port to connect to */
+ unsigned int ksnr_scheduled:1; /* scheduled for attention */
+ unsigned int ksnr_connecting:1;/* connection establishment in progress */
+ unsigned int ksnr_connected:4; /* connections established by type */
+ unsigned int ksnr_deleted:1; /* been removed from peer? */
+ unsigned int ksnr_share_count; /* created explicitly? */
+ int ksnr_conn_count; /* # conns established by this route */
+} ksock_route_t;
+
+#define SOCKNAL_KEEPALIVE_PING 1 /* cookie for keepalive ping */
+
+typedef struct ksock_peer {
+ struct list_head ksnp_list; /* stash on global peer list */
+ unsigned long ksnp_last_alive; /* when (in jiffies) I was last alive */
+ lnet_process_id_t ksnp_id; /* who's on the other end(s) */
+ atomic_t ksnp_refcount; /* # users */
+ int ksnp_sharecount; /* lconf usage counter */
+ int ksnp_closing; /* being closed */
+ int ksnp_accepting;/* # passive connections pending */
+ int ksnp_error; /* errno on closing last conn */
+ __u64 ksnp_zc_next_cookie;/* ZC completion cookie */
+ __u64 ksnp_incarnation; /* latest known peer incarnation */
+ struct ksock_proto *ksnp_proto; /* latest known peer protocol */
+ struct list_head ksnp_conns; /* all active connections */
+ struct list_head ksnp_routes; /* routes */
+ struct list_head ksnp_tx_queue; /* waiting packets */
+ spinlock_t ksnp_lock; /* serialize, g_lock unsafe */
+ struct list_head ksnp_zc_req_list; /* zero copy requests wait for ACK */
+ unsigned long ksnp_send_keepalive; /* time to send keepalive */
+ lnet_ni_t *ksnp_ni; /* which network */
+ int ksnp_n_passive_ips; /* # of... */
+ __u32 ksnp_passive_ips[LNET_MAX_INTERFACES]; /* preferred local interfaces */
+} ksock_peer_t;
+
+typedef struct ksock_connreq {
+ struct list_head ksncr_list; /* stash on ksnd_connd_connreqs */
+ lnet_ni_t *ksncr_ni; /* chosen NI */
+ struct socket *ksncr_sock; /* accepted socket */
+} ksock_connreq_t;
+
+extern ksock_nal_data_t ksocknal_data;
+extern ksock_tunables_t ksocknal_tunables;
+
+#define SOCKNAL_MATCH_NO 0 /* TX can't match type of connection */
+#define SOCKNAL_MATCH_YES 1 /* TX matches type of connection */
+#define SOCKNAL_MATCH_MAY 2 /* TX can be sent on the connection, but not preferred */
+
+typedef struct ksock_proto {
+ int pro_version; /* version number of protocol */
+ int (*pro_send_hello)(ksock_conn_t *, ksock_hello_msg_t *); /* handshake function */
+ int (*pro_recv_hello)(ksock_conn_t *, ksock_hello_msg_t *, int);/* handshake function */
+ void (*pro_pack)(ksock_tx_t *); /* message pack */
+ void (*pro_unpack)(ksock_msg_t *); /* message unpack */
+ ksock_tx_t *(*pro_queue_tx_msg)(ksock_conn_t *, ksock_tx_t *); /* queue tx on the connection */
+ int (*pro_queue_tx_zcack)(ksock_conn_t *, ksock_tx_t *, __u64); /* queue ZC ack on the connection */
+ int (*pro_handle_zcreq)(ksock_conn_t *, __u64, int); /* handle ZC request */
+ int (*pro_handle_zcack)(ksock_conn_t *, __u64, __u64); /* handle ZC ACK */
+ int (*pro_match_tx)(ksock_conn_t *, ksock_tx_t *, int); /* msg type matches the connection type:
+ * return value:
+ * return MATCH_NO : no
+ * return MATCH_YES : matching type
+ * return MATCH_MAY : can be backup */
+} ksock_proto_t;
+
+extern ksock_proto_t ksocknal_protocol_v1x;
+extern ksock_proto_t ksocknal_protocol_v2x;
+extern ksock_proto_t ksocknal_protocol_v3x;
+
+#define KSOCK_PROTO_V1_MAJOR LNET_PROTO_TCP_VERSION_MAJOR
+#define KSOCK_PROTO_V1_MINOR LNET_PROTO_TCP_VERSION_MINOR
+#define KSOCK_PROTO_V1 KSOCK_PROTO_V1_MAJOR
+
+#ifndef CPU_MASK_NONE
+#define CPU_MASK_NONE 0UL
+#endif
+
+static inline int
+ksocknal_route_mask(void)
+{
+ if (!*ksocknal_tunables.ksnd_typed_conns)
+ return (1 << SOCKLND_CONN_ANY);
+
+ return ((1 << SOCKLND_CONN_CONTROL) |
+ (1 << SOCKLND_CONN_BULK_IN) |
+ (1 << SOCKLND_CONN_BULK_OUT));
+}
+
+static inline struct list_head *
+ksocknal_nid2peerlist(lnet_nid_t nid)
+{
+ unsigned int hash = ((unsigned int)nid) % ksocknal_data.ksnd_peer_hash_size;
+
+ return &ksocknal_data.ksnd_peers[hash];
+}
+
+static inline void
+ksocknal_conn_addref(ksock_conn_t *conn)
+{
+ LASSERT(atomic_read(&conn->ksnc_conn_refcount) > 0);
+ atomic_inc(&conn->ksnc_conn_refcount);
+}
+
+extern void ksocknal_queue_zombie_conn(ksock_conn_t *conn);
+extern void ksocknal_finalize_zcreq(ksock_conn_t *conn);
+
+static inline void
+ksocknal_conn_decref(ksock_conn_t *conn)
+{
+ LASSERT(atomic_read(&conn->ksnc_conn_refcount) > 0);
+ if (atomic_dec_and_test(&conn->ksnc_conn_refcount))
+ ksocknal_queue_zombie_conn(conn);
+}
+
+static inline int
+ksocknal_connsock_addref(ksock_conn_t *conn)
+{
+ int rc = -ESHUTDOWN;
+
+ read_lock(&ksocknal_data.ksnd_global_lock);
+ if (!conn->ksnc_closing) {
+ LASSERT(atomic_read(&conn->ksnc_sock_refcount) > 0);
+ atomic_inc(&conn->ksnc_sock_refcount);
+ rc = 0;
+ }
+ read_unlock(&ksocknal_data.ksnd_global_lock);
+
+ return rc;
+}
+
+static inline void
+ksocknal_connsock_decref(ksock_conn_t *conn)
+{
+ LASSERT(atomic_read(&conn->ksnc_sock_refcount) > 0);
+ if (atomic_dec_and_test(&conn->ksnc_sock_refcount)) {
+ LASSERT(conn->ksnc_closing);
+ libcfs_sock_release(conn->ksnc_sock);
+ conn->ksnc_sock = NULL;
+ ksocknal_finalize_zcreq(conn);
+ }
+}
+
+static inline void
+ksocknal_tx_addref(ksock_tx_t *tx)
+{
+ LASSERT(atomic_read(&tx->tx_refcount) > 0);
+ atomic_inc(&tx->tx_refcount);
+}
+
+extern void ksocknal_tx_prep(ksock_conn_t *, ksock_tx_t *tx);
+extern void ksocknal_tx_done(lnet_ni_t *ni, ksock_tx_t *tx);
+
+static inline void
+ksocknal_tx_decref(ksock_tx_t *tx)
+{
+ LASSERT(atomic_read(&tx->tx_refcount) > 0);
+ if (atomic_dec_and_test(&tx->tx_refcount))
+ ksocknal_tx_done(NULL, tx);
+}
+
+static inline void
+ksocknal_route_addref(ksock_route_t *route)
+{
+ LASSERT(atomic_read(&route->ksnr_refcount) > 0);
+ atomic_inc(&route->ksnr_refcount);
+}
+
+extern void ksocknal_destroy_route(ksock_route_t *route);
+
+static inline void
+ksocknal_route_decref(ksock_route_t *route)
+{
+ LASSERT(atomic_read(&route->ksnr_refcount) > 0);
+ if (atomic_dec_and_test(&route->ksnr_refcount))
+ ksocknal_destroy_route(route);
+}
+
+static inline void
+ksocknal_peer_addref(ksock_peer_t *peer)
+{
+ LASSERT(atomic_read(&peer->ksnp_refcount) > 0);
+ atomic_inc(&peer->ksnp_refcount);
+}
+
+extern void ksocknal_destroy_peer(ksock_peer_t *peer);
+
+static inline void
+ksocknal_peer_decref(ksock_peer_t *peer)
+{
+ LASSERT(atomic_read(&peer->ksnp_refcount) > 0);
+ if (atomic_dec_and_test(&peer->ksnp_refcount))
+ ksocknal_destroy_peer(peer);
+}
+
+int ksocknal_startup(lnet_ni_t *ni);
+void ksocknal_shutdown(lnet_ni_t *ni);
+int ksocknal_ctl(lnet_ni_t *ni, unsigned int cmd, void *arg);
+int ksocknal_send(lnet_ni_t *ni, void *private, lnet_msg_t *lntmsg);
+int ksocknal_recv(lnet_ni_t *ni, void *private, lnet_msg_t *lntmsg,
+ int delayed, unsigned int niov,
+ struct kvec *iov, lnet_kiov_t *kiov,
+ unsigned int offset, unsigned int mlen, unsigned int rlen);
+int ksocknal_accept(lnet_ni_t *ni, struct socket *sock);
+
+extern int ksocknal_add_peer(lnet_ni_t *ni, lnet_process_id_t id, __u32 ip, int port);
+extern ksock_peer_t *ksocknal_find_peer_locked(lnet_ni_t *ni, lnet_process_id_t id);
+extern ksock_peer_t *ksocknal_find_peer(lnet_ni_t *ni, lnet_process_id_t id);
+extern void ksocknal_peer_failed(ksock_peer_t *peer);
+extern int ksocknal_create_conn(lnet_ni_t *ni, ksock_route_t *route,
+ struct socket *sock, int type);
+extern void ksocknal_close_conn_locked(ksock_conn_t *conn, int why);
+extern void ksocknal_terminate_conn(ksock_conn_t *conn);
+extern void ksocknal_destroy_conn(ksock_conn_t *conn);
+extern int ksocknal_close_peer_conns_locked(ksock_peer_t *peer,
+ __u32 ipaddr, int why);
+extern int ksocknal_close_conn_and_siblings(ksock_conn_t *conn, int why);
+extern int ksocknal_close_matching_conns(lnet_process_id_t id, __u32 ipaddr);
+extern ksock_conn_t *ksocknal_find_conn_locked(ksock_peer_t *peer,
+ ksock_tx_t *tx, int nonblk);
+
+extern int ksocknal_launch_packet(lnet_ni_t *ni, ksock_tx_t *tx,
+ lnet_process_id_t id);
+extern ksock_tx_t *ksocknal_alloc_tx(int type, int size);
+extern void ksocknal_free_tx(ksock_tx_t *tx);
+extern ksock_tx_t *ksocknal_alloc_tx_noop(__u64 cookie, int nonblk);
+extern void ksocknal_next_tx_carrier(ksock_conn_t *conn);
+extern void ksocknal_queue_tx_locked(ksock_tx_t *tx, ksock_conn_t *conn);
+extern void ksocknal_txlist_done(lnet_ni_t *ni, struct list_head *txlist,
+ int error);
+extern void ksocknal_notify(lnet_ni_t *ni, lnet_nid_t gw_nid, int alive);
+extern void ksocknal_query(struct lnet_ni *ni, lnet_nid_t nid, unsigned long *when);
+extern int ksocknal_thread_start(int (*fn)(void *arg), void *arg, char *name);
+extern void ksocknal_thread_fini(void);
+extern void ksocknal_launch_all_connections_locked(ksock_peer_t *peer);
+extern ksock_route_t *ksocknal_find_connectable_route_locked(ksock_peer_t *peer);
+extern ksock_route_t *ksocknal_find_connecting_route_locked(ksock_peer_t *peer);
+extern int ksocknal_new_packet(ksock_conn_t *conn, int skip);
+extern int ksocknal_scheduler(void *arg);
+extern int ksocknal_connd(void *arg);
+extern int ksocknal_reaper(void *arg);
+extern int ksocknal_send_hello(lnet_ni_t *ni, ksock_conn_t *conn,
+ lnet_nid_t peer_nid, ksock_hello_msg_t *hello);
+extern int ksocknal_recv_hello(lnet_ni_t *ni, ksock_conn_t *conn,
+ ksock_hello_msg_t *hello, lnet_process_id_t *id,
+ __u64 *incarnation);
+extern void ksocknal_read_callback(ksock_conn_t *conn);
+extern void ksocknal_write_callback(ksock_conn_t *conn);
+
+extern int ksocknal_lib_zc_capable(ksock_conn_t *conn);
+extern void ksocknal_lib_save_callback(struct socket *sock, ksock_conn_t *conn);
+extern void ksocknal_lib_set_callback(struct socket *sock, ksock_conn_t *conn);
+extern void ksocknal_lib_reset_callback(struct socket *sock, ksock_conn_t *conn);
+extern void ksocknal_lib_push_conn(ksock_conn_t *conn);
+extern int ksocknal_lib_get_conn_addrs(ksock_conn_t *conn);
+extern int ksocknal_lib_setup_sock(struct socket *so);
+extern int ksocknal_lib_send_iov(ksock_conn_t *conn, ksock_tx_t *tx);
+extern int ksocknal_lib_send_kiov(ksock_conn_t *conn, ksock_tx_t *tx);
+extern void ksocknal_lib_eager_ack(ksock_conn_t *conn);
+extern int ksocknal_lib_recv_iov(ksock_conn_t *conn);
+extern int ksocknal_lib_recv_kiov(ksock_conn_t *conn);
+extern int ksocknal_lib_get_conn_tunables(ksock_conn_t *conn, int *txmem,
+ int *rxmem, int *nagle);
+
+extern int ksocknal_tunables_init(void);
+
+extern void ksocknal_lib_csum_tx(ksock_tx_t *tx);
+
+extern int ksocknal_lib_memory_pressure(ksock_conn_t *conn);
+extern int ksocknal_lib_bind_thread_to_cpu(int id);
diff --git a/drivers/staging/lustre/lnet/klnds/socklnd/socklnd_cb.c b/drivers/staging/lustre/lnet/klnds/socklnd/socklnd_cb.c
new file mode 100644
index 000000000..fa7ad883b
--- /dev/null
+++ b/drivers/staging/lustre/lnet/klnds/socklnd/socklnd_cb.c
@@ -0,0 +1,2634 @@
+/*
+ * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
+ *
+ * Copyright (c) 2011, 2012, Intel Corporation.
+ *
+ * Author: Zach Brown <zab@zabbo.net>
+ * Author: Peter J. Braam <braam@clusterfs.com>
+ * Author: Phil Schwan <phil@clusterfs.com>
+ * Author: Eric Barton <eric@bartonsoftware.com>
+ *
+ * This file is part of Portals, http://www.sf.net/projects/sandiaportals/
+ *
+ * Portals is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * Portals 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 Portals; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "socklnd.h"
+
+ksock_tx_t *
+ksocknal_alloc_tx(int type, int size)
+{
+ ksock_tx_t *tx = NULL;
+
+ if (type == KSOCK_MSG_NOOP) {
+ LASSERT(size == KSOCK_NOOP_TX_SIZE);
+
+ /* searching for a noop tx in free list */
+ spin_lock(&ksocknal_data.ksnd_tx_lock);
+
+ if (!list_empty(&ksocknal_data.ksnd_idle_noop_txs)) {
+ tx = list_entry(ksocknal_data.ksnd_idle_noop_txs. \
+ next, ksock_tx_t, tx_list);
+ LASSERT(tx->tx_desc_size == size);
+ list_del(&tx->tx_list);
+ }
+
+ spin_unlock(&ksocknal_data.ksnd_tx_lock);
+ }
+
+ if (tx == NULL)
+ LIBCFS_ALLOC(tx, size);
+
+ if (tx == NULL)
+ return NULL;
+
+ atomic_set(&tx->tx_refcount, 1);
+ tx->tx_zc_aborted = 0;
+ tx->tx_zc_capable = 0;
+ tx->tx_zc_checked = 0;
+ tx->tx_desc_size = size;
+
+ atomic_inc(&ksocknal_data.ksnd_nactive_txs);
+
+ return tx;
+}
+
+ksock_tx_t *
+ksocknal_alloc_tx_noop(__u64 cookie, int nonblk)
+{
+ ksock_tx_t *tx;
+
+ tx = ksocknal_alloc_tx(KSOCK_MSG_NOOP, KSOCK_NOOP_TX_SIZE);
+ if (tx == NULL) {
+ CERROR("Can't allocate noop tx desc\n");
+ return NULL;
+ }
+
+ tx->tx_conn = NULL;
+ tx->tx_lnetmsg = NULL;
+ tx->tx_kiov = NULL;
+ tx->tx_nkiov = 0;
+ tx->tx_iov = tx->tx_frags.virt.iov;
+ tx->tx_niov = 1;
+ tx->tx_nonblk = nonblk;
+
+ socklnd_init_msg(&tx->tx_msg, KSOCK_MSG_NOOP);
+ tx->tx_msg.ksm_zc_cookies[1] = cookie;
+
+ return tx;
+}
+
+
+void
+ksocknal_free_tx (ksock_tx_t *tx)
+{
+ atomic_dec(&ksocknal_data.ksnd_nactive_txs);
+
+ if (tx->tx_lnetmsg == NULL && tx->tx_desc_size == KSOCK_NOOP_TX_SIZE) {
+ /* it's a noop tx */
+ spin_lock(&ksocknal_data.ksnd_tx_lock);
+
+ list_add(&tx->tx_list, &ksocknal_data.ksnd_idle_noop_txs);
+
+ spin_unlock(&ksocknal_data.ksnd_tx_lock);
+ } else {
+ LIBCFS_FREE(tx, tx->tx_desc_size);
+ }
+}
+
+static int
+ksocknal_send_iov (ksock_conn_t *conn, ksock_tx_t *tx)
+{
+ struct kvec *iov = tx->tx_iov;
+ int nob;
+ int rc;
+
+ LASSERT (tx->tx_niov > 0);
+
+ /* Never touch tx->tx_iov inside ksocknal_lib_send_iov() */
+ rc = ksocknal_lib_send_iov(conn, tx);
+
+ if (rc <= 0) /* sent nothing? */
+ return rc;
+
+ nob = rc;
+ LASSERT (nob <= tx->tx_resid);
+ tx->tx_resid -= nob;
+
+ /* "consume" iov */
+ do {
+ LASSERT (tx->tx_niov > 0);
+
+ if (nob < (int) iov->iov_len) {
+ iov->iov_base = (void *)((char *)iov->iov_base + nob);
+ iov->iov_len -= nob;
+ return rc;
+ }
+
+ nob -= iov->iov_len;
+ tx->tx_iov = ++iov;
+ tx->tx_niov--;
+ } while (nob != 0);
+
+ return rc;
+}
+
+static int
+ksocknal_send_kiov (ksock_conn_t *conn, ksock_tx_t *tx)
+{
+ lnet_kiov_t *kiov = tx->tx_kiov;
+ int nob;
+ int rc;
+
+ LASSERT (tx->tx_niov == 0);
+ LASSERT (tx->tx_nkiov > 0);
+
+ /* Never touch tx->tx_kiov inside ksocknal_lib_send_kiov() */
+ rc = ksocknal_lib_send_kiov(conn, tx);
+
+ if (rc <= 0) /* sent nothing? */
+ return rc;
+
+ nob = rc;
+ LASSERT (nob <= tx->tx_resid);
+ tx->tx_resid -= nob;
+
+ /* "consume" kiov */
+ do {
+ LASSERT(tx->tx_nkiov > 0);
+
+ if (nob < (int)kiov->kiov_len) {
+ kiov->kiov_offset += nob;
+ kiov->kiov_len -= nob;
+ return rc;
+ }
+
+ nob -= (int)kiov->kiov_len;
+ tx->tx_kiov = ++kiov;
+ tx->tx_nkiov--;
+ } while (nob != 0);
+
+ return rc;
+}
+
+static int
+ksocknal_transmit (ksock_conn_t *conn, ksock_tx_t *tx)
+{
+ int rc;
+ int bufnob;
+
+ if (ksocknal_data.ksnd_stall_tx != 0) {
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ schedule_timeout(cfs_time_seconds(ksocknal_data.ksnd_stall_tx));
+ }
+
+ LASSERT (tx->tx_resid != 0);
+
+ rc = ksocknal_connsock_addref(conn);
+ if (rc != 0) {
+ LASSERT (conn->ksnc_closing);
+ return -ESHUTDOWN;
+ }
+
+ do {
+ if (ksocknal_data.ksnd_enomem_tx > 0) {
+ /* testing... */
+ ksocknal_data.ksnd_enomem_tx--;
+ rc = -EAGAIN;
+ } else if (tx->tx_niov != 0) {
+ rc = ksocknal_send_iov (conn, tx);
+ } else {
+ rc = ksocknal_send_kiov (conn, tx);
+ }
+
+ bufnob = conn->ksnc_sock->sk->sk_wmem_queued;
+ if (rc > 0) /* sent something? */
+ conn->ksnc_tx_bufnob += rc; /* account it */
+
+ if (bufnob < conn->ksnc_tx_bufnob) {
+ /* allocated send buffer bytes < computed; infer
+ * something got ACKed */
+ conn->ksnc_tx_deadline =
+ cfs_time_shift(*ksocknal_tunables.ksnd_timeout);
+ conn->ksnc_peer->ksnp_last_alive = cfs_time_current();
+ conn->ksnc_tx_bufnob = bufnob;
+ mb();
+ }
+
+ if (rc <= 0) { /* Didn't write anything? */
+
+ if (rc == 0) /* some stacks return 0 instead of -EAGAIN */
+ rc = -EAGAIN;
+
+ /* Check if EAGAIN is due to memory pressure */
+ if (rc == -EAGAIN && ksocknal_lib_memory_pressure(conn))
+ rc = -ENOMEM;
+
+ break;
+ }
+
+ /* socket's wmem_queued now includes 'rc' bytes */
+ atomic_sub (rc, &conn->ksnc_tx_nob);
+ rc = 0;
+
+ } while (tx->tx_resid != 0);
+
+ ksocknal_connsock_decref(conn);
+ return rc;
+}
+
+static int
+ksocknal_recv_iov (ksock_conn_t *conn)
+{
+ struct kvec *iov = conn->ksnc_rx_iov;
+ int nob;
+ int rc;
+
+ LASSERT (conn->ksnc_rx_niov > 0);
+
+ /* Never touch conn->ksnc_rx_iov or change connection
+ * status inside ksocknal_lib_recv_iov */
+ rc = ksocknal_lib_recv_iov(conn);
+
+ if (rc <= 0)
+ return rc;
+
+ /* received something... */
+ nob = rc;
+
+ conn->ksnc_peer->ksnp_last_alive = cfs_time_current();
+ conn->ksnc_rx_deadline =
+ cfs_time_shift(*ksocknal_tunables.ksnd_timeout);
+ mb(); /* order with setting rx_started */
+ conn->ksnc_rx_started = 1;
+
+ conn->ksnc_rx_nob_wanted -= nob;
+ conn->ksnc_rx_nob_left -= nob;
+
+ do {
+ LASSERT (conn->ksnc_rx_niov > 0);
+
+ if (nob < (int)iov->iov_len) {
+ iov->iov_len -= nob;
+ iov->iov_base += nob;
+ return -EAGAIN;
+ }
+
+ nob -= iov->iov_len;
+ conn->ksnc_rx_iov = ++iov;
+ conn->ksnc_rx_niov--;
+ } while (nob != 0);
+
+ return rc;
+}
+
+static int
+ksocknal_recv_kiov (ksock_conn_t *conn)
+{
+ lnet_kiov_t *kiov = conn->ksnc_rx_kiov;
+ int nob;
+ int rc;
+ LASSERT (conn->ksnc_rx_nkiov > 0);
+
+ /* Never touch conn->ksnc_rx_kiov or change connection
+ * status inside ksocknal_lib_recv_iov */
+ rc = ksocknal_lib_recv_kiov(conn);
+
+ if (rc <= 0)
+ return rc;
+
+ /* received something... */
+ nob = rc;
+
+ conn->ksnc_peer->ksnp_last_alive = cfs_time_current();
+ conn->ksnc_rx_deadline =
+ cfs_time_shift(*ksocknal_tunables.ksnd_timeout);
+ mb(); /* order with setting rx_started */
+ conn->ksnc_rx_started = 1;
+
+ conn->ksnc_rx_nob_wanted -= nob;
+ conn->ksnc_rx_nob_left -= nob;
+
+ do {
+ LASSERT (conn->ksnc_rx_nkiov > 0);
+
+ if (nob < (int) kiov->kiov_len) {
+ kiov->kiov_offset += nob;
+ kiov->kiov_len -= nob;
+ return -EAGAIN;
+ }
+
+ nob -= kiov->kiov_len;
+ conn->ksnc_rx_kiov = ++kiov;
+ conn->ksnc_rx_nkiov--;
+ } while (nob != 0);
+
+ return 1;
+}
+
+static int
+ksocknal_receive (ksock_conn_t *conn)
+{
+ /* Return 1 on success, 0 on EOF, < 0 on error.
+ * Caller checks ksnc_rx_nob_wanted to determine
+ * progress/completion. */
+ int rc;
+
+ if (ksocknal_data.ksnd_stall_rx != 0) {
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ schedule_timeout(cfs_time_seconds(ksocknal_data.ksnd_stall_rx));
+ }
+
+ rc = ksocknal_connsock_addref(conn);
+ if (rc != 0) {
+ LASSERT (conn->ksnc_closing);
+ return -ESHUTDOWN;
+ }
+
+ for (;;) {
+ if (conn->ksnc_rx_niov != 0)
+ rc = ksocknal_recv_iov (conn);
+ else
+ rc = ksocknal_recv_kiov (conn);
+
+ if (rc <= 0) {
+ /* error/EOF or partial receive */
+ if (rc == -EAGAIN) {
+ rc = 1;
+ } else if (rc == 0 && conn->ksnc_rx_started) {
+ /* EOF in the middle of a message */
+ rc = -EPROTO;
+ }
+ break;
+ }
+
+ /* Completed a fragment */
+
+ if (conn->ksnc_rx_nob_wanted == 0) {
+ rc = 1;
+ break;
+ }
+ }
+
+ ksocknal_connsock_decref(conn);
+ return rc;
+}
+
+void
+ksocknal_tx_done (lnet_ni_t *ni, ksock_tx_t *tx)
+{
+ lnet_msg_t *lnetmsg = tx->tx_lnetmsg;
+ int rc = (tx->tx_resid == 0 && !tx->tx_zc_aborted) ? 0 : -EIO;
+
+ LASSERT(ni != NULL || tx->tx_conn != NULL);
+
+ if (tx->tx_conn != NULL)
+ ksocknal_conn_decref(tx->tx_conn);
+
+ if (ni == NULL && tx->tx_conn != NULL)
+ ni = tx->tx_conn->ksnc_peer->ksnp_ni;
+
+ ksocknal_free_tx (tx);
+ if (lnetmsg != NULL) /* KSOCK_MSG_NOOP go without lnetmsg */
+ lnet_finalize (ni, lnetmsg, rc);
+}
+
+void
+ksocknal_txlist_done (lnet_ni_t *ni, struct list_head *txlist, int error)
+{
+ ksock_tx_t *tx;
+
+ while (!list_empty (txlist)) {
+ tx = list_entry (txlist->next, ksock_tx_t, tx_list);
+
+ if (error && tx->tx_lnetmsg != NULL) {
+ CNETERR("Deleting packet type %d len %d %s->%s\n",
+ le32_to_cpu (tx->tx_lnetmsg->msg_hdr.type),
+ le32_to_cpu (tx->tx_lnetmsg->msg_hdr.payload_length),
+ libcfs_nid2str(le64_to_cpu(tx->tx_lnetmsg->msg_hdr.src_nid)),
+ libcfs_nid2str(le64_to_cpu(tx->tx_lnetmsg->msg_hdr.dest_nid)));
+ } else if (error) {
+ CNETERR("Deleting noop packet\n");
+ }
+
+ list_del (&tx->tx_list);
+
+ LASSERT (atomic_read(&tx->tx_refcount) == 1);
+ ksocknal_tx_done (ni, tx);
+ }
+}
+
+static void
+ksocknal_check_zc_req(ksock_tx_t *tx)
+{
+ ksock_conn_t *conn = tx->tx_conn;
+ ksock_peer_t *peer = conn->ksnc_peer;
+
+ /* Set tx_msg.ksm_zc_cookies[0] to a unique non-zero cookie and add tx
+ * to ksnp_zc_req_list if some fragment of this message should be sent
+ * zero-copy. Our peer will send an ACK containing this cookie when
+ * she has received this message to tell us we can signal completion.
+ * tx_msg.ksm_zc_cookies[0] remains non-zero while tx is on
+ * ksnp_zc_req_list. */
+ LASSERT (tx->tx_msg.ksm_type != KSOCK_MSG_NOOP);
+ LASSERT (tx->tx_zc_capable);
+
+ tx->tx_zc_checked = 1;
+
+ if (conn->ksnc_proto == &ksocknal_protocol_v1x ||
+ !conn->ksnc_zc_capable)
+ return;
+
+ /* assign cookie and queue tx to pending list, it will be released when
+ * a matching ack is received. See ksocknal_handle_zcack() */
+
+ ksocknal_tx_addref(tx);
+
+ spin_lock(&peer->ksnp_lock);
+
+ /* ZC_REQ is going to be pinned to the peer */
+ tx->tx_deadline =
+ cfs_time_shift(*ksocknal_tunables.ksnd_timeout);
+
+ LASSERT (tx->tx_msg.ksm_zc_cookies[0] == 0);
+
+ tx->tx_msg.ksm_zc_cookies[0] = peer->ksnp_zc_next_cookie++;
+
+ if (peer->ksnp_zc_next_cookie == 0)
+ peer->ksnp_zc_next_cookie = SOCKNAL_KEEPALIVE_PING + 1;
+
+ list_add_tail(&tx->tx_zc_list, &peer->ksnp_zc_req_list);
+
+ spin_unlock(&peer->ksnp_lock);
+}
+
+static void
+ksocknal_uncheck_zc_req(ksock_tx_t *tx)
+{
+ ksock_peer_t *peer = tx->tx_conn->ksnc_peer;
+
+ LASSERT(tx->tx_msg.ksm_type != KSOCK_MSG_NOOP);
+ LASSERT(tx->tx_zc_capable);
+
+ tx->tx_zc_checked = 0;
+
+ spin_lock(&peer->ksnp_lock);
+
+ if (tx->tx_msg.ksm_zc_cookies[0] == 0) {
+ /* Not waiting for an ACK */
+ spin_unlock(&peer->ksnp_lock);
+ return;
+ }
+
+ tx->tx_msg.ksm_zc_cookies[0] = 0;
+ list_del(&tx->tx_zc_list);
+
+ spin_unlock(&peer->ksnp_lock);
+
+ ksocknal_tx_decref(tx);
+}
+
+static int
+ksocknal_process_transmit (ksock_conn_t *conn, ksock_tx_t *tx)
+{
+ int rc;
+
+ if (tx->tx_zc_capable && !tx->tx_zc_checked)
+ ksocknal_check_zc_req(tx);
+
+ rc = ksocknal_transmit (conn, tx);
+
+ CDEBUG (D_NET, "send(%d) %d\n", tx->tx_resid, rc);
+
+ if (tx->tx_resid == 0) {
+ /* Sent everything OK */
+ LASSERT (rc == 0);
+
+ return 0;
+ }
+
+ if (rc == -EAGAIN)
+ return rc;
+
+ if (rc == -ENOMEM) {
+ static int counter;
+
+ counter++; /* exponential backoff warnings */
+ if ((counter & (-counter)) == counter)
+ CWARN("%u ENOMEM tx %p (%u allocated)\n",
+ counter, conn, atomic_read(&libcfs_kmemory));
+
+ /* Queue on ksnd_enomem_conns for retry after a timeout */
+ spin_lock_bh(&ksocknal_data.ksnd_reaper_lock);
+
+ /* enomem list takes over scheduler's ref... */
+ LASSERT (conn->ksnc_tx_scheduled);
+ list_add_tail(&conn->ksnc_tx_list,
+ &ksocknal_data.ksnd_enomem_conns);
+ if (!cfs_time_aftereq(cfs_time_add(cfs_time_current(),
+ SOCKNAL_ENOMEM_RETRY),
+ ksocknal_data.ksnd_reaper_waketime))
+ wake_up (&ksocknal_data.ksnd_reaper_waitq);
+
+ spin_unlock_bh(&ksocknal_data.ksnd_reaper_lock);
+ return rc;
+ }
+
+ /* Actual error */
+ LASSERT (rc < 0);
+
+ if (!conn->ksnc_closing) {
+ switch (rc) {
+ case -ECONNRESET:
+ LCONSOLE_WARN("Host %pI4h reset our connection while we were sending data; it may have rebooted.\n",
+ &conn->ksnc_ipaddr);
+ break;
+ default:
+ LCONSOLE_WARN("There was an unexpected network error while writing to %pI4h: %d.\n",
+ &conn->ksnc_ipaddr, rc);
+ break;
+ }
+ CDEBUG(D_NET, "[%p] Error %d on write to %s ip %pI4h:%d\n",
+ conn, rc,
+ libcfs_id2str(conn->ksnc_peer->ksnp_id),
+ &conn->ksnc_ipaddr,
+ conn->ksnc_port);
+ }
+
+ if (tx->tx_zc_checked)
+ ksocknal_uncheck_zc_req(tx);
+
+ /* it's not an error if conn is being closed */
+ ksocknal_close_conn_and_siblings (conn,
+ (conn->ksnc_closing) ? 0 : rc);
+
+ return rc;
+}
+
+static void
+ksocknal_launch_connection_locked (ksock_route_t *route)
+{
+
+ /* called holding write lock on ksnd_global_lock */
+
+ LASSERT (!route->ksnr_scheduled);
+ LASSERT (!route->ksnr_connecting);
+ LASSERT ((ksocknal_route_mask() & ~route->ksnr_connected) != 0);
+
+ route->ksnr_scheduled = 1; /* scheduling conn for connd */
+ ksocknal_route_addref(route); /* extra ref for connd */
+
+ spin_lock_bh(&ksocknal_data.ksnd_connd_lock);
+
+ list_add_tail(&route->ksnr_connd_list,
+ &ksocknal_data.ksnd_connd_routes);
+ wake_up(&ksocknal_data.ksnd_connd_waitq);
+
+ spin_unlock_bh(&ksocknal_data.ksnd_connd_lock);
+}
+
+void
+ksocknal_launch_all_connections_locked (ksock_peer_t *peer)
+{
+ ksock_route_t *route;
+
+ /* called holding write lock on ksnd_global_lock */
+ for (;;) {
+ /* launch any/all connections that need it */
+ route = ksocknal_find_connectable_route_locked(peer);
+ if (route == NULL)
+ return;
+
+ ksocknal_launch_connection_locked(route);
+ }
+}
+
+ksock_conn_t *
+ksocknal_find_conn_locked(ksock_peer_t *peer, ksock_tx_t *tx, int nonblk)
+{
+ struct list_head *tmp;
+ ksock_conn_t *conn;
+ ksock_conn_t *typed = NULL;
+ ksock_conn_t *fallback = NULL;
+ int tnob = 0;
+ int fnob = 0;
+
+ list_for_each (tmp, &peer->ksnp_conns) {
+ ksock_conn_t *c = list_entry(tmp, ksock_conn_t, ksnc_list);
+ int nob = atomic_read(&c->ksnc_tx_nob) +
+ c->ksnc_sock->sk->sk_wmem_queued;
+ int rc;
+
+ LASSERT (!c->ksnc_closing);
+ LASSERT (c->ksnc_proto != NULL &&
+ c->ksnc_proto->pro_match_tx != NULL);
+
+ rc = c->ksnc_proto->pro_match_tx(c, tx, nonblk);
+
+ switch (rc) {
+ default:
+ LBUG();
+ case SOCKNAL_MATCH_NO: /* protocol rejected the tx */
+ continue;
+
+ case SOCKNAL_MATCH_YES: /* typed connection */
+ if (typed == NULL || tnob > nob ||
+ (tnob == nob && *ksocknal_tunables.ksnd_round_robin &&
+ cfs_time_after(typed->ksnc_tx_last_post, c->ksnc_tx_last_post))) {
+ typed = c;
+ tnob = nob;
+ }
+ break;
+
+ case SOCKNAL_MATCH_MAY: /* fallback connection */
+ if (fallback == NULL || fnob > nob ||
+ (fnob == nob && *ksocknal_tunables.ksnd_round_robin &&
+ cfs_time_after(fallback->ksnc_tx_last_post, c->ksnc_tx_last_post))) {
+ fallback = c;
+ fnob = nob;
+ }
+ break;
+ }
+ }
+
+ /* prefer the typed selection */
+ conn = (typed != NULL) ? typed : fallback;
+
+ if (conn != NULL)
+ conn->ksnc_tx_last_post = cfs_time_current();
+
+ return conn;
+}
+
+void
+ksocknal_tx_prep(ksock_conn_t *conn, ksock_tx_t *tx)
+{
+ conn->ksnc_proto->pro_pack(tx);
+
+ atomic_add (tx->tx_nob, &conn->ksnc_tx_nob);
+ ksocknal_conn_addref(conn); /* +1 ref for tx */
+ tx->tx_conn = conn;
+}
+
+void
+ksocknal_queue_tx_locked (ksock_tx_t *tx, ksock_conn_t *conn)
+{
+ ksock_sched_t *sched = conn->ksnc_scheduler;
+ ksock_msg_t *msg = &tx->tx_msg;
+ ksock_tx_t *ztx = NULL;
+ int bufnob = 0;
+
+ /* called holding global lock (read or irq-write) and caller may
+ * not have dropped this lock between finding conn and calling me,
+ * so we don't need the {get,put}connsock dance to deref
+ * ksnc_sock... */
+ LASSERT(!conn->ksnc_closing);
+
+ CDEBUG(D_NET, "Sending to %s ip %pI4h:%d\n",
+ libcfs_id2str(conn->ksnc_peer->ksnp_id),
+ &conn->ksnc_ipaddr,
+ conn->ksnc_port);
+
+ ksocknal_tx_prep(conn, tx);
+
+ /* Ensure the frags we've been given EXACTLY match the number of
+ * bytes we want to send. Many TCP/IP stacks disregard any total
+ * size parameters passed to them and just look at the frags.
+ *
+ * We always expect at least 1 mapped fragment containing the
+ * complete ksocknal message header. */
+ LASSERT (lnet_iov_nob (tx->tx_niov, tx->tx_iov) +
+ lnet_kiov_nob(tx->tx_nkiov, tx->tx_kiov) ==
+ (unsigned int)tx->tx_nob);
+ LASSERT (tx->tx_niov >= 1);
+ LASSERT (tx->tx_resid == tx->tx_nob);
+
+ CDEBUG (D_NET, "Packet %p type %d, nob %d niov %d nkiov %d\n",
+ tx, (tx->tx_lnetmsg != NULL) ? tx->tx_lnetmsg->msg_hdr.type:
+ KSOCK_MSG_NOOP,
+ tx->tx_nob, tx->tx_niov, tx->tx_nkiov);
+
+ /*
+ * FIXME: SOCK_WMEM_QUEUED and SOCK_ERROR could block in __DARWIN8__
+ * but they're used inside spinlocks a lot.
+ */
+ bufnob = conn->ksnc_sock->sk->sk_wmem_queued;
+ spin_lock_bh(&sched->kss_lock);
+
+ if (list_empty(&conn->ksnc_tx_queue) && bufnob == 0) {
+ /* First packet starts the timeout */
+ conn->ksnc_tx_deadline =
+ cfs_time_shift(*ksocknal_tunables.ksnd_timeout);
+ if (conn->ksnc_tx_bufnob > 0) /* something got ACKed */
+ conn->ksnc_peer->ksnp_last_alive = cfs_time_current();
+ conn->ksnc_tx_bufnob = 0;
+ mb(); /* order with adding to tx_queue */
+ }
+
+ if (msg->ksm_type == KSOCK_MSG_NOOP) {
+ /* The packet is noop ZC ACK, try to piggyback the ack_cookie
+ * on a normal packet so I don't need to send it */
+ LASSERT (msg->ksm_zc_cookies[1] != 0);
+ LASSERT (conn->ksnc_proto->pro_queue_tx_zcack != NULL);
+
+ if (conn->ksnc_proto->pro_queue_tx_zcack(conn, tx, 0))
+ ztx = tx; /* ZC ACK piggybacked on ztx release tx later */
+
+ } else {
+ /* It's a normal packet - can it piggback a noop zc-ack that
+ * has been queued already? */
+ LASSERT (msg->ksm_zc_cookies[1] == 0);
+ LASSERT (conn->ksnc_proto->pro_queue_tx_msg != NULL);
+
+ ztx = conn->ksnc_proto->pro_queue_tx_msg(conn, tx);
+ /* ztx will be released later */
+ }
+
+ if (ztx != NULL) {
+ atomic_sub (ztx->tx_nob, &conn->ksnc_tx_nob);
+ list_add_tail(&ztx->tx_list, &sched->kss_zombie_noop_txs);
+ }
+
+ if (conn->ksnc_tx_ready && /* able to send */
+ !conn->ksnc_tx_scheduled) { /* not scheduled to send */
+ /* +1 ref for scheduler */
+ ksocknal_conn_addref(conn);
+ list_add_tail (&conn->ksnc_tx_list,
+ &sched->kss_tx_conns);
+ conn->ksnc_tx_scheduled = 1;
+ wake_up (&sched->kss_waitq);
+ }
+
+ spin_unlock_bh(&sched->kss_lock);
+}
+
+
+ksock_route_t *
+ksocknal_find_connectable_route_locked (ksock_peer_t *peer)
+{
+ unsigned long now = cfs_time_current();
+ struct list_head *tmp;
+ ksock_route_t *route;
+
+ list_for_each (tmp, &peer->ksnp_routes) {
+ route = list_entry (tmp, ksock_route_t, ksnr_list);
+
+ LASSERT (!route->ksnr_connecting || route->ksnr_scheduled);
+
+ if (route->ksnr_scheduled) /* connections being established */
+ continue;
+
+ /* all route types connected ? */
+ if ((ksocknal_route_mask() & ~route->ksnr_connected) == 0)
+ continue;
+
+ if (!(route->ksnr_retry_interval == 0 || /* first attempt */
+ cfs_time_aftereq(now, route->ksnr_timeout))) {
+ CDEBUG(D_NET,
+ "Too soon to retry route %pI4h (cnted %d, interval %ld, %ld secs later)\n",
+ &route->ksnr_ipaddr,
+ route->ksnr_connected,
+ route->ksnr_retry_interval,
+ cfs_duration_sec(route->ksnr_timeout - now));
+ continue;
+ }
+
+ return route;
+ }
+
+ return NULL;
+}
+
+ksock_route_t *
+ksocknal_find_connecting_route_locked (ksock_peer_t *peer)
+{
+ struct list_head *tmp;
+ ksock_route_t *route;
+
+ list_for_each (tmp, &peer->ksnp_routes) {
+ route = list_entry (tmp, ksock_route_t, ksnr_list);
+
+ LASSERT (!route->ksnr_connecting || route->ksnr_scheduled);
+
+ if (route->ksnr_scheduled)
+ return route;
+ }
+
+ return NULL;
+}
+
+int
+ksocknal_launch_packet (lnet_ni_t *ni, ksock_tx_t *tx, lnet_process_id_t id)
+{
+ ksock_peer_t *peer;
+ ksock_conn_t *conn;
+ rwlock_t *g_lock;
+ int retry;
+ int rc;
+
+ LASSERT (tx->tx_conn == NULL);
+
+ g_lock = &ksocknal_data.ksnd_global_lock;
+
+ for (retry = 0;; retry = 1) {
+ read_lock(g_lock);
+ peer = ksocknal_find_peer_locked(ni, id);
+ if (peer != NULL) {
+ if (ksocknal_find_connectable_route_locked(peer) == NULL) {
+ conn = ksocknal_find_conn_locked(peer, tx, tx->tx_nonblk);
+ if (conn != NULL) {
+ /* I've got no routes that need to be
+ * connecting and I do have an actual
+ * connection... */
+ ksocknal_queue_tx_locked (tx, conn);
+ read_unlock(g_lock);
+ return 0;
+ }
+ }
+ }
+
+ /* I'll need a write lock... */
+ read_unlock(g_lock);
+
+ write_lock_bh(g_lock);
+
+ peer = ksocknal_find_peer_locked(ni, id);
+ if (peer != NULL)
+ break;
+
+ write_unlock_bh(g_lock);
+
+ if ((id.pid & LNET_PID_USERFLAG) != 0) {
+ CERROR("Refusing to create a connection to userspace process %s\n",
+ libcfs_id2str(id));
+ return -EHOSTUNREACH;
+ }
+
+ if (retry) {
+ CERROR("Can't find peer %s\n", libcfs_id2str(id));
+ return -EHOSTUNREACH;
+ }
+
+ rc = ksocknal_add_peer(ni, id,
+ LNET_NIDADDR(id.nid),
+ lnet_acceptor_port());
+ if (rc != 0) {
+ CERROR("Can't add peer %s: %d\n",
+ libcfs_id2str(id), rc);
+ return rc;
+ }
+ }
+
+ ksocknal_launch_all_connections_locked(peer);
+
+ conn = ksocknal_find_conn_locked(peer, tx, tx->tx_nonblk);
+ if (conn != NULL) {
+ /* Connection exists; queue message on it */
+ ksocknal_queue_tx_locked (tx, conn);
+ write_unlock_bh(g_lock);
+ return 0;
+ }
+
+ if (peer->ksnp_accepting > 0 ||
+ ksocknal_find_connecting_route_locked (peer) != NULL) {
+ /* the message is going to be pinned to the peer */
+ tx->tx_deadline =
+ cfs_time_shift(*ksocknal_tunables.ksnd_timeout);
+
+ /* Queue the message until a connection is established */
+ list_add_tail (&tx->tx_list, &peer->ksnp_tx_queue);
+ write_unlock_bh(g_lock);
+ return 0;
+ }
+
+ write_unlock_bh(g_lock);
+
+ /* NB Routes may be ignored if connections to them failed recently */
+ CNETERR("No usable routes to %s\n", libcfs_id2str(id));
+ return -EHOSTUNREACH;
+}
+
+int
+ksocknal_send(lnet_ni_t *ni, void *private, lnet_msg_t *lntmsg)
+{
+ int mpflag = 1;
+ int type = lntmsg->msg_type;
+ lnet_process_id_t target = lntmsg->msg_target;
+ unsigned int payload_niov = lntmsg->msg_niov;
+ struct kvec *payload_iov = lntmsg->msg_iov;
+ lnet_kiov_t *payload_kiov = lntmsg->msg_kiov;
+ unsigned int payload_offset = lntmsg->msg_offset;
+ unsigned int payload_nob = lntmsg->msg_len;
+ ksock_tx_t *tx;
+ int desc_size;
+ int rc;
+
+ /* NB 'private' is different depending on what we're sending.
+ * Just ignore it... */
+
+ CDEBUG(D_NET, "sending %u bytes in %d frags to %s\n",
+ payload_nob, payload_niov, libcfs_id2str(target));
+
+ LASSERT (payload_nob == 0 || payload_niov > 0);
+ LASSERT (payload_niov <= LNET_MAX_IOV);
+ /* payload is either all vaddrs or all pages */
+ LASSERT (!(payload_kiov != NULL && payload_iov != NULL));
+ LASSERT (!in_interrupt ());
+
+ if (payload_iov != NULL)
+ desc_size = offsetof(ksock_tx_t,
+ tx_frags.virt.iov[1 + payload_niov]);
+ else
+ desc_size = offsetof(ksock_tx_t,
+ tx_frags.paged.kiov[payload_niov]);
+
+ if (lntmsg->msg_vmflush)
+ mpflag = cfs_memory_pressure_get_and_set();
+ tx = ksocknal_alloc_tx(KSOCK_MSG_LNET, desc_size);
+ if (tx == NULL) {
+ CERROR("Can't allocate tx desc type %d size %d\n",
+ type, desc_size);
+ if (lntmsg->msg_vmflush)
+ cfs_memory_pressure_restore(mpflag);
+ return -ENOMEM;
+ }
+
+ tx->tx_conn = NULL; /* set when assigned a conn */
+ tx->tx_lnetmsg = lntmsg;
+
+ if (payload_iov != NULL) {
+ tx->tx_kiov = NULL;
+ tx->tx_nkiov = 0;
+ tx->tx_iov = tx->tx_frags.virt.iov;
+ tx->tx_niov = 1 +
+ lnet_extract_iov(payload_niov, &tx->tx_iov[1],
+ payload_niov, payload_iov,
+ payload_offset, payload_nob);
+ } else {
+ tx->tx_niov = 1;
+ tx->tx_iov = &tx->tx_frags.paged.iov;
+ tx->tx_kiov = tx->tx_frags.paged.kiov;
+ tx->tx_nkiov = lnet_extract_kiov(payload_niov, tx->tx_kiov,
+ payload_niov, payload_kiov,
+ payload_offset, payload_nob);
+
+ if (payload_nob >= *ksocknal_tunables.ksnd_zc_min_payload)
+ tx->tx_zc_capable = 1;
+ }
+
+ socklnd_init_msg(&tx->tx_msg, KSOCK_MSG_LNET);
+
+ /* The first fragment will be set later in pro_pack */
+ rc = ksocknal_launch_packet(ni, tx, target);
+ if (!mpflag)
+ cfs_memory_pressure_restore(mpflag);
+
+ if (rc == 0)
+ return 0;
+
+ ksocknal_free_tx(tx);
+ return -EIO;
+}
+
+int
+ksocknal_thread_start(int (*fn)(void *arg), void *arg, char *name)
+{
+ struct task_struct *task = kthread_run(fn, arg, "%s", name);
+
+ if (IS_ERR(task))
+ return PTR_ERR(task);
+
+ write_lock_bh(&ksocknal_data.ksnd_global_lock);
+ ksocknal_data.ksnd_nthreads++;
+ write_unlock_bh(&ksocknal_data.ksnd_global_lock);
+ return 0;
+}
+
+void
+ksocknal_thread_fini (void)
+{
+ write_lock_bh(&ksocknal_data.ksnd_global_lock);
+ ksocknal_data.ksnd_nthreads--;
+ write_unlock_bh(&ksocknal_data.ksnd_global_lock);
+}
+
+int
+ksocknal_new_packet (ksock_conn_t *conn, int nob_to_skip)
+{
+ static char ksocknal_slop_buffer[4096];
+
+ int nob;
+ unsigned int niov;
+ int skipped;
+
+ LASSERT(conn->ksnc_proto != NULL);
+
+ if ((*ksocknal_tunables.ksnd_eager_ack & conn->ksnc_type) != 0) {
+ /* Remind the socket to ack eagerly... */
+ ksocknal_lib_eager_ack(conn);
+ }
+
+ if (nob_to_skip == 0) { /* right at next packet boundary now */
+ conn->ksnc_rx_started = 0;
+ mb(); /* racing with timeout thread */
+
+ switch (conn->ksnc_proto->pro_version) {
+ case KSOCK_PROTO_V2:
+ case KSOCK_PROTO_V3:
+ conn->ksnc_rx_state = SOCKNAL_RX_KSM_HEADER;
+ conn->ksnc_rx_iov = (struct kvec *)&conn->ksnc_rx_iov_space;
+ conn->ksnc_rx_iov[0].iov_base = &conn->ksnc_msg;
+
+ conn->ksnc_rx_nob_wanted = offsetof(ksock_msg_t, ksm_u);
+ conn->ksnc_rx_nob_left = offsetof(ksock_msg_t, ksm_u);
+ conn->ksnc_rx_iov[0].iov_len = offsetof(ksock_msg_t, ksm_u);
+ break;
+
+ case KSOCK_PROTO_V1:
+ /* Receiving bare lnet_hdr_t */
+ conn->ksnc_rx_state = SOCKNAL_RX_LNET_HEADER;
+ conn->ksnc_rx_nob_wanted = sizeof(lnet_hdr_t);
+ conn->ksnc_rx_nob_left = sizeof(lnet_hdr_t);
+
+ conn->ksnc_rx_iov = (struct kvec *)&conn->ksnc_rx_iov_space;
+ conn->ksnc_rx_iov[0].iov_base = &conn->ksnc_msg.ksm_u.lnetmsg;
+ conn->ksnc_rx_iov[0].iov_len = sizeof (lnet_hdr_t);
+ break;
+
+ default:
+ LBUG ();
+ }
+ conn->ksnc_rx_niov = 1;
+
+ conn->ksnc_rx_kiov = NULL;
+ conn->ksnc_rx_nkiov = 0;
+ conn->ksnc_rx_csum = ~0;
+ return 1;
+ }
+
+ /* Set up to skip as much as possible now. If there's more left
+ * (ran out of iov entries) we'll get called again */
+
+ conn->ksnc_rx_state = SOCKNAL_RX_SLOP;
+ conn->ksnc_rx_nob_left = nob_to_skip;
+ conn->ksnc_rx_iov = (struct kvec *)&conn->ksnc_rx_iov_space;
+ skipped = 0;
+ niov = 0;
+
+ do {
+ nob = min_t(int, nob_to_skip, sizeof(ksocknal_slop_buffer));
+
+ conn->ksnc_rx_iov[niov].iov_base = ksocknal_slop_buffer;
+ conn->ksnc_rx_iov[niov].iov_len = nob;
+ niov++;
+ skipped += nob;
+ nob_to_skip -=nob;
+
+ } while (nob_to_skip != 0 && /* mustn't overflow conn's rx iov */
+ niov < sizeof(conn->ksnc_rx_iov_space) / sizeof (struct iovec));
+
+ conn->ksnc_rx_niov = niov;
+ conn->ksnc_rx_kiov = NULL;
+ conn->ksnc_rx_nkiov = 0;
+ conn->ksnc_rx_nob_wanted = skipped;
+ return 0;
+}
+
+static int
+ksocknal_process_receive (ksock_conn_t *conn)
+{
+ lnet_hdr_t *lhdr;
+ lnet_process_id_t *id;
+ int rc;
+
+ LASSERT (atomic_read(&conn->ksnc_conn_refcount) > 0);
+
+ /* NB: sched lock NOT held */
+ /* SOCKNAL_RX_LNET_HEADER is here for backward compatibility */
+ LASSERT (conn->ksnc_rx_state == SOCKNAL_RX_KSM_HEADER ||
+ conn->ksnc_rx_state == SOCKNAL_RX_LNET_PAYLOAD ||
+ conn->ksnc_rx_state == SOCKNAL_RX_LNET_HEADER ||
+ conn->ksnc_rx_state == SOCKNAL_RX_SLOP);
+ again:
+ if (conn->ksnc_rx_nob_wanted != 0) {
+ rc = ksocknal_receive(conn);
+
+ if (rc <= 0) {
+ LASSERT (rc != -EAGAIN);
+
+ if (rc == 0)
+ CDEBUG(D_NET, "[%p] EOF from %s ip %pI4h:%d\n",
+ conn,
+ libcfs_id2str(conn->ksnc_peer->ksnp_id),
+ &conn->ksnc_ipaddr,
+ conn->ksnc_port);
+ else if (!conn->ksnc_closing)
+ CERROR("[%p] Error %d on read from %s ip %pI4h:%d\n",
+ conn, rc,
+ libcfs_id2str(conn->ksnc_peer->ksnp_id),
+ &conn->ksnc_ipaddr,
+ conn->ksnc_port);
+
+ /* it's not an error if conn is being closed */
+ ksocknal_close_conn_and_siblings (conn,
+ (conn->ksnc_closing) ? 0 : rc);
+ return (rc == 0 ? -ESHUTDOWN : rc);
+ }
+
+ if (conn->ksnc_rx_nob_wanted != 0) {
+ /* short read */
+ return -EAGAIN;
+ }
+ }
+ switch (conn->ksnc_rx_state) {
+ case SOCKNAL_RX_KSM_HEADER:
+ if (conn->ksnc_flip) {
+ __swab32s(&conn->ksnc_msg.ksm_type);
+ __swab32s(&conn->ksnc_msg.ksm_csum);
+ __swab64s(&conn->ksnc_msg.ksm_zc_cookies[0]);
+ __swab64s(&conn->ksnc_msg.ksm_zc_cookies[1]);
+ }
+
+ if (conn->ksnc_msg.ksm_type != KSOCK_MSG_NOOP &&
+ conn->ksnc_msg.ksm_type != KSOCK_MSG_LNET) {
+ CERROR("%s: Unknown message type: %x\n",
+ libcfs_id2str(conn->ksnc_peer->ksnp_id),
+ conn->ksnc_msg.ksm_type);
+ ksocknal_new_packet(conn, 0);
+ ksocknal_close_conn_and_siblings(conn, -EPROTO);
+ return -EPROTO;
+ }
+
+ if (conn->ksnc_msg.ksm_type == KSOCK_MSG_NOOP &&
+ conn->ksnc_msg.ksm_csum != 0 && /* has checksum */
+ conn->ksnc_msg.ksm_csum != conn->ksnc_rx_csum) {
+ /* NOOP Checksum error */
+ CERROR("%s: Checksum error, wire:0x%08X data:0x%08X\n",
+ libcfs_id2str(conn->ksnc_peer->ksnp_id),
+ conn->ksnc_msg.ksm_csum, conn->ksnc_rx_csum);
+ ksocknal_new_packet(conn, 0);
+ ksocknal_close_conn_and_siblings(conn, -EPROTO);
+ return -EIO;
+ }
+
+ if (conn->ksnc_msg.ksm_zc_cookies[1] != 0) {
+ __u64 cookie = 0;
+
+ LASSERT (conn->ksnc_proto != &ksocknal_protocol_v1x);
+
+ if (conn->ksnc_msg.ksm_type == KSOCK_MSG_NOOP)
+ cookie = conn->ksnc_msg.ksm_zc_cookies[0];
+
+ rc = conn->ksnc_proto->pro_handle_zcack(conn, cookie,
+ conn->ksnc_msg.ksm_zc_cookies[1]);
+
+ if (rc != 0) {
+ CERROR("%s: Unknown ZC-ACK cookie: %llu, %llu\n",
+ libcfs_id2str(conn->ksnc_peer->ksnp_id),
+ cookie, conn->ksnc_msg.ksm_zc_cookies[1]);
+ ksocknal_new_packet(conn, 0);
+ ksocknal_close_conn_and_siblings(conn, -EPROTO);
+ return rc;
+ }
+ }
+
+ if (conn->ksnc_msg.ksm_type == KSOCK_MSG_NOOP) {
+ ksocknal_new_packet (conn, 0);
+ return 0; /* NOOP is done and just return */
+ }
+
+ conn->ksnc_rx_state = SOCKNAL_RX_LNET_HEADER;
+ conn->ksnc_rx_nob_wanted = sizeof(ksock_lnet_msg_t);
+ conn->ksnc_rx_nob_left = sizeof(ksock_lnet_msg_t);
+
+ conn->ksnc_rx_iov = (struct kvec *)&conn->ksnc_rx_iov_space;
+ conn->ksnc_rx_iov[0].iov_base = &conn->ksnc_msg.ksm_u.lnetmsg;
+ conn->ksnc_rx_iov[0].iov_len = sizeof(ksock_lnet_msg_t);
+
+ conn->ksnc_rx_niov = 1;
+ conn->ksnc_rx_kiov = NULL;
+ conn->ksnc_rx_nkiov = 0;
+
+ goto again; /* read lnet header now */
+
+ case SOCKNAL_RX_LNET_HEADER:
+ /* unpack message header */
+ conn->ksnc_proto->pro_unpack(&conn->ksnc_msg);
+
+ if ((conn->ksnc_peer->ksnp_id.pid & LNET_PID_USERFLAG) != 0) {
+ /* Userspace peer */
+ lhdr = &conn->ksnc_msg.ksm_u.lnetmsg.ksnm_hdr;
+ id = &conn->ksnc_peer->ksnp_id;
+
+ /* Substitute process ID assigned at connection time */
+ lhdr->src_pid = cpu_to_le32(id->pid);
+ lhdr->src_nid = cpu_to_le64(id->nid);
+ }
+
+ conn->ksnc_rx_state = SOCKNAL_RX_PARSE;
+ ksocknal_conn_addref(conn); /* ++ref while parsing */
+
+ rc = lnet_parse(conn->ksnc_peer->ksnp_ni,
+ &conn->ksnc_msg.ksm_u.lnetmsg.ksnm_hdr,
+ conn->ksnc_peer->ksnp_id.nid, conn, 0);
+ if (rc < 0) {
+ /* I just received garbage: give up on this conn */
+ ksocknal_new_packet(conn, 0);
+ ksocknal_close_conn_and_siblings (conn, rc);
+ ksocknal_conn_decref(conn);
+ return -EPROTO;
+ }
+
+ /* I'm racing with ksocknal_recv() */
+ LASSERT (conn->ksnc_rx_state == SOCKNAL_RX_PARSE ||
+ conn->ksnc_rx_state == SOCKNAL_RX_LNET_PAYLOAD);
+
+ if (conn->ksnc_rx_state != SOCKNAL_RX_LNET_PAYLOAD)
+ return 0;
+
+ /* ksocknal_recv() got called */
+ goto again;
+
+ case SOCKNAL_RX_LNET_PAYLOAD:
+ /* payload all received */
+ rc = 0;
+
+ if (conn->ksnc_rx_nob_left == 0 && /* not truncating */
+ conn->ksnc_msg.ksm_csum != 0 && /* has checksum */
+ conn->ksnc_msg.ksm_csum != conn->ksnc_rx_csum) {
+ CERROR("%s: Checksum error, wire:0x%08X data:0x%08X\n",
+ libcfs_id2str(conn->ksnc_peer->ksnp_id),
+ conn->ksnc_msg.ksm_csum, conn->ksnc_rx_csum);
+ rc = -EIO;
+ }
+
+ if (rc == 0 && conn->ksnc_msg.ksm_zc_cookies[0] != 0) {
+ LASSERT(conn->ksnc_proto != &ksocknal_protocol_v1x);
+
+ lhdr = &conn->ksnc_msg.ksm_u.lnetmsg.ksnm_hdr;
+ id = &conn->ksnc_peer->ksnp_id;
+
+ rc = conn->ksnc_proto->pro_handle_zcreq(conn,
+ conn->ksnc_msg.ksm_zc_cookies[0],
+ *ksocknal_tunables.ksnd_nonblk_zcack ||
+ le64_to_cpu(lhdr->src_nid) != id->nid);
+ }
+
+ lnet_finalize(conn->ksnc_peer->ksnp_ni, conn->ksnc_cookie, rc);
+
+ if (rc != 0) {
+ ksocknal_new_packet(conn, 0);
+ ksocknal_close_conn_and_siblings (conn, rc);
+ return -EPROTO;
+ }
+ /* Fall through */
+
+ case SOCKNAL_RX_SLOP:
+ /* starting new packet? */
+ if (ksocknal_new_packet (conn, conn->ksnc_rx_nob_left))
+ return 0; /* come back later */
+ goto again; /* try to finish reading slop now */
+
+ default:
+ break;
+ }
+
+ /* Not Reached */
+ LBUG ();
+ return -EINVAL; /* keep gcc happy */
+}
+
+int
+ksocknal_recv (lnet_ni_t *ni, void *private, lnet_msg_t *msg, int delayed,
+ unsigned int niov, struct kvec *iov, lnet_kiov_t *kiov,
+ unsigned int offset, unsigned int mlen, unsigned int rlen)
+{
+ ksock_conn_t *conn = (ksock_conn_t *)private;
+ ksock_sched_t *sched = conn->ksnc_scheduler;
+
+ LASSERT (mlen <= rlen);
+ LASSERT (niov <= LNET_MAX_IOV);
+
+ conn->ksnc_cookie = msg;
+ conn->ksnc_rx_nob_wanted = mlen;
+ conn->ksnc_rx_nob_left = rlen;
+
+ if (mlen == 0 || iov != NULL) {
+ conn->ksnc_rx_nkiov = 0;
+ conn->ksnc_rx_kiov = NULL;
+ conn->ksnc_rx_iov = conn->ksnc_rx_iov_space.iov;
+ conn->ksnc_rx_niov =
+ lnet_extract_iov(LNET_MAX_IOV, conn->ksnc_rx_iov,
+ niov, iov, offset, mlen);
+ } else {
+ conn->ksnc_rx_niov = 0;
+ conn->ksnc_rx_iov = NULL;
+ conn->ksnc_rx_kiov = conn->ksnc_rx_iov_space.kiov;
+ conn->ksnc_rx_nkiov =
+ lnet_extract_kiov(LNET_MAX_IOV, conn->ksnc_rx_kiov,
+ niov, kiov, offset, mlen);
+ }
+
+ LASSERT (mlen ==
+ lnet_iov_nob (conn->ksnc_rx_niov, conn->ksnc_rx_iov) +
+ lnet_kiov_nob (conn->ksnc_rx_nkiov, conn->ksnc_rx_kiov));
+
+ LASSERT (conn->ksnc_rx_scheduled);
+
+ spin_lock_bh(&sched->kss_lock);
+
+ switch (conn->ksnc_rx_state) {
+ case SOCKNAL_RX_PARSE_WAIT:
+ list_add_tail(&conn->ksnc_rx_list, &sched->kss_rx_conns);
+ wake_up (&sched->kss_waitq);
+ LASSERT (conn->ksnc_rx_ready);
+ break;
+
+ case SOCKNAL_RX_PARSE:
+ /* scheduler hasn't noticed I'm parsing yet */
+ break;
+ }
+
+ conn->ksnc_rx_state = SOCKNAL_RX_LNET_PAYLOAD;
+
+ spin_unlock_bh(&sched->kss_lock);
+ ksocknal_conn_decref(conn);
+ return 0;
+}
+
+static inline int
+ksocknal_sched_cansleep(ksock_sched_t *sched)
+{
+ int rc;
+
+ spin_lock_bh(&sched->kss_lock);
+
+ rc = !ksocknal_data.ksnd_shuttingdown &&
+ list_empty(&sched->kss_rx_conns) &&
+ list_empty(&sched->kss_tx_conns);
+
+ spin_unlock_bh(&sched->kss_lock);
+ return rc;
+}
+
+int ksocknal_scheduler(void *arg)
+{
+ struct ksock_sched_info *info;
+ ksock_sched_t *sched;
+ ksock_conn_t *conn;
+ ksock_tx_t *tx;
+ int rc;
+ int nloops = 0;
+ long id = (long)arg;
+
+ info = ksocknal_data.ksnd_sched_info[KSOCK_THREAD_CPT(id)];
+ sched = &info->ksi_scheds[KSOCK_THREAD_SID(id)];
+
+ cfs_block_allsigs();
+
+ rc = cfs_cpt_bind(lnet_cpt_table(), info->ksi_cpt);
+ if (rc != 0) {
+ CERROR("Can't set CPT affinity to %d: %d\n",
+ info->ksi_cpt, rc);
+ }
+
+ spin_lock_bh(&sched->kss_lock);
+
+ while (!ksocknal_data.ksnd_shuttingdown) {
+ int did_something = 0;
+
+ /* Ensure I progress everything semi-fairly */
+
+ if (!list_empty (&sched->kss_rx_conns)) {
+ conn = list_entry(sched->kss_rx_conns.next,
+ ksock_conn_t, ksnc_rx_list);
+ list_del(&conn->ksnc_rx_list);
+
+ LASSERT(conn->ksnc_rx_scheduled);
+ LASSERT(conn->ksnc_rx_ready);
+
+ /* clear rx_ready in case receive isn't complete.
+ * Do it BEFORE we call process_recv, since
+ * data_ready can set it any time after we release
+ * kss_lock. */
+ conn->ksnc_rx_ready = 0;
+ spin_unlock_bh(&sched->kss_lock);
+
+ rc = ksocknal_process_receive(conn);
+
+ spin_lock_bh(&sched->kss_lock);
+
+ /* I'm the only one that can clear this flag */
+ LASSERT(conn->ksnc_rx_scheduled);
+
+ /* Did process_receive get everything it wanted? */
+ if (rc == 0)
+ conn->ksnc_rx_ready = 1;
+
+ if (conn->ksnc_rx_state == SOCKNAL_RX_PARSE) {
+ /* Conn blocked waiting for ksocknal_recv()
+ * I change its state (under lock) to signal
+ * it can be rescheduled */
+ conn->ksnc_rx_state = SOCKNAL_RX_PARSE_WAIT;
+ } else if (conn->ksnc_rx_ready) {
+ /* reschedule for rx */
+ list_add_tail (&conn->ksnc_rx_list,
+ &sched->kss_rx_conns);
+ } else {
+ conn->ksnc_rx_scheduled = 0;
+ /* drop my ref */
+ ksocknal_conn_decref(conn);
+ }
+
+ did_something = 1;
+ }
+
+ if (!list_empty (&sched->kss_tx_conns)) {
+ LIST_HEAD (zlist);
+
+ if (!list_empty(&sched->kss_zombie_noop_txs)) {
+ list_add(&zlist,
+ &sched->kss_zombie_noop_txs);
+ list_del_init(&sched->kss_zombie_noop_txs);
+ }
+
+ conn = list_entry(sched->kss_tx_conns.next,
+ ksock_conn_t, ksnc_tx_list);
+ list_del (&conn->ksnc_tx_list);
+
+ LASSERT(conn->ksnc_tx_scheduled);
+ LASSERT(conn->ksnc_tx_ready);
+ LASSERT(!list_empty(&conn->ksnc_tx_queue));
+
+ tx = list_entry(conn->ksnc_tx_queue.next,
+ ksock_tx_t, tx_list);
+
+ if (conn->ksnc_tx_carrier == tx)
+ ksocknal_next_tx_carrier(conn);
+
+ /* dequeue now so empty list => more to send */
+ list_del(&tx->tx_list);
+
+ /* Clear tx_ready in case send isn't complete. Do
+ * it BEFORE we call process_transmit, since
+ * write_space can set it any time after we release
+ * kss_lock. */
+ conn->ksnc_tx_ready = 0;
+ spin_unlock_bh(&sched->kss_lock);
+
+ if (!list_empty(&zlist)) {
+ /* free zombie noop txs, it's fast because
+ * noop txs are just put in freelist */
+ ksocknal_txlist_done(NULL, &zlist, 0);
+ }
+
+ rc = ksocknal_process_transmit(conn, tx);
+
+ if (rc == -ENOMEM || rc == -EAGAIN) {
+ /* Incomplete send: replace tx on HEAD of tx_queue */
+ spin_lock_bh(&sched->kss_lock);
+ list_add(&tx->tx_list,
+ &conn->ksnc_tx_queue);
+ } else {
+ /* Complete send; tx -ref */
+ ksocknal_tx_decref(tx);
+
+ spin_lock_bh(&sched->kss_lock);
+ /* assume space for more */
+ conn->ksnc_tx_ready = 1;
+ }
+
+ if (rc == -ENOMEM) {
+ /* Do nothing; after a short timeout, this
+ * conn will be reposted on kss_tx_conns. */
+ } else if (conn->ksnc_tx_ready &&
+ !list_empty (&conn->ksnc_tx_queue)) {
+ /* reschedule for tx */
+ list_add_tail (&conn->ksnc_tx_list,
+ &sched->kss_tx_conns);
+ } else {
+ conn->ksnc_tx_scheduled = 0;
+ /* drop my ref */
+ ksocknal_conn_decref(conn);
+ }
+
+ did_something = 1;
+ }
+ if (!did_something || /* nothing to do */
+ ++nloops == SOCKNAL_RESCHED) { /* hogging CPU? */
+ spin_unlock_bh(&sched->kss_lock);
+
+ nloops = 0;
+
+ if (!did_something) { /* wait for something to do */
+ rc = wait_event_interruptible_exclusive(
+ sched->kss_waitq,
+ !ksocknal_sched_cansleep(sched));
+ LASSERT (rc == 0);
+ } else {
+ cond_resched();
+ }
+
+ spin_lock_bh(&sched->kss_lock);
+ }
+ }
+
+ spin_unlock_bh(&sched->kss_lock);
+ ksocknal_thread_fini();
+ return 0;
+}
+
+/*
+ * Add connection to kss_rx_conns of scheduler
+ * and wakeup the scheduler.
+ */
+void ksocknal_read_callback (ksock_conn_t *conn)
+{
+ ksock_sched_t *sched;
+
+ sched = conn->ksnc_scheduler;
+
+ spin_lock_bh(&sched->kss_lock);
+
+ conn->ksnc_rx_ready = 1;
+
+ if (!conn->ksnc_rx_scheduled) { /* not being progressed */
+ list_add_tail(&conn->ksnc_rx_list,
+ &sched->kss_rx_conns);
+ conn->ksnc_rx_scheduled = 1;
+ /* extra ref for scheduler */
+ ksocknal_conn_addref(conn);
+
+ wake_up (&sched->kss_waitq);
+ }
+ spin_unlock_bh(&sched->kss_lock);
+}
+
+/*
+ * Add connection to kss_tx_conns of scheduler
+ * and wakeup the scheduler.
+ */
+void ksocknal_write_callback (ksock_conn_t *conn)
+{
+ ksock_sched_t *sched;
+
+ sched = conn->ksnc_scheduler;
+
+ spin_lock_bh(&sched->kss_lock);
+
+ conn->ksnc_tx_ready = 1;
+
+ if (!conn->ksnc_tx_scheduled && /* not being progressed */
+ !list_empty(&conn->ksnc_tx_queue)) { /* packets to send */
+ list_add_tail (&conn->ksnc_tx_list,
+ &sched->kss_tx_conns);
+ conn->ksnc_tx_scheduled = 1;
+ /* extra ref for scheduler */
+ ksocknal_conn_addref(conn);
+
+ wake_up (&sched->kss_waitq);
+ }
+
+ spin_unlock_bh(&sched->kss_lock);
+}
+
+static ksock_proto_t *
+ksocknal_parse_proto_version (ksock_hello_msg_t *hello)
+{
+ __u32 version = 0;
+
+ if (hello->kshm_magic == LNET_PROTO_MAGIC)
+ version = hello->kshm_version;
+ else if (hello->kshm_magic == __swab32(LNET_PROTO_MAGIC))
+ version = __swab32(hello->kshm_version);
+
+ if (version != 0) {
+#if SOCKNAL_VERSION_DEBUG
+ if (*ksocknal_tunables.ksnd_protocol == 1)
+ return NULL;
+
+ if (*ksocknal_tunables.ksnd_protocol == 2 &&
+ version == KSOCK_PROTO_V3)
+ return NULL;
+#endif
+ if (version == KSOCK_PROTO_V2)
+ return &ksocknal_protocol_v2x;
+
+ if (version == KSOCK_PROTO_V3)
+ return &ksocknal_protocol_v3x;
+
+ return NULL;
+ }
+
+ if (hello->kshm_magic == le32_to_cpu(LNET_PROTO_TCP_MAGIC)) {
+ lnet_magicversion_t *hmv = (lnet_magicversion_t *)hello;
+
+ CLASSERT (sizeof (lnet_magicversion_t) ==
+ offsetof (ksock_hello_msg_t, kshm_src_nid));
+
+ if (hmv->version_major == cpu_to_le16 (KSOCK_PROTO_V1_MAJOR) &&
+ hmv->version_minor == cpu_to_le16 (KSOCK_PROTO_V1_MINOR))
+ return &ksocknal_protocol_v1x;
+ }
+
+ return NULL;
+}
+
+int
+ksocknal_send_hello (lnet_ni_t *ni, ksock_conn_t *conn,
+ lnet_nid_t peer_nid, ksock_hello_msg_t *hello)
+{
+ /* CAVEAT EMPTOR: this byte flips 'ipaddrs' */
+ ksock_net_t *net = (ksock_net_t *)ni->ni_data;
+
+ LASSERT (hello->kshm_nips <= LNET_MAX_INTERFACES);
+
+ /* rely on caller to hold a ref on socket so it wouldn't disappear */
+ LASSERT (conn->ksnc_proto != NULL);
+
+ hello->kshm_src_nid = ni->ni_nid;
+ hello->kshm_dst_nid = peer_nid;
+ hello->kshm_src_pid = the_lnet.ln_pid;
+
+ hello->kshm_src_incarnation = net->ksnn_incarnation;
+ hello->kshm_ctype = conn->ksnc_type;
+
+ return conn->ksnc_proto->pro_send_hello(conn, hello);
+}
+
+static int
+ksocknal_invert_type(int type)
+{
+ switch (type) {
+ case SOCKLND_CONN_ANY:
+ case SOCKLND_CONN_CONTROL:
+ return type;
+ case SOCKLND_CONN_BULK_IN:
+ return SOCKLND_CONN_BULK_OUT;
+ case SOCKLND_CONN_BULK_OUT:
+ return SOCKLND_CONN_BULK_IN;
+ default:
+ return SOCKLND_CONN_NONE;
+ }
+}
+
+int
+ksocknal_recv_hello (lnet_ni_t *ni, ksock_conn_t *conn,
+ ksock_hello_msg_t *hello, lnet_process_id_t *peerid,
+ __u64 *incarnation)
+{
+ /* Return < 0 fatal error
+ * 0 success
+ * EALREADY lost connection race
+ * EPROTO protocol version mismatch
+ */
+ struct socket *sock = conn->ksnc_sock;
+ int active = (conn->ksnc_proto != NULL);
+ int timeout;
+ int proto_match;
+ int rc;
+ ksock_proto_t *proto;
+ lnet_process_id_t recv_id;
+
+ /* socket type set on active connections - not set on passive */
+ LASSERT (!active == !(conn->ksnc_type != SOCKLND_CONN_NONE));
+
+ timeout = active ? *ksocknal_tunables.ksnd_timeout :
+ lnet_acceptor_timeout();
+
+ rc = libcfs_sock_read(sock, &hello->kshm_magic, sizeof (hello->kshm_magic), timeout);
+ if (rc != 0) {
+ CERROR("Error %d reading HELLO from %pI4h\n",
+ rc, &conn->ksnc_ipaddr);
+ LASSERT (rc < 0);
+ return rc;
+ }
+
+ if (hello->kshm_magic != LNET_PROTO_MAGIC &&
+ hello->kshm_magic != __swab32(LNET_PROTO_MAGIC) &&
+ hello->kshm_magic != le32_to_cpu (LNET_PROTO_TCP_MAGIC)) {
+ /* Unexpected magic! */
+ CERROR("Bad magic(1) %#08x (%#08x expected) from %pI4h\n",
+ __cpu_to_le32 (hello->kshm_magic),
+ LNET_PROTO_TCP_MAGIC,
+ &conn->ksnc_ipaddr);
+ return -EPROTO;
+ }
+
+ rc = libcfs_sock_read(sock, &hello->kshm_version,
+ sizeof(hello->kshm_version), timeout);
+ if (rc != 0) {
+ CERROR("Error %d reading HELLO from %pI4h\n",
+ rc, &conn->ksnc_ipaddr);
+ LASSERT (rc < 0);
+ return rc;
+ }
+
+ proto = ksocknal_parse_proto_version(hello);
+ if (proto == NULL) {
+ if (!active) {
+ /* unknown protocol from peer, tell peer my protocol */
+ conn->ksnc_proto = &ksocknal_protocol_v3x;
+#if SOCKNAL_VERSION_DEBUG
+ if (*ksocknal_tunables.ksnd_protocol == 2)
+ conn->ksnc_proto = &ksocknal_protocol_v2x;
+ else if (*ksocknal_tunables.ksnd_protocol == 1)
+ conn->ksnc_proto = &ksocknal_protocol_v1x;
+#endif
+ hello->kshm_nips = 0;
+ ksocknal_send_hello(ni, conn, ni->ni_nid, hello);
+ }
+
+ CERROR("Unknown protocol version (%d.x expected) from %pI4h\n",
+ conn->ksnc_proto->pro_version,
+ &conn->ksnc_ipaddr);
+
+ return -EPROTO;
+ }
+
+ proto_match = (conn->ksnc_proto == proto);
+ conn->ksnc_proto = proto;
+
+ /* receive the rest of hello message anyway */
+ rc = conn->ksnc_proto->pro_recv_hello(conn, hello, timeout);
+ if (rc != 0) {
+ CERROR("Error %d reading or checking hello from from %pI4h\n",
+ rc, &conn->ksnc_ipaddr);
+ LASSERT (rc < 0);
+ return rc;
+ }
+
+ *incarnation = hello->kshm_src_incarnation;
+
+ if (hello->kshm_src_nid == LNET_NID_ANY) {
+ CERROR("Expecting a HELLO hdr with a NID, but got LNET_NID_ANY from %pI4h\n",
+ &conn->ksnc_ipaddr);
+ return -EPROTO;
+ }
+
+ if (!active &&
+ conn->ksnc_port > LNET_ACCEPTOR_MAX_RESERVED_PORT) {
+ /* Userspace NAL assigns peer process ID from socket */
+ recv_id.pid = conn->ksnc_port | LNET_PID_USERFLAG;
+ recv_id.nid = LNET_MKNID(LNET_NIDNET(ni->ni_nid), conn->ksnc_ipaddr);
+ } else {
+ recv_id.nid = hello->kshm_src_nid;
+ recv_id.pid = hello->kshm_src_pid;
+ }
+
+ if (!active) {
+ *peerid = recv_id;
+
+ /* peer determines type */
+ conn->ksnc_type = ksocknal_invert_type(hello->kshm_ctype);
+ if (conn->ksnc_type == SOCKLND_CONN_NONE) {
+ CERROR("Unexpected type %d from %s ip %pI4h\n",
+ hello->kshm_ctype, libcfs_id2str(*peerid),
+ &conn->ksnc_ipaddr);
+ return -EPROTO;
+ }
+
+ return 0;
+ }
+
+ if (peerid->pid != recv_id.pid ||
+ peerid->nid != recv_id.nid) {
+ LCONSOLE_ERROR_MSG(0x130, "Connected successfully to %s on host %pI4h, but they claimed they were %s; please check your Lustre configuration.\n",
+ libcfs_id2str(*peerid),
+ &conn->ksnc_ipaddr,
+ libcfs_id2str(recv_id));
+ return -EPROTO;
+ }
+
+ if (hello->kshm_ctype == SOCKLND_CONN_NONE) {
+ /* Possible protocol mismatch or I lost the connection race */
+ return proto_match ? EALREADY : EPROTO;
+ }
+
+ if (ksocknal_invert_type(hello->kshm_ctype) != conn->ksnc_type) {
+ CERROR("Mismatched types: me %d, %s ip %pI4h %d\n",
+ conn->ksnc_type, libcfs_id2str(*peerid),
+ &conn->ksnc_ipaddr,
+ hello->kshm_ctype);
+ return -EPROTO;
+ }
+
+ return 0;
+}
+
+static int
+ksocknal_connect (ksock_route_t *route)
+{
+ LIST_HEAD (zombies);
+ ksock_peer_t *peer = route->ksnr_peer;
+ int type;
+ int wanted;
+ struct socket *sock;
+ unsigned long deadline;
+ int retry_later = 0;
+ int rc = 0;
+
+ deadline = cfs_time_add(cfs_time_current(),
+ cfs_time_seconds(*ksocknal_tunables.ksnd_timeout));
+
+ write_lock_bh(&ksocknal_data.ksnd_global_lock);
+
+ LASSERT (route->ksnr_scheduled);
+ LASSERT (!route->ksnr_connecting);
+
+ route->ksnr_connecting = 1;
+
+ for (;;) {
+ wanted = ksocknal_route_mask() & ~route->ksnr_connected;
+
+ /* stop connecting if peer/route got closed under me, or
+ * route got connected while queued */
+ if (peer->ksnp_closing || route->ksnr_deleted ||
+ wanted == 0) {
+ retry_later = 0;
+ break;
+ }
+
+ /* reschedule if peer is connecting to me */
+ if (peer->ksnp_accepting > 0) {
+ CDEBUG(D_NET,
+ "peer %s(%d) already connecting to me, retry later.\n",
+ libcfs_nid2str(peer->ksnp_id.nid), peer->ksnp_accepting);
+ retry_later = 1;
+ }
+
+ if (retry_later) /* needs reschedule */
+ break;
+
+ if ((wanted & (1 << SOCKLND_CONN_ANY)) != 0) {
+ type = SOCKLND_CONN_ANY;
+ } else if ((wanted & (1 << SOCKLND_CONN_CONTROL)) != 0) {
+ type = SOCKLND_CONN_CONTROL;
+ } else if ((wanted & (1 << SOCKLND_CONN_BULK_IN)) != 0) {
+ type = SOCKLND_CONN_BULK_IN;
+ } else {
+ LASSERT ((wanted & (1 << SOCKLND_CONN_BULK_OUT)) != 0);
+ type = SOCKLND_CONN_BULK_OUT;
+ }
+
+ write_unlock_bh(&ksocknal_data.ksnd_global_lock);
+
+ if (cfs_time_aftereq(cfs_time_current(), deadline)) {
+ rc = -ETIMEDOUT;
+ lnet_connect_console_error(rc, peer->ksnp_id.nid,
+ route->ksnr_ipaddr,
+ route->ksnr_port);
+ goto failed;
+ }
+
+ rc = lnet_connect(&sock, peer->ksnp_id.nid,
+ route->ksnr_myipaddr,
+ route->ksnr_ipaddr, route->ksnr_port);
+ if (rc != 0)
+ goto failed;
+
+ rc = ksocknal_create_conn(peer->ksnp_ni, route, sock, type);
+ if (rc < 0) {
+ lnet_connect_console_error(rc, peer->ksnp_id.nid,
+ route->ksnr_ipaddr,
+ route->ksnr_port);
+ goto failed;
+ }
+
+ /* A +ve RC means I have to retry because I lost the connection
+ * race or I have to renegotiate protocol version */
+ retry_later = (rc != 0);
+ if (retry_later)
+ CDEBUG(D_NET, "peer %s: conn race, retry later.\n",
+ libcfs_nid2str(peer->ksnp_id.nid));
+
+ write_lock_bh(&ksocknal_data.ksnd_global_lock);
+ }
+
+ route->ksnr_scheduled = 0;
+ route->ksnr_connecting = 0;
+
+ if (retry_later) {
+ /* re-queue for attention; this frees me up to handle
+ * the peer's incoming connection request */
+
+ if (rc == EALREADY ||
+ (rc == 0 && peer->ksnp_accepting > 0)) {
+ /* We want to introduce a delay before next
+ * attempt to connect if we lost conn race,
+ * but the race is resolved quickly usually,
+ * so min_reconnectms should be good heuristic */
+ route->ksnr_retry_interval =
+ cfs_time_seconds(*ksocknal_tunables.ksnd_min_reconnectms)/1000;
+ route->ksnr_timeout = cfs_time_add(cfs_time_current(),
+ route->ksnr_retry_interval);
+ }
+
+ ksocknal_launch_connection_locked(route);
+ }
+
+ write_unlock_bh(&ksocknal_data.ksnd_global_lock);
+ return retry_later;
+
+ failed:
+ write_lock_bh(&ksocknal_data.ksnd_global_lock);
+
+ route->ksnr_scheduled = 0;
+ route->ksnr_connecting = 0;
+
+ /* This is a retry rather than a new connection */
+ route->ksnr_retry_interval *= 2;
+ route->ksnr_retry_interval =
+ max(route->ksnr_retry_interval,
+ cfs_time_seconds(*ksocknal_tunables.ksnd_min_reconnectms)/1000);
+ route->ksnr_retry_interval =
+ min(route->ksnr_retry_interval,
+ cfs_time_seconds(*ksocknal_tunables.ksnd_max_reconnectms)/1000);
+
+ LASSERT (route->ksnr_retry_interval != 0);
+ route->ksnr_timeout = cfs_time_add(cfs_time_current(),
+ route->ksnr_retry_interval);
+
+ if (!list_empty(&peer->ksnp_tx_queue) &&
+ peer->ksnp_accepting == 0 &&
+ ksocknal_find_connecting_route_locked(peer) == NULL) {
+ ksock_conn_t *conn;
+
+ /* ksnp_tx_queue is queued on a conn on successful
+ * connection for V1.x and V2.x */
+ if (!list_empty (&peer->ksnp_conns)) {
+ conn = list_entry(peer->ksnp_conns.next,
+ ksock_conn_t, ksnc_list);
+ LASSERT (conn->ksnc_proto == &ksocknal_protocol_v3x);
+ }
+
+ /* take all the blocked packets while I've got the lock and
+ * complete below... */
+ list_splice_init(&peer->ksnp_tx_queue, &zombies);
+ }
+
+#if 0 /* irrelevant with only eager routes */
+ if (!route->ksnr_deleted) {
+ /* make this route least-favourite for re-selection */
+ list_del(&route->ksnr_list);
+ list_add_tail(&route->ksnr_list, &peer->ksnp_routes);
+ }
+#endif
+ write_unlock_bh(&ksocknal_data.ksnd_global_lock);
+
+ ksocknal_peer_failed(peer);
+ ksocknal_txlist_done(peer->ksnp_ni, &zombies, 1);
+ return 0;
+}
+
+/*
+ * check whether we need to create more connds.
+ * It will try to create new thread if it's necessary, @timeout can
+ * be updated if failed to create, so caller wouldn't keep try while
+ * running out of resource.
+ */
+static int
+ksocknal_connd_check_start(long sec, long *timeout)
+{
+ char name[16];
+ int rc;
+ int total = ksocknal_data.ksnd_connd_starting +
+ ksocknal_data.ksnd_connd_running;
+
+ if (unlikely(ksocknal_data.ksnd_init < SOCKNAL_INIT_ALL)) {
+ /* still in initializing */
+ return 0;
+ }
+
+ if (total >= *ksocknal_tunables.ksnd_nconnds_max ||
+ total > ksocknal_data.ksnd_connd_connecting + SOCKNAL_CONND_RESV) {
+ /* can't create more connd, or still have enough
+ * threads to handle more connecting */
+ return 0;
+ }
+
+ if (list_empty(&ksocknal_data.ksnd_connd_routes)) {
+ /* no pending connecting request */
+ return 0;
+ }
+
+ if (sec - ksocknal_data.ksnd_connd_failed_stamp <= 1) {
+ /* may run out of resource, retry later */
+ *timeout = cfs_time_seconds(1);
+ return 0;
+ }
+
+ if (ksocknal_data.ksnd_connd_starting > 0) {
+ /* serialize starting to avoid flood */
+ return 0;
+ }
+
+ ksocknal_data.ksnd_connd_starting_stamp = sec;
+ ksocknal_data.ksnd_connd_starting++;
+ spin_unlock_bh(&ksocknal_data.ksnd_connd_lock);
+
+ /* NB: total is the next id */
+ snprintf(name, sizeof(name), "socknal_cd%02d", total);
+ rc = ksocknal_thread_start(ksocknal_connd, NULL, name);
+
+ spin_lock_bh(&ksocknal_data.ksnd_connd_lock);
+ if (rc == 0)
+ return 1;
+
+ /* we tried ... */
+ LASSERT(ksocknal_data.ksnd_connd_starting > 0);
+ ksocknal_data.ksnd_connd_starting--;
+ ksocknal_data.ksnd_connd_failed_stamp = get_seconds();
+
+ return 1;
+}
+
+/*
+ * check whether current thread can exit, it will return 1 if there are too
+ * many threads and no creating in past 120 seconds.
+ * Also, this function may update @timeout to make caller come back
+ * again to recheck these conditions.
+ */
+static int
+ksocknal_connd_check_stop(long sec, long *timeout)
+{
+ int val;
+
+ if (unlikely(ksocknal_data.ksnd_init < SOCKNAL_INIT_ALL)) {
+ /* still in initializing */
+ return 0;
+ }
+
+ if (ksocknal_data.ksnd_connd_starting > 0) {
+ /* in progress of starting new thread */
+ return 0;
+ }
+
+ if (ksocknal_data.ksnd_connd_running <=
+ *ksocknal_tunables.ksnd_nconnds) { /* can't shrink */
+ return 0;
+ }
+
+ /* created thread in past 120 seconds? */
+ val = (int)(ksocknal_data.ksnd_connd_starting_stamp +
+ SOCKNAL_CONND_TIMEOUT - sec);
+
+ *timeout = (val > 0) ? cfs_time_seconds(val) :
+ cfs_time_seconds(SOCKNAL_CONND_TIMEOUT);
+ if (val > 0)
+ return 0;
+
+ /* no creating in past 120 seconds */
+
+ return ksocknal_data.ksnd_connd_running >
+ ksocknal_data.ksnd_connd_connecting + SOCKNAL_CONND_RESV;
+}
+
+/* Go through connd_routes queue looking for a route that we can process
+ * right now, @timeout_p can be updated if we need to come back later */
+static ksock_route_t *
+ksocknal_connd_get_route_locked(signed long *timeout_p)
+{
+ ksock_route_t *route;
+ unsigned long now;
+
+ now = cfs_time_current();
+
+ /* connd_routes can contain both pending and ordinary routes */
+ list_for_each_entry (route, &ksocknal_data.ksnd_connd_routes,
+ ksnr_connd_list) {
+
+ if (route->ksnr_retry_interval == 0 ||
+ cfs_time_aftereq(now, route->ksnr_timeout))
+ return route;
+
+ if (*timeout_p == MAX_SCHEDULE_TIMEOUT ||
+ (int)*timeout_p > (int)(route->ksnr_timeout - now))
+ *timeout_p = (int)(route->ksnr_timeout - now);
+ }
+
+ return NULL;
+}
+
+int
+ksocknal_connd (void *arg)
+{
+ spinlock_t *connd_lock = &ksocknal_data.ksnd_connd_lock;
+ ksock_connreq_t *cr;
+ wait_queue_t wait;
+ int nloops = 0;
+ int cons_retry = 0;
+
+ cfs_block_allsigs ();
+
+ init_waitqueue_entry(&wait, current);
+
+ spin_lock_bh(connd_lock);
+
+ LASSERT(ksocknal_data.ksnd_connd_starting > 0);
+ ksocknal_data.ksnd_connd_starting--;
+ ksocknal_data.ksnd_connd_running++;
+
+ while (!ksocknal_data.ksnd_shuttingdown) {
+ ksock_route_t *route = NULL;
+ long sec = get_seconds();
+ long timeout = MAX_SCHEDULE_TIMEOUT;
+ int dropped_lock = 0;
+
+ if (ksocknal_connd_check_stop(sec, &timeout)) {
+ /* wakeup another one to check stop */
+ wake_up(&ksocknal_data.ksnd_connd_waitq);
+ break;
+ }
+
+ if (ksocknal_connd_check_start(sec, &timeout)) {
+ /* created new thread */
+ dropped_lock = 1;
+ }
+
+ if (!list_empty(&ksocknal_data.ksnd_connd_connreqs)) {
+ /* Connection accepted by the listener */
+ cr = list_entry(ksocknal_data.ksnd_connd_connreqs. \
+ next, ksock_connreq_t, ksncr_list);
+
+ list_del(&cr->ksncr_list);
+ spin_unlock_bh(connd_lock);
+ dropped_lock = 1;
+
+ ksocknal_create_conn(cr->ksncr_ni, NULL,
+ cr->ksncr_sock, SOCKLND_CONN_NONE);
+ lnet_ni_decref(cr->ksncr_ni);
+ LIBCFS_FREE(cr, sizeof(*cr));
+
+ spin_lock_bh(connd_lock);
+ }
+
+ /* Only handle an outgoing connection request if there
+ * is a thread left to handle incoming connections and
+ * create new connd */
+ if (ksocknal_data.ksnd_connd_connecting + SOCKNAL_CONND_RESV <
+ ksocknal_data.ksnd_connd_running) {
+ route = ksocknal_connd_get_route_locked(&timeout);
+ }
+ if (route != NULL) {
+ list_del (&route->ksnr_connd_list);
+ ksocknal_data.ksnd_connd_connecting++;
+ spin_unlock_bh(connd_lock);
+ dropped_lock = 1;
+
+ if (ksocknal_connect(route)) {
+ /* consecutive retry */
+ if (cons_retry++ > SOCKNAL_INSANITY_RECONN) {
+ CWARN("massive consecutive re-connecting to %pI4h\n",
+ &route->ksnr_ipaddr);
+ cons_retry = 0;
+ }
+ } else {
+ cons_retry = 0;
+ }
+
+ ksocknal_route_decref(route);
+
+ spin_lock_bh(connd_lock);
+ ksocknal_data.ksnd_connd_connecting--;
+ }
+
+ if (dropped_lock) {
+ if (++nloops < SOCKNAL_RESCHED)
+ continue;
+ spin_unlock_bh(connd_lock);
+ nloops = 0;
+ cond_resched();
+ spin_lock_bh(connd_lock);
+ continue;
+ }
+
+ /* Nothing to do for 'timeout' */
+ set_current_state(TASK_INTERRUPTIBLE);
+ add_wait_queue_exclusive(&ksocknal_data.ksnd_connd_waitq, &wait);
+ spin_unlock_bh(connd_lock);
+
+ nloops = 0;
+ schedule_timeout(timeout);
+
+ remove_wait_queue(&ksocknal_data.ksnd_connd_waitq, &wait);
+ spin_lock_bh(connd_lock);
+ }
+ ksocknal_data.ksnd_connd_running--;
+ spin_unlock_bh(connd_lock);
+
+ ksocknal_thread_fini();
+ return 0;
+}
+
+static ksock_conn_t *
+ksocknal_find_timed_out_conn (ksock_peer_t *peer)
+{
+ /* We're called with a shared lock on ksnd_global_lock */
+ ksock_conn_t *conn;
+ struct list_head *ctmp;
+
+ list_for_each (ctmp, &peer->ksnp_conns) {
+ int error;
+ conn = list_entry (ctmp, ksock_conn_t, ksnc_list);
+
+ /* Don't need the {get,put}connsock dance to deref ksnc_sock */
+ LASSERT (!conn->ksnc_closing);
+
+ /* SOCK_ERROR will reset error code of socket in
+ * some platform (like Darwin8.x) */
+ error = conn->ksnc_sock->sk->sk_err;
+ if (error != 0) {
+ ksocknal_conn_addref(conn);
+
+ switch (error) {
+ case ECONNRESET:
+ CNETERR("A connection with %s (%pI4h:%d) was reset; it may have rebooted.\n",
+ libcfs_id2str(peer->ksnp_id),
+ &conn->ksnc_ipaddr,
+ conn->ksnc_port);
+ break;
+ case ETIMEDOUT:
+ CNETERR("A connection with %s (%pI4h:%d) timed out; the network or node may be down.\n",
+ libcfs_id2str(peer->ksnp_id),
+ &conn->ksnc_ipaddr,
+ conn->ksnc_port);
+ break;
+ default:
+ CNETERR("An unexpected network error %d occurred with %s (%pI4h:%d\n",
+ error,
+ libcfs_id2str(peer->ksnp_id),
+ &conn->ksnc_ipaddr,
+ conn->ksnc_port);
+ break;
+ }
+
+ return conn;
+ }
+
+ if (conn->ksnc_rx_started &&
+ cfs_time_aftereq(cfs_time_current(),
+ conn->ksnc_rx_deadline)) {
+ /* Timed out incomplete incoming message */
+ ksocknal_conn_addref(conn);
+ CNETERR("Timeout receiving from %s (%pI4h:%d), state %d wanted %d left %d\n",
+ libcfs_id2str(peer->ksnp_id),
+ &conn->ksnc_ipaddr,
+ conn->ksnc_port,
+ conn->ksnc_rx_state,
+ conn->ksnc_rx_nob_wanted,
+ conn->ksnc_rx_nob_left);
+ return conn;
+ }
+
+ if ((!list_empty(&conn->ksnc_tx_queue) ||
+ conn->ksnc_sock->sk->sk_wmem_queued != 0) &&
+ cfs_time_aftereq(cfs_time_current(),
+ conn->ksnc_tx_deadline)) {
+ /* Timed out messages queued for sending or
+ * buffered in the socket's send buffer */
+ ksocknal_conn_addref(conn);
+ CNETERR("Timeout sending data to %s (%pI4h:%d) the network or that node may be down.\n",
+ libcfs_id2str(peer->ksnp_id),
+ &conn->ksnc_ipaddr,
+ conn->ksnc_port);
+ return conn;
+ }
+ }
+
+ return NULL;
+}
+
+static inline void
+ksocknal_flush_stale_txs(ksock_peer_t *peer)
+{
+ ksock_tx_t *tx;
+ LIST_HEAD (stale_txs);
+
+ write_lock_bh(&ksocknal_data.ksnd_global_lock);
+
+ while (!list_empty (&peer->ksnp_tx_queue)) {
+ tx = list_entry (peer->ksnp_tx_queue.next,
+ ksock_tx_t, tx_list);
+
+ if (!cfs_time_aftereq(cfs_time_current(),
+ tx->tx_deadline))
+ break;
+
+ list_del (&tx->tx_list);
+ list_add_tail (&tx->tx_list, &stale_txs);
+ }
+
+ write_unlock_bh(&ksocknal_data.ksnd_global_lock);
+
+ ksocknal_txlist_done(peer->ksnp_ni, &stale_txs, 1);
+}
+
+static int
+ksocknal_send_keepalive_locked(ksock_peer_t *peer)
+{
+ ksock_sched_t *sched;
+ ksock_conn_t *conn;
+ ksock_tx_t *tx;
+
+ if (list_empty(&peer->ksnp_conns)) /* last_alive will be updated by create_conn */
+ return 0;
+
+ if (peer->ksnp_proto != &ksocknal_protocol_v3x)
+ return 0;
+
+ if (*ksocknal_tunables.ksnd_keepalive <= 0 ||
+ time_before(cfs_time_current(),
+ cfs_time_add(peer->ksnp_last_alive,
+ cfs_time_seconds(*ksocknal_tunables.ksnd_keepalive))))
+ return 0;
+
+ if (time_before(cfs_time_current(), peer->ksnp_send_keepalive))
+ return 0;
+
+ /* retry 10 secs later, so we wouldn't put pressure
+ * on this peer if we failed to send keepalive this time */
+ peer->ksnp_send_keepalive = cfs_time_shift(10);
+
+ conn = ksocknal_find_conn_locked(peer, NULL, 1);
+ if (conn != NULL) {
+ sched = conn->ksnc_scheduler;
+
+ spin_lock_bh(&sched->kss_lock);
+ if (!list_empty(&conn->ksnc_tx_queue)) {
+ spin_unlock_bh(&sched->kss_lock);
+ /* there is an queued ACK, don't need keepalive */
+ return 0;
+ }
+
+ spin_unlock_bh(&sched->kss_lock);
+ }
+
+ read_unlock(&ksocknal_data.ksnd_global_lock);
+
+ /* cookie = 1 is reserved for keepalive PING */
+ tx = ksocknal_alloc_tx_noop(1, 1);
+ if (tx == NULL) {
+ read_lock(&ksocknal_data.ksnd_global_lock);
+ return -ENOMEM;
+ }
+
+ if (ksocknal_launch_packet(peer->ksnp_ni, tx, peer->ksnp_id) == 0) {
+ read_lock(&ksocknal_data.ksnd_global_lock);
+ return 1;
+ }
+
+ ksocknal_free_tx(tx);
+ read_lock(&ksocknal_data.ksnd_global_lock);
+
+ return -EIO;
+}
+
+
+static void
+ksocknal_check_peer_timeouts (int idx)
+{
+ struct list_head *peers = &ksocknal_data.ksnd_peers[idx];
+ ksock_peer_t *peer;
+ ksock_conn_t *conn;
+ ksock_tx_t *tx;
+
+ again:
+ /* NB. We expect to have a look at all the peers and not find any
+ * connections to time out, so we just use a shared lock while we
+ * take a look... */
+ read_lock(&ksocknal_data.ksnd_global_lock);
+
+ list_for_each_entry(peer, peers, ksnp_list) {
+ unsigned long deadline = 0;
+ int resid = 0;
+ int n = 0;
+
+ if (ksocknal_send_keepalive_locked(peer) != 0) {
+ read_unlock(&ksocknal_data.ksnd_global_lock);
+ goto again;
+ }
+
+ conn = ksocknal_find_timed_out_conn (peer);
+
+ if (conn != NULL) {
+ read_unlock(&ksocknal_data.ksnd_global_lock);
+
+ ksocknal_close_conn_and_siblings (conn, -ETIMEDOUT);
+
+ /* NB we won't find this one again, but we can't
+ * just proceed with the next peer, since we dropped
+ * ksnd_global_lock and it might be dead already! */
+ ksocknal_conn_decref(conn);
+ goto again;
+ }
+
+ /* we can't process stale txs right here because we're
+ * holding only shared lock */
+ if (!list_empty (&peer->ksnp_tx_queue)) {
+ ksock_tx_t *tx =
+ list_entry (peer->ksnp_tx_queue.next,
+ ksock_tx_t, tx_list);
+
+ if (cfs_time_aftereq(cfs_time_current(),
+ tx->tx_deadline)) {
+
+ ksocknal_peer_addref(peer);
+ read_unlock(&ksocknal_data.ksnd_global_lock);
+
+ ksocknal_flush_stale_txs(peer);
+
+ ksocknal_peer_decref(peer);
+ goto again;
+ }
+ }
+
+ if (list_empty(&peer->ksnp_zc_req_list))
+ continue;
+
+ spin_lock(&peer->ksnp_lock);
+ list_for_each_entry(tx, &peer->ksnp_zc_req_list, tx_zc_list) {
+ if (!cfs_time_aftereq(cfs_time_current(),
+ tx->tx_deadline))
+ break;
+ /* ignore the TX if connection is being closed */
+ if (tx->tx_conn->ksnc_closing)
+ continue;
+ n++;
+ }
+
+ if (n == 0) {
+ spin_unlock(&peer->ksnp_lock);
+ continue;
+ }
+
+ tx = list_entry(peer->ksnp_zc_req_list.next,
+ ksock_tx_t, tx_zc_list);
+ deadline = tx->tx_deadline;
+ resid = tx->tx_resid;
+ conn = tx->tx_conn;
+ ksocknal_conn_addref(conn);
+
+ spin_unlock(&peer->ksnp_lock);
+ read_unlock(&ksocknal_data.ksnd_global_lock);
+
+ CERROR("Total %d stale ZC_REQs for peer %s detected; the oldest(%p) timed out %ld secs ago, resid: %d, wmem: %d\n",
+ n, libcfs_nid2str(peer->ksnp_id.nid), tx,
+ cfs_duration_sec(cfs_time_current() - deadline),
+ resid, conn->ksnc_sock->sk->sk_wmem_queued);
+
+ ksocknal_close_conn_and_siblings (conn, -ETIMEDOUT);
+ ksocknal_conn_decref(conn);
+ goto again;
+ }
+
+ read_unlock(&ksocknal_data.ksnd_global_lock);
+}
+
+int
+ksocknal_reaper (void *arg)
+{
+ wait_queue_t wait;
+ ksock_conn_t *conn;
+ ksock_sched_t *sched;
+ struct list_head enomem_conns;
+ int nenomem_conns;
+ long timeout;
+ int i;
+ int peer_index = 0;
+ unsigned long deadline = cfs_time_current();
+
+ cfs_block_allsigs ();
+
+ INIT_LIST_HEAD(&enomem_conns);
+ init_waitqueue_entry(&wait, current);
+
+ spin_lock_bh(&ksocknal_data.ksnd_reaper_lock);
+
+ while (!ksocknal_data.ksnd_shuttingdown) {
+
+ if (!list_empty (&ksocknal_data.ksnd_deathrow_conns)) {
+ conn = list_entry (ksocknal_data. \
+ ksnd_deathrow_conns.next,
+ ksock_conn_t, ksnc_list);
+ list_del (&conn->ksnc_list);
+
+ spin_unlock_bh(&ksocknal_data.ksnd_reaper_lock);
+
+ ksocknal_terminate_conn(conn);
+ ksocknal_conn_decref(conn);
+
+ spin_lock_bh(&ksocknal_data.ksnd_reaper_lock);
+ continue;
+ }
+
+ if (!list_empty (&ksocknal_data.ksnd_zombie_conns)) {
+ conn = list_entry (ksocknal_data.ksnd_zombie_conns.\
+ next, ksock_conn_t, ksnc_list);
+ list_del (&conn->ksnc_list);
+
+ spin_unlock_bh(&ksocknal_data.ksnd_reaper_lock);
+
+ ksocknal_destroy_conn(conn);
+
+ spin_lock_bh(&ksocknal_data.ksnd_reaper_lock);
+ continue;
+ }
+
+ if (!list_empty (&ksocknal_data.ksnd_enomem_conns)) {
+ list_add(&enomem_conns,
+ &ksocknal_data.ksnd_enomem_conns);
+ list_del_init(&ksocknal_data.ksnd_enomem_conns);
+ }
+
+ spin_unlock_bh(&ksocknal_data.ksnd_reaper_lock);
+
+ /* reschedule all the connections that stalled with ENOMEM... */
+ nenomem_conns = 0;
+ while (!list_empty (&enomem_conns)) {
+ conn = list_entry (enomem_conns.next,
+ ksock_conn_t, ksnc_tx_list);
+ list_del (&conn->ksnc_tx_list);
+
+ sched = conn->ksnc_scheduler;
+
+ spin_lock_bh(&sched->kss_lock);
+
+ LASSERT(conn->ksnc_tx_scheduled);
+ conn->ksnc_tx_ready = 1;
+ list_add_tail(&conn->ksnc_tx_list,
+ &sched->kss_tx_conns);
+ wake_up(&sched->kss_waitq);
+
+ spin_unlock_bh(&sched->kss_lock);
+ nenomem_conns++;
+ }
+
+ /* careful with the jiffy wrap... */
+ while ((timeout = cfs_time_sub(deadline,
+ cfs_time_current())) <= 0) {
+ const int n = 4;
+ const int p = 1;
+ int chunk = ksocknal_data.ksnd_peer_hash_size;
+
+ /* Time to check for timeouts on a few more peers: I do
+ * checks every 'p' seconds on a proportion of the peer
+ * table and I need to check every connection 'n' times
+ * within a timeout interval, to ensure I detect a
+ * timeout on any connection within (n+1)/n times the
+ * timeout interval. */
+
+ if (*ksocknal_tunables.ksnd_timeout > n * p)
+ chunk = (chunk * n * p) /
+ *ksocknal_tunables.ksnd_timeout;
+ if (chunk == 0)
+ chunk = 1;
+
+ for (i = 0; i < chunk; i++) {
+ ksocknal_check_peer_timeouts (peer_index);
+ peer_index = (peer_index + 1) %
+ ksocknal_data.ksnd_peer_hash_size;
+ }
+
+ deadline = cfs_time_add(deadline, cfs_time_seconds(p));
+ }
+
+ if (nenomem_conns != 0) {
+ /* Reduce my timeout if I rescheduled ENOMEM conns.
+ * This also prevents me getting woken immediately
+ * if any go back on my enomem list. */
+ timeout = SOCKNAL_ENOMEM_RETRY;
+ }
+ ksocknal_data.ksnd_reaper_waketime =
+ cfs_time_add(cfs_time_current(), timeout);
+
+ set_current_state (TASK_INTERRUPTIBLE);
+ add_wait_queue (&ksocknal_data.ksnd_reaper_waitq, &wait);
+
+ if (!ksocknal_data.ksnd_shuttingdown &&
+ list_empty (&ksocknal_data.ksnd_deathrow_conns) &&
+ list_empty (&ksocknal_data.ksnd_zombie_conns))
+ schedule_timeout(timeout);
+
+ set_current_state (TASK_RUNNING);
+ remove_wait_queue (&ksocknal_data.ksnd_reaper_waitq, &wait);
+
+ spin_lock_bh(&ksocknal_data.ksnd_reaper_lock);
+ }
+
+ spin_unlock_bh(&ksocknal_data.ksnd_reaper_lock);
+
+ ksocknal_thread_fini();
+ return 0;
+}
diff --git a/drivers/staging/lustre/lnet/klnds/socklnd/socklnd_lib-linux.c b/drivers/staging/lustre/lnet/klnds/socklnd/socklnd_lib-linux.c
new file mode 100644
index 000000000..f5e8ab060
--- /dev/null
+++ b/drivers/staging/lustre/lnet/klnds/socklnd/socklnd_lib-linux.c
@@ -0,0 +1,714 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2011, 2012, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ */
+
+#include "socklnd.h"
+
+int
+ksocknal_lib_get_conn_addrs(ksock_conn_t *conn)
+{
+ int rc = libcfs_sock_getaddr(conn->ksnc_sock, 1,
+ &conn->ksnc_ipaddr,
+ &conn->ksnc_port);
+
+ /* Didn't need the {get,put}connsock dance to deref ksnc_sock... */
+ LASSERT(!conn->ksnc_closing);
+
+ if (rc != 0) {
+ CERROR("Error %d getting sock peer IP\n", rc);
+ return rc;
+ }
+
+ rc = libcfs_sock_getaddr(conn->ksnc_sock, 0,
+ &conn->ksnc_myipaddr, NULL);
+ if (rc != 0) {
+ CERROR("Error %d getting sock local IP\n", rc);
+ return rc;
+ }
+
+ return 0;
+}
+
+int
+ksocknal_lib_zc_capable(ksock_conn_t *conn)
+{
+ int caps = conn->ksnc_sock->sk->sk_route_caps;
+
+ if (conn->ksnc_proto == &ksocknal_protocol_v1x)
+ return 0;
+
+ /* ZC if the socket supports scatter/gather and doesn't need software
+ * checksums */
+ return ((caps & NETIF_F_SG) != 0 && (caps & NETIF_F_ALL_CSUM) != 0);
+}
+
+int
+ksocknal_lib_send_iov(ksock_conn_t *conn, ksock_tx_t *tx)
+{
+ struct socket *sock = conn->ksnc_sock;
+ int nob;
+ int rc;
+
+ if (*ksocknal_tunables.ksnd_enable_csum && /* checksum enabled */
+ conn->ksnc_proto == &ksocknal_protocol_v2x && /* V2.x connection */
+ tx->tx_nob == tx->tx_resid && /* frist sending */
+ tx->tx_msg.ksm_csum == 0) /* not checksummed */
+ ksocknal_lib_csum_tx(tx);
+
+ /* NB we can't trust socket ops to either consume our iovs
+ * or leave them alone. */
+
+ {
+#if SOCKNAL_SINGLE_FRAG_TX
+ struct kvec scratch;
+ struct kvec *scratchiov = &scratch;
+ unsigned int niov = 1;
+#else
+ struct kvec *scratchiov = conn->ksnc_scheduler->kss_scratch_iov;
+ unsigned int niov = tx->tx_niov;
+#endif
+ struct msghdr msg = {.msg_flags = MSG_DONTWAIT};
+ int i;
+
+ for (nob = i = 0; i < niov; i++) {
+ scratchiov[i] = tx->tx_iov[i];
+ nob += scratchiov[i].iov_len;
+ }
+
+ if (!list_empty(&conn->ksnc_tx_queue) ||
+ nob < tx->tx_resid)
+ msg.msg_flags |= MSG_MORE;
+
+ rc = kernel_sendmsg(sock, &msg, scratchiov, niov, nob);
+ }
+ return rc;
+}
+
+int
+ksocknal_lib_send_kiov(ksock_conn_t *conn, ksock_tx_t *tx)
+{
+ struct socket *sock = conn->ksnc_sock;
+ lnet_kiov_t *kiov = tx->tx_kiov;
+ int rc;
+ int nob;
+
+ /* Not NOOP message */
+ LASSERT(tx->tx_lnetmsg != NULL);
+
+ /* NB we can't trust socket ops to either consume our iovs
+ * or leave them alone. */
+ if (tx->tx_msg.ksm_zc_cookies[0] != 0) {
+ /* Zero copy is enabled */
+ struct sock *sk = sock->sk;
+ struct page *page = kiov->kiov_page;
+ int offset = kiov->kiov_offset;
+ int fragsize = kiov->kiov_len;
+ int msgflg = MSG_DONTWAIT;
+
+ CDEBUG(D_NET, "page %p + offset %x for %d\n",
+ page, offset, kiov->kiov_len);
+
+ if (!list_empty(&conn->ksnc_tx_queue) ||
+ fragsize < tx->tx_resid)
+ msgflg |= MSG_MORE;
+
+ if (sk->sk_prot->sendpage != NULL) {
+ rc = sk->sk_prot->sendpage(sk, page,
+ offset, fragsize, msgflg);
+ } else {
+ rc = cfs_tcp_sendpage(sk, page, offset, fragsize,
+ msgflg);
+ }
+ } else {
+#if SOCKNAL_SINGLE_FRAG_TX || !SOCKNAL_RISK_KMAP_DEADLOCK
+ struct kvec scratch;
+ struct kvec *scratchiov = &scratch;
+ unsigned int niov = 1;
+#else
+#ifdef CONFIG_HIGHMEM
+#warning "XXX risk of kmap deadlock on multiple frags..."
+#endif
+ struct kvec *scratchiov = conn->ksnc_scheduler->kss_scratch_iov;
+ unsigned int niov = tx->tx_nkiov;
+#endif
+ struct msghdr msg = {.msg_flags = MSG_DONTWAIT};
+ int i;
+
+ for (nob = i = 0; i < niov; i++) {
+ scratchiov[i].iov_base = kmap(kiov[i].kiov_page) +
+ kiov[i].kiov_offset;
+ nob += scratchiov[i].iov_len = kiov[i].kiov_len;
+ }
+
+ if (!list_empty(&conn->ksnc_tx_queue) ||
+ nob < tx->tx_resid)
+ msg.msg_flags |= MSG_MORE;
+
+ rc = kernel_sendmsg(sock, &msg, (struct kvec *)scratchiov, niov, nob);
+
+ for (i = 0; i < niov; i++)
+ kunmap(kiov[i].kiov_page);
+ }
+ return rc;
+}
+
+void
+ksocknal_lib_eager_ack(ksock_conn_t *conn)
+{
+ int opt = 1;
+ struct socket *sock = conn->ksnc_sock;
+
+ /* Remind the socket to ACK eagerly. If I don't, the socket might
+ * think I'm about to send something it could piggy-back the ACK
+ * on, introducing delay in completing zero-copy sends in my
+ * peer. */
+
+ kernel_setsockopt(sock, SOL_TCP, TCP_QUICKACK,
+ (char *)&opt, sizeof(opt));
+}
+
+int
+ksocknal_lib_recv_iov(ksock_conn_t *conn)
+{
+#if SOCKNAL_SINGLE_FRAG_RX
+ struct kvec scratch;
+ struct kvec *scratchiov = &scratch;
+ unsigned int niov = 1;
+#else
+ struct kvec *scratchiov = conn->ksnc_scheduler->kss_scratch_iov;
+ unsigned int niov = conn->ksnc_rx_niov;
+#endif
+ struct kvec *iov = conn->ksnc_rx_iov;
+ struct msghdr msg = {
+ .msg_flags = 0
+ };
+ int nob;
+ int i;
+ int rc;
+ int fragnob;
+ int sum;
+ __u32 saved_csum;
+
+ /* NB we can't trust socket ops to either consume our iovs
+ * or leave them alone. */
+ LASSERT(niov > 0);
+
+ for (nob = i = 0; i < niov; i++) {
+ scratchiov[i] = iov[i];
+ nob += scratchiov[i].iov_len;
+ }
+ LASSERT(nob <= conn->ksnc_rx_nob_wanted);
+
+ rc = kernel_recvmsg(conn->ksnc_sock, &msg,
+ scratchiov, niov, nob, MSG_DONTWAIT);
+
+ saved_csum = 0;
+ if (conn->ksnc_proto == &ksocknal_protocol_v2x) {
+ saved_csum = conn->ksnc_msg.ksm_csum;
+ conn->ksnc_msg.ksm_csum = 0;
+ }
+
+ if (saved_csum != 0) {
+ /* accumulate checksum */
+ for (i = 0, sum = rc; sum > 0; i++, sum -= fragnob) {
+ LASSERT(i < niov);
+
+ fragnob = iov[i].iov_len;
+ if (fragnob > sum)
+ fragnob = sum;
+
+ conn->ksnc_rx_csum = ksocknal_csum(conn->ksnc_rx_csum,
+ iov[i].iov_base, fragnob);
+ }
+ conn->ksnc_msg.ksm_csum = saved_csum;
+ }
+
+ return rc;
+}
+
+static void
+ksocknal_lib_kiov_vunmap(void *addr)
+{
+ if (addr == NULL)
+ return;
+
+ vunmap(addr);
+}
+
+static void *
+ksocknal_lib_kiov_vmap(lnet_kiov_t *kiov, int niov,
+ struct kvec *iov, struct page **pages)
+{
+ void *addr;
+ int nob;
+ int i;
+
+ if (!*ksocknal_tunables.ksnd_zc_recv || pages == NULL)
+ return NULL;
+
+ LASSERT(niov <= LNET_MAX_IOV);
+
+ if (niov < 2 ||
+ niov < *ksocknal_tunables.ksnd_zc_recv_min_nfrags)
+ return NULL;
+
+ for (nob = i = 0; i < niov; i++) {
+ if ((kiov[i].kiov_offset != 0 && i > 0) ||
+ (kiov[i].kiov_offset + kiov[i].kiov_len != PAGE_CACHE_SIZE && i < niov - 1))
+ return NULL;
+
+ pages[i] = kiov[i].kiov_page;
+ nob += kiov[i].kiov_len;
+ }
+
+ addr = vmap(pages, niov, VM_MAP, PAGE_KERNEL);
+ if (addr == NULL)
+ return NULL;
+
+ iov->iov_base = addr + kiov[0].kiov_offset;
+ iov->iov_len = nob;
+
+ return addr;
+}
+
+int
+ksocknal_lib_recv_kiov(ksock_conn_t *conn)
+{
+#if SOCKNAL_SINGLE_FRAG_RX || !SOCKNAL_RISK_KMAP_DEADLOCK
+ struct kvec scratch;
+ struct kvec *scratchiov = &scratch;
+ struct page **pages = NULL;
+ unsigned int niov = 1;
+#else
+#ifdef CONFIG_HIGHMEM
+#warning "XXX risk of kmap deadlock on multiple frags..."
+#endif
+ struct kvec *scratchiov = conn->ksnc_scheduler->kss_scratch_iov;
+ struct page **pages = conn->ksnc_scheduler->kss_rx_scratch_pgs;
+ unsigned int niov = conn->ksnc_rx_nkiov;
+#endif
+ lnet_kiov_t *kiov = conn->ksnc_rx_kiov;
+ struct msghdr msg = {
+ .msg_flags = 0
+ };
+ int nob;
+ int i;
+ int rc;
+ void *base;
+ void *addr;
+ int sum;
+ int fragnob;
+ int n;
+
+ /* NB we can't trust socket ops to either consume our iovs
+ * or leave them alone. */
+ addr = ksocknal_lib_kiov_vmap(kiov, niov, scratchiov, pages);
+ if (addr != NULL) {
+ nob = scratchiov[0].iov_len;
+ n = 1;
+
+ } else {
+ for (nob = i = 0; i < niov; i++) {
+ nob += scratchiov[i].iov_len = kiov[i].kiov_len;
+ scratchiov[i].iov_base = kmap(kiov[i].kiov_page) +
+ kiov[i].kiov_offset;
+ }
+ n = niov;
+ }
+
+ LASSERT(nob <= conn->ksnc_rx_nob_wanted);
+
+ rc = kernel_recvmsg(conn->ksnc_sock, &msg,
+ (struct kvec *)scratchiov, n, nob, MSG_DONTWAIT);
+
+ if (conn->ksnc_msg.ksm_csum != 0) {
+ for (i = 0, sum = rc; sum > 0; i++, sum -= fragnob) {
+ LASSERT(i < niov);
+
+ /* Dang! have to kmap again because I have nowhere to stash the
+ * mapped address. But by doing it while the page is still
+ * mapped, the kernel just bumps the map count and returns me
+ * the address it stashed. */
+ base = kmap(kiov[i].kiov_page) + kiov[i].kiov_offset;
+ fragnob = kiov[i].kiov_len;
+ if (fragnob > sum)
+ fragnob = sum;
+
+ conn->ksnc_rx_csum = ksocknal_csum(conn->ksnc_rx_csum,
+ base, fragnob);
+
+ kunmap(kiov[i].kiov_page);
+ }
+ }
+
+ if (addr != NULL) {
+ ksocknal_lib_kiov_vunmap(addr);
+ } else {
+ for (i = 0; i < niov; i++)
+ kunmap(kiov[i].kiov_page);
+ }
+
+ return rc;
+}
+
+void
+ksocknal_lib_csum_tx(ksock_tx_t *tx)
+{
+ int i;
+ __u32 csum;
+ void *base;
+
+ LASSERT(tx->tx_iov[0].iov_base == &tx->tx_msg);
+ LASSERT(tx->tx_conn != NULL);
+ LASSERT(tx->tx_conn->ksnc_proto == &ksocknal_protocol_v2x);
+
+ tx->tx_msg.ksm_csum = 0;
+
+ csum = ksocknal_csum(~0, tx->tx_iov[0].iov_base,
+ tx->tx_iov[0].iov_len);
+
+ if (tx->tx_kiov != NULL) {
+ for (i = 0; i < tx->tx_nkiov; i++) {
+ base = kmap(tx->tx_kiov[i].kiov_page) +
+ tx->tx_kiov[i].kiov_offset;
+
+ csum = ksocknal_csum(csum, base, tx->tx_kiov[i].kiov_len);
+
+ kunmap(tx->tx_kiov[i].kiov_page);
+ }
+ } else {
+ for (i = 1; i < tx->tx_niov; i++)
+ csum = ksocknal_csum(csum, tx->tx_iov[i].iov_base,
+ tx->tx_iov[i].iov_len);
+ }
+
+ if (*ksocknal_tunables.ksnd_inject_csum_error) {
+ csum++;
+ *ksocknal_tunables.ksnd_inject_csum_error = 0;
+ }
+
+ tx->tx_msg.ksm_csum = csum;
+}
+
+int
+ksocknal_lib_get_conn_tunables(ksock_conn_t *conn, int *txmem, int *rxmem, int *nagle)
+{
+ struct socket *sock = conn->ksnc_sock;
+ int len;
+ int rc;
+
+ rc = ksocknal_connsock_addref(conn);
+ if (rc != 0) {
+ LASSERT(conn->ksnc_closing);
+ *txmem = *rxmem = *nagle = 0;
+ return -ESHUTDOWN;
+ }
+
+ rc = libcfs_sock_getbuf(sock, txmem, rxmem);
+ if (rc == 0) {
+ len = sizeof(*nagle);
+ rc = kernel_getsockopt(sock, SOL_TCP, TCP_NODELAY,
+ (char *)nagle, &len);
+ }
+
+ ksocknal_connsock_decref(conn);
+
+ if (rc == 0)
+ *nagle = !*nagle;
+ else
+ *txmem = *rxmem = *nagle = 0;
+
+ return rc;
+}
+
+int
+ksocknal_lib_setup_sock(struct socket *sock)
+{
+ int rc;
+ int option;
+ int keep_idle;
+ int keep_intvl;
+ int keep_count;
+ int do_keepalive;
+ struct linger linger;
+
+ sock->sk->sk_allocation = GFP_NOFS;
+
+ /* Ensure this socket aborts active sends immediately when we close
+ * it. */
+
+ linger.l_onoff = 0;
+ linger.l_linger = 0;
+
+ rc = kernel_setsockopt(sock, SOL_SOCKET, SO_LINGER,
+ (char *)&linger, sizeof(linger));
+ if (rc != 0) {
+ CERROR("Can't set SO_LINGER: %d\n", rc);
+ return rc;
+ }
+
+ option = -1;
+ rc = kernel_setsockopt(sock, SOL_TCP, TCP_LINGER2,
+ (char *)&option, sizeof(option));
+ if (rc != 0) {
+ CERROR("Can't set SO_LINGER2: %d\n", rc);
+ return rc;
+ }
+
+ if (!*ksocknal_tunables.ksnd_nagle) {
+ option = 1;
+
+ rc = kernel_setsockopt(sock, SOL_TCP, TCP_NODELAY,
+ (char *)&option, sizeof(option));
+ if (rc != 0) {
+ CERROR("Can't disable nagle: %d\n", rc);
+ return rc;
+ }
+ }
+
+ rc = libcfs_sock_setbuf(sock,
+ *ksocknal_tunables.ksnd_tx_buffer_size,
+ *ksocknal_tunables.ksnd_rx_buffer_size);
+ if (rc != 0) {
+ CERROR("Can't set buffer tx %d, rx %d buffers: %d\n",
+ *ksocknal_tunables.ksnd_tx_buffer_size,
+ *ksocknal_tunables.ksnd_rx_buffer_size, rc);
+ return rc;
+ }
+
+/* TCP_BACKOFF_* sockopt tunables unsupported in stock kernels */
+
+ /* snapshot tunables */
+ keep_idle = *ksocknal_tunables.ksnd_keepalive_idle;
+ keep_count = *ksocknal_tunables.ksnd_keepalive_count;
+ keep_intvl = *ksocknal_tunables.ksnd_keepalive_intvl;
+
+ do_keepalive = (keep_idle > 0 && keep_count > 0 && keep_intvl > 0);
+
+ option = (do_keepalive ? 1 : 0);
+ rc = kernel_setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE,
+ (char *)&option, sizeof(option));
+ if (rc != 0) {
+ CERROR("Can't set SO_KEEPALIVE: %d\n", rc);
+ return rc;
+ }
+
+ if (!do_keepalive)
+ return 0;
+
+ rc = kernel_setsockopt(sock, SOL_TCP, TCP_KEEPIDLE,
+ (char *)&keep_idle, sizeof(keep_idle));
+ if (rc != 0) {
+ CERROR("Can't set TCP_KEEPIDLE: %d\n", rc);
+ return rc;
+ }
+
+ rc = kernel_setsockopt(sock, SOL_TCP, TCP_KEEPINTVL,
+ (char *)&keep_intvl, sizeof(keep_intvl));
+ if (rc != 0) {
+ CERROR("Can't set TCP_KEEPINTVL: %d\n", rc);
+ return rc;
+ }
+
+ rc = kernel_setsockopt(sock, SOL_TCP, TCP_KEEPCNT,
+ (char *)&keep_count, sizeof(keep_count));
+ if (rc != 0) {
+ CERROR("Can't set TCP_KEEPCNT: %d\n", rc);
+ return rc;
+ }
+
+ return 0;
+}
+
+void
+ksocknal_lib_push_conn(ksock_conn_t *conn)
+{
+ struct sock *sk;
+ struct tcp_sock *tp;
+ int nonagle;
+ int val = 1;
+ int rc;
+
+ rc = ksocknal_connsock_addref(conn);
+ if (rc != 0) /* being shut down */
+ return;
+
+ sk = conn->ksnc_sock->sk;
+ tp = tcp_sk(sk);
+
+ lock_sock(sk);
+ nonagle = tp->nonagle;
+ tp->nonagle = 1;
+ release_sock(sk);
+
+ rc = kernel_setsockopt(conn->ksnc_sock, SOL_TCP, TCP_NODELAY,
+ (char *)&val, sizeof(val));
+ LASSERT(rc == 0);
+
+ lock_sock(sk);
+ tp->nonagle = nonagle;
+ release_sock(sk);
+
+ ksocknal_connsock_decref(conn);
+}
+
+extern void ksocknal_read_callback(ksock_conn_t *conn);
+extern void ksocknal_write_callback(ksock_conn_t *conn);
+/*
+ * socket call back in Linux
+ */
+static void
+ksocknal_data_ready(struct sock *sk)
+{
+ ksock_conn_t *conn;
+
+ /* interleave correctly with closing sockets... */
+ LASSERT(!in_irq());
+ read_lock(&ksocknal_data.ksnd_global_lock);
+
+ conn = sk->sk_user_data;
+ if (conn == NULL) { /* raced with ksocknal_terminate_conn */
+ LASSERT(sk->sk_data_ready != &ksocknal_data_ready);
+ sk->sk_data_ready(sk);
+ } else
+ ksocknal_read_callback(conn);
+
+ read_unlock(&ksocknal_data.ksnd_global_lock);
+}
+
+static void
+ksocknal_write_space(struct sock *sk)
+{
+ ksock_conn_t *conn;
+ int wspace;
+ int min_wpace;
+
+ /* interleave correctly with closing sockets... */
+ LASSERT(!in_irq());
+ read_lock(&ksocknal_data.ksnd_global_lock);
+
+ conn = sk->sk_user_data;
+ wspace = SOCKNAL_WSPACE(sk);
+ min_wpace = SOCKNAL_MIN_WSPACE(sk);
+
+ CDEBUG(D_NET, "sk %p wspace %d low water %d conn %p%s%s%s\n",
+ sk, wspace, min_wpace, conn,
+ (conn == NULL) ? "" : (conn->ksnc_tx_ready ?
+ " ready" : " blocked"),
+ (conn == NULL) ? "" : (conn->ksnc_tx_scheduled ?
+ " scheduled" : " idle"),
+ (conn == NULL) ? "" : (list_empty(&conn->ksnc_tx_queue) ?
+ " empty" : " queued"));
+
+ if (conn == NULL) { /* raced with ksocknal_terminate_conn */
+ LASSERT(sk->sk_write_space != &ksocknal_write_space);
+ sk->sk_write_space(sk);
+
+ read_unlock(&ksocknal_data.ksnd_global_lock);
+ return;
+ }
+
+ if (wspace >= min_wpace) { /* got enough space */
+ ksocknal_write_callback(conn);
+
+ /* Clear SOCK_NOSPACE _after_ ksocknal_write_callback so the
+ * ENOMEM check in ksocknal_transmit is race-free (think about
+ * it). */
+
+ clear_bit(SOCK_NOSPACE, &sk->sk_socket->flags);
+ }
+
+ read_unlock(&ksocknal_data.ksnd_global_lock);
+}
+
+void
+ksocknal_lib_save_callback(struct socket *sock, ksock_conn_t *conn)
+{
+ conn->ksnc_saved_data_ready = sock->sk->sk_data_ready;
+ conn->ksnc_saved_write_space = sock->sk->sk_write_space;
+}
+
+void
+ksocknal_lib_set_callback(struct socket *sock, ksock_conn_t *conn)
+{
+ sock->sk->sk_user_data = conn;
+ sock->sk->sk_data_ready = ksocknal_data_ready;
+ sock->sk->sk_write_space = ksocknal_write_space;
+ return;
+}
+
+void
+ksocknal_lib_reset_callback(struct socket *sock, ksock_conn_t *conn)
+{
+ /* Remove conn's network callbacks.
+ * NB I _have_ to restore the callback, rather than storing a noop,
+ * since the socket could survive past this module being unloaded!! */
+ sock->sk->sk_data_ready = conn->ksnc_saved_data_ready;
+ sock->sk->sk_write_space = conn->ksnc_saved_write_space;
+
+ /* A callback could be in progress already; they hold a read lock
+ * on ksnd_global_lock (to serialise with me) and NOOP if
+ * sk_user_data is NULL. */
+ sock->sk->sk_user_data = NULL;
+
+ return ;
+}
+
+int
+ksocknal_lib_memory_pressure(ksock_conn_t *conn)
+{
+ int rc = 0;
+ ksock_sched_t *sched;
+
+ sched = conn->ksnc_scheduler;
+ spin_lock_bh(&sched->kss_lock);
+
+ if (!test_bit(SOCK_NOSPACE, &conn->ksnc_sock->flags) &&
+ !conn->ksnc_tx_ready) {
+ /* SOCK_NOSPACE is set when the socket fills
+ * and cleared in the write_space callback
+ * (which also sets ksnc_tx_ready). If
+ * SOCK_NOSPACE and ksnc_tx_ready are BOTH
+ * zero, I didn't fill the socket and
+ * write_space won't reschedule me, so I
+ * return -ENOMEM to get my caller to retry
+ * after a timeout */
+ rc = -ENOMEM;
+ }
+
+ spin_unlock_bh(&sched->kss_lock);
+
+ return rc;
+}
diff --git a/drivers/staging/lustre/lnet/klnds/socklnd/socklnd_lib-linux.h b/drivers/staging/lustre/lnet/klnds/socklnd/socklnd_lib-linux.h
new file mode 100644
index 000000000..f5563881b
--- /dev/null
+++ b/drivers/staging/lustre/lnet/klnds/socklnd/socklnd_lib-linux.h
@@ -0,0 +1,86 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2012, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ */
+
+#define DEBUG_PORTAL_ALLOC
+
+#ifndef __LINUX_SOCKNAL_LIB_H__
+#define __LINUX_SOCKNAL_LIB_H__
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/string.h>
+#include <linux/stat.h>
+#include <linux/errno.h>
+#include <linux/unistd.h>
+#include <net/sock.h>
+#include <net/tcp.h>
+#include <linux/uio.h>
+#include <linux/if.h>
+#include <linux/uaccess.h>
+
+#include <asm/irq.h>
+
+#include <linux/fs.h>
+#include <linux/file.h>
+#include <linux/list.h>
+#include <linux/kmod.h>
+#include <linux/sysctl.h>
+#include <asm/div64.h>
+#include <linux/syscalls.h>
+
+#include "../../../include/linux/libcfs/libcfs.h"
+
+#include <linux/crc32.h>
+static inline __u32 ksocknal_csum(__u32 crc, unsigned char const *p, size_t len)
+{
+#if 1
+ return crc32_le(crc, p, len);
+#else
+ while (len-- > 0)
+ crc = ((crc + 0x100) & ~0xff) | ((crc + *p++) & 0xff) ;
+ return crc;
+#endif
+}
+
+#define SOCKNAL_WSPACE(sk) sk_stream_wspace(sk)
+#define SOCKNAL_MIN_WSPACE(sk) sk_stream_min_wspace(sk)
+
+/* assume one thread for each connection type */
+#define SOCKNAL_NSCHEDS 3
+#define SOCKNAL_NSCHEDS_HIGH (SOCKNAL_NSCHEDS << 1)
+
+#endif
diff --git a/drivers/staging/lustre/lnet/klnds/socklnd/socklnd_modparams.c b/drivers/staging/lustre/lnet/klnds/socklnd/socklnd_modparams.c
new file mode 100644
index 000000000..86b88db1c
--- /dev/null
+++ b/drivers/staging/lustre/lnet/klnds/socklnd/socklnd_modparams.c
@@ -0,0 +1,188 @@
+/*
+ * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
+ *
+ * Copyright (c) 2011, 2012, Intel Corporation.
+ *
+ * Author: Eric Barton <eric@bartonsoftware.com>
+ *
+ * Portals is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * Portals 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 Portals; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "socklnd.h"
+
+static int sock_timeout = 50;
+module_param(sock_timeout, int, 0644);
+MODULE_PARM_DESC(sock_timeout, "dead socket timeout (seconds)");
+
+static int credits = 256;
+module_param(credits, int, 0444);
+MODULE_PARM_DESC(credits, "# concurrent sends");
+
+static int peer_credits = 8;
+module_param(peer_credits, int, 0444);
+MODULE_PARM_DESC(peer_credits, "# concurrent sends to 1 peer");
+
+static int peer_buffer_credits;
+module_param(peer_buffer_credits, int, 0444);
+MODULE_PARM_DESC(peer_buffer_credits, "# per-peer router buffer credits");
+
+static int peer_timeout = 180;
+module_param(peer_timeout, int, 0444);
+MODULE_PARM_DESC(peer_timeout, "Seconds without aliveness news to declare peer dead (<=0 to disable)");
+
+/* Number of daemons in each thread pool which is percpt,
+ * we will estimate reasonable value based on CPUs if it's not set. */
+static unsigned int nscheds;
+module_param(nscheds, int, 0444);
+MODULE_PARM_DESC(nscheds, "# scheduler daemons in each pool while starting");
+
+static int nconnds = 4;
+module_param(nconnds, int, 0444);
+MODULE_PARM_DESC(nconnds, "# connection daemons while starting");
+
+static int nconnds_max = 64;
+module_param(nconnds_max, int, 0444);
+MODULE_PARM_DESC(nconnds_max, "max # connection daemons");
+
+static int min_reconnectms = 1000;
+module_param(min_reconnectms, int, 0644);
+MODULE_PARM_DESC(min_reconnectms, "min connection retry interval (mS)");
+
+static int max_reconnectms = 60000;
+module_param(max_reconnectms, int, 0644);
+MODULE_PARM_DESC(max_reconnectms, "max connection retry interval (mS)");
+
+# define DEFAULT_EAGER_ACK 0
+static int eager_ack = DEFAULT_EAGER_ACK;
+module_param(eager_ack, int, 0644);
+MODULE_PARM_DESC(eager_ack, "send tcp ack packets eagerly");
+
+static int typed_conns = 1;
+module_param(typed_conns, int, 0444);
+MODULE_PARM_DESC(typed_conns, "use different sockets for bulk");
+
+static int min_bulk = 1<<10;
+module_param(min_bulk, int, 0644);
+MODULE_PARM_DESC(min_bulk, "smallest 'large' message");
+
+# define DEFAULT_BUFFER_SIZE 0
+static int tx_buffer_size = DEFAULT_BUFFER_SIZE;
+module_param(tx_buffer_size, int, 0644);
+MODULE_PARM_DESC(tx_buffer_size, "socket tx buffer size (0 for system default)");
+
+static int rx_buffer_size = DEFAULT_BUFFER_SIZE;
+module_param(rx_buffer_size, int, 0644);
+MODULE_PARM_DESC(rx_buffer_size, "socket rx buffer size (0 for system default)");
+
+static int nagle;
+module_param(nagle, int, 0644);
+MODULE_PARM_DESC(nagle, "enable NAGLE?");
+
+static int round_robin = 1;
+module_param(round_robin, int, 0644);
+MODULE_PARM_DESC(round_robin, "Round robin for multiple interfaces");
+
+static int keepalive = 30;
+module_param(keepalive, int, 0644);
+MODULE_PARM_DESC(keepalive, "# seconds before send keepalive");
+
+static int keepalive_idle = 30;
+module_param(keepalive_idle, int, 0644);
+MODULE_PARM_DESC(keepalive_idle, "# idle seconds before probe");
+
+#define DEFAULT_KEEPALIVE_COUNT 5
+static int keepalive_count = DEFAULT_KEEPALIVE_COUNT;
+module_param(keepalive_count, int, 0644);
+MODULE_PARM_DESC(keepalive_count, "# missed probes == dead");
+
+static int keepalive_intvl = 5;
+module_param(keepalive_intvl, int, 0644);
+MODULE_PARM_DESC(keepalive_intvl, "seconds between probes");
+
+static int enable_csum;
+module_param(enable_csum, int, 0644);
+MODULE_PARM_DESC(enable_csum, "enable check sum");
+
+static int inject_csum_error;
+module_param(inject_csum_error, int, 0644);
+MODULE_PARM_DESC(inject_csum_error, "set non-zero to inject a checksum error");
+
+static int nonblk_zcack = 1;
+module_param(nonblk_zcack, int, 0644);
+MODULE_PARM_DESC(nonblk_zcack, "always send ZC-ACK on non-blocking connection");
+
+static unsigned int zc_min_payload = 16 << 10;
+module_param(zc_min_payload, int, 0644);
+MODULE_PARM_DESC(zc_min_payload, "minimum payload size to zero copy");
+
+static unsigned int zc_recv;
+module_param(zc_recv, int, 0644);
+MODULE_PARM_DESC(zc_recv, "enable ZC recv for Chelsio driver");
+
+static unsigned int zc_recv_min_nfrags = 16;
+module_param(zc_recv_min_nfrags, int, 0644);
+MODULE_PARM_DESC(zc_recv_min_nfrags, "minimum # of fragments to enable ZC recv");
+
+
+#if SOCKNAL_VERSION_DEBUG
+static int protocol = 3;
+module_param(protocol, int, 0644);
+MODULE_PARM_DESC(protocol, "protocol version");
+#endif
+
+ksock_tunables_t ksocknal_tunables;
+
+int ksocknal_tunables_init(void)
+{
+
+ /* initialize ksocknal_tunables structure */
+ ksocknal_tunables.ksnd_timeout = &sock_timeout;
+ ksocknal_tunables.ksnd_nscheds = &nscheds;
+ ksocknal_tunables.ksnd_nconnds = &nconnds;
+ ksocknal_tunables.ksnd_nconnds_max = &nconnds_max;
+ ksocknal_tunables.ksnd_min_reconnectms = &min_reconnectms;
+ ksocknal_tunables.ksnd_max_reconnectms = &max_reconnectms;
+ ksocknal_tunables.ksnd_eager_ack = &eager_ack;
+ ksocknal_tunables.ksnd_typed_conns = &typed_conns;
+ ksocknal_tunables.ksnd_min_bulk = &min_bulk;
+ ksocknal_tunables.ksnd_tx_buffer_size = &tx_buffer_size;
+ ksocknal_tunables.ksnd_rx_buffer_size = &rx_buffer_size;
+ ksocknal_tunables.ksnd_nagle = &nagle;
+ ksocknal_tunables.ksnd_round_robin = &round_robin;
+ ksocknal_tunables.ksnd_keepalive = &keepalive;
+ ksocknal_tunables.ksnd_keepalive_idle = &keepalive_idle;
+ ksocknal_tunables.ksnd_keepalive_count = &keepalive_count;
+ ksocknal_tunables.ksnd_keepalive_intvl = &keepalive_intvl;
+ ksocknal_tunables.ksnd_credits = &credits;
+ ksocknal_tunables.ksnd_peertxcredits = &peer_credits;
+ ksocknal_tunables.ksnd_peerrtrcredits = &peer_buffer_credits;
+ ksocknal_tunables.ksnd_peertimeout = &peer_timeout;
+ ksocknal_tunables.ksnd_enable_csum = &enable_csum;
+ ksocknal_tunables.ksnd_inject_csum_error = &inject_csum_error;
+ ksocknal_tunables.ksnd_nonblk_zcack = &nonblk_zcack;
+ ksocknal_tunables.ksnd_zc_min_payload = &zc_min_payload;
+ ksocknal_tunables.ksnd_zc_recv = &zc_recv;
+ ksocknal_tunables.ksnd_zc_recv_min_nfrags = &zc_recv_min_nfrags;
+
+
+
+#if SOCKNAL_VERSION_DEBUG
+ ksocknal_tunables.ksnd_protocol = &protocol;
+#endif
+
+ if (*ksocknal_tunables.ksnd_zc_min_payload < (2 << 10))
+ *ksocknal_tunables.ksnd_zc_min_payload = 2 << 10;
+
+ return 0;
+};
diff --git a/drivers/staging/lustre/lnet/klnds/socklnd/socklnd_proto.c b/drivers/staging/lustre/lnet/klnds/socklnd/socklnd_proto.c
new file mode 100644
index 000000000..8596581f5
--- /dev/null
+++ b/drivers/staging/lustre/lnet/klnds/socklnd/socklnd_proto.c
@@ -0,0 +1,797 @@
+/*
+ * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
+ *
+ * Copyright (c) 2012, Intel Corporation.
+ *
+ * Author: Zach Brown <zab@zabbo.net>
+ * Author: Peter J. Braam <braam@clusterfs.com>
+ * Author: Phil Schwan <phil@clusterfs.com>
+ * Author: Eric Barton <eric@bartonsoftware.com>
+ *
+ * This file is part of Portals, http://www.sf.net/projects/sandiaportals/
+ *
+ * Portals is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * Portals 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 Portals; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "socklnd.h"
+
+/*
+ * Protocol entries :
+ * pro_send_hello : send hello message
+ * pro_recv_hello : receive hello message
+ * pro_pack : pack message header
+ * pro_unpack : unpack message header
+ * pro_queue_tx_zcack() : Called holding BH lock: kss_lock
+ * return 1 if ACK is piggybacked, otherwise return 0
+ * pro_queue_tx_msg() : Called holding BH lock: kss_lock
+ * return the ACK that piggybacked by my message, or NULL
+ * pro_handle_zcreq() : handler of incoming ZC-REQ
+ * pro_handle_zcack() : handler of incoming ZC-ACK
+ * pro_match_tx() : Called holding glock
+ */
+
+static ksock_tx_t *
+ksocknal_queue_tx_msg_v1(ksock_conn_t *conn, ksock_tx_t *tx_msg)
+{
+ /* V1.x, just enqueue it */
+ list_add_tail(&tx_msg->tx_list, &conn->ksnc_tx_queue);
+ return NULL;
+}
+
+void
+ksocknal_next_tx_carrier(ksock_conn_t *conn)
+{
+ ksock_tx_t *tx = conn->ksnc_tx_carrier;
+
+ /* Called holding BH lock: conn->ksnc_scheduler->kss_lock */
+ LASSERT(!list_empty(&conn->ksnc_tx_queue));
+ LASSERT(tx != NULL);
+
+ /* Next TX that can carry ZC-ACK or LNet message */
+ if (tx->tx_list.next == &conn->ksnc_tx_queue) {
+ /* no more packets queued */
+ conn->ksnc_tx_carrier = NULL;
+ } else {
+ conn->ksnc_tx_carrier = list_entry(tx->tx_list.next,
+ ksock_tx_t, tx_list);
+ LASSERT(conn->ksnc_tx_carrier->tx_msg.ksm_type == tx->tx_msg.ksm_type);
+ }
+}
+
+static int
+ksocknal_queue_tx_zcack_v2(ksock_conn_t *conn,
+ ksock_tx_t *tx_ack, __u64 cookie)
+{
+ ksock_tx_t *tx = conn->ksnc_tx_carrier;
+
+ LASSERT(tx_ack == NULL ||
+ tx_ack->tx_msg.ksm_type == KSOCK_MSG_NOOP);
+
+ /*
+ * Enqueue or piggyback tx_ack / cookie
+ * . no tx can piggyback cookie of tx_ack (or cookie), just
+ * enqueue the tx_ack (if tx_ack != NUL) and return NULL.
+ * . There is tx can piggyback cookie of tx_ack (or cookie),
+ * piggyback the cookie and return the tx.
+ */
+ if (tx == NULL) {
+ if (tx_ack != NULL) {
+ list_add_tail(&tx_ack->tx_list,
+ &conn->ksnc_tx_queue);
+ conn->ksnc_tx_carrier = tx_ack;
+ }
+ return 0;
+ }
+
+ if (tx->tx_msg.ksm_type == KSOCK_MSG_NOOP) {
+ /* tx is noop zc-ack, can't piggyback zc-ack cookie */
+ if (tx_ack != NULL)
+ list_add_tail(&tx_ack->tx_list,
+ &conn->ksnc_tx_queue);
+ return 0;
+ }
+
+ LASSERT(tx->tx_msg.ksm_type == KSOCK_MSG_LNET);
+ LASSERT(tx->tx_msg.ksm_zc_cookies[1] == 0);
+
+ if (tx_ack != NULL)
+ cookie = tx_ack->tx_msg.ksm_zc_cookies[1];
+
+ /* piggyback the zc-ack cookie */
+ tx->tx_msg.ksm_zc_cookies[1] = cookie;
+ /* move on to the next TX which can carry cookie */
+ ksocknal_next_tx_carrier(conn);
+
+ return 1;
+}
+
+static ksock_tx_t *
+ksocknal_queue_tx_msg_v2(ksock_conn_t *conn, ksock_tx_t *tx_msg)
+{
+ ksock_tx_t *tx = conn->ksnc_tx_carrier;
+
+ /*
+ * Enqueue tx_msg:
+ * . If there is no NOOP on the connection, just enqueue
+ * tx_msg and return NULL
+ * . If there is NOOP on the connection, piggyback the cookie
+ * and replace the NOOP tx, and return the NOOP tx.
+ */
+ if (tx == NULL) { /* nothing on queue */
+ list_add_tail(&tx_msg->tx_list, &conn->ksnc_tx_queue);
+ conn->ksnc_tx_carrier = tx_msg;
+ return NULL;
+ }
+
+ if (tx->tx_msg.ksm_type == KSOCK_MSG_LNET) { /* nothing to carry */
+ list_add_tail(&tx_msg->tx_list, &conn->ksnc_tx_queue);
+ return NULL;
+ }
+
+ LASSERT(tx->tx_msg.ksm_type == KSOCK_MSG_NOOP);
+
+ /* There is a noop zc-ack can be piggybacked */
+ tx_msg->tx_msg.ksm_zc_cookies[1] = tx->tx_msg.ksm_zc_cookies[1];
+ ksocknal_next_tx_carrier(conn);
+
+ /* use new_tx to replace the noop zc-ack packet */
+ list_add(&tx_msg->tx_list, &tx->tx_list);
+ list_del(&tx->tx_list);
+
+ return tx;
+}
+
+static int
+ksocknal_queue_tx_zcack_v3(ksock_conn_t *conn,
+ ksock_tx_t *tx_ack, __u64 cookie)
+{
+ ksock_tx_t *tx;
+
+ if (conn->ksnc_type != SOCKLND_CONN_ACK)
+ return ksocknal_queue_tx_zcack_v2(conn, tx_ack, cookie);
+
+ /* non-blocking ZC-ACK (to router) */
+ LASSERT(tx_ack == NULL ||
+ tx_ack->tx_msg.ksm_type == KSOCK_MSG_NOOP);
+
+ tx = conn->ksnc_tx_carrier;
+ if (tx == NULL) {
+ if (tx_ack != NULL) {
+ list_add_tail(&tx_ack->tx_list,
+ &conn->ksnc_tx_queue);
+ conn->ksnc_tx_carrier = tx_ack;
+ }
+ return 0;
+ }
+
+ /* conn->ksnc_tx_carrier != NULL */
+
+ if (tx_ack != NULL)
+ cookie = tx_ack->tx_msg.ksm_zc_cookies[1];
+
+ if (cookie == SOCKNAL_KEEPALIVE_PING) /* ignore keepalive PING */
+ return 1;
+
+ if (tx->tx_msg.ksm_zc_cookies[1] == SOCKNAL_KEEPALIVE_PING) {
+ /* replace the keepalive PING with a real ACK */
+ LASSERT(tx->tx_msg.ksm_zc_cookies[0] == 0);
+ tx->tx_msg.ksm_zc_cookies[1] = cookie;
+ return 1;
+ }
+
+ if (cookie == tx->tx_msg.ksm_zc_cookies[0] ||
+ cookie == tx->tx_msg.ksm_zc_cookies[1]) {
+ CWARN("%s: duplicated ZC cookie: %llu\n",
+ libcfs_id2str(conn->ksnc_peer->ksnp_id), cookie);
+ return 1; /* XXX return error in the future */
+ }
+
+ if (tx->tx_msg.ksm_zc_cookies[0] == 0) {
+ /* NOOP tx has only one ZC-ACK cookie, can carry at least one more */
+ if (tx->tx_msg.ksm_zc_cookies[1] > cookie) {
+ tx->tx_msg.ksm_zc_cookies[0] = tx->tx_msg.ksm_zc_cookies[1];
+ tx->tx_msg.ksm_zc_cookies[1] = cookie;
+ } else {
+ tx->tx_msg.ksm_zc_cookies[0] = cookie;
+ }
+
+ if (tx->tx_msg.ksm_zc_cookies[0] - tx->tx_msg.ksm_zc_cookies[1] > 2) {
+ /* not likely to carry more ACKs, skip it to simplify logic */
+ ksocknal_next_tx_carrier(conn);
+ }
+
+ return 1;
+ }
+
+ /* takes two or more cookies already */
+
+ if (tx->tx_msg.ksm_zc_cookies[0] > tx->tx_msg.ksm_zc_cookies[1]) {
+ __u64 tmp = 0;
+
+ /* two separated cookies: (a+2, a) or (a+1, a) */
+ LASSERT(tx->tx_msg.ksm_zc_cookies[0] -
+ tx->tx_msg.ksm_zc_cookies[1] <= 2);
+
+ if (tx->tx_msg.ksm_zc_cookies[0] -
+ tx->tx_msg.ksm_zc_cookies[1] == 2) {
+ if (cookie == tx->tx_msg.ksm_zc_cookies[1] + 1)
+ tmp = cookie;
+ } else if (cookie == tx->tx_msg.ksm_zc_cookies[1] - 1) {
+ tmp = tx->tx_msg.ksm_zc_cookies[1];
+ } else if (cookie == tx->tx_msg.ksm_zc_cookies[0] + 1) {
+ tmp = tx->tx_msg.ksm_zc_cookies[0];
+ }
+
+ if (tmp != 0) {
+ /* range of cookies */
+ tx->tx_msg.ksm_zc_cookies[0] = tmp - 1;
+ tx->tx_msg.ksm_zc_cookies[1] = tmp + 1;
+ return 1;
+ }
+
+ } else {
+ /* ksm_zc_cookies[0] < ksm_zc_cookies[1], it is range of cookies */
+ if (cookie >= tx->tx_msg.ksm_zc_cookies[0] &&
+ cookie <= tx->tx_msg.ksm_zc_cookies[1]) {
+ CWARN("%s: duplicated ZC cookie: %llu\n",
+ libcfs_id2str(conn->ksnc_peer->ksnp_id), cookie);
+ return 1; /* XXX: return error in the future */
+ }
+
+ if (cookie == tx->tx_msg.ksm_zc_cookies[1] + 1) {
+ tx->tx_msg.ksm_zc_cookies[1] = cookie;
+ return 1;
+ }
+
+ if (cookie == tx->tx_msg.ksm_zc_cookies[0] - 1) {
+ tx->tx_msg.ksm_zc_cookies[0] = cookie;
+ return 1;
+ }
+ }
+
+ /* failed to piggyback ZC-ACK */
+ if (tx_ack != NULL) {
+ list_add_tail(&tx_ack->tx_list, &conn->ksnc_tx_queue);
+ /* the next tx can piggyback at least 1 ACK */
+ ksocknal_next_tx_carrier(conn);
+ }
+
+ return 0;
+}
+
+static int
+ksocknal_match_tx(ksock_conn_t *conn, ksock_tx_t *tx, int nonblk)
+{
+ int nob;
+
+#if SOCKNAL_VERSION_DEBUG
+ if (!*ksocknal_tunables.ksnd_typed_conns)
+ return SOCKNAL_MATCH_YES;
+#endif
+
+ if (tx == NULL || tx->tx_lnetmsg == NULL) {
+ /* noop packet */
+ nob = offsetof(ksock_msg_t, ksm_u);
+ } else {
+ nob = tx->tx_lnetmsg->msg_len +
+ ((conn->ksnc_proto == &ksocknal_protocol_v1x) ?
+ sizeof(lnet_hdr_t) : sizeof(ksock_msg_t));
+ }
+
+ /* default checking for typed connection */
+ switch (conn->ksnc_type) {
+ default:
+ CERROR("ksnc_type bad: %u\n", conn->ksnc_type);
+ LBUG();
+ case SOCKLND_CONN_ANY:
+ return SOCKNAL_MATCH_YES;
+
+ case SOCKLND_CONN_BULK_IN:
+ return SOCKNAL_MATCH_MAY;
+
+ case SOCKLND_CONN_BULK_OUT:
+ if (nob < *ksocknal_tunables.ksnd_min_bulk)
+ return SOCKNAL_MATCH_MAY;
+ else
+ return SOCKNAL_MATCH_YES;
+
+ case SOCKLND_CONN_CONTROL:
+ if (nob >= *ksocknal_tunables.ksnd_min_bulk)
+ return SOCKNAL_MATCH_MAY;
+ else
+ return SOCKNAL_MATCH_YES;
+ }
+}
+
+static int
+ksocknal_match_tx_v3(ksock_conn_t *conn, ksock_tx_t *tx, int nonblk)
+{
+ int nob;
+
+ if (tx == NULL || tx->tx_lnetmsg == NULL)
+ nob = offsetof(ksock_msg_t, ksm_u);
+ else
+ nob = tx->tx_lnetmsg->msg_len + sizeof(ksock_msg_t);
+
+ switch (conn->ksnc_type) {
+ default:
+ CERROR("ksnc_type bad: %u\n", conn->ksnc_type);
+ LBUG();
+ case SOCKLND_CONN_ANY:
+ return SOCKNAL_MATCH_NO;
+
+ case SOCKLND_CONN_ACK:
+ if (nonblk)
+ return SOCKNAL_MATCH_YES;
+ else if (tx == NULL || tx->tx_lnetmsg == NULL)
+ return SOCKNAL_MATCH_MAY;
+ else
+ return SOCKNAL_MATCH_NO;
+
+ case SOCKLND_CONN_BULK_OUT:
+ if (nonblk)
+ return SOCKNAL_MATCH_NO;
+ else if (nob < *ksocknal_tunables.ksnd_min_bulk)
+ return SOCKNAL_MATCH_MAY;
+ else
+ return SOCKNAL_MATCH_YES;
+
+ case SOCKLND_CONN_CONTROL:
+ if (nonblk)
+ return SOCKNAL_MATCH_NO;
+ else if (nob >= *ksocknal_tunables.ksnd_min_bulk)
+ return SOCKNAL_MATCH_MAY;
+ else
+ return SOCKNAL_MATCH_YES;
+ }
+}
+
+/* (Sink) handle incoming ZC request from sender */
+static int
+ksocknal_handle_zcreq(ksock_conn_t *c, __u64 cookie, int remote)
+{
+ ksock_peer_t *peer = c->ksnc_peer;
+ ksock_conn_t *conn;
+ ksock_tx_t *tx;
+ int rc;
+
+ read_lock(&ksocknal_data.ksnd_global_lock);
+
+ conn = ksocknal_find_conn_locked(peer, NULL, !!remote);
+ if (conn != NULL) {
+ ksock_sched_t *sched = conn->ksnc_scheduler;
+
+ LASSERT(conn->ksnc_proto->pro_queue_tx_zcack != NULL);
+
+ spin_lock_bh(&sched->kss_lock);
+
+ rc = conn->ksnc_proto->pro_queue_tx_zcack(conn, NULL, cookie);
+
+ spin_unlock_bh(&sched->kss_lock);
+
+ if (rc) { /* piggybacked */
+ read_unlock(&ksocknal_data.ksnd_global_lock);
+ return 0;
+ }
+ }
+
+ read_unlock(&ksocknal_data.ksnd_global_lock);
+
+ /* ACK connection is not ready, or can't piggyback the ACK */
+ tx = ksocknal_alloc_tx_noop(cookie, !!remote);
+ if (tx == NULL)
+ return -ENOMEM;
+
+ rc = ksocknal_launch_packet(peer->ksnp_ni, tx, peer->ksnp_id);
+ if (rc == 0)
+ return 0;
+
+ ksocknal_free_tx(tx);
+ return rc;
+}
+
+/* (Sender) handle ZC_ACK from sink */
+static int
+ksocknal_handle_zcack(ksock_conn_t *conn, __u64 cookie1, __u64 cookie2)
+{
+ ksock_peer_t *peer = conn->ksnc_peer;
+ ksock_tx_t *tx;
+ ksock_tx_t *tmp;
+ LIST_HEAD(zlist);
+ int count;
+
+ if (cookie1 == 0)
+ cookie1 = cookie2;
+
+ count = (cookie1 > cookie2) ? 2 : (cookie2 - cookie1 + 1);
+
+ if (cookie2 == SOCKNAL_KEEPALIVE_PING &&
+ conn->ksnc_proto == &ksocknal_protocol_v3x) {
+ /* keepalive PING for V3.x, just ignore it */
+ return count == 1 ? 0 : -EPROTO;
+ }
+
+ spin_lock(&peer->ksnp_lock);
+
+ list_for_each_entry_safe(tx, tmp,
+ &peer->ksnp_zc_req_list, tx_zc_list) {
+ __u64 c = tx->tx_msg.ksm_zc_cookies[0];
+
+ if (c == cookie1 || c == cookie2 || (cookie1 < c && c < cookie2)) {
+ tx->tx_msg.ksm_zc_cookies[0] = 0;
+ list_del(&tx->tx_zc_list);
+ list_add(&tx->tx_zc_list, &zlist);
+
+ if (--count == 0)
+ break;
+ }
+ }
+
+ spin_unlock(&peer->ksnp_lock);
+
+ while (!list_empty(&zlist)) {
+ tx = list_entry(zlist.next, ksock_tx_t, tx_zc_list);
+ list_del(&tx->tx_zc_list);
+ ksocknal_tx_decref(tx);
+ }
+
+ return count == 0 ? 0 : -EPROTO;
+}
+
+static int
+ksocknal_send_hello_v1(ksock_conn_t *conn, ksock_hello_msg_t *hello)
+{
+ struct socket *sock = conn->ksnc_sock;
+ lnet_hdr_t *hdr;
+ lnet_magicversion_t *hmv;
+ int rc;
+ int i;
+
+ CLASSERT(sizeof(lnet_magicversion_t) == offsetof(lnet_hdr_t, src_nid));
+
+ LIBCFS_ALLOC(hdr, sizeof(*hdr));
+ if (hdr == NULL) {
+ CERROR("Can't allocate lnet_hdr_t\n");
+ return -ENOMEM;
+ }
+
+ hmv = (lnet_magicversion_t *)&hdr->dest_nid;
+
+ /* Re-organize V2.x message header to V1.x (lnet_hdr_t)
+ * header and send out */
+ hmv->magic = cpu_to_le32 (LNET_PROTO_TCP_MAGIC);
+ hmv->version_major = cpu_to_le16 (KSOCK_PROTO_V1_MAJOR);
+ hmv->version_minor = cpu_to_le16 (KSOCK_PROTO_V1_MINOR);
+
+ if (the_lnet.ln_testprotocompat != 0) {
+ /* single-shot proto check */
+ LNET_LOCK();
+ if ((the_lnet.ln_testprotocompat & 1) != 0) {
+ hmv->version_major++; /* just different! */
+ the_lnet.ln_testprotocompat &= ~1;
+ }
+ if ((the_lnet.ln_testprotocompat & 2) != 0) {
+ hmv->magic = LNET_PROTO_MAGIC;
+ the_lnet.ln_testprotocompat &= ~2;
+ }
+ LNET_UNLOCK();
+ }
+
+ hdr->src_nid = cpu_to_le64 (hello->kshm_src_nid);
+ hdr->src_pid = cpu_to_le32 (hello->kshm_src_pid);
+ hdr->type = cpu_to_le32 (LNET_MSG_HELLO);
+ hdr->payload_length = cpu_to_le32 (hello->kshm_nips * sizeof(__u32));
+ hdr->msg.hello.type = cpu_to_le32 (hello->kshm_ctype);
+ hdr->msg.hello.incarnation = cpu_to_le64 (hello->kshm_src_incarnation);
+
+ rc = libcfs_sock_write(sock, hdr, sizeof(*hdr),
+ lnet_acceptor_timeout());
+
+ if (rc != 0) {
+ CNETERR("Error %d sending HELLO hdr to %pI4h/%d\n",
+ rc, &conn->ksnc_ipaddr, conn->ksnc_port);
+ goto out;
+ }
+
+ if (hello->kshm_nips == 0)
+ goto out;
+
+ for (i = 0; i < (int) hello->kshm_nips; i++) {
+ hello->kshm_ips[i] = __cpu_to_le32 (hello->kshm_ips[i]);
+ }
+
+ rc = libcfs_sock_write(sock, hello->kshm_ips,
+ hello->kshm_nips * sizeof(__u32),
+ lnet_acceptor_timeout());
+ if (rc != 0) {
+ CNETERR("Error %d sending HELLO payload (%d) to %pI4h/%d\n",
+ rc, hello->kshm_nips,
+ &conn->ksnc_ipaddr, conn->ksnc_port);
+ }
+out:
+ LIBCFS_FREE(hdr, sizeof(*hdr));
+
+ return rc;
+}
+
+static int
+ksocknal_send_hello_v2(ksock_conn_t *conn, ksock_hello_msg_t *hello)
+{
+ struct socket *sock = conn->ksnc_sock;
+ int rc;
+
+ hello->kshm_magic = LNET_PROTO_MAGIC;
+ hello->kshm_version = conn->ksnc_proto->pro_version;
+
+ if (the_lnet.ln_testprotocompat != 0) {
+ /* single-shot proto check */
+ LNET_LOCK();
+ if ((the_lnet.ln_testprotocompat & 1) != 0) {
+ hello->kshm_version++; /* just different! */
+ the_lnet.ln_testprotocompat &= ~1;
+ }
+ LNET_UNLOCK();
+ }
+
+ rc = libcfs_sock_write(sock, hello, offsetof(ksock_hello_msg_t, kshm_ips),
+ lnet_acceptor_timeout());
+
+ if (rc != 0) {
+ CNETERR("Error %d sending HELLO hdr to %pI4h/%d\n",
+ rc, &conn->ksnc_ipaddr, conn->ksnc_port);
+ return rc;
+ }
+
+ if (hello->kshm_nips == 0)
+ return 0;
+
+ rc = libcfs_sock_write(sock, hello->kshm_ips,
+ hello->kshm_nips * sizeof(__u32),
+ lnet_acceptor_timeout());
+ if (rc != 0) {
+ CNETERR("Error %d sending HELLO payload (%d) to %pI4h/%d\n",
+ rc, hello->kshm_nips,
+ &conn->ksnc_ipaddr, conn->ksnc_port);
+ }
+
+ return rc;
+}
+
+static int
+ksocknal_recv_hello_v1(ksock_conn_t *conn, ksock_hello_msg_t *hello,
+ int timeout)
+{
+ struct socket *sock = conn->ksnc_sock;
+ lnet_hdr_t *hdr;
+ int rc;
+ int i;
+
+ LIBCFS_ALLOC(hdr, sizeof(*hdr));
+ if (hdr == NULL) {
+ CERROR("Can't allocate lnet_hdr_t\n");
+ return -ENOMEM;
+ }
+
+ rc = libcfs_sock_read(sock, &hdr->src_nid,
+ sizeof(*hdr) - offsetof(lnet_hdr_t, src_nid),
+ timeout);
+ if (rc != 0) {
+ CERROR("Error %d reading rest of HELLO hdr from %pI4h\n",
+ rc, &conn->ksnc_ipaddr);
+ LASSERT(rc < 0 && rc != -EALREADY);
+ goto out;
+ }
+
+ /* ...and check we got what we expected */
+ if (hdr->type != cpu_to_le32 (LNET_MSG_HELLO)) {
+ CERROR("Expecting a HELLO hdr, but got type %d from %pI4h\n",
+ le32_to_cpu(hdr->type),
+ &conn->ksnc_ipaddr);
+ rc = -EPROTO;
+ goto out;
+ }
+
+ hello->kshm_src_nid = le64_to_cpu(hdr->src_nid);
+ hello->kshm_src_pid = le32_to_cpu(hdr->src_pid);
+ hello->kshm_src_incarnation = le64_to_cpu(hdr->msg.hello.incarnation);
+ hello->kshm_ctype = le32_to_cpu(hdr->msg.hello.type);
+ hello->kshm_nips = le32_to_cpu(hdr->payload_length) /
+ sizeof(__u32);
+
+ if (hello->kshm_nips > LNET_MAX_INTERFACES) {
+ CERROR("Bad nips %d from ip %pI4h\n",
+ hello->kshm_nips, &conn->ksnc_ipaddr);
+ rc = -EPROTO;
+ goto out;
+ }
+
+ if (hello->kshm_nips == 0)
+ goto out;
+
+ rc = libcfs_sock_read(sock, hello->kshm_ips,
+ hello->kshm_nips * sizeof(__u32), timeout);
+ if (rc != 0) {
+ CERROR("Error %d reading IPs from ip %pI4h\n",
+ rc, &conn->ksnc_ipaddr);
+ LASSERT(rc < 0 && rc != -EALREADY);
+ goto out;
+ }
+
+ for (i = 0; i < (int) hello->kshm_nips; i++) {
+ hello->kshm_ips[i] = __le32_to_cpu(hello->kshm_ips[i]);
+
+ if (hello->kshm_ips[i] == 0) {
+ CERROR("Zero IP[%d] from ip %pI4h\n",
+ i, &conn->ksnc_ipaddr);
+ rc = -EPROTO;
+ break;
+ }
+ }
+out:
+ LIBCFS_FREE(hdr, sizeof(*hdr));
+
+ return rc;
+}
+
+static int
+ksocknal_recv_hello_v2(ksock_conn_t *conn, ksock_hello_msg_t *hello, int timeout)
+{
+ struct socket *sock = conn->ksnc_sock;
+ int rc;
+ int i;
+
+ if (hello->kshm_magic == LNET_PROTO_MAGIC)
+ conn->ksnc_flip = 0;
+ else
+ conn->ksnc_flip = 1;
+
+ rc = libcfs_sock_read(sock, &hello->kshm_src_nid,
+ offsetof(ksock_hello_msg_t, kshm_ips) -
+ offsetof(ksock_hello_msg_t, kshm_src_nid),
+ timeout);
+ if (rc != 0) {
+ CERROR("Error %d reading HELLO from %pI4h\n",
+ rc, &conn->ksnc_ipaddr);
+ LASSERT(rc < 0 && rc != -EALREADY);
+ return rc;
+ }
+
+ if (conn->ksnc_flip) {
+ __swab32s(&hello->kshm_src_pid);
+ __swab64s(&hello->kshm_src_nid);
+ __swab32s(&hello->kshm_dst_pid);
+ __swab64s(&hello->kshm_dst_nid);
+ __swab64s(&hello->kshm_src_incarnation);
+ __swab64s(&hello->kshm_dst_incarnation);
+ __swab32s(&hello->kshm_ctype);
+ __swab32s(&hello->kshm_nips);
+ }
+
+ if (hello->kshm_nips > LNET_MAX_INTERFACES) {
+ CERROR("Bad nips %d from ip %pI4h\n",
+ hello->kshm_nips, &conn->ksnc_ipaddr);
+ return -EPROTO;
+ }
+
+ if (hello->kshm_nips == 0)
+ return 0;
+
+ rc = libcfs_sock_read(sock, hello->kshm_ips,
+ hello->kshm_nips * sizeof(__u32), timeout);
+ if (rc != 0) {
+ CERROR("Error %d reading IPs from ip %pI4h\n",
+ rc, &conn->ksnc_ipaddr);
+ LASSERT(rc < 0 && rc != -EALREADY);
+ return rc;
+ }
+
+ for (i = 0; i < (int) hello->kshm_nips; i++) {
+ if (conn->ksnc_flip)
+ __swab32s(&hello->kshm_ips[i]);
+
+ if (hello->kshm_ips[i] == 0) {
+ CERROR("Zero IP[%d] from ip %pI4h\n",
+ i, &conn->ksnc_ipaddr);
+ return -EPROTO;
+ }
+ }
+
+ return 0;
+}
+
+static void
+ksocknal_pack_msg_v1(ksock_tx_t *tx)
+{
+ /* V1.x has no KSOCK_MSG_NOOP */
+ LASSERT(tx->tx_msg.ksm_type != KSOCK_MSG_NOOP);
+ LASSERT(tx->tx_lnetmsg != NULL);
+
+ tx->tx_iov[0].iov_base = &tx->tx_lnetmsg->msg_hdr;
+ tx->tx_iov[0].iov_len = sizeof(lnet_hdr_t);
+
+ tx->tx_resid = tx->tx_nob = tx->tx_lnetmsg->msg_len + sizeof(lnet_hdr_t);
+}
+
+static void
+ksocknal_pack_msg_v2(ksock_tx_t *tx)
+{
+ tx->tx_iov[0].iov_base = &tx->tx_msg;
+
+ if (tx->tx_lnetmsg != NULL) {
+ LASSERT(tx->tx_msg.ksm_type != KSOCK_MSG_NOOP);
+
+ tx->tx_msg.ksm_u.lnetmsg.ksnm_hdr = tx->tx_lnetmsg->msg_hdr;
+ tx->tx_iov[0].iov_len = sizeof(ksock_msg_t);
+ tx->tx_resid = tx->tx_nob = sizeof(ksock_msg_t) + tx->tx_lnetmsg->msg_len;
+ } else {
+ LASSERT(tx->tx_msg.ksm_type == KSOCK_MSG_NOOP);
+
+ tx->tx_iov[0].iov_len = offsetof(ksock_msg_t, ksm_u.lnetmsg.ksnm_hdr);
+ tx->tx_resid = tx->tx_nob = offsetof(ksock_msg_t, ksm_u.lnetmsg.ksnm_hdr);
+ }
+ /* Don't checksum before start sending, because packet can be piggybacked with ACK */
+}
+
+static void
+ksocknal_unpack_msg_v1(ksock_msg_t *msg)
+{
+ msg->ksm_csum = 0;
+ msg->ksm_type = KSOCK_MSG_LNET;
+ msg->ksm_zc_cookies[0] = msg->ksm_zc_cookies[1] = 0;
+}
+
+static void
+ksocknal_unpack_msg_v2(ksock_msg_t *msg)
+{
+ return; /* Do nothing */
+}
+
+ksock_proto_t ksocknal_protocol_v1x = {
+ .pro_version = KSOCK_PROTO_V1,
+ .pro_send_hello = ksocknal_send_hello_v1,
+ .pro_recv_hello = ksocknal_recv_hello_v1,
+ .pro_pack = ksocknal_pack_msg_v1,
+ .pro_unpack = ksocknal_unpack_msg_v1,
+ .pro_queue_tx_msg = ksocknal_queue_tx_msg_v1,
+ .pro_handle_zcreq = NULL,
+ .pro_handle_zcack = NULL,
+ .pro_queue_tx_zcack = NULL,
+ .pro_match_tx = ksocknal_match_tx
+};
+
+ksock_proto_t ksocknal_protocol_v2x = {
+ .pro_version = KSOCK_PROTO_V2,
+ .pro_send_hello = ksocknal_send_hello_v2,
+ .pro_recv_hello = ksocknal_recv_hello_v2,
+ .pro_pack = ksocknal_pack_msg_v2,
+ .pro_unpack = ksocknal_unpack_msg_v2,
+ .pro_queue_tx_msg = ksocknal_queue_tx_msg_v2,
+ .pro_queue_tx_zcack = ksocknal_queue_tx_zcack_v2,
+ .pro_handle_zcreq = ksocknal_handle_zcreq,
+ .pro_handle_zcack = ksocknal_handle_zcack,
+ .pro_match_tx = ksocknal_match_tx
+};
+
+ksock_proto_t ksocknal_protocol_v3x = {
+ .pro_version = KSOCK_PROTO_V3,
+ .pro_send_hello = ksocknal_send_hello_v2,
+ .pro_recv_hello = ksocknal_recv_hello_v2,
+ .pro_pack = ksocknal_pack_msg_v2,
+ .pro_unpack = ksocknal_unpack_msg_v2,
+ .pro_queue_tx_msg = ksocknal_queue_tx_msg_v2,
+ .pro_queue_tx_zcack = ksocknal_queue_tx_zcack_v3,
+ .pro_handle_zcreq = ksocknal_handle_zcreq,
+ .pro_handle_zcack = ksocknal_handle_zcack,
+ .pro_match_tx = ksocknal_match_tx_v3
+};
diff --git a/drivers/staging/lustre/lnet/lnet/Makefile b/drivers/staging/lustre/lnet/lnet/Makefile
new file mode 100644
index 000000000..336b8ea4f
--- /dev/null
+++ b/drivers/staging/lustre/lnet/lnet/Makefile
@@ -0,0 +1,5 @@
+obj-$(CONFIG_LNET) += lnet.o
+
+lnet-y := api-ni.o config.o lib-me.o lib-msg.o lib-eq.o \
+ lib-md.o lib-ptl.o lib-move.o module.o lo.o router.o \
+ router_proc.o acceptor.o peer.o
diff --git a/drivers/staging/lustre/lnet/lnet/acceptor.c b/drivers/staging/lustre/lnet/lnet/acceptor.c
new file mode 100644
index 000000000..72fd1bf70
--- /dev/null
+++ b/drivers/staging/lustre/lnet/lnet/acceptor.c
@@ -0,0 +1,500 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2011, 2012, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ */
+
+#define DEBUG_SUBSYSTEM S_LNET
+#include "../../include/linux/lnet/lib-lnet.h"
+
+
+static int accept_port = 988;
+static int accept_backlog = 127;
+static int accept_timeout = 5;
+
+static struct {
+ int pta_shutdown;
+ struct socket *pta_sock;
+ struct completion pta_signal;
+} lnet_acceptor_state;
+
+int
+lnet_acceptor_port(void)
+{
+ return accept_port;
+}
+EXPORT_SYMBOL(lnet_acceptor_port);
+
+static inline int
+lnet_accept_magic(__u32 magic, __u32 constant)
+{
+ return (magic == constant ||
+ magic == __swab32(constant));
+}
+
+static char *accept = "secure";
+
+module_param(accept, charp, 0444);
+MODULE_PARM_DESC(accept, "Accept connections (secure|all|none)");
+module_param(accept_port, int, 0444);
+MODULE_PARM_DESC(accept_port, "Acceptor's port (same on all nodes)");
+module_param(accept_backlog, int, 0444);
+MODULE_PARM_DESC(accept_backlog, "Acceptor's listen backlog");
+module_param(accept_timeout, int, 0644);
+MODULE_PARM_DESC(accept_timeout, "Acceptor's timeout (seconds)");
+
+static char *accept_type;
+
+static int
+lnet_acceptor_get_tunables(void)
+{
+ /* Userland acceptor uses 'accept_type' instead of 'accept', due to
+ * conflict with 'accept(2)', but kernel acceptor still uses 'accept'
+ * for compatibility. Hence the trick. */
+ accept_type = accept;
+ return 0;
+}
+
+int
+lnet_acceptor_timeout(void)
+{
+ return accept_timeout;
+}
+EXPORT_SYMBOL(lnet_acceptor_timeout);
+
+void
+lnet_connect_console_error(int rc, lnet_nid_t peer_nid,
+ __u32 peer_ip, int peer_port)
+{
+ switch (rc) {
+ /* "normal" errors */
+ case -ECONNREFUSED:
+ CNETERR("Connection to %s at host %pI4h on port %d was refused: check that Lustre is running on that node.\n",
+ libcfs_nid2str(peer_nid),
+ &peer_ip, peer_port);
+ break;
+ case -EHOSTUNREACH:
+ case -ENETUNREACH:
+ CNETERR("Connection to %s at host %pI4h was unreachable: the network or that node may be down, or Lustre may be misconfigured.\n",
+ libcfs_nid2str(peer_nid), &peer_ip);
+ break;
+ case -ETIMEDOUT:
+ CNETERR("Connection to %s at host %pI4h on port %d took too long: that node may be hung or experiencing high load.\n",
+ libcfs_nid2str(peer_nid),
+ &peer_ip, peer_port);
+ break;
+ case -ECONNRESET:
+ LCONSOLE_ERROR_MSG(0x11b, "Connection to %s at host %pI4h on port %d was reset: is it running a compatible version of Lustre and is %s one of its NIDs?\n",
+ libcfs_nid2str(peer_nid),
+ &peer_ip, peer_port,
+ libcfs_nid2str(peer_nid));
+ break;
+ case -EPROTO:
+ LCONSOLE_ERROR_MSG(0x11c, "Protocol error connecting to %s at host %pI4h on port %d: is it running a compatible version of Lustre?\n",
+ libcfs_nid2str(peer_nid),
+ &peer_ip, peer_port);
+ break;
+ case -EADDRINUSE:
+ LCONSOLE_ERROR_MSG(0x11d, "No privileged ports available to connect to %s at host %pI4h on port %d\n",
+ libcfs_nid2str(peer_nid),
+ &peer_ip, peer_port);
+ break;
+ default:
+ LCONSOLE_ERROR_MSG(0x11e, "Unexpected error %d connecting to %s at host %pI4h on port %d\n",
+ rc, libcfs_nid2str(peer_nid),
+ &peer_ip, peer_port);
+ break;
+ }
+}
+EXPORT_SYMBOL(lnet_connect_console_error);
+
+int
+lnet_connect(struct socket **sockp, lnet_nid_t peer_nid,
+ __u32 local_ip, __u32 peer_ip, int peer_port)
+{
+ lnet_acceptor_connreq_t cr;
+ struct socket *sock;
+ int rc;
+ int port;
+ int fatal;
+
+ CLASSERT(sizeof(cr) <= 16); /* not too big to be on the stack */
+
+ for (port = LNET_ACCEPTOR_MAX_RESERVED_PORT;
+ port >= LNET_ACCEPTOR_MIN_RESERVED_PORT;
+ --port) {
+ /* Iterate through reserved ports. */
+
+ rc = libcfs_sock_connect(&sock, &fatal,
+ local_ip, port,
+ peer_ip, peer_port);
+ if (rc != 0) {
+ if (fatal)
+ goto failed;
+ continue;
+ }
+
+ CLASSERT(LNET_PROTO_ACCEPTOR_VERSION == 1);
+
+ cr.acr_magic = LNET_PROTO_ACCEPTOR_MAGIC;
+ cr.acr_version = LNET_PROTO_ACCEPTOR_VERSION;
+ cr.acr_nid = peer_nid;
+
+ if (the_lnet.ln_testprotocompat != 0) {
+ /* single-shot proto check */
+ lnet_net_lock(LNET_LOCK_EX);
+ if ((the_lnet.ln_testprotocompat & 4) != 0) {
+ cr.acr_version++;
+ the_lnet.ln_testprotocompat &= ~4;
+ }
+ if ((the_lnet.ln_testprotocompat & 8) != 0) {
+ cr.acr_magic = LNET_PROTO_MAGIC;
+ the_lnet.ln_testprotocompat &= ~8;
+ }
+ lnet_net_unlock(LNET_LOCK_EX);
+ }
+
+ rc = libcfs_sock_write(sock, &cr, sizeof(cr),
+ accept_timeout);
+ if (rc != 0)
+ goto failed_sock;
+
+ *sockp = sock;
+ return 0;
+ }
+
+ rc = -EADDRINUSE;
+ goto failed;
+
+ failed_sock:
+ libcfs_sock_release(sock);
+ failed:
+ lnet_connect_console_error(rc, peer_nid, peer_ip, peer_port);
+ return rc;
+}
+EXPORT_SYMBOL(lnet_connect);
+
+
+/* Below is the code common for both kernel and MT user-space */
+
+static int
+lnet_accept(struct socket *sock, __u32 magic)
+{
+ lnet_acceptor_connreq_t cr;
+ __u32 peer_ip;
+ int peer_port;
+ int rc;
+ int flip;
+ lnet_ni_t *ni;
+ char *str;
+
+ LASSERT(sizeof(cr) <= 16); /* not too big for the stack */
+
+ rc = libcfs_sock_getaddr(sock, 1, &peer_ip, &peer_port);
+ LASSERT(rc == 0); /* we succeeded before */
+
+ if (!lnet_accept_magic(magic, LNET_PROTO_ACCEPTOR_MAGIC)) {
+
+ if (lnet_accept_magic(magic, LNET_PROTO_MAGIC)) {
+ /* future version compatibility!
+ * When LNET unifies protocols over all LNDs, the first
+ * thing sent will be a version query. I send back
+ * LNET_PROTO_ACCEPTOR_MAGIC to tell her I'm "old" */
+
+ memset(&cr, 0, sizeof(cr));
+ cr.acr_magic = LNET_PROTO_ACCEPTOR_MAGIC;
+ cr.acr_version = LNET_PROTO_ACCEPTOR_VERSION;
+ rc = libcfs_sock_write(sock, &cr, sizeof(cr),
+ accept_timeout);
+
+ if (rc != 0)
+ CERROR("Error sending magic+version in response to LNET magic from %pI4h: %d\n",
+ &peer_ip, rc);
+ return -EPROTO;
+ }
+
+ if (magic == le32_to_cpu(LNET_PROTO_TCP_MAGIC))
+ str = "'old' socknal/tcpnal";
+ else if (lnet_accept_magic(magic, LNET_PROTO_RA_MAGIC))
+ str = "'old' ranal";
+ else
+ str = "unrecognised";
+
+ LCONSOLE_ERROR_MSG(0x11f, "Refusing connection from %pI4h magic %08x: %s acceptor protocol\n",
+ &peer_ip, magic, str);
+ return -EPROTO;
+ }
+
+ flip = (magic != LNET_PROTO_ACCEPTOR_MAGIC);
+
+ rc = libcfs_sock_read(sock, &cr.acr_version,
+ sizeof(cr.acr_version),
+ accept_timeout);
+ if (rc != 0) {
+ CERROR("Error %d reading connection request version from %pI4h\n",
+ rc, &peer_ip);
+ return -EIO;
+ }
+
+ if (flip)
+ __swab32s(&cr.acr_version);
+
+ if (cr.acr_version != LNET_PROTO_ACCEPTOR_VERSION) {
+ /* future version compatibility!
+ * An acceptor-specific protocol rev will first send a version
+ * query. I send back my current version to tell her I'm
+ * "old". */
+ int peer_version = cr.acr_version;
+
+ memset(&cr, 0, sizeof(cr));
+ cr.acr_magic = LNET_PROTO_ACCEPTOR_MAGIC;
+ cr.acr_version = LNET_PROTO_ACCEPTOR_VERSION;
+
+ rc = libcfs_sock_write(sock, &cr, sizeof(cr),
+ accept_timeout);
+
+ if (rc != 0)
+ CERROR("Error sending magic+version in response to version %d from %pI4h: %d\n",
+ peer_version, &peer_ip, rc);
+ return -EPROTO;
+ }
+
+ rc = libcfs_sock_read(sock, &cr.acr_nid,
+ sizeof(cr) -
+ offsetof(lnet_acceptor_connreq_t, acr_nid),
+ accept_timeout);
+ if (rc != 0) {
+ CERROR("Error %d reading connection request from %pI4h\n",
+ rc, &peer_ip);
+ return -EIO;
+ }
+
+ if (flip)
+ __swab64s(&cr.acr_nid);
+
+ ni = lnet_net2ni(LNET_NIDNET(cr.acr_nid));
+ if (ni == NULL || /* no matching net */
+ ni->ni_nid != cr.acr_nid) { /* right NET, wrong NID! */
+ if (ni != NULL)
+ lnet_ni_decref(ni);
+ LCONSOLE_ERROR_MSG(0x120, "Refusing connection from %pI4h for %s: No matching NI\n",
+ &peer_ip, libcfs_nid2str(cr.acr_nid));
+ return -EPERM;
+ }
+
+ if (ni->ni_lnd->lnd_accept == NULL) {
+ /* This catches a request for the loopback LND */
+ lnet_ni_decref(ni);
+ LCONSOLE_ERROR_MSG(0x121, "Refusing connection from %pI4h for %s: NI doesn not accept IP connections\n",
+ &peer_ip, libcfs_nid2str(cr.acr_nid));
+ return -EPERM;
+ }
+
+ CDEBUG(D_NET, "Accept %s from %pI4h\n",
+ libcfs_nid2str(cr.acr_nid), &peer_ip);
+
+ rc = ni->ni_lnd->lnd_accept(ni, sock);
+
+ lnet_ni_decref(ni);
+ return rc;
+}
+
+static int
+lnet_acceptor(void *arg)
+{
+ struct socket *newsock;
+ int rc;
+ __u32 magic;
+ __u32 peer_ip;
+ int peer_port;
+ int secure = (int)((long_ptr_t)arg);
+
+ LASSERT(lnet_acceptor_state.pta_sock == NULL);
+
+ cfs_block_allsigs();
+
+ rc = libcfs_sock_listen(&lnet_acceptor_state.pta_sock,
+ 0, accept_port, accept_backlog);
+ if (rc != 0) {
+ if (rc == -EADDRINUSE)
+ LCONSOLE_ERROR_MSG(0x122, "Can't start acceptor on port %d: port already in use\n",
+ accept_port);
+ else
+ LCONSOLE_ERROR_MSG(0x123, "Can't start acceptor on port %d: unexpected error %d\n",
+ accept_port, rc);
+
+ lnet_acceptor_state.pta_sock = NULL;
+ } else {
+ LCONSOLE(0, "Accept %s, port %d\n", accept_type, accept_port);
+ }
+
+ /* set init status and unblock parent */
+ lnet_acceptor_state.pta_shutdown = rc;
+ complete(&lnet_acceptor_state.pta_signal);
+
+ if (rc != 0)
+ return rc;
+
+ while (!lnet_acceptor_state.pta_shutdown) {
+
+ rc = libcfs_sock_accept(&newsock, lnet_acceptor_state.pta_sock);
+ if (rc != 0) {
+ if (rc != -EAGAIN) {
+ CWARN("Accept error %d: pausing...\n", rc);
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ schedule_timeout(cfs_time_seconds(1));
+ }
+ continue;
+ }
+
+ /* maybe we're waken up with libcfs_sock_abort_accept() */
+ if (lnet_acceptor_state.pta_shutdown) {
+ libcfs_sock_release(newsock);
+ break;
+ }
+
+ rc = libcfs_sock_getaddr(newsock, 1, &peer_ip, &peer_port);
+ if (rc != 0) {
+ CERROR("Can't determine new connection's address\n");
+ goto failed;
+ }
+
+ if (secure && peer_port > LNET_ACCEPTOR_MAX_RESERVED_PORT) {
+ CERROR("Refusing connection from %pI4h: insecure port %d\n",
+ &peer_ip, peer_port);
+ goto failed;
+ }
+
+ rc = libcfs_sock_read(newsock, &magic, sizeof(magic),
+ accept_timeout);
+ if (rc != 0) {
+ CERROR("Error %d reading connection request from %pI4h\n",
+ rc, &peer_ip);
+ goto failed;
+ }
+
+ rc = lnet_accept(newsock, magic);
+ if (rc != 0)
+ goto failed;
+
+ continue;
+
+failed:
+ libcfs_sock_release(newsock);
+ }
+
+ libcfs_sock_release(lnet_acceptor_state.pta_sock);
+ lnet_acceptor_state.pta_sock = NULL;
+
+ CDEBUG(D_NET, "Acceptor stopping\n");
+
+ /* unblock lnet_acceptor_stop() */
+ complete(&lnet_acceptor_state.pta_signal);
+ return 0;
+}
+
+static inline int
+accept2secure(const char *acc, long *sec)
+{
+ if (!strcmp(acc, "secure")) {
+ *sec = 1;
+ return 1;
+ } else if (!strcmp(acc, "all")) {
+ *sec = 0;
+ return 1;
+ } else if (!strcmp(acc, "none")) {
+ return 0;
+ }
+
+ LCONSOLE_ERROR_MSG(0x124, "Can't parse 'accept=\"%s\"'\n",
+ acc);
+ return -EINVAL;
+}
+
+int
+lnet_acceptor_start(void)
+{
+ int rc;
+ long rc2;
+ long secure;
+
+ LASSERT(lnet_acceptor_state.pta_sock == NULL);
+
+ rc = lnet_acceptor_get_tunables();
+ if (rc != 0)
+ return rc;
+
+
+ init_completion(&lnet_acceptor_state.pta_signal);
+ rc = accept2secure(accept_type, &secure);
+ if (rc <= 0)
+ return rc;
+
+ if (lnet_count_acceptor_nis() == 0) /* not required */
+ return 0;
+
+ rc2 = PTR_ERR(kthread_run(lnet_acceptor,
+ (void *)(ulong_ptr_t)secure,
+ "acceptor_%03ld", secure));
+ if (IS_ERR_VALUE(rc2)) {
+ CERROR("Can't start acceptor thread: %ld\n", rc2);
+
+ return -ESRCH;
+ }
+
+ /* wait for acceptor to startup */
+ wait_for_completion(&lnet_acceptor_state.pta_signal);
+
+ if (!lnet_acceptor_state.pta_shutdown) {
+ /* started OK */
+ LASSERT(lnet_acceptor_state.pta_sock != NULL);
+ return 0;
+ }
+
+ LASSERT(lnet_acceptor_state.pta_sock == NULL);
+
+ return -ENETDOWN;
+}
+
+void
+lnet_acceptor_stop(void)
+{
+ if (lnet_acceptor_state.pta_sock == NULL) /* not running */
+ return;
+
+ lnet_acceptor_state.pta_shutdown = 1;
+ libcfs_sock_abort_accept(lnet_acceptor_state.pta_sock);
+
+ /* block until acceptor signals exit */
+ wait_for_completion(&lnet_acceptor_state.pta_signal);
+}
diff --git a/drivers/staging/lustre/lnet/lnet/api-ni.c b/drivers/staging/lustre/lnet/lnet/api-ni.c
new file mode 100644
index 000000000..4a14e5109
--- /dev/null
+++ b/drivers/staging/lustre/lnet/lnet/api-ni.c
@@ -0,0 +1,1940 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2011, 2012, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ */
+
+#define DEBUG_SUBSYSTEM S_LNET
+#include "../../include/linux/lnet/lib-lnet.h"
+#include <linux/log2.h>
+#include <linux/ktime.h>
+
+#define D_LNI D_CONSOLE
+
+lnet_t the_lnet; /* THE state of the network */
+EXPORT_SYMBOL(the_lnet);
+
+
+static char *ip2nets = "";
+module_param(ip2nets, charp, 0444);
+MODULE_PARM_DESC(ip2nets, "LNET network <- IP table");
+
+static char *networks = "";
+module_param(networks, charp, 0444);
+MODULE_PARM_DESC(networks, "local networks");
+
+static char *routes = "";
+module_param(routes, charp, 0444);
+MODULE_PARM_DESC(routes, "routes to non-local networks");
+
+static int rnet_htable_size = LNET_REMOTE_NETS_HASH_DEFAULT;
+module_param(rnet_htable_size, int, 0444);
+MODULE_PARM_DESC(rnet_htable_size, "size of remote network hash table");
+
+static char *
+lnet_get_routes(void)
+{
+ return routes;
+}
+
+static char *
+lnet_get_networks(void)
+{
+ char *nets;
+ int rc;
+
+ if (*networks != 0 && *ip2nets != 0) {
+ LCONSOLE_ERROR_MSG(0x101, "Please specify EITHER 'networks' or 'ip2nets' but not both at once\n");
+ return NULL;
+ }
+
+ if (*ip2nets != 0) {
+ rc = lnet_parse_ip2nets(&nets, ip2nets);
+ return (rc == 0) ? nets : NULL;
+ }
+
+ if (*networks != 0)
+ return networks;
+
+ return "tcp";
+}
+
+static void
+lnet_init_locks(void)
+{
+ spin_lock_init(&the_lnet.ln_eq_wait_lock);
+ init_waitqueue_head(&the_lnet.ln_eq_waitq);
+ mutex_init(&the_lnet.ln_lnd_mutex);
+ mutex_init(&the_lnet.ln_api_mutex);
+}
+
+static void
+lnet_fini_locks(void)
+{
+}
+
+
+static int
+lnet_create_remote_nets_table(void)
+{
+ int i;
+ struct list_head *hash;
+
+ LASSERT(the_lnet.ln_remote_nets_hash == NULL);
+ LASSERT(the_lnet.ln_remote_nets_hbits > 0);
+ LIBCFS_ALLOC(hash, LNET_REMOTE_NETS_HASH_SIZE * sizeof(*hash));
+ if (hash == NULL) {
+ CERROR("Failed to create remote nets hash table\n");
+ return -ENOMEM;
+ }
+
+ for (i = 0; i < LNET_REMOTE_NETS_HASH_SIZE; i++)
+ INIT_LIST_HEAD(&hash[i]);
+ the_lnet.ln_remote_nets_hash = hash;
+ return 0;
+}
+
+static void
+lnet_destroy_remote_nets_table(void)
+{
+ int i;
+
+ if (the_lnet.ln_remote_nets_hash == NULL)
+ return;
+
+ for (i = 0; i < LNET_REMOTE_NETS_HASH_SIZE; i++)
+ LASSERT(list_empty(&the_lnet.ln_remote_nets_hash[i]));
+
+ LIBCFS_FREE(the_lnet.ln_remote_nets_hash,
+ LNET_REMOTE_NETS_HASH_SIZE *
+ sizeof(the_lnet.ln_remote_nets_hash[0]));
+ the_lnet.ln_remote_nets_hash = NULL;
+}
+
+static void
+lnet_destroy_locks(void)
+{
+ if (the_lnet.ln_res_lock != NULL) {
+ cfs_percpt_lock_free(the_lnet.ln_res_lock);
+ the_lnet.ln_res_lock = NULL;
+ }
+
+ if (the_lnet.ln_net_lock != NULL) {
+ cfs_percpt_lock_free(the_lnet.ln_net_lock);
+ the_lnet.ln_net_lock = NULL;
+ }
+
+ lnet_fini_locks();
+}
+
+static int
+lnet_create_locks(void)
+{
+ lnet_init_locks();
+
+ the_lnet.ln_res_lock = cfs_percpt_lock_alloc(lnet_cpt_table());
+ if (the_lnet.ln_res_lock == NULL)
+ goto failed;
+
+ the_lnet.ln_net_lock = cfs_percpt_lock_alloc(lnet_cpt_table());
+ if (the_lnet.ln_net_lock == NULL)
+ goto failed;
+
+ return 0;
+
+ failed:
+ lnet_destroy_locks();
+ return -ENOMEM;
+}
+
+static void lnet_assert_wire_constants(void)
+{
+ /* Wire protocol assertions generated by 'wirecheck'
+ * running on Linux robert.bartonsoftware.com 2.6.8-1.521
+ * #1 Mon Aug 16 09:01:18 EDT 2004 i686 athlon i386 GNU/Linux
+ * with gcc version 3.3.3 20040412 (Red Hat Linux 3.3.3-7) */
+
+ /* Constants... */
+ CLASSERT(LNET_PROTO_TCP_MAGIC == 0xeebc0ded);
+ CLASSERT(LNET_PROTO_TCP_VERSION_MAJOR == 1);
+ CLASSERT(LNET_PROTO_TCP_VERSION_MINOR == 0);
+ CLASSERT(LNET_MSG_ACK == 0);
+ CLASSERT(LNET_MSG_PUT == 1);
+ CLASSERT(LNET_MSG_GET == 2);
+ CLASSERT(LNET_MSG_REPLY == 3);
+ CLASSERT(LNET_MSG_HELLO == 4);
+
+ /* Checks for struct ptl_handle_wire_t */
+ CLASSERT((int)sizeof(lnet_handle_wire_t) == 16);
+ CLASSERT((int)offsetof(lnet_handle_wire_t, wh_interface_cookie) == 0);
+ CLASSERT((int)sizeof(((lnet_handle_wire_t *)0)->wh_interface_cookie) == 8);
+ CLASSERT((int)offsetof(lnet_handle_wire_t, wh_object_cookie) == 8);
+ CLASSERT((int)sizeof(((lnet_handle_wire_t *)0)->wh_object_cookie) == 8);
+
+ /* Checks for struct lnet_magicversion_t */
+ CLASSERT((int)sizeof(lnet_magicversion_t) == 8);
+ CLASSERT((int)offsetof(lnet_magicversion_t, magic) == 0);
+ CLASSERT((int)sizeof(((lnet_magicversion_t *)0)->magic) == 4);
+ CLASSERT((int)offsetof(lnet_magicversion_t, version_major) == 4);
+ CLASSERT((int)sizeof(((lnet_magicversion_t *)0)->version_major) == 2);
+ CLASSERT((int)offsetof(lnet_magicversion_t, version_minor) == 6);
+ CLASSERT((int)sizeof(((lnet_magicversion_t *)0)->version_minor) == 2);
+
+ /* Checks for struct lnet_hdr_t */
+ CLASSERT((int)sizeof(lnet_hdr_t) == 72);
+ CLASSERT((int)offsetof(lnet_hdr_t, dest_nid) == 0);
+ CLASSERT((int)sizeof(((lnet_hdr_t *)0)->dest_nid) == 8);
+ CLASSERT((int)offsetof(lnet_hdr_t, src_nid) == 8);
+ CLASSERT((int)sizeof(((lnet_hdr_t *)0)->src_nid) == 8);
+ CLASSERT((int)offsetof(lnet_hdr_t, dest_pid) == 16);
+ CLASSERT((int)sizeof(((lnet_hdr_t *)0)->dest_pid) == 4);
+ CLASSERT((int)offsetof(lnet_hdr_t, src_pid) == 20);
+ CLASSERT((int)sizeof(((lnet_hdr_t *)0)->src_pid) == 4);
+ CLASSERT((int)offsetof(lnet_hdr_t, type) == 24);
+ CLASSERT((int)sizeof(((lnet_hdr_t *)0)->type) == 4);
+ CLASSERT((int)offsetof(lnet_hdr_t, payload_length) == 28);
+ CLASSERT((int)sizeof(((lnet_hdr_t *)0)->payload_length) == 4);
+ CLASSERT((int)offsetof(lnet_hdr_t, msg) == 32);
+ CLASSERT((int)sizeof(((lnet_hdr_t *)0)->msg) == 40);
+
+ /* Ack */
+ CLASSERT((int)offsetof(lnet_hdr_t, msg.ack.dst_wmd) == 32);
+ CLASSERT((int)sizeof(((lnet_hdr_t *)0)->msg.ack.dst_wmd) == 16);
+ CLASSERT((int)offsetof(lnet_hdr_t, msg.ack.match_bits) == 48);
+ CLASSERT((int)sizeof(((lnet_hdr_t *)0)->msg.ack.match_bits) == 8);
+ CLASSERT((int)offsetof(lnet_hdr_t, msg.ack.mlength) == 56);
+ CLASSERT((int)sizeof(((lnet_hdr_t *)0)->msg.ack.mlength) == 4);
+
+ /* Put */
+ CLASSERT((int)offsetof(lnet_hdr_t, msg.put.ack_wmd) == 32);
+ CLASSERT((int)sizeof(((lnet_hdr_t *)0)->msg.put.ack_wmd) == 16);
+ CLASSERT((int)offsetof(lnet_hdr_t, msg.put.match_bits) == 48);
+ CLASSERT((int)sizeof(((lnet_hdr_t *)0)->msg.put.match_bits) == 8);
+ CLASSERT((int)offsetof(lnet_hdr_t, msg.put.hdr_data) == 56);
+ CLASSERT((int)sizeof(((lnet_hdr_t *)0)->msg.put.hdr_data) == 8);
+ CLASSERT((int)offsetof(lnet_hdr_t, msg.put.ptl_index) == 64);
+ CLASSERT((int)sizeof(((lnet_hdr_t *)0)->msg.put.ptl_index) == 4);
+ CLASSERT((int)offsetof(lnet_hdr_t, msg.put.offset) == 68);
+ CLASSERT((int)sizeof(((lnet_hdr_t *)0)->msg.put.offset) == 4);
+
+ /* Get */
+ CLASSERT((int)offsetof(lnet_hdr_t, msg.get.return_wmd) == 32);
+ CLASSERT((int)sizeof(((lnet_hdr_t *)0)->msg.get.return_wmd) == 16);
+ CLASSERT((int)offsetof(lnet_hdr_t, msg.get.match_bits) == 48);
+ CLASSERT((int)sizeof(((lnet_hdr_t *)0)->msg.get.match_bits) == 8);
+ CLASSERT((int)offsetof(lnet_hdr_t, msg.get.ptl_index) == 56);
+ CLASSERT((int)sizeof(((lnet_hdr_t *)0)->msg.get.ptl_index) == 4);
+ CLASSERT((int)offsetof(lnet_hdr_t, msg.get.src_offset) == 60);
+ CLASSERT((int)sizeof(((lnet_hdr_t *)0)->msg.get.src_offset) == 4);
+ CLASSERT((int)offsetof(lnet_hdr_t, msg.get.sink_length) == 64);
+ CLASSERT((int)sizeof(((lnet_hdr_t *)0)->msg.get.sink_length) == 4);
+
+ /* Reply */
+ CLASSERT((int)offsetof(lnet_hdr_t, msg.reply.dst_wmd) == 32);
+ CLASSERT((int)sizeof(((lnet_hdr_t *)0)->msg.reply.dst_wmd) == 16);
+
+ /* Hello */
+ CLASSERT((int)offsetof(lnet_hdr_t, msg.hello.incarnation) == 32);
+ CLASSERT((int)sizeof(((lnet_hdr_t *)0)->msg.hello.incarnation) == 8);
+ CLASSERT((int)offsetof(lnet_hdr_t, msg.hello.type) == 40);
+ CLASSERT((int)sizeof(((lnet_hdr_t *)0)->msg.hello.type) == 4);
+}
+
+static lnd_t *
+lnet_find_lnd_by_type(int type)
+{
+ lnd_t *lnd;
+ struct list_head *tmp;
+
+ /* holding lnd mutex */
+ list_for_each(tmp, &the_lnet.ln_lnds) {
+ lnd = list_entry(tmp, lnd_t, lnd_list);
+
+ if ((int)lnd->lnd_type == type)
+ return lnd;
+ }
+
+ return NULL;
+}
+
+void
+lnet_register_lnd(lnd_t *lnd)
+{
+ LNET_MUTEX_LOCK(&the_lnet.ln_lnd_mutex);
+
+ LASSERT(the_lnet.ln_init);
+ LASSERT(libcfs_isknown_lnd(lnd->lnd_type));
+ LASSERT(lnet_find_lnd_by_type(lnd->lnd_type) == NULL);
+
+ list_add_tail(&lnd->lnd_list, &the_lnet.ln_lnds);
+ lnd->lnd_refcount = 0;
+
+ CDEBUG(D_NET, "%s LND registered\n", libcfs_lnd2str(lnd->lnd_type));
+
+ LNET_MUTEX_UNLOCK(&the_lnet.ln_lnd_mutex);
+}
+EXPORT_SYMBOL(lnet_register_lnd);
+
+void
+lnet_unregister_lnd(lnd_t *lnd)
+{
+ LNET_MUTEX_LOCK(&the_lnet.ln_lnd_mutex);
+
+ LASSERT(the_lnet.ln_init);
+ LASSERT(lnet_find_lnd_by_type(lnd->lnd_type) == lnd);
+ LASSERT(lnd->lnd_refcount == 0);
+
+ list_del(&lnd->lnd_list);
+ CDEBUG(D_NET, "%s LND unregistered\n", libcfs_lnd2str(lnd->lnd_type));
+
+ LNET_MUTEX_UNLOCK(&the_lnet.ln_lnd_mutex);
+}
+EXPORT_SYMBOL(lnet_unregister_lnd);
+
+void
+lnet_counters_get(lnet_counters_t *counters)
+{
+ lnet_counters_t *ctr;
+ int i;
+
+ memset(counters, 0, sizeof(*counters));
+
+ lnet_net_lock(LNET_LOCK_EX);
+
+ cfs_percpt_for_each(ctr, i, the_lnet.ln_counters) {
+ counters->msgs_max += ctr->msgs_max;
+ counters->msgs_alloc += ctr->msgs_alloc;
+ counters->errors += ctr->errors;
+ counters->send_count += ctr->send_count;
+ counters->recv_count += ctr->recv_count;
+ counters->route_count += ctr->route_count;
+ counters->drop_count += ctr->drop_count;
+ counters->send_length += ctr->send_length;
+ counters->recv_length += ctr->recv_length;
+ counters->route_length += ctr->route_length;
+ counters->drop_length += ctr->drop_length;
+
+ }
+ lnet_net_unlock(LNET_LOCK_EX);
+}
+EXPORT_SYMBOL(lnet_counters_get);
+
+void
+lnet_counters_reset(void)
+{
+ lnet_counters_t *counters;
+ int i;
+
+ lnet_net_lock(LNET_LOCK_EX);
+
+ cfs_percpt_for_each(counters, i, the_lnet.ln_counters)
+ memset(counters, 0, sizeof(lnet_counters_t));
+
+ lnet_net_unlock(LNET_LOCK_EX);
+}
+EXPORT_SYMBOL(lnet_counters_reset);
+
+#ifdef LNET_USE_LIB_FREELIST
+
+int
+lnet_freelist_init(lnet_freelist_t *fl, int n, int size)
+{
+ char *space;
+
+ LASSERT(n > 0);
+
+ size += offsetof(lnet_freeobj_t, fo_contents);
+
+ LIBCFS_ALLOC(space, n * size);
+ if (space == NULL)
+ return -ENOMEM;
+
+ INIT_LIST_HEAD(&fl->fl_list);
+ fl->fl_objs = space;
+ fl->fl_nobjs = n;
+ fl->fl_objsize = size;
+
+ do {
+ memset(space, 0, size);
+ list_add((struct list_head *)space, &fl->fl_list);
+ space += size;
+ } while (--n != 0);
+
+ return 0;
+}
+
+void
+lnet_freelist_fini(lnet_freelist_t *fl)
+{
+ struct list_head *el;
+ int count;
+
+ if (fl->fl_nobjs == 0)
+ return;
+
+ count = 0;
+ for (el = fl->fl_list.next; el != &fl->fl_list; el = el->next)
+ count++;
+
+ LASSERT(count == fl->fl_nobjs);
+
+ LIBCFS_FREE(fl->fl_objs, fl->fl_nobjs * fl->fl_objsize);
+ memset(fl, 0, sizeof(*fl));
+}
+
+#endif /* LNET_USE_LIB_FREELIST */
+
+static __u64
+lnet_create_interface_cookie(void)
+{
+ /* NB the interface cookie in wire handles guards against delayed
+ * replies and ACKs appearing valid after reboot.
+ */
+ return ktime_get_ns();
+}
+
+static char *
+lnet_res_type2str(int type)
+{
+ switch (type) {
+ default:
+ LBUG();
+ case LNET_COOKIE_TYPE_MD:
+ return "MD";
+ case LNET_COOKIE_TYPE_ME:
+ return "ME";
+ case LNET_COOKIE_TYPE_EQ:
+ return "EQ";
+ }
+}
+
+static void
+lnet_res_container_cleanup(struct lnet_res_container *rec)
+{
+ int count = 0;
+
+ if (rec->rec_type == 0) /* not set yet, it's uninitialized */
+ return;
+
+ while (!list_empty(&rec->rec_active)) {
+ struct list_head *e = rec->rec_active.next;
+
+ list_del_init(e);
+ if (rec->rec_type == LNET_COOKIE_TYPE_EQ) {
+ lnet_eq_free(list_entry(e, lnet_eq_t, eq_list));
+
+ } else if (rec->rec_type == LNET_COOKIE_TYPE_MD) {
+ lnet_md_free(list_entry(e, lnet_libmd_t, md_list));
+
+ } else { /* NB: Active MEs should be attached on portals */
+ LBUG();
+ }
+ count++;
+ }
+
+ if (count > 0) {
+ /* Found alive MD/ME/EQ, user really should unlink/free
+ * all of them before finalize LNet, but if someone didn't,
+ * we have to recycle garbage for him */
+ CERROR("%d active elements on exit of %s container\n",
+ count, lnet_res_type2str(rec->rec_type));
+ }
+
+#ifdef LNET_USE_LIB_FREELIST
+ lnet_freelist_fini(&rec->rec_freelist);
+#endif
+ if (rec->rec_lh_hash != NULL) {
+ LIBCFS_FREE(rec->rec_lh_hash,
+ LNET_LH_HASH_SIZE * sizeof(rec->rec_lh_hash[0]));
+ rec->rec_lh_hash = NULL;
+ }
+
+ rec->rec_type = 0; /* mark it as finalized */
+}
+
+static int
+lnet_res_container_setup(struct lnet_res_container *rec,
+ int cpt, int type, int objnum, int objsz)
+{
+ int rc = 0;
+ int i;
+
+ LASSERT(rec->rec_type == 0);
+
+ rec->rec_type = type;
+ INIT_LIST_HEAD(&rec->rec_active);
+
+#ifdef LNET_USE_LIB_FREELIST
+ memset(&rec->rec_freelist, 0, sizeof(rec->rec_freelist));
+ rc = lnet_freelist_init(&rec->rec_freelist, objnum, objsz);
+ if (rc != 0)
+ goto out;
+#endif
+ rec->rec_lh_cookie = (cpt << LNET_COOKIE_TYPE_BITS) | type;
+
+ /* Arbitrary choice of hash table size */
+ LIBCFS_CPT_ALLOC(rec->rec_lh_hash, lnet_cpt_table(), cpt,
+ LNET_LH_HASH_SIZE * sizeof(rec->rec_lh_hash[0]));
+ if (rec->rec_lh_hash == NULL) {
+ rc = -ENOMEM;
+ goto out;
+ }
+
+ for (i = 0; i < LNET_LH_HASH_SIZE; i++)
+ INIT_LIST_HEAD(&rec->rec_lh_hash[i]);
+
+ return 0;
+
+out:
+ CERROR("Failed to setup %s resource container\n",
+ lnet_res_type2str(type));
+ lnet_res_container_cleanup(rec);
+ return rc;
+}
+
+static void
+lnet_res_containers_destroy(struct lnet_res_container **recs)
+{
+ struct lnet_res_container *rec;
+ int i;
+
+ cfs_percpt_for_each(rec, i, recs)
+ lnet_res_container_cleanup(rec);
+
+ cfs_percpt_free(recs);
+}
+
+static struct lnet_res_container **
+lnet_res_containers_create(int type, int objnum, int objsz)
+{
+ struct lnet_res_container **recs;
+ struct lnet_res_container *rec;
+ int rc;
+ int i;
+
+ recs = cfs_percpt_alloc(lnet_cpt_table(), sizeof(*rec));
+ if (recs == NULL) {
+ CERROR("Failed to allocate %s resource containers\n",
+ lnet_res_type2str(type));
+ return NULL;
+ }
+
+ cfs_percpt_for_each(rec, i, recs) {
+ rc = lnet_res_container_setup(rec, i, type, objnum, objsz);
+ if (rc != 0) {
+ lnet_res_containers_destroy(recs);
+ return NULL;
+ }
+ }
+
+ return recs;
+}
+
+lnet_libhandle_t *
+lnet_res_lh_lookup(struct lnet_res_container *rec, __u64 cookie)
+{
+ /* ALWAYS called with lnet_res_lock held */
+ struct list_head *head;
+ lnet_libhandle_t *lh;
+ unsigned int hash;
+
+ if ((cookie & LNET_COOKIE_MASK) != rec->rec_type)
+ return NULL;
+
+ hash = cookie >> (LNET_COOKIE_TYPE_BITS + LNET_CPT_BITS);
+ head = &rec->rec_lh_hash[hash & LNET_LH_HASH_MASK];
+
+ list_for_each_entry(lh, head, lh_hash_chain) {
+ if (lh->lh_cookie == cookie)
+ return lh;
+ }
+
+ return NULL;
+}
+
+void
+lnet_res_lh_initialize(struct lnet_res_container *rec, lnet_libhandle_t *lh)
+{
+ /* ALWAYS called with lnet_res_lock held */
+ unsigned int ibits = LNET_COOKIE_TYPE_BITS + LNET_CPT_BITS;
+ unsigned int hash;
+
+ lh->lh_cookie = rec->rec_lh_cookie;
+ rec->rec_lh_cookie += 1 << ibits;
+
+ hash = (lh->lh_cookie >> ibits) & LNET_LH_HASH_MASK;
+
+ list_add(&lh->lh_hash_chain, &rec->rec_lh_hash[hash]);
+}
+
+
+int lnet_unprepare(void);
+
+static int
+lnet_prepare(lnet_pid_t requested_pid)
+{
+ /* Prepare to bring up the network */
+ struct lnet_res_container **recs;
+ int rc = 0;
+
+ LASSERT(the_lnet.ln_refcount == 0);
+
+ the_lnet.ln_routing = 0;
+
+ LASSERT((requested_pid & LNET_PID_USERFLAG) == 0);
+ the_lnet.ln_pid = requested_pid;
+
+ INIT_LIST_HEAD(&the_lnet.ln_test_peers);
+ INIT_LIST_HEAD(&the_lnet.ln_nis);
+ INIT_LIST_HEAD(&the_lnet.ln_nis_cpt);
+ INIT_LIST_HEAD(&the_lnet.ln_nis_zombie);
+ INIT_LIST_HEAD(&the_lnet.ln_routers);
+
+ rc = lnet_create_remote_nets_table();
+ if (rc != 0)
+ goto failed;
+
+ the_lnet.ln_interface_cookie = lnet_create_interface_cookie();
+
+ the_lnet.ln_counters = cfs_percpt_alloc(lnet_cpt_table(),
+ sizeof(lnet_counters_t));
+ if (the_lnet.ln_counters == NULL) {
+ CERROR("Failed to allocate counters for LNet\n");
+ rc = -ENOMEM;
+ goto failed;
+ }
+
+ rc = lnet_peer_tables_create();
+ if (rc != 0)
+ goto failed;
+
+ rc = lnet_msg_containers_create();
+ if (rc != 0)
+ goto failed;
+
+ rc = lnet_res_container_setup(&the_lnet.ln_eq_container, 0,
+ LNET_COOKIE_TYPE_EQ, LNET_FL_MAX_EQS,
+ sizeof(lnet_eq_t));
+ if (rc != 0)
+ goto failed;
+
+ recs = lnet_res_containers_create(LNET_COOKIE_TYPE_ME, LNET_FL_MAX_MES,
+ sizeof(lnet_me_t));
+ if (recs == NULL) {
+ rc = -ENOMEM;
+ goto failed;
+ }
+
+ the_lnet.ln_me_containers = recs;
+
+ recs = lnet_res_containers_create(LNET_COOKIE_TYPE_MD, LNET_FL_MAX_MDS,
+ sizeof(lnet_libmd_t));
+ if (recs == NULL) {
+ rc = -ENOMEM;
+ goto failed;
+ }
+
+ the_lnet.ln_md_containers = recs;
+
+ rc = lnet_portals_create();
+ if (rc != 0) {
+ CERROR("Failed to create portals for LNet: %d\n", rc);
+ goto failed;
+ }
+
+ return 0;
+
+ failed:
+ lnet_unprepare();
+ return rc;
+}
+
+int
+lnet_unprepare(void)
+{
+ /* NB no LNET_LOCK since this is the last reference. All LND instances
+ * have shut down already, so it is safe to unlink and free all
+ * descriptors, even those that appear committed to a network op (eg MD
+ * with non-zero pending count) */
+
+ lnet_fail_nid(LNET_NID_ANY, 0);
+
+ LASSERT(the_lnet.ln_refcount == 0);
+ LASSERT(list_empty(&the_lnet.ln_test_peers));
+ LASSERT(list_empty(&the_lnet.ln_nis));
+ LASSERT(list_empty(&the_lnet.ln_nis_cpt));
+ LASSERT(list_empty(&the_lnet.ln_nis_zombie));
+
+ lnet_portals_destroy();
+
+ if (the_lnet.ln_md_containers != NULL) {
+ lnet_res_containers_destroy(the_lnet.ln_md_containers);
+ the_lnet.ln_md_containers = NULL;
+ }
+
+ if (the_lnet.ln_me_containers != NULL) {
+ lnet_res_containers_destroy(the_lnet.ln_me_containers);
+ the_lnet.ln_me_containers = NULL;
+ }
+
+ lnet_res_container_cleanup(&the_lnet.ln_eq_container);
+
+ lnet_msg_containers_destroy();
+ lnet_peer_tables_destroy();
+ lnet_rtrpools_free();
+
+ if (the_lnet.ln_counters != NULL) {
+ cfs_percpt_free(the_lnet.ln_counters);
+ the_lnet.ln_counters = NULL;
+ }
+ lnet_destroy_remote_nets_table();
+
+ return 0;
+}
+
+lnet_ni_t *
+lnet_net2ni_locked(__u32 net, int cpt)
+{
+ struct list_head *tmp;
+ lnet_ni_t *ni;
+
+ LASSERT(cpt != LNET_LOCK_EX);
+
+ list_for_each(tmp, &the_lnet.ln_nis) {
+ ni = list_entry(tmp, lnet_ni_t, ni_list);
+
+ if (LNET_NIDNET(ni->ni_nid) == net) {
+ lnet_ni_addref_locked(ni, cpt);
+ return ni;
+ }
+ }
+
+ return NULL;
+}
+
+lnet_ni_t *
+lnet_net2ni(__u32 net)
+{
+ lnet_ni_t *ni;
+
+ lnet_net_lock(0);
+ ni = lnet_net2ni_locked(net, 0);
+ lnet_net_unlock(0);
+
+ return ni;
+}
+EXPORT_SYMBOL(lnet_net2ni);
+
+static unsigned int
+lnet_nid_cpt_hash(lnet_nid_t nid, unsigned int number)
+{
+ __u64 key = nid;
+ unsigned int val;
+
+ LASSERT(number >= 1 && number <= LNET_CPT_NUMBER);
+
+ if (number == 1)
+ return 0;
+
+ val = hash_long(key, LNET_CPT_BITS);
+ /* NB: LNET_CP_NUMBER doesn't have to be PO2 */
+ if (val < number)
+ return val;
+
+ return (unsigned int)(key + val + (val >> 1)) % number;
+}
+
+int
+lnet_cpt_of_nid_locked(lnet_nid_t nid)
+{
+ struct lnet_ni *ni;
+
+ /* must called with hold of lnet_net_lock */
+ if (LNET_CPT_NUMBER == 1)
+ return 0; /* the only one */
+
+ /* take lnet_net_lock(any) would be OK */
+ if (!list_empty(&the_lnet.ln_nis_cpt)) {
+ list_for_each_entry(ni, &the_lnet.ln_nis_cpt, ni_cptlist) {
+ if (LNET_NIDNET(ni->ni_nid) != LNET_NIDNET(nid))
+ continue;
+
+ LASSERT(ni->ni_cpts != NULL);
+ return ni->ni_cpts[lnet_nid_cpt_hash
+ (nid, ni->ni_ncpts)];
+ }
+ }
+
+ return lnet_nid_cpt_hash(nid, LNET_CPT_NUMBER);
+}
+
+int
+lnet_cpt_of_nid(lnet_nid_t nid)
+{
+ int cpt;
+ int cpt2;
+
+ if (LNET_CPT_NUMBER == 1)
+ return 0; /* the only one */
+
+ if (list_empty(&the_lnet.ln_nis_cpt))
+ return lnet_nid_cpt_hash(nid, LNET_CPT_NUMBER);
+
+ cpt = lnet_net_lock_current();
+ cpt2 = lnet_cpt_of_nid_locked(nid);
+ lnet_net_unlock(cpt);
+
+ return cpt2;
+}
+EXPORT_SYMBOL(lnet_cpt_of_nid);
+
+int
+lnet_islocalnet(__u32 net)
+{
+ struct lnet_ni *ni;
+ int cpt;
+
+ cpt = lnet_net_lock_current();
+
+ ni = lnet_net2ni_locked(net, cpt);
+ if (ni != NULL)
+ lnet_ni_decref_locked(ni, cpt);
+
+ lnet_net_unlock(cpt);
+
+ return ni != NULL;
+}
+
+lnet_ni_t *
+lnet_nid2ni_locked(lnet_nid_t nid, int cpt)
+{
+ struct lnet_ni *ni;
+ struct list_head *tmp;
+
+ LASSERT(cpt != LNET_LOCK_EX);
+
+ list_for_each(tmp, &the_lnet.ln_nis) {
+ ni = list_entry(tmp, lnet_ni_t, ni_list);
+
+ if (ni->ni_nid == nid) {
+ lnet_ni_addref_locked(ni, cpt);
+ return ni;
+ }
+ }
+
+ return NULL;
+}
+
+int
+lnet_islocalnid(lnet_nid_t nid)
+{
+ struct lnet_ni *ni;
+ int cpt;
+
+ cpt = lnet_net_lock_current();
+ ni = lnet_nid2ni_locked(nid, cpt);
+ if (ni != NULL)
+ lnet_ni_decref_locked(ni, cpt);
+ lnet_net_unlock(cpt);
+
+ return ni != NULL;
+}
+
+int
+lnet_count_acceptor_nis(void)
+{
+ /* Return the # of NIs that need the acceptor. */
+ int count = 0;
+ struct list_head *tmp;
+ struct lnet_ni *ni;
+ int cpt;
+
+ cpt = lnet_net_lock_current();
+ list_for_each(tmp, &the_lnet.ln_nis) {
+ ni = list_entry(tmp, lnet_ni_t, ni_list);
+
+ if (ni->ni_lnd->lnd_accept != NULL)
+ count++;
+ }
+
+ lnet_net_unlock(cpt);
+
+ return count;
+}
+
+static int
+lnet_ni_tq_credits(lnet_ni_t *ni)
+{
+ int credits;
+
+ LASSERT(ni->ni_ncpts >= 1);
+
+ if (ni->ni_ncpts == 1)
+ return ni->ni_maxtxcredits;
+
+ credits = ni->ni_maxtxcredits / ni->ni_ncpts;
+ credits = max(credits, 8 * ni->ni_peertxcredits);
+ credits = min(credits, ni->ni_maxtxcredits);
+
+ return credits;
+}
+
+static void
+lnet_shutdown_lndnis(void)
+{
+ int i;
+ int islo;
+ lnet_ni_t *ni;
+
+ /* NB called holding the global mutex */
+
+ /* All quiet on the API front */
+ LASSERT(!the_lnet.ln_shutdown);
+ LASSERT(the_lnet.ln_refcount == 0);
+ LASSERT(list_empty(&the_lnet.ln_nis_zombie));
+
+ lnet_net_lock(LNET_LOCK_EX);
+ the_lnet.ln_shutdown = 1; /* flag shutdown */
+
+ /* Unlink NIs from the global table */
+ while (!list_empty(&the_lnet.ln_nis)) {
+ ni = list_entry(the_lnet.ln_nis.next,
+ lnet_ni_t, ni_list);
+ /* move it to zombie list and nobody can find it anymore */
+ list_move(&ni->ni_list, &the_lnet.ln_nis_zombie);
+ lnet_ni_decref_locked(ni, 0); /* drop ln_nis' ref */
+
+ if (!list_empty(&ni->ni_cptlist)) {
+ list_del_init(&ni->ni_cptlist);
+ lnet_ni_decref_locked(ni, 0);
+ }
+ }
+
+ /* Drop the cached eqwait NI. */
+ if (the_lnet.ln_eq_waitni != NULL) {
+ lnet_ni_decref_locked(the_lnet.ln_eq_waitni, 0);
+ the_lnet.ln_eq_waitni = NULL;
+ }
+
+ /* Drop the cached loopback NI. */
+ if (the_lnet.ln_loni != NULL) {
+ lnet_ni_decref_locked(the_lnet.ln_loni, 0);
+ the_lnet.ln_loni = NULL;
+ }
+
+ lnet_net_unlock(LNET_LOCK_EX);
+
+ /* Clear lazy portals and drop delayed messages which hold refs
+ * on their lnet_msg_t::msg_rxpeer */
+ for (i = 0; i < the_lnet.ln_nportals; i++)
+ LNetClearLazyPortal(i);
+
+ /* Clear the peer table and wait for all peers to go (they hold refs on
+ * their NIs) */
+ lnet_peer_tables_cleanup();
+
+ lnet_net_lock(LNET_LOCK_EX);
+ /* Now wait for the NI's I just nuked to show up on ln_zombie_nis
+ * and shut them down in guaranteed thread context */
+ i = 2;
+ while (!list_empty(&the_lnet.ln_nis_zombie)) {
+ int *ref;
+ int j;
+
+ ni = list_entry(the_lnet.ln_nis_zombie.next,
+ lnet_ni_t, ni_list);
+ list_del_init(&ni->ni_list);
+ cfs_percpt_for_each(ref, j, ni->ni_refs) {
+ if (*ref == 0)
+ continue;
+ /* still busy, add it back to zombie list */
+ list_add(&ni->ni_list, &the_lnet.ln_nis_zombie);
+ break;
+ }
+
+ if (!list_empty(&ni->ni_list)) {
+ lnet_net_unlock(LNET_LOCK_EX);
+ ++i;
+ if ((i & (-i)) == i) {
+ CDEBUG(D_WARNING, "Waiting for zombie LNI %s\n",
+ libcfs_nid2str(ni->ni_nid));
+ }
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ schedule_timeout(cfs_time_seconds(1));
+ lnet_net_lock(LNET_LOCK_EX);
+ continue;
+ }
+
+ ni->ni_lnd->lnd_refcount--;
+ lnet_net_unlock(LNET_LOCK_EX);
+
+ islo = ni->ni_lnd->lnd_type == LOLND;
+
+ LASSERT(!in_interrupt());
+ (ni->ni_lnd->lnd_shutdown)(ni);
+
+ /* can't deref lnd anymore now; it might have unregistered
+ * itself... */
+
+ if (!islo)
+ CDEBUG(D_LNI, "Removed LNI %s\n",
+ libcfs_nid2str(ni->ni_nid));
+
+ lnet_ni_free(ni);
+ i = 2;
+
+ lnet_net_lock(LNET_LOCK_EX);
+ }
+
+ the_lnet.ln_shutdown = 0;
+ lnet_net_unlock(LNET_LOCK_EX);
+
+ if (the_lnet.ln_network_tokens != NULL) {
+ LIBCFS_FREE(the_lnet.ln_network_tokens,
+ the_lnet.ln_network_tokens_nob);
+ the_lnet.ln_network_tokens = NULL;
+ }
+}
+
+static int
+lnet_startup_lndnis(void)
+{
+ lnd_t *lnd;
+ struct lnet_ni *ni;
+ struct lnet_tx_queue *tq;
+ struct list_head nilist;
+ int i;
+ int rc = 0;
+ int lnd_type;
+ int nicount = 0;
+ char *nets = lnet_get_networks();
+
+ INIT_LIST_HEAD(&nilist);
+
+ if (nets == NULL)
+ goto failed;
+
+ rc = lnet_parse_networks(&nilist, nets);
+ if (rc != 0)
+ goto failed;
+
+ while (!list_empty(&nilist)) {
+ ni = list_entry(nilist.next, lnet_ni_t, ni_list);
+ lnd_type = LNET_NETTYP(LNET_NIDNET(ni->ni_nid));
+
+ LASSERT(libcfs_isknown_lnd(lnd_type));
+
+ if (lnd_type == CIBLND ||
+ lnd_type == OPENIBLND ||
+ lnd_type == IIBLND ||
+ lnd_type == VIBLND) {
+ CERROR("LND %s obsoleted\n",
+ libcfs_lnd2str(lnd_type));
+ goto failed;
+ }
+
+ LNET_MUTEX_LOCK(&the_lnet.ln_lnd_mutex);
+ lnd = lnet_find_lnd_by_type(lnd_type);
+
+ if (lnd == NULL) {
+ LNET_MUTEX_UNLOCK(&the_lnet.ln_lnd_mutex);
+ rc = request_module("%s",
+ libcfs_lnd2modname(lnd_type));
+ LNET_MUTEX_LOCK(&the_lnet.ln_lnd_mutex);
+
+ lnd = lnet_find_lnd_by_type(lnd_type);
+ if (lnd == NULL) {
+ LNET_MUTEX_UNLOCK(&the_lnet.ln_lnd_mutex);
+ CERROR("Can't load LND %s, module %s, rc=%d\n",
+ libcfs_lnd2str(lnd_type),
+ libcfs_lnd2modname(lnd_type), rc);
+ goto failed;
+ }
+ }
+
+ lnet_net_lock(LNET_LOCK_EX);
+ lnd->lnd_refcount++;
+ lnet_net_unlock(LNET_LOCK_EX);
+
+ ni->ni_lnd = lnd;
+
+ rc = (lnd->lnd_startup)(ni);
+
+ LNET_MUTEX_UNLOCK(&the_lnet.ln_lnd_mutex);
+
+ if (rc != 0) {
+ LCONSOLE_ERROR_MSG(0x105, "Error %d starting up LNI %s\n",
+ rc, libcfs_lnd2str(lnd->lnd_type));
+ lnet_net_lock(LNET_LOCK_EX);
+ lnd->lnd_refcount--;
+ lnet_net_unlock(LNET_LOCK_EX);
+ goto failed;
+ }
+
+ LASSERT(ni->ni_peertimeout <= 0 || lnd->lnd_query != NULL);
+
+ list_del(&ni->ni_list);
+
+ lnet_net_lock(LNET_LOCK_EX);
+ /* refcount for ln_nis */
+ lnet_ni_addref_locked(ni, 0);
+ list_add_tail(&ni->ni_list, &the_lnet.ln_nis);
+ if (ni->ni_cpts != NULL) {
+ list_add_tail(&ni->ni_cptlist,
+ &the_lnet.ln_nis_cpt);
+ lnet_ni_addref_locked(ni, 0);
+ }
+
+ lnet_net_unlock(LNET_LOCK_EX);
+
+ if (lnd->lnd_type == LOLND) {
+ lnet_ni_addref(ni);
+ LASSERT(the_lnet.ln_loni == NULL);
+ the_lnet.ln_loni = ni;
+ continue;
+ }
+
+ if (ni->ni_peertxcredits == 0 ||
+ ni->ni_maxtxcredits == 0) {
+ LCONSOLE_ERROR_MSG(0x107, "LNI %s has no %scredits\n",
+ libcfs_lnd2str(lnd->lnd_type),
+ ni->ni_peertxcredits == 0 ?
+ "" : "per-peer ");
+ goto failed;
+ }
+
+ cfs_percpt_for_each(tq, i, ni->ni_tx_queues) {
+ tq->tq_credits_min =
+ tq->tq_credits_max =
+ tq->tq_credits = lnet_ni_tq_credits(ni);
+ }
+
+ CDEBUG(D_LNI, "Added LNI %s [%d/%d/%d/%d]\n",
+ libcfs_nid2str(ni->ni_nid), ni->ni_peertxcredits,
+ lnet_ni_tq_credits(ni) * LNET_CPT_NUMBER,
+ ni->ni_peerrtrcredits, ni->ni_peertimeout);
+
+ nicount++;
+ }
+
+ if (the_lnet.ln_eq_waitni != NULL && nicount > 1) {
+ lnd_type = the_lnet.ln_eq_waitni->ni_lnd->lnd_type;
+ LCONSOLE_ERROR_MSG(0x109, "LND %s can only run single-network\n",
+ libcfs_lnd2str(lnd_type));
+ goto failed;
+ }
+
+ return 0;
+
+ failed:
+ lnet_shutdown_lndnis();
+
+ while (!list_empty(&nilist)) {
+ ni = list_entry(nilist.next, lnet_ni_t, ni_list);
+ list_del(&ni->ni_list);
+ lnet_ni_free(ni);
+ }
+
+ return -ENETDOWN;
+}
+
+/**
+ * Initialize LNet library.
+ *
+ * Only userspace program needs to call this function - it's automatically
+ * called in the kernel at module loading time. Caller has to call LNetFini()
+ * after a call to LNetInit(), if and only if the latter returned 0. It must
+ * be called exactly once.
+ *
+ * \return 0 on success, and -ve on failures.
+ */
+int
+LNetInit(void)
+{
+ int rc;
+
+ lnet_assert_wire_constants();
+ LASSERT(!the_lnet.ln_init);
+
+ memset(&the_lnet, 0, sizeof(the_lnet));
+
+ /* refer to global cfs_cpt_table for now */
+ the_lnet.ln_cpt_table = cfs_cpt_table;
+ the_lnet.ln_cpt_number = cfs_cpt_number(cfs_cpt_table);
+
+ LASSERT(the_lnet.ln_cpt_number > 0);
+ if (the_lnet.ln_cpt_number > LNET_CPT_MAX) {
+ /* we are under risk of consuming all lh_cookie */
+ CERROR("Can't have %d CPTs for LNet (max allowed is %d), please change setting of CPT-table and retry\n",
+ the_lnet.ln_cpt_number, LNET_CPT_MAX);
+ return -1;
+ }
+
+ while ((1 << the_lnet.ln_cpt_bits) < the_lnet.ln_cpt_number)
+ the_lnet.ln_cpt_bits++;
+
+ rc = lnet_create_locks();
+ if (rc != 0) {
+ CERROR("Can't create LNet global locks: %d\n", rc);
+ return -1;
+ }
+
+ the_lnet.ln_refcount = 0;
+ the_lnet.ln_init = 1;
+ LNetInvalidateHandle(&the_lnet.ln_rc_eqh);
+ INIT_LIST_HEAD(&the_lnet.ln_lnds);
+ INIT_LIST_HEAD(&the_lnet.ln_rcd_zombie);
+ INIT_LIST_HEAD(&the_lnet.ln_rcd_deathrow);
+
+ /* The hash table size is the number of bits it takes to express the set
+ * ln_num_routes, minus 1 (better to under estimate than over so we
+ * don't waste memory). */
+ if (rnet_htable_size <= 0)
+ rnet_htable_size = LNET_REMOTE_NETS_HASH_DEFAULT;
+ else if (rnet_htable_size > LNET_REMOTE_NETS_HASH_MAX)
+ rnet_htable_size = LNET_REMOTE_NETS_HASH_MAX;
+ the_lnet.ln_remote_nets_hbits = max_t(int, 1,
+ order_base_2(rnet_htable_size) - 1);
+
+ /* All LNDs apart from the LOLND are in separate modules. They
+ * register themselves when their module loads, and unregister
+ * themselves when their module is unloaded. */
+ lnet_register_lnd(&the_lolnd);
+ return 0;
+}
+EXPORT_SYMBOL(LNetInit);
+
+/**
+ * Finalize LNet library.
+ *
+ * Only userspace program needs to call this function. It can be called
+ * at most once.
+ *
+ * \pre LNetInit() called with success.
+ * \pre All LNet users called LNetNIFini() for matching LNetNIInit() calls.
+ */
+void
+LNetFini(void)
+{
+ LASSERT(the_lnet.ln_init);
+ LASSERT(the_lnet.ln_refcount == 0);
+
+ while (!list_empty(&the_lnet.ln_lnds))
+ lnet_unregister_lnd(list_entry(the_lnet.ln_lnds.next,
+ lnd_t, lnd_list));
+ lnet_destroy_locks();
+
+ the_lnet.ln_init = 0;
+}
+EXPORT_SYMBOL(LNetFini);
+
+/**
+ * Set LNet PID and start LNet interfaces, routing, and forwarding.
+ *
+ * Userspace program should call this after a successful call to LNetInit().
+ * Users must call this function at least once before any other functions.
+ * For each successful call there must be a corresponding call to
+ * LNetNIFini(). For subsequent calls to LNetNIInit(), \a requested_pid is
+ * ignored.
+ *
+ * The PID used by LNet may be different from the one requested.
+ * See LNetGetId().
+ *
+ * \param requested_pid PID requested by the caller.
+ *
+ * \return >= 0 on success, and < 0 error code on failures.
+ */
+int
+LNetNIInit(lnet_pid_t requested_pid)
+{
+ int im_a_router = 0;
+ int rc;
+
+ LNET_MUTEX_LOCK(&the_lnet.ln_api_mutex);
+
+ LASSERT(the_lnet.ln_init);
+ CDEBUG(D_OTHER, "refs %d\n", the_lnet.ln_refcount);
+
+ if (the_lnet.ln_refcount > 0) {
+ rc = the_lnet.ln_refcount++;
+ goto out;
+ }
+
+ lnet_get_tunables();
+
+ if (requested_pid == LNET_PID_ANY) {
+ /* Don't instantiate LNET just for me */
+ rc = -ENETDOWN;
+ goto failed0;
+ }
+
+ rc = lnet_prepare(requested_pid);
+ if (rc != 0)
+ goto failed0;
+
+ rc = lnet_startup_lndnis();
+ if (rc != 0)
+ goto failed1;
+
+ rc = lnet_parse_routes(lnet_get_routes(), &im_a_router);
+ if (rc != 0)
+ goto failed2;
+
+ rc = lnet_check_routes();
+ if (rc != 0)
+ goto failed2;
+
+ rc = lnet_rtrpools_alloc(im_a_router);
+ if (rc != 0)
+ goto failed2;
+
+ rc = lnet_acceptor_start();
+ if (rc != 0)
+ goto failed2;
+
+ the_lnet.ln_refcount = 1;
+ /* Now I may use my own API functions... */
+
+ /* NB router checker needs the_lnet.ln_ping_info in
+ * lnet_router_checker -> lnet_update_ni_status_locked */
+ rc = lnet_ping_target_init();
+ if (rc != 0)
+ goto failed3;
+
+ rc = lnet_router_checker_start();
+ if (rc != 0)
+ goto failed4;
+
+ lnet_proc_init();
+ goto out;
+
+ failed4:
+ lnet_ping_target_fini();
+ failed3:
+ the_lnet.ln_refcount = 0;
+ lnet_acceptor_stop();
+ failed2:
+ lnet_destroy_routes();
+ lnet_shutdown_lndnis();
+ failed1:
+ lnet_unprepare();
+ failed0:
+ LASSERT(rc < 0);
+ out:
+ LNET_MUTEX_UNLOCK(&the_lnet.ln_api_mutex);
+ return rc;
+}
+EXPORT_SYMBOL(LNetNIInit);
+
+/**
+ * Stop LNet interfaces, routing, and forwarding.
+ *
+ * Users must call this function once for each successful call to LNetNIInit().
+ * Once the LNetNIFini() operation has been started, the results of pending
+ * API operations are undefined.
+ *
+ * \return always 0 for current implementation.
+ */
+int
+LNetNIFini(void)
+{
+ LNET_MUTEX_LOCK(&the_lnet.ln_api_mutex);
+
+ LASSERT(the_lnet.ln_init);
+ LASSERT(the_lnet.ln_refcount > 0);
+
+ if (the_lnet.ln_refcount != 1) {
+ the_lnet.ln_refcount--;
+ } else {
+ LASSERT(!the_lnet.ln_niinit_self);
+
+ lnet_proc_fini();
+ lnet_router_checker_stop();
+ lnet_ping_target_fini();
+
+ /* Teardown fns that use my own API functions BEFORE here */
+ the_lnet.ln_refcount = 0;
+
+ lnet_acceptor_stop();
+ lnet_destroy_routes();
+ lnet_shutdown_lndnis();
+ lnet_unprepare();
+ }
+
+ LNET_MUTEX_UNLOCK(&the_lnet.ln_api_mutex);
+ return 0;
+}
+EXPORT_SYMBOL(LNetNIFini);
+
+/**
+ * This is an ugly hack to export IOC_LIBCFS_DEBUG_PEER and
+ * IOC_LIBCFS_PORTALS_COMPATIBILITY commands to users, by tweaking the LNet
+ * internal ioctl handler.
+ *
+ * IOC_LIBCFS_PORTALS_COMPATIBILITY is now deprecated, don't use it.
+ *
+ * \param cmd IOC_LIBCFS_DEBUG_PEER to print debugging data about a peer.
+ * The data will be printed to system console. Don't use it excessively.
+ * \param arg A pointer to lnet_process_id_t, process ID of the peer.
+ *
+ * \return Always return 0 when called by users directly (i.e., not via ioctl).
+ */
+int
+LNetCtl(unsigned int cmd, void *arg)
+{
+ struct libcfs_ioctl_data *data = arg;
+ lnet_process_id_t id = {0};
+ lnet_ni_t *ni;
+ int rc;
+
+ LASSERT(the_lnet.ln_init);
+ LASSERT(the_lnet.ln_refcount > 0);
+
+ switch (cmd) {
+ case IOC_LIBCFS_GET_NI:
+ rc = LNetGetId(data->ioc_count, &id);
+ data->ioc_nid = id.nid;
+ return rc;
+
+ case IOC_LIBCFS_FAIL_NID:
+ return lnet_fail_nid(data->ioc_nid, data->ioc_count);
+
+ case IOC_LIBCFS_ADD_ROUTE:
+ rc = lnet_add_route(data->ioc_net, data->ioc_count,
+ data->ioc_nid, data->ioc_priority);
+ return (rc != 0) ? rc : lnet_check_routes();
+
+ case IOC_LIBCFS_DEL_ROUTE:
+ return lnet_del_route(data->ioc_net, data->ioc_nid);
+
+ case IOC_LIBCFS_GET_ROUTE:
+ return lnet_get_route(data->ioc_count,
+ &data->ioc_net, &data->ioc_count,
+ &data->ioc_nid, &data->ioc_flags,
+ &data->ioc_priority);
+ case IOC_LIBCFS_NOTIFY_ROUTER:
+ return lnet_notify(NULL, data->ioc_nid, data->ioc_flags,
+ cfs_time_current() -
+ cfs_time_seconds(get_seconds() -
+ (time_t)data->ioc_u64[0]));
+
+ case IOC_LIBCFS_PORTALS_COMPATIBILITY:
+ /* This can be removed once lustre stops calling it */
+ return 0;
+
+ case IOC_LIBCFS_LNET_DIST:
+ rc = LNetDist(data->ioc_nid, &data->ioc_nid, &data->ioc_u32[1]);
+ if (rc < 0 && rc != -EHOSTUNREACH)
+ return rc;
+
+ data->ioc_u32[0] = rc;
+ return 0;
+
+ case IOC_LIBCFS_TESTPROTOCOMPAT:
+ lnet_net_lock(LNET_LOCK_EX);
+ the_lnet.ln_testprotocompat = data->ioc_flags;
+ lnet_net_unlock(LNET_LOCK_EX);
+ return 0;
+
+ case IOC_LIBCFS_PING:
+ id.nid = data->ioc_nid;
+ id.pid = data->ioc_u32[0];
+ rc = lnet_ping(id, data->ioc_u32[1], /* timeout */
+ (lnet_process_id_t *)data->ioc_pbuf1,
+ data->ioc_plen1/sizeof(lnet_process_id_t));
+ if (rc < 0)
+ return rc;
+ data->ioc_count = rc;
+ return 0;
+
+ case IOC_LIBCFS_DEBUG_PEER: {
+ /* CAVEAT EMPTOR: this one designed for calling directly; not
+ * via an ioctl */
+ id = *((lnet_process_id_t *) arg);
+
+ lnet_debug_peer(id.nid);
+
+ ni = lnet_net2ni(LNET_NIDNET(id.nid));
+ if (ni == NULL) {
+ CDEBUG(D_WARNING, "No NI for %s\n", libcfs_id2str(id));
+ } else {
+ if (ni->ni_lnd->lnd_ctl == NULL) {
+ CDEBUG(D_WARNING, "No ctl for %s\n",
+ libcfs_id2str(id));
+ } else {
+ (void)ni->ni_lnd->lnd_ctl(ni, cmd, arg);
+ }
+
+ lnet_ni_decref(ni);
+ }
+ return 0;
+ }
+
+ default:
+ ni = lnet_net2ni(data->ioc_net);
+ if (ni == NULL)
+ return -EINVAL;
+
+ if (ni->ni_lnd->lnd_ctl == NULL)
+ rc = -EINVAL;
+ else
+ rc = ni->ni_lnd->lnd_ctl(ni, cmd, arg);
+
+ lnet_ni_decref(ni);
+ return rc;
+ }
+ /* not reached */
+}
+EXPORT_SYMBOL(LNetCtl);
+
+/**
+ * Retrieve the lnet_process_id_t ID of LNet interface at \a index. Note that
+ * all interfaces share a same PID, as requested by LNetNIInit().
+ *
+ * \param index Index of the interface to look up.
+ * \param id On successful return, this location will hold the
+ * lnet_process_id_t ID of the interface.
+ *
+ * \retval 0 If an interface exists at \a index.
+ * \retval -ENOENT If no interface has been found.
+ */
+int
+LNetGetId(unsigned int index, lnet_process_id_t *id)
+{
+ struct lnet_ni *ni;
+ struct list_head *tmp;
+ int cpt;
+ int rc = -ENOENT;
+
+ LASSERT(the_lnet.ln_init);
+
+ /* LNetNI initilization failed? */
+ if (the_lnet.ln_refcount == 0)
+ return rc;
+
+ cpt = lnet_net_lock_current();
+
+ list_for_each(tmp, &the_lnet.ln_nis) {
+ if (index-- != 0)
+ continue;
+
+ ni = list_entry(tmp, lnet_ni_t, ni_list);
+
+ id->nid = ni->ni_nid;
+ id->pid = the_lnet.ln_pid;
+ rc = 0;
+ break;
+ }
+
+ lnet_net_unlock(cpt);
+ return rc;
+}
+EXPORT_SYMBOL(LNetGetId);
+
+/**
+ * Print a string representation of handle \a h into buffer \a str of
+ * \a len bytes.
+ */
+void
+LNetSnprintHandle(char *str, int len, lnet_handle_any_t h)
+{
+ snprintf(str, len, "%#llx", h.cookie);
+}
+EXPORT_SYMBOL(LNetSnprintHandle);
+
+static int
+lnet_create_ping_info(void)
+{
+ int i;
+ int n;
+ int rc;
+ unsigned int infosz;
+ lnet_ni_t *ni;
+ lnet_process_id_t id;
+ lnet_ping_info_t *pinfo;
+
+ for (n = 0; ; n++) {
+ rc = LNetGetId(n, &id);
+ if (rc == -ENOENT)
+ break;
+
+ LASSERT(rc == 0);
+ }
+
+ infosz = offsetof(lnet_ping_info_t, pi_ni[n]);
+ LIBCFS_ALLOC(pinfo, infosz);
+ if (pinfo == NULL) {
+ CERROR("Can't allocate ping info[%d]\n", n);
+ return -ENOMEM;
+ }
+
+ pinfo->pi_nnis = n;
+ pinfo->pi_pid = the_lnet.ln_pid;
+ pinfo->pi_magic = LNET_PROTO_PING_MAGIC;
+ pinfo->pi_features = LNET_PING_FEAT_NI_STATUS;
+
+ for (i = 0; i < n; i++) {
+ lnet_ni_status_t *ns = &pinfo->pi_ni[i];
+
+ rc = LNetGetId(i, &id);
+ LASSERT(rc == 0);
+
+ ns->ns_nid = id.nid;
+ ns->ns_status = LNET_NI_STATUS_UP;
+
+ lnet_net_lock(0);
+
+ ni = lnet_nid2ni_locked(id.nid, 0);
+ LASSERT(ni != NULL);
+
+ lnet_ni_lock(ni);
+ LASSERT(ni->ni_status == NULL);
+ ni->ni_status = ns;
+ lnet_ni_unlock(ni);
+
+ lnet_ni_decref_locked(ni, 0);
+ lnet_net_unlock(0);
+ }
+
+ the_lnet.ln_ping_info = pinfo;
+ return 0;
+}
+
+static void
+lnet_destroy_ping_info(void)
+{
+ struct lnet_ni *ni;
+
+ lnet_net_lock(0);
+
+ list_for_each_entry(ni, &the_lnet.ln_nis, ni_list) {
+ lnet_ni_lock(ni);
+ ni->ni_status = NULL;
+ lnet_ni_unlock(ni);
+ }
+
+ lnet_net_unlock(0);
+
+ LIBCFS_FREE(the_lnet.ln_ping_info,
+ offsetof(lnet_ping_info_t,
+ pi_ni[the_lnet.ln_ping_info->pi_nnis]));
+ the_lnet.ln_ping_info = NULL;
+}
+
+int
+lnet_ping_target_init(void)
+{
+ lnet_md_t md = { NULL };
+ lnet_handle_me_t meh;
+ lnet_process_id_t id;
+ int rc;
+ int rc2;
+ int infosz;
+
+ rc = lnet_create_ping_info();
+ if (rc != 0)
+ return rc;
+
+ /* We can have a tiny EQ since we only need to see the unlink event on
+ * teardown, which by definition is the last one! */
+ rc = LNetEQAlloc(2, LNET_EQ_HANDLER_NONE, &the_lnet.ln_ping_target_eq);
+ if (rc != 0) {
+ CERROR("Can't allocate ping EQ: %d\n", rc);
+ goto failed_0;
+ }
+
+ memset(&id, 0, sizeof(lnet_process_id_t));
+ id.nid = LNET_NID_ANY;
+ id.pid = LNET_PID_ANY;
+
+ rc = LNetMEAttach(LNET_RESERVED_PORTAL, id,
+ LNET_PROTO_PING_MATCHBITS, 0,
+ LNET_UNLINK, LNET_INS_AFTER,
+ &meh);
+ if (rc != 0) {
+ CERROR("Can't create ping ME: %d\n", rc);
+ goto failed_1;
+ }
+
+ /* initialize md content */
+ infosz = offsetof(lnet_ping_info_t,
+ pi_ni[the_lnet.ln_ping_info->pi_nnis]);
+ md.start = the_lnet.ln_ping_info;
+ md.length = infosz;
+ md.threshold = LNET_MD_THRESH_INF;
+ md.max_size = 0;
+ md.options = LNET_MD_OP_GET | LNET_MD_TRUNCATE |
+ LNET_MD_MANAGE_REMOTE;
+ md.user_ptr = NULL;
+ md.eq_handle = the_lnet.ln_ping_target_eq;
+
+ rc = LNetMDAttach(meh, md,
+ LNET_RETAIN,
+ &the_lnet.ln_ping_target_md);
+ if (rc != 0) {
+ CERROR("Can't attach ping MD: %d\n", rc);
+ goto failed_2;
+ }
+
+ return 0;
+
+ failed_2:
+ rc2 = LNetMEUnlink(meh);
+ LASSERT(rc2 == 0);
+ failed_1:
+ rc2 = LNetEQFree(the_lnet.ln_ping_target_eq);
+ LASSERT(rc2 == 0);
+ failed_0:
+ lnet_destroy_ping_info();
+ return rc;
+}
+
+void
+lnet_ping_target_fini(void)
+{
+ lnet_event_t event;
+ int rc;
+ int which;
+ int timeout_ms = 1000;
+ sigset_t blocked = cfs_block_allsigs();
+
+ LNetMDUnlink(the_lnet.ln_ping_target_md);
+ /* NB md could be busy; this just starts the unlink */
+
+ for (;;) {
+ rc = LNetEQPoll(&the_lnet.ln_ping_target_eq, 1,
+ timeout_ms, &event, &which);
+
+ /* I expect overflow... */
+ LASSERT(rc >= 0 || rc == -EOVERFLOW);
+
+ if (rc == 0) {
+ /* timed out: provide a diagnostic */
+ CWARN("Still waiting for ping MD to unlink\n");
+ timeout_ms *= 2;
+ continue;
+ }
+
+ /* Got a valid event */
+ if (event.unlinked)
+ break;
+ }
+
+ rc = LNetEQFree(the_lnet.ln_ping_target_eq);
+ LASSERT(rc == 0);
+ lnet_destroy_ping_info();
+ cfs_restore_sigs(blocked);
+}
+
+int
+lnet_ping(lnet_process_id_t id, int timeout_ms, lnet_process_id_t *ids, int n_ids)
+{
+ lnet_handle_eq_t eqh;
+ lnet_handle_md_t mdh;
+ lnet_event_t event;
+ lnet_md_t md = { NULL };
+ int which;
+ int unlinked = 0;
+ int replied = 0;
+ const int a_long_time = 60000; /* mS */
+ int infosz = offsetof(lnet_ping_info_t, pi_ni[n_ids]);
+ lnet_ping_info_t *info;
+ lnet_process_id_t tmpid;
+ int i;
+ int nob;
+ int rc;
+ int rc2;
+ sigset_t blocked;
+
+ if (n_ids <= 0 ||
+ id.nid == LNET_NID_ANY ||
+ timeout_ms > 500000 || /* arbitrary limit! */
+ n_ids > 20) /* arbitrary limit! */
+ return -EINVAL;
+
+ if (id.pid == LNET_PID_ANY)
+ id.pid = LUSTRE_SRV_LNET_PID;
+
+ LIBCFS_ALLOC(info, infosz);
+ if (info == NULL)
+ return -ENOMEM;
+
+ /* NB 2 events max (including any unlink event) */
+ rc = LNetEQAlloc(2, LNET_EQ_HANDLER_NONE, &eqh);
+ if (rc != 0) {
+ CERROR("Can't allocate EQ: %d\n", rc);
+ goto out_0;
+ }
+
+ /* initialize md content */
+ md.start = info;
+ md.length = infosz;
+ md.threshold = 2; /*GET/REPLY*/
+ md.max_size = 0;
+ md.options = LNET_MD_TRUNCATE;
+ md.user_ptr = NULL;
+ md.eq_handle = eqh;
+
+ rc = LNetMDBind(md, LNET_UNLINK, &mdh);
+ if (rc != 0) {
+ CERROR("Can't bind MD: %d\n", rc);
+ goto out_1;
+ }
+
+ rc = LNetGet(LNET_NID_ANY, mdh, id,
+ LNET_RESERVED_PORTAL,
+ LNET_PROTO_PING_MATCHBITS, 0);
+
+ if (rc != 0) {
+ /* Don't CERROR; this could be deliberate! */
+
+ rc2 = LNetMDUnlink(mdh);
+ LASSERT(rc2 == 0);
+
+ /* NB must wait for the UNLINK event below... */
+ unlinked = 1;
+ timeout_ms = a_long_time;
+ }
+
+ do {
+ /* MUST block for unlink to complete */
+ if (unlinked)
+ blocked = cfs_block_allsigs();
+
+ rc2 = LNetEQPoll(&eqh, 1, timeout_ms, &event, &which);
+
+ if (unlinked)
+ cfs_restore_sigs(blocked);
+
+ CDEBUG(D_NET, "poll %d(%d %d)%s\n", rc2,
+ (rc2 <= 0) ? -1 : event.type,
+ (rc2 <= 0) ? -1 : event.status,
+ (rc2 > 0 && event.unlinked) ? " unlinked" : "");
+
+ LASSERT(rc2 != -EOVERFLOW); /* can't miss anything */
+
+ if (rc2 <= 0 || event.status != 0) {
+ /* timeout or error */
+ if (!replied && rc == 0)
+ rc = (rc2 < 0) ? rc2 :
+ (rc2 == 0) ? -ETIMEDOUT :
+ event.status;
+
+ if (!unlinked) {
+ /* Ensure completion in finite time... */
+ LNetMDUnlink(mdh);
+ /* No assertion (racing with network) */
+ unlinked = 1;
+ timeout_ms = a_long_time;
+ } else if (rc2 == 0) {
+ /* timed out waiting for unlink */
+ CWARN("ping %s: late network completion\n",
+ libcfs_id2str(id));
+ }
+ } else if (event.type == LNET_EVENT_REPLY) {
+ replied = 1;
+ rc = event.mlength;
+ }
+
+ } while (rc2 <= 0 || !event.unlinked);
+
+ if (!replied) {
+ if (rc >= 0)
+ CWARN("%s: Unexpected rc >= 0 but no reply!\n",
+ libcfs_id2str(id));
+ rc = -EIO;
+ goto out_1;
+ }
+
+ nob = rc;
+ LASSERT(nob >= 0 && nob <= infosz);
+
+ rc = -EPROTO; /* if I can't parse... */
+
+ if (nob < 8) {
+ /* can't check magic/version */
+ CERROR("%s: ping info too short %d\n",
+ libcfs_id2str(id), nob);
+ goto out_1;
+ }
+
+ if (info->pi_magic == __swab32(LNET_PROTO_PING_MAGIC)) {
+ lnet_swap_pinginfo(info);
+ } else if (info->pi_magic != LNET_PROTO_PING_MAGIC) {
+ CERROR("%s: Unexpected magic %08x\n",
+ libcfs_id2str(id), info->pi_magic);
+ goto out_1;
+ }
+
+ if ((info->pi_features & LNET_PING_FEAT_NI_STATUS) == 0) {
+ CERROR("%s: ping w/o NI status: 0x%x\n",
+ libcfs_id2str(id), info->pi_features);
+ goto out_1;
+ }
+
+ if (nob < offsetof(lnet_ping_info_t, pi_ni[0])) {
+ CERROR("%s: Short reply %d(%d min)\n", libcfs_id2str(id),
+ nob, (int)offsetof(lnet_ping_info_t, pi_ni[0]));
+ goto out_1;
+ }
+
+ if (info->pi_nnis < n_ids)
+ n_ids = info->pi_nnis;
+
+ if (nob < offsetof(lnet_ping_info_t, pi_ni[n_ids])) {
+ CERROR("%s: Short reply %d(%d expected)\n", libcfs_id2str(id),
+ nob, (int)offsetof(lnet_ping_info_t, pi_ni[n_ids]));
+ goto out_1;
+ }
+
+ rc = -EFAULT; /* If I SEGV... */
+
+ memset(&tmpid, 0, sizeof(tmpid));
+ for (i = 0; i < n_ids; i++) {
+ tmpid.pid = info->pi_pid;
+ tmpid.nid = info->pi_ni[i].ns_nid;
+ if (copy_to_user(&ids[i], &tmpid, sizeof(tmpid)))
+ goto out_1;
+ }
+ rc = info->pi_nnis;
+
+ out_1:
+ rc2 = LNetEQFree(eqh);
+ if (rc2 != 0)
+ CERROR("rc2 %d\n", rc2);
+ LASSERT(rc2 == 0);
+
+ out_0:
+ LIBCFS_FREE(info, infosz);
+ return rc;
+}
diff --git a/drivers/staging/lustre/lnet/lnet/config.c b/drivers/staging/lustre/lnet/lnet/config.c
new file mode 100644
index 000000000..2dc4c4a1a
--- /dev/null
+++ b/drivers/staging/lustre/lnet/lnet/config.c
@@ -0,0 +1,1292 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2012, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ */
+
+#define DEBUG_SUBSYSTEM S_LNET
+#include "../../include/linux/lnet/lib-lnet.h"
+
+struct lnet_text_buf_t { /* tmp struct for parsing routes */
+ struct list_head ltb_list; /* stash on lists */
+ int ltb_size; /* allocated size */
+ char ltb_text[0]; /* text buffer */
+};
+
+static int lnet_tbnob; /* track text buf allocation */
+#define LNET_MAX_TEXTBUF_NOB (64<<10) /* bound allocation */
+#define LNET_SINGLE_TEXTBUF_NOB (4<<10)
+
+static void
+lnet_syntax(char *name, char *str, int offset, int width)
+{
+ static char dots[LNET_SINGLE_TEXTBUF_NOB];
+ static char dashes[LNET_SINGLE_TEXTBUF_NOB];
+
+ memset(dots, '.', sizeof(dots));
+ dots[sizeof(dots)-1] = 0;
+ memset(dashes, '-', sizeof(dashes));
+ dashes[sizeof(dashes)-1] = 0;
+
+ LCONSOLE_ERROR_MSG(0x10f, "Error parsing '%s=\"%s\"'\n", name, str);
+ LCONSOLE_ERROR_MSG(0x110, "here...........%.*s..%.*s|%.*s|\n",
+ (int)strlen(name), dots, offset, dots,
+ (width < 1) ? 0 : width - 1, dashes);
+}
+
+static int
+lnet_issep(char c)
+{
+ switch (c) {
+ case '\n':
+ case '\r':
+ case ';':
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+static int
+lnet_net_unique(__u32 net, struct list_head *nilist)
+{
+ struct list_head *tmp;
+ lnet_ni_t *ni;
+
+ list_for_each(tmp, nilist) {
+ ni = list_entry(tmp, lnet_ni_t, ni_list);
+
+ if (LNET_NIDNET(ni->ni_nid) == net)
+ return 0;
+ }
+
+ return 1;
+}
+
+void
+lnet_ni_free(struct lnet_ni *ni)
+{
+ if (ni->ni_refs != NULL)
+ cfs_percpt_free(ni->ni_refs);
+
+ if (ni->ni_tx_queues != NULL)
+ cfs_percpt_free(ni->ni_tx_queues);
+
+ if (ni->ni_cpts != NULL)
+ cfs_expr_list_values_free(ni->ni_cpts, ni->ni_ncpts);
+
+ LIBCFS_FREE(ni, sizeof(*ni));
+}
+
+static lnet_ni_t *
+lnet_ni_alloc(__u32 net, struct cfs_expr_list *el, struct list_head *nilist)
+{
+ struct lnet_tx_queue *tq;
+ struct lnet_ni *ni;
+ int rc;
+ int i;
+
+ if (!lnet_net_unique(net, nilist)) {
+ LCONSOLE_ERROR_MSG(0x111, "Duplicate network specified: %s\n",
+ libcfs_net2str(net));
+ return NULL;
+ }
+
+ LIBCFS_ALLOC(ni, sizeof(*ni));
+ if (ni == NULL) {
+ CERROR("Out of memory creating network %s\n",
+ libcfs_net2str(net));
+ return NULL;
+ }
+
+ spin_lock_init(&ni->ni_lock);
+ INIT_LIST_HEAD(&ni->ni_cptlist);
+ ni->ni_refs = cfs_percpt_alloc(lnet_cpt_table(),
+ sizeof(*ni->ni_refs[0]));
+ if (ni->ni_refs == NULL)
+ goto failed;
+
+ ni->ni_tx_queues = cfs_percpt_alloc(lnet_cpt_table(),
+ sizeof(*ni->ni_tx_queues[0]));
+ if (ni->ni_tx_queues == NULL)
+ goto failed;
+
+ cfs_percpt_for_each(tq, i, ni->ni_tx_queues)
+ INIT_LIST_HEAD(&tq->tq_delayed);
+
+ if (el == NULL) {
+ ni->ni_cpts = NULL;
+ ni->ni_ncpts = LNET_CPT_NUMBER;
+ } else {
+ rc = cfs_expr_list_values(el, LNET_CPT_NUMBER, &ni->ni_cpts);
+ if (rc <= 0) {
+ CERROR("Failed to set CPTs for NI %s: %d\n",
+ libcfs_net2str(net), rc);
+ goto failed;
+ }
+
+ LASSERT(rc <= LNET_CPT_NUMBER);
+ if (rc == LNET_CPT_NUMBER) {
+ LIBCFS_FREE(ni->ni_cpts, rc * sizeof(ni->ni_cpts[0]));
+ ni->ni_cpts = NULL;
+ }
+
+ ni->ni_ncpts = rc;
+ }
+
+ /* LND will fill in the address part of the NID */
+ ni->ni_nid = LNET_MKNID(net, 0);
+ ni->ni_last_alive = get_seconds();
+ list_add_tail(&ni->ni_list, nilist);
+ return ni;
+ failed:
+ lnet_ni_free(ni);
+ return NULL;
+}
+
+int
+lnet_parse_networks(struct list_head *nilist, char *networks)
+{
+ struct cfs_expr_list *el = NULL;
+ int tokensize = strlen(networks) + 1;
+ char *tokens;
+ char *str;
+ char *tmp;
+ struct lnet_ni *ni;
+ __u32 net;
+ int nnets = 0;
+
+ if (strlen(networks) > LNET_SINGLE_TEXTBUF_NOB) {
+ /* _WAY_ conservative */
+ LCONSOLE_ERROR_MSG(0x112,
+ "Can't parse networks: string too long\n");
+ return -EINVAL;
+ }
+
+ LIBCFS_ALLOC(tokens, tokensize);
+ if (tokens == NULL) {
+ CERROR("Can't allocate net tokens\n");
+ return -ENOMEM;
+ }
+
+ the_lnet.ln_network_tokens = tokens;
+ the_lnet.ln_network_tokens_nob = tokensize;
+ memcpy(tokens, networks, tokensize);
+ str = tmp = tokens;
+
+ /* Add in the loopback network */
+ ni = lnet_ni_alloc(LNET_MKNET(LOLND, 0), NULL, nilist);
+ if (ni == NULL)
+ goto failed;
+
+ while (str != NULL && *str != 0) {
+ char *comma = strchr(str, ',');
+ char *bracket = strchr(str, '(');
+ char *square = strchr(str, '[');
+ char *iface;
+ int niface;
+ int rc;
+
+ /* NB we don't check interface conflicts here; it's the LNDs
+ * responsibility (if it cares at all) */
+
+ if (square != NULL && (comma == NULL || square < comma)) {
+ /* i.e: o2ib0(ib0)[1,2], number between square
+ * brackets are CPTs this NI needs to be bond */
+ if (bracket != NULL && bracket > square) {
+ tmp = square;
+ goto failed_syntax;
+ }
+
+ tmp = strchr(square, ']');
+ if (tmp == NULL) {
+ tmp = square;
+ goto failed_syntax;
+ }
+
+ rc = cfs_expr_list_parse(square, tmp - square + 1,
+ 0, LNET_CPT_NUMBER - 1, &el);
+ if (rc != 0) {
+ tmp = square;
+ goto failed_syntax;
+ }
+
+ while (square <= tmp)
+ *square++ = ' ';
+ }
+
+ if (bracket == NULL ||
+ (comma != NULL && comma < bracket)) {
+
+ /* no interface list specified */
+
+ if (comma != NULL)
+ *comma++ = 0;
+ net = libcfs_str2net(cfs_trimwhite(str));
+
+ if (net == LNET_NIDNET(LNET_NID_ANY)) {
+ LCONSOLE_ERROR_MSG(0x113,
+ "Unrecognised network type\n");
+ tmp = str;
+ goto failed_syntax;
+ }
+
+ if (LNET_NETTYP(net) != LOLND && /* LO is implicit */
+ lnet_ni_alloc(net, el, nilist) == NULL)
+ goto failed;
+
+ if (el != NULL) {
+ cfs_expr_list_free(el);
+ el = NULL;
+ }
+
+ str = comma;
+ continue;
+ }
+
+ *bracket = 0;
+ net = libcfs_str2net(cfs_trimwhite(str));
+ if (net == LNET_NIDNET(LNET_NID_ANY)) {
+ tmp = str;
+ goto failed_syntax;
+ }
+
+ nnets++;
+ ni = lnet_ni_alloc(net, el, nilist);
+ if (ni == NULL)
+ goto failed;
+
+ if (el != NULL) {
+ cfs_expr_list_free(el);
+ el = NULL;
+ }
+
+ niface = 0;
+ iface = bracket + 1;
+
+ bracket = strchr(iface, ')');
+ if (bracket == NULL) {
+ tmp = iface;
+ goto failed_syntax;
+ }
+
+ *bracket = 0;
+ do {
+ comma = strchr(iface, ',');
+ if (comma != NULL)
+ *comma++ = 0;
+
+ iface = cfs_trimwhite(iface);
+ if (*iface == 0) {
+ tmp = iface;
+ goto failed_syntax;
+ }
+
+ if (niface == LNET_MAX_INTERFACES) {
+ LCONSOLE_ERROR_MSG(0x115,
+ "Too many interfaces for net %s\n",
+ libcfs_net2str(net));
+ goto failed;
+ }
+
+ ni->ni_interfaces[niface++] = iface;
+ iface = comma;
+ } while (iface != NULL);
+
+ str = bracket + 1;
+ comma = strchr(bracket + 1, ',');
+ if (comma != NULL) {
+ *comma = 0;
+ str = cfs_trimwhite(str);
+ if (*str != 0) {
+ tmp = str;
+ goto failed_syntax;
+ }
+ str = comma + 1;
+ continue;
+ }
+
+ str = cfs_trimwhite(str);
+ if (*str != 0) {
+ tmp = str;
+ goto failed_syntax;
+ }
+ }
+
+ LASSERT(!list_empty(nilist));
+ return 0;
+
+ failed_syntax:
+ lnet_syntax("networks", networks, (int)(tmp - tokens), strlen(tmp));
+ failed:
+ while (!list_empty(nilist)) {
+ ni = list_entry(nilist->next, lnet_ni_t, ni_list);
+
+ list_del(&ni->ni_list);
+ lnet_ni_free(ni);
+ }
+
+ if (el != NULL)
+ cfs_expr_list_free(el);
+
+ LIBCFS_FREE(tokens, tokensize);
+ the_lnet.ln_network_tokens = NULL;
+
+ return -EINVAL;
+}
+
+static struct lnet_text_buf_t *
+lnet_new_text_buf(int str_len)
+{
+ struct lnet_text_buf_t *ltb;
+ int nob;
+
+ /* NB allocate space for the terminating 0 */
+ nob = offsetof(struct lnet_text_buf_t, ltb_text[str_len + 1]);
+ if (nob > LNET_SINGLE_TEXTBUF_NOB) {
+ /* _way_ conservative for "route net gateway..." */
+ CERROR("text buffer too big\n");
+ return NULL;
+ }
+
+ if (lnet_tbnob + nob > LNET_MAX_TEXTBUF_NOB) {
+ CERROR("Too many text buffers\n");
+ return NULL;
+ }
+
+ LIBCFS_ALLOC(ltb, nob);
+ if (ltb == NULL)
+ return NULL;
+
+ ltb->ltb_size = nob;
+ ltb->ltb_text[0] = 0;
+ lnet_tbnob += nob;
+ return ltb;
+}
+
+static void
+lnet_free_text_buf(struct lnet_text_buf_t *ltb)
+{
+ lnet_tbnob -= ltb->ltb_size;
+ LIBCFS_FREE(ltb, ltb->ltb_size);
+}
+
+static void
+lnet_free_text_bufs(struct list_head *tbs)
+{
+ struct lnet_text_buf_t *ltb;
+
+ while (!list_empty(tbs)) {
+ ltb = list_entry(tbs->next, struct lnet_text_buf_t, ltb_list);
+
+ list_del(&ltb->ltb_list);
+ lnet_free_text_buf(ltb);
+ }
+}
+
+static int
+lnet_str2tbs_sep(struct list_head *tbs, char *str)
+{
+ struct list_head pending;
+ char *sep;
+ int nob;
+ int i;
+ struct lnet_text_buf_t *ltb;
+
+ INIT_LIST_HEAD(&pending);
+
+ /* Split 'str' into separate commands */
+ for (;;) {
+ /* skip leading whitespace */
+ while (isspace(*str))
+ str++;
+
+ /* scan for separator or comment */
+ for (sep = str; *sep != 0; sep++)
+ if (lnet_issep(*sep) || *sep == '#')
+ break;
+
+ nob = (int)(sep - str);
+ if (nob > 0) {
+ ltb = lnet_new_text_buf(nob);
+ if (ltb == NULL) {
+ lnet_free_text_bufs(&pending);
+ return -1;
+ }
+
+ for (i = 0; i < nob; i++)
+ if (isspace(str[i]))
+ ltb->ltb_text[i] = ' ';
+ else
+ ltb->ltb_text[i] = str[i];
+
+ ltb->ltb_text[nob] = 0;
+
+ list_add_tail(&ltb->ltb_list, &pending);
+ }
+
+ if (*sep == '#') {
+ /* scan for separator */
+ do {
+ sep++;
+ } while (*sep != 0 && !lnet_issep(*sep));
+ }
+
+ if (*sep == 0)
+ break;
+
+ str = sep + 1;
+ }
+
+ list_splice(&pending, tbs->prev);
+ return 0;
+}
+
+static int
+lnet_expand1tb(struct list_head *list,
+ char *str, char *sep1, char *sep2,
+ char *item, int itemlen)
+{
+ int len1 = (int)(sep1 - str);
+ int len2 = strlen(sep2 + 1);
+ struct lnet_text_buf_t *ltb;
+
+ LASSERT(*sep1 == '[');
+ LASSERT(*sep2 == ']');
+
+ ltb = lnet_new_text_buf(len1 + itemlen + len2);
+ if (ltb == NULL)
+ return -ENOMEM;
+
+ memcpy(ltb->ltb_text, str, len1);
+ memcpy(&ltb->ltb_text[len1], item, itemlen);
+ memcpy(&ltb->ltb_text[len1+itemlen], sep2 + 1, len2);
+ ltb->ltb_text[len1 + itemlen + len2] = 0;
+
+ list_add_tail(&ltb->ltb_list, list);
+ return 0;
+}
+
+static int
+lnet_str2tbs_expand(struct list_head *tbs, char *str)
+{
+ char num[16];
+ struct list_head pending;
+ char *sep;
+ char *sep2;
+ char *parsed;
+ char *enditem;
+ int lo;
+ int hi;
+ int stride;
+ int i;
+ int nob;
+ int scanned;
+
+ INIT_LIST_HEAD(&pending);
+
+ sep = strchr(str, '[');
+ if (sep == NULL) /* nothing to expand */
+ return 0;
+
+ sep2 = strchr(sep, ']');
+ if (sep2 == NULL)
+ goto failed;
+
+ for (parsed = sep; parsed < sep2; parsed = enditem) {
+
+ enditem = ++parsed;
+ while (enditem < sep2 && *enditem != ',')
+ enditem++;
+
+ if (enditem == parsed) /* no empty items */
+ goto failed;
+
+ if (sscanf(parsed, "%d-%d/%d%n", &lo, &hi,
+ &stride, &scanned) < 3) {
+
+ if (sscanf(parsed, "%d-%d%n", &lo, &hi, &scanned) < 2) {
+
+ /* simple string enumeration */
+ if (lnet_expand1tb(
+ &pending, str, sep, sep2,
+ parsed,
+ (int)(enditem - parsed)) != 0) {
+ goto failed;
+ }
+
+ continue;
+ }
+
+ stride = 1;
+ }
+
+ /* range expansion */
+
+ if (enditem != parsed + scanned) /* no trailing junk */
+ goto failed;
+
+ if (hi < 0 || lo < 0 || stride < 0 || hi < lo ||
+ (hi - lo) % stride != 0)
+ goto failed;
+
+ for (i = lo; i <= hi; i += stride) {
+
+ snprintf(num, sizeof(num), "%d", i);
+ nob = strlen(num);
+ if (nob + 1 == sizeof(num))
+ goto failed;
+
+ if (lnet_expand1tb(&pending, str, sep, sep2,
+ num, nob) != 0)
+ goto failed;
+ }
+ }
+
+ list_splice(&pending, tbs->prev);
+ return 1;
+
+ failed:
+ lnet_free_text_bufs(&pending);
+ return -1;
+}
+
+static int
+lnet_parse_hops(char *str, unsigned int *hops)
+{
+ int len = strlen(str);
+ int nob = len;
+
+ return (sscanf(str, "%u%n", hops, &nob) >= 1 &&
+ nob == len &&
+ *hops > 0 && *hops < 256);
+}
+
+#define LNET_PRIORITY_SEPARATOR (':')
+
+static int
+lnet_parse_priority(char *str, unsigned int *priority, char **token)
+{
+ int nob;
+ char *sep;
+ int len;
+
+ sep = strchr(str, LNET_PRIORITY_SEPARATOR);
+ if (sep == NULL) {
+ *priority = 0;
+ return 0;
+ }
+ len = strlen(sep + 1);
+
+ if ((sscanf((sep+1), "%u%n", priority, &nob) < 1) || (len != nob)) {
+ /* Update the caller's token pointer so it treats the found
+ priority as the token to report in the error message. */
+ *token += sep - str + 1;
+ return -1;
+ }
+
+ CDEBUG(D_NET, "gateway %s, priority %d, nob %d\n", str, *priority, nob);
+
+ /*
+ * Change priority separator to \0 to be able to parse NID
+ */
+ *sep = '\0';
+ return 0;
+}
+
+static int
+lnet_parse_route(char *str, int *im_a_router)
+{
+ /* static scratch buffer OK (single threaded) */
+ static char cmd[LNET_SINGLE_TEXTBUF_NOB];
+
+ struct list_head nets;
+ struct list_head gateways;
+ struct list_head *tmp1;
+ struct list_head *tmp2;
+ __u32 net;
+ lnet_nid_t nid;
+ struct lnet_text_buf_t *ltb;
+ int rc;
+ char *sep;
+ char *token = str;
+ int ntokens = 0;
+ int myrc = -1;
+ unsigned int hops;
+ int got_hops = 0;
+ unsigned int priority = 0;
+
+ INIT_LIST_HEAD(&gateways);
+ INIT_LIST_HEAD(&nets);
+
+ /* save a copy of the string for error messages */
+ strncpy(cmd, str, sizeof(cmd) - 1);
+ cmd[sizeof(cmd) - 1] = 0;
+
+ sep = str;
+ for (;;) {
+ /* scan for token start */
+ while (isspace(*sep))
+ sep++;
+ if (*sep == 0) {
+ if (ntokens < (got_hops ? 3 : 2))
+ goto token_error;
+ break;
+ }
+
+ ntokens++;
+ token = sep++;
+
+ /* scan for token end */
+ while (*sep != 0 && !isspace(*sep))
+ sep++;
+ if (*sep != 0)
+ *sep++ = 0;
+
+ if (ntokens == 1) {
+ tmp2 = &nets; /* expanding nets */
+ } else if (ntokens == 2 &&
+ lnet_parse_hops(token, &hops)) {
+ got_hops = 1; /* got a hop count */
+ continue;
+ } else {
+ tmp2 = &gateways; /* expanding gateways */
+ }
+
+ ltb = lnet_new_text_buf(strlen(token));
+ if (ltb == NULL)
+ goto out;
+
+ strcpy(ltb->ltb_text, token);
+ tmp1 = &ltb->ltb_list;
+ list_add_tail(tmp1, tmp2);
+
+ while (tmp1 != tmp2) {
+ ltb = list_entry(tmp1, struct lnet_text_buf_t,
+ ltb_list);
+
+ rc = lnet_str2tbs_expand(tmp1->next, ltb->ltb_text);
+ if (rc < 0)
+ goto token_error;
+
+ tmp1 = tmp1->next;
+
+ if (rc > 0) { /* expanded! */
+ list_del(&ltb->ltb_list);
+ lnet_free_text_buf(ltb);
+ continue;
+ }
+
+ if (ntokens == 1) {
+ net = libcfs_str2net(ltb->ltb_text);
+ if (net == LNET_NIDNET(LNET_NID_ANY) ||
+ LNET_NETTYP(net) == LOLND)
+ goto token_error;
+ } else {
+ rc = lnet_parse_priority(ltb->ltb_text,
+ &priority, &token);
+ if (rc < 0)
+ goto token_error;
+
+ nid = libcfs_str2nid(ltb->ltb_text);
+ if (nid == LNET_NID_ANY ||
+ LNET_NETTYP(LNET_NIDNET(nid)) == LOLND)
+ goto token_error;
+ }
+ }
+ }
+
+ if (!got_hops)
+ hops = 1;
+
+ LASSERT(!list_empty(&nets));
+ LASSERT(!list_empty(&gateways));
+
+ list_for_each(tmp1, &nets) {
+ ltb = list_entry(tmp1, struct lnet_text_buf_t, ltb_list);
+ net = libcfs_str2net(ltb->ltb_text);
+ LASSERT(net != LNET_NIDNET(LNET_NID_ANY));
+
+ list_for_each(tmp2, &gateways) {
+ ltb = list_entry(tmp2, struct lnet_text_buf_t,
+ ltb_list);
+ nid = libcfs_str2nid(ltb->ltb_text);
+ LASSERT(nid != LNET_NID_ANY);
+
+ if (lnet_islocalnid(nid)) {
+ *im_a_router = 1;
+ continue;
+ }
+
+ rc = lnet_add_route(net, hops, nid, priority);
+ if (rc != 0) {
+ CERROR("Can't create route to %s via %s\n",
+ libcfs_net2str(net),
+ libcfs_nid2str(nid));
+ goto out;
+ }
+ }
+ }
+
+ myrc = 0;
+ goto out;
+
+ token_error:
+ lnet_syntax("routes", cmd, (int)(token - str), strlen(token));
+ out:
+ lnet_free_text_bufs(&nets);
+ lnet_free_text_bufs(&gateways);
+ return myrc;
+}
+
+static int
+lnet_parse_route_tbs(struct list_head *tbs, int *im_a_router)
+{
+ struct lnet_text_buf_t *ltb;
+
+ while (!list_empty(tbs)) {
+ ltb = list_entry(tbs->next, struct lnet_text_buf_t, ltb_list);
+
+ if (lnet_parse_route(ltb->ltb_text, im_a_router) < 0) {
+ lnet_free_text_bufs(tbs);
+ return -EINVAL;
+ }
+
+ list_del(&ltb->ltb_list);
+ lnet_free_text_buf(ltb);
+ }
+
+ return 0;
+}
+
+int
+lnet_parse_routes(char *routes, int *im_a_router)
+{
+ struct list_head tbs;
+ int rc = 0;
+
+ *im_a_router = 0;
+
+ INIT_LIST_HEAD(&tbs);
+
+ if (lnet_str2tbs_sep(&tbs, routes) < 0) {
+ CERROR("Error parsing routes\n");
+ rc = -EINVAL;
+ } else {
+ rc = lnet_parse_route_tbs(&tbs, im_a_router);
+ }
+
+ LASSERT(lnet_tbnob == 0);
+ return rc;
+}
+
+static int
+lnet_match_network_token(char *token, int len, __u32 *ipaddrs, int nip)
+{
+ LIST_HEAD(list);
+ int rc;
+ int i;
+
+ rc = cfs_ip_addr_parse(token, len, &list);
+ if (rc != 0)
+ return rc;
+
+ for (rc = i = 0; !rc && i < nip; i++)
+ rc = cfs_ip_addr_match(ipaddrs[i], &list);
+
+ cfs_ip_addr_free(&list);
+
+ return rc;
+}
+
+static int
+lnet_match_network_tokens(char *net_entry, __u32 *ipaddrs, int nip)
+{
+ static char tokens[LNET_SINGLE_TEXTBUF_NOB];
+
+ int matched = 0;
+ int ntokens = 0;
+ int len;
+ char *net = NULL;
+ char *sep;
+ char *token;
+ int rc;
+
+ LASSERT(strlen(net_entry) < sizeof(tokens));
+
+ /* work on a copy of the string */
+ strcpy(tokens, net_entry);
+ sep = tokens;
+ for (;;) {
+ /* scan for token start */
+ while (isspace(*sep))
+ sep++;
+ if (*sep == 0)
+ break;
+
+ token = sep++;
+
+ /* scan for token end */
+ while (*sep != 0 && !isspace(*sep))
+ sep++;
+ if (*sep != 0)
+ *sep++ = 0;
+
+ if (ntokens++ == 0) {
+ net = token;
+ continue;
+ }
+
+ len = strlen(token);
+
+ rc = lnet_match_network_token(token, len, ipaddrs, nip);
+ if (rc < 0) {
+ lnet_syntax("ip2nets", net_entry,
+ (int)(token - tokens), len);
+ return rc;
+ }
+
+ matched |= (rc != 0);
+ }
+
+ if (!matched)
+ return 0;
+
+ strcpy(net_entry, net); /* replace with matched net */
+ return 1;
+}
+
+static __u32
+lnet_netspec2net(char *netspec)
+{
+ char *bracket = strchr(netspec, '(');
+ __u32 net;
+
+ if (bracket != NULL)
+ *bracket = 0;
+
+ net = libcfs_str2net(netspec);
+
+ if (bracket != NULL)
+ *bracket = '(';
+
+ return net;
+}
+
+static int
+lnet_splitnets(char *source, struct list_head *nets)
+{
+ int offset = 0;
+ int offset2;
+ int len;
+ struct lnet_text_buf_t *tb;
+ struct lnet_text_buf_t *tb2;
+ struct list_head *t;
+ char *sep;
+ char *bracket;
+ __u32 net;
+
+ LASSERT(!list_empty(nets));
+ LASSERT(nets->next == nets->prev); /* single entry */
+
+ tb = list_entry(nets->next, struct lnet_text_buf_t, ltb_list);
+
+ for (;;) {
+ sep = strchr(tb->ltb_text, ',');
+ bracket = strchr(tb->ltb_text, '(');
+
+ if (sep != NULL &&
+ bracket != NULL &&
+ bracket < sep) {
+ /* netspec lists interfaces... */
+
+ offset2 = offset + (int)(bracket - tb->ltb_text);
+ len = strlen(bracket);
+
+ bracket = strchr(bracket + 1, ')');
+
+ if (bracket == NULL ||
+ !(bracket[1] == ',' || bracket[1] == 0)) {
+ lnet_syntax("ip2nets", source, offset2, len);
+ return -EINVAL;
+ }
+
+ sep = (bracket[1] == 0) ? NULL : bracket + 1;
+ }
+
+ if (sep != NULL)
+ *sep++ = 0;
+
+ net = lnet_netspec2net(tb->ltb_text);
+ if (net == LNET_NIDNET(LNET_NID_ANY)) {
+ lnet_syntax("ip2nets", source, offset,
+ strlen(tb->ltb_text));
+ return -EINVAL;
+ }
+
+ list_for_each(t, nets) {
+ tb2 = list_entry(t, struct lnet_text_buf_t, ltb_list);
+
+ if (tb2 == tb)
+ continue;
+
+ if (net == lnet_netspec2net(tb2->ltb_text)) {
+ /* duplicate network */
+ lnet_syntax("ip2nets", source, offset,
+ strlen(tb->ltb_text));
+ return -EINVAL;
+ }
+ }
+
+ if (sep == NULL)
+ return 0;
+
+ offset += (int)(sep - tb->ltb_text);
+ tb2 = lnet_new_text_buf(strlen(sep));
+ if (tb2 == NULL)
+ return -ENOMEM;
+
+ strcpy(tb2->ltb_text, sep);
+ list_add_tail(&tb2->ltb_list, nets);
+
+ tb = tb2;
+ }
+}
+
+static int
+lnet_match_networks(char **networksp, char *ip2nets, __u32 *ipaddrs, int nip)
+{
+ static char networks[LNET_SINGLE_TEXTBUF_NOB];
+ static char source[LNET_SINGLE_TEXTBUF_NOB];
+
+ struct list_head raw_entries;
+ struct list_head matched_nets;
+ struct list_head current_nets;
+ struct list_head *t;
+ struct list_head *t2;
+ struct lnet_text_buf_t *tb;
+ struct lnet_text_buf_t *tb2;
+ __u32 net1;
+ __u32 net2;
+ int len;
+ int count;
+ int dup;
+ int rc;
+
+ INIT_LIST_HEAD(&raw_entries);
+ if (lnet_str2tbs_sep(&raw_entries, ip2nets) < 0) {
+ CERROR("Error parsing ip2nets\n");
+ LASSERT(lnet_tbnob == 0);
+ return -EINVAL;
+ }
+
+ INIT_LIST_HEAD(&matched_nets);
+ INIT_LIST_HEAD(&current_nets);
+ networks[0] = 0;
+ count = 0;
+ len = 0;
+ rc = 0;
+
+ while (!list_empty(&raw_entries)) {
+ tb = list_entry(raw_entries.next, struct lnet_text_buf_t,
+ ltb_list);
+
+ strncpy(source, tb->ltb_text, sizeof(source)-1);
+ source[sizeof(source)-1] = 0;
+
+ /* replace ltb_text with the network(s) add on match */
+ rc = lnet_match_network_tokens(tb->ltb_text, ipaddrs, nip);
+ if (rc < 0)
+ break;
+
+ list_del(&tb->ltb_list);
+
+ if (rc == 0) { /* no match */
+ lnet_free_text_buf(tb);
+ continue;
+ }
+
+ /* split into separate networks */
+ INIT_LIST_HEAD(&current_nets);
+ list_add(&tb->ltb_list, &current_nets);
+ rc = lnet_splitnets(source, &current_nets);
+ if (rc < 0)
+ break;
+
+ dup = 0;
+ list_for_each(t, &current_nets) {
+ tb = list_entry(t, struct lnet_text_buf_t, ltb_list);
+ net1 = lnet_netspec2net(tb->ltb_text);
+ LASSERT(net1 != LNET_NIDNET(LNET_NID_ANY));
+
+ list_for_each(t2, &matched_nets) {
+ tb2 = list_entry(t2, struct lnet_text_buf_t,
+ ltb_list);
+ net2 = lnet_netspec2net(tb2->ltb_text);
+ LASSERT(net2 != LNET_NIDNET(LNET_NID_ANY));
+
+ if (net1 == net2) {
+ dup = 1;
+ break;
+ }
+ }
+
+ if (dup)
+ break;
+ }
+
+ if (dup) {
+ lnet_free_text_bufs(&current_nets);
+ continue;
+ }
+
+ list_for_each_safe(t, t2, &current_nets) {
+ tb = list_entry(t, struct lnet_text_buf_t, ltb_list);
+
+ list_del(&tb->ltb_list);
+ list_add_tail(&tb->ltb_list, &matched_nets);
+
+ len += snprintf(networks + len, sizeof(networks) - len,
+ "%s%s", (len == 0) ? "" : ",",
+ tb->ltb_text);
+
+ if (len >= sizeof(networks)) {
+ CERROR("Too many matched networks\n");
+ rc = -E2BIG;
+ goto out;
+ }
+ }
+
+ count++;
+ }
+
+ out:
+ lnet_free_text_bufs(&raw_entries);
+ lnet_free_text_bufs(&matched_nets);
+ lnet_free_text_bufs(&current_nets);
+ LASSERT(lnet_tbnob == 0);
+
+ if (rc < 0)
+ return rc;
+
+ *networksp = networks;
+ return count;
+}
+
+static void
+lnet_ipaddr_free_enumeration(__u32 *ipaddrs, int nip)
+{
+ LIBCFS_FREE(ipaddrs, nip * sizeof(*ipaddrs));
+}
+
+static int
+lnet_ipaddr_enumerate(__u32 **ipaddrsp)
+{
+ int up;
+ __u32 netmask;
+ __u32 *ipaddrs;
+ __u32 *ipaddrs2;
+ int nip;
+ char **ifnames;
+ int nif = libcfs_ipif_enumerate(&ifnames);
+ int i;
+ int rc;
+
+ if (nif <= 0)
+ return nif;
+
+ LIBCFS_ALLOC(ipaddrs, nif * sizeof(*ipaddrs));
+ if (ipaddrs == NULL) {
+ CERROR("Can't allocate ipaddrs[%d]\n", nif);
+ libcfs_ipif_free_enumeration(ifnames, nif);
+ return -ENOMEM;
+ }
+
+ for (i = nip = 0; i < nif; i++) {
+ if (!strcmp(ifnames[i], "lo"))
+ continue;
+
+ rc = libcfs_ipif_query(ifnames[i], &up,
+ &ipaddrs[nip], &netmask);
+ if (rc != 0) {
+ CWARN("Can't query interface %s: %d\n",
+ ifnames[i], rc);
+ continue;
+ }
+
+ if (!up) {
+ CWARN("Ignoring interface %s: it's down\n",
+ ifnames[i]);
+ continue;
+ }
+
+ nip++;
+ }
+
+ libcfs_ipif_free_enumeration(ifnames, nif);
+
+ if (nip == nif) {
+ *ipaddrsp = ipaddrs;
+ } else {
+ if (nip > 0) {
+ LIBCFS_ALLOC(ipaddrs2, nip * sizeof(*ipaddrs2));
+ if (ipaddrs2 == NULL) {
+ CERROR("Can't allocate ipaddrs[%d]\n", nip);
+ nip = -ENOMEM;
+ } else {
+ memcpy(ipaddrs2, ipaddrs,
+ nip * sizeof(*ipaddrs));
+ *ipaddrsp = ipaddrs2;
+ rc = nip;
+ }
+ }
+ lnet_ipaddr_free_enumeration(ipaddrs, nif);
+ }
+ return nip;
+}
+
+int
+lnet_parse_ip2nets(char **networksp, char *ip2nets)
+{
+ __u32 *ipaddrs = NULL;
+ int nip = lnet_ipaddr_enumerate(&ipaddrs);
+ int rc;
+
+ if (nip < 0) {
+ LCONSOLE_ERROR_MSG(0x117,
+ "Error %d enumerating local IP interfaces for ip2nets to match\n",
+ nip);
+ return nip;
+ }
+
+ if (nip == 0) {
+ LCONSOLE_ERROR_MSG(0x118,
+ "No local IP interfaces for ip2nets to match\n");
+ return -ENOENT;
+ }
+
+ rc = lnet_match_networks(networksp, ip2nets, ipaddrs, nip);
+ lnet_ipaddr_free_enumeration(ipaddrs, nip);
+
+ if (rc < 0) {
+ LCONSOLE_ERROR_MSG(0x119, "Error %d parsing ip2nets\n", rc);
+ return rc;
+ }
+
+ if (rc == 0) {
+ LCONSOLE_ERROR_MSG(0x11a,
+ "ip2nets does not match any local IP interfaces\n");
+ return -ENOENT;
+ }
+
+ return 0;
+}
+
+int
+lnet_set_ip_niaddr(lnet_ni_t *ni)
+{
+ __u32 net = LNET_NIDNET(ni->ni_nid);
+ char **names;
+ int n;
+ __u32 ip;
+ __u32 netmask;
+ int up;
+ int i;
+ int rc;
+
+ /* Convenience for LNDs that use the IP address of a local interface as
+ * the local address part of their NID */
+
+ if (ni->ni_interfaces[0] != NULL) {
+
+ CLASSERT(LNET_MAX_INTERFACES > 1);
+
+ if (ni->ni_interfaces[1] != NULL) {
+ CERROR("Net %s doesn't support multiple interfaces\n",
+ libcfs_net2str(net));
+ return -EPERM;
+ }
+
+ rc = libcfs_ipif_query(ni->ni_interfaces[0],
+ &up, &ip, &netmask);
+ if (rc != 0) {
+ CERROR("Net %s can't query interface %s: %d\n",
+ libcfs_net2str(net), ni->ni_interfaces[0], rc);
+ return -EPERM;
+ }
+
+ if (!up) {
+ CERROR("Net %s can't use interface %s: it's down\n",
+ libcfs_net2str(net), ni->ni_interfaces[0]);
+ return -ENETDOWN;
+ }
+
+ ni->ni_nid = LNET_MKNID(net, ip);
+ return 0;
+ }
+
+ n = libcfs_ipif_enumerate(&names);
+ if (n <= 0) {
+ CERROR("Net %s can't enumerate interfaces: %d\n",
+ libcfs_net2str(net), n);
+ return 0;
+ }
+
+ for (i = 0; i < n; i++) {
+ if (!strcmp(names[i], "lo")) /* skip the loopback IF */
+ continue;
+
+ rc = libcfs_ipif_query(names[i], &up, &ip, &netmask);
+
+ if (rc != 0) {
+ CWARN("Net %s can't query interface %s: %d\n",
+ libcfs_net2str(net), names[i], rc);
+ continue;
+ }
+
+ if (!up) {
+ CWARN("Net %s ignoring interface %s (down)\n",
+ libcfs_net2str(net), names[i]);
+ continue;
+ }
+
+ libcfs_ipif_free_enumeration(names, n);
+ ni->ni_nid = LNET_MKNID(net, ip);
+ return 0;
+ }
+
+ CERROR("Net %s can't find any interfaces\n", libcfs_net2str(net));
+ libcfs_ipif_free_enumeration(names, n);
+ return -ENOENT;
+}
+EXPORT_SYMBOL(lnet_set_ip_niaddr);
diff --git a/drivers/staging/lustre/lnet/lnet/lib-eq.c b/drivers/staging/lustre/lnet/lnet/lib-eq.c
new file mode 100644
index 000000000..5470148f5
--- /dev/null
+++ b/drivers/staging/lustre/lnet/lnet/lib-eq.c
@@ -0,0 +1,441 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2012, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ *
+ * lnet/lnet/lib-eq.c
+ *
+ * Library level Event queue management routines
+ */
+
+#define DEBUG_SUBSYSTEM S_LNET
+#include "../../include/linux/lnet/lib-lnet.h"
+
+/**
+ * Create an event queue that has room for \a count number of events.
+ *
+ * The event queue is circular and older events will be overwritten by new
+ * ones if they are not removed in time by the user using the functions
+ * LNetEQGet(), LNetEQWait(), or LNetEQPoll(). It is up to the user to
+ * determine the appropriate size of the event queue to prevent this loss
+ * of events. Note that when EQ handler is specified in \a callback, no
+ * event loss can happen, since the handler is run for each event deposited
+ * into the EQ.
+ *
+ * \param count The number of events to be stored in the event queue. It
+ * will be rounded up to the next power of two.
+ * \param callback A handler function that runs when an event is deposited
+ * into the EQ. The constant value LNET_EQ_HANDLER_NONE can be used to
+ * indicate that no event handler is desired.
+ * \param handle On successful return, this location will hold a handle for
+ * the newly created EQ.
+ *
+ * \retval 0 On success.
+ * \retval -EINVAL If an parameter is not valid.
+ * \retval -ENOMEM If memory for the EQ can't be allocated.
+ *
+ * \see lnet_eq_handler_t for the discussion on EQ handler semantics.
+ */
+int
+LNetEQAlloc(unsigned int count, lnet_eq_handler_t callback,
+ lnet_handle_eq_t *handle)
+{
+ lnet_eq_t *eq;
+
+ LASSERT(the_lnet.ln_init);
+ LASSERT(the_lnet.ln_refcount > 0);
+
+ /* We need count to be a power of 2 so that when eq_{enq,deq}_seq
+ * overflow, they don't skip entries, so the queue has the same
+ * apparent capacity at all times */
+
+ count = cfs_power2_roundup(count);
+
+ if (callback != LNET_EQ_HANDLER_NONE && count != 0)
+ CWARN("EQ callback is guaranteed to get every event, do you still want to set eqcount %d for polling event which will have locking overhead? Please contact with developer to confirm\n", count);
+
+ /* count can be 0 if only need callback, we can eliminate
+ * overhead of enqueue event */
+ if (count == 0 && callback == LNET_EQ_HANDLER_NONE)
+ return -EINVAL;
+
+ eq = lnet_eq_alloc();
+ if (eq == NULL)
+ return -ENOMEM;
+
+ if (count != 0) {
+ LIBCFS_ALLOC(eq->eq_events, count * sizeof(lnet_event_t));
+ if (eq->eq_events == NULL)
+ goto failed;
+ /* NB allocator has set all event sequence numbers to 0,
+ * so all them should be earlier than eq_deq_seq */
+ }
+
+ eq->eq_deq_seq = 1;
+ eq->eq_enq_seq = 1;
+ eq->eq_size = count;
+ eq->eq_callback = callback;
+
+ eq->eq_refs = cfs_percpt_alloc(lnet_cpt_table(),
+ sizeof(*eq->eq_refs[0]));
+ if (eq->eq_refs == NULL)
+ goto failed;
+
+ /* MUST hold both exclusive lnet_res_lock */
+ lnet_res_lock(LNET_LOCK_EX);
+ /* NB: hold lnet_eq_wait_lock for EQ link/unlink, so we can do
+ * both EQ lookup and poll event with only lnet_eq_wait_lock */
+ lnet_eq_wait_lock();
+
+ lnet_res_lh_initialize(&the_lnet.ln_eq_container, &eq->eq_lh);
+ list_add(&eq->eq_list, &the_lnet.ln_eq_container.rec_active);
+
+ lnet_eq_wait_unlock();
+ lnet_res_unlock(LNET_LOCK_EX);
+
+ lnet_eq2handle(handle, eq);
+ return 0;
+
+failed:
+ if (eq->eq_events != NULL)
+ LIBCFS_FREE(eq->eq_events, count * sizeof(lnet_event_t));
+
+ if (eq->eq_refs != NULL)
+ cfs_percpt_free(eq->eq_refs);
+
+ lnet_eq_free(eq);
+ return -ENOMEM;
+}
+EXPORT_SYMBOL(LNetEQAlloc);
+
+/**
+ * Release the resources associated with an event queue if it's idle;
+ * otherwise do nothing and it's up to the user to try again.
+ *
+ * \param eqh A handle for the event queue to be released.
+ *
+ * \retval 0 If the EQ is not in use and freed.
+ * \retval -ENOENT If \a eqh does not point to a valid EQ.
+ * \retval -EBUSY If the EQ is still in use by some MDs.
+ */
+int
+LNetEQFree(lnet_handle_eq_t eqh)
+{
+ struct lnet_eq *eq;
+ lnet_event_t *events = NULL;
+ int **refs = NULL;
+ int *ref;
+ int rc = 0;
+ int size = 0;
+ int i;
+
+ LASSERT(the_lnet.ln_init);
+ LASSERT(the_lnet.ln_refcount > 0);
+
+ lnet_res_lock(LNET_LOCK_EX);
+ /* NB: hold lnet_eq_wait_lock for EQ link/unlink, so we can do
+ * both EQ lookup and poll event with only lnet_eq_wait_lock */
+ lnet_eq_wait_lock();
+
+ eq = lnet_handle2eq(&eqh);
+ if (eq == NULL) {
+ rc = -ENOENT;
+ goto out;
+ }
+
+ cfs_percpt_for_each(ref, i, eq->eq_refs) {
+ LASSERT(*ref >= 0);
+ if (*ref == 0)
+ continue;
+
+ CDEBUG(D_NET, "Event equeue (%d: %d) busy on destroy.\n",
+ i, *ref);
+ rc = -EBUSY;
+ goto out;
+ }
+
+ /* stash for free after lock dropped */
+ events = eq->eq_events;
+ size = eq->eq_size;
+ refs = eq->eq_refs;
+
+ lnet_res_lh_invalidate(&eq->eq_lh);
+ list_del(&eq->eq_list);
+ lnet_eq_free_locked(eq);
+ out:
+ lnet_eq_wait_unlock();
+ lnet_res_unlock(LNET_LOCK_EX);
+
+ if (events != NULL)
+ LIBCFS_FREE(events, size * sizeof(lnet_event_t));
+ if (refs != NULL)
+ cfs_percpt_free(refs);
+
+ return rc;
+}
+EXPORT_SYMBOL(LNetEQFree);
+
+void
+lnet_eq_enqueue_event(lnet_eq_t *eq, lnet_event_t *ev)
+{
+ /* MUST called with resource lock hold but w/o lnet_eq_wait_lock */
+ int index;
+
+ if (eq->eq_size == 0) {
+ LASSERT(eq->eq_callback != LNET_EQ_HANDLER_NONE);
+ eq->eq_callback(ev);
+ return;
+ }
+
+ lnet_eq_wait_lock();
+ ev->sequence = eq->eq_enq_seq++;
+
+ LASSERT(eq->eq_size == LOWEST_BIT_SET(eq->eq_size));
+ index = ev->sequence & (eq->eq_size - 1);
+
+ eq->eq_events[index] = *ev;
+
+ if (eq->eq_callback != LNET_EQ_HANDLER_NONE)
+ eq->eq_callback(ev);
+
+ /* Wake anyone waiting in LNetEQPoll() */
+ if (waitqueue_active(&the_lnet.ln_eq_waitq))
+ wake_up_all(&the_lnet.ln_eq_waitq);
+ lnet_eq_wait_unlock();
+}
+
+static int
+lnet_eq_dequeue_event(lnet_eq_t *eq, lnet_event_t *ev)
+{
+ int new_index = eq->eq_deq_seq & (eq->eq_size - 1);
+ lnet_event_t *new_event = &eq->eq_events[new_index];
+ int rc;
+
+ /* must called with lnet_eq_wait_lock hold */
+ if (LNET_SEQ_GT(eq->eq_deq_seq, new_event->sequence))
+ return 0;
+
+ /* We've got a new event... */
+ *ev = *new_event;
+
+ CDEBUG(D_INFO, "event: %p, sequence: %lu, eq->size: %u\n",
+ new_event, eq->eq_deq_seq, eq->eq_size);
+
+ /* ...but did it overwrite an event we've not seen yet? */
+ if (eq->eq_deq_seq == new_event->sequence) {
+ rc = 1;
+ } else {
+ /* don't complain with CERROR: some EQs are sized small
+ * anyway; if it's important, the caller should complain */
+ CDEBUG(D_NET, "Event Queue Overflow: eq seq %lu ev seq %lu\n",
+ eq->eq_deq_seq, new_event->sequence);
+ rc = -EOVERFLOW;
+ }
+
+ eq->eq_deq_seq = new_event->sequence + 1;
+ return rc;
+}
+
+/**
+ * A nonblocking function that can be used to get the next event in an EQ.
+ * If an event handler is associated with the EQ, the handler will run before
+ * this function returns successfully. The event is removed from the queue.
+ *
+ * \param eventq A handle for the event queue.
+ * \param event On successful return (1 or -EOVERFLOW), this location will
+ * hold the next event in the EQ.
+ *
+ * \retval 0 No pending event in the EQ.
+ * \retval 1 Indicates success.
+ * \retval -ENOENT If \a eventq does not point to a valid EQ.
+ * \retval -EOVERFLOW Indicates success (i.e., an event is returned) and that
+ * at least one event between this event and the last event obtained from the
+ * EQ has been dropped due to limited space in the EQ.
+ */
+int
+LNetEQGet(lnet_handle_eq_t eventq, lnet_event_t *event)
+{
+ int which;
+
+ return LNetEQPoll(&eventq, 1, 0,
+ event, &which);
+}
+EXPORT_SYMBOL(LNetEQGet);
+
+/**
+ * Block the calling process until there is an event in the EQ.
+ * If an event handler is associated with the EQ, the handler will run before
+ * this function returns successfully. This function returns the next event
+ * in the EQ and removes it from the EQ.
+ *
+ * \param eventq A handle for the event queue.
+ * \param event On successful return (1 or -EOVERFLOW), this location will
+ * hold the next event in the EQ.
+ *
+ * \retval 1 Indicates success.
+ * \retval -ENOENT If \a eventq does not point to a valid EQ.
+ * \retval -EOVERFLOW Indicates success (i.e., an event is returned) and that
+ * at least one event between this event and the last event obtained from the
+ * EQ has been dropped due to limited space in the EQ.
+ */
+int
+LNetEQWait(lnet_handle_eq_t eventq, lnet_event_t *event)
+{
+ int which;
+
+ return LNetEQPoll(&eventq, 1, LNET_TIME_FOREVER,
+ event, &which);
+}
+EXPORT_SYMBOL(LNetEQWait);
+
+
+static int
+lnet_eq_wait_locked(int *timeout_ms)
+__must_hold(&the_lnet.ln_eq_wait_lock)
+{
+ int tms = *timeout_ms;
+ int wait;
+ wait_queue_t wl;
+ unsigned long now;
+
+ if (tms == 0)
+ return -1; /* don't want to wait and no new event */
+
+ init_waitqueue_entry(&wl, current);
+ set_current_state(TASK_INTERRUPTIBLE);
+ add_wait_queue(&the_lnet.ln_eq_waitq, &wl);
+
+ lnet_eq_wait_unlock();
+
+ if (tms < 0) {
+ schedule();
+
+ } else {
+ struct timeval tv;
+
+ now = cfs_time_current();
+ schedule_timeout(cfs_time_seconds(tms) / 1000);
+ cfs_duration_usec(cfs_time_sub(cfs_time_current(), now), &tv);
+ tms -= (int)(tv.tv_sec * 1000 + tv.tv_usec / 1000);
+ if (tms < 0) /* no more wait but may have new event */
+ tms = 0;
+ }
+
+ wait = tms != 0; /* might need to call here again */
+ *timeout_ms = tms;
+
+ lnet_eq_wait_lock();
+ remove_wait_queue(&the_lnet.ln_eq_waitq, &wl);
+
+ return wait;
+}
+
+
+
+/**
+ * Block the calling process until there's an event from a set of EQs or
+ * timeout happens.
+ *
+ * If an event handler is associated with the EQ, the handler will run before
+ * this function returns successfully, in which case the corresponding event
+ * is consumed.
+ *
+ * LNetEQPoll() provides a timeout to allow applications to poll, block for a
+ * fixed period, or block indefinitely.
+ *
+ * \param eventqs,neq An array of EQ handles, and size of the array.
+ * \param timeout_ms Time in milliseconds to wait for an event to occur on
+ * one of the EQs. The constant LNET_TIME_FOREVER can be used to indicate an
+ * infinite timeout.
+ * \param event,which On successful return (1 or -EOVERFLOW), \a event will
+ * hold the next event in the EQs, and \a which will contain the index of the
+ * EQ from which the event was taken.
+ *
+ * \retval 0 No pending event in the EQs after timeout.
+ * \retval 1 Indicates success.
+ * \retval -EOVERFLOW Indicates success (i.e., an event is returned) and that
+ * at least one event between this event and the last event obtained from the
+ * EQ indicated by \a which has been dropped due to limited space in the EQ.
+ * \retval -ENOENT If there's an invalid handle in \a eventqs.
+ */
+int
+LNetEQPoll(lnet_handle_eq_t *eventqs, int neq, int timeout_ms,
+ lnet_event_t *event, int *which)
+{
+ int wait = 1;
+ int rc;
+ int i;
+
+ LASSERT(the_lnet.ln_init);
+ LASSERT(the_lnet.ln_refcount > 0);
+
+ if (neq < 1)
+ return -ENOENT;
+
+ lnet_eq_wait_lock();
+
+ for (;;) {
+ for (i = 0; i < neq; i++) {
+ lnet_eq_t *eq = lnet_handle2eq(&eventqs[i]);
+
+ if (eq == NULL) {
+ lnet_eq_wait_unlock();
+ return -ENOENT;
+ }
+
+ rc = lnet_eq_dequeue_event(eq, event);
+ if (rc != 0) {
+ lnet_eq_wait_unlock();
+ *which = i;
+ return rc;
+ }
+ }
+
+ if (wait == 0)
+ break;
+
+ /*
+ * return value of lnet_eq_wait_locked:
+ * -1 : did nothing and it's sure no new event
+ * 1 : sleep inside and wait until new event
+ * 0 : don't want to wait anymore, but might have new event
+ * so need to call dequeue again
+ */
+ wait = lnet_eq_wait_locked(&timeout_ms);
+ if (wait < 0) /* no new event */
+ break;
+ }
+
+ lnet_eq_wait_unlock();
+ return 0;
+}
diff --git a/drivers/staging/lustre/lnet/lnet/lib-md.c b/drivers/staging/lustre/lnet/lnet/lib-md.c
new file mode 100644
index 000000000..89d660fef
--- /dev/null
+++ b/drivers/staging/lustre/lnet/lnet/lib-md.c
@@ -0,0 +1,454 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2012, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ *
+ * lnet/lnet/lib-md.c
+ *
+ * Memory Descriptor management routines
+ */
+
+#define DEBUG_SUBSYSTEM S_LNET
+
+#include "../../include/linux/lnet/lib-lnet.h"
+
+/* must be called with lnet_res_lock held */
+void
+lnet_md_unlink(lnet_libmd_t *md)
+{
+ if ((md->md_flags & LNET_MD_FLAG_ZOMBIE) == 0) {
+ /* first unlink attempt... */
+ lnet_me_t *me = md->md_me;
+
+ md->md_flags |= LNET_MD_FLAG_ZOMBIE;
+
+ /* Disassociate from ME (if any),
+ * and unlink it if it was created
+ * with LNET_UNLINK */
+ if (me != NULL) {
+ /* detach MD from portal */
+ lnet_ptl_detach_md(me, md);
+ if (me->me_unlink == LNET_UNLINK)
+ lnet_me_unlink(me);
+ }
+
+ /* ensure all future handle lookups fail */
+ lnet_res_lh_invalidate(&md->md_lh);
+ }
+
+ if (md->md_refcount != 0) {
+ CDEBUG(D_NET, "Queueing unlink of md %p\n", md);
+ return;
+ }
+
+ CDEBUG(D_NET, "Unlinking md %p\n", md);
+
+ if (md->md_eq != NULL) {
+ int cpt = lnet_cpt_of_cookie(md->md_lh.lh_cookie);
+
+ LASSERT(*md->md_eq->eq_refs[cpt] > 0);
+ (*md->md_eq->eq_refs[cpt])--;
+ }
+
+ LASSERT(!list_empty(&md->md_list));
+ list_del_init(&md->md_list);
+ lnet_md_free_locked(md);
+}
+
+static int
+lnet_md_build(lnet_libmd_t *lmd, lnet_md_t *umd, int unlink)
+{
+ int i;
+ unsigned int niov;
+ int total_length = 0;
+
+ lmd->md_me = NULL;
+ lmd->md_start = umd->start;
+ lmd->md_offset = 0;
+ lmd->md_max_size = umd->max_size;
+ lmd->md_options = umd->options;
+ lmd->md_user_ptr = umd->user_ptr;
+ lmd->md_eq = NULL;
+ lmd->md_threshold = umd->threshold;
+ lmd->md_refcount = 0;
+ lmd->md_flags = (unlink == LNET_UNLINK) ? LNET_MD_FLAG_AUTO_UNLINK : 0;
+
+ if ((umd->options & LNET_MD_IOVEC) != 0) {
+
+ if ((umd->options & LNET_MD_KIOV) != 0) /* Can't specify both */
+ return -EINVAL;
+
+ lmd->md_niov = niov = umd->length;
+ memcpy(lmd->md_iov.iov, umd->start,
+ niov * sizeof(lmd->md_iov.iov[0]));
+
+ for (i = 0; i < (int)niov; i++) {
+ /* We take the base address on trust */
+ /* invalid length */
+ if (lmd->md_iov.iov[i].iov_len <= 0)
+ return -EINVAL;
+
+ total_length += lmd->md_iov.iov[i].iov_len;
+ }
+
+ lmd->md_length = total_length;
+
+ if ((umd->options & LNET_MD_MAX_SIZE) != 0 && /* use max size */
+ (umd->max_size < 0 ||
+ umd->max_size > total_length)) /* illegal max_size */
+ return -EINVAL;
+
+ } else if ((umd->options & LNET_MD_KIOV) != 0) {
+ lmd->md_niov = niov = umd->length;
+ memcpy(lmd->md_iov.kiov, umd->start,
+ niov * sizeof(lmd->md_iov.kiov[0]));
+
+ for (i = 0; i < (int)niov; i++) {
+ /* We take the page pointer on trust */
+ if (lmd->md_iov.kiov[i].kiov_offset +
+ lmd->md_iov.kiov[i].kiov_len > PAGE_CACHE_SIZE)
+ return -EINVAL; /* invalid length */
+
+ total_length += lmd->md_iov.kiov[i].kiov_len;
+ }
+
+ lmd->md_length = total_length;
+
+ if ((umd->options & LNET_MD_MAX_SIZE) != 0 && /* max size used */
+ (umd->max_size < 0 ||
+ umd->max_size > total_length)) /* illegal max_size */
+ return -EINVAL;
+ } else { /* contiguous */
+ lmd->md_length = umd->length;
+ lmd->md_niov = niov = 1;
+ lmd->md_iov.iov[0].iov_base = umd->start;
+ lmd->md_iov.iov[0].iov_len = umd->length;
+
+ if ((umd->options & LNET_MD_MAX_SIZE) != 0 && /* max size used */
+ (umd->max_size < 0 ||
+ umd->max_size > (int)umd->length)) /* illegal max_size */
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/* must be called with resource lock held */
+static int
+lnet_md_link(lnet_libmd_t *md, lnet_handle_eq_t eq_handle, int cpt)
+{
+ struct lnet_res_container *container = the_lnet.ln_md_containers[cpt];
+
+ /* NB we are passed an allocated, but inactive md.
+ * if we return success, caller may lnet_md_unlink() it.
+ * otherwise caller may only lnet_md_free() it.
+ */
+ /* This implementation doesn't know how to create START events or
+ * disable END events. Best to LASSERT our caller is compliant so
+ * we find out quickly... */
+ /* TODO - reevaluate what should be here in light of
+ * the removal of the start and end events
+ * maybe there we shouldn't even allow LNET_EQ_NONE!)
+ * LASSERT (eq == NULL);
+ */
+ if (!LNetHandleIsInvalid(eq_handle)) {
+ md->md_eq = lnet_handle2eq(&eq_handle);
+
+ if (md->md_eq == NULL)
+ return -ENOENT;
+
+ (*md->md_eq->eq_refs[cpt])++;
+ }
+
+ lnet_res_lh_initialize(container, &md->md_lh);
+
+ LASSERT(list_empty(&md->md_list));
+ list_add(&md->md_list, &container->rec_active);
+
+ return 0;
+}
+
+/* must be called with lnet_res_lock held */
+void
+lnet_md_deconstruct(lnet_libmd_t *lmd, lnet_md_t *umd)
+{
+ /* NB this doesn't copy out all the iov entries so when a
+ * discontiguous MD is copied out, the target gets to know the
+ * original iov pointer (in start) and the number of entries it had
+ * and that's all.
+ */
+ umd->start = lmd->md_start;
+ umd->length = ((lmd->md_options &
+ (LNET_MD_IOVEC | LNET_MD_KIOV)) == 0) ?
+ lmd->md_length : lmd->md_niov;
+ umd->threshold = lmd->md_threshold;
+ umd->max_size = lmd->md_max_size;
+ umd->options = lmd->md_options;
+ umd->user_ptr = lmd->md_user_ptr;
+ lnet_eq2handle(&umd->eq_handle, lmd->md_eq);
+}
+
+static int
+lnet_md_validate(lnet_md_t *umd)
+{
+ if (umd->start == NULL && umd->length != 0) {
+ CERROR("MD start pointer can not be NULL with length %u\n",
+ umd->length);
+ return -EINVAL;
+ }
+
+ if ((umd->options & (LNET_MD_KIOV | LNET_MD_IOVEC)) != 0 &&
+ umd->length > LNET_MAX_IOV) {
+ CERROR("Invalid option: too many fragments %u, %d max\n",
+ umd->length, LNET_MAX_IOV);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/**
+ * Create a memory descriptor and attach it to a ME
+ *
+ * \param meh A handle for a ME to associate the new MD with.
+ * \param umd Provides initial values for the user-visible parts of a MD.
+ * Other than its use for initialization, there is no linkage between this
+ * structure and the MD maintained by the LNet.
+ * \param unlink A flag to indicate whether the MD is automatically unlinked
+ * when it becomes inactive, either because the operation threshold drops to
+ * zero or because the available memory becomes less than \a umd.max_size.
+ * (Note that the check for unlinking a MD only occurs after the completion
+ * of a successful operation on the MD.) The value LNET_UNLINK enables auto
+ * unlinking; the value LNET_RETAIN disables it.
+ * \param handle On successful returns, a handle to the newly created MD is
+ * saved here. This handle can be used later in LNetMDUnlink().
+ *
+ * \retval 0 On success.
+ * \retval -EINVAL If \a umd is not valid.
+ * \retval -ENOMEM If new MD cannot be allocated.
+ * \retval -ENOENT Either \a meh or \a umd.eq_handle does not point to a
+ * valid object. Note that it's OK to supply a NULL \a umd.eq_handle by
+ * calling LNetInvalidateHandle() on it.
+ * \retval -EBUSY If the ME pointed to by \a meh is already associated with
+ * a MD.
+ */
+int
+LNetMDAttach(lnet_handle_me_t meh, lnet_md_t umd,
+ lnet_unlink_t unlink, lnet_handle_md_t *handle)
+{
+ LIST_HEAD(matches);
+ LIST_HEAD(drops);
+ struct lnet_me *me;
+ struct lnet_libmd *md;
+ int cpt;
+ int rc;
+
+ LASSERT(the_lnet.ln_init);
+ LASSERT(the_lnet.ln_refcount > 0);
+
+ if (lnet_md_validate(&umd) != 0)
+ return -EINVAL;
+
+ if ((umd.options & (LNET_MD_OP_GET | LNET_MD_OP_PUT)) == 0) {
+ CERROR("Invalid option: no MD_OP set\n");
+ return -EINVAL;
+ }
+
+ md = lnet_md_alloc(&umd);
+ if (md == NULL)
+ return -ENOMEM;
+
+ rc = lnet_md_build(md, &umd, unlink);
+ cpt = lnet_cpt_of_cookie(meh.cookie);
+
+ lnet_res_lock(cpt);
+ if (rc != 0)
+ goto failed;
+
+ me = lnet_handle2me(&meh);
+ if (me == NULL)
+ rc = -ENOENT;
+ else if (me->me_md != NULL)
+ rc = -EBUSY;
+ else
+ rc = lnet_md_link(md, umd.eq_handle, cpt);
+
+ if (rc != 0)
+ goto failed;
+
+ /* attach this MD to portal of ME and check if it matches any
+ * blocked msgs on this portal */
+ lnet_ptl_attach_md(me, md, &matches, &drops);
+
+ lnet_md2handle(handle, md);
+
+ lnet_res_unlock(cpt);
+
+ lnet_drop_delayed_msg_list(&drops, "Bad match");
+ lnet_recv_delayed_msg_list(&matches);
+
+ return 0;
+
+ failed:
+ lnet_md_free_locked(md);
+
+ lnet_res_unlock(cpt);
+ return rc;
+}
+EXPORT_SYMBOL(LNetMDAttach);
+
+/**
+ * Create a "free floating" memory descriptor - a MD that is not associated
+ * with a ME. Such MDs are usually used in LNetPut() and LNetGet() operations.
+ *
+ * \param umd,unlink See the discussion for LNetMDAttach().
+ * \param handle On successful returns, a handle to the newly created MD is
+ * saved here. This handle can be used later in LNetMDUnlink(), LNetPut(),
+ * and LNetGet() operations.
+ *
+ * \retval 0 On success.
+ * \retval -EINVAL If \a umd is not valid.
+ * \retval -ENOMEM If new MD cannot be allocated.
+ * \retval -ENOENT \a umd.eq_handle does not point to a valid EQ. Note that
+ * it's OK to supply a NULL \a umd.eq_handle by calling
+ * LNetInvalidateHandle() on it.
+ */
+int
+LNetMDBind(lnet_md_t umd, lnet_unlink_t unlink, lnet_handle_md_t *handle)
+{
+ lnet_libmd_t *md;
+ int cpt;
+ int rc;
+
+ LASSERT(the_lnet.ln_init);
+ LASSERT(the_lnet.ln_refcount > 0);
+
+ if (lnet_md_validate(&umd) != 0)
+ return -EINVAL;
+
+ if ((umd.options & (LNET_MD_OP_GET | LNET_MD_OP_PUT)) != 0) {
+ CERROR("Invalid option: GET|PUT illegal on active MDs\n");
+ return -EINVAL;
+ }
+
+ md = lnet_md_alloc(&umd);
+ if (md == NULL)
+ return -ENOMEM;
+
+ rc = lnet_md_build(md, &umd, unlink);
+
+ cpt = lnet_res_lock_current();
+ if (rc != 0)
+ goto failed;
+
+ rc = lnet_md_link(md, umd.eq_handle, cpt);
+ if (rc != 0)
+ goto failed;
+
+ lnet_md2handle(handle, md);
+
+ lnet_res_unlock(cpt);
+ return 0;
+
+ failed:
+ lnet_md_free_locked(md);
+
+ lnet_res_unlock(cpt);
+ return rc;
+}
+EXPORT_SYMBOL(LNetMDBind);
+
+/**
+ * Unlink the memory descriptor from any ME it may be linked to and release
+ * the internal resources associated with it. As a result, active messages
+ * associated with the MD may get aborted.
+ *
+ * This function does not free the memory region associated with the MD;
+ * i.e., the memory the user allocated for this MD. If the ME associated with
+ * this MD is not NULL and was created with auto unlink enabled, the ME is
+ * unlinked as well (see LNetMEAttach()).
+ *
+ * Explicitly unlinking a MD via this function call has the same behavior as
+ * a MD that has been automatically unlinked, except that no LNET_EVENT_UNLINK
+ * is generated in the latter case.
+ *
+ * An unlinked event can be reported in two ways:
+ * - If there's no pending operations on the MD, it's unlinked immediately
+ * and an LNET_EVENT_UNLINK event is logged before this function returns.
+ * - Otherwise, the MD is only marked for deletion when this function
+ * returns, and the unlinked event will be piggybacked on the event of
+ * the completion of the last operation by setting the unlinked field of
+ * the event. No dedicated LNET_EVENT_UNLINK event is generated.
+ *
+ * Note that in both cases the unlinked field of the event is always set; no
+ * more event will happen on the MD after such an event is logged.
+ *
+ * \param mdh A handle for the MD to be unlinked.
+ *
+ * \retval 0 On success.
+ * \retval -ENOENT If \a mdh does not point to a valid MD object.
+ */
+int
+LNetMDUnlink(lnet_handle_md_t mdh)
+{
+ lnet_event_t ev;
+ lnet_libmd_t *md;
+ int cpt;
+
+ LASSERT(the_lnet.ln_init);
+ LASSERT(the_lnet.ln_refcount > 0);
+
+ cpt = lnet_cpt_of_cookie(mdh.cookie);
+ lnet_res_lock(cpt);
+
+ md = lnet_handle2md(&mdh);
+ if (md == NULL) {
+ lnet_res_unlock(cpt);
+ return -ENOENT;
+ }
+
+ md->md_flags |= LNET_MD_FLAG_ABORTED;
+ /* If the MD is busy, lnet_md_unlink just marks it for deletion, and
+ * when the LND is done, the completion event flags that the MD was
+ * unlinked. Otherwise, we enqueue an event now... */
+ if (md->md_eq != NULL && md->md_refcount == 0) {
+ lnet_build_unlink_event(md, &ev);
+ lnet_eq_enqueue_event(md->md_eq, &ev);
+ }
+
+ lnet_md_unlink(md);
+
+ lnet_res_unlock(cpt);
+ return 0;
+}
+EXPORT_SYMBOL(LNetMDUnlink);
diff --git a/drivers/staging/lustre/lnet/lnet/lib-me.c b/drivers/staging/lustre/lnet/lnet/lib-me.c
new file mode 100644
index 000000000..a3f929244
--- /dev/null
+++ b/drivers/staging/lustre/lnet/lnet/lib-me.c
@@ -0,0 +1,298 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2012, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ *
+ * lnet/lnet/lib-me.c
+ *
+ * Match Entry management routines
+ */
+
+#define DEBUG_SUBSYSTEM S_LNET
+
+#include "../../include/linux/lnet/lib-lnet.h"
+
+/**
+ * Create and attach a match entry to the match list of \a portal. The new
+ * ME is empty, i.e. not associated with a memory descriptor. LNetMDAttach()
+ * can be used to attach a MD to an empty ME.
+ *
+ * \param portal The portal table index where the ME should be attached.
+ * \param match_id Specifies the match criteria for the process ID of
+ * the requester. The constants LNET_PID_ANY and LNET_NID_ANY can be
+ * used to wildcard either of the identifiers in the lnet_process_id_t
+ * structure.
+ * \param match_bits,ignore_bits Specify the match criteria to apply
+ * to the match bits in the incoming request. The ignore bits are used
+ * to mask out insignificant bits in the incoming match bits. The resulting
+ * bits are then compared to the ME's match bits to determine if the
+ * incoming request meets the match criteria.
+ * \param unlink Indicates whether the ME should be unlinked when the memory
+ * descriptor associated with it is unlinked (Note that the check for
+ * unlinking a ME only occurs when the memory descriptor is unlinked.).
+ * Valid values are LNET_RETAIN and LNET_UNLINK.
+ * \param pos Indicates whether the new ME should be prepended or
+ * appended to the match list. Allowed constants: LNET_INS_BEFORE,
+ * LNET_INS_AFTER.
+ * \param handle On successful returns, a handle to the newly created ME
+ * object is saved here. This handle can be used later in LNetMEInsert(),
+ * LNetMEUnlink(), or LNetMDAttach() functions.
+ *
+ * \retval 0 On success.
+ * \retval -EINVAL If \a portal is invalid.
+ * \retval -ENOMEM If new ME object cannot be allocated.
+ */
+int
+LNetMEAttach(unsigned int portal,
+ lnet_process_id_t match_id,
+ __u64 match_bits, __u64 ignore_bits,
+ lnet_unlink_t unlink, lnet_ins_pos_t pos,
+ lnet_handle_me_t *handle)
+{
+ struct lnet_match_table *mtable;
+ struct lnet_me *me;
+ struct list_head *head;
+
+ LASSERT(the_lnet.ln_init);
+ LASSERT(the_lnet.ln_refcount > 0);
+
+ if ((int)portal >= the_lnet.ln_nportals)
+ return -EINVAL;
+
+ mtable = lnet_mt_of_attach(portal, match_id,
+ match_bits, ignore_bits, pos);
+ if (mtable == NULL) /* can't match portal type */
+ return -EPERM;
+
+ me = lnet_me_alloc();
+ if (me == NULL)
+ return -ENOMEM;
+
+ lnet_res_lock(mtable->mt_cpt);
+
+ me->me_portal = portal;
+ me->me_match_id = match_id;
+ me->me_match_bits = match_bits;
+ me->me_ignore_bits = ignore_bits;
+ me->me_unlink = unlink;
+ me->me_md = NULL;
+
+ lnet_res_lh_initialize(the_lnet.ln_me_containers[mtable->mt_cpt],
+ &me->me_lh);
+ if (ignore_bits != 0)
+ head = &mtable->mt_mhash[LNET_MT_HASH_IGNORE];
+ else
+ head = lnet_mt_match_head(mtable, match_id, match_bits);
+
+ me->me_pos = head - &mtable->mt_mhash[0];
+ if (pos == LNET_INS_AFTER || pos == LNET_INS_LOCAL)
+ list_add_tail(&me->me_list, head);
+ else
+ list_add(&me->me_list, head);
+
+ lnet_me2handle(handle, me);
+
+ lnet_res_unlock(mtable->mt_cpt);
+ return 0;
+}
+EXPORT_SYMBOL(LNetMEAttach);
+
+/**
+ * Create and a match entry and insert it before or after the ME pointed to by
+ * \a current_meh. The new ME is empty, i.e. not associated with a memory
+ * descriptor. LNetMDAttach() can be used to attach a MD to an empty ME.
+ *
+ * This function is identical to LNetMEAttach() except for the position
+ * where the new ME is inserted.
+ *
+ * \param current_meh A handle for a ME. The new ME will be inserted
+ * immediately before or immediately after this ME.
+ * \param match_id,match_bits,ignore_bits,unlink,pos,handle See the discussion
+ * for LNetMEAttach().
+ *
+ * \retval 0 On success.
+ * \retval -ENOMEM If new ME object cannot be allocated.
+ * \retval -ENOENT If \a current_meh does not point to a valid match entry.
+ */
+int
+LNetMEInsert(lnet_handle_me_t current_meh,
+ lnet_process_id_t match_id,
+ __u64 match_bits, __u64 ignore_bits,
+ lnet_unlink_t unlink, lnet_ins_pos_t pos,
+ lnet_handle_me_t *handle)
+{
+ struct lnet_me *current_me;
+ struct lnet_me *new_me;
+ struct lnet_portal *ptl;
+ int cpt;
+
+ LASSERT(the_lnet.ln_init);
+ LASSERT(the_lnet.ln_refcount > 0);
+
+ if (pos == LNET_INS_LOCAL)
+ return -EPERM;
+
+ new_me = lnet_me_alloc();
+ if (new_me == NULL)
+ return -ENOMEM;
+
+ cpt = lnet_cpt_of_cookie(current_meh.cookie);
+
+ lnet_res_lock(cpt);
+
+ current_me = lnet_handle2me(&current_meh);
+ if (current_me == NULL) {
+ lnet_me_free_locked(new_me);
+
+ lnet_res_unlock(cpt);
+ return -ENOENT;
+ }
+
+ LASSERT(current_me->me_portal < the_lnet.ln_nportals);
+
+ ptl = the_lnet.ln_portals[current_me->me_portal];
+ if (lnet_ptl_is_unique(ptl)) {
+ /* nosense to insertion on unique portal */
+ lnet_me_free_locked(new_me);
+ lnet_res_unlock(cpt);
+ return -EPERM;
+ }
+
+ new_me->me_pos = current_me->me_pos;
+ new_me->me_portal = current_me->me_portal;
+ new_me->me_match_id = match_id;
+ new_me->me_match_bits = match_bits;
+ new_me->me_ignore_bits = ignore_bits;
+ new_me->me_unlink = unlink;
+ new_me->me_md = NULL;
+
+ lnet_res_lh_initialize(the_lnet.ln_me_containers[cpt], &new_me->me_lh);
+
+ if (pos == LNET_INS_AFTER)
+ list_add(&new_me->me_list, &current_me->me_list);
+ else
+ list_add_tail(&new_me->me_list, &current_me->me_list);
+
+ lnet_me2handle(handle, new_me);
+
+ lnet_res_unlock(cpt);
+
+ return 0;
+}
+EXPORT_SYMBOL(LNetMEInsert);
+
+/**
+ * Unlink a match entry from its match list.
+ *
+ * This operation also releases any resources associated with the ME. If a
+ * memory descriptor is attached to the ME, then it will be unlinked as well
+ * and an unlink event will be generated. It is an error to use the ME handle
+ * after calling LNetMEUnlink().
+ *
+ * \param meh A handle for the ME to be unlinked.
+ *
+ * \retval 0 On success.
+ * \retval -ENOENT If \a meh does not point to a valid ME.
+ * \see LNetMDUnlink() for the discussion on delivering unlink event.
+ */
+int
+LNetMEUnlink(lnet_handle_me_t meh)
+{
+ lnet_me_t *me;
+ lnet_libmd_t *md;
+ lnet_event_t ev;
+ int cpt;
+
+ LASSERT(the_lnet.ln_init);
+ LASSERT(the_lnet.ln_refcount > 0);
+
+ cpt = lnet_cpt_of_cookie(meh.cookie);
+ lnet_res_lock(cpt);
+
+ me = lnet_handle2me(&meh);
+ if (me == NULL) {
+ lnet_res_unlock(cpt);
+ return -ENOENT;
+ }
+
+ md = me->me_md;
+ if (md != NULL) {
+ md->md_flags |= LNET_MD_FLAG_ABORTED;
+ if (md->md_eq != NULL && md->md_refcount == 0) {
+ lnet_build_unlink_event(md, &ev);
+ lnet_eq_enqueue_event(md->md_eq, &ev);
+ }
+ }
+
+ lnet_me_unlink(me);
+
+ lnet_res_unlock(cpt);
+ return 0;
+}
+EXPORT_SYMBOL(LNetMEUnlink);
+
+/* call with lnet_res_lock please */
+void
+lnet_me_unlink(lnet_me_t *me)
+{
+ list_del(&me->me_list);
+
+ if (me->me_md != NULL) {
+ lnet_libmd_t *md = me->me_md;
+
+ /* detach MD from portal of this ME */
+ lnet_ptl_detach_md(me, md);
+ lnet_md_unlink(md);
+ }
+
+ lnet_res_lh_invalidate(&me->me_lh);
+ lnet_me_free_locked(me);
+}
+
+#if 0
+static void
+lib_me_dump(lnet_me_t *me)
+{
+ CWARN("Match Entry %p (%#llx)\n", me,
+ me->me_lh.lh_cookie);
+
+ CWARN("\tMatch/Ignore\t= %016lx / %016lx\n",
+ me->me_match_bits, me->me_ignore_bits);
+
+ CWARN("\tMD\t= %p\n", me->md);
+ CWARN("\tprev\t= %p\n",
+ list_entry(me->me_list.prev, lnet_me_t, me_list));
+ CWARN("\tnext\t= %p\n",
+ list_entry(me->me_list.next, lnet_me_t, me_list));
+}
+#endif
diff --git a/drivers/staging/lustre/lnet/lnet/lib-move.c b/drivers/staging/lustre/lnet/lnet/lib-move.c
new file mode 100644
index 000000000..c2fb70e5f
--- /dev/null
+++ b/drivers/staging/lustre/lnet/lnet/lib-move.c
@@ -0,0 +1,2460 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2011, 2012, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ *
+ * lnet/lnet/lib-move.c
+ *
+ * Data movement routines
+ */
+
+#define DEBUG_SUBSYSTEM S_LNET
+
+#include "../../include/linux/lnet/lib-lnet.h"
+
+static int local_nid_dist_zero = 1;
+module_param(local_nid_dist_zero, int, 0444);
+MODULE_PARM_DESC(local_nid_dist_zero, "Reserved");
+
+int
+lnet_fail_nid(lnet_nid_t nid, unsigned int threshold)
+{
+ lnet_test_peer_t *tp;
+ struct list_head *el;
+ struct list_head *next;
+ struct list_head cull;
+
+ LASSERT(the_lnet.ln_init);
+
+ /* NB: use lnet_net_lock(0) to serialize operations on test peers */
+ if (threshold != 0) {
+ /* Adding a new entry */
+ LIBCFS_ALLOC(tp, sizeof(*tp));
+ if (tp == NULL)
+ return -ENOMEM;
+
+ tp->tp_nid = nid;
+ tp->tp_threshold = threshold;
+
+ lnet_net_lock(0);
+ list_add_tail(&tp->tp_list, &the_lnet.ln_test_peers);
+ lnet_net_unlock(0);
+ return 0;
+ }
+
+ /* removing entries */
+ INIT_LIST_HEAD(&cull);
+
+ lnet_net_lock(0);
+
+ list_for_each_safe(el, next, &the_lnet.ln_test_peers) {
+ tp = list_entry(el, lnet_test_peer_t, tp_list);
+
+ if (tp->tp_threshold == 0 || /* needs culling anyway */
+ nid == LNET_NID_ANY || /* removing all entries */
+ tp->tp_nid == nid) { /* matched this one */
+ list_del(&tp->tp_list);
+ list_add(&tp->tp_list, &cull);
+ }
+ }
+
+ lnet_net_unlock(0);
+
+ while (!list_empty(&cull)) {
+ tp = list_entry(cull.next, lnet_test_peer_t, tp_list);
+
+ list_del(&tp->tp_list);
+ LIBCFS_FREE(tp, sizeof(*tp));
+ }
+ return 0;
+}
+
+static int
+fail_peer(lnet_nid_t nid, int outgoing)
+{
+ lnet_test_peer_t *tp;
+ struct list_head *el;
+ struct list_head *next;
+ struct list_head cull;
+ int fail = 0;
+
+ INIT_LIST_HEAD(&cull);
+
+ /* NB: use lnet_net_lock(0) to serialize operations on test peers */
+ lnet_net_lock(0);
+
+ list_for_each_safe(el, next, &the_lnet.ln_test_peers) {
+ tp = list_entry(el, lnet_test_peer_t, tp_list);
+
+ if (tp->tp_threshold == 0) {
+ /* zombie entry */
+ if (outgoing) {
+ /* only cull zombies on outgoing tests,
+ * since we may be at interrupt priority on
+ * incoming messages. */
+ list_del(&tp->tp_list);
+ list_add(&tp->tp_list, &cull);
+ }
+ continue;
+ }
+
+ if (tp->tp_nid == LNET_NID_ANY || /* fail every peer */
+ nid == tp->tp_nid) { /* fail this peer */
+ fail = 1;
+
+ if (tp->tp_threshold != LNET_MD_THRESH_INF) {
+ tp->tp_threshold--;
+ if (outgoing &&
+ tp->tp_threshold == 0) {
+ /* see above */
+ list_del(&tp->tp_list);
+ list_add(&tp->tp_list, &cull);
+ }
+ }
+ break;
+ }
+ }
+
+ lnet_net_unlock(0);
+
+ while (!list_empty(&cull)) {
+ tp = list_entry(cull.next, lnet_test_peer_t, tp_list);
+ list_del(&tp->tp_list);
+
+ LIBCFS_FREE(tp, sizeof(*tp));
+ }
+
+ return fail;
+}
+
+unsigned int
+lnet_iov_nob(unsigned int niov, struct kvec *iov)
+{
+ unsigned int nob = 0;
+
+ while (niov-- > 0)
+ nob += (iov++)->iov_len;
+
+ return nob;
+}
+EXPORT_SYMBOL(lnet_iov_nob);
+
+void
+lnet_copy_iov2iov(unsigned int ndiov, struct kvec *diov, unsigned int doffset,
+ unsigned int nsiov, struct kvec *siov, unsigned int soffset,
+ unsigned int nob)
+{
+ /* NB diov, siov are READ-ONLY */
+ unsigned int this_nob;
+
+ if (nob == 0)
+ return;
+
+ /* skip complete frags before 'doffset' */
+ LASSERT(ndiov > 0);
+ while (doffset >= diov->iov_len) {
+ doffset -= diov->iov_len;
+ diov++;
+ ndiov--;
+ LASSERT(ndiov > 0);
+ }
+
+ /* skip complete frags before 'soffset' */
+ LASSERT(nsiov > 0);
+ while (soffset >= siov->iov_len) {
+ soffset -= siov->iov_len;
+ siov++;
+ nsiov--;
+ LASSERT(nsiov > 0);
+ }
+
+ do {
+ LASSERT(ndiov > 0);
+ LASSERT(nsiov > 0);
+ this_nob = min(diov->iov_len - doffset,
+ siov->iov_len - soffset);
+ this_nob = min(this_nob, nob);
+
+ memcpy((char *)diov->iov_base + doffset,
+ (char *)siov->iov_base + soffset, this_nob);
+ nob -= this_nob;
+
+ if (diov->iov_len > doffset + this_nob) {
+ doffset += this_nob;
+ } else {
+ diov++;
+ ndiov--;
+ doffset = 0;
+ }
+
+ if (siov->iov_len > soffset + this_nob) {
+ soffset += this_nob;
+ } else {
+ siov++;
+ nsiov--;
+ soffset = 0;
+ }
+ } while (nob > 0);
+}
+EXPORT_SYMBOL(lnet_copy_iov2iov);
+
+int
+lnet_extract_iov(int dst_niov, struct kvec *dst,
+ int src_niov, struct kvec *src,
+ unsigned int offset, unsigned int len)
+{
+ /* Initialise 'dst' to the subset of 'src' starting at 'offset',
+ * for exactly 'len' bytes, and return the number of entries.
+ * NB not destructive to 'src' */
+ unsigned int frag_len;
+ unsigned int niov;
+
+ if (len == 0) /* no data => */
+ return 0; /* no frags */
+
+ LASSERT(src_niov > 0);
+ while (offset >= src->iov_len) { /* skip initial frags */
+ offset -= src->iov_len;
+ src_niov--;
+ src++;
+ LASSERT(src_niov > 0);
+ }
+
+ niov = 1;
+ for (;;) {
+ LASSERT(src_niov > 0);
+ LASSERT((int)niov <= dst_niov);
+
+ frag_len = src->iov_len - offset;
+ dst->iov_base = ((char *)src->iov_base) + offset;
+
+ if (len <= frag_len) {
+ dst->iov_len = len;
+ return niov;
+ }
+
+ dst->iov_len = frag_len;
+
+ len -= frag_len;
+ dst++;
+ src++;
+ niov++;
+ src_niov--;
+ offset = 0;
+ }
+}
+EXPORT_SYMBOL(lnet_extract_iov);
+
+
+unsigned int
+lnet_kiov_nob(unsigned int niov, lnet_kiov_t *kiov)
+{
+ unsigned int nob = 0;
+
+ while (niov-- > 0)
+ nob += (kiov++)->kiov_len;
+
+ return nob;
+}
+EXPORT_SYMBOL(lnet_kiov_nob);
+
+void
+lnet_copy_kiov2kiov(unsigned int ndiov, lnet_kiov_t *diov, unsigned int doffset,
+ unsigned int nsiov, lnet_kiov_t *siov, unsigned int soffset,
+ unsigned int nob)
+{
+ /* NB diov, siov are READ-ONLY */
+ unsigned int this_nob;
+ char *daddr = NULL;
+ char *saddr = NULL;
+
+ if (nob == 0)
+ return;
+
+ LASSERT(!in_interrupt());
+
+ LASSERT(ndiov > 0);
+ while (doffset >= diov->kiov_len) {
+ doffset -= diov->kiov_len;
+ diov++;
+ ndiov--;
+ LASSERT(ndiov > 0);
+ }
+
+ LASSERT(nsiov > 0);
+ while (soffset >= siov->kiov_len) {
+ soffset -= siov->kiov_len;
+ siov++;
+ nsiov--;
+ LASSERT(nsiov > 0);
+ }
+
+ do {
+ LASSERT(ndiov > 0);
+ LASSERT(nsiov > 0);
+ this_nob = min(diov->kiov_len - doffset,
+ siov->kiov_len - soffset);
+ this_nob = min(this_nob, nob);
+
+ if (daddr == NULL)
+ daddr = ((char *)kmap(diov->kiov_page)) +
+ diov->kiov_offset + doffset;
+ if (saddr == NULL)
+ saddr = ((char *)kmap(siov->kiov_page)) +
+ siov->kiov_offset + soffset;
+
+ /* Vanishing risk of kmap deadlock when mapping 2 pages.
+ * However in practice at least one of the kiovs will be mapped
+ * kernel pages and the map/unmap will be NOOPs */
+
+ memcpy(daddr, saddr, this_nob);
+ nob -= this_nob;
+
+ if (diov->kiov_len > doffset + this_nob) {
+ daddr += this_nob;
+ doffset += this_nob;
+ } else {
+ kunmap(diov->kiov_page);
+ daddr = NULL;
+ diov++;
+ ndiov--;
+ doffset = 0;
+ }
+
+ if (siov->kiov_len > soffset + this_nob) {
+ saddr += this_nob;
+ soffset += this_nob;
+ } else {
+ kunmap(siov->kiov_page);
+ saddr = NULL;
+ siov++;
+ nsiov--;
+ soffset = 0;
+ }
+ } while (nob > 0);
+
+ if (daddr != NULL)
+ kunmap(diov->kiov_page);
+ if (saddr != NULL)
+ kunmap(siov->kiov_page);
+}
+EXPORT_SYMBOL(lnet_copy_kiov2kiov);
+
+void
+lnet_copy_kiov2iov(unsigned int niov, struct kvec *iov, unsigned int iovoffset,
+ unsigned int nkiov, lnet_kiov_t *kiov,
+ unsigned int kiovoffset, unsigned int nob)
+{
+ /* NB iov, kiov are READ-ONLY */
+ unsigned int this_nob;
+ char *addr = NULL;
+
+ if (nob == 0)
+ return;
+
+ LASSERT(!in_interrupt());
+
+ LASSERT(niov > 0);
+ while (iovoffset >= iov->iov_len) {
+ iovoffset -= iov->iov_len;
+ iov++;
+ niov--;
+ LASSERT(niov > 0);
+ }
+
+ LASSERT(nkiov > 0);
+ while (kiovoffset >= kiov->kiov_len) {
+ kiovoffset -= kiov->kiov_len;
+ kiov++;
+ nkiov--;
+ LASSERT(nkiov > 0);
+ }
+
+ do {
+ LASSERT(niov > 0);
+ LASSERT(nkiov > 0);
+ this_nob = min(iov->iov_len - iovoffset,
+ (__kernel_size_t) kiov->kiov_len - kiovoffset);
+ this_nob = min(this_nob, nob);
+
+ if (addr == NULL)
+ addr = ((char *)kmap(kiov->kiov_page)) +
+ kiov->kiov_offset + kiovoffset;
+
+ memcpy((char *)iov->iov_base + iovoffset, addr, this_nob);
+ nob -= this_nob;
+
+ if (iov->iov_len > iovoffset + this_nob) {
+ iovoffset += this_nob;
+ } else {
+ iov++;
+ niov--;
+ iovoffset = 0;
+ }
+
+ if (kiov->kiov_len > kiovoffset + this_nob) {
+ addr += this_nob;
+ kiovoffset += this_nob;
+ } else {
+ kunmap(kiov->kiov_page);
+ addr = NULL;
+ kiov++;
+ nkiov--;
+ kiovoffset = 0;
+ }
+
+ } while (nob > 0);
+
+ if (addr != NULL)
+ kunmap(kiov->kiov_page);
+}
+EXPORT_SYMBOL(lnet_copy_kiov2iov);
+
+void
+lnet_copy_iov2kiov(unsigned int nkiov, lnet_kiov_t *kiov,
+ unsigned int kiovoffset, unsigned int niov,
+ struct kvec *iov, unsigned int iovoffset,
+ unsigned int nob)
+{
+ /* NB kiov, iov are READ-ONLY */
+ unsigned int this_nob;
+ char *addr = NULL;
+
+ if (nob == 0)
+ return;
+
+ LASSERT(!in_interrupt());
+
+ LASSERT(nkiov > 0);
+ while (kiovoffset >= kiov->kiov_len) {
+ kiovoffset -= kiov->kiov_len;
+ kiov++;
+ nkiov--;
+ LASSERT(nkiov > 0);
+ }
+
+ LASSERT(niov > 0);
+ while (iovoffset >= iov->iov_len) {
+ iovoffset -= iov->iov_len;
+ iov++;
+ niov--;
+ LASSERT(niov > 0);
+ }
+
+ do {
+ LASSERT(nkiov > 0);
+ LASSERT(niov > 0);
+ this_nob = min((__kernel_size_t) kiov->kiov_len - kiovoffset,
+ iov->iov_len - iovoffset);
+ this_nob = min(this_nob, nob);
+
+ if (addr == NULL)
+ addr = ((char *)kmap(kiov->kiov_page)) +
+ kiov->kiov_offset + kiovoffset;
+
+ memcpy(addr, (char *)iov->iov_base + iovoffset, this_nob);
+ nob -= this_nob;
+
+ if (kiov->kiov_len > kiovoffset + this_nob) {
+ addr += this_nob;
+ kiovoffset += this_nob;
+ } else {
+ kunmap(kiov->kiov_page);
+ addr = NULL;
+ kiov++;
+ nkiov--;
+ kiovoffset = 0;
+ }
+
+ if (iov->iov_len > iovoffset + this_nob) {
+ iovoffset += this_nob;
+ } else {
+ iov++;
+ niov--;
+ iovoffset = 0;
+ }
+ } while (nob > 0);
+
+ if (addr != NULL)
+ kunmap(kiov->kiov_page);
+}
+EXPORT_SYMBOL(lnet_copy_iov2kiov);
+
+int
+lnet_extract_kiov(int dst_niov, lnet_kiov_t *dst,
+ int src_niov, lnet_kiov_t *src,
+ unsigned int offset, unsigned int len)
+{
+ /* Initialise 'dst' to the subset of 'src' starting at 'offset',
+ * for exactly 'len' bytes, and return the number of entries.
+ * NB not destructive to 'src' */
+ unsigned int frag_len;
+ unsigned int niov;
+
+ if (len == 0) /* no data => */
+ return 0; /* no frags */
+
+ LASSERT(src_niov > 0);
+ while (offset >= src->kiov_len) { /* skip initial frags */
+ offset -= src->kiov_len;
+ src_niov--;
+ src++;
+ LASSERT(src_niov > 0);
+ }
+
+ niov = 1;
+ for (;;) {
+ LASSERT(src_niov > 0);
+ LASSERT((int)niov <= dst_niov);
+
+ frag_len = src->kiov_len - offset;
+ dst->kiov_page = src->kiov_page;
+ dst->kiov_offset = src->kiov_offset + offset;
+
+ if (len <= frag_len) {
+ dst->kiov_len = len;
+ LASSERT(dst->kiov_offset + dst->kiov_len
+ <= PAGE_CACHE_SIZE);
+ return niov;
+ }
+
+ dst->kiov_len = frag_len;
+ LASSERT(dst->kiov_offset + dst->kiov_len <= PAGE_CACHE_SIZE);
+
+ len -= frag_len;
+ dst++;
+ src++;
+ niov++;
+ src_niov--;
+ offset = 0;
+ }
+}
+EXPORT_SYMBOL(lnet_extract_kiov);
+
+static void
+lnet_ni_recv(lnet_ni_t *ni, void *private, lnet_msg_t *msg, int delayed,
+ unsigned int offset, unsigned int mlen, unsigned int rlen)
+{
+ unsigned int niov = 0;
+ struct kvec *iov = NULL;
+ lnet_kiov_t *kiov = NULL;
+ int rc;
+
+ LASSERT(!in_interrupt());
+ LASSERT(mlen == 0 || msg != NULL);
+
+ if (msg != NULL) {
+ LASSERT(msg->msg_receiving);
+ LASSERT(!msg->msg_sending);
+ LASSERT(rlen == msg->msg_len);
+ LASSERT(mlen <= msg->msg_len);
+ LASSERT(msg->msg_offset == offset);
+ LASSERT(msg->msg_wanted == mlen);
+
+ msg->msg_receiving = 0;
+
+ if (mlen != 0) {
+ niov = msg->msg_niov;
+ iov = msg->msg_iov;
+ kiov = msg->msg_kiov;
+
+ LASSERT(niov > 0);
+ LASSERT((iov == NULL) != (kiov == NULL));
+ }
+ }
+
+ rc = (ni->ni_lnd->lnd_recv)(ni, private, msg, delayed,
+ niov, iov, kiov, offset, mlen, rlen);
+ if (rc < 0)
+ lnet_finalize(ni, msg, rc);
+}
+
+static void
+lnet_setpayloadbuffer(lnet_msg_t *msg)
+{
+ lnet_libmd_t *md = msg->msg_md;
+
+ LASSERT(msg->msg_len > 0);
+ LASSERT(!msg->msg_routing);
+ LASSERT(md != NULL);
+ LASSERT(msg->msg_niov == 0);
+ LASSERT(msg->msg_iov == NULL);
+ LASSERT(msg->msg_kiov == NULL);
+
+ msg->msg_niov = md->md_niov;
+ if ((md->md_options & LNET_MD_KIOV) != 0)
+ msg->msg_kiov = md->md_iov.kiov;
+ else
+ msg->msg_iov = md->md_iov.iov;
+}
+
+void
+lnet_prep_send(lnet_msg_t *msg, int type, lnet_process_id_t target,
+ unsigned int offset, unsigned int len)
+{
+ msg->msg_type = type;
+ msg->msg_target = target;
+ msg->msg_len = len;
+ msg->msg_offset = offset;
+
+ if (len != 0)
+ lnet_setpayloadbuffer(msg);
+
+ memset(&msg->msg_hdr, 0, sizeof(msg->msg_hdr));
+ msg->msg_hdr.type = cpu_to_le32(type);
+ msg->msg_hdr.dest_nid = cpu_to_le64(target.nid);
+ msg->msg_hdr.dest_pid = cpu_to_le32(target.pid);
+ /* src_nid will be set later */
+ msg->msg_hdr.src_pid = cpu_to_le32(the_lnet.ln_pid);
+ msg->msg_hdr.payload_length = cpu_to_le32(len);
+}
+
+static void
+lnet_ni_send(lnet_ni_t *ni, lnet_msg_t *msg)
+{
+ void *priv = msg->msg_private;
+ int rc;
+
+ LASSERT(!in_interrupt());
+ LASSERT(LNET_NETTYP(LNET_NIDNET(ni->ni_nid)) == LOLND ||
+ (msg->msg_txcredit && msg->msg_peertxcredit));
+
+ rc = (ni->ni_lnd->lnd_send)(ni, priv, msg);
+ if (rc < 0)
+ lnet_finalize(ni, msg, rc);
+}
+
+static int
+lnet_ni_eager_recv(lnet_ni_t *ni, lnet_msg_t *msg)
+{
+ int rc;
+
+ LASSERT(!msg->msg_sending);
+ LASSERT(msg->msg_receiving);
+ LASSERT(!msg->msg_rx_ready_delay);
+ LASSERT(ni->ni_lnd->lnd_eager_recv != NULL);
+
+ msg->msg_rx_ready_delay = 1;
+ rc = (ni->ni_lnd->lnd_eager_recv)(ni, msg->msg_private, msg,
+ &msg->msg_private);
+ if (rc != 0) {
+ CERROR("recv from %s / send to %s aborted: eager_recv failed %d\n",
+ libcfs_nid2str(msg->msg_rxpeer->lp_nid),
+ libcfs_id2str(msg->msg_target), rc);
+ LASSERT(rc < 0); /* required by my callers */
+ }
+
+ return rc;
+}
+
+/* NB: caller shall hold a ref on 'lp' as I'd drop lnet_net_lock */
+static void
+lnet_ni_query_locked(lnet_ni_t *ni, lnet_peer_t *lp)
+{
+ unsigned long last_alive = 0;
+
+ LASSERT(lnet_peer_aliveness_enabled(lp));
+ LASSERT(ni->ni_lnd->lnd_query != NULL);
+
+ lnet_net_unlock(lp->lp_cpt);
+ (ni->ni_lnd->lnd_query)(ni, lp->lp_nid, &last_alive);
+ lnet_net_lock(lp->lp_cpt);
+
+ lp->lp_last_query = cfs_time_current();
+
+ if (last_alive != 0) /* NI has updated timestamp */
+ lp->lp_last_alive = last_alive;
+}
+
+/* NB: always called with lnet_net_lock held */
+static inline int
+lnet_peer_is_alive(lnet_peer_t *lp, unsigned long now)
+{
+ int alive;
+ unsigned long deadline;
+
+ LASSERT(lnet_peer_aliveness_enabled(lp));
+
+ /* Trust lnet_notify() if it has more recent aliveness news, but
+ * ignore the initial assumed death (see lnet_peers_start_down()).
+ */
+ if (!lp->lp_alive && lp->lp_alive_count > 0 &&
+ cfs_time_aftereq(lp->lp_timestamp, lp->lp_last_alive))
+ return 0;
+
+ deadline = cfs_time_add(lp->lp_last_alive,
+ cfs_time_seconds(lp->lp_ni->ni_peertimeout));
+ alive = cfs_time_after(deadline, now);
+
+ /* Update obsolete lp_alive except for routers assumed to be dead
+ * initially, because router checker would update aliveness in this
+ * case, and moreover lp_last_alive at peer creation is assumed.
+ */
+ if (alive && !lp->lp_alive &&
+ !(lnet_isrouter(lp) && lp->lp_alive_count == 0))
+ lnet_notify_locked(lp, 0, 1, lp->lp_last_alive);
+
+ return alive;
+}
+
+
+/* NB: returns 1 when alive, 0 when dead, negative when error;
+ * may drop the lnet_net_lock */
+static int
+lnet_peer_alive_locked(lnet_peer_t *lp)
+{
+ unsigned long now = cfs_time_current();
+
+ if (!lnet_peer_aliveness_enabled(lp))
+ return -ENODEV;
+
+ if (lnet_peer_is_alive(lp, now))
+ return 1;
+
+ /* Peer appears dead, but we should avoid frequent NI queries (at
+ * most once per lnet_queryinterval seconds). */
+ if (lp->lp_last_query != 0) {
+ static const int lnet_queryinterval = 1;
+
+ unsigned long next_query =
+ cfs_time_add(lp->lp_last_query,
+ cfs_time_seconds(lnet_queryinterval));
+
+ if (time_before(now, next_query)) {
+ if (lp->lp_alive)
+ CWARN("Unexpected aliveness of peer %s: %d < %d (%d/%d)\n",
+ libcfs_nid2str(lp->lp_nid),
+ (int)now, (int)next_query,
+ lnet_queryinterval,
+ lp->lp_ni->ni_peertimeout);
+ return 0;
+ }
+ }
+
+ /* query NI for latest aliveness news */
+ lnet_ni_query_locked(lp->lp_ni, lp);
+
+ if (lnet_peer_is_alive(lp, now))
+ return 1;
+
+ lnet_notify_locked(lp, 0, 0, lp->lp_last_alive);
+ return 0;
+}
+
+/**
+ * \param msg The message to be sent.
+ * \param do_send True if lnet_ni_send() should be called in this function.
+ * lnet_send() is going to lnet_net_unlock immediately after this, so
+ * it sets do_send FALSE and I don't do the unlock/send/lock bit.
+ *
+ * \retval 0 If \a msg sent or OK to send.
+ * \retval EAGAIN If \a msg blocked for credit.
+ * \retval EHOSTUNREACH If the next hop of the message appears dead.
+ * \retval ECANCELED If the MD of the message has been unlinked.
+ */
+static int
+lnet_post_send_locked(lnet_msg_t *msg, int do_send)
+{
+ lnet_peer_t *lp = msg->msg_txpeer;
+ lnet_ni_t *ni = lp->lp_ni;
+ int cpt = msg->msg_tx_cpt;
+ struct lnet_tx_queue *tq = ni->ni_tx_queues[cpt];
+
+ /* non-lnet_send() callers have checked before */
+ LASSERT(!do_send || msg->msg_tx_delayed);
+ LASSERT(!msg->msg_receiving);
+ LASSERT(msg->msg_tx_committed);
+
+ /* NB 'lp' is always the next hop */
+ if ((msg->msg_target.pid & LNET_PID_USERFLAG) == 0 &&
+ lnet_peer_alive_locked(lp) == 0) {
+ the_lnet.ln_counters[cpt]->drop_count++;
+ the_lnet.ln_counters[cpt]->drop_length += msg->msg_len;
+ lnet_net_unlock(cpt);
+
+ CNETERR("Dropping message for %s: peer not alive\n",
+ libcfs_id2str(msg->msg_target));
+ if (do_send)
+ lnet_finalize(ni, msg, -EHOSTUNREACH);
+
+ lnet_net_lock(cpt);
+ return EHOSTUNREACH;
+ }
+
+ if (msg->msg_md != NULL &&
+ (msg->msg_md->md_flags & LNET_MD_FLAG_ABORTED) != 0) {
+ lnet_net_unlock(cpt);
+
+ CNETERR("Aborting message for %s: LNetM[DE]Unlink() already called on the MD/ME.\n",
+ libcfs_id2str(msg->msg_target));
+ if (do_send)
+ lnet_finalize(ni, msg, -ECANCELED);
+
+ lnet_net_lock(cpt);
+ return ECANCELED;
+ }
+
+ if (!msg->msg_peertxcredit) {
+ LASSERT((lp->lp_txcredits < 0) ==
+ !list_empty(&lp->lp_txq));
+
+ msg->msg_peertxcredit = 1;
+ lp->lp_txqnob += msg->msg_len + sizeof(lnet_hdr_t);
+ lp->lp_txcredits--;
+
+ if (lp->lp_txcredits < lp->lp_mintxcredits)
+ lp->lp_mintxcredits = lp->lp_txcredits;
+
+ if (lp->lp_txcredits < 0) {
+ msg->msg_tx_delayed = 1;
+ list_add_tail(&msg->msg_list, &lp->lp_txq);
+ return EAGAIN;
+ }
+ }
+
+ if (!msg->msg_txcredit) {
+ LASSERT((tq->tq_credits < 0) ==
+ !list_empty(&tq->tq_delayed));
+
+ msg->msg_txcredit = 1;
+ tq->tq_credits--;
+
+ if (tq->tq_credits < tq->tq_credits_min)
+ tq->tq_credits_min = tq->tq_credits;
+
+ if (tq->tq_credits < 0) {
+ msg->msg_tx_delayed = 1;
+ list_add_tail(&msg->msg_list, &tq->tq_delayed);
+ return EAGAIN;
+ }
+ }
+
+ if (do_send) {
+ lnet_net_unlock(cpt);
+ lnet_ni_send(ni, msg);
+ lnet_net_lock(cpt);
+ }
+ return 0;
+}
+
+
+static lnet_rtrbufpool_t *
+lnet_msg2bufpool(lnet_msg_t *msg)
+{
+ lnet_rtrbufpool_t *rbp;
+ int cpt;
+
+ LASSERT(msg->msg_rx_committed);
+
+ cpt = msg->msg_rx_cpt;
+ rbp = &the_lnet.ln_rtrpools[cpt][0];
+
+ LASSERT(msg->msg_len <= LNET_MTU);
+ while (msg->msg_len > (unsigned int)rbp->rbp_npages * PAGE_CACHE_SIZE) {
+ rbp++;
+ LASSERT(rbp < &the_lnet.ln_rtrpools[cpt][LNET_NRBPOOLS]);
+ }
+
+ return rbp;
+}
+
+static int
+lnet_post_routed_recv_locked(lnet_msg_t *msg, int do_recv)
+{
+ /* lnet_parse is going to lnet_net_unlock immediately after this, so it
+ * sets do_recv FALSE and I don't do the unlock/send/lock bit. I
+ * return EAGAIN if msg blocked and 0 if received or OK to receive */
+ lnet_peer_t *lp = msg->msg_rxpeer;
+ lnet_rtrbufpool_t *rbp;
+ lnet_rtrbuf_t *rb;
+
+ LASSERT(msg->msg_iov == NULL);
+ LASSERT(msg->msg_kiov == NULL);
+ LASSERT(msg->msg_niov == 0);
+ LASSERT(msg->msg_routing);
+ LASSERT(msg->msg_receiving);
+ LASSERT(!msg->msg_sending);
+
+ /* non-lnet_parse callers only receive delayed messages */
+ LASSERT(!do_recv || msg->msg_rx_delayed);
+
+ if (!msg->msg_peerrtrcredit) {
+ LASSERT((lp->lp_rtrcredits < 0) ==
+ !list_empty(&lp->lp_rtrq));
+
+ msg->msg_peerrtrcredit = 1;
+ lp->lp_rtrcredits--;
+ if (lp->lp_rtrcredits < lp->lp_minrtrcredits)
+ lp->lp_minrtrcredits = lp->lp_rtrcredits;
+
+ if (lp->lp_rtrcredits < 0) {
+ /* must have checked eager_recv before here */
+ LASSERT(msg->msg_rx_ready_delay);
+ msg->msg_rx_delayed = 1;
+ list_add_tail(&msg->msg_list, &lp->lp_rtrq);
+ return EAGAIN;
+ }
+ }
+
+ rbp = lnet_msg2bufpool(msg);
+
+ if (!msg->msg_rtrcredit) {
+ LASSERT((rbp->rbp_credits < 0) ==
+ !list_empty(&rbp->rbp_msgs));
+
+ msg->msg_rtrcredit = 1;
+ rbp->rbp_credits--;
+ if (rbp->rbp_credits < rbp->rbp_mincredits)
+ rbp->rbp_mincredits = rbp->rbp_credits;
+
+ if (rbp->rbp_credits < 0) {
+ /* must have checked eager_recv before here */
+ LASSERT(msg->msg_rx_ready_delay);
+ msg->msg_rx_delayed = 1;
+ list_add_tail(&msg->msg_list, &rbp->rbp_msgs);
+ return EAGAIN;
+ }
+ }
+
+ LASSERT(!list_empty(&rbp->rbp_bufs));
+ rb = list_entry(rbp->rbp_bufs.next, lnet_rtrbuf_t, rb_list);
+ list_del(&rb->rb_list);
+
+ msg->msg_niov = rbp->rbp_npages;
+ msg->msg_kiov = &rb->rb_kiov[0];
+
+ if (do_recv) {
+ int cpt = msg->msg_rx_cpt;
+
+ lnet_net_unlock(cpt);
+ lnet_ni_recv(lp->lp_ni, msg->msg_private, msg, 1,
+ 0, msg->msg_len, msg->msg_len);
+ lnet_net_lock(cpt);
+ }
+ return 0;
+}
+
+void
+lnet_return_tx_credits_locked(lnet_msg_t *msg)
+{
+ lnet_peer_t *txpeer = msg->msg_txpeer;
+ lnet_msg_t *msg2;
+
+ if (msg->msg_txcredit) {
+ struct lnet_ni *ni = txpeer->lp_ni;
+ struct lnet_tx_queue *tq = ni->ni_tx_queues[msg->msg_tx_cpt];
+
+ /* give back NI txcredits */
+ msg->msg_txcredit = 0;
+
+ LASSERT((tq->tq_credits < 0) ==
+ !list_empty(&tq->tq_delayed));
+
+ tq->tq_credits++;
+ if (tq->tq_credits <= 0) {
+ msg2 = list_entry(tq->tq_delayed.next,
+ lnet_msg_t, msg_list);
+ list_del(&msg2->msg_list);
+
+ LASSERT(msg2->msg_txpeer->lp_ni == ni);
+ LASSERT(msg2->msg_tx_delayed);
+
+ (void) lnet_post_send_locked(msg2, 1);
+ }
+ }
+
+ if (msg->msg_peertxcredit) {
+ /* give back peer txcredits */
+ msg->msg_peertxcredit = 0;
+
+ LASSERT((txpeer->lp_txcredits < 0) ==
+ !list_empty(&txpeer->lp_txq));
+
+ txpeer->lp_txqnob -= msg->msg_len + sizeof(lnet_hdr_t);
+ LASSERT(txpeer->lp_txqnob >= 0);
+
+ txpeer->lp_txcredits++;
+ if (txpeer->lp_txcredits <= 0) {
+ msg2 = list_entry(txpeer->lp_txq.next,
+ lnet_msg_t, msg_list);
+ list_del(&msg2->msg_list);
+
+ LASSERT(msg2->msg_txpeer == txpeer);
+ LASSERT(msg2->msg_tx_delayed);
+
+ (void) lnet_post_send_locked(msg2, 1);
+ }
+ }
+
+ if (txpeer != NULL) {
+ msg->msg_txpeer = NULL;
+ lnet_peer_decref_locked(txpeer);
+ }
+}
+
+void
+lnet_return_rx_credits_locked(lnet_msg_t *msg)
+{
+ lnet_peer_t *rxpeer = msg->msg_rxpeer;
+ lnet_msg_t *msg2;
+
+ if (msg->msg_rtrcredit) {
+ /* give back global router credits */
+ lnet_rtrbuf_t *rb;
+ lnet_rtrbufpool_t *rbp;
+
+ /* NB If a msg ever blocks for a buffer in rbp_msgs, it stays
+ * there until it gets one allocated, or aborts the wait
+ * itself */
+ LASSERT(msg->msg_kiov != NULL);
+
+ rb = list_entry(msg->msg_kiov, lnet_rtrbuf_t, rb_kiov[0]);
+ rbp = rb->rb_pool;
+ LASSERT(rbp == lnet_msg2bufpool(msg));
+
+ msg->msg_kiov = NULL;
+ msg->msg_rtrcredit = 0;
+
+ LASSERT((rbp->rbp_credits < 0) ==
+ !list_empty(&rbp->rbp_msgs));
+ LASSERT((rbp->rbp_credits > 0) ==
+ !list_empty(&rbp->rbp_bufs));
+
+ list_add(&rb->rb_list, &rbp->rbp_bufs);
+ rbp->rbp_credits++;
+ if (rbp->rbp_credits <= 0) {
+ msg2 = list_entry(rbp->rbp_msgs.next,
+ lnet_msg_t, msg_list);
+ list_del(&msg2->msg_list);
+
+ (void) lnet_post_routed_recv_locked(msg2, 1);
+ }
+ }
+
+ if (msg->msg_peerrtrcredit) {
+ /* give back peer router credits */
+ msg->msg_peerrtrcredit = 0;
+
+ LASSERT((rxpeer->lp_rtrcredits < 0) ==
+ !list_empty(&rxpeer->lp_rtrq));
+
+ rxpeer->lp_rtrcredits++;
+ if (rxpeer->lp_rtrcredits <= 0) {
+ msg2 = list_entry(rxpeer->lp_rtrq.next,
+ lnet_msg_t, msg_list);
+ list_del(&msg2->msg_list);
+
+ (void) lnet_post_routed_recv_locked(msg2, 1);
+ }
+ }
+ if (rxpeer != NULL) {
+ msg->msg_rxpeer = NULL;
+ lnet_peer_decref_locked(rxpeer);
+ }
+}
+
+static int
+lnet_compare_routes(lnet_route_t *r1, lnet_route_t *r2)
+{
+ lnet_peer_t *p1 = r1->lr_gateway;
+ lnet_peer_t *p2 = r2->lr_gateway;
+
+ if (r1->lr_priority < r2->lr_priority)
+ return 1;
+
+ if (r1->lr_priority > r2->lr_priority)
+ return -1;
+
+ if (r1->lr_hops < r2->lr_hops)
+ return 1;
+
+ if (r1->lr_hops > r2->lr_hops)
+ return -1;
+
+ if (p1->lp_txqnob < p2->lp_txqnob)
+ return 1;
+
+ if (p1->lp_txqnob > p2->lp_txqnob)
+ return -1;
+
+ if (p1->lp_txcredits > p2->lp_txcredits)
+ return 1;
+
+ if (p1->lp_txcredits < p2->lp_txcredits)
+ return -1;
+
+ if (r1->lr_seq - r2->lr_seq <= 0)
+ return 1;
+
+ return -1;
+}
+
+static lnet_peer_t *
+lnet_find_route_locked(lnet_ni_t *ni, lnet_nid_t target, lnet_nid_t rtr_nid)
+{
+ lnet_remotenet_t *rnet;
+ lnet_route_t *rtr;
+ lnet_route_t *rtr_best;
+ lnet_route_t *rtr_last;
+ struct lnet_peer *lp_best;
+ struct lnet_peer *lp;
+ int rc;
+
+ /* If @rtr_nid is not LNET_NID_ANY, return the gateway with
+ * rtr_nid nid, otherwise find the best gateway I can use */
+
+ rnet = lnet_find_net_locked(LNET_NIDNET(target));
+ if (rnet == NULL)
+ return NULL;
+
+ lp_best = NULL;
+ rtr_best = rtr_last = NULL;
+ list_for_each_entry(rtr, &rnet->lrn_routes, lr_list) {
+ lp = rtr->lr_gateway;
+
+ if (!lp->lp_alive || /* gateway is down */
+ ((lp->lp_ping_feats & LNET_PING_FEAT_NI_STATUS) != 0 &&
+ rtr->lr_downis != 0)) /* NI to target is down */
+ continue;
+
+ if (ni != NULL && lp->lp_ni != ni)
+ continue;
+
+ if (lp->lp_nid == rtr_nid) /* it's pre-determined router */
+ return lp;
+
+ if (lp_best == NULL) {
+ rtr_best = rtr_last = rtr;
+ lp_best = lp;
+ continue;
+ }
+
+ /* no protection on below fields, but it's harmless */
+ if (rtr_last->lr_seq - rtr->lr_seq < 0)
+ rtr_last = rtr;
+
+ rc = lnet_compare_routes(rtr, rtr_best);
+ if (rc < 0)
+ continue;
+
+ rtr_best = rtr;
+ lp_best = lp;
+ }
+
+ /* set sequence number on the best router to the latest sequence + 1
+ * so we can round-robin all routers, it's race and inaccurate but
+ * harmless and functional */
+ if (rtr_best != NULL)
+ rtr_best->lr_seq = rtr_last->lr_seq + 1;
+ return lp_best;
+}
+
+int
+lnet_send(lnet_nid_t src_nid, lnet_msg_t *msg, lnet_nid_t rtr_nid)
+{
+ lnet_nid_t dst_nid = msg->msg_target.nid;
+ struct lnet_ni *src_ni;
+ struct lnet_ni *local_ni;
+ struct lnet_peer *lp;
+ int cpt;
+ int cpt2;
+ int rc;
+
+ /* NB: rtr_nid is set to LNET_NID_ANY for all current use-cases,
+ * but we might want to use pre-determined router for ACK/REPLY
+ * in the future */
+ /* NB: ni != NULL == interface pre-determined (ACK/REPLY) */
+ LASSERT(msg->msg_txpeer == NULL);
+ LASSERT(!msg->msg_sending);
+ LASSERT(!msg->msg_target_is_router);
+ LASSERT(!msg->msg_receiving);
+
+ msg->msg_sending = 1;
+
+ LASSERT(!msg->msg_tx_committed);
+ cpt = lnet_cpt_of_nid(rtr_nid == LNET_NID_ANY ? dst_nid : rtr_nid);
+ again:
+ lnet_net_lock(cpt);
+
+ if (the_lnet.ln_shutdown) {
+ lnet_net_unlock(cpt);
+ return -ESHUTDOWN;
+ }
+
+ if (src_nid == LNET_NID_ANY) {
+ src_ni = NULL;
+ } else {
+ src_ni = lnet_nid2ni_locked(src_nid, cpt);
+ if (src_ni == NULL) {
+ lnet_net_unlock(cpt);
+ LCONSOLE_WARN("Can't send to %s: src %s is not a local nid\n",
+ libcfs_nid2str(dst_nid),
+ libcfs_nid2str(src_nid));
+ return -EINVAL;
+ }
+ LASSERT(!msg->msg_routing);
+ }
+
+ /* Is this for someone on a local network? */
+ local_ni = lnet_net2ni_locked(LNET_NIDNET(dst_nid), cpt);
+
+ if (local_ni != NULL) {
+ if (src_ni == NULL) {
+ src_ni = local_ni;
+ src_nid = src_ni->ni_nid;
+ } else if (src_ni == local_ni) {
+ lnet_ni_decref_locked(local_ni, cpt);
+ } else {
+ lnet_ni_decref_locked(local_ni, cpt);
+ lnet_ni_decref_locked(src_ni, cpt);
+ lnet_net_unlock(cpt);
+ LCONSOLE_WARN("No route to %s via from %s\n",
+ libcfs_nid2str(dst_nid),
+ libcfs_nid2str(src_nid));
+ return -EINVAL;
+ }
+
+ LASSERT(src_nid != LNET_NID_ANY);
+ lnet_msg_commit(msg, cpt);
+
+ if (!msg->msg_routing)
+ msg->msg_hdr.src_nid = cpu_to_le64(src_nid);
+
+ if (src_ni == the_lnet.ln_loni) {
+ /* No send credit hassles with LOLND */
+ lnet_net_unlock(cpt);
+ lnet_ni_send(src_ni, msg);
+
+ lnet_net_lock(cpt);
+ lnet_ni_decref_locked(src_ni, cpt);
+ lnet_net_unlock(cpt);
+ return 0;
+ }
+
+ rc = lnet_nid2peer_locked(&lp, dst_nid, cpt);
+ /* lp has ref on src_ni; lose mine */
+ lnet_ni_decref_locked(src_ni, cpt);
+ if (rc != 0) {
+ lnet_net_unlock(cpt);
+ LCONSOLE_WARN("Error %d finding peer %s\n", rc,
+ libcfs_nid2str(dst_nid));
+ /* ENOMEM or shutting down */
+ return rc;
+ }
+ LASSERT(lp->lp_ni == src_ni);
+ } else {
+ /* sending to a remote network */
+ lp = lnet_find_route_locked(src_ni, dst_nid, rtr_nid);
+ if (lp == NULL) {
+ if (src_ni != NULL)
+ lnet_ni_decref_locked(src_ni, cpt);
+ lnet_net_unlock(cpt);
+
+ LCONSOLE_WARN("No route to %s via %s (all routers down)\n",
+ libcfs_id2str(msg->msg_target),
+ libcfs_nid2str(src_nid));
+ return -EHOSTUNREACH;
+ }
+
+ /* rtr_nid is LNET_NID_ANY or NID of pre-determined router,
+ * it's possible that rtr_nid isn't LNET_NID_ANY and lp isn't
+ * pre-determined router, this can happen if router table
+ * was changed when we release the lock */
+ if (rtr_nid != lp->lp_nid) {
+ cpt2 = lnet_cpt_of_nid_locked(lp->lp_nid);
+ if (cpt2 != cpt) {
+ if (src_ni != NULL)
+ lnet_ni_decref_locked(src_ni, cpt);
+ lnet_net_unlock(cpt);
+
+ rtr_nid = lp->lp_nid;
+ cpt = cpt2;
+ goto again;
+ }
+ }
+
+ CDEBUG(D_NET, "Best route to %s via %s for %s %d\n",
+ libcfs_nid2str(dst_nid), libcfs_nid2str(lp->lp_nid),
+ lnet_msgtyp2str(msg->msg_type), msg->msg_len);
+
+ if (src_ni == NULL) {
+ src_ni = lp->lp_ni;
+ src_nid = src_ni->ni_nid;
+ } else {
+ LASSERT(src_ni == lp->lp_ni);
+ lnet_ni_decref_locked(src_ni, cpt);
+ }
+
+ lnet_peer_addref_locked(lp);
+
+ LASSERT(src_nid != LNET_NID_ANY);
+ lnet_msg_commit(msg, cpt);
+
+ if (!msg->msg_routing) {
+ /* I'm the source and now I know which NI to send on */
+ msg->msg_hdr.src_nid = cpu_to_le64(src_nid);
+ }
+
+ msg->msg_target_is_router = 1;
+ msg->msg_target.nid = lp->lp_nid;
+ msg->msg_target.pid = LUSTRE_SRV_LNET_PID;
+ }
+
+ /* 'lp' is our best choice of peer */
+
+ LASSERT(!msg->msg_peertxcredit);
+ LASSERT(!msg->msg_txcredit);
+ LASSERT(msg->msg_txpeer == NULL);
+
+ msg->msg_txpeer = lp; /* msg takes my ref on lp */
+
+ rc = lnet_post_send_locked(msg, 0);
+ lnet_net_unlock(cpt);
+
+ if (rc == EHOSTUNREACH || rc == ECANCELED)
+ return -rc;
+
+ if (rc == 0)
+ lnet_ni_send(src_ni, msg);
+
+ return 0; /* rc == 0 or EAGAIN */
+}
+
+static void
+lnet_drop_message(lnet_ni_t *ni, int cpt, void *private, unsigned int nob)
+{
+ lnet_net_lock(cpt);
+ the_lnet.ln_counters[cpt]->drop_count++;
+ the_lnet.ln_counters[cpt]->drop_length += nob;
+ lnet_net_unlock(cpt);
+
+ lnet_ni_recv(ni, private, NULL, 0, 0, 0, nob);
+}
+
+static void
+lnet_recv_put(lnet_ni_t *ni, lnet_msg_t *msg)
+{
+ lnet_hdr_t *hdr = &msg->msg_hdr;
+
+ if (msg->msg_wanted != 0)
+ lnet_setpayloadbuffer(msg);
+
+ lnet_build_msg_event(msg, LNET_EVENT_PUT);
+
+ /* Must I ACK? If so I'll grab the ack_wmd out of the header and put
+ * it back into the ACK during lnet_finalize() */
+ msg->msg_ack = (!lnet_is_wire_handle_none(&hdr->msg.put.ack_wmd) &&
+ (msg->msg_md->md_options & LNET_MD_ACK_DISABLE) == 0);
+
+ lnet_ni_recv(ni, msg->msg_private, msg, msg->msg_rx_delayed,
+ msg->msg_offset, msg->msg_wanted, hdr->payload_length);
+}
+
+static int
+lnet_parse_put(lnet_ni_t *ni, lnet_msg_t *msg)
+{
+ lnet_hdr_t *hdr = &msg->msg_hdr;
+ struct lnet_match_info info;
+ int rc;
+
+ /* Convert put fields to host byte order */
+ hdr->msg.put.match_bits = le64_to_cpu(hdr->msg.put.match_bits);
+ hdr->msg.put.ptl_index = le32_to_cpu(hdr->msg.put.ptl_index);
+ hdr->msg.put.offset = le32_to_cpu(hdr->msg.put.offset);
+
+ info.mi_id.nid = hdr->src_nid;
+ info.mi_id.pid = hdr->src_pid;
+ info.mi_opc = LNET_MD_OP_PUT;
+ info.mi_portal = hdr->msg.put.ptl_index;
+ info.mi_rlength = hdr->payload_length;
+ info.mi_roffset = hdr->msg.put.offset;
+ info.mi_mbits = hdr->msg.put.match_bits;
+
+ msg->msg_rx_ready_delay = ni->ni_lnd->lnd_eager_recv == NULL;
+
+ again:
+ rc = lnet_ptl_match_md(&info, msg);
+ switch (rc) {
+ default:
+ LBUG();
+
+ case LNET_MATCHMD_OK:
+ lnet_recv_put(ni, msg);
+ return 0;
+
+ case LNET_MATCHMD_NONE:
+ if (msg->msg_rx_delayed) /* attached on delayed list */
+ return 0;
+
+ rc = lnet_ni_eager_recv(ni, msg);
+ if (rc == 0)
+ goto again;
+ /* fall through */
+
+ case LNET_MATCHMD_DROP:
+ CNETERR("Dropping PUT from %s portal %d match %llu offset %d length %d: %d\n",
+ libcfs_id2str(info.mi_id), info.mi_portal,
+ info.mi_mbits, info.mi_roffset, info.mi_rlength, rc);
+
+ return ENOENT; /* +ve: OK but no match */
+ }
+}
+
+static int
+lnet_parse_get(lnet_ni_t *ni, lnet_msg_t *msg, int rdma_get)
+{
+ struct lnet_match_info info;
+ lnet_hdr_t *hdr = &msg->msg_hdr;
+ lnet_handle_wire_t reply_wmd;
+ int rc;
+
+ /* Convert get fields to host byte order */
+ hdr->msg.get.match_bits = le64_to_cpu(hdr->msg.get.match_bits);
+ hdr->msg.get.ptl_index = le32_to_cpu(hdr->msg.get.ptl_index);
+ hdr->msg.get.sink_length = le32_to_cpu(hdr->msg.get.sink_length);
+ hdr->msg.get.src_offset = le32_to_cpu(hdr->msg.get.src_offset);
+
+ info.mi_id.nid = hdr->src_nid;
+ info.mi_id.pid = hdr->src_pid;
+ info.mi_opc = LNET_MD_OP_GET;
+ info.mi_portal = hdr->msg.get.ptl_index;
+ info.mi_rlength = hdr->msg.get.sink_length;
+ info.mi_roffset = hdr->msg.get.src_offset;
+ info.mi_mbits = hdr->msg.get.match_bits;
+
+ rc = lnet_ptl_match_md(&info, msg);
+ if (rc == LNET_MATCHMD_DROP) {
+ CNETERR("Dropping GET from %s portal %d match %llu offset %d length %d\n",
+ libcfs_id2str(info.mi_id), info.mi_portal,
+ info.mi_mbits, info.mi_roffset, info.mi_rlength);
+ return ENOENT; /* +ve: OK but no match */
+ }
+
+ LASSERT(rc == LNET_MATCHMD_OK);
+
+ lnet_build_msg_event(msg, LNET_EVENT_GET);
+
+ reply_wmd = hdr->msg.get.return_wmd;
+
+ lnet_prep_send(msg, LNET_MSG_REPLY, info.mi_id,
+ msg->msg_offset, msg->msg_wanted);
+
+ msg->msg_hdr.msg.reply.dst_wmd = reply_wmd;
+
+ if (rdma_get) {
+ /* The LND completes the REPLY from her recv procedure */
+ lnet_ni_recv(ni, msg->msg_private, msg, 0,
+ msg->msg_offset, msg->msg_len, msg->msg_len);
+ return 0;
+ }
+
+ lnet_ni_recv(ni, msg->msg_private, NULL, 0, 0, 0, 0);
+ msg->msg_receiving = 0;
+
+ rc = lnet_send(ni->ni_nid, msg, LNET_NID_ANY);
+ if (rc < 0) {
+ /* didn't get as far as lnet_ni_send() */
+ CERROR("%s: Unable to send REPLY for GET from %s: %d\n",
+ libcfs_nid2str(ni->ni_nid),
+ libcfs_id2str(info.mi_id), rc);
+
+ lnet_finalize(ni, msg, rc);
+ }
+
+ return 0;
+}
+
+static int
+lnet_parse_reply(lnet_ni_t *ni, lnet_msg_t *msg)
+{
+ void *private = msg->msg_private;
+ lnet_hdr_t *hdr = &msg->msg_hdr;
+ lnet_process_id_t src = {0};
+ lnet_libmd_t *md;
+ int rlength;
+ int mlength;
+ int cpt;
+
+ cpt = lnet_cpt_of_cookie(hdr->msg.reply.dst_wmd.wh_object_cookie);
+ lnet_res_lock(cpt);
+
+ src.nid = hdr->src_nid;
+ src.pid = hdr->src_pid;
+
+ /* NB handles only looked up by creator (no flips) */
+ md = lnet_wire_handle2md(&hdr->msg.reply.dst_wmd);
+ if (md == NULL || md->md_threshold == 0 || md->md_me != NULL) {
+ CNETERR("%s: Dropping REPLY from %s for %s MD %#llx.%#llx\n",
+ libcfs_nid2str(ni->ni_nid), libcfs_id2str(src),
+ (md == NULL) ? "invalid" : "inactive",
+ hdr->msg.reply.dst_wmd.wh_interface_cookie,
+ hdr->msg.reply.dst_wmd.wh_object_cookie);
+ if (md != NULL && md->md_me != NULL)
+ CERROR("REPLY MD also attached to portal %d\n",
+ md->md_me->me_portal);
+
+ lnet_res_unlock(cpt);
+ return ENOENT; /* +ve: OK but no match */
+ }
+
+ LASSERT(md->md_offset == 0);
+
+ rlength = hdr->payload_length;
+ mlength = min_t(uint, rlength, md->md_length);
+
+ if (mlength < rlength &&
+ (md->md_options & LNET_MD_TRUNCATE) == 0) {
+ CNETERR("%s: Dropping REPLY from %s length %d for MD %#llx would overflow (%d)\n",
+ libcfs_nid2str(ni->ni_nid), libcfs_id2str(src),
+ rlength, hdr->msg.reply.dst_wmd.wh_object_cookie,
+ mlength);
+ lnet_res_unlock(cpt);
+ return ENOENT; /* +ve: OK but no match */
+ }
+
+ CDEBUG(D_NET, "%s: Reply from %s of length %d/%d into md %#llx\n",
+ libcfs_nid2str(ni->ni_nid), libcfs_id2str(src),
+ mlength, rlength, hdr->msg.reply.dst_wmd.wh_object_cookie);
+
+ lnet_msg_attach_md(msg, md, 0, mlength);
+
+ if (mlength != 0)
+ lnet_setpayloadbuffer(msg);
+
+ lnet_res_unlock(cpt);
+
+ lnet_build_msg_event(msg, LNET_EVENT_REPLY);
+
+ lnet_ni_recv(ni, private, msg, 0, 0, mlength, rlength);
+ return 0;
+}
+
+static int
+lnet_parse_ack(lnet_ni_t *ni, lnet_msg_t *msg)
+{
+ lnet_hdr_t *hdr = &msg->msg_hdr;
+ lnet_process_id_t src = {0};
+ lnet_libmd_t *md;
+ int cpt;
+
+ src.nid = hdr->src_nid;
+ src.pid = hdr->src_pid;
+
+ /* Convert ack fields to host byte order */
+ hdr->msg.ack.match_bits = le64_to_cpu(hdr->msg.ack.match_bits);
+ hdr->msg.ack.mlength = le32_to_cpu(hdr->msg.ack.mlength);
+
+ cpt = lnet_cpt_of_cookie(hdr->msg.ack.dst_wmd.wh_object_cookie);
+ lnet_res_lock(cpt);
+
+ /* NB handles only looked up by creator (no flips) */
+ md = lnet_wire_handle2md(&hdr->msg.ack.dst_wmd);
+ if (md == NULL || md->md_threshold == 0 || md->md_me != NULL) {
+ /* Don't moan; this is expected */
+ CDEBUG(D_NET,
+ "%s: Dropping ACK from %s to %s MD %#llx.%#llx\n",
+ libcfs_nid2str(ni->ni_nid), libcfs_id2str(src),
+ (md == NULL) ? "invalid" : "inactive",
+ hdr->msg.ack.dst_wmd.wh_interface_cookie,
+ hdr->msg.ack.dst_wmd.wh_object_cookie);
+ if (md != NULL && md->md_me != NULL)
+ CERROR("Source MD also attached to portal %d\n",
+ md->md_me->me_portal);
+
+ lnet_res_unlock(cpt);
+ return ENOENT; /* +ve! */
+ }
+
+ CDEBUG(D_NET, "%s: ACK from %s into md %#llx\n",
+ libcfs_nid2str(ni->ni_nid), libcfs_id2str(src),
+ hdr->msg.ack.dst_wmd.wh_object_cookie);
+
+ lnet_msg_attach_md(msg, md, 0, 0);
+
+ lnet_res_unlock(cpt);
+
+ lnet_build_msg_event(msg, LNET_EVENT_ACK);
+
+ lnet_ni_recv(ni, msg->msg_private, msg, 0, 0, 0, msg->msg_len);
+ return 0;
+}
+
+static int
+lnet_parse_forward_locked(lnet_ni_t *ni, lnet_msg_t *msg)
+{
+ int rc = 0;
+
+ if (msg->msg_rxpeer->lp_rtrcredits <= 0 ||
+ lnet_msg2bufpool(msg)->rbp_credits <= 0) {
+ if (ni->ni_lnd->lnd_eager_recv == NULL) {
+ msg->msg_rx_ready_delay = 1;
+ } else {
+ lnet_net_unlock(msg->msg_rx_cpt);
+ rc = lnet_ni_eager_recv(ni, msg);
+ lnet_net_lock(msg->msg_rx_cpt);
+ }
+ }
+
+ if (rc == 0)
+ rc = lnet_post_routed_recv_locked(msg, 0);
+ return rc;
+}
+
+char *
+lnet_msgtyp2str(int type)
+{
+ switch (type) {
+ case LNET_MSG_ACK:
+ return "ACK";
+ case LNET_MSG_PUT:
+ return "PUT";
+ case LNET_MSG_GET:
+ return "GET";
+ case LNET_MSG_REPLY:
+ return "REPLY";
+ case LNET_MSG_HELLO:
+ return "HELLO";
+ default:
+ return "<UNKNOWN>";
+ }
+}
+EXPORT_SYMBOL(lnet_msgtyp2str);
+
+void
+lnet_print_hdr(lnet_hdr_t *hdr)
+{
+ lnet_process_id_t src = {0};
+ lnet_process_id_t dst = {0};
+ char *type_str = lnet_msgtyp2str(hdr->type);
+
+ src.nid = hdr->src_nid;
+ src.pid = hdr->src_pid;
+
+ dst.nid = hdr->dest_nid;
+ dst.pid = hdr->dest_pid;
+
+ CWARN("P3 Header at %p of type %s\n", hdr, type_str);
+ CWARN(" From %s\n", libcfs_id2str(src));
+ CWARN(" To %s\n", libcfs_id2str(dst));
+
+ switch (hdr->type) {
+ default:
+ break;
+
+ case LNET_MSG_PUT:
+ CWARN(" Ptl index %d, ack md %#llx.%#llx, match bits %llu\n",
+ hdr->msg.put.ptl_index,
+ hdr->msg.put.ack_wmd.wh_interface_cookie,
+ hdr->msg.put.ack_wmd.wh_object_cookie,
+ hdr->msg.put.match_bits);
+ CWARN(" Length %d, offset %d, hdr data %#llx\n",
+ hdr->payload_length, hdr->msg.put.offset,
+ hdr->msg.put.hdr_data);
+ break;
+
+ case LNET_MSG_GET:
+ CWARN(" Ptl index %d, return md %#llx.%#llx, match bits %llu\n",
+ hdr->msg.get.ptl_index,
+ hdr->msg.get.return_wmd.wh_interface_cookie,
+ hdr->msg.get.return_wmd.wh_object_cookie,
+ hdr->msg.get.match_bits);
+ CWARN(" Length %d, src offset %d\n",
+ hdr->msg.get.sink_length,
+ hdr->msg.get.src_offset);
+ break;
+
+ case LNET_MSG_ACK:
+ CWARN(" dst md %#llx.%#llx, manipulated length %d\n",
+ hdr->msg.ack.dst_wmd.wh_interface_cookie,
+ hdr->msg.ack.dst_wmd.wh_object_cookie,
+ hdr->msg.ack.mlength);
+ break;
+
+ case LNET_MSG_REPLY:
+ CWARN(" dst md %#llx.%#llx, length %d\n",
+ hdr->msg.reply.dst_wmd.wh_interface_cookie,
+ hdr->msg.reply.dst_wmd.wh_object_cookie,
+ hdr->payload_length);
+ }
+
+}
+
+int
+lnet_parse(lnet_ni_t *ni, lnet_hdr_t *hdr, lnet_nid_t from_nid,
+ void *private, int rdma_req)
+{
+ int rc = 0;
+ int cpt;
+ int for_me;
+ struct lnet_msg *msg;
+ lnet_pid_t dest_pid;
+ lnet_nid_t dest_nid;
+ lnet_nid_t src_nid;
+ __u32 payload_length;
+ __u32 type;
+
+ LASSERT(!in_interrupt());
+
+ type = le32_to_cpu(hdr->type);
+ src_nid = le64_to_cpu(hdr->src_nid);
+ dest_nid = le64_to_cpu(hdr->dest_nid);
+ dest_pid = le32_to_cpu(hdr->dest_pid);
+ payload_length = le32_to_cpu(hdr->payload_length);
+
+ for_me = (ni->ni_nid == dest_nid);
+ cpt = lnet_cpt_of_nid(from_nid);
+
+ switch (type) {
+ case LNET_MSG_ACK:
+ case LNET_MSG_GET:
+ if (payload_length > 0) {
+ CERROR("%s, src %s: bad %s payload %d (0 expected)\n",
+ libcfs_nid2str(from_nid),
+ libcfs_nid2str(src_nid),
+ lnet_msgtyp2str(type), payload_length);
+ return -EPROTO;
+ }
+ break;
+
+ case LNET_MSG_PUT:
+ case LNET_MSG_REPLY:
+ if (payload_length >
+ (__u32)(for_me ? LNET_MAX_PAYLOAD : LNET_MTU)) {
+ CERROR("%s, src %s: bad %s payload %d (%d max expected)\n",
+ libcfs_nid2str(from_nid),
+ libcfs_nid2str(src_nid),
+ lnet_msgtyp2str(type),
+ payload_length,
+ for_me ? LNET_MAX_PAYLOAD : LNET_MTU);
+ return -EPROTO;
+ }
+ break;
+
+ default:
+ CERROR("%s, src %s: Bad message type 0x%x\n",
+ libcfs_nid2str(from_nid),
+ libcfs_nid2str(src_nid), type);
+ return -EPROTO;
+ }
+
+ if (the_lnet.ln_routing &&
+ ni->ni_last_alive != get_seconds()) {
+ lnet_ni_lock(ni);
+
+ /* NB: so far here is the only place to set NI status to "up */
+ ni->ni_last_alive = get_seconds();
+ if (ni->ni_status != NULL &&
+ ni->ni_status->ns_status == LNET_NI_STATUS_DOWN)
+ ni->ni_status->ns_status = LNET_NI_STATUS_UP;
+ lnet_ni_unlock(ni);
+ }
+
+ /* Regard a bad destination NID as a protocol error. Senders should
+ * know what they're doing; if they don't they're misconfigured, buggy
+ * or malicious so we chop them off at the knees :) */
+
+ if (!for_me) {
+ if (LNET_NIDNET(dest_nid) == LNET_NIDNET(ni->ni_nid)) {
+ /* should have gone direct */
+ CERROR("%s, src %s: Bad dest nid %s (should have been sent direct)\n",
+ libcfs_nid2str(from_nid),
+ libcfs_nid2str(src_nid),
+ libcfs_nid2str(dest_nid));
+ return -EPROTO;
+ }
+
+ if (lnet_islocalnid(dest_nid)) {
+ /* dest is another local NI; sender should have used
+ * this node's NID on its own network */
+ CERROR("%s, src %s: Bad dest nid %s (it's my nid but on a different network)\n",
+ libcfs_nid2str(from_nid),
+ libcfs_nid2str(src_nid),
+ libcfs_nid2str(dest_nid));
+ return -EPROTO;
+ }
+
+ if (rdma_req && type == LNET_MSG_GET) {
+ CERROR("%s, src %s: Bad optimized GET for %s (final destination must be me)\n",
+ libcfs_nid2str(from_nid),
+ libcfs_nid2str(src_nid),
+ libcfs_nid2str(dest_nid));
+ return -EPROTO;
+ }
+
+ if (!the_lnet.ln_routing) {
+ CERROR("%s, src %s: Dropping message for %s (routing not enabled)\n",
+ libcfs_nid2str(from_nid),
+ libcfs_nid2str(src_nid),
+ libcfs_nid2str(dest_nid));
+ goto drop;
+ }
+ }
+
+ /* Message looks OK; we're not going to return an error, so we MUST
+ * call back lnd_recv() come what may... */
+
+ if (!list_empty(&the_lnet.ln_test_peers) && /* normally we don't */
+ fail_peer(src_nid, 0)) { /* shall we now? */
+ CERROR("%s, src %s: Dropping %s to simulate failure\n",
+ libcfs_nid2str(from_nid), libcfs_nid2str(src_nid),
+ lnet_msgtyp2str(type));
+ goto drop;
+ }
+
+ msg = lnet_msg_alloc();
+ if (msg == NULL) {
+ CERROR("%s, src %s: Dropping %s (out of memory)\n",
+ libcfs_nid2str(from_nid), libcfs_nid2str(src_nid),
+ lnet_msgtyp2str(type));
+ goto drop;
+ }
+
+ /* msg zeroed in lnet_msg_alloc;
+ * i.e. flags all clear, pointers NULL etc
+ */
+
+ msg->msg_type = type;
+ msg->msg_private = private;
+ msg->msg_receiving = 1;
+ msg->msg_len = msg->msg_wanted = payload_length;
+ msg->msg_offset = 0;
+ msg->msg_hdr = *hdr;
+ /* for building message event */
+ msg->msg_from = from_nid;
+ if (!for_me) {
+ msg->msg_target.pid = dest_pid;
+ msg->msg_target.nid = dest_nid;
+ msg->msg_routing = 1;
+
+ } else {
+ /* convert common msg->hdr fields to host byteorder */
+ msg->msg_hdr.type = type;
+ msg->msg_hdr.src_nid = src_nid;
+ msg->msg_hdr.src_pid = le32_to_cpu(msg->msg_hdr.src_pid);
+ msg->msg_hdr.dest_nid = dest_nid;
+ msg->msg_hdr.dest_pid = dest_pid;
+ msg->msg_hdr.payload_length = payload_length;
+ }
+
+ lnet_net_lock(cpt);
+ rc = lnet_nid2peer_locked(&msg->msg_rxpeer, from_nid, cpt);
+ if (rc != 0) {
+ lnet_net_unlock(cpt);
+ CERROR("%s, src %s: Dropping %s (error %d looking up sender)\n",
+ libcfs_nid2str(from_nid), libcfs_nid2str(src_nid),
+ lnet_msgtyp2str(type), rc);
+ lnet_msg_free(msg);
+ goto drop;
+ }
+
+ if (lnet_isrouter(msg->msg_rxpeer)) {
+ lnet_peer_set_alive(msg->msg_rxpeer);
+ if (avoid_asym_router_failure &&
+ LNET_NIDNET(src_nid) != LNET_NIDNET(from_nid)) {
+ /* received a remote message from router, update
+ * remote NI status on this router.
+ * NB: multi-hop routed message will be ignored.
+ */
+ lnet_router_ni_update_locked(msg->msg_rxpeer,
+ LNET_NIDNET(src_nid));
+ }
+ }
+
+ lnet_msg_commit(msg, cpt);
+
+ if (!for_me) {
+ rc = lnet_parse_forward_locked(ni, msg);
+ lnet_net_unlock(cpt);
+
+ if (rc < 0)
+ goto free_drop;
+ if (rc == 0) {
+ lnet_ni_recv(ni, msg->msg_private, msg, 0,
+ 0, payload_length, payload_length);
+ }
+ return 0;
+ }
+
+ lnet_net_unlock(cpt);
+
+ switch (type) {
+ case LNET_MSG_ACK:
+ rc = lnet_parse_ack(ni, msg);
+ break;
+ case LNET_MSG_PUT:
+ rc = lnet_parse_put(ni, msg);
+ break;
+ case LNET_MSG_GET:
+ rc = lnet_parse_get(ni, msg, rdma_req);
+ break;
+ case LNET_MSG_REPLY:
+ rc = lnet_parse_reply(ni, msg);
+ break;
+ default:
+ LASSERT(0);
+ rc = -EPROTO;
+ goto free_drop; /* prevent an unused label if !kernel */
+ }
+
+ if (rc == 0)
+ return 0;
+
+ LASSERT(rc == ENOENT);
+
+ free_drop:
+ LASSERT(msg->msg_md == NULL);
+ lnet_finalize(ni, msg, rc);
+
+ drop:
+ lnet_drop_message(ni, cpt, private, payload_length);
+ return 0;
+}
+EXPORT_SYMBOL(lnet_parse);
+
+void
+lnet_drop_delayed_msg_list(struct list_head *head, char *reason)
+{
+ while (!list_empty(head)) {
+ lnet_process_id_t id = {0};
+ lnet_msg_t *msg;
+
+ msg = list_entry(head->next, lnet_msg_t, msg_list);
+ list_del(&msg->msg_list);
+
+ id.nid = msg->msg_hdr.src_nid;
+ id.pid = msg->msg_hdr.src_pid;
+
+ LASSERT(msg->msg_md == NULL);
+ LASSERT(msg->msg_rx_delayed);
+ LASSERT(msg->msg_rxpeer != NULL);
+ LASSERT(msg->msg_hdr.type == LNET_MSG_PUT);
+
+ CWARN("Dropping delayed PUT from %s portal %d match %llu offset %d length %d: %s\n",
+ libcfs_id2str(id),
+ msg->msg_hdr.msg.put.ptl_index,
+ msg->msg_hdr.msg.put.match_bits,
+ msg->msg_hdr.msg.put.offset,
+ msg->msg_hdr.payload_length, reason);
+
+ /* NB I can't drop msg's ref on msg_rxpeer until after I've
+ * called lnet_drop_message(), so I just hang onto msg as well
+ * until that's done */
+
+ lnet_drop_message(msg->msg_rxpeer->lp_ni,
+ msg->msg_rxpeer->lp_cpt,
+ msg->msg_private, msg->msg_len);
+ /*
+ * NB: message will not generate event because w/o attached MD,
+ * but we still should give error code so lnet_msg_decommit()
+ * can skip counters operations and other checks.
+ */
+ lnet_finalize(msg->msg_rxpeer->lp_ni, msg, -ENOENT);
+ }
+}
+
+void
+lnet_recv_delayed_msg_list(struct list_head *head)
+{
+ while (!list_empty(head)) {
+ lnet_msg_t *msg;
+ lnet_process_id_t id;
+
+ msg = list_entry(head->next, lnet_msg_t, msg_list);
+ list_del(&msg->msg_list);
+
+ /* md won't disappear under me, since each msg
+ * holds a ref on it */
+
+ id.nid = msg->msg_hdr.src_nid;
+ id.pid = msg->msg_hdr.src_pid;
+
+ LASSERT(msg->msg_rx_delayed);
+ LASSERT(msg->msg_md != NULL);
+ LASSERT(msg->msg_rxpeer != NULL);
+ LASSERT(msg->msg_hdr.type == LNET_MSG_PUT);
+
+ CDEBUG(D_NET, "Resuming delayed PUT from %s portal %d match %llu offset %d length %d.\n",
+ libcfs_id2str(id), msg->msg_hdr.msg.put.ptl_index,
+ msg->msg_hdr.msg.put.match_bits,
+ msg->msg_hdr.msg.put.offset,
+ msg->msg_hdr.payload_length);
+
+ lnet_recv_put(msg->msg_rxpeer->lp_ni, msg);
+ }
+}
+
+/**
+ * Initiate an asynchronous PUT operation.
+ *
+ * There are several events associated with a PUT: completion of the send on
+ * the initiator node (LNET_EVENT_SEND), and when the send completes
+ * successfully, the receipt of an acknowledgment (LNET_EVENT_ACK) indicating
+ * that the operation was accepted by the target. The event LNET_EVENT_PUT is
+ * used at the target node to indicate the completion of incoming data
+ * delivery.
+ *
+ * The local events will be logged in the EQ associated with the MD pointed to
+ * by \a mdh handle. Using a MD without an associated EQ results in these
+ * events being discarded. In this case, the caller must have another
+ * mechanism (e.g., a higher level protocol) for determining when it is safe
+ * to modify the memory region associated with the MD.
+ *
+ * Note that LNet does not guarantee the order of LNET_EVENT_SEND and
+ * LNET_EVENT_ACK, though intuitively ACK should happen after SEND.
+ *
+ * \param self Indicates the NID of a local interface through which to send
+ * the PUT request. Use LNET_NID_ANY to let LNet choose one by itself.
+ * \param mdh A handle for the MD that describes the memory to be sent. The MD
+ * must be "free floating" (See LNetMDBind()).
+ * \param ack Controls whether an acknowledgment is requested.
+ * Acknowledgments are only sent when they are requested by the initiating
+ * process and the target MD enables them.
+ * \param target A process identifier for the target process.
+ * \param portal The index in the \a target's portal table.
+ * \param match_bits The match bits to use for MD selection at the target
+ * process.
+ * \param offset The offset into the target MD (only used when the target
+ * MD has the LNET_MD_MANAGE_REMOTE option set).
+ * \param hdr_data 64 bits of user data that can be included in the message
+ * header. This data is written to an event queue entry at the target if an
+ * EQ is present on the matching MD.
+ *
+ * \retval 0 Success, and only in this case events will be generated
+ * and logged to EQ (if it exists).
+ * \retval -EIO Simulated failure.
+ * \retval -ENOMEM Memory allocation failure.
+ * \retval -ENOENT Invalid MD object.
+ *
+ * \see lnet_event_t::hdr_data and lnet_event_kind_t.
+ */
+int
+LNetPut(lnet_nid_t self, lnet_handle_md_t mdh, lnet_ack_req_t ack,
+ lnet_process_id_t target, unsigned int portal,
+ __u64 match_bits, unsigned int offset,
+ __u64 hdr_data)
+{
+ struct lnet_msg *msg;
+ struct lnet_libmd *md;
+ int cpt;
+ int rc;
+
+ LASSERT(the_lnet.ln_init);
+ LASSERT(the_lnet.ln_refcount > 0);
+
+ if (!list_empty(&the_lnet.ln_test_peers) && /* normally we don't */
+ fail_peer(target.nid, 1)) { /* shall we now? */
+ CERROR("Dropping PUT to %s: simulated failure\n",
+ libcfs_id2str(target));
+ return -EIO;
+ }
+
+ msg = lnet_msg_alloc();
+ if (msg == NULL) {
+ CERROR("Dropping PUT to %s: ENOMEM on lnet_msg_t\n",
+ libcfs_id2str(target));
+ return -ENOMEM;
+ }
+ msg->msg_vmflush = !!memory_pressure_get();
+
+ cpt = lnet_cpt_of_cookie(mdh.cookie);
+ lnet_res_lock(cpt);
+
+ md = lnet_handle2md(&mdh);
+ if (md == NULL || md->md_threshold == 0 || md->md_me != NULL) {
+ CERROR("Dropping PUT (%llu:%d:%s): MD (%d) invalid\n",
+ match_bits, portal, libcfs_id2str(target),
+ md == NULL ? -1 : md->md_threshold);
+ if (md != NULL && md->md_me != NULL)
+ CERROR("Source MD also attached to portal %d\n",
+ md->md_me->me_portal);
+ lnet_res_unlock(cpt);
+
+ lnet_msg_free(msg);
+ return -ENOENT;
+ }
+
+ CDEBUG(D_NET, "LNetPut -> %s\n", libcfs_id2str(target));
+
+ lnet_msg_attach_md(msg, md, 0, 0);
+
+ lnet_prep_send(msg, LNET_MSG_PUT, target, 0, md->md_length);
+
+ msg->msg_hdr.msg.put.match_bits = cpu_to_le64(match_bits);
+ msg->msg_hdr.msg.put.ptl_index = cpu_to_le32(portal);
+ msg->msg_hdr.msg.put.offset = cpu_to_le32(offset);
+ msg->msg_hdr.msg.put.hdr_data = hdr_data;
+
+ /* NB handles only looked up by creator (no flips) */
+ if (ack == LNET_ACK_REQ) {
+ msg->msg_hdr.msg.put.ack_wmd.wh_interface_cookie =
+ the_lnet.ln_interface_cookie;
+ msg->msg_hdr.msg.put.ack_wmd.wh_object_cookie =
+ md->md_lh.lh_cookie;
+ } else {
+ msg->msg_hdr.msg.put.ack_wmd.wh_interface_cookie =
+ LNET_WIRE_HANDLE_COOKIE_NONE;
+ msg->msg_hdr.msg.put.ack_wmd.wh_object_cookie =
+ LNET_WIRE_HANDLE_COOKIE_NONE;
+ }
+
+ lnet_res_unlock(cpt);
+
+ lnet_build_msg_event(msg, LNET_EVENT_SEND);
+
+ rc = lnet_send(self, msg, LNET_NID_ANY);
+ if (rc != 0) {
+ CNETERR("Error sending PUT to %s: %d\n",
+ libcfs_id2str(target), rc);
+ lnet_finalize(NULL, msg, rc);
+ }
+
+ /* completion will be signalled by an event */
+ return 0;
+}
+EXPORT_SYMBOL(LNetPut);
+
+lnet_msg_t *
+lnet_create_reply_msg(lnet_ni_t *ni, lnet_msg_t *getmsg)
+{
+ /* The LND can DMA direct to the GET md (i.e. no REPLY msg). This
+ * returns a msg for the LND to pass to lnet_finalize() when the sink
+ * data has been received.
+ *
+ * CAVEAT EMPTOR: 'getmsg' is the original GET, which is freed when
+ * lnet_finalize() is called on it, so the LND must call this first */
+
+ struct lnet_msg *msg = lnet_msg_alloc();
+ struct lnet_libmd *getmd = getmsg->msg_md;
+ lnet_process_id_t peer_id = getmsg->msg_target;
+ int cpt;
+
+ LASSERT(!getmsg->msg_target_is_router);
+ LASSERT(!getmsg->msg_routing);
+
+ cpt = lnet_cpt_of_cookie(getmd->md_lh.lh_cookie);
+ lnet_res_lock(cpt);
+
+ LASSERT(getmd->md_refcount > 0);
+
+ if (msg == NULL) {
+ CERROR("%s: Dropping REPLY from %s: can't allocate msg\n",
+ libcfs_nid2str(ni->ni_nid), libcfs_id2str(peer_id));
+ goto drop;
+ }
+
+ if (getmd->md_threshold == 0) {
+ CERROR("%s: Dropping REPLY from %s for inactive MD %p\n",
+ libcfs_nid2str(ni->ni_nid), libcfs_id2str(peer_id),
+ getmd);
+ lnet_res_unlock(cpt);
+ goto drop;
+ }
+
+ LASSERT(getmd->md_offset == 0);
+
+ CDEBUG(D_NET, "%s: Reply from %s md %p\n",
+ libcfs_nid2str(ni->ni_nid), libcfs_id2str(peer_id), getmd);
+
+ /* setup information for lnet_build_msg_event */
+ msg->msg_from = peer_id.nid;
+ msg->msg_type = LNET_MSG_GET; /* flag this msg as an "optimized" GET */
+ msg->msg_hdr.src_nid = peer_id.nid;
+ msg->msg_hdr.payload_length = getmd->md_length;
+ msg->msg_receiving = 1; /* required by lnet_msg_attach_md */
+
+ lnet_msg_attach_md(msg, getmd, getmd->md_offset, getmd->md_length);
+ lnet_res_unlock(cpt);
+
+ cpt = lnet_cpt_of_nid(peer_id.nid);
+
+ lnet_net_lock(cpt);
+ lnet_msg_commit(msg, cpt);
+ lnet_net_unlock(cpt);
+
+ lnet_build_msg_event(msg, LNET_EVENT_REPLY);
+
+ return msg;
+
+ drop:
+ cpt = lnet_cpt_of_nid(peer_id.nid);
+
+ lnet_net_lock(cpt);
+ the_lnet.ln_counters[cpt]->drop_count++;
+ the_lnet.ln_counters[cpt]->drop_length += getmd->md_length;
+ lnet_net_unlock(cpt);
+
+ if (msg != NULL)
+ lnet_msg_free(msg);
+
+ return NULL;
+}
+EXPORT_SYMBOL(lnet_create_reply_msg);
+
+void
+lnet_set_reply_msg_len(lnet_ni_t *ni, lnet_msg_t *reply, unsigned int len)
+{
+ /* Set the REPLY length, now the RDMA that elides the REPLY message has
+ * completed and I know it. */
+ LASSERT(reply != NULL);
+ LASSERT(reply->msg_type == LNET_MSG_GET);
+ LASSERT(reply->msg_ev.type == LNET_EVENT_REPLY);
+
+ /* NB I trusted my peer to RDMA. If she tells me she's written beyond
+ * the end of my buffer, I might as well be dead. */
+ LASSERT(len <= reply->msg_ev.mlength);
+
+ reply->msg_ev.mlength = len;
+}
+EXPORT_SYMBOL(lnet_set_reply_msg_len);
+
+/**
+ * Initiate an asynchronous GET operation.
+ *
+ * On the initiator node, an LNET_EVENT_SEND is logged when the GET request
+ * is sent, and an LNET_EVENT_REPLY is logged when the data returned from
+ * the target node in the REPLY has been written to local MD.
+ *
+ * On the target node, an LNET_EVENT_GET is logged when the GET request
+ * arrives and is accepted into a MD.
+ *
+ * \param self,target,portal,match_bits,offset See the discussion in LNetPut().
+ * \param mdh A handle for the MD that describes the memory into which the
+ * requested data will be received. The MD must be "free floating"
+ * (See LNetMDBind()).
+ *
+ * \retval 0 Success, and only in this case events will be generated
+ * and logged to EQ (if it exists) of the MD.
+ * \retval -EIO Simulated failure.
+ * \retval -ENOMEM Memory allocation failure.
+ * \retval -ENOENT Invalid MD object.
+ */
+int
+LNetGet(lnet_nid_t self, lnet_handle_md_t mdh,
+ lnet_process_id_t target, unsigned int portal,
+ __u64 match_bits, unsigned int offset)
+{
+ struct lnet_msg *msg;
+ struct lnet_libmd *md;
+ int cpt;
+ int rc;
+
+ LASSERT(the_lnet.ln_init);
+ LASSERT(the_lnet.ln_refcount > 0);
+
+ if (!list_empty(&the_lnet.ln_test_peers) && /* normally we don't */
+ fail_peer(target.nid, 1)) { /* shall we now? */
+ CERROR("Dropping GET to %s: simulated failure\n",
+ libcfs_id2str(target));
+ return -EIO;
+ }
+
+ msg = lnet_msg_alloc();
+ if (msg == NULL) {
+ CERROR("Dropping GET to %s: ENOMEM on lnet_msg_t\n",
+ libcfs_id2str(target));
+ return -ENOMEM;
+ }
+
+ cpt = lnet_cpt_of_cookie(mdh.cookie);
+ lnet_res_lock(cpt);
+
+ md = lnet_handle2md(&mdh);
+ if (md == NULL || md->md_threshold == 0 || md->md_me != NULL) {
+ CERROR("Dropping GET (%llu:%d:%s): MD (%d) invalid\n",
+ match_bits, portal, libcfs_id2str(target),
+ md == NULL ? -1 : md->md_threshold);
+ if (md != NULL && md->md_me != NULL)
+ CERROR("REPLY MD also attached to portal %d\n",
+ md->md_me->me_portal);
+
+ lnet_res_unlock(cpt);
+
+ lnet_msg_free(msg);
+ return -ENOENT;
+ }
+
+ CDEBUG(D_NET, "LNetGet -> %s\n", libcfs_id2str(target));
+
+ lnet_msg_attach_md(msg, md, 0, 0);
+
+ lnet_prep_send(msg, LNET_MSG_GET, target, 0, 0);
+
+ msg->msg_hdr.msg.get.match_bits = cpu_to_le64(match_bits);
+ msg->msg_hdr.msg.get.ptl_index = cpu_to_le32(portal);
+ msg->msg_hdr.msg.get.src_offset = cpu_to_le32(offset);
+ msg->msg_hdr.msg.get.sink_length = cpu_to_le32(md->md_length);
+
+ /* NB handles only looked up by creator (no flips) */
+ msg->msg_hdr.msg.get.return_wmd.wh_interface_cookie =
+ the_lnet.ln_interface_cookie;
+ msg->msg_hdr.msg.get.return_wmd.wh_object_cookie =
+ md->md_lh.lh_cookie;
+
+ lnet_res_unlock(cpt);
+
+ lnet_build_msg_event(msg, LNET_EVENT_SEND);
+
+ rc = lnet_send(self, msg, LNET_NID_ANY);
+ if (rc < 0) {
+ CNETERR("Error sending GET to %s: %d\n",
+ libcfs_id2str(target), rc);
+ lnet_finalize(NULL, msg, rc);
+ }
+
+ /* completion will be signalled by an event */
+ return 0;
+}
+EXPORT_SYMBOL(LNetGet);
+
+/**
+ * Calculate distance to node at \a dstnid.
+ *
+ * \param dstnid Target NID.
+ * \param srcnidp If not NULL, NID of the local interface to reach \a dstnid
+ * is saved here.
+ * \param orderp If not NULL, order of the route to reach \a dstnid is saved
+ * here.
+ *
+ * \retval 0 If \a dstnid belongs to a local interface, and reserved option
+ * local_nid_dist_zero is set, which is the default.
+ * \retval positives Distance to target NID, i.e. number of hops plus one.
+ * \retval -EHOSTUNREACH If \a dstnid is not reachable.
+ */
+int
+LNetDist(lnet_nid_t dstnid, lnet_nid_t *srcnidp, __u32 *orderp)
+{
+ struct list_head *e;
+ struct lnet_ni *ni;
+ lnet_remotenet_t *rnet;
+ __u32 dstnet = LNET_NIDNET(dstnid);
+ int hops;
+ int cpt;
+ __u32 order = 2;
+ struct list_head *rn_list;
+
+ /* if !local_nid_dist_zero, I don't return a distance of 0 ever
+ * (when lustre sees a distance of 0, it substitutes 0@lo), so I
+ * keep order 0 free for 0@lo and order 1 free for a local NID
+ * match */
+
+ LASSERT(the_lnet.ln_init);
+ LASSERT(the_lnet.ln_refcount > 0);
+
+ cpt = lnet_net_lock_current();
+
+ list_for_each(e, &the_lnet.ln_nis) {
+ ni = list_entry(e, lnet_ni_t, ni_list);
+
+ if (ni->ni_nid == dstnid) {
+ if (srcnidp != NULL)
+ *srcnidp = dstnid;
+ if (orderp != NULL) {
+ if (LNET_NETTYP(LNET_NIDNET(dstnid)) == LOLND)
+ *orderp = 0;
+ else
+ *orderp = 1;
+ }
+ lnet_net_unlock(cpt);
+
+ return local_nid_dist_zero ? 0 : 1;
+ }
+
+ if (LNET_NIDNET(ni->ni_nid) == dstnet) {
+ if (srcnidp != NULL)
+ *srcnidp = ni->ni_nid;
+ if (orderp != NULL)
+ *orderp = order;
+ lnet_net_unlock(cpt);
+ return 1;
+ }
+
+ order++;
+ }
+
+ rn_list = lnet_net2rnethash(dstnet);
+ list_for_each(e, rn_list) {
+ rnet = list_entry(e, lnet_remotenet_t, lrn_list);
+
+ if (rnet->lrn_net == dstnet) {
+ lnet_route_t *route;
+ lnet_route_t *shortest = NULL;
+
+ LASSERT(!list_empty(&rnet->lrn_routes));
+
+ list_for_each_entry(route, &rnet->lrn_routes,
+ lr_list) {
+ if (shortest == NULL ||
+ route->lr_hops < shortest->lr_hops)
+ shortest = route;
+ }
+
+ LASSERT(shortest != NULL);
+ hops = shortest->lr_hops;
+ if (srcnidp != NULL)
+ *srcnidp = shortest->lr_gateway->lp_ni->ni_nid;
+ if (orderp != NULL)
+ *orderp = order;
+ lnet_net_unlock(cpt);
+ return hops + 1;
+ }
+ order++;
+ }
+
+ lnet_net_unlock(cpt);
+ return -EHOSTUNREACH;
+}
+EXPORT_SYMBOL(LNetDist);
+
+/**
+ * Set the number of asynchronous messages expected from a target process.
+ *
+ * This function is only meaningful for userspace callers. It's a no-op when
+ * called from kernel.
+ *
+ * Asynchronous messages are those that can come from a target when the
+ * userspace process is not waiting for IO to complete; e.g., AST callbacks
+ * from Lustre servers. Specifying the expected number of such messages
+ * allows them to be eagerly received when user process is not running in
+ * LNet; otherwise network errors may occur.
+ *
+ * \param id Process ID of the target process.
+ * \param nasync Number of asynchronous messages expected from the target.
+ *
+ * \return 0 on success, and an error code otherwise.
+ */
+int
+LNetSetAsync(lnet_process_id_t id, int nasync)
+{
+ return 0;
+}
+EXPORT_SYMBOL(LNetSetAsync);
diff --git a/drivers/staging/lustre/lnet/lnet/lib-msg.c b/drivers/staging/lustre/lnet/lnet/lib-msg.c
new file mode 100644
index 000000000..a46ccbf66
--- /dev/null
+++ b/drivers/staging/lustre/lnet/lnet/lib-msg.c
@@ -0,0 +1,647 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2012, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ *
+ * lnet/lnet/lib-msg.c
+ *
+ * Message decoding, parsing and finalizing routines
+ */
+
+#define DEBUG_SUBSYSTEM S_LNET
+
+#include "../../include/linux/lnet/lib-lnet.h"
+
+void
+lnet_build_unlink_event(lnet_libmd_t *md, lnet_event_t *ev)
+{
+ memset(ev, 0, sizeof(*ev));
+
+ ev->status = 0;
+ ev->unlinked = 1;
+ ev->type = LNET_EVENT_UNLINK;
+ lnet_md_deconstruct(md, &ev->md);
+ lnet_md2handle(&ev->md_handle, md);
+}
+
+/*
+ * Don't need any lock, must be called after lnet_commit_md
+ */
+void
+lnet_build_msg_event(lnet_msg_t *msg, lnet_event_kind_t ev_type)
+{
+ lnet_hdr_t *hdr = &msg->msg_hdr;
+ lnet_event_t *ev = &msg->msg_ev;
+
+ LASSERT(!msg->msg_routing);
+
+ ev->type = ev_type;
+
+ if (ev_type == LNET_EVENT_SEND) {
+ /* event for active message */
+ ev->target.nid = le64_to_cpu(hdr->dest_nid);
+ ev->target.pid = le32_to_cpu(hdr->dest_pid);
+ ev->initiator.nid = LNET_NID_ANY;
+ ev->initiator.pid = the_lnet.ln_pid;
+ ev->sender = LNET_NID_ANY;
+
+ } else {
+ /* event for passive message */
+ ev->target.pid = hdr->dest_pid;
+ ev->target.nid = hdr->dest_nid;
+ ev->initiator.pid = hdr->src_pid;
+ ev->initiator.nid = hdr->src_nid;
+ ev->rlength = hdr->payload_length;
+ ev->sender = msg->msg_from;
+ ev->mlength = msg->msg_wanted;
+ ev->offset = msg->msg_offset;
+ }
+
+ switch (ev_type) {
+ default:
+ LBUG();
+
+ case LNET_EVENT_PUT: /* passive PUT */
+ ev->pt_index = hdr->msg.put.ptl_index;
+ ev->match_bits = hdr->msg.put.match_bits;
+ ev->hdr_data = hdr->msg.put.hdr_data;
+ return;
+
+ case LNET_EVENT_GET: /* passive GET */
+ ev->pt_index = hdr->msg.get.ptl_index;
+ ev->match_bits = hdr->msg.get.match_bits;
+ ev->hdr_data = 0;
+ return;
+
+ case LNET_EVENT_ACK: /* ACK */
+ ev->match_bits = hdr->msg.ack.match_bits;
+ ev->mlength = hdr->msg.ack.mlength;
+ return;
+
+ case LNET_EVENT_REPLY: /* REPLY */
+ return;
+
+ case LNET_EVENT_SEND: /* active message */
+ if (msg->msg_type == LNET_MSG_PUT) {
+ ev->pt_index = le32_to_cpu(hdr->msg.put.ptl_index);
+ ev->match_bits = le64_to_cpu(hdr->msg.put.match_bits);
+ ev->offset = le32_to_cpu(hdr->msg.put.offset);
+ ev->mlength =
+ ev->rlength = le32_to_cpu(hdr->payload_length);
+ ev->hdr_data = le64_to_cpu(hdr->msg.put.hdr_data);
+
+ } else {
+ LASSERT(msg->msg_type == LNET_MSG_GET);
+ ev->pt_index = le32_to_cpu(hdr->msg.get.ptl_index);
+ ev->match_bits = le64_to_cpu(hdr->msg.get.match_bits);
+ ev->mlength =
+ ev->rlength = le32_to_cpu(hdr->msg.get.sink_length);
+ ev->offset = le32_to_cpu(hdr->msg.get.src_offset);
+ ev->hdr_data = 0;
+ }
+ return;
+ }
+}
+
+void
+lnet_msg_commit(lnet_msg_t *msg, int cpt)
+{
+ struct lnet_msg_container *container = the_lnet.ln_msg_containers[cpt];
+ lnet_counters_t *counters = the_lnet.ln_counters[cpt];
+
+ /* routed message can be committed for both receiving and sending */
+ LASSERT(!msg->msg_tx_committed);
+
+ if (msg->msg_sending) {
+ LASSERT(!msg->msg_receiving);
+
+ msg->msg_tx_cpt = cpt;
+ msg->msg_tx_committed = 1;
+ if (msg->msg_rx_committed) { /* routed message REPLY */
+ LASSERT(msg->msg_onactivelist);
+ return;
+ }
+ } else {
+ LASSERT(!msg->msg_sending);
+ msg->msg_rx_cpt = cpt;
+ msg->msg_rx_committed = 1;
+ }
+
+ LASSERT(!msg->msg_onactivelist);
+ msg->msg_onactivelist = 1;
+ list_add(&msg->msg_activelist, &container->msc_active);
+
+ counters->msgs_alloc++;
+ if (counters->msgs_alloc > counters->msgs_max)
+ counters->msgs_max = counters->msgs_alloc;
+}
+
+static void
+lnet_msg_decommit_tx(lnet_msg_t *msg, int status)
+{
+ lnet_counters_t *counters;
+ lnet_event_t *ev = &msg->msg_ev;
+
+ LASSERT(msg->msg_tx_committed);
+ if (status != 0)
+ goto out;
+
+ counters = the_lnet.ln_counters[msg->msg_tx_cpt];
+ switch (ev->type) {
+ default: /* routed message */
+ LASSERT(msg->msg_routing);
+ LASSERT(msg->msg_rx_committed);
+ LASSERT(ev->type == 0);
+
+ counters->route_length += msg->msg_len;
+ counters->route_count++;
+ goto out;
+
+ case LNET_EVENT_PUT:
+ /* should have been decommitted */
+ LASSERT(!msg->msg_rx_committed);
+ /* overwritten while sending ACK */
+ LASSERT(msg->msg_type == LNET_MSG_ACK);
+ msg->msg_type = LNET_MSG_PUT; /* fix type */
+ break;
+
+ case LNET_EVENT_SEND:
+ LASSERT(!msg->msg_rx_committed);
+ if (msg->msg_type == LNET_MSG_PUT)
+ counters->send_length += msg->msg_len;
+ break;
+
+ case LNET_EVENT_GET:
+ LASSERT(msg->msg_rx_committed);
+ /* overwritten while sending reply, we should never be
+ * here for optimized GET */
+ LASSERT(msg->msg_type == LNET_MSG_REPLY);
+ msg->msg_type = LNET_MSG_GET; /* fix type */
+ break;
+ }
+
+ counters->send_count++;
+ out:
+ lnet_return_tx_credits_locked(msg);
+ msg->msg_tx_committed = 0;
+}
+
+static void
+lnet_msg_decommit_rx(lnet_msg_t *msg, int status)
+{
+ lnet_counters_t *counters;
+ lnet_event_t *ev = &msg->msg_ev;
+
+ LASSERT(!msg->msg_tx_committed); /* decommitted or never committed */
+ LASSERT(msg->msg_rx_committed);
+
+ if (status != 0)
+ goto out;
+
+ counters = the_lnet.ln_counters[msg->msg_rx_cpt];
+ switch (ev->type) {
+ default:
+ LASSERT(ev->type == 0);
+ LASSERT(msg->msg_routing);
+ goto out;
+
+ case LNET_EVENT_ACK:
+ LASSERT(msg->msg_type == LNET_MSG_ACK);
+ break;
+
+ case LNET_EVENT_GET:
+ /* type is "REPLY" if it's an optimized GET on passive side,
+ * because optimized GET will never be committed for sending,
+ * so message type wouldn't be changed back to "GET" by
+ * lnet_msg_decommit_tx(), see details in lnet_parse_get() */
+ LASSERT(msg->msg_type == LNET_MSG_REPLY ||
+ msg->msg_type == LNET_MSG_GET);
+ counters->send_length += msg->msg_wanted;
+ break;
+
+ case LNET_EVENT_PUT:
+ LASSERT(msg->msg_type == LNET_MSG_PUT);
+ break;
+
+ case LNET_EVENT_REPLY:
+ /* type is "GET" if it's an optimized GET on active side,
+ * see details in lnet_create_reply_msg() */
+ LASSERT(msg->msg_type == LNET_MSG_GET ||
+ msg->msg_type == LNET_MSG_REPLY);
+ break;
+ }
+
+ counters->recv_count++;
+ if (ev->type == LNET_EVENT_PUT || ev->type == LNET_EVENT_REPLY)
+ counters->recv_length += msg->msg_wanted;
+
+ out:
+ lnet_return_rx_credits_locked(msg);
+ msg->msg_rx_committed = 0;
+}
+
+void
+lnet_msg_decommit(lnet_msg_t *msg, int cpt, int status)
+{
+ int cpt2 = cpt;
+
+ LASSERT(msg->msg_tx_committed || msg->msg_rx_committed);
+ LASSERT(msg->msg_onactivelist);
+
+ if (msg->msg_tx_committed) { /* always decommit for sending first */
+ LASSERT(cpt == msg->msg_tx_cpt);
+ lnet_msg_decommit_tx(msg, status);
+ }
+
+ if (msg->msg_rx_committed) {
+ /* forwarding msg committed for both receiving and sending */
+ if (cpt != msg->msg_rx_cpt) {
+ lnet_net_unlock(cpt);
+ cpt2 = msg->msg_rx_cpt;
+ lnet_net_lock(cpt2);
+ }
+ lnet_msg_decommit_rx(msg, status);
+ }
+
+ list_del(&msg->msg_activelist);
+ msg->msg_onactivelist = 0;
+
+ the_lnet.ln_counters[cpt2]->msgs_alloc--;
+
+ if (cpt2 != cpt) {
+ lnet_net_unlock(cpt2);
+ lnet_net_lock(cpt);
+ }
+}
+
+void
+lnet_msg_attach_md(lnet_msg_t *msg, lnet_libmd_t *md,
+ unsigned int offset, unsigned int mlen)
+{
+ /* NB: @offset and @len are only useful for receiving */
+ /* Here, we attach the MD on lnet_msg and mark it busy and
+ * decrementing its threshold. Come what may, the lnet_msg "owns"
+ * the MD until a call to lnet_msg_detach_md or lnet_finalize()
+ * signals completion. */
+ LASSERT(!msg->msg_routing);
+
+ msg->msg_md = md;
+ if (msg->msg_receiving) { /* committed for receiving */
+ msg->msg_offset = offset;
+ msg->msg_wanted = mlen;
+ }
+
+ md->md_refcount++;
+ if (md->md_threshold != LNET_MD_THRESH_INF) {
+ LASSERT(md->md_threshold > 0);
+ md->md_threshold--;
+ }
+
+ /* build umd in event */
+ lnet_md2handle(&msg->msg_ev.md_handle, md);
+ lnet_md_deconstruct(md, &msg->msg_ev.md);
+}
+
+void
+lnet_msg_detach_md(lnet_msg_t *msg, int status)
+{
+ lnet_libmd_t *md = msg->msg_md;
+ int unlink;
+
+ /* Now it's safe to drop my caller's ref */
+ md->md_refcount--;
+ LASSERT(md->md_refcount >= 0);
+
+ unlink = lnet_md_unlinkable(md);
+ if (md->md_eq != NULL) {
+ msg->msg_ev.status = status;
+ msg->msg_ev.unlinked = unlink;
+ lnet_eq_enqueue_event(md->md_eq, &msg->msg_ev);
+ }
+
+ if (unlink)
+ lnet_md_unlink(md);
+
+ msg->msg_md = NULL;
+}
+
+static int
+lnet_complete_msg_locked(lnet_msg_t *msg, int cpt)
+{
+ lnet_handle_wire_t ack_wmd;
+ int rc;
+ int status = msg->msg_ev.status;
+
+ LASSERT(msg->msg_onactivelist);
+
+ if (status == 0 && msg->msg_ack) {
+ /* Only send an ACK if the PUT completed successfully */
+
+ lnet_msg_decommit(msg, cpt, 0);
+
+ msg->msg_ack = 0;
+ lnet_net_unlock(cpt);
+
+ LASSERT(msg->msg_ev.type == LNET_EVENT_PUT);
+ LASSERT(!msg->msg_routing);
+
+ ack_wmd = msg->msg_hdr.msg.put.ack_wmd;
+
+ lnet_prep_send(msg, LNET_MSG_ACK, msg->msg_ev.initiator, 0, 0);
+
+ msg->msg_hdr.msg.ack.dst_wmd = ack_wmd;
+ msg->msg_hdr.msg.ack.match_bits = msg->msg_ev.match_bits;
+ msg->msg_hdr.msg.ack.mlength = cpu_to_le32(msg->msg_ev.mlength);
+
+ /* NB: we probably want to use NID of msg::msg_from as 3rd
+ * parameter (router NID) if it's routed message */
+ rc = lnet_send(msg->msg_ev.target.nid, msg, LNET_NID_ANY);
+
+ lnet_net_lock(cpt);
+ /*
+ * NB: message is committed for sending, we should return
+ * on success because LND will finalize this message later.
+ *
+ * Also, there is possibility that message is committed for
+ * sending and also failed before delivering to LND,
+ * i.e: ENOMEM, in that case we can't fall through either
+ * because CPT for sending can be different with CPT for
+ * receiving, so we should return back to lnet_finalize()
+ * to make sure we are locking the correct partition.
+ */
+ return rc;
+
+ } else if (status == 0 && /* OK so far */
+ (msg->msg_routing && !msg->msg_sending)) {
+ /* not forwarded */
+ LASSERT(!msg->msg_receiving); /* called back recv already */
+ lnet_net_unlock(cpt);
+
+ rc = lnet_send(LNET_NID_ANY, msg, LNET_NID_ANY);
+
+ lnet_net_lock(cpt);
+ /*
+ * NB: message is committed for sending, we should return
+ * on success because LND will finalize this message later.
+ *
+ * Also, there is possibility that message is committed for
+ * sending and also failed before delivering to LND,
+ * i.e: ENOMEM, in that case we can't fall through either:
+ * - The rule is message must decommit for sending first if
+ * the it's committed for both sending and receiving
+ * - CPT for sending can be different with CPT for receiving,
+ * so we should return back to lnet_finalize() to make
+ * sure we are locking the correct partition.
+ */
+ return rc;
+ }
+
+ lnet_msg_decommit(msg, cpt, status);
+ lnet_msg_free_locked(msg);
+ return 0;
+}
+
+void
+lnet_finalize(lnet_ni_t *ni, lnet_msg_t *msg, int status)
+{
+ struct lnet_msg_container *container;
+ int my_slot;
+ int cpt;
+ int rc;
+ int i;
+
+ LASSERT(!in_interrupt());
+
+ if (msg == NULL)
+ return;
+#if 0
+ CDEBUG(D_WARNING, "%s msg->%s Flags:%s%s%s%s%s%s%s%s%s%s%s txp %s rxp %s\n",
+ lnet_msgtyp2str(msg->msg_type), libcfs_id2str(msg->msg_target),
+ msg->msg_target_is_router ? "t" : "",
+ msg->msg_routing ? "X" : "",
+ msg->msg_ack ? "A" : "",
+ msg->msg_sending ? "S" : "",
+ msg->msg_receiving ? "R" : "",
+ msg->msg_delayed ? "d" : "",
+ msg->msg_txcredit ? "C" : "",
+ msg->msg_peertxcredit ? "c" : "",
+ msg->msg_rtrcredit ? "F" : "",
+ msg->msg_peerrtrcredit ? "f" : "",
+ msg->msg_onactivelist ? "!" : "",
+ msg->msg_txpeer == NULL ? "<none>" : libcfs_nid2str(msg->msg_txpeer->lp_nid),
+ msg->msg_rxpeer == NULL ? "<none>" : libcfs_nid2str(msg->msg_rxpeer->lp_nid));
+#endif
+ msg->msg_ev.status = status;
+
+ if (msg->msg_md != NULL) {
+ cpt = lnet_cpt_of_cookie(msg->msg_md->md_lh.lh_cookie);
+
+ lnet_res_lock(cpt);
+ lnet_msg_detach_md(msg, status);
+ lnet_res_unlock(cpt);
+ }
+
+ again:
+ rc = 0;
+ if (!msg->msg_tx_committed && !msg->msg_rx_committed) {
+ /* not committed to network yet */
+ LASSERT(!msg->msg_onactivelist);
+ lnet_msg_free(msg);
+ return;
+ }
+
+ /*
+ * NB: routed message can be committed for both receiving and sending,
+ * we should finalize in LIFO order and keep counters correct.
+ * (finalize sending first then finalize receiving)
+ */
+ cpt = msg->msg_tx_committed ? msg->msg_tx_cpt : msg->msg_rx_cpt;
+ lnet_net_lock(cpt);
+
+ container = the_lnet.ln_msg_containers[cpt];
+ list_add_tail(&msg->msg_list, &container->msc_finalizing);
+
+ /* Recursion breaker. Don't complete the message here if I am (or
+ * enough other threads are) already completing messages */
+
+ my_slot = -1;
+ for (i = 0; i < container->msc_nfinalizers; i++) {
+ if (container->msc_finalizers[i] == current)
+ break;
+
+ if (my_slot < 0 && container->msc_finalizers[i] == NULL)
+ my_slot = i;
+ }
+
+ if (i < container->msc_nfinalizers || my_slot < 0) {
+ lnet_net_unlock(cpt);
+ return;
+ }
+
+ container->msc_finalizers[my_slot] = current;
+
+ while (!list_empty(&container->msc_finalizing)) {
+ msg = list_entry(container->msc_finalizing.next,
+ lnet_msg_t, msg_list);
+
+ list_del(&msg->msg_list);
+
+ /* NB drops and regains the lnet lock if it actually does
+ * anything, so my finalizing friends can chomp along too */
+ rc = lnet_complete_msg_locked(msg, cpt);
+ if (rc != 0)
+ break;
+ }
+
+ container->msc_finalizers[my_slot] = NULL;
+ lnet_net_unlock(cpt);
+
+ if (rc != 0)
+ goto again;
+}
+EXPORT_SYMBOL(lnet_finalize);
+
+void
+lnet_msg_container_cleanup(struct lnet_msg_container *container)
+{
+ int count = 0;
+
+ if (container->msc_init == 0)
+ return;
+
+ while (!list_empty(&container->msc_active)) {
+ lnet_msg_t *msg = list_entry(container->msc_active.next,
+ lnet_msg_t, msg_activelist);
+
+ LASSERT(msg->msg_onactivelist);
+ msg->msg_onactivelist = 0;
+ list_del(&msg->msg_activelist);
+ lnet_msg_free(msg);
+ count++;
+ }
+
+ if (count > 0)
+ CERROR("%d active msg on exit\n", count);
+
+ if (container->msc_finalizers != NULL) {
+ LIBCFS_FREE(container->msc_finalizers,
+ container->msc_nfinalizers *
+ sizeof(*container->msc_finalizers));
+ container->msc_finalizers = NULL;
+ }
+#ifdef LNET_USE_LIB_FREELIST
+ lnet_freelist_fini(&container->msc_freelist);
+#endif
+ container->msc_init = 0;
+}
+
+int
+lnet_msg_container_setup(struct lnet_msg_container *container, int cpt)
+{
+ int rc;
+
+ container->msc_init = 1;
+
+ INIT_LIST_HEAD(&container->msc_active);
+ INIT_LIST_HEAD(&container->msc_finalizing);
+
+#ifdef LNET_USE_LIB_FREELIST
+ memset(&container->msc_freelist, 0, sizeof(lnet_freelist_t));
+
+ rc = lnet_freelist_init(&container->msc_freelist,
+ LNET_FL_MAX_MSGS, sizeof(lnet_msg_t));
+ if (rc != 0) {
+ CERROR("Failed to init freelist for message container\n");
+ lnet_msg_container_cleanup(container);
+ return rc;
+ }
+#else
+ rc = 0;
+#endif
+ /* number of CPUs */
+ container->msc_nfinalizers = cfs_cpt_weight(lnet_cpt_table(), cpt);
+
+ LIBCFS_CPT_ALLOC(container->msc_finalizers, lnet_cpt_table(), cpt,
+ container->msc_nfinalizers *
+ sizeof(*container->msc_finalizers));
+
+ if (container->msc_finalizers == NULL) {
+ CERROR("Failed to allocate message finalizers\n");
+ lnet_msg_container_cleanup(container);
+ return -ENOMEM;
+ }
+
+ return rc;
+}
+
+void
+lnet_msg_containers_destroy(void)
+{
+ struct lnet_msg_container *container;
+ int i;
+
+ if (the_lnet.ln_msg_containers == NULL)
+ return;
+
+ cfs_percpt_for_each(container, i, the_lnet.ln_msg_containers)
+ lnet_msg_container_cleanup(container);
+
+ cfs_percpt_free(the_lnet.ln_msg_containers);
+ the_lnet.ln_msg_containers = NULL;
+}
+
+int
+lnet_msg_containers_create(void)
+{
+ struct lnet_msg_container *container;
+ int rc;
+ int i;
+
+ the_lnet.ln_msg_containers = cfs_percpt_alloc(lnet_cpt_table(),
+ sizeof(*container));
+
+ if (the_lnet.ln_msg_containers == NULL) {
+ CERROR("Failed to allocate cpu-partition data for network\n");
+ return -ENOMEM;
+ }
+
+ cfs_percpt_for_each(container, i, the_lnet.ln_msg_containers) {
+ rc = lnet_msg_container_setup(container, i);
+ if (rc != 0) {
+ lnet_msg_containers_destroy();
+ return rc;
+ }
+ }
+
+ return 0;
+}
diff --git a/drivers/staging/lustre/lnet/lnet/lib-ptl.c b/drivers/staging/lustre/lnet/lnet/lib-ptl.c
new file mode 100644
index 000000000..3ba0da919
--- /dev/null
+++ b/drivers/staging/lustre/lnet/lnet/lib-ptl.c
@@ -0,0 +1,935 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 021110-1307, USA
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2012, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ *
+ * lnet/lnet/lib-ptl.c
+ *
+ * portal & match routines
+ *
+ * Author: liang@whamcloud.com
+ */
+
+#define DEBUG_SUBSYSTEM S_LNET
+
+#include "../../include/linux/lnet/lib-lnet.h"
+
+/* NB: add /proc interfaces in upcoming patches */
+int portal_rotor = LNET_PTL_ROTOR_HASH_RT;
+module_param(portal_rotor, int, 0644);
+MODULE_PARM_DESC(portal_rotor, "redirect PUTs to different cpu-partitions");
+
+static int
+lnet_ptl_match_type(unsigned int index, lnet_process_id_t match_id,
+ __u64 mbits, __u64 ignore_bits)
+{
+ struct lnet_portal *ptl = the_lnet.ln_portals[index];
+ int unique;
+
+ unique = ignore_bits == 0 &&
+ match_id.nid != LNET_NID_ANY &&
+ match_id.pid != LNET_PID_ANY;
+
+ LASSERT(!lnet_ptl_is_unique(ptl) || !lnet_ptl_is_wildcard(ptl));
+
+ /* prefer to check w/o any lock */
+ if (likely(lnet_ptl_is_unique(ptl) || lnet_ptl_is_wildcard(ptl)))
+ goto match;
+
+ /* unset, new portal */
+ lnet_ptl_lock(ptl);
+ /* check again with lock */
+ if (unlikely(lnet_ptl_is_unique(ptl) || lnet_ptl_is_wildcard(ptl))) {
+ lnet_ptl_unlock(ptl);
+ goto match;
+ }
+
+ /* still not set */
+ if (unique)
+ lnet_ptl_setopt(ptl, LNET_PTL_MATCH_UNIQUE);
+ else
+ lnet_ptl_setopt(ptl, LNET_PTL_MATCH_WILDCARD);
+
+ lnet_ptl_unlock(ptl);
+
+ return 1;
+
+ match:
+ if ((lnet_ptl_is_unique(ptl) && !unique) ||
+ (lnet_ptl_is_wildcard(ptl) && unique))
+ return 0;
+ return 1;
+}
+
+static void
+lnet_ptl_enable_mt(struct lnet_portal *ptl, int cpt)
+{
+ struct lnet_match_table *mtable = ptl->ptl_mtables[cpt];
+ int i;
+
+ /* with hold of both lnet_res_lock(cpt) and lnet_ptl_lock */
+ LASSERT(lnet_ptl_is_wildcard(ptl));
+
+ mtable->mt_enabled = 1;
+
+ ptl->ptl_mt_maps[ptl->ptl_mt_nmaps] = cpt;
+ for (i = ptl->ptl_mt_nmaps - 1; i >= 0; i--) {
+ LASSERT(ptl->ptl_mt_maps[i] != cpt);
+ if (ptl->ptl_mt_maps[i] < cpt)
+ break;
+
+ /* swap to order */
+ ptl->ptl_mt_maps[i + 1] = ptl->ptl_mt_maps[i];
+ ptl->ptl_mt_maps[i] = cpt;
+ }
+
+ ptl->ptl_mt_nmaps++;
+}
+
+static void
+lnet_ptl_disable_mt(struct lnet_portal *ptl, int cpt)
+{
+ struct lnet_match_table *mtable = ptl->ptl_mtables[cpt];
+ int i;
+
+ /* with hold of both lnet_res_lock(cpt) and lnet_ptl_lock */
+ LASSERT(lnet_ptl_is_wildcard(ptl));
+
+ if (LNET_CPT_NUMBER == 1)
+ return; /* never disable the only match-table */
+
+ mtable->mt_enabled = 0;
+
+ LASSERT(ptl->ptl_mt_nmaps > 0 &&
+ ptl->ptl_mt_nmaps <= LNET_CPT_NUMBER);
+
+ /* remove it from mt_maps */
+ ptl->ptl_mt_nmaps--;
+ for (i = 0; i < ptl->ptl_mt_nmaps; i++) {
+ if (ptl->ptl_mt_maps[i] >= cpt) /* overwrite it */
+ ptl->ptl_mt_maps[i] = ptl->ptl_mt_maps[i + 1];
+ }
+}
+
+static int
+lnet_try_match_md(lnet_libmd_t *md,
+ struct lnet_match_info *info, struct lnet_msg *msg)
+{
+ /* ALWAYS called holding the lnet_res_lock, and can't lnet_res_unlock;
+ * lnet_match_blocked_msg() relies on this to avoid races */
+ unsigned int offset;
+ unsigned int mlength;
+ lnet_me_t *me = md->md_me;
+
+ /* MD exhausted */
+ if (lnet_md_exhausted(md))
+ return LNET_MATCHMD_NONE | LNET_MATCHMD_EXHAUSTED;
+
+ /* mismatched MD op */
+ if ((md->md_options & info->mi_opc) == 0)
+ return LNET_MATCHMD_NONE;
+
+ /* mismatched ME nid/pid? */
+ if (me->me_match_id.nid != LNET_NID_ANY &&
+ me->me_match_id.nid != info->mi_id.nid)
+ return LNET_MATCHMD_NONE;
+
+ if (me->me_match_id.pid != LNET_PID_ANY &&
+ me->me_match_id.pid != info->mi_id.pid)
+ return LNET_MATCHMD_NONE;
+
+ /* mismatched ME matchbits? */
+ if (((me->me_match_bits ^ info->mi_mbits) & ~me->me_ignore_bits) != 0)
+ return LNET_MATCHMD_NONE;
+
+ /* Hurrah! This _is_ a match; check it out... */
+
+ if ((md->md_options & LNET_MD_MANAGE_REMOTE) == 0)
+ offset = md->md_offset;
+ else
+ offset = info->mi_roffset;
+
+ if ((md->md_options & LNET_MD_MAX_SIZE) != 0) {
+ mlength = md->md_max_size;
+ LASSERT(md->md_offset + mlength <= md->md_length);
+ } else {
+ mlength = md->md_length - offset;
+ }
+
+ if (info->mi_rlength <= mlength) { /* fits in allowed space */
+ mlength = info->mi_rlength;
+ } else if ((md->md_options & LNET_MD_TRUNCATE) == 0) {
+ /* this packet _really_ is too big */
+ CERROR("Matching packet from %s, match %llu length %d too big: %d left, %d allowed\n",
+ libcfs_id2str(info->mi_id), info->mi_mbits,
+ info->mi_rlength, md->md_length - offset, mlength);
+
+ return LNET_MATCHMD_DROP;
+ }
+
+ /* Commit to this ME/MD */
+ CDEBUG(D_NET, "Incoming %s index %x from %s of length %d/%d into md %#llx [%d] + %d\n",
+ (info->mi_opc == LNET_MD_OP_PUT) ? "put" : "get",
+ info->mi_portal, libcfs_id2str(info->mi_id), mlength,
+ info->mi_rlength, md->md_lh.lh_cookie, md->md_niov, offset);
+
+ lnet_msg_attach_md(msg, md, offset, mlength);
+ md->md_offset = offset + mlength;
+
+ if (!lnet_md_exhausted(md))
+ return LNET_MATCHMD_OK;
+
+ /* Auto-unlink NOW, so the ME gets unlinked if required.
+ * We bumped md->md_refcount above so the MD just gets flagged
+ * for unlink when it is finalized. */
+ if ((md->md_flags & LNET_MD_FLAG_AUTO_UNLINK) != 0)
+ lnet_md_unlink(md);
+
+ return LNET_MATCHMD_OK | LNET_MATCHMD_EXHAUSTED;
+}
+
+static struct lnet_match_table *
+lnet_match2mt(struct lnet_portal *ptl, lnet_process_id_t id, __u64 mbits)
+{
+ if (LNET_CPT_NUMBER == 1)
+ return ptl->ptl_mtables[0]; /* the only one */
+
+ /* if it's a unique portal, return match-table hashed by NID */
+ return lnet_ptl_is_unique(ptl) ?
+ ptl->ptl_mtables[lnet_cpt_of_nid(id.nid)] : NULL;
+}
+
+struct lnet_match_table *
+lnet_mt_of_attach(unsigned int index, lnet_process_id_t id,
+ __u64 mbits, __u64 ignore_bits, lnet_ins_pos_t pos)
+{
+ struct lnet_portal *ptl;
+ struct lnet_match_table *mtable;
+
+ /* NB: called w/o lock */
+ LASSERT(index < the_lnet.ln_nportals);
+
+ if (!lnet_ptl_match_type(index, id, mbits, ignore_bits))
+ return NULL;
+
+ ptl = the_lnet.ln_portals[index];
+
+ mtable = lnet_match2mt(ptl, id, mbits);
+ if (mtable != NULL) /* unique portal or only one match-table */
+ return mtable;
+
+ /* it's a wildcard portal */
+ switch (pos) {
+ default:
+ return NULL;
+ case LNET_INS_BEFORE:
+ case LNET_INS_AFTER:
+ /* posted by no affinity thread, always hash to specific
+ * match-table to avoid buffer stealing which is heavy */
+ return ptl->ptl_mtables[ptl->ptl_index % LNET_CPT_NUMBER];
+ case LNET_INS_LOCAL:
+ /* posted by cpu-affinity thread */
+ return ptl->ptl_mtables[lnet_cpt_current()];
+ }
+}
+
+static struct lnet_match_table *
+lnet_mt_of_match(struct lnet_match_info *info, struct lnet_msg *msg)
+{
+ struct lnet_match_table *mtable;
+ struct lnet_portal *ptl;
+ unsigned int nmaps;
+ unsigned int rotor;
+ unsigned int cpt;
+ bool routed;
+
+ /* NB: called w/o lock */
+ LASSERT(info->mi_portal < the_lnet.ln_nportals);
+ ptl = the_lnet.ln_portals[info->mi_portal];
+
+ LASSERT(lnet_ptl_is_wildcard(ptl) || lnet_ptl_is_unique(ptl));
+
+ mtable = lnet_match2mt(ptl, info->mi_id, info->mi_mbits);
+ if (mtable != NULL)
+ return mtable;
+
+ /* it's a wildcard portal */
+ routed = LNET_NIDNET(msg->msg_hdr.src_nid) !=
+ LNET_NIDNET(msg->msg_hdr.dest_nid);
+
+ if (portal_rotor == LNET_PTL_ROTOR_OFF ||
+ (portal_rotor != LNET_PTL_ROTOR_ON && !routed)) {
+ cpt = lnet_cpt_current();
+ if (ptl->ptl_mtables[cpt]->mt_enabled)
+ return ptl->ptl_mtables[cpt];
+ }
+
+ rotor = ptl->ptl_rotor++; /* get round-robin factor */
+ if (portal_rotor == LNET_PTL_ROTOR_HASH_RT && routed)
+ cpt = lnet_cpt_of_nid(msg->msg_hdr.src_nid);
+ else
+ cpt = rotor % LNET_CPT_NUMBER;
+
+ if (!ptl->ptl_mtables[cpt]->mt_enabled) {
+ /* is there any active entry for this portal? */
+ nmaps = ptl->ptl_mt_nmaps;
+ /* map to an active mtable to avoid heavy "stealing" */
+ if (nmaps != 0) {
+ /* NB: there is possibility that ptl_mt_maps is being
+ * changed because we are not under protection of
+ * lnet_ptl_lock, but it shouldn't hurt anything */
+ cpt = ptl->ptl_mt_maps[rotor % nmaps];
+ }
+ }
+
+ return ptl->ptl_mtables[cpt];
+}
+
+static int
+lnet_mt_test_exhausted(struct lnet_match_table *mtable, int pos)
+{
+ __u64 *bmap;
+ int i;
+
+ if (!lnet_ptl_is_wildcard(the_lnet.ln_portals[mtable->mt_portal]))
+ return 0;
+
+ if (pos < 0) { /* check all bits */
+ for (i = 0; i < LNET_MT_EXHAUSTED_BMAP; i++) {
+ if (mtable->mt_exhausted[i] != (__u64)(-1))
+ return 0;
+ }
+ return 1;
+ }
+
+ LASSERT(pos <= LNET_MT_HASH_IGNORE);
+ /* mtable::mt_mhash[pos] is marked as exhausted or not */
+ bmap = &mtable->mt_exhausted[pos >> LNET_MT_BITS_U64];
+ pos &= (1 << LNET_MT_BITS_U64) - 1;
+
+ return ((*bmap) & (1ULL << pos)) != 0;
+}
+
+static void
+lnet_mt_set_exhausted(struct lnet_match_table *mtable, int pos, int exhausted)
+{
+ __u64 *bmap;
+
+ LASSERT(lnet_ptl_is_wildcard(the_lnet.ln_portals[mtable->mt_portal]));
+ LASSERT(pos <= LNET_MT_HASH_IGNORE);
+
+ /* set mtable::mt_mhash[pos] as exhausted/non-exhausted */
+ bmap = &mtable->mt_exhausted[pos >> LNET_MT_BITS_U64];
+ pos &= (1 << LNET_MT_BITS_U64) - 1;
+
+ if (!exhausted)
+ *bmap &= ~(1ULL << pos);
+ else
+ *bmap |= 1ULL << pos;
+}
+
+struct list_head *
+lnet_mt_match_head(struct lnet_match_table *mtable,
+ lnet_process_id_t id, __u64 mbits)
+{
+ struct lnet_portal *ptl = the_lnet.ln_portals[mtable->mt_portal];
+
+ if (lnet_ptl_is_wildcard(ptl)) {
+ return &mtable->mt_mhash[mbits & LNET_MT_HASH_MASK];
+ } else {
+ unsigned long hash = mbits + id.nid + id.pid;
+
+ LASSERT(lnet_ptl_is_unique(ptl));
+ hash = hash_long(hash, LNET_MT_HASH_BITS);
+ return &mtable->mt_mhash[hash];
+ }
+}
+
+int
+lnet_mt_match_md(struct lnet_match_table *mtable,
+ struct lnet_match_info *info, struct lnet_msg *msg)
+{
+ struct list_head *head;
+ lnet_me_t *me;
+ lnet_me_t *tmp;
+ int exhausted = 0;
+ int rc;
+
+ /* any ME with ignore bits? */
+ if (!list_empty(&mtable->mt_mhash[LNET_MT_HASH_IGNORE]))
+ head = &mtable->mt_mhash[LNET_MT_HASH_IGNORE];
+ else
+ head = lnet_mt_match_head(mtable, info->mi_id, info->mi_mbits);
+ again:
+ /* NB: only wildcard portal needs to return LNET_MATCHMD_EXHAUSTED */
+ if (lnet_ptl_is_wildcard(the_lnet.ln_portals[mtable->mt_portal]))
+ exhausted = LNET_MATCHMD_EXHAUSTED;
+
+ list_for_each_entry_safe(me, tmp, head, me_list) {
+ /* ME attached but MD not attached yet */
+ if (me->me_md == NULL)
+ continue;
+
+ LASSERT(me == me->me_md->md_me);
+
+ rc = lnet_try_match_md(me->me_md, info, msg);
+ if ((rc & LNET_MATCHMD_EXHAUSTED) == 0)
+ exhausted = 0; /* mlist is not empty */
+
+ if ((rc & LNET_MATCHMD_FINISH) != 0) {
+ /* don't return EXHAUSTED bit because we don't know
+ * whether the mlist is empty or not */
+ return rc & ~LNET_MATCHMD_EXHAUSTED;
+ }
+ }
+
+ if (exhausted == LNET_MATCHMD_EXHAUSTED) { /* @head is exhausted */
+ lnet_mt_set_exhausted(mtable, head - mtable->mt_mhash, 1);
+ if (!lnet_mt_test_exhausted(mtable, -1))
+ exhausted = 0;
+ }
+
+ if (exhausted == 0 && head == &mtable->mt_mhash[LNET_MT_HASH_IGNORE]) {
+ head = lnet_mt_match_head(mtable, info->mi_id, info->mi_mbits);
+ goto again; /* re-check MEs w/o ignore-bits */
+ }
+
+ if (info->mi_opc == LNET_MD_OP_GET ||
+ !lnet_ptl_is_lazy(the_lnet.ln_portals[info->mi_portal]))
+ return LNET_MATCHMD_DROP | exhausted;
+
+ return LNET_MATCHMD_NONE | exhausted;
+}
+
+static int
+lnet_ptl_match_early(struct lnet_portal *ptl, struct lnet_msg *msg)
+{
+ int rc;
+
+ /* message arrived before any buffer posting on this portal,
+ * simply delay or drop this message */
+ if (likely(lnet_ptl_is_wildcard(ptl) || lnet_ptl_is_unique(ptl)))
+ return 0;
+
+ lnet_ptl_lock(ptl);
+ /* check it again with hold of lock */
+ if (lnet_ptl_is_wildcard(ptl) || lnet_ptl_is_unique(ptl)) {
+ lnet_ptl_unlock(ptl);
+ return 0;
+ }
+
+ if (lnet_ptl_is_lazy(ptl)) {
+ if (msg->msg_rx_ready_delay) {
+ msg->msg_rx_delayed = 1;
+ list_add_tail(&msg->msg_list,
+ &ptl->ptl_msg_delayed);
+ }
+ rc = LNET_MATCHMD_NONE;
+ } else {
+ rc = LNET_MATCHMD_DROP;
+ }
+
+ lnet_ptl_unlock(ptl);
+ return rc;
+}
+
+static int
+lnet_ptl_match_delay(struct lnet_portal *ptl,
+ struct lnet_match_info *info, struct lnet_msg *msg)
+{
+ int first = ptl->ptl_mt_maps[0]; /* read w/o lock */
+ int rc = 0;
+ int i;
+
+ /* steal buffer from other CPTs, and delay it if nothing to steal,
+ * this function is more expensive than a regular match, but we
+ * don't expect it can happen a lot */
+ LASSERT(lnet_ptl_is_wildcard(ptl));
+
+ for (i = 0; i < LNET_CPT_NUMBER; i++) {
+ struct lnet_match_table *mtable;
+ int cpt;
+
+ cpt = (first + i) % LNET_CPT_NUMBER;
+ mtable = ptl->ptl_mtables[cpt];
+ if (i != 0 && i != LNET_CPT_NUMBER - 1 && !mtable->mt_enabled)
+ continue;
+
+ lnet_res_lock(cpt);
+ lnet_ptl_lock(ptl);
+
+ if (i == 0) { /* the first try, attach on stealing list */
+ list_add_tail(&msg->msg_list,
+ &ptl->ptl_msg_stealing);
+ }
+
+ if (!list_empty(&msg->msg_list)) { /* on stealing list */
+ rc = lnet_mt_match_md(mtable, info, msg);
+
+ if ((rc & LNET_MATCHMD_EXHAUSTED) != 0 &&
+ mtable->mt_enabled)
+ lnet_ptl_disable_mt(ptl, cpt);
+
+ if ((rc & LNET_MATCHMD_FINISH) != 0)
+ list_del_init(&msg->msg_list);
+
+ } else {
+ /* could be matched by lnet_ptl_attach_md()
+ * which is called by another thread */
+ rc = msg->msg_md == NULL ?
+ LNET_MATCHMD_DROP : LNET_MATCHMD_OK;
+ }
+
+ if (!list_empty(&msg->msg_list) && /* not matched yet */
+ (i == LNET_CPT_NUMBER - 1 || /* the last CPT */
+ ptl->ptl_mt_nmaps == 0 || /* no active CPT */
+ (ptl->ptl_mt_nmaps == 1 && /* the only active CPT */
+ ptl->ptl_mt_maps[0] == cpt))) {
+ /* nothing to steal, delay or drop */
+ list_del_init(&msg->msg_list);
+
+ if (lnet_ptl_is_lazy(ptl)) {
+ msg->msg_rx_delayed = 1;
+ list_add_tail(&msg->msg_list,
+ &ptl->ptl_msg_delayed);
+ rc = LNET_MATCHMD_NONE;
+ } else {
+ rc = LNET_MATCHMD_DROP;
+ }
+ }
+
+ lnet_ptl_unlock(ptl);
+ lnet_res_unlock(cpt);
+
+ if ((rc & LNET_MATCHMD_FINISH) != 0 || msg->msg_rx_delayed)
+ break;
+ }
+
+ return rc;
+}
+
+int
+lnet_ptl_match_md(struct lnet_match_info *info, struct lnet_msg *msg)
+{
+ struct lnet_match_table *mtable;
+ struct lnet_portal *ptl;
+ int rc;
+
+ CDEBUG(D_NET, "Request from %s of length %d into portal %d MB=%#llx\n",
+ libcfs_id2str(info->mi_id), info->mi_rlength, info->mi_portal,
+ info->mi_mbits);
+
+ if (info->mi_portal >= the_lnet.ln_nportals) {
+ CERROR("Invalid portal %d not in [0-%d]\n",
+ info->mi_portal, the_lnet.ln_nportals);
+ return LNET_MATCHMD_DROP;
+ }
+
+ ptl = the_lnet.ln_portals[info->mi_portal];
+ rc = lnet_ptl_match_early(ptl, msg);
+ if (rc != 0) /* matched or delayed early message */
+ return rc;
+
+ mtable = lnet_mt_of_match(info, msg);
+ lnet_res_lock(mtable->mt_cpt);
+
+ if (the_lnet.ln_shutdown) {
+ rc = LNET_MATCHMD_DROP;
+ goto out1;
+ }
+
+ rc = lnet_mt_match_md(mtable, info, msg);
+ if ((rc & LNET_MATCHMD_EXHAUSTED) != 0 && mtable->mt_enabled) {
+ lnet_ptl_lock(ptl);
+ lnet_ptl_disable_mt(ptl, mtable->mt_cpt);
+ lnet_ptl_unlock(ptl);
+ }
+
+ if ((rc & LNET_MATCHMD_FINISH) != 0) /* matched or dropping */
+ goto out1;
+
+ if (!msg->msg_rx_ready_delay)
+ goto out1;
+
+ LASSERT(lnet_ptl_is_lazy(ptl));
+ LASSERT(!msg->msg_rx_delayed);
+
+ /* NB: we don't expect "delay" can happen a lot */
+ if (lnet_ptl_is_unique(ptl) || LNET_CPT_NUMBER == 1) {
+ lnet_ptl_lock(ptl);
+
+ msg->msg_rx_delayed = 1;
+ list_add_tail(&msg->msg_list, &ptl->ptl_msg_delayed);
+
+ lnet_ptl_unlock(ptl);
+ lnet_res_unlock(mtable->mt_cpt);
+
+ } else {
+ lnet_res_unlock(mtable->mt_cpt);
+ rc = lnet_ptl_match_delay(ptl, info, msg);
+ }
+
+ if (msg->msg_rx_delayed) {
+ CDEBUG(D_NET,
+ "Delaying %s from %s ptl %d MB %#llx off %d len %d\n",
+ info->mi_opc == LNET_MD_OP_PUT ? "PUT" : "GET",
+ libcfs_id2str(info->mi_id), info->mi_portal,
+ info->mi_mbits, info->mi_roffset, info->mi_rlength);
+ }
+ goto out0;
+ out1:
+ lnet_res_unlock(mtable->mt_cpt);
+ out0:
+ /* EXHAUSTED bit is only meaningful for internal functions */
+ return rc & ~LNET_MATCHMD_EXHAUSTED;
+}
+
+void
+lnet_ptl_detach_md(lnet_me_t *me, lnet_libmd_t *md)
+{
+ LASSERT(me->me_md == md && md->md_me == me);
+
+ me->me_md = NULL;
+ md->md_me = NULL;
+}
+
+/* called with lnet_res_lock held */
+void
+lnet_ptl_attach_md(lnet_me_t *me, lnet_libmd_t *md,
+ struct list_head *matches, struct list_head *drops)
+{
+ struct lnet_portal *ptl = the_lnet.ln_portals[me->me_portal];
+ struct lnet_match_table *mtable;
+ struct list_head *head;
+ lnet_msg_t *tmp;
+ lnet_msg_t *msg;
+ int exhausted = 0;
+ int cpt;
+
+ LASSERT(md->md_refcount == 0); /* a brand new MD */
+
+ me->me_md = md;
+ md->md_me = me;
+
+ cpt = lnet_cpt_of_cookie(md->md_lh.lh_cookie);
+ mtable = ptl->ptl_mtables[cpt];
+
+ if (list_empty(&ptl->ptl_msg_stealing) &&
+ list_empty(&ptl->ptl_msg_delayed) &&
+ !lnet_mt_test_exhausted(mtable, me->me_pos))
+ return;
+
+ lnet_ptl_lock(ptl);
+ head = &ptl->ptl_msg_stealing;
+ again:
+ list_for_each_entry_safe(msg, tmp, head, msg_list) {
+ struct lnet_match_info info;
+ lnet_hdr_t *hdr;
+ int rc;
+
+ LASSERT(msg->msg_rx_delayed || head == &ptl->ptl_msg_stealing);
+
+ hdr = &msg->msg_hdr;
+ info.mi_id.nid = hdr->src_nid;
+ info.mi_id.pid = hdr->src_pid;
+ info.mi_opc = LNET_MD_OP_PUT;
+ info.mi_portal = hdr->msg.put.ptl_index;
+ info.mi_rlength = hdr->payload_length;
+ info.mi_roffset = hdr->msg.put.offset;
+ info.mi_mbits = hdr->msg.put.match_bits;
+
+ rc = lnet_try_match_md(md, &info, msg);
+
+ exhausted = (rc & LNET_MATCHMD_EXHAUSTED) != 0;
+ if ((rc & LNET_MATCHMD_NONE) != 0) {
+ if (exhausted)
+ break;
+ continue;
+ }
+
+ /* Hurrah! This _is_ a match */
+ LASSERT((rc & LNET_MATCHMD_FINISH) != 0);
+ list_del_init(&msg->msg_list);
+
+ if (head == &ptl->ptl_msg_stealing) {
+ if (exhausted)
+ break;
+ /* stealing thread will handle the message */
+ continue;
+ }
+
+ if ((rc & LNET_MATCHMD_OK) != 0) {
+ list_add_tail(&msg->msg_list, matches);
+
+ CDEBUG(D_NET, "Resuming delayed PUT from %s portal %d match %llu offset %d length %d.\n",
+ libcfs_id2str(info.mi_id),
+ info.mi_portal, info.mi_mbits,
+ info.mi_roffset, info.mi_rlength);
+ } else {
+ list_add_tail(&msg->msg_list, drops);
+ }
+
+ if (exhausted)
+ break;
+ }
+
+ if (!exhausted && head == &ptl->ptl_msg_stealing) {
+ head = &ptl->ptl_msg_delayed;
+ goto again;
+ }
+
+ if (lnet_ptl_is_wildcard(ptl) && !exhausted) {
+ lnet_mt_set_exhausted(mtable, me->me_pos, 0);
+ if (!mtable->mt_enabled)
+ lnet_ptl_enable_mt(ptl, cpt);
+ }
+
+ lnet_ptl_unlock(ptl);
+}
+
+static void
+lnet_ptl_cleanup(struct lnet_portal *ptl)
+{
+ struct lnet_match_table *mtable;
+ int i;
+
+ if (ptl->ptl_mtables == NULL) /* uninitialized portal */
+ return;
+
+ LASSERT(list_empty(&ptl->ptl_msg_delayed));
+ LASSERT(list_empty(&ptl->ptl_msg_stealing));
+ cfs_percpt_for_each(mtable, i, ptl->ptl_mtables) {
+ struct list_head *mhash;
+ lnet_me_t *me;
+ int j;
+
+ if (mtable->mt_mhash == NULL) /* uninitialized match-table */
+ continue;
+
+ mhash = mtable->mt_mhash;
+ /* cleanup ME */
+ for (j = 0; j < LNET_MT_HASH_SIZE + 1; j++) {
+ while (!list_empty(&mhash[j])) {
+ me = list_entry(mhash[j].next,
+ lnet_me_t, me_list);
+ CERROR("Active ME %p on exit\n", me);
+ list_del(&me->me_list);
+ lnet_me_free(me);
+ }
+ }
+ /* the extra entry is for MEs with ignore bits */
+ LIBCFS_FREE(mhash, sizeof(*mhash) * (LNET_MT_HASH_SIZE + 1));
+ }
+
+ cfs_percpt_free(ptl->ptl_mtables);
+ ptl->ptl_mtables = NULL;
+}
+
+static int
+lnet_ptl_setup(struct lnet_portal *ptl, int index)
+{
+ struct lnet_match_table *mtable;
+ struct list_head *mhash;
+ int i;
+ int j;
+
+ ptl->ptl_mtables = cfs_percpt_alloc(lnet_cpt_table(),
+ sizeof(struct lnet_match_table));
+ if (ptl->ptl_mtables == NULL) {
+ CERROR("Failed to create match table for portal %d\n", index);
+ return -ENOMEM;
+ }
+
+ ptl->ptl_index = index;
+ INIT_LIST_HEAD(&ptl->ptl_msg_delayed);
+ INIT_LIST_HEAD(&ptl->ptl_msg_stealing);
+ spin_lock_init(&ptl->ptl_lock);
+ cfs_percpt_for_each(mtable, i, ptl->ptl_mtables) {
+ /* the extra entry is for MEs with ignore bits */
+ LIBCFS_CPT_ALLOC(mhash, lnet_cpt_table(), i,
+ sizeof(*mhash) * (LNET_MT_HASH_SIZE + 1));
+ if (mhash == NULL) {
+ CERROR("Failed to create match hash for portal %d\n",
+ index);
+ goto failed;
+ }
+
+ memset(&mtable->mt_exhausted[0], -1,
+ sizeof(mtable->mt_exhausted[0]) *
+ LNET_MT_EXHAUSTED_BMAP);
+ mtable->mt_mhash = mhash;
+ for (j = 0; j < LNET_MT_HASH_SIZE + 1; j++)
+ INIT_LIST_HEAD(&mhash[j]);
+
+ mtable->mt_portal = index;
+ mtable->mt_cpt = i;
+ }
+
+ return 0;
+ failed:
+ lnet_ptl_cleanup(ptl);
+ return -ENOMEM;
+}
+
+void
+lnet_portals_destroy(void)
+{
+ int i;
+
+ if (the_lnet.ln_portals == NULL)
+ return;
+
+ for (i = 0; i < the_lnet.ln_nportals; i++)
+ lnet_ptl_cleanup(the_lnet.ln_portals[i]);
+
+ cfs_array_free(the_lnet.ln_portals);
+ the_lnet.ln_portals = NULL;
+}
+
+int
+lnet_portals_create(void)
+{
+ int size;
+ int i;
+
+ size = offsetof(struct lnet_portal, ptl_mt_maps[LNET_CPT_NUMBER]);
+
+ the_lnet.ln_nportals = MAX_PORTALS;
+ the_lnet.ln_portals = cfs_array_alloc(the_lnet.ln_nportals, size);
+ if (the_lnet.ln_portals == NULL) {
+ CERROR("Failed to allocate portals table\n");
+ return -ENOMEM;
+ }
+
+ for (i = 0; i < the_lnet.ln_nportals; i++) {
+ if (lnet_ptl_setup(the_lnet.ln_portals[i], i)) {
+ lnet_portals_destroy();
+ return -ENOMEM;
+ }
+ }
+
+ return 0;
+}
+
+/**
+ * Turn on the lazy portal attribute. Use with caution!
+ *
+ * This portal attribute only affects incoming PUT requests to the portal,
+ * and is off by default. By default, if there's no matching MD for an
+ * incoming PUT request, it is simply dropped. With the lazy attribute on,
+ * such requests are queued indefinitely until either a matching MD is
+ * posted to the portal or the lazy attribute is turned off.
+ *
+ * It would prevent dropped requests, however it should be regarded as the
+ * last line of defense - i.e. users must keep a close watch on active
+ * buffers on a lazy portal and once it becomes too low post more buffers as
+ * soon as possible. This is because delayed requests usually have detrimental
+ * effects on underlying network connections. A few delayed requests often
+ * suffice to bring an underlying connection to a complete halt, due to flow
+ * control mechanisms.
+ *
+ * There's also a DOS attack risk. If users don't post match-all MDs on a
+ * lazy portal, a malicious peer can easily stop a service by sending some
+ * PUT requests with match bits that won't match any MD. A routed server is
+ * especially vulnerable since the connections to its neighbor routers are
+ * shared among all clients.
+ *
+ * \param portal Index of the portal to enable the lazy attribute on.
+ *
+ * \retval 0 On success.
+ * \retval -EINVAL If \a portal is not a valid index.
+ */
+int
+LNetSetLazyPortal(int portal)
+{
+ struct lnet_portal *ptl;
+
+ if (portal < 0 || portal >= the_lnet.ln_nportals)
+ return -EINVAL;
+
+ CDEBUG(D_NET, "Setting portal %d lazy\n", portal);
+ ptl = the_lnet.ln_portals[portal];
+
+ lnet_res_lock(LNET_LOCK_EX);
+ lnet_ptl_lock(ptl);
+
+ lnet_ptl_setopt(ptl, LNET_PTL_LAZY);
+
+ lnet_ptl_unlock(ptl);
+ lnet_res_unlock(LNET_LOCK_EX);
+
+ return 0;
+}
+EXPORT_SYMBOL(LNetSetLazyPortal);
+
+/**
+ * Turn off the lazy portal attribute. Delayed requests on the portal,
+ * if any, will be all dropped when this function returns.
+ *
+ * \param portal Index of the portal to disable the lazy attribute on.
+ *
+ * \retval 0 On success.
+ * \retval -EINVAL If \a portal is not a valid index.
+ */
+int
+LNetClearLazyPortal(int portal)
+{
+ struct lnet_portal *ptl;
+ LIST_HEAD (zombies);
+
+ if (portal < 0 || portal >= the_lnet.ln_nportals)
+ return -EINVAL;
+
+ ptl = the_lnet.ln_portals[portal];
+
+ lnet_res_lock(LNET_LOCK_EX);
+ lnet_ptl_lock(ptl);
+
+ if (!lnet_ptl_is_lazy(ptl)) {
+ lnet_ptl_unlock(ptl);
+ lnet_res_unlock(LNET_LOCK_EX);
+ return 0;
+ }
+
+ if (the_lnet.ln_shutdown)
+ CWARN("Active lazy portal %d on exit\n", portal);
+ else
+ CDEBUG(D_NET, "clearing portal %d lazy\n", portal);
+
+ /* grab all the blocked messages atomically */
+ list_splice_init(&ptl->ptl_msg_delayed, &zombies);
+
+ lnet_ptl_unsetopt(ptl, LNET_PTL_LAZY);
+
+ lnet_ptl_unlock(ptl);
+ lnet_res_unlock(LNET_LOCK_EX);
+
+ lnet_drop_delayed_msg_list(&zombies, "Clearing lazy portal attr");
+
+ return 0;
+}
+EXPORT_SYMBOL(LNetClearLazyPortal);
diff --git a/drivers/staging/lustre/lnet/lnet/lo.c b/drivers/staging/lustre/lnet/lnet/lo.c
new file mode 100644
index 000000000..f708c2e64
--- /dev/null
+++ b/drivers/staging/lustre/lnet/lnet/lo.c
@@ -0,0 +1,120 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ */
+
+#define DEBUG_SUBSYSTEM S_LNET
+#include "../../include/linux/lnet/lib-lnet.h"
+
+static int
+lolnd_send(lnet_ni_t *ni, void *private, lnet_msg_t *lntmsg)
+{
+ LASSERT(!lntmsg->msg_routing);
+ LASSERT(!lntmsg->msg_target_is_router);
+
+ return lnet_parse(ni, &lntmsg->msg_hdr, ni->ni_nid, lntmsg, 0);
+}
+
+static int
+lolnd_recv(lnet_ni_t *ni, void *private, lnet_msg_t *lntmsg,
+ int delayed, unsigned int niov,
+ struct kvec *iov, lnet_kiov_t *kiov,
+ unsigned int offset, unsigned int mlen, unsigned int rlen)
+{
+ lnet_msg_t *sendmsg = private;
+
+ if (lntmsg != NULL) { /* not discarding */
+ if (sendmsg->msg_iov != NULL) {
+ if (iov != NULL)
+ lnet_copy_iov2iov(niov, iov, offset,
+ sendmsg->msg_niov,
+ sendmsg->msg_iov,
+ sendmsg->msg_offset, mlen);
+ else
+ lnet_copy_iov2kiov(niov, kiov, offset,
+ sendmsg->msg_niov,
+ sendmsg->msg_iov,
+ sendmsg->msg_offset, mlen);
+ } else {
+ if (iov != NULL)
+ lnet_copy_kiov2iov(niov, iov, offset,
+ sendmsg->msg_niov,
+ sendmsg->msg_kiov,
+ sendmsg->msg_offset, mlen);
+ else
+ lnet_copy_kiov2kiov(niov, kiov, offset,
+ sendmsg->msg_niov,
+ sendmsg->msg_kiov,
+ sendmsg->msg_offset, mlen);
+ }
+
+ lnet_finalize(ni, lntmsg, 0);
+ }
+
+ lnet_finalize(ni, sendmsg, 0);
+ return 0;
+}
+
+static int lolnd_instanced;
+
+static void
+lolnd_shutdown(lnet_ni_t *ni)
+{
+ CDEBUG(D_NET, "shutdown\n");
+ LASSERT(lolnd_instanced);
+
+ lolnd_instanced = 0;
+}
+
+static int
+lolnd_startup(lnet_ni_t *ni)
+{
+ LASSERT(ni->ni_lnd == &the_lolnd);
+ LASSERT(!lolnd_instanced);
+ lolnd_instanced = 1;
+
+ return 0;
+}
+
+lnd_t the_lolnd = {
+ /* .lnd_list = */ {&the_lolnd.lnd_list, &the_lolnd.lnd_list},
+ /* .lnd_refcount = */ 0,
+ /* .lnd_type = */ LOLND,
+ /* .lnd_startup = */ lolnd_startup,
+ /* .lnd_shutdown = */ lolnd_shutdown,
+ /* .lnt_ctl = */ NULL,
+ /* .lnd_send = */ lolnd_send,
+ /* .lnd_recv = */ lolnd_recv,
+ /* .lnd_eager_recv = */ NULL,
+ /* .lnd_notify = */ NULL,
+ /* .lnd_accept = */ NULL
+};
diff --git a/drivers/staging/lustre/lnet/lnet/module.c b/drivers/staging/lustre/lnet/lnet/module.c
new file mode 100644
index 000000000..72b7fbc83
--- /dev/null
+++ b/drivers/staging/lustre/lnet/lnet/module.c
@@ -0,0 +1,155 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2012, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ */
+
+#define DEBUG_SUBSYSTEM S_LNET
+#include "../../include/linux/lnet/lib-lnet.h"
+
+static int config_on_load;
+module_param(config_on_load, int, 0444);
+MODULE_PARM_DESC(config_on_load, "configure network at module load");
+
+static struct mutex lnet_config_mutex;
+
+static int
+lnet_configure(void *arg)
+{
+ /* 'arg' only there so I can be passed to cfs_create_thread() */
+ int rc = 0;
+
+ LNET_MUTEX_LOCK(&lnet_config_mutex);
+
+ if (!the_lnet.ln_niinit_self) {
+ rc = LNetNIInit(LUSTRE_SRV_LNET_PID);
+ if (rc >= 0) {
+ the_lnet.ln_niinit_self = 1;
+ rc = 0;
+ }
+ }
+
+ LNET_MUTEX_UNLOCK(&lnet_config_mutex);
+ return rc;
+}
+
+static int
+lnet_unconfigure(void)
+{
+ int refcount;
+
+ LNET_MUTEX_LOCK(&lnet_config_mutex);
+
+ if (the_lnet.ln_niinit_self) {
+ the_lnet.ln_niinit_self = 0;
+ LNetNIFini();
+ }
+
+ LNET_MUTEX_LOCK(&the_lnet.ln_api_mutex);
+ refcount = the_lnet.ln_refcount;
+ LNET_MUTEX_UNLOCK(&the_lnet.ln_api_mutex);
+
+ LNET_MUTEX_UNLOCK(&lnet_config_mutex);
+ return (refcount == 0) ? 0 : -EBUSY;
+}
+
+static int
+lnet_ioctl(unsigned int cmd, struct libcfs_ioctl_data *data)
+{
+ int rc;
+
+ switch (cmd) {
+ case IOC_LIBCFS_CONFIGURE:
+ return lnet_configure(NULL);
+
+ case IOC_LIBCFS_UNCONFIGURE:
+ return lnet_unconfigure();
+
+ default:
+ /* Passing LNET_PID_ANY only gives me a ref if the net is up
+ * already; I'll need it to ensure the net can't go down while
+ * I'm called into it */
+ rc = LNetNIInit(LNET_PID_ANY);
+ if (rc >= 0) {
+ rc = LNetCtl(cmd, data);
+ LNetNIFini();
+ }
+ return rc;
+ }
+}
+
+static DECLARE_IOCTL_HANDLER(lnet_ioctl_handler, lnet_ioctl);
+
+static int __init
+init_lnet(void)
+{
+ int rc;
+
+ mutex_init(&lnet_config_mutex);
+
+ rc = LNetInit();
+ if (rc != 0) {
+ CERROR("LNetInit: error %d\n", rc);
+ return rc;
+ }
+
+ rc = libcfs_register_ioctl(&lnet_ioctl_handler);
+ LASSERT(rc == 0);
+
+ if (config_on_load) {
+ /* Have to schedule a separate thread to avoid deadlocking
+ * in modload */
+ (void) kthread_run(lnet_configure, NULL, "lnet_initd");
+ }
+
+ return 0;
+}
+
+static void __exit
+fini_lnet(void)
+{
+ int rc;
+
+ rc = libcfs_deregister_ioctl(&lnet_ioctl_handler);
+ LASSERT(rc == 0);
+
+ LNetFini();
+}
+
+MODULE_AUTHOR("Peter J. Braam <braam@clusterfs.com>");
+MODULE_DESCRIPTION("Portals v3.1");
+MODULE_LICENSE("GPL");
+MODULE_VERSION("1.0.0");
+
+module_init(init_lnet);
+module_exit(fini_lnet);
diff --git a/drivers/staging/lustre/lnet/lnet/peer.c b/drivers/staging/lustre/lnet/lnet/peer.c
new file mode 100644
index 000000000..45b5742f1
--- /dev/null
+++ b/drivers/staging/lustre/lnet/lnet/peer.c
@@ -0,0 +1,338 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2012, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ *
+ * lnet/lnet/peer.c
+ */
+
+#define DEBUG_SUBSYSTEM S_LNET
+
+#include "../../include/linux/lnet/lib-lnet.h"
+
+int
+lnet_peer_tables_create(void)
+{
+ struct lnet_peer_table *ptable;
+ struct list_head *hash;
+ int i;
+ int j;
+
+ the_lnet.ln_peer_tables = cfs_percpt_alloc(lnet_cpt_table(),
+ sizeof(*ptable));
+ if (the_lnet.ln_peer_tables == NULL) {
+ CERROR("Failed to allocate cpu-partition peer tables\n");
+ return -ENOMEM;
+ }
+
+ cfs_percpt_for_each(ptable, i, the_lnet.ln_peer_tables) {
+ INIT_LIST_HEAD(&ptable->pt_deathrow);
+
+ LIBCFS_CPT_ALLOC(hash, lnet_cpt_table(), i,
+ LNET_PEER_HASH_SIZE * sizeof(*hash));
+ if (hash == NULL) {
+ CERROR("Failed to create peer hash table\n");
+ lnet_peer_tables_destroy();
+ return -ENOMEM;
+ }
+
+ for (j = 0; j < LNET_PEER_HASH_SIZE; j++)
+ INIT_LIST_HEAD(&hash[j]);
+ ptable->pt_hash = hash; /* sign of initialization */
+ }
+
+ return 0;
+}
+
+void
+lnet_peer_tables_destroy(void)
+{
+ struct lnet_peer_table *ptable;
+ struct list_head *hash;
+ int i;
+ int j;
+
+ if (the_lnet.ln_peer_tables == NULL)
+ return;
+
+ cfs_percpt_for_each(ptable, i, the_lnet.ln_peer_tables) {
+ hash = ptable->pt_hash;
+ if (hash == NULL) /* not initialized */
+ break;
+
+ LASSERT(list_empty(&ptable->pt_deathrow));
+
+ ptable->pt_hash = NULL;
+ for (j = 0; j < LNET_PEER_HASH_SIZE; j++)
+ LASSERT(list_empty(&hash[j]));
+
+ LIBCFS_FREE(hash, LNET_PEER_HASH_SIZE * sizeof(*hash));
+ }
+
+ cfs_percpt_free(the_lnet.ln_peer_tables);
+ the_lnet.ln_peer_tables = NULL;
+}
+
+void
+lnet_peer_tables_cleanup(void)
+{
+ struct lnet_peer_table *ptable;
+ int i;
+ int j;
+
+ LASSERT(the_lnet.ln_shutdown); /* i.e. no new peers */
+
+ cfs_percpt_for_each(ptable, i, the_lnet.ln_peer_tables) {
+ lnet_net_lock(i);
+
+ for (j = 0; j < LNET_PEER_HASH_SIZE; j++) {
+ struct list_head *peers = &ptable->pt_hash[j];
+
+ while (!list_empty(peers)) {
+ lnet_peer_t *lp = list_entry(peers->next,
+ lnet_peer_t,
+ lp_hashlist);
+ list_del_init(&lp->lp_hashlist);
+ /* lose hash table's ref */
+ lnet_peer_decref_locked(lp);
+ }
+ }
+
+ lnet_net_unlock(i);
+ }
+
+ cfs_percpt_for_each(ptable, i, the_lnet.ln_peer_tables) {
+ LIST_HEAD(deathrow);
+ lnet_peer_t *lp;
+
+ lnet_net_lock(i);
+
+ for (j = 3; ptable->pt_number != 0; j++) {
+ lnet_net_unlock(i);
+
+ if ((j & (j - 1)) == 0) {
+ CDEBUG(D_WARNING,
+ "Waiting for %d peers on peer table\n",
+ ptable->pt_number);
+ }
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ schedule_timeout(cfs_time_seconds(1) / 2);
+ lnet_net_lock(i);
+ }
+ list_splice_init(&ptable->pt_deathrow, &deathrow);
+
+ lnet_net_unlock(i);
+
+ while (!list_empty(&deathrow)) {
+ lp = list_entry(deathrow.next,
+ lnet_peer_t, lp_hashlist);
+ list_del(&lp->lp_hashlist);
+ LIBCFS_FREE(lp, sizeof(*lp));
+ }
+ }
+}
+
+void
+lnet_destroy_peer_locked(lnet_peer_t *lp)
+{
+ struct lnet_peer_table *ptable;
+
+ LASSERT(lp->lp_refcount == 0);
+ LASSERT(lp->lp_rtr_refcount == 0);
+ LASSERT(list_empty(&lp->lp_txq));
+ LASSERT(list_empty(&lp->lp_hashlist));
+ LASSERT(lp->lp_txqnob == 0);
+
+ ptable = the_lnet.ln_peer_tables[lp->lp_cpt];
+ LASSERT(ptable->pt_number > 0);
+ ptable->pt_number--;
+
+ lnet_ni_decref_locked(lp->lp_ni, lp->lp_cpt);
+ lp->lp_ni = NULL;
+
+ list_add(&lp->lp_hashlist, &ptable->pt_deathrow);
+}
+
+lnet_peer_t *
+lnet_find_peer_locked(struct lnet_peer_table *ptable, lnet_nid_t nid)
+{
+ struct list_head *peers;
+ lnet_peer_t *lp;
+
+ LASSERT(!the_lnet.ln_shutdown);
+
+ peers = &ptable->pt_hash[lnet_nid2peerhash(nid)];
+ list_for_each_entry(lp, peers, lp_hashlist) {
+ if (lp->lp_nid == nid) {
+ lnet_peer_addref_locked(lp);
+ return lp;
+ }
+ }
+
+ return NULL;
+}
+
+int
+lnet_nid2peer_locked(lnet_peer_t **lpp, lnet_nid_t nid, int cpt)
+{
+ struct lnet_peer_table *ptable;
+ lnet_peer_t *lp = NULL;
+ lnet_peer_t *lp2;
+ int cpt2;
+ int rc = 0;
+
+ *lpp = NULL;
+ if (the_lnet.ln_shutdown) /* it's shutting down */
+ return -ESHUTDOWN;
+
+ /* cpt can be LNET_LOCK_EX if it's called from router functions */
+ cpt2 = cpt != LNET_LOCK_EX ? cpt : lnet_cpt_of_nid_locked(nid);
+
+ ptable = the_lnet.ln_peer_tables[cpt2];
+ lp = lnet_find_peer_locked(ptable, nid);
+ if (lp != NULL) {
+ *lpp = lp;
+ return 0;
+ }
+
+ if (!list_empty(&ptable->pt_deathrow)) {
+ lp = list_entry(ptable->pt_deathrow.next,
+ lnet_peer_t, lp_hashlist);
+ list_del(&lp->lp_hashlist);
+ }
+
+ /*
+ * take extra refcount in case another thread has shutdown LNet
+ * and destroyed locks and peer-table before I finish the allocation
+ */
+ ptable->pt_number++;
+ lnet_net_unlock(cpt);
+
+ if (lp != NULL)
+ memset(lp, 0, sizeof(*lp));
+ else
+ LIBCFS_CPT_ALLOC(lp, lnet_cpt_table(), cpt2, sizeof(*lp));
+
+ if (lp == NULL) {
+ rc = -ENOMEM;
+ lnet_net_lock(cpt);
+ goto out;
+ }
+
+ INIT_LIST_HEAD(&lp->lp_txq);
+ INIT_LIST_HEAD(&lp->lp_rtrq);
+ INIT_LIST_HEAD(&lp->lp_routes);
+
+ lp->lp_notify = 0;
+ lp->lp_notifylnd = 0;
+ lp->lp_notifying = 0;
+ lp->lp_alive_count = 0;
+ lp->lp_timestamp = 0;
+ lp->lp_alive = !lnet_peers_start_down(); /* 1 bit!! */
+ lp->lp_last_alive = cfs_time_current(); /* assumes alive */
+ lp->lp_last_query = 0; /* haven't asked NI yet */
+ lp->lp_ping_timestamp = 0;
+ lp->lp_ping_feats = LNET_PING_FEAT_INVAL;
+ lp->lp_nid = nid;
+ lp->lp_cpt = cpt2;
+ lp->lp_refcount = 2; /* 1 for caller; 1 for hash */
+ lp->lp_rtr_refcount = 0;
+
+ lnet_net_lock(cpt);
+
+ if (the_lnet.ln_shutdown) {
+ rc = -ESHUTDOWN;
+ goto out;
+ }
+
+ lp2 = lnet_find_peer_locked(ptable, nid);
+ if (lp2 != NULL) {
+ *lpp = lp2;
+ goto out;
+ }
+
+ lp->lp_ni = lnet_net2ni_locked(LNET_NIDNET(nid), cpt2);
+ if (lp->lp_ni == NULL) {
+ rc = -EHOSTUNREACH;
+ goto out;
+ }
+
+ lp->lp_txcredits =
+ lp->lp_mintxcredits = lp->lp_ni->ni_peertxcredits;
+ lp->lp_rtrcredits =
+ lp->lp_minrtrcredits = lnet_peer_buffer_credits(lp->lp_ni);
+
+ list_add_tail(&lp->lp_hashlist,
+ &ptable->pt_hash[lnet_nid2peerhash(nid)]);
+ ptable->pt_version++;
+ *lpp = lp;
+
+ return 0;
+out:
+ if (lp != NULL)
+ list_add(&lp->lp_hashlist, &ptable->pt_deathrow);
+ ptable->pt_number--;
+ return rc;
+}
+
+void
+lnet_debug_peer(lnet_nid_t nid)
+{
+ char *aliveness = "NA";
+ lnet_peer_t *lp;
+ int rc;
+ int cpt;
+
+ cpt = lnet_cpt_of_nid(nid);
+ lnet_net_lock(cpt);
+
+ rc = lnet_nid2peer_locked(&lp, nid, cpt);
+ if (rc != 0) {
+ lnet_net_unlock(cpt);
+ CDEBUG(D_WARNING, "No peer %s\n", libcfs_nid2str(nid));
+ return;
+ }
+
+ if (lnet_isrouter(lp) || lnet_peer_aliveness_enabled(lp))
+ aliveness = lp->lp_alive ? "up" : "down";
+
+ CDEBUG(D_WARNING, "%-24s %4d %5s %5d %5d %5d %5d %5d %ld\n",
+ libcfs_nid2str(lp->lp_nid), lp->lp_refcount,
+ aliveness, lp->lp_ni->ni_peertxcredits,
+ lp->lp_rtrcredits, lp->lp_minrtrcredits,
+ lp->lp_txcredits, lp->lp_mintxcredits, lp->lp_txqnob);
+
+ lnet_peer_decref_locked(lp);
+
+ lnet_net_unlock(cpt);
+}
diff --git a/drivers/staging/lustre/lnet/lnet/router.c b/drivers/staging/lustre/lnet/lnet/router.c
new file mode 100644
index 000000000..8510bae48
--- /dev/null
+++ b/drivers/staging/lustre/lnet/lnet/router.c
@@ -0,0 +1,1706 @@
+/*
+ * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
+ *
+ * Copyright (c) 2011, 2012, Intel Corporation.
+ *
+ * This file is part of Portals
+ * http://sourceforge.net/projects/sandiaportals/
+ *
+ * Portals is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * Portals 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 Portals; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#define DEBUG_SUBSYSTEM S_LNET
+#include "../../include/linux/lnet/lib-lnet.h"
+
+#if defined(LNET_ROUTER)
+
+#define LNET_NRB_TINY_MIN 512 /* min value for each CPT */
+#define LNET_NRB_TINY (LNET_NRB_TINY_MIN * 4)
+#define LNET_NRB_SMALL_MIN 4096 /* min value for each CPT */
+#define LNET_NRB_SMALL (LNET_NRB_SMALL_MIN * 4)
+#define LNET_NRB_LARGE_MIN 256 /* min value for each CPT */
+#define LNET_NRB_LARGE (LNET_NRB_LARGE_MIN * 4)
+
+static char *forwarding = "";
+module_param(forwarding, charp, 0444);
+MODULE_PARM_DESC(forwarding, "Explicitly enable/disable forwarding between networks");
+
+static int tiny_router_buffers;
+module_param(tiny_router_buffers, int, 0444);
+MODULE_PARM_DESC(tiny_router_buffers, "# of 0 payload messages to buffer in the router");
+static int small_router_buffers;
+module_param(small_router_buffers, int, 0444);
+MODULE_PARM_DESC(small_router_buffers, "# of small (1 page) messages to buffer in the router");
+static int large_router_buffers;
+module_param(large_router_buffers, int, 0444);
+MODULE_PARM_DESC(large_router_buffers, "# of large messages to buffer in the router");
+static int peer_buffer_credits;
+module_param(peer_buffer_credits, int, 0444);
+MODULE_PARM_DESC(peer_buffer_credits, "# router buffer credits per peer");
+
+static int auto_down = 1;
+module_param(auto_down, int, 0444);
+MODULE_PARM_DESC(auto_down, "Automatically mark peers down on comms error");
+
+int
+lnet_peer_buffer_credits(lnet_ni_t *ni)
+{
+ /* NI option overrides LNet default */
+ if (ni->ni_peerrtrcredits > 0)
+ return ni->ni_peerrtrcredits;
+ if (peer_buffer_credits > 0)
+ return peer_buffer_credits;
+
+ /* As an approximation, allow this peer the same number of router
+ * buffers as it is allowed outstanding sends */
+ return ni->ni_peertxcredits;
+}
+
+/* forward ref's */
+static int lnet_router_checker(void *);
+#else
+
+int
+lnet_peer_buffer_credits(lnet_ni_t *ni)
+{
+ return 0;
+}
+
+#endif
+
+static int check_routers_before_use;
+module_param(check_routers_before_use, int, 0444);
+MODULE_PARM_DESC(check_routers_before_use, "Assume routers are down and ping them before use");
+
+int avoid_asym_router_failure = 1;
+module_param(avoid_asym_router_failure, int, 0644);
+MODULE_PARM_DESC(avoid_asym_router_failure, "Avoid asymmetrical router failures (0 to disable)");
+
+static int dead_router_check_interval = 60;
+module_param(dead_router_check_interval, int, 0644);
+MODULE_PARM_DESC(dead_router_check_interval, "Seconds between dead router health checks (<= 0 to disable)");
+
+static int live_router_check_interval = 60;
+module_param(live_router_check_interval, int, 0644);
+MODULE_PARM_DESC(live_router_check_interval, "Seconds between live router health checks (<= 0 to disable)");
+
+static int router_ping_timeout = 50;
+module_param(router_ping_timeout, int, 0644);
+MODULE_PARM_DESC(router_ping_timeout, "Seconds to wait for the reply to a router health query");
+
+int
+lnet_peers_start_down(void)
+{
+ return check_routers_before_use;
+}
+
+void
+lnet_notify_locked(lnet_peer_t *lp, int notifylnd, int alive,
+ unsigned long when)
+{
+ if (time_before(when, lp->lp_timestamp)) { /* out of date information */
+ CDEBUG(D_NET, "Out of date\n");
+ return;
+ }
+
+ lp->lp_timestamp = when; /* update timestamp */
+ lp->lp_ping_deadline = 0; /* disable ping timeout */
+
+ if (lp->lp_alive_count != 0 && /* got old news */
+ (!lp->lp_alive) == (!alive)) { /* new date for old news */
+ CDEBUG(D_NET, "Old news\n");
+ return;
+ }
+
+ /* Flag that notification is outstanding */
+
+ lp->lp_alive_count++;
+ lp->lp_alive = !(!alive); /* 1 bit! */
+ lp->lp_notify = 1;
+ lp->lp_notifylnd |= notifylnd;
+ if (lp->lp_alive)
+ lp->lp_ping_feats = LNET_PING_FEAT_INVAL; /* reset */
+
+ CDEBUG(D_NET, "set %s %d\n", libcfs_nid2str(lp->lp_nid), alive);
+}
+
+static void
+lnet_ni_notify_locked(lnet_ni_t *ni, lnet_peer_t *lp)
+{
+ int alive;
+ int notifylnd;
+
+ /* Notify only in 1 thread at any time to ensure ordered notification.
+ * NB individual events can be missed; the only guarantee is that you
+ * always get the most recent news */
+
+ if (lp->lp_notifying || ni == NULL)
+ return;
+
+ lp->lp_notifying = 1;
+
+ while (lp->lp_notify) {
+ alive = lp->lp_alive;
+ notifylnd = lp->lp_notifylnd;
+
+ lp->lp_notifylnd = 0;
+ lp->lp_notify = 0;
+
+ if (notifylnd && ni->ni_lnd->lnd_notify != NULL) {
+ lnet_net_unlock(lp->lp_cpt);
+
+ /* A new notification could happen now; I'll handle it
+ * when control returns to me */
+
+ (ni->ni_lnd->lnd_notify)(ni, lp->lp_nid, alive);
+
+ lnet_net_lock(lp->lp_cpt);
+ }
+ }
+
+ lp->lp_notifying = 0;
+}
+
+
+static void
+lnet_rtr_addref_locked(lnet_peer_t *lp)
+{
+ LASSERT(lp->lp_refcount > 0);
+ LASSERT(lp->lp_rtr_refcount >= 0);
+
+ /* lnet_net_lock must be exclusively locked */
+ lp->lp_rtr_refcount++;
+ if (lp->lp_rtr_refcount == 1) {
+ struct list_head *pos;
+
+ /* a simple insertion sort */
+ list_for_each_prev(pos, &the_lnet.ln_routers) {
+ lnet_peer_t *rtr = list_entry(pos, lnet_peer_t,
+ lp_rtr_list);
+
+ if (rtr->lp_nid < lp->lp_nid)
+ break;
+ }
+
+ list_add(&lp->lp_rtr_list, pos);
+ /* addref for the_lnet.ln_routers */
+ lnet_peer_addref_locked(lp);
+ the_lnet.ln_routers_version++;
+ }
+}
+
+static void
+lnet_rtr_decref_locked(lnet_peer_t *lp)
+{
+ LASSERT(lp->lp_refcount > 0);
+ LASSERT(lp->lp_rtr_refcount > 0);
+
+ /* lnet_net_lock must be exclusively locked */
+ lp->lp_rtr_refcount--;
+ if (lp->lp_rtr_refcount == 0) {
+ LASSERT(list_empty(&lp->lp_routes));
+
+ if (lp->lp_rcd != NULL) {
+ list_add(&lp->lp_rcd->rcd_list,
+ &the_lnet.ln_rcd_deathrow);
+ lp->lp_rcd = NULL;
+ }
+
+ list_del(&lp->lp_rtr_list);
+ /* decref for the_lnet.ln_routers */
+ lnet_peer_decref_locked(lp);
+ the_lnet.ln_routers_version++;
+ }
+}
+
+lnet_remotenet_t *
+lnet_find_net_locked(__u32 net)
+{
+ lnet_remotenet_t *rnet;
+ struct list_head *tmp;
+ struct list_head *rn_list;
+
+ LASSERT(!the_lnet.ln_shutdown);
+
+ rn_list = lnet_net2rnethash(net);
+ list_for_each(tmp, rn_list) {
+ rnet = list_entry(tmp, lnet_remotenet_t, lrn_list);
+
+ if (rnet->lrn_net == net)
+ return rnet;
+ }
+ return NULL;
+}
+
+static void lnet_shuffle_seed(void)
+{
+ static int seeded;
+ int lnd_type, seed[2];
+ struct timeval tv;
+ lnet_ni_t *ni;
+ struct list_head *tmp;
+
+ if (seeded)
+ return;
+
+ cfs_get_random_bytes(seed, sizeof(seed));
+
+ /* Nodes with small feet have little entropy
+ * the NID for this node gives the most entropy in the low bits */
+ list_for_each(tmp, &the_lnet.ln_nis) {
+ ni = list_entry(tmp, lnet_ni_t, ni_list);
+ lnd_type = LNET_NETTYP(LNET_NIDNET(ni->ni_nid));
+
+ if (lnd_type != LOLND)
+ seed[0] ^= (LNET_NIDADDR(ni->ni_nid) | lnd_type);
+ }
+
+ do_gettimeofday(&tv);
+ cfs_srand(tv.tv_sec ^ seed[0], tv.tv_usec ^ seed[1]);
+ seeded = 1;
+}
+
+/* NB expects LNET_LOCK held */
+static void
+lnet_add_route_to_rnet(lnet_remotenet_t *rnet, lnet_route_t *route)
+{
+ unsigned int len = 0;
+ unsigned int offset = 0;
+ struct list_head *e;
+
+ lnet_shuffle_seed();
+
+ list_for_each(e, &rnet->lrn_routes) {
+ len++;
+ }
+
+ /* len+1 positions to add a new entry, also prevents division by 0 */
+ offset = cfs_rand() % (len + 1);
+ list_for_each(e, &rnet->lrn_routes) {
+ if (offset == 0)
+ break;
+ offset--;
+ }
+ list_add(&route->lr_list, e);
+ list_add(&route->lr_gwlist, &route->lr_gateway->lp_routes);
+
+ the_lnet.ln_remote_nets_version++;
+ lnet_rtr_addref_locked(route->lr_gateway);
+}
+
+int
+lnet_add_route(__u32 net, unsigned int hops, lnet_nid_t gateway,
+ unsigned int priority)
+{
+ struct list_head *e;
+ lnet_remotenet_t *rnet;
+ lnet_remotenet_t *rnet2;
+ lnet_route_t *route;
+ lnet_ni_t *ni;
+ int add_route;
+ int rc;
+
+ CDEBUG(D_NET, "Add route: net %s hops %u priority %u gw %s\n",
+ libcfs_net2str(net), hops, priority, libcfs_nid2str(gateway));
+
+ if (gateway == LNET_NID_ANY ||
+ LNET_NETTYP(LNET_NIDNET(gateway)) == LOLND ||
+ net == LNET_NIDNET(LNET_NID_ANY) ||
+ LNET_NETTYP(net) == LOLND ||
+ LNET_NIDNET(gateway) == net ||
+ hops < 1 || hops > 255)
+ return -EINVAL;
+
+ if (lnet_islocalnet(net)) /* it's a local network */
+ return 0; /* ignore the route entry */
+
+ /* Assume net, route, all new */
+ LIBCFS_ALLOC(route, sizeof(*route));
+ LIBCFS_ALLOC(rnet, sizeof(*rnet));
+ if (route == NULL || rnet == NULL) {
+ CERROR("Out of memory creating route %s %d %s\n",
+ libcfs_net2str(net), hops, libcfs_nid2str(gateway));
+ if (route != NULL)
+ LIBCFS_FREE(route, sizeof(*route));
+ if (rnet != NULL)
+ LIBCFS_FREE(rnet, sizeof(*rnet));
+ return -ENOMEM;
+ }
+
+ INIT_LIST_HEAD(&rnet->lrn_routes);
+ rnet->lrn_net = net;
+ route->lr_hops = hops;
+ route->lr_net = net;
+ route->lr_priority = priority;
+
+ lnet_net_lock(LNET_LOCK_EX);
+
+ rc = lnet_nid2peer_locked(&route->lr_gateway, gateway, LNET_LOCK_EX);
+ if (rc != 0) {
+ lnet_net_unlock(LNET_LOCK_EX);
+
+ LIBCFS_FREE(route, sizeof(*route));
+ LIBCFS_FREE(rnet, sizeof(*rnet));
+
+ if (rc == -EHOSTUNREACH) /* gateway is not on a local net */
+ return 0; /* ignore the route entry */
+ CERROR("Error %d creating route %s %d %s\n", rc,
+ libcfs_net2str(net), hops,
+ libcfs_nid2str(gateway));
+
+ return rc;
+ }
+
+ LASSERT(!the_lnet.ln_shutdown);
+
+ rnet2 = lnet_find_net_locked(net);
+ if (rnet2 == NULL) {
+ /* new network */
+ list_add_tail(&rnet->lrn_list, lnet_net2rnethash(net));
+ rnet2 = rnet;
+ }
+
+ /* Search for a duplicate route (it's a NOOP if it is) */
+ add_route = 1;
+ list_for_each(e, &rnet2->lrn_routes) {
+ lnet_route_t *route2 = list_entry(e, lnet_route_t, lr_list);
+
+ if (route2->lr_gateway == route->lr_gateway) {
+ add_route = 0;
+ break;
+ }
+
+ /* our lookups must be true */
+ LASSERT(route2->lr_gateway->lp_nid != gateway);
+ }
+
+ if (add_route) {
+ lnet_peer_addref_locked(route->lr_gateway); /* +1 for notify */
+ lnet_add_route_to_rnet(rnet2, route);
+
+ ni = route->lr_gateway->lp_ni;
+ lnet_net_unlock(LNET_LOCK_EX);
+
+ /* XXX Assume alive */
+ if (ni->ni_lnd->lnd_notify != NULL)
+ (ni->ni_lnd->lnd_notify)(ni, gateway, 1);
+
+ lnet_net_lock(LNET_LOCK_EX);
+ }
+
+ /* -1 for notify or !add_route */
+ lnet_peer_decref_locked(route->lr_gateway);
+ lnet_net_unlock(LNET_LOCK_EX);
+
+ if (!add_route)
+ LIBCFS_FREE(route, sizeof(*route));
+
+ if (rnet != rnet2)
+ LIBCFS_FREE(rnet, sizeof(*rnet));
+
+ return 0;
+}
+
+int
+lnet_check_routes(void)
+{
+ lnet_remotenet_t *rnet;
+ lnet_route_t *route;
+ lnet_route_t *route2;
+ struct list_head *e1;
+ struct list_head *e2;
+ int cpt;
+ struct list_head *rn_list;
+ int i;
+
+ cpt = lnet_net_lock_current();
+
+ for (i = 0; i < LNET_REMOTE_NETS_HASH_SIZE; i++) {
+ rn_list = &the_lnet.ln_remote_nets_hash[i];
+ list_for_each(e1, rn_list) {
+ rnet = list_entry(e1, lnet_remotenet_t, lrn_list);
+
+ route2 = NULL;
+ list_for_each(e2, &rnet->lrn_routes) {
+ lnet_nid_t nid1;
+ lnet_nid_t nid2;
+ int net;
+
+ route = list_entry(e2, lnet_route_t,
+ lr_list);
+
+ if (route2 == NULL) {
+ route2 = route;
+ continue;
+ }
+
+ if (route->lr_gateway->lp_ni ==
+ route2->lr_gateway->lp_ni)
+ continue;
+
+ nid1 = route->lr_gateway->lp_nid;
+ nid2 = route2->lr_gateway->lp_nid;
+ net = rnet->lrn_net;
+
+ lnet_net_unlock(cpt);
+
+ CERROR("Routes to %s via %s and %s not supported\n",
+ libcfs_net2str(net),
+ libcfs_nid2str(nid1),
+ libcfs_nid2str(nid2));
+ return -EINVAL;
+ }
+ }
+ }
+
+ lnet_net_unlock(cpt);
+ return 0;
+}
+
+int
+lnet_del_route(__u32 net, lnet_nid_t gw_nid)
+{
+ struct lnet_peer *gateway;
+ lnet_remotenet_t *rnet;
+ lnet_route_t *route;
+ struct list_head *e1;
+ struct list_head *e2;
+ int rc = -ENOENT;
+ struct list_head *rn_list;
+ int idx = 0;
+
+ CDEBUG(D_NET, "Del route: net %s : gw %s\n",
+ libcfs_net2str(net), libcfs_nid2str(gw_nid));
+
+ /* NB Caller may specify either all routes via the given gateway
+ * or a specific route entry actual NIDs) */
+
+ lnet_net_lock(LNET_LOCK_EX);
+ if (net == LNET_NIDNET(LNET_NID_ANY))
+ rn_list = &the_lnet.ln_remote_nets_hash[0];
+ else
+ rn_list = lnet_net2rnethash(net);
+
+ again:
+ list_for_each(e1, rn_list) {
+ rnet = list_entry(e1, lnet_remotenet_t, lrn_list);
+
+ if (!(net == LNET_NIDNET(LNET_NID_ANY) ||
+ net == rnet->lrn_net))
+ continue;
+
+ list_for_each(e2, &rnet->lrn_routes) {
+ route = list_entry(e2, lnet_route_t, lr_list);
+
+ gateway = route->lr_gateway;
+ if (!(gw_nid == LNET_NID_ANY ||
+ gw_nid == gateway->lp_nid))
+ continue;
+
+ list_del(&route->lr_list);
+ list_del(&route->lr_gwlist);
+ the_lnet.ln_remote_nets_version++;
+
+ if (list_empty(&rnet->lrn_routes))
+ list_del(&rnet->lrn_list);
+ else
+ rnet = NULL;
+
+ lnet_rtr_decref_locked(gateway);
+ lnet_peer_decref_locked(gateway);
+
+ lnet_net_unlock(LNET_LOCK_EX);
+
+ LIBCFS_FREE(route, sizeof(*route));
+
+ if (rnet != NULL)
+ LIBCFS_FREE(rnet, sizeof(*rnet));
+
+ rc = 0;
+ lnet_net_lock(LNET_LOCK_EX);
+ goto again;
+ }
+ }
+
+ if (net == LNET_NIDNET(LNET_NID_ANY) &&
+ ++idx < LNET_REMOTE_NETS_HASH_SIZE) {
+ rn_list = &the_lnet.ln_remote_nets_hash[idx];
+ goto again;
+ }
+ lnet_net_unlock(LNET_LOCK_EX);
+
+ return rc;
+}
+
+void
+lnet_destroy_routes(void)
+{
+ lnet_del_route(LNET_NIDNET(LNET_NID_ANY), LNET_NID_ANY);
+}
+
+int
+lnet_get_route(int idx, __u32 *net, __u32 *hops,
+ lnet_nid_t *gateway, __u32 *alive, __u32 *priority)
+{
+ struct list_head *e1;
+ struct list_head *e2;
+ lnet_remotenet_t *rnet;
+ lnet_route_t *route;
+ int cpt;
+ int i;
+ struct list_head *rn_list;
+
+ cpt = lnet_net_lock_current();
+
+ for (i = 0; i < LNET_REMOTE_NETS_HASH_SIZE; i++) {
+ rn_list = &the_lnet.ln_remote_nets_hash[i];
+ list_for_each(e1, rn_list) {
+ rnet = list_entry(e1, lnet_remotenet_t, lrn_list);
+
+ list_for_each(e2, &rnet->lrn_routes) {
+ route = list_entry(e2, lnet_route_t,
+ lr_list);
+
+ if (idx-- == 0) {
+ *net = rnet->lrn_net;
+ *hops = route->lr_hops;
+ *priority = route->lr_priority;
+ *gateway = route->lr_gateway->lp_nid;
+ *alive = route->lr_gateway->lp_alive;
+ lnet_net_unlock(cpt);
+ return 0;
+ }
+ }
+ }
+ }
+
+ lnet_net_unlock(cpt);
+ return -ENOENT;
+}
+
+void
+lnet_swap_pinginfo(lnet_ping_info_t *info)
+{
+ int i;
+ lnet_ni_status_t *stat;
+
+ __swab32s(&info->pi_magic);
+ __swab32s(&info->pi_features);
+ __swab32s(&info->pi_pid);
+ __swab32s(&info->pi_nnis);
+ for (i = 0; i < info->pi_nnis && i < LNET_MAX_RTR_NIS; i++) {
+ stat = &info->pi_ni[i];
+ __swab64s(&stat->ns_nid);
+ __swab32s(&stat->ns_status);
+ }
+}
+
+/**
+ * parse router-checker pinginfo, record number of down NIs for remote
+ * networks on that router.
+ */
+static void
+lnet_parse_rc_info(lnet_rc_data_t *rcd)
+{
+ lnet_ping_info_t *info = rcd->rcd_pinginfo;
+ struct lnet_peer *gw = rcd->rcd_gateway;
+ lnet_route_t *rtr;
+
+ if (!gw->lp_alive)
+ return;
+
+ if (info->pi_magic == __swab32(LNET_PROTO_PING_MAGIC))
+ lnet_swap_pinginfo(info);
+
+ /* NB always racing with network! */
+ if (info->pi_magic != LNET_PROTO_PING_MAGIC) {
+ CDEBUG(D_NET, "%s: Unexpected magic %08x\n",
+ libcfs_nid2str(gw->lp_nid), info->pi_magic);
+ gw->lp_ping_feats = LNET_PING_FEAT_INVAL;
+ return;
+ }
+
+ gw->lp_ping_feats = info->pi_features;
+ if ((gw->lp_ping_feats & LNET_PING_FEAT_MASK) == 0) {
+ CDEBUG(D_NET, "%s: Unexpected features 0x%x\n",
+ libcfs_nid2str(gw->lp_nid), gw->lp_ping_feats);
+ return; /* nothing I can understand */
+ }
+
+ if ((gw->lp_ping_feats & LNET_PING_FEAT_NI_STATUS) == 0)
+ return; /* can't carry NI status info */
+
+ list_for_each_entry(rtr, &gw->lp_routes, lr_gwlist) {
+ int ptl_status = LNET_NI_STATUS_INVALID;
+ int down = 0;
+ int up = 0;
+ int i;
+
+ for (i = 0; i < info->pi_nnis && i < LNET_MAX_RTR_NIS; i++) {
+ lnet_ni_status_t *stat = &info->pi_ni[i];
+ lnet_nid_t nid = stat->ns_nid;
+
+ if (nid == LNET_NID_ANY) {
+ CDEBUG(D_NET, "%s: unexpected LNET_NID_ANY\n",
+ libcfs_nid2str(gw->lp_nid));
+ gw->lp_ping_feats = LNET_PING_FEAT_INVAL;
+ return;
+ }
+
+ if (LNET_NETTYP(LNET_NIDNET(nid)) == LOLND)
+ continue;
+
+ if (stat->ns_status == LNET_NI_STATUS_DOWN) {
+ if (LNET_NETTYP(LNET_NIDNET(nid)) != PTLLND)
+ down++;
+ else if (ptl_status != LNET_NI_STATUS_UP)
+ ptl_status = LNET_NI_STATUS_DOWN;
+ continue;
+ }
+
+ if (stat->ns_status == LNET_NI_STATUS_UP) {
+ if (LNET_NIDNET(nid) == rtr->lr_net) {
+ up = 1;
+ break;
+ }
+ /* ptl NIs are considered down only when
+ * they're all down */
+ if (LNET_NETTYP(LNET_NIDNET(nid)) == PTLLND)
+ ptl_status = LNET_NI_STATUS_UP;
+ continue;
+ }
+
+ CDEBUG(D_NET, "%s: Unexpected status 0x%x\n",
+ libcfs_nid2str(gw->lp_nid), stat->ns_status);
+ gw->lp_ping_feats = LNET_PING_FEAT_INVAL;
+ return;
+ }
+
+ if (up) { /* ignore downed NIs if NI for dest network is up */
+ rtr->lr_downis = 0;
+ continue;
+ }
+ rtr->lr_downis = down + (ptl_status == LNET_NI_STATUS_DOWN);
+ }
+}
+
+static void
+lnet_router_checker_event(lnet_event_t *event)
+{
+ lnet_rc_data_t *rcd = event->md.user_ptr;
+ struct lnet_peer *lp;
+
+ LASSERT(rcd != NULL);
+
+ if (event->unlinked) {
+ LNetInvalidateHandle(&rcd->rcd_mdh);
+ return;
+ }
+
+ LASSERT(event->type == LNET_EVENT_SEND ||
+ event->type == LNET_EVENT_REPLY);
+
+ lp = rcd->rcd_gateway;
+ LASSERT(lp != NULL);
+
+ /* NB: it's called with holding lnet_res_lock, we have a few
+ * places need to hold both locks at the same time, please take
+ * care of lock ordering */
+ lnet_net_lock(lp->lp_cpt);
+ if (!lnet_isrouter(lp) || lp->lp_rcd != rcd) {
+ /* ignore if no longer a router or rcd is replaced */
+ goto out;
+ }
+
+ if (event->type == LNET_EVENT_SEND) {
+ lp->lp_ping_notsent = 0;
+ if (event->status == 0)
+ goto out;
+ }
+
+ /* LNET_EVENT_REPLY */
+ /* A successful REPLY means the router is up. If _any_ comms
+ * to the router fail I assume it's down (this will happen if
+ * we ping alive routers to try to detect router death before
+ * apps get burned). */
+
+ lnet_notify_locked(lp, 1, (event->status == 0), cfs_time_current());
+ /* The router checker will wake up very shortly and do the
+ * actual notification.
+ * XXX If 'lp' stops being a router before then, it will still
+ * have the notification pending!!! */
+
+ if (avoid_asym_router_failure && event->status == 0)
+ lnet_parse_rc_info(rcd);
+
+ out:
+ lnet_net_unlock(lp->lp_cpt);
+}
+
+static void
+lnet_wait_known_routerstate(void)
+{
+ lnet_peer_t *rtr;
+ struct list_head *entry;
+ int all_known;
+
+ LASSERT(the_lnet.ln_rc_state == LNET_RC_STATE_RUNNING);
+
+ for (;;) {
+ int cpt = lnet_net_lock_current();
+
+ all_known = 1;
+ list_for_each(entry, &the_lnet.ln_routers) {
+ rtr = list_entry(entry, lnet_peer_t, lp_rtr_list);
+
+ if (rtr->lp_alive_count == 0) {
+ all_known = 0;
+ break;
+ }
+ }
+
+ lnet_net_unlock(cpt);
+
+ if (all_known)
+ return;
+
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ schedule_timeout(cfs_time_seconds(1));
+ }
+}
+
+void
+lnet_router_ni_update_locked(lnet_peer_t *gw, __u32 net)
+{
+ lnet_route_t *rte;
+
+ if ((gw->lp_ping_feats & LNET_PING_FEAT_NI_STATUS) != 0) {
+ list_for_each_entry(rte, &gw->lp_routes, lr_gwlist) {
+ if (rte->lr_net == net) {
+ rte->lr_downis = 0;
+ break;
+ }
+ }
+ }
+}
+
+static void
+lnet_update_ni_status_locked(void)
+{
+ lnet_ni_t *ni;
+ long now;
+ int timeout;
+
+ LASSERT(the_lnet.ln_routing);
+
+ timeout = router_ping_timeout +
+ max(live_router_check_interval, dead_router_check_interval);
+
+ now = get_seconds();
+ list_for_each_entry(ni, &the_lnet.ln_nis, ni_list) {
+ if (ni->ni_lnd->lnd_type == LOLND)
+ continue;
+
+ if (now < ni->ni_last_alive + timeout)
+ continue;
+
+ lnet_ni_lock(ni);
+ /* re-check with lock */
+ if (now < ni->ni_last_alive + timeout) {
+ lnet_ni_unlock(ni);
+ continue;
+ }
+
+ LASSERT(ni->ni_status != NULL);
+
+ if (ni->ni_status->ns_status != LNET_NI_STATUS_DOWN) {
+ CDEBUG(D_NET, "NI(%s:%d) status changed to down\n",
+ libcfs_nid2str(ni->ni_nid), timeout);
+ /* NB: so far, this is the only place to set
+ * NI status to "down" */
+ ni->ni_status->ns_status = LNET_NI_STATUS_DOWN;
+ }
+ lnet_ni_unlock(ni);
+ }
+}
+
+static void
+lnet_destroy_rc_data(lnet_rc_data_t *rcd)
+{
+ LASSERT(list_empty(&rcd->rcd_list));
+ /* detached from network */
+ LASSERT(LNetHandleIsInvalid(rcd->rcd_mdh));
+
+ if (rcd->rcd_gateway != NULL) {
+ int cpt = rcd->rcd_gateway->lp_cpt;
+
+ lnet_net_lock(cpt);
+ lnet_peer_decref_locked(rcd->rcd_gateway);
+ lnet_net_unlock(cpt);
+ }
+
+ if (rcd->rcd_pinginfo != NULL)
+ LIBCFS_FREE(rcd->rcd_pinginfo, LNET_PINGINFO_SIZE);
+
+ LIBCFS_FREE(rcd, sizeof(*rcd));
+}
+
+static lnet_rc_data_t *
+lnet_create_rc_data_locked(lnet_peer_t *gateway)
+{
+ lnet_rc_data_t *rcd = NULL;
+ lnet_ping_info_t *pi;
+ int rc;
+ int i;
+
+ lnet_net_unlock(gateway->lp_cpt);
+
+ LIBCFS_ALLOC(rcd, sizeof(*rcd));
+ if (rcd == NULL)
+ goto out;
+
+ LNetInvalidateHandle(&rcd->rcd_mdh);
+ INIT_LIST_HEAD(&rcd->rcd_list);
+
+ LIBCFS_ALLOC(pi, LNET_PINGINFO_SIZE);
+ if (pi == NULL)
+ goto out;
+
+ for (i = 0; i < LNET_MAX_RTR_NIS; i++) {
+ pi->pi_ni[i].ns_nid = LNET_NID_ANY;
+ pi->pi_ni[i].ns_status = LNET_NI_STATUS_INVALID;
+ }
+ rcd->rcd_pinginfo = pi;
+
+ LASSERT(!LNetHandleIsInvalid(the_lnet.ln_rc_eqh));
+ rc = LNetMDBind((lnet_md_t){.start = pi,
+ .user_ptr = rcd,
+ .length = LNET_PINGINFO_SIZE,
+ .threshold = LNET_MD_THRESH_INF,
+ .options = LNET_MD_TRUNCATE,
+ .eq_handle = the_lnet.ln_rc_eqh},
+ LNET_UNLINK,
+ &rcd->rcd_mdh);
+ if (rc < 0) {
+ CERROR("Can't bind MD: %d\n", rc);
+ goto out;
+ }
+ LASSERT(rc == 0);
+
+ lnet_net_lock(gateway->lp_cpt);
+ /* router table changed or someone has created rcd for this gateway */
+ if (!lnet_isrouter(gateway) || gateway->lp_rcd != NULL) {
+ lnet_net_unlock(gateway->lp_cpt);
+ goto out;
+ }
+
+ lnet_peer_addref_locked(gateway);
+ rcd->rcd_gateway = gateway;
+ gateway->lp_rcd = rcd;
+ gateway->lp_ping_notsent = 0;
+
+ return rcd;
+
+ out:
+ if (rcd != NULL) {
+ if (!LNetHandleIsInvalid(rcd->rcd_mdh)) {
+ rc = LNetMDUnlink(rcd->rcd_mdh);
+ LASSERT(rc == 0);
+ }
+ lnet_destroy_rc_data(rcd);
+ }
+
+ lnet_net_lock(gateway->lp_cpt);
+ return gateway->lp_rcd;
+}
+
+static int
+lnet_router_check_interval(lnet_peer_t *rtr)
+{
+ int secs;
+
+ secs = rtr->lp_alive ? live_router_check_interval :
+ dead_router_check_interval;
+ if (secs < 0)
+ secs = 0;
+
+ return secs;
+}
+
+static void
+lnet_ping_router_locked(lnet_peer_t *rtr)
+{
+ lnet_rc_data_t *rcd = NULL;
+ unsigned long now = cfs_time_current();
+ int secs;
+
+ lnet_peer_addref_locked(rtr);
+
+ if (rtr->lp_ping_deadline != 0 && /* ping timed out? */
+ cfs_time_after(now, rtr->lp_ping_deadline))
+ lnet_notify_locked(rtr, 1, 0, now);
+
+ /* Run any outstanding notifications */
+ lnet_ni_notify_locked(rtr->lp_ni, rtr);
+
+ if (!lnet_isrouter(rtr) ||
+ the_lnet.ln_rc_state != LNET_RC_STATE_RUNNING) {
+ /* router table changed or router checker is shutting down */
+ lnet_peer_decref_locked(rtr);
+ return;
+ }
+
+ rcd = rtr->lp_rcd != NULL ?
+ rtr->lp_rcd : lnet_create_rc_data_locked(rtr);
+
+ if (rcd == NULL)
+ return;
+
+ secs = lnet_router_check_interval(rtr);
+
+ CDEBUG(D_NET,
+ "rtr %s %d: deadline %lu ping_notsent %d alive %d alive_count %d lp_ping_timestamp %lu\n",
+ libcfs_nid2str(rtr->lp_nid), secs,
+ rtr->lp_ping_deadline, rtr->lp_ping_notsent,
+ rtr->lp_alive, rtr->lp_alive_count, rtr->lp_ping_timestamp);
+
+ if (secs != 0 && !rtr->lp_ping_notsent &&
+ cfs_time_after(now, cfs_time_add(rtr->lp_ping_timestamp,
+ cfs_time_seconds(secs)))) {
+ int rc;
+ lnet_process_id_t id;
+ lnet_handle_md_t mdh;
+
+ id.nid = rtr->lp_nid;
+ id.pid = LUSTRE_SRV_LNET_PID;
+ CDEBUG(D_NET, "Check: %s\n", libcfs_id2str(id));
+
+ rtr->lp_ping_notsent = 1;
+ rtr->lp_ping_timestamp = now;
+
+ mdh = rcd->rcd_mdh;
+
+ if (rtr->lp_ping_deadline == 0) {
+ rtr->lp_ping_deadline =
+ cfs_time_shift(router_ping_timeout);
+ }
+
+ lnet_net_unlock(rtr->lp_cpt);
+
+ rc = LNetGet(LNET_NID_ANY, mdh, id, LNET_RESERVED_PORTAL,
+ LNET_PROTO_PING_MATCHBITS, 0);
+
+ lnet_net_lock(rtr->lp_cpt);
+ if (rc != 0)
+ rtr->lp_ping_notsent = 0; /* no event pending */
+ }
+
+ lnet_peer_decref_locked(rtr);
+}
+
+int
+lnet_router_checker_start(void)
+{
+ int rc;
+ int eqsz;
+
+ LASSERT(the_lnet.ln_rc_state == LNET_RC_STATE_SHUTDOWN);
+
+ if (check_routers_before_use &&
+ dead_router_check_interval <= 0) {
+ LCONSOLE_ERROR_MSG(0x10a, "'dead_router_check_interval' must be set if 'check_routers_before_use' is set\n");
+ return -EINVAL;
+ }
+
+ if (!the_lnet.ln_routing &&
+ live_router_check_interval <= 0 &&
+ dead_router_check_interval <= 0)
+ return 0;
+
+ sema_init(&the_lnet.ln_rc_signal, 0);
+ /* EQ size doesn't matter; the callback is guaranteed to get every
+ * event */
+ eqsz = 0;
+ rc = LNetEQAlloc(eqsz, lnet_router_checker_event,
+ &the_lnet.ln_rc_eqh);
+ if (rc != 0) {
+ CERROR("Can't allocate EQ(%d): %d\n", eqsz, rc);
+ return -ENOMEM;
+ }
+
+ the_lnet.ln_rc_state = LNET_RC_STATE_RUNNING;
+ rc = PTR_ERR(kthread_run(lnet_router_checker,
+ NULL, "router_checker"));
+ if (IS_ERR_VALUE(rc)) {
+ CERROR("Can't start router checker thread: %d\n", rc);
+ /* block until event callback signals exit */
+ down(&the_lnet.ln_rc_signal);
+ rc = LNetEQFree(the_lnet.ln_rc_eqh);
+ LASSERT(rc == 0);
+ the_lnet.ln_rc_state = LNET_RC_STATE_SHUTDOWN;
+ return -ENOMEM;
+ }
+
+ if (check_routers_before_use) {
+ /* Note that a helpful side-effect of pinging all known routers
+ * at startup is that it makes them drop stale connections they
+ * may have to a previous instance of me. */
+ lnet_wait_known_routerstate();
+ }
+
+ return 0;
+}
+
+void
+lnet_router_checker_stop(void)
+{
+ int rc;
+
+ if (the_lnet.ln_rc_state == LNET_RC_STATE_SHUTDOWN)
+ return;
+
+ LASSERT(the_lnet.ln_rc_state == LNET_RC_STATE_RUNNING);
+ the_lnet.ln_rc_state = LNET_RC_STATE_STOPPING;
+
+ /* block until event callback signals exit */
+ down(&the_lnet.ln_rc_signal);
+ LASSERT(the_lnet.ln_rc_state == LNET_RC_STATE_SHUTDOWN);
+
+ rc = LNetEQFree(the_lnet.ln_rc_eqh);
+ LASSERT(rc == 0);
+}
+
+static void
+lnet_prune_rc_data(int wait_unlink)
+{
+ lnet_rc_data_t *rcd;
+ lnet_rc_data_t *tmp;
+ lnet_peer_t *lp;
+ struct list_head head;
+ int i = 2;
+
+ if (likely(the_lnet.ln_rc_state == LNET_RC_STATE_RUNNING &&
+ list_empty(&the_lnet.ln_rcd_deathrow) &&
+ list_empty(&the_lnet.ln_rcd_zombie)))
+ return;
+
+ INIT_LIST_HEAD(&head);
+
+ lnet_net_lock(LNET_LOCK_EX);
+
+ if (the_lnet.ln_rc_state != LNET_RC_STATE_RUNNING) {
+ /* router checker is stopping, prune all */
+ list_for_each_entry(lp, &the_lnet.ln_routers,
+ lp_rtr_list) {
+ if (lp->lp_rcd == NULL)
+ continue;
+
+ LASSERT(list_empty(&lp->lp_rcd->rcd_list));
+ list_add(&lp->lp_rcd->rcd_list,
+ &the_lnet.ln_rcd_deathrow);
+ lp->lp_rcd = NULL;
+ }
+ }
+
+ /* unlink all RCDs on deathrow list */
+ list_splice_init(&the_lnet.ln_rcd_deathrow, &head);
+
+ if (!list_empty(&head)) {
+ lnet_net_unlock(LNET_LOCK_EX);
+
+ list_for_each_entry(rcd, &head, rcd_list)
+ LNetMDUnlink(rcd->rcd_mdh);
+
+ lnet_net_lock(LNET_LOCK_EX);
+ }
+
+ list_splice_init(&head, &the_lnet.ln_rcd_zombie);
+
+ /* release all zombie RCDs */
+ while (!list_empty(&the_lnet.ln_rcd_zombie)) {
+ list_for_each_entry_safe(rcd, tmp, &the_lnet.ln_rcd_zombie,
+ rcd_list) {
+ if (LNetHandleIsInvalid(rcd->rcd_mdh))
+ list_move(&rcd->rcd_list, &head);
+ }
+
+ wait_unlink = wait_unlink &&
+ !list_empty(&the_lnet.ln_rcd_zombie);
+
+ lnet_net_unlock(LNET_LOCK_EX);
+
+ while (!list_empty(&head)) {
+ rcd = list_entry(head.next,
+ lnet_rc_data_t, rcd_list);
+ list_del_init(&rcd->rcd_list);
+ lnet_destroy_rc_data(rcd);
+ }
+
+ if (!wait_unlink)
+ return;
+
+ i++;
+ CDEBUG(((i & (-i)) == i) ? D_WARNING : D_NET,
+ "Waiting for rc buffers to unlink\n");
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ schedule_timeout(cfs_time_seconds(1) / 4);
+
+ lnet_net_lock(LNET_LOCK_EX);
+ }
+
+ lnet_net_unlock(LNET_LOCK_EX);
+}
+
+
+#if defined(LNET_ROUTER)
+
+static int
+lnet_router_checker(void *arg)
+{
+ lnet_peer_t *rtr;
+ struct list_head *entry;
+
+ cfs_block_allsigs();
+
+ LASSERT(the_lnet.ln_rc_state == LNET_RC_STATE_RUNNING);
+
+ while (the_lnet.ln_rc_state == LNET_RC_STATE_RUNNING) {
+ __u64 version;
+ int cpt;
+ int cpt2;
+
+ cpt = lnet_net_lock_current();
+rescan:
+ version = the_lnet.ln_routers_version;
+
+ list_for_each(entry, &the_lnet.ln_routers) {
+ rtr = list_entry(entry, lnet_peer_t, lp_rtr_list);
+
+ cpt2 = lnet_cpt_of_nid_locked(rtr->lp_nid);
+ if (cpt != cpt2) {
+ lnet_net_unlock(cpt);
+ cpt = cpt2;
+ lnet_net_lock(cpt);
+ /* the routers list has changed */
+ if (version != the_lnet.ln_routers_version)
+ goto rescan;
+ }
+
+ lnet_ping_router_locked(rtr);
+
+ /* NB dropped lock */
+ if (version != the_lnet.ln_routers_version) {
+ /* the routers list has changed */
+ goto rescan;
+ }
+ }
+
+ if (the_lnet.ln_routing)
+ lnet_update_ni_status_locked();
+
+ lnet_net_unlock(cpt);
+
+ lnet_prune_rc_data(0); /* don't wait for UNLINK */
+
+ /* Call schedule_timeout() here always adds 1 to load average
+ * because kernel counts # active tasks as nr_running
+ * + nr_uninterruptible. */
+ set_current_state(TASK_INTERRUPTIBLE);
+ schedule_timeout(cfs_time_seconds(1));
+ }
+
+ LASSERT(the_lnet.ln_rc_state == LNET_RC_STATE_STOPPING);
+
+ lnet_prune_rc_data(1); /* wait for UNLINK */
+
+ the_lnet.ln_rc_state = LNET_RC_STATE_SHUTDOWN;
+ up(&the_lnet.ln_rc_signal);
+ /* The unlink event callback will signal final completion */
+ return 0;
+}
+
+static void
+lnet_destroy_rtrbuf(lnet_rtrbuf_t *rb, int npages)
+{
+ int sz = offsetof(lnet_rtrbuf_t, rb_kiov[npages]);
+
+ while (--npages >= 0)
+ __free_page(rb->rb_kiov[npages].kiov_page);
+
+ LIBCFS_FREE(rb, sz);
+}
+
+static lnet_rtrbuf_t *
+lnet_new_rtrbuf(lnet_rtrbufpool_t *rbp, int cpt)
+{
+ int npages = rbp->rbp_npages;
+ int sz = offsetof(lnet_rtrbuf_t, rb_kiov[npages]);
+ struct page *page;
+ lnet_rtrbuf_t *rb;
+ int i;
+
+ LIBCFS_CPT_ALLOC(rb, lnet_cpt_table(), cpt, sz);
+ if (rb == NULL)
+ return NULL;
+
+ rb->rb_pool = rbp;
+
+ for (i = 0; i < npages; i++) {
+ page = alloc_pages_node(
+ cfs_cpt_spread_node(lnet_cpt_table(), cpt),
+ __GFP_ZERO | GFP_IOFS, 0);
+ if (page == NULL) {
+ while (--i >= 0)
+ __free_page(rb->rb_kiov[i].kiov_page);
+
+ LIBCFS_FREE(rb, sz);
+ return NULL;
+ }
+
+ rb->rb_kiov[i].kiov_len = PAGE_CACHE_SIZE;
+ rb->rb_kiov[i].kiov_offset = 0;
+ rb->rb_kiov[i].kiov_page = page;
+ }
+
+ return rb;
+}
+
+static void
+lnet_rtrpool_free_bufs(lnet_rtrbufpool_t *rbp)
+{
+ int npages = rbp->rbp_npages;
+ int nbuffers = 0;
+ lnet_rtrbuf_t *rb;
+
+ if (rbp->rbp_nbuffers == 0) /* not initialized or already freed */
+ return;
+
+ LASSERT(list_empty(&rbp->rbp_msgs));
+ LASSERT(rbp->rbp_credits == rbp->rbp_nbuffers);
+
+ while (!list_empty(&rbp->rbp_bufs)) {
+ LASSERT(rbp->rbp_credits > 0);
+
+ rb = list_entry(rbp->rbp_bufs.next,
+ lnet_rtrbuf_t, rb_list);
+ list_del(&rb->rb_list);
+ lnet_destroy_rtrbuf(rb, npages);
+ nbuffers++;
+ }
+
+ LASSERT(rbp->rbp_nbuffers == nbuffers);
+ LASSERT(rbp->rbp_credits == nbuffers);
+
+ rbp->rbp_nbuffers = rbp->rbp_credits = 0;
+}
+
+static int
+lnet_rtrpool_alloc_bufs(lnet_rtrbufpool_t *rbp, int nbufs, int cpt)
+{
+ lnet_rtrbuf_t *rb;
+ int i;
+
+ if (rbp->rbp_nbuffers != 0) {
+ LASSERT(rbp->rbp_nbuffers == nbufs);
+ return 0;
+ }
+
+ for (i = 0; i < nbufs; i++) {
+ rb = lnet_new_rtrbuf(rbp, cpt);
+
+ if (rb == NULL) {
+ CERROR("Failed to allocate %d router bufs of %d pages\n",
+ nbufs, rbp->rbp_npages);
+ return -ENOMEM;
+ }
+
+ rbp->rbp_nbuffers++;
+ rbp->rbp_credits++;
+ rbp->rbp_mincredits++;
+ list_add(&rb->rb_list, &rbp->rbp_bufs);
+
+ /* No allocation "under fire" */
+ /* Otherwise we'd need code to schedule blocked msgs etc */
+ LASSERT(!the_lnet.ln_routing);
+ }
+
+ LASSERT(rbp->rbp_credits == nbufs);
+ return 0;
+}
+
+static void
+lnet_rtrpool_init(lnet_rtrbufpool_t *rbp, int npages)
+{
+ INIT_LIST_HEAD(&rbp->rbp_msgs);
+ INIT_LIST_HEAD(&rbp->rbp_bufs);
+
+ rbp->rbp_npages = npages;
+ rbp->rbp_credits = 0;
+ rbp->rbp_mincredits = 0;
+}
+
+void
+lnet_rtrpools_free(void)
+{
+ lnet_rtrbufpool_t *rtrp;
+ int i;
+
+ if (the_lnet.ln_rtrpools == NULL) /* uninitialized or freed */
+ return;
+
+ cfs_percpt_for_each(rtrp, i, the_lnet.ln_rtrpools) {
+ lnet_rtrpool_free_bufs(&rtrp[0]);
+ lnet_rtrpool_free_bufs(&rtrp[1]);
+ lnet_rtrpool_free_bufs(&rtrp[2]);
+ }
+
+ cfs_percpt_free(the_lnet.ln_rtrpools);
+ the_lnet.ln_rtrpools = NULL;
+}
+
+static int
+lnet_nrb_tiny_calculate(int npages)
+{
+ int nrbs = LNET_NRB_TINY;
+
+ if (tiny_router_buffers < 0) {
+ LCONSOLE_ERROR_MSG(0x10c,
+ "tiny_router_buffers=%d invalid when routing enabled\n",
+ tiny_router_buffers);
+ return -1;
+ }
+
+ if (tiny_router_buffers > 0)
+ nrbs = tiny_router_buffers;
+
+ nrbs /= LNET_CPT_NUMBER;
+ return max(nrbs, LNET_NRB_TINY_MIN);
+}
+
+static int
+lnet_nrb_small_calculate(int npages)
+{
+ int nrbs = LNET_NRB_SMALL;
+
+ if (small_router_buffers < 0) {
+ LCONSOLE_ERROR_MSG(0x10c,
+ "small_router_buffers=%d invalid when routing enabled\n",
+ small_router_buffers);
+ return -1;
+ }
+
+ if (small_router_buffers > 0)
+ nrbs = small_router_buffers;
+
+ nrbs /= LNET_CPT_NUMBER;
+ return max(nrbs, LNET_NRB_SMALL_MIN);
+}
+
+static int
+lnet_nrb_large_calculate(int npages)
+{
+ int nrbs = LNET_NRB_LARGE;
+
+ if (large_router_buffers < 0) {
+ LCONSOLE_ERROR_MSG(0x10c,
+ "large_router_buffers=%d invalid when routing enabled\n",
+ large_router_buffers);
+ return -1;
+ }
+
+ if (large_router_buffers > 0)
+ nrbs = large_router_buffers;
+
+ nrbs /= LNET_CPT_NUMBER;
+ return max(nrbs, LNET_NRB_LARGE_MIN);
+}
+
+int
+lnet_rtrpools_alloc(int im_a_router)
+{
+ lnet_rtrbufpool_t *rtrp;
+ int large_pages;
+ int small_pages = 1;
+ int nrb_tiny;
+ int nrb_small;
+ int nrb_large;
+ int rc;
+ int i;
+
+ large_pages = (LNET_MTU + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT;
+
+ if (!strcmp(forwarding, "")) {
+ /* not set either way */
+ if (!im_a_router)
+ return 0;
+ } else if (!strcmp(forwarding, "disabled")) {
+ /* explicitly disabled */
+ return 0;
+ } else if (!strcmp(forwarding, "enabled")) {
+ /* explicitly enabled */
+ } else {
+ LCONSOLE_ERROR_MSG(0x10b, "'forwarding' not set to either 'enabled' or 'disabled'\n");
+ return -EINVAL;
+ }
+
+ nrb_tiny = lnet_nrb_tiny_calculate(0);
+ if (nrb_tiny < 0)
+ return -EINVAL;
+
+ nrb_small = lnet_nrb_small_calculate(small_pages);
+ if (nrb_small < 0)
+ return -EINVAL;
+
+ nrb_large = lnet_nrb_large_calculate(large_pages);
+ if (nrb_large < 0)
+ return -EINVAL;
+
+ the_lnet.ln_rtrpools = cfs_percpt_alloc(lnet_cpt_table(),
+ LNET_NRBPOOLS *
+ sizeof(lnet_rtrbufpool_t));
+ if (the_lnet.ln_rtrpools == NULL) {
+ LCONSOLE_ERROR_MSG(0x10c,
+ "Failed to initialize router buffe pool\n");
+ return -ENOMEM;
+ }
+
+ cfs_percpt_for_each(rtrp, i, the_lnet.ln_rtrpools) {
+ lnet_rtrpool_init(&rtrp[0], 0);
+ rc = lnet_rtrpool_alloc_bufs(&rtrp[0], nrb_tiny, i);
+ if (rc != 0)
+ goto failed;
+
+ lnet_rtrpool_init(&rtrp[1], small_pages);
+ rc = lnet_rtrpool_alloc_bufs(&rtrp[1], nrb_small, i);
+ if (rc != 0)
+ goto failed;
+
+ lnet_rtrpool_init(&rtrp[2], large_pages);
+ rc = lnet_rtrpool_alloc_bufs(&rtrp[2], nrb_large, i);
+ if (rc != 0)
+ goto failed;
+ }
+
+ lnet_net_lock(LNET_LOCK_EX);
+ the_lnet.ln_routing = 1;
+ lnet_net_unlock(LNET_LOCK_EX);
+
+ return 0;
+
+ failed:
+ lnet_rtrpools_free();
+ return rc;
+}
+
+int
+lnet_notify(lnet_ni_t *ni, lnet_nid_t nid, int alive, unsigned long when)
+{
+ struct lnet_peer *lp = NULL;
+ unsigned long now = cfs_time_current();
+ int cpt = lnet_cpt_of_nid(nid);
+
+ LASSERT(!in_interrupt ());
+
+ CDEBUG(D_NET, "%s notifying %s: %s\n",
+ (ni == NULL) ? "userspace" : libcfs_nid2str(ni->ni_nid),
+ libcfs_nid2str(nid),
+ alive ? "up" : "down");
+
+ if (ni != NULL &&
+ LNET_NIDNET(ni->ni_nid) != LNET_NIDNET(nid)) {
+ CWARN("Ignoring notification of %s %s by %s (different net)\n",
+ libcfs_nid2str(nid), alive ? "birth" : "death",
+ libcfs_nid2str(ni->ni_nid));
+ return -EINVAL;
+ }
+
+ /* can't do predictions... */
+ if (cfs_time_after(when, now)) {
+ CWARN("Ignoring prediction from %s of %s %s %ld seconds in the future\n",
+ (ni == NULL) ? "userspace" : libcfs_nid2str(ni->ni_nid),
+ libcfs_nid2str(nid), alive ? "up" : "down",
+ cfs_duration_sec(cfs_time_sub(when, now)));
+ return -EINVAL;
+ }
+
+ if (ni != NULL && !alive && /* LND telling me she's down */
+ !auto_down) { /* auto-down disabled */
+ CDEBUG(D_NET, "Auto-down disabled\n");
+ return 0;
+ }
+
+ lnet_net_lock(cpt);
+
+ if (the_lnet.ln_shutdown) {
+ lnet_net_unlock(cpt);
+ return -ESHUTDOWN;
+ }
+
+ lp = lnet_find_peer_locked(the_lnet.ln_peer_tables[cpt], nid);
+ if (lp == NULL) {
+ /* nid not found */
+ lnet_net_unlock(cpt);
+ CDEBUG(D_NET, "%s not found\n", libcfs_nid2str(nid));
+ return 0;
+ }
+
+ /* We can't fully trust LND on reporting exact peer last_alive
+ * if he notifies us about dead peer. For example ksocklnd can
+ * call us with when == _time_when_the_node_was_booted_ if
+ * no connections were successfully established */
+ if (ni != NULL && !alive && when < lp->lp_last_alive)
+ when = lp->lp_last_alive;
+
+ lnet_notify_locked(lp, ni == NULL, alive, when);
+
+ lnet_ni_notify_locked(ni, lp);
+
+ lnet_peer_decref_locked(lp);
+
+ lnet_net_unlock(cpt);
+ return 0;
+}
+EXPORT_SYMBOL(lnet_notify);
+
+void
+lnet_get_tunables(void)
+{
+}
+
+#else
+
+int
+lnet_notify(lnet_ni_t *ni, lnet_nid_t nid, int alive, unsigned long when)
+{
+ return -EOPNOTSUPP;
+}
+
+void
+lnet_router_checker(void)
+{
+ static time_t last;
+ static int running;
+
+ time_t now = get_seconds();
+ int interval = now - last;
+ int rc;
+ __u64 version;
+ lnet_peer_t *rtr;
+
+ /* It's no use to call me again within a sec - all intervals and
+ * timeouts are measured in seconds */
+ if (last != 0 && interval < 2)
+ return;
+
+ if (last != 0 &&
+ interval > max(live_router_check_interval,
+ dead_router_check_interval))
+ CNETERR("Checker(%d/%d) not called for %d seconds\n",
+ live_router_check_interval, dead_router_check_interval,
+ interval);
+
+ LASSERT(LNET_CPT_NUMBER == 1);
+
+ lnet_net_lock(0);
+ LASSERT(!running); /* recursion check */
+ running = 1;
+ lnet_net_unlock(0);
+
+ last = now;
+
+ if (the_lnet.ln_rc_state == LNET_RC_STATE_STOPPING)
+ lnet_prune_rc_data(0); /* unlink all rcd and nowait */
+
+ /* consume all pending events */
+ while (1) {
+ int i;
+ lnet_event_t ev;
+
+ /* NB ln_rc_eqh must be the 1st in 'eventqs' otherwise the
+ * recursion breaker in LNetEQPoll would fail */
+ rc = LNetEQPoll(&the_lnet.ln_rc_eqh, 1, 0, &ev, &i);
+ if (rc == 0) /* no event pending */
+ break;
+
+ /* NB a lost SENT prevents me from pinging a router again */
+ if (rc == -EOVERFLOW) {
+ CERROR("Dropped an event!!!\n");
+ abort();
+ }
+
+ LASSERT(rc == 1);
+
+ lnet_router_checker_event(&ev);
+ }
+
+ if (the_lnet.ln_rc_state == LNET_RC_STATE_STOPPING) {
+ lnet_prune_rc_data(1); /* release rcd */
+ the_lnet.ln_rc_state = LNET_RC_STATE_SHUTDOWN;
+ running = 0;
+ return;
+ }
+
+ LASSERT(the_lnet.ln_rc_state == LNET_RC_STATE_RUNNING);
+
+ lnet_net_lock(0);
+
+ version = the_lnet.ln_routers_version;
+ list_for_each_entry(rtr, &the_lnet.ln_routers, lp_rtr_list) {
+ lnet_ping_router_locked(rtr);
+ LASSERT(version == the_lnet.ln_routers_version);
+ }
+
+ lnet_net_unlock(0);
+
+ running = 0; /* lock only needed for the recursion check */
+}
+
+/* NB lnet_peers_start_down depends on me,
+ * so must be called before any peer creation */
+void
+lnet_get_tunables(void)
+{
+ char *s;
+
+ s = getenv("LNET_ROUTER_PING_TIMEOUT");
+ if (s != NULL)
+ router_ping_timeout = atoi(s);
+
+ s = getenv("LNET_LIVE_ROUTER_CHECK_INTERVAL");
+ if (s != NULL)
+ live_router_check_interval = atoi(s);
+
+ s = getenv("LNET_DEAD_ROUTER_CHECK_INTERVAL");
+ if (s != NULL)
+ dead_router_check_interval = atoi(s);
+
+ /* This replaces old lnd_notify mechanism */
+ check_routers_before_use = 1;
+ if (dead_router_check_interval <= 0)
+ dead_router_check_interval = 30;
+}
+
+void
+lnet_rtrpools_free(void)
+{
+}
+
+int
+lnet_rtrpools_alloc(int im_a_arouter)
+{
+ return 0;
+}
+
+#endif
diff --git a/drivers/staging/lustre/lnet/lnet/router_proc.c b/drivers/staging/lustre/lnet/lnet/router_proc.c
new file mode 100644
index 000000000..c055afc86
--- /dev/null
+++ b/drivers/staging/lustre/lnet/lnet/router_proc.c
@@ -0,0 +1,968 @@
+/*
+ * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
+ *
+ * Copyright (c) 2011, 2012, Intel Corporation.
+ *
+ * This file is part of Portals
+ * http://sourceforge.net/projects/sandiaportals/
+ *
+ * Portals is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * Portals 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 Portals; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#define DEBUG_SUBSYSTEM S_LNET
+#include "../../include/linux/libcfs/libcfs.h"
+#include "../../include/linux/lnet/lib-lnet.h"
+
+#if defined(LNET_ROUTER)
+
+/* This is really lnet_proc.c. You might need to update sanity test 215
+ * if any file format is changed. */
+
+static struct ctl_table_header *lnet_table_header;
+
+#define CTL_LNET (0x100)
+enum {
+ PSDEV_LNET_STATS = 100,
+ PSDEV_LNET_ROUTES,
+ PSDEV_LNET_ROUTERS,
+ PSDEV_LNET_PEERS,
+ PSDEV_LNET_BUFFERS,
+ PSDEV_LNET_NIS,
+ PSDEV_LNET_PTL_ROTOR,
+};
+
+#define LNET_LOFFT_BITS (sizeof(loff_t) * 8)
+/*
+ * NB: max allowed LNET_CPT_BITS is 8 on 64-bit system and 2 on 32-bit system
+ */
+#define LNET_PROC_CPT_BITS (LNET_CPT_BITS + 1)
+/* change version, 16 bits or 8 bits */
+#define LNET_PROC_VER_BITS max_t(size_t, min_t(size_t, LNET_LOFFT_BITS, 64) / 4, 8)
+
+#define LNET_PROC_HASH_BITS LNET_PEER_HASH_BITS
+/*
+ * bits for peer hash offset
+ * NB: we don't use the highest bit of *ppos because it's signed
+ */
+#define LNET_PROC_HOFF_BITS (LNET_LOFFT_BITS - \
+ LNET_PROC_CPT_BITS - \
+ LNET_PROC_VER_BITS - \
+ LNET_PROC_HASH_BITS - 1)
+/* bits for hash index + position */
+#define LNET_PROC_HPOS_BITS (LNET_PROC_HASH_BITS + LNET_PROC_HOFF_BITS)
+/* bits for peer hash table + hash version */
+#define LNET_PROC_VPOS_BITS (LNET_PROC_HPOS_BITS + LNET_PROC_VER_BITS)
+
+#define LNET_PROC_CPT_MASK ((1ULL << LNET_PROC_CPT_BITS) - 1)
+#define LNET_PROC_VER_MASK ((1ULL << LNET_PROC_VER_BITS) - 1)
+#define LNET_PROC_HASH_MASK ((1ULL << LNET_PROC_HASH_BITS) - 1)
+#define LNET_PROC_HOFF_MASK ((1ULL << LNET_PROC_HOFF_BITS) - 1)
+
+#define LNET_PROC_CPT_GET(pos) \
+ (int)(((pos) >> LNET_PROC_VPOS_BITS) & LNET_PROC_CPT_MASK)
+
+#define LNET_PROC_VER_GET(pos) \
+ (int)(((pos) >> LNET_PROC_HPOS_BITS) & LNET_PROC_VER_MASK)
+
+#define LNET_PROC_HASH_GET(pos) \
+ (int)(((pos) >> LNET_PROC_HOFF_BITS) & LNET_PROC_HASH_MASK)
+
+#define LNET_PROC_HOFF_GET(pos) \
+ (int)((pos) & LNET_PROC_HOFF_MASK)
+
+#define LNET_PROC_POS_MAKE(cpt, ver, hash, off) \
+ (((((loff_t)(cpt)) & LNET_PROC_CPT_MASK) << LNET_PROC_VPOS_BITS) | \
+ ((((loff_t)(ver)) & LNET_PROC_VER_MASK) << LNET_PROC_HPOS_BITS) | \
+ ((((loff_t)(hash)) & LNET_PROC_HASH_MASK) << LNET_PROC_HOFF_BITS) | \
+ ((off) & LNET_PROC_HOFF_MASK))
+
+#define LNET_PROC_VERSION(v) ((unsigned int)((v) & LNET_PROC_VER_MASK))
+
+static int proc_call_handler(void *data, int write, loff_t *ppos,
+ void __user *buffer, size_t *lenp,
+ int (*handler)(void *data, int write,
+ loff_t pos, void __user *buffer, int len))
+{
+ int rc = handler(data, write, *ppos, buffer, *lenp);
+
+ if (rc < 0)
+ return rc;
+
+ if (write) {
+ *ppos += *lenp;
+ } else {
+ *lenp = rc;
+ *ppos += rc;
+ }
+ return 0;
+}
+
+static int __proc_lnet_stats(void *data, int write,
+ loff_t pos, void __user *buffer, int nob)
+{
+ int rc;
+ lnet_counters_t *ctrs;
+ int len;
+ char *tmpstr;
+ const int tmpsiz = 256; /* 7 %u and 4 %llu */
+
+ if (write) {
+ lnet_counters_reset();
+ return 0;
+ }
+
+ /* read */
+
+ LIBCFS_ALLOC(ctrs, sizeof(*ctrs));
+ if (ctrs == NULL)
+ return -ENOMEM;
+
+ LIBCFS_ALLOC(tmpstr, tmpsiz);
+ if (tmpstr == NULL) {
+ LIBCFS_FREE(ctrs, sizeof(*ctrs));
+ return -ENOMEM;
+ }
+
+ lnet_counters_get(ctrs);
+
+ len = snprintf(tmpstr, tmpsiz,
+ "%u %u %u %u %u %u %u %llu %llu %llu %llu",
+ ctrs->msgs_alloc, ctrs->msgs_max,
+ ctrs->errors,
+ ctrs->send_count, ctrs->recv_count,
+ ctrs->route_count, ctrs->drop_count,
+ ctrs->send_length, ctrs->recv_length,
+ ctrs->route_length, ctrs->drop_length);
+
+ if (pos >= min_t(int, len, strlen(tmpstr)))
+ rc = 0;
+ else
+ rc = cfs_trace_copyout_string(buffer, nob,
+ tmpstr + pos, "\n");
+
+ LIBCFS_FREE(tmpstr, tmpsiz);
+ LIBCFS_FREE(ctrs, sizeof(*ctrs));
+ return rc;
+}
+
+static int proc_lnet_stats(struct ctl_table *table, int write,
+ void __user *buffer, size_t *lenp, loff_t *ppos)
+{
+ return proc_call_handler(table->data, write, ppos, buffer, lenp,
+ __proc_lnet_stats);
+}
+
+static int proc_lnet_routes(struct ctl_table *table, int write,
+ void __user *buffer, size_t *lenp, loff_t *ppos)
+{
+ const int tmpsiz = 256;
+ char *tmpstr;
+ char *s;
+ int rc = 0;
+ int len;
+ int ver;
+ int off;
+
+ CLASSERT(sizeof(loff_t) >= 4);
+
+ off = LNET_PROC_HOFF_GET(*ppos);
+ ver = LNET_PROC_VER_GET(*ppos);
+
+ LASSERT(!write);
+
+ if (*lenp == 0)
+ return 0;
+
+ LIBCFS_ALLOC(tmpstr, tmpsiz);
+ if (tmpstr == NULL)
+ return -ENOMEM;
+
+ s = tmpstr; /* points to current position in tmpstr[] */
+
+ if (*ppos == 0) {
+ s += snprintf(s, tmpstr + tmpsiz - s, "Routing %s\n",
+ the_lnet.ln_routing ? "enabled" : "disabled");
+ LASSERT(tmpstr + tmpsiz - s > 0);
+
+ s += snprintf(s, tmpstr + tmpsiz - s, "%-8s %4s %8s %7s %s\n",
+ "net", "hops", "priority", "state", "router");
+ LASSERT(tmpstr + tmpsiz - s > 0);
+
+ lnet_net_lock(0);
+ ver = (unsigned int)the_lnet.ln_remote_nets_version;
+ lnet_net_unlock(0);
+ *ppos = LNET_PROC_POS_MAKE(0, ver, 0, off);
+ } else {
+ struct list_head *n;
+ struct list_head *r;
+ lnet_route_t *route = NULL;
+ lnet_remotenet_t *rnet = NULL;
+ int skip = off - 1;
+ struct list_head *rn_list;
+ int i;
+
+ lnet_net_lock(0);
+
+ if (ver != LNET_PROC_VERSION(the_lnet.ln_remote_nets_version)) {
+ lnet_net_unlock(0);
+ LIBCFS_FREE(tmpstr, tmpsiz);
+ return -ESTALE;
+ }
+
+ for (i = 0; i < LNET_REMOTE_NETS_HASH_SIZE && route == NULL;
+ i++) {
+ rn_list = &the_lnet.ln_remote_nets_hash[i];
+
+ n = rn_list->next;
+
+ while (n != rn_list && route == NULL) {
+ rnet = list_entry(n, lnet_remotenet_t,
+ lrn_list);
+
+ r = rnet->lrn_routes.next;
+
+ while (r != &rnet->lrn_routes) {
+ lnet_route_t *re =
+ list_entry(r, lnet_route_t,
+ lr_list);
+ if (skip == 0) {
+ route = re;
+ break;
+ }
+
+ skip--;
+ r = r->next;
+ }
+
+ n = n->next;
+ }
+ }
+
+ if (route != NULL) {
+ __u32 net = rnet->lrn_net;
+ unsigned int hops = route->lr_hops;
+ unsigned int priority = route->lr_priority;
+ lnet_nid_t nid = route->lr_gateway->lp_nid;
+ int alive = route->lr_gateway->lp_alive;
+
+ s += snprintf(s, tmpstr + tmpsiz - s,
+ "%-8s %4u %8u %7s %s\n",
+ libcfs_net2str(net), hops,
+ priority,
+ alive ? "up" : "down",
+ libcfs_nid2str(nid));
+ LASSERT(tmpstr + tmpsiz - s > 0);
+ }
+
+ lnet_net_unlock(0);
+ }
+
+ len = s - tmpstr; /* how many bytes was written */
+
+ if (len > *lenp) { /* linux-supplied buffer is too small */
+ rc = -EINVAL;
+ } else if (len > 0) { /* wrote something */
+ if (copy_to_user(buffer, tmpstr, len))
+ rc = -EFAULT;
+ else {
+ off += 1;
+ *ppos = LNET_PROC_POS_MAKE(0, ver, 0, off);
+ }
+ }
+
+ LIBCFS_FREE(tmpstr, tmpsiz);
+
+ if (rc == 0)
+ *lenp = len;
+
+ return rc;
+}
+
+static int proc_lnet_routers(struct ctl_table *table, int write,
+ void __user *buffer, size_t *lenp, loff_t *ppos)
+{
+ int rc = 0;
+ char *tmpstr;
+ char *s;
+ const int tmpsiz = 256;
+ int len;
+ int ver;
+ int off;
+
+ off = LNET_PROC_HOFF_GET(*ppos);
+ ver = LNET_PROC_VER_GET(*ppos);
+
+ LASSERT(!write);
+
+ if (*lenp == 0)
+ return 0;
+
+ LIBCFS_ALLOC(tmpstr, tmpsiz);
+ if (tmpstr == NULL)
+ return -ENOMEM;
+
+ s = tmpstr; /* points to current position in tmpstr[] */
+
+ if (*ppos == 0) {
+ s += snprintf(s, tmpstr + tmpsiz - s,
+ "%-4s %7s %9s %6s %12s %9s %8s %7s %s\n",
+ "ref", "rtr_ref", "alive_cnt", "state",
+ "last_ping", "ping_sent", "deadline",
+ "down_ni", "router");
+ LASSERT(tmpstr + tmpsiz - s > 0);
+
+ lnet_net_lock(0);
+ ver = (unsigned int)the_lnet.ln_routers_version;
+ lnet_net_unlock(0);
+ *ppos = LNET_PROC_POS_MAKE(0, ver, 0, off);
+ } else {
+ struct list_head *r;
+ struct lnet_peer *peer = NULL;
+ int skip = off - 1;
+
+ lnet_net_lock(0);
+
+ if (ver != LNET_PROC_VERSION(the_lnet.ln_routers_version)) {
+ lnet_net_unlock(0);
+
+ LIBCFS_FREE(tmpstr, tmpsiz);
+ return -ESTALE;
+ }
+
+ r = the_lnet.ln_routers.next;
+
+ while (r != &the_lnet.ln_routers) {
+ lnet_peer_t *lp = list_entry(r, lnet_peer_t,
+ lp_rtr_list);
+
+ if (skip == 0) {
+ peer = lp;
+ break;
+ }
+
+ skip--;
+ r = r->next;
+ }
+
+ if (peer != NULL) {
+ lnet_nid_t nid = peer->lp_nid;
+ unsigned long now = cfs_time_current();
+ unsigned long deadline = peer->lp_ping_deadline;
+ int nrefs = peer->lp_refcount;
+ int nrtrrefs = peer->lp_rtr_refcount;
+ int alive_cnt = peer->lp_alive_count;
+ int alive = peer->lp_alive;
+ int pingsent = !peer->lp_ping_notsent;
+ int last_ping = cfs_duration_sec(cfs_time_sub(now,
+ peer->lp_ping_timestamp));
+ int down_ni = 0;
+ lnet_route_t *rtr;
+
+ if ((peer->lp_ping_feats &
+ LNET_PING_FEAT_NI_STATUS) != 0) {
+ list_for_each_entry(rtr, &peer->lp_routes,
+ lr_gwlist) {
+ /* downis on any route should be the
+ * number of downis on the gateway */
+ if (rtr->lr_downis != 0) {
+ down_ni = rtr->lr_downis;
+ break;
+ }
+ }
+ }
+
+ if (deadline == 0)
+ s += snprintf(s, tmpstr + tmpsiz - s,
+ "%-4d %7d %9d %6s %12d %9d %8s %7d %s\n",
+ nrefs, nrtrrefs, alive_cnt,
+ alive ? "up" : "down", last_ping,
+ pingsent, "NA", down_ni,
+ libcfs_nid2str(nid));
+ else
+ s += snprintf(s, tmpstr + tmpsiz - s,
+ "%-4d %7d %9d %6s %12d %9d %8lu %7d %s\n",
+ nrefs, nrtrrefs, alive_cnt,
+ alive ? "up" : "down", last_ping,
+ pingsent,
+ cfs_duration_sec(cfs_time_sub(deadline, now)),
+ down_ni, libcfs_nid2str(nid));
+ LASSERT(tmpstr + tmpsiz - s > 0);
+ }
+
+ lnet_net_unlock(0);
+ }
+
+ len = s - tmpstr; /* how many bytes was written */
+
+ if (len > *lenp) { /* linux-supplied buffer is too small */
+ rc = -EINVAL;
+ } else if (len > 0) { /* wrote something */
+ if (copy_to_user(buffer, tmpstr, len))
+ rc = -EFAULT;
+ else {
+ off += 1;
+ *ppos = LNET_PROC_POS_MAKE(0, ver, 0, off);
+ }
+ }
+
+ LIBCFS_FREE(tmpstr, tmpsiz);
+
+ if (rc == 0)
+ *lenp = len;
+
+ return rc;
+}
+
+static int proc_lnet_peers(struct ctl_table *table, int write,
+ void __user *buffer, size_t *lenp, loff_t *ppos)
+{
+ const int tmpsiz = 256;
+ struct lnet_peer_table *ptable;
+ char *tmpstr;
+ char *s;
+ int cpt = LNET_PROC_CPT_GET(*ppos);
+ int ver = LNET_PROC_VER_GET(*ppos);
+ int hash = LNET_PROC_HASH_GET(*ppos);
+ int hoff = LNET_PROC_HOFF_GET(*ppos);
+ int rc = 0;
+ int len;
+
+ CLASSERT(LNET_PROC_HASH_BITS >= LNET_PEER_HASH_BITS);
+ LASSERT(!write);
+
+ if (*lenp == 0)
+ return 0;
+
+ if (cpt >= LNET_CPT_NUMBER) {
+ *lenp = 0;
+ return 0;
+ }
+
+ LIBCFS_ALLOC(tmpstr, tmpsiz);
+ if (tmpstr == NULL)
+ return -ENOMEM;
+
+ s = tmpstr; /* points to current position in tmpstr[] */
+
+ if (*ppos == 0) {
+ s += snprintf(s, tmpstr + tmpsiz - s,
+ "%-24s %4s %5s %5s %5s %5s %5s %5s %5s %s\n",
+ "nid", "refs", "state", "last", "max",
+ "rtr", "min", "tx", "min", "queue");
+ LASSERT(tmpstr + tmpsiz - s > 0);
+
+ hoff++;
+ } else {
+ struct lnet_peer *peer;
+ struct list_head *p;
+ int skip;
+ again:
+ p = NULL;
+ peer = NULL;
+ skip = hoff - 1;
+
+ lnet_net_lock(cpt);
+ ptable = the_lnet.ln_peer_tables[cpt];
+ if (hoff == 1)
+ ver = LNET_PROC_VERSION(ptable->pt_version);
+
+ if (ver != LNET_PROC_VERSION(ptable->pt_version)) {
+ lnet_net_unlock(cpt);
+ LIBCFS_FREE(tmpstr, tmpsiz);
+ return -ESTALE;
+ }
+
+ while (hash < LNET_PEER_HASH_SIZE) {
+ if (p == NULL)
+ p = ptable->pt_hash[hash].next;
+
+ while (p != &ptable->pt_hash[hash]) {
+ lnet_peer_t *lp = list_entry(p, lnet_peer_t,
+ lp_hashlist);
+ if (skip == 0) {
+ peer = lp;
+
+ /* minor optimization: start from idx+1
+ * on next iteration if we've just
+ * drained lp_hashlist */
+ if (lp->lp_hashlist.next ==
+ &ptable->pt_hash[hash]) {
+ hoff = 1;
+ hash++;
+ } else {
+ hoff++;
+ }
+
+ break;
+ }
+
+ skip--;
+ p = lp->lp_hashlist.next;
+ }
+
+ if (peer != NULL)
+ break;
+
+ p = NULL;
+ hoff = 1;
+ hash++;
+ }
+
+ if (peer != NULL) {
+ lnet_nid_t nid = peer->lp_nid;
+ int nrefs = peer->lp_refcount;
+ int lastalive = -1;
+ char *aliveness = "NA";
+ int maxcr = peer->lp_ni->ni_peertxcredits;
+ int txcr = peer->lp_txcredits;
+ int mintxcr = peer->lp_mintxcredits;
+ int rtrcr = peer->lp_rtrcredits;
+ int minrtrcr = peer->lp_minrtrcredits;
+ int txqnob = peer->lp_txqnob;
+
+ if (lnet_isrouter(peer) ||
+ lnet_peer_aliveness_enabled(peer))
+ aliveness = peer->lp_alive ? "up" : "down";
+
+ if (lnet_peer_aliveness_enabled(peer)) {
+ unsigned long now = cfs_time_current();
+ long delta;
+
+ delta = cfs_time_sub(now, peer->lp_last_alive);
+ lastalive = cfs_duration_sec(delta);
+
+ /* No need to mess up peers contents with
+ * arbitrarily long integers - it suffices to
+ * know that lastalive is more than 10000s old
+ */
+ if (lastalive >= 10000)
+ lastalive = 9999;
+ }
+
+ lnet_net_unlock(cpt);
+
+ s += snprintf(s, tmpstr + tmpsiz - s,
+ "%-24s %4d %5s %5d %5d %5d %5d %5d %5d %d\n",
+ libcfs_nid2str(nid), nrefs, aliveness,
+ lastalive, maxcr, rtrcr, minrtrcr, txcr,
+ mintxcr, txqnob);
+ LASSERT(tmpstr + tmpsiz - s > 0);
+
+ } else { /* peer is NULL */
+ lnet_net_unlock(cpt);
+ }
+
+ if (hash == LNET_PEER_HASH_SIZE) {
+ cpt++;
+ hash = 0;
+ hoff = 1;
+ if (peer == NULL && cpt < LNET_CPT_NUMBER)
+ goto again;
+ }
+ }
+
+ len = s - tmpstr; /* how many bytes was written */
+
+ if (len > *lenp) { /* linux-supplied buffer is too small */
+ rc = -EINVAL;
+ } else if (len > 0) { /* wrote something */
+ if (copy_to_user(buffer, tmpstr, len))
+ rc = -EFAULT;
+ else
+ *ppos = LNET_PROC_POS_MAKE(cpt, ver, hash, hoff);
+ }
+
+ LIBCFS_FREE(tmpstr, tmpsiz);
+
+ if (rc == 0)
+ *lenp = len;
+
+ return rc;
+}
+
+static int __proc_lnet_buffers(void *data, int write,
+ loff_t pos, void __user *buffer, int nob)
+{
+ char *s;
+ char *tmpstr;
+ int tmpsiz;
+ int idx;
+ int len;
+ int rc;
+ int i;
+
+ LASSERT(!write);
+
+ /* (4 %d) * 4 * LNET_CPT_NUMBER */
+ tmpsiz = 64 * (LNET_NRBPOOLS + 1) * LNET_CPT_NUMBER;
+ LIBCFS_ALLOC(tmpstr, tmpsiz);
+ if (tmpstr == NULL)
+ return -ENOMEM;
+
+ s = tmpstr; /* points to current position in tmpstr[] */
+
+ s += snprintf(s, tmpstr + tmpsiz - s,
+ "%5s %5s %7s %7s\n",
+ "pages", "count", "credits", "min");
+ LASSERT(tmpstr + tmpsiz - s > 0);
+
+ if (the_lnet.ln_rtrpools == NULL)
+ goto out; /* I'm not a router */
+
+ for (idx = 0; idx < LNET_NRBPOOLS; idx++) {
+ lnet_rtrbufpool_t *rbp;
+
+ lnet_net_lock(LNET_LOCK_EX);
+ cfs_percpt_for_each(rbp, i, the_lnet.ln_rtrpools) {
+ s += snprintf(s, tmpstr + tmpsiz - s,
+ "%5d %5d %7d %7d\n",
+ rbp[idx].rbp_npages,
+ rbp[idx].rbp_nbuffers,
+ rbp[idx].rbp_credits,
+ rbp[idx].rbp_mincredits);
+ LASSERT(tmpstr + tmpsiz - s > 0);
+ }
+ lnet_net_unlock(LNET_LOCK_EX);
+ }
+
+ out:
+ len = s - tmpstr;
+
+ if (pos >= min_t(int, len, strlen(tmpstr)))
+ rc = 0;
+ else
+ rc = cfs_trace_copyout_string(buffer, nob,
+ tmpstr + pos, NULL);
+
+ LIBCFS_FREE(tmpstr, tmpsiz);
+ return rc;
+}
+
+static int proc_lnet_buffers(struct ctl_table *table, int write,
+ void __user *buffer, size_t *lenp, loff_t *ppos)
+{
+ return proc_call_handler(table->data, write, ppos, buffer, lenp,
+ __proc_lnet_buffers);
+}
+
+static int proc_lnet_nis(struct ctl_table *table, int write,
+ void __user *buffer, size_t *lenp, loff_t *ppos)
+{
+ int tmpsiz = 128 * LNET_CPT_NUMBER;
+ int rc = 0;
+ char *tmpstr;
+ char *s;
+ int len;
+
+ LASSERT(!write);
+
+ if (*lenp == 0)
+ return 0;
+
+ LIBCFS_ALLOC(tmpstr, tmpsiz);
+ if (tmpstr == NULL)
+ return -ENOMEM;
+
+ s = tmpstr; /* points to current position in tmpstr[] */
+
+ if (*ppos == 0) {
+ s += snprintf(s, tmpstr + tmpsiz - s,
+ "%-24s %6s %5s %4s %4s %4s %5s %5s %5s\n",
+ "nid", "status", "alive", "refs", "peer",
+ "rtr", "max", "tx", "min");
+ LASSERT(tmpstr + tmpsiz - s > 0);
+ } else {
+ struct list_head *n;
+ lnet_ni_t *ni = NULL;
+ int skip = *ppos - 1;
+
+ lnet_net_lock(0);
+
+ n = the_lnet.ln_nis.next;
+
+ while (n != &the_lnet.ln_nis) {
+ lnet_ni_t *a_ni = list_entry(n, lnet_ni_t, ni_list);
+
+ if (skip == 0) {
+ ni = a_ni;
+ break;
+ }
+
+ skip--;
+ n = n->next;
+ }
+
+ if (ni != NULL) {
+ struct lnet_tx_queue *tq;
+ char *stat;
+ long now = get_seconds();
+ int last_alive = -1;
+ int i;
+ int j;
+
+ if (the_lnet.ln_routing)
+ last_alive = now - ni->ni_last_alive;
+
+ /* @lo forever alive */
+ if (ni->ni_lnd->lnd_type == LOLND)
+ last_alive = 0;
+
+ lnet_ni_lock(ni);
+ LASSERT(ni->ni_status != NULL);
+ stat = (ni->ni_status->ns_status ==
+ LNET_NI_STATUS_UP) ? "up" : "down";
+ lnet_ni_unlock(ni);
+
+ /* we actually output credits information for
+ * TX queue of each partition */
+ cfs_percpt_for_each(tq, i, ni->ni_tx_queues) {
+ for (j = 0; ni->ni_cpts != NULL &&
+ j < ni->ni_ncpts; j++) {
+ if (i == ni->ni_cpts[j])
+ break;
+ }
+
+ if (j == ni->ni_ncpts)
+ continue;
+
+ if (i != 0)
+ lnet_net_lock(i);
+
+ s += snprintf(s, tmpstr + tmpsiz - s,
+ "%-24s %6s %5d %4d %4d %4d %5d %5d %5d\n",
+ libcfs_nid2str(ni->ni_nid), stat,
+ last_alive, *ni->ni_refs[i],
+ ni->ni_peertxcredits,
+ ni->ni_peerrtrcredits,
+ tq->tq_credits_max,
+ tq->tq_credits, tq->tq_credits_min);
+ if (i != 0)
+ lnet_net_unlock(i);
+ }
+ LASSERT(tmpstr + tmpsiz - s > 0);
+ }
+
+ lnet_net_unlock(0);
+ }
+
+ len = s - tmpstr; /* how many bytes was written */
+
+ if (len > *lenp) { /* linux-supplied buffer is too small */
+ rc = -EINVAL;
+ } else if (len > 0) { /* wrote something */
+ if (copy_to_user(buffer, tmpstr, len))
+ rc = -EFAULT;
+ else
+ *ppos += 1;
+ }
+
+ LIBCFS_FREE(tmpstr, tmpsiz);
+
+ if (rc == 0)
+ *lenp = len;
+
+ return rc;
+}
+
+struct lnet_portal_rotors {
+ int pr_value;
+ const char *pr_name;
+ const char *pr_desc;
+};
+
+static struct lnet_portal_rotors portal_rotors[] = {
+ {
+ .pr_value = LNET_PTL_ROTOR_OFF,
+ .pr_name = "OFF",
+ .pr_desc = "Turn off message rotor for wildcard portals"
+ },
+ {
+ .pr_value = LNET_PTL_ROTOR_ON,
+ .pr_name = "ON",
+ .pr_desc = "round-robin dispatch all PUT messages for wildcard portals"
+ },
+ {
+ .pr_value = LNET_PTL_ROTOR_RR_RT,
+ .pr_name = "RR_RT",
+ .pr_desc = "round-robin dispatch routed PUT message for wildcard portals"
+ },
+ {
+ .pr_value = LNET_PTL_ROTOR_HASH_RT,
+ .pr_name = "HASH_RT",
+ .pr_desc = "dispatch routed PUT message by hashing source NID for wildcard portals"
+ },
+ {
+ .pr_value = -1,
+ .pr_name = NULL,
+ .pr_desc = NULL
+ },
+};
+
+extern int portal_rotor;
+
+static int __proc_lnet_portal_rotor(void *data, int write,
+ loff_t pos, void __user *buffer, int nob)
+{
+ const int buf_len = 128;
+ char *buf;
+ char *tmp;
+ int rc;
+ int i;
+
+ LIBCFS_ALLOC(buf, buf_len);
+ if (buf == NULL)
+ return -ENOMEM;
+
+ if (!write) {
+ lnet_res_lock(0);
+
+ for (i = 0; portal_rotors[i].pr_value >= 0; i++) {
+ if (portal_rotors[i].pr_value == portal_rotor)
+ break;
+ }
+
+ LASSERT(portal_rotors[i].pr_value == portal_rotor);
+ lnet_res_unlock(0);
+
+ rc = snprintf(buf, buf_len,
+ "{\n\tportals: all\n"
+ "\trotor: %s\n\tdescription: %s\n}",
+ portal_rotors[i].pr_name,
+ portal_rotors[i].pr_desc);
+
+ if (pos >= min_t(int, rc, buf_len)) {
+ rc = 0;
+ } else {
+ rc = cfs_trace_copyout_string(buffer, nob,
+ buf + pos, "\n");
+ }
+ goto out;
+ }
+
+ rc = cfs_trace_copyin_string(buf, buf_len, buffer, nob);
+ if (rc < 0)
+ goto out;
+
+ tmp = cfs_trimwhite(buf);
+
+ rc = -EINVAL;
+ lnet_res_lock(0);
+ for (i = 0; portal_rotors[i].pr_name != NULL; i++) {
+ if (strncasecmp(portal_rotors[i].pr_name, tmp,
+ strlen(portal_rotors[i].pr_name)) == 0) {
+ portal_rotor = portal_rotors[i].pr_value;
+ rc = 0;
+ break;
+ }
+ }
+ lnet_res_unlock(0);
+out:
+ LIBCFS_FREE(buf, buf_len);
+ return rc;
+}
+
+static int proc_lnet_portal_rotor(struct ctl_table *table, int write,
+ void __user *buffer, size_t *lenp,
+ loff_t *ppos)
+{
+ return proc_call_handler(table->data, write, ppos, buffer, lenp,
+ __proc_lnet_portal_rotor);
+}
+
+static struct ctl_table lnet_table[] = {
+ /*
+ * NB No .strategy entries have been provided since sysctl(8) prefers
+ * to go via /proc for portability.
+ */
+ {
+ .procname = "stats",
+ .mode = 0644,
+ .proc_handler = &proc_lnet_stats,
+ },
+ {
+ .procname = "routes",
+ .mode = 0444,
+ .proc_handler = &proc_lnet_routes,
+ },
+ {
+ .procname = "routers",
+ .mode = 0444,
+ .proc_handler = &proc_lnet_routers,
+ },
+ {
+ .procname = "peers",
+ .mode = 0444,
+ .proc_handler = &proc_lnet_peers,
+ },
+ {
+ .procname = "buffers",
+ .mode = 0444,
+ .proc_handler = &proc_lnet_buffers,
+ },
+ {
+ .procname = "nis",
+ .mode = 0444,
+ .proc_handler = &proc_lnet_nis,
+ },
+ {
+ .procname = "portal_rotor",
+ .mode = 0644,
+ .proc_handler = &proc_lnet_portal_rotor,
+ },
+ {
+ }
+};
+
+static struct ctl_table top_table[] = {
+ {
+ .procname = "lnet",
+ .mode = 0555,
+ .data = NULL,
+ .maxlen = 0,
+ .child = lnet_table,
+ },
+ {
+ }
+};
+
+void
+lnet_proc_init(void)
+{
+ if (lnet_table_header == NULL)
+ lnet_table_header = register_sysctl_table(top_table);
+}
+
+void
+lnet_proc_fini(void)
+{
+ if (lnet_table_header != NULL)
+ unregister_sysctl_table(lnet_table_header);
+
+ lnet_table_header = NULL;
+}
+
+#else
+
+void
+lnet_proc_init(void)
+{
+}
+
+void
+lnet_proc_fini(void)
+{
+}
+
+#endif
diff --git a/drivers/staging/lustre/lnet/selftest/Makefile b/drivers/staging/lustre/lnet/selftest/Makefile
new file mode 100644
index 000000000..c0de6e2d9
--- /dev/null
+++ b/drivers/staging/lustre/lnet/selftest/Makefile
@@ -0,0 +1,4 @@
+obj-$(CONFIG_LNET_SELFTEST) := lnet_selftest.o
+
+lnet_selftest-y := console.o conrpc.o conctl.o framework.o timer.o rpc.o \
+ module.o ping_test.o brw_test.o
diff --git a/drivers/staging/lustre/lnet/selftest/brw_test.c b/drivers/staging/lustre/lnet/selftest/brw_test.c
new file mode 100644
index 000000000..658f4584f
--- /dev/null
+++ b/drivers/staging/lustre/lnet/selftest/brw_test.c
@@ -0,0 +1,508 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2011, 2012, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ *
+ * lnet/selftest/brw_test.c
+ *
+ * Author: Isaac Huang <isaac@clusterfs.com>
+ */
+
+#include "selftest.h"
+
+static int brw_srv_workitems = SFW_TEST_WI_MAX;
+module_param(brw_srv_workitems, int, 0644);
+MODULE_PARM_DESC(brw_srv_workitems, "# BRW server workitems");
+
+static int brw_inject_errors;
+module_param(brw_inject_errors, int, 0644);
+MODULE_PARM_DESC(brw_inject_errors, "# data errors to inject randomly, zero by default");
+
+static void
+brw_client_fini(sfw_test_instance_t *tsi)
+{
+ srpc_bulk_t *bulk;
+ sfw_test_unit_t *tsu;
+
+ LASSERT(tsi->tsi_is_client);
+
+ list_for_each_entry(tsu, &tsi->tsi_units, tsu_list) {
+ bulk = tsu->tsu_private;
+ if (bulk == NULL)
+ continue;
+
+ srpc_free_bulk(bulk);
+ tsu->tsu_private = NULL;
+ }
+}
+
+static int
+brw_client_init(sfw_test_instance_t *tsi)
+{
+ sfw_session_t *sn = tsi->tsi_batch->bat_session;
+ int flags;
+ int npg;
+ int len;
+ int opc;
+ srpc_bulk_t *bulk;
+ sfw_test_unit_t *tsu;
+
+ LASSERT(sn != NULL);
+ LASSERT(tsi->tsi_is_client);
+
+ if ((sn->sn_features & LST_FEAT_BULK_LEN) == 0) {
+ test_bulk_req_t *breq = &tsi->tsi_u.bulk_v0;
+
+ opc = breq->blk_opc;
+ flags = breq->blk_flags;
+ npg = breq->blk_npg;
+ /* NB: this is not going to work for variable page size,
+ * but we have to keep it for compatibility */
+ len = npg * PAGE_CACHE_SIZE;
+
+ } else {
+ test_bulk_req_v1_t *breq = &tsi->tsi_u.bulk_v1;
+
+ /* I should never get this step if it's unknown feature
+ * because make_session will reject unknown feature */
+ LASSERT((sn->sn_features & ~LST_FEATS_MASK) == 0);
+
+ opc = breq->blk_opc;
+ flags = breq->blk_flags;
+ len = breq->blk_len;
+ npg = (len + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT;
+ }
+
+ if (npg > LNET_MAX_IOV || npg <= 0)
+ return -EINVAL;
+
+ if (opc != LST_BRW_READ && opc != LST_BRW_WRITE)
+ return -EINVAL;
+
+ if (flags != LST_BRW_CHECK_NONE &&
+ flags != LST_BRW_CHECK_FULL && flags != LST_BRW_CHECK_SIMPLE)
+ return -EINVAL;
+
+ list_for_each_entry(tsu, &tsi->tsi_units, tsu_list) {
+ bulk = srpc_alloc_bulk(lnet_cpt_of_nid(tsu->tsu_dest.nid),
+ npg, len, opc == LST_BRW_READ);
+ if (bulk == NULL) {
+ brw_client_fini(tsi);
+ return -ENOMEM;
+ }
+
+ tsu->tsu_private = bulk;
+ }
+
+ return 0;
+}
+
+#define BRW_POISON 0xbeefbeefbeefbeefULL
+#define BRW_MAGIC 0xeeb0eeb1eeb2eeb3ULL
+#define BRW_MSIZE sizeof(__u64)
+
+static int
+brw_inject_one_error(void)
+{
+ struct timeval tv;
+
+ if (brw_inject_errors <= 0)
+ return 0;
+
+ do_gettimeofday(&tv);
+
+ if ((tv.tv_usec & 1) == 0)
+ return 0;
+
+ return brw_inject_errors--;
+}
+
+static void
+brw_fill_page(struct page *pg, int pattern, __u64 magic)
+{
+ char *addr = page_address(pg);
+ int i;
+
+ LASSERT(addr != NULL);
+
+ if (pattern == LST_BRW_CHECK_NONE)
+ return;
+
+ if (magic == BRW_MAGIC)
+ magic += brw_inject_one_error();
+
+ if (pattern == LST_BRW_CHECK_SIMPLE) {
+ memcpy(addr, &magic, BRW_MSIZE);
+ addr += PAGE_CACHE_SIZE - BRW_MSIZE;
+ memcpy(addr, &magic, BRW_MSIZE);
+ return;
+ }
+
+ if (pattern == LST_BRW_CHECK_FULL) {
+ for (i = 0; i < PAGE_CACHE_SIZE / BRW_MSIZE; i++)
+ memcpy(addr + i * BRW_MSIZE, &magic, BRW_MSIZE);
+ return;
+ }
+
+ LBUG();
+}
+
+static int
+brw_check_page(struct page *pg, int pattern, __u64 magic)
+{
+ char *addr = page_address(pg);
+ __u64 data = 0; /* make compiler happy */
+ int i;
+
+ LASSERT(addr != NULL);
+
+ if (pattern == LST_BRW_CHECK_NONE)
+ return 0;
+
+ if (pattern == LST_BRW_CHECK_SIMPLE) {
+ data = *((__u64 *) addr);
+ if (data != magic)
+ goto bad_data;
+
+ addr += PAGE_CACHE_SIZE - BRW_MSIZE;
+ data = *((__u64 *) addr);
+ if (data != magic)
+ goto bad_data;
+
+ return 0;
+ }
+
+ if (pattern == LST_BRW_CHECK_FULL) {
+ for (i = 0; i < PAGE_CACHE_SIZE / BRW_MSIZE; i++) {
+ data = *(((__u64 *) addr) + i);
+ if (data != magic)
+ goto bad_data;
+ }
+
+ return 0;
+ }
+
+ LBUG();
+
+bad_data:
+ CERROR("Bad data in page %p: %#llx, %#llx expected\n",
+ pg, data, magic);
+ return 1;
+}
+
+static void
+brw_fill_bulk(srpc_bulk_t *bk, int pattern, __u64 magic)
+{
+ int i;
+ struct page *pg;
+
+ for (i = 0; i < bk->bk_niov; i++) {
+ pg = bk->bk_iovs[i].kiov_page;
+ brw_fill_page(pg, pattern, magic);
+ }
+}
+
+static int
+brw_check_bulk(srpc_bulk_t *bk, int pattern, __u64 magic)
+{
+ int i;
+ struct page *pg;
+
+ for (i = 0; i < bk->bk_niov; i++) {
+ pg = bk->bk_iovs[i].kiov_page;
+ if (brw_check_page(pg, pattern, magic) != 0) {
+ CERROR("Bulk page %p (%d/%d) is corrupted!\n",
+ pg, i, bk->bk_niov);
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+static int
+brw_client_prep_rpc(sfw_test_unit_t *tsu,
+ lnet_process_id_t dest, srpc_client_rpc_t **rpcpp)
+{
+ srpc_bulk_t *bulk = tsu->tsu_private;
+ sfw_test_instance_t *tsi = tsu->tsu_instance;
+ sfw_session_t *sn = tsi->tsi_batch->bat_session;
+ srpc_client_rpc_t *rpc;
+ srpc_brw_reqst_t *req;
+ int flags;
+ int npg;
+ int len;
+ int opc;
+ int rc;
+
+ LASSERT(sn != NULL);
+ LASSERT(bulk != NULL);
+
+ if ((sn->sn_features & LST_FEAT_BULK_LEN) == 0) {
+ test_bulk_req_t *breq = &tsi->tsi_u.bulk_v0;
+
+ opc = breq->blk_opc;
+ flags = breq->blk_flags;
+ npg = breq->blk_npg;
+ len = npg * PAGE_CACHE_SIZE;
+
+ } else {
+ test_bulk_req_v1_t *breq = &tsi->tsi_u.bulk_v1;
+
+ /* I should never get this step if it's unknown feature
+ * because make_session will reject unknown feature */
+ LASSERT((sn->sn_features & ~LST_FEATS_MASK) == 0);
+
+ opc = breq->blk_opc;
+ flags = breq->blk_flags;
+ len = breq->blk_len;
+ npg = (len + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT;
+ }
+
+ rc = sfw_create_test_rpc(tsu, dest, sn->sn_features, npg, len, &rpc);
+ if (rc != 0)
+ return rc;
+
+ memcpy(&rpc->crpc_bulk, bulk, offsetof(srpc_bulk_t, bk_iovs[npg]));
+ if (opc == LST_BRW_WRITE)
+ brw_fill_bulk(&rpc->crpc_bulk, flags, BRW_MAGIC);
+ else
+ brw_fill_bulk(&rpc->crpc_bulk, flags, BRW_POISON);
+
+ req = &rpc->crpc_reqstmsg.msg_body.brw_reqst;
+ req->brw_flags = flags;
+ req->brw_rw = opc;
+ req->brw_len = len;
+
+ *rpcpp = rpc;
+ return 0;
+}
+
+static void
+brw_client_done_rpc(sfw_test_unit_t *tsu, srpc_client_rpc_t *rpc)
+{
+ __u64 magic = BRW_MAGIC;
+ sfw_test_instance_t *tsi = tsu->tsu_instance;
+ sfw_session_t *sn = tsi->tsi_batch->bat_session;
+ srpc_msg_t *msg = &rpc->crpc_replymsg;
+ srpc_brw_reply_t *reply = &msg->msg_body.brw_reply;
+ srpc_brw_reqst_t *reqst = &rpc->crpc_reqstmsg.msg_body.brw_reqst;
+
+ LASSERT(sn != NULL);
+
+ if (rpc->crpc_status != 0) {
+ CERROR("BRW RPC to %s failed with %d\n",
+ libcfs_id2str(rpc->crpc_dest), rpc->crpc_status);
+ if (!tsi->tsi_stopping) /* rpc could have been aborted */
+ atomic_inc(&sn->sn_brw_errors);
+ goto out;
+ }
+
+ if (msg->msg_magic != SRPC_MSG_MAGIC) {
+ __swab64s(&magic);
+ __swab32s(&reply->brw_status);
+ }
+
+ CDEBUG(reply->brw_status ? D_WARNING : D_NET,
+ "BRW RPC to %s finished with brw_status: %d\n",
+ libcfs_id2str(rpc->crpc_dest), reply->brw_status);
+
+ if (reply->brw_status != 0) {
+ atomic_inc(&sn->sn_brw_errors);
+ rpc->crpc_status = -(int)reply->brw_status;
+ goto out;
+ }
+
+ if (reqst->brw_rw == LST_BRW_WRITE)
+ goto out;
+
+ if (brw_check_bulk(&rpc->crpc_bulk, reqst->brw_flags, magic) != 0) {
+ CERROR("Bulk data from %s is corrupted!\n",
+ libcfs_id2str(rpc->crpc_dest));
+ atomic_inc(&sn->sn_brw_errors);
+ rpc->crpc_status = -EBADMSG;
+ }
+
+out:
+ return;
+}
+
+static void
+brw_server_rpc_done(srpc_server_rpc_t *rpc)
+{
+ srpc_bulk_t *blk = rpc->srpc_bulk;
+
+ if (blk == NULL)
+ return;
+
+ if (rpc->srpc_status != 0)
+ CERROR("Bulk transfer %s %s has failed: %d\n",
+ blk->bk_sink ? "from" : "to",
+ libcfs_id2str(rpc->srpc_peer), rpc->srpc_status);
+ else
+ CDEBUG(D_NET, "Transferred %d pages bulk data %s %s\n",
+ blk->bk_niov, blk->bk_sink ? "from" : "to",
+ libcfs_id2str(rpc->srpc_peer));
+
+ sfw_free_pages(rpc);
+}
+
+static int
+brw_bulk_ready(srpc_server_rpc_t *rpc, int status)
+{
+ __u64 magic = BRW_MAGIC;
+ srpc_brw_reply_t *reply = &rpc->srpc_replymsg.msg_body.brw_reply;
+ srpc_brw_reqst_t *reqst;
+ srpc_msg_t *reqstmsg;
+
+ LASSERT(rpc->srpc_bulk != NULL);
+ LASSERT(rpc->srpc_reqstbuf != NULL);
+
+ reqstmsg = &rpc->srpc_reqstbuf->buf_msg;
+ reqst = &reqstmsg->msg_body.brw_reqst;
+
+ if (status != 0) {
+ CERROR("BRW bulk %s failed for RPC from %s: %d\n",
+ reqst->brw_rw == LST_BRW_READ ? "READ" : "WRITE",
+ libcfs_id2str(rpc->srpc_peer), status);
+ return -EIO;
+ }
+
+ if (reqst->brw_rw == LST_BRW_READ)
+ return 0;
+
+ if (reqstmsg->msg_magic != SRPC_MSG_MAGIC)
+ __swab64s(&magic);
+
+ if (brw_check_bulk(rpc->srpc_bulk, reqst->brw_flags, magic) != 0) {
+ CERROR("Bulk data from %s is corrupted!\n",
+ libcfs_id2str(rpc->srpc_peer));
+ reply->brw_status = EBADMSG;
+ }
+
+ return 0;
+}
+
+static int
+brw_server_handle(struct srpc_server_rpc *rpc)
+{
+ struct srpc_service *sv = rpc->srpc_scd->scd_svc;
+ srpc_msg_t *replymsg = &rpc->srpc_replymsg;
+ srpc_msg_t *reqstmsg = &rpc->srpc_reqstbuf->buf_msg;
+ srpc_brw_reply_t *reply = &replymsg->msg_body.brw_reply;
+ srpc_brw_reqst_t *reqst = &reqstmsg->msg_body.brw_reqst;
+ int npg;
+ int rc;
+
+ LASSERT(sv->sv_id == SRPC_SERVICE_BRW);
+
+ if (reqstmsg->msg_magic != SRPC_MSG_MAGIC) {
+ LASSERT(reqstmsg->msg_magic == __swab32(SRPC_MSG_MAGIC));
+
+ __swab32s(&reqst->brw_rw);
+ __swab32s(&reqst->brw_len);
+ __swab32s(&reqst->brw_flags);
+ __swab64s(&reqst->brw_rpyid);
+ __swab64s(&reqst->brw_bulkid);
+ }
+ LASSERT(reqstmsg->msg_type == (__u32)srpc_service2request(sv->sv_id));
+
+ reply->brw_status = 0;
+ rpc->srpc_done = brw_server_rpc_done;
+
+ if ((reqst->brw_rw != LST_BRW_READ && reqst->brw_rw != LST_BRW_WRITE) ||
+ (reqst->brw_flags != LST_BRW_CHECK_NONE &&
+ reqst->brw_flags != LST_BRW_CHECK_FULL &&
+ reqst->brw_flags != LST_BRW_CHECK_SIMPLE)) {
+ reply->brw_status = EINVAL;
+ return 0;
+ }
+
+ if ((reqstmsg->msg_ses_feats & ~LST_FEATS_MASK) != 0) {
+ replymsg->msg_ses_feats = LST_FEATS_MASK;
+ reply->brw_status = EPROTO;
+ return 0;
+ }
+
+ if ((reqstmsg->msg_ses_feats & LST_FEAT_BULK_LEN) == 0) {
+ /* compat with old version */
+ if ((reqst->brw_len & ~CFS_PAGE_MASK) != 0) {
+ reply->brw_status = EINVAL;
+ return 0;
+ }
+ npg = reqst->brw_len >> PAGE_CACHE_SHIFT;
+
+ } else {
+ npg = (reqst->brw_len + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT;
+ }
+
+ replymsg->msg_ses_feats = reqstmsg->msg_ses_feats;
+
+ if (reqst->brw_len == 0 || npg > LNET_MAX_IOV) {
+ reply->brw_status = EINVAL;
+ return 0;
+ }
+
+ rc = sfw_alloc_pages(rpc, rpc->srpc_scd->scd_cpt, npg,
+ reqst->brw_len,
+ reqst->brw_rw == LST_BRW_WRITE);
+ if (rc != 0)
+ return rc;
+
+ if (reqst->brw_rw == LST_BRW_READ)
+ brw_fill_bulk(rpc->srpc_bulk, reqst->brw_flags, BRW_MAGIC);
+ else
+ brw_fill_bulk(rpc->srpc_bulk, reqst->brw_flags, BRW_POISON);
+
+ return 0;
+}
+
+sfw_test_client_ops_t brw_test_client;
+void brw_init_test_client(void)
+{
+ brw_test_client.tso_init = brw_client_init;
+ brw_test_client.tso_fini = brw_client_fini;
+ brw_test_client.tso_prep_rpc = brw_client_prep_rpc;
+ brw_test_client.tso_done_rpc = brw_client_done_rpc;
+};
+
+srpc_service_t brw_test_service;
+void brw_init_test_service(void)
+{
+
+ brw_test_service.sv_id = SRPC_SERVICE_BRW;
+ brw_test_service.sv_name = "brw_test";
+ brw_test_service.sv_handler = brw_server_handle;
+ brw_test_service.sv_bulk_ready = brw_bulk_ready;
+ brw_test_service.sv_wi_total = brw_srv_workitems;
+}
diff --git a/drivers/staging/lustre/lnet/selftest/conctl.c b/drivers/staging/lustre/lnet/selftest/conctl.c
new file mode 100644
index 000000000..045fe295a
--- /dev/null
+++ b/drivers/staging/lustre/lnet/selftest/conctl.c
@@ -0,0 +1,929 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2012, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ *
+ * lnet/selftest/conctl.c
+ *
+ * IOC handle in kernel
+ *
+ * Author: Liang Zhen <liangzhen@clusterfs.com>
+ */
+
+#include "../../include/linux/libcfs/libcfs.h"
+#include "../../include/linux/lnet/lib-lnet.h"
+#include "../../include/linux/lnet/lnetst.h"
+#include "console.h"
+
+static int
+lst_session_new_ioctl(lstio_session_new_args_t *args)
+{
+ char *name;
+ int rc;
+
+ if (args->lstio_ses_idp == NULL || /* address for output sid */
+ args->lstio_ses_key == 0 || /* no key is specified */
+ args->lstio_ses_namep == NULL || /* session name */
+ args->lstio_ses_nmlen <= 0 ||
+ args->lstio_ses_nmlen > LST_NAME_SIZE)
+ return -EINVAL;
+
+ LIBCFS_ALLOC(name, args->lstio_ses_nmlen + 1);
+ if (name == NULL)
+ return -ENOMEM;
+
+ if (copy_from_user(name,
+ args->lstio_ses_namep,
+ args->lstio_ses_nmlen)) {
+ LIBCFS_FREE(name, args->lstio_ses_nmlen + 1);
+ return -EFAULT;
+ }
+
+ name[args->lstio_ses_nmlen] = 0;
+
+ rc = lstcon_session_new(name,
+ args->lstio_ses_key,
+ args->lstio_ses_feats,
+ args->lstio_ses_force,
+ args->lstio_ses_timeout,
+ args->lstio_ses_idp);
+
+ LIBCFS_FREE(name, args->lstio_ses_nmlen + 1);
+ return rc;
+}
+
+static int
+lst_session_end_ioctl(lstio_session_end_args_t *args)
+{
+ if (args->lstio_ses_key != console_session.ses_key)
+ return -EACCES;
+
+ return lstcon_session_end();
+}
+
+static int
+lst_session_info_ioctl(lstio_session_info_args_t *args)
+{
+ /* no checking of key */
+
+ if (args->lstio_ses_idp == NULL || /* address for output sid */
+ args->lstio_ses_keyp == NULL || /* address for output key */
+ args->lstio_ses_featp == NULL || /* address for output features */
+ args->lstio_ses_ndinfo == NULL || /* address for output ndinfo */
+ args->lstio_ses_namep == NULL || /* address for output name */
+ args->lstio_ses_nmlen <= 0 ||
+ args->lstio_ses_nmlen > LST_NAME_SIZE)
+ return -EINVAL;
+
+ return lstcon_session_info(args->lstio_ses_idp,
+ args->lstio_ses_keyp,
+ args->lstio_ses_featp,
+ args->lstio_ses_ndinfo,
+ args->lstio_ses_namep,
+ args->lstio_ses_nmlen);
+}
+
+static int
+lst_debug_ioctl(lstio_debug_args_t *args)
+{
+ char *name = NULL;
+ int client = 1;
+ int rc;
+
+ if (args->lstio_dbg_key != console_session.ses_key)
+ return -EACCES;
+
+ if (args->lstio_dbg_resultp == NULL)
+ return -EINVAL;
+
+ if (args->lstio_dbg_namep != NULL && /* name of batch/group */
+ (args->lstio_dbg_nmlen <= 0 ||
+ args->lstio_dbg_nmlen > LST_NAME_SIZE))
+ return -EINVAL;
+
+ if (args->lstio_dbg_namep != NULL) {
+ LIBCFS_ALLOC(name, args->lstio_dbg_nmlen + 1);
+ if (name == NULL)
+ return -ENOMEM;
+
+ if (copy_from_user(name, args->lstio_dbg_namep,
+ args->lstio_dbg_nmlen)) {
+ LIBCFS_FREE(name, args->lstio_dbg_nmlen + 1);
+
+ return -EFAULT;
+ }
+
+ name[args->lstio_dbg_nmlen] = 0;
+ }
+
+ rc = -EINVAL;
+
+ switch (args->lstio_dbg_type) {
+ case LST_OPC_SESSION:
+ rc = lstcon_session_debug(args->lstio_dbg_timeout,
+ args->lstio_dbg_resultp);
+ break;
+
+ case LST_OPC_BATCHSRV:
+ client = 0;
+ case LST_OPC_BATCHCLI:
+ if (name == NULL)
+ goto out;
+
+ rc = lstcon_batch_debug(args->lstio_dbg_timeout,
+ name, client, args->lstio_dbg_resultp);
+ break;
+
+ case LST_OPC_GROUP:
+ if (name == NULL)
+ goto out;
+
+ rc = lstcon_group_debug(args->lstio_dbg_timeout,
+ name, args->lstio_dbg_resultp);
+ break;
+
+ case LST_OPC_NODES:
+ if (args->lstio_dbg_count <= 0 ||
+ args->lstio_dbg_idsp == NULL)
+ goto out;
+
+ rc = lstcon_nodes_debug(args->lstio_dbg_timeout,
+ args->lstio_dbg_count,
+ args->lstio_dbg_idsp,
+ args->lstio_dbg_resultp);
+ break;
+
+ default:
+ break;
+ }
+
+out:
+ if (name != NULL)
+ LIBCFS_FREE(name, args->lstio_dbg_nmlen + 1);
+
+ return rc;
+}
+
+static int
+lst_group_add_ioctl(lstio_group_add_args_t *args)
+{
+ char *name;
+ int rc;
+
+ if (args->lstio_grp_key != console_session.ses_key)
+ return -EACCES;
+
+ if (args->lstio_grp_namep == NULL ||
+ args->lstio_grp_nmlen <= 0 ||
+ args->lstio_grp_nmlen > LST_NAME_SIZE)
+ return -EINVAL;
+
+ LIBCFS_ALLOC(name, args->lstio_grp_nmlen + 1);
+ if (name == NULL)
+ return -ENOMEM;
+
+ if (copy_from_user(name,
+ args->lstio_grp_namep,
+ args->lstio_grp_nmlen)) {
+ LIBCFS_FREE(name, args->lstio_grp_nmlen);
+ return -EFAULT;
+ }
+
+ name[args->lstio_grp_nmlen] = 0;
+
+ rc = lstcon_group_add(name);
+
+ LIBCFS_FREE(name, args->lstio_grp_nmlen + 1);
+
+ return rc;
+}
+
+static int
+lst_group_del_ioctl(lstio_group_del_args_t *args)
+{
+ int rc;
+ char *name;
+
+ if (args->lstio_grp_key != console_session.ses_key)
+ return -EACCES;
+
+ if (args->lstio_grp_namep == NULL ||
+ args->lstio_grp_nmlen <= 0 ||
+ args->lstio_grp_nmlen > LST_NAME_SIZE)
+ return -EINVAL;
+
+ LIBCFS_ALLOC(name, args->lstio_grp_nmlen + 1);
+ if (name == NULL)
+ return -ENOMEM;
+
+ if (copy_from_user(name,
+ args->lstio_grp_namep,
+ args->lstio_grp_nmlen)) {
+ LIBCFS_FREE(name, args->lstio_grp_nmlen + 1);
+ return -EFAULT;
+ }
+
+ name[args->lstio_grp_nmlen] = 0;
+
+ rc = lstcon_group_del(name);
+
+ LIBCFS_FREE(name, args->lstio_grp_nmlen + 1);
+
+ return rc;
+}
+
+static int
+lst_group_update_ioctl(lstio_group_update_args_t *args)
+{
+ int rc;
+ char *name;
+
+ if (args->lstio_grp_key != console_session.ses_key)
+ return -EACCES;
+
+ if (args->lstio_grp_resultp == NULL ||
+ args->lstio_grp_namep == NULL ||
+ args->lstio_grp_nmlen <= 0 ||
+ args->lstio_grp_nmlen > LST_NAME_SIZE)
+ return -EINVAL;
+
+ LIBCFS_ALLOC(name, args->lstio_grp_nmlen + 1);
+ if (name == NULL)
+ return -ENOMEM;
+
+ if (copy_from_user(name,
+ args->lstio_grp_namep,
+ args->lstio_grp_nmlen)) {
+ LIBCFS_FREE(name, args->lstio_grp_nmlen + 1);
+ return -EFAULT;
+ }
+
+ name[args->lstio_grp_nmlen] = 0;
+
+ switch (args->lstio_grp_opc) {
+ case LST_GROUP_CLEAN:
+ rc = lstcon_group_clean(name, args->lstio_grp_args);
+ break;
+
+ case LST_GROUP_REFRESH:
+ rc = lstcon_group_refresh(name, args->lstio_grp_resultp);
+ break;
+
+ case LST_GROUP_RMND:
+ if (args->lstio_grp_count <= 0 ||
+ args->lstio_grp_idsp == NULL) {
+ rc = -EINVAL;
+ break;
+ }
+ rc = lstcon_nodes_remove(name, args->lstio_grp_count,
+ args->lstio_grp_idsp,
+ args->lstio_grp_resultp);
+ break;
+
+ default:
+ rc = -EINVAL;
+ break;
+ }
+
+ LIBCFS_FREE(name, args->lstio_grp_nmlen + 1);
+
+ return rc;
+}
+
+static int
+lst_nodes_add_ioctl(lstio_group_nodes_args_t *args)
+{
+ unsigned feats;
+ int rc;
+ char *name;
+
+ if (args->lstio_grp_key != console_session.ses_key)
+ return -EACCES;
+
+ if (args->lstio_grp_idsp == NULL || /* array of ids */
+ args->lstio_grp_count <= 0 ||
+ args->lstio_grp_resultp == NULL ||
+ args->lstio_grp_featp == NULL ||
+ args->lstio_grp_namep == NULL ||
+ args->lstio_grp_nmlen <= 0 ||
+ args->lstio_grp_nmlen > LST_NAME_SIZE)
+ return -EINVAL;
+
+ LIBCFS_ALLOC(name, args->lstio_grp_nmlen + 1);
+ if (name == NULL)
+ return -ENOMEM;
+
+ if (copy_from_user(name, args->lstio_grp_namep,
+ args->lstio_grp_nmlen)) {
+ LIBCFS_FREE(name, args->lstio_grp_nmlen + 1);
+
+ return -EFAULT;
+ }
+
+ name[args->lstio_grp_nmlen] = 0;
+
+ rc = lstcon_nodes_add(name, args->lstio_grp_count,
+ args->lstio_grp_idsp, &feats,
+ args->lstio_grp_resultp);
+
+ LIBCFS_FREE(name, args->lstio_grp_nmlen + 1);
+ if (rc == 0 &&
+ copy_to_user(args->lstio_grp_featp, &feats, sizeof(feats))) {
+ return -EINVAL;
+ }
+
+ return rc;
+}
+
+static int
+lst_group_list_ioctl(lstio_group_list_args_t *args)
+{
+ if (args->lstio_grp_key != console_session.ses_key)
+ return -EACCES;
+
+ if (args->lstio_grp_idx < 0 ||
+ args->lstio_grp_namep == NULL ||
+ args->lstio_grp_nmlen <= 0 ||
+ args->lstio_grp_nmlen > LST_NAME_SIZE)
+ return -EINVAL;
+
+ return lstcon_group_list(args->lstio_grp_idx,
+ args->lstio_grp_nmlen,
+ args->lstio_grp_namep);
+}
+
+static int
+lst_group_info_ioctl(lstio_group_info_args_t *args)
+{
+ char *name;
+ int ndent;
+ int index;
+ int rc;
+
+ if (args->lstio_grp_key != console_session.ses_key)
+ return -EACCES;
+
+ if (args->lstio_grp_namep == NULL ||
+ args->lstio_grp_nmlen <= 0 ||
+ args->lstio_grp_nmlen > LST_NAME_SIZE)
+ return -EINVAL;
+
+ if (args->lstio_grp_entp == NULL && /* output: group entry */
+ args->lstio_grp_dentsp == NULL) /* output: node entry */
+ return -EINVAL;
+
+ if (args->lstio_grp_dentsp != NULL) { /* have node entry */
+ if (args->lstio_grp_idxp == NULL || /* node index */
+ args->lstio_grp_ndentp == NULL) /* # of node entry */
+ return -EINVAL;
+
+ if (copy_from_user(&ndent, args->lstio_grp_ndentp,
+ sizeof(ndent)) ||
+ copy_from_user(&index, args->lstio_grp_idxp,
+ sizeof(index)))
+ return -EFAULT;
+
+ if (ndent <= 0 || index < 0)
+ return -EINVAL;
+ }
+
+ LIBCFS_ALLOC(name, args->lstio_grp_nmlen + 1);
+ if (name == NULL)
+ return -ENOMEM;
+
+ if (copy_from_user(name,
+ args->lstio_grp_namep,
+ args->lstio_grp_nmlen)) {
+ LIBCFS_FREE(name, args->lstio_grp_nmlen + 1);
+ return -EFAULT;
+ }
+
+ name[args->lstio_grp_nmlen] = 0;
+
+ rc = lstcon_group_info(name, args->lstio_grp_entp,
+ &index, &ndent, args->lstio_grp_dentsp);
+
+ LIBCFS_FREE(name, args->lstio_grp_nmlen + 1);
+
+ if (rc != 0)
+ return rc;
+
+ if (args->lstio_grp_dentsp != NULL &&
+ (copy_to_user(args->lstio_grp_idxp, &index, sizeof(index)) ||
+ copy_to_user(args->lstio_grp_ndentp, &ndent, sizeof(ndent))))
+ rc = -EFAULT;
+
+ return 0;
+}
+
+static int
+lst_batch_add_ioctl(lstio_batch_add_args_t *args)
+{
+ int rc;
+ char *name;
+
+ if (args->lstio_bat_key != console_session.ses_key)
+ return -EACCES;
+
+ if (args->lstio_bat_namep == NULL ||
+ args->lstio_bat_nmlen <= 0 ||
+ args->lstio_bat_nmlen > LST_NAME_SIZE)
+ return -EINVAL;
+
+ LIBCFS_ALLOC(name, args->lstio_bat_nmlen + 1);
+ if (name == NULL)
+ return -ENOMEM;
+
+ if (copy_from_user(name,
+ args->lstio_bat_namep,
+ args->lstio_bat_nmlen)) {
+ LIBCFS_FREE(name, args->lstio_bat_nmlen + 1);
+ return -EFAULT;
+ }
+
+ name[args->lstio_bat_nmlen] = 0;
+
+ rc = lstcon_batch_add(name);
+
+ LIBCFS_FREE(name, args->lstio_bat_nmlen + 1);
+
+ return rc;
+}
+
+static int
+lst_batch_run_ioctl(lstio_batch_run_args_t *args)
+{
+ int rc;
+ char *name;
+
+ if (args->lstio_bat_key != console_session.ses_key)
+ return -EACCES;
+
+ if (args->lstio_bat_namep == NULL ||
+ args->lstio_bat_nmlen <= 0 ||
+ args->lstio_bat_nmlen > LST_NAME_SIZE)
+ return -EINVAL;
+
+ LIBCFS_ALLOC(name, args->lstio_bat_nmlen + 1);
+ if (name == NULL)
+ return -ENOMEM;
+
+ if (copy_from_user(name,
+ args->lstio_bat_namep,
+ args->lstio_bat_nmlen)) {
+ LIBCFS_FREE(name, args->lstio_bat_nmlen + 1);
+ return -EFAULT;
+ }
+
+ name[args->lstio_bat_nmlen] = 0;
+
+ rc = lstcon_batch_run(name, args->lstio_bat_timeout,
+ args->lstio_bat_resultp);
+
+ LIBCFS_FREE(name, args->lstio_bat_nmlen + 1);
+
+ return rc;
+}
+
+static int
+lst_batch_stop_ioctl(lstio_batch_stop_args_t *args)
+{
+ int rc;
+ char *name;
+
+ if (args->lstio_bat_key != console_session.ses_key)
+ return -EACCES;
+
+ if (args->lstio_bat_resultp == NULL ||
+ args->lstio_bat_namep == NULL ||
+ args->lstio_bat_nmlen <= 0 ||
+ args->lstio_bat_nmlen > LST_NAME_SIZE)
+ return -EINVAL;
+
+ LIBCFS_ALLOC(name, args->lstio_bat_nmlen + 1);
+ if (name == NULL)
+ return -ENOMEM;
+
+ if (copy_from_user(name,
+ args->lstio_bat_namep,
+ args->lstio_bat_nmlen)) {
+ LIBCFS_FREE(name, args->lstio_bat_nmlen + 1);
+ return -EFAULT;
+ }
+
+ name[args->lstio_bat_nmlen] = 0;
+
+ rc = lstcon_batch_stop(name, args->lstio_bat_force,
+ args->lstio_bat_resultp);
+
+ LIBCFS_FREE(name, args->lstio_bat_nmlen + 1);
+
+ return rc;
+}
+
+static int
+lst_batch_query_ioctl(lstio_batch_query_args_t *args)
+{
+ char *name;
+ int rc;
+
+ if (args->lstio_bat_key != console_session.ses_key)
+ return -EACCES;
+
+ if (args->lstio_bat_resultp == NULL ||
+ args->lstio_bat_namep == NULL ||
+ args->lstio_bat_nmlen <= 0 ||
+ args->lstio_bat_nmlen > LST_NAME_SIZE)
+ return -EINVAL;
+
+ if (args->lstio_bat_testidx < 0)
+ return -EINVAL;
+
+ LIBCFS_ALLOC(name, args->lstio_bat_nmlen + 1);
+ if (name == NULL)
+ return -ENOMEM;
+
+ if (copy_from_user(name,
+ args->lstio_bat_namep,
+ args->lstio_bat_nmlen)) {
+ LIBCFS_FREE(name, args->lstio_bat_nmlen + 1);
+ return -EFAULT;
+ }
+
+ name[args->lstio_bat_nmlen] = 0;
+
+ rc = lstcon_test_batch_query(name,
+ args->lstio_bat_testidx,
+ args->lstio_bat_client,
+ args->lstio_bat_timeout,
+ args->lstio_bat_resultp);
+
+ LIBCFS_FREE(name, args->lstio_bat_nmlen + 1);
+
+ return rc;
+}
+
+static int
+lst_batch_list_ioctl(lstio_batch_list_args_t *args)
+{
+ if (args->lstio_bat_key != console_session.ses_key)
+ return -EACCES;
+
+ if (args->lstio_bat_idx < 0 ||
+ args->lstio_bat_namep == NULL ||
+ args->lstio_bat_nmlen <= 0 ||
+ args->lstio_bat_nmlen > LST_NAME_SIZE)
+ return -EINVAL;
+
+ return lstcon_batch_list(args->lstio_bat_idx,
+ args->lstio_bat_nmlen,
+ args->lstio_bat_namep);
+}
+
+static int
+lst_batch_info_ioctl(lstio_batch_info_args_t *args)
+{
+ char *name;
+ int rc;
+ int index;
+ int ndent;
+
+ if (args->lstio_bat_key != console_session.ses_key)
+ return -EACCES;
+
+ if (args->lstio_bat_namep == NULL || /* batch name */
+ args->lstio_bat_nmlen <= 0 ||
+ args->lstio_bat_nmlen > LST_NAME_SIZE)
+ return -EINVAL;
+
+ if (args->lstio_bat_entp == NULL && /* output: batch entry */
+ args->lstio_bat_dentsp == NULL) /* output: node entry */
+ return -EINVAL;
+
+ if (args->lstio_bat_dentsp != NULL) { /* have node entry */
+ if (args->lstio_bat_idxp == NULL || /* node index */
+ args->lstio_bat_ndentp == NULL) /* # of node entry */
+ return -EINVAL;
+
+ if (copy_from_user(&index, args->lstio_bat_idxp,
+ sizeof(index)) ||
+ copy_from_user(&ndent, args->lstio_bat_ndentp,
+ sizeof(ndent)))
+ return -EFAULT;
+
+ if (ndent <= 0 || index < 0)
+ return -EINVAL;
+ }
+
+ LIBCFS_ALLOC(name, args->lstio_bat_nmlen + 1);
+ if (name == NULL)
+ return -ENOMEM;
+
+ if (copy_from_user(name,
+ args->lstio_bat_namep, args->lstio_bat_nmlen)) {
+ LIBCFS_FREE(name, args->lstio_bat_nmlen + 1);
+ return -EFAULT;
+ }
+
+ name[args->lstio_bat_nmlen] = 0;
+
+ rc = lstcon_batch_info(name,
+ args->lstio_bat_entp, args->lstio_bat_server,
+ args->lstio_bat_testidx, &index, &ndent,
+ args->lstio_bat_dentsp);
+
+ LIBCFS_FREE(name, args->lstio_bat_nmlen + 1);
+
+ if (rc != 0)
+ return rc;
+
+ if (args->lstio_bat_dentsp != NULL &&
+ (copy_to_user(args->lstio_bat_idxp, &index, sizeof(index)) ||
+ copy_to_user(args->lstio_bat_ndentp, &ndent, sizeof(ndent))))
+ rc = -EFAULT;
+
+ return rc;
+}
+
+static int
+lst_stat_query_ioctl(lstio_stat_args_t *args)
+{
+ int rc;
+ char *name;
+
+ /* TODO: not finished */
+ if (args->lstio_sta_key != console_session.ses_key)
+ return -EACCES;
+
+ if (args->lstio_sta_resultp == NULL ||
+ (args->lstio_sta_namep == NULL &&
+ args->lstio_sta_idsp == NULL) ||
+ args->lstio_sta_nmlen <= 0 ||
+ args->lstio_sta_nmlen > LST_NAME_SIZE)
+ return -EINVAL;
+
+ if (args->lstio_sta_idsp != NULL &&
+ args->lstio_sta_count <= 0)
+ return -EINVAL;
+
+ LIBCFS_ALLOC(name, args->lstio_sta_nmlen + 1);
+ if (name == NULL)
+ return -ENOMEM;
+
+ if (copy_from_user(name, args->lstio_sta_namep,
+ args->lstio_sta_nmlen)) {
+ LIBCFS_FREE(name, args->lstio_sta_nmlen + 1);
+ return -EFAULT;
+ }
+
+ if (args->lstio_sta_idsp == NULL) {
+ rc = lstcon_group_stat(name, args->lstio_sta_timeout,
+ args->lstio_sta_resultp);
+ } else {
+ rc = lstcon_nodes_stat(args->lstio_sta_count,
+ args->lstio_sta_idsp,
+ args->lstio_sta_timeout,
+ args->lstio_sta_resultp);
+ }
+
+ LIBCFS_FREE(name, args->lstio_sta_nmlen + 1);
+
+ return rc;
+}
+
+static int lst_test_add_ioctl(lstio_test_args_t *args)
+{
+ char *batch_name;
+ char *src_name = NULL;
+ char *dst_name = NULL;
+ void *param = NULL;
+ int ret = 0;
+ int rc = -ENOMEM;
+
+ if (args->lstio_tes_resultp == NULL ||
+ args->lstio_tes_retp == NULL ||
+ args->lstio_tes_bat_name == NULL || /* no specified batch */
+ args->lstio_tes_bat_nmlen <= 0 ||
+ args->lstio_tes_bat_nmlen > LST_NAME_SIZE ||
+ args->lstio_tes_sgrp_name == NULL || /* no source group */
+ args->lstio_tes_sgrp_nmlen <= 0 ||
+ args->lstio_tes_sgrp_nmlen > LST_NAME_SIZE ||
+ args->lstio_tes_dgrp_name == NULL || /* no target group */
+ args->lstio_tes_dgrp_nmlen <= 0 ||
+ args->lstio_tes_dgrp_nmlen > LST_NAME_SIZE)
+ return -EINVAL;
+
+ if (args->lstio_tes_loop == 0 || /* negative is infinite */
+ args->lstio_tes_concur <= 0 ||
+ args->lstio_tes_dist <= 0 ||
+ args->lstio_tes_span <= 0)
+ return -EINVAL;
+
+ /* have parameter, check if parameter length is valid */
+ if (args->lstio_tes_param != NULL &&
+ (args->lstio_tes_param_len <= 0 ||
+ args->lstio_tes_param_len > PAGE_CACHE_SIZE - sizeof(lstcon_test_t)))
+ return -EINVAL;
+
+ LIBCFS_ALLOC(batch_name, args->lstio_tes_bat_nmlen + 1);
+ if (batch_name == NULL)
+ return rc;
+
+ LIBCFS_ALLOC(src_name, args->lstio_tes_sgrp_nmlen + 1);
+ if (src_name == NULL)
+ goto out;
+
+ LIBCFS_ALLOC(dst_name, args->lstio_tes_dgrp_nmlen + 1);
+ if (dst_name == NULL)
+ goto out;
+
+ if (args->lstio_tes_param != NULL) {
+ LIBCFS_ALLOC(param, args->lstio_tes_param_len);
+ if (param == NULL)
+ goto out;
+ }
+
+ rc = -EFAULT;
+ if (copy_from_user(batch_name, args->lstio_tes_bat_name,
+ args->lstio_tes_bat_nmlen) ||
+ copy_from_user(src_name, args->lstio_tes_sgrp_name,
+ args->lstio_tes_sgrp_nmlen) ||
+ copy_from_user(dst_name, args->lstio_tes_dgrp_name,
+ args->lstio_tes_dgrp_nmlen) ||
+ copy_from_user(param, args->lstio_tes_param,
+ args->lstio_tes_param_len))
+ goto out;
+
+ rc = lstcon_test_add(batch_name,
+ args->lstio_tes_type,
+ args->lstio_tes_loop,
+ args->lstio_tes_concur,
+ args->lstio_tes_dist, args->lstio_tes_span,
+ src_name, dst_name, param,
+ args->lstio_tes_param_len,
+ &ret, args->lstio_tes_resultp);
+
+ if (ret != 0)
+ rc = (copy_to_user(args->lstio_tes_retp, &ret,
+ sizeof(ret))) ? -EFAULT : 0;
+out:
+ if (batch_name != NULL)
+ LIBCFS_FREE(batch_name, args->lstio_tes_bat_nmlen + 1);
+
+ if (src_name != NULL)
+ LIBCFS_FREE(src_name, args->lstio_tes_sgrp_nmlen + 1);
+
+ if (dst_name != NULL)
+ LIBCFS_FREE(dst_name, args->lstio_tes_dgrp_nmlen + 1);
+
+ if (param != NULL)
+ LIBCFS_FREE(param, args->lstio_tes_param_len);
+
+ return rc;
+}
+
+int
+lstcon_ioctl_entry(unsigned int cmd, struct libcfs_ioctl_data *data)
+{
+ char *buf;
+ int opc = data->ioc_u32[0];
+ int rc;
+
+ if (cmd != IOC_LIBCFS_LNETST)
+ return -EINVAL;
+
+ if (data->ioc_plen1 > PAGE_CACHE_SIZE)
+ return -EINVAL;
+
+ LIBCFS_ALLOC(buf, data->ioc_plen1);
+ if (buf == NULL)
+ return -ENOMEM;
+
+ /* copy in parameter */
+ if (copy_from_user(buf, data->ioc_pbuf1, data->ioc_plen1)) {
+ LIBCFS_FREE(buf, data->ioc_plen1);
+ return -EFAULT;
+ }
+
+ mutex_lock(&console_session.ses_mutex);
+
+ console_session.ses_laststamp = get_seconds();
+
+ if (console_session.ses_shutdown) {
+ rc = -ESHUTDOWN;
+ goto out;
+ }
+
+ if (console_session.ses_expired)
+ lstcon_session_end();
+
+ if (opc != LSTIO_SESSION_NEW &&
+ console_session.ses_state == LST_SESSION_NONE) {
+ CDEBUG(D_NET, "LST no active session\n");
+ rc = -ESRCH;
+ goto out;
+ }
+
+ memset(&console_session.ses_trans_stat, 0, sizeof(lstcon_trans_stat_t));
+
+ switch (opc) {
+ case LSTIO_SESSION_NEW:
+ rc = lst_session_new_ioctl((lstio_session_new_args_t *)buf);
+ break;
+ case LSTIO_SESSION_END:
+ rc = lst_session_end_ioctl((lstio_session_end_args_t *)buf);
+ break;
+ case LSTIO_SESSION_INFO:
+ rc = lst_session_info_ioctl((lstio_session_info_args_t *)buf);
+ break;
+ case LSTIO_DEBUG:
+ rc = lst_debug_ioctl((lstio_debug_args_t *)buf);
+ break;
+ case LSTIO_GROUP_ADD:
+ rc = lst_group_add_ioctl((lstio_group_add_args_t *)buf);
+ break;
+ case LSTIO_GROUP_DEL:
+ rc = lst_group_del_ioctl((lstio_group_del_args_t *)buf);
+ break;
+ case LSTIO_GROUP_UPDATE:
+ rc = lst_group_update_ioctl((lstio_group_update_args_t *)buf);
+ break;
+ case LSTIO_NODES_ADD:
+ rc = lst_nodes_add_ioctl((lstio_group_nodes_args_t *)buf);
+ break;
+ case LSTIO_GROUP_LIST:
+ rc = lst_group_list_ioctl((lstio_group_list_args_t *)buf);
+ break;
+ case LSTIO_GROUP_INFO:
+ rc = lst_group_info_ioctl((lstio_group_info_args_t *)buf);
+ break;
+ case LSTIO_BATCH_ADD:
+ rc = lst_batch_add_ioctl((lstio_batch_add_args_t *)buf);
+ break;
+ case LSTIO_BATCH_START:
+ rc = lst_batch_run_ioctl((lstio_batch_run_args_t *)buf);
+ break;
+ case LSTIO_BATCH_STOP:
+ rc = lst_batch_stop_ioctl((lstio_batch_stop_args_t *)buf);
+ break;
+ case LSTIO_BATCH_QUERY:
+ rc = lst_batch_query_ioctl((lstio_batch_query_args_t *)buf);
+ break;
+ case LSTIO_BATCH_LIST:
+ rc = lst_batch_list_ioctl((lstio_batch_list_args_t *)buf);
+ break;
+ case LSTIO_BATCH_INFO:
+ rc = lst_batch_info_ioctl((lstio_batch_info_args_t *)buf);
+ break;
+ case LSTIO_TEST_ADD:
+ rc = lst_test_add_ioctl((lstio_test_args_t *)buf);
+ break;
+ case LSTIO_STAT_QUERY:
+ rc = lst_stat_query_ioctl((lstio_stat_args_t *)buf);
+ break;
+ default:
+ rc = -EINVAL;
+ }
+
+ if (copy_to_user(data->ioc_pbuf2, &console_session.ses_trans_stat,
+ sizeof(lstcon_trans_stat_t)))
+ rc = -EFAULT;
+out:
+ mutex_unlock(&console_session.ses_mutex);
+
+ LIBCFS_FREE(buf, data->ioc_plen1);
+
+ return rc;
+}
+
+EXPORT_SYMBOL(lstcon_ioctl_entry);
diff --git a/drivers/staging/lustre/lnet/selftest/conrpc.c b/drivers/staging/lustre/lnet/selftest/conrpc.c
new file mode 100644
index 000000000..77f02b761
--- /dev/null
+++ b/drivers/staging/lustre/lnet/selftest/conrpc.c
@@ -0,0 +1,1396 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2011, 2012, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ *
+ * lnet/selftest/conctl.c
+ *
+ * Console framework rpcs
+ *
+ * Author: Liang Zhen <liang@whamcloud.com>
+ */
+
+
+#include "../../include/linux/libcfs/libcfs.h"
+#include "../../include/linux/lnet/lib-lnet.h"
+#include "timer.h"
+#include "conrpc.h"
+#include "console.h"
+
+void lstcon_rpc_stat_reply(lstcon_rpc_trans_t *, srpc_msg_t *,
+ lstcon_node_t *, lstcon_trans_stat_t *);
+
+static void
+lstcon_rpc_done(srpc_client_rpc_t *rpc)
+{
+ lstcon_rpc_t *crpc = (lstcon_rpc_t *)rpc->crpc_priv;
+
+ LASSERT(crpc != NULL && rpc == crpc->crp_rpc);
+ LASSERT(crpc->crp_posted && !crpc->crp_finished);
+
+ spin_lock(&rpc->crpc_lock);
+
+ if (crpc->crp_trans == NULL) {
+ /* Orphan RPC is not in any transaction,
+ * I'm just a poor body and nobody loves me */
+ spin_unlock(&rpc->crpc_lock);
+
+ /* release it */
+ lstcon_rpc_put(crpc);
+ return;
+ }
+
+ /* not an orphan RPC */
+ crpc->crp_finished = 1;
+
+ if (crpc->crp_stamp == 0) {
+ /* not aborted */
+ LASSERT(crpc->crp_status == 0);
+
+ crpc->crp_stamp = cfs_time_current();
+ crpc->crp_status = rpc->crpc_status;
+ }
+
+ /* wakeup (transaction)thread if I'm the last RPC in the transaction */
+ if (atomic_dec_and_test(&crpc->crp_trans->tas_remaining))
+ wake_up(&crpc->crp_trans->tas_waitq);
+
+ spin_unlock(&rpc->crpc_lock);
+}
+
+static int
+lstcon_rpc_init(lstcon_node_t *nd, int service, unsigned feats,
+ int bulk_npg, int bulk_len, int embedded, lstcon_rpc_t *crpc)
+{
+ crpc->crp_rpc = sfw_create_rpc(nd->nd_id, service,
+ feats, bulk_npg, bulk_len,
+ lstcon_rpc_done, (void *)crpc);
+ if (crpc->crp_rpc == NULL)
+ return -ENOMEM;
+
+ crpc->crp_trans = NULL;
+ crpc->crp_node = nd;
+ crpc->crp_posted = 0;
+ crpc->crp_finished = 0;
+ crpc->crp_unpacked = 0;
+ crpc->crp_status = 0;
+ crpc->crp_stamp = 0;
+ crpc->crp_embedded = embedded;
+ INIT_LIST_HEAD(&crpc->crp_link);
+
+ atomic_inc(&console_session.ses_rpc_counter);
+
+ return 0;
+}
+
+static int
+lstcon_rpc_prep(lstcon_node_t *nd, int service, unsigned feats,
+ int bulk_npg, int bulk_len, lstcon_rpc_t **crpcpp)
+{
+ lstcon_rpc_t *crpc = NULL;
+ int rc;
+
+ spin_lock(&console_session.ses_rpc_lock);
+
+ if (!list_empty(&console_session.ses_rpc_freelist)) {
+ crpc = list_entry(console_session.ses_rpc_freelist.next,
+ lstcon_rpc_t, crp_link);
+ list_del_init(&crpc->crp_link);
+ }
+
+ spin_unlock(&console_session.ses_rpc_lock);
+
+ if (crpc == NULL) {
+ LIBCFS_ALLOC(crpc, sizeof(*crpc));
+ if (crpc == NULL)
+ return -ENOMEM;
+ }
+
+ rc = lstcon_rpc_init(nd, service, feats, bulk_npg, bulk_len, 0, crpc);
+ if (rc == 0) {
+ *crpcpp = crpc;
+ return 0;
+ }
+
+ LIBCFS_FREE(crpc, sizeof(*crpc));
+
+ return rc;
+}
+
+void
+lstcon_rpc_put(lstcon_rpc_t *crpc)
+{
+ srpc_bulk_t *bulk = &crpc->crp_rpc->crpc_bulk;
+ int i;
+
+ LASSERT(list_empty(&crpc->crp_link));
+
+ for (i = 0; i < bulk->bk_niov; i++) {
+ if (bulk->bk_iovs[i].kiov_page == NULL)
+ continue;
+
+ __free_page(bulk->bk_iovs[i].kiov_page);
+ }
+
+ srpc_client_rpc_decref(crpc->crp_rpc);
+
+ if (crpc->crp_embedded) {
+ /* embedded RPC, don't recycle it */
+ memset(crpc, 0, sizeof(*crpc));
+ crpc->crp_embedded = 1;
+
+ } else {
+ spin_lock(&console_session.ses_rpc_lock);
+
+ list_add(&crpc->crp_link,
+ &console_session.ses_rpc_freelist);
+
+ spin_unlock(&console_session.ses_rpc_lock);
+ }
+
+ /* RPC is not alive now */
+ atomic_dec(&console_session.ses_rpc_counter);
+}
+
+static void
+lstcon_rpc_post(lstcon_rpc_t *crpc)
+{
+ lstcon_rpc_trans_t *trans = crpc->crp_trans;
+
+ LASSERT(trans != NULL);
+
+ atomic_inc(&trans->tas_remaining);
+ crpc->crp_posted = 1;
+
+ sfw_post_rpc(crpc->crp_rpc);
+}
+
+static char *
+lstcon_rpc_trans_name(int transop)
+{
+ if (transop == LST_TRANS_SESNEW)
+ return "SESNEW";
+
+ if (transop == LST_TRANS_SESEND)
+ return "SESEND";
+
+ if (transop == LST_TRANS_SESQRY)
+ return "SESQRY";
+
+ if (transop == LST_TRANS_SESPING)
+ return "SESPING";
+
+ if (transop == LST_TRANS_TSBCLIADD)
+ return "TSBCLIADD";
+
+ if (transop == LST_TRANS_TSBSRVADD)
+ return "TSBSRVADD";
+
+ if (transop == LST_TRANS_TSBRUN)
+ return "TSBRUN";
+
+ if (transop == LST_TRANS_TSBSTOP)
+ return "TSBSTOP";
+
+ if (transop == LST_TRANS_TSBCLIQRY)
+ return "TSBCLIQRY";
+
+ if (transop == LST_TRANS_TSBSRVQRY)
+ return "TSBSRVQRY";
+
+ if (transop == LST_TRANS_STATQRY)
+ return "STATQRY";
+
+ return "Unknown";
+}
+
+int
+lstcon_rpc_trans_prep(struct list_head *translist,
+ int transop, lstcon_rpc_trans_t **transpp)
+{
+ lstcon_rpc_trans_t *trans;
+
+ if (translist != NULL) {
+ list_for_each_entry(trans, translist, tas_link) {
+ /* Can't enqueue two private transaction on
+ * the same object */
+ if ((trans->tas_opc & transop) == LST_TRANS_PRIVATE)
+ return -EPERM;
+ }
+ }
+
+ /* create a trans group */
+ LIBCFS_ALLOC(trans, sizeof(*trans));
+ if (trans == NULL)
+ return -ENOMEM;
+
+ trans->tas_opc = transop;
+
+ if (translist == NULL)
+ INIT_LIST_HEAD(&trans->tas_olink);
+ else
+ list_add_tail(&trans->tas_olink, translist);
+
+ list_add_tail(&trans->tas_link, &console_session.ses_trans_list);
+
+ INIT_LIST_HEAD(&trans->tas_rpcs_list);
+ atomic_set(&trans->tas_remaining, 0);
+ init_waitqueue_head(&trans->tas_waitq);
+
+ spin_lock(&console_session.ses_rpc_lock);
+ trans->tas_features = console_session.ses_features;
+ spin_unlock(&console_session.ses_rpc_lock);
+
+ *transpp = trans;
+ return 0;
+}
+
+void
+lstcon_rpc_trans_addreq(lstcon_rpc_trans_t *trans, lstcon_rpc_t *crpc)
+{
+ list_add_tail(&crpc->crp_link, &trans->tas_rpcs_list);
+ crpc->crp_trans = trans;
+}
+
+void
+lstcon_rpc_trans_abort(lstcon_rpc_trans_t *trans, int error)
+{
+ srpc_client_rpc_t *rpc;
+ lstcon_rpc_t *crpc;
+ lstcon_node_t *nd;
+
+ list_for_each_entry(crpc, &trans->tas_rpcs_list, crp_link) {
+ rpc = crpc->crp_rpc;
+
+ spin_lock(&rpc->crpc_lock);
+
+ if (!crpc->crp_posted || /* not posted */
+ crpc->crp_stamp != 0) { /* rpc done or aborted already */
+ if (crpc->crp_stamp == 0) {
+ crpc->crp_stamp = cfs_time_current();
+ crpc->crp_status = -EINTR;
+ }
+ spin_unlock(&rpc->crpc_lock);
+ continue;
+ }
+
+ crpc->crp_stamp = cfs_time_current();
+ crpc->crp_status = error;
+
+ spin_unlock(&rpc->crpc_lock);
+
+ sfw_abort_rpc(rpc);
+
+ if (error != ETIMEDOUT)
+ continue;
+
+ nd = crpc->crp_node;
+ if (cfs_time_after(nd->nd_stamp, crpc->crp_stamp))
+ continue;
+
+ nd->nd_stamp = crpc->crp_stamp;
+ nd->nd_state = LST_NODE_DOWN;
+ }
+}
+
+static int
+lstcon_rpc_trans_check(lstcon_rpc_trans_t *trans)
+{
+ if (console_session.ses_shutdown &&
+ !list_empty(&trans->tas_olink)) /* Not an end session RPC */
+ return 1;
+
+ return (atomic_read(&trans->tas_remaining) == 0) ? 1 : 0;
+}
+
+int
+lstcon_rpc_trans_postwait(lstcon_rpc_trans_t *trans, int timeout)
+{
+ lstcon_rpc_t *crpc;
+ int rc;
+
+ if (list_empty(&trans->tas_rpcs_list))
+ return 0;
+
+ if (timeout < LST_TRANS_MIN_TIMEOUT)
+ timeout = LST_TRANS_MIN_TIMEOUT;
+
+ CDEBUG(D_NET, "Transaction %s started\n",
+ lstcon_rpc_trans_name(trans->tas_opc));
+
+ /* post all requests */
+ list_for_each_entry(crpc, &trans->tas_rpcs_list, crp_link) {
+ LASSERT(!crpc->crp_posted);
+
+ lstcon_rpc_post(crpc);
+ }
+
+ mutex_unlock(&console_session.ses_mutex);
+
+ rc = wait_event_interruptible_timeout(trans->tas_waitq,
+ lstcon_rpc_trans_check(trans),
+ cfs_time_seconds(timeout));
+ rc = (rc > 0) ? 0 : ((rc < 0) ? -EINTR : -ETIMEDOUT);
+
+ mutex_lock(&console_session.ses_mutex);
+
+ if (console_session.ses_shutdown)
+ rc = -ESHUTDOWN;
+
+ if (rc != 0 || atomic_read(&trans->tas_remaining) != 0) {
+ /* treat short timeout as canceled */
+ if (rc == -ETIMEDOUT && timeout < LST_TRANS_MIN_TIMEOUT * 2)
+ rc = -EINTR;
+
+ lstcon_rpc_trans_abort(trans, rc);
+ }
+
+ CDEBUG(D_NET, "Transaction %s stopped: %d\n",
+ lstcon_rpc_trans_name(trans->tas_opc), rc);
+
+ lstcon_rpc_trans_stat(trans, lstcon_trans_stat());
+
+ return rc;
+}
+
+static int
+lstcon_rpc_get_reply(lstcon_rpc_t *crpc, srpc_msg_t **msgpp)
+{
+ lstcon_node_t *nd = crpc->crp_node;
+ srpc_client_rpc_t *rpc = crpc->crp_rpc;
+ srpc_generic_reply_t *rep;
+
+ LASSERT(nd != NULL && rpc != NULL);
+ LASSERT(crpc->crp_stamp != 0);
+
+ if (crpc->crp_status != 0) {
+ *msgpp = NULL;
+ return crpc->crp_status;
+ }
+
+ *msgpp = &rpc->crpc_replymsg;
+ if (!crpc->crp_unpacked) {
+ sfw_unpack_message(*msgpp);
+ crpc->crp_unpacked = 1;
+ }
+
+ if (cfs_time_after(nd->nd_stamp, crpc->crp_stamp))
+ return 0;
+
+ nd->nd_stamp = crpc->crp_stamp;
+ rep = &(*msgpp)->msg_body.reply;
+
+ if (rep->sid.ses_nid == LNET_NID_ANY)
+ nd->nd_state = LST_NODE_UNKNOWN;
+ else if (lstcon_session_match(rep->sid))
+ nd->nd_state = LST_NODE_ACTIVE;
+ else
+ nd->nd_state = LST_NODE_BUSY;
+
+ return 0;
+}
+
+void
+lstcon_rpc_trans_stat(lstcon_rpc_trans_t *trans, lstcon_trans_stat_t *stat)
+{
+ lstcon_rpc_t *crpc;
+ srpc_msg_t *rep;
+ int error;
+
+ LASSERT(stat != NULL);
+
+ memset(stat, 0, sizeof(*stat));
+
+ list_for_each_entry(crpc, &trans->tas_rpcs_list, crp_link) {
+ lstcon_rpc_stat_total(stat, 1);
+
+ LASSERT(crpc->crp_stamp != 0);
+
+ error = lstcon_rpc_get_reply(crpc, &rep);
+ if (error != 0) {
+ lstcon_rpc_stat_failure(stat, 1);
+ if (stat->trs_rpc_errno == 0)
+ stat->trs_rpc_errno = -error;
+
+ continue;
+ }
+
+ lstcon_rpc_stat_success(stat, 1);
+
+ lstcon_rpc_stat_reply(trans, rep, crpc->crp_node, stat);
+ }
+
+ if (trans->tas_opc == LST_TRANS_SESNEW && stat->trs_fwk_errno == 0) {
+ stat->trs_fwk_errno =
+ lstcon_session_feats_check(trans->tas_features);
+ }
+
+ CDEBUG(D_NET, "transaction %s : success %d, failure %d, total %d, RPC error(%d), Framework error(%d)\n",
+ lstcon_rpc_trans_name(trans->tas_opc),
+ lstcon_rpc_stat_success(stat, 0),
+ lstcon_rpc_stat_failure(stat, 0),
+ lstcon_rpc_stat_total(stat, 0),
+ stat->trs_rpc_errno, stat->trs_fwk_errno);
+
+ return;
+}
+
+int
+lstcon_rpc_trans_interpreter(lstcon_rpc_trans_t *trans,
+ struct list_head *head_up,
+ lstcon_rpc_readent_func_t readent)
+{
+ struct list_head tmp;
+ struct list_head *next;
+ lstcon_rpc_ent_t *ent;
+ srpc_generic_reply_t *rep;
+ lstcon_rpc_t *crpc;
+ srpc_msg_t *msg;
+ lstcon_node_t *nd;
+ long dur;
+ struct timeval tv;
+ int error;
+
+ LASSERT(head_up != NULL);
+
+ next = head_up;
+
+ list_for_each_entry(crpc, &trans->tas_rpcs_list, crp_link) {
+ if (copy_from_user(&tmp, next,
+ sizeof(struct list_head)))
+ return -EFAULT;
+
+ if (tmp.next == head_up)
+ return 0;
+
+ next = tmp.next;
+
+ ent = list_entry(next, lstcon_rpc_ent_t, rpe_link);
+
+ LASSERT(crpc->crp_stamp != 0);
+
+ error = lstcon_rpc_get_reply(crpc, &msg);
+
+ nd = crpc->crp_node;
+
+ dur = (long)cfs_time_sub(crpc->crp_stamp,
+ (unsigned long)console_session.ses_id.ses_stamp);
+ cfs_duration_usec(dur, &tv);
+
+ if (copy_to_user(&ent->rpe_peer,
+ &nd->nd_id, sizeof(lnet_process_id_t)) ||
+ copy_to_user(&ent->rpe_stamp, &tv, sizeof(tv)) ||
+ copy_to_user(&ent->rpe_state,
+ &nd->nd_state, sizeof(nd->nd_state)) ||
+ copy_to_user(&ent->rpe_rpc_errno, &error,
+ sizeof(error)))
+ return -EFAULT;
+
+ if (error != 0)
+ continue;
+
+ /* RPC is done */
+ rep = (srpc_generic_reply_t *)&msg->msg_body.reply;
+
+ if (copy_to_user(&ent->rpe_sid,
+ &rep->sid, sizeof(lst_sid_t)) ||
+ copy_to_user(&ent->rpe_fwk_errno,
+ &rep->status, sizeof(rep->status)))
+ return -EFAULT;
+
+ if (readent == NULL)
+ continue;
+
+ error = readent(trans->tas_opc, msg, ent);
+
+ if (error != 0)
+ return error;
+ }
+
+ return 0;
+}
+
+void
+lstcon_rpc_trans_destroy(lstcon_rpc_trans_t *trans)
+{
+ srpc_client_rpc_t *rpc;
+ lstcon_rpc_t *crpc;
+ lstcon_rpc_t *tmp;
+ int count = 0;
+
+ list_for_each_entry_safe(crpc, tmp, &trans->tas_rpcs_list,
+ crp_link) {
+ rpc = crpc->crp_rpc;
+
+ spin_lock(&rpc->crpc_lock);
+
+ /* free it if not posted or finished already */
+ if (!crpc->crp_posted || crpc->crp_finished) {
+ spin_unlock(&rpc->crpc_lock);
+
+ list_del_init(&crpc->crp_link);
+ lstcon_rpc_put(crpc);
+
+ continue;
+ }
+
+ /* rpcs can be still not callbacked (even LNetMDUnlink is called)
+ * because huge timeout for inaccessible network, don't make
+ * user wait for them, just abandon them, they will be recycled
+ * in callback */
+
+ LASSERT(crpc->crp_status != 0);
+
+ crpc->crp_node = NULL;
+ crpc->crp_trans = NULL;
+ list_del_init(&crpc->crp_link);
+ count++;
+
+ spin_unlock(&rpc->crpc_lock);
+
+ atomic_dec(&trans->tas_remaining);
+ }
+
+ LASSERT(atomic_read(&trans->tas_remaining) == 0);
+
+ list_del(&trans->tas_link);
+ if (!list_empty(&trans->tas_olink))
+ list_del(&trans->tas_olink);
+
+ CDEBUG(D_NET, "Transaction %s destroyed with %d pending RPCs\n",
+ lstcon_rpc_trans_name(trans->tas_opc), count);
+
+ LIBCFS_FREE(trans, sizeof(*trans));
+
+ return;
+}
+
+int
+lstcon_sesrpc_prep(lstcon_node_t *nd, int transop,
+ unsigned feats, lstcon_rpc_t **crpc)
+{
+ srpc_mksn_reqst_t *msrq;
+ srpc_rmsn_reqst_t *rsrq;
+ int rc;
+
+ switch (transop) {
+ case LST_TRANS_SESNEW:
+ rc = lstcon_rpc_prep(nd, SRPC_SERVICE_MAKE_SESSION,
+ feats, 0, 0, crpc);
+ if (rc != 0)
+ return rc;
+
+ msrq = &(*crpc)->crp_rpc->crpc_reqstmsg.msg_body.mksn_reqst;
+ msrq->mksn_sid = console_session.ses_id;
+ msrq->mksn_force = console_session.ses_force;
+ strncpy(msrq->mksn_name, console_session.ses_name,
+ strlen(console_session.ses_name));
+ break;
+
+ case LST_TRANS_SESEND:
+ rc = lstcon_rpc_prep(nd, SRPC_SERVICE_REMOVE_SESSION,
+ feats, 0, 0, crpc);
+ if (rc != 0)
+ return rc;
+
+ rsrq = &(*crpc)->crp_rpc->crpc_reqstmsg.msg_body.rmsn_reqst;
+ rsrq->rmsn_sid = console_session.ses_id;
+ break;
+
+ default:
+ LBUG();
+ }
+
+ return 0;
+}
+
+int
+lstcon_dbgrpc_prep(lstcon_node_t *nd, unsigned feats, lstcon_rpc_t **crpc)
+{
+ srpc_debug_reqst_t *drq;
+ int rc;
+
+ rc = lstcon_rpc_prep(nd, SRPC_SERVICE_DEBUG, feats, 0, 0, crpc);
+ if (rc != 0)
+ return rc;
+
+ drq = &(*crpc)->crp_rpc->crpc_reqstmsg.msg_body.dbg_reqst;
+
+ drq->dbg_sid = console_session.ses_id;
+ drq->dbg_flags = 0;
+
+ return rc;
+}
+
+int
+lstcon_batrpc_prep(lstcon_node_t *nd, int transop, unsigned feats,
+ lstcon_tsb_hdr_t *tsb, lstcon_rpc_t **crpc)
+{
+ lstcon_batch_t *batch;
+ srpc_batch_reqst_t *brq;
+ int rc;
+
+ rc = lstcon_rpc_prep(nd, SRPC_SERVICE_BATCH, feats, 0, 0, crpc);
+ if (rc != 0)
+ return rc;
+
+ brq = &(*crpc)->crp_rpc->crpc_reqstmsg.msg_body.bat_reqst;
+
+ brq->bar_sid = console_session.ses_id;
+ brq->bar_bid = tsb->tsb_id;
+ brq->bar_testidx = tsb->tsb_index;
+ brq->bar_opc = transop == LST_TRANS_TSBRUN ? SRPC_BATCH_OPC_RUN :
+ (transop == LST_TRANS_TSBSTOP ? SRPC_BATCH_OPC_STOP :
+ SRPC_BATCH_OPC_QUERY);
+
+ if (transop != LST_TRANS_TSBRUN &&
+ transop != LST_TRANS_TSBSTOP)
+ return 0;
+
+ LASSERT(tsb->tsb_index == 0);
+
+ batch = (lstcon_batch_t *)tsb;
+ brq->bar_arg = batch->bat_arg;
+
+ return 0;
+}
+
+int
+lstcon_statrpc_prep(lstcon_node_t *nd, unsigned feats, lstcon_rpc_t **crpc)
+{
+ srpc_stat_reqst_t *srq;
+ int rc;
+
+ rc = lstcon_rpc_prep(nd, SRPC_SERVICE_QUERY_STAT, feats, 0, 0, crpc);
+ if (rc != 0)
+ return rc;
+
+ srq = &(*crpc)->crp_rpc->crpc_reqstmsg.msg_body.stat_reqst;
+
+ srq->str_sid = console_session.ses_id;
+ srq->str_type = 0; /* XXX remove it */
+
+ return 0;
+}
+
+static lnet_process_id_packed_t *
+lstcon_next_id(int idx, int nkiov, lnet_kiov_t *kiov)
+{
+ lnet_process_id_packed_t *pid;
+ int i;
+
+ i = idx / SFW_ID_PER_PAGE;
+
+ LASSERT(i < nkiov);
+
+ pid = (lnet_process_id_packed_t *)page_address(kiov[i].kiov_page);
+
+ return &pid[idx % SFW_ID_PER_PAGE];
+}
+
+static int
+lstcon_dstnodes_prep(lstcon_group_t *grp, int idx,
+ int dist, int span, int nkiov, lnet_kiov_t *kiov)
+{
+ lnet_process_id_packed_t *pid;
+ lstcon_ndlink_t *ndl;
+ lstcon_node_t *nd;
+ int start;
+ int end;
+ int i = 0;
+
+ LASSERT(dist >= 1);
+ LASSERT(span >= 1);
+ LASSERT(grp->grp_nnode >= 1);
+
+ if (span > grp->grp_nnode)
+ return -EINVAL;
+
+ start = ((idx / dist) * span) % grp->grp_nnode;
+ end = ((idx / dist) * span + span - 1) % grp->grp_nnode;
+
+ list_for_each_entry(ndl, &grp->grp_ndl_list, ndl_link) {
+ nd = ndl->ndl_node;
+ if (i < start) {
+ i++;
+ continue;
+ }
+
+ if (i > (end >= start ? end : grp->grp_nnode))
+ break;
+
+ pid = lstcon_next_id((i - start), nkiov, kiov);
+ pid->nid = nd->nd_id.nid;
+ pid->pid = nd->nd_id.pid;
+ i++;
+ }
+
+ if (start <= end) /* done */
+ return 0;
+
+ list_for_each_entry(ndl, &grp->grp_ndl_list, ndl_link) {
+ if (i > grp->grp_nnode + end)
+ break;
+
+ nd = ndl->ndl_node;
+ pid = lstcon_next_id((i - start), nkiov, kiov);
+ pid->nid = nd->nd_id.nid;
+ pid->pid = nd->nd_id.pid;
+ i++;
+ }
+
+ return 0;
+}
+
+static int
+lstcon_pingrpc_prep(lst_test_ping_param_t *param, srpc_test_reqst_t *req)
+{
+ test_ping_req_t *prq = &req->tsr_u.ping;
+
+ prq->png_size = param->png_size;
+ prq->png_flags = param->png_flags;
+ /* TODO dest */
+ return 0;
+}
+
+static int
+lstcon_bulkrpc_v0_prep(lst_test_bulk_param_t *param, srpc_test_reqst_t *req)
+{
+ test_bulk_req_t *brq = &req->tsr_u.bulk_v0;
+
+ brq->blk_opc = param->blk_opc;
+ brq->blk_npg = (param->blk_size + PAGE_CACHE_SIZE - 1) / PAGE_CACHE_SIZE;
+ brq->blk_flags = param->blk_flags;
+
+ return 0;
+}
+
+static int
+lstcon_bulkrpc_v1_prep(lst_test_bulk_param_t *param, srpc_test_reqst_t *req)
+{
+ test_bulk_req_v1_t *brq = &req->tsr_u.bulk_v1;
+
+ brq->blk_opc = param->blk_opc;
+ brq->blk_flags = param->blk_flags;
+ brq->blk_len = param->blk_size;
+ brq->blk_offset = 0; /* reserved */
+
+ return 0;
+}
+
+int
+lstcon_testrpc_prep(lstcon_node_t *nd, int transop, unsigned feats,
+ lstcon_test_t *test, lstcon_rpc_t **crpc)
+{
+ lstcon_group_t *sgrp = test->tes_src_grp;
+ lstcon_group_t *dgrp = test->tes_dst_grp;
+ srpc_test_reqst_t *trq;
+ srpc_bulk_t *bulk;
+ int i;
+ int npg = 0;
+ int nob = 0;
+ int rc = 0;
+
+ if (transop == LST_TRANS_TSBCLIADD) {
+ npg = sfw_id_pages(test->tes_span);
+ nob = (feats & LST_FEAT_BULK_LEN) == 0 ?
+ npg * PAGE_CACHE_SIZE :
+ sizeof(lnet_process_id_packed_t) * test->tes_span;
+ }
+
+ rc = lstcon_rpc_prep(nd, SRPC_SERVICE_TEST, feats, npg, nob, crpc);
+ if (rc != 0)
+ return rc;
+
+ trq = &(*crpc)->crp_rpc->crpc_reqstmsg.msg_body.tes_reqst;
+
+ if (transop == LST_TRANS_TSBSRVADD) {
+ int ndist = (sgrp->grp_nnode + test->tes_dist - 1) / test->tes_dist;
+ int nspan = (dgrp->grp_nnode + test->tes_span - 1) / test->tes_span;
+ int nmax = (ndist + nspan - 1) / nspan;
+
+ trq->tsr_ndest = 0;
+ trq->tsr_loop = nmax * test->tes_dist * test->tes_concur;
+
+ } else {
+ bulk = &(*crpc)->crp_rpc->crpc_bulk;
+
+ for (i = 0; i < npg; i++) {
+ int len;
+
+ LASSERT(nob > 0);
+
+ len = (feats & LST_FEAT_BULK_LEN) == 0 ?
+ PAGE_CACHE_SIZE : min_t(int, nob, PAGE_CACHE_SIZE);
+ nob -= len;
+
+ bulk->bk_iovs[i].kiov_offset = 0;
+ bulk->bk_iovs[i].kiov_len = len;
+ bulk->bk_iovs[i].kiov_page =
+ alloc_page(GFP_IOFS);
+
+ if (bulk->bk_iovs[i].kiov_page == NULL) {
+ lstcon_rpc_put(*crpc);
+ return -ENOMEM;
+ }
+ }
+
+ bulk->bk_sink = 0;
+
+ LASSERT(transop == LST_TRANS_TSBCLIADD);
+
+ rc = lstcon_dstnodes_prep(test->tes_dst_grp,
+ test->tes_cliidx++,
+ test->tes_dist,
+ test->tes_span,
+ npg, &bulk->bk_iovs[0]);
+ if (rc != 0) {
+ lstcon_rpc_put(*crpc);
+ return rc;
+ }
+
+ trq->tsr_ndest = test->tes_span;
+ trq->tsr_loop = test->tes_loop;
+ }
+
+ trq->tsr_sid = console_session.ses_id;
+ trq->tsr_bid = test->tes_hdr.tsb_id;
+ trq->tsr_concur = test->tes_concur;
+ trq->tsr_is_client = (transop == LST_TRANS_TSBCLIADD) ? 1 : 0;
+ trq->tsr_stop_onerr = !!test->tes_stop_onerr;
+
+ switch (test->tes_type) {
+ case LST_TEST_PING:
+ trq->tsr_service = SRPC_SERVICE_PING;
+ rc = lstcon_pingrpc_prep((lst_test_ping_param_t *)
+ &test->tes_param[0], trq);
+ break;
+
+ case LST_TEST_BULK:
+ trq->tsr_service = SRPC_SERVICE_BRW;
+ if ((feats & LST_FEAT_BULK_LEN) == 0) {
+ rc = lstcon_bulkrpc_v0_prep((lst_test_bulk_param_t *)
+ &test->tes_param[0], trq);
+ } else {
+ rc = lstcon_bulkrpc_v1_prep((lst_test_bulk_param_t *)
+ &test->tes_param[0], trq);
+ }
+
+ break;
+ default:
+ LBUG();
+ break;
+ }
+
+ return rc;
+}
+
+static int
+lstcon_sesnew_stat_reply(lstcon_rpc_trans_t *trans,
+ lstcon_node_t *nd, srpc_msg_t *reply)
+{
+ srpc_mksn_reply_t *mksn_rep = &reply->msg_body.mksn_reply;
+ int status = mksn_rep->mksn_status;
+
+ if (status == 0 &&
+ (reply->msg_ses_feats & ~LST_FEATS_MASK) != 0) {
+ mksn_rep->mksn_status = EPROTO;
+ status = EPROTO;
+ }
+
+ if (status == EPROTO) {
+ CNETERR("session protocol error from %s: %u\n",
+ libcfs_nid2str(nd->nd_id.nid),
+ reply->msg_ses_feats);
+ }
+
+ if (status != 0)
+ return status;
+
+ if (!trans->tas_feats_updated) {
+ trans->tas_feats_updated = 1;
+ trans->tas_features = reply->msg_ses_feats;
+ }
+
+ if (reply->msg_ses_feats != trans->tas_features) {
+ CNETERR("Framework features %x from %s is different with features on this transaction: %x\n",
+ reply->msg_ses_feats, libcfs_nid2str(nd->nd_id.nid),
+ trans->tas_features);
+ status = mksn_rep->mksn_status = EPROTO;
+ }
+
+ if (status == 0) {
+ /* session timeout on remote node */
+ nd->nd_timeout = mksn_rep->mksn_timeout;
+ }
+
+ return status;
+}
+
+void
+lstcon_rpc_stat_reply(lstcon_rpc_trans_t *trans, srpc_msg_t *msg,
+ lstcon_node_t *nd, lstcon_trans_stat_t *stat)
+{
+ srpc_rmsn_reply_t *rmsn_rep;
+ srpc_debug_reply_t *dbg_rep;
+ srpc_batch_reply_t *bat_rep;
+ srpc_test_reply_t *test_rep;
+ srpc_stat_reply_t *stat_rep;
+ int rc = 0;
+
+ switch (trans->tas_opc) {
+ case LST_TRANS_SESNEW:
+ rc = lstcon_sesnew_stat_reply(trans, nd, msg);
+ if (rc == 0) {
+ lstcon_sesop_stat_success(stat, 1);
+ return;
+ }
+
+ lstcon_sesop_stat_failure(stat, 1);
+ break;
+
+ case LST_TRANS_SESEND:
+ rmsn_rep = &msg->msg_body.rmsn_reply;
+ /* ESRCH is not an error for end session */
+ if (rmsn_rep->rmsn_status == 0 ||
+ rmsn_rep->rmsn_status == ESRCH) {
+ lstcon_sesop_stat_success(stat, 1);
+ return;
+ }
+
+ lstcon_sesop_stat_failure(stat, 1);
+ rc = rmsn_rep->rmsn_status;
+ break;
+
+ case LST_TRANS_SESQRY:
+ case LST_TRANS_SESPING:
+ dbg_rep = &msg->msg_body.dbg_reply;
+
+ if (dbg_rep->dbg_status == ESRCH) {
+ lstcon_sesqry_stat_unknown(stat, 1);
+ return;
+ }
+
+ if (lstcon_session_match(dbg_rep->dbg_sid))
+ lstcon_sesqry_stat_active(stat, 1);
+ else
+ lstcon_sesqry_stat_busy(stat, 1);
+ return;
+
+ case LST_TRANS_TSBRUN:
+ case LST_TRANS_TSBSTOP:
+ bat_rep = &msg->msg_body.bat_reply;
+
+ if (bat_rep->bar_status == 0) {
+ lstcon_tsbop_stat_success(stat, 1);
+ return;
+ }
+
+ if (bat_rep->bar_status == EPERM &&
+ trans->tas_opc == LST_TRANS_TSBSTOP) {
+ lstcon_tsbop_stat_success(stat, 1);
+ return;
+ }
+
+ lstcon_tsbop_stat_failure(stat, 1);
+ rc = bat_rep->bar_status;
+ break;
+
+ case LST_TRANS_TSBCLIQRY:
+ case LST_TRANS_TSBSRVQRY:
+ bat_rep = &msg->msg_body.bat_reply;
+
+ if (bat_rep->bar_active != 0)
+ lstcon_tsbqry_stat_run(stat, 1);
+ else
+ lstcon_tsbqry_stat_idle(stat, 1);
+
+ if (bat_rep->bar_status == 0)
+ return;
+
+ lstcon_tsbqry_stat_failure(stat, 1);
+ rc = bat_rep->bar_status;
+ break;
+
+ case LST_TRANS_TSBCLIADD:
+ case LST_TRANS_TSBSRVADD:
+ test_rep = &msg->msg_body.tes_reply;
+
+ if (test_rep->tsr_status == 0) {
+ lstcon_tsbop_stat_success(stat, 1);
+ return;
+ }
+
+ lstcon_tsbop_stat_failure(stat, 1);
+ rc = test_rep->tsr_status;
+ break;
+
+ case LST_TRANS_STATQRY:
+ stat_rep = &msg->msg_body.stat_reply;
+
+ if (stat_rep->str_status == 0) {
+ lstcon_statqry_stat_success(stat, 1);
+ return;
+ }
+
+ lstcon_statqry_stat_failure(stat, 1);
+ rc = stat_rep->str_status;
+ break;
+
+ default:
+ LBUG();
+ }
+
+ if (stat->trs_fwk_errno == 0)
+ stat->trs_fwk_errno = rc;
+
+ return;
+}
+
+int
+lstcon_rpc_trans_ndlist(struct list_head *ndlist,
+ struct list_head *translist, int transop,
+ void *arg, lstcon_rpc_cond_func_t condition,
+ lstcon_rpc_trans_t **transpp)
+{
+ lstcon_rpc_trans_t *trans;
+ lstcon_ndlink_t *ndl;
+ lstcon_node_t *nd;
+ lstcon_rpc_t *rpc;
+ unsigned feats;
+ int rc;
+
+ /* Creating session RPG for list of nodes */
+
+ rc = lstcon_rpc_trans_prep(translist, transop, &trans);
+ if (rc != 0) {
+ CERROR("Can't create transaction %d: %d\n", transop, rc);
+ return rc;
+ }
+
+ feats = trans->tas_features;
+ list_for_each_entry(ndl, ndlist, ndl_link) {
+ rc = condition == NULL ? 1 :
+ condition(transop, ndl->ndl_node, arg);
+
+ if (rc == 0)
+ continue;
+
+ if (rc < 0) {
+ CDEBUG(D_NET, "Condition error while creating RPC for transaction %d: %d\n",
+ transop, rc);
+ break;
+ }
+
+ nd = ndl->ndl_node;
+
+ switch (transop) {
+ case LST_TRANS_SESNEW:
+ case LST_TRANS_SESEND:
+ rc = lstcon_sesrpc_prep(nd, transop, feats, &rpc);
+ break;
+ case LST_TRANS_SESQRY:
+ case LST_TRANS_SESPING:
+ rc = lstcon_dbgrpc_prep(nd, feats, &rpc);
+ break;
+ case LST_TRANS_TSBCLIADD:
+ case LST_TRANS_TSBSRVADD:
+ rc = lstcon_testrpc_prep(nd, transop, feats,
+ (lstcon_test_t *)arg, &rpc);
+ break;
+ case LST_TRANS_TSBRUN:
+ case LST_TRANS_TSBSTOP:
+ case LST_TRANS_TSBCLIQRY:
+ case LST_TRANS_TSBSRVQRY:
+ rc = lstcon_batrpc_prep(nd, transop, feats,
+ (lstcon_tsb_hdr_t *)arg, &rpc);
+ break;
+ case LST_TRANS_STATQRY:
+ rc = lstcon_statrpc_prep(nd, feats, &rpc);
+ break;
+ default:
+ rc = -EINVAL;
+ break;
+ }
+
+ if (rc != 0) {
+ CERROR("Failed to create RPC for transaction %s: %d\n",
+ lstcon_rpc_trans_name(transop), rc);
+ break;
+ }
+
+ lstcon_rpc_trans_addreq(trans, rpc);
+ }
+
+ if (rc == 0) {
+ *transpp = trans;
+ return 0;
+ }
+
+ lstcon_rpc_trans_destroy(trans);
+
+ return rc;
+}
+
+static void
+lstcon_rpc_pinger(void *arg)
+{
+ stt_timer_t *ptimer = (stt_timer_t *)arg;
+ lstcon_rpc_trans_t *trans;
+ lstcon_rpc_t *crpc;
+ srpc_msg_t *rep;
+ srpc_debug_reqst_t *drq;
+ lstcon_ndlink_t *ndl;
+ lstcon_node_t *nd;
+ time_t intv;
+ int count = 0;
+ int rc;
+
+ /* RPC pinger is a special case of transaction,
+ * it's called by timer at 8 seconds interval.
+ */
+ mutex_lock(&console_session.ses_mutex);
+
+ if (console_session.ses_shutdown || console_session.ses_expired) {
+ mutex_unlock(&console_session.ses_mutex);
+ return;
+ }
+
+ if (!console_session.ses_expired &&
+ get_seconds() - console_session.ses_laststamp >
+ (time_t)console_session.ses_timeout)
+ console_session.ses_expired = 1;
+
+ trans = console_session.ses_ping;
+
+ LASSERT(trans != NULL);
+
+ list_for_each_entry(ndl, &console_session.ses_ndl_list, ndl_link) {
+ nd = ndl->ndl_node;
+
+ if (console_session.ses_expired) {
+ /* idle console, end session on all nodes */
+ if (nd->nd_state != LST_NODE_ACTIVE)
+ continue;
+
+ rc = lstcon_sesrpc_prep(nd, LST_TRANS_SESEND,
+ trans->tas_features, &crpc);
+ if (rc != 0) {
+ CERROR("Out of memory\n");
+ break;
+ }
+
+ lstcon_rpc_trans_addreq(trans, crpc);
+ lstcon_rpc_post(crpc);
+
+ continue;
+ }
+
+ crpc = &nd->nd_ping;
+
+ if (crpc->crp_rpc != NULL) {
+ LASSERT(crpc->crp_trans == trans);
+ LASSERT(!list_empty(&crpc->crp_link));
+
+ spin_lock(&crpc->crp_rpc->crpc_lock);
+
+ LASSERT(crpc->crp_posted);
+
+ if (!crpc->crp_finished) {
+ /* in flight */
+ spin_unlock(&crpc->crp_rpc->crpc_lock);
+ continue;
+ }
+
+ spin_unlock(&crpc->crp_rpc->crpc_lock);
+
+ lstcon_rpc_get_reply(crpc, &rep);
+
+ list_del_init(&crpc->crp_link);
+
+ lstcon_rpc_put(crpc);
+ }
+
+ if (nd->nd_state != LST_NODE_ACTIVE)
+ continue;
+
+ intv = cfs_duration_sec(cfs_time_sub(cfs_time_current(),
+ nd->nd_stamp));
+ if (intv < (time_t)nd->nd_timeout / 2)
+ continue;
+
+ rc = lstcon_rpc_init(nd, SRPC_SERVICE_DEBUG,
+ trans->tas_features, 0, 0, 1, crpc);
+ if (rc != 0) {
+ CERROR("Out of memory\n");
+ break;
+ }
+
+ drq = &crpc->crp_rpc->crpc_reqstmsg.msg_body.dbg_reqst;
+
+ drq->dbg_sid = console_session.ses_id;
+ drq->dbg_flags = 0;
+
+ lstcon_rpc_trans_addreq(trans, crpc);
+ lstcon_rpc_post(crpc);
+
+ count++;
+ }
+
+ if (console_session.ses_expired) {
+ mutex_unlock(&console_session.ses_mutex);
+ return;
+ }
+
+ CDEBUG(D_NET, "Ping %d nodes in session\n", count);
+
+ ptimer->stt_expires = (unsigned long)(get_seconds() + LST_PING_INTERVAL);
+ stt_add_timer(ptimer);
+
+ mutex_unlock(&console_session.ses_mutex);
+}
+
+int
+lstcon_rpc_pinger_start(void)
+{
+ stt_timer_t *ptimer;
+ int rc;
+
+ LASSERT(list_empty(&console_session.ses_rpc_freelist));
+ LASSERT(atomic_read(&console_session.ses_rpc_counter) == 0);
+
+ rc = lstcon_rpc_trans_prep(NULL, LST_TRANS_SESPING,
+ &console_session.ses_ping);
+ if (rc != 0) {
+ CERROR("Failed to create console pinger\n");
+ return rc;
+ }
+
+ ptimer = &console_session.ses_ping_timer;
+ ptimer->stt_expires = (unsigned long)(get_seconds() + LST_PING_INTERVAL);
+
+ stt_add_timer(ptimer);
+
+ return 0;
+}
+
+void
+lstcon_rpc_pinger_stop(void)
+{
+ LASSERT(console_session.ses_shutdown);
+
+ stt_del_timer(&console_session.ses_ping_timer);
+
+ lstcon_rpc_trans_abort(console_session.ses_ping, -ESHUTDOWN);
+ lstcon_rpc_trans_stat(console_session.ses_ping, lstcon_trans_stat());
+ lstcon_rpc_trans_destroy(console_session.ses_ping);
+
+ memset(lstcon_trans_stat(), 0, sizeof(lstcon_trans_stat_t));
+
+ console_session.ses_ping = NULL;
+}
+
+void
+lstcon_rpc_cleanup_wait(void)
+{
+ lstcon_rpc_trans_t *trans;
+ lstcon_rpc_t *crpc;
+ struct list_head *pacer;
+ struct list_head zlist;
+
+ /* Called with hold of global mutex */
+
+ LASSERT(console_session.ses_shutdown);
+
+ while (!list_empty(&console_session.ses_trans_list)) {
+ list_for_each(pacer, &console_session.ses_trans_list) {
+ trans = list_entry(pacer, lstcon_rpc_trans_t,
+ tas_link);
+
+ CDEBUG(D_NET, "Session closed, wakeup transaction %s\n",
+ lstcon_rpc_trans_name(trans->tas_opc));
+
+ wake_up(&trans->tas_waitq);
+ }
+
+ mutex_unlock(&console_session.ses_mutex);
+
+ CWARN("Session is shutting down, waiting for termination of transactions\n");
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ schedule_timeout(cfs_time_seconds(1));
+
+ mutex_lock(&console_session.ses_mutex);
+ }
+
+ spin_lock(&console_session.ses_rpc_lock);
+
+ lst_wait_until((atomic_read(&console_session.ses_rpc_counter) == 0),
+ console_session.ses_rpc_lock,
+ "Network is not accessible or target is down, waiting for %d console RPCs to being recycled\n",
+ atomic_read(&console_session.ses_rpc_counter));
+
+ list_add(&zlist, &console_session.ses_rpc_freelist);
+ list_del_init(&console_session.ses_rpc_freelist);
+
+ spin_unlock(&console_session.ses_rpc_lock);
+
+ while (!list_empty(&zlist)) {
+ crpc = list_entry(zlist.next, lstcon_rpc_t, crp_link);
+
+ list_del(&crpc->crp_link);
+ LIBCFS_FREE(crpc, sizeof(lstcon_rpc_t));
+ }
+}
+
+int
+lstcon_rpc_module_init(void)
+{
+ INIT_LIST_HEAD(&console_session.ses_ping_timer.stt_list);
+ console_session.ses_ping_timer.stt_func = lstcon_rpc_pinger;
+ console_session.ses_ping_timer.stt_data = &console_session.ses_ping_timer;
+
+ console_session.ses_ping = NULL;
+
+ spin_lock_init(&console_session.ses_rpc_lock);
+ atomic_set(&console_session.ses_rpc_counter, 0);
+ INIT_LIST_HEAD(&console_session.ses_rpc_freelist);
+
+ return 0;
+}
+
+void
+lstcon_rpc_module_fini(void)
+{
+ LASSERT(list_empty(&console_session.ses_rpc_freelist));
+ LASSERT(atomic_read(&console_session.ses_rpc_counter) == 0);
+}
diff --git a/drivers/staging/lustre/lnet/selftest/conrpc.h b/drivers/staging/lustre/lnet/selftest/conrpc.h
new file mode 100644
index 000000000..2353889c6
--- /dev/null
+++ b/drivers/staging/lustre/lnet/selftest/conrpc.h
@@ -0,0 +1,146 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2011, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ *
+ * /lnet/selftest/conrpc.h
+ *
+ * Console rpc
+ *
+ * Author: Liang Zhen <liang@whamcloud.com>
+ */
+
+#ifndef __LST_CONRPC_H__
+#define __LST_CONRPC_H__
+
+#include "../../include/linux/libcfs/libcfs.h"
+#include "../../include/linux/lnet/lnet.h"
+#include "../../include/linux/lnet/lib-types.h"
+#include "../../include/linux/lnet/lnetst.h"
+#include "rpc.h"
+#include "selftest.h"
+
+/* Console rpc and rpc transaction */
+#define LST_TRANS_TIMEOUT 30
+#define LST_TRANS_MIN_TIMEOUT 3
+
+#define LST_VALIDATE_TIMEOUT(t) min(max(t, LST_TRANS_MIN_TIMEOUT), LST_TRANS_TIMEOUT)
+
+#define LST_PING_INTERVAL 8
+
+struct lstcon_rpc_trans;
+struct lstcon_tsb_hdr;
+struct lstcon_test;
+struct lstcon_node;
+
+typedef struct lstcon_rpc {
+ struct list_head crp_link; /* chain on rpc transaction */
+ srpc_client_rpc_t *crp_rpc; /* client rpc */
+ struct lstcon_node *crp_node; /* destination node */
+ struct lstcon_rpc_trans *crp_trans; /* conrpc transaction */
+
+ unsigned int crp_posted:1; /* rpc is posted */
+ unsigned int crp_finished:1; /* rpc is finished */
+ unsigned int crp_unpacked:1; /* reply is unpacked */
+ /** RPC is embedded in other structure and can't free it */
+ unsigned int crp_embedded:1;
+ int crp_status; /* console rpc errors */
+ unsigned long crp_stamp; /* replied time stamp */
+} lstcon_rpc_t;
+
+typedef struct lstcon_rpc_trans {
+ struct list_head tas_olink; /* link chain on owner list */
+ struct list_head tas_link; /* link chain on global list */
+ int tas_opc; /* operation code of transaction */
+ /* features mask is uptodate */
+ unsigned tas_feats_updated;
+ /* test features mask */
+ unsigned tas_features;
+ wait_queue_head_t tas_waitq; /* wait queue head */
+ atomic_t tas_remaining; /* # of un-scheduled rpcs */
+ struct list_head tas_rpcs_list; /* queued requests */
+} lstcon_rpc_trans_t;
+
+#define LST_TRANS_PRIVATE 0x1000
+
+#define LST_TRANS_SESNEW (LST_TRANS_PRIVATE | 0x01)
+#define LST_TRANS_SESEND (LST_TRANS_PRIVATE | 0x02)
+#define LST_TRANS_SESQRY 0x03
+#define LST_TRANS_SESPING 0x04
+
+#define LST_TRANS_TSBCLIADD (LST_TRANS_PRIVATE | 0x11)
+#define LST_TRANS_TSBSRVADD (LST_TRANS_PRIVATE | 0x12)
+#define LST_TRANS_TSBRUN (LST_TRANS_PRIVATE | 0x13)
+#define LST_TRANS_TSBSTOP (LST_TRANS_PRIVATE | 0x14)
+#define LST_TRANS_TSBCLIQRY 0x15
+#define LST_TRANS_TSBSRVQRY 0x16
+
+#define LST_TRANS_STATQRY 0x21
+
+typedef int (* lstcon_rpc_cond_func_t)(int, struct lstcon_node *, void *);
+typedef int (* lstcon_rpc_readent_func_t)(int, srpc_msg_t *, lstcon_rpc_ent_t *);
+
+int lstcon_sesrpc_prep(struct lstcon_node *nd, int transop,
+ unsigned version, lstcon_rpc_t **crpc);
+int lstcon_dbgrpc_prep(struct lstcon_node *nd,
+ unsigned version, lstcon_rpc_t **crpc);
+int lstcon_batrpc_prep(struct lstcon_node *nd, int transop, unsigned version,
+ struct lstcon_tsb_hdr *tsb, lstcon_rpc_t **crpc);
+int lstcon_testrpc_prep(struct lstcon_node *nd, int transop, unsigned version,
+ struct lstcon_test *test, lstcon_rpc_t **crpc);
+int lstcon_statrpc_prep(struct lstcon_node *nd, unsigned version,
+ lstcon_rpc_t **crpc);
+void lstcon_rpc_put(lstcon_rpc_t *crpc);
+int lstcon_rpc_trans_prep(struct list_head *translist,
+ int transop, lstcon_rpc_trans_t **transpp);
+int lstcon_rpc_trans_ndlist(struct list_head *ndlist,
+ struct list_head *translist, int transop,
+ void *arg, lstcon_rpc_cond_func_t condition,
+ lstcon_rpc_trans_t **transpp);
+void lstcon_rpc_trans_stat(lstcon_rpc_trans_t *trans,
+ lstcon_trans_stat_t *stat);
+int lstcon_rpc_trans_interpreter(lstcon_rpc_trans_t *trans,
+ struct list_head *head_up,
+ lstcon_rpc_readent_func_t readent);
+void lstcon_rpc_trans_abort(lstcon_rpc_trans_t *trans, int error);
+void lstcon_rpc_trans_destroy(lstcon_rpc_trans_t *trans);
+void lstcon_rpc_trans_addreq(lstcon_rpc_trans_t *trans, lstcon_rpc_t *req);
+int lstcon_rpc_trans_postwait(lstcon_rpc_trans_t *trans, int timeout);
+int lstcon_rpc_pinger_start(void);
+void lstcon_rpc_pinger_stop(void);
+void lstcon_rpc_cleanup_wait(void);
+int lstcon_rpc_module_init(void);
+void lstcon_rpc_module_fini(void);
+
+
+#endif
diff --git a/drivers/staging/lustre/lnet/selftest/console.c b/drivers/staging/lustre/lnet/selftest/console.c
new file mode 100644
index 000000000..2b5f53c7a
--- /dev/null
+++ b/drivers/staging/lustre/lnet/selftest/console.c
@@ -0,0 +1,2096 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2012, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ *
+ * lnet/selftest/conctl.c
+ *
+ * Infrastructure of LST console
+ *
+ * Author: Liang Zhen <liangzhen@clusterfs.com>
+ */
+
+
+#include "../../include/linux/libcfs/libcfs.h"
+#include "../../include/linux/lnet/lib-lnet.h"
+#include "console.h"
+#include "conrpc.h"
+
+#define LST_NODE_STATE_COUNTER(nd, p) \
+do { \
+ if ((nd)->nd_state == LST_NODE_ACTIVE) \
+ (p)->nle_nactive++; \
+ else if ((nd)->nd_state == LST_NODE_BUSY) \
+ (p)->nle_nbusy++; \
+ else if ((nd)->nd_state == LST_NODE_DOWN) \
+ (p)->nle_ndown++; \
+ else \
+ (p)->nle_nunknown++; \
+ (p)->nle_nnode++; \
+} while (0)
+
+lstcon_session_t console_session;
+
+static void
+lstcon_node_get(lstcon_node_t *nd)
+{
+ LASSERT(nd->nd_ref >= 1);
+
+ nd->nd_ref++;
+}
+
+static int
+lstcon_node_find(lnet_process_id_t id, lstcon_node_t **ndpp, int create)
+{
+ lstcon_ndlink_t *ndl;
+ unsigned int idx = LNET_NIDADDR(id.nid) % LST_GLOBAL_HASHSIZE;
+
+ LASSERT(id.nid != LNET_NID_ANY);
+
+ list_for_each_entry(ndl, &console_session.ses_ndl_hash[idx], ndl_hlink) {
+ if (ndl->ndl_node->nd_id.nid != id.nid ||
+ ndl->ndl_node->nd_id.pid != id.pid)
+ continue;
+
+ lstcon_node_get(ndl->ndl_node);
+ *ndpp = ndl->ndl_node;
+ return 0;
+ }
+
+ if (!create)
+ return -ENOENT;
+
+ LIBCFS_ALLOC(*ndpp, sizeof(lstcon_node_t) + sizeof(lstcon_ndlink_t));
+ if (*ndpp == NULL)
+ return -ENOMEM;
+
+ ndl = (lstcon_ndlink_t *)(*ndpp + 1);
+
+ ndl->ndl_node = *ndpp;
+
+ ndl->ndl_node->nd_ref = 1;
+ ndl->ndl_node->nd_id = id;
+ ndl->ndl_node->nd_stamp = cfs_time_current();
+ ndl->ndl_node->nd_state = LST_NODE_UNKNOWN;
+ ndl->ndl_node->nd_timeout = 0;
+ memset(&ndl->ndl_node->nd_ping, 0, sizeof(lstcon_rpc_t));
+
+ /* queued in global hash & list, no refcount is taken by
+ * global hash & list, if caller release his refcount,
+ * node will be released */
+ list_add_tail(&ndl->ndl_hlink, &console_session.ses_ndl_hash[idx]);
+ list_add_tail(&ndl->ndl_link, &console_session.ses_ndl_list);
+
+ return 0;
+}
+
+static void
+lstcon_node_put(lstcon_node_t *nd)
+{
+ lstcon_ndlink_t *ndl;
+
+ LASSERT(nd->nd_ref > 0);
+
+ if (--nd->nd_ref > 0)
+ return;
+
+ ndl = (lstcon_ndlink_t *)(nd + 1);
+
+ LASSERT(!list_empty(&ndl->ndl_link));
+ LASSERT(!list_empty(&ndl->ndl_hlink));
+
+ /* remove from session */
+ list_del(&ndl->ndl_link);
+ list_del(&ndl->ndl_hlink);
+
+ LIBCFS_FREE(nd, sizeof(lstcon_node_t) + sizeof(lstcon_ndlink_t));
+}
+
+static int
+lstcon_ndlink_find(struct list_head *hash,
+ lnet_process_id_t id, lstcon_ndlink_t **ndlpp, int create)
+{
+ unsigned int idx = LNET_NIDADDR(id.nid) % LST_NODE_HASHSIZE;
+ lstcon_ndlink_t *ndl;
+ lstcon_node_t *nd;
+ int rc;
+
+ if (id.nid == LNET_NID_ANY)
+ return -EINVAL;
+
+ /* search in hash */
+ list_for_each_entry(ndl, &hash[idx], ndl_hlink) {
+ if (ndl->ndl_node->nd_id.nid != id.nid ||
+ ndl->ndl_node->nd_id.pid != id.pid)
+ continue;
+
+ *ndlpp = ndl;
+ return 0;
+ }
+
+ if (create == 0)
+ return -ENOENT;
+
+ /* find or create in session hash */
+ rc = lstcon_node_find(id, &nd, (create == 1) ? 1 : 0);
+ if (rc != 0)
+ return rc;
+
+ LIBCFS_ALLOC(ndl, sizeof(lstcon_ndlink_t));
+ if (ndl == NULL) {
+ lstcon_node_put(nd);
+ return -ENOMEM;
+ }
+
+ *ndlpp = ndl;
+
+ ndl->ndl_node = nd;
+ INIT_LIST_HEAD(&ndl->ndl_link);
+ list_add_tail(&ndl->ndl_hlink, &hash[idx]);
+
+ return 0;
+}
+
+static void
+lstcon_ndlink_release(lstcon_ndlink_t *ndl)
+{
+ LASSERT(list_empty(&ndl->ndl_link));
+ LASSERT(!list_empty(&ndl->ndl_hlink));
+
+ list_del(&ndl->ndl_hlink); /* delete from hash */
+ lstcon_node_put(ndl->ndl_node);
+
+ LIBCFS_FREE(ndl, sizeof(*ndl));
+}
+
+static int
+lstcon_group_alloc(char *name, lstcon_group_t **grpp)
+{
+ lstcon_group_t *grp;
+ int i;
+
+ LIBCFS_ALLOC(grp, offsetof(lstcon_group_t,
+ grp_ndl_hash[LST_NODE_HASHSIZE]));
+ if (grp == NULL)
+ return -ENOMEM;
+
+ grp->grp_ref = 1;
+ if (name != NULL)
+ strcpy(grp->grp_name, name);
+
+ INIT_LIST_HEAD(&grp->grp_link);
+ INIT_LIST_HEAD(&grp->grp_ndl_list);
+ INIT_LIST_HEAD(&grp->grp_trans_list);
+
+ for (i = 0; i < LST_NODE_HASHSIZE; i++)
+ INIT_LIST_HEAD(&grp->grp_ndl_hash[i]);
+
+ *grpp = grp;
+
+ return 0;
+}
+
+static void
+lstcon_group_addref(lstcon_group_t *grp)
+{
+ grp->grp_ref++;
+}
+
+static void lstcon_group_ndlink_release(lstcon_group_t *, lstcon_ndlink_t *);
+
+static void
+lstcon_group_drain(lstcon_group_t *grp, int keep)
+{
+ lstcon_ndlink_t *ndl;
+ lstcon_ndlink_t *tmp;
+
+ list_for_each_entry_safe(ndl, tmp, &grp->grp_ndl_list, ndl_link) {
+ if ((ndl->ndl_node->nd_state & keep) == 0)
+ lstcon_group_ndlink_release(grp, ndl);
+ }
+}
+
+static void
+lstcon_group_decref(lstcon_group_t *grp)
+{
+ int i;
+
+ if (--grp->grp_ref > 0)
+ return;
+
+ if (!list_empty(&grp->grp_link))
+ list_del(&grp->grp_link);
+
+ lstcon_group_drain(grp, 0);
+
+ for (i = 0; i < LST_NODE_HASHSIZE; i++) {
+ LASSERT(list_empty(&grp->grp_ndl_hash[i]));
+ }
+
+ LIBCFS_FREE(grp, offsetof(lstcon_group_t,
+ grp_ndl_hash[LST_NODE_HASHSIZE]));
+}
+
+static int
+lstcon_group_find(const char *name, lstcon_group_t **grpp)
+{
+ lstcon_group_t *grp;
+
+ list_for_each_entry(grp, &console_session.ses_grp_list, grp_link) {
+ if (strncmp(grp->grp_name, name, LST_NAME_SIZE) != 0)
+ continue;
+
+ lstcon_group_addref(grp); /* +1 ref for caller */
+ *grpp = grp;
+ return 0;
+ }
+
+ return -ENOENT;
+}
+
+static void
+lstcon_group_put(lstcon_group_t *grp)
+{
+ lstcon_group_decref(grp);
+}
+
+static int
+lstcon_group_ndlink_find(lstcon_group_t *grp, lnet_process_id_t id,
+ lstcon_ndlink_t **ndlpp, int create)
+{
+ int rc;
+
+ rc = lstcon_ndlink_find(&grp->grp_ndl_hash[0], id, ndlpp, create);
+ if (rc != 0)
+ return rc;
+
+ if (!list_empty(&(*ndlpp)->ndl_link))
+ return 0;
+
+ list_add_tail(&(*ndlpp)->ndl_link, &grp->grp_ndl_list);
+ grp->grp_nnode++;
+
+ return 0;
+}
+
+static void
+lstcon_group_ndlink_release(lstcon_group_t *grp, lstcon_ndlink_t *ndl)
+{
+ list_del_init(&ndl->ndl_link);
+ lstcon_ndlink_release(ndl);
+ grp->grp_nnode --;
+}
+
+static void
+lstcon_group_ndlink_move(lstcon_group_t *old,
+ lstcon_group_t *new, lstcon_ndlink_t *ndl)
+{
+ unsigned int idx = LNET_NIDADDR(ndl->ndl_node->nd_id.nid) %
+ LST_NODE_HASHSIZE;
+
+ list_del(&ndl->ndl_hlink);
+ list_del(&ndl->ndl_link);
+ old->grp_nnode --;
+
+ list_add_tail(&ndl->ndl_hlink, &new->grp_ndl_hash[idx]);
+ list_add_tail(&ndl->ndl_link, &new->grp_ndl_list);
+ new->grp_nnode++;
+
+ return;
+}
+
+static void
+lstcon_group_move(lstcon_group_t *old, lstcon_group_t *new)
+{
+ lstcon_ndlink_t *ndl;
+
+ while (!list_empty(&old->grp_ndl_list)) {
+ ndl = list_entry(old->grp_ndl_list.next,
+ lstcon_ndlink_t, ndl_link);
+ lstcon_group_ndlink_move(old, new, ndl);
+ }
+}
+
+static int
+lstcon_sesrpc_condition(int transop, lstcon_node_t *nd, void *arg)
+{
+ lstcon_group_t *grp = (lstcon_group_t *)arg;
+
+ switch (transop) {
+ case LST_TRANS_SESNEW:
+ if (nd->nd_state == LST_NODE_ACTIVE)
+ return 0;
+ break;
+
+ case LST_TRANS_SESEND:
+ if (nd->nd_state != LST_NODE_ACTIVE)
+ return 0;
+
+ if (grp != NULL && nd->nd_ref > 1)
+ return 0;
+ break;
+
+ case LST_TRANS_SESQRY:
+ break;
+
+ default:
+ LBUG();
+ }
+
+ return 1;
+}
+
+static int
+lstcon_sesrpc_readent(int transop, srpc_msg_t *msg,
+ lstcon_rpc_ent_t *ent_up)
+{
+ srpc_debug_reply_t *rep;
+
+ switch (transop) {
+ case LST_TRANS_SESNEW:
+ case LST_TRANS_SESEND:
+ return 0;
+
+ case LST_TRANS_SESQRY:
+ rep = &msg->msg_body.dbg_reply;
+
+ if (copy_to_user(&ent_up->rpe_priv[0],
+ &rep->dbg_timeout, sizeof(int)) ||
+ copy_to_user(&ent_up->rpe_payload[0],
+ &rep->dbg_name, LST_NAME_SIZE))
+ return -EFAULT;
+
+ return 0;
+
+ default:
+ LBUG();
+ }
+
+ return 0;
+}
+
+static int
+lstcon_group_nodes_add(lstcon_group_t *grp,
+ int count, lnet_process_id_t *ids_up,
+ unsigned *featp, struct list_head *result_up)
+{
+ lstcon_rpc_trans_t *trans;
+ lstcon_ndlink_t *ndl;
+ lstcon_group_t *tmp;
+ lnet_process_id_t id;
+ int i;
+ int rc;
+
+ rc = lstcon_group_alloc(NULL, &tmp);
+ if (rc != 0) {
+ CERROR("Out of memory\n");
+ return -ENOMEM;
+ }
+
+ for (i = 0 ; i < count; i++) {
+ if (copy_from_user(&id, &ids_up[i], sizeof(id))) {
+ rc = -EFAULT;
+ break;
+ }
+
+ /* skip if it's in this group already */
+ rc = lstcon_group_ndlink_find(grp, id, &ndl, 0);
+ if (rc == 0)
+ continue;
+
+ /* add to tmp group */
+ rc = lstcon_group_ndlink_find(tmp, id, &ndl, 1);
+ if (rc != 0) {
+ CERROR("Can't create ndlink, out of memory\n");
+ break;
+ }
+ }
+
+ if (rc != 0) {
+ lstcon_group_put(tmp);
+ return rc;
+ }
+
+ rc = lstcon_rpc_trans_ndlist(&tmp->grp_ndl_list,
+ &tmp->grp_trans_list, LST_TRANS_SESNEW,
+ tmp, lstcon_sesrpc_condition, &trans);
+ if (rc != 0) {
+ CERROR("Can't create transaction: %d\n", rc);
+ lstcon_group_put(tmp);
+ return rc;
+ }
+
+ /* post all RPCs */
+ lstcon_rpc_trans_postwait(trans, LST_TRANS_TIMEOUT);
+
+ rc = lstcon_rpc_trans_interpreter(trans, result_up,
+ lstcon_sesrpc_readent);
+ *featp = trans->tas_features;
+
+ /* destroy all RPGs */
+ lstcon_rpc_trans_destroy(trans);
+
+ lstcon_group_move(tmp, grp);
+ lstcon_group_put(tmp);
+
+ return rc;
+}
+
+static int
+lstcon_group_nodes_remove(lstcon_group_t *grp,
+ int count, lnet_process_id_t *ids_up,
+ struct list_head *result_up)
+{
+ lstcon_rpc_trans_t *trans;
+ lstcon_ndlink_t *ndl;
+ lstcon_group_t *tmp;
+ lnet_process_id_t id;
+ int rc;
+ int i;
+
+ /* End session and remove node from the group */
+
+ rc = lstcon_group_alloc(NULL, &tmp);
+ if (rc != 0) {
+ CERROR("Out of memory\n");
+ return -ENOMEM;
+ }
+
+ for (i = 0; i < count; i++) {
+ if (copy_from_user(&id, &ids_up[i], sizeof(id))) {
+ rc = -EFAULT;
+ goto error;
+ }
+
+ /* move node to tmp group */
+ if (lstcon_group_ndlink_find(grp, id, &ndl, 0) == 0)
+ lstcon_group_ndlink_move(grp, tmp, ndl);
+ }
+
+ rc = lstcon_rpc_trans_ndlist(&tmp->grp_ndl_list,
+ &tmp->grp_trans_list, LST_TRANS_SESEND,
+ tmp, lstcon_sesrpc_condition, &trans);
+ if (rc != 0) {
+ CERROR("Can't create transaction: %d\n", rc);
+ goto error;
+ }
+
+ lstcon_rpc_trans_postwait(trans, LST_TRANS_TIMEOUT);
+
+ rc = lstcon_rpc_trans_interpreter(trans, result_up, NULL);
+
+ lstcon_rpc_trans_destroy(trans);
+ /* release nodes anyway, because we can't rollback status */
+ lstcon_group_put(tmp);
+
+ return rc;
+error:
+ lstcon_group_move(tmp, grp);
+ lstcon_group_put(tmp);
+
+ return rc;
+}
+
+int
+lstcon_group_add(char *name)
+{
+ lstcon_group_t *grp;
+ int rc;
+
+ rc = (lstcon_group_find(name, &grp) == 0)? -EEXIST: 0;
+ if (rc != 0) {
+ /* find a group with same name */
+ lstcon_group_put(grp);
+ return rc;
+ }
+
+ rc = lstcon_group_alloc(name, &grp);
+ if (rc != 0) {
+ CERROR("Can't allocate descriptor for group %s\n", name);
+ return -ENOMEM;
+ }
+
+ list_add_tail(&grp->grp_link, &console_session.ses_grp_list);
+
+ return rc;
+}
+
+int
+lstcon_nodes_add(char *name, int count, lnet_process_id_t *ids_up,
+ unsigned *featp, struct list_head *result_up)
+{
+ lstcon_group_t *grp;
+ int rc;
+
+ LASSERT(count > 0);
+ LASSERT(ids_up != NULL);
+
+ rc = lstcon_group_find(name, &grp);
+ if (rc != 0) {
+ CDEBUG(D_NET, "Can't find group %s\n", name);
+ return rc;
+ }
+
+ if (grp->grp_ref > 2) {
+ /* referred by other threads or test */
+ CDEBUG(D_NET, "Group %s is busy\n", name);
+ lstcon_group_put(grp);
+
+ return -EBUSY;
+ }
+
+ rc = lstcon_group_nodes_add(grp, count, ids_up, featp, result_up);
+
+ lstcon_group_put(grp);
+
+ return rc;
+}
+
+int
+lstcon_group_del(char *name)
+{
+ lstcon_rpc_trans_t *trans;
+ lstcon_group_t *grp;
+ int rc;
+
+ rc = lstcon_group_find(name, &grp);
+ if (rc != 0) {
+ CDEBUG(D_NET, "Can't find group: %s\n", name);
+ return rc;
+ }
+
+ if (grp->grp_ref > 2) {
+ /* referred by others threads or test */
+ CDEBUG(D_NET, "Group %s is busy\n", name);
+ lstcon_group_put(grp);
+ return -EBUSY;
+ }
+
+ rc = lstcon_rpc_trans_ndlist(&grp->grp_ndl_list,
+ &grp->grp_trans_list, LST_TRANS_SESEND,
+ grp, lstcon_sesrpc_condition, &trans);
+ if (rc != 0) {
+ CERROR("Can't create transaction: %d\n", rc);
+ lstcon_group_put(grp);
+ return rc;
+ }
+
+ lstcon_rpc_trans_postwait(trans, LST_TRANS_TIMEOUT);
+
+ lstcon_rpc_trans_destroy(trans);
+
+ lstcon_group_put(grp);
+ /* -ref for session, it's destroyed,
+ * status can't be rolled back, destroy group anyway */
+ lstcon_group_put(grp);
+
+ return rc;
+}
+
+int
+lstcon_group_clean(char *name, int args)
+{
+ lstcon_group_t *grp = NULL;
+ int rc;
+
+ rc = lstcon_group_find(name, &grp);
+ if (rc != 0) {
+ CDEBUG(D_NET, "Can't find group %s\n", name);
+ return rc;
+ }
+
+ if (grp->grp_ref > 2) {
+ /* referred by test */
+ CDEBUG(D_NET, "Group %s is busy\n", name);
+ lstcon_group_put(grp);
+ return -EBUSY;
+ }
+
+ args = (LST_NODE_ACTIVE | LST_NODE_BUSY |
+ LST_NODE_DOWN | LST_NODE_UNKNOWN) & ~args;
+
+ lstcon_group_drain(grp, args);
+
+ lstcon_group_put(grp);
+ /* release empty group */
+ if (list_empty(&grp->grp_ndl_list))
+ lstcon_group_put(grp);
+
+ return 0;
+}
+
+int
+lstcon_nodes_remove(char *name, int count,
+ lnet_process_id_t *ids_up, struct list_head *result_up)
+{
+ lstcon_group_t *grp = NULL;
+ int rc;
+
+ rc = lstcon_group_find(name, &grp);
+ if (rc != 0) {
+ CDEBUG(D_NET, "Can't find group: %s\n", name);
+ return rc;
+ }
+
+ if (grp->grp_ref > 2) {
+ /* referred by test */
+ CDEBUG(D_NET, "Group %s is busy\n", name);
+ lstcon_group_put(grp);
+ return -EBUSY;
+ }
+
+ rc = lstcon_group_nodes_remove(grp, count, ids_up, result_up);
+
+ lstcon_group_put(grp);
+ /* release empty group */
+ if (list_empty(&grp->grp_ndl_list))
+ lstcon_group_put(grp);
+
+ return rc;
+}
+
+int
+lstcon_group_refresh(char *name, struct list_head *result_up)
+{
+ lstcon_rpc_trans_t *trans;
+ lstcon_group_t *grp;
+ int rc;
+
+ rc = lstcon_group_find(name, &grp);
+ if (rc != 0) {
+ CDEBUG(D_NET, "Can't find group: %s\n", name);
+ return rc;
+ }
+
+ if (grp->grp_ref > 2) {
+ /* referred by test */
+ CDEBUG(D_NET, "Group %s is busy\n", name);
+ lstcon_group_put(grp);
+ return -EBUSY;
+ }
+
+ /* re-invite all inactive nodes int the group */
+ rc = lstcon_rpc_trans_ndlist(&grp->grp_ndl_list,
+ &grp->grp_trans_list, LST_TRANS_SESNEW,
+ grp, lstcon_sesrpc_condition, &trans);
+ if (rc != 0) {
+ /* local error, return */
+ CDEBUG(D_NET, "Can't create transaction: %d\n", rc);
+ lstcon_group_put(grp);
+ return rc;
+ }
+
+ lstcon_rpc_trans_postwait(trans, LST_TRANS_TIMEOUT);
+
+ rc = lstcon_rpc_trans_interpreter(trans, result_up, NULL);
+
+ lstcon_rpc_trans_destroy(trans);
+ /* -ref for me */
+ lstcon_group_put(grp);
+
+ return rc;
+}
+
+int
+lstcon_group_list(int index, int len, char *name_up)
+{
+ lstcon_group_t *grp;
+
+ LASSERT(index >= 0);
+ LASSERT(name_up != NULL);
+
+ list_for_each_entry(grp, &console_session.ses_grp_list, grp_link) {
+ if (index-- == 0) {
+ return copy_to_user(name_up, grp->grp_name, len) ?
+ -EFAULT : 0;
+ }
+ }
+
+ return -ENOENT;
+}
+
+static int
+lstcon_nodes_getent(struct list_head *head, int *index_p,
+ int *count_p, lstcon_node_ent_t *dents_up)
+{
+ lstcon_ndlink_t *ndl;
+ lstcon_node_t *nd;
+ int count = 0;
+ int index = 0;
+
+ LASSERT(index_p != NULL && count_p != NULL);
+ LASSERT(dents_up != NULL);
+ LASSERT(*index_p >= 0);
+ LASSERT(*count_p > 0);
+
+ list_for_each_entry(ndl, head, ndl_link) {
+ if (index++ < *index_p)
+ continue;
+
+ if (count >= *count_p)
+ break;
+
+ nd = ndl->ndl_node;
+ if (copy_to_user(&dents_up[count].nde_id,
+ &nd->nd_id, sizeof(nd->nd_id)) ||
+ copy_to_user(&dents_up[count].nde_state,
+ &nd->nd_state, sizeof(nd->nd_state)))
+ return -EFAULT;
+
+ count++;
+ }
+
+ if (index <= *index_p)
+ return -ENOENT;
+
+ *count_p = count;
+ *index_p = index;
+
+ return 0;
+}
+
+int
+lstcon_group_info(char *name, lstcon_ndlist_ent_t *gents_p,
+ int *index_p, int *count_p, lstcon_node_ent_t *dents_up)
+{
+ lstcon_ndlist_ent_t *gentp;
+ lstcon_group_t *grp;
+ lstcon_ndlink_t *ndl;
+ int rc;
+
+ rc = lstcon_group_find(name, &grp);
+ if (rc != 0) {
+ CDEBUG(D_NET, "Can't find group %s\n", name);
+ return rc;
+ }
+
+ if (dents_up) {
+ /* verbose query */
+ rc = lstcon_nodes_getent(&grp->grp_ndl_list,
+ index_p, count_p, dents_up);
+ lstcon_group_put(grp);
+
+ return rc;
+ }
+
+ /* non-verbose query */
+ LIBCFS_ALLOC(gentp, sizeof(lstcon_ndlist_ent_t));
+ if (gentp == NULL) {
+ CERROR("Can't allocate ndlist_ent\n");
+ lstcon_group_put(grp);
+
+ return -ENOMEM;
+ }
+
+ list_for_each_entry(ndl, &grp->grp_ndl_list, ndl_link)
+ LST_NODE_STATE_COUNTER(ndl->ndl_node, gentp);
+
+ rc = copy_to_user(gents_p, gentp,
+ sizeof(lstcon_ndlist_ent_t)) ? -EFAULT: 0;
+
+ LIBCFS_FREE(gentp, sizeof(lstcon_ndlist_ent_t));
+
+ lstcon_group_put(grp);
+
+ return 0;
+}
+
+static int
+lstcon_batch_find(const char *name, lstcon_batch_t **batpp)
+{
+ lstcon_batch_t *bat;
+
+ list_for_each_entry(bat, &console_session.ses_bat_list, bat_link) {
+ if (strncmp(bat->bat_name, name, LST_NAME_SIZE) == 0) {
+ *batpp = bat;
+ return 0;
+ }
+ }
+
+ return -ENOENT;
+}
+
+int
+lstcon_batch_add(char *name)
+{
+ lstcon_batch_t *bat;
+ int i;
+ int rc;
+
+ rc = (lstcon_batch_find(name, &bat) == 0)? -EEXIST: 0;
+ if (rc != 0) {
+ CDEBUG(D_NET, "Batch %s already exists\n", name);
+ return rc;
+ }
+
+ LIBCFS_ALLOC(bat, sizeof(lstcon_batch_t));
+ if (bat == NULL) {
+ CERROR("Can't allocate descriptor for batch %s\n", name);
+ return -ENOMEM;
+ }
+
+ LIBCFS_ALLOC(bat->bat_cli_hash,
+ sizeof(struct list_head) * LST_NODE_HASHSIZE);
+ if (bat->bat_cli_hash == NULL) {
+ CERROR("Can't allocate hash for batch %s\n", name);
+ LIBCFS_FREE(bat, sizeof(lstcon_batch_t));
+
+ return -ENOMEM;
+ }
+
+ LIBCFS_ALLOC(bat->bat_srv_hash,
+ sizeof(struct list_head) * LST_NODE_HASHSIZE);
+ if (bat->bat_srv_hash == NULL) {
+ CERROR("Can't allocate hash for batch %s\n", name);
+ LIBCFS_FREE(bat->bat_cli_hash, LST_NODE_HASHSIZE);
+ LIBCFS_FREE(bat, sizeof(lstcon_batch_t));
+
+ return -ENOMEM;
+ }
+
+ strcpy(bat->bat_name, name);
+ bat->bat_hdr.tsb_index = 0;
+ bat->bat_hdr.tsb_id.bat_id = ++console_session.ses_id_cookie;
+
+ bat->bat_ntest = 0;
+ bat->bat_state = LST_BATCH_IDLE;
+
+ INIT_LIST_HEAD(&bat->bat_cli_list);
+ INIT_LIST_HEAD(&bat->bat_srv_list);
+ INIT_LIST_HEAD(&bat->bat_test_list);
+ INIT_LIST_HEAD(&bat->bat_trans_list);
+
+ for (i = 0; i < LST_NODE_HASHSIZE; i++) {
+ INIT_LIST_HEAD(&bat->bat_cli_hash[i]);
+ INIT_LIST_HEAD(&bat->bat_srv_hash[i]);
+ }
+
+ list_add_tail(&bat->bat_link, &console_session.ses_bat_list);
+
+ return rc;
+}
+
+int
+lstcon_batch_list(int index, int len, char *name_up)
+{
+ lstcon_batch_t *bat;
+
+ LASSERT(name_up != NULL);
+ LASSERT(index >= 0);
+
+ list_for_each_entry(bat, &console_session.ses_bat_list, bat_link) {
+ if (index-- == 0) {
+ return copy_to_user(name_up, bat->bat_name, len) ?
+ -EFAULT: 0;
+ }
+ }
+
+ return -ENOENT;
+}
+
+int
+lstcon_batch_info(char *name, lstcon_test_batch_ent_t *ent_up, int server,
+ int testidx, int *index_p, int *ndent_p,
+ lstcon_node_ent_t *dents_up)
+{
+ lstcon_test_batch_ent_t *entp;
+ struct list_head *clilst;
+ struct list_head *srvlst;
+ lstcon_test_t *test = NULL;
+ lstcon_batch_t *bat;
+ lstcon_ndlink_t *ndl;
+ int rc;
+
+ rc = lstcon_batch_find(name, &bat);
+ if (rc != 0) {
+ CDEBUG(D_NET, "Can't find batch %s\n", name);
+ return -ENOENT;
+ }
+
+ if (testidx > 0) {
+ /* query test, test index start from 1 */
+ list_for_each_entry(test, &bat->bat_test_list, tes_link) {
+ if (testidx-- == 1)
+ break;
+ }
+
+ if (testidx > 0) {
+ CDEBUG(D_NET, "Can't find specified test in batch\n");
+ return -ENOENT;
+ }
+ }
+
+ clilst = (test == NULL) ? &bat->bat_cli_list :
+ &test->tes_src_grp->grp_ndl_list;
+ srvlst = (test == NULL) ? &bat->bat_srv_list :
+ &test->tes_dst_grp->grp_ndl_list;
+
+ if (dents_up != NULL) {
+ rc = lstcon_nodes_getent((server ? srvlst: clilst),
+ index_p, ndent_p, dents_up);
+ return rc;
+ }
+
+ /* non-verbose query */
+ LIBCFS_ALLOC(entp, sizeof(lstcon_test_batch_ent_t));
+ if (entp == NULL)
+ return -ENOMEM;
+
+ if (test == NULL) {
+ entp->u.tbe_batch.bae_ntest = bat->bat_ntest;
+ entp->u.tbe_batch.bae_state = bat->bat_state;
+
+ } else {
+
+ entp->u.tbe_test.tse_type = test->tes_type;
+ entp->u.tbe_test.tse_loop = test->tes_loop;
+ entp->u.tbe_test.tse_concur = test->tes_concur;
+ }
+
+ list_for_each_entry(ndl, clilst, ndl_link)
+ LST_NODE_STATE_COUNTER(ndl->ndl_node, &entp->tbe_cli_nle);
+
+ list_for_each_entry(ndl, srvlst, ndl_link)
+ LST_NODE_STATE_COUNTER(ndl->ndl_node, &entp->tbe_srv_nle);
+
+ rc = copy_to_user(ent_up, entp,
+ sizeof(lstcon_test_batch_ent_t)) ? -EFAULT : 0;
+
+ LIBCFS_FREE(entp, sizeof(lstcon_test_batch_ent_t));
+
+ return rc;
+}
+
+static int
+lstcon_batrpc_condition(int transop, lstcon_node_t *nd, void *arg)
+{
+ switch (transop) {
+ case LST_TRANS_TSBRUN:
+ if (nd->nd_state != LST_NODE_ACTIVE)
+ return -ENETDOWN;
+ break;
+
+ case LST_TRANS_TSBSTOP:
+ if (nd->nd_state != LST_NODE_ACTIVE)
+ return 0;
+ break;
+
+ case LST_TRANS_TSBCLIQRY:
+ case LST_TRANS_TSBSRVQRY:
+ break;
+ }
+
+ return 1;
+}
+
+static int
+lstcon_batch_op(lstcon_batch_t *bat, int transop,
+ struct list_head *result_up)
+{
+ lstcon_rpc_trans_t *trans;
+ int rc;
+
+ rc = lstcon_rpc_trans_ndlist(&bat->bat_cli_list,
+ &bat->bat_trans_list, transop,
+ bat, lstcon_batrpc_condition, &trans);
+ if (rc != 0) {
+ CERROR("Can't create transaction: %d\n", rc);
+ return rc;
+ }
+
+ lstcon_rpc_trans_postwait(trans, LST_TRANS_TIMEOUT);
+
+ rc = lstcon_rpc_trans_interpreter(trans, result_up, NULL);
+
+ lstcon_rpc_trans_destroy(trans);
+
+ return rc;
+}
+
+int
+lstcon_batch_run(char *name, int timeout, struct list_head *result_up)
+{
+ lstcon_batch_t *bat;
+ int rc;
+
+ if (lstcon_batch_find(name, &bat) != 0) {
+ CDEBUG(D_NET, "Can't find batch %s\n", name);
+ return -ENOENT;
+ }
+
+ bat->bat_arg = timeout;
+
+ rc = lstcon_batch_op(bat, LST_TRANS_TSBRUN, result_up);
+
+ /* mark batch as running if it's started in any node */
+ if (lstcon_tsbop_stat_success(lstcon_trans_stat(), 0) != 0)
+ bat->bat_state = LST_BATCH_RUNNING;
+
+ return rc;
+}
+
+int
+lstcon_batch_stop(char *name, int force, struct list_head *result_up)
+{
+ lstcon_batch_t *bat;
+ int rc;
+
+ if (lstcon_batch_find(name, &bat) != 0) {
+ CDEBUG(D_NET, "Can't find batch %s\n", name);
+ return -ENOENT;
+ }
+
+ bat->bat_arg = force;
+
+ rc = lstcon_batch_op(bat, LST_TRANS_TSBSTOP, result_up);
+
+ /* mark batch as stopped if all RPCs finished */
+ if (lstcon_tsbop_stat_failure(lstcon_trans_stat(), 0) == 0)
+ bat->bat_state = LST_BATCH_IDLE;
+
+ return rc;
+}
+
+static void
+lstcon_batch_destroy(lstcon_batch_t *bat)
+{
+ lstcon_ndlink_t *ndl;
+ lstcon_test_t *test;
+ int i;
+
+ list_del(&bat->bat_link);
+
+ while (!list_empty(&bat->bat_test_list)) {
+ test = list_entry(bat->bat_test_list.next,
+ lstcon_test_t, tes_link);
+ LASSERT(list_empty(&test->tes_trans_list));
+
+ list_del(&test->tes_link);
+
+ lstcon_group_put(test->tes_src_grp);
+ lstcon_group_put(test->tes_dst_grp);
+
+ LIBCFS_FREE(test, offsetof(lstcon_test_t,
+ tes_param[test->tes_paramlen]));
+ }
+
+ LASSERT(list_empty(&bat->bat_trans_list));
+
+ while (!list_empty(&bat->bat_cli_list)) {
+ ndl = list_entry(bat->bat_cli_list.next,
+ lstcon_ndlink_t, ndl_link);
+ list_del_init(&ndl->ndl_link);
+
+ lstcon_ndlink_release(ndl);
+ }
+
+ while (!list_empty(&bat->bat_srv_list)) {
+ ndl = list_entry(bat->bat_srv_list.next,
+ lstcon_ndlink_t, ndl_link);
+ list_del_init(&ndl->ndl_link);
+
+ lstcon_ndlink_release(ndl);
+ }
+
+ for (i = 0; i < LST_NODE_HASHSIZE; i++) {
+ LASSERT(list_empty(&bat->bat_cli_hash[i]));
+ LASSERT(list_empty(&bat->bat_srv_hash[i]));
+ }
+
+ LIBCFS_FREE(bat->bat_cli_hash,
+ sizeof(struct list_head) * LST_NODE_HASHSIZE);
+ LIBCFS_FREE(bat->bat_srv_hash,
+ sizeof(struct list_head) * LST_NODE_HASHSIZE);
+ LIBCFS_FREE(bat, sizeof(lstcon_batch_t));
+}
+
+static int
+lstcon_testrpc_condition(int transop, lstcon_node_t *nd, void *arg)
+{
+ lstcon_test_t *test;
+ lstcon_batch_t *batch;
+ lstcon_ndlink_t *ndl;
+ struct list_head *hash;
+ struct list_head *head;
+
+ test = (lstcon_test_t *)arg;
+ LASSERT(test != NULL);
+
+ batch = test->tes_batch;
+ LASSERT(batch != NULL);
+
+ if (test->tes_oneside &&
+ transop == LST_TRANS_TSBSRVADD)
+ return 0;
+
+ if (nd->nd_state != LST_NODE_ACTIVE)
+ return -ENETDOWN;
+
+ if (transop == LST_TRANS_TSBCLIADD) {
+ hash = batch->bat_cli_hash;
+ head = &batch->bat_cli_list;
+
+ } else {
+ LASSERT(transop == LST_TRANS_TSBSRVADD);
+
+ hash = batch->bat_srv_hash;
+ head = &batch->bat_srv_list;
+ }
+
+ LASSERT(nd->nd_id.nid != LNET_NID_ANY);
+
+ if (lstcon_ndlink_find(hash, nd->nd_id, &ndl, 1) != 0)
+ return -ENOMEM;
+
+ if (list_empty(&ndl->ndl_link))
+ list_add_tail(&ndl->ndl_link, head);
+
+ return 1;
+}
+
+static int
+lstcon_test_nodes_add(lstcon_test_t *test, struct list_head *result_up)
+{
+ lstcon_rpc_trans_t *trans;
+ lstcon_group_t *grp;
+ int transop;
+ int rc;
+
+ LASSERT(test->tes_src_grp != NULL);
+ LASSERT(test->tes_dst_grp != NULL);
+
+ transop = LST_TRANS_TSBSRVADD;
+ grp = test->tes_dst_grp;
+again:
+ rc = lstcon_rpc_trans_ndlist(&grp->grp_ndl_list,
+ &test->tes_trans_list, transop,
+ test, lstcon_testrpc_condition, &trans);
+ if (rc != 0) {
+ CERROR("Can't create transaction: %d\n", rc);
+ return rc;
+ }
+
+ lstcon_rpc_trans_postwait(trans, LST_TRANS_TIMEOUT);
+
+ if (lstcon_trans_stat()->trs_rpc_errno != 0 ||
+ lstcon_trans_stat()->trs_fwk_errno != 0) {
+ lstcon_rpc_trans_interpreter(trans, result_up, NULL);
+
+ lstcon_rpc_trans_destroy(trans);
+ /* return if any error */
+ CDEBUG(D_NET, "Failed to add test %s, RPC error %d, framework error %d\n",
+ transop == LST_TRANS_TSBCLIADD ? "client" : "server",
+ lstcon_trans_stat()->trs_rpc_errno,
+ lstcon_trans_stat()->trs_fwk_errno);
+
+ return rc;
+ }
+
+ lstcon_rpc_trans_destroy(trans);
+
+ if (transop == LST_TRANS_TSBCLIADD)
+ return rc;
+
+ transop = LST_TRANS_TSBCLIADD;
+ grp = test->tes_src_grp;
+ test->tes_cliidx = 0;
+
+ /* requests to test clients */
+ goto again;
+}
+
+static int
+lstcon_verify_batch(const char *name, lstcon_batch_t **batch)
+{
+ int rc;
+
+ rc = lstcon_batch_find(name, batch);
+ if (rc != 0) {
+ CDEBUG(D_NET, "Can't find batch %s\n", name);
+ return rc;
+ }
+
+ if ((*batch)->bat_state != LST_BATCH_IDLE) {
+ CDEBUG(D_NET, "Can't change running batch %s\n", name);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int
+lstcon_verify_group(const char *name, lstcon_group_t **grp)
+{
+ int rc;
+ lstcon_ndlink_t *ndl;
+
+ rc = lstcon_group_find(name, grp);
+ if (rc != 0) {
+ CDEBUG(D_NET, "can't find group %s\n", name);
+ return rc;
+ }
+
+ list_for_each_entry(ndl, &(*grp)->grp_ndl_list, ndl_link) {
+ if (ndl->ndl_node->nd_state == LST_NODE_ACTIVE)
+ return 0;
+ }
+
+ CDEBUG(D_NET, "Group %s has no ACTIVE nodes\n", name);
+
+ return -EINVAL;
+}
+
+int
+lstcon_test_add(char *batch_name, int type, int loop,
+ int concur, int dist, int span,
+ char *src_name, char *dst_name,
+ void *param, int paramlen, int *retp,
+ struct list_head *result_up)
+{
+ lstcon_test_t *test = NULL;
+ int rc;
+ lstcon_group_t *src_grp = NULL;
+ lstcon_group_t *dst_grp = NULL;
+ lstcon_batch_t *batch = NULL;
+
+ /*
+ * verify that a batch of the given name exists, and the groups
+ * that will be part of the batch exist and have at least one
+ * active node
+ */
+ rc = lstcon_verify_batch(batch_name, &batch);
+ if (rc != 0)
+ goto out;
+
+ rc = lstcon_verify_group(src_name, &src_grp);
+ if (rc != 0)
+ goto out;
+
+ rc = lstcon_verify_group(dst_name, &dst_grp);
+ if (rc != 0)
+ goto out;
+
+ if (dst_grp->grp_userland)
+ *retp = 1;
+
+ LIBCFS_ALLOC(test, offsetof(lstcon_test_t, tes_param[paramlen]));
+ if (!test) {
+ CERROR("Can't allocate test descriptor\n");
+ rc = -ENOMEM;
+
+ goto out;
+ }
+
+ test->tes_hdr.tsb_id = batch->bat_hdr.tsb_id;
+ test->tes_batch = batch;
+ test->tes_type = type;
+ test->tes_oneside = 0; /* TODO */
+ test->tes_loop = loop;
+ test->tes_concur = concur;
+ test->tes_stop_onerr = 1; /* TODO */
+ test->tes_span = span;
+ test->tes_dist = dist;
+ test->tes_cliidx = 0; /* just used for creating RPC */
+ test->tes_src_grp = src_grp;
+ test->tes_dst_grp = dst_grp;
+ INIT_LIST_HEAD(&test->tes_trans_list);
+
+ if (param != NULL) {
+ test->tes_paramlen = paramlen;
+ memcpy(&test->tes_param[0], param, paramlen);
+ }
+
+ rc = lstcon_test_nodes_add(test, result_up);
+
+ if (rc != 0)
+ goto out;
+
+ if (lstcon_trans_stat()->trs_rpc_errno != 0 ||
+ lstcon_trans_stat()->trs_fwk_errno != 0)
+ CDEBUG(D_NET, "Failed to add test %d to batch %s\n", type,
+ batch_name);
+
+ /* add to test list anyway, so user can check what's going on */
+ list_add_tail(&test->tes_link, &batch->bat_test_list);
+
+ batch->bat_ntest++;
+ test->tes_hdr.tsb_index = batch->bat_ntest;
+
+ /* hold groups so nobody can change them */
+ return rc;
+out:
+ if (test != NULL)
+ LIBCFS_FREE(test, offsetof(lstcon_test_t, tes_param[paramlen]));
+
+ if (dst_grp != NULL)
+ lstcon_group_put(dst_grp);
+
+ if (src_grp != NULL)
+ lstcon_group_put(src_grp);
+
+ return rc;
+}
+
+static int
+lstcon_test_find(lstcon_batch_t *batch, int idx, lstcon_test_t **testpp)
+{
+ lstcon_test_t *test;
+
+ list_for_each_entry(test, &batch->bat_test_list, tes_link) {
+ if (idx == test->tes_hdr.tsb_index) {
+ *testpp = test;
+ return 0;
+ }
+ }
+
+ return -ENOENT;
+}
+
+static int
+lstcon_tsbrpc_readent(int transop, srpc_msg_t *msg,
+ lstcon_rpc_ent_t *ent_up)
+{
+ srpc_batch_reply_t *rep = &msg->msg_body.bat_reply;
+
+ LASSERT(transop == LST_TRANS_TSBCLIQRY ||
+ transop == LST_TRANS_TSBSRVQRY);
+
+ /* positive errno, framework error code */
+ if (copy_to_user(&ent_up->rpe_priv[0],
+ &rep->bar_active, sizeof(rep->bar_active)))
+ return -EFAULT;
+
+ return 0;
+}
+
+int
+lstcon_test_batch_query(char *name, int testidx, int client,
+ int timeout, struct list_head *result_up)
+{
+ lstcon_rpc_trans_t *trans;
+ struct list_head *translist;
+ struct list_head *ndlist;
+ lstcon_tsb_hdr_t *hdr;
+ lstcon_batch_t *batch;
+ lstcon_test_t *test = NULL;
+ int transop;
+ int rc;
+
+ rc = lstcon_batch_find(name, &batch);
+ if (rc != 0) {
+ CDEBUG(D_NET, "Can't find batch: %s\n", name);
+ return rc;
+ }
+
+ if (testidx == 0) {
+ translist = &batch->bat_trans_list;
+ ndlist = &batch->bat_cli_list;
+ hdr = &batch->bat_hdr;
+
+ } else {
+ /* query specified test only */
+ rc = lstcon_test_find(batch, testidx, &test);
+ if (rc != 0) {
+ CDEBUG(D_NET, "Can't find test: %d\n", testidx);
+ return rc;
+ }
+
+ translist = &test->tes_trans_list;
+ ndlist = &test->tes_src_grp->grp_ndl_list;
+ hdr = &test->tes_hdr;
+ }
+
+ transop = client ? LST_TRANS_TSBCLIQRY : LST_TRANS_TSBSRVQRY;
+
+ rc = lstcon_rpc_trans_ndlist(ndlist, translist, transop, hdr,
+ lstcon_batrpc_condition, &trans);
+ if (rc != 0) {
+ CERROR("Can't create transaction: %d\n", rc);
+ return rc;
+ }
+
+ lstcon_rpc_trans_postwait(trans, timeout);
+
+ if (testidx == 0 && /* query a batch, not a test */
+ lstcon_rpc_stat_failure(lstcon_trans_stat(), 0) == 0 &&
+ lstcon_tsbqry_stat_run(lstcon_trans_stat(), 0) == 0) {
+ /* all RPCs finished, and no active test */
+ batch->bat_state = LST_BATCH_IDLE;
+ }
+
+ rc = lstcon_rpc_trans_interpreter(trans, result_up,
+ lstcon_tsbrpc_readent);
+ lstcon_rpc_trans_destroy(trans);
+
+ return rc;
+}
+
+static int
+lstcon_statrpc_readent(int transop, srpc_msg_t *msg,
+ lstcon_rpc_ent_t *ent_up)
+{
+ srpc_stat_reply_t *rep = &msg->msg_body.stat_reply;
+ sfw_counters_t *sfwk_stat;
+ srpc_counters_t *srpc_stat;
+ lnet_counters_t *lnet_stat;
+
+ if (rep->str_status != 0)
+ return 0;
+
+ sfwk_stat = (sfw_counters_t *)&ent_up->rpe_payload[0];
+ srpc_stat = (srpc_counters_t *)((char *)sfwk_stat + sizeof(*sfwk_stat));
+ lnet_stat = (lnet_counters_t *)((char *)srpc_stat + sizeof(*srpc_stat));
+
+ if (copy_to_user(sfwk_stat, &rep->str_fw, sizeof(*sfwk_stat)) ||
+ copy_to_user(srpc_stat, &rep->str_rpc, sizeof(*srpc_stat)) ||
+ copy_to_user(lnet_stat, &rep->str_lnet, sizeof(*lnet_stat)))
+ return -EFAULT;
+
+ return 0;
+}
+
+static int
+lstcon_ndlist_stat(struct list_head *ndlist,
+ int timeout, struct list_head *result_up)
+{
+ struct list_head head;
+ lstcon_rpc_trans_t *trans;
+ int rc;
+
+ INIT_LIST_HEAD(&head);
+
+ rc = lstcon_rpc_trans_ndlist(ndlist, &head,
+ LST_TRANS_STATQRY, NULL, NULL, &trans);
+ if (rc != 0) {
+ CERROR("Can't create transaction: %d\n", rc);
+ return rc;
+ }
+
+ lstcon_rpc_trans_postwait(trans, LST_VALIDATE_TIMEOUT(timeout));
+
+ rc = lstcon_rpc_trans_interpreter(trans, result_up,
+ lstcon_statrpc_readent);
+ lstcon_rpc_trans_destroy(trans);
+
+ return rc;
+}
+
+int
+lstcon_group_stat(char *grp_name, int timeout, struct list_head *result_up)
+{
+ lstcon_group_t *grp;
+ int rc;
+
+ rc = lstcon_group_find(grp_name, &grp);
+ if (rc != 0) {
+ CDEBUG(D_NET, "Can't find group %s\n", grp_name);
+ return rc;
+ }
+
+ rc = lstcon_ndlist_stat(&grp->grp_ndl_list, timeout, result_up);
+
+ lstcon_group_put(grp);
+
+ return rc;
+}
+
+int
+lstcon_nodes_stat(int count, lnet_process_id_t *ids_up,
+ int timeout, struct list_head *result_up)
+{
+ lstcon_ndlink_t *ndl;
+ lstcon_group_t *tmp;
+ lnet_process_id_t id;
+ int i;
+ int rc;
+
+ rc = lstcon_group_alloc(NULL, &tmp);
+ if (rc != 0) {
+ CERROR("Out of memory\n");
+ return -ENOMEM;
+ }
+
+ for (i = 0 ; i < count; i++) {
+ if (copy_from_user(&id, &ids_up[i], sizeof(id))) {
+ rc = -EFAULT;
+ break;
+ }
+
+ /* add to tmp group */
+ rc = lstcon_group_ndlink_find(tmp, id, &ndl, 2);
+ if (rc != 0) {
+ CDEBUG((rc == -ENOMEM) ? D_ERROR : D_NET,
+ "Failed to find or create %s: %d\n",
+ libcfs_id2str(id), rc);
+ break;
+ }
+ }
+
+ if (rc != 0) {
+ lstcon_group_put(tmp);
+ return rc;
+ }
+
+ rc = lstcon_ndlist_stat(&tmp->grp_ndl_list, timeout, result_up);
+
+ lstcon_group_put(tmp);
+
+ return rc;
+}
+
+static int
+lstcon_debug_ndlist(struct list_head *ndlist,
+ struct list_head *translist,
+ int timeout, struct list_head *result_up)
+{
+ lstcon_rpc_trans_t *trans;
+ int rc;
+
+ rc = lstcon_rpc_trans_ndlist(ndlist, translist, LST_TRANS_SESQRY,
+ NULL, lstcon_sesrpc_condition, &trans);
+ if (rc != 0) {
+ CERROR("Can't create transaction: %d\n", rc);
+ return rc;
+ }
+
+ lstcon_rpc_trans_postwait(trans, LST_VALIDATE_TIMEOUT(timeout));
+
+ rc = lstcon_rpc_trans_interpreter(trans, result_up,
+ lstcon_sesrpc_readent);
+ lstcon_rpc_trans_destroy(trans);
+
+ return rc;
+}
+
+int
+lstcon_session_debug(int timeout, struct list_head *result_up)
+{
+ return lstcon_debug_ndlist(&console_session.ses_ndl_list,
+ NULL, timeout, result_up);
+}
+
+int
+lstcon_batch_debug(int timeout, char *name,
+ int client, struct list_head *result_up)
+{
+ lstcon_batch_t *bat;
+ int rc;
+
+ rc = lstcon_batch_find(name, &bat);
+ if (rc != 0)
+ return -ENOENT;
+
+ rc = lstcon_debug_ndlist(client ? &bat->bat_cli_list :
+ &bat->bat_srv_list,
+ NULL, timeout, result_up);
+
+ return rc;
+}
+
+int
+lstcon_group_debug(int timeout, char *name,
+ struct list_head *result_up)
+{
+ lstcon_group_t *grp;
+ int rc;
+
+ rc = lstcon_group_find(name, &grp);
+ if (rc != 0)
+ return -ENOENT;
+
+ rc = lstcon_debug_ndlist(&grp->grp_ndl_list, NULL,
+ timeout, result_up);
+ lstcon_group_put(grp);
+
+ return rc;
+}
+
+int
+lstcon_nodes_debug(int timeout,
+ int count, lnet_process_id_t *ids_up,
+ struct list_head *result_up)
+{
+ lnet_process_id_t id;
+ lstcon_ndlink_t *ndl;
+ lstcon_group_t *grp;
+ int i;
+ int rc;
+
+ rc = lstcon_group_alloc(NULL, &grp);
+ if (rc != 0) {
+ CDEBUG(D_NET, "Out of memory\n");
+ return rc;
+ }
+
+ for (i = 0; i < count; i++) {
+ if (copy_from_user(&id, &ids_up[i], sizeof(id))) {
+ rc = -EFAULT;
+ break;
+ }
+
+ /* node is added to tmp group */
+ rc = lstcon_group_ndlink_find(grp, id, &ndl, 1);
+ if (rc != 0) {
+ CERROR("Can't create node link\n");
+ break;
+ }
+ }
+
+ if (rc != 0) {
+ lstcon_group_put(grp);
+ return rc;
+ }
+
+ rc = lstcon_debug_ndlist(&grp->grp_ndl_list, NULL,
+ timeout, result_up);
+
+ lstcon_group_put(grp);
+
+ return rc;
+}
+
+int
+lstcon_session_match(lst_sid_t sid)
+{
+ return (console_session.ses_id.ses_nid == sid.ses_nid &&
+ console_session.ses_id.ses_stamp == sid.ses_stamp) ? 1: 0;
+}
+
+static void
+lstcon_new_session_id(lst_sid_t *sid)
+{
+ lnet_process_id_t id;
+
+ LASSERT(console_session.ses_state == LST_SESSION_NONE);
+
+ LNetGetId(1, &id);
+ sid->ses_nid = id.nid;
+ sid->ses_stamp = cfs_time_current();
+}
+
+extern srpc_service_t lstcon_acceptor_service;
+
+int
+lstcon_session_new(char *name, int key, unsigned feats,
+ int timeout, int force, lst_sid_t *sid_up)
+{
+ int rc = 0;
+ int i;
+
+ if (console_session.ses_state != LST_SESSION_NONE) {
+ /* session exists */
+ if (!force) {
+ CNETERR("Session %s already exists\n",
+ console_session.ses_name);
+ return -EEXIST;
+ }
+
+ rc = lstcon_session_end();
+
+ /* lstcon_session_end() only return local error */
+ if (rc != 0)
+ return rc;
+ }
+
+ if ((feats & ~LST_FEATS_MASK) != 0) {
+ CNETERR("Unknown session features %x\n",
+ (feats & ~LST_FEATS_MASK));
+ return -EINVAL;
+ }
+
+ for (i = 0; i < LST_GLOBAL_HASHSIZE; i++)
+ LASSERT(list_empty(&console_session.ses_ndl_hash[i]));
+
+ lstcon_new_session_id(&console_session.ses_id);
+
+ console_session.ses_key = key;
+ console_session.ses_state = LST_SESSION_ACTIVE;
+ console_session.ses_force = !!force;
+ console_session.ses_features = feats;
+ console_session.ses_feats_updated = 0;
+ console_session.ses_timeout = (timeout <= 0) ?
+ LST_CONSOLE_TIMEOUT : timeout;
+ strcpy(console_session.ses_name, name);
+
+ rc = lstcon_batch_add(LST_DEFAULT_BATCH);
+ if (rc != 0)
+ return rc;
+
+ rc = lstcon_rpc_pinger_start();
+ if (rc != 0) {
+ lstcon_batch_t *bat = NULL;
+
+ lstcon_batch_find(LST_DEFAULT_BATCH, &bat);
+ lstcon_batch_destroy(bat);
+
+ return rc;
+ }
+
+ if (copy_to_user(sid_up, &console_session.ses_id,
+ sizeof(lst_sid_t)) == 0)
+ return rc;
+
+ lstcon_session_end();
+
+ return -EFAULT;
+}
+
+int
+lstcon_session_info(lst_sid_t *sid_up, int *key_up, unsigned *featp,
+ lstcon_ndlist_ent_t *ndinfo_up, char *name_up, int len)
+{
+ lstcon_ndlist_ent_t *entp;
+ lstcon_ndlink_t *ndl;
+ int rc = 0;
+
+ if (console_session.ses_state != LST_SESSION_ACTIVE)
+ return -ESRCH;
+
+ LIBCFS_ALLOC(entp, sizeof(*entp));
+ if (entp == NULL)
+ return -ENOMEM;
+
+ list_for_each_entry(ndl, &console_session.ses_ndl_list, ndl_link)
+ LST_NODE_STATE_COUNTER(ndl->ndl_node, entp);
+
+ if (copy_to_user(sid_up, &console_session.ses_id,
+ sizeof(lst_sid_t)) ||
+ copy_to_user(key_up, &console_session.ses_key,
+ sizeof(*key_up)) ||
+ copy_to_user(featp, &console_session.ses_features,
+ sizeof(*featp)) ||
+ copy_to_user(ndinfo_up, entp, sizeof(*entp)) ||
+ copy_to_user(name_up, console_session.ses_name, len))
+ rc = -EFAULT;
+
+ LIBCFS_FREE(entp, sizeof(*entp));
+
+ return rc;
+}
+
+int
+lstcon_session_end(void)
+{
+ lstcon_rpc_trans_t *trans;
+ lstcon_group_t *grp;
+ lstcon_batch_t *bat;
+ int rc = 0;
+
+ LASSERT(console_session.ses_state == LST_SESSION_ACTIVE);
+
+ rc = lstcon_rpc_trans_ndlist(&console_session.ses_ndl_list,
+ NULL, LST_TRANS_SESEND, NULL,
+ lstcon_sesrpc_condition, &trans);
+ if (rc != 0) {
+ CERROR("Can't create transaction: %d\n", rc);
+ return rc;
+ }
+
+ console_session.ses_shutdown = 1;
+
+ lstcon_rpc_pinger_stop();
+
+ lstcon_rpc_trans_postwait(trans, LST_TRANS_TIMEOUT);
+
+ lstcon_rpc_trans_destroy(trans);
+ /* User can do nothing even rpc failed, so go on */
+
+ /* waiting for orphan rpcs to die */
+ lstcon_rpc_cleanup_wait();
+
+ console_session.ses_id = LST_INVALID_SID;
+ console_session.ses_state = LST_SESSION_NONE;
+ console_session.ses_key = 0;
+ console_session.ses_force = 0;
+ console_session.ses_feats_updated = 0;
+
+ /* destroy all batches */
+ while (!list_empty(&console_session.ses_bat_list)) {
+ bat = list_entry(console_session.ses_bat_list.next,
+ lstcon_batch_t, bat_link);
+
+ lstcon_batch_destroy(bat);
+ }
+
+ /* destroy all groups */
+ while (!list_empty(&console_session.ses_grp_list)) {
+ grp = list_entry(console_session.ses_grp_list.next,
+ lstcon_group_t, grp_link);
+ LASSERT(grp->grp_ref == 1);
+
+ lstcon_group_put(grp);
+ }
+
+ /* all nodes should be released */
+ LASSERT(list_empty(&console_session.ses_ndl_list));
+
+ console_session.ses_shutdown = 0;
+ console_session.ses_expired = 0;
+
+ return rc;
+}
+
+int
+lstcon_session_feats_check(unsigned feats)
+{
+ int rc = 0;
+
+ if ((feats & ~LST_FEATS_MASK) != 0) {
+ CERROR("Can't support these features: %x\n",
+ (feats & ~LST_FEATS_MASK));
+ return -EPROTO;
+ }
+
+ spin_lock(&console_session.ses_rpc_lock);
+
+ if (!console_session.ses_feats_updated) {
+ console_session.ses_feats_updated = 1;
+ console_session.ses_features = feats;
+ }
+
+ if (console_session.ses_features != feats)
+ rc = -EPROTO;
+
+ spin_unlock(&console_session.ses_rpc_lock);
+
+ if (rc != 0) {
+ CERROR("remote features %x do not match with session features %x of console\n",
+ feats, console_session.ses_features);
+ }
+
+ return rc;
+}
+
+static int
+lstcon_acceptor_handle(srpc_server_rpc_t *rpc)
+{
+ srpc_msg_t *rep = &rpc->srpc_replymsg;
+ srpc_msg_t *req = &rpc->srpc_reqstbuf->buf_msg;
+ srpc_join_reqst_t *jreq = &req->msg_body.join_reqst;
+ srpc_join_reply_t *jrep = &rep->msg_body.join_reply;
+ lstcon_group_t *grp = NULL;
+ lstcon_ndlink_t *ndl;
+ int rc = 0;
+
+ sfw_unpack_message(req);
+
+ mutex_lock(&console_session.ses_mutex);
+
+ jrep->join_sid = console_session.ses_id;
+
+ if (console_session.ses_id.ses_nid == LNET_NID_ANY) {
+ jrep->join_status = ESRCH;
+ goto out;
+ }
+
+ if (lstcon_session_feats_check(req->msg_ses_feats) != 0) {
+ jrep->join_status = EPROTO;
+ goto out;
+ }
+
+ if (jreq->join_sid.ses_nid != LNET_NID_ANY &&
+ !lstcon_session_match(jreq->join_sid)) {
+ jrep->join_status = EBUSY;
+ goto out;
+ }
+
+ if (lstcon_group_find(jreq->join_group, &grp) != 0) {
+ rc = lstcon_group_alloc(jreq->join_group, &grp);
+ if (rc != 0) {
+ CERROR("Out of memory\n");
+ goto out;
+ }
+
+ list_add_tail(&grp->grp_link,
+ &console_session.ses_grp_list);
+ lstcon_group_addref(grp);
+ }
+
+ if (grp->grp_ref > 2) {
+ /* Group in using */
+ jrep->join_status = EBUSY;
+ goto out;
+ }
+
+ rc = lstcon_group_ndlink_find(grp, rpc->srpc_peer, &ndl, 0);
+ if (rc == 0) {
+ jrep->join_status = EEXIST;
+ goto out;
+ }
+
+ rc = lstcon_group_ndlink_find(grp, rpc->srpc_peer, &ndl, 1);
+ if (rc != 0) {
+ CERROR("Out of memory\n");
+ goto out;
+ }
+
+ ndl->ndl_node->nd_state = LST_NODE_ACTIVE;
+ ndl->ndl_node->nd_timeout = console_session.ses_timeout;
+
+ if (grp->grp_userland == 0)
+ grp->grp_userland = 1;
+
+ strcpy(jrep->join_session, console_session.ses_name);
+ jrep->join_timeout = console_session.ses_timeout;
+ jrep->join_status = 0;
+
+out:
+ rep->msg_ses_feats = console_session.ses_features;
+ if (grp != NULL)
+ lstcon_group_put(grp);
+
+ mutex_unlock(&console_session.ses_mutex);
+
+ return rc;
+}
+
+srpc_service_t lstcon_acceptor_service;
+static void lstcon_init_acceptor_service(void)
+{
+ /* initialize selftest console acceptor service table */
+ lstcon_acceptor_service.sv_name = "join session";
+ lstcon_acceptor_service.sv_handler = lstcon_acceptor_handle;
+ lstcon_acceptor_service.sv_id = SRPC_SERVICE_JOIN;
+ lstcon_acceptor_service.sv_wi_total = SFW_FRWK_WI_MAX;
+}
+
+extern int lstcon_ioctl_entry(unsigned int cmd, struct libcfs_ioctl_data *data);
+
+static DECLARE_IOCTL_HANDLER(lstcon_ioctl_handler, lstcon_ioctl_entry);
+
+/* initialize console */
+int
+lstcon_console_init(void)
+{
+ int i;
+ int rc;
+
+ memset(&console_session, 0, sizeof(lstcon_session_t));
+
+ console_session.ses_id = LST_INVALID_SID;
+ console_session.ses_state = LST_SESSION_NONE;
+ console_session.ses_timeout = 0;
+ console_session.ses_force = 0;
+ console_session.ses_expired = 0;
+ console_session.ses_feats_updated = 0;
+ console_session.ses_features = LST_FEATS_MASK;
+ console_session.ses_laststamp = get_seconds();
+
+ mutex_init(&console_session.ses_mutex);
+
+ INIT_LIST_HEAD(&console_session.ses_ndl_list);
+ INIT_LIST_HEAD(&console_session.ses_grp_list);
+ INIT_LIST_HEAD(&console_session.ses_bat_list);
+ INIT_LIST_HEAD(&console_session.ses_trans_list);
+
+ LIBCFS_ALLOC(console_session.ses_ndl_hash,
+ sizeof(struct list_head) * LST_GLOBAL_HASHSIZE);
+ if (console_session.ses_ndl_hash == NULL)
+ return -ENOMEM;
+
+ for (i = 0; i < LST_GLOBAL_HASHSIZE; i++)
+ INIT_LIST_HEAD(&console_session.ses_ndl_hash[i]);
+
+
+ /* initialize acceptor service table */
+ lstcon_init_acceptor_service();
+
+ rc = srpc_add_service(&lstcon_acceptor_service);
+ LASSERT(rc != -EBUSY);
+ if (rc != 0) {
+ LIBCFS_FREE(console_session.ses_ndl_hash,
+ sizeof(struct list_head) * LST_GLOBAL_HASHSIZE);
+ return rc;
+ }
+
+ rc = srpc_service_add_buffers(&lstcon_acceptor_service,
+ lstcon_acceptor_service.sv_wi_total);
+ if (rc != 0) {
+ rc = -ENOMEM;
+ goto out;
+ }
+
+ rc = libcfs_register_ioctl(&lstcon_ioctl_handler);
+
+ if (rc == 0) {
+ lstcon_rpc_module_init();
+ return 0;
+ }
+
+out:
+ srpc_shutdown_service(&lstcon_acceptor_service);
+ srpc_remove_service(&lstcon_acceptor_service);
+
+ LIBCFS_FREE(console_session.ses_ndl_hash,
+ sizeof(struct list_head) * LST_GLOBAL_HASHSIZE);
+
+ srpc_wait_service_shutdown(&lstcon_acceptor_service);
+
+ return rc;
+}
+
+int
+lstcon_console_fini(void)
+{
+ int i;
+
+ libcfs_deregister_ioctl(&lstcon_ioctl_handler);
+
+ mutex_lock(&console_session.ses_mutex);
+
+ srpc_shutdown_service(&lstcon_acceptor_service);
+ srpc_remove_service(&lstcon_acceptor_service);
+
+ if (console_session.ses_state != LST_SESSION_NONE)
+ lstcon_session_end();
+
+ lstcon_rpc_module_fini();
+
+ mutex_unlock(&console_session.ses_mutex);
+
+ LASSERT(list_empty(&console_session.ses_ndl_list));
+ LASSERT(list_empty(&console_session.ses_grp_list));
+ LASSERT(list_empty(&console_session.ses_bat_list));
+ LASSERT(list_empty(&console_session.ses_trans_list));
+
+ for (i = 0; i < LST_NODE_HASHSIZE; i++) {
+ LASSERT(list_empty(&console_session.ses_ndl_hash[i]));
+ }
+
+ LIBCFS_FREE(console_session.ses_ndl_hash,
+ sizeof(struct list_head) * LST_GLOBAL_HASHSIZE);
+
+ srpc_wait_service_shutdown(&lstcon_acceptor_service);
+
+ return 0;
+}
diff --git a/drivers/staging/lustre/lnet/selftest/console.h b/drivers/staging/lustre/lnet/selftest/console.h
new file mode 100644
index 000000000..e41ca89f1
--- /dev/null
+++ b/drivers/staging/lustre/lnet/selftest/console.h
@@ -0,0 +1,235 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2011, 2012, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ *
+ * lnet/selftest/console.h
+ *
+ * kernel structure for LST console
+ *
+ * Author: Liang Zhen <liangzhen@clusterfs.com>
+ */
+
+#ifndef __LST_CONSOLE_H__
+#define __LST_CONSOLE_H__
+
+
+#include "../../include/linux/libcfs/libcfs.h"
+#include "../../include/linux/lnet/lnet.h"
+#include "../../include/linux/lnet/lib-types.h"
+#include "../../include/linux/lnet/lnetst.h"
+#include "selftest.h"
+#include "conrpc.h"
+
+typedef struct lstcon_node {
+ lnet_process_id_t nd_id; /* id of the node */
+ int nd_ref; /* reference count */
+ int nd_state; /* state of the node */
+ int nd_timeout; /* session timeout */
+ unsigned long nd_stamp; /* timestamp of last replied RPC */
+ struct lstcon_rpc nd_ping; /* ping rpc */
+} lstcon_node_t; /*** node descriptor */
+
+typedef struct {
+ struct list_head ndl_link; /* chain on list */
+ struct list_head ndl_hlink; /* chain on hash */
+ lstcon_node_t *ndl_node; /* pointer to node */
+} lstcon_ndlink_t; /*** node link descriptor */
+
+typedef struct {
+ struct list_head grp_link; /* chain on global group list */
+ int grp_ref; /* reference count */
+ int grp_userland; /* has userland nodes */
+ int grp_nnode; /* # of nodes */
+ char grp_name[LST_NAME_SIZE]; /* group name */
+
+ struct list_head grp_trans_list; /* transaction list */
+ struct list_head grp_ndl_list; /* nodes list */
+ struct list_head grp_ndl_hash[0];/* hash table for nodes */
+} lstcon_group_t; /*** (alias of nodes) group descriptor */
+
+#define LST_BATCH_IDLE 0xB0 /* idle batch */
+#define LST_BATCH_RUNNING 0xB1 /* running batch */
+
+typedef struct lstcon_tsb_hdr {
+ lst_bid_t tsb_id; /* batch ID */
+ int tsb_index; /* test index */
+} lstcon_tsb_hdr_t;
+
+typedef struct {
+ lstcon_tsb_hdr_t bat_hdr; /* test_batch header */
+ struct list_head bat_link; /* chain on session's batches list */
+ int bat_ntest; /* # of test */
+ int bat_state; /* state of the batch */
+ int bat_arg; /* parameter for run|stop, timeout for run, force for stop */
+ char bat_name[LST_NAME_SIZE]; /* name of batch */
+
+ struct list_head bat_test_list; /* list head of tests (lstcon_test_t) */
+ struct list_head bat_trans_list; /* list head of transaction */
+ struct list_head bat_cli_list; /* list head of client nodes (lstcon_node_t) */
+ struct list_head *bat_cli_hash; /* hash table of client nodes */
+ struct list_head bat_srv_list; /* list head of server nodes */
+ struct list_head *bat_srv_hash; /* hash table of server nodes */
+} lstcon_batch_t; /*** (tests ) batch descriptor */
+
+typedef struct lstcon_test {
+ lstcon_tsb_hdr_t tes_hdr; /* test batch header */
+ struct list_head tes_link; /* chain on batch's tests list */
+ lstcon_batch_t *tes_batch; /* pointer to batch */
+
+ int tes_type; /* type of the test, i.e: bulk, ping */
+ int tes_stop_onerr; /* stop on error */
+ int tes_oneside; /* one-sided test */
+ int tes_concur; /* concurrency */
+ int tes_loop; /* loop count */
+ int tes_dist; /* nodes distribution of target group */
+ int tes_span; /* nodes span of target group */
+ int tes_cliidx; /* client index, used for RPC creating */
+
+ struct list_head tes_trans_list; /* transaction list */
+ lstcon_group_t *tes_src_grp; /* group run the test */
+ lstcon_group_t *tes_dst_grp; /* target group */
+
+ int tes_paramlen; /* test parameter length */
+ char tes_param[0]; /* test parameter */
+} lstcon_test_t; /*** a single test descriptor */
+
+#define LST_GLOBAL_HASHSIZE 503 /* global nodes hash table size */
+#define LST_NODE_HASHSIZE 239 /* node hash table (for batch or group) */
+
+#define LST_SESSION_NONE 0x0 /* no session */
+#define LST_SESSION_ACTIVE 0x1 /* working session */
+
+#define LST_CONSOLE_TIMEOUT 300 /* default console timeout */
+
+typedef struct {
+ struct mutex ses_mutex; /* only 1 thread in session */
+ lst_sid_t ses_id; /* global session id */
+ int ses_key; /* local session key */
+ int ses_state; /* state of session */
+ int ses_timeout; /* timeout in seconds */
+ time_t ses_laststamp; /* last operation stamp (seconds) */
+ /** tests features of the session */
+ unsigned ses_features;
+ /** features are synced with remote test nodes */
+ unsigned ses_feats_updated:1;
+ /** force creating */
+ unsigned ses_force:1;
+ /** session is shutting down */
+ unsigned ses_shutdown:1;
+ /** console is timedout */
+ unsigned ses_expired:1;
+ __u64 ses_id_cookie; /* batch id cookie */
+ char ses_name[LST_NAME_SIZE]; /* session name */
+ lstcon_rpc_trans_t *ses_ping; /* session pinger */
+ stt_timer_t ses_ping_timer; /* timer for pinger */
+ lstcon_trans_stat_t ses_trans_stat; /* transaction stats */
+
+ struct list_head ses_trans_list; /* global list of transaction */
+ struct list_head ses_grp_list; /* global list of groups */
+ struct list_head ses_bat_list; /* global list of batches */
+ struct list_head ses_ndl_list; /* global list of nodes */
+ struct list_head *ses_ndl_hash; /* hash table of nodes */
+
+ spinlock_t ses_rpc_lock; /* serialize */
+ atomic_t ses_rpc_counter;/* # of initialized RPCs */
+ struct list_head ses_rpc_freelist; /* idle console rpc */
+} lstcon_session_t; /*** session descriptor */
+
+extern lstcon_session_t console_session;
+
+static inline lstcon_trans_stat_t *
+lstcon_trans_stat(void)
+{
+ return &console_session.ses_trans_stat;
+}
+
+static inline struct list_head *
+lstcon_id2hash (lnet_process_id_t id, struct list_head *hash)
+{
+ unsigned int idx = LNET_NIDADDR(id.nid) % LST_NODE_HASHSIZE;
+
+ return &hash[idx];
+}
+
+int lstcon_console_init(void);
+int lstcon_ioctl_entry(unsigned int cmd, struct libcfs_ioctl_data *data);
+int lstcon_console_fini(void);
+extern int lstcon_session_match(lst_sid_t sid);
+extern int lstcon_session_new(char *name, int key, unsigned version,
+ int timeout, int flags, lst_sid_t *sid_up);
+extern int lstcon_session_info(lst_sid_t *sid_up, int *key, unsigned *verp,
+ lstcon_ndlist_ent_t *entp, char *name_up, int len);
+extern int lstcon_session_end(void);
+extern int lstcon_session_debug(int timeout, struct list_head *result_up);
+extern int lstcon_session_feats_check(unsigned feats);
+extern int lstcon_batch_debug(int timeout, char *name,
+ int client, struct list_head *result_up);
+extern int lstcon_group_debug(int timeout, char *name,
+ struct list_head *result_up);
+extern int lstcon_nodes_debug(int timeout, int nnd, lnet_process_id_t *nds_up,
+ struct list_head *result_up);
+extern int lstcon_group_add(char *name);
+extern int lstcon_group_del(char *name);
+extern int lstcon_group_clean(char *name, int args);
+extern int lstcon_group_refresh(char *name, struct list_head *result_up);
+extern int lstcon_nodes_add(char *name, int nnd, lnet_process_id_t *nds_up,
+ unsigned *featp, struct list_head *result_up);
+extern int lstcon_nodes_remove(char *name, int nnd, lnet_process_id_t *nds_up,
+ struct list_head *result_up);
+extern int lstcon_group_info(char *name, lstcon_ndlist_ent_t *gent_up,
+ int *index_p, int *ndent_p, lstcon_node_ent_t *ndents_up);
+extern int lstcon_group_list(int idx, int len, char *name_up);
+extern int lstcon_batch_add(char *name);
+extern int lstcon_batch_run(char *name, int timeout,
+ struct list_head *result_up);
+extern int lstcon_batch_stop(char *name, int force,
+ struct list_head *result_up);
+extern int lstcon_test_batch_query(char *name, int testidx,
+ int client, int timeout,
+ struct list_head *result_up);
+extern int lstcon_batch_del(char *name);
+extern int lstcon_batch_list(int idx, int namelen, char *name_up);
+extern int lstcon_batch_info(char *name, lstcon_test_batch_ent_t *ent_up,
+ int server, int testidx, int *index_p,
+ int *ndent_p, lstcon_node_ent_t *dents_up);
+extern int lstcon_group_stat(char *grp_name, int timeout,
+ struct list_head *result_up);
+extern int lstcon_nodes_stat(int count, lnet_process_id_t *ids_up,
+ int timeout, struct list_head *result_up);
+extern int lstcon_test_add(char *batch_name, int type, int loop,
+ int concur, int dist, int span,
+ char *src_name, char *dst_name,
+ void *param, int paramlen, int *retp,
+ struct list_head *result_up);
+#endif
diff --git a/drivers/staging/lustre/lnet/selftest/framework.c b/drivers/staging/lustre/lnet/selftest/framework.c
new file mode 100644
index 000000000..a93a90de0
--- /dev/null
+++ b/drivers/staging/lustre/lnet/selftest/framework.c
@@ -0,0 +1,1804 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2012, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ *
+ * lnet/selftest/framework.c
+ *
+ * Author: Isaac Huang <isaac@clusterfs.com>
+ * Author: Liang Zhen <liangzhen@clusterfs.com>
+ */
+
+#define DEBUG_SUBSYSTEM S_LNET
+
+#include "selftest.h"
+
+lst_sid_t LST_INVALID_SID = {LNET_NID_ANY, -1};
+
+static int session_timeout = 100;
+module_param(session_timeout, int, 0444);
+MODULE_PARM_DESC(session_timeout, "test session timeout in seconds (100 by default, 0 == never)");
+
+static int rpc_timeout = 64;
+module_param(rpc_timeout, int, 0644);
+MODULE_PARM_DESC(rpc_timeout, "rpc timeout in seconds (64 by default, 0 == never)");
+
+#define sfw_unpack_id(id) \
+do { \
+ __swab64s(&(id).nid); \
+ __swab32s(&(id).pid); \
+} while (0)
+
+#define sfw_unpack_sid(sid) \
+do { \
+ __swab64s(&(sid).ses_nid); \
+ __swab64s(&(sid).ses_stamp); \
+} while (0)
+
+#define sfw_unpack_fw_counters(fc) \
+do { \
+ __swab32s(&(fc).running_ms); \
+ __swab32s(&(fc).active_batches); \
+ __swab32s(&(fc).zombie_sessions); \
+ __swab32s(&(fc).brw_errors); \
+ __swab32s(&(fc).ping_errors); \
+} while (0)
+
+#define sfw_unpack_rpc_counters(rc) \
+do { \
+ __swab32s(&(rc).errors); \
+ __swab32s(&(rc).rpcs_sent); \
+ __swab32s(&(rc).rpcs_rcvd); \
+ __swab32s(&(rc).rpcs_dropped); \
+ __swab32s(&(rc).rpcs_expired); \
+ __swab64s(&(rc).bulk_get); \
+ __swab64s(&(rc).bulk_put); \
+} while (0)
+
+#define sfw_unpack_lnet_counters(lc) \
+do { \
+ __swab32s(&(lc).errors); \
+ __swab32s(&(lc).msgs_max); \
+ __swab32s(&(lc).msgs_alloc); \
+ __swab32s(&(lc).send_count); \
+ __swab32s(&(lc).recv_count); \
+ __swab32s(&(lc).drop_count); \
+ __swab32s(&(lc).route_count); \
+ __swab64s(&(lc).send_length); \
+ __swab64s(&(lc).recv_length); \
+ __swab64s(&(lc).drop_length); \
+ __swab64s(&(lc).route_length); \
+} while (0)
+
+#define sfw_test_active(t) (atomic_read(&(t)->tsi_nactive) != 0)
+#define sfw_batch_active(b) (atomic_read(&(b)->bat_nactive) != 0)
+
+static struct smoketest_framework {
+ struct list_head fw_zombie_rpcs; /* RPCs to be recycled */
+ struct list_head fw_zombie_sessions; /* stopping sessions */
+ struct list_head fw_tests; /* registered test cases */
+ atomic_t fw_nzombies; /* # zombie sessions */
+ spinlock_t fw_lock; /* serialise */
+ sfw_session_t *fw_session; /* _the_ session */
+ int fw_shuttingdown; /* shutdown in progress */
+ srpc_server_rpc_t *fw_active_srpc; /* running RPC */
+} sfw_data;
+
+/* forward ref's */
+int sfw_stop_batch(sfw_batch_t *tsb, int force);
+void sfw_destroy_session(sfw_session_t *sn);
+
+static inline sfw_test_case_t *
+sfw_find_test_case(int id)
+{
+ sfw_test_case_t *tsc;
+
+ LASSERT(id <= SRPC_SERVICE_MAX_ID);
+ LASSERT(id > SRPC_FRAMEWORK_SERVICE_MAX_ID);
+
+ list_for_each_entry(tsc, &sfw_data.fw_tests, tsc_list) {
+ if (tsc->tsc_srv_service->sv_id == id)
+ return tsc;
+ }
+
+ return NULL;
+}
+
+static int
+sfw_register_test(srpc_service_t *service, sfw_test_client_ops_t *cliops)
+{
+ sfw_test_case_t *tsc;
+
+ if (sfw_find_test_case(service->sv_id) != NULL) {
+ CERROR("Failed to register test %s (%d)\n",
+ service->sv_name, service->sv_id);
+ return -EEXIST;
+ }
+
+ LIBCFS_ALLOC(tsc, sizeof(sfw_test_case_t));
+ if (tsc == NULL)
+ return -ENOMEM;
+
+ tsc->tsc_cli_ops = cliops;
+ tsc->tsc_srv_service = service;
+
+ list_add_tail(&tsc->tsc_list, &sfw_data.fw_tests);
+ return 0;
+}
+
+static void
+sfw_add_session_timer(void)
+{
+ sfw_session_t *sn = sfw_data.fw_session;
+ stt_timer_t *timer = &sn->sn_timer;
+
+ LASSERT(!sfw_data.fw_shuttingdown);
+
+ if (sn == NULL || sn->sn_timeout == 0)
+ return;
+
+ LASSERT(!sn->sn_timer_active);
+
+ sn->sn_timer_active = 1;
+ timer->stt_expires = cfs_time_add(sn->sn_timeout,
+ get_seconds());
+ stt_add_timer(timer);
+ return;
+}
+
+static int
+sfw_del_session_timer(void)
+{
+ sfw_session_t *sn = sfw_data.fw_session;
+
+ if (sn == NULL || !sn->sn_timer_active)
+ return 0;
+
+ LASSERT(sn->sn_timeout != 0);
+
+ if (stt_del_timer(&sn->sn_timer)) { /* timer defused */
+ sn->sn_timer_active = 0;
+ return 0;
+ }
+
+ return EBUSY; /* racing with sfw_session_expired() */
+}
+
+static void
+sfw_deactivate_session(void)
+ __must_hold(&sfw_data.fw_lock)
+{
+ sfw_session_t *sn = sfw_data.fw_session;
+ int nactive = 0;
+ sfw_batch_t *tsb;
+ sfw_test_case_t *tsc;
+
+ if (sn == NULL) return;
+
+ LASSERT(!sn->sn_timer_active);
+
+ sfw_data.fw_session = NULL;
+ atomic_inc(&sfw_data.fw_nzombies);
+ list_add(&sn->sn_list, &sfw_data.fw_zombie_sessions);
+
+ spin_unlock(&sfw_data.fw_lock);
+
+ list_for_each_entry(tsc, &sfw_data.fw_tests, tsc_list) {
+ srpc_abort_service(tsc->tsc_srv_service);
+ }
+
+ spin_lock(&sfw_data.fw_lock);
+
+ list_for_each_entry(tsb, &sn->sn_batches, bat_list) {
+ if (sfw_batch_active(tsb)) {
+ nactive++;
+ sfw_stop_batch(tsb, 1);
+ }
+ }
+
+ if (nactive != 0)
+ return; /* wait for active batches to stop */
+
+ list_del_init(&sn->sn_list);
+ spin_unlock(&sfw_data.fw_lock);
+
+ sfw_destroy_session(sn);
+
+ spin_lock(&sfw_data.fw_lock);
+}
+
+
+static void
+sfw_session_expired(void *data)
+{
+ sfw_session_t *sn = data;
+
+ spin_lock(&sfw_data.fw_lock);
+
+ LASSERT(sn->sn_timer_active);
+ LASSERT(sn == sfw_data.fw_session);
+
+ CWARN("Session expired! sid: %s-%llu, name: %s\n",
+ libcfs_nid2str(sn->sn_id.ses_nid),
+ sn->sn_id.ses_stamp, &sn->sn_name[0]);
+
+ sn->sn_timer_active = 0;
+ sfw_deactivate_session();
+
+ spin_unlock(&sfw_data.fw_lock);
+}
+
+static inline void
+sfw_init_session(sfw_session_t *sn, lst_sid_t sid,
+ unsigned features, const char *name)
+{
+ stt_timer_t *timer = &sn->sn_timer;
+
+ memset(sn, 0, sizeof(sfw_session_t));
+ INIT_LIST_HEAD(&sn->sn_list);
+ INIT_LIST_HEAD(&sn->sn_batches);
+ atomic_set(&sn->sn_refcount, 1); /* +1 for caller */
+ atomic_set(&sn->sn_brw_errors, 0);
+ atomic_set(&sn->sn_ping_errors, 0);
+ strlcpy(&sn->sn_name[0], name, sizeof(sn->sn_name));
+
+ sn->sn_timer_active = 0;
+ sn->sn_id = sid;
+ sn->sn_features = features;
+ sn->sn_timeout = session_timeout;
+ sn->sn_started = cfs_time_current();
+
+ timer->stt_data = sn;
+ timer->stt_func = sfw_session_expired;
+ INIT_LIST_HEAD(&timer->stt_list);
+}
+
+/* completion handler for incoming framework RPCs */
+static void
+sfw_server_rpc_done(struct srpc_server_rpc *rpc)
+{
+ struct srpc_service *sv = rpc->srpc_scd->scd_svc;
+ int status = rpc->srpc_status;
+
+ CDEBUG(D_NET,
+ "Incoming framework RPC done: service %s, peer %s, status %s:%d\n",
+ sv->sv_name, libcfs_id2str(rpc->srpc_peer),
+ swi_state2str(rpc->srpc_wi.swi_state),
+ status);
+
+ if (rpc->srpc_bulk != NULL)
+ sfw_free_pages(rpc);
+ return;
+}
+
+static void
+sfw_client_rpc_fini(srpc_client_rpc_t *rpc)
+{
+ LASSERT(rpc->crpc_bulk.bk_niov == 0);
+ LASSERT(list_empty(&rpc->crpc_list));
+ LASSERT(atomic_read(&rpc->crpc_refcount) == 0);
+
+ CDEBUG(D_NET,
+ "Outgoing framework RPC done: service %d, peer %s, status %s:%d:%d\n",
+ rpc->crpc_service, libcfs_id2str(rpc->crpc_dest),
+ swi_state2str(rpc->crpc_wi.swi_state),
+ rpc->crpc_aborted, rpc->crpc_status);
+
+ spin_lock(&sfw_data.fw_lock);
+
+ /* my callers must finish all RPCs before shutting me down */
+ LASSERT(!sfw_data.fw_shuttingdown);
+ list_add(&rpc->crpc_list, &sfw_data.fw_zombie_rpcs);
+
+ spin_unlock(&sfw_data.fw_lock);
+}
+
+static sfw_batch_t *
+sfw_find_batch(lst_bid_t bid)
+{
+ sfw_session_t *sn = sfw_data.fw_session;
+ sfw_batch_t *bat;
+
+ LASSERT(sn != NULL);
+
+ list_for_each_entry(bat, &sn->sn_batches, bat_list) {
+ if (bat->bat_id.bat_id == bid.bat_id)
+ return bat;
+ }
+
+ return NULL;
+}
+
+static sfw_batch_t *
+sfw_bid2batch(lst_bid_t bid)
+{
+ sfw_session_t *sn = sfw_data.fw_session;
+ sfw_batch_t *bat;
+
+ LASSERT(sn != NULL);
+
+ bat = sfw_find_batch(bid);
+ if (bat != NULL)
+ return bat;
+
+ LIBCFS_ALLOC(bat, sizeof(sfw_batch_t));
+ if (bat == NULL)
+ return NULL;
+
+ bat->bat_error = 0;
+ bat->bat_session = sn;
+ bat->bat_id = bid;
+ atomic_set(&bat->bat_nactive, 0);
+ INIT_LIST_HEAD(&bat->bat_tests);
+
+ list_add_tail(&bat->bat_list, &sn->sn_batches);
+ return bat;
+}
+
+static int
+sfw_get_stats(srpc_stat_reqst_t *request, srpc_stat_reply_t *reply)
+{
+ sfw_session_t *sn = sfw_data.fw_session;
+ sfw_counters_t *cnt = &reply->str_fw;
+ sfw_batch_t *bat;
+ struct timeval tv;
+
+ reply->str_sid = (sn == NULL) ? LST_INVALID_SID : sn->sn_id;
+
+ if (request->str_sid.ses_nid == LNET_NID_ANY) {
+ reply->str_status = EINVAL;
+ return 0;
+ }
+
+ if (sn == NULL || !sfw_sid_equal(request->str_sid, sn->sn_id)) {
+ reply->str_status = ESRCH;
+ return 0;
+ }
+
+ lnet_counters_get(&reply->str_lnet);
+ srpc_get_counters(&reply->str_rpc);
+
+ /* send over the msecs since the session was started
+ - with 32 bits to send, this is ~49 days */
+ cfs_duration_usec(cfs_time_sub(cfs_time_current(),
+ sn->sn_started), &tv);
+
+ cnt->running_ms = (__u32)(tv.tv_sec * 1000 + tv.tv_usec / 1000);
+ cnt->brw_errors = atomic_read(&sn->sn_brw_errors);
+ cnt->ping_errors = atomic_read(&sn->sn_ping_errors);
+ cnt->zombie_sessions = atomic_read(&sfw_data.fw_nzombies);
+
+ cnt->active_batches = 0;
+ list_for_each_entry(bat, &sn->sn_batches, bat_list) {
+ if (atomic_read(&bat->bat_nactive) > 0)
+ cnt->active_batches++;
+ }
+
+ reply->str_status = 0;
+ return 0;
+}
+
+int
+sfw_make_session(srpc_mksn_reqst_t *request, srpc_mksn_reply_t *reply)
+{
+ sfw_session_t *sn = sfw_data.fw_session;
+ srpc_msg_t *msg = container_of(request, srpc_msg_t,
+ msg_body.mksn_reqst);
+ int cplen = 0;
+
+ if (request->mksn_sid.ses_nid == LNET_NID_ANY) {
+ reply->mksn_sid = (sn == NULL) ? LST_INVALID_SID : sn->sn_id;
+ reply->mksn_status = EINVAL;
+ return 0;
+ }
+
+ if (sn != NULL) {
+ reply->mksn_status = 0;
+ reply->mksn_sid = sn->sn_id;
+ reply->mksn_timeout = sn->sn_timeout;
+
+ if (sfw_sid_equal(request->mksn_sid, sn->sn_id)) {
+ atomic_inc(&sn->sn_refcount);
+ return 0;
+ }
+
+ if (!request->mksn_force) {
+ reply->mksn_status = EBUSY;
+ cplen = strlcpy(&reply->mksn_name[0], &sn->sn_name[0],
+ sizeof(reply->mksn_name));
+ if (cplen >= sizeof(reply->mksn_name))
+ return -E2BIG;
+ return 0;
+ }
+ }
+
+ /* reject the request if it requires unknown features
+ * NB: old version will always accept all features because it's not
+ * aware of srpc_msg_t::msg_ses_feats, it's a defect but it's also
+ * harmless because it will return zero feature to console, and it's
+ * console's responsibility to make sure all nodes in a session have
+ * same feature mask. */
+ if ((msg->msg_ses_feats & ~LST_FEATS_MASK) != 0) {
+ reply->mksn_status = EPROTO;
+ return 0;
+ }
+
+ /* brand new or create by force */
+ LIBCFS_ALLOC(sn, sizeof(sfw_session_t));
+ if (sn == NULL) {
+ CERROR("Dropping RPC (mksn) under memory pressure.\n");
+ return -ENOMEM;
+ }
+
+ sfw_init_session(sn, request->mksn_sid,
+ msg->msg_ses_feats, &request->mksn_name[0]);
+
+ spin_lock(&sfw_data.fw_lock);
+
+ sfw_deactivate_session();
+ LASSERT(sfw_data.fw_session == NULL);
+ sfw_data.fw_session = sn;
+
+ spin_unlock(&sfw_data.fw_lock);
+
+ reply->mksn_status = 0;
+ reply->mksn_sid = sn->sn_id;
+ reply->mksn_timeout = sn->sn_timeout;
+ return 0;
+}
+
+static int
+sfw_remove_session(srpc_rmsn_reqst_t *request, srpc_rmsn_reply_t *reply)
+{
+ sfw_session_t *sn = sfw_data.fw_session;
+
+ reply->rmsn_sid = (sn == NULL) ? LST_INVALID_SID : sn->sn_id;
+
+ if (request->rmsn_sid.ses_nid == LNET_NID_ANY) {
+ reply->rmsn_status = EINVAL;
+ return 0;
+ }
+
+ if (sn == NULL || !sfw_sid_equal(request->rmsn_sid, sn->sn_id)) {
+ reply->rmsn_status = (sn == NULL) ? ESRCH : EBUSY;
+ return 0;
+ }
+
+ if (!atomic_dec_and_test(&sn->sn_refcount)) {
+ reply->rmsn_status = 0;
+ return 0;
+ }
+
+ spin_lock(&sfw_data.fw_lock);
+ sfw_deactivate_session();
+ spin_unlock(&sfw_data.fw_lock);
+
+ reply->rmsn_status = 0;
+ reply->rmsn_sid = LST_INVALID_SID;
+ LASSERT(sfw_data.fw_session == NULL);
+ return 0;
+}
+
+static int
+sfw_debug_session(srpc_debug_reqst_t *request, srpc_debug_reply_t *reply)
+{
+ sfw_session_t *sn = sfw_data.fw_session;
+
+ if (sn == NULL) {
+ reply->dbg_status = ESRCH;
+ reply->dbg_sid = LST_INVALID_SID;
+ return 0;
+ }
+
+ reply->dbg_status = 0;
+ reply->dbg_sid = sn->sn_id;
+ reply->dbg_timeout = sn->sn_timeout;
+ if (strlcpy(reply->dbg_name, &sn->sn_name[0], sizeof(reply->dbg_name))
+ >= sizeof(reply->dbg_name))
+ return -E2BIG;
+
+ return 0;
+}
+
+static void
+sfw_test_rpc_fini(srpc_client_rpc_t *rpc)
+{
+ sfw_test_unit_t *tsu = rpc->crpc_priv;
+ sfw_test_instance_t *tsi = tsu->tsu_instance;
+
+ /* Called with hold of tsi->tsi_lock */
+ LASSERT(list_empty(&rpc->crpc_list));
+ list_add(&rpc->crpc_list, &tsi->tsi_free_rpcs);
+}
+
+static inline int
+sfw_test_buffers(sfw_test_instance_t *tsi)
+{
+ struct sfw_test_case *tsc = sfw_find_test_case(tsi->tsi_service);
+ struct srpc_service *svc = tsc->tsc_srv_service;
+ int nbuf;
+
+ nbuf = min(svc->sv_wi_total, tsi->tsi_loop) / svc->sv_ncpts;
+ return max(SFW_TEST_WI_MIN, nbuf + SFW_TEST_WI_EXTRA);
+}
+
+static int
+sfw_load_test(struct sfw_test_instance *tsi)
+{
+ struct sfw_test_case *tsc;
+ struct srpc_service *svc;
+ int nbuf;
+ int rc;
+
+ LASSERT(tsi != NULL);
+ tsc = sfw_find_test_case(tsi->tsi_service);
+ nbuf = sfw_test_buffers(tsi);
+ LASSERT(tsc != NULL);
+ svc = tsc->tsc_srv_service;
+
+ if (tsi->tsi_is_client) {
+ tsi->tsi_ops = tsc->tsc_cli_ops;
+ return 0;
+ }
+
+ rc = srpc_service_add_buffers(svc, nbuf);
+ if (rc != 0) {
+ CWARN("Failed to reserve enough buffers: service %s, %d needed: %d\n",
+ svc->sv_name, nbuf, rc);
+ /* NB: this error handler is not strictly correct, because
+ * it may release more buffers than already allocated,
+ * but it doesn't matter because request portal should
+ * be lazy portal and will grow buffers if necessary. */
+ srpc_service_remove_buffers(svc, nbuf);
+ return -ENOMEM;
+ }
+
+ CDEBUG(D_NET, "Reserved %d buffers for test %s\n",
+ nbuf * (srpc_serv_is_framework(svc) ?
+ 1 : cfs_cpt_number(cfs_cpt_table)), svc->sv_name);
+ return 0;
+}
+
+static void
+sfw_unload_test(struct sfw_test_instance *tsi)
+{
+ struct sfw_test_case *tsc = sfw_find_test_case(tsi->tsi_service);
+
+ LASSERT(tsc != NULL);
+
+ if (tsi->tsi_is_client)
+ return;
+
+ /* shrink buffers, because request portal is lazy portal
+ * which can grow buffers at runtime so we may leave
+ * some buffers behind, but never mind... */
+ srpc_service_remove_buffers(tsc->tsc_srv_service,
+ sfw_test_buffers(tsi));
+ return;
+}
+
+static void
+sfw_destroy_test_instance(sfw_test_instance_t *tsi)
+{
+ srpc_client_rpc_t *rpc;
+ sfw_test_unit_t *tsu;
+
+ if (!tsi->tsi_is_client) goto clean;
+
+ tsi->tsi_ops->tso_fini(tsi);
+
+ LASSERT(!tsi->tsi_stopping);
+ LASSERT(list_empty(&tsi->tsi_active_rpcs));
+ LASSERT(!sfw_test_active(tsi));
+
+ while (!list_empty(&tsi->tsi_units)) {
+ tsu = list_entry(tsi->tsi_units.next,
+ sfw_test_unit_t, tsu_list);
+ list_del(&tsu->tsu_list);
+ LIBCFS_FREE(tsu, sizeof(*tsu));
+ }
+
+ while (!list_empty(&tsi->tsi_free_rpcs)) {
+ rpc = list_entry(tsi->tsi_free_rpcs.next,
+ srpc_client_rpc_t, crpc_list);
+ list_del(&rpc->crpc_list);
+ LIBCFS_FREE(rpc, srpc_client_rpc_size(rpc));
+ }
+
+clean:
+ sfw_unload_test(tsi);
+ LIBCFS_FREE(tsi, sizeof(*tsi));
+ return;
+}
+
+static void
+sfw_destroy_batch(sfw_batch_t *tsb)
+{
+ sfw_test_instance_t *tsi;
+
+ LASSERT(!sfw_batch_active(tsb));
+ LASSERT(list_empty(&tsb->bat_list));
+
+ while (!list_empty(&tsb->bat_tests)) {
+ tsi = list_entry(tsb->bat_tests.next,
+ sfw_test_instance_t, tsi_list);
+ list_del_init(&tsi->tsi_list);
+ sfw_destroy_test_instance(tsi);
+ }
+
+ LIBCFS_FREE(tsb, sizeof(sfw_batch_t));
+ return;
+}
+
+void
+sfw_destroy_session(sfw_session_t *sn)
+{
+ sfw_batch_t *batch;
+
+ LASSERT(list_empty(&sn->sn_list));
+ LASSERT(sn != sfw_data.fw_session);
+
+ while (!list_empty(&sn->sn_batches)) {
+ batch = list_entry(sn->sn_batches.next,
+ sfw_batch_t, bat_list);
+ list_del_init(&batch->bat_list);
+ sfw_destroy_batch(batch);
+ }
+
+ LIBCFS_FREE(sn, sizeof(*sn));
+ atomic_dec(&sfw_data.fw_nzombies);
+ return;
+}
+
+static void
+sfw_unpack_addtest_req(srpc_msg_t *msg)
+{
+ srpc_test_reqst_t *req = &msg->msg_body.tes_reqst;
+
+ LASSERT(msg->msg_type == SRPC_MSG_TEST_REQST);
+ LASSERT(req->tsr_is_client);
+
+ if (msg->msg_magic == SRPC_MSG_MAGIC)
+ return; /* no flipping needed */
+
+ LASSERT(msg->msg_magic == __swab32(SRPC_MSG_MAGIC));
+
+ if (req->tsr_service == SRPC_SERVICE_BRW) {
+ if ((msg->msg_ses_feats & LST_FEAT_BULK_LEN) == 0) {
+ test_bulk_req_t *bulk = &req->tsr_u.bulk_v0;
+
+ __swab32s(&bulk->blk_opc);
+ __swab32s(&bulk->blk_npg);
+ __swab32s(&bulk->blk_flags);
+
+ } else {
+ test_bulk_req_v1_t *bulk = &req->tsr_u.bulk_v1;
+
+ __swab16s(&bulk->blk_opc);
+ __swab16s(&bulk->blk_flags);
+ __swab32s(&bulk->blk_offset);
+ __swab32s(&bulk->blk_len);
+ }
+
+ return;
+ }
+
+ if (req->tsr_service == SRPC_SERVICE_PING) {
+ test_ping_req_t *ping = &req->tsr_u.ping;
+
+ __swab32s(&ping->png_size);
+ __swab32s(&ping->png_flags);
+ return;
+ }
+
+ LBUG();
+ return;
+}
+
+static int
+sfw_add_test_instance(sfw_batch_t *tsb, srpc_server_rpc_t *rpc)
+{
+ srpc_msg_t *msg = &rpc->srpc_reqstbuf->buf_msg;
+ srpc_test_reqst_t *req = &msg->msg_body.tes_reqst;
+ srpc_bulk_t *bk = rpc->srpc_bulk;
+ int ndest = req->tsr_ndest;
+ sfw_test_unit_t *tsu;
+ sfw_test_instance_t *tsi;
+ int i;
+ int rc;
+
+ LIBCFS_ALLOC(tsi, sizeof(*tsi));
+ if (tsi == NULL) {
+ CERROR("Can't allocate test instance for batch: %llu\n",
+ tsb->bat_id.bat_id);
+ return -ENOMEM;
+ }
+
+ spin_lock_init(&tsi->tsi_lock);
+ atomic_set(&tsi->tsi_nactive, 0);
+ INIT_LIST_HEAD(&tsi->tsi_units);
+ INIT_LIST_HEAD(&tsi->tsi_free_rpcs);
+ INIT_LIST_HEAD(&tsi->tsi_active_rpcs);
+
+ tsi->tsi_stopping = 0;
+ tsi->tsi_batch = tsb;
+ tsi->tsi_loop = req->tsr_loop;
+ tsi->tsi_concur = req->tsr_concur;
+ tsi->tsi_service = req->tsr_service;
+ tsi->tsi_is_client = !!(req->tsr_is_client);
+ tsi->tsi_stoptsu_onerr = !!(req->tsr_stop_onerr);
+
+ rc = sfw_load_test(tsi);
+ if (rc != 0) {
+ LIBCFS_FREE(tsi, sizeof(*tsi));
+ return rc;
+ }
+
+ LASSERT(!sfw_batch_active(tsb));
+
+ if (!tsi->tsi_is_client) {
+ /* it's test server, just add it to tsb */
+ list_add_tail(&tsi->tsi_list, &tsb->bat_tests);
+ return 0;
+ }
+
+ LASSERT(bk != NULL);
+ LASSERT(bk->bk_niov * SFW_ID_PER_PAGE >= (unsigned int)ndest);
+ LASSERT((unsigned int)bk->bk_len >=
+ sizeof(lnet_process_id_packed_t) * ndest);
+
+ sfw_unpack_addtest_req(msg);
+ memcpy(&tsi->tsi_u, &req->tsr_u, sizeof(tsi->tsi_u));
+
+ for (i = 0; i < ndest; i++) {
+ lnet_process_id_packed_t *dests;
+ lnet_process_id_packed_t id;
+ int j;
+
+ dests = page_address(bk->bk_iovs[i / SFW_ID_PER_PAGE].kiov_page);
+ LASSERT(dests != NULL); /* my pages are within KVM always */
+ id = dests[i % SFW_ID_PER_PAGE];
+ if (msg->msg_magic != SRPC_MSG_MAGIC)
+ sfw_unpack_id(id);
+
+ for (j = 0; j < tsi->tsi_concur; j++) {
+ LIBCFS_ALLOC(tsu, sizeof(sfw_test_unit_t));
+ if (tsu == NULL) {
+ rc = -ENOMEM;
+ CERROR("Can't allocate tsu for %d\n",
+ tsi->tsi_service);
+ goto error;
+ }
+
+ tsu->tsu_dest.nid = id.nid;
+ tsu->tsu_dest.pid = id.pid;
+ tsu->tsu_instance = tsi;
+ tsu->tsu_private = NULL;
+ list_add_tail(&tsu->tsu_list, &tsi->tsi_units);
+ }
+ }
+
+ rc = tsi->tsi_ops->tso_init(tsi);
+ if (rc == 0) {
+ list_add_tail(&tsi->tsi_list, &tsb->bat_tests);
+ return 0;
+ }
+
+error:
+ LASSERT(rc != 0);
+ sfw_destroy_test_instance(tsi);
+ return rc;
+}
+
+static void
+sfw_test_unit_done(sfw_test_unit_t *tsu)
+{
+ sfw_test_instance_t *tsi = tsu->tsu_instance;
+ sfw_batch_t *tsb = tsi->tsi_batch;
+ sfw_session_t *sn = tsb->bat_session;
+
+ LASSERT(sfw_test_active(tsi));
+
+ if (!atomic_dec_and_test(&tsi->tsi_nactive))
+ return;
+
+ /* the test instance is done */
+ spin_lock(&tsi->tsi_lock);
+
+ tsi->tsi_stopping = 0;
+
+ spin_unlock(&tsi->tsi_lock);
+
+ spin_lock(&sfw_data.fw_lock);
+
+ if (!atomic_dec_and_test(&tsb->bat_nactive) ||/* tsb still active */
+ sn == sfw_data.fw_session) { /* sn also active */
+ spin_unlock(&sfw_data.fw_lock);
+ return;
+ }
+
+ LASSERT(!list_empty(&sn->sn_list)); /* I'm a zombie! */
+
+ list_for_each_entry(tsb, &sn->sn_batches, bat_list) {
+ if (sfw_batch_active(tsb)) {
+ spin_unlock(&sfw_data.fw_lock);
+ return;
+ }
+ }
+
+ list_del_init(&sn->sn_list);
+ spin_unlock(&sfw_data.fw_lock);
+
+ sfw_destroy_session(sn);
+ return;
+}
+
+static void
+sfw_test_rpc_done(srpc_client_rpc_t *rpc)
+{
+ sfw_test_unit_t *tsu = rpc->crpc_priv;
+ sfw_test_instance_t *tsi = tsu->tsu_instance;
+ int done = 0;
+
+ tsi->tsi_ops->tso_done_rpc(tsu, rpc);
+
+ spin_lock(&tsi->tsi_lock);
+
+ LASSERT(sfw_test_active(tsi));
+ LASSERT(!list_empty(&rpc->crpc_list));
+
+ list_del_init(&rpc->crpc_list);
+
+ /* batch is stopping or loop is done or get error */
+ if (tsi->tsi_stopping ||
+ tsu->tsu_loop == 0 ||
+ (rpc->crpc_status != 0 && tsi->tsi_stoptsu_onerr))
+ done = 1;
+
+ /* dec ref for poster */
+ srpc_client_rpc_decref(rpc);
+
+ spin_unlock(&tsi->tsi_lock);
+
+ if (!done) {
+ swi_schedule_workitem(&tsu->tsu_worker);
+ return;
+ }
+
+ sfw_test_unit_done(tsu);
+ return;
+}
+
+int
+sfw_create_test_rpc(sfw_test_unit_t *tsu, lnet_process_id_t peer,
+ unsigned features, int nblk, int blklen,
+ srpc_client_rpc_t **rpcpp)
+{
+ srpc_client_rpc_t *rpc = NULL;
+ sfw_test_instance_t *tsi = tsu->tsu_instance;
+
+ spin_lock(&tsi->tsi_lock);
+
+ LASSERT(sfw_test_active(tsi));
+
+ if (!list_empty(&tsi->tsi_free_rpcs)) {
+ /* pick request from buffer */
+ rpc = list_entry(tsi->tsi_free_rpcs.next,
+ srpc_client_rpc_t, crpc_list);
+ LASSERT(nblk == rpc->crpc_bulk.bk_niov);
+ list_del_init(&rpc->crpc_list);
+ }
+
+ spin_unlock(&tsi->tsi_lock);
+
+ if (rpc == NULL) {
+ rpc = srpc_create_client_rpc(peer, tsi->tsi_service, nblk,
+ blklen, sfw_test_rpc_done,
+ sfw_test_rpc_fini, tsu);
+ } else {
+ srpc_init_client_rpc(rpc, peer, tsi->tsi_service, nblk,
+ blklen, sfw_test_rpc_done,
+ sfw_test_rpc_fini, tsu);
+ }
+
+ if (rpc == NULL) {
+ CERROR("Can't create rpc for test %d\n", tsi->tsi_service);
+ return -ENOMEM;
+ }
+
+ rpc->crpc_reqstmsg.msg_ses_feats = features;
+ *rpcpp = rpc;
+
+ return 0;
+}
+
+static int
+sfw_run_test(swi_workitem_t *wi)
+{
+ sfw_test_unit_t *tsu = wi->swi_workitem.wi_data;
+ sfw_test_instance_t *tsi = tsu->tsu_instance;
+ srpc_client_rpc_t *rpc = NULL;
+
+ LASSERT(wi == &tsu->tsu_worker);
+
+ if (tsi->tsi_ops->tso_prep_rpc(tsu, tsu->tsu_dest, &rpc) != 0) {
+ LASSERT(rpc == NULL);
+ goto test_done;
+ }
+
+ LASSERT(rpc != NULL);
+
+ spin_lock(&tsi->tsi_lock);
+
+ if (tsi->tsi_stopping) {
+ list_add(&rpc->crpc_list, &tsi->tsi_free_rpcs);
+ spin_unlock(&tsi->tsi_lock);
+ goto test_done;
+ }
+
+ if (tsu->tsu_loop > 0)
+ tsu->tsu_loop--;
+
+ list_add_tail(&rpc->crpc_list, &tsi->tsi_active_rpcs);
+ spin_unlock(&tsi->tsi_lock);
+
+ rpc->crpc_timeout = rpc_timeout;
+
+ spin_lock(&rpc->crpc_lock);
+ srpc_post_rpc(rpc);
+ spin_unlock(&rpc->crpc_lock);
+ return 0;
+
+test_done:
+ /*
+ * No one can schedule me now since:
+ * - previous RPC, if any, has done and
+ * - no new RPC is initiated.
+ * - my batch is still active; no one can run it again now.
+ * Cancel pending schedules and prevent future schedule attempts:
+ */
+ swi_exit_workitem(wi);
+ sfw_test_unit_done(tsu);
+ return 1;
+}
+
+static int
+sfw_run_batch(sfw_batch_t *tsb)
+{
+ swi_workitem_t *wi;
+ sfw_test_unit_t *tsu;
+ sfw_test_instance_t *tsi;
+
+ if (sfw_batch_active(tsb)) {
+ CDEBUG(D_NET, "Batch already active: %llu (%d)\n",
+ tsb->bat_id.bat_id, atomic_read(&tsb->bat_nactive));
+ return 0;
+ }
+
+ list_for_each_entry(tsi, &tsb->bat_tests, tsi_list) {
+ if (!tsi->tsi_is_client) /* skip server instances */
+ continue;
+
+ LASSERT(!tsi->tsi_stopping);
+ LASSERT(!sfw_test_active(tsi));
+
+ atomic_inc(&tsb->bat_nactive);
+
+ list_for_each_entry(tsu, &tsi->tsi_units, tsu_list) {
+ atomic_inc(&tsi->tsi_nactive);
+ tsu->tsu_loop = tsi->tsi_loop;
+ wi = &tsu->tsu_worker;
+ swi_init_workitem(wi, tsu, sfw_run_test,
+ lst_sched_test[\
+ lnet_cpt_of_nid(tsu->tsu_dest.nid)]);
+ swi_schedule_workitem(wi);
+ }
+ }
+
+ return 0;
+}
+
+int
+sfw_stop_batch(sfw_batch_t *tsb, int force)
+{
+ sfw_test_instance_t *tsi;
+ srpc_client_rpc_t *rpc;
+
+ if (!sfw_batch_active(tsb)) {
+ CDEBUG(D_NET, "Batch %llu inactive\n", tsb->bat_id.bat_id);
+ return 0;
+ }
+
+ list_for_each_entry(tsi, &tsb->bat_tests, tsi_list) {
+ spin_lock(&tsi->tsi_lock);
+
+ if (!tsi->tsi_is_client ||
+ !sfw_test_active(tsi) || tsi->tsi_stopping) {
+ spin_unlock(&tsi->tsi_lock);
+ continue;
+ }
+
+ tsi->tsi_stopping = 1;
+
+ if (!force) {
+ spin_unlock(&tsi->tsi_lock);
+ continue;
+ }
+
+ /* abort launched rpcs in the test */
+ list_for_each_entry(rpc, &tsi->tsi_active_rpcs, crpc_list) {
+ spin_lock(&rpc->crpc_lock);
+
+ srpc_abort_rpc(rpc, -EINTR);
+
+ spin_unlock(&rpc->crpc_lock);
+ }
+
+ spin_unlock(&tsi->tsi_lock);
+ }
+
+ return 0;
+}
+
+static int
+sfw_query_batch(sfw_batch_t *tsb, int testidx, srpc_batch_reply_t *reply)
+{
+ sfw_test_instance_t *tsi;
+
+ if (testidx < 0)
+ return -EINVAL;
+
+ if (testidx == 0) {
+ reply->bar_active = atomic_read(&tsb->bat_nactive);
+ return 0;
+ }
+
+ list_for_each_entry(tsi, &tsb->bat_tests, tsi_list) {
+ if (testidx-- > 1)
+ continue;
+
+ reply->bar_active = atomic_read(&tsi->tsi_nactive);
+ return 0;
+ }
+
+ return -ENOENT;
+}
+
+void
+sfw_free_pages(srpc_server_rpc_t *rpc)
+{
+ srpc_free_bulk(rpc->srpc_bulk);
+ rpc->srpc_bulk = NULL;
+}
+
+int
+sfw_alloc_pages(struct srpc_server_rpc *rpc, int cpt, int npages, int len,
+ int sink)
+{
+ LASSERT(rpc->srpc_bulk == NULL);
+ LASSERT(npages > 0 && npages <= LNET_MAX_IOV);
+
+ rpc->srpc_bulk = srpc_alloc_bulk(cpt, npages, len, sink);
+ if (rpc->srpc_bulk == NULL)
+ return -ENOMEM;
+
+ return 0;
+}
+
+static int
+sfw_add_test(srpc_server_rpc_t *rpc)
+{
+ sfw_session_t *sn = sfw_data.fw_session;
+ srpc_test_reply_t *reply = &rpc->srpc_replymsg.msg_body.tes_reply;
+ srpc_test_reqst_t *request;
+ int rc;
+ sfw_batch_t *bat;
+
+ request = &rpc->srpc_reqstbuf->buf_msg.msg_body.tes_reqst;
+ reply->tsr_sid = (sn == NULL) ? LST_INVALID_SID : sn->sn_id;
+
+ if (request->tsr_loop == 0 ||
+ request->tsr_concur == 0 ||
+ request->tsr_sid.ses_nid == LNET_NID_ANY ||
+ request->tsr_ndest > SFW_MAX_NDESTS ||
+ (request->tsr_is_client && request->tsr_ndest == 0) ||
+ request->tsr_concur > SFW_MAX_CONCUR ||
+ request->tsr_service > SRPC_SERVICE_MAX_ID ||
+ request->tsr_service <= SRPC_FRAMEWORK_SERVICE_MAX_ID) {
+ reply->tsr_status = EINVAL;
+ return 0;
+ }
+
+ if (sn == NULL || !sfw_sid_equal(request->tsr_sid, sn->sn_id) ||
+ sfw_find_test_case(request->tsr_service) == NULL) {
+ reply->tsr_status = ENOENT;
+ return 0;
+ }
+
+ bat = sfw_bid2batch(request->tsr_bid);
+ if (bat == NULL) {
+ CERROR("Dropping RPC (%s) from %s under memory pressure.\n",
+ rpc->srpc_scd->scd_svc->sv_name,
+ libcfs_id2str(rpc->srpc_peer));
+ return -ENOMEM;
+ }
+
+ if (sfw_batch_active(bat)) {
+ reply->tsr_status = EBUSY;
+ return 0;
+ }
+
+ if (request->tsr_is_client && rpc->srpc_bulk == NULL) {
+ /* rpc will be resumed later in sfw_bulk_ready */
+ int npg = sfw_id_pages(request->tsr_ndest);
+ int len;
+
+ if ((sn->sn_features & LST_FEAT_BULK_LEN) == 0) {
+ len = npg * PAGE_CACHE_SIZE;
+
+ } else {
+ len = sizeof(lnet_process_id_packed_t) *
+ request->tsr_ndest;
+ }
+
+ return sfw_alloc_pages(rpc, CFS_CPT_ANY, npg, len, 1);
+ }
+
+ rc = sfw_add_test_instance(bat, rpc);
+ CDEBUG(rc == 0 ? D_NET : D_WARNING,
+ "%s test: sv %d %s, loop %d, concur %d, ndest %d\n",
+ rc == 0 ? "Added" : "Failed to add", request->tsr_service,
+ request->tsr_is_client ? "client" : "server",
+ request->tsr_loop, request->tsr_concur, request->tsr_ndest);
+
+ reply->tsr_status = (rc < 0) ? -rc : rc;
+ return 0;
+}
+
+static int
+sfw_control_batch(srpc_batch_reqst_t *request, srpc_batch_reply_t *reply)
+{
+ sfw_session_t *sn = sfw_data.fw_session;
+ int rc = 0;
+ sfw_batch_t *bat;
+
+ reply->bar_sid = (sn == NULL) ? LST_INVALID_SID : sn->sn_id;
+
+ if (sn == NULL || !sfw_sid_equal(request->bar_sid, sn->sn_id)) {
+ reply->bar_status = ESRCH;
+ return 0;
+ }
+
+ bat = sfw_find_batch(request->bar_bid);
+ if (bat == NULL) {
+ reply->bar_status = ENOENT;
+ return 0;
+ }
+
+ switch (request->bar_opc) {
+ case SRPC_BATCH_OPC_RUN:
+ rc = sfw_run_batch(bat);
+ break;
+
+ case SRPC_BATCH_OPC_STOP:
+ rc = sfw_stop_batch(bat, request->bar_arg);
+ break;
+
+ case SRPC_BATCH_OPC_QUERY:
+ rc = sfw_query_batch(bat, request->bar_testidx, reply);
+ break;
+
+ default:
+ return -EINVAL; /* drop it */
+ }
+
+ reply->bar_status = (rc < 0) ? -rc : rc;
+ return 0;
+}
+
+static int
+sfw_handle_server_rpc(struct srpc_server_rpc *rpc)
+{
+ struct srpc_service *sv = rpc->srpc_scd->scd_svc;
+ srpc_msg_t *reply = &rpc->srpc_replymsg;
+ srpc_msg_t *request = &rpc->srpc_reqstbuf->buf_msg;
+ unsigned features = LST_FEATS_MASK;
+ int rc = 0;
+
+ LASSERT(sfw_data.fw_active_srpc == NULL);
+ LASSERT(sv->sv_id <= SRPC_FRAMEWORK_SERVICE_MAX_ID);
+
+ spin_lock(&sfw_data.fw_lock);
+
+ if (sfw_data.fw_shuttingdown) {
+ spin_unlock(&sfw_data.fw_lock);
+ return -ESHUTDOWN;
+ }
+
+ /* Remove timer to avoid racing with it or expiring active session */
+ if (sfw_del_session_timer() != 0) {
+ CERROR("Dropping RPC (%s) from %s: racing with expiry timer.",
+ sv->sv_name, libcfs_id2str(rpc->srpc_peer));
+ spin_unlock(&sfw_data.fw_lock);
+ return -EAGAIN;
+ }
+
+ sfw_data.fw_active_srpc = rpc;
+ spin_unlock(&sfw_data.fw_lock);
+
+ sfw_unpack_message(request);
+ LASSERT(request->msg_type == srpc_service2request(sv->sv_id));
+
+ /* rpc module should have checked this */
+ LASSERT(request->msg_version == SRPC_MSG_VERSION);
+
+ if (sv->sv_id != SRPC_SERVICE_MAKE_SESSION &&
+ sv->sv_id != SRPC_SERVICE_DEBUG) {
+ sfw_session_t *sn = sfw_data.fw_session;
+
+ if (sn != NULL &&
+ sn->sn_features != request->msg_ses_feats) {
+ CNETERR("Features of framework RPC don't match features of current session: %x/%x\n",
+ request->msg_ses_feats, sn->sn_features);
+ reply->msg_body.reply.status = EPROTO;
+ reply->msg_body.reply.sid = sn->sn_id;
+ goto out;
+ }
+
+ } else if ((request->msg_ses_feats & ~LST_FEATS_MASK) != 0) {
+ /* NB: at this point, old version will ignore features and
+ * create new session anyway, so console should be able
+ * to handle this */
+ reply->msg_body.reply.status = EPROTO;
+ goto out;
+ }
+
+ switch (sv->sv_id) {
+ default:
+ LBUG();
+ case SRPC_SERVICE_TEST:
+ rc = sfw_add_test(rpc);
+ break;
+
+ case SRPC_SERVICE_BATCH:
+ rc = sfw_control_batch(&request->msg_body.bat_reqst,
+ &reply->msg_body.bat_reply);
+ break;
+
+ case SRPC_SERVICE_QUERY_STAT:
+ rc = sfw_get_stats(&request->msg_body.stat_reqst,
+ &reply->msg_body.stat_reply);
+ break;
+
+ case SRPC_SERVICE_DEBUG:
+ rc = sfw_debug_session(&request->msg_body.dbg_reqst,
+ &reply->msg_body.dbg_reply);
+ break;
+
+ case SRPC_SERVICE_MAKE_SESSION:
+ rc = sfw_make_session(&request->msg_body.mksn_reqst,
+ &reply->msg_body.mksn_reply);
+ break;
+
+ case SRPC_SERVICE_REMOVE_SESSION:
+ rc = sfw_remove_session(&request->msg_body.rmsn_reqst,
+ &reply->msg_body.rmsn_reply);
+ break;
+ }
+
+ if (sfw_data.fw_session != NULL)
+ features = sfw_data.fw_session->sn_features;
+ out:
+ reply->msg_ses_feats = features;
+ rpc->srpc_done = sfw_server_rpc_done;
+ spin_lock(&sfw_data.fw_lock);
+
+ if (!sfw_data.fw_shuttingdown)
+ sfw_add_session_timer();
+
+ sfw_data.fw_active_srpc = NULL;
+ spin_unlock(&sfw_data.fw_lock);
+ return rc;
+}
+
+static int
+sfw_bulk_ready(struct srpc_server_rpc *rpc, int status)
+{
+ struct srpc_service *sv = rpc->srpc_scd->scd_svc;
+ int rc;
+
+ LASSERT(rpc->srpc_bulk != NULL);
+ LASSERT(sv->sv_id == SRPC_SERVICE_TEST);
+ LASSERT(sfw_data.fw_active_srpc == NULL);
+ LASSERT(rpc->srpc_reqstbuf->buf_msg.msg_body.tes_reqst.tsr_is_client);
+
+ spin_lock(&sfw_data.fw_lock);
+
+ if (status != 0) {
+ CERROR("Bulk transfer failed for RPC: service %s, peer %s, status %d\n",
+ sv->sv_name, libcfs_id2str(rpc->srpc_peer), status);
+ spin_unlock(&sfw_data.fw_lock);
+ return -EIO;
+ }
+
+ if (sfw_data.fw_shuttingdown) {
+ spin_unlock(&sfw_data.fw_lock);
+ return -ESHUTDOWN;
+ }
+
+ if (sfw_del_session_timer() != 0) {
+ CERROR("Dropping RPC (%s) from %s: racing with expiry timer",
+ sv->sv_name, libcfs_id2str(rpc->srpc_peer));
+ spin_unlock(&sfw_data.fw_lock);
+ return -EAGAIN;
+ }
+
+ sfw_data.fw_active_srpc = rpc;
+ spin_unlock(&sfw_data.fw_lock);
+
+ rc = sfw_add_test(rpc);
+
+ spin_lock(&sfw_data.fw_lock);
+
+ if (!sfw_data.fw_shuttingdown)
+ sfw_add_session_timer();
+
+ sfw_data.fw_active_srpc = NULL;
+ spin_unlock(&sfw_data.fw_lock);
+ return rc;
+}
+
+srpc_client_rpc_t *
+sfw_create_rpc(lnet_process_id_t peer, int service,
+ unsigned features, int nbulkiov, int bulklen,
+ void (*done)(srpc_client_rpc_t *), void *priv)
+{
+ srpc_client_rpc_t *rpc = NULL;
+
+ spin_lock(&sfw_data.fw_lock);
+
+ LASSERT(!sfw_data.fw_shuttingdown);
+ LASSERT(service <= SRPC_FRAMEWORK_SERVICE_MAX_ID);
+
+ if (nbulkiov == 0 && !list_empty(&sfw_data.fw_zombie_rpcs)) {
+ rpc = list_entry(sfw_data.fw_zombie_rpcs.next,
+ srpc_client_rpc_t, crpc_list);
+ list_del(&rpc->crpc_list);
+
+ srpc_init_client_rpc(rpc, peer, service, 0, 0,
+ done, sfw_client_rpc_fini, priv);
+ }
+
+ spin_unlock(&sfw_data.fw_lock);
+
+ if (rpc == NULL) {
+ rpc = srpc_create_client_rpc(peer, service,
+ nbulkiov, bulklen, done,
+ nbulkiov != 0 ? NULL :
+ sfw_client_rpc_fini,
+ priv);
+ }
+
+ if (rpc != NULL) /* "session" is concept in framework */
+ rpc->crpc_reqstmsg.msg_ses_feats = features;
+
+ return rpc;
+}
+
+void
+sfw_unpack_message(srpc_msg_t *msg)
+{
+ if (msg->msg_magic == SRPC_MSG_MAGIC)
+ return; /* no flipping needed */
+
+ /* srpc module should guarantee I wouldn't get crap */
+ LASSERT(msg->msg_magic == __swab32(SRPC_MSG_MAGIC));
+
+ if (msg->msg_type == SRPC_MSG_STAT_REQST) {
+ srpc_stat_reqst_t *req = &msg->msg_body.stat_reqst;
+
+ __swab32s(&req->str_type);
+ __swab64s(&req->str_rpyid);
+ sfw_unpack_sid(req->str_sid);
+ return;
+ }
+
+ if (msg->msg_type == SRPC_MSG_STAT_REPLY) {
+ srpc_stat_reply_t *rep = &msg->msg_body.stat_reply;
+
+ __swab32s(&rep->str_status);
+ sfw_unpack_sid(rep->str_sid);
+ sfw_unpack_fw_counters(rep->str_fw);
+ sfw_unpack_rpc_counters(rep->str_rpc);
+ sfw_unpack_lnet_counters(rep->str_lnet);
+ return;
+ }
+
+ if (msg->msg_type == SRPC_MSG_MKSN_REQST) {
+ srpc_mksn_reqst_t *req = &msg->msg_body.mksn_reqst;
+
+ __swab64s(&req->mksn_rpyid);
+ __swab32s(&req->mksn_force);
+ sfw_unpack_sid(req->mksn_sid);
+ return;
+ }
+
+ if (msg->msg_type == SRPC_MSG_MKSN_REPLY) {
+ srpc_mksn_reply_t *rep = &msg->msg_body.mksn_reply;
+
+ __swab32s(&rep->mksn_status);
+ __swab32s(&rep->mksn_timeout);
+ sfw_unpack_sid(rep->mksn_sid);
+ return;
+ }
+
+ if (msg->msg_type == SRPC_MSG_RMSN_REQST) {
+ srpc_rmsn_reqst_t *req = &msg->msg_body.rmsn_reqst;
+
+ __swab64s(&req->rmsn_rpyid);
+ sfw_unpack_sid(req->rmsn_sid);
+ return;
+ }
+
+ if (msg->msg_type == SRPC_MSG_RMSN_REPLY) {
+ srpc_rmsn_reply_t *rep = &msg->msg_body.rmsn_reply;
+
+ __swab32s(&rep->rmsn_status);
+ sfw_unpack_sid(rep->rmsn_sid);
+ return;
+ }
+
+ if (msg->msg_type == SRPC_MSG_DEBUG_REQST) {
+ srpc_debug_reqst_t *req = &msg->msg_body.dbg_reqst;
+
+ __swab64s(&req->dbg_rpyid);
+ __swab32s(&req->dbg_flags);
+ sfw_unpack_sid(req->dbg_sid);
+ return;
+ }
+
+ if (msg->msg_type == SRPC_MSG_DEBUG_REPLY) {
+ srpc_debug_reply_t *rep = &msg->msg_body.dbg_reply;
+
+ __swab32s(&rep->dbg_nbatch);
+ __swab32s(&rep->dbg_timeout);
+ sfw_unpack_sid(rep->dbg_sid);
+ return;
+ }
+
+ if (msg->msg_type == SRPC_MSG_BATCH_REQST) {
+ srpc_batch_reqst_t *req = &msg->msg_body.bat_reqst;
+
+ __swab32s(&req->bar_opc);
+ __swab64s(&req->bar_rpyid);
+ __swab32s(&req->bar_testidx);
+ __swab32s(&req->bar_arg);
+ sfw_unpack_sid(req->bar_sid);
+ __swab64s(&req->bar_bid.bat_id);
+ return;
+ }
+
+ if (msg->msg_type == SRPC_MSG_BATCH_REPLY) {
+ srpc_batch_reply_t *rep = &msg->msg_body.bat_reply;
+
+ __swab32s(&rep->bar_status);
+ sfw_unpack_sid(rep->bar_sid);
+ return;
+ }
+
+ if (msg->msg_type == SRPC_MSG_TEST_REQST) {
+ srpc_test_reqst_t *req = &msg->msg_body.tes_reqst;
+
+ __swab64s(&req->tsr_rpyid);
+ __swab64s(&req->tsr_bulkid);
+ __swab32s(&req->tsr_loop);
+ __swab32s(&req->tsr_ndest);
+ __swab32s(&req->tsr_concur);
+ __swab32s(&req->tsr_service);
+ sfw_unpack_sid(req->tsr_sid);
+ __swab64s(&req->tsr_bid.bat_id);
+ return;
+ }
+
+ if (msg->msg_type == SRPC_MSG_TEST_REPLY) {
+ srpc_test_reply_t *rep = &msg->msg_body.tes_reply;
+
+ __swab32s(&rep->tsr_status);
+ sfw_unpack_sid(rep->tsr_sid);
+ return;
+ }
+
+ if (msg->msg_type == SRPC_MSG_JOIN_REQST) {
+ srpc_join_reqst_t *req = &msg->msg_body.join_reqst;
+
+ __swab64s(&req->join_rpyid);
+ sfw_unpack_sid(req->join_sid);
+ return;
+ }
+
+ if (msg->msg_type == SRPC_MSG_JOIN_REPLY) {
+ srpc_join_reply_t *rep = &msg->msg_body.join_reply;
+
+ __swab32s(&rep->join_status);
+ __swab32s(&rep->join_timeout);
+ sfw_unpack_sid(rep->join_sid);
+ return;
+ }
+
+ LBUG();
+ return;
+}
+
+void
+sfw_abort_rpc(srpc_client_rpc_t *rpc)
+{
+ LASSERT(atomic_read(&rpc->crpc_refcount) > 0);
+ LASSERT(rpc->crpc_service <= SRPC_FRAMEWORK_SERVICE_MAX_ID);
+
+ spin_lock(&rpc->crpc_lock);
+ srpc_abort_rpc(rpc, -EINTR);
+ spin_unlock(&rpc->crpc_lock);
+ return;
+}
+
+void
+sfw_post_rpc(srpc_client_rpc_t *rpc)
+{
+ spin_lock(&rpc->crpc_lock);
+
+ LASSERT(!rpc->crpc_closed);
+ LASSERT(!rpc->crpc_aborted);
+ LASSERT(list_empty(&rpc->crpc_list));
+ LASSERT(!sfw_data.fw_shuttingdown);
+
+ rpc->crpc_timeout = rpc_timeout;
+ srpc_post_rpc(rpc);
+
+ spin_unlock(&rpc->crpc_lock);
+ return;
+}
+
+static srpc_service_t sfw_services[] = {
+ {
+ /* sv_id */ SRPC_SERVICE_DEBUG,
+ /* sv_name */ "debug",
+ 0
+ },
+ {
+ /* sv_id */ SRPC_SERVICE_QUERY_STAT,
+ /* sv_name */ "query stats",
+ 0
+ },
+ {
+ /* sv_id */ SRPC_SERVICE_MAKE_SESSION,
+ /* sv_name */ "make session",
+ 0
+ },
+ {
+ /* sv_id */ SRPC_SERVICE_REMOVE_SESSION,
+ /* sv_name */ "remove session",
+ 0
+ },
+ {
+ /* sv_id */ SRPC_SERVICE_BATCH,
+ /* sv_name */ "batch service",
+ 0
+ },
+ {
+ /* sv_id */ SRPC_SERVICE_TEST,
+ /* sv_name */ "test service",
+ 0
+ },
+ {
+ /* sv_id */ 0,
+ /* sv_name */ NULL,
+ 0
+ }
+};
+
+extern sfw_test_client_ops_t ping_test_client;
+extern srpc_service_t ping_test_service;
+extern void ping_init_test_client(void);
+extern void ping_init_test_service(void);
+
+extern sfw_test_client_ops_t brw_test_client;
+extern srpc_service_t brw_test_service;
+extern void brw_init_test_client(void);
+extern void brw_init_test_service(void);
+
+
+int
+sfw_startup(void)
+{
+ int i;
+ int rc;
+ int error;
+ srpc_service_t *sv;
+ sfw_test_case_t *tsc;
+
+
+ if (session_timeout < 0) {
+ CERROR("Session timeout must be non-negative: %d\n",
+ session_timeout);
+ return -EINVAL;
+ }
+
+ if (rpc_timeout < 0) {
+ CERROR("RPC timeout must be non-negative: %d\n",
+ rpc_timeout);
+ return -EINVAL;
+ }
+
+ if (session_timeout == 0)
+ CWARN("Zero session_timeout specified - test sessions never expire.\n");
+
+ if (rpc_timeout == 0)
+ CWARN("Zero rpc_timeout specified - test RPC never expire.\n");
+
+ memset(&sfw_data, 0, sizeof(struct smoketest_framework));
+
+ sfw_data.fw_session = NULL;
+ sfw_data.fw_active_srpc = NULL;
+ spin_lock_init(&sfw_data.fw_lock);
+ atomic_set(&sfw_data.fw_nzombies, 0);
+ INIT_LIST_HEAD(&sfw_data.fw_tests);
+ INIT_LIST_HEAD(&sfw_data.fw_zombie_rpcs);
+ INIT_LIST_HEAD(&sfw_data.fw_zombie_sessions);
+
+ brw_init_test_client();
+ brw_init_test_service();
+ rc = sfw_register_test(&brw_test_service, &brw_test_client);
+ LASSERT(rc == 0);
+
+ ping_init_test_client();
+ ping_init_test_service();
+ rc = sfw_register_test(&ping_test_service, &ping_test_client);
+ LASSERT(rc == 0);
+
+ error = 0;
+ list_for_each_entry(tsc, &sfw_data.fw_tests, tsc_list) {
+ sv = tsc->tsc_srv_service;
+
+ rc = srpc_add_service(sv);
+ LASSERT(rc != -EBUSY);
+ if (rc != 0) {
+ CWARN("Failed to add %s service: %d\n",
+ sv->sv_name, rc);
+ error = rc;
+ }
+ }
+
+ for (i = 0; ; i++) {
+ sv = &sfw_services[i];
+ if (sv->sv_name == NULL) break;
+
+ sv->sv_bulk_ready = NULL;
+ sv->sv_handler = sfw_handle_server_rpc;
+ sv->sv_wi_total = SFW_FRWK_WI_MAX;
+ if (sv->sv_id == SRPC_SERVICE_TEST)
+ sv->sv_bulk_ready = sfw_bulk_ready;
+
+ rc = srpc_add_service(sv);
+ LASSERT(rc != -EBUSY);
+ if (rc != 0) {
+ CWARN("Failed to add %s service: %d\n",
+ sv->sv_name, rc);
+ error = rc;
+ }
+
+ /* about to sfw_shutdown, no need to add buffer */
+ if (error) continue;
+
+ rc = srpc_service_add_buffers(sv, sv->sv_wi_total);
+ if (rc != 0) {
+ CWARN("Failed to reserve enough buffers: service %s, %d needed: %d\n",
+ sv->sv_name, sv->sv_wi_total, rc);
+ error = -ENOMEM;
+ }
+ }
+
+ if (error != 0)
+ sfw_shutdown();
+ return error;
+}
+
+void
+sfw_shutdown(void)
+{
+ srpc_service_t *sv;
+ sfw_test_case_t *tsc;
+ int i;
+
+ spin_lock(&sfw_data.fw_lock);
+
+ sfw_data.fw_shuttingdown = 1;
+ lst_wait_until(sfw_data.fw_active_srpc == NULL, sfw_data.fw_lock,
+ "waiting for active RPC to finish.\n");
+
+ if (sfw_del_session_timer() != 0)
+ lst_wait_until(sfw_data.fw_session == NULL, sfw_data.fw_lock,
+ "waiting for session timer to explode.\n");
+
+ sfw_deactivate_session();
+ lst_wait_until(atomic_read(&sfw_data.fw_nzombies) == 0,
+ sfw_data.fw_lock,
+ "waiting for %d zombie sessions to die.\n",
+ atomic_read(&sfw_data.fw_nzombies));
+
+ spin_unlock(&sfw_data.fw_lock);
+
+ for (i = 0; ; i++) {
+ sv = &sfw_services[i];
+ if (sv->sv_name == NULL)
+ break;
+
+ srpc_shutdown_service(sv);
+ srpc_remove_service(sv);
+ }
+
+ list_for_each_entry(tsc, &sfw_data.fw_tests, tsc_list) {
+ sv = tsc->tsc_srv_service;
+ srpc_shutdown_service(sv);
+ srpc_remove_service(sv);
+ }
+
+ while (!list_empty(&sfw_data.fw_zombie_rpcs)) {
+ srpc_client_rpc_t *rpc;
+
+ rpc = list_entry(sfw_data.fw_zombie_rpcs.next,
+ srpc_client_rpc_t, crpc_list);
+ list_del(&rpc->crpc_list);
+
+ LIBCFS_FREE(rpc, srpc_client_rpc_size(rpc));
+ }
+
+ for (i = 0; ; i++) {
+ sv = &sfw_services[i];
+ if (sv->sv_name == NULL)
+ break;
+
+ srpc_wait_service_shutdown(sv);
+ }
+
+ while (!list_empty(&sfw_data.fw_tests)) {
+ tsc = list_entry(sfw_data.fw_tests.next,
+ sfw_test_case_t, tsc_list);
+
+ srpc_wait_service_shutdown(tsc->tsc_srv_service);
+
+ list_del(&tsc->tsc_list);
+ LIBCFS_FREE(tsc, sizeof(*tsc));
+ }
+
+ return;
+}
diff --git a/drivers/staging/lustre/lnet/selftest/module.c b/drivers/staging/lustre/lnet/selftest/module.c
new file mode 100644
index 000000000..7ad62f167
--- /dev/null
+++ b/drivers/staging/lustre/lnet/selftest/module.c
@@ -0,0 +1,159 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2012, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ */
+
+#define DEBUG_SUBSYSTEM S_LNET
+
+#include "selftest.h"
+
+enum {
+ LST_INIT_NONE = 0,
+ LST_INIT_WI_SERIAL,
+ LST_INIT_WI_TEST,
+ LST_INIT_RPC,
+ LST_INIT_FW,
+ LST_INIT_CONSOLE
+};
+
+extern int lstcon_console_init(void);
+extern int lstcon_console_fini(void);
+
+static int lst_init_step = LST_INIT_NONE;
+
+struct cfs_wi_sched *lst_sched_serial;
+struct cfs_wi_sched **lst_sched_test;
+
+static void
+lnet_selftest_fini(void)
+{
+ int i;
+
+ switch (lst_init_step) {
+ case LST_INIT_CONSOLE:
+ lstcon_console_fini();
+ case LST_INIT_FW:
+ sfw_shutdown();
+ case LST_INIT_RPC:
+ srpc_shutdown();
+ case LST_INIT_WI_TEST:
+ for (i = 0;
+ i < cfs_cpt_number(lnet_cpt_table()); i++) {
+ if (lst_sched_test[i] == NULL)
+ continue;
+ cfs_wi_sched_destroy(lst_sched_test[i]);
+ }
+ LIBCFS_FREE(lst_sched_test,
+ sizeof(lst_sched_test[0]) *
+ cfs_cpt_number(lnet_cpt_table()));
+ lst_sched_test = NULL;
+
+ case LST_INIT_WI_SERIAL:
+ cfs_wi_sched_destroy(lst_sched_serial);
+ lst_sched_serial = NULL;
+ case LST_INIT_NONE:
+ break;
+ default:
+ LBUG();
+ }
+}
+
+static int
+lnet_selftest_init(void)
+{
+ int nscheds;
+ int rc;
+ int i;
+
+ rc = cfs_wi_sched_create("lst_s", lnet_cpt_table(), CFS_CPT_ANY,
+ 1, &lst_sched_serial);
+ if (rc != 0) {
+ CERROR("Failed to create serial WI scheduler for LST\n");
+ return rc;
+ }
+ lst_init_step = LST_INIT_WI_SERIAL;
+
+ nscheds = cfs_cpt_number(lnet_cpt_table());
+ LIBCFS_ALLOC(lst_sched_test, sizeof(lst_sched_test[0]) * nscheds);
+ if (lst_sched_test == NULL)
+ goto error;
+
+ lst_init_step = LST_INIT_WI_TEST;
+ for (i = 0; i < nscheds; i++) {
+ int nthrs = cfs_cpt_weight(lnet_cpt_table(), i);
+
+ /* reserve at least one CPU for LND */
+ nthrs = max(nthrs - 1, 1);
+ rc = cfs_wi_sched_create("lst_t", lnet_cpt_table(), i,
+ nthrs, &lst_sched_test[i]);
+ if (rc != 0) {
+ CERROR("Failed to create CPT affinity WI scheduler %d for LST\n",
+ i);
+ goto error;
+ }
+ }
+
+ rc = srpc_startup();
+ if (rc != 0) {
+ CERROR("LST can't startup rpc\n");
+ goto error;
+ }
+ lst_init_step = LST_INIT_RPC;
+
+ rc = sfw_startup();
+ if (rc != 0) {
+ CERROR("LST can't startup framework\n");
+ goto error;
+ }
+ lst_init_step = LST_INIT_FW;
+
+ rc = lstcon_console_init();
+ if (rc != 0) {
+ CERROR("LST can't startup console\n");
+ goto error;
+ }
+ lst_init_step = LST_INIT_CONSOLE;
+ return 0;
+error:
+ lnet_selftest_fini();
+ return rc;
+}
+
+
+MODULE_DESCRIPTION("LNet Selftest");
+MODULE_LICENSE("GPL");
+MODULE_VERSION("0.9.0");
+
+module_init(lnet_selftest_init);
+module_exit(lnet_selftest_fini);
diff --git a/drivers/staging/lustre/lnet/selftest/ping_test.c b/drivers/staging/lustre/lnet/selftest/ping_test.c
new file mode 100644
index 000000000..644069a9f
--- /dev/null
+++ b/drivers/staging/lustre/lnet/selftest/ping_test.c
@@ -0,0 +1,230 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2012, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ *
+ * lnet/selftest/conctl.c
+ *
+ * Test client & Server
+ *
+ * Author: Liang Zhen <liangzhen@clusterfs.com>
+ */
+
+#include "selftest.h"
+
+#define LST_PING_TEST_MAGIC 0xbabeface
+
+static int ping_srv_workitems = SFW_TEST_WI_MAX;
+module_param(ping_srv_workitems, int, 0644);
+MODULE_PARM_DESC(ping_srv_workitems, "# PING server workitems");
+
+typedef struct {
+ spinlock_t pnd_lock; /* serialize */
+ int pnd_counter; /* sequence counter */
+} lst_ping_data_t;
+
+static lst_ping_data_t lst_ping_data;
+
+static int
+ping_client_init(sfw_test_instance_t *tsi)
+{
+ sfw_session_t *sn = tsi->tsi_batch->bat_session;
+
+ LASSERT(tsi->tsi_is_client);
+ LASSERT(sn != NULL && (sn->sn_features & ~LST_FEATS_MASK) == 0);
+
+ spin_lock_init(&lst_ping_data.pnd_lock);
+ lst_ping_data.pnd_counter = 0;
+
+ return 0;
+}
+
+static void
+ping_client_fini(sfw_test_instance_t *tsi)
+{
+ sfw_session_t *sn = tsi->tsi_batch->bat_session;
+ int errors;
+
+ LASSERT(sn != NULL);
+ LASSERT(tsi->tsi_is_client);
+
+ errors = atomic_read(&sn->sn_ping_errors);
+ if (errors)
+ CWARN("%d pings have failed.\n", errors);
+ else
+ CDEBUG(D_NET, "Ping test finished OK.\n");
+}
+
+static int
+ping_client_prep_rpc(sfw_test_unit_t *tsu,
+ lnet_process_id_t dest, srpc_client_rpc_t **rpc)
+{
+ srpc_ping_reqst_t *req;
+ sfw_test_instance_t *tsi = tsu->tsu_instance;
+ sfw_session_t *sn = tsi->tsi_batch->bat_session;
+ struct timeval tv;
+ int rc;
+
+ LASSERT(sn != NULL);
+ LASSERT((sn->sn_features & ~LST_FEATS_MASK) == 0);
+
+ rc = sfw_create_test_rpc(tsu, dest, sn->sn_features, 0, 0, rpc);
+ if (rc != 0)
+ return rc;
+
+ req = &(*rpc)->crpc_reqstmsg.msg_body.ping_reqst;
+
+ req->pnr_magic = LST_PING_TEST_MAGIC;
+
+ spin_lock(&lst_ping_data.pnd_lock);
+ req->pnr_seq = lst_ping_data.pnd_counter++;
+ spin_unlock(&lst_ping_data.pnd_lock);
+
+ cfs_fs_timeval(&tv);
+ req->pnr_time_sec = tv.tv_sec;
+ req->pnr_time_usec = tv.tv_usec;
+
+ return rc;
+}
+
+static void
+ping_client_done_rpc(sfw_test_unit_t *tsu, srpc_client_rpc_t *rpc)
+{
+ sfw_test_instance_t *tsi = tsu->tsu_instance;
+ sfw_session_t *sn = tsi->tsi_batch->bat_session;
+ srpc_ping_reqst_t *reqst = &rpc->crpc_reqstmsg.msg_body.ping_reqst;
+ srpc_ping_reply_t *reply = &rpc->crpc_replymsg.msg_body.ping_reply;
+ struct timeval tv;
+
+ LASSERT(sn != NULL);
+
+ if (rpc->crpc_status != 0) {
+ if (!tsi->tsi_stopping) /* rpc could have been aborted */
+ atomic_inc(&sn->sn_ping_errors);
+ CERROR("Unable to ping %s (%d): %d\n",
+ libcfs_id2str(rpc->crpc_dest),
+ reqst->pnr_seq, rpc->crpc_status);
+ return;
+ }
+
+ if (rpc->crpc_replymsg.msg_magic != SRPC_MSG_MAGIC) {
+ __swab32s(&reply->pnr_seq);
+ __swab32s(&reply->pnr_magic);
+ __swab32s(&reply->pnr_status);
+ }
+
+ if (reply->pnr_magic != LST_PING_TEST_MAGIC) {
+ rpc->crpc_status = -EBADMSG;
+ atomic_inc(&sn->sn_ping_errors);
+ CERROR("Bad magic %u from %s, %u expected.\n",
+ reply->pnr_magic, libcfs_id2str(rpc->crpc_dest),
+ LST_PING_TEST_MAGIC);
+ return;
+ }
+
+ if (reply->pnr_seq != reqst->pnr_seq) {
+ rpc->crpc_status = -EBADMSG;
+ atomic_inc(&sn->sn_ping_errors);
+ CERROR("Bad seq %u from %s, %u expected.\n",
+ reply->pnr_seq, libcfs_id2str(rpc->crpc_dest),
+ reqst->pnr_seq);
+ return;
+ }
+
+ cfs_fs_timeval(&tv);
+ CDEBUG(D_NET, "%d reply in %u usec\n", reply->pnr_seq,
+ (unsigned)((tv.tv_sec - (unsigned)reqst->pnr_time_sec) * 1000000
+ + (tv.tv_usec - reqst->pnr_time_usec)));
+ return;
+}
+
+static int
+ping_server_handle(struct srpc_server_rpc *rpc)
+{
+ struct srpc_service *sv = rpc->srpc_scd->scd_svc;
+ srpc_msg_t *reqstmsg = &rpc->srpc_reqstbuf->buf_msg;
+ srpc_msg_t *replymsg = &rpc->srpc_replymsg;
+ srpc_ping_reqst_t *req = &reqstmsg->msg_body.ping_reqst;
+ srpc_ping_reply_t *rep = &rpc->srpc_replymsg.msg_body.ping_reply;
+
+ LASSERT(sv->sv_id == SRPC_SERVICE_PING);
+
+ if (reqstmsg->msg_magic != SRPC_MSG_MAGIC) {
+ LASSERT(reqstmsg->msg_magic == __swab32(SRPC_MSG_MAGIC));
+
+ __swab32s(&req->pnr_seq);
+ __swab32s(&req->pnr_magic);
+ __swab64s(&req->pnr_time_sec);
+ __swab64s(&req->pnr_time_usec);
+ }
+ LASSERT(reqstmsg->msg_type == srpc_service2request(sv->sv_id));
+
+ if (req->pnr_magic != LST_PING_TEST_MAGIC) {
+ CERROR("Unexpected magic %08x from %s\n",
+ req->pnr_magic, libcfs_id2str(rpc->srpc_peer));
+ return -EINVAL;
+ }
+
+ rep->pnr_seq = req->pnr_seq;
+ rep->pnr_magic = LST_PING_TEST_MAGIC;
+
+ if ((reqstmsg->msg_ses_feats & ~LST_FEATS_MASK) != 0) {
+ replymsg->msg_ses_feats = LST_FEATS_MASK;
+ rep->pnr_status = EPROTO;
+ return 0;
+ }
+
+ replymsg->msg_ses_feats = reqstmsg->msg_ses_feats;
+
+ CDEBUG(D_NET, "Get ping %d from %s\n",
+ req->pnr_seq, libcfs_id2str(rpc->srpc_peer));
+ return 0;
+}
+
+sfw_test_client_ops_t ping_test_client;
+void ping_init_test_client(void)
+{
+ ping_test_client.tso_init = ping_client_init;
+ ping_test_client.tso_fini = ping_client_fini;
+ ping_test_client.tso_prep_rpc = ping_client_prep_rpc;
+ ping_test_client.tso_done_rpc = ping_client_done_rpc;
+}
+
+srpc_service_t ping_test_service;
+void ping_init_test_service(void)
+{
+ ping_test_service.sv_id = SRPC_SERVICE_PING;
+ ping_test_service.sv_name = "ping_test";
+ ping_test_service.sv_handler = ping_server_handle;
+ ping_test_service.sv_wi_total = ping_srv_workitems;
+}
diff --git a/drivers/staging/lustre/lnet/selftest/rpc.c b/drivers/staging/lustre/lnet/selftest/rpc.c
new file mode 100644
index 000000000..080788ab7
--- /dev/null
+++ b/drivers/staging/lustre/lnet/selftest/rpc.c
@@ -0,0 +1,1673 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2012, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ *
+ * lnet/selftest/rpc.c
+ *
+ * Author: Isaac Huang <isaac@clusterfs.com>
+ *
+ * 2012-05-13: Liang Zhen <liang@whamcloud.com>
+ * - percpt data for service to improve smp performance
+ * - code cleanup
+ */
+
+#define DEBUG_SUBSYSTEM S_LNET
+
+#include "selftest.h"
+
+typedef enum {
+ SRPC_STATE_NONE,
+ SRPC_STATE_NI_INIT,
+ SRPC_STATE_EQ_INIT,
+ SRPC_STATE_RUNNING,
+ SRPC_STATE_STOPPING,
+} srpc_state_t;
+
+static struct smoketest_rpc {
+ spinlock_t rpc_glock; /* global lock */
+ srpc_service_t *rpc_services[SRPC_SERVICE_MAX_ID + 1];
+ lnet_handle_eq_t rpc_lnet_eq; /* _the_ LNet event queue */
+ srpc_state_t rpc_state;
+ srpc_counters_t rpc_counters;
+ __u64 rpc_matchbits; /* matchbits counter */
+} srpc_data;
+
+static inline int
+srpc_serv_portal(int svc_id)
+{
+ return svc_id < SRPC_FRAMEWORK_SERVICE_MAX_ID ?
+ SRPC_FRAMEWORK_REQUEST_PORTAL : SRPC_REQUEST_PORTAL;
+}
+
+/* forward ref's */
+int srpc_handle_rpc(swi_workitem_t *wi);
+
+void srpc_get_counters(srpc_counters_t *cnt)
+{
+ spin_lock(&srpc_data.rpc_glock);
+ *cnt = srpc_data.rpc_counters;
+ spin_unlock(&srpc_data.rpc_glock);
+}
+
+void srpc_set_counters(const srpc_counters_t *cnt)
+{
+ spin_lock(&srpc_data.rpc_glock);
+ srpc_data.rpc_counters = *cnt;
+ spin_unlock(&srpc_data.rpc_glock);
+}
+
+static int
+srpc_add_bulk_page(srpc_bulk_t *bk, struct page *pg, int i, int nob)
+{
+ nob = min(nob, (int)PAGE_CACHE_SIZE);
+
+ LASSERT(nob > 0);
+ LASSERT(i >= 0 && i < bk->bk_niov);
+
+ bk->bk_iovs[i].kiov_offset = 0;
+ bk->bk_iovs[i].kiov_page = pg;
+ bk->bk_iovs[i].kiov_len = nob;
+ return nob;
+}
+
+void
+srpc_free_bulk(srpc_bulk_t *bk)
+{
+ int i;
+ struct page *pg;
+
+ LASSERT(bk != NULL);
+
+ for (i = 0; i < bk->bk_niov; i++) {
+ pg = bk->bk_iovs[i].kiov_page;
+ if (pg == NULL)
+ break;
+
+ __free_page(pg);
+ }
+
+ LIBCFS_FREE(bk, offsetof(srpc_bulk_t, bk_iovs[bk->bk_niov]));
+ return;
+}
+
+srpc_bulk_t *
+srpc_alloc_bulk(int cpt, unsigned bulk_npg, unsigned bulk_len, int sink)
+{
+ srpc_bulk_t *bk;
+ int i;
+
+ LASSERT(bulk_npg > 0 && bulk_npg <= LNET_MAX_IOV);
+
+ LIBCFS_CPT_ALLOC(bk, lnet_cpt_table(), cpt,
+ offsetof(srpc_bulk_t, bk_iovs[bulk_npg]));
+ if (bk == NULL) {
+ CERROR("Can't allocate descriptor for %d pages\n", bulk_npg);
+ return NULL;
+ }
+
+ memset(bk, 0, offsetof(srpc_bulk_t, bk_iovs[bulk_npg]));
+ bk->bk_sink = sink;
+ bk->bk_len = bulk_len;
+ bk->bk_niov = bulk_npg;
+
+ for (i = 0; i < bulk_npg; i++) {
+ struct page *pg;
+ int nob;
+
+ pg = alloc_pages_node(cfs_cpt_spread_node(lnet_cpt_table(), cpt),
+ GFP_IOFS, 0);
+ if (pg == NULL) {
+ CERROR("Can't allocate page %d of %d\n", i, bulk_npg);
+ srpc_free_bulk(bk);
+ return NULL;
+ }
+
+ nob = srpc_add_bulk_page(bk, pg, i, bulk_len);
+ bulk_len -= nob;
+ }
+
+ return bk;
+}
+
+static inline __u64
+srpc_next_id(void)
+{
+ __u64 id;
+
+ spin_lock(&srpc_data.rpc_glock);
+ id = srpc_data.rpc_matchbits++;
+ spin_unlock(&srpc_data.rpc_glock);
+ return id;
+}
+
+static void
+srpc_init_server_rpc(struct srpc_server_rpc *rpc,
+ struct srpc_service_cd *scd,
+ struct srpc_buffer *buffer)
+{
+ memset(rpc, 0, sizeof(*rpc));
+ swi_init_workitem(&rpc->srpc_wi, rpc, srpc_handle_rpc,
+ srpc_serv_is_framework(scd->scd_svc) ?
+ lst_sched_serial : lst_sched_test[scd->scd_cpt]);
+
+ rpc->srpc_ev.ev_fired = 1; /* no event expected now */
+
+ rpc->srpc_scd = scd;
+ rpc->srpc_reqstbuf = buffer;
+ rpc->srpc_peer = buffer->buf_peer;
+ rpc->srpc_self = buffer->buf_self;
+ LNetInvalidateHandle(&rpc->srpc_replymdh);
+}
+
+static void
+srpc_service_fini(struct srpc_service *svc)
+{
+ struct srpc_service_cd *scd;
+ struct srpc_server_rpc *rpc;
+ struct srpc_buffer *buf;
+ struct list_head *q;
+ int i;
+
+ if (svc->sv_cpt_data == NULL)
+ return;
+
+ cfs_percpt_for_each(scd, i, svc->sv_cpt_data) {
+ while (1) {
+ if (!list_empty(&scd->scd_buf_posted))
+ q = &scd->scd_buf_posted;
+ else if (!list_empty(&scd->scd_buf_blocked))
+ q = &scd->scd_buf_blocked;
+ else
+ break;
+
+ while (!list_empty(q)) {
+ buf = list_entry(q->next,
+ struct srpc_buffer,
+ buf_list);
+ list_del(&buf->buf_list);
+ LIBCFS_FREE(buf, sizeof(*buf));
+ }
+ }
+
+ LASSERT(list_empty(&scd->scd_rpc_active));
+
+ while (!list_empty(&scd->scd_rpc_free)) {
+ rpc = list_entry(scd->scd_rpc_free.next,
+ struct srpc_server_rpc,
+ srpc_list);
+ list_del(&rpc->srpc_list);
+ LIBCFS_FREE(rpc, sizeof(*rpc));
+ }
+ }
+
+ cfs_percpt_free(svc->sv_cpt_data);
+ svc->sv_cpt_data = NULL;
+}
+
+static int
+srpc_service_nrpcs(struct srpc_service *svc)
+{
+ int nrpcs = svc->sv_wi_total / svc->sv_ncpts;
+
+ return srpc_serv_is_framework(svc) ?
+ max(nrpcs, SFW_FRWK_WI_MIN) : max(nrpcs, SFW_TEST_WI_MIN);
+}
+
+int srpc_add_buffer(struct swi_workitem *wi);
+
+static int
+srpc_service_init(struct srpc_service *svc)
+{
+ struct srpc_service_cd *scd;
+ struct srpc_server_rpc *rpc;
+ int nrpcs;
+ int i;
+ int j;
+
+ svc->sv_shuttingdown = 0;
+
+ svc->sv_cpt_data = cfs_percpt_alloc(lnet_cpt_table(),
+ sizeof(struct srpc_service_cd));
+ if (svc->sv_cpt_data == NULL)
+ return -ENOMEM;
+
+ svc->sv_ncpts = srpc_serv_is_framework(svc) ?
+ 1 : cfs_cpt_number(lnet_cpt_table());
+ nrpcs = srpc_service_nrpcs(svc);
+
+ cfs_percpt_for_each(scd, i, svc->sv_cpt_data) {
+ scd->scd_cpt = i;
+ scd->scd_svc = svc;
+ spin_lock_init(&scd->scd_lock);
+ INIT_LIST_HEAD(&scd->scd_rpc_free);
+ INIT_LIST_HEAD(&scd->scd_rpc_active);
+ INIT_LIST_HEAD(&scd->scd_buf_posted);
+ INIT_LIST_HEAD(&scd->scd_buf_blocked);
+
+ scd->scd_ev.ev_data = scd;
+ scd->scd_ev.ev_type = SRPC_REQUEST_RCVD;
+
+ /* NB: don't use lst_sched_serial for adding buffer,
+ * see details in srpc_service_add_buffers() */
+ swi_init_workitem(&scd->scd_buf_wi, scd,
+ srpc_add_buffer, lst_sched_test[i]);
+
+ if (i != 0 && srpc_serv_is_framework(svc)) {
+ /* NB: framework service only needs srpc_service_cd for
+ * one partition, but we allocate for all to make
+ * it easier to implement, it will waste a little
+ * memory but nobody should care about this */
+ continue;
+ }
+
+ for (j = 0; j < nrpcs; j++) {
+ LIBCFS_CPT_ALLOC(rpc, lnet_cpt_table(),
+ i, sizeof(*rpc));
+ if (rpc == NULL) {
+ srpc_service_fini(svc);
+ return -ENOMEM;
+ }
+ list_add(&rpc->srpc_list, &scd->scd_rpc_free);
+ }
+ }
+
+ return 0;
+}
+
+int
+srpc_add_service(struct srpc_service *sv)
+{
+ int id = sv->sv_id;
+
+ LASSERT(0 <= id && id <= SRPC_SERVICE_MAX_ID);
+
+ if (srpc_service_init(sv) != 0)
+ return -ENOMEM;
+
+ spin_lock(&srpc_data.rpc_glock);
+
+ LASSERT(srpc_data.rpc_state == SRPC_STATE_RUNNING);
+
+ if (srpc_data.rpc_services[id] != NULL) {
+ spin_unlock(&srpc_data.rpc_glock);
+ goto failed;
+ }
+
+ srpc_data.rpc_services[id] = sv;
+ spin_unlock(&srpc_data.rpc_glock);
+
+ CDEBUG(D_NET, "Adding service: id %d, name %s\n", id, sv->sv_name);
+ return 0;
+
+ failed:
+ srpc_service_fini(sv);
+ return -EBUSY;
+}
+
+int
+srpc_remove_service(srpc_service_t *sv)
+{
+ int id = sv->sv_id;
+
+ spin_lock(&srpc_data.rpc_glock);
+
+ if (srpc_data.rpc_services[id] != sv) {
+ spin_unlock(&srpc_data.rpc_glock);
+ return -ENOENT;
+ }
+
+ srpc_data.rpc_services[id] = NULL;
+ spin_unlock(&srpc_data.rpc_glock);
+ return 0;
+}
+
+static int
+srpc_post_passive_rdma(int portal, int local, __u64 matchbits, void *buf,
+ int len, int options, lnet_process_id_t peer,
+ lnet_handle_md_t *mdh, srpc_event_t *ev)
+{
+ int rc;
+ lnet_md_t md;
+ lnet_handle_me_t meh;
+
+ rc = LNetMEAttach(portal, peer, matchbits, 0, LNET_UNLINK,
+ local ? LNET_INS_LOCAL : LNET_INS_AFTER, &meh);
+ if (rc != 0) {
+ CERROR("LNetMEAttach failed: %d\n", rc);
+ LASSERT(rc == -ENOMEM);
+ return -ENOMEM;
+ }
+
+ md.threshold = 1;
+ md.user_ptr = ev;
+ md.start = buf;
+ md.length = len;
+ md.options = options;
+ md.eq_handle = srpc_data.rpc_lnet_eq;
+
+ rc = LNetMDAttach(meh, md, LNET_UNLINK, mdh);
+ if (rc != 0) {
+ CERROR("LNetMDAttach failed: %d\n", rc);
+ LASSERT(rc == -ENOMEM);
+
+ rc = LNetMEUnlink(meh);
+ LASSERT(rc == 0);
+ return -ENOMEM;
+ }
+
+ CDEBUG(D_NET,
+ "Posted passive RDMA: peer %s, portal %d, matchbits %#llx\n",
+ libcfs_id2str(peer), portal, matchbits);
+ return 0;
+}
+
+static int
+srpc_post_active_rdma(int portal, __u64 matchbits, void *buf, int len,
+ int options, lnet_process_id_t peer, lnet_nid_t self,
+ lnet_handle_md_t *mdh, srpc_event_t *ev)
+{
+ int rc;
+ lnet_md_t md;
+
+ md.user_ptr = ev;
+ md.start = buf;
+ md.length = len;
+ md.eq_handle = srpc_data.rpc_lnet_eq;
+ md.threshold = ((options & LNET_MD_OP_GET) != 0) ? 2 : 1;
+ md.options = options & ~(LNET_MD_OP_PUT | LNET_MD_OP_GET);
+
+ rc = LNetMDBind(md, LNET_UNLINK, mdh);
+ if (rc != 0) {
+ CERROR("LNetMDBind failed: %d\n", rc);
+ LASSERT(rc == -ENOMEM);
+ return -ENOMEM;
+ }
+
+ /* this is kind of an abuse of the LNET_MD_OP_{PUT,GET} options.
+ * they're only meaningful for MDs attached to an ME (i.e. passive
+ * buffers... */
+ if ((options & LNET_MD_OP_PUT) != 0) {
+ rc = LNetPut(self, *mdh, LNET_NOACK_REQ, peer,
+ portal, matchbits, 0, 0);
+ } else {
+ LASSERT((options & LNET_MD_OP_GET) != 0);
+
+ rc = LNetGet(self, *mdh, peer, portal, matchbits, 0);
+ }
+
+ if (rc != 0) {
+ CERROR("LNet%s(%s, %d, %lld) failed: %d\n",
+ ((options & LNET_MD_OP_PUT) != 0) ? "Put" : "Get",
+ libcfs_id2str(peer), portal, matchbits, rc);
+
+ /* The forthcoming unlink event will complete this operation
+ * with failure, so fall through and return success here.
+ */
+ rc = LNetMDUnlink(*mdh);
+ LASSERT(rc == 0);
+ } else {
+ CDEBUG(D_NET,
+ "Posted active RDMA: peer %s, portal %u, matchbits %#llx\n",
+ libcfs_id2str(peer), portal, matchbits);
+ }
+ return 0;
+}
+
+static int
+srpc_post_active_rqtbuf(lnet_process_id_t peer, int service, void *buf,
+ int len, lnet_handle_md_t *mdh, srpc_event_t *ev)
+{
+ return srpc_post_active_rdma(srpc_serv_portal(service), service,
+ buf, len, LNET_MD_OP_PUT, peer,
+ LNET_NID_ANY, mdh, ev);
+}
+
+static int
+srpc_post_passive_rqtbuf(int service, int local, void *buf, int len,
+ lnet_handle_md_t *mdh, srpc_event_t *ev)
+{
+ lnet_process_id_t any = {0};
+
+ any.nid = LNET_NID_ANY;
+ any.pid = LNET_PID_ANY;
+
+ return srpc_post_passive_rdma(srpc_serv_portal(service),
+ local, service, buf, len,
+ LNET_MD_OP_PUT, any, mdh, ev);
+}
+
+static int
+srpc_service_post_buffer(struct srpc_service_cd *scd, struct srpc_buffer *buf)
+ __must_hold(&scd->scd_lock)
+{
+ struct srpc_service *sv = scd->scd_svc;
+ struct srpc_msg *msg = &buf->buf_msg;
+ int rc;
+
+ LNetInvalidateHandle(&buf->buf_mdh);
+ list_add(&buf->buf_list, &scd->scd_buf_posted);
+ scd->scd_buf_nposted++;
+ spin_unlock(&scd->scd_lock);
+
+ rc = srpc_post_passive_rqtbuf(sv->sv_id,
+ !srpc_serv_is_framework(sv),
+ msg, sizeof(*msg), &buf->buf_mdh,
+ &scd->scd_ev);
+
+ /* At this point, a RPC (new or delayed) may have arrived in
+ * msg and its event handler has been called. So we must add
+ * buf to scd_buf_posted _before_ dropping scd_lock */
+
+ spin_lock(&scd->scd_lock);
+
+ if (rc == 0) {
+ if (!sv->sv_shuttingdown)
+ return 0;
+
+ spin_unlock(&scd->scd_lock);
+ /* srpc_shutdown_service might have tried to unlink me
+ * when my buf_mdh was still invalid */
+ LNetMDUnlink(buf->buf_mdh);
+ spin_lock(&scd->scd_lock);
+ return 0;
+ }
+
+ scd->scd_buf_nposted--;
+ if (sv->sv_shuttingdown)
+ return rc; /* don't allow to change scd_buf_posted */
+
+ list_del(&buf->buf_list);
+ spin_unlock(&scd->scd_lock);
+
+ LIBCFS_FREE(buf, sizeof(*buf));
+
+ spin_lock(&scd->scd_lock);
+ return rc;
+}
+
+int
+srpc_add_buffer(struct swi_workitem *wi)
+{
+ struct srpc_service_cd *scd = wi->swi_workitem.wi_data;
+ struct srpc_buffer *buf;
+ int rc = 0;
+
+ /* it's called by workitem scheduler threads, these threads
+ * should have been set CPT affinity, so buffers will be posted
+ * on CPT local list of Portal */
+ spin_lock(&scd->scd_lock);
+
+ while (scd->scd_buf_adjust > 0 &&
+ !scd->scd_svc->sv_shuttingdown) {
+ scd->scd_buf_adjust--; /* consume it */
+ scd->scd_buf_posting++;
+
+ spin_unlock(&scd->scd_lock);
+
+ LIBCFS_ALLOC(buf, sizeof(*buf));
+ if (buf == NULL) {
+ CERROR("Failed to add new buf to service: %s\n",
+ scd->scd_svc->sv_name);
+ spin_lock(&scd->scd_lock);
+ rc = -ENOMEM;
+ break;
+ }
+
+ spin_lock(&scd->scd_lock);
+ if (scd->scd_svc->sv_shuttingdown) {
+ spin_unlock(&scd->scd_lock);
+ LIBCFS_FREE(buf, sizeof(*buf));
+
+ spin_lock(&scd->scd_lock);
+ rc = -ESHUTDOWN;
+ break;
+ }
+
+ rc = srpc_service_post_buffer(scd, buf);
+ if (rc != 0)
+ break; /* buf has been freed inside */
+
+ LASSERT(scd->scd_buf_posting > 0);
+ scd->scd_buf_posting--;
+ scd->scd_buf_total++;
+ scd->scd_buf_low = max(2, scd->scd_buf_total / 4);
+ }
+
+ if (rc != 0) {
+ scd->scd_buf_err_stamp = get_seconds();
+ scd->scd_buf_err = rc;
+
+ LASSERT(scd->scd_buf_posting > 0);
+ scd->scd_buf_posting--;
+ }
+
+ spin_unlock(&scd->scd_lock);
+ return 0;
+}
+
+int
+srpc_service_add_buffers(struct srpc_service *sv, int nbuffer)
+{
+ struct srpc_service_cd *scd;
+ int rc = 0;
+ int i;
+
+ LASSERTF(nbuffer > 0, "nbuffer must be positive: %d\n", nbuffer);
+
+ cfs_percpt_for_each(scd, i, sv->sv_cpt_data) {
+ spin_lock(&scd->scd_lock);
+
+ scd->scd_buf_err = 0;
+ scd->scd_buf_err_stamp = 0;
+ scd->scd_buf_posting = 0;
+ scd->scd_buf_adjust = nbuffer;
+ /* start to post buffers */
+ swi_schedule_workitem(&scd->scd_buf_wi);
+ spin_unlock(&scd->scd_lock);
+
+ /* framework service only post buffer for one partition */
+ if (srpc_serv_is_framework(sv))
+ break;
+ }
+
+ cfs_percpt_for_each(scd, i, sv->sv_cpt_data) {
+ spin_lock(&scd->scd_lock);
+ /*
+ * NB: srpc_service_add_buffers() can be called inside
+ * thread context of lst_sched_serial, and we don't normally
+ * allow to sleep inside thread context of WI scheduler
+ * because it will block current scheduler thread from doing
+ * anything else, even worse, it could deadlock if it's
+ * waiting on result from another WI of the same scheduler.
+ * However, it's safe at here because scd_buf_wi is scheduled
+ * by thread in a different WI scheduler (lst_sched_test),
+ * so we don't have any risk of deadlock, though this could
+ * block all WIs pending on lst_sched_serial for a moment
+ * which is not good but not fatal.
+ */
+ lst_wait_until(scd->scd_buf_err != 0 ||
+ (scd->scd_buf_adjust == 0 &&
+ scd->scd_buf_posting == 0),
+ scd->scd_lock, "waiting for adding buffer\n");
+
+ if (scd->scd_buf_err != 0 && rc == 0)
+ rc = scd->scd_buf_err;
+
+ spin_unlock(&scd->scd_lock);
+ }
+
+ return rc;
+}
+
+void
+srpc_service_remove_buffers(struct srpc_service *sv, int nbuffer)
+{
+ struct srpc_service_cd *scd;
+ int num;
+ int i;
+
+ LASSERT(!sv->sv_shuttingdown);
+
+ cfs_percpt_for_each(scd, i, sv->sv_cpt_data) {
+ spin_lock(&scd->scd_lock);
+
+ num = scd->scd_buf_total + scd->scd_buf_posting;
+ scd->scd_buf_adjust -= min(nbuffer, num);
+
+ spin_unlock(&scd->scd_lock);
+ }
+}
+
+/* returns 1 if sv has finished, otherwise 0 */
+int
+srpc_finish_service(struct srpc_service *sv)
+{
+ struct srpc_service_cd *scd;
+ struct srpc_server_rpc *rpc;
+ int i;
+
+ LASSERT(sv->sv_shuttingdown); /* srpc_shutdown_service called */
+
+ cfs_percpt_for_each(scd, i, sv->sv_cpt_data) {
+ spin_lock(&scd->scd_lock);
+ if (!swi_deschedule_workitem(&scd->scd_buf_wi)) {
+ spin_unlock(&scd->scd_lock);
+ return 0;
+ }
+
+ if (scd->scd_buf_nposted > 0) {
+ CDEBUG(D_NET, "waiting for %d posted buffers to unlink",
+ scd->scd_buf_nposted);
+ spin_unlock(&scd->scd_lock);
+ return 0;
+ }
+
+ if (list_empty(&scd->scd_rpc_active)) {
+ spin_unlock(&scd->scd_lock);
+ continue;
+ }
+
+ rpc = list_entry(scd->scd_rpc_active.next,
+ struct srpc_server_rpc, srpc_list);
+ CNETERR("Active RPC %p on shutdown: sv %s, peer %s, wi %s scheduled %d running %d, ev fired %d type %d status %d lnet %d\n",
+ rpc, sv->sv_name, libcfs_id2str(rpc->srpc_peer),
+ swi_state2str(rpc->srpc_wi.swi_state),
+ rpc->srpc_wi.swi_workitem.wi_scheduled,
+ rpc->srpc_wi.swi_workitem.wi_running,
+ rpc->srpc_ev.ev_fired, rpc->srpc_ev.ev_type,
+ rpc->srpc_ev.ev_status, rpc->srpc_ev.ev_lnet);
+ spin_unlock(&scd->scd_lock);
+ return 0;
+ }
+
+ /* no lock needed from now on */
+ srpc_service_fini(sv);
+ return 1;
+}
+
+/* called with sv->sv_lock held */
+static void
+srpc_service_recycle_buffer(struct srpc_service_cd *scd, srpc_buffer_t *buf)
+ __must_hold(&scd->scd_lock)
+{
+ if (!scd->scd_svc->sv_shuttingdown && scd->scd_buf_adjust >= 0) {
+ if (srpc_service_post_buffer(scd, buf) != 0) {
+ CWARN("Failed to post %s buffer\n",
+ scd->scd_svc->sv_name);
+ }
+ return;
+ }
+
+ /* service is shutting down, or we want to recycle some buffers */
+ scd->scd_buf_total--;
+
+ if (scd->scd_buf_adjust < 0) {
+ scd->scd_buf_adjust++;
+ if (scd->scd_buf_adjust < 0 &&
+ scd->scd_buf_total == 0 && scd->scd_buf_posting == 0) {
+ CDEBUG(D_INFO,
+ "Try to recycle %d buffers but nothing left\n",
+ scd->scd_buf_adjust);
+ scd->scd_buf_adjust = 0;
+ }
+ }
+
+ spin_unlock(&scd->scd_lock);
+ LIBCFS_FREE(buf, sizeof(*buf));
+ spin_lock(&scd->scd_lock);
+}
+
+void
+srpc_abort_service(struct srpc_service *sv)
+{
+ struct srpc_service_cd *scd;
+ struct srpc_server_rpc *rpc;
+ int i;
+
+ CDEBUG(D_NET, "Aborting service: id %d, name %s\n",
+ sv->sv_id, sv->sv_name);
+
+ cfs_percpt_for_each(scd, i, sv->sv_cpt_data) {
+ spin_lock(&scd->scd_lock);
+
+ /* schedule in-flight RPCs to notice the abort, NB:
+ * racing with incoming RPCs; complete fix should make test
+ * RPCs carry session ID in its headers */
+ list_for_each_entry(rpc, &scd->scd_rpc_active, srpc_list) {
+ rpc->srpc_aborted = 1;
+ swi_schedule_workitem(&rpc->srpc_wi);
+ }
+
+ spin_unlock(&scd->scd_lock);
+ }
+}
+
+void
+srpc_shutdown_service(srpc_service_t *sv)
+{
+ struct srpc_service_cd *scd;
+ struct srpc_server_rpc *rpc;
+ srpc_buffer_t *buf;
+ int i;
+
+ CDEBUG(D_NET, "Shutting down service: id %d, name %s\n",
+ sv->sv_id, sv->sv_name);
+
+ cfs_percpt_for_each(scd, i, sv->sv_cpt_data)
+ spin_lock(&scd->scd_lock);
+
+ sv->sv_shuttingdown = 1; /* i.e. no new active RPC */
+
+ cfs_percpt_for_each(scd, i, sv->sv_cpt_data)
+ spin_unlock(&scd->scd_lock);
+
+ cfs_percpt_for_each(scd, i, sv->sv_cpt_data) {
+ spin_lock(&scd->scd_lock);
+
+ /* schedule in-flight RPCs to notice the shutdown */
+ list_for_each_entry(rpc, &scd->scd_rpc_active, srpc_list)
+ swi_schedule_workitem(&rpc->srpc_wi);
+
+ spin_unlock(&scd->scd_lock);
+
+ /* OK to traverse scd_buf_posted without lock, since no one
+ * touches scd_buf_posted now */
+ list_for_each_entry(buf, &scd->scd_buf_posted, buf_list)
+ LNetMDUnlink(buf->buf_mdh);
+ }
+}
+
+static int
+srpc_send_request(srpc_client_rpc_t *rpc)
+{
+ srpc_event_t *ev = &rpc->crpc_reqstev;
+ int rc;
+
+ ev->ev_fired = 0;
+ ev->ev_data = rpc;
+ ev->ev_type = SRPC_REQUEST_SENT;
+
+ rc = srpc_post_active_rqtbuf(rpc->crpc_dest, rpc->crpc_service,
+ &rpc->crpc_reqstmsg, sizeof(srpc_msg_t),
+ &rpc->crpc_reqstmdh, ev);
+ if (rc != 0) {
+ LASSERT(rc == -ENOMEM);
+ ev->ev_fired = 1; /* no more event expected */
+ }
+ return rc;
+}
+
+static int
+srpc_prepare_reply(srpc_client_rpc_t *rpc)
+{
+ srpc_event_t *ev = &rpc->crpc_replyev;
+ __u64 *id = &rpc->crpc_reqstmsg.msg_body.reqst.rpyid;
+ int rc;
+
+ ev->ev_fired = 0;
+ ev->ev_data = rpc;
+ ev->ev_type = SRPC_REPLY_RCVD;
+
+ *id = srpc_next_id();
+
+ rc = srpc_post_passive_rdma(SRPC_RDMA_PORTAL, 0, *id,
+ &rpc->crpc_replymsg, sizeof(srpc_msg_t),
+ LNET_MD_OP_PUT, rpc->crpc_dest,
+ &rpc->crpc_replymdh, ev);
+ if (rc != 0) {
+ LASSERT(rc == -ENOMEM);
+ ev->ev_fired = 1; /* no more event expected */
+ }
+ return rc;
+}
+
+static int
+srpc_prepare_bulk(srpc_client_rpc_t *rpc)
+{
+ srpc_bulk_t *bk = &rpc->crpc_bulk;
+ srpc_event_t *ev = &rpc->crpc_bulkev;
+ __u64 *id = &rpc->crpc_reqstmsg.msg_body.reqst.bulkid;
+ int rc;
+ int opt;
+
+ LASSERT(bk->bk_niov <= LNET_MAX_IOV);
+
+ if (bk->bk_niov == 0)
+ return 0; /* nothing to do */
+
+ opt = bk->bk_sink ? LNET_MD_OP_PUT : LNET_MD_OP_GET;
+ opt |= LNET_MD_KIOV;
+
+ ev->ev_fired = 0;
+ ev->ev_data = rpc;
+ ev->ev_type = SRPC_BULK_REQ_RCVD;
+
+ *id = srpc_next_id();
+
+ rc = srpc_post_passive_rdma(SRPC_RDMA_PORTAL, 0, *id,
+ &bk->bk_iovs[0], bk->bk_niov, opt,
+ rpc->crpc_dest, &bk->bk_mdh, ev);
+ if (rc != 0) {
+ LASSERT(rc == -ENOMEM);
+ ev->ev_fired = 1; /* no more event expected */
+ }
+ return rc;
+}
+
+static int
+srpc_do_bulk(srpc_server_rpc_t *rpc)
+{
+ srpc_event_t *ev = &rpc->srpc_ev;
+ srpc_bulk_t *bk = rpc->srpc_bulk;
+ __u64 id = rpc->srpc_reqstbuf->buf_msg.msg_body.reqst.bulkid;
+ int rc;
+ int opt;
+
+ LASSERT(bk != NULL);
+
+ opt = bk->bk_sink ? LNET_MD_OP_GET : LNET_MD_OP_PUT;
+ opt |= LNET_MD_KIOV;
+
+ ev->ev_fired = 0;
+ ev->ev_data = rpc;
+ ev->ev_type = bk->bk_sink ? SRPC_BULK_GET_RPLD : SRPC_BULK_PUT_SENT;
+
+ rc = srpc_post_active_rdma(SRPC_RDMA_PORTAL, id,
+ &bk->bk_iovs[0], bk->bk_niov, opt,
+ rpc->srpc_peer, rpc->srpc_self,
+ &bk->bk_mdh, ev);
+ if (rc != 0)
+ ev->ev_fired = 1; /* no more event expected */
+ return rc;
+}
+
+/* only called from srpc_handle_rpc */
+static void
+srpc_server_rpc_done(srpc_server_rpc_t *rpc, int status)
+{
+ struct srpc_service_cd *scd = rpc->srpc_scd;
+ struct srpc_service *sv = scd->scd_svc;
+ srpc_buffer_t *buffer;
+
+ LASSERT(status != 0 || rpc->srpc_wi.swi_state == SWI_STATE_DONE);
+
+ rpc->srpc_status = status;
+
+ CDEBUG_LIMIT(status == 0 ? D_NET : D_NETERROR,
+ "Server RPC %p done: service %s, peer %s, status %s:%d\n",
+ rpc, sv->sv_name, libcfs_id2str(rpc->srpc_peer),
+ swi_state2str(rpc->srpc_wi.swi_state), status);
+
+ if (status != 0) {
+ spin_lock(&srpc_data.rpc_glock);
+ srpc_data.rpc_counters.rpcs_dropped++;
+ spin_unlock(&srpc_data.rpc_glock);
+ }
+
+ if (rpc->srpc_done != NULL)
+ (*rpc->srpc_done) (rpc);
+ LASSERT(rpc->srpc_bulk == NULL);
+
+ spin_lock(&scd->scd_lock);
+
+ if (rpc->srpc_reqstbuf != NULL) {
+ /* NB might drop sv_lock in srpc_service_recycle_buffer, but
+ * sv won't go away for scd_rpc_active must not be empty */
+ srpc_service_recycle_buffer(scd, rpc->srpc_reqstbuf);
+ rpc->srpc_reqstbuf = NULL;
+ }
+
+ list_del(&rpc->srpc_list); /* from scd->scd_rpc_active */
+
+ /*
+ * No one can schedule me now since:
+ * - I'm not on scd_rpc_active.
+ * - all LNet events have been fired.
+ * Cancel pending schedules and prevent future schedule attempts:
+ */
+ LASSERT(rpc->srpc_ev.ev_fired);
+ swi_exit_workitem(&rpc->srpc_wi);
+
+ if (!sv->sv_shuttingdown && !list_empty(&scd->scd_buf_blocked)) {
+ buffer = list_entry(scd->scd_buf_blocked.next,
+ srpc_buffer_t, buf_list);
+ list_del(&buffer->buf_list);
+
+ srpc_init_server_rpc(rpc, scd, buffer);
+ list_add_tail(&rpc->srpc_list, &scd->scd_rpc_active);
+ swi_schedule_workitem(&rpc->srpc_wi);
+ } else {
+ list_add(&rpc->srpc_list, &scd->scd_rpc_free);
+ }
+
+ spin_unlock(&scd->scd_lock);
+ return;
+}
+
+/* handles an incoming RPC */
+int
+srpc_handle_rpc(swi_workitem_t *wi)
+{
+ struct srpc_server_rpc *rpc = wi->swi_workitem.wi_data;
+ struct srpc_service_cd *scd = rpc->srpc_scd;
+ struct srpc_service *sv = scd->scd_svc;
+ srpc_event_t *ev = &rpc->srpc_ev;
+ int rc = 0;
+
+ LASSERT(wi == &rpc->srpc_wi);
+
+ spin_lock(&scd->scd_lock);
+
+ if (sv->sv_shuttingdown || rpc->srpc_aborted) {
+ spin_unlock(&scd->scd_lock);
+
+ if (rpc->srpc_bulk != NULL)
+ LNetMDUnlink(rpc->srpc_bulk->bk_mdh);
+ LNetMDUnlink(rpc->srpc_replymdh);
+
+ if (ev->ev_fired) { /* no more event, OK to finish */
+ srpc_server_rpc_done(rpc, -ESHUTDOWN);
+ return 1;
+ }
+ return 0;
+ }
+
+ spin_unlock(&scd->scd_lock);
+
+ switch (wi->swi_state) {
+ default:
+ LBUG();
+ case SWI_STATE_NEWBORN: {
+ srpc_msg_t *msg;
+ srpc_generic_reply_t *reply;
+
+ msg = &rpc->srpc_reqstbuf->buf_msg;
+ reply = &rpc->srpc_replymsg.msg_body.reply;
+
+ if (msg->msg_magic == 0) {
+ /* moaned already in srpc_lnet_ev_handler */
+ srpc_server_rpc_done(rpc, EBADMSG);
+ return 1;
+ }
+
+ srpc_unpack_msg_hdr(msg);
+ if (msg->msg_version != SRPC_MSG_VERSION) {
+ CWARN("Version mismatch: %u, %u expected, from %s\n",
+ msg->msg_version, SRPC_MSG_VERSION,
+ libcfs_id2str(rpc->srpc_peer));
+ reply->status = EPROTO;
+ /* drop through and send reply */
+ } else {
+ reply->status = 0;
+ rc = (*sv->sv_handler)(rpc);
+ LASSERT(reply->status == 0 || !rpc->srpc_bulk);
+ if (rc != 0) {
+ srpc_server_rpc_done(rpc, rc);
+ return 1;
+ }
+ }
+
+ wi->swi_state = SWI_STATE_BULK_STARTED;
+
+ if (rpc->srpc_bulk != NULL) {
+ rc = srpc_do_bulk(rpc);
+ if (rc == 0)
+ return 0; /* wait for bulk */
+
+ LASSERT(ev->ev_fired);
+ ev->ev_status = rc;
+ }
+ }
+ case SWI_STATE_BULK_STARTED:
+ LASSERT(rpc->srpc_bulk == NULL || ev->ev_fired);
+
+ if (rpc->srpc_bulk != NULL) {
+ rc = ev->ev_status;
+
+ if (sv->sv_bulk_ready != NULL)
+ rc = (*sv->sv_bulk_ready) (rpc, rc);
+
+ if (rc != 0) {
+ srpc_server_rpc_done(rpc, rc);
+ return 1;
+ }
+ }
+
+ wi->swi_state = SWI_STATE_REPLY_SUBMITTED;
+ rc = srpc_send_reply(rpc);
+ if (rc == 0)
+ return 0; /* wait for reply */
+ srpc_server_rpc_done(rpc, rc);
+ return 1;
+
+ case SWI_STATE_REPLY_SUBMITTED:
+ if (!ev->ev_fired) {
+ CERROR("RPC %p: bulk %p, service %d\n",
+ rpc, rpc->srpc_bulk, sv->sv_id);
+ CERROR("Event: status %d, type %d, lnet %d\n",
+ ev->ev_status, ev->ev_type, ev->ev_lnet);
+ LASSERT(ev->ev_fired);
+ }
+
+ wi->swi_state = SWI_STATE_DONE;
+ srpc_server_rpc_done(rpc, ev->ev_status);
+ return 1;
+ }
+
+ return 0;
+}
+
+static void
+srpc_client_rpc_expired(void *data)
+{
+ srpc_client_rpc_t *rpc = data;
+
+ CWARN("Client RPC expired: service %d, peer %s, timeout %d.\n",
+ rpc->crpc_service, libcfs_id2str(rpc->crpc_dest),
+ rpc->crpc_timeout);
+
+ spin_lock(&rpc->crpc_lock);
+
+ rpc->crpc_timeout = 0;
+ srpc_abort_rpc(rpc, -ETIMEDOUT);
+
+ spin_unlock(&rpc->crpc_lock);
+
+ spin_lock(&srpc_data.rpc_glock);
+ srpc_data.rpc_counters.rpcs_expired++;
+ spin_unlock(&srpc_data.rpc_glock);
+}
+
+inline void
+srpc_add_client_rpc_timer(srpc_client_rpc_t *rpc)
+{
+ stt_timer_t *timer = &rpc->crpc_timer;
+
+ if (rpc->crpc_timeout == 0)
+ return;
+
+ INIT_LIST_HEAD(&timer->stt_list);
+ timer->stt_data = rpc;
+ timer->stt_func = srpc_client_rpc_expired;
+ timer->stt_expires = cfs_time_add(rpc->crpc_timeout,
+ get_seconds());
+ stt_add_timer(timer);
+ return;
+}
+
+/*
+ * Called with rpc->crpc_lock held.
+ *
+ * Upon exit the RPC expiry timer is not queued and the handler is not
+ * running on any CPU. */
+static void
+srpc_del_client_rpc_timer(srpc_client_rpc_t *rpc)
+{
+ /* timer not planted or already exploded */
+ if (rpc->crpc_timeout == 0)
+ return;
+
+ /* timer successfully defused */
+ if (stt_del_timer(&rpc->crpc_timer))
+ return;
+
+ /* timer detonated, wait for it to explode */
+ while (rpc->crpc_timeout != 0) {
+ spin_unlock(&rpc->crpc_lock);
+
+ schedule();
+
+ spin_lock(&rpc->crpc_lock);
+ }
+}
+
+static void
+srpc_client_rpc_done(srpc_client_rpc_t *rpc, int status)
+{
+ swi_workitem_t *wi = &rpc->crpc_wi;
+
+ LASSERT(status != 0 || wi->swi_state == SWI_STATE_DONE);
+
+ spin_lock(&rpc->crpc_lock);
+
+ rpc->crpc_closed = 1;
+ if (rpc->crpc_status == 0)
+ rpc->crpc_status = status;
+
+ srpc_del_client_rpc_timer(rpc);
+
+ CDEBUG_LIMIT((status == 0) ? D_NET : D_NETERROR,
+ "Client RPC done: service %d, peer %s, status %s:%d:%d\n",
+ rpc->crpc_service, libcfs_id2str(rpc->crpc_dest),
+ swi_state2str(wi->swi_state), rpc->crpc_aborted, status);
+
+ /*
+ * No one can schedule me now since:
+ * - RPC timer has been defused.
+ * - all LNet events have been fired.
+ * - crpc_closed has been set, preventing srpc_abort_rpc from
+ * scheduling me.
+ * Cancel pending schedules and prevent future schedule attempts:
+ */
+ LASSERT(!srpc_event_pending(rpc));
+ swi_exit_workitem(wi);
+
+ spin_unlock(&rpc->crpc_lock);
+
+ (*rpc->crpc_done)(rpc);
+ return;
+}
+
+/* sends an outgoing RPC */
+int
+srpc_send_rpc(swi_workitem_t *wi)
+{
+ int rc = 0;
+ srpc_client_rpc_t *rpc;
+ srpc_msg_t *reply;
+ int do_bulk;
+
+ LASSERT(wi != NULL);
+
+ rpc = wi->swi_workitem.wi_data;
+
+ LASSERT(rpc != NULL);
+ LASSERT(wi == &rpc->crpc_wi);
+
+ reply = &rpc->crpc_replymsg;
+ do_bulk = rpc->crpc_bulk.bk_niov > 0;
+
+ spin_lock(&rpc->crpc_lock);
+
+ if (rpc->crpc_aborted) {
+ spin_unlock(&rpc->crpc_lock);
+ goto abort;
+ }
+
+ spin_unlock(&rpc->crpc_lock);
+
+ switch (wi->swi_state) {
+ default:
+ LBUG();
+ case SWI_STATE_NEWBORN:
+ LASSERT(!srpc_event_pending(rpc));
+
+ rc = srpc_prepare_reply(rpc);
+ if (rc != 0) {
+ srpc_client_rpc_done(rpc, rc);
+ return 1;
+ }
+
+ rc = srpc_prepare_bulk(rpc);
+ if (rc != 0)
+ break;
+
+ wi->swi_state = SWI_STATE_REQUEST_SUBMITTED;
+ rc = srpc_send_request(rpc);
+ break;
+
+ case SWI_STATE_REQUEST_SUBMITTED:
+ /* CAVEAT EMPTOR: rqtev, rpyev, and bulkev may come in any
+ * order; however, they're processed in a strict order:
+ * rqt, rpy, and bulk. */
+ if (!rpc->crpc_reqstev.ev_fired)
+ break;
+
+ rc = rpc->crpc_reqstev.ev_status;
+ if (rc != 0)
+ break;
+
+ wi->swi_state = SWI_STATE_REQUEST_SENT;
+ /* perhaps more events, fall thru */
+ case SWI_STATE_REQUEST_SENT: {
+ srpc_msg_type_t type = srpc_service2reply(rpc->crpc_service);
+
+ if (!rpc->crpc_replyev.ev_fired)
+ break;
+
+ rc = rpc->crpc_replyev.ev_status;
+ if (rc != 0)
+ break;
+
+ srpc_unpack_msg_hdr(reply);
+ if (reply->msg_type != type ||
+ (reply->msg_magic != SRPC_MSG_MAGIC &&
+ reply->msg_magic != __swab32(SRPC_MSG_MAGIC))) {
+ CWARN("Bad message from %s: type %u (%d expected), magic %u (%d expected).\n",
+ libcfs_id2str(rpc->crpc_dest),
+ reply->msg_type, type,
+ reply->msg_magic, SRPC_MSG_MAGIC);
+ rc = -EBADMSG;
+ break;
+ }
+
+ if (do_bulk && reply->msg_body.reply.status != 0) {
+ CWARN("Remote error %d at %s, unlink bulk buffer in case peer didn't initiate bulk transfer\n",
+ reply->msg_body.reply.status,
+ libcfs_id2str(rpc->crpc_dest));
+ LNetMDUnlink(rpc->crpc_bulk.bk_mdh);
+ }
+
+ wi->swi_state = SWI_STATE_REPLY_RECEIVED;
+ }
+ case SWI_STATE_REPLY_RECEIVED:
+ if (do_bulk && !rpc->crpc_bulkev.ev_fired)
+ break;
+
+ rc = do_bulk ? rpc->crpc_bulkev.ev_status : 0;
+
+ /* Bulk buffer was unlinked due to remote error. Clear error
+ * since reply buffer still contains valid data.
+ * NB rpc->crpc_done shouldn't look into bulk data in case of
+ * remote error. */
+ if (do_bulk && rpc->crpc_bulkev.ev_lnet == LNET_EVENT_UNLINK &&
+ rpc->crpc_status == 0 && reply->msg_body.reply.status != 0)
+ rc = 0;
+
+ wi->swi_state = SWI_STATE_DONE;
+ srpc_client_rpc_done(rpc, rc);
+ return 1;
+ }
+
+ if (rc != 0) {
+ spin_lock(&rpc->crpc_lock);
+ srpc_abort_rpc(rpc, rc);
+ spin_unlock(&rpc->crpc_lock);
+ }
+
+abort:
+ if (rpc->crpc_aborted) {
+ LNetMDUnlink(rpc->crpc_reqstmdh);
+ LNetMDUnlink(rpc->crpc_replymdh);
+ LNetMDUnlink(rpc->crpc_bulk.bk_mdh);
+
+ if (!srpc_event_pending(rpc)) {
+ srpc_client_rpc_done(rpc, -EINTR);
+ return 1;
+ }
+ }
+ return 0;
+}
+
+srpc_client_rpc_t *
+srpc_create_client_rpc(lnet_process_id_t peer, int service,
+ int nbulkiov, int bulklen,
+ void (*rpc_done)(srpc_client_rpc_t *),
+ void (*rpc_fini)(srpc_client_rpc_t *), void *priv)
+{
+ srpc_client_rpc_t *rpc;
+
+ LIBCFS_ALLOC(rpc, offsetof(srpc_client_rpc_t,
+ crpc_bulk.bk_iovs[nbulkiov]));
+ if (rpc == NULL)
+ return NULL;
+
+ srpc_init_client_rpc(rpc, peer, service, nbulkiov,
+ bulklen, rpc_done, rpc_fini, priv);
+ return rpc;
+}
+
+/* called with rpc->crpc_lock held */
+void
+srpc_abort_rpc(srpc_client_rpc_t *rpc, int why)
+{
+ LASSERT(why != 0);
+
+ if (rpc->crpc_aborted || /* already aborted */
+ rpc->crpc_closed) /* callback imminent */
+ return;
+
+ CDEBUG(D_NET,
+ "Aborting RPC: service %d, peer %s, state %s, why %d\n",
+ rpc->crpc_service, libcfs_id2str(rpc->crpc_dest),
+ swi_state2str(rpc->crpc_wi.swi_state), why);
+
+ rpc->crpc_aborted = 1;
+ rpc->crpc_status = why;
+ swi_schedule_workitem(&rpc->crpc_wi);
+ return;
+}
+
+/* called with rpc->crpc_lock held */
+void
+srpc_post_rpc(srpc_client_rpc_t *rpc)
+{
+ LASSERT(!rpc->crpc_aborted);
+ LASSERT(srpc_data.rpc_state == SRPC_STATE_RUNNING);
+
+ CDEBUG(D_NET, "Posting RPC: peer %s, service %d, timeout %d\n",
+ libcfs_id2str(rpc->crpc_dest), rpc->crpc_service,
+ rpc->crpc_timeout);
+
+ srpc_add_client_rpc_timer(rpc);
+ swi_schedule_workitem(&rpc->crpc_wi);
+ return;
+}
+
+
+int
+srpc_send_reply(struct srpc_server_rpc *rpc)
+{
+ srpc_event_t *ev = &rpc->srpc_ev;
+ struct srpc_msg *msg = &rpc->srpc_replymsg;
+ struct srpc_buffer *buffer = rpc->srpc_reqstbuf;
+ struct srpc_service_cd *scd = rpc->srpc_scd;
+ struct srpc_service *sv = scd->scd_svc;
+ __u64 rpyid;
+ int rc;
+
+ LASSERT(buffer != NULL);
+ rpyid = buffer->buf_msg.msg_body.reqst.rpyid;
+
+ spin_lock(&scd->scd_lock);
+
+ if (!sv->sv_shuttingdown && !srpc_serv_is_framework(sv)) {
+ /* Repost buffer before replying since test client
+ * might send me another RPC once it gets the reply */
+ if (srpc_service_post_buffer(scd, buffer) != 0)
+ CWARN("Failed to repost %s buffer\n", sv->sv_name);
+ rpc->srpc_reqstbuf = NULL;
+ }
+
+ spin_unlock(&scd->scd_lock);
+
+ ev->ev_fired = 0;
+ ev->ev_data = rpc;
+ ev->ev_type = SRPC_REPLY_SENT;
+
+ msg->msg_magic = SRPC_MSG_MAGIC;
+ msg->msg_version = SRPC_MSG_VERSION;
+ msg->msg_type = srpc_service2reply(sv->sv_id);
+
+ rc = srpc_post_active_rdma(SRPC_RDMA_PORTAL, rpyid, msg,
+ sizeof(*msg), LNET_MD_OP_PUT,
+ rpc->srpc_peer, rpc->srpc_self,
+ &rpc->srpc_replymdh, ev);
+ if (rc != 0)
+ ev->ev_fired = 1; /* no more event expected */
+ return rc;
+}
+
+/* when in kernel always called with LNET_LOCK() held, and in thread context */
+static void
+srpc_lnet_ev_handler(lnet_event_t *ev)
+{
+ struct srpc_service_cd *scd;
+ srpc_event_t *rpcev = ev->md.user_ptr;
+ srpc_client_rpc_t *crpc;
+ srpc_server_rpc_t *srpc;
+ srpc_buffer_t *buffer;
+ srpc_service_t *sv;
+ srpc_msg_t *msg;
+ srpc_msg_type_t type;
+
+ LASSERT(!in_interrupt());
+
+ if (ev->status != 0) {
+ spin_lock(&srpc_data.rpc_glock);
+ srpc_data.rpc_counters.errors++;
+ spin_unlock(&srpc_data.rpc_glock);
+ }
+
+ rpcev->ev_lnet = ev->type;
+
+ switch (rpcev->ev_type) {
+ default:
+ CERROR("Unknown event: status %d, type %d, lnet %d\n",
+ rpcev->ev_status, rpcev->ev_type, rpcev->ev_lnet);
+ LBUG();
+ case SRPC_REQUEST_SENT:
+ if (ev->status == 0 && ev->type != LNET_EVENT_UNLINK) {
+ spin_lock(&srpc_data.rpc_glock);
+ srpc_data.rpc_counters.rpcs_sent++;
+ spin_unlock(&srpc_data.rpc_glock);
+ }
+ case SRPC_REPLY_RCVD:
+ case SRPC_BULK_REQ_RCVD:
+ crpc = rpcev->ev_data;
+
+ if (rpcev != &crpc->crpc_reqstev &&
+ rpcev != &crpc->crpc_replyev &&
+ rpcev != &crpc->crpc_bulkev) {
+ CERROR("rpcev %p, crpc %p, reqstev %p, replyev %p, bulkev %p\n",
+ rpcev, crpc, &crpc->crpc_reqstev,
+ &crpc->crpc_replyev, &crpc->crpc_bulkev);
+ CERROR("Bad event: status %d, type %d, lnet %d\n",
+ rpcev->ev_status, rpcev->ev_type, rpcev->ev_lnet);
+ LBUG();
+ }
+
+ spin_lock(&crpc->crpc_lock);
+
+ LASSERT(rpcev->ev_fired == 0);
+ rpcev->ev_fired = 1;
+ rpcev->ev_status = (ev->type == LNET_EVENT_UNLINK) ?
+ -EINTR : ev->status;
+ swi_schedule_workitem(&crpc->crpc_wi);
+
+ spin_unlock(&crpc->crpc_lock);
+ break;
+
+ case SRPC_REQUEST_RCVD:
+ scd = rpcev->ev_data;
+ sv = scd->scd_svc;
+
+ LASSERT(rpcev == &scd->scd_ev);
+
+ spin_lock(&scd->scd_lock);
+
+ LASSERT(ev->unlinked);
+ LASSERT(ev->type == LNET_EVENT_PUT ||
+ ev->type == LNET_EVENT_UNLINK);
+ LASSERT(ev->type != LNET_EVENT_UNLINK ||
+ sv->sv_shuttingdown);
+
+ buffer = container_of(ev->md.start, srpc_buffer_t, buf_msg);
+ buffer->buf_peer = ev->initiator;
+ buffer->buf_self = ev->target.nid;
+
+ LASSERT(scd->scd_buf_nposted > 0);
+ scd->scd_buf_nposted--;
+
+ if (sv->sv_shuttingdown) {
+ /* Leave buffer on scd->scd_buf_nposted since
+ * srpc_finish_service needs to traverse it. */
+ spin_unlock(&scd->scd_lock);
+ break;
+ }
+
+ if (scd->scd_buf_err_stamp != 0 &&
+ scd->scd_buf_err_stamp < get_seconds()) {
+ /* re-enable adding buffer */
+ scd->scd_buf_err_stamp = 0;
+ scd->scd_buf_err = 0;
+ }
+
+ if (scd->scd_buf_err == 0 && /* adding buffer is enabled */
+ scd->scd_buf_adjust == 0 &&
+ scd->scd_buf_nposted < scd->scd_buf_low) {
+ scd->scd_buf_adjust = max(scd->scd_buf_total / 2,
+ SFW_TEST_WI_MIN);
+ swi_schedule_workitem(&scd->scd_buf_wi);
+ }
+
+ list_del(&buffer->buf_list); /* from scd->scd_buf_posted */
+ msg = &buffer->buf_msg;
+ type = srpc_service2request(sv->sv_id);
+
+ if (ev->status != 0 || ev->mlength != sizeof(*msg) ||
+ (msg->msg_type != type &&
+ msg->msg_type != __swab32(type)) ||
+ (msg->msg_magic != SRPC_MSG_MAGIC &&
+ msg->msg_magic != __swab32(SRPC_MSG_MAGIC))) {
+ CERROR("Dropping RPC (%s) from %s: status %d mlength %d type %u magic %u.\n",
+ sv->sv_name, libcfs_id2str(ev->initiator),
+ ev->status, ev->mlength,
+ msg->msg_type, msg->msg_magic);
+
+ /* NB can't call srpc_service_recycle_buffer here since
+ * it may call LNetM[DE]Attach. The invalid magic tells
+ * srpc_handle_rpc to drop this RPC */
+ msg->msg_magic = 0;
+ }
+
+ if (!list_empty(&scd->scd_rpc_free)) {
+ srpc = list_entry(scd->scd_rpc_free.next,
+ struct srpc_server_rpc,
+ srpc_list);
+ list_del(&srpc->srpc_list);
+
+ srpc_init_server_rpc(srpc, scd, buffer);
+ list_add_tail(&srpc->srpc_list,
+ &scd->scd_rpc_active);
+ swi_schedule_workitem(&srpc->srpc_wi);
+ } else {
+ list_add_tail(&buffer->buf_list,
+ &scd->scd_buf_blocked);
+ }
+
+ spin_unlock(&scd->scd_lock);
+
+ spin_lock(&srpc_data.rpc_glock);
+ srpc_data.rpc_counters.rpcs_rcvd++;
+ spin_unlock(&srpc_data.rpc_glock);
+ break;
+
+ case SRPC_BULK_GET_RPLD:
+ LASSERT(ev->type == LNET_EVENT_SEND ||
+ ev->type == LNET_EVENT_REPLY ||
+ ev->type == LNET_EVENT_UNLINK);
+
+ if (!ev->unlinked)
+ break; /* wait for final event */
+
+ case SRPC_BULK_PUT_SENT:
+ if (ev->status == 0 && ev->type != LNET_EVENT_UNLINK) {
+ spin_lock(&srpc_data.rpc_glock);
+
+ if (rpcev->ev_type == SRPC_BULK_GET_RPLD)
+ srpc_data.rpc_counters.bulk_get += ev->mlength;
+ else
+ srpc_data.rpc_counters.bulk_put += ev->mlength;
+
+ spin_unlock(&srpc_data.rpc_glock);
+ }
+ case SRPC_REPLY_SENT:
+ srpc = rpcev->ev_data;
+ scd = srpc->srpc_scd;
+
+ LASSERT(rpcev == &srpc->srpc_ev);
+
+ spin_lock(&scd->scd_lock);
+
+ rpcev->ev_fired = 1;
+ rpcev->ev_status = (ev->type == LNET_EVENT_UNLINK) ?
+ -EINTR : ev->status;
+ swi_schedule_workitem(&srpc->srpc_wi);
+
+ spin_unlock(&scd->scd_lock);
+ break;
+ }
+}
+
+
+int
+srpc_startup(void)
+{
+ int rc;
+
+ memset(&srpc_data, 0, sizeof(struct smoketest_rpc));
+ spin_lock_init(&srpc_data.rpc_glock);
+
+ /* 1 second pause to avoid timestamp reuse */
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ schedule_timeout(cfs_time_seconds(1));
+ srpc_data.rpc_matchbits = ((__u64) get_seconds()) << 48;
+
+ srpc_data.rpc_state = SRPC_STATE_NONE;
+
+ rc = LNetNIInit(LUSTRE_SRV_LNET_PID);
+ if (rc < 0) {
+ CERROR("LNetNIInit() has failed: %d\n", rc);
+ return rc;
+ }
+
+ srpc_data.rpc_state = SRPC_STATE_NI_INIT;
+
+ LNetInvalidateHandle(&srpc_data.rpc_lnet_eq);
+ rc = LNetEQAlloc(0, srpc_lnet_ev_handler, &srpc_data.rpc_lnet_eq);
+ if (rc != 0) {
+ CERROR("LNetEQAlloc() has failed: %d\n", rc);
+ goto bail;
+ }
+
+ rc = LNetSetLazyPortal(SRPC_FRAMEWORK_REQUEST_PORTAL);
+ LASSERT(rc == 0);
+ rc = LNetSetLazyPortal(SRPC_REQUEST_PORTAL);
+ LASSERT(rc == 0);
+
+ srpc_data.rpc_state = SRPC_STATE_EQ_INIT;
+
+ rc = stt_startup();
+
+bail:
+ if (rc != 0)
+ srpc_shutdown();
+ else
+ srpc_data.rpc_state = SRPC_STATE_RUNNING;
+
+ return rc;
+}
+
+void
+srpc_shutdown(void)
+{
+ int i;
+ int rc;
+ int state;
+
+ state = srpc_data.rpc_state;
+ srpc_data.rpc_state = SRPC_STATE_STOPPING;
+
+ switch (state) {
+ default:
+ LBUG();
+ case SRPC_STATE_RUNNING:
+ spin_lock(&srpc_data.rpc_glock);
+
+ for (i = 0; i <= SRPC_SERVICE_MAX_ID; i++) {
+ srpc_service_t *sv = srpc_data.rpc_services[i];
+
+ LASSERTF(sv == NULL,
+ "service not empty: id %d, name %s\n",
+ i, sv->sv_name);
+ }
+
+ spin_unlock(&srpc_data.rpc_glock);
+
+ stt_shutdown();
+
+ case SRPC_STATE_EQ_INIT:
+ rc = LNetClearLazyPortal(SRPC_FRAMEWORK_REQUEST_PORTAL);
+ rc = LNetClearLazyPortal(SRPC_REQUEST_PORTAL);
+ LASSERT(rc == 0);
+ rc = LNetEQFree(srpc_data.rpc_lnet_eq);
+ LASSERT(rc == 0); /* the EQ should have no user by now */
+
+ case SRPC_STATE_NI_INIT:
+ LNetNIFini();
+ }
+
+ return;
+}
diff --git a/drivers/staging/lustre/lnet/selftest/rpc.h b/drivers/staging/lustre/lnet/selftest/rpc.h
new file mode 100644
index 000000000..fbeb75fe5
--- /dev/null
+++ b/drivers/staging/lustre/lnet/selftest/rpc.h
@@ -0,0 +1,302 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2012, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ */
+
+#ifndef __SELFTEST_RPC_H__
+#define __SELFTEST_RPC_H__
+
+#include "../../include/linux/lnet/lnetst.h"
+
+/*
+ * LST wired structures
+ *
+ * XXX: *REPLY == *REQST + 1
+ */
+typedef enum {
+ SRPC_MSG_MKSN_REQST = 0,
+ SRPC_MSG_MKSN_REPLY = 1,
+ SRPC_MSG_RMSN_REQST = 2,
+ SRPC_MSG_RMSN_REPLY = 3,
+ SRPC_MSG_BATCH_REQST = 4,
+ SRPC_MSG_BATCH_REPLY = 5,
+ SRPC_MSG_STAT_REQST = 6,
+ SRPC_MSG_STAT_REPLY = 7,
+ SRPC_MSG_TEST_REQST = 8,
+ SRPC_MSG_TEST_REPLY = 9,
+ SRPC_MSG_DEBUG_REQST = 10,
+ SRPC_MSG_DEBUG_REPLY = 11,
+ SRPC_MSG_BRW_REQST = 12,
+ SRPC_MSG_BRW_REPLY = 13,
+ SRPC_MSG_PING_REQST = 14,
+ SRPC_MSG_PING_REPLY = 15,
+ SRPC_MSG_JOIN_REQST = 16,
+ SRPC_MSG_JOIN_REPLY = 17,
+} srpc_msg_type_t;
+
+
+/* CAVEAT EMPTOR:
+ * All srpc_*_reqst_t's 1st field must be matchbits of reply buffer,
+ * and 2nd field matchbits of bulk buffer if any.
+ *
+ * All srpc_*_reply_t's 1st field must be a __u32 status, and 2nd field
+ * session id if needed.
+ */
+typedef struct {
+ __u64 rpyid; /* reply buffer matchbits */
+ __u64 bulkid; /* bulk buffer matchbits */
+} WIRE_ATTR srpc_generic_reqst_t;
+
+typedef struct {
+ __u32 status;
+ lst_sid_t sid;
+} WIRE_ATTR srpc_generic_reply_t;
+
+/* FRAMEWORK RPCs */
+typedef struct {
+ __u64 mksn_rpyid; /* reply buffer matchbits */
+ lst_sid_t mksn_sid; /* session id */
+ __u32 mksn_force; /* use brute force */
+ char mksn_name[LST_NAME_SIZE];
+} WIRE_ATTR srpc_mksn_reqst_t; /* make session request */
+
+typedef struct {
+ __u32 mksn_status; /* session status */
+ lst_sid_t mksn_sid; /* session id */
+ __u32 mksn_timeout; /* session timeout */
+ char mksn_name[LST_NAME_SIZE];
+} WIRE_ATTR srpc_mksn_reply_t; /* make session reply */
+
+typedef struct {
+ __u64 rmsn_rpyid; /* reply buffer matchbits */
+ lst_sid_t rmsn_sid; /* session id */
+} WIRE_ATTR srpc_rmsn_reqst_t; /* remove session request */
+
+typedef struct {
+ __u32 rmsn_status;
+ lst_sid_t rmsn_sid; /* session id */
+} WIRE_ATTR srpc_rmsn_reply_t; /* remove session reply */
+
+typedef struct {
+ __u64 join_rpyid; /* reply buffer matchbits */
+ lst_sid_t join_sid; /* session id to join */
+ char join_group[LST_NAME_SIZE]; /* group name */
+} WIRE_ATTR srpc_join_reqst_t;
+
+typedef struct {
+ __u32 join_status; /* returned status */
+ lst_sid_t join_sid; /* session id */
+ __u32 join_timeout; /* # seconds' inactivity to expire */
+ char join_session[LST_NAME_SIZE]; /* session name */
+} WIRE_ATTR srpc_join_reply_t;
+
+typedef struct {
+ __u64 dbg_rpyid; /* reply buffer matchbits */
+ lst_sid_t dbg_sid; /* session id */
+ __u32 dbg_flags; /* bitmap of debug */
+} WIRE_ATTR srpc_debug_reqst_t;
+
+typedef struct {
+ __u32 dbg_status; /* returned code */
+ lst_sid_t dbg_sid; /* session id */
+ __u32 dbg_timeout; /* session timeout */
+ __u32 dbg_nbatch; /* # of batches in the node */
+ char dbg_name[LST_NAME_SIZE]; /* session name */
+} WIRE_ATTR srpc_debug_reply_t;
+
+#define SRPC_BATCH_OPC_RUN 1
+#define SRPC_BATCH_OPC_STOP 2
+#define SRPC_BATCH_OPC_QUERY 3
+
+typedef struct {
+ __u64 bar_rpyid; /* reply buffer matchbits */
+ lst_sid_t bar_sid; /* session id */
+ lst_bid_t bar_bid; /* batch id */
+ __u32 bar_opc; /* create/start/stop batch */
+ __u32 bar_testidx; /* index of test */
+ __u32 bar_arg; /* parameters */
+} WIRE_ATTR srpc_batch_reqst_t;
+
+typedef struct {
+ __u32 bar_status; /* status of request */
+ lst_sid_t bar_sid; /* session id */
+ __u32 bar_active; /* # of active tests in batch/test */
+ __u32 bar_time; /* remained time */
+} WIRE_ATTR srpc_batch_reply_t;
+
+typedef struct {
+ __u64 str_rpyid; /* reply buffer matchbits */
+ lst_sid_t str_sid; /* session id */
+ __u32 str_type; /* type of stat */
+} WIRE_ATTR srpc_stat_reqst_t;
+
+typedef struct {
+ __u32 str_status;
+ lst_sid_t str_sid;
+ sfw_counters_t str_fw;
+ srpc_counters_t str_rpc;
+ lnet_counters_t str_lnet;
+} WIRE_ATTR srpc_stat_reply_t;
+
+typedef struct {
+ __u32 blk_opc; /* bulk operation code */
+ __u32 blk_npg; /* # of pages */
+ __u32 blk_flags; /* reserved flags */
+} WIRE_ATTR test_bulk_req_t;
+
+typedef struct {
+ /** bulk operation code */
+ __u16 blk_opc;
+ /** data check flags */
+ __u16 blk_flags;
+ /** data length */
+ __u32 blk_len;
+ /** reserved: offset */
+ __u32 blk_offset;
+} WIRE_ATTR test_bulk_req_v1_t;
+
+typedef struct {
+ __u32 png_size; /* size of ping message */
+ __u32 png_flags; /* reserved flags */
+} WIRE_ATTR test_ping_req_t;
+
+typedef struct {
+ __u64 tsr_rpyid; /* reply buffer matchbits */
+ __u64 tsr_bulkid; /* bulk buffer matchbits */
+ lst_sid_t tsr_sid; /* session id */
+ lst_bid_t tsr_bid; /* batch id */
+ __u32 tsr_service; /* test type: bulk|ping|... */
+ /* test client loop count or # server buffers needed */
+ __u32 tsr_loop;
+ __u32 tsr_concur; /* concurrency of test */
+ __u8 tsr_is_client; /* is test client or not */
+ __u8 tsr_stop_onerr; /* stop on error */
+ __u32 tsr_ndest; /* # of dest nodes */
+
+ union {
+ test_ping_req_t ping;
+ test_bulk_req_t bulk_v0;
+ test_bulk_req_v1_t bulk_v1;
+ } tsr_u;
+} WIRE_ATTR srpc_test_reqst_t;
+
+typedef struct {
+ __u32 tsr_status; /* returned code */
+ lst_sid_t tsr_sid;
+} WIRE_ATTR srpc_test_reply_t;
+
+/* TEST RPCs */
+typedef struct {
+ __u64 pnr_rpyid;
+ __u32 pnr_magic;
+ __u32 pnr_seq;
+ __u64 pnr_time_sec;
+ __u64 pnr_time_usec;
+} WIRE_ATTR srpc_ping_reqst_t;
+
+typedef struct {
+ __u32 pnr_status;
+ __u32 pnr_magic;
+ __u32 pnr_seq;
+} WIRE_ATTR srpc_ping_reply_t;
+
+typedef struct {
+ __u64 brw_rpyid; /* reply buffer matchbits */
+ __u64 brw_bulkid; /* bulk buffer matchbits */
+ __u32 brw_rw; /* read or write */
+ __u32 brw_len; /* bulk data len */
+ __u32 brw_flags; /* bulk data patterns */
+} WIRE_ATTR srpc_brw_reqst_t; /* bulk r/w request */
+
+typedef struct {
+ __u32 brw_status;
+} WIRE_ATTR srpc_brw_reply_t; /* bulk r/w reply */
+
+#define SRPC_MSG_MAGIC 0xeeb0f00d
+#define SRPC_MSG_VERSION 1
+
+typedef struct srpc_msg {
+ /** magic number */
+ __u32 msg_magic;
+ /** message version number */
+ __u32 msg_version;
+ /** type of message body: srpc_msg_type_t */
+ __u32 msg_type;
+ __u32 msg_reserved0;
+ __u32 msg_reserved1;
+ /** test session features */
+ __u32 msg_ses_feats;
+ union {
+ srpc_generic_reqst_t reqst;
+ srpc_generic_reply_t reply;
+
+ srpc_mksn_reqst_t mksn_reqst;
+ srpc_mksn_reply_t mksn_reply;
+ srpc_rmsn_reqst_t rmsn_reqst;
+ srpc_rmsn_reply_t rmsn_reply;
+ srpc_debug_reqst_t dbg_reqst;
+ srpc_debug_reply_t dbg_reply;
+ srpc_batch_reqst_t bat_reqst;
+ srpc_batch_reply_t bat_reply;
+ srpc_stat_reqst_t stat_reqst;
+ srpc_stat_reply_t stat_reply;
+ srpc_test_reqst_t tes_reqst;
+ srpc_test_reply_t tes_reply;
+ srpc_join_reqst_t join_reqst;
+ srpc_join_reply_t join_reply;
+
+ srpc_ping_reqst_t ping_reqst;
+ srpc_ping_reply_t ping_reply;
+ srpc_brw_reqst_t brw_reqst;
+ srpc_brw_reply_t brw_reply;
+ } msg_body;
+} WIRE_ATTR srpc_msg_t;
+
+static inline void
+srpc_unpack_msg_hdr(srpc_msg_t *msg)
+{
+ if (msg->msg_magic == SRPC_MSG_MAGIC)
+ return; /* no flipping needed */
+
+ /* We do not swap the magic number here as it is needed to
+ determine whether the body needs to be swapped. */
+ /* __swab32s(&msg->msg_magic); */
+ __swab32s(&msg->msg_type);
+ __swab32s(&msg->msg_version);
+ __swab32s(&msg->msg_ses_feats);
+ __swab32s(&msg->msg_reserved0);
+ __swab32s(&msg->msg_reserved1);
+}
+
+#endif /* __SELFTEST_RPC_H__ */
diff --git a/drivers/staging/lustre/lnet/selftest/selftest.h b/drivers/staging/lustre/lnet/selftest/selftest.h
new file mode 100644
index 000000000..d48701834
--- /dev/null
+++ b/drivers/staging/lustre/lnet/selftest/selftest.h
@@ -0,0 +1,624 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ * copy of GPLv2].
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2012, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ *
+ * lnet/selftest/selftest.h
+ *
+ * Author: Isaac Huang <isaac@clusterfs.com>
+ */
+#ifndef __SELFTEST_SELFTEST_H__
+#define __SELFTEST_SELFTEST_H__
+
+#define LNET_ONLY
+
+#include "../../include/linux/libcfs/libcfs.h"
+#include "../../include/linux/lnet/lnet.h"
+#include "../../include/linux/lnet/lib-lnet.h"
+#include "../../include/linux/lnet/lib-types.h"
+#include "../../include/linux/lnet/lnetst.h"
+
+#include "rpc.h"
+#include "timer.h"
+
+#ifndef MADE_WITHOUT_COMPROMISE
+#define MADE_WITHOUT_COMPROMISE
+#endif
+
+
+#define SWI_STATE_NEWBORN 0
+#define SWI_STATE_REPLY_SUBMITTED 1
+#define SWI_STATE_REPLY_SENT 2
+#define SWI_STATE_REQUEST_SUBMITTED 3
+#define SWI_STATE_REQUEST_SENT 4
+#define SWI_STATE_REPLY_RECEIVED 5
+#define SWI_STATE_BULK_STARTED 6
+#define SWI_STATE_DONE 10
+
+/* forward refs */
+struct srpc_service;
+struct srpc_service_cd;
+struct sfw_test_unit;
+struct sfw_test_instance;
+
+/* services below SRPC_FRAMEWORK_SERVICE_MAX_ID are framework
+ * services, e.g. create/modify session.
+ */
+#define SRPC_SERVICE_DEBUG 0
+#define SRPC_SERVICE_MAKE_SESSION 1
+#define SRPC_SERVICE_REMOVE_SESSION 2
+#define SRPC_SERVICE_BATCH 3
+#define SRPC_SERVICE_TEST 4
+#define SRPC_SERVICE_QUERY_STAT 5
+#define SRPC_SERVICE_JOIN 6
+#define SRPC_FRAMEWORK_SERVICE_MAX_ID 10
+/* other services start from SRPC_FRAMEWORK_SERVICE_MAX_ID+1 */
+#define SRPC_SERVICE_BRW 11
+#define SRPC_SERVICE_PING 12
+#define SRPC_SERVICE_MAX_ID 12
+
+#define SRPC_REQUEST_PORTAL 50
+/* a lazy portal for framework RPC requests */
+#define SRPC_FRAMEWORK_REQUEST_PORTAL 51
+/* all reply/bulk RDMAs go to this portal */
+#define SRPC_RDMA_PORTAL 52
+
+static inline srpc_msg_type_t
+srpc_service2request (int service)
+{
+ switch (service) {
+ default:
+ LBUG ();
+ case SRPC_SERVICE_DEBUG:
+ return SRPC_MSG_DEBUG_REQST;
+
+ case SRPC_SERVICE_MAKE_SESSION:
+ return SRPC_MSG_MKSN_REQST;
+
+ case SRPC_SERVICE_REMOVE_SESSION:
+ return SRPC_MSG_RMSN_REQST;
+
+ case SRPC_SERVICE_BATCH:
+ return SRPC_MSG_BATCH_REQST;
+
+ case SRPC_SERVICE_TEST:
+ return SRPC_MSG_TEST_REQST;
+
+ case SRPC_SERVICE_QUERY_STAT:
+ return SRPC_MSG_STAT_REQST;
+
+ case SRPC_SERVICE_BRW:
+ return SRPC_MSG_BRW_REQST;
+
+ case SRPC_SERVICE_PING:
+ return SRPC_MSG_PING_REQST;
+
+ case SRPC_SERVICE_JOIN:
+ return SRPC_MSG_JOIN_REQST;
+ }
+}
+
+static inline srpc_msg_type_t
+srpc_service2reply (int service)
+{
+ return srpc_service2request(service) + 1;
+}
+
+typedef enum {
+ SRPC_BULK_REQ_RCVD = 1, /* passive bulk request(PUT sink/GET source) received */
+ SRPC_BULK_PUT_SENT = 2, /* active bulk PUT sent (source) */
+ SRPC_BULK_GET_RPLD = 3, /* active bulk GET replied (sink) */
+ SRPC_REPLY_RCVD = 4, /* incoming reply received */
+ SRPC_REPLY_SENT = 5, /* outgoing reply sent */
+ SRPC_REQUEST_RCVD = 6, /* incoming request received */
+ SRPC_REQUEST_SENT = 7, /* outgoing request sent */
+} srpc_event_type_t;
+
+/* RPC event */
+typedef struct {
+ srpc_event_type_t ev_type; /* what's up */
+ lnet_event_kind_t ev_lnet; /* LNet event type */
+ int ev_fired; /* LNet event fired? */
+ int ev_status; /* LNet event status */
+ void *ev_data; /* owning server/client RPC */
+} srpc_event_t;
+
+typedef struct {
+ int bk_len; /* len of bulk data */
+ lnet_handle_md_t bk_mdh;
+ int bk_sink; /* sink/source */
+ int bk_niov; /* # iov in bk_iovs */
+ lnet_kiov_t bk_iovs[0];
+} srpc_bulk_t; /* bulk descriptor */
+
+/* message buffer descriptor */
+typedef struct srpc_buffer {
+ struct list_head buf_list; /* chain on srpc_service::*_msgq */
+ srpc_msg_t buf_msg;
+ lnet_handle_md_t buf_mdh;
+ lnet_nid_t buf_self;
+ lnet_process_id_t buf_peer;
+} srpc_buffer_t;
+
+struct swi_workitem;
+typedef int (*swi_action_t) (struct swi_workitem *);
+
+typedef struct swi_workitem {
+ struct cfs_wi_sched *swi_sched;
+ cfs_workitem_t swi_workitem;
+ swi_action_t swi_action;
+ int swi_state;
+} swi_workitem_t;
+
+/* server-side state of a RPC */
+typedef struct srpc_server_rpc {
+ /* chain on srpc_service::*_rpcq */
+ struct list_head srpc_list;
+ struct srpc_service_cd *srpc_scd;
+ swi_workitem_t srpc_wi;
+ srpc_event_t srpc_ev; /* bulk/reply event */
+ lnet_nid_t srpc_self;
+ lnet_process_id_t srpc_peer;
+ srpc_msg_t srpc_replymsg;
+ lnet_handle_md_t srpc_replymdh;
+ srpc_buffer_t *srpc_reqstbuf;
+ srpc_bulk_t *srpc_bulk;
+
+ unsigned int srpc_aborted; /* being given up */
+ int srpc_status;
+ void (*srpc_done)(struct srpc_server_rpc *);
+} srpc_server_rpc_t;
+
+/* client-side state of a RPC */
+typedef struct srpc_client_rpc {
+ struct list_head crpc_list; /* chain on user's lists */
+ spinlock_t crpc_lock; /* serialize */
+ int crpc_service;
+ atomic_t crpc_refcount;
+ int crpc_timeout; /* # seconds to wait for reply */
+ stt_timer_t crpc_timer;
+ swi_workitem_t crpc_wi;
+ lnet_process_id_t crpc_dest;
+
+ void (*crpc_done)(struct srpc_client_rpc *);
+ void (*crpc_fini)(struct srpc_client_rpc *);
+ int crpc_status; /* completion status */
+ void *crpc_priv; /* caller data */
+
+ /* state flags */
+ unsigned int crpc_aborted:1; /* being given up */
+ unsigned int crpc_closed:1; /* completed */
+
+ /* RPC events */
+ srpc_event_t crpc_bulkev; /* bulk event */
+ srpc_event_t crpc_reqstev; /* request event */
+ srpc_event_t crpc_replyev; /* reply event */
+
+ /* bulk, request(reqst), and reply exchanged on wire */
+ srpc_msg_t crpc_reqstmsg;
+ srpc_msg_t crpc_replymsg;
+ lnet_handle_md_t crpc_reqstmdh;
+ lnet_handle_md_t crpc_replymdh;
+ srpc_bulk_t crpc_bulk;
+} srpc_client_rpc_t;
+
+#define srpc_client_rpc_size(rpc) \
+offsetof(srpc_client_rpc_t, crpc_bulk.bk_iovs[(rpc)->crpc_bulk.bk_niov])
+
+#define srpc_client_rpc_addref(rpc) \
+do { \
+ CDEBUG(D_NET, "RPC[%p] -> %s (%d)++\n", \
+ (rpc), libcfs_id2str((rpc)->crpc_dest), \
+ atomic_read(&(rpc)->crpc_refcount)); \
+ LASSERT(atomic_read(&(rpc)->crpc_refcount) > 0); \
+ atomic_inc(&(rpc)->crpc_refcount); \
+} while (0)
+
+#define srpc_client_rpc_decref(rpc) \
+do { \
+ CDEBUG(D_NET, "RPC[%p] -> %s (%d)--\n", \
+ (rpc), libcfs_id2str((rpc)->crpc_dest), \
+ atomic_read(&(rpc)->crpc_refcount)); \
+ LASSERT(atomic_read(&(rpc)->crpc_refcount) > 0); \
+ if (atomic_dec_and_test(&(rpc)->crpc_refcount)) \
+ srpc_destroy_client_rpc(rpc); \
+} while (0)
+
+#define srpc_event_pending(rpc) ((rpc)->crpc_bulkev.ev_fired == 0 || \
+ (rpc)->crpc_reqstev.ev_fired == 0 || \
+ (rpc)->crpc_replyev.ev_fired == 0)
+
+/* CPU partition data of srpc service */
+struct srpc_service_cd {
+ /** serialize */
+ spinlock_t scd_lock;
+ /** backref to service */
+ struct srpc_service *scd_svc;
+ /** event buffer */
+ srpc_event_t scd_ev;
+ /** free RPC descriptors */
+ struct list_head scd_rpc_free;
+ /** in-flight RPCs */
+ struct list_head scd_rpc_active;
+ /** workitem for posting buffer */
+ swi_workitem_t scd_buf_wi;
+ /** CPT id */
+ int scd_cpt;
+ /** error code for scd_buf_wi */
+ int scd_buf_err;
+ /** timestamp for scd_buf_err */
+ unsigned long scd_buf_err_stamp;
+ /** total # request buffers */
+ int scd_buf_total;
+ /** # posted request buffers */
+ int scd_buf_nposted;
+ /** in progress of buffer posting */
+ int scd_buf_posting;
+ /** allocate more buffers if scd_buf_nposted < scd_buf_low */
+ int scd_buf_low;
+ /** increase/decrease some buffers */
+ int scd_buf_adjust;
+ /** posted message buffers */
+ struct list_head scd_buf_posted;
+ /** blocked for RPC descriptor */
+ struct list_head scd_buf_blocked;
+};
+
+/* number of server workitems (mini-thread) for testing service */
+#define SFW_TEST_WI_MIN 256
+#define SFW_TEST_WI_MAX 2048
+/* extra buffers for tolerating buggy peers, or unbalanced number
+ * of peers between partitions */
+#define SFW_TEST_WI_EXTRA 64
+
+/* number of server workitems (mini-thread) for framework service */
+#define SFW_FRWK_WI_MIN 16
+#define SFW_FRWK_WI_MAX 256
+
+typedef struct srpc_service {
+ int sv_id; /* service id */
+ const char *sv_name; /* human readable name */
+ int sv_wi_total; /* total server workitems */
+ int sv_shuttingdown;
+ int sv_ncpts;
+ /* percpt data for srpc_service */
+ struct srpc_service_cd **sv_cpt_data;
+ /* Service callbacks:
+ * - sv_handler: process incoming RPC request
+ * - sv_bulk_ready: notify bulk data
+ */
+ int (*sv_handler) (srpc_server_rpc_t *);
+ int (*sv_bulk_ready) (srpc_server_rpc_t *, int);
+} srpc_service_t;
+
+typedef struct {
+ struct list_head sn_list; /* chain on fw_zombie_sessions */
+ lst_sid_t sn_id; /* unique identifier */
+ unsigned int sn_timeout; /* # seconds' inactivity to expire */
+ int sn_timer_active;
+ unsigned int sn_features;
+ stt_timer_t sn_timer;
+ struct list_head sn_batches; /* list of batches */
+ char sn_name[LST_NAME_SIZE];
+ atomic_t sn_refcount;
+ atomic_t sn_brw_errors;
+ atomic_t sn_ping_errors;
+ unsigned long sn_started;
+} sfw_session_t;
+
+#define sfw_sid_equal(sid0, sid1) ((sid0).ses_nid == (sid1).ses_nid && \
+ (sid0).ses_stamp == (sid1).ses_stamp)
+
+typedef struct {
+ struct list_head bat_list; /* chain on sn_batches */
+ lst_bid_t bat_id; /* batch id */
+ int bat_error; /* error code of batch */
+ sfw_session_t *bat_session; /* batch's session */
+ atomic_t bat_nactive; /* # of active tests */
+ struct list_head bat_tests; /* test instances */
+} sfw_batch_t;
+
+typedef struct {
+ int (*tso_init)(struct sfw_test_instance *tsi); /* initialize test client */
+ void (*tso_fini)(struct sfw_test_instance *tsi); /* finalize test client */
+ int (*tso_prep_rpc)(struct sfw_test_unit *tsu,
+ lnet_process_id_t dest,
+ srpc_client_rpc_t **rpc); /* prep a tests rpc */
+ void (*tso_done_rpc)(struct sfw_test_unit *tsu,
+ srpc_client_rpc_t *rpc); /* done a test rpc */
+} sfw_test_client_ops_t;
+
+typedef struct sfw_test_instance {
+ struct list_head tsi_list; /* chain on batch */
+ int tsi_service; /* test type */
+ sfw_batch_t *tsi_batch; /* batch */
+ sfw_test_client_ops_t *tsi_ops; /* test client operations */
+
+ /* public parameter for all test units */
+ unsigned int tsi_is_client:1; /* is test client */
+ unsigned int tsi_stoptsu_onerr:1; /* stop tsu on error */
+ int tsi_concur; /* concurrency */
+ int tsi_loop; /* loop count */
+
+ /* status of test instance */
+ spinlock_t tsi_lock; /* serialize */
+ unsigned int tsi_stopping:1; /* test is stopping */
+ atomic_t tsi_nactive; /* # of active test unit */
+ struct list_head tsi_units; /* test units */
+ struct list_head tsi_free_rpcs; /* free rpcs */
+ struct list_head tsi_active_rpcs; /* active rpcs */
+
+ union {
+ test_ping_req_t ping; /* ping parameter */
+ test_bulk_req_t bulk_v0; /* bulk parameter */
+ test_bulk_req_v1_t bulk_v1; /* bulk v1 parameter */
+ } tsi_u;
+} sfw_test_instance_t;
+
+/* XXX: trailing (PAGE_CACHE_SIZE % sizeof(lnet_process_id_t)) bytes at
+ * the end of pages are not used */
+#define SFW_MAX_CONCUR LST_MAX_CONCUR
+#define SFW_ID_PER_PAGE (PAGE_CACHE_SIZE / sizeof(lnet_process_id_packed_t))
+#define SFW_MAX_NDESTS (LNET_MAX_IOV * SFW_ID_PER_PAGE)
+#define sfw_id_pages(n) (((n) + SFW_ID_PER_PAGE - 1) / SFW_ID_PER_PAGE)
+
+typedef struct sfw_test_unit {
+ struct list_head tsu_list; /* chain on lst_test_instance */
+ lnet_process_id_t tsu_dest; /* id of dest node */
+ int tsu_loop; /* loop count of the test */
+ sfw_test_instance_t *tsu_instance; /* pointer to test instance */
+ void *tsu_private; /* private data */
+ swi_workitem_t tsu_worker; /* workitem of the test unit */
+} sfw_test_unit_t;
+
+typedef struct sfw_test_case {
+ struct list_head tsc_list; /* chain on fw_tests */
+ srpc_service_t *tsc_srv_service; /* test service */
+ sfw_test_client_ops_t *tsc_cli_ops; /* ops of test client */
+} sfw_test_case_t;
+
+srpc_client_rpc_t *
+sfw_create_rpc(lnet_process_id_t peer, int service,
+ unsigned features, int nbulkiov, int bulklen,
+ void (*done) (srpc_client_rpc_t *), void *priv);
+int sfw_create_test_rpc(sfw_test_unit_t *tsu,
+ lnet_process_id_t peer, unsigned features,
+ int nblk, int blklen, srpc_client_rpc_t **rpc);
+void sfw_abort_rpc(srpc_client_rpc_t *rpc);
+void sfw_post_rpc(srpc_client_rpc_t *rpc);
+void sfw_client_rpc_done(srpc_client_rpc_t *rpc);
+void sfw_unpack_message(srpc_msg_t *msg);
+void sfw_free_pages(srpc_server_rpc_t *rpc);
+void sfw_add_bulk_page(srpc_bulk_t *bk, struct page *pg, int i);
+int sfw_alloc_pages(srpc_server_rpc_t *rpc, int cpt, int npages, int len,
+ int sink);
+int sfw_make_session (srpc_mksn_reqst_t *request, srpc_mksn_reply_t *reply);
+
+srpc_client_rpc_t *
+srpc_create_client_rpc(lnet_process_id_t peer, int service,
+ int nbulkiov, int bulklen,
+ void (*rpc_done)(srpc_client_rpc_t *),
+ void (*rpc_fini)(srpc_client_rpc_t *), void *priv);
+void srpc_post_rpc(srpc_client_rpc_t *rpc);
+void srpc_abort_rpc(srpc_client_rpc_t *rpc, int why);
+void srpc_free_bulk(srpc_bulk_t *bk);
+srpc_bulk_t *srpc_alloc_bulk(int cpt, unsigned bulk_npg, unsigned bulk_len,
+ int sink);
+int srpc_send_rpc(swi_workitem_t *wi);
+int srpc_send_reply(srpc_server_rpc_t *rpc);
+int srpc_add_service(srpc_service_t *sv);
+int srpc_remove_service(srpc_service_t *sv);
+void srpc_shutdown_service(srpc_service_t *sv);
+void srpc_abort_service(srpc_service_t *sv);
+int srpc_finish_service(srpc_service_t *sv);
+int srpc_service_add_buffers(srpc_service_t *sv, int nbuffer);
+void srpc_service_remove_buffers(srpc_service_t *sv, int nbuffer);
+void srpc_get_counters(srpc_counters_t *cnt);
+void srpc_set_counters(const srpc_counters_t *cnt);
+
+extern struct cfs_wi_sched *lst_sched_serial;
+extern struct cfs_wi_sched **lst_sched_test;
+
+static inline int
+srpc_serv_is_framework(struct srpc_service *svc)
+{
+ return svc->sv_id < SRPC_FRAMEWORK_SERVICE_MAX_ID;
+}
+
+static inline int
+swi_wi_action(cfs_workitem_t *wi)
+{
+ swi_workitem_t *swi = container_of(wi, swi_workitem_t, swi_workitem);
+
+ return swi->swi_action(swi);
+}
+
+static inline void
+swi_init_workitem(swi_workitem_t *swi, void *data,
+ swi_action_t action, struct cfs_wi_sched *sched)
+{
+ swi->swi_sched = sched;
+ swi->swi_action = action;
+ swi->swi_state = SWI_STATE_NEWBORN;
+ cfs_wi_init(&swi->swi_workitem, data, swi_wi_action);
+}
+
+static inline void
+swi_schedule_workitem(swi_workitem_t *wi)
+{
+ cfs_wi_schedule(wi->swi_sched, &wi->swi_workitem);
+}
+
+static inline void
+swi_exit_workitem(swi_workitem_t *swi)
+{
+ cfs_wi_exit(swi->swi_sched, &swi->swi_workitem);
+}
+
+static inline int
+swi_deschedule_workitem(swi_workitem_t *swi)
+{
+ return cfs_wi_deschedule(swi->swi_sched, &swi->swi_workitem);
+}
+
+
+int sfw_startup(void);
+int srpc_startup(void);
+void sfw_shutdown(void);
+void srpc_shutdown(void);
+
+static inline void
+srpc_destroy_client_rpc (srpc_client_rpc_t *rpc)
+{
+ LASSERT (rpc != NULL);
+ LASSERT (!srpc_event_pending(rpc));
+ LASSERT (atomic_read(&rpc->crpc_refcount) == 0);
+
+ if (rpc->crpc_fini == NULL) {
+ LIBCFS_FREE(rpc, srpc_client_rpc_size(rpc));
+ } else {
+ (*rpc->crpc_fini) (rpc);
+ }
+
+ return;
+}
+
+static inline void
+srpc_init_client_rpc (srpc_client_rpc_t *rpc, lnet_process_id_t peer,
+ int service, int nbulkiov, int bulklen,
+ void (*rpc_done)(srpc_client_rpc_t *),
+ void (*rpc_fini)(srpc_client_rpc_t *), void *priv)
+{
+ LASSERT (nbulkiov <= LNET_MAX_IOV);
+
+ memset(rpc, 0, offsetof(srpc_client_rpc_t,
+ crpc_bulk.bk_iovs[nbulkiov]));
+
+ INIT_LIST_HEAD(&rpc->crpc_list);
+ swi_init_workitem(&rpc->crpc_wi, rpc, srpc_send_rpc,
+ lst_sched_test[lnet_cpt_of_nid(peer.nid)]);
+ spin_lock_init(&rpc->crpc_lock);
+ atomic_set(&rpc->crpc_refcount, 1); /* 1 ref for caller */
+
+ rpc->crpc_dest = peer;
+ rpc->crpc_priv = priv;
+ rpc->crpc_service = service;
+ rpc->crpc_bulk.bk_len = bulklen;
+ rpc->crpc_bulk.bk_niov = nbulkiov;
+ rpc->crpc_done = rpc_done;
+ rpc->crpc_fini = rpc_fini;
+ LNetInvalidateHandle(&rpc->crpc_reqstmdh);
+ LNetInvalidateHandle(&rpc->crpc_replymdh);
+ LNetInvalidateHandle(&rpc->crpc_bulk.bk_mdh);
+
+ /* no event is expected at this point */
+ rpc->crpc_bulkev.ev_fired =
+ rpc->crpc_reqstev.ev_fired =
+ rpc->crpc_replyev.ev_fired = 1;
+
+ rpc->crpc_reqstmsg.msg_magic = SRPC_MSG_MAGIC;
+ rpc->crpc_reqstmsg.msg_version = SRPC_MSG_VERSION;
+ rpc->crpc_reqstmsg.msg_type = srpc_service2request(service);
+ return;
+}
+
+static inline const char *
+swi_state2str (int state)
+{
+#define STATE2STR(x) case x: return #x
+ switch(state) {
+ default:
+ LBUG();
+ STATE2STR(SWI_STATE_NEWBORN);
+ STATE2STR(SWI_STATE_REPLY_SUBMITTED);
+ STATE2STR(SWI_STATE_REPLY_SENT);
+ STATE2STR(SWI_STATE_REQUEST_SUBMITTED);
+ STATE2STR(SWI_STATE_REQUEST_SENT);
+ STATE2STR(SWI_STATE_REPLY_RECEIVED);
+ STATE2STR(SWI_STATE_BULK_STARTED);
+ STATE2STR(SWI_STATE_DONE);
+ }
+#undef STATE2STR
+}
+
+#define selftest_wait_events() \
+ do { \
+ set_current_state(TASK_UNINTERRUPTIBLE); \
+ schedule_timeout(cfs_time_seconds(1) / 10); \
+ } while (0)
+
+
+#define lst_wait_until(cond, lock, fmt, ...) \
+do { \
+ int __I = 2; \
+ while (!(cond)) { \
+ CDEBUG(IS_PO2(++__I) ? D_WARNING : D_NET, \
+ fmt, ## __VA_ARGS__); \
+ spin_unlock(&(lock)); \
+ \
+ selftest_wait_events(); \
+ \
+ spin_lock(&(lock)); \
+ } \
+} while (0)
+
+static inline void
+srpc_wait_service_shutdown(srpc_service_t *sv)
+{
+ int i = 2;
+
+ LASSERT(sv->sv_shuttingdown);
+
+ while (srpc_finish_service(sv) == 0) {
+ i++;
+ CDEBUG (((i & -i) == i) ? D_WARNING : D_NET,
+ "Waiting for %s service to shutdown...\n",
+ sv->sv_name);
+ selftest_wait_events();
+ }
+}
+
+extern sfw_test_client_ops_t brw_test_client;
+void brw_init_test_client(void);
+
+extern srpc_service_t brw_test_service;
+void brw_init_test_service(void);
+
+extern sfw_test_client_ops_t ping_test_client;
+void ping_init_test_client(void);
+
+extern srpc_service_t ping_test_service;
+void ping_init_test_service(void);
+
+#endif /* __SELFTEST_SELFTEST_H__ */
diff --git a/drivers/staging/lustre/lnet/selftest/timer.c b/drivers/staging/lustre/lnet/selftest/timer.c
new file mode 100644
index 000000000..441f9472a
--- /dev/null
+++ b/drivers/staging/lustre/lnet/selftest/timer.c
@@ -0,0 +1,248 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2011, 2012, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ *
+ * lnet/selftest/timer.c
+ *
+ * Author: Isaac Huang <isaac@clusterfs.com>
+ */
+
+#define DEBUG_SUBSYSTEM S_LNET
+
+#include "selftest.h"
+
+
+/*
+ * Timers are implemented as a sorted queue of expiry times. The queue
+ * is slotted, with each slot holding timers which expire in a
+ * 2**STTIMER_MINPOLL (8) second period. The timers in each slot are
+ * sorted by increasing expiry time. The number of slots is 2**7 (128),
+ * to cover a time period of 1024 seconds into the future before wrapping.
+ */
+#define STTIMER_MINPOLL 3 /* log2 min poll interval (8 s) */
+#define STTIMER_SLOTTIME (1 << STTIMER_MINPOLL)
+#define STTIMER_SLOTTIMEMASK (~(STTIMER_SLOTTIME - 1))
+#define STTIMER_NSLOTS (1 << 7)
+#define STTIMER_SLOT(t) (&stt_data.stt_hash[(((t) >> STTIMER_MINPOLL) & \
+ (STTIMER_NSLOTS - 1))])
+
+static struct st_timer_data {
+ spinlock_t stt_lock;
+ /* start time of the slot processed previously */
+ unsigned long stt_prev_slot;
+ struct list_head stt_hash[STTIMER_NSLOTS];
+ int stt_shuttingdown;
+ wait_queue_head_t stt_waitq;
+ int stt_nthreads;
+} stt_data;
+
+void
+stt_add_timer(stt_timer_t *timer)
+{
+ struct list_head *pos;
+
+ spin_lock(&stt_data.stt_lock);
+
+ LASSERT(stt_data.stt_nthreads > 0);
+ LASSERT(!stt_data.stt_shuttingdown);
+ LASSERT(timer->stt_func != NULL);
+ LASSERT(list_empty(&timer->stt_list));
+ LASSERT(cfs_time_after(timer->stt_expires, get_seconds()));
+
+ /* a simple insertion sort */
+ list_for_each_prev(pos, STTIMER_SLOT(timer->stt_expires)) {
+ stt_timer_t *old = list_entry(pos, stt_timer_t, stt_list);
+
+ if (cfs_time_aftereq(timer->stt_expires, old->stt_expires))
+ break;
+ }
+ list_add(&timer->stt_list, pos);
+
+ spin_unlock(&stt_data.stt_lock);
+}
+
+/*
+ * The function returns whether it has deactivated a pending timer or not.
+ * (ie. del_timer() of an inactive timer returns 0, del_timer() of an
+ * active timer returns 1.)
+ *
+ * CAVEAT EMPTOR:
+ * When 0 is returned, it is possible that timer->stt_func _is_ running on
+ * another CPU.
+ */
+int
+stt_del_timer(stt_timer_t *timer)
+{
+ int ret = 0;
+
+ spin_lock(&stt_data.stt_lock);
+
+ LASSERT(stt_data.stt_nthreads > 0);
+ LASSERT(!stt_data.stt_shuttingdown);
+
+ if (!list_empty(&timer->stt_list)) {
+ ret = 1;
+ list_del_init(&timer->stt_list);
+ }
+
+ spin_unlock(&stt_data.stt_lock);
+ return ret;
+}
+
+/* called with stt_data.stt_lock held */
+static int
+stt_expire_list(struct list_head *slot, unsigned long now)
+{
+ int expired = 0;
+ stt_timer_t *timer;
+
+ while (!list_empty(slot)) {
+ timer = list_entry(slot->next, stt_timer_t, stt_list);
+
+ if (cfs_time_after(timer->stt_expires, now))
+ break;
+
+ list_del_init(&timer->stt_list);
+ spin_unlock(&stt_data.stt_lock);
+
+ expired++;
+ (*timer->stt_func) (timer->stt_data);
+
+ spin_lock(&stt_data.stt_lock);
+ }
+
+ return expired;
+}
+
+static int
+stt_check_timers(unsigned long *last)
+{
+ int expired = 0;
+ unsigned long now;
+ unsigned long this_slot;
+
+ now = get_seconds();
+ this_slot = now & STTIMER_SLOTTIMEMASK;
+
+ spin_lock(&stt_data.stt_lock);
+
+ while (cfs_time_aftereq(this_slot, *last)) {
+ expired += stt_expire_list(STTIMER_SLOT(this_slot), now);
+ this_slot = cfs_time_sub(this_slot, STTIMER_SLOTTIME);
+ }
+
+ *last = now & STTIMER_SLOTTIMEMASK;
+ spin_unlock(&stt_data.stt_lock);
+ return expired;
+}
+
+
+static int
+stt_timer_main(void *arg)
+{
+ cfs_block_allsigs();
+
+ while (!stt_data.stt_shuttingdown) {
+ stt_check_timers(&stt_data.stt_prev_slot);
+
+ wait_event_timeout(stt_data.stt_waitq,
+ stt_data.stt_shuttingdown,
+ cfs_time_seconds(STTIMER_SLOTTIME));
+ }
+
+ spin_lock(&stt_data.stt_lock);
+ stt_data.stt_nthreads--;
+ spin_unlock(&stt_data.stt_lock);
+ return 0;
+}
+
+static int
+stt_start_timer_thread(void)
+{
+ struct task_struct *task;
+
+ LASSERT(!stt_data.stt_shuttingdown);
+
+ task = kthread_run(stt_timer_main, NULL, "st_timer");
+ if (IS_ERR(task))
+ return PTR_ERR(task);
+
+ spin_lock(&stt_data.stt_lock);
+ stt_data.stt_nthreads++;
+ spin_unlock(&stt_data.stt_lock);
+ return 0;
+}
+
+
+int
+stt_startup(void)
+{
+ int rc = 0;
+ int i;
+
+ stt_data.stt_shuttingdown = 0;
+ stt_data.stt_prev_slot = get_seconds() & STTIMER_SLOTTIMEMASK;
+
+ spin_lock_init(&stt_data.stt_lock);
+ for (i = 0; i < STTIMER_NSLOTS; i++)
+ INIT_LIST_HEAD(&stt_data.stt_hash[i]);
+
+ stt_data.stt_nthreads = 0;
+ init_waitqueue_head(&stt_data.stt_waitq);
+ rc = stt_start_timer_thread();
+ if (rc != 0)
+ CERROR("Can't spawn timer thread: %d\n", rc);
+
+ return rc;
+}
+
+void
+stt_shutdown(void)
+{
+ int i;
+
+ spin_lock(&stt_data.stt_lock);
+
+ for (i = 0; i < STTIMER_NSLOTS; i++)
+ LASSERT(list_empty(&stt_data.stt_hash[i]));
+
+ stt_data.stt_shuttingdown = 1;
+
+ wake_up(&stt_data.stt_waitq);
+ lst_wait_until(stt_data.stt_nthreads == 0, stt_data.stt_lock,
+ "waiting for %d threads to terminate\n",
+ stt_data.stt_nthreads);
+
+ spin_unlock(&stt_data.stt_lock);
+}
diff --git a/drivers/staging/lustre/lnet/selftest/timer.h b/drivers/staging/lustre/lnet/selftest/timer.h
new file mode 100644
index 000000000..d727c1e2b
--- /dev/null
+++ b/drivers/staging/lustre/lnet/selftest/timer.h
@@ -0,0 +1,53 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ *
+ * lnet/selftest/timer.h
+ *
+ * Author: Isaac Huang <isaac@clusterfs.com>
+ */
+#ifndef __SELFTEST_TIMER_H__
+#define __SELFTEST_TIMER_H__
+
+typedef struct {
+ struct list_head stt_list;
+ unsigned long stt_expires;
+ void (*stt_func) (void *);
+ void *stt_data;
+} stt_timer_t;
+
+void stt_add_timer (stt_timer_t *timer);
+int stt_del_timer (stt_timer_t *timer);
+int stt_startup (void);
+void stt_shutdown (void);
+
+#endif /* __SELFTEST_TIMER_H__ */
diff --git a/drivers/staging/lustre/lustre/Kconfig b/drivers/staging/lustre/lustre/Kconfig
new file mode 100644
index 000000000..62c7bba75
--- /dev/null
+++ b/drivers/staging/lustre/lustre/Kconfig
@@ -0,0 +1,62 @@
+config LUSTRE_FS
+ tristate "Lustre file system client support"
+ depends on INET && m && !MIPS && !XTENSA && !SUPERH
+ select LNET
+ select CRYPTO
+ select CRYPTO_CRC32
+ select CRYPTO_CRC32_PCLMUL if X86
+ select CRYPTO_CRC32C
+ select CRYPTO_MD5
+ select CRYPTO_SHA1
+ select CRYPTO_SHA256
+ select CRYPTO_SHA512
+ depends on MULTIUSER
+ help
+ This option enables Lustre file system client support. Choose Y
+ here if you want to access a Lustre file system cluster. To compile
+ this file system support as a module, choose M here: the module will
+ be called lustre.
+
+ To mount Lustre file systems, you also need to install the user space
+ mount.lustre and other user space commands which can be found in the
+ lustre-client package, available from
+ http://downloads.whamcloud.com/public/lustre/
+
+ Lustre file system is the most popular cluster file system in high
+ performance computing. Source code of both kernel space and user space
+ Lustre components can also be found at
+ http://git.whamcloud.com/?p=fs/lustre-release.git;a=summary
+
+ If unsure, say N.
+
+ See also http://wiki.lustre.org/
+
+config LUSTRE_OBD_MAX_IOCTL_BUFFER
+ int "Lustre obd max ioctl buffer bytes (default 8KB)"
+ depends on LUSTRE_FS
+ default 8192
+ help
+ This option defines the maximum size of buffer in bytes that user space
+ applications can pass to Lustre kernel module through ioctl interface.
+
+ If unsure, use default.
+
+config LUSTRE_DEBUG_EXPENSIVE_CHECK
+ bool "Enable Lustre DEBUG checks"
+ depends on LUSTRE_FS
+ help
+ This option is mainly for debug purpose. It enables Lustre code to do
+ expensive checks that may have a performance impact.
+
+ Use with caution. If unsure, say N.
+
+config LUSTRE_TRANSLATE_ERRNOS
+ bool
+ depends on LUSTRE_FS && !X86
+ default y
+
+config LUSTRE_LLITE_LLOOP
+ tristate "Lustre virtual block device"
+ depends on LUSTRE_FS && BLOCK
+ depends on !PPC_64K_PAGES && !ARM64_64K_PAGES && !MICROBLAZE_64K_PAGES && !PAGE_SIZE_64KB && !IA64_PAGE_SIZE_64KB && !PARISC_PAGE_SIZE_64KB
+ default m
diff --git a/drivers/staging/lustre/lustre/Makefile b/drivers/staging/lustre/lustre/Makefile
new file mode 100644
index 000000000..35d8b0b2d
--- /dev/null
+++ b/drivers/staging/lustre/lustre/Makefile
@@ -0,0 +1,2 @@
+obj-$(CONFIG_LUSTRE_FS) += libcfs/ obdclass/ ptlrpc/ fld/ osc/ mgc/ \
+ fid/ lov/ mdc/ lmv/ llite/ obdecho/
diff --git a/drivers/staging/lustre/lustre/fid/Makefile b/drivers/staging/lustre/lustre/fid/Makefile
new file mode 100644
index 000000000..5513ce416
--- /dev/null
+++ b/drivers/staging/lustre/lustre/fid/Makefile
@@ -0,0 +1,3 @@
+obj-$(CONFIG_LUSTRE_FS) += fid.o
+fid-y := fid_request.o fid_lib.o
+fid-$(CONFIG_PROC_FS) += lproc_fid.o
diff --git a/drivers/staging/lustre/lustre/fid/fid_internal.h b/drivers/staging/lustre/lustre/fid/fid_internal.h
new file mode 100644
index 000000000..b5e8da895
--- /dev/null
+++ b/drivers/staging/lustre/lustre/fid/fid_internal.h
@@ -0,0 +1,56 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2012, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ *
+ * lustre/fid/fid_internal.h
+ *
+ * Author: Yury Umanets <umka@clusterfs.com>
+ */
+#ifndef __FID_INTERNAL_H
+#define __FID_INTERNAL_H
+
+#include "../include/lustre/lustre_idl.h"
+#include "../../include/linux/libcfs/libcfs.h"
+
+/* Functions used internally in module. */
+int seq_client_alloc_super(struct lu_client_seq *seq,
+ const struct lu_env *env);
+
+#if defined(CONFIG_PROC_FS)
+extern struct lprocfs_vars seq_client_proc_list[];
+#endif
+
+extern struct proc_dir_entry *seq_type_proc_dir;
+
+#endif /* __FID_INTERNAL_H */
diff --git a/drivers/staging/lustre/lustre/fid/fid_lib.c b/drivers/staging/lustre/lustre/fid/fid_lib.c
new file mode 100644
index 000000000..dd65159eb
--- /dev/null
+++ b/drivers/staging/lustre/lustre/fid/fid_lib.c
@@ -0,0 +1,95 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2012, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ *
+ * lustre/fid/fid_lib.c
+ *
+ * Miscellaneous fid functions.
+ *
+ * Author: Nikita Danilov <nikita@clusterfs.com>
+ * Author: Yury Umanets <umka@clusterfs.com>
+ */
+
+#define DEBUG_SUBSYSTEM S_FID
+
+#include "../../include/linux/libcfs/libcfs.h"
+#include <linux/module.h>
+#include "../include/lustre/lustre_idl.h"
+#include "../include/lustre_fid.h"
+
+/**
+ * A cluster-wide range from which fid-sequences are granted to servers and
+ * then clients.
+ *
+ * Fid namespace:
+ * <pre>
+ * Normal FID: seq:64 [2^33,2^64-1] oid:32 ver:32
+ * IGIF : 0:32, ino:32 gen:32 0:32
+ * IDIF : 0:31, 1:1, ost-index:16, objd:48 0:32
+ * </pre>
+ *
+ * The first 0x400 sequences of normal FID are reserved for special purpose.
+ * FID_SEQ_START + 1 is for local file id generation.
+ * FID_SEQ_START + 2 is for .lustre directory and its objects
+ */
+const struct lu_seq_range LUSTRE_SEQ_SPACE_RANGE = {
+ FID_SEQ_NORMAL,
+ (__u64)~0ULL
+};
+EXPORT_SYMBOL(LUSTRE_SEQ_SPACE_RANGE);
+
+/* Zero range, used for init and other purposes. */
+const struct lu_seq_range LUSTRE_SEQ_ZERO_RANGE = {
+ 0,
+ 0
+};
+EXPORT_SYMBOL(LUSTRE_SEQ_ZERO_RANGE);
+
+/* Lustre Big Fs Lock fid. */
+const struct lu_fid LUSTRE_BFL_FID = { .f_seq = FID_SEQ_SPECIAL,
+ .f_oid = FID_OID_SPECIAL_BFL,
+ .f_ver = 0x0000000000000000 };
+EXPORT_SYMBOL(LUSTRE_BFL_FID);
+
+/** Special fid for ".lustre" directory */
+const struct lu_fid LU_DOT_LUSTRE_FID = { .f_seq = FID_SEQ_DOT_LUSTRE,
+ .f_oid = FID_OID_DOT_LUSTRE,
+ .f_ver = 0x0000000000000000 };
+EXPORT_SYMBOL(LU_DOT_LUSTRE_FID);
+
+/** Special fid for "fid" special object in .lustre */
+const struct lu_fid LU_OBF_FID = { .f_seq = FID_SEQ_DOT_LUSTRE,
+ .f_oid = FID_OID_DOT_LUSTRE_OBF,
+ .f_ver = 0x0000000000000000 };
+EXPORT_SYMBOL(LU_OBF_FID);
diff --git a/drivers/staging/lustre/lustre/fid/fid_request.c b/drivers/staging/lustre/lustre/fid/fid_request.c
new file mode 100644
index 000000000..063441abf
--- /dev/null
+++ b/drivers/staging/lustre/lustre/fid/fid_request.c
@@ -0,0 +1,572 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2011, 2013, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ *
+ * lustre/fid/fid_request.c
+ *
+ * Lustre Sequence Manager
+ *
+ * Author: Yury Umanets <umka@clusterfs.com>
+ */
+
+#define DEBUG_SUBSYSTEM S_FID
+
+#include "../../include/linux/libcfs/libcfs.h"
+#include <linux/module.h>
+
+#include "../include/obd.h"
+#include "../include/obd_class.h"
+#include "../include/obd_support.h"
+#include "../include/lustre_fid.h"
+/* mdc RPC locks */
+#include "../include/lustre_mdc.h"
+#include "fid_internal.h"
+
+static int seq_client_rpc(struct lu_client_seq *seq,
+ struct lu_seq_range *output, __u32 opc,
+ const char *opcname)
+{
+ struct obd_export *exp = seq->lcs_exp;
+ struct ptlrpc_request *req;
+ struct lu_seq_range *out, *in;
+ __u32 *op;
+ unsigned int debug_mask;
+ int rc;
+
+ req = ptlrpc_request_alloc_pack(class_exp2cliimp(exp), &RQF_SEQ_QUERY,
+ LUSTRE_MDS_VERSION, SEQ_QUERY);
+ if (req == NULL)
+ return -ENOMEM;
+
+ /* Init operation code */
+ op = req_capsule_client_get(&req->rq_pill, &RMF_SEQ_OPC);
+ *op = opc;
+
+ /* Zero out input range, this is not recovery yet. */
+ in = req_capsule_client_get(&req->rq_pill, &RMF_SEQ_RANGE);
+ range_init(in);
+
+ ptlrpc_request_set_replen(req);
+
+ in->lsr_index = seq->lcs_space.lsr_index;
+ if (seq->lcs_type == LUSTRE_SEQ_METADATA)
+ fld_range_set_mdt(in);
+ else
+ fld_range_set_ost(in);
+
+ if (opc == SEQ_ALLOC_SUPER) {
+ req->rq_request_portal = SEQ_CONTROLLER_PORTAL;
+ req->rq_reply_portal = MDC_REPLY_PORTAL;
+ /* During allocating super sequence for data object,
+ * the current thread might hold the export of MDT0(MDT0
+ * precreating objects on this OST), and it will send the
+ * request to MDT0 here, so we can not keep resending the
+ * request here, otherwise if MDT0 is failed(umounted),
+ * it can not release the export of MDT0 */
+ if (seq->lcs_type == LUSTRE_SEQ_DATA)
+ req->rq_no_delay = req->rq_no_resend = 1;
+ debug_mask = D_CONSOLE;
+ } else {
+ if (seq->lcs_type == LUSTRE_SEQ_METADATA)
+ req->rq_request_portal = SEQ_METADATA_PORTAL;
+ else
+ req->rq_request_portal = SEQ_DATA_PORTAL;
+ debug_mask = D_INFO;
+ }
+
+ ptlrpc_at_set_req_timeout(req);
+
+ if (seq->lcs_type == LUSTRE_SEQ_METADATA)
+ mdc_get_rpc_lock(exp->exp_obd->u.cli.cl_rpc_lock, NULL);
+ rc = ptlrpc_queue_wait(req);
+ if (seq->lcs_type == LUSTRE_SEQ_METADATA)
+ mdc_put_rpc_lock(exp->exp_obd->u.cli.cl_rpc_lock, NULL);
+ if (rc)
+ goto out_req;
+
+ out = req_capsule_server_get(&req->rq_pill, &RMF_SEQ_RANGE);
+ *output = *out;
+
+ if (!range_is_sane(output)) {
+ CERROR("%s: Invalid range received from server: "
+ DRANGE"\n", seq->lcs_name, PRANGE(output));
+ rc = -EINVAL;
+ goto out_req;
+ }
+
+ if (range_is_exhausted(output)) {
+ CERROR("%s: Range received from server is exhausted: "
+ DRANGE"]\n", seq->lcs_name, PRANGE(output));
+ rc = -EINVAL;
+ goto out_req;
+ }
+
+ CDEBUG_LIMIT(debug_mask, "%s: Allocated %s-sequence "DRANGE"]\n",
+ seq->lcs_name, opcname, PRANGE(output));
+
+out_req:
+ ptlrpc_req_finished(req);
+ return rc;
+}
+
+/* Request sequence-controller node to allocate new super-sequence. */
+int seq_client_alloc_super(struct lu_client_seq *seq,
+ const struct lu_env *env)
+{
+ int rc;
+
+ mutex_lock(&seq->lcs_mutex);
+
+ if (seq->lcs_srv) {
+ rc = 0;
+ } else {
+ /* Check whether the connection to seq controller has been
+ * setup (lcs_exp != NULL) */
+ if (seq->lcs_exp == NULL) {
+ mutex_unlock(&seq->lcs_mutex);
+ return -EINPROGRESS;
+ }
+
+ rc = seq_client_rpc(seq, &seq->lcs_space,
+ SEQ_ALLOC_SUPER, "super");
+ }
+ mutex_unlock(&seq->lcs_mutex);
+ return rc;
+}
+
+/* Request sequence-controller node to allocate new meta-sequence. */
+static int seq_client_alloc_meta(const struct lu_env *env,
+ struct lu_client_seq *seq)
+{
+ int rc;
+
+ if (seq->lcs_srv) {
+ rc = 0;
+ } else {
+ do {
+ /* If meta server return -EINPROGRESS or EAGAIN,
+ * it means meta server might not be ready to
+ * allocate super sequence from sequence controller
+ * (MDT0)yet */
+ rc = seq_client_rpc(seq, &seq->lcs_space,
+ SEQ_ALLOC_META, "meta");
+ } while (rc == -EINPROGRESS || rc == -EAGAIN);
+ }
+
+ return rc;
+}
+
+/* Allocate new sequence for client. */
+static int seq_client_alloc_seq(const struct lu_env *env,
+ struct lu_client_seq *seq, u64 *seqnr)
+{
+ int rc;
+
+ LASSERT(range_is_sane(&seq->lcs_space));
+
+ if (range_is_exhausted(&seq->lcs_space)) {
+ rc = seq_client_alloc_meta(env, seq);
+ if (rc) {
+ CERROR("%s: Can't allocate new meta-sequence, rc %d\n",
+ seq->lcs_name, rc);
+ return rc;
+ } else {
+ CDEBUG(D_INFO, "%s: New range - "DRANGE"\n",
+ seq->lcs_name, PRANGE(&seq->lcs_space));
+ }
+ } else {
+ rc = 0;
+ }
+
+ LASSERT(!range_is_exhausted(&seq->lcs_space));
+ *seqnr = seq->lcs_space.lsr_start;
+ seq->lcs_space.lsr_start += 1;
+
+ CDEBUG(D_INFO, "%s: Allocated sequence [%#llx]\n", seq->lcs_name,
+ *seqnr);
+
+ return rc;
+}
+
+static int seq_fid_alloc_prep(struct lu_client_seq *seq,
+ wait_queue_t *link)
+{
+ if (seq->lcs_update) {
+ add_wait_queue(&seq->lcs_waitq, link);
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ mutex_unlock(&seq->lcs_mutex);
+
+ schedule();
+
+ mutex_lock(&seq->lcs_mutex);
+ remove_wait_queue(&seq->lcs_waitq, link);
+ set_current_state(TASK_RUNNING);
+ return -EAGAIN;
+ }
+ ++seq->lcs_update;
+ mutex_unlock(&seq->lcs_mutex);
+ return 0;
+}
+
+static void seq_fid_alloc_fini(struct lu_client_seq *seq)
+{
+ LASSERT(seq->lcs_update == 1);
+ mutex_lock(&seq->lcs_mutex);
+ --seq->lcs_update;
+ wake_up(&seq->lcs_waitq);
+}
+
+/**
+ * Allocate the whole seq to the caller.
+ **/
+int seq_client_get_seq(const struct lu_env *env,
+ struct lu_client_seq *seq, u64 *seqnr)
+{
+ wait_queue_t link;
+ int rc;
+
+ LASSERT(seqnr != NULL);
+ mutex_lock(&seq->lcs_mutex);
+ init_waitqueue_entry(&link, current);
+
+ while (1) {
+ rc = seq_fid_alloc_prep(seq, &link);
+ if (rc == 0)
+ break;
+ }
+
+ rc = seq_client_alloc_seq(env, seq, seqnr);
+ if (rc) {
+ CERROR("%s: Can't allocate new sequence, rc %d\n",
+ seq->lcs_name, rc);
+ seq_fid_alloc_fini(seq);
+ mutex_unlock(&seq->lcs_mutex);
+ return rc;
+ }
+
+ CDEBUG(D_INFO, "%s: allocate sequence [0x%16.16Lx]\n",
+ seq->lcs_name, *seqnr);
+
+ /* Since the caller require the whole seq,
+ * so marked this seq to be used */
+ if (seq->lcs_type == LUSTRE_SEQ_METADATA)
+ seq->lcs_fid.f_oid = LUSTRE_METADATA_SEQ_MAX_WIDTH;
+ else
+ seq->lcs_fid.f_oid = LUSTRE_DATA_SEQ_MAX_WIDTH;
+
+ seq->lcs_fid.f_seq = *seqnr;
+ seq->lcs_fid.f_ver = 0;
+ /*
+ * Inform caller that sequence switch is performed to allow it
+ * to setup FLD for it.
+ */
+ seq_fid_alloc_fini(seq);
+ mutex_unlock(&seq->lcs_mutex);
+
+ return rc;
+}
+EXPORT_SYMBOL(seq_client_get_seq);
+
+/* Allocate new fid on passed client @seq and save it to @fid. */
+int seq_client_alloc_fid(const struct lu_env *env,
+ struct lu_client_seq *seq, struct lu_fid *fid)
+{
+ wait_queue_t link;
+ int rc;
+
+ LASSERT(seq != NULL);
+ LASSERT(fid != NULL);
+
+ init_waitqueue_entry(&link, current);
+ mutex_lock(&seq->lcs_mutex);
+
+ if (OBD_FAIL_CHECK(OBD_FAIL_SEQ_EXHAUST))
+ seq->lcs_fid.f_oid = seq->lcs_width;
+
+ while (1) {
+ u64 seqnr;
+
+ if (!fid_is_zero(&seq->lcs_fid) &&
+ fid_oid(&seq->lcs_fid) < seq->lcs_width) {
+ /* Just bump last allocated fid and return to caller. */
+ seq->lcs_fid.f_oid += 1;
+ rc = 0;
+ break;
+ }
+
+ rc = seq_fid_alloc_prep(seq, &link);
+ if (rc)
+ continue;
+
+ rc = seq_client_alloc_seq(env, seq, &seqnr);
+ if (rc) {
+ CERROR("%s: Can't allocate new sequence, rc %d\n",
+ seq->lcs_name, rc);
+ seq_fid_alloc_fini(seq);
+ mutex_unlock(&seq->lcs_mutex);
+ return rc;
+ }
+
+ CDEBUG(D_INFO, "%s: Switch to sequence [0x%16.16Lx]\n",
+ seq->lcs_name, seqnr);
+
+ seq->lcs_fid.f_oid = LUSTRE_FID_INIT_OID;
+ seq->lcs_fid.f_seq = seqnr;
+ seq->lcs_fid.f_ver = 0;
+
+ /*
+ * Inform caller that sequence switch is performed to allow it
+ * to setup FLD for it.
+ */
+ rc = 1;
+
+ seq_fid_alloc_fini(seq);
+ break;
+ }
+
+ *fid = seq->lcs_fid;
+ mutex_unlock(&seq->lcs_mutex);
+
+ CDEBUG(D_INFO, "%s: Allocated FID "DFID"\n", seq->lcs_name, PFID(fid));
+ return rc;
+}
+EXPORT_SYMBOL(seq_client_alloc_fid);
+
+/*
+ * Finish the current sequence due to disconnect.
+ * See mdc_import_event()
+ */
+void seq_client_flush(struct lu_client_seq *seq)
+{
+ wait_queue_t link;
+
+ LASSERT(seq != NULL);
+ init_waitqueue_entry(&link, current);
+ mutex_lock(&seq->lcs_mutex);
+
+ while (seq->lcs_update) {
+ add_wait_queue(&seq->lcs_waitq, &link);
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ mutex_unlock(&seq->lcs_mutex);
+
+ schedule();
+
+ mutex_lock(&seq->lcs_mutex);
+ remove_wait_queue(&seq->lcs_waitq, &link);
+ set_current_state(TASK_RUNNING);
+ }
+
+ fid_zero(&seq->lcs_fid);
+ /**
+ * this id shld not be used for seq range allocation.
+ * set to -1 for dgb check.
+ */
+
+ seq->lcs_space.lsr_index = -1;
+
+ range_init(&seq->lcs_space);
+ mutex_unlock(&seq->lcs_mutex);
+}
+EXPORT_SYMBOL(seq_client_flush);
+
+static void seq_client_proc_fini(struct lu_client_seq *seq)
+{
+#if defined(CONFIG_PROC_FS)
+ if (seq->lcs_proc_dir) {
+ if (!IS_ERR(seq->lcs_proc_dir))
+ lprocfs_remove(&seq->lcs_proc_dir);
+ seq->lcs_proc_dir = NULL;
+ }
+#endif /* CONFIG_PROC_FS */
+}
+
+static int seq_client_proc_init(struct lu_client_seq *seq)
+{
+#if defined(CONFIG_PROC_FS)
+ int rc;
+
+ seq->lcs_proc_dir = lprocfs_register(seq->lcs_name,
+ seq_type_proc_dir,
+ NULL, NULL);
+
+ if (IS_ERR(seq->lcs_proc_dir)) {
+ CERROR("%s: LProcFS failed in seq-init\n",
+ seq->lcs_name);
+ rc = PTR_ERR(seq->lcs_proc_dir);
+ return rc;
+ }
+
+ rc = lprocfs_add_vars(seq->lcs_proc_dir,
+ seq_client_proc_list, seq);
+ if (rc) {
+ CERROR("%s: Can't init sequence manager proc, rc %d\n",
+ seq->lcs_name, rc);
+ goto out_cleanup;
+ }
+
+ return 0;
+
+out_cleanup:
+ seq_client_proc_fini(seq);
+ return rc;
+
+#else /* CONFIG_PROC_FS */
+ return 0;
+#endif
+}
+
+int seq_client_init(struct lu_client_seq *seq,
+ struct obd_export *exp,
+ enum lu_cli_type type,
+ const char *prefix,
+ struct lu_server_seq *srv)
+{
+ int rc;
+
+ LASSERT(seq != NULL);
+ LASSERT(prefix != NULL);
+
+ seq->lcs_srv = srv;
+ seq->lcs_type = type;
+
+ mutex_init(&seq->lcs_mutex);
+ if (type == LUSTRE_SEQ_METADATA)
+ seq->lcs_width = LUSTRE_METADATA_SEQ_MAX_WIDTH;
+ else
+ seq->lcs_width = LUSTRE_DATA_SEQ_MAX_WIDTH;
+
+ init_waitqueue_head(&seq->lcs_waitq);
+ /* Make sure that things are clear before work is started. */
+ seq_client_flush(seq);
+
+ if (exp != NULL)
+ seq->lcs_exp = class_export_get(exp);
+ else if (type == LUSTRE_SEQ_METADATA)
+ LASSERT(seq->lcs_srv != NULL);
+
+ snprintf(seq->lcs_name, sizeof(seq->lcs_name),
+ "cli-%s", prefix);
+
+ rc = seq_client_proc_init(seq);
+ if (rc)
+ seq_client_fini(seq);
+ return rc;
+}
+EXPORT_SYMBOL(seq_client_init);
+
+void seq_client_fini(struct lu_client_seq *seq)
+{
+ seq_client_proc_fini(seq);
+
+ if (seq->lcs_exp != NULL) {
+ class_export_put(seq->lcs_exp);
+ seq->lcs_exp = NULL;
+ }
+
+ seq->lcs_srv = NULL;
+}
+EXPORT_SYMBOL(seq_client_fini);
+
+int client_fid_init(struct obd_device *obd,
+ struct obd_export *exp, enum lu_cli_type type)
+{
+ struct client_obd *cli = &obd->u.cli;
+ char *prefix;
+ int rc;
+
+ OBD_ALLOC_PTR(cli->cl_seq);
+ if (cli->cl_seq == NULL)
+ return -ENOMEM;
+
+ OBD_ALLOC(prefix, MAX_OBD_NAME + 5);
+ if (prefix == NULL) {
+ rc = -ENOMEM;
+ goto out_free_seq;
+ }
+
+ snprintf(prefix, MAX_OBD_NAME + 5, "cli-%s", obd->obd_name);
+
+ /* Init client side sequence-manager */
+ rc = seq_client_init(cli->cl_seq, exp, type, prefix, NULL);
+ OBD_FREE(prefix, MAX_OBD_NAME + 5);
+ if (rc)
+ goto out_free_seq;
+
+ return rc;
+out_free_seq:
+ OBD_FREE_PTR(cli->cl_seq);
+ cli->cl_seq = NULL;
+ return rc;
+}
+EXPORT_SYMBOL(client_fid_init);
+
+int client_fid_fini(struct obd_device *obd)
+{
+ struct client_obd *cli = &obd->u.cli;
+
+ if (cli->cl_seq != NULL) {
+ seq_client_fini(cli->cl_seq);
+ OBD_FREE_PTR(cli->cl_seq);
+ cli->cl_seq = NULL;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL(client_fid_fini);
+
+struct proc_dir_entry *seq_type_proc_dir;
+
+static int __init fid_mod_init(void)
+{
+ seq_type_proc_dir = lprocfs_register(LUSTRE_SEQ_NAME,
+ proc_lustre_root,
+ NULL, NULL);
+ return PTR_ERR_OR_ZERO(seq_type_proc_dir);
+}
+
+static void __exit fid_mod_exit(void)
+{
+ if (seq_type_proc_dir != NULL && !IS_ERR(seq_type_proc_dir)) {
+ lprocfs_remove(&seq_type_proc_dir);
+ seq_type_proc_dir = NULL;
+ }
+}
+
+MODULE_AUTHOR("Sun Microsystems, Inc. <http://www.lustre.org/>");
+MODULE_DESCRIPTION("Lustre FID Module");
+MODULE_LICENSE("GPL");
+MODULE_VERSION("0.1.0");
+
+module_init(fid_mod_init);
+module_exit(fid_mod_exit);
diff --git a/drivers/staging/lustre/lustre/fid/lproc_fid.c b/drivers/staging/lustre/lustre/fid/lproc_fid.c
new file mode 100644
index 000000000..783939dbd
--- /dev/null
+++ b/drivers/staging/lustre/lustre/fid/lproc_fid.c
@@ -0,0 +1,225 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2011, 2012, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ *
+ * lustre/fid/lproc_fid.c
+ *
+ * Lustre Sequence Manager
+ *
+ * Author: Yury Umanets <umka@clusterfs.com>
+ */
+
+#define DEBUG_SUBSYSTEM S_FID
+
+#include "../../include/linux/libcfs/libcfs.h"
+#include <linux/module.h>
+
+#include "../include/obd.h"
+#include "../include/obd_class.h"
+#include "../include/dt_object.h"
+#include "../include/obd_support.h"
+#include "../include/lustre_req_layout.h"
+#include "../include/lustre_fid.h"
+#include "fid_internal.h"
+
+/* Format: [0x64BIT_INT - 0x64BIT_INT] + 32 bytes just in case */
+#define MAX_FID_RANGE_STRLEN (32 + 2 * 2 * sizeof(__u64))
+/*
+ * Note: this function is only used for testing, it is no safe for production
+ * use.
+ */
+static int lprocfs_fid_write_common(const char __user *buffer, size_t count,
+ struct lu_seq_range *range)
+{
+ struct lu_seq_range tmp;
+ int rc;
+ char kernbuf[MAX_FID_RANGE_STRLEN];
+
+ LASSERT(range != NULL);
+
+ if (count >= sizeof(kernbuf))
+ return -EINVAL;
+
+ if (copy_from_user(kernbuf, buffer, count))
+ return -EFAULT;
+
+ kernbuf[count] = 0;
+
+ if (count == 5 && strcmp(kernbuf, "clear") == 0) {
+ memset(range, 0, sizeof(*range));
+ return count;
+ }
+
+ /* of the form "[0x0000000240000400 - 0x000000028000400]" */
+ rc = sscanf(kernbuf, "[%llx - %llx]\n",
+ (unsigned long long *)&tmp.lsr_start,
+ (unsigned long long *)&tmp.lsr_end);
+ if (!range_is_sane(&tmp) || range_is_zero(&tmp) ||
+ tmp.lsr_start < range->lsr_start || tmp.lsr_end > range->lsr_end)
+ return -EINVAL;
+ *range = tmp;
+ return count;
+}
+
+/* Client side procfs stuff */
+static ssize_t lprocfs_fid_space_seq_write(struct file *file,
+ const char __user *buffer,
+ size_t count, loff_t *off)
+{
+ struct lu_client_seq *seq;
+ int rc;
+
+ seq = ((struct seq_file *)file->private_data)->private;
+ LASSERT(seq != NULL);
+
+ mutex_lock(&seq->lcs_mutex);
+ rc = lprocfs_fid_write_common(buffer, count, &seq->lcs_space);
+
+ if (rc == 0) {
+ CDEBUG(D_INFO, "%s: Space: "DRANGE"\n",
+ seq->lcs_name, PRANGE(&seq->lcs_space));
+ }
+
+ mutex_unlock(&seq->lcs_mutex);
+
+ return count;
+}
+
+static int
+lprocfs_fid_space_seq_show(struct seq_file *m, void *unused)
+{
+ struct lu_client_seq *seq = (struct lu_client_seq *)m->private;
+
+ LASSERT(seq != NULL);
+
+ mutex_lock(&seq->lcs_mutex);
+ seq_printf(m, "[%#llx - %#llx]:%x:%s\n", PRANGE(&seq->lcs_space));
+ mutex_unlock(&seq->lcs_mutex);
+
+ return 0;
+}
+
+static ssize_t lprocfs_fid_width_seq_write(struct file *file,
+ const char __user *buffer,
+ size_t count, loff_t *off)
+{
+ struct lu_client_seq *seq;
+ __u64 max;
+ int rc, val;
+
+ seq = ((struct seq_file *)file->private_data)->private;
+ LASSERT(seq != NULL);
+
+ rc = lprocfs_write_helper(buffer, count, &val);
+ if (rc)
+ return rc;
+
+ mutex_lock(&seq->lcs_mutex);
+ if (seq->lcs_type == LUSTRE_SEQ_DATA)
+ max = LUSTRE_DATA_SEQ_MAX_WIDTH;
+ else
+ max = LUSTRE_METADATA_SEQ_MAX_WIDTH;
+
+ if (val <= max && val > 0) {
+ seq->lcs_width = val;
+
+ if (rc == 0) {
+ CDEBUG(D_INFO, "%s: Sequence size: %llu\n",
+ seq->lcs_name, seq->lcs_width);
+ }
+ }
+
+ mutex_unlock(&seq->lcs_mutex);
+
+ return count;
+}
+
+static int
+lprocfs_fid_width_seq_show(struct seq_file *m, void *unused)
+{
+ struct lu_client_seq *seq = (struct lu_client_seq *)m->private;
+
+ LASSERT(seq != NULL);
+
+ mutex_lock(&seq->lcs_mutex);
+ seq_printf(m, "%llu\n", seq->lcs_width);
+ mutex_unlock(&seq->lcs_mutex);
+
+ return 0;
+}
+
+static int
+lprocfs_fid_fid_seq_show(struct seq_file *m, void *unused)
+{
+ struct lu_client_seq *seq = (struct lu_client_seq *)m->private;
+
+ LASSERT(seq != NULL);
+
+ mutex_lock(&seq->lcs_mutex);
+ seq_printf(m, DFID "\n", PFID(&seq->lcs_fid));
+ mutex_unlock(&seq->lcs_mutex);
+
+ return 0;
+}
+
+static int
+lprocfs_fid_server_seq_show(struct seq_file *m, void *unused)
+{
+ struct lu_client_seq *seq = (struct lu_client_seq *)m->private;
+ struct client_obd *cli;
+
+ LASSERT(seq != NULL);
+
+ if (seq->lcs_exp != NULL) {
+ cli = &seq->lcs_exp->exp_obd->u.cli;
+ seq_printf(m, "%s\n", cli->cl_target_uuid.uuid);
+ } else {
+ seq_printf(m, "%s\n", seq->lcs_srv->lss_name);
+ }
+
+ return 0;
+}
+
+LPROC_SEQ_FOPS(lprocfs_fid_space);
+LPROC_SEQ_FOPS(lprocfs_fid_width);
+LPROC_SEQ_FOPS_RO(lprocfs_fid_server);
+LPROC_SEQ_FOPS_RO(lprocfs_fid_fid);
+
+struct lprocfs_vars seq_client_proc_list[] = {
+ { "space", &lprocfs_fid_space_fops },
+ { "width", &lprocfs_fid_width_fops },
+ { "server", &lprocfs_fid_server_fops },
+ { "fid", &lprocfs_fid_fid_fops },
+ { NULL }
+};
diff --git a/drivers/staging/lustre/lustre/fld/Makefile b/drivers/staging/lustre/lustre/fld/Makefile
new file mode 100644
index 000000000..2bbf08433
--- /dev/null
+++ b/drivers/staging/lustre/lustre/fld/Makefile
@@ -0,0 +1,3 @@
+obj-$(CONFIG_LUSTRE_FS) += fld.o
+fld-y := fld_request.o fld_cache.o
+fld-$(CONFIG_PROC_FS) += lproc_fld.o
diff --git a/drivers/staging/lustre/lustre/fld/fld_cache.c b/drivers/staging/lustre/lustre/fld/fld_cache.c
new file mode 100644
index 000000000..0d0a73745
--- /dev/null
+++ b/drivers/staging/lustre/lustre/fld/fld_cache.c
@@ -0,0 +1,546 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2012, 2013, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ *
+ * lustre/fld/fld_cache.c
+ *
+ * FLD (Fids Location Database)
+ *
+ * Author: Pravin Shelar <pravin.shelar@sun.com>
+ * Author: Yury Umanets <umka@clusterfs.com>
+ */
+
+#define DEBUG_SUBSYSTEM S_FLD
+
+#include "../../include/linux/libcfs/libcfs.h"
+#include <linux/module.h>
+#include <asm/div64.h>
+
+#include "../include/obd.h"
+#include "../include/obd_class.h"
+#include "../include/lustre_ver.h"
+#include "../include/obd_support.h"
+#include "../include/lprocfs_status.h"
+
+#include "../include/dt_object.h"
+#include "../include/lustre_req_layout.h"
+#include "../include/lustre_fld.h"
+#include "fld_internal.h"
+
+/**
+ * create fld cache.
+ */
+struct fld_cache *fld_cache_init(const char *name,
+ int cache_size, int cache_threshold)
+{
+ struct fld_cache *cache;
+
+ LASSERT(name != NULL);
+ LASSERT(cache_threshold < cache_size);
+
+ OBD_ALLOC_PTR(cache);
+ if (cache == NULL)
+ return ERR_PTR(-ENOMEM);
+
+ INIT_LIST_HEAD(&cache->fci_entries_head);
+ INIT_LIST_HEAD(&cache->fci_lru);
+
+ cache->fci_cache_count = 0;
+ rwlock_init(&cache->fci_lock);
+
+ strlcpy(cache->fci_name, name,
+ sizeof(cache->fci_name));
+
+ cache->fci_cache_size = cache_size;
+ cache->fci_threshold = cache_threshold;
+
+ /* Init fld cache info. */
+ memset(&cache->fci_stat, 0, sizeof(cache->fci_stat));
+
+ CDEBUG(D_INFO, "%s: FLD cache - Size: %d, Threshold: %d\n",
+ cache->fci_name, cache_size, cache_threshold);
+
+ return cache;
+}
+
+/**
+ * destroy fld cache.
+ */
+void fld_cache_fini(struct fld_cache *cache)
+{
+ __u64 pct;
+
+ LASSERT(cache != NULL);
+ fld_cache_flush(cache);
+
+ if (cache->fci_stat.fst_count > 0) {
+ pct = cache->fci_stat.fst_cache * 100;
+ do_div(pct, cache->fci_stat.fst_count);
+ } else {
+ pct = 0;
+ }
+
+ CDEBUG(D_INFO, "FLD cache statistics (%s):\n", cache->fci_name);
+ CDEBUG(D_INFO, " Total reqs: %llu\n", cache->fci_stat.fst_count);
+ CDEBUG(D_INFO, " Cache reqs: %llu\n", cache->fci_stat.fst_cache);
+ CDEBUG(D_INFO, " Cache hits: %llu%%\n", pct);
+
+ OBD_FREE_PTR(cache);
+}
+
+/**
+ * delete given node from list.
+ */
+void fld_cache_entry_delete(struct fld_cache *cache,
+ struct fld_cache_entry *node)
+{
+ list_del(&node->fce_list);
+ list_del(&node->fce_lru);
+ cache->fci_cache_count--;
+ OBD_FREE_PTR(node);
+}
+
+/**
+ * fix list by checking new entry with NEXT entry in order.
+ */
+static void fld_fix_new_list(struct fld_cache *cache)
+{
+ struct fld_cache_entry *f_curr;
+ struct fld_cache_entry *f_next;
+ struct lu_seq_range *c_range;
+ struct lu_seq_range *n_range;
+ struct list_head *head = &cache->fci_entries_head;
+
+restart_fixup:
+
+ list_for_each_entry_safe(f_curr, f_next, head, fce_list) {
+ c_range = &f_curr->fce_range;
+ n_range = &f_next->fce_range;
+
+ LASSERT(range_is_sane(c_range));
+ if (&f_next->fce_list == head)
+ break;
+
+ if (c_range->lsr_flags != n_range->lsr_flags)
+ continue;
+
+ LASSERTF(c_range->lsr_start <= n_range->lsr_start,
+ "cur lsr_start "DRANGE" next lsr_start "DRANGE"\n",
+ PRANGE(c_range), PRANGE(n_range));
+
+ /* check merge possibility with next range */
+ if (c_range->lsr_end == n_range->lsr_start) {
+ if (c_range->lsr_index != n_range->lsr_index)
+ continue;
+ n_range->lsr_start = c_range->lsr_start;
+ fld_cache_entry_delete(cache, f_curr);
+ continue;
+ }
+
+ /* check if current range overlaps with next range. */
+ if (n_range->lsr_start < c_range->lsr_end) {
+ if (c_range->lsr_index == n_range->lsr_index) {
+ n_range->lsr_start = c_range->lsr_start;
+ n_range->lsr_end = max(c_range->lsr_end,
+ n_range->lsr_end);
+ fld_cache_entry_delete(cache, f_curr);
+ } else {
+ if (n_range->lsr_end <= c_range->lsr_end) {
+ *n_range = *c_range;
+ fld_cache_entry_delete(cache, f_curr);
+ } else
+ n_range->lsr_start = c_range->lsr_end;
+ }
+
+ /* we could have overlap over next
+ * range too. better restart. */
+ goto restart_fixup;
+ }
+
+ /* kill duplicates */
+ if (c_range->lsr_start == n_range->lsr_start &&
+ c_range->lsr_end == n_range->lsr_end)
+ fld_cache_entry_delete(cache, f_curr);
+ }
+}
+
+/**
+ * add node to fld cache
+ */
+static inline void fld_cache_entry_add(struct fld_cache *cache,
+ struct fld_cache_entry *f_new,
+ struct list_head *pos)
+{
+ list_add(&f_new->fce_list, pos);
+ list_add(&f_new->fce_lru, &cache->fci_lru);
+
+ cache->fci_cache_count++;
+ fld_fix_new_list(cache);
+}
+
+/**
+ * Check if cache needs to be shrunk. If so - do it.
+ * Remove one entry in list and so on until cache is shrunk enough.
+ */
+static int fld_cache_shrink(struct fld_cache *cache)
+{
+ struct fld_cache_entry *flde;
+ struct list_head *curr;
+ int num = 0;
+
+ LASSERT(cache != NULL);
+
+ if (cache->fci_cache_count < cache->fci_cache_size)
+ return 0;
+
+ curr = cache->fci_lru.prev;
+
+ while (cache->fci_cache_count + cache->fci_threshold >
+ cache->fci_cache_size && curr != &cache->fci_lru) {
+
+ flde = list_entry(curr, struct fld_cache_entry, fce_lru);
+ curr = curr->prev;
+ fld_cache_entry_delete(cache, flde);
+ num++;
+ }
+
+ CDEBUG(D_INFO, "%s: FLD cache - Shrunk by %d entries\n",
+ cache->fci_name, num);
+
+ return 0;
+}
+
+/**
+ * kill all fld cache entries.
+ */
+void fld_cache_flush(struct fld_cache *cache)
+{
+ write_lock(&cache->fci_lock);
+ cache->fci_cache_size = 0;
+ fld_cache_shrink(cache);
+ write_unlock(&cache->fci_lock);
+}
+
+/**
+ * punch hole in existing range. divide this range and add new
+ * entry accordingly.
+ */
+
+static void fld_cache_punch_hole(struct fld_cache *cache,
+ struct fld_cache_entry *f_curr,
+ struct fld_cache_entry *f_new)
+{
+ const struct lu_seq_range *range = &f_new->fce_range;
+ const u64 new_start = range->lsr_start;
+ const u64 new_end = range->lsr_end;
+ struct fld_cache_entry *fldt;
+
+ OBD_ALLOC_GFP(fldt, sizeof(*fldt), GFP_ATOMIC);
+ if (!fldt) {
+ OBD_FREE_PTR(f_new);
+ /* overlap is not allowed, so dont mess up list. */
+ return;
+ }
+ /* break f_curr RANGE into three RANGES:
+ * f_curr, f_new , fldt
+ */
+
+ /* f_new = *range */
+
+ /* fldt */
+ fldt->fce_range.lsr_start = new_end;
+ fldt->fce_range.lsr_end = f_curr->fce_range.lsr_end;
+ fldt->fce_range.lsr_index = f_curr->fce_range.lsr_index;
+
+ /* f_curr */
+ f_curr->fce_range.lsr_end = new_start;
+
+ /* add these two entries to list */
+ fld_cache_entry_add(cache, f_new, &f_curr->fce_list);
+ fld_cache_entry_add(cache, fldt, &f_new->fce_list);
+
+ /* no need to fixup */
+}
+
+/**
+ * handle range overlap in fld cache.
+ */
+static void fld_cache_overlap_handle(struct fld_cache *cache,
+ struct fld_cache_entry *f_curr,
+ struct fld_cache_entry *f_new)
+{
+ const struct lu_seq_range *range = &f_new->fce_range;
+ const u64 new_start = range->lsr_start;
+ const u64 new_end = range->lsr_end;
+ const u32 mdt = range->lsr_index;
+
+ /* this is overlap case, these case are checking overlapping with
+ * prev range only. fixup will handle overlapping with next range. */
+
+ if (f_curr->fce_range.lsr_index == mdt) {
+ f_curr->fce_range.lsr_start = min(f_curr->fce_range.lsr_start,
+ new_start);
+
+ f_curr->fce_range.lsr_end = max(f_curr->fce_range.lsr_end,
+ new_end);
+
+ OBD_FREE_PTR(f_new);
+ fld_fix_new_list(cache);
+
+ } else if (new_start <= f_curr->fce_range.lsr_start &&
+ f_curr->fce_range.lsr_end <= new_end) {
+ /* case 1: new range completely overshadowed existing range.
+ * e.g. whole range migrated. update fld cache entry */
+
+ f_curr->fce_range = *range;
+ OBD_FREE_PTR(f_new);
+ fld_fix_new_list(cache);
+
+ } else if (f_curr->fce_range.lsr_start < new_start &&
+ new_end < f_curr->fce_range.lsr_end) {
+ /* case 2: new range fit within existing range. */
+
+ fld_cache_punch_hole(cache, f_curr, f_new);
+
+ } else if (new_end <= f_curr->fce_range.lsr_end) {
+ /* case 3: overlap:
+ * [new_start [c_start new_end) c_end)
+ */
+
+ LASSERT(new_start <= f_curr->fce_range.lsr_start);
+
+ f_curr->fce_range.lsr_start = new_end;
+ fld_cache_entry_add(cache, f_new, f_curr->fce_list.prev);
+
+ } else if (f_curr->fce_range.lsr_start <= new_start) {
+ /* case 4: overlap:
+ * [c_start [new_start c_end) new_end)
+ */
+
+ LASSERT(f_curr->fce_range.lsr_end <= new_end);
+
+ f_curr->fce_range.lsr_end = new_start;
+ fld_cache_entry_add(cache, f_new, &f_curr->fce_list);
+ } else
+ CERROR("NEW range ="DRANGE" curr = "DRANGE"\n",
+ PRANGE(range), PRANGE(&f_curr->fce_range));
+}
+
+struct fld_cache_entry
+*fld_cache_entry_create(const struct lu_seq_range *range)
+{
+ struct fld_cache_entry *f_new;
+
+ LASSERT(range_is_sane(range));
+
+ OBD_ALLOC_PTR(f_new);
+ if (!f_new)
+ return ERR_PTR(-ENOMEM);
+
+ f_new->fce_range = *range;
+ return f_new;
+}
+
+/**
+ * Insert FLD entry in FLD cache.
+ *
+ * This function handles all cases of merging and breaking up of
+ * ranges.
+ */
+int fld_cache_insert_nolock(struct fld_cache *cache,
+ struct fld_cache_entry *f_new)
+{
+ struct fld_cache_entry *f_curr;
+ struct fld_cache_entry *n;
+ struct list_head *head;
+ struct list_head *prev = NULL;
+ const u64 new_start = f_new->fce_range.lsr_start;
+ const u64 new_end = f_new->fce_range.lsr_end;
+ __u32 new_flags = f_new->fce_range.lsr_flags;
+
+ /*
+ * Duplicate entries are eliminated in insert op.
+ * So we don't need to search new entry before starting
+ * insertion loop.
+ */
+
+ if (!cache->fci_no_shrink)
+ fld_cache_shrink(cache);
+
+ head = &cache->fci_entries_head;
+
+ list_for_each_entry_safe(f_curr, n, head, fce_list) {
+ /* add list if next is end of list */
+ if (new_end < f_curr->fce_range.lsr_start ||
+ (new_end == f_curr->fce_range.lsr_start &&
+ new_flags != f_curr->fce_range.lsr_flags))
+ break;
+
+ prev = &f_curr->fce_list;
+ /* check if this range is to left of new range. */
+ if (new_start < f_curr->fce_range.lsr_end &&
+ new_flags == f_curr->fce_range.lsr_flags) {
+ fld_cache_overlap_handle(cache, f_curr, f_new);
+ goto out;
+ }
+ }
+
+ if (prev == NULL)
+ prev = head;
+
+ CDEBUG(D_INFO, "insert range "DRANGE"\n", PRANGE(&f_new->fce_range));
+ /* Add new entry to cache and lru list. */
+ fld_cache_entry_add(cache, f_new, prev);
+out:
+ return 0;
+}
+
+int fld_cache_insert(struct fld_cache *cache,
+ const struct lu_seq_range *range)
+{
+ struct fld_cache_entry *flde;
+ int rc;
+
+ flde = fld_cache_entry_create(range);
+ if (IS_ERR(flde))
+ return PTR_ERR(flde);
+
+ write_lock(&cache->fci_lock);
+ rc = fld_cache_insert_nolock(cache, flde);
+ write_unlock(&cache->fci_lock);
+ if (rc)
+ OBD_FREE_PTR(flde);
+
+ return rc;
+}
+
+void fld_cache_delete_nolock(struct fld_cache *cache,
+ const struct lu_seq_range *range)
+{
+ struct fld_cache_entry *flde;
+ struct fld_cache_entry *tmp;
+ struct list_head *head;
+
+ head = &cache->fci_entries_head;
+ list_for_each_entry_safe(flde, tmp, head, fce_list) {
+ /* add list if next is end of list */
+ if (range->lsr_start == flde->fce_range.lsr_start ||
+ (range->lsr_end == flde->fce_range.lsr_end &&
+ range->lsr_flags == flde->fce_range.lsr_flags)) {
+ fld_cache_entry_delete(cache, flde);
+ break;
+ }
+ }
+}
+
+/**
+ * Delete FLD entry in FLD cache.
+ *
+ */
+void fld_cache_delete(struct fld_cache *cache,
+ const struct lu_seq_range *range)
+{
+ write_lock(&cache->fci_lock);
+ fld_cache_delete_nolock(cache, range);
+ write_unlock(&cache->fci_lock);
+}
+
+struct fld_cache_entry
+*fld_cache_entry_lookup_nolock(struct fld_cache *cache,
+ struct lu_seq_range *range)
+{
+ struct fld_cache_entry *flde;
+ struct fld_cache_entry *got = NULL;
+ struct list_head *head;
+
+ head = &cache->fci_entries_head;
+ list_for_each_entry(flde, head, fce_list) {
+ if (range->lsr_start == flde->fce_range.lsr_start ||
+ (range->lsr_end == flde->fce_range.lsr_end &&
+ range->lsr_flags == flde->fce_range.lsr_flags)) {
+ got = flde;
+ break;
+ }
+ }
+
+ return got;
+}
+
+/**
+ * lookup \a seq sequence for range in fld cache.
+ */
+struct fld_cache_entry
+*fld_cache_entry_lookup(struct fld_cache *cache, struct lu_seq_range *range)
+{
+ struct fld_cache_entry *got = NULL;
+
+ read_lock(&cache->fci_lock);
+ got = fld_cache_entry_lookup_nolock(cache, range);
+ read_unlock(&cache->fci_lock);
+ return got;
+}
+
+/**
+ * lookup \a seq sequence for range in fld cache.
+ */
+int fld_cache_lookup(struct fld_cache *cache,
+ const u64 seq, struct lu_seq_range *range)
+{
+ struct fld_cache_entry *flde;
+ struct fld_cache_entry *prev = NULL;
+ struct list_head *head;
+
+ read_lock(&cache->fci_lock);
+ head = &cache->fci_entries_head;
+
+ cache->fci_stat.fst_count++;
+ list_for_each_entry(flde, head, fce_list) {
+ if (flde->fce_range.lsr_start > seq) {
+ if (prev != NULL)
+ *range = prev->fce_range;
+ break;
+ }
+
+ prev = flde;
+ if (range_within(&flde->fce_range, seq)) {
+ *range = flde->fce_range;
+
+ cache->fci_stat.fst_cache++;
+ read_unlock(&cache->fci_lock);
+ return 0;
+ }
+ }
+ read_unlock(&cache->fci_lock);
+ return -ENOENT;
+}
diff --git a/drivers/staging/lustre/lustre/fld/fld_internal.h b/drivers/staging/lustre/lustre/fld/fld_internal.h
new file mode 100644
index 000000000..68bec7658
--- /dev/null
+++ b/drivers/staging/lustre/lustre/fld/fld_internal.h
@@ -0,0 +1,193 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2012, 2013, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ *
+ * lustre/fld/fld_internal.h
+ *
+ * Author: Yury Umanets <umka@clusterfs.com>
+ * Author: Tom WangDi <wangdi@clusterfs.com>
+ */
+#ifndef __FLD_INTERNAL_H
+#define __FLD_INTERNAL_H
+
+#include "../include/lustre/lustre_idl.h"
+#include "../include/dt_object.h"
+
+#include "../../include/linux/libcfs/libcfs.h"
+#include "../include/lustre_req_layout.h"
+#include "../include/lustre_fld.h"
+
+enum {
+ LUSTRE_FLD_INIT = 1 << 0,
+ LUSTRE_FLD_RUN = 1 << 1
+};
+
+struct fld_stats {
+ __u64 fst_count;
+ __u64 fst_cache;
+ __u64 fst_inflight;
+};
+
+typedef int (*fld_hash_func_t) (struct lu_client_fld *, __u64);
+
+typedef struct lu_fld_target *
+(*fld_scan_func_t) (struct lu_client_fld *, __u64);
+
+struct lu_fld_hash {
+ const char *fh_name;
+ fld_hash_func_t fh_hash_func;
+ fld_scan_func_t fh_scan_func;
+};
+
+struct fld_cache_entry {
+ struct list_head fce_lru;
+ struct list_head fce_list;
+ /**
+ * fld cache entries are sorted on range->lsr_start field. */
+ struct lu_seq_range fce_range;
+};
+
+struct fld_cache {
+ /**
+ * Cache guard, protects fci_hash mostly because others immutable after
+ * init is finished.
+ */
+ rwlock_t fci_lock;
+
+ /**
+ * Cache shrink threshold */
+ int fci_threshold;
+
+ /**
+ * Preferred number of cached entries */
+ int fci_cache_size;
+
+ /**
+ * Current number of cached entries. Protected by \a fci_lock */
+ int fci_cache_count;
+
+ /**
+ * LRU list fld entries. */
+ struct list_head fci_lru;
+
+ /**
+ * sorted fld entries. */
+ struct list_head fci_entries_head;
+
+ /**
+ * Cache statistics. */
+ struct fld_stats fci_stat;
+
+ /**
+ * Cache name used for debug and messages. */
+ char fci_name[LUSTRE_MDT_MAXNAMELEN];
+ unsigned int fci_no_shrink:1;
+};
+
+enum fld_op {
+ FLD_CREATE = 0,
+ FLD_DELETE = 1,
+ FLD_LOOKUP = 2
+};
+
+enum {
+ /* 4M of FLD cache will not hurt client a lot. */
+ FLD_SERVER_CACHE_SIZE = (4 * 0x100000),
+
+ /* 1M of FLD cache will not hurt client a lot. */
+ FLD_CLIENT_CACHE_SIZE = (1 * 0x100000)
+};
+
+enum {
+ /* Cache threshold is 10 percent of size. */
+ FLD_SERVER_CACHE_THRESHOLD = 10,
+
+ /* Cache threshold is 10 percent of size. */
+ FLD_CLIENT_CACHE_THRESHOLD = 10
+};
+
+extern struct lu_fld_hash fld_hash[];
+
+int fld_client_rpc(struct obd_export *exp,
+ struct lu_seq_range *range, __u32 fld_op);
+
+#if defined(CONFIG_PROC_FS)
+extern struct lprocfs_vars fld_client_proc_list[];
+#endif
+
+
+struct fld_cache *fld_cache_init(const char *name,
+ int cache_size, int cache_threshold);
+
+void fld_cache_fini(struct fld_cache *cache);
+
+void fld_cache_flush(struct fld_cache *cache);
+
+int fld_cache_insert(struct fld_cache *cache,
+ const struct lu_seq_range *range);
+
+struct fld_cache_entry
+*fld_cache_entry_create(const struct lu_seq_range *range);
+
+int fld_cache_insert_nolock(struct fld_cache *cache,
+ struct fld_cache_entry *f_new);
+void fld_cache_delete(struct fld_cache *cache,
+ const struct lu_seq_range *range);
+void fld_cache_delete_nolock(struct fld_cache *cache,
+ const struct lu_seq_range *range);
+int fld_cache_lookup(struct fld_cache *cache,
+ const u64 seq, struct lu_seq_range *range);
+
+struct fld_cache_entry*
+fld_cache_entry_lookup(struct fld_cache *cache, struct lu_seq_range *range);
+void fld_cache_entry_delete(struct fld_cache *cache,
+ struct fld_cache_entry *node);
+void fld_dump_cache_entries(struct fld_cache *cache);
+
+struct fld_cache_entry
+*fld_cache_entry_lookup_nolock(struct fld_cache *cache,
+ struct lu_seq_range *range);
+int fld_write_range(const struct lu_env *env, struct dt_object *dt,
+ const struct lu_seq_range *range, struct thandle *th);
+
+static inline const char *
+fld_target_name(struct lu_fld_target *tar)
+{
+ if (tar->ft_srv != NULL)
+ return tar->ft_srv->lsf_name;
+
+ return (const char *)tar->ft_exp->exp_obd->obd_name;
+}
+
+#endif /* __FLD_INTERNAL_H */
diff --git a/drivers/staging/lustre/lustre/fld/fld_request.c b/drivers/staging/lustre/lustre/fld/fld_request.c
new file mode 100644
index 000000000..6ac225e90
--- /dev/null
+++ b/drivers/staging/lustre/lustre/fld/fld_request.c
@@ -0,0 +1,526 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2011, 2013, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ *
+ * lustre/fld/fld_request.c
+ *
+ * FLD (Fids Location Database)
+ *
+ * Author: Yury Umanets <umka@clusterfs.com>
+ */
+
+#define DEBUG_SUBSYSTEM S_FLD
+
+#include "../../include/linux/libcfs/libcfs.h"
+#include <linux/module.h>
+#include <asm/div64.h>
+
+#include "../include/obd.h"
+#include "../include/obd_class.h"
+#include "../include/lustre_ver.h"
+#include "../include/obd_support.h"
+#include "../include/lprocfs_status.h"
+
+#include "../include/dt_object.h"
+#include "../include/lustre_req_layout.h"
+#include "../include/lustre_fld.h"
+#include "../include/lustre_mdc.h"
+#include "fld_internal.h"
+
+/* TODO: these 3 functions are copies of flow-control code from mdc_lib.c
+ * It should be common thing. The same about mdc RPC lock */
+static int fld_req_avail(struct client_obd *cli, struct mdc_cache_waiter *mcw)
+{
+ int rc;
+
+ client_obd_list_lock(&cli->cl_loi_list_lock);
+ rc = list_empty(&mcw->mcw_entry);
+ client_obd_list_unlock(&cli->cl_loi_list_lock);
+ return rc;
+};
+
+static void fld_enter_request(struct client_obd *cli)
+{
+ struct mdc_cache_waiter mcw;
+ struct l_wait_info lwi = { 0 };
+
+ client_obd_list_lock(&cli->cl_loi_list_lock);
+ if (cli->cl_r_in_flight >= cli->cl_max_rpcs_in_flight) {
+ list_add_tail(&mcw.mcw_entry, &cli->cl_cache_waiters);
+ init_waitqueue_head(&mcw.mcw_waitq);
+ client_obd_list_unlock(&cli->cl_loi_list_lock);
+ l_wait_event(mcw.mcw_waitq, fld_req_avail(cli, &mcw), &lwi);
+ } else {
+ cli->cl_r_in_flight++;
+ client_obd_list_unlock(&cli->cl_loi_list_lock);
+ }
+}
+
+static void fld_exit_request(struct client_obd *cli)
+{
+ struct list_head *l, *tmp;
+ struct mdc_cache_waiter *mcw;
+
+ client_obd_list_lock(&cli->cl_loi_list_lock);
+ cli->cl_r_in_flight--;
+ list_for_each_safe(l, tmp, &cli->cl_cache_waiters) {
+
+ if (cli->cl_r_in_flight >= cli->cl_max_rpcs_in_flight) {
+ /* No free request slots anymore */
+ break;
+ }
+
+ mcw = list_entry(l, struct mdc_cache_waiter, mcw_entry);
+ list_del_init(&mcw->mcw_entry);
+ cli->cl_r_in_flight++;
+ wake_up(&mcw->mcw_waitq);
+ }
+ client_obd_list_unlock(&cli->cl_loi_list_lock);
+}
+
+static int fld_rrb_hash(struct lu_client_fld *fld, u64 seq)
+{
+ LASSERT(fld->lcf_count > 0);
+ return do_div(seq, fld->lcf_count);
+}
+
+static struct lu_fld_target *
+fld_rrb_scan(struct lu_client_fld *fld, u64 seq)
+{
+ struct lu_fld_target *target;
+ int hash;
+
+ /* Because almost all of special sequence located in MDT0,
+ * it should go to index 0 directly, instead of calculating
+ * hash again, and also if other MDTs is not being connected,
+ * the fld lookup requests(for seq on MDT0) should not be
+ * blocked because of other MDTs */
+ if (fid_seq_is_norm(seq))
+ hash = fld_rrb_hash(fld, seq);
+ else
+ hash = 0;
+
+again:
+ list_for_each_entry(target, &fld->lcf_targets, ft_chain) {
+ if (target->ft_idx == hash)
+ return target;
+ }
+
+ if (hash != 0) {
+ /* It is possible the remote target(MDT) are not connected to
+ * with client yet, so we will refer this to MDT0, which should
+ * be connected during mount */
+ hash = 0;
+ goto again;
+ }
+
+ CERROR("%s: Can't find target by hash %d (seq %#llx). Targets (%d):\n",
+ fld->lcf_name, hash, seq, fld->lcf_count);
+
+ list_for_each_entry(target, &fld->lcf_targets, ft_chain) {
+ const char *srv_name = target->ft_srv != NULL ?
+ target->ft_srv->lsf_name : "<null>";
+ const char *exp_name = target->ft_exp != NULL ?
+ (char *)target->ft_exp->exp_obd->obd_uuid.uuid :
+ "<null>";
+
+ CERROR(" exp: 0x%p (%s), srv: 0x%p (%s), idx: %llu\n",
+ target->ft_exp, exp_name, target->ft_srv,
+ srv_name, target->ft_idx);
+ }
+
+ /*
+ * If target is not found, there is logical error anyway, so here is
+ * LBUG() to catch this situation.
+ */
+ LBUG();
+ return NULL;
+}
+
+struct lu_fld_hash fld_hash[] = {
+ {
+ .fh_name = "RRB",
+ .fh_hash_func = fld_rrb_hash,
+ .fh_scan_func = fld_rrb_scan
+ },
+ {
+ NULL,
+ }
+};
+
+static struct lu_fld_target *
+fld_client_get_target(struct lu_client_fld *fld, u64 seq)
+{
+ struct lu_fld_target *target;
+
+ LASSERT(fld->lcf_hash != NULL);
+
+ spin_lock(&fld->lcf_lock);
+ target = fld->lcf_hash->fh_scan_func(fld, seq);
+ spin_unlock(&fld->lcf_lock);
+
+ if (target != NULL) {
+ CDEBUG(D_INFO, "%s: Found target (idx %llu) by seq %#llx\n",
+ fld->lcf_name, target->ft_idx, seq);
+ }
+
+ return target;
+}
+
+/*
+ * Add export to FLD. This is usually done by CMM and LMV as they are main users
+ * of FLD module.
+ */
+int fld_client_add_target(struct lu_client_fld *fld,
+ struct lu_fld_target *tar)
+{
+ const char *name;
+ struct lu_fld_target *target, *tmp;
+
+ LASSERT(tar != NULL);
+ name = fld_target_name(tar);
+ LASSERT(name != NULL);
+ LASSERT(tar->ft_srv != NULL || tar->ft_exp != NULL);
+
+ if (fld->lcf_flags != LUSTRE_FLD_INIT) {
+ CERROR("%s: Attempt to add target %s (idx %llu) on fly - skip it\n",
+ fld->lcf_name, name, tar->ft_idx);
+ return 0;
+ }
+ CDEBUG(D_INFO, "%s: Adding target %s (idx %llu)\n",
+ fld->lcf_name, name, tar->ft_idx);
+
+ OBD_ALLOC_PTR(target);
+ if (target == NULL)
+ return -ENOMEM;
+
+ spin_lock(&fld->lcf_lock);
+ list_for_each_entry(tmp, &fld->lcf_targets, ft_chain) {
+ if (tmp->ft_idx == tar->ft_idx) {
+ spin_unlock(&fld->lcf_lock);
+ OBD_FREE_PTR(target);
+ CERROR("Target %s exists in FLD and known as %s:#%llu\n",
+ name, fld_target_name(tmp), tmp->ft_idx);
+ return -EEXIST;
+ }
+ }
+
+ target->ft_exp = tar->ft_exp;
+ if (target->ft_exp != NULL)
+ class_export_get(target->ft_exp);
+ target->ft_srv = tar->ft_srv;
+ target->ft_idx = tar->ft_idx;
+
+ list_add_tail(&target->ft_chain,
+ &fld->lcf_targets);
+
+ fld->lcf_count++;
+ spin_unlock(&fld->lcf_lock);
+
+ return 0;
+}
+EXPORT_SYMBOL(fld_client_add_target);
+
+/* Remove export from FLD */
+int fld_client_del_target(struct lu_client_fld *fld, __u64 idx)
+{
+ struct lu_fld_target *target, *tmp;
+
+ spin_lock(&fld->lcf_lock);
+ list_for_each_entry_safe(target, tmp,
+ &fld->lcf_targets, ft_chain) {
+ if (target->ft_idx == idx) {
+ fld->lcf_count--;
+ list_del(&target->ft_chain);
+ spin_unlock(&fld->lcf_lock);
+
+ if (target->ft_exp != NULL)
+ class_export_put(target->ft_exp);
+
+ OBD_FREE_PTR(target);
+ return 0;
+ }
+ }
+ spin_unlock(&fld->lcf_lock);
+ return -ENOENT;
+}
+EXPORT_SYMBOL(fld_client_del_target);
+
+static struct proc_dir_entry *fld_type_proc_dir;
+
+#if defined(CONFIG_PROC_FS)
+static int fld_client_proc_init(struct lu_client_fld *fld)
+{
+ int rc;
+
+ fld->lcf_proc_dir = lprocfs_register(fld->lcf_name,
+ fld_type_proc_dir,
+ NULL, NULL);
+
+ if (IS_ERR(fld->lcf_proc_dir)) {
+ CERROR("%s: LProcFS failed in fld-init\n",
+ fld->lcf_name);
+ rc = PTR_ERR(fld->lcf_proc_dir);
+ return rc;
+ }
+
+ rc = lprocfs_add_vars(fld->lcf_proc_dir,
+ fld_client_proc_list, fld);
+ if (rc) {
+ CERROR("%s: Can't init FLD proc, rc %d\n",
+ fld->lcf_name, rc);
+ goto out_cleanup;
+ }
+
+ return 0;
+
+out_cleanup:
+ fld_client_proc_fini(fld);
+ return rc;
+}
+
+void fld_client_proc_fini(struct lu_client_fld *fld)
+{
+ if (fld->lcf_proc_dir) {
+ if (!IS_ERR(fld->lcf_proc_dir))
+ lprocfs_remove(&fld->lcf_proc_dir);
+ fld->lcf_proc_dir = NULL;
+ }
+}
+#else
+static int fld_client_proc_init(struct lu_client_fld *fld)
+{
+ return 0;
+}
+
+void fld_client_proc_fini(struct lu_client_fld *fld)
+{
+}
+#endif
+EXPORT_SYMBOL(fld_client_proc_fini);
+
+static inline int hash_is_sane(int hash)
+{
+ return (hash >= 0 && hash < ARRAY_SIZE(fld_hash));
+}
+
+int fld_client_init(struct lu_client_fld *fld,
+ const char *prefix, int hash)
+{
+ int cache_size, cache_threshold;
+ int rc;
+
+ LASSERT(fld != NULL);
+
+ snprintf(fld->lcf_name, sizeof(fld->lcf_name),
+ "cli-%s", prefix);
+
+ if (!hash_is_sane(hash)) {
+ CERROR("%s: Wrong hash function %#x\n",
+ fld->lcf_name, hash);
+ return -EINVAL;
+ }
+
+ fld->lcf_count = 0;
+ spin_lock_init(&fld->lcf_lock);
+ fld->lcf_hash = &fld_hash[hash];
+ fld->lcf_flags = LUSTRE_FLD_INIT;
+ INIT_LIST_HEAD(&fld->lcf_targets);
+
+ cache_size = FLD_CLIENT_CACHE_SIZE /
+ sizeof(struct fld_cache_entry);
+
+ cache_threshold = cache_size *
+ FLD_CLIENT_CACHE_THRESHOLD / 100;
+
+ fld->lcf_cache = fld_cache_init(fld->lcf_name,
+ cache_size, cache_threshold);
+ if (IS_ERR(fld->lcf_cache)) {
+ rc = PTR_ERR(fld->lcf_cache);
+ fld->lcf_cache = NULL;
+ goto out;
+ }
+
+ rc = fld_client_proc_init(fld);
+ if (rc)
+ goto out;
+out:
+ if (rc)
+ fld_client_fini(fld);
+ else
+ CDEBUG(D_INFO, "%s: Using \"%s\" hash\n",
+ fld->lcf_name, fld->lcf_hash->fh_name);
+ return rc;
+}
+EXPORT_SYMBOL(fld_client_init);
+
+void fld_client_fini(struct lu_client_fld *fld)
+{
+ struct lu_fld_target *target, *tmp;
+
+ spin_lock(&fld->lcf_lock);
+ list_for_each_entry_safe(target, tmp,
+ &fld->lcf_targets, ft_chain) {
+ fld->lcf_count--;
+ list_del(&target->ft_chain);
+ if (target->ft_exp != NULL)
+ class_export_put(target->ft_exp);
+ OBD_FREE_PTR(target);
+ }
+ spin_unlock(&fld->lcf_lock);
+
+ if (fld->lcf_cache != NULL) {
+ if (!IS_ERR(fld->lcf_cache))
+ fld_cache_fini(fld->lcf_cache);
+ fld->lcf_cache = NULL;
+ }
+}
+EXPORT_SYMBOL(fld_client_fini);
+
+int fld_client_rpc(struct obd_export *exp,
+ struct lu_seq_range *range, __u32 fld_op)
+{
+ struct ptlrpc_request *req;
+ struct lu_seq_range *prange;
+ __u32 *op;
+ int rc;
+ struct obd_import *imp;
+
+ LASSERT(exp != NULL);
+
+ imp = class_exp2cliimp(exp);
+ req = ptlrpc_request_alloc_pack(imp, &RQF_FLD_QUERY, LUSTRE_MDS_VERSION,
+ FLD_QUERY);
+ if (req == NULL)
+ return -ENOMEM;
+
+ op = req_capsule_client_get(&req->rq_pill, &RMF_FLD_OPC);
+ *op = fld_op;
+
+ prange = req_capsule_client_get(&req->rq_pill, &RMF_FLD_MDFLD);
+ *prange = *range;
+
+ ptlrpc_request_set_replen(req);
+ req->rq_request_portal = FLD_REQUEST_PORTAL;
+ req->rq_reply_portal = MDC_REPLY_PORTAL;
+ ptlrpc_at_set_req_timeout(req);
+
+ if (fld_op == FLD_LOOKUP &&
+ imp->imp_connect_flags_orig & OBD_CONNECT_MDS_MDS)
+ req->rq_allow_replay = 1;
+
+ if (fld_op != FLD_LOOKUP)
+ mdc_get_rpc_lock(exp->exp_obd->u.cli.cl_rpc_lock, NULL);
+ fld_enter_request(&exp->exp_obd->u.cli);
+ rc = ptlrpc_queue_wait(req);
+ fld_exit_request(&exp->exp_obd->u.cli);
+ if (fld_op != FLD_LOOKUP)
+ mdc_put_rpc_lock(exp->exp_obd->u.cli.cl_rpc_lock, NULL);
+ if (rc)
+ goto out_req;
+
+ prange = req_capsule_server_get(&req->rq_pill, &RMF_FLD_MDFLD);
+ if (prange == NULL) {
+ rc = -EFAULT;
+ goto out_req;
+ }
+ *range = *prange;
+out_req:
+ ptlrpc_req_finished(req);
+ return rc;
+}
+
+int fld_client_lookup(struct lu_client_fld *fld, u64 seq, u32 *mds,
+ __u32 flags, const struct lu_env *env)
+{
+ struct lu_seq_range res = { 0 };
+ struct lu_fld_target *target;
+ int rc;
+
+ fld->lcf_flags |= LUSTRE_FLD_RUN;
+
+ rc = fld_cache_lookup(fld->lcf_cache, seq, &res);
+ if (rc == 0) {
+ *mds = res.lsr_index;
+ return 0;
+ }
+
+ /* Can not find it in the cache */
+ target = fld_client_get_target(fld, seq);
+ LASSERT(target != NULL);
+
+ CDEBUG(D_INFO, "%s: Lookup fld entry (seq: %#llx) on target %s (idx %llu)\n",
+ fld->lcf_name, seq, fld_target_name(target), target->ft_idx);
+
+ res.lsr_start = seq;
+ fld_range_set_type(&res, flags);
+ rc = fld_client_rpc(target->ft_exp, &res, FLD_LOOKUP);
+
+ if (rc == 0) {
+ *mds = res.lsr_index;
+
+ fld_cache_insert(fld->lcf_cache, &res);
+ }
+ return rc;
+}
+EXPORT_SYMBOL(fld_client_lookup);
+
+void fld_client_flush(struct lu_client_fld *fld)
+{
+ fld_cache_flush(fld->lcf_cache);
+}
+EXPORT_SYMBOL(fld_client_flush);
+
+static int __init fld_mod_init(void)
+{
+ fld_type_proc_dir = lprocfs_register(LUSTRE_FLD_NAME,
+ proc_lustre_root,
+ NULL, NULL);
+ return PTR_ERR_OR_ZERO(fld_type_proc_dir);
+}
+
+static void __exit fld_mod_exit(void)
+{
+ if (fld_type_proc_dir != NULL && !IS_ERR(fld_type_proc_dir)) {
+ lprocfs_remove(&fld_type_proc_dir);
+ fld_type_proc_dir = NULL;
+ }
+}
+
+MODULE_AUTHOR("Sun Microsystems, Inc. <http://www.lustre.org/>");
+MODULE_DESCRIPTION("Lustre FLD");
+MODULE_LICENSE("GPL");
+
+module_init(fld_mod_init)
+module_exit(fld_mod_exit)
diff --git a/drivers/staging/lustre/lustre/fld/lproc_fld.c b/drivers/staging/lustre/lustre/fld/lproc_fld.c
new file mode 100644
index 000000000..f53fdcfae
--- /dev/null
+++ b/drivers/staging/lustre/lustre/fld/lproc_fld.c
@@ -0,0 +1,172 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2012, 2013, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ *
+ * lustre/fld/lproc_fld.c
+ *
+ * FLD (FIDs Location Database)
+ *
+ * Author: Yury Umanets <umka@clusterfs.com>
+ * Di Wang <di.wang@whamcloud.com>
+ */
+
+#define DEBUG_SUBSYSTEM S_FLD
+
+#include "../../include/linux/libcfs/libcfs.h"
+#include <linux/module.h>
+
+#include "../include/obd.h"
+#include "../include/obd_class.h"
+#include "../include/dt_object.h"
+#include "../include/obd_support.h"
+#include "../include/lustre_req_layout.h"
+#include "../include/lustre_fld.h"
+#include "../include/lustre_fid.h"
+#include "fld_internal.h"
+
+static int
+fld_proc_targets_seq_show(struct seq_file *m, void *unused)
+{
+ struct lu_client_fld *fld = (struct lu_client_fld *)m->private;
+ struct lu_fld_target *target;
+
+ LASSERT(fld != NULL);
+
+ spin_lock(&fld->lcf_lock);
+ list_for_each_entry(target,
+ &fld->lcf_targets, ft_chain)
+ seq_printf(m, "%s\n", fld_target_name(target));
+ spin_unlock(&fld->lcf_lock);
+
+ return 0;
+}
+
+static int
+fld_proc_hash_seq_show(struct seq_file *m, void *unused)
+{
+ struct lu_client_fld *fld = (struct lu_client_fld *)m->private;
+
+ LASSERT(fld != NULL);
+
+ spin_lock(&fld->lcf_lock);
+ seq_printf(m, "%s\n", fld->lcf_hash->fh_name);
+ spin_unlock(&fld->lcf_lock);
+
+ return 0;
+}
+
+static ssize_t
+fld_proc_hash_seq_write(struct file *file,
+ const char __user *buffer,
+ size_t count, loff_t *off)
+{
+ struct lu_client_fld *fld;
+ struct lu_fld_hash *hash = NULL;
+ char fh_name[8];
+ int i;
+
+ if (count > sizeof(fh_name))
+ return -ENAMETOOLONG;
+
+ if (copy_from_user(fh_name, buffer, count) != 0)
+ return -EFAULT;
+
+ fld = ((struct seq_file *)file->private_data)->private;
+ LASSERT(fld != NULL);
+
+ for (i = 0; fld_hash[i].fh_name != NULL; i++) {
+ if (count != strlen(fld_hash[i].fh_name))
+ continue;
+
+ if (!strncmp(fld_hash[i].fh_name, fh_name, count)) {
+ hash = &fld_hash[i];
+ break;
+ }
+ }
+
+ if (hash != NULL) {
+ spin_lock(&fld->lcf_lock);
+ fld->lcf_hash = hash;
+ spin_unlock(&fld->lcf_lock);
+
+ CDEBUG(D_INFO, "%s: Changed hash to \"%s\"\n",
+ fld->lcf_name, hash->fh_name);
+ }
+
+ return count;
+}
+
+static ssize_t
+fld_proc_cache_flush_write(struct file *file, const char __user *buffer,
+ size_t count, loff_t *pos)
+{
+ struct lu_client_fld *fld = file->private_data;
+
+ LASSERT(fld != NULL);
+
+ fld_cache_flush(fld->lcf_cache);
+
+ CDEBUG(D_INFO, "%s: Lookup cache is flushed\n", fld->lcf_name);
+
+ return count;
+}
+
+static int fld_proc_cache_flush_open(struct inode *inode, struct file *file)
+{
+ file->private_data = PDE_DATA(inode);
+ return 0;
+}
+
+static int fld_proc_cache_flush_release(struct inode *inode, struct file *file)
+{
+ file->private_data = NULL;
+ return 0;
+}
+
+static struct file_operations fld_proc_cache_flush_fops = {
+ .owner = THIS_MODULE,
+ .open = fld_proc_cache_flush_open,
+ .write = fld_proc_cache_flush_write,
+ .release = fld_proc_cache_flush_release,
+};
+
+LPROC_SEQ_FOPS_RO(fld_proc_targets);
+LPROC_SEQ_FOPS(fld_proc_hash);
+
+struct lprocfs_vars fld_client_proc_list[] = {
+ { "targets", &fld_proc_targets_fops },
+ { "hash", &fld_proc_hash_fops },
+ { "cache_flush", &fld_proc_cache_flush_fops },
+ { NULL }
+};
diff --git a/drivers/staging/lustre/lustre/include/cl_object.h b/drivers/staging/lustre/lustre/include/cl_object.h
new file mode 100644
index 000000000..d56c8bea8
--- /dev/null
+++ b/drivers/staging/lustre/lustre/include/cl_object.h
@@ -0,0 +1,3287 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2011, 2012, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ */
+#ifndef _LUSTRE_CL_OBJECT_H
+#define _LUSTRE_CL_OBJECT_H
+
+/** \defgroup clio clio
+ *
+ * Client objects implement io operations and cache pages.
+ *
+ * Examples: lov and osc are implementations of cl interface.
+ *
+ * Big Theory Statement.
+ *
+ * Layered objects.
+ *
+ * Client implementation is based on the following data-types:
+ *
+ * - cl_object
+ *
+ * - cl_page
+ *
+ * - cl_lock represents an extent lock on an object.
+ *
+ * - cl_io represents high-level i/o activity such as whole read/write
+ * system call, or write-out of pages from under the lock being
+ * canceled. cl_io has sub-ios that can be stopped and resumed
+ * independently, thus achieving high degree of transfer
+ * parallelism. Single cl_io can be advanced forward by
+ * the multiple threads (although in the most usual case of
+ * read/write system call it is associated with the single user
+ * thread, that issued the system call).
+ *
+ * - cl_req represents a collection of pages for a transfer. cl_req is
+ * constructed by req-forming engine that tries to saturate
+ * transport with large and continuous transfers.
+ *
+ * Terminology
+ *
+ * - to avoid confusion high-level I/O operation like read or write system
+ * call is referred to as "an io", whereas low-level I/O operation, like
+ * RPC, is referred to as "a transfer"
+ *
+ * - "generic code" means generic (not file system specific) code in the
+ * hosting environment. "cl-code" means code (mostly in cl_*.c files) that
+ * is not layer specific.
+ *
+ * Locking.
+ *
+ * - i_mutex
+ * - PG_locked
+ * - cl_object_header::coh_page_guard
+ * - cl_object_header::coh_lock_guard
+ * - lu_site::ls_guard
+ *
+ * See the top comment in cl_object.c for the description of overall locking and
+ * reference-counting design.
+ *
+ * See comments below for the description of i/o, page, and dlm-locking
+ * design.
+ *
+ * @{
+ */
+
+/*
+ * super-class definitions.
+ */
+#include "lu_object.h"
+#include "linux/lustre_compat25.h"
+#include <linux/mutex.h>
+#include <linux/radix-tree.h>
+
+struct inode;
+
+struct cl_device;
+struct cl_device_operations;
+
+struct cl_object;
+struct cl_object_page_operations;
+struct cl_object_lock_operations;
+
+struct cl_page;
+struct cl_page_slice;
+struct cl_lock;
+struct cl_lock_slice;
+
+struct cl_lock_operations;
+struct cl_page_operations;
+
+struct cl_io;
+struct cl_io_slice;
+
+struct cl_req;
+struct cl_req_slice;
+
+/**
+ * Operations for each data device in the client stack.
+ *
+ * \see vvp_cl_ops, lov_cl_ops, lovsub_cl_ops, osc_cl_ops
+ */
+struct cl_device_operations {
+ /**
+ * Initialize cl_req. This method is called top-to-bottom on all
+ * devices in the stack to get them a chance to allocate layer-private
+ * data, and to attach them to the cl_req by calling
+ * cl_req_slice_add().
+ *
+ * \see osc_req_init(), lov_req_init(), lovsub_req_init()
+ * \see ccc_req_init()
+ */
+ int (*cdo_req_init)(const struct lu_env *env, struct cl_device *dev,
+ struct cl_req *req);
+};
+
+/**
+ * Device in the client stack.
+ *
+ * \see ccc_device, lov_device, lovsub_device, osc_device
+ */
+struct cl_device {
+ /** Super-class. */
+ struct lu_device cd_lu_dev;
+ /** Per-layer operation vector. */
+ const struct cl_device_operations *cd_ops;
+};
+
+/** \addtogroup cl_object cl_object
+ * @{ */
+/**
+ * "Data attributes" of cl_object. Data attributes can be updated
+ * independently for a sub-object, and top-object's attributes are calculated
+ * from sub-objects' ones.
+ */
+struct cl_attr {
+ /** Object size, in bytes */
+ loff_t cat_size;
+ /**
+ * Known minimal size, in bytes.
+ *
+ * This is only valid when at least one DLM lock is held.
+ */
+ loff_t cat_kms;
+ /** Modification time. Measured in seconds since epoch. */
+ time_t cat_mtime;
+ /** Access time. Measured in seconds since epoch. */
+ time_t cat_atime;
+ /** Change time. Measured in seconds since epoch. */
+ time_t cat_ctime;
+ /**
+ * Blocks allocated to this cl_object on the server file system.
+ *
+ * \todo XXX An interface for block size is needed.
+ */
+ __u64 cat_blocks;
+ /**
+ * User identifier for quota purposes.
+ */
+ uid_t cat_uid;
+ /**
+ * Group identifier for quota purposes.
+ */
+ gid_t cat_gid;
+};
+
+/**
+ * Fields in cl_attr that are being set.
+ */
+enum cl_attr_valid {
+ CAT_SIZE = 1 << 0,
+ CAT_KMS = 1 << 1,
+ CAT_MTIME = 1 << 3,
+ CAT_ATIME = 1 << 4,
+ CAT_CTIME = 1 << 5,
+ CAT_BLOCKS = 1 << 6,
+ CAT_UID = 1 << 7,
+ CAT_GID = 1 << 8
+};
+
+/**
+ * Sub-class of lu_object with methods common for objects on the client
+ * stacks.
+ *
+ * cl_object: represents a regular file system object, both a file and a
+ * stripe. cl_object is based on lu_object: it is identified by a fid,
+ * layered, cached, hashed, and lrued. Important distinction with the server
+ * side, where md_object and dt_object are used, is that cl_object "fans out"
+ * at the lov/sns level: depending on the file layout, single file is
+ * represented as a set of "sub-objects" (stripes). At the implementation
+ * level, struct lov_object contains an array of cl_objects. Each sub-object
+ * is a full-fledged cl_object, having its fid, living in the lru and hash
+ * table.
+ *
+ * This leads to the next important difference with the server side: on the
+ * client, it's quite usual to have objects with the different sequence of
+ * layers. For example, typical top-object is composed of the following
+ * layers:
+ *
+ * - vvp
+ * - lov
+ *
+ * whereas its sub-objects are composed of
+ *
+ * - lovsub
+ * - osc
+ *
+ * layers. Here "lovsub" is a mostly dummy layer, whose purpose is to keep
+ * track of the object-subobject relationship.
+ *
+ * Sub-objects are not cached independently: when top-object is about to
+ * be discarded from the memory, all its sub-objects are torn-down and
+ * destroyed too.
+ *
+ * \see ccc_object, lov_object, lovsub_object, osc_object
+ */
+struct cl_object {
+ /** super class */
+ struct lu_object co_lu;
+ /** per-object-layer operations */
+ const struct cl_object_operations *co_ops;
+ /** offset of page slice in cl_page buffer */
+ int co_slice_off;
+};
+
+/**
+ * Description of the client object configuration. This is used for the
+ * creation of a new client object that is identified by a more state than
+ * fid.
+ */
+struct cl_object_conf {
+ /** Super-class. */
+ struct lu_object_conf coc_lu;
+ union {
+ /**
+ * Object layout. This is consumed by lov.
+ */
+ struct lustre_md *coc_md;
+ /**
+ * Description of particular stripe location in the
+ * cluster. This is consumed by osc.
+ */
+ struct lov_oinfo *coc_oinfo;
+ } u;
+ /**
+ * VFS inode. This is consumed by vvp.
+ */
+ struct inode *coc_inode;
+ /**
+ * Layout lock handle.
+ */
+ struct ldlm_lock *coc_lock;
+ /**
+ * Operation to handle layout, OBJECT_CONF_XYZ.
+ */
+ int coc_opc;
+};
+
+enum {
+ /** configure layout, set up a new stripe, must be called while
+ * holding layout lock. */
+ OBJECT_CONF_SET = 0,
+ /** invalidate the current stripe configuration due to losing
+ * layout lock. */
+ OBJECT_CONF_INVALIDATE = 1,
+ /** wait for old layout to go away so that new layout can be
+ * set up. */
+ OBJECT_CONF_WAIT = 2
+};
+
+/**
+ * Operations implemented for each cl object layer.
+ *
+ * \see vvp_ops, lov_ops, lovsub_ops, osc_ops
+ */
+struct cl_object_operations {
+ /**
+ * Initialize page slice for this layer. Called top-to-bottom through
+ * every object layer when a new cl_page is instantiated. Layer
+ * keeping private per-page data, or requiring its own page operations
+ * vector should allocate these data here, and attach then to the page
+ * by calling cl_page_slice_add(). \a vmpage is locked (in the VM
+ * sense). Optional.
+ *
+ * \retval NULL success.
+ *
+ * \retval ERR_PTR(errno) failure code.
+ *
+ * \retval valid-pointer pointer to already existing referenced page
+ * to be used instead of newly created.
+ */
+ int (*coo_page_init)(const struct lu_env *env, struct cl_object *obj,
+ struct cl_page *page, struct page *vmpage);
+ /**
+ * Initialize lock slice for this layer. Called top-to-bottom through
+ * every object layer when a new cl_lock is instantiated. Layer
+ * keeping private per-lock data, or requiring its own lock operations
+ * vector should allocate these data here, and attach then to the lock
+ * by calling cl_lock_slice_add(). Mandatory.
+ */
+ int (*coo_lock_init)(const struct lu_env *env,
+ struct cl_object *obj, struct cl_lock *lock,
+ const struct cl_io *io);
+ /**
+ * Initialize io state for a given layer.
+ *
+ * called top-to-bottom once per io existence to initialize io
+ * state. If layer wants to keep some state for this type of io, it
+ * has to embed struct cl_io_slice in lu_env::le_ses, and register
+ * slice with cl_io_slice_add(). It is guaranteed that all threads
+ * participating in this io share the same session.
+ */
+ int (*coo_io_init)(const struct lu_env *env,
+ struct cl_object *obj, struct cl_io *io);
+ /**
+ * Fill portion of \a attr that this layer controls. This method is
+ * called top-to-bottom through all object layers.
+ *
+ * \pre cl_object_header::coh_attr_guard of the top-object is locked.
+ *
+ * \return 0: to continue
+ * \return +ve: to stop iterating through layers (but 0 is returned
+ * from enclosing cl_object_attr_get())
+ * \return -ve: to signal error
+ */
+ int (*coo_attr_get)(const struct lu_env *env, struct cl_object *obj,
+ struct cl_attr *attr);
+ /**
+ * Update attributes.
+ *
+ * \a valid is a bitmask composed from enum #cl_attr_valid, and
+ * indicating what attributes are to be set.
+ *
+ * \pre cl_object_header::coh_attr_guard of the top-object is locked.
+ *
+ * \return the same convention as for
+ * cl_object_operations::coo_attr_get() is used.
+ */
+ int (*coo_attr_set)(const struct lu_env *env, struct cl_object *obj,
+ const struct cl_attr *attr, unsigned valid);
+ /**
+ * Update object configuration. Called top-to-bottom to modify object
+ * configuration.
+ *
+ * XXX error conditions and handling.
+ */
+ int (*coo_conf_set)(const struct lu_env *env, struct cl_object *obj,
+ const struct cl_object_conf *conf);
+ /**
+ * Glimpse ast. Executed when glimpse ast arrives for a lock on this
+ * object. Layers are supposed to fill parts of \a lvb that will be
+ * shipped to the glimpse originator as a glimpse result.
+ *
+ * \see ccc_object_glimpse(), lovsub_object_glimpse(),
+ * \see osc_object_glimpse()
+ */
+ int (*coo_glimpse)(const struct lu_env *env,
+ const struct cl_object *obj, struct ost_lvb *lvb);
+};
+
+/**
+ * Extended header for client object.
+ */
+struct cl_object_header {
+ /** Standard lu_object_header. cl_object::co_lu::lo_header points
+ * here. */
+ struct lu_object_header coh_lu;
+ /** \name locks
+ * \todo XXX move locks below to the separate cache-lines, they are
+ * mostly useless otherwise.
+ */
+ /** @{ */
+ /** Lock protecting page tree. */
+ spinlock_t coh_page_guard;
+ /** Lock protecting lock list. */
+ spinlock_t coh_lock_guard;
+ /** @} locks */
+ /** Radix tree of cl_page's, cached for this object. */
+ struct radix_tree_root coh_tree;
+ /** # of pages in radix tree. */
+ unsigned long coh_pages;
+ /** List of cl_lock's granted for this object. */
+ struct list_head coh_locks;
+
+ /**
+ * Parent object. It is assumed that an object has a well-defined
+ * parent, but not a well-defined child (there may be multiple
+ * sub-objects, for the same top-object). cl_object_header::coh_parent
+ * field allows certain code to be written generically, without
+ * limiting possible cl_object layouts unduly.
+ */
+ struct cl_object_header *coh_parent;
+ /**
+ * Protects consistency between cl_attr of parent object and
+ * attributes of sub-objects, that the former is calculated ("merged")
+ * from.
+ *
+ * \todo XXX this can be read/write lock if needed.
+ */
+ spinlock_t coh_attr_guard;
+ /**
+ * Size of cl_page + page slices
+ */
+ unsigned short coh_page_bufsize;
+ /**
+ * Number of objects above this one: 0 for a top-object, 1 for its
+ * sub-object, etc.
+ */
+ unsigned char coh_nesting;
+};
+
+/**
+ * Helper macro: iterate over all layers of the object \a obj, assigning every
+ * layer top-to-bottom to \a slice.
+ */
+#define cl_object_for_each(slice, obj) \
+ list_for_each_entry((slice), \
+ &(obj)->co_lu.lo_header->loh_layers, \
+ co_lu.lo_linkage)
+/**
+ * Helper macro: iterate over all layers of the object \a obj, assigning every
+ * layer bottom-to-top to \a slice.
+ */
+#define cl_object_for_each_reverse(slice, obj) \
+ list_for_each_entry_reverse((slice), \
+ &(obj)->co_lu.lo_header->loh_layers, \
+ co_lu.lo_linkage)
+/** @} cl_object */
+
+#ifndef pgoff_t
+#define pgoff_t unsigned long
+#endif
+
+#define CL_PAGE_EOF ((pgoff_t)~0ull)
+
+/** \addtogroup cl_page cl_page
+ * @{ */
+
+/** \struct cl_page
+ * Layered client page.
+ *
+ * cl_page: represents a portion of a file, cached in the memory. All pages
+ * of the given file are of the same size, and are kept in the radix tree
+ * hanging off the cl_object. cl_page doesn't fan out, but as sub-objects
+ * of the top-level file object are first class cl_objects, they have their
+ * own radix trees of pages and hence page is implemented as a sequence of
+ * struct cl_pages's, linked into double-linked list through
+ * cl_page::cp_parent and cl_page::cp_child pointers, each residing in the
+ * corresponding radix tree at the corresponding logical offset.
+ *
+ * cl_page is associated with VM page of the hosting environment (struct
+ * page in Linux kernel, for example), struct page. It is assumed, that this
+ * association is implemented by one of cl_page layers (top layer in the
+ * current design) that
+ *
+ * - intercepts per-VM-page call-backs made by the environment (e.g.,
+ * memory pressure),
+ *
+ * - translates state (page flag bits) and locking between lustre and
+ * environment.
+ *
+ * The association between cl_page and struct page is immutable and
+ * established when cl_page is created.
+ *
+ * cl_page can be "owned" by a particular cl_io (see below), guaranteeing
+ * this io an exclusive access to this page w.r.t. other io attempts and
+ * various events changing page state (such as transfer completion, or
+ * eviction of the page from the memory). Note, that in general cl_io
+ * cannot be identified with a particular thread, and page ownership is not
+ * exactly equal to the current thread holding a lock on the page. Layer
+ * implementing association between cl_page and struct page has to implement
+ * ownership on top of available synchronization mechanisms.
+ *
+ * While lustre client maintains the notion of an page ownership by io,
+ * hosting MM/VM usually has its own page concurrency control
+ * mechanisms. For example, in Linux, page access is synchronized by the
+ * per-page PG_locked bit-lock, and generic kernel code (generic_file_*())
+ * takes care to acquire and release such locks as necessary around the
+ * calls to the file system methods (->readpage(), ->prepare_write(),
+ * ->commit_write(), etc.). This leads to the situation when there are two
+ * different ways to own a page in the client:
+ *
+ * - client code explicitly and voluntary owns the page (cl_page_own());
+ *
+ * - VM locks a page and then calls the client, that has "to assume"
+ * the ownership from the VM (cl_page_assume()).
+ *
+ * Dual methods to release ownership are cl_page_disown() and
+ * cl_page_unassume().
+ *
+ * cl_page is reference counted (cl_page::cp_ref). When reference counter
+ * drops to 0, the page is returned to the cache, unless it is in
+ * cl_page_state::CPS_FREEING state, in which case it is immediately
+ * destroyed.
+ *
+ * The general logic guaranteeing the absence of "existential races" for
+ * pages is the following:
+ *
+ * - there are fixed known ways for a thread to obtain a new reference
+ * to a page:
+ *
+ * - by doing a lookup in the cl_object radix tree, protected by the
+ * spin-lock;
+ *
+ * - by starting from VM-locked struct page and following some
+ * hosting environment method (e.g., following ->private pointer in
+ * the case of Linux kernel), see cl_vmpage_page();
+ *
+ * - when the page enters cl_page_state::CPS_FREEING state, all these
+ * ways are severed with the proper synchronization
+ * (cl_page_delete());
+ *
+ * - entry into cl_page_state::CPS_FREEING is serialized by the VM page
+ * lock;
+ *
+ * - no new references to the page in cl_page_state::CPS_FREEING state
+ * are allowed (checked in cl_page_get()).
+ *
+ * Together this guarantees that when last reference to a
+ * cl_page_state::CPS_FREEING page is released, it is safe to destroy the
+ * page, as neither references to it can be acquired at that point, nor
+ * ones exist.
+ *
+ * cl_page is a state machine. States are enumerated in enum
+ * cl_page_state. Possible state transitions are enumerated in
+ * cl_page_state_set(). State transition process (i.e., actual changing of
+ * cl_page::cp_state field) is protected by the lock on the underlying VM
+ * page.
+ *
+ * Linux Kernel implementation.
+ *
+ * Binding between cl_page and struct page (which is a typedef for
+ * struct page) is implemented in the vvp layer. cl_page is attached to the
+ * ->private pointer of the struct page, together with the setting of
+ * PG_private bit in page->flags, and acquiring additional reference on the
+ * struct page (much like struct buffer_head, or any similar file system
+ * private data structures).
+ *
+ * PG_locked lock is used to implement both ownership and transfer
+ * synchronization, that is, page is VM-locked in CPS_{OWNED,PAGE{IN,OUT}}
+ * states. No additional references are acquired for the duration of the
+ * transfer.
+ *
+ * \warning *THIS IS NOT* the behavior expected by the Linux kernel, where
+ * write-out is "protected" by the special PG_writeback bit.
+ */
+
+/**
+ * States of cl_page. cl_page.c assumes particular order here.
+ *
+ * The page state machine is rather crude, as it doesn't recognize finer page
+ * states like "dirty" or "up to date". This is because such states are not
+ * always well defined for the whole stack (see, for example, the
+ * implementation of the read-ahead, that hides page up-to-dateness to track
+ * cache hits accurately). Such sub-states are maintained by the layers that
+ * are interested in them.
+ */
+enum cl_page_state {
+ /**
+ * Page is in the cache, un-owned. Page leaves cached state in the
+ * following cases:
+ *
+ * - [cl_page_state::CPS_OWNED] io comes across the page and
+ * owns it;
+ *
+ * - [cl_page_state::CPS_PAGEOUT] page is dirty, the
+ * req-formation engine decides that it wants to include this page
+ * into an cl_req being constructed, and yanks it from the cache;
+ *
+ * - [cl_page_state::CPS_FREEING] VM callback is executed to
+ * evict the page form the memory;
+ *
+ * \invariant cl_page::cp_owner == NULL && cl_page::cp_req == NULL
+ */
+ CPS_CACHED,
+ /**
+ * Page is exclusively owned by some cl_io. Page may end up in this
+ * state as a result of
+ *
+ * - io creating new page and immediately owning it;
+ *
+ * - [cl_page_state::CPS_CACHED] io finding existing cached page
+ * and owning it;
+ *
+ * - [cl_page_state::CPS_OWNED] io finding existing owned page
+ * and waiting for owner to release the page;
+ *
+ * Page leaves owned state in the following cases:
+ *
+ * - [cl_page_state::CPS_CACHED] io decides to leave the page in
+ * the cache, doing nothing;
+ *
+ * - [cl_page_state::CPS_PAGEIN] io starts read transfer for
+ * this page;
+ *
+ * - [cl_page_state::CPS_PAGEOUT] io starts immediate write
+ * transfer for this page;
+ *
+ * - [cl_page_state::CPS_FREEING] io decides to destroy this
+ * page (e.g., as part of truncate or extent lock cancellation).
+ *
+ * \invariant cl_page::cp_owner != NULL && cl_page::cp_req == NULL
+ */
+ CPS_OWNED,
+ /**
+ * Page is being written out, as a part of a transfer. This state is
+ * entered when req-formation logic decided that it wants this page to
+ * be sent through the wire _now_. Specifically, it means that once
+ * this state is achieved, transfer completion handler (with either
+ * success or failure indication) is guaranteed to be executed against
+ * this page independently of any locks and any scheduling decisions
+ * made by the hosting environment (that effectively means that the
+ * page is never put into cl_page_state::CPS_PAGEOUT state "in
+ * advance". This property is mentioned, because it is important when
+ * reasoning about possible dead-locks in the system). The page can
+ * enter this state as a result of
+ *
+ * - [cl_page_state::CPS_OWNED] an io requesting an immediate
+ * write-out of this page, or
+ *
+ * - [cl_page_state::CPS_CACHED] req-forming engine deciding
+ * that it has enough dirty pages cached to issue a "good"
+ * transfer.
+ *
+ * The page leaves cl_page_state::CPS_PAGEOUT state when the transfer
+ * is completed---it is moved into cl_page_state::CPS_CACHED state.
+ *
+ * Underlying VM page is locked for the duration of transfer.
+ *
+ * \invariant: cl_page::cp_owner == NULL && cl_page::cp_req != NULL
+ */
+ CPS_PAGEOUT,
+ /**
+ * Page is being read in, as a part of a transfer. This is quite
+ * similar to the cl_page_state::CPS_PAGEOUT state, except that
+ * read-in is always "immediate"---there is no such thing a sudden
+ * construction of read cl_req from cached, presumably not up to date,
+ * pages.
+ *
+ * Underlying VM page is locked for the duration of transfer.
+ *
+ * \invariant: cl_page::cp_owner == NULL && cl_page::cp_req != NULL
+ */
+ CPS_PAGEIN,
+ /**
+ * Page is being destroyed. This state is entered when client decides
+ * that page has to be deleted from its host object, as, e.g., a part
+ * of truncate.
+ *
+ * Once this state is reached, there is no way to escape it.
+ *
+ * \invariant: cl_page::cp_owner == NULL && cl_page::cp_req == NULL
+ */
+ CPS_FREEING,
+ CPS_NR
+};
+
+enum cl_page_type {
+ /** Host page, the page is from the host inode which the cl_page
+ * belongs to. */
+ CPT_CACHEABLE = 1,
+
+ /** Transient page, the transient cl_page is used to bind a cl_page
+ * to vmpage which is not belonging to the same object of cl_page.
+ * it is used in DirectIO, lockless IO and liblustre. */
+ CPT_TRANSIENT,
+};
+
+/**
+ * Flags maintained for every cl_page.
+ */
+enum cl_page_flags {
+ /**
+ * Set when pagein completes. Used for debugging (read completes at
+ * most once for a page).
+ */
+ CPF_READ_COMPLETED = 1 << 0
+};
+
+/**
+ * Fields are protected by the lock on struct page, except for atomics and
+ * immutables.
+ *
+ * \invariant Data type invariants are in cl_page_invariant(). Basically:
+ * cl_page::cp_parent and cl_page::cp_child are a well-formed double-linked
+ * list, consistent with the parent/child pointers in the cl_page::cp_obj and
+ * cl_page::cp_owner (when set).
+ */
+struct cl_page {
+ /** Reference counter. */
+ atomic_t cp_ref;
+ /** An object this page is a part of. Immutable after creation. */
+ struct cl_object *cp_obj;
+ /** Logical page index within the object. Immutable after creation. */
+ pgoff_t cp_index;
+ /** List of slices. Immutable after creation. */
+ struct list_head cp_layers;
+ /** Parent page, NULL for top-level page. Immutable after creation. */
+ struct cl_page *cp_parent;
+ /** Lower-layer page. NULL for bottommost page. Immutable after
+ * creation. */
+ struct cl_page *cp_child;
+ /**
+ * Page state. This field is const to avoid accidental update, it is
+ * modified only internally within cl_page.c. Protected by a VM lock.
+ */
+ const enum cl_page_state cp_state;
+ /** Linkage of pages within group. Protected by cl_page::cp_mutex. */
+ struct list_head cp_batch;
+ /** Mutex serializing membership of a page in a batch. */
+ struct mutex cp_mutex;
+ /** Linkage of pages within cl_req. */
+ struct list_head cp_flight;
+ /** Transfer error. */
+ int cp_error;
+
+ /**
+ * Page type. Only CPT_TRANSIENT is used so far. Immutable after
+ * creation.
+ */
+ enum cl_page_type cp_type;
+
+ /**
+ * Owning IO in cl_page_state::CPS_OWNED state. Sub-page can be owned
+ * by sub-io. Protected by a VM lock.
+ */
+ struct cl_io *cp_owner;
+ /**
+ * Debug information, the task is owning the page.
+ */
+ struct task_struct *cp_task;
+ /**
+ * Owning IO request in cl_page_state::CPS_PAGEOUT and
+ * cl_page_state::CPS_PAGEIN states. This field is maintained only in
+ * the top-level pages. Protected by a VM lock.
+ */
+ struct cl_req *cp_req;
+ /** List of references to this page, for debugging. */
+ struct lu_ref cp_reference;
+ /** Link to an object, for debugging. */
+ struct lu_ref_link cp_obj_ref;
+ /** Link to a queue, for debugging. */
+ struct lu_ref_link cp_queue_ref;
+ /** Per-page flags from enum cl_page_flags. Protected by a VM lock. */
+ unsigned cp_flags;
+ /** Assigned if doing a sync_io */
+ struct cl_sync_io *cp_sync_io;
+};
+
+/**
+ * Per-layer part of cl_page.
+ *
+ * \see ccc_page, lov_page, osc_page
+ */
+struct cl_page_slice {
+ struct cl_page *cpl_page;
+ /**
+ * Object slice corresponding to this page slice. Immutable after
+ * creation.
+ */
+ struct cl_object *cpl_obj;
+ const struct cl_page_operations *cpl_ops;
+ /** Linkage into cl_page::cp_layers. Immutable after creation. */
+ struct list_head cpl_linkage;
+};
+
+/**
+ * Lock mode. For the client extent locks.
+ *
+ * \warning: cl_lock_mode_match() assumes particular ordering here.
+ * \ingroup cl_lock
+ */
+enum cl_lock_mode {
+ /**
+ * Mode of a lock that protects no data, and exists only as a
+ * placeholder. This is used for `glimpse' requests. A phantom lock
+ * might get promoted to real lock at some point.
+ */
+ CLM_PHANTOM,
+ CLM_READ,
+ CLM_WRITE,
+ CLM_GROUP
+};
+
+/**
+ * Requested transfer type.
+ * \ingroup cl_req
+ */
+enum cl_req_type {
+ CRT_READ,
+ CRT_WRITE,
+ CRT_NR
+};
+
+/**
+ * Per-layer page operations.
+ *
+ * Methods taking an \a io argument are for the activity happening in the
+ * context of given \a io. Page is assumed to be owned by that io, except for
+ * the obvious cases (like cl_page_operations::cpo_own()).
+ *
+ * \see vvp_page_ops, lov_page_ops, osc_page_ops
+ */
+struct cl_page_operations {
+ /**
+ * cl_page<->struct page methods. Only one layer in the stack has to
+ * implement these. Current code assumes that this functionality is
+ * provided by the topmost layer, see cl_page_disown0() as an example.
+ */
+
+ /**
+ * \return the underlying VM page. Optional.
+ */
+ struct page *(*cpo_vmpage)(const struct lu_env *env,
+ const struct cl_page_slice *slice);
+ /**
+ * Called when \a io acquires this page into the exclusive
+ * ownership. When this method returns, it is guaranteed that the is
+ * not owned by other io, and no transfer is going on against
+ * it. Optional.
+ *
+ * \see cl_page_own()
+ * \see vvp_page_own(), lov_page_own()
+ */
+ int (*cpo_own)(const struct lu_env *env,
+ const struct cl_page_slice *slice,
+ struct cl_io *io, int nonblock);
+ /** Called when ownership it yielded. Optional.
+ *
+ * \see cl_page_disown()
+ * \see vvp_page_disown()
+ */
+ void (*cpo_disown)(const struct lu_env *env,
+ const struct cl_page_slice *slice, struct cl_io *io);
+ /**
+ * Called for a page that is already "owned" by \a io from VM point of
+ * view. Optional.
+ *
+ * \see cl_page_assume()
+ * \see vvp_page_assume(), lov_page_assume()
+ */
+ void (*cpo_assume)(const struct lu_env *env,
+ const struct cl_page_slice *slice, struct cl_io *io);
+ /** Dual to cl_page_operations::cpo_assume(). Optional. Called
+ * bottom-to-top when IO releases a page without actually unlocking
+ * it.
+ *
+ * \see cl_page_unassume()
+ * \see vvp_page_unassume()
+ */
+ void (*cpo_unassume)(const struct lu_env *env,
+ const struct cl_page_slice *slice,
+ struct cl_io *io);
+ /**
+ * Announces whether the page contains valid data or not by \a uptodate.
+ *
+ * \see cl_page_export()
+ * \see vvp_page_export()
+ */
+ void (*cpo_export)(const struct lu_env *env,
+ const struct cl_page_slice *slice, int uptodate);
+ /**
+ * Unmaps page from the user space (if it is mapped).
+ *
+ * \see cl_page_unmap()
+ * \see vvp_page_unmap()
+ */
+ int (*cpo_unmap)(const struct lu_env *env,
+ const struct cl_page_slice *slice, struct cl_io *io);
+ /**
+ * Checks whether underlying VM page is locked (in the suitable
+ * sense). Used for assertions.
+ *
+ * \retval -EBUSY: page is protected by a lock of a given mode;
+ * \retval -ENODATA: page is not protected by a lock;
+ * \retval 0: this layer cannot decide. (Should never happen.)
+ */
+ int (*cpo_is_vmlocked)(const struct lu_env *env,
+ const struct cl_page_slice *slice);
+ /**
+ * Page destruction.
+ */
+
+ /**
+ * Called when page is truncated from the object. Optional.
+ *
+ * \see cl_page_discard()
+ * \see vvp_page_discard(), osc_page_discard()
+ */
+ void (*cpo_discard)(const struct lu_env *env,
+ const struct cl_page_slice *slice,
+ struct cl_io *io);
+ /**
+ * Called when page is removed from the cache, and is about to being
+ * destroyed. Optional.
+ *
+ * \see cl_page_delete()
+ * \see vvp_page_delete(), osc_page_delete()
+ */
+ void (*cpo_delete)(const struct lu_env *env,
+ const struct cl_page_slice *slice);
+ /** Destructor. Frees resources and slice itself. */
+ void (*cpo_fini)(const struct lu_env *env,
+ struct cl_page_slice *slice);
+
+ /**
+ * Checks whether the page is protected by a cl_lock. This is a
+ * per-layer method, because certain layers have ways to check for the
+ * lock much more efficiently than through the generic locks scan, or
+ * implement locking mechanisms separate from cl_lock, e.g.,
+ * LL_FILE_GROUP_LOCKED in vvp. If \a pending is true, check for locks
+ * being canceled, or scheduled for cancellation as soon as the last
+ * user goes away, too.
+ *
+ * \retval -EBUSY: page is protected by a lock of a given mode;
+ * \retval -ENODATA: page is not protected by a lock;
+ * \retval 0: this layer cannot decide.
+ *
+ * \see cl_page_is_under_lock()
+ */
+ int (*cpo_is_under_lock)(const struct lu_env *env,
+ const struct cl_page_slice *slice,
+ struct cl_io *io);
+
+ /**
+ * Optional debugging helper. Prints given page slice.
+ *
+ * \see cl_page_print()
+ */
+ int (*cpo_print)(const struct lu_env *env,
+ const struct cl_page_slice *slice,
+ void *cookie, lu_printer_t p);
+ /**
+ * \name transfer
+ *
+ * Transfer methods. See comment on cl_req for a description of
+ * transfer formation and life-cycle.
+ *
+ * @{
+ */
+ /**
+ * Request type dependent vector of operations.
+ *
+ * Transfer operations depend on transfer mode (cl_req_type). To avoid
+ * passing transfer mode to each and every of these methods, and to
+ * avoid branching on request type inside of the methods, separate
+ * methods for cl_req_type:CRT_READ and cl_req_type:CRT_WRITE are
+ * provided. That is, method invocation usually looks like
+ *
+ * slice->cp_ops.io[req->crq_type].cpo_method(env, slice, ...);
+ */
+ struct {
+ /**
+ * Called when a page is submitted for a transfer as a part of
+ * cl_page_list.
+ *
+ * \return 0 : page is eligible for submission;
+ * \return -EALREADY : skip this page;
+ * \return -ve : error.
+ *
+ * \see cl_page_prep()
+ */
+ int (*cpo_prep)(const struct lu_env *env,
+ const struct cl_page_slice *slice,
+ struct cl_io *io);
+ /**
+ * Completion handler. This is guaranteed to be eventually
+ * fired after cl_page_operations::cpo_prep() or
+ * cl_page_operations::cpo_make_ready() call.
+ *
+ * This method can be called in a non-blocking context. It is
+ * guaranteed however, that the page involved and its object
+ * are pinned in memory (and, hence, calling cl_page_put() is
+ * safe).
+ *
+ * \see cl_page_completion()
+ */
+ void (*cpo_completion)(const struct lu_env *env,
+ const struct cl_page_slice *slice,
+ int ioret);
+ /**
+ * Called when cached page is about to be added to the
+ * cl_req as a part of req formation.
+ *
+ * \return 0 : proceed with this page;
+ * \return -EAGAIN : skip this page;
+ * \return -ve : error.
+ *
+ * \see cl_page_make_ready()
+ */
+ int (*cpo_make_ready)(const struct lu_env *env,
+ const struct cl_page_slice *slice);
+ /**
+ * Announce that this page is to be written out
+ * opportunistically, that is, page is dirty, it is not
+ * necessary to start write-out transfer right now, but
+ * eventually page has to be written out.
+ *
+ * Main caller of this is the write path (see
+ * vvp_io_commit_write()), using this method to build a
+ * "transfer cache" from which large transfers are then
+ * constructed by the req-formation engine.
+ *
+ * \todo XXX it would make sense to add page-age tracking
+ * semantics here, and to oblige the req-formation engine to
+ * send the page out not later than it is too old.
+ *
+ * \see cl_page_cache_add()
+ */
+ int (*cpo_cache_add)(const struct lu_env *env,
+ const struct cl_page_slice *slice,
+ struct cl_io *io);
+ } io[CRT_NR];
+ /**
+ * Tell transfer engine that only [to, from] part of a page should be
+ * transmitted.
+ *
+ * This is used for immediate transfers.
+ *
+ * \todo XXX this is not very good interface. It would be much better
+ * if all transfer parameters were supplied as arguments to
+ * cl_io_operations::cio_submit() call, but it is not clear how to do
+ * this for page queues.
+ *
+ * \see cl_page_clip()
+ */
+ void (*cpo_clip)(const struct lu_env *env,
+ const struct cl_page_slice *slice,
+ int from, int to);
+ /**
+ * \pre the page was queued for transferring.
+ * \post page is removed from client's pending list, or -EBUSY
+ * is returned if it has already been in transferring.
+ *
+ * This is one of seldom page operation which is:
+ * 0. called from top level;
+ * 1. don't have vmpage locked;
+ * 2. every layer should synchronize execution of its ->cpo_cancel()
+ * with completion handlers. Osc uses client obd lock for this
+ * purpose. Based on there is no vvp_page_cancel and
+ * lov_page_cancel(), cpo_cancel is defacto protected by client lock.
+ *
+ * \see osc_page_cancel().
+ */
+ int (*cpo_cancel)(const struct lu_env *env,
+ const struct cl_page_slice *slice);
+ /**
+ * Write out a page by kernel. This is only called by ll_writepage
+ * right now.
+ *
+ * \see cl_page_flush()
+ */
+ int (*cpo_flush)(const struct lu_env *env,
+ const struct cl_page_slice *slice,
+ struct cl_io *io);
+ /** @} transfer */
+};
+
+/**
+ * Helper macro, dumping detailed information about \a page into a log.
+ */
+#define CL_PAGE_DEBUG(mask, env, page, format, ...) \
+do { \
+ LIBCFS_DEBUG_MSG_DATA_DECL(msgdata, mask, NULL); \
+ \
+ if (cfs_cdebug_show(mask, DEBUG_SUBSYSTEM)) { \
+ cl_page_print(env, &msgdata, lu_cdebug_printer, page); \
+ CDEBUG(mask, format , ## __VA_ARGS__); \
+ } \
+} while (0)
+
+/**
+ * Helper macro, dumping shorter information about \a page into a log.
+ */
+#define CL_PAGE_HEADER(mask, env, page, format, ...) \
+do { \
+ LIBCFS_DEBUG_MSG_DATA_DECL(msgdata, mask, NULL); \
+ \
+ if (cfs_cdebug_show(mask, DEBUG_SUBSYSTEM)) { \
+ cl_page_header_print(env, &msgdata, lu_cdebug_printer, page); \
+ CDEBUG(mask, format , ## __VA_ARGS__); \
+ } \
+} while (0)
+
+static inline int __page_in_use(const struct cl_page *page, int refc)
+{
+ if (page->cp_type == CPT_CACHEABLE)
+ ++refc;
+ LASSERT(atomic_read(&page->cp_ref) > 0);
+ return (atomic_read(&page->cp_ref) > refc);
+}
+#define cl_page_in_use(pg) __page_in_use(pg, 1)
+#define cl_page_in_use_noref(pg) __page_in_use(pg, 0)
+
+/** @} cl_page */
+
+/** \addtogroup cl_lock cl_lock
+ * @{ */
+/** \struct cl_lock
+ *
+ * Extent locking on the client.
+ *
+ * LAYERING
+ *
+ * The locking model of the new client code is built around
+ *
+ * struct cl_lock
+ *
+ * data-type representing an extent lock on a regular file. cl_lock is a
+ * layered object (much like cl_object and cl_page), it consists of a header
+ * (struct cl_lock) and a list of layers (struct cl_lock_slice), linked to
+ * cl_lock::cll_layers list through cl_lock_slice::cls_linkage.
+ *
+ * All locks for a given object are linked into cl_object_header::coh_locks
+ * list (protected by cl_object_header::coh_lock_guard spin-lock) through
+ * cl_lock::cll_linkage. Currently this list is not sorted in any way. We can
+ * sort it in starting lock offset, or use altogether different data structure
+ * like a tree.
+ *
+ * Typical cl_lock consists of the two layers:
+ *
+ * - vvp_lock (vvp specific data), and
+ * - lov_lock (lov specific data).
+ *
+ * lov_lock contains an array of sub-locks. Each of these sub-locks is a
+ * normal cl_lock: it has a header (struct cl_lock) and a list of layers:
+ *
+ * - lovsub_lock, and
+ * - osc_lock
+ *
+ * Each sub-lock is associated with a cl_object (representing stripe
+ * sub-object or the file to which top-level cl_lock is associated to), and is
+ * linked into that cl_object::coh_locks. In this respect cl_lock is similar to
+ * cl_object (that at lov layer also fans out into multiple sub-objects), and
+ * is different from cl_page, that doesn't fan out (there is usually exactly
+ * one osc_page for every vvp_page). We shall call vvp-lov portion of the lock
+ * a "top-lock" and its lovsub-osc portion a "sub-lock".
+ *
+ * LIFE CYCLE
+ *
+ * cl_lock is reference counted. When reference counter drops to 0, lock is
+ * placed in the cache, except when lock is in CLS_FREEING state. CLS_FREEING
+ * lock is destroyed when last reference is released. Referencing between
+ * top-lock and its sub-locks is described in the lov documentation module.
+ *
+ * STATE MACHINE
+ *
+ * Also, cl_lock is a state machine. This requires some clarification. One of
+ * the goals of client IO re-write was to make IO path non-blocking, or at
+ * least to make it easier to make it non-blocking in the future. Here
+ * `non-blocking' means that when a system call (read, write, truncate)
+ * reaches a situation where it has to wait for a communication with the
+ * server, it should --instead of waiting-- remember its current state and
+ * switch to some other work. E.g,. instead of waiting for a lock enqueue,
+ * client should proceed doing IO on the next stripe, etc. Obviously this is
+ * rather radical redesign, and it is not planned to be fully implemented at
+ * this time, instead we are putting some infrastructure in place, that would
+ * make it easier to do asynchronous non-blocking IO easier in the
+ * future. Specifically, where old locking code goes to sleep (waiting for
+ * enqueue, for example), new code returns cl_lock_transition::CLO_WAIT. When
+ * enqueue reply comes, its completion handler signals that lock state-machine
+ * is ready to transit to the next state. There is some generic code in
+ * cl_lock.c that sleeps, waiting for these signals. As a result, for users of
+ * this cl_lock.c code, it looks like locking is done in normal blocking
+ * fashion, and it the same time it is possible to switch to the non-blocking
+ * locking (simply by returning cl_lock_transition::CLO_WAIT from cl_lock.c
+ * functions).
+ *
+ * For a description of state machine states and transitions see enum
+ * cl_lock_state.
+ *
+ * There are two ways to restrict a set of states which lock might move to:
+ *
+ * - placing a "hold" on a lock guarantees that lock will not be moved
+ * into cl_lock_state::CLS_FREEING state until hold is released. Hold
+ * can be only acquired on a lock that is not in
+ * cl_lock_state::CLS_FREEING. All holds on a lock are counted in
+ * cl_lock::cll_holds. Hold protects lock from cancellation and
+ * destruction. Requests to cancel and destroy a lock on hold will be
+ * recorded, but only honored when last hold on a lock is released;
+ *
+ * - placing a "user" on a lock guarantees that lock will not leave
+ * cl_lock_state::CLS_NEW, cl_lock_state::CLS_QUEUING,
+ * cl_lock_state::CLS_ENQUEUED and cl_lock_state::CLS_HELD set of
+ * states, once it enters this set. That is, if a user is added onto a
+ * lock in a state not from this set, it doesn't immediately enforce
+ * lock to move to this set, but once lock enters this set it will
+ * remain there until all users are removed. Lock users are counted in
+ * cl_lock::cll_users.
+ *
+ * User is used to assure that lock is not canceled or destroyed while
+ * it is being enqueued, or actively used by some IO.
+ *
+ * Currently, a user always comes with a hold (cl_lock_invariant()
+ * checks that a number of holds is not less than a number of users).
+ *
+ * CONCURRENCY
+ *
+ * This is how lock state-machine operates. struct cl_lock contains a mutex
+ * cl_lock::cll_guard that protects struct fields.
+ *
+ * - mutex is taken, and cl_lock::cll_state is examined.
+ *
+ * - for every state there are possible target states where lock can move
+ * into. They are tried in order. Attempts to move into next state are
+ * done by _try() functions in cl_lock.c:cl_{enqueue,unlock,wait}_try().
+ *
+ * - if the transition can be performed immediately, state is changed,
+ * and mutex is released.
+ *
+ * - if the transition requires blocking, _try() function returns
+ * cl_lock_transition::CLO_WAIT. Caller unlocks mutex and goes to
+ * sleep, waiting for possibility of lock state change. It is woken
+ * up when some event occurs, that makes lock state change possible
+ * (e.g., the reception of the reply from the server), and repeats
+ * the loop.
+ *
+ * Top-lock and sub-lock has separate mutexes and the latter has to be taken
+ * first to avoid dead-lock.
+ *
+ * To see an example of interaction of all these issues, take a look at the
+ * lov_cl.c:lov_lock_enqueue() function. It is called as a part of
+ * cl_enqueue_try(), and tries to advance top-lock to ENQUEUED state, by
+ * advancing state-machines of its sub-locks (lov_lock_enqueue_one()). Note
+ * also, that it uses trylock to grab sub-lock mutex to avoid dead-lock. It
+ * also has to handle CEF_ASYNC enqueue, when sub-locks enqueues have to be
+ * done in parallel, rather than one after another (this is used for glimpse
+ * locks, that cannot dead-lock).
+ *
+ * INTERFACE AND USAGE
+ *
+ * struct cl_lock_operations provide a number of call-backs that are invoked
+ * when events of interest occurs. Layers can intercept and handle glimpse,
+ * blocking, cancel ASTs and a reception of the reply from the server.
+ *
+ * One important difference with the old client locking model is that new
+ * client has a representation for the top-lock, whereas in the old code only
+ * sub-locks existed as real data structures and file-level locks are
+ * represented by "request sets" that are created and destroyed on each and
+ * every lock creation.
+ *
+ * Top-locks are cached, and can be found in the cache by the system calls. It
+ * is possible that top-lock is in cache, but some of its sub-locks were
+ * canceled and destroyed. In that case top-lock has to be enqueued again
+ * before it can be used.
+ *
+ * Overall process of the locking during IO operation is as following:
+ *
+ * - once parameters for IO are setup in cl_io, cl_io_operations::cio_lock()
+ * is called on each layer. Responsibility of this method is to add locks,
+ * needed by a given layer into cl_io.ci_lockset.
+ *
+ * - once locks for all layers were collected, they are sorted to avoid
+ * dead-locks (cl_io_locks_sort()), and enqueued.
+ *
+ * - when all locks are acquired, IO is performed;
+ *
+ * - locks are released into cache.
+ *
+ * Striping introduces major additional complexity into locking. The
+ * fundamental problem is that it is generally unsafe to actively use (hold)
+ * two locks on the different OST servers at the same time, as this introduces
+ * inter-server dependency and can lead to cascading evictions.
+ *
+ * Basic solution is to sub-divide large read/write IOs into smaller pieces so
+ * that no multi-stripe locks are taken (note that this design abandons POSIX
+ * read/write semantics). Such pieces ideally can be executed concurrently. At
+ * the same time, certain types of IO cannot be sub-divived, without
+ * sacrificing correctness. This includes:
+ *
+ * - O_APPEND write, where [0, EOF] lock has to be taken, to guarantee
+ * atomicity;
+ *
+ * - ftruncate(fd, offset), where [offset, EOF] lock has to be taken.
+ *
+ * Also, in the case of read(fd, buf, count) or write(fd, buf, count), where
+ * buf is a part of memory mapped Lustre file, a lock or locks protecting buf
+ * has to be held together with the usual lock on [offset, offset + count].
+ *
+ * As multi-stripe locks have to be allowed, it makes sense to cache them, so
+ * that, for example, a sequence of O_APPEND writes can proceed quickly
+ * without going down to the individual stripes to do lock matching. On the
+ * other hand, multi-stripe locks shouldn't be used by normal read/write
+ * calls. To achieve this, every layer can implement ->clo_fits_into() method,
+ * that is called by lock matching code (cl_lock_lookup()), and that can be
+ * used to selectively disable matching of certain locks for certain IOs. For
+ * example, lov layer implements lov_lock_fits_into() that allow multi-stripe
+ * locks to be matched only for truncates and O_APPEND writes.
+ *
+ * Interaction with DLM
+ *
+ * In the expected setup, cl_lock is ultimately backed up by a collection of
+ * DLM locks (struct ldlm_lock). Association between cl_lock and DLM lock is
+ * implemented in osc layer, that also matches DLM events (ASTs, cancellation,
+ * etc.) into cl_lock_operation calls. See struct osc_lock for a more detailed
+ * description of interaction with DLM.
+ */
+
+/**
+ * Lock description.
+ */
+struct cl_lock_descr {
+ /** Object this lock is granted for. */
+ struct cl_object *cld_obj;
+ /** Index of the first page protected by this lock. */
+ pgoff_t cld_start;
+ /** Index of the last page (inclusive) protected by this lock. */
+ pgoff_t cld_end;
+ /** Group ID, for group lock */
+ __u64 cld_gid;
+ /** Lock mode. */
+ enum cl_lock_mode cld_mode;
+ /**
+ * flags to enqueue lock. A combination of bit-flags from
+ * enum cl_enq_flags.
+ */
+ __u32 cld_enq_flags;
+};
+
+#define DDESCR "%s(%d):[%lu, %lu]"
+#define PDESCR(descr) \
+ cl_lock_mode_name((descr)->cld_mode), (descr)->cld_mode, \
+ (descr)->cld_start, (descr)->cld_end
+
+const char *cl_lock_mode_name(const enum cl_lock_mode mode);
+
+/**
+ * Lock state-machine states.
+ *
+ * \htmlonly
+ * <pre>
+ *
+ * Possible state transitions:
+ *
+ * +------------------>NEW
+ * | |
+ * | | cl_enqueue_try()
+ * | |
+ * | cl_unuse_try() V
+ * | +--------------QUEUING (*)
+ * | | |
+ * | | | cl_enqueue_try()
+ * | | |
+ * | | cl_unuse_try() V
+ * sub-lock | +-------------ENQUEUED (*)
+ * canceled | | |
+ * | | | cl_wait_try()
+ * | | |
+ * | | (R)
+ * | | |
+ * | | V
+ * | | HELD<---------+
+ * | | | |
+ * | | | | cl_use_try()
+ * | | cl_unuse_try() | |
+ * | | | |
+ * | | V ---+
+ * | +------------>INTRANSIT (D) <--+
+ * | | |
+ * | cl_unuse_try() | | cached lock found
+ * | | | cl_use_try()
+ * | | |
+ * | V |
+ * +------------------CACHED---------+
+ * |
+ * (C)
+ * |
+ * V
+ * FREEING
+ *
+ * Legend:
+ *
+ * In states marked with (*) transition to the same state (i.e., a loop
+ * in the diagram) is possible.
+ *
+ * (R) is the point where Receive call-back is invoked: it allows layers
+ * to handle arrival of lock reply.
+ *
+ * (C) is the point where Cancellation call-back is invoked.
+ *
+ * (D) is the transit state which means the lock is changing.
+ *
+ * Transition to FREEING state is possible from any other state in the
+ * diagram in case of unrecoverable error.
+ * </pre>
+ * \endhtmlonly
+ *
+ * These states are for individual cl_lock object. Top-lock and its sub-locks
+ * can be in the different states. Another way to say this is that we have
+ * nested state-machines.
+ *
+ * Separate QUEUING and ENQUEUED states are needed to support non-blocking
+ * operation for locks with multiple sub-locks. Imagine lock on a file F, that
+ * intersects 3 stripes S0, S1, and S2. To enqueue F client has to send
+ * enqueue to S0, wait for its completion, then send enqueue for S1, wait for
+ * its completion and at last enqueue lock for S2, and wait for its
+ * completion. In that case, top-lock is in QUEUING state while S0, S1 are
+ * handled, and is in ENQUEUED state after enqueue to S2 has been sent (note
+ * that in this case, sub-locks move from state to state, and top-lock remains
+ * in the same state).
+ */
+enum cl_lock_state {
+ /**
+ * Lock that wasn't yet enqueued
+ */
+ CLS_NEW,
+ /**
+ * Enqueue is in progress, blocking for some intermediate interaction
+ * with the other side.
+ */
+ CLS_QUEUING,
+ /**
+ * Lock is fully enqueued, waiting for server to reply when it is
+ * granted.
+ */
+ CLS_ENQUEUED,
+ /**
+ * Lock granted, actively used by some IO.
+ */
+ CLS_HELD,
+ /**
+ * This state is used to mark the lock is being used, or unused.
+ * We need this state because the lock may have several sublocks,
+ * so it's impossible to have an atomic way to bring all sublocks
+ * into CLS_HELD state at use case, or all sublocks to CLS_CACHED
+ * at unuse case.
+ * If a thread is referring to a lock, and it sees the lock is in this
+ * state, it must wait for the lock.
+ * See state diagram for details.
+ */
+ CLS_INTRANSIT,
+ /**
+ * Lock granted, not used.
+ */
+ CLS_CACHED,
+ /**
+ * Lock is being destroyed.
+ */
+ CLS_FREEING,
+ CLS_NR
+};
+
+enum cl_lock_flags {
+ /**
+ * lock has been cancelled. This flag is never cleared once set (by
+ * cl_lock_cancel0()).
+ */
+ CLF_CANCELLED = 1 << 0,
+ /** cancellation is pending for this lock. */
+ CLF_CANCELPEND = 1 << 1,
+ /** destruction is pending for this lock. */
+ CLF_DOOMED = 1 << 2,
+ /** from enqueue RPC reply upcall. */
+ CLF_FROM_UPCALL= 1 << 3,
+};
+
+/**
+ * Lock closure.
+ *
+ * Lock closure is a collection of locks (both top-locks and sub-locks) that
+ * might be updated in a result of an operation on a certain lock (which lock
+ * this is a closure of).
+ *
+ * Closures are needed to guarantee dead-lock freedom in the presence of
+ *
+ * - nested state-machines (top-lock state-machine composed of sub-lock
+ * state-machines), and
+ *
+ * - shared sub-locks.
+ *
+ * Specifically, many operations, such as lock enqueue, wait, unlock,
+ * etc. start from a top-lock, and then operate on a sub-locks of this
+ * top-lock, holding a top-lock mutex. When sub-lock state changes as a result
+ * of such operation, this change has to be propagated to all top-locks that
+ * share this sub-lock. Obviously, no natural lock ordering (e.g.,
+ * top-to-bottom or bottom-to-top) captures this scenario, so try-locking has
+ * to be used. Lock closure systematizes this try-and-repeat logic.
+ */
+struct cl_lock_closure {
+ /**
+ * Lock that is mutexed when closure construction is started. When
+ * closure in is `wait' mode (cl_lock_closure::clc_wait), mutex on
+ * origin is released before waiting.
+ */
+ struct cl_lock *clc_origin;
+ /**
+ * List of enclosed locks, so far. Locks are linked here through
+ * cl_lock::cll_inclosure.
+ */
+ struct list_head clc_list;
+ /**
+ * True iff closure is in a `wait' mode. This determines what
+ * cl_lock_enclosure() does when a lock L to be added to the closure
+ * is currently mutexed by some other thread.
+ *
+ * If cl_lock_closure::clc_wait is not set, then closure construction
+ * fails with CLO_REPEAT immediately.
+ *
+ * In wait mode, cl_lock_enclosure() waits until next attempt to build
+ * a closure might succeed. To this end it releases an origin mutex
+ * (cl_lock_closure::clc_origin), that has to be the only lock mutex
+ * owned by the current thread, and then waits on L mutex (by grabbing
+ * it and immediately releasing), before returning CLO_REPEAT to the
+ * caller.
+ */
+ int clc_wait;
+ /** Number of locks in the closure. */
+ int clc_nr;
+};
+
+/**
+ * Layered client lock.
+ */
+struct cl_lock {
+ /** Reference counter. */
+ atomic_t cll_ref;
+ /** List of slices. Immutable after creation. */
+ struct list_head cll_layers;
+ /**
+ * Linkage into cl_lock::cll_descr::cld_obj::coh_locks list. Protected
+ * by cl_lock::cll_descr::cld_obj::coh_lock_guard.
+ */
+ struct list_head cll_linkage;
+ /**
+ * Parameters of this lock. Protected by
+ * cl_lock::cll_descr::cld_obj::coh_lock_guard nested within
+ * cl_lock::cll_guard. Modified only on lock creation and in
+ * cl_lock_modify().
+ */
+ struct cl_lock_descr cll_descr;
+ /** Protected by cl_lock::cll_guard. */
+ enum cl_lock_state cll_state;
+ /** signals state changes. */
+ wait_queue_head_t cll_wq;
+ /**
+ * Recursive lock, most fields in cl_lock{} are protected by this.
+ *
+ * Locking rules: this mutex is never held across network
+ * communication, except when lock is being canceled.
+ *
+ * Lock ordering: a mutex of a sub-lock is taken first, then a mutex
+ * on a top-lock. Other direction is implemented through a
+ * try-lock-repeat loop. Mutices of unrelated locks can be taken only
+ * by try-locking.
+ *
+ * \see osc_lock_enqueue_wait(), lov_lock_cancel(), lov_sublock_wait().
+ */
+ struct mutex cll_guard;
+ struct task_struct *cll_guarder;
+ int cll_depth;
+
+ /**
+ * the owner for INTRANSIT state
+ */
+ struct task_struct *cll_intransit_owner;
+ int cll_error;
+ /**
+ * Number of holds on a lock. A hold prevents a lock from being
+ * canceled and destroyed. Protected by cl_lock::cll_guard.
+ *
+ * \see cl_lock_hold(), cl_lock_unhold(), cl_lock_release()
+ */
+ int cll_holds;
+ /**
+ * Number of lock users. Valid in cl_lock_state::CLS_HELD state
+ * only. Lock user pins lock in CLS_HELD state. Protected by
+ * cl_lock::cll_guard.
+ *
+ * \see cl_wait(), cl_unuse().
+ */
+ int cll_users;
+ /**
+ * Flag bit-mask. Values from enum cl_lock_flags. Updates are
+ * protected by cl_lock::cll_guard.
+ */
+ unsigned long cll_flags;
+ /**
+ * A linkage into a list of locks in a closure.
+ *
+ * \see cl_lock_closure
+ */
+ struct list_head cll_inclosure;
+ /**
+ * Confict lock at queuing time.
+ */
+ struct cl_lock *cll_conflict;
+ /**
+ * A list of references to this lock, for debugging.
+ */
+ struct lu_ref cll_reference;
+ /**
+ * A list of holds on this lock, for debugging.
+ */
+ struct lu_ref cll_holders;
+ /**
+ * A reference for cl_lock::cll_descr::cld_obj. For debugging.
+ */
+ struct lu_ref_link cll_obj_ref;
+#ifdef CONFIG_LOCKDEP
+ /* "dep_map" name is assumed by lockdep.h macros. */
+ struct lockdep_map dep_map;
+#endif
+};
+
+/**
+ * Per-layer part of cl_lock
+ *
+ * \see ccc_lock, lov_lock, lovsub_lock, osc_lock
+ */
+struct cl_lock_slice {
+ struct cl_lock *cls_lock;
+ /** Object slice corresponding to this lock slice. Immutable after
+ * creation. */
+ struct cl_object *cls_obj;
+ const struct cl_lock_operations *cls_ops;
+ /** Linkage into cl_lock::cll_layers. Immutable after creation. */
+ struct list_head cls_linkage;
+};
+
+/**
+ * Possible (non-error) return values of ->clo_{enqueue,wait,unlock}().
+ *
+ * NOTE: lov_subresult() depends on ordering here.
+ */
+enum cl_lock_transition {
+ /** operation cannot be completed immediately. Wait for state change. */
+ CLO_WAIT = 1,
+ /** operation had to release lock mutex, restart. */
+ CLO_REPEAT = 2,
+ /** lower layer re-enqueued. */
+ CLO_REENQUEUED = 3,
+};
+
+/**
+ *
+ * \see vvp_lock_ops, lov_lock_ops, lovsub_lock_ops, osc_lock_ops
+ */
+struct cl_lock_operations {
+ /**
+ * \name statemachine
+ *
+ * State machine transitions. These 3 methods are called to transfer
+ * lock from one state to another, as described in the commentary
+ * above enum #cl_lock_state.
+ *
+ * \retval 0 this layer has nothing more to do to before
+ * transition to the target state happens;
+ *
+ * \retval CLO_REPEAT method had to release and re-acquire cl_lock
+ * mutex, repeat invocation of transition method
+ * across all layers;
+ *
+ * \retval CLO_WAIT this layer cannot move to the target state
+ * immediately, as it has to wait for certain event
+ * (e.g., the communication with the server). It
+ * is guaranteed, that when the state transfer
+ * becomes possible, cl_lock::cll_wq wait-queue
+ * is signaled. Caller can wait for this event by
+ * calling cl_lock_state_wait();
+ *
+ * \retval -ve failure, abort state transition, move the lock
+ * into cl_lock_state::CLS_FREEING state, and set
+ * cl_lock::cll_error.
+ *
+ * Once all layers voted to agree to transition (by returning 0), lock
+ * is moved into corresponding target state. All state transition
+ * methods are optional.
+ */
+ /** @{ */
+ /**
+ * Attempts to enqueue the lock. Called top-to-bottom.
+ *
+ * \see ccc_lock_enqueue(), lov_lock_enqueue(), lovsub_lock_enqueue(),
+ * \see osc_lock_enqueue()
+ */
+ int (*clo_enqueue)(const struct lu_env *env,
+ const struct cl_lock_slice *slice,
+ struct cl_io *io, __u32 enqflags);
+ /**
+ * Attempts to wait for enqueue result. Called top-to-bottom.
+ *
+ * \see ccc_lock_wait(), lov_lock_wait(), osc_lock_wait()
+ */
+ int (*clo_wait)(const struct lu_env *env,
+ const struct cl_lock_slice *slice);
+ /**
+ * Attempts to unlock the lock. Called bottom-to-top. In addition to
+ * usual return values of lock state-machine methods, this can return
+ * -ESTALE to indicate that lock cannot be returned to the cache, and
+ * has to be re-initialized.
+ * unuse is a one-shot operation, so it must NOT return CLO_WAIT.
+ *
+ * \see ccc_lock_unuse(), lov_lock_unuse(), osc_lock_unuse()
+ */
+ int (*clo_unuse)(const struct lu_env *env,
+ const struct cl_lock_slice *slice);
+ /**
+ * Notifies layer that cached lock is started being used.
+ *
+ * \pre lock->cll_state == CLS_CACHED
+ *
+ * \see lov_lock_use(), osc_lock_use()
+ */
+ int (*clo_use)(const struct lu_env *env,
+ const struct cl_lock_slice *slice);
+ /** @} statemachine */
+ /**
+ * A method invoked when lock state is changed (as a result of state
+ * transition). This is used, for example, to track when the state of
+ * a sub-lock changes, to propagate this change to the corresponding
+ * top-lock. Optional
+ *
+ * \see lovsub_lock_state()
+ */
+ void (*clo_state)(const struct lu_env *env,
+ const struct cl_lock_slice *slice,
+ enum cl_lock_state st);
+ /**
+ * Returns true, iff given lock is suitable for the given io, idea
+ * being, that there are certain "unsafe" locks, e.g., ones acquired
+ * for O_APPEND writes, that we don't want to re-use for a normal
+ * write, to avoid the danger of cascading evictions. Optional. Runs
+ * under cl_object_header::coh_lock_guard.
+ *
+ * XXX this should take more information about lock needed by
+ * io. Probably lock description or something similar.
+ *
+ * \see lov_fits_into()
+ */
+ int (*clo_fits_into)(const struct lu_env *env,
+ const struct cl_lock_slice *slice,
+ const struct cl_lock_descr *need,
+ const struct cl_io *io);
+ /**
+ * \name ast
+ * Asynchronous System Traps. All of then are optional, all are
+ * executed bottom-to-top.
+ */
+ /** @{ */
+
+ /**
+ * Cancellation callback. Cancel a lock voluntarily, or under
+ * the request of server.
+ */
+ void (*clo_cancel)(const struct lu_env *env,
+ const struct cl_lock_slice *slice);
+ /**
+ * Lock weighting ast. Executed to estimate how precious this lock
+ * is. The sum of results across all layers is used to determine
+ * whether lock worth keeping in cache given present memory usage.
+ *
+ * \see osc_lock_weigh(), vvp_lock_weigh(), lovsub_lock_weigh().
+ */
+ unsigned long (*clo_weigh)(const struct lu_env *env,
+ const struct cl_lock_slice *slice);
+ /** @} ast */
+
+ /**
+ * \see lovsub_lock_closure()
+ */
+ int (*clo_closure)(const struct lu_env *env,
+ const struct cl_lock_slice *slice,
+ struct cl_lock_closure *closure);
+ /**
+ * Executed bottom-to-top when lock description changes (e.g., as a
+ * result of server granting more generous lock than was requested).
+ *
+ * \see lovsub_lock_modify()
+ */
+ int (*clo_modify)(const struct lu_env *env,
+ const struct cl_lock_slice *slice,
+ const struct cl_lock_descr *updated);
+ /**
+ * Notifies layers (bottom-to-top) that lock is going to be
+ * destroyed. Responsibility of layers is to prevent new references on
+ * this lock from being acquired once this method returns.
+ *
+ * This can be called multiple times due to the races.
+ *
+ * \see cl_lock_delete()
+ * \see osc_lock_delete(), lovsub_lock_delete()
+ */
+ void (*clo_delete)(const struct lu_env *env,
+ const struct cl_lock_slice *slice);
+ /**
+ * Destructor. Frees resources and the slice.
+ *
+ * \see ccc_lock_fini(), lov_lock_fini(), lovsub_lock_fini(),
+ * \see osc_lock_fini()
+ */
+ void (*clo_fini)(const struct lu_env *env, struct cl_lock_slice *slice);
+ /**
+ * Optional debugging helper. Prints given lock slice.
+ */
+ int (*clo_print)(const struct lu_env *env,
+ void *cookie, lu_printer_t p,
+ const struct cl_lock_slice *slice);
+};
+
+#define CL_LOCK_DEBUG(mask, env, lock, format, ...) \
+do { \
+ LIBCFS_DEBUG_MSG_DATA_DECL(msgdata, mask, NULL); \
+ \
+ if (cfs_cdebug_show(mask, DEBUG_SUBSYSTEM)) { \
+ cl_lock_print(env, &msgdata, lu_cdebug_printer, lock); \
+ CDEBUG(mask, format , ## __VA_ARGS__); \
+ } \
+} while (0)
+
+#define CL_LOCK_ASSERT(expr, env, lock) do { \
+ if (likely(expr)) \
+ break; \
+ \
+ CL_LOCK_DEBUG(D_ERROR, env, lock, "failed at %s.\n", #expr); \
+ LBUG(); \
+} while (0)
+
+/** @} cl_lock */
+
+/** \addtogroup cl_page_list cl_page_list
+ * Page list used to perform collective operations on a group of pages.
+ *
+ * Pages are added to the list one by one. cl_page_list acquires a reference
+ * for every page in it. Page list is used to perform collective operations on
+ * pages:
+ *
+ * - submit pages for an immediate transfer,
+ *
+ * - own pages on behalf of certain io (waiting for each page in turn),
+ *
+ * - discard pages.
+ *
+ * When list is finalized, it releases references on all pages it still has.
+ *
+ * \todo XXX concurrency control.
+ *
+ * @{
+ */
+struct cl_page_list {
+ unsigned pl_nr;
+ struct list_head pl_pages;
+ struct task_struct *pl_owner;
+};
+
+/**
+ * A 2-queue of pages. A convenience data-type for common use case, 2-queue
+ * contains an incoming page list and an outgoing page list.
+ */
+struct cl_2queue {
+ struct cl_page_list c2_qin;
+ struct cl_page_list c2_qout;
+};
+
+/** @} cl_page_list */
+
+/** \addtogroup cl_io cl_io
+ * @{ */
+/** \struct cl_io
+ * I/O
+ *
+ * cl_io represents a high level I/O activity like
+ * read(2)/write(2)/truncate(2) system call, or cancellation of an extent
+ * lock.
+ *
+ * cl_io is a layered object, much like cl_{object,page,lock} but with one
+ * important distinction. We want to minimize number of calls to the allocator
+ * in the fast path, e.g., in the case of read(2) when everything is cached:
+ * client already owns the lock over region being read, and data are cached
+ * due to read-ahead. To avoid allocation of cl_io layers in such situations,
+ * per-layer io state is stored in the session, associated with the io, see
+ * struct {vvp,lov,osc}_io for example. Sessions allocation is amortized
+ * by using free-lists, see cl_env_get().
+ *
+ * There is a small predefined number of possible io types, enumerated in enum
+ * cl_io_type.
+ *
+ * cl_io is a state machine, that can be advanced concurrently by the multiple
+ * threads. It is up to these threads to control the concurrency and,
+ * specifically, to detect when io is done, and its state can be safely
+ * released.
+ *
+ * For read/write io overall execution plan is as following:
+ *
+ * (0) initialize io state through all layers;
+ *
+ * (1) loop: prepare chunk of work to do
+ *
+ * (2) call all layers to collect locks they need to process current chunk
+ *
+ * (3) sort all locks to avoid dead-locks, and acquire them
+ *
+ * (4) process the chunk: call per-page methods
+ * (cl_io_operations::cio_read_page() for read,
+ * cl_io_operations::cio_prepare_write(),
+ * cl_io_operations::cio_commit_write() for write)
+ *
+ * (5) release locks
+ *
+ * (6) repeat loop.
+ *
+ * To implement the "parallel IO mode", lov layer creates sub-io's (lazily to
+ * address allocation efficiency issues mentioned above), and returns with the
+ * special error condition from per-page method when current sub-io has to
+ * block. This causes io loop to be repeated, and lov switches to the next
+ * sub-io in its cl_io_operations::cio_iter_init() implementation.
+ */
+
+/** IO types */
+enum cl_io_type {
+ /** read system call */
+ CIT_READ,
+ /** write system call */
+ CIT_WRITE,
+ /** truncate, utime system calls */
+ CIT_SETATTR,
+ /**
+ * page fault handling
+ */
+ CIT_FAULT,
+ /**
+ * fsync system call handling
+ * To write out a range of file
+ */
+ CIT_FSYNC,
+ /**
+ * Miscellaneous io. This is used for occasional io activity that
+ * doesn't fit into other types. Currently this is used for:
+ *
+ * - cancellation of an extent lock. This io exists as a context
+ * to write dirty pages from under the lock being canceled back
+ * to the server;
+ *
+ * - VM induced page write-out. An io context for writing page out
+ * for memory cleansing;
+ *
+ * - glimpse. An io context to acquire glimpse lock.
+ *
+ * - grouplock. An io context to acquire group lock.
+ *
+ * CIT_MISC io is used simply as a context in which locks and pages
+ * are manipulated. Such io has no internal "process", that is,
+ * cl_io_loop() is never called for it.
+ */
+ CIT_MISC,
+ CIT_OP_NR
+};
+
+/**
+ * States of cl_io state machine
+ */
+enum cl_io_state {
+ /** Not initialized. */
+ CIS_ZERO,
+ /** Initialized. */
+ CIS_INIT,
+ /** IO iteration started. */
+ CIS_IT_STARTED,
+ /** Locks taken. */
+ CIS_LOCKED,
+ /** Actual IO is in progress. */
+ CIS_IO_GOING,
+ /** IO for the current iteration finished. */
+ CIS_IO_FINISHED,
+ /** Locks released. */
+ CIS_UNLOCKED,
+ /** Iteration completed. */
+ CIS_IT_ENDED,
+ /** cl_io finalized. */
+ CIS_FINI
+};
+
+/**
+ * IO state private for a layer.
+ *
+ * This is usually embedded into layer session data, rather than allocated
+ * dynamically.
+ *
+ * \see vvp_io, lov_io, osc_io, ccc_io
+ */
+struct cl_io_slice {
+ struct cl_io *cis_io;
+ /** corresponding object slice. Immutable after creation. */
+ struct cl_object *cis_obj;
+ /** io operations. Immutable after creation. */
+ const struct cl_io_operations *cis_iop;
+ /**
+ * linkage into a list of all slices for a given cl_io, hanging off
+ * cl_io::ci_layers. Immutable after creation.
+ */
+ struct list_head cis_linkage;
+};
+
+
+/**
+ * Per-layer io operations.
+ * \see vvp_io_ops, lov_io_ops, lovsub_io_ops, osc_io_ops
+ */
+struct cl_io_operations {
+ /**
+ * Vector of io state transition methods for every io type.
+ *
+ * \see cl_page_operations::io
+ */
+ struct {
+ /**
+ * Prepare io iteration at a given layer.
+ *
+ * Called top-to-bottom at the beginning of each iteration of
+ * "io loop" (if it makes sense for this type of io). Here
+ * layer selects what work it will do during this iteration.
+ *
+ * \see cl_io_operations::cio_iter_fini()
+ */
+ int (*cio_iter_init) (const struct lu_env *env,
+ const struct cl_io_slice *slice);
+ /**
+ * Finalize io iteration.
+ *
+ * Called bottom-to-top at the end of each iteration of "io
+ * loop". Here layers can decide whether IO has to be
+ * continued.
+ *
+ * \see cl_io_operations::cio_iter_init()
+ */
+ void (*cio_iter_fini) (const struct lu_env *env,
+ const struct cl_io_slice *slice);
+ /**
+ * Collect locks for the current iteration of io.
+ *
+ * Called top-to-bottom to collect all locks necessary for
+ * this iteration. This methods shouldn't actually enqueue
+ * anything, instead it should post a lock through
+ * cl_io_lock_add(). Once all locks are collected, they are
+ * sorted and enqueued in the proper order.
+ */
+ int (*cio_lock) (const struct lu_env *env,
+ const struct cl_io_slice *slice);
+ /**
+ * Finalize unlocking.
+ *
+ * Called bottom-to-top to finish layer specific unlocking
+ * functionality, after generic code released all locks
+ * acquired by cl_io_operations::cio_lock().
+ */
+ void (*cio_unlock)(const struct lu_env *env,
+ const struct cl_io_slice *slice);
+ /**
+ * Start io iteration.
+ *
+ * Once all locks are acquired, called top-to-bottom to
+ * commence actual IO. In the current implementation,
+ * top-level vvp_io_{read,write}_start() does all the work
+ * synchronously by calling generic_file_*(), so other layers
+ * are called when everything is done.
+ */
+ int (*cio_start)(const struct lu_env *env,
+ const struct cl_io_slice *slice);
+ /**
+ * Called top-to-bottom at the end of io loop. Here layer
+ * might wait for an unfinished asynchronous io.
+ */
+ void (*cio_end) (const struct lu_env *env,
+ const struct cl_io_slice *slice);
+ /**
+ * Called bottom-to-top to notify layers that read/write IO
+ * iteration finished, with \a nob bytes transferred.
+ */
+ void (*cio_advance)(const struct lu_env *env,
+ const struct cl_io_slice *slice,
+ size_t nob);
+ /**
+ * Called once per io, bottom-to-top to release io resources.
+ */
+ void (*cio_fini) (const struct lu_env *env,
+ const struct cl_io_slice *slice);
+ } op[CIT_OP_NR];
+ struct {
+ /**
+ * Submit pages from \a queue->c2_qin for IO, and move
+ * successfully submitted pages into \a queue->c2_qout. Return
+ * non-zero if failed to submit even the single page. If
+ * submission failed after some pages were moved into \a
+ * queue->c2_qout, completion callback with non-zero ioret is
+ * executed on them.
+ */
+ int (*cio_submit)(const struct lu_env *env,
+ const struct cl_io_slice *slice,
+ enum cl_req_type crt,
+ struct cl_2queue *queue);
+ } req_op[CRT_NR];
+ /**
+ * Read missing page.
+ *
+ * Called by a top-level cl_io_operations::op[CIT_READ]::cio_start()
+ * method, when it hits not-up-to-date page in the range. Optional.
+ *
+ * \pre io->ci_type == CIT_READ
+ */
+ int (*cio_read_page)(const struct lu_env *env,
+ const struct cl_io_slice *slice,
+ const struct cl_page_slice *page);
+ /**
+ * Prepare write of a \a page. Called bottom-to-top by a top-level
+ * cl_io_operations::op[CIT_WRITE]::cio_start() to prepare page for
+ * get data from user-level buffer.
+ *
+ * \pre io->ci_type == CIT_WRITE
+ *
+ * \see vvp_io_prepare_write(), lov_io_prepare_write(),
+ * osc_io_prepare_write().
+ */
+ int (*cio_prepare_write)(const struct lu_env *env,
+ const struct cl_io_slice *slice,
+ const struct cl_page_slice *page,
+ unsigned from, unsigned to);
+ /**
+ *
+ * \pre io->ci_type == CIT_WRITE
+ *
+ * \see vvp_io_commit_write(), lov_io_commit_write(),
+ * osc_io_commit_write().
+ */
+ int (*cio_commit_write)(const struct lu_env *env,
+ const struct cl_io_slice *slice,
+ const struct cl_page_slice *page,
+ unsigned from, unsigned to);
+ /**
+ * Optional debugging helper. Print given io slice.
+ */
+ int (*cio_print)(const struct lu_env *env, void *cookie,
+ lu_printer_t p, const struct cl_io_slice *slice);
+};
+
+/**
+ * Flags to lock enqueue procedure.
+ * \ingroup cl_lock
+ */
+enum cl_enq_flags {
+ /**
+ * instruct server to not block, if conflicting lock is found. Instead
+ * -EWOULDBLOCK is returned immediately.
+ */
+ CEF_NONBLOCK = 0x00000001,
+ /**
+ * take lock asynchronously (out of order), as it cannot
+ * deadlock. This is for LDLM_FL_HAS_INTENT locks used for glimpsing.
+ */
+ CEF_ASYNC = 0x00000002,
+ /**
+ * tell the server to instruct (though a flag in the blocking ast) an
+ * owner of the conflicting lock, that it can drop dirty pages
+ * protected by this lock, without sending them to the server.
+ */
+ CEF_DISCARD_DATA = 0x00000004,
+ /**
+ * tell the sub layers that it must be a `real' lock. This is used for
+ * mmapped-buffer locks and glimpse locks that must be never converted
+ * into lockless mode.
+ *
+ * \see vvp_mmap_locks(), cl_glimpse_lock().
+ */
+ CEF_MUST = 0x00000008,
+ /**
+ * tell the sub layers that never request a `real' lock. This flag is
+ * not used currently.
+ *
+ * cl_io::ci_lockreq and CEF_{MUST,NEVER} flags specify lockless
+ * conversion policy: ci_lockreq describes generic information of lock
+ * requirement for this IO, especially for locks which belong to the
+ * object doing IO; however, lock itself may have precise requirements
+ * that are described by the enqueue flags.
+ */
+ CEF_NEVER = 0x00000010,
+ /**
+ * for async glimpse lock.
+ */
+ CEF_AGL = 0x00000020,
+ /**
+ * mask of enq_flags.
+ */
+ CEF_MASK = 0x0000003f,
+};
+
+/**
+ * Link between lock and io. Intermediate structure is needed, because the
+ * same lock can be part of multiple io's simultaneously.
+ */
+struct cl_io_lock_link {
+ /** linkage into one of cl_lockset lists. */
+ struct list_head cill_linkage;
+ struct cl_lock_descr cill_descr;
+ struct cl_lock *cill_lock;
+ /** optional destructor */
+ void (*cill_fini)(const struct lu_env *env,
+ struct cl_io_lock_link *link);
+};
+
+/**
+ * Lock-set represents a collection of locks, that io needs at a
+ * time. Generally speaking, client tries to avoid holding multiple locks when
+ * possible, because
+ *
+ * - holding extent locks over multiple ost's introduces the danger of
+ * "cascading timeouts";
+ *
+ * - holding multiple locks over the same ost is still dead-lock prone,
+ * see comment in osc_lock_enqueue(),
+ *
+ * but there are certain situations where this is unavoidable:
+ *
+ * - O_APPEND writes have to take [0, EOF] lock for correctness;
+ *
+ * - truncate has to take [new-size, EOF] lock for correctness;
+ *
+ * - SNS has to take locks across full stripe for correctness;
+ *
+ * - in the case when user level buffer, supplied to {read,write}(file0),
+ * is a part of a memory mapped lustre file, client has to take a dlm
+ * locks on file0, and all files that back up the buffer (or a part of
+ * the buffer, that is being processed in the current chunk, in any
+ * case, there are situations where at least 2 locks are necessary).
+ *
+ * In such cases we at least try to take locks in the same consistent
+ * order. To this end, all locks are first collected, then sorted, and then
+ * enqueued.
+ */
+struct cl_lockset {
+ /** locks to be acquired. */
+ struct list_head cls_todo;
+ /** locks currently being processed. */
+ struct list_head cls_curr;
+ /** locks acquired. */
+ struct list_head cls_done;
+};
+
+/**
+ * Lock requirements(demand) for IO. It should be cl_io_lock_req,
+ * but 'req' is always to be thought as 'request' :-)
+ */
+enum cl_io_lock_dmd {
+ /** Always lock data (e.g., O_APPEND). */
+ CILR_MANDATORY = 0,
+ /** Layers are free to decide between local and global locking. */
+ CILR_MAYBE,
+ /** Never lock: there is no cache (e.g., liblustre). */
+ CILR_NEVER
+};
+
+enum cl_fsync_mode {
+ /** start writeback, do not wait for them to finish */
+ CL_FSYNC_NONE = 0,
+ /** start writeback and wait for them to finish */
+ CL_FSYNC_LOCAL = 1,
+ /** discard all of dirty pages in a specific file range */
+ CL_FSYNC_DISCARD = 2,
+ /** start writeback and make sure they have reached storage before
+ * return. OST_SYNC RPC must be issued and finished */
+ CL_FSYNC_ALL = 3
+};
+
+struct cl_io_rw_common {
+ loff_t crw_pos;
+ size_t crw_count;
+ int crw_nonblock;
+};
+
+
+/**
+ * State for io.
+ *
+ * cl_io is shared by all threads participating in this IO (in current
+ * implementation only one thread advances IO, but parallel IO design and
+ * concurrent copy_*_user() require multiple threads acting on the same IO. It
+ * is up to these threads to serialize their activities, including updates to
+ * mutable cl_io fields.
+ */
+struct cl_io {
+ /** type of this IO. Immutable after creation. */
+ enum cl_io_type ci_type;
+ /** current state of cl_io state machine. */
+ enum cl_io_state ci_state;
+ /** main object this io is against. Immutable after creation. */
+ struct cl_object *ci_obj;
+ /**
+ * Upper layer io, of which this io is a part of. Immutable after
+ * creation.
+ */
+ struct cl_io *ci_parent;
+ /** List of slices. Immutable after creation. */
+ struct list_head ci_layers;
+ /** list of locks (to be) acquired by this io. */
+ struct cl_lockset ci_lockset;
+ /** lock requirements, this is just a help info for sublayers. */
+ enum cl_io_lock_dmd ci_lockreq;
+ union {
+ struct cl_rd_io {
+ struct cl_io_rw_common rd;
+ } ci_rd;
+ struct cl_wr_io {
+ struct cl_io_rw_common wr;
+ int wr_append;
+ int wr_sync;
+ } ci_wr;
+ struct cl_io_rw_common ci_rw;
+ struct cl_setattr_io {
+ struct ost_lvb sa_attr;
+ unsigned int sa_valid;
+ struct obd_capa *sa_capa;
+ } ci_setattr;
+ struct cl_fault_io {
+ /** page index within file. */
+ pgoff_t ft_index;
+ /** bytes valid byte on a faulted page. */
+ int ft_nob;
+ /** writable page? for nopage() only */
+ int ft_writable;
+ /** page of an executable? */
+ int ft_executable;
+ /** page_mkwrite() */
+ int ft_mkwrite;
+ /** resulting page */
+ struct cl_page *ft_page;
+ } ci_fault;
+ struct cl_fsync_io {
+ loff_t fi_start;
+ loff_t fi_end;
+ struct obd_capa *fi_capa;
+ /** file system level fid */
+ struct lu_fid *fi_fid;
+ enum cl_fsync_mode fi_mode;
+ /* how many pages were written/discarded */
+ unsigned int fi_nr_written;
+ } ci_fsync;
+ } u;
+ struct cl_2queue ci_queue;
+ size_t ci_nob;
+ int ci_result;
+ unsigned int ci_continue:1,
+ /**
+ * This io has held grouplock, to inform sublayers that
+ * don't do lockless i/o.
+ */
+ ci_no_srvlock:1,
+ /**
+ * The whole IO need to be restarted because layout has been changed
+ */
+ ci_need_restart:1,
+ /**
+ * to not refresh layout - the IO issuer knows that the layout won't
+ * change(page operations, layout change causes all page to be
+ * discarded), or it doesn't matter if it changes(sync).
+ */
+ ci_ignore_layout:1,
+ /**
+ * Check if layout changed after the IO finishes. Mainly for HSM
+ * requirement. If IO occurs to openning files, it doesn't need to
+ * verify layout because HSM won't release openning files.
+ * Right now, only two operations need to verify layout: glimpse
+ * and setattr.
+ */
+ ci_verify_layout:1,
+ /**
+ * file is released, restore has to to be triggered by vvp layer
+ */
+ ci_restore_needed:1,
+ /**
+ * O_NOATIME
+ */
+ ci_noatime:1;
+ /**
+ * Number of pages owned by this IO. For invariant checking.
+ */
+ unsigned ci_owned_nr;
+};
+
+/** @} cl_io */
+
+/** \addtogroup cl_req cl_req
+ * @{ */
+/** \struct cl_req
+ * Transfer.
+ *
+ * There are two possible modes of transfer initiation on the client:
+ *
+ * - immediate transfer: this is started when a high level io wants a page
+ * or a collection of pages to be transferred right away. Examples:
+ * read-ahead, synchronous read in the case of non-page aligned write,
+ * page write-out as a part of extent lock cancellation, page write-out
+ * as a part of memory cleansing. Immediate transfer can be both
+ * cl_req_type::CRT_READ and cl_req_type::CRT_WRITE;
+ *
+ * - opportunistic transfer (cl_req_type::CRT_WRITE only), that happens
+ * when io wants to transfer a page to the server some time later, when
+ * it can be done efficiently. Example: pages dirtied by the write(2)
+ * path.
+ *
+ * In any case, transfer takes place in the form of a cl_req, which is a
+ * representation for a network RPC.
+ *
+ * Pages queued for an opportunistic transfer are cached until it is decided
+ * that efficient RPC can be composed of them. This decision is made by "a
+ * req-formation engine", currently implemented as a part of osc
+ * layer. Req-formation depends on many factors: the size of the resulting
+ * RPC, whether or not multi-object RPCs are supported by the server,
+ * max-rpc-in-flight limitations, size of the dirty cache, etc.
+ *
+ * For the immediate transfer io submits a cl_page_list, that req-formation
+ * engine slices into cl_req's, possibly adding cached pages to some of
+ * the resulting req's.
+ *
+ * Whenever a page from cl_page_list is added to a newly constructed req, its
+ * cl_page_operations::cpo_prep() layer methods are called. At that moment,
+ * page state is atomically changed from cl_page_state::CPS_OWNED to
+ * cl_page_state::CPS_PAGEOUT or cl_page_state::CPS_PAGEIN, cl_page::cp_owner
+ * is zeroed, and cl_page::cp_req is set to the
+ * req. cl_page_operations::cpo_prep() method at the particular layer might
+ * return -EALREADY to indicate that it does not need to submit this page
+ * at all. This is possible, for example, if page, submitted for read,
+ * became up-to-date in the meantime; and for write, the page don't have
+ * dirty bit marked. \see cl_io_submit_rw()
+ *
+ * Whenever a cached page is added to a newly constructed req, its
+ * cl_page_operations::cpo_make_ready() layer methods are called. At that
+ * moment, page state is atomically changed from cl_page_state::CPS_CACHED to
+ * cl_page_state::CPS_PAGEOUT, and cl_page::cp_req is set to
+ * req. cl_page_operations::cpo_make_ready() method at the particular layer
+ * might return -EAGAIN to indicate that this page is not eligible for the
+ * transfer right now.
+ *
+ * FUTURE
+ *
+ * Plan is to divide transfers into "priority bands" (indicated when
+ * submitting cl_page_list, and queuing a page for the opportunistic transfer)
+ * and allow glueing of cached pages to immediate transfers only within single
+ * band. This would make high priority transfers (like lock cancellation or
+ * memory pressure induced write-out) really high priority.
+ *
+ */
+
+/**
+ * Per-transfer attributes.
+ */
+struct cl_req_attr {
+ /** Generic attributes for the server consumption. */
+ struct obdo *cra_oa;
+ /** Capability. */
+ struct obd_capa *cra_capa;
+ /** Jobid */
+ char cra_jobid[JOBSTATS_JOBID_SIZE];
+};
+
+/**
+ * Transfer request operations definable at every layer.
+ *
+ * Concurrency: transfer formation engine synchronizes calls to all transfer
+ * methods.
+ */
+struct cl_req_operations {
+ /**
+ * Invoked top-to-bottom by cl_req_prep() when transfer formation is
+ * complete (all pages are added).
+ *
+ * \see osc_req_prep()
+ */
+ int (*cro_prep)(const struct lu_env *env,
+ const struct cl_req_slice *slice);
+ /**
+ * Called top-to-bottom to fill in \a oa fields. This is called twice
+ * with different flags, see bug 10150 and osc_build_req().
+ *
+ * \param obj an object from cl_req which attributes are to be set in
+ * \a oa.
+ *
+ * \param oa struct obdo where attributes are placed
+ *
+ * \param flags \a oa fields to be filled.
+ */
+ void (*cro_attr_set)(const struct lu_env *env,
+ const struct cl_req_slice *slice,
+ const struct cl_object *obj,
+ struct cl_req_attr *attr, u64 flags);
+ /**
+ * Called top-to-bottom from cl_req_completion() to notify layers that
+ * transfer completed. Has to free all state allocated by
+ * cl_device_operations::cdo_req_init().
+ */
+ void (*cro_completion)(const struct lu_env *env,
+ const struct cl_req_slice *slice, int ioret);
+};
+
+/**
+ * A per-object state that (potentially multi-object) transfer request keeps.
+ */
+struct cl_req_obj {
+ /** object itself */
+ struct cl_object *ro_obj;
+ /** reference to cl_req_obj::ro_obj. For debugging. */
+ struct lu_ref_link ro_obj_ref;
+ /* something else? Number of pages for a given object? */
+};
+
+/**
+ * Transfer request.
+ *
+ * Transfer requests are not reference counted, because IO sub-system owns
+ * them exclusively and knows when to free them.
+ *
+ * Life cycle.
+ *
+ * cl_req is created by cl_req_alloc() that calls
+ * cl_device_operations::cdo_req_init() device methods to allocate per-req
+ * state in every layer.
+ *
+ * Then pages are added (cl_req_page_add()), req keeps track of all objects it
+ * contains pages for.
+ *
+ * Once all pages were collected, cl_page_operations::cpo_prep() method is
+ * called top-to-bottom. At that point layers can modify req, let it pass, or
+ * deny it completely. This is to support things like SNS that have transfer
+ * ordering requirements invisible to the individual req-formation engine.
+ *
+ * On transfer completion (or transfer timeout, or failure to initiate the
+ * transfer of an allocated req), cl_req_operations::cro_completion() method
+ * is called, after execution of cl_page_operations::cpo_completion() of all
+ * req's pages.
+ */
+struct cl_req {
+ enum cl_req_type crq_type;
+ /** A list of pages being transferred */
+ struct list_head crq_pages;
+ /** Number of pages in cl_req::crq_pages */
+ unsigned crq_nrpages;
+ /** An array of objects which pages are in ->crq_pages */
+ struct cl_req_obj *crq_o;
+ /** Number of elements in cl_req::crq_objs[] */
+ unsigned crq_nrobjs;
+ struct list_head crq_layers;
+};
+
+/**
+ * Per-layer state for request.
+ */
+struct cl_req_slice {
+ struct cl_req *crs_req;
+ struct cl_device *crs_dev;
+ struct list_head crs_linkage;
+ const struct cl_req_operations *crs_ops;
+};
+
+/* @} cl_req */
+
+enum cache_stats_item {
+ /** how many cache lookups were performed */
+ CS_lookup = 0,
+ /** how many times cache lookup resulted in a hit */
+ CS_hit,
+ /** how many entities are in the cache right now */
+ CS_total,
+ /** how many entities in the cache are actively used (and cannot be
+ * evicted) right now */
+ CS_busy,
+ /** how many entities were created at all */
+ CS_create,
+ CS_NR
+};
+
+#define CS_NAMES { "lookup", "hit", "total", "busy", "create" }
+
+/**
+ * Stats for a generic cache (similar to inode, lu_object, etc. caches).
+ */
+struct cache_stats {
+ const char *cs_name;
+ atomic_t cs_stats[CS_NR];
+};
+
+/** These are not exported so far */
+void cache_stats_init (struct cache_stats *cs, const char *name);
+
+/**
+ * Client-side site. This represents particular client stack. "Global"
+ * variables should (directly or indirectly) be added here to allow multiple
+ * clients to co-exist in the single address space.
+ */
+struct cl_site {
+ struct lu_site cs_lu;
+ /**
+ * Statistical counters. Atomics do not scale, something better like
+ * per-cpu counters is needed.
+ *
+ * These are exported as /proc/fs/lustre/llite/.../site
+ *
+ * When interpreting keep in mind that both sub-locks (and sub-pages)
+ * and top-locks (and top-pages) are accounted here.
+ */
+ struct cache_stats cs_pages;
+ struct cache_stats cs_locks;
+ atomic_t cs_pages_state[CPS_NR];
+ atomic_t cs_locks_state[CLS_NR];
+};
+
+int cl_site_init (struct cl_site *s, struct cl_device *top);
+void cl_site_fini (struct cl_site *s);
+void cl_stack_fini(const struct lu_env *env, struct cl_device *cl);
+
+/**
+ * Output client site statistical counters into a buffer. Suitable for
+ * ll_rd_*()-style functions.
+ */
+int cl_site_stats_print(const struct cl_site *site, struct seq_file *m);
+
+/**
+ * \name helpers
+ *
+ * Type conversion and accessory functions.
+ */
+/** @{ */
+
+static inline struct cl_site *lu2cl_site(const struct lu_site *site)
+{
+ return container_of(site, struct cl_site, cs_lu);
+}
+
+static inline int lu_device_is_cl(const struct lu_device *d)
+{
+ return d->ld_type->ldt_tags & LU_DEVICE_CL;
+}
+
+static inline struct cl_device *lu2cl_dev(const struct lu_device *d)
+{
+ LASSERT(d == NULL || IS_ERR(d) || lu_device_is_cl(d));
+ return container_of0(d, struct cl_device, cd_lu_dev);
+}
+
+static inline struct lu_device *cl2lu_dev(struct cl_device *d)
+{
+ return &d->cd_lu_dev;
+}
+
+static inline struct cl_object *lu2cl(const struct lu_object *o)
+{
+ LASSERT(o == NULL || IS_ERR(o) || lu_device_is_cl(o->lo_dev));
+ return container_of0(o, struct cl_object, co_lu);
+}
+
+static inline const struct cl_object_conf *
+lu2cl_conf(const struct lu_object_conf *conf)
+{
+ return container_of0(conf, struct cl_object_conf, coc_lu);
+}
+
+static inline struct cl_object *cl_object_next(const struct cl_object *obj)
+{
+ return obj ? lu2cl(lu_object_next(&obj->co_lu)) : NULL;
+}
+
+static inline struct cl_device *cl_object_device(const struct cl_object *o)
+{
+ LASSERT(o == NULL || IS_ERR(o) || lu_device_is_cl(o->co_lu.lo_dev));
+ return container_of0(o->co_lu.lo_dev, struct cl_device, cd_lu_dev);
+}
+
+static inline struct cl_object_header *luh2coh(const struct lu_object_header *h)
+{
+ return container_of0(h, struct cl_object_header, coh_lu);
+}
+
+static inline struct cl_site *cl_object_site(const struct cl_object *obj)
+{
+ return lu2cl_site(obj->co_lu.lo_dev->ld_site);
+}
+
+static inline
+struct cl_object_header *cl_object_header(const struct cl_object *obj)
+{
+ return luh2coh(obj->co_lu.lo_header);
+}
+
+static inline int cl_device_init(struct cl_device *d, struct lu_device_type *t)
+{
+ return lu_device_init(&d->cd_lu_dev, t);
+}
+
+static inline void cl_device_fini(struct cl_device *d)
+{
+ lu_device_fini(&d->cd_lu_dev);
+}
+
+void cl_page_slice_add(struct cl_page *page, struct cl_page_slice *slice,
+ struct cl_object *obj,
+ const struct cl_page_operations *ops);
+void cl_lock_slice_add(struct cl_lock *lock, struct cl_lock_slice *slice,
+ struct cl_object *obj,
+ const struct cl_lock_operations *ops);
+void cl_io_slice_add(struct cl_io *io, struct cl_io_slice *slice,
+ struct cl_object *obj, const struct cl_io_operations *ops);
+void cl_req_slice_add(struct cl_req *req, struct cl_req_slice *slice,
+ struct cl_device *dev,
+ const struct cl_req_operations *ops);
+/** @} helpers */
+
+/** \defgroup cl_object cl_object
+ * @{ */
+struct cl_object *cl_object_top (struct cl_object *o);
+struct cl_object *cl_object_find(const struct lu_env *env, struct cl_device *cd,
+ const struct lu_fid *fid,
+ const struct cl_object_conf *c);
+
+int cl_object_header_init(struct cl_object_header *h);
+void cl_object_header_fini(struct cl_object_header *h);
+void cl_object_put (const struct lu_env *env, struct cl_object *o);
+void cl_object_get (struct cl_object *o);
+void cl_object_attr_lock (struct cl_object *o);
+void cl_object_attr_unlock(struct cl_object *o);
+int cl_object_attr_get (const struct lu_env *env, struct cl_object *obj,
+ struct cl_attr *attr);
+int cl_object_attr_set (const struct lu_env *env, struct cl_object *obj,
+ const struct cl_attr *attr, unsigned valid);
+int cl_object_glimpse (const struct lu_env *env, struct cl_object *obj,
+ struct ost_lvb *lvb);
+int cl_conf_set (const struct lu_env *env, struct cl_object *obj,
+ const struct cl_object_conf *conf);
+void cl_object_prune (const struct lu_env *env, struct cl_object *obj);
+void cl_object_kill (const struct lu_env *env, struct cl_object *obj);
+int cl_object_has_locks (struct cl_object *obj);
+
+/**
+ * Returns true, iff \a o0 and \a o1 are slices of the same object.
+ */
+static inline int cl_object_same(struct cl_object *o0, struct cl_object *o1)
+{
+ return cl_object_header(o0) == cl_object_header(o1);
+}
+
+static inline void cl_object_page_init(struct cl_object *clob, int size)
+{
+ clob->co_slice_off = cl_object_header(clob)->coh_page_bufsize;
+ cl_object_header(clob)->coh_page_bufsize += ALIGN(size, 8);
+}
+
+static inline void *cl_object_page_slice(struct cl_object *clob,
+ struct cl_page *page)
+{
+ return (void *)((char *)page + clob->co_slice_off);
+}
+
+/** @} cl_object */
+
+/** \defgroup cl_page cl_page
+ * @{ */
+enum {
+ CLP_GANG_OKAY = 0,
+ CLP_GANG_RESCHED,
+ CLP_GANG_AGAIN,
+ CLP_GANG_ABORT
+};
+
+/* callback of cl_page_gang_lookup() */
+typedef int (*cl_page_gang_cb_t) (const struct lu_env *, struct cl_io *,
+ struct cl_page *, void *);
+int cl_page_gang_lookup (const struct lu_env *env,
+ struct cl_object *obj,
+ struct cl_io *io,
+ pgoff_t start, pgoff_t end,
+ cl_page_gang_cb_t cb, void *cbdata);
+struct cl_page *cl_page_lookup (struct cl_object_header *hdr,
+ pgoff_t index);
+struct cl_page *cl_page_find (const struct lu_env *env,
+ struct cl_object *obj,
+ pgoff_t idx, struct page *vmpage,
+ enum cl_page_type type);
+struct cl_page *cl_page_find_sub (const struct lu_env *env,
+ struct cl_object *obj,
+ pgoff_t idx, struct page *vmpage,
+ struct cl_page *parent);
+void cl_page_get (struct cl_page *page);
+void cl_page_put (const struct lu_env *env,
+ struct cl_page *page);
+void cl_page_print (const struct lu_env *env, void *cookie,
+ lu_printer_t printer,
+ const struct cl_page *pg);
+void cl_page_header_print(const struct lu_env *env, void *cookie,
+ lu_printer_t printer,
+ const struct cl_page *pg);
+struct page *cl_page_vmpage (const struct lu_env *env,
+ struct cl_page *page);
+struct cl_page *cl_vmpage_page (struct page *vmpage, struct cl_object *obj);
+struct cl_page *cl_page_top (struct cl_page *page);
+
+const struct cl_page_slice *cl_page_at(const struct cl_page *page,
+ const struct lu_device_type *dtype);
+
+/**
+ * \name ownership
+ *
+ * Functions dealing with the ownership of page by io.
+ */
+/** @{ */
+
+int cl_page_own (const struct lu_env *env,
+ struct cl_io *io, struct cl_page *page);
+int cl_page_own_try (const struct lu_env *env,
+ struct cl_io *io, struct cl_page *page);
+void cl_page_assume (const struct lu_env *env,
+ struct cl_io *io, struct cl_page *page);
+void cl_page_unassume (const struct lu_env *env,
+ struct cl_io *io, struct cl_page *pg);
+void cl_page_disown (const struct lu_env *env,
+ struct cl_io *io, struct cl_page *page);
+int cl_page_is_owned (const struct cl_page *pg, const struct cl_io *io);
+
+/** @} ownership */
+
+/**
+ * \name transfer
+ *
+ * Functions dealing with the preparation of a page for a transfer, and
+ * tracking transfer state.
+ */
+/** @{ */
+int cl_page_prep (const struct lu_env *env, struct cl_io *io,
+ struct cl_page *pg, enum cl_req_type crt);
+void cl_page_completion (const struct lu_env *env,
+ struct cl_page *pg, enum cl_req_type crt, int ioret);
+int cl_page_make_ready (const struct lu_env *env, struct cl_page *pg,
+ enum cl_req_type crt);
+int cl_page_cache_add (const struct lu_env *env, struct cl_io *io,
+ struct cl_page *pg, enum cl_req_type crt);
+void cl_page_clip (const struct lu_env *env, struct cl_page *pg,
+ int from, int to);
+int cl_page_cancel (const struct lu_env *env, struct cl_page *page);
+int cl_page_flush (const struct lu_env *env, struct cl_io *io,
+ struct cl_page *pg);
+
+/** @} transfer */
+
+
+/**
+ * \name helper routines
+ * Functions to discard, delete and export a cl_page.
+ */
+/** @{ */
+void cl_page_discard (const struct lu_env *env, struct cl_io *io,
+ struct cl_page *pg);
+void cl_page_delete (const struct lu_env *env, struct cl_page *pg);
+int cl_page_unmap (const struct lu_env *env, struct cl_io *io,
+ struct cl_page *pg);
+int cl_page_is_vmlocked (const struct lu_env *env,
+ const struct cl_page *pg);
+void cl_page_export (const struct lu_env *env,
+ struct cl_page *pg, int uptodate);
+int cl_page_is_under_lock(const struct lu_env *env, struct cl_io *io,
+ struct cl_page *page);
+loff_t cl_offset (const struct cl_object *obj, pgoff_t idx);
+pgoff_t cl_index (const struct cl_object *obj, loff_t offset);
+int cl_page_size (const struct cl_object *obj);
+int cl_pages_prune (const struct lu_env *env, struct cl_object *obj);
+
+void cl_lock_print (const struct lu_env *env, void *cookie,
+ lu_printer_t printer, const struct cl_lock *lock);
+void cl_lock_descr_print(const struct lu_env *env, void *cookie,
+ lu_printer_t printer,
+ const struct cl_lock_descr *descr);
+/* @} helper */
+
+/** @} cl_page */
+
+/** \defgroup cl_lock cl_lock
+ * @{ */
+
+struct cl_lock *cl_lock_hold(const struct lu_env *env, const struct cl_io *io,
+ const struct cl_lock_descr *need,
+ const char *scope, const void *source);
+struct cl_lock *cl_lock_peek(const struct lu_env *env, const struct cl_io *io,
+ const struct cl_lock_descr *need,
+ const char *scope, const void *source);
+struct cl_lock *cl_lock_request(const struct lu_env *env, struct cl_io *io,
+ const struct cl_lock_descr *need,
+ const char *scope, const void *source);
+struct cl_lock *cl_lock_at_pgoff(const struct lu_env *env,
+ struct cl_object *obj, pgoff_t index,
+ struct cl_lock *except, int pending,
+ int canceld);
+static inline struct cl_lock *cl_lock_at_page(const struct lu_env *env,
+ struct cl_object *obj,
+ struct cl_page *page,
+ struct cl_lock *except,
+ int pending, int canceld)
+{
+ LASSERT(cl_object_header(obj) == cl_object_header(page->cp_obj));
+ return cl_lock_at_pgoff(env, obj, page->cp_index, except,
+ pending, canceld);
+}
+
+const struct cl_lock_slice *cl_lock_at(const struct cl_lock *lock,
+ const struct lu_device_type *dtype);
+
+void cl_lock_get (struct cl_lock *lock);
+void cl_lock_get_trust (struct cl_lock *lock);
+void cl_lock_put (const struct lu_env *env, struct cl_lock *lock);
+void cl_lock_hold_add (const struct lu_env *env, struct cl_lock *lock,
+ const char *scope, const void *source);
+void cl_lock_hold_release(const struct lu_env *env, struct cl_lock *lock,
+ const char *scope, const void *source);
+void cl_lock_unhold (const struct lu_env *env, struct cl_lock *lock,
+ const char *scope, const void *source);
+void cl_lock_release (const struct lu_env *env, struct cl_lock *lock,
+ const char *scope, const void *source);
+void cl_lock_user_add (const struct lu_env *env, struct cl_lock *lock);
+void cl_lock_user_del (const struct lu_env *env, struct cl_lock *lock);
+
+enum cl_lock_state cl_lock_intransit(const struct lu_env *env,
+ struct cl_lock *lock);
+void cl_lock_extransit(const struct lu_env *env, struct cl_lock *lock,
+ enum cl_lock_state state);
+int cl_lock_is_intransit(struct cl_lock *lock);
+
+int cl_lock_enqueue_wait(const struct lu_env *env, struct cl_lock *lock,
+ int keep_mutex);
+
+/** \name statemachine statemachine
+ * Interface to lock state machine consists of 3 parts:
+ *
+ * - "try" functions that attempt to effect a state transition. If state
+ * transition is not possible right now (e.g., if it has to wait for some
+ * asynchronous event to occur), these functions return
+ * cl_lock_transition::CLO_WAIT.
+ *
+ * - "non-try" functions that implement synchronous blocking interface on
+ * top of non-blocking "try" functions. These functions repeatedly call
+ * corresponding "try" versions, and if state transition is not possible
+ * immediately, wait for lock state change.
+ *
+ * - methods from cl_lock_operations, called by "try" functions. Lock can
+ * be advanced to the target state only when all layers voted that they
+ * are ready for this transition. "Try" functions call methods under lock
+ * mutex. If a layer had to release a mutex, it re-acquires it and returns
+ * cl_lock_transition::CLO_REPEAT, causing "try" function to call all
+ * layers again.
+ *
+ * TRY NON-TRY METHOD FINAL STATE
+ *
+ * cl_enqueue_try() cl_enqueue() cl_lock_operations::clo_enqueue() CLS_ENQUEUED
+ *
+ * cl_wait_try() cl_wait() cl_lock_operations::clo_wait() CLS_HELD
+ *
+ * cl_unuse_try() cl_unuse() cl_lock_operations::clo_unuse() CLS_CACHED
+ *
+ * cl_use_try() NONE cl_lock_operations::clo_use() CLS_HELD
+ *
+ * @{ */
+
+int cl_enqueue (const struct lu_env *env, struct cl_lock *lock,
+ struct cl_io *io, __u32 flags);
+int cl_wait (const struct lu_env *env, struct cl_lock *lock);
+void cl_unuse (const struct lu_env *env, struct cl_lock *lock);
+int cl_enqueue_try(const struct lu_env *env, struct cl_lock *lock,
+ struct cl_io *io, __u32 flags);
+int cl_unuse_try (const struct lu_env *env, struct cl_lock *lock);
+int cl_wait_try (const struct lu_env *env, struct cl_lock *lock);
+int cl_use_try (const struct lu_env *env, struct cl_lock *lock, int atomic);
+
+/** @} statemachine */
+
+void cl_lock_signal (const struct lu_env *env, struct cl_lock *lock);
+int cl_lock_state_wait (const struct lu_env *env, struct cl_lock *lock);
+void cl_lock_state_set (const struct lu_env *env, struct cl_lock *lock,
+ enum cl_lock_state state);
+int cl_queue_match (const struct list_head *queue,
+ const struct cl_lock_descr *need);
+
+void cl_lock_mutex_get (const struct lu_env *env, struct cl_lock *lock);
+int cl_lock_mutex_try (const struct lu_env *env, struct cl_lock *lock);
+void cl_lock_mutex_put (const struct lu_env *env, struct cl_lock *lock);
+int cl_lock_is_mutexed (struct cl_lock *lock);
+int cl_lock_nr_mutexed (const struct lu_env *env);
+int cl_lock_discard_pages(const struct lu_env *env, struct cl_lock *lock);
+int cl_lock_ext_match (const struct cl_lock_descr *has,
+ const struct cl_lock_descr *need);
+int cl_lock_descr_match(const struct cl_lock_descr *has,
+ const struct cl_lock_descr *need);
+int cl_lock_mode_match (enum cl_lock_mode has, enum cl_lock_mode need);
+int cl_lock_modify (const struct lu_env *env, struct cl_lock *lock,
+ const struct cl_lock_descr *desc);
+
+void cl_lock_closure_init (const struct lu_env *env,
+ struct cl_lock_closure *closure,
+ struct cl_lock *origin, int wait);
+void cl_lock_closure_fini (struct cl_lock_closure *closure);
+int cl_lock_closure_build(const struct lu_env *env, struct cl_lock *lock,
+ struct cl_lock_closure *closure);
+void cl_lock_disclosure (const struct lu_env *env,
+ struct cl_lock_closure *closure);
+int cl_lock_enclosure (const struct lu_env *env, struct cl_lock *lock,
+ struct cl_lock_closure *closure);
+
+void cl_lock_cancel(const struct lu_env *env, struct cl_lock *lock);
+void cl_lock_delete(const struct lu_env *env, struct cl_lock *lock);
+void cl_lock_error (const struct lu_env *env, struct cl_lock *lock, int error);
+void cl_locks_prune(const struct lu_env *env, struct cl_object *obj, int wait);
+
+unsigned long cl_lock_weigh(const struct lu_env *env, struct cl_lock *lock);
+
+/** @} cl_lock */
+
+/** \defgroup cl_io cl_io
+ * @{ */
+
+int cl_io_init (const struct lu_env *env, struct cl_io *io,
+ enum cl_io_type iot, struct cl_object *obj);
+int cl_io_sub_init (const struct lu_env *env, struct cl_io *io,
+ enum cl_io_type iot, struct cl_object *obj);
+int cl_io_rw_init (const struct lu_env *env, struct cl_io *io,
+ enum cl_io_type iot, loff_t pos, size_t count);
+int cl_io_loop (const struct lu_env *env, struct cl_io *io);
+
+void cl_io_fini (const struct lu_env *env, struct cl_io *io);
+int cl_io_iter_init (const struct lu_env *env, struct cl_io *io);
+void cl_io_iter_fini (const struct lu_env *env, struct cl_io *io);
+int cl_io_lock (const struct lu_env *env, struct cl_io *io);
+void cl_io_unlock (const struct lu_env *env, struct cl_io *io);
+int cl_io_start (const struct lu_env *env, struct cl_io *io);
+void cl_io_end (const struct lu_env *env, struct cl_io *io);
+int cl_io_lock_add (const struct lu_env *env, struct cl_io *io,
+ struct cl_io_lock_link *link);
+int cl_io_lock_alloc_add(const struct lu_env *env, struct cl_io *io,
+ struct cl_lock_descr *descr);
+int cl_io_read_page (const struct lu_env *env, struct cl_io *io,
+ struct cl_page *page);
+int cl_io_prepare_write(const struct lu_env *env, struct cl_io *io,
+ struct cl_page *page, unsigned from, unsigned to);
+int cl_io_commit_write (const struct lu_env *env, struct cl_io *io,
+ struct cl_page *page, unsigned from, unsigned to);
+int cl_io_submit_rw (const struct lu_env *env, struct cl_io *io,
+ enum cl_req_type iot, struct cl_2queue *queue);
+int cl_io_submit_sync (const struct lu_env *env, struct cl_io *io,
+ enum cl_req_type iot, struct cl_2queue *queue,
+ long timeout);
+void cl_io_rw_advance (const struct lu_env *env, struct cl_io *io,
+ size_t nob);
+int cl_io_cancel (const struct lu_env *env, struct cl_io *io,
+ struct cl_page_list *queue);
+int cl_io_is_going (const struct lu_env *env);
+
+/**
+ * True, iff \a io is an O_APPEND write(2).
+ */
+static inline int cl_io_is_append(const struct cl_io *io)
+{
+ return io->ci_type == CIT_WRITE && io->u.ci_wr.wr_append;
+}
+
+static inline int cl_io_is_sync_write(const struct cl_io *io)
+{
+ return io->ci_type == CIT_WRITE && io->u.ci_wr.wr_sync;
+}
+
+static inline int cl_io_is_mkwrite(const struct cl_io *io)
+{
+ return io->ci_type == CIT_FAULT && io->u.ci_fault.ft_mkwrite;
+}
+
+/**
+ * True, iff \a io is a truncate(2).
+ */
+static inline int cl_io_is_trunc(const struct cl_io *io)
+{
+ return io->ci_type == CIT_SETATTR &&
+ (io->u.ci_setattr.sa_valid & ATTR_SIZE);
+}
+
+struct cl_io *cl_io_top(struct cl_io *io);
+
+void cl_io_print(const struct lu_env *env, void *cookie,
+ lu_printer_t printer, const struct cl_io *io);
+
+#define CL_IO_SLICE_CLEAN(foo_io, base) \
+do { \
+ typeof(foo_io) __foo_io = (foo_io); \
+ \
+ CLASSERT(offsetof(typeof(*__foo_io), base) == 0); \
+ memset(&__foo_io->base + 1, 0, \
+ sizeof(*__foo_io) - sizeof(__foo_io->base)); \
+} while (0)
+
+/** @} cl_io */
+
+/** \defgroup cl_page_list cl_page_list
+ * @{ */
+
+/**
+ * Last page in the page list.
+ */
+static inline struct cl_page *cl_page_list_last(struct cl_page_list *plist)
+{
+ LASSERT(plist->pl_nr > 0);
+ return list_entry(plist->pl_pages.prev, struct cl_page, cp_batch);
+}
+
+/**
+ * Iterate over pages in a page list.
+ */
+#define cl_page_list_for_each(page, list) \
+ list_for_each_entry((page), &(list)->pl_pages, cp_batch)
+
+/**
+ * Iterate over pages in a page list, taking possible removals into account.
+ */
+#define cl_page_list_for_each_safe(page, temp, list) \
+ list_for_each_entry_safe((page), (temp), &(list)->pl_pages, cp_batch)
+
+void cl_page_list_init (struct cl_page_list *plist);
+void cl_page_list_add (struct cl_page_list *plist, struct cl_page *page);
+void cl_page_list_move (struct cl_page_list *dst, struct cl_page_list *src,
+ struct cl_page *page);
+void cl_page_list_splice (struct cl_page_list *list,
+ struct cl_page_list *head);
+void cl_page_list_del (const struct lu_env *env,
+ struct cl_page_list *plist, struct cl_page *page);
+void cl_page_list_disown (const struct lu_env *env,
+ struct cl_io *io, struct cl_page_list *plist);
+int cl_page_list_own (const struct lu_env *env,
+ struct cl_io *io, struct cl_page_list *plist);
+void cl_page_list_assume (const struct lu_env *env,
+ struct cl_io *io, struct cl_page_list *plist);
+void cl_page_list_discard(const struct lu_env *env,
+ struct cl_io *io, struct cl_page_list *plist);
+int cl_page_list_unmap (const struct lu_env *env,
+ struct cl_io *io, struct cl_page_list *plist);
+void cl_page_list_fini (const struct lu_env *env, struct cl_page_list *plist);
+
+void cl_2queue_init (struct cl_2queue *queue);
+void cl_2queue_add (struct cl_2queue *queue, struct cl_page *page);
+void cl_2queue_disown (const struct lu_env *env,
+ struct cl_io *io, struct cl_2queue *queue);
+void cl_2queue_assume (const struct lu_env *env,
+ struct cl_io *io, struct cl_2queue *queue);
+void cl_2queue_discard (const struct lu_env *env,
+ struct cl_io *io, struct cl_2queue *queue);
+void cl_2queue_fini (const struct lu_env *env, struct cl_2queue *queue);
+void cl_2queue_init_page(struct cl_2queue *queue, struct cl_page *page);
+
+/** @} cl_page_list */
+
+/** \defgroup cl_req cl_req
+ * @{ */
+struct cl_req *cl_req_alloc(const struct lu_env *env, struct cl_page *page,
+ enum cl_req_type crt, int nr_objects);
+
+void cl_req_page_add (const struct lu_env *env, struct cl_req *req,
+ struct cl_page *page);
+void cl_req_page_done (const struct lu_env *env, struct cl_page *page);
+int cl_req_prep (const struct lu_env *env, struct cl_req *req);
+void cl_req_attr_set (const struct lu_env *env, struct cl_req *req,
+ struct cl_req_attr *attr, u64 flags);
+void cl_req_completion(const struct lu_env *env, struct cl_req *req, int ioret);
+
+/** \defgroup cl_sync_io cl_sync_io
+ * @{ */
+
+/**
+ * Anchor for synchronous transfer. This is allocated on a stack by thread
+ * doing synchronous transfer, and a pointer to this structure is set up in
+ * every page submitted for transfer. Transfer completion routine updates
+ * anchor and wakes up waiting thread when transfer is complete.
+ */
+struct cl_sync_io {
+ /** number of pages yet to be transferred. */
+ atomic_t csi_sync_nr;
+ /** error code. */
+ int csi_sync_rc;
+ /** barrier of destroy this structure */
+ atomic_t csi_barrier;
+ /** completion to be signaled when transfer is complete. */
+ wait_queue_head_t csi_waitq;
+};
+
+void cl_sync_io_init(struct cl_sync_io *anchor, int nrpages);
+int cl_sync_io_wait(const struct lu_env *env, struct cl_io *io,
+ struct cl_page_list *queue, struct cl_sync_io *anchor,
+ long timeout);
+void cl_sync_io_note(struct cl_sync_io *anchor, int ioret);
+
+/** @} cl_sync_io */
+
+/** @} cl_req */
+
+/** \defgroup cl_env cl_env
+ *
+ * lu_env handling for a client.
+ *
+ * lu_env is an environment within which lustre code executes. Its major part
+ * is lu_context---a fast memory allocation mechanism that is used to conserve
+ * precious kernel stack space. Originally lu_env was designed for a server,
+ * where
+ *
+ * - there is a (mostly) fixed number of threads, and
+ *
+ * - call chains have no non-lustre portions inserted between lustre code.
+ *
+ * On a client both these assumption fails, because every user thread can
+ * potentially execute lustre code as part of a system call, and lustre calls
+ * into VFS or MM that call back into lustre.
+ *
+ * To deal with that, cl_env wrapper functions implement the following
+ * optimizations:
+ *
+ * - allocation and destruction of environment is amortized by caching no
+ * longer used environments instead of destroying them;
+ *
+ * - there is a notion of "current" environment, attached to the kernel
+ * data structure representing current thread Top-level lustre code
+ * allocates an environment and makes it current, then calls into
+ * non-lustre code, that in turn calls lustre back. Low-level lustre
+ * code thus called can fetch environment created by the top-level code
+ * and reuse it, avoiding additional environment allocation.
+ * Right now, three interfaces can attach the cl_env to running thread:
+ * - cl_env_get
+ * - cl_env_implant
+ * - cl_env_reexit(cl_env_reenter had to be called priorly)
+ *
+ * \see lu_env, lu_context, lu_context_key
+ * @{ */
+
+struct cl_env_nest {
+ int cen_refcheck;
+ void *cen_cookie;
+};
+
+struct lu_env *cl_env_peek (int *refcheck);
+struct lu_env *cl_env_get (int *refcheck);
+struct lu_env *cl_env_alloc (int *refcheck, __u32 tags);
+struct lu_env *cl_env_nested_get (struct cl_env_nest *nest);
+void cl_env_put (struct lu_env *env, int *refcheck);
+void cl_env_nested_put (struct cl_env_nest *nest, struct lu_env *env);
+void *cl_env_reenter (void);
+void cl_env_reexit (void *cookie);
+void cl_env_implant (struct lu_env *env, int *refcheck);
+void cl_env_unplant (struct lu_env *env, int *refcheck);
+
+/** @} cl_env */
+
+/*
+ * Misc
+ */
+void cl_attr2lvb(struct ost_lvb *lvb, const struct cl_attr *attr);
+void cl_lvb2attr(struct cl_attr *attr, const struct ost_lvb *lvb);
+
+struct cl_device *cl_type_setup(const struct lu_env *env, struct lu_site *site,
+ struct lu_device_type *ldt,
+ struct lu_device *next);
+/** @} clio */
+
+int cl_global_init(void);
+void cl_global_fini(void);
+
+#endif /* _LINUX_CL_OBJECT_H */
diff --git a/drivers/staging/lustre/lustre/include/dt_object.h b/drivers/staging/lustre/lustre/include/dt_object.h
new file mode 100644
index 000000000..be4c7d95e
--- /dev/null
+++ b/drivers/staging/lustre/lustre/include/dt_object.h
@@ -0,0 +1,1499 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2011, 2012, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ */
+
+#ifndef __LUSTRE_DT_OBJECT_H
+#define __LUSTRE_DT_OBJECT_H
+
+/** \defgroup dt dt
+ * Sub-class of lu_object with methods common for "data" objects in OST stack.
+ *
+ * Data objects behave like regular files: you can read/write them, get and
+ * set their attributes. Implementation of dt interface is supposed to
+ * implement some form of garbage collection, normally reference counting
+ * (nlink) based one.
+ *
+ * Examples: osd (lustre/osd) is an implementation of dt interface.
+ * @{
+ */
+
+
+/*
+ * super-class definitions.
+ */
+#include "lu_object.h"
+
+#include "../../include/linux/libcfs/libcfs.h"
+
+struct seq_file;
+struct proc_dir_entry;
+struct lustre_cfg;
+
+struct thandle;
+struct dt_device;
+struct dt_object;
+struct dt_index_features;
+struct niobuf_local;
+struct niobuf_remote;
+struct ldlm_enqueue_info;
+
+typedef enum {
+ MNTOPT_USERXATTR = 0x00000001,
+ MNTOPT_ACL = 0x00000002,
+} mntopt_t;
+
+struct dt_device_param {
+ unsigned ddp_max_name_len;
+ unsigned ddp_max_nlink;
+ unsigned ddp_block_shift;
+ mntopt_t ddp_mntopts;
+ unsigned ddp_max_ea_size;
+ void *ddp_mnt; /* XXX: old code can retrieve mnt -bzzz */
+ int ddp_mount_type;
+ unsigned long long ddp_maxbytes;
+ /* percentage of available space to reserve for grant error margin */
+ int ddp_grant_reserved;
+ /* per-inode space consumption */
+ short ddp_inodespace;
+ /* per-fragment grant overhead to be used by client for grant
+ * calculation */
+ int ddp_grant_frag;
+};
+
+/**
+ * Per-transaction commit callback function
+ */
+struct dt_txn_commit_cb;
+typedef void (*dt_cb_t)(struct lu_env *env, struct thandle *th,
+ struct dt_txn_commit_cb *cb, int err);
+/**
+ * Special per-transaction callback for cases when just commit callback
+ * is needed and per-device callback are not convenient to use
+ */
+#define TRANS_COMMIT_CB_MAGIC 0xa0a00a0a
+#define MAX_COMMIT_CB_STR_LEN 32
+
+struct dt_txn_commit_cb {
+ struct list_head dcb_linkage;
+ dt_cb_t dcb_func;
+ __u32 dcb_magic;
+ char dcb_name[MAX_COMMIT_CB_STR_LEN];
+};
+
+/**
+ * Operations on dt device.
+ */
+struct dt_device_operations {
+ /**
+ * Return device-wide statistics.
+ */
+ int (*dt_statfs)(const struct lu_env *env,
+ struct dt_device *dev, struct obd_statfs *osfs);
+ /**
+ * Create transaction, described by \a param.
+ */
+ struct thandle *(*dt_trans_create)(const struct lu_env *env,
+ struct dt_device *dev);
+ /**
+ * Start transaction, described by \a param.
+ */
+ int (*dt_trans_start)(const struct lu_env *env,
+ struct dt_device *dev, struct thandle *th);
+ /**
+ * Finish previously started transaction.
+ */
+ int (*dt_trans_stop)(const struct lu_env *env,
+ struct thandle *th);
+ /**
+ * Add commit callback to the transaction.
+ */
+ int (*dt_trans_cb_add)(struct thandle *th,
+ struct dt_txn_commit_cb *dcb);
+ /**
+ * Return fid of root index object.
+ */
+ int (*dt_root_get)(const struct lu_env *env,
+ struct dt_device *dev, struct lu_fid *f);
+ /**
+ * Return device configuration data.
+ */
+ void (*dt_conf_get)(const struct lu_env *env,
+ const struct dt_device *dev,
+ struct dt_device_param *param);
+ /**
+ * handling device state, mostly for tests
+ */
+ int (*dt_sync)(const struct lu_env *env, struct dt_device *dev);
+ int (*dt_ro)(const struct lu_env *env, struct dt_device *dev);
+ /**
+ * Start a transaction commit asynchronously
+ *
+ * \param env environment
+ * \param dev dt_device to start commit on
+ *
+ * \return 0 success, negative value if error
+ */
+ int (*dt_commit_async)(const struct lu_env *env,
+ struct dt_device *dev);
+ /**
+ * Initialize capability context.
+ */
+ int (*dt_init_capa_ctxt)(const struct lu_env *env,
+ struct dt_device *dev,
+ int mode, unsigned long timeout,
+ __u32 alg, struct lustre_capa_key *keys);
+};
+
+struct dt_index_features {
+ /** required feature flags from enum dt_index_flags */
+ __u32 dif_flags;
+ /** minimal required key size */
+ size_t dif_keysize_min;
+ /** maximal required key size, 0 if no limit */
+ size_t dif_keysize_max;
+ /** minimal required record size */
+ size_t dif_recsize_min;
+ /** maximal required record size, 0 if no limit */
+ size_t dif_recsize_max;
+ /** pointer size for record */
+ size_t dif_ptrsize;
+};
+
+enum dt_index_flags {
+ /** index supports variable sized keys */
+ DT_IND_VARKEY = 1 << 0,
+ /** index supports variable sized records */
+ DT_IND_VARREC = 1 << 1,
+ /** index can be modified */
+ DT_IND_UPDATE = 1 << 2,
+ /** index supports records with non-unique (duplicate) keys */
+ DT_IND_NONUNQ = 1 << 3,
+ /**
+ * index support fixed-size keys sorted with natural numerical way
+ * and is able to return left-side value if no exact value found
+ */
+ DT_IND_RANGE = 1 << 4,
+};
+
+/**
+ * Features, required from index to support file system directories (mapping
+ * names to fids).
+ */
+extern const struct dt_index_features dt_directory_features;
+extern const struct dt_index_features dt_otable_features;
+extern const struct dt_index_features dt_lfsck_features;
+
+/* index features supported by the accounting objects */
+extern const struct dt_index_features dt_acct_features;
+
+/* index features supported by the quota global indexes */
+extern const struct dt_index_features dt_quota_glb_features;
+
+/* index features supported by the quota slave indexes */
+extern const struct dt_index_features dt_quota_slv_features;
+
+/**
+ * This is a general purpose dt allocation hint.
+ * It now contains the parent object.
+ * It can contain any allocation hint in the future.
+ */
+struct dt_allocation_hint {
+ struct dt_object *dah_parent;
+ __u32 dah_mode;
+};
+
+/**
+ * object type specifier.
+ */
+
+enum dt_format_type {
+ DFT_REGULAR,
+ DFT_DIR,
+ /** for mknod */
+ DFT_NODE,
+ /** for special index */
+ DFT_INDEX,
+ /** for symbolic link */
+ DFT_SYM,
+};
+
+/**
+ * object format specifier.
+ */
+struct dt_object_format {
+ /** type for dt object */
+ enum dt_format_type dof_type;
+ union {
+ struct dof_regular {
+ int striped;
+ } dof_reg;
+ struct dof_dir {
+ } dof_dir;
+ struct dof_node {
+ } dof_node;
+ /**
+ * special index need feature as parameter to create
+ * special idx
+ */
+ struct dof_index {
+ const struct dt_index_features *di_feat;
+ } dof_idx;
+ } u;
+};
+
+enum dt_format_type dt_mode_to_dft(__u32 mode);
+
+typedef __u64 dt_obj_version_t;
+
+/**
+ * Per-dt-object operations.
+ */
+struct dt_object_operations {
+ void (*do_read_lock)(const struct lu_env *env,
+ struct dt_object *dt, unsigned role);
+ void (*do_write_lock)(const struct lu_env *env,
+ struct dt_object *dt, unsigned role);
+ void (*do_read_unlock)(const struct lu_env *env,
+ struct dt_object *dt);
+ void (*do_write_unlock)(const struct lu_env *env,
+ struct dt_object *dt);
+ int (*do_write_locked)(const struct lu_env *env,
+ struct dt_object *dt);
+ /**
+ * Note: following ->do_{x,}attr_{set,get}() operations are very
+ * similar to ->moo_{x,}attr_{set,get}() operations in struct
+ * md_object_operations (see md_object.h). These operations are not in
+ * lu_object_operations, because ->do_{x,}attr_set() versions take
+ * transaction handle as an argument (this transaction is started by
+ * caller). We might factor ->do_{x,}attr_get() into
+ * lu_object_operations, but that would break existing symmetry.
+ */
+
+ /**
+ * Return standard attributes.
+ *
+ * precondition: lu_object_exists(&dt->do_lu);
+ */
+ int (*do_attr_get)(const struct lu_env *env,
+ struct dt_object *dt, struct lu_attr *attr,
+ struct lustre_capa *capa);
+ /**
+ * Set standard attributes.
+ *
+ * precondition: dt_object_exists(dt);
+ */
+ int (*do_declare_attr_set)(const struct lu_env *env,
+ struct dt_object *dt,
+ const struct lu_attr *attr,
+ struct thandle *handle);
+ int (*do_attr_set)(const struct lu_env *env,
+ struct dt_object *dt,
+ const struct lu_attr *attr,
+ struct thandle *handle,
+ struct lustre_capa *capa);
+ /**
+ * Return a value of an extended attribute.
+ *
+ * precondition: dt_object_exists(dt);
+ */
+ int (*do_xattr_get)(const struct lu_env *env, struct dt_object *dt,
+ struct lu_buf *buf, const char *name,
+ struct lustre_capa *capa);
+ /**
+ * Set value of an extended attribute.
+ *
+ * \a fl - flags from enum lu_xattr_flags
+ *
+ * precondition: dt_object_exists(dt);
+ */
+ int (*do_declare_xattr_set)(const struct lu_env *env,
+ struct dt_object *dt,
+ const struct lu_buf *buf,
+ const char *name, int fl,
+ struct thandle *handle);
+ int (*do_xattr_set)(const struct lu_env *env,
+ struct dt_object *dt, const struct lu_buf *buf,
+ const char *name, int fl, struct thandle *handle,
+ struct lustre_capa *capa);
+ /**
+ * Delete existing extended attribute.
+ *
+ * precondition: dt_object_exists(dt);
+ */
+ int (*do_declare_xattr_del)(const struct lu_env *env,
+ struct dt_object *dt,
+ const char *name, struct thandle *handle);
+ int (*do_xattr_del)(const struct lu_env *env,
+ struct dt_object *dt,
+ const char *name, struct thandle *handle,
+ struct lustre_capa *capa);
+ /**
+ * Place list of existing extended attributes into \a buf (which has
+ * length len).
+ *
+ * precondition: dt_object_exists(dt);
+ */
+ int (*do_xattr_list)(const struct lu_env *env,
+ struct dt_object *dt, struct lu_buf *buf,
+ struct lustre_capa *capa);
+ /**
+ * Init allocation hint using parent object and child mode.
+ * (1) The \a parent might be NULL if this is a partial creation for
+ * remote object.
+ * (2) The type of child is in \a child_mode.
+ * (3) The result hint is stored in \a ah;
+ */
+ void (*do_ah_init)(const struct lu_env *env,
+ struct dt_allocation_hint *ah,
+ struct dt_object *parent,
+ struct dt_object *child,
+ umode_t child_mode);
+ /**
+ * Create new object on this device.
+ *
+ * precondition: !dt_object_exists(dt);
+ * postcondition: ergo(result == 0, dt_object_exists(dt));
+ */
+ int (*do_declare_create)(const struct lu_env *env,
+ struct dt_object *dt,
+ struct lu_attr *attr,
+ struct dt_allocation_hint *hint,
+ struct dt_object_format *dof,
+ struct thandle *th);
+ int (*do_create)(const struct lu_env *env, struct dt_object *dt,
+ struct lu_attr *attr,
+ struct dt_allocation_hint *hint,
+ struct dt_object_format *dof,
+ struct thandle *th);
+
+ /**
+ Destroy object on this device
+ * precondition: !dt_object_exists(dt);
+ * postcondition: ergo(result == 0, dt_object_exists(dt));
+ */
+ int (*do_declare_destroy)(const struct lu_env *env,
+ struct dt_object *dt,
+ struct thandle *th);
+ int (*do_destroy)(const struct lu_env *env, struct dt_object *dt,
+ struct thandle *th);
+
+ /**
+ * Announce that this object is going to be used as an index. This
+ * operation check that object supports indexing operations and
+ * installs appropriate dt_index_operations vector on success.
+ *
+ * Also probes for features. Operation is successful if all required
+ * features are supported.
+ */
+ int (*do_index_try)(const struct lu_env *env,
+ struct dt_object *dt,
+ const struct dt_index_features *feat);
+ /**
+ * Add nlink of the object
+ * precondition: dt_object_exists(dt);
+ */
+ int (*do_declare_ref_add)(const struct lu_env *env,
+ struct dt_object *dt, struct thandle *th);
+ int (*do_ref_add)(const struct lu_env *env,
+ struct dt_object *dt, struct thandle *th);
+ /**
+ * Del nlink of the object
+ * precondition: dt_object_exists(dt);
+ */
+ int (*do_declare_ref_del)(const struct lu_env *env,
+ struct dt_object *dt, struct thandle *th);
+ int (*do_ref_del)(const struct lu_env *env,
+ struct dt_object *dt, struct thandle *th);
+
+ struct obd_capa *(*do_capa_get)(const struct lu_env *env,
+ struct dt_object *dt,
+ struct lustre_capa *old,
+ __u64 opc);
+ int (*do_object_sync)(const struct lu_env *env, struct dt_object *obj,
+ __u64 start, __u64 end);
+ /**
+ * Get object info of next level. Currently, only get inode from osd.
+ * This is only used by quota b=16542
+ * precondition: dt_object_exists(dt);
+ */
+ int (*do_data_get)(const struct lu_env *env, struct dt_object *dt,
+ void **data);
+
+ /**
+ * Lock object.
+ */
+ int (*do_object_lock)(const struct lu_env *env, struct dt_object *dt,
+ struct lustre_handle *lh,
+ struct ldlm_enqueue_info *einfo,
+ void *policy);
+};
+
+/**
+ * Per-dt-object operations on "file body".
+ */
+struct dt_body_operations {
+ /**
+ * precondition: dt_object_exists(dt);
+ */
+ ssize_t (*dbo_read)(const struct lu_env *env, struct dt_object *dt,
+ struct lu_buf *buf, loff_t *pos,
+ struct lustre_capa *capa);
+ /**
+ * precondition: dt_object_exists(dt);
+ */
+ ssize_t (*dbo_declare_write)(const struct lu_env *env,
+ struct dt_object *dt,
+ const loff_t size, loff_t pos,
+ struct thandle *handle);
+ ssize_t (*dbo_write)(const struct lu_env *env, struct dt_object *dt,
+ const struct lu_buf *buf, loff_t *pos,
+ struct thandle *handle, struct lustre_capa *capa,
+ int ignore_quota);
+ /*
+ * methods for zero-copy IO
+ */
+
+ /*
+ * precondition: dt_object_exists(dt);
+ * returns:
+ * < 0 - error code
+ * = 0 - illegal
+ * > 0 - number of local buffers prepared
+ */
+ int (*dbo_bufs_get)(const struct lu_env *env, struct dt_object *dt,
+ loff_t pos, ssize_t len, struct niobuf_local *lb,
+ int rw, struct lustre_capa *capa);
+ /*
+ * precondition: dt_object_exists(dt);
+ */
+ int (*dbo_bufs_put)(const struct lu_env *env, struct dt_object *dt,
+ struct niobuf_local *lb, int nr);
+ /*
+ * precondition: dt_object_exists(dt);
+ */
+ int (*dbo_write_prep)(const struct lu_env *env, struct dt_object *dt,
+ struct niobuf_local *lb, int nr);
+ /*
+ * precondition: dt_object_exists(dt);
+ */
+ int (*dbo_declare_write_commit)(const struct lu_env *env,
+ struct dt_object *dt,
+ struct niobuf_local *,
+ int, struct thandle *);
+ /*
+ * precondition: dt_object_exists(dt);
+ */
+ int (*dbo_write_commit)(const struct lu_env *env, struct dt_object *dt,
+ struct niobuf_local *, int, struct thandle *);
+ /*
+ * precondition: dt_object_exists(dt);
+ */
+ int (*dbo_read_prep)(const struct lu_env *env, struct dt_object *dt,
+ struct niobuf_local *lnb, int nr);
+ int (*dbo_fiemap_get)(const struct lu_env *env, struct dt_object *dt,
+ struct ll_user_fiemap *fm);
+ /**
+ * Punch object's content
+ * precondition: regular object, not index
+ */
+ int (*dbo_declare_punch)(const struct lu_env *, struct dt_object *,
+ __u64, __u64, struct thandle *th);
+ int (*dbo_punch)(const struct lu_env *env, struct dt_object *dt,
+ __u64 start, __u64 end, struct thandle *th,
+ struct lustre_capa *capa);
+};
+
+/**
+ * Incomplete type of index record.
+ */
+struct dt_rec;
+
+/**
+ * Incomplete type of index key.
+ */
+struct dt_key;
+
+/**
+ * Incomplete type of dt iterator.
+ */
+struct dt_it;
+
+/**
+ * Per-dt-object operations on object as index.
+ */
+struct dt_index_operations {
+ /**
+ * precondition: dt_object_exists(dt);
+ */
+ int (*dio_lookup)(const struct lu_env *env, struct dt_object *dt,
+ struct dt_rec *rec, const struct dt_key *key,
+ struct lustre_capa *capa);
+ /**
+ * precondition: dt_object_exists(dt);
+ */
+ int (*dio_declare_insert)(const struct lu_env *env,
+ struct dt_object *dt,
+ const struct dt_rec *rec,
+ const struct dt_key *key,
+ struct thandle *handle);
+ int (*dio_insert)(const struct lu_env *env, struct dt_object *dt,
+ const struct dt_rec *rec, const struct dt_key *key,
+ struct thandle *handle, struct lustre_capa *capa,
+ int ignore_quota);
+ /**
+ * precondition: dt_object_exists(dt);
+ */
+ int (*dio_declare_delete)(const struct lu_env *env,
+ struct dt_object *dt,
+ const struct dt_key *key,
+ struct thandle *handle);
+ int (*dio_delete)(const struct lu_env *env, struct dt_object *dt,
+ const struct dt_key *key, struct thandle *handle,
+ struct lustre_capa *capa);
+ /**
+ * Iterator interface
+ */
+ struct dt_it_ops {
+ /**
+ * Allocate and initialize new iterator.
+ *
+ * precondition: dt_object_exists(dt);
+ */
+ struct dt_it *(*init)(const struct lu_env *env,
+ struct dt_object *dt,
+ __u32 attr,
+ struct lustre_capa *capa);
+ void (*fini)(const struct lu_env *env,
+ struct dt_it *di);
+ int (*get)(const struct lu_env *env,
+ struct dt_it *di,
+ const struct dt_key *key);
+ void (*put)(const struct lu_env *env,
+ struct dt_it *di);
+ int (*next)(const struct lu_env *env,
+ struct dt_it *di);
+ struct dt_key *(*key)(const struct lu_env *env,
+ const struct dt_it *di);
+ int (*key_size)(const struct lu_env *env,
+ const struct dt_it *di);
+ int (*rec)(const struct lu_env *env,
+ const struct dt_it *di,
+ struct dt_rec *rec,
+ __u32 attr);
+ __u64 (*store)(const struct lu_env *env,
+ const struct dt_it *di);
+ int (*load)(const struct lu_env *env,
+ const struct dt_it *di, __u64 hash);
+ int (*key_rec)(const struct lu_env *env,
+ const struct dt_it *di, void *key_rec);
+ } dio_it;
+};
+
+enum dt_otable_it_valid {
+ DOIV_ERROR_HANDLE = 0x0001,
+};
+
+enum dt_otable_it_flags {
+ /* Exit when fail. */
+ DOIF_FAILOUT = 0x0001,
+
+ /* Reset iteration position to the device beginning. */
+ DOIF_RESET = 0x0002,
+
+ /* There is up layer component uses the iteration. */
+ DOIF_OUTUSED = 0x0004,
+};
+
+/* otable based iteration needs to use the common DT interation APIs.
+ * To initialize the iteration, it needs call dio_it::init() firstly.
+ * Here is how the otable based iteration should prepare arguments to
+ * call dt_it_ops::init().
+ *
+ * For otable based iteration, the 32-bits 'attr' for dt_it_ops::init()
+ * is composed of two parts:
+ * low 16-bits is for valid bits, high 16-bits is for flags bits. */
+#define DT_OTABLE_IT_FLAGS_SHIFT 16
+#define DT_OTABLE_IT_FLAGS_MASK 0xffff0000
+
+struct dt_device {
+ struct lu_device dd_lu_dev;
+ const struct dt_device_operations *dd_ops;
+
+ /**
+ * List of dt_txn_callback (see below). This is not protected in any
+ * way, because callbacks are supposed to be added/deleted only during
+ * single-threaded start-up shut-down procedures.
+ */
+ struct list_head dd_txn_callbacks;
+};
+
+int dt_device_init(struct dt_device *dev, struct lu_device_type *t);
+void dt_device_fini(struct dt_device *dev);
+
+static inline int lu_device_is_dt(const struct lu_device *d)
+{
+ return ergo(d != NULL, d->ld_type->ldt_tags & LU_DEVICE_DT);
+}
+
+static inline struct dt_device *lu2dt_dev(struct lu_device *l)
+{
+ LASSERT(lu_device_is_dt(l));
+ return container_of0(l, struct dt_device, dd_lu_dev);
+}
+
+struct dt_object {
+ struct lu_object do_lu;
+ const struct dt_object_operations *do_ops;
+ const struct dt_body_operations *do_body_ops;
+ const struct dt_index_operations *do_index_ops;
+};
+
+/*
+ * In-core representation of per-device local object OID storage
+ */
+struct local_oid_storage {
+ /* all initialized llog systems on this node linked by this */
+ struct list_head los_list;
+
+ /* how many handle's reference this los has */
+ atomic_t los_refcount;
+ struct dt_device *los_dev;
+ struct dt_object *los_obj;
+
+ /* data used to generate new fids */
+ struct mutex los_id_lock;
+ __u64 los_seq;
+ __u32 los_last_oid;
+};
+
+static inline struct dt_object *lu2dt(struct lu_object *l)
+{
+ LASSERT(l == NULL || IS_ERR(l) || lu_device_is_dt(l->lo_dev));
+ return container_of0(l, struct dt_object, do_lu);
+}
+
+int dt_object_init(struct dt_object *obj,
+ struct lu_object_header *h, struct lu_device *d);
+
+void dt_object_fini(struct dt_object *obj);
+
+static inline int dt_object_exists(const struct dt_object *dt)
+{
+ return lu_object_exists(&dt->do_lu);
+}
+
+static inline int dt_object_remote(const struct dt_object *dt)
+{
+ return lu_object_remote(&dt->do_lu);
+}
+
+static inline struct dt_object *lu2dt_obj(struct lu_object *o)
+{
+ LASSERT(ergo(o != NULL, lu_device_is_dt(o->lo_dev)));
+ return container_of0(o, struct dt_object, do_lu);
+}
+
+/**
+ * This is the general purpose transaction handle.
+ * 1. Transaction Life Cycle
+ * This transaction handle is allocated upon starting a new transaction,
+ * and deallocated after this transaction is committed.
+ * 2. Transaction Nesting
+ * We do _NOT_ support nested transaction. So, every thread should only
+ * have one active transaction, and a transaction only belongs to one
+ * thread. Due to this, transaction handle need no reference count.
+ * 3. Transaction & dt_object locking
+ * dt_object locks should be taken inside transaction.
+ * 4. Transaction & RPC
+ * No RPC request should be issued inside transaction.
+ */
+struct thandle {
+ /** the dt device on which the transactions are executed */
+ struct dt_device *th_dev;
+
+ /** context for this transaction, tag is LCT_TX_HANDLE */
+ struct lu_context th_ctx;
+
+ /** additional tags (layers can add in declare) */
+ __u32 th_tags;
+
+ /** the last operation result in this transaction.
+ * this value is used in recovery */
+ __s32 th_result;
+
+ /** whether we need sync commit */
+ unsigned int th_sync:1;
+
+ /* local transation, no need to inform other layers */
+ unsigned int th_local:1;
+
+ /* In DNE, one transaction can be disassemblied into
+ * updates on several different MDTs, and these updates
+ * will be attached to th_remote_update_list per target.
+ * Only single thread will access the list, no need lock
+ */
+ struct list_head th_remote_update_list;
+ struct update_request *th_current_request;
+};
+
+/**
+ * Transaction call-backs.
+ *
+ * These are invoked by osd (or underlying transaction engine) when
+ * transaction changes state.
+ *
+ * Call-backs are used by upper layers to modify transaction parameters and to
+ * perform some actions on for each transaction state transition. Typical
+ * example is mdt registering call-back to write into last-received file
+ * before each transaction commit.
+ */
+struct dt_txn_callback {
+ int (*dtc_txn_start)(const struct lu_env *env,
+ struct thandle *txn, void *cookie);
+ int (*dtc_txn_stop)(const struct lu_env *env,
+ struct thandle *txn, void *cookie);
+ void (*dtc_txn_commit)(struct thandle *txn, void *cookie);
+ void *dtc_cookie;
+ __u32 dtc_tag;
+ struct list_head dtc_linkage;
+};
+
+void dt_txn_callback_add(struct dt_device *dev, struct dt_txn_callback *cb);
+void dt_txn_callback_del(struct dt_device *dev, struct dt_txn_callback *cb);
+
+int dt_txn_hook_start(const struct lu_env *env,
+ struct dt_device *dev, struct thandle *txn);
+int dt_txn_hook_stop(const struct lu_env *env, struct thandle *txn);
+void dt_txn_hook_commit(struct thandle *txn);
+
+int dt_try_as_dir(const struct lu_env *env, struct dt_object *obj);
+
+/**
+ * Callback function used for parsing path.
+ * \see llo_store_resolve
+ */
+typedef int (*dt_entry_func_t)(const struct lu_env *env,
+ const char *name,
+ void *pvt);
+
+#define DT_MAX_PATH 1024
+
+int dt_path_parser(const struct lu_env *env,
+ char *local, dt_entry_func_t entry_func,
+ void *data);
+
+struct dt_object *
+dt_store_resolve(const struct lu_env *env, struct dt_device *dt,
+ const char *path, struct lu_fid *fid);
+
+struct dt_object *dt_store_open(const struct lu_env *env,
+ struct dt_device *dt,
+ const char *dirname,
+ const char *filename,
+ struct lu_fid *fid);
+
+struct dt_object *dt_find_or_create(const struct lu_env *env,
+ struct dt_device *dt,
+ const struct lu_fid *fid,
+ struct dt_object_format *dof,
+ struct lu_attr *attr);
+
+struct dt_object *dt_locate_at(const struct lu_env *env,
+ struct dt_device *dev,
+ const struct lu_fid *fid,
+ struct lu_device *top_dev);
+static inline struct dt_object *
+dt_locate(const struct lu_env *env, struct dt_device *dev,
+ const struct lu_fid *fid)
+{
+ return dt_locate_at(env, dev, fid, dev->dd_lu_dev.ld_site->ls_top_dev);
+}
+
+
+int local_oid_storage_init(const struct lu_env *env, struct dt_device *dev,
+ const struct lu_fid *first_fid,
+ struct local_oid_storage **los);
+void local_oid_storage_fini(const struct lu_env *env,
+ struct local_oid_storage *los);
+int local_object_fid_generate(const struct lu_env *env,
+ struct local_oid_storage *los,
+ struct lu_fid *fid);
+int local_object_declare_create(const struct lu_env *env,
+ struct local_oid_storage *los,
+ struct dt_object *o,
+ struct lu_attr *attr,
+ struct dt_object_format *dof,
+ struct thandle *th);
+int local_object_create(const struct lu_env *env,
+ struct local_oid_storage *los,
+ struct dt_object *o,
+ struct lu_attr *attr, struct dt_object_format *dof,
+ struct thandle *th);
+struct dt_object *local_file_find_or_create(const struct lu_env *env,
+ struct local_oid_storage *los,
+ struct dt_object *parent,
+ const char *name, __u32 mode);
+struct dt_object *local_file_find_or_create_with_fid(const struct lu_env *env,
+ struct dt_device *dt,
+ const struct lu_fid *fid,
+ struct dt_object *parent,
+ const char *name,
+ __u32 mode);
+struct dt_object *
+local_index_find_or_create(const struct lu_env *env,
+ struct local_oid_storage *los,
+ struct dt_object *parent,
+ const char *name, __u32 mode,
+ const struct dt_index_features *ft);
+struct dt_object *
+local_index_find_or_create_with_fid(const struct lu_env *env,
+ struct dt_device *dt,
+ const struct lu_fid *fid,
+ struct dt_object *parent,
+ const char *name, __u32 mode,
+ const struct dt_index_features *ft);
+int local_object_unlink(const struct lu_env *env, struct dt_device *dt,
+ struct dt_object *parent, const char *name);
+
+static inline int dt_object_lock(const struct lu_env *env,
+ struct dt_object *o, struct lustre_handle *lh,
+ struct ldlm_enqueue_info *einfo,
+ void *policy)
+{
+ LASSERT(o);
+ LASSERT(o->do_ops);
+ LASSERT(o->do_ops->do_object_lock);
+ return o->do_ops->do_object_lock(env, o, lh, einfo, policy);
+}
+
+int dt_lookup_dir(const struct lu_env *env, struct dt_object *dir,
+ const char *name, struct lu_fid *fid);
+
+static inline int dt_object_sync(const struct lu_env *env, struct dt_object *o,
+ __u64 start, __u64 end)
+{
+ LASSERT(o);
+ LASSERT(o->do_ops);
+ LASSERT(o->do_ops->do_object_sync);
+ return o->do_ops->do_object_sync(env, o, start, end);
+}
+
+int dt_declare_version_set(const struct lu_env *env, struct dt_object *o,
+ struct thandle *th);
+void dt_version_set(const struct lu_env *env, struct dt_object *o,
+ dt_obj_version_t version, struct thandle *th);
+dt_obj_version_t dt_version_get(const struct lu_env *env, struct dt_object *o);
+
+
+int dt_read(const struct lu_env *env, struct dt_object *dt,
+ struct lu_buf *buf, loff_t *pos);
+int dt_record_read(const struct lu_env *env, struct dt_object *dt,
+ struct lu_buf *buf, loff_t *pos);
+int dt_record_write(const struct lu_env *env, struct dt_object *dt,
+ const struct lu_buf *buf, loff_t *pos, struct thandle *th);
+typedef int (*dt_index_page_build_t)(const struct lu_env *env,
+ union lu_page *lp, int nob,
+ const struct dt_it_ops *iops,
+ struct dt_it *it, __u32 attr, void *arg);
+int dt_index_walk(const struct lu_env *env, struct dt_object *obj,
+ const struct lu_rdpg *rdpg, dt_index_page_build_t filler,
+ void *arg);
+int dt_index_read(const struct lu_env *env, struct dt_device *dev,
+ struct idx_info *ii, const struct lu_rdpg *rdpg);
+
+static inline struct thandle *dt_trans_create(const struct lu_env *env,
+ struct dt_device *d)
+{
+ LASSERT(d->dd_ops->dt_trans_create);
+ return d->dd_ops->dt_trans_create(env, d);
+}
+
+static inline int dt_trans_start(const struct lu_env *env,
+ struct dt_device *d, struct thandle *th)
+{
+ LASSERT(d->dd_ops->dt_trans_start);
+ return d->dd_ops->dt_trans_start(env, d, th);
+}
+
+/* for this transaction hooks shouldn't be called */
+static inline int dt_trans_start_local(const struct lu_env *env,
+ struct dt_device *d, struct thandle *th)
+{
+ LASSERT(d->dd_ops->dt_trans_start);
+ th->th_local = 1;
+ return d->dd_ops->dt_trans_start(env, d, th);
+}
+
+static inline int dt_trans_stop(const struct lu_env *env,
+ struct dt_device *d, struct thandle *th)
+{
+ LASSERT(d->dd_ops->dt_trans_stop);
+ return d->dd_ops->dt_trans_stop(env, th);
+}
+
+static inline int dt_trans_cb_add(struct thandle *th,
+ struct dt_txn_commit_cb *dcb)
+{
+ LASSERT(th->th_dev->dd_ops->dt_trans_cb_add);
+ dcb->dcb_magic = TRANS_COMMIT_CB_MAGIC;
+ return th->th_dev->dd_ops->dt_trans_cb_add(th, dcb);
+}
+/** @} dt */
+
+
+static inline int dt_declare_record_write(const struct lu_env *env,
+ struct dt_object *dt,
+ int size, loff_t pos,
+ struct thandle *th)
+{
+ int rc;
+
+ LASSERTF(dt != NULL, "dt is NULL when we want to write record\n");
+ LASSERT(th != NULL);
+ LASSERT(dt->do_body_ops);
+ LASSERT(dt->do_body_ops->dbo_declare_write);
+ rc = dt->do_body_ops->dbo_declare_write(env, dt, size, pos, th);
+ return rc;
+}
+
+static inline int dt_declare_create(const struct lu_env *env,
+ struct dt_object *dt,
+ struct lu_attr *attr,
+ struct dt_allocation_hint *hint,
+ struct dt_object_format *dof,
+ struct thandle *th)
+{
+ LASSERT(dt);
+ LASSERT(dt->do_ops);
+ LASSERT(dt->do_ops->do_declare_create);
+ return dt->do_ops->do_declare_create(env, dt, attr, hint, dof, th);
+}
+
+static inline int dt_create(const struct lu_env *env,
+ struct dt_object *dt,
+ struct lu_attr *attr,
+ struct dt_allocation_hint *hint,
+ struct dt_object_format *dof,
+ struct thandle *th)
+{
+ LASSERT(dt);
+ LASSERT(dt->do_ops);
+ LASSERT(dt->do_ops->do_create);
+ return dt->do_ops->do_create(env, dt, attr, hint, dof, th);
+}
+
+static inline int dt_declare_destroy(const struct lu_env *env,
+ struct dt_object *dt,
+ struct thandle *th)
+{
+ LASSERT(dt);
+ LASSERT(dt->do_ops);
+ LASSERT(dt->do_ops->do_declare_destroy);
+ return dt->do_ops->do_declare_destroy(env, dt, th);
+}
+
+static inline int dt_destroy(const struct lu_env *env,
+ struct dt_object *dt,
+ struct thandle *th)
+{
+ LASSERT(dt);
+ LASSERT(dt->do_ops);
+ LASSERT(dt->do_ops->do_destroy);
+ return dt->do_ops->do_destroy(env, dt, th);
+}
+
+static inline void dt_read_lock(const struct lu_env *env,
+ struct dt_object *dt,
+ unsigned role)
+{
+ LASSERT(dt);
+ LASSERT(dt->do_ops);
+ LASSERT(dt->do_ops->do_read_lock);
+ dt->do_ops->do_read_lock(env, dt, role);
+}
+
+static inline void dt_write_lock(const struct lu_env *env,
+ struct dt_object *dt,
+ unsigned role)
+{
+ LASSERT(dt);
+ LASSERT(dt->do_ops);
+ LASSERT(dt->do_ops->do_write_lock);
+ dt->do_ops->do_write_lock(env, dt, role);
+}
+
+static inline void dt_read_unlock(const struct lu_env *env,
+ struct dt_object *dt)
+{
+ LASSERT(dt);
+ LASSERT(dt->do_ops);
+ LASSERT(dt->do_ops->do_read_unlock);
+ dt->do_ops->do_read_unlock(env, dt);
+}
+
+static inline void dt_write_unlock(const struct lu_env *env,
+ struct dt_object *dt)
+{
+ LASSERT(dt);
+ LASSERT(dt->do_ops);
+ LASSERT(dt->do_ops->do_write_unlock);
+ dt->do_ops->do_write_unlock(env, dt);
+}
+
+static inline int dt_write_locked(const struct lu_env *env,
+ struct dt_object *dt)
+{
+ LASSERT(dt);
+ LASSERT(dt->do_ops);
+ LASSERT(dt->do_ops->do_write_locked);
+ return dt->do_ops->do_write_locked(env, dt);
+}
+
+static inline int dt_attr_get(const struct lu_env *env, struct dt_object *dt,
+ struct lu_attr *la, void *arg)
+{
+ LASSERT(dt);
+ LASSERT(dt->do_ops);
+ LASSERT(dt->do_ops->do_attr_get);
+ return dt->do_ops->do_attr_get(env, dt, la, arg);
+}
+
+static inline int dt_declare_attr_set(const struct lu_env *env,
+ struct dt_object *dt,
+ const struct lu_attr *la,
+ struct thandle *th)
+{
+ LASSERT(dt);
+ LASSERT(dt->do_ops);
+ LASSERT(dt->do_ops->do_declare_attr_set);
+ return dt->do_ops->do_declare_attr_set(env, dt, la, th);
+}
+
+static inline int dt_attr_set(const struct lu_env *env, struct dt_object *dt,
+ const struct lu_attr *la, struct thandle *th,
+ struct lustre_capa *capa)
+{
+ LASSERT(dt);
+ LASSERT(dt->do_ops);
+ LASSERT(dt->do_ops->do_attr_set);
+ return dt->do_ops->do_attr_set(env, dt, la, th, capa);
+}
+
+static inline int dt_declare_ref_add(const struct lu_env *env,
+ struct dt_object *dt, struct thandle *th)
+{
+ LASSERT(dt);
+ LASSERT(dt->do_ops);
+ LASSERT(dt->do_ops->do_declare_ref_add);
+ return dt->do_ops->do_declare_ref_add(env, dt, th);
+}
+
+static inline int dt_ref_add(const struct lu_env *env,
+ struct dt_object *dt, struct thandle *th)
+{
+ LASSERT(dt);
+ LASSERT(dt->do_ops);
+ LASSERT(dt->do_ops->do_ref_add);
+ return dt->do_ops->do_ref_add(env, dt, th);
+}
+
+static inline int dt_declare_ref_del(const struct lu_env *env,
+ struct dt_object *dt, struct thandle *th)
+{
+ LASSERT(dt);
+ LASSERT(dt->do_ops);
+ LASSERT(dt->do_ops->do_declare_ref_del);
+ return dt->do_ops->do_declare_ref_del(env, dt, th);
+}
+
+static inline int dt_ref_del(const struct lu_env *env,
+ struct dt_object *dt, struct thandle *th)
+{
+ LASSERT(dt);
+ LASSERT(dt->do_ops);
+ LASSERT(dt->do_ops->do_ref_del);
+ return dt->do_ops->do_ref_del(env, dt, th);
+}
+
+static inline struct obd_capa *dt_capa_get(const struct lu_env *env,
+ struct dt_object *dt,
+ struct lustre_capa *old, __u64 opc)
+{
+ LASSERT(dt);
+ LASSERT(dt->do_ops);
+ LASSERT(dt->do_ops->do_ref_del);
+ return dt->do_ops->do_capa_get(env, dt, old, opc);
+}
+
+static inline int dt_bufs_get(const struct lu_env *env, struct dt_object *d,
+ struct niobuf_remote *rnb,
+ struct niobuf_local *lnb, int rw,
+ struct lustre_capa *capa)
+{
+ LASSERT(d);
+ LASSERT(d->do_body_ops);
+ LASSERT(d->do_body_ops->dbo_bufs_get);
+ return d->do_body_ops->dbo_bufs_get(env, d, rnb->offset,
+ rnb->len, lnb, rw, capa);
+}
+
+static inline int dt_bufs_put(const struct lu_env *env, struct dt_object *d,
+ struct niobuf_local *lnb, int n)
+{
+ LASSERT(d);
+ LASSERT(d->do_body_ops);
+ LASSERT(d->do_body_ops->dbo_bufs_put);
+ return d->do_body_ops->dbo_bufs_put(env, d, lnb, n);
+}
+
+static inline int dt_write_prep(const struct lu_env *env, struct dt_object *d,
+ struct niobuf_local *lnb, int n)
+{
+ LASSERT(d);
+ LASSERT(d->do_body_ops);
+ LASSERT(d->do_body_ops->dbo_write_prep);
+ return d->do_body_ops->dbo_write_prep(env, d, lnb, n);
+}
+
+static inline int dt_declare_write_commit(const struct lu_env *env,
+ struct dt_object *d,
+ struct niobuf_local *lnb,
+ int n, struct thandle *th)
+{
+ LASSERTF(d != NULL, "dt is NULL when we want to declare write\n");
+ LASSERT(th != NULL);
+ return d->do_body_ops->dbo_declare_write_commit(env, d, lnb, n, th);
+}
+
+
+static inline int dt_write_commit(const struct lu_env *env,
+ struct dt_object *d, struct niobuf_local *lnb,
+ int n, struct thandle *th)
+{
+ LASSERT(d);
+ LASSERT(d->do_body_ops);
+ LASSERT(d->do_body_ops->dbo_write_commit);
+ return d->do_body_ops->dbo_write_commit(env, d, lnb, n, th);
+}
+
+static inline int dt_read_prep(const struct lu_env *env, struct dt_object *d,
+ struct niobuf_local *lnb, int n)
+{
+ LASSERT(d);
+ LASSERT(d->do_body_ops);
+ LASSERT(d->do_body_ops->dbo_read_prep);
+ return d->do_body_ops->dbo_read_prep(env, d, lnb, n);
+}
+
+static inline int dt_declare_punch(const struct lu_env *env,
+ struct dt_object *dt, __u64 start,
+ __u64 end, struct thandle *th)
+{
+ LASSERT(dt);
+ LASSERT(dt->do_body_ops);
+ LASSERT(dt->do_body_ops->dbo_declare_punch);
+ return dt->do_body_ops->dbo_declare_punch(env, dt, start, end, th);
+}
+
+static inline int dt_punch(const struct lu_env *env, struct dt_object *dt,
+ __u64 start, __u64 end, struct thandle *th,
+ struct lustre_capa *capa)
+{
+ LASSERT(dt);
+ LASSERT(dt->do_body_ops);
+ LASSERT(dt->do_body_ops->dbo_punch);
+ return dt->do_body_ops->dbo_punch(env, dt, start, end, th, capa);
+}
+
+static inline int dt_fiemap_get(const struct lu_env *env, struct dt_object *d,
+ struct ll_user_fiemap *fm)
+{
+ LASSERT(d);
+ if (d->do_body_ops == NULL)
+ return -EPROTO;
+ if (d->do_body_ops->dbo_fiemap_get == NULL)
+ return -EOPNOTSUPP;
+ return d->do_body_ops->dbo_fiemap_get(env, d, fm);
+}
+
+static inline int dt_statfs(const struct lu_env *env, struct dt_device *dev,
+ struct obd_statfs *osfs)
+{
+ LASSERT(dev);
+ LASSERT(dev->dd_ops);
+ LASSERT(dev->dd_ops->dt_statfs);
+ return dev->dd_ops->dt_statfs(env, dev, osfs);
+}
+
+static inline int dt_root_get(const struct lu_env *env, struct dt_device *dev,
+ struct lu_fid *f)
+{
+ LASSERT(dev);
+ LASSERT(dev->dd_ops);
+ LASSERT(dev->dd_ops->dt_root_get);
+ return dev->dd_ops->dt_root_get(env, dev, f);
+}
+
+static inline void dt_conf_get(const struct lu_env *env,
+ const struct dt_device *dev,
+ struct dt_device_param *param)
+{
+ LASSERT(dev);
+ LASSERT(dev->dd_ops);
+ LASSERT(dev->dd_ops->dt_conf_get);
+ return dev->dd_ops->dt_conf_get(env, dev, param);
+}
+
+static inline int dt_sync(const struct lu_env *env, struct dt_device *dev)
+{
+ LASSERT(dev);
+ LASSERT(dev->dd_ops);
+ LASSERT(dev->dd_ops->dt_sync);
+ return dev->dd_ops->dt_sync(env, dev);
+}
+
+static inline int dt_ro(const struct lu_env *env, struct dt_device *dev)
+{
+ LASSERT(dev);
+ LASSERT(dev->dd_ops);
+ LASSERT(dev->dd_ops->dt_ro);
+ return dev->dd_ops->dt_ro(env, dev);
+}
+
+static inline int dt_declare_insert(const struct lu_env *env,
+ struct dt_object *dt,
+ const struct dt_rec *rec,
+ const struct dt_key *key,
+ struct thandle *th)
+{
+ LASSERT(dt);
+ LASSERT(dt->do_index_ops);
+ LASSERT(dt->do_index_ops->dio_declare_insert);
+ return dt->do_index_ops->dio_declare_insert(env, dt, rec, key, th);
+}
+
+static inline int dt_insert(const struct lu_env *env,
+ struct dt_object *dt,
+ const struct dt_rec *rec,
+ const struct dt_key *key,
+ struct thandle *th,
+ struct lustre_capa *capa,
+ int noquota)
+{
+ LASSERT(dt);
+ LASSERT(dt->do_index_ops);
+ LASSERT(dt->do_index_ops->dio_insert);
+ return dt->do_index_ops->dio_insert(env, dt, rec, key, th,
+ capa, noquota);
+}
+
+static inline int dt_declare_xattr_del(const struct lu_env *env,
+ struct dt_object *dt,
+ const char *name,
+ struct thandle *th)
+{
+ LASSERT(dt);
+ LASSERT(dt->do_ops);
+ LASSERT(dt->do_ops->do_declare_xattr_del);
+ return dt->do_ops->do_declare_xattr_del(env, dt, name, th);
+}
+
+static inline int dt_xattr_del(const struct lu_env *env,
+ struct dt_object *dt, const char *name,
+ struct thandle *th,
+ struct lustre_capa *capa)
+{
+ LASSERT(dt);
+ LASSERT(dt->do_ops);
+ LASSERT(dt->do_ops->do_xattr_del);
+ return dt->do_ops->do_xattr_del(env, dt, name, th, capa);
+}
+
+static inline int dt_declare_xattr_set(const struct lu_env *env,
+ struct dt_object *dt,
+ const struct lu_buf *buf,
+ const char *name, int fl,
+ struct thandle *th)
+{
+ LASSERT(dt);
+ LASSERT(dt->do_ops);
+ LASSERT(dt->do_ops->do_declare_xattr_set);
+ return dt->do_ops->do_declare_xattr_set(env, dt, buf, name, fl, th);
+}
+
+static inline int dt_xattr_set(const struct lu_env *env,
+ struct dt_object *dt, const struct lu_buf *buf,
+ const char *name, int fl, struct thandle *th,
+ struct lustre_capa *capa)
+{
+ LASSERT(dt);
+ LASSERT(dt->do_ops);
+ LASSERT(dt->do_ops->do_xattr_set);
+ return dt->do_ops->do_xattr_set(env, dt, buf, name, fl, th, capa);
+}
+
+static inline int dt_xattr_get(const struct lu_env *env,
+ struct dt_object *dt, struct lu_buf *buf,
+ const char *name, struct lustre_capa *capa)
+{
+ LASSERT(dt);
+ LASSERT(dt->do_ops);
+ LASSERT(dt->do_ops->do_xattr_get);
+ return dt->do_ops->do_xattr_get(env, dt, buf, name, capa);
+}
+
+static inline int dt_xattr_list(const struct lu_env *env,
+ struct dt_object *dt, struct lu_buf *buf,
+ struct lustre_capa *capa)
+{
+ LASSERT(dt);
+ LASSERT(dt->do_ops);
+ LASSERT(dt->do_ops->do_xattr_list);
+ return dt->do_ops->do_xattr_list(env, dt, buf, capa);
+}
+
+static inline int dt_declare_delete(const struct lu_env *env,
+ struct dt_object *dt,
+ const struct dt_key *key,
+ struct thandle *th)
+{
+ LASSERT(dt);
+ LASSERT(dt->do_index_ops);
+ LASSERT(dt->do_index_ops->dio_declare_delete);
+ return dt->do_index_ops->dio_declare_delete(env, dt, key, th);
+}
+
+static inline int dt_delete(const struct lu_env *env,
+ struct dt_object *dt,
+ const struct dt_key *key,
+ struct thandle *th,
+ struct lustre_capa *capa)
+{
+ LASSERT(dt);
+ LASSERT(dt->do_index_ops);
+ LASSERT(dt->do_index_ops->dio_delete);
+ return dt->do_index_ops->dio_delete(env, dt, key, th, capa);
+}
+
+static inline int dt_commit_async(const struct lu_env *env,
+ struct dt_device *dev)
+{
+ LASSERT(dev);
+ LASSERT(dev->dd_ops);
+ LASSERT(dev->dd_ops->dt_commit_async);
+ return dev->dd_ops->dt_commit_async(env, dev);
+}
+
+static inline int dt_init_capa_ctxt(const struct lu_env *env,
+ struct dt_device *dev,
+ int mode, unsigned long timeout,
+ __u32 alg, struct lustre_capa_key *keys)
+{
+ LASSERT(dev);
+ LASSERT(dev->dd_ops);
+ LASSERT(dev->dd_ops->dt_init_capa_ctxt);
+ return dev->dd_ops->dt_init_capa_ctxt(env, dev, mode,
+ timeout, alg, keys);
+}
+
+static inline int dt_lookup(const struct lu_env *env,
+ struct dt_object *dt,
+ struct dt_rec *rec,
+ const struct dt_key *key,
+ struct lustre_capa *capa)
+{
+ int ret;
+
+ LASSERT(dt);
+ LASSERT(dt->do_index_ops);
+ LASSERT(dt->do_index_ops->dio_lookup);
+
+ ret = dt->do_index_ops->dio_lookup(env, dt, rec, key, capa);
+ if (ret > 0)
+ ret = 0;
+ else if (ret == 0)
+ ret = -ENOENT;
+ return ret;
+}
+
+#define LU221_BAD_TIME (0x80000000U + 24 * 3600)
+
+struct dt_find_hint {
+ struct lu_fid *dfh_fid;
+ struct dt_device *dfh_dt;
+ struct dt_object *dfh_o;
+};
+
+struct dt_thread_info {
+ char dti_buf[DT_MAX_PATH];
+ struct dt_find_hint dti_dfh;
+ struct lu_attr dti_attr;
+ struct lu_fid dti_fid;
+ struct dt_object_format dti_dof;
+ struct lustre_mdt_attrs dti_lma;
+ struct lu_buf dti_lb;
+ loff_t dti_off;
+};
+
+extern struct lu_context_key dt_key;
+
+static inline struct dt_thread_info *dt_info(const struct lu_env *env)
+{
+ struct dt_thread_info *dti;
+
+ dti = lu_context_key_get(&env->le_ctx, &dt_key);
+ LASSERT(dti);
+ return dti;
+}
+
+int dt_global_init(void);
+void dt_global_fini(void);
+
+#if defined (CONFIG_PROC_FS)
+int lprocfs_dt_rd_blksize(char *page, char **start, off_t off,
+ int count, int *eof, void *data);
+int lprocfs_dt_rd_kbytestotal(char *page, char **start, off_t off,
+ int count, int *eof, void *data);
+int lprocfs_dt_rd_kbytesfree(char *page, char **start, off_t off,
+ int count, int *eof, void *data);
+int lprocfs_dt_rd_kbytesavail(char *page, char **start, off_t off,
+ int count, int *eof, void *data);
+int lprocfs_dt_rd_filestotal(char *page, char **start, off_t off,
+ int count, int *eof, void *data);
+int lprocfs_dt_rd_filesfree(char *page, char **start, off_t off,
+ int count, int *eof, void *data);
+#endif /* CONFIG_PROC_FS */
+
+#endif /* __LUSTRE_DT_OBJECT_H */
diff --git a/drivers/staging/lustre/lustre/include/interval_tree.h b/drivers/staging/lustre/lustre/include/interval_tree.h
new file mode 100644
index 000000000..bf9027d5f
--- /dev/null
+++ b/drivers/staging/lustre/lustre/include/interval_tree.h
@@ -0,0 +1,124 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ *
+ * lustre/include/interval_tree.h
+ *
+ * Author: Huang Wei <huangwei@clusterfs.com>
+ * Author: Jay Xiong <jinshan.xiong@sun.com>
+ */
+
+#ifndef _INTERVAL_H__
+#define _INTERVAL_H__
+
+#include "../../include/linux/libcfs/libcfs.h" /* LASSERT. */
+
+struct interval_node {
+ struct interval_node *in_left;
+ struct interval_node *in_right;
+ struct interval_node *in_parent;
+ unsigned in_color:1,
+ in_intree:1, /** set if the node is in tree */
+ in_res1:30;
+ __u8 in_res2[4]; /** tags, 8-bytes aligned */
+ __u64 in_max_high;
+ struct interval_node_extent {
+ __u64 start;
+ __u64 end;
+ } in_extent;
+};
+
+enum interval_iter {
+ INTERVAL_ITER_CONT = 1,
+ INTERVAL_ITER_STOP = 2
+};
+
+static inline int interval_is_intree(struct interval_node *node)
+{
+ return node->in_intree == 1;
+}
+
+static inline __u64 interval_low(struct interval_node *node)
+{
+ return node->in_extent.start;
+}
+
+static inline __u64 interval_high(struct interval_node *node)
+{
+ return node->in_extent.end;
+}
+
+static inline void interval_set(struct interval_node *node,
+ __u64 start, __u64 end)
+{
+ LASSERT(start <= end);
+ node->in_extent.start = start;
+ node->in_extent.end = end;
+ node->in_max_high = end;
+}
+
+/* Rules to write an interval callback.
+ * - the callback returns INTERVAL_ITER_STOP when it thinks the iteration
+ * should be stopped. It will then cause the iteration function to return
+ * immediately with return value INTERVAL_ITER_STOP.
+ * - callbacks for interval_iterate and interval_iterate_reverse: Every
+ * nodes in the tree will be set to @node before the callback being called
+ * - callback for interval_search: Only overlapped node will be set to @node
+ * before the callback being called.
+ */
+typedef enum interval_iter (*interval_callback_t)(struct interval_node *node,
+ void *args);
+
+struct interval_node *interval_insert(struct interval_node *node,
+ struct interval_node **root);
+void interval_erase(struct interval_node *node, struct interval_node **root);
+
+/* Search the extents in the tree and call @func for each overlapped
+ * extents. */
+enum interval_iter interval_search(struct interval_node *root,
+ struct interval_node_extent *ex,
+ interval_callback_t func, void *data);
+
+/* Iterate every node in the tree - by reverse order or regular order. */
+enum interval_iter interval_iterate(struct interval_node *root,
+ interval_callback_t func, void *data);
+enum interval_iter interval_iterate_reverse(struct interval_node *root,
+ interval_callback_t func, void *data);
+
+void interval_expand(struct interval_node *root,
+ struct interval_node_extent *ext,
+ struct interval_node_extent *limiter);
+int interval_is_overlapped(struct interval_node *root,
+ struct interval_node_extent *ex);
+struct interval_node *interval_find(struct interval_node *root,
+ struct interval_node_extent *ex);
+#endif
diff --git a/drivers/staging/lustre/lustre/include/lclient.h b/drivers/staging/lustre/lustre/include/lclient.h
new file mode 100644
index 000000000..c5c3a8d9e
--- /dev/null
+++ b/drivers/staging/lustre/lustre/include/lclient.h
@@ -0,0 +1,433 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2011, 2012, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ *
+ * Definitions shared between vvp and liblustre, and other clients in the
+ * future.
+ *
+ * Author: Oleg Drokin <oleg.drokin@sun.com>
+ * Author: Nikita Danilov <nikita.danilov@sun.com>
+ */
+
+#ifndef LCLIENT_H
+#define LCLIENT_H
+
+blkcnt_t dirty_cnt(struct inode *inode);
+
+int cl_glimpse_size0(struct inode *inode, int agl);
+int cl_glimpse_lock(const struct lu_env *env, struct cl_io *io,
+ struct inode *inode, struct cl_object *clob, int agl);
+
+static inline int cl_glimpse_size(struct inode *inode)
+{
+ return cl_glimpse_size0(inode, 0);
+}
+
+static inline int cl_agl(struct inode *inode)
+{
+ return cl_glimpse_size0(inode, 1);
+}
+
+/**
+ * Locking policy for setattr.
+ */
+enum ccc_setattr_lock_type {
+ /** Locking is done by server */
+ SETATTR_NOLOCK,
+ /** Extent lock is enqueued */
+ SETATTR_EXTENT_LOCK,
+ /** Existing local extent lock is used */
+ SETATTR_MATCH_LOCK
+};
+
+
+/**
+ * IO state private to vvp or slp layers.
+ */
+struct ccc_io {
+ /** super class */
+ struct cl_io_slice cui_cl;
+ struct cl_io_lock_link cui_link;
+ /**
+ * I/O vector information to or from which read/write is going.
+ */
+ struct iov_iter *cui_iter;
+ /**
+ * Total size for the left IO.
+ */
+ size_t cui_tot_count;
+
+ union {
+ struct {
+ enum ccc_setattr_lock_type cui_local_lock;
+ } setattr;
+ } u;
+ /**
+ * True iff io is processing glimpse right now.
+ */
+ int cui_glimpse;
+ /**
+ * Layout version when this IO is initialized
+ */
+ __u32 cui_layout_gen;
+ /**
+ * File descriptor against which IO is done.
+ */
+ struct ll_file_data *cui_fd;
+ struct kiocb *cui_iocb;
+};
+
+/**
+ * True, if \a io is a normal io, False for splice_{read,write}.
+ * must be implemented in arch specific code.
+ */
+int cl_is_normalio(const struct lu_env *env, const struct cl_io *io);
+
+extern struct lu_context_key ccc_key;
+extern struct lu_context_key ccc_session_key;
+
+struct ccc_thread_info {
+ struct cl_lock_descr cti_descr;
+ struct cl_io cti_io;
+ struct cl_attr cti_attr;
+};
+
+static inline struct ccc_thread_info *ccc_env_info(const struct lu_env *env)
+{
+ struct ccc_thread_info *info;
+
+ info = lu_context_key_get(&env->le_ctx, &ccc_key);
+ LASSERT(info != NULL);
+ return info;
+}
+
+static inline struct cl_attr *ccc_env_thread_attr(const struct lu_env *env)
+{
+ struct cl_attr *attr = &ccc_env_info(env)->cti_attr;
+
+ memset(attr, 0, sizeof(*attr));
+ return attr;
+}
+
+static inline struct cl_io *ccc_env_thread_io(const struct lu_env *env)
+{
+ struct cl_io *io = &ccc_env_info(env)->cti_io;
+
+ memset(io, 0, sizeof(*io));
+ return io;
+}
+
+struct ccc_session {
+ struct ccc_io cs_ios;
+};
+
+static inline struct ccc_session *ccc_env_session(const struct lu_env *env)
+{
+ struct ccc_session *ses;
+
+ ses = lu_context_key_get(env->le_ses, &ccc_session_key);
+ LASSERT(ses != NULL);
+ return ses;
+}
+
+static inline struct ccc_io *ccc_env_io(const struct lu_env *env)
+{
+ return &ccc_env_session(env)->cs_ios;
+}
+
+/**
+ * ccc-private object state.
+ */
+struct ccc_object {
+ struct cl_object_header cob_header;
+ struct cl_object cob_cl;
+ struct inode *cob_inode;
+
+ /**
+ * A list of dirty pages pending IO in the cache. Used by
+ * SOM. Protected by ll_inode_info::lli_lock.
+ *
+ * \see ccc_page::cpg_pending_linkage
+ */
+ struct list_head cob_pending_list;
+
+ /**
+ * Access this counter is protected by inode->i_sem. Now that
+ * the lifetime of transient pages must be covered by inode sem,
+ * we don't need to hold any lock..
+ */
+ int cob_transient_pages;
+ /**
+ * Number of outstanding mmaps on this file.
+ *
+ * \see ll_vm_open(), ll_vm_close().
+ */
+ atomic_t cob_mmap_cnt;
+
+ /**
+ * various flags
+ * cob_discard_page_warned
+ * if pages belonging to this object are discarded when a client
+ * is evicted, some debug info will be printed, this flag will be set
+ * during processing the first discarded page, then avoid flooding
+ * debug message for lots of discarded pages.
+ *
+ * \see ll_dirty_page_discard_warn.
+ */
+ unsigned int cob_discard_page_warned:1;
+};
+
+/**
+ * ccc-private page state.
+ */
+struct ccc_page {
+ struct cl_page_slice cpg_cl;
+ int cpg_defer_uptodate;
+ int cpg_ra_used;
+ int cpg_write_queued;
+ /**
+ * Non-empty iff this page is already counted in
+ * ccc_object::cob_pending_list. Protected by
+ * ccc_object::cob_pending_guard. This list is only used as a flag,
+ * that is, never iterated through, only checked for list_empty(), but
+ * having a list is useful for debugging.
+ */
+ struct list_head cpg_pending_linkage;
+ /** VM page */
+ struct page *cpg_page;
+};
+
+static inline struct ccc_page *cl2ccc_page(const struct cl_page_slice *slice)
+{
+ return container_of(slice, struct ccc_page, cpg_cl);
+}
+
+struct cl_page *ccc_vmpage_page_transient(struct page *vmpage);
+
+struct ccc_device {
+ struct cl_device cdv_cl;
+ struct super_block *cdv_sb;
+ struct cl_device *cdv_next;
+};
+
+struct ccc_lock {
+ struct cl_lock_slice clk_cl;
+};
+
+struct ccc_req {
+ struct cl_req_slice crq_cl;
+};
+
+void *ccc_key_init (const struct lu_context *ctx,
+ struct lu_context_key *key);
+void ccc_key_fini (const struct lu_context *ctx,
+ struct lu_context_key *key, void *data);
+void *ccc_session_key_init(const struct lu_context *ctx,
+ struct lu_context_key *key);
+void ccc_session_key_fini(const struct lu_context *ctx,
+ struct lu_context_key *key, void *data);
+
+int ccc_device_init (const struct lu_env *env,
+ struct lu_device *d,
+ const char *name, struct lu_device *next);
+struct lu_device *ccc_device_fini (const struct lu_env *env,
+ struct lu_device *d);
+struct lu_device *ccc_device_alloc(const struct lu_env *env,
+ struct lu_device_type *t,
+ struct lustre_cfg *cfg,
+ const struct lu_device_operations *luops,
+ const struct cl_device_operations *clops);
+struct lu_device *ccc_device_free (const struct lu_env *env,
+ struct lu_device *d);
+struct lu_object *ccc_object_alloc(const struct lu_env *env,
+ const struct lu_object_header *hdr,
+ struct lu_device *dev,
+ const struct cl_object_operations *clops,
+ const struct lu_object_operations *luops);
+
+int ccc_req_init(const struct lu_env *env, struct cl_device *dev,
+ struct cl_req *req);
+void ccc_umount(const struct lu_env *env, struct cl_device *dev);
+int ccc_global_init(struct lu_device_type *device_type);
+void ccc_global_fini(struct lu_device_type *device_type);
+int ccc_object_init0(const struct lu_env *env, struct ccc_object *vob,
+ const struct cl_object_conf *conf);
+int ccc_object_init(const struct lu_env *env, struct lu_object *obj,
+ const struct lu_object_conf *conf);
+void ccc_object_free(const struct lu_env *env, struct lu_object *obj);
+int ccc_lock_init(const struct lu_env *env, struct cl_object *obj,
+ struct cl_lock *lock, const struct cl_io *io,
+ const struct cl_lock_operations *lkops);
+int ccc_attr_set(const struct lu_env *env, struct cl_object *obj,
+ const struct cl_attr *attr, unsigned valid);
+int ccc_object_glimpse(const struct lu_env *env,
+ const struct cl_object *obj, struct ost_lvb *lvb);
+int ccc_conf_set(const struct lu_env *env, struct cl_object *obj,
+ const struct cl_object_conf *conf);
+struct page *ccc_page_vmpage(const struct lu_env *env,
+ const struct cl_page_slice *slice);
+int ccc_page_is_under_lock(const struct lu_env *env,
+ const struct cl_page_slice *slice, struct cl_io *io);
+int ccc_fail(const struct lu_env *env, const struct cl_page_slice *slice);
+void ccc_transient_page_verify(const struct cl_page *page);
+int ccc_transient_page_own(const struct lu_env *env,
+ const struct cl_page_slice *slice,
+ struct cl_io *io, int nonblock);
+void ccc_transient_page_assume(const struct lu_env *env,
+ const struct cl_page_slice *slice,
+ struct cl_io *io);
+void ccc_transient_page_unassume(const struct lu_env *env,
+ const struct cl_page_slice *slice,
+ struct cl_io *io);
+void ccc_transient_page_disown(const struct lu_env *env,
+ const struct cl_page_slice *slice,
+ struct cl_io *io);
+void ccc_transient_page_discard(const struct lu_env *env,
+ const struct cl_page_slice *slice,
+ struct cl_io *io);
+int ccc_transient_page_prep(const struct lu_env *env,
+ const struct cl_page_slice *slice,
+ struct cl_io *io);
+void ccc_lock_delete(const struct lu_env *env,
+ const struct cl_lock_slice *slice);
+void ccc_lock_fini(const struct lu_env *env, struct cl_lock_slice *slice);
+int ccc_lock_enqueue(const struct lu_env *env,
+ const struct cl_lock_slice *slice,
+ struct cl_io *io, __u32 enqflags);
+int ccc_lock_use(const struct lu_env *env, const struct cl_lock_slice *slice);
+int ccc_lock_unuse(const struct lu_env *env, const struct cl_lock_slice *slice);
+int ccc_lock_wait(const struct lu_env *env, const struct cl_lock_slice *slice);
+int ccc_lock_fits_into(const struct lu_env *env,
+ const struct cl_lock_slice *slice,
+ const struct cl_lock_descr *need,
+ const struct cl_io *io);
+void ccc_lock_state(const struct lu_env *env,
+ const struct cl_lock_slice *slice,
+ enum cl_lock_state state);
+
+void ccc_io_fini(const struct lu_env *env, const struct cl_io_slice *ios);
+int ccc_io_one_lock_index(const struct lu_env *env, struct cl_io *io,
+ __u32 enqflags, enum cl_lock_mode mode,
+ pgoff_t start, pgoff_t end);
+int ccc_io_one_lock(const struct lu_env *env, struct cl_io *io,
+ __u32 enqflags, enum cl_lock_mode mode,
+ loff_t start, loff_t end);
+void ccc_io_end(const struct lu_env *env, const struct cl_io_slice *ios);
+void ccc_io_advance(const struct lu_env *env, const struct cl_io_slice *ios,
+ size_t nob);
+void ccc_io_update_iov(const struct lu_env *env, struct ccc_io *cio,
+ struct cl_io *io);
+int ccc_prep_size(const struct lu_env *env, struct cl_object *obj,
+ struct cl_io *io, loff_t start, size_t count, int *exceed);
+void ccc_req_completion(const struct lu_env *env,
+ const struct cl_req_slice *slice, int ioret);
+void ccc_req_attr_set(const struct lu_env *env,
+ const struct cl_req_slice *slice,
+ const struct cl_object *obj,
+ struct cl_req_attr *oa, u64 flags);
+
+struct lu_device *ccc2lu_dev (struct ccc_device *vdv);
+struct lu_object *ccc2lu (struct ccc_object *vob);
+struct ccc_device *lu2ccc_dev (const struct lu_device *d);
+struct ccc_device *cl2ccc_dev (const struct cl_device *d);
+struct ccc_object *lu2ccc (const struct lu_object *obj);
+struct ccc_object *cl2ccc (const struct cl_object *obj);
+struct ccc_lock *cl2ccc_lock (const struct cl_lock_slice *slice);
+struct ccc_io *cl2ccc_io (const struct lu_env *env,
+ const struct cl_io_slice *slice);
+struct ccc_req *cl2ccc_req (const struct cl_req_slice *slice);
+struct page *cl2vm_page (const struct cl_page_slice *slice);
+struct inode *ccc_object_inode(const struct cl_object *obj);
+struct ccc_object *cl_inode2ccc (struct inode *inode);
+
+int cl_setattr_ost(struct inode *inode, const struct iattr *attr,
+ struct obd_capa *capa);
+
+struct cl_page *ccc_vmpage_page_transient(struct page *vmpage);
+int ccc_object_invariant(const struct cl_object *obj);
+int cl_file_inode_init(struct inode *inode, struct lustre_md *md);
+void cl_inode_fini(struct inode *inode);
+int cl_local_size(struct inode *inode);
+
+__u16 ll_dirent_type_get(struct lu_dirent *ent);
+__u64 cl_fid_build_ino(const struct lu_fid *fid, int api32);
+__u32 cl_fid_build_gen(const struct lu_fid *fid);
+
+# define CLOBINVRNT(env, clob, expr) \
+ ((void)sizeof(env), (void)sizeof(clob), (void)sizeof(!!(expr)))
+
+int cl_init_ea_size(struct obd_export *md_exp, struct obd_export *dt_exp);
+int cl_ocd_update(struct obd_device *host,
+ struct obd_device *watched,
+ enum obd_notify_event ev, void *owner, void *data);
+
+struct ccc_grouplock {
+ struct lu_env *cg_env;
+ struct cl_io *cg_io;
+ struct cl_lock *cg_lock;
+ unsigned long cg_gid;
+};
+
+int cl_get_grouplock(struct cl_object *obj, unsigned long gid, int nonblock,
+ struct ccc_grouplock *cg);
+void cl_put_grouplock(struct ccc_grouplock *cg);
+
+/**
+ * New interfaces to get and put lov_stripe_md from lov layer. This violates
+ * layering because lov_stripe_md is supposed to be a private data in lov.
+ *
+ * NB: If you find you have to use these interfaces for your new code, please
+ * think about it again. These interfaces may be removed in the future for
+ * better layering. */
+struct lov_stripe_md *lov_lsm_get(struct cl_object *clobj);
+void lov_lsm_put(struct cl_object *clobj, struct lov_stripe_md *lsm);
+int lov_read_and_clear_async_rc(struct cl_object *clob);
+
+struct lov_stripe_md *ccc_inode_lsm_get(struct inode *inode);
+void ccc_inode_lsm_put(struct inode *inode, struct lov_stripe_md *lsm);
+
+/**
+ * Data structure managing a client's cached clean pages. An LRU of
+ * pages is maintained, along with other statistics.
+ */
+struct cl_client_cache {
+ atomic_t ccc_users; /* # of users (OSCs) of this data */
+ struct list_head ccc_lru; /* LRU list of cached clean pages */
+ spinlock_t ccc_lru_lock; /* lock for list */
+ atomic_t ccc_lru_left; /* # of LRU entries available */
+ unsigned long ccc_lru_max; /* Max # of LRU entries possible */
+ unsigned int ccc_lru_shrinkers; /* # of threads reclaiming */
+};
+
+#endif /*LCLIENT_H */
diff --git a/drivers/staging/lustre/lustre/include/linux/lustre_compat25.h b/drivers/staging/lustre/lustre/include/linux/lustre_compat25.h
new file mode 100644
index 000000000..3925db160
--- /dev/null
+++ b/drivers/staging/lustre/lustre/include/linux/lustre_compat25.h
@@ -0,0 +1,216 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2011, 2012, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ */
+
+#ifndef _LINUX_COMPAT25_H
+#define _LINUX_COMPAT25_H
+
+#include <linux/fs_struct.h>
+#include <linux/namei.h>
+
+#include "lustre_patchless_compat.h"
+
+/*
+ * set ATTR_BLOCKS to a high value to avoid any risk of collision with other
+ * ATTR_* attributes (see bug 13828)
+ */
+#define ATTR_BLOCKS (1 << 27)
+
+#define current_ngroups current_cred()->group_info->ngroups
+#define current_groups current_cred()->group_info->small_block
+
+/*
+ * OBD need working random driver, thus all our
+ * initialization routines must be called after device
+ * driver initialization
+ */
+#ifndef MODULE
+#undef module_init
+#define module_init(a) late_initcall(a)
+#endif
+
+
+#define LTIME_S(time) (time.tv_sec)
+
+/* inode_dio_wait(i) use as-is for write lock */
+# define inode_dio_write_done(i) do {} while (0) /* for write unlock */
+# define inode_dio_read(i) atomic_inc(&(i)->i_dio_count)
+/* inode_dio_done(i) use as-is for read unlock */
+
+
+#ifndef FS_HAS_FIEMAP
+#define FS_HAS_FIEMAP (0)
+#endif
+
+#define ll_vfs_rmdir(dir, entry, mnt) vfs_rmdir(dir, entry)
+#define ll_vfs_mkdir(inode, dir, mnt, mode) vfs_mkdir(inode, dir, mode)
+#define ll_vfs_link(old, mnt, dir, new, mnt1) vfs_link(old, dir, new)
+#define ll_vfs_unlink(inode, entry, mnt) vfs_unlink(inode, entry)
+#define ll_vfs_mknod(dir, entry, mnt, mode, dev) \
+ vfs_mknod(dir, entry, mode, dev)
+#define ll_security_inode_unlink(dir, entry, mnt) \
+ security_inode_unlink(dir, entry)
+#define ll_vfs_rename(old, old_dir, mnt, new, new_dir, mnt1) \
+ vfs_rename(old, old_dir, new, new_dir, NULL, 0)
+
+#define cfs_bio_io_error(a, b) bio_io_error((a))
+#define cfs_bio_endio(a, b, c) bio_endio((a), (c))
+
+#define cfs_path_put(nd) path_put(&(nd)->path)
+
+
+#ifndef SLAB_DESTROY_BY_RCU
+#define SLAB_DESTROY_BY_RCU 0
+#endif
+
+
+
+static inline int
+ll_quota_on(struct super_block *sb, int off, int ver, char *name, int remount)
+{
+ int rc;
+
+ if (sb->s_qcop->quota_on) {
+ struct path path;
+
+ rc = kern_path(name, LOOKUP_FOLLOW, &path);
+ if (!rc)
+ return rc;
+ rc = sb->s_qcop->quota_on(sb, off, ver
+ , &path
+ );
+ path_put(&path);
+ return rc;
+ } else
+ return -ENOSYS;
+}
+
+static inline int ll_quota_off(struct super_block *sb, int off, int remount)
+{
+ if (sb->s_qcop->quota_off) {
+ return sb->s_qcop->quota_off(sb, off
+ );
+ } else
+ return -ENOSYS;
+}
+
+
+# define ll_vfs_dq_init dquot_initialize
+# define ll_vfs_dq_drop dquot_drop
+# define ll_vfs_dq_transfer dquot_transfer
+# define ll_vfs_dq_off(sb, remount) dquot_suspend(sb, -1)
+
+
+
+
+
+#define queue_max_phys_segments(rq) queue_max_segments(rq)
+#define queue_max_hw_segments(rq) queue_max_segments(rq)
+
+
+#define ll_d_hlist_node hlist_node
+#define ll_d_hlist_empty(list) hlist_empty(list)
+#define ll_d_hlist_entry(ptr, type, name) hlist_entry(ptr.first, type, name)
+#define ll_d_hlist_for_each(tmp, i_dentry) hlist_for_each(tmp, i_dentry)
+#define ll_d_hlist_for_each_entry(dentry, p, i_dentry, alias) \
+ p = NULL; hlist_for_each_entry(dentry, i_dentry, alias)
+
+
+#define bio_hw_segments(q, bio) 0
+
+
+#define ll_pagevec_init(pv, cold) do {} while (0)
+#define ll_pagevec_add(pv, pg) (0)
+#define ll_pagevec_lru_add_file(pv) do {} while (0)
+
+
+#ifndef QUOTA_OK
+# define QUOTA_OK 0
+#endif
+#ifndef NO_QUOTA
+# define NO_QUOTA (-EDQUOT)
+#endif
+
+#ifndef SEEK_DATA
+#define SEEK_DATA 3 /* seek to the next data */
+#endif
+#ifndef SEEK_HOLE
+#define SEEK_HOLE 4 /* seek to the next hole */
+#endif
+
+#ifndef FMODE_UNSIGNED_OFFSET
+#define FMODE_UNSIGNED_OFFSET ((__force fmode_t)0x2000)
+#endif
+
+#if !defined(_ASM_GENERIC_BITOPS_EXT2_NON_ATOMIC_H_) && !defined(ext2_set_bit)
+# define ext2_set_bit __test_and_set_bit_le
+# define ext2_clear_bit __test_and_clear_bit_le
+# define ext2_test_bit test_bit_le
+# define ext2_find_first_zero_bit find_first_zero_bit_le
+# define ext2_find_next_zero_bit find_next_zero_bit_le
+#endif
+
+#ifdef ATTR_TIMES_SET
+# define TIMES_SET_FLAGS (ATTR_MTIME_SET | ATTR_ATIME_SET | ATTR_TIMES_SET)
+#else
+# define TIMES_SET_FLAGS (ATTR_MTIME_SET | ATTR_ATIME_SET)
+#endif
+
+
+
+/*
+ * After 3.1, kernel's nameidata.intent.open.flags is different
+ * with lustre's lookup_intent.it_flags, as lustre's it_flags'
+ * lower bits equal to FMODE_xxx while kernel doesn't transliterate
+ * lower bits of nameidata.intent.open.flags to FMODE_xxx.
+ * */
+#include <linux/version.h>
+static inline int ll_namei_to_lookup_intent_flag(int flag)
+{
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 1, 0)
+ flag = (flag & ~O_ACCMODE) | OPEN_FMODE(flag);
+#endif
+ return flag;
+}
+
+#include <linux/fs.h>
+
+# define ll_umode_t umode_t
+
+#include <linux/dcache.h>
+
+# define ll_dirty_inode(inode, flag) (inode)->i_sb->s_op->dirty_inode((inode), flag)
+
+#endif /* _COMPAT25_H */
diff --git a/drivers/staging/lustre/lustre/include/linux/lustre_lite.h b/drivers/staging/lustre/lustre/include/linux/lustre_lite.h
new file mode 100644
index 000000000..a7658a99a
--- /dev/null
+++ b/drivers/staging/lustre/lustre/include/linux/lustre_lite.h
@@ -0,0 +1,98 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2002, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ */
+
+#ifndef _LINUX_LL_H
+#define _LINUX_LL_H
+
+#ifndef _LL_H
+#error Do not #include this file directly. #include <lustre_lite.h> instead
+#endif
+
+
+#include <asm/statfs.h>
+
+#include <linux/fs.h>
+#include <linux/dcache.h>
+#include <linux/proc_fs.h>
+
+#include "../obd_class.h"
+#include "../lustre_net.h"
+#include "../lustre_ha.h"
+
+#include <linux/rbtree.h>
+#include "../../include/linux/lustre_compat25.h"
+#include <linux/pagemap.h>
+
+/* lprocfs.c */
+enum {
+ LPROC_LL_DIRTY_HITS = 0,
+ LPROC_LL_DIRTY_MISSES,
+ LPROC_LL_READ_BYTES,
+ LPROC_LL_WRITE_BYTES,
+ LPROC_LL_BRW_READ,
+ LPROC_LL_BRW_WRITE,
+ LPROC_LL_OSC_READ,
+ LPROC_LL_OSC_WRITE,
+ LPROC_LL_IOCTL,
+ LPROC_LL_OPEN,
+ LPROC_LL_RELEASE,
+ LPROC_LL_MAP,
+ LPROC_LL_LLSEEK,
+ LPROC_LL_FSYNC,
+ LPROC_LL_READDIR,
+ LPROC_LL_SETATTR,
+ LPROC_LL_TRUNC,
+ LPROC_LL_FLOCK,
+ LPROC_LL_GETATTR,
+ LPROC_LL_CREATE,
+ LPROC_LL_LINK,
+ LPROC_LL_UNLINK,
+ LPROC_LL_SYMLINK,
+ LPROC_LL_MKDIR,
+ LPROC_LL_RMDIR,
+ LPROC_LL_MKNOD,
+ LPROC_LL_RENAME,
+ LPROC_LL_STAFS,
+ LPROC_LL_ALLOC_INODE,
+ LPROC_LL_SETXATTR,
+ LPROC_LL_GETXATTR,
+ LPROC_LL_GETXATTR_HITS,
+ LPROC_LL_LISTXATTR,
+ LPROC_LL_REMOVEXATTR,
+ LPROC_LL_INODE_PERM,
+ LPROC_LL_FILE_OPCODES
+};
+
+
+#endif
diff --git a/drivers/staging/lustre/lustre/include/linux/lustre_patchless_compat.h b/drivers/staging/lustre/lustre/include/linux/lustre_patchless_compat.h
new file mode 100644
index 000000000..d72605864
--- /dev/null
+++ b/drivers/staging/lustre/lustre/include/linux/lustre_patchless_compat.h
@@ -0,0 +1,85 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2011, 2012, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ */
+
+#ifndef LUSTRE_PATCHLESS_COMPAT_H
+#define LUSTRE_PATCHLESS_COMPAT_H
+
+#include <linux/fs.h>
+
+#include <linux/list.h>
+#include <linux/mm.h>
+#include <linux/hash.h>
+
+
+#define ll_delete_from_page_cache(page) delete_from_page_cache(page)
+
+static inline void
+truncate_complete_page(struct address_space *mapping, struct page *page)
+{
+ if (page->mapping != mapping)
+ return;
+
+ if (PagePrivate(page))
+ page->mapping->a_ops->invalidatepage(page, 0, PAGE_CACHE_SIZE);
+
+ if (TestClearPageDirty(page))
+ account_page_cleaned(page, mapping);
+
+ ClearPageMappedToDisk(page);
+ ll_delete_from_page_cache(page);
+}
+
+#ifdef ATTR_OPEN
+# define ATTR_FROM_OPEN ATTR_OPEN
+#else
+# ifndef ATTR_FROM_OPEN
+# define ATTR_FROM_OPEN 0
+# endif
+#endif /* ATTR_OPEN */
+
+#ifndef ATTR_RAW
+#define ATTR_RAW 0
+#endif
+
+#ifndef ATTR_CTIME_SET
+/*
+ * set ATTR_CTIME_SET to a high value to avoid any risk of collision with other
+ * ATTR_* attributes (see bug 13828)
+ */
+#define ATTR_CTIME_SET (1 << 28)
+#endif
+
+#endif /* LUSTRE_PATCHLESS_COMPAT_H */
diff --git a/drivers/staging/lustre/lustre/include/linux/lustre_user.h b/drivers/staging/lustre/lustre/include/linux/lustre_user.h
new file mode 100644
index 000000000..9cc2849f3
--- /dev/null
+++ b/drivers/staging/lustre/lustre/include/linux/lustre_user.h
@@ -0,0 +1,70 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2012, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ *
+ * lustre/include/linux/lustre_user.h
+ *
+ * Lustre public user-space interface definitions.
+ */
+
+#ifndef _LINUX_LUSTRE_USER_H
+#define _LINUX_LUSTRE_USER_H
+
+# include <linux/quota.h>
+
+/*
+ * asm-x86_64/processor.h on some SLES 9 distros seems to use
+ * kernel-only typedefs. fortunately skipping it altogether is ok
+ * (for now).
+ */
+#define __ASM_X86_64_PROCESSOR_H
+
+#include <linux/string.h>
+
+/*
+ * We need to always use 64bit version because the structure
+ * is shared across entire cluster where 32bit and 64bit machines
+ * are co-existing.
+ */
+#if __BITS_PER_LONG != 64 || defined(__ARCH_WANT_STAT64)
+typedef struct stat64 lstat_t;
+#define lstat_f lstat64
+#else
+typedef struct stat lstat_t;
+#define lstat_f lstat
+#endif
+
+#define HAVE_LOV_USER_MDS_DATA
+
+#endif /* _LUSTRE_USER_H */
diff --git a/drivers/staging/lustre/lustre/include/linux/obd.h b/drivers/staging/lustre/lustre/include/linux/obd.h
new file mode 100644
index 000000000..9cd868357
--- /dev/null
+++ b/drivers/staging/lustre/lustre/include/linux/obd.h
@@ -0,0 +1,125 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2011, 2012, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ */
+
+#ifndef __LINUX_OBD_H
+#define __LINUX_OBD_H
+
+#ifndef __OBD_H
+#error Do not #include this file directly. #include <obd.h> instead
+#endif
+
+#include "../obd_support.h"
+
+# include <linux/fs.h>
+# include <linux/list.h>
+# include <linux/sched.h> /* for struct task_struct, for current.h */
+# include <linux/proc_fs.h>
+# include <linux/mount.h>
+#include "../lustre_intent.h"
+
+struct ll_iattr {
+ struct iattr iattr;
+ unsigned int ia_attr_flags;
+};
+
+#define CLIENT_OBD_LIST_LOCK_DEBUG 1
+
+typedef struct {
+ spinlock_t lock;
+
+ unsigned long time;
+ struct task_struct *task;
+ const char *func;
+ int line;
+} client_obd_lock_t;
+
+static inline void __client_obd_list_lock(client_obd_lock_t *lock,
+ const char *func, int line)
+{
+ unsigned long cur = jiffies;
+ while (1) {
+ if (spin_trylock(&lock->lock)) {
+ LASSERT(lock->task == NULL);
+ lock->task = current;
+ lock->func = func;
+ lock->line = line;
+ lock->time = jiffies;
+ break;
+ }
+
+ if (time_before(cur + 5 * HZ, jiffies) &&
+ time_before(lock->time + 5 * HZ, jiffies)) {
+ struct task_struct *task = lock->task;
+
+ if (task == NULL)
+ continue;
+
+ LCONSOLE_WARN("%s:%d: lock %p was acquired by <%s:%d:%s:%d> for %lu seconds.\n",
+ current->comm, current->pid,
+ lock, task->comm, task->pid,
+ lock->func, lock->line,
+ (jiffies - lock->time) / HZ);
+ LCONSOLE_WARN("====== for current process =====\n");
+ dump_stack();
+ LCONSOLE_WARN("====== end =======\n");
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ schedule_timeout(1000 * HZ);
+ }
+ cpu_relax();
+ }
+}
+
+#define client_obd_list_lock(lock) \
+ __client_obd_list_lock(lock, __func__, __LINE__)
+
+static inline void client_obd_list_unlock(client_obd_lock_t *lock)
+{
+ LASSERT(lock->task != NULL);
+ lock->task = NULL;
+ lock->time = jiffies;
+ spin_unlock(&lock->lock);
+}
+
+
+static inline void client_obd_list_lock_init(client_obd_lock_t *lock)
+{
+ spin_lock_init(&lock->lock);
+}
+
+static inline void client_obd_list_lock_done(client_obd_lock_t *lock)
+{}
+
+#endif /* __LINUX_OBD_H */
diff --git a/drivers/staging/lustre/lustre/include/lprocfs_status.h b/drivers/staging/lustre/lustre/include/lprocfs_status.h
new file mode 100644
index 000000000..d030847e5
--- /dev/null
+++ b/drivers/staging/lustre/lustre/include/lprocfs_status.h
@@ -0,0 +1,1015 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2011, 2012, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ *
+ * lustre/include/lprocfs_status.h
+ *
+ * Top level header file for LProc SNMP
+ *
+ * Author: Hariharan Thantry thantry@users.sourceforge.net
+ */
+#ifndef _LPROCFS_SNMP_H
+#define _LPROCFS_SNMP_H
+
+#include <linux/proc_fs.h>
+#include <linux/seq_file.h>
+#include <linux/spinlock.h>
+#include <linux/types.h>
+
+#include "lustre/lustre_idl.h"
+
+struct lprocfs_vars {
+ const char *name;
+ struct file_operations *fops;
+ void *data;
+ /**
+ * /proc file mode.
+ */
+ umode_t proc_mode;
+};
+
+struct lprocfs_static_vars {
+ struct lprocfs_vars *module_vars;
+ struct lprocfs_vars *obd_vars;
+};
+
+/* if we find more consumers this could be generalized */
+#define OBD_HIST_MAX 32
+struct obd_histogram {
+ spinlock_t oh_lock;
+ unsigned long oh_buckets[OBD_HIST_MAX];
+};
+
+enum {
+ BRW_R_PAGES = 0,
+ BRW_W_PAGES,
+ BRW_R_RPC_HIST,
+ BRW_W_RPC_HIST,
+ BRW_R_IO_TIME,
+ BRW_W_IO_TIME,
+ BRW_R_DISCONT_PAGES,
+ BRW_W_DISCONT_PAGES,
+ BRW_R_DISCONT_BLOCKS,
+ BRW_W_DISCONT_BLOCKS,
+ BRW_R_DISK_IOSIZE,
+ BRW_W_DISK_IOSIZE,
+ BRW_R_DIO_FRAGS,
+ BRW_W_DIO_FRAGS,
+ BRW_LAST,
+};
+
+struct brw_stats {
+ struct obd_histogram hist[BRW_LAST];
+};
+
+enum {
+ RENAME_SAMEDIR_SIZE = 0,
+ RENAME_CROSSDIR_SRC_SIZE,
+ RENAME_CROSSDIR_TGT_SIZE,
+ RENAME_LAST,
+};
+
+struct rename_stats {
+ struct obd_histogram hist[RENAME_LAST];
+};
+
+/* An lprocfs counter can be configured using the enum bit masks below.
+ *
+ * LPROCFS_CNTR_EXTERNALLOCK indicates that an external lock already
+ * protects this counter from concurrent updates. If not specified,
+ * lprocfs an internal per-counter lock variable. External locks are
+ * not used to protect counter increments, but are used to protect
+ * counter readout and resets.
+ *
+ * LPROCFS_CNTR_AVGMINMAX indicates a multi-valued counter samples,
+ * (i.e. counter can be incremented by more than "1"). When specified,
+ * the counter maintains min, max and sum in addition to a simple
+ * invocation count. This allows averages to be be computed.
+ * If not specified, the counter is an increment-by-1 counter.
+ * min, max, sum, etc. are not maintained.
+ *
+ * LPROCFS_CNTR_STDDEV indicates that the counter should track sum of
+ * squares (for multi-valued counter samples only). This allows
+ * external computation of standard deviation, but involves a 64-bit
+ * multiply per counter increment.
+ */
+
+enum {
+ LPROCFS_CNTR_EXTERNALLOCK = 0x0001,
+ LPROCFS_CNTR_AVGMINMAX = 0x0002,
+ LPROCFS_CNTR_STDDEV = 0x0004,
+
+ /* counter data type */
+ LPROCFS_TYPE_REGS = 0x0100,
+ LPROCFS_TYPE_BYTES = 0x0200,
+ LPROCFS_TYPE_PAGES = 0x0400,
+ LPROCFS_TYPE_CYCLE = 0x0800,
+};
+
+#define LC_MIN_INIT ((~(__u64)0) >> 1)
+
+struct lprocfs_counter_header {
+ unsigned int lc_config;
+ const char *lc_name; /* must be static */
+ const char *lc_units; /* must be static */
+};
+
+struct lprocfs_counter {
+ __s64 lc_count;
+ __s64 lc_min;
+ __s64 lc_max;
+ __s64 lc_sumsquare;
+ /*
+ * Every counter has lc_array_sum[0], while lc_array_sum[1] is only
+ * for irq context counter, i.e. stats with
+ * LPROCFS_STATS_FLAG_IRQ_SAFE flag, its counter need
+ * lc_array_sum[1]
+ */
+ __s64 lc_array_sum[1];
+};
+#define lc_sum lc_array_sum[0]
+#define lc_sum_irq lc_array_sum[1]
+
+struct lprocfs_percpu {
+#ifndef __GNUC__
+ __s64 pad;
+#endif
+ struct lprocfs_counter lp_cntr[0];
+};
+
+#define LPROCFS_GET_NUM_CPU 0x0001
+#define LPROCFS_GET_SMP_ID 0x0002
+
+enum lprocfs_stats_flags {
+ LPROCFS_STATS_FLAG_NONE = 0x0000, /* per cpu counter */
+ LPROCFS_STATS_FLAG_NOPERCPU = 0x0001, /* stats have no percpu
+ * area and need locking */
+ LPROCFS_STATS_FLAG_IRQ_SAFE = 0x0002, /* alloc need irq safe */
+};
+
+enum lprocfs_fields_flags {
+ LPROCFS_FIELDS_FLAGS_CONFIG = 0x0001,
+ LPROCFS_FIELDS_FLAGS_SUM = 0x0002,
+ LPROCFS_FIELDS_FLAGS_MIN = 0x0003,
+ LPROCFS_FIELDS_FLAGS_MAX = 0x0004,
+ LPROCFS_FIELDS_FLAGS_AVG = 0x0005,
+ LPROCFS_FIELDS_FLAGS_SUMSQUARE = 0x0006,
+ LPROCFS_FIELDS_FLAGS_COUNT = 0x0007,
+};
+
+struct lprocfs_stats {
+ /* # of counters */
+ unsigned short ls_num;
+ /* 1 + the biggest cpu # whose ls_percpu slot has been allocated */
+ unsigned short ls_biggest_alloc_num;
+ enum lprocfs_stats_flags ls_flags;
+ /* Lock used when there are no percpu stats areas; For percpu stats,
+ * it is used to protect ls_biggest_alloc_num change */
+ spinlock_t ls_lock;
+
+ /* has ls_num of counter headers */
+ struct lprocfs_counter_header *ls_cnt_header;
+ struct lprocfs_percpu *ls_percpu[0];
+};
+
+#define OPC_RANGE(seg) (seg ## _LAST_OPC - seg ## _FIRST_OPC)
+
+/* Pack all opcodes down into a single monotonically increasing index */
+static inline int opcode_offset(__u32 opc) {
+ if (opc < OST_LAST_OPC) {
+ /* OST opcode */
+ return (opc - OST_FIRST_OPC);
+ } else if (opc < MDS_LAST_OPC) {
+ /* MDS opcode */
+ return (opc - MDS_FIRST_OPC +
+ OPC_RANGE(OST));
+ } else if (opc < LDLM_LAST_OPC) {
+ /* LDLM Opcode */
+ return (opc - LDLM_FIRST_OPC +
+ OPC_RANGE(MDS) +
+ OPC_RANGE(OST));
+ } else if (opc < MGS_LAST_OPC) {
+ /* MGS Opcode */
+ return (opc - MGS_FIRST_OPC +
+ OPC_RANGE(LDLM) +
+ OPC_RANGE(MDS) +
+ OPC_RANGE(OST));
+ } else if (opc < OBD_LAST_OPC) {
+ /* OBD Ping */
+ return (opc - OBD_FIRST_OPC +
+ OPC_RANGE(MGS) +
+ OPC_RANGE(LDLM) +
+ OPC_RANGE(MDS) +
+ OPC_RANGE(OST));
+ } else if (opc < LLOG_LAST_OPC) {
+ /* LLOG Opcode */
+ return (opc - LLOG_FIRST_OPC +
+ OPC_RANGE(OBD) +
+ OPC_RANGE(MGS) +
+ OPC_RANGE(LDLM) +
+ OPC_RANGE(MDS) +
+ OPC_RANGE(OST));
+ } else if (opc < QUOTA_LAST_OPC) {
+ /* LQUOTA Opcode */
+ return (opc - QUOTA_FIRST_OPC +
+ OPC_RANGE(LLOG) +
+ OPC_RANGE(OBD) +
+ OPC_RANGE(MGS) +
+ OPC_RANGE(LDLM) +
+ OPC_RANGE(MDS) +
+ OPC_RANGE(OST));
+ } else if (opc < SEQ_LAST_OPC) {
+ /* SEQ opcode */
+ return (opc - SEQ_FIRST_OPC +
+ OPC_RANGE(QUOTA) +
+ OPC_RANGE(LLOG) +
+ OPC_RANGE(OBD) +
+ OPC_RANGE(MGS) +
+ OPC_RANGE(LDLM) +
+ OPC_RANGE(MDS) +
+ OPC_RANGE(OST));
+ } else if (opc < SEC_LAST_OPC) {
+ /* SEC opcode */
+ return (opc - SEC_FIRST_OPC +
+ OPC_RANGE(SEQ) +
+ OPC_RANGE(QUOTA) +
+ OPC_RANGE(LLOG) +
+ OPC_RANGE(OBD) +
+ OPC_RANGE(MGS) +
+ OPC_RANGE(LDLM) +
+ OPC_RANGE(MDS) +
+ OPC_RANGE(OST));
+ } else if (opc < FLD_LAST_OPC) {
+ /* FLD opcode */
+ return (opc - FLD_FIRST_OPC +
+ OPC_RANGE(SEC) +
+ OPC_RANGE(SEQ) +
+ OPC_RANGE(QUOTA) +
+ OPC_RANGE(LLOG) +
+ OPC_RANGE(OBD) +
+ OPC_RANGE(MGS) +
+ OPC_RANGE(LDLM) +
+ OPC_RANGE(MDS) +
+ OPC_RANGE(OST));
+ } else if (opc < UPDATE_LAST_OPC) {
+ /* update opcode */
+ return (opc - UPDATE_FIRST_OPC +
+ OPC_RANGE(FLD) +
+ OPC_RANGE(SEC) +
+ OPC_RANGE(SEQ) +
+ OPC_RANGE(QUOTA) +
+ OPC_RANGE(LLOG) +
+ OPC_RANGE(OBD) +
+ OPC_RANGE(MGS) +
+ OPC_RANGE(LDLM) +
+ OPC_RANGE(MDS) +
+ OPC_RANGE(OST));
+ } else {
+ /* Unknown Opcode */
+ return -1;
+ }
+}
+
+
+#define LUSTRE_MAX_OPCODES (OPC_RANGE(OST) + \
+ OPC_RANGE(MDS) + \
+ OPC_RANGE(LDLM) + \
+ OPC_RANGE(MGS) + \
+ OPC_RANGE(OBD) + \
+ OPC_RANGE(LLOG) + \
+ OPC_RANGE(SEC) + \
+ OPC_RANGE(SEQ) + \
+ OPC_RANGE(SEC) + \
+ OPC_RANGE(FLD) + \
+ OPC_RANGE(UPDATE))
+
+#define EXTRA_MAX_OPCODES ((PTLRPC_LAST_CNTR - PTLRPC_FIRST_CNTR) + \
+ OPC_RANGE(EXTRA))
+
+enum {
+ PTLRPC_REQWAIT_CNTR = 0,
+ PTLRPC_REQQDEPTH_CNTR,
+ PTLRPC_REQACTIVE_CNTR,
+ PTLRPC_TIMEOUT,
+ PTLRPC_REQBUF_AVAIL_CNTR,
+ PTLRPC_LAST_CNTR
+};
+
+#define PTLRPC_FIRST_CNTR PTLRPC_REQWAIT_CNTR
+
+enum {
+ LDLM_GLIMPSE_ENQUEUE = 0,
+ LDLM_PLAIN_ENQUEUE,
+ LDLM_EXTENT_ENQUEUE,
+ LDLM_FLOCK_ENQUEUE,
+ LDLM_IBITS_ENQUEUE,
+ MDS_REINT_SETATTR,
+ MDS_REINT_CREATE,
+ MDS_REINT_LINK,
+ MDS_REINT_UNLINK,
+ MDS_REINT_RENAME,
+ MDS_REINT_OPEN,
+ MDS_REINT_SETXATTR,
+ BRW_READ_BYTES,
+ BRW_WRITE_BYTES,
+ EXTRA_LAST_OPC
+};
+
+#define EXTRA_FIRST_OPC LDLM_GLIMPSE_ENQUEUE
+/* class_obd.c */
+extern struct proc_dir_entry *proc_lustre_root;
+
+struct obd_device;
+struct obd_histogram;
+
+/* Days / hours / mins / seconds format */
+struct dhms {
+ int d, h, m, s;
+};
+static inline void s2dhms(struct dhms *ts, time_t secs)
+{
+ ts->d = secs / 86400;
+ secs = secs % 86400;
+ ts->h = secs / 3600;
+ secs = secs % 3600;
+ ts->m = secs / 60;
+ ts->s = secs % 60;
+}
+#define DHMS_FMT "%dd%dh%02dm%02ds"
+#define DHMS_VARS(x) (x)->d, (x)->h, (x)->m, (x)->s
+
+#define JOBSTATS_JOBID_VAR_MAX_LEN 20
+#define JOBSTATS_DISABLE "disable"
+#define JOBSTATS_PROCNAME_UID "procname_uid"
+#define JOBSTATS_NODELOCAL "nodelocal"
+
+extern int lprocfs_write_frac_helper(const char __user *buffer,
+ unsigned long count, int *val, int mult);
+extern int lprocfs_read_frac_helper(char *buffer, unsigned long count,
+ long val, int mult);
+#if defined (CONFIG_PROC_FS)
+
+extern int lprocfs_stats_alloc_one(struct lprocfs_stats *stats,
+ unsigned int cpuid);
+/*
+ * \return value
+ * < 0 : on error (only possible for opc as LPROCFS_GET_SMP_ID)
+ */
+static inline int lprocfs_stats_lock(struct lprocfs_stats *stats, int opc,
+ unsigned long *flags)
+{
+ int rc = 0;
+
+ switch (opc) {
+ default:
+ LBUG();
+
+ case LPROCFS_GET_SMP_ID:
+ if (stats->ls_flags & LPROCFS_STATS_FLAG_NOPERCPU) {
+ if (stats->ls_flags & LPROCFS_STATS_FLAG_IRQ_SAFE)
+ spin_lock_irqsave(&stats->ls_lock, *flags);
+ else
+ spin_lock(&stats->ls_lock);
+ return 0;
+ } else {
+ unsigned int cpuid = get_cpu();
+
+ if (unlikely(stats->ls_percpu[cpuid] == NULL)) {
+ rc = lprocfs_stats_alloc_one(stats, cpuid);
+ if (rc < 0) {
+ put_cpu();
+ return rc;
+ }
+ }
+ return cpuid;
+ }
+
+ case LPROCFS_GET_NUM_CPU:
+ if (stats->ls_flags & LPROCFS_STATS_FLAG_NOPERCPU) {
+ if (stats->ls_flags & LPROCFS_STATS_FLAG_IRQ_SAFE)
+ spin_lock_irqsave(&stats->ls_lock, *flags);
+ else
+ spin_lock(&stats->ls_lock);
+ return 1;
+ } else {
+ return stats->ls_biggest_alloc_num;
+ }
+ }
+}
+
+static inline void lprocfs_stats_unlock(struct lprocfs_stats *stats, int opc,
+ unsigned long *flags)
+{
+ switch (opc) {
+ default:
+ LBUG();
+
+ case LPROCFS_GET_SMP_ID:
+ if (stats->ls_flags & LPROCFS_STATS_FLAG_NOPERCPU) {
+ if (stats->ls_flags & LPROCFS_STATS_FLAG_IRQ_SAFE) {
+ spin_unlock_irqrestore(&stats->ls_lock,
+ *flags);
+ } else {
+ spin_unlock(&stats->ls_lock);
+ }
+ } else {
+ put_cpu();
+ }
+ return;
+
+ case LPROCFS_GET_NUM_CPU:
+ if (stats->ls_flags & LPROCFS_STATS_FLAG_NOPERCPU) {
+ if (stats->ls_flags & LPROCFS_STATS_FLAG_IRQ_SAFE) {
+ spin_unlock_irqrestore(&stats->ls_lock,
+ *flags);
+ } else {
+ spin_unlock(&stats->ls_lock);
+ }
+ }
+ return;
+ }
+}
+
+static inline unsigned int
+lprocfs_stats_counter_size(struct lprocfs_stats *stats)
+{
+ unsigned int percpusize;
+
+ percpusize = offsetof(struct lprocfs_percpu, lp_cntr[stats->ls_num]);
+
+ /* irq safe stats need lc_array_sum[1] */
+ if ((stats->ls_flags & LPROCFS_STATS_FLAG_IRQ_SAFE) != 0)
+ percpusize += stats->ls_num * sizeof(__s64);
+
+ if ((stats->ls_flags & LPROCFS_STATS_FLAG_NOPERCPU) == 0)
+ percpusize = L1_CACHE_ALIGN(percpusize);
+
+ return percpusize;
+}
+
+static inline struct lprocfs_counter *
+lprocfs_stats_counter_get(struct lprocfs_stats *stats, unsigned int cpuid,
+ int index)
+{
+ struct lprocfs_counter *cntr;
+
+ cntr = &stats->ls_percpu[cpuid]->lp_cntr[index];
+
+ if ((stats->ls_flags & LPROCFS_STATS_FLAG_IRQ_SAFE) != 0)
+ cntr = (void *)cntr + index * sizeof(__s64);
+
+ return cntr;
+}
+
+/* Two optimized LPROCFS counter increment functions are provided:
+ * lprocfs_counter_incr(cntr, value) - optimized for by-one counters
+ * lprocfs_counter_add(cntr) - use for multi-valued counters
+ * Counter data layout allows config flag, counter lock and the
+ * count itself to reside within a single cache line.
+ */
+
+extern void lprocfs_counter_add(struct lprocfs_stats *stats, int idx,
+ long amount);
+extern void lprocfs_counter_sub(struct lprocfs_stats *stats, int idx,
+ long amount);
+
+#define lprocfs_counter_incr(stats, idx) \
+ lprocfs_counter_add(stats, idx, 1)
+#define lprocfs_counter_decr(stats, idx) \
+ lprocfs_counter_sub(stats, idx, 1)
+
+extern __s64 lprocfs_read_helper(struct lprocfs_counter *lc,
+ struct lprocfs_counter_header *header,
+ enum lprocfs_stats_flags flags,
+ enum lprocfs_fields_flags field);
+static inline __u64 lprocfs_stats_collector(struct lprocfs_stats *stats,
+ int idx,
+ enum lprocfs_fields_flags field)
+{
+ int i;
+ unsigned int num_cpu;
+ unsigned long flags = 0;
+ __u64 ret = 0;
+
+ LASSERT(stats != NULL);
+
+ num_cpu = lprocfs_stats_lock(stats, LPROCFS_GET_NUM_CPU, &flags);
+ for (i = 0; i < num_cpu; i++) {
+ if (stats->ls_percpu[i] == NULL)
+ continue;
+ ret += lprocfs_read_helper(
+ lprocfs_stats_counter_get(stats, i, idx),
+ &stats->ls_cnt_header[idx], stats->ls_flags,
+ field);
+ }
+ lprocfs_stats_unlock(stats, LPROCFS_GET_NUM_CPU, &flags);
+ return ret;
+}
+
+extern struct lprocfs_stats *
+lprocfs_alloc_stats(unsigned int num, enum lprocfs_stats_flags flags);
+extern void lprocfs_clear_stats(struct lprocfs_stats *stats);
+extern void lprocfs_free_stats(struct lprocfs_stats **stats);
+extern void lprocfs_init_ops_stats(int num_private_stats,
+ struct lprocfs_stats *stats);
+extern void lprocfs_init_mps_stats(int num_private_stats,
+ struct lprocfs_stats *stats);
+extern void lprocfs_init_ldlm_stats(struct lprocfs_stats *ldlm_stats);
+extern int lprocfs_alloc_obd_stats(struct obd_device *obddev,
+ unsigned int num_private_stats);
+extern int lprocfs_alloc_md_stats(struct obd_device *obddev,
+ unsigned int num_private_stats);
+extern void lprocfs_counter_init(struct lprocfs_stats *stats, int index,
+ unsigned conf, const char *name,
+ const char *units);
+extern void lprocfs_free_obd_stats(struct obd_device *obddev);
+extern void lprocfs_free_md_stats(struct obd_device *obddev);
+struct obd_export;
+struct nid_stat;
+extern int lprocfs_add_clear_entry(struct obd_device *obd,
+ struct proc_dir_entry *entry);
+extern int lprocfs_exp_setup(struct obd_export *exp,
+ lnet_nid_t *peer_nid, int *newnid);
+extern int lprocfs_exp_cleanup(struct obd_export *exp);
+extern struct proc_dir_entry *lprocfs_add_simple(struct proc_dir_entry *root,
+ char *name,
+ void *data,
+ struct file_operations *fops);
+extern struct proc_dir_entry *
+lprocfs_add_symlink(const char *name, struct proc_dir_entry *parent,
+ const char *format, ...);
+extern void lprocfs_free_per_client_stats(struct obd_device *obd);
+extern int
+lprocfs_nid_stats_clear_write(struct file *file, const char *buffer,
+ unsigned long count, void *data);
+extern int lprocfs_nid_stats_clear_read(struct seq_file *m, void *data);
+
+extern int lprocfs_register_stats(struct proc_dir_entry *root, const char *name,
+ struct lprocfs_stats *stats);
+
+/* lprocfs_status.c */
+extern int lprocfs_add_vars(struct proc_dir_entry *root,
+ struct lprocfs_vars *var,
+ void *data);
+
+extern struct proc_dir_entry *lprocfs_register(const char *name,
+ struct proc_dir_entry *parent,
+ struct lprocfs_vars *list,
+ void *data);
+
+extern void lprocfs_remove(struct proc_dir_entry **root);
+extern void lprocfs_remove_proc_entry(const char *name,
+ struct proc_dir_entry *parent);
+
+extern int lprocfs_obd_setup(struct obd_device *obd, struct lprocfs_vars *list);
+extern int lprocfs_obd_cleanup(struct obd_device *obd);
+
+extern int lprocfs_seq_create(struct proc_dir_entry *parent, const char *name,
+ umode_t mode,
+ const struct file_operations *seq_fops,
+ void *data);
+extern int lprocfs_obd_seq_create(struct obd_device *dev, const char *name,
+ umode_t mode,
+ const struct file_operations *seq_fops,
+ void *data);
+
+/* Generic callbacks */
+
+extern int lprocfs_rd_u64(struct seq_file *m, void *data);
+extern int lprocfs_rd_atomic(struct seq_file *m, void *data);
+extern int lprocfs_wr_atomic(struct file *file, const char __user *buffer,
+ unsigned long count, void *data);
+extern int lprocfs_rd_uint(struct seq_file *m, void *data);
+extern int lprocfs_wr_uint(struct file *file, const char __user *buffer,
+ unsigned long count, void *data);
+extern int lprocfs_rd_uuid(struct seq_file *m, void *data);
+extern int lprocfs_rd_name(struct seq_file *m, void *data);
+extern int lprocfs_rd_server_uuid(struct seq_file *m, void *data);
+extern int lprocfs_rd_conn_uuid(struct seq_file *m, void *data);
+extern int lprocfs_rd_import(struct seq_file *m, void *data);
+extern int lprocfs_rd_state(struct seq_file *m, void *data);
+extern int lprocfs_rd_connect_flags(struct seq_file *m, void *data);
+extern int lprocfs_rd_num_exports(struct seq_file *m, void *data);
+extern int lprocfs_rd_numrefs(struct seq_file *m, void *data);
+
+struct adaptive_timeout;
+extern int lprocfs_at_hist_helper(struct seq_file *m,
+ struct adaptive_timeout *at);
+extern int lprocfs_rd_timeouts(struct seq_file *m, void *data);
+extern int lprocfs_wr_timeouts(struct file *file, const char __user *buffer,
+ unsigned long count, void *data);
+extern int lprocfs_wr_evict_client(struct file *file, const char __user *buffer,
+ size_t count, loff_t *off);
+extern int lprocfs_wr_ping(struct file *file, const char __user *buffer,
+ size_t count, loff_t *off);
+extern int lprocfs_wr_import(struct file *file, const char __user *buffer,
+ size_t count, loff_t *off);
+extern int lprocfs_rd_pinger_recov(struct seq_file *m, void *n);
+extern int lprocfs_wr_pinger_recov(struct file *file, const char __user *buffer,
+ size_t count, loff_t *off);
+
+/* Statfs helpers */
+extern int lprocfs_rd_blksize(struct seq_file *m, void *data);
+extern int lprocfs_rd_kbytestotal(struct seq_file *m, void *data);
+extern int lprocfs_rd_kbytesfree(struct seq_file *m, void *data);
+extern int lprocfs_rd_kbytesavail(struct seq_file *m, void *data);
+extern int lprocfs_rd_filestotal(struct seq_file *m, void *data);
+extern int lprocfs_rd_filesfree(struct seq_file *m, void *data);
+
+extern int lprocfs_write_helper(const char __user *buffer, unsigned long count,
+ int *val);
+extern int lprocfs_seq_read_frac_helper(struct seq_file *m, long val, int mult);
+extern int lprocfs_write_u64_helper(const char __user *buffer,
+ unsigned long count, __u64 *val);
+extern int lprocfs_write_frac_u64_helper(const char *buffer,
+ unsigned long count,
+ __u64 *val, int mult);
+extern char *lprocfs_find_named_value(const char *buffer, const char *name,
+ size_t *count);
+void lprocfs_oh_tally(struct obd_histogram *oh, unsigned int value);
+void lprocfs_oh_tally_log2(struct obd_histogram *oh, unsigned int value);
+void lprocfs_oh_clear(struct obd_histogram *oh);
+unsigned long lprocfs_oh_sum(struct obd_histogram *oh);
+
+void lprocfs_stats_collect(struct lprocfs_stats *stats, int idx,
+ struct lprocfs_counter *cnt);
+
+extern int lprocfs_single_release(struct inode *, struct file *);
+extern int lprocfs_seq_release(struct inode *, struct file *);
+
+/* You must use these macros when you want to refer to
+ * the import in a client obd_device for a lprocfs entry */
+#define LPROCFS_CLIMP_CHECK(obd) do { \
+ typecheck(struct obd_device *, obd); \
+ down_read(&(obd)->u.cli.cl_sem); \
+ if ((obd)->u.cli.cl_import == NULL) { \
+ up_read(&(obd)->u.cli.cl_sem); \
+ return -ENODEV; \
+ } \
+} while (0)
+#define LPROCFS_CLIMP_EXIT(obd) \
+ up_read(&(obd)->u.cli.cl_sem)
+
+
+/* write the name##_seq_show function, call LPROC_SEQ_FOPS_RO for read-only
+ proc entries; otherwise, you will define name##_seq_write function also for
+ a read-write proc entry, and then call LPROC_SEQ_SEQ instead. Finally,
+ call lprocfs_obd_seq_create(obd, filename, 0444, &name#_fops, data); */
+#define __LPROC_SEQ_FOPS(name, custom_seq_write) \
+static int name##_single_open(struct inode *inode, struct file *file) \
+{ \
+ return single_open(file, name##_seq_show, PDE_DATA(inode)); \
+} \
+static struct file_operations name##_fops = { \
+ .owner = THIS_MODULE, \
+ .open = name##_single_open, \
+ .read = seq_read, \
+ .write = custom_seq_write, \
+ .llseek = seq_lseek, \
+ .release = lprocfs_single_release, \
+}
+
+#define LPROC_SEQ_FOPS_RO(name) __LPROC_SEQ_FOPS(name, NULL)
+#define LPROC_SEQ_FOPS(name) __LPROC_SEQ_FOPS(name, name##_seq_write)
+
+#define LPROC_SEQ_FOPS_RO_TYPE(name, type) \
+ static int name##_##type##_seq_show(struct seq_file *m, void *v)\
+ { \
+ return lprocfs_rd_##type(m, m->private); \
+ } \
+ LPROC_SEQ_FOPS_RO(name##_##type)
+
+#define LPROC_SEQ_FOPS_RW_TYPE(name, type) \
+ static int name##_##type##_seq_show(struct seq_file *m, void *v)\
+ { \
+ return lprocfs_rd_##type(m, m->private); \
+ } \
+ static ssize_t name##_##type##_seq_write(struct file *file, \
+ const char __user *buffer, size_t count, \
+ loff_t *off) \
+ { \
+ struct seq_file *seq = file->private_data; \
+ return lprocfs_wr_##type(file, buffer, \
+ count, seq->private); \
+ } \
+ LPROC_SEQ_FOPS(name##_##type)
+
+#define LPROC_SEQ_FOPS_WR_ONLY(name, type) \
+ static ssize_t name##_##type##_write(struct file *file, \
+ const char __user *buffer, size_t count, \
+ loff_t *off) \
+ { \
+ return lprocfs_wr_##type(file, buffer, count, off); \
+ } \
+ static int name##_##type##_open(struct inode *inode, struct file *file) \
+ { \
+ return single_open(file, NULL, PDE_DATA(inode)); \
+ } \
+ static struct file_operations name##_##type##_fops = { \
+ .open = name##_##type##_open, \
+ .write = name##_##type##_write, \
+ .release = lprocfs_single_release, \
+ }
+
+/* lproc_ptlrpc.c */
+struct ptlrpc_request;
+extern void target_print_req(void *seq_file, struct ptlrpc_request *req);
+
+/* lproc_status.c */
+int lprocfs_obd_rd_max_pages_per_rpc(struct seq_file *m, void *data);
+int lprocfs_obd_wr_max_pages_per_rpc(struct file *file, const char *buffer,
+ size_t count, loff_t *off);
+
+/* all quota proc functions */
+extern int lprocfs_quota_rd_bunit(char *page, char **start,
+ loff_t off, int count,
+ int *eof, void *data);
+extern int lprocfs_quota_wr_bunit(struct file *file, const char *buffer,
+ unsigned long count, void *data);
+extern int lprocfs_quota_rd_btune(char *page, char **start,
+ loff_t off, int count,
+ int *eof, void *data);
+extern int lprocfs_quota_wr_btune(struct file *file, const char *buffer,
+ unsigned long count, void *data);
+extern int lprocfs_quota_rd_iunit(char *page, char **start,
+ loff_t off, int count,
+ int *eof, void *data);
+extern int lprocfs_quota_wr_iunit(struct file *file, const char *buffer,
+ unsigned long count, void *data);
+extern int lprocfs_quota_rd_itune(char *page, char **start,
+ loff_t off, int count,
+ int *eof, void *data);
+extern int lprocfs_quota_wr_itune(struct file *file, const char *buffer,
+ unsigned long count, void *data);
+extern int lprocfs_quota_rd_type(char *page, char **start, loff_t off, int count,
+ int *eof, void *data);
+extern int lprocfs_quota_wr_type(struct file *file, const char *buffer,
+ unsigned long count, void *data);
+extern int lprocfs_quota_rd_switch_seconds(char *page, char **start, loff_t off,
+ int count, int *eof, void *data);
+extern int lprocfs_quota_wr_switch_seconds(struct file *file,
+ const char *buffer,
+ unsigned long count, void *data);
+extern int lprocfs_quota_rd_sync_blk(char *page, char **start, loff_t off,
+ int count, int *eof, void *data);
+extern int lprocfs_quota_wr_sync_blk(struct file *file, const char *buffer,
+ unsigned long count, void *data);
+extern int lprocfs_quota_rd_switch_qs(char *page, char **start, loff_t off,
+ int count, int *eof, void *data);
+extern int lprocfs_quota_wr_switch_qs(struct file *file,
+ const char *buffer,
+ unsigned long count, void *data);
+extern int lprocfs_quota_rd_boundary_factor(char *page, char **start, loff_t off,
+ int count, int *eof, void *data);
+extern int lprocfs_quota_wr_boundary_factor(struct file *file,
+ const char *buffer,
+ unsigned long count, void *data);
+extern int lprocfs_quota_rd_least_bunit(char *page, char **start, loff_t off,
+ int count, int *eof, void *data);
+extern int lprocfs_quota_wr_least_bunit(struct file *file,
+ const char *buffer,
+ unsigned long count, void *data);
+extern int lprocfs_quota_rd_least_iunit(char *page, char **start, loff_t off,
+ int count, int *eof, void *data);
+extern int lprocfs_quota_wr_least_iunit(struct file *file,
+ const char *buffer,
+ unsigned long count, void *data);
+extern int lprocfs_quota_rd_qs_factor(char *page, char **start, loff_t off,
+ int count, int *eof, void *data);
+extern int lprocfs_quota_wr_qs_factor(struct file *file,
+ const char *buffer,
+ unsigned long count, void *data);
+#else
+/* CONFIG_PROC_FS is not defined */
+
+#define proc_lustre_root NULL
+
+static inline void lprocfs_counter_add(struct lprocfs_stats *stats,
+ int index, long amount)
+{ return; }
+static inline void lprocfs_counter_incr(struct lprocfs_stats *stats,
+ int index)
+{ return; }
+static inline void lprocfs_counter_sub(struct lprocfs_stats *stats,
+ int index, long amount)
+{ return; }
+static inline void lprocfs_counter_decr(struct lprocfs_stats *stats,
+ int index)
+{ return; }
+static inline void lprocfs_counter_init(struct lprocfs_stats *stats,
+ int index, unsigned conf,
+ const char *name, const char *units)
+{ return; }
+
+static inline __u64 lc_read_helper(struct lprocfs_counter *lc,
+ enum lprocfs_fields_flags field)
+{ return 0; }
+
+/* NB: we return !NULL to satisfy error checker */
+static inline struct lprocfs_stats *
+lprocfs_alloc_stats(unsigned int num, enum lprocfs_stats_flags flags)
+{ return (struct lprocfs_stats *)1; }
+static inline void lprocfs_clear_stats(struct lprocfs_stats *stats)
+{ return; }
+static inline void lprocfs_free_stats(struct lprocfs_stats **stats)
+{ return; }
+static inline int lprocfs_register_stats(struct proc_dir_entry *root,
+ const char *name,
+ struct lprocfs_stats *stats)
+{ return 0; }
+static inline void lprocfs_init_ops_stats(int num_private_stats,
+ struct lprocfs_stats *stats)
+{ return; }
+static inline void lprocfs_init_mps_stats(int num_private_stats,
+ struct lprocfs_stats *stats)
+{ return; }
+static inline void lprocfs_init_ldlm_stats(struct lprocfs_stats *ldlm_stats)
+{ return; }
+static inline int lprocfs_alloc_obd_stats(struct obd_device *obddev,
+ unsigned int num_private_stats)
+{ return 0; }
+static inline int lprocfs_alloc_md_stats(struct obd_device *obddev,
+ unsigned int num_private_stats)
+{ return 0; }
+static inline void lprocfs_free_obd_stats(struct obd_device *obddev)
+{ return; }
+static inline void lprocfs_free_md_stats(struct obd_device *obddev)
+{ return; }
+
+struct obd_export;
+static inline int lprocfs_add_clear_entry(struct obd_export *exp)
+{ return 0; }
+static inline int lprocfs_exp_setup(struct obd_export *exp,
+ lnet_nid_t *peer_nid,
+ int *newnid)
+{ return 0; }
+static inline int lprocfs_exp_cleanup(struct obd_export *exp)
+{ return 0; }
+static inline struct proc_dir_entry *
+lprocfs_add_simple(struct proc_dir_entry *root, char *name,
+ void *data, struct file_operations *fops)
+{return 0; }
+static inline struct proc_dir_entry *
+lprocfs_add_symlink(const char *name, struct proc_dir_entry *parent,
+ const char *format, ...)
+{return NULL; }
+static inline void lprocfs_free_per_client_stats(struct obd_device *obd)
+{ return; }
+static inline
+int lprocfs_nid_stats_clear_write(struct file *file, const char *buffer,
+ unsigned long count, void *data)
+{return count;}
+static inline
+int lprocfs_nid_stats_clear_read(struct seq_file *m, void *data)
+{ return 0; }
+
+static inline struct proc_dir_entry *
+lprocfs_register(const char *name, struct proc_dir_entry *parent,
+ struct lprocfs_vars *list, void *data)
+{ return NULL; }
+static inline int lprocfs_add_vars(struct proc_dir_entry *root,
+ struct lprocfs_vars *var,
+ void *data)
+{ return 0; }
+static inline void lprocfs_remove(struct proc_dir_entry **root)
+{ return; }
+static inline void lprocfs_remove_proc_entry(const char *name,
+ struct proc_dir_entry *parent)
+{ return; }
+static inline int lprocfs_obd_setup(struct obd_device *dev,
+ struct lprocfs_vars *list)
+{ return 0; }
+static inline int lprocfs_obd_cleanup(struct obd_device *dev)
+{ return 0; }
+static inline int lprocfs_rd_u64(struct seq_file *m, void *data)
+{ return 0; }
+static inline int lprocfs_rd_uuid(struct seq_file *m, void *data)
+{ return 0; }
+static inline int lprocfs_rd_name(struct seq_file *m, void *data)
+{ return 0; }
+static inline int lprocfs_rd_server_uuid(struct seq_file *m, void *data)
+{ return 0; }
+static inline int lprocfs_rd_conn_uuid(struct seq_file *m, void *data)
+{ return 0; }
+static inline int lprocfs_rd_import(struct seq_file *m, void *data)
+{ return 0; }
+static inline int lprocfs_rd_pinger_recov(struct seq_file *m, void *n)
+{ return 0; }
+static inline int lprocfs_rd_state(struct seq_file *m, void *data)
+{ return 0; }
+static inline int lprocfs_rd_connect_flags(struct seq_file *m, void *data)
+{ return 0; }
+static inline int lprocfs_rd_num_exports(struct seq_file *m, void *data)
+{ return 0; }
+extern inline int lprocfs_rd_numrefs(struct seq_file *m, void *data)
+{ return 0; }
+struct adaptive_timeout;
+static inline int lprocfs_at_hist_helper(struct seq_file *m,
+ struct adaptive_timeout *at)
+{ return 0; }
+static inline int lprocfs_rd_timeouts(struct seq_file *m, void *data)
+{ return 0; }
+static inline int lprocfs_wr_timeouts(struct file *file,
+ const char __user *buffer,
+ unsigned long count, void *data)
+{ return 0; }
+static inline int lprocfs_wr_evict_client(struct file *file,
+ const char __user *buffer,
+ size_t count, loff_t *off)
+{ return 0; }
+static inline int lprocfs_wr_ping(struct file *file,
+ const char __user *buffer,
+ size_t count, loff_t *off)
+{ return 0; }
+static inline int lprocfs_wr_import(struct file *file,
+ const char __user *buffer,
+ size_t count, loff_t *off)
+{ return 0; }
+static inline int lprocfs_wr_pinger_recov(struct file *file,
+ const char __user *buffer,
+ size_t count, loff_t *off)
+{ return 0; }
+
+/* Statfs helpers */
+static inline
+int lprocfs_rd_blksize(struct seq_file *m, void *data)
+{ return 0; }
+static inline
+int lprocfs_rd_kbytestotal(struct seq_file *m, void *data)
+{ return 0; }
+static inline
+int lprocfs_rd_kbytesfree(struct seq_file *m, void *data)
+{ return 0; }
+static inline
+int lprocfs_rd_kbytesavail(struct seq_file *m, void *data)
+{ return 0; }
+static inline
+int lprocfs_rd_filestotal(struct seq_file *m, void *data)
+{ return 0; }
+static inline
+int lprocfs_rd_filesfree(struct seq_file *m, void *data)
+{ return 0; }
+static inline
+void lprocfs_oh_tally(struct obd_histogram *oh, unsigned int value)
+{ return; }
+static inline
+void lprocfs_oh_tally_log2(struct obd_histogram *oh, unsigned int value)
+{ return; }
+static inline
+void lprocfs_oh_clear(struct obd_histogram *oh)
+{ return; }
+static inline
+unsigned long lprocfs_oh_sum(struct obd_histogram *oh)
+{ return 0; }
+static inline
+void lprocfs_stats_collect(struct lprocfs_stats *stats, int idx,
+ struct lprocfs_counter *cnt)
+{ return; }
+static inline
+__u64 lprocfs_stats_collector(struct lprocfs_stats *stats, int idx,
+ enum lprocfs_fields_flags field)
+{ return (__u64)0; }
+
+#define LPROC_SEQ_FOPS_RO(name)
+#define LPROC_SEQ_FOPS(name)
+#define LPROC_SEQ_FOPS_RO_TYPE(name, type)
+#define LPROC_SEQ_FOPS_RW_TYPE(name, type)
+#define LPROC_SEQ_FOPS_WR_ONLY(name, type)
+
+/* lproc_ptlrpc.c */
+#define target_print_req NULL
+
+#endif /* CONFIG_PROC_FS */
+
+#endif /* LPROCFS_SNMP_H */
diff --git a/drivers/staging/lustre/lustre/include/lu_object.h b/drivers/staging/lustre/lustre/include/lu_object.h
new file mode 100644
index 000000000..c8cc48f00
--- /dev/null
+++ b/drivers/staging/lustre/lustre/include/lu_object.h
@@ -0,0 +1,1340 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2011, 2012, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ */
+
+#ifndef __LUSTRE_LU_OBJECT_H
+#define __LUSTRE_LU_OBJECT_H
+
+#include <stdarg.h>
+#include "../../include/linux/libcfs/libcfs.h"
+#include "lustre/lustre_idl.h"
+#include "lu_ref.h"
+
+struct seq_file;
+struct proc_dir_entry;
+struct lustre_cfg;
+struct lprocfs_stats;
+
+/** \defgroup lu lu
+ * lu_* data-types represent server-side entities shared by data and meta-data
+ * stacks.
+ *
+ * Design goals:
+ *
+ * -# support for layering.
+ *
+ * Server side object is split into layers, one per device in the
+ * corresponding device stack. Individual layer is represented by struct
+ * lu_object. Compound layered object --- by struct lu_object_header. Most
+ * interface functions take lu_object as an argument and operate on the
+ * whole compound object. This decision was made due to the following
+ * reasons:
+ *
+ * - it's envisaged that lu_object will be used much more often than
+ * lu_object_header;
+ *
+ * - we want lower (non-top) layers to be able to initiate operations
+ * on the whole object.
+ *
+ * Generic code supports layering more complex than simple stacking, e.g.,
+ * it is possible that at some layer object "spawns" multiple sub-objects
+ * on the lower layer.
+ *
+ * -# fid-based identification.
+ *
+ * Compound object is uniquely identified by its fid. Objects are indexed
+ * by their fids (hash table is used for index).
+ *
+ * -# caching and life-cycle management.
+ *
+ * Object's life-time is controlled by reference counting. When reference
+ * count drops to 0, object is returned to cache. Cached objects still
+ * retain their identity (i.e., fid), and can be recovered from cache.
+ *
+ * Objects are kept in the global LRU list, and lu_site_purge() function
+ * can be used to reclaim given number of unused objects from the tail of
+ * the LRU.
+ *
+ * -# avoiding recursion.
+ *
+ * Generic code tries to replace recursion through layers by iterations
+ * where possible. Additionally to the end of reducing stack consumption,
+ * data, when practically possible, are allocated through lu_context_key
+ * interface rather than on stack.
+ * @{
+ */
+
+struct lu_site;
+struct lu_object;
+struct lu_device;
+struct lu_object_header;
+struct lu_context;
+struct lu_env;
+
+/**
+ * Operations common for data and meta-data devices.
+ */
+struct lu_device_operations {
+ /**
+ * Allocate object for the given device (without lower-layer
+ * parts). This is called by lu_object_operations::loo_object_init()
+ * from the parent layer, and should setup at least lu_object::lo_dev
+ * and lu_object::lo_ops fields of resulting lu_object.
+ *
+ * Object creation protocol.
+ *
+ * Due to design goal of avoiding recursion, object creation (see
+ * lu_object_alloc()) is somewhat involved:
+ *
+ * - first, lu_device_operations::ldo_object_alloc() method of the
+ * top-level device in the stack is called. It should allocate top
+ * level object (including lu_object_header), but without any
+ * lower-layer sub-object(s).
+ *
+ * - then lu_object_alloc() sets fid in the header of newly created
+ * object.
+ *
+ * - then lu_object_operations::loo_object_init() is called. It has
+ * to allocate lower-layer object(s). To do this,
+ * lu_object_operations::loo_object_init() calls ldo_object_alloc()
+ * of the lower-layer device(s).
+ *
+ * - for all new objects allocated by
+ * lu_object_operations::loo_object_init() (and inserted into object
+ * stack), lu_object_operations::loo_object_init() is called again
+ * repeatedly, until no new objects are created.
+ *
+ * \post ergo(!IS_ERR(result), result->lo_dev == d &&
+ * result->lo_ops != NULL);
+ */
+ struct lu_object *(*ldo_object_alloc)(const struct lu_env *env,
+ const struct lu_object_header *h,
+ struct lu_device *d);
+ /**
+ * process config specific for device.
+ */
+ int (*ldo_process_config)(const struct lu_env *env,
+ struct lu_device *, struct lustre_cfg *);
+ int (*ldo_recovery_complete)(const struct lu_env *,
+ struct lu_device *);
+
+ /**
+ * initialize local objects for device. this method called after layer has
+ * been initialized (after LCFG_SETUP stage) and before it starts serving
+ * user requests.
+ */
+
+ int (*ldo_prepare)(const struct lu_env *,
+ struct lu_device *parent,
+ struct lu_device *dev);
+
+};
+
+/**
+ * For lu_object_conf flags
+ */
+typedef enum {
+ /* This is a new object to be allocated, or the file
+ * corresponding to the object does not exists. */
+ LOC_F_NEW = 0x00000001,
+} loc_flags_t;
+
+/**
+ * Object configuration, describing particulars of object being created. On
+ * server this is not used, as server objects are full identified by fid. On
+ * client configuration contains struct lustre_md.
+ */
+struct lu_object_conf {
+ /**
+ * Some hints for obj find and alloc.
+ */
+ loc_flags_t loc_flags;
+};
+
+/**
+ * Type of "printer" function used by lu_object_operations::loo_object_print()
+ * method.
+ *
+ * Printer function is needed to provide some flexibility in (semi-)debugging
+ * output: possible implementations: printk, CDEBUG, sysfs/seq_file
+ */
+typedef int (*lu_printer_t)(const struct lu_env *env,
+ void *cookie, const char *format, ...)
+ __printf(3, 4);
+
+/**
+ * Operations specific for particular lu_object.
+ */
+struct lu_object_operations {
+
+ /**
+ * Allocate lower-layer parts of the object by calling
+ * lu_device_operations::ldo_object_alloc() of the corresponding
+ * underlying device.
+ *
+ * This method is called once for each object inserted into object
+ * stack. It's responsibility of this method to insert lower-layer
+ * object(s) it create into appropriate places of object stack.
+ */
+ int (*loo_object_init)(const struct lu_env *env,
+ struct lu_object *o,
+ const struct lu_object_conf *conf);
+ /**
+ * Called (in top-to-bottom order) during object allocation after all
+ * layers were allocated and initialized. Can be used to perform
+ * initialization depending on lower layers.
+ */
+ int (*loo_object_start)(const struct lu_env *env,
+ struct lu_object *o);
+ /**
+ * Called before lu_object_operations::loo_object_free() to signal
+ * that object is being destroyed. Dual to
+ * lu_object_operations::loo_object_init().
+ */
+ void (*loo_object_delete)(const struct lu_env *env,
+ struct lu_object *o);
+ /**
+ * Dual to lu_device_operations::ldo_object_alloc(). Called when
+ * object is removed from memory.
+ */
+ void (*loo_object_free)(const struct lu_env *env,
+ struct lu_object *o);
+ /**
+ * Called when last active reference to the object is released (and
+ * object returns to the cache). This method is optional.
+ */
+ void (*loo_object_release)(const struct lu_env *env,
+ struct lu_object *o);
+ /**
+ * Optional debugging helper. Print given object.
+ */
+ int (*loo_object_print)(const struct lu_env *env, void *cookie,
+ lu_printer_t p, const struct lu_object *o);
+ /**
+ * Optional debugging method. Returns true iff method is internally
+ * consistent.
+ */
+ int (*loo_object_invariant)(const struct lu_object *o);
+};
+
+/**
+ * Type of lu_device.
+ */
+struct lu_device_type;
+
+/**
+ * Device: a layer in the server side abstraction stacking.
+ */
+struct lu_device {
+ /**
+ * reference count. This is incremented, in particular, on each object
+ * created at this layer.
+ *
+ * \todo XXX which means that atomic_t is probably too small.
+ */
+ atomic_t ld_ref;
+ /**
+ * Pointer to device type. Never modified once set.
+ */
+ struct lu_device_type *ld_type;
+ /**
+ * Operation vector for this device.
+ */
+ const struct lu_device_operations *ld_ops;
+ /**
+ * Stack this device belongs to.
+ */
+ struct lu_site *ld_site;
+ struct proc_dir_entry *ld_proc_entry;
+
+ /** \todo XXX: temporary back pointer into obd. */
+ struct obd_device *ld_obd;
+ /**
+ * A list of references to this object, for debugging.
+ */
+ struct lu_ref ld_reference;
+ /**
+ * Link the device to the site.
+ **/
+ struct list_head ld_linkage;
+};
+
+struct lu_device_type_operations;
+
+/**
+ * Tag bits for device type. They are used to distinguish certain groups of
+ * device types.
+ */
+enum lu_device_tag {
+ /** this is meta-data device */
+ LU_DEVICE_MD = (1 << 0),
+ /** this is data device */
+ LU_DEVICE_DT = (1 << 1),
+ /** data device in the client stack */
+ LU_DEVICE_CL = (1 << 2)
+};
+
+/**
+ * Type of device.
+ */
+struct lu_device_type {
+ /**
+ * Tag bits. Taken from enum lu_device_tag. Never modified once set.
+ */
+ __u32 ldt_tags;
+ /**
+ * Name of this class. Unique system-wide. Never modified once set.
+ */
+ char *ldt_name;
+ /**
+ * Operations for this type.
+ */
+ const struct lu_device_type_operations *ldt_ops;
+ /**
+ * \todo XXX: temporary pointer to associated obd_type.
+ */
+ struct obd_type *ldt_obd_type;
+ /**
+ * \todo XXX: temporary: context tags used by obd_*() calls.
+ */
+ __u32 ldt_ctx_tags;
+ /**
+ * Number of existing device type instances.
+ */
+ unsigned ldt_device_nr;
+ /**
+ * Linkage into a global list of all device types.
+ *
+ * \see lu_device_types.
+ */
+ struct list_head ldt_linkage;
+};
+
+/**
+ * Operations on a device type.
+ */
+struct lu_device_type_operations {
+ /**
+ * Allocate new device.
+ */
+ struct lu_device *(*ldto_device_alloc)(const struct lu_env *env,
+ struct lu_device_type *t,
+ struct lustre_cfg *lcfg);
+ /**
+ * Free device. Dual to
+ * lu_device_type_operations::ldto_device_alloc(). Returns pointer to
+ * the next device in the stack.
+ */
+ struct lu_device *(*ldto_device_free)(const struct lu_env *,
+ struct lu_device *);
+
+ /**
+ * Initialize the devices after allocation
+ */
+ int (*ldto_device_init)(const struct lu_env *env,
+ struct lu_device *, const char *,
+ struct lu_device *);
+ /**
+ * Finalize device. Dual to
+ * lu_device_type_operations::ldto_device_init(). Returns pointer to
+ * the next device in the stack.
+ */
+ struct lu_device *(*ldto_device_fini)(const struct lu_env *env,
+ struct lu_device *);
+ /**
+ * Initialize device type. This is called on module load.
+ */
+ int (*ldto_init)(struct lu_device_type *t);
+ /**
+ * Finalize device type. Dual to
+ * lu_device_type_operations::ldto_init(). Called on module unload.
+ */
+ void (*ldto_fini)(struct lu_device_type *t);
+ /**
+ * Called when the first device is created.
+ */
+ void (*ldto_start)(struct lu_device_type *t);
+ /**
+ * Called when number of devices drops to 0.
+ */
+ void (*ldto_stop)(struct lu_device_type *t);
+};
+
+static inline int lu_device_is_md(const struct lu_device *d)
+{
+ return ergo(d != NULL, d->ld_type->ldt_tags & LU_DEVICE_MD);
+}
+
+/**
+ * Common object attributes.
+ */
+struct lu_attr {
+ /** size in bytes */
+ __u64 la_size;
+ /** modification time in seconds since Epoch */
+ s64 la_mtime;
+ /** access time in seconds since Epoch */
+ s64 la_atime;
+ /** change time in seconds since Epoch */
+ s64 la_ctime;
+ /** 512-byte blocks allocated to object */
+ __u64 la_blocks;
+ /** permission bits and file type */
+ __u32 la_mode;
+ /** owner id */
+ __u32 la_uid;
+ /** group id */
+ __u32 la_gid;
+ /** object flags */
+ __u32 la_flags;
+ /** number of persistent references to this object */
+ __u32 la_nlink;
+ /** blk bits of the object*/
+ __u32 la_blkbits;
+ /** blk size of the object*/
+ __u32 la_blksize;
+ /** real device */
+ __u32 la_rdev;
+ /**
+ * valid bits
+ *
+ * \see enum la_valid
+ */
+ __u64 la_valid;
+};
+
+/** Bit-mask of valid attributes */
+enum la_valid {
+ LA_ATIME = 1 << 0,
+ LA_MTIME = 1 << 1,
+ LA_CTIME = 1 << 2,
+ LA_SIZE = 1 << 3,
+ LA_MODE = 1 << 4,
+ LA_UID = 1 << 5,
+ LA_GID = 1 << 6,
+ LA_BLOCKS = 1 << 7,
+ LA_TYPE = 1 << 8,
+ LA_FLAGS = 1 << 9,
+ LA_NLINK = 1 << 10,
+ LA_RDEV = 1 << 11,
+ LA_BLKSIZE = 1 << 12,
+ LA_KILL_SUID = 1 << 13,
+ LA_KILL_SGID = 1 << 14,
+};
+
+/**
+ * Layer in the layered object.
+ */
+struct lu_object {
+ /**
+ * Header for this object.
+ */
+ struct lu_object_header *lo_header;
+ /**
+ * Device for this layer.
+ */
+ struct lu_device *lo_dev;
+ /**
+ * Operations for this object.
+ */
+ const struct lu_object_operations *lo_ops;
+ /**
+ * Linkage into list of all layers.
+ */
+ struct list_head lo_linkage;
+ /**
+ * Link to the device, for debugging.
+ */
+ struct lu_ref_link lo_dev_ref;
+};
+
+enum lu_object_header_flags {
+ /**
+ * Don't keep this object in cache. Object will be destroyed as soon
+ * as last reference to it is released. This flag cannot be cleared
+ * once set.
+ */
+ LU_OBJECT_HEARD_BANSHEE = 0,
+ /**
+ * Mark this object has already been taken out of cache.
+ */
+ LU_OBJECT_UNHASHED = 1
+};
+
+enum lu_object_header_attr {
+ LOHA_EXISTS = 1 << 0,
+ LOHA_REMOTE = 1 << 1,
+ /**
+ * UNIX file type is stored in S_IFMT bits.
+ */
+ LOHA_FT_START = 001 << 12, /**< S_IFIFO */
+ LOHA_FT_END = 017 << 12, /**< S_IFMT */
+};
+
+/**
+ * "Compound" object, consisting of multiple layers.
+ *
+ * Compound object with given fid is unique with given lu_site.
+ *
+ * Note, that object does *not* necessary correspond to the real object in the
+ * persistent storage: object is an anchor for locking and method calling, so
+ * it is created for things like not-yet-existing child created by mkdir or
+ * create calls. lu_object_operations::loo_exists() can be used to check
+ * whether object is backed by persistent storage entity.
+ */
+struct lu_object_header {
+ /**
+ * Fid, uniquely identifying this object.
+ */
+ struct lu_fid loh_fid;
+ /**
+ * Object flags from enum lu_object_header_flags. Set and checked
+ * atomically.
+ */
+ unsigned long loh_flags;
+ /**
+ * Object reference count. Protected by lu_site::ls_guard.
+ */
+ atomic_t loh_ref;
+ /**
+ * Common object attributes, cached for efficiency. From enum
+ * lu_object_header_attr.
+ */
+ __u32 loh_attr;
+ /**
+ * Linkage into per-site hash table. Protected by lu_site::ls_guard.
+ */
+ struct hlist_node loh_hash;
+ /**
+ * Linkage into per-site LRU list. Protected by lu_site::ls_guard.
+ */
+ struct list_head loh_lru;
+ /**
+ * Linkage into list of layers. Never modified once set (except lately
+ * during object destruction). No locking is necessary.
+ */
+ struct list_head loh_layers;
+ /**
+ * A list of references to this object, for debugging.
+ */
+ struct lu_ref loh_reference;
+};
+
+struct fld;
+
+struct lu_site_bkt_data {
+ /**
+ * number of busy object on this bucket
+ */
+ long lsb_busy;
+ /**
+ * LRU list, updated on each access to object. Protected by
+ * bucket lock of lu_site::ls_obj_hash.
+ *
+ * "Cold" end of LRU is lu_site::ls_lru.next. Accessed object are
+ * moved to the lu_site::ls_lru.prev (this is due to the non-existence
+ * of list_for_each_entry_safe_reverse()).
+ */
+ struct list_head lsb_lru;
+ /**
+ * Wait-queue signaled when an object in this site is ultimately
+ * destroyed (lu_object_free()). It is used by lu_object_find() to
+ * wait before re-trying when object in the process of destruction is
+ * found in the hash table.
+ *
+ * \see htable_lookup().
+ */
+ wait_queue_head_t lsb_marche_funebre;
+};
+
+enum {
+ LU_SS_CREATED = 0,
+ LU_SS_CACHE_HIT,
+ LU_SS_CACHE_MISS,
+ LU_SS_CACHE_RACE,
+ LU_SS_CACHE_DEATH_RACE,
+ LU_SS_LRU_PURGED,
+ LU_SS_LAST_STAT
+};
+
+/**
+ * lu_site is a "compartment" within which objects are unique, and LRU
+ * discipline is maintained.
+ *
+ * lu_site exists so that multiple layered stacks can co-exist in the same
+ * address space.
+ *
+ * lu_site has the same relation to lu_device as lu_object_header to
+ * lu_object.
+ */
+struct lu_site {
+ /**
+ * objects hash table
+ */
+ struct cfs_hash *ls_obj_hash;
+ /**
+ * index of bucket on hash table while purging
+ */
+ int ls_purge_start;
+ /**
+ * Top-level device for this stack.
+ */
+ struct lu_device *ls_top_dev;
+ /**
+ * Bottom-level device for this stack
+ */
+ struct lu_device *ls_bottom_dev;
+ /**
+ * Linkage into global list of sites.
+ */
+ struct list_head ls_linkage;
+ /**
+ * List for lu device for this site, protected
+ * by ls_ld_lock.
+ **/
+ struct list_head ls_ld_linkage;
+ spinlock_t ls_ld_lock;
+
+ /**
+ * lu_site stats
+ */
+ struct lprocfs_stats *ls_stats;
+ /**
+ * XXX: a hack! fld has to find md_site via site, remove when possible
+ */
+ struct seq_server_site *ld_seq_site;
+};
+
+static inline struct lu_site_bkt_data *
+lu_site_bkt_from_fid(struct lu_site *site, struct lu_fid *fid)
+{
+ struct cfs_hash_bd bd;
+
+ cfs_hash_bd_get(site->ls_obj_hash, fid, &bd);
+ return cfs_hash_bd_extra_get(site->ls_obj_hash, &bd);
+}
+
+static inline struct seq_server_site *lu_site2seq(const struct lu_site *s)
+{
+ return s->ld_seq_site;
+}
+
+/** \name ctors
+ * Constructors/destructors.
+ * @{
+ */
+
+int lu_site_init (struct lu_site *s, struct lu_device *d);
+void lu_site_fini (struct lu_site *s);
+int lu_site_init_finish (struct lu_site *s);
+void lu_stack_fini (const struct lu_env *env, struct lu_device *top);
+void lu_device_get (struct lu_device *d);
+void lu_device_put (struct lu_device *d);
+int lu_device_init (struct lu_device *d, struct lu_device_type *t);
+void lu_device_fini (struct lu_device *d);
+int lu_object_header_init(struct lu_object_header *h);
+void lu_object_header_fini(struct lu_object_header *h);
+int lu_object_init (struct lu_object *o,
+ struct lu_object_header *h, struct lu_device *d);
+void lu_object_fini (struct lu_object *o);
+void lu_object_add_top (struct lu_object_header *h, struct lu_object *o);
+void lu_object_add (struct lu_object *before, struct lu_object *o);
+
+void lu_dev_add_linkage(struct lu_site *s, struct lu_device *d);
+void lu_dev_del_linkage(struct lu_site *s, struct lu_device *d);
+
+/**
+ * Helpers to initialize and finalize device types.
+ */
+
+int lu_device_type_init(struct lu_device_type *ldt);
+void lu_device_type_fini(struct lu_device_type *ldt);
+void lu_types_stop(void);
+
+/** @} ctors */
+
+/** \name caching
+ * Caching and reference counting.
+ * @{
+ */
+
+/**
+ * Acquire additional reference to the given object. This function is used to
+ * attain additional reference. To acquire initial reference use
+ * lu_object_find().
+ */
+static inline void lu_object_get(struct lu_object *o)
+{
+ LASSERT(atomic_read(&o->lo_header->loh_ref) > 0);
+ atomic_inc(&o->lo_header->loh_ref);
+}
+
+/**
+ * Return true of object will not be cached after last reference to it is
+ * released.
+ */
+static inline int lu_object_is_dying(const struct lu_object_header *h)
+{
+ return test_bit(LU_OBJECT_HEARD_BANSHEE, &h->loh_flags);
+}
+
+void lu_object_put(const struct lu_env *env, struct lu_object *o);
+void lu_object_put_nocache(const struct lu_env *env, struct lu_object *o);
+void lu_object_unhash(const struct lu_env *env, struct lu_object *o);
+
+int lu_site_purge(const struct lu_env *env, struct lu_site *s, int nr);
+
+void lu_site_print(const struct lu_env *env, struct lu_site *s, void *cookie,
+ lu_printer_t printer);
+struct lu_object *lu_object_find(const struct lu_env *env,
+ struct lu_device *dev, const struct lu_fid *f,
+ const struct lu_object_conf *conf);
+struct lu_object *lu_object_find_at(const struct lu_env *env,
+ struct lu_device *dev,
+ const struct lu_fid *f,
+ const struct lu_object_conf *conf);
+struct lu_object *lu_object_find_slice(const struct lu_env *env,
+ struct lu_device *dev,
+ const struct lu_fid *f,
+ const struct lu_object_conf *conf);
+/** @} caching */
+
+/** \name helpers
+ * Helpers.
+ * @{
+ */
+
+/**
+ * First (topmost) sub-object of given compound object
+ */
+static inline struct lu_object *lu_object_top(struct lu_object_header *h)
+{
+ LASSERT(!list_empty(&h->loh_layers));
+ return container_of0(h->loh_layers.next, struct lu_object, lo_linkage);
+}
+
+/**
+ * Next sub-object in the layering
+ */
+static inline struct lu_object *lu_object_next(const struct lu_object *o)
+{
+ return container_of0(o->lo_linkage.next, struct lu_object, lo_linkage);
+}
+
+/**
+ * Pointer to the fid of this object.
+ */
+static inline const struct lu_fid *lu_object_fid(const struct lu_object *o)
+{
+ return &o->lo_header->loh_fid;
+}
+
+/**
+ * return device operations vector for this object
+ */
+static const inline struct lu_device_operations *
+lu_object_ops(const struct lu_object *o)
+{
+ return o->lo_dev->ld_ops;
+}
+
+/**
+ * Given a compound object, find its slice, corresponding to the device type
+ * \a dtype.
+ */
+struct lu_object *lu_object_locate(struct lu_object_header *h,
+ const struct lu_device_type *dtype);
+
+/**
+ * Printer function emitting messages through libcfs_debug_msg().
+ */
+int lu_cdebug_printer(const struct lu_env *env,
+ void *cookie, const char *format, ...);
+
+/**
+ * Print object description followed by a user-supplied message.
+ */
+#define LU_OBJECT_DEBUG(mask, env, object, format, ...) \
+do { \
+ LIBCFS_DEBUG_MSG_DATA_DECL(msgdata, mask, NULL); \
+ \
+ if (cfs_cdebug_show(mask, DEBUG_SUBSYSTEM)) { \
+ lu_object_print(env, &msgdata, lu_cdebug_printer, object);\
+ CDEBUG(mask, format , ## __VA_ARGS__); \
+ } \
+} while (0)
+
+/**
+ * Print short object description followed by a user-supplied message.
+ */
+#define LU_OBJECT_HEADER(mask, env, object, format, ...) \
+do { \
+ LIBCFS_DEBUG_MSG_DATA_DECL(msgdata, mask, NULL); \
+ \
+ if (cfs_cdebug_show(mask, DEBUG_SUBSYSTEM)) { \
+ lu_object_header_print(env, &msgdata, lu_cdebug_printer,\
+ (object)->lo_header); \
+ lu_cdebug_printer(env, &msgdata, "\n"); \
+ CDEBUG(mask, format , ## __VA_ARGS__); \
+ } \
+} while (0)
+
+void lu_object_print (const struct lu_env *env, void *cookie,
+ lu_printer_t printer, const struct lu_object *o);
+void lu_object_header_print(const struct lu_env *env, void *cookie,
+ lu_printer_t printer,
+ const struct lu_object_header *hdr);
+
+/**
+ * Check object consistency.
+ */
+int lu_object_invariant(const struct lu_object *o);
+
+
+/**
+ * Check whether object exists, no matter on local or remote storage.
+ * Note: LOHA_EXISTS will be set once some one created the object,
+ * and it does not needs to be committed to storage.
+ */
+#define lu_object_exists(o) ((o)->lo_header->loh_attr & LOHA_EXISTS)
+
+/**
+ * Check whether object on the remote storage.
+ */
+#define lu_object_remote(o) unlikely((o)->lo_header->loh_attr & LOHA_REMOTE)
+
+static inline int lu_object_assert_exists(const struct lu_object *o)
+{
+ return lu_object_exists(o);
+}
+
+static inline int lu_object_assert_not_exists(const struct lu_object *o)
+{
+ return !lu_object_exists(o);
+}
+
+/**
+ * Attr of this object.
+ */
+static inline __u32 lu_object_attr(const struct lu_object *o)
+{
+ LASSERT(lu_object_exists(o) != 0);
+ return o->lo_header->loh_attr;
+}
+
+static inline void lu_object_ref_add(struct lu_object *o,
+ const char *scope,
+ const void *source)
+{
+ lu_ref_add(&o->lo_header->loh_reference, scope, source);
+}
+
+static inline void lu_object_ref_add_at(struct lu_object *o,
+ struct lu_ref_link *link,
+ const char *scope,
+ const void *source)
+{
+ lu_ref_add_at(&o->lo_header->loh_reference, link, scope, source);
+}
+
+static inline void lu_object_ref_del(struct lu_object *o,
+ const char *scope, const void *source)
+{
+ lu_ref_del(&o->lo_header->loh_reference, scope, source);
+}
+
+static inline void lu_object_ref_del_at(struct lu_object *o,
+ struct lu_ref_link *link,
+ const char *scope, const void *source)
+{
+ lu_ref_del_at(&o->lo_header->loh_reference, link, scope, source);
+}
+
+/** input params, should be filled out by mdt */
+struct lu_rdpg {
+ /** hash */
+ __u64 rp_hash;
+ /** count in bytes */
+ unsigned int rp_count;
+ /** number of pages */
+ unsigned int rp_npages;
+ /** requested attr */
+ __u32 rp_attrs;
+ /** pointers to pages */
+ struct page **rp_pages;
+};
+
+enum lu_xattr_flags {
+ LU_XATTR_REPLACE = (1 << 0),
+ LU_XATTR_CREATE = (1 << 1)
+};
+
+/** @} helpers */
+
+/** \name lu_context
+ * @{ */
+
+/** For lu_context health-checks */
+enum lu_context_state {
+ LCS_INITIALIZED = 1,
+ LCS_ENTERED,
+ LCS_LEFT,
+ LCS_FINALIZED
+};
+
+/**
+ * lu_context. Execution context for lu_object methods. Currently associated
+ * with thread.
+ *
+ * All lu_object methods, except device and device type methods (called during
+ * system initialization and shutdown) are executed "within" some
+ * lu_context. This means, that pointer to some "current" lu_context is passed
+ * as an argument to all methods.
+ *
+ * All service ptlrpc threads create lu_context as part of their
+ * initialization. It is possible to create "stand-alone" context for other
+ * execution environments (like system calls).
+ *
+ * lu_object methods mainly use lu_context through lu_context_key interface
+ * that allows each layer to associate arbitrary pieces of data with each
+ * context (see pthread_key_create(3) for similar interface).
+ *
+ * On a client, lu_context is bound to a thread, see cl_env_get().
+ *
+ * \see lu_context_key
+ */
+struct lu_context {
+ /**
+ * lu_context is used on the client side too. Yet we don't want to
+ * allocate values of server-side keys for the client contexts and
+ * vice versa.
+ *
+ * To achieve this, set of tags in introduced. Contexts and keys are
+ * marked with tags. Key value are created only for context whose set
+ * of tags has non-empty intersection with one for key. Tags are taken
+ * from enum lu_context_tag.
+ */
+ __u32 lc_tags;
+ enum lu_context_state lc_state;
+ /**
+ * Pointer to the home service thread. NULL for other execution
+ * contexts.
+ */
+ struct ptlrpc_thread *lc_thread;
+ /**
+ * Pointer to an array with key values. Internal implementation
+ * detail.
+ */
+ void **lc_value;
+ /**
+ * Linkage into a list of all remembered contexts. Only
+ * `non-transient' contexts, i.e., ones created for service threads
+ * are placed here.
+ */
+ struct list_head lc_remember;
+ /**
+ * Version counter used to skip calls to lu_context_refill() when no
+ * keys were registered.
+ */
+ unsigned lc_version;
+ /**
+ * Debugging cookie.
+ */
+ unsigned lc_cookie;
+};
+
+/**
+ * lu_context_key interface. Similar to pthread_key.
+ */
+
+enum lu_context_tag {
+ /**
+ * Thread on md server
+ */
+ LCT_MD_THREAD = 1 << 0,
+ /**
+ * Thread on dt server
+ */
+ LCT_DT_THREAD = 1 << 1,
+ /**
+ * Context for transaction handle
+ */
+ LCT_TX_HANDLE = 1 << 2,
+ /**
+ * Thread on client
+ */
+ LCT_CL_THREAD = 1 << 3,
+ /**
+ * A per-request session on a server, and a per-system-call session on
+ * a client.
+ */
+ LCT_SESSION = 1 << 4,
+ /**
+ * A per-request data on OSP device
+ */
+ LCT_OSP_THREAD = 1 << 5,
+ /**
+ * MGS device thread
+ */
+ LCT_MG_THREAD = 1 << 6,
+ /**
+ * Context for local operations
+ */
+ LCT_LOCAL = 1 << 7,
+ /**
+ * Set when at least one of keys, having values in this context has
+ * non-NULL lu_context_key::lct_exit() method. This is used to
+ * optimize lu_context_exit() call.
+ */
+ LCT_HAS_EXIT = 1 << 28,
+ /**
+ * Don't add references for modules creating key values in that context.
+ * This is only for contexts used internally by lu_object framework.
+ */
+ LCT_NOREF = 1 << 29,
+ /**
+ * Key is being prepared for retiring, don't create new values for it.
+ */
+ LCT_QUIESCENT = 1 << 30,
+ /**
+ * Context should be remembered.
+ */
+ LCT_REMEMBER = 1 << 31,
+ /**
+ * Contexts usable in cache shrinker thread.
+ */
+ LCT_SHRINKER = LCT_MD_THREAD|LCT_DT_THREAD|LCT_CL_THREAD|LCT_NOREF
+};
+
+/**
+ * Key. Represents per-context value slot.
+ *
+ * Keys are usually registered when module owning the key is initialized, and
+ * de-registered when module is unloaded. Once key is registered, all new
+ * contexts with matching tags, will get key value. "Old" contexts, already
+ * initialized at the time of key registration, can be forced to get key value
+ * by calling lu_context_refill().
+ *
+ * Every key value is counted in lu_context_key::lct_used and acquires a
+ * reference on an owning module. This means, that all key values have to be
+ * destroyed before module can be unloaded. This is usually achieved by
+ * stopping threads started by the module, that created contexts in their
+ * entry functions. Situation is complicated by the threads shared by multiple
+ * modules, like ptlrpcd daemon on a client. To work around this problem,
+ * contexts, created in such threads, are `remembered' (see
+ * LCT_REMEMBER)---i.e., added into a global list. When module is preparing
+ * for unloading it does the following:
+ *
+ * - marks its keys as `quiescent' (lu_context_tag::LCT_QUIESCENT)
+ * preventing new key values from being allocated in the new contexts,
+ * and
+ *
+ * - scans a list of remembered contexts, destroying values of module
+ * keys, thus releasing references to the module.
+ *
+ * This is done by lu_context_key_quiesce(). If module is re-activated
+ * before key has been de-registered, lu_context_key_revive() call clears
+ * `quiescent' marker.
+ *
+ * lu_context code doesn't provide any internal synchronization for these
+ * activities---it's assumed that startup (including threads start-up) and
+ * shutdown are serialized by some external means.
+ *
+ * \see lu_context
+ */
+struct lu_context_key {
+ /**
+ * Set of tags for which values of this key are to be instantiated.
+ */
+ __u32 lct_tags;
+ /**
+ * Value constructor. This is called when new value is created for a
+ * context. Returns pointer to new value of error pointer.
+ */
+ void *(*lct_init)(const struct lu_context *ctx,
+ struct lu_context_key *key);
+ /**
+ * Value destructor. Called when context with previously allocated
+ * value of this slot is destroyed. \a data is a value that was returned
+ * by a matching call to lu_context_key::lct_init().
+ */
+ void (*lct_fini)(const struct lu_context *ctx,
+ struct lu_context_key *key, void *data);
+ /**
+ * Optional method called on lu_context_exit() for all allocated
+ * keys. Can be used by debugging code checking that locks are
+ * released, etc.
+ */
+ void (*lct_exit)(const struct lu_context *ctx,
+ struct lu_context_key *key, void *data);
+ /**
+ * Internal implementation detail: index within lu_context::lc_value[]
+ * reserved for this key.
+ */
+ int lct_index;
+ /**
+ * Internal implementation detail: number of values created for this
+ * key.
+ */
+ atomic_t lct_used;
+ /**
+ * Internal implementation detail: module for this key.
+ */
+ struct module *lct_owner;
+ /**
+ * References to this key. For debugging.
+ */
+ struct lu_ref lct_reference;
+};
+
+#define LU_KEY_INIT(mod, type) \
+ static void *mod##_key_init(const struct lu_context *ctx, \
+ struct lu_context_key *key) \
+ { \
+ type *value; \
+ \
+ CLASSERT(PAGE_CACHE_SIZE >= sizeof (*value)); \
+ \
+ OBD_ALLOC_PTR(value); \
+ if (value == NULL) \
+ value = ERR_PTR(-ENOMEM); \
+ \
+ return value; \
+ } \
+ struct __##mod##__dummy_init {;} /* semicolon catcher */
+
+#define LU_KEY_FINI(mod, type) \
+ static void mod##_key_fini(const struct lu_context *ctx, \
+ struct lu_context_key *key, void *data) \
+ { \
+ type *info = data; \
+ \
+ OBD_FREE_PTR(info); \
+ } \
+ struct __##mod##__dummy_fini {;} /* semicolon catcher */
+
+#define LU_KEY_INIT_FINI(mod, type) \
+ LU_KEY_INIT(mod, type); \
+ LU_KEY_FINI(mod, type)
+
+#define LU_CONTEXT_KEY_DEFINE(mod, tags) \
+ struct lu_context_key mod##_thread_key = { \
+ .lct_tags = tags, \
+ .lct_init = mod##_key_init, \
+ .lct_fini = mod##_key_fini \
+ }
+
+#define LU_CONTEXT_KEY_INIT(key) \
+do { \
+ (key)->lct_owner = THIS_MODULE; \
+} while (0)
+
+int lu_context_key_register(struct lu_context_key *key);
+void lu_context_key_degister(struct lu_context_key *key);
+void *lu_context_key_get (const struct lu_context *ctx,
+ const struct lu_context_key *key);
+void lu_context_key_quiesce (struct lu_context_key *key);
+void lu_context_key_revive (struct lu_context_key *key);
+
+
+/*
+ * LU_KEY_INIT_GENERIC() has to be a macro to correctly determine an
+ * owning module.
+ */
+
+#define LU_KEY_INIT_GENERIC(mod) \
+ static void mod##_key_init_generic(struct lu_context_key *k, ...) \
+ { \
+ struct lu_context_key *key = k; \
+ va_list args; \
+ \
+ va_start(args, k); \
+ do { \
+ LU_CONTEXT_KEY_INIT(key); \
+ key = va_arg(args, struct lu_context_key *); \
+ } while (key != NULL); \
+ va_end(args); \
+ }
+
+#define LU_TYPE_INIT(mod, ...) \
+ LU_KEY_INIT_GENERIC(mod) \
+ static int mod##_type_init(struct lu_device_type *t) \
+ { \
+ mod##_key_init_generic(__VA_ARGS__, NULL); \
+ return lu_context_key_register_many(__VA_ARGS__, NULL); \
+ } \
+ struct __##mod##_dummy_type_init {;}
+
+#define LU_TYPE_FINI(mod, ...) \
+ static void mod##_type_fini(struct lu_device_type *t) \
+ { \
+ lu_context_key_degister_many(__VA_ARGS__, NULL); \
+ } \
+ struct __##mod##_dummy_type_fini {;}
+
+#define LU_TYPE_START(mod, ...) \
+ static void mod##_type_start(struct lu_device_type *t) \
+ { \
+ lu_context_key_revive_many(__VA_ARGS__, NULL); \
+ } \
+ struct __##mod##_dummy_type_start {;}
+
+#define LU_TYPE_STOP(mod, ...) \
+ static void mod##_type_stop(struct lu_device_type *t) \
+ { \
+ lu_context_key_quiesce_many(__VA_ARGS__, NULL); \
+ } \
+ struct __##mod##_dummy_type_stop {;}
+
+
+
+#define LU_TYPE_INIT_FINI(mod, ...) \
+ LU_TYPE_INIT(mod, __VA_ARGS__); \
+ LU_TYPE_FINI(mod, __VA_ARGS__); \
+ LU_TYPE_START(mod, __VA_ARGS__); \
+ LU_TYPE_STOP(mod, __VA_ARGS__)
+
+int lu_context_init (struct lu_context *ctx, __u32 tags);
+void lu_context_fini (struct lu_context *ctx);
+void lu_context_enter (struct lu_context *ctx);
+void lu_context_exit (struct lu_context *ctx);
+int lu_context_refill(struct lu_context *ctx);
+
+/*
+ * Helper functions to operate on multiple keys. These are used by the default
+ * device type operations, defined by LU_TYPE_INIT_FINI().
+ */
+
+int lu_context_key_register_many(struct lu_context_key *k, ...);
+void lu_context_key_degister_many(struct lu_context_key *k, ...);
+void lu_context_key_revive_many (struct lu_context_key *k, ...);
+void lu_context_key_quiesce_many (struct lu_context_key *k, ...);
+
+/*
+ * update/clear ctx/ses tags.
+ */
+void lu_context_tags_update(__u32 tags);
+void lu_context_tags_clear(__u32 tags);
+void lu_session_tags_update(__u32 tags);
+void lu_session_tags_clear(__u32 tags);
+
+/**
+ * Environment.
+ */
+struct lu_env {
+ /**
+ * "Local" context, used to store data instead of stack.
+ */
+ struct lu_context le_ctx;
+ /**
+ * "Session" context for per-request data.
+ */
+ struct lu_context *le_ses;
+};
+
+int lu_env_init (struct lu_env *env, __u32 tags);
+void lu_env_fini (struct lu_env *env);
+int lu_env_refill(struct lu_env *env);
+int lu_env_refill_by_tags(struct lu_env *env, __u32 ctags, __u32 stags);
+
+/** @} lu_context */
+
+/**
+ * Output site statistical counters into a buffer. Suitable for
+ * ll_rd_*()-style functions.
+ */
+int lu_site_stats_print(const struct lu_site *s, struct seq_file *m);
+
+/**
+ * Common name structure to be passed around for various name related methods.
+ */
+struct lu_name {
+ const char *ln_name;
+ int ln_namelen;
+};
+
+/**
+ * Common buffer structure to be passed around for various xattr_{s,g}et()
+ * methods.
+ */
+struct lu_buf {
+ void *lb_buf;
+ ssize_t lb_len;
+};
+
+#define DLUBUF "(%p %zu)"
+#define PLUBUF(buf) (buf)->lb_buf, (buf)->lb_len
+/**
+ * One-time initializers, called at obdclass module initialization, not
+ * exported.
+ */
+
+/**
+ * Initialization of global lu_* data.
+ */
+int lu_global_init(void);
+
+/**
+ * Dual to lu_global_init().
+ */
+void lu_global_fini(void);
+
+struct lu_kmem_descr {
+ struct kmem_cache **ckd_cache;
+ const char *ckd_name;
+ const size_t ckd_size;
+};
+
+int lu_kmem_init(struct lu_kmem_descr *caches);
+void lu_kmem_fini(struct lu_kmem_descr *caches);
+
+void lu_object_assign_fid(const struct lu_env *env, struct lu_object *o,
+ const struct lu_fid *fid);
+struct lu_object *lu_object_anon(const struct lu_env *env,
+ struct lu_device *dev,
+ const struct lu_object_conf *conf);
+
+/** null buffer */
+extern struct lu_buf LU_BUF_NULL;
+
+void lu_buf_free(struct lu_buf *buf);
+void lu_buf_alloc(struct lu_buf *buf, int size);
+void lu_buf_realloc(struct lu_buf *buf, int size);
+
+int lu_buf_check_and_grow(struct lu_buf *buf, int len);
+struct lu_buf *lu_buf_check_and_alloc(struct lu_buf *buf, int len);
+
+/** @} lu */
+#endif /* __LUSTRE_LU_OBJECT_H */
diff --git a/drivers/staging/lustre/lustre/include/lu_ref.h b/drivers/staging/lustre/lustre/include/lu_ref.h
new file mode 100644
index 000000000..b451a888c
--- /dev/null
+++ b/drivers/staging/lustre/lustre/include/lu_ref.h
@@ -0,0 +1,182 @@
+/*
+ * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2012, Intel Corporation.
+ *
+ * Author: Nikita Danilov <nikita.danilov@sun.com>
+ *
+ * This file is part of Lustre, http://www.lustre.org.
+ *
+ * Lustre is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * Lustre 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 Lustre; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#ifndef __LUSTRE_LU_REF_H
+#define __LUSTRE_LU_REF_H
+
+#include <linux/list.h>
+
+/** \defgroup lu_ref lu_ref
+ *
+ * An interface to track references between objects. Mostly for debugging.
+ *
+ * Suppose there is a reference counted data-structure struct foo. To track
+ * who acquired references to instance of struct foo, add lu_ref field to it:
+ *
+ * \code
+ * struct foo {
+ * atomic_t foo_refcount;
+ * struct lu_ref foo_reference;
+ * ...
+ * };
+ * \endcode
+ *
+ * foo::foo_reference has to be initialized by calling
+ * lu_ref_init(). Typically there will be functions or macros to increment and
+ * decrement foo::foo_refcount, let's say they are foo_get(struct foo *foo)
+ * and foo_put(struct foo *foo), respectively.
+ *
+ * Whenever foo_get() is called to acquire a reference on a foo, lu_ref_add()
+ * has to be called to insert into foo::foo_reference a record, describing
+ * acquired reference. Dually, lu_ref_del() removes matching record. Typical
+ * usages are:
+ *
+ * \code
+ * struct bar *bar;
+ *
+ * // bar owns a reference to foo.
+ * bar->bar_foo = foo_get(foo);
+ * lu_ref_add(&foo->foo_reference, "bar", bar);
+ *
+ * ...
+ *
+ * // reference from bar to foo is released.
+ * lu_ref_del(&foo->foo_reference, "bar", bar);
+ * foo_put(bar->bar_foo);
+ *
+ *
+ * // current thread acquired a temporary reference to foo.
+ * foo_get(foo);
+ * lu_ref_add(&foo->reference, __func__, current);
+ *
+ * ...
+ *
+ * // temporary reference is released.
+ * lu_ref_del(&foo->reference, __func__, current);
+ * foo_put(foo);
+ * \endcode
+ *
+ * \e Et \e cetera. Often it makes sense to include lu_ref_add() and
+ * lu_ref_del() calls into foo_get() and foo_put(). When an instance of struct
+ * foo is destroyed, lu_ref_fini() has to be called that checks that no
+ * pending references remain. lu_ref_print() can be used to dump a list of
+ * pending references, while hunting down a leak.
+ *
+ * For objects to which a large number of references can be acquired,
+ * lu_ref_del() can become cpu consuming, as it has to scan the list of
+ * references. To work around this, remember result of lu_ref_add() (usually
+ * in the same place where pointer to struct foo is stored), and use
+ * lu_ref_del_at():
+ *
+ * \code
+ * // There is a large number of bar's for a single foo.
+ * bar->bar_foo = foo_get(foo);
+ * bar->bar_foo_ref = lu_ref_add(&foo->foo_reference, "bar", bar);
+ *
+ * ...
+ *
+ * // reference from bar to foo is released.
+ * lu_ref_del_at(&foo->foo_reference, bar->bar_foo_ref, "bar", bar);
+ * foo_put(bar->bar_foo);
+ * \endcode
+ *
+ * lu_ref interface degrades gracefully in case of memory shortages.
+ *
+ * @{
+ */
+
+
+/*
+ * dummy data structures/functions to pass compile for now.
+ * We need to reimplement them with kref.
+ */
+struct lu_ref {};
+struct lu_ref_link {};
+
+static inline void lu_ref_init(struct lu_ref *ref)
+{
+}
+
+static inline void lu_ref_fini(struct lu_ref *ref)
+{
+}
+
+static inline struct lu_ref_link *lu_ref_add(struct lu_ref *ref,
+ const char *scope,
+ const void *source)
+{
+ return NULL;
+}
+
+static inline struct lu_ref_link *lu_ref_add_atomic(struct lu_ref *ref,
+ const char *scope,
+ const void *source)
+{
+ return NULL;
+}
+
+static inline void lu_ref_add_at(struct lu_ref *ref,
+ struct lu_ref_link *link,
+ const char *scope,
+ const void *source)
+{
+}
+
+static inline void lu_ref_del(struct lu_ref *ref, const char *scope,
+ const void *source)
+{
+}
+
+static inline void lu_ref_set_at(struct lu_ref *ref, struct lu_ref_link *link,
+ const char *scope, const void *source0,
+ const void *source1)
+{
+}
+
+static inline void lu_ref_del_at(struct lu_ref *ref, struct lu_ref_link *link,
+ const char *scope, const void *source)
+{
+}
+
+static inline int lu_ref_global_init(void)
+{
+ return 0;
+}
+
+static inline void lu_ref_global_fini(void)
+{
+}
+
+static inline void lu_ref_print(const struct lu_ref *ref)
+{
+}
+
+static inline void lu_ref_print_all(void)
+{
+}
+
+/** @} lu */
+
+#endif /* __LUSTRE_LU_REF_H */
diff --git a/drivers/staging/lustre/lustre/include/lustre/libiam.h b/drivers/staging/lustre/lustre/include/lustre/libiam.h
new file mode 100644
index 000000000..e8e0b084a
--- /dev/null
+++ b/drivers/staging/lustre/lustre/include/lustre/libiam.h
@@ -0,0 +1,145 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ *
+ * lustre/include/lustre/libiam.h
+ *
+ * iam user level library
+ *
+ * Author: Wang Di <wangdi@clusterfs.com>
+ * Author: Nikita Danilov <nikita@clusterfs.com>
+ * Author: Fan Yong <fanyong@clusterfs.com>
+ */
+
+/*
+ * lustre/libiam.h
+ */
+
+#ifndef __IAM_ULIB_H__
+#define __IAM_ULIB_H__
+
+/** \defgroup libiam libiam
+ *
+ * @{
+ */
+
+
+#define DX_FMT_NAME_LEN 16
+
+enum iam_fmt_t {
+ FMT_LFIX,
+ FMT_LVAR
+};
+
+struct iam_uapi_info {
+ __u16 iui_keysize;
+ __u16 iui_recsize;
+ __u16 iui_ptrsize;
+ __u16 iui_height;
+ char iui_fmt_name[DX_FMT_NAME_LEN];
+};
+
+/*
+ * Creat an iam file, but do NOT open it.
+ * Return 0 if success, else -1.
+ */
+int iam_creat(char *filename, enum iam_fmt_t fmt,
+ int blocksize, int keysize, int recsize, int ptrsize);
+
+/*
+ * Open an iam file, but do NOT creat it if the file doesn't exist.
+ * Please use iam_creat for creating the file before use iam_open.
+ * Return file id (fd) if success, else -1.
+ */
+int iam_open(char *filename, struct iam_uapi_info *ua);
+
+/*
+ * Close file opened by iam_open.
+ */
+int iam_close(int fd);
+
+/*
+ * Please use iam_open before use this function.
+ */
+int iam_insert(int fd, struct iam_uapi_info *ua,
+ int key_need_convert, char *keybuf,
+ int rec_need_convert, char *recbuf);
+
+/*
+ * Please use iam_open before use this function.
+ */
+int iam_lookup(int fd, struct iam_uapi_info *ua,
+ int key_need_convert, char *key_buf,
+ int *keysize, char *save_key,
+ int rec_need_convert, char *rec_buf,
+ int *recsize, char *save_rec);
+
+/*
+ * Please use iam_open before use this function.
+ */
+int iam_delete(int fd, struct iam_uapi_info *ua,
+ int key_need_convert, char *keybuf,
+ int rec_need_convert, char *recbuf);
+
+/*
+ * Please use iam_open before use this function.
+ */
+int iam_it_start(int fd, struct iam_uapi_info *ua,
+ int key_need_convert, char *key_buf,
+ int *keysize, char *save_key,
+ int rec_need_convert, char *rec_buf,
+ int *recsize, char *save_rec);
+
+/*
+ * Please use iam_open before use this function.
+ */
+int iam_it_next(int fd, struct iam_uapi_info *ua,
+ int key_need_convert, char *key_buf,
+ int *keysize, char *save_key,
+ int rec_need_convert, char *rec_buf,
+ int *recsize, char *save_rec);
+
+/*
+ * Please use iam_open before use this function.
+ */
+int iam_it_stop(int fd, struct iam_uapi_info *ua,
+ int key_need_convert, char *keybuf,
+ int rec_need_convert, char *recbuf);
+
+/*
+ * Change iam file mode.
+ */
+int iam_polymorph(char *filename, unsigned long mode);
+
+/** @} libiam */
+
+#endif
diff --git a/drivers/staging/lustre/lustre/include/lustre/ll_fiemap.h b/drivers/staging/lustre/lustre/include/lustre/ll_fiemap.h
new file mode 100644
index 000000000..ad253c6de
--- /dev/null
+++ b/drivers/staging/lustre/lustre/include/lustre/ll_fiemap.h
@@ -0,0 +1,121 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ *
+ * lustre/include/lustre/ll_fiemap.h
+ *
+ * FIEMAP data structures and flags. This header file will be used until
+ * fiemap.h is available in the upstream kernel.
+ *
+ * Author: Kalpak Shah <kalpak.shah@sun.com>
+ * Author: Andreas Dilger <adilger@sun.com>
+ */
+
+#ifndef _LUSTRE_FIEMAP_H
+#define _LUSTRE_FIEMAP_H
+
+
+
+struct ll_fiemap_extent {
+ __u64 fe_logical; /* logical offset in bytes for the start of
+ * the extent from the beginning of the file */
+ __u64 fe_physical; /* physical offset in bytes for the start
+ * of the extent from the beginning of the disk */
+ __u64 fe_length; /* length in bytes for this extent */
+ __u64 fe_reserved64[2];
+ __u32 fe_flags; /* FIEMAP_EXTENT_* flags for this extent */
+ __u32 fe_device; /* device number for this extent */
+ __u32 fe_reserved[2];
+};
+
+struct ll_user_fiemap {
+ __u64 fm_start; /* logical offset (inclusive) at
+ * which to start mapping (in) */
+ __u64 fm_length; /* logical length of mapping which
+ * userspace wants (in) */
+ __u32 fm_flags; /* FIEMAP_FLAG_* flags for request (in/out) */
+ __u32 fm_mapped_extents;/* number of extents that were mapped (out) */
+ __u32 fm_extent_count; /* size of fm_extents array (in) */
+ __u32 fm_reserved;
+ struct ll_fiemap_extent fm_extents[0]; /* array of mapped extents (out) */
+};
+
+#define FIEMAP_MAX_OFFSET (~0ULL)
+
+#define FIEMAP_FLAG_SYNC 0x00000001 /* sync file data before map */
+#define FIEMAP_FLAG_XATTR 0x00000002 /* map extended attribute tree */
+
+#define FIEMAP_EXTENT_LAST 0x00000001 /* Last extent in file. */
+#define FIEMAP_EXTENT_UNKNOWN 0x00000002 /* Data location unknown. */
+#define FIEMAP_EXTENT_DELALLOC 0x00000004 /* Location still pending.
+ * Sets EXTENT_UNKNOWN. */
+#define FIEMAP_EXTENT_ENCODED 0x00000008 /* Data can not be read
+ * while fs is unmounted */
+#define FIEMAP_EXTENT_DATA_ENCRYPTED 0x00000080 /* Data is encrypted by fs.
+ * Sets EXTENT_NO_DIRECT. */
+#define FIEMAP_EXTENT_NOT_ALIGNED 0x00000100 /* Extent offsets may not be
+ * block aligned. */
+#define FIEMAP_EXTENT_DATA_INLINE 0x00000200 /* Data mixed with metadata.
+ * Sets EXTENT_NOT_ALIGNED.*/
+#define FIEMAP_EXTENT_DATA_TAIL 0x00000400 /* Multiple files in block.
+ * Sets EXTENT_NOT_ALIGNED.*/
+#define FIEMAP_EXTENT_UNWRITTEN 0x00000800 /* Space allocated, but
+ * no data (i.e. zero). */
+#define FIEMAP_EXTENT_MERGED 0x00001000 /* File does not natively
+ * support extents. Result
+ * merged for efficiency. */
+
+
+static inline size_t fiemap_count_to_size(size_t extent_count)
+{
+ return (sizeof(struct ll_user_fiemap) + extent_count *
+ sizeof(struct ll_fiemap_extent));
+}
+
+static inline unsigned fiemap_size_to_count(size_t array_size)
+{
+ return ((array_size - sizeof(struct ll_user_fiemap)) /
+ sizeof(struct ll_fiemap_extent));
+}
+
+#define FIEMAP_FLAG_DEVICE_ORDER 0x40000000 /* return device ordered mapping */
+
+#ifdef FIEMAP_FLAGS_COMPAT
+#undef FIEMAP_FLAGS_COMPAT
+#endif
+
+/* Lustre specific flags - use a high bit, don't conflict with upstream flag */
+#define FIEMAP_EXTENT_NO_DIRECT 0x40000000 /* Data mapping undefined */
+#define FIEMAP_EXTENT_NET 0x80000000 /* Data stored remotely.
+ * Sets NO_DIRECT flag */
+
+#endif /* _LUSTRE_FIEMAP_H */
diff --git a/drivers/staging/lustre/lustre/include/lustre/lustre_build_version.h b/drivers/staging/lustre/lustre/include/lustre/lustre_build_version.h
new file mode 100644
index 000000000..93a3d7db3
--- /dev/null
+++ b/drivers/staging/lustre/lustre/include/lustre/lustre_build_version.h
@@ -0,0 +1,2 @@
+#define BUILD_VERSION "v2_3_64_0-g6e62c21-CHANGED-3.9.0"
+#define LUSTRE_RELEASE 3.9.0_g6e62c21
diff --git a/drivers/staging/lustre/lustre/include/lustre/lustre_errno.h b/drivers/staging/lustre/lustre/include/lustre/lustre_errno.h
new file mode 100644
index 000000000..35aefa2cd
--- /dev/null
+++ b/drivers/staging/lustre/lustre/include/lustre/lustre_errno.h
@@ -0,0 +1,215 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.gnu.org/licenses/gpl-2.0.txt
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (C) 2011 FUJITSU LIMITED. All rights reserved.
+ *
+ * Copyright (c) 2013, Intel Corporation.
+ */
+
+#ifndef LUSTRE_ERRNO_H
+#define LUSTRE_ERRNO_H
+
+/*
+ * Only "network" errnos, which are defined below, are allowed on wire (or on
+ * disk). Generic routines exist to help translate between these and a subset
+ * of the "host" errnos. Some host errnos (e.g., EDEADLOCK) are intentionally
+ * left out. See also the comment on lustre_errno_hton_mapping[].
+ *
+ * To maintain compatibility with existing x86 clients and servers, each of
+ * these network errnos has the same numerical value as its corresponding host
+ * errno on x86.
+ */
+#define LUSTRE_EPERM 1 /* Operation not permitted */
+#define LUSTRE_ENOENT 2 /* No such file or directory */
+#define LUSTRE_ESRCH 3 /* No such process */
+#define LUSTRE_EINTR 4 /* Interrupted system call */
+#define LUSTRE_EIO 5 /* I/O error */
+#define LUSTRE_ENXIO 6 /* No such device or address */
+#define LUSTRE_E2BIG 7 /* Argument list too long */
+#define LUSTRE_ENOEXEC 8 /* Exec format error */
+#define LUSTRE_EBADF 9 /* Bad file number */
+#define LUSTRE_ECHILD 10 /* No child processes */
+#define LUSTRE_EAGAIN 11 /* Try again */
+#define LUSTRE_ENOMEM 12 /* Out of memory */
+#define LUSTRE_EACCES 13 /* Permission denied */
+#define LUSTRE_EFAULT 14 /* Bad address */
+#define LUSTRE_ENOTBLK 15 /* Block device required */
+#define LUSTRE_EBUSY 16 /* Device or resource busy */
+#define LUSTRE_EEXIST 17 /* File exists */
+#define LUSTRE_EXDEV 18 /* Cross-device link */
+#define LUSTRE_ENODEV 19 /* No such device */
+#define LUSTRE_ENOTDIR 20 /* Not a directory */
+#define LUSTRE_EISDIR 21 /* Is a directory */
+#define LUSTRE_EINVAL 22 /* Invalid argument */
+#define LUSTRE_ENFILE 23 /* File table overflow */
+#define LUSTRE_EMFILE 24 /* Too many open files */
+#define LUSTRE_ENOTTY 25 /* Not a typewriter */
+#define LUSTRE_ETXTBSY 26 /* Text file busy */
+#define LUSTRE_EFBIG 27 /* File too large */
+#define LUSTRE_ENOSPC 28 /* No space left on device */
+#define LUSTRE_ESPIPE 29 /* Illegal seek */
+#define LUSTRE_EROFS 30 /* Read-only file system */
+#define LUSTRE_EMLINK 31 /* Too many links */
+#define LUSTRE_EPIPE 32 /* Broken pipe */
+#define LUSTRE_EDOM 33 /* Math argument out of domain of
+ func */
+#define LUSTRE_ERANGE 34 /* Math result not representable */
+#define LUSTRE_EDEADLK 35 /* Resource deadlock would occur */
+#define LUSTRE_ENAMETOOLONG 36 /* File name too long */
+#define LUSTRE_ENOLCK 37 /* No record locks available */
+#define LUSTRE_ENOSYS 38 /* Function not implemented */
+#define LUSTRE_ENOTEMPTY 39 /* Directory not empty */
+#define LUSTRE_ELOOP 40 /* Too many symbolic links
+ encountered */
+#define LUSTRE_ENOMSG 42 /* No message of desired type */
+#define LUSTRE_EIDRM 43 /* Identifier removed */
+#define LUSTRE_ECHRNG 44 /* Channel number out of range */
+#define LUSTRE_EL2NSYNC 45 /* Level 2 not synchronized */
+#define LUSTRE_EL3HLT 46 /* Level 3 halted */
+#define LUSTRE_EL3RST 47 /* Level 3 reset */
+#define LUSTRE_ELNRNG 48 /* Link number out of range */
+#define LUSTRE_EUNATCH 49 /* Protocol driver not attached */
+#define LUSTRE_ENOCSI 50 /* No CSI structure available */
+#define LUSTRE_EL2HLT 51 /* Level 2 halted */
+#define LUSTRE_EBADE 52 /* Invalid exchange */
+#define LUSTRE_EBADR 53 /* Invalid request descriptor */
+#define LUSTRE_EXFULL 54 /* Exchange full */
+#define LUSTRE_ENOANO 55 /* No anode */
+#define LUSTRE_EBADRQC 56 /* Invalid request code */
+#define LUSTRE_EBADSLT 57 /* Invalid slot */
+#define LUSTRE_EBFONT 59 /* Bad font file format */
+#define LUSTRE_ENOSTR 60 /* Device not a stream */
+#define LUSTRE_ENODATA 61 /* No data available */
+#define LUSTRE_ETIME 62 /* Timer expired */
+#define LUSTRE_ENOSR 63 /* Out of streams resources */
+#define LUSTRE_ENONET 64 /* Machine is not on the network */
+#define LUSTRE_ENOPKG 65 /* Package not installed */
+#define LUSTRE_EREMOTE 66 /* Object is remote */
+#define LUSTRE_ENOLINK 67 /* Link has been severed */
+#define LUSTRE_EADV 68 /* Advertise error */
+#define LUSTRE_ESRMNT 69 /* Srmount error */
+#define LUSTRE_ECOMM 70 /* Communication error on send */
+#define LUSTRE_EPROTO 71 /* Protocol error */
+#define LUSTRE_EMULTIHOP 72 /* Multihop attempted */
+#define LUSTRE_EDOTDOT 73 /* RFS specific error */
+#define LUSTRE_EBADMSG 74 /* Not a data message */
+#define LUSTRE_EOVERFLOW 75 /* Value too large for defined data
+ type */
+#define LUSTRE_ENOTUNIQ 76 /* Name not unique on network */
+#define LUSTRE_EBADFD 77 /* File descriptor in bad state */
+#define LUSTRE_EREMCHG 78 /* Remote address changed */
+#define LUSTRE_ELIBACC 79 /* Can not access a needed shared
+ library */
+#define LUSTRE_ELIBBAD 80 /* Accessing a corrupted shared
+ library */
+#define LUSTRE_ELIBSCN 81 /* .lib section in a.out corrupted */
+#define LUSTRE_ELIBMAX 82 /* Attempting to link in too many shared
+ libraries */
+#define LUSTRE_ELIBEXEC 83 /* Cannot exec a shared library
+ directly */
+#define LUSTRE_EILSEQ 84 /* Illegal byte sequence */
+#define LUSTRE_ERESTART 85 /* Interrupted system call should be
+ restarted */
+#define LUSTRE_ESTRPIPE 86 /* Streams pipe error */
+#define LUSTRE_EUSERS 87 /* Too many users */
+#define LUSTRE_ENOTSOCK 88 /* Socket operation on non-socket */
+#define LUSTRE_EDESTADDRREQ 89 /* Destination address required */
+#define LUSTRE_EMSGSIZE 90 /* Message too long */
+#define LUSTRE_EPROTOTYPE 91 /* Protocol wrong type for socket */
+#define LUSTRE_ENOPROTOOPT 92 /* Protocol not available */
+#define LUSTRE_EPROTONOSUPPORT 93 /* Protocol not supported */
+#define LUSTRE_ESOCKTNOSUPPORT 94 /* Socket type not supported */
+#define LUSTRE_EOPNOTSUPP 95 /* Operation not supported on transport
+ endpoint */
+#define LUSTRE_EPFNOSUPPORT 96 /* Protocol family not supported */
+#define LUSTRE_EAFNOSUPPORT 97 /* Address family not supported by
+ protocol */
+#define LUSTRE_EADDRINUSE 98 /* Address already in use */
+#define LUSTRE_EADDRNOTAVAIL 99 /* Cannot assign requested address */
+#define LUSTRE_ENETDOWN 100 /* Network is down */
+#define LUSTRE_ENETUNREACH 101 /* Network is unreachable */
+#define LUSTRE_ENETRESET 102 /* Network dropped connection because of
+ reset */
+#define LUSTRE_ECONNABORTED 103 /* Software caused connection abort */
+#define LUSTRE_ECONNRESET 104 /* Connection reset by peer */
+#define LUSTRE_ENOBUFS 105 /* No buffer space available */
+#define LUSTRE_EISCONN 106 /* Transport endpoint is already
+ connected */
+#define LUSTRE_ENOTCONN 107 /* Transport endpoint is not
+ connected */
+#define LUSTRE_ESHUTDOWN 108 /* Cannot send after transport endpoint
+ shutdown */
+#define LUSTRE_ETOOMANYREFS 109 /* Too many references: cannot splice */
+#define LUSTRE_ETIMEDOUT 110 /* Connection timed out */
+#define LUSTRE_ECONNREFUSED 111 /* Connection refused */
+#define LUSTRE_EHOSTDOWN 112 /* Host is down */
+#define LUSTRE_EHOSTUNREACH 113 /* No route to host */
+#define LUSTRE_EALREADY 114 /* Operation already in progress */
+#define LUSTRE_EINPROGRESS 115 /* Operation now in progress */
+#define LUSTRE_ESTALE 116 /* Stale file handle */
+#define LUSTRE_EUCLEAN 117 /* Structure needs cleaning */
+#define LUSTRE_ENOTNAM 118 /* Not a XENIX named type file */
+#define LUSTRE_ENAVAIL 119 /* No XENIX semaphores available */
+#define LUSTRE_EISNAM 120 /* Is a named type file */
+#define LUSTRE_EREMOTEIO 121 /* Remote I/O error */
+#define LUSTRE_EDQUOT 122 /* Quota exceeded */
+#define LUSTRE_ENOMEDIUM 123 /* No medium found */
+#define LUSTRE_EMEDIUMTYPE 124 /* Wrong medium type */
+#define LUSTRE_ECANCELED 125 /* Operation Canceled */
+#define LUSTRE_ENOKEY 126 /* Required key not available */
+#define LUSTRE_EKEYEXPIRED 127 /* Key has expired */
+#define LUSTRE_EKEYREVOKED 128 /* Key has been revoked */
+#define LUSTRE_EKEYREJECTED 129 /* Key was rejected by service */
+#define LUSTRE_EOWNERDEAD 130 /* Owner died */
+#define LUSTRE_ENOTRECOVERABLE 131 /* State not recoverable */
+#define LUSTRE_ERESTARTSYS 512
+#define LUSTRE_ERESTARTNOINTR 513
+#define LUSTRE_ERESTARTNOHAND 514 /* restart if no handler.. */
+#define LUSTRE_ENOIOCTLCMD 515 /* No ioctl command */
+#define LUSTRE_ERESTART_RESTARTBLOCK 516 /* restart by calling
+ sys_restart_syscall */
+#define LUSTRE_EBADHANDLE 521 /* Illegal NFS file handle */
+#define LUSTRE_ENOTSYNC 522 /* Update synchronization mismatch */
+#define LUSTRE_EBADCOOKIE 523 /* Cookie is stale */
+#define LUSTRE_ENOTSUPP 524 /* Operation is not supported */
+#define LUSTRE_ETOOSMALL 525 /* Buffer or request is too small */
+#define LUSTRE_ESERVERFAULT 526 /* An untranslatable error occurred */
+#define LUSTRE_EBADTYPE 527 /* Type not supported by server */
+#define LUSTRE_EJUKEBOX 528 /* Request initiated, but will not
+ complete before timeout */
+#define LUSTRE_EIOCBQUEUED 529 /* iocb queued, will get completion
+ event */
+#define LUSTRE_EIOCBRETRY 530 /* iocb queued, will trigger a retry */
+
+/*
+ * Translations are optimized away on x86. Host errnos that shouldn't be put
+ * on wire could leak through as a result. Do not count on this side effect.
+ */
+#ifdef CONFIG_LUSTRE_TRANSLATE_ERRNOS
+unsigned int lustre_errno_hton(unsigned int h);
+unsigned int lustre_errno_ntoh(unsigned int n);
+#else
+#define lustre_errno_hton(h) (h)
+#define lustre_errno_ntoh(n) (n)
+#endif
+
+#endif /* LUSTRE_ERRNO_H */
diff --git a/drivers/staging/lustre/lustre/include/lustre/lustre_idl.h b/drivers/staging/lustre/lustre/include/lustre/lustre_idl.h
new file mode 100644
index 000000000..305ecbee9
--- /dev/null
+++ b/drivers/staging/lustre/lustre/include/lustre/lustre_idl.h
@@ -0,0 +1,3734 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2011, 2012, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ *
+ * lustre/include/lustre/lustre_idl.h
+ *
+ * Lustre wire protocol definitions.
+ */
+
+/** \defgroup lustreidl lustreidl
+ *
+ * Lustre wire protocol definitions.
+ *
+ * ALL structs passing over the wire should be declared here. Structs
+ * that are used in interfaces with userspace should go in lustre_user.h.
+ *
+ * All structs being declared here should be built from simple fixed-size
+ * types (__u8, __u16, __u32, __u64) or be built from other types or
+ * structs also declared in this file. Similarly, all flags and magic
+ * values in those structs should also be declared here. This ensures
+ * that the Lustre wire protocol is not influenced by external dependencies.
+ *
+ * The only other acceptable items in this file are VERY SIMPLE accessor
+ * functions to avoid callers grubbing inside the structures, and the
+ * prototypes of the swabber functions for each struct. Nothing that
+ * depends on external functions or definitions should be in here.
+ *
+ * Structs must be properly aligned to put 64-bit values on an 8-byte
+ * boundary. Any structs being added here must also be added to
+ * utils/wirecheck.c and "make newwiretest" run to regenerate the
+ * utils/wiretest.c sources. This allows us to verify that wire structs
+ * have the proper alignment/size on all architectures.
+ *
+ * DO NOT CHANGE any of the structs, flags, values declared here and used
+ * in released Lustre versions. Some structs may have padding fields that
+ * can be used. Some structs might allow addition at the end (verify this
+ * in the code to ensure that new/old clients that see this larger struct
+ * do not fail, otherwise you need to implement protocol compatibility).
+ *
+ * We assume all nodes are either little-endian or big-endian, and we
+ * always send messages in the sender's native format. The receiver
+ * detects the message format by checking the 'magic' field of the message
+ * (see lustre_msg_swabbed() below).
+ *
+ * Each wire type has corresponding 'lustre_swab_xxxtypexxx()' routines,
+ * implemented either here, inline (trivial implementations) or in
+ * ptlrpc/pack_generic.c. These 'swabbers' convert the type from "other"
+ * endian, in-place in the message buffer.
+ *
+ * A swabber takes a single pointer argument. The caller must already have
+ * verified that the length of the message buffer >= sizeof (type).
+ *
+ * For variable length types, a second 'lustre_swab_v_xxxtypexxx()' routine
+ * may be defined that swabs just the variable part, after the caller has
+ * verified that the message buffer is large enough.
+ *
+ * @{
+ */
+
+#ifndef _LUSTRE_IDL_H_
+#define _LUSTRE_IDL_H_
+
+#include "../../../include/linux/libcfs/libcfs.h"
+
+/* Defn's shared with user-space. */
+#include "lustre_user.h"
+#include "lustre_errno.h"
+
+/*
+ * GENERAL STUFF
+ */
+/* FOO_REQUEST_PORTAL is for incoming requests on the FOO
+ * FOO_REPLY_PORTAL is for incoming replies on the FOO
+ * FOO_BULK_PORTAL is for incoming bulk on the FOO
+ */
+
+/* Lustre service names are following the format
+ * service name + MDT + seq name
+ */
+#define LUSTRE_MDT_MAXNAMELEN 80
+
+#define CONNMGR_REQUEST_PORTAL 1
+#define CONNMGR_REPLY_PORTAL 2
+//#define OSC_REQUEST_PORTAL 3
+#define OSC_REPLY_PORTAL 4
+//#define OSC_BULK_PORTAL 5
+#define OST_IO_PORTAL 6
+#define OST_CREATE_PORTAL 7
+#define OST_BULK_PORTAL 8
+//#define MDC_REQUEST_PORTAL 9
+#define MDC_REPLY_PORTAL 10
+//#define MDC_BULK_PORTAL 11
+#define MDS_REQUEST_PORTAL 12
+//#define MDS_REPLY_PORTAL 13
+#define MDS_BULK_PORTAL 14
+#define LDLM_CB_REQUEST_PORTAL 15
+#define LDLM_CB_REPLY_PORTAL 16
+#define LDLM_CANCEL_REQUEST_PORTAL 17
+#define LDLM_CANCEL_REPLY_PORTAL 18
+//#define PTLBD_REQUEST_PORTAL 19
+//#define PTLBD_REPLY_PORTAL 20
+//#define PTLBD_BULK_PORTAL 21
+#define MDS_SETATTR_PORTAL 22
+#define MDS_READPAGE_PORTAL 23
+#define OUT_PORTAL 24
+
+#define MGC_REPLY_PORTAL 25
+#define MGS_REQUEST_PORTAL 26
+#define MGS_REPLY_PORTAL 27
+#define OST_REQUEST_PORTAL 28
+#define FLD_REQUEST_PORTAL 29
+#define SEQ_METADATA_PORTAL 30
+#define SEQ_DATA_PORTAL 31
+#define SEQ_CONTROLLER_PORTAL 32
+#define MGS_BULK_PORTAL 33
+
+/* Portal 63 is reserved for the Cray Inc DVS - nic@cray.com, roe@cray.com, n8851@cray.com */
+
+/* packet types */
+#define PTL_RPC_MSG_REQUEST 4711
+#define PTL_RPC_MSG_ERR 4712
+#define PTL_RPC_MSG_REPLY 4713
+
+/* DON'T use swabbed values of MAGIC as magic! */
+#define LUSTRE_MSG_MAGIC_V1 0x0BD00BD0
+#define LUSTRE_MSG_MAGIC_V2 0x0BD00BD3
+
+#define LUSTRE_MSG_MAGIC_V1_SWABBED 0xD00BD00B
+#define LUSTRE_MSG_MAGIC_V2_SWABBED 0xD30BD00B
+
+#define LUSTRE_MSG_MAGIC LUSTRE_MSG_MAGIC_V2
+
+#define PTLRPC_MSG_VERSION 0x00000003
+#define LUSTRE_VERSION_MASK 0xffff0000
+#define LUSTRE_OBD_VERSION 0x00010000
+#define LUSTRE_MDS_VERSION 0x00020000
+#define LUSTRE_OST_VERSION 0x00030000
+#define LUSTRE_DLM_VERSION 0x00040000
+#define LUSTRE_LOG_VERSION 0x00050000
+#define LUSTRE_MGS_VERSION 0x00060000
+
+/**
+ * Describes a range of sequence, lsr_start is included but lsr_end is
+ * not in the range.
+ * Same structure is used in fld module where lsr_index field holds mdt id
+ * of the home mdt.
+ */
+struct lu_seq_range {
+ __u64 lsr_start;
+ __u64 lsr_end;
+ __u32 lsr_index;
+ __u32 lsr_flags;
+};
+
+#define LU_SEQ_RANGE_MDT 0x0
+#define LU_SEQ_RANGE_OST 0x1
+#define LU_SEQ_RANGE_ANY 0x3
+
+#define LU_SEQ_RANGE_MASK 0x3
+
+static inline unsigned fld_range_type(const struct lu_seq_range *range)
+{
+ return range->lsr_flags & LU_SEQ_RANGE_MASK;
+}
+
+static inline int fld_range_is_ost(const struct lu_seq_range *range)
+{
+ return fld_range_type(range) == LU_SEQ_RANGE_OST;
+}
+
+static inline int fld_range_is_mdt(const struct lu_seq_range *range)
+{
+ return fld_range_type(range) == LU_SEQ_RANGE_MDT;
+}
+
+/**
+ * This all range is only being used when fld client sends fld query request,
+ * but it does not know whether the seq is MDT or OST, so it will send req
+ * with ALL type, which means either seq type gotten from lookup can be
+ * expected.
+ */
+static inline unsigned fld_range_is_any(const struct lu_seq_range *range)
+{
+ return fld_range_type(range) == LU_SEQ_RANGE_ANY;
+}
+
+static inline void fld_range_set_type(struct lu_seq_range *range,
+ unsigned flags)
+{
+ range->lsr_flags |= flags;
+}
+
+static inline void fld_range_set_mdt(struct lu_seq_range *range)
+{
+ fld_range_set_type(range, LU_SEQ_RANGE_MDT);
+}
+
+static inline void fld_range_set_ost(struct lu_seq_range *range)
+{
+ fld_range_set_type(range, LU_SEQ_RANGE_OST);
+}
+
+static inline void fld_range_set_any(struct lu_seq_range *range)
+{
+ fld_range_set_type(range, LU_SEQ_RANGE_ANY);
+}
+
+/**
+ * returns width of given range \a r
+ */
+
+static inline __u64 range_space(const struct lu_seq_range *range)
+{
+ return range->lsr_end - range->lsr_start;
+}
+
+/**
+ * initialize range to zero
+ */
+
+static inline void range_init(struct lu_seq_range *range)
+{
+ memset(range, 0, sizeof(*range));
+}
+
+/**
+ * check if given seq id \a s is within given range \a r
+ */
+
+static inline int range_within(const struct lu_seq_range *range,
+ __u64 s)
+{
+ return s >= range->lsr_start && s < range->lsr_end;
+}
+
+static inline int range_is_sane(const struct lu_seq_range *range)
+{
+ return (range->lsr_end >= range->lsr_start);
+}
+
+static inline int range_is_zero(const struct lu_seq_range *range)
+{
+ return (range->lsr_start == 0 && range->lsr_end == 0);
+}
+
+static inline int range_is_exhausted(const struct lu_seq_range *range)
+
+{
+ return range_space(range) == 0;
+}
+
+/* return 0 if two range have the same location */
+static inline int range_compare_loc(const struct lu_seq_range *r1,
+ const struct lu_seq_range *r2)
+{
+ return r1->lsr_index != r2->lsr_index ||
+ r1->lsr_flags != r2->lsr_flags;
+}
+
+#define DRANGE "[%#16.16Lx-%#16.16Lx):%x:%s"
+
+#define PRANGE(range) \
+ (range)->lsr_start, \
+ (range)->lsr_end, \
+ (range)->lsr_index, \
+ fld_range_is_mdt(range) ? "mdt" : "ost"
+
+
+/** \defgroup lu_fid lu_fid
+ * @{ */
+
+/**
+ * Flags for lustre_mdt_attrs::lma_compat and lustre_mdt_attrs::lma_incompat.
+ * Deprecated since HSM and SOM attributes are now stored in separate on-disk
+ * xattr.
+ */
+enum lma_compat {
+ LMAC_HSM = 0x00000001,
+ LMAC_SOM = 0x00000002,
+ LMAC_NOT_IN_OI = 0x00000004, /* the object does NOT need OI mapping */
+ LMAC_FID_ON_OST = 0x00000008, /* For OST-object, its OI mapping is
+ * under /O/<seq>/d<x>. */
+};
+
+/**
+ * Masks for all features that should be supported by a Lustre version to
+ * access a specific file.
+ * This information is stored in lustre_mdt_attrs::lma_incompat.
+ */
+enum lma_incompat {
+ LMAI_RELEASED = 0x00000001, /* file is released */
+ LMAI_AGENT = 0x00000002, /* agent inode */
+ LMAI_REMOTE_PARENT = 0x00000004, /* the parent of the object
+ is on the remote MDT */
+};
+#define LMA_INCOMPAT_SUPP (LMAI_AGENT | LMAI_REMOTE_PARENT)
+
+/**
+ * fid constants
+ */
+enum {
+ /** LASTID file has zero OID */
+ LUSTRE_FID_LASTID_OID = 0UL,
+ /** initial fid id value */
+ LUSTRE_FID_INIT_OID = 1UL
+};
+
+/** returns fid object sequence */
+static inline __u64 fid_seq(const struct lu_fid *fid)
+{
+ return fid->f_seq;
+}
+
+/** returns fid object id */
+static inline __u32 fid_oid(const struct lu_fid *fid)
+{
+ return fid->f_oid;
+}
+
+/** returns fid object version */
+static inline __u32 fid_ver(const struct lu_fid *fid)
+{
+ return fid->f_ver;
+}
+
+static inline void fid_zero(struct lu_fid *fid)
+{
+ memset(fid, 0, sizeof(*fid));
+}
+
+static inline __u64 fid_ver_oid(const struct lu_fid *fid)
+{
+ return ((__u64)fid_ver(fid) << 32 | fid_oid(fid));
+}
+
+/**
+ * Note that reserved SEQ numbers below 12 will conflict with ldiskfs
+ * inodes in the IGIF namespace, so these reserved SEQ numbers can be
+ * used for other purposes and not risk collisions with existing inodes.
+ *
+ * Different FID Format
+ * http://arch.lustre.org/index.php?title=Interoperability_fids_zfs#NEW.0
+ */
+enum fid_seq {
+ FID_SEQ_OST_MDT0 = 0,
+ FID_SEQ_LLOG = 1, /* unnamed llogs */
+ FID_SEQ_ECHO = 2,
+ FID_SEQ_OST_MDT1 = 3,
+ FID_SEQ_OST_MAX = 9, /* Max MDT count before OST_on_FID */
+ FID_SEQ_LLOG_NAME = 10, /* named llogs */
+ FID_SEQ_RSVD = 11,
+ FID_SEQ_IGIF = 12,
+ FID_SEQ_IGIF_MAX = 0x0ffffffffULL,
+ FID_SEQ_IDIF = 0x100000000ULL,
+ FID_SEQ_IDIF_MAX = 0x1ffffffffULL,
+ /* Normal FID sequence starts from this value, i.e. 1<<33 */
+ FID_SEQ_START = 0x200000000ULL,
+ /* sequence for local pre-defined FIDs listed in local_oid */
+ FID_SEQ_LOCAL_FILE = 0x200000001ULL,
+ FID_SEQ_DOT_LUSTRE = 0x200000002ULL,
+ /* sequence is used for local named objects FIDs generated
+ * by local_object_storage library */
+ FID_SEQ_LOCAL_NAME = 0x200000003ULL,
+ /* Because current FLD will only cache the fid sequence, instead
+ * of oid on the client side, if the FID needs to be exposed to
+ * clients sides, it needs to make sure all of fids under one
+ * sequence will be located in one MDT. */
+ FID_SEQ_SPECIAL = 0x200000004ULL,
+ FID_SEQ_QUOTA = 0x200000005ULL,
+ FID_SEQ_QUOTA_GLB = 0x200000006ULL,
+ FID_SEQ_ROOT = 0x200000007ULL, /* Located on MDT0 */
+ FID_SEQ_NORMAL = 0x200000400ULL,
+ FID_SEQ_LOV_DEFAULT = 0xffffffffffffffffULL
+};
+
+#define OBIF_OID_MAX_BITS 32
+#define OBIF_MAX_OID (1ULL << OBIF_OID_MAX_BITS)
+#define OBIF_OID_MASK ((1ULL << OBIF_OID_MAX_BITS) - 1)
+#define IDIF_OID_MAX_BITS 48
+#define IDIF_MAX_OID (1ULL << IDIF_OID_MAX_BITS)
+#define IDIF_OID_MASK ((1ULL << IDIF_OID_MAX_BITS) - 1)
+
+/** OID for FID_SEQ_SPECIAL */
+enum special_oid {
+ /* Big Filesystem Lock to serialize rename operations */
+ FID_OID_SPECIAL_BFL = 1UL,
+};
+
+/** OID for FID_SEQ_DOT_LUSTRE */
+enum dot_lustre_oid {
+ FID_OID_DOT_LUSTRE = 1UL,
+ FID_OID_DOT_LUSTRE_OBF = 2UL,
+};
+
+static inline int fid_seq_is_mdt0(__u64 seq)
+{
+ return (seq == FID_SEQ_OST_MDT0);
+}
+
+static inline int fid_seq_is_mdt(const __u64 seq)
+{
+ return seq == FID_SEQ_OST_MDT0 || seq >= FID_SEQ_NORMAL;
+};
+
+static inline int fid_seq_is_echo(__u64 seq)
+{
+ return (seq == FID_SEQ_ECHO);
+}
+
+static inline int fid_is_echo(const struct lu_fid *fid)
+{
+ return fid_seq_is_echo(fid_seq(fid));
+}
+
+static inline int fid_seq_is_llog(__u64 seq)
+{
+ return (seq == FID_SEQ_LLOG);
+}
+
+static inline int fid_is_llog(const struct lu_fid *fid)
+{
+ /* file with OID == 0 is not llog but contains last oid */
+ return fid_seq_is_llog(fid_seq(fid)) && fid_oid(fid) > 0;
+}
+
+static inline int fid_seq_is_rsvd(const __u64 seq)
+{
+ return (seq > FID_SEQ_OST_MDT0 && seq <= FID_SEQ_RSVD);
+};
+
+static inline int fid_seq_is_special(const __u64 seq)
+{
+ return seq == FID_SEQ_SPECIAL;
+};
+
+static inline int fid_seq_is_local_file(const __u64 seq)
+{
+ return seq == FID_SEQ_LOCAL_FILE ||
+ seq == FID_SEQ_LOCAL_NAME;
+};
+
+static inline int fid_seq_is_root(const __u64 seq)
+{
+ return seq == FID_SEQ_ROOT;
+}
+
+static inline int fid_seq_is_dot(const __u64 seq)
+{
+ return seq == FID_SEQ_DOT_LUSTRE;
+}
+
+static inline int fid_seq_is_default(const __u64 seq)
+{
+ return seq == FID_SEQ_LOV_DEFAULT;
+}
+
+static inline int fid_is_mdt0(const struct lu_fid *fid)
+{
+ return fid_seq_is_mdt0(fid_seq(fid));
+}
+
+static inline void lu_root_fid(struct lu_fid *fid)
+{
+ fid->f_seq = FID_SEQ_ROOT;
+ fid->f_oid = 1;
+ fid->f_ver = 0;
+}
+
+/**
+ * Check if a fid is igif or not.
+ * \param fid the fid to be tested.
+ * \return true if the fid is a igif; otherwise false.
+ */
+static inline int fid_seq_is_igif(const __u64 seq)
+{
+ return seq >= FID_SEQ_IGIF && seq <= FID_SEQ_IGIF_MAX;
+}
+
+static inline int fid_is_igif(const struct lu_fid *fid)
+{
+ return fid_seq_is_igif(fid_seq(fid));
+}
+
+/**
+ * Check if a fid is idif or not.
+ * \param fid the fid to be tested.
+ * \return true if the fid is a idif; otherwise false.
+ */
+static inline int fid_seq_is_idif(const __u64 seq)
+{
+ return seq >= FID_SEQ_IDIF && seq <= FID_SEQ_IDIF_MAX;
+}
+
+static inline int fid_is_idif(const struct lu_fid *fid)
+{
+ return fid_seq_is_idif(fid_seq(fid));
+}
+
+static inline int fid_is_local_file(const struct lu_fid *fid)
+{
+ return fid_seq_is_local_file(fid_seq(fid));
+}
+
+static inline int fid_seq_is_norm(const __u64 seq)
+{
+ return (seq >= FID_SEQ_NORMAL);
+}
+
+static inline int fid_is_norm(const struct lu_fid *fid)
+{
+ return fid_seq_is_norm(fid_seq(fid));
+}
+
+/* convert an OST objid into an IDIF FID SEQ number */
+static inline __u64 fid_idif_seq(__u64 id, __u32 ost_idx)
+{
+ return FID_SEQ_IDIF | (ost_idx << 16) | ((id >> 32) & 0xffff);
+}
+
+/* convert a packed IDIF FID into an OST objid */
+static inline __u64 fid_idif_id(__u64 seq, __u32 oid, __u32 ver)
+{
+ return ((__u64)ver << 48) | ((seq & 0xffff) << 32) | oid;
+}
+
+/* extract ost index from IDIF FID */
+static inline __u32 fid_idif_ost_idx(const struct lu_fid *fid)
+{
+ return (fid_seq(fid) >> 16) & 0xffff;
+}
+
+/* extract OST sequence (group) from a wire ost_id (id/seq) pair */
+static inline __u64 ostid_seq(const struct ost_id *ostid)
+{
+ if (fid_seq_is_mdt0(ostid->oi.oi_seq))
+ return FID_SEQ_OST_MDT0;
+
+ if (fid_seq_is_default(ostid->oi.oi_seq))
+ return FID_SEQ_LOV_DEFAULT;
+
+ if (fid_is_idif(&ostid->oi_fid))
+ return FID_SEQ_OST_MDT0;
+
+ return fid_seq(&ostid->oi_fid);
+}
+
+/* extract OST objid from a wire ost_id (id/seq) pair */
+static inline __u64 ostid_id(const struct ost_id *ostid)
+{
+ if (fid_seq_is_mdt0(ostid_seq(ostid)))
+ return ostid->oi.oi_id & IDIF_OID_MASK;
+
+ if (fid_is_idif(&ostid->oi_fid))
+ return fid_idif_id(fid_seq(&ostid->oi_fid),
+ fid_oid(&ostid->oi_fid), 0);
+
+ return fid_oid(&ostid->oi_fid);
+}
+
+static inline void ostid_set_seq(struct ost_id *oi, __u64 seq)
+{
+ if (fid_seq_is_mdt0(seq) || fid_seq_is_default(seq)) {
+ oi->oi.oi_seq = seq;
+ } else {
+ oi->oi_fid.f_seq = seq;
+ /* Note: if f_oid + f_ver is zero, we need init it
+ * to be 1, otherwise, ostid_seq will treat this
+ * as old ostid (oi_seq == 0) */
+ if (oi->oi_fid.f_oid == 0 && oi->oi_fid.f_ver == 0)
+ oi->oi_fid.f_oid = LUSTRE_FID_INIT_OID;
+ }
+}
+
+static inline void ostid_set_seq_mdt0(struct ost_id *oi)
+{
+ ostid_set_seq(oi, FID_SEQ_OST_MDT0);
+}
+
+static inline void ostid_set_seq_echo(struct ost_id *oi)
+{
+ ostid_set_seq(oi, FID_SEQ_ECHO);
+}
+
+static inline void ostid_set_seq_llog(struct ost_id *oi)
+{
+ ostid_set_seq(oi, FID_SEQ_LLOG);
+}
+
+/**
+ * Note: we need check oi_seq to decide where to set oi_id,
+ * so oi_seq should always be set ahead of oi_id.
+ */
+static inline void ostid_set_id(struct ost_id *oi, __u64 oid)
+{
+ if (fid_seq_is_mdt0(ostid_seq(oi))) {
+ if (oid >= IDIF_MAX_OID) {
+ CERROR("Bad %llu to set "DOSTID"\n",
+ oid, POSTID(oi));
+ return;
+ }
+ oi->oi.oi_id = oid;
+ } else {
+ if (oid > OBIF_MAX_OID) {
+ CERROR("Bad %llu to set "DOSTID"\n",
+ oid, POSTID(oi));
+ return;
+ }
+ oi->oi_fid.f_oid = oid;
+ }
+}
+
+static inline void ostid_inc_id(struct ost_id *oi)
+{
+ if (fid_seq_is_mdt0(ostid_seq(oi))) {
+ if (unlikely(ostid_id(oi) + 1 > IDIF_MAX_OID)) {
+ CERROR("Bad inc "DOSTID"\n", POSTID(oi));
+ return;
+ }
+ oi->oi.oi_id++;
+ } else {
+ oi->oi_fid.f_oid++;
+ }
+}
+
+static inline void ostid_dec_id(struct ost_id *oi)
+{
+ if (fid_seq_is_mdt0(ostid_seq(oi)))
+ oi->oi.oi_id--;
+ else
+ oi->oi_fid.f_oid--;
+}
+
+/**
+ * Unpack an OST object id/seq (group) into a FID. This is needed for
+ * converting all obdo, lmm, lsm, etc. 64-bit id/seq pairs into proper
+ * FIDs. Note that if an id/seq is already in FID/IDIF format it will
+ * be passed through unchanged. Only legacy OST objects in "group 0"
+ * will be mapped into the IDIF namespace so that they can fit into the
+ * struct lu_fid fields without loss. For reference see:
+ * http://arch.lustre.org/index.php?title=Interoperability_fids_zfs
+ */
+static inline int ostid_to_fid(struct lu_fid *fid, struct ost_id *ostid,
+ __u32 ost_idx)
+{
+ if (ost_idx > 0xffff) {
+ CERROR("bad ost_idx, "DOSTID" ost_idx:%u\n", POSTID(ostid),
+ ost_idx);
+ return -EBADF;
+ }
+
+ if (fid_seq_is_mdt0(ostid_seq(ostid))) {
+ /* This is a "legacy" (old 1.x/2.early) OST object in "group 0"
+ * that we map into the IDIF namespace. It allows up to 2^48
+ * objects per OST, as this is the object namespace that has
+ * been in production for years. This can handle create rates
+ * of 1M objects/s/OST for 9 years, or combinations thereof. */
+ if (ostid_id(ostid) >= IDIF_MAX_OID) {
+ CERROR("bad MDT0 id, "DOSTID" ost_idx:%u\n",
+ POSTID(ostid), ost_idx);
+ return -EBADF;
+ }
+ fid->f_seq = fid_idif_seq(ostid_id(ostid), ost_idx);
+ /* truncate to 32 bits by assignment */
+ fid->f_oid = ostid_id(ostid);
+ /* in theory, not currently used */
+ fid->f_ver = ostid_id(ostid) >> 48;
+ } else /* if (fid_seq_is_idif(seq) || fid_seq_is_norm(seq)) */ {
+ /* This is either an IDIF object, which identifies objects across
+ * all OSTs, or a regular FID. The IDIF namespace maps legacy
+ * OST objects into the FID namespace. In both cases, we just
+ * pass the FID through, no conversion needed. */
+ if (ostid->oi_fid.f_ver != 0) {
+ CERROR("bad MDT0 id, "DOSTID" ost_idx:%u\n",
+ POSTID(ostid), ost_idx);
+ return -EBADF;
+ }
+ *fid = ostid->oi_fid;
+ }
+
+ return 0;
+}
+
+/* pack any OST FID into an ostid (id/seq) for the wire/disk */
+static inline int fid_to_ostid(const struct lu_fid *fid, struct ost_id *ostid)
+{
+ if (unlikely(fid_seq_is_igif(fid->f_seq))) {
+ CERROR("bad IGIF, "DFID"\n", PFID(fid));
+ return -EBADF;
+ }
+
+ if (fid_is_idif(fid)) {
+ ostid_set_seq_mdt0(ostid);
+ ostid_set_id(ostid, fid_idif_id(fid_seq(fid), fid_oid(fid),
+ fid_ver(fid)));
+ } else {
+ ostid->oi_fid = *fid;
+ }
+
+ return 0;
+}
+
+/* Check whether the fid is for LAST_ID */
+static inline int fid_is_last_id(const struct lu_fid *fid)
+{
+ return (fid_oid(fid) == 0);
+}
+
+/**
+ * Get inode number from a igif.
+ * \param fid a igif to get inode number from.
+ * \return inode number for the igif.
+ */
+static inline ino_t lu_igif_ino(const struct lu_fid *fid)
+{
+ return fid_seq(fid);
+}
+
+extern void lustre_swab_ost_id(struct ost_id *oid);
+
+/**
+ * Get inode generation from a igif.
+ * \param fid a igif to get inode generation from.
+ * \return inode generation for the igif.
+ */
+static inline __u32 lu_igif_gen(const struct lu_fid *fid)
+{
+ return fid_oid(fid);
+}
+
+/**
+ * Build igif from the inode number/generation.
+ */
+static inline void lu_igif_build(struct lu_fid *fid, __u32 ino, __u32 gen)
+{
+ fid->f_seq = ino;
+ fid->f_oid = gen;
+ fid->f_ver = 0;
+}
+
+/*
+ * Fids are transmitted across network (in the sender byte-ordering),
+ * and stored on disk in big-endian order.
+ */
+static inline void fid_cpu_to_le(struct lu_fid *dst, const struct lu_fid *src)
+{
+ dst->f_seq = cpu_to_le64(fid_seq(src));
+ dst->f_oid = cpu_to_le32(fid_oid(src));
+ dst->f_ver = cpu_to_le32(fid_ver(src));
+}
+
+static inline void fid_le_to_cpu(struct lu_fid *dst, const struct lu_fid *src)
+{
+ dst->f_seq = le64_to_cpu(fid_seq(src));
+ dst->f_oid = le32_to_cpu(fid_oid(src));
+ dst->f_ver = le32_to_cpu(fid_ver(src));
+}
+
+static inline void fid_cpu_to_be(struct lu_fid *dst, const struct lu_fid *src)
+{
+ dst->f_seq = cpu_to_be64(fid_seq(src));
+ dst->f_oid = cpu_to_be32(fid_oid(src));
+ dst->f_ver = cpu_to_be32(fid_ver(src));
+}
+
+static inline void fid_be_to_cpu(struct lu_fid *dst, const struct lu_fid *src)
+{
+ dst->f_seq = be64_to_cpu(fid_seq(src));
+ dst->f_oid = be32_to_cpu(fid_oid(src));
+ dst->f_ver = be32_to_cpu(fid_ver(src));
+}
+
+static inline int fid_is_sane(const struct lu_fid *fid)
+{
+ return fid != NULL &&
+ ((fid_seq(fid) >= FID_SEQ_START && fid_ver(fid) == 0) ||
+ fid_is_igif(fid) || fid_is_idif(fid) ||
+ fid_seq_is_rsvd(fid_seq(fid)));
+}
+
+static inline int fid_is_zero(const struct lu_fid *fid)
+{
+ return fid_seq(fid) == 0 && fid_oid(fid) == 0;
+}
+
+extern void lustre_swab_lu_fid(struct lu_fid *fid);
+extern void lustre_swab_lu_seq_range(struct lu_seq_range *range);
+
+static inline int lu_fid_eq(const struct lu_fid *f0, const struct lu_fid *f1)
+{
+ return memcmp(f0, f1, sizeof(*f0)) == 0;
+}
+
+#define __diff_normalize(val0, val1) \
+({ \
+ typeof(val0) __val0 = (val0); \
+ typeof(val1) __val1 = (val1); \
+ \
+ (__val0 == __val1 ? 0 : __val0 > __val1 ? +1 : -1); \
+})
+
+static inline int lu_fid_cmp(const struct lu_fid *f0,
+ const struct lu_fid *f1)
+{
+ return
+ __diff_normalize(fid_seq(f0), fid_seq(f1)) ?:
+ __diff_normalize(fid_oid(f0), fid_oid(f1)) ?:
+ __diff_normalize(fid_ver(f0), fid_ver(f1));
+}
+
+static inline void ostid_cpu_to_le(const struct ost_id *src_oi,
+ struct ost_id *dst_oi)
+{
+ if (fid_seq_is_mdt0(ostid_seq(src_oi))) {
+ dst_oi->oi.oi_id = cpu_to_le64(src_oi->oi.oi_id);
+ dst_oi->oi.oi_seq = cpu_to_le64(src_oi->oi.oi_seq);
+ } else {
+ fid_cpu_to_le(&dst_oi->oi_fid, &src_oi->oi_fid);
+ }
+}
+
+static inline void ostid_le_to_cpu(const struct ost_id *src_oi,
+ struct ost_id *dst_oi)
+{
+ if (fid_seq_is_mdt0(ostid_seq(src_oi))) {
+ dst_oi->oi.oi_id = le64_to_cpu(src_oi->oi.oi_id);
+ dst_oi->oi.oi_seq = le64_to_cpu(src_oi->oi.oi_seq);
+ } else {
+ fid_le_to_cpu(&dst_oi->oi_fid, &src_oi->oi_fid);
+ }
+}
+
+/** @} lu_fid */
+
+/** \defgroup lu_dir lu_dir
+ * @{ */
+
+/**
+ * Enumeration of possible directory entry attributes.
+ *
+ * Attributes follow directory entry header in the order they appear in this
+ * enumeration.
+ */
+enum lu_dirent_attrs {
+ LUDA_FID = 0x0001,
+ LUDA_TYPE = 0x0002,
+ LUDA_64BITHASH = 0x0004,
+
+ /* The following attrs are used for MDT internal only,
+ * not visible to client */
+
+ /* Verify the dirent consistency */
+ LUDA_VERIFY = 0x8000,
+ /* Only check but not repair the dirent inconsistency */
+ LUDA_VERIFY_DRYRUN = 0x4000,
+ /* The dirent has been repaired, or to be repaired (dryrun). */
+ LUDA_REPAIR = 0x2000,
+ /* The system is upgraded, has beed or to be repaired (dryrun). */
+ LUDA_UPGRADE = 0x1000,
+ /* Ignore this record, go to next directly. */
+ LUDA_IGNORE = 0x0800,
+};
+
+#define LU_DIRENT_ATTRS_MASK 0xf800
+
+/**
+ * Layout of readdir pages, as transmitted on wire.
+ */
+struct lu_dirent {
+ /** valid if LUDA_FID is set. */
+ struct lu_fid lde_fid;
+ /** a unique entry identifier: a hash or an offset. */
+ __u64 lde_hash;
+ /** total record length, including all attributes. */
+ __u16 lde_reclen;
+ /** name length */
+ __u16 lde_namelen;
+ /** optional variable size attributes following this entry.
+ * taken from enum lu_dirent_attrs.
+ */
+ __u32 lde_attrs;
+ /** name is followed by the attributes indicated in ->ldp_attrs, in
+ * their natural order. After the last attribute, padding bytes are
+ * added to make ->lde_reclen a multiple of 8.
+ */
+ char lde_name[0];
+};
+
+/*
+ * Definitions of optional directory entry attributes formats.
+ *
+ * Individual attributes do not have their length encoded in a generic way. It
+ * is assumed that consumer of an attribute knows its format. This means that
+ * it is impossible to skip over an unknown attribute, except by skipping over all
+ * remaining attributes (by using ->lde_reclen), which is not too
+ * constraining, because new server versions will append new attributes at
+ * the end of an entry.
+ */
+
+/**
+ * Fid directory attribute: a fid of an object referenced by the entry. This
+ * will be almost always requested by the client and supplied by the server.
+ *
+ * Aligned to 8 bytes.
+ */
+/* To have compatibility with 1.8, lets have fid in lu_dirent struct. */
+
+/**
+ * File type.
+ *
+ * Aligned to 2 bytes.
+ */
+struct luda_type {
+ __u16 lt_type;
+};
+
+#ifndef IFSHIFT
+#define IFSHIFT 12
+#endif
+
+#ifndef IFTODT
+#define IFTODT(type) (((type) & S_IFMT) >> IFSHIFT)
+#endif
+#ifndef DTTOIF
+#define DTTOIF(dirtype) ((dirtype) << IFSHIFT)
+#endif
+
+
+struct lu_dirpage {
+ __u64 ldp_hash_start;
+ __u64 ldp_hash_end;
+ __u32 ldp_flags;
+ __u32 ldp_pad0;
+ struct lu_dirent ldp_entries[0];
+};
+
+enum lu_dirpage_flags {
+ /**
+ * dirpage contains no entry.
+ */
+ LDF_EMPTY = 1 << 0,
+ /**
+ * last entry's lde_hash equals ldp_hash_end.
+ */
+ LDF_COLLIDE = 1 << 1
+};
+
+static inline struct lu_dirent *lu_dirent_start(struct lu_dirpage *dp)
+{
+ if (le32_to_cpu(dp->ldp_flags) & LDF_EMPTY)
+ return NULL;
+ else
+ return dp->ldp_entries;
+}
+
+static inline struct lu_dirent *lu_dirent_next(struct lu_dirent *ent)
+{
+ struct lu_dirent *next;
+
+ if (le16_to_cpu(ent->lde_reclen) != 0)
+ next = ((void *)ent) + le16_to_cpu(ent->lde_reclen);
+ else
+ next = NULL;
+
+ return next;
+}
+
+static inline int lu_dirent_calc_size(int namelen, __u16 attr)
+{
+ int size;
+
+ if (attr & LUDA_TYPE) {
+ const unsigned align = sizeof(struct luda_type) - 1;
+ size = (sizeof(struct lu_dirent) + namelen + align) & ~align;
+ size += sizeof(struct luda_type);
+ } else
+ size = sizeof(struct lu_dirent) + namelen;
+
+ return (size + 7) & ~7;
+}
+
+static inline int lu_dirent_size(struct lu_dirent *ent)
+{
+ if (le16_to_cpu(ent->lde_reclen) == 0) {
+ return lu_dirent_calc_size(le16_to_cpu(ent->lde_namelen),
+ le32_to_cpu(ent->lde_attrs));
+ }
+ return le16_to_cpu(ent->lde_reclen);
+}
+
+#define MDS_DIR_END_OFF 0xfffffffffffffffeULL
+
+/**
+ * MDS_READPAGE page size
+ *
+ * This is the directory page size packed in MDS_READPAGE RPC.
+ * It's different than PAGE_CACHE_SIZE because the client needs to
+ * access the struct lu_dirpage header packed at the beginning of
+ * the "page" and without this there isn't any way to know find the
+ * lu_dirpage header is if client and server PAGE_CACHE_SIZE differ.
+ */
+#define LU_PAGE_SHIFT 12
+#define LU_PAGE_SIZE (1UL << LU_PAGE_SHIFT)
+#define LU_PAGE_MASK (~(LU_PAGE_SIZE - 1))
+
+#define LU_PAGE_COUNT (1 << (PAGE_CACHE_SHIFT - LU_PAGE_SHIFT))
+
+/** @} lu_dir */
+
+struct lustre_handle {
+ __u64 cookie;
+};
+#define DEAD_HANDLE_MAGIC 0xdeadbeefcafebabeULL
+
+static inline int lustre_handle_is_used(struct lustre_handle *lh)
+{
+ return lh->cookie != 0ull;
+}
+
+static inline int lustre_handle_equal(const struct lustre_handle *lh1,
+ const struct lustre_handle *lh2)
+{
+ return lh1->cookie == lh2->cookie;
+}
+
+static inline void lustre_handle_copy(struct lustre_handle *tgt,
+ struct lustre_handle *src)
+{
+ tgt->cookie = src->cookie;
+}
+
+/* flags for lm_flags */
+#define MSGHDR_AT_SUPPORT 0x1
+#define MSGHDR_CKSUM_INCOMPAT18 0x2
+
+#define lustre_msg lustre_msg_v2
+/* we depend on this structure to be 8-byte aligned */
+/* this type is only endian-adjusted in lustre_unpack_msg() */
+struct lustre_msg_v2 {
+ __u32 lm_bufcount;
+ __u32 lm_secflvr;
+ __u32 lm_magic;
+ __u32 lm_repsize;
+ __u32 lm_cksum;
+ __u32 lm_flags;
+ __u32 lm_padding_2;
+ __u32 lm_padding_3;
+ __u32 lm_buflens[0];
+};
+
+/* without gss, ptlrpc_body is put at the first buffer. */
+#define PTLRPC_NUM_VERSIONS 4
+#define JOBSTATS_JOBID_SIZE 32 /* 32 bytes string */
+struct ptlrpc_body_v3 {
+ struct lustre_handle pb_handle;
+ __u32 pb_type;
+ __u32 pb_version;
+ __u32 pb_opc;
+ __u32 pb_status;
+ __u64 pb_last_xid;
+ __u64 pb_last_seen;
+ __u64 pb_last_committed;
+ __u64 pb_transno;
+ __u32 pb_flags;
+ __u32 pb_op_flags;
+ __u32 pb_conn_cnt;
+ __u32 pb_timeout; /* for req, the deadline, for rep, the service est */
+ __u32 pb_service_time; /* for rep, actual service time */
+ __u32 pb_limit;
+ __u64 pb_slv;
+ /* VBR: pre-versions */
+ __u64 pb_pre_versions[PTLRPC_NUM_VERSIONS];
+ /* padding for future needs */
+ __u64 pb_padding[4];
+ char pb_jobid[JOBSTATS_JOBID_SIZE];
+};
+#define ptlrpc_body ptlrpc_body_v3
+
+struct ptlrpc_body_v2 {
+ struct lustre_handle pb_handle;
+ __u32 pb_type;
+ __u32 pb_version;
+ __u32 pb_opc;
+ __u32 pb_status;
+ __u64 pb_last_xid;
+ __u64 pb_last_seen;
+ __u64 pb_last_committed;
+ __u64 pb_transno;
+ __u32 pb_flags;
+ __u32 pb_op_flags;
+ __u32 pb_conn_cnt;
+ __u32 pb_timeout; /* for req, the deadline, for rep, the service est */
+ __u32 pb_service_time; /* for rep, actual service time, also used for
+ net_latency of req */
+ __u32 pb_limit;
+ __u64 pb_slv;
+ /* VBR: pre-versions */
+ __u64 pb_pre_versions[PTLRPC_NUM_VERSIONS];
+ /* padding for future needs */
+ __u64 pb_padding[4];
+};
+
+extern void lustre_swab_ptlrpc_body(struct ptlrpc_body *pb);
+
+/* message body offset for lustre_msg_v2 */
+/* ptlrpc body offset in all request/reply messages */
+#define MSG_PTLRPC_BODY_OFF 0
+
+/* normal request/reply message record offset */
+#define REQ_REC_OFF 1
+#define REPLY_REC_OFF 1
+
+/* ldlm request message body offset */
+#define DLM_LOCKREQ_OFF 1 /* lockreq offset */
+#define DLM_REQ_REC_OFF 2 /* normal dlm request record offset */
+
+/* ldlm intent lock message body offset */
+#define DLM_INTENT_IT_OFF 2 /* intent lock it offset */
+#define DLM_INTENT_REC_OFF 3 /* intent lock record offset */
+
+/* ldlm reply message body offset */
+#define DLM_LOCKREPLY_OFF 1 /* lockrep offset */
+#define DLM_REPLY_REC_OFF 2 /* reply record offset */
+
+/** only use in req->rq_{req,rep}_swab_mask */
+#define MSG_PTLRPC_HEADER_OFF 31
+
+/* Flags that are operation-specific go in the top 16 bits. */
+#define MSG_OP_FLAG_MASK 0xffff0000
+#define MSG_OP_FLAG_SHIFT 16
+
+/* Flags that apply to all requests are in the bottom 16 bits */
+#define MSG_GEN_FLAG_MASK 0x0000ffff
+#define MSG_LAST_REPLAY 0x0001
+#define MSG_RESENT 0x0002
+#define MSG_REPLAY 0x0004
+/* #define MSG_AT_SUPPORT 0x0008
+ * This was used in early prototypes of adaptive timeouts, and while there
+ * shouldn't be any users of that code there also isn't a need for using this
+ * bits. Defer usage until at least 1.10 to avoid potential conflict. */
+#define MSG_DELAY_REPLAY 0x0010
+#define MSG_VERSION_REPLAY 0x0020
+#define MSG_REQ_REPLAY_DONE 0x0040
+#define MSG_LOCK_REPLAY_DONE 0x0080
+
+/*
+ * Flags for all connect opcodes (MDS_CONNECT, OST_CONNECT)
+ */
+
+#define MSG_CONNECT_RECOVERING 0x00000001
+#define MSG_CONNECT_RECONNECT 0x00000002
+#define MSG_CONNECT_REPLAYABLE 0x00000004
+//#define MSG_CONNECT_PEER 0x8
+#define MSG_CONNECT_LIBCLIENT 0x00000010
+#define MSG_CONNECT_INITIAL 0x00000020
+#define MSG_CONNECT_ASYNC 0x00000040
+#define MSG_CONNECT_NEXT_VER 0x00000080 /* use next version of lustre_msg */
+#define MSG_CONNECT_TRANSNO 0x00000100 /* report transno */
+
+/* Connect flags */
+#define OBD_CONNECT_RDONLY 0x1ULL /*client has read-only access*/
+#define OBD_CONNECT_INDEX 0x2ULL /*connect specific LOV idx */
+#define OBD_CONNECT_MDS 0x4ULL /*connect from MDT to OST */
+#define OBD_CONNECT_GRANT 0x8ULL /*OSC gets grant at connect */
+#define OBD_CONNECT_SRVLOCK 0x10ULL /*server takes locks for cli */
+#define OBD_CONNECT_VERSION 0x20ULL /*Lustre versions in ocd */
+#define OBD_CONNECT_REQPORTAL 0x40ULL /*Separate non-IO req portal */
+#define OBD_CONNECT_ACL 0x80ULL /*access control lists */
+#define OBD_CONNECT_XATTR 0x100ULL /*client use extended attr */
+#define OBD_CONNECT_CROW 0x200ULL /*MDS+OST create obj on write*/
+#define OBD_CONNECT_TRUNCLOCK 0x400ULL /*locks on server for punch */
+#define OBD_CONNECT_TRANSNO 0x800ULL /*replay sends init transno */
+#define OBD_CONNECT_IBITS 0x1000ULL /*support for inodebits locks*/
+#define OBD_CONNECT_JOIN 0x2000ULL /*files can be concatenated.
+ *We do not support JOIN FILE
+ *anymore, reserve this flags
+ *just for preventing such bit
+ *to be reused.*/
+#define OBD_CONNECT_ATTRFID 0x4000ULL /*Server can GetAttr By Fid*/
+#define OBD_CONNECT_NODEVOH 0x8000ULL /*No open hndl on specl nodes*/
+#define OBD_CONNECT_RMT_CLIENT 0x10000ULL /*Remote client */
+#define OBD_CONNECT_RMT_CLIENT_FORCE 0x20000ULL /*Remote client by force */
+#define OBD_CONNECT_BRW_SIZE 0x40000ULL /*Max bytes per rpc */
+#define OBD_CONNECT_QUOTA64 0x80000ULL /*Not used since 2.4 */
+#define OBD_CONNECT_MDS_CAPA 0x100000ULL /*MDS capability */
+#define OBD_CONNECT_OSS_CAPA 0x200000ULL /*OSS capability */
+#define OBD_CONNECT_CANCELSET 0x400000ULL /*Early batched cancels. */
+#define OBD_CONNECT_SOM 0x800000ULL /*Size on MDS */
+#define OBD_CONNECT_AT 0x1000000ULL /*client uses AT */
+#define OBD_CONNECT_LRU_RESIZE 0x2000000ULL /*LRU resize feature. */
+#define OBD_CONNECT_MDS_MDS 0x4000000ULL /*MDS-MDS connection */
+#define OBD_CONNECT_REAL 0x8000000ULL /*real connection */
+#define OBD_CONNECT_CHANGE_QS 0x10000000ULL /*Not used since 2.4 */
+#define OBD_CONNECT_CKSUM 0x20000000ULL /*support several cksum algos*/
+#define OBD_CONNECT_FID 0x40000000ULL /*FID is supported by server */
+#define OBD_CONNECT_VBR 0x80000000ULL /*version based recovery */
+#define OBD_CONNECT_LOV_V3 0x100000000ULL /*client supports LOV v3 EA */
+#define OBD_CONNECT_GRANT_SHRINK 0x200000000ULL /* support grant shrink */
+#define OBD_CONNECT_SKIP_ORPHAN 0x400000000ULL /* don't reuse orphan objids */
+#define OBD_CONNECT_MAX_EASIZE 0x800000000ULL /* preserved for large EA */
+#define OBD_CONNECT_FULL20 0x1000000000ULL /* it is 2.0 client */
+#define OBD_CONNECT_LAYOUTLOCK 0x2000000000ULL /* client uses layout lock */
+#define OBD_CONNECT_64BITHASH 0x4000000000ULL /* client supports 64-bits
+ * directory hash */
+#define OBD_CONNECT_MAXBYTES 0x8000000000ULL /* max stripe size */
+#define OBD_CONNECT_IMP_RECOV 0x10000000000ULL /* imp recovery support */
+#define OBD_CONNECT_JOBSTATS 0x20000000000ULL /* jobid in ptlrpc_body */
+#define OBD_CONNECT_UMASK 0x40000000000ULL /* create uses client umask */
+#define OBD_CONNECT_EINPROGRESS 0x80000000000ULL /* client handles -EINPROGRESS
+ * RPC error properly */
+#define OBD_CONNECT_GRANT_PARAM 0x100000000000ULL/* extra grant params used for
+ * finer space reservation */
+#define OBD_CONNECT_FLOCK_OWNER 0x200000000000ULL /* for the fixed 1.8
+ * policy and 2.x server */
+#define OBD_CONNECT_LVB_TYPE 0x400000000000ULL /* variable type of LVB */
+#define OBD_CONNECT_NANOSEC_TIME 0x800000000000ULL /* nanosecond timestamps */
+#define OBD_CONNECT_LIGHTWEIGHT 0x1000000000000ULL/* lightweight connection */
+#define OBD_CONNECT_SHORTIO 0x2000000000000ULL/* short io */
+#define OBD_CONNECT_PINGLESS 0x4000000000000ULL/* pings not required */
+#define OBD_CONNECT_FLOCK_DEAD 0x8000000000000ULL/* flock deadlock detection */
+#define OBD_CONNECT_DISP_STRIPE 0x10000000000000ULL/*create stripe disposition*/
+
+/* XXX README XXX:
+ * Please DO NOT add flag values here before first ensuring that this same
+ * flag value is not in use on some other branch. Please clear any such
+ * changes with senior engineers before starting to use a new flag. Then,
+ * submit a small patch against EVERY branch that ONLY adds the new flag,
+ * updates obd_connect_names[] for lprocfs_rd_connect_flags(), adds the
+ * flag to check_obd_connect_data(), and updates wiretests accordingly, so it
+ * can be approved and landed easily to reserve the flag for future use. */
+
+/* The MNE_SWAB flag is overloading the MDS_MDS bit only for the MGS
+ * connection. It is a temporary bug fix for Imperative Recovery interop
+ * between 2.2 and 2.3 x86/ppc nodes, and can be removed when interop for
+ * 2.2 clients/servers is no longer needed. LU-1252/LU-1644. */
+#define OBD_CONNECT_MNE_SWAB OBD_CONNECT_MDS_MDS
+
+#define OCD_HAS_FLAG(ocd, flg) \
+ (!!((ocd)->ocd_connect_flags & OBD_CONNECT_##flg))
+
+
+#define LRU_RESIZE_CONNECT_FLAG OBD_CONNECT_LRU_RESIZE
+
+#define MDT_CONNECT_SUPPORTED (OBD_CONNECT_RDONLY | OBD_CONNECT_VERSION | \
+ OBD_CONNECT_ACL | OBD_CONNECT_XATTR | \
+ OBD_CONNECT_IBITS | \
+ OBD_CONNECT_NODEVOH | OBD_CONNECT_ATTRFID | \
+ OBD_CONNECT_CANCELSET | OBD_CONNECT_AT | \
+ OBD_CONNECT_RMT_CLIENT | \
+ OBD_CONNECT_RMT_CLIENT_FORCE | \
+ OBD_CONNECT_BRW_SIZE | OBD_CONNECT_MDS_CAPA | \
+ OBD_CONNECT_OSS_CAPA | OBD_CONNECT_MDS_MDS | \
+ OBD_CONNECT_FID | LRU_RESIZE_CONNECT_FLAG | \
+ OBD_CONNECT_VBR | OBD_CONNECT_LOV_V3 | \
+ OBD_CONNECT_SOM | OBD_CONNECT_FULL20 | \
+ OBD_CONNECT_64BITHASH | OBD_CONNECT_JOBSTATS | \
+ OBD_CONNECT_EINPROGRESS | \
+ OBD_CONNECT_LIGHTWEIGHT | OBD_CONNECT_UMASK | \
+ OBD_CONNECT_LVB_TYPE | OBD_CONNECT_LAYOUTLOCK |\
+ OBD_CONNECT_PINGLESS | OBD_CONNECT_MAX_EASIZE |\
+ OBD_CONNECT_FLOCK_DEAD | \
+ OBD_CONNECT_DISP_STRIPE)
+
+#define OST_CONNECT_SUPPORTED (OBD_CONNECT_SRVLOCK | OBD_CONNECT_GRANT | \
+ OBD_CONNECT_REQPORTAL | OBD_CONNECT_VERSION | \
+ OBD_CONNECT_TRUNCLOCK | OBD_CONNECT_INDEX | \
+ OBD_CONNECT_BRW_SIZE | OBD_CONNECT_OSS_CAPA | \
+ OBD_CONNECT_CANCELSET | OBD_CONNECT_AT | \
+ LRU_RESIZE_CONNECT_FLAG | OBD_CONNECT_CKSUM | \
+ OBD_CONNECT_RMT_CLIENT | \
+ OBD_CONNECT_RMT_CLIENT_FORCE | OBD_CONNECT_VBR | \
+ OBD_CONNECT_MDS | OBD_CONNECT_SKIP_ORPHAN | \
+ OBD_CONNECT_GRANT_SHRINK | OBD_CONNECT_FULL20 | \
+ OBD_CONNECT_64BITHASH | OBD_CONNECT_MAXBYTES | \
+ OBD_CONNECT_MAX_EASIZE | \
+ OBD_CONNECT_EINPROGRESS | \
+ OBD_CONNECT_JOBSTATS | \
+ OBD_CONNECT_LIGHTWEIGHT | OBD_CONNECT_LVB_TYPE|\
+ OBD_CONNECT_LAYOUTLOCK | OBD_CONNECT_FID | \
+ OBD_CONNECT_PINGLESS)
+#define ECHO_CONNECT_SUPPORTED (0)
+#define MGS_CONNECT_SUPPORTED (OBD_CONNECT_VERSION | OBD_CONNECT_AT | \
+ OBD_CONNECT_FULL20 | OBD_CONNECT_IMP_RECOV | \
+ OBD_CONNECT_MNE_SWAB | OBD_CONNECT_PINGLESS)
+
+/* Features required for this version of the client to work with server */
+#define CLIENT_CONNECT_MDT_REQD (OBD_CONNECT_IBITS | OBD_CONNECT_FID | \
+ OBD_CONNECT_FULL20)
+
+#define OBD_OCD_VERSION(major, minor, patch, fix) (((major)<<24) + \
+ ((minor)<<16) + \
+ ((patch)<<8) + (fix))
+#define OBD_OCD_VERSION_MAJOR(version) ((int)((version)>>24)&255)
+#define OBD_OCD_VERSION_MINOR(version) ((int)((version)>>16)&255)
+#define OBD_OCD_VERSION_PATCH(version) ((int)((version)>>8)&255)
+#define OBD_OCD_VERSION_FIX(version) ((int)(version)&255)
+
+/* This structure is used for both request and reply.
+ *
+ * If we eventually have separate connect data for different types, which we
+ * almost certainly will, then perhaps we stick a union in here. */
+struct obd_connect_data_v1 {
+ __u64 ocd_connect_flags; /* OBD_CONNECT_* per above */
+ __u32 ocd_version; /* lustre release version number */
+ __u32 ocd_grant; /* initial cache grant amount (bytes) */
+ __u32 ocd_index; /* LOV index to connect to */
+ __u32 ocd_brw_size; /* Maximum BRW size in bytes, must be 2^n */
+ __u64 ocd_ibits_known; /* inode bits this client understands */
+ __u8 ocd_blocksize; /* log2 of the backend filesystem blocksize */
+ __u8 ocd_inodespace; /* log2 of the per-inode space consumption */
+ __u16 ocd_grant_extent; /* per-extent grant overhead, in 1K blocks */
+ __u32 ocd_unused; /* also fix lustre_swab_connect */
+ __u64 ocd_transno; /* first transno from client to be replayed */
+ __u32 ocd_group; /* MDS group on OST */
+ __u32 ocd_cksum_types; /* supported checksum algorithms */
+ __u32 ocd_max_easize; /* How big LOV EA can be on MDS */
+ __u32 ocd_instance; /* also fix lustre_swab_connect */
+ __u64 ocd_maxbytes; /* Maximum stripe size in bytes */
+};
+
+struct obd_connect_data {
+ __u64 ocd_connect_flags; /* OBD_CONNECT_* per above */
+ __u32 ocd_version; /* lustre release version number */
+ __u32 ocd_grant; /* initial cache grant amount (bytes) */
+ __u32 ocd_index; /* LOV index to connect to */
+ __u32 ocd_brw_size; /* Maximum BRW size in bytes */
+ __u64 ocd_ibits_known; /* inode bits this client understands */
+ __u8 ocd_blocksize; /* log2 of the backend filesystem blocksize */
+ __u8 ocd_inodespace; /* log2 of the per-inode space consumption */
+ __u16 ocd_grant_extent; /* per-extent grant overhead, in 1K blocks */
+ __u32 ocd_unused; /* also fix lustre_swab_connect */
+ __u64 ocd_transno; /* first transno from client to be replayed */
+ __u32 ocd_group; /* MDS group on OST */
+ __u32 ocd_cksum_types; /* supported checksum algorithms */
+ __u32 ocd_max_easize; /* How big LOV EA can be on MDS */
+ __u32 ocd_instance; /* instance # of this target */
+ __u64 ocd_maxbytes; /* Maximum stripe size in bytes */
+ /* Fields after ocd_maxbytes are only accessible by the receiver
+ * if the corresponding flag in ocd_connect_flags is set. Accessing
+ * any field after ocd_maxbytes on the receiver without a valid flag
+ * may result in out-of-bound memory access and kernel oops. */
+ __u64 padding1; /* added 2.1.0. also fix lustre_swab_connect */
+ __u64 padding2; /* added 2.1.0. also fix lustre_swab_connect */
+ __u64 padding3; /* added 2.1.0. also fix lustre_swab_connect */
+ __u64 padding4; /* added 2.1.0. also fix lustre_swab_connect */
+ __u64 padding5; /* added 2.1.0. also fix lustre_swab_connect */
+ __u64 padding6; /* added 2.1.0. also fix lustre_swab_connect */
+ __u64 padding7; /* added 2.1.0. also fix lustre_swab_connect */
+ __u64 padding8; /* added 2.1.0. also fix lustre_swab_connect */
+ __u64 padding9; /* added 2.1.0. also fix lustre_swab_connect */
+ __u64 paddingA; /* added 2.1.0. also fix lustre_swab_connect */
+ __u64 paddingB; /* added 2.1.0. also fix lustre_swab_connect */
+ __u64 paddingC; /* added 2.1.0. also fix lustre_swab_connect */
+ __u64 paddingD; /* added 2.1.0. also fix lustre_swab_connect */
+ __u64 paddingE; /* added 2.1.0. also fix lustre_swab_connect */
+ __u64 paddingF; /* added 2.1.0. also fix lustre_swab_connect */
+};
+/* XXX README XXX:
+ * Please DO NOT use any fields here before first ensuring that this same
+ * field is not in use on some other branch. Please clear any such changes
+ * with senior engineers before starting to use a new field. Then, submit
+ * a small patch against EVERY branch that ONLY adds the new field along with
+ * the matching OBD_CONNECT flag, so that can be approved and landed easily to
+ * reserve the flag for future use. */
+
+
+extern void lustre_swab_connect(struct obd_connect_data *ocd);
+
+/*
+ * Supported checksum algorithms. Up to 32 checksum types are supported.
+ * (32-bit mask stored in obd_connect_data::ocd_cksum_types)
+ * Please update DECLARE_CKSUM_NAME/OBD_CKSUM_ALL in obd.h when adding a new
+ * algorithm and also the OBD_FL_CKSUM* flags.
+ */
+typedef enum {
+ OBD_CKSUM_CRC32 = 0x00000001,
+ OBD_CKSUM_ADLER = 0x00000002,
+ OBD_CKSUM_CRC32C= 0x00000004,
+} cksum_type_t;
+
+/*
+ * OST requests: OBDO & OBD request records
+ */
+
+/* opcodes */
+typedef enum {
+ OST_REPLY = 0, /* reply ? */
+ OST_GETATTR = 1,
+ OST_SETATTR = 2,
+ OST_READ = 3,
+ OST_WRITE = 4,
+ OST_CREATE = 5,
+ OST_DESTROY = 6,
+ OST_GET_INFO = 7,
+ OST_CONNECT = 8,
+ OST_DISCONNECT = 9,
+ OST_PUNCH = 10,
+ OST_OPEN = 11,
+ OST_CLOSE = 12,
+ OST_STATFS = 13,
+ OST_SYNC = 16,
+ OST_SET_INFO = 17,
+ OST_QUOTACHECK = 18,
+ OST_QUOTACTL = 19,
+ OST_QUOTA_ADJUST_QUNIT = 20, /* not used since 2.4 */
+ OST_LAST_OPC
+} ost_cmd_t;
+#define OST_FIRST_OPC OST_REPLY
+
+enum obdo_flags {
+ OBD_FL_INLINEDATA = 0x00000001,
+ OBD_FL_OBDMDEXISTS = 0x00000002,
+ OBD_FL_DELORPHAN = 0x00000004, /* if set in o_flags delete orphans */
+ OBD_FL_NORPC = 0x00000008, /* set in o_flags do in OSC not OST */
+ OBD_FL_IDONLY = 0x00000010, /* set in o_flags only adjust obj id*/
+ OBD_FL_RECREATE_OBJS= 0x00000020, /* recreate missing obj */
+ OBD_FL_DEBUG_CHECK = 0x00000040, /* echo client/server debug check */
+ OBD_FL_NO_USRQUOTA = 0x00000100, /* the object's owner is over quota */
+ OBD_FL_NO_GRPQUOTA = 0x00000200, /* the object's group is over quota */
+ OBD_FL_CREATE_CROW = 0x00000400, /* object should be create on write */
+ OBD_FL_SRVLOCK = 0x00000800, /* delegate DLM locking to server */
+ OBD_FL_CKSUM_CRC32 = 0x00001000, /* CRC32 checksum type */
+ OBD_FL_CKSUM_ADLER = 0x00002000, /* ADLER checksum type */
+ OBD_FL_CKSUM_CRC32C = 0x00004000, /* CRC32C checksum type */
+ OBD_FL_CKSUM_RSVD2 = 0x00008000, /* for future cksum types */
+ OBD_FL_CKSUM_RSVD3 = 0x00010000, /* for future cksum types */
+ OBD_FL_SHRINK_GRANT = 0x00020000, /* object shrink the grant */
+ OBD_FL_MMAP = 0x00040000, /* object is mmapped on the client.
+ * XXX: obsoleted - reserved for old
+ * clients prior than 2.2 */
+ OBD_FL_RECOV_RESEND = 0x00080000, /* recoverable resent */
+ OBD_FL_NOSPC_BLK = 0x00100000, /* no more block space on OST */
+
+ /* Note that while these checksum values are currently separate bits,
+ * in 2.x we can actually allow all values from 1-31 if we wanted. */
+ OBD_FL_CKSUM_ALL = OBD_FL_CKSUM_CRC32 | OBD_FL_CKSUM_ADLER |
+ OBD_FL_CKSUM_CRC32C,
+
+ /* mask for local-only flag, which won't be sent over network */
+ OBD_FL_LOCAL_MASK = 0xF0000000,
+};
+
+#define LOV_MAGIC_V1 0x0BD10BD0
+#define LOV_MAGIC LOV_MAGIC_V1
+#define LOV_MAGIC_JOIN_V1 0x0BD20BD0
+#define LOV_MAGIC_V3 0x0BD30BD0
+
+/*
+ * magic for fully defined striping
+ * the idea is that we should have different magics for striping "hints"
+ * (struct lov_user_md_v[13]) and defined ready-to-use striping (struct
+ * lov_mds_md_v[13]). at the moment the magics are used in wire protocol,
+ * we can't just change it w/o long way preparation, but we still need a
+ * mechanism to allow LOD to differentiate hint versus ready striping.
+ * so, at the moment we do a trick: MDT knows what to expect from request
+ * depending on the case (replay uses ready striping, non-replay req uses
+ * hints), so MDT replaces magic with appropriate one and now LOD can
+ * easily understand what's inside -bzzz
+ */
+#define LOV_MAGIC_V1_DEF 0x0CD10BD0
+#define LOV_MAGIC_V3_DEF 0x0CD30BD0
+
+#define LOV_PATTERN_RAID0 0x001 /* stripes are used round-robin */
+#define LOV_PATTERN_RAID1 0x002 /* stripes are mirrors of each other */
+#define LOV_PATTERN_FIRST 0x100 /* first stripe is not in round-robin */
+#define LOV_PATTERN_CMOBD 0x200
+
+#define LOV_PATTERN_F_MASK 0xffff0000
+#define LOV_PATTERN_F_RELEASED 0x80000000 /* HSM released file */
+
+#define lov_pattern(pattern) (pattern & ~LOV_PATTERN_F_MASK)
+#define lov_pattern_flags(pattern) (pattern & LOV_PATTERN_F_MASK)
+
+#define lov_ost_data lov_ost_data_v1
+struct lov_ost_data_v1 { /* per-stripe data structure (little-endian)*/
+ struct ost_id l_ost_oi; /* OST object ID */
+ __u32 l_ost_gen; /* generation of this l_ost_idx */
+ __u32 l_ost_idx; /* OST index in LOV (lov_tgt_desc->tgts) */
+};
+
+#define lov_mds_md lov_mds_md_v1
+struct lov_mds_md_v1 { /* LOV EA mds/wire data (little-endian) */
+ __u32 lmm_magic; /* magic number = LOV_MAGIC_V1 */
+ __u32 lmm_pattern; /* LOV_PATTERN_RAID0, LOV_PATTERN_RAID1 */
+ struct ost_id lmm_oi; /* LOV object ID */
+ __u32 lmm_stripe_size; /* size of stripe in bytes */
+ /* lmm_stripe_count used to be __u32 */
+ __u16 lmm_stripe_count; /* num stripes in use for this object */
+ __u16 lmm_layout_gen; /* layout generation number */
+ struct lov_ost_data_v1 lmm_objects[0]; /* per-stripe data */
+};
+
+/**
+ * Sigh, because pre-2.4 uses
+ * struct lov_mds_md_v1 {
+ * ........
+ * __u64 lmm_object_id;
+ * __u64 lmm_object_seq;
+ * ......
+ * }
+ * to identify the LOV(MDT) object, and lmm_object_seq will
+ * be normal_fid, which make it hard to combine these conversion
+ * to ostid_to FID. so we will do lmm_oi/fid conversion separately
+ *
+ * We can tell the lmm_oi by this way,
+ * 1.8: lmm_object_id = {inode}, lmm_object_gr = 0
+ * 2.1: lmm_object_id = {oid < 128k}, lmm_object_seq = FID_SEQ_NORMAL
+ * 2.4: lmm_oi.f_seq = FID_SEQ_NORMAL, lmm_oi.f_oid = {oid < 128k},
+ * lmm_oi.f_ver = 0
+ *
+ * But currently lmm_oi/lsm_oi does not have any "real" usages,
+ * except for printing some information, and the user can always
+ * get the real FID from LMA, besides this multiple case check might
+ * make swab more complicate. So we will keep using id/seq for lmm_oi.
+ */
+
+static inline void fid_to_lmm_oi(const struct lu_fid *fid,
+ struct ost_id *oi)
+{
+ oi->oi.oi_id = fid_oid(fid);
+ oi->oi.oi_seq = fid_seq(fid);
+}
+
+static inline void lmm_oi_set_seq(struct ost_id *oi, __u64 seq)
+{
+ oi->oi.oi_seq = seq;
+}
+
+static inline __u64 lmm_oi_id(struct ost_id *oi)
+{
+ return oi->oi.oi_id;
+}
+
+static inline __u64 lmm_oi_seq(struct ost_id *oi)
+{
+ return oi->oi.oi_seq;
+}
+
+static inline void lmm_oi_le_to_cpu(struct ost_id *dst_oi,
+ struct ost_id *src_oi)
+{
+ dst_oi->oi.oi_id = le64_to_cpu(src_oi->oi.oi_id);
+ dst_oi->oi.oi_seq = le64_to_cpu(src_oi->oi.oi_seq);
+}
+
+static inline void lmm_oi_cpu_to_le(struct ost_id *dst_oi,
+ struct ost_id *src_oi)
+{
+ dst_oi->oi.oi_id = cpu_to_le64(src_oi->oi.oi_id);
+ dst_oi->oi.oi_seq = cpu_to_le64(src_oi->oi.oi_seq);
+}
+
+/* extern void lustre_swab_lov_mds_md(struct lov_mds_md *llm); */
+
+#define MAX_MD_SIZE \
+ (sizeof(struct lov_mds_md) + 4 * sizeof(struct lov_ost_data))
+#define MIN_MD_SIZE \
+ (sizeof(struct lov_mds_md) + 1 * sizeof(struct lov_ost_data))
+
+#define XATTR_NAME_ACL_ACCESS "system.posix_acl_access"
+#define XATTR_NAME_ACL_DEFAULT "system.posix_acl_default"
+#define XATTR_USER_PREFIX "user."
+#define XATTR_TRUSTED_PREFIX "trusted."
+#define XATTR_SECURITY_PREFIX "security."
+#define XATTR_LUSTRE_PREFIX "lustre."
+
+#define XATTR_NAME_LOV "trusted.lov"
+#define XATTR_NAME_LMA "trusted.lma"
+#define XATTR_NAME_LMV "trusted.lmv"
+#define XATTR_NAME_LINK "trusted.link"
+#define XATTR_NAME_FID "trusted.fid"
+#define XATTR_NAME_VERSION "trusted.version"
+#define XATTR_NAME_SOM "trusted.som"
+#define XATTR_NAME_HSM "trusted.hsm"
+#define XATTR_NAME_LFSCK_NAMESPACE "trusted.lfsck_namespace"
+
+struct lov_mds_md_v3 { /* LOV EA mds/wire data (little-endian) */
+ __u32 lmm_magic; /* magic number = LOV_MAGIC_V3 */
+ __u32 lmm_pattern; /* LOV_PATTERN_RAID0, LOV_PATTERN_RAID1 */
+ struct ost_id lmm_oi; /* LOV object ID */
+ __u32 lmm_stripe_size; /* size of stripe in bytes */
+ /* lmm_stripe_count used to be __u32 */
+ __u16 lmm_stripe_count; /* num stripes in use for this object */
+ __u16 lmm_layout_gen; /* layout generation number */
+ char lmm_pool_name[LOV_MAXPOOLNAME]; /* must be 32bit aligned */
+ struct lov_ost_data_v1 lmm_objects[0]; /* per-stripe data */
+};
+
+static inline __u32 lov_mds_md_size(__u16 stripes, __u32 lmm_magic)
+{
+ if (lmm_magic == LOV_MAGIC_V3)
+ return sizeof(struct lov_mds_md_v3) +
+ stripes * sizeof(struct lov_ost_data_v1);
+ else
+ return sizeof(struct lov_mds_md_v1) +
+ stripes * sizeof(struct lov_ost_data_v1);
+}
+
+static inline __u32
+lov_mds_md_max_stripe_count(size_t buf_size, __u32 lmm_magic)
+{
+ switch (lmm_magic) {
+ case LOV_MAGIC_V1: {
+ struct lov_mds_md_v1 lmm;
+
+ if (buf_size < sizeof(lmm))
+ return 0;
+
+ return (buf_size - sizeof(lmm)) / sizeof(lmm.lmm_objects[0]);
+ }
+ case LOV_MAGIC_V3: {
+ struct lov_mds_md_v3 lmm;
+
+ if (buf_size < sizeof(lmm))
+ return 0;
+
+ return (buf_size - sizeof(lmm)) / sizeof(lmm.lmm_objects[0]);
+ }
+ default:
+ return 0;
+ }
+}
+
+#define OBD_MD_FLID (0x00000001ULL) /* object ID */
+#define OBD_MD_FLATIME (0x00000002ULL) /* access time */
+#define OBD_MD_FLMTIME (0x00000004ULL) /* data modification time */
+#define OBD_MD_FLCTIME (0x00000008ULL) /* change time */
+#define OBD_MD_FLSIZE (0x00000010ULL) /* size */
+#define OBD_MD_FLBLOCKS (0x00000020ULL) /* allocated blocks count */
+#define OBD_MD_FLBLKSZ (0x00000040ULL) /* block size */
+#define OBD_MD_FLMODE (0x00000080ULL) /* access bits (mode & ~S_IFMT) */
+#define OBD_MD_FLTYPE (0x00000100ULL) /* object type (mode & S_IFMT) */
+#define OBD_MD_FLUID (0x00000200ULL) /* user ID */
+#define OBD_MD_FLGID (0x00000400ULL) /* group ID */
+#define OBD_MD_FLFLAGS (0x00000800ULL) /* flags word */
+#define OBD_MD_FLNLINK (0x00002000ULL) /* link count */
+#define OBD_MD_FLGENER (0x00004000ULL) /* generation number */
+/*#define OBD_MD_FLINLINE (0x00008000ULL) inline data. used until 1.6.5 */
+#define OBD_MD_FLRDEV (0x00010000ULL) /* device number */
+#define OBD_MD_FLEASIZE (0x00020000ULL) /* extended attribute data */
+#define OBD_MD_LINKNAME (0x00040000ULL) /* symbolic link target */
+#define OBD_MD_FLHANDLE (0x00080000ULL) /* file/lock handle */
+#define OBD_MD_FLCKSUM (0x00100000ULL) /* bulk data checksum */
+#define OBD_MD_FLQOS (0x00200000ULL) /* quality of service stats */
+/*#define OBD_MD_FLOSCOPQ (0x00400000ULL) osc opaque data, never used */
+#define OBD_MD_FLCOOKIE (0x00800000ULL) /* log cancellation cookie */
+#define OBD_MD_FLGROUP (0x01000000ULL) /* group */
+#define OBD_MD_FLFID (0x02000000ULL) /* ->ost write inline fid */
+#define OBD_MD_FLEPOCH (0x04000000ULL) /* ->ost write with ioepoch */
+ /* ->mds if epoch opens or closes */
+#define OBD_MD_FLGRANT (0x08000000ULL) /* ost preallocation space grant */
+#define OBD_MD_FLDIREA (0x10000000ULL) /* dir's extended attribute data */
+#define OBD_MD_FLUSRQUOTA (0x20000000ULL) /* over quota flags sent from ost */
+#define OBD_MD_FLGRPQUOTA (0x40000000ULL) /* over quota flags sent from ost */
+#define OBD_MD_FLMODEASIZE (0x80000000ULL) /* EA size will be changed */
+
+#define OBD_MD_MDS (0x0000000100000000ULL) /* where an inode lives on */
+#define OBD_MD_REINT (0x0000000200000000ULL) /* reintegrate oa */
+#define OBD_MD_MEA (0x0000000400000000ULL) /* CMD split EA */
+#define OBD_MD_TSTATE (0x0000000800000000ULL) /* transient state field */
+
+#define OBD_MD_FLXATTR (0x0000001000000000ULL) /* xattr */
+#define OBD_MD_FLXATTRLS (0x0000002000000000ULL) /* xattr list */
+#define OBD_MD_FLXATTRRM (0x0000004000000000ULL) /* xattr remove */
+#define OBD_MD_FLACL (0x0000008000000000ULL) /* ACL */
+#define OBD_MD_FLRMTPERM (0x0000010000000000ULL) /* remote permission */
+#define OBD_MD_FLMDSCAPA (0x0000020000000000ULL) /* MDS capability */
+#define OBD_MD_FLOSSCAPA (0x0000040000000000ULL) /* OSS capability */
+#define OBD_MD_FLCKSPLIT (0x0000080000000000ULL) /* Check split on server */
+#define OBD_MD_FLCROSSREF (0x0000100000000000ULL) /* Cross-ref case */
+#define OBD_MD_FLGETATTRLOCK (0x0000200000000000ULL) /* Get IOEpoch attributes
+ * under lock; for xattr
+ * requests means the
+ * client holds the lock */
+#define OBD_MD_FLOBJCOUNT (0x0000400000000000ULL) /* for multiple destroy */
+
+#define OBD_MD_FLRMTLSETFACL (0x0001000000000000ULL) /* lfs lsetfacl case */
+#define OBD_MD_FLRMTLGETFACL (0x0002000000000000ULL) /* lfs lgetfacl case */
+#define OBD_MD_FLRMTRSETFACL (0x0004000000000000ULL) /* lfs rsetfacl case */
+#define OBD_MD_FLRMTRGETFACL (0x0008000000000000ULL) /* lfs rgetfacl case */
+
+#define OBD_MD_FLDATAVERSION (0x0010000000000000ULL) /* iversion sum */
+#define OBD_MD_FLRELEASED (0x0020000000000000ULL) /* file released */
+
+#define OBD_MD_FLGETATTR (OBD_MD_FLID | OBD_MD_FLATIME | OBD_MD_FLMTIME | \
+ OBD_MD_FLCTIME | OBD_MD_FLSIZE | OBD_MD_FLBLKSZ | \
+ OBD_MD_FLMODE | OBD_MD_FLTYPE | OBD_MD_FLUID | \
+ OBD_MD_FLGID | OBD_MD_FLFLAGS | OBD_MD_FLNLINK | \
+ OBD_MD_FLGENER | OBD_MD_FLRDEV | OBD_MD_FLGROUP)
+
+#define OBD_MD_FLXATTRALL (OBD_MD_FLXATTR | OBD_MD_FLXATTRLS)
+
+/* don't forget obdo_fid which is way down at the bottom so it can
+ * come after the definition of llog_cookie */
+
+enum hss_valid {
+ HSS_SETMASK = 0x01,
+ HSS_CLEARMASK = 0x02,
+ HSS_ARCHIVE_ID = 0x04,
+};
+
+struct hsm_state_set {
+ __u32 hss_valid;
+ __u32 hss_archive_id;
+ __u64 hss_setmask;
+ __u64 hss_clearmask;
+};
+
+extern void lustre_swab_hsm_user_state(struct hsm_user_state *hus);
+extern void lustre_swab_hsm_state_set(struct hsm_state_set *hss);
+
+extern void lustre_swab_obd_statfs (struct obd_statfs *os);
+
+/* ost_body.data values for OST_BRW */
+
+#define OBD_BRW_READ 0x01
+#define OBD_BRW_WRITE 0x02
+#define OBD_BRW_RWMASK (OBD_BRW_READ | OBD_BRW_WRITE)
+#define OBD_BRW_SYNC 0x08 /* this page is a part of synchronous
+ * transfer and is not accounted in
+ * the grant. */
+#define OBD_BRW_CHECK 0x10
+#define OBD_BRW_FROM_GRANT 0x20 /* the osc manages this under llite */
+#define OBD_BRW_GRANTED 0x40 /* the ost manages this */
+#define OBD_BRW_NOCACHE 0x80 /* this page is a part of non-cached IO */
+#define OBD_BRW_NOQUOTA 0x100
+#define OBD_BRW_SRVLOCK 0x200 /* Client holds no lock over this page */
+#define OBD_BRW_ASYNC 0x400 /* Server may delay commit to disk */
+#define OBD_BRW_MEMALLOC 0x800 /* Client runs in the "kswapd" context */
+#define OBD_BRW_OVER_USRQUOTA 0x1000 /* Running out of user quota */
+#define OBD_BRW_OVER_GRPQUOTA 0x2000 /* Running out of group quota */
+
+#define OBD_OBJECT_EOF 0xffffffffffffffffULL
+
+#define OST_MIN_PRECREATE 32
+#define OST_MAX_PRECREATE 20000
+
+struct obd_ioobj {
+ struct ost_id ioo_oid; /* object ID, if multi-obj BRW */
+ __u32 ioo_max_brw; /* low 16 bits were o_mode before 2.4,
+ * now (PTLRPC_BULK_OPS_COUNT - 1) in
+ * high 16 bits in 2.4 and later */
+ __u32 ioo_bufcnt; /* number of niobufs for this object */
+};
+
+#define IOOBJ_MAX_BRW_BITS 16
+#define IOOBJ_TYPE_MASK ((1U << IOOBJ_MAX_BRW_BITS) - 1)
+#define ioobj_max_brw_get(ioo) (((ioo)->ioo_max_brw >> IOOBJ_MAX_BRW_BITS) + 1)
+#define ioobj_max_brw_set(ioo, num) \
+do { (ioo)->ioo_max_brw = ((num) - 1) << IOOBJ_MAX_BRW_BITS; } while (0)
+
+extern void lustre_swab_obd_ioobj (struct obd_ioobj *ioo);
+
+/* multiple of 8 bytes => can array */
+struct niobuf_remote {
+ __u64 offset;
+ __u32 len;
+ __u32 flags;
+};
+
+extern void lustre_swab_niobuf_remote (struct niobuf_remote *nbr);
+
+/* lock value block communicated between the filter and llite */
+
+/* OST_LVB_ERR_INIT is needed because the return code in rc is
+ * negative, i.e. because ((MASK + rc) & MASK) != MASK. */
+#define OST_LVB_ERR_INIT 0xffbadbad80000000ULL
+#define OST_LVB_ERR_MASK 0xffbadbad00000000ULL
+#define OST_LVB_IS_ERR(blocks) \
+ ((blocks & OST_LVB_ERR_MASK) == OST_LVB_ERR_MASK)
+#define OST_LVB_SET_ERR(blocks, rc) \
+ do { blocks = OST_LVB_ERR_INIT + rc; } while (0)
+#define OST_LVB_GET_ERR(blocks) (int)(blocks - OST_LVB_ERR_INIT)
+
+struct ost_lvb_v1 {
+ __u64 lvb_size;
+ __s64 lvb_mtime;
+ __s64 lvb_atime;
+ __s64 lvb_ctime;
+ __u64 lvb_blocks;
+};
+
+extern void lustre_swab_ost_lvb_v1(struct ost_lvb_v1 *lvb);
+
+struct ost_lvb {
+ __u64 lvb_size;
+ __s64 lvb_mtime;
+ __s64 lvb_atime;
+ __s64 lvb_ctime;
+ __u64 lvb_blocks;
+ __u32 lvb_mtime_ns;
+ __u32 lvb_atime_ns;
+ __u32 lvb_ctime_ns;
+ __u32 lvb_padding;
+};
+
+extern void lustre_swab_ost_lvb(struct ost_lvb *lvb);
+
+/*
+ * lquota data structures
+ */
+
+#ifndef QUOTABLOCK_BITS
+#define QUOTABLOCK_BITS 10
+#endif
+
+#ifndef QUOTABLOCK_SIZE
+#define QUOTABLOCK_SIZE (1 << QUOTABLOCK_BITS)
+#endif
+
+#ifndef toqb
+#define toqb(x) (((x) + QUOTABLOCK_SIZE - 1) >> QUOTABLOCK_BITS)
+#endif
+
+/* The lquota_id structure is an union of all the possible identifier types that
+ * can be used with quota, this includes:
+ * - 64-bit user ID
+ * - 64-bit group ID
+ * - a FID which can be used for per-directory quota in the future */
+union lquota_id {
+ struct lu_fid qid_fid; /* FID for per-directory quota */
+ __u64 qid_uid; /* user identifier */
+ __u64 qid_gid; /* group identifier */
+};
+
+/* quotactl management */
+struct obd_quotactl {
+ __u32 qc_cmd;
+ __u32 qc_type; /* see Q_* flag below */
+ __u32 qc_id;
+ __u32 qc_stat;
+ struct obd_dqinfo qc_dqinfo;
+ struct obd_dqblk qc_dqblk;
+};
+
+extern void lustre_swab_obd_quotactl(struct obd_quotactl *q);
+
+#define Q_QUOTACHECK 0x800100 /* deprecated as of 2.4 */
+#define Q_INITQUOTA 0x800101 /* deprecated as of 2.4 */
+#define Q_GETOINFO 0x800102 /* get obd quota info */
+#define Q_GETOQUOTA 0x800103 /* get obd quotas */
+#define Q_FINVALIDATE 0x800104 /* deprecated as of 2.4 */
+
+#define Q_COPY(out, in, member) (out)->member = (in)->member
+
+#define QCTL_COPY(out, in) \
+do { \
+ Q_COPY(out, in, qc_cmd); \
+ Q_COPY(out, in, qc_type); \
+ Q_COPY(out, in, qc_id); \
+ Q_COPY(out, in, qc_stat); \
+ Q_COPY(out, in, qc_dqinfo); \
+ Q_COPY(out, in, qc_dqblk); \
+} while (0)
+
+/* Body of quota request used for quota acquire/release RPCs between quota
+ * master (aka QMT) and slaves (ak QSD). */
+struct quota_body {
+ struct lu_fid qb_fid; /* FID of global index packing the pool ID
+ * and type (data or metadata) as well as
+ * the quota type (user or group). */
+ union lquota_id qb_id; /* uid or gid or directory FID */
+ __u32 qb_flags; /* see below */
+ __u32 qb_padding;
+ __u64 qb_count; /* acquire/release count (kbytes/inodes) */
+ __u64 qb_usage; /* current slave usage (kbytes/inodes) */
+ __u64 qb_slv_ver; /* slave index file version */
+ struct lustre_handle qb_lockh; /* per-ID lock handle */
+ struct lustre_handle qb_glb_lockh; /* global lock handle */
+ __u64 qb_padding1[4];
+};
+
+/* When the quota_body is used in the reply of quota global intent
+ * lock (IT_QUOTA_CONN) reply, qb_fid contains slave index file FID. */
+#define qb_slv_fid qb_fid
+/* qb_usage is the current qunit (in kbytes/inodes) when quota_body is used in
+ * quota reply */
+#define qb_qunit qb_usage
+
+#define QUOTA_DQACQ_FL_ACQ 0x1 /* acquire quota */
+#define QUOTA_DQACQ_FL_PREACQ 0x2 /* pre-acquire */
+#define QUOTA_DQACQ_FL_REL 0x4 /* release quota */
+#define QUOTA_DQACQ_FL_REPORT 0x8 /* report usage */
+
+extern void lustre_swab_quota_body(struct quota_body *b);
+
+/* Quota types currently supported */
+enum {
+ LQUOTA_TYPE_USR = 0x00, /* maps to USRQUOTA */
+ LQUOTA_TYPE_GRP = 0x01, /* maps to GRPQUOTA */
+ LQUOTA_TYPE_MAX
+};
+
+/* There are 2 different resource types on which a quota limit can be enforced:
+ * - inodes on the MDTs
+ * - blocks on the OSTs */
+enum {
+ LQUOTA_RES_MD = 0x01, /* skip 0 to avoid null oid in FID */
+ LQUOTA_RES_DT = 0x02,
+ LQUOTA_LAST_RES,
+ LQUOTA_FIRST_RES = LQUOTA_RES_MD
+};
+#define LQUOTA_NR_RES (LQUOTA_LAST_RES - LQUOTA_FIRST_RES + 1)
+
+/*
+ * Space accounting support
+ * Format of an accounting record, providing disk usage information for a given
+ * user or group
+ */
+struct lquota_acct_rec { /* 16 bytes */
+ __u64 bspace; /* current space in use */
+ __u64 ispace; /* current # inodes in use */
+};
+
+/*
+ * Global quota index support
+ * Format of a global record, providing global quota settings for a given quota
+ * identifier
+ */
+struct lquota_glb_rec { /* 32 bytes */
+ __u64 qbr_hardlimit; /* quota hard limit, in #inodes or kbytes */
+ __u64 qbr_softlimit; /* quota soft limit, in #inodes or kbytes */
+ __u64 qbr_time; /* grace time, in seconds */
+ __u64 qbr_granted; /* how much is granted to slaves, in #inodes or
+ * kbytes */
+};
+
+/*
+ * Slave index support
+ * Format of a slave record, recording how much space is granted to a given
+ * slave
+ */
+struct lquota_slv_rec { /* 8 bytes */
+ __u64 qsr_granted; /* space granted to the slave for the key=ID,
+ * in #inodes or kbytes */
+};
+
+/* Data structures associated with the quota locks */
+
+/* Glimpse descriptor used for the index & per-ID quota locks */
+struct ldlm_gl_lquota_desc {
+ union lquota_id gl_id; /* quota ID subject to the glimpse */
+ __u64 gl_flags; /* see LQUOTA_FL* below */
+ __u64 gl_ver; /* new index version */
+ __u64 gl_hardlimit; /* new hardlimit or qunit value */
+ __u64 gl_softlimit; /* new softlimit */
+ __u64 gl_time;
+ __u64 gl_pad2;
+};
+#define gl_qunit gl_hardlimit /* current qunit value used when
+ * glimpsing per-ID quota locks */
+
+/* quota glimpse flags */
+#define LQUOTA_FL_EDQUOT 0x1 /* user/group out of quota space on QMT */
+
+/* LVB used with quota (global and per-ID) locks */
+struct lquota_lvb {
+ __u64 lvb_flags; /* see LQUOTA_FL* above */
+ __u64 lvb_id_may_rel; /* space that might be released later */
+ __u64 lvb_id_rel; /* space released by the slave for this ID */
+ __u64 lvb_id_qunit; /* current qunit value */
+ __u64 lvb_pad1;
+};
+
+extern void lustre_swab_lquota_lvb(struct lquota_lvb *lvb);
+
+/* LVB used with global quota lock */
+#define lvb_glb_ver lvb_id_may_rel /* current version of the global index */
+
+/* op codes */
+typedef enum {
+ QUOTA_DQACQ = 601,
+ QUOTA_DQREL = 602,
+ QUOTA_LAST_OPC
+} quota_cmd_t;
+#define QUOTA_FIRST_OPC QUOTA_DQACQ
+
+/*
+ * MDS REQ RECORDS
+ */
+
+/* opcodes */
+typedef enum {
+ MDS_GETATTR = 33,
+ MDS_GETATTR_NAME = 34,
+ MDS_CLOSE = 35,
+ MDS_REINT = 36,
+ MDS_READPAGE = 37,
+ MDS_CONNECT = 38,
+ MDS_DISCONNECT = 39,
+ MDS_GETSTATUS = 40,
+ MDS_STATFS = 41,
+ MDS_PIN = 42,
+ MDS_UNPIN = 43,
+ MDS_SYNC = 44,
+ MDS_DONE_WRITING = 45,
+ MDS_SET_INFO = 46,
+ MDS_QUOTACHECK = 47,
+ MDS_QUOTACTL = 48,
+ MDS_GETXATTR = 49,
+ MDS_SETXATTR = 50, /* obsolete, now it's MDS_REINT op */
+ MDS_WRITEPAGE = 51,
+ MDS_IS_SUBDIR = 52,
+ MDS_GET_INFO = 53,
+ MDS_HSM_STATE_GET = 54,
+ MDS_HSM_STATE_SET = 55,
+ MDS_HSM_ACTION = 56,
+ MDS_HSM_PROGRESS = 57,
+ MDS_HSM_REQUEST = 58,
+ MDS_HSM_CT_REGISTER = 59,
+ MDS_HSM_CT_UNREGISTER = 60,
+ MDS_SWAP_LAYOUTS = 61,
+ MDS_LAST_OPC
+} mds_cmd_t;
+
+#define MDS_FIRST_OPC MDS_GETATTR
+
+
+/* opcodes for object update */
+typedef enum {
+ UPDATE_OBJ = 1000,
+ UPDATE_LAST_OPC
+} update_cmd_t;
+
+#define UPDATE_FIRST_OPC UPDATE_OBJ
+
+/*
+ * Do not exceed 63
+ */
+
+typedef enum {
+ REINT_SETATTR = 1,
+ REINT_CREATE = 2,
+ REINT_LINK = 3,
+ REINT_UNLINK = 4,
+ REINT_RENAME = 5,
+ REINT_OPEN = 6,
+ REINT_SETXATTR = 7,
+ REINT_RMENTRY = 8,
+// REINT_WRITE = 9,
+ REINT_MAX
+} mds_reint_t, mdt_reint_t;
+
+extern void lustre_swab_generic_32s (__u32 *val);
+
+/* the disposition of the intent outlines what was executed */
+#define DISP_IT_EXECD 0x00000001
+#define DISP_LOOKUP_EXECD 0x00000002
+#define DISP_LOOKUP_NEG 0x00000004
+#define DISP_LOOKUP_POS 0x00000008
+#define DISP_OPEN_CREATE 0x00000010
+#define DISP_OPEN_OPEN 0x00000020
+#define DISP_ENQ_COMPLETE 0x00400000 /* obsolete and unused */
+#define DISP_ENQ_OPEN_REF 0x00800000
+#define DISP_ENQ_CREATE_REF 0x01000000
+#define DISP_OPEN_LOCK 0x02000000
+#define DISP_OPEN_LEASE 0x04000000
+#define DISP_OPEN_STRIPE 0x08000000
+
+/* INODE LOCK PARTS */
+#define MDS_INODELOCK_LOOKUP 0x000001 /* For namespace, dentry etc, and also
+ * was used to protect permission (mode,
+ * owner, group etc) before 2.4. */
+#define MDS_INODELOCK_UPDATE 0x000002 /* size, links, timestamps */
+#define MDS_INODELOCK_OPEN 0x000004 /* For opened files */
+#define MDS_INODELOCK_LAYOUT 0x000008 /* for layout */
+
+/* The PERM bit is added int 2.4, and it is used to protect permission(mode,
+ * owner, group, acl etc), so to separate the permission from LOOKUP lock.
+ * Because for remote directories(in DNE), these locks will be granted by
+ * different MDTs(different ldlm namespace).
+ *
+ * For local directory, MDT will always grant UPDATE_LOCK|PERM_LOCK together.
+ * For Remote directory, the master MDT, where the remote directory is, will
+ * grant UPDATE_LOCK|PERM_LOCK, and the remote MDT, where the name entry is,
+ * will grant LOOKUP_LOCK. */
+#define MDS_INODELOCK_PERM 0x000010
+#define MDS_INODELOCK_XATTR 0x000020 /* extended attributes */
+
+#define MDS_INODELOCK_MAXSHIFT 5
+/* This FULL lock is useful to take on unlink sort of operations */
+#define MDS_INODELOCK_FULL ((1<<(MDS_INODELOCK_MAXSHIFT+1))-1)
+
+extern void lustre_swab_ll_fid (struct ll_fid *fid);
+
+/* NOTE: until Lustre 1.8.7/2.1.1 the fid_ver() was packed into name[2],
+ * but was moved into name[1] along with the OID to avoid consuming the
+ * name[2,3] fields that need to be used for the quota id (also a FID). */
+enum {
+ LUSTRE_RES_ID_SEQ_OFF = 0,
+ LUSTRE_RES_ID_VER_OID_OFF = 1,
+ LUSTRE_RES_ID_WAS_VER_OFF = 2, /* see note above */
+ LUSTRE_RES_ID_QUOTA_SEQ_OFF = 2,
+ LUSTRE_RES_ID_QUOTA_VER_OID_OFF = 3,
+ LUSTRE_RES_ID_HSH_OFF = 3
+};
+
+#define MDS_STATUS_CONN 1
+#define MDS_STATUS_LOV 2
+
+/* mdt_thread_info.mti_flags. */
+enum md_op_flags {
+ /* The flag indicates Size-on-MDS attributes are changed. */
+ MF_SOM_CHANGE = (1 << 0),
+ /* Flags indicates an epoch opens or closes. */
+ MF_EPOCH_OPEN = (1 << 1),
+ MF_EPOCH_CLOSE = (1 << 2),
+ MF_MDC_CANCEL_FID1 = (1 << 3),
+ MF_MDC_CANCEL_FID2 = (1 << 4),
+ MF_MDC_CANCEL_FID3 = (1 << 5),
+ MF_MDC_CANCEL_FID4 = (1 << 6),
+ /* There is a pending attribute update. */
+ MF_SOM_AU = (1 << 7),
+ /* Cancel OST locks while getattr OST attributes. */
+ MF_GETATTR_LOCK = (1 << 8),
+ MF_GET_MDT_IDX = (1 << 9),
+};
+
+#define MF_SOM_LOCAL_FLAGS (MF_SOM_CHANGE | MF_EPOCH_OPEN | MF_EPOCH_CLOSE)
+
+#define LUSTRE_BFLAG_UNCOMMITTED_WRITES 0x1
+
+/* these should be identical to their EXT4_*_FL counterparts, they are
+ * redefined here only to avoid dragging in fs/ext4/ext4.h */
+#define LUSTRE_SYNC_FL 0x00000008 /* Synchronous updates */
+#define LUSTRE_IMMUTABLE_FL 0x00000010 /* Immutable file */
+#define LUSTRE_APPEND_FL 0x00000020 /* writes to file may only append */
+#define LUSTRE_NOATIME_FL 0x00000080 /* do not update atime */
+#define LUSTRE_DIRSYNC_FL 0x00010000 /* dirsync behaviour (dir only) */
+
+/* Convert wire LUSTRE_*_FL to corresponding client local VFS S_* values
+ * for the client inode i_flags. The LUSTRE_*_FL are the Lustre wire
+ * protocol equivalents of LDISKFS_*_FL values stored on disk, while
+ * the S_* flags are kernel-internal values that change between kernel
+ * versions. These flags are set/cleared via FSFILT_IOC_{GET,SET}_FLAGS.
+ * See b=16526 for a full history. */
+static inline int ll_ext_to_inode_flags(int flags)
+{
+ return (((flags & LUSTRE_SYNC_FL) ? S_SYNC : 0) |
+ ((flags & LUSTRE_NOATIME_FL) ? S_NOATIME : 0) |
+ ((flags & LUSTRE_APPEND_FL) ? S_APPEND : 0) |
+#if defined(S_DIRSYNC)
+ ((flags & LUSTRE_DIRSYNC_FL) ? S_DIRSYNC : 0) |
+#endif
+ ((flags & LUSTRE_IMMUTABLE_FL) ? S_IMMUTABLE : 0));
+}
+
+static inline int ll_inode_to_ext_flags(int iflags)
+{
+ return (((iflags & S_SYNC) ? LUSTRE_SYNC_FL : 0) |
+ ((iflags & S_NOATIME) ? LUSTRE_NOATIME_FL : 0) |
+ ((iflags & S_APPEND) ? LUSTRE_APPEND_FL : 0) |
+#if defined(S_DIRSYNC)
+ ((iflags & S_DIRSYNC) ? LUSTRE_DIRSYNC_FL : 0) |
+#endif
+ ((iflags & S_IMMUTABLE) ? LUSTRE_IMMUTABLE_FL : 0));
+}
+
+/* 64 possible states */
+enum md_transient_state {
+ MS_RESTORE = (1 << 0), /* restore is running */
+};
+
+struct mdt_body {
+ struct lu_fid fid1;
+ struct lu_fid fid2;
+ struct lustre_handle handle;
+ __u64 valid;
+ __u64 size; /* Offset, in the case of MDS_READPAGE */
+ __s64 mtime;
+ __s64 atime;
+ __s64 ctime;
+ __u64 blocks; /* XID, in the case of MDS_READPAGE */
+ __u64 ioepoch;
+ __u64 t_state; /* transient file state defined in
+ * enum md_transient_state
+ * was "ino" until 2.4.0 */
+ __u32 fsuid;
+ __u32 fsgid;
+ __u32 capability;
+ __u32 mode;
+ __u32 uid;
+ __u32 gid;
+ __u32 flags; /* from vfs for pin/unpin, LUSTRE_BFLAG close */
+ __u32 rdev;
+ __u32 nlink; /* #bytes to read in the case of MDS_READPAGE */
+ __u32 unused2; /* was "generation" until 2.4.0 */
+ __u32 suppgid;
+ __u32 eadatasize;
+ __u32 aclsize;
+ __u32 max_mdsize;
+ __u32 max_cookiesize;
+ __u32 uid_h; /* high 32-bits of uid, for FUID */
+ __u32 gid_h; /* high 32-bits of gid, for FUID */
+ __u32 padding_5; /* also fix lustre_swab_mdt_body */
+ __u64 padding_6;
+ __u64 padding_7;
+ __u64 padding_8;
+ __u64 padding_9;
+ __u64 padding_10;
+}; /* 216 */
+
+extern void lustre_swab_mdt_body (struct mdt_body *b);
+
+struct mdt_ioepoch {
+ struct lustre_handle handle;
+ __u64 ioepoch;
+ __u32 flags;
+ __u32 padding;
+};
+
+extern void lustre_swab_mdt_ioepoch (struct mdt_ioepoch *b);
+
+/* permissions for md_perm.mp_perm */
+enum {
+ CFS_SETUID_PERM = 0x01,
+ CFS_SETGID_PERM = 0x02,
+ CFS_SETGRP_PERM = 0x04,
+ CFS_RMTACL_PERM = 0x08,
+ CFS_RMTOWN_PERM = 0x10
+};
+
+/* inode access permission for remote user, the inode info are omitted,
+ * for client knows them. */
+struct mdt_remote_perm {
+ __u32 rp_uid;
+ __u32 rp_gid;
+ __u32 rp_fsuid;
+ __u32 rp_fsuid_h;
+ __u32 rp_fsgid;
+ __u32 rp_fsgid_h;
+ __u32 rp_access_perm; /* MAY_READ/WRITE/EXEC */
+ __u32 rp_padding;
+};
+
+extern void lustre_swab_mdt_remote_perm(struct mdt_remote_perm *p);
+
+struct mdt_rec_setattr {
+ __u32 sa_opcode;
+ __u32 sa_cap;
+ __u32 sa_fsuid;
+ __u32 sa_fsuid_h;
+ __u32 sa_fsgid;
+ __u32 sa_fsgid_h;
+ __u32 sa_suppgid;
+ __u32 sa_suppgid_h;
+ __u32 sa_padding_1;
+ __u32 sa_padding_1_h;
+ struct lu_fid sa_fid;
+ __u64 sa_valid;
+ __u32 sa_uid;
+ __u32 sa_gid;
+ __u64 sa_size;
+ __u64 sa_blocks;
+ __s64 sa_mtime;
+ __s64 sa_atime;
+ __s64 sa_ctime;
+ __u32 sa_attr_flags;
+ __u32 sa_mode;
+ __u32 sa_bias; /* some operation flags */
+ __u32 sa_padding_3;
+ __u32 sa_padding_4;
+ __u32 sa_padding_5;
+};
+
+extern void lustre_swab_mdt_rec_setattr (struct mdt_rec_setattr *sa);
+
+/*
+ * Attribute flags used in mdt_rec_setattr::sa_valid.
+ * The kernel's #defines for ATTR_* should not be used over the network
+ * since the client and MDS may run different kernels (see bug 13828)
+ * Therefore, we should only use MDS_ATTR_* attributes for sa_valid.
+ */
+#define MDS_ATTR_MODE 0x1ULL /* = 1 */
+#define MDS_ATTR_UID 0x2ULL /* = 2 */
+#define MDS_ATTR_GID 0x4ULL /* = 4 */
+#define MDS_ATTR_SIZE 0x8ULL /* = 8 */
+#define MDS_ATTR_ATIME 0x10ULL /* = 16 */
+#define MDS_ATTR_MTIME 0x20ULL /* = 32 */
+#define MDS_ATTR_CTIME 0x40ULL /* = 64 */
+#define MDS_ATTR_ATIME_SET 0x80ULL /* = 128 */
+#define MDS_ATTR_MTIME_SET 0x100ULL /* = 256 */
+#define MDS_ATTR_FORCE 0x200ULL /* = 512, Not a change, but a change it */
+#define MDS_ATTR_ATTR_FLAG 0x400ULL /* = 1024 */
+#define MDS_ATTR_KILL_SUID 0x800ULL /* = 2048 */
+#define MDS_ATTR_KILL_SGID 0x1000ULL /* = 4096 */
+#define MDS_ATTR_CTIME_SET 0x2000ULL /* = 8192 */
+#define MDS_ATTR_FROM_OPEN 0x4000ULL /* = 16384, called from open path, ie O_TRUNC */
+#define MDS_ATTR_BLOCKS 0x8000ULL /* = 32768 */
+
+#ifndef FMODE_READ
+#define FMODE_READ 00000001
+#define FMODE_WRITE 00000002
+#endif
+
+#define MDS_FMODE_CLOSED 00000000
+#define MDS_FMODE_EXEC 00000004
+/* IO Epoch is opened on a closed file. */
+#define MDS_FMODE_EPOCH 01000000
+/* IO Epoch is opened on a file truncate. */
+#define MDS_FMODE_TRUNC 02000000
+/* Size-on-MDS Attribute Update is pending. */
+#define MDS_FMODE_SOM 04000000
+
+#define MDS_OPEN_CREATED 00000010
+#define MDS_OPEN_CROSS 00000020
+
+#define MDS_OPEN_CREAT 00000100
+#define MDS_OPEN_EXCL 00000200
+#define MDS_OPEN_TRUNC 00001000
+#define MDS_OPEN_APPEND 00002000
+#define MDS_OPEN_SYNC 00010000
+#define MDS_OPEN_DIRECTORY 00200000
+
+#define MDS_OPEN_BY_FID 040000000 /* open_by_fid for known object */
+#define MDS_OPEN_DELAY_CREATE 0100000000 /* delay initial object create */
+#define MDS_OPEN_OWNEROVERRIDE 0200000000 /* NFSD rw-reopen ro file for owner */
+#define MDS_OPEN_JOIN_FILE 0400000000 /* open for join file.
+ * We do not support JOIN FILE
+ * anymore, reserve this flags
+ * just for preventing such bit
+ * to be reused. */
+
+#define MDS_OPEN_LOCK 04000000000 /* This open requires open lock */
+#define MDS_OPEN_HAS_EA 010000000000 /* specify object create pattern */
+#define MDS_OPEN_HAS_OBJS 020000000000 /* Just set the EA the obj exist */
+#define MDS_OPEN_NORESTORE 0100000000000ULL /* Do not restore file at open */
+#define MDS_OPEN_NEWSTRIPE 0200000000000ULL /* New stripe needed (restripe or
+ * hsm restore) */
+#define MDS_OPEN_VOLATILE 0400000000000ULL /* File is volatile = created
+ unlinked */
+#define MDS_OPEN_LEASE 01000000000000ULL /* Open the file and grant lease
+ * delegation, succeed if it's not
+ * being opened with conflict mode.
+ */
+#define MDS_OPEN_RELEASE 02000000000000ULL /* Open the file for HSM release */
+
+/* permission for create non-directory file */
+#define MAY_CREATE (1 << 7)
+/* permission for create directory file */
+#define MAY_LINK (1 << 8)
+/* permission for delete from the directory */
+#define MAY_UNLINK (1 << 9)
+/* source's permission for rename */
+#define MAY_RENAME_SRC (1 << 10)
+/* target's permission for rename */
+#define MAY_RENAME_TAR (1 << 11)
+/* part (parent's) VTX permission check */
+#define MAY_VTX_PART (1 << 12)
+/* full VTX permission check */
+#define MAY_VTX_FULL (1 << 13)
+/* lfs rgetfacl permission check */
+#define MAY_RGETFACL (1 << 14)
+
+enum mds_op_bias {
+ MDS_CHECK_SPLIT = 1 << 0,
+ MDS_CROSS_REF = 1 << 1,
+ MDS_VTX_BYPASS = 1 << 2,
+ MDS_PERM_BYPASS = 1 << 3,
+ MDS_SOM = 1 << 4,
+ MDS_QUOTA_IGNORE = 1 << 5,
+ MDS_CLOSE_CLEANUP = 1 << 6,
+ MDS_KEEP_ORPHAN = 1 << 7,
+ MDS_RECOV_OPEN = 1 << 8,
+ MDS_DATA_MODIFIED = 1 << 9,
+ MDS_CREATE_VOLATILE = 1 << 10,
+ MDS_OWNEROVERRIDE = 1 << 11,
+ MDS_HSM_RELEASE = 1 << 12,
+};
+
+/* instance of mdt_reint_rec */
+struct mdt_rec_create {
+ __u32 cr_opcode;
+ __u32 cr_cap;
+ __u32 cr_fsuid;
+ __u32 cr_fsuid_h;
+ __u32 cr_fsgid;
+ __u32 cr_fsgid_h;
+ __u32 cr_suppgid1;
+ __u32 cr_suppgid1_h;
+ __u32 cr_suppgid2;
+ __u32 cr_suppgid2_h;
+ struct lu_fid cr_fid1;
+ struct lu_fid cr_fid2;
+ struct lustre_handle cr_old_handle; /* handle in case of open replay */
+ __s64 cr_time;
+ __u64 cr_rdev;
+ __u64 cr_ioepoch;
+ __u64 cr_padding_1; /* rr_blocks */
+ __u32 cr_mode;
+ __u32 cr_bias;
+ /* use of helpers set/get_mrc_cr_flags() is needed to access
+ * 64 bits cr_flags [cr_flags_l, cr_flags_h], this is done to
+ * extend cr_flags size without breaking 1.8 compat */
+ __u32 cr_flags_l; /* for use with open, low 32 bits */
+ __u32 cr_flags_h; /* for use with open, high 32 bits */
+ __u32 cr_umask; /* umask for create */
+ __u32 cr_padding_4; /* rr_padding_4 */
+};
+
+static inline void set_mrc_cr_flags(struct mdt_rec_create *mrc, __u64 flags)
+{
+ mrc->cr_flags_l = (__u32)(flags & 0xFFFFFFFFUll);
+ mrc->cr_flags_h = (__u32)(flags >> 32);
+}
+
+static inline __u64 get_mrc_cr_flags(struct mdt_rec_create *mrc)
+{
+ return ((__u64)(mrc->cr_flags_l) | ((__u64)mrc->cr_flags_h << 32));
+}
+
+/* instance of mdt_reint_rec */
+struct mdt_rec_link {
+ __u32 lk_opcode;
+ __u32 lk_cap;
+ __u32 lk_fsuid;
+ __u32 lk_fsuid_h;
+ __u32 lk_fsgid;
+ __u32 lk_fsgid_h;
+ __u32 lk_suppgid1;
+ __u32 lk_suppgid1_h;
+ __u32 lk_suppgid2;
+ __u32 lk_suppgid2_h;
+ struct lu_fid lk_fid1;
+ struct lu_fid lk_fid2;
+ __s64 lk_time;
+ __u64 lk_padding_1; /* rr_atime */
+ __u64 lk_padding_2; /* rr_ctime */
+ __u64 lk_padding_3; /* rr_size */
+ __u64 lk_padding_4; /* rr_blocks */
+ __u32 lk_bias;
+ __u32 lk_padding_5; /* rr_mode */
+ __u32 lk_padding_6; /* rr_flags */
+ __u32 lk_padding_7; /* rr_padding_2 */
+ __u32 lk_padding_8; /* rr_padding_3 */
+ __u32 lk_padding_9; /* rr_padding_4 */
+};
+
+/* instance of mdt_reint_rec */
+struct mdt_rec_unlink {
+ __u32 ul_opcode;
+ __u32 ul_cap;
+ __u32 ul_fsuid;
+ __u32 ul_fsuid_h;
+ __u32 ul_fsgid;
+ __u32 ul_fsgid_h;
+ __u32 ul_suppgid1;
+ __u32 ul_suppgid1_h;
+ __u32 ul_suppgid2;
+ __u32 ul_suppgid2_h;
+ struct lu_fid ul_fid1;
+ struct lu_fid ul_fid2;
+ __s64 ul_time;
+ __u64 ul_padding_2; /* rr_atime */
+ __u64 ul_padding_3; /* rr_ctime */
+ __u64 ul_padding_4; /* rr_size */
+ __u64 ul_padding_5; /* rr_blocks */
+ __u32 ul_bias;
+ __u32 ul_mode;
+ __u32 ul_padding_6; /* rr_flags */
+ __u32 ul_padding_7; /* rr_padding_2 */
+ __u32 ul_padding_8; /* rr_padding_3 */
+ __u32 ul_padding_9; /* rr_padding_4 */
+};
+
+/* instance of mdt_reint_rec */
+struct mdt_rec_rename {
+ __u32 rn_opcode;
+ __u32 rn_cap;
+ __u32 rn_fsuid;
+ __u32 rn_fsuid_h;
+ __u32 rn_fsgid;
+ __u32 rn_fsgid_h;
+ __u32 rn_suppgid1;
+ __u32 rn_suppgid1_h;
+ __u32 rn_suppgid2;
+ __u32 rn_suppgid2_h;
+ struct lu_fid rn_fid1;
+ struct lu_fid rn_fid2;
+ __s64 rn_time;
+ __u64 rn_padding_1; /* rr_atime */
+ __u64 rn_padding_2; /* rr_ctime */
+ __u64 rn_padding_3; /* rr_size */
+ __u64 rn_padding_4; /* rr_blocks */
+ __u32 rn_bias; /* some operation flags */
+ __u32 rn_mode; /* cross-ref rename has mode */
+ __u32 rn_padding_5; /* rr_flags */
+ __u32 rn_padding_6; /* rr_padding_2 */
+ __u32 rn_padding_7; /* rr_padding_3 */
+ __u32 rn_padding_8; /* rr_padding_4 */
+};
+
+/* instance of mdt_reint_rec */
+struct mdt_rec_setxattr {
+ __u32 sx_opcode;
+ __u32 sx_cap;
+ __u32 sx_fsuid;
+ __u32 sx_fsuid_h;
+ __u32 sx_fsgid;
+ __u32 sx_fsgid_h;
+ __u32 sx_suppgid1;
+ __u32 sx_suppgid1_h;
+ __u32 sx_suppgid2;
+ __u32 sx_suppgid2_h;
+ struct lu_fid sx_fid;
+ __u64 sx_padding_1; /* These three are rr_fid2 */
+ __u32 sx_padding_2;
+ __u32 sx_padding_3;
+ __u64 sx_valid;
+ __s64 sx_time;
+ __u64 sx_padding_5; /* rr_ctime */
+ __u64 sx_padding_6; /* rr_size */
+ __u64 sx_padding_7; /* rr_blocks */
+ __u32 sx_size;
+ __u32 sx_flags;
+ __u32 sx_padding_8; /* rr_flags */
+ __u32 sx_padding_9; /* rr_padding_2 */
+ __u32 sx_padding_10; /* rr_padding_3 */
+ __u32 sx_padding_11; /* rr_padding_4 */
+};
+
+/*
+ * mdt_rec_reint is the template for all mdt_reint_xxx structures.
+ * Do NOT change the size of various members, otherwise the value
+ * will be broken in lustre_swab_mdt_rec_reint().
+ *
+ * If you add new members in other mdt_reint_xxx structures and need to use the
+ * rr_padding_x fields, then update lustre_swab_mdt_rec_reint() also.
+ */
+struct mdt_rec_reint {
+ __u32 rr_opcode;
+ __u32 rr_cap;
+ __u32 rr_fsuid;
+ __u32 rr_fsuid_h;
+ __u32 rr_fsgid;
+ __u32 rr_fsgid_h;
+ __u32 rr_suppgid1;
+ __u32 rr_suppgid1_h;
+ __u32 rr_suppgid2;
+ __u32 rr_suppgid2_h;
+ struct lu_fid rr_fid1;
+ struct lu_fid rr_fid2;
+ __s64 rr_mtime;
+ __s64 rr_atime;
+ __s64 rr_ctime;
+ __u64 rr_size;
+ __u64 rr_blocks;
+ __u32 rr_bias;
+ __u32 rr_mode;
+ __u32 rr_flags;
+ __u32 rr_flags_h;
+ __u32 rr_umask;
+ __u32 rr_padding_4; /* also fix lustre_swab_mdt_rec_reint */
+};
+
+extern void lustre_swab_mdt_rec_reint(struct mdt_rec_reint *rr);
+
+struct lmv_desc {
+ __u32 ld_tgt_count; /* how many MDS's */
+ __u32 ld_active_tgt_count; /* how many active */
+ __u32 ld_default_stripe_count; /* how many objects are used */
+ __u32 ld_pattern; /* default MEA_MAGIC_* */
+ __u64 ld_default_hash_size;
+ __u64 ld_padding_1; /* also fix lustre_swab_lmv_desc */
+ __u32 ld_padding_2; /* also fix lustre_swab_lmv_desc */
+ __u32 ld_qos_maxage; /* in second */
+ __u32 ld_padding_3; /* also fix lustre_swab_lmv_desc */
+ __u32 ld_padding_4; /* also fix lustre_swab_lmv_desc */
+ struct obd_uuid ld_uuid;
+};
+
+extern void lustre_swab_lmv_desc (struct lmv_desc *ld);
+
+/* TODO: lmv_stripe_md should contain mds capabilities for all slave fids */
+struct lmv_stripe_md {
+ __u32 mea_magic;
+ __u32 mea_count;
+ __u32 mea_master;
+ __u32 mea_padding;
+ char mea_pool_name[LOV_MAXPOOLNAME];
+ struct lu_fid mea_ids[0];
+};
+
+extern void lustre_swab_lmv_stripe_md(struct lmv_stripe_md *mea);
+
+/* lmv structures */
+#define MEA_MAGIC_LAST_CHAR 0xb2221ca1
+#define MEA_MAGIC_ALL_CHARS 0xb222a11c
+#define MEA_MAGIC_HASH_SEGMENT 0xb222a11b
+
+#define MAX_HASH_SIZE_32 0x7fffffffUL
+#define MAX_HASH_SIZE 0x7fffffffffffffffULL
+#define MAX_HASH_HIGHEST_BIT 0x1000000000000000ULL
+
+enum fld_rpc_opc {
+ FLD_QUERY = 900,
+ FLD_LAST_OPC,
+ FLD_FIRST_OPC = FLD_QUERY
+};
+
+enum seq_rpc_opc {
+ SEQ_QUERY = 700,
+ SEQ_LAST_OPC,
+ SEQ_FIRST_OPC = SEQ_QUERY
+};
+
+enum seq_op {
+ SEQ_ALLOC_SUPER = 0,
+ SEQ_ALLOC_META = 1
+};
+
+/*
+ * LOV data structures
+ */
+
+#define LOV_MAX_UUID_BUFFER_SIZE 8192
+/* The size of the buffer the lov/mdc reserves for the
+ * array of UUIDs returned by the MDS. With the current
+ * protocol, this will limit the max number of OSTs per LOV */
+
+#define LOV_DESC_MAGIC 0xB0CCDE5C
+#define LOV_DESC_QOS_MAXAGE_DEFAULT 5 /* Seconds */
+#define LOV_DESC_STRIPE_SIZE_DEFAULT (1 << LNET_MTU_BITS)
+
+/* LOV settings descriptor (should only contain static info) */
+struct lov_desc {
+ __u32 ld_tgt_count; /* how many OBD's */
+ __u32 ld_active_tgt_count; /* how many active */
+ __u32 ld_default_stripe_count; /* how many objects are used */
+ __u32 ld_pattern; /* default PATTERN_RAID0 */
+ __u64 ld_default_stripe_size; /* in bytes */
+ __u64 ld_default_stripe_offset; /* in bytes */
+ __u32 ld_padding_0; /* unused */
+ __u32 ld_qos_maxage; /* in second */
+ __u32 ld_padding_1; /* also fix lustre_swab_lov_desc */
+ __u32 ld_padding_2; /* also fix lustre_swab_lov_desc */
+ struct obd_uuid ld_uuid;
+};
+
+#define ld_magic ld_active_tgt_count /* for swabbing from llogs */
+
+extern void lustre_swab_lov_desc (struct lov_desc *ld);
+
+/*
+ * LDLM requests:
+ */
+/* opcodes -- MUST be distinct from OST/MDS opcodes */
+typedef enum {
+ LDLM_ENQUEUE = 101,
+ LDLM_CONVERT = 102,
+ LDLM_CANCEL = 103,
+ LDLM_BL_CALLBACK = 104,
+ LDLM_CP_CALLBACK = 105,
+ LDLM_GL_CALLBACK = 106,
+ LDLM_SET_INFO = 107,
+ LDLM_LAST_OPC
+} ldlm_cmd_t;
+#define LDLM_FIRST_OPC LDLM_ENQUEUE
+
+#define RES_NAME_SIZE 4
+struct ldlm_res_id {
+ __u64 name[RES_NAME_SIZE];
+};
+
+#define DLDLMRES "[%#llx:%#llx:%#llx].%llx"
+#define PLDLMRES(res) (res)->lr_name.name[0], (res)->lr_name.name[1], \
+ (res)->lr_name.name[2], (res)->lr_name.name[3]
+
+extern void lustre_swab_ldlm_res_id (struct ldlm_res_id *id);
+
+static inline int ldlm_res_eq(const struct ldlm_res_id *res0,
+ const struct ldlm_res_id *res1)
+{
+ return !memcmp(res0, res1, sizeof(*res0));
+}
+
+/* lock types */
+typedef enum {
+ LCK_MINMODE = 0,
+ LCK_EX = 1,
+ LCK_PW = 2,
+ LCK_PR = 4,
+ LCK_CW = 8,
+ LCK_CR = 16,
+ LCK_NL = 32,
+ LCK_GROUP = 64,
+ LCK_COS = 128,
+ LCK_MAXMODE
+} ldlm_mode_t;
+
+#define LCK_MODE_NUM 8
+
+typedef enum {
+ LDLM_PLAIN = 10,
+ LDLM_EXTENT = 11,
+ LDLM_FLOCK = 12,
+ LDLM_IBITS = 13,
+ LDLM_MAX_TYPE
+} ldlm_type_t;
+
+#define LDLM_MIN_TYPE LDLM_PLAIN
+
+struct ldlm_extent {
+ __u64 start;
+ __u64 end;
+ __u64 gid;
+};
+
+static inline int ldlm_extent_overlap(struct ldlm_extent *ex1,
+ struct ldlm_extent *ex2)
+{
+ return (ex1->start <= ex2->end) && (ex2->start <= ex1->end);
+}
+
+/* check if @ex1 contains @ex2 */
+static inline int ldlm_extent_contain(struct ldlm_extent *ex1,
+ struct ldlm_extent *ex2)
+{
+ return (ex1->start <= ex2->start) && (ex1->end >= ex2->end);
+}
+
+struct ldlm_inodebits {
+ __u64 bits;
+};
+
+struct ldlm_flock_wire {
+ __u64 lfw_start;
+ __u64 lfw_end;
+ __u64 lfw_owner;
+ __u32 lfw_padding;
+ __u32 lfw_pid;
+};
+
+/* it's important that the fields of the ldlm_extent structure match
+ * the first fields of the ldlm_flock structure because there is only
+ * one ldlm_swab routine to process the ldlm_policy_data_t union. if
+ * this ever changes we will need to swab the union differently based
+ * on the resource type. */
+
+typedef union {
+ struct ldlm_extent l_extent;
+ struct ldlm_flock_wire l_flock;
+ struct ldlm_inodebits l_inodebits;
+} ldlm_wire_policy_data_t;
+
+extern void lustre_swab_ldlm_policy_data (ldlm_wire_policy_data_t *d);
+
+union ldlm_gl_desc {
+ struct ldlm_gl_lquota_desc lquota_desc;
+};
+
+extern void lustre_swab_gl_desc(union ldlm_gl_desc *);
+
+struct ldlm_intent {
+ __u64 opc;
+};
+
+extern void lustre_swab_ldlm_intent (struct ldlm_intent *i);
+
+struct ldlm_resource_desc {
+ ldlm_type_t lr_type;
+ __u32 lr_padding; /* also fix lustre_swab_ldlm_resource_desc */
+ struct ldlm_res_id lr_name;
+};
+
+extern void lustre_swab_ldlm_resource_desc (struct ldlm_resource_desc *r);
+
+struct ldlm_lock_desc {
+ struct ldlm_resource_desc l_resource;
+ ldlm_mode_t l_req_mode;
+ ldlm_mode_t l_granted_mode;
+ ldlm_wire_policy_data_t l_policy_data;
+};
+
+extern void lustre_swab_ldlm_lock_desc (struct ldlm_lock_desc *l);
+
+#define LDLM_LOCKREQ_HANDLES 2
+#define LDLM_ENQUEUE_CANCEL_OFF 1
+
+struct ldlm_request {
+ __u32 lock_flags;
+ __u32 lock_count;
+ struct ldlm_lock_desc lock_desc;
+ struct lustre_handle lock_handle[LDLM_LOCKREQ_HANDLES];
+};
+
+extern void lustre_swab_ldlm_request (struct ldlm_request *rq);
+
+/* If LDLM_ENQUEUE, 1 slot is already occupied, 1 is available.
+ * Otherwise, 2 are available. */
+#define ldlm_request_bufsize(count, type) \
+({ \
+ int _avail = LDLM_LOCKREQ_HANDLES; \
+ _avail -= (type == LDLM_ENQUEUE ? LDLM_ENQUEUE_CANCEL_OFF : 0); \
+ sizeof(struct ldlm_request) + \
+ (count > _avail ? count - _avail : 0) * \
+ sizeof(struct lustre_handle); \
+})
+
+struct ldlm_reply {
+ __u32 lock_flags;
+ __u32 lock_padding; /* also fix lustre_swab_ldlm_reply */
+ struct ldlm_lock_desc lock_desc;
+ struct lustre_handle lock_handle;
+ __u64 lock_policy_res1;
+ __u64 lock_policy_res2;
+};
+
+extern void lustre_swab_ldlm_reply (struct ldlm_reply *r);
+
+#define ldlm_flags_to_wire(flags) ((__u32)(flags))
+#define ldlm_flags_from_wire(flags) ((__u64)(flags))
+
+/*
+ * Opcodes for mountconf (mgs and mgc)
+ */
+typedef enum {
+ MGS_CONNECT = 250,
+ MGS_DISCONNECT,
+ MGS_EXCEPTION, /* node died, etc. */
+ MGS_TARGET_REG, /* whenever target starts up */
+ MGS_TARGET_DEL,
+ MGS_SET_INFO,
+ MGS_CONFIG_READ,
+ MGS_LAST_OPC
+} mgs_cmd_t;
+#define MGS_FIRST_OPC MGS_CONNECT
+
+#define MGS_PARAM_MAXLEN 1024
+#define KEY_SET_INFO "set_info"
+
+struct mgs_send_param {
+ char mgs_param[MGS_PARAM_MAXLEN];
+};
+
+/* We pass this info to the MGS so it can write config logs */
+#define MTI_NAME_MAXLEN 64
+#define MTI_PARAM_MAXLEN 4096
+#define MTI_NIDS_MAX 32
+struct mgs_target_info {
+ __u32 mti_lustre_ver;
+ __u32 mti_stripe_index;
+ __u32 mti_config_ver;
+ __u32 mti_flags;
+ __u32 mti_nid_count;
+ __u32 mti_instance; /* Running instance of target */
+ char mti_fsname[MTI_NAME_MAXLEN];
+ char mti_svname[MTI_NAME_MAXLEN];
+ char mti_uuid[sizeof(struct obd_uuid)];
+ __u64 mti_nids[MTI_NIDS_MAX]; /* host nids (lnet_nid_t)*/
+ char mti_params[MTI_PARAM_MAXLEN];
+};
+extern void lustre_swab_mgs_target_info(struct mgs_target_info *oinfo);
+
+struct mgs_nidtbl_entry {
+ __u64 mne_version; /* table version of this entry */
+ __u32 mne_instance; /* target instance # */
+ __u32 mne_index; /* target index */
+ __u32 mne_length; /* length of this entry - by bytes */
+ __u8 mne_type; /* target type LDD_F_SV_TYPE_OST/MDT */
+ __u8 mne_nid_type; /* type of nid(mbz). for ipv6. */
+ __u8 mne_nid_size; /* size of each NID, by bytes */
+ __u8 mne_nid_count; /* # of NIDs in buffer */
+ union {
+ lnet_nid_t nids[0]; /* variable size buffer for NIDs. */
+ } u;
+};
+extern void lustre_swab_mgs_nidtbl_entry(struct mgs_nidtbl_entry *oinfo);
+
+struct mgs_config_body {
+ char mcb_name[MTI_NAME_MAXLEN]; /* logname */
+ __u64 mcb_offset; /* next index of config log to request */
+ __u16 mcb_type; /* type of log: CONFIG_T_[CONFIG|RECOVER] */
+ __u8 mcb_reserved;
+ __u8 mcb_bits; /* bits unit size of config log */
+ __u32 mcb_units; /* # of units for bulk transfer */
+};
+extern void lustre_swab_mgs_config_body(struct mgs_config_body *body);
+
+struct mgs_config_res {
+ __u64 mcr_offset; /* index of last config log */
+ __u64 mcr_size; /* size of the log */
+};
+extern void lustre_swab_mgs_config_res(struct mgs_config_res *body);
+
+/* Config marker flags (in config log) */
+#define CM_START 0x01
+#define CM_END 0x02
+#define CM_SKIP 0x04
+#define CM_UPGRADE146 0x08
+#define CM_EXCLUDE 0x10
+#define CM_START_SKIP (CM_START | CM_SKIP)
+
+struct cfg_marker {
+ __u32 cm_step; /* aka config version */
+ __u32 cm_flags;
+ __u32 cm_vers; /* lustre release version number */
+ __u32 cm_padding; /* 64 bit align */
+ __s64 cm_createtime; /*when this record was first created */
+ __s64 cm_canceltime; /*when this record is no longer valid*/
+ char cm_tgtname[MTI_NAME_MAXLEN];
+ char cm_comment[MTI_NAME_MAXLEN];
+};
+
+extern void lustre_swab_cfg_marker(struct cfg_marker *marker,
+ int swab, int size);
+
+/*
+ * Opcodes for multiple servers.
+ */
+
+typedef enum {
+ OBD_PING = 400,
+ OBD_LOG_CANCEL,
+ OBD_QC_CALLBACK,
+ OBD_IDX_READ,
+ OBD_LAST_OPC
+} obd_cmd_t;
+#define OBD_FIRST_OPC OBD_PING
+
+/* catalog of log objects */
+
+/** Identifier for a single log object */
+struct llog_logid {
+ struct ost_id lgl_oi;
+ __u32 lgl_ogen;
+} __attribute__((packed));
+
+/** Records written to the CATALOGS list */
+#define CATLIST "CATALOGS"
+struct llog_catid {
+ struct llog_logid lci_logid;
+ __u32 lci_padding1;
+ __u32 lci_padding2;
+ __u32 lci_padding3;
+} __attribute__((packed));
+
+/* Log data record types - there is no specific reason that these need to
+ * be related to the RPC opcodes, but no reason not to (may be handy later?)
+ */
+#define LLOG_OP_MAGIC 0x10600000
+#define LLOG_OP_MASK 0xfff00000
+
+typedef enum {
+ LLOG_PAD_MAGIC = LLOG_OP_MAGIC | 0x00000,
+ OST_SZ_REC = LLOG_OP_MAGIC | 0x00f00,
+ /* OST_RAID1_REC = LLOG_OP_MAGIC | 0x01000, never used */
+ MDS_UNLINK_REC = LLOG_OP_MAGIC | 0x10000 | (MDS_REINT << 8) |
+ REINT_UNLINK, /* obsolete after 2.5.0 */
+ MDS_UNLINK64_REC = LLOG_OP_MAGIC | 0x90000 | (MDS_REINT << 8) |
+ REINT_UNLINK,
+ /* MDS_SETATTR_REC = LLOG_OP_MAGIC | 0x12401, obsolete 1.8.0 */
+ MDS_SETATTR64_REC = LLOG_OP_MAGIC | 0x90000 | (MDS_REINT << 8) |
+ REINT_SETATTR,
+ OBD_CFG_REC = LLOG_OP_MAGIC | 0x20000,
+ /* PTL_CFG_REC = LLOG_OP_MAGIC | 0x30000, obsolete 1.4.0 */
+ LLOG_GEN_REC = LLOG_OP_MAGIC | 0x40000,
+ /* LLOG_JOIN_REC = LLOG_OP_MAGIC | 0x50000, obsolete 1.8.0 */
+ CHANGELOG_REC = LLOG_OP_MAGIC | 0x60000,
+ CHANGELOG_USER_REC = LLOG_OP_MAGIC | 0x70000,
+ HSM_AGENT_REC = LLOG_OP_MAGIC | 0x80000,
+ LLOG_HDR_MAGIC = LLOG_OP_MAGIC | 0x45539,
+ LLOG_LOGID_MAGIC = LLOG_OP_MAGIC | 0x4553b,
+} llog_op_type;
+
+#define LLOG_REC_HDR_NEEDS_SWABBING(r) \
+ (((r)->lrh_type & __swab32(LLOG_OP_MASK)) == __swab32(LLOG_OP_MAGIC))
+
+/** Log record header - stored in little endian order.
+ * Each record must start with this struct, end with a llog_rec_tail,
+ * and be a multiple of 256 bits in size.
+ */
+struct llog_rec_hdr {
+ __u32 lrh_len;
+ __u32 lrh_index;
+ __u32 lrh_type;
+ __u32 lrh_id;
+};
+
+struct llog_rec_tail {
+ __u32 lrt_len;
+ __u32 lrt_index;
+};
+
+/* Where data follow just after header */
+#define REC_DATA(ptr) \
+ ((void *)((char *)ptr + sizeof(struct llog_rec_hdr)))
+
+#define REC_DATA_LEN(rec) \
+ (rec->lrh_len - sizeof(struct llog_rec_hdr) - \
+ sizeof(struct llog_rec_tail))
+
+struct llog_logid_rec {
+ struct llog_rec_hdr lid_hdr;
+ struct llog_logid lid_id;
+ __u32 lid_padding1;
+ __u64 lid_padding2;
+ __u64 lid_padding3;
+ struct llog_rec_tail lid_tail;
+} __attribute__((packed));
+
+struct llog_unlink_rec {
+ struct llog_rec_hdr lur_hdr;
+ __u64 lur_oid;
+ __u32 lur_oseq;
+ __u32 lur_count;
+ struct llog_rec_tail lur_tail;
+} __attribute__((packed));
+
+struct llog_unlink64_rec {
+ struct llog_rec_hdr lur_hdr;
+ struct lu_fid lur_fid;
+ __u32 lur_count; /* to destroy the lost precreated */
+ __u32 lur_padding1;
+ __u64 lur_padding2;
+ __u64 lur_padding3;
+ struct llog_rec_tail lur_tail;
+} __attribute__((packed));
+
+struct llog_setattr64_rec {
+ struct llog_rec_hdr lsr_hdr;
+ struct ost_id lsr_oi;
+ __u32 lsr_uid;
+ __u32 lsr_uid_h;
+ __u32 lsr_gid;
+ __u32 lsr_gid_h;
+ __u64 lsr_padding;
+ struct llog_rec_tail lsr_tail;
+} __attribute__((packed));
+
+struct llog_size_change_rec {
+ struct llog_rec_hdr lsc_hdr;
+ struct ll_fid lsc_fid;
+ __u32 lsc_ioepoch;
+ __u32 lsc_padding1;
+ __u64 lsc_padding2;
+ __u64 lsc_padding3;
+ struct llog_rec_tail lsc_tail;
+} __attribute__((packed));
+
+#define CHANGELOG_MAGIC 0xca103000
+
+/** \a changelog_rec_type's that can't be masked */
+#define CHANGELOG_MINMASK (1 << CL_MARK)
+/** bits covering all \a changelog_rec_type's */
+#define CHANGELOG_ALLMASK 0XFFFFFFFF
+/** default \a changelog_rec_type mask */
+#define CHANGELOG_DEFMASK CHANGELOG_ALLMASK & ~(1 << CL_ATIME | 1 << CL_CLOSE)
+
+/* changelog llog name, needed by client replicators */
+#define CHANGELOG_CATALOG "changelog_catalog"
+
+struct changelog_setinfo {
+ __u64 cs_recno;
+ __u32 cs_id;
+} __attribute__((packed));
+
+/** changelog record */
+struct llog_changelog_rec {
+ struct llog_rec_hdr cr_hdr;
+ struct changelog_rec cr;
+ struct llog_rec_tail cr_tail; /**< for_sizezof_only */
+} __attribute__((packed));
+
+struct llog_changelog_ext_rec {
+ struct llog_rec_hdr cr_hdr;
+ struct changelog_ext_rec cr;
+ struct llog_rec_tail cr_tail; /**< for_sizezof_only */
+} __attribute__((packed));
+
+#define CHANGELOG_USER_PREFIX "cl"
+
+struct llog_changelog_user_rec {
+ struct llog_rec_hdr cur_hdr;
+ __u32 cur_id;
+ __u32 cur_padding;
+ __u64 cur_endrec;
+ struct llog_rec_tail cur_tail;
+} __attribute__((packed));
+
+enum agent_req_status {
+ ARS_WAITING,
+ ARS_STARTED,
+ ARS_FAILED,
+ ARS_CANCELED,
+ ARS_SUCCEED,
+};
+
+static inline char *agent_req_status2name(enum agent_req_status ars)
+{
+ switch (ars) {
+ case ARS_WAITING:
+ return "WAITING";
+ case ARS_STARTED:
+ return "STARTED";
+ case ARS_FAILED:
+ return "FAILED";
+ case ARS_CANCELED:
+ return "CANCELED";
+ case ARS_SUCCEED:
+ return "SUCCEED";
+ default:
+ return "UNKNOWN";
+ }
+}
+
+static inline bool agent_req_in_final_state(enum agent_req_status ars)
+{
+ return ((ars == ARS_SUCCEED) || (ars == ARS_FAILED) ||
+ (ars == ARS_CANCELED));
+}
+
+struct llog_agent_req_rec {
+ struct llog_rec_hdr arr_hdr; /**< record header */
+ __u32 arr_status; /**< status of the request */
+ /* must match enum
+ * agent_req_status */
+ __u32 arr_archive_id; /**< backend archive number */
+ __u64 arr_flags; /**< req flags */
+ __u64 arr_compound_id; /**< compound cookie */
+ __u64 arr_req_create; /**< req. creation time */
+ __u64 arr_req_change; /**< req. status change time */
+ struct hsm_action_item arr_hai; /**< req. to the agent */
+ struct llog_rec_tail arr_tail; /**< record tail for_sizezof_only */
+} __attribute__((packed));
+
+/* Old llog gen for compatibility */
+struct llog_gen {
+ __u64 mnt_cnt;
+ __u64 conn_cnt;
+} __attribute__((packed));
+
+struct llog_gen_rec {
+ struct llog_rec_hdr lgr_hdr;
+ struct llog_gen lgr_gen;
+ __u64 padding1;
+ __u64 padding2;
+ __u64 padding3;
+ struct llog_rec_tail lgr_tail;
+};
+
+/* On-disk header structure of each log object, stored in little endian order */
+#define LLOG_CHUNK_SIZE 8192
+#define LLOG_HEADER_SIZE (96)
+#define LLOG_BITMAP_BYTES (LLOG_CHUNK_SIZE - LLOG_HEADER_SIZE)
+
+#define LLOG_MIN_REC_SIZE (24) /* round(llog_rec_hdr + llog_rec_tail) */
+
+/* flags for the logs */
+enum llog_flag {
+ LLOG_F_ZAP_WHEN_EMPTY = 0x1,
+ LLOG_F_IS_CAT = 0x2,
+ LLOG_F_IS_PLAIN = 0x4,
+};
+
+struct llog_log_hdr {
+ struct llog_rec_hdr llh_hdr;
+ __s64 llh_timestamp;
+ __u32 llh_count;
+ __u32 llh_bitmap_offset;
+ __u32 llh_size;
+ __u32 llh_flags;
+ __u32 llh_cat_idx;
+ /* for a catalog the first plain slot is next to it */
+ struct obd_uuid llh_tgtuuid;
+ __u32 llh_reserved[LLOG_HEADER_SIZE/sizeof(__u32) - 23];
+ __u32 llh_bitmap[LLOG_BITMAP_BYTES/sizeof(__u32)];
+ struct llog_rec_tail llh_tail;
+} __attribute__((packed));
+
+#define LLOG_BITMAP_SIZE(llh) (__u32)((llh->llh_hdr.lrh_len - \
+ llh->llh_bitmap_offset - \
+ sizeof(llh->llh_tail)) * 8)
+
+/** log cookies are used to reference a specific log file and a record therein */
+struct llog_cookie {
+ struct llog_logid lgc_lgl;
+ __u32 lgc_subsys;
+ __u32 lgc_index;
+ __u32 lgc_padding;
+} __attribute__((packed));
+
+/** llog protocol */
+enum llogd_rpc_ops {
+ LLOG_ORIGIN_HANDLE_CREATE = 501,
+ LLOG_ORIGIN_HANDLE_NEXT_BLOCK = 502,
+ LLOG_ORIGIN_HANDLE_READ_HEADER = 503,
+ LLOG_ORIGIN_HANDLE_WRITE_REC = 504,
+ LLOG_ORIGIN_HANDLE_CLOSE = 505,
+ LLOG_ORIGIN_CONNECT = 506,
+ LLOG_CATINFO = 507, /* deprecated */
+ LLOG_ORIGIN_HANDLE_PREV_BLOCK = 508,
+ LLOG_ORIGIN_HANDLE_DESTROY = 509, /* for destroy llog object*/
+ LLOG_LAST_OPC,
+ LLOG_FIRST_OPC = LLOG_ORIGIN_HANDLE_CREATE
+};
+
+struct llogd_body {
+ struct llog_logid lgd_logid;
+ __u32 lgd_ctxt_idx;
+ __u32 lgd_llh_flags;
+ __u32 lgd_index;
+ __u32 lgd_saved_index;
+ __u32 lgd_len;
+ __u64 lgd_cur_offset;
+} __attribute__((packed));
+
+struct llogd_conn_body {
+ struct llog_gen lgdc_gen;
+ struct llog_logid lgdc_logid;
+ __u32 lgdc_ctxt_idx;
+} __attribute__((packed));
+
+/* Note: 64-bit types are 64-bit aligned in structure */
+struct obdo {
+ __u64 o_valid; /* hot fields in this obdo */
+ struct ost_id o_oi;
+ __u64 o_parent_seq;
+ __u64 o_size; /* o_size-o_blocks == ost_lvb */
+ __s64 o_mtime;
+ __s64 o_atime;
+ __s64 o_ctime;
+ __u64 o_blocks; /* brw: cli sent cached bytes */
+ __u64 o_grant;
+
+ /* 32-bit fields start here: keep an even number of them via padding */
+ __u32 o_blksize; /* optimal IO blocksize */
+ __u32 o_mode; /* brw: cli sent cache remain */
+ __u32 o_uid;
+ __u32 o_gid;
+ __u32 o_flags;
+ __u32 o_nlink; /* brw: checksum */
+ __u32 o_parent_oid;
+ __u32 o_misc; /* brw: o_dropped */
+
+ __u64 o_ioepoch; /* epoch in ost writes */
+ __u32 o_stripe_idx; /* holds stripe idx */
+ __u32 o_parent_ver;
+ struct lustre_handle o_handle; /* brw: lock handle to prolong
+ * locks */
+ struct llog_cookie o_lcookie; /* destroy: unlink cookie from
+ * MDS */
+ __u32 o_uid_h;
+ __u32 o_gid_h;
+
+ __u64 o_data_version; /* getattr: sum of iversion for
+ * each stripe.
+ * brw: grant space consumed on
+ * the client for the write */
+ __u64 o_padding_4;
+ __u64 o_padding_5;
+ __u64 o_padding_6;
+};
+
+#define o_dirty o_blocks
+#define o_undirty o_mode
+#define o_dropped o_misc
+#define o_cksum o_nlink
+#define o_grant_used o_data_version
+
+static inline void lustre_set_wire_obdo(struct obd_connect_data *ocd,
+ struct obdo *wobdo,
+ const struct obdo *lobdo)
+{
+ *wobdo = *lobdo;
+ wobdo->o_flags &= ~OBD_FL_LOCAL_MASK;
+ if (ocd == NULL)
+ return;
+
+ if (unlikely(!(ocd->ocd_connect_flags & OBD_CONNECT_FID)) &&
+ fid_seq_is_echo(ostid_seq(&lobdo->o_oi))) {
+ /* Currently OBD_FL_OSTID will only be used when 2.4 echo
+ * client communicate with pre-2.4 server */
+ wobdo->o_oi.oi.oi_id = fid_oid(&lobdo->o_oi.oi_fid);
+ wobdo->o_oi.oi.oi_seq = fid_seq(&lobdo->o_oi.oi_fid);
+ }
+}
+
+static inline void lustre_get_wire_obdo(struct obd_connect_data *ocd,
+ struct obdo *lobdo,
+ const struct obdo *wobdo)
+{
+ __u32 local_flags = 0;
+
+ if (lobdo->o_valid & OBD_MD_FLFLAGS)
+ local_flags = lobdo->o_flags & OBD_FL_LOCAL_MASK;
+
+ *lobdo = *wobdo;
+ if (local_flags != 0) {
+ lobdo->o_valid |= OBD_MD_FLFLAGS;
+ lobdo->o_flags &= ~OBD_FL_LOCAL_MASK;
+ lobdo->o_flags |= local_flags;
+ }
+ if (ocd == NULL)
+ return;
+
+ if (unlikely(!(ocd->ocd_connect_flags & OBD_CONNECT_FID)) &&
+ fid_seq_is_echo(wobdo->o_oi.oi.oi_seq)) {
+ /* see above */
+ lobdo->o_oi.oi_fid.f_seq = wobdo->o_oi.oi.oi_seq;
+ lobdo->o_oi.oi_fid.f_oid = wobdo->o_oi.oi.oi_id;
+ lobdo->o_oi.oi_fid.f_ver = 0;
+ }
+}
+
+extern void lustre_swab_obdo (struct obdo *o);
+
+/* request structure for OST's */
+struct ost_body {
+ struct obdo oa;
+};
+
+/* Key for FIEMAP to be used in get_info calls */
+struct ll_fiemap_info_key {
+ char name[8];
+ struct obdo oa;
+ struct ll_user_fiemap fiemap;
+};
+
+extern void lustre_swab_ost_body (struct ost_body *b);
+extern void lustre_swab_ost_last_id(__u64 *id);
+extern void lustre_swab_fiemap(struct ll_user_fiemap *fiemap);
+
+extern void lustre_swab_lov_user_md_v1(struct lov_user_md_v1 *lum);
+extern void lustre_swab_lov_user_md_v3(struct lov_user_md_v3 *lum);
+extern void lustre_swab_lov_user_md_objects(struct lov_user_ost_data *lod,
+ int stripe_count);
+extern void lustre_swab_lov_mds_md(struct lov_mds_md *lmm);
+
+/* llog_swab.c */
+extern void lustre_swab_llogd_body (struct llogd_body *d);
+extern void lustre_swab_llog_hdr (struct llog_log_hdr *h);
+extern void lustre_swab_llogd_conn_body (struct llogd_conn_body *d);
+extern void lustre_swab_llog_rec(struct llog_rec_hdr *rec);
+extern void lustre_swab_llog_id(struct llog_logid *lid);
+
+struct lustre_cfg;
+extern void lustre_swab_lustre_cfg(struct lustre_cfg *lcfg);
+
+/* Functions for dumping PTLRPC fields */
+void dump_rniobuf(struct niobuf_remote *rnb);
+void dump_ioo(struct obd_ioobj *nb);
+void dump_obdo(struct obdo *oa);
+void dump_ost_body(struct ost_body *ob);
+void dump_rcs(__u32 *rc);
+
+#define IDX_INFO_MAGIC 0x3D37CC37
+
+/* Index file transfer through the network. The server serializes the index into
+ * a byte stream which is sent to the client via a bulk transfer */
+struct idx_info {
+ __u32 ii_magic;
+
+ /* reply: see idx_info_flags below */
+ __u32 ii_flags;
+
+ /* request & reply: number of lu_idxpage (to be) transferred */
+ __u16 ii_count;
+ __u16 ii_pad0;
+
+ /* request: requested attributes passed down to the iterator API */
+ __u32 ii_attrs;
+
+ /* request & reply: index file identifier (FID) */
+ struct lu_fid ii_fid;
+
+ /* reply: version of the index file before starting to walk the index.
+ * Please note that the version can be modified at any time during the
+ * transfer */
+ __u64 ii_version;
+
+ /* request: hash to start with:
+ * reply: hash of the first entry of the first lu_idxpage and hash
+ * of the entry to read next if any */
+ __u64 ii_hash_start;
+ __u64 ii_hash_end;
+
+ /* reply: size of keys in lu_idxpages, minimal one if II_FL_VARKEY is
+ * set */
+ __u16 ii_keysize;
+
+ /* reply: size of records in lu_idxpages, minimal one if II_FL_VARREC
+ * is set */
+ __u16 ii_recsize;
+
+ __u32 ii_pad1;
+ __u64 ii_pad2;
+ __u64 ii_pad3;
+};
+extern void lustre_swab_idx_info(struct idx_info *ii);
+
+#define II_END_OFF MDS_DIR_END_OFF /* all entries have been read */
+
+/* List of flags used in idx_info::ii_flags */
+enum idx_info_flags {
+ II_FL_NOHASH = 1 << 0, /* client doesn't care about hash value */
+ II_FL_VARKEY = 1 << 1, /* keys can be of variable size */
+ II_FL_VARREC = 1 << 2, /* records can be of variable size */
+ II_FL_NONUNQ = 1 << 3, /* index supports non-unique keys */
+};
+
+#define LIP_MAGIC 0x8A6D6B6C
+
+/* 4KB (= LU_PAGE_SIZE) container gathering key/record pairs */
+struct lu_idxpage {
+ /* 16-byte header */
+ __u32 lip_magic;
+ __u16 lip_flags;
+ __u16 lip_nr; /* number of entries in the container */
+ __u64 lip_pad0; /* additional padding for future use */
+
+ /* key/record pairs are stored in the remaining 4080 bytes.
+ * depending upon the flags in idx_info::ii_flags, each key/record
+ * pair might be preceded by:
+ * - a hash value
+ * - the key size (II_FL_VARKEY is set)
+ * - the record size (II_FL_VARREC is set)
+ *
+ * For the time being, we only support fixed-size key & record. */
+ char lip_entries[0];
+};
+extern void lustre_swab_lip_header(struct lu_idxpage *lip);
+
+#define LIP_HDR_SIZE (offsetof(struct lu_idxpage, lip_entries))
+
+/* Gather all possible type associated with a 4KB container */
+union lu_page {
+ struct lu_dirpage lp_dir; /* for MDS_READPAGE */
+ struct lu_idxpage lp_idx; /* for OBD_IDX_READ */
+ char lp_array[LU_PAGE_SIZE];
+};
+
+/* security opcodes */
+typedef enum {
+ SEC_CTX_INIT = 801,
+ SEC_CTX_INIT_CONT = 802,
+ SEC_CTX_FINI = 803,
+ SEC_LAST_OPC,
+ SEC_FIRST_OPC = SEC_CTX_INIT
+} sec_cmd_t;
+
+/*
+ * capa related definitions
+ */
+#define CAPA_HMAC_MAX_LEN 64
+#define CAPA_HMAC_KEY_MAX_LEN 56
+
+/* NB take care when changing the sequence of elements this struct,
+ * because the offset info is used in find_capa() */
+struct lustre_capa {
+ struct lu_fid lc_fid; /** fid */
+ __u64 lc_opc; /** operations allowed */
+ __u64 lc_uid; /** file owner */
+ __u64 lc_gid; /** file group */
+ __u32 lc_flags; /** HMAC algorithm & flags */
+ __u32 lc_keyid; /** key# used for the capability */
+ __u32 lc_timeout; /** capa timeout value (sec) */
+ __u32 lc_expiry; /** expiry time (sec) */
+ __u8 lc_hmac[CAPA_HMAC_MAX_LEN]; /** HMAC */
+} __attribute__((packed));
+
+extern void lustre_swab_lustre_capa(struct lustre_capa *c);
+
+/** lustre_capa::lc_opc */
+enum {
+ CAPA_OPC_BODY_WRITE = 1<<0, /**< write object data */
+ CAPA_OPC_BODY_READ = 1<<1, /**< read object data */
+ CAPA_OPC_INDEX_LOOKUP = 1<<2, /**< lookup object fid */
+ CAPA_OPC_INDEX_INSERT = 1<<3, /**< insert object fid */
+ CAPA_OPC_INDEX_DELETE = 1<<4, /**< delete object fid */
+ CAPA_OPC_OSS_WRITE = 1<<5, /**< write oss object data */
+ CAPA_OPC_OSS_READ = 1<<6, /**< read oss object data */
+ CAPA_OPC_OSS_TRUNC = 1<<7, /**< truncate oss object */
+ CAPA_OPC_OSS_DESTROY = 1<<8, /**< destroy oss object */
+ CAPA_OPC_META_WRITE = 1<<9, /**< write object meta data */
+ CAPA_OPC_META_READ = 1<<10, /**< read object meta data */
+};
+
+#define CAPA_OPC_OSS_RW (CAPA_OPC_OSS_READ | CAPA_OPC_OSS_WRITE)
+#define CAPA_OPC_MDS_ONLY \
+ (CAPA_OPC_BODY_WRITE | CAPA_OPC_BODY_READ | CAPA_OPC_INDEX_LOOKUP | \
+ CAPA_OPC_INDEX_INSERT | CAPA_OPC_INDEX_DELETE)
+#define CAPA_OPC_OSS_ONLY \
+ (CAPA_OPC_OSS_WRITE | CAPA_OPC_OSS_READ | CAPA_OPC_OSS_TRUNC | \
+ CAPA_OPC_OSS_DESTROY)
+#define CAPA_OPC_MDS_DEFAULT ~CAPA_OPC_OSS_ONLY
+#define CAPA_OPC_OSS_DEFAULT ~(CAPA_OPC_MDS_ONLY | CAPA_OPC_OSS_ONLY)
+
+/* MDS capability covers object capability for operations of body r/w
+ * (dir readpage/sendpage), index lookup/insert/delete and meta data r/w,
+ * while OSS capability only covers object capability for operations of
+ * oss data(file content) r/w/truncate.
+ */
+static inline int capa_for_mds(struct lustre_capa *c)
+{
+ return (c->lc_opc & CAPA_OPC_INDEX_LOOKUP) != 0;
+}
+
+static inline int capa_for_oss(struct lustre_capa *c)
+{
+ return (c->lc_opc & CAPA_OPC_INDEX_LOOKUP) == 0;
+}
+
+/* lustre_capa::lc_hmac_alg */
+enum {
+ CAPA_HMAC_ALG_SHA1 = 1, /**< sha1 algorithm */
+ CAPA_HMAC_ALG_MAX,
+};
+
+#define CAPA_FL_MASK 0x00ffffff
+#define CAPA_HMAC_ALG_MASK 0xff000000
+
+struct lustre_capa_key {
+ __u64 lk_seq; /**< mds# */
+ __u32 lk_keyid; /**< key# */
+ __u32 lk_padding;
+ __u8 lk_key[CAPA_HMAC_KEY_MAX_LEN]; /**< key */
+} __attribute__((packed));
+
+extern void lustre_swab_lustre_capa_key(struct lustre_capa_key *k);
+
+/** The link ea holds 1 \a link_ea_entry for each hardlink */
+#define LINK_EA_MAGIC 0x11EAF1DFUL
+struct link_ea_header {
+ __u32 leh_magic;
+ __u32 leh_reccount;
+ __u64 leh_len; /* total size */
+ /* future use */
+ __u32 padding1;
+ __u32 padding2;
+};
+
+/** Hardlink data is name and parent fid.
+ * Stored in this crazy struct for maximum packing and endian-neutrality
+ */
+struct link_ea_entry {
+ /** __u16 stored big-endian, unaligned */
+ unsigned char lee_reclen[2];
+ unsigned char lee_parent_fid[sizeof(struct lu_fid)];
+ char lee_name[0];
+}__attribute__((packed));
+
+/** fid2path request/reply structure */
+struct getinfo_fid2path {
+ struct lu_fid gf_fid;
+ __u64 gf_recno;
+ __u32 gf_linkno;
+ __u32 gf_pathlen;
+ char gf_path[0];
+} __attribute__((packed));
+
+void lustre_swab_fid2path (struct getinfo_fid2path *gf);
+
+enum {
+ LAYOUT_INTENT_ACCESS = 0,
+ LAYOUT_INTENT_READ = 1,
+ LAYOUT_INTENT_WRITE = 2,
+ LAYOUT_INTENT_GLIMPSE = 3,
+ LAYOUT_INTENT_TRUNC = 4,
+ LAYOUT_INTENT_RELEASE = 5,
+ LAYOUT_INTENT_RESTORE = 6
+};
+
+/* enqueue layout lock with intent */
+struct layout_intent {
+ __u32 li_opc; /* intent operation for enqueue, read, write etc */
+ __u32 li_flags;
+ __u64 li_start;
+ __u64 li_end;
+};
+
+void lustre_swab_layout_intent(struct layout_intent *li);
+
+/**
+ * On the wire version of hsm_progress structure.
+ *
+ * Contains the userspace hsm_progress and some internal fields.
+ */
+struct hsm_progress_kernel {
+ /* Field taken from struct hsm_progress */
+ lustre_fid hpk_fid;
+ __u64 hpk_cookie;
+ struct hsm_extent hpk_extent;
+ __u16 hpk_flags;
+ __u16 hpk_errval; /* positive val */
+ __u32 hpk_padding1;
+ /* Additional fields */
+ __u64 hpk_data_version;
+ __u64 hpk_padding2;
+} __attribute__((packed));
+
+extern void lustre_swab_hsm_user_state(struct hsm_user_state *hus);
+extern void lustre_swab_hsm_current_action(struct hsm_current_action *action);
+extern void lustre_swab_hsm_progress_kernel(struct hsm_progress_kernel *hpk);
+extern void lustre_swab_hsm_user_state(struct hsm_user_state *hus);
+extern void lustre_swab_hsm_user_item(struct hsm_user_item *hui);
+extern void lustre_swab_hsm_request(struct hsm_request *hr);
+
+/**
+ * These are object update opcode under UPDATE_OBJ, which is currently
+ * being used by cross-ref operations between MDT.
+ *
+ * During the cross-ref operation, the Master MDT, which the client send the
+ * request to, will disassembly the operation into object updates, then OSP
+ * will send these updates to the remote MDT to be executed.
+ *
+ * Update request format
+ * magic: UPDATE_BUFFER_MAGIC_V1
+ * Count: How many updates in the req.
+ * bufs[0] : following are packets of object.
+ * update[0]:
+ * type: object_update_op, the op code of update
+ * fid: The object fid of the update.
+ * lens/bufs: other parameters of the update.
+ * update[1]:
+ * type: object_update_op, the op code of update
+ * fid: The object fid of the update.
+ * lens/bufs: other parameters of the update.
+ * ..........
+ * update[7]: type: object_update_op, the op code of update
+ * fid: The object fid of the update.
+ * lens/bufs: other parameters of the update.
+ * Current 8 maxim updates per object update request.
+ *
+ *******************************************************************
+ * update reply format:
+ *
+ * ur_version: UPDATE_REPLY_V1
+ * ur_count: The count of the reply, which is usually equal
+ * to the number of updates in the request.
+ * ur_lens: The reply lengths of each object update.
+ *
+ * replies: 1st update reply [4bytes_ret: other body]
+ * 2nd update reply [4bytes_ret: other body]
+ * .....
+ * nth update reply [4bytes_ret: other body]
+ *
+ * For each reply of the update, the format would be
+ * result(4 bytes):Other stuff
+ */
+
+#define UPDATE_MAX_OPS 10
+#define UPDATE_BUFFER_MAGIC_V1 0xBDDE0001
+#define UPDATE_BUFFER_MAGIC UPDATE_BUFFER_MAGIC_V1
+#define UPDATE_BUF_COUNT 8
+enum object_update_op {
+ OBJ_CREATE = 1,
+ OBJ_DESTROY = 2,
+ OBJ_REF_ADD = 3,
+ OBJ_REF_DEL = 4,
+ OBJ_ATTR_SET = 5,
+ OBJ_ATTR_GET = 6,
+ OBJ_XATTR_SET = 7,
+ OBJ_XATTR_GET = 8,
+ OBJ_INDEX_LOOKUP = 9,
+ OBJ_INDEX_INSERT = 10,
+ OBJ_INDEX_DELETE = 11,
+ OBJ_LAST
+};
+
+struct update {
+ __u32 u_type;
+ __u32 u_batchid;
+ struct lu_fid u_fid;
+ __u32 u_lens[UPDATE_BUF_COUNT];
+ __u32 u_bufs[0];
+};
+
+struct update_buf {
+ __u32 ub_magic;
+ __u32 ub_count;
+ __u32 ub_bufs[0];
+};
+
+#define UPDATE_REPLY_V1 0x00BD0001
+struct update_reply {
+ __u32 ur_version;
+ __u32 ur_count;
+ __u32 ur_lens[0];
+};
+
+void lustre_swab_update_buf(struct update_buf *ub);
+void lustre_swab_update_reply_buf(struct update_reply *ur);
+
+/** layout swap request structure
+ * fid1 and fid2 are in mdt_body
+ */
+struct mdc_swap_layouts {
+ __u64 msl_flags;
+} __packed;
+
+void lustre_swab_swap_layouts(struct mdc_swap_layouts *msl);
+
+struct close_data {
+ struct lustre_handle cd_handle;
+ struct lu_fid cd_fid;
+ __u64 cd_data_version;
+ __u64 cd_reserved[8];
+};
+
+void lustre_swab_close_data(struct close_data *data);
+
+#endif
+/** @} lustreidl */
diff --git a/drivers/staging/lustre/lustre/include/lustre/lustre_lfsck_user.h b/drivers/staging/lustre/lustre/include/lustre/lustre_lfsck_user.h
new file mode 100644
index 000000000..1c87a61a7
--- /dev/null
+++ b/drivers/staging/lustre/lustre/include/lustre/lustre_lfsck_user.h
@@ -0,0 +1,95 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+
+ * 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 version 2 for more details. A copy is
+ * included in the COPYING file that accompanied this code.
+
+ * 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
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2012, Intel Corporation.
+ */
+/*
+ * lustre/include/lustre/lustre_lfsck_user.h
+ *
+ * Lustre LFSCK userspace interfaces.
+ *
+ * Author: Fan Yong <yong.fan@whamcloud.com>
+ */
+
+#ifndef _LUSTRE_LFSCK_USER_H
+# define _LUSTRE_LFSCK_USER_H
+
+enum lfsck_param_flags {
+ /* Reset LFSCK iterator position to the device beginning. */
+ LPF_RESET = 0x0001,
+
+ /* Exit when fail. */
+ LPF_FAILOUT = 0x0002,
+
+ /* Dryrun mode, only check without modification */
+ LPF_DRYRUN = 0x0004,
+};
+
+enum lfsck_type {
+ /* For MDT-OST consistency check/repair. */
+ LT_LAYOUT = 0x0001,
+
+ /* For MDT-MDT consistency check/repair. */
+ LT_DNE = 0x0002,
+
+ /* For FID-in-dirent and linkEA consistency check/repair. */
+ LT_NAMESPACE = 0x0004,
+};
+
+#define LFSCK_VERSION_V1 1
+#define LFSCK_VERSION_V2 2
+
+#define LFSCK_TYPES_ALL ((__u16)(~0))
+#define LFSCK_TYPES_DEF ((__u16)0)
+#define LFSCK_TYPES_SUPPORTED LT_NAMESPACE
+
+#define LFSCK_SPEED_NO_LIMIT 0
+#define LFSCK_SPEED_LIMIT_DEF LFSCK_SPEED_NO_LIMIT
+
+enum lfsck_start_valid {
+ LSV_SPEED_LIMIT = 0x00000001,
+ LSV_ERROR_HANDLE = 0x00000002,
+ LSV_DRYRUN = 0x00000004,
+};
+
+/* Arguments for starting lfsck. */
+struct lfsck_start {
+ /* Which arguments are valid, see 'enum lfsck_start_valid'. */
+ __u32 ls_valid;
+
+ /* How many items can be scanned at most per second. */
+ __u32 ls_speed_limit;
+
+ /* For compatibility between user space tools and kernel service. */
+ __u16 ls_version;
+
+ /* Which LFSCK components to be (have been) started. */
+ __u16 ls_active;
+
+ /* Flags for the LFSCK, see 'enum lfsck_param_flags'. */
+ __u16 ls_flags;
+
+ /* For 64-bits aligned. */
+ __u16 ls_padding;
+};
+
+#endif /* _LUSTRE_LFSCK_USER_H */
diff --git a/drivers/staging/lustre/lustre/include/lustre/lustre_user.h b/drivers/staging/lustre/lustre/include/lustre/lustre_user.h
new file mode 100644
index 000000000..89794fdfe
--- /dev/null
+++ b/drivers/staging/lustre/lustre/include/lustre/lustre_user.h
@@ -0,0 +1,1179 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2010, 2012, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ *
+ * lustre/include/lustre/lustre_user.h
+ *
+ * Lustre public user-space interface definitions.
+ */
+
+#ifndef _LUSTRE_USER_H
+#define _LUSTRE_USER_H
+
+/** \defgroup lustreuser lustreuser
+ *
+ * @{
+ */
+
+#include "ll_fiemap.h"
+#include "../linux/lustre_user.h"
+
+/* for statfs() */
+#define LL_SUPER_MAGIC 0x0BD00BD0
+
+#ifndef FSFILT_IOC_GETFLAGS
+#define FSFILT_IOC_GETFLAGS _IOR('f', 1, long)
+#define FSFILT_IOC_SETFLAGS _IOW('f', 2, long)
+#define FSFILT_IOC_GETVERSION _IOR('f', 3, long)
+#define FSFILT_IOC_SETVERSION _IOW('f', 4, long)
+#define FSFILT_IOC_GETVERSION_OLD _IOR('v', 1, long)
+#define FSFILT_IOC_SETVERSION_OLD _IOW('v', 2, long)
+#define FSFILT_IOC_FIEMAP _IOWR('f', 11, struct ll_user_fiemap)
+#endif
+
+/* FIEMAP flags supported by Lustre */
+#define LUSTRE_FIEMAP_FLAGS_COMPAT (FIEMAP_FLAG_SYNC | FIEMAP_FLAG_DEVICE_ORDER)
+
+enum obd_statfs_state {
+ OS_STATE_DEGRADED = 0x00000001, /**< RAID degraded/rebuilding */
+ OS_STATE_READONLY = 0x00000002, /**< filesystem is read-only */
+ OS_STATE_RDONLY_1 = 0x00000004, /**< obsolete 1.6, was EROFS=30 */
+ OS_STATE_RDONLY_2 = 0x00000008, /**< obsolete 1.6, was EROFS=30 */
+ OS_STATE_RDONLY_3 = 0x00000010, /**< obsolete 1.6, was EROFS=30 */
+};
+
+struct obd_statfs {
+ __u64 os_type;
+ __u64 os_blocks;
+ __u64 os_bfree;
+ __u64 os_bavail;
+ __u64 os_files;
+ __u64 os_ffree;
+ __u8 os_fsid[40];
+ __u32 os_bsize;
+ __u32 os_namelen;
+ __u64 os_maxbytes;
+ __u32 os_state; /**< obd_statfs_state OS_STATE_* flag */
+ __u32 os_fprecreated; /* objs available now to the caller */
+ /* used in QoS code to find preferred
+ * OSTs */
+ __u32 os_spare2;
+ __u32 os_spare3;
+ __u32 os_spare4;
+ __u32 os_spare5;
+ __u32 os_spare6;
+ __u32 os_spare7;
+ __u32 os_spare8;
+ __u32 os_spare9;
+};
+
+/**
+ * File IDentifier.
+ *
+ * FID is a cluster-wide unique identifier of a file or an object (stripe).
+ * FIDs are never reused.
+ **/
+struct lu_fid {
+ /**
+ * FID sequence. Sequence is a unit of migration: all files (objects)
+ * with FIDs from a given sequence are stored on the same server.
+ * Lustre should support 2^64 objects, so even if each sequence
+ * has only a single object we can still enumerate 2^64 objects.
+ **/
+ __u64 f_seq;
+ /* FID number within sequence. */
+ __u32 f_oid;
+ /**
+ * FID version, used to distinguish different versions (in the sense
+ * of snapshots, etc.) of the same file system object. Not currently
+ * used.
+ **/
+ __u32 f_ver;
+};
+
+struct filter_fid {
+ struct lu_fid ff_parent; /* ff_parent.f_ver == file stripe number */
+};
+
+/* keep this one for compatibility */
+struct filter_fid_old {
+ struct lu_fid ff_parent;
+ __u64 ff_objid;
+ __u64 ff_seq;
+};
+
+/* Userspace should treat lu_fid as opaque, and only use the following methods
+ * to print or parse them. Other functions (e.g. compare, swab) could be moved
+ * here from lustre_idl.h if needed. */
+typedef struct lu_fid lustre_fid;
+
+/**
+ * Following struct for object attributes, that will be kept inode's EA.
+ * Introduced in 2.0 release (please see b15993, for details)
+ * Added to all objects since Lustre 2.4 as contains self FID
+ */
+struct lustre_mdt_attrs {
+ /**
+ * Bitfield for supported data in this structure. From enum lma_compat.
+ * lma_self_fid and lma_flags are always available.
+ */
+ __u32 lma_compat;
+ /**
+ * Per-file incompat feature list. Lustre version should support all
+ * flags set in this field. The supported feature mask is available in
+ * LMA_INCOMPAT_SUPP.
+ */
+ __u32 lma_incompat;
+ /** FID of this inode */
+ struct lu_fid lma_self_fid;
+};
+
+/**
+ * Prior to 2.4, the LMA structure also included SOM attributes which has since
+ * been moved to a dedicated xattr
+ * lma_flags was also removed because of lma_compat/incompat fields.
+ */
+#define LMA_OLD_SIZE (sizeof(struct lustre_mdt_attrs) + 5 * sizeof(__u64))
+
+/**
+ * OST object IDentifier.
+ */
+struct ost_id {
+ union {
+ struct ostid {
+ __u64 oi_id;
+ __u64 oi_seq;
+ } oi;
+ struct lu_fid oi_fid;
+ };
+};
+
+#define DOSTID "%#llx:%llu"
+#define POSTID(oi) ostid_seq(oi), ostid_id(oi)
+
+/*
+ * The ioctl naming rules:
+ * LL_* - works on the currently opened filehandle instead of parent dir
+ * *_OBD_* - gets data for both OSC or MDC (LOV, LMV indirectly)
+ * *_MDC_* - gets/sets data related to MDC
+ * *_LOV_* - gets/sets data related to OSC/LOV
+ * *FILE* - called on parent dir and passes in a filename
+ * *STRIPE* - set/get lov_user_md
+ * *INFO - set/get lov_user_mds_data
+ */
+/* see <lustre_lib.h> for ioctl numberss 101-150 */
+#define LL_IOC_GETFLAGS _IOR ('f', 151, long)
+#define LL_IOC_SETFLAGS _IOW ('f', 152, long)
+#define LL_IOC_CLRFLAGS _IOW ('f', 153, long)
+/* LL_IOC_LOV_SETSTRIPE: See also OBD_IOC_LOV_SETSTRIPE */
+#define LL_IOC_LOV_SETSTRIPE _IOW ('f', 154, long)
+/* LL_IOC_LOV_GETSTRIPE: See also OBD_IOC_LOV_GETSTRIPE */
+#define LL_IOC_LOV_GETSTRIPE _IOW ('f', 155, long)
+/* LL_IOC_LOV_SETEA: See also OBD_IOC_LOV_SETEA */
+#define LL_IOC_LOV_SETEA _IOW ('f', 156, long)
+#define LL_IOC_RECREATE_OBJ _IOW ('f', 157, long)
+#define LL_IOC_RECREATE_FID _IOW ('f', 157, struct lu_fid)
+#define LL_IOC_GROUP_LOCK _IOW ('f', 158, long)
+#define LL_IOC_GROUP_UNLOCK _IOW ('f', 159, long)
+/* LL_IOC_QUOTACHECK: See also OBD_IOC_QUOTACHECK */
+#define LL_IOC_QUOTACHECK _IOW ('f', 160, int)
+/* LL_IOC_POLL_QUOTACHECK: See also OBD_IOC_POLL_QUOTACHECK */
+#define LL_IOC_POLL_QUOTACHECK _IOR ('f', 161, struct if_quotacheck *)
+/* LL_IOC_QUOTACTL: See also OBD_IOC_QUOTACTL */
+#define LL_IOC_QUOTACTL _IOWR('f', 162, struct if_quotactl)
+#define IOC_OBD_STATFS _IOWR('f', 164, struct obd_statfs *)
+#define IOC_LOV_GETINFO _IOWR('f', 165, struct lov_user_mds_data *)
+#define LL_IOC_FLUSHCTX _IOW ('f', 166, long)
+#define LL_IOC_RMTACL _IOW ('f', 167, long)
+#define LL_IOC_GETOBDCOUNT _IOR ('f', 168, long)
+#define LL_IOC_LLOOP_ATTACH _IOWR('f', 169, long)
+#define LL_IOC_LLOOP_DETACH _IOWR('f', 170, long)
+#define LL_IOC_LLOOP_INFO _IOWR('f', 171, struct lu_fid)
+#define LL_IOC_LLOOP_DETACH_BYDEV _IOWR('f', 172, long)
+#define LL_IOC_PATH2FID _IOR ('f', 173, long)
+#define LL_IOC_GET_CONNECT_FLAGS _IOWR('f', 174, __u64 *)
+#define LL_IOC_GET_MDTIDX _IOR ('f', 175, int)
+
+/* see <lustre_lib.h> for ioctl numbers 177-210 */
+
+#define LL_IOC_HSM_STATE_GET _IOR('f', 211, struct hsm_user_state)
+#define LL_IOC_HSM_STATE_SET _IOW('f', 212, struct hsm_state_set)
+#define LL_IOC_HSM_CT_START _IOW('f', 213, struct lustre_kernelcomm)
+#define LL_IOC_HSM_COPY_START _IOW('f', 214, struct hsm_copy *)
+#define LL_IOC_HSM_COPY_END _IOW('f', 215, struct hsm_copy *)
+#define LL_IOC_HSM_PROGRESS _IOW('f', 216, struct hsm_user_request)
+#define LL_IOC_HSM_REQUEST _IOW('f', 217, struct hsm_user_request)
+#define LL_IOC_DATA_VERSION _IOR('f', 218, struct ioc_data_version)
+#define LL_IOC_LOV_SWAP_LAYOUTS _IOW('f', 219, \
+ struct lustre_swap_layouts)
+#define LL_IOC_HSM_ACTION _IOR('f', 220, \
+ struct hsm_current_action)
+/* see <lustre_lib.h> for ioctl numbers 221-232 */
+
+#define LL_IOC_LMV_SETSTRIPE _IOWR('f', 240, struct lmv_user_md)
+#define LL_IOC_LMV_GETSTRIPE _IOWR('f', 241, struct lmv_user_md)
+#define LL_IOC_REMOVE_ENTRY _IOWR('f', 242, __u64)
+#define LL_IOC_SET_LEASE _IOWR('f', 243, long)
+#define LL_IOC_GET_LEASE _IO('f', 244)
+#define LL_IOC_HSM_IMPORT _IOWR('f', 245, struct hsm_user_import)
+
+#define LL_STATFS_LMV 1
+#define LL_STATFS_LOV 2
+#define LL_STATFS_NODELAY 4
+
+#define IOC_MDC_TYPE 'i'
+#define IOC_MDC_LOOKUP _IOWR(IOC_MDC_TYPE, 20, struct obd_device *)
+#define IOC_MDC_GETFILESTRIPE _IOWR(IOC_MDC_TYPE, 21, struct lov_user_md *)
+#define IOC_MDC_GETFILEINFO _IOWR(IOC_MDC_TYPE, 22, struct lov_user_mds_data *)
+#define LL_IOC_MDC_GETINFO _IOWR(IOC_MDC_TYPE, 23, struct lov_user_mds_data *)
+
+/* Keep these for backward compartability. */
+#define LL_IOC_OBD_STATFS IOC_OBD_STATFS
+#define IOC_MDC_GETSTRIPE IOC_MDC_GETFILESTRIPE
+
+
+#define MAX_OBD_NAME 128 /* If this changes, a NEW ioctl must be added */
+
+/* Define O_LOV_DELAY_CREATE to be a mask that is not useful for regular
+ * files, but are unlikely to be used in practice and are not harmful if
+ * used incorrectly. O_NOCTTY and FASYNC are only meaningful for character
+ * devices and are safe for use on new files (See LU-812, LU-4209). */
+#define O_LOV_DELAY_CREATE (O_NOCTTY | FASYNC)
+
+#define LL_FILE_IGNORE_LOCK 0x00000001
+#define LL_FILE_GROUP_LOCKED 0x00000002
+#define LL_FILE_READAHEA 0x00000004
+#define LL_FILE_LOCKED_DIRECTIO 0x00000008 /* client-side locks with dio */
+#define LL_FILE_LOCKLESS_IO 0x00000010 /* server-side locks with cio */
+#define LL_FILE_RMTACL 0x00000020
+
+#define LOV_USER_MAGIC_V1 0x0BD10BD0
+#define LOV_USER_MAGIC LOV_USER_MAGIC_V1
+#define LOV_USER_MAGIC_JOIN_V1 0x0BD20BD0
+#define LOV_USER_MAGIC_V3 0x0BD30BD0
+
+#define LMV_MAGIC_V1 0x0CD10CD0 /*normal stripe lmv magic */
+#define LMV_USER_MAGIC 0x0CD20CD0 /*default lmv magic*/
+
+#define LOV_PATTERN_RAID0 0x001
+#define LOV_PATTERN_RAID1 0x002
+#define LOV_PATTERN_FIRST 0x100
+
+#define LOV_MAXPOOLNAME 16
+#define LOV_POOLNAMEF "%.16s"
+
+#define LOV_MIN_STRIPE_BITS 16 /* maximum PAGE_SIZE (ia64), power of 2 */
+#define LOV_MIN_STRIPE_SIZE (1 << LOV_MIN_STRIPE_BITS)
+#define LOV_MAX_STRIPE_COUNT_OLD 160
+/* This calculation is crafted so that input of 4096 will result in 160
+ * which in turn is equal to old maximal stripe count.
+ * XXX: In fact this is too simplified for now, what it also need is to get
+ * ea_type argument to clearly know how much space each stripe consumes.
+ *
+ * The limit of 12 pages is somewhat arbitrary, but is a reasonably large
+ * allocation that is sufficient for the current generation of systems.
+ *
+ * (max buffer size - lov+rpc header) / sizeof(struct lov_ost_data_v1) */
+#define LOV_MAX_STRIPE_COUNT 2000 /* ((12 * 4096 - 256) / 24) */
+#define LOV_ALL_STRIPES 0xffff /* only valid for directories */
+#define LOV_V1_INSANE_STRIPE_COUNT 65532 /* maximum stripe count bz13933 */
+
+#define lov_user_ost_data lov_user_ost_data_v1
+struct lov_user_ost_data_v1 { /* per-stripe data structure */
+ struct ost_id l_ost_oi; /* OST object ID */
+ __u32 l_ost_gen; /* generation of this OST index */
+ __u32 l_ost_idx; /* OST index in LOV */
+} __attribute__((packed));
+
+#define lov_user_md lov_user_md_v1
+struct lov_user_md_v1 { /* LOV EA user data (host-endian) */
+ __u32 lmm_magic; /* magic number = LOV_USER_MAGIC_V1 */
+ __u32 lmm_pattern; /* LOV_PATTERN_RAID0, LOV_PATTERN_RAID1 */
+ struct ost_id lmm_oi; /* LOV object ID */
+ __u32 lmm_stripe_size; /* size of stripe in bytes */
+ __u16 lmm_stripe_count; /* num stripes in use for this object */
+ union {
+ __u16 lmm_stripe_offset; /* starting stripe offset in
+ * lmm_objects, use when writing */
+ __u16 lmm_layout_gen; /* layout generation number
+ * used when reading */
+ };
+ struct lov_user_ost_data_v1 lmm_objects[0]; /* per-stripe data */
+} __attribute__((packed, __may_alias__));
+
+struct lov_user_md_v3 { /* LOV EA user data (host-endian) */
+ __u32 lmm_magic; /* magic number = LOV_USER_MAGIC_V3 */
+ __u32 lmm_pattern; /* LOV_PATTERN_RAID0, LOV_PATTERN_RAID1 */
+ struct ost_id lmm_oi; /* LOV object ID */
+ __u32 lmm_stripe_size; /* size of stripe in bytes */
+ __u16 lmm_stripe_count; /* num stripes in use for this object */
+ union {
+ __u16 lmm_stripe_offset; /* starting stripe offset in
+ * lmm_objects, use when writing */
+ __u16 lmm_layout_gen; /* layout generation number
+ * used when reading */
+ };
+ char lmm_pool_name[LOV_MAXPOOLNAME]; /* pool name */
+ struct lov_user_ost_data_v1 lmm_objects[0]; /* per-stripe data */
+} __attribute__((packed));
+
+static inline __u32 lov_user_md_size(__u16 stripes, __u32 lmm_magic)
+{
+ if (lmm_magic == LOV_USER_MAGIC_V3)
+ return sizeof(struct lov_user_md_v3) +
+ stripes * sizeof(struct lov_user_ost_data_v1);
+ else
+ return sizeof(struct lov_user_md_v1) +
+ stripes * sizeof(struct lov_user_ost_data_v1);
+}
+
+/* Compile with -D_LARGEFILE64_SOURCE or -D_GNU_SOURCE (or #define) to
+ * use this. It is unsafe to #define those values in this header as it
+ * is possible the application has already #included <sys/stat.h>. */
+#ifdef HAVE_LOV_USER_MDS_DATA
+#define lov_user_mds_data lov_user_mds_data_v1
+struct lov_user_mds_data_v1 {
+ lstat_t lmd_st; /* MDS stat struct */
+ struct lov_user_md_v1 lmd_lmm; /* LOV EA V1 user data */
+} __attribute__((packed));
+
+struct lov_user_mds_data_v3 {
+ lstat_t lmd_st; /* MDS stat struct */
+ struct lov_user_md_v3 lmd_lmm; /* LOV EA V3 user data */
+} __attribute__((packed));
+#endif
+
+/* keep this to be the same size as lov_user_ost_data_v1 */
+struct lmv_user_mds_data {
+ struct lu_fid lum_fid;
+ __u32 lum_padding;
+ __u32 lum_mds;
+};
+
+/* lum_type */
+enum {
+ LMV_STRIPE_TYPE = 0,
+ LMV_DEFAULT_TYPE = 1,
+};
+
+#define lmv_user_md lmv_user_md_v1
+struct lmv_user_md_v1 {
+ __u32 lum_magic; /* must be the first field */
+ __u32 lum_stripe_count; /* dirstripe count */
+ __u32 lum_stripe_offset; /* MDT idx for default dirstripe */
+ __u32 lum_hash_type; /* Dir stripe policy */
+ __u32 lum_type; /* LMV type: default or normal */
+ __u32 lum_padding1;
+ __u32 lum_padding2;
+ __u32 lum_padding3;
+ char lum_pool_name[LOV_MAXPOOLNAME];
+ struct lmv_user_mds_data lum_objects[0];
+};
+
+static inline int lmv_user_md_size(int stripes, int lmm_magic)
+{
+ return sizeof(struct lmv_user_md) +
+ stripes * sizeof(struct lmv_user_mds_data);
+}
+
+extern void lustre_swab_lmv_user_md(struct lmv_user_md *lum);
+
+struct ll_recreate_obj {
+ __u64 lrc_id;
+ __u32 lrc_ost_idx;
+};
+
+struct ll_fid {
+ __u64 id; /* holds object id */
+ __u32 generation; /* holds object generation */
+ __u32 f_type; /* holds object type or stripe idx when passing it to
+ * OST for saving into EA. */
+};
+
+#define UUID_MAX 40
+struct obd_uuid {
+ char uuid[UUID_MAX];
+};
+
+static inline bool obd_uuid_equals(const struct obd_uuid *u1,
+ const struct obd_uuid *u2)
+{
+ return strcmp((char *)u1->uuid, (char *)u2->uuid) == 0;
+}
+
+static inline int obd_uuid_empty(struct obd_uuid *uuid)
+{
+ return uuid->uuid[0] == '\0';
+}
+
+static inline void obd_str2uuid(struct obd_uuid *uuid, const char *tmp)
+{
+ strncpy((char *)uuid->uuid, tmp, sizeof(*uuid));
+ uuid->uuid[sizeof(*uuid) - 1] = '\0';
+}
+
+/* For printf's only, make sure uuid is terminated */
+static inline char *obd_uuid2str(const struct obd_uuid *uuid)
+{
+ if (uuid->uuid[sizeof(*uuid) - 1] != '\0') {
+ /* Obviously not safe, but for printfs, no real harm done...
+ we're always null-terminated, even in a race. */
+ static char temp[sizeof(*uuid)];
+ memcpy(temp, uuid->uuid, sizeof(*uuid) - 1);
+ temp[sizeof(*uuid) - 1] = '\0';
+ return temp;
+ }
+ return (char *)(uuid->uuid);
+}
+
+/* Extract fsname from uuid (or target name) of a target
+ e.g. (myfs-OST0007_UUID -> myfs)
+ see also deuuidify. */
+static inline void obd_uuid2fsname(char *buf, char *uuid, int buflen)
+{
+ char *p;
+
+ strncpy(buf, uuid, buflen - 1);
+ buf[buflen - 1] = '\0';
+ p = strrchr(buf, '-');
+ if (p)
+ *p = '\0';
+}
+
+/* printf display format
+ e.g. printf("file FID is "DFID"\n", PFID(fid)); */
+#define FID_NOBRACE_LEN 40
+#define FID_LEN (FID_NOBRACE_LEN + 2)
+#define DFID_NOBRACE "%#llx:0x%x:0x%x"
+#define DFID "["DFID_NOBRACE"]"
+#define PFID(fid) \
+ (fid)->f_seq, \
+ (fid)->f_oid, \
+ (fid)->f_ver
+
+/* scanf input parse format -- strip '[' first.
+ e.g. sscanf(fidstr, SFID, RFID(&fid)); */
+#define SFID "0x%llx:0x%x:0x%x"
+#define RFID(fid) \
+ &((fid)->f_seq), \
+ &((fid)->f_oid), \
+ &((fid)->f_ver)
+
+
+/********* Quotas **********/
+
+/* these must be explicitly translated into linux Q_* in ll_dir_ioctl */
+#define LUSTRE_Q_QUOTAON 0x800002 /* turn quotas on */
+#define LUSTRE_Q_QUOTAOFF 0x800003 /* turn quotas off */
+#define LUSTRE_Q_GETINFO 0x800005 /* get information about quota files */
+#define LUSTRE_Q_SETINFO 0x800006 /* set information about quota files */
+#define LUSTRE_Q_GETQUOTA 0x800007 /* get user quota structure */
+#define LUSTRE_Q_SETQUOTA 0x800008 /* set user quota structure */
+/* lustre-specific control commands */
+#define LUSTRE_Q_INVALIDATE 0x80000b /* invalidate quota data */
+#define LUSTRE_Q_FINVALIDATE 0x80000c /* invalidate filter quota data */
+
+#define UGQUOTA 2 /* set both USRQUOTA and GRPQUOTA */
+
+struct if_quotacheck {
+ char obd_type[16];
+ struct obd_uuid obd_uuid;
+};
+
+#define IDENTITY_DOWNCALL_MAGIC 0x6d6dd629
+
+/* permission */
+#define N_PERMS_MAX 64
+
+struct perm_downcall_data {
+ __u64 pdd_nid;
+ __u32 pdd_perm;
+ __u32 pdd_padding;
+};
+
+struct identity_downcall_data {
+ __u32 idd_magic;
+ __u32 idd_err;
+ __u32 idd_uid;
+ __u32 idd_gid;
+ __u32 idd_nperms;
+ __u32 idd_ngroups;
+ struct perm_downcall_data idd_perms[N_PERMS_MAX];
+ __u32 idd_groups[0];
+};
+
+/* for non-mapped uid/gid */
+#define NOBODY_UID 99
+#define NOBODY_GID 99
+
+#define INVALID_ID (-1)
+
+enum {
+ RMT_LSETFACL = 1,
+ RMT_LGETFACL = 2,
+ RMT_RSETFACL = 3,
+ RMT_RGETFACL = 4
+};
+
+#ifdef NEED_QUOTA_DEFS
+#ifndef QIF_BLIMITS
+#define QIF_BLIMITS 1
+#define QIF_SPACE 2
+#define QIF_ILIMITS 4
+#define QIF_INODES 8
+#define QIF_BTIME 16
+#define QIF_ITIME 32
+#define QIF_LIMITS (QIF_BLIMITS | QIF_ILIMITS)
+#define QIF_USAGE (QIF_SPACE | QIF_INODES)
+#define QIF_TIMES (QIF_BTIME | QIF_ITIME)
+#define QIF_ALL (QIF_LIMITS | QIF_USAGE | QIF_TIMES)
+#endif
+
+#endif /* !__KERNEL__ */
+
+/* lustre volatile file support
+ * file name header: .^L^S^T^R:volatile"
+ */
+#define LUSTRE_VOLATILE_HDR ".\x0c\x13\x14\x12:VOLATILE"
+#define LUSTRE_VOLATILE_HDR_LEN 14
+/* hdr + MDT index */
+#define LUSTRE_VOLATILE_IDX LUSTRE_VOLATILE_HDR":%.4X:"
+
+typedef enum lustre_quota_version {
+ LUSTRE_QUOTA_V2 = 1
+} lustre_quota_version_t;
+
+/* XXX: same as if_dqinfo struct in kernel */
+struct obd_dqinfo {
+ __u64 dqi_bgrace;
+ __u64 dqi_igrace;
+ __u32 dqi_flags;
+ __u32 dqi_valid;
+};
+
+/* XXX: same as if_dqblk struct in kernel, plus one padding */
+struct obd_dqblk {
+ __u64 dqb_bhardlimit;
+ __u64 dqb_bsoftlimit;
+ __u64 dqb_curspace;
+ __u64 dqb_ihardlimit;
+ __u64 dqb_isoftlimit;
+ __u64 dqb_curinodes;
+ __u64 dqb_btime;
+ __u64 dqb_itime;
+ __u32 dqb_valid;
+ __u32 dqb_padding;
+};
+
+enum {
+ QC_GENERAL = 0,
+ QC_MDTIDX = 1,
+ QC_OSTIDX = 2,
+ QC_UUID = 3
+};
+
+struct if_quotactl {
+ __u32 qc_cmd;
+ __u32 qc_type;
+ __u32 qc_id;
+ __u32 qc_stat;
+ __u32 qc_valid;
+ __u32 qc_idx;
+ struct obd_dqinfo qc_dqinfo;
+ struct obd_dqblk qc_dqblk;
+ char obd_type[16];
+ struct obd_uuid obd_uuid;
+};
+
+/* swap layout flags */
+#define SWAP_LAYOUTS_CHECK_DV1 (1 << 0)
+#define SWAP_LAYOUTS_CHECK_DV2 (1 << 1)
+#define SWAP_LAYOUTS_KEEP_MTIME (1 << 2)
+#define SWAP_LAYOUTS_KEEP_ATIME (1 << 3)
+
+/* Swap XATTR_NAME_HSM as well, only on the MDT so far */
+#define SWAP_LAYOUTS_MDS_HSM (1 << 31)
+struct lustre_swap_layouts {
+ __u64 sl_flags;
+ __u32 sl_fd;
+ __u32 sl_gid;
+ __u64 sl_dv1;
+ __u64 sl_dv2;
+};
+
+
+/********* Changelogs **********/
+/** Changelog record types */
+enum changelog_rec_type {
+ CL_MARK = 0,
+ CL_CREATE = 1, /* namespace */
+ CL_MKDIR = 2, /* namespace */
+ CL_HARDLINK = 3, /* namespace */
+ CL_SOFTLINK = 4, /* namespace */
+ CL_MKNOD = 5, /* namespace */
+ CL_UNLINK = 6, /* namespace */
+ CL_RMDIR = 7, /* namespace */
+ CL_RENAME = 8, /* namespace */
+ CL_EXT = 9, /* namespace extended record (2nd half of rename) */
+ CL_OPEN = 10, /* not currently used */
+ CL_CLOSE = 11, /* may be written to log only with mtime change */
+ CL_LAYOUT = 12, /* file layout/striping modified */
+ CL_TRUNC = 13,
+ CL_SETATTR = 14,
+ CL_XATTR = 15,
+ CL_HSM = 16, /* HSM specific events, see flags */
+ CL_MTIME = 17, /* Precedence: setattr > mtime > ctime > atime */
+ CL_CTIME = 18,
+ CL_ATIME = 19,
+ CL_LAST
+};
+
+static inline const char *changelog_type2str(int type) {
+ static const char *changelog_str[] = {
+ "MARK", "CREAT", "MKDIR", "HLINK", "SLINK", "MKNOD", "UNLNK",
+ "RMDIR", "RENME", "RNMTO", "OPEN", "CLOSE", "LYOUT", "TRUNC",
+ "SATTR", "XATTR", "HSM", "MTIME", "CTIME", "ATIME",
+ };
+
+ if (type >= 0 && type < CL_LAST)
+ return changelog_str[type];
+ return NULL;
+}
+
+/* per-record flags */
+#define CLF_VERSION 0x1000
+#define CLF_EXT_VERSION 0x2000
+#define CLF_FLAGSHIFT 12
+#define CLF_FLAGMASK ((1U << CLF_FLAGSHIFT) - 1)
+#define CLF_VERMASK (~CLF_FLAGMASK)
+/* Anything under the flagmask may be per-type (if desired) */
+/* Flags for unlink */
+#define CLF_UNLINK_LAST 0x0001 /* Unlink of last hardlink */
+#define CLF_UNLINK_HSM_EXISTS 0x0002 /* File has something in HSM */
+ /* HSM cleaning needed */
+/* Flags for rename */
+#define CLF_RENAME_LAST 0x0001 /* rename unlink last hardlink of target */
+
+/* Flags for HSM */
+/* 12b used (from high weight to low weight):
+ * 2b for flags
+ * 3b for event
+ * 7b for error code
+ */
+#define CLF_HSM_ERR_L 0 /* HSM return code, 7 bits */
+#define CLF_HSM_ERR_H 6
+#define CLF_HSM_EVENT_L 7 /* HSM event, 3 bits, see enum hsm_event */
+#define CLF_HSM_EVENT_H 9
+#define CLF_HSM_FLAG_L 10 /* HSM flags, 2 bits, 1 used, 1 spare */
+#define CLF_HSM_FLAG_H 11
+#define CLF_HSM_SPARE_L 12 /* 4 spare bits */
+#define CLF_HSM_SPARE_H 15
+#define CLF_HSM_LAST 15
+
+/* Remove bits higher than _h, then extract the value
+ * between _h and _l by shifting lower weigth to bit 0. */
+#define CLF_GET_BITS(_b, _h, _l) (((_b << (CLF_HSM_LAST - _h)) & 0xFFFF) \
+ >> (CLF_HSM_LAST - _h + _l))
+
+#define CLF_HSM_SUCCESS 0x00
+#define CLF_HSM_MAXERROR 0x7E
+#define CLF_HSM_ERROVERFLOW 0x7F
+
+#define CLF_HSM_DIRTY 1 /* file is dirty after HSM request end */
+
+/* 3 bits field => 8 values allowed */
+enum hsm_event {
+ HE_ARCHIVE = 0,
+ HE_RESTORE = 1,
+ HE_CANCEL = 2,
+ HE_RELEASE = 3,
+ HE_REMOVE = 4,
+ HE_STATE = 5,
+ HE_SPARE1 = 6,
+ HE_SPARE2 = 7,
+};
+
+static inline enum hsm_event hsm_get_cl_event(__u16 flags)
+{
+ return CLF_GET_BITS(flags, CLF_HSM_EVENT_H, CLF_HSM_EVENT_L);
+}
+
+static inline void hsm_set_cl_event(int *flags, enum hsm_event he)
+{
+ *flags |= (he << CLF_HSM_EVENT_L);
+}
+
+static inline __u16 hsm_get_cl_flags(int flags)
+{
+ return CLF_GET_BITS(flags, CLF_HSM_FLAG_H, CLF_HSM_FLAG_L);
+}
+
+static inline void hsm_set_cl_flags(int *flags, int bits)
+{
+ *flags |= (bits << CLF_HSM_FLAG_L);
+}
+
+static inline int hsm_get_cl_error(int flags)
+{
+ return CLF_GET_BITS(flags, CLF_HSM_ERR_H, CLF_HSM_ERR_L);
+}
+
+static inline void hsm_set_cl_error(int *flags, int error)
+{
+ *flags |= (error << CLF_HSM_ERR_L);
+}
+
+#define CR_MAXSIZE cfs_size_round(2*NAME_MAX + 1 + \
+ sizeof(struct changelog_ext_rec))
+
+struct changelog_rec {
+ __u16 cr_namelen;
+ __u16 cr_flags; /**< (flags&CLF_FLAGMASK)|CLF_VERSION */
+ __u32 cr_type; /**< \a changelog_rec_type */
+ __u64 cr_index; /**< changelog record number */
+ __u64 cr_prev; /**< last index for this target fid */
+ __u64 cr_time;
+ union {
+ lustre_fid cr_tfid; /**< target fid */
+ __u32 cr_markerflags; /**< CL_MARK flags */
+ };
+ lustre_fid cr_pfid; /**< parent fid */
+ char cr_name[0]; /**< last element */
+} __attribute__((packed));
+
+/* changelog_ext_rec is 2*sizeof(lu_fid) bigger than changelog_rec, to save
+ * space, only rename uses changelog_ext_rec, while others use changelog_rec to
+ * store records.
+ */
+struct changelog_ext_rec {
+ __u16 cr_namelen;
+ __u16 cr_flags; /**< (flags & CLF_FLAGMASK) |
+ CLF_EXT_VERSION */
+ __u32 cr_type; /**< \a changelog_rec_type */
+ __u64 cr_index; /**< changelog record number */
+ __u64 cr_prev; /**< last index for this target fid */
+ __u64 cr_time;
+ union {
+ lustre_fid cr_tfid; /**< target fid */
+ __u32 cr_markerflags; /**< CL_MARK flags */
+ };
+ lustre_fid cr_pfid; /**< target parent fid */
+ lustre_fid cr_sfid; /**< source fid, or zero */
+ lustre_fid cr_spfid; /**< source parent fid, or zero */
+ char cr_name[0]; /**< last element */
+} __attribute__((packed));
+
+#define CHANGELOG_REC_EXTENDED(rec) \
+ (((rec)->cr_flags & CLF_VERMASK) == CLF_EXT_VERSION)
+
+static inline int changelog_rec_size(struct changelog_rec *rec)
+{
+ return CHANGELOG_REC_EXTENDED(rec) ? sizeof(struct changelog_ext_rec):
+ sizeof(*rec);
+}
+
+static inline char *changelog_rec_name(struct changelog_rec *rec)
+{
+ return CHANGELOG_REC_EXTENDED(rec) ?
+ ((struct changelog_ext_rec *)rec)->cr_name: rec->cr_name;
+}
+
+static inline int changelog_rec_snamelen(struct changelog_ext_rec *rec)
+{
+ return rec->cr_namelen - strlen(rec->cr_name) - 1;
+}
+
+static inline char *changelog_rec_sname(struct changelog_ext_rec *rec)
+{
+ return rec->cr_name + strlen(rec->cr_name) + 1;
+}
+
+struct ioc_changelog {
+ __u64 icc_recno;
+ __u32 icc_mdtindex;
+ __u32 icc_id;
+ __u32 icc_flags;
+};
+
+enum changelog_message_type {
+ CL_RECORD = 10, /* message is a changelog_rec */
+ CL_EOF = 11, /* at end of current changelog */
+};
+
+/********* Misc **********/
+
+struct ioc_data_version {
+ __u64 idv_version;
+ __u64 idv_flags; /* See LL_DV_xxx */
+};
+#define LL_DV_NOFLUSH 0x01 /* Do not take READ EXTENT LOCK before sampling
+ version. Dirty caches are left unchanged. */
+
+#ifndef offsetof
+# define offsetof(typ, memb) ((unsigned long)((char *)&(((typ *)0)->memb)))
+#endif
+
+#define dot_lustre_name ".lustre"
+
+
+/********* HSM **********/
+
+/** HSM per-file state
+ * See HSM_FLAGS below.
+ */
+enum hsm_states {
+ HS_EXISTS = 0x00000001,
+ HS_DIRTY = 0x00000002,
+ HS_RELEASED = 0x00000004,
+ HS_ARCHIVED = 0x00000008,
+ HS_NORELEASE = 0x00000010,
+ HS_NOARCHIVE = 0x00000020,
+ HS_LOST = 0x00000040,
+};
+
+/* HSM user-setable flags. */
+#define HSM_USER_MASK (HS_NORELEASE | HS_NOARCHIVE | HS_DIRTY)
+
+/* Other HSM flags. */
+#define HSM_STATUS_MASK (HS_EXISTS | HS_LOST | HS_RELEASED | HS_ARCHIVED)
+
+/*
+ * All HSM-related possible flags that could be applied to a file.
+ * This should be kept in sync with hsm_states.
+ */
+#define HSM_FLAGS_MASK (HSM_USER_MASK | HSM_STATUS_MASK)
+
+/**
+ * HSM request progress state
+ */
+enum hsm_progress_states {
+ HPS_WAITING = 1,
+ HPS_RUNNING = 2,
+ HPS_DONE = 3,
+};
+#define HPS_NONE 0
+
+static inline char *hsm_progress_state2name(enum hsm_progress_states s)
+{
+ switch (s) {
+ case HPS_WAITING: return "waiting";
+ case HPS_RUNNING: return "running";
+ case HPS_DONE: return "done";
+ default: return "unknown";
+ }
+}
+
+struct hsm_extent {
+ __u64 offset;
+ __u64 length;
+} __attribute__((packed));
+
+/**
+ * Current HSM states of a Lustre file.
+ *
+ * This structure purpose is to be sent to user-space mainly. It describes the
+ * current HSM flags and in-progress action.
+ */
+struct hsm_user_state {
+ /** Current HSM states, from enum hsm_states. */
+ __u32 hus_states;
+ __u32 hus_archive_id;
+ /** The current undergoing action, if there is one */
+ __u32 hus_in_progress_state;
+ __u32 hus_in_progress_action;
+ struct hsm_extent hus_in_progress_location;
+ char hus_extended_info[];
+};
+
+struct hsm_state_set_ioc {
+ struct lu_fid hssi_fid;
+ __u64 hssi_setmask;
+ __u64 hssi_clearmask;
+};
+
+/*
+ * This structure describes the current in-progress action for a file.
+ * it is returned to user space and send over the wire
+ */
+struct hsm_current_action {
+ /** The current undergoing action, if there is one */
+ /* state is one of hsm_progress_states */
+ __u32 hca_state;
+ /* action is one of hsm_user_action */
+ __u32 hca_action;
+ struct hsm_extent hca_location;
+};
+
+/***** HSM user requests ******/
+/* User-generated (lfs/ioctl) request types */
+enum hsm_user_action {
+ HUA_NONE = 1, /* no action (noop) */
+ HUA_ARCHIVE = 10, /* copy to hsm */
+ HUA_RESTORE = 11, /* prestage */
+ HUA_RELEASE = 12, /* drop ost objects */
+ HUA_REMOVE = 13, /* remove from archive */
+ HUA_CANCEL = 14 /* cancel a request */
+};
+
+static inline char *hsm_user_action2name(enum hsm_user_action a)
+{
+ switch (a) {
+ case HUA_NONE: return "NOOP";
+ case HUA_ARCHIVE: return "ARCHIVE";
+ case HUA_RESTORE: return "RESTORE";
+ case HUA_RELEASE: return "RELEASE";
+ case HUA_REMOVE: return "REMOVE";
+ case HUA_CANCEL: return "CANCEL";
+ default: return "UNKNOWN";
+ }
+}
+
+/*
+ * List of hr_flags (bit field)
+ */
+#define HSM_FORCE_ACTION 0x0001
+/* used by CT, connot be set by user */
+#define HSM_GHOST_COPY 0x0002
+
+/**
+ * Contains all the fixed part of struct hsm_user_request.
+ *
+ */
+struct hsm_request {
+ __u32 hr_action; /* enum hsm_user_action */
+ __u32 hr_archive_id; /* archive id, used only with HUA_ARCHIVE */
+ __u64 hr_flags; /* request flags */
+ __u32 hr_itemcount; /* item count in hur_user_item vector */
+ __u32 hr_data_len;
+};
+
+struct hsm_user_item {
+ lustre_fid hui_fid;
+ struct hsm_extent hui_extent;
+} __attribute__((packed));
+
+struct hsm_user_request {
+ struct hsm_request hur_request;
+ struct hsm_user_item hur_user_item[0];
+ /* extra data blob at end of struct (after all
+ * hur_user_items), only use helpers to access it
+ */
+} __attribute__((packed));
+
+/** Return pointer to data field in a hsm user request */
+static inline void *hur_data(struct hsm_user_request *hur)
+{
+ return &(hur->hur_user_item[hur->hur_request.hr_itemcount]);
+}
+
+/**
+ * Compute the current length of the provided hsm_user_request. This returns -1
+ * instead of an errno because ssize_t is defined to be only [ -1, SSIZE_MAX ]
+ *
+ * return -1 on bounds check error.
+ */
+static inline ssize_t hur_len(struct hsm_user_request *hur)
+{
+ __u64 size;
+
+ /* can't overflow a __u64 since hr_itemcount is only __u32 */
+ size = offsetof(struct hsm_user_request, hur_user_item[0]) +
+ (__u64)hur->hur_request.hr_itemcount *
+ sizeof(hur->hur_user_item[0]) + hur->hur_request.hr_data_len;
+
+ if (size != (ssize_t)size)
+ return -1;
+
+ return size;
+}
+
+/****** HSM RPCs to copytool *****/
+/* Message types the copytool may receive */
+enum hsm_message_type {
+ HMT_ACTION_LIST = 100, /* message is a hsm_action_list */
+};
+
+/* Actions the copytool may be instructed to take for a given action_item */
+enum hsm_copytool_action {
+ HSMA_NONE = 10, /* no action */
+ HSMA_ARCHIVE = 20, /* arbitrary offset */
+ HSMA_RESTORE = 21,
+ HSMA_REMOVE = 22,
+ HSMA_CANCEL = 23
+};
+
+static inline char *hsm_copytool_action2name(enum hsm_copytool_action a)
+{
+ switch (a) {
+ case HSMA_NONE: return "NOOP";
+ case HSMA_ARCHIVE: return "ARCHIVE";
+ case HSMA_RESTORE: return "RESTORE";
+ case HSMA_REMOVE: return "REMOVE";
+ case HSMA_CANCEL: return "CANCEL";
+ default: return "UNKNOWN";
+ }
+}
+
+/* Copytool item action description */
+struct hsm_action_item {
+ __u32 hai_len; /* valid size of this struct */
+ __u32 hai_action; /* hsm_copytool_action, but use known size */
+ lustre_fid hai_fid; /* Lustre FID to operated on */
+ lustre_fid hai_dfid; /* fid used for data access */
+ struct hsm_extent hai_extent; /* byte range to operate on */
+ __u64 hai_cookie; /* action cookie from coordinator */
+ __u64 hai_gid; /* grouplock id */
+ char hai_data[0]; /* variable length */
+} __attribute__((packed));
+
+/*
+ * helper function which print in hexa the first bytes of
+ * hai opaque field
+ * \param hai [IN] record to print
+ * \param buffer [OUT] output buffer
+ * \param len [IN] max buffer len
+ * \retval buffer
+ */
+static inline char *hai_dump_data_field(struct hsm_action_item *hai,
+ char *buffer, int len)
+{
+ int i, sz, data_len;
+ char *ptr;
+
+ ptr = buffer;
+ sz = len;
+ data_len = hai->hai_len - sizeof(*hai);
+ for (i = 0 ; (i < data_len) && (sz > 0) ; i++) {
+ int cnt;
+
+ cnt = snprintf(ptr, sz, "%.2X",
+ (unsigned char)hai->hai_data[i]);
+ ptr += cnt;
+ sz -= cnt;
+ }
+ *ptr = '\0';
+ return buffer;
+}
+
+/* Copytool action list */
+#define HAL_VERSION 1
+#define HAL_MAXSIZE LNET_MTU /* bytes, used in userspace only */
+struct hsm_action_list {
+ __u32 hal_version;
+ __u32 hal_count; /* number of hai's to follow */
+ __u64 hal_compound_id; /* returned by coordinator */
+ __u64 hal_flags;
+ __u32 hal_archive_id; /* which archive backend */
+ __u32 padding1;
+ char hal_fsname[0]; /* null-terminated */
+ /* struct hsm_action_item[hal_count] follows, aligned on 8-byte
+ boundaries. See hai_zero */
+} __attribute__((packed));
+
+#ifndef HAVE_CFS_SIZE_ROUND
+static inline int cfs_size_round (int val)
+{
+ return (val + 7) & (~0x7);
+}
+#define HAVE_CFS_SIZE_ROUND
+#endif
+
+/* Return pointer to first hai in action list */
+static inline struct hsm_action_item *hai_zero(struct hsm_action_list *hal)
+{
+ return (struct hsm_action_item *)(hal->hal_fsname +
+ cfs_size_round(strlen(hal-> \
+ hal_fsname)
+ + 1));
+}
+/* Return pointer to next hai */
+static inline struct hsm_action_item *hai_next(struct hsm_action_item *hai)
+{
+ return (struct hsm_action_item *)((char *)hai +
+ cfs_size_round(hai->hai_len));
+}
+
+/* Return size of an hsm_action_list */
+static inline int hal_size(struct hsm_action_list *hal)
+{
+ int i, sz;
+ struct hsm_action_item *hai;
+
+ sz = sizeof(*hal) + cfs_size_round(strlen(hal->hal_fsname) + 1);
+ hai = hai_zero(hal);
+ for (i = 0; i < hal->hal_count; i++, hai = hai_next(hai))
+ sz += cfs_size_round(hai->hai_len);
+
+ return sz;
+}
+
+/* HSM file import
+ * describe the attributes to be set on imported file
+ */
+struct hsm_user_import {
+ __u64 hui_size;
+ __u64 hui_atime;
+ __u64 hui_mtime;
+ __u32 hui_atime_ns;
+ __u32 hui_mtime_ns;
+ __u32 hui_uid;
+ __u32 hui_gid;
+ __u32 hui_mode;
+ __u32 hui_archive_id;
+};
+
+/* Copytool progress reporting */
+#define HP_FLAG_COMPLETED 0x01
+#define HP_FLAG_RETRY 0x02
+
+struct hsm_progress {
+ lustre_fid hp_fid;
+ __u64 hp_cookie;
+ struct hsm_extent hp_extent;
+ __u16 hp_flags;
+ __u16 hp_errval; /* positive val */
+ __u32 padding;
+};
+
+struct hsm_copy {
+ __u64 hc_data_version;
+ __u16 hc_flags;
+ __u16 hc_errval; /* positive val */
+ __u32 padding;
+ struct hsm_action_item hc_hai;
+};
+
+/** @} lustreuser */
+
+#endif /* _LUSTRE_USER_H */
diff --git a/drivers/staging/lustre/lustre/include/lustre_acl.h b/drivers/staging/lustre/lustre/include/lustre_acl.h
new file mode 100644
index 000000000..aa4cfa7b7
--- /dev/null
+++ b/drivers/staging/lustre/lustre/include/lustre_acl.h
@@ -0,0 +1,49 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ *
+ * lustre/include/lustre_acl.h
+ */
+
+#ifndef _LUSTRE_ACL_H
+#define _LUSTRE_ACL_H
+
+#include <linux/fs.h>
+#include <linux/dcache.h>
+#include <linux/posix_acl_xattr.h>
+
+#define LUSTRE_POSIX_ACL_MAX_ENTRIES 32
+#define LUSTRE_POSIX_ACL_MAX_SIZE \
+ (sizeof(posix_acl_xattr_header) + \
+ LUSTRE_POSIX_ACL_MAX_ENTRIES * sizeof(posix_acl_xattr_entry))
+
+#endif
diff --git a/drivers/staging/lustre/lustre/include/lustre_capa.h b/drivers/staging/lustre/lustre/include/lustre_capa.h
new file mode 100644
index 000000000..fe19534eb
--- /dev/null
+++ b/drivers/staging/lustre/lustre/include/lustre_capa.h
@@ -0,0 +1,305 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2012, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ *
+ * lustre/include/lustre_capa.h
+ *
+ * Author: Lai Siyao <lsy@clusterfs.com>
+ */
+
+#ifndef __LINUX_CAPA_H_
+#define __LINUX_CAPA_H_
+
+/** \defgroup capa capa
+ *
+ * @{
+ */
+
+/*
+ * capability
+ */
+#include <linux/crypto.h>
+#include "lustre/lustre_idl.h"
+
+#define CAPA_TIMEOUT 1800 /* sec, == 30 min */
+#define CAPA_KEY_TIMEOUT (24 * 60 * 60) /* sec, == 1 days */
+
+struct capa_hmac_alg {
+ const char *ha_name;
+ int ha_len;
+ int ha_keylen;
+};
+
+#define DEF_CAPA_HMAC_ALG(name, type, len, keylen) \
+[CAPA_HMAC_ALG_ ## type] = { \
+ .ha_name = name, \
+ .ha_len = len, \
+ .ha_keylen = keylen, \
+}
+
+struct client_capa {
+ struct inode *inode;
+ struct list_head lli_list; /* link to lli_oss_capas */
+};
+
+struct target_capa {
+ struct hlist_node c_hash; /* link to capa hash */
+};
+
+struct obd_capa {
+ struct list_head c_list; /* link to capa_list */
+
+ struct lustre_capa c_capa; /* capa */
+ atomic_t c_refc; /* ref count */
+ unsigned long c_expiry; /* jiffies */
+ spinlock_t c_lock; /* protect capa content */
+ int c_site;
+
+ union {
+ struct client_capa cli;
+ struct target_capa tgt;
+ } u;
+};
+
+enum {
+ CAPA_SITE_CLIENT = 0,
+ CAPA_SITE_SERVER,
+ CAPA_SITE_MAX
+};
+
+static inline struct lu_fid *capa_fid(struct lustre_capa *capa)
+{
+ return &capa->lc_fid;
+}
+
+static inline __u64 capa_opc(struct lustre_capa *capa)
+{
+ return capa->lc_opc;
+}
+
+static inline __u64 capa_uid(struct lustre_capa *capa)
+{
+ return capa->lc_uid;
+}
+
+static inline __u64 capa_gid(struct lustre_capa *capa)
+{
+ return capa->lc_gid;
+}
+
+static inline __u32 capa_flags(struct lustre_capa *capa)
+{
+ return capa->lc_flags & 0xffffff;
+}
+
+static inline __u32 capa_alg(struct lustre_capa *capa)
+{
+ return (capa->lc_flags >> 24);
+}
+
+static inline __u32 capa_keyid(struct lustre_capa *capa)
+{
+ return capa->lc_keyid;
+}
+
+static inline __u64 capa_key_seq(struct lustre_capa_key *key)
+{
+ return key->lk_seq;
+}
+
+static inline __u32 capa_key_keyid(struct lustre_capa_key *key)
+{
+ return key->lk_keyid;
+}
+
+static inline __u32 capa_timeout(struct lustre_capa *capa)
+{
+ return capa->lc_timeout;
+}
+
+static inline __u32 capa_expiry(struct lustre_capa *capa)
+{
+ return capa->lc_expiry;
+}
+
+void _debug_capa(struct lustre_capa *, struct libcfs_debug_msg_data *,
+ const char *fmt, ...);
+#define DEBUG_CAPA(level, capa, fmt, args...) \
+do { \
+ if (((level) & D_CANTMASK) != 0 || \
+ ((libcfs_debug & (level)) != 0 && \
+ (libcfs_subsystem_debug & DEBUG_SUBSYSTEM) != 0)) { \
+ LIBCFS_DEBUG_MSG_DATA_DECL(msgdata, level, NULL); \
+ _debug_capa((capa), &msgdata, fmt, ##args); \
+ } \
+} while (0)
+
+#define DEBUG_CAPA_KEY(level, k, fmt, args...) \
+do { \
+CDEBUG(level, fmt " capability key@%p seq %llu keyid %u\n", \
+ ##args, k, capa_key_seq(k), capa_key_keyid(k)); \
+} while (0)
+
+typedef int (* renew_capa_cb_t)(struct obd_capa *, struct lustre_capa *);
+
+/* obdclass/capa.c */
+extern struct list_head capa_list[];
+extern spinlock_t capa_lock;
+extern int capa_count[];
+extern struct kmem_cache *capa_cachep;
+
+struct hlist_head *init_capa_hash(void);
+void cleanup_capa_hash(struct hlist_head *hash);
+
+struct obd_capa *capa_add(struct hlist_head *hash,
+ struct lustre_capa *capa);
+struct obd_capa *capa_lookup(struct hlist_head *hash,
+ struct lustre_capa *capa, int alive);
+
+int capa_hmac(__u8 *hmac, struct lustre_capa *capa, __u8 *key);
+int capa_encrypt_id(__u32 *d, __u32 *s, __u8 *key, int keylen);
+int capa_decrypt_id(__u32 *d, __u32 *s, __u8 *key, int keylen);
+void capa_cpy(void *dst, struct obd_capa *ocapa);
+static inline struct obd_capa *alloc_capa(int site)
+{
+ struct obd_capa *ocapa;
+
+ if (unlikely(site != CAPA_SITE_CLIENT && site != CAPA_SITE_SERVER))
+ return ERR_PTR(-EINVAL);
+
+ OBD_SLAB_ALLOC_PTR(ocapa, capa_cachep);
+ if (unlikely(!ocapa))
+ return ERR_PTR(-ENOMEM);
+
+ INIT_LIST_HEAD(&ocapa->c_list);
+ atomic_set(&ocapa->c_refc, 1);
+ spin_lock_init(&ocapa->c_lock);
+ ocapa->c_site = site;
+ if (ocapa->c_site == CAPA_SITE_CLIENT)
+ INIT_LIST_HEAD(&ocapa->u.cli.lli_list);
+ else
+ INIT_HLIST_NODE(&ocapa->u.tgt.c_hash);
+
+ return ocapa;
+}
+
+static inline struct obd_capa *capa_get(struct obd_capa *ocapa)
+{
+ if (!ocapa)
+ return NULL;
+
+ atomic_inc(&ocapa->c_refc);
+ return ocapa;
+}
+
+static inline void capa_put(struct obd_capa *ocapa)
+{
+ if (!ocapa)
+ return;
+
+ if (atomic_read(&ocapa->c_refc) == 0) {
+ DEBUG_CAPA(D_ERROR, &ocapa->c_capa, "refc is 0 for");
+ LBUG();
+ }
+
+ if (atomic_dec_and_test(&ocapa->c_refc)) {
+ LASSERT(list_empty(&ocapa->c_list));
+ if (ocapa->c_site == CAPA_SITE_CLIENT) {
+ LASSERT(list_empty(&ocapa->u.cli.lli_list));
+ } else {
+ struct hlist_node *hnode;
+
+ hnode = &ocapa->u.tgt.c_hash;
+ LASSERT(!hnode->next && !hnode->pprev);
+ }
+ OBD_SLAB_FREE(ocapa, capa_cachep, sizeof(*ocapa));
+ }
+}
+
+static inline int open_flags_to_accmode(int flags)
+{
+ int mode = flags;
+
+ if ((mode + 1) & O_ACCMODE)
+ mode++;
+ if (mode & O_TRUNC)
+ mode |= 2;
+
+ return mode;
+}
+
+static inline __u64 capa_open_opc(int mode)
+{
+ return mode & FMODE_WRITE ? CAPA_OPC_OSS_WRITE : CAPA_OPC_OSS_READ;
+}
+
+static inline void set_capa_expiry(struct obd_capa *ocapa)
+{
+ unsigned long expiry = cfs_time_sub((unsigned long)ocapa->c_capa.lc_expiry,
+ get_seconds());
+ ocapa->c_expiry = cfs_time_add(cfs_time_current(),
+ cfs_time_seconds(expiry));
+}
+
+static inline int capa_is_expired_sec(struct lustre_capa *capa)
+{
+ return (capa->lc_expiry - get_seconds() <= 0);
+}
+
+static inline int capa_is_expired(struct obd_capa *ocapa)
+{
+ return time_before_eq(ocapa->c_expiry, cfs_time_current());
+}
+
+static inline int capa_opc_supported(struct lustre_capa *capa, __u64 opc)
+{
+ return (capa_opc(capa) & opc) == opc;
+}
+
+struct filter_capa_key {
+ struct list_head k_list;
+ struct lustre_capa_key k_key;
+};
+
+enum {
+ LC_ID_NONE = 0,
+ LC_ID_PLAIN = 1,
+ LC_ID_CONVERT = 2
+};
+
+#define BYPASS_CAPA (struct lustre_capa *)ERR_PTR(-ENOENT)
+
+/** @} capa */
+
+#endif /* __LINUX_CAPA_H_ */
diff --git a/drivers/staging/lustre/lustre/include/lustre_cfg.h b/drivers/staging/lustre/lustre/include/lustre_cfg.h
new file mode 100644
index 000000000..7b385b872
--- /dev/null
+++ b/drivers/staging/lustre/lustre/include/lustre_cfg.h
@@ -0,0 +1,293 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2012, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ */
+
+#ifndef _LUSTRE_CFG_H
+#define _LUSTRE_CFG_H
+
+/** \defgroup cfg cfg
+ *
+ * @{
+ */
+
+/*
+ * 1cf6
+ * lcfG
+ */
+#define LUSTRE_CFG_VERSION 0x1cf60001
+#define LUSTRE_CFG_MAX_BUFCOUNT 8
+
+#define LCFG_HDR_SIZE(count) \
+ cfs_size_round(offsetof (struct lustre_cfg, lcfg_buflens[(count)]))
+
+/** If the LCFG_REQUIRED bit is set in a configuration command,
+ * then the client is required to understand this parameter
+ * in order to mount the filesystem. If it does not understand
+ * a REQUIRED command the client mount will fail. */
+#define LCFG_REQUIRED 0x0001000
+
+enum lcfg_command_type {
+ LCFG_ATTACH = 0x00cf001, /**< create a new obd instance */
+ LCFG_DETACH = 0x00cf002, /**< destroy obd instance */
+ LCFG_SETUP = 0x00cf003, /**< call type-specific setup */
+ LCFG_CLEANUP = 0x00cf004, /**< call type-specific cleanup */
+ LCFG_ADD_UUID = 0x00cf005, /**< add a nid to a niduuid */
+ LCFG_DEL_UUID = 0x00cf006, /**< remove a nid from a niduuid */
+ LCFG_MOUNTOPT = 0x00cf007, /**< create a profile (mdc, osc) */
+ LCFG_DEL_MOUNTOPT = 0x00cf008, /**< destroy a profile */
+ LCFG_SET_TIMEOUT = 0x00cf009, /**< set obd_timeout */
+ LCFG_SET_UPCALL = 0x00cf00a, /**< deprecated */
+ LCFG_ADD_CONN = 0x00cf00b, /**< add a failover niduuid to an obd */
+ LCFG_DEL_CONN = 0x00cf00c, /**< remove a failover niduuid */
+ LCFG_LOV_ADD_OBD = 0x00cf00d, /**< add an osc to a lov */
+ LCFG_LOV_DEL_OBD = 0x00cf00e, /**< remove an osc from a lov */
+ LCFG_PARAM = 0x00cf00f, /**< set a proc parameter */
+ LCFG_MARKER = 0x00cf010, /**< metadata about next cfg rec */
+ LCFG_LOG_START = 0x00ce011, /**< mgc only, process a cfg log */
+ LCFG_LOG_END = 0x00ce012, /**< stop processing updates */
+ LCFG_LOV_ADD_INA = 0x00ce013, /**< like LOV_ADD_OBD, inactive */
+ LCFG_ADD_MDC = 0x00cf014, /**< add an mdc to a lmv */
+ LCFG_DEL_MDC = 0x00cf015, /**< remove an mdc from a lmv */
+ LCFG_SPTLRPC_CONF = 0x00ce016, /**< security */
+ LCFG_POOL_NEW = 0x00ce020, /**< create an ost pool name */
+ LCFG_POOL_ADD = 0x00ce021, /**< add an ost to a pool */
+ LCFG_POOL_REM = 0x00ce022, /**< remove an ost from a pool */
+ LCFG_POOL_DEL = 0x00ce023, /**< destroy an ost pool name */
+ LCFG_SET_LDLM_TIMEOUT = 0x00ce030, /**< set ldlm_timeout */
+ LCFG_PRE_CLEANUP = 0x00cf031, /**< call type-specific pre
+ * cleanup cleanup */
+ LCFG_SET_PARAM = 0x00ce032, /**< use set_param syntax to set
+ *a proc parameters */
+};
+
+struct lustre_cfg_bufs {
+ void *lcfg_buf[LUSTRE_CFG_MAX_BUFCOUNT];
+ __u32 lcfg_buflen[LUSTRE_CFG_MAX_BUFCOUNT];
+ __u32 lcfg_bufcount;
+};
+
+struct lustre_cfg {
+ __u32 lcfg_version;
+ __u32 lcfg_command;
+
+ __u32 lcfg_num;
+ __u32 lcfg_flags;
+ __u64 lcfg_nid;
+ __u32 lcfg_nal; /* not used any more */
+
+ __u32 lcfg_bufcount;
+ __u32 lcfg_buflens[0];
+};
+
+enum cfg_record_type {
+ PORTALS_CFG_TYPE = 1,
+ LUSTRE_CFG_TYPE = 123,
+};
+
+#define LUSTRE_CFG_BUFLEN(lcfg, idx) \
+ ((lcfg)->lcfg_bufcount <= (idx) \
+ ? 0 \
+ : (lcfg)->lcfg_buflens[(idx)])
+
+static inline void lustre_cfg_bufs_set(struct lustre_cfg_bufs *bufs,
+ __u32 index,
+ void *buf,
+ __u32 buflen)
+{
+ if (index >= LUSTRE_CFG_MAX_BUFCOUNT)
+ return;
+ if (bufs == NULL)
+ return;
+
+ if (bufs->lcfg_bufcount <= index)
+ bufs->lcfg_bufcount = index + 1;
+
+ bufs->lcfg_buf[index] = buf;
+ bufs->lcfg_buflen[index] = buflen;
+}
+
+static inline void lustre_cfg_bufs_set_string(struct lustre_cfg_bufs *bufs,
+ __u32 index,
+ char *str)
+{
+ lustre_cfg_bufs_set(bufs, index, str, str ? strlen(str) + 1 : 0);
+}
+
+static inline void lustre_cfg_bufs_reset(struct lustre_cfg_bufs *bufs, char *name)
+{
+ memset((bufs), 0, sizeof(*bufs));
+ if (name)
+ lustre_cfg_bufs_set_string(bufs, 0, name);
+}
+
+static inline void *lustre_cfg_buf(struct lustre_cfg *lcfg, int index)
+{
+ int i;
+ int offset;
+ int bufcount;
+ LASSERT (lcfg != NULL);
+ LASSERT (index >= 0);
+
+ bufcount = lcfg->lcfg_bufcount;
+ if (index >= bufcount)
+ return NULL;
+
+ offset = LCFG_HDR_SIZE(lcfg->lcfg_bufcount);
+ for (i = 0; i < index; i++)
+ offset += cfs_size_round(lcfg->lcfg_buflens[i]);
+ return (char *)lcfg + offset;
+}
+
+static inline void lustre_cfg_bufs_init(struct lustre_cfg_bufs *bufs,
+ struct lustre_cfg *lcfg)
+{
+ int i;
+ bufs->lcfg_bufcount = lcfg->lcfg_bufcount;
+ for (i = 0; i < bufs->lcfg_bufcount; i++) {
+ bufs->lcfg_buflen[i] = lcfg->lcfg_buflens[i];
+ bufs->lcfg_buf[i] = lustre_cfg_buf(lcfg, i);
+ }
+}
+
+static inline char *lustre_cfg_string(struct lustre_cfg *lcfg, int index)
+{
+ char *s;
+
+ if (lcfg->lcfg_buflens[index] == 0)
+ return NULL;
+
+ s = lustre_cfg_buf(lcfg, index);
+ if (s == NULL)
+ return NULL;
+
+ /*
+ * make sure it's NULL terminated, even if this kills a char
+ * of data. Try to use the padding first though.
+ */
+ if (s[lcfg->lcfg_buflens[index] - 1] != '\0') {
+ int last = min((int)lcfg->lcfg_buflens[index],
+ cfs_size_round(lcfg->lcfg_buflens[index]) - 1);
+ char lost = s[last];
+ s[last] = '\0';
+ if (lost != '\0') {
+ CWARN("Truncated buf %d to '%s' (lost '%c'...)\n",
+ index, s, lost);
+ }
+ }
+ return s;
+}
+
+static inline int lustre_cfg_len(__u32 bufcount, __u32 *buflens)
+{
+ int i;
+ int len;
+
+ len = LCFG_HDR_SIZE(bufcount);
+ for (i = 0; i < bufcount; i++)
+ len += cfs_size_round(buflens[i]);
+
+ return cfs_size_round(len);
+}
+
+
+#include "obd_support.h"
+
+static inline struct lustre_cfg *lustre_cfg_new(int cmd,
+ struct lustre_cfg_bufs *bufs)
+{
+ struct lustre_cfg *lcfg;
+ char *ptr;
+ int i;
+
+ OBD_ALLOC(lcfg, lustre_cfg_len(bufs->lcfg_bufcount,
+ bufs->lcfg_buflen));
+ if (!lcfg)
+ return ERR_PTR(-ENOMEM);
+
+ lcfg->lcfg_version = LUSTRE_CFG_VERSION;
+ lcfg->lcfg_command = cmd;
+ lcfg->lcfg_bufcount = bufs->lcfg_bufcount;
+
+ ptr = (char *)lcfg + LCFG_HDR_SIZE(lcfg->lcfg_bufcount);
+ for (i = 0; i < lcfg->lcfg_bufcount; i++) {
+ lcfg->lcfg_buflens[i] = bufs->lcfg_buflen[i];
+ LOGL((char *)bufs->lcfg_buf[i], bufs->lcfg_buflen[i], ptr);
+ }
+ return lcfg;
+}
+
+static inline void lustre_cfg_free(struct lustre_cfg *lcfg)
+{
+ int len;
+
+ len = lustre_cfg_len(lcfg->lcfg_bufcount, lcfg->lcfg_buflens);
+
+ OBD_FREE(lcfg, len);
+ return;
+}
+
+static inline int lustre_cfg_sanity_check(void *buf, int len)
+{
+ struct lustre_cfg *lcfg = (struct lustre_cfg *)buf;
+
+ if (!lcfg)
+ return -EINVAL;
+
+ /* check that the first bits of the struct are valid */
+ if (len < LCFG_HDR_SIZE(0))
+ return -EINVAL;
+
+ if (lcfg->lcfg_version != LUSTRE_CFG_VERSION)
+ return -EINVAL;
+
+ if (lcfg->lcfg_bufcount >= LUSTRE_CFG_MAX_BUFCOUNT)
+ return -EINVAL;
+
+ /* check that the buflens are valid */
+ if (len < LCFG_HDR_SIZE(lcfg->lcfg_bufcount))
+ return -EINVAL;
+
+ /* make sure all the pointers point inside the data */
+ if (len < lustre_cfg_len(lcfg->lcfg_bufcount, lcfg->lcfg_buflens))
+ return -EINVAL;
+
+ return 0;
+}
+
+#include "lustre/lustre_user.h"
+
+/** @} cfg */
+
+#endif /* _LUSTRE_CFG_H */
diff --git a/drivers/staging/lustre/lustre/include/lustre_debug.h b/drivers/staging/lustre/lustre/include/lustre_debug.h
new file mode 100644
index 000000000..6c92d0bc9
--- /dev/null
+++ b/drivers/staging/lustre/lustre/include/lustre_debug.h
@@ -0,0 +1,56 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2011, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ */
+
+#ifndef _LUSTRE_DEBUG_H
+#define _LUSTRE_DEBUG_H
+
+/** \defgroup debug debug
+ *
+ * @{
+ */
+
+#include "lustre_net.h"
+#include "obd.h"
+
+/* lib/debug.c */
+void dump_lniobuf(struct niobuf_local *lnb);
+int dump_req(struct ptlrpc_request *req);
+int block_debug_setup(void *addr, int len, __u64 off, __u64 id);
+int block_debug_check(char *who, void *addr, int len, __u64 off, __u64 id);
+
+/** @} debug */
+
+#endif
diff --git a/drivers/staging/lustre/lustre/include/lustre_disk.h b/drivers/staging/lustre/lustre/include/lustre_disk.h
new file mode 100644
index 000000000..9b2833131
--- /dev/null
+++ b/drivers/staging/lustre/lustre/include/lustre_disk.h
@@ -0,0 +1,547 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2011, 2012, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ *
+ * lustre/include/lustre_disk.h
+ *
+ * Lustre disk format definitions.
+ *
+ * Author: Nathan Rutman <nathan@clusterfs.com>
+ */
+
+#ifndef _LUSTRE_DISK_H
+#define _LUSTRE_DISK_H
+
+/** \defgroup disk disk
+ *
+ * @{
+ */
+
+#include "../../include/linux/libcfs/libcfs.h"
+#include "../../include/linux/lnet/types.h"
+#include <linux/backing-dev.h>
+
+/****************** on-disk files *********************/
+
+#define MDT_LOGS_DIR "LOGS" /* COMPAT_146 */
+#define MOUNT_CONFIGS_DIR "CONFIGS"
+#define CONFIGS_FILE "mountdata"
+/** Persistent mount data are stored on the disk in this file. */
+#define MOUNT_DATA_FILE MOUNT_CONFIGS_DIR"/"CONFIGS_FILE
+#define LAST_RCVD "last_rcvd"
+#define LOV_OBJID "lov_objid"
+#define LOV_OBJSEQ "lov_objseq"
+#define HEALTH_CHECK "health_check"
+#define CAPA_KEYS "capa_keys"
+#define CHANGELOG_USERS "changelog_users"
+#define MGS_NIDTBL_DIR "NIDTBL_VERSIONS"
+#define QMT_DIR "quota_master"
+#define QSD_DIR "quota_slave"
+#define HSM_ACTIONS "hsm_actions"
+
+/****************** persistent mount data *********************/
+
+#define LDD_F_SV_TYPE_MDT 0x0001
+#define LDD_F_SV_TYPE_OST 0x0002
+#define LDD_F_SV_TYPE_MGS 0x0004
+#define LDD_F_SV_TYPE_MASK (LDD_F_SV_TYPE_MDT | \
+ LDD_F_SV_TYPE_OST | \
+ LDD_F_SV_TYPE_MGS)
+#define LDD_F_SV_ALL 0x0008
+/** need an index assignment */
+#define LDD_F_NEED_INDEX 0x0010
+/** never registered */
+#define LDD_F_VIRGIN 0x0020
+/** update the config logs for this server */
+#define LDD_F_UPDATE 0x0040
+/** rewrite the LDD */
+#define LDD_F_REWRITE_LDD 0x0080
+/** regenerate config logs for this fs or server */
+#define LDD_F_WRITECONF 0x0100
+/** COMPAT_14 */
+#define LDD_F_UPGRADE14 0x0200
+/** process as lctl conf_param */
+#define LDD_F_PARAM 0x0400
+/** all nodes are specified as service nodes */
+#define LDD_F_NO_PRIMNODE 0x1000
+/** IR enable flag */
+#define LDD_F_IR_CAPABLE 0x2000
+/** the MGS refused to register the target. */
+#define LDD_F_ERROR 0x4000
+/** process at lctl conf_param */
+#define LDD_F_PARAM2 0x8000
+
+/* opc for target register */
+#define LDD_F_OPC_REG 0x10000000
+#define LDD_F_OPC_UNREG 0x20000000
+#define LDD_F_OPC_READY 0x40000000
+#define LDD_F_OPC_MASK 0xf0000000
+
+#define LDD_F_ONDISK_MASK (LDD_F_SV_TYPE_MASK)
+
+#define LDD_F_MASK 0xFFFF
+
+enum ldd_mount_type {
+ LDD_MT_EXT3 = 0,
+ LDD_MT_LDISKFS,
+ LDD_MT_SMFS,
+ LDD_MT_REISERFS,
+ LDD_MT_LDISKFS2,
+ LDD_MT_ZFS,
+ LDD_MT_LAST
+};
+
+static inline char *mt_str(enum ldd_mount_type mt)
+{
+ static char *mount_type_string[] = {
+ "ext3",
+ "ldiskfs",
+ "smfs",
+ "reiserfs",
+ "ldiskfs2",
+ "zfs",
+ };
+ return mount_type_string[mt];
+}
+
+static inline char *mt_type(enum ldd_mount_type mt)
+{
+ static char *mount_type_string[] = {
+ "osd-ldiskfs",
+ "osd-ldiskfs",
+ "osd-smfs",
+ "osd-reiserfs",
+ "osd-ldiskfs",
+ "osd-zfs",
+ };
+ return mount_type_string[mt];
+}
+
+#define LDD_INCOMPAT_SUPP 0
+#define LDD_ROCOMPAT_SUPP 0
+
+#define LDD_MAGIC 0x1dd00001
+
+/* On-disk configuration file. In host-endian order. */
+struct lustre_disk_data {
+ __u32 ldd_magic;
+ __u32 ldd_feature_compat; /* compatible feature flags */
+ __u32 ldd_feature_rocompat;/* read-only compatible feature flags */
+ __u32 ldd_feature_incompat;/* incompatible feature flags */
+
+ __u32 ldd_config_ver; /* config rewrite count - not used */
+ __u32 ldd_flags; /* LDD_SV_TYPE */
+ __u32 ldd_svindex; /* server index (0001), must match
+ svname */
+ __u32 ldd_mount_type; /* target fs type LDD_MT_* */
+ char ldd_fsname[64]; /* filesystem this server is part of,
+ MTI_NAME_MAXLEN */
+ char ldd_svname[64]; /* this server's name (lustre-mdt0001)*/
+ __u8 ldd_uuid[40]; /* server UUID (COMPAT_146) */
+
+/*200*/ char ldd_userdata[1024 - 200]; /* arbitrary user string */
+/*1024*/__u8 ldd_padding[4096 - 1024];
+/*4096*/char ldd_mount_opts[4096]; /* target fs mount opts */
+/*8192*/char ldd_params[4096]; /* key=value pairs */
+};
+
+
+#define IS_MDT(data) ((data)->lsi_flags & LDD_F_SV_TYPE_MDT)
+#define IS_OST(data) ((data)->lsi_flags & LDD_F_SV_TYPE_OST)
+#define IS_MGS(data) ((data)->lsi_flags & LDD_F_SV_TYPE_MGS)
+#define IS_SERVER(data) ((data)->lsi_flags & (LDD_F_SV_TYPE_MGS | \
+ LDD_F_SV_TYPE_MDT | LDD_F_SV_TYPE_OST))
+#define MT_STR(data) mt_str((data)->ldd_mount_type)
+
+/* Make the mdt/ost server obd name based on the filesystem name */
+static inline int server_make_name(__u32 flags, __u16 index, char *fs,
+ char *name)
+{
+ if (flags & (LDD_F_SV_TYPE_MDT | LDD_F_SV_TYPE_OST)) {
+ if (!(flags & LDD_F_SV_ALL))
+ sprintf(name, "%.8s%c%s%04x", fs,
+ (flags & LDD_F_VIRGIN) ? ':' :
+ ((flags & LDD_F_WRITECONF) ? '=' : '-'),
+ (flags & LDD_F_SV_TYPE_MDT) ? "MDT" : "OST",
+ index);
+ } else if (flags & LDD_F_SV_TYPE_MGS) {
+ sprintf(name, "MGS");
+ } else {
+ CERROR("unknown server type %#x\n", flags);
+ return 1;
+ }
+ return 0;
+}
+
+/****************** mount command *********************/
+
+/* The lmd is only used internally by Lustre; mount simply passes
+ everything as string options */
+
+#define LMD_MAGIC 0xbdacbd03
+
+/* gleaned from the mount command - no persistent info here */
+struct lustre_mount_data {
+ __u32 lmd_magic;
+ __u32 lmd_flags; /* lustre mount flags */
+ int lmd_mgs_failnodes; /* mgs failover node count */
+ int lmd_exclude_count;
+ int lmd_recovery_time_soft;
+ int lmd_recovery_time_hard;
+ char *lmd_dev; /* device name */
+ char *lmd_profile; /* client only */
+ char *lmd_mgssec; /* sptlrpc flavor to mgs */
+ char *lmd_opts; /* lustre mount options (as opposed to
+ _device_ mount options) */
+ char *lmd_params; /* lustre params */
+ __u32 *lmd_exclude; /* array of OSTs to ignore */
+ char *lmd_mgs; /* MGS nid */
+ char *lmd_osd_type; /* OSD type */
+};
+
+#define LMD_FLG_SERVER 0x0001 /* Mounting a server */
+#define LMD_FLG_CLIENT 0x0002 /* Mounting a client */
+#define LMD_FLG_ABORT_RECOV 0x0008 /* Abort recovery */
+#define LMD_FLG_NOSVC 0x0010 /* Only start MGS/MGC for servers,
+ no other services */
+#define LMD_FLG_NOMGS 0x0020 /* Only start target for servers, reusing
+ existing MGS services */
+#define LMD_FLG_WRITECONF 0x0040 /* Rewrite config log */
+#define LMD_FLG_NOIR 0x0080 /* NO imperative recovery */
+#define LMD_FLG_NOSCRUB 0x0100 /* Do not trigger scrub automatically */
+#define LMD_FLG_MGS 0x0200 /* Also start MGS along with server */
+#define LMD_FLG_IAM 0x0400 /* IAM dir */
+#define LMD_FLG_NO_PRIMNODE 0x0800 /* all nodes are service nodes */
+#define LMD_FLG_VIRGIN 0x1000 /* the service registers first time */
+#define LMD_FLG_UPDATE 0x2000 /* update parameters */
+#define LMD_FLG_HSM 0x4000 /* Start coordinator */
+
+#define lmd_is_client(x) ((x)->lmd_flags & LMD_FLG_CLIENT)
+
+
+/****************** last_rcvd file *********************/
+
+/** version recovery epoch */
+#define LR_EPOCH_BITS 32
+#define lr_epoch(a) ((a) >> LR_EPOCH_BITS)
+#define LR_EXPIRE_INTERVALS 16 /**< number of intervals to track transno */
+#define ENOENT_VERSION 1 /** 'virtual' version of non-existent object */
+
+#define LR_SERVER_SIZE 512
+#define LR_CLIENT_START 8192
+#define LR_CLIENT_SIZE 128
+#if LR_CLIENT_START < LR_SERVER_SIZE
+#error "Can't have LR_CLIENT_START < LR_SERVER_SIZE"
+#endif
+
+/*
+ * This limit is arbitrary (131072 clients on x86), but it is convenient to use
+ * 2^n * PAGE_CACHE_SIZE * 8 for the number of bits that fit an order-n allocation.
+ * If we need more than 131072 clients (order-2 allocation on x86) then this
+ * should become an array of single-page pointers that are allocated on demand.
+ */
+#if (128 * 1024UL) > (PAGE_CACHE_SIZE * 8)
+#define LR_MAX_CLIENTS (128 * 1024UL)
+#else
+#define LR_MAX_CLIENTS (PAGE_CACHE_SIZE * 8)
+#endif
+
+/** COMPAT_146: this is an OST (temporary) */
+#define OBD_COMPAT_OST 0x00000002
+/** COMPAT_146: this is an MDT (temporary) */
+#define OBD_COMPAT_MDT 0x00000004
+/** 2.0 server, interop flag to show server version is changed */
+#define OBD_COMPAT_20 0x00000008
+
+/** MDS handles LOV_OBJID file */
+#define OBD_ROCOMPAT_LOVOBJID 0x00000001
+
+/** OST handles group subdirs */
+#define OBD_INCOMPAT_GROUPS 0x00000001
+/** this is an OST */
+#define OBD_INCOMPAT_OST 0x00000002
+/** this is an MDT */
+#define OBD_INCOMPAT_MDT 0x00000004
+/** common last_rvcd format */
+#define OBD_INCOMPAT_COMMON_LR 0x00000008
+/** FID is enabled */
+#define OBD_INCOMPAT_FID 0x00000010
+/** Size-on-MDS is enabled */
+#define OBD_INCOMPAT_SOM 0x00000020
+/** filesystem using iam format to store directory entries */
+#define OBD_INCOMPAT_IAM_DIR 0x00000040
+/** LMA attribute contains per-inode incompatible flags */
+#define OBD_INCOMPAT_LMA 0x00000080
+/** lmm_stripe_count has been shrunk from __u32 to __u16 and the remaining 16
+ * bits are now used to store a generation. Once we start changing the layout
+ * and bumping the generation, old versions expecting a 32-bit lmm_stripe_count
+ * will be confused by interpreting stripe_count | gen << 16 as the actual
+ * stripe count */
+#define OBD_INCOMPAT_LMM_VER 0x00000100
+/** multiple OI files for MDT */
+#define OBD_INCOMPAT_MULTI_OI 0x00000200
+
+/* Data stored per server at the head of the last_rcvd file. In le32 order.
+ This should be common to filter_internal.h, lustre_mds.h */
+struct lr_server_data {
+ __u8 lsd_uuid[40]; /* server UUID */
+ __u64 lsd_last_transno; /* last completed transaction ID */
+ __u64 lsd_compat14; /* reserved - compat with old last_rcvd */
+ __u64 lsd_mount_count; /* incarnation number */
+ __u32 lsd_feature_compat; /* compatible feature flags */
+ __u32 lsd_feature_rocompat;/* read-only compatible feature flags */
+ __u32 lsd_feature_incompat;/* incompatible feature flags */
+ __u32 lsd_server_size; /* size of server data area */
+ __u32 lsd_client_start; /* start of per-client data area */
+ __u16 lsd_client_size; /* size of per-client data area */
+ __u16 lsd_subdir_count; /* number of subdirectories for objects */
+ __u64 lsd_catalog_oid; /* recovery catalog object id */
+ __u32 lsd_catalog_ogen; /* recovery catalog inode generation */
+ __u8 lsd_peeruuid[40]; /* UUID of MDS associated with this OST */
+ __u32 lsd_osd_index; /* index number of OST in LOV */
+ __u32 lsd_padding1; /* was lsd_mdt_index, unused in 2.4.0 */
+ __u32 lsd_start_epoch; /* VBR: start epoch from last boot */
+ /** transaction values since lsd_trans_table_time */
+ __u64 lsd_trans_table[LR_EXPIRE_INTERVALS];
+ /** start point of transno table below */
+ __u32 lsd_trans_table_time; /* time of first slot in table above */
+ __u32 lsd_expire_intervals; /* LR_EXPIRE_INTERVALS */
+ __u8 lsd_padding[LR_SERVER_SIZE - 288];
+};
+
+/* Data stored per client in the last_rcvd file. In le32 order. */
+struct lsd_client_data {
+ __u8 lcd_uuid[40]; /* client UUID */
+ __u64 lcd_last_transno; /* last completed transaction ID */
+ __u64 lcd_last_xid; /* xid for the last transaction */
+ __u32 lcd_last_result; /* result from last RPC */
+ __u32 lcd_last_data; /* per-op data (disposition for open &c.) */
+ /* for MDS_CLOSE requests */
+ __u64 lcd_last_close_transno; /* last completed transaction ID */
+ __u64 lcd_last_close_xid; /* xid for the last transaction */
+ __u32 lcd_last_close_result; /* result from last RPC */
+ __u32 lcd_last_close_data; /* per-op data */
+ /* VBR: last versions */
+ __u64 lcd_pre_versions[4];
+ __u32 lcd_last_epoch;
+ /** orphans handling for delayed export rely on that */
+ __u32 lcd_first_epoch;
+ __u8 lcd_padding[LR_CLIENT_SIZE - 128];
+};
+
+/* bug20354: the lcd_uuid for export of clients may be wrong */
+static inline void check_lcd(char *obd_name, int index,
+ struct lsd_client_data *lcd)
+{
+ int length = sizeof(lcd->lcd_uuid);
+ if (strnlen((char*)lcd->lcd_uuid, length) == length) {
+ lcd->lcd_uuid[length - 1] = '\0';
+
+ LCONSOLE_ERROR("the client UUID (%s) on %s for exports stored in last_rcvd(index = %d) is bad!\n",
+ lcd->lcd_uuid, obd_name, index);
+ }
+}
+
+/* last_rcvd handling */
+static inline void lsd_le_to_cpu(struct lr_server_data *buf,
+ struct lr_server_data *lsd)
+{
+ int i;
+ memcpy(lsd->lsd_uuid, buf->lsd_uuid, sizeof(lsd->lsd_uuid));
+ lsd->lsd_last_transno = le64_to_cpu(buf->lsd_last_transno);
+ lsd->lsd_compat14 = le64_to_cpu(buf->lsd_compat14);
+ lsd->lsd_mount_count = le64_to_cpu(buf->lsd_mount_count);
+ lsd->lsd_feature_compat = le32_to_cpu(buf->lsd_feature_compat);
+ lsd->lsd_feature_rocompat = le32_to_cpu(buf->lsd_feature_rocompat);
+ lsd->lsd_feature_incompat = le32_to_cpu(buf->lsd_feature_incompat);
+ lsd->lsd_server_size = le32_to_cpu(buf->lsd_server_size);
+ lsd->lsd_client_start = le32_to_cpu(buf->lsd_client_start);
+ lsd->lsd_client_size = le16_to_cpu(buf->lsd_client_size);
+ lsd->lsd_subdir_count = le16_to_cpu(buf->lsd_subdir_count);
+ lsd->lsd_catalog_oid = le64_to_cpu(buf->lsd_catalog_oid);
+ lsd->lsd_catalog_ogen = le32_to_cpu(buf->lsd_catalog_ogen);
+ memcpy(lsd->lsd_peeruuid, buf->lsd_peeruuid, sizeof(lsd->lsd_peeruuid));
+ lsd->lsd_osd_index = le32_to_cpu(buf->lsd_osd_index);
+ lsd->lsd_padding1 = le32_to_cpu(buf->lsd_padding1);
+ lsd->lsd_start_epoch = le32_to_cpu(buf->lsd_start_epoch);
+ for (i = 0; i < LR_EXPIRE_INTERVALS; i++)
+ lsd->lsd_trans_table[i] = le64_to_cpu(buf->lsd_trans_table[i]);
+ lsd->lsd_trans_table_time = le32_to_cpu(buf->lsd_trans_table_time);
+ lsd->lsd_expire_intervals = le32_to_cpu(buf->lsd_expire_intervals);
+}
+
+static inline void lsd_cpu_to_le(struct lr_server_data *lsd,
+ struct lr_server_data *buf)
+{
+ int i;
+ memcpy(buf->lsd_uuid, lsd->lsd_uuid, sizeof(buf->lsd_uuid));
+ buf->lsd_last_transno = cpu_to_le64(lsd->lsd_last_transno);
+ buf->lsd_compat14 = cpu_to_le64(lsd->lsd_compat14);
+ buf->lsd_mount_count = cpu_to_le64(lsd->lsd_mount_count);
+ buf->lsd_feature_compat = cpu_to_le32(lsd->lsd_feature_compat);
+ buf->lsd_feature_rocompat = cpu_to_le32(lsd->lsd_feature_rocompat);
+ buf->lsd_feature_incompat = cpu_to_le32(lsd->lsd_feature_incompat);
+ buf->lsd_server_size = cpu_to_le32(lsd->lsd_server_size);
+ buf->lsd_client_start = cpu_to_le32(lsd->lsd_client_start);
+ buf->lsd_client_size = cpu_to_le16(lsd->lsd_client_size);
+ buf->lsd_subdir_count = cpu_to_le16(lsd->lsd_subdir_count);
+ buf->lsd_catalog_oid = cpu_to_le64(lsd->lsd_catalog_oid);
+ buf->lsd_catalog_ogen = cpu_to_le32(lsd->lsd_catalog_ogen);
+ memcpy(buf->lsd_peeruuid, lsd->lsd_peeruuid, sizeof(buf->lsd_peeruuid));
+ buf->lsd_osd_index = cpu_to_le32(lsd->lsd_osd_index);
+ buf->lsd_padding1 = cpu_to_le32(lsd->lsd_padding1);
+ buf->lsd_start_epoch = cpu_to_le32(lsd->lsd_start_epoch);
+ for (i = 0; i < LR_EXPIRE_INTERVALS; i++)
+ buf->lsd_trans_table[i] = cpu_to_le64(lsd->lsd_trans_table[i]);
+ buf->lsd_trans_table_time = cpu_to_le32(lsd->lsd_trans_table_time);
+ buf->lsd_expire_intervals = cpu_to_le32(lsd->lsd_expire_intervals);
+}
+
+static inline void lcd_le_to_cpu(struct lsd_client_data *buf,
+ struct lsd_client_data *lcd)
+{
+ memcpy(lcd->lcd_uuid, buf->lcd_uuid, sizeof (lcd->lcd_uuid));
+ lcd->lcd_last_transno = le64_to_cpu(buf->lcd_last_transno);
+ lcd->lcd_last_xid = le64_to_cpu(buf->lcd_last_xid);
+ lcd->lcd_last_result = le32_to_cpu(buf->lcd_last_result);
+ lcd->lcd_last_data = le32_to_cpu(buf->lcd_last_data);
+ lcd->lcd_last_close_transno = le64_to_cpu(buf->lcd_last_close_transno);
+ lcd->lcd_last_close_xid = le64_to_cpu(buf->lcd_last_close_xid);
+ lcd->lcd_last_close_result = le32_to_cpu(buf->lcd_last_close_result);
+ lcd->lcd_last_close_data = le32_to_cpu(buf->lcd_last_close_data);
+ lcd->lcd_pre_versions[0] = le64_to_cpu(buf->lcd_pre_versions[0]);
+ lcd->lcd_pre_versions[1] = le64_to_cpu(buf->lcd_pre_versions[1]);
+ lcd->lcd_pre_versions[2] = le64_to_cpu(buf->lcd_pre_versions[2]);
+ lcd->lcd_pre_versions[3] = le64_to_cpu(buf->lcd_pre_versions[3]);
+ lcd->lcd_last_epoch = le32_to_cpu(buf->lcd_last_epoch);
+ lcd->lcd_first_epoch = le32_to_cpu(buf->lcd_first_epoch);
+}
+
+static inline void lcd_cpu_to_le(struct lsd_client_data *lcd,
+ struct lsd_client_data *buf)
+{
+ memcpy(buf->lcd_uuid, lcd->lcd_uuid, sizeof (lcd->lcd_uuid));
+ buf->lcd_last_transno = cpu_to_le64(lcd->lcd_last_transno);
+ buf->lcd_last_xid = cpu_to_le64(lcd->lcd_last_xid);
+ buf->lcd_last_result = cpu_to_le32(lcd->lcd_last_result);
+ buf->lcd_last_data = cpu_to_le32(lcd->lcd_last_data);
+ buf->lcd_last_close_transno = cpu_to_le64(lcd->lcd_last_close_transno);
+ buf->lcd_last_close_xid = cpu_to_le64(lcd->lcd_last_close_xid);
+ buf->lcd_last_close_result = cpu_to_le32(lcd->lcd_last_close_result);
+ buf->lcd_last_close_data = cpu_to_le32(lcd->lcd_last_close_data);
+ buf->lcd_pre_versions[0] = cpu_to_le64(lcd->lcd_pre_versions[0]);
+ buf->lcd_pre_versions[1] = cpu_to_le64(lcd->lcd_pre_versions[1]);
+ buf->lcd_pre_versions[2] = cpu_to_le64(lcd->lcd_pre_versions[2]);
+ buf->lcd_pre_versions[3] = cpu_to_le64(lcd->lcd_pre_versions[3]);
+ buf->lcd_last_epoch = cpu_to_le32(lcd->lcd_last_epoch);
+ buf->lcd_first_epoch = cpu_to_le32(lcd->lcd_first_epoch);
+}
+
+static inline __u64 lcd_last_transno(struct lsd_client_data *lcd)
+{
+ return (lcd->lcd_last_transno > lcd->lcd_last_close_transno ?
+ lcd->lcd_last_transno : lcd->lcd_last_close_transno);
+}
+
+static inline __u64 lcd_last_xid(struct lsd_client_data *lcd)
+{
+ return (lcd->lcd_last_xid > lcd->lcd_last_close_xid ?
+ lcd->lcd_last_xid : lcd->lcd_last_close_xid);
+}
+
+/****************** superblock additional info *********************/
+
+struct ll_sb_info;
+
+struct lustre_sb_info {
+ int lsi_flags;
+ struct obd_device *lsi_mgc; /* mgc obd */
+ struct lustre_mount_data *lsi_lmd; /* mount command info */
+ struct ll_sb_info *lsi_llsbi; /* add'l client sbi info */
+ struct dt_device *lsi_dt_dev; /* dt device to access disk fs*/
+ struct vfsmount *lsi_srv_mnt; /* the one server mount */
+ atomic_t lsi_mounts; /* references to the srv_mnt */
+ char lsi_svname[MTI_NAME_MAXLEN];
+ char lsi_osd_obdname[64];
+ char lsi_osd_uuid[64];
+ struct obd_export *lsi_osd_exp;
+ char lsi_osd_type[16];
+ char lsi_fstype[16];
+ struct backing_dev_info lsi_bdi; /* each client mountpoint needs
+ own backing_dev_info */
+};
+
+#define LSI_UMOUNT_FAILOVER 0x00200000
+#define LSI_BDI_INITIALIZED 0x00400000
+
+#define s2lsi(sb) ((struct lustre_sb_info *)((sb)->s_fs_info))
+#define s2lsi_nocast(sb) ((sb)->s_fs_info)
+
+#define get_profile_name(sb) (s2lsi(sb)->lsi_lmd->lmd_profile)
+#define get_mount_flags(sb) (s2lsi(sb)->lsi_lmd->lmd_flags)
+#define get_mntdev_name(sb) (s2lsi(sb)->lsi_lmd->lmd_dev)
+
+
+/****************** mount lookup info *********************/
+
+struct lustre_mount_info {
+ char *lmi_name;
+ struct super_block *lmi_sb;
+ struct vfsmount *lmi_mnt;
+ struct list_head lmi_list_chain;
+};
+
+/****************** prototypes *********************/
+
+/* obd_mount.c */
+int server_name2fsname(const char *svname, char *fsname, const char **endptr);
+int server_name2index(const char *svname, __u32 *idx, const char **endptr);
+int server_name2svname(const char *label, char *svname, const char **endptr,
+ size_t svsize);
+
+int lustre_put_lsi(struct super_block *sb);
+int lustre_start_simple(char *obdname, char *type, char *uuid,
+ char *s1, char *s2, char *s3, char *s4);
+int lustre_start_mgc(struct super_block *sb);
+void lustre_register_client_fill_super(int (*cfs)(struct super_block *sb,
+ struct vfsmount *mnt));
+void lustre_register_kill_super_cb(void (*cfs)(struct super_block *sb));
+int lustre_common_put_super(struct super_block *sb);
+
+
+int mgc_fsname2resid(char *fsname, struct ldlm_res_id *res_id, int type);
+
+/** @} disk */
+
+#endif /* _LUSTRE_DISK_H */
diff --git a/drivers/staging/lustre/lustre/include/lustre_dlm.h b/drivers/staging/lustre/lustre/include/lustre_dlm.h
new file mode 100644
index 000000000..bac9902b5
--- /dev/null
+++ b/drivers/staging/lustre/lustre/include/lustre_dlm.h
@@ -0,0 +1,1480 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2010, 2012, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ */
+
+/** \defgroup LDLM Lustre Distributed Lock Manager
+ *
+ * Lustre DLM is based on VAX DLM.
+ * Its two main roles are:
+ * - To provide locking assuring consistency of data on all Lustre nodes.
+ * - To allow clients to cache state protected by a lock by holding the
+ * lock until a conflicting lock is requested or it is expired by the LRU.
+ *
+ * @{
+ */
+
+#ifndef _LUSTRE_DLM_H__
+#define _LUSTRE_DLM_H__
+
+#include "lustre_lib.h"
+#include "lustre_net.h"
+#include "lustre_import.h"
+#include "lustre_handles.h"
+#include "interval_tree.h" /* for interval_node{}, ldlm_extent */
+#include "lu_ref.h"
+
+#include "lustre_dlm_flags.h"
+
+struct obd_ops;
+struct obd_device;
+
+#define OBD_LDLM_DEVICENAME "ldlm"
+
+#define LDLM_DEFAULT_LRU_SIZE (100 * num_online_cpus())
+#define LDLM_DEFAULT_MAX_ALIVE (cfs_time_seconds(36000))
+#define LDLM_CTIME_AGE_LIMIT (10)
+#define LDLM_DEFAULT_PARALLEL_AST_LIMIT 1024
+
+/**
+ * LDLM non-error return states
+ */
+typedef enum {
+ ELDLM_OK = 0,
+
+ ELDLM_LOCK_CHANGED = 300,
+ ELDLM_LOCK_ABORTED = 301,
+ ELDLM_LOCK_REPLACED = 302,
+ ELDLM_NO_LOCK_DATA = 303,
+ ELDLM_LOCK_WOULDBLOCK = 304,
+
+ ELDLM_NAMESPACE_EXISTS = 400,
+ ELDLM_BAD_NAMESPACE = 401
+} ldlm_error_t;
+
+/**
+ * LDLM namespace type.
+ * The "client" type is actually an indication that this is a narrow local view
+ * into complete namespace on the server. Such namespaces cannot make any
+ * decisions about lack of conflicts or do any autonomous lock granting without
+ * first speaking to a server.
+ */
+typedef enum {
+ LDLM_NAMESPACE_SERVER = 1 << 0,
+ LDLM_NAMESPACE_CLIENT = 1 << 1
+} ldlm_side_t;
+
+/**
+ * The blocking callback is overloaded to perform two functions. These flags
+ * indicate which operation should be performed.
+ */
+#define LDLM_CB_BLOCKING 1
+#define LDLM_CB_CANCELING 2
+
+/**
+ * \name Lock Compatibility Matrix.
+ *
+ * A lock has both a type (extent, flock, inode bits, or plain) and a mode.
+ * Lock types are described in their respective implementation files:
+ * ldlm_{extent,flock,inodebits,plain}.c.
+ *
+ * There are six lock modes along with a compatibility matrix to indicate if
+ * two locks are compatible.
+ *
+ * - EX: Exclusive mode. Before a new file is created, MDS requests EX lock
+ * on the parent.
+ * - PW: Protective Write (normal write) mode. When a client requests a write
+ * lock from an OST, a lock with PW mode will be issued.
+ * - PR: Protective Read (normal read) mode. When a client requests a read from
+ * an OST, a lock with PR mode will be issued. Also, if the client opens a
+ * file for execution, it is granted a lock with PR mode.
+ * - CW: Concurrent Write mode. The type of lock that the MDS grants if a client
+ * requests a write lock during a file open operation.
+ * - CR Concurrent Read mode. When a client performs a path lookup, MDS grants
+ * an inodebit lock with the CR mode on the intermediate path component.
+ * - NL Null mode.
+ *
+ * <PRE>
+ * NL CR CW PR PW EX
+ * NL 1 1 1 1 1 1
+ * CR 1 1 1 1 1 0
+ * CW 1 1 1 0 0 0
+ * PR 1 1 0 1 0 0
+ * PW 1 1 0 0 0 0
+ * EX 1 0 0 0 0 0
+ * </PRE>
+ */
+/** @{ */
+#define LCK_COMPAT_EX LCK_NL
+#define LCK_COMPAT_PW (LCK_COMPAT_EX | LCK_CR)
+#define LCK_COMPAT_PR (LCK_COMPAT_PW | LCK_PR)
+#define LCK_COMPAT_CW (LCK_COMPAT_PW | LCK_CW)
+#define LCK_COMPAT_CR (LCK_COMPAT_CW | LCK_PR | LCK_PW)
+#define LCK_COMPAT_NL (LCK_COMPAT_CR | LCK_EX | LCK_GROUP)
+#define LCK_COMPAT_GROUP (LCK_GROUP | LCK_NL)
+#define LCK_COMPAT_COS (LCK_COS)
+/** @} Lock Compatibility Matrix */
+
+extern ldlm_mode_t lck_compat_array[];
+
+static inline void lockmode_verify(ldlm_mode_t mode)
+{
+ LASSERT(mode > LCK_MINMODE && mode < LCK_MAXMODE);
+}
+
+static inline int lockmode_compat(ldlm_mode_t exist_mode, ldlm_mode_t new_mode)
+{
+ return (lck_compat_array[exist_mode] & new_mode);
+}
+
+/*
+ *
+ * cluster name spaces
+ *
+ */
+
+#define DLM_OST_NAMESPACE 1
+#define DLM_MDS_NAMESPACE 2
+
+/* XXX
+ - do we just separate this by security domains and use a prefix for
+ multiple namespaces in the same domain?
+ -
+*/
+
+/**
+ * Locking rules for LDLM:
+ *
+ * lr_lock
+ *
+ * lr_lock
+ * waiting_locks_spinlock
+ *
+ * lr_lock
+ * led_lock
+ *
+ * lr_lock
+ * ns_lock
+ *
+ * lr_lvb_mutex
+ * lr_lock
+ *
+ */
+
+struct ldlm_pool;
+struct ldlm_lock;
+struct ldlm_resource;
+struct ldlm_namespace;
+
+/**
+ * Operations on LDLM pools.
+ * LDLM pool is a pool of locks in the namespace without any implicitly
+ * specified limits.
+ * Locks in the pool are organized in LRU.
+ * Local memory pressure or server instructions (e.g. mempressure on server)
+ * can trigger freeing of locks from the pool
+ */
+struct ldlm_pool_ops {
+ /** Recalculate pool \a pl usage */
+ int (*po_recalc)(struct ldlm_pool *pl);
+ /** Cancel at least \a nr locks from pool \a pl */
+ int (*po_shrink)(struct ldlm_pool *pl, int nr,
+ gfp_t gfp_mask);
+ int (*po_setup)(struct ldlm_pool *pl, int limit);
+};
+
+/** One second for pools thread check interval. Each pool has own period. */
+#define LDLM_POOLS_THREAD_PERIOD (1)
+
+/** ~6% margin for modest pools. See ldlm_pool.c for details. */
+#define LDLM_POOLS_MODEST_MARGIN_SHIFT (4)
+
+/** Default recalc period for server side pools in sec. */
+#define LDLM_POOL_SRV_DEF_RECALC_PERIOD (1)
+
+/** Default recalc period for client side pools in sec. */
+#define LDLM_POOL_CLI_DEF_RECALC_PERIOD (10)
+
+/**
+ * LDLM pool structure to track granted locks.
+ * For purposes of determining when to release locks on e.g. memory pressure.
+ * This feature is commonly referred to as lru_resize.
+ */
+struct ldlm_pool {
+ /** Pool proc directory. */
+ struct proc_dir_entry *pl_proc_dir;
+ /** Pool name, must be long enough to hold compound proc entry name. */
+ char pl_name[100];
+ /** Lock for protecting SLV/CLV updates. */
+ spinlock_t pl_lock;
+ /** Number of allowed locks in in pool, both, client and server side. */
+ atomic_t pl_limit;
+ /** Number of granted locks in */
+ atomic_t pl_granted;
+ /** Grant rate per T. */
+ atomic_t pl_grant_rate;
+ /** Cancel rate per T. */
+ atomic_t pl_cancel_rate;
+ /** Server lock volume (SLV). Protected by pl_lock. */
+ __u64 pl_server_lock_volume;
+ /** Current biggest client lock volume. Protected by pl_lock. */
+ __u64 pl_client_lock_volume;
+ /** Lock volume factor. SLV on client is calculated as following:
+ * server_slv * lock_volume_factor. */
+ atomic_t pl_lock_volume_factor;
+ /** Time when last SLV from server was obtained. */
+ time_t pl_recalc_time;
+ /** Recalculation period for pool. */
+ time_t pl_recalc_period;
+ /** Recalculation and shrink operations. */
+ const struct ldlm_pool_ops *pl_ops;
+ /** Number of planned locks for next period. */
+ int pl_grant_plan;
+ /** Pool statistics. */
+ struct lprocfs_stats *pl_stats;
+};
+
+typedef int (*ldlm_res_policy)(struct ldlm_namespace *, struct ldlm_lock **,
+ void *req_cookie, ldlm_mode_t mode, __u64 flags,
+ void *data);
+
+typedef int (*ldlm_cancel_for_recovery)(struct ldlm_lock *lock);
+
+/**
+ * LVB operations.
+ * LVB is Lock Value Block. This is a special opaque (to LDLM) value that could
+ * be associated with an LDLM lock and transferred from client to server and
+ * back.
+ *
+ * Currently LVBs are used by:
+ * - OSC-OST code to maintain current object size/times
+ * - layout lock code to return the layout when the layout lock is granted
+ */
+struct ldlm_valblock_ops {
+ int (*lvbo_init)(struct ldlm_resource *res);
+ int (*lvbo_update)(struct ldlm_resource *res,
+ struct ptlrpc_request *r,
+ int increase);
+ int (*lvbo_free)(struct ldlm_resource *res);
+ /* Return size of lvb data appropriate RPC size can be reserved */
+ int (*lvbo_size)(struct ldlm_lock *lock);
+ /* Called to fill in lvb data to RPC buffer @buf */
+ int (*lvbo_fill)(struct ldlm_lock *lock, void *buf, int buflen);
+};
+
+/**
+ * LDLM pools related, type of lock pool in the namespace.
+ * Greedy means release cached locks aggressively
+ */
+typedef enum {
+ LDLM_NAMESPACE_GREEDY = 1 << 0,
+ LDLM_NAMESPACE_MODEST = 1 << 1
+} ldlm_appetite_t;
+
+/**
+ * Default values for the "max_nolock_size", "contention_time" and
+ * "contended_locks" namespace tunables.
+ */
+#define NS_DEFAULT_MAX_NOLOCK_BYTES 0
+#define NS_DEFAULT_CONTENTION_SECONDS 2
+#define NS_DEFAULT_CONTENDED_LOCKS 32
+
+struct ldlm_ns_bucket {
+ /** back pointer to namespace */
+ struct ldlm_namespace *nsb_namespace;
+ /**
+ * Estimated lock callback time. Used by adaptive timeout code to
+ * avoid spurious client evictions due to unresponsiveness when in
+ * fact the network or overall system load is at fault
+ */
+ struct adaptive_timeout nsb_at_estimate;
+};
+
+enum {
+ /** LDLM namespace lock stats */
+ LDLM_NSS_LOCKS = 0,
+ LDLM_NSS_LAST
+};
+
+typedef enum {
+ /** invalid type */
+ LDLM_NS_TYPE_UNKNOWN = 0,
+ /** mdc namespace */
+ LDLM_NS_TYPE_MDC,
+ /** mds namespace */
+ LDLM_NS_TYPE_MDT,
+ /** osc namespace */
+ LDLM_NS_TYPE_OSC,
+ /** ost namespace */
+ LDLM_NS_TYPE_OST,
+ /** mgc namespace */
+ LDLM_NS_TYPE_MGC,
+ /** mgs namespace */
+ LDLM_NS_TYPE_MGT,
+} ldlm_ns_type_t;
+
+/**
+ * LDLM Namespace.
+ *
+ * Namespace serves to contain locks related to a particular service.
+ * There are two kinds of namespaces:
+ * - Server namespace has knowledge of all locks and is therefore authoritative
+ * to make decisions like what locks could be granted and what conflicts
+ * exist during new lock enqueue.
+ * - Client namespace only has limited knowledge about locks in the namespace,
+ * only seeing locks held by the client.
+ *
+ * Every Lustre service has one server namespace present on the server serving
+ * that service. Every client connected to the service has a client namespace
+ * for it.
+ * Every lock obtained by client in that namespace is actually represented by
+ * two in-memory locks. One on the server and one on the client. The locks are
+ * linked by a special cookie by which one node can tell to the other which lock
+ * it actually means during communications. Such locks are called remote locks.
+ * The locks held by server only without any reference to a client are called
+ * local locks.
+ */
+struct ldlm_namespace {
+ /** Backward link to OBD, required for LDLM pool to store new SLV. */
+ struct obd_device *ns_obd;
+
+ /** Flag indicating if namespace is on client instead of server */
+ ldlm_side_t ns_client;
+
+ /** Resource hash table for namespace. */
+ struct cfs_hash *ns_rs_hash;
+
+ /** serialize */
+ spinlock_t ns_lock;
+
+ /** big refcount (by bucket) */
+ atomic_t ns_bref;
+
+ /**
+ * Namespace connect flags supported by server (may be changed via
+ * /proc, LRU resize may be disabled/enabled).
+ */
+ __u64 ns_connect_flags;
+
+ /** Client side original connect flags supported by server. */
+ __u64 ns_orig_connect_flags;
+
+ /* namespace proc dir entry */
+ struct proc_dir_entry *ns_proc_dir_entry;
+
+ /**
+ * Position in global namespace list linking all namespaces on
+ * the node.
+ */
+ struct list_head ns_list_chain;
+
+ /**
+ * List of unused locks for this namespace. This list is also called
+ * LRU lock list.
+ * Unused locks are locks with zero reader/writer reference counts.
+ * This list is only used on clients for lock caching purposes.
+ * When we want to release some locks voluntarily or if server wants
+ * us to release some locks due to e.g. memory pressure, we take locks
+ * to release from the head of this list.
+ * Locks are linked via l_lru field in \see struct ldlm_lock.
+ */
+ struct list_head ns_unused_list;
+ /** Number of locks in the LRU list above */
+ int ns_nr_unused;
+
+ /**
+ * Maximum number of locks permitted in the LRU. If 0, means locks
+ * are managed by pools and there is no preset limit, rather it is all
+ * controlled by available memory on this client and on server.
+ */
+ unsigned int ns_max_unused;
+ /** Maximum allowed age (last used time) for locks in the LRU */
+ unsigned int ns_max_age;
+ /**
+ * Server only: number of times we evicted clients due to lack of reply
+ * to ASTs.
+ */
+ unsigned int ns_timeouts;
+ /**
+ * Number of seconds since the file change time after which the
+ * MDT will return an UPDATE lock along with a LOOKUP lock.
+ * This allows the client to start caching negative dentries
+ * for a directory and may save an RPC for a later stat.
+ */
+ unsigned int ns_ctime_age_limit;
+
+ /**
+ * Used to rate-limit ldlm_namespace_dump calls.
+ * \see ldlm_namespace_dump. Increased by 10 seconds every time
+ * it is called.
+ */
+ unsigned long ns_next_dump;
+
+ /** "policy" function that does actual lock conflict determination */
+ ldlm_res_policy ns_policy;
+
+ /**
+ * LVB operations for this namespace.
+ * \see struct ldlm_valblock_ops
+ */
+ struct ldlm_valblock_ops *ns_lvbo;
+
+ /**
+ * Used by filter code to store pointer to OBD of the service.
+ * Should be dropped in favor of \a ns_obd
+ */
+ void *ns_lvbp;
+
+ /**
+ * Wait queue used by __ldlm_namespace_free. Gets woken up every time
+ * a resource is removed.
+ */
+ wait_queue_head_t ns_waitq;
+ /** LDLM pool structure for this namespace */
+ struct ldlm_pool ns_pool;
+ /** Definition of how eagerly unused locks will be released from LRU */
+ ldlm_appetite_t ns_appetite;
+
+ /**
+ * If more than \a ns_contended_locks are found, the resource is
+ * considered to be contended. Lock enqueues might specify that no
+ * contended locks should be granted
+ */
+ unsigned ns_contended_locks;
+
+ /**
+ * The resources in this namespace remember contended state during
+ * \a ns_contention_time, in seconds.
+ */
+ unsigned ns_contention_time;
+
+ /**
+ * Limit size of contended extent locks, in bytes.
+ * If extended lock is requested for more then this many bytes and
+ * caller instructs us not to grant contended locks, we would disregard
+ * such a request.
+ */
+ unsigned ns_max_nolock_size;
+
+ /** Limit of parallel AST RPC count. */
+ unsigned ns_max_parallel_ast;
+
+ /** Callback to cancel locks before replaying it during recovery. */
+ ldlm_cancel_for_recovery ns_cancel_for_recovery;
+
+ /** LDLM lock stats */
+ struct lprocfs_stats *ns_stats;
+
+ /**
+ * Flag to indicate namespace is being freed. Used to determine if
+ * recalculation of LDLM pool statistics should be skipped.
+ */
+ unsigned ns_stopping:1;
+};
+
+/**
+ * Returns 1 if namespace \a ns is a client namespace.
+ */
+static inline int ns_is_client(struct ldlm_namespace *ns)
+{
+ LASSERT(ns != NULL);
+ LASSERT(!(ns->ns_client & ~(LDLM_NAMESPACE_CLIENT |
+ LDLM_NAMESPACE_SERVER)));
+ LASSERT(ns->ns_client == LDLM_NAMESPACE_CLIENT ||
+ ns->ns_client == LDLM_NAMESPACE_SERVER);
+ return ns->ns_client == LDLM_NAMESPACE_CLIENT;
+}
+
+/**
+ * Returns 1 if namespace \a ns is a server namespace.
+ */
+static inline int ns_is_server(struct ldlm_namespace *ns)
+{
+ LASSERT(ns != NULL);
+ LASSERT(!(ns->ns_client & ~(LDLM_NAMESPACE_CLIENT |
+ LDLM_NAMESPACE_SERVER)));
+ LASSERT(ns->ns_client == LDLM_NAMESPACE_CLIENT ||
+ ns->ns_client == LDLM_NAMESPACE_SERVER);
+ return ns->ns_client == LDLM_NAMESPACE_SERVER;
+}
+
+/**
+ * Returns 1 if namespace \a ns supports early lock cancel (ELC).
+ */
+static inline int ns_connect_cancelset(struct ldlm_namespace *ns)
+{
+ LASSERT(ns != NULL);
+ return !!(ns->ns_connect_flags & OBD_CONNECT_CANCELSET);
+}
+
+/**
+ * Returns 1 if this namespace supports lru_resize.
+ */
+static inline int ns_connect_lru_resize(struct ldlm_namespace *ns)
+{
+ LASSERT(ns != NULL);
+ return !!(ns->ns_connect_flags & OBD_CONNECT_LRU_RESIZE);
+}
+
+static inline void ns_register_cancel(struct ldlm_namespace *ns,
+ ldlm_cancel_for_recovery arg)
+{
+ LASSERT(ns != NULL);
+ ns->ns_cancel_for_recovery = arg;
+}
+
+struct ldlm_lock;
+
+/** Type for blocking callback function of a lock. */
+typedef int (*ldlm_blocking_callback)(struct ldlm_lock *lock,
+ struct ldlm_lock_desc *new, void *data,
+ int flag);
+/** Type for completion callback function of a lock. */
+typedef int (*ldlm_completion_callback)(struct ldlm_lock *lock, __u64 flags,
+ void *data);
+/** Type for glimpse callback function of a lock. */
+typedef int (*ldlm_glimpse_callback)(struct ldlm_lock *lock, void *data);
+
+/** Work list for sending GL ASTs to multiple locks. */
+struct ldlm_glimpse_work {
+ struct ldlm_lock *gl_lock; /* lock to glimpse */
+ struct list_head gl_list; /* linkage to other gl work structs */
+ __u32 gl_flags;/* see LDLM_GL_WORK_* below */
+ union ldlm_gl_desc *gl_desc; /* glimpse descriptor to be packed in
+ * glimpse callback request */
+};
+
+/** The ldlm_glimpse_work is allocated on the stack and should not be freed. */
+#define LDLM_GL_WORK_NOFREE 0x1
+
+/** Interval node data for each LDLM_EXTENT lock. */
+struct ldlm_interval {
+ struct interval_node li_node; /* node for tree management */
+ struct list_head li_group; /* the locks which have the same
+ * policy - group of the policy */
+};
+#define to_ldlm_interval(n) container_of(n, struct ldlm_interval, li_node)
+
+/**
+ * Interval tree for extent locks.
+ * The interval tree must be accessed under the resource lock.
+ * Interval trees are used for granted extent locks to speed up conflicts
+ * lookup. See ldlm/interval_tree.c for more details.
+ */
+struct ldlm_interval_tree {
+ /** Tree size. */
+ int lit_size;
+ ldlm_mode_t lit_mode; /* lock mode */
+ struct interval_node *lit_root; /* actual ldlm_interval */
+};
+
+/** Whether to track references to exports by LDLM locks. */
+#define LUSTRE_TRACKS_LOCK_EXP_REFS (0)
+
+/** Cancel flags. */
+typedef enum {
+ LCF_ASYNC = 0x1, /* Cancel locks asynchronously. */
+ LCF_LOCAL = 0x2, /* Cancel locks locally, not notifing server */
+ LCF_BL_AST = 0x4, /* Cancel locks marked as LDLM_FL_BL_AST
+ * in the same RPC */
+} ldlm_cancel_flags_t;
+
+struct ldlm_flock {
+ __u64 start;
+ __u64 end;
+ __u64 owner;
+ __u64 blocking_owner;
+ struct obd_export *blocking_export;
+ /* Protected by the hash lock */
+ __u32 blocking_refs;
+ __u32 pid;
+};
+
+typedef union {
+ struct ldlm_extent l_extent;
+ struct ldlm_flock l_flock;
+ struct ldlm_inodebits l_inodebits;
+} ldlm_policy_data_t;
+
+void ldlm_convert_policy_to_wire(ldlm_type_t type,
+ const ldlm_policy_data_t *lpolicy,
+ ldlm_wire_policy_data_t *wpolicy);
+void ldlm_convert_policy_to_local(struct obd_export *exp, ldlm_type_t type,
+ const ldlm_wire_policy_data_t *wpolicy,
+ ldlm_policy_data_t *lpolicy);
+
+enum lvb_type {
+ LVB_T_NONE = 0,
+ LVB_T_OST = 1,
+ LVB_T_LQUOTA = 2,
+ LVB_T_LAYOUT = 3,
+};
+
+/**
+ * LDLM lock structure
+ *
+ * Represents a single LDLM lock and its state in memory. Each lock is
+ * associated with a single ldlm_resource, the object which is being
+ * locked. There may be multiple ldlm_locks on a single resource,
+ * depending on the lock type and whether the locks are conflicting or
+ * not.
+ */
+struct ldlm_lock {
+ /**
+ * Local lock handle.
+ * When remote side wants to tell us about a lock, they address
+ * it by this opaque handle. The handle does not hold a
+ * reference on the ldlm_lock, so it can be safely passed to
+ * other threads or nodes. When the lock needs to be accessed
+ * from the handle, it is looked up again in the lock table, and
+ * may no longer exist.
+ *
+ * Must be first in the structure.
+ */
+ struct portals_handle l_handle;
+ /**
+ * Lock reference count.
+ * This is how many users have pointers to actual structure, so that
+ * we do not accidentally free lock structure that is in use.
+ */
+ atomic_t l_refc;
+ /**
+ * Internal spinlock protects l_resource. We should hold this lock
+ * first before taking res_lock.
+ */
+ spinlock_t l_lock;
+ /**
+ * Pointer to actual resource this lock is in.
+ * ldlm_lock_change_resource() can change this.
+ */
+ struct ldlm_resource *l_resource;
+ /**
+ * List item for client side LRU list.
+ * Protected by ns_lock in struct ldlm_namespace.
+ */
+ struct list_head l_lru;
+ /**
+ * Linkage to resource's lock queues according to current lock state.
+ * (could be granted, waiting or converting)
+ * Protected by lr_lock in struct ldlm_resource.
+ */
+ struct list_head l_res_link;
+ /**
+ * Tree node for ldlm_extent.
+ */
+ struct ldlm_interval *l_tree_node;
+ /**
+ * Per export hash of locks.
+ * Protected by per-bucket exp->exp_lock_hash locks.
+ */
+ struct hlist_node l_exp_hash;
+ /**
+ * Per export hash of flock locks.
+ * Protected by per-bucket exp->exp_flock_hash locks.
+ */
+ struct hlist_node l_exp_flock_hash;
+ /**
+ * Requested mode.
+ * Protected by lr_lock.
+ */
+ ldlm_mode_t l_req_mode;
+ /**
+ * Granted mode, also protected by lr_lock.
+ */
+ ldlm_mode_t l_granted_mode;
+ /** Lock completion handler pointer. Called when lock is granted. */
+ ldlm_completion_callback l_completion_ast;
+ /**
+ * Lock blocking AST handler pointer.
+ * It plays two roles:
+ * - as a notification of an attempt to queue a conflicting lock (once)
+ * - as a notification when the lock is being cancelled.
+ *
+ * As such it's typically called twice: once for the initial conflict
+ * and then once more when the last user went away and the lock is
+ * cancelled (could happen recursively).
+ */
+ ldlm_blocking_callback l_blocking_ast;
+ /**
+ * Lock glimpse handler.
+ * Glimpse handler is used to obtain LVB updates from a client by
+ * server
+ */
+ ldlm_glimpse_callback l_glimpse_ast;
+
+ /**
+ * Lock export.
+ * This is a pointer to actual client export for locks that were granted
+ * to clients. Used server-side.
+ */
+ struct obd_export *l_export;
+ /**
+ * Lock connection export.
+ * Pointer to server export on a client.
+ */
+ struct obd_export *l_conn_export;
+
+ /**
+ * Remote lock handle.
+ * If the lock is remote, this is the handle of the other side lock
+ * (l_handle)
+ */
+ struct lustre_handle l_remote_handle;
+
+ /**
+ * Representation of private data specific for a lock type.
+ * Examples are: extent range for extent lock or bitmask for ibits locks
+ */
+ ldlm_policy_data_t l_policy_data;
+
+ /**
+ * Lock state flags. Protected by lr_lock.
+ * \see lustre_dlm_flags.h where the bits are defined.
+ */
+ __u64 l_flags;
+
+ /**
+ * Lock r/w usage counters.
+ * Protected by lr_lock.
+ */
+ __u32 l_readers;
+ __u32 l_writers;
+ /**
+ * If the lock is granted, a process sleeps on this waitq to learn when
+ * it's no longer in use. If the lock is not granted, a process sleeps
+ * on this waitq to learn when it becomes granted.
+ */
+ wait_queue_head_t l_waitq;
+
+ /**
+ * Seconds. It will be updated if there is any activity related to
+ * the lock, e.g. enqueue the lock or send blocking AST.
+ */
+ unsigned long l_last_activity;
+
+ /**
+ * Time last used by e.g. being matched by lock match.
+ * Jiffies. Should be converted to time if needed.
+ */
+ unsigned long l_last_used;
+
+ /** Originally requested extent for the extent lock. */
+ struct ldlm_extent l_req_extent;
+
+ /*
+ * Client-side-only members.
+ */
+
+ enum lvb_type l_lvb_type;
+
+ /**
+ * Temporary storage for a LVB received during an enqueue operation.
+ */
+ __u32 l_lvb_len;
+ void *l_lvb_data;
+
+ /** Private storage for lock user. Opaque to LDLM. */
+ void *l_ast_data;
+
+ /*
+ * Server-side-only members.
+ */
+
+ /**
+ * Connection cookie for the client originating the operation.
+ * Used by Commit on Share (COS) code. Currently only used for
+ * inodebits locks on MDS.
+ */
+ __u64 l_client_cookie;
+
+ /**
+ * List item for locks waiting for cancellation from clients.
+ * The lists this could be linked into are:
+ * waiting_locks_list (protected by waiting_locks_spinlock),
+ * then if the lock timed out, it is moved to
+ * expired_lock_thread.elt_expired_locks for further processing.
+ * Protected by elt_lock.
+ */
+ struct list_head l_pending_chain;
+
+ /**
+ * Set when lock is sent a blocking AST. Time in seconds when timeout
+ * is reached and client holding this lock could be evicted.
+ * This timeout could be further extended by e.g. certain IO activity
+ * under this lock.
+ * \see ost_rw_prolong_locks
+ */
+ unsigned long l_callback_timeout;
+
+ /** Local PID of process which created this lock. */
+ __u32 l_pid;
+
+ /**
+ * Number of times blocking AST was sent for this lock.
+ * This is for debugging. Valid values are 0 and 1, if there is an
+ * attempt to send blocking AST more than once, an assertion would be
+ * hit. \see ldlm_work_bl_ast_lock
+ */
+ int l_bl_ast_run;
+ /** List item ldlm_add_ast_work_item() for case of blocking ASTs. */
+ struct list_head l_bl_ast;
+ /** List item ldlm_add_ast_work_item() for case of completion ASTs. */
+ struct list_head l_cp_ast;
+ /** For ldlm_add_ast_work_item() for "revoke" AST used in COS. */
+ struct list_head l_rk_ast;
+
+ /**
+ * Pointer to a conflicting lock that caused blocking AST to be sent
+ * for this lock
+ */
+ struct ldlm_lock *l_blocking_lock;
+
+ /**
+ * Protected by lr_lock, linkages to "skip lists".
+ * For more explanations of skip lists see ldlm/ldlm_inodebits.c
+ */
+ struct list_head l_sl_mode;
+ struct list_head l_sl_policy;
+
+ /** Reference tracking structure to debug leaked locks. */
+ struct lu_ref l_reference;
+#if LUSTRE_TRACKS_LOCK_EXP_REFS
+ /* Debugging stuff for bug 20498, for tracking export references. */
+ /** number of export references taken */
+ int l_exp_refs_nr;
+ /** link all locks referencing one export */
+ struct list_head l_exp_refs_link;
+ /** referenced export object */
+ struct obd_export *l_exp_refs_target;
+#endif
+ /**
+ * export blocking dlm lock list, protected by
+ * l_export->exp_bl_list_lock.
+ * Lock order of waiting_lists_spinlock, exp_bl_list_lock and res lock
+ * is: res lock -> exp_bl_list_lock -> wanting_lists_spinlock.
+ */
+ struct list_head l_exp_list;
+};
+
+/**
+ * LDLM resource description.
+ * Basically, resource is a representation for a single object.
+ * Object has a name which is currently 4 64-bit integers. LDLM user is
+ * responsible for creation of a mapping between objects it wants to be
+ * protected and resource names.
+ *
+ * A resource can only hold locks of a single lock type, though there may be
+ * multiple ldlm_locks on a single resource, depending on the lock type and
+ * whether the locks are conflicting or not.
+ */
+struct ldlm_resource {
+ struct ldlm_ns_bucket *lr_ns_bucket;
+
+ /**
+ * List item for list in namespace hash.
+ * protected by ns_lock
+ */
+ struct hlist_node lr_hash;
+
+ /** Spinlock to protect locks under this resource. */
+ spinlock_t lr_lock;
+
+ /**
+ * protected by lr_lock
+ * @{ */
+ /** List of locks in granted state */
+ struct list_head lr_granted;
+ /** List of locks waiting to change their granted mode (converted) */
+ struct list_head lr_converting;
+ /**
+ * List of locks that could not be granted due to conflicts and
+ * that are waiting for conflicts to go away */
+ struct list_head lr_waiting;
+ /** @} */
+
+ /* XXX No longer needed? Remove ASAP */
+ ldlm_mode_t lr_most_restr;
+
+ /** Type of locks this resource can hold. Only one type per resource. */
+ ldlm_type_t lr_type; /* LDLM_{PLAIN,EXTENT,FLOCK,IBITS} */
+
+ /** Resource name */
+ struct ldlm_res_id lr_name;
+ /** Reference count for this resource */
+ atomic_t lr_refcount;
+
+ /**
+ * Interval trees (only for extent locks) for all modes of this resource
+ */
+ struct ldlm_interval_tree lr_itree[LCK_MODE_NUM];
+
+ /**
+ * Server-side-only lock value block elements.
+ * To serialize lvbo_init.
+ */
+ struct mutex lr_lvb_mutex;
+ int lr_lvb_len;
+ /** protected by lr_lock */
+ void *lr_lvb_data;
+
+ /** When the resource was considered as contended. */
+ unsigned long lr_contention_time;
+ /** List of references to this resource. For debugging. */
+ struct lu_ref lr_reference;
+
+ struct inode *lr_lvb_inode;
+};
+
+static inline bool ldlm_has_layout(struct ldlm_lock *lock)
+{
+ return lock->l_resource->lr_type == LDLM_IBITS &&
+ lock->l_policy_data.l_inodebits.bits & MDS_INODELOCK_LAYOUT;
+}
+
+static inline char *
+ldlm_ns_name(struct ldlm_namespace *ns)
+{
+ return ns->ns_rs_hash->hs_name;
+}
+
+static inline struct ldlm_namespace *
+ldlm_res_to_ns(struct ldlm_resource *res)
+{
+ return res->lr_ns_bucket->nsb_namespace;
+}
+
+static inline struct ldlm_namespace *
+ldlm_lock_to_ns(struct ldlm_lock *lock)
+{
+ return ldlm_res_to_ns(lock->l_resource);
+}
+
+static inline char *
+ldlm_lock_to_ns_name(struct ldlm_lock *lock)
+{
+ return ldlm_ns_name(ldlm_lock_to_ns(lock));
+}
+
+static inline struct adaptive_timeout *
+ldlm_lock_to_ns_at(struct ldlm_lock *lock)
+{
+ return &lock->l_resource->lr_ns_bucket->nsb_at_estimate;
+}
+
+static inline int ldlm_lvbo_init(struct ldlm_resource *res)
+{
+ struct ldlm_namespace *ns = ldlm_res_to_ns(res);
+
+ if (ns->ns_lvbo != NULL && ns->ns_lvbo->lvbo_init != NULL)
+ return ns->ns_lvbo->lvbo_init(res);
+
+ return 0;
+}
+
+static inline int ldlm_lvbo_size(struct ldlm_lock *lock)
+{
+ struct ldlm_namespace *ns = ldlm_lock_to_ns(lock);
+
+ if (ns->ns_lvbo != NULL && ns->ns_lvbo->lvbo_size != NULL)
+ return ns->ns_lvbo->lvbo_size(lock);
+
+ return 0;
+}
+
+static inline int ldlm_lvbo_fill(struct ldlm_lock *lock, void *buf, int len)
+{
+ struct ldlm_namespace *ns = ldlm_lock_to_ns(lock);
+
+ if (ns->ns_lvbo != NULL) {
+ LASSERT(ns->ns_lvbo->lvbo_fill != NULL);
+ return ns->ns_lvbo->lvbo_fill(lock, buf, len);
+ }
+ return 0;
+}
+
+struct ldlm_ast_work {
+ struct ldlm_lock *w_lock;
+ int w_blocking;
+ struct ldlm_lock_desc w_desc;
+ struct list_head w_list;
+ int w_flags;
+ void *w_data;
+ int w_datalen;
+};
+
+/**
+ * Common ldlm_enqueue parameters
+ */
+struct ldlm_enqueue_info {
+ __u32 ei_type; /** Type of the lock being enqueued. */
+ __u32 ei_mode; /** Mode of the lock being enqueued. */
+ void *ei_cb_bl; /** blocking lock callback */
+ void *ei_cb_cp; /** lock completion callback */
+ void *ei_cb_gl; /** lock glimpse callback */
+ void *ei_cbdata; /** Data to be passed into callbacks. */
+};
+
+extern struct obd_ops ldlm_obd_ops;
+
+extern char *ldlm_lockname[];
+extern char *ldlm_typename[];
+extern char *ldlm_it2str(int it);
+
+/**
+ * Just a fancy CDEBUG call with log level preset to LDLM_DEBUG.
+ * For the cases where we do not have actual lock to print along
+ * with a debugging message that is ldlm-related
+ */
+#define LDLM_DEBUG_NOLOCK(format, a...) \
+ CDEBUG(D_DLMTRACE, "### " format "\n" , ##a)
+
+/**
+ * Support function for lock information printing into debug logs.
+ * \see LDLM_DEBUG
+ */
+#define ldlm_lock_debug(msgdata, mask, cdls, lock, fmt, a...) do { \
+ CFS_CHECK_STACK(msgdata, mask, cdls); \
+ \
+ if (((mask) & D_CANTMASK) != 0 || \
+ ((libcfs_debug & (mask)) != 0 && \
+ (libcfs_subsystem_debug & DEBUG_SUBSYSTEM) != 0)) \
+ _ldlm_lock_debug(lock, msgdata, fmt, ##a); \
+} while (0)
+
+void _ldlm_lock_debug(struct ldlm_lock *lock,
+ struct libcfs_debug_msg_data *data,
+ const char *fmt, ...)
+ __printf(3, 4);
+
+/**
+ * Rate-limited version of lock printing function.
+ */
+#define LDLM_DEBUG_LIMIT(mask, lock, fmt, a...) do { \
+ static struct cfs_debug_limit_state _ldlm_cdls; \
+ LIBCFS_DEBUG_MSG_DATA_DECL(msgdata, mask, &_ldlm_cdls); \
+ ldlm_lock_debug(&msgdata, mask, &_ldlm_cdls, lock, "### " fmt , ##a);\
+} while (0)
+
+#define LDLM_ERROR(lock, fmt, a...) LDLM_DEBUG_LIMIT(D_ERROR, lock, fmt, ## a)
+#define LDLM_WARN(lock, fmt, a...) LDLM_DEBUG_LIMIT(D_WARNING, lock, fmt, ## a)
+
+/** Non-rate-limited lock printing function for debugging purposes. */
+#define LDLM_DEBUG(lock, fmt, a...) do { \
+ if (likely(lock != NULL)) { \
+ LIBCFS_DEBUG_MSG_DATA_DECL(msgdata, D_DLMTRACE, NULL); \
+ ldlm_lock_debug(&msgdata, D_DLMTRACE, NULL, lock, \
+ "### " fmt , ##a); \
+ } else { \
+ LDLM_DEBUG_NOLOCK("no dlm lock: " fmt, ##a); \
+ } \
+} while (0)
+
+typedef int (*ldlm_processing_policy)(struct ldlm_lock *lock, __u64 *flags,
+ int first_enq, ldlm_error_t *err,
+ struct list_head *work_list);
+
+/**
+ * Return values for lock iterators.
+ * Also used during deciding of lock grants and cancellations.
+ */
+#define LDLM_ITER_CONTINUE 1 /* keep iterating */
+#define LDLM_ITER_STOP 2 /* stop iterating */
+
+typedef int (*ldlm_iterator_t)(struct ldlm_lock *, void *);
+typedef int (*ldlm_res_iterator_t)(struct ldlm_resource *, void *);
+
+/** \defgroup ldlm_iterator Lock iterators
+ *
+ * LDLM provides for a way to iterate through every lock on a resource or
+ * namespace or every resource in a namespace.
+ * @{ */
+int ldlm_resource_foreach(struct ldlm_resource *res, ldlm_iterator_t iter,
+ void *closure);
+void ldlm_namespace_foreach(struct ldlm_namespace *ns, ldlm_iterator_t iter,
+ void *closure);
+int ldlm_resource_iterate(struct ldlm_namespace *, const struct ldlm_res_id *,
+ ldlm_iterator_t iter, void *data);
+/** @} ldlm_iterator */
+
+int ldlm_replay_locks(struct obd_import *imp);
+
+/* ldlm_flock.c */
+int ldlm_flock_completion_ast(struct ldlm_lock *lock, __u64 flags, void *data);
+
+/* ldlm_extent.c */
+__u64 ldlm_extent_shift_kms(struct ldlm_lock *lock, __u64 old_kms);
+
+struct ldlm_callback_suite {
+ ldlm_completion_callback lcs_completion;
+ ldlm_blocking_callback lcs_blocking;
+ ldlm_glimpse_callback lcs_glimpse;
+};
+
+/* ldlm_lockd.c */
+int ldlm_del_waiting_lock(struct ldlm_lock *lock);
+int ldlm_refresh_waiting_lock(struct ldlm_lock *lock, int timeout);
+int ldlm_get_ref(void);
+void ldlm_put_ref(void);
+int ldlm_init_export(struct obd_export *exp);
+void ldlm_destroy_export(struct obd_export *exp);
+struct ldlm_lock *ldlm_request_lock(struct ptlrpc_request *req);
+
+/* ldlm_lock.c */
+void ldlm_register_intent(struct ldlm_namespace *ns, ldlm_res_policy arg);
+void ldlm_lock2handle(const struct ldlm_lock *lock,
+ struct lustre_handle *lockh);
+struct ldlm_lock *__ldlm_handle2lock(const struct lustre_handle *, __u64 flags);
+void ldlm_cancel_callback(struct ldlm_lock *);
+int ldlm_lock_remove_from_lru(struct ldlm_lock *);
+int ldlm_lock_set_data(struct lustre_handle *, void *);
+
+/**
+ * Obtain a lock reference by its handle.
+ */
+static inline struct ldlm_lock *ldlm_handle2lock(const struct lustre_handle *h)
+{
+ return __ldlm_handle2lock(h, 0);
+}
+
+#define LDLM_LOCK_REF_DEL(lock) \
+ lu_ref_del(&lock->l_reference, "handle", current)
+
+static inline struct ldlm_lock *
+ldlm_handle2lock_long(const struct lustre_handle *h, __u64 flags)
+{
+ struct ldlm_lock *lock;
+
+ lock = __ldlm_handle2lock(h, flags);
+ if (lock != NULL)
+ LDLM_LOCK_REF_DEL(lock);
+ return lock;
+}
+
+/**
+ * Update Lock Value Block Operations (LVBO) on a resource taking into account
+ * data from request \a r
+ */
+static inline int ldlm_res_lvbo_update(struct ldlm_resource *res,
+ struct ptlrpc_request *r, int increase)
+{
+ if (ldlm_res_to_ns(res)->ns_lvbo &&
+ ldlm_res_to_ns(res)->ns_lvbo->lvbo_update) {
+ return ldlm_res_to_ns(res)->ns_lvbo->lvbo_update(res, r,
+ increase);
+ }
+ return 0;
+}
+
+int ldlm_error2errno(ldlm_error_t error);
+ldlm_error_t ldlm_errno2error(int err_no); /* don't call it `errno': this
+ * confuses user-space. */
+#if LUSTRE_TRACKS_LOCK_EXP_REFS
+void ldlm_dump_export_locks(struct obd_export *exp);
+#endif
+
+/**
+ * Release a temporary lock reference obtained by ldlm_handle2lock() or
+ * __ldlm_handle2lock().
+ */
+#define LDLM_LOCK_PUT(lock) \
+do { \
+ LDLM_LOCK_REF_DEL(lock); \
+ /*LDLM_DEBUG((lock), "put");*/ \
+ ldlm_lock_put(lock); \
+} while (0)
+
+/**
+ * Release a lock reference obtained by some other means (see
+ * LDLM_LOCK_PUT()).
+ */
+#define LDLM_LOCK_RELEASE(lock) \
+do { \
+ /*LDLM_DEBUG((lock), "put");*/ \
+ ldlm_lock_put(lock); \
+} while (0)
+
+#define LDLM_LOCK_GET(lock) \
+({ \
+ ldlm_lock_get(lock); \
+ /*LDLM_DEBUG((lock), "get");*/ \
+ lock; \
+})
+
+#define ldlm_lock_list_put(head, member, count) \
+({ \
+ struct ldlm_lock *_lock, *_next; \
+ int c = count; \
+ list_for_each_entry_safe(_lock, _next, head, member) { \
+ if (c-- == 0) \
+ break; \
+ list_del_init(&_lock->member); \
+ LDLM_LOCK_RELEASE(_lock); \
+ } \
+ LASSERT(c <= 0); \
+})
+
+struct ldlm_lock *ldlm_lock_get(struct ldlm_lock *lock);
+void ldlm_lock_put(struct ldlm_lock *lock);
+void ldlm_lock_destroy(struct ldlm_lock *lock);
+void ldlm_lock2desc(struct ldlm_lock *lock, struct ldlm_lock_desc *desc);
+void ldlm_lock_addref(struct lustre_handle *lockh, __u32 mode);
+int ldlm_lock_addref_try(struct lustre_handle *lockh, __u32 mode);
+void ldlm_lock_decref(struct lustre_handle *lockh, __u32 mode);
+void ldlm_lock_decref_and_cancel(struct lustre_handle *lockh, __u32 mode);
+void ldlm_lock_fail_match_locked(struct ldlm_lock *lock);
+void ldlm_lock_fail_match(struct ldlm_lock *lock);
+void ldlm_lock_allow_match(struct ldlm_lock *lock);
+void ldlm_lock_allow_match_locked(struct ldlm_lock *lock);
+ldlm_mode_t ldlm_lock_match(struct ldlm_namespace *ns, __u64 flags,
+ const struct ldlm_res_id *, ldlm_type_t type,
+ ldlm_policy_data_t *, ldlm_mode_t mode,
+ struct lustre_handle *, int unref);
+ldlm_mode_t ldlm_revalidate_lock_handle(struct lustre_handle *lockh,
+ __u64 *bits);
+struct ldlm_resource *ldlm_lock_convert(struct ldlm_lock *lock, int new_mode,
+ __u32 *flags);
+void ldlm_lock_downgrade(struct ldlm_lock *lock, int new_mode);
+void ldlm_lock_cancel(struct ldlm_lock *lock);
+void ldlm_reprocess_all(struct ldlm_resource *res);
+void ldlm_reprocess_all_ns(struct ldlm_namespace *ns);
+void ldlm_lock_dump_handle(int level, struct lustre_handle *);
+void ldlm_unlink_lock_skiplist(struct ldlm_lock *req);
+
+/* resource.c */
+struct ldlm_namespace *
+ldlm_namespace_new(struct obd_device *obd, char *name,
+ ldlm_side_t client, ldlm_appetite_t apt,
+ ldlm_ns_type_t ns_type);
+int ldlm_namespace_cleanup(struct ldlm_namespace *ns, __u64 flags);
+void ldlm_namespace_free(struct ldlm_namespace *ns,
+ struct obd_import *imp, int force);
+void ldlm_namespace_register(struct ldlm_namespace *ns, ldlm_side_t client);
+void ldlm_namespace_unregister(struct ldlm_namespace *ns, ldlm_side_t client);
+void ldlm_namespace_get(struct ldlm_namespace *ns);
+void ldlm_namespace_put(struct ldlm_namespace *ns);
+#if defined (CONFIG_PROC_FS)
+int ldlm_proc_setup(void);
+void ldlm_proc_cleanup(void);
+#else
+static inline int ldlm_proc_setup(void) { return 0; }
+static inline void ldlm_proc_cleanup(void) {}
+#endif
+
+/* resource.c - internal */
+struct ldlm_resource *ldlm_resource_get(struct ldlm_namespace *ns,
+ struct ldlm_resource *parent,
+ const struct ldlm_res_id *,
+ ldlm_type_t type, int create);
+struct ldlm_resource *ldlm_resource_getref(struct ldlm_resource *res);
+int ldlm_resource_putref(struct ldlm_resource *res);
+void ldlm_resource_add_lock(struct ldlm_resource *res,
+ struct list_head *head,
+ struct ldlm_lock *lock);
+void ldlm_resource_unlink_lock(struct ldlm_lock *lock);
+void ldlm_res2desc(struct ldlm_resource *res, struct ldlm_resource_desc *desc);
+void ldlm_dump_all_namespaces(ldlm_side_t client, int level);
+void ldlm_namespace_dump(int level, struct ldlm_namespace *);
+void ldlm_resource_dump(int level, struct ldlm_resource *);
+int ldlm_lock_change_resource(struct ldlm_namespace *, struct ldlm_lock *,
+ const struct ldlm_res_id *);
+
+#define LDLM_RESOURCE_ADDREF(res) do { \
+ lu_ref_add_atomic(&(res)->lr_reference, __func__, current); \
+} while (0)
+
+#define LDLM_RESOURCE_DELREF(res) do { \
+ lu_ref_del(&(res)->lr_reference, __func__, current); \
+} while (0)
+
+/* ldlm_request.c */
+int ldlm_expired_completion_wait(void *data);
+/** \defgroup ldlm_local_ast Default AST handlers for local locks
+ * These AST handlers are typically used for server-side local locks and are
+ * also used by client-side lock handlers to perform minimum level base
+ * processing.
+ * @{ */
+int ldlm_blocking_ast_nocheck(struct ldlm_lock *lock);
+int ldlm_blocking_ast(struct ldlm_lock *lock, struct ldlm_lock_desc *desc,
+ void *data, int flag);
+int ldlm_glimpse_ast(struct ldlm_lock *lock, void *reqp);
+int ldlm_completion_ast_async(struct ldlm_lock *lock, __u64 flags, void *data);
+int ldlm_completion_ast(struct ldlm_lock *lock, __u64 flags, void *data);
+/** @} ldlm_local_ast */
+
+/** \defgroup ldlm_cli_api API to operate on locks from actual LDLM users.
+ * These are typically used by client and server (*_local versions)
+ * to obtain and release locks.
+ * @{ */
+int ldlm_cli_enqueue(struct obd_export *exp, struct ptlrpc_request **reqp,
+ struct ldlm_enqueue_info *einfo,
+ const struct ldlm_res_id *res_id,
+ ldlm_policy_data_t const *policy, __u64 *flags,
+ void *lvb, __u32 lvb_len, enum lvb_type lvb_type,
+ struct lustre_handle *lockh, int async);
+int ldlm_prep_enqueue_req(struct obd_export *exp,
+ struct ptlrpc_request *req,
+ struct list_head *cancels,
+ int count);
+int ldlm_prep_elc_req(struct obd_export *exp,
+ struct ptlrpc_request *req,
+ int version, int opc, int canceloff,
+ struct list_head *cancels, int count);
+
+struct ptlrpc_request *ldlm_enqueue_pack(struct obd_export *exp, int lvb_len);
+int ldlm_handle_enqueue0(struct ldlm_namespace *ns, struct ptlrpc_request *req,
+ const struct ldlm_request *dlm_req,
+ const struct ldlm_callback_suite *cbs);
+int ldlm_cli_enqueue_fini(struct obd_export *exp, struct ptlrpc_request *req,
+ ldlm_type_t type, __u8 with_policy, ldlm_mode_t mode,
+ __u64 *flags, void *lvb, __u32 lvb_len,
+ struct lustre_handle *lockh, int rc);
+int ldlm_cli_enqueue_local(struct ldlm_namespace *ns,
+ const struct ldlm_res_id *res_id,
+ ldlm_type_t type, ldlm_policy_data_t *policy,
+ ldlm_mode_t mode, __u64 *flags,
+ ldlm_blocking_callback blocking,
+ ldlm_completion_callback completion,
+ ldlm_glimpse_callback glimpse,
+ void *data, __u32 lvb_len, enum lvb_type lvb_type,
+ const __u64 *client_cookie,
+ struct lustre_handle *lockh);
+int ldlm_server_ast(struct lustre_handle *lockh, struct ldlm_lock_desc *new,
+ void *data, __u32 data_len);
+int ldlm_cli_convert(struct lustre_handle *, int new_mode, __u32 *flags);
+int ldlm_cli_update_pool(struct ptlrpc_request *req);
+int ldlm_cli_cancel(struct lustre_handle *lockh,
+ ldlm_cancel_flags_t cancel_flags);
+int ldlm_cli_cancel_unused(struct ldlm_namespace *, const struct ldlm_res_id *,
+ ldlm_cancel_flags_t flags, void *opaque);
+int ldlm_cli_cancel_unused_resource(struct ldlm_namespace *ns,
+ const struct ldlm_res_id *res_id,
+ ldlm_policy_data_t *policy,
+ ldlm_mode_t mode,
+ ldlm_cancel_flags_t flags,
+ void *opaque);
+int ldlm_cli_cancel_req(struct obd_export *exp, struct list_head *head,
+ int count, ldlm_cancel_flags_t flags);
+int ldlm_cancel_resource_local(struct ldlm_resource *res,
+ struct list_head *cancels,
+ ldlm_policy_data_t *policy,
+ ldlm_mode_t mode, __u64 lock_flags,
+ ldlm_cancel_flags_t cancel_flags, void *opaque);
+int ldlm_cli_cancel_list_local(struct list_head *cancels, int count,
+ ldlm_cancel_flags_t flags);
+int ldlm_cli_cancel_list(struct list_head *head, int count,
+ struct ptlrpc_request *req, ldlm_cancel_flags_t flags);
+/** @} ldlm_cli_api */
+
+/* mds/handler.c */
+/* This has to be here because recursive inclusion sucks. */
+int intent_disposition(struct ldlm_reply *rep, int flag);
+void intent_set_disposition(struct ldlm_reply *rep, int flag);
+
+
+/* ioctls for trying requests */
+#define IOC_LDLM_TYPE 'f'
+#define IOC_LDLM_MIN_NR 40
+
+#define IOC_LDLM_TEST _IOWR('f', 40, long)
+#define IOC_LDLM_DUMP _IOWR('f', 41, long)
+#define IOC_LDLM_REGRESS_START _IOWR('f', 42, long)
+#define IOC_LDLM_REGRESS_STOP _IOWR('f', 43, long)
+#define IOC_LDLM_MAX_NR 43
+
+/**
+ * "Modes" of acquiring lock_res, necessary to tell lockdep that taking more
+ * than one lock_res is dead-lock safe.
+ */
+enum lock_res_type {
+ LRT_NORMAL,
+ LRT_NEW
+};
+
+/** Lock resource. */
+static inline void lock_res(struct ldlm_resource *res)
+{
+ spin_lock(&res->lr_lock);
+}
+
+/** Lock resource with a way to instruct lockdep code about nestedness-safe. */
+static inline void lock_res_nested(struct ldlm_resource *res,
+ enum lock_res_type mode)
+{
+ spin_lock_nested(&res->lr_lock, mode);
+}
+
+/** Unlock resource. */
+static inline void unlock_res(struct ldlm_resource *res)
+{
+ spin_unlock(&res->lr_lock);
+}
+
+/** Check if resource is already locked, assert if not. */
+static inline void check_res_locked(struct ldlm_resource *res)
+{
+ assert_spin_locked(&res->lr_lock);
+}
+
+struct ldlm_resource *lock_res_and_lock(struct ldlm_lock *lock);
+void unlock_res_and_lock(struct ldlm_lock *lock);
+
+/* ldlm_pool.c */
+/** \defgroup ldlm_pools Various LDLM pool related functions
+ * There are not used outside of ldlm.
+ * @{
+ */
+int ldlm_pools_recalc(ldlm_side_t client);
+int ldlm_pools_init(void);
+void ldlm_pools_fini(void);
+
+int ldlm_pool_init(struct ldlm_pool *pl, struct ldlm_namespace *ns,
+ int idx, ldlm_side_t client);
+int ldlm_pool_shrink(struct ldlm_pool *pl, int nr,
+ gfp_t gfp_mask);
+void ldlm_pool_fini(struct ldlm_pool *pl);
+int ldlm_pool_setup(struct ldlm_pool *pl, int limit);
+int ldlm_pool_recalc(struct ldlm_pool *pl);
+__u32 ldlm_pool_get_lvf(struct ldlm_pool *pl);
+__u64 ldlm_pool_get_slv(struct ldlm_pool *pl);
+__u64 ldlm_pool_get_clv(struct ldlm_pool *pl);
+__u32 ldlm_pool_get_limit(struct ldlm_pool *pl);
+void ldlm_pool_set_slv(struct ldlm_pool *pl, __u64 slv);
+void ldlm_pool_set_clv(struct ldlm_pool *pl, __u64 clv);
+void ldlm_pool_set_limit(struct ldlm_pool *pl, __u32 limit);
+void ldlm_pool_add(struct ldlm_pool *pl, struct ldlm_lock *lock);
+void ldlm_pool_del(struct ldlm_pool *pl, struct ldlm_lock *lock);
+/** @} */
+
+#endif
+/** @} LDLM */
diff --git a/drivers/staging/lustre/lustre/include/lustre_dlm_flags.h b/drivers/staging/lustre/lustre/include/lustre_dlm_flags.h
new file mode 100644
index 000000000..16dcdbfae
--- /dev/null
+++ b/drivers/staging/lustre/lustre/include/lustre_dlm_flags.h
@@ -0,0 +1,476 @@
+/* -*- buffer-read-only: t -*- vi: set ro:
+ *
+ * DO NOT EDIT THIS FILE (lustre_dlm_flags.h)
+ *
+ * It has been AutoGen-ed
+ * From the definitions lustre_dlm_flags.def
+ * and the template file lustre_dlm_flags.tpl
+ *
+ * lustre is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * lustre 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, see <http://www.gnu.org/licenses/>.
+ */
+/**
+ * \file lustre_dlm_flags.h
+ * The flags and collections of flags (masks) for \see struct ldlm_lock.
+ * This file is derived from flag definitions in lustre_dlm_flags.def.
+ * The format is defined in the lustre_dlm_flags.tpl template file.
+ *
+ * \addtogroup LDLM Lustre Distributed Lock Manager
+ * @{
+ *
+ * \name flags
+ * The flags and collections of flags (masks) for \see struct ldlm_lock.
+ * @{
+ */
+#ifndef LDLM_ALL_FLAGS_MASK
+
+/** l_flags bits marked as "all_flags" bits */
+#define LDLM_FL_ALL_FLAGS_MASK 0x00FFFFFFC08F932FULL
+
+/** l_flags bits marked as "ast" bits */
+#define LDLM_FL_AST_MASK 0x0000000080008000ULL
+
+/** l_flags bits marked as "blocked" bits */
+#define LDLM_FL_BLOCKED_MASK 0x000000000000000EULL
+
+/** l_flags bits marked as "gone" bits */
+#define LDLM_FL_GONE_MASK 0x0006004000000000ULL
+
+/** l_flags bits marked as "hide_lock" bits */
+#define LDLM_FL_HIDE_LOCK_MASK 0x0000206400000000ULL
+
+/** l_flags bits marked as "inherit" bits */
+#define LDLM_FL_INHERIT_MASK 0x0000000000800000ULL
+
+/** l_flags bits marked as "local_only" bits */
+#define LDLM_FL_LOCAL_ONLY_MASK 0x00FFFFFF00000000ULL
+
+/** l_flags bits marked as "on_wire" bits */
+#define LDLM_FL_ON_WIRE_MASK 0x00000000C08F932FULL
+
+/** extent, mode, or resource changed */
+#define LDLM_FL_LOCK_CHANGED 0x0000000000000001ULL // bit 0
+#define ldlm_is_lock_changed(_l) LDLM_TEST_FLAG(( _l), 1ULL << 0)
+#define ldlm_set_lock_changed(_l) LDLM_SET_FLAG(( _l), 1ULL << 0)
+#define ldlm_clear_lock_changed(_l) LDLM_CLEAR_FLAG((_l), 1ULL << 0)
+
+/**
+ * Server placed lock on granted list, or a recovering client wants the
+ * lock added to the granted list, no questions asked. */
+#define LDLM_FL_BLOCK_GRANTED 0x0000000000000002ULL // bit 1
+#define ldlm_is_block_granted(_l) LDLM_TEST_FLAG(( _l), 1ULL << 1)
+#define ldlm_set_block_granted(_l) LDLM_SET_FLAG(( _l), 1ULL << 1)
+#define ldlm_clear_block_granted(_l) LDLM_CLEAR_FLAG((_l), 1ULL << 1)
+
+/**
+ * Server placed lock on conv list, or a recovering client wants the lock
+ * added to the conv list, no questions asked. */
+#define LDLM_FL_BLOCK_CONV 0x0000000000000004ULL // bit 2
+#define ldlm_is_block_conv(_l) LDLM_TEST_FLAG(( _l), 1ULL << 2)
+#define ldlm_set_block_conv(_l) LDLM_SET_FLAG(( _l), 1ULL << 2)
+#define ldlm_clear_block_conv(_l) LDLM_CLEAR_FLAG((_l), 1ULL << 2)
+
+/**
+ * Server placed lock on wait list, or a recovering client wants the lock
+ * added to the wait list, no questions asked. */
+#define LDLM_FL_BLOCK_WAIT 0x0000000000000008ULL // bit 3
+#define ldlm_is_block_wait(_l) LDLM_TEST_FLAG(( _l), 1ULL << 3)
+#define ldlm_set_block_wait(_l) LDLM_SET_FLAG(( _l), 1ULL << 3)
+#define ldlm_clear_block_wait(_l) LDLM_CLEAR_FLAG((_l), 1ULL << 3)
+
+/** blocking or cancel packet was queued for sending. */
+#define LDLM_FL_AST_SENT 0x0000000000000020ULL // bit 5
+#define ldlm_is_ast_sent(_l) LDLM_TEST_FLAG(( _l), 1ULL << 5)
+#define ldlm_set_ast_sent(_l) LDLM_SET_FLAG(( _l), 1ULL << 5)
+#define ldlm_clear_ast_sent(_l) LDLM_CLEAR_FLAG((_l), 1ULL << 5)
+
+/**
+ * Lock is being replayed. This could probably be implied by the fact that
+ * one of BLOCK_{GRANTED,CONV,WAIT} is set, but that is pretty dangerous. */
+#define LDLM_FL_REPLAY 0x0000000000000100ULL // bit 8
+#define ldlm_is_replay(_l) LDLM_TEST_FLAG(( _l), 1ULL << 8)
+#define ldlm_set_replay(_l) LDLM_SET_FLAG(( _l), 1ULL << 8)
+#define ldlm_clear_replay(_l) LDLM_CLEAR_FLAG((_l), 1ULL << 8)
+
+/** Don't grant lock, just do intent. */
+#define LDLM_FL_INTENT_ONLY 0x0000000000000200ULL // bit 9
+#define ldlm_is_intent_only(_l) LDLM_TEST_FLAG(( _l), 1ULL << 9)
+#define ldlm_set_intent_only(_l) LDLM_SET_FLAG(( _l), 1ULL << 9)
+#define ldlm_clear_intent_only(_l) LDLM_CLEAR_FLAG((_l), 1ULL << 9)
+
+/** lock request has intent */
+#define LDLM_FL_HAS_INTENT 0x0000000000001000ULL // bit 12
+#define ldlm_is_has_intent(_l) LDLM_TEST_FLAG(( _l), 1ULL << 12)
+#define ldlm_set_has_intent(_l) LDLM_SET_FLAG(( _l), 1ULL << 12)
+#define ldlm_clear_has_intent(_l) LDLM_CLEAR_FLAG((_l), 1ULL << 12)
+
+/** flock deadlock detected */
+#define LDLM_FL_FLOCK_DEADLOCK 0x0000000000008000ULL /* bit 15 */
+#define ldlm_is_flock_deadlock(_l) LDLM_TEST_FLAG((_l), 1ULL << 15)
+#define ldlm_set_flock_deadlock(_l) LDLM_SET_FLAG((_l), 1ULL << 15)
+#define ldlm_clear_flock_deadlock(_l) LDLM_CLEAR_FLAG((_l), 1ULL << 15)
+
+/** discard (no writeback) on cancel */
+#define LDLM_FL_DISCARD_DATA 0x0000000000010000ULL // bit 16
+#define ldlm_is_discard_data(_l) LDLM_TEST_FLAG(( _l), 1ULL << 16)
+#define ldlm_set_discard_data(_l) LDLM_SET_FLAG(( _l), 1ULL << 16)
+#define ldlm_clear_discard_data(_l) LDLM_CLEAR_FLAG((_l), 1ULL << 16)
+
+/** Blocked by group lock - wait indefinitely */
+#define LDLM_FL_NO_TIMEOUT 0x0000000000020000ULL // bit 17
+#define ldlm_is_no_timeout(_l) LDLM_TEST_FLAG(( _l), 1ULL << 17)
+#define ldlm_set_no_timeout(_l) LDLM_SET_FLAG(( _l), 1ULL << 17)
+#define ldlm_clear_no_timeout(_l) LDLM_CLEAR_FLAG((_l), 1ULL << 17)
+
+/**
+ * Server told not to wait if blocked. For AGL, OST will not send glimpse
+ * callback. */
+#define LDLM_FL_BLOCK_NOWAIT 0x0000000000040000ULL // bit 18
+#define ldlm_is_block_nowait(_l) LDLM_TEST_FLAG(( _l), 1ULL << 18)
+#define ldlm_set_block_nowait(_l) LDLM_SET_FLAG(( _l), 1ULL << 18)
+#define ldlm_clear_block_nowait(_l) LDLM_CLEAR_FLAG((_l), 1ULL << 18)
+
+/** return blocking lock */
+#define LDLM_FL_TEST_LOCK 0x0000000000080000ULL // bit 19
+#define ldlm_is_test_lock(_l) LDLM_TEST_FLAG(( _l), 1ULL << 19)
+#define ldlm_set_test_lock(_l) LDLM_SET_FLAG(( _l), 1ULL << 19)
+#define ldlm_clear_test_lock(_l) LDLM_CLEAR_FLAG((_l), 1ULL << 19)
+
+/**
+ * Immediately cancel such locks when they block some other locks. Send
+ * cancel notification to original lock holder, but expect no reply. This
+ * is for clients (like liblustre) that cannot be expected to reliably
+ * response to blocking AST. */
+#define LDLM_FL_CANCEL_ON_BLOCK 0x0000000000800000ULL // bit 23
+#define ldlm_is_cancel_on_block(_l) LDLM_TEST_FLAG(( _l), 1ULL << 23)
+#define ldlm_set_cancel_on_block(_l) LDLM_SET_FLAG(( _l), 1ULL << 23)
+#define ldlm_clear_cancel_on_block(_l) LDLM_CLEAR_FLAG((_l), 1ULL << 23)
+
+/**
+ * measure lock contention and return -EUSERS if locking contention is high */
+#define LDLM_FL_DENY_ON_CONTENTION 0x0000000040000000ULL // bit 30
+#define ldlm_is_deny_on_contention(_l) LDLM_TEST_FLAG(( _l), 1ULL << 30)
+#define ldlm_set_deny_on_contention(_l) LDLM_SET_FLAG(( _l), 1ULL << 30)
+#define ldlm_clear_deny_on_contention(_l) LDLM_CLEAR_FLAG((_l), 1ULL << 30)
+
+/**
+ * These are flags that are mapped into the flags and ASTs of blocking
+ * locks Add FL_DISCARD to blocking ASTs */
+#define LDLM_FL_AST_DISCARD_DATA 0x0000000080000000ULL // bit 31
+#define ldlm_is_ast_discard_data(_l) LDLM_TEST_FLAG(( _l), 1ULL << 31)
+#define ldlm_set_ast_discard_data(_l) LDLM_SET_FLAG(( _l), 1ULL << 31)
+#define ldlm_clear_ast_discard_data(_l) LDLM_CLEAR_FLAG((_l), 1ULL << 31)
+
+/**
+ * Used for marking lock as a target for -EINTR while cp_ast sleep emulation
+ * + race with upcoming bl_ast. */
+#define LDLM_FL_FAIL_LOC 0x0000000100000000ULL // bit 32
+#define ldlm_is_fail_loc(_l) LDLM_TEST_FLAG(( _l), 1ULL << 32)
+#define ldlm_set_fail_loc(_l) LDLM_SET_FLAG(( _l), 1ULL << 32)
+#define ldlm_clear_fail_loc(_l) LDLM_CLEAR_FLAG((_l), 1ULL << 32)
+
+/**
+ * Used while processing the unused list to know that we have already
+ * handled this lock and decided to skip it. */
+#define LDLM_FL_SKIPPED 0x0000000200000000ULL // bit 33
+#define ldlm_is_skipped(_l) LDLM_TEST_FLAG(( _l), 1ULL << 33)
+#define ldlm_set_skipped(_l) LDLM_SET_FLAG(( _l), 1ULL << 33)
+#define ldlm_clear_skipped(_l) LDLM_CLEAR_FLAG((_l), 1ULL << 33)
+
+/** this lock is being destroyed */
+#define LDLM_FL_CBPENDING 0x0000000400000000ULL // bit 34
+#define ldlm_is_cbpending(_l) LDLM_TEST_FLAG(( _l), 1ULL << 34)
+#define ldlm_set_cbpending(_l) LDLM_SET_FLAG(( _l), 1ULL << 34)
+#define ldlm_clear_cbpending(_l) LDLM_CLEAR_FLAG((_l), 1ULL << 34)
+
+/** not a real flag, not saved in lock */
+#define LDLM_FL_WAIT_NOREPROC 0x0000000800000000ULL // bit 35
+#define ldlm_is_wait_noreproc(_l) LDLM_TEST_FLAG(( _l), 1ULL << 35)
+#define ldlm_set_wait_noreproc(_l) LDLM_SET_FLAG(( _l), 1ULL << 35)
+#define ldlm_clear_wait_noreproc(_l) LDLM_CLEAR_FLAG((_l), 1ULL << 35)
+
+/** cancellation callback already run */
+#define LDLM_FL_CANCEL 0x0000001000000000ULL // bit 36
+#define ldlm_is_cancel(_l) LDLM_TEST_FLAG(( _l), 1ULL << 36)
+#define ldlm_set_cancel(_l) LDLM_SET_FLAG(( _l), 1ULL << 36)
+#define ldlm_clear_cancel(_l) LDLM_CLEAR_FLAG((_l), 1ULL << 36)
+
+/** whatever it might mean */
+#define LDLM_FL_LOCAL_ONLY 0x0000002000000000ULL // bit 37
+#define ldlm_is_local_only(_l) LDLM_TEST_FLAG(( _l), 1ULL << 37)
+#define ldlm_set_local_only(_l) LDLM_SET_FLAG(( _l), 1ULL << 37)
+#define ldlm_clear_local_only(_l) LDLM_CLEAR_FLAG((_l), 1ULL << 37)
+
+/** don't run the cancel callback under ldlm_cli_cancel_unused */
+#define LDLM_FL_FAILED 0x0000004000000000ULL // bit 38
+#define ldlm_is_failed(_l) LDLM_TEST_FLAG(( _l), 1ULL << 38)
+#define ldlm_set_failed(_l) LDLM_SET_FLAG(( _l), 1ULL << 38)
+#define ldlm_clear_failed(_l) LDLM_CLEAR_FLAG((_l), 1ULL << 38)
+
+/** lock cancel has already been sent */
+#define LDLM_FL_CANCELING 0x0000008000000000ULL // bit 39
+#define ldlm_is_canceling(_l) LDLM_TEST_FLAG(( _l), 1ULL << 39)
+#define ldlm_set_canceling(_l) LDLM_SET_FLAG(( _l), 1ULL << 39)
+#define ldlm_clear_canceling(_l) LDLM_CLEAR_FLAG((_l), 1ULL << 39)
+
+/** local lock (ie, no srv/cli split) */
+#define LDLM_FL_LOCAL 0x0000010000000000ULL // bit 40
+#define ldlm_is_local(_l) LDLM_TEST_FLAG(( _l), 1ULL << 40)
+#define ldlm_set_local(_l) LDLM_SET_FLAG(( _l), 1ULL << 40)
+#define ldlm_clear_local(_l) LDLM_CLEAR_FLAG((_l), 1ULL << 40)
+
+/**
+ * XXX FIXME: This is being added to b_size as a low-risk fix to the
+ * fact that the LVB filling happens _after_ the lock has been granted,
+ * so another thread can match it before the LVB has been updated. As a
+ * dirty hack, we set LDLM_FL_LVB_READY only after we've done the LVB poop.
+ * this is only needed on LOV/OSC now, where LVB is actually used and
+ * callers must set it in input flags.
+ *
+ * The proper fix is to do the granting inside of the completion AST,
+ * which can be replaced with a LVB-aware wrapping function for OSC locks.
+ * That change is pretty high-risk, though, and would need a lot more
+ * testing. */
+#define LDLM_FL_LVB_READY 0x0000020000000000ULL // bit 41
+#define ldlm_is_lvb_ready(_l) LDLM_TEST_FLAG(( _l), 1ULL << 41)
+#define ldlm_set_lvb_ready(_l) LDLM_SET_FLAG(( _l), 1ULL << 41)
+#define ldlm_clear_lvb_ready(_l) LDLM_CLEAR_FLAG((_l), 1ULL << 41)
+
+/**
+ * A lock contributes to the known minimum size (KMS) calculation until it
+ * has finished the part of its cancellation that performs write back on its
+ * dirty pages. It can remain on the granted list during this whole time.
+ * Threads racing to update the KMS after performing their writeback need
+ * to know to exclude each other's locks from the calculation as they walk
+ * the granted list. */
+#define LDLM_FL_KMS_IGNORE 0x0000040000000000ULL // bit 42
+#define ldlm_is_kms_ignore(_l) LDLM_TEST_FLAG(( _l), 1ULL << 42)
+#define ldlm_set_kms_ignore(_l) LDLM_SET_FLAG(( _l), 1ULL << 42)
+#define ldlm_clear_kms_ignore(_l) LDLM_CLEAR_FLAG((_l), 1ULL << 42)
+
+/** completion AST to be executed */
+#define LDLM_FL_CP_REQD 0x0000080000000000ULL // bit 43
+#define ldlm_is_cp_reqd(_l) LDLM_TEST_FLAG(( _l), 1ULL << 43)
+#define ldlm_set_cp_reqd(_l) LDLM_SET_FLAG(( _l), 1ULL << 43)
+#define ldlm_clear_cp_reqd(_l) LDLM_CLEAR_FLAG((_l), 1ULL << 43)
+
+/** cleanup_resource has already handled the lock */
+#define LDLM_FL_CLEANED 0x0000100000000000ULL // bit 44
+#define ldlm_is_cleaned(_l) LDLM_TEST_FLAG(( _l), 1ULL << 44)
+#define ldlm_set_cleaned(_l) LDLM_SET_FLAG(( _l), 1ULL << 44)
+#define ldlm_clear_cleaned(_l) LDLM_CLEAR_FLAG((_l), 1ULL << 44)
+
+/**
+ * optimization hint: LDLM can run blocking callback from current context
+ * w/o involving separate thread. in order to decrease cs rate */
+#define LDLM_FL_ATOMIC_CB 0x0000200000000000ULL // bit 45
+#define ldlm_is_atomic_cb(_l) LDLM_TEST_FLAG(( _l), 1ULL << 45)
+#define ldlm_set_atomic_cb(_l) LDLM_SET_FLAG(( _l), 1ULL << 45)
+#define ldlm_clear_atomic_cb(_l) LDLM_CLEAR_FLAG((_l), 1ULL << 45)
+
+/**
+ * It may happen that a client initiates two operations, e.g. unlink and
+ * mkdir, such that the server sends a blocking AST for conflicting locks
+ * to this client for the first operation, whereas the second operation
+ * has canceled this lock and is waiting for rpc_lock which is taken by
+ * the first operation. LDLM_FL_BL_AST is set by ldlm_callback_handler() in
+ * the lock to prevent the Early Lock Cancel (ELC) code from cancelling it.
+ *
+ * LDLM_FL_BL_DONE is to be set by ldlm_cancel_callback() when lock cache is
+ * dropped to let ldlm_callback_handler() return EINVAL to the server. It
+ * is used when ELC RPC is already prepared and is waiting for rpc_lock,
+ * too late to send a separate CANCEL RPC. */
+#define LDLM_FL_BL_AST 0x0000400000000000ULL // bit 46
+#define ldlm_is_bl_ast(_l) LDLM_TEST_FLAG(( _l), 1ULL << 46)
+#define ldlm_set_bl_ast(_l) LDLM_SET_FLAG(( _l), 1ULL << 46)
+#define ldlm_clear_bl_ast(_l) LDLM_CLEAR_FLAG((_l), 1ULL << 46)
+
+/** whatever it might mean */
+#define LDLM_FL_BL_DONE 0x0000800000000000ULL // bit 47
+#define ldlm_is_bl_done(_l) LDLM_TEST_FLAG(( _l), 1ULL << 47)
+#define ldlm_set_bl_done(_l) LDLM_SET_FLAG(( _l), 1ULL << 47)
+#define ldlm_clear_bl_done(_l) LDLM_CLEAR_FLAG((_l), 1ULL << 47)
+
+/**
+ * Don't put lock into the LRU list, so that it is not canceled due
+ * to aging. Used by MGC locks, they are cancelled only at unmount or
+ * by callback. */
+#define LDLM_FL_NO_LRU 0x0001000000000000ULL // bit 48
+#define ldlm_is_no_lru(_l) LDLM_TEST_FLAG(( _l), 1ULL << 48)
+#define ldlm_set_no_lru(_l) LDLM_SET_FLAG(( _l), 1ULL << 48)
+#define ldlm_clear_no_lru(_l) LDLM_CLEAR_FLAG((_l), 1ULL << 48)
+
+/**
+ * Set for locks that failed and where the server has been notified.
+ *
+ * Protected by lock and resource locks. */
+#define LDLM_FL_FAIL_NOTIFIED 0x0002000000000000ULL // bit 49
+#define ldlm_is_fail_notified(_l) LDLM_TEST_FLAG(( _l), 1ULL << 49)
+#define ldlm_set_fail_notified(_l) LDLM_SET_FLAG(( _l), 1ULL << 49)
+#define ldlm_clear_fail_notified(_l) LDLM_CLEAR_FLAG((_l), 1ULL << 49)
+
+/**
+ * Set for locks that were removed from class hash table and will
+ * be destroyed when last reference to them is released. Set by
+ * ldlm_lock_destroy_internal().
+ *
+ * Protected by lock and resource locks. */
+#define LDLM_FL_DESTROYED 0x0004000000000000ULL // bit 50
+#define ldlm_is_destroyed(_l) LDLM_TEST_FLAG(( _l), 1ULL << 50)
+#define ldlm_set_destroyed(_l) LDLM_SET_FLAG(( _l), 1ULL << 50)
+#define ldlm_clear_destroyed(_l) LDLM_CLEAR_FLAG((_l), 1ULL << 50)
+
+/** flag whether this is a server namespace lock */
+#define LDLM_FL_SERVER_LOCK 0x0008000000000000ULL // bit 51
+#define ldlm_is_server_lock(_l) LDLM_TEST_FLAG(( _l), 1ULL << 51)
+#define ldlm_set_server_lock(_l) LDLM_SET_FLAG(( _l), 1ULL << 51)
+#define ldlm_clear_server_lock(_l) LDLM_CLEAR_FLAG((_l), 1ULL << 51)
+
+/**
+ * It's set in lock_res_and_lock() and unset in unlock_res_and_lock().
+ *
+ * NB: compared with check_res_locked(), checking this bit is cheaper.
+ * Also, spin_is_locked() is deprecated for kernel code; one reason is
+ * because it works only for SMP so user needs to add extra macros like
+ * LASSERT_SPIN_LOCKED for uniprocessor kernels. */
+#define LDLM_FL_RES_LOCKED 0x0010000000000000ULL // bit 52
+#define ldlm_is_res_locked(_l) LDLM_TEST_FLAG(( _l), 1ULL << 52)
+#define ldlm_set_res_locked(_l) LDLM_SET_FLAG(( _l), 1ULL << 52)
+#define ldlm_clear_res_locked(_l) LDLM_CLEAR_FLAG((_l), 1ULL << 52)
+
+/**
+ * It's set once we call ldlm_add_waiting_lock_res_locked() to start the
+ * lock-timeout timer and it will never be reset.
+ *
+ * Protected by lock and resource locks. */
+#define LDLM_FL_WAITED 0x0020000000000000ULL // bit 53
+#define ldlm_is_waited(_l) LDLM_TEST_FLAG(( _l), 1ULL << 53)
+#define ldlm_set_waited(_l) LDLM_SET_FLAG(( _l), 1ULL << 53)
+#define ldlm_clear_waited(_l) LDLM_CLEAR_FLAG((_l), 1ULL << 53)
+
+/** Flag whether this is a server namespace lock. */
+#define LDLM_FL_NS_SRV 0x0040000000000000ULL // bit 54
+#define ldlm_is_ns_srv(_l) LDLM_TEST_FLAG(( _l), 1ULL << 54)
+#define ldlm_set_ns_srv(_l) LDLM_SET_FLAG(( _l), 1ULL << 54)
+#define ldlm_clear_ns_srv(_l) LDLM_CLEAR_FLAG((_l), 1ULL << 54)
+
+/** Flag whether this lock can be reused. Used by exclusive open. */
+#define LDLM_FL_EXCL 0x0080000000000000ULL /* bit 55 */
+#define ldlm_is_excl(_l) LDLM_TEST_FLAG((_l), 1ULL << 55)
+#define ldlm_set_excl(_l) LDLM_SET_FLAG((_l), 1ULL << 55)
+#define ldlm_clear_excl(_l) LDLM_CLEAR_FLAG((_l), 1ULL << 55)
+
+/** test for ldlm_lock flag bit set */
+#define LDLM_TEST_FLAG(_l, _b) (((_l)->l_flags & (_b)) != 0)
+
+/** set a ldlm_lock flag bit */
+#define LDLM_SET_FLAG(_l, _b) (((_l)->l_flags |= (_b))
+
+/** clear a ldlm_lock flag bit */
+#define LDLM_CLEAR_FLAG(_l, _b) (((_l)->l_flags &= ~(_b))
+
+/** Mask of flags inherited from parent lock when doing intents. */
+#define LDLM_INHERIT_FLAGS LDLM_FL_INHERIT_MASK
+
+/** Mask of Flags sent in AST lock_flags to map into the receiving lock. */
+#define LDLM_AST_FLAGS LDLM_FL_AST_MASK
+
+/** @} subgroup */
+/** @} group */
+#ifdef WIRESHARK_COMPILE
+static int hf_lustre_ldlm_fl_lock_changed = -1;
+static int hf_lustre_ldlm_fl_block_granted = -1;
+static int hf_lustre_ldlm_fl_block_conv = -1;
+static int hf_lustre_ldlm_fl_block_wait = -1;
+static int hf_lustre_ldlm_fl_ast_sent = -1;
+static int hf_lustre_ldlm_fl_replay = -1;
+static int hf_lustre_ldlm_fl_intent_only = -1;
+static int hf_lustre_ldlm_fl_has_intent = -1;
+static int hf_lustre_ldlm_fl_flock_deadlock = -1;
+static int hf_lustre_ldlm_fl_discard_data = -1;
+static int hf_lustre_ldlm_fl_no_timeout = -1;
+static int hf_lustre_ldlm_fl_block_nowait = -1;
+static int hf_lustre_ldlm_fl_test_lock = -1;
+static int hf_lustre_ldlm_fl_cancel_on_block = -1;
+static int hf_lustre_ldlm_fl_deny_on_contention = -1;
+static int hf_lustre_ldlm_fl_ast_discard_data = -1;
+static int hf_lustre_ldlm_fl_fail_loc = -1;
+static int hf_lustre_ldlm_fl_skipped = -1;
+static int hf_lustre_ldlm_fl_cbpending = -1;
+static int hf_lustre_ldlm_fl_wait_noreproc = -1;
+static int hf_lustre_ldlm_fl_cancel = -1;
+static int hf_lustre_ldlm_fl_local_only = -1;
+static int hf_lustre_ldlm_fl_failed = -1;
+static int hf_lustre_ldlm_fl_canceling = -1;
+static int hf_lustre_ldlm_fl_local = -1;
+static int hf_lustre_ldlm_fl_lvb_ready = -1;
+static int hf_lustre_ldlm_fl_kms_ignore = -1;
+static int hf_lustre_ldlm_fl_cp_reqd = -1;
+static int hf_lustre_ldlm_fl_cleaned = -1;
+static int hf_lustre_ldlm_fl_atomic_cb = -1;
+static int hf_lustre_ldlm_fl_bl_ast = -1;
+static int hf_lustre_ldlm_fl_bl_done = -1;
+static int hf_lustre_ldlm_fl_no_lru = -1;
+static int hf_lustre_ldlm_fl_fail_notified = -1;
+static int hf_lustre_ldlm_fl_destroyed = -1;
+static int hf_lustre_ldlm_fl_server_lock = -1;
+static int hf_lustre_ldlm_fl_res_locked = -1;
+static int hf_lustre_ldlm_fl_waited = -1;
+static int hf_lustre_ldlm_fl_ns_srv = -1;
+static int hf_lustre_ldlm_fl_excl = -1;
+
+const value_string lustre_ldlm_flags_vals[] = {
+ {LDLM_FL_LOCK_CHANGED, "LDLM_FL_LOCK_CHANGED"},
+ {LDLM_FL_BLOCK_GRANTED, "LDLM_FL_BLOCK_GRANTED"},
+ {LDLM_FL_BLOCK_CONV, "LDLM_FL_BLOCK_CONV"},
+ {LDLM_FL_BLOCK_WAIT, "LDLM_FL_BLOCK_WAIT"},
+ {LDLM_FL_AST_SENT, "LDLM_FL_AST_SENT"},
+ {LDLM_FL_REPLAY, "LDLM_FL_REPLAY"},
+ {LDLM_FL_INTENT_ONLY, "LDLM_FL_INTENT_ONLY"},
+ {LDLM_FL_HAS_INTENT, "LDLM_FL_HAS_INTENT"},
+ {LDLM_FL_FLOCK_DEADLOCK, "LDLM_FL_FLOCK_DEADLOCK"},
+ {LDLM_FL_DISCARD_DATA, "LDLM_FL_DISCARD_DATA"},
+ {LDLM_FL_NO_TIMEOUT, "LDLM_FL_NO_TIMEOUT"},
+ {LDLM_FL_BLOCK_NOWAIT, "LDLM_FL_BLOCK_NOWAIT"},
+ {LDLM_FL_TEST_LOCK, "LDLM_FL_TEST_LOCK"},
+ {LDLM_FL_CANCEL_ON_BLOCK, "LDLM_FL_CANCEL_ON_BLOCK"},
+ {LDLM_FL_DENY_ON_CONTENTION, "LDLM_FL_DENY_ON_CONTENTION"},
+ {LDLM_FL_AST_DISCARD_DATA, "LDLM_FL_AST_DISCARD_DATA"},
+ {LDLM_FL_FAIL_LOC, "LDLM_FL_FAIL_LOC"},
+ {LDLM_FL_SKIPPED, "LDLM_FL_SKIPPED"},
+ {LDLM_FL_CBPENDING, "LDLM_FL_CBPENDING"},
+ {LDLM_FL_WAIT_NOREPROC, "LDLM_FL_WAIT_NOREPROC"},
+ {LDLM_FL_CANCEL, "LDLM_FL_CANCEL"},
+ {LDLM_FL_LOCAL_ONLY, "LDLM_FL_LOCAL_ONLY"},
+ {LDLM_FL_FAILED, "LDLM_FL_FAILED"},
+ {LDLM_FL_CANCELING, "LDLM_FL_CANCELING"},
+ {LDLM_FL_LOCAL, "LDLM_FL_LOCAL"},
+ {LDLM_FL_LVB_READY, "LDLM_FL_LVB_READY"},
+ {LDLM_FL_KMS_IGNORE, "LDLM_FL_KMS_IGNORE"},
+ {LDLM_FL_CP_REQD, "LDLM_FL_CP_REQD"},
+ {LDLM_FL_CLEANED, "LDLM_FL_CLEANED"},
+ {LDLM_FL_ATOMIC_CB, "LDLM_FL_ATOMIC_CB"},
+ {LDLM_FL_BL_AST, "LDLM_FL_BL_AST"},
+ {LDLM_FL_BL_DONE, "LDLM_FL_BL_DONE"},
+ {LDLM_FL_NO_LRU, "LDLM_FL_NO_LRU"},
+ {LDLM_FL_FAIL_NOTIFIED, "LDLM_FL_FAIL_NOTIFIED"},
+ {LDLM_FL_DESTROYED, "LDLM_FL_DESTROYED"},
+ {LDLM_FL_SERVER_LOCK, "LDLM_FL_SERVER_LOCK"},
+ {LDLM_FL_RES_LOCKED, "LDLM_FL_RES_LOCKED"},
+ {LDLM_FL_WAITED, "LDLM_FL_WAITED"},
+ {LDLM_FL_NS_SRV, "LDLM_FL_NS_SRV"},
+ {LDLM_FL_EXCL, "LDLM_FL_EXCL"},
+ { 0, NULL }
+};
+#endif /* WIRESHARK_COMPILE */
+#endif /* LDLM_ALL_FLAGS_MASK */
diff --git a/drivers/staging/lustre/lustre/include/lustre_eacl.h b/drivers/staging/lustre/lustre/include/lustre_eacl.h
new file mode 100644
index 000000000..0f8f76c43
--- /dev/null
+++ b/drivers/staging/lustre/lustre/include/lustre_eacl.h
@@ -0,0 +1,95 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ */
+/*
+ * Copyright (c) 2011, 2012, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ *
+ * lustre/lustre/include/lustre_idmap.h
+ *
+ * MDS data structures.
+ * See also lustre_idl.h for wire formats of requests.
+ */
+
+#ifndef _LUSTRE_EACL_H
+#define _LUSTRE_EACL_H
+
+/** \defgroup eacl eacl
+ *
+ * @{
+ */
+
+#ifdef CONFIG_FS_POSIX_ACL
+
+#include <linux/posix_acl_xattr.h>
+
+typedef struct {
+ __u16 e_tag;
+ __u16 e_perm;
+ __u32 e_id;
+ __u32 e_stat;
+} ext_acl_xattr_entry;
+
+typedef struct {
+ __u32 a_count;
+ ext_acl_xattr_entry a_entries[0];
+} ext_acl_xattr_header;
+
+#define CFS_ACL_XATTR_SIZE(count, prefix) \
+ (sizeof(prefix ## _header) + (count) * sizeof(prefix ## _entry))
+
+#define CFS_ACL_XATTR_COUNT(size, prefix) \
+ (((size) - sizeof(prefix ## _header)) / sizeof(prefix ## _entry))
+
+
+extern ext_acl_xattr_header *
+lustre_posix_acl_xattr_2ext(posix_acl_xattr_header *header, int size);
+extern int
+lustre_posix_acl_xattr_filter(posix_acl_xattr_header *header, size_t size,
+ posix_acl_xattr_header **out);
+extern void
+lustre_posix_acl_xattr_free(posix_acl_xattr_header *header, int size);
+extern void
+lustre_ext_acl_xattr_free(ext_acl_xattr_header *header);
+extern int
+lustre_acl_xattr_merge2posix(posix_acl_xattr_header *posix_header, int size,
+ ext_acl_xattr_header *ext_header,
+ posix_acl_xattr_header **out);
+extern ext_acl_xattr_header *
+lustre_acl_xattr_merge2ext(posix_acl_xattr_header *posix_header, int size,
+ ext_acl_xattr_header *ext_header);
+
+#endif /* CONFIG_FS_POSIX_ACL */
+
+/** @} eacl */
+
+#endif
diff --git a/drivers/staging/lustre/lustre/include/lustre_export.h b/drivers/staging/lustre/lustre/include/lustre_export.h
new file mode 100644
index 000000000..9c06a49f1
--- /dev/null
+++ b/drivers/staging/lustre/lustre/include/lustre_export.h
@@ -0,0 +1,406 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2002, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2011, 2012, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ */
+/** \defgroup obd_export PortalRPC export definitions
+ *
+ * @{
+ */
+
+#ifndef __EXPORT_H
+#define __EXPORT_H
+
+/** \defgroup export export
+ *
+ * @{
+ */
+
+#include "lprocfs_status.h"
+#include "lustre/lustre_idl.h"
+#include "lustre_dlm.h"
+
+struct mds_client_data;
+struct mdt_client_data;
+struct mds_idmap_table;
+struct mdt_idmap_table;
+
+/**
+ * Target-specific export data
+ */
+struct tg_export_data {
+ /** Protects led_lcd below */
+ struct mutex ted_lcd_lock;
+ /** Per-client data for each export */
+ struct lsd_client_data *ted_lcd;
+ /** Offset of record in last_rcvd file */
+ loff_t ted_lr_off;
+ /** Client index in last_rcvd file */
+ int ted_lr_idx;
+};
+
+/**
+ * MDT-specific export data
+ */
+struct mdt_export_data {
+ struct tg_export_data med_ted;
+ /** List of all files opened by client on this MDT */
+ struct list_head med_open_head;
+ spinlock_t med_open_lock; /* med_open_head, mfd_list */
+ /** Bitmask of all ibit locks this MDT understands */
+ __u64 med_ibits_known;
+ struct mutex med_idmap_mutex;
+ struct lustre_idmap_table *med_idmap;
+};
+
+struct ec_export_data { /* echo client */
+ struct list_head eced_locks;
+};
+
+/* In-memory access to client data from OST struct */
+/** Filter (oss-side) specific import data */
+struct filter_export_data {
+ struct tg_export_data fed_ted;
+ spinlock_t fed_lock; /**< protects fed_mod_list */
+ long fed_dirty; /* in bytes */
+ long fed_grant; /* in bytes */
+ struct list_head fed_mod_list; /* files being modified */
+ int fed_mod_count;/* items in fed_writing list */
+ long fed_pending; /* bytes just being written */
+ __u32 fed_group;
+ __u8 fed_pagesize; /* log2 of client page size */
+};
+
+struct mgs_export_data {
+ struct list_head med_clients; /* mgc fs client via this exp */
+ spinlock_t med_lock; /* protect med_clients */
+};
+
+/**
+ * per-NID statistics structure.
+ * It tracks access patterns to this export on a per-client-NID basis
+ */
+struct nid_stat {
+ lnet_nid_t nid;
+ struct hlist_node nid_hash;
+ struct list_head nid_list;
+ struct obd_device *nid_obd;
+ struct proc_dir_entry *nid_proc;
+ struct lprocfs_stats *nid_stats;
+ struct lprocfs_stats *nid_ldlm_stats;
+ atomic_t nid_exp_ref_count; /* for obd_nid_stats_hash
+ exp_nid_stats */
+};
+
+#define nidstat_getref(nidstat) \
+do { \
+ atomic_inc(&(nidstat)->nid_exp_ref_count); \
+} while (0)
+
+#define nidstat_putref(nidstat) \
+do { \
+ atomic_dec(&(nidstat)->nid_exp_ref_count); \
+ LASSERTF(atomic_read(&(nidstat)->nid_exp_ref_count) >= 0, \
+ "stat %p nid_exp_ref_count < 0\n", nidstat); \
+} while (0)
+
+enum obd_option {
+ OBD_OPT_FORCE = 0x0001,
+ OBD_OPT_FAILOVER = 0x0002,
+ OBD_OPT_ABORT_RECOV = 0x0004,
+};
+
+/**
+ * Export structure. Represents target-side of connection in portals.
+ * Also used in Lustre to connect between layers on the same node when
+ * there is no network-connection in-between.
+ * For every connected client there is an export structure on the server
+ * attached to the same obd device.
+ */
+struct obd_export {
+ /**
+ * Export handle, it's id is provided to client on connect
+ * Subsequent client RPCs contain this handle id to identify
+ * what export they are talking to.
+ */
+ struct portals_handle exp_handle;
+ atomic_t exp_refcount;
+ /**
+ * Set of counters below is to track where export references are
+ * kept. The exp_rpc_count is used for reconnect handling also,
+ * the cb_count and locks_count are for debug purposes only for now.
+ * The sum of them should be less than exp_refcount by 3
+ */
+ atomic_t exp_rpc_count; /* RPC references */
+ atomic_t exp_cb_count; /* Commit callback references */
+ /** Number of queued replay requests to be processes */
+ atomic_t exp_replay_count;
+ atomic_t exp_locks_count; /** Lock references */
+#if LUSTRE_TRACKS_LOCK_EXP_REFS
+ struct list_head exp_locks_list;
+ spinlock_t exp_locks_list_guard;
+#endif
+ /** UUID of client connected to this export */
+ struct obd_uuid exp_client_uuid;
+ /** To link all exports on an obd device */
+ struct list_head exp_obd_chain;
+ struct hlist_node exp_uuid_hash; /** uuid-export hash*/
+ struct hlist_node exp_nid_hash; /** nid-export hash */
+ /**
+ * All exports eligible for ping evictor are linked into a list
+ * through this field in "most time since last request on this export"
+ * order
+ * protected by obd_dev_lock
+ */
+ struct list_head exp_obd_chain_timed;
+ /** Obd device of this export */
+ struct obd_device *exp_obd;
+ /**
+ * "reverse" import to send requests (e.g. from ldlm) back to client
+ * exp_lock protect its change
+ */
+ struct obd_import *exp_imp_reverse;
+ struct nid_stat *exp_nid_stats;
+ struct lprocfs_stats *exp_md_stats;
+ /** Active connection */
+ struct ptlrpc_connection *exp_connection;
+ /** Connection count value from last successful reconnect rpc */
+ __u32 exp_conn_cnt;
+ /** Hash list of all ldlm locks granted on this export */
+ struct cfs_hash *exp_lock_hash;
+ /**
+ * Hash list for Posix lock deadlock detection, added with
+ * ldlm_lock::l_exp_flock_hash.
+ */
+ struct cfs_hash *exp_flock_hash;
+ struct list_head exp_outstanding_replies;
+ struct list_head exp_uncommitted_replies;
+ spinlock_t exp_uncommitted_replies_lock;
+ /** Last committed transno for this export */
+ __u64 exp_last_committed;
+ /** When was last request received */
+ unsigned long exp_last_request_time;
+ /** On replay all requests waiting for replay are linked here */
+ struct list_head exp_req_replay_queue;
+ /**
+ * protects exp_flags, exp_outstanding_replies and the change
+ * of exp_imp_reverse
+ */
+ spinlock_t exp_lock;
+ /** Compatibility flags for this export are embedded into
+ * exp_connect_data */
+ struct obd_connect_data exp_connect_data;
+ enum obd_option exp_flags;
+ unsigned long exp_failed:1,
+ exp_in_recovery:1,
+ exp_disconnected:1,
+ exp_connecting:1,
+ /** VBR: export missed recovery */
+ exp_delayed:1,
+ /** VBR: failed version checking */
+ exp_vbr_failed:1,
+ exp_req_replay_needed:1,
+ exp_lock_replay_needed:1,
+ exp_need_sync:1,
+ exp_flvr_changed:1,
+ exp_flvr_adapt:1,
+ exp_libclient:1, /* liblustre client? */
+ /* client timed out and tried to reconnect,
+ * but couldn't because of active rpcs */
+ exp_abort_active_req:1,
+ /* if to swap nidtbl entries for 2.2 clients.
+ * Only used by the MGS to fix LU-1644. */
+ exp_need_mne_swab:1;
+ /* also protected by exp_lock */
+ enum lustre_sec_part exp_sp_peer;
+ struct sptlrpc_flavor exp_flvr; /* current */
+ struct sptlrpc_flavor exp_flvr_old[2]; /* about-to-expire */
+ unsigned long exp_flvr_expire[2]; /* seconds */
+
+ /** protects exp_hp_rpcs */
+ spinlock_t exp_rpc_lock;
+ struct list_head exp_hp_rpcs; /* (potential) HP RPCs */
+
+ /** blocking dlm lock list, protected by exp_bl_list_lock */
+ struct list_head exp_bl_list;
+ spinlock_t exp_bl_list_lock;
+
+ /** Target specific data */
+ union {
+ struct tg_export_data eu_target_data;
+ struct mdt_export_data eu_mdt_data;
+ struct filter_export_data eu_filter_data;
+ struct ec_export_data eu_ec_data;
+ struct mgs_export_data eu_mgs_data;
+ } u;
+};
+
+#define exp_target_data u.eu_target_data
+#define exp_mdt_data u.eu_mdt_data
+#define exp_filter_data u.eu_filter_data
+#define exp_ec_data u.eu_ec_data
+
+static inline __u64 *exp_connect_flags_ptr(struct obd_export *exp)
+{
+ return &exp->exp_connect_data.ocd_connect_flags;
+}
+
+static inline __u64 exp_connect_flags(struct obd_export *exp)
+{
+ return *exp_connect_flags_ptr(exp);
+}
+
+static inline int exp_max_brw_size(struct obd_export *exp)
+{
+ LASSERT(exp != NULL);
+ if (exp_connect_flags(exp) & OBD_CONNECT_BRW_SIZE)
+ return exp->exp_connect_data.ocd_brw_size;
+
+ return ONE_MB_BRW_SIZE;
+}
+
+static inline int exp_connect_multibulk(struct obd_export *exp)
+{
+ return exp_max_brw_size(exp) > ONE_MB_BRW_SIZE;
+}
+
+static inline int exp_expired(struct obd_export *exp, long age)
+{
+ LASSERT(exp->exp_delayed);
+ return time_before(cfs_time_add(exp->exp_last_request_time, age),
+ get_seconds());
+}
+
+static inline int exp_connect_cancelset(struct obd_export *exp)
+{
+ LASSERT(exp != NULL);
+ return !!(exp_connect_flags(exp) & OBD_CONNECT_CANCELSET);
+}
+
+static inline int exp_connect_lru_resize(struct obd_export *exp)
+{
+ LASSERT(exp != NULL);
+ return !!(exp_connect_flags(exp) & OBD_CONNECT_LRU_RESIZE);
+}
+
+static inline int exp_connect_rmtclient(struct obd_export *exp)
+{
+ LASSERT(exp != NULL);
+ return !!(exp_connect_flags(exp) & OBD_CONNECT_RMT_CLIENT);
+}
+
+static inline int client_is_remote(struct obd_export *exp)
+{
+ struct obd_import *imp = class_exp2cliimp(exp);
+
+ return !!(imp->imp_connect_data.ocd_connect_flags &
+ OBD_CONNECT_RMT_CLIENT);
+}
+
+static inline int exp_connect_vbr(struct obd_export *exp)
+{
+ LASSERT(exp != NULL);
+ LASSERT(exp->exp_connection);
+ return !!(exp_connect_flags(exp) & OBD_CONNECT_VBR);
+}
+
+static inline int exp_connect_som(struct obd_export *exp)
+{
+ LASSERT(exp != NULL);
+ return !!(exp_connect_flags(exp) & OBD_CONNECT_SOM);
+}
+
+static inline int exp_connect_umask(struct obd_export *exp)
+{
+ return !!(exp_connect_flags(exp) & OBD_CONNECT_UMASK);
+}
+
+static inline int imp_connect_lru_resize(struct obd_import *imp)
+{
+ struct obd_connect_data *ocd;
+
+ LASSERT(imp != NULL);
+ ocd = &imp->imp_connect_data;
+ return !!(ocd->ocd_connect_flags & OBD_CONNECT_LRU_RESIZE);
+}
+
+static inline int exp_connect_layout(struct obd_export *exp)
+{
+ return !!(exp_connect_flags(exp) & OBD_CONNECT_LAYOUTLOCK);
+}
+
+static inline bool exp_connect_lvb_type(struct obd_export *exp)
+{
+ LASSERT(exp != NULL);
+ if (exp_connect_flags(exp) & OBD_CONNECT_LVB_TYPE)
+ return true;
+ else
+ return false;
+}
+
+static inline bool imp_connect_lvb_type(struct obd_import *imp)
+{
+ struct obd_connect_data *ocd;
+
+ LASSERT(imp != NULL);
+ ocd = &imp->imp_connect_data;
+ if (ocd->ocd_connect_flags & OBD_CONNECT_LVB_TYPE)
+ return true;
+ else
+ return false;
+}
+
+static inline __u64 exp_connect_ibits(struct obd_export *exp)
+{
+ struct obd_connect_data *ocd;
+
+ ocd = &exp->exp_connect_data;
+ return ocd->ocd_ibits_known;
+}
+
+static inline bool imp_connect_disp_stripe(struct obd_import *imp)
+{
+ struct obd_connect_data *ocd;
+
+ LASSERT(imp != NULL);
+ ocd = &imp->imp_connect_data;
+ return ocd->ocd_connect_flags & OBD_CONNECT_DISP_STRIPE;
+}
+
+extern struct obd_export *class_conn2export(struct lustre_handle *conn);
+extern struct obd_device *class_conn2obd(struct lustre_handle *conn);
+
+/** @} export */
+
+#endif /* __EXPORT_H */
+/** @} obd_export */
diff --git a/drivers/staging/lustre/lustre/include/lustre_fid.h b/drivers/staging/lustre/lustre/include/lustre_fid.h
new file mode 100644
index 000000000..0a0929fd9
--- /dev/null
+++ b/drivers/staging/lustre/lustre/include/lustre_fid.h
@@ -0,0 +1,767 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2011, 2012, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ *
+ * lustre/include/lustre_fid.h
+ *
+ * Author: Yury Umanets <umka@clusterfs.com>
+ */
+
+#ifndef __LUSTRE_FID_H
+#define __LUSTRE_FID_H
+
+/** \defgroup fid fid
+ *
+ * @{
+ *
+ * http://wiki.lustre.org/index.php/Architecture_-_Interoperability_fids_zfs
+ * describes the FID namespace and interoperability requirements for FIDs.
+ * The important parts of that document are included here for reference.
+ *
+ * FID
+ * File IDentifier generated by client from range allocated by the SEQuence
+ * service and stored in struct lu_fid. The FID is composed of three parts:
+ * SEQuence, ObjectID, and VERsion. The SEQ component is a filesystem
+ * unique 64-bit integer, and only one client is ever assigned any SEQ value.
+ * The first 0x400 FID_SEQ_NORMAL [2^33, 2^33 + 0x400] values are reserved
+ * for system use. The OID component is a 32-bit value generated by the
+ * client on a per-SEQ basis to allow creating many unique FIDs without
+ * communication with the server. The VER component is a 32-bit value that
+ * distinguishes between different FID instantiations, such as snapshots or
+ * separate subtrees within the filesystem. FIDs with the same VER field
+ * are considered part of the same namespace.
+ *
+ * OLD filesystems are those upgraded from Lustre 1.x that predate FIDs, and
+ * MDTs use 32-bit ldiskfs internal inode/generation numbers (IGIFs), while
+ * OSTs use 64-bit Lustre object IDs and generation numbers.
+ *
+ * NEW filesystems are those formatted since the introduction of FIDs.
+ *
+ * IGIF
+ * Inode and Generation In FID, a surrogate FID used to globally identify
+ * an existing object on OLD formatted MDT file system. This would only be
+ * used on MDT0 in a DNE filesystem, because there cannot be more than one
+ * MDT in an OLD formatted filesystem. Belongs to sequence in [12, 2^32 - 1]
+ * range, where inode number is stored in SEQ, and inode generation is in OID.
+ * NOTE: This assumes no more than 2^32-1 inodes exist in the MDT filesystem,
+ * which is the maximum possible for an ldiskfs backend. It also assumes
+ * that the reserved ext3/ext4/ldiskfs inode numbers [0-11] are never visible
+ * to clients, which has always been true.
+ *
+ * IDIF
+ * object ID In FID, a surrogate FID used to globally identify an existing
+ * OST object on OLD formatted OST file system. Belongs to a sequence in
+ * [2^32, 2^33 - 1]. Sequence number is calculated as:
+ *
+ * 1 << 32 | (ost_index << 16) | ((objid >> 32) & 0xffff)
+ *
+ * that is, SEQ consists of 16-bit OST index, and higher 16 bits of object
+ * ID. The generation of unique SEQ values per OST allows the IDIF FIDs to
+ * be identified in the FLD correctly. The OID field is calculated as:
+ *
+ * objid & 0xffffffff
+ *
+ * that is, it consists of lower 32 bits of object ID. For objects within
+ * the IDIF range, object ID extraction will be:
+ *
+ * o_id = (fid->f_seq & 0x7fff) << 16 | fid->f_oid;
+ * o_seq = 0; // formerly group number
+ *
+ * NOTE: This assumes that no more than 2^48-1 objects have ever been created
+ * on any OST, and that no more than 65535 OSTs are in use. Both are very
+ * reasonable assumptions, i.e. an IDIF can uniquely map all objects assuming
+ * a maximum creation rate of 1M objects per second for a maximum of 9 years,
+ * or combinations thereof.
+ *
+ * OST_MDT0
+ * Surrogate FID used to identify an existing object on OLD formatted OST
+ * filesystem. Belongs to the reserved SEQuence 0, and is used prior to
+ * the introduction of FID-on-OST, at which point IDIF will be used to
+ * identify objects as residing on a specific OST.
+ *
+ * LLOG
+ * For Lustre Log objects the object sequence 1 is used. This is compatible
+ * with both OLD and NEW namespaces, as this SEQ number is in the
+ * ext3/ldiskfs reserved inode range and does not conflict with IGIF
+ * sequence numbers.
+ *
+ * ECHO
+ * For testing OST IO performance the object sequence 2 is used. This is
+ * compatible with both OLD and NEW namespaces, as this SEQ number is in
+ * the ext3/ldiskfs reserved inode range and does not conflict with IGIF
+ * sequence numbers.
+ *
+ * OST_MDT1 .. OST_MAX
+ * For testing with multiple MDTs the object sequence 3 through 9 is used,
+ * allowing direct mapping of MDTs 1 through 7 respectively, for a total
+ * of 8 MDTs including OST_MDT0. This matches the legacy CMD project "group"
+ * mappings. However, this SEQ range is only for testing prior to any
+ * production DNE release, as the objects in this range conflict across all
+ * OSTs, as the OST index is not part of the FID. For production DNE usage,
+ * OST objects created by MDT1+ will use FID_SEQ_NORMAL FIDs.
+ *
+ * DLM OST objid to IDIF mapping
+ * For compatibility with existing OLD OST network protocol structures, the
+ * FID must map onto the o_id and o_seq in a manner that ensures existing
+ * objects are identified consistently for IO, as well as onto the LDLM
+ * namespace to ensure IDIFs there is only a single resource name for any
+ * object in the DLM. The OLD OST object DLM resource mapping is:
+ *
+ * resource[] = {o_id, o_seq, 0, 0}; // o_seq == 0 for production releases
+ *
+ * The NEW OST object DLM resource mapping is the same for both MDT and OST:
+ *
+ * resource[] = {SEQ, OID, VER, HASH};
+ *
+ * NOTE: for mapping IDIF values to DLM resource names the o_id may be
+ * larger than the 2^33 reserved sequence numbers for IDIF, so it is possible
+ * for the o_id numbers to overlap FID SEQ numbers in the resource. However,
+ * in all production releases the OLD o_seq field is always zero, and all
+ * valid FID OID values are non-zero, so the lock resources will not collide.
+ * Even so, the MDT and OST resources are also in different LDLM namespaces.
+ */
+
+#include "../../include/linux/libcfs/libcfs.h"
+#include "lustre/lustre_idl.h"
+
+struct lu_env;
+struct lu_site;
+struct lu_context;
+struct obd_device;
+struct obd_export;
+
+/* Whole sequences space range and zero range definitions */
+extern const struct lu_seq_range LUSTRE_SEQ_SPACE_RANGE;
+extern const struct lu_seq_range LUSTRE_SEQ_ZERO_RANGE;
+extern const struct lu_fid LUSTRE_BFL_FID;
+extern const struct lu_fid LU_OBF_FID;
+extern const struct lu_fid LU_DOT_LUSTRE_FID;
+
+enum {
+ /*
+ * This is how may metadata FIDs may be allocated in one sequence(128k)
+ */
+ LUSTRE_METADATA_SEQ_MAX_WIDTH = 0x0000000000020000ULL,
+
+ /*
+ * This is how many data FIDs could be allocated in one sequence(4B - 1)
+ */
+ LUSTRE_DATA_SEQ_MAX_WIDTH = 0x00000000FFFFFFFFULL,
+
+ /*
+ * How many sequences to allocate to a client at once.
+ */
+ LUSTRE_SEQ_META_WIDTH = 0x0000000000000001ULL,
+
+ /*
+ * seq allocation pool size.
+ */
+ LUSTRE_SEQ_BATCH_WIDTH = LUSTRE_SEQ_META_WIDTH * 1000,
+
+ /*
+ * This is how many sequences may be in one super-sequence allocated to
+ * MDTs.
+ */
+ LUSTRE_SEQ_SUPER_WIDTH = ((1ULL << 30ULL) * LUSTRE_SEQ_META_WIDTH)
+};
+
+enum {
+ /** 2^6 FIDs for OI containers */
+ OSD_OI_FID_OID_BITS = 6,
+ /** reserve enough FIDs in case we want more in the future */
+ OSD_OI_FID_OID_BITS_MAX = 10,
+};
+
+/** special OID for local objects */
+enum local_oid {
+ /** \see fld_mod_init */
+ FLD_INDEX_OID = 3UL,
+ /** \see fid_mod_init */
+ FID_SEQ_CTL_OID = 4UL,
+ FID_SEQ_SRV_OID = 5UL,
+ /** \see mdd_mod_init */
+ MDD_ROOT_INDEX_OID = 6UL, /* deprecated in 2.4 */
+ MDD_ORPHAN_OID = 7UL, /* deprecated in 2.4 */
+ MDD_LOV_OBJ_OID = 8UL,
+ MDD_CAPA_KEYS_OID = 9UL,
+ /** \see mdt_mod_init */
+ LAST_RECV_OID = 11UL,
+ OSD_FS_ROOT_OID = 13UL,
+ ACCT_USER_OID = 15UL,
+ ACCT_GROUP_OID = 16UL,
+ LFSCK_BOOKMARK_OID = 17UL,
+ OTABLE_IT_OID = 18UL,
+ /* These two definitions are obsolete
+ * OFD_GROUP0_LAST_OID = 20UL,
+ * OFD_GROUP4K_LAST_OID = 20UL+4096,
+ */
+ OFD_LAST_GROUP_OID = 4117UL,
+ LLOG_CATALOGS_OID = 4118UL,
+ MGS_CONFIGS_OID = 4119UL,
+ OFD_HEALTH_CHECK_OID = 4120UL,
+ MDD_LOV_OBJ_OSEQ = 4121UL,
+ LFSCK_NAMESPACE_OID = 4122UL,
+ REMOTE_PARENT_DIR_OID = 4123UL,
+};
+
+static inline void lu_local_obj_fid(struct lu_fid *fid, __u32 oid)
+{
+ fid->f_seq = FID_SEQ_LOCAL_FILE;
+ fid->f_oid = oid;
+ fid->f_ver = 0;
+}
+
+static inline void lu_local_name_obj_fid(struct lu_fid *fid, __u32 oid)
+{
+ fid->f_seq = FID_SEQ_LOCAL_NAME;
+ fid->f_oid = oid;
+ fid->f_ver = 0;
+}
+
+/* For new FS (>= 2.4), the root FID will be changed to
+ * [FID_SEQ_ROOT:1:0], for existing FS, (upgraded to 2.4),
+ * the root FID will still be IGIF */
+static inline int fid_is_root(const struct lu_fid *fid)
+{
+ return unlikely((fid_seq(fid) == FID_SEQ_ROOT &&
+ fid_oid(fid) == 1));
+}
+
+static inline int fid_is_dot_lustre(const struct lu_fid *fid)
+{
+ return unlikely(fid_seq(fid) == FID_SEQ_DOT_LUSTRE &&
+ fid_oid(fid) == FID_OID_DOT_LUSTRE);
+}
+
+static inline int fid_is_obf(const struct lu_fid *fid)
+{
+ return unlikely(fid_seq(fid) == FID_SEQ_DOT_LUSTRE &&
+ fid_oid(fid) == FID_OID_DOT_LUSTRE_OBF);
+}
+
+static inline int fid_is_otable_it(const struct lu_fid *fid)
+{
+ return unlikely(fid_seq(fid) == FID_SEQ_LOCAL_FILE &&
+ fid_oid(fid) == OTABLE_IT_OID);
+}
+
+static inline int fid_is_acct(const struct lu_fid *fid)
+{
+ return fid_seq(fid) == FID_SEQ_LOCAL_FILE &&
+ (fid_oid(fid) == ACCT_USER_OID ||
+ fid_oid(fid) == ACCT_GROUP_OID);
+}
+
+static inline int fid_is_quota(const struct lu_fid *fid)
+{
+ return fid_seq(fid) == FID_SEQ_QUOTA ||
+ fid_seq(fid) == FID_SEQ_QUOTA_GLB;
+}
+
+static inline int fid_is_namespace_visible(const struct lu_fid *fid)
+{
+ const __u64 seq = fid_seq(fid);
+
+ /* Here, we cannot distinguish whether the normal FID is for OST
+ * object or not. It is caller's duty to check more if needed. */
+ return (!fid_is_last_id(fid) &&
+ (fid_seq_is_norm(seq) || fid_seq_is_igif(seq))) ||
+ fid_is_root(fid) || fid_is_dot_lustre(fid);
+}
+
+static inline int fid_seq_in_fldb(__u64 seq)
+{
+ return fid_seq_is_igif(seq) || fid_seq_is_norm(seq) ||
+ fid_seq_is_root(seq) || fid_seq_is_dot(seq);
+}
+
+static inline void lu_last_id_fid(struct lu_fid *fid, __u64 seq)
+{
+ if (fid_seq_is_mdt0(seq)) {
+ fid->f_seq = fid_idif_seq(0, 0);
+ } else {
+ LASSERTF(fid_seq_is_norm(seq) || fid_seq_is_echo(seq) ||
+ fid_seq_is_idif(seq), "%#llx\n", seq);
+ fid->f_seq = seq;
+ }
+ fid->f_oid = 0;
+ fid->f_ver = 0;
+}
+
+/* seq client type */
+enum lu_cli_type {
+ LUSTRE_SEQ_METADATA = 1,
+ LUSTRE_SEQ_DATA
+};
+
+enum lu_mgr_type {
+ LUSTRE_SEQ_SERVER,
+ LUSTRE_SEQ_CONTROLLER
+};
+
+struct lu_server_seq;
+
+/* Client sequence manager interface. */
+struct lu_client_seq {
+ /* Sequence-controller export. */
+ struct obd_export *lcs_exp;
+ struct mutex lcs_mutex;
+
+ /*
+ * Range of allowed for allocation sequences. When using lu_client_seq on
+ * clients, this contains meta-sequence range. And for servers this
+ * contains super-sequence range.
+ */
+ struct lu_seq_range lcs_space;
+
+ /* Seq related proc */
+ struct proc_dir_entry *lcs_proc_dir;
+
+ /* This holds last allocated fid in last obtained seq */
+ struct lu_fid lcs_fid;
+
+ /* LUSTRE_SEQ_METADATA or LUSTRE_SEQ_DATA */
+ enum lu_cli_type lcs_type;
+
+ /*
+ * Service uuid, passed from MDT + seq name to form unique seq name to
+ * use it with procfs.
+ */
+ char lcs_name[LUSTRE_MDT_MAXNAMELEN];
+
+ /*
+ * Sequence width, that is how many objects may be allocated in one
+ * sequence. Default value for it is LUSTRE_SEQ_MAX_WIDTH.
+ */
+ __u64 lcs_width;
+
+ /* Seq-server for direct talking */
+ struct lu_server_seq *lcs_srv;
+
+ /* wait queue for fid allocation and update indicator */
+ wait_queue_head_t lcs_waitq;
+ int lcs_update;
+};
+
+/* server sequence manager interface */
+struct lu_server_seq {
+ /* Available sequences space */
+ struct lu_seq_range lss_space;
+
+ /* keeps highwater in lsr_end for seq allocation algorithm */
+ struct lu_seq_range lss_lowater_set;
+ struct lu_seq_range lss_hiwater_set;
+
+ /*
+ * Device for server side seq manager needs (saving sequences to backing
+ * store).
+ */
+ struct dt_device *lss_dev;
+
+ /* /seq file object device */
+ struct dt_object *lss_obj;
+
+ /* Seq related proc */
+ struct proc_dir_entry *lss_proc_dir;
+
+ /* LUSTRE_SEQ_SERVER or LUSTRE_SEQ_CONTROLLER */
+ enum lu_mgr_type lss_type;
+
+ /* Client interface to request controller */
+ struct lu_client_seq *lss_cli;
+
+ /* Mutex for protecting allocation */
+ struct mutex lss_mutex;
+
+ /*
+ * Service uuid, passed from MDT + seq name to form unique seq name to
+ * use it with procfs.
+ */
+ char lss_name[LUSTRE_MDT_MAXNAMELEN];
+
+ /*
+ * Allocation chunks for super and meta sequences. Default values are
+ * LUSTRE_SEQ_SUPER_WIDTH and LUSTRE_SEQ_META_WIDTH.
+ */
+ __u64 lss_width;
+
+ /*
+ * minimum lss_alloc_set size that should be allocated from
+ * lss_space
+ */
+ __u64 lss_set_width;
+
+ /* sync is needed for update operation */
+ __u32 lss_need_sync;
+
+ /**
+ * Pointer to site object, required to access site fld.
+ */
+ struct seq_server_site *lss_site;
+};
+
+/* Server methods */
+
+int seq_server_init(struct lu_server_seq *seq,
+ struct dt_device *dev,
+ const char *prefix,
+ enum lu_mgr_type type,
+ struct seq_server_site *ss,
+ const struct lu_env *env);
+
+void seq_server_fini(struct lu_server_seq *seq,
+ const struct lu_env *env);
+
+int seq_server_alloc_super(struct lu_server_seq *seq,
+ struct lu_seq_range *out,
+ const struct lu_env *env);
+
+int seq_server_alloc_meta(struct lu_server_seq *seq,
+ struct lu_seq_range *out,
+ const struct lu_env *env);
+
+int seq_server_set_cli(struct lu_server_seq *seq,
+ struct lu_client_seq *cli,
+ const struct lu_env *env);
+
+/* Client methods */
+int seq_client_init(struct lu_client_seq *seq,
+ struct obd_export *exp,
+ enum lu_cli_type type,
+ const char *prefix,
+ struct lu_server_seq *srv);
+
+void seq_client_fini(struct lu_client_seq *seq);
+
+void seq_client_flush(struct lu_client_seq *seq);
+
+int seq_client_alloc_fid(const struct lu_env *env, struct lu_client_seq *seq,
+ struct lu_fid *fid);
+int seq_client_get_seq(const struct lu_env *env, struct lu_client_seq *seq,
+ u64 *seqnr);
+int seq_site_fini(const struct lu_env *env, struct seq_server_site *ss);
+/* Fids common stuff */
+int fid_is_local(const struct lu_env *env,
+ struct lu_site *site, const struct lu_fid *fid);
+
+enum lu_cli_type;
+int client_fid_init(struct obd_device *obd, struct obd_export *exp,
+ enum lu_cli_type type);
+int client_fid_fini(struct obd_device *obd);
+
+/* fid locking */
+
+struct ldlm_namespace;
+
+/*
+ * Build (DLM) resource name from FID.
+ *
+ * NOTE: until Lustre 1.8.7/2.1.1 the fid_ver() was packed into name[2],
+ * but was moved into name[1] along with the OID to avoid consuming the
+ * renaming name[2,3] fields that need to be used for the quota identifier.
+ */
+static inline struct ldlm_res_id *
+fid_build_reg_res_name(const struct lu_fid *fid, struct ldlm_res_id *res)
+{
+ memset(res, 0, sizeof(*res));
+ res->name[LUSTRE_RES_ID_SEQ_OFF] = fid_seq(fid);
+ res->name[LUSTRE_RES_ID_VER_OID_OFF] = fid_ver_oid(fid);
+
+ return res;
+}
+
+/*
+ * Return true if resource is for object identified by FID.
+ */
+static inline int fid_res_name_eq(const struct lu_fid *fid,
+ const struct ldlm_res_id *res)
+{
+ return res->name[LUSTRE_RES_ID_SEQ_OFF] == fid_seq(fid) &&
+ res->name[LUSTRE_RES_ID_VER_OID_OFF] == fid_ver_oid(fid);
+}
+
+/*
+ * Extract FID from LDLM resource. Reverse of fid_build_reg_res_name().
+ */
+static inline struct lu_fid *
+fid_extract_from_res_name(struct lu_fid *fid, const struct ldlm_res_id *res)
+{
+ fid->f_seq = res->name[LUSTRE_RES_ID_SEQ_OFF];
+ fid->f_oid = (__u32)(res->name[LUSTRE_RES_ID_VER_OID_OFF]);
+ fid->f_ver = (__u32)(res->name[LUSTRE_RES_ID_VER_OID_OFF] >> 32);
+ LASSERT(fid_res_name_eq(fid, res));
+
+ return fid;
+}
+
+/*
+ * Build (DLM) resource identifier from global quota FID and quota ID.
+ */
+static inline struct ldlm_res_id *
+fid_build_quota_res_name(const struct lu_fid *glb_fid, union lquota_id *qid,
+ struct ldlm_res_id *res)
+{
+ fid_build_reg_res_name(glb_fid, res);
+ res->name[LUSTRE_RES_ID_QUOTA_SEQ_OFF] = fid_seq(&qid->qid_fid);
+ res->name[LUSTRE_RES_ID_QUOTA_VER_OID_OFF] = fid_ver_oid(&qid->qid_fid);
+
+ return res;
+}
+
+/*
+ * Extract global FID and quota ID from resource name
+ */
+static inline void fid_extract_from_quota_res(struct lu_fid *glb_fid,
+ union lquota_id *qid,
+ const struct ldlm_res_id *res)
+{
+ fid_extract_from_res_name(glb_fid, res);
+ qid->qid_fid.f_seq = res->name[LUSTRE_RES_ID_QUOTA_SEQ_OFF];
+ qid->qid_fid.f_oid = (__u32)res->name[LUSTRE_RES_ID_QUOTA_VER_OID_OFF];
+ qid->qid_fid.f_ver =
+ (__u32)(res->name[LUSTRE_RES_ID_QUOTA_VER_OID_OFF] >> 32);
+}
+
+static inline struct ldlm_res_id *
+fid_build_pdo_res_name(const struct lu_fid *fid, unsigned int hash,
+ struct ldlm_res_id *res)
+{
+ fid_build_reg_res_name(fid, res);
+ res->name[LUSTRE_RES_ID_HSH_OFF] = hash;
+
+ return res;
+}
+
+/**
+ * Build DLM resource name from object id & seq, which will be removed
+ * finally, when we replace ost_id with FID in data stack.
+ *
+ * Currently, resid from the old client, whose res[0] = object_id,
+ * res[1] = object_seq, is just opposite with Metatdata
+ * resid, where, res[0] = fid->f_seq, res[1] = fid->f_oid.
+ * To unify the resid identification, we will reverse the data
+ * resid to keep it same with Metadata resid, i.e.
+ *
+ * For resid from the old client,
+ * res[0] = objid, res[1] = 0, still keep the original order,
+ * for compatibility.
+ *
+ * For new resid
+ * res will be built from normal FID directly, i.e. res[0] = f_seq,
+ * res[1] = f_oid + f_ver.
+ */
+static inline void ostid_build_res_name(struct ost_id *oi,
+ struct ldlm_res_id *name)
+{
+ memset(name, 0, sizeof(*name));
+ if (fid_seq_is_mdt0(ostid_seq(oi))) {
+ name->name[LUSTRE_RES_ID_SEQ_OFF] = ostid_id(oi);
+ name->name[LUSTRE_RES_ID_VER_OID_OFF] = ostid_seq(oi);
+ } else {
+ fid_build_reg_res_name(&oi->oi_fid, name);
+ }
+}
+
+static inline void ostid_res_name_to_id(struct ost_id *oi,
+ struct ldlm_res_id *name)
+{
+ if (fid_seq_is_mdt0(name->name[LUSTRE_RES_ID_SEQ_OFF])) {
+ /* old resid */
+ ostid_set_seq(oi, name->name[LUSTRE_RES_ID_VER_OID_OFF]);
+ ostid_set_id(oi, name->name[LUSTRE_RES_ID_SEQ_OFF]);
+ } else {
+ /* new resid */
+ fid_extract_from_res_name(&oi->oi_fid, name);
+ }
+}
+
+/**
+ * Return true if the resource is for the object identified by this id & group.
+ */
+static inline int ostid_res_name_eq(struct ost_id *oi,
+ struct ldlm_res_id *name)
+{
+ /* Note: it is just a trick here to save some effort, probably the
+ * correct way would be turn them into the FID and compare */
+ if (fid_seq_is_mdt0(ostid_seq(oi))) {
+ return name->name[LUSTRE_RES_ID_SEQ_OFF] == ostid_id(oi) &&
+ name->name[LUSTRE_RES_ID_VER_OID_OFF] == ostid_seq(oi);
+ } else {
+ return name->name[LUSTRE_RES_ID_SEQ_OFF] == ostid_seq(oi) &&
+ name->name[LUSTRE_RES_ID_VER_OID_OFF] == ostid_id(oi);
+ }
+}
+
+/* The same as osc_build_res_name() */
+static inline void ost_fid_build_resid(const struct lu_fid *fid,
+ struct ldlm_res_id *resname)
+{
+ if (fid_is_mdt0(fid) || fid_is_idif(fid)) {
+ struct ost_id oi;
+ oi.oi.oi_id = 0; /* gcc 4.7.2 complains otherwise */
+ if (fid_to_ostid(fid, &oi) != 0)
+ return;
+ ostid_build_res_name(&oi, resname);
+ } else {
+ fid_build_reg_res_name(fid, resname);
+ }
+}
+
+static inline void ost_fid_from_resid(struct lu_fid *fid,
+ const struct ldlm_res_id *name)
+{
+ if (fid_seq_is_mdt0(name->name[LUSTRE_RES_ID_VER_OID_OFF])) {
+ /* old resid */
+ struct ost_id oi;
+ ostid_set_seq(&oi, name->name[LUSTRE_RES_ID_VER_OID_OFF]);
+ ostid_set_id(&oi, name->name[LUSTRE_RES_ID_SEQ_OFF]);
+ ostid_to_fid(fid, &oi, 0);
+ } else {
+ /* new resid */
+ fid_extract_from_res_name(fid, name);
+ }
+}
+
+/**
+ * Flatten 128-bit FID values into a 64-bit value for use as an inode number.
+ * For non-IGIF FIDs this starts just over 2^32, and continues without
+ * conflict until 2^64, at which point we wrap the high 24 bits of the SEQ
+ * into the range where there may not be many OID values in use, to minimize
+ * the risk of conflict.
+ *
+ * Suppose LUSTRE_SEQ_MAX_WIDTH less than (1 << 24) which is currently true,
+ * the time between re-used inode numbers is very long - 2^40 SEQ numbers,
+ * or about 2^40 client mounts, if clients create less than 2^24 files/mount.
+ */
+static inline __u64 fid_flatten(const struct lu_fid *fid)
+{
+ __u64 ino;
+ __u64 seq;
+
+ if (fid_is_igif(fid)) {
+ ino = lu_igif_ino(fid);
+ return ino;
+ }
+
+ seq = fid_seq(fid);
+
+ ino = (seq << 24) + ((seq >> 24) & 0xffffff0000ULL) + fid_oid(fid);
+
+ return ino ? ino : fid_oid(fid);
+}
+
+static inline __u32 fid_hash(const struct lu_fid *f, int bits)
+{
+ /* all objects with same id and different versions will belong to same
+ * collisions list. */
+ return hash_long(fid_flatten(f), bits);
+}
+
+/**
+ * map fid to 32 bit value for ino on 32bit systems. */
+static inline __u32 fid_flatten32(const struct lu_fid *fid)
+{
+ __u32 ino;
+ __u64 seq;
+
+ if (fid_is_igif(fid)) {
+ ino = lu_igif_ino(fid);
+ return ino;
+ }
+
+ seq = fid_seq(fid) - FID_SEQ_START;
+
+ /* Map the high bits of the OID into higher bits of the inode number so
+ * that inodes generated at about the same time have a reduced chance
+ * of collisions. This will give a period of 2^12 = 1024 unique clients
+ * (from SEQ) and up to min(LUSTRE_SEQ_MAX_WIDTH, 2^20) = 128k objects
+ * (from OID), or up to 128M inodes without collisions for new files. */
+ ino = ((seq & 0x000fffffULL) << 12) + ((seq >> 8) & 0xfffff000) +
+ (seq >> (64 - (40-8)) & 0xffffff00) +
+ (fid_oid(fid) & 0xff000fff) + ((fid_oid(fid) & 0x00fff000) << 8);
+
+ return ino ? ino : fid_oid(fid);
+}
+
+static inline int lu_fid_diff(struct lu_fid *fid1, struct lu_fid *fid2)
+{
+ LASSERTF(fid_seq(fid1) == fid_seq(fid2), "fid1:"DFID", fid2:"DFID"\n",
+ PFID(fid1), PFID(fid2));
+
+ if (fid_is_idif(fid1) && fid_is_idif(fid2))
+ return fid_idif_id(fid1->f_seq, fid1->f_oid, fid1->f_ver) -
+ fid_idif_id(fid2->f_seq, fid2->f_oid, fid2->f_ver);
+
+ return fid_oid(fid1) - fid_oid(fid2);
+}
+
+#define LUSTRE_SEQ_SRV_NAME "seq_srv"
+#define LUSTRE_SEQ_CTL_NAME "seq_ctl"
+
+/* Range common stuff */
+static inline void range_cpu_to_le(struct lu_seq_range *dst, const struct lu_seq_range *src)
+{
+ dst->lsr_start = cpu_to_le64(src->lsr_start);
+ dst->lsr_end = cpu_to_le64(src->lsr_end);
+ dst->lsr_index = cpu_to_le32(src->lsr_index);
+ dst->lsr_flags = cpu_to_le32(src->lsr_flags);
+}
+
+static inline void range_le_to_cpu(struct lu_seq_range *dst, const struct lu_seq_range *src)
+{
+ dst->lsr_start = le64_to_cpu(src->lsr_start);
+ dst->lsr_end = le64_to_cpu(src->lsr_end);
+ dst->lsr_index = le32_to_cpu(src->lsr_index);
+ dst->lsr_flags = le32_to_cpu(src->lsr_flags);
+}
+
+static inline void range_cpu_to_be(struct lu_seq_range *dst, const struct lu_seq_range *src)
+{
+ dst->lsr_start = cpu_to_be64(src->lsr_start);
+ dst->lsr_end = cpu_to_be64(src->lsr_end);
+ dst->lsr_index = cpu_to_be32(src->lsr_index);
+ dst->lsr_flags = cpu_to_be32(src->lsr_flags);
+}
+
+static inline void range_be_to_cpu(struct lu_seq_range *dst, const struct lu_seq_range *src)
+{
+ dst->lsr_start = be64_to_cpu(src->lsr_start);
+ dst->lsr_end = be64_to_cpu(src->lsr_end);
+ dst->lsr_index = be32_to_cpu(src->lsr_index);
+ dst->lsr_flags = be32_to_cpu(src->lsr_flags);
+}
+
+/** @} fid */
+
+#endif /* __LUSTRE_FID_H */
diff --git a/drivers/staging/lustre/lustre/include/lustre_fld.h b/drivers/staging/lustre/lustre/include/lustre_fld.h
new file mode 100644
index 000000000..5ee4b1ed0
--- /dev/null
+++ b/drivers/staging/lustre/lustre/include/lustre_fld.h
@@ -0,0 +1,160 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2011, 2013, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ */
+
+#ifndef __LINUX_FLD_H
+#define __LINUX_FLD_H
+
+/** \defgroup fld fld
+ *
+ * @{
+ */
+
+#include "lustre/lustre_idl.h"
+#include "../../include/linux/libcfs/libcfs.h"
+
+struct lu_client_fld;
+struct lu_server_fld;
+struct lu_fld_hash;
+struct fld_cache;
+
+extern const struct dt_index_features fld_index_features;
+extern const char fld_index_name[];
+
+/*
+ * FLD (Fid Location Database) interface.
+ */
+enum {
+ LUSTRE_CLI_FLD_HASH_DHT = 0,
+ LUSTRE_CLI_FLD_HASH_RRB
+};
+
+
+struct lu_fld_target {
+ struct list_head ft_chain;
+ struct obd_export *ft_exp;
+ struct lu_server_fld *ft_srv;
+ __u64 ft_idx;
+};
+
+struct lu_server_fld {
+ /**
+ * Fld dir proc entry. */
+ struct proc_dir_entry *lsf_proc_dir;
+
+ /**
+ * /fld file object device */
+ struct dt_object *lsf_obj;
+
+ /**
+ * super sequence controller export, needed to forward fld
+ * lookup request. */
+ struct obd_export *lsf_control_exp;
+
+ /**
+ * Client FLD cache. */
+ struct fld_cache *lsf_cache;
+
+ /**
+ * Protect index modifications */
+ struct mutex lsf_lock;
+
+ /**
+ * Fld service name in form "fld-srv-lustre-MDTXXX" */
+ char lsf_name[LUSTRE_MDT_MAXNAMELEN];
+
+};
+
+struct lu_client_fld {
+ /**
+ * Client side proc entry. */
+ struct proc_dir_entry *lcf_proc_dir;
+
+ /**
+ * List of exports client FLD knows about. */
+ struct list_head lcf_targets;
+
+ /**
+ * Current hash to be used to chose an export. */
+ struct lu_fld_hash *lcf_hash;
+
+ /**
+ * Exports count. */
+ int lcf_count;
+
+ /**
+ * Lock protecting exports list and fld_hash. */
+ spinlock_t lcf_lock;
+
+ /**
+ * Client FLD cache. */
+ struct fld_cache *lcf_cache;
+
+ /**
+ * Client fld proc entry name. */
+ char lcf_name[LUSTRE_MDT_MAXNAMELEN];
+
+ int lcf_flags;
+};
+
+/* Client methods */
+int fld_client_init(struct lu_client_fld *fld,
+ const char *prefix, int hash);
+
+void fld_client_fini(struct lu_client_fld *fld);
+
+void fld_client_flush(struct lu_client_fld *fld);
+
+int fld_client_lookup(struct lu_client_fld *fld, u64 seq, u32 *mds,
+ __u32 flags, const struct lu_env *env);
+
+int fld_client_create(struct lu_client_fld *fld,
+ struct lu_seq_range *range,
+ const struct lu_env *env);
+
+int fld_client_delete(struct lu_client_fld *fld, u64 seq,
+ const struct lu_env *env);
+
+int fld_client_add_target(struct lu_client_fld *fld,
+ struct lu_fld_target *tar);
+
+int fld_client_del_target(struct lu_client_fld *fld,
+ __u64 idx);
+
+void fld_client_proc_fini(struct lu_client_fld *fld);
+
+/** @} fld */
+
+#endif
diff --git a/drivers/staging/lustre/lustre/include/lustre_ha.h b/drivers/staging/lustre/lustre/include/lustre_ha.h
new file mode 100644
index 000000000..f3ae02b3e
--- /dev/null
+++ b/drivers/staging/lustre/lustre/include/lustre_ha.h
@@ -0,0 +1,64 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2002, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2011, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ */
+
+#ifndef _LUSTRE_HA_H
+#define _LUSTRE_HA_H
+
+/** \defgroup ha ha
+ *
+ * @{
+ */
+
+struct obd_import;
+struct obd_export;
+struct obd_device;
+struct ptlrpc_request;
+
+
+int ptlrpc_replay(struct obd_import *imp);
+int ptlrpc_resend(struct obd_import *imp);
+void ptlrpc_free_committed(struct obd_import *imp);
+void ptlrpc_wake_delayed(struct obd_import *imp);
+int ptlrpc_recover_import(struct obd_import *imp, char *new_uuid, int async);
+int ptlrpc_set_import_active(struct obd_import *imp, int active);
+void ptlrpc_activate_import(struct obd_import *imp);
+void ptlrpc_deactivate_import(struct obd_import *imp);
+void ptlrpc_invalidate_import(struct obd_import *imp);
+void ptlrpc_fail_import(struct obd_import *imp, __u32 conn_cnt);
+
+/** @} ha */
+
+#endif
diff --git a/drivers/staging/lustre/lustre/include/lustre_handles.h b/drivers/staging/lustre/lustre/include/lustre_handles.h
new file mode 100644
index 000000000..726bbd3ea
--- /dev/null
+++ b/drivers/staging/lustre/lustre/include/lustre_handles.h
@@ -0,0 +1,97 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2011, 2012, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ */
+
+#ifndef __LUSTRE_HANDLES_H_
+#define __LUSTRE_HANDLES_H_
+
+/** \defgroup handles handles
+ *
+ * @{
+ */
+
+#include <linux/atomic.h>
+#include <linux/list.h>
+#include <linux/rcupdate.h>
+#include <linux/spinlock.h>
+#include <linux/types.h>
+
+#include "../../include/linux/libcfs/libcfs.h"
+
+
+struct portals_handle_ops {
+ void (*hop_addref)(void *object);
+ void (*hop_free)(void *object, int size);
+};
+
+/* These handles are most easily used by having them appear at the very top of
+ * whatever object that you want to make handles for. ie:
+ *
+ * struct ldlm_lock {
+ * struct portals_handle handle;
+ * ...
+ * };
+ *
+ * Now you're able to assign the results of cookie2handle directly to an
+ * ldlm_lock. If it's not at the top, you'll want to use container_of()
+ * to compute the start of the structure based on the handle field. */
+struct portals_handle {
+ struct list_head h_link;
+ __u64 h_cookie;
+ struct portals_handle_ops *h_ops;
+
+ /* newly added fields to handle the RCU issue. -jxiong */
+ struct rcu_head h_rcu;
+ spinlock_t h_lock;
+ unsigned int h_size:31;
+ unsigned int h_in:1;
+};
+#define RCU2HANDLE(rcu) container_of(rcu, struct portals_handle, h_rcu)
+
+/* handles.c */
+
+/* Add a handle to the hash table */
+void class_handle_hash(struct portals_handle *,
+ struct portals_handle_ops *ops);
+void class_handle_unhash(struct portals_handle *);
+void class_handle_hash_back(struct portals_handle *);
+void *class_handle2object(__u64 cookie);
+void class_handle_free_cb(struct rcu_head *rcu);
+int class_handle_init(void);
+void class_handle_cleanup(void);
+
+/** @} handles */
+
+#endif
diff --git a/drivers/staging/lustre/lustre/include/lustre_import.h b/drivers/staging/lustre/lustre/include/lustre_import.h
new file mode 100644
index 000000000..dcc807676
--- /dev/null
+++ b/drivers/staging/lustre/lustre/include/lustre_import.h
@@ -0,0 +1,385 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2002, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2011, 2012, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ */
+/** \defgroup obd_import PtlRPC import definitions
+ * Imports are client-side representation of remote obd target.
+ *
+ * @{
+ */
+
+#ifndef __IMPORT_H
+#define __IMPORT_H
+
+/** \defgroup export export
+ *
+ * @{
+ */
+
+#include "lustre_handles.h"
+#include "lustre/lustre_idl.h"
+
+
+/**
+ * Adaptive Timeout stuff
+ *
+ * @{
+ */
+#define D_ADAPTTO D_OTHER
+#define AT_BINS 4 /* "bin" means "N seconds of history" */
+#define AT_FLG_NOHIST 0x1 /* use last reported value only */
+
+struct adaptive_timeout {
+ time_t at_binstart; /* bin start time */
+ unsigned int at_hist[AT_BINS]; /* timeout history bins */
+ unsigned int at_flags;
+ unsigned int at_current; /* current timeout value */
+ unsigned int at_worst_ever; /* worst-ever timeout value */
+ time_t at_worst_time; /* worst-ever timeout timestamp */
+ spinlock_t at_lock;
+};
+
+struct ptlrpc_at_array {
+ struct list_head *paa_reqs_array; /** array to hold requests */
+ __u32 paa_size; /** the size of array */
+ __u32 paa_count; /** the total count of reqs */
+ time_t paa_deadline; /** the earliest deadline of reqs */
+ __u32 *paa_reqs_count; /** the count of reqs in each entry */
+};
+
+#define IMP_AT_MAX_PORTALS 8
+struct imp_at {
+ int iat_portal[IMP_AT_MAX_PORTALS];
+ struct adaptive_timeout iat_net_latency;
+ struct adaptive_timeout iat_service_estimate[IMP_AT_MAX_PORTALS];
+};
+
+
+/** @} */
+
+/** Possible import states */
+enum lustre_imp_state {
+ LUSTRE_IMP_CLOSED = 1,
+ LUSTRE_IMP_NEW = 2,
+ LUSTRE_IMP_DISCON = 3,
+ LUSTRE_IMP_CONNECTING = 4,
+ LUSTRE_IMP_REPLAY = 5,
+ LUSTRE_IMP_REPLAY_LOCKS = 6,
+ LUSTRE_IMP_REPLAY_WAIT = 7,
+ LUSTRE_IMP_RECOVER = 8,
+ LUSTRE_IMP_FULL = 9,
+ LUSTRE_IMP_EVICTED = 10,
+};
+
+/** Returns test string representation of numeric import state \a state */
+static inline char *ptlrpc_import_state_name(enum lustre_imp_state state)
+{
+ static char *import_state_names[] = {
+ "<UNKNOWN>", "CLOSED", "NEW", "DISCONN",
+ "CONNECTING", "REPLAY", "REPLAY_LOCKS", "REPLAY_WAIT",
+ "RECOVER", "FULL", "EVICTED",
+ };
+
+ LASSERT (state <= LUSTRE_IMP_EVICTED);
+ return import_state_names[state];
+}
+
+/**
+ * List of import event types
+ */
+enum obd_import_event {
+ IMP_EVENT_DISCON = 0x808001,
+ IMP_EVENT_INACTIVE = 0x808002,
+ IMP_EVENT_INVALIDATE = 0x808003,
+ IMP_EVENT_ACTIVE = 0x808004,
+ IMP_EVENT_OCD = 0x808005,
+ IMP_EVENT_DEACTIVATE = 0x808006,
+ IMP_EVENT_ACTIVATE = 0x808007,
+};
+
+/**
+ * Definition of import connection structure
+ */
+struct obd_import_conn {
+ /** Item for linking connections together */
+ struct list_head oic_item;
+ /** Pointer to actual PortalRPC connection */
+ struct ptlrpc_connection *oic_conn;
+ /** uuid of remote side */
+ struct obd_uuid oic_uuid;
+ /**
+ * Time (64 bit jiffies) of last connection attempt on this connection
+ */
+ __u64 oic_last_attempt;
+};
+
+/* state history */
+#define IMP_STATE_HIST_LEN 16
+struct import_state_hist {
+ enum lustre_imp_state ish_state;
+ time_t ish_time;
+};
+
+/**
+ * Definition of PortalRPC import structure.
+ * Imports are representing client-side view to remote target.
+ */
+struct obd_import {
+ /** Local handle (== id) for this import. */
+ struct portals_handle imp_handle;
+ /** Reference counter */
+ atomic_t imp_refcount;
+ struct lustre_handle imp_dlm_handle; /* client's ldlm export */
+ /** Currently active connection */
+ struct ptlrpc_connection *imp_connection;
+ /** PortalRPC client structure for this import */
+ struct ptlrpc_client *imp_client;
+ /** List element for linking into pinger chain */
+ struct list_head imp_pinger_chain;
+ /** List element for linking into chain for destruction */
+ struct list_head imp_zombie_chain;
+
+ /**
+ * Lists of requests that are retained for replay, waiting for a reply,
+ * or waiting for recovery to complete, respectively.
+ * @{
+ */
+ struct list_head imp_replay_list;
+ struct list_head imp_sending_list;
+ struct list_head imp_delayed_list;
+ /** @} */
+
+ /**
+ * List of requests that are retained for committed open replay. Once
+ * open is committed, open replay request will be moved from the
+ * imp_replay_list into the imp_committed_list.
+ * The imp_replay_cursor is for accelerating searching during replay.
+ * @{
+ */
+ struct list_head imp_committed_list;
+ struct list_head *imp_replay_cursor;
+ /** @} */
+
+ /** obd device for this import */
+ struct obd_device *imp_obd;
+
+ /**
+ * some seciruty-related fields
+ * @{
+ */
+ struct ptlrpc_sec *imp_sec;
+ struct mutex imp_sec_mutex;
+ unsigned long imp_sec_expire;
+ /** @} */
+
+ /** Wait queue for those who need to wait for recovery completion */
+ wait_queue_head_t imp_recovery_waitq;
+
+ /** Number of requests currently in-flight */
+ atomic_t imp_inflight;
+ /** Number of requests currently unregistering */
+ atomic_t imp_unregistering;
+ /** Number of replay requests inflight */
+ atomic_t imp_replay_inflight;
+ /** Number of currently happening import invalidations */
+ atomic_t imp_inval_count;
+ /** Numbner of request timeouts */
+ atomic_t imp_timeouts;
+ /** Current import state */
+ enum lustre_imp_state imp_state;
+ /** Last replay state */
+ enum lustre_imp_state imp_replay_state;
+ /** History of import states */
+ struct import_state_hist imp_state_hist[IMP_STATE_HIST_LEN];
+ int imp_state_hist_idx;
+ /** Current import generation. Incremented on every reconnect */
+ int imp_generation;
+ /** Incremented every time we send reconnection request */
+ __u32 imp_conn_cnt;
+ /**
+ * \see ptlrpc_free_committed remembers imp_generation value here
+ * after a check to save on unnecessary replay list iterations
+ */
+ int imp_last_generation_checked;
+ /** Last transno we replayed */
+ __u64 imp_last_replay_transno;
+ /** Last transno committed on remote side */
+ __u64 imp_peer_committed_transno;
+ /**
+ * \see ptlrpc_free_committed remembers last_transno since its last
+ * check here and if last_transno did not change since last run of
+ * ptlrpc_free_committed and import generation is the same, we can
+ * skip looking for requests to remove from replay list as optimisation
+ */
+ __u64 imp_last_transno_checked;
+ /**
+ * Remote export handle. This is how remote side knows what export
+ * we are talking to. Filled from response to connect request
+ */
+ struct lustre_handle imp_remote_handle;
+ /** When to perform next ping. time in jiffies. */
+ unsigned long imp_next_ping;
+ /** When we last successfully connected. time in 64bit jiffies */
+ __u64 imp_last_success_conn;
+
+ /** List of all possible connection for import. */
+ struct list_head imp_conn_list;
+ /**
+ * Current connection. \a imp_connection is imp_conn_current->oic_conn
+ */
+ struct obd_import_conn *imp_conn_current;
+
+ /** Protects flags, level, generation, conn_cnt, *_list */
+ spinlock_t imp_lock;
+
+ /* flags */
+ unsigned long imp_no_timeout:1, /* timeouts are disabled */
+ imp_invalid:1, /* evicted */
+ /* administratively disabled */
+ imp_deactive:1,
+ /* try to recover the import */
+ imp_replayable:1,
+ /* don't run recovery (timeout instead) */
+ imp_dlm_fake:1,
+ /* use 1/2 timeout on MDS' OSCs */
+ imp_server_timeout:1,
+ /* VBR: imp in delayed recovery */
+ imp_delayed_recovery:1,
+ /* VBR: if gap was found then no lock replays
+ */
+ imp_no_lock_replay:1,
+ /* recovery by versions was failed */
+ imp_vbr_failed:1,
+ /* force an immediate ping */
+ imp_force_verify:1,
+ /* force a scheduled ping */
+ imp_force_next_verify:1,
+ /* pingable */
+ imp_pingable:1,
+ /* resend for replay */
+ imp_resend_replay:1,
+ /* disable normal recovery, for test only. */
+ imp_no_pinger_recover:1,
+ /* need IR MNE swab */
+ imp_need_mne_swab:1,
+ /* import must be reconnected instead of
+ * chose new connection */
+ imp_force_reconnect:1,
+ /* import has tried to connect with server */
+ imp_connect_tried:1;
+ __u32 imp_connect_op;
+ struct obd_connect_data imp_connect_data;
+ __u64 imp_connect_flags_orig;
+ int imp_connect_error;
+
+ __u32 imp_msg_magic;
+ __u32 imp_msghdr_flags; /* adjusted based on server capability */
+
+ struct ptlrpc_request_pool *imp_rq_pool; /* emergency request pool */
+
+ struct imp_at imp_at; /* adaptive timeout data */
+ time_t imp_last_reply_time; /* for health check */
+};
+
+typedef void (*obd_import_callback)(struct obd_import *imp, void *closure,
+ int event, void *event_arg, void *cb_data);
+
+/**
+ * Structure for import observer.
+ * It is possible to register "observer" on an import and every time
+ * something happens to an import (like connect/evict/disconnect)
+ * obderver will get its callback called with event type
+ */
+struct obd_import_observer {
+ struct list_head oio_chain;
+ obd_import_callback oio_cb;
+ void *oio_cb_data;
+};
+
+void class_observe_import(struct obd_import *imp, obd_import_callback cb,
+ void *cb_data);
+void class_unobserve_import(struct obd_import *imp, obd_import_callback cb,
+ void *cb_data);
+void class_notify_import_observers(struct obd_import *imp, int event,
+ void *event_arg);
+
+/* import.c */
+static inline unsigned int at_est2timeout(unsigned int val)
+{
+ /* add an arbitrary minimum: 125% +5 sec */
+ return (val + (val >> 2) + 5);
+}
+
+static inline unsigned int at_timeout2est(unsigned int val)
+{
+ /* restore estimate value from timeout: e=4/5(t-5) */
+ LASSERT(val);
+ return (max((val << 2) / 5, 5U) - 4);
+}
+
+static inline void at_reset(struct adaptive_timeout *at, int val)
+{
+ spin_lock(&at->at_lock);
+ at->at_current = val;
+ at->at_worst_ever = val;
+ at->at_worst_time = get_seconds();
+ spin_unlock(&at->at_lock);
+}
+static inline void at_init(struct adaptive_timeout *at, int val, int flags)
+{
+ memset(at, 0, sizeof(*at));
+ spin_lock_init(&at->at_lock);
+ at->at_flags = flags;
+ at_reset(at, val);
+}
+extern unsigned int at_min;
+static inline int at_get(struct adaptive_timeout *at)
+{
+ return (at->at_current > at_min) ? at->at_current : at_min;
+}
+int at_measured(struct adaptive_timeout *at, unsigned int val);
+int import_at_get_index(struct obd_import *imp, int portal);
+extern unsigned int at_max;
+#define AT_OFF (at_max == 0)
+
+/* genops.c */
+struct obd_export;
+extern struct obd_import *class_exp2cliimp(struct obd_export *);
+extern struct obd_import *class_conn2cliimp(struct lustre_handle *);
+
+/** @} import */
+
+#endif /* __IMPORT_H */
+
+/** @} obd_import */
diff --git a/drivers/staging/lustre/lustre/include/lustre_intent.h b/drivers/staging/lustre/lustre/include/lustre_intent.h
new file mode 100644
index 000000000..c491d52d8
--- /dev/null
+++ b/drivers/staging/lustre/lustre/include/lustre_intent.h
@@ -0,0 +1,62 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2011, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ */
+
+#ifndef LUSTRE_INTENT_H
+#define LUSTRE_INTENT_H
+
+/* intent IT_XXX are defined in lustre/include/obd.h */
+struct lustre_intent_data {
+ int it_disposition;
+ int it_status;
+ __u64 it_lock_handle;
+ __u64 it_lock_bits;
+ int it_lock_mode;
+ int it_remote_lock_mode;
+ __u64 it_remote_lock_handle;
+ void *it_data;
+ unsigned int it_lock_set:1;
+};
+
+struct lookup_intent {
+ int it_op;
+ int it_create_mode;
+ __u64 it_flags;
+ union {
+ struct lustre_intent_data lustre;
+ } d;
+};
+
+#endif
diff --git a/drivers/staging/lustre/lustre/include/lustre_lib.h b/drivers/staging/lustre/lustre/include/lustre_lib.h
new file mode 100644
index 000000000..bf135630c
--- /dev/null
+++ b/drivers/staging/lustre/lustre/include/lustre_lib.h
@@ -0,0 +1,666 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2011, 2012, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ *
+ * lustre/include/lustre_lib.h
+ *
+ * Basic Lustre library routines.
+ */
+
+#ifndef _LUSTRE_LIB_H
+#define _LUSTRE_LIB_H
+
+/** \defgroup lib lib
+ *
+ * @{
+ */
+
+#include <linux/sched.h>
+#include <linux/signal.h>
+#include <linux/types.h>
+#include "../../include/linux/libcfs/libcfs.h"
+#include "lustre/lustre_idl.h"
+#include "lustre_ver.h"
+#include "lustre_cfg.h"
+
+/* target.c */
+struct kstatfs;
+struct ptlrpc_request;
+struct obd_export;
+struct lu_target;
+struct l_wait_info;
+#include "lustre_ha.h"
+#include "lustre_net.h"
+
+#define LI_POISON 0x5a5a5a5a
+#if BITS_PER_LONG > 32
+# define LL_POISON 0x5a5a5a5a5a5a5a5aL
+#else
+# define LL_POISON 0x5a5a5a5aL
+#endif
+#define LP_POISON ((void *)LL_POISON)
+
+int target_pack_pool_reply(struct ptlrpc_request *req);
+int do_set_info_async(struct obd_import *imp,
+ int opcode, int version,
+ u32 keylen, void *key,
+ u32 vallen, void *val,
+ struct ptlrpc_request_set *set);
+
+#define OBD_RECOVERY_MAX_TIME (obd_timeout * 18) /* b13079 */
+#define OBD_MAX_IOCTL_BUFFER CONFIG_LUSTRE_OBD_MAX_IOCTL_BUFFER
+
+void target_send_reply(struct ptlrpc_request *req, int rc, int fail_id);
+
+/* client.c */
+
+int client_sanobd_setup(struct obd_device *obddev, struct lustre_cfg *lcfg);
+struct client_obd *client_conn2cli(struct lustre_handle *conn);
+
+struct md_open_data;
+struct obd_client_handle {
+ struct lustre_handle och_fh;
+ struct lu_fid och_fid;
+ struct md_open_data *och_mod;
+ struct lustre_handle och_lease_handle; /* open lock for lease */
+ __u32 och_magic;
+ fmode_t och_flags;
+};
+#define OBD_CLIENT_HANDLE_MAGIC 0xd15ea5ed
+
+/* statfs_pack.c */
+void statfs_pack(struct obd_statfs *osfs, struct kstatfs *sfs);
+void statfs_unpack(struct kstatfs *sfs, struct obd_statfs *osfs);
+
+/*
+ * For md echo client
+ */
+enum md_echo_cmd {
+ ECHO_MD_CREATE = 1, /* Open/Create file on MDT */
+ ECHO_MD_MKDIR = 2, /* Mkdir on MDT */
+ ECHO_MD_DESTROY = 3, /* Unlink file on MDT */
+ ECHO_MD_RMDIR = 4, /* Rmdir on MDT */
+ ECHO_MD_LOOKUP = 5, /* Lookup on MDT */
+ ECHO_MD_GETATTR = 6, /* Getattr on MDT */
+ ECHO_MD_SETATTR = 7, /* Setattr on MDT */
+ ECHO_MD_ALLOC_FID = 8, /* Get FIDs from MDT */
+};
+
+/*
+ * OBD IOCTLS
+ */
+#define OBD_IOCTL_VERSION 0x00010004
+
+struct obd_ioctl_data {
+ __u32 ioc_len;
+ __u32 ioc_version;
+
+ union {
+ __u64 ioc_cookie;
+ __u64 ioc_u64_1;
+ };
+ union {
+ __u32 ioc_conn1;
+ __u32 ioc_u32_1;
+ };
+ union {
+ __u32 ioc_conn2;
+ __u32 ioc_u32_2;
+ };
+
+ struct obdo ioc_obdo1;
+ struct obdo ioc_obdo2;
+
+ u64 ioc_count;
+ u64 ioc_offset;
+ __u32 ioc_dev;
+ __u32 ioc_command;
+
+ __u64 ioc_nid;
+ __u32 ioc_nal;
+ __u32 ioc_type;
+
+ /* buffers the kernel will treat as user pointers */
+ __u32 ioc_plen1;
+ char *ioc_pbuf1;
+ __u32 ioc_plen2;
+ char *ioc_pbuf2;
+
+ /* inline buffers for various arguments */
+ __u32 ioc_inllen1;
+ char *ioc_inlbuf1;
+ __u32 ioc_inllen2;
+ char *ioc_inlbuf2;
+ __u32 ioc_inllen3;
+ char *ioc_inlbuf3;
+ __u32 ioc_inllen4;
+ char *ioc_inlbuf4;
+
+ char ioc_bulk[0];
+};
+
+struct obd_ioctl_hdr {
+ __u32 ioc_len;
+ __u32 ioc_version;
+};
+
+static inline int obd_ioctl_packlen(struct obd_ioctl_data *data)
+{
+ int len = cfs_size_round(sizeof(struct obd_ioctl_data));
+ len += cfs_size_round(data->ioc_inllen1);
+ len += cfs_size_round(data->ioc_inllen2);
+ len += cfs_size_round(data->ioc_inllen3);
+ len += cfs_size_round(data->ioc_inllen4);
+ return len;
+}
+
+
+static inline int obd_ioctl_is_invalid(struct obd_ioctl_data *data)
+{
+ if (data->ioc_len > OBD_MAX_IOCTL_BUFFER) {
+ CERROR("OBD ioctl: ioc_len larger than %d\n",
+ OBD_MAX_IOCTL_BUFFER);
+ return 1;
+ }
+ if (data->ioc_inllen1 > OBD_MAX_IOCTL_BUFFER) {
+ CERROR("OBD ioctl: ioc_inllen1 larger than ioc_len\n");
+ return 1;
+ }
+ if (data->ioc_inllen2 > OBD_MAX_IOCTL_BUFFER) {
+ CERROR("OBD ioctl: ioc_inllen2 larger than ioc_len\n");
+ return 1;
+ }
+ if (data->ioc_inllen3 > OBD_MAX_IOCTL_BUFFER) {
+ CERROR("OBD ioctl: ioc_inllen3 larger than ioc_len\n");
+ return 1;
+ }
+ if (data->ioc_inllen4 > OBD_MAX_IOCTL_BUFFER) {
+ CERROR("OBD ioctl: ioc_inllen4 larger than ioc_len\n");
+ return 1;
+ }
+ if (data->ioc_inlbuf1 && !data->ioc_inllen1) {
+ CERROR("OBD ioctl: inlbuf1 pointer but 0 length\n");
+ return 1;
+ }
+ if (data->ioc_inlbuf2 && !data->ioc_inllen2) {
+ CERROR("OBD ioctl: inlbuf2 pointer but 0 length\n");
+ return 1;
+ }
+ if (data->ioc_inlbuf3 && !data->ioc_inllen3) {
+ CERROR("OBD ioctl: inlbuf3 pointer but 0 length\n");
+ return 1;
+ }
+ if (data->ioc_inlbuf4 && !data->ioc_inllen4) {
+ CERROR("OBD ioctl: inlbuf4 pointer but 0 length\n");
+ return 1;
+ }
+ if (data->ioc_pbuf1 && !data->ioc_plen1) {
+ CERROR("OBD ioctl: pbuf1 pointer but 0 length\n");
+ return 1;
+ }
+ if (data->ioc_pbuf2 && !data->ioc_plen2) {
+ CERROR("OBD ioctl: pbuf2 pointer but 0 length\n");
+ return 1;
+ }
+ if (data->ioc_plen1 && !data->ioc_pbuf1) {
+ CERROR("OBD ioctl: plen1 set but NULL pointer\n");
+ return 1;
+ }
+ if (data->ioc_plen2 && !data->ioc_pbuf2) {
+ CERROR("OBD ioctl: plen2 set but NULL pointer\n");
+ return 1;
+ }
+ if (obd_ioctl_packlen(data) > data->ioc_len) {
+ CERROR("OBD ioctl: packlen exceeds ioc_len (%d > %d)\n",
+ obd_ioctl_packlen(data), data->ioc_len);
+ return 1;
+ }
+ return 0;
+}
+
+
+#include "obd_support.h"
+
+/* function defined in lustre/obdclass/<platform>/<platform>-module.c */
+int obd_ioctl_getdata(char **buf, int *len, void *arg);
+int obd_ioctl_popdata(void *arg, void *data, int len);
+
+static inline void obd_ioctl_freedata(char *buf, int len)
+{
+ OBD_FREE_LARGE(buf, len);
+ return;
+}
+
+/*
+ * BSD ioctl description:
+ * #define IOC_V1 _IOR(g, n1, long)
+ * #define IOC_V2 _IOW(g, n2, long)
+ *
+ * ioctl(f, IOC_V1, arg);
+ * arg will be treated as a long value,
+ *
+ * ioctl(f, IOC_V2, arg)
+ * arg will be treated as a pointer, bsd will call
+ * copyin(buf, arg, sizeof(long))
+ *
+ * To make BSD ioctl handles argument correctly and simplely,
+ * we change _IOR to _IOWR so BSD will copyin obd_ioctl_data
+ * for us. Does this change affect Linux? (XXX Liang)
+ */
+#define OBD_IOC_DATA_TYPE long
+
+#define OBD_IOC_CREATE _IOWR('f', 101, OBD_IOC_DATA_TYPE)
+#define OBD_IOC_DESTROY _IOW ('f', 104, OBD_IOC_DATA_TYPE)
+#define OBD_IOC_PREALLOCATE _IOWR('f', 105, OBD_IOC_DATA_TYPE)
+
+#define OBD_IOC_SETATTR _IOW ('f', 107, OBD_IOC_DATA_TYPE)
+#define OBD_IOC_GETATTR _IOWR ('f', 108, OBD_IOC_DATA_TYPE)
+#define OBD_IOC_READ _IOWR('f', 109, OBD_IOC_DATA_TYPE)
+#define OBD_IOC_WRITE _IOWR('f', 110, OBD_IOC_DATA_TYPE)
+
+
+#define OBD_IOC_STATFS _IOWR('f', 113, OBD_IOC_DATA_TYPE)
+#define OBD_IOC_SYNC _IOW ('f', 114, OBD_IOC_DATA_TYPE)
+#define OBD_IOC_READ2 _IOWR('f', 115, OBD_IOC_DATA_TYPE)
+#define OBD_IOC_FORMAT _IOWR('f', 116, OBD_IOC_DATA_TYPE)
+#define OBD_IOC_PARTITION _IOWR('f', 117, OBD_IOC_DATA_TYPE)
+#define OBD_IOC_COPY _IOWR('f', 120, OBD_IOC_DATA_TYPE)
+#define OBD_IOC_MIGR _IOWR('f', 121, OBD_IOC_DATA_TYPE)
+#define OBD_IOC_PUNCH _IOWR('f', 122, OBD_IOC_DATA_TYPE)
+
+#define OBD_IOC_MODULE_DEBUG _IOWR('f', 124, OBD_IOC_DATA_TYPE)
+#define OBD_IOC_BRW_READ _IOWR('f', 125, OBD_IOC_DATA_TYPE)
+#define OBD_IOC_BRW_WRITE _IOWR('f', 126, OBD_IOC_DATA_TYPE)
+#define OBD_IOC_NAME2DEV _IOWR('f', 127, OBD_IOC_DATA_TYPE)
+#define OBD_IOC_UUID2DEV _IOWR('f', 130, OBD_IOC_DATA_TYPE)
+
+#define OBD_IOC_GETNAME _IOWR('f', 131, OBD_IOC_DATA_TYPE)
+#define OBD_IOC_GETMDNAME _IOR('f', 131, char[MAX_OBD_NAME])
+#define OBD_IOC_GETDTNAME OBD_IOC_GETNAME
+
+#define OBD_IOC_LOV_GET_CONFIG _IOWR('f', 132, OBD_IOC_DATA_TYPE)
+#define OBD_IOC_CLIENT_RECOVER _IOW ('f', 133, OBD_IOC_DATA_TYPE)
+#define OBD_IOC_PING_TARGET _IOW ('f', 136, OBD_IOC_DATA_TYPE)
+
+#define OBD_IOC_DEC_FS_USE_COUNT _IO ('f', 139 )
+#define OBD_IOC_NO_TRANSNO _IOW ('f', 140, OBD_IOC_DATA_TYPE)
+#define OBD_IOC_SET_READONLY _IOW ('f', 141, OBD_IOC_DATA_TYPE)
+#define OBD_IOC_ABORT_RECOVERY _IOR ('f', 142, OBD_IOC_DATA_TYPE)
+
+#define OBD_IOC_ROOT_SQUASH _IOWR('f', 143, OBD_IOC_DATA_TYPE)
+
+#define OBD_GET_VERSION _IOWR ('f', 144, OBD_IOC_DATA_TYPE)
+
+#define OBD_IOC_GSS_SUPPORT _IOWR('f', 145, OBD_IOC_DATA_TYPE)
+
+#define OBD_IOC_CLOSE_UUID _IOWR ('f', 147, OBD_IOC_DATA_TYPE)
+
+#define OBD_IOC_CHANGELOG_SEND _IOW ('f', 148, OBD_IOC_DATA_TYPE)
+#define OBD_IOC_GETDEVICE _IOWR ('f', 149, OBD_IOC_DATA_TYPE)
+#define OBD_IOC_FID2PATH _IOWR ('f', 150, OBD_IOC_DATA_TYPE)
+/* see also <lustre/lustre_user.h> for ioctls 151-153 */
+/* OBD_IOC_LOV_SETSTRIPE: See also LL_IOC_LOV_SETSTRIPE */
+#define OBD_IOC_LOV_SETSTRIPE _IOW ('f', 154, OBD_IOC_DATA_TYPE)
+/* OBD_IOC_LOV_GETSTRIPE: See also LL_IOC_LOV_GETSTRIPE */
+#define OBD_IOC_LOV_GETSTRIPE _IOW ('f', 155, OBD_IOC_DATA_TYPE)
+/* OBD_IOC_LOV_SETEA: See also LL_IOC_LOV_SETEA */
+#define OBD_IOC_LOV_SETEA _IOW ('f', 156, OBD_IOC_DATA_TYPE)
+/* see <lustre/lustre_user.h> for ioctls 157-159 */
+/* OBD_IOC_QUOTACHECK: See also LL_IOC_QUOTACHECK */
+#define OBD_IOC_QUOTACHECK _IOW ('f', 160, int)
+/* OBD_IOC_POLL_QUOTACHECK: See also LL_IOC_POLL_QUOTACHECK */
+#define OBD_IOC_POLL_QUOTACHECK _IOR ('f', 161, struct if_quotacheck *)
+/* OBD_IOC_QUOTACTL: See also LL_IOC_QUOTACTL */
+#define OBD_IOC_QUOTACTL _IOWR('f', 162, struct if_quotactl)
+/* see also <lustre/lustre_user.h> for ioctls 163-176 */
+#define OBD_IOC_CHANGELOG_REG _IOW ('f', 177, struct obd_ioctl_data)
+#define OBD_IOC_CHANGELOG_DEREG _IOW ('f', 178, struct obd_ioctl_data)
+#define OBD_IOC_CHANGELOG_CLEAR _IOW ('f', 179, struct obd_ioctl_data)
+#define OBD_IOC_RECORD _IOWR('f', 180, OBD_IOC_DATA_TYPE)
+#define OBD_IOC_ENDRECORD _IOWR('f', 181, OBD_IOC_DATA_TYPE)
+#define OBD_IOC_PARSE _IOWR('f', 182, OBD_IOC_DATA_TYPE)
+#define OBD_IOC_DORECORD _IOWR('f', 183, OBD_IOC_DATA_TYPE)
+#define OBD_IOC_PROCESS_CFG _IOWR('f', 184, OBD_IOC_DATA_TYPE)
+#define OBD_IOC_DUMP_LOG _IOWR('f', 185, OBD_IOC_DATA_TYPE)
+#define OBD_IOC_CLEAR_LOG _IOWR('f', 186, OBD_IOC_DATA_TYPE)
+#define OBD_IOC_PARAM _IOW ('f', 187, OBD_IOC_DATA_TYPE)
+#define OBD_IOC_POOL _IOWR('f', 188, OBD_IOC_DATA_TYPE)
+#define OBD_IOC_REPLACE_NIDS _IOWR('f', 189, OBD_IOC_DATA_TYPE)
+
+#define OBD_IOC_CATLOGLIST _IOWR('f', 190, OBD_IOC_DATA_TYPE)
+#define OBD_IOC_LLOG_INFO _IOWR('f', 191, OBD_IOC_DATA_TYPE)
+#define OBD_IOC_LLOG_PRINT _IOWR('f', 192, OBD_IOC_DATA_TYPE)
+#define OBD_IOC_LLOG_CANCEL _IOWR('f', 193, OBD_IOC_DATA_TYPE)
+#define OBD_IOC_LLOG_REMOVE _IOWR('f', 194, OBD_IOC_DATA_TYPE)
+#define OBD_IOC_LLOG_CHECK _IOWR('f', 195, OBD_IOC_DATA_TYPE)
+/* OBD_IOC_LLOG_CATINFO is deprecated */
+#define OBD_IOC_LLOG_CATINFO _IOWR('f', 196, OBD_IOC_DATA_TYPE)
+
+#define ECHO_IOC_GET_STRIPE _IOWR('f', 200, OBD_IOC_DATA_TYPE)
+#define ECHO_IOC_SET_STRIPE _IOWR('f', 201, OBD_IOC_DATA_TYPE)
+#define ECHO_IOC_ENQUEUE _IOWR('f', 202, OBD_IOC_DATA_TYPE)
+#define ECHO_IOC_CANCEL _IOWR('f', 203, OBD_IOC_DATA_TYPE)
+
+#define OBD_IOC_GET_OBJ_VERSION _IOR('f', 210, OBD_IOC_DATA_TYPE)
+
+/* <lustre/lustre_user.h> defines ioctl number 218-219 */
+#define OBD_IOC_GET_MNTOPT _IOW('f', 220, mntopt_t)
+
+#define OBD_IOC_ECHO_MD _IOR('f', 221, struct obd_ioctl_data)
+#define OBD_IOC_ECHO_ALLOC_SEQ _IOWR('f', 222, struct obd_ioctl_data)
+
+#define OBD_IOC_START_LFSCK _IOWR('f', 230, OBD_IOC_DATA_TYPE)
+#define OBD_IOC_STOP_LFSCK _IOW('f', 231, OBD_IOC_DATA_TYPE)
+#define OBD_IOC_PAUSE_LFSCK _IOW('f', 232, OBD_IOC_DATA_TYPE)
+
+/* XXX _IOWR('f', 250, long) has been defined in
+ * libcfs/include/libcfs/libcfs_private.h for debug, don't use it
+ */
+
+/* Until such time as we get_info the per-stripe maximum from the OST,
+ * we define this to be 2T - 4k, which is the ext3 maxbytes. */
+#define LUSTRE_STRIPE_MAXBYTES 0x1fffffff000ULL
+
+/* Special values for remove LOV EA from disk */
+#define LOVEA_DELETE_VALUES(size, count, offset) (size == 0 && count == 0 && \
+ offset == (typeof(offset))(-1))
+
+/* #define POISON_BULK 0 */
+
+/*
+ * l_wait_event is a flexible sleeping function, permitting simple caller
+ * configuration of interrupt and timeout sensitivity along with actions to
+ * be performed in the event of either exception.
+ *
+ * The first form of usage looks like this:
+ *
+ * struct l_wait_info lwi = LWI_TIMEOUT_INTR(timeout, timeout_handler,
+ * intr_handler, callback_data);
+ * rc = l_wait_event(waitq, condition, &lwi);
+ *
+ * l_wait_event() makes the current process wait on 'waitq' until 'condition'
+ * is TRUE or a "killable" signal (SIGTERM, SIKGILL, SIGINT) is pending. It
+ * returns 0 to signify 'condition' is TRUE, but if a signal wakes it before
+ * 'condition' becomes true, it optionally calls the specified 'intr_handler'
+ * if not NULL, and returns -EINTR.
+ *
+ * If a non-zero timeout is specified, signals are ignored until the timeout
+ * has expired. At this time, if 'timeout_handler' is not NULL it is called.
+ * If it returns FALSE l_wait_event() continues to wait as described above with
+ * signals enabled. Otherwise it returns -ETIMEDOUT.
+ *
+ * LWI_INTR(intr_handler, callback_data) is shorthand for
+ * LWI_TIMEOUT_INTR(0, NULL, intr_handler, callback_data)
+ *
+ * The second form of usage looks like this:
+ *
+ * struct l_wait_info lwi = LWI_TIMEOUT(timeout, timeout_handler);
+ * rc = l_wait_event(waitq, condition, &lwi);
+ *
+ * This form is the same as the first except that it COMPLETELY IGNORES
+ * SIGNALS. The caller must therefore beware that if 'timeout' is zero, or if
+ * 'timeout_handler' is not NULL and returns FALSE, then the ONLY thing that
+ * can unblock the current process is 'condition' becoming TRUE.
+ *
+ * Another form of usage is:
+ * struct l_wait_info lwi = LWI_TIMEOUT_INTERVAL(timeout, interval,
+ * timeout_handler);
+ * rc = l_wait_event(waitq, condition, &lwi);
+ * This is the same as previous case, but condition is checked once every
+ * 'interval' jiffies (if non-zero).
+ *
+ * Subtle synchronization point: this macro does *not* necessary takes
+ * wait-queue spin-lock before returning, and, hence, following idiom is safe
+ * ONLY when caller provides some external locking:
+ *
+ * Thread1 Thread2
+ *
+ * l_wait_event(&obj->wq, ....); (1)
+ *
+ * wake_up(&obj->wq): (2)
+ * spin_lock(&q->lock); (2.1)
+ * __wake_up_common(q, ...); (2.2)
+ * spin_unlock(&q->lock, flags); (2.3)
+ *
+ * OBD_FREE_PTR(obj); (3)
+ *
+ * As l_wait_event() may "short-cut" execution and return without taking
+ * wait-queue spin-lock, some additional synchronization is necessary to
+ * guarantee that step (3) can begin only after (2.3) finishes.
+ *
+ * XXX nikita: some ptlrpc daemon threads have races of that sort.
+ *
+ */
+static inline int back_to_sleep(void *arg)
+{
+ return 0;
+}
+
+#define LWI_ON_SIGNAL_NOOP ((void (*)(void *))(-1))
+
+struct l_wait_info {
+ long lwi_timeout;
+ long lwi_interval;
+ int lwi_allow_intr;
+ int (*lwi_on_timeout)(void *);
+ void (*lwi_on_signal)(void *);
+ void *lwi_cb_data;
+};
+
+/* NB: LWI_TIMEOUT ignores signals completely */
+#define LWI_TIMEOUT(time, cb, data) \
+((struct l_wait_info) { \
+ .lwi_timeout = time, \
+ .lwi_on_timeout = cb, \
+ .lwi_cb_data = data, \
+ .lwi_interval = 0, \
+ .lwi_allow_intr = 0 \
+})
+
+#define LWI_TIMEOUT_INTERVAL(time, interval, cb, data) \
+((struct l_wait_info) { \
+ .lwi_timeout = time, \
+ .lwi_on_timeout = cb, \
+ .lwi_cb_data = data, \
+ .lwi_interval = interval, \
+ .lwi_allow_intr = 0 \
+})
+
+#define LWI_TIMEOUT_INTR(time, time_cb, sig_cb, data) \
+((struct l_wait_info) { \
+ .lwi_timeout = time, \
+ .lwi_on_timeout = time_cb, \
+ .lwi_on_signal = sig_cb, \
+ .lwi_cb_data = data, \
+ .lwi_interval = 0, \
+ .lwi_allow_intr = 0 \
+})
+
+#define LWI_TIMEOUT_INTR_ALL(time, time_cb, sig_cb, data) \
+((struct l_wait_info) { \
+ .lwi_timeout = time, \
+ .lwi_on_timeout = time_cb, \
+ .lwi_on_signal = sig_cb, \
+ .lwi_cb_data = data, \
+ .lwi_interval = 0, \
+ .lwi_allow_intr = 1 \
+})
+
+#define LWI_INTR(cb, data) LWI_TIMEOUT_INTR(0, NULL, cb, data)
+
+#define LUSTRE_FATAL_SIGS (sigmask(SIGKILL) | sigmask(SIGINT) | \
+ sigmask(SIGTERM) | sigmask(SIGQUIT) | \
+ sigmask(SIGALRM))
+
+
+/*
+ * wait for @condition to become true, but no longer than timeout, specified
+ * by @info.
+ */
+#define __l_wait_event(wq, condition, info, ret, l_add_wait) \
+do { \
+ wait_queue_t __wait; \
+ long __timeout = info->lwi_timeout; \
+ sigset_t __blocked; \
+ int __allow_intr = info->lwi_allow_intr; \
+ \
+ ret = 0; \
+ if (condition) \
+ break; \
+ \
+ init_waitqueue_entry(&__wait, current); \
+ l_add_wait(&wq, &__wait); \
+ \
+ /* Block all signals (just the non-fatal ones if no timeout). */ \
+ if (info->lwi_on_signal != NULL && (__timeout == 0 || __allow_intr)) \
+ __blocked = cfs_block_sigsinv(LUSTRE_FATAL_SIGS); \
+ else \
+ __blocked = cfs_block_sigsinv(0); \
+ \
+ for (;;) { \
+ unsigned __wstate; \
+ \
+ __wstate = info->lwi_on_signal != NULL && \
+ (__timeout == 0 || __allow_intr) ? \
+ TASK_INTERRUPTIBLE : TASK_UNINTERRUPTIBLE; \
+ \
+ set_current_state(TASK_INTERRUPTIBLE); \
+ \
+ if (condition) \
+ break; \
+ \
+ if (__timeout == 0) { \
+ schedule(); \
+ } else { \
+ long interval = info->lwi_interval? \
+ min_t(long, \
+ info->lwi_interval,__timeout):\
+ __timeout; \
+ long remaining = schedule_timeout(interval);\
+ __timeout = cfs_time_sub(__timeout, \
+ cfs_time_sub(interval, remaining));\
+ if (__timeout == 0) { \
+ if (info->lwi_on_timeout == NULL || \
+ info->lwi_on_timeout(info->lwi_cb_data)) { \
+ ret = -ETIMEDOUT; \
+ break; \
+ } \
+ /* Take signals after the timeout expires. */ \
+ if (info->lwi_on_signal != NULL) \
+ (void)cfs_block_sigsinv(LUSTRE_FATAL_SIGS);\
+ } \
+ } \
+ \
+ if (condition) \
+ break; \
+ if (cfs_signal_pending()) { \
+ if (info->lwi_on_signal != NULL && \
+ (__timeout == 0 || __allow_intr)) { \
+ if (info->lwi_on_signal != LWI_ON_SIGNAL_NOOP) \
+ info->lwi_on_signal(info->lwi_cb_data);\
+ ret = -EINTR; \
+ break; \
+ } \
+ /* We have to do this here because some signals */ \
+ /* are not blockable - ie from strace(1). */ \
+ /* In these cases we want to schedule_timeout() */ \
+ /* again, because we don't want that to return */ \
+ /* -EINTR when the RPC actually succeeded. */ \
+ /* the recalc_sigpending() below will deliver the */ \
+ /* signal properly. */ \
+ cfs_clear_sigpending(); \
+ } \
+ } \
+ \
+ cfs_restore_sigs(__blocked); \
+ \
+ set_current_state(TASK_RUNNING); \
+ remove_wait_queue(&wq, &__wait); \
+} while (0)
+
+
+
+#define l_wait_event(wq, condition, info) \
+({ \
+ int __ret; \
+ struct l_wait_info *__info = (info); \
+ \
+ __l_wait_event(wq, condition, __info, \
+ __ret, add_wait_queue); \
+ __ret; \
+})
+
+#define l_wait_event_exclusive(wq, condition, info) \
+({ \
+ int __ret; \
+ struct l_wait_info *__info = (info); \
+ \
+ __l_wait_event(wq, condition, __info, \
+ __ret, add_wait_queue_exclusive); \
+ __ret; \
+})
+
+#define l_wait_event_exclusive_head(wq, condition, info) \
+({ \
+ int __ret; \
+ struct l_wait_info *__info = (info); \
+ \
+ __l_wait_event(wq, condition, __info, \
+ __ret, add_wait_queue_exclusive_head); \
+ __ret; \
+})
+
+#define l_wait_condition(wq, condition) \
+({ \
+ struct l_wait_info lwi = { 0 }; \
+ l_wait_event(wq, condition, &lwi); \
+})
+
+#define l_wait_condition_exclusive(wq, condition) \
+({ \
+ struct l_wait_info lwi = { 0 }; \
+ l_wait_event_exclusive(wq, condition, &lwi); \
+})
+
+#define l_wait_condition_exclusive_head(wq, condition) \
+({ \
+ struct l_wait_info lwi = { 0 }; \
+ l_wait_event_exclusive_head(wq, condition, &lwi); \
+})
+
+#define LIBLUSTRE_CLIENT (0)
+
+/** @} lib */
+
+#endif /* _LUSTRE_LIB_H */
diff --git a/drivers/staging/lustre/lustre/include/lustre_lite.h b/drivers/staging/lustre/lustre/include/lustre_lite.h
new file mode 100644
index 000000000..df557c22a
--- /dev/null
+++ b/drivers/staging/lustre/lustre/include/lustre_lite.h
@@ -0,0 +1,150 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2011, 2012, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ */
+
+#ifndef _LL_H
+#define _LL_H
+
+/** \defgroup lite lite
+ *
+ * @{
+ */
+
+#include "linux/lustre_lite.h"
+
+#include "obd_class.h"
+#include "lustre_net.h"
+#include "lustre_mds.h"
+#include "lustre_ha.h"
+
+/* 4UL * 1024 * 1024 */
+#define LL_MAX_BLKSIZE_BITS (22)
+#define LL_MAX_BLKSIZE (1UL<<LL_MAX_BLKSIZE_BITS)
+
+#include "lustre/lustre_user.h"
+
+
+struct lustre_rw_params {
+ int lrp_lock_mode;
+ ldlm_policy_data_t lrp_policy;
+ u32 lrp_brw_flags;
+ int lrp_ast_flags;
+};
+
+/*
+ * XXX nikita: this function lives in the header because it is used by both
+ * llite kernel module and liblustre library, and there is no (?) better place
+ * to put it in.
+ */
+static inline void lustre_build_lock_params(int cmd, unsigned long open_flags,
+ __u64 connect_flags,
+ loff_t pos, ssize_t len,
+ struct lustre_rw_params *params)
+{
+ params->lrp_lock_mode = (cmd == OBD_BRW_READ) ? LCK_PR : LCK_PW;
+ params->lrp_brw_flags = 0;
+
+ params->lrp_policy.l_extent.start = pos;
+ params->lrp_policy.l_extent.end = pos + len - 1;
+ /*
+ * for now O_APPEND always takes local locks.
+ */
+ if (cmd == OBD_BRW_WRITE && (open_flags & O_APPEND)) {
+ params->lrp_policy.l_extent.start = 0;
+ params->lrp_policy.l_extent.end = OBD_OBJECT_EOF;
+ } else if (LIBLUSTRE_CLIENT && (connect_flags & OBD_CONNECT_SRVLOCK)) {
+ /*
+ * liblustre: OST-side locking for all non-O_APPEND
+ * reads/writes.
+ */
+ params->lrp_lock_mode = LCK_NL;
+ params->lrp_brw_flags = OBD_BRW_SRVLOCK;
+ } else {
+ /*
+ * nothing special for the kernel. In the future llite may use
+ * OST-side locks for small writes into highly contended
+ * files.
+ */
+ }
+ params->lrp_ast_flags = (open_flags & O_NONBLOCK) ?
+ LDLM_FL_BLOCK_NOWAIT : 0;
+}
+
+/*
+ * This is embedded into liblustre and llite super-blocks to keep track of
+ * connect flags (capabilities) supported by all imports given mount is
+ * connected to.
+ */
+struct lustre_client_ocd {
+ /*
+ * This is conjunction of connect_flags across all imports (LOVs) this
+ * mount is connected to. This field is updated by cl_ocd_update()
+ * under ->lco_lock.
+ */
+ __u64 lco_flags;
+ struct mutex lco_lock;
+ struct obd_export *lco_md_exp;
+ struct obd_export *lco_dt_exp;
+};
+
+/*
+ * Chain of hash overflow pages.
+ */
+struct ll_dir_chain {
+ /* XXX something. Later */
+};
+
+static inline void ll_dir_chain_init(struct ll_dir_chain *chain)
+{
+}
+
+static inline void ll_dir_chain_fini(struct ll_dir_chain *chain)
+{
+}
+
+static inline unsigned long hash_x_index(__u64 hash, int hash64)
+{
+ if (BITS_PER_LONG == 32 && hash64)
+ hash >>= 32;
+ /* save hash 0 as index 0 because otherwise we'll save it at
+ * page index end (~0UL) and it causes truncate_inode_pages_range()
+ * to loop forever.
+ */
+ return ~0UL - (hash + !hash);
+}
+
+/** @} lite */
+
+#endif
diff --git a/drivers/staging/lustre/lustre/include/lustre_log.h b/drivers/staging/lustre/lustre/include/lustre_log.h
new file mode 100644
index 000000000..2187fb615
--- /dev/null
+++ b/drivers/staging/lustre/lustre/include/lustre_log.h
@@ -0,0 +1,545 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2012, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ *
+ * lustre/include/lustre_log.h
+ *
+ * Generic infrastructure for managing a collection of logs.
+ * These logs are used for:
+ *
+ * - orphan recovery: OST adds record on create
+ * - mtime/size consistency: the OST adds a record on first write
+ * - open/unlinked objects: OST adds a record on destroy
+ *
+ * - mds unlink log: the MDS adds an entry upon delete
+ *
+ * - raid1 replication log between OST's
+ * - MDS replication logs
+ */
+
+#ifndef _LUSTRE_LOG_H
+#define _LUSTRE_LOG_H
+
+/** \defgroup log log
+ *
+ * @{
+ */
+
+#include "obd_class.h"
+#include "lustre/lustre_idl.h"
+#include "dt_object.h"
+
+#define LOG_NAME_LIMIT(logname, name) \
+ snprintf(logname, sizeof(logname), "LOGS/%s", name)
+#define LLOG_EEMPTY 4711
+
+enum llog_open_param {
+ LLOG_OPEN_EXISTS = 0x0000,
+ LLOG_OPEN_NEW = 0x0001,
+};
+
+struct plain_handle_data {
+ struct list_head phd_entry;
+ struct llog_handle *phd_cat_handle;
+ struct llog_cookie phd_cookie; /* cookie of this log in its cat */
+};
+
+struct cat_handle_data {
+ struct list_head chd_head;
+ struct llog_handle *chd_current_log; /* currently open log */
+ struct llog_handle *chd_next_log; /* llog to be used next */
+};
+
+static inline void logid_to_fid(struct llog_logid *id, struct lu_fid *fid)
+{
+ /* For compatibility purposes we identify pre-OSD (~< 2.3.51 MDS)
+ * logid's by non-zero ogen (inode generation) and convert them
+ * into IGIF */
+ if (id->lgl_ogen == 0) {
+ fid->f_seq = id->lgl_oi.oi.oi_seq;
+ fid->f_oid = id->lgl_oi.oi.oi_id;
+ fid->f_ver = 0;
+ } else {
+ lu_igif_build(fid, id->lgl_oi.oi.oi_id, id->lgl_ogen);
+ }
+}
+
+static inline void fid_to_logid(struct lu_fid *fid, struct llog_logid *id)
+{
+ id->lgl_oi.oi.oi_seq = fid->f_seq;
+ id->lgl_oi.oi.oi_id = fid->f_oid;
+ id->lgl_ogen = 0;
+}
+
+static inline void logid_set_id(struct llog_logid *log_id, __u64 id)
+{
+ log_id->lgl_oi.oi.oi_id = id;
+}
+
+static inline __u64 logid_id(struct llog_logid *log_id)
+{
+ return log_id->lgl_oi.oi.oi_id;
+}
+
+struct llog_handle;
+
+/* llog.c - general API */
+int llog_init_handle(const struct lu_env *env, struct llog_handle *handle,
+ int flags, struct obd_uuid *uuid);
+int llog_copy_handler(const struct lu_env *env, struct llog_handle *llh,
+ struct llog_rec_hdr *rec, void *data);
+int llog_process(const struct lu_env *env, struct llog_handle *loghandle,
+ llog_cb_t cb, void *data, void *catdata);
+int llog_process_or_fork(const struct lu_env *env,
+ struct llog_handle *loghandle,
+ llog_cb_t cb, void *data, void *catdata, bool fork);
+int llog_reverse_process(const struct lu_env *env,
+ struct llog_handle *loghandle, llog_cb_t cb,
+ void *data, void *catdata);
+int llog_cancel_rec(const struct lu_env *env, struct llog_handle *loghandle,
+ int index);
+int llog_open(const struct lu_env *env, struct llog_ctxt *ctxt,
+ struct llog_handle **lgh, struct llog_logid *logid,
+ char *name, enum llog_open_param open_param);
+int llog_close(const struct lu_env *env, struct llog_handle *cathandle);
+int llog_is_empty(const struct lu_env *env, struct llog_ctxt *ctxt,
+ char *name);
+int llog_backup(const struct lu_env *env, struct obd_device *obd,
+ struct llog_ctxt *ctxt, struct llog_ctxt *bak_ctxt,
+ char *name, char *backup);
+
+/* llog_process flags */
+#define LLOG_FLAG_NODEAMON 0x0001
+
+/* llog_cat.c - catalog api */
+struct llog_process_data {
+ /**
+ * Any useful data needed while processing catalog. This is
+ * passed later to process callback.
+ */
+ void *lpd_data;
+ /**
+ * Catalog process callback function, called for each record
+ * in catalog.
+ */
+ llog_cb_t lpd_cb;
+ /**
+ * Start processing the catalog from startcat/startidx
+ */
+ int lpd_startcat;
+ int lpd_startidx;
+};
+
+struct llog_process_cat_data {
+ /**
+ * Temporary stored first_idx while scanning log.
+ */
+ int lpcd_first_idx;
+ /**
+ * Temporary stored last_idx while scanning log.
+ */
+ int lpcd_last_idx;
+};
+
+int llog_cat_close(const struct lu_env *env, struct llog_handle *cathandle);
+int llog_cat_add_rec(const struct lu_env *env, struct llog_handle *cathandle,
+ struct llog_rec_hdr *rec, struct llog_cookie *reccookie,
+ void *buf, struct thandle *th);
+int llog_cat_declare_add_rec(const struct lu_env *env,
+ struct llog_handle *cathandle,
+ struct llog_rec_hdr *rec, struct thandle *th);
+int llog_cat_add(const struct lu_env *env, struct llog_handle *cathandle,
+ struct llog_rec_hdr *rec, struct llog_cookie *reccookie,
+ void *buf);
+int llog_cat_cancel_records(const struct lu_env *env,
+ struct llog_handle *cathandle, int count,
+ struct llog_cookie *cookies);
+int llog_cat_process_or_fork(const struct lu_env *env,
+ struct llog_handle *cat_llh, llog_cb_t cb,
+ void *data, int startcat, int startidx, bool fork);
+int llog_cat_process(const struct lu_env *env, struct llog_handle *cat_llh,
+ llog_cb_t cb, void *data, int startcat, int startidx);
+int llog_cat_reverse_process(const struct lu_env *env,
+ struct llog_handle *cat_llh, llog_cb_t cb,
+ void *data);
+int llog_cat_init_and_process(const struct lu_env *env,
+ struct llog_handle *llh);
+
+/* llog_obd.c */
+int llog_setup(const struct lu_env *env, struct obd_device *obd,
+ struct obd_llog_group *olg, int index,
+ struct obd_device *disk_obd, struct llog_operations *op);
+int __llog_ctxt_put(const struct lu_env *env, struct llog_ctxt *ctxt);
+int llog_cleanup(const struct lu_env *env, struct llog_ctxt *);
+int llog_sync(struct llog_ctxt *ctxt, struct obd_export *exp, int flags);
+int llog_cancel(const struct lu_env *env, struct llog_ctxt *ctxt,
+ struct llog_cookie *cookies, int flags);
+
+/* llog_net.c */
+int llog_initiator_connect(struct llog_ctxt *ctxt);
+
+struct llog_operations {
+ int (*lop_destroy)(const struct lu_env *env,
+ struct llog_handle *handle);
+ int (*lop_next_block)(const struct lu_env *env, struct llog_handle *h,
+ int *curr_idx, int next_idx, __u64 *offset,
+ void *buf, int len);
+ int (*lop_prev_block)(const struct lu_env *env, struct llog_handle *h,
+ int prev_idx, void *buf, int len);
+ int (*lop_read_header)(const struct lu_env *env,
+ struct llog_handle *handle);
+ int (*lop_setup)(const struct lu_env *env, struct obd_device *obd,
+ struct obd_llog_group *olg, int ctxt_idx,
+ struct obd_device *disk_obd);
+ int (*lop_sync)(struct llog_ctxt *ctxt, struct obd_export *exp,
+ int flags);
+ int (*lop_cleanup)(const struct lu_env *env, struct llog_ctxt *ctxt);
+ int (*lop_cancel)(const struct lu_env *env, struct llog_ctxt *ctxt,
+ struct llog_cookie *cookies, int flags);
+ int (*lop_connect)(struct llog_ctxt *ctxt, struct llog_logid *logid,
+ struct llog_gen *gen, struct obd_uuid *uuid);
+ /**
+ * Any llog file must be opened first using llog_open(). Llog can be
+ * opened by name, logid or without both, in last case the new logid
+ * will be generated.
+ */
+ int (*lop_open)(const struct lu_env *env, struct llog_handle *lgh,
+ struct llog_logid *logid, char *name,
+ enum llog_open_param);
+ /**
+ * Opened llog may not exist and this must be checked where needed using
+ * the llog_exist() call.
+ */
+ int (*lop_exist)(struct llog_handle *lgh);
+ /**
+ * Close llog file and calls llog_free_handle() implicitly.
+ * Any opened llog must be closed by llog_close() call.
+ */
+ int (*lop_close)(const struct lu_env *env, struct llog_handle *handle);
+ /**
+ * Create new llog file. The llog must be opened.
+ * Must be used only for local llog operations.
+ */
+ int (*lop_declare_create)(const struct lu_env *env,
+ struct llog_handle *handle,
+ struct thandle *th);
+ int (*lop_create)(const struct lu_env *env, struct llog_handle *handle,
+ struct thandle *th);
+ /**
+ * write new record in llog. It appends records usually but can edit
+ * existing records too.
+ */
+ int (*lop_declare_write_rec)(const struct lu_env *env,
+ struct llog_handle *lgh,
+ struct llog_rec_hdr *rec,
+ int idx, struct thandle *th);
+ int (*lop_write_rec)(const struct lu_env *env,
+ struct llog_handle *loghandle,
+ struct llog_rec_hdr *rec,
+ struct llog_cookie *cookie, int cookiecount,
+ void *buf, int idx, struct thandle *th);
+ /**
+ * Add new record in llog catalog. Does the same as llog_write_rec()
+ * but using llog catalog.
+ */
+ int (*lop_declare_add)(const struct lu_env *env,
+ struct llog_handle *lgh,
+ struct llog_rec_hdr *rec, struct thandle *th);
+ int (*lop_add)(const struct lu_env *env, struct llog_handle *lgh,
+ struct llog_rec_hdr *rec, struct llog_cookie *cookie,
+ void *buf, struct thandle *th);
+};
+
+/* In-memory descriptor for a log object or log catalog */
+struct llog_handle {
+ struct rw_semaphore lgh_lock;
+ spinlock_t lgh_hdr_lock; /* protect lgh_hdr data */
+ struct llog_logid lgh_id; /* id of this log */
+ struct llog_log_hdr *lgh_hdr;
+ struct file *lgh_file;
+ struct dt_object *lgh_obj;
+ int lgh_last_idx;
+ int lgh_cur_idx; /* used during llog_process */
+ __u64 lgh_cur_offset; /* used during llog_process */
+ struct llog_ctxt *lgh_ctxt;
+ union {
+ struct plain_handle_data phd;
+ struct cat_handle_data chd;
+ } u;
+ char *lgh_name;
+ void *private_data;
+ struct llog_operations *lgh_logops;
+ atomic_t lgh_refcount;
+};
+
+#define LLOG_CTXT_FLAG_UNINITIALIZED 0x00000001
+#define LLOG_CTXT_FLAG_STOP 0x00000002
+
+struct llog_ctxt {
+ int loc_idx; /* my index the obd array of ctxt's */
+ struct obd_device *loc_obd; /* points back to the containing obd*/
+ struct obd_llog_group *loc_olg; /* group containing that ctxt */
+ struct obd_export *loc_exp; /* parent "disk" export (e.g. MDS) */
+ struct obd_import *loc_imp; /* to use in RPC's: can be backward
+ pointing import */
+ struct llog_operations *loc_logops;
+ struct llog_handle *loc_handle;
+ struct mutex loc_mutex; /* protect loc_imp */
+ atomic_t loc_refcount;
+ long loc_flags; /* flags, see above defines */
+ struct dt_object *loc_dir;
+};
+
+#define LLOG_PROC_BREAK 0x0001
+#define LLOG_DEL_RECORD 0x0002
+
+static inline int llog_obd2ops(struct llog_ctxt *ctxt,
+ struct llog_operations **lop)
+{
+ if (ctxt == NULL)
+ return -ENOTCONN;
+
+ *lop = ctxt->loc_logops;
+ if (*lop == NULL)
+ return -EOPNOTSUPP;
+
+ return 0;
+}
+
+static inline int llog_handle2ops(struct llog_handle *loghandle,
+ struct llog_operations **lop)
+{
+ if (loghandle == NULL || loghandle->lgh_logops == NULL)
+ return -EINVAL;
+
+ *lop = loghandle->lgh_logops;
+ return 0;
+}
+
+static inline int llog_data_len(int len)
+{
+ return cfs_size_round(len);
+}
+
+static inline int llog_get_size(struct llog_handle *loghandle)
+{
+ if (loghandle && loghandle->lgh_hdr)
+ return loghandle->lgh_hdr->llh_count;
+ return 0;
+}
+
+static inline struct llog_ctxt *llog_ctxt_get(struct llog_ctxt *ctxt)
+{
+ atomic_inc(&ctxt->loc_refcount);
+ CDEBUG(D_INFO, "GETting ctxt %p : new refcount %d\n", ctxt,
+ atomic_read(&ctxt->loc_refcount));
+ return ctxt;
+}
+
+static inline void llog_ctxt_put(struct llog_ctxt *ctxt)
+{
+ if (ctxt == NULL)
+ return;
+ LASSERT_ATOMIC_GT_LT(&ctxt->loc_refcount, 0, LI_POISON);
+ CDEBUG(D_INFO, "PUTting ctxt %p : new refcount %d\n", ctxt,
+ atomic_read(&ctxt->loc_refcount) - 1);
+ __llog_ctxt_put(NULL, ctxt);
+}
+
+static inline void llog_group_init(struct obd_llog_group *olg, int group)
+{
+ init_waitqueue_head(&olg->olg_waitq);
+ spin_lock_init(&olg->olg_lock);
+ mutex_init(&olg->olg_cat_processing);
+ olg->olg_seq = group;
+}
+
+static inline int llog_group_set_ctxt(struct obd_llog_group *olg,
+ struct llog_ctxt *ctxt, int index)
+{
+ LASSERT(index >= 0 && index < LLOG_MAX_CTXTS);
+
+ spin_lock(&olg->olg_lock);
+ if (olg->olg_ctxts[index] != NULL) {
+ spin_unlock(&olg->olg_lock);
+ return -EEXIST;
+ }
+ olg->olg_ctxts[index] = ctxt;
+ spin_unlock(&olg->olg_lock);
+ return 0;
+}
+
+static inline struct llog_ctxt *llog_group_get_ctxt(struct obd_llog_group *olg,
+ int index)
+{
+ struct llog_ctxt *ctxt;
+
+ LASSERT(index >= 0 && index < LLOG_MAX_CTXTS);
+
+ spin_lock(&olg->olg_lock);
+ if (olg->olg_ctxts[index] == NULL)
+ ctxt = NULL;
+ else
+ ctxt = llog_ctxt_get(olg->olg_ctxts[index]);
+ spin_unlock(&olg->olg_lock);
+ return ctxt;
+}
+
+static inline void llog_group_clear_ctxt(struct obd_llog_group *olg, int index)
+{
+ LASSERT(index >= 0 && index < LLOG_MAX_CTXTS);
+ spin_lock(&olg->olg_lock);
+ olg->olg_ctxts[index] = NULL;
+ spin_unlock(&olg->olg_lock);
+}
+
+static inline struct llog_ctxt *llog_get_context(struct obd_device *obd,
+ int index)
+{
+ return llog_group_get_ctxt(&obd->obd_olg, index);
+}
+
+static inline int llog_group_ctxt_null(struct obd_llog_group *olg, int index)
+{
+ return (olg->olg_ctxts[index] == NULL);
+}
+
+static inline int llog_ctxt_null(struct obd_device *obd, int index)
+{
+ return llog_group_ctxt_null(&obd->obd_olg, index);
+}
+
+static inline int llog_destroy(const struct lu_env *env,
+ struct llog_handle *handle)
+{
+ struct llog_operations *lop;
+ int rc;
+
+ rc = llog_handle2ops(handle, &lop);
+ if (rc)
+ return rc;
+ if (lop->lop_destroy == NULL)
+ return -EOPNOTSUPP;
+
+ rc = lop->lop_destroy(env, handle);
+ return rc;
+}
+
+static inline int llog_next_block(const struct lu_env *env,
+ struct llog_handle *loghandle, int *cur_idx,
+ int next_idx, __u64 *cur_offset, void *buf,
+ int len)
+{
+ struct llog_operations *lop;
+ int rc;
+
+ rc = llog_handle2ops(loghandle, &lop);
+ if (rc)
+ return rc;
+ if (lop->lop_next_block == NULL)
+ return -EOPNOTSUPP;
+
+ rc = lop->lop_next_block(env, loghandle, cur_idx, next_idx,
+ cur_offset, buf, len);
+ return rc;
+}
+
+static inline int llog_prev_block(const struct lu_env *env,
+ struct llog_handle *loghandle,
+ int prev_idx, void *buf, int len)
+{
+ struct llog_operations *lop;
+ int rc;
+
+ rc = llog_handle2ops(loghandle, &lop);
+ if (rc)
+ return rc;
+ if (lop->lop_prev_block == NULL)
+ return -EOPNOTSUPP;
+
+ rc = lop->lop_prev_block(env, loghandle, prev_idx, buf, len);
+ return rc;
+}
+
+static inline int llog_connect(struct llog_ctxt *ctxt,
+ struct llog_logid *logid, struct llog_gen *gen,
+ struct obd_uuid *uuid)
+{
+ struct llog_operations *lop;
+ int rc;
+
+ rc = llog_obd2ops(ctxt, &lop);
+ if (rc)
+ return rc;
+ if (lop->lop_connect == NULL)
+ return -EOPNOTSUPP;
+
+ rc = lop->lop_connect(ctxt, logid, gen, uuid);
+ return rc;
+}
+
+/* llog.c */
+int llog_exist(struct llog_handle *loghandle);
+int llog_declare_create(const struct lu_env *env,
+ struct llog_handle *loghandle, struct thandle *th);
+int llog_create(const struct lu_env *env, struct llog_handle *handle,
+ struct thandle *th);
+int llog_declare_write_rec(const struct lu_env *env,
+ struct llog_handle *handle,
+ struct llog_rec_hdr *rec, int idx,
+ struct thandle *th);
+int llog_write_rec(const struct lu_env *env, struct llog_handle *handle,
+ struct llog_rec_hdr *rec, struct llog_cookie *logcookies,
+ int numcookies, void *buf, int idx, struct thandle *th);
+int llog_add(const struct lu_env *env, struct llog_handle *lgh,
+ struct llog_rec_hdr *rec, struct llog_cookie *logcookies,
+ void *buf, struct thandle *th);
+int llog_declare_add(const struct lu_env *env, struct llog_handle *lgh,
+ struct llog_rec_hdr *rec, struct thandle *th);
+int lustre_process_log(struct super_block *sb, char *logname,
+ struct config_llog_instance *cfg);
+int lustre_end_log(struct super_block *sb, char *logname,
+ struct config_llog_instance *cfg);
+int llog_open_create(const struct lu_env *env, struct llog_ctxt *ctxt,
+ struct llog_handle **res, struct llog_logid *logid,
+ char *name);
+int llog_erase(const struct lu_env *env, struct llog_ctxt *ctxt,
+ struct llog_logid *logid, char *name);
+int llog_write(const struct lu_env *env, struct llog_handle *loghandle,
+ struct llog_rec_hdr *rec, struct llog_cookie *reccookie,
+ int cookiecount, void *buf, int idx);
+
+/** @} log */
+
+#endif
diff --git a/drivers/staging/lustre/lustre/include/lustre_mdc.h b/drivers/staging/lustre/lustre/include/lustre_mdc.h
new file mode 100644
index 000000000..b1b05c8a3
--- /dev/null
+++ b/drivers/staging/lustre/lustre/include/lustre_mdc.h
@@ -0,0 +1,191 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ */
+/*
+ * Copyright (c) 2011, 2012, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ *
+ * lustre/include/lustre_mdc.h
+ *
+ * MDS data structures.
+ * See also lustre_idl.h for wire formats of requests.
+ */
+
+#ifndef _LUSTRE_MDC_H
+#define _LUSTRE_MDC_H
+
+/** \defgroup mdc mdc
+ *
+ * @{
+ */
+
+#include <linux/fs.h>
+#include <linux/dcache.h>
+#include "lustre_intent.h"
+#include "lustre_handles.h"
+#include "../../include/linux/libcfs/libcfs.h"
+#include "obd_class.h"
+#include "lustre/lustre_idl.h"
+#include "lustre_lib.h"
+#include "lustre_dlm.h"
+#include "lustre_export.h"
+
+struct ptlrpc_client;
+struct obd_export;
+struct ptlrpc_request;
+struct obd_device;
+
+struct mdc_rpc_lock {
+ struct mutex rpcl_mutex;
+ struct lookup_intent *rpcl_it;
+ int rpcl_fakes;
+};
+
+#define MDC_FAKE_RPCL_IT ((void *)0x2c0012bfUL)
+
+static inline void mdc_init_rpc_lock(struct mdc_rpc_lock *lck)
+{
+ mutex_init(&lck->rpcl_mutex);
+ lck->rpcl_it = NULL;
+}
+
+static inline void mdc_get_rpc_lock(struct mdc_rpc_lock *lck,
+ struct lookup_intent *it)
+{
+ if (it != NULL && (it->it_op == IT_GETATTR || it->it_op == IT_LOOKUP ||
+ it->it_op == IT_LAYOUT))
+ return;
+
+ /* This would normally block until the existing request finishes.
+ * If fail_loc is set it will block until the regular request is
+ * done, then set rpcl_it to MDC_FAKE_RPCL_IT. Once that is set
+ * it will only be cleared when all fake requests are finished.
+ * Only when all fake requests are finished can normal requests
+ * be sent, to ensure they are recoverable again. */
+ again:
+ mutex_lock(&lck->rpcl_mutex);
+
+ if (CFS_FAIL_CHECK_QUIET(OBD_FAIL_MDC_RPCS_SEM)) {
+ lck->rpcl_it = MDC_FAKE_RPCL_IT;
+ lck->rpcl_fakes++;
+ mutex_unlock(&lck->rpcl_mutex);
+ return;
+ }
+
+ /* This will only happen when the CFS_FAIL_CHECK() was
+ * just turned off but there are still requests in progress.
+ * Wait until they finish. It doesn't need to be efficient
+ * in this extremely rare case, just have low overhead in
+ * the common case when it isn't true. */
+ while (unlikely(lck->rpcl_it == MDC_FAKE_RPCL_IT)) {
+ mutex_unlock(&lck->rpcl_mutex);
+ schedule_timeout(cfs_time_seconds(1) / 4);
+ goto again;
+ }
+
+ LASSERT(lck->rpcl_it == NULL);
+ lck->rpcl_it = it;
+}
+
+static inline void mdc_put_rpc_lock(struct mdc_rpc_lock *lck,
+ struct lookup_intent *it)
+{
+ if (it != NULL && (it->it_op == IT_GETATTR || it->it_op == IT_LOOKUP ||
+ it->it_op == IT_LAYOUT))
+ return;
+
+ if (lck->rpcl_it == MDC_FAKE_RPCL_IT) { /* OBD_FAIL_MDC_RPCS_SEM */
+ mutex_lock(&lck->rpcl_mutex);
+
+ LASSERTF(lck->rpcl_fakes > 0, "%d\n", lck->rpcl_fakes);
+ lck->rpcl_fakes--;
+
+ if (lck->rpcl_fakes == 0)
+ lck->rpcl_it = NULL;
+
+ } else {
+ LASSERTF(it == lck->rpcl_it, "%p != %p\n", it, lck->rpcl_it);
+ lck->rpcl_it = NULL;
+ }
+
+ mutex_unlock(&lck->rpcl_mutex);
+}
+
+/* Update the maximum observed easize and cookiesize. The default easize
+ * and cookiesize is initialized to the minimum value but allowed to grow
+ * up to a single page in size if required to handle the common case.
+ */
+static inline void mdc_update_max_ea_from_body(struct obd_export *exp,
+ struct mdt_body *body)
+{
+ if (body->valid & OBD_MD_FLMODEASIZE) {
+ struct client_obd *cli = &exp->exp_obd->u.cli;
+
+ if (cli->cl_max_mds_easize < body->max_mdsize) {
+ cli->cl_max_mds_easize = body->max_mdsize;
+ cli->cl_default_mds_easize =
+ min_t(__u32, body->max_mdsize, PAGE_CACHE_SIZE);
+ }
+ if (cli->cl_max_mds_cookiesize < body->max_cookiesize) {
+ cli->cl_max_mds_cookiesize = body->max_cookiesize;
+ cli->cl_default_mds_cookiesize =
+ min_t(__u32, body->max_cookiesize, PAGE_CACHE_SIZE);
+ }
+ }
+}
+
+
+struct mdc_cache_waiter {
+ struct list_head mcw_entry;
+ wait_queue_head_t mcw_waitq;
+};
+
+/* mdc/mdc_locks.c */
+int it_disposition(struct lookup_intent *it, int flag);
+void it_clear_disposition(struct lookup_intent *it, int flag);
+void it_set_disposition(struct lookup_intent *it, int flag);
+int it_open_error(int phase, struct lookup_intent *it);
+
+static inline bool cl_is_lov_delay_create(unsigned int flags)
+{
+ return (flags & O_LOV_DELAY_CREATE) == O_LOV_DELAY_CREATE;
+}
+
+static inline void cl_lov_delay_create_clear(unsigned int *flags)
+{
+ if ((*flags & O_LOV_DELAY_CREATE) == O_LOV_DELAY_CREATE)
+ *flags &= ~O_LOV_DELAY_CREATE;
+}
+
+/** @} mdc */
+
+#endif
diff --git a/drivers/staging/lustre/lustre/include/lustre_mds.h b/drivers/staging/lustre/lustre/include/lustre_mds.h
new file mode 100644
index 000000000..f0cce41c5
--- /dev/null
+++ b/drivers/staging/lustre/lustre/include/lustre_mds.h
@@ -0,0 +1,81 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2011, 2012, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ *
+ * lustre/include/lustre_mds.h
+ *
+ * MDS data structures.
+ * See also lustre_idl.h for wire formats of requests.
+ */
+
+#ifndef _LUSTRE_MDS_H
+#define _LUSTRE_MDS_H
+
+/** \defgroup mds mds
+ *
+ * @{
+ */
+
+#include "lustre_handles.h"
+#include "../../include/linux/libcfs/libcfs.h"
+#include "lustre/lustre_idl.h"
+#include "lustre_lib.h"
+#include "lustre_dlm.h"
+#include "lustre_export.h"
+
+struct mds_group_info {
+ struct obd_uuid *uuid;
+ int group;
+};
+
+struct mds_capa_info {
+ struct obd_uuid *uuid;
+ struct lustre_capa_key *capa;
+};
+
+#define MDD_OBD_NAME "mdd_obd"
+#define MDD_OBD_UUID "mdd_obd_uuid"
+
+static inline int md_should_create(__u64 flags)
+{
+ return !(flags & MDS_OPEN_DELAY_CREATE ||
+ !(flags & FMODE_WRITE));
+}
+
+/* these are local flags, used only on the client, private */
+#define M_CHECK_STALE 0200000000
+
+/** @} mds */
+
+#endif
diff --git a/drivers/staging/lustre/lustre/include/lustre_net.h b/drivers/staging/lustre/lustre/include/lustre_net.h
new file mode 100644
index 000000000..e2805bd1a
--- /dev/null
+++ b/drivers/staging/lustre/lustre/include/lustre_net.h
@@ -0,0 +1,2967 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2010, 2012, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ */
+/** \defgroup PtlRPC Portal RPC and networking module.
+ *
+ * PortalRPC is the layer used by rest of lustre code to achieve network
+ * communications: establish connections with corresponding export and import
+ * states, listen for a service, send and receive RPCs.
+ * PortalRPC also includes base recovery framework: packet resending and
+ * replaying, reconnections, pinger.
+ *
+ * PortalRPC utilizes LNet as its transport layer.
+ *
+ * @{
+ */
+
+
+#ifndef _LUSTRE_NET_H
+#define _LUSTRE_NET_H
+
+/** \defgroup net net
+ *
+ * @{
+ */
+
+#include "../../include/linux/libcfs/libcfs.h"
+// #include <obd.h>
+#include "../../include/linux/lnet/lnet.h"
+#include "lustre/lustre_idl.h"
+#include "lustre_ha.h"
+#include "lustre_sec.h"
+#include "lustre_import.h"
+#include "lprocfs_status.h"
+#include "lu_object.h"
+#include "lustre_req_layout.h"
+
+#include "obd_support.h"
+#include "lustre_ver.h"
+
+/* MD flags we _always_ use */
+#define PTLRPC_MD_OPTIONS 0
+
+/**
+ * Max # of bulk operations in one request.
+ * In order for the client and server to properly negotiate the maximum
+ * possible transfer size, PTLRPC_BULK_OPS_COUNT must be a power-of-two
+ * value. The client is free to limit the actual RPC size for any bulk
+ * transfer via cl_max_pages_per_rpc to some non-power-of-two value. */
+#define PTLRPC_BULK_OPS_BITS 2
+#define PTLRPC_BULK_OPS_COUNT (1U << PTLRPC_BULK_OPS_BITS)
+/**
+ * PTLRPC_BULK_OPS_MASK is for the convenience of the client only, and
+ * should not be used on the server at all. Otherwise, it imposes a
+ * protocol limitation on the maximum RPC size that can be used by any
+ * RPC sent to that server in the future. Instead, the server should
+ * use the negotiated per-client ocd_brw_size to determine the bulk
+ * RPC count. */
+#define PTLRPC_BULK_OPS_MASK (~((__u64)PTLRPC_BULK_OPS_COUNT - 1))
+
+/**
+ * Define maxima for bulk I/O.
+ *
+ * A single PTLRPC BRW request is sent via up to PTLRPC_BULK_OPS_COUNT
+ * of LNET_MTU sized RDMA transfers. Clients and servers negotiate the
+ * currently supported maximum between peers at connect via ocd_brw_size.
+ */
+#define PTLRPC_MAX_BRW_BITS (LNET_MTU_BITS + PTLRPC_BULK_OPS_BITS)
+#define PTLRPC_MAX_BRW_SIZE (1 << PTLRPC_MAX_BRW_BITS)
+#define PTLRPC_MAX_BRW_PAGES (PTLRPC_MAX_BRW_SIZE >> PAGE_CACHE_SHIFT)
+
+#define ONE_MB_BRW_SIZE (1 << LNET_MTU_BITS)
+#define MD_MAX_BRW_SIZE (1 << LNET_MTU_BITS)
+#define MD_MAX_BRW_PAGES (MD_MAX_BRW_SIZE >> PAGE_CACHE_SHIFT)
+#define DT_MAX_BRW_SIZE PTLRPC_MAX_BRW_SIZE
+#define DT_MAX_BRW_PAGES (DT_MAX_BRW_SIZE >> PAGE_CACHE_SHIFT)
+#define OFD_MAX_BRW_SIZE (1 << LNET_MTU_BITS)
+
+/* When PAGE_SIZE is a constant, we can check our arithmetic here with cpp! */
+# if ((PTLRPC_MAX_BRW_PAGES & (PTLRPC_MAX_BRW_PAGES - 1)) != 0)
+# error "PTLRPC_MAX_BRW_PAGES isn't a power of two"
+# endif
+# if (PTLRPC_MAX_BRW_SIZE != (PTLRPC_MAX_BRW_PAGES * PAGE_CACHE_SIZE))
+# error "PTLRPC_MAX_BRW_SIZE isn't PTLRPC_MAX_BRW_PAGES * PAGE_CACHE_SIZE"
+# endif
+# if (PTLRPC_MAX_BRW_SIZE > LNET_MTU * PTLRPC_BULK_OPS_COUNT)
+# error "PTLRPC_MAX_BRW_SIZE too big"
+# endif
+# if (PTLRPC_MAX_BRW_PAGES > LNET_MAX_IOV * PTLRPC_BULK_OPS_COUNT)
+# error "PTLRPC_MAX_BRW_PAGES too big"
+# endif
+
+#define PTLRPC_NTHRS_INIT 2
+
+/**
+ * Buffer Constants
+ *
+ * Constants determine how memory is used to buffer incoming service requests.
+ *
+ * ?_NBUFS # buffers to allocate when growing the pool
+ * ?_BUFSIZE # bytes in a single request buffer
+ * ?_MAXREQSIZE # maximum request service will receive
+ *
+ * When fewer than ?_NBUFS/2 buffers are posted for receive, another chunk
+ * of ?_NBUFS is added to the pool.
+ *
+ * Messages larger than ?_MAXREQSIZE are dropped. Request buffers are
+ * considered full when less than ?_MAXREQSIZE is left in them.
+ */
+/**
+ * Thread Constants
+ *
+ * Constants determine how threads are created for ptlrpc service.
+ *
+ * ?_NTHRS_INIT # threads to create for each service partition on
+ * initializing. If it's non-affinity service and
+ * there is only one partition, it's the overall #
+ * threads for the service while initializing.
+ * ?_NTHRS_BASE # threads should be created at least for each
+ * ptlrpc partition to keep the service healthy.
+ * It's the low-water mark of threads upper-limit
+ * for each partition.
+ * ?_THR_FACTOR # threads can be added on threads upper-limit for
+ * each CPU core. This factor is only for reference,
+ * we might decrease value of factor if number of cores
+ * per CPT is above a limit.
+ * ?_NTHRS_MAX # overall threads can be created for a service,
+ * it's a soft limit because if service is running
+ * on machine with hundreds of cores and tens of
+ * CPU partitions, we need to guarantee each partition
+ * has ?_NTHRS_BASE threads, which means total threads
+ * will be ?_NTHRS_BASE * number_of_cpts which can
+ * exceed ?_NTHRS_MAX.
+ *
+ * Examples
+ *
+ * #define MDS_NTHRS_INIT 2
+ * #define MDS_NTHRS_BASE 64
+ * #define MDS_NTHRS_FACTOR 8
+ * #define MDS_NTHRS_MAX 1024
+ *
+ * Example 1):
+ * ---------------------------------------------------------------------
+ * Server(A) has 16 cores, user configured it to 4 partitions so each
+ * partition has 4 cores, then actual number of service threads on each
+ * partition is:
+ * MDS_NTHRS_BASE(64) + cores(4) * MDS_NTHRS_FACTOR(8) = 96
+ *
+ * Total number of threads for the service is:
+ * 96 * partitions(4) = 384
+ *
+ * Example 2):
+ * ---------------------------------------------------------------------
+ * Server(B) has 32 cores, user configured it to 4 partitions so each
+ * partition has 8 cores, then actual number of service threads on each
+ * partition is:
+ * MDS_NTHRS_BASE(64) + cores(8) * MDS_NTHRS_FACTOR(8) = 128
+ *
+ * Total number of threads for the service is:
+ * 128 * partitions(4) = 512
+ *
+ * Example 3):
+ * ---------------------------------------------------------------------
+ * Server(B) has 96 cores, user configured it to 8 partitions so each
+ * partition has 12 cores, then actual number of service threads on each
+ * partition is:
+ * MDS_NTHRS_BASE(64) + cores(12) * MDS_NTHRS_FACTOR(8) = 160
+ *
+ * Total number of threads for the service is:
+ * 160 * partitions(8) = 1280
+ *
+ * However, it's above the soft limit MDS_NTHRS_MAX, so we choose this number
+ * as upper limit of threads number for each partition:
+ * MDS_NTHRS_MAX(1024) / partitions(8) = 128
+ *
+ * Example 4):
+ * ---------------------------------------------------------------------
+ * Server(C) have a thousand of cores and user configured it to 32 partitions
+ * MDS_NTHRS_BASE(64) * 32 = 2048
+ *
+ * which is already above soft limit MDS_NTHRS_MAX(1024), but we still need
+ * to guarantee that each partition has at least MDS_NTHRS_BASE(64) threads
+ * to keep service healthy, so total number of threads will just be 2048.
+ *
+ * NB: we don't suggest to choose server with that many cores because backend
+ * filesystem itself, buffer cache, or underlying network stack might
+ * have some SMP scalability issues at that large scale.
+ *
+ * If user already has a fat machine with hundreds or thousands of cores,
+ * there are two choices for configuration:
+ * a) create CPU table from subset of all CPUs and run Lustre on
+ * top of this subset
+ * b) bind service threads on a few partitions, see modparameters of
+ * MDS and OSS for details
+*
+ * NB: these calculations (and examples below) are simplified to help
+ * understanding, the real implementation is a little more complex,
+ * please see ptlrpc_server_nthreads_check() for details.
+ *
+ */
+
+ /*
+ * LDLM threads constants:
+ *
+ * Given 8 as factor and 24 as base threads number
+ *
+ * example 1)
+ * On 4-core machine we will have 24 + 8 * 4 = 56 threads.
+ *
+ * example 2)
+ * On 8-core machine with 2 partitions we will have 24 + 4 * 8 = 56
+ * threads for each partition and total threads number will be 112.
+ *
+ * example 3)
+ * On 64-core machine with 8 partitions we will need LDLM_NTHRS_BASE(24)
+ * threads for each partition to keep service healthy, so total threads
+ * number should be 24 * 8 = 192.
+ *
+ * So with these constants, threads number will be at the similar level
+ * of old versions, unless target machine has over a hundred cores
+ */
+#define LDLM_THR_FACTOR 8
+#define LDLM_NTHRS_INIT PTLRPC_NTHRS_INIT
+#define LDLM_NTHRS_BASE 24
+#define LDLM_NTHRS_MAX (num_online_cpus() == 1 ? 64 : 128)
+
+#define LDLM_BL_THREADS LDLM_NTHRS_AUTO_INIT
+#define LDLM_CLIENT_NBUFS 1
+#define LDLM_SERVER_NBUFS 64
+#define LDLM_BUFSIZE (8 * 1024)
+#define LDLM_MAXREQSIZE (5 * 1024)
+#define LDLM_MAXREPSIZE (1024)
+
+#define MDS_MAXREQSIZE (5 * 1024) /* >= 4736 */
+
+#define OST_MAXREQSIZE (5 * 1024)
+
+/* Macro to hide a typecast. */
+#define ptlrpc_req_async_args(req) ((void *)&req->rq_async_args)
+
+/**
+ * Structure to single define portal connection.
+ */
+struct ptlrpc_connection {
+ /** linkage for connections hash table */
+ struct hlist_node c_hash;
+ /** Our own lnet nid for this connection */
+ lnet_nid_t c_self;
+ /** Remote side nid for this connection */
+ lnet_process_id_t c_peer;
+ /** UUID of the other side */
+ struct obd_uuid c_remote_uuid;
+ /** reference counter for this connection */
+ atomic_t c_refcount;
+};
+
+/** Client definition for PortalRPC */
+struct ptlrpc_client {
+ /** What lnet portal does this client send messages to by default */
+ __u32 cli_request_portal;
+ /** What portal do we expect replies on */
+ __u32 cli_reply_portal;
+ /** Name of the client */
+ char *cli_name;
+};
+
+/** state flags of requests */
+/* XXX only ones left are those used by the bulk descs as well! */
+#define PTL_RPC_FL_INTR (1 << 0) /* reply wait was interrupted by user */
+#define PTL_RPC_FL_TIMEOUT (1 << 7) /* request timed out waiting for reply */
+
+#define REQ_MAX_ACK_LOCKS 8
+
+union ptlrpc_async_args {
+ /**
+ * Scratchpad for passing args to completion interpreter. Users
+ * cast to the struct of their choosing, and CLASSERT that this is
+ * big enough. For _tons_ of context, OBD_ALLOC a struct and store
+ * a pointer to it here. The pointer_arg ensures this struct is at
+ * least big enough for that.
+ */
+ void *pointer_arg[11];
+ __u64 space[7];
+};
+
+struct ptlrpc_request_set;
+typedef int (*set_interpreter_func)(struct ptlrpc_request_set *, void *, int);
+typedef int (*set_producer_func)(struct ptlrpc_request_set *, void *);
+
+/**
+ * Definition of request set structure.
+ * Request set is a list of requests (not necessary to the same target) that
+ * once populated with RPCs could be sent in parallel.
+ * There are two kinds of request sets. General purpose and with dedicated
+ * serving thread. Example of the latter is ptlrpcd set.
+ * For general purpose sets once request set started sending it is impossible
+ * to add new requests to such set.
+ * Provides a way to call "completion callbacks" when all requests in the set
+ * returned.
+ */
+struct ptlrpc_request_set {
+ atomic_t set_refcount;
+ /** number of in queue requests */
+ atomic_t set_new_count;
+ /** number of uncompleted requests */
+ atomic_t set_remaining;
+ /** wait queue to wait on for request events */
+ wait_queue_head_t set_waitq;
+ wait_queue_head_t *set_wakeup_ptr;
+ /** List of requests in the set */
+ struct list_head set_requests;
+ /**
+ * List of completion callbacks to be called when the set is completed
+ * This is only used if \a set_interpret is NULL.
+ * Links struct ptlrpc_set_cbdata.
+ */
+ struct list_head set_cblist;
+ /** Completion callback, if only one. */
+ set_interpreter_func set_interpret;
+ /** opaq argument passed to completion \a set_interpret callback. */
+ void *set_arg;
+ /**
+ * Lock for \a set_new_requests manipulations
+ * locked so that any old caller can communicate requests to
+ * the set holder who can then fold them into the lock-free set
+ */
+ spinlock_t set_new_req_lock;
+ /** List of new yet unsent requests. Only used with ptlrpcd now. */
+ struct list_head set_new_requests;
+
+ /** rq_status of requests that have been freed already */
+ int set_rc;
+ /** Additional fields used by the flow control extension */
+ /** Maximum number of RPCs in flight */
+ int set_max_inflight;
+ /** Callback function used to generate RPCs */
+ set_producer_func set_producer;
+ /** opaq argument passed to the producer callback */
+ void *set_producer_arg;
+};
+
+/**
+ * Description of a single ptrlrpc_set callback
+ */
+struct ptlrpc_set_cbdata {
+ /** List linkage item */
+ struct list_head psc_item;
+ /** Pointer to interpreting function */
+ set_interpreter_func psc_interpret;
+ /** Opaq argument to pass to the callback */
+ void *psc_data;
+};
+
+struct ptlrpc_bulk_desc;
+struct ptlrpc_service_part;
+struct ptlrpc_service;
+
+/**
+ * ptlrpc callback & work item stuff
+ */
+struct ptlrpc_cb_id {
+ void (*cbid_fn)(lnet_event_t *ev); /* specific callback fn */
+ void *cbid_arg; /* additional arg */
+};
+
+/** Maximum number of locks to fit into reply state */
+#define RS_MAX_LOCKS 8
+#define RS_DEBUG 0
+
+/**
+ * Structure to define reply state on the server
+ * Reply state holds various reply message information. Also for "difficult"
+ * replies (rep-ack case) we store the state after sending reply and wait
+ * for the client to acknowledge the reception. In these cases locks could be
+ * added to the state for replay/failover consistency guarantees.
+ */
+struct ptlrpc_reply_state {
+ /** Callback description */
+ struct ptlrpc_cb_id rs_cb_id;
+ /** Linkage for list of all reply states in a system */
+ struct list_head rs_list;
+ /** Linkage for list of all reply states on same export */
+ struct list_head rs_exp_list;
+ /** Linkage for list of all reply states for same obd */
+ struct list_head rs_obd_list;
+#if RS_DEBUG
+ struct list_head rs_debug_list;
+#endif
+ /** A spinlock to protect the reply state flags */
+ spinlock_t rs_lock;
+ /** Reply state flags */
+ unsigned long rs_difficult:1; /* ACK/commit stuff */
+ unsigned long rs_no_ack:1; /* no ACK, even for
+ difficult requests */
+ unsigned long rs_scheduled:1; /* being handled? */
+ unsigned long rs_scheduled_ever:1;/* any schedule attempts? */
+ unsigned long rs_handled:1; /* been handled yet? */
+ unsigned long rs_on_net:1; /* reply_out_callback pending? */
+ unsigned long rs_prealloc:1; /* rs from prealloc list */
+ unsigned long rs_committed:1;/* the transaction was committed
+ and the rs was dispatched
+ by ptlrpc_commit_replies */
+ /** Size of the state */
+ int rs_size;
+ /** opcode */
+ __u32 rs_opc;
+ /** Transaction number */
+ __u64 rs_transno;
+ /** xid */
+ __u64 rs_xid;
+ struct obd_export *rs_export;
+ struct ptlrpc_service_part *rs_svcpt;
+ /** Lnet metadata handle for the reply */
+ lnet_handle_md_t rs_md_h;
+ atomic_t rs_refcount;
+
+ /** Context for the service thread */
+ struct ptlrpc_svc_ctx *rs_svc_ctx;
+ /** Reply buffer (actually sent to the client), encoded if needed */
+ struct lustre_msg *rs_repbuf; /* wrapper */
+ /** Size of the reply buffer */
+ int rs_repbuf_len; /* wrapper buf length */
+ /** Size of the reply message */
+ int rs_repdata_len; /* wrapper msg length */
+ /**
+ * Actual reply message. Its content is encrypted (if needed) to
+ * produce reply buffer for actual sending. In simple case
+ * of no network encryption we just set \a rs_repbuf to \a rs_msg
+ */
+ struct lustre_msg *rs_msg; /* reply message */
+
+ /** Number of locks awaiting client ACK */
+ int rs_nlocks;
+ /** Handles of locks awaiting client reply ACK */
+ struct lustre_handle rs_locks[RS_MAX_LOCKS];
+ /** Lock modes of locks in \a rs_locks */
+ ldlm_mode_t rs_modes[RS_MAX_LOCKS];
+};
+
+struct ptlrpc_thread;
+
+/** RPC stages */
+enum rq_phase {
+ RQ_PHASE_NEW = 0xebc0de00,
+ RQ_PHASE_RPC = 0xebc0de01,
+ RQ_PHASE_BULK = 0xebc0de02,
+ RQ_PHASE_INTERPRET = 0xebc0de03,
+ RQ_PHASE_COMPLETE = 0xebc0de04,
+ RQ_PHASE_UNREGISTERING = 0xebc0de05,
+ RQ_PHASE_UNDEFINED = 0xebc0de06
+};
+
+/** Type of request interpreter call-back */
+typedef int (*ptlrpc_interpterer_t)(const struct lu_env *env,
+ struct ptlrpc_request *req,
+ void *arg, int rc);
+
+/**
+ * Definition of request pool structure.
+ * The pool is used to store empty preallocated requests for the case
+ * when we would actually need to send something without performing
+ * any allocations (to avoid e.g. OOM).
+ */
+struct ptlrpc_request_pool {
+ /** Locks the list */
+ spinlock_t prp_lock;
+ /** list of ptlrpc_request structs */
+ struct list_head prp_req_list;
+ /** Maximum message size that would fit into a request from this pool */
+ int prp_rq_size;
+ /** Function to allocate more requests for this pool */
+ void (*prp_populate)(struct ptlrpc_request_pool *, int);
+};
+
+struct lu_context;
+struct lu_env;
+
+struct ldlm_lock;
+
+/**
+ * \defgroup nrs Network Request Scheduler
+ * @{
+ */
+struct ptlrpc_nrs_policy;
+struct ptlrpc_nrs_resource;
+struct ptlrpc_nrs_request;
+
+/**
+ * NRS control operations.
+ *
+ * These are common for all policies.
+ */
+enum ptlrpc_nrs_ctl {
+ /**
+ * Not a valid opcode.
+ */
+ PTLRPC_NRS_CTL_INVALID,
+ /**
+ * Activate the policy.
+ */
+ PTLRPC_NRS_CTL_START,
+ /**
+ * Reserved for multiple primary policies, which may be a possibility
+ * in the future.
+ */
+ PTLRPC_NRS_CTL_STOP,
+ /**
+ * Policies can start using opcodes from this value and onwards for
+ * their own purposes; the assigned value itself is arbitrary.
+ */
+ PTLRPC_NRS_CTL_1ST_POL_SPEC = 0x20,
+};
+
+/**
+ * ORR policy operations
+ */
+enum nrs_ctl_orr {
+ NRS_CTL_ORR_RD_QUANTUM = PTLRPC_NRS_CTL_1ST_POL_SPEC,
+ NRS_CTL_ORR_WR_QUANTUM,
+ NRS_CTL_ORR_RD_OFF_TYPE,
+ NRS_CTL_ORR_WR_OFF_TYPE,
+ NRS_CTL_ORR_RD_SUPP_REQ,
+ NRS_CTL_ORR_WR_SUPP_REQ,
+};
+
+/**
+ * NRS policy operations.
+ *
+ * These determine the behaviour of a policy, and are called in response to
+ * NRS core events.
+ */
+struct ptlrpc_nrs_pol_ops {
+ /**
+ * Called during policy registration; this operation is optional.
+ *
+ * \param[in,out] policy The policy being initialized
+ */
+ int (*op_policy_init) (struct ptlrpc_nrs_policy *policy);
+ /**
+ * Called during policy unregistration; this operation is optional.
+ *
+ * \param[in,out] policy The policy being unregistered/finalized
+ */
+ void (*op_policy_fini) (struct ptlrpc_nrs_policy *policy);
+ /**
+ * Called when activating a policy via lprocfs; policies allocate and
+ * initialize their resources here; this operation is optional.
+ *
+ * \param[in,out] policy The policy being started
+ *
+ * \see nrs_policy_start_locked()
+ */
+ int (*op_policy_start) (struct ptlrpc_nrs_policy *policy);
+ /**
+ * Called when deactivating a policy via lprocfs; policies deallocate
+ * their resources here; this operation is optional
+ *
+ * \param[in,out] policy The policy being stopped
+ *
+ * \see nrs_policy_stop0()
+ */
+ void (*op_policy_stop) (struct ptlrpc_nrs_policy *policy);
+ /**
+ * Used for policy-specific operations; i.e. not generic ones like
+ * \e PTLRPC_NRS_CTL_START and \e PTLRPC_NRS_CTL_GET_INFO; analogous
+ * to an ioctl; this operation is optional.
+ *
+ * \param[in,out] policy The policy carrying out operation \a opc
+ * \param[in] opc The command operation being carried out
+ * \param[in,out] arg An generic buffer for communication between the
+ * user and the control operation
+ *
+ * \retval -ve error
+ * \retval 0 success
+ *
+ * \see ptlrpc_nrs_policy_control()
+ */
+ int (*op_policy_ctl) (struct ptlrpc_nrs_policy *policy,
+ enum ptlrpc_nrs_ctl opc, void *arg);
+
+ /**
+ * Called when obtaining references to the resources of the resource
+ * hierarchy for a request that has arrived for handling at the PTLRPC
+ * service. Policies should return -ve for requests they do not wish
+ * to handle. This operation is mandatory.
+ *
+ * \param[in,out] policy The policy we're getting resources for.
+ * \param[in,out] nrq The request we are getting resources for.
+ * \param[in] parent The parent resource of the resource being
+ * requested; set to NULL if none.
+ * \param[out] resp The resource is to be returned here; the
+ * fallback policy in an NRS head should
+ * \e always return a non-NULL pointer value.
+ * \param[in] moving_req When set, signifies that this is an attempt
+ * to obtain resources for a request being moved
+ * to the high-priority NRS head by
+ * ldlm_lock_reorder_req().
+ * This implies two things:
+ * 1. We are under obd_export::exp_rpc_lock and
+ * so should not sleep.
+ * 2. We should not perform non-idempotent or can
+ * skip performing idempotent operations that
+ * were carried out when resources were first
+ * taken for the request when it was initialized
+ * in ptlrpc_nrs_req_initialize().
+ *
+ * \retval 0, +ve The level of the returned resource in the resource
+ * hierarchy; currently only 0 (for a non-leaf resource)
+ * and 1 (for a leaf resource) are supported by the
+ * framework.
+ * \retval -ve error
+ *
+ * \see ptlrpc_nrs_req_initialize()
+ * \see ptlrpc_nrs_hpreq_add_nolock()
+ * \see ptlrpc_nrs_req_hp_move()
+ */
+ int (*op_res_get) (struct ptlrpc_nrs_policy *policy,
+ struct ptlrpc_nrs_request *nrq,
+ const struct ptlrpc_nrs_resource *parent,
+ struct ptlrpc_nrs_resource **resp,
+ bool moving_req);
+ /**
+ * Called when releasing references taken for resources in the resource
+ * hierarchy for the request; this operation is optional.
+ *
+ * \param[in,out] policy The policy the resource belongs to
+ * \param[in] res The resource to be freed
+ *
+ * \see ptlrpc_nrs_req_finalize()
+ * \see ptlrpc_nrs_hpreq_add_nolock()
+ * \see ptlrpc_nrs_req_hp_move()
+ */
+ void (*op_res_put) (struct ptlrpc_nrs_policy *policy,
+ const struct ptlrpc_nrs_resource *res);
+
+ /**
+ * Obtains a request for handling from the policy, and optionally
+ * removes the request from the policy; this operation is mandatory.
+ *
+ * \param[in,out] policy The policy to poll
+ * \param[in] peek When set, signifies that we just want to
+ * examine the request, and not handle it, so the
+ * request is not removed from the policy.
+ * \param[in] force When set, it will force a policy to return a
+ * request if it has one queued.
+ *
+ * \retval NULL No request available for handling
+ * \retval valid-pointer The request polled for handling
+ *
+ * \see ptlrpc_nrs_req_get_nolock()
+ */
+ struct ptlrpc_nrs_request *
+ (*op_req_get) (struct ptlrpc_nrs_policy *policy, bool peek,
+ bool force);
+ /**
+ * Called when attempting to add a request to a policy for later
+ * handling; this operation is mandatory.
+ *
+ * \param[in,out] policy The policy on which to enqueue \a nrq
+ * \param[in,out] nrq The request to enqueue
+ *
+ * \retval 0 success
+ * \retval != 0 error
+ *
+ * \see ptlrpc_nrs_req_add_nolock()
+ */
+ int (*op_req_enqueue) (struct ptlrpc_nrs_policy *policy,
+ struct ptlrpc_nrs_request *nrq);
+ /**
+ * Removes a request from the policy's set of pending requests. Normally
+ * called after a request has been polled successfully from the policy
+ * for handling; this operation is mandatory.
+ *
+ * \param[in,out] policy The policy the request \a nrq belongs to
+ * \param[in,out] nrq The request to dequeue
+ *
+ * \see ptlrpc_nrs_req_del_nolock()
+ */
+ void (*op_req_dequeue) (struct ptlrpc_nrs_policy *policy,
+ struct ptlrpc_nrs_request *nrq);
+ /**
+ * Called after the request being carried out. Could be used for
+ * job/resource control; this operation is optional.
+ *
+ * \param[in,out] policy The policy which is stopping to handle request
+ * \a nrq
+ * \param[in,out] nrq The request
+ *
+ * \pre assert_spin_locked(&svcpt->scp_req_lock)
+ *
+ * \see ptlrpc_nrs_req_stop_nolock()
+ */
+ void (*op_req_stop) (struct ptlrpc_nrs_policy *policy,
+ struct ptlrpc_nrs_request *nrq);
+ /**
+ * Registers the policy's lprocfs interface with a PTLRPC service.
+ *
+ * \param[in] svc The service
+ *
+ * \retval 0 success
+ * \retval != 0 error
+ */
+ int (*op_lprocfs_init) (struct ptlrpc_service *svc);
+ /**
+ * Unegisters the policy's lprocfs interface with a PTLRPC service.
+ *
+ * In cases of failed policy registration in
+ * \e ptlrpc_nrs_policy_register(), this function may be called for a
+ * service which has not registered the policy successfully, so
+ * implementations of this method should make sure their operations are
+ * safe in such cases.
+ *
+ * \param[in] svc The service
+ */
+ void (*op_lprocfs_fini) (struct ptlrpc_service *svc);
+};
+
+/**
+ * Policy flags
+ */
+enum nrs_policy_flags {
+ /**
+ * Fallback policy, use this flag only on a single supported policy per
+ * service. The flag cannot be used on policies that use
+ * \e PTLRPC_NRS_FL_REG_EXTERN
+ */
+ PTLRPC_NRS_FL_FALLBACK = (1 << 0),
+ /**
+ * Start policy immediately after registering.
+ */
+ PTLRPC_NRS_FL_REG_START = (1 << 1),
+ /**
+ * This is a policy registering from a module different to the one NRS
+ * core ships in (currently ptlrpc).
+ */
+ PTLRPC_NRS_FL_REG_EXTERN = (1 << 2),
+};
+
+/**
+ * NRS queue type.
+ *
+ * Denotes whether an NRS instance is for handling normal or high-priority
+ * RPCs, or whether an operation pertains to one or both of the NRS instances
+ * in a service.
+ */
+enum ptlrpc_nrs_queue_type {
+ PTLRPC_NRS_QUEUE_REG = (1 << 0),
+ PTLRPC_NRS_QUEUE_HP = (1 << 1),
+ PTLRPC_NRS_QUEUE_BOTH = (PTLRPC_NRS_QUEUE_REG | PTLRPC_NRS_QUEUE_HP)
+};
+
+/**
+ * NRS head
+ *
+ * A PTLRPC service has at least one NRS head instance for handling normal
+ * priority RPCs, and may optionally have a second NRS head instance for
+ * handling high-priority RPCs. Each NRS head maintains a list of available
+ * policies, of which one and only one policy is acting as the fallback policy,
+ * and optionally a different policy may be acting as the primary policy. For
+ * all RPCs handled by this NRS head instance, NRS core will first attempt to
+ * enqueue the RPC using the primary policy (if any). The fallback policy is
+ * used in the following cases:
+ * - when there was no primary policy in the
+ * ptlrpc_nrs_pol_state::NRS_POL_STATE_STARTED state at the time the request
+ * was initialized.
+ * - when the primary policy that was at the
+ * ptlrpc_nrs_pol_state::PTLRPC_NRS_POL_STATE_STARTED state at the time the
+ * RPC was initialized, denoted it did not wish, or for some other reason was
+ * not able to handle the request, by returning a non-valid NRS resource
+ * reference.
+ * - when the primary policy that was at the
+ * ptlrpc_nrs_pol_state::PTLRPC_NRS_POL_STATE_STARTED state at the time the
+ * RPC was initialized, fails later during the request enqueueing stage.
+ *
+ * \see nrs_resource_get_safe()
+ * \see nrs_request_enqueue()
+ */
+struct ptlrpc_nrs {
+ spinlock_t nrs_lock;
+ /** XXX Possibly replace svcpt->scp_req_lock with another lock here. */
+ /**
+ * List of registered policies
+ */
+ struct list_head nrs_policy_list;
+ /**
+ * List of policies with queued requests. Policies that have any
+ * outstanding requests are queued here, and this list is queried
+ * in a round-robin manner from NRS core when obtaining a request
+ * for handling. This ensures that requests from policies that at some
+ * point transition away from the
+ * ptlrpc_nrs_pol_state::NRS_POL_STATE_STARTED state are drained.
+ */
+ struct list_head nrs_policy_queued;
+ /**
+ * Service partition for this NRS head
+ */
+ struct ptlrpc_service_part *nrs_svcpt;
+ /**
+ * Primary policy, which is the preferred policy for handling RPCs
+ */
+ struct ptlrpc_nrs_policy *nrs_policy_primary;
+ /**
+ * Fallback policy, which is the backup policy for handling RPCs
+ */
+ struct ptlrpc_nrs_policy *nrs_policy_fallback;
+ /**
+ * This NRS head handles either HP or regular requests
+ */
+ enum ptlrpc_nrs_queue_type nrs_queue_type;
+ /**
+ * # queued requests from all policies in this NRS head
+ */
+ unsigned long nrs_req_queued;
+ /**
+ * # scheduled requests from all policies in this NRS head
+ */
+ unsigned long nrs_req_started;
+ /**
+ * # policies on this NRS
+ */
+ unsigned nrs_num_pols;
+ /**
+ * This NRS head is in progress of starting a policy
+ */
+ unsigned nrs_policy_starting:1;
+ /**
+ * In progress of shutting down the whole NRS head; used during
+ * unregistration
+ */
+ unsigned nrs_stopping:1;
+};
+
+#define NRS_POL_NAME_MAX 16
+
+struct ptlrpc_nrs_pol_desc;
+
+/**
+ * Service compatibility predicate; this determines whether a policy is adequate
+ * for handling RPCs of a particular PTLRPC service.
+ *
+ * XXX:This should give the same result during policy registration and
+ * unregistration, and for all partitions of a service; so the result should not
+ * depend on temporal service or other properties, that may influence the
+ * result.
+ */
+typedef bool (*nrs_pol_desc_compat_t) (const struct ptlrpc_service *svc,
+ const struct ptlrpc_nrs_pol_desc *desc);
+
+struct ptlrpc_nrs_pol_conf {
+ /**
+ * Human-readable policy name
+ */
+ char nc_name[NRS_POL_NAME_MAX];
+ /**
+ * NRS operations for this policy
+ */
+ const struct ptlrpc_nrs_pol_ops *nc_ops;
+ /**
+ * Service compatibility predicate
+ */
+ nrs_pol_desc_compat_t nc_compat;
+ /**
+ * Set for policies that support a single ptlrpc service, i.e. ones that
+ * have \a pd_compat set to nrs_policy_compat_one(). The variable value
+ * depicts the name of the single service that such policies are
+ * compatible with.
+ */
+ const char *nc_compat_svc_name;
+ /**
+ * Owner module for this policy descriptor; policies registering from a
+ * different module to the one the NRS framework is held within
+ * (currently ptlrpc), should set this field to THIS_MODULE.
+ */
+ struct module *nc_owner;
+ /**
+ * Policy registration flags; a bitmask of \e nrs_policy_flags
+ */
+ unsigned nc_flags;
+};
+
+/**
+ * NRS policy registering descriptor
+ *
+ * Is used to hold a description of a policy that can be passed to NRS core in
+ * order to register the policy with NRS heads in different PTLRPC services.
+ */
+struct ptlrpc_nrs_pol_desc {
+ /**
+ * Human-readable policy name
+ */
+ char pd_name[NRS_POL_NAME_MAX];
+ /**
+ * Link into nrs_core::nrs_policies
+ */
+ struct list_head pd_list;
+ /**
+ * NRS operations for this policy
+ */
+ const struct ptlrpc_nrs_pol_ops *pd_ops;
+ /**
+ * Service compatibility predicate
+ */
+ nrs_pol_desc_compat_t pd_compat;
+ /**
+ * Set for policies that are compatible with only one PTLRPC service.
+ *
+ * \see ptlrpc_nrs_pol_conf::nc_compat_svc_name
+ */
+ const char *pd_compat_svc_name;
+ /**
+ * Owner module for this policy descriptor.
+ *
+ * We need to hold a reference to the module whenever we might make use
+ * of any of the module's contents, i.e.
+ * - If one or more instances of the policy are at a state where they
+ * might be handling a request, i.e.
+ * ptlrpc_nrs_pol_state::NRS_POL_STATE_STARTED or
+ * ptlrpc_nrs_pol_state::NRS_POL_STATE_STOPPING as we will have to
+ * call into the policy's ptlrpc_nrs_pol_ops() handlers. A reference
+ * is taken on the module when
+ * \e ptlrpc_nrs_pol_desc::pd_refs becomes 1, and released when it
+ * becomes 0, so that we hold only one reference to the module maximum
+ * at any time.
+ *
+ * We do not need to hold a reference to the module, even though we
+ * might use code and data from the module, in the following cases:
+ * - During external policy registration, because this should happen in
+ * the module's init() function, in which case the module is safe from
+ * removal because a reference is being held on the module by the
+ * kernel, and iirc kmod (and I guess module-init-tools also) will
+ * serialize any racing processes properly anyway.
+ * - During external policy unregistration, because this should happen
+ * in a module's exit() function, and any attempts to start a policy
+ * instance would need to take a reference on the module, and this is
+ * not possible once we have reached the point where the exit()
+ * handler is called.
+ * - During service registration and unregistration, as service setup
+ * and cleanup, and policy registration, unregistration and policy
+ * instance starting, are serialized by \e nrs_core::nrs_mutex, so
+ * as long as users adhere to the convention of registering policies
+ * in init() and unregistering them in module exit() functions, there
+ * should not be a race between these operations.
+ * - During any policy-specific lprocfs operations, because a reference
+ * is held by the kernel on a proc entry that has been entered by a
+ * syscall, so as long as proc entries are removed during unregistration time,
+ * then unregistration and lprocfs operations will be properly
+ * serialized.
+ */
+ struct module *pd_owner;
+ /**
+ * Bitmask of \e nrs_policy_flags
+ */
+ unsigned pd_flags;
+ /**
+ * # of references on this descriptor
+ */
+ atomic_t pd_refs;
+};
+
+/**
+ * NRS policy state
+ *
+ * Policies transition from one state to the other during their lifetime
+ */
+enum ptlrpc_nrs_pol_state {
+ /**
+ * Not a valid policy state.
+ */
+ NRS_POL_STATE_INVALID,
+ /**
+ * Policies are at this state either at the start of their life, or
+ * transition here when the user selects a different policy to act
+ * as the primary one.
+ */
+ NRS_POL_STATE_STOPPED,
+ /**
+ * Policy is progress of stopping
+ */
+ NRS_POL_STATE_STOPPING,
+ /**
+ * Policy is in progress of starting
+ */
+ NRS_POL_STATE_STARTING,
+ /**
+ * A policy is in this state in two cases:
+ * - it is the fallback policy, which is always in this state.
+ * - it has been activated by the user; i.e. it is the primary policy,
+ */
+ NRS_POL_STATE_STARTED,
+};
+
+/**
+ * NRS policy information
+ *
+ * Used for obtaining information for the status of a policy via lprocfs
+ */
+struct ptlrpc_nrs_pol_info {
+ /**
+ * Policy name
+ */
+ char pi_name[NRS_POL_NAME_MAX];
+ /**
+ * Current policy state
+ */
+ enum ptlrpc_nrs_pol_state pi_state;
+ /**
+ * # RPCs enqueued for later dispatching by the policy
+ */
+ long pi_req_queued;
+ /**
+ * # RPCs started for dispatch by the policy
+ */
+ long pi_req_started;
+ /**
+ * Is this a fallback policy?
+ */
+ unsigned pi_fallback:1;
+};
+
+/**
+ * NRS policy
+ *
+ * There is one instance of this for each policy in each NRS head of each
+ * PTLRPC service partition.
+ */
+struct ptlrpc_nrs_policy {
+ /**
+ * Linkage into the NRS head's list of policies,
+ * ptlrpc_nrs:nrs_policy_list
+ */
+ struct list_head pol_list;
+ /**
+ * Linkage into the NRS head's list of policies with enqueued
+ * requests ptlrpc_nrs:nrs_policy_queued
+ */
+ struct list_head pol_list_queued;
+ /**
+ * Current state of this policy
+ */
+ enum ptlrpc_nrs_pol_state pol_state;
+ /**
+ * Bitmask of nrs_policy_flags
+ */
+ unsigned pol_flags;
+ /**
+ * # RPCs enqueued for later dispatching by the policy
+ */
+ long pol_req_queued;
+ /**
+ * # RPCs started for dispatch by the policy
+ */
+ long pol_req_started;
+ /**
+ * Usage Reference count taken on the policy instance
+ */
+ long pol_ref;
+ /**
+ * The NRS head this policy has been created at
+ */
+ struct ptlrpc_nrs *pol_nrs;
+ /**
+ * Private policy data; varies by policy type
+ */
+ void *pol_private;
+ /**
+ * Policy descriptor for this policy instance.
+ */
+ struct ptlrpc_nrs_pol_desc *pol_desc;
+};
+
+/**
+ * NRS resource
+ *
+ * Resources are embedded into two types of NRS entities:
+ * - Inside NRS policies, in the policy's private data in
+ * ptlrpc_nrs_policy::pol_private
+ * - In objects that act as prime-level scheduling entities in different NRS
+ * policies; e.g. on a policy that performs round robin or similar order
+ * scheduling across client NIDs, there would be one NRS resource per unique
+ * client NID. On a policy which performs round robin scheduling across
+ * backend filesystem objects, there would be one resource associated with
+ * each of the backend filesystem objects partaking in the scheduling
+ * performed by the policy.
+ *
+ * NRS resources share a parent-child relationship, in which resources embedded
+ * in policy instances are the parent entities, with all scheduling entities
+ * a policy schedules across being the children, thus forming a simple resource
+ * hierarchy. This hierarchy may be extended with one or more levels in the
+ * future if the ability to have more than one primary policy is added.
+ *
+ * Upon request initialization, references to the then active NRS policies are
+ * taken and used to later handle the dispatching of the request with one of
+ * these policies.
+ *
+ * \see nrs_resource_get_safe()
+ * \see ptlrpc_nrs_req_add()
+ */
+struct ptlrpc_nrs_resource {
+ /**
+ * This NRS resource's parent; is NULL for resources embedded in NRS
+ * policy instances; i.e. those are top-level ones.
+ */
+ struct ptlrpc_nrs_resource *res_parent;
+ /**
+ * The policy associated with this resource.
+ */
+ struct ptlrpc_nrs_policy *res_policy;
+};
+
+enum {
+ NRS_RES_FALLBACK,
+ NRS_RES_PRIMARY,
+ NRS_RES_MAX
+};
+
+/* \name fifo
+ *
+ * FIFO policy
+ *
+ * This policy is a logical wrapper around previous, non-NRS functionality.
+ * It dispatches RPCs in the same order as they arrive from the network. This
+ * policy is currently used as the fallback policy, and the only enabled policy
+ * on all NRS heads of all PTLRPC service partitions.
+ * @{
+ */
+
+/**
+ * Private data structure for the FIFO policy
+ */
+struct nrs_fifo_head {
+ /**
+ * Resource object for policy instance.
+ */
+ struct ptlrpc_nrs_resource fh_res;
+ /**
+ * List of queued requests.
+ */
+ struct list_head fh_list;
+ /**
+ * For debugging purposes.
+ */
+ __u64 fh_sequence;
+};
+
+struct nrs_fifo_req {
+ struct list_head fr_list;
+ __u64 fr_sequence;
+};
+
+/** @} fifo */
+
+/**
+ * NRS request
+ *
+ * Instances of this object exist embedded within ptlrpc_request; the main
+ * purpose of this object is to hold references to the request's resources
+ * for the lifetime of the request, and to hold properties that policies use
+ * use for determining the request's scheduling priority.
+ * */
+struct ptlrpc_nrs_request {
+ /**
+ * The request's resource hierarchy.
+ */
+ struct ptlrpc_nrs_resource *nr_res_ptrs[NRS_RES_MAX];
+ /**
+ * Index into ptlrpc_nrs_request::nr_res_ptrs of the resource of the
+ * policy that was used to enqueue the request.
+ *
+ * \see nrs_request_enqueue()
+ */
+ unsigned nr_res_idx;
+ unsigned nr_initialized:1;
+ unsigned nr_enqueued:1;
+ unsigned nr_started:1;
+ unsigned nr_finalized:1;
+
+ /**
+ * Policy-specific fields, used for determining a request's scheduling
+ * priority, and other supporting functionality.
+ */
+ union {
+ /**
+ * Fields for the FIFO policy
+ */
+ struct nrs_fifo_req fifo;
+ } nr_u;
+ /**
+ * Externally-registering policies may want to use this to allocate
+ * their own request properties.
+ */
+ void *ext;
+};
+
+/** @} nrs */
+
+/**
+ * Basic request prioritization operations structure.
+ * The whole idea is centered around locks and RPCs that might affect locks.
+ * When a lock is contended we try to give priority to RPCs that might lead
+ * to fastest release of that lock.
+ * Currently only implemented for OSTs only in a way that makes all
+ * IO and truncate RPCs that are coming from a locked region where a lock is
+ * contended a priority over other requests.
+ */
+struct ptlrpc_hpreq_ops {
+ /**
+ * Check if the lock handle of the given lock is the same as
+ * taken from the request.
+ */
+ int (*hpreq_lock_match)(struct ptlrpc_request *, struct ldlm_lock *);
+ /**
+ * Check if the request is a high priority one.
+ */
+ int (*hpreq_check)(struct ptlrpc_request *);
+ /**
+ * Called after the request has been handled.
+ */
+ void (*hpreq_fini)(struct ptlrpc_request *);
+};
+
+/**
+ * Represents remote procedure call.
+ *
+ * This is a staple structure used by everybody wanting to send a request
+ * in Lustre.
+ */
+struct ptlrpc_request {
+ /* Request type: one of PTL_RPC_MSG_* */
+ int rq_type;
+ /** Result of request processing */
+ int rq_status;
+ /**
+ * Linkage item through which this request is included into
+ * sending/delayed lists on client and into rqbd list on server
+ */
+ struct list_head rq_list;
+ /**
+ * Server side list of incoming unserved requests sorted by arrival
+ * time. Traversed from time to time to notice about to expire
+ * requests and sent back "early replies" to clients to let them
+ * know server is alive and well, just very busy to service their
+ * requests in time
+ */
+ struct list_head rq_timed_list;
+ /** server-side history, used for debugging purposes. */
+ struct list_head rq_history_list;
+ /** server-side per-export list */
+ struct list_head rq_exp_list;
+ /** server-side hp handlers */
+ struct ptlrpc_hpreq_ops *rq_ops;
+
+ /** initial thread servicing this request */
+ struct ptlrpc_thread *rq_svc_thread;
+
+ /** history sequence # */
+ __u64 rq_history_seq;
+ /** \addtogroup nrs
+ * @{
+ */
+ /** stub for NRS request */
+ struct ptlrpc_nrs_request rq_nrq;
+ /** @} nrs */
+ /** the index of service's srv_at_array into which request is linked */
+ time_t rq_at_index;
+ /** Lock to protect request flags and some other important bits, like
+ * rq_list
+ */
+ spinlock_t rq_lock;
+ /** client-side flags are serialized by rq_lock */
+ unsigned int rq_intr:1, rq_replied:1, rq_err:1,
+ rq_timedout:1, rq_resend:1, rq_restart:1,
+ /**
+ * when ->rq_replay is set, request is kept by the client even
+ * after server commits corresponding transaction. This is
+ * used for operations that require sequence of multiple
+ * requests to be replayed. The only example currently is file
+ * open/close. When last request in such a sequence is
+ * committed, ->rq_replay is cleared on all requests in the
+ * sequence.
+ */
+ rq_replay:1,
+ rq_no_resend:1, rq_waiting:1, rq_receiving_reply:1,
+ rq_no_delay:1, rq_net_err:1, rq_wait_ctx:1,
+ rq_early:1,
+ rq_req_unlink:1, rq_reply_unlink:1,
+ rq_memalloc:1, /* req originated from "kswapd" */
+ /* server-side flags */
+ rq_packed_final:1, /* packed final reply */
+ rq_hp:1, /* high priority RPC */
+ rq_at_linked:1, /* link into service's srv_at_array */
+ rq_reply_truncate:1,
+ rq_committed:1,
+ /* whether the "rq_set" is a valid one */
+ rq_invalid_rqset:1,
+ rq_generation_set:1,
+ /* do not resend request on -EINPROGRESS */
+ rq_no_retry_einprogress:1,
+ /* allow the req to be sent if the import is in recovery
+ * status */
+ rq_allow_replay:1;
+
+ unsigned int rq_nr_resend;
+
+ enum rq_phase rq_phase; /* one of RQ_PHASE_* */
+ enum rq_phase rq_next_phase; /* one of RQ_PHASE_* to be used next */
+ atomic_t rq_refcount;/* client-side refcount for SENT race,
+ server-side refcount for multiple replies */
+
+ /** Portal to which this request would be sent */
+ short rq_request_portal; /* XXX FIXME bug 249 */
+ /** Portal where to wait for reply and where reply would be sent */
+ short rq_reply_portal; /* XXX FIXME bug 249 */
+
+ /**
+ * client-side:
+ * !rq_truncate : # reply bytes actually received,
+ * rq_truncate : required repbuf_len for resend
+ */
+ int rq_nob_received;
+ /** Request length */
+ int rq_reqlen;
+ /** Reply length */
+ int rq_replen;
+ /** Request message - what client sent */
+ struct lustre_msg *rq_reqmsg;
+ /** Reply message - server response */
+ struct lustre_msg *rq_repmsg;
+ /** Transaction number */
+ __u64 rq_transno;
+ /** xid */
+ __u64 rq_xid;
+ /**
+ * List item to for replay list. Not yet committed requests get linked
+ * there.
+ * Also see \a rq_replay comment above.
+ */
+ struct list_head rq_replay_list;
+
+ /**
+ * security and encryption data
+ * @{ */
+ struct ptlrpc_cli_ctx *rq_cli_ctx; /**< client's half ctx */
+ struct ptlrpc_svc_ctx *rq_svc_ctx; /**< server's half ctx */
+ struct list_head rq_ctx_chain; /**< link to waited ctx */
+
+ struct sptlrpc_flavor rq_flvr; /**< for client & server */
+ enum lustre_sec_part rq_sp_from;
+
+ /* client/server security flags */
+ unsigned int
+ rq_ctx_init:1, /* context initiation */
+ rq_ctx_fini:1, /* context destroy */
+ rq_bulk_read:1, /* request bulk read */
+ rq_bulk_write:1, /* request bulk write */
+ /* server authentication flags */
+ rq_auth_gss:1, /* authenticated by gss */
+ rq_auth_remote:1, /* authed as remote user */
+ rq_auth_usr_root:1, /* authed as root */
+ rq_auth_usr_mdt:1, /* authed as mdt */
+ rq_auth_usr_ost:1, /* authed as ost */
+ /* security tfm flags */
+ rq_pack_udesc:1,
+ rq_pack_bulk:1,
+ /* doesn't expect reply FIXME */
+ rq_no_reply:1,
+ rq_pill_init:1; /* pill initialized */
+
+ uid_t rq_auth_uid; /* authed uid */
+ uid_t rq_auth_mapped_uid; /* authed uid mapped to */
+
+ /* (server side), pointed directly into req buffer */
+ struct ptlrpc_user_desc *rq_user_desc;
+
+ /* various buffer pointers */
+ struct lustre_msg *rq_reqbuf; /* req wrapper */
+ char *rq_repbuf; /* rep buffer */
+ struct lustre_msg *rq_repdata; /* rep wrapper msg */
+ struct lustre_msg *rq_clrbuf; /* only in priv mode */
+ int rq_reqbuf_len; /* req wrapper buf len */
+ int rq_reqdata_len; /* req wrapper msg len */
+ int rq_repbuf_len; /* rep buffer len */
+ int rq_repdata_len; /* rep wrapper msg len */
+ int rq_clrbuf_len; /* only in priv mode */
+ int rq_clrdata_len; /* only in priv mode */
+
+ /** early replies go to offset 0, regular replies go after that */
+ unsigned int rq_reply_off;
+
+ /** @} */
+
+ /** Fields that help to see if request and reply were swabbed or not */
+ __u32 rq_req_swab_mask;
+ __u32 rq_rep_swab_mask;
+
+ /** What was import generation when this request was sent */
+ int rq_import_generation;
+ enum lustre_imp_state rq_send_state;
+
+ /** how many early replies (for stats) */
+ int rq_early_count;
+
+ /** client+server request */
+ lnet_handle_md_t rq_req_md_h;
+ struct ptlrpc_cb_id rq_req_cbid;
+ /** optional time limit for send attempts */
+ long rq_delay_limit;
+ /** time request was first queued */
+ unsigned long rq_queued_time;
+
+ /* server-side... */
+ /** request arrival time */
+ struct timeval rq_arrival_time;
+ /** separated reply state */
+ struct ptlrpc_reply_state *rq_reply_state;
+ /** incoming request buffer */
+ struct ptlrpc_request_buffer_desc *rq_rqbd;
+
+ /** client-only incoming reply */
+ lnet_handle_md_t rq_reply_md_h;
+ wait_queue_head_t rq_reply_waitq;
+ struct ptlrpc_cb_id rq_reply_cbid;
+
+ /** our LNet NID */
+ lnet_nid_t rq_self;
+ /** Peer description (the other side) */
+ lnet_process_id_t rq_peer;
+ /** Server-side, export on which request was received */
+ struct obd_export *rq_export;
+ /** Client side, import where request is being sent */
+ struct obd_import *rq_import;
+
+ /** Replay callback, called after request is replayed at recovery */
+ void (*rq_replay_cb)(struct ptlrpc_request *);
+ /**
+ * Commit callback, called when request is committed and about to be
+ * freed.
+ */
+ void (*rq_commit_cb)(struct ptlrpc_request *);
+ /** Opaq data for replay and commit callbacks. */
+ void *rq_cb_data;
+
+ /** For bulk requests on client only: bulk descriptor */
+ struct ptlrpc_bulk_desc *rq_bulk;
+
+ /** client outgoing req */
+ /**
+ * when request/reply sent (secs), or time when request should be sent
+ */
+ time_t rq_sent;
+ /** time for request really sent out */
+ time_t rq_real_sent;
+
+ /** when request must finish. volatile
+ * so that servers' early reply updates to the deadline aren't
+ * kept in per-cpu cache */
+ volatile time_t rq_deadline;
+ /** when req reply unlink must finish. */
+ time_t rq_reply_deadline;
+ /** when req bulk unlink must finish. */
+ time_t rq_bulk_deadline;
+ /**
+ * service time estimate (secs)
+ * If the requestsis not served by this time, it is marked as timed out.
+ */
+ int rq_timeout;
+
+ /** Multi-rpc bits */
+ /** Per-request waitq introduced by bug 21938 for recovery waiting */
+ wait_queue_head_t rq_set_waitq;
+ /** Link item for request set lists */
+ struct list_head rq_set_chain;
+ /** Link back to the request set */
+ struct ptlrpc_request_set *rq_set;
+ /** Async completion handler, called when reply is received */
+ ptlrpc_interpterer_t rq_interpret_reply;
+ /** Async completion context */
+ union ptlrpc_async_args rq_async_args;
+
+ /** Pool if request is from preallocated list */
+ struct ptlrpc_request_pool *rq_pool;
+
+ struct lu_context rq_session;
+ struct lu_context rq_recov_session;
+
+ /** request format description */
+ struct req_capsule rq_pill;
+};
+
+/**
+ * Call completion handler for rpc if any, return it's status or original
+ * rc if there was no handler defined for this request.
+ */
+static inline int ptlrpc_req_interpret(const struct lu_env *env,
+ struct ptlrpc_request *req, int rc)
+{
+ if (req->rq_interpret_reply != NULL) {
+ req->rq_status = req->rq_interpret_reply(env, req,
+ &req->rq_async_args,
+ rc);
+ return req->rq_status;
+ }
+ return rc;
+}
+
+/** \addtogroup nrs
+ * @{
+ */
+int ptlrpc_nrs_policy_register(struct ptlrpc_nrs_pol_conf *conf);
+int ptlrpc_nrs_policy_unregister(struct ptlrpc_nrs_pol_conf *conf);
+void ptlrpc_nrs_req_hp_move(struct ptlrpc_request *req);
+void nrs_policy_get_info_locked(struct ptlrpc_nrs_policy *policy,
+ struct ptlrpc_nrs_pol_info *info);
+
+/*
+ * Can the request be moved from the regular NRS head to the high-priority NRS
+ * head (of the same PTLRPC service partition), if any?
+ *
+ * For a reliable result, this should be checked under svcpt->scp_req lock.
+ */
+static inline bool ptlrpc_nrs_req_can_move(struct ptlrpc_request *req)
+{
+ struct ptlrpc_nrs_request *nrq = &req->rq_nrq;
+
+ /**
+ * LU-898: Check ptlrpc_nrs_request::nr_enqueued to make sure the
+ * request has been enqueued first, and ptlrpc_nrs_request::nr_started
+ * to make sure it has not been scheduled yet (analogous to previous
+ * (non-NRS) checking of !list_empty(&ptlrpc_request::rq_list).
+ */
+ return nrq->nr_enqueued && !nrq->nr_started && !req->rq_hp;
+}
+/** @} nrs */
+
+/**
+ * Returns 1 if request buffer at offset \a index was already swabbed
+ */
+static inline int lustre_req_swabbed(struct ptlrpc_request *req, int index)
+{
+ LASSERT(index < sizeof(req->rq_req_swab_mask) * 8);
+ return req->rq_req_swab_mask & (1 << index);
+}
+
+/**
+ * Returns 1 if request reply buffer at offset \a index was already swabbed
+ */
+static inline int lustre_rep_swabbed(struct ptlrpc_request *req, int index)
+{
+ LASSERT(index < sizeof(req->rq_rep_swab_mask) * 8);
+ return req->rq_rep_swab_mask & (1 << index);
+}
+
+/**
+ * Returns 1 if request needs to be swabbed into local cpu byteorder
+ */
+static inline int ptlrpc_req_need_swab(struct ptlrpc_request *req)
+{
+ return lustre_req_swabbed(req, MSG_PTLRPC_HEADER_OFF);
+}
+
+/**
+ * Returns 1 if request reply needs to be swabbed into local cpu byteorder
+ */
+static inline int ptlrpc_rep_need_swab(struct ptlrpc_request *req)
+{
+ return lustre_rep_swabbed(req, MSG_PTLRPC_HEADER_OFF);
+}
+
+/**
+ * Mark request buffer at offset \a index that it was already swabbed
+ */
+static inline void lustre_set_req_swabbed(struct ptlrpc_request *req, int index)
+{
+ LASSERT(index < sizeof(req->rq_req_swab_mask) * 8);
+ LASSERT((req->rq_req_swab_mask & (1 << index)) == 0);
+ req->rq_req_swab_mask |= 1 << index;
+}
+
+/**
+ * Mark request reply buffer at offset \a index that it was already swabbed
+ */
+static inline void lustre_set_rep_swabbed(struct ptlrpc_request *req, int index)
+{
+ LASSERT(index < sizeof(req->rq_rep_swab_mask) * 8);
+ LASSERT((req->rq_rep_swab_mask & (1 << index)) == 0);
+ req->rq_rep_swab_mask |= 1 << index;
+}
+
+/**
+ * Convert numerical request phase value \a phase into text string description
+ */
+static inline const char *
+ptlrpc_phase2str(enum rq_phase phase)
+{
+ switch (phase) {
+ case RQ_PHASE_NEW:
+ return "New";
+ case RQ_PHASE_RPC:
+ return "Rpc";
+ case RQ_PHASE_BULK:
+ return "Bulk";
+ case RQ_PHASE_INTERPRET:
+ return "Interpret";
+ case RQ_PHASE_COMPLETE:
+ return "Complete";
+ case RQ_PHASE_UNREGISTERING:
+ return "Unregistering";
+ default:
+ return "?Phase?";
+ }
+}
+
+/**
+ * Convert numerical request phase of the request \a req into text stringi
+ * description
+ */
+static inline const char *
+ptlrpc_rqphase2str(struct ptlrpc_request *req)
+{
+ return ptlrpc_phase2str(req->rq_phase);
+}
+
+/**
+ * Debugging functions and helpers to print request structure into debug log
+ * @{
+ */
+/* Spare the preprocessor, spoil the bugs. */
+#define FLAG(field, str) (field ? str : "")
+
+/** Convert bit flags into a string */
+#define DEBUG_REQ_FLAGS(req) \
+ ptlrpc_rqphase2str(req), \
+ FLAG(req->rq_intr, "I"), FLAG(req->rq_replied, "R"), \
+ FLAG(req->rq_err, "E"), \
+ FLAG(req->rq_timedout, "X") /* eXpired */, FLAG(req->rq_resend, "S"), \
+ FLAG(req->rq_restart, "T"), FLAG(req->rq_replay, "P"), \
+ FLAG(req->rq_no_resend, "N"), \
+ FLAG(req->rq_waiting, "W"), \
+ FLAG(req->rq_wait_ctx, "C"), FLAG(req->rq_hp, "H"), \
+ FLAG(req->rq_committed, "M")
+
+#define REQ_FLAGS_FMT "%s:%s%s%s%s%s%s%s%s%s%s%s%s"
+
+void _debug_req(struct ptlrpc_request *req,
+ struct libcfs_debug_msg_data *data, const char *fmt, ...)
+ __printf(3, 4);
+
+/**
+ * Helper that decides if we need to print request according to current debug
+ * level settings
+ */
+#define debug_req(msgdata, mask, cdls, req, fmt, a...) \
+do { \
+ CFS_CHECK_STACK(msgdata, mask, cdls); \
+ \
+ if (((mask) & D_CANTMASK) != 0 || \
+ ((libcfs_debug & (mask)) != 0 && \
+ (libcfs_subsystem_debug & DEBUG_SUBSYSTEM) != 0)) \
+ _debug_req((req), msgdata, fmt, ##a); \
+} while (0)
+
+/**
+ * This is the debug print function you need to use to print request structure
+ * content into lustre debug log.
+ * for most callers (level is a constant) this is resolved at compile time */
+#define DEBUG_REQ(level, req, fmt, args...) \
+do { \
+ if ((level) & (D_ERROR | D_WARNING)) { \
+ static struct cfs_debug_limit_state cdls; \
+ LIBCFS_DEBUG_MSG_DATA_DECL(msgdata, level, &cdls); \
+ debug_req(&msgdata, level, &cdls, req, "@@@ "fmt" ", ## args);\
+ } else { \
+ LIBCFS_DEBUG_MSG_DATA_DECL(msgdata, level, NULL); \
+ debug_req(&msgdata, level, NULL, req, "@@@ "fmt" ", ## args); \
+ } \
+} while (0)
+/** @} */
+
+/**
+ * Structure that defines a single page of a bulk transfer
+ */
+struct ptlrpc_bulk_page {
+ /** Linkage to list of pages in a bulk */
+ struct list_head bp_link;
+ /**
+ * Number of bytes in a page to transfer starting from \a bp_pageoffset
+ */
+ int bp_buflen;
+ /** offset within a page */
+ int bp_pageoffset;
+ /** The page itself */
+ struct page *bp_page;
+};
+
+#define BULK_GET_SOURCE 0
+#define BULK_PUT_SINK 1
+#define BULK_GET_SINK 2
+#define BULK_PUT_SOURCE 3
+
+/**
+ * Definition of bulk descriptor.
+ * Bulks are special "Two phase" RPCs where initial request message
+ * is sent first and it is followed bt a transfer (o receiving) of a large
+ * amount of data to be settled into pages referenced from the bulk descriptors.
+ * Bulks transfers (the actual data following the small requests) are done
+ * on separate LNet portals.
+ * In lustre we use bulk transfers for READ and WRITE transfers from/to OSTs.
+ * Another user is readpage for MDT.
+ */
+struct ptlrpc_bulk_desc {
+ /** completed with failure */
+ unsigned long bd_failure:1;
+ /** {put,get}{source,sink} */
+ unsigned long bd_type:2;
+ /** client side */
+ unsigned long bd_registered:1;
+ /** For serialization with callback */
+ spinlock_t bd_lock;
+ /** Import generation when request for this bulk was sent */
+ int bd_import_generation;
+ /** LNet portal for this bulk */
+ __u32 bd_portal;
+ /** Server side - export this bulk created for */
+ struct obd_export *bd_export;
+ /** Client side - import this bulk was sent on */
+ struct obd_import *bd_import;
+ /** Back pointer to the request */
+ struct ptlrpc_request *bd_req;
+ wait_queue_head_t bd_waitq; /* server side only WQ */
+ int bd_iov_count; /* # entries in bd_iov */
+ int bd_max_iov; /* allocated size of bd_iov */
+ int bd_nob; /* # bytes covered */
+ int bd_nob_transferred; /* # bytes GOT/PUT */
+
+ __u64 bd_last_xid;
+
+ struct ptlrpc_cb_id bd_cbid; /* network callback info */
+ lnet_nid_t bd_sender; /* stash event::sender */
+ int bd_md_count; /* # valid entries in bd_mds */
+ int bd_md_max_brw; /* max entries in bd_mds */
+ /** array of associated MDs */
+ lnet_handle_md_t bd_mds[PTLRPC_BULK_OPS_COUNT];
+
+ /*
+ * encrypt iov, size is either 0 or bd_iov_count.
+ */
+ lnet_kiov_t *bd_enc_iov;
+
+ lnet_kiov_t bd_iov[0];
+};
+
+enum {
+ SVC_STOPPED = 1 << 0,
+ SVC_STOPPING = 1 << 1,
+ SVC_STARTING = 1 << 2,
+ SVC_RUNNING = 1 << 3,
+ SVC_EVENT = 1 << 4,
+ SVC_SIGNAL = 1 << 5,
+};
+
+#define PTLRPC_THR_NAME_LEN 32
+/**
+ * Definition of server service thread structure
+ */
+struct ptlrpc_thread {
+ /**
+ * List of active threads in svc->srv_threads
+ */
+ struct list_head t_link;
+ /**
+ * thread-private data (preallocated memory)
+ */
+ void *t_data;
+ __u32 t_flags;
+ /**
+ * service thread index, from ptlrpc_start_threads
+ */
+ unsigned int t_id;
+ /**
+ * service thread pid
+ */
+ pid_t t_pid;
+ /**
+ * put watchdog in the structure per thread b=14840
+ *
+ * Lustre watchdog is removed for client in the hope
+ * of a generic watchdog can be merged in kernel.
+ * When that happens, we should add below back.
+ *
+ * struct lc_watchdog *t_watchdog;
+ */
+ /**
+ * the svc this thread belonged to b=18582
+ */
+ struct ptlrpc_service_part *t_svcpt;
+ wait_queue_head_t t_ctl_waitq;
+ struct lu_env *t_env;
+ char t_name[PTLRPC_THR_NAME_LEN];
+};
+
+static inline int thread_is_init(struct ptlrpc_thread *thread)
+{
+ return thread->t_flags == 0;
+}
+
+static inline int thread_is_stopped(struct ptlrpc_thread *thread)
+{
+ return !!(thread->t_flags & SVC_STOPPED);
+}
+
+static inline int thread_is_stopping(struct ptlrpc_thread *thread)
+{
+ return !!(thread->t_flags & SVC_STOPPING);
+}
+
+static inline int thread_is_starting(struct ptlrpc_thread *thread)
+{
+ return !!(thread->t_flags & SVC_STARTING);
+}
+
+static inline int thread_is_running(struct ptlrpc_thread *thread)
+{
+ return !!(thread->t_flags & SVC_RUNNING);
+}
+
+static inline int thread_is_event(struct ptlrpc_thread *thread)
+{
+ return !!(thread->t_flags & SVC_EVENT);
+}
+
+static inline int thread_is_signal(struct ptlrpc_thread *thread)
+{
+ return !!(thread->t_flags & SVC_SIGNAL);
+}
+
+static inline void thread_clear_flags(struct ptlrpc_thread *thread, __u32 flags)
+{
+ thread->t_flags &= ~flags;
+}
+
+static inline void thread_set_flags(struct ptlrpc_thread *thread, __u32 flags)
+{
+ thread->t_flags = flags;
+}
+
+static inline void thread_add_flags(struct ptlrpc_thread *thread, __u32 flags)
+{
+ thread->t_flags |= flags;
+}
+
+static inline int thread_test_and_clear_flags(struct ptlrpc_thread *thread,
+ __u32 flags)
+{
+ if (thread->t_flags & flags) {
+ thread->t_flags &= ~flags;
+ return 1;
+ }
+ return 0;
+}
+
+/**
+ * Request buffer descriptor structure.
+ * This is a structure that contains one posted request buffer for service.
+ * Once data land into a buffer, event callback creates actual request and
+ * notifies wakes one of the service threads to process new incoming request.
+ * More than one request can fit into the buffer.
+ */
+struct ptlrpc_request_buffer_desc {
+ /** Link item for rqbds on a service */
+ struct list_head rqbd_list;
+ /** History of requests for this buffer */
+ struct list_head rqbd_reqs;
+ /** Back pointer to service for which this buffer is registered */
+ struct ptlrpc_service_part *rqbd_svcpt;
+ /** LNet descriptor */
+ lnet_handle_md_t rqbd_md_h;
+ int rqbd_refcount;
+ /** The buffer itself */
+ char *rqbd_buffer;
+ struct ptlrpc_cb_id rqbd_cbid;
+ /**
+ * This "embedded" request structure is only used for the
+ * last request to fit into the buffer
+ */
+ struct ptlrpc_request rqbd_req;
+};
+
+typedef int (*svc_handler_t)(struct ptlrpc_request *req);
+
+struct ptlrpc_service_ops {
+ /**
+ * if non-NULL called during thread creation (ptlrpc_start_thread())
+ * to initialize service specific per-thread state.
+ */
+ int (*so_thr_init)(struct ptlrpc_thread *thr);
+ /**
+ * if non-NULL called during thread shutdown (ptlrpc_main()) to
+ * destruct state created by ->srv_init().
+ */
+ void (*so_thr_done)(struct ptlrpc_thread *thr);
+ /**
+ * Handler function for incoming requests for this service
+ */
+ int (*so_req_handler)(struct ptlrpc_request *req);
+ /**
+ * function to determine priority of the request, it's called
+ * on every new request
+ */
+ int (*so_hpreq_handler)(struct ptlrpc_request *);
+ /**
+ * service-specific print fn
+ */
+ void (*so_req_printer)(void *, struct ptlrpc_request *);
+};
+
+#ifndef __cfs_cacheline_aligned
+/* NB: put it here for reducing patche dependence */
+# define __cfs_cacheline_aligned
+#endif
+
+/**
+ * How many high priority requests to serve before serving one normal
+ * priority request
+ */
+#define PTLRPC_SVC_HP_RATIO 10
+
+/**
+ * Definition of PortalRPC service.
+ * The service is listening on a particular portal (like tcp port)
+ * and perform actions for a specific server like IO service for OST
+ * or general metadata service for MDS.
+ */
+struct ptlrpc_service {
+ /** serialize /proc operations */
+ spinlock_t srv_lock;
+ /** most often accessed fields */
+ /** chain thru all services */
+ struct list_head srv_list;
+ /** service operations table */
+ struct ptlrpc_service_ops srv_ops;
+ /** only statically allocated strings here; we don't clean them */
+ char *srv_name;
+ /** only statically allocated strings here; we don't clean them */
+ char *srv_thread_name;
+ /** service thread list */
+ struct list_head srv_threads;
+ /** threads # should be created for each partition on initializing */
+ int srv_nthrs_cpt_init;
+ /** limit of threads number for each partition */
+ int srv_nthrs_cpt_limit;
+ /** Root of /proc dir tree for this service */
+ struct proc_dir_entry *srv_procroot;
+ /** Pointer to statistic data for this service */
+ struct lprocfs_stats *srv_stats;
+ /** # hp per lp reqs to handle */
+ int srv_hpreq_ratio;
+ /** biggest request to receive */
+ int srv_max_req_size;
+ /** biggest reply to send */
+ int srv_max_reply_size;
+ /** size of individual buffers */
+ int srv_buf_size;
+ /** # buffers to allocate in 1 group */
+ int srv_nbuf_per_group;
+ /** Local portal on which to receive requests */
+ __u32 srv_req_portal;
+ /** Portal on the client to send replies to */
+ __u32 srv_rep_portal;
+ /**
+ * Tags for lu_context associated with this thread, see struct
+ * lu_context.
+ */
+ __u32 srv_ctx_tags;
+ /** soft watchdog timeout multiplier */
+ int srv_watchdog_factor;
+ /** under unregister_service */
+ unsigned srv_is_stopping:1;
+
+ /** max # request buffers in history per partition */
+ int srv_hist_nrqbds_cpt_max;
+ /** number of CPTs this service bound on */
+ int srv_ncpts;
+ /** CPTs array this service bound on */
+ __u32 *srv_cpts;
+ /** 2^srv_cptab_bits >= cfs_cpt_numbert(srv_cptable) */
+ int srv_cpt_bits;
+ /** CPT table this service is running over */
+ struct cfs_cpt_table *srv_cptable;
+ /**
+ * partition data for ptlrpc service
+ */
+ struct ptlrpc_service_part *srv_parts[0];
+};
+
+/**
+ * Definition of PortalRPC service partition data.
+ * Although a service only has one instance of it right now, but we
+ * will have multiple instances very soon (instance per CPT).
+ *
+ * it has four locks:
+ * \a scp_lock
+ * serialize operations on rqbd and requests waiting for preprocess
+ * \a scp_req_lock
+ * serialize operations active requests sent to this portal
+ * \a scp_at_lock
+ * serialize adaptive timeout stuff
+ * \a scp_rep_lock
+ * serialize operations on RS list (reply states)
+ *
+ * We don't have any use-case to take two or more locks at the same time
+ * for now, so there is no lock order issue.
+ */
+struct ptlrpc_service_part {
+ /** back reference to owner */
+ struct ptlrpc_service *scp_service __cfs_cacheline_aligned;
+ /* CPT id, reserved */
+ int scp_cpt;
+ /** always increasing number */
+ int scp_thr_nextid;
+ /** # of starting threads */
+ int scp_nthrs_starting;
+ /** # of stopping threads, reserved for shrinking threads */
+ int scp_nthrs_stopping;
+ /** # running threads */
+ int scp_nthrs_running;
+ /** service threads list */
+ struct list_head scp_threads;
+
+ /**
+ * serialize the following fields, used for protecting
+ * rqbd list and incoming requests waiting for preprocess,
+ * threads starting & stopping are also protected by this lock.
+ */
+ spinlock_t scp_lock __cfs_cacheline_aligned;
+ /** total # req buffer descs allocated */
+ int scp_nrqbds_total;
+ /** # posted request buffers for receiving */
+ int scp_nrqbds_posted;
+ /** in progress of allocating rqbd */
+ int scp_rqbd_allocating;
+ /** # incoming reqs */
+ int scp_nreqs_incoming;
+ /** request buffers to be reposted */
+ struct list_head scp_rqbd_idle;
+ /** req buffers receiving */
+ struct list_head scp_rqbd_posted;
+ /** incoming reqs */
+ struct list_head scp_req_incoming;
+ /** timeout before re-posting reqs, in tick */
+ long scp_rqbd_timeout;
+ /**
+ * all threads sleep on this. This wait-queue is signalled when new
+ * incoming request arrives and when difficult reply has to be handled.
+ */
+ wait_queue_head_t scp_waitq;
+
+ /** request history */
+ struct list_head scp_hist_reqs;
+ /** request buffer history */
+ struct list_head scp_hist_rqbds;
+ /** # request buffers in history */
+ int scp_hist_nrqbds;
+ /** sequence number for request */
+ __u64 scp_hist_seq;
+ /** highest seq culled from history */
+ __u64 scp_hist_seq_culled;
+
+ /**
+ * serialize the following fields, used for processing requests
+ * sent to this portal
+ */
+ spinlock_t scp_req_lock __cfs_cacheline_aligned;
+ /** # reqs in either of the NRS heads below */
+ /** # reqs being served */
+ int scp_nreqs_active;
+ /** # HPreqs being served */
+ int scp_nhreqs_active;
+ /** # hp requests handled */
+ int scp_hreq_count;
+
+ /** NRS head for regular requests */
+ struct ptlrpc_nrs scp_nrs_reg;
+ /** NRS head for HP requests; this is only valid for services that can
+ * handle HP requests */
+ struct ptlrpc_nrs *scp_nrs_hp;
+
+ /** AT stuff */
+ /** @{ */
+ /**
+ * serialize the following fields, used for changes on
+ * adaptive timeout
+ */
+ spinlock_t scp_at_lock __cfs_cacheline_aligned;
+ /** estimated rpc service time */
+ struct adaptive_timeout scp_at_estimate;
+ /** reqs waiting for replies */
+ struct ptlrpc_at_array scp_at_array;
+ /** early reply timer */
+ struct timer_list scp_at_timer;
+ /** debug */
+ unsigned long scp_at_checktime;
+ /** check early replies */
+ unsigned scp_at_check;
+ /** @} */
+
+ /**
+ * serialize the following fields, used for processing
+ * replies for this portal
+ */
+ spinlock_t scp_rep_lock __cfs_cacheline_aligned;
+ /** all the active replies */
+ struct list_head scp_rep_active;
+ /** List of free reply_states */
+ struct list_head scp_rep_idle;
+ /** waitq to run, when adding stuff to srv_free_rs_list */
+ wait_queue_head_t scp_rep_waitq;
+ /** # 'difficult' replies */
+ atomic_t scp_nreps_difficult;
+};
+
+#define ptlrpc_service_for_each_part(part, i, svc) \
+ for (i = 0; \
+ i < (svc)->srv_ncpts && \
+ (svc)->srv_parts != NULL && \
+ ((part) = (svc)->srv_parts[i]) != NULL; i++)
+
+/**
+ * Declaration of ptlrpcd control structure
+ */
+struct ptlrpcd_ctl {
+ /**
+ * Ptlrpc thread control flags (LIOD_START, LIOD_STOP, LIOD_FORCE)
+ */
+ unsigned long pc_flags;
+ /**
+ * Thread lock protecting structure fields.
+ */
+ spinlock_t pc_lock;
+ /**
+ * Start completion.
+ */
+ struct completion pc_starting;
+ /**
+ * Stop completion.
+ */
+ struct completion pc_finishing;
+ /**
+ * Thread requests set.
+ */
+ struct ptlrpc_request_set *pc_set;
+ /**
+ * Thread name used in cfs_daemonize()
+ */
+ char pc_name[16];
+ /**
+ * Environment for request interpreters to run in.
+ */
+ struct lu_env pc_env;
+ /**
+ * Index of ptlrpcd thread in the array.
+ */
+ int pc_index;
+ /**
+ * Number of the ptlrpcd's partners.
+ */
+ int pc_npartners;
+ /**
+ * Pointer to the array of partners' ptlrpcd_ctl structure.
+ */
+ struct ptlrpcd_ctl **pc_partners;
+ /**
+ * Record the partner index to be processed next.
+ */
+ int pc_cursor;
+};
+
+/* Bits for pc_flags */
+enum ptlrpcd_ctl_flags {
+ /**
+ * Ptlrpc thread start flag.
+ */
+ LIOD_START = 1 << 0,
+ /**
+ * Ptlrpc thread stop flag.
+ */
+ LIOD_STOP = 1 << 1,
+ /**
+ * Ptlrpc thread force flag (only stop force so far).
+ * This will cause aborting any inflight rpcs handled
+ * by thread if LIOD_STOP is specified.
+ */
+ LIOD_FORCE = 1 << 2,
+ /**
+ * This is a recovery ptlrpc thread.
+ */
+ LIOD_RECOVERY = 1 << 3,
+ /**
+ * The ptlrpcd is bound to some CPU core.
+ */
+ LIOD_BIND = 1 << 4,
+};
+
+/**
+ * \addtogroup nrs
+ * @{
+ *
+ * Service compatibility function; the policy is compatible with all services.
+ *
+ * \param[in] svc The service the policy is attempting to register with.
+ * \param[in] desc The policy descriptor
+ *
+ * \retval true The policy is compatible with the service
+ *
+ * \see ptlrpc_nrs_pol_desc::pd_compat()
+ */
+static inline bool nrs_policy_compat_all(const struct ptlrpc_service *svc,
+ const struct ptlrpc_nrs_pol_desc *desc)
+{
+ return true;
+}
+
+/**
+ * Service compatibility function; the policy is compatible with only a specific
+ * service which is identified by its human-readable name at
+ * ptlrpc_service::srv_name.
+ *
+ * \param[in] svc The service the policy is attempting to register with.
+ * \param[in] desc The policy descriptor
+ *
+ * \retval false The policy is not compatible with the service
+ * \retval true The policy is compatible with the service
+ *
+ * \see ptlrpc_nrs_pol_desc::pd_compat()
+ */
+static inline bool nrs_policy_compat_one(const struct ptlrpc_service *svc,
+ const struct ptlrpc_nrs_pol_desc *desc)
+{
+ LASSERT(desc->pd_compat_svc_name != NULL);
+ return strcmp(svc->srv_name, desc->pd_compat_svc_name) == 0;
+}
+
+/** @} nrs */
+
+/* ptlrpc/events.c */
+extern lnet_handle_eq_t ptlrpc_eq_h;
+extern int ptlrpc_uuid_to_peer(struct obd_uuid *uuid,
+ lnet_process_id_t *peer, lnet_nid_t *self);
+/**
+ * These callbacks are invoked by LNet when something happened to
+ * underlying buffer
+ * @{
+ */
+extern void request_out_callback(lnet_event_t *ev);
+extern void reply_in_callback(lnet_event_t *ev);
+extern void client_bulk_callback(lnet_event_t *ev);
+extern void request_in_callback(lnet_event_t *ev);
+extern void reply_out_callback(lnet_event_t *ev);
+/** @} */
+
+/* ptlrpc/connection.c */
+struct ptlrpc_connection *ptlrpc_connection_get(lnet_process_id_t peer,
+ lnet_nid_t self,
+ struct obd_uuid *uuid);
+int ptlrpc_connection_put(struct ptlrpc_connection *c);
+struct ptlrpc_connection *ptlrpc_connection_addref(struct ptlrpc_connection *);
+int ptlrpc_connection_init(void);
+void ptlrpc_connection_fini(void);
+extern lnet_pid_t ptl_get_pid(void);
+
+/* ptlrpc/niobuf.c */
+/**
+ * Actual interfacing with LNet to put/get/register/unregister stuff
+ * @{
+ */
+
+int ptlrpc_register_bulk(struct ptlrpc_request *req);
+int ptlrpc_unregister_bulk(struct ptlrpc_request *req, int async);
+
+static inline int ptlrpc_client_bulk_active(struct ptlrpc_request *req)
+{
+ struct ptlrpc_bulk_desc *desc;
+ int rc;
+
+ LASSERT(req != NULL);
+ desc = req->rq_bulk;
+
+ if (OBD_FAIL_CHECK(OBD_FAIL_PTLRPC_LONG_BULK_UNLINK) &&
+ req->rq_bulk_deadline > get_seconds())
+ return 1;
+
+ if (!desc)
+ return 0;
+
+ spin_lock(&desc->bd_lock);
+ rc = desc->bd_md_count;
+ spin_unlock(&desc->bd_lock);
+ return rc;
+}
+
+#define PTLRPC_REPLY_MAYBE_DIFFICULT 0x01
+#define PTLRPC_REPLY_EARLY 0x02
+int ptlrpc_send_reply(struct ptlrpc_request *req, int flags);
+int ptlrpc_reply(struct ptlrpc_request *req);
+int ptlrpc_send_error(struct ptlrpc_request *req, int difficult);
+int ptlrpc_error(struct ptlrpc_request *req);
+void ptlrpc_resend_req(struct ptlrpc_request *request);
+int ptlrpc_at_get_net_latency(struct ptlrpc_request *req);
+int ptl_send_rpc(struct ptlrpc_request *request, int noreply);
+int ptlrpc_register_rqbd(struct ptlrpc_request_buffer_desc *rqbd);
+/** @} */
+
+/* ptlrpc/client.c */
+/**
+ * Client-side portals API. Everything to send requests, receive replies,
+ * request queues, request management, etc.
+ * @{
+ */
+void ptlrpc_request_committed(struct ptlrpc_request *req, int force);
+
+void ptlrpc_init_client(int req_portal, int rep_portal, char *name,
+ struct ptlrpc_client *);
+void ptlrpc_cleanup_client(struct obd_import *imp);
+struct ptlrpc_connection *ptlrpc_uuid_to_connection(struct obd_uuid *uuid);
+
+int ptlrpc_queue_wait(struct ptlrpc_request *req);
+int ptlrpc_replay_req(struct ptlrpc_request *req);
+int ptlrpc_unregister_reply(struct ptlrpc_request *req, int async);
+void ptlrpc_restart_req(struct ptlrpc_request *req);
+void ptlrpc_abort_inflight(struct obd_import *imp);
+void ptlrpc_cleanup_imp(struct obd_import *imp);
+void ptlrpc_abort_set(struct ptlrpc_request_set *set);
+
+struct ptlrpc_request_set *ptlrpc_prep_set(void);
+struct ptlrpc_request_set *ptlrpc_prep_fcset(int max, set_producer_func func,
+ void *arg);
+int ptlrpc_set_add_cb(struct ptlrpc_request_set *set,
+ set_interpreter_func fn, void *data);
+int ptlrpc_set_next_timeout(struct ptlrpc_request_set *);
+int ptlrpc_check_set(const struct lu_env *env, struct ptlrpc_request_set *set);
+int ptlrpc_set_wait(struct ptlrpc_request_set *);
+int ptlrpc_expired_set(void *data);
+void ptlrpc_interrupted_set(void *data);
+void ptlrpc_mark_interrupted(struct ptlrpc_request *req);
+void ptlrpc_set_destroy(struct ptlrpc_request_set *);
+void ptlrpc_set_add_req(struct ptlrpc_request_set *, struct ptlrpc_request *);
+void ptlrpc_set_add_new_req(struct ptlrpcd_ctl *pc,
+ struct ptlrpc_request *req);
+
+void ptlrpc_free_rq_pool(struct ptlrpc_request_pool *pool);
+void ptlrpc_add_rqs_to_pool(struct ptlrpc_request_pool *pool, int num_rq);
+
+struct ptlrpc_request_pool *
+ptlrpc_init_rq_pool(int, int,
+ void (*populate_pool)(struct ptlrpc_request_pool *, int));
+
+void ptlrpc_at_set_req_timeout(struct ptlrpc_request *req);
+struct ptlrpc_request *ptlrpc_request_alloc(struct obd_import *imp,
+ const struct req_format *format);
+struct ptlrpc_request *ptlrpc_request_alloc_pool(struct obd_import *imp,
+ struct ptlrpc_request_pool *,
+ const struct req_format *format);
+void ptlrpc_request_free(struct ptlrpc_request *request);
+int ptlrpc_request_pack(struct ptlrpc_request *request,
+ __u32 version, int opcode);
+struct ptlrpc_request *ptlrpc_request_alloc_pack(struct obd_import *imp,
+ const struct req_format *format,
+ __u32 version, int opcode);
+int ptlrpc_request_bufs_pack(struct ptlrpc_request *request,
+ __u32 version, int opcode, char **bufs,
+ struct ptlrpc_cli_ctx *ctx);
+struct ptlrpc_request *ptlrpc_prep_req(struct obd_import *imp, __u32 version,
+ int opcode, int count, __u32 *lengths,
+ char **bufs);
+struct ptlrpc_request *ptlrpc_prep_req_pool(struct obd_import *imp,
+ __u32 version, int opcode,
+ int count, __u32 *lengths, char **bufs,
+ struct ptlrpc_request_pool *pool);
+void ptlrpc_req_finished(struct ptlrpc_request *request);
+void ptlrpc_req_finished_with_imp_lock(struct ptlrpc_request *request);
+struct ptlrpc_request *ptlrpc_request_addref(struct ptlrpc_request *req);
+struct ptlrpc_bulk_desc *ptlrpc_prep_bulk_imp(struct ptlrpc_request *req,
+ unsigned npages, unsigned max_brw,
+ unsigned type, unsigned portal);
+void __ptlrpc_free_bulk(struct ptlrpc_bulk_desc *bulk, int pin);
+static inline void ptlrpc_free_bulk_pin(struct ptlrpc_bulk_desc *bulk)
+{
+ __ptlrpc_free_bulk(bulk, 1);
+}
+static inline void ptlrpc_free_bulk_nopin(struct ptlrpc_bulk_desc *bulk)
+{
+ __ptlrpc_free_bulk(bulk, 0);
+}
+void __ptlrpc_prep_bulk_page(struct ptlrpc_bulk_desc *desc,
+ struct page *page, int pageoffset, int len, int);
+static inline void ptlrpc_prep_bulk_page_pin(struct ptlrpc_bulk_desc *desc,
+ struct page *page, int pageoffset,
+ int len)
+{
+ __ptlrpc_prep_bulk_page(desc, page, pageoffset, len, 1);
+}
+
+static inline void ptlrpc_prep_bulk_page_nopin(struct ptlrpc_bulk_desc *desc,
+ struct page *page, int pageoffset,
+ int len)
+{
+ __ptlrpc_prep_bulk_page(desc, page, pageoffset, len, 0);
+}
+
+void ptlrpc_retain_replayable_request(struct ptlrpc_request *req,
+ struct obd_import *imp);
+__u64 ptlrpc_next_xid(void);
+__u64 ptlrpc_sample_next_xid(void);
+__u64 ptlrpc_req_xid(struct ptlrpc_request *request);
+
+/* Set of routines to run a function in ptlrpcd context */
+void *ptlrpcd_alloc_work(struct obd_import *imp,
+ int (*cb)(const struct lu_env *, void *), void *data);
+void ptlrpcd_destroy_work(void *handler);
+int ptlrpcd_queue_work(void *handler);
+
+/** @} */
+struct ptlrpc_service_buf_conf {
+ /* nbufs is buffers # to allocate when growing the pool */
+ unsigned int bc_nbufs;
+ /* buffer size to post */
+ unsigned int bc_buf_size;
+ /* portal to listed for requests on */
+ unsigned int bc_req_portal;
+ /* portal of where to send replies to */
+ unsigned int bc_rep_portal;
+ /* maximum request size to be accepted for this service */
+ unsigned int bc_req_max_size;
+ /* maximum reply size this service can ever send */
+ unsigned int bc_rep_max_size;
+};
+
+struct ptlrpc_service_thr_conf {
+ /* threadname should be 8 characters or less - 6 will be added on */
+ char *tc_thr_name;
+ /* threads increasing factor for each CPU */
+ unsigned int tc_thr_factor;
+ /* service threads # to start on each partition while initializing */
+ unsigned int tc_nthrs_init;
+ /*
+ * low water of threads # upper-limit on each partition while running,
+ * service availability may be impacted if threads number is lower
+ * than this value. It can be ZERO if the service doesn't require
+ * CPU affinity or there is only one partition.
+ */
+ unsigned int tc_nthrs_base;
+ /* "soft" limit for total threads number */
+ unsigned int tc_nthrs_max;
+ /* user specified threads number, it will be validated due to
+ * other members of this structure. */
+ unsigned int tc_nthrs_user;
+ /* set NUMA node affinity for service threads */
+ unsigned int tc_cpu_affinity;
+ /* Tags for lu_context associated with service thread */
+ __u32 tc_ctx_tags;
+};
+
+struct ptlrpc_service_cpt_conf {
+ struct cfs_cpt_table *cc_cptable;
+ /* string pattern to describe CPTs for a service */
+ char *cc_pattern;
+};
+
+struct ptlrpc_service_conf {
+ /* service name */
+ char *psc_name;
+ /* soft watchdog timeout multiplifier to print stuck service traces */
+ unsigned int psc_watchdog_factor;
+ /* buffer information */
+ struct ptlrpc_service_buf_conf psc_buf;
+ /* thread information */
+ struct ptlrpc_service_thr_conf psc_thr;
+ /* CPU partition information */
+ struct ptlrpc_service_cpt_conf psc_cpt;
+ /* function table */
+ struct ptlrpc_service_ops psc_ops;
+};
+
+/* ptlrpc/service.c */
+/**
+ * Server-side services API. Register/unregister service, request state
+ * management, service thread management
+ *
+ * @{
+ */
+void ptlrpc_save_lock(struct ptlrpc_request *req,
+ struct lustre_handle *lock, int mode, int no_ack);
+void ptlrpc_commit_replies(struct obd_export *exp);
+void ptlrpc_dispatch_difficult_reply(struct ptlrpc_reply_state *rs);
+void ptlrpc_schedule_difficult_reply(struct ptlrpc_reply_state *rs);
+int ptlrpc_hpreq_handler(struct ptlrpc_request *req);
+struct ptlrpc_service *ptlrpc_register_service(
+ struct ptlrpc_service_conf *conf,
+ struct proc_dir_entry *proc_entry);
+void ptlrpc_stop_all_threads(struct ptlrpc_service *svc);
+
+int ptlrpc_start_threads(struct ptlrpc_service *svc);
+int ptlrpc_unregister_service(struct ptlrpc_service *service);
+int liblustre_check_services(void *arg);
+void ptlrpc_daemonize(char *name);
+int ptlrpc_service_health_check(struct ptlrpc_service *);
+void ptlrpc_server_drop_request(struct ptlrpc_request *req);
+void ptlrpc_request_change_export(struct ptlrpc_request *req,
+ struct obd_export *export);
+
+int ptlrpc_hr_init(void);
+void ptlrpc_hr_fini(void);
+
+/** @} */
+
+/* ptlrpc/import.c */
+/**
+ * Import API
+ * @{
+ */
+int ptlrpc_connect_import(struct obd_import *imp);
+int ptlrpc_init_import(struct obd_import *imp);
+int ptlrpc_disconnect_import(struct obd_import *imp, int noclose);
+int ptlrpc_import_recovery_state_machine(struct obd_import *imp);
+void deuuidify(char *uuid, const char *prefix, char **uuid_start,
+ int *uuid_len);
+
+/* ptlrpc/pack_generic.c */
+int ptlrpc_reconnect_import(struct obd_import *imp);
+/** @} */
+
+/**
+ * ptlrpc msg buffer and swab interface
+ *
+ * @{
+ */
+int ptlrpc_buf_need_swab(struct ptlrpc_request *req, const int inout,
+ int index);
+void ptlrpc_buf_set_swabbed(struct ptlrpc_request *req, const int inout,
+ int index);
+int ptlrpc_unpack_rep_msg(struct ptlrpc_request *req, int len);
+int ptlrpc_unpack_req_msg(struct ptlrpc_request *req, int len);
+
+int lustre_msg_check_version(struct lustre_msg *msg, __u32 version);
+void lustre_init_msg_v2(struct lustre_msg_v2 *msg, int count, __u32 *lens,
+ char **bufs);
+int lustre_pack_request(struct ptlrpc_request *, __u32 magic, int count,
+ __u32 *lens, char **bufs);
+int lustre_pack_reply(struct ptlrpc_request *, int count, __u32 *lens,
+ char **bufs);
+int lustre_pack_reply_v2(struct ptlrpc_request *req, int count,
+ __u32 *lens, char **bufs, int flags);
+#define LPRFL_EARLY_REPLY 1
+int lustre_pack_reply_flags(struct ptlrpc_request *, int count, __u32 *lens,
+ char **bufs, int flags);
+int lustre_shrink_msg(struct lustre_msg *msg, int segment,
+ unsigned int newlen, int move_data);
+void lustre_free_reply_state(struct ptlrpc_reply_state *rs);
+int __lustre_unpack_msg(struct lustre_msg *m, int len);
+int lustre_msg_hdr_size(__u32 magic, int count);
+int lustre_msg_size(__u32 magic, int count, __u32 *lengths);
+int lustre_msg_size_v2(int count, __u32 *lengths);
+int lustre_packed_msg_size(struct lustre_msg *msg);
+int lustre_msg_early_size(void);
+void *lustre_msg_buf_v2(struct lustre_msg_v2 *m, int n, int min_size);
+void *lustre_msg_buf(struct lustre_msg *m, int n, int minlen);
+int lustre_msg_buflen(struct lustre_msg *m, int n);
+void lustre_msg_set_buflen(struct lustre_msg *m, int n, int len);
+int lustre_msg_bufcount(struct lustre_msg *m);
+char *lustre_msg_string(struct lustre_msg *m, int n, int max_len);
+__u32 lustre_msghdr_get_flags(struct lustre_msg *msg);
+void lustre_msghdr_set_flags(struct lustre_msg *msg, __u32 flags);
+__u32 lustre_msg_get_flags(struct lustre_msg *msg);
+void lustre_msg_add_flags(struct lustre_msg *msg, int flags);
+void lustre_msg_set_flags(struct lustre_msg *msg, int flags);
+void lustre_msg_clear_flags(struct lustre_msg *msg, int flags);
+__u32 lustre_msg_get_op_flags(struct lustre_msg *msg);
+void lustre_msg_add_op_flags(struct lustre_msg *msg, int flags);
+void lustre_msg_set_op_flags(struct lustre_msg *msg, int flags);
+struct lustre_handle *lustre_msg_get_handle(struct lustre_msg *msg);
+__u32 lustre_msg_get_type(struct lustre_msg *msg);
+__u32 lustre_msg_get_version(struct lustre_msg *msg);
+void lustre_msg_add_version(struct lustre_msg *msg, int version);
+__u32 lustre_msg_get_opc(struct lustre_msg *msg);
+__u64 lustre_msg_get_last_xid(struct lustre_msg *msg);
+__u64 lustre_msg_get_last_committed(struct lustre_msg *msg);
+__u64 *lustre_msg_get_versions(struct lustre_msg *msg);
+__u64 lustre_msg_get_transno(struct lustre_msg *msg);
+__u64 lustre_msg_get_slv(struct lustre_msg *msg);
+__u32 lustre_msg_get_limit(struct lustre_msg *msg);
+void lustre_msg_set_slv(struct lustre_msg *msg, __u64 slv);
+void lustre_msg_set_limit(struct lustre_msg *msg, __u64 limit);
+int lustre_msg_get_status(struct lustre_msg *msg);
+__u32 lustre_msg_get_conn_cnt(struct lustre_msg *msg);
+int lustre_msg_is_v1(struct lustre_msg *msg);
+__u32 lustre_msg_get_magic(struct lustre_msg *msg);
+__u32 lustre_msg_get_timeout(struct lustre_msg *msg);
+__u32 lustre_msg_get_service_time(struct lustre_msg *msg);
+char *lustre_msg_get_jobid(struct lustre_msg *msg);
+__u32 lustre_msg_get_cksum(struct lustre_msg *msg);
+__u32 lustre_msg_calc_cksum(struct lustre_msg *msg);
+void lustre_msg_set_handle(struct lustre_msg *msg,
+ struct lustre_handle *handle);
+void lustre_msg_set_type(struct lustre_msg *msg, __u32 type);
+void lustre_msg_set_opc(struct lustre_msg *msg, __u32 opc);
+void lustre_msg_set_last_xid(struct lustre_msg *msg, __u64 last_xid);
+void lustre_msg_set_last_committed(struct lustre_msg *msg,
+ __u64 last_committed);
+void lustre_msg_set_versions(struct lustre_msg *msg, __u64 *versions);
+void lustre_msg_set_transno(struct lustre_msg *msg, __u64 transno);
+void lustre_msg_set_status(struct lustre_msg *msg, __u32 status);
+void lustre_msg_set_conn_cnt(struct lustre_msg *msg, __u32 conn_cnt);
+void ptlrpc_req_set_repsize(struct ptlrpc_request *req, int count, __u32 *sizes);
+void ptlrpc_request_set_replen(struct ptlrpc_request *req);
+void lustre_msg_set_timeout(struct lustre_msg *msg, __u32 timeout);
+void lustre_msg_set_service_time(struct lustre_msg *msg, __u32 service_time);
+void lustre_msg_set_jobid(struct lustre_msg *msg, char *jobid);
+void lustre_msg_set_cksum(struct lustre_msg *msg, __u32 cksum);
+
+static inline void
+lustre_shrink_reply(struct ptlrpc_request *req, int segment,
+ unsigned int newlen, int move_data)
+{
+ LASSERT(req->rq_reply_state);
+ LASSERT(req->rq_repmsg);
+ req->rq_replen = lustre_shrink_msg(req->rq_repmsg, segment,
+ newlen, move_data);
+}
+
+#ifdef CONFIG_LUSTRE_TRANSLATE_ERRNOS
+
+static inline int ptlrpc_status_hton(int h)
+{
+ /*
+ * Positive errnos must be network errnos, such as LUSTRE_EDEADLK,
+ * ELDLM_LOCK_ABORTED, etc.
+ */
+ if (h < 0)
+ return -lustre_errno_hton(-h);
+ else
+ return h;
+}
+
+static inline int ptlrpc_status_ntoh(int n)
+{
+ /*
+ * See the comment in ptlrpc_status_hton().
+ */
+ if (n < 0)
+ return -lustre_errno_ntoh(-n);
+ else
+ return n;
+}
+
+#else
+
+#define ptlrpc_status_hton(h) (h)
+#define ptlrpc_status_ntoh(n) (n)
+
+#endif
+/** @} */
+
+/** Change request phase of \a req to \a new_phase */
+static inline void
+ptlrpc_rqphase_move(struct ptlrpc_request *req, enum rq_phase new_phase)
+{
+ if (req->rq_phase == new_phase)
+ return;
+
+ if (new_phase == RQ_PHASE_UNREGISTERING) {
+ req->rq_next_phase = req->rq_phase;
+ if (req->rq_import)
+ atomic_inc(&req->rq_import->imp_unregistering);
+ }
+
+ if (req->rq_phase == RQ_PHASE_UNREGISTERING) {
+ if (req->rq_import)
+ atomic_dec(&req->rq_import->imp_unregistering);
+ }
+
+ DEBUG_REQ(D_INFO, req, "move req \"%s\" -> \"%s\"",
+ ptlrpc_rqphase2str(req), ptlrpc_phase2str(new_phase));
+
+ req->rq_phase = new_phase;
+}
+
+/**
+ * Returns true if request \a req got early reply and hard deadline is not met
+ */
+static inline int
+ptlrpc_client_early(struct ptlrpc_request *req)
+{
+ if (OBD_FAIL_CHECK(OBD_FAIL_PTLRPC_LONG_REPL_UNLINK) &&
+ req->rq_reply_deadline > get_seconds())
+ return 0;
+ return req->rq_early;
+}
+
+/**
+ * Returns true if we got real reply from server for this request
+ */
+static inline int
+ptlrpc_client_replied(struct ptlrpc_request *req)
+{
+ if (OBD_FAIL_CHECK(OBD_FAIL_PTLRPC_LONG_REPL_UNLINK) &&
+ req->rq_reply_deadline > get_seconds())
+ return 0;
+ return req->rq_replied;
+}
+
+/** Returns true if request \a req is in process of receiving server reply */
+static inline int
+ptlrpc_client_recv(struct ptlrpc_request *req)
+{
+ if (OBD_FAIL_CHECK(OBD_FAIL_PTLRPC_LONG_REPL_UNLINK) &&
+ req->rq_reply_deadline > get_seconds())
+ return 1;
+ return req->rq_receiving_reply;
+}
+
+static inline int
+ptlrpc_client_recv_or_unlink(struct ptlrpc_request *req)
+{
+ int rc;
+
+ spin_lock(&req->rq_lock);
+ if (OBD_FAIL_CHECK(OBD_FAIL_PTLRPC_LONG_REPL_UNLINK) &&
+ req->rq_reply_deadline > get_seconds()) {
+ spin_unlock(&req->rq_lock);
+ return 1;
+ }
+ rc = req->rq_receiving_reply;
+ rc = rc || req->rq_req_unlink || req->rq_reply_unlink;
+ spin_unlock(&req->rq_lock);
+ return rc;
+}
+
+static inline void
+ptlrpc_client_wake_req(struct ptlrpc_request *req)
+{
+ if (req->rq_set == NULL)
+ wake_up(&req->rq_reply_waitq);
+ else
+ wake_up(&req->rq_set->set_waitq);
+}
+
+static inline void
+ptlrpc_rs_addref(struct ptlrpc_reply_state *rs)
+{
+ LASSERT(atomic_read(&rs->rs_refcount) > 0);
+ atomic_inc(&rs->rs_refcount);
+}
+
+static inline void
+ptlrpc_rs_decref(struct ptlrpc_reply_state *rs)
+{
+ LASSERT(atomic_read(&rs->rs_refcount) > 0);
+ if (atomic_dec_and_test(&rs->rs_refcount))
+ lustre_free_reply_state(rs);
+}
+
+/* Should only be called once per req */
+static inline void ptlrpc_req_drop_rs(struct ptlrpc_request *req)
+{
+ if (req->rq_reply_state == NULL)
+ return; /* shouldn't occur */
+ ptlrpc_rs_decref(req->rq_reply_state);
+ req->rq_reply_state = NULL;
+ req->rq_repmsg = NULL;
+}
+
+static inline __u32 lustre_request_magic(struct ptlrpc_request *req)
+{
+ return lustre_msg_get_magic(req->rq_reqmsg);
+}
+
+static inline int ptlrpc_req_get_repsize(struct ptlrpc_request *req)
+{
+ switch (req->rq_reqmsg->lm_magic) {
+ case LUSTRE_MSG_MAGIC_V2:
+ return req->rq_reqmsg->lm_repsize;
+ default:
+ LASSERTF(0, "incorrect message magic: %08x\n",
+ req->rq_reqmsg->lm_magic);
+ return -EFAULT;
+ }
+}
+
+static inline int ptlrpc_send_limit_expired(struct ptlrpc_request *req)
+{
+ if (req->rq_delay_limit != 0 &&
+ time_before(cfs_time_add(req->rq_queued_time,
+ cfs_time_seconds(req->rq_delay_limit)),
+ cfs_time_current())) {
+ return 1;
+ }
+ return 0;
+}
+
+static inline int ptlrpc_no_resend(struct ptlrpc_request *req)
+{
+ if (!req->rq_no_resend && ptlrpc_send_limit_expired(req)) {
+ spin_lock(&req->rq_lock);
+ req->rq_no_resend = 1;
+ spin_unlock(&req->rq_lock);
+ }
+ return req->rq_no_resend;
+}
+
+static inline int
+ptlrpc_server_get_timeout(struct ptlrpc_service_part *svcpt)
+{
+ int at = AT_OFF ? 0 : at_get(&svcpt->scp_at_estimate);
+
+ return svcpt->scp_service->srv_watchdog_factor *
+ max_t(int, at, obd_timeout);
+}
+
+static inline struct ptlrpc_service *
+ptlrpc_req2svc(struct ptlrpc_request *req)
+{
+ LASSERT(req->rq_rqbd != NULL);
+ return req->rq_rqbd->rqbd_svcpt->scp_service;
+}
+
+/* ldlm/ldlm_lib.c */
+/**
+ * Target client logic
+ * @{
+ */
+int client_obd_setup(struct obd_device *obddev, struct lustre_cfg *lcfg);
+int client_obd_cleanup(struct obd_device *obddev);
+int client_connect_import(const struct lu_env *env,
+ struct obd_export **exp, struct obd_device *obd,
+ struct obd_uuid *cluuid, struct obd_connect_data *,
+ void *localdata);
+int client_disconnect_export(struct obd_export *exp);
+int client_import_add_conn(struct obd_import *imp, struct obd_uuid *uuid,
+ int priority);
+int client_import_del_conn(struct obd_import *imp, struct obd_uuid *uuid);
+int client_import_find_conn(struct obd_import *imp, lnet_nid_t peer,
+ struct obd_uuid *uuid);
+int import_set_conn_priority(struct obd_import *imp, struct obd_uuid *uuid);
+void client_destroy_import(struct obd_import *imp);
+/** @} */
+
+
+/* ptlrpc/pinger.c */
+/**
+ * Pinger API (client side only)
+ * @{
+ */
+enum timeout_event {
+ TIMEOUT_GRANT = 1
+};
+struct timeout_item;
+typedef int (*timeout_cb_t)(struct timeout_item *, void *);
+int ptlrpc_pinger_add_import(struct obd_import *imp);
+int ptlrpc_pinger_del_import(struct obd_import *imp);
+int ptlrpc_add_timeout_client(int time, enum timeout_event event,
+ timeout_cb_t cb, void *data,
+ struct list_head *obd_list);
+int ptlrpc_del_timeout_client(struct list_head *obd_list,
+ enum timeout_event event);
+struct ptlrpc_request *ptlrpc_prep_ping(struct obd_import *imp);
+int ptlrpc_obd_ping(struct obd_device *obd);
+void ping_evictor_start(void);
+void ping_evictor_stop(void);
+void ptlrpc_pinger_ir_up(void);
+void ptlrpc_pinger_ir_down(void);
+/** @} */
+int ptlrpc_pinger_suppress_pings(void);
+
+/* ptlrpc daemon bind policy */
+typedef enum {
+ /* all ptlrpcd threads are free mode */
+ PDB_POLICY_NONE = 1,
+ /* all ptlrpcd threads are bound mode */
+ PDB_POLICY_FULL = 2,
+ /* <free1 bound1> <free2 bound2> ... <freeN boundN> */
+ PDB_POLICY_PAIR = 3,
+ /* <free1 bound1> <bound1 free2> ... <freeN boundN> <boundN free1>,
+ * means each ptlrpcd[X] has two partners: thread[X-1] and thread[X+1].
+ * If kernel supports NUMA, pthrpcd threads are binded and
+ * grouped by NUMA node */
+ PDB_POLICY_NEIGHBOR = 4,
+} pdb_policy_t;
+
+/* ptlrpc daemon load policy
+ * It is caller's duty to specify how to push the async RPC into some ptlrpcd
+ * queue, but it is not enforced, affected by "ptlrpcd_bind_policy". If it is
+ * "PDB_POLICY_FULL", then the RPC will be processed by the selected ptlrpcd,
+ * Otherwise, the RPC may be processed by the selected ptlrpcd or its partner,
+ * depends on which is scheduled firstly, to accelerate the RPC processing. */
+typedef enum {
+ /* on the same CPU core as the caller */
+ PDL_POLICY_SAME = 1,
+ /* within the same CPU partition, but not the same core as the caller */
+ PDL_POLICY_LOCAL = 2,
+ /* round-robin on all CPU cores, but not the same core as the caller */
+ PDL_POLICY_ROUND = 3,
+ /* the specified CPU core is preferred, but not enforced */
+ PDL_POLICY_PREFERRED = 4,
+} pdl_policy_t;
+
+/* ptlrpc/ptlrpcd.c */
+void ptlrpcd_stop(struct ptlrpcd_ctl *pc, int force);
+void ptlrpcd_free(struct ptlrpcd_ctl *pc);
+void ptlrpcd_wake(struct ptlrpc_request *req);
+void ptlrpcd_add_req(struct ptlrpc_request *req, pdl_policy_t policy, int idx);
+void ptlrpcd_add_rqset(struct ptlrpc_request_set *set);
+int ptlrpcd_addref(void);
+void ptlrpcd_decref(void);
+
+/* ptlrpc/lproc_ptlrpc.c */
+/**
+ * procfs output related functions
+ * @{
+ */
+const char *ll_opcode2str(__u32 opcode);
+#if defined (CONFIG_PROC_FS)
+void ptlrpc_lprocfs_register_obd(struct obd_device *obd);
+void ptlrpc_lprocfs_unregister_obd(struct obd_device *obd);
+void ptlrpc_lprocfs_brw(struct ptlrpc_request *req, int bytes);
+#else
+static inline void ptlrpc_lprocfs_register_obd(struct obd_device *obd) {}
+static inline void ptlrpc_lprocfs_unregister_obd(struct obd_device *obd) {}
+static inline void ptlrpc_lprocfs_brw(struct ptlrpc_request *req, int bytes) {}
+#endif
+/** @} */
+
+/* ptlrpc/llog_client.c */
+extern struct llog_operations llog_client_ops;
+
+/** @} net */
+
+#endif
+/** @} PtlRPC */
diff --git a/drivers/staging/lustre/lustre/include/lustre_param.h b/drivers/staging/lustre/lustre/include/lustre_param.h
new file mode 100644
index 000000000..ed654684c
--- /dev/null
+++ b/drivers/staging/lustre/lustre/include/lustre_param.h
@@ -0,0 +1,121 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2011, 2012, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ *
+ * lustre/include/lustre_param.h
+ *
+ * User-settable parameter keys
+ *
+ * Author: Nathan Rutman <nathan@clusterfs.com>
+ */
+
+#ifndef _LUSTRE_PARAM_H
+#define _LUSTRE_PARAM_H
+
+/** \defgroup param param
+ *
+ * @{
+ */
+
+/* For interoperability */
+struct cfg_interop_param {
+ char *old_param;
+ char *new_param;
+};
+
+/* obd_config.c */
+int class_find_param(char *buf, char *key, char **valp);
+struct cfg_interop_param *class_find_old_param(const char *param,
+ struct cfg_interop_param *ptr);
+int class_get_next_param(char **params, char *copy);
+int class_match_param(char *buf, char *key, char **valp);
+int class_parse_nid(char *buf, lnet_nid_t *nid, char **endh);
+int class_parse_nid_quiet(char *buf, lnet_nid_t *nid, char **endh);
+int class_parse_net(char *buf, __u32 *net, char **endh);
+int class_match_nid(char *buf, char *key, lnet_nid_t nid);
+int class_match_net(char *buf, char *key, __u32 net);
+/* obd_mount.c */
+int do_lcfg(char *cfgname, lnet_nid_t nid, int cmd,
+ char *s1, char *s2, char *s3, char *s4);
+
+
+
+/****************** User-settable parameter keys *********************/
+/* e.g.
+ tunefs.lustre --param="failover.node=192.168.0.13@tcp0" /dev/sda
+ lctl conf_param testfs-OST0000 failover.node=3@elan,192.168.0.3@tcp0
+ ... testfs-MDT0000.lov.stripesize=4M
+ ... testfs-OST0000.ost.client_cache_seconds=15
+ ... testfs.sys.timeout=<secs>
+ ... testfs.llite.max_read_ahead_mb=16
+*/
+
+/* System global or special params not handled in obd's proc
+ * See mgs_write_log_sys()
+ */
+#define PARAM_TIMEOUT "timeout=" /* global */
+#define PARAM_LDLM_TIMEOUT "ldlm_timeout=" /* global */
+#define PARAM_AT_MIN "at_min=" /* global */
+#define PARAM_AT_MAX "at_max=" /* global */
+#define PARAM_AT_EXTRA "at_extra=" /* global */
+#define PARAM_AT_EARLY_MARGIN "at_early_margin=" /* global */
+#define PARAM_AT_HISTORY "at_history=" /* global */
+#define PARAM_JOBID_VAR "jobid_var=" /* global */
+#define PARAM_MGSNODE "mgsnode=" /* only at mounttime */
+#define PARAM_FAILNODE "failover.node=" /* add failover nid */
+#define PARAM_FAILMODE "failover.mode=" /* initial mount only */
+#define PARAM_ACTIVE "active=" /* activate/deactivate */
+#define PARAM_NETWORK "network=" /* bind on nid */
+#define PARAM_ID_UPCALL "identity_upcall=" /* identity upcall */
+
+/* Prefixes for parameters handled by obd's proc methods (XXX_process_config) */
+#define PARAM_OST "ost."
+#define PARAM_OSC "osc."
+#define PARAM_MDT "mdt."
+#define PARAM_MDD "mdd."
+#define PARAM_MDC "mdc."
+#define PARAM_LLITE "llite."
+#define PARAM_LOV "lov."
+#define PARAM_LOD "lod."
+#define PARAM_OSP "osp."
+#define PARAM_SYS "sys." /* global */
+#define PARAM_SRPC "srpc."
+#define PARAM_SRPC_FLVR "srpc.flavor."
+#define PARAM_SRPC_UDESC "srpc.udesc.cli2mdt"
+#define PARAM_SEC "security."
+#define PARAM_QUOTA "quota." /* global */
+
+/** @} param */
+
+#endif /* _LUSTRE_PARAM_H */
diff --git a/drivers/staging/lustre/lustre/include/lustre_quota.h b/drivers/staging/lustre/lustre/include/lustre_quota.h
new file mode 100644
index 000000000..2643f2807
--- /dev/null
+++ b/drivers/staging/lustre/lustre/include/lustre_quota.h
@@ -0,0 +1,241 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 021110-1307, USA
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2011, 2012, Intel Corporation.
+ * Use is subject to license terms.
+ */
+
+#ifndef _LUSTRE_QUOTA_H
+#define _LUSTRE_QUOTA_H
+
+/** \defgroup quota quota
+ *
+ */
+
+#include <linux/fs.h>
+#include <linux/quota.h>
+#include <linux/quotaops.h>
+
+#include "dt_object.h"
+#include "lustre_fid.h"
+#include "lustre_dlm.h"
+
+#ifndef MAX_IQ_TIME
+#define MAX_IQ_TIME 604800 /* (7*24*60*60) 1 week */
+#endif
+
+#ifndef MAX_DQ_TIME
+#define MAX_DQ_TIME 604800 /* (7*24*60*60) 1 week */
+#endif
+
+struct lquota_id_info;
+struct lquota_trans;
+
+/* Gather all quota record type in an union that can be used to read any records
+ * from disk. All fields of these records must be 64-bit aligned, otherwise the
+ * OSD layer may swab them incorrectly. */
+union lquota_rec {
+ struct lquota_glb_rec lqr_glb_rec;
+ struct lquota_slv_rec lqr_slv_rec;
+ struct lquota_acct_rec lqr_acct_rec;
+};
+
+/* Index features supported by the global index objects
+ * Only used for migration purpose and should be removed once on-disk migration
+ * is no longer needed */
+extern struct dt_index_features dt_quota_iusr_features;
+extern struct dt_index_features dt_quota_busr_features;
+extern struct dt_index_features dt_quota_igrp_features;
+extern struct dt_index_features dt_quota_bgrp_features;
+
+/* Name used in the configuration logs to identify the default metadata pool
+ * (composed of all the MDTs, with pool ID 0) and the default data pool (all
+ * the OSTs, with pool ID 0 too). */
+#define QUOTA_METAPOOL_NAME "mdt="
+#define QUOTA_DATAPOOL_NAME "ost="
+
+/*
+ * Quota Master Target support
+ */
+
+/* Request handlers for quota master operations.
+ * This is used by the MDT to pass quota/lock requests to the quota master
+ * target. This won't be needed any more once the QMT is a real target and
+ * does not rely any more on the MDT service threads and namespace. */
+struct qmt_handlers {
+ /* Handle quotactl request from client. */
+ int (*qmth_quotactl)(const struct lu_env *, struct lu_device *,
+ struct obd_quotactl *);
+
+ /* Handle dqacq/dqrel request from slave. */
+ int (*qmth_dqacq)(const struct lu_env *, struct lu_device *,
+ struct ptlrpc_request *);
+
+ /* LDLM intent policy associated with quota locks */
+ int (*qmth_intent_policy)(const struct lu_env *, struct lu_device *,
+ struct ptlrpc_request *, struct ldlm_lock **,
+ int);
+
+ /* Initialize LVB of ldlm resource associated with quota objects */
+ int (*qmth_lvbo_init)(struct lu_device *, struct ldlm_resource *);
+
+ /* Update LVB of ldlm resource associated with quota objects */
+ int (*qmth_lvbo_update)(struct lu_device *, struct ldlm_resource *,
+ struct ptlrpc_request *, int);
+
+ /* Return size of LVB to be packed in ldlm message */
+ int (*qmth_lvbo_size)(struct lu_device *, struct ldlm_lock *);
+
+ /* Fill request buffer with lvb */
+ int (*qmth_lvbo_fill)(struct lu_device *, struct ldlm_lock *, void *,
+ int);
+
+ /* Free lvb associated with ldlm resource */
+ int (*qmth_lvbo_free)(struct lu_device *, struct ldlm_resource *);
+};
+
+/* actual handlers are defined in lustre/quota/qmt_handler.c */
+extern struct qmt_handlers qmt_hdls;
+
+/*
+ * Quota enforcement support on slaves
+ */
+
+struct qsd_instance;
+
+/* The quota slave feature is implemented under the form of a library.
+ * The API is the following:
+ *
+ * - qsd_init(): the user (mostly the OSD layer) should first allocate a qsd
+ * instance via qsd_init(). This creates all required structures
+ * to manage quota enforcement for this target and performs all
+ * low-level initialization which does not involve any lustre
+ * object. qsd_init() should typically be called when the OSD
+ * is being set up.
+ *
+ * - qsd_prepare(): This sets up on-disk objects associated with the quota slave
+ * feature and initiates the quota reintegration procedure if
+ * needed. qsd_prepare() should typically be called when
+ * ->ldo_prepare is invoked.
+ *
+ * - qsd_start(): a qsd instance should be started once recovery is completed
+ * (i.e. when ->ldo_recovery_complete is called). This is used
+ * to notify the qsd layer that quota should now be enforced
+ * again via the qsd_op_begin/end functions. The last step of the
+ * reintegration procedure (namely usage reconciliation) will be
+ * completed during start.
+ *
+ * - qsd_fini(): is used to release a qsd_instance structure allocated with
+ * qsd_init(). This releases all quota slave objects and frees the
+ * structures associated with the qsd_instance.
+ *
+ * - qsd_op_begin(): is used to enforce quota, it must be called in the
+ * declaration of each operation. qsd_op_end() should then be
+ * invoked later once all operations have been completed in
+ * order to release/adjust the quota space.
+ * Running qsd_op_begin() before qsd_start() isn't fatal and
+ * will return success.
+ * Once qsd_start() has been run, qsd_op_begin() will block
+ * until the reintegration procedure is completed.
+ *
+ * - qsd_op_end(): performs the post operation quota processing. This must be
+ * called after the operation transaction stopped.
+ * While qsd_op_begin() must be invoked each time a new
+ * operation is declared, qsd_op_end() should be called only
+ * once for the whole transaction.
+ *
+ * - qsd_op_adjust(): triggers pre-acquire/release if necessary.
+ *
+ * Below are the function prototypes to be used by OSD layer to manage quota
+ * enforcement. Arguments are documented where each function is defined. */
+
+struct qsd_instance *qsd_init(const struct lu_env *, char *, struct dt_device *,
+ struct proc_dir_entry *);
+int qsd_prepare(const struct lu_env *, struct qsd_instance *);
+int qsd_start(const struct lu_env *, struct qsd_instance *);
+void qsd_fini(const struct lu_env *, struct qsd_instance *);
+int qsd_op_begin(const struct lu_env *, struct qsd_instance *,
+ struct lquota_trans *, struct lquota_id_info *, int *);
+void qsd_op_end(const struct lu_env *, struct qsd_instance *,
+ struct lquota_trans *);
+void qsd_op_adjust(const struct lu_env *, struct qsd_instance *,
+ union lquota_id *, int);
+/* This is exported for the ldiskfs quota migration only,
+ * see convert_quota_file() */
+int lquota_disk_write_glb(const struct lu_env *, struct dt_object *,
+ __u64, struct lquota_glb_rec *);
+
+/*
+ * Quota information attached to a transaction
+ */
+
+struct lquota_entry;
+
+struct lquota_id_info {
+ /* quota identifier */
+ union lquota_id lqi_id;
+
+ /* USRQUOTA or GRPQUOTA for now, could be expanded for
+ * directory quota or other types later. */
+ int lqi_type;
+
+ /* inodes or kbytes to be consumed or released, it could
+ * be negative when releasing space. */
+ long long lqi_space;
+
+ /* quota slave entry structure associated with this ID */
+ struct lquota_entry *lqi_qentry;
+
+ /* whether we are reporting blocks or inodes */
+ bool lqi_is_blk;
+};
+
+/* Since we enforce only inode quota in meta pool (MDTs), and block quota in
+ * data pool (OSTs), there are at most 4 quota ids being enforced in a single
+ * transaction, which is chown transaction:
+ * original uid and gid, new uid and gid.
+ *
+ * This value might need to be revised when directory quota is added. */
+#define QUOTA_MAX_TRANSIDS 4
+
+/* all qids involved in a single transaction */
+struct lquota_trans {
+ unsigned short lqt_id_cnt;
+ struct lquota_id_info lqt_ids[QUOTA_MAX_TRANSIDS];
+};
+
+/* flags for quota local enforcement */
+#define QUOTA_FL_OVER_USRQUOTA 0x01
+#define QUOTA_FL_OVER_GRPQUOTA 0x02
+#define QUOTA_FL_SYNC 0x04
+
+#define IS_LQUOTA_RES(res) \
+ (res->lr_name.name[LUSTRE_RES_ID_SEQ_OFF] == FID_SEQ_QUOTA || \
+ res->lr_name.name[LUSTRE_RES_ID_SEQ_OFF] == FID_SEQ_QUOTA_GLB)
+
+/* helper function used by MDT & OFD to retrieve quota accounting information
+ * on slave */
+int lquotactl_slv(const struct lu_env *, struct dt_device *,
+ struct obd_quotactl *);
+/** @} quota */
+#endif /* _LUSTRE_QUOTA_H */
diff --git a/drivers/staging/lustre/lustre/include/lustre_req_layout.h b/drivers/staging/lustre/lustre/include/lustre_req_layout.h
new file mode 100644
index 000000000..c6457b27c
--- /dev/null
+++ b/drivers/staging/lustre/lustre/include/lustre_req_layout.h
@@ -0,0 +1,341 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2011, 2012, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ *
+ * lustre/include/lustre_req_layout.h
+ *
+ * Lustre Metadata Target (mdt) request handler
+ *
+ * Author: Nikita Danilov <nikita@clusterfs.com>
+ */
+
+#ifndef _LUSTRE_REQ_LAYOUT_H__
+#define _LUSTRE_REQ_LAYOUT_H__
+
+/** \defgroup req_layout req_layout
+ *
+ * @{
+ */
+
+struct req_msg_field;
+struct req_format;
+struct req_capsule;
+
+struct ptlrpc_request;
+
+enum req_location {
+ RCL_CLIENT,
+ RCL_SERVER,
+ RCL_NR
+};
+
+/* Maximal number of fields (buffers) in a request message. */
+#define REQ_MAX_FIELD_NR 9
+
+struct req_capsule {
+ struct ptlrpc_request *rc_req;
+ const struct req_format *rc_fmt;
+ enum req_location rc_loc;
+ __u32 rc_area[RCL_NR][REQ_MAX_FIELD_NR];
+};
+
+#if !defined(__REQ_LAYOUT_USER__)
+
+/* struct ptlrpc_request, lustre_msg* */
+#include "lustre_net.h"
+
+void req_capsule_init(struct req_capsule *pill, struct ptlrpc_request *req,
+ enum req_location location);
+void req_capsule_fini(struct req_capsule *pill);
+
+void req_capsule_set(struct req_capsule *pill, const struct req_format *fmt);
+void req_capsule_client_dump(struct req_capsule *pill);
+void req_capsule_server_dump(struct req_capsule *pill);
+void req_capsule_init_area(struct req_capsule *pill);
+int req_capsule_filled_sizes(struct req_capsule *pill, enum req_location loc);
+int req_capsule_server_pack(struct req_capsule *pill);
+
+void *req_capsule_client_get(struct req_capsule *pill,
+ const struct req_msg_field *field);
+void *req_capsule_client_swab_get(struct req_capsule *pill,
+ const struct req_msg_field *field,
+ void *swabber);
+void *req_capsule_client_sized_get(struct req_capsule *pill,
+ const struct req_msg_field *field,
+ int len);
+void *req_capsule_server_get(struct req_capsule *pill,
+ const struct req_msg_field *field);
+void *req_capsule_server_sized_get(struct req_capsule *pill,
+ const struct req_msg_field *field,
+ int len);
+void *req_capsule_server_swab_get(struct req_capsule *pill,
+ const struct req_msg_field *field,
+ void *swabber);
+void *req_capsule_server_sized_swab_get(struct req_capsule *pill,
+ const struct req_msg_field *field,
+ int len, void *swabber);
+const void *req_capsule_other_get(struct req_capsule *pill,
+ const struct req_msg_field *field);
+
+void req_capsule_set_size(struct req_capsule *pill,
+ const struct req_msg_field *field,
+ enum req_location loc, int size);
+int req_capsule_get_size(const struct req_capsule *pill,
+ const struct req_msg_field *field,
+ enum req_location loc);
+int req_capsule_msg_size(struct req_capsule *pill, enum req_location loc);
+int req_capsule_fmt_size(__u32 magic, const struct req_format *fmt,
+ enum req_location loc);
+void req_capsule_extend(struct req_capsule *pill, const struct req_format *fmt);
+
+int req_capsule_has_field(const struct req_capsule *pill,
+ const struct req_msg_field *field,
+ enum req_location loc);
+int req_capsule_field_present(const struct req_capsule *pill,
+ const struct req_msg_field *field,
+ enum req_location loc);
+void req_capsule_shrink(struct req_capsule *pill,
+ const struct req_msg_field *field,
+ unsigned int newlen,
+ enum req_location loc);
+int req_capsule_server_grow(struct req_capsule *pill,
+ const struct req_msg_field *field,
+ unsigned int newlen);
+int req_layout_init(void);
+void req_layout_fini(void);
+
+/* __REQ_LAYOUT_USER__ */
+#endif
+
+extern struct req_format RQF_OBD_PING;
+extern struct req_format RQF_OBD_SET_INFO;
+extern struct req_format RQF_SEC_CTX;
+extern struct req_format RQF_OBD_IDX_READ;
+/* MGS req_format */
+extern struct req_format RQF_MGS_TARGET_REG;
+extern struct req_format RQF_MGS_SET_INFO;
+extern struct req_format RQF_MGS_CONFIG_READ;
+/* fid/fld req_format */
+extern struct req_format RQF_SEQ_QUERY;
+extern struct req_format RQF_FLD_QUERY;
+/* MDS req_format */
+extern struct req_format RQF_MDS_CONNECT;
+extern struct req_format RQF_MDS_DISCONNECT;
+extern struct req_format RQF_MDS_STATFS;
+extern struct req_format RQF_MDS_GETSTATUS;
+extern struct req_format RQF_MDS_SYNC;
+extern struct req_format RQF_MDS_GETXATTR;
+extern struct req_format RQF_MDS_GETATTR;
+extern struct req_format RQF_UPDATE_OBJ;
+
+/*
+ * This is format of direct (non-intent) MDS_GETATTR_NAME request.
+ */
+extern struct req_format RQF_MDS_GETATTR_NAME;
+extern struct req_format RQF_MDS_CLOSE;
+extern struct req_format RQF_MDS_RELEASE_CLOSE;
+extern struct req_format RQF_MDS_PIN;
+extern struct req_format RQF_MDS_UNPIN;
+extern struct req_format RQF_MDS_CONNECT;
+extern struct req_format RQF_MDS_DISCONNECT;
+extern struct req_format RQF_MDS_GET_INFO;
+extern struct req_format RQF_MDS_READPAGE;
+extern struct req_format RQF_MDS_WRITEPAGE;
+extern struct req_format RQF_MDS_IS_SUBDIR;
+extern struct req_format RQF_MDS_DONE_WRITING;
+extern struct req_format RQF_MDS_REINT;
+extern struct req_format RQF_MDS_REINT_CREATE;
+extern struct req_format RQF_MDS_REINT_CREATE_RMT_ACL;
+extern struct req_format RQF_MDS_REINT_CREATE_SLAVE;
+extern struct req_format RQF_MDS_REINT_CREATE_SYM;
+extern struct req_format RQF_MDS_REINT_OPEN;
+extern struct req_format RQF_MDS_REINT_UNLINK;
+extern struct req_format RQF_MDS_REINT_LINK;
+extern struct req_format RQF_MDS_REINT_RENAME;
+extern struct req_format RQF_MDS_REINT_SETATTR;
+extern struct req_format RQF_MDS_REINT_SETXATTR;
+extern struct req_format RQF_MDS_QUOTACHECK;
+extern struct req_format RQF_MDS_QUOTACTL;
+extern struct req_format RQF_QC_CALLBACK;
+extern struct req_format RQF_QUOTA_DQACQ;
+extern struct req_format RQF_MDS_SWAP_LAYOUTS;
+/* MDS hsm formats */
+extern struct req_format RQF_MDS_HSM_STATE_GET;
+extern struct req_format RQF_MDS_HSM_STATE_SET;
+extern struct req_format RQF_MDS_HSM_ACTION;
+extern struct req_format RQF_MDS_HSM_PROGRESS;
+extern struct req_format RQF_MDS_HSM_CT_REGISTER;
+extern struct req_format RQF_MDS_HSM_CT_UNREGISTER;
+extern struct req_format RQF_MDS_HSM_REQUEST;
+/* OST req_format */
+extern struct req_format RQF_OST_CONNECT;
+extern struct req_format RQF_OST_DISCONNECT;
+extern struct req_format RQF_OST_QUOTACHECK;
+extern struct req_format RQF_OST_QUOTACTL;
+extern struct req_format RQF_OST_GETATTR;
+extern struct req_format RQF_OST_SETATTR;
+extern struct req_format RQF_OST_CREATE;
+extern struct req_format RQF_OST_PUNCH;
+extern struct req_format RQF_OST_SYNC;
+extern struct req_format RQF_OST_DESTROY;
+extern struct req_format RQF_OST_BRW_READ;
+extern struct req_format RQF_OST_BRW_WRITE;
+extern struct req_format RQF_OST_STATFS;
+extern struct req_format RQF_OST_SET_GRANT_INFO;
+extern struct req_format RQF_OST_GET_INFO_GENERIC;
+extern struct req_format RQF_OST_GET_INFO_LAST_ID;
+extern struct req_format RQF_OST_GET_INFO_LAST_FID;
+extern struct req_format RQF_OST_SET_INFO_LAST_FID;
+extern struct req_format RQF_OST_GET_INFO_FIEMAP;
+
+/* LDLM req_format */
+extern struct req_format RQF_LDLM_ENQUEUE;
+extern struct req_format RQF_LDLM_ENQUEUE_LVB;
+extern struct req_format RQF_LDLM_CONVERT;
+extern struct req_format RQF_LDLM_INTENT;
+extern struct req_format RQF_LDLM_INTENT_BASIC;
+extern struct req_format RQF_LDLM_INTENT_LAYOUT;
+extern struct req_format RQF_LDLM_INTENT_GETATTR;
+extern struct req_format RQF_LDLM_INTENT_OPEN;
+extern struct req_format RQF_LDLM_INTENT_CREATE;
+extern struct req_format RQF_LDLM_INTENT_UNLINK;
+extern struct req_format RQF_LDLM_INTENT_GETXATTR;
+extern struct req_format RQF_LDLM_INTENT_QUOTA;
+extern struct req_format RQF_LDLM_CANCEL;
+extern struct req_format RQF_LDLM_CALLBACK;
+extern struct req_format RQF_LDLM_CP_CALLBACK;
+extern struct req_format RQF_LDLM_BL_CALLBACK;
+extern struct req_format RQF_LDLM_GL_CALLBACK;
+extern struct req_format RQF_LDLM_GL_DESC_CALLBACK;
+/* LOG req_format */
+extern struct req_format RQF_LOG_CANCEL;
+extern struct req_format RQF_LLOG_ORIGIN_HANDLE_CREATE;
+extern struct req_format RQF_LLOG_ORIGIN_HANDLE_DESTROY;
+extern struct req_format RQF_LLOG_ORIGIN_HANDLE_NEXT_BLOCK;
+extern struct req_format RQF_LLOG_ORIGIN_HANDLE_PREV_BLOCK;
+extern struct req_format RQF_LLOG_ORIGIN_HANDLE_READ_HEADER;
+extern struct req_format RQF_LLOG_ORIGIN_CONNECT;
+
+extern struct req_format RQF_CONNECT;
+
+extern struct req_msg_field RMF_GENERIC_DATA;
+extern struct req_msg_field RMF_PTLRPC_BODY;
+extern struct req_msg_field RMF_MDT_BODY;
+extern struct req_msg_field RMF_MDT_EPOCH;
+extern struct req_msg_field RMF_OBD_STATFS;
+extern struct req_msg_field RMF_NAME;
+extern struct req_msg_field RMF_SYMTGT;
+extern struct req_msg_field RMF_TGTUUID;
+extern struct req_msg_field RMF_CLUUID;
+extern struct req_msg_field RMF_SETINFO_VAL;
+extern struct req_msg_field RMF_SETINFO_KEY;
+extern struct req_msg_field RMF_GETINFO_VAL;
+extern struct req_msg_field RMF_GETINFO_VALLEN;
+extern struct req_msg_field RMF_GETINFO_KEY;
+extern struct req_msg_field RMF_IDX_INFO;
+extern struct req_msg_field RMF_CLOSE_DATA;
+
+/*
+ * connection handle received in MDS_CONNECT request.
+ */
+extern struct req_msg_field RMF_CONN;
+extern struct req_msg_field RMF_CONNECT_DATA;
+extern struct req_msg_field RMF_DLM_REQ;
+extern struct req_msg_field RMF_DLM_REP;
+extern struct req_msg_field RMF_DLM_LVB;
+extern struct req_msg_field RMF_DLM_GL_DESC;
+extern struct req_msg_field RMF_LDLM_INTENT;
+extern struct req_msg_field RMF_LAYOUT_INTENT;
+extern struct req_msg_field RMF_MDT_MD;
+extern struct req_msg_field RMF_REC_REINT;
+extern struct req_msg_field RMF_EADATA;
+extern struct req_msg_field RMF_EAVALS;
+extern struct req_msg_field RMF_EAVALS_LENS;
+extern struct req_msg_field RMF_ACL;
+extern struct req_msg_field RMF_LOGCOOKIES;
+extern struct req_msg_field RMF_CAPA1;
+extern struct req_msg_field RMF_CAPA2;
+extern struct req_msg_field RMF_OBD_QUOTACHECK;
+extern struct req_msg_field RMF_OBD_QUOTACTL;
+extern struct req_msg_field RMF_QUOTA_BODY;
+extern struct req_msg_field RMF_STRING;
+extern struct req_msg_field RMF_SWAP_LAYOUTS;
+extern struct req_msg_field RMF_MDS_HSM_PROGRESS;
+extern struct req_msg_field RMF_MDS_HSM_REQUEST;
+extern struct req_msg_field RMF_MDS_HSM_USER_ITEM;
+extern struct req_msg_field RMF_MDS_HSM_ARCHIVE;
+extern struct req_msg_field RMF_HSM_USER_STATE;
+extern struct req_msg_field RMF_HSM_STATE_SET;
+extern struct req_msg_field RMF_MDS_HSM_CURRENT_ACTION;
+extern struct req_msg_field RMF_MDS_HSM_REQUEST;
+
+/* seq-mgr fields */
+extern struct req_msg_field RMF_SEQ_OPC;
+extern struct req_msg_field RMF_SEQ_RANGE;
+extern struct req_msg_field RMF_FID_SPACE;
+
+/* FLD fields */
+extern struct req_msg_field RMF_FLD_OPC;
+extern struct req_msg_field RMF_FLD_MDFLD;
+
+extern struct req_msg_field RMF_LLOGD_BODY;
+extern struct req_msg_field RMF_LLOG_LOG_HDR;
+extern struct req_msg_field RMF_LLOGD_CONN_BODY;
+
+extern struct req_msg_field RMF_MGS_TARGET_INFO;
+extern struct req_msg_field RMF_MGS_SEND_PARAM;
+
+extern struct req_msg_field RMF_OST_BODY;
+extern struct req_msg_field RMF_OBD_IOOBJ;
+extern struct req_msg_field RMF_OBD_ID;
+extern struct req_msg_field RMF_FID;
+extern struct req_msg_field RMF_NIOBUF_REMOTE;
+extern struct req_msg_field RMF_RCS;
+extern struct req_msg_field RMF_FIEMAP_KEY;
+extern struct req_msg_field RMF_FIEMAP_VAL;
+extern struct req_msg_field RMF_OST_ID;
+
+/* MGS config read message format */
+extern struct req_msg_field RMF_MGS_CONFIG_BODY;
+extern struct req_msg_field RMF_MGS_CONFIG_RES;
+
+/* generic uint32 */
+extern struct req_msg_field RMF_U32;
+
+/* OBJ update format */
+extern struct req_msg_field RMF_UPDATE;
+extern struct req_msg_field RMF_UPDATE_REPLY;
+/** @} req_layout */
+
+#endif /* _LUSTRE_REQ_LAYOUT_H__ */
diff --git a/drivers/staging/lustre/lustre/include/lustre_sec.h b/drivers/staging/lustre/lustre/include/lustre_sec.h
new file mode 100644
index 000000000..dff70a5b9
--- /dev/null
+++ b/drivers/staging/lustre/lustre/include/lustre_sec.h
@@ -0,0 +1,1147 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2012, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ */
+
+#ifndef _LUSTRE_SEC_H_
+#define _LUSTRE_SEC_H_
+
+/** \defgroup sptlrpc sptlrpc
+ *
+ * @{
+ */
+
+/*
+ * to avoid include
+ */
+struct obd_import;
+struct obd_export;
+struct ptlrpc_request;
+struct ptlrpc_reply_state;
+struct ptlrpc_bulk_desc;
+struct brw_page;
+/* Linux specific */
+struct key;
+struct seq_file;
+
+/*
+ * forward declaration
+ */
+struct ptlrpc_sec_policy;
+struct ptlrpc_sec_cops;
+struct ptlrpc_sec_sops;
+struct ptlrpc_sec;
+struct ptlrpc_svc_ctx;
+struct ptlrpc_cli_ctx;
+struct ptlrpc_ctx_ops;
+
+/**
+ * \addtogroup flavor flavor
+ *
+ * RPC flavor is represented by a 32 bits integer. Currently the high 12 bits
+ * are unused, must be set to 0 for future expansion.
+ * <pre>
+ * ------------------------------------------------------------------------
+ * | 4b (bulk svc) | 4b (bulk type) | 4b (svc) | 4b (mech) | 4b (policy) |
+ * ------------------------------------------------------------------------
+ * </pre>
+ *
+ * @{
+ */
+
+/*
+ * flavor constants
+ */
+enum sptlrpc_policy {
+ SPTLRPC_POLICY_NULL = 0,
+ SPTLRPC_POLICY_PLAIN = 1,
+ SPTLRPC_POLICY_GSS = 2,
+ SPTLRPC_POLICY_MAX,
+};
+
+enum sptlrpc_mech_null {
+ SPTLRPC_MECH_NULL = 0,
+ SPTLRPC_MECH_NULL_MAX,
+};
+
+enum sptlrpc_mech_plain {
+ SPTLRPC_MECH_PLAIN = 0,
+ SPTLRPC_MECH_PLAIN_MAX,
+};
+
+enum sptlrpc_mech_gss {
+ SPTLRPC_MECH_GSS_NULL = 0,
+ SPTLRPC_MECH_GSS_KRB5 = 1,
+ SPTLRPC_MECH_GSS_MAX,
+};
+
+enum sptlrpc_service_type {
+ SPTLRPC_SVC_NULL = 0, /**< no security */
+ SPTLRPC_SVC_AUTH = 1, /**< authentication only */
+ SPTLRPC_SVC_INTG = 2, /**< integrity */
+ SPTLRPC_SVC_PRIV = 3, /**< privacy */
+ SPTLRPC_SVC_MAX,
+};
+
+enum sptlrpc_bulk_type {
+ SPTLRPC_BULK_DEFAULT = 0, /**< follow rpc flavor */
+ SPTLRPC_BULK_HASH = 1, /**< hash integrity */
+ SPTLRPC_BULK_MAX,
+};
+
+enum sptlrpc_bulk_service {
+ SPTLRPC_BULK_SVC_NULL = 0, /**< no security */
+ SPTLRPC_BULK_SVC_AUTH = 1, /**< authentication only */
+ SPTLRPC_BULK_SVC_INTG = 2, /**< integrity */
+ SPTLRPC_BULK_SVC_PRIV = 3, /**< privacy */
+ SPTLRPC_BULK_SVC_MAX,
+};
+
+/*
+ * compose/extract macros
+ */
+#define FLVR_POLICY_OFFSET (0)
+#define FLVR_MECH_OFFSET (4)
+#define FLVR_SVC_OFFSET (8)
+#define FLVR_BULK_TYPE_OFFSET (12)
+#define FLVR_BULK_SVC_OFFSET (16)
+
+#define MAKE_FLVR(policy, mech, svc, btype, bsvc) \
+ (((__u32)(policy) << FLVR_POLICY_OFFSET) | \
+ ((__u32)(mech) << FLVR_MECH_OFFSET) | \
+ ((__u32)(svc) << FLVR_SVC_OFFSET) | \
+ ((__u32)(btype) << FLVR_BULK_TYPE_OFFSET) | \
+ ((__u32)(bsvc) << FLVR_BULK_SVC_OFFSET))
+
+/*
+ * extraction
+ */
+#define SPTLRPC_FLVR_POLICY(flavor) \
+ ((((__u32)(flavor)) >> FLVR_POLICY_OFFSET) & 0xF)
+#define SPTLRPC_FLVR_MECH(flavor) \
+ ((((__u32)(flavor)) >> FLVR_MECH_OFFSET) & 0xF)
+#define SPTLRPC_FLVR_SVC(flavor) \
+ ((((__u32)(flavor)) >> FLVR_SVC_OFFSET) & 0xF)
+#define SPTLRPC_FLVR_BULK_TYPE(flavor) \
+ ((((__u32)(flavor)) >> FLVR_BULK_TYPE_OFFSET) & 0xF)
+#define SPTLRPC_FLVR_BULK_SVC(flavor) \
+ ((((__u32)(flavor)) >> FLVR_BULK_SVC_OFFSET) & 0xF)
+
+#define SPTLRPC_FLVR_BASE(flavor) \
+ ((((__u32)(flavor)) >> FLVR_POLICY_OFFSET) & 0xFFF)
+#define SPTLRPC_FLVR_BASE_SUB(flavor) \
+ ((((__u32)(flavor)) >> FLVR_MECH_OFFSET) & 0xFF)
+
+/*
+ * gss subflavors
+ */
+#define MAKE_BASE_SUBFLVR(mech, svc) \
+ ((__u32)(mech) | \
+ ((__u32)(svc) << (FLVR_SVC_OFFSET - FLVR_MECH_OFFSET)))
+
+#define SPTLRPC_SUBFLVR_KRB5N \
+ MAKE_BASE_SUBFLVR(SPTLRPC_MECH_GSS_KRB5, SPTLRPC_SVC_NULL)
+#define SPTLRPC_SUBFLVR_KRB5A \
+ MAKE_BASE_SUBFLVR(SPTLRPC_MECH_GSS_KRB5, SPTLRPC_SVC_AUTH)
+#define SPTLRPC_SUBFLVR_KRB5I \
+ MAKE_BASE_SUBFLVR(SPTLRPC_MECH_GSS_KRB5, SPTLRPC_SVC_INTG)
+#define SPTLRPC_SUBFLVR_KRB5P \
+ MAKE_BASE_SUBFLVR(SPTLRPC_MECH_GSS_KRB5, SPTLRPC_SVC_PRIV)
+
+/*
+ * "end user" flavors
+ */
+#define SPTLRPC_FLVR_NULL \
+ MAKE_FLVR(SPTLRPC_POLICY_NULL, \
+ SPTLRPC_MECH_NULL, \
+ SPTLRPC_SVC_NULL, \
+ SPTLRPC_BULK_DEFAULT, \
+ SPTLRPC_BULK_SVC_NULL)
+#define SPTLRPC_FLVR_PLAIN \
+ MAKE_FLVR(SPTLRPC_POLICY_PLAIN, \
+ SPTLRPC_MECH_PLAIN, \
+ SPTLRPC_SVC_NULL, \
+ SPTLRPC_BULK_HASH, \
+ SPTLRPC_BULK_SVC_INTG)
+#define SPTLRPC_FLVR_KRB5N \
+ MAKE_FLVR(SPTLRPC_POLICY_GSS, \
+ SPTLRPC_MECH_GSS_KRB5, \
+ SPTLRPC_SVC_NULL, \
+ SPTLRPC_BULK_DEFAULT, \
+ SPTLRPC_BULK_SVC_NULL)
+#define SPTLRPC_FLVR_KRB5A \
+ MAKE_FLVR(SPTLRPC_POLICY_GSS, \
+ SPTLRPC_MECH_GSS_KRB5, \
+ SPTLRPC_SVC_AUTH, \
+ SPTLRPC_BULK_DEFAULT, \
+ SPTLRPC_BULK_SVC_NULL)
+#define SPTLRPC_FLVR_KRB5I \
+ MAKE_FLVR(SPTLRPC_POLICY_GSS, \
+ SPTLRPC_MECH_GSS_KRB5, \
+ SPTLRPC_SVC_INTG, \
+ SPTLRPC_BULK_DEFAULT, \
+ SPTLRPC_BULK_SVC_INTG)
+#define SPTLRPC_FLVR_KRB5P \
+ MAKE_FLVR(SPTLRPC_POLICY_GSS, \
+ SPTLRPC_MECH_GSS_KRB5, \
+ SPTLRPC_SVC_PRIV, \
+ SPTLRPC_BULK_DEFAULT, \
+ SPTLRPC_BULK_SVC_PRIV)
+
+#define SPTLRPC_FLVR_DEFAULT SPTLRPC_FLVR_NULL
+
+#define SPTLRPC_FLVR_INVALID ((__u32) 0xFFFFFFFF)
+#define SPTLRPC_FLVR_ANY ((__u32) 0xFFF00000)
+
+/**
+ * extract the useful part from wire flavor
+ */
+#define WIRE_FLVR(wflvr) (((__u32) (wflvr)) & 0x000FFFFF)
+
+/** @} flavor */
+
+static inline void flvr_set_svc(__u32 *flvr, __u32 svc)
+{
+ LASSERT(svc < SPTLRPC_SVC_MAX);
+ *flvr = MAKE_FLVR(SPTLRPC_FLVR_POLICY(*flvr),
+ SPTLRPC_FLVR_MECH(*flvr),
+ svc,
+ SPTLRPC_FLVR_BULK_TYPE(*flvr),
+ SPTLRPC_FLVR_BULK_SVC(*flvr));
+}
+
+static inline void flvr_set_bulk_svc(__u32 *flvr, __u32 svc)
+{
+ LASSERT(svc < SPTLRPC_BULK_SVC_MAX);
+ *flvr = MAKE_FLVR(SPTLRPC_FLVR_POLICY(*flvr),
+ SPTLRPC_FLVR_MECH(*flvr),
+ SPTLRPC_FLVR_SVC(*flvr),
+ SPTLRPC_FLVR_BULK_TYPE(*flvr),
+ svc);
+}
+
+struct bulk_spec_hash {
+ __u8 hash_alg;
+};
+
+/**
+ * Full description of flavors being used on a ptlrpc connection, include
+ * both regular RPC and bulk transfer parts.
+ */
+struct sptlrpc_flavor {
+ /**
+ * wire flavor, should be renamed to sf_wire.
+ */
+ __u32 sf_rpc;
+ /**
+ * general flags of PTLRPC_SEC_FL_*
+ */
+ __u32 sf_flags;
+ /**
+ * rpc flavor specification
+ */
+ union {
+ /* nothing for now */
+ } u_rpc;
+ /**
+ * bulk flavor specification
+ */
+ union {
+ struct bulk_spec_hash hash;
+ } u_bulk;
+};
+
+/**
+ * identify the RPC is generated from what part of Lustre. It's encoded into
+ * RPC requests and to be checked by ptlrpc service.
+ */
+enum lustre_sec_part {
+ LUSTRE_SP_CLI = 0,
+ LUSTRE_SP_MDT,
+ LUSTRE_SP_OST,
+ LUSTRE_SP_MGC,
+ LUSTRE_SP_MGS,
+ LUSTRE_SP_ANY = 0xFF
+};
+
+const char *sptlrpc_part2name(enum lustre_sec_part sp);
+enum lustre_sec_part sptlrpc_target_sec_part(struct obd_device *obd);
+
+/**
+ * A rule specifies a flavor to be used by a ptlrpc connection between
+ * two Lustre parts.
+ */
+struct sptlrpc_rule {
+ __u32 sr_netid; /* LNET network ID */
+ __u8 sr_from; /* sec_part */
+ __u8 sr_to; /* sec_part */
+ __u16 sr_padding;
+ struct sptlrpc_flavor sr_flvr;
+};
+
+/**
+ * A set of rules in memory.
+ *
+ * Rules are generated and stored on MGS, and propagated to MDT, OST,
+ * and client when needed.
+ */
+struct sptlrpc_rule_set {
+ int srs_nslot;
+ int srs_nrule;
+ struct sptlrpc_rule *srs_rules;
+};
+
+int sptlrpc_parse_flavor(const char *str, struct sptlrpc_flavor *flvr);
+int sptlrpc_flavor_has_bulk(struct sptlrpc_flavor *flvr);
+
+static inline void sptlrpc_rule_set_init(struct sptlrpc_rule_set *set)
+{
+ memset(set, 0, sizeof(*set));
+}
+
+void sptlrpc_rule_set_free(struct sptlrpc_rule_set *set);
+int sptlrpc_rule_set_expand(struct sptlrpc_rule_set *set);
+int sptlrpc_rule_set_merge(struct sptlrpc_rule_set *set,
+ struct sptlrpc_rule *rule);
+int sptlrpc_rule_set_choose(struct sptlrpc_rule_set *rset,
+ enum lustre_sec_part from,
+ enum lustre_sec_part to,
+ lnet_nid_t nid,
+ struct sptlrpc_flavor *sf);
+void sptlrpc_rule_set_dump(struct sptlrpc_rule_set *set);
+
+int sptlrpc_process_config(struct lustre_cfg *lcfg);
+void sptlrpc_conf_log_start(const char *logname);
+void sptlrpc_conf_log_stop(const char *logname);
+void sptlrpc_conf_log_update_begin(const char *logname);
+void sptlrpc_conf_log_update_end(const char *logname);
+void sptlrpc_conf_client_adapt(struct obd_device *obd);
+void sptlrpc_target_choose_flavor(struct sptlrpc_rule_set *rset,
+ enum lustre_sec_part from,
+ lnet_nid_t nid,
+ struct sptlrpc_flavor *flavor);
+
+/* The maximum length of security payload. 1024 is enough for Kerberos 5,
+ * and should be enough for other future mechanisms but not sure.
+ * Only used by pre-allocated request/reply pool.
+ */
+#define SPTLRPC_MAX_PAYLOAD (1024)
+
+
+struct vfs_cred {
+ uint32_t vc_uid;
+ uint32_t vc_gid;
+};
+
+struct ptlrpc_ctx_ops {
+ /**
+ * To determine whether it's suitable to use the \a ctx for \a vcred.
+ */
+ int (*match) (struct ptlrpc_cli_ctx *ctx,
+ struct vfs_cred *vcred);
+
+ /**
+ * To bring the \a ctx uptodate.
+ */
+ int (*refresh) (struct ptlrpc_cli_ctx *ctx);
+
+ /**
+ * Validate the \a ctx.
+ */
+ int (*validate) (struct ptlrpc_cli_ctx *ctx);
+
+ /**
+ * Force the \a ctx to die.
+ */
+ void (*force_die) (struct ptlrpc_cli_ctx *ctx,
+ int grace);
+ int (*display) (struct ptlrpc_cli_ctx *ctx,
+ char *buf, int bufsize);
+
+ /**
+ * Sign the request message using \a ctx.
+ *
+ * \pre req->rq_reqmsg point to request message.
+ * \pre req->rq_reqlen is the request message length.
+ * \post req->rq_reqbuf point to request message with signature.
+ * \post req->rq_reqdata_len is set to the final request message size.
+ *
+ * \see null_ctx_sign(), plain_ctx_sign(), gss_cli_ctx_sign().
+ */
+ int (*sign) (struct ptlrpc_cli_ctx *ctx,
+ struct ptlrpc_request *req);
+
+ /**
+ * Verify the reply message using \a ctx.
+ *
+ * \pre req->rq_repdata point to reply message with signature.
+ * \pre req->rq_repdata_len is the total reply message length.
+ * \post req->rq_repmsg point to reply message without signature.
+ * \post req->rq_replen is the reply message length.
+ *
+ * \see null_ctx_verify(), plain_ctx_verify(), gss_cli_ctx_verify().
+ */
+ int (*verify) (struct ptlrpc_cli_ctx *ctx,
+ struct ptlrpc_request *req);
+
+ /**
+ * Encrypt the request message using \a ctx.
+ *
+ * \pre req->rq_reqmsg point to request message in clear text.
+ * \pre req->rq_reqlen is the request message length.
+ * \post req->rq_reqbuf point to request message.
+ * \post req->rq_reqdata_len is set to the final request message size.
+ *
+ * \see gss_cli_ctx_seal().
+ */
+ int (*seal) (struct ptlrpc_cli_ctx *ctx,
+ struct ptlrpc_request *req);
+
+ /**
+ * Decrypt the reply message using \a ctx.
+ *
+ * \pre req->rq_repdata point to encrypted reply message.
+ * \pre req->rq_repdata_len is the total cipher text length.
+ * \post req->rq_repmsg point to reply message in clear text.
+ * \post req->rq_replen is the reply message length in clear text.
+ *
+ * \see gss_cli_ctx_unseal().
+ */
+ int (*unseal) (struct ptlrpc_cli_ctx *ctx,
+ struct ptlrpc_request *req);
+
+ /**
+ * Wrap bulk request data. This is called before wrapping RPC
+ * request message.
+ *
+ * \pre bulk buffer is descripted by desc->bd_iov and
+ * desc->bd_iov_count. note for read it's just buffer, no data
+ * need to be sent; for write it contains data in clear text.
+ * \post when necessary, ptlrpc_bulk_sec_desc was properly prepared
+ * (usually inside of RPC request message).
+ * - encryption: cipher text bulk buffer is descripted by
+ * desc->bd_enc_iov and desc->bd_iov_count (currently assume iov
+ * count remains the same).
+ * - otherwise: bulk buffer is still desc->bd_iov and
+ * desc->bd_iov_count.
+ *
+ * \return 0: success.
+ * \return -ev: error code.
+ *
+ * \see plain_cli_wrap_bulk(), gss_cli_ctx_wrap_bulk().
+ */
+ int (*wrap_bulk) (struct ptlrpc_cli_ctx *ctx,
+ struct ptlrpc_request *req,
+ struct ptlrpc_bulk_desc *desc);
+
+ /**
+ * Unwrap bulk reply data. This is called after wrapping RPC
+ * reply message.
+ *
+ * \pre bulk buffer is descripted by desc->bd_iov/desc->bd_enc_iov and
+ * desc->bd_iov_count, according to wrap_bulk().
+ * \post final bulk data in clear text is placed in buffer described
+ * by desc->bd_iov and desc->bd_iov_count.
+ * \return +ve nob of actual bulk data in clear text.
+ * \return -ve error code.
+ *
+ * \see plain_cli_unwrap_bulk(), gss_cli_ctx_unwrap_bulk().
+ */
+ int (*unwrap_bulk) (struct ptlrpc_cli_ctx *ctx,
+ struct ptlrpc_request *req,
+ struct ptlrpc_bulk_desc *desc);
+};
+
+#define PTLRPC_CTX_NEW_BIT (0) /* newly created */
+#define PTLRPC_CTX_UPTODATE_BIT (1) /* uptodate */
+#define PTLRPC_CTX_DEAD_BIT (2) /* mark expired gracefully */
+#define PTLRPC_CTX_ERROR_BIT (3) /* fatal error (refresh, etc.) */
+#define PTLRPC_CTX_CACHED_BIT (8) /* in ctx cache (hash etc.) */
+#define PTLRPC_CTX_ETERNAL_BIT (9) /* always valid */
+
+#define PTLRPC_CTX_NEW (1 << PTLRPC_CTX_NEW_BIT)
+#define PTLRPC_CTX_UPTODATE (1 << PTLRPC_CTX_UPTODATE_BIT)
+#define PTLRPC_CTX_DEAD (1 << PTLRPC_CTX_DEAD_BIT)
+#define PTLRPC_CTX_ERROR (1 << PTLRPC_CTX_ERROR_BIT)
+#define PTLRPC_CTX_CACHED (1 << PTLRPC_CTX_CACHED_BIT)
+#define PTLRPC_CTX_ETERNAL (1 << PTLRPC_CTX_ETERNAL_BIT)
+
+#define PTLRPC_CTX_STATUS_MASK (PTLRPC_CTX_NEW_BIT | \
+ PTLRPC_CTX_UPTODATE | \
+ PTLRPC_CTX_DEAD | \
+ PTLRPC_CTX_ERROR)
+
+struct ptlrpc_cli_ctx {
+ struct hlist_node cc_cache; /* linked into ctx cache */
+ atomic_t cc_refcount;
+ struct ptlrpc_sec *cc_sec;
+ struct ptlrpc_ctx_ops *cc_ops;
+ unsigned long cc_expire; /* in seconds */
+ unsigned int cc_early_expire:1;
+ unsigned long cc_flags;
+ struct vfs_cred cc_vcred;
+ spinlock_t cc_lock;
+ struct list_head cc_req_list; /* waiting reqs linked here */
+ struct list_head cc_gc_chain; /* linked to gc chain */
+};
+
+/**
+ * client side policy operation vector.
+ */
+struct ptlrpc_sec_cops {
+ /**
+ * Given an \a imp, create and initialize a ptlrpc_sec structure.
+ * \param ctx service context:
+ * - regular import: \a ctx should be NULL;
+ * - reverse import: \a ctx is obtained from incoming request.
+ * \param flavor specify what flavor to use.
+ *
+ * When necessary, policy module is responsible for taking reference
+ * on the import.
+ *
+ * \see null_create_sec(), plain_create_sec(), gss_sec_create_kr().
+ */
+ struct ptlrpc_sec * (*create_sec) (struct obd_import *imp,
+ struct ptlrpc_svc_ctx *ctx,
+ struct sptlrpc_flavor *flavor);
+
+ /**
+ * Destructor of ptlrpc_sec. When called, refcount has been dropped
+ * to 0 and all contexts has been destroyed.
+ *
+ * \see null_destroy_sec(), plain_destroy_sec(), gss_sec_destroy_kr().
+ */
+ void (*destroy_sec) (struct ptlrpc_sec *sec);
+
+ /**
+ * Notify that this ptlrpc_sec is going to die. Optionally, policy
+ * module is supposed to set sec->ps_dying and whatever necessary
+ * actions.
+ *
+ * \see plain_kill_sec(), gss_sec_kill().
+ */
+ void (*kill_sec) (struct ptlrpc_sec *sec);
+
+ /**
+ * Given \a vcred, lookup and/or create its context. The policy module
+ * is supposed to maintain its own context cache.
+ * XXX currently \a create and \a remove_dead is always 1, perhaps
+ * should be removed completely.
+ *
+ * \see null_lookup_ctx(), plain_lookup_ctx(), gss_sec_lookup_ctx_kr().
+ */
+ struct ptlrpc_cli_ctx * (*lookup_ctx) (struct ptlrpc_sec *sec,
+ struct vfs_cred *vcred,
+ int create,
+ int remove_dead);
+
+ /**
+ * Called then the reference of \a ctx dropped to 0. The policy module
+ * is supposed to destroy this context or whatever else according to
+ * its cache maintenance mechanism.
+ *
+ * \param sync if zero, we shouldn't wait for the context being
+ * destroyed completely.
+ *
+ * \see plain_release_ctx(), gss_sec_release_ctx_kr().
+ */
+ void (*release_ctx) (struct ptlrpc_sec *sec,
+ struct ptlrpc_cli_ctx *ctx,
+ int sync);
+
+ /**
+ * Flush the context cache.
+ *
+ * \param uid context of which user, -1 means all contexts.
+ * \param grace if zero, the PTLRPC_CTX_UPTODATE_BIT of affected
+ * contexts should be cleared immediately.
+ * \param force if zero, only idle contexts will be flushed.
+ *
+ * \see plain_flush_ctx_cache(), gss_sec_flush_ctx_cache_kr().
+ */
+ int (*flush_ctx_cache)
+ (struct ptlrpc_sec *sec,
+ uid_t uid,
+ int grace,
+ int force);
+
+ /**
+ * Called periodically by garbage collector to remove dead contexts
+ * from cache.
+ *
+ * \see gss_sec_gc_ctx_kr().
+ */
+ void (*gc_ctx) (struct ptlrpc_sec *sec);
+
+ /**
+ * Given an context \a ctx, install a corresponding reverse service
+ * context on client side.
+ * XXX currently it's only used by GSS module, maybe we should remove
+ * this from general API.
+ */
+ int (*install_rctx)(struct obd_import *imp,
+ struct ptlrpc_sec *sec,
+ struct ptlrpc_cli_ctx *ctx);
+
+ /**
+ * To allocate request buffer for \a req.
+ *
+ * \pre req->rq_reqmsg == NULL.
+ * \pre req->rq_reqbuf == NULL, otherwise it must be pre-allocated,
+ * we are not supposed to free it.
+ * \post if success, req->rq_reqmsg point to a buffer with size
+ * at least \a lustre_msg_size.
+ *
+ * \see null_alloc_reqbuf(), plain_alloc_reqbuf(), gss_alloc_reqbuf().
+ */
+ int (*alloc_reqbuf)(struct ptlrpc_sec *sec,
+ struct ptlrpc_request *req,
+ int lustre_msg_size);
+
+ /**
+ * To free request buffer for \a req.
+ *
+ * \pre req->rq_reqbuf != NULL.
+ *
+ * \see null_free_reqbuf(), plain_free_reqbuf(), gss_free_reqbuf().
+ */
+ void (*free_reqbuf) (struct ptlrpc_sec *sec,
+ struct ptlrpc_request *req);
+
+ /**
+ * To allocate reply buffer for \a req.
+ *
+ * \pre req->rq_repbuf == NULL.
+ * \post if success, req->rq_repbuf point to a buffer with size
+ * req->rq_repbuf_len, the size should be large enough to receive
+ * reply which be transformed from \a lustre_msg_size of clear text.
+ *
+ * \see null_alloc_repbuf(), plain_alloc_repbuf(), gss_alloc_repbuf().
+ */
+ int (*alloc_repbuf)(struct ptlrpc_sec *sec,
+ struct ptlrpc_request *req,
+ int lustre_msg_size);
+
+ /**
+ * To free reply buffer for \a req.
+ *
+ * \pre req->rq_repbuf != NULL.
+ * \post req->rq_repbuf == NULL.
+ * \post req->rq_repbuf_len == 0.
+ *
+ * \see null_free_repbuf(), plain_free_repbuf(), gss_free_repbuf().
+ */
+ void (*free_repbuf) (struct ptlrpc_sec *sec,
+ struct ptlrpc_request *req);
+
+ /**
+ * To expand the request buffer of \a req, thus the \a segment in
+ * the request message pointed by req->rq_reqmsg can accommodate
+ * at least \a newsize of data.
+ *
+ * \pre req->rq_reqmsg->lm_buflens[segment] < newsize.
+ *
+ * \see null_enlarge_reqbuf(), plain_enlarge_reqbuf(),
+ * gss_enlarge_reqbuf().
+ */
+ int (*enlarge_reqbuf)
+ (struct ptlrpc_sec *sec,
+ struct ptlrpc_request *req,
+ int segment, int newsize);
+ /*
+ * misc
+ */
+ int (*display) (struct ptlrpc_sec *sec,
+ struct seq_file *seq);
+};
+
+/**
+ * server side policy operation vector.
+ */
+struct ptlrpc_sec_sops {
+ /**
+ * verify an incoming request.
+ *
+ * \pre request message is pointed by req->rq_reqbuf, size is
+ * req->rq_reqdata_len; and the message has been unpacked to
+ * host byte order.
+ *
+ * \retval SECSVC_OK success, req->rq_reqmsg point to request message
+ * in clear text, size is req->rq_reqlen; req->rq_svc_ctx is set;
+ * req->rq_sp_from is decoded from request.
+ * \retval SECSVC_COMPLETE success, the request has been fully
+ * processed, and reply message has been prepared; req->rq_sp_from is
+ * decoded from request.
+ * \retval SECSVC_DROP failed, this request should be dropped.
+ *
+ * \see null_accept(), plain_accept(), gss_svc_accept_kr().
+ */
+ int (*accept) (struct ptlrpc_request *req);
+
+ /**
+ * Perform security transformation upon reply message.
+ *
+ * \pre reply message is pointed by req->rq_reply_state->rs_msg, size
+ * is req->rq_replen.
+ * \post req->rs_repdata_len is the final message size.
+ * \post req->rq_reply_off is set.
+ *
+ * \see null_authorize(), plain_authorize(), gss_svc_authorize().
+ */
+ int (*authorize) (struct ptlrpc_request *req);
+
+ /**
+ * Invalidate server context \a ctx.
+ *
+ * \see gss_svc_invalidate_ctx().
+ */
+ void (*invalidate_ctx)
+ (struct ptlrpc_svc_ctx *ctx);
+
+ /**
+ * Allocate a ptlrpc_reply_state.
+ *
+ * \param msgsize size of the reply message in clear text.
+ * \pre if req->rq_reply_state != NULL, then it's pre-allocated, we
+ * should simply use it; otherwise we'll responsible for allocating
+ * a new one.
+ * \post req->rq_reply_state != NULL;
+ * \post req->rq_reply_state->rs_msg != NULL;
+ *
+ * \see null_alloc_rs(), plain_alloc_rs(), gss_svc_alloc_rs().
+ */
+ int (*alloc_rs) (struct ptlrpc_request *req,
+ int msgsize);
+
+ /**
+ * Free a ptlrpc_reply_state.
+ */
+ void (*free_rs) (struct ptlrpc_reply_state *rs);
+
+ /**
+ * Release the server context \a ctx.
+ *
+ * \see gss_svc_free_ctx().
+ */
+ void (*free_ctx) (struct ptlrpc_svc_ctx *ctx);
+
+ /**
+ * Install a reverse context based on the server context \a ctx.
+ *
+ * \see gss_svc_install_rctx_kr().
+ */
+ int (*install_rctx)(struct obd_import *imp,
+ struct ptlrpc_svc_ctx *ctx);
+
+ /**
+ * Prepare buffer for incoming bulk write.
+ *
+ * \pre desc->bd_iov and desc->bd_iov_count describes the buffer
+ * intended to receive the write.
+ *
+ * \see gss_svc_prep_bulk().
+ */
+ int (*prep_bulk) (struct ptlrpc_request *req,
+ struct ptlrpc_bulk_desc *desc);
+
+ /**
+ * Unwrap the bulk write data.
+ *
+ * \see plain_svc_unwrap_bulk(), gss_svc_unwrap_bulk().
+ */
+ int (*unwrap_bulk) (struct ptlrpc_request *req,
+ struct ptlrpc_bulk_desc *desc);
+
+ /**
+ * Wrap the bulk read data.
+ *
+ * \see plain_svc_wrap_bulk(), gss_svc_wrap_bulk().
+ */
+ int (*wrap_bulk) (struct ptlrpc_request *req,
+ struct ptlrpc_bulk_desc *desc);
+};
+
+struct ptlrpc_sec_policy {
+ struct module *sp_owner;
+ char *sp_name;
+ __u16 sp_policy; /* policy number */
+ struct ptlrpc_sec_cops *sp_cops; /* client ops */
+ struct ptlrpc_sec_sops *sp_sops; /* server ops */
+};
+
+#define PTLRPC_SEC_FL_REVERSE 0x0001 /* reverse sec */
+#define PTLRPC_SEC_FL_ROOTONLY 0x0002 /* treat everyone as root */
+#define PTLRPC_SEC_FL_UDESC 0x0004 /* ship udesc */
+#define PTLRPC_SEC_FL_BULK 0x0008 /* intensive bulk i/o expected */
+#define PTLRPC_SEC_FL_PAG 0x0010 /* PAG mode */
+
+/**
+ * The ptlrpc_sec represents the client side ptlrpc security facilities,
+ * each obd_import (both regular and reverse import) must associate with
+ * a ptlrpc_sec.
+ *
+ * \see sptlrpc_import_sec_adapt().
+ */
+struct ptlrpc_sec {
+ struct ptlrpc_sec_policy *ps_policy;
+ atomic_t ps_refcount;
+ /** statistic only */
+ atomic_t ps_nctx;
+ /** unique identifier */
+ int ps_id;
+ struct sptlrpc_flavor ps_flvr;
+ enum lustre_sec_part ps_part;
+ /** after set, no more new context will be created */
+ unsigned int ps_dying:1;
+ /** owning import */
+ struct obd_import *ps_import;
+ spinlock_t ps_lock;
+
+ /*
+ * garbage collection
+ */
+ struct list_head ps_gc_list;
+ unsigned long ps_gc_interval; /* in seconds */
+ unsigned long ps_gc_next; /* in seconds */
+};
+
+static inline int sec_is_reverse(struct ptlrpc_sec *sec)
+{
+ return (sec->ps_flvr.sf_flags & PTLRPC_SEC_FL_REVERSE);
+}
+
+static inline int sec_is_rootonly(struct ptlrpc_sec *sec)
+{
+ return (sec->ps_flvr.sf_flags & PTLRPC_SEC_FL_ROOTONLY);
+}
+
+
+struct ptlrpc_svc_ctx {
+ atomic_t sc_refcount;
+ struct ptlrpc_sec_policy *sc_policy;
+};
+
+/*
+ * user identity descriptor
+ */
+#define LUSTRE_MAX_GROUPS (128)
+
+struct ptlrpc_user_desc {
+ __u32 pud_uid;
+ __u32 pud_gid;
+ __u32 pud_fsuid;
+ __u32 pud_fsgid;
+ __u32 pud_cap;
+ __u32 pud_ngroups;
+ __u32 pud_groups[0];
+};
+
+/*
+ * bulk flavors
+ */
+enum sptlrpc_bulk_hash_alg {
+ BULK_HASH_ALG_NULL = 0,
+ BULK_HASH_ALG_ADLER32,
+ BULK_HASH_ALG_CRC32,
+ BULK_HASH_ALG_MD5,
+ BULK_HASH_ALG_SHA1,
+ BULK_HASH_ALG_SHA256,
+ BULK_HASH_ALG_SHA384,
+ BULK_HASH_ALG_SHA512,
+ BULK_HASH_ALG_MAX
+};
+
+const char *sptlrpc_get_hash_name(__u8 hash_alg);
+__u8 sptlrpc_get_hash_alg(const char *algname);
+
+enum {
+ BSD_FL_ERR = 1,
+};
+
+struct ptlrpc_bulk_sec_desc {
+ __u8 bsd_version; /* 0 */
+ __u8 bsd_type; /* SPTLRPC_BULK_XXX */
+ __u8 bsd_svc; /* SPTLRPC_BULK_SVC_XXXX */
+ __u8 bsd_flags; /* flags */
+ __u32 bsd_nob; /* nob of bulk data */
+ __u8 bsd_data[0]; /* policy-specific token */
+};
+
+
+/*
+ * round size up to next power of 2, for slab allocation.
+ * @size must be sane (can't overflow after round up)
+ */
+static inline int size_roundup_power2(int size)
+{
+ size--;
+ size |= size >> 1;
+ size |= size >> 2;
+ size |= size >> 4;
+ size |= size >> 8;
+ size |= size >> 16;
+ size++;
+ return size;
+}
+
+/*
+ * internal support libraries
+ */
+void _sptlrpc_enlarge_msg_inplace(struct lustre_msg *msg,
+ int segment, int newsize);
+
+/*
+ * security policies
+ */
+int sptlrpc_register_policy(struct ptlrpc_sec_policy *policy);
+int sptlrpc_unregister_policy(struct ptlrpc_sec_policy *policy);
+
+__u32 sptlrpc_name2flavor_base(const char *name);
+const char *sptlrpc_flavor2name_base(__u32 flvr);
+char *sptlrpc_flavor2name_bulk(struct sptlrpc_flavor *sf,
+ char *buf, int bufsize);
+char *sptlrpc_flavor2name(struct sptlrpc_flavor *sf, char *buf, int bufsize);
+char *sptlrpc_secflags2str(__u32 flags, char *buf, int bufsize);
+
+static inline
+struct ptlrpc_sec_policy *sptlrpc_policy_get(struct ptlrpc_sec_policy *policy)
+{
+ __module_get(policy->sp_owner);
+ return policy;
+}
+
+static inline
+void sptlrpc_policy_put(struct ptlrpc_sec_policy *policy)
+{
+ module_put(policy->sp_owner);
+}
+
+/*
+ * client credential
+ */
+static inline
+unsigned long cli_ctx_status(struct ptlrpc_cli_ctx *ctx)
+{
+ return (ctx->cc_flags & PTLRPC_CTX_STATUS_MASK);
+}
+
+static inline
+int cli_ctx_is_ready(struct ptlrpc_cli_ctx *ctx)
+{
+ return (cli_ctx_status(ctx) == PTLRPC_CTX_UPTODATE);
+}
+
+static inline
+int cli_ctx_is_refreshed(struct ptlrpc_cli_ctx *ctx)
+{
+ return (cli_ctx_status(ctx) != 0);
+}
+
+static inline
+int cli_ctx_is_uptodate(struct ptlrpc_cli_ctx *ctx)
+{
+ return ((ctx->cc_flags & PTLRPC_CTX_UPTODATE) != 0);
+}
+
+static inline
+int cli_ctx_is_error(struct ptlrpc_cli_ctx *ctx)
+{
+ return ((ctx->cc_flags & PTLRPC_CTX_ERROR) != 0);
+}
+
+static inline
+int cli_ctx_is_dead(struct ptlrpc_cli_ctx *ctx)
+{
+ return ((ctx->cc_flags & (PTLRPC_CTX_DEAD | PTLRPC_CTX_ERROR)) != 0);
+}
+
+static inline
+int cli_ctx_is_eternal(struct ptlrpc_cli_ctx *ctx)
+{
+ return ((ctx->cc_flags & PTLRPC_CTX_ETERNAL) != 0);
+}
+
+/*
+ * sec get/put
+ */
+struct ptlrpc_sec *sptlrpc_sec_get(struct ptlrpc_sec *sec);
+void sptlrpc_sec_put(struct ptlrpc_sec *sec);
+
+/*
+ * internal apis which only used by policy implementation
+ */
+int sptlrpc_get_next_secid(void);
+void sptlrpc_sec_destroy(struct ptlrpc_sec *sec);
+
+/*
+ * exported client context api
+ */
+struct ptlrpc_cli_ctx *sptlrpc_cli_ctx_get(struct ptlrpc_cli_ctx *ctx);
+void sptlrpc_cli_ctx_put(struct ptlrpc_cli_ctx *ctx, int sync);
+void sptlrpc_cli_ctx_expire(struct ptlrpc_cli_ctx *ctx);
+void sptlrpc_cli_ctx_wakeup(struct ptlrpc_cli_ctx *ctx);
+int sptlrpc_cli_ctx_display(struct ptlrpc_cli_ctx *ctx, char *buf, int bufsize);
+
+/*
+ * exported client context wrap/buffers
+ */
+int sptlrpc_cli_wrap_request(struct ptlrpc_request *req);
+int sptlrpc_cli_unwrap_reply(struct ptlrpc_request *req);
+int sptlrpc_cli_alloc_reqbuf(struct ptlrpc_request *req, int msgsize);
+void sptlrpc_cli_free_reqbuf(struct ptlrpc_request *req);
+int sptlrpc_cli_alloc_repbuf(struct ptlrpc_request *req, int msgsize);
+void sptlrpc_cli_free_repbuf(struct ptlrpc_request *req);
+int sptlrpc_cli_enlarge_reqbuf(struct ptlrpc_request *req,
+ int segment, int newsize);
+int sptlrpc_cli_unwrap_early_reply(struct ptlrpc_request *req,
+ struct ptlrpc_request **req_ret);
+void sptlrpc_cli_finish_early_reply(struct ptlrpc_request *early_req);
+
+void sptlrpc_request_out_callback(struct ptlrpc_request *req);
+
+/*
+ * exported higher interface of import & request
+ */
+int sptlrpc_import_sec_adapt(struct obd_import *imp,
+ struct ptlrpc_svc_ctx *ctx,
+ struct sptlrpc_flavor *flvr);
+struct ptlrpc_sec *sptlrpc_import_sec_ref(struct obd_import *imp);
+void sptlrpc_import_sec_put(struct obd_import *imp);
+
+int sptlrpc_import_check_ctx(struct obd_import *imp);
+void sptlrpc_import_flush_root_ctx(struct obd_import *imp);
+void sptlrpc_import_flush_my_ctx(struct obd_import *imp);
+void sptlrpc_import_flush_all_ctx(struct obd_import *imp);
+int sptlrpc_req_get_ctx(struct ptlrpc_request *req);
+void sptlrpc_req_put_ctx(struct ptlrpc_request *req, int sync);
+int sptlrpc_req_refresh_ctx(struct ptlrpc_request *req, long timeout);
+int sptlrpc_req_replace_dead_ctx(struct ptlrpc_request *req);
+void sptlrpc_req_set_flavor(struct ptlrpc_request *req, int opcode);
+
+int sptlrpc_parse_rule(char *param, struct sptlrpc_rule *rule);
+
+/* gc */
+void sptlrpc_gc_add_sec(struct ptlrpc_sec *sec);
+void sptlrpc_gc_del_sec(struct ptlrpc_sec *sec);
+void sptlrpc_gc_add_ctx(struct ptlrpc_cli_ctx *ctx);
+
+/* misc */
+const char *sec2target_str(struct ptlrpc_sec *sec);
+/*
+ * lprocfs
+ */
+#if defined (CONFIG_PROC_FS)
+struct proc_dir_entry;
+extern struct proc_dir_entry *sptlrpc_proc_root;
+int sptlrpc_lprocfs_cliobd_attach(struct obd_device *dev);
+#else
+#define sptlrpc_proc_root NULL
+static inline int sptlrpc_lprocfs_cliobd_attach(struct obd_device *dev)
+{ return 0; }
+#endif
+
+/*
+ * server side
+ */
+enum secsvc_accept_res {
+ SECSVC_OK = 0,
+ SECSVC_COMPLETE,
+ SECSVC_DROP,
+};
+
+int sptlrpc_svc_unwrap_request(struct ptlrpc_request *req);
+int sptlrpc_svc_alloc_rs(struct ptlrpc_request *req, int msglen);
+int sptlrpc_svc_wrap_reply(struct ptlrpc_request *req);
+void sptlrpc_svc_free_rs(struct ptlrpc_reply_state *rs);
+void sptlrpc_svc_ctx_addref(struct ptlrpc_request *req);
+void sptlrpc_svc_ctx_decref(struct ptlrpc_request *req);
+void sptlrpc_svc_ctx_invalidate(struct ptlrpc_request *req);
+
+int sptlrpc_target_export_check(struct obd_export *exp,
+ struct ptlrpc_request *req);
+void sptlrpc_target_update_exp_flavor(struct obd_device *obd,
+ struct sptlrpc_rule_set *rset);
+
+/*
+ * reverse context
+ */
+int sptlrpc_svc_install_rvs_ctx(struct obd_import *imp,
+ struct ptlrpc_svc_ctx *ctx);
+int sptlrpc_cli_install_rvs_ctx(struct obd_import *imp,
+ struct ptlrpc_cli_ctx *ctx);
+
+/* bulk security api */
+int sptlrpc_enc_pool_add_user(void);
+int sptlrpc_enc_pool_del_user(void);
+int sptlrpc_enc_pool_get_pages(struct ptlrpc_bulk_desc *desc);
+void sptlrpc_enc_pool_put_pages(struct ptlrpc_bulk_desc *desc);
+
+int sptlrpc_cli_wrap_bulk(struct ptlrpc_request *req,
+ struct ptlrpc_bulk_desc *desc);
+int sptlrpc_cli_unwrap_bulk_read(struct ptlrpc_request *req,
+ struct ptlrpc_bulk_desc *desc,
+ int nob);
+int sptlrpc_cli_unwrap_bulk_write(struct ptlrpc_request *req,
+ struct ptlrpc_bulk_desc *desc);
+
+/* bulk helpers (internal use only by policies) */
+int sptlrpc_get_bulk_checksum(struct ptlrpc_bulk_desc *desc, __u8 alg,
+ void *buf, int buflen);
+
+int bulk_sec_desc_unpack(struct lustre_msg *msg, int offset, int swabbed);
+
+/* user descriptor helpers */
+static inline int sptlrpc_user_desc_size(int ngroups)
+{
+ return sizeof(struct ptlrpc_user_desc) + ngroups * sizeof(__u32);
+}
+
+int sptlrpc_current_user_desc_size(void);
+int sptlrpc_pack_user_desc(struct lustre_msg *msg, int offset);
+int sptlrpc_unpack_user_desc(struct lustre_msg *req, int offset, int swabbed);
+
+
+#define CFS_CAP_CHOWN_MASK (1 << CFS_CAP_CHOWN)
+#define CFS_CAP_SYS_RESOURCE_MASK (1 << CFS_CAP_SYS_RESOURCE)
+
+enum {
+ LUSTRE_SEC_NONE = 0,
+ LUSTRE_SEC_REMOTE = 1,
+ LUSTRE_SEC_SPECIFY = 2,
+ LUSTRE_SEC_ALL = 3
+};
+
+/** @} sptlrpc */
+
+#endif /* _LUSTRE_SEC_H_ */
diff --git a/drivers/staging/lustre/lustre/include/lustre_ver.h b/drivers/staging/lustre/lustre/include/lustre_ver.h
new file mode 100644
index 000000000..caa4da12f
--- /dev/null
+++ b/drivers/staging/lustre/lustre/include/lustre_ver.h
@@ -0,0 +1,26 @@
+#ifndef _LUSTRE_VER_H_
+#define _LUSTRE_VER_H_
+/* This file automatically generated from lustre/include/lustre_ver.h.in,
+ * based on parameters in lustre/autoconf/lustre-version.ac.
+ * Changes made directly to this file will be lost. */
+
+#define LUSTRE_MAJOR 2
+#define LUSTRE_MINOR 3
+#define LUSTRE_PATCH 64
+#define LUSTRE_FIX 0
+#define LUSTRE_VERSION_STRING "2.3.64"
+
+#define LUSTRE_VERSION_CODE OBD_OCD_VERSION(LUSTRE_MAJOR, \
+ LUSTRE_MINOR, LUSTRE_PATCH, \
+ LUSTRE_FIX)
+
+/* liblustre clients are only allowed to connect if their LUSTRE_FIX mismatches
+ * by this amount (set in lustre/autoconf/lustre-version.ac). */
+#define LUSTRE_VERSION_ALLOWED_OFFSET OBD_OCD_VERSION(0, 0, 1, 32)
+
+/* If lustre version of client and servers it connects to differs by more
+ * than this amount, client would issue a warning.
+ * (set in lustre/autoconf/lustre-version.ac) */
+#define LUSTRE_VERSION_OFFSET_WARN OBD_OCD_VERSION(0, 4, 0, 0)
+
+#endif
diff --git a/drivers/staging/lustre/lustre/include/obd.h b/drivers/staging/lustre/lustre/include/obd.h
new file mode 100644
index 000000000..2a88b806f
--- /dev/null
+++ b/drivers/staging/lustre/lustre/include/obd.h
@@ -0,0 +1,1496 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2011, 2012, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ */
+
+#ifndef __OBD_H
+#define __OBD_H
+
+#include "linux/obd.h"
+
+#define IOC_OSC_TYPE 'h'
+#define IOC_OSC_MIN_NR 20
+#define IOC_OSC_SET_ACTIVE _IOWR(IOC_OSC_TYPE, 21, struct obd_device *)
+#define IOC_OSC_MAX_NR 50
+
+#define IOC_MDC_TYPE 'i'
+#define IOC_MDC_MIN_NR 20
+#define IOC_MDC_MAX_NR 50
+
+#include "lustre/lustre_idl.h"
+#include "lustre_lib.h"
+#include "lu_ref.h"
+#include "lustre_export.h"
+#include "lustre_fid.h"
+#include "lustre_fld.h"
+#include "lustre_capa.h"
+
+#define MAX_OBD_DEVICES 8192
+
+struct osc_async_rc {
+ int ar_rc;
+ int ar_force_sync;
+ __u64 ar_min_xid;
+};
+
+struct lov_oinfo { /* per-stripe data structure */
+ struct ost_id loi_oi; /* object ID/Sequence on the target OST */
+ int loi_ost_idx; /* OST stripe index in lov_tgt_desc->tgts */
+ int loi_ost_gen; /* generation of this loi_ost_idx */
+
+ unsigned long loi_kms_valid:1;
+ __u64 loi_kms; /* known minimum size */
+ struct ost_lvb loi_lvb;
+ struct osc_async_rc loi_ar;
+};
+
+static inline void loi_kms_set(struct lov_oinfo *oinfo, __u64 kms)
+{
+ oinfo->loi_kms = kms;
+ oinfo->loi_kms_valid = 1;
+}
+
+static inline void loi_init(struct lov_oinfo *loi)
+{
+}
+
+struct lov_stripe_md {
+ atomic_t lsm_refc;
+ spinlock_t lsm_lock;
+ pid_t lsm_lock_owner; /* debugging */
+
+ /* maximum possible file size, might change as OSTs status changes,
+ * e.g. disconnected, deactivated */
+ __u64 lsm_maxbytes;
+ struct {
+ /* Public members. */
+ struct ost_id lw_object_oi; /* lov object id/seq */
+
+ /* LOV-private members start here -- only for use in lov/. */
+ __u32 lw_magic;
+ __u32 lw_stripe_size; /* size of the stripe */
+ __u32 lw_pattern; /* striping pattern (RAID0, RAID1) */
+ __u16 lw_stripe_count; /* number of objects being striped over */
+ __u16 lw_layout_gen; /* generation of the layout */
+ char lw_pool_name[LOV_MAXPOOLNAME]; /* pool name */
+ } lsm_wire;
+
+ struct lov_oinfo *lsm_oinfo[0];
+};
+
+#define lsm_oi lsm_wire.lw_object_oi
+#define lsm_magic lsm_wire.lw_magic
+#define lsm_layout_gen lsm_wire.lw_layout_gen
+#define lsm_stripe_size lsm_wire.lw_stripe_size
+#define lsm_pattern lsm_wire.lw_pattern
+#define lsm_stripe_count lsm_wire.lw_stripe_count
+#define lsm_pool_name lsm_wire.lw_pool_name
+
+static inline bool lsm_is_released(struct lov_stripe_md *lsm)
+{
+ return !!(lsm->lsm_pattern & LOV_PATTERN_F_RELEASED);
+}
+
+static inline bool lsm_has_objects(struct lov_stripe_md *lsm)
+{
+ if (lsm == NULL)
+ return false;
+ if (lsm_is_released(lsm))
+ return false;
+ return true;
+}
+
+static inline int lov_stripe_md_size(unsigned int stripe_count)
+{
+ struct lov_stripe_md lsm;
+
+ return sizeof(lsm) + stripe_count * sizeof(lsm.lsm_oinfo[0]);
+}
+
+struct obd_info;
+
+typedef int (*obd_enqueue_update_f)(void *cookie, int rc);
+
+/* obd info for a particular level (lov, osc). */
+struct obd_info {
+ /* Lock policy. It keeps an extent which is specific for a particular
+ * OSC. (e.g. lov_prep_enqueue_set initialises extent of the policy,
+ * and osc_enqueue passes it into ldlm_lock_match & ldlm_cli_enqueue. */
+ ldlm_policy_data_t oi_policy;
+ /* Flags used for set request specific flags:
+ - while lock handling, the flags obtained on the enqueue
+ request are set here.
+ - while stats, the flags used for control delay/resend.
+ - while setattr, the flags used for distinguish punch operation
+ */
+ __u64 oi_flags;
+ /* Lock handle specific for every OSC lock. */
+ struct lustre_handle *oi_lockh;
+ /* lsm data specific for every OSC. */
+ struct lov_stripe_md *oi_md;
+ /* obdo data specific for every OSC, if needed at all. */
+ struct obdo *oi_oa;
+ /* statfs data specific for every OSC, if needed at all. */
+ struct obd_statfs *oi_osfs;
+ /* An update callback which is called to update some data on upper
+ * level. E.g. it is used for update lsm->lsm_oinfo at every received
+ * request in osc level for enqueue requests. It is also possible to
+ * update some caller data from LOV layer if needed. */
+ obd_enqueue_update_f oi_cb_up;
+ /* oss capability, its type is obd_capa in client to avoid copy.
+ * in contrary its type is lustre_capa in OSS. */
+ void *oi_capa;
+ /* transfer jobid from ost_sync() to filter_sync()... */
+ char *oi_jobid;
+};
+
+/* compare all relevant fields. */
+static inline int lov_stripe_md_cmp(struct lov_stripe_md *m1,
+ struct lov_stripe_md *m2)
+{
+ /*
+ * ->lsm_wire contains padding, but it should be zeroed out during
+ * allocation.
+ */
+ return memcmp(&m1->lsm_wire, &m2->lsm_wire, sizeof(m1->lsm_wire));
+}
+
+static inline int lov_lum_lsm_cmp(struct lov_user_md *lum,
+ struct lov_stripe_md *lsm)
+{
+ if (lsm->lsm_magic != lum->lmm_magic)
+ return 1;
+ if ((lsm->lsm_stripe_count != 0) && (lum->lmm_stripe_count != 0) &&
+ (lsm->lsm_stripe_count != lum->lmm_stripe_count))
+ return 2;
+ if ((lsm->lsm_stripe_size != 0) && (lum->lmm_stripe_size != 0) &&
+ (lsm->lsm_stripe_size != lum->lmm_stripe_size))
+ return 3;
+ if ((lsm->lsm_pattern != 0) && (lum->lmm_pattern != 0) &&
+ (lsm->lsm_pattern != lum->lmm_pattern))
+ return 4;
+ if ((lsm->lsm_magic == LOV_MAGIC_V3) &&
+ (strncmp(lsm->lsm_pool_name,
+ ((struct lov_user_md_v3 *)lum)->lmm_pool_name,
+ LOV_MAXPOOLNAME) != 0))
+ return 5;
+ return 0;
+}
+
+static inline int lov_lum_swab_if_needed(struct lov_user_md_v3 *lumv3,
+ int *lmm_magic,
+ struct lov_user_md *lum)
+{
+ if (lum && copy_from_user(lumv3, lum, sizeof(struct lov_user_md_v1)))
+ return -EFAULT;
+
+ *lmm_magic = lumv3->lmm_magic;
+
+ if (*lmm_magic == __swab32(LOV_USER_MAGIC_V1)) {
+ lustre_swab_lov_user_md_v1((struct lov_user_md_v1 *)lumv3);
+ *lmm_magic = LOV_USER_MAGIC_V1;
+ } else if (*lmm_magic == LOV_USER_MAGIC_V3) {
+ if (lum && copy_from_user(lumv3, lum, sizeof(*lumv3)))
+ return -EFAULT;
+ } else if (*lmm_magic == __swab32(LOV_USER_MAGIC_V3)) {
+ if (lum && copy_from_user(lumv3, lum, sizeof(*lumv3)))
+ return -EFAULT;
+ lustre_swab_lov_user_md_v3(lumv3);
+ *lmm_magic = LOV_USER_MAGIC_V3;
+ } else if (*lmm_magic != LOV_USER_MAGIC_V1) {
+ CDEBUG(D_IOCTL,
+ "bad userland LOV MAGIC: %#08x != %#08x nor %#08x\n",
+ *lmm_magic, LOV_USER_MAGIC_V1, LOV_USER_MAGIC_V3);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+void lov_stripe_lock(struct lov_stripe_md *md);
+void lov_stripe_unlock(struct lov_stripe_md *md);
+
+struct obd_type {
+ struct list_head typ_chain;
+ struct obd_ops *typ_dt_ops;
+ struct md_ops *typ_md_ops;
+ struct proc_dir_entry *typ_procroot;
+ char *typ_name;
+ int typ_refcnt;
+ struct lu_device_type *typ_lu;
+ spinlock_t obd_type_lock;
+};
+
+struct brw_page {
+ u64 off;
+ struct page *pg;
+ int count;
+ u32 flag;
+};
+
+/* llog contexts */
+enum llog_ctxt_id {
+ LLOG_CONFIG_ORIG_CTXT = 0,
+ LLOG_CONFIG_REPL_CTXT,
+ LLOG_MDS_OST_ORIG_CTXT,
+ LLOG_MDS_OST_REPL_CTXT,
+ LLOG_SIZE_ORIG_CTXT,
+ LLOG_SIZE_REPL_CTXT,
+ LLOG_RD1_ORIG_CTXT,
+ LLOG_RD1_REPL_CTXT,
+ LLOG_TEST_ORIG_CTXT,
+ LLOG_TEST_REPL_CTXT,
+ LLOG_LOVEA_ORIG_CTXT,
+ LLOG_LOVEA_REPL_CTXT,
+ LLOG_CHANGELOG_ORIG_CTXT, /**< changelog generation on mdd */
+ LLOG_CHANGELOG_REPL_CTXT, /**< changelog access on clients */
+ LLOG_CHANGELOG_USER_ORIG_CTXT, /**< for multiple changelog consumers */
+ LLOG_AGENT_ORIG_CTXT, /**< agent requests generation on cdt */
+ LLOG_MAX_CTXTS
+};
+
+struct timeout_item {
+ enum timeout_event ti_event;
+ unsigned long ti_timeout;
+ timeout_cb_t ti_cb;
+ void *ti_cb_data;
+ struct list_head ti_obd_list;
+ struct list_head ti_chain;
+};
+
+#define OSC_MAX_RIF_DEFAULT 8
+#define MDS_OSC_MAX_RIF_DEFAULT 50
+#define OSC_MAX_RIF_MAX 256
+#define OSC_MAX_DIRTY_DEFAULT (OSC_MAX_RIF_DEFAULT * 4)
+#define OSC_MAX_DIRTY_MB_MAX 2048 /* arbitrary, but < MAX_LONG bytes */
+#define OSC_DEFAULT_RESENDS 10
+
+/* possible values for fo_sync_lock_cancel */
+enum {
+ NEVER_SYNC_ON_CANCEL = 0,
+ BLOCKING_SYNC_ON_CANCEL = 1,
+ ALWAYS_SYNC_ON_CANCEL = 2,
+ NUM_SYNC_ON_CANCEL_STATES
+};
+
+#define MDC_MAX_RIF_DEFAULT 8
+#define MDC_MAX_RIF_MAX 512
+
+struct mdc_rpc_lock;
+struct obd_import;
+struct client_obd {
+ struct rw_semaphore cl_sem;
+ struct obd_uuid cl_target_uuid;
+ struct obd_import *cl_import; /* ptlrpc connection state */
+ int cl_conn_count;
+ /* max_mds_easize is purely a performance thing so we don't have to
+ * call obd_size_diskmd() all the time. */
+ int cl_default_mds_easize;
+ int cl_max_mds_easize;
+ int cl_default_mds_cookiesize;
+ int cl_max_mds_cookiesize;
+
+ enum lustre_sec_part cl_sp_me;
+ enum lustre_sec_part cl_sp_to;
+ struct sptlrpc_flavor cl_flvr_mgc; /* fixed flavor of mgc->mgs */
+
+ /* the grant values are protected by loi_list_lock below */
+ long cl_dirty; /* all _dirty_ in bytes */
+ long cl_dirty_max; /* allowed w/o rpc */
+ long cl_dirty_transit; /* dirty synchronous */
+ long cl_avail_grant; /* bytes of credit for ost */
+ long cl_lost_grant; /* lost credits (trunc) */
+
+ /* since we allocate grant by blocks, we don't know how many grant will
+ * be used to add a page into cache. As a solution, we reserve maximum
+ * grant before trying to dirty a page and unreserve the rest.
+ * See osc_{reserve|unreserve}_grant for details. */
+ long cl_reserved_grant;
+ struct list_head cl_cache_waiters; /* waiting for cache/grant */
+ unsigned long cl_next_shrink_grant; /* jiffies */
+ struct list_head cl_grant_shrink_list; /* Timeout event list */
+ int cl_grant_shrink_interval; /* seconds */
+
+ /* A chunk is an optimal size used by osc_extent to determine
+ * the extent size. A chunk is max(PAGE_CACHE_SIZE, OST block size) */
+ int cl_chunkbits;
+ int cl_chunk;
+ int cl_extent_tax; /* extent overhead, by bytes */
+
+ /* keep track of objects that have lois that contain pages which
+ * have been queued for async brw. this lock also protects the
+ * lists of osc_client_pages that hang off of the loi */
+ /*
+ * ->cl_loi_list_lock protects consistency of
+ * ->cl_loi_{ready,read,write}_list. ->ap_make_ready() and
+ * ->ap_completion() call-backs are executed under this lock. As we
+ * cannot guarantee that these call-backs never block on all platforms
+ * (as a matter of fact they do block on Mac OS X), type of
+ * ->cl_loi_list_lock is platform dependent: it's a spin-lock on Linux
+ * and blocking mutex on Mac OS X. (Alternative is to make this lock
+ * blocking everywhere, but we don't want to slow down fast-path of
+ * our main platform.)
+ *
+ * Exact type of ->cl_loi_list_lock is defined in arch/obd.h together
+ * with client_obd_list_{un,}lock() and
+ * client_obd_list_lock_{init,done}() functions.
+ *
+ * NB by Jinshan: though field names are still _loi_, but actually
+ * osc_object{}s are in the list.
+ */
+ client_obd_lock_t cl_loi_list_lock;
+ struct list_head cl_loi_ready_list;
+ struct list_head cl_loi_hp_ready_list;
+ struct list_head cl_loi_write_list;
+ struct list_head cl_loi_read_list;
+ int cl_r_in_flight;
+ int cl_w_in_flight;
+ /* just a sum of the loi/lop pending numbers to be exported by /proc */
+ atomic_t cl_pending_w_pages;
+ atomic_t cl_pending_r_pages;
+ __u32 cl_max_pages_per_rpc;
+ int cl_max_rpcs_in_flight;
+ struct obd_histogram cl_read_rpc_hist;
+ struct obd_histogram cl_write_rpc_hist;
+ struct obd_histogram cl_read_page_hist;
+ struct obd_histogram cl_write_page_hist;
+ struct obd_histogram cl_read_offset_hist;
+ struct obd_histogram cl_write_offset_hist;
+
+ /* lru for osc caching pages */
+ struct cl_client_cache *cl_cache;
+ struct list_head cl_lru_osc; /* member of cl_cache->ccc_lru */
+ atomic_t *cl_lru_left;
+ atomic_t cl_lru_busy;
+ atomic_t cl_lru_shrinkers;
+ atomic_t cl_lru_in_list;
+ struct list_head cl_lru_list; /* lru page list */
+ client_obd_lock_t cl_lru_list_lock; /* page list protector */
+
+ /* number of in flight destroy rpcs is limited to max_rpcs_in_flight */
+ atomic_t cl_destroy_in_flight;
+ wait_queue_head_t cl_destroy_waitq;
+
+ struct mdc_rpc_lock *cl_rpc_lock;
+ struct mdc_rpc_lock *cl_close_lock;
+
+ /* mgc datastruct */
+ struct mutex cl_mgc_mutex;
+ struct local_oid_storage *cl_mgc_los;
+ struct dt_object *cl_mgc_configs_dir;
+ atomic_t cl_mgc_refcount;
+ struct obd_export *cl_mgc_mgsexp;
+
+ /* checksumming for data sent over the network */
+ unsigned int cl_checksum:1; /* 0 = disabled, 1 = enabled */
+ /* supported checksum types that are worked out at connect time */
+ __u32 cl_supp_cksum_types;
+ /* checksum algorithm to be used */
+ cksum_type_t cl_cksum_type;
+
+ /* also protected by the poorly named _loi_list_lock lock above */
+ struct osc_async_rc cl_ar;
+
+ /* used by quotacheck when the servers are older than 2.4 */
+ int cl_qchk_stat; /* quotacheck stat of the peer */
+#define CL_NOT_QUOTACHECKED 1 /* client->cl_qchk_stat init value */
+#if LUSTRE_VERSION_CODE >= OBD_OCD_VERSION(2, 7, 50, 0)
+#warning "please consider removing quotacheck compatibility code"
+#endif
+
+ /* sequence manager */
+ struct lu_client_seq *cl_seq;
+
+ atomic_t cl_resends; /* resend count */
+
+ /* ptlrpc work for writeback in ptlrpcd context */
+ void *cl_writeback_work;
+ /* hash tables for osc_quota_info */
+ struct cfs_hash *cl_quota_hash[MAXQUOTAS];
+};
+#define obd2cli_tgt(obd) ((char *)(obd)->u.cli.cl_target_uuid.uuid)
+
+struct obd_id_info {
+ __u32 idx;
+ u64 *data;
+};
+
+struct echo_client_obd {
+ struct obd_export *ec_exp; /* the local connection to osc/lov */
+ spinlock_t ec_lock;
+ struct list_head ec_objects;
+ struct list_head ec_locks;
+ int ec_nstripes;
+ __u64 ec_unique;
+};
+
+struct lov_qos_oss {
+ struct obd_uuid lqo_uuid; /* ptlrpc's c_remote_uuid */
+ struct list_head lqo_oss_list; /* link to lov_qos */
+ __u64 lqo_bavail; /* total bytes avail on OSS */
+ __u64 lqo_penalty; /* current penalty */
+ __u64 lqo_penalty_per_obj;/* penalty decrease every obj*/
+ time_t lqo_used; /* last used time, seconds */
+ __u32 lqo_ost_count; /* number of osts on this oss */
+};
+
+struct ltd_qos {
+ struct lov_qos_oss *ltq_oss; /* oss info */
+ __u64 ltq_penalty; /* current penalty */
+ __u64 ltq_penalty_per_obj; /* penalty decrease every obj*/
+ __u64 ltq_weight; /* net weighting */
+ time_t ltq_used; /* last used time, seconds */
+ unsigned int ltq_usable:1; /* usable for striping */
+};
+
+/* Generic subset of OSTs */
+struct ost_pool {
+ __u32 *op_array; /* array of index of
+ lov_obd->lov_tgts */
+ unsigned int op_count; /* number of OSTs in the array */
+ unsigned int op_size; /* allocated size of lp_array */
+ struct rw_semaphore op_rw_sem; /* to protect ost_pool use */
+};
+
+/* Round-robin allocator data */
+struct lov_qos_rr {
+ __u32 lqr_start_idx; /* start index of new inode */
+ __u32 lqr_offset_idx; /* aliasing for start_idx */
+ int lqr_start_count; /* reseed counter */
+ struct ost_pool lqr_pool; /* round-robin optimized list */
+ unsigned long lqr_dirty:1; /* recalc round-robin list */
+};
+
+/* allow statfs data caching for 1 second */
+#define OBD_STATFS_CACHE_SECONDS 1
+
+struct lov_statfs_data {
+ struct obd_info lsd_oi;
+ struct obd_statfs lsd_statfs;
+};
+/* Stripe placement optimization */
+struct lov_qos {
+ struct list_head lq_oss_list; /* list of OSSs that targets use */
+ struct rw_semaphore lq_rw_sem;
+ __u32 lq_active_oss_count;
+ unsigned int lq_prio_free; /* priority for free space */
+ unsigned int lq_threshold_rr;/* priority for rr */
+ struct lov_qos_rr lq_rr; /* round robin qos data */
+ unsigned long lq_dirty:1, /* recalc qos data */
+ lq_same_space:1,/* the ost's all have approx.
+ the same space avail */
+ lq_reset:1, /* zero current penalties */
+ lq_statfs_in_progress:1; /* statfs op in
+ progress */
+ /* qos statfs data */
+ struct lov_statfs_data *lq_statfs_data;
+ wait_queue_head_t lq_statfs_waitq; /* waitqueue to notify statfs
+ * requests completion */
+};
+
+struct lov_tgt_desc {
+ struct list_head ltd_kill;
+ struct obd_uuid ltd_uuid;
+ struct obd_device *ltd_obd;
+ struct obd_export *ltd_exp;
+ struct ltd_qos ltd_qos; /* qos info per target */
+ __u32 ltd_gen;
+ __u32 ltd_index; /* index in lov_obd->tgts */
+ unsigned long ltd_active:1,/* is this target up for requests */
+ ltd_activate:1,/* should target be activated */
+ ltd_reap:1; /* should this target be deleted */
+};
+
+/* Pool metadata */
+#define pool_tgt_size(_p) _p->pool_obds.op_size
+#define pool_tgt_count(_p) _p->pool_obds.op_count
+#define pool_tgt_array(_p) _p->pool_obds.op_array
+#define pool_tgt_rw_sem(_p) _p->pool_obds.op_rw_sem
+
+struct pool_desc {
+ char pool_name[LOV_MAXPOOLNAME + 1]; /* name of pool */
+ struct ost_pool pool_obds; /* pool members */
+ atomic_t pool_refcount; /* pool ref. counter */
+ struct lov_qos_rr pool_rr; /* round robin qos */
+ struct hlist_node pool_hash; /* access by poolname */
+ struct list_head pool_list; /* serial access */
+ struct proc_dir_entry *pool_proc_entry; /* file in /proc */
+ struct obd_device *pool_lobd; /* obd of the lov/lod to which
+ * this pool belongs */
+};
+
+struct lov_obd {
+ struct lov_desc desc;
+ struct lov_tgt_desc **lov_tgts; /* sparse array */
+ struct ost_pool lov_packed; /* all OSTs in a packed
+ array */
+ struct mutex lov_lock;
+ struct obd_connect_data lov_ocd;
+ atomic_t lov_refcount;
+ __u32 lov_tgt_count; /* how many OBD's */
+ __u32 lov_active_tgt_count; /* how many active */
+ __u32 lov_death_row;/* tgts scheduled to be deleted */
+ __u32 lov_tgt_size; /* size of tgts array */
+ int lov_connects;
+ int lov_pool_count;
+ struct cfs_hash *lov_pools_hash_body; /* used for key access */
+ struct list_head lov_pool_list; /* used for sequential access */
+ struct proc_dir_entry *lov_pool_proc_entry;
+ enum lustre_sec_part lov_sp_me;
+
+ /* Cached LRU pages from upper layer */
+ void *lov_cache;
+
+ struct rw_semaphore lov_notify_lock;
+};
+
+struct lmv_tgt_desc {
+ struct obd_uuid ltd_uuid;
+ struct obd_export *ltd_exp;
+ int ltd_idx;
+ struct mutex ltd_fid_mutex;
+ unsigned long ltd_active:1; /* target up for requests */
+};
+
+enum placement_policy {
+ PLACEMENT_CHAR_POLICY = 0,
+ PLACEMENT_NID_POLICY = 1,
+ PLACEMENT_INVAL_POLICY = 2,
+ PLACEMENT_MAX_POLICY
+};
+
+struct lmv_obd {
+ int refcount;
+ struct lu_client_fld lmv_fld;
+ spinlock_t lmv_lock;
+ enum placement_policy lmv_placement;
+ struct lmv_desc desc;
+ struct obd_uuid cluuid;
+ struct obd_export *exp;
+
+ struct mutex init_mutex;
+ int connected;
+ int max_easize;
+ int max_def_easize;
+ int max_cookiesize;
+ int max_def_cookiesize;
+ int server_timeout;
+
+ int tgts_size; /* size of tgts array */
+ struct lmv_tgt_desc **tgts;
+
+ struct obd_connect_data conn_data;
+};
+
+struct niobuf_local {
+ __u64 lnb_file_offset;
+ __u32 lnb_page_offset;
+ __u32 len;
+ __u32 flags;
+ struct page *page;
+ struct dentry *dentry;
+ int lnb_grant_used;
+ int rc;
+};
+
+#define LUSTRE_FLD_NAME "fld"
+#define LUSTRE_SEQ_NAME "seq"
+
+#define LUSTRE_MDD_NAME "mdd"
+#define LUSTRE_OSD_LDISKFS_NAME "osd-ldiskfs"
+#define LUSTRE_OSD_ZFS_NAME "osd-zfs"
+#define LUSTRE_VVP_NAME "vvp"
+#define LUSTRE_LMV_NAME "lmv"
+#define LUSTRE_SLP_NAME "slp"
+#define LUSTRE_LOD_NAME "lod"
+#define LUSTRE_OSP_NAME "osp"
+#define LUSTRE_LWP_NAME "lwp"
+
+/* obd device type names */
+ /* FIXME all the references to LUSTRE_MDS_NAME should be swapped with LUSTRE_MDT_NAME */
+#define LUSTRE_MDS_NAME "mds"
+#define LUSTRE_MDT_NAME "mdt"
+#define LUSTRE_MDC_NAME "mdc"
+#define LUSTRE_OSS_NAME "ost" /* FIXME change name to oss */
+#define LUSTRE_OST_NAME "obdfilter" /* FIXME change name to ost */
+#define LUSTRE_OSC_NAME "osc"
+#define LUSTRE_LOV_NAME "lov"
+#define LUSTRE_MGS_NAME "mgs"
+#define LUSTRE_MGC_NAME "mgc"
+
+#define LUSTRE_ECHO_NAME "obdecho"
+#define LUSTRE_ECHO_CLIENT_NAME "echo_client"
+#define LUSTRE_QMT_NAME "qmt"
+
+/* Constant obd names (post-rename) */
+#define LUSTRE_MDS_OBDNAME "MDS"
+#define LUSTRE_OSS_OBDNAME "OSS"
+#define LUSTRE_MGS_OBDNAME "MGS"
+#define LUSTRE_MGC_OBDNAME "MGC"
+
+/* Don't conflict with on-wire flags OBD_BRW_WRITE, etc */
+#define N_LOCAL_TEMP_PAGE 0x10000000
+
+struct obd_trans_info {
+ __u64 oti_transno;
+ __u64 oti_xid;
+ /* Only used on the server side for tracking acks. */
+ struct oti_req_ack_lock {
+ struct lustre_handle lock;
+ __u32 mode;
+ } oti_ack_locks[4];
+ void *oti_handle;
+ struct llog_cookie oti_onecookie;
+ struct llog_cookie *oti_logcookies;
+ int oti_numcookies;
+ /** synchronous write is needed */
+ unsigned long oti_sync_write:1;
+
+ /* initial thread handling transaction */
+ struct ptlrpc_thread *oti_thread;
+ __u32 oti_conn_cnt;
+ /** VBR: versions */
+ __u64 oti_pre_version;
+ /** JobID */
+ char *oti_jobid;
+
+ struct obd_uuid *oti_ost_uuid;
+};
+
+static inline void oti_init(struct obd_trans_info *oti,
+ struct ptlrpc_request *req)
+{
+ if (oti == NULL)
+ return;
+ memset(oti, 0, sizeof(*oti));
+
+ if (req == NULL)
+ return;
+
+ oti->oti_xid = req->rq_xid;
+ /** VBR: take versions from request */
+ if (req->rq_reqmsg != NULL &&
+ lustre_msg_get_flags(req->rq_reqmsg) & MSG_REPLAY) {
+ __u64 *pre_version = lustre_msg_get_versions(req->rq_reqmsg);
+
+ oti->oti_pre_version = pre_version ? pre_version[0] : 0;
+ oti->oti_transno = lustre_msg_get_transno(req->rq_reqmsg);
+ }
+
+ /** called from mds_create_objects */
+ if (req->rq_repmsg != NULL)
+ oti->oti_transno = lustre_msg_get_transno(req->rq_repmsg);
+ oti->oti_thread = req->rq_svc_thread;
+ if (req->rq_reqmsg != NULL)
+ oti->oti_conn_cnt = lustre_msg_get_conn_cnt(req->rq_reqmsg);
+}
+
+static inline void oti_alloc_cookies(struct obd_trans_info *oti,
+ int num_cookies)
+{
+ if (!oti)
+ return;
+
+ if (num_cookies == 1)
+ oti->oti_logcookies = &oti->oti_onecookie;
+ else
+ OBD_ALLOC_LARGE(oti->oti_logcookies,
+ num_cookies * sizeof(oti->oti_onecookie));
+
+ oti->oti_numcookies = num_cookies;
+}
+
+static inline void oti_free_cookies(struct obd_trans_info *oti)
+{
+ if (!oti || !oti->oti_logcookies)
+ return;
+
+ if (oti->oti_logcookies == &oti->oti_onecookie)
+ LASSERT(oti->oti_numcookies == 1);
+ else
+ OBD_FREE_LARGE(oti->oti_logcookies,
+ oti->oti_numcookies*sizeof(oti->oti_onecookie));
+ oti->oti_logcookies = NULL;
+ oti->oti_numcookies = 0;
+}
+
+/*
+ * Events signalled through obd_notify() upcall-chain.
+ */
+enum obd_notify_event {
+ /* target added */
+ OBD_NOTIFY_CREATE,
+ /* Device connect start */
+ OBD_NOTIFY_CONNECT,
+ /* Device activated */
+ OBD_NOTIFY_ACTIVE,
+ /* Device deactivated */
+ OBD_NOTIFY_INACTIVE,
+ /* Device disconnected */
+ OBD_NOTIFY_DISCON,
+ /* Connect data for import were changed */
+ OBD_NOTIFY_OCD,
+ /* Sync request */
+ OBD_NOTIFY_SYNC_NONBLOCK,
+ OBD_NOTIFY_SYNC,
+ /* Configuration event */
+ OBD_NOTIFY_CONFIG,
+ /* Administratively deactivate/activate event */
+ OBD_NOTIFY_DEACTIVATE,
+ OBD_NOTIFY_ACTIVATE
+};
+
+/*
+ * Data structure used to pass obd_notify()-event to non-obd listeners (llite
+ * and liblustre being main examples).
+ */
+struct obd_notify_upcall {
+ int (*onu_upcall)(struct obd_device *host, struct obd_device *watched,
+ enum obd_notify_event ev, void *owner, void *data);
+ /* Opaque datum supplied by upper layer listener */
+ void *onu_owner;
+};
+
+struct target_recovery_data {
+ svc_handler_t trd_recovery_handler;
+ pid_t trd_processing_task;
+ struct completion trd_starting;
+ struct completion trd_finishing;
+};
+
+struct obd_llog_group {
+ int olg_seq;
+ struct llog_ctxt *olg_ctxts[LLOG_MAX_CTXTS];
+ wait_queue_head_t olg_waitq;
+ spinlock_t olg_lock;
+ struct mutex olg_cat_processing;
+};
+
+/* corresponds to one of the obd's */
+#define OBD_DEVICE_MAGIC 0XAB5CD6EF
+#define OBD_DEV_BY_DEVNAME 0xffffd0de
+
+struct lvfs_run_ctxt {
+ struct dt_device *dt;
+};
+
+struct obd_device {
+ struct obd_type *obd_type;
+ __u32 obd_magic;
+
+ /* common and UUID name of this device */
+ char obd_name[MAX_OBD_NAME];
+ struct obd_uuid obd_uuid;
+
+ struct lu_device *obd_lu_dev;
+
+ int obd_minor;
+ /* bitfield modification is protected by obd_dev_lock */
+ unsigned long obd_attached:1, /* finished attach */
+ obd_set_up:1, /* finished setup */
+ obd_recovering:1, /* there are recoverable clients */
+ obd_abort_recovery:1,/* recovery expired */
+ obd_version_recov:1, /* obd uses version checking */
+ obd_replayable:1, /* recovery is enabled; inform clients */
+ obd_no_transno:1, /* no committed-transno notification */
+ obd_no_recov:1, /* fail instead of retry messages */
+ obd_stopping:1, /* started cleanup */
+ obd_starting:1, /* started setup */
+ obd_force:1, /* cleanup with > 0 obd refcount */
+ obd_fail:1, /* cleanup with failover */
+ obd_async_recov:1, /* allow asynchronous orphan cleanup */
+ obd_no_conn:1, /* deny new connections */
+ obd_inactive:1, /* device active/inactive
+ * (for /proc/status only!!) */
+ obd_no_ir:1, /* no imperative recovery. */
+ obd_process_conf:1; /* device is processing mgs config */
+ /* use separate field as it is set in interrupt to don't mess with
+ * protection of other bits using _bh lock */
+ unsigned long obd_recovery_expired:1;
+ /* uuid-export hash body */
+ struct cfs_hash *obd_uuid_hash;
+ /* nid-export hash body */
+ struct cfs_hash *obd_nid_hash;
+ /* nid stats body */
+ struct cfs_hash *obd_nid_stats_hash;
+ struct list_head obd_nid_stats;
+ atomic_t obd_refcount;
+ wait_queue_head_t obd_refcount_waitq;
+ struct list_head obd_exports;
+ struct list_head obd_unlinked_exports;
+ struct list_head obd_delayed_exports;
+ int obd_num_exports;
+ spinlock_t obd_nid_lock;
+ struct ldlm_namespace *obd_namespace;
+ struct ptlrpc_client obd_ldlm_client; /* XXX OST/MDS only */
+ /* a spinlock is OK for what we do now, may need a semaphore later */
+ spinlock_t obd_dev_lock; /* protect OBD bitfield above */
+ struct mutex obd_dev_mutex;
+ __u64 obd_last_committed;
+ spinlock_t obd_osfs_lock;
+ struct obd_statfs obd_osfs; /* locked by obd_osfs_lock */
+ __u64 obd_osfs_age;
+ struct lvfs_run_ctxt obd_lvfs_ctxt;
+ struct obd_llog_group obd_olg; /* default llog group */
+ struct obd_device *obd_observer;
+ struct rw_semaphore obd_observer_link_sem;
+ struct obd_notify_upcall obd_upcall;
+ struct obd_export *obd_self_export;
+ /* list of exports in LRU order, for ping evictor, with obd_dev_lock */
+ struct list_head obd_exports_timed;
+ time_t obd_eviction_timer; /* for ping evictor */
+
+ int obd_max_recoverable_clients;
+ atomic_t obd_connected_clients;
+ int obd_stale_clients;
+ int obd_delayed_clients;
+ /* this lock protects all recovery list_heads, timer and
+ * obd_next_recovery_transno value */
+ spinlock_t obd_recovery_task_lock;
+ __u64 obd_next_recovery_transno;
+ int obd_replayed_requests;
+ int obd_requests_queued_for_recovery;
+ wait_queue_head_t obd_next_transno_waitq;
+ /* protected by obd_recovery_task_lock */
+ struct timer_list obd_recovery_timer;
+ time_t obd_recovery_start; /* seconds */
+ time_t obd_recovery_end; /* seconds, for lprocfs_status */
+ int obd_recovery_time_hard;
+ int obd_recovery_timeout;
+ int obd_recovery_ir_factor;
+
+ /* new recovery stuff from CMD2 */
+ struct target_recovery_data obd_recovery_data;
+ int obd_replayed_locks;
+ atomic_t obd_req_replay_clients;
+ atomic_t obd_lock_replay_clients;
+ /* all lists are protected by obd_recovery_task_lock */
+ struct list_head obd_req_replay_queue;
+ struct list_head obd_lock_replay_queue;
+ struct list_head obd_final_req_queue;
+ int obd_recovery_stage;
+
+ union {
+ struct client_obd cli;
+ struct echo_client_obd echo_client;
+ struct lov_obd lov;
+ struct lmv_obd lmv;
+ } u;
+ /* Fields used by LProcFS */
+ unsigned int obd_cntr_base;
+ struct lprocfs_stats *obd_stats;
+
+ unsigned int md_cntr_base;
+ struct lprocfs_stats *md_stats;
+
+ struct proc_dir_entry *obd_proc_entry;
+ void *obd_proc_private; /* type private PDEs */
+ struct proc_dir_entry *obd_proc_exports_entry;
+ struct proc_dir_entry *obd_svc_procroot;
+ struct lprocfs_stats *obd_svc_stats;
+ atomic_t obd_evict_inprogress;
+ wait_queue_head_t obd_evict_inprogress_waitq;
+ struct list_head obd_evict_list; /* protected with pet_lock */
+
+ /**
+ * Ldlm pool part. Save last calculated SLV and Limit.
+ */
+ rwlock_t obd_pool_lock;
+ int obd_pool_limit;
+ __u64 obd_pool_slv;
+
+ /**
+ * A list of outstanding class_incref()'s against this obd. For
+ * debugging.
+ */
+ struct lu_ref obd_reference;
+
+ int obd_conn_inprogress;
+};
+
+#define OBD_LLOG_FL_SENDNOW 0x0001
+#define OBD_LLOG_FL_EXIT 0x0002
+
+enum obd_cleanup_stage {
+/* Special case hack for MDS LOVs */
+ OBD_CLEANUP_EARLY,
+/* can be directly mapped to .ldto_device_fini() */
+ OBD_CLEANUP_EXPORTS,
+};
+
+/* get/set_info keys */
+#define KEY_ASYNC "async"
+#define KEY_BLOCKSIZE_BITS "blocksize_bits"
+#define KEY_BLOCKSIZE "blocksize"
+#define KEY_CAPA_KEY "capa_key"
+#define KEY_CHANGELOG_CLEAR "changelog_clear"
+#define KEY_FID2PATH "fid2path"
+#define KEY_CHECKSUM "checksum"
+#define KEY_CLEAR_FS "clear_fs"
+#define KEY_CONN_DATA "conn_data"
+#define KEY_EVICT_BY_NID "evict_by_nid"
+#define KEY_FIEMAP "fiemap"
+#define KEY_FLUSH_CTX "flush_ctx"
+#define KEY_GRANT_SHRINK "grant_shrink"
+#define KEY_HSM_COPYTOOL_SEND "hsm_send"
+#define KEY_INIT_RECOV_BACKUP "init_recov_bk"
+#define KEY_INIT_RECOV "initial_recov"
+#define KEY_INTERMDS "inter_mds"
+#define KEY_LAST_ID "last_id"
+#define KEY_LAST_FID "last_fid"
+#define KEY_LOCK_TO_STRIPE "lock_to_stripe"
+#define KEY_LOVDESC "lovdesc"
+#define KEY_LOV_IDX "lov_idx"
+#define KEY_MAX_EASIZE "max_easize"
+#define KEY_DEFAULT_EASIZE "default_easize"
+#define KEY_MAX_COOKIESIZE "max_cookiesize"
+#define KEY_DEFAULT_COOKIESIZE "default_cookiesize"
+#define KEY_MDS_CONN "mds_conn"
+#define KEY_MGSSEC "mgssec"
+#define KEY_NEXT_ID "next_id"
+#define KEY_READ_ONLY "read-only"
+#define KEY_REGISTER_TARGET "register_target"
+#define KEY_SET_FS "set_fs"
+#define KEY_TGT_COUNT "tgt_count"
+/* KEY_SET_INFO in lustre_idl.h */
+#define KEY_SPTLRPC_CONF "sptlrpc_conf"
+#define KEY_CONNECT_FLAG "connect_flags"
+#define KEY_SYNC_LOCK_CANCEL "sync_lock_cancel"
+
+#define KEY_CACHE_SET "cache_set"
+#define KEY_CACHE_LRU_SHRINK "cache_lru_shrink"
+#define KEY_CHANGELOG_INDEX "changelog_index"
+
+struct lu_context;
+
+/* /!\ must be coherent with include/linux/namei.h on patched kernel */
+#define IT_OPEN (1 << 0)
+#define IT_CREAT (1 << 1)
+#define IT_READDIR (1 << 2)
+#define IT_GETATTR (1 << 3)
+#define IT_LOOKUP (1 << 4)
+#define IT_UNLINK (1 << 5)
+#define IT_TRUNC (1 << 6)
+#define IT_GETXATTR (1 << 7)
+#define IT_EXEC (1 << 8)
+#define IT_PIN (1 << 9)
+#define IT_LAYOUT (1 << 10)
+#define IT_QUOTA_DQACQ (1 << 11)
+#define IT_QUOTA_CONN (1 << 12)
+#define IT_SETXATTR (1 << 13)
+
+static inline int it_to_lock_mode(struct lookup_intent *it)
+{
+ /* CREAT needs to be tested before open (both could be set) */
+ if (it->it_op & IT_CREAT)
+ return LCK_CW;
+ else if (it->it_op & (IT_READDIR | IT_GETATTR | IT_OPEN | IT_LOOKUP |
+ IT_LAYOUT))
+ return LCK_CR;
+ else if (it->it_op & IT_GETXATTR)
+ return LCK_PR;
+ else if (it->it_op & IT_SETXATTR)
+ return LCK_PW;
+
+ LASSERTF(0, "Invalid it_op: %d\n", it->it_op);
+ return -EINVAL;
+}
+
+struct md_op_data {
+ struct lu_fid op_fid1; /* operation fid1 (usually parent) */
+ struct lu_fid op_fid2; /* operation fid2 (usually child) */
+ struct lu_fid op_fid3; /* 2 extra fids to find conflicting */
+ struct lu_fid op_fid4; /* to the operation locks. */
+ u32 op_mds; /* what mds server open will go to */
+ struct lustre_handle op_handle;
+ s64 op_mod_time;
+ const char *op_name;
+ int op_namelen;
+ __u32 op_mode;
+ struct lmv_stripe_md *op_mea1;
+ struct lmv_stripe_md *op_mea2;
+ __u32 op_suppgids[2];
+ __u32 op_fsuid;
+ __u32 op_fsgid;
+ cfs_cap_t op_cap;
+ void *op_data;
+
+ /* iattr fields and blocks. */
+ struct iattr op_attr;
+ unsigned int op_attr_flags;
+ __u64 op_valid;
+ loff_t op_attr_blocks;
+
+ /* Size-on-MDS epoch and flags. */
+ __u64 op_ioepoch;
+ __u32 op_flags;
+
+ /* Capa fields */
+ struct obd_capa *op_capa1;
+ struct obd_capa *op_capa2;
+
+ /* Various operation flags. */
+ enum mds_op_bias op_bias;
+
+ /* Operation type */
+ __u32 op_opc;
+
+ /* Used by readdir */
+ __u64 op_offset;
+
+ /* Used by readdir */
+ __u32 op_npages;
+
+ /* used to transfer info between the stacks of MD client
+ * see enum op_cli_flags */
+ __u32 op_cli_flags;
+
+ /* File object data version for HSM release, on client */
+ __u64 op_data_version;
+ struct lustre_handle op_lease_handle;
+};
+
+enum op_cli_flags {
+ CLI_SET_MEA = 1 << 0,
+ CLI_RM_ENTRY = 1 << 1,
+};
+
+struct md_enqueue_info;
+/* metadata stat-ahead */
+
+struct md_enqueue_info {
+ struct md_op_data mi_data;
+ struct lookup_intent mi_it;
+ struct lustre_handle mi_lockh;
+ struct inode *mi_dir;
+ int (*mi_cb)(struct ptlrpc_request *req,
+ struct md_enqueue_info *minfo, int rc);
+ __u64 mi_cbdata;
+ unsigned int mi_generation;
+};
+
+struct obd_ops {
+ struct module *o_owner;
+ int (*o_iocontrol)(unsigned int cmd, struct obd_export *exp, int len,
+ void *karg, void *uarg);
+ int (*o_get_info)(const struct lu_env *env, struct obd_export *,
+ __u32 keylen, void *key, __u32 *vallen, void *val,
+ struct lov_stripe_md *lsm);
+ int (*o_set_info_async)(const struct lu_env *, struct obd_export *,
+ __u32 keylen, void *key,
+ __u32 vallen, void *val,
+ struct ptlrpc_request_set *set);
+ int (*o_attach)(struct obd_device *dev, u32 len, void *data);
+ int (*o_detach)(struct obd_device *dev);
+ int (*o_setup)(struct obd_device *dev, struct lustre_cfg *cfg);
+ int (*o_precleanup)(struct obd_device *dev,
+ enum obd_cleanup_stage cleanup_stage);
+ int (*o_cleanup)(struct obd_device *dev);
+ int (*o_process_config)(struct obd_device *dev, u32 len, void *data);
+ int (*o_postrecov)(struct obd_device *dev);
+ int (*o_add_conn)(struct obd_import *imp, struct obd_uuid *uuid,
+ int priority);
+ int (*o_del_conn)(struct obd_import *imp, struct obd_uuid *uuid);
+ /* connect to the target device with given connection
+ * data. @ocd->ocd_connect_flags is modified to reflect flags actually
+ * granted by the target, which are guaranteed to be a subset of flags
+ * asked for. If @ocd == NULL, use default parameters. */
+ int (*o_connect)(const struct lu_env *env,
+ struct obd_export **exp, struct obd_device *src,
+ struct obd_uuid *cluuid, struct obd_connect_data *ocd,
+ void *localdata);
+ int (*o_reconnect)(const struct lu_env *env,
+ struct obd_export *exp, struct obd_device *src,
+ struct obd_uuid *cluuid,
+ struct obd_connect_data *ocd,
+ void *localdata);
+ int (*o_disconnect)(struct obd_export *exp);
+
+ /* Initialize/finalize fids infrastructure. */
+ int (*o_fid_init)(struct obd_device *obd,
+ struct obd_export *exp, enum lu_cli_type type);
+ int (*o_fid_fini)(struct obd_device *obd);
+
+ /* Allocate new fid according to passed @hint. */
+ int (*o_fid_alloc)(struct obd_export *exp, struct lu_fid *fid,
+ struct md_op_data *op_data);
+
+ /*
+ * Object with @fid is getting deleted, we may want to do something
+ * about this.
+ */
+ int (*o_statfs)(const struct lu_env *, struct obd_export *exp,
+ struct obd_statfs *osfs, __u64 max_age, __u32 flags);
+ int (*o_statfs_async)(struct obd_export *exp, struct obd_info *oinfo,
+ __u64 max_age, struct ptlrpc_request_set *set);
+ int (*o_packmd)(struct obd_export *exp, struct lov_mds_md **disk_tgt,
+ struct lov_stripe_md *mem_src);
+ int (*o_unpackmd)(struct obd_export *exp,
+ struct lov_stripe_md **mem_tgt,
+ struct lov_mds_md *disk_src, int disk_len);
+ int (*o_preallocate)(struct lustre_handle *, u32 *req, u64 *ids);
+ /* FIXME: add fid capability support for create & destroy! */
+ int (*o_create)(const struct lu_env *env, struct obd_export *exp,
+ struct obdo *oa, struct lov_stripe_md **ea,
+ struct obd_trans_info *oti);
+ int (*o_destroy)(const struct lu_env *env, struct obd_export *exp,
+ struct obdo *oa, struct lov_stripe_md *ea,
+ struct obd_trans_info *oti, struct obd_export *md_exp,
+ void *capa);
+ int (*o_setattr)(const struct lu_env *, struct obd_export *exp,
+ struct obd_info *oinfo, struct obd_trans_info *oti);
+ int (*o_setattr_async)(struct obd_export *exp, struct obd_info *oinfo,
+ struct obd_trans_info *oti,
+ struct ptlrpc_request_set *rqset);
+ int (*o_getattr)(const struct lu_env *env, struct obd_export *exp,
+ struct obd_info *oinfo);
+ int (*o_getattr_async)(struct obd_export *exp, struct obd_info *oinfo,
+ struct ptlrpc_request_set *set);
+ int (*o_adjust_kms)(struct obd_export *exp, struct lov_stripe_md *lsm,
+ u64 size, int shrink);
+ int (*o_preprw)(const struct lu_env *env, int cmd,
+ struct obd_export *exp, struct obdo *oa, int objcount,
+ struct obd_ioobj *obj, struct niobuf_remote *remote,
+ int *nr_pages, struct niobuf_local *local,
+ struct obd_trans_info *oti, struct lustre_capa *capa);
+ int (*o_commitrw)(const struct lu_env *env, int cmd,
+ struct obd_export *exp, struct obdo *oa,
+ int objcount, struct obd_ioobj *obj,
+ struct niobuf_remote *remote, int pages,
+ struct niobuf_local *local,
+ struct obd_trans_info *oti, int rc);
+ int (*o_find_cbdata)(struct obd_export *, struct lov_stripe_md *,
+ ldlm_iterator_t it, void *data);
+ int (*o_init_export)(struct obd_export *exp);
+ int (*o_destroy_export)(struct obd_export *exp);
+
+ /* metadata-only methods */
+ int (*o_import_event)(struct obd_device *, struct obd_import *,
+ enum obd_import_event);
+
+ int (*o_notify)(struct obd_device *obd, struct obd_device *watched,
+ enum obd_notify_event ev, void *data);
+
+ int (*o_health_check)(const struct lu_env *env, struct obd_device *);
+ struct obd_uuid *(*o_get_uuid)(struct obd_export *exp);
+
+ /* quota methods */
+ int (*o_quotacheck)(struct obd_device *, struct obd_export *,
+ struct obd_quotactl *);
+ int (*o_quotactl)(struct obd_device *, struct obd_export *,
+ struct obd_quotactl *);
+
+ /* pools methods */
+ int (*o_pool_new)(struct obd_device *obd, char *poolname);
+ int (*o_pool_del)(struct obd_device *obd, char *poolname);
+ int (*o_pool_add)(struct obd_device *obd, char *poolname,
+ char *ostname);
+ int (*o_pool_rem)(struct obd_device *obd, char *poolname,
+ char *ostname);
+ void (*o_getref)(struct obd_device *obd);
+ void (*o_putref)(struct obd_device *obd);
+ /*
+ * NOTE: If adding ops, add another LPROCFS_OBD_OP_INIT() line
+ * to lprocfs_alloc_obd_stats() in obdclass/lprocfs_status.c.
+ * Also, add a wrapper function in include/linux/obd_class.h. */
+};
+
+enum {
+ LUSTRE_OPC_MKDIR = (1 << 0),
+ LUSTRE_OPC_SYMLINK = (1 << 1),
+ LUSTRE_OPC_MKNOD = (1 << 2),
+ LUSTRE_OPC_CREATE = (1 << 3),
+ LUSTRE_OPC_ANY = (1 << 4)
+};
+
+/* lmv structures */
+#define MEA_MAGIC_LAST_CHAR 0xb2221ca1
+#define MEA_MAGIC_ALL_CHARS 0xb222a11c
+#define MEA_MAGIC_HASH_SEGMENT 0xb222a11b
+
+#define MAX_HASH_SIZE_32 0x7fffffffUL
+#define MAX_HASH_SIZE 0x7fffffffffffffffULL
+#define MAX_HASH_HIGHEST_BIT 0x1000000000000000ULL
+
+struct lustre_md {
+ struct mdt_body *body;
+ struct lov_stripe_md *lsm;
+ struct lmv_stripe_md *mea;
+#ifdef CONFIG_FS_POSIX_ACL
+ struct posix_acl *posix_acl;
+#endif
+ struct mdt_remote_perm *remote_perm;
+ struct obd_capa *mds_capa;
+ struct obd_capa *oss_capa;
+};
+
+struct md_open_data {
+ struct obd_client_handle *mod_och;
+ struct ptlrpc_request *mod_open_req;
+ struct ptlrpc_request *mod_close_req;
+ atomic_t mod_refcount;
+ bool mod_is_create;
+};
+
+struct lookup_intent;
+
+struct md_ops {
+ int (*m_getstatus)(struct obd_export *, struct lu_fid *,
+ struct obd_capa **);
+ int (*m_null_inode)(struct obd_export *, const struct lu_fid *);
+ int (*m_find_cbdata)(struct obd_export *, const struct lu_fid *,
+ ldlm_iterator_t, void *);
+ int (*m_close)(struct obd_export *, struct md_op_data *,
+ struct md_open_data *, struct ptlrpc_request **);
+ int (*m_create)(struct obd_export *, struct md_op_data *,
+ const void *, int, int, __u32, __u32, cfs_cap_t,
+ __u64, struct ptlrpc_request **);
+ int (*m_done_writing)(struct obd_export *, struct md_op_data *,
+ struct md_open_data *);
+ int (*m_enqueue)(struct obd_export *, struct ldlm_enqueue_info *,
+ struct lookup_intent *, struct md_op_data *,
+ struct lustre_handle *, void *, int,
+ struct ptlrpc_request **, __u64);
+ int (*m_getattr)(struct obd_export *, struct md_op_data *,
+ struct ptlrpc_request **);
+ int (*m_getattr_name)(struct obd_export *, struct md_op_data *,
+ struct ptlrpc_request **);
+ int (*m_intent_lock)(struct obd_export *, struct md_op_data *,
+ void *, int, struct lookup_intent *, int,
+ struct ptlrpc_request **,
+ ldlm_blocking_callback, __u64);
+ int (*m_link)(struct obd_export *, struct md_op_data *,
+ struct ptlrpc_request **);
+ int (*m_rename)(struct obd_export *, struct md_op_data *,
+ const char *, int, const char *, int,
+ struct ptlrpc_request **);
+ int (*m_is_subdir)(struct obd_export *, const struct lu_fid *,
+ const struct lu_fid *,
+ struct ptlrpc_request **);
+ int (*m_setattr)(struct obd_export *, struct md_op_data *, void *,
+ int , void *, int, struct ptlrpc_request **,
+ struct md_open_data **mod);
+ int (*m_sync)(struct obd_export *, const struct lu_fid *,
+ struct obd_capa *, struct ptlrpc_request **);
+ int (*m_readpage)(struct obd_export *, struct md_op_data *,
+ struct page **, struct ptlrpc_request **);
+
+ int (*m_unlink)(struct obd_export *, struct md_op_data *,
+ struct ptlrpc_request **);
+
+ int (*m_setxattr)(struct obd_export *, const struct lu_fid *,
+ struct obd_capa *, u64, const char *,
+ const char *, int, int, int, __u32,
+ struct ptlrpc_request **);
+
+ int (*m_getxattr)(struct obd_export *, const struct lu_fid *,
+ struct obd_capa *, u64, const char *,
+ const char *, int, int, int,
+ struct ptlrpc_request **);
+
+ int (*m_init_ea_size)(struct obd_export *, int, int, int, int);
+
+ int (*m_get_lustre_md)(struct obd_export *, struct ptlrpc_request *,
+ struct obd_export *, struct obd_export *,
+ struct lustre_md *);
+
+ int (*m_free_lustre_md)(struct obd_export *, struct lustre_md *);
+
+ int (*m_set_open_replay_data)(struct obd_export *,
+ struct obd_client_handle *,
+ struct lookup_intent *);
+ int (*m_clear_open_replay_data)(struct obd_export *,
+ struct obd_client_handle *);
+ int (*m_set_lock_data)(struct obd_export *, __u64 *, void *, __u64 *);
+
+ ldlm_mode_t (*m_lock_match)(struct obd_export *, __u64,
+ const struct lu_fid *, ldlm_type_t,
+ ldlm_policy_data_t *, ldlm_mode_t,
+ struct lustre_handle *);
+
+ int (*m_cancel_unused)(struct obd_export *, const struct lu_fid *,
+ ldlm_policy_data_t *, ldlm_mode_t,
+ ldlm_cancel_flags_t flags, void *opaque);
+ int (*m_renew_capa)(struct obd_export *, struct obd_capa *oc,
+ renew_capa_cb_t cb);
+ int (*m_unpack_capa)(struct obd_export *, struct ptlrpc_request *,
+ const struct req_msg_field *, struct obd_capa **);
+
+ int (*m_get_remote_perm)(struct obd_export *, const struct lu_fid *,
+ struct obd_capa *, __u32,
+ struct ptlrpc_request **);
+
+ int (*m_intent_getattr_async)(struct obd_export *,
+ struct md_enqueue_info *,
+ struct ldlm_enqueue_info *);
+
+ int (*m_revalidate_lock)(struct obd_export *, struct lookup_intent *,
+ struct lu_fid *, __u64 *bits);
+
+ /*
+ * NOTE: If adding ops, add another LPROCFS_MD_OP_INIT() line to
+ * lprocfs_alloc_md_stats() in obdclass/lprocfs_status.c. Also, add a
+ * wrapper function in include/linux/obd_class.h.
+ */
+};
+
+struct lsm_operations {
+ void (*lsm_free)(struct lov_stripe_md *);
+ int (*lsm_destroy)(struct lov_stripe_md *, struct obdo *oa,
+ struct obd_export *md_exp);
+ void (*lsm_stripe_by_index)(struct lov_stripe_md *, int *, u64 *,
+ u64 *);
+ void (*lsm_stripe_by_offset)(struct lov_stripe_md *, int *, u64 *,
+ u64 *);
+ int (*lsm_lmm_verify)(struct lov_mds_md *lmm, int lmm_bytes,
+ __u16 *stripe_count);
+ int (*lsm_unpackmd)(struct lov_obd *lov, struct lov_stripe_md *lsm,
+ struct lov_mds_md *lmm);
+};
+
+extern const struct lsm_operations lsm_v1_ops;
+extern const struct lsm_operations lsm_v3_ops;
+static inline const struct lsm_operations *lsm_op_find(int magic)
+{
+ switch (magic) {
+ case LOV_MAGIC_V1:
+ return &lsm_v1_ops;
+ case LOV_MAGIC_V3:
+ return &lsm_v3_ops;
+ default:
+ CERROR("Cannot recognize lsm_magic %08x\n", magic);
+ return NULL;
+ }
+}
+
+/* Requests for obd_extent_calc() */
+#define OBD_CALC_STRIPE_START 1
+#define OBD_CALC_STRIPE_END 2
+
+static inline struct lustre_capa *oinfo_capa(struct obd_info *oinfo)
+{
+ return oinfo->oi_capa;
+}
+
+static inline struct md_open_data *obd_mod_alloc(void)
+{
+ struct md_open_data *mod;
+
+ OBD_ALLOC_PTR(mod);
+ if (mod == NULL)
+ return NULL;
+ atomic_set(&mod->mod_refcount, 1);
+ return mod;
+}
+
+#define obd_mod_get(mod) atomic_inc(&(mod)->mod_refcount)
+#define obd_mod_put(mod) \
+({ \
+ if (atomic_dec_and_test(&(mod)->mod_refcount)) { \
+ if ((mod)->mod_open_req) \
+ ptlrpc_req_finished((mod)->mod_open_req); \
+ OBD_FREE_PTR(mod); \
+ } \
+})
+
+void obdo_from_inode(struct obdo *dst, struct inode *src, u32 valid);
+void obdo_set_parent_fid(struct obdo *dst, const struct lu_fid *parent);
+
+/* return 1 if client should be resend request */
+static inline int client_should_resend(int resend, struct client_obd *cli)
+{
+ return atomic_read(&cli->cl_resends) ?
+ atomic_read(&cli->cl_resends) > resend : 1;
+}
+
+/**
+ * Return device name for this device
+ *
+ * XXX: lu_device is declared before obd_device, while a pointer pointing
+ * back to obd_device in lu_device, so this helper function defines here
+ * instead of in lu_object.h
+ */
+static inline const char *lu_dev_name(const struct lu_device *lu_dev)
+{
+ return lu_dev->ld_obd->obd_name;
+}
+
+static inline bool filename_is_volatile(const char *name, int namelen, int *idx)
+{
+ const char *start;
+ char *end;
+
+ if (strncmp(name, LUSTRE_VOLATILE_HDR, LUSTRE_VOLATILE_HDR_LEN) != 0)
+ return false;
+
+ /* caller does not care of idx */
+ if (idx == NULL)
+ return true;
+
+ /* volatile file, the MDT can be set from name */
+ /* name format is LUSTRE_VOLATILE_HDR:[idx]: */
+ /* if no MDT is specified, use std way */
+ if (namelen < LUSTRE_VOLATILE_HDR_LEN + 2)
+ goto bad_format;
+ /* test for no MDT idx case */
+ if ((*(name + LUSTRE_VOLATILE_HDR_LEN) == ':') &&
+ (*(name + LUSTRE_VOLATILE_HDR_LEN + 1) == ':')) {
+ *idx = -1;
+ return true;
+ }
+ /* we have an idx, read it */
+ start = name + LUSTRE_VOLATILE_HDR_LEN + 1;
+ *idx = strtoul(start, &end, 0);
+ /* error cases:
+ * no digit, no trailing :, negative value
+ */
+ if (((*idx == 0) && (end == start)) ||
+ (*end != ':') || (*idx < 0))
+ goto bad_format;
+
+ return true;
+bad_format:
+ /* bad format of mdt idx, we cannot return an error
+ * to caller so we use hash algo */
+ CERROR("Bad volatile file name format: %s\n",
+ name + LUSTRE_VOLATILE_HDR_LEN);
+ return false;
+}
+
+static inline int cli_brw_size(struct obd_device *obd)
+{
+ LASSERT(obd != NULL);
+ return obd->u.cli.cl_max_pages_per_rpc << PAGE_CACHE_SHIFT;
+}
+
+#endif /* __OBD_H */
diff --git a/drivers/staging/lustre/lustre/include/obd_cache.h b/drivers/staging/lustre/lustre/include/obd_cache.h
new file mode 100644
index 000000000..c8249fbb0
--- /dev/null
+++ b/drivers/staging/lustre/lustre/include/obd_cache.h
@@ -0,0 +1,39 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ */
+
+#ifndef _OBD_CACHE_H__
+#define _OBD_CACHE_H__
+
+
+#endif
diff --git a/drivers/staging/lustre/lustre/include/obd_cksum.h b/drivers/staging/lustre/lustre/include/obd_cksum.h
new file mode 100644
index 000000000..3a63462aa
--- /dev/null
+++ b/drivers/staging/lustre/lustre/include/obd_cksum.h
@@ -0,0 +1,176 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ */
+
+#ifndef __OBD_CKSUM
+#define __OBD_CKSUM
+#include "../../include/linux/libcfs/libcfs.h"
+#include "lustre/lustre_idl.h"
+
+static inline unsigned char cksum_obd2cfs(cksum_type_t cksum_type)
+{
+ switch (cksum_type) {
+ case OBD_CKSUM_CRC32:
+ return CFS_HASH_ALG_CRC32;
+ case OBD_CKSUM_ADLER:
+ return CFS_HASH_ALG_ADLER32;
+ case OBD_CKSUM_CRC32C:
+ return CFS_HASH_ALG_CRC32C;
+ default:
+ CERROR("Unknown checksum type (%x)!!!\n", cksum_type);
+ LBUG();
+ }
+ return 0;
+}
+
+/* The OBD_FL_CKSUM_* flags is packed into 5 bits of o_flags, since there can
+ * only be a single checksum type per RPC.
+ *
+ * The OBD_CHECKSUM_* type bits passed in ocd_cksum_types are a 32-bit bitmask
+ * since they need to represent the full range of checksum algorithms that
+ * both the client and server can understand.
+ *
+ * In case of an unsupported types/flags we fall back to ADLER
+ * because that is supported by all clients since 1.8
+ *
+ * In case multiple algorithms are supported the best one is used. */
+static inline u32 cksum_type_pack(cksum_type_t cksum_type)
+{
+ unsigned int performance = 0, tmp;
+ u32 flag = OBD_FL_CKSUM_ADLER;
+
+ if (cksum_type & OBD_CKSUM_CRC32) {
+ tmp = cfs_crypto_hash_speed(cksum_obd2cfs(OBD_CKSUM_CRC32));
+ if (tmp > performance) {
+ performance = tmp;
+ flag = OBD_FL_CKSUM_CRC32;
+ }
+ }
+ if (cksum_type & OBD_CKSUM_CRC32C) {
+ tmp = cfs_crypto_hash_speed(cksum_obd2cfs(OBD_CKSUM_CRC32C));
+ if (tmp > performance) {
+ performance = tmp;
+ flag = OBD_FL_CKSUM_CRC32C;
+ }
+ }
+ if (cksum_type & OBD_CKSUM_ADLER) {
+ tmp = cfs_crypto_hash_speed(cksum_obd2cfs(OBD_CKSUM_ADLER));
+ if (tmp > performance) {
+ performance = tmp;
+ flag = OBD_FL_CKSUM_ADLER;
+ }
+ }
+ if (unlikely(cksum_type && !(cksum_type & (OBD_CKSUM_CRC32C |
+ OBD_CKSUM_CRC32 |
+ OBD_CKSUM_ADLER))))
+ CWARN("unknown cksum type %x\n", cksum_type);
+
+ return flag;
+}
+
+static inline cksum_type_t cksum_type_unpack(u32 o_flags)
+{
+ switch (o_flags & OBD_FL_CKSUM_ALL) {
+ case OBD_FL_CKSUM_CRC32C:
+ return OBD_CKSUM_CRC32C;
+ case OBD_FL_CKSUM_CRC32:
+ return OBD_CKSUM_CRC32;
+ default:
+ break;
+ }
+
+ return OBD_CKSUM_ADLER;
+}
+
+/* Return a bitmask of the checksum types supported on this system.
+ * 1.8 supported ADLER it is base and not depend on hw
+ * Client uses all available local algos
+ */
+static inline cksum_type_t cksum_types_supported_client(void)
+{
+ cksum_type_t ret = OBD_CKSUM_ADLER;
+
+ CDEBUG(D_INFO, "Crypto hash speed: crc %d, crc32c %d, adler %d\n",
+ cfs_crypto_hash_speed(cksum_obd2cfs(OBD_CKSUM_CRC32)),
+ cfs_crypto_hash_speed(cksum_obd2cfs(OBD_CKSUM_CRC32C)),
+ cfs_crypto_hash_speed(cksum_obd2cfs(OBD_CKSUM_ADLER)));
+
+ if (cfs_crypto_hash_speed(cksum_obd2cfs(OBD_CKSUM_CRC32C)) > 0)
+ ret |= OBD_CKSUM_CRC32C;
+ if (cfs_crypto_hash_speed(cksum_obd2cfs(OBD_CKSUM_CRC32)) > 0)
+ ret |= OBD_CKSUM_CRC32;
+
+ return ret;
+}
+
+/* Server uses algos that perform at 50% or better of the Adler */
+static inline cksum_type_t cksum_types_supported_server(void)
+{
+ int base_speed;
+ cksum_type_t ret = OBD_CKSUM_ADLER;
+
+ CDEBUG(D_INFO, "Crypto hash speed: crc %d, crc32c %d, adler %d\n",
+ cfs_crypto_hash_speed(cksum_obd2cfs(OBD_CKSUM_CRC32)),
+ cfs_crypto_hash_speed(cksum_obd2cfs(OBD_CKSUM_CRC32C)),
+ cfs_crypto_hash_speed(cksum_obd2cfs(OBD_CKSUM_ADLER)));
+
+ base_speed = cfs_crypto_hash_speed(cksum_obd2cfs(OBD_CKSUM_ADLER)) / 2;
+
+ if (cfs_crypto_hash_speed(cksum_obd2cfs(OBD_CKSUM_CRC32C)) >=
+ base_speed)
+ ret |= OBD_CKSUM_CRC32C;
+ if (cfs_crypto_hash_speed(cksum_obd2cfs(OBD_CKSUM_CRC32)) >=
+ base_speed)
+ ret |= OBD_CKSUM_CRC32;
+
+ return ret;
+}
+
+
+/* Select the best checksum algorithm among those supplied in the cksum_types
+ * input.
+ *
+ * Currently, calling cksum_type_pack() with a mask will return the fastest
+ * checksum type due to its benchmarking at libcfs module load.
+ * Caution is advised, however, since what is fastest on a single client may
+ * not be the fastest or most efficient algorithm on the server. */
+static inline cksum_type_t cksum_type_select(cksum_type_t cksum_types)
+{
+ return cksum_type_unpack(cksum_type_pack(cksum_types));
+}
+
+/* Checksum algorithm names. Must be defined in the same order as the
+ * OBD_CKSUM_* flags. */
+#define DECLARE_CKSUM_NAME char *cksum_name[] = {"crc32", "adler", "crc32c"}
+
+#endif /* __OBD_H */
diff --git a/drivers/staging/lustre/lustre/include/obd_class.h b/drivers/staging/lustre/lustre/include/obd_class.h
new file mode 100644
index 000000000..34b5fa3f0
--- /dev/null
+++ b/drivers/staging/lustre/lustre/include/obd_class.h
@@ -0,0 +1,1929 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2011, 2012, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ */
+#ifndef __CLASS_OBD_H
+#define __CLASS_OBD_H
+
+
+#include "obd_support.h"
+#include "lustre_import.h"
+#include "lustre_net.h"
+#include "obd.h"
+#include "lustre_lib.h"
+#include "lustre/lustre_idl.h"
+#include "lprocfs_status.h"
+
+#define OBD_STATFS_NODELAY 0x0001 /* requests should be send without delay
+ * and resends for avoid deadlocks */
+#define OBD_STATFS_FROM_CACHE 0x0002 /* the statfs callback should not update
+ * obd_osfs_age */
+#define OBD_STATFS_PTLRPCD 0x0004 /* requests will be sent via ptlrpcd
+ * instead of a specific set. This
+ * means that we cannot rely on the set
+ * interpret routine to be called.
+ * lov_statfs_fini() must thus be called
+ * by the request interpret routine */
+#define OBD_STATFS_FOR_MDT0 0x0008 /* The statfs is only for retrieving
+ * information from MDT0. */
+#define OBD_FL_PUNCH 0x00000001 /* To indicate it is punch operation */
+
+/* OBD Device Declarations */
+extern struct obd_device *obd_devs[MAX_OBD_DEVICES];
+extern rwlock_t obd_dev_lock;
+
+/* OBD Operations Declarations */
+extern struct obd_device *class_conn2obd(struct lustre_handle *);
+extern struct obd_device *class_exp2obd(struct obd_export *);
+extern int class_handle_ioctl(unsigned int cmd, unsigned long arg);
+extern int lustre_get_jobid(char *jobid);
+
+struct lu_device_type;
+
+/* genops.c */
+extern struct list_head obd_types;
+struct obd_export *class_conn2export(struct lustre_handle *);
+int class_register_type(struct obd_ops *, struct md_ops *,
+ struct lprocfs_vars *, const char *nm,
+ struct lu_device_type *ldt);
+int class_unregister_type(const char *nm);
+
+struct obd_device *class_newdev(const char *type_name, const char *name);
+void class_release_dev(struct obd_device *obd);
+
+int class_name2dev(const char *name);
+struct obd_device *class_name2obd(const char *name);
+int class_uuid2dev(struct obd_uuid *uuid);
+struct obd_device *class_uuid2obd(struct obd_uuid *uuid);
+void class_obd_list(void);
+struct obd_device *class_find_client_obd(struct obd_uuid *tgt_uuid,
+ const char *typ_name,
+ struct obd_uuid *grp_uuid);
+struct obd_device *class_devices_in_group(struct obd_uuid *grp_uuid,
+ int *next);
+struct obd_device *class_num2obd(int num);
+int get_devices_count(void);
+
+int class_notify_sptlrpc_conf(const char *fsname, int namelen);
+
+char *obd_export_nid2str(struct obd_export *exp);
+
+int obd_export_evict_by_nid(struct obd_device *obd, const char *nid);
+int obd_export_evict_by_uuid(struct obd_device *obd, const char *uuid);
+int obd_connect_flags2str(char *page, int count, __u64 flags, char *sep);
+
+int obd_zombie_impexp_init(void);
+void obd_zombie_impexp_stop(void);
+void obd_zombie_impexp_cull(void);
+void obd_zombie_barrier(void);
+void obd_exports_barrier(struct obd_device *obd);
+int kuc_len(int payload_len);
+struct kuc_hdr *kuc_ptr(void *p);
+int kuc_ispayload(void *p);
+void *kuc_alloc(int payload_len, int transport, int type);
+void kuc_free(void *p, int payload_len);
+
+struct llog_handle;
+struct llog_rec_hdr;
+typedef int (*llog_cb_t)(const struct lu_env *, struct llog_handle *,
+ struct llog_rec_hdr *, void *);
+/* obd_config.c */
+struct lustre_cfg *lustre_cfg_rename(struct lustre_cfg *cfg,
+ const char *new_name);
+int class_process_config(struct lustre_cfg *lcfg);
+int class_process_proc_param(char *prefix, struct lprocfs_vars *lvars,
+ struct lustre_cfg *lcfg, void *data);
+int class_attach(struct lustre_cfg *lcfg);
+int class_setup(struct obd_device *obd, struct lustre_cfg *lcfg);
+int class_cleanup(struct obd_device *obd, struct lustre_cfg *lcfg);
+int class_detach(struct obd_device *obd, struct lustre_cfg *lcfg);
+struct obd_device *class_incref(struct obd_device *obd,
+ const char *scope, const void *source);
+void class_decref(struct obd_device *obd,
+ const char *scope, const void *source);
+void dump_exports(struct obd_device *obd, int locks);
+int class_config_llog_handler(const struct lu_env *env,
+ struct llog_handle *handle,
+ struct llog_rec_hdr *rec, void *data);
+int class_add_conn(struct obd_device *obd, struct lustre_cfg *lcfg);
+int class_add_uuid(const char *uuid, __u64 nid);
+
+/*obdecho*/
+#if defined (CONFIG_PROC_FS)
+extern void lprocfs_echo_init_vars(struct lprocfs_static_vars *lvars);
+#else
+static inline void lprocfs_echo_init_vars(struct lprocfs_static_vars *lvars)
+{
+ memset(lvars, 0, sizeof(*lvars));
+}
+#endif
+
+#define CFG_F_START 0x01 /* Set when we start updating from a log */
+#define CFG_F_MARKER 0x02 /* We are within a maker */
+#define CFG_F_SKIP 0x04 /* We should ignore this cfg command */
+#define CFG_F_COMPAT146 0x08 /* Allow old-style logs */
+#define CFG_F_EXCLUDE 0x10 /* OST exclusion list */
+
+/* Passed as data param to class_config_parse_llog */
+struct config_llog_instance {
+ char *cfg_obdname;
+ void *cfg_instance;
+ struct super_block *cfg_sb;
+ struct obd_uuid cfg_uuid;
+ llog_cb_t cfg_callback;
+ int cfg_last_idx; /* for partial llog processing */
+ int cfg_flags;
+};
+int class_config_parse_llog(const struct lu_env *env, struct llog_ctxt *ctxt,
+ char *name, struct config_llog_instance *cfg);
+int class_config_dump_llog(const struct lu_env *env, struct llog_ctxt *ctxt,
+ char *name, struct config_llog_instance *cfg);
+
+enum {
+ CONFIG_T_CONFIG = 0,
+ CONFIG_T_SPTLRPC = 1,
+ CONFIG_T_RECOVER = 2,
+ CONFIG_T_PARAMS = 3,
+ CONFIG_T_MAX = 4
+};
+
+#define PARAMS_FILENAME "params"
+#define LCTL_UPCALL "lctl"
+
+/* list of active configuration logs */
+struct config_llog_data {
+ struct ldlm_res_id cld_resid;
+ struct config_llog_instance cld_cfg;
+ struct list_head cld_list_chain;
+ atomic_t cld_refcount;
+ struct config_llog_data *cld_sptlrpc;/* depended sptlrpc log */
+ struct config_llog_data *cld_params; /* common parameters log */
+ struct config_llog_data *cld_recover;/* imperative recover log */
+ struct obd_export *cld_mgcexp;
+ struct mutex cld_lock;
+ int cld_type;
+ unsigned int cld_stopping:1, /* we were told to stop
+ * watching */
+ cld_lostlock:1; /* lock not requeued */
+ char cld_logname[0];
+};
+
+struct lustre_profile {
+ struct list_head lp_list;
+ char *lp_profile;
+ char *lp_dt;
+ char *lp_md;
+};
+
+struct lustre_profile *class_get_profile(const char *prof);
+void class_del_profile(const char *prof);
+void class_del_profiles(void);
+
+#if LUSTRE_TRACKS_LOCK_EXP_REFS
+
+void __class_export_add_lock_ref(struct obd_export *, struct ldlm_lock *);
+void __class_export_del_lock_ref(struct obd_export *, struct ldlm_lock *);
+extern void (*class_export_dump_hook)(struct obd_export *);
+
+#else
+
+#define __class_export_add_lock_ref(exp, lock) do {} while (0)
+#define __class_export_del_lock_ref(exp, lock) do {} while (0)
+
+#endif
+
+static inline void class_export_rpc_inc(struct obd_export *exp)
+{
+ atomic_inc(&(exp)->exp_rpc_count);
+ CDEBUG(D_INFO, "RPC GETting export %p : new rpc_count %d\n",
+ (exp), atomic_read(&(exp)->exp_rpc_count));
+}
+
+static inline void class_export_rpc_dec(struct obd_export *exp)
+{
+ LASSERT_ATOMIC_POS(&exp->exp_rpc_count);
+ atomic_dec(&(exp)->exp_rpc_count);
+ CDEBUG(D_INFO, "RPC PUTting export %p : new rpc_count %d\n",
+ (exp), atomic_read(&(exp)->exp_rpc_count));
+}
+
+#define class_export_lock_get(exp, lock) \
+({ \
+ atomic_inc(&(exp)->exp_locks_count); \
+ __class_export_add_lock_ref(exp, lock); \
+ CDEBUG(D_INFO, "lock GETting export %p : new locks_count %d\n", \
+ (exp), atomic_read(&(exp)->exp_locks_count)); \
+ class_export_get(exp); \
+})
+
+#define class_export_lock_put(exp, lock) \
+({ \
+ LASSERT_ATOMIC_POS(&exp->exp_locks_count); \
+ atomic_dec(&(exp)->exp_locks_count); \
+ __class_export_del_lock_ref(exp, lock); \
+ CDEBUG(D_INFO, "lock PUTting export %p : new locks_count %d\n", \
+ (exp), atomic_read(&(exp)->exp_locks_count)); \
+ class_export_put(exp); \
+})
+
+#define class_export_cb_get(exp) \
+({ \
+ atomic_inc(&(exp)->exp_cb_count); \
+ CDEBUG(D_INFO, "callback GETting export %p : new cb_count %d\n",\
+ (exp), atomic_read(&(exp)->exp_cb_count)); \
+ class_export_get(exp); \
+})
+
+#define class_export_cb_put(exp) \
+({ \
+ LASSERT_ATOMIC_POS(&exp->exp_cb_count); \
+ atomic_dec(&(exp)->exp_cb_count); \
+ CDEBUG(D_INFO, "callback PUTting export %p : new cb_count %d\n",\
+ (exp), atomic_read(&(exp)->exp_cb_count)); \
+ class_export_put(exp); \
+})
+
+/* genops.c */
+struct obd_export *class_export_get(struct obd_export *exp);
+void class_export_put(struct obd_export *exp);
+struct obd_export *class_new_export(struct obd_device *obddev,
+ struct obd_uuid *cluuid);
+void class_unlink_export(struct obd_export *exp);
+
+struct obd_import *class_import_get(struct obd_import *);
+void class_import_put(struct obd_import *);
+struct obd_import *class_new_import(struct obd_device *obd);
+void class_destroy_import(struct obd_import *exp);
+
+struct obd_type *class_search_type(const char *name);
+struct obd_type *class_get_type(const char *name);
+void class_put_type(struct obd_type *type);
+int class_connect(struct lustre_handle *conn, struct obd_device *obd,
+ struct obd_uuid *cluuid);
+int class_disconnect(struct obd_export *exp);
+void class_fail_export(struct obd_export *exp);
+int class_connected_export(struct obd_export *exp);
+void class_disconnect_exports(struct obd_device *obddev);
+int class_manual_cleanup(struct obd_device *obd);
+void class_disconnect_stale_exports(struct obd_device *,
+ int (*test_export)(struct obd_export *));
+static inline enum obd_option exp_flags_from_obd(struct obd_device *obd)
+{
+ return ((obd->obd_fail ? OBD_OPT_FAILOVER : 0) |
+ (obd->obd_force ? OBD_OPT_FORCE : 0) |
+ (obd->obd_abort_recovery ? OBD_OPT_ABORT_RECOV : 0) |
+ 0);
+}
+
+struct inode;
+struct lu_attr;
+struct obdo;
+void obdo_from_la(struct obdo *dst, struct lu_attr *la, __u64 valid);
+void la_from_obdo(struct lu_attr *la, struct obdo *dst, u32 valid);
+void obdo_refresh_inode(struct inode *dst, struct obdo *src, u32 valid);
+void obdo_to_inode(struct inode *dst, struct obdo *src, u32 valid);
+
+void obdo_cpy_md(struct obdo *dst, struct obdo *src, u32 valid);
+void obdo_to_ioobj(struct obdo *oa, struct obd_ioobj *ioobj);
+void obdo_from_iattr(struct obdo *oa, struct iattr *attr,
+ unsigned int ia_valid);
+void iattr_from_obdo(struct iattr *attr, struct obdo *oa, u32 valid);
+void md_from_obdo(struct md_op_data *op_data, struct obdo *oa, u32 valid);
+void obdo_from_md(struct obdo *oa, struct md_op_data *op_data,
+ unsigned int valid);
+
+void obdo_cpu_to_le(struct obdo *dobdo, struct obdo *sobdo);
+void obdo_le_to_cpu(struct obdo *dobdo, struct obdo *sobdo);
+
+#define OBT(dev) (dev)->obd_type
+#define OBP(dev, op) (dev)->obd_type->typ_dt_ops->o_ ## op
+#define MDP(dev, op) (dev)->obd_type->typ_md_ops->m_ ## op
+#define CTXTP(ctxt, op) (ctxt)->loc_logops->lop_##op
+
+/* Ensure obd_setup: used for cleanup which must be called
+ while obd is stopping */
+static inline int obd_check_dev(struct obd_device *obd)
+{
+ if (!obd) {
+ CERROR("NULL device\n");
+ return -ENODEV;
+ }
+ return 0;
+}
+
+/* ensure obd_setup and !obd_stopping */
+static inline int obd_check_dev_active(struct obd_device *obd)
+{
+ int rc;
+
+ rc = obd_check_dev(obd);
+ if (rc)
+ return rc;
+ if (!obd->obd_set_up || obd->obd_stopping) {
+ CERROR("Device %d not setup\n", obd->obd_minor);
+ return -ENODEV;
+ }
+ return rc;
+}
+
+#if defined (CONFIG_PROC_FS)
+#define OBD_COUNTER_OFFSET(op) \
+ ((offsetof(struct obd_ops, o_ ## op) - \
+ offsetof(struct obd_ops, o_iocontrol)) \
+ / sizeof(((struct obd_ops *)(0))->o_iocontrol))
+
+#define OBD_COUNTER_INCREMENT(obdx, op) \
+ if ((obdx)->obd_stats != NULL) { \
+ unsigned int coffset; \
+ coffset = (unsigned int)((obdx)->obd_cntr_base) + \
+ OBD_COUNTER_OFFSET(op); \
+ LASSERT(coffset < (obdx)->obd_stats->ls_num); \
+ lprocfs_counter_incr((obdx)->obd_stats, coffset); \
+ }
+
+#define EXP_COUNTER_INCREMENT(export, op) \
+ if ((export)->exp_obd->obd_stats != NULL) { \
+ unsigned int coffset; \
+ coffset = (unsigned int)((export)->exp_obd->obd_cntr_base) + \
+ OBD_COUNTER_OFFSET(op); \
+ LASSERT(coffset < (export)->exp_obd->obd_stats->ls_num); \
+ lprocfs_counter_incr((export)->exp_obd->obd_stats, coffset); \
+ if ((export)->exp_nid_stats != NULL && \
+ (export)->exp_nid_stats->nid_stats != NULL) \
+ lprocfs_counter_incr( \
+ (export)->exp_nid_stats->nid_stats, coffset);\
+ }
+
+#define MD_COUNTER_OFFSET(op) \
+ ((offsetof(struct md_ops, m_ ## op) - \
+ offsetof(struct md_ops, m_getstatus)) \
+ / sizeof(((struct md_ops *)(0))->m_getstatus))
+
+#define MD_COUNTER_INCREMENT(obdx, op) \
+ if ((obd)->md_stats != NULL) { \
+ unsigned int coffset; \
+ coffset = (unsigned int)((obdx)->md_cntr_base) + \
+ MD_COUNTER_OFFSET(op); \
+ LASSERT(coffset < (obdx)->md_stats->ls_num); \
+ lprocfs_counter_incr((obdx)->md_stats, coffset); \
+ }
+
+#define EXP_MD_COUNTER_INCREMENT(export, op) \
+ if ((export)->exp_obd->obd_stats != NULL) { \
+ unsigned int coffset; \
+ coffset = (unsigned int)((export)->exp_obd->md_cntr_base) + \
+ MD_COUNTER_OFFSET(op); \
+ LASSERT(coffset < (export)->exp_obd->md_stats->ls_num); \
+ lprocfs_counter_incr((export)->exp_obd->md_stats, coffset); \
+ if ((export)->exp_md_stats != NULL) \
+ lprocfs_counter_incr( \
+ (export)->exp_md_stats, coffset); \
+ }
+
+#else
+#define OBD_COUNTER_OFFSET(op)
+#define OBD_COUNTER_INCREMENT(obd, op)
+#define EXP_COUNTER_INCREMENT(exp, op)
+#define MD_COUNTER_INCREMENT(obd, op)
+#define EXP_MD_COUNTER_INCREMENT(exp, op)
+#endif
+
+static inline int lprocfs_nid_ldlm_stats_init(struct nid_stat *tmp)
+{
+ /* Always add in ldlm_stats */
+ tmp->nid_ldlm_stats = lprocfs_alloc_stats(LDLM_LAST_OPC - LDLM_FIRST_OPC
+ ,LPROCFS_STATS_FLAG_NOPERCPU);
+ if (tmp->nid_ldlm_stats == NULL)
+ return -ENOMEM;
+
+ lprocfs_init_ldlm_stats(tmp->nid_ldlm_stats);
+
+ return lprocfs_register_stats(tmp->nid_proc, "ldlm_stats",
+ tmp->nid_ldlm_stats);
+}
+
+#define OBD_CHECK_MD_OP(obd, op, err) \
+do { \
+ if (!OBT(obd) || !MDP((obd), op)) { \
+ if (err) \
+ CERROR("md_" #op ": dev %s/%d no operation\n", \
+ obd->obd_name, obd->obd_minor); \
+ return err; \
+ } \
+} while (0)
+
+#define EXP_CHECK_MD_OP(exp, op) \
+do { \
+ if ((exp) == NULL) { \
+ CERROR("obd_" #op ": NULL export\n"); \
+ return -ENODEV; \
+ } \
+ if ((exp)->exp_obd == NULL || !OBT((exp)->exp_obd)) { \
+ CERROR("obd_" #op ": cleaned up obd\n"); \
+ return -EOPNOTSUPP; \
+ } \
+ if (!OBT((exp)->exp_obd) || !MDP((exp)->exp_obd, op)) { \
+ CERROR("obd_" #op ": dev %s/%d no operation\n", \
+ (exp)->exp_obd->obd_name, \
+ (exp)->exp_obd->obd_minor); \
+ return -EOPNOTSUPP; \
+ } \
+} while (0)
+
+
+#define OBD_CHECK_DT_OP(obd, op, err) \
+do { \
+ if (!OBT(obd) || !OBP((obd), op)) { \
+ if (err) \
+ CERROR("obd_" #op ": dev %d no operation\n", \
+ obd->obd_minor); \
+ return err; \
+ } \
+} while (0)
+
+#define EXP_CHECK_DT_OP(exp, op) \
+do { \
+ if ((exp) == NULL) { \
+ CERROR("obd_" #op ": NULL export\n"); \
+ return -ENODEV; \
+ } \
+ if ((exp)->exp_obd == NULL || !OBT((exp)->exp_obd)) { \
+ CERROR("obd_" #op ": cleaned up obd\n"); \
+ return -EOPNOTSUPP; \
+ } \
+ if (!OBT((exp)->exp_obd) || !OBP((exp)->exp_obd, op)) { \
+ CERROR("obd_" #op ": dev %d no operation\n", \
+ (exp)->exp_obd->obd_minor); \
+ return -EOPNOTSUPP; \
+ } \
+} while (0)
+
+#define CTXT_CHECK_OP(ctxt, op, err) \
+do { \
+ if (!OBT(ctxt->loc_obd) || !CTXTP((ctxt), op)) { \
+ if (err) \
+ CERROR("lop_" #op ": dev %d no operation\n", \
+ ctxt->loc_obd->obd_minor); \
+ return err; \
+ } \
+} while (0)
+
+static inline int class_devno_max(void)
+{
+ return MAX_OBD_DEVICES;
+}
+
+static inline int obd_get_info(const struct lu_env *env,
+ struct obd_export *exp, __u32 keylen,
+ void *key, __u32 *vallen, void *val,
+ struct lov_stripe_md *lsm)
+{
+ int rc;
+
+ EXP_CHECK_DT_OP(exp, get_info);
+ EXP_COUNTER_INCREMENT(exp, get_info);
+
+ rc = OBP(exp->exp_obd, get_info)(env, exp, keylen, key, vallen, val,
+ lsm);
+ return rc;
+}
+
+static inline int obd_set_info_async(const struct lu_env *env,
+ struct obd_export *exp, u32 keylen,
+ void *key, u32 vallen, void *val,
+ struct ptlrpc_request_set *set)
+{
+ int rc;
+
+ EXP_CHECK_DT_OP(exp, set_info_async);
+ EXP_COUNTER_INCREMENT(exp, set_info_async);
+
+ rc = OBP(exp->exp_obd, set_info_async)(env, exp, keylen, key, vallen,
+ val, set);
+ return rc;
+}
+
+/*
+ * obd-lu integration.
+ *
+ * Functionality is being moved into new lu_device-based layering, but some
+ * pieces of configuration process are still based on obd devices.
+ *
+ * Specifically, lu_device_type_operations::ldto_device_alloc() methods fully
+ * subsume ->o_setup() methods of obd devices they replace. The same for
+ * lu_device_operations::ldo_process_config() and ->o_process_config(). As a
+ * result, obd_setup() and obd_process_config() branch and call one XOR
+ * another.
+ *
+ * Yet neither lu_device_type_operations::ldto_device_fini() nor
+ * lu_device_type_operations::ldto_device_free() fully implement the
+ * functionality of ->o_precleanup() and ->o_cleanup() they override. Hence,
+ * obd_precleanup() and obd_cleanup() call both lu_device and obd operations.
+ */
+
+#define DECLARE_LU_VARS(ldt, d) \
+ struct lu_device_type *ldt; \
+ struct lu_device *d
+
+static inline int obd_setup(struct obd_device *obd, struct lustre_cfg *cfg)
+{
+ int rc;
+ DECLARE_LU_VARS(ldt, d);
+
+ ldt = obd->obd_type->typ_lu;
+ if (ldt != NULL) {
+ struct lu_context session_ctx;
+ struct lu_env env;
+ lu_context_init(&session_ctx, LCT_SESSION);
+ session_ctx.lc_thread = NULL;
+ lu_context_enter(&session_ctx);
+
+ rc = lu_env_init(&env, ldt->ldt_ctx_tags);
+ if (rc == 0) {
+ env.le_ses = &session_ctx;
+ d = ldt->ldt_ops->ldto_device_alloc(&env, ldt, cfg);
+ lu_env_fini(&env);
+ if (!IS_ERR(d)) {
+ obd->obd_lu_dev = d;
+ d->ld_obd = obd;
+ rc = 0;
+ } else
+ rc = PTR_ERR(d);
+ }
+ lu_context_exit(&session_ctx);
+ lu_context_fini(&session_ctx);
+
+ } else {
+ OBD_CHECK_DT_OP(obd, setup, -EOPNOTSUPP);
+ OBD_COUNTER_INCREMENT(obd, setup);
+ rc = OBP(obd, setup)(obd, cfg);
+ }
+ return rc;
+}
+
+static inline int obd_precleanup(struct obd_device *obd,
+ enum obd_cleanup_stage cleanup_stage)
+{
+ int rc;
+ DECLARE_LU_VARS(ldt, d);
+
+ rc = obd_check_dev(obd);
+ if (rc)
+ return rc;
+ ldt = obd->obd_type->typ_lu;
+ d = obd->obd_lu_dev;
+ if (ldt != NULL && d != NULL) {
+ if (cleanup_stage == OBD_CLEANUP_EXPORTS) {
+ struct lu_env env;
+
+ rc = lu_env_init(&env, ldt->ldt_ctx_tags);
+ if (rc == 0) {
+ ldt->ldt_ops->ldto_device_fini(&env, d);
+ lu_env_fini(&env);
+ }
+ }
+ }
+ OBD_CHECK_DT_OP(obd, precleanup, 0);
+ OBD_COUNTER_INCREMENT(obd, precleanup);
+
+ rc = OBP(obd, precleanup)(obd, cleanup_stage);
+ return rc;
+}
+
+static inline int obd_cleanup(struct obd_device *obd)
+{
+ int rc;
+ DECLARE_LU_VARS(ldt, d);
+
+ rc = obd_check_dev(obd);
+ if (rc)
+ return rc;
+
+ ldt = obd->obd_type->typ_lu;
+ d = obd->obd_lu_dev;
+ if (ldt != NULL && d != NULL) {
+ struct lu_env env;
+
+ rc = lu_env_init(&env, ldt->ldt_ctx_tags);
+ if (rc == 0) {
+ ldt->ldt_ops->ldto_device_free(&env, d);
+ lu_env_fini(&env);
+ obd->obd_lu_dev = NULL;
+ }
+ }
+ OBD_CHECK_DT_OP(obd, cleanup, 0);
+ OBD_COUNTER_INCREMENT(obd, cleanup);
+
+ rc = OBP(obd, cleanup)(obd);
+ return rc;
+}
+
+static inline void obd_cleanup_client_import(struct obd_device *obd)
+{
+ /* If we set up but never connected, the
+ client import will not have been cleaned. */
+ down_write(&obd->u.cli.cl_sem);
+ if (obd->u.cli.cl_import) {
+ struct obd_import *imp;
+ imp = obd->u.cli.cl_import;
+ CDEBUG(D_CONFIG, "%s: client import never connected\n",
+ obd->obd_name);
+ ptlrpc_invalidate_import(imp);
+ if (imp->imp_rq_pool) {
+ ptlrpc_free_rq_pool(imp->imp_rq_pool);
+ imp->imp_rq_pool = NULL;
+ }
+ client_destroy_import(imp);
+ obd->u.cli.cl_import = NULL;
+ }
+ up_write(&obd->u.cli.cl_sem);
+}
+
+static inline int
+obd_process_config(struct obd_device *obd, int datalen, void *data)
+{
+ int rc;
+ DECLARE_LU_VARS(ldt, d);
+
+ rc = obd_check_dev(obd);
+ if (rc)
+ return rc;
+
+ obd->obd_process_conf = 1;
+ ldt = obd->obd_type->typ_lu;
+ d = obd->obd_lu_dev;
+ if (ldt != NULL && d != NULL) {
+ struct lu_env env;
+
+ rc = lu_env_init(&env, ldt->ldt_ctx_tags);
+ if (rc == 0) {
+ rc = d->ld_ops->ldo_process_config(&env, d, data);
+ lu_env_fini(&env);
+ }
+ } else {
+ OBD_CHECK_DT_OP(obd, process_config, -EOPNOTSUPP);
+ rc = OBP(obd, process_config)(obd, datalen, data);
+ }
+ OBD_COUNTER_INCREMENT(obd, process_config);
+ obd->obd_process_conf = 0;
+
+ return rc;
+}
+
+/* Pack an in-memory MD struct for storage on disk.
+ * Returns +ve size of packed MD (0 for free), or -ve error.
+ *
+ * If @disk_tgt == NULL, MD size is returned (max size if @mem_src == NULL).
+ * If @*disk_tgt != NULL and @mem_src == NULL, @*disk_tgt will be freed.
+ * If @*disk_tgt == NULL, it will be allocated
+ */
+static inline int obd_packmd(struct obd_export *exp,
+ struct lov_mds_md **disk_tgt,
+ struct lov_stripe_md *mem_src)
+{
+ int rc;
+
+ EXP_CHECK_DT_OP(exp, packmd);
+ EXP_COUNTER_INCREMENT(exp, packmd);
+
+ rc = OBP(exp->exp_obd, packmd)(exp, disk_tgt, mem_src);
+ return rc;
+}
+
+static inline int obd_size_diskmd(struct obd_export *exp,
+ struct lov_stripe_md *mem_src)
+{
+ return obd_packmd(exp, NULL, mem_src);
+}
+
+static inline int obd_free_diskmd(struct obd_export *exp,
+ struct lov_mds_md **disk_tgt)
+{
+ LASSERT(disk_tgt);
+ LASSERT(*disk_tgt);
+ /*
+ * LU-2590, for caller's convenience, *disk_tgt could be host
+ * endianness, it needs swab to LE if necessary, while just
+ * lov_mds_md header needs it for figuring out how much memory
+ * needs to be freed.
+ */
+ if ((cpu_to_le32(LOV_MAGIC) != LOV_MAGIC) &&
+ (((*disk_tgt)->lmm_magic == LOV_MAGIC_V1) ||
+ ((*disk_tgt)->lmm_magic == LOV_MAGIC_V3)))
+ lustre_swab_lov_mds_md(*disk_tgt);
+ return obd_packmd(exp, disk_tgt, NULL);
+}
+
+/* Unpack an MD struct from disk to in-memory format.
+ * Returns +ve size of unpacked MD (0 for free), or -ve error.
+ *
+ * If @mem_tgt == NULL, MD size is returned (max size if @disk_src == NULL).
+ * If @*mem_tgt != NULL and @disk_src == NULL, @*mem_tgt will be freed.
+ * If @*mem_tgt == NULL, it will be allocated
+ */
+static inline int obd_unpackmd(struct obd_export *exp,
+ struct lov_stripe_md **mem_tgt,
+ struct lov_mds_md *disk_src,
+ int disk_len)
+{
+ int rc;
+
+ EXP_CHECK_DT_OP(exp, unpackmd);
+ EXP_COUNTER_INCREMENT(exp, unpackmd);
+
+ rc = OBP(exp->exp_obd, unpackmd)(exp, mem_tgt, disk_src, disk_len);
+ return rc;
+}
+
+/* helper functions */
+static inline int obd_alloc_memmd(struct obd_export *exp,
+ struct lov_stripe_md **mem_tgt)
+{
+ LASSERT(mem_tgt);
+ LASSERT(*mem_tgt == NULL);
+ return obd_unpackmd(exp, mem_tgt, NULL, 0);
+}
+
+static inline int obd_free_memmd(struct obd_export *exp,
+ struct lov_stripe_md **mem_tgt)
+{
+ int rc;
+
+ LASSERT(mem_tgt);
+ LASSERT(*mem_tgt);
+ rc = obd_unpackmd(exp, mem_tgt, NULL, 0);
+ *mem_tgt = NULL;
+ return rc;
+}
+
+static inline int obd_create(const struct lu_env *env, struct obd_export *exp,
+ struct obdo *obdo, struct lov_stripe_md **ea,
+ struct obd_trans_info *oti)
+{
+ int rc;
+
+ EXP_CHECK_DT_OP(exp, create);
+ EXP_COUNTER_INCREMENT(exp, create);
+
+ rc = OBP(exp->exp_obd, create)(env, exp, obdo, ea, oti);
+ return rc;
+}
+
+static inline int obd_destroy(const struct lu_env *env, struct obd_export *exp,
+ struct obdo *obdo, struct lov_stripe_md *ea,
+ struct obd_trans_info *oti,
+ struct obd_export *md_exp, void *capa)
+{
+ int rc;
+
+ EXP_CHECK_DT_OP(exp, destroy);
+ EXP_COUNTER_INCREMENT(exp, destroy);
+
+ rc = OBP(exp->exp_obd, destroy)(env, exp, obdo, ea, oti, md_exp, capa);
+ return rc;
+}
+
+static inline int obd_getattr(const struct lu_env *env, struct obd_export *exp,
+ struct obd_info *oinfo)
+{
+ int rc;
+
+ EXP_CHECK_DT_OP(exp, getattr);
+ EXP_COUNTER_INCREMENT(exp, getattr);
+
+ rc = OBP(exp->exp_obd, getattr)(env, exp, oinfo);
+ return rc;
+}
+
+static inline int obd_getattr_async(struct obd_export *exp,
+ struct obd_info *oinfo,
+ struct ptlrpc_request_set *set)
+{
+ int rc;
+
+ EXP_CHECK_DT_OP(exp, getattr_async);
+ EXP_COUNTER_INCREMENT(exp, getattr_async);
+
+ rc = OBP(exp->exp_obd, getattr_async)(exp, oinfo, set);
+ return rc;
+}
+
+static inline int obd_setattr(const struct lu_env *env, struct obd_export *exp,
+ struct obd_info *oinfo,
+ struct obd_trans_info *oti)
+{
+ int rc;
+
+ EXP_CHECK_DT_OP(exp, setattr);
+ EXP_COUNTER_INCREMENT(exp, setattr);
+
+ rc = OBP(exp->exp_obd, setattr)(env, exp, oinfo, oti);
+ return rc;
+}
+
+/* This performs all the requests set init/wait/destroy actions. */
+static inline int obd_setattr_rqset(struct obd_export *exp,
+ struct obd_info *oinfo,
+ struct obd_trans_info *oti)
+{
+ struct ptlrpc_request_set *set = NULL;
+ int rc;
+
+ EXP_CHECK_DT_OP(exp, setattr_async);
+ EXP_COUNTER_INCREMENT(exp, setattr_async);
+
+ set = ptlrpc_prep_set();
+ if (set == NULL)
+ return -ENOMEM;
+
+ rc = OBP(exp->exp_obd, setattr_async)(exp, oinfo, oti, set);
+ if (rc == 0)
+ rc = ptlrpc_set_wait(set);
+ ptlrpc_set_destroy(set);
+ return rc;
+}
+
+/* This adds all the requests into @set if @set != NULL, otherwise
+ all requests are sent asynchronously without waiting for response. */
+static inline int obd_setattr_async(struct obd_export *exp,
+ struct obd_info *oinfo,
+ struct obd_trans_info *oti,
+ struct ptlrpc_request_set *set)
+{
+ int rc;
+
+ EXP_CHECK_DT_OP(exp, setattr_async);
+ EXP_COUNTER_INCREMENT(exp, setattr_async);
+
+ rc = OBP(exp->exp_obd, setattr_async)(exp, oinfo, oti, set);
+ return rc;
+}
+
+static inline int obd_add_conn(struct obd_import *imp, struct obd_uuid *uuid,
+ int priority)
+{
+ struct obd_device *obd = imp->imp_obd;
+ int rc;
+
+ rc = obd_check_dev_active(obd);
+ if (rc)
+ return rc;
+ OBD_CHECK_DT_OP(obd, add_conn, -EOPNOTSUPP);
+ OBD_COUNTER_INCREMENT(obd, add_conn);
+
+ rc = OBP(obd, add_conn)(imp, uuid, priority);
+ return rc;
+}
+
+static inline int obd_del_conn(struct obd_import *imp, struct obd_uuid *uuid)
+{
+ struct obd_device *obd = imp->imp_obd;
+ int rc;
+
+ rc = obd_check_dev_active(obd);
+ if (rc)
+ return rc;
+ OBD_CHECK_DT_OP(obd, del_conn, -EOPNOTSUPP);
+ OBD_COUNTER_INCREMENT(obd, del_conn);
+
+ rc = OBP(obd, del_conn)(imp, uuid);
+ return rc;
+}
+
+static inline struct obd_uuid *obd_get_uuid(struct obd_export *exp)
+{
+ struct obd_uuid *uuid;
+
+ OBD_CHECK_DT_OP(exp->exp_obd, get_uuid, NULL);
+ EXP_COUNTER_INCREMENT(exp, get_uuid);
+
+ uuid = OBP(exp->exp_obd, get_uuid)(exp);
+ return uuid;
+}
+
+/** Create a new /a exp on device /a obd for the uuid /a cluuid
+ * @param exp New export handle
+ * @param d Connect data, supported flags are set, flags also understood
+ * by obd are returned.
+ */
+static inline int obd_connect(const struct lu_env *env,
+ struct obd_export **exp, struct obd_device *obd,
+ struct obd_uuid *cluuid,
+ struct obd_connect_data *data,
+ void *localdata)
+{
+ int rc;
+ __u64 ocf = data ? data->ocd_connect_flags : 0; /* for post-condition
+ * check */
+
+ rc = obd_check_dev_active(obd);
+ if (rc)
+ return rc;
+ OBD_CHECK_DT_OP(obd, connect, -EOPNOTSUPP);
+ OBD_COUNTER_INCREMENT(obd, connect);
+
+ rc = OBP(obd, connect)(env, exp, obd, cluuid, data, localdata);
+ /* check that only subset is granted */
+ LASSERT(ergo(data != NULL, (data->ocd_connect_flags & ocf) ==
+ data->ocd_connect_flags));
+ return rc;
+}
+
+static inline int obd_reconnect(const struct lu_env *env,
+ struct obd_export *exp,
+ struct obd_device *obd,
+ struct obd_uuid *cluuid,
+ struct obd_connect_data *d,
+ void *localdata)
+{
+ int rc;
+ __u64 ocf = d ? d->ocd_connect_flags : 0; /* for post-condition
+ * check */
+
+ rc = obd_check_dev_active(obd);
+ if (rc)
+ return rc;
+ OBD_CHECK_DT_OP(obd, reconnect, 0);
+ OBD_COUNTER_INCREMENT(obd, reconnect);
+
+ rc = OBP(obd, reconnect)(env, exp, obd, cluuid, d, localdata);
+ /* check that only subset is granted */
+ LASSERT(ergo(d != NULL,
+ (d->ocd_connect_flags & ocf) == d->ocd_connect_flags));
+ return rc;
+}
+
+static inline int obd_disconnect(struct obd_export *exp)
+{
+ int rc;
+
+ EXP_CHECK_DT_OP(exp, disconnect);
+ EXP_COUNTER_INCREMENT(exp, disconnect);
+
+ rc = OBP(exp->exp_obd, disconnect)(exp);
+ return rc;
+}
+
+static inline int obd_fid_init(struct obd_device *obd, struct obd_export *exp,
+ enum lu_cli_type type)
+{
+ int rc;
+
+ OBD_CHECK_DT_OP(obd, fid_init, 0);
+ OBD_COUNTER_INCREMENT(obd, fid_init);
+
+ rc = OBP(obd, fid_init)(obd, exp, type);
+ return rc;
+}
+
+static inline int obd_fid_fini(struct obd_device *obd)
+{
+ int rc;
+
+ OBD_CHECK_DT_OP(obd, fid_fini, 0);
+ OBD_COUNTER_INCREMENT(obd, fid_fini);
+
+ rc = OBP(obd, fid_fini)(obd);
+ return rc;
+}
+
+static inline int obd_fid_alloc(struct obd_export *exp,
+ struct lu_fid *fid,
+ struct md_op_data *op_data)
+{
+ int rc;
+
+ EXP_CHECK_DT_OP(exp, fid_alloc);
+ EXP_COUNTER_INCREMENT(exp, fid_alloc);
+
+ rc = OBP(exp->exp_obd, fid_alloc)(exp, fid, op_data);
+ return rc;
+}
+
+static inline int obd_pool_new(struct obd_device *obd, char *poolname)
+{
+ int rc;
+
+ OBD_CHECK_DT_OP(obd, pool_new, -EOPNOTSUPP);
+ OBD_COUNTER_INCREMENT(obd, pool_new);
+
+ rc = OBP(obd, pool_new)(obd, poolname);
+ return rc;
+}
+
+static inline int obd_pool_del(struct obd_device *obd, char *poolname)
+{
+ int rc;
+
+ OBD_CHECK_DT_OP(obd, pool_del, -EOPNOTSUPP);
+ OBD_COUNTER_INCREMENT(obd, pool_del);
+
+ rc = OBP(obd, pool_del)(obd, poolname);
+ return rc;
+}
+
+static inline int obd_pool_add(struct obd_device *obd, char *poolname, char *ostname)
+{
+ int rc;
+
+ OBD_CHECK_DT_OP(obd, pool_add, -EOPNOTSUPP);
+ OBD_COUNTER_INCREMENT(obd, pool_add);
+
+ rc = OBP(obd, pool_add)(obd, poolname, ostname);
+ return rc;
+}
+
+static inline int obd_pool_rem(struct obd_device *obd, char *poolname, char *ostname)
+{
+ int rc;
+
+ OBD_CHECK_DT_OP(obd, pool_rem, -EOPNOTSUPP);
+ OBD_COUNTER_INCREMENT(obd, pool_rem);
+
+ rc = OBP(obd, pool_rem)(obd, poolname, ostname);
+ return rc;
+}
+
+static inline void obd_getref(struct obd_device *obd)
+{
+ if (OBT(obd) && OBP(obd, getref)) {
+ OBD_COUNTER_INCREMENT(obd, getref);
+ OBP(obd, getref)(obd);
+ }
+}
+
+static inline void obd_putref(struct obd_device *obd)
+{
+ if (OBT(obd) && OBP(obd, putref)) {
+ OBD_COUNTER_INCREMENT(obd, putref);
+ OBP(obd, putref)(obd);
+ }
+}
+
+static inline int obd_init_export(struct obd_export *exp)
+{
+ int rc = 0;
+
+ if ((exp)->exp_obd != NULL && OBT((exp)->exp_obd) &&
+ OBP((exp)->exp_obd, init_export))
+ rc = OBP(exp->exp_obd, init_export)(exp);
+ return rc;
+}
+
+static inline int obd_destroy_export(struct obd_export *exp)
+{
+ if ((exp)->exp_obd != NULL && OBT((exp)->exp_obd) &&
+ OBP((exp)->exp_obd, destroy_export))
+ OBP(exp->exp_obd, destroy_export)(exp);
+ return 0;
+}
+
+/* @max_age is the oldest time in jiffies that we accept using a cached data.
+ * If the cache is older than @max_age we will get a new value from the
+ * target. Use a value of "cfs_time_current() + HZ" to guarantee freshness. */
+static inline int obd_statfs_async(struct obd_export *exp,
+ struct obd_info *oinfo,
+ __u64 max_age,
+ struct ptlrpc_request_set *rqset)
+{
+ int rc = 0;
+ struct obd_device *obd;
+
+ if (exp == NULL || exp->exp_obd == NULL)
+ return -EINVAL;
+
+ obd = exp->exp_obd;
+ OBD_CHECK_DT_OP(obd, statfs, -EOPNOTSUPP);
+ OBD_COUNTER_INCREMENT(obd, statfs);
+
+ CDEBUG(D_SUPER, "%s: osfs %p age %llu, max_age %llu\n",
+ obd->obd_name, &obd->obd_osfs, obd->obd_osfs_age, max_age);
+ if (cfs_time_before_64(obd->obd_osfs_age, max_age)) {
+ rc = OBP(obd, statfs_async)(exp, oinfo, max_age, rqset);
+ } else {
+ CDEBUG(D_SUPER,
+ "%s: use %p cache blocks %llu/%llu objects %llu/%llu\n",
+ obd->obd_name, &obd->obd_osfs,
+ obd->obd_osfs.os_bavail, obd->obd_osfs.os_blocks,
+ obd->obd_osfs.os_ffree, obd->obd_osfs.os_files);
+ spin_lock(&obd->obd_osfs_lock);
+ memcpy(oinfo->oi_osfs, &obd->obd_osfs, sizeof(*oinfo->oi_osfs));
+ spin_unlock(&obd->obd_osfs_lock);
+ oinfo->oi_flags |= OBD_STATFS_FROM_CACHE;
+ if (oinfo->oi_cb_up)
+ oinfo->oi_cb_up(oinfo, 0);
+ }
+ return rc;
+}
+
+static inline int obd_statfs_rqset(struct obd_export *exp,
+ struct obd_statfs *osfs, __u64 max_age,
+ __u32 flags)
+{
+ struct ptlrpc_request_set *set = NULL;
+ struct obd_info oinfo = { { { 0 } } };
+ int rc = 0;
+
+ set = ptlrpc_prep_set();
+ if (set == NULL)
+ return -ENOMEM;
+
+ oinfo.oi_osfs = osfs;
+ oinfo.oi_flags = flags;
+ rc = obd_statfs_async(exp, &oinfo, max_age, set);
+ if (rc == 0)
+ rc = ptlrpc_set_wait(set);
+ ptlrpc_set_destroy(set);
+ return rc;
+}
+
+/* @max_age is the oldest time in jiffies that we accept using a cached data.
+ * If the cache is older than @max_age we will get a new value from the
+ * target. Use a value of "cfs_time_current() + HZ" to guarantee freshness. */
+static inline int obd_statfs(const struct lu_env *env, struct obd_export *exp,
+ struct obd_statfs *osfs, __u64 max_age,
+ __u32 flags)
+{
+ int rc = 0;
+ struct obd_device *obd = exp->exp_obd;
+
+ if (obd == NULL)
+ return -EINVAL;
+
+ OBD_CHECK_DT_OP(obd, statfs, -EOPNOTSUPP);
+ OBD_COUNTER_INCREMENT(obd, statfs);
+
+ CDEBUG(D_SUPER, "osfs %llu, max_age %llu\n",
+ obd->obd_osfs_age, max_age);
+ if (cfs_time_before_64(obd->obd_osfs_age, max_age)) {
+ rc = OBP(obd, statfs)(env, exp, osfs, max_age, flags);
+ if (rc == 0) {
+ spin_lock(&obd->obd_osfs_lock);
+ memcpy(&obd->obd_osfs, osfs, sizeof(obd->obd_osfs));
+ obd->obd_osfs_age = cfs_time_current_64();
+ spin_unlock(&obd->obd_osfs_lock);
+ }
+ } else {
+ CDEBUG(D_SUPER, "%s: use %p cache blocks %llu/%llu objects %llu/%llu\n",
+ obd->obd_name, &obd->obd_osfs,
+ obd->obd_osfs.os_bavail, obd->obd_osfs.os_blocks,
+ obd->obd_osfs.os_ffree, obd->obd_osfs.os_files);
+ spin_lock(&obd->obd_osfs_lock);
+ memcpy(osfs, &obd->obd_osfs, sizeof(*osfs));
+ spin_unlock(&obd->obd_osfs_lock);
+ }
+ return rc;
+}
+
+static inline int obd_preprw(const struct lu_env *env, int cmd,
+ struct obd_export *exp, struct obdo *oa,
+ int objcount, struct obd_ioobj *obj,
+ struct niobuf_remote *remote, int *pages,
+ struct niobuf_local *local,
+ struct obd_trans_info *oti,
+ struct lustre_capa *capa)
+{
+ int rc;
+
+ EXP_CHECK_DT_OP(exp, preprw);
+ EXP_COUNTER_INCREMENT(exp, preprw);
+
+ rc = OBP(exp->exp_obd, preprw)(env, cmd, exp, oa, objcount, obj, remote,
+ pages, local, oti, capa);
+ return rc;
+}
+
+static inline int obd_commitrw(const struct lu_env *env, int cmd,
+ struct obd_export *exp, struct obdo *oa,
+ int objcount, struct obd_ioobj *obj,
+ struct niobuf_remote *rnb, int pages,
+ struct niobuf_local *local,
+ struct obd_trans_info *oti, int rc)
+{
+ EXP_CHECK_DT_OP(exp, commitrw);
+ EXP_COUNTER_INCREMENT(exp, commitrw);
+
+ rc = OBP(exp->exp_obd, commitrw)(env, cmd, exp, oa, objcount, obj,
+ rnb, pages, local, oti, rc);
+ return rc;
+}
+
+static inline int obd_adjust_kms(struct obd_export *exp,
+ struct lov_stripe_md *lsm, u64 size,
+ int shrink)
+{
+ int rc;
+
+ EXP_CHECK_DT_OP(exp, adjust_kms);
+ EXP_COUNTER_INCREMENT(exp, adjust_kms);
+
+ rc = OBP(exp->exp_obd, adjust_kms)(exp, lsm, size, shrink);
+ return rc;
+}
+
+static inline int obd_iocontrol(unsigned int cmd, struct obd_export *exp,
+ int len, void *karg, void *uarg)
+{
+ int rc;
+
+ EXP_CHECK_DT_OP(exp, iocontrol);
+ EXP_COUNTER_INCREMENT(exp, iocontrol);
+
+ rc = OBP(exp->exp_obd, iocontrol)(cmd, exp, len, karg, uarg);
+ return rc;
+}
+
+static inline int obd_find_cbdata(struct obd_export *exp,
+ struct lov_stripe_md *lsm,
+ ldlm_iterator_t it, void *data)
+{
+ int rc;
+
+ EXP_CHECK_DT_OP(exp, find_cbdata);
+ EXP_COUNTER_INCREMENT(exp, find_cbdata);
+
+ rc = OBP(exp->exp_obd, find_cbdata)(exp, lsm, it, data);
+ return rc;
+}
+
+static inline void obd_import_event(struct obd_device *obd,
+ struct obd_import *imp,
+ enum obd_import_event event)
+{
+ if (!obd) {
+ CERROR("NULL device\n");
+ return;
+ }
+ if (obd->obd_set_up && OBP(obd, import_event)) {
+ OBD_COUNTER_INCREMENT(obd, import_event);
+ OBP(obd, import_event)(obd, imp, event);
+ }
+}
+
+static inline int obd_notify(struct obd_device *obd,
+ struct obd_device *watched,
+ enum obd_notify_event ev,
+ void *data)
+{
+ int rc;
+
+ rc = obd_check_dev(obd);
+ if (rc)
+ return rc;
+
+ /* the check for async_recov is a complete hack - I'm hereby
+ overloading the meaning to also mean "this was called from
+ mds_postsetup". I know that my mds is able to handle notifies
+ by this point, and it needs to get them to execute mds_postrecov. */
+ if (!obd->obd_set_up && !obd->obd_async_recov) {
+ CDEBUG(D_HA, "obd %s not set up\n", obd->obd_name);
+ return -EINVAL;
+ }
+
+ if (!OBP(obd, notify)) {
+ CDEBUG(D_HA, "obd %s has no notify handler\n", obd->obd_name);
+ return -ENOSYS;
+ }
+
+ OBD_COUNTER_INCREMENT(obd, notify);
+ rc = OBP(obd, notify)(obd, watched, ev, data);
+ return rc;
+}
+
+static inline int obd_notify_observer(struct obd_device *observer,
+ struct obd_device *observed,
+ enum obd_notify_event ev,
+ void *data)
+{
+ int rc1;
+ int rc2;
+
+ struct obd_notify_upcall *onu;
+
+ if (observer->obd_observer)
+ rc1 = obd_notify(observer->obd_observer, observed, ev, data);
+ else
+ rc1 = 0;
+ /*
+ * Also, call non-obd listener, if any
+ */
+ onu = &observer->obd_upcall;
+ if (onu->onu_upcall != NULL)
+ rc2 = onu->onu_upcall(observer, observed, ev,
+ onu->onu_owner, NULL);
+ else
+ rc2 = 0;
+
+ return rc1 ? rc1 : rc2;
+}
+
+static inline int obd_quotacheck(struct obd_export *exp,
+ struct obd_quotactl *oqctl)
+{
+ int rc;
+
+ EXP_CHECK_DT_OP(exp, quotacheck);
+ EXP_COUNTER_INCREMENT(exp, quotacheck);
+
+ rc = OBP(exp->exp_obd, quotacheck)(exp->exp_obd, exp, oqctl);
+ return rc;
+}
+
+static inline int obd_quotactl(struct obd_export *exp,
+ struct obd_quotactl *oqctl)
+{
+ int rc;
+
+ EXP_CHECK_DT_OP(exp, quotactl);
+ EXP_COUNTER_INCREMENT(exp, quotactl);
+
+ rc = OBP(exp->exp_obd, quotactl)(exp->exp_obd, exp, oqctl);
+ return rc;
+}
+
+static inline int obd_health_check(const struct lu_env *env,
+ struct obd_device *obd)
+{
+ /* returns: 0 on healthy
+ * >0 on unhealthy + reason code/flag
+ * however the only supported reason == 1 right now
+ * We'll need to define some better reasons
+ * or flags in the future.
+ * <0 on error
+ */
+ int rc;
+
+ /* don't use EXP_CHECK_DT_OP, because NULL method is normal here */
+ if (obd == NULL || !OBT(obd)) {
+ CERROR("cleaned up obd\n");
+ return -EOPNOTSUPP;
+ }
+ if (!obd->obd_set_up || obd->obd_stopping)
+ return 0;
+ if (!OBP(obd, health_check))
+ return 0;
+
+ rc = OBP(obd, health_check)(env, obd);
+ return rc;
+}
+
+static inline int obd_register_observer(struct obd_device *obd,
+ struct obd_device *observer)
+{
+ int rc;
+
+ rc = obd_check_dev(obd);
+ if (rc)
+ return rc;
+ down_write(&obd->obd_observer_link_sem);
+ if (obd->obd_observer && observer) {
+ up_write(&obd->obd_observer_link_sem);
+ return -EALREADY;
+ }
+ obd->obd_observer = observer;
+ up_write(&obd->obd_observer_link_sem);
+ return 0;
+}
+
+#if 0
+static inline int obd_register_page_removal_cb(struct obd_export *exp,
+ obd_page_removal_cb_t cb,
+ obd_pin_extent_cb pin_cb)
+{
+ int rc;
+
+ OBD_CHECK_DT_OP(exp->exp_obd, register_page_removal_cb, 0);
+ OBD_COUNTER_INCREMENT(exp->exp_obd, register_page_removal_cb);
+
+ rc = OBP(exp->exp_obd, register_page_removal_cb)(exp, cb, pin_cb);
+ return rc;
+}
+
+static inline int obd_unregister_page_removal_cb(struct obd_export *exp,
+ obd_page_removal_cb_t cb)
+{
+ int rc;
+
+ OBD_CHECK_DT_OP(exp->exp_obd, unregister_page_removal_cb, 0);
+ OBD_COUNTER_INCREMENT(exp->exp_obd, unregister_page_removal_cb);
+
+ rc = OBP(exp->exp_obd, unregister_page_removal_cb)(exp, cb);
+ return rc;
+}
+
+static inline int obd_register_lock_cancel_cb(struct obd_export *exp,
+ obd_lock_cancel_cb cb)
+{
+ int rc;
+
+ OBD_CHECK_DT_OP(exp->exp_obd, register_lock_cancel_cb, 0);
+ OBD_COUNTER_INCREMENT(exp->exp_obd, register_lock_cancel_cb);
+
+ rc = OBP(exp->exp_obd, register_lock_cancel_cb)(exp, cb);
+ return rc;
+}
+
+static inline int obd_unregister_lock_cancel_cb(struct obd_export *exp,
+ obd_lock_cancel_cb cb)
+{
+ int rc;
+
+ OBD_CHECK_DT_OP(exp->exp_obd, unregister_lock_cancel_cb, 0);
+ OBD_COUNTER_INCREMENT(exp->exp_obd, unregister_lock_cancel_cb);
+
+ rc = OBP(exp->exp_obd, unregister_lock_cancel_cb)(exp, cb);
+ return rc;
+}
+#endif
+
+/* metadata helpers */
+static inline int md_getstatus(struct obd_export *exp,
+ struct lu_fid *fid, struct obd_capa **pc)
+{
+ int rc;
+
+ EXP_CHECK_MD_OP(exp, getstatus);
+ EXP_MD_COUNTER_INCREMENT(exp, getstatus);
+ rc = MDP(exp->exp_obd, getstatus)(exp, fid, pc);
+ return rc;
+}
+
+static inline int md_getattr(struct obd_export *exp, struct md_op_data *op_data,
+ struct ptlrpc_request **request)
+{
+ int rc;
+
+ EXP_CHECK_MD_OP(exp, getattr);
+ EXP_MD_COUNTER_INCREMENT(exp, getattr);
+ rc = MDP(exp->exp_obd, getattr)(exp, op_data, request);
+ return rc;
+}
+
+static inline int md_null_inode(struct obd_export *exp,
+ const struct lu_fid *fid)
+{
+ int rc;
+
+ EXP_CHECK_MD_OP(exp, null_inode);
+ EXP_MD_COUNTER_INCREMENT(exp, null_inode);
+ rc = MDP(exp->exp_obd, null_inode)(exp, fid);
+ return rc;
+}
+
+static inline int md_find_cbdata(struct obd_export *exp,
+ const struct lu_fid *fid,
+ ldlm_iterator_t it, void *data)
+{
+ int rc;
+
+ EXP_CHECK_MD_OP(exp, find_cbdata);
+ EXP_MD_COUNTER_INCREMENT(exp, find_cbdata);
+ rc = MDP(exp->exp_obd, find_cbdata)(exp, fid, it, data);
+ return rc;
+}
+
+static inline int md_close(struct obd_export *exp, struct md_op_data *op_data,
+ struct md_open_data *mod,
+ struct ptlrpc_request **request)
+{
+ int rc;
+
+ EXP_CHECK_MD_OP(exp, close);
+ EXP_MD_COUNTER_INCREMENT(exp, close);
+ rc = MDP(exp->exp_obd, close)(exp, op_data, mod, request);
+ return rc;
+}
+
+static inline int md_create(struct obd_export *exp, struct md_op_data *op_data,
+ const void *data, int datalen, int mode, __u32 uid,
+ __u32 gid, cfs_cap_t cap_effective, __u64 rdev,
+ struct ptlrpc_request **request)
+{
+ int rc;
+
+ EXP_CHECK_MD_OP(exp, create);
+ EXP_MD_COUNTER_INCREMENT(exp, create);
+ rc = MDP(exp->exp_obd, create)(exp, op_data, data, datalen, mode,
+ uid, gid, cap_effective, rdev, request);
+ return rc;
+}
+
+static inline int md_done_writing(struct obd_export *exp,
+ struct md_op_data *op_data,
+ struct md_open_data *mod)
+{
+ int rc;
+
+ EXP_CHECK_MD_OP(exp, done_writing);
+ EXP_MD_COUNTER_INCREMENT(exp, done_writing);
+ rc = MDP(exp->exp_obd, done_writing)(exp, op_data, mod);
+ return rc;
+}
+
+static inline int md_enqueue(struct obd_export *exp,
+ struct ldlm_enqueue_info *einfo,
+ struct lookup_intent *it,
+ struct md_op_data *op_data,
+ struct lustre_handle *lockh,
+ void *lmm, int lmmsize,
+ struct ptlrpc_request **req,
+ __u64 extra_lock_flags)
+{
+ int rc;
+
+ EXP_CHECK_MD_OP(exp, enqueue);
+ EXP_MD_COUNTER_INCREMENT(exp, enqueue);
+ rc = MDP(exp->exp_obd, enqueue)(exp, einfo, it, op_data, lockh,
+ lmm, lmmsize, req, extra_lock_flags);
+ return rc;
+}
+
+static inline int md_getattr_name(struct obd_export *exp,
+ struct md_op_data *op_data,
+ struct ptlrpc_request **request)
+{
+ int rc;
+
+ EXP_CHECK_MD_OP(exp, getattr_name);
+ EXP_MD_COUNTER_INCREMENT(exp, getattr_name);
+ rc = MDP(exp->exp_obd, getattr_name)(exp, op_data, request);
+ return rc;
+}
+
+static inline int md_intent_lock(struct obd_export *exp,
+ struct md_op_data *op_data, void *lmm,
+ int lmmsize, struct lookup_intent *it,
+ int lookup_flags, struct ptlrpc_request **reqp,
+ ldlm_blocking_callback cb_blocking,
+ __u64 extra_lock_flags)
+{
+ int rc;
+
+ EXP_CHECK_MD_OP(exp, intent_lock);
+ EXP_MD_COUNTER_INCREMENT(exp, intent_lock);
+ rc = MDP(exp->exp_obd, intent_lock)(exp, op_data, lmm, lmmsize,
+ it, lookup_flags, reqp, cb_blocking,
+ extra_lock_flags);
+ return rc;
+}
+
+static inline int md_link(struct obd_export *exp, struct md_op_data *op_data,
+ struct ptlrpc_request **request)
+{
+ int rc;
+
+ EXP_CHECK_MD_OP(exp, link);
+ EXP_MD_COUNTER_INCREMENT(exp, link);
+ rc = MDP(exp->exp_obd, link)(exp, op_data, request);
+ return rc;
+}
+
+static inline int md_rename(struct obd_export *exp, struct md_op_data *op_data,
+ const char *old, int oldlen, const char *new,
+ int newlen, struct ptlrpc_request **request)
+{
+ int rc;
+
+ EXP_CHECK_MD_OP(exp, rename);
+ EXP_MD_COUNTER_INCREMENT(exp, rename);
+ rc = MDP(exp->exp_obd, rename)(exp, op_data, old, oldlen, new,
+ newlen, request);
+ return rc;
+}
+
+static inline int md_is_subdir(struct obd_export *exp,
+ const struct lu_fid *pfid,
+ const struct lu_fid *cfid,
+ struct ptlrpc_request **request)
+{
+ int rc;
+
+ EXP_CHECK_MD_OP(exp, is_subdir);
+ EXP_MD_COUNTER_INCREMENT(exp, is_subdir);
+ rc = MDP(exp->exp_obd, is_subdir)(exp, pfid, cfid, request);
+ return rc;
+}
+
+static inline int md_setattr(struct obd_export *exp, struct md_op_data *op_data,
+ void *ea, int ealen, void *ea2, int ea2len,
+ struct ptlrpc_request **request,
+ struct md_open_data **mod)
+{
+ int rc;
+
+ EXP_CHECK_MD_OP(exp, setattr);
+ EXP_MD_COUNTER_INCREMENT(exp, setattr);
+ rc = MDP(exp->exp_obd, setattr)(exp, op_data, ea, ealen,
+ ea2, ea2len, request, mod);
+ return rc;
+}
+
+static inline int md_sync(struct obd_export *exp, const struct lu_fid *fid,
+ struct obd_capa *oc, struct ptlrpc_request **request)
+{
+ int rc;
+
+ EXP_CHECK_MD_OP(exp, sync);
+ EXP_MD_COUNTER_INCREMENT(exp, sync);
+ rc = MDP(exp->exp_obd, sync)(exp, fid, oc, request);
+ return rc;
+}
+
+static inline int md_readpage(struct obd_export *exp, struct md_op_data *opdata,
+ struct page **pages,
+ struct ptlrpc_request **request)
+{
+ int rc;
+
+ EXP_CHECK_MD_OP(exp, readpage);
+ EXP_MD_COUNTER_INCREMENT(exp, readpage);
+ rc = MDP(exp->exp_obd, readpage)(exp, opdata, pages, request);
+ return rc;
+}
+
+static inline int md_unlink(struct obd_export *exp, struct md_op_data *op_data,
+ struct ptlrpc_request **request)
+{
+ int rc;
+
+ EXP_CHECK_MD_OP(exp, unlink);
+ EXP_MD_COUNTER_INCREMENT(exp, unlink);
+ rc = MDP(exp->exp_obd, unlink)(exp, op_data, request);
+ return rc;
+}
+
+static inline int md_get_lustre_md(struct obd_export *exp,
+ struct ptlrpc_request *req,
+ struct obd_export *dt_exp,
+ struct obd_export *md_exp,
+ struct lustre_md *md)
+{
+ EXP_CHECK_MD_OP(exp, get_lustre_md);
+ EXP_MD_COUNTER_INCREMENT(exp, get_lustre_md);
+ return MDP(exp->exp_obd, get_lustre_md)(exp, req, dt_exp, md_exp, md);
+}
+
+static inline int md_free_lustre_md(struct obd_export *exp,
+ struct lustre_md *md)
+{
+ EXP_CHECK_MD_OP(exp, free_lustre_md);
+ EXP_MD_COUNTER_INCREMENT(exp, free_lustre_md);
+ return MDP(exp->exp_obd, free_lustre_md)(exp, md);
+}
+
+static inline int md_setxattr(struct obd_export *exp,
+ const struct lu_fid *fid, struct obd_capa *oc,
+ u64 valid, const char *name,
+ const char *input, int input_size,
+ int output_size, int flags, __u32 suppgid,
+ struct ptlrpc_request **request)
+{
+ EXP_CHECK_MD_OP(exp, setxattr);
+ EXP_MD_COUNTER_INCREMENT(exp, setxattr);
+ return MDP(exp->exp_obd, setxattr)(exp, fid, oc, valid, name, input,
+ input_size, output_size, flags,
+ suppgid, request);
+}
+
+static inline int md_getxattr(struct obd_export *exp,
+ const struct lu_fid *fid, struct obd_capa *oc,
+ u64 valid, const char *name,
+ const char *input, int input_size,
+ int output_size, int flags,
+ struct ptlrpc_request **request)
+{
+ EXP_CHECK_MD_OP(exp, getxattr);
+ EXP_MD_COUNTER_INCREMENT(exp, getxattr);
+ return MDP(exp->exp_obd, getxattr)(exp, fid, oc, valid, name, input,
+ input_size, output_size, flags,
+ request);
+}
+
+static inline int md_set_open_replay_data(struct obd_export *exp,
+ struct obd_client_handle *och,
+ struct lookup_intent *it)
+{
+ EXP_CHECK_MD_OP(exp, set_open_replay_data);
+ EXP_MD_COUNTER_INCREMENT(exp, set_open_replay_data);
+ return MDP(exp->exp_obd, set_open_replay_data)(exp, och, it);
+}
+
+static inline int md_clear_open_replay_data(struct obd_export *exp,
+ struct obd_client_handle *och)
+{
+ EXP_CHECK_MD_OP(exp, clear_open_replay_data);
+ EXP_MD_COUNTER_INCREMENT(exp, clear_open_replay_data);
+ return MDP(exp->exp_obd, clear_open_replay_data)(exp, och);
+}
+
+static inline int md_set_lock_data(struct obd_export *exp,
+ __u64 *lockh, void *data, __u64 *bits)
+{
+ EXP_CHECK_MD_OP(exp, set_lock_data);
+ EXP_MD_COUNTER_INCREMENT(exp, set_lock_data);
+ return MDP(exp->exp_obd, set_lock_data)(exp, lockh, data, bits);
+}
+
+static inline int md_cancel_unused(struct obd_export *exp,
+ const struct lu_fid *fid,
+ ldlm_policy_data_t *policy,
+ ldlm_mode_t mode,
+ ldlm_cancel_flags_t flags,
+ void *opaque)
+{
+ int rc;
+
+ EXP_CHECK_MD_OP(exp, cancel_unused);
+ EXP_MD_COUNTER_INCREMENT(exp, cancel_unused);
+
+ rc = MDP(exp->exp_obd, cancel_unused)(exp, fid, policy, mode,
+ flags, opaque);
+ return rc;
+}
+
+static inline ldlm_mode_t md_lock_match(struct obd_export *exp, __u64 flags,
+ const struct lu_fid *fid,
+ ldlm_type_t type,
+ ldlm_policy_data_t *policy,
+ ldlm_mode_t mode,
+ struct lustre_handle *lockh)
+{
+ EXP_CHECK_MD_OP(exp, lock_match);
+ EXP_MD_COUNTER_INCREMENT(exp, lock_match);
+ return MDP(exp->exp_obd, lock_match)(exp, flags, fid, type,
+ policy, mode, lockh);
+}
+
+static inline int md_init_ea_size(struct obd_export *exp, int easize,
+ int def_asize, int cookiesize,
+ int def_cookiesize)
+{
+ EXP_CHECK_MD_OP(exp, init_ea_size);
+ EXP_MD_COUNTER_INCREMENT(exp, init_ea_size);
+ return MDP(exp->exp_obd, init_ea_size)(exp, easize, def_asize,
+ cookiesize, def_cookiesize);
+}
+
+static inline int md_get_remote_perm(struct obd_export *exp,
+ const struct lu_fid *fid,
+ struct obd_capa *oc, __u32 suppgid,
+ struct ptlrpc_request **request)
+{
+ EXP_CHECK_MD_OP(exp, get_remote_perm);
+ EXP_MD_COUNTER_INCREMENT(exp, get_remote_perm);
+ return MDP(exp->exp_obd, get_remote_perm)(exp, fid, oc, suppgid,
+ request);
+}
+
+static inline int md_renew_capa(struct obd_export *exp, struct obd_capa *ocapa,
+ renew_capa_cb_t cb)
+{
+ int rc;
+
+ EXP_CHECK_MD_OP(exp, renew_capa);
+ EXP_MD_COUNTER_INCREMENT(exp, renew_capa);
+ rc = MDP(exp->exp_obd, renew_capa)(exp, ocapa, cb);
+ return rc;
+}
+
+static inline int md_unpack_capa(struct obd_export *exp,
+ struct ptlrpc_request *req,
+ const struct req_msg_field *field,
+ struct obd_capa **oc)
+{
+ int rc;
+
+ EXP_CHECK_MD_OP(exp, unpack_capa);
+ EXP_MD_COUNTER_INCREMENT(exp, unpack_capa);
+ rc = MDP(exp->exp_obd, unpack_capa)(exp, req, field, oc);
+ return rc;
+}
+
+static inline int md_intent_getattr_async(struct obd_export *exp,
+ struct md_enqueue_info *minfo,
+ struct ldlm_enqueue_info *einfo)
+{
+ int rc;
+
+ EXP_CHECK_MD_OP(exp, intent_getattr_async);
+ EXP_MD_COUNTER_INCREMENT(exp, intent_getattr_async);
+ rc = MDP(exp->exp_obd, intent_getattr_async)(exp, minfo, einfo);
+ return rc;
+}
+
+static inline int md_revalidate_lock(struct obd_export *exp,
+ struct lookup_intent *it,
+ struct lu_fid *fid, __u64 *bits)
+{
+ int rc;
+
+ EXP_CHECK_MD_OP(exp, revalidate_lock);
+ EXP_MD_COUNTER_INCREMENT(exp, revalidate_lock);
+ rc = MDP(exp->exp_obd, revalidate_lock)(exp, it, fid, bits);
+ return rc;
+}
+
+
+/* OBD Metadata Support */
+
+extern int obd_init_caches(void);
+extern void obd_cleanup_caches(void);
+
+/* support routines */
+extern struct kmem_cache *obdo_cachep;
+
+#define OBDO_ALLOC(ptr) \
+do { \
+ OBD_SLAB_ALLOC_PTR_GFP((ptr), obdo_cachep, GFP_NOFS); \
+} while (0)
+
+#define OBDO_FREE(ptr) \
+do { \
+ OBD_SLAB_FREE_PTR((ptr), obdo_cachep); \
+} while (0)
+
+
+static inline void obdo2fid(struct obdo *oa, struct lu_fid *fid)
+{
+ /* something here */
+}
+
+static inline void fid2obdo(struct lu_fid *fid, struct obdo *oa)
+{
+ /* something here */
+}
+
+typedef int (*register_lwp_cb)(void *data);
+
+struct lwp_register_item {
+ struct obd_export **lri_exp;
+ register_lwp_cb lri_cb_func;
+ void *lri_cb_data;
+ struct list_head lri_list;
+ char lri_name[MTI_NAME_MAXLEN];
+};
+
+/* I'm as embarrassed about this as you are.
+ *
+ * <shaver> // XXX do not look into _superhack with remaining eye
+ * <shaver> // XXX if this were any uglier, I'd get my own show on MTV */
+extern int (*ptlrpc_put_connection_superhack)(struct ptlrpc_connection *c);
+
+/* obd_mount.c */
+
+/* sysctl.c */
+extern void obd_sysctl_init (void);
+extern void obd_sysctl_clean (void);
+
+/* uuid.c */
+typedef __u8 class_uuid_t[16];
+void class_uuid_unparse(class_uuid_t in, struct obd_uuid *out);
+
+/* lustre_peer.c */
+int lustre_uuid_to_peer(const char *uuid, lnet_nid_t *peer_nid, int index);
+int class_add_uuid(const char *uuid, __u64 nid);
+int class_del_uuid (const char *uuid);
+int class_check_uuid(struct obd_uuid *uuid, __u64 nid);
+void class_init_uuidlist(void);
+void class_exit_uuidlist(void);
+
+/* class_obd.c */
+extern char obd_jobid_node[];
+extern struct miscdevice obd_psdev;
+extern spinlock_t obd_types_lock;
+
+/* prng.c */
+#define ll_generate_random_uuid(uuid_out) cfs_get_random_bytes(uuid_out, sizeof(class_uuid_t))
+
+#endif /* __LINUX_OBD_CLASS_H */
diff --git a/drivers/staging/lustre/lustre/include/obd_support.h b/drivers/staging/lustre/lustre/include/obd_support.h
new file mode 100644
index 000000000..2991d2ee7
--- /dev/null
+++ b/drivers/staging/lustre/lustre/include/obd_support.h
@@ -0,0 +1,862 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2011, 2012, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ */
+
+#ifndef _OBD_SUPPORT
+#define _OBD_SUPPORT
+
+#include <linux/slab.h>
+#include "../../include/linux/libcfs/libcfs.h"
+#include "linux/lustre_compat25.h"
+#include "lprocfs_status.h"
+
+/* global variables */
+extern struct lprocfs_stats *obd_memory;
+enum {
+ OBD_MEMORY_STAT = 0,
+ OBD_MEMORY_PAGES_STAT = 1,
+ OBD_STATS_NUM,
+};
+
+extern unsigned int obd_debug_peer_on_timeout;
+extern unsigned int obd_dump_on_timeout;
+extern unsigned int obd_dump_on_eviction;
+/* obd_timeout should only be used for recovery, not for
+ networking / disk / timings affected by load (use Adaptive Timeouts) */
+extern unsigned int obd_timeout; /* seconds */
+extern unsigned int ldlm_timeout; /* seconds */
+extern unsigned int obd_timeout_set;
+extern unsigned int ldlm_timeout_set;
+extern unsigned int at_min;
+extern unsigned int at_max;
+extern unsigned int at_history;
+extern int at_early_margin;
+extern int at_extra;
+extern unsigned int obd_sync_filter;
+extern unsigned int obd_max_dirty_pages;
+extern atomic_t obd_dirty_pages;
+extern atomic_t obd_dirty_transit_pages;
+extern unsigned int obd_alloc_fail_rate;
+extern char obd_jobid_var[];
+
+/* lvfs.c */
+int obd_alloc_fail(const void *ptr, const char *name, const char *type,
+ size_t size, const char *file, int line);
+
+/* Some hash init argument constants */
+#define HASH_POOLS_BKT_BITS 3
+#define HASH_POOLS_CUR_BITS 3
+#define HASH_POOLS_MAX_BITS 7
+#define HASH_UUID_BKT_BITS 5
+#define HASH_UUID_CUR_BITS 7
+#define HASH_UUID_MAX_BITS 12
+#define HASH_NID_BKT_BITS 5
+#define HASH_NID_CUR_BITS 7
+#define HASH_NID_MAX_BITS 12
+#define HASH_NID_STATS_BKT_BITS 5
+#define HASH_NID_STATS_CUR_BITS 7
+#define HASH_NID_STATS_MAX_BITS 12
+#define HASH_LQE_BKT_BITS 5
+#define HASH_LQE_CUR_BITS 7
+#define HASH_LQE_MAX_BITS 12
+#define HASH_CONN_BKT_BITS 5
+#define HASH_CONN_CUR_BITS 5
+#define HASH_CONN_MAX_BITS 15
+#define HASH_EXP_LOCK_BKT_BITS 5
+#define HASH_EXP_LOCK_CUR_BITS 7
+#define HASH_EXP_LOCK_MAX_BITS 16
+#define HASH_CL_ENV_BKT_BITS 5
+#define HASH_CL_ENV_BITS 10
+#define HASH_JOB_STATS_BKT_BITS 5
+#define HASH_JOB_STATS_CUR_BITS 7
+#define HASH_JOB_STATS_MAX_BITS 12
+
+/* Timeout definitions */
+#define OBD_TIMEOUT_DEFAULT 100
+#define LDLM_TIMEOUT_DEFAULT 20
+#define MDS_LDLM_TIMEOUT_DEFAULT 6
+/* Time to wait for all clients to reconnect during recovery (hard limit) */
+#define OBD_RECOVERY_TIME_HARD (obd_timeout * 9)
+/* Time to wait for all clients to reconnect during recovery (soft limit) */
+/* Should be very conservative; must catch the first reconnect after reboot */
+#define OBD_RECOVERY_TIME_SOFT (obd_timeout * 3)
+/* Change recovery-small 26b time if you change this */
+#define PING_INTERVAL max(obd_timeout / 4, 1U)
+/* a bit more than maximal journal commit time in seconds */
+#define PING_INTERVAL_SHORT min(PING_INTERVAL, 7U)
+/* Client may skip 1 ping; we must wait at least 2.5. But for multiple
+ * failover targets the client only pings one server at a time, and pings
+ * can be lost on a loaded network. Since eviction has serious consequences,
+ * and there's no urgent need to evict a client just because it's idle, we
+ * should be very conservative here. */
+#define PING_EVICT_TIMEOUT (PING_INTERVAL * 6)
+#define DISK_TIMEOUT 50 /* Beyond this we warn about disk speed */
+#define CONNECTION_SWITCH_MIN 5U /* Connection switching rate limiter */
+ /* Max connect interval for nonresponsive servers; ~50s to avoid building up
+ connect requests in the LND queues, but within obd_timeout so we don't
+ miss the recovery window */
+#define CONNECTION_SWITCH_MAX min(50U, max(CONNECTION_SWITCH_MIN, obd_timeout))
+#define CONNECTION_SWITCH_INC 5 /* Connection timeout backoff */
+/* In general this should be low to have quick detection of a system
+ running on a backup server. (If it's too low, import_select_connection
+ will increase the timeout anyhow.) */
+#define INITIAL_CONNECT_TIMEOUT max(CONNECTION_SWITCH_MIN, obd_timeout/20)
+/* The max delay between connects is SWITCH_MAX + SWITCH_INC + INITIAL */
+#define RECONNECT_DELAY_MAX (CONNECTION_SWITCH_MAX + CONNECTION_SWITCH_INC + \
+ INITIAL_CONNECT_TIMEOUT)
+/* The min time a target should wait for clients to reconnect in recovery */
+#define OBD_RECOVERY_TIME_MIN (2*RECONNECT_DELAY_MAX)
+#define OBD_IR_FACTOR_MIN 1
+#define OBD_IR_FACTOR_MAX 10
+#define OBD_IR_FACTOR_DEFAULT (OBD_IR_FACTOR_MAX/2)
+/* default timeout for the MGS to become IR_FULL */
+#define OBD_IR_MGS_TIMEOUT (4*obd_timeout)
+#define LONG_UNLINK 300 /* Unlink should happen before now */
+
+/**
+ * Time interval of shrink, if the client is "idle" more than this interval,
+ * then the ll_grant thread will return the requested grant space to filter
+ */
+#define GRANT_SHRINK_INTERVAL 1200/*20 minutes*/
+
+#define OBD_FAIL_MDS 0x100
+#define OBD_FAIL_MDS_HANDLE_UNPACK 0x101
+#define OBD_FAIL_MDS_GETATTR_NET 0x102
+#define OBD_FAIL_MDS_GETATTR_PACK 0x103
+#define OBD_FAIL_MDS_READPAGE_NET 0x104
+#define OBD_FAIL_MDS_READPAGE_PACK 0x105
+#define OBD_FAIL_MDS_SENDPAGE 0x106
+#define OBD_FAIL_MDS_REINT_NET 0x107
+#define OBD_FAIL_MDS_REINT_UNPACK 0x108
+#define OBD_FAIL_MDS_REINT_SETATTR 0x109
+#define OBD_FAIL_MDS_REINT_SETATTR_WRITE 0x10a
+#define OBD_FAIL_MDS_REINT_CREATE 0x10b
+#define OBD_FAIL_MDS_REINT_CREATE_WRITE 0x10c
+#define OBD_FAIL_MDS_REINT_UNLINK 0x10d
+#define OBD_FAIL_MDS_REINT_UNLINK_WRITE 0x10e
+#define OBD_FAIL_MDS_REINT_LINK 0x10f
+#define OBD_FAIL_MDS_REINT_LINK_WRITE 0x110
+#define OBD_FAIL_MDS_REINT_RENAME 0x111
+#define OBD_FAIL_MDS_REINT_RENAME_WRITE 0x112
+#define OBD_FAIL_MDS_OPEN_NET 0x113
+#define OBD_FAIL_MDS_OPEN_PACK 0x114
+#define OBD_FAIL_MDS_CLOSE_NET 0x115
+#define OBD_FAIL_MDS_CLOSE_PACK 0x116
+#define OBD_FAIL_MDS_CONNECT_NET 0x117
+#define OBD_FAIL_MDS_CONNECT_PACK 0x118
+#define OBD_FAIL_MDS_REINT_NET_REP 0x119
+#define OBD_FAIL_MDS_DISCONNECT_NET 0x11a
+#define OBD_FAIL_MDS_GETSTATUS_NET 0x11b
+#define OBD_FAIL_MDS_GETSTATUS_PACK 0x11c
+#define OBD_FAIL_MDS_STATFS_PACK 0x11d
+#define OBD_FAIL_MDS_STATFS_NET 0x11e
+#define OBD_FAIL_MDS_GETATTR_NAME_NET 0x11f
+#define OBD_FAIL_MDS_PIN_NET 0x120
+#define OBD_FAIL_MDS_UNPIN_NET 0x121
+#define OBD_FAIL_MDS_ALL_REPLY_NET 0x122
+#define OBD_FAIL_MDS_ALL_REQUEST_NET 0x123
+#define OBD_FAIL_MDS_SYNC_NET 0x124
+#define OBD_FAIL_MDS_SYNC_PACK 0x125
+#define OBD_FAIL_MDS_DONE_WRITING_NET 0x126
+#define OBD_FAIL_MDS_DONE_WRITING_PACK 0x127
+#define OBD_FAIL_MDS_ALLOC_OBDO 0x128
+#define OBD_FAIL_MDS_PAUSE_OPEN 0x129
+#define OBD_FAIL_MDS_STATFS_LCW_SLEEP 0x12a
+#define OBD_FAIL_MDS_OPEN_CREATE 0x12b
+#define OBD_FAIL_MDS_OST_SETATTR 0x12c
+#define OBD_FAIL_MDS_QUOTACHECK_NET 0x12d
+#define OBD_FAIL_MDS_QUOTACTL_NET 0x12e
+#define OBD_FAIL_MDS_CLIENT_ADD 0x12f
+#define OBD_FAIL_MDS_GETXATTR_NET 0x130
+#define OBD_FAIL_MDS_GETXATTR_PACK 0x131
+#define OBD_FAIL_MDS_SETXATTR_NET 0x132
+#define OBD_FAIL_MDS_SETXATTR 0x133
+#define OBD_FAIL_MDS_SETXATTR_WRITE 0x134
+#define OBD_FAIL_MDS_FS_SETUP 0x135
+#define OBD_FAIL_MDS_RESEND 0x136
+#define OBD_FAIL_MDS_LLOG_CREATE_FAILED 0x137
+#define OBD_FAIL_MDS_LOV_SYNC_RACE 0x138
+#define OBD_FAIL_MDS_OSC_PRECREATE 0x139
+#define OBD_FAIL_MDS_LLOG_SYNC_TIMEOUT 0x13a
+#define OBD_FAIL_MDS_CLOSE_NET_REP 0x13b
+#define OBD_FAIL_MDS_BLOCK_QUOTA_REQ 0x13c
+#define OBD_FAIL_MDS_DROP_QUOTA_REQ 0x13d
+#define OBD_FAIL_MDS_REMOVE_COMMON_EA 0x13e
+#define OBD_FAIL_MDS_ALLOW_COMMON_EA_SETTING 0x13f
+#define OBD_FAIL_MDS_FAIL_LOV_LOG_ADD 0x140
+#define OBD_FAIL_MDS_LOV_PREP_CREATE 0x141
+#define OBD_FAIL_MDS_REINT_DELAY 0x142
+#define OBD_FAIL_MDS_READLINK_EPROTO 0x143
+#define OBD_FAIL_MDS_OPEN_WAIT_CREATE 0x144
+#define OBD_FAIL_MDS_PDO_LOCK 0x145
+#define OBD_FAIL_MDS_PDO_LOCK2 0x146
+#define OBD_FAIL_MDS_OSC_CREATE_FAIL 0x147
+#define OBD_FAIL_MDS_NEGATIVE_POSITIVE 0x148
+#define OBD_FAIL_MDS_HSM_STATE_GET_NET 0x149
+#define OBD_FAIL_MDS_HSM_STATE_SET_NET 0x14a
+#define OBD_FAIL_MDS_HSM_PROGRESS_NET 0x14b
+#define OBD_FAIL_MDS_HSM_REQUEST_NET 0x14c
+#define OBD_FAIL_MDS_HSM_CT_REGISTER_NET 0x14d
+#define OBD_FAIL_MDS_HSM_CT_UNREGISTER_NET 0x14e
+#define OBD_FAIL_MDS_SWAP_LAYOUTS_NET 0x14f
+#define OBD_FAIL_MDS_HSM_ACTION_NET 0x150
+#define OBD_FAIL_MDS_CHANGELOG_INIT 0x151
+
+/* layout lock */
+#define OBD_FAIL_MDS_NO_LL_GETATTR 0x170
+#define OBD_FAIL_MDS_NO_LL_OPEN 0x171
+#define OBD_FAIL_MDS_LL_BLOCK 0x172
+
+/* CMD */
+#define OBD_FAIL_MDS_IS_SUBDIR_NET 0x180
+#define OBD_FAIL_MDS_IS_SUBDIR_PACK 0x181
+#define OBD_FAIL_MDS_SET_INFO_NET 0x182
+#define OBD_FAIL_MDS_WRITEPAGE_NET 0x183
+#define OBD_FAIL_MDS_WRITEPAGE_PACK 0x184
+#define OBD_FAIL_MDS_RECOVERY_ACCEPTS_GAPS 0x185
+#define OBD_FAIL_MDS_GET_INFO_NET 0x186
+#define OBD_FAIL_MDS_DQACQ_NET 0x187
+
+/* OI scrub */
+#define OBD_FAIL_OSD_SCRUB_DELAY 0x190
+#define OBD_FAIL_OSD_SCRUB_CRASH 0x191
+#define OBD_FAIL_OSD_SCRUB_FATAL 0x192
+#define OBD_FAIL_OSD_FID_MAPPING 0x193
+#define OBD_FAIL_OSD_LMA_INCOMPAT 0x194
+#define OBD_FAIL_OSD_COMPAT_INVALID_ENTRY 0x195
+
+#define OBD_FAIL_OST 0x200
+#define OBD_FAIL_OST_CONNECT_NET 0x201
+#define OBD_FAIL_OST_DISCONNECT_NET 0x202
+#define OBD_FAIL_OST_GET_INFO_NET 0x203
+#define OBD_FAIL_OST_CREATE_NET 0x204
+#define OBD_FAIL_OST_DESTROY_NET 0x205
+#define OBD_FAIL_OST_GETATTR_NET 0x206
+#define OBD_FAIL_OST_SETATTR_NET 0x207
+#define OBD_FAIL_OST_OPEN_NET 0x208
+#define OBD_FAIL_OST_CLOSE_NET 0x209
+#define OBD_FAIL_OST_BRW_NET 0x20a
+#define OBD_FAIL_OST_PUNCH_NET 0x20b
+#define OBD_FAIL_OST_STATFS_NET 0x20c
+#define OBD_FAIL_OST_HANDLE_UNPACK 0x20d
+#define OBD_FAIL_OST_BRW_WRITE_BULK 0x20e
+#define OBD_FAIL_OST_BRW_READ_BULK 0x20f
+#define OBD_FAIL_OST_SYNC_NET 0x210
+#define OBD_FAIL_OST_ALL_REPLY_NET 0x211
+#define OBD_FAIL_OST_ALL_REQUEST_NET 0x212
+#define OBD_FAIL_OST_LDLM_REPLY_NET 0x213
+#define OBD_FAIL_OST_BRW_PAUSE_BULK 0x214
+#define OBD_FAIL_OST_ENOSPC 0x215
+#define OBD_FAIL_OST_EROFS 0x216
+#define OBD_FAIL_OST_ENOENT 0x217
+#define OBD_FAIL_OST_QUOTACHECK_NET 0x218
+#define OBD_FAIL_OST_QUOTACTL_NET 0x219
+#define OBD_FAIL_OST_CHECKSUM_RECEIVE 0x21a
+#define OBD_FAIL_OST_CHECKSUM_SEND 0x21b
+#define OBD_FAIL_OST_BRW_SIZE 0x21c
+#define OBD_FAIL_OST_DROP_REQ 0x21d
+#define OBD_FAIL_OST_SETATTR_CREDITS 0x21e
+#define OBD_FAIL_OST_HOLD_WRITE_RPC 0x21f
+#define OBD_FAIL_OST_BRW_WRITE_BULK2 0x220
+#define OBD_FAIL_OST_LLOG_RECOVERY_TIMEOUT 0x221
+#define OBD_FAIL_OST_CANCEL_COOKIE_TIMEOUT 0x222
+#define OBD_FAIL_OST_PAUSE_CREATE 0x223
+#define OBD_FAIL_OST_BRW_PAUSE_PACK 0x224
+#define OBD_FAIL_OST_CONNECT_NET2 0x225
+#define OBD_FAIL_OST_NOMEM 0x226
+#define OBD_FAIL_OST_BRW_PAUSE_BULK2 0x227
+#define OBD_FAIL_OST_MAPBLK_ENOSPC 0x228
+#define OBD_FAIL_OST_ENOINO 0x229
+#define OBD_FAIL_OST_DQACQ_NET 0x230
+#define OBD_FAIL_OST_STATFS_EINPROGRESS 0x231
+
+#define OBD_FAIL_LDLM 0x300
+#define OBD_FAIL_LDLM_NAMESPACE_NEW 0x301
+#define OBD_FAIL_LDLM_ENQUEUE_NET 0x302
+#define OBD_FAIL_LDLM_CONVERT_NET 0x303
+#define OBD_FAIL_LDLM_CANCEL_NET 0x304
+#define OBD_FAIL_LDLM_BL_CALLBACK_NET 0x305
+#define OBD_FAIL_LDLM_CP_CALLBACK_NET 0x306
+#define OBD_FAIL_LDLM_GL_CALLBACK_NET 0x307
+#define OBD_FAIL_LDLM_ENQUEUE_EXTENT_ERR 0x308
+#define OBD_FAIL_LDLM_ENQUEUE_INTENT_ERR 0x309
+#define OBD_FAIL_LDLM_CREATE_RESOURCE 0x30a
+#define OBD_FAIL_LDLM_ENQUEUE_BLOCKED 0x30b
+#define OBD_FAIL_LDLM_REPLY 0x30c
+#define OBD_FAIL_LDLM_RECOV_CLIENTS 0x30d
+#define OBD_FAIL_LDLM_ENQUEUE_OLD_EXPORT 0x30e
+#define OBD_FAIL_LDLM_GLIMPSE 0x30f
+#define OBD_FAIL_LDLM_CANCEL_RACE 0x310
+#define OBD_FAIL_LDLM_CANCEL_EVICT_RACE 0x311
+#define OBD_FAIL_LDLM_PAUSE_CANCEL 0x312
+#define OBD_FAIL_LDLM_CLOSE_THREAD 0x313
+#define OBD_FAIL_LDLM_CANCEL_BL_CB_RACE 0x314
+#define OBD_FAIL_LDLM_CP_CB_WAIT 0x315
+#define OBD_FAIL_LDLM_OST_FAIL_RACE 0x316
+#define OBD_FAIL_LDLM_INTR_CP_AST 0x317
+#define OBD_FAIL_LDLM_CP_BL_RACE 0x318
+#define OBD_FAIL_LDLM_NEW_LOCK 0x319
+#define OBD_FAIL_LDLM_AGL_DELAY 0x31a
+#define OBD_FAIL_LDLM_AGL_NOLOCK 0x31b
+#define OBD_FAIL_LDLM_OST_LVB 0x31c
+
+/* LOCKLESS IO */
+#define OBD_FAIL_LDLM_SET_CONTENTION 0x385
+
+#define OBD_FAIL_OSC 0x400
+#define OBD_FAIL_OSC_BRW_READ_BULK 0x401
+#define OBD_FAIL_OSC_BRW_WRITE_BULK 0x402
+#define OBD_FAIL_OSC_LOCK_BL_AST 0x403
+#define OBD_FAIL_OSC_LOCK_CP_AST 0x404
+#define OBD_FAIL_OSC_MATCH 0x405
+#define OBD_FAIL_OSC_BRW_PREP_REQ 0x406
+#define OBD_FAIL_OSC_SHUTDOWN 0x407
+#define OBD_FAIL_OSC_CHECKSUM_RECEIVE 0x408
+#define OBD_FAIL_OSC_CHECKSUM_SEND 0x409
+#define OBD_FAIL_OSC_BRW_PREP_REQ2 0x40a
+#define OBD_FAIL_OSC_CONNECT_CKSUM 0x40b
+#define OBD_FAIL_OSC_CKSUM_ADLER_ONLY 0x40c
+#define OBD_FAIL_OSC_DIO_PAUSE 0x40d
+#define OBD_FAIL_OSC_OBJECT_CONTENTION 0x40e
+#define OBD_FAIL_OSC_CP_CANCEL_RACE 0x40f
+#define OBD_FAIL_OSC_CP_ENQ_RACE 0x410
+#define OBD_FAIL_OSC_NO_GRANT 0x411
+#define OBD_FAIL_OSC_DELAY_SETTIME 0x412
+
+#define OBD_FAIL_PTLRPC 0x500
+#define OBD_FAIL_PTLRPC_ACK 0x501
+#define OBD_FAIL_PTLRPC_RQBD 0x502
+#define OBD_FAIL_PTLRPC_BULK_GET_NET 0x503
+#define OBD_FAIL_PTLRPC_BULK_PUT_NET 0x504
+#define OBD_FAIL_PTLRPC_DROP_RPC 0x505
+#define OBD_FAIL_PTLRPC_DELAY_SEND 0x506
+#define OBD_FAIL_PTLRPC_DELAY_RECOV 0x507
+#define OBD_FAIL_PTLRPC_CLIENT_BULK_CB 0x508
+#define OBD_FAIL_PTLRPC_PAUSE_REQ 0x50a
+#define OBD_FAIL_PTLRPC_PAUSE_REP 0x50c
+#define OBD_FAIL_PTLRPC_IMP_DEACTIVE 0x50d
+#define OBD_FAIL_PTLRPC_DUMP_LOG 0x50e
+#define OBD_FAIL_PTLRPC_LONG_REPL_UNLINK 0x50f
+#define OBD_FAIL_PTLRPC_LONG_BULK_UNLINK 0x510
+#define OBD_FAIL_PTLRPC_HPREQ_TIMEOUT 0x511
+#define OBD_FAIL_PTLRPC_HPREQ_NOTIMEOUT 0x512
+#define OBD_FAIL_PTLRPC_DROP_REQ_OPC 0x513
+#define OBD_FAIL_PTLRPC_FINISH_REPLAY 0x514
+#define OBD_FAIL_PTLRPC_CLIENT_BULK_CB2 0x515
+#define OBD_FAIL_PTLRPC_DELAY_IMP_FULL 0x516
+#define OBD_FAIL_PTLRPC_CANCEL_RESEND 0x517
+
+#define OBD_FAIL_OBD_PING_NET 0x600
+#define OBD_FAIL_OBD_LOG_CANCEL_NET 0x601
+#define OBD_FAIL_OBD_LOGD_NET 0x602
+#define OBD_FAIL_OBD_QC_CALLBACK_NET 0x603
+#define OBD_FAIL_OBD_DQACQ 0x604
+#define OBD_FAIL_OBD_LLOG_SETUP 0x605
+#define OBD_FAIL_OBD_LOG_CANCEL_REP 0x606
+#define OBD_FAIL_OBD_IDX_READ_NET 0x607
+#define OBD_FAIL_OBD_IDX_READ_BREAK 0x608
+#define OBD_FAIL_OBD_NO_LRU 0x609
+
+#define OBD_FAIL_TGT_REPLY_NET 0x700
+#define OBD_FAIL_TGT_CONN_RACE 0x701
+#define OBD_FAIL_TGT_FORCE_RECONNECT 0x702
+#define OBD_FAIL_TGT_DELAY_CONNECT 0x703
+#define OBD_FAIL_TGT_DELAY_RECONNECT 0x704
+#define OBD_FAIL_TGT_DELAY_PRECREATE 0x705
+#define OBD_FAIL_TGT_TOOMANY_THREADS 0x706
+#define OBD_FAIL_TGT_REPLAY_DROP 0x707
+#define OBD_FAIL_TGT_FAKE_EXP 0x708
+#define OBD_FAIL_TGT_REPLAY_DELAY 0x709
+#define OBD_FAIL_TGT_LAST_REPLAY 0x710
+#define OBD_FAIL_TGT_CLIENT_ADD 0x711
+#define OBD_FAIL_TGT_RCVG_FLAG 0x712
+#define OBD_FAIL_TGT_DELAY_CONDITIONAL 0x713
+
+#define OBD_FAIL_MDC_REVALIDATE_PAUSE 0x800
+#define OBD_FAIL_MDC_ENQUEUE_PAUSE 0x801
+#define OBD_FAIL_MDC_OLD_EXT_FLAGS 0x802
+#define OBD_FAIL_MDC_GETATTR_ENQUEUE 0x803
+#define OBD_FAIL_MDC_RPCS_SEM 0x804
+#define OBD_FAIL_MDC_LIGHTWEIGHT 0x805
+
+#define OBD_FAIL_MGS 0x900
+#define OBD_FAIL_MGS_ALL_REQUEST_NET 0x901
+#define OBD_FAIL_MGS_ALL_REPLY_NET 0x902
+#define OBD_FAIL_MGC_PAUSE_PROCESS_LOG 0x903
+#define OBD_FAIL_MGS_PAUSE_REQ 0x904
+#define OBD_FAIL_MGS_PAUSE_TARGET_REG 0x905
+#define OBD_FAIL_MGS_CONNECT_NET 0x906
+#define OBD_FAIL_MGS_DISCONNECT_NET 0x907
+#define OBD_FAIL_MGS_SET_INFO_NET 0x908
+#define OBD_FAIL_MGS_EXCEPTION_NET 0x909
+#define OBD_FAIL_MGS_TARGET_REG_NET 0x90a
+#define OBD_FAIL_MGS_TARGET_DEL_NET 0x90b
+#define OBD_FAIL_MGS_CONFIG_READ_NET 0x90c
+
+#define OBD_FAIL_QUOTA_DQACQ_NET 0xA01
+#define OBD_FAIL_QUOTA_EDQUOT 0xA02
+#define OBD_FAIL_QUOTA_DELAY_REINT 0xA03
+#define OBD_FAIL_QUOTA_RECOVERABLE_ERR 0xA04
+
+#define OBD_FAIL_LPROC_REMOVE 0xB00
+
+#define OBD_FAIL_GENERAL_ALLOC 0xC00
+
+#define OBD_FAIL_SEQ 0x1000
+#define OBD_FAIL_SEQ_QUERY_NET 0x1001
+#define OBD_FAIL_SEQ_EXHAUST 0x1002
+
+#define OBD_FAIL_FLD 0x1100
+#define OBD_FAIL_FLD_QUERY_NET 0x1101
+
+#define OBD_FAIL_SEC_CTX 0x1200
+#define OBD_FAIL_SEC_CTX_INIT_NET 0x1201
+#define OBD_FAIL_SEC_CTX_INIT_CONT_NET 0x1202
+#define OBD_FAIL_SEC_CTX_FINI_NET 0x1203
+#define OBD_FAIL_SEC_CTX_HDL_PAUSE 0x1204
+
+#define OBD_FAIL_LLOG 0x1300
+#define OBD_FAIL_LLOG_ORIGIN_CONNECT_NET 0x1301
+#define OBD_FAIL_LLOG_ORIGIN_HANDLE_CREATE_NET 0x1302
+#define OBD_FAIL_LLOG_ORIGIN_HANDLE_DESTROY_NET 0x1303
+#define OBD_FAIL_LLOG_ORIGIN_HANDLE_READ_HEADER_NET 0x1304
+#define OBD_FAIL_LLOG_ORIGIN_HANDLE_NEXT_BLOCK_NET 0x1305
+#define OBD_FAIL_LLOG_ORIGIN_HANDLE_PREV_BLOCK_NET 0x1306
+#define OBD_FAIL_LLOG_ORIGIN_HANDLE_WRITE_REC_NET 0x1307
+#define OBD_FAIL_LLOG_ORIGIN_HANDLE_CLOSE_NET 0x1308
+#define OBD_FAIL_LLOG_CATINFO_NET 0x1309
+#define OBD_FAIL_MDS_SYNC_CAPA_SL 0x1310
+#define OBD_FAIL_SEQ_ALLOC 0x1311
+
+#define OBD_FAIL_LLITE 0x1400
+#define OBD_FAIL_LLITE_FAULT_TRUNC_RACE 0x1401
+#define OBD_FAIL_LOCK_STATE_WAIT_INTR 0x1402
+#define OBD_FAIL_LOV_INIT 0x1403
+#define OBD_FAIL_GLIMPSE_DELAY 0x1404
+#define OBD_FAIL_LLITE_XATTR_ENOMEM 0x1405
+
+#define OBD_FAIL_FID_INDIR 0x1501
+#define OBD_FAIL_FID_INLMA 0x1502
+#define OBD_FAIL_FID_IGIF 0x1504
+#define OBD_FAIL_FID_LOOKUP 0x1505
+#define OBD_FAIL_FID_NOLMA 0x1506
+
+/* LFSCK */
+#define OBD_FAIL_LFSCK_DELAY1 0x1600
+#define OBD_FAIL_LFSCK_DELAY2 0x1601
+#define OBD_FAIL_LFSCK_DELAY3 0x1602
+#define OBD_FAIL_LFSCK_LINKEA_CRASH 0x1603
+#define OBD_FAIL_LFSCK_LINKEA_MORE 0x1604
+#define OBD_FAIL_LFSCK_LINKEA_MORE2 0x1605
+#define OBD_FAIL_LFSCK_FATAL1 0x1608
+#define OBD_FAIL_LFSCK_FATAL2 0x1609
+#define OBD_FAIL_LFSCK_CRASH 0x160a
+#define OBD_FAIL_LFSCK_NO_AUTO 0x160b
+#define OBD_FAIL_LFSCK_NO_DOUBLESCAN 0x160c
+
+/* UPDATE */
+#define OBD_FAIL_UPDATE_OBJ_NET 0x1700
+#define OBD_FAIL_UPDATE_OBJ_NET_REP 0x1701
+
+
+/* Assign references to moved code to reduce code changes */
+#define OBD_FAIL_PRECHECK(id) CFS_FAIL_PRECHECK(id)
+#define OBD_FAIL_CHECK(id) CFS_FAIL_CHECK(id)
+#define OBD_FAIL_CHECK_VALUE(id, value) CFS_FAIL_CHECK_VALUE(id, value)
+#define OBD_FAIL_CHECK_ORSET(id, value) CFS_FAIL_CHECK_ORSET(id, value)
+#define OBD_FAIL_CHECK_RESET(id, value) CFS_FAIL_CHECK_RESET(id, value)
+#define OBD_FAIL_RETURN(id, ret) CFS_FAIL_RETURN(id, ret)
+#define OBD_FAIL_TIMEOUT(id, secs) CFS_FAIL_TIMEOUT(id, secs)
+#define OBD_FAIL_TIMEOUT_MS(id, ms) CFS_FAIL_TIMEOUT_MS(id, ms)
+#define OBD_FAIL_TIMEOUT_ORSET(id, value, secs) CFS_FAIL_TIMEOUT_ORSET(id, value, secs)
+#define OBD_RACE(id) CFS_RACE(id)
+#define OBD_FAIL_ONCE CFS_FAIL_ONCE
+#define OBD_FAILED CFS_FAILED
+
+extern atomic_t libcfs_kmemory;
+
+extern void obd_update_maxusage(void);
+
+#if defined (CONFIG_PROC_FS)
+#define obd_memory_add(size) \
+ lprocfs_counter_add(obd_memory, OBD_MEMORY_STAT, (long)(size))
+#define obd_memory_sub(size) \
+ lprocfs_counter_sub(obd_memory, OBD_MEMORY_STAT, (long)(size))
+#define obd_memory_sum() \
+ lprocfs_stats_collector(obd_memory, OBD_MEMORY_STAT, \
+ LPROCFS_FIELDS_FLAGS_SUM)
+#define obd_pages_add(order) \
+ lprocfs_counter_add(obd_memory, OBD_MEMORY_PAGES_STAT, \
+ (long)(1 << (order)))
+#define obd_pages_sub(order) \
+ lprocfs_counter_sub(obd_memory, OBD_MEMORY_PAGES_STAT, \
+ (long)(1 << (order)))
+#define obd_pages_sum() \
+ lprocfs_stats_collector(obd_memory, OBD_MEMORY_PAGES_STAT, \
+ LPROCFS_FIELDS_FLAGS_SUM)
+
+extern __u64 obd_memory_max(void);
+extern __u64 obd_pages_max(void);
+
+#else
+
+extern __u64 obd_alloc;
+extern __u64 obd_pages;
+
+extern __u64 obd_max_alloc;
+extern __u64 obd_max_pages;
+
+static inline void obd_memory_add(long size)
+{
+ obd_alloc += size;
+ if (obd_alloc > obd_max_alloc)
+ obd_max_alloc = obd_alloc;
+}
+
+static inline void obd_memory_sub(long size)
+{
+ obd_alloc -= size;
+}
+
+static inline void obd_pages_add(int order)
+{
+ obd_pages += 1<< order;
+ if (obd_pages > obd_max_pages)
+ obd_max_pages = obd_pages;
+}
+
+static inline void obd_pages_sub(int order)
+{
+ obd_pages -= 1<< order;
+}
+
+#define obd_memory_sum() (obd_alloc)
+#define obd_pages_sum() (obd_pages)
+
+#define obd_memory_max() (obd_max_alloc)
+#define obd_pages_max() (obd_max_pages)
+
+#endif
+
+#define OBD_DEBUG_MEMUSAGE (1)
+
+#if OBD_DEBUG_MEMUSAGE
+#define OBD_ALLOC_POST(ptr, size, name) \
+ obd_memory_add(size); \
+ CDEBUG(D_MALLOC, name " '" #ptr "': %d at %p.\n", \
+ (int)(size), ptr)
+
+#define OBD_FREE_PRE(ptr, size, name) \
+ LASSERT(ptr); \
+ obd_memory_sub(size); \
+ CDEBUG(D_MALLOC, name " '" #ptr "': %d at %p.\n", \
+ (int)(size), ptr); \
+ POISON(ptr, 0x5a, size)
+
+#else /* !OBD_DEBUG_MEMUSAGE */
+
+#define OBD_ALLOC_POST(ptr, size, name) ((void)0)
+#define OBD_FREE_PRE(ptr, size, name) ((void)0)
+
+#endif /* !OBD_DEBUG_MEMUSAGE */
+
+#define HAS_FAIL_ALLOC_FLAG OBD_FAIL_CHECK(OBD_FAIL_GENERAL_ALLOC)
+
+#define OBD_ALLOC_FAIL_BITS 24
+#define OBD_ALLOC_FAIL_MASK ((1 << OBD_ALLOC_FAIL_BITS) - 1)
+#define OBD_ALLOC_FAIL_MULT (OBD_ALLOC_FAIL_MASK / 100)
+
+#if defined(LUSTRE_UTILS) /* this version is for utils only */
+#define __OBD_MALLOC_VERBOSE(ptr, cptab, cpt, size, flags) \
+do { \
+ (ptr) = (cptab) == NULL ? \
+ kmalloc(size, flags) : \
+ kmalloc_node(size, flags, cfs_cpt_spread_node(cptab, cpt)); \
+ if (unlikely((ptr) == NULL)) { \
+ CERROR("kmalloc of '" #ptr "' (%d bytes) failed at %s:%d\n", \
+ (int)(size), __FILE__, __LINE__); \
+ } else { \
+ memset(ptr, 0, size); \
+ CDEBUG(D_MALLOC, "kmalloced '" #ptr "': %d at %p\n", \
+ (int)(size), ptr); \
+ } \
+} while (0)
+
+#else /* this version is for the kernel and liblustre */
+#define OBD_FREE_RTN0(ptr) \
+({ \
+ kfree(ptr); \
+ (ptr) = NULL; \
+ 0; \
+})
+
+#define __OBD_MALLOC_VERBOSE(ptr, cptab, cpt, size, flags) \
+do { \
+ (ptr) = (cptab) == NULL ? \
+ kmalloc(size, flags | __GFP_ZERO) : \
+ kmalloc_node(size, flags | __GFP_ZERO, \
+ cfs_cpt_spread_node(cptab, cpt)); \
+ if (likely((ptr) != NULL && \
+ (!HAS_FAIL_ALLOC_FLAG || obd_alloc_fail_rate == 0 || \
+ !obd_alloc_fail(ptr, #ptr, "km", size, \
+ __FILE__, __LINE__) || \
+ OBD_FREE_RTN0(ptr)))){ \
+ OBD_ALLOC_POST(ptr, size, "kmalloced"); \
+ } \
+} while (0)
+#endif
+
+#define OBD_ALLOC_GFP(ptr, size, gfp_mask) \
+ __OBD_MALLOC_VERBOSE(ptr, NULL, 0, size, gfp_mask)
+
+#define OBD_ALLOC(ptr, size) OBD_ALLOC_GFP(ptr, size, GFP_NOFS)
+#define OBD_ALLOC_WAIT(ptr, size) OBD_ALLOC_GFP(ptr, size, GFP_KERNEL)
+#define OBD_ALLOC_PTR(ptr) OBD_ALLOC(ptr, sizeof(*(ptr)))
+#define OBD_ALLOC_PTR_WAIT(ptr) OBD_ALLOC_WAIT(ptr, sizeof(*(ptr)))
+
+#define OBD_CPT_ALLOC_GFP(ptr, cptab, cpt, size, gfp_mask) \
+ __OBD_MALLOC_VERBOSE(ptr, cptab, cpt, size, gfp_mask)
+
+#define OBD_CPT_ALLOC(ptr, cptab, cpt, size) \
+ OBD_CPT_ALLOC_GFP(ptr, cptab, cpt, size, GFP_NOFS)
+
+#define OBD_CPT_ALLOC_PTR(ptr, cptab, cpt) \
+ OBD_CPT_ALLOC(ptr, cptab, cpt, sizeof(*(ptr)))
+
+# define __OBD_VMALLOC_VEROBSE(ptr, cptab, cpt, size) \
+do { \
+ (ptr) = cptab == NULL ? \
+ vzalloc(size) : \
+ vzalloc_node(size, cfs_cpt_spread_node(cptab, cpt)); \
+ if (unlikely((ptr) == NULL)) { \
+ CERROR("vmalloc of '" #ptr "' (%d bytes) failed\n", \
+ (int)(size)); \
+ CERROR("%llu total bytes allocated by Lustre, %d by LNET\n", \
+ obd_memory_sum(), atomic_read(&libcfs_kmemory)); \
+ } else { \
+ OBD_ALLOC_POST(ptr, size, "vmalloced"); \
+ } \
+} while (0)
+
+# define OBD_VMALLOC(ptr, size) \
+ __OBD_VMALLOC_VEROBSE(ptr, NULL, 0, size)
+# define OBD_CPT_VMALLOC(ptr, cptab, cpt, size) \
+ __OBD_VMALLOC_VEROBSE(ptr, cptab, cpt, size)
+
+
+/* Allocations above this size are considered too big and could not be done
+ * atomically.
+ *
+ * Be very careful when changing this value, especially when decreasing it,
+ * since vmalloc in Linux doesn't perform well on multi-cores system, calling
+ * vmalloc in critical path would hurt performance badly. See LU-66.
+ */
+#define OBD_ALLOC_BIG (4 * PAGE_CACHE_SIZE)
+
+#define OBD_ALLOC_LARGE(ptr, size) \
+do { \
+ if (size > OBD_ALLOC_BIG) \
+ OBD_VMALLOC(ptr, size); \
+ else \
+ OBD_ALLOC(ptr, size); \
+} while (0)
+
+#define OBD_CPT_ALLOC_LARGE(ptr, cptab, cpt, size) \
+do { \
+ if (size > OBD_ALLOC_BIG) \
+ OBD_CPT_VMALLOC(ptr, cptab, cpt, size); \
+ else \
+ OBD_CPT_ALLOC(ptr, cptab, cpt, size); \
+} while (0)
+
+#define OBD_FREE_LARGE(ptr, size) \
+do { \
+ if (size > OBD_ALLOC_BIG) \
+ OBD_VFREE(ptr, size); \
+ else \
+ OBD_FREE(ptr, size); \
+} while (0)
+
+
+#ifdef CONFIG_DEBUG_SLAB
+#define POISON(ptr, c, s) do {} while (0)
+#define POISON_PTR(ptr) ((void)0)
+#else
+#define POISON(ptr, c, s) memset(ptr, c, s)
+#define POISON_PTR(ptr) (ptr) = (void *)0xdeadbeef
+#endif
+
+#ifdef POISON_BULK
+#define POISON_PAGE(page, val) do { memset(kmap(page), val, PAGE_CACHE_SIZE); \
+ kunmap(page); } while (0)
+#else
+#define POISON_PAGE(page, val) do { } while (0)
+#endif
+
+#define OBD_FREE(ptr, size) \
+do { \
+ OBD_FREE_PRE(ptr, size, "kfreed"); \
+ kfree(ptr); \
+ POISON_PTR(ptr); \
+} while (0)
+
+
+#define OBD_FREE_RCU(ptr, size, handle) \
+do { \
+ struct portals_handle *__h = (handle); \
+ \
+ LASSERT(handle != NULL); \
+ __h->h_cookie = (unsigned long)(ptr); \
+ __h->h_size = (size); \
+ call_rcu(&__h->h_rcu, class_handle_free_cb); \
+ POISON_PTR(ptr); \
+} while (0)
+
+
+#define OBD_VFREE(ptr, size) \
+ do { \
+ OBD_FREE_PRE(ptr, size, "vfreed"); \
+ vfree(ptr); \
+ POISON_PTR(ptr); \
+ } while (0)
+
+/* we memset() the slab object to 0 when allocation succeeds, so DO NOT
+ * HAVE A CTOR THAT DOES ANYTHING. its work will be cleared here. we'd
+ * love to assert on that, but slab.c keeps kmem_cache_s all to itself. */
+#define OBD_SLAB_FREE_RTN0(ptr, slab) \
+({ \
+ kmem_cache_free((slab), (ptr)); \
+ (ptr) = NULL; \
+ 0; \
+})
+
+#define __OBD_SLAB_ALLOC_VERBOSE(ptr, slab, cptab, cpt, size, type) \
+do { \
+ LASSERT(ergo((type) != GFP_ATOMIC, !in_interrupt())); \
+ (ptr) = (cptab) == NULL ? \
+ kmem_cache_alloc(slab, type | __GFP_ZERO) : \
+ kmem_cache_alloc_node(slab, type | __GFP_ZERO, \
+ cfs_cpt_spread_node(cptab, cpt)); \
+ if (likely((ptr) != NULL && \
+ (!HAS_FAIL_ALLOC_FLAG || obd_alloc_fail_rate == 0 || \
+ !obd_alloc_fail(ptr, #ptr, "slab-", size, \
+ __FILE__, __LINE__) || \
+ OBD_SLAB_FREE_RTN0(ptr, slab)))) { \
+ OBD_ALLOC_POST(ptr, size, "slab-alloced"); \
+ } \
+} while (0)
+
+#define OBD_SLAB_ALLOC_GFP(ptr, slab, size, flags) \
+ __OBD_SLAB_ALLOC_VERBOSE(ptr, slab, NULL, 0, size, flags)
+#define OBD_SLAB_CPT_ALLOC_GFP(ptr, slab, cptab, cpt, size, flags) \
+ __OBD_SLAB_ALLOC_VERBOSE(ptr, slab, cptab, cpt, size, flags)
+
+#define OBD_FREE_PTR(ptr) OBD_FREE(ptr, sizeof(*(ptr)))
+
+#define OBD_SLAB_FREE(ptr, slab, size) \
+do { \
+ OBD_FREE_PRE(ptr, size, "slab-freed"); \
+ kmem_cache_free(slab, ptr); \
+ POISON_PTR(ptr); \
+} while (0)
+
+#define OBD_SLAB_ALLOC(ptr, slab, size) \
+ OBD_SLAB_ALLOC_GFP(ptr, slab, size, GFP_NOFS)
+
+#define OBD_SLAB_CPT_ALLOC(ptr, slab, cptab, cpt, size) \
+ OBD_SLAB_CPT_ALLOC_GFP(ptr, slab, cptab, cpt, size, GFP_NOFS)
+
+#define OBD_SLAB_ALLOC_PTR(ptr, slab) \
+ OBD_SLAB_ALLOC(ptr, slab, sizeof(*(ptr)))
+
+#define OBD_SLAB_CPT_ALLOC_PTR(ptr, slab, cptab, cpt) \
+ OBD_SLAB_CPT_ALLOC(ptr, slab, cptab, cpt, sizeof(*(ptr)))
+
+#define OBD_SLAB_ALLOC_PTR_GFP(ptr, slab, flags) \
+ OBD_SLAB_ALLOC_GFP(ptr, slab, sizeof(*(ptr)), flags)
+
+#define OBD_SLAB_CPT_ALLOC_PTR_GFP(ptr, slab, cptab, cpt, flags) \
+ OBD_SLAB_CPT_ALLOC_GFP(ptr, slab, cptab, cpt, sizeof(*(ptr)), flags)
+
+#define OBD_SLAB_FREE_PTR(ptr, slab) \
+ OBD_SLAB_FREE((ptr), (slab), sizeof(*(ptr)))
+
+#define KEY_IS(str) \
+ (keylen >= (sizeof(str)-1) && memcmp(key, str, (sizeof(str)-1)) == 0)
+
+/* Wrapper for contiguous page frame allocation */
+#define __OBD_PAGE_ALLOC_VERBOSE(ptr, cptab, cpt, gfp_mask) \
+do { \
+ (ptr) = (cptab) == NULL ? \
+ alloc_page(gfp_mask) : \
+ alloc_pages_node(cfs_cpt_spread_node(cptab, cpt), gfp_mask, 0);\
+ if (unlikely((ptr) == NULL)) { \
+ CERROR("alloc_pages of '" #ptr "' %d page(s) / %llu bytes "\
+ "failed\n", (int)1, \
+ (__u64)(1 << PAGE_CACHE_SHIFT)); \
+ CERROR("%llu total bytes and %llu total pages " \
+ "(%llu bytes) allocated by Lustre, " \
+ "%d total bytes by LNET\n", \
+ obd_memory_sum(), \
+ obd_pages_sum() << PAGE_CACHE_SHIFT, \
+ obd_pages_sum(), \
+ atomic_read(&libcfs_kmemory)); \
+ } else { \
+ obd_pages_add(0); \
+ CDEBUG(D_MALLOC, "alloc_pages '" #ptr "': %d page(s) / " \
+ "%llu bytes at %p.\n", \
+ (int)1, \
+ (__u64)(1 << PAGE_CACHE_SHIFT), ptr); \
+ } \
+} while (0)
+
+#define OBD_PAGE_ALLOC(ptr, gfp_mask) \
+ __OBD_PAGE_ALLOC_VERBOSE(ptr, NULL, 0, gfp_mask)
+#define OBD_PAGE_CPT_ALLOC(ptr, cptab, cpt, gfp_mask) \
+ __OBD_PAGE_ALLOC_VERBOSE(ptr, cptab, cpt, gfp_mask)
+
+#define OBD_PAGE_FREE(ptr) \
+do { \
+ LASSERT(ptr); \
+ obd_pages_sub(0); \
+ CDEBUG(D_MALLOC, "free_pages '" #ptr "': %d page(s) / %llu bytes " \
+ "at %p.\n", \
+ (int)1, (__u64)(1 << PAGE_CACHE_SHIFT), \
+ ptr); \
+ __free_page(ptr); \
+ (ptr) = (void *)0xdeadbeef; \
+} while (0)
+
+#endif
diff --git a/drivers/staging/lustre/lustre/lclient/glimpse.c b/drivers/staging/lustre/lustre/lclient/glimpse.c
new file mode 100644
index 000000000..b9f2bb66d
--- /dev/null
+++ b/drivers/staging/lustre/lustre/lclient/glimpse.c
@@ -0,0 +1,269 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2011, 2012, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ *
+ * glimpse code shared between vvp and liblustre (and other Lustre clients in
+ * the future).
+ *
+ * Author: Nikita Danilov <nikita.danilov@sun.com>
+ * Author: Oleg Drokin <oleg.drokin@sun.com>
+ */
+
+#include "../../include/linux/libcfs/libcfs.h"
+#include "../include/obd_class.h"
+#include "../include/obd_support.h"
+#include "../include/obd.h"
+
+#include "../include/lustre_dlm.h"
+#include "../include/lustre_lite.h"
+#include "../include/lustre_mdc.h"
+#include <linux/pagemap.h>
+#include <linux/file.h>
+
+#include "../include/cl_object.h"
+#include "../include/lclient.h"
+#include "../llite/llite_internal.h"
+
+static const struct cl_lock_descr whole_file = {
+ .cld_start = 0,
+ .cld_end = CL_PAGE_EOF,
+ .cld_mode = CLM_READ
+};
+
+/*
+ * Check whether file has possible unwriten pages.
+ *
+ * \retval 1 file is mmap-ed or has dirty pages
+ * 0 otherwise
+ */
+blkcnt_t dirty_cnt(struct inode *inode)
+{
+ blkcnt_t cnt = 0;
+ struct ccc_object *vob = cl_inode2ccc(inode);
+ void *results[1];
+
+ if (inode->i_mapping != NULL)
+ cnt += radix_tree_gang_lookup_tag(&inode->i_mapping->page_tree,
+ results, 0, 1,
+ PAGECACHE_TAG_DIRTY);
+ if (cnt == 0 && atomic_read(&vob->cob_mmap_cnt) > 0)
+ cnt = 1;
+
+ return (cnt > 0) ? 1 : 0;
+}
+
+int cl_glimpse_lock(const struct lu_env *env, struct cl_io *io,
+ struct inode *inode, struct cl_object *clob, int agl)
+{
+ struct cl_lock_descr *descr = &ccc_env_info(env)->cti_descr;
+ struct cl_inode_info *lli = cl_i2info(inode);
+ const struct lu_fid *fid = lu_object_fid(&clob->co_lu);
+ struct ccc_io *cio = ccc_env_io(env);
+ struct cl_lock *lock;
+ int result;
+
+ result = 0;
+ if (!(lli->lli_flags & LLIF_MDS_SIZE_LOCK)) {
+ CDEBUG(D_DLMTRACE, "Glimpsing inode "DFID"\n", PFID(fid));
+ if (lli->lli_has_smd) {
+ /* NOTE: this looks like DLM lock request, but it may
+ * not be one. Due to CEF_ASYNC flag (translated
+ * to LDLM_FL_HAS_INTENT by osc), this is
+ * glimpse request, that won't revoke any
+ * conflicting DLM locks held. Instead,
+ * ll_glimpse_callback() will be called on each
+ * client holding a DLM lock against this file,
+ * and resulting size will be returned for each
+ * stripe. DLM lock on [0, EOF] is acquired only
+ * if there were no conflicting locks. If there
+ * were conflicting locks, enqueuing or waiting
+ * fails with -ENAVAIL, but valid inode
+ * attributes are returned anyway. */
+ *descr = whole_file;
+ descr->cld_obj = clob;
+ descr->cld_mode = CLM_PHANTOM;
+ descr->cld_enq_flags = CEF_ASYNC | CEF_MUST;
+ if (agl)
+ descr->cld_enq_flags |= CEF_AGL;
+ cio->cui_glimpse = 1;
+ /*
+ * CEF_ASYNC is used because glimpse sub-locks cannot
+ * deadlock (because they never conflict with other
+ * locks) and, hence, can be enqueued out-of-order.
+ *
+ * CEF_MUST protects glimpse lock from conversion into
+ * a lockless mode.
+ */
+ lock = cl_lock_request(env, io, descr, "glimpse",
+ current);
+ cio->cui_glimpse = 0;
+
+ if (lock == NULL)
+ return 0;
+
+ if (IS_ERR(lock))
+ return PTR_ERR(lock);
+
+ LASSERT(agl == 0);
+ result = cl_wait(env, lock);
+ if (result == 0) {
+ cl_merge_lvb(env, inode);
+ if (cl_isize_read(inode) > 0 &&
+ inode->i_blocks == 0) {
+ /*
+ * LU-417: Add dirty pages block count
+ * lest i_blocks reports 0, some "cp" or
+ * "tar" may think it's a completely
+ * sparse file and skip it.
+ */
+ inode->i_blocks = dirty_cnt(inode);
+ }
+ cl_unuse(env, lock);
+ }
+ cl_lock_release(env, lock, "glimpse", current);
+ } else {
+ CDEBUG(D_DLMTRACE, "No objects for inode\n");
+ cl_merge_lvb(env, inode);
+ }
+ }
+
+ return result;
+}
+
+static int cl_io_get(struct inode *inode, struct lu_env **envout,
+ struct cl_io **ioout, int *refcheck)
+{
+ struct lu_env *env;
+ struct cl_io *io;
+ struct cl_inode_info *lli = cl_i2info(inode);
+ struct cl_object *clob = lli->lli_clob;
+ int result;
+
+ if (S_ISREG(cl_inode_mode(inode))) {
+ env = cl_env_get(refcheck);
+ if (!IS_ERR(env)) {
+ io = ccc_env_thread_io(env);
+ io->ci_obj = clob;
+ *envout = env;
+ *ioout = io;
+ result = 1;
+ } else
+ result = PTR_ERR(env);
+ } else
+ result = 0;
+ return result;
+}
+
+int cl_glimpse_size0(struct inode *inode, int agl)
+{
+ /*
+ * We don't need ast_flags argument to cl_glimpse_size(), because
+ * osc_lock_enqueue() takes care of the possible deadlock that said
+ * argument was introduced to avoid.
+ */
+ /*
+ * XXX but note that ll_file_seek() passes LDLM_FL_BLOCK_NOWAIT to
+ * cl_glimpse_size(), which doesn't make sense: glimpse locks are not
+ * blocking anyway.
+ */
+ struct lu_env *env = NULL;
+ struct cl_io *io = NULL;
+ int result;
+ int refcheck;
+
+ result = cl_io_get(inode, &env, &io, &refcheck);
+ if (result > 0) {
+again:
+ io->ci_verify_layout = 1;
+ result = cl_io_init(env, io, CIT_MISC, io->ci_obj);
+ if (result > 0)
+ /*
+ * nothing to do for this io. This currently happens
+ * when stripe sub-object's are not yet created.
+ */
+ result = io->ci_result;
+ else if (result == 0)
+ result = cl_glimpse_lock(env, io, inode, io->ci_obj,
+ agl);
+
+ OBD_FAIL_TIMEOUT(OBD_FAIL_GLIMPSE_DELAY, 2);
+ cl_io_fini(env, io);
+ if (unlikely(io->ci_need_restart))
+ goto again;
+ cl_env_put(env, &refcheck);
+ }
+ return result;
+}
+
+int cl_local_size(struct inode *inode)
+{
+ struct lu_env *env = NULL;
+ struct cl_io *io = NULL;
+ struct ccc_thread_info *cti;
+ struct cl_object *clob;
+ struct cl_lock_descr *descr;
+ struct cl_lock *lock;
+ int result;
+ int refcheck;
+
+ if (!cl_i2info(inode)->lli_has_smd)
+ return 0;
+
+ result = cl_io_get(inode, &env, &io, &refcheck);
+ if (result <= 0)
+ return result;
+
+ clob = io->ci_obj;
+ result = cl_io_init(env, io, CIT_MISC, clob);
+ if (result > 0)
+ result = io->ci_result;
+ else if (result == 0) {
+ cti = ccc_env_info(env);
+ descr = &cti->cti_descr;
+
+ *descr = whole_file;
+ descr->cld_obj = clob;
+ lock = cl_lock_peek(env, io, descr, "localsize", current);
+ if (lock != NULL) {
+ cl_merge_lvb(env, inode);
+ cl_unuse(env, lock);
+ cl_lock_release(env, lock, "localsize", current);
+ result = 0;
+ } else
+ result = -ENODATA;
+ }
+ cl_io_fini(env, io);
+ cl_env_put(env, &refcheck);
+ return result;
+}
diff --git a/drivers/staging/lustre/lustre/lclient/lcommon_cl.c b/drivers/staging/lustre/lustre/lclient/lcommon_cl.c
new file mode 100644
index 000000000..ab6cb4193
--- /dev/null
+++ b/drivers/staging/lustre/lustre/lclient/lcommon_cl.c
@@ -0,0 +1,1287 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2011, 2012, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ *
+ * cl code shared between vvp and liblustre (and other Lustre clients in the
+ * future).
+ *
+ * Author: Nikita Danilov <nikita.danilov@sun.com>
+ */
+
+#define DEBUG_SUBSYSTEM S_LLITE
+
+#include "../../include/linux/libcfs/libcfs.h"
+# include <linux/fs.h>
+# include <linux/sched.h>
+# include <linux/mm.h>
+# include <linux/quotaops.h>
+# include <linux/highmem.h>
+# include <linux/pagemap.h>
+# include <linux/rbtree.h>
+
+#include "../include/obd.h"
+#include "../include/obd_support.h"
+#include "../include/lustre_fid.h"
+#include "../include/lustre_lite.h"
+#include "../include/lustre_dlm.h"
+#include "../include/lustre_ver.h"
+#include "../include/lustre_mdc.h"
+#include "../include/cl_object.h"
+
+#include "../include/lclient.h"
+
+#include "../llite/llite_internal.h"
+
+static const struct cl_req_operations ccc_req_ops;
+
+/*
+ * ccc_ prefix stands for "Common Client Code".
+ */
+
+static struct kmem_cache *ccc_lock_kmem;
+static struct kmem_cache *ccc_object_kmem;
+static struct kmem_cache *ccc_thread_kmem;
+static struct kmem_cache *ccc_session_kmem;
+static struct kmem_cache *ccc_req_kmem;
+
+static struct lu_kmem_descr ccc_caches[] = {
+ {
+ .ckd_cache = &ccc_lock_kmem,
+ .ckd_name = "ccc_lock_kmem",
+ .ckd_size = sizeof(struct ccc_lock)
+ },
+ {
+ .ckd_cache = &ccc_object_kmem,
+ .ckd_name = "ccc_object_kmem",
+ .ckd_size = sizeof(struct ccc_object)
+ },
+ {
+ .ckd_cache = &ccc_thread_kmem,
+ .ckd_name = "ccc_thread_kmem",
+ .ckd_size = sizeof(struct ccc_thread_info),
+ },
+ {
+ .ckd_cache = &ccc_session_kmem,
+ .ckd_name = "ccc_session_kmem",
+ .ckd_size = sizeof(struct ccc_session)
+ },
+ {
+ .ckd_cache = &ccc_req_kmem,
+ .ckd_name = "ccc_req_kmem",
+ .ckd_size = sizeof(struct ccc_req)
+ },
+ {
+ .ckd_cache = NULL
+ }
+};
+
+/*****************************************************************************
+ *
+ * Vvp device and device type functions.
+ *
+ */
+
+void *ccc_key_init(const struct lu_context *ctx, struct lu_context_key *key)
+{
+ struct ccc_thread_info *info;
+
+ OBD_SLAB_ALLOC_PTR_GFP(info, ccc_thread_kmem, GFP_NOFS);
+ if (info == NULL)
+ info = ERR_PTR(-ENOMEM);
+ return info;
+}
+
+void ccc_key_fini(const struct lu_context *ctx,
+ struct lu_context_key *key, void *data)
+{
+ struct ccc_thread_info *info = data;
+
+ OBD_SLAB_FREE_PTR(info, ccc_thread_kmem);
+}
+
+void *ccc_session_key_init(const struct lu_context *ctx,
+ struct lu_context_key *key)
+{
+ struct ccc_session *session;
+
+ OBD_SLAB_ALLOC_PTR_GFP(session, ccc_session_kmem, GFP_NOFS);
+ if (session == NULL)
+ session = ERR_PTR(-ENOMEM);
+ return session;
+}
+
+void ccc_session_key_fini(const struct lu_context *ctx,
+ struct lu_context_key *key, void *data)
+{
+ struct ccc_session *session = data;
+
+ OBD_SLAB_FREE_PTR(session, ccc_session_kmem);
+}
+
+struct lu_context_key ccc_key = {
+ .lct_tags = LCT_CL_THREAD,
+ .lct_init = ccc_key_init,
+ .lct_fini = ccc_key_fini
+};
+
+struct lu_context_key ccc_session_key = {
+ .lct_tags = LCT_SESSION,
+ .lct_init = ccc_session_key_init,
+ .lct_fini = ccc_session_key_fini
+};
+
+
+/* type constructor/destructor: ccc_type_{init,fini,start,stop}(). */
+/* LU_TYPE_INIT_FINI(ccc, &ccc_key, &ccc_session_key); */
+
+int ccc_device_init(const struct lu_env *env, struct lu_device *d,
+ const char *name, struct lu_device *next)
+{
+ struct ccc_device *vdv;
+ int rc;
+
+ vdv = lu2ccc_dev(d);
+ vdv->cdv_next = lu2cl_dev(next);
+
+ LASSERT(d->ld_site != NULL && next->ld_type != NULL);
+ next->ld_site = d->ld_site;
+ rc = next->ld_type->ldt_ops->ldto_device_init(
+ env, next, next->ld_type->ldt_name, NULL);
+ if (rc == 0) {
+ lu_device_get(next);
+ lu_ref_add(&next->ld_reference, "lu-stack", &lu_site_init);
+ }
+ return rc;
+}
+
+struct lu_device *ccc_device_fini(const struct lu_env *env,
+ struct lu_device *d)
+{
+ return cl2lu_dev(lu2ccc_dev(d)->cdv_next);
+}
+
+struct lu_device *ccc_device_alloc(const struct lu_env *env,
+ struct lu_device_type *t,
+ struct lustre_cfg *cfg,
+ const struct lu_device_operations *luops,
+ const struct cl_device_operations *clops)
+{
+ struct ccc_device *vdv;
+ struct lu_device *lud;
+ struct cl_site *site;
+ int rc;
+
+ OBD_ALLOC_PTR(vdv);
+ if (vdv == NULL)
+ return ERR_PTR(-ENOMEM);
+
+ lud = &vdv->cdv_cl.cd_lu_dev;
+ cl_device_init(&vdv->cdv_cl, t);
+ ccc2lu_dev(vdv)->ld_ops = luops;
+ vdv->cdv_cl.cd_ops = clops;
+
+ OBD_ALLOC_PTR(site);
+ if (site != NULL) {
+ rc = cl_site_init(site, &vdv->cdv_cl);
+ if (rc == 0)
+ rc = lu_site_init_finish(&site->cs_lu);
+ else {
+ LASSERT(lud->ld_site == NULL);
+ CERROR("Cannot init lu_site, rc %d.\n", rc);
+ OBD_FREE_PTR(site);
+ }
+ } else
+ rc = -ENOMEM;
+ if (rc != 0) {
+ ccc_device_free(env, lud);
+ lud = ERR_PTR(rc);
+ }
+ return lud;
+}
+
+struct lu_device *ccc_device_free(const struct lu_env *env,
+ struct lu_device *d)
+{
+ struct ccc_device *vdv = lu2ccc_dev(d);
+ struct cl_site *site = lu2cl_site(d->ld_site);
+ struct lu_device *next = cl2lu_dev(vdv->cdv_next);
+
+ if (d->ld_site != NULL) {
+ cl_site_fini(site);
+ OBD_FREE_PTR(site);
+ }
+ cl_device_fini(lu2cl_dev(d));
+ OBD_FREE_PTR(vdv);
+ return next;
+}
+
+int ccc_req_init(const struct lu_env *env, struct cl_device *dev,
+ struct cl_req *req)
+{
+ struct ccc_req *vrq;
+ int result;
+
+ OBD_SLAB_ALLOC_PTR_GFP(vrq, ccc_req_kmem, GFP_NOFS);
+ if (vrq != NULL) {
+ cl_req_slice_add(req, &vrq->crq_cl, dev, &ccc_req_ops);
+ result = 0;
+ } else
+ result = -ENOMEM;
+ return result;
+}
+
+/**
+ * An `emergency' environment used by ccc_inode_fini() when cl_env_get()
+ * fails. Access to this environment is serialized by ccc_inode_fini_guard
+ * mutex.
+ */
+static struct lu_env *ccc_inode_fini_env;
+
+/**
+ * A mutex serializing calls to slp_inode_fini() under extreme memory
+ * pressure, when environments cannot be allocated.
+ */
+static DEFINE_MUTEX(ccc_inode_fini_guard);
+static int dummy_refcheck;
+
+int ccc_global_init(struct lu_device_type *device_type)
+{
+ int result;
+
+ result = lu_kmem_init(ccc_caches);
+ if (result)
+ return result;
+
+ result = lu_device_type_init(device_type);
+ if (result)
+ goto out_kmem;
+
+ ccc_inode_fini_env = cl_env_alloc(&dummy_refcheck,
+ LCT_REMEMBER|LCT_NOREF);
+ if (IS_ERR(ccc_inode_fini_env)) {
+ result = PTR_ERR(ccc_inode_fini_env);
+ goto out_device;
+ }
+
+ ccc_inode_fini_env->le_ctx.lc_cookie = 0x4;
+ return 0;
+out_device:
+ lu_device_type_fini(device_type);
+out_kmem:
+ lu_kmem_fini(ccc_caches);
+ return result;
+}
+
+void ccc_global_fini(struct lu_device_type *device_type)
+{
+ if (ccc_inode_fini_env != NULL) {
+ cl_env_put(ccc_inode_fini_env, &dummy_refcheck);
+ ccc_inode_fini_env = NULL;
+ }
+ lu_device_type_fini(device_type);
+ lu_kmem_fini(ccc_caches);
+}
+
+/*****************************************************************************
+ *
+ * Object operations.
+ *
+ */
+
+struct lu_object *ccc_object_alloc(const struct lu_env *env,
+ const struct lu_object_header *unused,
+ struct lu_device *dev,
+ const struct cl_object_operations *clops,
+ const struct lu_object_operations *luops)
+{
+ struct ccc_object *vob;
+ struct lu_object *obj;
+
+ OBD_SLAB_ALLOC_PTR_GFP(vob, ccc_object_kmem, GFP_NOFS);
+ if (vob != NULL) {
+ struct cl_object_header *hdr;
+
+ obj = ccc2lu(vob);
+ hdr = &vob->cob_header;
+ cl_object_header_init(hdr);
+ lu_object_init(obj, &hdr->coh_lu, dev);
+ lu_object_add_top(&hdr->coh_lu, obj);
+
+ vob->cob_cl.co_ops = clops;
+ obj->lo_ops = luops;
+ } else
+ obj = NULL;
+ return obj;
+}
+
+int ccc_object_init0(const struct lu_env *env,
+ struct ccc_object *vob,
+ const struct cl_object_conf *conf)
+{
+ vob->cob_inode = conf->coc_inode;
+ vob->cob_transient_pages = 0;
+ cl_object_page_init(&vob->cob_cl, sizeof(struct ccc_page));
+ return 0;
+}
+
+int ccc_object_init(const struct lu_env *env, struct lu_object *obj,
+ const struct lu_object_conf *conf)
+{
+ struct ccc_device *dev = lu2ccc_dev(obj->lo_dev);
+ struct ccc_object *vob = lu2ccc(obj);
+ struct lu_object *below;
+ struct lu_device *under;
+ int result;
+
+ under = &dev->cdv_next->cd_lu_dev;
+ below = under->ld_ops->ldo_object_alloc(env, obj->lo_header, under);
+ if (below != NULL) {
+ const struct cl_object_conf *cconf;
+
+ cconf = lu2cl_conf(conf);
+ INIT_LIST_HEAD(&vob->cob_pending_list);
+ lu_object_add(obj, below);
+ result = ccc_object_init0(env, vob, cconf);
+ } else
+ result = -ENOMEM;
+ return result;
+}
+
+void ccc_object_free(const struct lu_env *env, struct lu_object *obj)
+{
+ struct ccc_object *vob = lu2ccc(obj);
+
+ lu_object_fini(obj);
+ lu_object_header_fini(obj->lo_header);
+ OBD_SLAB_FREE_PTR(vob, ccc_object_kmem);
+}
+
+int ccc_lock_init(const struct lu_env *env,
+ struct cl_object *obj, struct cl_lock *lock,
+ const struct cl_io *unused,
+ const struct cl_lock_operations *lkops)
+{
+ struct ccc_lock *clk;
+ int result;
+
+ CLOBINVRNT(env, obj, ccc_object_invariant(obj));
+
+ OBD_SLAB_ALLOC_PTR_GFP(clk, ccc_lock_kmem, GFP_NOFS);
+ if (clk != NULL) {
+ cl_lock_slice_add(lock, &clk->clk_cl, obj, lkops);
+ result = 0;
+ } else
+ result = -ENOMEM;
+ return result;
+}
+
+int ccc_attr_set(const struct lu_env *env, struct cl_object *obj,
+ const struct cl_attr *attr, unsigned valid)
+{
+ return 0;
+}
+
+int ccc_object_glimpse(const struct lu_env *env,
+ const struct cl_object *obj, struct ost_lvb *lvb)
+{
+ struct inode *inode = ccc_object_inode(obj);
+
+ lvb->lvb_mtime = cl_inode_mtime(inode);
+ lvb->lvb_atime = cl_inode_atime(inode);
+ lvb->lvb_ctime = cl_inode_ctime(inode);
+ /*
+ * LU-417: Add dirty pages block count lest i_blocks reports 0, some
+ * "cp" or "tar" on remote node may think it's a completely sparse file
+ * and skip it.
+ */
+ if (lvb->lvb_size > 0 && lvb->lvb_blocks == 0)
+ lvb->lvb_blocks = dirty_cnt(inode);
+ return 0;
+}
+
+
+
+int ccc_conf_set(const struct lu_env *env, struct cl_object *obj,
+ const struct cl_object_conf *conf)
+{
+ /* TODO: destroy all pages attached to this object. */
+ return 0;
+}
+
+static void ccc_object_size_lock(struct cl_object *obj)
+{
+ struct inode *inode = ccc_object_inode(obj);
+
+ cl_isize_lock(inode);
+ cl_object_attr_lock(obj);
+}
+
+static void ccc_object_size_unlock(struct cl_object *obj)
+{
+ struct inode *inode = ccc_object_inode(obj);
+
+ cl_object_attr_unlock(obj);
+ cl_isize_unlock(inode);
+}
+
+/*****************************************************************************
+ *
+ * Page operations.
+ *
+ */
+
+struct page *ccc_page_vmpage(const struct lu_env *env,
+ const struct cl_page_slice *slice)
+{
+ return cl2vm_page(slice);
+}
+
+int ccc_page_is_under_lock(const struct lu_env *env,
+ const struct cl_page_slice *slice,
+ struct cl_io *io)
+{
+ struct ccc_io *cio = ccc_env_io(env);
+ struct cl_lock_descr *desc = &ccc_env_info(env)->cti_descr;
+ struct cl_page *page = slice->cpl_page;
+
+ int result;
+
+ if (io->ci_type == CIT_READ || io->ci_type == CIT_WRITE ||
+ io->ci_type == CIT_FAULT) {
+ if (cio->cui_fd->fd_flags & LL_FILE_GROUP_LOCKED)
+ result = -EBUSY;
+ else {
+ desc->cld_start = page->cp_index;
+ desc->cld_end = page->cp_index;
+ desc->cld_obj = page->cp_obj;
+ desc->cld_mode = CLM_READ;
+ result = cl_queue_match(&io->ci_lockset.cls_done,
+ desc) ? -EBUSY : 0;
+ }
+ } else
+ result = 0;
+ return result;
+}
+
+int ccc_fail(const struct lu_env *env, const struct cl_page_slice *slice)
+{
+ /*
+ * Cached read?
+ */
+ LBUG();
+ return 0;
+}
+
+void ccc_transient_page_verify(const struct cl_page *page)
+{
+}
+
+int ccc_transient_page_own(const struct lu_env *env,
+ const struct cl_page_slice *slice,
+ struct cl_io *unused,
+ int nonblock)
+{
+ ccc_transient_page_verify(slice->cpl_page);
+ return 0;
+}
+
+void ccc_transient_page_assume(const struct lu_env *env,
+ const struct cl_page_slice *slice,
+ struct cl_io *unused)
+{
+ ccc_transient_page_verify(slice->cpl_page);
+}
+
+void ccc_transient_page_unassume(const struct lu_env *env,
+ const struct cl_page_slice *slice,
+ struct cl_io *unused)
+{
+ ccc_transient_page_verify(slice->cpl_page);
+}
+
+void ccc_transient_page_disown(const struct lu_env *env,
+ const struct cl_page_slice *slice,
+ struct cl_io *unused)
+{
+ ccc_transient_page_verify(slice->cpl_page);
+}
+
+void ccc_transient_page_discard(const struct lu_env *env,
+ const struct cl_page_slice *slice,
+ struct cl_io *unused)
+{
+ struct cl_page *page = slice->cpl_page;
+
+ ccc_transient_page_verify(slice->cpl_page);
+
+ /*
+ * For transient pages, remove it from the radix tree.
+ */
+ cl_page_delete(env, page);
+}
+
+int ccc_transient_page_prep(const struct lu_env *env,
+ const struct cl_page_slice *slice,
+ struct cl_io *unused)
+{
+ /* transient page should always be sent. */
+ return 0;
+}
+
+/*****************************************************************************
+ *
+ * Lock operations.
+ *
+ */
+
+void ccc_lock_delete(const struct lu_env *env,
+ const struct cl_lock_slice *slice)
+{
+ CLOBINVRNT(env, slice->cls_obj, ccc_object_invariant(slice->cls_obj));
+}
+
+void ccc_lock_fini(const struct lu_env *env, struct cl_lock_slice *slice)
+{
+ struct ccc_lock *clk = cl2ccc_lock(slice);
+
+ OBD_SLAB_FREE_PTR(clk, ccc_lock_kmem);
+}
+
+int ccc_lock_enqueue(const struct lu_env *env,
+ const struct cl_lock_slice *slice,
+ struct cl_io *unused, __u32 enqflags)
+{
+ CLOBINVRNT(env, slice->cls_obj, ccc_object_invariant(slice->cls_obj));
+ return 0;
+}
+
+int ccc_lock_use(const struct lu_env *env, const struct cl_lock_slice *slice)
+{
+ CLOBINVRNT(env, slice->cls_obj, ccc_object_invariant(slice->cls_obj));
+ return 0;
+}
+
+int ccc_lock_unuse(const struct lu_env *env, const struct cl_lock_slice *slice)
+{
+ CLOBINVRNT(env, slice->cls_obj, ccc_object_invariant(slice->cls_obj));
+ return 0;
+}
+
+int ccc_lock_wait(const struct lu_env *env, const struct cl_lock_slice *slice)
+{
+ CLOBINVRNT(env, slice->cls_obj, ccc_object_invariant(slice->cls_obj));
+ return 0;
+}
+
+/**
+ * Implementation of cl_lock_operations::clo_fits_into() methods for ccc
+ * layer. This function is executed every time io finds an existing lock in
+ * the lock cache while creating new lock. This function has to decide whether
+ * cached lock "fits" into io.
+ *
+ * \param slice lock to be checked
+ * \param io IO that wants a lock.
+ *
+ * \see lov_lock_fits_into().
+ */
+int ccc_lock_fits_into(const struct lu_env *env,
+ const struct cl_lock_slice *slice,
+ const struct cl_lock_descr *need,
+ const struct cl_io *io)
+{
+ const struct cl_lock *lock = slice->cls_lock;
+ const struct cl_lock_descr *descr = &lock->cll_descr;
+ const struct ccc_io *cio = ccc_env_io(env);
+ int result;
+
+ /*
+ * Work around DLM peculiarity: it assumes that glimpse
+ * (LDLM_FL_HAS_INTENT) lock is always LCK_PR, and returns reads lock
+ * when asked for LCK_PW lock with LDLM_FL_HAS_INTENT flag set. Make
+ * sure that glimpse doesn't get CLM_WRITE top-lock, so that it
+ * doesn't enqueue CLM_WRITE sub-locks.
+ */
+ if (cio->cui_glimpse)
+ result = descr->cld_mode != CLM_WRITE;
+
+ /*
+ * Also, don't match incomplete write locks for read, otherwise read
+ * would enqueue missing sub-locks in the write mode.
+ */
+ else if (need->cld_mode != descr->cld_mode)
+ result = lock->cll_state >= CLS_ENQUEUED;
+ else
+ result = 1;
+ return result;
+}
+
+/**
+ * Implements cl_lock_operations::clo_state() method for ccc layer, invoked
+ * whenever lock state changes. Transfers object attributes, that might be
+ * updated as a result of lock acquiring into inode.
+ */
+void ccc_lock_state(const struct lu_env *env,
+ const struct cl_lock_slice *slice,
+ enum cl_lock_state state)
+{
+ struct cl_lock *lock = slice->cls_lock;
+
+ /*
+ * Refresh inode attributes when the lock is moving into CLS_HELD
+ * state, and only when this is a result of real enqueue, rather than
+ * of finding lock in the cache.
+ */
+ if (state == CLS_HELD && lock->cll_state < CLS_HELD) {
+ struct cl_object *obj;
+ struct inode *inode;
+
+ obj = slice->cls_obj;
+ inode = ccc_object_inode(obj);
+
+ /* vmtruncate() sets the i_size
+ * under both a DLM lock and the
+ * ll_inode_size_lock(). If we don't get the
+ * ll_inode_size_lock() here we can match the DLM lock and
+ * reset i_size. generic_file_write can then trust the
+ * stale i_size when doing appending writes and effectively
+ * cancel the result of the truncate. Getting the
+ * ll_inode_size_lock() after the enqueue maintains the DLM
+ * -> ll_inode_size_lock() acquiring order. */
+ if (lock->cll_descr.cld_start == 0 &&
+ lock->cll_descr.cld_end == CL_PAGE_EOF)
+ cl_merge_lvb(env, inode);
+ }
+}
+
+/*****************************************************************************
+ *
+ * io operations.
+ *
+ */
+
+void ccc_io_fini(const struct lu_env *env, const struct cl_io_slice *ios)
+{
+ struct cl_io *io = ios->cis_io;
+
+ CLOBINVRNT(env, io->ci_obj, ccc_object_invariant(io->ci_obj));
+}
+
+int ccc_io_one_lock_index(const struct lu_env *env, struct cl_io *io,
+ __u32 enqflags, enum cl_lock_mode mode,
+ pgoff_t start, pgoff_t end)
+{
+ struct ccc_io *cio = ccc_env_io(env);
+ struct cl_lock_descr *descr = &cio->cui_link.cill_descr;
+ struct cl_object *obj = io->ci_obj;
+
+ CLOBINVRNT(env, obj, ccc_object_invariant(obj));
+
+ CDEBUG(D_VFSTRACE, "lock: %d [%lu, %lu]\n", mode, start, end);
+
+ memset(&cio->cui_link, 0, sizeof(cio->cui_link));
+
+ if (cio->cui_fd && (cio->cui_fd->fd_flags & LL_FILE_GROUP_LOCKED)) {
+ descr->cld_mode = CLM_GROUP;
+ descr->cld_gid = cio->cui_fd->fd_grouplock.cg_gid;
+ } else {
+ descr->cld_mode = mode;
+ }
+ descr->cld_obj = obj;
+ descr->cld_start = start;
+ descr->cld_end = end;
+ descr->cld_enq_flags = enqflags;
+
+ cl_io_lock_add(env, io, &cio->cui_link);
+ return 0;
+}
+
+void ccc_io_update_iov(const struct lu_env *env,
+ struct ccc_io *cio, struct cl_io *io)
+{
+ size_t size = io->u.ci_rw.crw_count;
+
+ if (!cl_is_normalio(env, io) || cio->cui_iter == NULL)
+ return;
+
+ iov_iter_truncate(cio->cui_iter, size);
+}
+
+int ccc_io_one_lock(const struct lu_env *env, struct cl_io *io,
+ __u32 enqflags, enum cl_lock_mode mode,
+ loff_t start, loff_t end)
+{
+ struct cl_object *obj = io->ci_obj;
+
+ return ccc_io_one_lock_index(env, io, enqflags, mode,
+ cl_index(obj, start), cl_index(obj, end));
+}
+
+void ccc_io_end(const struct lu_env *env, const struct cl_io_slice *ios)
+{
+ CLOBINVRNT(env, ios->cis_io->ci_obj,
+ ccc_object_invariant(ios->cis_io->ci_obj));
+}
+
+void ccc_io_advance(const struct lu_env *env,
+ const struct cl_io_slice *ios,
+ size_t nob)
+{
+ struct ccc_io *cio = cl2ccc_io(env, ios);
+ struct cl_io *io = ios->cis_io;
+ struct cl_object *obj = ios->cis_io->ci_obj;
+
+ CLOBINVRNT(env, obj, ccc_object_invariant(obj));
+
+ if (!cl_is_normalio(env, io))
+ return;
+
+ iov_iter_reexpand(cio->cui_iter, cio->cui_tot_count -= nob);
+}
+
+/**
+ * Helper function that if necessary adjusts file size (inode->i_size), when
+ * position at the offset \a pos is accessed. File size can be arbitrary stale
+ * on a Lustre client, but client at least knows KMS. If accessed area is
+ * inside [0, KMS], set file size to KMS, otherwise glimpse file size.
+ *
+ * Locking: cl_isize_lock is used to serialize changes to inode size and to
+ * protect consistency between inode size and cl_object
+ * attributes. cl_object_size_lock() protects consistency between cl_attr's of
+ * top-object and sub-objects.
+ */
+int ccc_prep_size(const struct lu_env *env, struct cl_object *obj,
+ struct cl_io *io, loff_t start, size_t count, int *exceed)
+{
+ struct cl_attr *attr = ccc_env_thread_attr(env);
+ struct inode *inode = ccc_object_inode(obj);
+ loff_t pos = start + count - 1;
+ loff_t kms;
+ int result;
+
+ /*
+ * Consistency guarantees: following possibilities exist for the
+ * relation between region being accessed and real file size at this
+ * moment:
+ *
+ * (A): the region is completely inside of the file;
+ *
+ * (B-x): x bytes of region are inside of the file, the rest is
+ * outside;
+ *
+ * (C): the region is completely outside of the file.
+ *
+ * This classification is stable under DLM lock already acquired by
+ * the caller, because to change the class, other client has to take
+ * DLM lock conflicting with our lock. Also, any updates to ->i_size
+ * by other threads on this client are serialized by
+ * ll_inode_size_lock(). This guarantees that short reads are handled
+ * correctly in the face of concurrent writes and truncates.
+ */
+ ccc_object_size_lock(obj);
+ result = cl_object_attr_get(env, obj, attr);
+ if (result == 0) {
+ kms = attr->cat_kms;
+ if (pos > kms) {
+ /*
+ * A glimpse is necessary to determine whether we
+ * return a short read (B) or some zeroes at the end
+ * of the buffer (C)
+ */
+ ccc_object_size_unlock(obj);
+ result = cl_glimpse_lock(env, io, inode, obj, 0);
+ if (result == 0 && exceed != NULL) {
+ /* If objective page index exceed end-of-file
+ * page index, return directly. Do not expect
+ * kernel will check such case correctly.
+ * linux-2.6.18-128.1.1 miss to do that.
+ * --bug 17336 */
+ loff_t size = cl_isize_read(inode);
+ loff_t cur_index = start >> PAGE_CACHE_SHIFT;
+ loff_t size_index = (size - 1) >>
+ PAGE_CACHE_SHIFT;
+
+ if ((size == 0 && cur_index != 0) ||
+ size_index < cur_index)
+ *exceed = 1;
+ }
+ return result;
+ } else {
+ /*
+ * region is within kms and, hence, within real file
+ * size (A). We need to increase i_size to cover the
+ * read region so that generic_file_read() will do its
+ * job, but that doesn't mean the kms size is
+ * _correct_, it is only the _minimum_ size. If
+ * someone does a stat they will get the correct size
+ * which will always be >= the kms value here.
+ * b=11081
+ */
+ if (cl_isize_read(inode) < kms) {
+ cl_isize_write_nolock(inode, kms);
+ CDEBUG(D_VFSTRACE,
+ DFID" updating i_size %llu\n",
+ PFID(lu_object_fid(&obj->co_lu)),
+ (__u64)cl_isize_read(inode));
+
+ }
+ }
+ }
+ ccc_object_size_unlock(obj);
+ return result;
+}
+
+/*****************************************************************************
+ *
+ * Transfer operations.
+ *
+ */
+
+void ccc_req_completion(const struct lu_env *env,
+ const struct cl_req_slice *slice, int ioret)
+{
+ struct ccc_req *vrq;
+
+ if (ioret > 0)
+ cl_stats_tally(slice->crs_dev, slice->crs_req->crq_type, ioret);
+
+ vrq = cl2ccc_req(slice);
+ OBD_SLAB_FREE_PTR(vrq, ccc_req_kmem);
+}
+
+/**
+ * Implementation of struct cl_req_operations::cro_attr_set() for ccc
+ * layer. ccc is responsible for
+ *
+ * - o_[mac]time
+ *
+ * - o_mode
+ *
+ * - o_parent_seq
+ *
+ * - o_[ug]id
+ *
+ * - o_parent_oid
+ *
+ * - o_parent_ver
+ *
+ * - o_ioepoch,
+ *
+ * and capability.
+ */
+void ccc_req_attr_set(const struct lu_env *env,
+ const struct cl_req_slice *slice,
+ const struct cl_object *obj,
+ struct cl_req_attr *attr, u64 flags)
+{
+ struct inode *inode;
+ struct obdo *oa;
+ u32 valid_flags;
+
+ oa = attr->cra_oa;
+ inode = ccc_object_inode(obj);
+ valid_flags = OBD_MD_FLTYPE;
+
+ if ((flags & OBD_MD_FLOSSCAPA) != 0) {
+ LASSERT(attr->cra_capa == NULL);
+ attr->cra_capa = cl_capa_lookup(inode,
+ slice->crs_req->crq_type);
+ }
+
+ if (slice->crs_req->crq_type == CRT_WRITE) {
+ if (flags & OBD_MD_FLEPOCH) {
+ oa->o_valid |= OBD_MD_FLEPOCH;
+ oa->o_ioepoch = cl_i2info(inode)->lli_ioepoch;
+ valid_flags |= OBD_MD_FLMTIME | OBD_MD_FLCTIME |
+ OBD_MD_FLUID | OBD_MD_FLGID;
+ }
+ }
+ obdo_from_inode(oa, inode, valid_flags & flags);
+ obdo_set_parent_fid(oa, &cl_i2info(inode)->lli_fid);
+ memcpy(attr->cra_jobid, cl_i2info(inode)->lli_jobid,
+ JOBSTATS_JOBID_SIZE);
+}
+
+static const struct cl_req_operations ccc_req_ops = {
+ .cro_attr_set = ccc_req_attr_set,
+ .cro_completion = ccc_req_completion
+};
+
+int cl_setattr_ost(struct inode *inode, const struct iattr *attr,
+ struct obd_capa *capa)
+{
+ struct lu_env *env;
+ struct cl_io *io;
+ int result;
+ int refcheck;
+
+ env = cl_env_get(&refcheck);
+ if (IS_ERR(env))
+ return PTR_ERR(env);
+
+ io = ccc_env_thread_io(env);
+ io->ci_obj = cl_i2info(inode)->lli_clob;
+
+ io->u.ci_setattr.sa_attr.lvb_atime = LTIME_S(attr->ia_atime);
+ io->u.ci_setattr.sa_attr.lvb_mtime = LTIME_S(attr->ia_mtime);
+ io->u.ci_setattr.sa_attr.lvb_ctime = LTIME_S(attr->ia_ctime);
+ io->u.ci_setattr.sa_attr.lvb_size = attr->ia_size;
+ io->u.ci_setattr.sa_valid = attr->ia_valid;
+ io->u.ci_setattr.sa_capa = capa;
+
+again:
+ if (cl_io_init(env, io, CIT_SETATTR, io->ci_obj) == 0) {
+ struct ccc_io *cio = ccc_env_io(env);
+
+ if (attr->ia_valid & ATTR_FILE)
+ /* populate the file descriptor for ftruncate to honor
+ * group lock - see LU-787 */
+ cio->cui_fd = cl_iattr2fd(inode, attr);
+
+ result = cl_io_loop(env, io);
+ } else {
+ result = io->ci_result;
+ }
+ cl_io_fini(env, io);
+ if (unlikely(io->ci_need_restart))
+ goto again;
+ /* HSM import case: file is released, cannot be restored
+ * no need to fail except if restore registration failed
+ * with -ENODATA */
+ if (result == -ENODATA && io->ci_restore_needed &&
+ io->ci_result != -ENODATA)
+ result = 0;
+ cl_env_put(env, &refcheck);
+ return result;
+}
+
+/*****************************************************************************
+ *
+ * Type conversions.
+ *
+ */
+
+struct lu_device *ccc2lu_dev(struct ccc_device *vdv)
+{
+ return &vdv->cdv_cl.cd_lu_dev;
+}
+
+struct ccc_device *lu2ccc_dev(const struct lu_device *d)
+{
+ return container_of0(d, struct ccc_device, cdv_cl.cd_lu_dev);
+}
+
+struct ccc_device *cl2ccc_dev(const struct cl_device *d)
+{
+ return container_of0(d, struct ccc_device, cdv_cl);
+}
+
+struct lu_object *ccc2lu(struct ccc_object *vob)
+{
+ return &vob->cob_cl.co_lu;
+}
+
+struct ccc_object *lu2ccc(const struct lu_object *obj)
+{
+ return container_of0(obj, struct ccc_object, cob_cl.co_lu);
+}
+
+struct ccc_object *cl2ccc(const struct cl_object *obj)
+{
+ return container_of0(obj, struct ccc_object, cob_cl);
+}
+
+struct ccc_lock *cl2ccc_lock(const struct cl_lock_slice *slice)
+{
+ return container_of(slice, struct ccc_lock, clk_cl);
+}
+
+struct ccc_io *cl2ccc_io(const struct lu_env *env,
+ const struct cl_io_slice *slice)
+{
+ struct ccc_io *cio;
+
+ cio = container_of(slice, struct ccc_io, cui_cl);
+ LASSERT(cio == ccc_env_io(env));
+ return cio;
+}
+
+struct ccc_req *cl2ccc_req(const struct cl_req_slice *slice)
+{
+ return container_of0(slice, struct ccc_req, crq_cl);
+}
+
+struct page *cl2vm_page(const struct cl_page_slice *slice)
+{
+ return cl2ccc_page(slice)->cpg_page;
+}
+
+/*****************************************************************************
+ *
+ * Accessors.
+ *
+ */
+int ccc_object_invariant(const struct cl_object *obj)
+{
+ struct inode *inode = ccc_object_inode(obj);
+ struct cl_inode_info *lli = cl_i2info(inode);
+
+ return (S_ISREG(cl_inode_mode(inode)) ||
+ /* i_mode of unlinked inode is zeroed. */
+ cl_inode_mode(inode) == 0) && lli->lli_clob == obj;
+}
+
+struct inode *ccc_object_inode(const struct cl_object *obj)
+{
+ return cl2ccc(obj)->cob_inode;
+}
+
+/**
+ * Returns a pointer to cl_page associated with \a vmpage, without acquiring
+ * additional reference to the resulting page. This is an unsafe version of
+ * cl_vmpage_page() that can only be used under vmpage lock.
+ */
+struct cl_page *ccc_vmpage_page_transient(struct page *vmpage)
+{
+ KLASSERT(PageLocked(vmpage));
+ return (struct cl_page *)vmpage->private;
+}
+
+/**
+ * Initialize or update CLIO structures for regular files when new
+ * meta-data arrives from the server.
+ *
+ * \param inode regular file inode
+ * \param md new file metadata from MDS
+ * - allocates cl_object if necessary,
+ * - updated layout, if object was already here.
+ */
+int cl_file_inode_init(struct inode *inode, struct lustre_md *md)
+{
+ struct lu_env *env;
+ struct cl_inode_info *lli;
+ struct cl_object *clob;
+ struct lu_site *site;
+ struct lu_fid *fid;
+ struct cl_object_conf conf = {
+ .coc_inode = inode,
+ .u = {
+ .coc_md = md
+ }
+ };
+ int result = 0;
+ int refcheck;
+
+ LASSERT(md->body->valid & OBD_MD_FLID);
+ LASSERT(S_ISREG(cl_inode_mode(inode)));
+
+ env = cl_env_get(&refcheck);
+ if (IS_ERR(env))
+ return PTR_ERR(env);
+
+ site = cl_i2sbi(inode)->ll_site;
+ lli = cl_i2info(inode);
+ fid = &lli->lli_fid;
+ LASSERT(fid_is_sane(fid));
+
+ if (lli->lli_clob == NULL) {
+ /* clob is slave of inode, empty lli_clob means for new inode,
+ * there is no clob in cache with the given fid, so it is
+ * unnecessary to perform lookup-alloc-lookup-insert, just
+ * alloc and insert directly. */
+ LASSERT(inode->i_state & I_NEW);
+ conf.coc_lu.loc_flags = LOC_F_NEW;
+ clob = cl_object_find(env, lu2cl_dev(site->ls_top_dev),
+ fid, &conf);
+ if (!IS_ERR(clob)) {
+ /*
+ * No locking is necessary, as new inode is
+ * locked by I_NEW bit.
+ */
+ lli->lli_clob = clob;
+ lli->lli_has_smd = lsm_has_objects(md->lsm);
+ lu_object_ref_add(&clob->co_lu, "inode", inode);
+ } else
+ result = PTR_ERR(clob);
+ } else {
+ result = cl_conf_set(env, lli->lli_clob, &conf);
+ }
+
+ cl_env_put(env, &refcheck);
+
+ if (result != 0)
+ CERROR("Failure to initialize cl object "DFID": %d\n",
+ PFID(fid), result);
+ return result;
+}
+
+/**
+ * Wait for others drop their references of the object at first, then we drop
+ * the last one, which will lead to the object be destroyed immediately.
+ * Must be called after cl_object_kill() against this object.
+ *
+ * The reason we want to do this is: destroying top object will wait for sub
+ * objects being destroyed first, so we can't let bottom layer (e.g. from ASTs)
+ * to initiate top object destroying which may deadlock. See bz22520.
+ */
+static void cl_object_put_last(struct lu_env *env, struct cl_object *obj)
+{
+ struct lu_object_header *header = obj->co_lu.lo_header;
+ wait_queue_t waiter;
+
+ if (unlikely(atomic_read(&header->loh_ref) != 1)) {
+ struct lu_site *site = obj->co_lu.lo_dev->ld_site;
+ struct lu_site_bkt_data *bkt;
+
+ bkt = lu_site_bkt_from_fid(site, &header->loh_fid);
+
+ init_waitqueue_entry(&waiter, current);
+ add_wait_queue(&bkt->lsb_marche_funebre, &waiter);
+
+ while (1) {
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ if (atomic_read(&header->loh_ref) == 1)
+ break;
+ schedule();
+ }
+
+ set_current_state(TASK_RUNNING);
+ remove_wait_queue(&bkt->lsb_marche_funebre, &waiter);
+ }
+
+ cl_object_put(env, obj);
+}
+
+void cl_inode_fini(struct inode *inode)
+{
+ struct lu_env *env;
+ struct cl_inode_info *lli = cl_i2info(inode);
+ struct cl_object *clob = lli->lli_clob;
+ int refcheck;
+ int emergency;
+
+ if (clob != NULL) {
+ void *cookie;
+
+ cookie = cl_env_reenter();
+ env = cl_env_get(&refcheck);
+ emergency = IS_ERR(env);
+ if (emergency) {
+ mutex_lock(&ccc_inode_fini_guard);
+ LASSERT(ccc_inode_fini_env != NULL);
+ cl_env_implant(ccc_inode_fini_env, &refcheck);
+ env = ccc_inode_fini_env;
+ }
+ /*
+ * cl_object cache is a slave to inode cache (which, in turn
+ * is a slave to dentry cache), don't keep cl_object in memory
+ * when its master is evicted.
+ */
+ cl_object_kill(env, clob);
+ lu_object_ref_del(&clob->co_lu, "inode", inode);
+ cl_object_put_last(env, clob);
+ lli->lli_clob = NULL;
+ if (emergency) {
+ cl_env_unplant(ccc_inode_fini_env, &refcheck);
+ mutex_unlock(&ccc_inode_fini_guard);
+ } else
+ cl_env_put(env, &refcheck);
+ cl_env_reexit(cookie);
+ }
+}
+
+/**
+ * return IF_* type for given lu_dirent entry.
+ * IF_* flag shld be converted to particular OS file type in
+ * platform llite module.
+ */
+__u16 ll_dirent_type_get(struct lu_dirent *ent)
+{
+ __u16 type = 0;
+ struct luda_type *lt;
+ int len = 0;
+
+ if (le32_to_cpu(ent->lde_attrs) & LUDA_TYPE) {
+ const unsigned align = sizeof(struct luda_type) - 1;
+
+ len = le16_to_cpu(ent->lde_namelen);
+ len = (len + align) & ~align;
+ lt = (void *)ent->lde_name + len;
+ type = IFTODT(le16_to_cpu(lt->lt_type));
+ }
+ return type;
+}
+
+/**
+ * build inode number from passed @fid */
+__u64 cl_fid_build_ino(const struct lu_fid *fid, int api32)
+{
+ if (BITS_PER_LONG == 32 || api32)
+ return fid_flatten32(fid);
+ else
+ return fid_flatten(fid);
+}
+
+/**
+ * build inode generation from passed @fid. If our FID overflows the 32-bit
+ * inode number then return a non-zero generation to distinguish them. */
+__u32 cl_fid_build_gen(const struct lu_fid *fid)
+{
+ __u32 gen;
+
+ if (fid_is_igif(fid)) {
+ gen = lu_igif_gen(fid);
+ return gen;
+ }
+
+ gen = fid_flatten(fid) >> 32;
+ return gen;
+}
+
+/* lsm is unreliable after hsm implementation as layout can be changed at
+ * any time. This is only to support old, non-clio-ized interfaces. It will
+ * cause deadlock if clio operations are called with this extra layout refcount
+ * because in case the layout changed during the IO, ll_layout_refresh() will
+ * have to wait for the refcount to become zero to destroy the older layout.
+ *
+ * Notice that the lsm returned by this function may not be valid unless called
+ * inside layout lock - MDS_INODELOCK_LAYOUT. */
+struct lov_stripe_md *ccc_inode_lsm_get(struct inode *inode)
+{
+ return lov_lsm_get(cl_i2info(inode)->lli_clob);
+}
+
+inline void ccc_inode_lsm_put(struct inode *inode, struct lov_stripe_md *lsm)
+{
+ lov_lsm_put(cl_i2info(inode)->lli_clob, lsm);
+}
diff --git a/drivers/staging/lustre/lustre/lclient/lcommon_misc.c b/drivers/staging/lustre/lustre/lclient/lcommon_misc.c
new file mode 100644
index 000000000..01bf894d4
--- /dev/null
+++ b/drivers/staging/lustre/lustre/lclient/lcommon_misc.c
@@ -0,0 +1,199 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2011, 2012, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ *
+ * cl code shared between vvp and liblustre (and other Lustre clients in the
+ * future).
+ *
+ */
+#include "../include/obd_class.h"
+#include "../include/obd_support.h"
+#include "../include/obd.h"
+#include "../include/cl_object.h"
+#include "../include/lclient.h"
+
+#include "../include/lustre_lite.h"
+
+
+/* Initialize the default and maximum LOV EA and cookie sizes. This allows
+ * us to make MDS RPCs with large enough reply buffers to hold the
+ * maximum-sized (= maximum striped) EA and cookie without having to
+ * calculate this (via a call into the LOV + OSCs) each time we make an RPC. */
+int cl_init_ea_size(struct obd_export *md_exp, struct obd_export *dt_exp)
+{
+ struct lov_stripe_md lsm = { .lsm_magic = LOV_MAGIC_V3 };
+ __u32 valsize = sizeof(struct lov_desc);
+ int rc, easize, def_easize, cookiesize;
+ struct lov_desc desc;
+ __u16 stripes, def_stripes;
+
+ rc = obd_get_info(NULL, dt_exp, sizeof(KEY_LOVDESC), KEY_LOVDESC,
+ &valsize, &desc, NULL);
+ if (rc)
+ return rc;
+
+ stripes = min_t(__u32, desc.ld_tgt_count, LOV_MAX_STRIPE_COUNT);
+ lsm.lsm_stripe_count = stripes;
+ easize = obd_size_diskmd(dt_exp, &lsm);
+
+ def_stripes = min_t(__u32, desc.ld_default_stripe_count,
+ LOV_MAX_STRIPE_COUNT);
+ lsm.lsm_stripe_count = def_stripes;
+ def_easize = obd_size_diskmd(dt_exp, &lsm);
+
+ cookiesize = stripes * sizeof(struct llog_cookie);
+
+ /* default cookiesize is 0 because from 2.4 server doesn't send
+ * llog cookies to client. */
+ CDEBUG(D_HA,
+ "updating def/max_easize: %d/%d def/max_cookiesize: 0/%d\n",
+ def_easize, easize, cookiesize);
+
+ rc = md_init_ea_size(md_exp, easize, def_easize, cookiesize, 0);
+ return rc;
+}
+
+/**
+ * This function is used as an upcall-callback hooked by liblustre and llite
+ * clients into obd_notify() listeners chain to handle notifications about
+ * change of import connect_flags. See llu_fsswop_mount() and
+ * lustre_common_fill_super().
+ */
+int cl_ocd_update(struct obd_device *host,
+ struct obd_device *watched,
+ enum obd_notify_event ev, void *owner, void *data)
+{
+ struct lustre_client_ocd *lco;
+ struct client_obd *cli;
+ __u64 flags;
+ int result;
+
+ if (!strcmp(watched->obd_type->typ_name, LUSTRE_OSC_NAME)) {
+ cli = &watched->u.cli;
+ lco = owner;
+ flags = cli->cl_import->imp_connect_data.ocd_connect_flags;
+ CDEBUG(D_SUPER, "Changing connect_flags: %#llx -> %#llx\n",
+ lco->lco_flags, flags);
+ mutex_lock(&lco->lco_lock);
+ lco->lco_flags &= flags;
+ /* for each osc event update ea size */
+ if (lco->lco_dt_exp)
+ cl_init_ea_size(lco->lco_md_exp, lco->lco_dt_exp);
+
+ mutex_unlock(&lco->lco_lock);
+ result = 0;
+ } else {
+ CERROR("unexpected notification from %s %s!\n",
+ watched->obd_type->typ_name,
+ watched->obd_name);
+ result = -EINVAL;
+ }
+ return result;
+}
+
+#define GROUPLOCK_SCOPE "grouplock"
+
+int cl_get_grouplock(struct cl_object *obj, unsigned long gid, int nonblock,
+ struct ccc_grouplock *cg)
+{
+ struct lu_env *env;
+ struct cl_io *io;
+ struct cl_lock *lock;
+ struct cl_lock_descr *descr;
+ __u32 enqflags;
+ int refcheck;
+ int rc;
+
+ env = cl_env_get(&refcheck);
+ if (IS_ERR(env))
+ return PTR_ERR(env);
+
+ io = ccc_env_thread_io(env);
+ io->ci_obj = obj;
+ io->ci_ignore_layout = 1;
+
+ rc = cl_io_init(env, io, CIT_MISC, io->ci_obj);
+ if (rc) {
+ /* Does not make sense to take GL for released layout */
+ if (rc > 0)
+ rc = -ENOTSUPP;
+ cl_env_put(env, &refcheck);
+ return rc;
+ }
+
+ descr = &ccc_env_info(env)->cti_descr;
+ descr->cld_obj = obj;
+ descr->cld_start = 0;
+ descr->cld_end = CL_PAGE_EOF;
+ descr->cld_gid = gid;
+ descr->cld_mode = CLM_GROUP;
+
+ enqflags = CEF_MUST | (nonblock ? CEF_NONBLOCK : 0);
+ descr->cld_enq_flags = enqflags;
+
+ lock = cl_lock_request(env, io, descr, GROUPLOCK_SCOPE, current);
+ if (IS_ERR(lock)) {
+ cl_io_fini(env, io);
+ cl_env_put(env, &refcheck);
+ return PTR_ERR(lock);
+ }
+
+ cg->cg_env = cl_env_get(&refcheck);
+ cg->cg_io = io;
+ cg->cg_lock = lock;
+ cg->cg_gid = gid;
+ LASSERT(cg->cg_env == env);
+
+ cl_env_unplant(env, &refcheck);
+ return 0;
+}
+
+void cl_put_grouplock(struct ccc_grouplock *cg)
+{
+ struct lu_env *env = cg->cg_env;
+ struct cl_io *io = cg->cg_io;
+ struct cl_lock *lock = cg->cg_lock;
+ int refcheck;
+
+ LASSERT(cg->cg_env);
+ LASSERT(cg->cg_gid);
+
+ cl_env_implant(env, &refcheck);
+ cl_env_put(env, &refcheck);
+
+ cl_unuse(env, lock);
+ cl_lock_release(env, lock, GROUPLOCK_SCOPE, current);
+ cl_io_fini(env, io);
+ cl_env_put(env, NULL);
+}
diff --git a/drivers/staging/lustre/lustre/ldlm/interval_tree.c b/drivers/staging/lustre/lustre/ldlm/interval_tree.c
new file mode 100644
index 000000000..eab2bd602
--- /dev/null
+++ b/drivers/staging/lustre/lustre/ldlm/interval_tree.c
@@ -0,0 +1,751 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ *
+ * lustre/ldlm/interval_tree.c
+ *
+ * Interval tree library used by ldlm extent lock code
+ *
+ * Author: Huang Wei <huangwei@clusterfs.com>
+ * Author: Jay Xiong <jinshan.xiong@sun.com>
+ */
+#include "../include/lustre_dlm.h"
+#include "../include/obd_support.h"
+#include "../include/interval_tree.h"
+
+enum {
+ INTERVAL_RED = 0,
+ INTERVAL_BLACK = 1
+};
+
+static inline int node_is_left_child(struct interval_node *node)
+{
+ LASSERT(node->in_parent != NULL);
+ return node == node->in_parent->in_left;
+}
+
+static inline int node_is_right_child(struct interval_node *node)
+{
+ LASSERT(node->in_parent != NULL);
+ return node == node->in_parent->in_right;
+}
+
+static inline int node_is_red(struct interval_node *node)
+{
+ return node->in_color == INTERVAL_RED;
+}
+
+static inline int node_is_black(struct interval_node *node)
+{
+ return node->in_color == INTERVAL_BLACK;
+}
+
+static inline int extent_compare(struct interval_node_extent *e1,
+ struct interval_node_extent *e2)
+{
+ int rc;
+
+ if (e1->start == e2->start) {
+ if (e1->end < e2->end)
+ rc = -1;
+ else if (e1->end > e2->end)
+ rc = 1;
+ else
+ rc = 0;
+ } else {
+ if (e1->start < e2->start)
+ rc = -1;
+ else
+ rc = 1;
+ }
+ return rc;
+}
+
+static inline int extent_equal(struct interval_node_extent *e1,
+ struct interval_node_extent *e2)
+{
+ return (e1->start == e2->start) && (e1->end == e2->end);
+}
+
+static inline int extent_overlapped(struct interval_node_extent *e1,
+ struct interval_node_extent *e2)
+{
+ return (e1->start <= e2->end) && (e2->start <= e1->end);
+}
+
+static inline int node_compare(struct interval_node *n1,
+ struct interval_node *n2)
+{
+ return extent_compare(&n1->in_extent, &n2->in_extent);
+}
+
+static inline int node_equal(struct interval_node *n1,
+ struct interval_node *n2)
+{
+ return extent_equal(&n1->in_extent, &n2->in_extent);
+}
+
+static inline __u64 max_u64(__u64 x, __u64 y)
+{
+ return x > y ? x : y;
+}
+
+static inline __u64 min_u64(__u64 x, __u64 y)
+{
+ return x < y ? x : y;
+}
+
+#define interval_for_each(node, root) \
+for (node = interval_first(root); node != NULL; \
+ node = interval_next(node))
+
+#define interval_for_each_reverse(node, root) \
+for (node = interval_last(root); node != NULL; \
+ node = interval_prev(node))
+
+static struct interval_node *interval_first(struct interval_node *node)
+{
+ if (!node)
+ return NULL;
+ while (node->in_left)
+ node = node->in_left;
+ return node;
+}
+
+static struct interval_node *interval_last(struct interval_node *node)
+{
+ if (!node)
+ return NULL;
+ while (node->in_right)
+ node = node->in_right;
+ return node;
+}
+
+static struct interval_node *interval_next(struct interval_node *node)
+{
+ if (!node)
+ return NULL;
+ if (node->in_right)
+ return interval_first(node->in_right);
+ while (node->in_parent && node_is_right_child(node))
+ node = node->in_parent;
+ return node->in_parent;
+}
+
+static struct interval_node *interval_prev(struct interval_node *node)
+{
+ if (!node)
+ return NULL;
+
+ if (node->in_left)
+ return interval_last(node->in_left);
+
+ while (node->in_parent && node_is_left_child(node))
+ node = node->in_parent;
+
+ return node->in_parent;
+}
+
+enum interval_iter interval_iterate(struct interval_node *root,
+ interval_callback_t func,
+ void *data)
+{
+ struct interval_node *node;
+ enum interval_iter rc = INTERVAL_ITER_CONT;
+
+ interval_for_each(node, root) {
+ rc = func(node, data);
+ if (rc == INTERVAL_ITER_STOP)
+ break;
+ }
+
+ return rc;
+}
+EXPORT_SYMBOL(interval_iterate);
+
+enum interval_iter interval_iterate_reverse(struct interval_node *root,
+ interval_callback_t func,
+ void *data)
+{
+ struct interval_node *node;
+ enum interval_iter rc = INTERVAL_ITER_CONT;
+
+ interval_for_each_reverse(node, root) {
+ rc = func(node, data);
+ if (rc == INTERVAL_ITER_STOP)
+ break;
+ }
+
+ return rc;
+}
+EXPORT_SYMBOL(interval_iterate_reverse);
+
+/* try to find a node with same interval in the tree,
+ * if found, return the pointer to the node, otherwise return NULL*/
+struct interval_node *interval_find(struct interval_node *root,
+ struct interval_node_extent *ex)
+{
+ struct interval_node *walk = root;
+ int rc;
+
+ while (walk) {
+ rc = extent_compare(ex, &walk->in_extent);
+ if (rc == 0)
+ break;
+ else if (rc < 0)
+ walk = walk->in_left;
+ else
+ walk = walk->in_right;
+ }
+
+ return walk;
+}
+EXPORT_SYMBOL(interval_find);
+
+static void __rotate_change_maxhigh(struct interval_node *node,
+ struct interval_node *rotate)
+{
+ __u64 left_max, right_max;
+
+ rotate->in_max_high = node->in_max_high;
+ left_max = node->in_left ? node->in_left->in_max_high : 0;
+ right_max = node->in_right ? node->in_right->in_max_high : 0;
+ node->in_max_high = max_u64(interval_high(node),
+ max_u64(left_max, right_max));
+}
+
+/* The left rotation "pivots" around the link from node to node->right, and
+ * - node will be linked to node->right's left child, and
+ * - node->right's left child will be linked to node's right child. */
+static void __rotate_left(struct interval_node *node,
+ struct interval_node **root)
+{
+ struct interval_node *right = node->in_right;
+ struct interval_node *parent = node->in_parent;
+
+ node->in_right = right->in_left;
+ if (node->in_right)
+ right->in_left->in_parent = node;
+
+ right->in_left = node;
+ right->in_parent = parent;
+ if (parent) {
+ if (node_is_left_child(node))
+ parent->in_left = right;
+ else
+ parent->in_right = right;
+ } else {
+ *root = right;
+ }
+ node->in_parent = right;
+
+ /* update max_high for node and right */
+ __rotate_change_maxhigh(node, right);
+}
+
+/* The right rotation "pivots" around the link from node to node->left, and
+ * - node will be linked to node->left's right child, and
+ * - node->left's right child will be linked to node's left child. */
+static void __rotate_right(struct interval_node *node,
+ struct interval_node **root)
+{
+ struct interval_node *left = node->in_left;
+ struct interval_node *parent = node->in_parent;
+
+ node->in_left = left->in_right;
+ if (node->in_left)
+ left->in_right->in_parent = node;
+ left->in_right = node;
+
+ left->in_parent = parent;
+ if (parent) {
+ if (node_is_right_child(node))
+ parent->in_right = left;
+ else
+ parent->in_left = left;
+ } else {
+ *root = left;
+ }
+ node->in_parent = left;
+
+ /* update max_high for node and left */
+ __rotate_change_maxhigh(node, left);
+}
+
+#define interval_swap(a, b) do { \
+ struct interval_node *c = a; a = b; b = c; \
+} while (0)
+
+/*
+ * Operations INSERT and DELETE, when run on a tree with n keys,
+ * take O(logN) time.Because they modify the tree, the result
+ * may violate the red-black properties.To restore these properties,
+ * we must change the colors of some of the nodes in the tree
+ * and also change the pointer structure.
+ */
+static void interval_insert_color(struct interval_node *node,
+ struct interval_node **root)
+{
+ struct interval_node *parent, *gparent;
+
+ while ((parent = node->in_parent) && node_is_red(parent)) {
+ gparent = parent->in_parent;
+ /* Parent is RED, so gparent must not be NULL */
+ if (node_is_left_child(parent)) {
+ struct interval_node *uncle;
+
+ uncle = gparent->in_right;
+ if (uncle && node_is_red(uncle)) {
+ uncle->in_color = INTERVAL_BLACK;
+ parent->in_color = INTERVAL_BLACK;
+ gparent->in_color = INTERVAL_RED;
+ node = gparent;
+ continue;
+ }
+
+ if (parent->in_right == node) {
+ __rotate_left(parent, root);
+ interval_swap(node, parent);
+ }
+
+ parent->in_color = INTERVAL_BLACK;
+ gparent->in_color = INTERVAL_RED;
+ __rotate_right(gparent, root);
+ } else {
+ struct interval_node *uncle;
+
+ uncle = gparent->in_left;
+ if (uncle && node_is_red(uncle)) {
+ uncle->in_color = INTERVAL_BLACK;
+ parent->in_color = INTERVAL_BLACK;
+ gparent->in_color = INTERVAL_RED;
+ node = gparent;
+ continue;
+ }
+
+ if (node_is_left_child(node)) {
+ __rotate_right(parent, root);
+ interval_swap(node, parent);
+ }
+
+ parent->in_color = INTERVAL_BLACK;
+ gparent->in_color = INTERVAL_RED;
+ __rotate_left(gparent, root);
+ }
+ }
+
+ (*root)->in_color = INTERVAL_BLACK;
+}
+
+struct interval_node *interval_insert(struct interval_node *node,
+ struct interval_node **root)
+
+{
+ struct interval_node **p, *parent = NULL;
+
+ LASSERT(!interval_is_intree(node));
+ p = root;
+ while (*p) {
+ parent = *p;
+ if (node_equal(parent, node))
+ return parent;
+
+ /* max_high field must be updated after each iteration */
+ if (parent->in_max_high < interval_high(node))
+ parent->in_max_high = interval_high(node);
+
+ if (node_compare(node, parent) < 0)
+ p = &parent->in_left;
+ else
+ p = &parent->in_right;
+ }
+
+ /* link node into the tree */
+ node->in_parent = parent;
+ node->in_color = INTERVAL_RED;
+ node->in_left = node->in_right = NULL;
+ *p = node;
+
+ interval_insert_color(node, root);
+ node->in_intree = 1;
+
+ return NULL;
+}
+EXPORT_SYMBOL(interval_insert);
+
+static inline int node_is_black_or_0(struct interval_node *node)
+{
+ return !node || node_is_black(node);
+}
+
+static void interval_erase_color(struct interval_node *node,
+ struct interval_node *parent,
+ struct interval_node **root)
+{
+ struct interval_node *tmp;
+
+ while (node_is_black_or_0(node) && node != *root) {
+ if (parent->in_left == node) {
+ tmp = parent->in_right;
+ if (node_is_red(tmp)) {
+ tmp->in_color = INTERVAL_BLACK;
+ parent->in_color = INTERVAL_RED;
+ __rotate_left(parent, root);
+ tmp = parent->in_right;
+ }
+ if (node_is_black_or_0(tmp->in_left) &&
+ node_is_black_or_0(tmp->in_right)) {
+ tmp->in_color = INTERVAL_RED;
+ node = parent;
+ parent = node->in_parent;
+ } else {
+ if (node_is_black_or_0(tmp->in_right)) {
+ struct interval_node *o_left;
+
+ o_left = tmp->in_left;
+ if (o_left)
+ o_left->in_color = INTERVAL_BLACK;
+ tmp->in_color = INTERVAL_RED;
+ __rotate_right(tmp, root);
+ tmp = parent->in_right;
+ }
+ tmp->in_color = parent->in_color;
+ parent->in_color = INTERVAL_BLACK;
+ if (tmp->in_right)
+ tmp->in_right->in_color = INTERVAL_BLACK;
+ __rotate_left(parent, root);
+ node = *root;
+ break;
+ }
+ } else {
+ tmp = parent->in_left;
+ if (node_is_red(tmp)) {
+ tmp->in_color = INTERVAL_BLACK;
+ parent->in_color = INTERVAL_RED;
+ __rotate_right(parent, root);
+ tmp = parent->in_left;
+ }
+ if (node_is_black_or_0(tmp->in_left) &&
+ node_is_black_or_0(tmp->in_right)) {
+ tmp->in_color = INTERVAL_RED;
+ node = parent;
+ parent = node->in_parent;
+ } else {
+ if (node_is_black_or_0(tmp->in_left)) {
+ struct interval_node *o_right;
+
+ o_right = tmp->in_right;
+ if (o_right)
+ o_right->in_color = INTERVAL_BLACK;
+ tmp->in_color = INTERVAL_RED;
+ __rotate_left(tmp, root);
+ tmp = parent->in_left;
+ }
+ tmp->in_color = parent->in_color;
+ parent->in_color = INTERVAL_BLACK;
+ if (tmp->in_left)
+ tmp->in_left->in_color = INTERVAL_BLACK;
+ __rotate_right(parent, root);
+ node = *root;
+ break;
+ }
+ }
+ }
+ if (node)
+ node->in_color = INTERVAL_BLACK;
+}
+
+/*
+ * if the @max_high value of @node is changed, this function traverse a path
+ * from node up to the root to update max_high for the whole tree.
+ */
+static void update_maxhigh(struct interval_node *node,
+ __u64 old_maxhigh)
+{
+ __u64 left_max, right_max;
+
+ while (node) {
+ left_max = node->in_left ? node->in_left->in_max_high : 0;
+ right_max = node->in_right ? node->in_right->in_max_high : 0;
+ node->in_max_high = max_u64(interval_high(node),
+ max_u64(left_max, right_max));
+
+ if (node->in_max_high >= old_maxhigh)
+ break;
+ node = node->in_parent;
+ }
+}
+
+void interval_erase(struct interval_node *node,
+ struct interval_node **root)
+{
+ struct interval_node *child, *parent;
+ int color;
+
+ LASSERT(interval_is_intree(node));
+ node->in_intree = 0;
+ if (!node->in_left) {
+ child = node->in_right;
+ } else if (!node->in_right) {
+ child = node->in_left;
+ } else { /* Both left and right child are not NULL */
+ struct interval_node *old = node;
+
+ node = interval_next(node);
+ child = node->in_right;
+ parent = node->in_parent;
+ color = node->in_color;
+
+ if (child)
+ child->in_parent = parent;
+ if (parent == old)
+ parent->in_right = child;
+ else
+ parent->in_left = child;
+
+ node->in_color = old->in_color;
+ node->in_right = old->in_right;
+ node->in_left = old->in_left;
+ node->in_parent = old->in_parent;
+
+ if (old->in_parent) {
+ if (node_is_left_child(old))
+ old->in_parent->in_left = node;
+ else
+ old->in_parent->in_right = node;
+ } else {
+ *root = node;
+ }
+
+ old->in_left->in_parent = node;
+ if (old->in_right)
+ old->in_right->in_parent = node;
+ update_maxhigh(child ? : parent, node->in_max_high);
+ update_maxhigh(node, old->in_max_high);
+ if (parent == old)
+ parent = node;
+ goto color;
+ }
+ parent = node->in_parent;
+ color = node->in_color;
+
+ if (child)
+ child->in_parent = parent;
+ if (parent) {
+ if (node_is_left_child(node))
+ parent->in_left = child;
+ else
+ parent->in_right = child;
+ } else {
+ *root = child;
+ }
+
+ update_maxhigh(child ? : parent, node->in_max_high);
+
+color:
+ if (color == INTERVAL_BLACK)
+ interval_erase_color(child, parent, root);
+}
+EXPORT_SYMBOL(interval_erase);
+
+static inline int interval_may_overlap(struct interval_node *node,
+ struct interval_node_extent *ext)
+{
+ return (ext->start <= node->in_max_high &&
+ ext->end >= interval_low(node));
+}
+
+/*
+ * This function finds all intervals that overlap interval ext,
+ * and calls func to handle resulted intervals one by one.
+ * in lustre, this function will find all conflicting locks in
+ * the granted queue and add these locks to the ast work list.
+ *
+ * {
+ * if (node == NULL)
+ * return 0;
+ * if (ext->end < interval_low(node)) {
+ * interval_search(node->in_left, ext, func, data);
+ * } else if (interval_may_overlap(node, ext)) {
+ * if (extent_overlapped(ext, &node->in_extent))
+ * func(node, data);
+ * interval_search(node->in_left, ext, func, data);
+ * interval_search(node->in_right, ext, func, data);
+ * }
+ * return 0;
+ * }
+ *
+ */
+enum interval_iter interval_search(struct interval_node *node,
+ struct interval_node_extent *ext,
+ interval_callback_t func,
+ void *data)
+{
+ struct interval_node *parent;
+ enum interval_iter rc = INTERVAL_ITER_CONT;
+
+ LASSERT(ext != NULL);
+ LASSERT(func != NULL);
+
+ while (node) {
+ if (ext->end < interval_low(node)) {
+ if (node->in_left) {
+ node = node->in_left;
+ continue;
+ }
+ } else if (interval_may_overlap(node, ext)) {
+ if (extent_overlapped(ext, &node->in_extent)) {
+ rc = func(node, data);
+ if (rc == INTERVAL_ITER_STOP)
+ break;
+ }
+
+ if (node->in_left) {
+ node = node->in_left;
+ continue;
+ }
+ if (node->in_right) {
+ node = node->in_right;
+ continue;
+ }
+ }
+
+ parent = node->in_parent;
+ while (parent) {
+ if (node_is_left_child(node) &&
+ parent->in_right) {
+ /* If we ever got the left, it means that the
+ * parent met ext->end<interval_low(parent), or
+ * may_overlap(parent). If the former is true,
+ * we needn't go back. So stop early and check
+ * may_overlap(parent) after this loop. */
+ node = parent->in_right;
+ break;
+ }
+ node = parent;
+ parent = parent->in_parent;
+ }
+ if (parent == NULL || !interval_may_overlap(parent, ext))
+ break;
+ }
+
+ return rc;
+}
+EXPORT_SYMBOL(interval_search);
+
+static enum interval_iter interval_overlap_cb(struct interval_node *n,
+ void *args)
+{
+ *(int *)args = 1;
+ return INTERVAL_ITER_STOP;
+}
+
+int interval_is_overlapped(struct interval_node *root,
+ struct interval_node_extent *ext)
+{
+ int has = 0;
+ (void)interval_search(root, ext, interval_overlap_cb, &has);
+ return has;
+}
+EXPORT_SYMBOL(interval_is_overlapped);
+
+/* Don't expand to low. Expanding downwards is expensive, and meaningless to
+ * some extents, because programs seldom do IO backward.
+ *
+ * The recursive algorithm of expanding low:
+ * expand_low {
+ * struct interval_node *tmp;
+ * static __u64 res = 0;
+ *
+ * if (root == NULL)
+ * return res;
+ * if (root->in_max_high < low) {
+ * res = max_u64(root->in_max_high + 1, res);
+ * return res;
+ * } else if (low < interval_low(root)) {
+ * interval_expand_low(root->in_left, low);
+ * return res;
+ * }
+ *
+ * if (interval_high(root) < low)
+ * res = max_u64(interval_high(root) + 1, res);
+ * interval_expand_low(root->in_left, low);
+ * interval_expand_low(root->in_right, low);
+ *
+ * return res;
+ * }
+ *
+ * It's much easy to eliminate the recursion, see interval_search for
+ * an example. -jay
+ */
+static inline __u64 interval_expand_low(struct interval_node *root, __u64 low)
+{
+ /* we only concern the empty tree right now. */
+ if (root == NULL)
+ return 0;
+ return low;
+}
+
+static inline __u64 interval_expand_high(struct interval_node *node, __u64 high)
+{
+ __u64 result = ~0;
+
+ while (node != NULL) {
+ if (node->in_max_high < high)
+ break;
+
+ if (interval_low(node) > high) {
+ result = interval_low(node) - 1;
+ node = node->in_left;
+ } else {
+ node = node->in_right;
+ }
+ }
+
+ return result;
+}
+
+/* expanding the extent based on @ext. */
+void interval_expand(struct interval_node *root,
+ struct interval_node_extent *ext,
+ struct interval_node_extent *limiter)
+{
+ /* The assertion of interval_is_overlapped is expensive because we may
+ * travel many nodes to find the overlapped node. */
+ LASSERT(interval_is_overlapped(root, ext) == 0);
+ if (!limiter || limiter->start < ext->start)
+ ext->start = interval_expand_low(root, ext->start);
+ if (!limiter || limiter->end > ext->end)
+ ext->end = interval_expand_high(root, ext->end);
+ LASSERT(interval_is_overlapped(root, ext) == 0);
+}
+EXPORT_SYMBOL(interval_expand);
diff --git a/drivers/staging/lustre/lustre/ldlm/l_lock.c b/drivers/staging/lustre/lustre/ldlm/l_lock.c
new file mode 100644
index 000000000..cd8ab40e3
--- /dev/null
+++ b/drivers/staging/lustre/lustre/ldlm/l_lock.c
@@ -0,0 +1,76 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2002, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2012, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ */
+
+#define DEBUG_SUBSYSTEM S_LDLM
+#include "../../include/linux/libcfs/libcfs.h"
+
+#include "../include/lustre_dlm.h"
+#include "../include/lustre_lib.h"
+
+/**
+ * Lock a lock and its resource.
+ *
+ * LDLM locking uses resource to serialize access to locks
+ * but there is a case when we change resource of lock upon
+ * enqueue reply. We rely on lock->l_resource = new_res
+ * being an atomic operation.
+ */
+struct ldlm_resource *lock_res_and_lock(struct ldlm_lock *lock)
+{
+ /* on server-side resource of lock doesn't change */
+ if ((lock->l_flags & LDLM_FL_NS_SRV) == 0)
+ spin_lock(&lock->l_lock);
+
+ lock_res(lock->l_resource);
+
+ lock->l_flags |= LDLM_FL_RES_LOCKED;
+ return lock->l_resource;
+}
+EXPORT_SYMBOL(lock_res_and_lock);
+
+/**
+ * Unlock a lock and its resource previously locked with lock_res_and_lock
+ */
+void unlock_res_and_lock(struct ldlm_lock *lock)
+{
+ /* on server-side resource of lock doesn't change */
+ lock->l_flags &= ~LDLM_FL_RES_LOCKED;
+
+ unlock_res(lock->l_resource);
+ if ((lock->l_flags & LDLM_FL_NS_SRV) == 0)
+ spin_unlock(&lock->l_lock);
+}
+EXPORT_SYMBOL(unlock_res_and_lock);
diff --git a/drivers/staging/lustre/lustre/ldlm/ldlm_extent.c b/drivers/staging/lustre/lustre/ldlm/ldlm_extent.c
new file mode 100644
index 000000000..fd9b05936
--- /dev/null
+++ b/drivers/staging/lustre/lustre/ldlm/ldlm_extent.c
@@ -0,0 +1,241 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2002, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2010, 2012, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ *
+ * lustre/ldlm/ldlm_extent.c
+ *
+ * Author: Peter Braam <braam@clusterfs.com>
+ * Author: Phil Schwan <phil@clusterfs.com>
+ */
+
+/**
+ * This file contains implementation of EXTENT lock type
+ *
+ * EXTENT lock type is for locking a contiguous range of values, represented
+ * by 64-bit starting and ending offsets (inclusive). There are several extent
+ * lock modes, some of which may be mutually incompatible. Extent locks are
+ * considered incompatible if their modes are incompatible and their extents
+ * intersect. See the lock mode compatibility matrix in lustre_dlm.h.
+ */
+
+#define DEBUG_SUBSYSTEM S_LDLM
+#include "../../include/linux/libcfs/libcfs.h"
+#include "../include/lustre_dlm.h"
+#include "../include/obd_support.h"
+#include "../include/obd.h"
+#include "../include/obd_class.h"
+#include "../include/lustre_lib.h"
+#include "ldlm_internal.h"
+
+
+/* When a lock is cancelled by a client, the KMS may undergo change if this
+ * is the "highest lock". This function returns the new KMS value.
+ * Caller must hold lr_lock already.
+ *
+ * NB: A lock on [x,y] protects a KMS of up to y + 1 bytes! */
+__u64 ldlm_extent_shift_kms(struct ldlm_lock *lock, __u64 old_kms)
+{
+ struct ldlm_resource *res = lock->l_resource;
+ struct list_head *tmp;
+ struct ldlm_lock *lck;
+ __u64 kms = 0;
+
+ /* don't let another thread in ldlm_extent_shift_kms race in
+ * just after we finish and take our lock into account in its
+ * calculation of the kms */
+ lock->l_flags |= LDLM_FL_KMS_IGNORE;
+
+ list_for_each(tmp, &res->lr_granted) {
+ lck = list_entry(tmp, struct ldlm_lock, l_res_link);
+
+ if (lck->l_flags & LDLM_FL_KMS_IGNORE)
+ continue;
+
+ if (lck->l_policy_data.l_extent.end >= old_kms)
+ return old_kms;
+
+ /* This extent _has_ to be smaller than old_kms (checked above)
+ * so kms can only ever be smaller or the same as old_kms. */
+ if (lck->l_policy_data.l_extent.end + 1 > kms)
+ kms = lck->l_policy_data.l_extent.end + 1;
+ }
+ LASSERTF(kms <= old_kms, "kms %llu old_kms %llu\n", kms, old_kms);
+
+ return kms;
+}
+EXPORT_SYMBOL(ldlm_extent_shift_kms);
+
+struct kmem_cache *ldlm_interval_slab;
+struct ldlm_interval *ldlm_interval_alloc(struct ldlm_lock *lock)
+{
+ struct ldlm_interval *node;
+
+ LASSERT(lock->l_resource->lr_type == LDLM_EXTENT);
+ OBD_SLAB_ALLOC_PTR_GFP(node, ldlm_interval_slab, GFP_NOFS);
+ if (node == NULL)
+ return NULL;
+
+ INIT_LIST_HEAD(&node->li_group);
+ ldlm_interval_attach(node, lock);
+ return node;
+}
+
+void ldlm_interval_free(struct ldlm_interval *node)
+{
+ if (node) {
+ LASSERT(list_empty(&node->li_group));
+ LASSERT(!interval_is_intree(&node->li_node));
+ OBD_SLAB_FREE(node, ldlm_interval_slab, sizeof(*node));
+ }
+}
+
+/* interval tree, for LDLM_EXTENT. */
+void ldlm_interval_attach(struct ldlm_interval *n,
+ struct ldlm_lock *l)
+{
+ LASSERT(l->l_tree_node == NULL);
+ LASSERT(l->l_resource->lr_type == LDLM_EXTENT);
+
+ list_add_tail(&l->l_sl_policy, &n->li_group);
+ l->l_tree_node = n;
+}
+
+struct ldlm_interval *ldlm_interval_detach(struct ldlm_lock *l)
+{
+ struct ldlm_interval *n = l->l_tree_node;
+
+ if (n == NULL)
+ return NULL;
+
+ LASSERT(!list_empty(&n->li_group));
+ l->l_tree_node = NULL;
+ list_del_init(&l->l_sl_policy);
+
+ return list_empty(&n->li_group) ? n : NULL;
+}
+
+static inline int lock_mode_to_index(ldlm_mode_t mode)
+{
+ int index;
+
+ LASSERT(mode != 0);
+ LASSERT(IS_PO2(mode));
+ for (index = -1; mode; index++)
+ mode >>= 1;
+ LASSERT(index < LCK_MODE_NUM);
+ return index;
+}
+
+/** Add newly granted lock into interval tree for the resource. */
+void ldlm_extent_add_lock(struct ldlm_resource *res,
+ struct ldlm_lock *lock)
+{
+ struct interval_node *found, **root;
+ struct ldlm_interval *node;
+ struct ldlm_extent *extent;
+ int idx;
+
+ LASSERT(lock->l_granted_mode == lock->l_req_mode);
+
+ node = lock->l_tree_node;
+ LASSERT(node != NULL);
+ LASSERT(!interval_is_intree(&node->li_node));
+
+ idx = lock_mode_to_index(lock->l_granted_mode);
+ LASSERT(lock->l_granted_mode == 1 << idx);
+ LASSERT(lock->l_granted_mode == res->lr_itree[idx].lit_mode);
+
+ /* node extent initialize */
+ extent = &lock->l_policy_data.l_extent;
+ interval_set(&node->li_node, extent->start, extent->end);
+
+ root = &res->lr_itree[idx].lit_root;
+ found = interval_insert(&node->li_node, root);
+ if (found) { /* The policy group found. */
+ struct ldlm_interval *tmp;
+
+ tmp = ldlm_interval_detach(lock);
+ LASSERT(tmp != NULL);
+ ldlm_interval_free(tmp);
+ ldlm_interval_attach(to_ldlm_interval(found), lock);
+ }
+ res->lr_itree[idx].lit_size++;
+
+ /* even though we use interval tree to manage the extent lock, we also
+ * add the locks into grant list, for debug purpose, .. */
+ ldlm_resource_add_lock(res, &res->lr_granted, lock);
+}
+
+/** Remove cancelled lock from resource interval tree. */
+void ldlm_extent_unlink_lock(struct ldlm_lock *lock)
+{
+ struct ldlm_resource *res = lock->l_resource;
+ struct ldlm_interval *node = lock->l_tree_node;
+ struct ldlm_interval_tree *tree;
+ int idx;
+
+ if (!node || !interval_is_intree(&node->li_node)) /* duplicate unlink */
+ return;
+
+ idx = lock_mode_to_index(lock->l_granted_mode);
+ LASSERT(lock->l_granted_mode == 1 << idx);
+ tree = &res->lr_itree[idx];
+
+ LASSERT(tree->lit_root != NULL); /* assure the tree is not null */
+
+ tree->lit_size--;
+ node = ldlm_interval_detach(lock);
+ if (node) {
+ interval_erase(&node->li_node, &tree->lit_root);
+ ldlm_interval_free(node);
+ }
+}
+
+void ldlm_extent_policy_wire_to_local(const ldlm_wire_policy_data_t *wpolicy,
+ ldlm_policy_data_t *lpolicy)
+{
+ memset(lpolicy, 0, sizeof(*lpolicy));
+ lpolicy->l_extent.start = wpolicy->l_extent.start;
+ lpolicy->l_extent.end = wpolicy->l_extent.end;
+ lpolicy->l_extent.gid = wpolicy->l_extent.gid;
+}
+
+void ldlm_extent_policy_local_to_wire(const ldlm_policy_data_t *lpolicy,
+ ldlm_wire_policy_data_t *wpolicy)
+{
+ memset(wpolicy, 0, sizeof(*wpolicy));
+ wpolicy->l_extent.start = lpolicy->l_extent.start;
+ wpolicy->l_extent.end = lpolicy->l_extent.end;
+ wpolicy->l_extent.gid = lpolicy->l_extent.gid;
+}
diff --git a/drivers/staging/lustre/lustre/ldlm/ldlm_flock.c b/drivers/staging/lustre/lustre/ldlm/ldlm_flock.c
new file mode 100644
index 000000000..a4c252feb
--- /dev/null
+++ b/drivers/staging/lustre/lustre/ldlm/ldlm_flock.c
@@ -0,0 +1,859 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2003 Hewlett-Packard Development Company LP.
+ * Developed under the sponsorship of the US Government under
+ * Subcontract No. B514193
+ *
+ * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2010, 2012, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ */
+
+/**
+ * This file implements POSIX lock type for Lustre.
+ * Its policy properties are start and end of extent and PID.
+ *
+ * These locks are only done through MDS due to POSIX semantics requiring
+ * e.g. that locks could be only partially released and as such split into
+ * two parts, and also that two adjacent locks from the same process may be
+ * merged into a single wider lock.
+ *
+ * Lock modes are mapped like this:
+ * PR and PW for READ and WRITE locks
+ * NL to request a releasing of a portion of the lock
+ *
+ * These flock locks never timeout.
+ */
+
+#define DEBUG_SUBSYSTEM S_LDLM
+
+#include "../include/lustre_dlm.h"
+#include "../include/obd_support.h"
+#include "../include/obd_class.h"
+#include "../include/lustre_lib.h"
+#include <linux/list.h>
+#include "ldlm_internal.h"
+
+int ldlm_flock_blocking_ast(struct ldlm_lock *lock, struct ldlm_lock_desc *desc,
+ void *data, int flag);
+
+/**
+ * list_for_remaining_safe - iterate over the remaining entries in a list
+ * and safeguard against removal of a list entry.
+ * \param pos the &struct list_head to use as a loop counter. pos MUST
+ * have been initialized prior to using it in this macro.
+ * \param n another &struct list_head to use as temporary storage
+ * \param head the head for your list.
+ */
+#define list_for_remaining_safe(pos, n, head) \
+ for (n = pos->next; pos != (head); pos = n, n = pos->next)
+
+static inline int
+ldlm_same_flock_owner(struct ldlm_lock *lock, struct ldlm_lock *new)
+{
+ return((new->l_policy_data.l_flock.owner ==
+ lock->l_policy_data.l_flock.owner) &&
+ (new->l_export == lock->l_export));
+}
+
+static inline int
+ldlm_flocks_overlap(struct ldlm_lock *lock, struct ldlm_lock *new)
+{
+ return((new->l_policy_data.l_flock.start <=
+ lock->l_policy_data.l_flock.end) &&
+ (new->l_policy_data.l_flock.end >=
+ lock->l_policy_data.l_flock.start));
+}
+
+static inline void ldlm_flock_blocking_link(struct ldlm_lock *req,
+ struct ldlm_lock *lock)
+{
+ /* For server only */
+ if (req->l_export == NULL)
+ return;
+
+ LASSERT(hlist_unhashed(&req->l_exp_flock_hash));
+
+ req->l_policy_data.l_flock.blocking_owner =
+ lock->l_policy_data.l_flock.owner;
+ req->l_policy_data.l_flock.blocking_export =
+ lock->l_export;
+ req->l_policy_data.l_flock.blocking_refs = 0;
+
+ cfs_hash_add(req->l_export->exp_flock_hash,
+ &req->l_policy_data.l_flock.owner,
+ &req->l_exp_flock_hash);
+}
+
+static inline void ldlm_flock_blocking_unlink(struct ldlm_lock *req)
+{
+ /* For server only */
+ if (req->l_export == NULL)
+ return;
+
+ check_res_locked(req->l_resource);
+ if (req->l_export->exp_flock_hash != NULL &&
+ !hlist_unhashed(&req->l_exp_flock_hash))
+ cfs_hash_del(req->l_export->exp_flock_hash,
+ &req->l_policy_data.l_flock.owner,
+ &req->l_exp_flock_hash);
+}
+
+static inline void
+ldlm_flock_destroy(struct ldlm_lock *lock, ldlm_mode_t mode, __u64 flags)
+{
+ LDLM_DEBUG(lock, "ldlm_flock_destroy(mode: %d, flags: 0x%llx)",
+ mode, flags);
+
+ /* Safe to not lock here, since it should be empty anyway */
+ LASSERT(hlist_unhashed(&lock->l_exp_flock_hash));
+
+ list_del_init(&lock->l_res_link);
+ if (flags == LDLM_FL_WAIT_NOREPROC &&
+ !(lock->l_flags & LDLM_FL_FAILED)) {
+ /* client side - set a flag to prevent sending a CANCEL */
+ lock->l_flags |= LDLM_FL_LOCAL_ONLY | LDLM_FL_CBPENDING;
+
+ /* when reaching here, it is under lock_res_and_lock(). Thus,
+ need call the nolock version of ldlm_lock_decref_internal*/
+ ldlm_lock_decref_internal_nolock(lock, mode);
+ }
+
+ ldlm_lock_destroy_nolock(lock);
+}
+
+/**
+ * POSIX locks deadlock detection code.
+ *
+ * Given a new lock \a req and an existing lock \a bl_lock it conflicts
+ * with, we need to iterate through all blocked POSIX locks for this
+ * export and see if there is a deadlock condition arising. (i.e. when
+ * one client holds a lock on something and want a lock on something
+ * else and at the same time another client has the opposite situation).
+ */
+static int
+ldlm_flock_deadlock(struct ldlm_lock *req, struct ldlm_lock *bl_lock)
+{
+ struct obd_export *req_exp = req->l_export;
+ struct obd_export *bl_exp = bl_lock->l_export;
+ __u64 req_owner = req->l_policy_data.l_flock.owner;
+ __u64 bl_owner = bl_lock->l_policy_data.l_flock.owner;
+
+ /* For server only */
+ if (req_exp == NULL)
+ return 0;
+
+ class_export_get(bl_exp);
+ while (1) {
+ struct obd_export *bl_exp_new;
+ struct ldlm_lock *lock = NULL;
+ struct ldlm_flock *flock;
+
+ if (bl_exp->exp_flock_hash != NULL)
+ lock = cfs_hash_lookup(bl_exp->exp_flock_hash,
+ &bl_owner);
+ if (lock == NULL)
+ break;
+
+ LASSERT(req != lock);
+ flock = &lock->l_policy_data.l_flock;
+ LASSERT(flock->owner == bl_owner);
+ bl_owner = flock->blocking_owner;
+ bl_exp_new = class_export_get(flock->blocking_export);
+ class_export_put(bl_exp);
+
+ cfs_hash_put(bl_exp->exp_flock_hash, &lock->l_exp_flock_hash);
+ bl_exp = bl_exp_new;
+
+ if (bl_owner == req_owner && bl_exp == req_exp) {
+ class_export_put(bl_exp);
+ return 1;
+ }
+ }
+ class_export_put(bl_exp);
+
+ return 0;
+}
+
+static void ldlm_flock_cancel_on_deadlock(struct ldlm_lock *lock,
+ struct list_head *work_list)
+{
+ CDEBUG(D_INFO, "reprocess deadlock req=%p\n", lock);
+
+ if ((exp_connect_flags(lock->l_export) &
+ OBD_CONNECT_FLOCK_DEAD) == 0) {
+ CERROR(
+ "deadlock found, but client doesn't support flock canceliation\n");
+ } else {
+ LASSERT(lock->l_completion_ast);
+ LASSERT((lock->l_flags & LDLM_FL_AST_SENT) == 0);
+ lock->l_flags |= LDLM_FL_AST_SENT | LDLM_FL_CANCEL_ON_BLOCK |
+ LDLM_FL_FLOCK_DEADLOCK;
+ ldlm_flock_blocking_unlink(lock);
+ ldlm_resource_unlink_lock(lock);
+ ldlm_add_ast_work_item(lock, NULL, work_list);
+ }
+}
+
+/**
+ * Process a granting attempt for flock lock.
+ * Must be called under ns lock held.
+ *
+ * This function looks for any conflicts for \a lock in the granted or
+ * waiting queues. The lock is granted if no conflicts are found in
+ * either queue.
+ *
+ * It is also responsible for splitting a lock if a portion of the lock
+ * is released.
+ *
+ * If \a first_enq is 0 (ie, called from ldlm_reprocess_queue):
+ * - blocking ASTs have already been sent
+ *
+ * If \a first_enq is 1 (ie, called from ldlm_lock_enqueue):
+ * - blocking ASTs have not been sent yet, so list of conflicting locks
+ * would be collected and ASTs sent.
+ */
+int
+ldlm_process_flock_lock(struct ldlm_lock *req, __u64 *flags, int first_enq,
+ ldlm_error_t *err, struct list_head *work_list)
+{
+ struct ldlm_resource *res = req->l_resource;
+ struct ldlm_namespace *ns = ldlm_res_to_ns(res);
+ struct list_head *tmp;
+ struct list_head *ownlocks = NULL;
+ struct ldlm_lock *lock = NULL;
+ struct ldlm_lock *new = req;
+ struct ldlm_lock *new2 = NULL;
+ ldlm_mode_t mode = req->l_req_mode;
+ int local = ns_is_client(ns);
+ int added = (mode == LCK_NL);
+ int overlaps = 0;
+ int splitted = 0;
+ const struct ldlm_callback_suite null_cbs = { NULL };
+
+ CDEBUG(D_DLMTRACE,
+ "flags %#llx owner %llu pid %u mode %u start %llu end %llu\n",
+ *flags, new->l_policy_data.l_flock.owner,
+ new->l_policy_data.l_flock.pid, mode,
+ req->l_policy_data.l_flock.start,
+ req->l_policy_data.l_flock.end);
+
+ *err = ELDLM_OK;
+
+ if (local) {
+ /* No blocking ASTs are sent to the clients for
+ * Posix file & record locks */
+ req->l_blocking_ast = NULL;
+ } else {
+ /* Called on the server for lock cancels. */
+ req->l_blocking_ast = ldlm_flock_blocking_ast;
+ }
+
+reprocess:
+ if ((*flags == LDLM_FL_WAIT_NOREPROC) || (mode == LCK_NL)) {
+ /* This loop determines where this processes locks start
+ * in the resource lr_granted list. */
+ list_for_each(tmp, &res->lr_granted) {
+ lock = list_entry(tmp, struct ldlm_lock,
+ l_res_link);
+ if (ldlm_same_flock_owner(lock, req)) {
+ ownlocks = tmp;
+ break;
+ }
+ }
+ } else {
+ int reprocess_failed = 0;
+
+ lockmode_verify(mode);
+
+ /* This loop determines if there are existing locks
+ * that conflict with the new lock request. */
+ list_for_each(tmp, &res->lr_granted) {
+ lock = list_entry(tmp, struct ldlm_lock,
+ l_res_link);
+
+ if (ldlm_same_flock_owner(lock, req)) {
+ if (!ownlocks)
+ ownlocks = tmp;
+ continue;
+ }
+
+ /* locks are compatible, overlap doesn't matter */
+ if (lockmode_compat(lock->l_granted_mode, mode))
+ continue;
+
+ if (!ldlm_flocks_overlap(lock, req))
+ continue;
+
+ if (!first_enq) {
+ reprocess_failed = 1;
+ if (ldlm_flock_deadlock(req, lock)) {
+ ldlm_flock_cancel_on_deadlock(req,
+ work_list);
+ return LDLM_ITER_CONTINUE;
+ }
+ continue;
+ }
+
+ if (*flags & LDLM_FL_BLOCK_NOWAIT) {
+ ldlm_flock_destroy(req, mode, *flags);
+ *err = -EAGAIN;
+ return LDLM_ITER_STOP;
+ }
+
+ if (*flags & LDLM_FL_TEST_LOCK) {
+ ldlm_flock_destroy(req, mode, *flags);
+ req->l_req_mode = lock->l_granted_mode;
+ req->l_policy_data.l_flock.pid =
+ lock->l_policy_data.l_flock.pid;
+ req->l_policy_data.l_flock.start =
+ lock->l_policy_data.l_flock.start;
+ req->l_policy_data.l_flock.end =
+ lock->l_policy_data.l_flock.end;
+ *flags |= LDLM_FL_LOCK_CHANGED;
+ return LDLM_ITER_STOP;
+ }
+
+ /* add lock to blocking list before deadlock
+ * check to prevent race */
+ ldlm_flock_blocking_link(req, lock);
+
+ if (ldlm_flock_deadlock(req, lock)) {
+ ldlm_flock_blocking_unlink(req);
+ ldlm_flock_destroy(req, mode, *flags);
+ *err = -EDEADLK;
+ return LDLM_ITER_STOP;
+ }
+
+ ldlm_resource_add_lock(res, &res->lr_waiting, req);
+ *flags |= LDLM_FL_BLOCK_GRANTED;
+ return LDLM_ITER_STOP;
+ }
+ if (reprocess_failed)
+ return LDLM_ITER_CONTINUE;
+ }
+
+ if (*flags & LDLM_FL_TEST_LOCK) {
+ ldlm_flock_destroy(req, mode, *flags);
+ req->l_req_mode = LCK_NL;
+ *flags |= LDLM_FL_LOCK_CHANGED;
+ return LDLM_ITER_STOP;
+ }
+
+ /* In case we had slept on this lock request take it off of the
+ * deadlock detection hash list. */
+ ldlm_flock_blocking_unlink(req);
+
+ /* Scan the locks owned by this process that overlap this request.
+ * We may have to merge or split existing locks. */
+
+ if (!ownlocks)
+ ownlocks = &res->lr_granted;
+
+ list_for_remaining_safe(ownlocks, tmp, &res->lr_granted) {
+ lock = list_entry(ownlocks, struct ldlm_lock, l_res_link);
+
+ if (!ldlm_same_flock_owner(lock, new))
+ break;
+
+ if (lock->l_granted_mode == mode) {
+ /* If the modes are the same then we need to process
+ * locks that overlap OR adjoin the new lock. The extra
+ * logic condition is necessary to deal with arithmetic
+ * overflow and underflow. */
+ if ((new->l_policy_data.l_flock.start >
+ (lock->l_policy_data.l_flock.end + 1))
+ && (lock->l_policy_data.l_flock.end !=
+ OBD_OBJECT_EOF))
+ continue;
+
+ if ((new->l_policy_data.l_flock.end <
+ (lock->l_policy_data.l_flock.start - 1))
+ && (lock->l_policy_data.l_flock.start != 0))
+ break;
+
+ if (new->l_policy_data.l_flock.start <
+ lock->l_policy_data.l_flock.start) {
+ lock->l_policy_data.l_flock.start =
+ new->l_policy_data.l_flock.start;
+ } else {
+ new->l_policy_data.l_flock.start =
+ lock->l_policy_data.l_flock.start;
+ }
+
+ if (new->l_policy_data.l_flock.end >
+ lock->l_policy_data.l_flock.end) {
+ lock->l_policy_data.l_flock.end =
+ new->l_policy_data.l_flock.end;
+ } else {
+ new->l_policy_data.l_flock.end =
+ lock->l_policy_data.l_flock.end;
+ }
+
+ if (added) {
+ ldlm_flock_destroy(lock, mode, *flags);
+ } else {
+ new = lock;
+ added = 1;
+ }
+ continue;
+ }
+
+ if (new->l_policy_data.l_flock.start >
+ lock->l_policy_data.l_flock.end)
+ continue;
+
+ if (new->l_policy_data.l_flock.end <
+ lock->l_policy_data.l_flock.start)
+ break;
+
+ ++overlaps;
+
+ if (new->l_policy_data.l_flock.start <=
+ lock->l_policy_data.l_flock.start) {
+ if (new->l_policy_data.l_flock.end <
+ lock->l_policy_data.l_flock.end) {
+ lock->l_policy_data.l_flock.start =
+ new->l_policy_data.l_flock.end + 1;
+ break;
+ }
+ ldlm_flock_destroy(lock, lock->l_req_mode, *flags);
+ continue;
+ }
+ if (new->l_policy_data.l_flock.end >=
+ lock->l_policy_data.l_flock.end) {
+ lock->l_policy_data.l_flock.end =
+ new->l_policy_data.l_flock.start - 1;
+ continue;
+ }
+
+ /* split the existing lock into two locks */
+
+ /* if this is an F_UNLCK operation then we could avoid
+ * allocating a new lock and use the req lock passed in
+ * with the request but this would complicate the reply
+ * processing since updates to req get reflected in the
+ * reply. The client side replays the lock request so
+ * it must see the original lock data in the reply. */
+
+ /* XXX - if ldlm_lock_new() can sleep we should
+ * release the lr_lock, allocate the new lock,
+ * and restart processing this lock. */
+ if (!new2) {
+ unlock_res_and_lock(req);
+ new2 = ldlm_lock_create(ns, &res->lr_name, LDLM_FLOCK,
+ lock->l_granted_mode, &null_cbs,
+ NULL, 0, LVB_T_NONE);
+ lock_res_and_lock(req);
+ if (!new2) {
+ ldlm_flock_destroy(req, lock->l_granted_mode,
+ *flags);
+ *err = -ENOLCK;
+ return LDLM_ITER_STOP;
+ }
+ goto reprocess;
+ }
+
+ splitted = 1;
+
+ new2->l_granted_mode = lock->l_granted_mode;
+ new2->l_policy_data.l_flock.pid =
+ new->l_policy_data.l_flock.pid;
+ new2->l_policy_data.l_flock.owner =
+ new->l_policy_data.l_flock.owner;
+ new2->l_policy_data.l_flock.start =
+ lock->l_policy_data.l_flock.start;
+ new2->l_policy_data.l_flock.end =
+ new->l_policy_data.l_flock.start - 1;
+ lock->l_policy_data.l_flock.start =
+ new->l_policy_data.l_flock.end + 1;
+ new2->l_conn_export = lock->l_conn_export;
+ if (lock->l_export != NULL) {
+ new2->l_export = class_export_lock_get(lock->l_export,
+ new2);
+ if (new2->l_export->exp_lock_hash &&
+ hlist_unhashed(&new2->l_exp_hash))
+ cfs_hash_add(new2->l_export->exp_lock_hash,
+ &new2->l_remote_handle,
+ &new2->l_exp_hash);
+ }
+ if (*flags == LDLM_FL_WAIT_NOREPROC)
+ ldlm_lock_addref_internal_nolock(new2,
+ lock->l_granted_mode);
+
+ /* insert new2 at lock */
+ ldlm_resource_add_lock(res, ownlocks, new2);
+ LDLM_LOCK_RELEASE(new2);
+ break;
+ }
+
+ /* if new2 is created but never used, destroy it*/
+ if (splitted == 0 && new2 != NULL)
+ ldlm_lock_destroy_nolock(new2);
+
+ /* At this point we're granting the lock request. */
+ req->l_granted_mode = req->l_req_mode;
+
+ /* Add req to the granted queue before calling ldlm_reprocess_all(). */
+ if (!added) {
+ list_del_init(&req->l_res_link);
+ /* insert new lock before ownlocks in list. */
+ ldlm_resource_add_lock(res, ownlocks, req);
+ }
+
+ if (*flags != LDLM_FL_WAIT_NOREPROC) {
+ /* The only one possible case for client-side calls flock
+ * policy function is ldlm_flock_completion_ast inside which
+ * carries LDLM_FL_WAIT_NOREPROC flag. */
+ CERROR("Illegal parameter for client-side-only module.\n");
+ LBUG();
+ }
+
+ /* In case we're reprocessing the requested lock we can't destroy
+ * it until after calling ldlm_add_ast_work_item() above so that laawi()
+ * can bump the reference count on \a req. Otherwise \a req
+ * could be freed before the completion AST can be sent. */
+ if (added)
+ ldlm_flock_destroy(req, mode, *flags);
+
+ ldlm_resource_dump(D_INFO, res);
+ return LDLM_ITER_CONTINUE;
+}
+
+struct ldlm_flock_wait_data {
+ struct ldlm_lock *fwd_lock;
+ int fwd_generation;
+};
+
+static void
+ldlm_flock_interrupted_wait(void *data)
+{
+ struct ldlm_lock *lock;
+
+ lock = ((struct ldlm_flock_wait_data *)data)->fwd_lock;
+
+ /* take lock off the deadlock detection hash list. */
+ lock_res_and_lock(lock);
+ ldlm_flock_blocking_unlink(lock);
+
+ /* client side - set flag to prevent lock from being put on LRU list */
+ lock->l_flags |= LDLM_FL_CBPENDING;
+ unlock_res_and_lock(lock);
+}
+
+/**
+ * Flock completion callback function.
+ *
+ * \param lock [in,out]: A lock to be handled
+ * \param flags [in]: flags
+ * \param *data [in]: ldlm_work_cp_ast_lock() will use ldlm_cb_set_arg
+ *
+ * \retval 0 : success
+ * \retval <0 : failure
+ */
+int
+ldlm_flock_completion_ast(struct ldlm_lock *lock, __u64 flags, void *data)
+{
+ struct file_lock *getlk = lock->l_ast_data;
+ struct obd_device *obd;
+ struct obd_import *imp = NULL;
+ struct ldlm_flock_wait_data fwd;
+ struct l_wait_info lwi;
+ ldlm_error_t err;
+ int rc = 0;
+
+ CDEBUG(D_DLMTRACE, "flags: 0x%llx data: %p getlk: %p\n",
+ flags, data, getlk);
+
+ /* Import invalidation. We need to actually release the lock
+ * references being held, so that it can go away. No point in
+ * holding the lock even if app still believes it has it, since
+ * server already dropped it anyway. Only for granted locks too. */
+ if ((lock->l_flags & (LDLM_FL_FAILED|LDLM_FL_LOCAL_ONLY)) ==
+ (LDLM_FL_FAILED|LDLM_FL_LOCAL_ONLY)) {
+ if (lock->l_req_mode == lock->l_granted_mode &&
+ lock->l_granted_mode != LCK_NL &&
+ NULL == data)
+ ldlm_lock_decref_internal(lock, lock->l_req_mode);
+
+ /* Need to wake up the waiter if we were evicted */
+ wake_up(&lock->l_waitq);
+ return 0;
+ }
+
+ LASSERT(flags != LDLM_FL_WAIT_NOREPROC);
+
+ if (!(flags & (LDLM_FL_BLOCK_WAIT | LDLM_FL_BLOCK_GRANTED |
+ LDLM_FL_BLOCK_CONV))) {
+ if (NULL == data)
+ /* mds granted the lock in the reply */
+ goto granted;
+ /* CP AST RPC: lock get granted, wake it up */
+ wake_up(&lock->l_waitq);
+ return 0;
+ }
+
+ LDLM_DEBUG(lock, "client-side enqueue returned a blocked lock, sleeping");
+ fwd.fwd_lock = lock;
+ obd = class_exp2obd(lock->l_conn_export);
+
+ /* if this is a local lock, there is no import */
+ if (NULL != obd)
+ imp = obd->u.cli.cl_import;
+
+ if (NULL != imp) {
+ spin_lock(&imp->imp_lock);
+ fwd.fwd_generation = imp->imp_generation;
+ spin_unlock(&imp->imp_lock);
+ }
+
+ lwi = LWI_TIMEOUT_INTR(0, NULL, ldlm_flock_interrupted_wait, &fwd);
+
+ /* Go to sleep until the lock is granted. */
+ rc = l_wait_event(lock->l_waitq, is_granted_or_cancelled(lock), &lwi);
+
+ if (rc) {
+ LDLM_DEBUG(lock, "client-side enqueue waking up: failed (%d)",
+ rc);
+ return rc;
+ }
+
+granted:
+ OBD_FAIL_TIMEOUT(OBD_FAIL_LDLM_CP_CB_WAIT, 10);
+
+ if (lock->l_flags & LDLM_FL_DESTROYED) {
+ LDLM_DEBUG(lock, "client-side enqueue waking up: destroyed");
+ return 0;
+ }
+
+ if (lock->l_flags & LDLM_FL_FAILED) {
+ LDLM_DEBUG(lock, "client-side enqueue waking up: failed");
+ return -EIO;
+ }
+
+ if (rc) {
+ LDLM_DEBUG(lock, "client-side enqueue waking up: failed (%d)",
+ rc);
+ return rc;
+ }
+
+ LDLM_DEBUG(lock, "client-side enqueue granted");
+
+ lock_res_and_lock(lock);
+
+ /* take lock off the deadlock detection hash list. */
+ ldlm_flock_blocking_unlink(lock);
+
+ /* ldlm_lock_enqueue() has already placed lock on the granted list. */
+ list_del_init(&lock->l_res_link);
+
+ if (lock->l_flags & LDLM_FL_FLOCK_DEADLOCK) {
+ LDLM_DEBUG(lock, "client-side enqueue deadlock received");
+ rc = -EDEADLK;
+ } else if (flags & LDLM_FL_TEST_LOCK) {
+ /* fcntl(F_GETLK) request */
+ /* The old mode was saved in getlk->fl_type so that if the mode
+ * in the lock changes we can decref the appropriate refcount.*/
+ ldlm_flock_destroy(lock, getlk->fl_type, LDLM_FL_WAIT_NOREPROC);
+ switch (lock->l_granted_mode) {
+ case LCK_PR:
+ getlk->fl_type = F_RDLCK;
+ break;
+ case LCK_PW:
+ getlk->fl_type = F_WRLCK;
+ break;
+ default:
+ getlk->fl_type = F_UNLCK;
+ }
+ getlk->fl_pid = (pid_t)lock->l_policy_data.l_flock.pid;
+ getlk->fl_start = (loff_t)lock->l_policy_data.l_flock.start;
+ getlk->fl_end = (loff_t)lock->l_policy_data.l_flock.end;
+ } else {
+ __u64 noreproc = LDLM_FL_WAIT_NOREPROC;
+
+ /* We need to reprocess the lock to do merges or splits
+ * with existing locks owned by this process. */
+ ldlm_process_flock_lock(lock, &noreproc, 1, &err, NULL);
+ }
+ unlock_res_and_lock(lock);
+ return rc;
+}
+EXPORT_SYMBOL(ldlm_flock_completion_ast);
+
+int ldlm_flock_blocking_ast(struct ldlm_lock *lock, struct ldlm_lock_desc *desc,
+ void *data, int flag)
+{
+ LASSERT(lock);
+ LASSERT(flag == LDLM_CB_CANCELING);
+
+ /* take lock off the deadlock detection hash list. */
+ lock_res_and_lock(lock);
+ ldlm_flock_blocking_unlink(lock);
+ unlock_res_and_lock(lock);
+ return 0;
+}
+
+void ldlm_flock_policy_wire18_to_local(const ldlm_wire_policy_data_t *wpolicy,
+ ldlm_policy_data_t *lpolicy)
+{
+ memset(lpolicy, 0, sizeof(*lpolicy));
+ lpolicy->l_flock.start = wpolicy->l_flock.lfw_start;
+ lpolicy->l_flock.end = wpolicy->l_flock.lfw_end;
+ lpolicy->l_flock.pid = wpolicy->l_flock.lfw_pid;
+ /* Compat code, old clients had no idea about owner field and
+ * relied solely on pid for ownership. Introduced in LU-104, 2.1,
+ * April 2011 */
+ lpolicy->l_flock.owner = wpolicy->l_flock.lfw_pid;
+}
+
+
+void ldlm_flock_policy_wire21_to_local(const ldlm_wire_policy_data_t *wpolicy,
+ ldlm_policy_data_t *lpolicy)
+{
+ memset(lpolicy, 0, sizeof(*lpolicy));
+ lpolicy->l_flock.start = wpolicy->l_flock.lfw_start;
+ lpolicy->l_flock.end = wpolicy->l_flock.lfw_end;
+ lpolicy->l_flock.pid = wpolicy->l_flock.lfw_pid;
+ lpolicy->l_flock.owner = wpolicy->l_flock.lfw_owner;
+}
+
+void ldlm_flock_policy_local_to_wire(const ldlm_policy_data_t *lpolicy,
+ ldlm_wire_policy_data_t *wpolicy)
+{
+ memset(wpolicy, 0, sizeof(*wpolicy));
+ wpolicy->l_flock.lfw_start = lpolicy->l_flock.start;
+ wpolicy->l_flock.lfw_end = lpolicy->l_flock.end;
+ wpolicy->l_flock.lfw_pid = lpolicy->l_flock.pid;
+ wpolicy->l_flock.lfw_owner = lpolicy->l_flock.owner;
+}
+
+/*
+ * Export handle<->flock hash operations.
+ */
+static unsigned
+ldlm_export_flock_hash(struct cfs_hash *hs, const void *key, unsigned mask)
+{
+ return cfs_hash_u64_hash(*(__u64 *)key, mask);
+}
+
+static void *
+ldlm_export_flock_key(struct hlist_node *hnode)
+{
+ struct ldlm_lock *lock;
+
+ lock = hlist_entry(hnode, struct ldlm_lock, l_exp_flock_hash);
+ return &lock->l_policy_data.l_flock.owner;
+}
+
+static int
+ldlm_export_flock_keycmp(const void *key, struct hlist_node *hnode)
+{
+ return !memcmp(ldlm_export_flock_key(hnode), key, sizeof(__u64));
+}
+
+static void *
+ldlm_export_flock_object(struct hlist_node *hnode)
+{
+ return hlist_entry(hnode, struct ldlm_lock, l_exp_flock_hash);
+}
+
+static void
+ldlm_export_flock_get(struct cfs_hash *hs, struct hlist_node *hnode)
+{
+ struct ldlm_lock *lock;
+ struct ldlm_flock *flock;
+
+ lock = hlist_entry(hnode, struct ldlm_lock, l_exp_flock_hash);
+ LDLM_LOCK_GET(lock);
+
+ flock = &lock->l_policy_data.l_flock;
+ LASSERT(flock->blocking_export != NULL);
+ class_export_get(flock->blocking_export);
+ flock->blocking_refs++;
+}
+
+static void
+ldlm_export_flock_put(struct cfs_hash *hs, struct hlist_node *hnode)
+{
+ struct ldlm_lock *lock;
+ struct ldlm_flock *flock;
+
+ lock = hlist_entry(hnode, struct ldlm_lock, l_exp_flock_hash);
+ LDLM_LOCK_RELEASE(lock);
+
+ flock = &lock->l_policy_data.l_flock;
+ LASSERT(flock->blocking_export != NULL);
+ class_export_put(flock->blocking_export);
+ if (--flock->blocking_refs == 0) {
+ flock->blocking_owner = 0;
+ flock->blocking_export = NULL;
+ }
+}
+
+static cfs_hash_ops_t ldlm_export_flock_ops = {
+ .hs_hash = ldlm_export_flock_hash,
+ .hs_key = ldlm_export_flock_key,
+ .hs_keycmp = ldlm_export_flock_keycmp,
+ .hs_object = ldlm_export_flock_object,
+ .hs_get = ldlm_export_flock_get,
+ .hs_put = ldlm_export_flock_put,
+ .hs_put_locked = ldlm_export_flock_put,
+};
+
+int ldlm_init_flock_export(struct obd_export *exp)
+{
+ if (strcmp(exp->exp_obd->obd_type->typ_name, LUSTRE_MDT_NAME) != 0)
+ return 0;
+
+ exp->exp_flock_hash =
+ cfs_hash_create(obd_uuid2str(&exp->exp_client_uuid),
+ HASH_EXP_LOCK_CUR_BITS,
+ HASH_EXP_LOCK_MAX_BITS,
+ HASH_EXP_LOCK_BKT_BITS, 0,
+ CFS_HASH_MIN_THETA, CFS_HASH_MAX_THETA,
+ &ldlm_export_flock_ops,
+ CFS_HASH_DEFAULT | CFS_HASH_NBLK_CHANGE);
+ if (!exp->exp_flock_hash)
+ return -ENOMEM;
+
+ return 0;
+}
+EXPORT_SYMBOL(ldlm_init_flock_export);
+
+void ldlm_destroy_flock_export(struct obd_export *exp)
+{
+ if (exp->exp_flock_hash) {
+ cfs_hash_putref(exp->exp_flock_hash);
+ exp->exp_flock_hash = NULL;
+ }
+}
+EXPORT_SYMBOL(ldlm_destroy_flock_export);
diff --git a/drivers/staging/lustre/lustre/ldlm/ldlm_inodebits.c b/drivers/staging/lustre/lustre/ldlm/ldlm_inodebits.c
new file mode 100644
index 000000000..40d333850
--- /dev/null
+++ b/drivers/staging/lustre/lustre/ldlm/ldlm_inodebits.c
@@ -0,0 +1,74 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2011, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ *
+ * lustre/ldlm/ldlm_inodebits.c
+ *
+ * Author: Peter Braam <braam@clusterfs.com>
+ * Author: Phil Schwan <phil@clusterfs.com>
+ */
+
+/**
+ * This file contains implementation of IBITS lock type
+ *
+ * IBITS lock type contains a bit mask determining various properties of an
+ * object. The meanings of specific bits are specific to the caller and are
+ * opaque to LDLM code.
+ *
+ * Locks with intersecting bitmasks and conflicting lock modes (e.g. LCK_PW)
+ * are considered conflicting. See the lock mode compatibility matrix
+ * in lustre_dlm.h.
+ */
+
+#define DEBUG_SUBSYSTEM S_LDLM
+
+#include "../include/lustre_dlm.h"
+#include "../include/obd_support.h"
+#include "../include/lustre_lib.h"
+#include "ldlm_internal.h"
+
+
+void ldlm_ibits_policy_wire_to_local(const ldlm_wire_policy_data_t *wpolicy,
+ ldlm_policy_data_t *lpolicy)
+{
+ memset(lpolicy, 0, sizeof(*lpolicy));
+ lpolicy->l_inodebits.bits = wpolicy->l_inodebits.bits;
+}
+
+void ldlm_ibits_policy_local_to_wire(const ldlm_policy_data_t *lpolicy,
+ ldlm_wire_policy_data_t *wpolicy)
+{
+ memset(wpolicy, 0, sizeof(*wpolicy));
+ wpolicy->l_inodebits.bits = lpolicy->l_inodebits.bits;
+}
diff --git a/drivers/staging/lustre/lustre/ldlm/ldlm_internal.h b/drivers/staging/lustre/lustre/ldlm/ldlm_internal.h
new file mode 100644
index 000000000..70b909f55
--- /dev/null
+++ b/drivers/staging/lustre/lustre/ldlm/ldlm_internal.h
@@ -0,0 +1,316 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2011, 2012, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ */
+
+#define MAX_STRING_SIZE 128
+
+extern int ldlm_srv_namespace_nr;
+extern int ldlm_cli_namespace_nr;
+extern struct mutex ldlm_srv_namespace_lock;
+extern struct list_head ldlm_srv_namespace_list;
+extern struct mutex ldlm_cli_namespace_lock;
+extern struct list_head ldlm_cli_active_namespace_list;
+extern struct list_head ldlm_cli_inactive_namespace_list;
+
+static inline int ldlm_namespace_nr_read(ldlm_side_t client)
+{
+ return client == LDLM_NAMESPACE_SERVER ?
+ ldlm_srv_namespace_nr : ldlm_cli_namespace_nr;
+}
+
+static inline void ldlm_namespace_nr_inc(ldlm_side_t client)
+{
+ if (client == LDLM_NAMESPACE_SERVER)
+ ldlm_srv_namespace_nr++;
+ else
+ ldlm_cli_namespace_nr++;
+}
+
+static inline void ldlm_namespace_nr_dec(ldlm_side_t client)
+{
+ if (client == LDLM_NAMESPACE_SERVER)
+ ldlm_srv_namespace_nr--;
+ else
+ ldlm_cli_namespace_nr--;
+}
+
+static inline struct list_head *ldlm_namespace_list(ldlm_side_t client)
+{
+ return client == LDLM_NAMESPACE_SERVER ?
+ &ldlm_srv_namespace_list : &ldlm_cli_active_namespace_list;
+}
+
+static inline struct list_head *ldlm_namespace_inactive_list(ldlm_side_t client)
+{
+ return client == LDLM_NAMESPACE_SERVER ?
+ &ldlm_srv_namespace_list : &ldlm_cli_inactive_namespace_list;
+}
+
+static inline struct mutex *ldlm_namespace_lock(ldlm_side_t client)
+{
+ return client == LDLM_NAMESPACE_SERVER ?
+ &ldlm_srv_namespace_lock : &ldlm_cli_namespace_lock;
+}
+
+/* ns_bref is the number of resources in this namespace */
+static inline int ldlm_ns_empty(struct ldlm_namespace *ns)
+{
+ return atomic_read(&ns->ns_bref) == 0;
+}
+
+void ldlm_namespace_move_to_active_locked(struct ldlm_namespace *, ldlm_side_t);
+void ldlm_namespace_move_to_inactive_locked(struct ldlm_namespace *,
+ ldlm_side_t);
+struct ldlm_namespace *ldlm_namespace_first_locked(ldlm_side_t);
+
+/* ldlm_request.c */
+/* Cancel lru flag, it indicates we cancel aged locks. */
+enum {
+ LDLM_CANCEL_AGED = 1 << 0, /* Cancel aged locks (non lru resize). */
+ LDLM_CANCEL_PASSED = 1 << 1, /* Cancel passed number of locks. */
+ LDLM_CANCEL_SHRINK = 1 << 2, /* Cancel locks from shrinker. */
+ LDLM_CANCEL_LRUR = 1 << 3, /* Cancel locks from lru resize. */
+ LDLM_CANCEL_NO_WAIT = 1 << 4 /* Cancel locks w/o blocking (neither
+ * sending nor waiting for any rpcs) */
+};
+
+int ldlm_cancel_lru(struct ldlm_namespace *ns, int nr,
+ ldlm_cancel_flags_t sync, int flags);
+int ldlm_cancel_lru_local(struct ldlm_namespace *ns,
+ struct list_head *cancels, int count, int max,
+ ldlm_cancel_flags_t cancel_flags, int flags);
+extern int ldlm_enqueue_min;
+int ldlm_get_enq_timeout(struct ldlm_lock *lock);
+
+/* ldlm_resource.c */
+int ldlm_resource_putref_locked(struct ldlm_resource *res);
+void ldlm_resource_insert_lock_after(struct ldlm_lock *original,
+ struct ldlm_lock *new);
+void ldlm_namespace_free_prior(struct ldlm_namespace *ns,
+ struct obd_import *imp, int force);
+void ldlm_namespace_free_post(struct ldlm_namespace *ns);
+/* ldlm_lock.c */
+
+struct ldlm_cb_set_arg {
+ struct ptlrpc_request_set *set;
+ int type; /* LDLM_{CP,BL,GL}_CALLBACK */
+ atomic_t restart;
+ struct list_head *list;
+ union ldlm_gl_desc *gl_desc; /* glimpse AST descriptor */
+};
+
+enum ldlm_desc_ast_t {
+ LDLM_WORK_BL_AST,
+ LDLM_WORK_CP_AST,
+ LDLM_WORK_REVOKE_AST,
+ LDLM_WORK_GL_AST
+};
+
+void ldlm_grant_lock(struct ldlm_lock *lock, struct list_head *work_list);
+int ldlm_fill_lvb(struct ldlm_lock *lock, struct req_capsule *pill,
+ enum req_location loc, void *data, int size);
+struct ldlm_lock *
+ldlm_lock_create(struct ldlm_namespace *ns, const struct ldlm_res_id *,
+ ldlm_type_t type, ldlm_mode_t,
+ const struct ldlm_callback_suite *cbs,
+ void *data, __u32 lvb_len, enum lvb_type lvb_type);
+ldlm_error_t ldlm_lock_enqueue(struct ldlm_namespace *, struct ldlm_lock **,
+ void *cookie, __u64 *flags);
+void ldlm_lock_addref_internal(struct ldlm_lock *, __u32 mode);
+void ldlm_lock_addref_internal_nolock(struct ldlm_lock *, __u32 mode);
+void ldlm_lock_decref_internal(struct ldlm_lock *, __u32 mode);
+void ldlm_lock_decref_internal_nolock(struct ldlm_lock *, __u32 mode);
+void ldlm_add_ast_work_item(struct ldlm_lock *lock, struct ldlm_lock *new,
+ struct list_head *work_list);
+int ldlm_run_ast_work(struct ldlm_namespace *ns, struct list_head *rpc_list,
+ enum ldlm_desc_ast_t ast_type);
+int ldlm_work_gl_ast_lock(struct ptlrpc_request_set *rqset, void *opaq);
+int ldlm_lock_remove_from_lru(struct ldlm_lock *lock);
+int ldlm_lock_remove_from_lru_nolock(struct ldlm_lock *lock);
+void ldlm_lock_add_to_lru_nolock(struct ldlm_lock *lock);
+void ldlm_lock_add_to_lru(struct ldlm_lock *lock);
+void ldlm_lock_touch_in_lru(struct ldlm_lock *lock);
+void ldlm_lock_destroy_nolock(struct ldlm_lock *lock);
+
+void ldlm_cancel_locks_for_export(struct obd_export *export);
+
+/* ldlm_lockd.c */
+int ldlm_bl_to_thread_lock(struct ldlm_namespace *ns, struct ldlm_lock_desc *ld,
+ struct ldlm_lock *lock);
+int ldlm_bl_to_thread_list(struct ldlm_namespace *ns,
+ struct ldlm_lock_desc *ld,
+ struct list_head *cancels, int count,
+ ldlm_cancel_flags_t cancel_flags);
+
+void ldlm_handle_bl_callback(struct ldlm_namespace *ns,
+ struct ldlm_lock_desc *ld, struct ldlm_lock *lock);
+
+extern struct kmem_cache *ldlm_resource_slab;
+
+/* ldlm_lockd.c & ldlm_lock.c */
+extern struct kmem_cache *ldlm_lock_slab;
+
+/* ldlm_extent.c */
+void ldlm_extent_add_lock(struct ldlm_resource *res, struct ldlm_lock *lock);
+void ldlm_extent_unlink_lock(struct ldlm_lock *lock);
+
+/* ldlm_flock.c */
+int ldlm_process_flock_lock(struct ldlm_lock *req, __u64 *flags,
+ int first_enq, ldlm_error_t *err,
+ struct list_head *work_list);
+int ldlm_init_flock_export(struct obd_export *exp);
+void ldlm_destroy_flock_export(struct obd_export *exp);
+
+/* l_lock.c */
+void l_check_ns_lock(struct ldlm_namespace *ns);
+void l_check_no_ns_lock(struct ldlm_namespace *ns);
+
+extern struct proc_dir_entry *ldlm_svc_proc_dir;
+extern struct proc_dir_entry *ldlm_type_proc_dir;
+
+struct ldlm_state {
+ struct ptlrpc_service *ldlm_cb_service;
+ struct ptlrpc_service *ldlm_cancel_service;
+ struct ptlrpc_client *ldlm_client;
+ struct ptlrpc_connection *ldlm_server_conn;
+ struct ldlm_bl_pool *ldlm_bl_pool;
+};
+
+/* interval tree, for LDLM_EXTENT. */
+extern struct kmem_cache *ldlm_interval_slab; /* slab cache for ldlm_interval */
+extern void ldlm_interval_attach(struct ldlm_interval *n, struct ldlm_lock *l);
+struct ldlm_interval *ldlm_interval_detach(struct ldlm_lock *l);
+struct ldlm_interval *ldlm_interval_alloc(struct ldlm_lock *lock);
+void ldlm_interval_free(struct ldlm_interval *node);
+/* this function must be called with res lock held */
+static inline struct ldlm_extent *
+ldlm_interval_extent(struct ldlm_interval *node)
+{
+ struct ldlm_lock *lock;
+
+ LASSERT(!list_empty(&node->li_group));
+
+ lock = list_entry(node->li_group.next, struct ldlm_lock,
+ l_sl_policy);
+ return &lock->l_policy_data.l_extent;
+}
+
+int ldlm_init(void);
+void ldlm_exit(void);
+
+enum ldlm_policy_res {
+ LDLM_POLICY_CANCEL_LOCK,
+ LDLM_POLICY_KEEP_LOCK,
+ LDLM_POLICY_SKIP_LOCK
+};
+
+typedef enum ldlm_policy_res ldlm_policy_res_t;
+
+#define LDLM_POOL_PROC_READER_SEQ_SHOW(var, type) \
+ static int lprocfs_##var##_seq_show(struct seq_file *m, void *v) \
+ { \
+ struct ldlm_pool *pl = m->private; \
+ type tmp; \
+ \
+ spin_lock(&pl->pl_lock); \
+ tmp = pl->pl_##var; \
+ spin_unlock(&pl->pl_lock); \
+ \
+ return lprocfs_rd_uint(m, &tmp); \
+ } \
+ struct __##var##__dummy_read {; } /* semicolon catcher */
+
+#define LDLM_POOL_PROC_WRITER(var, type) \
+ static int lprocfs_wr_##var(struct file *file, \
+ const char __user *buffer, \
+ unsigned long count, void *data) \
+ { \
+ struct ldlm_pool *pl = data; \
+ type tmp; \
+ int rc; \
+ \
+ rc = lprocfs_wr_uint(file, buffer, count, &tmp); \
+ if (rc < 0) { \
+ CERROR("Can't parse user input, rc = %d\n", rc); \
+ return rc; \
+ } \
+ \
+ spin_lock(&pl->pl_lock); \
+ pl->pl_##var = tmp; \
+ spin_unlock(&pl->pl_lock); \
+ \
+ return rc; \
+ } \
+ struct __##var##__dummy_write {; } /* semicolon catcher */
+
+static inline int is_granted_or_cancelled(struct ldlm_lock *lock)
+{
+ int ret = 0;
+
+ lock_res_and_lock(lock);
+ if (((lock->l_req_mode == lock->l_granted_mode) &&
+ !(lock->l_flags & LDLM_FL_CP_REQD)) ||
+ (lock->l_flags & (LDLM_FL_FAILED | LDLM_FL_CANCEL)))
+ ret = 1;
+ unlock_res_and_lock(lock);
+
+ return ret;
+}
+
+typedef void (*ldlm_policy_wire_to_local_t)(const ldlm_wire_policy_data_t *,
+ ldlm_policy_data_t *);
+
+typedef void (*ldlm_policy_local_to_wire_t)(const ldlm_policy_data_t *,
+ ldlm_wire_policy_data_t *);
+
+void ldlm_plain_policy_wire_to_local(const ldlm_wire_policy_data_t *wpolicy,
+ ldlm_policy_data_t *lpolicy);
+void ldlm_plain_policy_local_to_wire(const ldlm_policy_data_t *lpolicy,
+ ldlm_wire_policy_data_t *wpolicy);
+void ldlm_ibits_policy_wire_to_local(const ldlm_wire_policy_data_t *wpolicy,
+ ldlm_policy_data_t *lpolicy);
+void ldlm_ibits_policy_local_to_wire(const ldlm_policy_data_t *lpolicy,
+ ldlm_wire_policy_data_t *wpolicy);
+void ldlm_extent_policy_wire_to_local(const ldlm_wire_policy_data_t *wpolicy,
+ ldlm_policy_data_t *lpolicy);
+void ldlm_extent_policy_local_to_wire(const ldlm_policy_data_t *lpolicy,
+ ldlm_wire_policy_data_t *wpolicy);
+void ldlm_flock_policy_wire18_to_local(const ldlm_wire_policy_data_t *wpolicy,
+ ldlm_policy_data_t *lpolicy);
+void ldlm_flock_policy_wire21_to_local(const ldlm_wire_policy_data_t *wpolicy,
+ ldlm_policy_data_t *lpolicy);
+
+void ldlm_flock_policy_local_to_wire(const ldlm_policy_data_t *lpolicy,
+ ldlm_wire_policy_data_t *wpolicy);
diff --git a/drivers/staging/lustre/lustre/ldlm/ldlm_lib.c b/drivers/staging/lustre/lustre/ldlm/ldlm_lib.c
new file mode 100644
index 000000000..c5c86e73c
--- /dev/null
+++ b/drivers/staging/lustre/lustre/ldlm/ldlm_lib.c
@@ -0,0 +1,870 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2010, 2012, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ */
+
+/**
+ * This file deals with various client/target related logic including recovery.
+ *
+ * TODO: This code more logically belongs in the ptlrpc module than in ldlm and
+ * should be moved.
+ */
+
+#define DEBUG_SUBSYSTEM S_LDLM
+
+#include "../../include/linux/libcfs/libcfs.h"
+#include "../include/obd.h"
+#include "../include/obd_class.h"
+#include "../include/lustre_dlm.h"
+#include "../include/lustre_net.h"
+#include "../include/lustre_sec.h"
+#include "ldlm_internal.h"
+
+/* @priority: If non-zero, move the selected connection to the list head.
+ * @create: If zero, only search in existing connections.
+ */
+static int import_set_conn(struct obd_import *imp, struct obd_uuid *uuid,
+ int priority, int create)
+{
+ struct ptlrpc_connection *ptlrpc_conn;
+ struct obd_import_conn *imp_conn = NULL, *item;
+ int rc = 0;
+
+ if (!create && !priority) {
+ CDEBUG(D_HA, "Nothing to do\n");
+ return -EINVAL;
+ }
+
+ ptlrpc_conn = ptlrpc_uuid_to_connection(uuid);
+ if (!ptlrpc_conn) {
+ CDEBUG(D_HA, "can't find connection %s\n", uuid->uuid);
+ return -ENOENT;
+ }
+
+ if (create) {
+ OBD_ALLOC(imp_conn, sizeof(*imp_conn));
+ if (!imp_conn) {
+ rc = -ENOMEM;
+ goto out_put;
+ }
+ }
+
+ spin_lock(&imp->imp_lock);
+ list_for_each_entry(item, &imp->imp_conn_list, oic_item) {
+ if (obd_uuid_equals(uuid, &item->oic_uuid)) {
+ if (priority) {
+ list_del(&item->oic_item);
+ list_add(&item->oic_item,
+ &imp->imp_conn_list);
+ item->oic_last_attempt = 0;
+ }
+ CDEBUG(D_HA, "imp %p@%s: found existing conn %s%s\n",
+ imp, imp->imp_obd->obd_name, uuid->uuid,
+ (priority ? ", moved to head" : ""));
+ spin_unlock(&imp->imp_lock);
+ rc = 0;
+ goto out_free;
+ }
+ }
+ /* No existing import connection found for \a uuid. */
+ if (create) {
+ imp_conn->oic_conn = ptlrpc_conn;
+ imp_conn->oic_uuid = *uuid;
+ imp_conn->oic_last_attempt = 0;
+ if (priority)
+ list_add(&imp_conn->oic_item, &imp->imp_conn_list);
+ else
+ list_add_tail(&imp_conn->oic_item,
+ &imp->imp_conn_list);
+ CDEBUG(D_HA, "imp %p@%s: add connection %s at %s\n",
+ imp, imp->imp_obd->obd_name, uuid->uuid,
+ (priority ? "head" : "tail"));
+ } else {
+ spin_unlock(&imp->imp_lock);
+ rc = -ENOENT;
+ goto out_free;
+ }
+
+ spin_unlock(&imp->imp_lock);
+ return 0;
+out_free:
+ if (imp_conn)
+ OBD_FREE(imp_conn, sizeof(*imp_conn));
+out_put:
+ ptlrpc_connection_put(ptlrpc_conn);
+ return rc;
+}
+
+int import_set_conn_priority(struct obd_import *imp, struct obd_uuid *uuid)
+{
+ return import_set_conn(imp, uuid, 1, 0);
+}
+
+int client_import_add_conn(struct obd_import *imp, struct obd_uuid *uuid,
+ int priority)
+{
+ return import_set_conn(imp, uuid, priority, 1);
+}
+EXPORT_SYMBOL(client_import_add_conn);
+
+int client_import_del_conn(struct obd_import *imp, struct obd_uuid *uuid)
+{
+ struct obd_import_conn *imp_conn;
+ struct obd_export *dlmexp;
+ int rc = -ENOENT;
+
+ spin_lock(&imp->imp_lock);
+ if (list_empty(&imp->imp_conn_list)) {
+ LASSERT(!imp->imp_connection);
+ goto out;
+ }
+
+ list_for_each_entry(imp_conn, &imp->imp_conn_list, oic_item) {
+ if (!obd_uuid_equals(uuid, &imp_conn->oic_uuid))
+ continue;
+ LASSERT(imp_conn->oic_conn);
+
+ if (imp_conn == imp->imp_conn_current) {
+ LASSERT(imp_conn->oic_conn == imp->imp_connection);
+
+ if (imp->imp_state != LUSTRE_IMP_CLOSED &&
+ imp->imp_state != LUSTRE_IMP_DISCON) {
+ CERROR("can't remove current connection\n");
+ rc = -EBUSY;
+ goto out;
+ }
+
+ ptlrpc_connection_put(imp->imp_connection);
+ imp->imp_connection = NULL;
+
+ dlmexp = class_conn2export(&imp->imp_dlm_handle);
+ if (dlmexp && dlmexp->exp_connection) {
+ LASSERT(dlmexp->exp_connection ==
+ imp_conn->oic_conn);
+ ptlrpc_connection_put(dlmexp->exp_connection);
+ dlmexp->exp_connection = NULL;
+ }
+ }
+
+ list_del(&imp_conn->oic_item);
+ ptlrpc_connection_put(imp_conn->oic_conn);
+ OBD_FREE(imp_conn, sizeof(*imp_conn));
+ CDEBUG(D_HA, "imp %p@%s: remove connection %s\n",
+ imp, imp->imp_obd->obd_name, uuid->uuid);
+ rc = 0;
+ break;
+ }
+out:
+ spin_unlock(&imp->imp_lock);
+ if (rc == -ENOENT)
+ CERROR("connection %s not found\n", uuid->uuid);
+ return rc;
+}
+EXPORT_SYMBOL(client_import_del_conn);
+
+/**
+ * Find conn UUID by peer NID. \a peer is a server NID. This function is used
+ * to find a conn uuid of \a imp which can reach \a peer.
+ */
+int client_import_find_conn(struct obd_import *imp, lnet_nid_t peer,
+ struct obd_uuid *uuid)
+{
+ struct obd_import_conn *conn;
+ int rc = -ENOENT;
+
+ spin_lock(&imp->imp_lock);
+ list_for_each_entry(conn, &imp->imp_conn_list, oic_item) {
+ /* Check if conn UUID does have this peer NID. */
+ if (class_check_uuid(&conn->oic_uuid, peer)) {
+ *uuid = conn->oic_uuid;
+ rc = 0;
+ break;
+ }
+ }
+ spin_unlock(&imp->imp_lock);
+ return rc;
+}
+EXPORT_SYMBOL(client_import_find_conn);
+
+void client_destroy_import(struct obd_import *imp)
+{
+ /* Drop security policy instance after all RPCs have finished/aborted
+ * to let all busy contexts be released. */
+ class_import_get(imp);
+ class_destroy_import(imp);
+ sptlrpc_import_sec_put(imp);
+ class_import_put(imp);
+}
+EXPORT_SYMBOL(client_destroy_import);
+
+/**
+ * Check whether or not the OSC is on MDT.
+ * In the config log,
+ * osc on MDT
+ * setup 0:{fsname}-OSTxxxx-osc[-MDTxxxx] 1:lustre-OST0000_UUID 2:NID
+ * osc on client
+ * setup 0:{fsname}-OSTxxxx-osc 1:lustre-OST0000_UUID 2:NID
+ *
+ **/
+static int osc_on_mdt(char *obdname)
+{
+ char *ptr;
+
+ ptr = strrchr(obdname, '-');
+ if (ptr == NULL)
+ return 0;
+
+ if (strncmp(ptr + 1, "MDT", 3) == 0)
+ return 1;
+
+ return 0;
+}
+
+/* Configure an RPC client OBD device.
+ *
+ * lcfg parameters:
+ * 1 - client UUID
+ * 2 - server UUID
+ * 3 - inactive-on-startup
+ */
+int client_obd_setup(struct obd_device *obddev, struct lustre_cfg *lcfg)
+{
+ struct client_obd *cli = &obddev->u.cli;
+ struct obd_import *imp;
+ struct obd_uuid server_uuid;
+ int rq_portal, rp_portal, connect_op;
+ char *name = obddev->obd_type->typ_name;
+ ldlm_ns_type_t ns_type = LDLM_NS_TYPE_UNKNOWN;
+ int rc;
+
+ /* In a more perfect world, we would hang a ptlrpc_client off of
+ * obd_type and just use the values from there. */
+ if (!strcmp(name, LUSTRE_OSC_NAME)) {
+ rq_portal = OST_REQUEST_PORTAL;
+ rp_portal = OSC_REPLY_PORTAL;
+ connect_op = OST_CONNECT;
+ cli->cl_sp_me = LUSTRE_SP_CLI;
+ cli->cl_sp_to = LUSTRE_SP_OST;
+ ns_type = LDLM_NS_TYPE_OSC;
+ } else if (!strcmp(name, LUSTRE_MDC_NAME) ||
+ !strcmp(name, LUSTRE_LWP_NAME)) {
+ rq_portal = MDS_REQUEST_PORTAL;
+ rp_portal = MDC_REPLY_PORTAL;
+ connect_op = MDS_CONNECT;
+ cli->cl_sp_me = LUSTRE_SP_CLI;
+ cli->cl_sp_to = LUSTRE_SP_MDT;
+ ns_type = LDLM_NS_TYPE_MDC;
+ } else if (!strcmp(name, LUSTRE_OSP_NAME)) {
+ if (strstr(lustre_cfg_buf(lcfg, 1), "OST") == NULL) {
+ /* OSP_on_MDT for other MDTs */
+ connect_op = MDS_CONNECT;
+ cli->cl_sp_to = LUSTRE_SP_MDT;
+ ns_type = LDLM_NS_TYPE_MDC;
+ rq_portal = OUT_PORTAL;
+ } else {
+ /* OSP on MDT for OST */
+ connect_op = OST_CONNECT;
+ cli->cl_sp_to = LUSTRE_SP_OST;
+ ns_type = LDLM_NS_TYPE_OSC;
+ rq_portal = OST_REQUEST_PORTAL;
+ }
+ rp_portal = OSC_REPLY_PORTAL;
+ cli->cl_sp_me = LUSTRE_SP_CLI;
+ } else if (!strcmp(name, LUSTRE_MGC_NAME)) {
+ rq_portal = MGS_REQUEST_PORTAL;
+ rp_portal = MGC_REPLY_PORTAL;
+ connect_op = MGS_CONNECT;
+ cli->cl_sp_me = LUSTRE_SP_MGC;
+ cli->cl_sp_to = LUSTRE_SP_MGS;
+ cli->cl_flvr_mgc.sf_rpc = SPTLRPC_FLVR_INVALID;
+ ns_type = LDLM_NS_TYPE_MGC;
+ } else {
+ CERROR("unknown client OBD type \"%s\", can't setup\n",
+ name);
+ return -EINVAL;
+ }
+
+ if (LUSTRE_CFG_BUFLEN(lcfg, 1) < 1) {
+ CERROR("requires a TARGET UUID\n");
+ return -EINVAL;
+ }
+
+ if (LUSTRE_CFG_BUFLEN(lcfg, 1) > 37) {
+ CERROR("client UUID must be less than 38 characters\n");
+ return -EINVAL;
+ }
+
+ if (LUSTRE_CFG_BUFLEN(lcfg, 2) < 1) {
+ CERROR("setup requires a SERVER UUID\n");
+ return -EINVAL;
+ }
+
+ if (LUSTRE_CFG_BUFLEN(lcfg, 2) > 37) {
+ CERROR("target UUID must be less than 38 characters\n");
+ return -EINVAL;
+ }
+
+ init_rwsem(&cli->cl_sem);
+ mutex_init(&cli->cl_mgc_mutex);
+ cli->cl_conn_count = 0;
+ memcpy(server_uuid.uuid, lustre_cfg_buf(lcfg, 2),
+ min_t(unsigned int, LUSTRE_CFG_BUFLEN(lcfg, 2),
+ sizeof(server_uuid)));
+
+ cli->cl_dirty = 0;
+ cli->cl_avail_grant = 0;
+ /* FIXME: Should limit this for the sum of all cl_dirty_max. */
+ cli->cl_dirty_max = OSC_MAX_DIRTY_DEFAULT * 1024 * 1024;
+ if (cli->cl_dirty_max >> PAGE_CACHE_SHIFT > totalram_pages / 8)
+ cli->cl_dirty_max = totalram_pages << (PAGE_CACHE_SHIFT - 3);
+ INIT_LIST_HEAD(&cli->cl_cache_waiters);
+ INIT_LIST_HEAD(&cli->cl_loi_ready_list);
+ INIT_LIST_HEAD(&cli->cl_loi_hp_ready_list);
+ INIT_LIST_HEAD(&cli->cl_loi_write_list);
+ INIT_LIST_HEAD(&cli->cl_loi_read_list);
+ client_obd_list_lock_init(&cli->cl_loi_list_lock);
+ atomic_set(&cli->cl_pending_w_pages, 0);
+ atomic_set(&cli->cl_pending_r_pages, 0);
+ cli->cl_r_in_flight = 0;
+ cli->cl_w_in_flight = 0;
+
+ spin_lock_init(&cli->cl_read_rpc_hist.oh_lock);
+ spin_lock_init(&cli->cl_write_rpc_hist.oh_lock);
+ spin_lock_init(&cli->cl_read_page_hist.oh_lock);
+ spin_lock_init(&cli->cl_write_page_hist.oh_lock);
+ spin_lock_init(&cli->cl_read_offset_hist.oh_lock);
+ spin_lock_init(&cli->cl_write_offset_hist.oh_lock);
+
+ /* lru for osc. */
+ INIT_LIST_HEAD(&cli->cl_lru_osc);
+ atomic_set(&cli->cl_lru_shrinkers, 0);
+ atomic_set(&cli->cl_lru_busy, 0);
+ atomic_set(&cli->cl_lru_in_list, 0);
+ INIT_LIST_HEAD(&cli->cl_lru_list);
+ client_obd_list_lock_init(&cli->cl_lru_list_lock);
+
+ init_waitqueue_head(&cli->cl_destroy_waitq);
+ atomic_set(&cli->cl_destroy_in_flight, 0);
+ /* Turn on checksumming by default. */
+ cli->cl_checksum = 1;
+ /*
+ * The supported checksum types will be worked out at connect time
+ * Set cl_chksum* to CRC32 for now to avoid returning screwed info
+ * through procfs.
+ */
+ cli->cl_cksum_type = cli->cl_supp_cksum_types = OBD_CKSUM_CRC32;
+ atomic_set(&cli->cl_resends, OSC_DEFAULT_RESENDS);
+
+ /* This value may be reduced at connect time in
+ * ptlrpc_connect_interpret() . We initialize it to only
+ * 1MB until we know what the performance looks like.
+ * In the future this should likely be increased. LU-1431 */
+ cli->cl_max_pages_per_rpc = min_t(int, PTLRPC_MAX_BRW_PAGES,
+ LNET_MTU >> PAGE_CACHE_SHIFT);
+
+ if (!strcmp(name, LUSTRE_MDC_NAME)) {
+ cli->cl_max_rpcs_in_flight = MDC_MAX_RIF_DEFAULT;
+ } else if (totalram_pages >> (20 - PAGE_CACHE_SHIFT) <= 128 /* MB */) {
+ cli->cl_max_rpcs_in_flight = 2;
+ } else if (totalram_pages >> (20 - PAGE_CACHE_SHIFT) <= 256 /* MB */) {
+ cli->cl_max_rpcs_in_flight = 3;
+ } else if (totalram_pages >> (20 - PAGE_CACHE_SHIFT) <= 512 /* MB */) {
+ cli->cl_max_rpcs_in_flight = 4;
+ } else {
+ if (osc_on_mdt(obddev->obd_name))
+ cli->cl_max_rpcs_in_flight = MDS_OSC_MAX_RIF_DEFAULT;
+ else
+ cli->cl_max_rpcs_in_flight = OSC_MAX_RIF_DEFAULT;
+ }
+ rc = ldlm_get_ref();
+ if (rc) {
+ CERROR("ldlm_get_ref failed: %d\n", rc);
+ goto err;
+ }
+
+ ptlrpc_init_client(rq_portal, rp_portal, name,
+ &obddev->obd_ldlm_client);
+
+ imp = class_new_import(obddev);
+ if (imp == NULL) {
+ rc = -ENOENT;
+ goto err_ldlm;
+ }
+ imp->imp_client = &obddev->obd_ldlm_client;
+ imp->imp_connect_op = connect_op;
+ memcpy(cli->cl_target_uuid.uuid, lustre_cfg_buf(lcfg, 1),
+ LUSTRE_CFG_BUFLEN(lcfg, 1));
+ class_import_put(imp);
+
+ rc = client_import_add_conn(imp, &server_uuid, 1);
+ if (rc) {
+ CERROR("can't add initial connection\n");
+ goto err_import;
+ }
+
+ cli->cl_import = imp;
+ /* cli->cl_max_mds_{easize,cookiesize} updated by mdc_init_ea_size() */
+ cli->cl_max_mds_easize = sizeof(struct lov_mds_md_v3);
+ cli->cl_max_mds_cookiesize = sizeof(struct llog_cookie);
+
+ if (LUSTRE_CFG_BUFLEN(lcfg, 3) > 0) {
+ if (!strcmp(lustre_cfg_string(lcfg, 3), "inactive")) {
+ CDEBUG(D_HA, "marking %s %s->%s as inactive\n",
+ name, obddev->obd_name,
+ cli->cl_target_uuid.uuid);
+ spin_lock(&imp->imp_lock);
+ imp->imp_deactive = 1;
+ spin_unlock(&imp->imp_lock);
+ }
+ }
+
+ obddev->obd_namespace = ldlm_namespace_new(obddev, obddev->obd_name,
+ LDLM_NAMESPACE_CLIENT,
+ LDLM_NAMESPACE_GREEDY,
+ ns_type);
+ if (obddev->obd_namespace == NULL) {
+ CERROR("Unable to create client namespace - %s\n",
+ obddev->obd_name);
+ rc = -ENOMEM;
+ goto err_import;
+ }
+
+ cli->cl_qchk_stat = CL_NOT_QUOTACHECKED;
+
+ return rc;
+
+err_import:
+ class_destroy_import(imp);
+err_ldlm:
+ ldlm_put_ref();
+err:
+ return rc;
+
+}
+EXPORT_SYMBOL(client_obd_setup);
+
+int client_obd_cleanup(struct obd_device *obddev)
+{
+ ldlm_namespace_free_post(obddev->obd_namespace);
+ obddev->obd_namespace = NULL;
+
+ LASSERT(obddev->u.cli.cl_import == NULL);
+
+ ldlm_put_ref();
+ return 0;
+}
+EXPORT_SYMBOL(client_obd_cleanup);
+
+/* ->o_connect() method for client side (OSC and MDC and MGC) */
+int client_connect_import(const struct lu_env *env,
+ struct obd_export **exp,
+ struct obd_device *obd, struct obd_uuid *cluuid,
+ struct obd_connect_data *data, void *localdata)
+{
+ struct client_obd *cli = &obd->u.cli;
+ struct obd_import *imp = cli->cl_import;
+ struct obd_connect_data *ocd;
+ struct lustre_handle conn = { 0 };
+ int rc;
+
+ *exp = NULL;
+ down_write(&cli->cl_sem);
+ if (cli->cl_conn_count > 0) {
+ rc = -EALREADY;
+ goto out_sem;
+ }
+
+ rc = class_connect(&conn, obd, cluuid);
+ if (rc)
+ goto out_sem;
+
+ cli->cl_conn_count++;
+ *exp = class_conn2export(&conn);
+
+ LASSERT(obd->obd_namespace);
+
+ imp->imp_dlm_handle = conn;
+ rc = ptlrpc_init_import(imp);
+ if (rc != 0)
+ goto out_ldlm;
+
+ ocd = &imp->imp_connect_data;
+ if (data) {
+ *ocd = *data;
+ imp->imp_connect_flags_orig = data->ocd_connect_flags;
+ }
+
+ rc = ptlrpc_connect_import(imp);
+ if (rc != 0) {
+ LASSERT(imp->imp_state == LUSTRE_IMP_DISCON);
+ goto out_ldlm;
+ }
+ LASSERT(*exp != NULL && (*exp)->exp_connection);
+
+ if (data) {
+ LASSERTF((ocd->ocd_connect_flags & data->ocd_connect_flags) ==
+ ocd->ocd_connect_flags, "old %#llx, new %#llx\n",
+ data->ocd_connect_flags, ocd->ocd_connect_flags);
+ data->ocd_connect_flags = ocd->ocd_connect_flags;
+ }
+
+ ptlrpc_pinger_add_import(imp);
+
+ if (rc) {
+out_ldlm:
+ cli->cl_conn_count--;
+ class_disconnect(*exp);
+ *exp = NULL;
+ }
+out_sem:
+ up_write(&cli->cl_sem);
+
+ return rc;
+}
+EXPORT_SYMBOL(client_connect_import);
+
+int client_disconnect_export(struct obd_export *exp)
+{
+ struct obd_device *obd = class_exp2obd(exp);
+ struct client_obd *cli;
+ struct obd_import *imp;
+ int rc = 0, err;
+
+ if (!obd) {
+ CERROR("invalid export for disconnect: exp %p cookie %#llx\n",
+ exp, exp ? exp->exp_handle.h_cookie : -1);
+ return -EINVAL;
+ }
+
+ cli = &obd->u.cli;
+ imp = cli->cl_import;
+
+ down_write(&cli->cl_sem);
+ CDEBUG(D_INFO, "disconnect %s - %d\n", obd->obd_name,
+ cli->cl_conn_count);
+
+ if (!cli->cl_conn_count) {
+ CERROR("disconnecting disconnected device (%s)\n",
+ obd->obd_name);
+ rc = -EINVAL;
+ goto out_disconnect;
+ }
+
+ cli->cl_conn_count--;
+ if (cli->cl_conn_count) {
+ rc = 0;
+ goto out_disconnect;
+ }
+
+ /* Mark import deactivated now, so we don't try to reconnect if any
+ * of the cleanup RPCs fails (e.g. LDLM cancel, etc). We don't
+ * fully deactivate the import, or that would drop all requests. */
+ spin_lock(&imp->imp_lock);
+ imp->imp_deactive = 1;
+ spin_unlock(&imp->imp_lock);
+
+ /* Some non-replayable imports (MDS's OSCs) are pinged, so just
+ * delete it regardless. (It's safe to delete an import that was
+ * never added.) */
+ (void)ptlrpc_pinger_del_import(imp);
+
+ if (obd->obd_namespace != NULL) {
+ /* obd_force == local only */
+ ldlm_cli_cancel_unused(obd->obd_namespace, NULL,
+ obd->obd_force ? LCF_LOCAL : 0, NULL);
+ ldlm_namespace_free_prior(obd->obd_namespace, imp,
+ obd->obd_force);
+ }
+
+ /* There's no need to hold sem while disconnecting an import,
+ * and it may actually cause deadlock in GSS. */
+ up_write(&cli->cl_sem);
+ rc = ptlrpc_disconnect_import(imp, 0);
+ down_write(&cli->cl_sem);
+
+ ptlrpc_invalidate_import(imp);
+
+out_disconnect:
+ /* Use server style - class_disconnect should be always called for
+ * o_disconnect. */
+ err = class_disconnect(exp);
+ if (!rc && err)
+ rc = err;
+
+ up_write(&cli->cl_sem);
+
+ return rc;
+}
+EXPORT_SYMBOL(client_disconnect_export);
+
+
+/**
+ * Packs current SLV and Limit into \a req.
+ */
+int target_pack_pool_reply(struct ptlrpc_request *req)
+{
+ struct obd_device *obd;
+
+ /* Check that we still have all structures alive as this may
+ * be some late RPC at shutdown time. */
+ if (unlikely(!req->rq_export || !req->rq_export->exp_obd ||
+ !exp_connect_lru_resize(req->rq_export))) {
+ lustre_msg_set_slv(req->rq_repmsg, 0);
+ lustre_msg_set_limit(req->rq_repmsg, 0);
+ return 0;
+ }
+
+ /* OBD is alive here as export is alive, which we checked above. */
+ obd = req->rq_export->exp_obd;
+
+ read_lock(&obd->obd_pool_lock);
+ lustre_msg_set_slv(req->rq_repmsg, obd->obd_pool_slv);
+ lustre_msg_set_limit(req->rq_repmsg, obd->obd_pool_limit);
+ read_unlock(&obd->obd_pool_lock);
+
+ return 0;
+}
+EXPORT_SYMBOL(target_pack_pool_reply);
+
+int target_send_reply_msg(struct ptlrpc_request *req, int rc, int fail_id)
+{
+ if (OBD_FAIL_CHECK_ORSET(fail_id & ~OBD_FAIL_ONCE, OBD_FAIL_ONCE)) {
+ DEBUG_REQ(D_ERROR, req, "dropping reply");
+ return -ECOMM;
+ }
+
+ if (unlikely(rc)) {
+ DEBUG_REQ(D_NET, req, "processing error (%d)", rc);
+ req->rq_status = rc;
+ return ptlrpc_send_error(req, 1);
+ } else {
+ DEBUG_REQ(D_NET, req, "sending reply");
+ }
+
+ return ptlrpc_send_reply(req, PTLRPC_REPLY_MAYBE_DIFFICULT);
+}
+
+void target_send_reply(struct ptlrpc_request *req, int rc, int fail_id)
+{
+ struct ptlrpc_service_part *svcpt;
+ int netrc;
+ struct ptlrpc_reply_state *rs;
+ struct obd_export *exp;
+
+ if (req->rq_no_reply)
+ return;
+
+ svcpt = req->rq_rqbd->rqbd_svcpt;
+ rs = req->rq_reply_state;
+ if (rs == NULL || !rs->rs_difficult) {
+ /* no notifiers */
+ target_send_reply_msg(req, rc, fail_id);
+ return;
+ }
+
+ /* must be an export if locks saved */
+ LASSERT(req->rq_export != NULL);
+ /* req/reply consistent */
+ LASSERT(rs->rs_svcpt == svcpt);
+
+ /* "fresh" reply */
+ LASSERT(!rs->rs_scheduled);
+ LASSERT(!rs->rs_scheduled_ever);
+ LASSERT(!rs->rs_handled);
+ LASSERT(!rs->rs_on_net);
+ LASSERT(rs->rs_export == NULL);
+ LASSERT(list_empty(&rs->rs_obd_list));
+ LASSERT(list_empty(&rs->rs_exp_list));
+
+ exp = class_export_get(req->rq_export);
+
+ /* disable reply scheduling while I'm setting up */
+ rs->rs_scheduled = 1;
+ rs->rs_on_net = 1;
+ rs->rs_xid = req->rq_xid;
+ rs->rs_transno = req->rq_transno;
+ rs->rs_export = exp;
+ rs->rs_opc = lustre_msg_get_opc(req->rq_reqmsg);
+
+ spin_lock(&exp->exp_uncommitted_replies_lock);
+ CDEBUG(D_NET, "rs transno = %llu, last committed = %llu\n",
+ rs->rs_transno, exp->exp_last_committed);
+ if (rs->rs_transno > exp->exp_last_committed) {
+ /* not committed already */
+ list_add_tail(&rs->rs_obd_list,
+ &exp->exp_uncommitted_replies);
+ }
+ spin_unlock(&exp->exp_uncommitted_replies_lock);
+
+ spin_lock(&exp->exp_lock);
+ list_add_tail(&rs->rs_exp_list, &exp->exp_outstanding_replies);
+ spin_unlock(&exp->exp_lock);
+
+ netrc = target_send_reply_msg(req, rc, fail_id);
+
+ spin_lock(&svcpt->scp_rep_lock);
+
+ atomic_inc(&svcpt->scp_nreps_difficult);
+
+ if (netrc != 0) {
+ /* error sending: reply is off the net. Also we need +1
+ * reply ref until ptlrpc_handle_rs() is done
+ * with the reply state (if the send was successful, there
+ * would have been +1 ref for the net, which
+ * reply_out_callback leaves alone) */
+ rs->rs_on_net = 0;
+ ptlrpc_rs_addref(rs);
+ }
+
+ spin_lock(&rs->rs_lock);
+ if (rs->rs_transno <= exp->exp_last_committed ||
+ (!rs->rs_on_net && !rs->rs_no_ack) ||
+ list_empty(&rs->rs_exp_list) || /* completed already */
+ list_empty(&rs->rs_obd_list)) {
+ CDEBUG(D_HA, "Schedule reply immediately\n");
+ ptlrpc_dispatch_difficult_reply(rs);
+ } else {
+ list_add(&rs->rs_list, &svcpt->scp_rep_active);
+ rs->rs_scheduled = 0; /* allow notifier to schedule */
+ }
+ spin_unlock(&rs->rs_lock);
+ spin_unlock(&svcpt->scp_rep_lock);
+}
+EXPORT_SYMBOL(target_send_reply);
+
+ldlm_mode_t lck_compat_array[] = {
+ [LCK_EX] = LCK_COMPAT_EX,
+ [LCK_PW] = LCK_COMPAT_PW,
+ [LCK_PR] = LCK_COMPAT_PR,
+ [LCK_CW] = LCK_COMPAT_CW,
+ [LCK_CR] = LCK_COMPAT_CR,
+ [LCK_NL] = LCK_COMPAT_NL,
+ [LCK_GROUP] = LCK_COMPAT_GROUP,
+ [LCK_COS] = LCK_COMPAT_COS,
+};
+
+/**
+ * Rather arbitrary mapping from LDLM error codes to errno values. This should
+ * not escape to the user level.
+ */
+int ldlm_error2errno(ldlm_error_t error)
+{
+ int result;
+
+ switch (error) {
+ case ELDLM_OK:
+ result = 0;
+ break;
+ case ELDLM_LOCK_CHANGED:
+ result = -ESTALE;
+ break;
+ case ELDLM_LOCK_ABORTED:
+ result = -ENAVAIL;
+ break;
+ case ELDLM_LOCK_REPLACED:
+ result = -ESRCH;
+ break;
+ case ELDLM_NO_LOCK_DATA:
+ result = -ENOENT;
+ break;
+ case ELDLM_NAMESPACE_EXISTS:
+ result = -EEXIST;
+ break;
+ case ELDLM_BAD_NAMESPACE:
+ result = -EBADF;
+ break;
+ default:
+ if (((int)error) < 0) /* cast to signed type */
+ result = error; /* as ldlm_error_t can be unsigned */
+ else {
+ CERROR("Invalid DLM result code: %d\n", error);
+ result = -EPROTO;
+ }
+ }
+ return result;
+}
+EXPORT_SYMBOL(ldlm_error2errno);
+
+/**
+ * Dual to ldlm_error2errno(): maps errno values back to ldlm_error_t.
+ */
+ldlm_error_t ldlm_errno2error(int err_no)
+{
+ int error;
+
+ switch (err_no) {
+ case 0:
+ error = ELDLM_OK;
+ break;
+ case -ESTALE:
+ error = ELDLM_LOCK_CHANGED;
+ break;
+ case -ENAVAIL:
+ error = ELDLM_LOCK_ABORTED;
+ break;
+ case -ESRCH:
+ error = ELDLM_LOCK_REPLACED;
+ break;
+ case -ENOENT:
+ error = ELDLM_NO_LOCK_DATA;
+ break;
+ case -EEXIST:
+ error = ELDLM_NAMESPACE_EXISTS;
+ break;
+ case -EBADF:
+ error = ELDLM_BAD_NAMESPACE;
+ break;
+ default:
+ error = err_no;
+ }
+ return error;
+}
+EXPORT_SYMBOL(ldlm_errno2error);
+
+#if LUSTRE_TRACKS_LOCK_EXP_REFS
+void ldlm_dump_export_locks(struct obd_export *exp)
+{
+ spin_lock(&exp->exp_locks_list_guard);
+ if (!list_empty(&exp->exp_locks_list)) {
+ struct ldlm_lock *lock;
+
+ CERROR("dumping locks for export %p,ignore if the unmount doesn't hang\n",
+ exp);
+ list_for_each_entry(lock, &exp->exp_locks_list,
+ l_exp_refs_link)
+ LDLM_ERROR(lock, "lock:");
+ }
+ spin_unlock(&exp->exp_locks_list_guard);
+}
+#endif
diff --git a/drivers/staging/lustre/lustre/ldlm/ldlm_lock.c b/drivers/staging/lustre/lustre/ldlm/ldlm_lock.c
new file mode 100644
index 000000000..84b111eb4
--- /dev/null
+++ b/drivers/staging/lustre/lustre/ldlm/ldlm_lock.c
@@ -0,0 +1,2322 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2002, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2010, 2012, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ *
+ * lustre/ldlm/ldlm_lock.c
+ *
+ * Author: Peter Braam <braam@clusterfs.com>
+ * Author: Phil Schwan <phil@clusterfs.com>
+ */
+
+#define DEBUG_SUBSYSTEM S_LDLM
+
+#include "../../include/linux/libcfs/libcfs.h"
+#include "../include/lustre_intent.h"
+#include "../include/obd_class.h"
+#include "ldlm_internal.h"
+
+/* lock types */
+char *ldlm_lockname[] = {
+ [0] = "--",
+ [LCK_EX] = "EX",
+ [LCK_PW] = "PW",
+ [LCK_PR] = "PR",
+ [LCK_CW] = "CW",
+ [LCK_CR] = "CR",
+ [LCK_NL] = "NL",
+ [LCK_GROUP] = "GROUP",
+ [LCK_COS] = "COS",
+};
+EXPORT_SYMBOL(ldlm_lockname);
+
+char *ldlm_typename[] = {
+ [LDLM_PLAIN] = "PLN",
+ [LDLM_EXTENT] = "EXT",
+ [LDLM_FLOCK] = "FLK",
+ [LDLM_IBITS] = "IBT",
+};
+EXPORT_SYMBOL(ldlm_typename);
+
+static ldlm_policy_wire_to_local_t ldlm_policy_wire18_to_local[] = {
+ [LDLM_PLAIN - LDLM_MIN_TYPE] = ldlm_plain_policy_wire_to_local,
+ [LDLM_EXTENT - LDLM_MIN_TYPE] = ldlm_extent_policy_wire_to_local,
+ [LDLM_FLOCK - LDLM_MIN_TYPE] = ldlm_flock_policy_wire18_to_local,
+ [LDLM_IBITS - LDLM_MIN_TYPE] = ldlm_ibits_policy_wire_to_local,
+};
+
+static ldlm_policy_wire_to_local_t ldlm_policy_wire21_to_local[] = {
+ [LDLM_PLAIN - LDLM_MIN_TYPE] = ldlm_plain_policy_wire_to_local,
+ [LDLM_EXTENT - LDLM_MIN_TYPE] = ldlm_extent_policy_wire_to_local,
+ [LDLM_FLOCK - LDLM_MIN_TYPE] = ldlm_flock_policy_wire21_to_local,
+ [LDLM_IBITS - LDLM_MIN_TYPE] = ldlm_ibits_policy_wire_to_local,
+};
+
+static ldlm_policy_local_to_wire_t ldlm_policy_local_to_wire[] = {
+ [LDLM_PLAIN - LDLM_MIN_TYPE] = ldlm_plain_policy_local_to_wire,
+ [LDLM_EXTENT - LDLM_MIN_TYPE] = ldlm_extent_policy_local_to_wire,
+ [LDLM_FLOCK - LDLM_MIN_TYPE] = ldlm_flock_policy_local_to_wire,
+ [LDLM_IBITS - LDLM_MIN_TYPE] = ldlm_ibits_policy_local_to_wire,
+};
+
+/**
+ * Converts lock policy from local format to on the wire lock_desc format
+ */
+void ldlm_convert_policy_to_wire(ldlm_type_t type,
+ const ldlm_policy_data_t *lpolicy,
+ ldlm_wire_policy_data_t *wpolicy)
+{
+ ldlm_policy_local_to_wire_t convert;
+
+ convert = ldlm_policy_local_to_wire[type - LDLM_MIN_TYPE];
+
+ convert(lpolicy, wpolicy);
+}
+
+/**
+ * Converts lock policy from on the wire lock_desc format to local format
+ */
+void ldlm_convert_policy_to_local(struct obd_export *exp, ldlm_type_t type,
+ const ldlm_wire_policy_data_t *wpolicy,
+ ldlm_policy_data_t *lpolicy)
+{
+ ldlm_policy_wire_to_local_t convert;
+ int new_client;
+
+ /** some badness for 2.0.0 clients, but 2.0.0 isn't supported */
+ new_client = (exp_connect_flags(exp) & OBD_CONNECT_FULL20) != 0;
+ if (new_client)
+ convert = ldlm_policy_wire21_to_local[type - LDLM_MIN_TYPE];
+ else
+ convert = ldlm_policy_wire18_to_local[type - LDLM_MIN_TYPE];
+
+ convert(wpolicy, lpolicy);
+}
+
+char *ldlm_it2str(int it)
+{
+ switch (it) {
+ case IT_OPEN:
+ return "open";
+ case IT_CREAT:
+ return "creat";
+ case (IT_OPEN | IT_CREAT):
+ return "open|creat";
+ case IT_READDIR:
+ return "readdir";
+ case IT_GETATTR:
+ return "getattr";
+ case IT_LOOKUP:
+ return "lookup";
+ case IT_UNLINK:
+ return "unlink";
+ case IT_GETXATTR:
+ return "getxattr";
+ case IT_LAYOUT:
+ return "layout";
+ default:
+ CERROR("Unknown intent %d\n", it);
+ return "UNKNOWN";
+ }
+}
+EXPORT_SYMBOL(ldlm_it2str);
+
+
+void ldlm_register_intent(struct ldlm_namespace *ns, ldlm_res_policy arg)
+{
+ ns->ns_policy = arg;
+}
+EXPORT_SYMBOL(ldlm_register_intent);
+
+/*
+ * REFCOUNTED LOCK OBJECTS
+ */
+
+
+/**
+ * Get a reference on a lock.
+ *
+ * Lock refcounts, during creation:
+ * - one special one for allocation, dec'd only once in destroy
+ * - one for being a lock that's in-use
+ * - one for the addref associated with a new lock
+ */
+struct ldlm_lock *ldlm_lock_get(struct ldlm_lock *lock)
+{
+ atomic_inc(&lock->l_refc);
+ return lock;
+}
+EXPORT_SYMBOL(ldlm_lock_get);
+
+/**
+ * Release lock reference.
+ *
+ * Also frees the lock if it was last reference.
+ */
+void ldlm_lock_put(struct ldlm_lock *lock)
+{
+ LASSERT(lock->l_resource != LP_POISON);
+ LASSERT(atomic_read(&lock->l_refc) > 0);
+ if (atomic_dec_and_test(&lock->l_refc)) {
+ struct ldlm_resource *res;
+
+ LDLM_DEBUG(lock,
+ "final lock_put on destroyed lock, freeing it.");
+
+ res = lock->l_resource;
+ LASSERT(lock->l_flags & LDLM_FL_DESTROYED);
+ LASSERT(list_empty(&lock->l_res_link));
+ LASSERT(list_empty(&lock->l_pending_chain));
+
+ lprocfs_counter_decr(ldlm_res_to_ns(res)->ns_stats,
+ LDLM_NSS_LOCKS);
+ lu_ref_del(&res->lr_reference, "lock", lock);
+ ldlm_resource_putref(res);
+ lock->l_resource = NULL;
+ if (lock->l_export) {
+ class_export_lock_put(lock->l_export, lock);
+ lock->l_export = NULL;
+ }
+
+ if (lock->l_lvb_data != NULL)
+ OBD_FREE(lock->l_lvb_data, lock->l_lvb_len);
+
+ ldlm_interval_free(ldlm_interval_detach(lock));
+ lu_ref_fini(&lock->l_reference);
+ OBD_FREE_RCU(lock, sizeof(*lock), &lock->l_handle);
+ }
+}
+EXPORT_SYMBOL(ldlm_lock_put);
+
+/**
+ * Removes LDLM lock \a lock from LRU. Assumes LRU is already locked.
+ */
+int ldlm_lock_remove_from_lru_nolock(struct ldlm_lock *lock)
+{
+ int rc = 0;
+
+ if (!list_empty(&lock->l_lru)) {
+ struct ldlm_namespace *ns = ldlm_lock_to_ns(lock);
+
+ LASSERT(lock->l_resource->lr_type != LDLM_FLOCK);
+ list_del_init(&lock->l_lru);
+ LASSERT(ns->ns_nr_unused > 0);
+ ns->ns_nr_unused--;
+ rc = 1;
+ }
+ return rc;
+}
+
+/**
+ * Removes LDLM lock \a lock from LRU. Obtains the LRU lock first.
+ */
+int ldlm_lock_remove_from_lru(struct ldlm_lock *lock)
+{
+ struct ldlm_namespace *ns = ldlm_lock_to_ns(lock);
+ int rc;
+
+ if (lock->l_flags & LDLM_FL_NS_SRV) {
+ LASSERT(list_empty(&lock->l_lru));
+ return 0;
+ }
+
+ spin_lock(&ns->ns_lock);
+ rc = ldlm_lock_remove_from_lru_nolock(lock);
+ spin_unlock(&ns->ns_lock);
+ return rc;
+}
+
+/**
+ * Adds LDLM lock \a lock to namespace LRU. Assumes LRU is already locked.
+ */
+void ldlm_lock_add_to_lru_nolock(struct ldlm_lock *lock)
+{
+ struct ldlm_namespace *ns = ldlm_lock_to_ns(lock);
+
+ lock->l_last_used = cfs_time_current();
+ LASSERT(list_empty(&lock->l_lru));
+ LASSERT(lock->l_resource->lr_type != LDLM_FLOCK);
+ list_add_tail(&lock->l_lru, &ns->ns_unused_list);
+ if (lock->l_flags & LDLM_FL_SKIPPED)
+ lock->l_flags &= ~LDLM_FL_SKIPPED;
+ LASSERT(ns->ns_nr_unused >= 0);
+ ns->ns_nr_unused++;
+}
+
+/**
+ * Adds LDLM lock \a lock to namespace LRU. Obtains necessary LRU locks
+ * first.
+ */
+void ldlm_lock_add_to_lru(struct ldlm_lock *lock)
+{
+ struct ldlm_namespace *ns = ldlm_lock_to_ns(lock);
+
+ spin_lock(&ns->ns_lock);
+ ldlm_lock_add_to_lru_nolock(lock);
+ spin_unlock(&ns->ns_lock);
+}
+
+/**
+ * Moves LDLM lock \a lock that is already in namespace LRU to the tail of
+ * the LRU. Performs necessary LRU locking
+ */
+void ldlm_lock_touch_in_lru(struct ldlm_lock *lock)
+{
+ struct ldlm_namespace *ns = ldlm_lock_to_ns(lock);
+
+ if (lock->l_flags & LDLM_FL_NS_SRV) {
+ LASSERT(list_empty(&lock->l_lru));
+ return;
+ }
+
+ spin_lock(&ns->ns_lock);
+ if (!list_empty(&lock->l_lru)) {
+ ldlm_lock_remove_from_lru_nolock(lock);
+ ldlm_lock_add_to_lru_nolock(lock);
+ }
+ spin_unlock(&ns->ns_lock);
+}
+
+/**
+ * Helper to destroy a locked lock.
+ *
+ * Used by ldlm_lock_destroy and ldlm_lock_destroy_nolock
+ * Must be called with l_lock and lr_lock held.
+ *
+ * Does not actually free the lock data, but rather marks the lock as
+ * destroyed by setting l_destroyed field in the lock to 1. Destroys a
+ * handle->lock association too, so that the lock can no longer be found
+ * and removes the lock from LRU list. Actual lock freeing occurs when
+ * last lock reference goes away.
+ *
+ * Original comment (of some historical value):
+ * This used to have a 'strict' flag, which recovery would use to mark an
+ * in-use lock as needing-to-die. Lest I am ever tempted to put it back, I
+ * shall explain why it's gone: with the new hash table scheme, once you call
+ * ldlm_lock_destroy, you can never drop your final references on this lock.
+ * Because it's not in the hash table anymore. -phil
+ */
+int ldlm_lock_destroy_internal(struct ldlm_lock *lock)
+{
+ if (lock->l_readers || lock->l_writers) {
+ LDLM_ERROR(lock, "lock still has references");
+ LBUG();
+ }
+
+ if (!list_empty(&lock->l_res_link)) {
+ LDLM_ERROR(lock, "lock still on resource");
+ LBUG();
+ }
+
+ if (lock->l_flags & LDLM_FL_DESTROYED) {
+ LASSERT(list_empty(&lock->l_lru));
+ return 0;
+ }
+ lock->l_flags |= LDLM_FL_DESTROYED;
+
+ if (lock->l_export && lock->l_export->exp_lock_hash) {
+ /* NB: it's safe to call cfs_hash_del() even lock isn't
+ * in exp_lock_hash. */
+ /* In the function below, .hs_keycmp resolves to
+ * ldlm_export_lock_keycmp() */
+ /* coverity[overrun-buffer-val] */
+ cfs_hash_del(lock->l_export->exp_lock_hash,
+ &lock->l_remote_handle, &lock->l_exp_hash);
+ }
+
+ ldlm_lock_remove_from_lru(lock);
+ class_handle_unhash(&lock->l_handle);
+
+#if 0
+ /* Wake anyone waiting for this lock */
+ /* FIXME: I should probably add yet another flag, instead of using
+ * l_export to only call this on clients */
+ if (lock->l_export)
+ class_export_put(lock->l_export);
+ lock->l_export = NULL;
+ if (lock->l_export && lock->l_completion_ast)
+ lock->l_completion_ast(lock, 0);
+#endif
+ return 1;
+}
+
+/**
+ * Destroys a LDLM lock \a lock. Performs necessary locking first.
+ */
+void ldlm_lock_destroy(struct ldlm_lock *lock)
+{
+ int first;
+
+ lock_res_and_lock(lock);
+ first = ldlm_lock_destroy_internal(lock);
+ unlock_res_and_lock(lock);
+
+ /* drop reference from hashtable only for first destroy */
+ if (first) {
+ lu_ref_del(&lock->l_reference, "hash", lock);
+ LDLM_LOCK_RELEASE(lock);
+ }
+}
+
+/**
+ * Destroys a LDLM lock \a lock that is already locked.
+ */
+void ldlm_lock_destroy_nolock(struct ldlm_lock *lock)
+{
+ int first;
+
+ first = ldlm_lock_destroy_internal(lock);
+ /* drop reference from hashtable only for first destroy */
+ if (first) {
+ lu_ref_del(&lock->l_reference, "hash", lock);
+ LDLM_LOCK_RELEASE(lock);
+ }
+}
+
+/* this is called by portals_handle2object with the handle lock taken */
+static void lock_handle_addref(void *lock)
+{
+ LDLM_LOCK_GET((struct ldlm_lock *)lock);
+}
+
+static void lock_handle_free(void *lock, int size)
+{
+ LASSERT(size == sizeof(struct ldlm_lock));
+ OBD_SLAB_FREE(lock, ldlm_lock_slab, size);
+}
+
+struct portals_handle_ops lock_handle_ops = {
+ .hop_addref = lock_handle_addref,
+ .hop_free = lock_handle_free,
+};
+
+/**
+ *
+ * Allocate and initialize new lock structure.
+ *
+ * usage: pass in a resource on which you have done ldlm_resource_get
+ * new lock will take over the refcount.
+ * returns: lock with refcount 2 - one for current caller and one for remote
+ */
+static struct ldlm_lock *ldlm_lock_new(struct ldlm_resource *resource)
+{
+ struct ldlm_lock *lock;
+
+ if (resource == NULL)
+ LBUG();
+
+ OBD_SLAB_ALLOC_PTR_GFP(lock, ldlm_lock_slab, GFP_NOFS);
+ if (lock == NULL)
+ return NULL;
+
+ spin_lock_init(&lock->l_lock);
+ lock->l_resource = resource;
+ lu_ref_add(&resource->lr_reference, "lock", lock);
+
+ atomic_set(&lock->l_refc, 2);
+ INIT_LIST_HEAD(&lock->l_res_link);
+ INIT_LIST_HEAD(&lock->l_lru);
+ INIT_LIST_HEAD(&lock->l_pending_chain);
+ INIT_LIST_HEAD(&lock->l_bl_ast);
+ INIT_LIST_HEAD(&lock->l_cp_ast);
+ INIT_LIST_HEAD(&lock->l_rk_ast);
+ init_waitqueue_head(&lock->l_waitq);
+ lock->l_blocking_lock = NULL;
+ INIT_LIST_HEAD(&lock->l_sl_mode);
+ INIT_LIST_HEAD(&lock->l_sl_policy);
+ INIT_HLIST_NODE(&lock->l_exp_hash);
+ INIT_HLIST_NODE(&lock->l_exp_flock_hash);
+
+ lprocfs_counter_incr(ldlm_res_to_ns(resource)->ns_stats,
+ LDLM_NSS_LOCKS);
+ INIT_LIST_HEAD(&lock->l_handle.h_link);
+ class_handle_hash(&lock->l_handle, &lock_handle_ops);
+
+ lu_ref_init(&lock->l_reference);
+ lu_ref_add(&lock->l_reference, "hash", lock);
+ lock->l_callback_timeout = 0;
+
+#if LUSTRE_TRACKS_LOCK_EXP_REFS
+ INIT_LIST_HEAD(&lock->l_exp_refs_link);
+ lock->l_exp_refs_nr = 0;
+ lock->l_exp_refs_target = NULL;
+#endif
+ INIT_LIST_HEAD(&lock->l_exp_list);
+
+ return lock;
+}
+
+/**
+ * Moves LDLM lock \a lock to another resource.
+ * This is used on client when server returns some other lock than requested
+ * (typically as a result of intent operation)
+ */
+int ldlm_lock_change_resource(struct ldlm_namespace *ns, struct ldlm_lock *lock,
+ const struct ldlm_res_id *new_resid)
+{
+ struct ldlm_resource *oldres = lock->l_resource;
+ struct ldlm_resource *newres;
+ int type;
+
+ LASSERT(ns_is_client(ns));
+
+ lock_res_and_lock(lock);
+ if (memcmp(new_resid, &lock->l_resource->lr_name,
+ sizeof(lock->l_resource->lr_name)) == 0) {
+ /* Nothing to do */
+ unlock_res_and_lock(lock);
+ return 0;
+ }
+
+ LASSERT(new_resid->name[0] != 0);
+
+ /* This function assumes that the lock isn't on any lists */
+ LASSERT(list_empty(&lock->l_res_link));
+
+ type = oldres->lr_type;
+ unlock_res_and_lock(lock);
+
+ newres = ldlm_resource_get(ns, NULL, new_resid, type, 1);
+ if (newres == NULL)
+ return -ENOMEM;
+
+ lu_ref_add(&newres->lr_reference, "lock", lock);
+ /*
+ * To flip the lock from the old to the new resource, lock, oldres and
+ * newres have to be locked. Resource spin-locks are nested within
+ * lock->l_lock, and are taken in the memory address order to avoid
+ * dead-locks.
+ */
+ spin_lock(&lock->l_lock);
+ oldres = lock->l_resource;
+ if (oldres < newres) {
+ lock_res(oldres);
+ lock_res_nested(newres, LRT_NEW);
+ } else {
+ lock_res(newres);
+ lock_res_nested(oldres, LRT_NEW);
+ }
+ LASSERT(memcmp(new_resid, &oldres->lr_name,
+ sizeof(oldres->lr_name)) != 0);
+ lock->l_resource = newres;
+ unlock_res(oldres);
+ unlock_res_and_lock(lock);
+
+ /* ...and the flowers are still standing! */
+ lu_ref_del(&oldres->lr_reference, "lock", lock);
+ ldlm_resource_putref(oldres);
+
+ return 0;
+}
+EXPORT_SYMBOL(ldlm_lock_change_resource);
+
+/** \defgroup ldlm_handles LDLM HANDLES
+ * Ways to get hold of locks without any addresses.
+ * @{
+ */
+
+/**
+ * Fills in handle for LDLM lock \a lock into supplied \a lockh
+ * Does not take any references.
+ */
+void ldlm_lock2handle(const struct ldlm_lock *lock, struct lustre_handle *lockh)
+{
+ lockh->cookie = lock->l_handle.h_cookie;
+}
+EXPORT_SYMBOL(ldlm_lock2handle);
+
+/**
+ * Obtain a lock reference by handle.
+ *
+ * if \a flags: atomically get the lock and set the flags.
+ * Return NULL if flag already set
+ */
+struct ldlm_lock *__ldlm_handle2lock(const struct lustre_handle *handle,
+ __u64 flags)
+{
+ struct ldlm_lock *lock;
+
+ LASSERT(handle);
+
+ lock = class_handle2object(handle->cookie);
+ if (lock == NULL)
+ return NULL;
+
+ /* It's unlikely but possible that someone marked the lock as
+ * destroyed after we did handle2object on it */
+ if (flags == 0 && ((lock->l_flags & LDLM_FL_DESTROYED) == 0)) {
+ lu_ref_add(&lock->l_reference, "handle", current);
+ return lock;
+ }
+
+ lock_res_and_lock(lock);
+
+ LASSERT(lock->l_resource != NULL);
+
+ lu_ref_add_atomic(&lock->l_reference, "handle", current);
+ if (unlikely(lock->l_flags & LDLM_FL_DESTROYED)) {
+ unlock_res_and_lock(lock);
+ CDEBUG(D_INFO, "lock already destroyed: lock %p\n", lock);
+ LDLM_LOCK_PUT(lock);
+ return NULL;
+ }
+
+ if (flags && (lock->l_flags & flags)) {
+ unlock_res_and_lock(lock);
+ LDLM_LOCK_PUT(lock);
+ return NULL;
+ }
+
+ if (flags)
+ lock->l_flags |= flags;
+
+ unlock_res_and_lock(lock);
+ return lock;
+}
+EXPORT_SYMBOL(__ldlm_handle2lock);
+/** @} ldlm_handles */
+
+/**
+ * Fill in "on the wire" representation for given LDLM lock into supplied
+ * lock descriptor \a desc structure.
+ */
+void ldlm_lock2desc(struct ldlm_lock *lock, struct ldlm_lock_desc *desc)
+{
+ ldlm_res2desc(lock->l_resource, &desc->l_resource);
+ desc->l_req_mode = lock->l_req_mode;
+ desc->l_granted_mode = lock->l_granted_mode;
+ ldlm_convert_policy_to_wire(lock->l_resource->lr_type,
+ &lock->l_policy_data,
+ &desc->l_policy_data);
+}
+EXPORT_SYMBOL(ldlm_lock2desc);
+
+/**
+ * Add a lock to list of conflicting locks to send AST to.
+ *
+ * Only add if we have not sent a blocking AST to the lock yet.
+ */
+void ldlm_add_bl_work_item(struct ldlm_lock *lock, struct ldlm_lock *new,
+ struct list_head *work_list)
+{
+ if ((lock->l_flags & LDLM_FL_AST_SENT) == 0) {
+ LDLM_DEBUG(lock, "lock incompatible; sending blocking AST.");
+ lock->l_flags |= LDLM_FL_AST_SENT;
+ /* If the enqueuing client said so, tell the AST recipient to
+ * discard dirty data, rather than writing back. */
+ if (new->l_flags & LDLM_FL_AST_DISCARD_DATA)
+ lock->l_flags |= LDLM_FL_DISCARD_DATA;
+ LASSERT(list_empty(&lock->l_bl_ast));
+ list_add(&lock->l_bl_ast, work_list);
+ LDLM_LOCK_GET(lock);
+ LASSERT(lock->l_blocking_lock == NULL);
+ lock->l_blocking_lock = LDLM_LOCK_GET(new);
+ }
+}
+
+/**
+ * Add a lock to list of just granted locks to send completion AST to.
+ */
+void ldlm_add_cp_work_item(struct ldlm_lock *lock, struct list_head *work_list)
+{
+ if ((lock->l_flags & LDLM_FL_CP_REQD) == 0) {
+ lock->l_flags |= LDLM_FL_CP_REQD;
+ LDLM_DEBUG(lock, "lock granted; sending completion AST.");
+ LASSERT(list_empty(&lock->l_cp_ast));
+ list_add(&lock->l_cp_ast, work_list);
+ LDLM_LOCK_GET(lock);
+ }
+}
+
+/**
+ * Aggregator function to add AST work items into a list. Determines
+ * what sort of an AST work needs to be done and calls the proper
+ * adding function.
+ * Must be called with lr_lock held.
+ */
+void ldlm_add_ast_work_item(struct ldlm_lock *lock, struct ldlm_lock *new,
+ struct list_head *work_list)
+{
+ check_res_locked(lock->l_resource);
+ if (new)
+ ldlm_add_bl_work_item(lock, new, work_list);
+ else
+ ldlm_add_cp_work_item(lock, work_list);
+}
+
+/**
+ * Add specified reader/writer reference to LDLM lock with handle \a lockh.
+ * r/w reference type is determined by \a mode
+ * Calls ldlm_lock_addref_internal.
+ */
+void ldlm_lock_addref(struct lustre_handle *lockh, __u32 mode)
+{
+ struct ldlm_lock *lock;
+
+ lock = ldlm_handle2lock(lockh);
+ LASSERT(lock != NULL);
+ ldlm_lock_addref_internal(lock, mode);
+ LDLM_LOCK_PUT(lock);
+}
+EXPORT_SYMBOL(ldlm_lock_addref);
+
+/**
+ * Helper function.
+ * Add specified reader/writer reference to LDLM lock \a lock.
+ * r/w reference type is determined by \a mode
+ * Removes lock from LRU if it is there.
+ * Assumes the LDLM lock is already locked.
+ */
+void ldlm_lock_addref_internal_nolock(struct ldlm_lock *lock, __u32 mode)
+{
+ ldlm_lock_remove_from_lru(lock);
+ if (mode & (LCK_NL | LCK_CR | LCK_PR)) {
+ lock->l_readers++;
+ lu_ref_add_atomic(&lock->l_reference, "reader", lock);
+ }
+ if (mode & (LCK_EX | LCK_CW | LCK_PW | LCK_GROUP | LCK_COS)) {
+ lock->l_writers++;
+ lu_ref_add_atomic(&lock->l_reference, "writer", lock);
+ }
+ LDLM_LOCK_GET(lock);
+ lu_ref_add_atomic(&lock->l_reference, "user", lock);
+ LDLM_DEBUG(lock, "ldlm_lock_addref(%s)", ldlm_lockname[mode]);
+}
+
+/**
+ * Attempts to add reader/writer reference to a lock with handle \a lockh, and
+ * fails if lock is already LDLM_FL_CBPENDING or destroyed.
+ *
+ * \retval 0 success, lock was addref-ed
+ *
+ * \retval -EAGAIN lock is being canceled.
+ */
+int ldlm_lock_addref_try(struct lustre_handle *lockh, __u32 mode)
+{
+ struct ldlm_lock *lock;
+ int result;
+
+ result = -EAGAIN;
+ lock = ldlm_handle2lock(lockh);
+ if (lock != NULL) {
+ lock_res_and_lock(lock);
+ if (lock->l_readers != 0 || lock->l_writers != 0 ||
+ !(lock->l_flags & LDLM_FL_CBPENDING)) {
+ ldlm_lock_addref_internal_nolock(lock, mode);
+ result = 0;
+ }
+ unlock_res_and_lock(lock);
+ LDLM_LOCK_PUT(lock);
+ }
+ return result;
+}
+EXPORT_SYMBOL(ldlm_lock_addref_try);
+
+/**
+ * Add specified reader/writer reference to LDLM lock \a lock.
+ * Locks LDLM lock and calls ldlm_lock_addref_internal_nolock to do the work.
+ * Only called for local locks.
+ */
+void ldlm_lock_addref_internal(struct ldlm_lock *lock, __u32 mode)
+{
+ lock_res_and_lock(lock);
+ ldlm_lock_addref_internal_nolock(lock, mode);
+ unlock_res_and_lock(lock);
+}
+
+/**
+ * Removes reader/writer reference for LDLM lock \a lock.
+ * Assumes LDLM lock is already locked.
+ * only called in ldlm_flock_destroy and for local locks.
+ * Does NOT add lock to LRU if no r/w references left to accommodate flock locks
+ * that cannot be placed in LRU.
+ */
+void ldlm_lock_decref_internal_nolock(struct ldlm_lock *lock, __u32 mode)
+{
+ LDLM_DEBUG(lock, "ldlm_lock_decref(%s)", ldlm_lockname[mode]);
+ if (mode & (LCK_NL | LCK_CR | LCK_PR)) {
+ LASSERT(lock->l_readers > 0);
+ lu_ref_del(&lock->l_reference, "reader", lock);
+ lock->l_readers--;
+ }
+ if (mode & (LCK_EX | LCK_CW | LCK_PW | LCK_GROUP | LCK_COS)) {
+ LASSERT(lock->l_writers > 0);
+ lu_ref_del(&lock->l_reference, "writer", lock);
+ lock->l_writers--;
+ }
+
+ lu_ref_del(&lock->l_reference, "user", lock);
+ LDLM_LOCK_RELEASE(lock); /* matches the LDLM_LOCK_GET() in addref */
+}
+
+/**
+ * Removes reader/writer reference for LDLM lock \a lock.
+ * Locks LDLM lock first.
+ * If the lock is determined to be client lock on a client and r/w refcount
+ * drops to zero and the lock is not blocked, the lock is added to LRU lock
+ * on the namespace.
+ * For blocked LDLM locks if r/w count drops to zero, blocking_ast is called.
+ */
+void ldlm_lock_decref_internal(struct ldlm_lock *lock, __u32 mode)
+{
+ struct ldlm_namespace *ns;
+
+ lock_res_and_lock(lock);
+
+ ns = ldlm_lock_to_ns(lock);
+
+ ldlm_lock_decref_internal_nolock(lock, mode);
+
+ if (lock->l_flags & LDLM_FL_LOCAL &&
+ !lock->l_readers && !lock->l_writers) {
+ /* If this is a local lock on a server namespace and this was
+ * the last reference, cancel the lock. */
+ CDEBUG(D_INFO, "forcing cancel of local lock\n");
+ lock->l_flags |= LDLM_FL_CBPENDING;
+ }
+
+ if (!lock->l_readers && !lock->l_writers &&
+ (lock->l_flags & LDLM_FL_CBPENDING)) {
+ /* If we received a blocked AST and this was the last reference,
+ * run the callback. */
+ if ((lock->l_flags & LDLM_FL_NS_SRV) && lock->l_export)
+ CERROR("FL_CBPENDING set on non-local lock--just a warning\n");
+
+ LDLM_DEBUG(lock, "final decref done on cbpending lock");
+
+ LDLM_LOCK_GET(lock); /* dropped by bl thread */
+ ldlm_lock_remove_from_lru(lock);
+ unlock_res_and_lock(lock);
+
+ if (lock->l_flags & LDLM_FL_FAIL_LOC)
+ OBD_RACE(OBD_FAIL_LDLM_CP_BL_RACE);
+
+ if ((lock->l_flags & LDLM_FL_ATOMIC_CB) ||
+ ldlm_bl_to_thread_lock(ns, NULL, lock) != 0)
+ ldlm_handle_bl_callback(ns, NULL, lock);
+ } else if (ns_is_client(ns) &&
+ !lock->l_readers && !lock->l_writers &&
+ !(lock->l_flags & LDLM_FL_NO_LRU) &&
+ !(lock->l_flags & LDLM_FL_BL_AST)) {
+
+ LDLM_DEBUG(lock, "add lock into lru list");
+
+ /* If this is a client-side namespace and this was the last
+ * reference, put it on the LRU. */
+ ldlm_lock_add_to_lru(lock);
+ unlock_res_and_lock(lock);
+
+ if (lock->l_flags & LDLM_FL_FAIL_LOC)
+ OBD_RACE(OBD_FAIL_LDLM_CP_BL_RACE);
+
+ /* Call ldlm_cancel_lru() only if EARLY_CANCEL and LRU RESIZE
+ * are not supported by the server, otherwise, it is done on
+ * enqueue. */
+ if (!exp_connect_cancelset(lock->l_conn_export) &&
+ !ns_connect_lru_resize(ns))
+ ldlm_cancel_lru(ns, 0, LCF_ASYNC, 0);
+ } else {
+ LDLM_DEBUG(lock, "do not add lock into lru list");
+ unlock_res_and_lock(lock);
+ }
+}
+
+/**
+ * Decrease reader/writer refcount for LDLM lock with handle \a lockh
+ */
+void ldlm_lock_decref(struct lustre_handle *lockh, __u32 mode)
+{
+ struct ldlm_lock *lock = __ldlm_handle2lock(lockh, 0);
+
+ LASSERTF(lock != NULL, "Non-existing lock: %#llx\n", lockh->cookie);
+ ldlm_lock_decref_internal(lock, mode);
+ LDLM_LOCK_PUT(lock);
+}
+EXPORT_SYMBOL(ldlm_lock_decref);
+
+/**
+ * Decrease reader/writer refcount for LDLM lock with handle
+ * \a lockh and mark it for subsequent cancellation once r/w refcount
+ * drops to zero instead of putting into LRU.
+ *
+ * Typical usage is for GROUP locks which we cannot allow to be cached.
+ */
+void ldlm_lock_decref_and_cancel(struct lustre_handle *lockh, __u32 mode)
+{
+ struct ldlm_lock *lock = __ldlm_handle2lock(lockh, 0);
+
+ LASSERT(lock != NULL);
+
+ LDLM_DEBUG(lock, "ldlm_lock_decref(%s)", ldlm_lockname[mode]);
+ lock_res_and_lock(lock);
+ lock->l_flags |= LDLM_FL_CBPENDING;
+ unlock_res_and_lock(lock);
+ ldlm_lock_decref_internal(lock, mode);
+ LDLM_LOCK_PUT(lock);
+}
+EXPORT_SYMBOL(ldlm_lock_decref_and_cancel);
+
+struct sl_insert_point {
+ struct list_head *res_link;
+ struct list_head *mode_link;
+ struct list_head *policy_link;
+};
+
+/**
+ * Finds a position to insert the new lock into granted lock list.
+ *
+ * Used for locks eligible for skiplist optimization.
+ *
+ * Parameters:
+ * queue [input]: the granted list where search acts on;
+ * req [input]: the lock whose position to be located;
+ * prev [output]: positions within 3 lists to insert @req to
+ * Return Value:
+ * filled @prev
+ * NOTE: called by
+ * - ldlm_grant_lock_with_skiplist
+ */
+static void search_granted_lock(struct list_head *queue,
+ struct ldlm_lock *req,
+ struct sl_insert_point *prev)
+{
+ struct list_head *tmp;
+ struct ldlm_lock *lock, *mode_end, *policy_end;
+
+ list_for_each(tmp, queue) {
+ lock = list_entry(tmp, struct ldlm_lock, l_res_link);
+
+ mode_end = list_entry(lock->l_sl_mode.prev,
+ struct ldlm_lock, l_sl_mode);
+
+ if (lock->l_req_mode != req->l_req_mode) {
+ /* jump to last lock of mode group */
+ tmp = &mode_end->l_res_link;
+ continue;
+ }
+
+ /* suitable mode group is found */
+ if (lock->l_resource->lr_type == LDLM_PLAIN) {
+ /* insert point is last lock of the mode group */
+ prev->res_link = &mode_end->l_res_link;
+ prev->mode_link = &mode_end->l_sl_mode;
+ prev->policy_link = &req->l_sl_policy;
+ return;
+ } else if (lock->l_resource->lr_type == LDLM_IBITS) {
+ for (;;) {
+ policy_end =
+ list_entry(lock->l_sl_policy.prev,
+ struct ldlm_lock,
+ l_sl_policy);
+
+ if (lock->l_policy_data.l_inodebits.bits ==
+ req->l_policy_data.l_inodebits.bits) {
+ /* insert point is last lock of
+ * the policy group */
+ prev->res_link =
+ &policy_end->l_res_link;
+ prev->mode_link =
+ &policy_end->l_sl_mode;
+ prev->policy_link =
+ &policy_end->l_sl_policy;
+ return;
+ }
+
+ if (policy_end == mode_end)
+ /* done with mode group */
+ break;
+
+ /* go to next policy group within mode group */
+ tmp = policy_end->l_res_link.next;
+ lock = list_entry(tmp, struct ldlm_lock,
+ l_res_link);
+ } /* loop over policy groups within the mode group */
+
+ /* insert point is last lock of the mode group,
+ * new policy group is started */
+ prev->res_link = &mode_end->l_res_link;
+ prev->mode_link = &mode_end->l_sl_mode;
+ prev->policy_link = &req->l_sl_policy;
+ return;
+ } else {
+ LDLM_ERROR(lock,
+ "is not LDLM_PLAIN or LDLM_IBITS lock");
+ LBUG();
+ }
+ }
+
+ /* insert point is last lock on the queue,
+ * new mode group and new policy group are started */
+ prev->res_link = queue->prev;
+ prev->mode_link = &req->l_sl_mode;
+ prev->policy_link = &req->l_sl_policy;
+}
+
+/**
+ * Add a lock into resource granted list after a position described by
+ * \a prev.
+ */
+static void ldlm_granted_list_add_lock(struct ldlm_lock *lock,
+ struct sl_insert_point *prev)
+{
+ struct ldlm_resource *res = lock->l_resource;
+
+ check_res_locked(res);
+
+ ldlm_resource_dump(D_INFO, res);
+ LDLM_DEBUG(lock, "About to add lock:");
+
+ if (lock->l_flags & LDLM_FL_DESTROYED) {
+ CDEBUG(D_OTHER, "Lock destroyed, not adding to resource\n");
+ return;
+ }
+
+ LASSERT(list_empty(&lock->l_res_link));
+ LASSERT(list_empty(&lock->l_sl_mode));
+ LASSERT(list_empty(&lock->l_sl_policy));
+
+ /*
+ * lock->link == prev->link means lock is first starting the group.
+ * Don't re-add to itself to suppress kernel warnings.
+ */
+ if (&lock->l_res_link != prev->res_link)
+ list_add(&lock->l_res_link, prev->res_link);
+ if (&lock->l_sl_mode != prev->mode_link)
+ list_add(&lock->l_sl_mode, prev->mode_link);
+ if (&lock->l_sl_policy != prev->policy_link)
+ list_add(&lock->l_sl_policy, prev->policy_link);
+}
+
+/**
+ * Add a lock to granted list on a resource maintaining skiplist
+ * correctness.
+ */
+static void ldlm_grant_lock_with_skiplist(struct ldlm_lock *lock)
+{
+ struct sl_insert_point prev;
+
+ LASSERT(lock->l_req_mode == lock->l_granted_mode);
+
+ search_granted_lock(&lock->l_resource->lr_granted, lock, &prev);
+ ldlm_granted_list_add_lock(lock, &prev);
+}
+
+/**
+ * Perform lock granting bookkeeping.
+ *
+ * Includes putting the lock into granted list and updating lock mode.
+ * NOTE: called by
+ * - ldlm_lock_enqueue
+ * - ldlm_reprocess_queue
+ * - ldlm_lock_convert
+ *
+ * must be called with lr_lock held
+ */
+void ldlm_grant_lock(struct ldlm_lock *lock, struct list_head *work_list)
+{
+ struct ldlm_resource *res = lock->l_resource;
+
+ check_res_locked(res);
+
+ lock->l_granted_mode = lock->l_req_mode;
+ if (res->lr_type == LDLM_PLAIN || res->lr_type == LDLM_IBITS)
+ ldlm_grant_lock_with_skiplist(lock);
+ else if (res->lr_type == LDLM_EXTENT)
+ ldlm_extent_add_lock(res, lock);
+ else
+ ldlm_resource_add_lock(res, &res->lr_granted, lock);
+
+ if (lock->l_granted_mode < res->lr_most_restr)
+ res->lr_most_restr = lock->l_granted_mode;
+
+ if (work_list && lock->l_completion_ast != NULL)
+ ldlm_add_ast_work_item(lock, NULL, work_list);
+
+ ldlm_pool_add(&ldlm_res_to_ns(res)->ns_pool, lock);
+}
+
+/**
+ * Search for a lock with given properties in a queue.
+ *
+ * \retval a referenced lock or NULL. See the flag descriptions below, in the
+ * comment above ldlm_lock_match
+ */
+static struct ldlm_lock *search_queue(struct list_head *queue,
+ ldlm_mode_t *mode,
+ ldlm_policy_data_t *policy,
+ struct ldlm_lock *old_lock,
+ __u64 flags, int unref)
+{
+ struct ldlm_lock *lock;
+ struct list_head *tmp;
+
+ list_for_each(tmp, queue) {
+ ldlm_mode_t match;
+
+ lock = list_entry(tmp, struct ldlm_lock, l_res_link);
+
+ if (lock == old_lock)
+ break;
+
+ /* Check if this lock can be matched.
+ * Used by LU-2919(exclusive open) for open lease lock */
+ if (ldlm_is_excl(lock))
+ continue;
+
+ /* llite sometimes wants to match locks that will be
+ * canceled when their users drop, but we allow it to match
+ * if it passes in CBPENDING and the lock still has users.
+ * this is generally only going to be used by children
+ * whose parents already hold a lock so forward progress
+ * can still happen. */
+ if (lock->l_flags & LDLM_FL_CBPENDING &&
+ !(flags & LDLM_FL_CBPENDING))
+ continue;
+ if (!unref && lock->l_flags & LDLM_FL_CBPENDING &&
+ lock->l_readers == 0 && lock->l_writers == 0)
+ continue;
+
+ if (!(lock->l_req_mode & *mode))
+ continue;
+ match = lock->l_req_mode;
+
+ if (lock->l_resource->lr_type == LDLM_EXTENT &&
+ (lock->l_policy_data.l_extent.start >
+ policy->l_extent.start ||
+ lock->l_policy_data.l_extent.end < policy->l_extent.end))
+ continue;
+
+ if (unlikely(match == LCK_GROUP) &&
+ lock->l_resource->lr_type == LDLM_EXTENT &&
+ lock->l_policy_data.l_extent.gid != policy->l_extent.gid)
+ continue;
+
+ /* We match if we have existing lock with same or wider set
+ of bits. */
+ if (lock->l_resource->lr_type == LDLM_IBITS &&
+ ((lock->l_policy_data.l_inodebits.bits &
+ policy->l_inodebits.bits) !=
+ policy->l_inodebits.bits))
+ continue;
+
+ if (!unref && (lock->l_flags & LDLM_FL_GONE_MASK))
+ continue;
+
+ if ((flags & LDLM_FL_LOCAL_ONLY) &&
+ !(lock->l_flags & LDLM_FL_LOCAL))
+ continue;
+
+ if (flags & LDLM_FL_TEST_LOCK) {
+ LDLM_LOCK_GET(lock);
+ ldlm_lock_touch_in_lru(lock);
+ } else {
+ ldlm_lock_addref_internal_nolock(lock, match);
+ }
+ *mode = match;
+ return lock;
+ }
+
+ return NULL;
+}
+
+void ldlm_lock_fail_match_locked(struct ldlm_lock *lock)
+{
+ if ((lock->l_flags & LDLM_FL_FAIL_NOTIFIED) == 0) {
+ lock->l_flags |= LDLM_FL_FAIL_NOTIFIED;
+ wake_up_all(&lock->l_waitq);
+ }
+}
+EXPORT_SYMBOL(ldlm_lock_fail_match_locked);
+
+void ldlm_lock_fail_match(struct ldlm_lock *lock)
+{
+ lock_res_and_lock(lock);
+ ldlm_lock_fail_match_locked(lock);
+ unlock_res_and_lock(lock);
+}
+EXPORT_SYMBOL(ldlm_lock_fail_match);
+
+/**
+ * Mark lock as "matchable" by OST.
+ *
+ * Used to prevent certain races in LOV/OSC where the lock is granted, but LVB
+ * is not yet valid.
+ * Assumes LDLM lock is already locked.
+ */
+void ldlm_lock_allow_match_locked(struct ldlm_lock *lock)
+{
+ lock->l_flags |= LDLM_FL_LVB_READY;
+ wake_up_all(&lock->l_waitq);
+}
+EXPORT_SYMBOL(ldlm_lock_allow_match_locked);
+
+/**
+ * Mark lock as "matchable" by OST.
+ * Locks the lock and then \see ldlm_lock_allow_match_locked
+ */
+void ldlm_lock_allow_match(struct ldlm_lock *lock)
+{
+ lock_res_and_lock(lock);
+ ldlm_lock_allow_match_locked(lock);
+ unlock_res_and_lock(lock);
+}
+EXPORT_SYMBOL(ldlm_lock_allow_match);
+
+/**
+ * Attempt to find a lock with specified properties.
+ *
+ * Typically returns a reference to matched lock unless LDLM_FL_TEST_LOCK is
+ * set in \a flags
+ *
+ * Can be called in two ways:
+ *
+ * If 'ns' is NULL, then lockh describes an existing lock that we want to look
+ * for a duplicate of.
+ *
+ * Otherwise, all of the fields must be filled in, to match against.
+ *
+ * If 'flags' contains LDLM_FL_LOCAL_ONLY, then only match local locks on the
+ * server (ie, connh is NULL)
+ * If 'flags' contains LDLM_FL_BLOCK_GRANTED, then only locks on the granted
+ * list will be considered
+ * If 'flags' contains LDLM_FL_CBPENDING, then locks that have been marked
+ * to be canceled can still be matched as long as they still have reader
+ * or writer referneces
+ * If 'flags' contains LDLM_FL_TEST_LOCK, then don't actually reference a lock,
+ * just tell us if we would have matched.
+ *
+ * \retval 1 if it finds an already-existing lock that is compatible; in this
+ * case, lockh is filled in with a addref()ed lock
+ *
+ * We also check security context, and if that fails we simply return 0 (to
+ * keep caller code unchanged), the context failure will be discovered by
+ * caller sometime later.
+ */
+ldlm_mode_t ldlm_lock_match(struct ldlm_namespace *ns, __u64 flags,
+ const struct ldlm_res_id *res_id, ldlm_type_t type,
+ ldlm_policy_data_t *policy, ldlm_mode_t mode,
+ struct lustre_handle *lockh, int unref)
+{
+ struct ldlm_resource *res;
+ struct ldlm_lock *lock, *old_lock = NULL;
+ int rc = 0;
+
+ if (ns == NULL) {
+ old_lock = ldlm_handle2lock(lockh);
+ LASSERT(old_lock);
+
+ ns = ldlm_lock_to_ns(old_lock);
+ res_id = &old_lock->l_resource->lr_name;
+ type = old_lock->l_resource->lr_type;
+ mode = old_lock->l_req_mode;
+ }
+
+ res = ldlm_resource_get(ns, NULL, res_id, type, 0);
+ if (res == NULL) {
+ LASSERT(old_lock == NULL);
+ return 0;
+ }
+
+ LDLM_RESOURCE_ADDREF(res);
+ lock_res(res);
+
+ lock = search_queue(&res->lr_granted, &mode, policy, old_lock,
+ flags, unref);
+ if (lock != NULL) {
+ rc = 1;
+ goto out;
+ }
+ if (flags & LDLM_FL_BLOCK_GRANTED) {
+ rc = 0;
+ goto out;
+ }
+ lock = search_queue(&res->lr_converting, &mode, policy, old_lock,
+ flags, unref);
+ if (lock != NULL) {
+ rc = 1;
+ goto out;
+ }
+ lock = search_queue(&res->lr_waiting, &mode, policy, old_lock,
+ flags, unref);
+ if (lock != NULL) {
+ rc = 1;
+ goto out;
+ }
+
+ out:
+ unlock_res(res);
+ LDLM_RESOURCE_DELREF(res);
+ ldlm_resource_putref(res);
+
+ if (lock) {
+ ldlm_lock2handle(lock, lockh);
+ if ((flags & LDLM_FL_LVB_READY) &&
+ (!(lock->l_flags & LDLM_FL_LVB_READY))) {
+ __u64 wait_flags = LDLM_FL_LVB_READY |
+ LDLM_FL_DESTROYED | LDLM_FL_FAIL_NOTIFIED;
+ struct l_wait_info lwi;
+
+ if (lock->l_completion_ast) {
+ int err = lock->l_completion_ast(lock,
+ LDLM_FL_WAIT_NOREPROC,
+ NULL);
+ if (err) {
+ if (flags & LDLM_FL_TEST_LOCK)
+ LDLM_LOCK_RELEASE(lock);
+ else
+ ldlm_lock_decref_internal(lock,
+ mode);
+ rc = 0;
+ goto out2;
+ }
+ }
+
+ lwi = LWI_TIMEOUT_INTR(cfs_time_seconds(obd_timeout),
+ NULL, LWI_ON_SIGNAL_NOOP, NULL);
+
+ /* XXX FIXME see comment on CAN_MATCH in lustre_dlm.h */
+ l_wait_event(lock->l_waitq,
+ lock->l_flags & wait_flags,
+ &lwi);
+ if (!(lock->l_flags & LDLM_FL_LVB_READY)) {
+ if (flags & LDLM_FL_TEST_LOCK)
+ LDLM_LOCK_RELEASE(lock);
+ else
+ ldlm_lock_decref_internal(lock, mode);
+ rc = 0;
+ }
+ }
+ }
+ out2:
+ if (rc) {
+ LDLM_DEBUG(lock, "matched (%llu %llu)",
+ (type == LDLM_PLAIN || type == LDLM_IBITS) ?
+ res_id->name[2] : policy->l_extent.start,
+ (type == LDLM_PLAIN || type == LDLM_IBITS) ?
+ res_id->name[3] : policy->l_extent.end);
+
+ /* check user's security context */
+ if (lock->l_conn_export &&
+ sptlrpc_import_check_ctx(
+ class_exp2cliimp(lock->l_conn_export))) {
+ if (!(flags & LDLM_FL_TEST_LOCK))
+ ldlm_lock_decref_internal(lock, mode);
+ rc = 0;
+ }
+
+ if (flags & LDLM_FL_TEST_LOCK)
+ LDLM_LOCK_RELEASE(lock);
+
+ } else if (!(flags & LDLM_FL_TEST_LOCK)) {/*less verbose for test-only*/
+ LDLM_DEBUG_NOLOCK("not matched ns %p type %u mode %u res %llu/%llu (%llu %llu)",
+ ns, type, mode, res_id->name[0],
+ res_id->name[1],
+ (type == LDLM_PLAIN || type == LDLM_IBITS) ?
+ res_id->name[2] : policy->l_extent.start,
+ (type == LDLM_PLAIN || type == LDLM_IBITS) ?
+ res_id->name[3] : policy->l_extent.end);
+ }
+ if (old_lock)
+ LDLM_LOCK_PUT(old_lock);
+
+ return rc ? mode : 0;
+}
+EXPORT_SYMBOL(ldlm_lock_match);
+
+ldlm_mode_t ldlm_revalidate_lock_handle(struct lustre_handle *lockh,
+ __u64 *bits)
+{
+ struct ldlm_lock *lock;
+ ldlm_mode_t mode = 0;
+
+ lock = ldlm_handle2lock(lockh);
+ if (lock != NULL) {
+ lock_res_and_lock(lock);
+ if (lock->l_flags & LDLM_FL_GONE_MASK)
+ goto out;
+
+ if (lock->l_flags & LDLM_FL_CBPENDING &&
+ lock->l_readers == 0 && lock->l_writers == 0)
+ goto out;
+
+ if (bits)
+ *bits = lock->l_policy_data.l_inodebits.bits;
+ mode = lock->l_granted_mode;
+ ldlm_lock_addref_internal_nolock(lock, mode);
+ }
+
+out:
+ if (lock != NULL) {
+ unlock_res_and_lock(lock);
+ LDLM_LOCK_PUT(lock);
+ }
+ return mode;
+}
+EXPORT_SYMBOL(ldlm_revalidate_lock_handle);
+
+/** The caller must guarantee that the buffer is large enough. */
+int ldlm_fill_lvb(struct ldlm_lock *lock, struct req_capsule *pill,
+ enum req_location loc, void *data, int size)
+{
+ void *lvb;
+
+ LASSERT(data != NULL);
+ LASSERT(size >= 0);
+
+ switch (lock->l_lvb_type) {
+ case LVB_T_OST:
+ if (size == sizeof(struct ost_lvb)) {
+ if (loc == RCL_CLIENT)
+ lvb = req_capsule_client_swab_get(pill,
+ &RMF_DLM_LVB,
+ lustre_swab_ost_lvb);
+ else
+ lvb = req_capsule_server_swab_get(pill,
+ &RMF_DLM_LVB,
+ lustre_swab_ost_lvb);
+ if (unlikely(lvb == NULL)) {
+ LDLM_ERROR(lock, "no LVB");
+ return -EPROTO;
+ }
+
+ memcpy(data, lvb, size);
+ } else if (size == sizeof(struct ost_lvb_v1)) {
+ struct ost_lvb *olvb = data;
+
+ if (loc == RCL_CLIENT)
+ lvb = req_capsule_client_swab_get(pill,
+ &RMF_DLM_LVB,
+ lustre_swab_ost_lvb_v1);
+ else
+ lvb = req_capsule_server_sized_swab_get(pill,
+ &RMF_DLM_LVB, size,
+ lustre_swab_ost_lvb_v1);
+ if (unlikely(lvb == NULL)) {
+ LDLM_ERROR(lock, "no LVB");
+ return -EPROTO;
+ }
+
+ memcpy(data, lvb, size);
+ olvb->lvb_mtime_ns = 0;
+ olvb->lvb_atime_ns = 0;
+ olvb->lvb_ctime_ns = 0;
+ } else {
+ LDLM_ERROR(lock, "Replied unexpected ost LVB size %d",
+ size);
+ return -EINVAL;
+ }
+ break;
+ case LVB_T_LQUOTA:
+ if (size == sizeof(struct lquota_lvb)) {
+ if (loc == RCL_CLIENT)
+ lvb = req_capsule_client_swab_get(pill,
+ &RMF_DLM_LVB,
+ lustre_swab_lquota_lvb);
+ else
+ lvb = req_capsule_server_swab_get(pill,
+ &RMF_DLM_LVB,
+ lustre_swab_lquota_lvb);
+ if (unlikely(lvb == NULL)) {
+ LDLM_ERROR(lock, "no LVB");
+ return -EPROTO;
+ }
+
+ memcpy(data, lvb, size);
+ } else {
+ LDLM_ERROR(lock,
+ "Replied unexpected lquota LVB size %d",
+ size);
+ return -EINVAL;
+ }
+ break;
+ case LVB_T_LAYOUT:
+ if (size == 0)
+ break;
+
+ if (loc == RCL_CLIENT)
+ lvb = req_capsule_client_get(pill, &RMF_DLM_LVB);
+ else
+ lvb = req_capsule_server_get(pill, &RMF_DLM_LVB);
+ if (unlikely(lvb == NULL)) {
+ LDLM_ERROR(lock, "no LVB");
+ return -EPROTO;
+ }
+
+ memcpy(data, lvb, size);
+ break;
+ default:
+ LDLM_ERROR(lock, "Unknown LVB type: %d\n", lock->l_lvb_type);
+ dump_stack();
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/**
+ * Create and fill in new LDLM lock with specified properties.
+ * Returns a referenced lock
+ */
+struct ldlm_lock *ldlm_lock_create(struct ldlm_namespace *ns,
+ const struct ldlm_res_id *res_id,
+ ldlm_type_t type,
+ ldlm_mode_t mode,
+ const struct ldlm_callback_suite *cbs,
+ void *data, __u32 lvb_len,
+ enum lvb_type lvb_type)
+{
+ struct ldlm_lock *lock;
+ struct ldlm_resource *res;
+
+ res = ldlm_resource_get(ns, NULL, res_id, type, 1);
+ if (res == NULL)
+ return NULL;
+
+ lock = ldlm_lock_new(res);
+
+ if (lock == NULL)
+ return NULL;
+
+ lock->l_req_mode = mode;
+ lock->l_ast_data = data;
+ lock->l_pid = current_pid();
+ if (ns_is_server(ns))
+ lock->l_flags |= LDLM_FL_NS_SRV;
+ if (cbs) {
+ lock->l_blocking_ast = cbs->lcs_blocking;
+ lock->l_completion_ast = cbs->lcs_completion;
+ lock->l_glimpse_ast = cbs->lcs_glimpse;
+ }
+
+ lock->l_tree_node = NULL;
+ /* if this is the extent lock, allocate the interval tree node */
+ if (type == LDLM_EXTENT) {
+ if (ldlm_interval_alloc(lock) == NULL)
+ goto out;
+ }
+
+ if (lvb_len) {
+ lock->l_lvb_len = lvb_len;
+ OBD_ALLOC(lock->l_lvb_data, lvb_len);
+ if (lock->l_lvb_data == NULL)
+ goto out;
+ }
+
+ lock->l_lvb_type = lvb_type;
+ if (OBD_FAIL_CHECK(OBD_FAIL_LDLM_NEW_LOCK))
+ goto out;
+
+ return lock;
+
+out:
+ ldlm_lock_destroy(lock);
+ LDLM_LOCK_RELEASE(lock);
+ return NULL;
+}
+
+/**
+ * Enqueue (request) a lock.
+ *
+ * Does not block. As a result of enqueue the lock would be put
+ * into granted or waiting list.
+ *
+ * If namespace has intent policy sent and the lock has LDLM_FL_HAS_INTENT flag
+ * set, skip all the enqueueing and delegate lock processing to intent policy
+ * function.
+ */
+ldlm_error_t ldlm_lock_enqueue(struct ldlm_namespace *ns,
+ struct ldlm_lock **lockp,
+ void *cookie, __u64 *flags)
+{
+ struct ldlm_lock *lock = *lockp;
+ struct ldlm_resource *res = lock->l_resource;
+ int local = ns_is_client(ldlm_res_to_ns(res));
+ ldlm_error_t rc = ELDLM_OK;
+ struct ldlm_interval *node = NULL;
+
+ lock->l_last_activity = get_seconds();
+ /* policies are not executed on the client or during replay */
+ if ((*flags & (LDLM_FL_HAS_INTENT|LDLM_FL_REPLAY)) == LDLM_FL_HAS_INTENT
+ && !local && ns->ns_policy) {
+ rc = ns->ns_policy(ns, lockp, cookie, lock->l_req_mode, *flags,
+ NULL);
+ if (rc == ELDLM_LOCK_REPLACED) {
+ /* The lock that was returned has already been granted,
+ * and placed into lockp. If it's not the same as the
+ * one we passed in, then destroy the old one and our
+ * work here is done. */
+ if (lock != *lockp) {
+ ldlm_lock_destroy(lock);
+ LDLM_LOCK_RELEASE(lock);
+ }
+ *flags |= LDLM_FL_LOCK_CHANGED;
+ return 0;
+ } else if (rc != ELDLM_OK ||
+ (rc == ELDLM_OK && (*flags & LDLM_FL_INTENT_ONLY))) {
+ ldlm_lock_destroy(lock);
+ return rc;
+ }
+ }
+
+ /* For a replaying lock, it might be already in granted list. So
+ * unlinking the lock will cause the interval node to be freed, we
+ * have to allocate the interval node early otherwise we can't regrant
+ * this lock in the future. - jay */
+ if (!local && (*flags & LDLM_FL_REPLAY) && res->lr_type == LDLM_EXTENT)
+ OBD_SLAB_ALLOC_PTR_GFP(node, ldlm_interval_slab, GFP_NOFS);
+
+ lock_res_and_lock(lock);
+ if (local && lock->l_req_mode == lock->l_granted_mode) {
+ /* The server returned a blocked lock, but it was granted
+ * before we got a chance to actually enqueue it. We don't
+ * need to do anything else. */
+ *flags &= ~(LDLM_FL_BLOCK_GRANTED |
+ LDLM_FL_BLOCK_CONV | LDLM_FL_BLOCK_WAIT);
+ goto out;
+ }
+
+ ldlm_resource_unlink_lock(lock);
+ if (res->lr_type == LDLM_EXTENT && lock->l_tree_node == NULL) {
+ if (node == NULL) {
+ ldlm_lock_destroy_nolock(lock);
+ rc = -ENOMEM;
+ goto out;
+ }
+
+ INIT_LIST_HEAD(&node->li_group);
+ ldlm_interval_attach(node, lock);
+ node = NULL;
+ }
+
+ /* Some flags from the enqueue want to make it into the AST, via the
+ * lock's l_flags. */
+ lock->l_flags |= *flags & LDLM_FL_AST_DISCARD_DATA;
+
+ /* This distinction between local lock trees is very important; a client
+ * namespace only has information about locks taken by that client, and
+ * thus doesn't have enough information to decide for itself if it can
+ * be granted (below). In this case, we do exactly what the server
+ * tells us to do, as dictated by the 'flags'.
+ *
+ * We do exactly the same thing during recovery, when the server is
+ * more or less trusting the clients not to lie.
+ *
+ * FIXME (bug 268): Detect obvious lies by checking compatibility in
+ * granted/converting queues. */
+ if (local) {
+ if (*flags & LDLM_FL_BLOCK_CONV)
+ ldlm_resource_add_lock(res, &res->lr_converting, lock);
+ else if (*flags & (LDLM_FL_BLOCK_WAIT | LDLM_FL_BLOCK_GRANTED))
+ ldlm_resource_add_lock(res, &res->lr_waiting, lock);
+ else
+ ldlm_grant_lock(lock, NULL);
+ goto out;
+ } else {
+ CERROR("This is client-side-only module, cannot handle LDLM_NAMESPACE_SERVER resource type lock.\n");
+ LBUG();
+ }
+
+out:
+ unlock_res_and_lock(lock);
+ if (node)
+ OBD_SLAB_FREE(node, ldlm_interval_slab, sizeof(*node));
+ return rc;
+}
+
+
+/**
+ * Process a call to blocking AST callback for a lock in ast_work list
+ */
+static int
+ldlm_work_bl_ast_lock(struct ptlrpc_request_set *rqset, void *opaq)
+{
+ struct ldlm_cb_set_arg *arg = opaq;
+ struct ldlm_lock_desc d;
+ int rc;
+ struct ldlm_lock *lock;
+
+ if (list_empty(arg->list))
+ return -ENOENT;
+
+ lock = list_entry(arg->list->next, struct ldlm_lock, l_bl_ast);
+
+ /* nobody should touch l_bl_ast */
+ lock_res_and_lock(lock);
+ list_del_init(&lock->l_bl_ast);
+
+ LASSERT(lock->l_flags & LDLM_FL_AST_SENT);
+ LASSERT(lock->l_bl_ast_run == 0);
+ LASSERT(lock->l_blocking_lock);
+ lock->l_bl_ast_run++;
+ unlock_res_and_lock(lock);
+
+ ldlm_lock2desc(lock->l_blocking_lock, &d);
+
+ rc = lock->l_blocking_ast(lock, &d, (void *)arg, LDLM_CB_BLOCKING);
+ LDLM_LOCK_RELEASE(lock->l_blocking_lock);
+ lock->l_blocking_lock = NULL;
+ LDLM_LOCK_RELEASE(lock);
+
+ return rc;
+}
+
+/**
+ * Process a call to completion AST callback for a lock in ast_work list
+ */
+static int
+ldlm_work_cp_ast_lock(struct ptlrpc_request_set *rqset, void *opaq)
+{
+ struct ldlm_cb_set_arg *arg = opaq;
+ int rc = 0;
+ struct ldlm_lock *lock;
+ ldlm_completion_callback completion_callback;
+
+ if (list_empty(arg->list))
+ return -ENOENT;
+
+ lock = list_entry(arg->list->next, struct ldlm_lock, l_cp_ast);
+
+ /* It's possible to receive a completion AST before we've set
+ * the l_completion_ast pointer: either because the AST arrived
+ * before the reply, or simply because there's a small race
+ * window between receiving the reply and finishing the local
+ * enqueue. (bug 842)
+ *
+ * This can't happen with the blocking_ast, however, because we
+ * will never call the local blocking_ast until we drop our
+ * reader/writer reference, which we won't do until we get the
+ * reply and finish enqueueing. */
+
+ /* nobody should touch l_cp_ast */
+ lock_res_and_lock(lock);
+ list_del_init(&lock->l_cp_ast);
+ LASSERT(lock->l_flags & LDLM_FL_CP_REQD);
+ /* save l_completion_ast since it can be changed by
+ * mds_intent_policy(), see bug 14225 */
+ completion_callback = lock->l_completion_ast;
+ lock->l_flags &= ~LDLM_FL_CP_REQD;
+ unlock_res_and_lock(lock);
+
+ if (completion_callback != NULL)
+ rc = completion_callback(lock, 0, (void *)arg);
+ LDLM_LOCK_RELEASE(lock);
+
+ return rc;
+}
+
+/**
+ * Process a call to revocation AST callback for a lock in ast_work list
+ */
+static int
+ldlm_work_revoke_ast_lock(struct ptlrpc_request_set *rqset, void *opaq)
+{
+ struct ldlm_cb_set_arg *arg = opaq;
+ struct ldlm_lock_desc desc;
+ int rc;
+ struct ldlm_lock *lock;
+
+ if (list_empty(arg->list))
+ return -ENOENT;
+
+ lock = list_entry(arg->list->next, struct ldlm_lock, l_rk_ast);
+ list_del_init(&lock->l_rk_ast);
+
+ /* the desc just pretend to exclusive */
+ ldlm_lock2desc(lock, &desc);
+ desc.l_req_mode = LCK_EX;
+ desc.l_granted_mode = 0;
+
+ rc = lock->l_blocking_ast(lock, &desc, (void *)arg, LDLM_CB_BLOCKING);
+ LDLM_LOCK_RELEASE(lock);
+
+ return rc;
+}
+
+/**
+ * Process a call to glimpse AST callback for a lock in ast_work list
+ */
+int ldlm_work_gl_ast_lock(struct ptlrpc_request_set *rqset, void *opaq)
+{
+ struct ldlm_cb_set_arg *arg = opaq;
+ struct ldlm_glimpse_work *gl_work;
+ struct ldlm_lock *lock;
+ int rc = 0;
+
+ if (list_empty(arg->list))
+ return -ENOENT;
+
+ gl_work = list_entry(arg->list->next, struct ldlm_glimpse_work,
+ gl_list);
+ list_del_init(&gl_work->gl_list);
+
+ lock = gl_work->gl_lock;
+
+ /* transfer the glimpse descriptor to ldlm_cb_set_arg */
+ arg->gl_desc = gl_work->gl_desc;
+
+ /* invoke the actual glimpse callback */
+ if (lock->l_glimpse_ast(lock, (void *)arg) == 0)
+ rc = 1;
+
+ LDLM_LOCK_RELEASE(lock);
+
+ if ((gl_work->gl_flags & LDLM_GL_WORK_NOFREE) == 0)
+ OBD_FREE_PTR(gl_work);
+
+ return rc;
+}
+
+/**
+ * Process list of locks in need of ASTs being sent.
+ *
+ * Used on server to send multiple ASTs together instead of sending one by
+ * one.
+ */
+int ldlm_run_ast_work(struct ldlm_namespace *ns, struct list_head *rpc_list,
+ enum ldlm_desc_ast_t ast_type)
+{
+ struct ldlm_cb_set_arg *arg;
+ set_producer_func work_ast_lock;
+ int rc;
+
+ if (list_empty(rpc_list))
+ return 0;
+
+ OBD_ALLOC_PTR(arg);
+ if (arg == NULL)
+ return -ENOMEM;
+
+ atomic_set(&arg->restart, 0);
+ arg->list = rpc_list;
+
+ switch (ast_type) {
+ case LDLM_WORK_BL_AST:
+ arg->type = LDLM_BL_CALLBACK;
+ work_ast_lock = ldlm_work_bl_ast_lock;
+ break;
+ case LDLM_WORK_CP_AST:
+ arg->type = LDLM_CP_CALLBACK;
+ work_ast_lock = ldlm_work_cp_ast_lock;
+ break;
+ case LDLM_WORK_REVOKE_AST:
+ arg->type = LDLM_BL_CALLBACK;
+ work_ast_lock = ldlm_work_revoke_ast_lock;
+ break;
+ case LDLM_WORK_GL_AST:
+ arg->type = LDLM_GL_CALLBACK;
+ work_ast_lock = ldlm_work_gl_ast_lock;
+ break;
+ default:
+ LBUG();
+ }
+
+ /* We create a ptlrpc request set with flow control extension.
+ * This request set will use the work_ast_lock function to produce new
+ * requests and will send a new request each time one completes in order
+ * to keep the number of requests in flight to ns_max_parallel_ast */
+ arg->set = ptlrpc_prep_fcset(ns->ns_max_parallel_ast ? : UINT_MAX,
+ work_ast_lock, arg);
+ if (arg->set == NULL) {
+ rc = -ENOMEM;
+ goto out;
+ }
+
+ ptlrpc_set_wait(arg->set);
+ ptlrpc_set_destroy(arg->set);
+
+ rc = atomic_read(&arg->restart) ? -ERESTART : 0;
+ goto out;
+out:
+ OBD_FREE_PTR(arg);
+ return rc;
+}
+
+static int reprocess_one_queue(struct ldlm_resource *res, void *closure)
+{
+ ldlm_reprocess_all(res);
+ return LDLM_ITER_CONTINUE;
+}
+
+static int ldlm_reprocess_res(struct cfs_hash *hs, struct cfs_hash_bd *bd,
+ struct hlist_node *hnode, void *arg)
+{
+ struct ldlm_resource *res = cfs_hash_object(hs, hnode);
+ int rc;
+
+ rc = reprocess_one_queue(res, arg);
+
+ return rc == LDLM_ITER_STOP;
+}
+
+/**
+ * Iterate through all resources on a namespace attempting to grant waiting
+ * locks.
+ */
+void ldlm_reprocess_all_ns(struct ldlm_namespace *ns)
+{
+ if (ns != NULL) {
+ cfs_hash_for_each_nolock(ns->ns_rs_hash,
+ ldlm_reprocess_res, NULL);
+ }
+}
+EXPORT_SYMBOL(ldlm_reprocess_all_ns);
+
+/**
+ * Try to grant all waiting locks on a resource.
+ *
+ * Calls ldlm_reprocess_queue on converting and waiting queues.
+ *
+ * Typically called after some resource locks are cancelled to see
+ * if anything could be granted as a result of the cancellation.
+ */
+void ldlm_reprocess_all(struct ldlm_resource *res)
+{
+ LIST_HEAD(rpc_list);
+
+ if (!ns_is_client(ldlm_res_to_ns(res))) {
+ CERROR("This is client-side-only module, cannot handle LDLM_NAMESPACE_SERVER resource type lock.\n");
+ LBUG();
+ }
+}
+
+/**
+ * Helper function to call blocking AST for LDLM lock \a lock in a
+ * "cancelling" mode.
+ */
+void ldlm_cancel_callback(struct ldlm_lock *lock)
+{
+ check_res_locked(lock->l_resource);
+ if (!(lock->l_flags & LDLM_FL_CANCEL)) {
+ lock->l_flags |= LDLM_FL_CANCEL;
+ if (lock->l_blocking_ast) {
+ unlock_res_and_lock(lock);
+ lock->l_blocking_ast(lock, NULL, lock->l_ast_data,
+ LDLM_CB_CANCELING);
+ lock_res_and_lock(lock);
+ } else {
+ LDLM_DEBUG(lock, "no blocking ast");
+ }
+ }
+ lock->l_flags |= LDLM_FL_BL_DONE;
+}
+
+/**
+ * Remove skiplist-enabled LDLM lock \a req from granted list
+ */
+void ldlm_unlink_lock_skiplist(struct ldlm_lock *req)
+{
+ if (req->l_resource->lr_type != LDLM_PLAIN &&
+ req->l_resource->lr_type != LDLM_IBITS)
+ return;
+
+ list_del_init(&req->l_sl_policy);
+ list_del_init(&req->l_sl_mode);
+}
+
+/**
+ * Attempts to cancel LDLM lock \a lock that has no reader/writer references.
+ */
+void ldlm_lock_cancel(struct ldlm_lock *lock)
+{
+ struct ldlm_resource *res;
+ struct ldlm_namespace *ns;
+
+ lock_res_and_lock(lock);
+
+ res = lock->l_resource;
+ ns = ldlm_res_to_ns(res);
+
+ /* Please do not, no matter how tempting, remove this LBUG without
+ * talking to me first. -phik */
+ if (lock->l_readers || lock->l_writers) {
+ LDLM_ERROR(lock, "lock still has references");
+ LBUG();
+ }
+
+ if (lock->l_flags & LDLM_FL_WAITED)
+ ldlm_del_waiting_lock(lock);
+
+ /* Releases cancel callback. */
+ ldlm_cancel_callback(lock);
+
+ /* Yes, second time, just in case it was added again while we were
+ * running with no res lock in ldlm_cancel_callback */
+ if (lock->l_flags & LDLM_FL_WAITED)
+ ldlm_del_waiting_lock(lock);
+
+ ldlm_resource_unlink_lock(lock);
+ ldlm_lock_destroy_nolock(lock);
+
+ if (lock->l_granted_mode == lock->l_req_mode)
+ ldlm_pool_del(&ns->ns_pool, lock);
+
+ /* Make sure we will not be called again for same lock what is possible
+ * if not to zero out lock->l_granted_mode */
+ lock->l_granted_mode = LCK_MINMODE;
+ unlock_res_and_lock(lock);
+}
+EXPORT_SYMBOL(ldlm_lock_cancel);
+
+/**
+ * Set opaque data into the lock that only makes sense to upper layer.
+ */
+int ldlm_lock_set_data(struct lustre_handle *lockh, void *data)
+{
+ struct ldlm_lock *lock = ldlm_handle2lock(lockh);
+ int rc = -EINVAL;
+
+ if (lock) {
+ if (lock->l_ast_data == NULL)
+ lock->l_ast_data = data;
+ if (lock->l_ast_data == data)
+ rc = 0;
+ LDLM_LOCK_PUT(lock);
+ }
+ return rc;
+}
+EXPORT_SYMBOL(ldlm_lock_set_data);
+
+struct export_cl_data {
+ struct obd_export *ecl_exp;
+ int ecl_loop;
+};
+
+/**
+ * Iterator function for ldlm_cancel_locks_for_export.
+ * Cancels passed locks.
+ */
+int ldlm_cancel_locks_for_export_cb(struct cfs_hash *hs, struct cfs_hash_bd *bd,
+ struct hlist_node *hnode, void *data)
+
+{
+ struct export_cl_data *ecl = (struct export_cl_data *)data;
+ struct obd_export *exp = ecl->ecl_exp;
+ struct ldlm_lock *lock = cfs_hash_object(hs, hnode);
+ struct ldlm_resource *res;
+
+ res = ldlm_resource_getref(lock->l_resource);
+ LDLM_LOCK_GET(lock);
+
+ LDLM_DEBUG(lock, "export %p", exp);
+ ldlm_res_lvbo_update(res, NULL, 1);
+ ldlm_lock_cancel(lock);
+ ldlm_reprocess_all(res);
+ ldlm_resource_putref(res);
+ LDLM_LOCK_RELEASE(lock);
+
+ ecl->ecl_loop++;
+ if ((ecl->ecl_loop & -ecl->ecl_loop) == ecl->ecl_loop) {
+ CDEBUG(D_INFO,
+ "Cancel lock %p for export %p (loop %d), still have %d locks left on hash table.\n",
+ lock, exp, ecl->ecl_loop,
+ atomic_read(&hs->hs_count));
+ }
+
+ return 0;
+}
+
+/**
+ * Cancel all locks for given export.
+ *
+ * Typically called on client disconnection/eviction
+ */
+void ldlm_cancel_locks_for_export(struct obd_export *exp)
+{
+ struct export_cl_data ecl = {
+ .ecl_exp = exp,
+ .ecl_loop = 0,
+ };
+
+ cfs_hash_for_each_empty(exp->exp_lock_hash,
+ ldlm_cancel_locks_for_export_cb, &ecl);
+}
+
+/**
+ * Downgrade an exclusive lock.
+ *
+ * A fast variant of ldlm_lock_convert for conversion of exclusive
+ * locks. The conversion is always successful.
+ * Used by Commit on Sharing (COS) code.
+ *
+ * \param lock A lock to convert
+ * \param new_mode new lock mode
+ */
+void ldlm_lock_downgrade(struct ldlm_lock *lock, int new_mode)
+{
+ LASSERT(lock->l_granted_mode & (LCK_PW | LCK_EX));
+ LASSERT(new_mode == LCK_COS);
+
+ lock_res_and_lock(lock);
+ ldlm_resource_unlink_lock(lock);
+ /*
+ * Remove the lock from pool as it will be added again in
+ * ldlm_grant_lock() called below.
+ */
+ ldlm_pool_del(&ldlm_lock_to_ns(lock)->ns_pool, lock);
+
+ lock->l_req_mode = new_mode;
+ ldlm_grant_lock(lock, NULL);
+ unlock_res_and_lock(lock);
+ ldlm_reprocess_all(lock->l_resource);
+}
+EXPORT_SYMBOL(ldlm_lock_downgrade);
+
+/**
+ * Attempt to convert already granted lock to a different mode.
+ *
+ * While lock conversion is not currently used, future client-side
+ * optimizations could take advantage of it to avoid discarding cached
+ * pages on a file.
+ */
+struct ldlm_resource *ldlm_lock_convert(struct ldlm_lock *lock, int new_mode,
+ __u32 *flags)
+{
+ LIST_HEAD(rpc_list);
+ struct ldlm_resource *res;
+ struct ldlm_namespace *ns;
+ int granted = 0;
+ struct ldlm_interval *node;
+
+ /* Just return if mode is unchanged. */
+ if (new_mode == lock->l_granted_mode) {
+ *flags |= LDLM_FL_BLOCK_GRANTED;
+ return lock->l_resource;
+ }
+
+ /* I can't check the type of lock here because the bitlock of lock
+ * is not held here, so do the allocation blindly. -jay */
+ OBD_SLAB_ALLOC_PTR_GFP(node, ldlm_interval_slab, GFP_NOFS);
+ if (node == NULL)
+ /* Actually, this causes EDEADLOCK to be returned */
+ return NULL;
+
+ LASSERTF((new_mode == LCK_PW && lock->l_granted_mode == LCK_PR),
+ "new_mode %u, granted %u\n", new_mode, lock->l_granted_mode);
+
+ lock_res_and_lock(lock);
+
+ res = lock->l_resource;
+ ns = ldlm_res_to_ns(res);
+
+ lock->l_req_mode = new_mode;
+ if (res->lr_type == LDLM_PLAIN || res->lr_type == LDLM_IBITS) {
+ ldlm_resource_unlink_lock(lock);
+ } else {
+ ldlm_resource_unlink_lock(lock);
+ if (res->lr_type == LDLM_EXTENT) {
+ /* FIXME: ugly code, I have to attach the lock to a
+ * interval node again since perhaps it will be granted
+ * soon */
+ INIT_LIST_HEAD(&node->li_group);
+ ldlm_interval_attach(node, lock);
+ node = NULL;
+ }
+ }
+
+ /*
+ * Remove old lock from the pool before adding the lock with new
+ * mode below in ->policy()
+ */
+ ldlm_pool_del(&ns->ns_pool, lock);
+
+ /* If this is a local resource, put it on the appropriate list. */
+ if (ns_is_client(ldlm_res_to_ns(res))) {
+ if (*flags & (LDLM_FL_BLOCK_CONV | LDLM_FL_BLOCK_GRANTED)) {
+ ldlm_resource_add_lock(res, &res->lr_converting, lock);
+ } else {
+ /* This should never happen, because of the way the
+ * server handles conversions. */
+ LDLM_ERROR(lock, "Erroneous flags %x on local lock\n",
+ *flags);
+ LBUG();
+
+ ldlm_grant_lock(lock, &rpc_list);
+ granted = 1;
+ /* FIXME: completion handling not with lr_lock held ! */
+ if (lock->l_completion_ast)
+ lock->l_completion_ast(lock, 0, NULL);
+ }
+ } else {
+ CERROR("This is client-side-only module, cannot handle LDLM_NAMESPACE_SERVER resource type lock.\n");
+ LBUG();
+ }
+ unlock_res_and_lock(lock);
+
+ if (granted)
+ ldlm_run_ast_work(ns, &rpc_list, LDLM_WORK_CP_AST);
+ if (node)
+ OBD_SLAB_FREE(node, ldlm_interval_slab, sizeof(*node));
+ return res;
+}
+EXPORT_SYMBOL(ldlm_lock_convert);
+
+/**
+ * Print lock with lock handle \a lockh description into debug log.
+ *
+ * Used when printing all locks on a resource for debug purposes.
+ */
+void ldlm_lock_dump_handle(int level, struct lustre_handle *lockh)
+{
+ struct ldlm_lock *lock;
+
+ if (!((libcfs_debug | D_ERROR) & level))
+ return;
+
+ lock = ldlm_handle2lock(lockh);
+ if (lock == NULL)
+ return;
+
+ LDLM_DEBUG_LIMIT(level, lock, "###");
+
+ LDLM_LOCK_PUT(lock);
+}
+EXPORT_SYMBOL(ldlm_lock_dump_handle);
+
+/**
+ * Print lock information with custom message into debug log.
+ * Helper function.
+ */
+void _ldlm_lock_debug(struct ldlm_lock *lock,
+ struct libcfs_debug_msg_data *msgdata,
+ const char *fmt, ...)
+{
+ va_list args;
+ struct obd_export *exp = lock->l_export;
+ struct ldlm_resource *resource = lock->l_resource;
+ char *nid = "local";
+
+ va_start(args, fmt);
+
+ if (exp && exp->exp_connection) {
+ nid = libcfs_nid2str(exp->exp_connection->c_peer.nid);
+ } else if (exp && exp->exp_obd != NULL) {
+ struct obd_import *imp = exp->exp_obd->u.cli.cl_import;
+
+ nid = libcfs_nid2str(imp->imp_connection->c_peer.nid);
+ }
+
+ if (resource == NULL) {
+ libcfs_debug_vmsg2(msgdata, fmt, args,
+ " ns: \?\? lock: %p/%#llx lrc: %d/%d,%d mode: %s/%s res: \?\? rrc=\?\? type: \?\?\? flags: %#llx nid: %s remote: %#llx expref: %d pid: %u timeout: %lu lvb_type: %d\n",
+ lock,
+ lock->l_handle.h_cookie, atomic_read(&lock->l_refc),
+ lock->l_readers, lock->l_writers,
+ ldlm_lockname[lock->l_granted_mode],
+ ldlm_lockname[lock->l_req_mode],
+ lock->l_flags, nid, lock->l_remote_handle.cookie,
+ exp ? atomic_read(&exp->exp_refcount) : -99,
+ lock->l_pid, lock->l_callback_timeout, lock->l_lvb_type);
+ va_end(args);
+ return;
+ }
+
+ switch (resource->lr_type) {
+ case LDLM_EXTENT:
+ libcfs_debug_vmsg2(msgdata, fmt, args,
+ " ns: %s lock: %p/%#llx lrc: %d/%d,%d mode: %s/%s res: " DLDLMRES " rrc: %d type: %s [%llu->%llu] (req %llu->%llu) flags: %#llx nid: %s remote: %#llx expref: %d pid: %u timeout: %lu lvb_type: %d\n",
+ ldlm_lock_to_ns_name(lock), lock,
+ lock->l_handle.h_cookie, atomic_read(&lock->l_refc),
+ lock->l_readers, lock->l_writers,
+ ldlm_lockname[lock->l_granted_mode],
+ ldlm_lockname[lock->l_req_mode],
+ PLDLMRES(resource),
+ atomic_read(&resource->lr_refcount),
+ ldlm_typename[resource->lr_type],
+ lock->l_policy_data.l_extent.start,
+ lock->l_policy_data.l_extent.end,
+ lock->l_req_extent.start, lock->l_req_extent.end,
+ lock->l_flags, nid, lock->l_remote_handle.cookie,
+ exp ? atomic_read(&exp->exp_refcount) : -99,
+ lock->l_pid, lock->l_callback_timeout,
+ lock->l_lvb_type);
+ break;
+
+ case LDLM_FLOCK:
+ libcfs_debug_vmsg2(msgdata, fmt, args,
+ " ns: %s lock: %p/%#llx lrc: %d/%d,%d mode: %s/%s res: " DLDLMRES " rrc: %d type: %s pid: %d [%llu->%llu] flags: %#llx nid: %s remote: %#llx expref: %d pid: %u timeout: %lu\n",
+ ldlm_lock_to_ns_name(lock), lock,
+ lock->l_handle.h_cookie, atomic_read(&lock->l_refc),
+ lock->l_readers, lock->l_writers,
+ ldlm_lockname[lock->l_granted_mode],
+ ldlm_lockname[lock->l_req_mode],
+ PLDLMRES(resource),
+ atomic_read(&resource->lr_refcount),
+ ldlm_typename[resource->lr_type],
+ lock->l_policy_data.l_flock.pid,
+ lock->l_policy_data.l_flock.start,
+ lock->l_policy_data.l_flock.end,
+ lock->l_flags, nid, lock->l_remote_handle.cookie,
+ exp ? atomic_read(&exp->exp_refcount) : -99,
+ lock->l_pid, lock->l_callback_timeout);
+ break;
+
+ case LDLM_IBITS:
+ libcfs_debug_vmsg2(msgdata, fmt, args,
+ " ns: %s lock: %p/%#llx lrc: %d/%d,%d mode: %s/%s res: " DLDLMRES " bits %#llx rrc: %d type: %s flags: %#llx nid: %s remote: %#llx expref: %d pid: %u timeout: %lu lvb_type: %d\n",
+ ldlm_lock_to_ns_name(lock),
+ lock, lock->l_handle.h_cookie,
+ atomic_read(&lock->l_refc),
+ lock->l_readers, lock->l_writers,
+ ldlm_lockname[lock->l_granted_mode],
+ ldlm_lockname[lock->l_req_mode],
+ PLDLMRES(resource),
+ lock->l_policy_data.l_inodebits.bits,
+ atomic_read(&resource->lr_refcount),
+ ldlm_typename[resource->lr_type],
+ lock->l_flags, nid, lock->l_remote_handle.cookie,
+ exp ? atomic_read(&exp->exp_refcount) : -99,
+ lock->l_pid, lock->l_callback_timeout,
+ lock->l_lvb_type);
+ break;
+
+ default:
+ libcfs_debug_vmsg2(msgdata, fmt, args,
+ " ns: %s lock: %p/%#llx lrc: %d/%d,%d mode: %s/%s res: " DLDLMRES " rrc: %d type: %s flags: %#llx nid: %s remote: %#llx expref: %d pid: %u timeout: %lu lvb_type: %d\n",
+ ldlm_lock_to_ns_name(lock),
+ lock, lock->l_handle.h_cookie,
+ atomic_read(&lock->l_refc),
+ lock->l_readers, lock->l_writers,
+ ldlm_lockname[lock->l_granted_mode],
+ ldlm_lockname[lock->l_req_mode],
+ PLDLMRES(resource),
+ atomic_read(&resource->lr_refcount),
+ ldlm_typename[resource->lr_type],
+ lock->l_flags, nid, lock->l_remote_handle.cookie,
+ exp ? atomic_read(&exp->exp_refcount) : -99,
+ lock->l_pid, lock->l_callback_timeout,
+ lock->l_lvb_type);
+ break;
+ }
+ va_end(args);
+}
+EXPORT_SYMBOL(_ldlm_lock_debug);
diff --git a/drivers/staging/lustre/lustre/ldlm/ldlm_lockd.c b/drivers/staging/lustre/lustre/ldlm/ldlm_lockd.c
new file mode 100644
index 000000000..08a91f5d9
--- /dev/null
+++ b/drivers/staging/lustre/lustre/ldlm/ldlm_lockd.c
@@ -0,0 +1,1191 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2002, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2010, 2012, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ *
+ * lustre/ldlm/ldlm_lockd.c
+ *
+ * Author: Peter Braam <braam@clusterfs.com>
+ * Author: Phil Schwan <phil@clusterfs.com>
+ */
+
+#define DEBUG_SUBSYSTEM S_LDLM
+
+#include "../../include/linux/libcfs/libcfs.h"
+#include "../include/lustre_dlm.h"
+#include "../include/obd_class.h"
+#include <linux/list.h>
+#include "ldlm_internal.h"
+
+static int ldlm_num_threads;
+module_param(ldlm_num_threads, int, 0444);
+MODULE_PARM_DESC(ldlm_num_threads, "number of DLM service threads to start");
+
+static char *ldlm_cpts;
+module_param(ldlm_cpts, charp, 0444);
+MODULE_PARM_DESC(ldlm_cpts, "CPU partitions ldlm threads should run on");
+
+static struct mutex ldlm_ref_mutex;
+static int ldlm_refcount;
+
+struct ldlm_cb_async_args {
+ struct ldlm_cb_set_arg *ca_set_arg;
+ struct ldlm_lock *ca_lock;
+};
+
+/* LDLM state */
+
+static struct ldlm_state *ldlm_state;
+
+inline unsigned long round_timeout(unsigned long timeout)
+{
+ return cfs_time_seconds((int)cfs_duration_sec(cfs_time_sub(timeout, 0)) + 1);
+}
+
+/* timeout for initial callback (AST) reply (bz10399) */
+static inline unsigned int ldlm_get_rq_timeout(void)
+{
+ /* Non-AT value */
+ unsigned int timeout = min(ldlm_timeout, obd_timeout / 3);
+
+ return timeout < 1 ? 1 : timeout;
+}
+
+#define ELT_STOPPED 0
+#define ELT_READY 1
+#define ELT_TERMINATE 2
+
+struct ldlm_bl_pool {
+ spinlock_t blp_lock;
+
+ /*
+ * blp_prio_list is used for callbacks that should be handled
+ * as a priority. It is used for LDLM_FL_DISCARD_DATA requests.
+ * see bug 13843
+ */
+ struct list_head blp_prio_list;
+
+ /*
+ * blp_list is used for all other callbacks which are likely
+ * to take longer to process.
+ */
+ struct list_head blp_list;
+
+ wait_queue_head_t blp_waitq;
+ struct completion blp_comp;
+ atomic_t blp_num_threads;
+ atomic_t blp_busy_threads;
+ int blp_min_threads;
+ int blp_max_threads;
+};
+
+struct ldlm_bl_work_item {
+ struct list_head blwi_entry;
+ struct ldlm_namespace *blwi_ns;
+ struct ldlm_lock_desc blwi_ld;
+ struct ldlm_lock *blwi_lock;
+ struct list_head blwi_head;
+ int blwi_count;
+ struct completion blwi_comp;
+ ldlm_cancel_flags_t blwi_flags;
+ int blwi_mem_pressure;
+};
+
+
+int ldlm_del_waiting_lock(struct ldlm_lock *lock)
+{
+ return 0;
+}
+
+int ldlm_refresh_waiting_lock(struct ldlm_lock *lock, int timeout)
+{
+ return 0;
+}
+
+
+
+/**
+ * Callback handler for receiving incoming blocking ASTs.
+ *
+ * This can only happen on client side.
+ */
+void ldlm_handle_bl_callback(struct ldlm_namespace *ns,
+ struct ldlm_lock_desc *ld, struct ldlm_lock *lock)
+{
+ int do_ast;
+
+ LDLM_DEBUG(lock, "client blocking AST callback handler");
+
+ lock_res_and_lock(lock);
+ lock->l_flags |= LDLM_FL_CBPENDING;
+
+ if (lock->l_flags & LDLM_FL_CANCEL_ON_BLOCK)
+ lock->l_flags |= LDLM_FL_CANCEL;
+
+ do_ast = !lock->l_readers && !lock->l_writers;
+ unlock_res_and_lock(lock);
+
+ if (do_ast) {
+ CDEBUG(D_DLMTRACE,
+ "Lock %p already unused, calling callback (%p)\n", lock,
+ lock->l_blocking_ast);
+ if (lock->l_blocking_ast != NULL)
+ lock->l_blocking_ast(lock, ld, lock->l_ast_data,
+ LDLM_CB_BLOCKING);
+ } else {
+ CDEBUG(D_DLMTRACE,
+ "Lock %p is referenced, will be cancelled later\n",
+ lock);
+ }
+
+ LDLM_DEBUG(lock, "client blocking callback handler END");
+ LDLM_LOCK_RELEASE(lock);
+}
+
+/**
+ * Callback handler for receiving incoming completion ASTs.
+ *
+ * This only can happen on client side.
+ */
+static void ldlm_handle_cp_callback(struct ptlrpc_request *req,
+ struct ldlm_namespace *ns,
+ struct ldlm_request *dlm_req,
+ struct ldlm_lock *lock)
+{
+ int lvb_len;
+ LIST_HEAD(ast_list);
+ int rc = 0;
+
+ LDLM_DEBUG(lock, "client completion callback handler START");
+
+ if (OBD_FAIL_CHECK(OBD_FAIL_LDLM_CANCEL_BL_CB_RACE)) {
+ int to = cfs_time_seconds(1);
+
+ while (to > 0) {
+ set_current_state(TASK_INTERRUPTIBLE);
+ schedule_timeout(to);
+ if (lock->l_granted_mode == lock->l_req_mode ||
+ lock->l_flags & LDLM_FL_DESTROYED)
+ break;
+ }
+ }
+
+ lvb_len = req_capsule_get_size(&req->rq_pill, &RMF_DLM_LVB, RCL_CLIENT);
+ if (lvb_len < 0) {
+ LDLM_ERROR(lock, "Fail to get lvb_len, rc = %d", lvb_len);
+ rc = lvb_len;
+ goto out;
+ } else if (lvb_len > 0) {
+ if (lock->l_lvb_len > 0) {
+ /* for extent lock, lvb contains ost_lvb{}. */
+ LASSERT(lock->l_lvb_data != NULL);
+
+ if (unlikely(lock->l_lvb_len < lvb_len)) {
+ LDLM_ERROR(lock, "Replied LVB is larger than expectation, expected = %d, replied = %d",
+ lock->l_lvb_len, lvb_len);
+ rc = -EINVAL;
+ goto out;
+ }
+ } else if (ldlm_has_layout(lock)) { /* for layout lock, lvb has
+ * variable length */
+ void *lvb_data;
+
+ OBD_ALLOC(lvb_data, lvb_len);
+ if (lvb_data == NULL) {
+ LDLM_ERROR(lock, "No memory: %d.\n", lvb_len);
+ rc = -ENOMEM;
+ goto out;
+ }
+
+ lock_res_and_lock(lock);
+ LASSERT(lock->l_lvb_data == NULL);
+ lock->l_lvb_type = LVB_T_LAYOUT;
+ lock->l_lvb_data = lvb_data;
+ lock->l_lvb_len = lvb_len;
+ unlock_res_and_lock(lock);
+ }
+ }
+
+ lock_res_and_lock(lock);
+ if ((lock->l_flags & LDLM_FL_DESTROYED) ||
+ lock->l_granted_mode == lock->l_req_mode) {
+ /* bug 11300: the lock has already been granted */
+ unlock_res_and_lock(lock);
+ LDLM_DEBUG(lock, "Double grant race happened");
+ rc = 0;
+ goto out;
+ }
+
+ /* If we receive the completion AST before the actual enqueue returned,
+ * then we might need to switch lock modes, resources, or extents. */
+ if (dlm_req->lock_desc.l_granted_mode != lock->l_req_mode) {
+ lock->l_req_mode = dlm_req->lock_desc.l_granted_mode;
+ LDLM_DEBUG(lock, "completion AST, new lock mode");
+ }
+
+ if (lock->l_resource->lr_type != LDLM_PLAIN) {
+ ldlm_convert_policy_to_local(req->rq_export,
+ dlm_req->lock_desc.l_resource.lr_type,
+ &dlm_req->lock_desc.l_policy_data,
+ &lock->l_policy_data);
+ LDLM_DEBUG(lock, "completion AST, new policy data");
+ }
+
+ ldlm_resource_unlink_lock(lock);
+ if (memcmp(&dlm_req->lock_desc.l_resource.lr_name,
+ &lock->l_resource->lr_name,
+ sizeof(lock->l_resource->lr_name)) != 0) {
+ unlock_res_and_lock(lock);
+ rc = ldlm_lock_change_resource(ns, lock,
+ &dlm_req->lock_desc.l_resource.lr_name);
+ if (rc < 0) {
+ LDLM_ERROR(lock, "Failed to allocate resource");
+ goto out;
+ }
+ LDLM_DEBUG(lock, "completion AST, new resource");
+ CERROR("change resource!\n");
+ lock_res_and_lock(lock);
+ }
+
+ if (dlm_req->lock_flags & LDLM_FL_AST_SENT) {
+ /* BL_AST locks are not needed in LRU.
+ * Let ldlm_cancel_lru() be fast. */
+ ldlm_lock_remove_from_lru(lock);
+ lock->l_flags |= LDLM_FL_CBPENDING | LDLM_FL_BL_AST;
+ LDLM_DEBUG(lock, "completion AST includes blocking AST");
+ }
+
+ if (lock->l_lvb_len > 0) {
+ rc = ldlm_fill_lvb(lock, &req->rq_pill, RCL_CLIENT,
+ lock->l_lvb_data, lvb_len);
+ if (rc < 0) {
+ unlock_res_and_lock(lock);
+ goto out;
+ }
+ }
+
+ ldlm_grant_lock(lock, &ast_list);
+ unlock_res_and_lock(lock);
+
+ LDLM_DEBUG(lock, "callback handler finished, about to run_ast_work");
+
+ /* Let Enqueue to call osc_lock_upcall() and initialize
+ * l_ast_data */
+ OBD_FAIL_TIMEOUT(OBD_FAIL_OSC_CP_ENQ_RACE, 2);
+
+ ldlm_run_ast_work(ns, &ast_list, LDLM_WORK_CP_AST);
+
+ LDLM_DEBUG_NOLOCK("client completion callback handler END (lock %p)",
+ lock);
+ goto out;
+
+out:
+ if (rc < 0) {
+ lock_res_and_lock(lock);
+ lock->l_flags |= LDLM_FL_FAILED;
+ unlock_res_and_lock(lock);
+ wake_up(&lock->l_waitq);
+ }
+ LDLM_LOCK_RELEASE(lock);
+}
+
+/**
+ * Callback handler for receiving incoming glimpse ASTs.
+ *
+ * This only can happen on client side. After handling the glimpse AST
+ * we also consider dropping the lock here if it is unused locally for a
+ * long time.
+ */
+static void ldlm_handle_gl_callback(struct ptlrpc_request *req,
+ struct ldlm_namespace *ns,
+ struct ldlm_request *dlm_req,
+ struct ldlm_lock *lock)
+{
+ int rc = -ENOSYS;
+
+ LDLM_DEBUG(lock, "client glimpse AST callback handler");
+
+ if (lock->l_glimpse_ast != NULL)
+ rc = lock->l_glimpse_ast(lock, req);
+
+ if (req->rq_repmsg != NULL) {
+ ptlrpc_reply(req);
+ } else {
+ req->rq_status = rc;
+ ptlrpc_error(req);
+ }
+
+ lock_res_and_lock(lock);
+ if (lock->l_granted_mode == LCK_PW &&
+ !lock->l_readers && !lock->l_writers &&
+ cfs_time_after(cfs_time_current(),
+ cfs_time_add(lock->l_last_used,
+ cfs_time_seconds(10)))) {
+ unlock_res_and_lock(lock);
+ if (ldlm_bl_to_thread_lock(ns, NULL, lock))
+ ldlm_handle_bl_callback(ns, NULL, lock);
+
+ return;
+ }
+ unlock_res_and_lock(lock);
+ LDLM_LOCK_RELEASE(lock);
+}
+
+static int ldlm_callback_reply(struct ptlrpc_request *req, int rc)
+{
+ if (req->rq_no_reply)
+ return 0;
+
+ req->rq_status = rc;
+ if (!req->rq_packed_final) {
+ rc = lustre_pack_reply(req, 1, NULL, NULL);
+ if (rc)
+ return rc;
+ }
+ return ptlrpc_reply(req);
+}
+
+static int __ldlm_bl_to_thread(struct ldlm_bl_work_item *blwi,
+ ldlm_cancel_flags_t cancel_flags)
+{
+ struct ldlm_bl_pool *blp = ldlm_state->ldlm_bl_pool;
+
+ spin_lock(&blp->blp_lock);
+ if (blwi->blwi_lock &&
+ blwi->blwi_lock->l_flags & LDLM_FL_DISCARD_DATA) {
+ /* add LDLM_FL_DISCARD_DATA requests to the priority list */
+ list_add_tail(&blwi->blwi_entry, &blp->blp_prio_list);
+ } else {
+ /* other blocking callbacks are added to the regular list */
+ list_add_tail(&blwi->blwi_entry, &blp->blp_list);
+ }
+ spin_unlock(&blp->blp_lock);
+
+ wake_up(&blp->blp_waitq);
+
+ /* can not check blwi->blwi_flags as blwi could be already freed in
+ LCF_ASYNC mode */
+ if (!(cancel_flags & LCF_ASYNC))
+ wait_for_completion(&blwi->blwi_comp);
+
+ return 0;
+}
+
+static inline void init_blwi(struct ldlm_bl_work_item *blwi,
+ struct ldlm_namespace *ns,
+ struct ldlm_lock_desc *ld,
+ struct list_head *cancels, int count,
+ struct ldlm_lock *lock,
+ ldlm_cancel_flags_t cancel_flags)
+{
+ init_completion(&blwi->blwi_comp);
+ INIT_LIST_HEAD(&blwi->blwi_head);
+
+ if (memory_pressure_get())
+ blwi->blwi_mem_pressure = 1;
+
+ blwi->blwi_ns = ns;
+ blwi->blwi_flags = cancel_flags;
+ if (ld != NULL)
+ blwi->blwi_ld = *ld;
+ if (count) {
+ list_add(&blwi->blwi_head, cancels);
+ list_del_init(cancels);
+ blwi->blwi_count = count;
+ } else {
+ blwi->blwi_lock = lock;
+ }
+}
+
+/**
+ * Queues a list of locks \a cancels containing \a count locks
+ * for later processing by a blocking thread. If \a count is zero,
+ * then the lock referenced as \a lock is queued instead.
+ *
+ * The blocking thread would then call ->l_blocking_ast callback in the lock.
+ * If list addition fails an error is returned and caller is supposed to
+ * call ->l_blocking_ast itself.
+ */
+static int ldlm_bl_to_thread(struct ldlm_namespace *ns,
+ struct ldlm_lock_desc *ld,
+ struct ldlm_lock *lock,
+ struct list_head *cancels, int count,
+ ldlm_cancel_flags_t cancel_flags)
+{
+ if (cancels && count == 0)
+ return 0;
+
+ if (cancel_flags & LCF_ASYNC) {
+ struct ldlm_bl_work_item *blwi;
+
+ OBD_ALLOC(blwi, sizeof(*blwi));
+ if (blwi == NULL)
+ return -ENOMEM;
+ init_blwi(blwi, ns, ld, cancels, count, lock, cancel_flags);
+
+ return __ldlm_bl_to_thread(blwi, cancel_flags);
+ } else {
+ /* if it is synchronous call do minimum mem alloc, as it could
+ * be triggered from kernel shrinker
+ */
+ struct ldlm_bl_work_item blwi;
+
+ memset(&blwi, 0, sizeof(blwi));
+ init_blwi(&blwi, ns, ld, cancels, count, lock, cancel_flags);
+ return __ldlm_bl_to_thread(&blwi, cancel_flags);
+ }
+}
+
+
+int ldlm_bl_to_thread_lock(struct ldlm_namespace *ns, struct ldlm_lock_desc *ld,
+ struct ldlm_lock *lock)
+{
+ return ldlm_bl_to_thread(ns, ld, lock, NULL, 0, LCF_ASYNC);
+}
+
+int ldlm_bl_to_thread_list(struct ldlm_namespace *ns, struct ldlm_lock_desc *ld,
+ struct list_head *cancels, int count,
+ ldlm_cancel_flags_t cancel_flags)
+{
+ return ldlm_bl_to_thread(ns, ld, NULL, cancels, count, cancel_flags);
+}
+
+/* Setinfo coming from Server (eg MDT) to Client (eg MDC)! */
+static int ldlm_handle_setinfo(struct ptlrpc_request *req)
+{
+ struct obd_device *obd = req->rq_export->exp_obd;
+ char *key;
+ void *val;
+ int keylen, vallen;
+ int rc = -ENOSYS;
+
+ DEBUG_REQ(D_HSM, req, "%s: handle setinfo\n", obd->obd_name);
+
+ req_capsule_set(&req->rq_pill, &RQF_OBD_SET_INFO);
+
+ key = req_capsule_client_get(&req->rq_pill, &RMF_SETINFO_KEY);
+ if (key == NULL) {
+ DEBUG_REQ(D_IOCTL, req, "no set_info key");
+ return -EFAULT;
+ }
+ keylen = req_capsule_get_size(&req->rq_pill, &RMF_SETINFO_KEY,
+ RCL_CLIENT);
+ val = req_capsule_client_get(&req->rq_pill, &RMF_SETINFO_VAL);
+ if (val == NULL) {
+ DEBUG_REQ(D_IOCTL, req, "no set_info val");
+ return -EFAULT;
+ }
+ vallen = req_capsule_get_size(&req->rq_pill, &RMF_SETINFO_VAL,
+ RCL_CLIENT);
+
+ /* We are responsible for swabbing contents of val */
+
+ if (KEY_IS(KEY_HSM_COPYTOOL_SEND))
+ /* Pass it on to mdc (the "export" in this case) */
+ rc = obd_set_info_async(req->rq_svc_thread->t_env,
+ req->rq_export,
+ sizeof(KEY_HSM_COPYTOOL_SEND),
+ KEY_HSM_COPYTOOL_SEND,
+ vallen, val, NULL);
+ else
+ DEBUG_REQ(D_WARNING, req, "ignoring unknown key %s", key);
+
+ return rc;
+}
+
+static inline void ldlm_callback_errmsg(struct ptlrpc_request *req,
+ const char *msg, int rc,
+ struct lustre_handle *handle)
+{
+ DEBUG_REQ((req->rq_no_reply || rc) ? D_WARNING : D_DLMTRACE, req,
+ "%s: [nid %s] [rc %d] [lock %#llx]",
+ msg, libcfs_id2str(req->rq_peer), rc,
+ handle ? handle->cookie : 0);
+ if (req->rq_no_reply)
+ CWARN("No reply was sent, maybe cause bug 21636.\n");
+ else if (rc)
+ CWARN("Send reply failed, maybe cause bug 21636.\n");
+}
+
+static int ldlm_handle_qc_callback(struct ptlrpc_request *req)
+{
+ struct obd_quotactl *oqctl;
+ struct client_obd *cli = &req->rq_export->exp_obd->u.cli;
+
+ oqctl = req_capsule_client_get(&req->rq_pill, &RMF_OBD_QUOTACTL);
+ if (oqctl == NULL) {
+ CERROR("Can't unpack obd_quotactl\n");
+ return -EPROTO;
+ }
+
+ oqctl->qc_stat = ptlrpc_status_ntoh(oqctl->qc_stat);
+
+ cli->cl_qchk_stat = oqctl->qc_stat;
+ return 0;
+}
+
+/* TODO: handle requests in a similar way as MDT: see mdt_handle_common() */
+static int ldlm_callback_handler(struct ptlrpc_request *req)
+{
+ struct ldlm_namespace *ns;
+ struct ldlm_request *dlm_req;
+ struct ldlm_lock *lock;
+ int rc;
+
+ /* Requests arrive in sender's byte order. The ptlrpc service
+ * handler has already checked and, if necessary, byte-swapped the
+ * incoming request message body, but I am responsible for the
+ * message buffers. */
+
+ /* do nothing for sec context finalize */
+ if (lustre_msg_get_opc(req->rq_reqmsg) == SEC_CTX_FINI)
+ return 0;
+
+ req_capsule_init(&req->rq_pill, req, RCL_SERVER);
+
+ if (req->rq_export == NULL) {
+ rc = ldlm_callback_reply(req, -ENOTCONN);
+ ldlm_callback_errmsg(req, "Operate on unconnected server",
+ rc, NULL);
+ return 0;
+ }
+
+ LASSERT(req->rq_export != NULL);
+ LASSERT(req->rq_export->exp_obd != NULL);
+
+ switch (lustre_msg_get_opc(req->rq_reqmsg)) {
+ case LDLM_BL_CALLBACK:
+ if (OBD_FAIL_CHECK(OBD_FAIL_LDLM_BL_CALLBACK_NET))
+ return 0;
+ break;
+ case LDLM_CP_CALLBACK:
+ if (OBD_FAIL_CHECK(OBD_FAIL_LDLM_CP_CALLBACK_NET))
+ return 0;
+ break;
+ case LDLM_GL_CALLBACK:
+ if (OBD_FAIL_CHECK(OBD_FAIL_LDLM_GL_CALLBACK_NET))
+ return 0;
+ break;
+ case LDLM_SET_INFO:
+ rc = ldlm_handle_setinfo(req);
+ ldlm_callback_reply(req, rc);
+ return 0;
+ case OBD_QC_CALLBACK:
+ req_capsule_set(&req->rq_pill, &RQF_QC_CALLBACK);
+ if (OBD_FAIL_CHECK(OBD_FAIL_OBD_QC_CALLBACK_NET))
+ return 0;
+ rc = ldlm_handle_qc_callback(req);
+ ldlm_callback_reply(req, rc);
+ return 0;
+ default:
+ CERROR("unknown opcode %u\n",
+ lustre_msg_get_opc(req->rq_reqmsg));
+ ldlm_callback_reply(req, -EPROTO);
+ return 0;
+ }
+
+ ns = req->rq_export->exp_obd->obd_namespace;
+ LASSERT(ns != NULL);
+
+ req_capsule_set(&req->rq_pill, &RQF_LDLM_CALLBACK);
+
+ dlm_req = req_capsule_client_get(&req->rq_pill, &RMF_DLM_REQ);
+ if (dlm_req == NULL) {
+ rc = ldlm_callback_reply(req, -EPROTO);
+ ldlm_callback_errmsg(req, "Operate without parameter", rc,
+ NULL);
+ return 0;
+ }
+
+ /* Force a known safe race, send a cancel to the server for a lock
+ * which the server has already started a blocking callback on. */
+ if (OBD_FAIL_CHECK(OBD_FAIL_LDLM_CANCEL_BL_CB_RACE) &&
+ lustre_msg_get_opc(req->rq_reqmsg) == LDLM_BL_CALLBACK) {
+ rc = ldlm_cli_cancel(&dlm_req->lock_handle[0], 0);
+ if (rc < 0)
+ CERROR("ldlm_cli_cancel: %d\n", rc);
+ }
+
+ lock = ldlm_handle2lock_long(&dlm_req->lock_handle[0], 0);
+ if (!lock) {
+ CDEBUG(D_DLMTRACE, "callback on lock %#llx - lock disappeared\n",
+ dlm_req->lock_handle[0].cookie);
+ rc = ldlm_callback_reply(req, -EINVAL);
+ ldlm_callback_errmsg(req, "Operate with invalid parameter", rc,
+ &dlm_req->lock_handle[0]);
+ return 0;
+ }
+
+ if ((lock->l_flags & LDLM_FL_FAIL_LOC) &&
+ lustre_msg_get_opc(req->rq_reqmsg) == LDLM_BL_CALLBACK)
+ OBD_RACE(OBD_FAIL_LDLM_CP_BL_RACE);
+
+ /* Copy hints/flags (e.g. LDLM_FL_DISCARD_DATA) from AST. */
+ lock_res_and_lock(lock);
+ lock->l_flags |= ldlm_flags_from_wire(dlm_req->lock_flags &
+ LDLM_AST_FLAGS);
+ if (lustre_msg_get_opc(req->rq_reqmsg) == LDLM_BL_CALLBACK) {
+ /* If somebody cancels lock and cache is already dropped,
+ * or lock is failed before cp_ast received on client,
+ * we can tell the server we have no lock. Otherwise, we
+ * should send cancel after dropping the cache. */
+ if (((lock->l_flags & LDLM_FL_CANCELING) &&
+ (lock->l_flags & LDLM_FL_BL_DONE)) ||
+ (lock->l_flags & LDLM_FL_FAILED)) {
+ LDLM_DEBUG(lock, "callback on lock %#llx - lock disappeared\n",
+ dlm_req->lock_handle[0].cookie);
+ unlock_res_and_lock(lock);
+ LDLM_LOCK_RELEASE(lock);
+ rc = ldlm_callback_reply(req, -EINVAL);
+ ldlm_callback_errmsg(req, "Operate on stale lock", rc,
+ &dlm_req->lock_handle[0]);
+ return 0;
+ }
+ /* BL_AST locks are not needed in LRU.
+ * Let ldlm_cancel_lru() be fast. */
+ ldlm_lock_remove_from_lru(lock);
+ lock->l_flags |= LDLM_FL_BL_AST;
+ }
+ unlock_res_and_lock(lock);
+
+ /* We want the ost thread to get this reply so that it can respond
+ * to ost requests (write cache writeback) that might be triggered
+ * in the callback.
+ *
+ * But we'd also like to be able to indicate in the reply that we're
+ * cancelling right now, because it's unused, or have an intent result
+ * in the reply, so we might have to push the responsibility for sending
+ * the reply down into the AST handlers, alas. */
+
+ switch (lustre_msg_get_opc(req->rq_reqmsg)) {
+ case LDLM_BL_CALLBACK:
+ CDEBUG(D_INODE, "blocking ast\n");
+ req_capsule_extend(&req->rq_pill, &RQF_LDLM_BL_CALLBACK);
+ if (!(lock->l_flags & LDLM_FL_CANCEL_ON_BLOCK)) {
+ rc = ldlm_callback_reply(req, 0);
+ if (req->rq_no_reply || rc)
+ ldlm_callback_errmsg(req, "Normal process", rc,
+ &dlm_req->lock_handle[0]);
+ }
+ if (ldlm_bl_to_thread_lock(ns, &dlm_req->lock_desc, lock))
+ ldlm_handle_bl_callback(ns, &dlm_req->lock_desc, lock);
+ break;
+ case LDLM_CP_CALLBACK:
+ CDEBUG(D_INODE, "completion ast\n");
+ req_capsule_extend(&req->rq_pill, &RQF_LDLM_CP_CALLBACK);
+ ldlm_callback_reply(req, 0);
+ ldlm_handle_cp_callback(req, ns, dlm_req, lock);
+ break;
+ case LDLM_GL_CALLBACK:
+ CDEBUG(D_INODE, "glimpse ast\n");
+ req_capsule_extend(&req->rq_pill, &RQF_LDLM_GL_CALLBACK);
+ ldlm_handle_gl_callback(req, ns, dlm_req, lock);
+ break;
+ default:
+ LBUG(); /* checked above */
+ }
+
+ return 0;
+}
+
+
+static struct ldlm_bl_work_item *ldlm_bl_get_work(struct ldlm_bl_pool *blp)
+{
+ struct ldlm_bl_work_item *blwi = NULL;
+ static unsigned int num_bl;
+
+ spin_lock(&blp->blp_lock);
+ /* process a request from the blp_list at least every blp_num_threads */
+ if (!list_empty(&blp->blp_list) &&
+ (list_empty(&blp->blp_prio_list) || num_bl == 0))
+ blwi = list_entry(blp->blp_list.next,
+ struct ldlm_bl_work_item, blwi_entry);
+ else
+ if (!list_empty(&blp->blp_prio_list))
+ blwi = list_entry(blp->blp_prio_list.next,
+ struct ldlm_bl_work_item,
+ blwi_entry);
+
+ if (blwi) {
+ if (++num_bl >= atomic_read(&blp->blp_num_threads))
+ num_bl = 0;
+ list_del(&blwi->blwi_entry);
+ }
+ spin_unlock(&blp->blp_lock);
+
+ return blwi;
+}
+
+/* This only contains temporary data until the thread starts */
+struct ldlm_bl_thread_data {
+ char bltd_name[CFS_CURPROC_COMM_MAX];
+ struct ldlm_bl_pool *bltd_blp;
+ struct completion bltd_comp;
+ int bltd_num;
+};
+
+static int ldlm_bl_thread_main(void *arg);
+
+static int ldlm_bl_thread_start(struct ldlm_bl_pool *blp)
+{
+ struct ldlm_bl_thread_data bltd = { .bltd_blp = blp };
+ struct task_struct *task;
+
+ init_completion(&bltd.bltd_comp);
+ bltd.bltd_num = atomic_read(&blp->blp_num_threads);
+ snprintf(bltd.bltd_name, sizeof(bltd.bltd_name),
+ "ldlm_bl_%02d", bltd.bltd_num);
+ task = kthread_run(ldlm_bl_thread_main, &bltd, "%s", bltd.bltd_name);
+ if (IS_ERR(task)) {
+ CERROR("cannot start LDLM thread ldlm_bl_%02d: rc %ld\n",
+ atomic_read(&blp->blp_num_threads), PTR_ERR(task));
+ return PTR_ERR(task);
+ }
+ wait_for_completion(&bltd.bltd_comp);
+
+ return 0;
+}
+
+/**
+ * Main blocking requests processing thread.
+ *
+ * Callers put locks into its queue by calling ldlm_bl_to_thread.
+ * This thread in the end ends up doing actual call to ->l_blocking_ast
+ * for queued locks.
+ */
+static int ldlm_bl_thread_main(void *arg)
+{
+ struct ldlm_bl_pool *blp;
+
+ {
+ struct ldlm_bl_thread_data *bltd = arg;
+
+ blp = bltd->bltd_blp;
+
+ atomic_inc(&blp->blp_num_threads);
+ atomic_inc(&blp->blp_busy_threads);
+
+ complete(&bltd->bltd_comp);
+ /* cannot use bltd after this, it is only on caller's stack */
+ }
+
+ while (1) {
+ struct l_wait_info lwi = { 0 };
+ struct ldlm_bl_work_item *blwi = NULL;
+ int busy;
+
+ blwi = ldlm_bl_get_work(blp);
+
+ if (blwi == NULL) {
+ atomic_dec(&blp->blp_busy_threads);
+ l_wait_event_exclusive(blp->blp_waitq,
+ (blwi = ldlm_bl_get_work(blp)) != NULL,
+ &lwi);
+ busy = atomic_inc_return(&blp->blp_busy_threads);
+ } else {
+ busy = atomic_read(&blp->blp_busy_threads);
+ }
+
+ if (blwi->blwi_ns == NULL)
+ /* added by ldlm_cleanup() */
+ break;
+
+ /* Not fatal if racy and have a few too many threads */
+ if (unlikely(busy < blp->blp_max_threads &&
+ busy >= atomic_read(&blp->blp_num_threads) &&
+ !blwi->blwi_mem_pressure))
+ /* discard the return value, we tried */
+ ldlm_bl_thread_start(blp);
+
+ if (blwi->blwi_mem_pressure)
+ memory_pressure_set();
+
+ if (blwi->blwi_count) {
+ int count;
+ /* The special case when we cancel locks in LRU
+ * asynchronously, we pass the list of locks here.
+ * Thus locks are marked LDLM_FL_CANCELING, but NOT
+ * canceled locally yet. */
+ count = ldlm_cli_cancel_list_local(&blwi->blwi_head,
+ blwi->blwi_count,
+ LCF_BL_AST);
+ ldlm_cli_cancel_list(&blwi->blwi_head, count, NULL,
+ blwi->blwi_flags);
+ } else {
+ ldlm_handle_bl_callback(blwi->blwi_ns, &blwi->blwi_ld,
+ blwi->blwi_lock);
+ }
+ if (blwi->blwi_mem_pressure)
+ memory_pressure_clr();
+
+ if (blwi->blwi_flags & LCF_ASYNC)
+ OBD_FREE(blwi, sizeof(*blwi));
+ else
+ complete(&blwi->blwi_comp);
+ }
+
+ atomic_dec(&blp->blp_busy_threads);
+ atomic_dec(&blp->blp_num_threads);
+ complete(&blp->blp_comp);
+ return 0;
+}
+
+
+static int ldlm_setup(void);
+static int ldlm_cleanup(void);
+
+int ldlm_get_ref(void)
+{
+ int rc = 0;
+
+ mutex_lock(&ldlm_ref_mutex);
+ if (++ldlm_refcount == 1) {
+ rc = ldlm_setup();
+ if (rc)
+ ldlm_refcount--;
+ }
+ mutex_unlock(&ldlm_ref_mutex);
+
+ return rc;
+}
+EXPORT_SYMBOL(ldlm_get_ref);
+
+void ldlm_put_ref(void)
+{
+ mutex_lock(&ldlm_ref_mutex);
+ if (ldlm_refcount == 1) {
+ int rc = ldlm_cleanup();
+
+ if (rc)
+ CERROR("ldlm_cleanup failed: %d\n", rc);
+ else
+ ldlm_refcount--;
+ } else {
+ ldlm_refcount--;
+ }
+ mutex_unlock(&ldlm_ref_mutex);
+}
+EXPORT_SYMBOL(ldlm_put_ref);
+
+/*
+ * Export handle<->lock hash operations.
+ */
+static unsigned
+ldlm_export_lock_hash(struct cfs_hash *hs, const void *key, unsigned mask)
+{
+ return cfs_hash_u64_hash(((struct lustre_handle *)key)->cookie, mask);
+}
+
+static void *
+ldlm_export_lock_key(struct hlist_node *hnode)
+{
+ struct ldlm_lock *lock;
+
+ lock = hlist_entry(hnode, struct ldlm_lock, l_exp_hash);
+ return &lock->l_remote_handle;
+}
+
+static void
+ldlm_export_lock_keycpy(struct hlist_node *hnode, void *key)
+{
+ struct ldlm_lock *lock;
+
+ lock = hlist_entry(hnode, struct ldlm_lock, l_exp_hash);
+ lock->l_remote_handle = *(struct lustre_handle *)key;
+}
+
+static int
+ldlm_export_lock_keycmp(const void *key, struct hlist_node *hnode)
+{
+ return lustre_handle_equal(ldlm_export_lock_key(hnode), key);
+}
+
+static void *
+ldlm_export_lock_object(struct hlist_node *hnode)
+{
+ return hlist_entry(hnode, struct ldlm_lock, l_exp_hash);
+}
+
+static void
+ldlm_export_lock_get(struct cfs_hash *hs, struct hlist_node *hnode)
+{
+ struct ldlm_lock *lock;
+
+ lock = hlist_entry(hnode, struct ldlm_lock, l_exp_hash);
+ LDLM_LOCK_GET(lock);
+}
+
+static void
+ldlm_export_lock_put(struct cfs_hash *hs, struct hlist_node *hnode)
+{
+ struct ldlm_lock *lock;
+
+ lock = hlist_entry(hnode, struct ldlm_lock, l_exp_hash);
+ LDLM_LOCK_RELEASE(lock);
+}
+
+static cfs_hash_ops_t ldlm_export_lock_ops = {
+ .hs_hash = ldlm_export_lock_hash,
+ .hs_key = ldlm_export_lock_key,
+ .hs_keycmp = ldlm_export_lock_keycmp,
+ .hs_keycpy = ldlm_export_lock_keycpy,
+ .hs_object = ldlm_export_lock_object,
+ .hs_get = ldlm_export_lock_get,
+ .hs_put = ldlm_export_lock_put,
+ .hs_put_locked = ldlm_export_lock_put,
+};
+
+int ldlm_init_export(struct obd_export *exp)
+{
+ int rc;
+
+ exp->exp_lock_hash =
+ cfs_hash_create(obd_uuid2str(&exp->exp_client_uuid),
+ HASH_EXP_LOCK_CUR_BITS,
+ HASH_EXP_LOCK_MAX_BITS,
+ HASH_EXP_LOCK_BKT_BITS, 0,
+ CFS_HASH_MIN_THETA, CFS_HASH_MAX_THETA,
+ &ldlm_export_lock_ops,
+ CFS_HASH_DEFAULT | CFS_HASH_REHASH_KEY |
+ CFS_HASH_NBLK_CHANGE);
+
+ if (!exp->exp_lock_hash)
+ return -ENOMEM;
+
+ rc = ldlm_init_flock_export(exp);
+ if (rc)
+ goto err;
+
+ return 0;
+err:
+ ldlm_destroy_export(exp);
+ return rc;
+}
+EXPORT_SYMBOL(ldlm_init_export);
+
+void ldlm_destroy_export(struct obd_export *exp)
+{
+ cfs_hash_putref(exp->exp_lock_hash);
+ exp->exp_lock_hash = NULL;
+
+ ldlm_destroy_flock_export(exp);
+}
+EXPORT_SYMBOL(ldlm_destroy_export);
+
+static int ldlm_setup(void)
+{
+ static struct ptlrpc_service_conf conf;
+ struct ldlm_bl_pool *blp = NULL;
+ int rc = 0;
+ int i;
+
+ if (ldlm_state != NULL)
+ return -EALREADY;
+
+ OBD_ALLOC(ldlm_state, sizeof(*ldlm_state));
+ if (ldlm_state == NULL)
+ return -ENOMEM;
+
+ rc = ldlm_proc_setup();
+ if (rc != 0)
+ goto out;
+
+ memset(&conf, 0, sizeof(conf));
+ conf = (typeof(conf)) {
+ .psc_name = "ldlm_cbd",
+ .psc_watchdog_factor = 2,
+ .psc_buf = {
+ .bc_nbufs = LDLM_CLIENT_NBUFS,
+ .bc_buf_size = LDLM_BUFSIZE,
+ .bc_req_max_size = LDLM_MAXREQSIZE,
+ .bc_rep_max_size = LDLM_MAXREPSIZE,
+ .bc_req_portal = LDLM_CB_REQUEST_PORTAL,
+ .bc_rep_portal = LDLM_CB_REPLY_PORTAL,
+ },
+ .psc_thr = {
+ .tc_thr_name = "ldlm_cb",
+ .tc_thr_factor = LDLM_THR_FACTOR,
+ .tc_nthrs_init = LDLM_NTHRS_INIT,
+ .tc_nthrs_base = LDLM_NTHRS_BASE,
+ .tc_nthrs_max = LDLM_NTHRS_MAX,
+ .tc_nthrs_user = ldlm_num_threads,
+ .tc_cpu_affinity = 1,
+ .tc_ctx_tags = LCT_MD_THREAD | LCT_DT_THREAD,
+ },
+ .psc_cpt = {
+ .cc_pattern = ldlm_cpts,
+ },
+ .psc_ops = {
+ .so_req_handler = ldlm_callback_handler,
+ },
+ };
+ ldlm_state->ldlm_cb_service =
+ ptlrpc_register_service(&conf, ldlm_svc_proc_dir);
+ if (IS_ERR(ldlm_state->ldlm_cb_service)) {
+ CERROR("failed to start service\n");
+ rc = PTR_ERR(ldlm_state->ldlm_cb_service);
+ ldlm_state->ldlm_cb_service = NULL;
+ goto out;
+ }
+
+
+ OBD_ALLOC(blp, sizeof(*blp));
+ if (blp == NULL) {
+ rc = -ENOMEM;
+ goto out;
+ }
+ ldlm_state->ldlm_bl_pool = blp;
+
+ spin_lock_init(&blp->blp_lock);
+ INIT_LIST_HEAD(&blp->blp_list);
+ INIT_LIST_HEAD(&blp->blp_prio_list);
+ init_waitqueue_head(&blp->blp_waitq);
+ atomic_set(&blp->blp_num_threads, 0);
+ atomic_set(&blp->blp_busy_threads, 0);
+
+ if (ldlm_num_threads == 0) {
+ blp->blp_min_threads = LDLM_NTHRS_INIT;
+ blp->blp_max_threads = LDLM_NTHRS_MAX;
+ } else {
+ blp->blp_min_threads = blp->blp_max_threads =
+ min_t(int, LDLM_NTHRS_MAX, max_t(int, LDLM_NTHRS_INIT,
+ ldlm_num_threads));
+ }
+
+ for (i = 0; i < blp->blp_min_threads; i++) {
+ rc = ldlm_bl_thread_start(blp);
+ if (rc < 0)
+ goto out;
+ }
+
+
+ rc = ldlm_pools_init();
+ if (rc) {
+ CERROR("Failed to initialize LDLM pools: %d\n", rc);
+ goto out;
+ }
+ return 0;
+
+ out:
+ ldlm_cleanup();
+ return rc;
+}
+
+static int ldlm_cleanup(void)
+{
+ if (!list_empty(ldlm_namespace_list(LDLM_NAMESPACE_SERVER)) ||
+ !list_empty(ldlm_namespace_list(LDLM_NAMESPACE_CLIENT))) {
+ CERROR("ldlm still has namespaces; clean these up first.\n");
+ ldlm_dump_all_namespaces(LDLM_NAMESPACE_SERVER, D_DLMTRACE);
+ ldlm_dump_all_namespaces(LDLM_NAMESPACE_CLIENT, D_DLMTRACE);
+ return -EBUSY;
+ }
+
+ ldlm_pools_fini();
+
+ if (ldlm_state->ldlm_bl_pool != NULL) {
+ struct ldlm_bl_pool *blp = ldlm_state->ldlm_bl_pool;
+
+ while (atomic_read(&blp->blp_num_threads) > 0) {
+ struct ldlm_bl_work_item blwi = { .blwi_ns = NULL };
+
+ init_completion(&blp->blp_comp);
+
+ spin_lock(&blp->blp_lock);
+ list_add_tail(&blwi.blwi_entry, &blp->blp_list);
+ wake_up(&blp->blp_waitq);
+ spin_unlock(&blp->blp_lock);
+
+ wait_for_completion(&blp->blp_comp);
+ }
+
+ OBD_FREE(blp, sizeof(*blp));
+ }
+
+ if (ldlm_state->ldlm_cb_service != NULL)
+ ptlrpc_unregister_service(ldlm_state->ldlm_cb_service);
+
+ ldlm_proc_cleanup();
+
+
+ OBD_FREE(ldlm_state, sizeof(*ldlm_state));
+ ldlm_state = NULL;
+
+ return 0;
+}
+
+int ldlm_init(void)
+{
+ mutex_init(&ldlm_ref_mutex);
+ mutex_init(ldlm_namespace_lock(LDLM_NAMESPACE_SERVER));
+ mutex_init(ldlm_namespace_lock(LDLM_NAMESPACE_CLIENT));
+ ldlm_resource_slab = kmem_cache_create("ldlm_resources",
+ sizeof(struct ldlm_resource), 0,
+ SLAB_HWCACHE_ALIGN, NULL);
+ if (ldlm_resource_slab == NULL)
+ return -ENOMEM;
+
+ ldlm_lock_slab = kmem_cache_create("ldlm_locks",
+ sizeof(struct ldlm_lock), 0,
+ SLAB_HWCACHE_ALIGN | SLAB_DESTROY_BY_RCU, NULL);
+ if (ldlm_lock_slab == NULL) {
+ kmem_cache_destroy(ldlm_resource_slab);
+ return -ENOMEM;
+ }
+
+ ldlm_interval_slab = kmem_cache_create("interval_node",
+ sizeof(struct ldlm_interval),
+ 0, SLAB_HWCACHE_ALIGN, NULL);
+ if (ldlm_interval_slab == NULL) {
+ kmem_cache_destroy(ldlm_resource_slab);
+ kmem_cache_destroy(ldlm_lock_slab);
+ return -ENOMEM;
+ }
+#if LUSTRE_TRACKS_LOCK_EXP_REFS
+ class_export_dump_hook = ldlm_dump_export_locks;
+#endif
+ return 0;
+}
+
+void ldlm_exit(void)
+{
+ if (ldlm_refcount)
+ CERROR("ldlm_refcount is %d in ldlm_exit!\n", ldlm_refcount);
+ kmem_cache_destroy(ldlm_resource_slab);
+ /* ldlm_lock_put() use RCU to call ldlm_lock_free, so need call
+ * synchronize_rcu() to wait a grace period elapsed, so that
+ * ldlm_lock_free() get a chance to be called. */
+ synchronize_rcu();
+ kmem_cache_destroy(ldlm_lock_slab);
+ kmem_cache_destroy(ldlm_interval_slab);
+}
diff --git a/drivers/staging/lustre/lustre/ldlm/ldlm_plain.c b/drivers/staging/lustre/lustre/ldlm/ldlm_plain.c
new file mode 100644
index 000000000..a1fe2c161
--- /dev/null
+++ b/drivers/staging/lustre/lustre/ldlm/ldlm_plain.c
@@ -0,0 +1,72 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2011, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ *
+ * lustre/ldlm/ldlm_plain.c
+ *
+ * Author: Peter Braam <braam@clusterfs.com>
+ * Author: Phil Schwan <phil@clusterfs.com>
+ */
+
+/**
+ * This file contains implementation of PLAIN lock type.
+ *
+ * PLAIN locks are the simplest form of LDLM locking, and are used when
+ * there only needs to be a single lock on a resource. This avoids some
+ * of the complexity of EXTENT and IBITS lock types, but doesn't allow
+ * different "parts" of a resource to be locked concurrently. Example
+ * use cases for PLAIN locks include locking of MGS configuration logs
+ * and (as of Lustre 2.4) quota records.
+ */
+
+#define DEBUG_SUBSYSTEM S_LDLM
+
+#include "../include/lustre_dlm.h"
+#include "../include/obd_support.h"
+#include "../include/lustre_lib.h"
+
+#include "ldlm_internal.h"
+
+
+void ldlm_plain_policy_wire_to_local(const ldlm_wire_policy_data_t *wpolicy,
+ ldlm_policy_data_t *lpolicy)
+{
+ /* No policy for plain locks */
+}
+
+void ldlm_plain_policy_local_to_wire(const ldlm_policy_data_t *lpolicy,
+ ldlm_wire_policy_data_t *wpolicy)
+{
+ /* No policy for plain locks */
+}
diff --git a/drivers/staging/lustre/lustre/ldlm/ldlm_pool.c b/drivers/staging/lustre/lustre/ldlm/ldlm_pool.c
new file mode 100644
index 000000000..a9f4833e0
--- /dev/null
+++ b/drivers/staging/lustre/lustre/ldlm/ldlm_pool.c
@@ -0,0 +1,1455 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2010, 2012, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ *
+ * lustre/ldlm/ldlm_pool.c
+ *
+ * Author: Yury Umanets <umka@clusterfs.com>
+ */
+
+/*
+ * Idea of this code is rather simple. Each second, for each server namespace
+ * we have SLV - server lock volume which is calculated on current number of
+ * granted locks, grant speed for past period, etc - that is, locking load.
+ * This SLV number may be thought as a flow definition for simplicity. It is
+ * sent to clients with each occasion to let them know what is current load
+ * situation on the server. By default, at the beginning, SLV on server is
+ * set max value which is calculated as the following: allow to one client
+ * have all locks of limit ->pl_limit for 10h.
+ *
+ * Next, on clients, number of cached locks is not limited artificially in any
+ * way as it was before. Instead, client calculates CLV, that is, client lock
+ * volume for each lock and compares it with last SLV from the server. CLV is
+ * calculated as the number of locks in LRU * lock live time in seconds. If
+ * CLV > SLV - lock is canceled.
+ *
+ * Client has LVF, that is, lock volume factor which regulates how much
+ * sensitive client should be about last SLV from server. The higher LVF is the
+ * more locks will be canceled on client. Default value for it is 1. Setting LVF
+ * to 2 means that client will cancel locks 2 times faster.
+ *
+ * Locks on a client will be canceled more intensively in these cases:
+ * (1) if SLV is smaller, that is, load is higher on the server;
+ * (2) client has a lot of locks (the more locks are held by client, the bigger
+ * chances that some of them should be canceled);
+ * (3) client has old locks (taken some time ago);
+ *
+ * Thus, according to flow paradigm that we use for better understanding SLV,
+ * CLV is the volume of particle in flow described by SLV. According to this,
+ * if flow is getting thinner, more and more particles become outside of it and
+ * as particles are locks, they should be canceled.
+ *
+ * General idea of this belongs to Vitaly Fertman (vitaly@clusterfs.com).
+ * Andreas Dilger (adilger@clusterfs.com) proposed few nice ideas like using
+ * LVF and many cleanups. Flow definition to allow more easy understanding of
+ * the logic belongs to Nikita Danilov (nikita@clusterfs.com) as well as many
+ * cleanups and fixes. And design and implementation are done by Yury Umanets
+ * (umka@clusterfs.com).
+ *
+ * Glossary for terms used:
+ *
+ * pl_limit - Number of allowed locks in pool. Applies to server and client
+ * side (tunable);
+ *
+ * pl_granted - Number of granted locks (calculated);
+ * pl_grant_rate - Number of granted locks for last T (calculated);
+ * pl_cancel_rate - Number of canceled locks for last T (calculated);
+ * pl_grant_speed - Grant speed (GR - CR) for last T (calculated);
+ * pl_grant_plan - Planned number of granted locks for next T (calculated);
+ * pl_server_lock_volume - Current server lock volume (calculated);
+ *
+ * As it may be seen from list above, we have few possible tunables which may
+ * affect behavior much. They all may be modified via proc. However, they also
+ * give a possibility for constructing few pre-defined behavior policies. If
+ * none of predefines is suitable for a working pattern being used, new one may
+ * be "constructed" via proc tunables.
+ */
+
+#define DEBUG_SUBSYSTEM S_LDLM
+
+#include "../include/lustre_dlm.h"
+#include "../include/cl_object.h"
+#include "../include/obd_class.h"
+#include "../include/obd_support.h"
+#include "ldlm_internal.h"
+
+
+/*
+ * 50 ldlm locks for 1MB of RAM.
+ */
+#define LDLM_POOL_HOST_L ((NUM_CACHEPAGES >> (20 - PAGE_CACHE_SHIFT)) * 50)
+
+/*
+ * Maximal possible grant step plan in %.
+ */
+#define LDLM_POOL_MAX_GSP (30)
+
+/*
+ * Minimal possible grant step plan in %.
+ */
+#define LDLM_POOL_MIN_GSP (1)
+
+/*
+ * This controls the speed of reaching LDLM_POOL_MAX_GSP
+ * with increasing thread period.
+ */
+#define LDLM_POOL_GSP_STEP_SHIFT (2)
+
+/*
+ * LDLM_POOL_GSP% of all locks is default GP.
+ */
+#define LDLM_POOL_GP(L) (((L) * LDLM_POOL_MAX_GSP) / 100)
+
+/*
+ * Max age for locks on clients.
+ */
+#define LDLM_POOL_MAX_AGE (36000)
+
+/*
+ * The granularity of SLV calculation.
+ */
+#define LDLM_POOL_SLV_SHIFT (10)
+
+static inline __u64 dru(__u64 val, __u32 shift, int round_up)
+{
+ return (val + (round_up ? (1 << shift) - 1 : 0)) >> shift;
+}
+
+static inline __u64 ldlm_pool_slv_max(__u32 L)
+{
+ /*
+ * Allow to have all locks for 1 client for 10 hrs.
+ * Formula is the following: limit * 10h / 1 client.
+ */
+ __u64 lim = (__u64)L * LDLM_POOL_MAX_AGE / 1;
+ return lim;
+}
+
+static inline __u64 ldlm_pool_slv_min(__u32 L)
+{
+ return 1;
+}
+
+enum {
+ LDLM_POOL_FIRST_STAT = 0,
+ LDLM_POOL_GRANTED_STAT = LDLM_POOL_FIRST_STAT,
+ LDLM_POOL_GRANT_STAT,
+ LDLM_POOL_CANCEL_STAT,
+ LDLM_POOL_GRANT_RATE_STAT,
+ LDLM_POOL_CANCEL_RATE_STAT,
+ LDLM_POOL_GRANT_PLAN_STAT,
+ LDLM_POOL_SLV_STAT,
+ LDLM_POOL_SHRINK_REQTD_STAT,
+ LDLM_POOL_SHRINK_FREED_STAT,
+ LDLM_POOL_RECALC_STAT,
+ LDLM_POOL_TIMING_STAT,
+ LDLM_POOL_LAST_STAT
+};
+
+static inline struct ldlm_namespace *ldlm_pl2ns(struct ldlm_pool *pl)
+{
+ return container_of(pl, struct ldlm_namespace, ns_pool);
+}
+
+/**
+ * Calculates suggested grant_step in % of available locks for passed
+ * \a period. This is later used in grant_plan calculations.
+ */
+static inline int ldlm_pool_t2gsp(unsigned int t)
+{
+ /*
+ * This yields 1% grant step for anything below LDLM_POOL_GSP_STEP
+ * and up to 30% for anything higher than LDLM_POOL_GSP_STEP.
+ *
+ * How this will affect execution is the following:
+ *
+ * - for thread period 1s we will have grant_step 1% which good from
+ * pov of taking some load off from server and push it out to clients.
+ * This is like that because 1% for grant_step means that server will
+ * not allow clients to get lots of locks in short period of time and
+ * keep all old locks in their caches. Clients will always have to
+ * get some locks back if they want to take some new;
+ *
+ * - for thread period 10s (which is default) we will have 23% which
+ * means that clients will have enough of room to take some new locks
+ * without getting some back. All locks from this 23% which were not
+ * taken by clients in current period will contribute in SLV growing.
+ * SLV growing means more locks cached on clients until limit or grant
+ * plan is reached.
+ */
+ return LDLM_POOL_MAX_GSP -
+ ((LDLM_POOL_MAX_GSP - LDLM_POOL_MIN_GSP) >>
+ (t >> LDLM_POOL_GSP_STEP_SHIFT));
+}
+
+/**
+ * Recalculates next grant limit on passed \a pl.
+ *
+ * \pre ->pl_lock is locked.
+ */
+static void ldlm_pool_recalc_grant_plan(struct ldlm_pool *pl)
+{
+ int granted, grant_step, limit;
+
+ limit = ldlm_pool_get_limit(pl);
+ granted = atomic_read(&pl->pl_granted);
+
+ grant_step = ldlm_pool_t2gsp(pl->pl_recalc_period);
+ grant_step = ((limit - granted) * grant_step) / 100;
+ pl->pl_grant_plan = granted + grant_step;
+ limit = (limit * 5) >> 2;
+ if (pl->pl_grant_plan > limit)
+ pl->pl_grant_plan = limit;
+}
+
+/**
+ * Recalculates next SLV on passed \a pl.
+ *
+ * \pre ->pl_lock is locked.
+ */
+static void ldlm_pool_recalc_slv(struct ldlm_pool *pl)
+{
+ int granted;
+ int grant_plan;
+ int round_up;
+ __u64 slv;
+ __u64 slv_factor;
+ __u64 grant_usage;
+ __u32 limit;
+
+ slv = pl->pl_server_lock_volume;
+ grant_plan = pl->pl_grant_plan;
+ limit = ldlm_pool_get_limit(pl);
+ granted = atomic_read(&pl->pl_granted);
+ round_up = granted < limit;
+
+ grant_usage = max_t(int, limit - (granted - grant_plan), 1);
+
+ /*
+ * Find out SLV change factor which is the ratio of grant usage
+ * from limit. SLV changes as fast as the ratio of grant plan
+ * consumption. The more locks from grant plan are not consumed
+ * by clients in last interval (idle time), the faster grows
+ * SLV. And the opposite, the more grant plan is over-consumed
+ * (load time) the faster drops SLV.
+ */
+ slv_factor = grant_usage << LDLM_POOL_SLV_SHIFT;
+ do_div(slv_factor, limit);
+ slv = slv * slv_factor;
+ slv = dru(slv, LDLM_POOL_SLV_SHIFT, round_up);
+
+ if (slv > ldlm_pool_slv_max(limit))
+ slv = ldlm_pool_slv_max(limit);
+ else if (slv < ldlm_pool_slv_min(limit))
+ slv = ldlm_pool_slv_min(limit);
+
+ pl->pl_server_lock_volume = slv;
+}
+
+/**
+ * Recalculates next stats on passed \a pl.
+ *
+ * \pre ->pl_lock is locked.
+ */
+static void ldlm_pool_recalc_stats(struct ldlm_pool *pl)
+{
+ int grant_plan = pl->pl_grant_plan;
+ __u64 slv = pl->pl_server_lock_volume;
+ int granted = atomic_read(&pl->pl_granted);
+ int grant_rate = atomic_read(&pl->pl_grant_rate);
+ int cancel_rate = atomic_read(&pl->pl_cancel_rate);
+
+ lprocfs_counter_add(pl->pl_stats, LDLM_POOL_SLV_STAT,
+ slv);
+ lprocfs_counter_add(pl->pl_stats, LDLM_POOL_GRANTED_STAT,
+ granted);
+ lprocfs_counter_add(pl->pl_stats, LDLM_POOL_GRANT_RATE_STAT,
+ grant_rate);
+ lprocfs_counter_add(pl->pl_stats, LDLM_POOL_GRANT_PLAN_STAT,
+ grant_plan);
+ lprocfs_counter_add(pl->pl_stats, LDLM_POOL_CANCEL_RATE_STAT,
+ cancel_rate);
+}
+
+/**
+ * Sets current SLV into obd accessible via ldlm_pl2ns(pl)->ns_obd.
+ */
+static void ldlm_srv_pool_push_slv(struct ldlm_pool *pl)
+{
+ struct obd_device *obd;
+
+ /*
+ * Set new SLV in obd field for using it later without accessing the
+ * pool. This is required to avoid race between sending reply to client
+ * with new SLV and cleanup server stack in which we can't guarantee
+ * that namespace is still alive. We know only that obd is alive as
+ * long as valid export is alive.
+ */
+ obd = ldlm_pl2ns(pl)->ns_obd;
+ LASSERT(obd != NULL);
+ write_lock(&obd->obd_pool_lock);
+ obd->obd_pool_slv = pl->pl_server_lock_volume;
+ write_unlock(&obd->obd_pool_lock);
+}
+
+/**
+ * Recalculates all pool fields on passed \a pl.
+ *
+ * \pre ->pl_lock is not locked.
+ */
+static int ldlm_srv_pool_recalc(struct ldlm_pool *pl)
+{
+ time_t recalc_interval_sec;
+
+ recalc_interval_sec = get_seconds() - pl->pl_recalc_time;
+ if (recalc_interval_sec < pl->pl_recalc_period)
+ return 0;
+
+ spin_lock(&pl->pl_lock);
+ recalc_interval_sec = get_seconds() - pl->pl_recalc_time;
+ if (recalc_interval_sec < pl->pl_recalc_period) {
+ spin_unlock(&pl->pl_lock);
+ return 0;
+ }
+ /*
+ * Recalc SLV after last period. This should be done
+ * _before_ recalculating new grant plan.
+ */
+ ldlm_pool_recalc_slv(pl);
+
+ /*
+ * Make sure that pool informed obd of last SLV changes.
+ */
+ ldlm_srv_pool_push_slv(pl);
+
+ /*
+ * Update grant_plan for new period.
+ */
+ ldlm_pool_recalc_grant_plan(pl);
+
+ pl->pl_recalc_time = get_seconds();
+ lprocfs_counter_add(pl->pl_stats, LDLM_POOL_TIMING_STAT,
+ recalc_interval_sec);
+ spin_unlock(&pl->pl_lock);
+ return 0;
+}
+
+/**
+ * This function is used on server side as main entry point for memory
+ * pressure handling. It decreases SLV on \a pl according to passed
+ * \a nr and \a gfp_mask.
+ *
+ * Our goal here is to decrease SLV such a way that clients hold \a nr
+ * locks smaller in next 10h.
+ */
+static int ldlm_srv_pool_shrink(struct ldlm_pool *pl,
+ int nr, gfp_t gfp_mask)
+{
+ __u32 limit;
+
+ /*
+ * VM is asking how many entries may be potentially freed.
+ */
+ if (nr == 0)
+ return atomic_read(&pl->pl_granted);
+
+ /*
+ * Client already canceled locks but server is already in shrinker
+ * and can't cancel anything. Let's catch this race.
+ */
+ if (atomic_read(&pl->pl_granted) == 0)
+ return 0;
+
+ spin_lock(&pl->pl_lock);
+
+ /*
+ * We want shrinker to possibly cause cancellation of @nr locks from
+ * clients or grant approximately @nr locks smaller next intervals.
+ *
+ * This is why we decreased SLV by @nr. This effect will only be as
+ * long as one re-calc interval (1s these days) and this should be
+ * enough to pass this decreased SLV to all clients. On next recalc
+ * interval pool will either increase SLV if locks load is not high
+ * or will keep on same level or even decrease again, thus, shrinker
+ * decreased SLV will affect next recalc intervals and this way will
+ * make locking load lower.
+ */
+ if (nr < pl->pl_server_lock_volume) {
+ pl->pl_server_lock_volume = pl->pl_server_lock_volume - nr;
+ } else {
+ limit = ldlm_pool_get_limit(pl);
+ pl->pl_server_lock_volume = ldlm_pool_slv_min(limit);
+ }
+
+ /*
+ * Make sure that pool informed obd of last SLV changes.
+ */
+ ldlm_srv_pool_push_slv(pl);
+ spin_unlock(&pl->pl_lock);
+
+ /*
+ * We did not really free any memory here so far, it only will be
+ * freed later may be, so that we return 0 to not confuse VM.
+ */
+ return 0;
+}
+
+/**
+ * Setup server side pool \a pl with passed \a limit.
+ */
+static int ldlm_srv_pool_setup(struct ldlm_pool *pl, int limit)
+{
+ struct obd_device *obd;
+
+ obd = ldlm_pl2ns(pl)->ns_obd;
+ LASSERT(obd != NULL && obd != LP_POISON);
+ LASSERT(obd->obd_type != LP_POISON);
+ write_lock(&obd->obd_pool_lock);
+ obd->obd_pool_limit = limit;
+ write_unlock(&obd->obd_pool_lock);
+
+ ldlm_pool_set_limit(pl, limit);
+ return 0;
+}
+
+/**
+ * Sets SLV and Limit from ldlm_pl2ns(pl)->ns_obd tp passed \a pl.
+ */
+static void ldlm_cli_pool_pop_slv(struct ldlm_pool *pl)
+{
+ struct obd_device *obd;
+
+ /*
+ * Get new SLV and Limit from obd which is updated with coming
+ * RPCs.
+ */
+ obd = ldlm_pl2ns(pl)->ns_obd;
+ LASSERT(obd != NULL);
+ read_lock(&obd->obd_pool_lock);
+ pl->pl_server_lock_volume = obd->obd_pool_slv;
+ ldlm_pool_set_limit(pl, obd->obd_pool_limit);
+ read_unlock(&obd->obd_pool_lock);
+}
+
+/**
+ * Recalculates client size pool \a pl according to current SLV and Limit.
+ */
+static int ldlm_cli_pool_recalc(struct ldlm_pool *pl)
+{
+ time_t recalc_interval_sec;
+ int ret;
+
+ recalc_interval_sec = get_seconds() - pl->pl_recalc_time;
+ if (recalc_interval_sec < pl->pl_recalc_period)
+ return 0;
+
+ spin_lock(&pl->pl_lock);
+ /*
+ * Check if we need to recalc lists now.
+ */
+ recalc_interval_sec = get_seconds() - pl->pl_recalc_time;
+ if (recalc_interval_sec < pl->pl_recalc_period) {
+ spin_unlock(&pl->pl_lock);
+ return 0;
+ }
+
+ /*
+ * Make sure that pool knows last SLV and Limit from obd.
+ */
+ ldlm_cli_pool_pop_slv(pl);
+
+ spin_unlock(&pl->pl_lock);
+
+ /*
+ * Do not cancel locks in case lru resize is disabled for this ns.
+ */
+ if (!ns_connect_lru_resize(ldlm_pl2ns(pl))) {
+ ret = 0;
+ goto out;
+ }
+
+ /*
+ * In the time of canceling locks on client we do not need to maintain
+ * sharp timing, we only want to cancel locks asap according to new SLV.
+ * It may be called when SLV has changed much, this is why we do not
+ * take into account pl->pl_recalc_time here.
+ */
+ ret = ldlm_cancel_lru(ldlm_pl2ns(pl), 0, LCF_ASYNC, LDLM_CANCEL_LRUR);
+
+out:
+ spin_lock(&pl->pl_lock);
+ /*
+ * Time of LRU resizing might be longer than period,
+ * so update after LRU resizing rather than before it.
+ */
+ pl->pl_recalc_time = get_seconds();
+ lprocfs_counter_add(pl->pl_stats, LDLM_POOL_TIMING_STAT,
+ recalc_interval_sec);
+ spin_unlock(&pl->pl_lock);
+ return ret;
+}
+
+/**
+ * This function is main entry point for memory pressure handling on client
+ * side. Main goal of this function is to cancel some number of locks on
+ * passed \a pl according to \a nr and \a gfp_mask.
+ */
+static int ldlm_cli_pool_shrink(struct ldlm_pool *pl,
+ int nr, gfp_t gfp_mask)
+{
+ struct ldlm_namespace *ns;
+ int unused;
+
+ ns = ldlm_pl2ns(pl);
+
+ /*
+ * Do not cancel locks in case lru resize is disabled for this ns.
+ */
+ if (!ns_connect_lru_resize(ns))
+ return 0;
+
+ /*
+ * Make sure that pool knows last SLV and Limit from obd.
+ */
+ ldlm_cli_pool_pop_slv(pl);
+
+ spin_lock(&ns->ns_lock);
+ unused = ns->ns_nr_unused;
+ spin_unlock(&ns->ns_lock);
+
+ if (nr == 0)
+ return (unused / 100) * sysctl_vfs_cache_pressure;
+ else
+ return ldlm_cancel_lru(ns, nr, LCF_ASYNC, LDLM_CANCEL_SHRINK);
+}
+
+static const struct ldlm_pool_ops ldlm_srv_pool_ops = {
+ .po_recalc = ldlm_srv_pool_recalc,
+ .po_shrink = ldlm_srv_pool_shrink,
+ .po_setup = ldlm_srv_pool_setup
+};
+
+static const struct ldlm_pool_ops ldlm_cli_pool_ops = {
+ .po_recalc = ldlm_cli_pool_recalc,
+ .po_shrink = ldlm_cli_pool_shrink
+};
+
+/**
+ * Pool recalc wrapper. Will call either client or server pool recalc callback
+ * depending what pool \a pl is used.
+ */
+int ldlm_pool_recalc(struct ldlm_pool *pl)
+{
+ time_t recalc_interval_sec;
+ int count;
+
+ recalc_interval_sec = get_seconds() - pl->pl_recalc_time;
+ if (recalc_interval_sec <= 0)
+ goto recalc;
+
+ spin_lock(&pl->pl_lock);
+ if (recalc_interval_sec > 0) {
+ /*
+ * Update pool statistics every 1s.
+ */
+ ldlm_pool_recalc_stats(pl);
+
+ /*
+ * Zero out all rates and speed for the last period.
+ */
+ atomic_set(&pl->pl_grant_rate, 0);
+ atomic_set(&pl->pl_cancel_rate, 0);
+ }
+ spin_unlock(&pl->pl_lock);
+
+ recalc:
+ if (pl->pl_ops->po_recalc != NULL) {
+ count = pl->pl_ops->po_recalc(pl);
+ lprocfs_counter_add(pl->pl_stats, LDLM_POOL_RECALC_STAT,
+ count);
+ }
+ recalc_interval_sec = pl->pl_recalc_time - get_seconds() +
+ pl->pl_recalc_period;
+ if (recalc_interval_sec <= 0) {
+ /* Prevent too frequent recalculation. */
+ CDEBUG(D_DLMTRACE, "Negative interval(%ld), "
+ "too short period(%ld)",
+ recalc_interval_sec,
+ pl->pl_recalc_period);
+ recalc_interval_sec = 1;
+ }
+
+ return recalc_interval_sec;
+}
+
+/*
+ * Pool shrink wrapper. Will call either client or server pool recalc callback
+ * depending what pool pl is used. When nr == 0, just return the number of
+ * freeable locks. Otherwise, return the number of canceled locks.
+ */
+int ldlm_pool_shrink(struct ldlm_pool *pl, int nr,
+ gfp_t gfp_mask)
+{
+ int cancel = 0;
+
+ if (pl->pl_ops->po_shrink != NULL) {
+ cancel = pl->pl_ops->po_shrink(pl, nr, gfp_mask);
+ if (nr > 0) {
+ lprocfs_counter_add(pl->pl_stats,
+ LDLM_POOL_SHRINK_REQTD_STAT,
+ nr);
+ lprocfs_counter_add(pl->pl_stats,
+ LDLM_POOL_SHRINK_FREED_STAT,
+ cancel);
+ CDEBUG(D_DLMTRACE, "%s: request to shrink %d locks, shrunk %d\n",
+ pl->pl_name, nr, cancel);
+ }
+ }
+ return cancel;
+}
+EXPORT_SYMBOL(ldlm_pool_shrink);
+
+/**
+ * Pool setup wrapper. Will call either client or server pool recalc callback
+ * depending what pool \a pl is used.
+ *
+ * Sets passed \a limit into pool \a pl.
+ */
+int ldlm_pool_setup(struct ldlm_pool *pl, int limit)
+{
+ if (pl->pl_ops->po_setup != NULL)
+ return pl->pl_ops->po_setup(pl, limit);
+ return 0;
+}
+EXPORT_SYMBOL(ldlm_pool_setup);
+
+#if defined(CONFIG_PROC_FS)
+static int lprocfs_pool_state_seq_show(struct seq_file *m, void *unused)
+{
+ int granted, grant_rate, cancel_rate, grant_step;
+ int grant_speed, grant_plan, lvf;
+ struct ldlm_pool *pl = m->private;
+ __u64 slv, clv;
+ __u32 limit;
+
+ spin_lock(&pl->pl_lock);
+ slv = pl->pl_server_lock_volume;
+ clv = pl->pl_client_lock_volume;
+ limit = ldlm_pool_get_limit(pl);
+ grant_plan = pl->pl_grant_plan;
+ granted = atomic_read(&pl->pl_granted);
+ grant_rate = atomic_read(&pl->pl_grant_rate);
+ cancel_rate = atomic_read(&pl->pl_cancel_rate);
+ grant_speed = grant_rate - cancel_rate;
+ lvf = atomic_read(&pl->pl_lock_volume_factor);
+ grant_step = ldlm_pool_t2gsp(pl->pl_recalc_period);
+ spin_unlock(&pl->pl_lock);
+
+ seq_printf(m, "LDLM pool state (%s):\n"
+ " SLV: %llu\n"
+ " CLV: %llu\n"
+ " LVF: %d\n",
+ pl->pl_name, slv, clv, lvf);
+
+ if (ns_is_server(ldlm_pl2ns(pl))) {
+ seq_printf(m, " GSP: %d%%\n"
+ " GP: %d\n",
+ grant_step, grant_plan);
+ }
+ seq_printf(m, " GR: %d\n CR: %d\n GS: %d\n"
+ " G: %d\n L: %d\n",
+ grant_rate, cancel_rate, grant_speed,
+ granted, limit);
+
+ return 0;
+}
+LPROC_SEQ_FOPS_RO(lprocfs_pool_state);
+
+static int lprocfs_grant_speed_seq_show(struct seq_file *m, void *unused)
+{
+ struct ldlm_pool *pl = m->private;
+ int grant_speed;
+
+ spin_lock(&pl->pl_lock);
+ /* serialize with ldlm_pool_recalc */
+ grant_speed = atomic_read(&pl->pl_grant_rate) -
+ atomic_read(&pl->pl_cancel_rate);
+ spin_unlock(&pl->pl_lock);
+ return lprocfs_rd_uint(m, &grant_speed);
+}
+
+LDLM_POOL_PROC_READER_SEQ_SHOW(grant_plan, int);
+LPROC_SEQ_FOPS_RO(lprocfs_grant_plan);
+
+LDLM_POOL_PROC_READER_SEQ_SHOW(recalc_period, int);
+LDLM_POOL_PROC_WRITER(recalc_period, int);
+static ssize_t lprocfs_recalc_period_seq_write(struct file *file,
+ const char __user *buf,
+ size_t len, loff_t *off)
+{
+ struct seq_file *seq = file->private_data;
+
+ return lprocfs_wr_recalc_period(file, buf, len, seq->private);
+}
+LPROC_SEQ_FOPS(lprocfs_recalc_period);
+
+LPROC_SEQ_FOPS_RO_TYPE(ldlm_pool, u64);
+LPROC_SEQ_FOPS_RO_TYPE(ldlm_pool, atomic);
+LPROC_SEQ_FOPS_RW_TYPE(ldlm_pool_rw, atomic);
+
+LPROC_SEQ_FOPS_RO(lprocfs_grant_speed);
+
+#define LDLM_POOL_ADD_VAR(name, var, ops) \
+ do { \
+ snprintf(var_name, MAX_STRING_SIZE, #name); \
+ pool_vars[0].data = var; \
+ pool_vars[0].fops = ops; \
+ lprocfs_add_vars(pl->pl_proc_dir, pool_vars, NULL);\
+ } while (0)
+
+static int ldlm_pool_proc_init(struct ldlm_pool *pl)
+{
+ struct ldlm_namespace *ns = ldlm_pl2ns(pl);
+ struct proc_dir_entry *parent_ns_proc;
+ struct lprocfs_vars pool_vars[2];
+ char *var_name = NULL;
+ int rc = 0;
+
+ OBD_ALLOC(var_name, MAX_STRING_SIZE + 1);
+ if (!var_name)
+ return -ENOMEM;
+
+ parent_ns_proc = ns->ns_proc_dir_entry;
+ if (parent_ns_proc == NULL) {
+ CERROR("%s: proc entry is not initialized\n",
+ ldlm_ns_name(ns));
+ rc = -EINVAL;
+ goto out_free_name;
+ }
+ pl->pl_proc_dir = lprocfs_register("pool", parent_ns_proc,
+ NULL, NULL);
+ if (IS_ERR(pl->pl_proc_dir)) {
+ CERROR("LProcFS failed in ldlm-pool-init\n");
+ rc = PTR_ERR(pl->pl_proc_dir);
+ pl->pl_proc_dir = NULL;
+ goto out_free_name;
+ }
+
+ var_name[MAX_STRING_SIZE] = '\0';
+ memset(pool_vars, 0, sizeof(pool_vars));
+ pool_vars[0].name = var_name;
+
+ LDLM_POOL_ADD_VAR("server_lock_volume", &pl->pl_server_lock_volume,
+ &ldlm_pool_u64_fops);
+ LDLM_POOL_ADD_VAR("limit", &pl->pl_limit, &ldlm_pool_rw_atomic_fops);
+ LDLM_POOL_ADD_VAR("granted", &pl->pl_granted, &ldlm_pool_atomic_fops);
+ LDLM_POOL_ADD_VAR("grant_speed", pl, &lprocfs_grant_speed_fops);
+ LDLM_POOL_ADD_VAR("cancel_rate", &pl->pl_cancel_rate,
+ &ldlm_pool_atomic_fops);
+ LDLM_POOL_ADD_VAR("grant_rate", &pl->pl_grant_rate,
+ &ldlm_pool_atomic_fops);
+ LDLM_POOL_ADD_VAR("grant_plan", pl, &lprocfs_grant_plan_fops);
+ LDLM_POOL_ADD_VAR("recalc_period", pl, &lprocfs_recalc_period_fops);
+ LDLM_POOL_ADD_VAR("lock_volume_factor", &pl->pl_lock_volume_factor,
+ &ldlm_pool_rw_atomic_fops);
+ LDLM_POOL_ADD_VAR("state", pl, &lprocfs_pool_state_fops);
+
+ pl->pl_stats = lprocfs_alloc_stats(LDLM_POOL_LAST_STAT -
+ LDLM_POOL_FIRST_STAT, 0);
+ if (!pl->pl_stats) {
+ rc = -ENOMEM;
+ goto out_free_name;
+ }
+
+ lprocfs_counter_init(pl->pl_stats, LDLM_POOL_GRANTED_STAT,
+ LPROCFS_CNTR_AVGMINMAX | LPROCFS_CNTR_STDDEV,
+ "granted", "locks");
+ lprocfs_counter_init(pl->pl_stats, LDLM_POOL_GRANT_STAT,
+ LPROCFS_CNTR_AVGMINMAX | LPROCFS_CNTR_STDDEV,
+ "grant", "locks");
+ lprocfs_counter_init(pl->pl_stats, LDLM_POOL_CANCEL_STAT,
+ LPROCFS_CNTR_AVGMINMAX | LPROCFS_CNTR_STDDEV,
+ "cancel", "locks");
+ lprocfs_counter_init(pl->pl_stats, LDLM_POOL_GRANT_RATE_STAT,
+ LPROCFS_CNTR_AVGMINMAX | LPROCFS_CNTR_STDDEV,
+ "grant_rate", "locks/s");
+ lprocfs_counter_init(pl->pl_stats, LDLM_POOL_CANCEL_RATE_STAT,
+ LPROCFS_CNTR_AVGMINMAX | LPROCFS_CNTR_STDDEV,
+ "cancel_rate", "locks/s");
+ lprocfs_counter_init(pl->pl_stats, LDLM_POOL_GRANT_PLAN_STAT,
+ LPROCFS_CNTR_AVGMINMAX | LPROCFS_CNTR_STDDEV,
+ "grant_plan", "locks/s");
+ lprocfs_counter_init(pl->pl_stats, LDLM_POOL_SLV_STAT,
+ LPROCFS_CNTR_AVGMINMAX | LPROCFS_CNTR_STDDEV,
+ "slv", "slv");
+ lprocfs_counter_init(pl->pl_stats, LDLM_POOL_SHRINK_REQTD_STAT,
+ LPROCFS_CNTR_AVGMINMAX | LPROCFS_CNTR_STDDEV,
+ "shrink_request", "locks");
+ lprocfs_counter_init(pl->pl_stats, LDLM_POOL_SHRINK_FREED_STAT,
+ LPROCFS_CNTR_AVGMINMAX | LPROCFS_CNTR_STDDEV,
+ "shrink_freed", "locks");
+ lprocfs_counter_init(pl->pl_stats, LDLM_POOL_RECALC_STAT,
+ LPROCFS_CNTR_AVGMINMAX | LPROCFS_CNTR_STDDEV,
+ "recalc_freed", "locks");
+ lprocfs_counter_init(pl->pl_stats, LDLM_POOL_TIMING_STAT,
+ LPROCFS_CNTR_AVGMINMAX | LPROCFS_CNTR_STDDEV,
+ "recalc_timing", "sec");
+ rc = lprocfs_register_stats(pl->pl_proc_dir, "stats", pl->pl_stats);
+
+out_free_name:
+ OBD_FREE(var_name, MAX_STRING_SIZE + 1);
+ return rc;
+}
+
+static void ldlm_pool_proc_fini(struct ldlm_pool *pl)
+{
+ if (pl->pl_stats != NULL) {
+ lprocfs_free_stats(&pl->pl_stats);
+ pl->pl_stats = NULL;
+ }
+ if (pl->pl_proc_dir != NULL) {
+ lprocfs_remove(&pl->pl_proc_dir);
+ pl->pl_proc_dir = NULL;
+ }
+}
+#else /* !CONFIG_PROC_FS */
+static int ldlm_pool_proc_init(struct ldlm_pool *pl)
+{
+ return 0;
+}
+
+static void ldlm_pool_proc_fini(struct ldlm_pool *pl) {}
+#endif /* CONFIG_PROC_FS */
+
+int ldlm_pool_init(struct ldlm_pool *pl, struct ldlm_namespace *ns,
+ int idx, ldlm_side_t client)
+{
+ int rc;
+
+ spin_lock_init(&pl->pl_lock);
+ atomic_set(&pl->pl_granted, 0);
+ pl->pl_recalc_time = get_seconds();
+ atomic_set(&pl->pl_lock_volume_factor, 1);
+
+ atomic_set(&pl->pl_grant_rate, 0);
+ atomic_set(&pl->pl_cancel_rate, 0);
+ pl->pl_grant_plan = LDLM_POOL_GP(LDLM_POOL_HOST_L);
+
+ snprintf(pl->pl_name, sizeof(pl->pl_name), "ldlm-pool-%s-%d",
+ ldlm_ns_name(ns), idx);
+
+ if (client == LDLM_NAMESPACE_SERVER) {
+ pl->pl_ops = &ldlm_srv_pool_ops;
+ ldlm_pool_set_limit(pl, LDLM_POOL_HOST_L);
+ pl->pl_recalc_period = LDLM_POOL_SRV_DEF_RECALC_PERIOD;
+ pl->pl_server_lock_volume = ldlm_pool_slv_max(LDLM_POOL_HOST_L);
+ } else {
+ ldlm_pool_set_limit(pl, 1);
+ pl->pl_server_lock_volume = 0;
+ pl->pl_ops = &ldlm_cli_pool_ops;
+ pl->pl_recalc_period = LDLM_POOL_CLI_DEF_RECALC_PERIOD;
+ }
+ pl->pl_client_lock_volume = 0;
+ rc = ldlm_pool_proc_init(pl);
+ if (rc)
+ return rc;
+
+ CDEBUG(D_DLMTRACE, "Lock pool %s is initialized\n", pl->pl_name);
+
+ return rc;
+}
+EXPORT_SYMBOL(ldlm_pool_init);
+
+void ldlm_pool_fini(struct ldlm_pool *pl)
+{
+ ldlm_pool_proc_fini(pl);
+
+ /*
+ * Pool should not be used after this point. We can't free it here as
+ * it lives in struct ldlm_namespace, but still interested in catching
+ * any abnormal using cases.
+ */
+ POISON(pl, 0x5a, sizeof(*pl));
+}
+EXPORT_SYMBOL(ldlm_pool_fini);
+
+/**
+ * Add new taken ldlm lock \a lock into pool \a pl accounting.
+ */
+void ldlm_pool_add(struct ldlm_pool *pl, struct ldlm_lock *lock)
+{
+ /*
+ * FLOCK locks are special in a sense that they are almost never
+ * cancelled, instead special kind of lock is used to drop them.
+ * also there is no LRU for flock locks, so no point in tracking
+ * them anyway.
+ */
+ if (lock->l_resource->lr_type == LDLM_FLOCK)
+ return;
+
+ atomic_inc(&pl->pl_granted);
+ atomic_inc(&pl->pl_grant_rate);
+ lprocfs_counter_incr(pl->pl_stats, LDLM_POOL_GRANT_STAT);
+ /*
+ * Do not do pool recalc for client side as all locks which
+ * potentially may be canceled has already been packed into
+ * enqueue/cancel rpc. Also we do not want to run out of stack
+ * with too long call paths.
+ */
+ if (ns_is_server(ldlm_pl2ns(pl)))
+ ldlm_pool_recalc(pl);
+}
+EXPORT_SYMBOL(ldlm_pool_add);
+
+/**
+ * Remove ldlm lock \a lock from pool \a pl accounting.
+ */
+void ldlm_pool_del(struct ldlm_pool *pl, struct ldlm_lock *lock)
+{
+ /*
+ * Filter out FLOCK locks. Read above comment in ldlm_pool_add().
+ */
+ if (lock->l_resource->lr_type == LDLM_FLOCK)
+ return;
+
+ LASSERT(atomic_read(&pl->pl_granted) > 0);
+ atomic_dec(&pl->pl_granted);
+ atomic_inc(&pl->pl_cancel_rate);
+
+ lprocfs_counter_incr(pl->pl_stats, LDLM_POOL_CANCEL_STAT);
+
+ if (ns_is_server(ldlm_pl2ns(pl)))
+ ldlm_pool_recalc(pl);
+}
+EXPORT_SYMBOL(ldlm_pool_del);
+
+/**
+ * Returns current \a pl SLV.
+ *
+ * \pre ->pl_lock is not locked.
+ */
+__u64 ldlm_pool_get_slv(struct ldlm_pool *pl)
+{
+ __u64 slv;
+
+ spin_lock(&pl->pl_lock);
+ slv = pl->pl_server_lock_volume;
+ spin_unlock(&pl->pl_lock);
+ return slv;
+}
+EXPORT_SYMBOL(ldlm_pool_get_slv);
+
+/**
+ * Sets passed \a slv to \a pl.
+ *
+ * \pre ->pl_lock is not locked.
+ */
+void ldlm_pool_set_slv(struct ldlm_pool *pl, __u64 slv)
+{
+ spin_lock(&pl->pl_lock);
+ pl->pl_server_lock_volume = slv;
+ spin_unlock(&pl->pl_lock);
+}
+EXPORT_SYMBOL(ldlm_pool_set_slv);
+
+/**
+ * Returns current \a pl CLV.
+ *
+ * \pre ->pl_lock is not locked.
+ */
+__u64 ldlm_pool_get_clv(struct ldlm_pool *pl)
+{
+ __u64 slv;
+
+ spin_lock(&pl->pl_lock);
+ slv = pl->pl_client_lock_volume;
+ spin_unlock(&pl->pl_lock);
+ return slv;
+}
+EXPORT_SYMBOL(ldlm_pool_get_clv);
+
+/**
+ * Sets passed \a clv to \a pl.
+ *
+ * \pre ->pl_lock is not locked.
+ */
+void ldlm_pool_set_clv(struct ldlm_pool *pl, __u64 clv)
+{
+ spin_lock(&pl->pl_lock);
+ pl->pl_client_lock_volume = clv;
+ spin_unlock(&pl->pl_lock);
+}
+EXPORT_SYMBOL(ldlm_pool_set_clv);
+
+/**
+ * Returns current \a pl limit.
+ */
+__u32 ldlm_pool_get_limit(struct ldlm_pool *pl)
+{
+ return atomic_read(&pl->pl_limit);
+}
+EXPORT_SYMBOL(ldlm_pool_get_limit);
+
+/**
+ * Sets passed \a limit to \a pl.
+ */
+void ldlm_pool_set_limit(struct ldlm_pool *pl, __u32 limit)
+{
+ atomic_set(&pl->pl_limit, limit);
+}
+EXPORT_SYMBOL(ldlm_pool_set_limit);
+
+/**
+ * Returns current LVF from \a pl.
+ */
+__u32 ldlm_pool_get_lvf(struct ldlm_pool *pl)
+{
+ return atomic_read(&pl->pl_lock_volume_factor);
+}
+EXPORT_SYMBOL(ldlm_pool_get_lvf);
+
+static int ldlm_pool_granted(struct ldlm_pool *pl)
+{
+ return atomic_read(&pl->pl_granted);
+}
+
+static struct ptlrpc_thread *ldlm_pools_thread;
+static struct completion ldlm_pools_comp;
+
+/*
+ * count locks from all namespaces (if possible). Returns number of
+ * cached locks.
+ */
+static unsigned long ldlm_pools_count(ldlm_side_t client, gfp_t gfp_mask)
+{
+ int total = 0, nr_ns;
+ struct ldlm_namespace *ns;
+ struct ldlm_namespace *ns_old = NULL; /* loop detection */
+ void *cookie;
+
+ if (client == LDLM_NAMESPACE_CLIENT && !(gfp_mask & __GFP_FS))
+ return 0;
+
+ CDEBUG(D_DLMTRACE, "Request to count %s locks from all pools\n",
+ client == LDLM_NAMESPACE_CLIENT ? "client" : "server");
+
+ cookie = cl_env_reenter();
+
+ /*
+ * Find out how many resources we may release.
+ */
+ for (nr_ns = ldlm_namespace_nr_read(client);
+ nr_ns > 0; nr_ns--) {
+ mutex_lock(ldlm_namespace_lock(client));
+ if (list_empty(ldlm_namespace_list(client))) {
+ mutex_unlock(ldlm_namespace_lock(client));
+ cl_env_reexit(cookie);
+ return 0;
+ }
+ ns = ldlm_namespace_first_locked(client);
+
+ if (ns == ns_old) {
+ mutex_unlock(ldlm_namespace_lock(client));
+ break;
+ }
+
+ if (ldlm_ns_empty(ns)) {
+ ldlm_namespace_move_to_inactive_locked(ns, client);
+ mutex_unlock(ldlm_namespace_lock(client));
+ continue;
+ }
+
+ if (ns_old == NULL)
+ ns_old = ns;
+
+ ldlm_namespace_get(ns);
+ ldlm_namespace_move_to_active_locked(ns, client);
+ mutex_unlock(ldlm_namespace_lock(client));
+ total += ldlm_pool_shrink(&ns->ns_pool, 0, gfp_mask);
+ ldlm_namespace_put(ns);
+ }
+
+ cl_env_reexit(cookie);
+ return total;
+}
+
+static unsigned long ldlm_pools_scan(ldlm_side_t client, int nr, gfp_t gfp_mask)
+{
+ unsigned long freed = 0;
+ int tmp, nr_ns;
+ struct ldlm_namespace *ns;
+ void *cookie;
+
+ if (client == LDLM_NAMESPACE_CLIENT && !(gfp_mask & __GFP_FS))
+ return -1;
+
+ cookie = cl_env_reenter();
+
+ /*
+ * Shrink at least ldlm_namespace_nr_read(client) namespaces.
+ */
+ for (tmp = nr_ns = ldlm_namespace_nr_read(client);
+ tmp > 0; tmp--) {
+ int cancel, nr_locks;
+
+ /*
+ * Do not call shrink under ldlm_namespace_lock(client)
+ */
+ mutex_lock(ldlm_namespace_lock(client));
+ if (list_empty(ldlm_namespace_list(client))) {
+ mutex_unlock(ldlm_namespace_lock(client));
+ break;
+ }
+ ns = ldlm_namespace_first_locked(client);
+ ldlm_namespace_get(ns);
+ ldlm_namespace_move_to_active_locked(ns, client);
+ mutex_unlock(ldlm_namespace_lock(client));
+
+ nr_locks = ldlm_pool_granted(&ns->ns_pool);
+ /*
+ * We use to shrink propotionally but with new shrinker API,
+ * we lost the total number of freeable locks.
+ */
+ cancel = 1 + min_t(int, nr_locks, nr / nr_ns);
+ freed += ldlm_pool_shrink(&ns->ns_pool, cancel, gfp_mask);
+ ldlm_namespace_put(ns);
+ }
+ cl_env_reexit(cookie);
+ /*
+ * we only decrease the SLV in server pools shrinker, return
+ * SHRINK_STOP to kernel to avoid needless loop. LU-1128
+ */
+ return (client == LDLM_NAMESPACE_SERVER) ? SHRINK_STOP : freed;
+}
+
+static unsigned long ldlm_pools_srv_count(struct shrinker *s,
+ struct shrink_control *sc)
+{
+ return ldlm_pools_count(LDLM_NAMESPACE_SERVER, sc->gfp_mask);
+}
+
+static unsigned long ldlm_pools_srv_scan(struct shrinker *s,
+ struct shrink_control *sc)
+{
+ return ldlm_pools_scan(LDLM_NAMESPACE_SERVER, sc->nr_to_scan,
+ sc->gfp_mask);
+}
+
+static unsigned long ldlm_pools_cli_count(struct shrinker *s,
+ struct shrink_control *sc)
+{
+ return ldlm_pools_count(LDLM_NAMESPACE_CLIENT, sc->gfp_mask);
+}
+
+static unsigned long ldlm_pools_cli_scan(struct shrinker *s,
+ struct shrink_control *sc)
+{
+ return ldlm_pools_scan(LDLM_NAMESPACE_CLIENT, sc->nr_to_scan,
+ sc->gfp_mask);
+}
+
+int ldlm_pools_recalc(ldlm_side_t client)
+{
+ __u32 nr_l = 0, nr_p = 0, l;
+ struct ldlm_namespace *ns;
+ struct ldlm_namespace *ns_old = NULL;
+ int nr, equal = 0;
+ int time = 50; /* seconds of sleep if no active namespaces */
+
+ /*
+ * No need to setup pool limit for client pools.
+ */
+ if (client == LDLM_NAMESPACE_SERVER) {
+ /*
+ * Check all modest namespaces first.
+ */
+ mutex_lock(ldlm_namespace_lock(client));
+ list_for_each_entry(ns, ldlm_namespace_list(client),
+ ns_list_chain) {
+ if (ns->ns_appetite != LDLM_NAMESPACE_MODEST)
+ continue;
+
+ l = ldlm_pool_granted(&ns->ns_pool);
+ if (l == 0)
+ l = 1;
+
+ /*
+ * Set the modest pools limit equal to their avg granted
+ * locks + ~6%.
+ */
+ l += dru(l, LDLM_POOLS_MODEST_MARGIN_SHIFT, 0);
+ ldlm_pool_setup(&ns->ns_pool, l);
+ nr_l += l;
+ nr_p++;
+ }
+
+ /*
+ * Make sure that modest namespaces did not eat more that 2/3
+ * of limit.
+ */
+ if (nr_l >= 2 * (LDLM_POOL_HOST_L / 3)) {
+ CWARN("\"Modest\" pools eat out 2/3 of server locks limit (%d of %lu). This means that you have too many clients for this amount of server RAM. Upgrade server!\n",
+ nr_l, LDLM_POOL_HOST_L);
+ equal = 1;
+ }
+
+ /*
+ * The rest is given to greedy namespaces.
+ */
+ list_for_each_entry(ns, ldlm_namespace_list(client),
+ ns_list_chain) {
+ if (!equal && ns->ns_appetite != LDLM_NAMESPACE_GREEDY)
+ continue;
+
+ if (equal) {
+ /*
+ * In the case 2/3 locks are eaten out by
+ * modest pools, we re-setup equal limit
+ * for _all_ pools.
+ */
+ l = LDLM_POOL_HOST_L /
+ ldlm_namespace_nr_read(client);
+ } else {
+ /*
+ * All the rest of greedy pools will have
+ * all locks in equal parts.
+ */
+ l = (LDLM_POOL_HOST_L - nr_l) /
+ (ldlm_namespace_nr_read(client) -
+ nr_p);
+ }
+ ldlm_pool_setup(&ns->ns_pool, l);
+ }
+ mutex_unlock(ldlm_namespace_lock(client));
+ }
+
+ /*
+ * Recalc at least ldlm_namespace_nr_read(client) namespaces.
+ */
+ for (nr = ldlm_namespace_nr_read(client); nr > 0; nr--) {
+ int skip;
+ /*
+ * Lock the list, get first @ns in the list, getref, move it
+ * to the tail, unlock and call pool recalc. This way we avoid
+ * calling recalc under @ns lock what is really good as we get
+ * rid of potential deadlock on client nodes when canceling
+ * locks synchronously.
+ */
+ mutex_lock(ldlm_namespace_lock(client));
+ if (list_empty(ldlm_namespace_list(client))) {
+ mutex_unlock(ldlm_namespace_lock(client));
+ break;
+ }
+ ns = ldlm_namespace_first_locked(client);
+
+ if (ns_old == ns) { /* Full pass complete */
+ mutex_unlock(ldlm_namespace_lock(client));
+ break;
+ }
+
+ /* We got an empty namespace, need to move it back to inactive
+ * list.
+ * The race with parallel resource creation is fine:
+ * - If they do namespace_get before our check, we fail the
+ * check and they move this item to the end of the list anyway
+ * - If we do the check and then they do namespace_get, then
+ * we move the namespace to inactive and they will move
+ * it back to active (synchronised by the lock, so no clash
+ * there).
+ */
+ if (ldlm_ns_empty(ns)) {
+ ldlm_namespace_move_to_inactive_locked(ns, client);
+ mutex_unlock(ldlm_namespace_lock(client));
+ continue;
+ }
+
+ if (ns_old == NULL)
+ ns_old = ns;
+
+ spin_lock(&ns->ns_lock);
+ /*
+ * skip ns which is being freed, and we don't want to increase
+ * its refcount again, not even temporarily. bz21519 & LU-499.
+ */
+ if (ns->ns_stopping) {
+ skip = 1;
+ } else {
+ skip = 0;
+ ldlm_namespace_get(ns);
+ }
+ spin_unlock(&ns->ns_lock);
+
+ ldlm_namespace_move_to_active_locked(ns, client);
+ mutex_unlock(ldlm_namespace_lock(client));
+
+ /*
+ * After setup is done - recalc the pool.
+ */
+ if (!skip) {
+ int ttime = ldlm_pool_recalc(&ns->ns_pool);
+
+ if (ttime < time)
+ time = ttime;
+
+ ldlm_namespace_put(ns);
+ }
+ }
+ return time;
+}
+EXPORT_SYMBOL(ldlm_pools_recalc);
+
+static int ldlm_pools_thread_main(void *arg)
+{
+ struct ptlrpc_thread *thread = (struct ptlrpc_thread *)arg;
+ int s_time, c_time;
+
+ thread_set_flags(thread, SVC_RUNNING);
+ wake_up(&thread->t_ctl_waitq);
+
+ CDEBUG(D_DLMTRACE, "%s: pool thread starting, process %d\n",
+ "ldlm_poold", current_pid());
+
+ while (1) {
+ struct l_wait_info lwi;
+
+ /*
+ * Recal all pools on this tick.
+ */
+ s_time = ldlm_pools_recalc(LDLM_NAMESPACE_SERVER);
+ c_time = ldlm_pools_recalc(LDLM_NAMESPACE_CLIENT);
+
+ /*
+ * Wait until the next check time, or until we're
+ * stopped.
+ */
+ lwi = LWI_TIMEOUT(cfs_time_seconds(min(s_time, c_time)),
+ NULL, NULL);
+ l_wait_event(thread->t_ctl_waitq,
+ thread_is_stopping(thread) ||
+ thread_is_event(thread),
+ &lwi);
+
+ if (thread_test_and_clear_flags(thread, SVC_STOPPING))
+ break;
+ else
+ thread_test_and_clear_flags(thread, SVC_EVENT);
+ }
+
+ thread_set_flags(thread, SVC_STOPPED);
+ wake_up(&thread->t_ctl_waitq);
+
+ CDEBUG(D_DLMTRACE, "%s: pool thread exiting, process %d\n",
+ "ldlm_poold", current_pid());
+
+ complete_and_exit(&ldlm_pools_comp, 0);
+}
+
+static int ldlm_pools_thread_start(void)
+{
+ struct l_wait_info lwi = { 0 };
+ struct task_struct *task;
+
+ if (ldlm_pools_thread != NULL)
+ return -EALREADY;
+
+ OBD_ALLOC_PTR(ldlm_pools_thread);
+ if (ldlm_pools_thread == NULL)
+ return -ENOMEM;
+
+ init_completion(&ldlm_pools_comp);
+ init_waitqueue_head(&ldlm_pools_thread->t_ctl_waitq);
+
+ task = kthread_run(ldlm_pools_thread_main, ldlm_pools_thread,
+ "ldlm_poold");
+ if (IS_ERR(task)) {
+ CERROR("Can't start pool thread, error %ld\n", PTR_ERR(task));
+ OBD_FREE(ldlm_pools_thread, sizeof(*ldlm_pools_thread));
+ ldlm_pools_thread = NULL;
+ return PTR_ERR(task);
+ }
+ l_wait_event(ldlm_pools_thread->t_ctl_waitq,
+ thread_is_running(ldlm_pools_thread), &lwi);
+ return 0;
+}
+
+static void ldlm_pools_thread_stop(void)
+{
+ if (ldlm_pools_thread == NULL)
+ return;
+
+ thread_set_flags(ldlm_pools_thread, SVC_STOPPING);
+ wake_up(&ldlm_pools_thread->t_ctl_waitq);
+
+ /*
+ * Make sure that pools thread is finished before freeing @thread.
+ * This fixes possible race and oops due to accessing freed memory
+ * in pools thread.
+ */
+ wait_for_completion(&ldlm_pools_comp);
+ OBD_FREE_PTR(ldlm_pools_thread);
+ ldlm_pools_thread = NULL;
+}
+
+static struct shrinker ldlm_pools_srv_shrinker = {
+ .count_objects = ldlm_pools_srv_count,
+ .scan_objects = ldlm_pools_srv_scan,
+ .seeks = DEFAULT_SEEKS,
+};
+
+static struct shrinker ldlm_pools_cli_shrinker = {
+ .count_objects = ldlm_pools_cli_count,
+ .scan_objects = ldlm_pools_cli_scan,
+ .seeks = DEFAULT_SEEKS,
+};
+
+int ldlm_pools_init(void)
+{
+ int rc;
+
+ rc = ldlm_pools_thread_start();
+ if (rc == 0) {
+ register_shrinker(&ldlm_pools_srv_shrinker);
+ register_shrinker(&ldlm_pools_cli_shrinker);
+ }
+ return rc;
+}
+EXPORT_SYMBOL(ldlm_pools_init);
+
+void ldlm_pools_fini(void)
+{
+ unregister_shrinker(&ldlm_pools_srv_shrinker);
+ unregister_shrinker(&ldlm_pools_cli_shrinker);
+ ldlm_pools_thread_stop();
+}
+EXPORT_SYMBOL(ldlm_pools_fini);
diff --git a/drivers/staging/lustre/lustre/ldlm/ldlm_request.c b/drivers/staging/lustre/lustre/ldlm/ldlm_request.c
new file mode 100644
index 000000000..4f7131831
--- /dev/null
+++ b/drivers/staging/lustre/lustre/ldlm/ldlm_request.c
@@ -0,0 +1,2294 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2002, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2010, 2012, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ */
+/**
+ * This file contains Asynchronous System Trap (AST) handlers and related
+ * LDLM request-processing routines.
+ *
+ * An AST is a callback issued on a lock when its state is changed. There are
+ * several different types of ASTs (callbacks) registered for each lock:
+ *
+ * - completion AST: when a lock is enqueued by some process, but cannot be
+ * granted immediately due to other conflicting locks on the same resource,
+ * the completion AST is sent to notify the caller when the lock is
+ * eventually granted
+ *
+ * - blocking AST: when a lock is granted to some process, if another process
+ * enqueues a conflicting (blocking) lock on a resource, a blocking AST is
+ * sent to notify the holder(s) of the lock(s) of the conflicting lock
+ * request. The lock holder(s) must release their lock(s) on that resource in
+ * a timely manner or be evicted by the server.
+ *
+ * - glimpse AST: this is used when a process wants information about a lock
+ * (i.e. the lock value block (LVB)) but does not necessarily require holding
+ * the lock. If the resource is locked, the lock holder(s) are sent glimpse
+ * ASTs and the LVB is returned to the caller, and lock holder(s) may CANCEL
+ * their lock(s) if they are idle. If the resource is not locked, the server
+ * may grant the lock.
+ */
+
+#define DEBUG_SUBSYSTEM S_LDLM
+
+#include "../include/lustre_dlm.h"
+#include "../include/obd_class.h"
+#include "../include/obd.h"
+
+#include "ldlm_internal.h"
+
+int ldlm_enqueue_min = OBD_TIMEOUT_DEFAULT;
+module_param(ldlm_enqueue_min, int, 0644);
+MODULE_PARM_DESC(ldlm_enqueue_min, "lock enqueue timeout minimum");
+
+/* in client side, whether the cached locks will be canceled before replay */
+unsigned int ldlm_cancel_unused_locks_before_replay = 1;
+
+static void interrupted_completion_wait(void *data)
+{
+}
+
+struct lock_wait_data {
+ struct ldlm_lock *lwd_lock;
+ __u32 lwd_conn_cnt;
+};
+
+struct ldlm_async_args {
+ struct lustre_handle lock_handle;
+};
+
+int ldlm_expired_completion_wait(void *data)
+{
+ struct lock_wait_data *lwd = data;
+ struct ldlm_lock *lock = lwd->lwd_lock;
+ struct obd_import *imp;
+ struct obd_device *obd;
+
+ if (lock->l_conn_export == NULL) {
+ static unsigned long next_dump, last_dump;
+
+ LCONSOLE_WARN("lock timed out (enqueued at "CFS_TIME_T", "
+ CFS_DURATION_T"s ago)\n",
+ lock->l_last_activity,
+ cfs_time_sub(get_seconds(),
+ lock->l_last_activity));
+ LDLM_DEBUG(lock, "lock timed out (enqueued at " CFS_TIME_T ", " CFS_DURATION_T "s ago); not entering recovery in server code, just going back to sleep",
+ lock->l_last_activity,
+ cfs_time_sub(get_seconds(),
+ lock->l_last_activity));
+ if (cfs_time_after(cfs_time_current(), next_dump)) {
+ last_dump = next_dump;
+ next_dump = cfs_time_shift(300);
+ ldlm_namespace_dump(D_DLMTRACE,
+ ldlm_lock_to_ns(lock));
+ if (last_dump == 0)
+ libcfs_debug_dumplog();
+ }
+ return 0;
+ }
+
+ obd = lock->l_conn_export->exp_obd;
+ imp = obd->u.cli.cl_import;
+ ptlrpc_fail_import(imp, lwd->lwd_conn_cnt);
+ LDLM_ERROR(lock, "lock timed out (enqueued at "CFS_TIME_T", "
+ CFS_DURATION_T"s ago), entering recovery for %s@%s",
+ lock->l_last_activity,
+ cfs_time_sub(get_seconds(), lock->l_last_activity),
+ obd2cli_tgt(obd), imp->imp_connection->c_remote_uuid.uuid);
+
+ return 0;
+}
+EXPORT_SYMBOL(ldlm_expired_completion_wait);
+
+/* We use the same basis for both server side and client side functions
+ from a single node. */
+int ldlm_get_enq_timeout(struct ldlm_lock *lock)
+{
+ int timeout = at_get(ldlm_lock_to_ns_at(lock));
+
+ if (AT_OFF)
+ return obd_timeout / 2;
+ /* Since these are non-updating timeouts, we should be conservative.
+ It would be nice to have some kind of "early reply" mechanism for
+ lock callbacks too... */
+ timeout = min_t(int, at_max, timeout + (timeout >> 1)); /* 150% */
+ return max(timeout, ldlm_enqueue_min);
+}
+EXPORT_SYMBOL(ldlm_get_enq_timeout);
+
+/**
+ * Helper function for ldlm_completion_ast(), updating timings when lock is
+ * actually granted.
+ */
+static int ldlm_completion_tail(struct ldlm_lock *lock)
+{
+ long delay;
+ int result;
+
+ if (lock->l_flags & (LDLM_FL_DESTROYED | LDLM_FL_FAILED)) {
+ LDLM_DEBUG(lock, "client-side enqueue: destroyed");
+ result = -EIO;
+ } else {
+ delay = cfs_time_sub(get_seconds(),
+ lock->l_last_activity);
+ LDLM_DEBUG(lock, "client-side enqueue: granted after "
+ CFS_DURATION_T"s", delay);
+
+ /* Update our time estimate */
+ at_measured(ldlm_lock_to_ns_at(lock),
+ delay);
+ result = 0;
+ }
+ return result;
+}
+
+/**
+ * Implementation of ->l_completion_ast() for a client, that doesn't wait
+ * until lock is granted. Suitable for locks enqueued through ptlrpcd, of
+ * other threads that cannot block for long.
+ */
+int ldlm_completion_ast_async(struct ldlm_lock *lock, __u64 flags, void *data)
+{
+ if (flags == LDLM_FL_WAIT_NOREPROC) {
+ LDLM_DEBUG(lock, "client-side enqueue waiting on pending lock");
+ return 0;
+ }
+
+ if (!(flags & (LDLM_FL_BLOCK_WAIT | LDLM_FL_BLOCK_GRANTED |
+ LDLM_FL_BLOCK_CONV))) {
+ wake_up(&lock->l_waitq);
+ return ldlm_completion_tail(lock);
+ }
+
+ LDLM_DEBUG(lock, "client-side enqueue returned a blocked lock, going forward");
+ ldlm_reprocess_all(lock->l_resource);
+ return 0;
+}
+EXPORT_SYMBOL(ldlm_completion_ast_async);
+
+/**
+ * Generic LDLM "completion" AST. This is called in several cases:
+ *
+ * - when a reply to an ENQUEUE RPC is received from the server
+ * (ldlm_cli_enqueue_fini()). Lock might be granted or not granted at
+ * this point (determined by flags);
+ *
+ * - when LDLM_CP_CALLBACK RPC comes to client to notify it that lock has
+ * been granted;
+ *
+ * - when ldlm_lock_match(LDLM_FL_LVB_READY) is about to wait until lock
+ * gets correct lvb;
+ *
+ * - to force all locks when resource is destroyed (cleanup_resource());
+ *
+ * - during lock conversion (not used currently).
+ *
+ * If lock is not granted in the first case, this function waits until second
+ * or penultimate cases happen in some other thread.
+ *
+ */
+int ldlm_completion_ast(struct ldlm_lock *lock, __u64 flags, void *data)
+{
+ /* XXX ALLOCATE - 160 bytes */
+ struct lock_wait_data lwd;
+ struct obd_device *obd;
+ struct obd_import *imp = NULL;
+ struct l_wait_info lwi;
+ __u32 timeout;
+ int rc = 0;
+
+ if (flags == LDLM_FL_WAIT_NOREPROC) {
+ LDLM_DEBUG(lock, "client-side enqueue waiting on pending lock");
+ goto noreproc;
+ }
+
+ if (!(flags & (LDLM_FL_BLOCK_WAIT | LDLM_FL_BLOCK_GRANTED |
+ LDLM_FL_BLOCK_CONV))) {
+ wake_up(&lock->l_waitq);
+ return 0;
+ }
+
+ LDLM_DEBUG(lock, "client-side enqueue returned a blocked lock, sleeping");
+
+noreproc:
+
+ obd = class_exp2obd(lock->l_conn_export);
+
+ /* if this is a local lock, then there is no import */
+ if (obd != NULL)
+ imp = obd->u.cli.cl_import;
+
+ /* Wait a long time for enqueue - server may have to callback a
+ lock from another client. Server will evict the other client if it
+ doesn't respond reasonably, and then give us the lock. */
+ timeout = ldlm_get_enq_timeout(lock) * 2;
+
+ lwd.lwd_lock = lock;
+
+ if (lock->l_flags & LDLM_FL_NO_TIMEOUT) {
+ LDLM_DEBUG(lock, "waiting indefinitely because of NO_TIMEOUT");
+ lwi = LWI_INTR(interrupted_completion_wait, &lwd);
+ } else {
+ lwi = LWI_TIMEOUT_INTR(cfs_time_seconds(timeout),
+ ldlm_expired_completion_wait,
+ interrupted_completion_wait, &lwd);
+ }
+
+ if (imp != NULL) {
+ spin_lock(&imp->imp_lock);
+ lwd.lwd_conn_cnt = imp->imp_conn_cnt;
+ spin_unlock(&imp->imp_lock);
+ }
+
+ if (ns_is_client(ldlm_lock_to_ns(lock)) &&
+ OBD_FAIL_CHECK_RESET(OBD_FAIL_LDLM_INTR_CP_AST,
+ OBD_FAIL_LDLM_CP_BL_RACE | OBD_FAIL_ONCE)) {
+ lock->l_flags |= LDLM_FL_FAIL_LOC;
+ rc = -EINTR;
+ } else {
+ /* Go to sleep until the lock is granted or cancelled. */
+ rc = l_wait_event(lock->l_waitq,
+ is_granted_or_cancelled(lock), &lwi);
+ }
+
+ if (rc) {
+ LDLM_DEBUG(lock, "client-side enqueue waking up: failed (%d)",
+ rc);
+ return rc;
+ }
+
+ return ldlm_completion_tail(lock);
+}
+EXPORT_SYMBOL(ldlm_completion_ast);
+
+/**
+ * A helper to build a blocking AST function
+ *
+ * Perform a common operation for blocking ASTs:
+ * deferred lock cancellation.
+ *
+ * \param lock the lock blocking or canceling AST was called on
+ * \retval 0
+ * \see mdt_blocking_ast
+ * \see ldlm_blocking_ast
+ */
+int ldlm_blocking_ast_nocheck(struct ldlm_lock *lock)
+{
+ int do_ast;
+
+ lock->l_flags |= LDLM_FL_CBPENDING;
+ do_ast = !lock->l_readers && !lock->l_writers;
+ unlock_res_and_lock(lock);
+
+ if (do_ast) {
+ struct lustre_handle lockh;
+ int rc;
+
+ LDLM_DEBUG(lock, "already unused, calling ldlm_cli_cancel");
+ ldlm_lock2handle(lock, &lockh);
+ rc = ldlm_cli_cancel(&lockh, LCF_ASYNC);
+ if (rc < 0)
+ CERROR("ldlm_cli_cancel: %d\n", rc);
+ } else {
+ LDLM_DEBUG(lock, "Lock still has references, will be cancelled later");
+ }
+ return 0;
+}
+EXPORT_SYMBOL(ldlm_blocking_ast_nocheck);
+
+/**
+ * Server blocking AST
+ *
+ * ->l_blocking_ast() callback for LDLM locks acquired by server-side
+ * OBDs.
+ *
+ * \param lock the lock which blocks a request or cancelling lock
+ * \param desc unused
+ * \param data unused
+ * \param flag indicates whether this cancelling or blocking callback
+ * \retval 0
+ * \see ldlm_blocking_ast_nocheck
+ */
+int ldlm_blocking_ast(struct ldlm_lock *lock, struct ldlm_lock_desc *desc,
+ void *data, int flag)
+{
+ if (flag == LDLM_CB_CANCELING) {
+ /* Don't need to do anything here. */
+ return 0;
+ }
+
+ lock_res_and_lock(lock);
+ /* Get this: if ldlm_blocking_ast is racing with intent_policy, such
+ * that ldlm_blocking_ast is called just before intent_policy method
+ * takes the lr_lock, then by the time we get the lock, we might not
+ * be the correct blocking function anymore. So check, and return
+ * early, if so. */
+ if (lock->l_blocking_ast != ldlm_blocking_ast) {
+ unlock_res_and_lock(lock);
+ return 0;
+ }
+ return ldlm_blocking_ast_nocheck(lock);
+}
+EXPORT_SYMBOL(ldlm_blocking_ast);
+
+/**
+ * ->l_glimpse_ast() for DLM extent locks acquired on the server-side. See
+ * comment in filter_intent_policy() on why you may need this.
+ */
+int ldlm_glimpse_ast(struct ldlm_lock *lock, void *reqp)
+{
+ /*
+ * Returning -ELDLM_NO_LOCK_DATA actually works, but the reason for
+ * that is rather subtle: with OST-side locking, it may so happen that
+ * _all_ extent locks are held by the OST. If client wants to obtain
+ * current file size it calls ll{,u}_glimpse_size(), and (as locks are
+ * on the server), dummy glimpse callback fires and does
+ * nothing. Client still receives correct file size due to the
+ * following fragment in filter_intent_policy():
+ *
+ * rc = l->l_glimpse_ast(l, NULL); // this will update the LVB
+ * if (rc != 0 && res->lr_namespace->ns_lvbo &&
+ * res->lr_namespace->ns_lvbo->lvbo_update) {
+ * res->lr_namespace->ns_lvbo->lvbo_update(res, NULL, 0, 1);
+ * }
+ *
+ * that is, after glimpse_ast() fails, filter_lvbo_update() runs, and
+ * returns correct file size to the client.
+ */
+ return -ELDLM_NO_LOCK_DATA;
+}
+EXPORT_SYMBOL(ldlm_glimpse_ast);
+
+/**
+ * Enqueue a local lock (typically on a server).
+ */
+int ldlm_cli_enqueue_local(struct ldlm_namespace *ns,
+ const struct ldlm_res_id *res_id,
+ ldlm_type_t type, ldlm_policy_data_t *policy,
+ ldlm_mode_t mode, __u64 *flags,
+ ldlm_blocking_callback blocking,
+ ldlm_completion_callback completion,
+ ldlm_glimpse_callback glimpse,
+ void *data, __u32 lvb_len, enum lvb_type lvb_type,
+ const __u64 *client_cookie,
+ struct lustre_handle *lockh)
+{
+ struct ldlm_lock *lock;
+ int err;
+ const struct ldlm_callback_suite cbs = { .lcs_completion = completion,
+ .lcs_blocking = blocking,
+ .lcs_glimpse = glimpse,
+ };
+
+ LASSERT(!(*flags & LDLM_FL_REPLAY));
+ if (unlikely(ns_is_client(ns))) {
+ CERROR("Trying to enqueue local lock in a shadow namespace\n");
+ LBUG();
+ }
+
+ lock = ldlm_lock_create(ns, res_id, type, mode, &cbs, data, lvb_len,
+ lvb_type);
+ if (unlikely(!lock)) {
+ err = -ENOMEM;
+ goto out_nolock;
+ }
+
+ ldlm_lock2handle(lock, lockh);
+
+ /* NB: we don't have any lock now (lock_res_and_lock)
+ * because it's a new lock */
+ ldlm_lock_addref_internal_nolock(lock, mode);
+ lock->l_flags |= LDLM_FL_LOCAL;
+ if (*flags & LDLM_FL_ATOMIC_CB)
+ lock->l_flags |= LDLM_FL_ATOMIC_CB;
+
+ if (policy != NULL)
+ lock->l_policy_data = *policy;
+ if (client_cookie != NULL)
+ lock->l_client_cookie = *client_cookie;
+ if (type == LDLM_EXTENT)
+ lock->l_req_extent = policy->l_extent;
+
+ err = ldlm_lock_enqueue(ns, &lock, policy, flags);
+ if (unlikely(err != ELDLM_OK))
+ goto out;
+
+ if (policy != NULL)
+ *policy = lock->l_policy_data;
+
+ if (lock->l_completion_ast)
+ lock->l_completion_ast(lock, *flags, NULL);
+
+ LDLM_DEBUG(lock, "client-side local enqueue handler, new lock created");
+ out:
+ LDLM_LOCK_RELEASE(lock);
+ out_nolock:
+ return err;
+}
+EXPORT_SYMBOL(ldlm_cli_enqueue_local);
+
+static void failed_lock_cleanup(struct ldlm_namespace *ns,
+ struct ldlm_lock *lock, int mode)
+{
+ int need_cancel = 0;
+
+ /* Set a flag to prevent us from sending a CANCEL (bug 407) */
+ lock_res_and_lock(lock);
+ /* Check that lock is not granted or failed, we might race. */
+ if ((lock->l_req_mode != lock->l_granted_mode) &&
+ !(lock->l_flags & LDLM_FL_FAILED)) {
+ /* Make sure that this lock will not be found by raced
+ * bl_ast and -EINVAL reply is sent to server anyways.
+ * bug 17645 */
+ lock->l_flags |= LDLM_FL_LOCAL_ONLY | LDLM_FL_FAILED |
+ LDLM_FL_ATOMIC_CB | LDLM_FL_CBPENDING;
+ need_cancel = 1;
+ }
+ unlock_res_and_lock(lock);
+
+ if (need_cancel)
+ LDLM_DEBUG(lock,
+ "setting FL_LOCAL_ONLY | LDLM_FL_FAILED | LDLM_FL_ATOMIC_CB | LDLM_FL_CBPENDING");
+ else
+ LDLM_DEBUG(lock, "lock was granted or failed in race");
+
+ ldlm_lock_decref_internal(lock, mode);
+
+ /* XXX - HACK because we shouldn't call ldlm_lock_destroy()
+ * from llite/file.c/ll_file_flock(). */
+ /* This code makes for the fact that we do not have blocking handler on
+ * a client for flock locks. As such this is the place where we must
+ * completely kill failed locks. (interrupted and those that
+ * were waiting to be granted when server evicted us. */
+ if (lock->l_resource->lr_type == LDLM_FLOCK) {
+ lock_res_and_lock(lock);
+ ldlm_resource_unlink_lock(lock);
+ ldlm_lock_destroy_nolock(lock);
+ unlock_res_and_lock(lock);
+ }
+}
+
+/**
+ * Finishing portion of client lock enqueue code.
+ *
+ * Called after receiving reply from server.
+ */
+int ldlm_cli_enqueue_fini(struct obd_export *exp, struct ptlrpc_request *req,
+ ldlm_type_t type, __u8 with_policy, ldlm_mode_t mode,
+ __u64 *flags, void *lvb, __u32 lvb_len,
+ struct lustre_handle *lockh, int rc)
+{
+ struct ldlm_namespace *ns = exp->exp_obd->obd_namespace;
+ int is_replay = *flags & LDLM_FL_REPLAY;
+ struct ldlm_lock *lock;
+ struct ldlm_reply *reply;
+ int cleanup_phase = 1;
+ int size = 0;
+
+ lock = ldlm_handle2lock(lockh);
+ /* ldlm_cli_enqueue is holding a reference on this lock. */
+ if (!lock) {
+ LASSERT(type == LDLM_FLOCK);
+ return -ENOLCK;
+ }
+
+ LASSERTF(ergo(lvb_len != 0, lvb_len == lock->l_lvb_len),
+ "lvb_len = %d, l_lvb_len = %d\n", lvb_len, lock->l_lvb_len);
+
+ if (rc != ELDLM_OK) {
+ LASSERT(!is_replay);
+ LDLM_DEBUG(lock, "client-side enqueue END (%s)",
+ rc == ELDLM_LOCK_ABORTED ? "ABORTED" : "FAILED");
+
+ if (rc != ELDLM_LOCK_ABORTED)
+ goto cleanup;
+ }
+
+ /* Before we return, swab the reply */
+ reply = req_capsule_server_get(&req->rq_pill, &RMF_DLM_REP);
+ if (reply == NULL) {
+ rc = -EPROTO;
+ goto cleanup;
+ }
+
+ if (lvb_len != 0) {
+ LASSERT(lvb != NULL);
+
+ size = req_capsule_get_size(&req->rq_pill, &RMF_DLM_LVB,
+ RCL_SERVER);
+ if (size < 0) {
+ LDLM_ERROR(lock, "Fail to get lvb_len, rc = %d", size);
+ rc = size;
+ goto cleanup;
+ } else if (unlikely(size > lvb_len)) {
+ LDLM_ERROR(lock, "Replied LVB is larger than expectation, expected = %d, replied = %d",
+ lvb_len, size);
+ rc = -EINVAL;
+ goto cleanup;
+ }
+ }
+
+ if (rc == ELDLM_LOCK_ABORTED) {
+ if (lvb_len != 0)
+ rc = ldlm_fill_lvb(lock, &req->rq_pill, RCL_SERVER,
+ lvb, size);
+ if (rc == 0)
+ rc = ELDLM_LOCK_ABORTED;
+ goto cleanup;
+ }
+
+ /* lock enqueued on the server */
+ cleanup_phase = 0;
+
+ lock_res_and_lock(lock);
+ /* Key change rehash lock in per-export hash with new key */
+ if (exp->exp_lock_hash) {
+ /* In the function below, .hs_keycmp resolves to
+ * ldlm_export_lock_keycmp() */
+ /* coverity[overrun-buffer-val] */
+ cfs_hash_rehash_key(exp->exp_lock_hash,
+ &lock->l_remote_handle,
+ &reply->lock_handle,
+ &lock->l_exp_hash);
+ } else {
+ lock->l_remote_handle = reply->lock_handle;
+ }
+
+ *flags = ldlm_flags_from_wire(reply->lock_flags);
+ lock->l_flags |= ldlm_flags_from_wire(reply->lock_flags &
+ LDLM_INHERIT_FLAGS);
+ /* move NO_TIMEOUT flag to the lock to force ldlm_lock_match()
+ * to wait with no timeout as well */
+ lock->l_flags |= ldlm_flags_from_wire(reply->lock_flags &
+ LDLM_FL_NO_TIMEOUT);
+ unlock_res_and_lock(lock);
+
+ CDEBUG(D_INFO, "local: %p, remote cookie: %#llx, flags: 0x%llx\n",
+ lock, reply->lock_handle.cookie, *flags);
+
+ /* If enqueue returned a blocked lock but the completion handler has
+ * already run, then it fixed up the resource and we don't need to do it
+ * again. */
+ if ((*flags) & LDLM_FL_LOCK_CHANGED) {
+ int newmode = reply->lock_desc.l_req_mode;
+
+ LASSERT(!is_replay);
+ if (newmode && newmode != lock->l_req_mode) {
+ LDLM_DEBUG(lock, "server returned different mode %s",
+ ldlm_lockname[newmode]);
+ lock->l_req_mode = newmode;
+ }
+
+ if (!ldlm_res_eq(&reply->lock_desc.l_resource.lr_name,
+ &lock->l_resource->lr_name)) {
+ CDEBUG(D_INFO, "remote intent success, locking "DLDLMRES
+ " instead of "DLDLMRES"\n",
+ PLDLMRES(&reply->lock_desc.l_resource),
+ PLDLMRES(lock->l_resource));
+
+ rc = ldlm_lock_change_resource(ns, lock,
+ &reply->lock_desc.l_resource.lr_name);
+ if (rc || lock->l_resource == NULL) {
+ rc = -ENOMEM;
+ goto cleanup;
+ }
+ LDLM_DEBUG(lock, "client-side enqueue, new resource");
+ }
+ if (with_policy)
+ if (!(type == LDLM_IBITS &&
+ !(exp_connect_flags(exp) & OBD_CONNECT_IBITS)))
+ /* We assume lock type cannot change on server*/
+ ldlm_convert_policy_to_local(exp,
+ lock->l_resource->lr_type,
+ &reply->lock_desc.l_policy_data,
+ &lock->l_policy_data);
+ if (type != LDLM_PLAIN)
+ LDLM_DEBUG(lock,
+ "client-side enqueue, new policy data");
+ }
+
+ if ((*flags) & LDLM_FL_AST_SENT ||
+ /* Cancel extent locks as soon as possible on a liblustre client,
+ * because it cannot handle asynchronous ASTs robustly (see
+ * bug 7311). */
+ (LIBLUSTRE_CLIENT && type == LDLM_EXTENT)) {
+ lock_res_and_lock(lock);
+ lock->l_flags |= LDLM_FL_CBPENDING | LDLM_FL_BL_AST;
+ unlock_res_and_lock(lock);
+ LDLM_DEBUG(lock, "enqueue reply includes blocking AST");
+ }
+
+ /* If the lock has already been granted by a completion AST, don't
+ * clobber the LVB with an older one. */
+ if (lvb_len != 0) {
+ /* We must lock or a racing completion might update lvb without
+ * letting us know and we'll clobber the correct value.
+ * Cannot unlock after the check either, a that still leaves
+ * a tiny window for completion to get in */
+ lock_res_and_lock(lock);
+ if (lock->l_req_mode != lock->l_granted_mode)
+ rc = ldlm_fill_lvb(lock, &req->rq_pill, RCL_SERVER,
+ lock->l_lvb_data, size);
+ unlock_res_and_lock(lock);
+ if (rc < 0) {
+ cleanup_phase = 1;
+ goto cleanup;
+ }
+ }
+
+ if (!is_replay) {
+ rc = ldlm_lock_enqueue(ns, &lock, NULL, flags);
+ if (lock->l_completion_ast != NULL) {
+ int err = lock->l_completion_ast(lock, *flags, NULL);
+
+ if (!rc)
+ rc = err;
+ if (rc)
+ cleanup_phase = 1;
+ }
+ }
+
+ if (lvb_len && lvb != NULL) {
+ /* Copy the LVB here, and not earlier, because the completion
+ * AST (if any) can override what we got in the reply */
+ memcpy(lvb, lock->l_lvb_data, lvb_len);
+ }
+
+ LDLM_DEBUG(lock, "client-side enqueue END");
+cleanup:
+ if (cleanup_phase == 1 && rc)
+ failed_lock_cleanup(ns, lock, mode);
+ /* Put lock 2 times, the second reference is held by ldlm_cli_enqueue */
+ LDLM_LOCK_PUT(lock);
+ LDLM_LOCK_RELEASE(lock);
+ return rc;
+}
+EXPORT_SYMBOL(ldlm_cli_enqueue_fini);
+
+/**
+ * Estimate number of lock handles that would fit into request of given
+ * size. PAGE_SIZE-512 is to allow TCP/IP and LNET headers to fit into
+ * a single page on the send/receive side. XXX: 512 should be changed to
+ * more adequate value.
+ */
+static inline int ldlm_req_handles_avail(int req_size, int off)
+{
+ int avail;
+
+ avail = min_t(int, LDLM_MAXREQSIZE, PAGE_CACHE_SIZE - 512) - req_size;
+ if (likely(avail >= 0))
+ avail /= (int)sizeof(struct lustre_handle);
+ else
+ avail = 0;
+ avail += LDLM_LOCKREQ_HANDLES - off;
+
+ return avail;
+}
+
+static inline int ldlm_capsule_handles_avail(struct req_capsule *pill,
+ enum req_location loc,
+ int off)
+{
+ int size = req_capsule_msg_size(pill, loc);
+
+ return ldlm_req_handles_avail(size, off);
+}
+
+static inline int ldlm_format_handles_avail(struct obd_import *imp,
+ const struct req_format *fmt,
+ enum req_location loc, int off)
+{
+ int size = req_capsule_fmt_size(imp->imp_msg_magic, fmt, loc);
+
+ return ldlm_req_handles_avail(size, off);
+}
+
+/**
+ * Cancel LRU locks and pack them into the enqueue request. Pack there the given
+ * \a count locks in \a cancels.
+ *
+ * This is to be called by functions preparing their own requests that
+ * might contain lists of locks to cancel in addition to actual operation
+ * that needs to be performed.
+ */
+int ldlm_prep_elc_req(struct obd_export *exp, struct ptlrpc_request *req,
+ int version, int opc, int canceloff,
+ struct list_head *cancels, int count)
+{
+ struct ldlm_namespace *ns = exp->exp_obd->obd_namespace;
+ struct req_capsule *pill = &req->rq_pill;
+ struct ldlm_request *dlm = NULL;
+ int flags, avail, to_free, pack = 0;
+ LIST_HEAD(head);
+ int rc;
+
+ if (cancels == NULL)
+ cancels = &head;
+ if (ns_connect_cancelset(ns)) {
+ /* Estimate the amount of available space in the request. */
+ req_capsule_filled_sizes(pill, RCL_CLIENT);
+ avail = ldlm_capsule_handles_avail(pill, RCL_CLIENT, canceloff);
+
+ flags = ns_connect_lru_resize(ns) ?
+ LDLM_CANCEL_LRUR : LDLM_CANCEL_AGED;
+ to_free = !ns_connect_lru_resize(ns) &&
+ opc == LDLM_ENQUEUE ? 1 : 0;
+
+ /* Cancel LRU locks here _only_ if the server supports
+ * EARLY_CANCEL. Otherwise we have to send extra CANCEL
+ * RPC, which will make us slower. */
+ if (avail > count)
+ count += ldlm_cancel_lru_local(ns, cancels, to_free,
+ avail - count, 0, flags);
+ if (avail > count)
+ pack = count;
+ else
+ pack = avail;
+ req_capsule_set_size(pill, &RMF_DLM_REQ, RCL_CLIENT,
+ ldlm_request_bufsize(pack, opc));
+ }
+
+ rc = ptlrpc_request_pack(req, version, opc);
+ if (rc) {
+ ldlm_lock_list_put(cancels, l_bl_ast, count);
+ return rc;
+ }
+
+ if (ns_connect_cancelset(ns)) {
+ if (canceloff) {
+ dlm = req_capsule_client_get(pill, &RMF_DLM_REQ);
+ LASSERT(dlm);
+ /* Skip first lock handler in ldlm_request_pack(),
+ * this method will increment @lock_count according
+ * to the lock handle amount actually written to
+ * the buffer. */
+ dlm->lock_count = canceloff;
+ }
+ /* Pack into the request @pack lock handles. */
+ ldlm_cli_cancel_list(cancels, pack, req, 0);
+ /* Prepare and send separate cancel RPC for others. */
+ ldlm_cli_cancel_list(cancels, count - pack, NULL, 0);
+ } else {
+ ldlm_lock_list_put(cancels, l_bl_ast, count);
+ }
+ return 0;
+}
+EXPORT_SYMBOL(ldlm_prep_elc_req);
+
+int ldlm_prep_enqueue_req(struct obd_export *exp, struct ptlrpc_request *req,
+ struct list_head *cancels, int count)
+{
+ return ldlm_prep_elc_req(exp, req, LUSTRE_DLM_VERSION, LDLM_ENQUEUE,
+ LDLM_ENQUEUE_CANCEL_OFF, cancels, count);
+}
+EXPORT_SYMBOL(ldlm_prep_enqueue_req);
+
+struct ptlrpc_request *ldlm_enqueue_pack(struct obd_export *exp, int lvb_len)
+{
+ struct ptlrpc_request *req;
+ int rc;
+
+ req = ptlrpc_request_alloc(class_exp2cliimp(exp), &RQF_LDLM_ENQUEUE);
+ if (req == NULL)
+ return ERR_PTR(-ENOMEM);
+
+ rc = ldlm_prep_enqueue_req(exp, req, NULL, 0);
+ if (rc) {
+ ptlrpc_request_free(req);
+ return ERR_PTR(rc);
+ }
+
+ req_capsule_set_size(&req->rq_pill, &RMF_DLM_LVB, RCL_SERVER, lvb_len);
+ ptlrpc_request_set_replen(req);
+ return req;
+}
+EXPORT_SYMBOL(ldlm_enqueue_pack);
+
+/**
+ * Client-side lock enqueue.
+ *
+ * If a request has some specific initialisation it is passed in \a reqp,
+ * otherwise it is created in ldlm_cli_enqueue.
+ *
+ * Supports sync and async requests, pass \a async flag accordingly. If a
+ * request was created in ldlm_cli_enqueue and it is the async request,
+ * pass it to the caller in \a reqp.
+ */
+int ldlm_cli_enqueue(struct obd_export *exp, struct ptlrpc_request **reqp,
+ struct ldlm_enqueue_info *einfo,
+ const struct ldlm_res_id *res_id,
+ ldlm_policy_data_t const *policy, __u64 *flags,
+ void *lvb, __u32 lvb_len, enum lvb_type lvb_type,
+ struct lustre_handle *lockh, int async)
+{
+ struct ldlm_namespace *ns;
+ struct ldlm_lock *lock;
+ struct ldlm_request *body;
+ int is_replay = *flags & LDLM_FL_REPLAY;
+ int req_passed_in = 1;
+ int rc, err;
+ struct ptlrpc_request *req;
+
+ LASSERT(exp != NULL);
+
+ ns = exp->exp_obd->obd_namespace;
+
+ /* If we're replaying this lock, just check some invariants.
+ * If we're creating a new lock, get everything all setup nice. */
+ if (is_replay) {
+ lock = ldlm_handle2lock_long(lockh, 0);
+ LASSERT(lock != NULL);
+ LDLM_DEBUG(lock, "client-side enqueue START");
+ LASSERT(exp == lock->l_conn_export);
+ } else {
+ const struct ldlm_callback_suite cbs = {
+ .lcs_completion = einfo->ei_cb_cp,
+ .lcs_blocking = einfo->ei_cb_bl,
+ .lcs_glimpse = einfo->ei_cb_gl
+ };
+ lock = ldlm_lock_create(ns, res_id, einfo->ei_type,
+ einfo->ei_mode, &cbs, einfo->ei_cbdata,
+ lvb_len, lvb_type);
+ if (lock == NULL)
+ return -ENOMEM;
+ /* for the local lock, add the reference */
+ ldlm_lock_addref_internal(lock, einfo->ei_mode);
+ ldlm_lock2handle(lock, lockh);
+ if (policy != NULL)
+ lock->l_policy_data = *policy;
+
+ if (einfo->ei_type == LDLM_EXTENT)
+ lock->l_req_extent = policy->l_extent;
+ LDLM_DEBUG(lock, "client-side enqueue START, flags %llx\n",
+ *flags);
+ }
+
+ lock->l_conn_export = exp;
+ lock->l_export = NULL;
+ lock->l_blocking_ast = einfo->ei_cb_bl;
+ lock->l_flags |= (*flags & (LDLM_FL_NO_LRU | LDLM_FL_EXCL));
+
+ /* lock not sent to server yet */
+
+ if (reqp == NULL || *reqp == NULL) {
+ req = ptlrpc_request_alloc_pack(class_exp2cliimp(exp),
+ &RQF_LDLM_ENQUEUE,
+ LUSTRE_DLM_VERSION,
+ LDLM_ENQUEUE);
+ if (req == NULL) {
+ failed_lock_cleanup(ns, lock, einfo->ei_mode);
+ LDLM_LOCK_RELEASE(lock);
+ return -ENOMEM;
+ }
+ req_passed_in = 0;
+ if (reqp)
+ *reqp = req;
+ } else {
+ int len;
+
+ req = *reqp;
+ len = req_capsule_get_size(&req->rq_pill, &RMF_DLM_REQ,
+ RCL_CLIENT);
+ LASSERTF(len >= sizeof(*body), "buflen[%d] = %d, not %d\n",
+ DLM_LOCKREQ_OFF, len, (int)sizeof(*body));
+ }
+
+ /* Dump lock data into the request buffer */
+ body = req_capsule_client_get(&req->rq_pill, &RMF_DLM_REQ);
+ ldlm_lock2desc(lock, &body->lock_desc);
+ body->lock_flags = ldlm_flags_to_wire(*flags);
+ body->lock_handle[0] = *lockh;
+
+ /* Continue as normal. */
+ if (!req_passed_in) {
+ if (lvb_len > 0)
+ req_capsule_extend(&req->rq_pill,
+ &RQF_LDLM_ENQUEUE_LVB);
+ req_capsule_set_size(&req->rq_pill, &RMF_DLM_LVB, RCL_SERVER,
+ lvb_len);
+ ptlrpc_request_set_replen(req);
+ }
+
+ /*
+ * Liblustre client doesn't get extent locks, except for O_APPEND case
+ * where [0, OBD_OBJECT_EOF] lock is taken, or truncate, where
+ * [i_size, OBD_OBJECT_EOF] lock is taken.
+ */
+ LASSERT(ergo(LIBLUSTRE_CLIENT, einfo->ei_type != LDLM_EXTENT ||
+ policy->l_extent.end == OBD_OBJECT_EOF));
+
+ if (async) {
+ LASSERT(reqp != NULL);
+ return 0;
+ }
+
+ LDLM_DEBUG(lock, "sending request");
+
+ rc = ptlrpc_queue_wait(req);
+
+ err = ldlm_cli_enqueue_fini(exp, req, einfo->ei_type, policy ? 1 : 0,
+ einfo->ei_mode, flags, lvb, lvb_len,
+ lockh, rc);
+
+ /* If ldlm_cli_enqueue_fini did not find the lock, we need to free
+ * one reference that we took */
+ if (err == -ENOLCK)
+ LDLM_LOCK_RELEASE(lock);
+ else
+ rc = err;
+
+ if (!req_passed_in && req != NULL) {
+ ptlrpc_req_finished(req);
+ if (reqp)
+ *reqp = NULL;
+ }
+
+ return rc;
+}
+EXPORT_SYMBOL(ldlm_cli_enqueue);
+
+static int ldlm_cli_convert_local(struct ldlm_lock *lock, int new_mode,
+ __u32 *flags)
+{
+ struct ldlm_resource *res;
+ int rc;
+
+ if (ns_is_client(ldlm_lock_to_ns(lock))) {
+ CERROR("Trying to cancel local lock\n");
+ LBUG();
+ }
+ LDLM_DEBUG(lock, "client-side local convert");
+
+ res = ldlm_lock_convert(lock, new_mode, flags);
+ if (res) {
+ ldlm_reprocess_all(res);
+ rc = 0;
+ } else {
+ rc = LUSTRE_EDEADLK;
+ }
+ LDLM_DEBUG(lock, "client-side local convert handler END");
+ LDLM_LOCK_PUT(lock);
+ return rc;
+}
+
+/* FIXME: one of ldlm_cli_convert or the server side should reject attempted
+ * conversion of locks which are on the waiting or converting queue */
+/* Caller of this code is supposed to take care of lock readers/writers
+ accounting */
+int ldlm_cli_convert(struct lustre_handle *lockh, int new_mode, __u32 *flags)
+{
+ struct ldlm_request *body;
+ struct ldlm_reply *reply;
+ struct ldlm_lock *lock;
+ struct ldlm_resource *res;
+ struct ptlrpc_request *req;
+ int rc;
+
+ lock = ldlm_handle2lock(lockh);
+ if (!lock) {
+ LBUG();
+ return -EINVAL;
+ }
+ *flags = 0;
+
+ if (lock->l_conn_export == NULL)
+ return ldlm_cli_convert_local(lock, new_mode, flags);
+
+ LDLM_DEBUG(lock, "client-side convert");
+
+ req = ptlrpc_request_alloc_pack(class_exp2cliimp(lock->l_conn_export),
+ &RQF_LDLM_CONVERT, LUSTRE_DLM_VERSION,
+ LDLM_CONVERT);
+ if (req == NULL) {
+ LDLM_LOCK_PUT(lock);
+ return -ENOMEM;
+ }
+
+ body = req_capsule_client_get(&req->rq_pill, &RMF_DLM_REQ);
+ body->lock_handle[0] = lock->l_remote_handle;
+
+ body->lock_desc.l_req_mode = new_mode;
+ body->lock_flags = ldlm_flags_to_wire(*flags);
+
+
+ ptlrpc_request_set_replen(req);
+ rc = ptlrpc_queue_wait(req);
+ if (rc != ELDLM_OK)
+ goto out;
+
+ reply = req_capsule_server_get(&req->rq_pill, &RMF_DLM_REP);
+ if (reply == NULL) {
+ rc = -EPROTO;
+ goto out;
+ }
+
+ if (req->rq_status) {
+ rc = req->rq_status;
+ goto out;
+ }
+
+ res = ldlm_lock_convert(lock, new_mode, &reply->lock_flags);
+ if (res != NULL) {
+ ldlm_reprocess_all(res);
+ /* Go to sleep until the lock is granted. */
+ /* FIXME: or cancelled. */
+ if (lock->l_completion_ast) {
+ rc = lock->l_completion_ast(lock, LDLM_FL_WAIT_NOREPROC,
+ NULL);
+ if (rc)
+ goto out;
+ }
+ } else {
+ rc = LUSTRE_EDEADLK;
+ }
+ out:
+ LDLM_LOCK_PUT(lock);
+ ptlrpc_req_finished(req);
+ return rc;
+}
+EXPORT_SYMBOL(ldlm_cli_convert);
+
+/**
+ * Cancel locks locally.
+ * Returns:
+ * \retval LDLM_FL_LOCAL_ONLY if there is no need for a CANCEL RPC to the server
+ * \retval LDLM_FL_CANCELING otherwise;
+ * \retval LDLM_FL_BL_AST if there is a need for a separate CANCEL RPC.
+ */
+static __u64 ldlm_cli_cancel_local(struct ldlm_lock *lock)
+{
+ __u64 rc = LDLM_FL_LOCAL_ONLY;
+
+ if (lock->l_conn_export) {
+ bool local_only;
+
+ LDLM_DEBUG(lock, "client-side cancel");
+ /* Set this flag to prevent others from getting new references*/
+ lock_res_and_lock(lock);
+ lock->l_flags |= LDLM_FL_CBPENDING;
+ local_only = !!(lock->l_flags &
+ (LDLM_FL_LOCAL_ONLY|LDLM_FL_CANCEL_ON_BLOCK));
+ ldlm_cancel_callback(lock);
+ rc = (lock->l_flags & LDLM_FL_BL_AST) ?
+ LDLM_FL_BL_AST : LDLM_FL_CANCELING;
+ unlock_res_and_lock(lock);
+
+ if (local_only) {
+ CDEBUG(D_DLMTRACE, "not sending request (at caller's instruction)\n");
+ rc = LDLM_FL_LOCAL_ONLY;
+ }
+ ldlm_lock_cancel(lock);
+ } else {
+ if (ns_is_client(ldlm_lock_to_ns(lock))) {
+ LDLM_ERROR(lock, "Trying to cancel local lock");
+ LBUG();
+ }
+ LDLM_DEBUG(lock, "server-side local cancel");
+ ldlm_lock_cancel(lock);
+ ldlm_reprocess_all(lock->l_resource);
+ }
+
+ return rc;
+}
+
+/**
+ * Pack \a count locks in \a head into ldlm_request buffer of request \a req.
+ */
+static void ldlm_cancel_pack(struct ptlrpc_request *req,
+ struct list_head *head, int count)
+{
+ struct ldlm_request *dlm;
+ struct ldlm_lock *lock;
+ int max, packed = 0;
+
+ dlm = req_capsule_client_get(&req->rq_pill, &RMF_DLM_REQ);
+ LASSERT(dlm != NULL);
+
+ /* Check the room in the request buffer. */
+ max = req_capsule_get_size(&req->rq_pill, &RMF_DLM_REQ, RCL_CLIENT) -
+ sizeof(struct ldlm_request);
+ max /= sizeof(struct lustre_handle);
+ max += LDLM_LOCKREQ_HANDLES;
+ LASSERT(max >= dlm->lock_count + count);
+
+ /* XXX: it would be better to pack lock handles grouped by resource.
+ * so that the server cancel would call filter_lvbo_update() less
+ * frequently. */
+ list_for_each_entry(lock, head, l_bl_ast) {
+ if (!count--)
+ break;
+ LASSERT(lock->l_conn_export);
+ /* Pack the lock handle to the given request buffer. */
+ LDLM_DEBUG(lock, "packing");
+ dlm->lock_handle[dlm->lock_count++] = lock->l_remote_handle;
+ packed++;
+ }
+ CDEBUG(D_DLMTRACE, "%d locks packed\n", packed);
+}
+
+/**
+ * Prepare and send a batched cancel RPC. It will include \a count lock
+ * handles of locks given in \a cancels list. */
+int ldlm_cli_cancel_req(struct obd_export *exp, struct list_head *cancels,
+ int count, ldlm_cancel_flags_t flags)
+{
+ struct ptlrpc_request *req = NULL;
+ struct obd_import *imp;
+ int free, sent = 0;
+ int rc = 0;
+
+ LASSERT(exp != NULL);
+ LASSERT(count > 0);
+
+ CFS_FAIL_TIMEOUT(OBD_FAIL_LDLM_PAUSE_CANCEL, cfs_fail_val);
+
+ if (CFS_FAIL_CHECK(OBD_FAIL_LDLM_CANCEL_RACE))
+ return count;
+
+ free = ldlm_format_handles_avail(class_exp2cliimp(exp),
+ &RQF_LDLM_CANCEL, RCL_CLIENT, 0);
+ if (count > free)
+ count = free;
+
+ while (1) {
+ imp = class_exp2cliimp(exp);
+ if (imp == NULL || imp->imp_invalid) {
+ CDEBUG(D_DLMTRACE,
+ "skipping cancel on invalid import %p\n", imp);
+ return count;
+ }
+
+ req = ptlrpc_request_alloc(imp, &RQF_LDLM_CANCEL);
+ if (req == NULL) {
+ rc = -ENOMEM;
+ goto out;
+ }
+
+ req_capsule_filled_sizes(&req->rq_pill, RCL_CLIENT);
+ req_capsule_set_size(&req->rq_pill, &RMF_DLM_REQ, RCL_CLIENT,
+ ldlm_request_bufsize(count, LDLM_CANCEL));
+
+ rc = ptlrpc_request_pack(req, LUSTRE_DLM_VERSION, LDLM_CANCEL);
+ if (rc) {
+ ptlrpc_request_free(req);
+ goto out;
+ }
+
+ req->rq_request_portal = LDLM_CANCEL_REQUEST_PORTAL;
+ req->rq_reply_portal = LDLM_CANCEL_REPLY_PORTAL;
+ ptlrpc_at_set_req_timeout(req);
+
+ ldlm_cancel_pack(req, cancels, count);
+
+ ptlrpc_request_set_replen(req);
+ if (flags & LCF_ASYNC) {
+ ptlrpcd_add_req(req, PDL_POLICY_LOCAL, -1);
+ sent = count;
+ goto out;
+ } else {
+ rc = ptlrpc_queue_wait(req);
+ }
+ if (rc == LUSTRE_ESTALE) {
+ CDEBUG(D_DLMTRACE, "client/server (nid %s) out of sync -- not fatal\n",
+ libcfs_nid2str(req->rq_import->
+ imp_connection->c_peer.nid));
+ rc = 0;
+ } else if (rc == -ETIMEDOUT && /* check there was no reconnect*/
+ req->rq_import_generation == imp->imp_generation) {
+ ptlrpc_req_finished(req);
+ continue;
+ } else if (rc != ELDLM_OK) {
+ /* -ESHUTDOWN is common on umount */
+ CDEBUG_LIMIT(rc == -ESHUTDOWN ? D_DLMTRACE : D_ERROR,
+ "Got rc %d from cancel RPC: canceling anyway\n",
+ rc);
+ break;
+ }
+ sent = count;
+ break;
+ }
+
+ ptlrpc_req_finished(req);
+out:
+ return sent ? sent : rc;
+}
+EXPORT_SYMBOL(ldlm_cli_cancel_req);
+
+static inline struct ldlm_pool *ldlm_imp2pl(struct obd_import *imp)
+{
+ LASSERT(imp != NULL);
+ return &imp->imp_obd->obd_namespace->ns_pool;
+}
+
+/**
+ * Update client's OBD pool related fields with new SLV and Limit from \a req.
+ */
+int ldlm_cli_update_pool(struct ptlrpc_request *req)
+{
+ struct obd_device *obd;
+ __u64 new_slv;
+ __u32 new_limit;
+
+ if (unlikely(!req->rq_import || !req->rq_import->imp_obd ||
+ !imp_connect_lru_resize(req->rq_import))) {
+ /*
+ * Do nothing for corner cases.
+ */
+ return 0;
+ }
+
+ /* In some cases RPC may contain SLV and limit zeroed out. This
+ * is the case when server does not support LRU resize feature.
+ * This is also possible in some recovery cases when server-side
+ * reqs have no reference to the OBD export and thus access to
+ * server-side namespace is not possible. */
+ if (lustre_msg_get_slv(req->rq_repmsg) == 0 ||
+ lustre_msg_get_limit(req->rq_repmsg) == 0) {
+ DEBUG_REQ(D_HA, req,
+ "Zero SLV or Limit found (SLV: %llu, Limit: %u)",
+ lustre_msg_get_slv(req->rq_repmsg),
+ lustre_msg_get_limit(req->rq_repmsg));
+ return 0;
+ }
+
+ new_limit = lustre_msg_get_limit(req->rq_repmsg);
+ new_slv = lustre_msg_get_slv(req->rq_repmsg);
+ obd = req->rq_import->imp_obd;
+
+ /* Set new SLV and limit in OBD fields to make them accessible
+ * to the pool thread. We do not access obd_namespace and pool
+ * directly here as there is no reliable way to make sure that
+ * they are still alive at cleanup time. Evil races are possible
+ * which may cause Oops at that time. */
+ write_lock(&obd->obd_pool_lock);
+ obd->obd_pool_slv = new_slv;
+ obd->obd_pool_limit = new_limit;
+ write_unlock(&obd->obd_pool_lock);
+
+ return 0;
+}
+EXPORT_SYMBOL(ldlm_cli_update_pool);
+
+/**
+ * Client side lock cancel.
+ *
+ * Lock must not have any readers or writers by this time.
+ */
+int ldlm_cli_cancel(struct lustre_handle *lockh,
+ ldlm_cancel_flags_t cancel_flags)
+{
+ struct obd_export *exp;
+ int avail, flags, count = 1;
+ __u64 rc = 0;
+ struct ldlm_namespace *ns;
+ struct ldlm_lock *lock;
+ LIST_HEAD(cancels);
+
+ /* concurrent cancels on the same handle can happen */
+ lock = ldlm_handle2lock_long(lockh, LDLM_FL_CANCELING);
+ if (lock == NULL) {
+ LDLM_DEBUG_NOLOCK("lock is already being destroyed\n");
+ return 0;
+ }
+
+ rc = ldlm_cli_cancel_local(lock);
+ if (rc == LDLM_FL_LOCAL_ONLY || cancel_flags & LCF_LOCAL) {
+ LDLM_LOCK_RELEASE(lock);
+ return 0;
+ }
+ /* Even if the lock is marked as LDLM_FL_BL_AST, this is a LDLM_CANCEL
+ * RPC which goes to canceld portal, so we can cancel other LRU locks
+ * here and send them all as one LDLM_CANCEL RPC. */
+ LASSERT(list_empty(&lock->l_bl_ast));
+ list_add(&lock->l_bl_ast, &cancels);
+
+ exp = lock->l_conn_export;
+ if (exp_connect_cancelset(exp)) {
+ avail = ldlm_format_handles_avail(class_exp2cliimp(exp),
+ &RQF_LDLM_CANCEL,
+ RCL_CLIENT, 0);
+ LASSERT(avail > 0);
+
+ ns = ldlm_lock_to_ns(lock);
+ flags = ns_connect_lru_resize(ns) ?
+ LDLM_CANCEL_LRUR : LDLM_CANCEL_AGED;
+ count += ldlm_cancel_lru_local(ns, &cancels, 0, avail - 1,
+ LCF_BL_AST, flags);
+ }
+ ldlm_cli_cancel_list(&cancels, count, NULL, cancel_flags);
+ return 0;
+}
+EXPORT_SYMBOL(ldlm_cli_cancel);
+
+/**
+ * Locally cancel up to \a count locks in list \a cancels.
+ * Return the number of cancelled locks.
+ */
+int ldlm_cli_cancel_list_local(struct list_head *cancels, int count,
+ ldlm_cancel_flags_t flags)
+{
+ LIST_HEAD(head);
+ struct ldlm_lock *lock, *next;
+ int left = 0, bl_ast = 0;
+ __u64 rc;
+
+ left = count;
+ list_for_each_entry_safe(lock, next, cancels, l_bl_ast) {
+ if (left-- == 0)
+ break;
+
+ if (flags & LCF_LOCAL) {
+ rc = LDLM_FL_LOCAL_ONLY;
+ ldlm_lock_cancel(lock);
+ } else {
+ rc = ldlm_cli_cancel_local(lock);
+ }
+ /* Until we have compound requests and can send LDLM_CANCEL
+ * requests batched with generic RPCs, we need to send cancels
+ * with the LDLM_FL_BL_AST flag in a separate RPC from
+ * the one being generated now. */
+ if (!(flags & LCF_BL_AST) && (rc == LDLM_FL_BL_AST)) {
+ LDLM_DEBUG(lock, "Cancel lock separately");
+ list_del_init(&lock->l_bl_ast);
+ list_add(&lock->l_bl_ast, &head);
+ bl_ast++;
+ continue;
+ }
+ if (rc == LDLM_FL_LOCAL_ONLY) {
+ /* CANCEL RPC should not be sent to server. */
+ list_del_init(&lock->l_bl_ast);
+ LDLM_LOCK_RELEASE(lock);
+ count--;
+ }
+ }
+ if (bl_ast > 0) {
+ count -= bl_ast;
+ ldlm_cli_cancel_list(&head, bl_ast, NULL, 0);
+ }
+
+ return count;
+}
+EXPORT_SYMBOL(ldlm_cli_cancel_list_local);
+
+/**
+ * Cancel as many locks as possible w/o sending any RPCs (e.g. to write back
+ * dirty data, to close a file, ...) or waiting for any RPCs in-flight (e.g.
+ * readahead requests, ...)
+ */
+static ldlm_policy_res_t ldlm_cancel_no_wait_policy(struct ldlm_namespace *ns,
+ struct ldlm_lock *lock,
+ int unused, int added,
+ int count)
+{
+ ldlm_policy_res_t result = LDLM_POLICY_CANCEL_LOCK;
+ ldlm_cancel_for_recovery cb = ns->ns_cancel_for_recovery;
+
+ lock_res_and_lock(lock);
+
+ /* don't check added & count since we want to process all locks
+ * from unused list */
+ switch (lock->l_resource->lr_type) {
+ case LDLM_EXTENT:
+ case LDLM_IBITS:
+ if (cb && cb(lock))
+ break;
+ default:
+ result = LDLM_POLICY_SKIP_LOCK;
+ lock->l_flags |= LDLM_FL_SKIPPED;
+ break;
+ }
+
+ unlock_res_and_lock(lock);
+ return result;
+}
+
+/**
+ * Callback function for LRU-resize policy. Decides whether to keep
+ * \a lock in LRU for current \a LRU size \a unused, added in current
+ * scan \a added and number of locks to be preferably canceled \a count.
+ *
+ * \retval LDLM_POLICY_KEEP_LOCK keep lock in LRU in stop scanning
+ *
+ * \retval LDLM_POLICY_CANCEL_LOCK cancel lock from LRU
+ */
+static ldlm_policy_res_t ldlm_cancel_lrur_policy(struct ldlm_namespace *ns,
+ struct ldlm_lock *lock,
+ int unused, int added,
+ int count)
+{
+ unsigned long cur = cfs_time_current();
+ struct ldlm_pool *pl = &ns->ns_pool;
+ __u64 slv, lvf, lv;
+ unsigned long la;
+
+ /* Stop LRU processing when we reach past @count or have checked all
+ * locks in LRU. */
+ if (count && added >= count)
+ return LDLM_POLICY_KEEP_LOCK;
+
+ slv = ldlm_pool_get_slv(pl);
+ lvf = ldlm_pool_get_lvf(pl);
+ la = cfs_duration_sec(cfs_time_sub(cur,
+ lock->l_last_used));
+ lv = lvf * la * unused;
+
+ /* Inform pool about current CLV to see it via proc. */
+ ldlm_pool_set_clv(pl, lv);
+
+ /* Stop when SLV is not yet come from server or lv is smaller than
+ * it is. */
+ return (slv == 0 || lv < slv) ?
+ LDLM_POLICY_KEEP_LOCK : LDLM_POLICY_CANCEL_LOCK;
+}
+
+/**
+ * Callback function for proc used policy. Makes decision whether to keep
+ * \a lock in LRU for current \a LRU size \a unused, added in current scan \a
+ * added and number of locks to be preferably canceled \a count.
+ *
+ * \retval LDLM_POLICY_KEEP_LOCK keep lock in LRU in stop scanning
+ *
+ * \retval LDLM_POLICY_CANCEL_LOCK cancel lock from LRU
+ */
+static ldlm_policy_res_t ldlm_cancel_passed_policy(struct ldlm_namespace *ns,
+ struct ldlm_lock *lock,
+ int unused, int added,
+ int count)
+{
+ /* Stop LRU processing when we reach past @count or have checked all
+ * locks in LRU. */
+ return (added >= count) ?
+ LDLM_POLICY_KEEP_LOCK : LDLM_POLICY_CANCEL_LOCK;
+}
+
+/**
+ * Callback function for aged policy. Makes decision whether to keep \a lock in
+ * LRU for current LRU size \a unused, added in current scan \a added and
+ * number of locks to be preferably canceled \a count.
+ *
+ * \retval LDLM_POLICY_KEEP_LOCK keep lock in LRU in stop scanning
+ *
+ * \retval LDLM_POLICY_CANCEL_LOCK cancel lock from LRU
+ */
+static ldlm_policy_res_t ldlm_cancel_aged_policy(struct ldlm_namespace *ns,
+ struct ldlm_lock *lock,
+ int unused, int added,
+ int count)
+{
+ /* Stop LRU processing if young lock is found and we reach past count */
+ return ((added >= count) &&
+ time_before(cfs_time_current(),
+ cfs_time_add(lock->l_last_used, ns->ns_max_age))) ?
+ LDLM_POLICY_KEEP_LOCK : LDLM_POLICY_CANCEL_LOCK;
+}
+
+/**
+ * Callback function for default policy. Makes decision whether to keep \a lock
+ * in LRU for current LRU size \a unused, added in current scan \a added and
+ * number of locks to be preferably canceled \a count.
+ *
+ * \retval LDLM_POLICY_KEEP_LOCK keep lock in LRU in stop scanning
+ *
+ * \retval LDLM_POLICY_CANCEL_LOCK cancel lock from LRU
+ */
+static ldlm_policy_res_t ldlm_cancel_default_policy(struct ldlm_namespace *ns,
+ struct ldlm_lock *lock,
+ int unused, int added,
+ int count)
+{
+ /* Stop LRU processing when we reach past count or have checked all
+ * locks in LRU. */
+ return (added >= count) ?
+ LDLM_POLICY_KEEP_LOCK : LDLM_POLICY_CANCEL_LOCK;
+}
+
+typedef ldlm_policy_res_t (*ldlm_cancel_lru_policy_t)(struct ldlm_namespace *,
+ struct ldlm_lock *, int,
+ int, int);
+
+static ldlm_cancel_lru_policy_t
+ldlm_cancel_lru_policy(struct ldlm_namespace *ns, int flags)
+{
+ if (flags & LDLM_CANCEL_NO_WAIT)
+ return ldlm_cancel_no_wait_policy;
+
+ if (ns_connect_lru_resize(ns)) {
+ if (flags & LDLM_CANCEL_SHRINK)
+ /* We kill passed number of old locks. */
+ return ldlm_cancel_passed_policy;
+ else if (flags & LDLM_CANCEL_LRUR)
+ return ldlm_cancel_lrur_policy;
+ else if (flags & LDLM_CANCEL_PASSED)
+ return ldlm_cancel_passed_policy;
+ } else {
+ if (flags & LDLM_CANCEL_AGED)
+ return ldlm_cancel_aged_policy;
+ }
+
+ return ldlm_cancel_default_policy;
+}
+
+/**
+ * - Free space in LRU for \a count new locks,
+ * redundant unused locks are canceled locally;
+ * - also cancel locally unused aged locks;
+ * - do not cancel more than \a max locks;
+ * - GET the found locks and add them into the \a cancels list.
+ *
+ * A client lock can be added to the l_bl_ast list only when it is
+ * marked LDLM_FL_CANCELING. Otherwise, somebody is already doing
+ * CANCEL. There are the following use cases:
+ * ldlm_cancel_resource_local(), ldlm_cancel_lru_local() and
+ * ldlm_cli_cancel(), which check and set this flag properly. As any
+ * attempt to cancel a lock rely on this flag, l_bl_ast list is accessed
+ * later without any special locking.
+ *
+ * Calling policies for enabled LRU resize:
+ * ----------------------------------------
+ * flags & LDLM_CANCEL_LRUR - use LRU resize policy (SLV from server) to
+ * cancel not more than \a count locks;
+ *
+ * flags & LDLM_CANCEL_PASSED - cancel \a count number of old locks (located at
+ * the beginning of LRU list);
+ *
+ * flags & LDLM_CANCEL_SHRINK - cancel not more than \a count locks according to
+ * memory pressure policy function;
+ *
+ * flags & LDLM_CANCEL_AGED - cancel \a count locks according to "aged policy".
+ *
+ * flags & LDLM_CANCEL_NO_WAIT - cancel as many unused locks as possible
+ * (typically before replaying locks) w/o
+ * sending any RPCs or waiting for any
+ * outstanding RPC to complete.
+ */
+static int ldlm_prepare_lru_list(struct ldlm_namespace *ns,
+ struct list_head *cancels, int count, int max,
+ int flags)
+{
+ ldlm_cancel_lru_policy_t pf;
+ struct ldlm_lock *lock, *next;
+ int added = 0, unused, remained;
+
+ spin_lock(&ns->ns_lock);
+ unused = ns->ns_nr_unused;
+ remained = unused;
+
+ if (!ns_connect_lru_resize(ns))
+ count += unused - ns->ns_max_unused;
+
+ pf = ldlm_cancel_lru_policy(ns, flags);
+ LASSERT(pf != NULL);
+
+ while (!list_empty(&ns->ns_unused_list)) {
+ ldlm_policy_res_t result;
+
+ /* all unused locks */
+ if (remained-- <= 0)
+ break;
+
+ /* For any flags, stop scanning if @max is reached. */
+ if (max && added >= max)
+ break;
+
+ list_for_each_entry_safe(lock, next, &ns->ns_unused_list,
+ l_lru) {
+ /* No locks which got blocking requests. */
+ LASSERT(!(lock->l_flags & LDLM_FL_BL_AST));
+
+ if (flags & LDLM_CANCEL_NO_WAIT &&
+ lock->l_flags & LDLM_FL_SKIPPED)
+ /* already processed */
+ continue;
+
+ /* Somebody is already doing CANCEL. No need for this
+ * lock in LRU, do not traverse it again. */
+ if (!(lock->l_flags & LDLM_FL_CANCELING))
+ break;
+
+ ldlm_lock_remove_from_lru_nolock(lock);
+ }
+ if (&lock->l_lru == &ns->ns_unused_list)
+ break;
+
+ LDLM_LOCK_GET(lock);
+ spin_unlock(&ns->ns_lock);
+ lu_ref_add(&lock->l_reference, __func__, current);
+
+ /* Pass the lock through the policy filter and see if it
+ * should stay in LRU.
+ *
+ * Even for shrinker policy we stop scanning if
+ * we find a lock that should stay in the cache.
+ * We should take into account lock age anyway
+ * as a new lock is a valuable resource even if
+ * it has a low weight.
+ *
+ * That is, for shrinker policy we drop only
+ * old locks, but additionally choose them by
+ * their weight. Big extent locks will stay in
+ * the cache. */
+ result = pf(ns, lock, unused, added, count);
+ if (result == LDLM_POLICY_KEEP_LOCK) {
+ lu_ref_del(&lock->l_reference,
+ __func__, current);
+ LDLM_LOCK_RELEASE(lock);
+ spin_lock(&ns->ns_lock);
+ break;
+ }
+ if (result == LDLM_POLICY_SKIP_LOCK) {
+ lu_ref_del(&lock->l_reference,
+ __func__, current);
+ LDLM_LOCK_RELEASE(lock);
+ spin_lock(&ns->ns_lock);
+ continue;
+ }
+
+ lock_res_and_lock(lock);
+ /* Check flags again under the lock. */
+ if ((lock->l_flags & LDLM_FL_CANCELING) ||
+ (ldlm_lock_remove_from_lru(lock) == 0)) {
+ /* Another thread is removing lock from LRU, or
+ * somebody is already doing CANCEL, or there
+ * is a blocking request which will send cancel
+ * by itself, or the lock is no longer unused. */
+ unlock_res_and_lock(lock);
+ lu_ref_del(&lock->l_reference,
+ __func__, current);
+ LDLM_LOCK_RELEASE(lock);
+ spin_lock(&ns->ns_lock);
+ continue;
+ }
+ LASSERT(!lock->l_readers && !lock->l_writers);
+
+ /* If we have chosen to cancel this lock voluntarily, we
+ * better send cancel notification to server, so that it
+ * frees appropriate state. This might lead to a race
+ * where while we are doing cancel here, server is also
+ * silently cancelling this lock. */
+ lock->l_flags &= ~LDLM_FL_CANCEL_ON_BLOCK;
+
+ /* Setting the CBPENDING flag is a little misleading,
+ * but prevents an important race; namely, once
+ * CBPENDING is set, the lock can accumulate no more
+ * readers/writers. Since readers and writers are
+ * already zero here, ldlm_lock_decref() won't see
+ * this flag and call l_blocking_ast */
+ lock->l_flags |= LDLM_FL_CBPENDING | LDLM_FL_CANCELING;
+
+ /* We can't re-add to l_lru as it confuses the
+ * refcounting in ldlm_lock_remove_from_lru() if an AST
+ * arrives after we drop lr_lock below. We use l_bl_ast
+ * and can't use l_pending_chain as it is used both on
+ * server and client nevertheless bug 5666 says it is
+ * used only on server */
+ LASSERT(list_empty(&lock->l_bl_ast));
+ list_add(&lock->l_bl_ast, cancels);
+ unlock_res_and_lock(lock);
+ lu_ref_del(&lock->l_reference, __func__, current);
+ spin_lock(&ns->ns_lock);
+ added++;
+ unused--;
+ }
+ spin_unlock(&ns->ns_lock);
+ return added;
+}
+
+int ldlm_cancel_lru_local(struct ldlm_namespace *ns, struct list_head *cancels,
+ int count, int max, ldlm_cancel_flags_t cancel_flags,
+ int flags)
+{
+ int added;
+
+ added = ldlm_prepare_lru_list(ns, cancels, count, max, flags);
+ if (added <= 0)
+ return added;
+ return ldlm_cli_cancel_list_local(cancels, added, cancel_flags);
+}
+
+/**
+ * Cancel at least \a nr locks from given namespace LRU.
+ *
+ * When called with LCF_ASYNC the blocking callback will be handled
+ * in a thread and this function will return after the thread has been
+ * asked to call the callback. When called with LCF_ASYNC the blocking
+ * callback will be performed in this function.
+ */
+int ldlm_cancel_lru(struct ldlm_namespace *ns, int nr,
+ ldlm_cancel_flags_t cancel_flags,
+ int flags)
+{
+ LIST_HEAD(cancels);
+ int count, rc;
+
+ /* Just prepare the list of locks, do not actually cancel them yet.
+ * Locks are cancelled later in a separate thread. */
+ count = ldlm_prepare_lru_list(ns, &cancels, nr, 0, flags);
+ rc = ldlm_bl_to_thread_list(ns, NULL, &cancels, count, cancel_flags);
+ if (rc == 0)
+ return count;
+
+ return 0;
+}
+
+/**
+ * Find and cancel locally unused locks found on resource, matched to the
+ * given policy, mode. GET the found locks and add them into the \a cancels
+ * list.
+ */
+int ldlm_cancel_resource_local(struct ldlm_resource *res,
+ struct list_head *cancels,
+ ldlm_policy_data_t *policy,
+ ldlm_mode_t mode, __u64 lock_flags,
+ ldlm_cancel_flags_t cancel_flags, void *opaque)
+{
+ struct ldlm_lock *lock;
+ int count = 0;
+
+ lock_res(res);
+ list_for_each_entry(lock, &res->lr_granted, l_res_link) {
+ if (opaque != NULL && lock->l_ast_data != opaque) {
+ LDLM_ERROR(lock, "data %p doesn't match opaque %p",
+ lock->l_ast_data, opaque);
+ continue;
+ }
+
+ if (lock->l_readers || lock->l_writers)
+ continue;
+
+ /* If somebody is already doing CANCEL, or blocking AST came,
+ * skip this lock. */
+ if (lock->l_flags & LDLM_FL_BL_AST ||
+ lock->l_flags & LDLM_FL_CANCELING)
+ continue;
+
+ if (lockmode_compat(lock->l_granted_mode, mode))
+ continue;
+
+ /* If policy is given and this is IBITS lock, add to list only
+ * those locks that match by policy. */
+ if (policy && (lock->l_resource->lr_type == LDLM_IBITS) &&
+ !(lock->l_policy_data.l_inodebits.bits &
+ policy->l_inodebits.bits))
+ continue;
+
+ /* See CBPENDING comment in ldlm_cancel_lru */
+ lock->l_flags |= LDLM_FL_CBPENDING | LDLM_FL_CANCELING |
+ lock_flags;
+
+ LASSERT(list_empty(&lock->l_bl_ast));
+ list_add(&lock->l_bl_ast, cancels);
+ LDLM_LOCK_GET(lock);
+ count++;
+ }
+ unlock_res(res);
+
+ return ldlm_cli_cancel_list_local(cancels, count, cancel_flags);
+}
+EXPORT_SYMBOL(ldlm_cancel_resource_local);
+
+/**
+ * Cancel client-side locks from a list and send/prepare cancel RPCs to the
+ * server.
+ * If \a req is NULL, send CANCEL request to server with handles of locks
+ * in the \a cancels. If EARLY_CANCEL is not supported, send CANCEL requests
+ * separately per lock.
+ * If \a req is not NULL, put handles of locks in \a cancels into the request
+ * buffer at the offset \a off.
+ * Destroy \a cancels at the end.
+ */
+int ldlm_cli_cancel_list(struct list_head *cancels, int count,
+ struct ptlrpc_request *req, ldlm_cancel_flags_t flags)
+{
+ struct ldlm_lock *lock;
+ int res = 0;
+
+ if (list_empty(cancels) || count == 0)
+ return 0;
+
+ /* XXX: requests (both batched and not) could be sent in parallel.
+ * Usually it is enough to have just 1 RPC, but it is possible that
+ * there are too many locks to be cancelled in LRU or on a resource.
+ * It would also speed up the case when the server does not support
+ * the feature. */
+ while (count > 0) {
+ LASSERT(!list_empty(cancels));
+ lock = list_entry(cancels->next, struct ldlm_lock,
+ l_bl_ast);
+ LASSERT(lock->l_conn_export);
+
+ if (exp_connect_cancelset(lock->l_conn_export)) {
+ res = count;
+ if (req)
+ ldlm_cancel_pack(req, cancels, count);
+ else
+ res = ldlm_cli_cancel_req(lock->l_conn_export,
+ cancels, count,
+ flags);
+ } else {
+ res = ldlm_cli_cancel_req(lock->l_conn_export,
+ cancels, 1, flags);
+ }
+
+ if (res < 0) {
+ CDEBUG_LIMIT(res == -ESHUTDOWN ? D_DLMTRACE : D_ERROR,
+ "ldlm_cli_cancel_list: %d\n", res);
+ res = count;
+ }
+
+ count -= res;
+ ldlm_lock_list_put(cancels, l_bl_ast, res);
+ }
+ LASSERT(count == 0);
+ return 0;
+}
+EXPORT_SYMBOL(ldlm_cli_cancel_list);
+
+/**
+ * Cancel all locks on a resource that have 0 readers/writers.
+ *
+ * If flags & LDLM_FL_LOCAL_ONLY, throw the locks away without trying
+ * to notify the server. */
+int ldlm_cli_cancel_unused_resource(struct ldlm_namespace *ns,
+ const struct ldlm_res_id *res_id,
+ ldlm_policy_data_t *policy,
+ ldlm_mode_t mode,
+ ldlm_cancel_flags_t flags,
+ void *opaque)
+{
+ struct ldlm_resource *res;
+ LIST_HEAD(cancels);
+ int count;
+ int rc;
+
+ res = ldlm_resource_get(ns, NULL, res_id, 0, 0);
+ if (res == NULL) {
+ /* This is not a problem. */
+ CDEBUG(D_INFO, "No resource %llu\n", res_id->name[0]);
+ return 0;
+ }
+
+ LDLM_RESOURCE_ADDREF(res);
+ count = ldlm_cancel_resource_local(res, &cancels, policy, mode,
+ 0, flags | LCF_BL_AST, opaque);
+ rc = ldlm_cli_cancel_list(&cancels, count, NULL, flags);
+ if (rc != ELDLM_OK)
+ CERROR("canceling unused lock "DLDLMRES": rc = %d\n",
+ PLDLMRES(res), rc);
+
+ LDLM_RESOURCE_DELREF(res);
+ ldlm_resource_putref(res);
+ return 0;
+}
+EXPORT_SYMBOL(ldlm_cli_cancel_unused_resource);
+
+struct ldlm_cli_cancel_arg {
+ int lc_flags;
+ void *lc_opaque;
+};
+
+static int ldlm_cli_hash_cancel_unused(struct cfs_hash *hs,
+ struct cfs_hash_bd *bd,
+ struct hlist_node *hnode, void *arg)
+{
+ struct ldlm_resource *res = cfs_hash_object(hs, hnode);
+ struct ldlm_cli_cancel_arg *lc = arg;
+
+ ldlm_cli_cancel_unused_resource(ldlm_res_to_ns(res), &res->lr_name,
+ NULL, LCK_MINMODE,
+ lc->lc_flags, lc->lc_opaque);
+ /* must return 0 for hash iteration */
+ return 0;
+}
+
+/**
+ * Cancel all locks on a namespace (or a specific resource, if given)
+ * that have 0 readers/writers.
+ *
+ * If flags & LCF_LOCAL, throw the locks away without trying
+ * to notify the server. */
+int ldlm_cli_cancel_unused(struct ldlm_namespace *ns,
+ const struct ldlm_res_id *res_id,
+ ldlm_cancel_flags_t flags, void *opaque)
+{
+ struct ldlm_cli_cancel_arg arg = {
+ .lc_flags = flags,
+ .lc_opaque = opaque,
+ };
+
+ if (ns == NULL)
+ return ELDLM_OK;
+
+ if (res_id != NULL) {
+ return ldlm_cli_cancel_unused_resource(ns, res_id, NULL,
+ LCK_MINMODE, flags,
+ opaque);
+ } else {
+ cfs_hash_for_each_nolock(ns->ns_rs_hash,
+ ldlm_cli_hash_cancel_unused, &arg);
+ return ELDLM_OK;
+ }
+}
+EXPORT_SYMBOL(ldlm_cli_cancel_unused);
+
+/* Lock iterators. */
+
+int ldlm_resource_foreach(struct ldlm_resource *res, ldlm_iterator_t iter,
+ void *closure)
+{
+ struct list_head *tmp, *next;
+ struct ldlm_lock *lock;
+ int rc = LDLM_ITER_CONTINUE;
+
+ if (!res)
+ return LDLM_ITER_CONTINUE;
+
+ lock_res(res);
+ list_for_each_safe(tmp, next, &res->lr_granted) {
+ lock = list_entry(tmp, struct ldlm_lock, l_res_link);
+
+ if (iter(lock, closure) == LDLM_ITER_STOP) {
+ rc = LDLM_ITER_STOP;
+ goto out;
+ }
+ }
+
+ list_for_each_safe(tmp, next, &res->lr_converting) {
+ lock = list_entry(tmp, struct ldlm_lock, l_res_link);
+
+ if (iter(lock, closure) == LDLM_ITER_STOP) {
+ rc = LDLM_ITER_STOP;
+ goto out;
+ }
+ }
+
+ list_for_each_safe(tmp, next, &res->lr_waiting) {
+ lock = list_entry(tmp, struct ldlm_lock, l_res_link);
+
+ if (iter(lock, closure) == LDLM_ITER_STOP) {
+ rc = LDLM_ITER_STOP;
+ goto out;
+ }
+ }
+ out:
+ unlock_res(res);
+ return rc;
+}
+EXPORT_SYMBOL(ldlm_resource_foreach);
+
+struct iter_helper_data {
+ ldlm_iterator_t iter;
+ void *closure;
+};
+
+static int ldlm_iter_helper(struct ldlm_lock *lock, void *closure)
+{
+ struct iter_helper_data *helper = closure;
+
+ return helper->iter(lock, helper->closure);
+}
+
+static int ldlm_res_iter_helper(struct cfs_hash *hs, struct cfs_hash_bd *bd,
+ struct hlist_node *hnode, void *arg)
+
+{
+ struct ldlm_resource *res = cfs_hash_object(hs, hnode);
+
+ return ldlm_resource_foreach(res, ldlm_iter_helper, arg) ==
+ LDLM_ITER_STOP;
+}
+
+void ldlm_namespace_foreach(struct ldlm_namespace *ns,
+ ldlm_iterator_t iter, void *closure)
+
+{
+ struct iter_helper_data helper = {
+ .iter = iter,
+ .closure = closure,
+ };
+
+ cfs_hash_for_each_nolock(ns->ns_rs_hash,
+ ldlm_res_iter_helper, &helper);
+
+}
+EXPORT_SYMBOL(ldlm_namespace_foreach);
+
+/* non-blocking function to manipulate a lock whose cb_data is being put away.
+ * return 0: find no resource
+ * > 0: must be LDLM_ITER_STOP/LDLM_ITER_CONTINUE.
+ * < 0: errors
+ */
+int ldlm_resource_iterate(struct ldlm_namespace *ns,
+ const struct ldlm_res_id *res_id,
+ ldlm_iterator_t iter, void *data)
+{
+ struct ldlm_resource *res;
+ int rc;
+
+ if (ns == NULL) {
+ CERROR("must pass in namespace\n");
+ LBUG();
+ }
+
+ res = ldlm_resource_get(ns, NULL, res_id, 0, 0);
+ if (res == NULL)
+ return 0;
+
+ LDLM_RESOURCE_ADDREF(res);
+ rc = ldlm_resource_foreach(res, iter, data);
+ LDLM_RESOURCE_DELREF(res);
+ ldlm_resource_putref(res);
+ return rc;
+}
+EXPORT_SYMBOL(ldlm_resource_iterate);
+
+/* Lock replay */
+
+static int ldlm_chain_lock_for_replay(struct ldlm_lock *lock, void *closure)
+{
+ struct list_head *list = closure;
+
+ /* we use l_pending_chain here, because it's unused on clients. */
+ LASSERTF(list_empty(&lock->l_pending_chain),
+ "lock %p next %p prev %p\n",
+ lock, &lock->l_pending_chain.next,
+ &lock->l_pending_chain.prev);
+ /* bug 9573: don't replay locks left after eviction, or
+ * bug 17614: locks being actively cancelled. Get a reference
+ * on a lock so that it does not disappear under us (e.g. due to cancel)
+ */
+ if (!(lock->l_flags & (LDLM_FL_FAILED|LDLM_FL_CANCELING))) {
+ list_add(&lock->l_pending_chain, list);
+ LDLM_LOCK_GET(lock);
+ }
+
+ return LDLM_ITER_CONTINUE;
+}
+
+static int replay_lock_interpret(const struct lu_env *env,
+ struct ptlrpc_request *req,
+ struct ldlm_async_args *aa, int rc)
+{
+ struct ldlm_lock *lock;
+ struct ldlm_reply *reply;
+ struct obd_export *exp;
+
+ atomic_dec(&req->rq_import->imp_replay_inflight);
+ if (rc != ELDLM_OK)
+ goto out;
+
+
+ reply = req_capsule_server_get(&req->rq_pill, &RMF_DLM_REP);
+ if (reply == NULL) {
+ rc = -EPROTO;
+ goto out;
+ }
+
+ lock = ldlm_handle2lock(&aa->lock_handle);
+ if (!lock) {
+ CERROR("received replay ack for unknown local cookie %#llx remote cookie %#llx from server %s id %s\n",
+ aa->lock_handle.cookie, reply->lock_handle.cookie,
+ req->rq_export->exp_client_uuid.uuid,
+ libcfs_id2str(req->rq_peer));
+ rc = -ESTALE;
+ goto out;
+ }
+
+ /* Key change rehash lock in per-export hash with new key */
+ exp = req->rq_export;
+ if (exp && exp->exp_lock_hash) {
+ /* In the function below, .hs_keycmp resolves to
+ * ldlm_export_lock_keycmp() */
+ /* coverity[overrun-buffer-val] */
+ cfs_hash_rehash_key(exp->exp_lock_hash,
+ &lock->l_remote_handle,
+ &reply->lock_handle,
+ &lock->l_exp_hash);
+ } else {
+ lock->l_remote_handle = reply->lock_handle;
+ }
+
+ LDLM_DEBUG(lock, "replayed lock:");
+ ptlrpc_import_recovery_state_machine(req->rq_import);
+ LDLM_LOCK_PUT(lock);
+out:
+ if (rc != ELDLM_OK)
+ ptlrpc_connect_import(req->rq_import);
+
+ return rc;
+}
+
+static int replay_one_lock(struct obd_import *imp, struct ldlm_lock *lock)
+{
+ struct ptlrpc_request *req;
+ struct ldlm_async_args *aa;
+ struct ldlm_request *body;
+ int flags;
+
+ /* Bug 11974: Do not replay a lock which is actively being canceled */
+ if (lock->l_flags & LDLM_FL_CANCELING) {
+ LDLM_DEBUG(lock, "Not replaying canceled lock:");
+ return 0;
+ }
+
+ /* If this is reply-less callback lock, we cannot replay it, since
+ * server might have long dropped it, but notification of that event was
+ * lost by network. (and server granted conflicting lock already) */
+ if (lock->l_flags & LDLM_FL_CANCEL_ON_BLOCK) {
+ LDLM_DEBUG(lock, "Not replaying reply-less lock:");
+ ldlm_lock_cancel(lock);
+ return 0;
+ }
+
+ /*
+ * If granted mode matches the requested mode, this lock is granted.
+ *
+ * If they differ, but we have a granted mode, then we were granted
+ * one mode and now want another: ergo, converting.
+ *
+ * If we haven't been granted anything and are on a resource list,
+ * then we're blocked/waiting.
+ *
+ * If we haven't been granted anything and we're NOT on a resource list,
+ * then we haven't got a reply yet and don't have a known disposition.
+ * This happens whenever a lock enqueue is the request that triggers
+ * recovery.
+ */
+ if (lock->l_granted_mode == lock->l_req_mode)
+ flags = LDLM_FL_REPLAY | LDLM_FL_BLOCK_GRANTED;
+ else if (lock->l_granted_mode)
+ flags = LDLM_FL_REPLAY | LDLM_FL_BLOCK_CONV;
+ else if (!list_empty(&lock->l_res_link))
+ flags = LDLM_FL_REPLAY | LDLM_FL_BLOCK_WAIT;
+ else
+ flags = LDLM_FL_REPLAY;
+
+ req = ptlrpc_request_alloc_pack(imp, &RQF_LDLM_ENQUEUE,
+ LUSTRE_DLM_VERSION, LDLM_ENQUEUE);
+ if (req == NULL)
+ return -ENOMEM;
+
+ /* We're part of recovery, so don't wait for it. */
+ req->rq_send_state = LUSTRE_IMP_REPLAY_LOCKS;
+
+ body = req_capsule_client_get(&req->rq_pill, &RMF_DLM_REQ);
+ ldlm_lock2desc(lock, &body->lock_desc);
+ body->lock_flags = ldlm_flags_to_wire(flags);
+
+ ldlm_lock2handle(lock, &body->lock_handle[0]);
+ if (lock->l_lvb_len > 0)
+ req_capsule_extend(&req->rq_pill, &RQF_LDLM_ENQUEUE_LVB);
+ req_capsule_set_size(&req->rq_pill, &RMF_DLM_LVB, RCL_SERVER,
+ lock->l_lvb_len);
+ ptlrpc_request_set_replen(req);
+ /* notify the server we've replayed all requests.
+ * also, we mark the request to be put on a dedicated
+ * queue to be processed after all request replayes.
+ * bug 6063 */
+ lustre_msg_set_flags(req->rq_reqmsg, MSG_REQ_REPLAY_DONE);
+
+ LDLM_DEBUG(lock, "replaying lock:");
+
+ atomic_inc(&req->rq_import->imp_replay_inflight);
+ CLASSERT(sizeof(*aa) <= sizeof(req->rq_async_args));
+ aa = ptlrpc_req_async_args(req);
+ aa->lock_handle = body->lock_handle[0];
+ req->rq_interpret_reply = (ptlrpc_interpterer_t)replay_lock_interpret;
+ ptlrpcd_add_req(req, PDL_POLICY_LOCAL, -1);
+
+ return 0;
+}
+
+/**
+ * Cancel as many unused locks as possible before replay. since we are
+ * in recovery, we can't wait for any outstanding RPCs to send any RPC
+ * to the server.
+ *
+ * Called only in recovery before replaying locks. there is no need to
+ * replay locks that are unused. since the clients may hold thousands of
+ * cached unused locks, dropping the unused locks can greatly reduce the
+ * load on the servers at recovery time.
+ */
+static void ldlm_cancel_unused_locks_for_replay(struct ldlm_namespace *ns)
+{
+ int canceled;
+ LIST_HEAD(cancels);
+
+ CDEBUG(D_DLMTRACE, "Dropping as many unused locks as possible before replay for namespace %s (%d)\n",
+ ldlm_ns_name(ns), ns->ns_nr_unused);
+
+ /* We don't need to care whether or not LRU resize is enabled
+ * because the LDLM_CANCEL_NO_WAIT policy doesn't use the
+ * count parameter */
+ canceled = ldlm_cancel_lru_local(ns, &cancels, ns->ns_nr_unused, 0,
+ LCF_LOCAL, LDLM_CANCEL_NO_WAIT);
+
+ CDEBUG(D_DLMTRACE, "Canceled %d unused locks from namespace %s\n",
+ canceled, ldlm_ns_name(ns));
+}
+
+int ldlm_replay_locks(struct obd_import *imp)
+{
+ struct ldlm_namespace *ns = imp->imp_obd->obd_namespace;
+ LIST_HEAD(list);
+ struct ldlm_lock *lock, *next;
+ int rc = 0;
+
+ LASSERT(atomic_read(&imp->imp_replay_inflight) == 0);
+
+ /* don't replay locks if import failed recovery */
+ if (imp->imp_vbr_failed)
+ return 0;
+
+ /* ensure this doesn't fall to 0 before all have been queued */
+ atomic_inc(&imp->imp_replay_inflight);
+
+ if (ldlm_cancel_unused_locks_before_replay)
+ ldlm_cancel_unused_locks_for_replay(ns);
+
+ ldlm_namespace_foreach(ns, ldlm_chain_lock_for_replay, &list);
+
+ list_for_each_entry_safe(lock, next, &list, l_pending_chain) {
+ list_del_init(&lock->l_pending_chain);
+ if (rc) {
+ LDLM_LOCK_RELEASE(lock);
+ continue; /* or try to do the rest? */
+ }
+ rc = replay_one_lock(imp, lock);
+ LDLM_LOCK_RELEASE(lock);
+ }
+
+ atomic_dec(&imp->imp_replay_inflight);
+
+ return rc;
+}
+EXPORT_SYMBOL(ldlm_replay_locks);
diff --git a/drivers/staging/lustre/lustre/ldlm/ldlm_resource.c b/drivers/staging/lustre/lustre/ldlm/ldlm_resource.c
new file mode 100644
index 000000000..f750d42a7
--- /dev/null
+++ b/drivers/staging/lustre/lustre/ldlm/ldlm_resource.c
@@ -0,0 +1,1425 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2002, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2010, 2012, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ *
+ * lustre/ldlm/ldlm_resource.c
+ *
+ * Author: Phil Schwan <phil@clusterfs.com>
+ * Author: Peter Braam <braam@clusterfs.com>
+ */
+
+#define DEBUG_SUBSYSTEM S_LDLM
+#include "../include/lustre_dlm.h"
+#include "../include/lustre_fid.h"
+#include "../include/obd_class.h"
+#include "ldlm_internal.h"
+
+struct kmem_cache *ldlm_resource_slab, *ldlm_lock_slab;
+
+int ldlm_srv_namespace_nr = 0;
+int ldlm_cli_namespace_nr = 0;
+
+struct mutex ldlm_srv_namespace_lock;
+LIST_HEAD(ldlm_srv_namespace_list);
+
+struct mutex ldlm_cli_namespace_lock;
+/* Client Namespaces that have active resources in them.
+ * Once all resources go away, ldlm_poold moves such namespaces to the
+ * inactive list */
+LIST_HEAD(ldlm_cli_active_namespace_list);
+/* Client namespaces that don't have any locks in them */
+LIST_HEAD(ldlm_cli_inactive_namespace_list);
+
+struct proc_dir_entry *ldlm_type_proc_dir = NULL;
+static struct proc_dir_entry *ldlm_ns_proc_dir = NULL;
+struct proc_dir_entry *ldlm_svc_proc_dir = NULL;
+
+extern unsigned int ldlm_cancel_unused_locks_before_replay;
+
+/* during debug dump certain amount of granted locks for one resource to avoid
+ * DDOS. */
+unsigned int ldlm_dump_granted_max = 256;
+
+#if defined(CONFIG_PROC_FS)
+static ssize_t lprocfs_wr_dump_ns(struct file *file, const char __user *buffer,
+ size_t count, loff_t *off)
+{
+ ldlm_dump_all_namespaces(LDLM_NAMESPACE_SERVER, D_DLMTRACE);
+ ldlm_dump_all_namespaces(LDLM_NAMESPACE_CLIENT, D_DLMTRACE);
+ return count;
+}
+LPROC_SEQ_FOPS_WR_ONLY(ldlm, dump_ns);
+
+LPROC_SEQ_FOPS_RW_TYPE(ldlm_rw, uint);
+LPROC_SEQ_FOPS_RO_TYPE(ldlm, uint);
+
+int ldlm_proc_setup(void)
+{
+ int rc;
+ struct lprocfs_vars list[] = {
+ { "dump_namespaces", &ldlm_dump_ns_fops, NULL, 0222 },
+ { "dump_granted_max", &ldlm_rw_uint_fops,
+ &ldlm_dump_granted_max },
+ { "cancel_unused_locks_before_replay", &ldlm_rw_uint_fops,
+ &ldlm_cancel_unused_locks_before_replay },
+ { NULL } };
+ LASSERT(ldlm_ns_proc_dir == NULL);
+
+ ldlm_type_proc_dir = lprocfs_register(OBD_LDLM_DEVICENAME,
+ proc_lustre_root,
+ NULL, NULL);
+ if (IS_ERR(ldlm_type_proc_dir)) {
+ CERROR("LProcFS failed in ldlm-init\n");
+ rc = PTR_ERR(ldlm_type_proc_dir);
+ goto err;
+ }
+
+ ldlm_ns_proc_dir = lprocfs_register("namespaces",
+ ldlm_type_proc_dir,
+ NULL, NULL);
+ if (IS_ERR(ldlm_ns_proc_dir)) {
+ CERROR("LProcFS failed in ldlm-init\n");
+ rc = PTR_ERR(ldlm_ns_proc_dir);
+ goto err_type;
+ }
+
+ ldlm_svc_proc_dir = lprocfs_register("services",
+ ldlm_type_proc_dir,
+ NULL, NULL);
+ if (IS_ERR(ldlm_svc_proc_dir)) {
+ CERROR("LProcFS failed in ldlm-init\n");
+ rc = PTR_ERR(ldlm_svc_proc_dir);
+ goto err_ns;
+ }
+
+ rc = lprocfs_add_vars(ldlm_type_proc_dir, list, NULL);
+
+ return 0;
+
+err_ns:
+ lprocfs_remove(&ldlm_ns_proc_dir);
+err_type:
+ lprocfs_remove(&ldlm_type_proc_dir);
+err:
+ ldlm_svc_proc_dir = NULL;
+ ldlm_type_proc_dir = NULL;
+ ldlm_ns_proc_dir = NULL;
+ return rc;
+}
+
+void ldlm_proc_cleanup(void)
+{
+ if (ldlm_svc_proc_dir)
+ lprocfs_remove(&ldlm_svc_proc_dir);
+
+ if (ldlm_ns_proc_dir)
+ lprocfs_remove(&ldlm_ns_proc_dir);
+
+ if (ldlm_type_proc_dir)
+ lprocfs_remove(&ldlm_type_proc_dir);
+
+ ldlm_svc_proc_dir = NULL;
+ ldlm_type_proc_dir = NULL;
+ ldlm_ns_proc_dir = NULL;
+}
+
+static int lprocfs_ns_resources_seq_show(struct seq_file *m, void *v)
+{
+ struct ldlm_namespace *ns = m->private;
+ __u64 res = 0;
+ struct cfs_hash_bd bd;
+ int i;
+
+ /* result is not strictly consistent */
+ cfs_hash_for_each_bucket(ns->ns_rs_hash, &bd, i)
+ res += cfs_hash_bd_count_get(&bd);
+ return lprocfs_rd_u64(m, &res);
+}
+LPROC_SEQ_FOPS_RO(lprocfs_ns_resources);
+
+static int lprocfs_ns_locks_seq_show(struct seq_file *m, void *v)
+{
+ struct ldlm_namespace *ns = m->private;
+ __u64 locks;
+
+ locks = lprocfs_stats_collector(ns->ns_stats, LDLM_NSS_LOCKS,
+ LPROCFS_FIELDS_FLAGS_SUM);
+ return lprocfs_rd_u64(m, &locks);
+}
+LPROC_SEQ_FOPS_RO(lprocfs_ns_locks);
+
+static int lprocfs_lru_size_seq_show(struct seq_file *m, void *v)
+{
+ struct ldlm_namespace *ns = m->private;
+ __u32 *nr = &ns->ns_max_unused;
+
+ if (ns_connect_lru_resize(ns))
+ nr = &ns->ns_nr_unused;
+ return lprocfs_rd_uint(m, nr);
+}
+
+static ssize_t lprocfs_lru_size_seq_write(struct file *file,
+ const char __user *buffer,
+ size_t count, loff_t *off)
+{
+ struct ldlm_namespace *ns = ((struct seq_file *)file->private_data)->private;
+ char dummy[MAX_STRING_SIZE + 1];
+ unsigned long tmp;
+ int lru_resize;
+ int err;
+
+ dummy[MAX_STRING_SIZE] = '\0';
+ if (copy_from_user(dummy, buffer, MAX_STRING_SIZE))
+ return -EFAULT;
+
+ if (strncmp(dummy, "clear", 5) == 0) {
+ CDEBUG(D_DLMTRACE,
+ "dropping all unused locks from namespace %s\n",
+ ldlm_ns_name(ns));
+ if (ns_connect_lru_resize(ns)) {
+ int canceled, unused = ns->ns_nr_unused;
+
+ /* Try to cancel all @ns_nr_unused locks. */
+ canceled = ldlm_cancel_lru(ns, unused, 0,
+ LDLM_CANCEL_PASSED);
+ if (canceled < unused) {
+ CDEBUG(D_DLMTRACE,
+ "not all requested locks are canceled, requested: %d, canceled: %d\n",
+ unused,
+ canceled);
+ return -EINVAL;
+ }
+ } else {
+ tmp = ns->ns_max_unused;
+ ns->ns_max_unused = 0;
+ ldlm_cancel_lru(ns, 0, 0, LDLM_CANCEL_PASSED);
+ ns->ns_max_unused = tmp;
+ }
+ return count;
+ }
+
+ err = kstrtoul(dummy, 10, &tmp);
+ if (err != 0) {
+ CERROR("invalid value written\n");
+ return -EINVAL;
+ }
+ lru_resize = (tmp == 0);
+
+ if (ns_connect_lru_resize(ns)) {
+ if (!lru_resize)
+ ns->ns_max_unused = (unsigned int)tmp;
+
+ if (tmp > ns->ns_nr_unused)
+ tmp = ns->ns_nr_unused;
+ tmp = ns->ns_nr_unused - tmp;
+
+ CDEBUG(D_DLMTRACE,
+ "changing namespace %s unused locks from %u to %u\n",
+ ldlm_ns_name(ns), ns->ns_nr_unused,
+ (unsigned int)tmp);
+ ldlm_cancel_lru(ns, tmp, LCF_ASYNC, LDLM_CANCEL_PASSED);
+
+ if (!lru_resize) {
+ CDEBUG(D_DLMTRACE,
+ "disable lru_resize for namespace %s\n",
+ ldlm_ns_name(ns));
+ ns->ns_connect_flags &= ~OBD_CONNECT_LRU_RESIZE;
+ }
+ } else {
+ CDEBUG(D_DLMTRACE,
+ "changing namespace %s max_unused from %u to %u\n",
+ ldlm_ns_name(ns), ns->ns_max_unused,
+ (unsigned int)tmp);
+ ns->ns_max_unused = (unsigned int)tmp;
+ ldlm_cancel_lru(ns, 0, LCF_ASYNC, LDLM_CANCEL_PASSED);
+
+ /* Make sure that LRU resize was originally supported before
+ * turning it on here. */
+ if (lru_resize &&
+ (ns->ns_orig_connect_flags & OBD_CONNECT_LRU_RESIZE)) {
+ CDEBUG(D_DLMTRACE,
+ "enable lru_resize for namespace %s\n",
+ ldlm_ns_name(ns));
+ ns->ns_connect_flags |= OBD_CONNECT_LRU_RESIZE;
+ }
+ }
+
+ return count;
+}
+LPROC_SEQ_FOPS(lprocfs_lru_size);
+
+static int lprocfs_elc_seq_show(struct seq_file *m, void *v)
+{
+ struct ldlm_namespace *ns = m->private;
+ unsigned int supp = ns_connect_cancelset(ns);
+
+ return lprocfs_rd_uint(m, &supp);
+}
+
+static ssize_t lprocfs_elc_seq_write(struct file *file,
+ const char __user *buffer,
+ size_t count, loff_t *off)
+{
+ struct ldlm_namespace *ns = ((struct seq_file *)file->private_data)->private;
+ unsigned int supp = -1;
+ int rc;
+
+ rc = lprocfs_wr_uint(file, buffer, count, &supp);
+ if (rc < 0)
+ return rc;
+
+ if (supp == 0)
+ ns->ns_connect_flags &= ~OBD_CONNECT_CANCELSET;
+ else if (ns->ns_orig_connect_flags & OBD_CONNECT_CANCELSET)
+ ns->ns_connect_flags |= OBD_CONNECT_CANCELSET;
+ return count;
+}
+LPROC_SEQ_FOPS(lprocfs_elc);
+
+void ldlm_namespace_proc_unregister(struct ldlm_namespace *ns)
+{
+ if (ns->ns_proc_dir_entry == NULL)
+ CERROR("dlm namespace %s has no procfs dir?\n",
+ ldlm_ns_name(ns));
+ else
+ lprocfs_remove(&ns->ns_proc_dir_entry);
+
+ if (ns->ns_stats != NULL)
+ lprocfs_free_stats(&ns->ns_stats);
+}
+
+#define LDLM_NS_ADD_VAR(name, var, ops) \
+ do { \
+ snprintf(lock_name, MAX_STRING_SIZE, name); \
+ lock_vars[0].data = var; \
+ lock_vars[0].fops = ops; \
+ lprocfs_add_vars(ns_pde, lock_vars, NULL); \
+ } while (0)
+
+int ldlm_namespace_proc_register(struct ldlm_namespace *ns)
+{
+ struct lprocfs_vars lock_vars[2];
+ char lock_name[MAX_STRING_SIZE + 1];
+ struct proc_dir_entry *ns_pde;
+
+ LASSERT(ns != NULL);
+ LASSERT(ns->ns_rs_hash != NULL);
+
+ if (ns->ns_proc_dir_entry != NULL) {
+ ns_pde = ns->ns_proc_dir_entry;
+ } else {
+ ns_pde = proc_mkdir(ldlm_ns_name(ns), ldlm_ns_proc_dir);
+ if (ns_pde == NULL)
+ return -ENOMEM;
+ ns->ns_proc_dir_entry = ns_pde;
+ }
+
+ ns->ns_stats = lprocfs_alloc_stats(LDLM_NSS_LAST, 0);
+ if (ns->ns_stats == NULL)
+ return -ENOMEM;
+
+ lprocfs_counter_init(ns->ns_stats, LDLM_NSS_LOCKS,
+ LPROCFS_CNTR_AVGMINMAX, "locks", "locks");
+
+ lock_name[MAX_STRING_SIZE] = '\0';
+
+ memset(lock_vars, 0, sizeof(lock_vars));
+ lock_vars[0].name = lock_name;
+
+ LDLM_NS_ADD_VAR("resource_count", ns, &lprocfs_ns_resources_fops);
+ LDLM_NS_ADD_VAR("lock_count", ns, &lprocfs_ns_locks_fops);
+
+ if (ns_is_client(ns)) {
+ LDLM_NS_ADD_VAR("lock_unused_count", &ns->ns_nr_unused,
+ &ldlm_uint_fops);
+ LDLM_NS_ADD_VAR("lru_size", ns, &lprocfs_lru_size_fops);
+ LDLM_NS_ADD_VAR("lru_max_age", &ns->ns_max_age,
+ &ldlm_rw_uint_fops);
+ LDLM_NS_ADD_VAR("early_lock_cancel", ns, &lprocfs_elc_fops);
+ } else {
+ LDLM_NS_ADD_VAR("ctime_age_limit", &ns->ns_ctime_age_limit,
+ &ldlm_rw_uint_fops);
+ LDLM_NS_ADD_VAR("lock_timeouts", &ns->ns_timeouts,
+ &ldlm_uint_fops);
+ LDLM_NS_ADD_VAR("max_nolock_bytes", &ns->ns_max_nolock_size,
+ &ldlm_rw_uint_fops);
+ LDLM_NS_ADD_VAR("contention_seconds", &ns->ns_contention_time,
+ &ldlm_rw_uint_fops);
+ LDLM_NS_ADD_VAR("contended_locks", &ns->ns_contended_locks,
+ &ldlm_rw_uint_fops);
+ LDLM_NS_ADD_VAR("max_parallel_ast", &ns->ns_max_parallel_ast,
+ &ldlm_rw_uint_fops);
+ }
+ return 0;
+}
+#undef MAX_STRING_SIZE
+#else /* CONFIG_PROC_FS */
+
+#define ldlm_namespace_proc_unregister(ns) ({; })
+#define ldlm_namespace_proc_register(ns) ({0; })
+
+#endif /* CONFIG_PROC_FS */
+
+static unsigned ldlm_res_hop_hash(struct cfs_hash *hs,
+ const void *key, unsigned mask)
+{
+ const struct ldlm_res_id *id = key;
+ unsigned val = 0;
+ unsigned i;
+
+ for (i = 0; i < RES_NAME_SIZE; i++)
+ val += id->name[i];
+ return val & mask;
+}
+
+static unsigned ldlm_res_hop_fid_hash(struct cfs_hash *hs,
+ const void *key, unsigned mask)
+{
+ const struct ldlm_res_id *id = key;
+ struct lu_fid fid;
+ __u32 hash;
+ __u32 val;
+
+ fid.f_seq = id->name[LUSTRE_RES_ID_SEQ_OFF];
+ fid.f_oid = (__u32)id->name[LUSTRE_RES_ID_VER_OID_OFF];
+ fid.f_ver = (__u32)(id->name[LUSTRE_RES_ID_VER_OID_OFF] >> 32);
+
+ hash = fid_flatten32(&fid);
+ hash += (hash >> 4) + (hash << 12); /* mixing oid and seq */
+ if (id->name[LUSTRE_RES_ID_HSH_OFF] != 0) {
+ val = id->name[LUSTRE_RES_ID_HSH_OFF];
+ hash += (val >> 5) + (val << 11);
+ } else {
+ val = fid_oid(&fid);
+ }
+ hash = hash_long(hash, hs->hs_bkt_bits);
+ /* give me another random factor */
+ hash -= hash_long((unsigned long)hs, val % 11 + 3);
+
+ hash <<= hs->hs_cur_bits - hs->hs_bkt_bits;
+ hash |= ldlm_res_hop_hash(hs, key, CFS_HASH_NBKT(hs) - 1);
+
+ return hash & mask;
+}
+
+static void *ldlm_res_hop_key(struct hlist_node *hnode)
+{
+ struct ldlm_resource *res;
+
+ res = hlist_entry(hnode, struct ldlm_resource, lr_hash);
+ return &res->lr_name;
+}
+
+static int ldlm_res_hop_keycmp(const void *key, struct hlist_node *hnode)
+{
+ struct ldlm_resource *res;
+
+ res = hlist_entry(hnode, struct ldlm_resource, lr_hash);
+ return ldlm_res_eq((const struct ldlm_res_id *)key,
+ (const struct ldlm_res_id *)&res->lr_name);
+}
+
+static void *ldlm_res_hop_object(struct hlist_node *hnode)
+{
+ return hlist_entry(hnode, struct ldlm_resource, lr_hash);
+}
+
+static void ldlm_res_hop_get_locked(struct cfs_hash *hs,
+ struct hlist_node *hnode)
+{
+ struct ldlm_resource *res;
+
+ res = hlist_entry(hnode, struct ldlm_resource, lr_hash);
+ ldlm_resource_getref(res);
+}
+
+static void ldlm_res_hop_put_locked(struct cfs_hash *hs,
+ struct hlist_node *hnode)
+{
+ struct ldlm_resource *res;
+
+ res = hlist_entry(hnode, struct ldlm_resource, lr_hash);
+ /* cfs_hash_for_each_nolock is the only chance we call it */
+ ldlm_resource_putref_locked(res);
+}
+
+static void ldlm_res_hop_put(struct cfs_hash *hs, struct hlist_node *hnode)
+{
+ struct ldlm_resource *res;
+
+ res = hlist_entry(hnode, struct ldlm_resource, lr_hash);
+ ldlm_resource_putref(res);
+}
+
+cfs_hash_ops_t ldlm_ns_hash_ops = {
+ .hs_hash = ldlm_res_hop_hash,
+ .hs_key = ldlm_res_hop_key,
+ .hs_keycmp = ldlm_res_hop_keycmp,
+ .hs_keycpy = NULL,
+ .hs_object = ldlm_res_hop_object,
+ .hs_get = ldlm_res_hop_get_locked,
+ .hs_put_locked = ldlm_res_hop_put_locked,
+ .hs_put = ldlm_res_hop_put
+};
+
+cfs_hash_ops_t ldlm_ns_fid_hash_ops = {
+ .hs_hash = ldlm_res_hop_fid_hash,
+ .hs_key = ldlm_res_hop_key,
+ .hs_keycmp = ldlm_res_hop_keycmp,
+ .hs_keycpy = NULL,
+ .hs_object = ldlm_res_hop_object,
+ .hs_get = ldlm_res_hop_get_locked,
+ .hs_put_locked = ldlm_res_hop_put_locked,
+ .hs_put = ldlm_res_hop_put
+};
+
+struct ldlm_ns_hash_def {
+ ldlm_ns_type_t nsd_type;
+ /** hash bucket bits */
+ unsigned nsd_bkt_bits;
+ /** hash bits */
+ unsigned nsd_all_bits;
+ /** hash operations */
+ cfs_hash_ops_t *nsd_hops;
+};
+
+struct ldlm_ns_hash_def ldlm_ns_hash_defs[] = {
+ {
+ .nsd_type = LDLM_NS_TYPE_MDC,
+ .nsd_bkt_bits = 11,
+ .nsd_all_bits = 16,
+ .nsd_hops = &ldlm_ns_fid_hash_ops,
+ },
+ {
+ .nsd_type = LDLM_NS_TYPE_MDT,
+ .nsd_bkt_bits = 14,
+ .nsd_all_bits = 21,
+ .nsd_hops = &ldlm_ns_fid_hash_ops,
+ },
+ {
+ .nsd_type = LDLM_NS_TYPE_OSC,
+ .nsd_bkt_bits = 8,
+ .nsd_all_bits = 12,
+ .nsd_hops = &ldlm_ns_hash_ops,
+ },
+ {
+ .nsd_type = LDLM_NS_TYPE_OST,
+ .nsd_bkt_bits = 11,
+ .nsd_all_bits = 17,
+ .nsd_hops = &ldlm_ns_hash_ops,
+ },
+ {
+ .nsd_type = LDLM_NS_TYPE_MGC,
+ .nsd_bkt_bits = 4,
+ .nsd_all_bits = 4,
+ .nsd_hops = &ldlm_ns_hash_ops,
+ },
+ {
+ .nsd_type = LDLM_NS_TYPE_MGT,
+ .nsd_bkt_bits = 4,
+ .nsd_all_bits = 4,
+ .nsd_hops = &ldlm_ns_hash_ops,
+ },
+ {
+ .nsd_type = LDLM_NS_TYPE_UNKNOWN,
+ },
+};
+
+/**
+ * Create and initialize new empty namespace.
+ */
+struct ldlm_namespace *ldlm_namespace_new(struct obd_device *obd, char *name,
+ ldlm_side_t client,
+ ldlm_appetite_t apt,
+ ldlm_ns_type_t ns_type)
+{
+ struct ldlm_namespace *ns = NULL;
+ struct ldlm_ns_bucket *nsb;
+ struct ldlm_ns_hash_def *nsd;
+ struct cfs_hash_bd bd;
+ int idx;
+ int rc;
+
+ LASSERT(obd != NULL);
+
+ rc = ldlm_get_ref();
+ if (rc) {
+ CERROR("ldlm_get_ref failed: %d\n", rc);
+ return NULL;
+ }
+
+ for (idx = 0;; idx++) {
+ nsd = &ldlm_ns_hash_defs[idx];
+ if (nsd->nsd_type == LDLM_NS_TYPE_UNKNOWN) {
+ CERROR("Unknown type %d for ns %s\n", ns_type, name);
+ goto out_ref;
+ }
+
+ if (nsd->nsd_type == ns_type)
+ break;
+ }
+
+ OBD_ALLOC_PTR(ns);
+ if (!ns)
+ goto out_ref;
+
+ ns->ns_rs_hash = cfs_hash_create(name,
+ nsd->nsd_all_bits, nsd->nsd_all_bits,
+ nsd->nsd_bkt_bits, sizeof(*nsb),
+ CFS_HASH_MIN_THETA,
+ CFS_HASH_MAX_THETA,
+ nsd->nsd_hops,
+ CFS_HASH_DEPTH |
+ CFS_HASH_BIGNAME |
+ CFS_HASH_SPIN_BKTLOCK |
+ CFS_HASH_NO_ITEMREF);
+ if (ns->ns_rs_hash == NULL)
+ goto out_ns;
+
+ cfs_hash_for_each_bucket(ns->ns_rs_hash, &bd, idx) {
+ nsb = cfs_hash_bd_extra_get(ns->ns_rs_hash, &bd);
+ at_init(&nsb->nsb_at_estimate, ldlm_enqueue_min, 0);
+ nsb->nsb_namespace = ns;
+ }
+
+ ns->ns_obd = obd;
+ ns->ns_appetite = apt;
+ ns->ns_client = client;
+
+ INIT_LIST_HEAD(&ns->ns_list_chain);
+ INIT_LIST_HEAD(&ns->ns_unused_list);
+ spin_lock_init(&ns->ns_lock);
+ atomic_set(&ns->ns_bref, 0);
+ init_waitqueue_head(&ns->ns_waitq);
+
+ ns->ns_max_nolock_size = NS_DEFAULT_MAX_NOLOCK_BYTES;
+ ns->ns_contention_time = NS_DEFAULT_CONTENTION_SECONDS;
+ ns->ns_contended_locks = NS_DEFAULT_CONTENDED_LOCKS;
+
+ ns->ns_max_parallel_ast = LDLM_DEFAULT_PARALLEL_AST_LIMIT;
+ ns->ns_nr_unused = 0;
+ ns->ns_max_unused = LDLM_DEFAULT_LRU_SIZE;
+ ns->ns_max_age = LDLM_DEFAULT_MAX_ALIVE;
+ ns->ns_ctime_age_limit = LDLM_CTIME_AGE_LIMIT;
+ ns->ns_timeouts = 0;
+ ns->ns_orig_connect_flags = 0;
+ ns->ns_connect_flags = 0;
+ ns->ns_stopping = 0;
+ rc = ldlm_namespace_proc_register(ns);
+ if (rc != 0) {
+ CERROR("Can't initialize ns proc, rc %d\n", rc);
+ goto out_hash;
+ }
+
+ idx = ldlm_namespace_nr_read(client);
+ rc = ldlm_pool_init(&ns->ns_pool, ns, idx, client);
+ if (rc) {
+ CERROR("Can't initialize lock pool, rc %d\n", rc);
+ goto out_proc;
+ }
+
+ ldlm_namespace_register(ns, client);
+ return ns;
+out_proc:
+ ldlm_namespace_proc_unregister(ns);
+ ldlm_namespace_cleanup(ns, 0);
+out_hash:
+ cfs_hash_putref(ns->ns_rs_hash);
+out_ns:
+ OBD_FREE_PTR(ns);
+out_ref:
+ ldlm_put_ref();
+ return NULL;
+}
+EXPORT_SYMBOL(ldlm_namespace_new);
+
+extern struct ldlm_lock *ldlm_lock_get(struct ldlm_lock *lock);
+
+/**
+ * Cancel and destroy all locks on a resource.
+ *
+ * If flags contains FL_LOCAL_ONLY, don't try to tell the server, just
+ * clean up. This is currently only used for recovery, and we make
+ * certain assumptions as a result--notably, that we shouldn't cancel
+ * locks with refs.
+ */
+static void cleanup_resource(struct ldlm_resource *res, struct list_head *q,
+ __u64 flags)
+{
+ struct list_head *tmp;
+ int rc = 0, client = ns_is_client(ldlm_res_to_ns(res));
+ bool local_only = !!(flags & LDLM_FL_LOCAL_ONLY);
+
+ do {
+ struct ldlm_lock *lock = NULL;
+
+ /* First, we look for non-cleaned-yet lock
+ * all cleaned locks are marked by CLEANED flag. */
+ lock_res(res);
+ list_for_each(tmp, q) {
+ lock = list_entry(tmp, struct ldlm_lock,
+ l_res_link);
+ if (lock->l_flags & LDLM_FL_CLEANED) {
+ lock = NULL;
+ continue;
+ }
+ LDLM_LOCK_GET(lock);
+ lock->l_flags |= LDLM_FL_CLEANED;
+ break;
+ }
+
+ if (lock == NULL) {
+ unlock_res(res);
+ break;
+ }
+
+ /* Set CBPENDING so nothing in the cancellation path
+ * can match this lock. */
+ lock->l_flags |= LDLM_FL_CBPENDING;
+ lock->l_flags |= LDLM_FL_FAILED;
+ lock->l_flags |= flags;
+
+ /* ... without sending a CANCEL message for local_only. */
+ if (local_only)
+ lock->l_flags |= LDLM_FL_LOCAL_ONLY;
+
+ if (local_only && (lock->l_readers || lock->l_writers)) {
+ /* This is a little bit gross, but much better than the
+ * alternative: pretend that we got a blocking AST from
+ * the server, so that when the lock is decref'd, it
+ * will go away ... */
+ unlock_res(res);
+ LDLM_DEBUG(lock, "setting FL_LOCAL_ONLY");
+ if (lock->l_completion_ast)
+ lock->l_completion_ast(lock, 0, NULL);
+ LDLM_LOCK_RELEASE(lock);
+ continue;
+ }
+
+ if (client) {
+ struct lustre_handle lockh;
+
+ unlock_res(res);
+ ldlm_lock2handle(lock, &lockh);
+ rc = ldlm_cli_cancel(&lockh, LCF_ASYNC);
+ if (rc)
+ CERROR("ldlm_cli_cancel: %d\n", rc);
+ } else {
+ ldlm_resource_unlink_lock(lock);
+ unlock_res(res);
+ LDLM_DEBUG(lock, "Freeing a lock still held by a client node");
+ ldlm_lock_destroy(lock);
+ }
+ LDLM_LOCK_RELEASE(lock);
+ } while (1);
+}
+
+static int ldlm_resource_clean(struct cfs_hash *hs, struct cfs_hash_bd *bd,
+ struct hlist_node *hnode, void *arg)
+{
+ struct ldlm_resource *res = cfs_hash_object(hs, hnode);
+ __u64 flags = *(__u64 *)arg;
+
+ cleanup_resource(res, &res->lr_granted, flags);
+ cleanup_resource(res, &res->lr_converting, flags);
+ cleanup_resource(res, &res->lr_waiting, flags);
+
+ return 0;
+}
+
+static int ldlm_resource_complain(struct cfs_hash *hs, struct cfs_hash_bd *bd,
+ struct hlist_node *hnode, void *arg)
+{
+ struct ldlm_resource *res = cfs_hash_object(hs, hnode);
+
+ lock_res(res);
+ CERROR("%s: namespace resource "DLDLMRES
+ " (%p) refcount nonzero (%d) after lock cleanup; forcing cleanup.\n",
+ ldlm_ns_name(ldlm_res_to_ns(res)), PLDLMRES(res), res,
+ atomic_read(&res->lr_refcount) - 1);
+
+ ldlm_resource_dump(D_ERROR, res);
+ unlock_res(res);
+ return 0;
+}
+
+/**
+ * Cancel and destroy all locks in the namespace.
+ *
+ * Typically used during evictions when server notified client that it was
+ * evicted and all of its state needs to be destroyed.
+ * Also used during shutdown.
+ */
+int ldlm_namespace_cleanup(struct ldlm_namespace *ns, __u64 flags)
+{
+ if (ns == NULL) {
+ CDEBUG(D_INFO, "NULL ns, skipping cleanup\n");
+ return ELDLM_OK;
+ }
+
+ cfs_hash_for_each_nolock(ns->ns_rs_hash, ldlm_resource_clean, &flags);
+ cfs_hash_for_each_nolock(ns->ns_rs_hash, ldlm_resource_complain, NULL);
+ return ELDLM_OK;
+}
+EXPORT_SYMBOL(ldlm_namespace_cleanup);
+
+/**
+ * Attempts to free namespace.
+ *
+ * Only used when namespace goes away, like during an unmount.
+ */
+static int __ldlm_namespace_free(struct ldlm_namespace *ns, int force)
+{
+ /* At shutdown time, don't call the cancellation callback */
+ ldlm_namespace_cleanup(ns, force ? LDLM_FL_LOCAL_ONLY : 0);
+
+ if (atomic_read(&ns->ns_bref) > 0) {
+ struct l_wait_info lwi = LWI_INTR(LWI_ON_SIGNAL_NOOP, NULL);
+ int rc;
+
+ CDEBUG(D_DLMTRACE,
+ "dlm namespace %s free waiting on refcount %d\n",
+ ldlm_ns_name(ns), atomic_read(&ns->ns_bref));
+force_wait:
+ if (force)
+ lwi = LWI_TIMEOUT(obd_timeout * HZ / 4, NULL, NULL);
+
+ rc = l_wait_event(ns->ns_waitq,
+ atomic_read(&ns->ns_bref) == 0, &lwi);
+
+ /* Forced cleanups should be able to reclaim all references,
+ * so it's safe to wait forever... we can't leak locks... */
+ if (force && rc == -ETIMEDOUT) {
+ LCONSOLE_ERROR("Forced cleanup waiting for %s namespace with %d resources in use, (rc=%d)\n",
+ ldlm_ns_name(ns),
+ atomic_read(&ns->ns_bref), rc);
+ goto force_wait;
+ }
+
+ if (atomic_read(&ns->ns_bref)) {
+ LCONSOLE_ERROR("Cleanup waiting for %s namespace with %d resources in use, (rc=%d)\n",
+ ldlm_ns_name(ns),
+ atomic_read(&ns->ns_bref), rc);
+ return ELDLM_NAMESPACE_EXISTS;
+ }
+ CDEBUG(D_DLMTRACE, "dlm namespace %s free done waiting\n",
+ ldlm_ns_name(ns));
+ }
+
+ return ELDLM_OK;
+}
+
+/**
+ * Performs various cleanups for passed \a ns to make it drop refc and be
+ * ready for freeing. Waits for refc == 0.
+ *
+ * The following is done:
+ * (0) Unregister \a ns from its list to make inaccessible for potential
+ * users like pools thread and others;
+ * (1) Clear all locks in \a ns.
+ */
+void ldlm_namespace_free_prior(struct ldlm_namespace *ns,
+ struct obd_import *imp,
+ int force)
+{
+ int rc;
+
+ if (!ns)
+ return;
+
+ spin_lock(&ns->ns_lock);
+ ns->ns_stopping = 1;
+ spin_unlock(&ns->ns_lock);
+
+ /*
+ * Can fail with -EINTR when force == 0 in which case try harder.
+ */
+ rc = __ldlm_namespace_free(ns, force);
+ if (rc != ELDLM_OK) {
+ if (imp) {
+ ptlrpc_disconnect_import(imp, 0);
+ ptlrpc_invalidate_import(imp);
+ }
+
+ /*
+ * With all requests dropped and the import inactive
+ * we are guaranteed all reference will be dropped.
+ */
+ rc = __ldlm_namespace_free(ns, 1);
+ LASSERT(rc == 0);
+ }
+}
+
+/**
+ * Performs freeing memory structures related to \a ns. This is only done
+ * when ldlm_namespce_free_prior() successfully removed all resources
+ * referencing \a ns and its refc == 0.
+ */
+void ldlm_namespace_free_post(struct ldlm_namespace *ns)
+{
+ if (!ns)
+ return;
+
+ /* Make sure that nobody can find this ns in its list. */
+ ldlm_namespace_unregister(ns, ns->ns_client);
+ /* Fini pool _before_ parent proc dir is removed. This is important as
+ * ldlm_pool_fini() removes own proc dir which is child to @dir.
+ * Removing it after @dir may cause oops. */
+ ldlm_pool_fini(&ns->ns_pool);
+
+ ldlm_namespace_proc_unregister(ns);
+ cfs_hash_putref(ns->ns_rs_hash);
+ /* Namespace \a ns should be not on list at this time, otherwise
+ * this will cause issues related to using freed \a ns in poold
+ * thread. */
+ LASSERT(list_empty(&ns->ns_list_chain));
+ OBD_FREE_PTR(ns);
+ ldlm_put_ref();
+}
+
+/**
+ * Cleanup the resource, and free namespace.
+ * bug 12864:
+ * Deadlock issue:
+ * proc1: destroy import
+ * class_disconnect_export(grab cl_sem) ->
+ * -> ldlm_namespace_free ->
+ * -> lprocfs_remove(grab _lprocfs_lock).
+ * proc2: read proc info
+ * lprocfs_fops_read(grab _lprocfs_lock) ->
+ * -> osc_rd_active, etc(grab cl_sem).
+ *
+ * So that I have to split the ldlm_namespace_free into two parts - the first
+ * part ldlm_namespace_free_prior is used to cleanup the resource which is
+ * being used; the 2nd part ldlm_namespace_free_post is used to unregister the
+ * lprocfs entries, and then free memory. It will be called w/o cli->cl_sem
+ * held.
+ */
+void ldlm_namespace_free(struct ldlm_namespace *ns,
+ struct obd_import *imp,
+ int force)
+{
+ ldlm_namespace_free_prior(ns, imp, force);
+ ldlm_namespace_free_post(ns);
+}
+EXPORT_SYMBOL(ldlm_namespace_free);
+
+void ldlm_namespace_get(struct ldlm_namespace *ns)
+{
+ atomic_inc(&ns->ns_bref);
+}
+EXPORT_SYMBOL(ldlm_namespace_get);
+
+/* This is only for callers that care about refcount */
+int ldlm_namespace_get_return(struct ldlm_namespace *ns)
+{
+ return atomic_inc_return(&ns->ns_bref);
+}
+
+void ldlm_namespace_put(struct ldlm_namespace *ns)
+{
+ if (atomic_dec_and_lock(&ns->ns_bref, &ns->ns_lock)) {
+ wake_up(&ns->ns_waitq);
+ spin_unlock(&ns->ns_lock);
+ }
+}
+EXPORT_SYMBOL(ldlm_namespace_put);
+
+/** Register \a ns in the list of namespaces */
+void ldlm_namespace_register(struct ldlm_namespace *ns, ldlm_side_t client)
+{
+ mutex_lock(ldlm_namespace_lock(client));
+ LASSERT(list_empty(&ns->ns_list_chain));
+ list_add(&ns->ns_list_chain, ldlm_namespace_inactive_list(client));
+ ldlm_namespace_nr_inc(client);
+ mutex_unlock(ldlm_namespace_lock(client));
+}
+
+/** Unregister \a ns from the list of namespaces. */
+void ldlm_namespace_unregister(struct ldlm_namespace *ns, ldlm_side_t client)
+{
+ mutex_lock(ldlm_namespace_lock(client));
+ LASSERT(!list_empty(&ns->ns_list_chain));
+ /* Some asserts and possibly other parts of the code are still
+ * using list_empty(&ns->ns_list_chain). This is why it is
+ * important to use list_del_init() here. */
+ list_del_init(&ns->ns_list_chain);
+ ldlm_namespace_nr_dec(client);
+ mutex_unlock(ldlm_namespace_lock(client));
+}
+
+/** Should be called with ldlm_namespace_lock(client) taken. */
+void ldlm_namespace_move_to_active_locked(struct ldlm_namespace *ns,
+ ldlm_side_t client)
+{
+ LASSERT(!list_empty(&ns->ns_list_chain));
+ LASSERT(mutex_is_locked(ldlm_namespace_lock(client)));
+ list_move_tail(&ns->ns_list_chain, ldlm_namespace_list(client));
+}
+
+/** Should be called with ldlm_namespace_lock(client) taken. */
+void ldlm_namespace_move_to_inactive_locked(struct ldlm_namespace *ns,
+ ldlm_side_t client)
+{
+ LASSERT(!list_empty(&ns->ns_list_chain));
+ LASSERT(mutex_is_locked(ldlm_namespace_lock(client)));
+ list_move_tail(&ns->ns_list_chain,
+ ldlm_namespace_inactive_list(client));
+}
+
+/** Should be called with ldlm_namespace_lock(client) taken. */
+struct ldlm_namespace *ldlm_namespace_first_locked(ldlm_side_t client)
+{
+ LASSERT(mutex_is_locked(ldlm_namespace_lock(client)));
+ LASSERT(!list_empty(ldlm_namespace_list(client)));
+ return container_of(ldlm_namespace_list(client)->next,
+ struct ldlm_namespace, ns_list_chain);
+}
+
+/** Create and initialize new resource. */
+static struct ldlm_resource *ldlm_resource_new(void)
+{
+ struct ldlm_resource *res;
+ int idx;
+
+ OBD_SLAB_ALLOC_PTR_GFP(res, ldlm_resource_slab, GFP_NOFS);
+ if (res == NULL)
+ return NULL;
+
+ INIT_LIST_HEAD(&res->lr_granted);
+ INIT_LIST_HEAD(&res->lr_converting);
+ INIT_LIST_HEAD(&res->lr_waiting);
+
+ /* Initialize interval trees for each lock mode. */
+ for (idx = 0; idx < LCK_MODE_NUM; idx++) {
+ res->lr_itree[idx].lit_size = 0;
+ res->lr_itree[idx].lit_mode = 1 << idx;
+ res->lr_itree[idx].lit_root = NULL;
+ }
+
+ atomic_set(&res->lr_refcount, 1);
+ spin_lock_init(&res->lr_lock);
+ lu_ref_init(&res->lr_reference);
+
+ /* The creator of the resource must unlock the mutex after LVB
+ * initialization. */
+ mutex_init(&res->lr_lvb_mutex);
+ mutex_lock(&res->lr_lvb_mutex);
+
+ return res;
+}
+
+/**
+ * Return a reference to resource with given name, creating it if necessary.
+ * Args: namespace with ns_lock unlocked
+ * Locks: takes and releases NS hash-lock and res->lr_lock
+ * Returns: referenced, unlocked ldlm_resource or NULL
+ */
+struct ldlm_resource *
+ldlm_resource_get(struct ldlm_namespace *ns, struct ldlm_resource *parent,
+ const struct ldlm_res_id *name, ldlm_type_t type, int create)
+{
+ struct hlist_node *hnode;
+ struct ldlm_resource *res;
+ struct cfs_hash_bd bd;
+ __u64 version;
+ int ns_refcount = 0;
+
+ LASSERT(ns != NULL);
+ LASSERT(parent == NULL);
+ LASSERT(ns->ns_rs_hash != NULL);
+ LASSERT(name->name[0] != 0);
+
+ cfs_hash_bd_get_and_lock(ns->ns_rs_hash, (void *)name, &bd, 0);
+ hnode = cfs_hash_bd_lookup_locked(ns->ns_rs_hash, &bd, (void *)name);
+ if (hnode != NULL) {
+ cfs_hash_bd_unlock(ns->ns_rs_hash, &bd, 0);
+ res = hlist_entry(hnode, struct ldlm_resource, lr_hash);
+ /* Synchronize with regard to resource creation. */
+ if (ns->ns_lvbo && ns->ns_lvbo->lvbo_init) {
+ mutex_lock(&res->lr_lvb_mutex);
+ mutex_unlock(&res->lr_lvb_mutex);
+ }
+
+ if (unlikely(res->lr_lvb_len < 0)) {
+ ldlm_resource_putref(res);
+ res = NULL;
+ }
+ return res;
+ }
+
+ version = cfs_hash_bd_version_get(&bd);
+ cfs_hash_bd_unlock(ns->ns_rs_hash, &bd, 0);
+
+ if (create == 0)
+ return NULL;
+
+ LASSERTF(type >= LDLM_MIN_TYPE && type < LDLM_MAX_TYPE,
+ "type: %d\n", type);
+ res = ldlm_resource_new();
+ if (!res)
+ return NULL;
+
+ res->lr_ns_bucket = cfs_hash_bd_extra_get(ns->ns_rs_hash, &bd);
+ res->lr_name = *name;
+ res->lr_type = type;
+ res->lr_most_restr = LCK_NL;
+
+ cfs_hash_bd_lock(ns->ns_rs_hash, &bd, 1);
+ hnode = (version == cfs_hash_bd_version_get(&bd)) ? NULL :
+ cfs_hash_bd_lookup_locked(ns->ns_rs_hash, &bd, (void *)name);
+
+ if (hnode != NULL) {
+ /* Someone won the race and already added the resource. */
+ cfs_hash_bd_unlock(ns->ns_rs_hash, &bd, 1);
+ /* Clean lu_ref for failed resource. */
+ lu_ref_fini(&res->lr_reference);
+ /* We have taken lr_lvb_mutex. Drop it. */
+ mutex_unlock(&res->lr_lvb_mutex);
+ OBD_SLAB_FREE(res, ldlm_resource_slab, sizeof(*res));
+
+ res = hlist_entry(hnode, struct ldlm_resource, lr_hash);
+ /* Synchronize with regard to resource creation. */
+ if (ns->ns_lvbo && ns->ns_lvbo->lvbo_init) {
+ mutex_lock(&res->lr_lvb_mutex);
+ mutex_unlock(&res->lr_lvb_mutex);
+ }
+
+ if (unlikely(res->lr_lvb_len < 0)) {
+ ldlm_resource_putref(res);
+ res = NULL;
+ }
+ return res;
+ }
+ /* We won! Let's add the resource. */
+ cfs_hash_bd_add_locked(ns->ns_rs_hash, &bd, &res->lr_hash);
+ if (cfs_hash_bd_count_get(&bd) == 1)
+ ns_refcount = ldlm_namespace_get_return(ns);
+
+ cfs_hash_bd_unlock(ns->ns_rs_hash, &bd, 1);
+ if (ns->ns_lvbo && ns->ns_lvbo->lvbo_init) {
+ int rc;
+
+ OBD_FAIL_TIMEOUT(OBD_FAIL_LDLM_CREATE_RESOURCE, 2);
+ rc = ns->ns_lvbo->lvbo_init(res);
+ if (rc < 0) {
+ CERROR("%s: lvbo_init failed for resource %#llx:%#llx: rc = %d\n",
+ ns->ns_obd->obd_name, name->name[0],
+ name->name[1], rc);
+ if (res->lr_lvb_data) {
+ OBD_FREE(res->lr_lvb_data, res->lr_lvb_len);
+ res->lr_lvb_data = NULL;
+ }
+ res->lr_lvb_len = rc;
+ mutex_unlock(&res->lr_lvb_mutex);
+ ldlm_resource_putref(res);
+ return NULL;
+ }
+ }
+
+ /* We create resource with locked lr_lvb_mutex. */
+ mutex_unlock(&res->lr_lvb_mutex);
+
+ /* Let's see if we happened to be the very first resource in this
+ * namespace. If so, and this is a client namespace, we need to move
+ * the namespace into the active namespaces list to be patrolled by
+ * the ldlm_poold. */
+ if (ns_is_client(ns) && ns_refcount == 1) {
+ mutex_lock(ldlm_namespace_lock(LDLM_NAMESPACE_CLIENT));
+ ldlm_namespace_move_to_active_locked(ns, LDLM_NAMESPACE_CLIENT);
+ mutex_unlock(ldlm_namespace_lock(LDLM_NAMESPACE_CLIENT));
+ }
+
+ return res;
+}
+EXPORT_SYMBOL(ldlm_resource_get);
+
+struct ldlm_resource *ldlm_resource_getref(struct ldlm_resource *res)
+{
+ LASSERT(res != NULL);
+ LASSERT(res != LP_POISON);
+ atomic_inc(&res->lr_refcount);
+ CDEBUG(D_INFO, "getref res: %p count: %d\n", res,
+ atomic_read(&res->lr_refcount));
+ return res;
+}
+
+static void __ldlm_resource_putref_final(struct cfs_hash_bd *bd,
+ struct ldlm_resource *res)
+{
+ struct ldlm_ns_bucket *nsb = res->lr_ns_bucket;
+
+ if (!list_empty(&res->lr_granted)) {
+ ldlm_resource_dump(D_ERROR, res);
+ LBUG();
+ }
+
+ if (!list_empty(&res->lr_converting)) {
+ ldlm_resource_dump(D_ERROR, res);
+ LBUG();
+ }
+
+ if (!list_empty(&res->lr_waiting)) {
+ ldlm_resource_dump(D_ERROR, res);
+ LBUG();
+ }
+
+ cfs_hash_bd_del_locked(nsb->nsb_namespace->ns_rs_hash,
+ bd, &res->lr_hash);
+ lu_ref_fini(&res->lr_reference);
+ if (cfs_hash_bd_count_get(bd) == 0)
+ ldlm_namespace_put(nsb->nsb_namespace);
+}
+
+/* Returns 1 if the resource was freed, 0 if it remains. */
+int ldlm_resource_putref(struct ldlm_resource *res)
+{
+ struct ldlm_namespace *ns = ldlm_res_to_ns(res);
+ struct cfs_hash_bd bd;
+
+ LASSERT_ATOMIC_GT_LT(&res->lr_refcount, 0, LI_POISON);
+ CDEBUG(D_INFO, "putref res: %p count: %d\n",
+ res, atomic_read(&res->lr_refcount) - 1);
+
+ cfs_hash_bd_get(ns->ns_rs_hash, &res->lr_name, &bd);
+ if (cfs_hash_bd_dec_and_lock(ns->ns_rs_hash, &bd, &res->lr_refcount)) {
+ __ldlm_resource_putref_final(&bd, res);
+ cfs_hash_bd_unlock(ns->ns_rs_hash, &bd, 1);
+ if (ns->ns_lvbo && ns->ns_lvbo->lvbo_free)
+ ns->ns_lvbo->lvbo_free(res);
+ OBD_SLAB_FREE(res, ldlm_resource_slab, sizeof(*res));
+ return 1;
+ }
+ return 0;
+}
+EXPORT_SYMBOL(ldlm_resource_putref);
+
+/* Returns 1 if the resource was freed, 0 if it remains. */
+int ldlm_resource_putref_locked(struct ldlm_resource *res)
+{
+ struct ldlm_namespace *ns = ldlm_res_to_ns(res);
+
+ LASSERT_ATOMIC_GT_LT(&res->lr_refcount, 0, LI_POISON);
+ CDEBUG(D_INFO, "putref res: %p count: %d\n",
+ res, atomic_read(&res->lr_refcount) - 1);
+
+ if (atomic_dec_and_test(&res->lr_refcount)) {
+ struct cfs_hash_bd bd;
+
+ cfs_hash_bd_get(ldlm_res_to_ns(res)->ns_rs_hash,
+ &res->lr_name, &bd);
+ __ldlm_resource_putref_final(&bd, res);
+ cfs_hash_bd_unlock(ns->ns_rs_hash, &bd, 1);
+ /* NB: ns_rs_hash is created with CFS_HASH_NO_ITEMREF,
+ * so we should never be here while calling cfs_hash_del,
+ * cfs_hash_for_each_nolock is the only case we can get
+ * here, which is safe to release cfs_hash_bd_lock.
+ */
+ if (ns->ns_lvbo && ns->ns_lvbo->lvbo_free)
+ ns->ns_lvbo->lvbo_free(res);
+ OBD_SLAB_FREE(res, ldlm_resource_slab, sizeof(*res));
+
+ cfs_hash_bd_lock(ns->ns_rs_hash, &bd, 1);
+ return 1;
+ }
+ return 0;
+}
+
+/**
+ * Add a lock into a given resource into specified lock list.
+ */
+void ldlm_resource_add_lock(struct ldlm_resource *res, struct list_head *head,
+ struct ldlm_lock *lock)
+{
+ check_res_locked(res);
+
+ LDLM_DEBUG(lock, "About to add this lock:\n");
+
+ if (lock->l_flags & LDLM_FL_DESTROYED) {
+ CDEBUG(D_OTHER, "Lock destroyed, not adding to resource\n");
+ return;
+ }
+
+ LASSERT(list_empty(&lock->l_res_link));
+
+ list_add_tail(&lock->l_res_link, head);
+}
+
+/**
+ * Insert a lock into resource after specified lock.
+ *
+ * Obtain resource description from the lock we are inserting after.
+ */
+void ldlm_resource_insert_lock_after(struct ldlm_lock *original,
+ struct ldlm_lock *new)
+{
+ struct ldlm_resource *res = original->l_resource;
+
+ check_res_locked(res);
+
+ ldlm_resource_dump(D_INFO, res);
+ LDLM_DEBUG(new, "About to insert this lock after %p:\n", original);
+
+ if (new->l_flags & LDLM_FL_DESTROYED) {
+ CDEBUG(D_OTHER, "Lock destroyed, not adding to resource\n");
+ goto out;
+ }
+
+ LASSERT(list_empty(&new->l_res_link));
+
+ list_add(&new->l_res_link, &original->l_res_link);
+ out:;
+}
+
+void ldlm_resource_unlink_lock(struct ldlm_lock *lock)
+{
+ int type = lock->l_resource->lr_type;
+
+ check_res_locked(lock->l_resource);
+ if (type == LDLM_IBITS || type == LDLM_PLAIN)
+ ldlm_unlink_lock_skiplist(lock);
+ else if (type == LDLM_EXTENT)
+ ldlm_extent_unlink_lock(lock);
+ list_del_init(&lock->l_res_link);
+}
+EXPORT_SYMBOL(ldlm_resource_unlink_lock);
+
+void ldlm_res2desc(struct ldlm_resource *res, struct ldlm_resource_desc *desc)
+{
+ desc->lr_type = res->lr_type;
+ desc->lr_name = res->lr_name;
+}
+
+/**
+ * Print information about all locks in all namespaces on this node to debug
+ * log.
+ */
+void ldlm_dump_all_namespaces(ldlm_side_t client, int level)
+{
+ struct list_head *tmp;
+
+ if (!((libcfs_debug | D_ERROR) & level))
+ return;
+
+ mutex_lock(ldlm_namespace_lock(client));
+
+ list_for_each(tmp, ldlm_namespace_list(client)) {
+ struct ldlm_namespace *ns;
+
+ ns = list_entry(tmp, struct ldlm_namespace, ns_list_chain);
+ ldlm_namespace_dump(level, ns);
+ }
+
+ mutex_unlock(ldlm_namespace_lock(client));
+}
+EXPORT_SYMBOL(ldlm_dump_all_namespaces);
+
+static int ldlm_res_hash_dump(struct cfs_hash *hs, struct cfs_hash_bd *bd,
+ struct hlist_node *hnode, void *arg)
+{
+ struct ldlm_resource *res = cfs_hash_object(hs, hnode);
+ int level = (int)(unsigned long)arg;
+
+ lock_res(res);
+ ldlm_resource_dump(level, res);
+ unlock_res(res);
+
+ return 0;
+}
+
+/**
+ * Print information about all locks in this namespace on this node to debug
+ * log.
+ */
+void ldlm_namespace_dump(int level, struct ldlm_namespace *ns)
+{
+ if (!((libcfs_debug | D_ERROR) & level))
+ return;
+
+ CDEBUG(level, "--- Namespace: %s (rc: %d, side: %s)\n",
+ ldlm_ns_name(ns), atomic_read(&ns->ns_bref),
+ ns_is_client(ns) ? "client" : "server");
+
+ if (time_before(cfs_time_current(), ns->ns_next_dump))
+ return;
+
+ cfs_hash_for_each_nolock(ns->ns_rs_hash,
+ ldlm_res_hash_dump,
+ (void *)(unsigned long)level);
+ spin_lock(&ns->ns_lock);
+ ns->ns_next_dump = cfs_time_shift(10);
+ spin_unlock(&ns->ns_lock);
+}
+EXPORT_SYMBOL(ldlm_namespace_dump);
+
+/**
+ * Print information about all locks in this resource to debug log.
+ */
+void ldlm_resource_dump(int level, struct ldlm_resource *res)
+{
+ struct ldlm_lock *lock;
+ unsigned int granted = 0;
+
+ CLASSERT(RES_NAME_SIZE == 4);
+
+ if (!((libcfs_debug | D_ERROR) & level))
+ return;
+
+ CDEBUG(level, "--- Resource: "DLDLMRES" (%p) refcount = %d\n",
+ PLDLMRES(res), res, atomic_read(&res->lr_refcount));
+
+ if (!list_empty(&res->lr_granted)) {
+ CDEBUG(level, "Granted locks (in reverse order):\n");
+ list_for_each_entry_reverse(lock, &res->lr_granted,
+ l_res_link) {
+ LDLM_DEBUG_LIMIT(level, lock, "###");
+ if (!(level & D_CANTMASK) &&
+ ++granted > ldlm_dump_granted_max) {
+ CDEBUG(level, "only dump %d granted locks to avoid DDOS.\n",
+ granted);
+ break;
+ }
+ }
+ }
+ if (!list_empty(&res->lr_converting)) {
+ CDEBUG(level, "Converting locks:\n");
+ list_for_each_entry(lock, &res->lr_converting, l_res_link)
+ LDLM_DEBUG_LIMIT(level, lock, "###");
+ }
+ if (!list_empty(&res->lr_waiting)) {
+ CDEBUG(level, "Waiting locks:\n");
+ list_for_each_entry(lock, &res->lr_waiting, l_res_link)
+ LDLM_DEBUG_LIMIT(level, lock, "###");
+ }
+}
diff --git a/drivers/staging/lustre/lustre/libcfs/Makefile b/drivers/staging/lustre/lustre/libcfs/Makefile
new file mode 100644
index 000000000..2996a48a3
--- /dev/null
+++ b/drivers/staging/lustre/lustre/libcfs/Makefile
@@ -0,0 +1,18 @@
+obj-$(CONFIG_LUSTRE_FS) += libcfs.o
+
+libcfs-linux-objs := linux-tracefile.o linux-debug.o
+libcfs-linux-objs += linux-prim.o linux-cpu.o
+libcfs-linux-objs += linux-tcpip.o
+libcfs-linux-objs += linux-curproc.o
+libcfs-linux-objs += linux-module.o
+libcfs-linux-objs += linux-crypto.o
+libcfs-linux-objs += linux-crypto-adler.o
+
+libcfs-linux-objs := $(addprefix linux/,$(libcfs-linux-objs))
+
+libcfs-all-objs := debug.o fail.o nidstrings.o module.o tracefile.o \
+ libcfs_string.o hash.o kernel_user_comm.o \
+ prng.o workitem.o libcfs_cpu.o \
+ libcfs_mem.o libcfs_lock.o
+
+libcfs-objs := $(libcfs-linux-objs) $(libcfs-all-objs)
diff --git a/drivers/staging/lustre/lustre/libcfs/debug.c b/drivers/staging/lustre/lustre/libcfs/debug.c
new file mode 100644
index 000000000..021c92fa0
--- /dev/null
+++ b/drivers/staging/lustre/lustre/libcfs/debug.c
@@ -0,0 +1,460 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2011, 2012, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ *
+ * libcfs/libcfs/debug.c
+ *
+ * Author: Phil Schwan <phil@clusterfs.com>
+ *
+ */
+
+# define DEBUG_SUBSYSTEM S_LNET
+
+#include "../../include/linux/libcfs/libcfs.h"
+#include "tracefile.h"
+
+static char debug_file_name[1024];
+
+unsigned int libcfs_subsystem_debug = ~0;
+module_param(libcfs_subsystem_debug, int, 0644);
+MODULE_PARM_DESC(libcfs_subsystem_debug, "Lustre kernel debug subsystem mask");
+EXPORT_SYMBOL(libcfs_subsystem_debug);
+
+unsigned int libcfs_debug = (D_CANTMASK |
+ D_NETERROR | D_HA | D_CONFIG | D_IOCTL);
+module_param(libcfs_debug, int, 0644);
+MODULE_PARM_DESC(libcfs_debug, "Lustre kernel debug mask");
+EXPORT_SYMBOL(libcfs_debug);
+
+static unsigned int libcfs_debug_mb;
+module_param(libcfs_debug_mb, uint, 0644);
+MODULE_PARM_DESC(libcfs_debug_mb, "Total debug buffer size.");
+EXPORT_SYMBOL(libcfs_debug_mb);
+
+unsigned int libcfs_printk = D_CANTMASK;
+module_param(libcfs_printk, uint, 0644);
+MODULE_PARM_DESC(libcfs_printk, "Lustre kernel debug console mask");
+EXPORT_SYMBOL(libcfs_printk);
+
+unsigned int libcfs_console_ratelimit = 1;
+module_param(libcfs_console_ratelimit, uint, 0644);
+MODULE_PARM_DESC(libcfs_console_ratelimit, "Lustre kernel debug console ratelimit (0 to disable)");
+EXPORT_SYMBOL(libcfs_console_ratelimit);
+
+unsigned int libcfs_console_max_delay;
+module_param(libcfs_console_max_delay, uint, 0644);
+MODULE_PARM_DESC(libcfs_console_max_delay, "Lustre kernel debug console max delay (jiffies)");
+EXPORT_SYMBOL(libcfs_console_max_delay);
+
+unsigned int libcfs_console_min_delay;
+module_param(libcfs_console_min_delay, uint, 0644);
+MODULE_PARM_DESC(libcfs_console_min_delay, "Lustre kernel debug console min delay (jiffies)");
+EXPORT_SYMBOL(libcfs_console_min_delay);
+
+unsigned int libcfs_console_backoff = CDEBUG_DEFAULT_BACKOFF;
+module_param(libcfs_console_backoff, uint, 0644);
+MODULE_PARM_DESC(libcfs_console_backoff, "Lustre kernel debug console backoff factor");
+EXPORT_SYMBOL(libcfs_console_backoff);
+
+unsigned int libcfs_debug_binary = 1;
+EXPORT_SYMBOL(libcfs_debug_binary);
+
+unsigned int libcfs_stack = 3 * THREAD_SIZE / 4;
+EXPORT_SYMBOL(libcfs_stack);
+
+static unsigned int portal_enter_debugger;
+EXPORT_SYMBOL(portal_enter_debugger);
+
+unsigned int libcfs_catastrophe;
+EXPORT_SYMBOL(libcfs_catastrophe);
+
+unsigned int libcfs_watchdog_ratelimit = 300;
+EXPORT_SYMBOL(libcfs_watchdog_ratelimit);
+
+unsigned int libcfs_panic_on_lbug = 1;
+module_param(libcfs_panic_on_lbug, uint, 0644);
+MODULE_PARM_DESC(libcfs_panic_on_lbug, "Lustre kernel panic on LBUG");
+EXPORT_SYMBOL(libcfs_panic_on_lbug);
+
+atomic_t libcfs_kmemory = ATOMIC_INIT(0);
+EXPORT_SYMBOL(libcfs_kmemory);
+
+static wait_queue_head_t debug_ctlwq;
+
+char libcfs_debug_file_path_arr[PATH_MAX] = LIBCFS_DEBUG_FILE_PATH_DEFAULT;
+
+/* We need to pass a pointer here, but elsewhere this must be a const */
+static char *libcfs_debug_file_path;
+module_param(libcfs_debug_file_path, charp, 0644);
+MODULE_PARM_DESC(libcfs_debug_file_path,
+ "Path for dumping debug logs, set 'NONE' to prevent log dumping");
+
+int libcfs_panic_in_progress;
+
+/* libcfs_debug_token2mask() expects the returned
+ * string in lower-case */
+static const char *
+libcfs_debug_subsys2str(int subsys)
+{
+ switch (1 << subsys) {
+ default:
+ return NULL;
+ case S_UNDEFINED:
+ return "undefined";
+ case S_MDC:
+ return "mdc";
+ case S_MDS:
+ return "mds";
+ case S_OSC:
+ return "osc";
+ case S_OST:
+ return "ost";
+ case S_CLASS:
+ return "class";
+ case S_LOG:
+ return "log";
+ case S_LLITE:
+ return "llite";
+ case S_RPC:
+ return "rpc";
+ case S_LNET:
+ return "lnet";
+ case S_LND:
+ return "lnd";
+ case S_PINGER:
+ return "pinger";
+ case S_FILTER:
+ return "filter";
+ case S_ECHO:
+ return "echo";
+ case S_LDLM:
+ return "ldlm";
+ case S_LOV:
+ return "lov";
+ case S_LQUOTA:
+ return "lquota";
+ case S_OSD:
+ return "osd";
+ case S_LMV:
+ return "lmv";
+ case S_SEC:
+ return "sec";
+ case S_GSS:
+ return "gss";
+ case S_MGC:
+ return "mgc";
+ case S_MGS:
+ return "mgs";
+ case S_FID:
+ return "fid";
+ case S_FLD:
+ return "fld";
+ }
+}
+
+/* libcfs_debug_token2mask() expects the returned
+ * string in lower-case */
+static const char *
+libcfs_debug_dbg2str(int debug)
+{
+ switch (1 << debug) {
+ default:
+ return NULL;
+ case D_TRACE:
+ return "trace";
+ case D_INODE:
+ return "inode";
+ case D_SUPER:
+ return "super";
+ case D_EXT2:
+ return "ext2";
+ case D_MALLOC:
+ return "malloc";
+ case D_CACHE:
+ return "cache";
+ case D_INFO:
+ return "info";
+ case D_IOCTL:
+ return "ioctl";
+ case D_NETERROR:
+ return "neterror";
+ case D_NET:
+ return "net";
+ case D_WARNING:
+ return "warning";
+ case D_BUFFS:
+ return "buffs";
+ case D_OTHER:
+ return "other";
+ case D_DENTRY:
+ return "dentry";
+ case D_NETTRACE:
+ return "nettrace";
+ case D_PAGE:
+ return "page";
+ case D_DLMTRACE:
+ return "dlmtrace";
+ case D_ERROR:
+ return "error";
+ case D_EMERG:
+ return "emerg";
+ case D_HA:
+ return "ha";
+ case D_RPCTRACE:
+ return "rpctrace";
+ case D_VFSTRACE:
+ return "vfstrace";
+ case D_READA:
+ return "reada";
+ case D_MMAP:
+ return "mmap";
+ case D_CONFIG:
+ return "config";
+ case D_CONSOLE:
+ return "console";
+ case D_QUOTA:
+ return "quota";
+ case D_SEC:
+ return "sec";
+ case D_LFSCK:
+ return "lfsck";
+ }
+}
+
+int
+libcfs_debug_mask2str(char *str, int size, int mask, int is_subsys)
+{
+ const char *(*fn)(int bit) = is_subsys ? libcfs_debug_subsys2str :
+ libcfs_debug_dbg2str;
+ int len = 0;
+ const char *token;
+ int i;
+
+ if (mask == 0) { /* "0" */
+ if (size > 0)
+ str[0] = '0';
+ len = 1;
+ } else { /* space-separated tokens */
+ for (i = 0; i < 32; i++) {
+ if ((mask & (1 << i)) == 0)
+ continue;
+
+ token = fn(i);
+ if (token == NULL) /* unused bit */
+ continue;
+
+ if (len > 0) { /* separator? */
+ if (len < size)
+ str[len] = ' ';
+ len++;
+ }
+
+ while (*token != 0) {
+ if (len < size)
+ str[len] = *token;
+ token++;
+ len++;
+ }
+ }
+ }
+
+ /* terminate 'str' */
+ if (len < size)
+ str[len] = 0;
+ else
+ str[size - 1] = 0;
+
+ return len;
+}
+
+int
+libcfs_debug_str2mask(int *mask, const char *str, int is_subsys)
+{
+ const char *(*fn)(int bit) = is_subsys ? libcfs_debug_subsys2str :
+ libcfs_debug_dbg2str;
+ int m = 0;
+ int matched;
+ int n;
+ int t;
+
+ /* Allow a number for backwards compatibility */
+
+ for (n = strlen(str); n > 0; n--)
+ if (!isspace(str[n-1]))
+ break;
+ matched = n;
+ t = sscanf(str, "%i%n", &m, &matched);
+ if (t >= 1 && matched == n) {
+ /* don't print warning for lctl set_param debug=0 or -1 */
+ if (m != 0 && m != -1)
+ CWARN("You are trying to use a numerical value for the mask - this will be deprecated in a future release.\n");
+ *mask = m;
+ return 0;
+ }
+
+ return cfs_str2mask(str, fn, mask, is_subsys ? 0 : D_CANTMASK,
+ 0xffffffff);
+}
+
+/**
+ * Dump Lustre log to ::debug_file_path by calling tracefile_dump_all_pages()
+ */
+void libcfs_debug_dumplog_internal(void *arg)
+{
+ void *journal_info;
+
+ journal_info = current->journal_info;
+ current->journal_info = NULL;
+
+ if (strncmp(libcfs_debug_file_path_arr, "NONE", 4) != 0) {
+ snprintf(debug_file_name, sizeof(debug_file_name) - 1,
+ "%s.%ld.%ld", libcfs_debug_file_path_arr,
+ get_seconds(), (long_ptr_t)arg);
+ pr_alert("LustreError: dumping log to %s\n",
+ debug_file_name);
+ cfs_tracefile_dump_all_pages(debug_file_name);
+ libcfs_run_debug_log_upcall(debug_file_name);
+ }
+
+ current->journal_info = journal_info;
+}
+
+static int libcfs_debug_dumplog_thread(void *arg)
+{
+ libcfs_debug_dumplog_internal(arg);
+ wake_up(&debug_ctlwq);
+ return 0;
+}
+
+void libcfs_debug_dumplog(void)
+{
+ wait_queue_t wait;
+ struct task_struct *dumper;
+
+ /* we're being careful to ensure that the kernel thread is
+ * able to set our state to running as it exits before we
+ * get to schedule() */
+ init_waitqueue_entry(&wait, current);
+ set_current_state(TASK_INTERRUPTIBLE);
+ add_wait_queue(&debug_ctlwq, &wait);
+
+ dumper = kthread_run(libcfs_debug_dumplog_thread,
+ (void *)(long)current_pid(),
+ "libcfs_debug_dumper");
+ if (IS_ERR(dumper))
+ pr_err("LustreError: cannot start log dump thread: %ld\n",
+ PTR_ERR(dumper));
+ else
+ schedule();
+
+ /* be sure to teardown if cfs_create_thread() failed */
+ remove_wait_queue(&debug_ctlwq, &wait);
+ set_current_state(TASK_RUNNING);
+}
+EXPORT_SYMBOL(libcfs_debug_dumplog);
+
+int libcfs_debug_init(unsigned long bufsize)
+{
+ int rc = 0;
+ unsigned int max = libcfs_debug_mb;
+
+ init_waitqueue_head(&debug_ctlwq);
+
+ if (libcfs_console_max_delay <= 0 || /* not set by user or */
+ libcfs_console_min_delay <= 0 || /* set to invalid values */
+ libcfs_console_min_delay >= libcfs_console_max_delay) {
+ libcfs_console_max_delay = CDEBUG_DEFAULT_MAX_DELAY;
+ libcfs_console_min_delay = CDEBUG_DEFAULT_MIN_DELAY;
+ }
+
+ if (libcfs_debug_file_path != NULL) {
+ strncpy(libcfs_debug_file_path_arr,
+ libcfs_debug_file_path, PATH_MAX-1);
+ libcfs_debug_file_path_arr[PATH_MAX - 1] = '\0';
+ }
+
+ /* If libcfs_debug_mb is set to an invalid value or uninitialized
+ * then just make the total buffers smp_num_cpus * TCD_MAX_PAGES */
+ if (max > cfs_trace_max_debug_mb() || max < num_possible_cpus()) {
+ max = TCD_MAX_PAGES;
+ } else {
+ max = max / num_possible_cpus();
+ max <<= (20 - PAGE_CACHE_SHIFT);
+ }
+ rc = cfs_tracefile_init(max);
+
+ if (rc == 0)
+ libcfs_register_panic_notifier();
+
+ return rc;
+}
+
+int libcfs_debug_cleanup(void)
+{
+ libcfs_unregister_panic_notifier();
+ cfs_tracefile_exit();
+ return 0;
+}
+
+int libcfs_debug_clear_buffer(void)
+{
+ cfs_trace_flush_pages();
+ return 0;
+}
+
+/* Debug markers, although printed by S_LNET
+ * should not be be marked as such. */
+#undef DEBUG_SUBSYSTEM
+#define DEBUG_SUBSYSTEM S_UNDEFINED
+int libcfs_debug_mark_buffer(const char *text)
+{
+ CDEBUG(D_TRACE,
+ "***************************************************\n");
+ LCONSOLE(D_WARNING, "DEBUG MARKER: %s\n", text);
+ CDEBUG(D_TRACE,
+ "***************************************************\n");
+
+ return 0;
+}
+#undef DEBUG_SUBSYSTEM
+#define DEBUG_SUBSYSTEM S_LNET
+
+void libcfs_debug_set_level(unsigned int debug_level)
+{
+ pr_warn("Lustre: Setting portals debug level to %08x\n",
+ debug_level);
+ libcfs_debug = debug_level;
+}
+
+EXPORT_SYMBOL(libcfs_debug_set_level);
diff --git a/drivers/staging/lustre/lustre/libcfs/fail.c b/drivers/staging/lustre/lustre/libcfs/fail.c
new file mode 100644
index 000000000..92444b0fe
--- /dev/null
+++ b/drivers/staging/lustre/lustre/libcfs/fail.c
@@ -0,0 +1,138 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see http://www.gnu.org/licenses
+ *
+ * Please contact Oracle Corporation, Inc., 500 Oracle Parkway, Redwood Shores,
+ * CA 94065 USA or visit www.oracle.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2011, 2012, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Oracle Corporation, Inc.
+ */
+
+#include "../../include/linux/libcfs/libcfs.h"
+
+unsigned long cfs_fail_loc = 0;
+EXPORT_SYMBOL(cfs_fail_loc);
+
+unsigned int cfs_fail_val = 0;
+EXPORT_SYMBOL(cfs_fail_val);
+
+wait_queue_head_t cfs_race_waitq;
+EXPORT_SYMBOL(cfs_race_waitq);
+
+int cfs_race_state;
+EXPORT_SYMBOL(cfs_race_state);
+
+int __cfs_fail_check_set(__u32 id, __u32 value, int set)
+{
+ static atomic_t cfs_fail_count = ATOMIC_INIT(0);
+
+ LASSERT(!(id & CFS_FAIL_ONCE));
+
+ if ((cfs_fail_loc & (CFS_FAILED | CFS_FAIL_ONCE)) ==
+ (CFS_FAILED | CFS_FAIL_ONCE)) {
+ atomic_set(&cfs_fail_count, 0); /* paranoia */
+ return 0;
+ }
+
+ /* Fail 1/cfs_fail_val times */
+ if (cfs_fail_loc & CFS_FAIL_RAND) {
+ if (cfs_fail_val < 2 || cfs_rand() % cfs_fail_val > 0)
+ return 0;
+ }
+
+ /* Skip the first cfs_fail_val, then fail */
+ if (cfs_fail_loc & CFS_FAIL_SKIP) {
+ if (atomic_inc_return(&cfs_fail_count) <= cfs_fail_val)
+ return 0;
+ }
+
+ /* check cfs_fail_val... */
+ if (set == CFS_FAIL_LOC_VALUE) {
+ if (cfs_fail_val != -1 && cfs_fail_val != value)
+ return 0;
+ }
+
+ /* Fail cfs_fail_val times, overridden by FAIL_ONCE */
+ if (cfs_fail_loc & CFS_FAIL_SOME &&
+ (!(cfs_fail_loc & CFS_FAIL_ONCE) || cfs_fail_val <= 1)) {
+ int count = atomic_inc_return(&cfs_fail_count);
+
+ if (count >= cfs_fail_val) {
+ set_bit(CFS_FAIL_ONCE_BIT, &cfs_fail_loc);
+ atomic_set(&cfs_fail_count, 0);
+ /* we are lost race to increase */
+ if (count > cfs_fail_val)
+ return 0;
+ }
+ }
+
+ if ((set == CFS_FAIL_LOC_ORSET || set == CFS_FAIL_LOC_RESET) &&
+ (value & CFS_FAIL_ONCE))
+ set_bit(CFS_FAIL_ONCE_BIT, &cfs_fail_loc);
+ /* Lost race to set CFS_FAILED_BIT. */
+ if (test_and_set_bit(CFS_FAILED_BIT, &cfs_fail_loc)) {
+ /* If CFS_FAIL_ONCE is valid, only one process can fail,
+ * otherwise multi-process can fail at the same time. */
+ if (cfs_fail_loc & CFS_FAIL_ONCE)
+ return 0;
+ }
+
+ switch (set) {
+ case CFS_FAIL_LOC_NOSET:
+ case CFS_FAIL_LOC_VALUE:
+ break;
+ case CFS_FAIL_LOC_ORSET:
+ cfs_fail_loc |= value & ~(CFS_FAILED | CFS_FAIL_ONCE);
+ break;
+ case CFS_FAIL_LOC_RESET:
+ cfs_fail_loc = value;
+ break;
+ default:
+ LASSERTF(0, "called with bad set %u\n", set);
+ break;
+ }
+
+ return 1;
+}
+EXPORT_SYMBOL(__cfs_fail_check_set);
+
+int __cfs_fail_timeout_set(__u32 id, __u32 value, int ms, int set)
+{
+ int ret = 0;
+
+ ret = __cfs_fail_check_set(id, value, set);
+ if (ret) {
+ CERROR("cfs_fail_timeout id %x sleeping for %dms\n",
+ id, ms);
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ schedule_timeout(cfs_time_seconds(ms) / 1000);
+ CERROR("cfs_fail_timeout id %x awake\n", id);
+ }
+ return ret;
+}
+EXPORT_SYMBOL(__cfs_fail_timeout_set);
diff --git a/drivers/staging/lustre/lustre/libcfs/hash.c b/drivers/staging/lustre/lustre/libcfs/hash.c
new file mode 100644
index 000000000..a55567e0d
--- /dev/null
+++ b/drivers/staging/lustre/lustre/libcfs/hash.c
@@ -0,0 +1,2098 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2011, 2012, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ *
+ * libcfs/libcfs/hash.c
+ *
+ * Implement a hash class for hash process in lustre system.
+ *
+ * Author: YuZhangyong <yzy@clusterfs.com>
+ *
+ * 2008-08-15: Brian Behlendorf <behlendorf1@llnl.gov>
+ * - Simplified API and improved documentation
+ * - Added per-hash feature flags:
+ * * CFS_HASH_DEBUG additional validation
+ * * CFS_HASH_REHASH dynamic rehashing
+ * - Added per-hash statistics
+ * - General performance enhancements
+ *
+ * 2009-07-31: Liang Zhen <zhen.liang@sun.com>
+ * - move all stuff to libcfs
+ * - don't allow cur_bits != max_bits without setting of CFS_HASH_REHASH
+ * - ignore hs_rwlock if without CFS_HASH_REHASH setting
+ * - buckets are allocated one by one(instead of contiguous memory),
+ * to avoid unnecessary cacheline conflict
+ *
+ * 2010-03-01: Liang Zhen <zhen.liang@sun.com>
+ * - "bucket" is a group of hlist_head now, user can specify bucket size
+ * by bkt_bits of cfs_hash_create(), all hlist_heads in a bucket share
+ * one lock for reducing memory overhead.
+ *
+ * - support lockless hash, caller will take care of locks:
+ * avoid lock overhead for hash tables that are already protected
+ * by locking in the caller for another reason
+ *
+ * - support both spin_lock/rwlock for bucket:
+ * overhead of spinlock contention is lower than read/write
+ * contention of rwlock, so using spinlock to serialize operations on
+ * bucket is more reasonable for those frequently changed hash tables
+ *
+ * - support one-single lock mode:
+ * one lock to protect all hash operations to avoid overhead of
+ * multiple locks if hash table is always small
+ *
+ * - removed a lot of unnecessary addref & decref on hash element:
+ * addref & decref are atomic operations in many use-cases which
+ * are expensive.
+ *
+ * - support non-blocking cfs_hash_add() and cfs_hash_findadd():
+ * some lustre use-cases require these functions to be strictly
+ * non-blocking, we need to schedule required rehash on a different
+ * thread on those cases.
+ *
+ * - safer rehash on large hash table
+ * In old implementation, rehash function will exclusively lock the
+ * hash table and finish rehash in one batch, it's dangerous on SMP
+ * system because rehash millions of elements could take long time.
+ * New implemented rehash can release lock and relax CPU in middle
+ * of rehash, it's safe for another thread to search/change on the
+ * hash table even it's in rehasing.
+ *
+ * - support two different refcount modes
+ * . hash table has refcount on element
+ * . hash table doesn't change refcount on adding/removing element
+ *
+ * - support long name hash table (for param-tree)
+ *
+ * - fix a bug for cfs_hash_rehash_key:
+ * in old implementation, cfs_hash_rehash_key could screw up the
+ * hash-table because @key is overwritten without any protection.
+ * Now we need user to define hs_keycpy for those rehash enabled
+ * hash tables, cfs_hash_rehash_key will overwrite hash-key
+ * inside lock by calling hs_keycpy.
+ *
+ * - better hash iteration:
+ * Now we support both locked iteration & lockless iteration of hash
+ * table. Also, user can break the iteration by return 1 in callback.
+ */
+
+#include "../../include/linux/libcfs/libcfs.h"
+#include <linux/seq_file.h>
+
+#if CFS_HASH_DEBUG_LEVEL >= CFS_HASH_DEBUG_1
+static unsigned int warn_on_depth = 8;
+module_param(warn_on_depth, uint, 0644);
+MODULE_PARM_DESC(warn_on_depth, "warning when hash depth is high.");
+#endif
+
+struct cfs_wi_sched *cfs_sched_rehash;
+
+static inline void
+cfs_hash_nl_lock(union cfs_hash_lock *lock, int exclusive) {}
+
+static inline void
+cfs_hash_nl_unlock(union cfs_hash_lock *lock, int exclusive) {}
+
+static inline void
+cfs_hash_spin_lock(union cfs_hash_lock *lock, int exclusive)
+ __acquires(&lock->spin)
+{
+ spin_lock(&lock->spin);
+}
+
+static inline void
+cfs_hash_spin_unlock(union cfs_hash_lock *lock, int exclusive)
+ __releases(&lock->spin)
+{
+ spin_unlock(&lock->spin);
+}
+
+static inline void
+cfs_hash_rw_lock(union cfs_hash_lock *lock, int exclusive)
+ __acquires(&lock->rw)
+{
+ if (!exclusive)
+ read_lock(&lock->rw);
+ else
+ write_lock(&lock->rw);
+}
+
+static inline void
+cfs_hash_rw_unlock(union cfs_hash_lock *lock, int exclusive)
+ __releases(&lock->rw)
+{
+ if (!exclusive)
+ read_unlock(&lock->rw);
+ else
+ write_unlock(&lock->rw);
+}
+
+/** No lock hash */
+static cfs_hash_lock_ops_t cfs_hash_nl_lops = {
+ .hs_lock = cfs_hash_nl_lock,
+ .hs_unlock = cfs_hash_nl_unlock,
+ .hs_bkt_lock = cfs_hash_nl_lock,
+ .hs_bkt_unlock = cfs_hash_nl_unlock,
+};
+
+/** no bucket lock, one spinlock to protect everything */
+static cfs_hash_lock_ops_t cfs_hash_nbl_lops = {
+ .hs_lock = cfs_hash_spin_lock,
+ .hs_unlock = cfs_hash_spin_unlock,
+ .hs_bkt_lock = cfs_hash_nl_lock,
+ .hs_bkt_unlock = cfs_hash_nl_unlock,
+};
+
+/** spin bucket lock, rehash is enabled */
+static cfs_hash_lock_ops_t cfs_hash_bkt_spin_lops = {
+ .hs_lock = cfs_hash_rw_lock,
+ .hs_unlock = cfs_hash_rw_unlock,
+ .hs_bkt_lock = cfs_hash_spin_lock,
+ .hs_bkt_unlock = cfs_hash_spin_unlock,
+};
+
+/** rw bucket lock, rehash is enabled */
+static cfs_hash_lock_ops_t cfs_hash_bkt_rw_lops = {
+ .hs_lock = cfs_hash_rw_lock,
+ .hs_unlock = cfs_hash_rw_unlock,
+ .hs_bkt_lock = cfs_hash_rw_lock,
+ .hs_bkt_unlock = cfs_hash_rw_unlock,
+};
+
+/** spin bucket lock, rehash is disabled */
+static cfs_hash_lock_ops_t cfs_hash_nr_bkt_spin_lops = {
+ .hs_lock = cfs_hash_nl_lock,
+ .hs_unlock = cfs_hash_nl_unlock,
+ .hs_bkt_lock = cfs_hash_spin_lock,
+ .hs_bkt_unlock = cfs_hash_spin_unlock,
+};
+
+/** rw bucket lock, rehash is disabled */
+static cfs_hash_lock_ops_t cfs_hash_nr_bkt_rw_lops = {
+ .hs_lock = cfs_hash_nl_lock,
+ .hs_unlock = cfs_hash_nl_unlock,
+ .hs_bkt_lock = cfs_hash_rw_lock,
+ .hs_bkt_unlock = cfs_hash_rw_unlock,
+};
+
+static void
+cfs_hash_lock_setup(struct cfs_hash *hs)
+{
+ if (cfs_hash_with_no_lock(hs)) {
+ hs->hs_lops = &cfs_hash_nl_lops;
+
+ } else if (cfs_hash_with_no_bktlock(hs)) {
+ hs->hs_lops = &cfs_hash_nbl_lops;
+ spin_lock_init(&hs->hs_lock.spin);
+
+ } else if (cfs_hash_with_rehash(hs)) {
+ rwlock_init(&hs->hs_lock.rw);
+
+ if (cfs_hash_with_rw_bktlock(hs))
+ hs->hs_lops = &cfs_hash_bkt_rw_lops;
+ else if (cfs_hash_with_spin_bktlock(hs))
+ hs->hs_lops = &cfs_hash_bkt_spin_lops;
+ else
+ LBUG();
+ } else {
+ if (cfs_hash_with_rw_bktlock(hs))
+ hs->hs_lops = &cfs_hash_nr_bkt_rw_lops;
+ else if (cfs_hash_with_spin_bktlock(hs))
+ hs->hs_lops = &cfs_hash_nr_bkt_spin_lops;
+ else
+ LBUG();
+ }
+}
+
+/**
+ * Simple hash head without depth tracking
+ * new element is always added to head of hlist
+ */
+typedef struct {
+ struct hlist_head hh_head; /**< entries list */
+} cfs_hash_head_t;
+
+static int
+cfs_hash_hh_hhead_size(struct cfs_hash *hs)
+{
+ return sizeof(cfs_hash_head_t);
+}
+
+static struct hlist_head *
+cfs_hash_hh_hhead(struct cfs_hash *hs, struct cfs_hash_bd *bd)
+{
+ cfs_hash_head_t *head = (cfs_hash_head_t *)&bd->bd_bucket->hsb_head[0];
+
+ return &head[bd->bd_offset].hh_head;
+}
+
+static int
+cfs_hash_hh_hnode_add(struct cfs_hash *hs, struct cfs_hash_bd *bd,
+ struct hlist_node *hnode)
+{
+ hlist_add_head(hnode, cfs_hash_hh_hhead(hs, bd));
+ return -1; /* unknown depth */
+}
+
+static int
+cfs_hash_hh_hnode_del(struct cfs_hash *hs, struct cfs_hash_bd *bd,
+ struct hlist_node *hnode)
+{
+ hlist_del_init(hnode);
+ return -1; /* unknown depth */
+}
+
+/**
+ * Simple hash head with depth tracking
+ * new element is always added to head of hlist
+ */
+typedef struct {
+ struct hlist_head hd_head; /**< entries list */
+ unsigned int hd_depth; /**< list length */
+} cfs_hash_head_dep_t;
+
+static int
+cfs_hash_hd_hhead_size(struct cfs_hash *hs)
+{
+ return sizeof(cfs_hash_head_dep_t);
+}
+
+static struct hlist_head *
+cfs_hash_hd_hhead(struct cfs_hash *hs, struct cfs_hash_bd *bd)
+{
+ cfs_hash_head_dep_t *head;
+
+ head = (cfs_hash_head_dep_t *)&bd->bd_bucket->hsb_head[0];
+ return &head[bd->bd_offset].hd_head;
+}
+
+static int
+cfs_hash_hd_hnode_add(struct cfs_hash *hs, struct cfs_hash_bd *bd,
+ struct hlist_node *hnode)
+{
+ cfs_hash_head_dep_t *hh = container_of(cfs_hash_hd_hhead(hs, bd),
+ cfs_hash_head_dep_t, hd_head);
+ hlist_add_head(hnode, &hh->hd_head);
+ return ++hh->hd_depth;
+}
+
+static int
+cfs_hash_hd_hnode_del(struct cfs_hash *hs, struct cfs_hash_bd *bd,
+ struct hlist_node *hnode)
+{
+ cfs_hash_head_dep_t *hh = container_of(cfs_hash_hd_hhead(hs, bd),
+ cfs_hash_head_dep_t, hd_head);
+ hlist_del_init(hnode);
+ return --hh->hd_depth;
+}
+
+/**
+ * double links hash head without depth tracking
+ * new element is always added to tail of hlist
+ */
+typedef struct {
+ struct hlist_head dh_head; /**< entries list */
+ struct hlist_node *dh_tail; /**< the last entry */
+} cfs_hash_dhead_t;
+
+static int
+cfs_hash_dh_hhead_size(struct cfs_hash *hs)
+{
+ return sizeof(cfs_hash_dhead_t);
+}
+
+static struct hlist_head *
+cfs_hash_dh_hhead(struct cfs_hash *hs, struct cfs_hash_bd *bd)
+{
+ cfs_hash_dhead_t *head;
+
+ head = (cfs_hash_dhead_t *)&bd->bd_bucket->hsb_head[0];
+ return &head[bd->bd_offset].dh_head;
+}
+
+static int
+cfs_hash_dh_hnode_add(struct cfs_hash *hs, struct cfs_hash_bd *bd,
+ struct hlist_node *hnode)
+{
+ cfs_hash_dhead_t *dh = container_of(cfs_hash_dh_hhead(hs, bd),
+ cfs_hash_dhead_t, dh_head);
+
+ if (dh->dh_tail != NULL) /* not empty */
+ hlist_add_behind(hnode, dh->dh_tail);
+ else /* empty list */
+ hlist_add_head(hnode, &dh->dh_head);
+ dh->dh_tail = hnode;
+ return -1; /* unknown depth */
+}
+
+static int
+cfs_hash_dh_hnode_del(struct cfs_hash *hs, struct cfs_hash_bd *bd,
+ struct hlist_node *hnd)
+{
+ cfs_hash_dhead_t *dh = container_of(cfs_hash_dh_hhead(hs, bd),
+ cfs_hash_dhead_t, dh_head);
+
+ if (hnd->next == NULL) { /* it's the tail */
+ dh->dh_tail = (hnd->pprev == &dh->dh_head.first) ? NULL :
+ container_of(hnd->pprev, struct hlist_node, next);
+ }
+ hlist_del_init(hnd);
+ return -1; /* unknown depth */
+}
+
+/**
+ * double links hash head with depth tracking
+ * new element is always added to tail of hlist
+ */
+typedef struct {
+ struct hlist_head dd_head; /**< entries list */
+ struct hlist_node *dd_tail; /**< the last entry */
+ unsigned int dd_depth; /**< list length */
+} cfs_hash_dhead_dep_t;
+
+static int
+cfs_hash_dd_hhead_size(struct cfs_hash *hs)
+{
+ return sizeof(cfs_hash_dhead_dep_t);
+}
+
+static struct hlist_head *
+cfs_hash_dd_hhead(struct cfs_hash *hs, struct cfs_hash_bd *bd)
+{
+ cfs_hash_dhead_dep_t *head;
+
+ head = (cfs_hash_dhead_dep_t *)&bd->bd_bucket->hsb_head[0];
+ return &head[bd->bd_offset].dd_head;
+}
+
+static int
+cfs_hash_dd_hnode_add(struct cfs_hash *hs, struct cfs_hash_bd *bd,
+ struct hlist_node *hnode)
+{
+ cfs_hash_dhead_dep_t *dh = container_of(cfs_hash_dd_hhead(hs, bd),
+ cfs_hash_dhead_dep_t, dd_head);
+
+ if (dh->dd_tail != NULL) /* not empty */
+ hlist_add_behind(hnode, dh->dd_tail);
+ else /* empty list */
+ hlist_add_head(hnode, &dh->dd_head);
+ dh->dd_tail = hnode;
+ return ++dh->dd_depth;
+}
+
+static int
+cfs_hash_dd_hnode_del(struct cfs_hash *hs, struct cfs_hash_bd *bd,
+ struct hlist_node *hnd)
+{
+ cfs_hash_dhead_dep_t *dh = container_of(cfs_hash_dd_hhead(hs, bd),
+ cfs_hash_dhead_dep_t, dd_head);
+
+ if (hnd->next == NULL) { /* it's the tail */
+ dh->dd_tail = (hnd->pprev == &dh->dd_head.first) ? NULL :
+ container_of(hnd->pprev, struct hlist_node, next);
+ }
+ hlist_del_init(hnd);
+ return --dh->dd_depth;
+}
+
+static cfs_hash_hlist_ops_t cfs_hash_hh_hops = {
+ .hop_hhead = cfs_hash_hh_hhead,
+ .hop_hhead_size = cfs_hash_hh_hhead_size,
+ .hop_hnode_add = cfs_hash_hh_hnode_add,
+ .hop_hnode_del = cfs_hash_hh_hnode_del,
+};
+
+static cfs_hash_hlist_ops_t cfs_hash_hd_hops = {
+ .hop_hhead = cfs_hash_hd_hhead,
+ .hop_hhead_size = cfs_hash_hd_hhead_size,
+ .hop_hnode_add = cfs_hash_hd_hnode_add,
+ .hop_hnode_del = cfs_hash_hd_hnode_del,
+};
+
+static cfs_hash_hlist_ops_t cfs_hash_dh_hops = {
+ .hop_hhead = cfs_hash_dh_hhead,
+ .hop_hhead_size = cfs_hash_dh_hhead_size,
+ .hop_hnode_add = cfs_hash_dh_hnode_add,
+ .hop_hnode_del = cfs_hash_dh_hnode_del,
+};
+
+static cfs_hash_hlist_ops_t cfs_hash_dd_hops = {
+ .hop_hhead = cfs_hash_dd_hhead,
+ .hop_hhead_size = cfs_hash_dd_hhead_size,
+ .hop_hnode_add = cfs_hash_dd_hnode_add,
+ .hop_hnode_del = cfs_hash_dd_hnode_del,
+};
+
+static void
+cfs_hash_hlist_setup(struct cfs_hash *hs)
+{
+ if (cfs_hash_with_add_tail(hs)) {
+ hs->hs_hops = cfs_hash_with_depth(hs) ?
+ &cfs_hash_dd_hops : &cfs_hash_dh_hops;
+ } else {
+ hs->hs_hops = cfs_hash_with_depth(hs) ?
+ &cfs_hash_hd_hops : &cfs_hash_hh_hops;
+ }
+}
+
+static void
+cfs_hash_bd_from_key(struct cfs_hash *hs, struct cfs_hash_bucket **bkts,
+ unsigned int bits, const void *key, struct cfs_hash_bd *bd)
+{
+ unsigned int index = cfs_hash_id(hs, key, (1U << bits) - 1);
+
+ LASSERT(bits == hs->hs_cur_bits || bits == hs->hs_rehash_bits);
+
+ bd->bd_bucket = bkts[index & ((1U << (bits - hs->hs_bkt_bits)) - 1)];
+ bd->bd_offset = index >> (bits - hs->hs_bkt_bits);
+}
+
+void
+cfs_hash_bd_get(struct cfs_hash *hs, const void *key, struct cfs_hash_bd *bd)
+{
+ /* NB: caller should hold hs->hs_rwlock if REHASH is set */
+ if (likely(hs->hs_rehash_buckets == NULL)) {
+ cfs_hash_bd_from_key(hs, hs->hs_buckets,
+ hs->hs_cur_bits, key, bd);
+ } else {
+ LASSERT(hs->hs_rehash_bits != 0);
+ cfs_hash_bd_from_key(hs, hs->hs_rehash_buckets,
+ hs->hs_rehash_bits, key, bd);
+ }
+}
+EXPORT_SYMBOL(cfs_hash_bd_get);
+
+static inline void
+cfs_hash_bd_dep_record(struct cfs_hash *hs, struct cfs_hash_bd *bd, int dep_cur)
+{
+ if (likely(dep_cur <= bd->bd_bucket->hsb_depmax))
+ return;
+
+ bd->bd_bucket->hsb_depmax = dep_cur;
+# if CFS_HASH_DEBUG_LEVEL >= CFS_HASH_DEBUG_1
+ if (likely(warn_on_depth == 0 ||
+ max(warn_on_depth, hs->hs_dep_max) >= dep_cur))
+ return;
+
+ spin_lock(&hs->hs_dep_lock);
+ hs->hs_dep_max = dep_cur;
+ hs->hs_dep_bkt = bd->bd_bucket->hsb_index;
+ hs->hs_dep_off = bd->bd_offset;
+ hs->hs_dep_bits = hs->hs_cur_bits;
+ spin_unlock(&hs->hs_dep_lock);
+
+ cfs_wi_schedule(cfs_sched_rehash, &hs->hs_dep_wi);
+# endif
+}
+
+void
+cfs_hash_bd_add_locked(struct cfs_hash *hs, struct cfs_hash_bd *bd,
+ struct hlist_node *hnode)
+{
+ int rc;
+
+ rc = hs->hs_hops->hop_hnode_add(hs, bd, hnode);
+ cfs_hash_bd_dep_record(hs, bd, rc);
+ bd->bd_bucket->hsb_version++;
+ if (unlikely(bd->bd_bucket->hsb_version == 0))
+ bd->bd_bucket->hsb_version++;
+ bd->bd_bucket->hsb_count++;
+
+ if (cfs_hash_with_counter(hs))
+ atomic_inc(&hs->hs_count);
+ if (!cfs_hash_with_no_itemref(hs))
+ cfs_hash_get(hs, hnode);
+}
+EXPORT_SYMBOL(cfs_hash_bd_add_locked);
+
+void
+cfs_hash_bd_del_locked(struct cfs_hash *hs, struct cfs_hash_bd *bd,
+ struct hlist_node *hnode)
+{
+ hs->hs_hops->hop_hnode_del(hs, bd, hnode);
+
+ LASSERT(bd->bd_bucket->hsb_count > 0);
+ bd->bd_bucket->hsb_count--;
+ bd->bd_bucket->hsb_version++;
+ if (unlikely(bd->bd_bucket->hsb_version == 0))
+ bd->bd_bucket->hsb_version++;
+
+ if (cfs_hash_with_counter(hs)) {
+ LASSERT(atomic_read(&hs->hs_count) > 0);
+ atomic_dec(&hs->hs_count);
+ }
+ if (!cfs_hash_with_no_itemref(hs))
+ cfs_hash_put_locked(hs, hnode);
+}
+EXPORT_SYMBOL(cfs_hash_bd_del_locked);
+
+void
+cfs_hash_bd_move_locked(struct cfs_hash *hs, struct cfs_hash_bd *bd_old,
+ struct cfs_hash_bd *bd_new, struct hlist_node *hnode)
+{
+ struct cfs_hash_bucket *obkt = bd_old->bd_bucket;
+ struct cfs_hash_bucket *nbkt = bd_new->bd_bucket;
+ int rc;
+
+ if (cfs_hash_bd_compare(bd_old, bd_new) == 0)
+ return;
+
+ /* use cfs_hash_bd_hnode_add/del, to avoid atomic & refcount ops
+ * in cfs_hash_bd_del/add_locked */
+ hs->hs_hops->hop_hnode_del(hs, bd_old, hnode);
+ rc = hs->hs_hops->hop_hnode_add(hs, bd_new, hnode);
+ cfs_hash_bd_dep_record(hs, bd_new, rc);
+
+ LASSERT(obkt->hsb_count > 0);
+ obkt->hsb_count--;
+ obkt->hsb_version++;
+ if (unlikely(obkt->hsb_version == 0))
+ obkt->hsb_version++;
+ nbkt->hsb_count++;
+ nbkt->hsb_version++;
+ if (unlikely(nbkt->hsb_version == 0))
+ nbkt->hsb_version++;
+}
+EXPORT_SYMBOL(cfs_hash_bd_move_locked);
+
+enum {
+ /** always set, for sanity (avoid ZERO intent) */
+ CFS_HS_LOOKUP_MASK_FIND = 1 << 0,
+ /** return entry with a ref */
+ CFS_HS_LOOKUP_MASK_REF = 1 << 1,
+ /** add entry if not existing */
+ CFS_HS_LOOKUP_MASK_ADD = 1 << 2,
+ /** delete entry, ignore other masks */
+ CFS_HS_LOOKUP_MASK_DEL = 1 << 3,
+};
+
+typedef enum cfs_hash_lookup_intent {
+ /** return item w/o refcount */
+ CFS_HS_LOOKUP_IT_PEEK = CFS_HS_LOOKUP_MASK_FIND,
+ /** return item with refcount */
+ CFS_HS_LOOKUP_IT_FIND = (CFS_HS_LOOKUP_MASK_FIND |
+ CFS_HS_LOOKUP_MASK_REF),
+ /** return item w/o refcount if existed, otherwise add */
+ CFS_HS_LOOKUP_IT_ADD = (CFS_HS_LOOKUP_MASK_FIND |
+ CFS_HS_LOOKUP_MASK_ADD),
+ /** return item with refcount if existed, otherwise add */
+ CFS_HS_LOOKUP_IT_FINDADD = (CFS_HS_LOOKUP_IT_FIND |
+ CFS_HS_LOOKUP_MASK_ADD),
+ /** delete if existed */
+ CFS_HS_LOOKUP_IT_FINDDEL = (CFS_HS_LOOKUP_MASK_FIND |
+ CFS_HS_LOOKUP_MASK_DEL)
+} cfs_hash_lookup_intent_t;
+
+static struct hlist_node *
+cfs_hash_bd_lookup_intent(struct cfs_hash *hs, struct cfs_hash_bd *bd,
+ const void *key, struct hlist_node *hnode,
+ cfs_hash_lookup_intent_t intent)
+
+{
+ struct hlist_head *hhead = cfs_hash_bd_hhead(hs, bd);
+ struct hlist_node *ehnode;
+ struct hlist_node *match;
+ int intent_add = (intent & CFS_HS_LOOKUP_MASK_ADD) != 0;
+
+ /* with this function, we can avoid a lot of useless refcount ops,
+ * which are expensive atomic operations most time. */
+ match = intent_add ? NULL : hnode;
+ hlist_for_each(ehnode, hhead) {
+ if (!cfs_hash_keycmp(hs, key, ehnode))
+ continue;
+
+ if (match != NULL && match != ehnode) /* can't match */
+ continue;
+
+ /* match and ... */
+ if ((intent & CFS_HS_LOOKUP_MASK_DEL) != 0) {
+ cfs_hash_bd_del_locked(hs, bd, ehnode);
+ return ehnode;
+ }
+
+ /* caller wants refcount? */
+ if ((intent & CFS_HS_LOOKUP_MASK_REF) != 0)
+ cfs_hash_get(hs, ehnode);
+ return ehnode;
+ }
+ /* no match item */
+ if (!intent_add)
+ return NULL;
+
+ LASSERT(hnode != NULL);
+ cfs_hash_bd_add_locked(hs, bd, hnode);
+ return hnode;
+}
+
+struct hlist_node *
+cfs_hash_bd_lookup_locked(struct cfs_hash *hs, struct cfs_hash_bd *bd, const void *key)
+{
+ return cfs_hash_bd_lookup_intent(hs, bd, key, NULL,
+ CFS_HS_LOOKUP_IT_FIND);
+}
+EXPORT_SYMBOL(cfs_hash_bd_lookup_locked);
+
+struct hlist_node *
+cfs_hash_bd_peek_locked(struct cfs_hash *hs, struct cfs_hash_bd *bd, const void *key)
+{
+ return cfs_hash_bd_lookup_intent(hs, bd, key, NULL,
+ CFS_HS_LOOKUP_IT_PEEK);
+}
+EXPORT_SYMBOL(cfs_hash_bd_peek_locked);
+
+struct hlist_node *
+cfs_hash_bd_findadd_locked(struct cfs_hash *hs, struct cfs_hash_bd *bd,
+ const void *key, struct hlist_node *hnode,
+ int noref)
+{
+ return cfs_hash_bd_lookup_intent(hs, bd, key, hnode,
+ CFS_HS_LOOKUP_IT_ADD |
+ (!noref * CFS_HS_LOOKUP_MASK_REF));
+}
+EXPORT_SYMBOL(cfs_hash_bd_findadd_locked);
+
+struct hlist_node *
+cfs_hash_bd_finddel_locked(struct cfs_hash *hs, struct cfs_hash_bd *bd,
+ const void *key, struct hlist_node *hnode)
+{
+ /* hnode can be NULL, we find the first item with @key */
+ return cfs_hash_bd_lookup_intent(hs, bd, key, hnode,
+ CFS_HS_LOOKUP_IT_FINDDEL);
+}
+EXPORT_SYMBOL(cfs_hash_bd_finddel_locked);
+
+static void
+cfs_hash_multi_bd_lock(struct cfs_hash *hs, struct cfs_hash_bd *bds,
+ unsigned n, int excl)
+{
+ struct cfs_hash_bucket *prev = NULL;
+ int i;
+
+ /**
+ * bds must be ascendantly ordered by bd->bd_bucket->hsb_index.
+ * NB: it's possible that several bds point to the same bucket but
+ * have different bd::bd_offset, so need take care of deadlock.
+ */
+ cfs_hash_for_each_bd(bds, n, i) {
+ if (prev == bds[i].bd_bucket)
+ continue;
+
+ LASSERT(prev == NULL ||
+ prev->hsb_index < bds[i].bd_bucket->hsb_index);
+ cfs_hash_bd_lock(hs, &bds[i], excl);
+ prev = bds[i].bd_bucket;
+ }
+}
+
+static void
+cfs_hash_multi_bd_unlock(struct cfs_hash *hs, struct cfs_hash_bd *bds,
+ unsigned n, int excl)
+{
+ struct cfs_hash_bucket *prev = NULL;
+ int i;
+
+ cfs_hash_for_each_bd(bds, n, i) {
+ if (prev != bds[i].bd_bucket) {
+ cfs_hash_bd_unlock(hs, &bds[i], excl);
+ prev = bds[i].bd_bucket;
+ }
+ }
+}
+
+static struct hlist_node *
+cfs_hash_multi_bd_lookup_locked(struct cfs_hash *hs, struct cfs_hash_bd *bds,
+ unsigned n, const void *key)
+{
+ struct hlist_node *ehnode;
+ unsigned i;
+
+ cfs_hash_for_each_bd(bds, n, i) {
+ ehnode = cfs_hash_bd_lookup_intent(hs, &bds[i], key, NULL,
+ CFS_HS_LOOKUP_IT_FIND);
+ if (ehnode != NULL)
+ return ehnode;
+ }
+ return NULL;
+}
+
+static struct hlist_node *
+cfs_hash_multi_bd_findadd_locked(struct cfs_hash *hs,
+ struct cfs_hash_bd *bds, unsigned n, const void *key,
+ struct hlist_node *hnode, int noref)
+{
+ struct hlist_node *ehnode;
+ int intent;
+ unsigned i;
+
+ LASSERT(hnode != NULL);
+ intent = CFS_HS_LOOKUP_IT_PEEK | (!noref * CFS_HS_LOOKUP_MASK_REF);
+
+ cfs_hash_for_each_bd(bds, n, i) {
+ ehnode = cfs_hash_bd_lookup_intent(hs, &bds[i], key,
+ NULL, intent);
+ if (ehnode != NULL)
+ return ehnode;
+ }
+
+ if (i == 1) { /* only one bucket */
+ cfs_hash_bd_add_locked(hs, &bds[0], hnode);
+ } else {
+ struct cfs_hash_bd mybd;
+
+ cfs_hash_bd_get(hs, key, &mybd);
+ cfs_hash_bd_add_locked(hs, &mybd, hnode);
+ }
+
+ return hnode;
+}
+
+static struct hlist_node *
+cfs_hash_multi_bd_finddel_locked(struct cfs_hash *hs, struct cfs_hash_bd *bds,
+ unsigned n, const void *key,
+ struct hlist_node *hnode)
+{
+ struct hlist_node *ehnode;
+ unsigned i;
+
+ cfs_hash_for_each_bd(bds, n, i) {
+ ehnode = cfs_hash_bd_lookup_intent(hs, &bds[i], key, hnode,
+ CFS_HS_LOOKUP_IT_FINDDEL);
+ if (ehnode != NULL)
+ return ehnode;
+ }
+ return NULL;
+}
+
+static void
+cfs_hash_bd_order(struct cfs_hash_bd *bd1, struct cfs_hash_bd *bd2)
+{
+ int rc;
+
+ if (bd2->bd_bucket == NULL)
+ return;
+
+ if (bd1->bd_bucket == NULL) {
+ *bd1 = *bd2;
+ bd2->bd_bucket = NULL;
+ return;
+ }
+
+ rc = cfs_hash_bd_compare(bd1, bd2);
+ if (rc == 0) {
+ bd2->bd_bucket = NULL;
+
+ } else if (rc > 0) { /* swab bd1 and bd2 */
+ struct cfs_hash_bd tmp;
+
+ tmp = *bd2;
+ *bd2 = *bd1;
+ *bd1 = tmp;
+ }
+}
+
+void
+cfs_hash_dual_bd_get(struct cfs_hash *hs, const void *key, struct cfs_hash_bd *bds)
+{
+ /* NB: caller should hold hs_lock.rw if REHASH is set */
+ cfs_hash_bd_from_key(hs, hs->hs_buckets,
+ hs->hs_cur_bits, key, &bds[0]);
+ if (likely(hs->hs_rehash_buckets == NULL)) {
+ /* no rehash or not rehashing */
+ bds[1].bd_bucket = NULL;
+ return;
+ }
+
+ LASSERT(hs->hs_rehash_bits != 0);
+ cfs_hash_bd_from_key(hs, hs->hs_rehash_buckets,
+ hs->hs_rehash_bits, key, &bds[1]);
+
+ cfs_hash_bd_order(&bds[0], &bds[1]);
+}
+EXPORT_SYMBOL(cfs_hash_dual_bd_get);
+
+void
+cfs_hash_dual_bd_lock(struct cfs_hash *hs, struct cfs_hash_bd *bds, int excl)
+{
+ cfs_hash_multi_bd_lock(hs, bds, 2, excl);
+}
+EXPORT_SYMBOL(cfs_hash_dual_bd_lock);
+
+void
+cfs_hash_dual_bd_unlock(struct cfs_hash *hs, struct cfs_hash_bd *bds, int excl)
+{
+ cfs_hash_multi_bd_unlock(hs, bds, 2, excl);
+}
+EXPORT_SYMBOL(cfs_hash_dual_bd_unlock);
+
+struct hlist_node *
+cfs_hash_dual_bd_lookup_locked(struct cfs_hash *hs, struct cfs_hash_bd *bds,
+ const void *key)
+{
+ return cfs_hash_multi_bd_lookup_locked(hs, bds, 2, key);
+}
+EXPORT_SYMBOL(cfs_hash_dual_bd_lookup_locked);
+
+struct hlist_node *
+cfs_hash_dual_bd_findadd_locked(struct cfs_hash *hs, struct cfs_hash_bd *bds,
+ const void *key, struct hlist_node *hnode,
+ int noref)
+{
+ return cfs_hash_multi_bd_findadd_locked(hs, bds, 2, key,
+ hnode, noref);
+}
+EXPORT_SYMBOL(cfs_hash_dual_bd_findadd_locked);
+
+struct hlist_node *
+cfs_hash_dual_bd_finddel_locked(struct cfs_hash *hs, struct cfs_hash_bd *bds,
+ const void *key, struct hlist_node *hnode)
+{
+ return cfs_hash_multi_bd_finddel_locked(hs, bds, 2, key, hnode);
+}
+EXPORT_SYMBOL(cfs_hash_dual_bd_finddel_locked);
+
+static void
+cfs_hash_buckets_free(struct cfs_hash_bucket **buckets,
+ int bkt_size, int prev_size, int size)
+{
+ int i;
+
+ for (i = prev_size; i < size; i++) {
+ if (buckets[i] != NULL)
+ LIBCFS_FREE(buckets[i], bkt_size);
+ }
+
+ LIBCFS_FREE(buckets, sizeof(buckets[0]) * size);
+}
+
+/*
+ * Create or grow bucket memory. Return old_buckets if no allocation was
+ * needed, the newly allocated buckets if allocation was needed and
+ * successful, and NULL on error.
+ */
+static struct cfs_hash_bucket **
+cfs_hash_buckets_realloc(struct cfs_hash *hs, struct cfs_hash_bucket **old_bkts,
+ unsigned int old_size, unsigned int new_size)
+{
+ struct cfs_hash_bucket **new_bkts;
+ int i;
+
+ LASSERT(old_size == 0 || old_bkts != NULL);
+
+ if (old_bkts != NULL && old_size == new_size)
+ return old_bkts;
+
+ LIBCFS_ALLOC(new_bkts, sizeof(new_bkts[0]) * new_size);
+ if (new_bkts == NULL)
+ return NULL;
+
+ if (old_bkts != NULL) {
+ memcpy(new_bkts, old_bkts,
+ min(old_size, new_size) * sizeof(*old_bkts));
+ }
+
+ for (i = old_size; i < new_size; i++) {
+ struct hlist_head *hhead;
+ struct cfs_hash_bd bd;
+
+ LIBCFS_ALLOC(new_bkts[i], cfs_hash_bkt_size(hs));
+ if (new_bkts[i] == NULL) {
+ cfs_hash_buckets_free(new_bkts, cfs_hash_bkt_size(hs),
+ old_size, new_size);
+ return NULL;
+ }
+
+ new_bkts[i]->hsb_index = i;
+ new_bkts[i]->hsb_version = 1; /* shouldn't be zero */
+ new_bkts[i]->hsb_depmax = -1; /* unknown */
+ bd.bd_bucket = new_bkts[i];
+ cfs_hash_bd_for_each_hlist(hs, &bd, hhead)
+ INIT_HLIST_HEAD(hhead);
+
+ if (cfs_hash_with_no_lock(hs) ||
+ cfs_hash_with_no_bktlock(hs))
+ continue;
+
+ if (cfs_hash_with_rw_bktlock(hs))
+ rwlock_init(&new_bkts[i]->hsb_lock.rw);
+ else if (cfs_hash_with_spin_bktlock(hs))
+ spin_lock_init(&new_bkts[i]->hsb_lock.spin);
+ else
+ LBUG(); /* invalid use-case */
+ }
+ return new_bkts;
+}
+
+/**
+ * Initialize new libcfs hash, where:
+ * @name - Descriptive hash name
+ * @cur_bits - Initial hash table size, in bits
+ * @max_bits - Maximum allowed hash table resize, in bits
+ * @ops - Registered hash table operations
+ * @flags - CFS_HASH_REHASH enable synamic hash resizing
+ * - CFS_HASH_SORT enable chained hash sort
+ */
+static int cfs_hash_rehash_worker(cfs_workitem_t *wi);
+
+#if CFS_HASH_DEBUG_LEVEL >= CFS_HASH_DEBUG_1
+static int cfs_hash_dep_print(cfs_workitem_t *wi)
+{
+ struct cfs_hash *hs = container_of(wi, struct cfs_hash, hs_dep_wi);
+ int dep;
+ int bkt;
+ int off;
+ int bits;
+
+ spin_lock(&hs->hs_dep_lock);
+ dep = hs->hs_dep_max;
+ bkt = hs->hs_dep_bkt;
+ off = hs->hs_dep_off;
+ bits = hs->hs_dep_bits;
+ spin_unlock(&hs->hs_dep_lock);
+
+ LCONSOLE_WARN("#### HASH %s (bits: %d): max depth %d at bucket %d/%d\n",
+ hs->hs_name, bits, dep, bkt, off);
+ spin_lock(&hs->hs_dep_lock);
+ hs->hs_dep_bits = 0; /* mark as workitem done */
+ spin_unlock(&hs->hs_dep_lock);
+ return 0;
+}
+
+static void cfs_hash_depth_wi_init(struct cfs_hash *hs)
+{
+ spin_lock_init(&hs->hs_dep_lock);
+ cfs_wi_init(&hs->hs_dep_wi, hs, cfs_hash_dep_print);
+}
+
+static void cfs_hash_depth_wi_cancel(struct cfs_hash *hs)
+{
+ if (cfs_wi_deschedule(cfs_sched_rehash, &hs->hs_dep_wi))
+ return;
+
+ spin_lock(&hs->hs_dep_lock);
+ while (hs->hs_dep_bits != 0) {
+ spin_unlock(&hs->hs_dep_lock);
+ cond_resched();
+ spin_lock(&hs->hs_dep_lock);
+ }
+ spin_unlock(&hs->hs_dep_lock);
+}
+
+#else /* CFS_HASH_DEBUG_LEVEL < CFS_HASH_DEBUG_1 */
+
+static inline void cfs_hash_depth_wi_init(struct cfs_hash *hs) {}
+static inline void cfs_hash_depth_wi_cancel(struct cfs_hash *hs) {}
+
+#endif /* CFS_HASH_DEBUG_LEVEL >= CFS_HASH_DEBUG_1 */
+
+struct cfs_hash *
+cfs_hash_create(char *name, unsigned cur_bits, unsigned max_bits,
+ unsigned bkt_bits, unsigned extra_bytes,
+ unsigned min_theta, unsigned max_theta,
+ cfs_hash_ops_t *ops, unsigned flags)
+{
+ struct cfs_hash *hs;
+ int len;
+
+ CLASSERT(CFS_HASH_THETA_BITS < 15);
+
+ LASSERT(name != NULL);
+ LASSERT(ops != NULL);
+ LASSERT(ops->hs_key);
+ LASSERT(ops->hs_hash);
+ LASSERT(ops->hs_object);
+ LASSERT(ops->hs_keycmp);
+ LASSERT(ops->hs_get != NULL);
+ LASSERT(ops->hs_put_locked != NULL);
+
+ if ((flags & CFS_HASH_REHASH) != 0)
+ flags |= CFS_HASH_COUNTER; /* must have counter */
+
+ LASSERT(cur_bits > 0);
+ LASSERT(cur_bits >= bkt_bits);
+ LASSERT(max_bits >= cur_bits && max_bits < 31);
+ LASSERT(ergo((flags & CFS_HASH_REHASH) == 0, cur_bits == max_bits));
+ LASSERT(ergo((flags & CFS_HASH_REHASH) != 0,
+ (flags & CFS_HASH_NO_LOCK) == 0));
+ LASSERT(ergo((flags & CFS_HASH_REHASH_KEY) != 0,
+ ops->hs_keycpy != NULL));
+
+ len = (flags & CFS_HASH_BIGNAME) == 0 ?
+ CFS_HASH_NAME_LEN : CFS_HASH_BIGNAME_LEN;
+ LIBCFS_ALLOC(hs, offsetof(struct cfs_hash, hs_name[len]));
+ if (hs == NULL)
+ return NULL;
+
+ strncpy(hs->hs_name, name, len);
+ hs->hs_name[len - 1] = '\0';
+ hs->hs_flags = flags;
+
+ atomic_set(&hs->hs_refcount, 1);
+ atomic_set(&hs->hs_count, 0);
+
+ cfs_hash_lock_setup(hs);
+ cfs_hash_hlist_setup(hs);
+
+ hs->hs_cur_bits = (__u8)cur_bits;
+ hs->hs_min_bits = (__u8)cur_bits;
+ hs->hs_max_bits = (__u8)max_bits;
+ hs->hs_bkt_bits = (__u8)bkt_bits;
+
+ hs->hs_ops = ops;
+ hs->hs_extra_bytes = extra_bytes;
+ hs->hs_rehash_bits = 0;
+ cfs_wi_init(&hs->hs_rehash_wi, hs, cfs_hash_rehash_worker);
+ cfs_hash_depth_wi_init(hs);
+
+ if (cfs_hash_with_rehash(hs))
+ __cfs_hash_set_theta(hs, min_theta, max_theta);
+
+ hs->hs_buckets = cfs_hash_buckets_realloc(hs, NULL, 0,
+ CFS_HASH_NBKT(hs));
+ if (hs->hs_buckets != NULL)
+ return hs;
+
+ LIBCFS_FREE(hs, offsetof(struct cfs_hash, hs_name[len]));
+ return NULL;
+}
+EXPORT_SYMBOL(cfs_hash_create);
+
+/**
+ * Cleanup libcfs hash @hs.
+ */
+static void
+cfs_hash_destroy(struct cfs_hash *hs)
+{
+ struct hlist_node *hnode;
+ struct hlist_node *pos;
+ struct cfs_hash_bd bd;
+ int i;
+
+ LASSERT(hs != NULL);
+ LASSERT(!cfs_hash_is_exiting(hs) &&
+ !cfs_hash_is_iterating(hs));
+
+ /**
+ * prohibit further rehashes, don't need any lock because
+ * I'm the only (last) one can change it.
+ */
+ hs->hs_exiting = 1;
+ if (cfs_hash_with_rehash(hs))
+ cfs_hash_rehash_cancel(hs);
+
+ cfs_hash_depth_wi_cancel(hs);
+ /* rehash should be done/canceled */
+ LASSERT(hs->hs_buckets != NULL &&
+ hs->hs_rehash_buckets == NULL);
+
+ cfs_hash_for_each_bucket(hs, &bd, i) {
+ struct hlist_head *hhead;
+
+ LASSERT(bd.bd_bucket != NULL);
+ /* no need to take this lock, just for consistent code */
+ cfs_hash_bd_lock(hs, &bd, 1);
+
+ cfs_hash_bd_for_each_hlist(hs, &bd, hhead) {
+ hlist_for_each_safe(hnode, pos, hhead) {
+ LASSERTF(!cfs_hash_with_assert_empty(hs),
+ "hash %s bucket %u(%u) is not empty: %u items left\n",
+ hs->hs_name, bd.bd_bucket->hsb_index,
+ bd.bd_offset, bd.bd_bucket->hsb_count);
+ /* can't assert key valicate, because we
+ * can interrupt rehash */
+ cfs_hash_bd_del_locked(hs, &bd, hnode);
+ cfs_hash_exit(hs, hnode);
+ }
+ }
+ LASSERT(bd.bd_bucket->hsb_count == 0);
+ cfs_hash_bd_unlock(hs, &bd, 1);
+ cond_resched();
+ }
+
+ LASSERT(atomic_read(&hs->hs_count) == 0);
+
+ cfs_hash_buckets_free(hs->hs_buckets, cfs_hash_bkt_size(hs),
+ 0, CFS_HASH_NBKT(hs));
+ i = cfs_hash_with_bigname(hs) ?
+ CFS_HASH_BIGNAME_LEN : CFS_HASH_NAME_LEN;
+ LIBCFS_FREE(hs, offsetof(struct cfs_hash, hs_name[i]));
+}
+
+struct cfs_hash *cfs_hash_getref(struct cfs_hash *hs)
+{
+ if (atomic_inc_not_zero(&hs->hs_refcount))
+ return hs;
+ return NULL;
+}
+EXPORT_SYMBOL(cfs_hash_getref);
+
+void cfs_hash_putref(struct cfs_hash *hs)
+{
+ if (atomic_dec_and_test(&hs->hs_refcount))
+ cfs_hash_destroy(hs);
+}
+EXPORT_SYMBOL(cfs_hash_putref);
+
+static inline int
+cfs_hash_rehash_bits(struct cfs_hash *hs)
+{
+ if (cfs_hash_with_no_lock(hs) ||
+ !cfs_hash_with_rehash(hs))
+ return -EOPNOTSUPP;
+
+ if (unlikely(cfs_hash_is_exiting(hs)))
+ return -ESRCH;
+
+ if (unlikely(cfs_hash_is_rehashing(hs)))
+ return -EALREADY;
+
+ if (unlikely(cfs_hash_is_iterating(hs)))
+ return -EAGAIN;
+
+ /* XXX: need to handle case with max_theta != 2.0
+ * and the case with min_theta != 0.5 */
+ if ((hs->hs_cur_bits < hs->hs_max_bits) &&
+ (__cfs_hash_theta(hs) > hs->hs_max_theta))
+ return hs->hs_cur_bits + 1;
+
+ if (!cfs_hash_with_shrink(hs))
+ return 0;
+
+ if ((hs->hs_cur_bits > hs->hs_min_bits) &&
+ (__cfs_hash_theta(hs) < hs->hs_min_theta))
+ return hs->hs_cur_bits - 1;
+
+ return 0;
+}
+
+/**
+ * don't allow inline rehash if:
+ * - user wants non-blocking change (add/del) on hash table
+ * - too many elements
+ */
+static inline int
+cfs_hash_rehash_inline(struct cfs_hash *hs)
+{
+ return !cfs_hash_with_nblk_change(hs) &&
+ atomic_read(&hs->hs_count) < CFS_HASH_LOOP_HOG;
+}
+
+/**
+ * Add item @hnode to libcfs hash @hs using @key. The registered
+ * ops->hs_get function will be called when the item is added.
+ */
+void
+cfs_hash_add(struct cfs_hash *hs, const void *key, struct hlist_node *hnode)
+{
+ struct cfs_hash_bd bd;
+ int bits;
+
+ LASSERT(hlist_unhashed(hnode));
+
+ cfs_hash_lock(hs, 0);
+ cfs_hash_bd_get_and_lock(hs, key, &bd, 1);
+
+ cfs_hash_key_validate(hs, key, hnode);
+ cfs_hash_bd_add_locked(hs, &bd, hnode);
+
+ cfs_hash_bd_unlock(hs, &bd, 1);
+
+ bits = cfs_hash_rehash_bits(hs);
+ cfs_hash_unlock(hs, 0);
+ if (bits > 0)
+ cfs_hash_rehash(hs, cfs_hash_rehash_inline(hs));
+}
+EXPORT_SYMBOL(cfs_hash_add);
+
+static struct hlist_node *
+cfs_hash_find_or_add(struct cfs_hash *hs, const void *key,
+ struct hlist_node *hnode, int noref)
+{
+ struct hlist_node *ehnode;
+ struct cfs_hash_bd bds[2];
+ int bits = 0;
+
+ LASSERT(hlist_unhashed(hnode));
+
+ cfs_hash_lock(hs, 0);
+ cfs_hash_dual_bd_get_and_lock(hs, key, bds, 1);
+
+ cfs_hash_key_validate(hs, key, hnode);
+ ehnode = cfs_hash_dual_bd_findadd_locked(hs, bds, key,
+ hnode, noref);
+ cfs_hash_dual_bd_unlock(hs, bds, 1);
+
+ if (ehnode == hnode) /* new item added */
+ bits = cfs_hash_rehash_bits(hs);
+ cfs_hash_unlock(hs, 0);
+ if (bits > 0)
+ cfs_hash_rehash(hs, cfs_hash_rehash_inline(hs));
+
+ return ehnode;
+}
+
+/**
+ * Add item @hnode to libcfs hash @hs using @key. The registered
+ * ops->hs_get function will be called if the item was added.
+ * Returns 0 on success or -EALREADY on key collisions.
+ */
+int
+cfs_hash_add_unique(struct cfs_hash *hs, const void *key, struct hlist_node *hnode)
+{
+ return cfs_hash_find_or_add(hs, key, hnode, 1) != hnode ?
+ -EALREADY : 0;
+}
+EXPORT_SYMBOL(cfs_hash_add_unique);
+
+/**
+ * Add item @hnode to libcfs hash @hs using @key. If this @key
+ * already exists in the hash then ops->hs_get will be called on the
+ * conflicting entry and that entry will be returned to the caller.
+ * Otherwise ops->hs_get is called on the item which was added.
+ */
+void *
+cfs_hash_findadd_unique(struct cfs_hash *hs, const void *key,
+ struct hlist_node *hnode)
+{
+ hnode = cfs_hash_find_or_add(hs, key, hnode, 0);
+
+ return cfs_hash_object(hs, hnode);
+}
+EXPORT_SYMBOL(cfs_hash_findadd_unique);
+
+/**
+ * Delete item @hnode from the libcfs hash @hs using @key. The @key
+ * is required to ensure the correct hash bucket is locked since there
+ * is no direct linkage from the item to the bucket. The object
+ * removed from the hash will be returned and obs->hs_put is called
+ * on the removed object.
+ */
+void *
+cfs_hash_del(struct cfs_hash *hs, const void *key, struct hlist_node *hnode)
+{
+ void *obj = NULL;
+ int bits = 0;
+ struct cfs_hash_bd bds[2];
+
+ cfs_hash_lock(hs, 0);
+ cfs_hash_dual_bd_get_and_lock(hs, key, bds, 1);
+
+ /* NB: do nothing if @hnode is not in hash table */
+ if (hnode == NULL || !hlist_unhashed(hnode)) {
+ if (bds[1].bd_bucket == NULL && hnode != NULL) {
+ cfs_hash_bd_del_locked(hs, &bds[0], hnode);
+ } else {
+ hnode = cfs_hash_dual_bd_finddel_locked(hs, bds,
+ key, hnode);
+ }
+ }
+
+ if (hnode != NULL) {
+ obj = cfs_hash_object(hs, hnode);
+ bits = cfs_hash_rehash_bits(hs);
+ }
+
+ cfs_hash_dual_bd_unlock(hs, bds, 1);
+ cfs_hash_unlock(hs, 0);
+ if (bits > 0)
+ cfs_hash_rehash(hs, cfs_hash_rehash_inline(hs));
+
+ return obj;
+}
+EXPORT_SYMBOL(cfs_hash_del);
+
+/**
+ * Delete item given @key in libcfs hash @hs. The first @key found in
+ * the hash will be removed, if the key exists multiple times in the hash
+ * @hs this function must be called once per key. The removed object
+ * will be returned and ops->hs_put is called on the removed object.
+ */
+void *
+cfs_hash_del_key(struct cfs_hash *hs, const void *key)
+{
+ return cfs_hash_del(hs, key, NULL);
+}
+EXPORT_SYMBOL(cfs_hash_del_key);
+
+/**
+ * Lookup an item using @key in the libcfs hash @hs and return it.
+ * If the @key is found in the hash hs->hs_get() is called and the
+ * matching objects is returned. It is the callers responsibility
+ * to call the counterpart ops->hs_put using the cfs_hash_put() macro
+ * when when finished with the object. If the @key was not found
+ * in the hash @hs NULL is returned.
+ */
+void *
+cfs_hash_lookup(struct cfs_hash *hs, const void *key)
+{
+ void *obj = NULL;
+ struct hlist_node *hnode;
+ struct cfs_hash_bd bds[2];
+
+ cfs_hash_lock(hs, 0);
+ cfs_hash_dual_bd_get_and_lock(hs, key, bds, 0);
+
+ hnode = cfs_hash_dual_bd_lookup_locked(hs, bds, key);
+ if (hnode != NULL)
+ obj = cfs_hash_object(hs, hnode);
+
+ cfs_hash_dual_bd_unlock(hs, bds, 0);
+ cfs_hash_unlock(hs, 0);
+
+ return obj;
+}
+EXPORT_SYMBOL(cfs_hash_lookup);
+
+static void
+cfs_hash_for_each_enter(struct cfs_hash *hs) {
+ LASSERT(!cfs_hash_is_exiting(hs));
+
+ if (!cfs_hash_with_rehash(hs))
+ return;
+ /*
+ * NB: it's race on cfs_has_t::hs_iterating, but doesn't matter
+ * because it's just an unreliable signal to rehash-thread,
+ * rehash-thread will try to finish rehash ASAP when seeing this.
+ */
+ hs->hs_iterating = 1;
+
+ cfs_hash_lock(hs, 1);
+ hs->hs_iterators++;
+
+ /* NB: iteration is mostly called by service thread,
+ * we tend to cancel pending rehash-request, instead of
+ * blocking service thread, we will relaunch rehash request
+ * after iteration */
+ if (cfs_hash_is_rehashing(hs))
+ cfs_hash_rehash_cancel_locked(hs);
+ cfs_hash_unlock(hs, 1);
+}
+
+static void
+cfs_hash_for_each_exit(struct cfs_hash *hs) {
+ int remained;
+ int bits;
+
+ if (!cfs_hash_with_rehash(hs))
+ return;
+ cfs_hash_lock(hs, 1);
+ remained = --hs->hs_iterators;
+ bits = cfs_hash_rehash_bits(hs);
+ cfs_hash_unlock(hs, 1);
+ /* NB: it's race on cfs_has_t::hs_iterating, see above */
+ if (remained == 0)
+ hs->hs_iterating = 0;
+ if (bits > 0) {
+ cfs_hash_rehash(hs, atomic_read(&hs->hs_count) <
+ CFS_HASH_LOOP_HOG);
+ }
+}
+
+/**
+ * For each item in the libcfs hash @hs call the passed callback @func
+ * and pass to it as an argument each hash item and the private @data.
+ *
+ * a) the function may sleep!
+ * b) during the callback:
+ * . the bucket lock is held so the callback must never sleep.
+ * . if @removal_safe is true, use can remove current item by
+ * cfs_hash_bd_del_locked
+ */
+static __u64
+cfs_hash_for_each_tight(struct cfs_hash *hs, cfs_hash_for_each_cb_t func,
+ void *data, int remove_safe) {
+ struct hlist_node *hnode;
+ struct hlist_node *pos;
+ struct cfs_hash_bd bd;
+ __u64 count = 0;
+ int excl = !!remove_safe;
+ int loop = 0;
+ int i;
+
+ cfs_hash_for_each_enter(hs);
+
+ cfs_hash_lock(hs, 0);
+ LASSERT(!cfs_hash_is_rehashing(hs));
+
+ cfs_hash_for_each_bucket(hs, &bd, i) {
+ struct hlist_head *hhead;
+
+ cfs_hash_bd_lock(hs, &bd, excl);
+ if (func == NULL) { /* only glimpse size */
+ count += bd.bd_bucket->hsb_count;
+ cfs_hash_bd_unlock(hs, &bd, excl);
+ continue;
+ }
+
+ cfs_hash_bd_for_each_hlist(hs, &bd, hhead) {
+ hlist_for_each_safe(hnode, pos, hhead) {
+ cfs_hash_bucket_validate(hs, &bd, hnode);
+ count++;
+ loop++;
+ if (func(hs, &bd, hnode, data)) {
+ cfs_hash_bd_unlock(hs, &bd, excl);
+ goto out;
+ }
+ }
+ }
+ cfs_hash_bd_unlock(hs, &bd, excl);
+ if (loop < CFS_HASH_LOOP_HOG)
+ continue;
+ loop = 0;
+ cfs_hash_unlock(hs, 0);
+ cond_resched();
+ cfs_hash_lock(hs, 0);
+ }
+ out:
+ cfs_hash_unlock(hs, 0);
+
+ cfs_hash_for_each_exit(hs);
+ return count;
+}
+
+typedef struct {
+ cfs_hash_cond_opt_cb_t func;
+ void *arg;
+} cfs_hash_cond_arg_t;
+
+static int
+cfs_hash_cond_del_locked(struct cfs_hash *hs, struct cfs_hash_bd *bd,
+ struct hlist_node *hnode, void *data)
+{
+ cfs_hash_cond_arg_t *cond = data;
+
+ if (cond->func(cfs_hash_object(hs, hnode), cond->arg))
+ cfs_hash_bd_del_locked(hs, bd, hnode);
+ return 0;
+}
+
+/**
+ * Delete item from the libcfs hash @hs when @func return true.
+ * The write lock being hold during loop for each bucket to avoid
+ * any object be reference.
+ */
+void
+cfs_hash_cond_del(struct cfs_hash *hs, cfs_hash_cond_opt_cb_t func, void *data)
+{
+ cfs_hash_cond_arg_t arg = {
+ .func = func,
+ .arg = data,
+ };
+
+ cfs_hash_for_each_tight(hs, cfs_hash_cond_del_locked, &arg, 1);
+}
+EXPORT_SYMBOL(cfs_hash_cond_del);
+
+void
+cfs_hash_for_each(struct cfs_hash *hs,
+ cfs_hash_for_each_cb_t func, void *data)
+{
+ cfs_hash_for_each_tight(hs, func, data, 0);
+}
+EXPORT_SYMBOL(cfs_hash_for_each);
+
+void
+cfs_hash_for_each_safe(struct cfs_hash *hs,
+ cfs_hash_for_each_cb_t func, void *data) {
+ cfs_hash_for_each_tight(hs, func, data, 1);
+}
+EXPORT_SYMBOL(cfs_hash_for_each_safe);
+
+static int
+cfs_hash_peek(struct cfs_hash *hs, struct cfs_hash_bd *bd,
+ struct hlist_node *hnode, void *data)
+{
+ *(int *)data = 0;
+ return 1; /* return 1 to break the loop */
+}
+
+int
+cfs_hash_is_empty(struct cfs_hash *hs)
+{
+ int empty = 1;
+
+ cfs_hash_for_each_tight(hs, cfs_hash_peek, &empty, 0);
+ return empty;
+}
+EXPORT_SYMBOL(cfs_hash_is_empty);
+
+__u64
+cfs_hash_size_get(struct cfs_hash *hs)
+{
+ return cfs_hash_with_counter(hs) ?
+ atomic_read(&hs->hs_count) :
+ cfs_hash_for_each_tight(hs, NULL, NULL, 0);
+}
+EXPORT_SYMBOL(cfs_hash_size_get);
+
+/*
+ * cfs_hash_for_each_relax:
+ * Iterate the hash table and call @func on each item without
+ * any lock. This function can't guarantee to finish iteration
+ * if these features are enabled:
+ *
+ * a. if rehash_key is enabled, an item can be moved from
+ * one bucket to another bucket
+ * b. user can remove non-zero-ref item from hash-table,
+ * so the item can be removed from hash-table, even worse,
+ * it's possible that user changed key and insert to another
+ * hash bucket.
+ * there's no way for us to finish iteration correctly on previous
+ * two cases, so iteration has to be stopped on change.
+ */
+static int
+cfs_hash_for_each_relax(struct cfs_hash *hs, cfs_hash_for_each_cb_t func,
+ void *data) {
+ struct hlist_node *hnode;
+ struct hlist_node *tmp;
+ struct cfs_hash_bd bd;
+ __u32 version;
+ int count = 0;
+ int stop_on_change;
+ int rc;
+ int i;
+
+ stop_on_change = cfs_hash_with_rehash_key(hs) ||
+ !cfs_hash_with_no_itemref(hs) ||
+ hs->hs_ops->hs_put_locked == NULL;
+ cfs_hash_lock(hs, 0);
+ LASSERT(!cfs_hash_is_rehashing(hs));
+
+ cfs_hash_for_each_bucket(hs, &bd, i) {
+ struct hlist_head *hhead;
+
+ cfs_hash_bd_lock(hs, &bd, 0);
+ version = cfs_hash_bd_version_get(&bd);
+
+ cfs_hash_bd_for_each_hlist(hs, &bd, hhead) {
+ for (hnode = hhead->first; hnode != NULL;) {
+ cfs_hash_bucket_validate(hs, &bd, hnode);
+ cfs_hash_get(hs, hnode);
+ cfs_hash_bd_unlock(hs, &bd, 0);
+ cfs_hash_unlock(hs, 0);
+
+ rc = func(hs, &bd, hnode, data);
+ if (stop_on_change)
+ cfs_hash_put(hs, hnode);
+ cond_resched();
+ count++;
+
+ cfs_hash_lock(hs, 0);
+ cfs_hash_bd_lock(hs, &bd, 0);
+ if (!stop_on_change) {
+ tmp = hnode->next;
+ cfs_hash_put_locked(hs, hnode);
+ hnode = tmp;
+ } else { /* bucket changed? */
+ if (version !=
+ cfs_hash_bd_version_get(&bd))
+ break;
+ /* safe to continue because no change */
+ hnode = hnode->next;
+ }
+ if (rc) /* callback wants to break iteration */
+ break;
+ }
+ }
+ cfs_hash_bd_unlock(hs, &bd, 0);
+ }
+ cfs_hash_unlock(hs, 0);
+
+ return count;
+}
+
+int
+cfs_hash_for_each_nolock(struct cfs_hash *hs,
+ cfs_hash_for_each_cb_t func, void *data) {
+ if (cfs_hash_with_no_lock(hs) ||
+ cfs_hash_with_rehash_key(hs) ||
+ !cfs_hash_with_no_itemref(hs))
+ return -EOPNOTSUPP;
+
+ if (hs->hs_ops->hs_get == NULL ||
+ (hs->hs_ops->hs_put == NULL &&
+ hs->hs_ops->hs_put_locked == NULL))
+ return -EOPNOTSUPP;
+
+ cfs_hash_for_each_enter(hs);
+ cfs_hash_for_each_relax(hs, func, data);
+ cfs_hash_for_each_exit(hs);
+
+ return 0;
+}
+EXPORT_SYMBOL(cfs_hash_for_each_nolock);
+
+/**
+ * For each hash bucket in the libcfs hash @hs call the passed callback
+ * @func until all the hash buckets are empty. The passed callback @func
+ * or the previously registered callback hs->hs_put must remove the item
+ * from the hash. You may either use the cfs_hash_del() or hlist_del()
+ * functions. No rwlocks will be held during the callback @func it is
+ * safe to sleep if needed. This function will not terminate until the
+ * hash is empty. Note it is still possible to concurrently add new
+ * items in to the hash. It is the callers responsibility to ensure
+ * the required locking is in place to prevent concurrent insertions.
+ */
+int
+cfs_hash_for_each_empty(struct cfs_hash *hs,
+ cfs_hash_for_each_cb_t func, void *data) {
+ unsigned i = 0;
+
+ if (cfs_hash_with_no_lock(hs))
+ return -EOPNOTSUPP;
+
+ if (hs->hs_ops->hs_get == NULL ||
+ (hs->hs_ops->hs_put == NULL &&
+ hs->hs_ops->hs_put_locked == NULL))
+ return -EOPNOTSUPP;
+
+ cfs_hash_for_each_enter(hs);
+ while (cfs_hash_for_each_relax(hs, func, data)) {
+ CDEBUG(D_INFO, "Try to empty hash: %s, loop: %u\n",
+ hs->hs_name, i++);
+ }
+ cfs_hash_for_each_exit(hs);
+ return 0;
+}
+EXPORT_SYMBOL(cfs_hash_for_each_empty);
+
+void
+cfs_hash_hlist_for_each(struct cfs_hash *hs, unsigned hindex,
+ cfs_hash_for_each_cb_t func, void *data)
+{
+ struct hlist_head *hhead;
+ struct hlist_node *hnode;
+ struct cfs_hash_bd bd;
+
+ cfs_hash_for_each_enter(hs);
+ cfs_hash_lock(hs, 0);
+ if (hindex >= CFS_HASH_NHLIST(hs))
+ goto out;
+
+ cfs_hash_bd_index_set(hs, hindex, &bd);
+
+ cfs_hash_bd_lock(hs, &bd, 0);
+ hhead = cfs_hash_bd_hhead(hs, &bd);
+ hlist_for_each(hnode, hhead) {
+ if (func(hs, &bd, hnode, data))
+ break;
+ }
+ cfs_hash_bd_unlock(hs, &bd, 0);
+ out:
+ cfs_hash_unlock(hs, 0);
+ cfs_hash_for_each_exit(hs);
+}
+
+EXPORT_SYMBOL(cfs_hash_hlist_for_each);
+
+/*
+ * For each item in the libcfs hash @hs which matches the @key call
+ * the passed callback @func and pass to it as an argument each hash
+ * item and the private @data. During the callback the bucket lock
+ * is held so the callback must never sleep.
+ */
+void
+cfs_hash_for_each_key(struct cfs_hash *hs, const void *key,
+ cfs_hash_for_each_cb_t func, void *data) {
+ struct hlist_node *hnode;
+ struct cfs_hash_bd bds[2];
+ unsigned i;
+
+ cfs_hash_lock(hs, 0);
+
+ cfs_hash_dual_bd_get_and_lock(hs, key, bds, 0);
+
+ cfs_hash_for_each_bd(bds, 2, i) {
+ struct hlist_head *hlist = cfs_hash_bd_hhead(hs, &bds[i]);
+
+ hlist_for_each(hnode, hlist) {
+ cfs_hash_bucket_validate(hs, &bds[i], hnode);
+
+ if (cfs_hash_keycmp(hs, key, hnode)) {
+ if (func(hs, &bds[i], hnode, data))
+ break;
+ }
+ }
+ }
+
+ cfs_hash_dual_bd_unlock(hs, bds, 0);
+ cfs_hash_unlock(hs, 0);
+}
+EXPORT_SYMBOL(cfs_hash_for_each_key);
+
+/**
+ * Rehash the libcfs hash @hs to the given @bits. This can be used
+ * to grow the hash size when excessive chaining is detected, or to
+ * shrink the hash when it is larger than needed. When the CFS_HASH_REHASH
+ * flag is set in @hs the libcfs hash may be dynamically rehashed
+ * during addition or removal if the hash's theta value exceeds
+ * either the hs->hs_min_theta or hs->max_theta values. By default
+ * these values are tuned to keep the chained hash depth small, and
+ * this approach assumes a reasonably uniform hashing function. The
+ * theta thresholds for @hs are tunable via cfs_hash_set_theta().
+ */
+void
+cfs_hash_rehash_cancel_locked(struct cfs_hash *hs)
+{
+ int i;
+
+ /* need hold cfs_hash_lock(hs, 1) */
+ LASSERT(cfs_hash_with_rehash(hs) &&
+ !cfs_hash_with_no_lock(hs));
+
+ if (!cfs_hash_is_rehashing(hs))
+ return;
+
+ if (cfs_wi_deschedule(cfs_sched_rehash, &hs->hs_rehash_wi)) {
+ hs->hs_rehash_bits = 0;
+ return;
+ }
+
+ for (i = 2; cfs_hash_is_rehashing(hs); i++) {
+ cfs_hash_unlock(hs, 1);
+ /* raise console warning while waiting too long */
+ CDEBUG(IS_PO2(i >> 3) ? D_WARNING : D_INFO,
+ "hash %s is still rehashing, rescheded %d\n",
+ hs->hs_name, i - 1);
+ cond_resched();
+ cfs_hash_lock(hs, 1);
+ }
+}
+EXPORT_SYMBOL(cfs_hash_rehash_cancel_locked);
+
+void
+cfs_hash_rehash_cancel(struct cfs_hash *hs)
+{
+ cfs_hash_lock(hs, 1);
+ cfs_hash_rehash_cancel_locked(hs);
+ cfs_hash_unlock(hs, 1);
+}
+EXPORT_SYMBOL(cfs_hash_rehash_cancel);
+
+int
+cfs_hash_rehash(struct cfs_hash *hs, int do_rehash)
+{
+ int rc;
+
+ LASSERT(cfs_hash_with_rehash(hs) && !cfs_hash_with_no_lock(hs));
+
+ cfs_hash_lock(hs, 1);
+
+ rc = cfs_hash_rehash_bits(hs);
+ if (rc <= 0) {
+ cfs_hash_unlock(hs, 1);
+ return rc;
+ }
+
+ hs->hs_rehash_bits = rc;
+ if (!do_rehash) {
+ /* launch and return */
+ cfs_wi_schedule(cfs_sched_rehash, &hs->hs_rehash_wi);
+ cfs_hash_unlock(hs, 1);
+ return 0;
+ }
+
+ /* rehash right now */
+ cfs_hash_unlock(hs, 1);
+
+ return cfs_hash_rehash_worker(&hs->hs_rehash_wi);
+}
+EXPORT_SYMBOL(cfs_hash_rehash);
+
+static int
+cfs_hash_rehash_bd(struct cfs_hash *hs, struct cfs_hash_bd *old)
+{
+ struct cfs_hash_bd new;
+ struct hlist_head *hhead;
+ struct hlist_node *hnode;
+ struct hlist_node *pos;
+ void *key;
+ int c = 0;
+
+ /* hold cfs_hash_lock(hs, 1), so don't need any bucket lock */
+ cfs_hash_bd_for_each_hlist(hs, old, hhead) {
+ hlist_for_each_safe(hnode, pos, hhead) {
+ key = cfs_hash_key(hs, hnode);
+ LASSERT(key != NULL);
+ /* Validate hnode is in the correct bucket. */
+ cfs_hash_bucket_validate(hs, old, hnode);
+ /*
+ * Delete from old hash bucket; move to new bucket.
+ * ops->hs_key must be defined.
+ */
+ cfs_hash_bd_from_key(hs, hs->hs_rehash_buckets,
+ hs->hs_rehash_bits, key, &new);
+ cfs_hash_bd_move_locked(hs, old, &new, hnode);
+ c++;
+ }
+ }
+
+ return c;
+}
+
+static int
+cfs_hash_rehash_worker(cfs_workitem_t *wi)
+{
+ struct cfs_hash *hs = container_of(wi, struct cfs_hash, hs_rehash_wi);
+ struct cfs_hash_bucket **bkts;
+ struct cfs_hash_bd bd;
+ unsigned int old_size;
+ unsigned int new_size;
+ int bsize;
+ int count = 0;
+ int rc = 0;
+ int i;
+
+ LASSERT (hs != NULL && cfs_hash_with_rehash(hs));
+
+ cfs_hash_lock(hs, 0);
+ LASSERT(cfs_hash_is_rehashing(hs));
+
+ old_size = CFS_HASH_NBKT(hs);
+ new_size = CFS_HASH_RH_NBKT(hs);
+
+ cfs_hash_unlock(hs, 0);
+
+ /*
+ * don't need hs::hs_rwlock for hs::hs_buckets,
+ * because nobody can change bkt-table except me.
+ */
+ bkts = cfs_hash_buckets_realloc(hs, hs->hs_buckets,
+ old_size, new_size);
+ cfs_hash_lock(hs, 1);
+ if (bkts == NULL) {
+ rc = -ENOMEM;
+ goto out;
+ }
+
+ if (bkts == hs->hs_buckets) {
+ bkts = NULL; /* do nothing */
+ goto out;
+ }
+
+ rc = __cfs_hash_theta(hs);
+ if ((rc >= hs->hs_min_theta) && (rc <= hs->hs_max_theta)) {
+ /* free the new allocated bkt-table */
+ old_size = new_size;
+ new_size = CFS_HASH_NBKT(hs);
+ rc = -EALREADY;
+ goto out;
+ }
+
+ LASSERT(hs->hs_rehash_buckets == NULL);
+ hs->hs_rehash_buckets = bkts;
+
+ rc = 0;
+ cfs_hash_for_each_bucket(hs, &bd, i) {
+ if (cfs_hash_is_exiting(hs)) {
+ rc = -ESRCH;
+ /* someone wants to destroy the hash, abort now */
+ if (old_size < new_size) /* OK to free old bkt-table */
+ break;
+ /* it's shrinking, need free new bkt-table */
+ hs->hs_rehash_buckets = NULL;
+ old_size = new_size;
+ new_size = CFS_HASH_NBKT(hs);
+ goto out;
+ }
+
+ count += cfs_hash_rehash_bd(hs, &bd);
+ if (count < CFS_HASH_LOOP_HOG ||
+ cfs_hash_is_iterating(hs)) { /* need to finish ASAP */
+ continue;
+ }
+
+ count = 0;
+ cfs_hash_unlock(hs, 1);
+ cond_resched();
+ cfs_hash_lock(hs, 1);
+ }
+
+ hs->hs_rehash_count++;
+
+ bkts = hs->hs_buckets;
+ hs->hs_buckets = hs->hs_rehash_buckets;
+ hs->hs_rehash_buckets = NULL;
+
+ hs->hs_cur_bits = hs->hs_rehash_bits;
+ out:
+ hs->hs_rehash_bits = 0;
+ if (rc == -ESRCH) /* never be scheduled again */
+ cfs_wi_exit(cfs_sched_rehash, wi);
+ bsize = cfs_hash_bkt_size(hs);
+ cfs_hash_unlock(hs, 1);
+ /* can't refer to @hs anymore because it could be destroyed */
+ if (bkts != NULL)
+ cfs_hash_buckets_free(bkts, bsize, new_size, old_size);
+ if (rc != 0)
+ CDEBUG(D_INFO, "early quit of rehashing: %d\n", rc);
+ /* return 1 only if cfs_wi_exit is called */
+ return rc == -ESRCH;
+}
+
+/**
+ * Rehash the object referenced by @hnode in the libcfs hash @hs. The
+ * @old_key must be provided to locate the objects previous location
+ * in the hash, and the @new_key will be used to reinsert the object.
+ * Use this function instead of a cfs_hash_add() + cfs_hash_del()
+ * combo when it is critical that there is no window in time where the
+ * object is missing from the hash. When an object is being rehashed
+ * the registered cfs_hash_get() and cfs_hash_put() functions will
+ * not be called.
+ */
+void cfs_hash_rehash_key(struct cfs_hash *hs, const void *old_key,
+ void *new_key, struct hlist_node *hnode)
+{
+ struct cfs_hash_bd bds[3];
+ struct cfs_hash_bd old_bds[2];
+ struct cfs_hash_bd new_bd;
+
+ LASSERT(!hlist_unhashed(hnode));
+
+ cfs_hash_lock(hs, 0);
+
+ cfs_hash_dual_bd_get(hs, old_key, old_bds);
+ cfs_hash_bd_get(hs, new_key, &new_bd);
+
+ bds[0] = old_bds[0];
+ bds[1] = old_bds[1];
+ bds[2] = new_bd;
+
+ /* NB: bds[0] and bds[1] are ordered already */
+ cfs_hash_bd_order(&bds[1], &bds[2]);
+ cfs_hash_bd_order(&bds[0], &bds[1]);
+
+ cfs_hash_multi_bd_lock(hs, bds, 3, 1);
+ if (likely(old_bds[1].bd_bucket == NULL)) {
+ cfs_hash_bd_move_locked(hs, &old_bds[0], &new_bd, hnode);
+ } else {
+ cfs_hash_dual_bd_finddel_locked(hs, old_bds, old_key, hnode);
+ cfs_hash_bd_add_locked(hs, &new_bd, hnode);
+ }
+ /* overwrite key inside locks, otherwise may screw up with
+ * other operations, i.e: rehash */
+ cfs_hash_keycpy(hs, new_key, hnode);
+
+ cfs_hash_multi_bd_unlock(hs, bds, 3, 1);
+ cfs_hash_unlock(hs, 0);
+}
+EXPORT_SYMBOL(cfs_hash_rehash_key);
+
+void cfs_hash_debug_header(struct seq_file *m)
+{
+ seq_printf(m, "%-*s cur min max theta t-min t-max flags rehash count maxdep maxdepb distribution\n",
+ CFS_HASH_BIGNAME_LEN, "name");
+}
+EXPORT_SYMBOL(cfs_hash_debug_header);
+
+static struct cfs_hash_bucket **
+cfs_hash_full_bkts(struct cfs_hash *hs)
+{
+ /* NB: caller should hold hs->hs_rwlock if REHASH is set */
+ if (hs->hs_rehash_buckets == NULL)
+ return hs->hs_buckets;
+
+ LASSERT(hs->hs_rehash_bits != 0);
+ return hs->hs_rehash_bits > hs->hs_cur_bits ?
+ hs->hs_rehash_buckets : hs->hs_buckets;
+}
+
+static unsigned int
+cfs_hash_full_nbkt(struct cfs_hash *hs)
+{
+ /* NB: caller should hold hs->hs_rwlock if REHASH is set */
+ if (hs->hs_rehash_buckets == NULL)
+ return CFS_HASH_NBKT(hs);
+
+ LASSERT(hs->hs_rehash_bits != 0);
+ return hs->hs_rehash_bits > hs->hs_cur_bits ?
+ CFS_HASH_RH_NBKT(hs) : CFS_HASH_NBKT(hs);
+}
+
+void cfs_hash_debug_str(struct cfs_hash *hs, struct seq_file *m)
+{
+ int dist[8] = { 0, };
+ int maxdep = -1;
+ int maxdepb = -1;
+ int total = 0;
+ int theta;
+ int i;
+
+ cfs_hash_lock(hs, 0);
+ theta = __cfs_hash_theta(hs);
+
+ seq_printf(m, "%-*s %5d %5d %5d %d.%03d %d.%03d %d.%03d 0x%02x %6d ",
+ CFS_HASH_BIGNAME_LEN, hs->hs_name,
+ 1 << hs->hs_cur_bits, 1 << hs->hs_min_bits,
+ 1 << hs->hs_max_bits,
+ __cfs_hash_theta_int(theta), __cfs_hash_theta_frac(theta),
+ __cfs_hash_theta_int(hs->hs_min_theta),
+ __cfs_hash_theta_frac(hs->hs_min_theta),
+ __cfs_hash_theta_int(hs->hs_max_theta),
+ __cfs_hash_theta_frac(hs->hs_max_theta),
+ hs->hs_flags, hs->hs_rehash_count);
+
+ /*
+ * The distribution is a summary of the chained hash depth in
+ * each of the libcfs hash buckets. Each buckets hsb_count is
+ * divided by the hash theta value and used to generate a
+ * histogram of the hash distribution. A uniform hash will
+ * result in all hash buckets being close to the average thus
+ * only the first few entries in the histogram will be non-zero.
+ * If you hash function results in a non-uniform hash the will
+ * be observable by outlier bucks in the distribution histogram.
+ *
+ * Uniform hash distribution: 128/128/0/0/0/0/0/0
+ * Non-Uniform hash distribution: 128/125/0/0/0/0/2/1
+ */
+ for (i = 0; i < cfs_hash_full_nbkt(hs); i++) {
+ struct cfs_hash_bd bd;
+
+ bd.bd_bucket = cfs_hash_full_bkts(hs)[i];
+ cfs_hash_bd_lock(hs, &bd, 0);
+ if (maxdep < bd.bd_bucket->hsb_depmax) {
+ maxdep = bd.bd_bucket->hsb_depmax;
+ maxdepb = ffz(~maxdep);
+ }
+ total += bd.bd_bucket->hsb_count;
+ dist[min(fls(bd.bd_bucket->hsb_count / max(theta, 1)), 7)]++;
+ cfs_hash_bd_unlock(hs, &bd, 0);
+ }
+
+ seq_printf(m, "%7d %7d %7d ", total, maxdep, maxdepb);
+ for (i = 0; i < 8; i++)
+ seq_printf(m, "%d%c", dist[i], (i == 7) ? '\n' : '/');
+
+ cfs_hash_unlock(hs, 0);
+}
+EXPORT_SYMBOL(cfs_hash_debug_str);
diff --git a/drivers/staging/lustre/lustre/libcfs/kernel_user_comm.c b/drivers/staging/lustre/lustre/libcfs/kernel_user_comm.c
new file mode 100644
index 000000000..d9b7c6b69
--- /dev/null
+++ b/drivers/staging/lustre/lustre/libcfs/kernel_user_comm.c
@@ -0,0 +1,240 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2012, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ *
+ * Author: Nathan Rutman <nathan.rutman@sun.com>
+ *
+ * Kernel <-> userspace communication routines.
+ * Using pipes for all arches.
+ */
+
+#define DEBUG_SUBSYSTEM S_CLASS
+#define D_KUC D_OTHER
+
+#include "../../include/linux/libcfs/libcfs.h"
+
+/* This is the kernel side (liblustre as well). */
+
+/**
+ * libcfs_kkuc_msg_put - send an message from kernel to userspace
+ * @param fp to send the message to
+ * @param payload Payload data. First field of payload is always
+ * struct kuc_hdr
+ */
+int libcfs_kkuc_msg_put(struct file *filp, void *payload)
+{
+ struct kuc_hdr *kuch = (struct kuc_hdr *)payload;
+ ssize_t count = kuch->kuc_msglen;
+ loff_t offset = 0;
+ mm_segment_t fs;
+ int rc = -ENOSYS;
+
+ if (filp == NULL || IS_ERR(filp))
+ return -EBADF;
+
+ if (kuch->kuc_magic != KUC_MAGIC) {
+ CERROR("KernelComm: bad magic %x\n", kuch->kuc_magic);
+ return -ENOSYS;
+ }
+
+ fs = get_fs();
+ set_fs(KERNEL_DS);
+ while (count > 0) {
+ rc = vfs_write(filp, (void __force __user *)payload,
+ count, &offset);
+ if (rc < 0)
+ break;
+ count -= rc;
+ payload += rc;
+ rc = 0;
+ }
+ set_fs(fs);
+
+ if (rc < 0)
+ CWARN("message send failed (%d)\n", rc);
+ else
+ CDEBUG(D_KUC, "Sent message rc=%d, fp=%p\n", rc, filp);
+
+ return rc;
+}
+EXPORT_SYMBOL(libcfs_kkuc_msg_put);
+
+/* Broadcast groups are global across all mounted filesystems;
+ * i.e. registering for a group on 1 fs will get messages for that
+ * group from any fs */
+/** A single group registration has a uid and a file pointer */
+struct kkuc_reg {
+ struct list_head kr_chain;
+ int kr_uid;
+ struct file *kr_fp;
+ __u32 kr_data;
+};
+static struct list_head kkuc_groups[KUC_GRP_MAX+1] = {};
+/* Protect message sending against remove and adds */
+static DECLARE_RWSEM(kg_sem);
+
+/** Add a receiver to a broadcast group
+ * @param filp pipe to write into
+ * @param uid identifier for this receiver
+ * @param group group number
+ */
+int libcfs_kkuc_group_add(struct file *filp, int uid, int group, __u32 data)
+{
+ struct kkuc_reg *reg;
+
+ if (group > KUC_GRP_MAX) {
+ CDEBUG(D_WARNING, "Kernelcomm: bad group %d\n", group);
+ return -EINVAL;
+ }
+
+ /* fput in group_rem */
+ if (filp == NULL)
+ return -EBADF;
+
+ /* freed in group_rem */
+ reg = kmalloc(sizeof(*reg), 0);
+ if (reg == NULL)
+ return -ENOMEM;
+
+ reg->kr_fp = filp;
+ reg->kr_uid = uid;
+ reg->kr_data = data;
+
+ down_write(&kg_sem);
+ if (kkuc_groups[group].next == NULL)
+ INIT_LIST_HEAD(&kkuc_groups[group]);
+ list_add(&reg->kr_chain, &kkuc_groups[group]);
+ up_write(&kg_sem);
+
+ CDEBUG(D_KUC, "Added uid=%d fp=%p to group %d\n", uid, filp, group);
+
+ return 0;
+}
+EXPORT_SYMBOL(libcfs_kkuc_group_add);
+
+int libcfs_kkuc_group_rem(int uid, int group)
+{
+ struct kkuc_reg *reg, *next;
+
+ if (kkuc_groups[group].next == NULL)
+ return 0;
+
+ if (uid == 0) {
+ /* Broadcast a shutdown message */
+ struct kuc_hdr lh;
+
+ lh.kuc_magic = KUC_MAGIC;
+ lh.kuc_transport = KUC_TRANSPORT_GENERIC;
+ lh.kuc_msgtype = KUC_MSG_SHUTDOWN;
+ lh.kuc_msglen = sizeof(lh);
+ libcfs_kkuc_group_put(group, &lh);
+ }
+
+ down_write(&kg_sem);
+ list_for_each_entry_safe(reg, next, &kkuc_groups[group], kr_chain) {
+ if ((uid == 0) || (uid == reg->kr_uid)) {
+ list_del(&reg->kr_chain);
+ CDEBUG(D_KUC, "Removed uid=%d fp=%p from group %d\n",
+ reg->kr_uid, reg->kr_fp, group);
+ if (reg->kr_fp != NULL)
+ fput(reg->kr_fp);
+ kfree(reg);
+ }
+ }
+ up_write(&kg_sem);
+
+ return 0;
+}
+EXPORT_SYMBOL(libcfs_kkuc_group_rem);
+
+int libcfs_kkuc_group_put(int group, void *payload)
+{
+ struct kkuc_reg *reg;
+ int rc = 0;
+ int one_success = 0;
+
+ down_read(&kg_sem);
+ list_for_each_entry(reg, &kkuc_groups[group], kr_chain) {
+ if (reg->kr_fp != NULL) {
+ rc = libcfs_kkuc_msg_put(reg->kr_fp, payload);
+ if (rc == 0)
+ one_success = 1;
+ else if (rc == -EPIPE) {
+ fput(reg->kr_fp);
+ reg->kr_fp = NULL;
+ }
+ }
+ }
+ up_read(&kg_sem);
+
+ /* don't return an error if the message has been delivered
+ * at least to one agent */
+ if (one_success)
+ rc = 0;
+
+ return rc;
+}
+EXPORT_SYMBOL(libcfs_kkuc_group_put);
+
+/**
+ * Calls a callback function for each link of the given kuc group.
+ * @param group the group to call the function on.
+ * @param cb_func the function to be called.
+ * @param cb_arg iextra argument to be passed to the callback function.
+ */
+int libcfs_kkuc_group_foreach(int group, libcfs_kkuc_cb_t cb_func,
+ void *cb_arg)
+{
+ struct kkuc_reg *reg;
+ int rc = 0;
+
+ if (group > KUC_GRP_MAX) {
+ CDEBUG(D_WARNING, "Kernelcomm: bad group %d\n", group);
+ return -EINVAL;
+ }
+
+ /* no link for this group */
+ if (kkuc_groups[group].next == NULL)
+ return 0;
+
+ down_write(&kg_sem);
+ list_for_each_entry(reg, &kkuc_groups[group], kr_chain) {
+ if (reg->kr_fp != NULL)
+ rc = cb_func(reg->kr_data, cb_arg);
+ }
+ up_write(&kg_sem);
+
+ return rc;
+}
+EXPORT_SYMBOL(libcfs_kkuc_group_foreach);
diff --git a/drivers/staging/lustre/lustre/libcfs/libcfs_cpu.c b/drivers/staging/lustre/lustre/libcfs/libcfs_cpu.c
new file mode 100644
index 000000000..31a558115
--- /dev/null
+++ b/drivers/staging/lustre/lustre/libcfs/libcfs_cpu.c
@@ -0,0 +1,224 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 021110-1307, USA
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ *
+ * Please see comments in libcfs/include/libcfs/libcfs_cpu.h for introduction
+ *
+ * Author: liang@whamcloud.com
+ */
+
+#define DEBUG_SUBSYSTEM S_LNET
+
+#include "../../include/linux/libcfs/libcfs.h"
+
+/** Global CPU partition table */
+struct cfs_cpt_table *cfs_cpt_table __read_mostly;
+EXPORT_SYMBOL(cfs_cpt_table);
+
+#ifndef HAVE_LIBCFS_CPT
+
+#define CFS_CPU_VERSION_MAGIC 0xbabecafe
+
+struct cfs_cpt_table *
+cfs_cpt_table_alloc(unsigned int ncpt)
+{
+ struct cfs_cpt_table *cptab;
+
+ if (ncpt != 1) {
+ CERROR("Can't support cpu partition number %d\n", ncpt);
+ return NULL;
+ }
+
+ LIBCFS_ALLOC(cptab, sizeof(*cptab));
+ if (cptab != NULL) {
+ cptab->ctb_version = CFS_CPU_VERSION_MAGIC;
+ cptab->ctb_nparts = ncpt;
+ }
+
+ return cptab;
+}
+EXPORT_SYMBOL(cfs_cpt_table_alloc);
+
+void
+cfs_cpt_table_free(struct cfs_cpt_table *cptab)
+{
+ LASSERT(cptab->ctb_version == CFS_CPU_VERSION_MAGIC);
+
+ LIBCFS_FREE(cptab, sizeof(*cptab));
+}
+EXPORT_SYMBOL(cfs_cpt_table_free);
+
+#ifdef CONFIG_SMP
+int
+cfs_cpt_table_print(struct cfs_cpt_table *cptab, char *buf, int len)
+{
+ int rc = 0;
+
+ rc = snprintf(buf, len, "%d\t: %d\n", 0, 0);
+ len -= rc;
+ if (len <= 0)
+ return -EFBIG;
+
+ return rc;
+}
+EXPORT_SYMBOL(cfs_cpt_table_print);
+#endif /* CONFIG_SMP */
+
+int
+cfs_cpt_number(struct cfs_cpt_table *cptab)
+{
+ return 1;
+}
+EXPORT_SYMBOL(cfs_cpt_number);
+
+int
+cfs_cpt_weight(struct cfs_cpt_table *cptab, int cpt)
+{
+ return 1;
+}
+EXPORT_SYMBOL(cfs_cpt_weight);
+
+int
+cfs_cpt_online(struct cfs_cpt_table *cptab, int cpt)
+{
+ return 1;
+}
+EXPORT_SYMBOL(cfs_cpt_online);
+
+int
+cfs_cpt_set_cpu(struct cfs_cpt_table *cptab, int cpt, int cpu)
+{
+ return 1;
+}
+EXPORT_SYMBOL(cfs_cpt_set_cpu);
+
+void
+cfs_cpt_unset_cpu(struct cfs_cpt_table *cptab, int cpt, int cpu)
+{
+}
+EXPORT_SYMBOL(cfs_cpt_unset_cpu);
+
+int
+cfs_cpt_set_cpumask(struct cfs_cpt_table *cptab, int cpt, cpumask_t *mask)
+{
+ return 1;
+}
+EXPORT_SYMBOL(cfs_cpt_set_cpumask);
+
+void
+cfs_cpt_unset_cpumask(struct cfs_cpt_table *cptab, int cpt, cpumask_t *mask)
+{
+}
+EXPORT_SYMBOL(cfs_cpt_unset_cpumask);
+
+int
+cfs_cpt_set_node(struct cfs_cpt_table *cptab, int cpt, int node)
+{
+ return 1;
+}
+EXPORT_SYMBOL(cfs_cpt_set_node);
+
+void
+cfs_cpt_unset_node(struct cfs_cpt_table *cptab, int cpt, int node)
+{
+}
+EXPORT_SYMBOL(cfs_cpt_unset_node);
+
+int
+cfs_cpt_set_nodemask(struct cfs_cpt_table *cptab, int cpt, nodemask_t *mask)
+{
+ return 1;
+}
+EXPORT_SYMBOL(cfs_cpt_set_nodemask);
+
+void
+cfs_cpt_unset_nodemask(struct cfs_cpt_table *cptab, int cpt, nodemask_t *mask)
+{
+}
+EXPORT_SYMBOL(cfs_cpt_unset_nodemask);
+
+void
+cfs_cpt_clear(struct cfs_cpt_table *cptab, int cpt)
+{
+}
+EXPORT_SYMBOL(cfs_cpt_clear);
+
+int
+cfs_cpt_spread_node(struct cfs_cpt_table *cptab, int cpt)
+{
+ return 0;
+}
+EXPORT_SYMBOL(cfs_cpt_spread_node);
+
+int
+cfs_cpu_ht_nsiblings(int cpu)
+{
+ return 1;
+}
+EXPORT_SYMBOL(cfs_cpu_ht_nsiblings);
+
+int
+cfs_cpt_current(struct cfs_cpt_table *cptab, int remap)
+{
+ return 0;
+}
+EXPORT_SYMBOL(cfs_cpt_current);
+
+int
+cfs_cpt_of_cpu(struct cfs_cpt_table *cptab, int cpu)
+{
+ return 0;
+}
+EXPORT_SYMBOL(cfs_cpt_of_cpu);
+
+int
+cfs_cpt_bind(struct cfs_cpt_table *cptab, int cpt)
+{
+ return 0;
+}
+EXPORT_SYMBOL(cfs_cpt_bind);
+
+void
+cfs_cpu_fini(void)
+{
+ if (cfs_cpt_table != NULL) {
+ cfs_cpt_table_free(cfs_cpt_table);
+ cfs_cpt_table = NULL;
+ }
+}
+
+int
+cfs_cpu_init(void)
+{
+ cfs_cpt_table = cfs_cpt_table_alloc(1);
+
+ return cfs_cpt_table != NULL ? 0 : -1;
+}
+
+#endif /* HAVE_LIBCFS_CPT */
diff --git a/drivers/staging/lustre/lustre/libcfs/libcfs_lock.c b/drivers/staging/lustre/lustre/libcfs/libcfs_lock.c
new file mode 100644
index 000000000..2c199c725
--- /dev/null
+++ b/drivers/staging/lustre/lustre/libcfs/libcfs_lock.c
@@ -0,0 +1,189 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 021110-1307, USA
+ *
+ * GPL HEADER END
+ */
+/* Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ *
+ * Author: liang@whamcloud.com
+ */
+
+#define DEBUG_SUBSYSTEM S_LNET
+
+#include "../../include/linux/libcfs/libcfs.h"
+
+
+/** destroy cpu-partition lock, see libcfs_private.h for more detail */
+void
+cfs_percpt_lock_free(struct cfs_percpt_lock *pcl)
+{
+ LASSERT(pcl->pcl_locks != NULL);
+ LASSERT(!pcl->pcl_locked);
+
+ cfs_percpt_free(pcl->pcl_locks);
+ LIBCFS_FREE(pcl, sizeof(*pcl));
+}
+EXPORT_SYMBOL(cfs_percpt_lock_free);
+
+/**
+ * create cpu-partition lock, see libcfs_private.h for more detail.
+ *
+ * cpu-partition lock is designed for large-scale SMP system, so we need to
+ * reduce cacheline conflict as possible as we can, that's the
+ * reason we always allocate cacheline-aligned memory block.
+ */
+struct cfs_percpt_lock *
+cfs_percpt_lock_alloc(struct cfs_cpt_table *cptab)
+{
+ struct cfs_percpt_lock *pcl;
+ spinlock_t *lock;
+ int i;
+
+ /* NB: cptab can be NULL, pcl will be for HW CPUs on that case */
+ LIBCFS_ALLOC(pcl, sizeof(*pcl));
+ if (pcl == NULL)
+ return NULL;
+
+ pcl->pcl_cptab = cptab;
+ pcl->pcl_locks = cfs_percpt_alloc(cptab, sizeof(*lock));
+ if (pcl->pcl_locks == NULL) {
+ LIBCFS_FREE(pcl, sizeof(*pcl));
+ return NULL;
+ }
+
+ cfs_percpt_for_each(lock, i, pcl->pcl_locks)
+ spin_lock_init(lock);
+
+ return pcl;
+}
+EXPORT_SYMBOL(cfs_percpt_lock_alloc);
+
+/**
+ * lock a CPU partition
+ *
+ * \a index != CFS_PERCPT_LOCK_EX
+ * hold private lock indexed by \a index
+ *
+ * \a index == CFS_PERCPT_LOCK_EX
+ * exclusively lock @pcl and nobody can take private lock
+ */
+void
+cfs_percpt_lock(struct cfs_percpt_lock *pcl, int index)
+{
+ int ncpt = cfs_cpt_number(pcl->pcl_cptab);
+ int i;
+
+ LASSERT(index >= CFS_PERCPT_LOCK_EX && index < ncpt);
+
+ if (ncpt == 1) {
+ index = 0;
+ } else { /* serialize with exclusive lock */
+ while (pcl->pcl_locked)
+ cpu_relax();
+ }
+
+ if (likely(index != CFS_PERCPT_LOCK_EX)) {
+ spin_lock(pcl->pcl_locks[index]);
+ return;
+ }
+
+ /* exclusive lock request */
+ for (i = 0; i < ncpt; i++) {
+ spin_lock(pcl->pcl_locks[i]);
+ if (i == 0) {
+ LASSERT(!pcl->pcl_locked);
+ /* nobody should take private lock after this
+ * so I wouldn't starve for too long time */
+ pcl->pcl_locked = 1;
+ }
+ }
+}
+EXPORT_SYMBOL(cfs_percpt_lock);
+
+/** unlock a CPU partition */
+void
+cfs_percpt_unlock(struct cfs_percpt_lock *pcl, int index)
+{
+ int ncpt = cfs_cpt_number(pcl->pcl_cptab);
+ int i;
+
+ index = ncpt == 1 ? 0 : index;
+
+ if (likely(index != CFS_PERCPT_LOCK_EX)) {
+ spin_unlock(pcl->pcl_locks[index]);
+ return;
+ }
+
+ for (i = ncpt - 1; i >= 0; i--) {
+ if (i == 0) {
+ LASSERT(pcl->pcl_locked);
+ pcl->pcl_locked = 0;
+ }
+ spin_unlock(pcl->pcl_locks[i]);
+ }
+}
+EXPORT_SYMBOL(cfs_percpt_unlock);
+
+
+/** free cpu-partition refcount */
+void
+cfs_percpt_atomic_free(atomic_t **refs)
+{
+ cfs_percpt_free(refs);
+}
+EXPORT_SYMBOL(cfs_percpt_atomic_free);
+
+/** allocate cpu-partition refcount with initial value @init_val */
+atomic_t **
+cfs_percpt_atomic_alloc(struct cfs_cpt_table *cptab, int init_val)
+{
+ atomic_t **refs;
+ atomic_t *ref;
+ int i;
+
+ refs = cfs_percpt_alloc(cptab, sizeof(*ref));
+ if (refs == NULL)
+ return NULL;
+
+ cfs_percpt_for_each(ref, i, refs)
+ atomic_set(ref, init_val);
+ return refs;
+}
+EXPORT_SYMBOL(cfs_percpt_atomic_alloc);
+
+/** return sum of cpu-partition refs */
+int
+cfs_percpt_atomic_summary(atomic_t **refs)
+{
+ atomic_t *ref;
+ int i;
+ int val = 0;
+
+ cfs_percpt_for_each(ref, i, refs)
+ val += atomic_read(ref);
+
+ return val;
+}
+EXPORT_SYMBOL(cfs_percpt_atomic_summary);
diff --git a/drivers/staging/lustre/lustre/libcfs/libcfs_mem.c b/drivers/staging/lustre/lustre/libcfs/libcfs_mem.c
new file mode 100644
index 000000000..1debdda72
--- /dev/null
+++ b/drivers/staging/lustre/lustre/libcfs/libcfs_mem.c
@@ -0,0 +1,202 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 021110-1307, USA
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ *
+ * Author: liang@whamcloud.com
+ */
+
+#define DEBUG_SUBSYSTEM S_LNET
+
+#include "../../include/linux/libcfs/libcfs.h"
+
+struct cfs_var_array {
+ unsigned int va_count; /* # of buffers */
+ unsigned int va_size; /* size of each var */
+ struct cfs_cpt_table *va_cptab; /* cpu partition table */
+ void *va_ptrs[0]; /* buffer addresses */
+};
+
+/*
+ * free per-cpu data, see more detail in cfs_percpt_free
+ */
+void
+cfs_percpt_free(void *vars)
+{
+ struct cfs_var_array *arr;
+ int i;
+
+ arr = container_of(vars, struct cfs_var_array, va_ptrs[0]);
+
+ for (i = 0; i < arr->va_count; i++) {
+ if (arr->va_ptrs[i] != NULL)
+ LIBCFS_FREE(arr->va_ptrs[i], arr->va_size);
+ }
+
+ LIBCFS_FREE(arr, offsetof(struct cfs_var_array,
+ va_ptrs[arr->va_count]));
+}
+EXPORT_SYMBOL(cfs_percpt_free);
+
+/*
+ * allocate per cpu-partition variables, returned value is an array of pointers,
+ * variable can be indexed by CPU partition ID, i.e:
+ *
+ * arr = cfs_percpt_alloc(cfs_cpu_pt, size);
+ * then caller can access memory block for CPU 0 by arr[0],
+ * memory block for CPU 1 by arr[1]...
+ * memory block for CPU N by arr[N]...
+ *
+ * cacheline aligned.
+ */
+void *
+cfs_percpt_alloc(struct cfs_cpt_table *cptab, unsigned int size)
+{
+ struct cfs_var_array *arr;
+ int count;
+ int i;
+
+ count = cfs_cpt_number(cptab);
+
+ LIBCFS_ALLOC(arr, offsetof(struct cfs_var_array, va_ptrs[count]));
+ if (arr == NULL)
+ return NULL;
+
+ arr->va_size = size = L1_CACHE_ALIGN(size);
+ arr->va_count = count;
+ arr->va_cptab = cptab;
+
+ for (i = 0; i < count; i++) {
+ LIBCFS_CPT_ALLOC(arr->va_ptrs[i], cptab, i, size);
+ if (arr->va_ptrs[i] == NULL) {
+ cfs_percpt_free((void *)&arr->va_ptrs[0]);
+ return NULL;
+ }
+ }
+
+ return (void *)&arr->va_ptrs[0];
+}
+EXPORT_SYMBOL(cfs_percpt_alloc);
+
+/*
+ * return number of CPUs (or number of elements in per-cpu data)
+ * according to cptab of @vars
+ */
+int
+cfs_percpt_number(void *vars)
+{
+ struct cfs_var_array *arr;
+
+ arr = container_of(vars, struct cfs_var_array, va_ptrs[0]);
+
+ return arr->va_count;
+}
+EXPORT_SYMBOL(cfs_percpt_number);
+
+/*
+ * return memory block shadowed from current CPU
+ */
+void *
+cfs_percpt_current(void *vars)
+{
+ struct cfs_var_array *arr;
+ int cpt;
+
+ arr = container_of(vars, struct cfs_var_array, va_ptrs[0]);
+ cpt = cfs_cpt_current(arr->va_cptab, 0);
+ if (cpt < 0)
+ return NULL;
+
+ return arr->va_ptrs[cpt];
+}
+EXPORT_SYMBOL(cfs_percpt_current);
+
+void *
+cfs_percpt_index(void *vars, int idx)
+{
+ struct cfs_var_array *arr;
+
+ arr = container_of(vars, struct cfs_var_array, va_ptrs[0]);
+
+ LASSERT(idx >= 0 && idx < arr->va_count);
+ return arr->va_ptrs[idx];
+}
+EXPORT_SYMBOL(cfs_percpt_index);
+
+/*
+ * free variable array, see more detail in cfs_array_alloc
+ */
+void
+cfs_array_free(void *vars)
+{
+ struct cfs_var_array *arr;
+ int i;
+
+ arr = container_of(vars, struct cfs_var_array, va_ptrs[0]);
+
+ for (i = 0; i < arr->va_count; i++) {
+ if (arr->va_ptrs[i] == NULL)
+ continue;
+
+ LIBCFS_FREE(arr->va_ptrs[i], arr->va_size);
+ }
+ LIBCFS_FREE(arr, offsetof(struct cfs_var_array,
+ va_ptrs[arr->va_count]));
+}
+EXPORT_SYMBOL(cfs_array_free);
+
+/*
+ * allocate a variable array, returned value is an array of pointers.
+ * Caller can specify length of array by @count, @size is size of each
+ * memory block in array.
+ */
+void *
+cfs_array_alloc(int count, unsigned int size)
+{
+ struct cfs_var_array *arr;
+ int i;
+
+ LIBCFS_ALLOC(arr, offsetof(struct cfs_var_array, va_ptrs[count]));
+ if (arr == NULL)
+ return NULL;
+
+ arr->va_count = count;
+ arr->va_size = size;
+
+ for (i = 0; i < count; i++) {
+ LIBCFS_ALLOC(arr->va_ptrs[i], size);
+
+ if (arr->va_ptrs[i] == NULL) {
+ cfs_array_free((void *)&arr->va_ptrs[0]);
+ return NULL;
+ }
+ }
+
+ return (void *)&arr->va_ptrs[0];
+}
+EXPORT_SYMBOL(cfs_array_alloc);
diff --git a/drivers/staging/lustre/lustre/libcfs/libcfs_string.c b/drivers/staging/lustre/lustre/libcfs/libcfs_string.c
new file mode 100644
index 000000000..76d4392bd
--- /dev/null
+++ b/drivers/staging/lustre/lustre/libcfs/libcfs_string.c
@@ -0,0 +1,562 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2012, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ *
+ * String manipulation functions.
+ *
+ * libcfs/libcfs/libcfs_string.c
+ *
+ * Author: Nathan Rutman <nathan.rutman@sun.com>
+ */
+
+#include "../../include/linux/libcfs/libcfs.h"
+
+/* Convert a text string to a bitmask */
+int cfs_str2mask(const char *str, const char *(*bit2str)(int bit),
+ int *oldmask, int minmask, int allmask)
+{
+ const char *debugstr;
+ char op = '\0';
+ int newmask = minmask, i, len, found = 0;
+
+ /* <str> must be a list of tokens separated by whitespace
+ * and optionally an operator ('+' or '-'). If an operator
+ * appears first in <str>, '*oldmask' is used as the starting point
+ * (relative), otherwise minmask is used (absolute). An operator
+ * applies to all following tokens up to the next operator. */
+ while (*str != '\0') {
+ while (isspace(*str))
+ str++;
+ if (*str == '\0')
+ break;
+ if (*str == '+' || *str == '-') {
+ op = *str++;
+ if (!found)
+ /* only if first token is relative */
+ newmask = *oldmask;
+ while (isspace(*str))
+ str++;
+ if (*str == '\0') /* trailing op */
+ return -EINVAL;
+ }
+
+ /* find token length */
+ len = 0;
+ while (str[len] != '\0' && !isspace(str[len]) &&
+ str[len] != '+' && str[len] != '-')
+ len++;
+
+ /* match token */
+ found = 0;
+ for (i = 0; i < 32; i++) {
+ debugstr = bit2str(i);
+ if (debugstr != NULL &&
+ strlen(debugstr) == len &&
+ strncasecmp(str, debugstr, len) == 0) {
+ if (op == '-')
+ newmask &= ~(1 << i);
+ else
+ newmask |= (1 << i);
+ found = 1;
+ break;
+ }
+ }
+ if (!found && len == 3 &&
+ (strncasecmp(str, "ALL", len) == 0)) {
+ if (op == '-')
+ newmask = minmask;
+ else
+ newmask = allmask;
+ found = 1;
+ }
+ if (!found) {
+ CWARN("unknown mask '%.*s'.\n"
+ "mask usage: [+|-]<all|type> ...\n", len, str);
+ return -EINVAL;
+ }
+ str += len;
+ }
+
+ *oldmask = newmask;
+ return 0;
+}
+
+/* get the first string out of @str */
+char *cfs_firststr(char *str, size_t size)
+{
+ size_t i = 0;
+ char *end;
+
+ /* trim leading spaces */
+ while (i < size && *str && isspace(*str)) {
+ ++i;
+ ++str;
+ }
+
+ /* string with all spaces */
+ if (*str == '\0')
+ goto out;
+
+ end = str;
+ while (i < size && *end != '\0' && !isspace(*end)) {
+ ++i;
+ ++end;
+ }
+
+ *end = '\0';
+out:
+ return str;
+}
+EXPORT_SYMBOL(cfs_firststr);
+
+char *
+cfs_trimwhite(char *str)
+{
+ char *end;
+
+ while (isspace(*str))
+ str++;
+
+ end = str + strlen(str);
+ while (end > str) {
+ if (!isspace(end[-1]))
+ break;
+ end--;
+ }
+
+ *end = 0;
+ return str;
+}
+EXPORT_SYMBOL(cfs_trimwhite);
+
+/**
+ * Extracts tokens from strings.
+ *
+ * Looks for \a delim in string \a next, sets \a res to point to
+ * substring before the delimiter, sets \a next right after the found
+ * delimiter.
+ *
+ * \retval 1 if \a res points to a string of non-whitespace characters
+ * \retval 0 otherwise
+ */
+int
+cfs_gettok(struct cfs_lstr *next, char delim, struct cfs_lstr *res)
+{
+ char *end;
+
+ if (next->ls_str == NULL)
+ return 0;
+
+ /* skip leading white spaces */
+ while (next->ls_len) {
+ if (!isspace(*next->ls_str))
+ break;
+ next->ls_str++;
+ next->ls_len--;
+ }
+
+ if (next->ls_len == 0) /* whitespaces only */
+ return 0;
+
+ if (*next->ls_str == delim) {
+ /* first non-writespace is the delimiter */
+ return 0;
+ }
+
+ res->ls_str = next->ls_str;
+ end = memchr(next->ls_str, delim, next->ls_len);
+ if (end == NULL) {
+ /* there is no the delimeter in the string */
+ end = next->ls_str + next->ls_len;
+ next->ls_str = NULL;
+ } else {
+ next->ls_str = end + 1;
+ next->ls_len -= (end - res->ls_str + 1);
+ }
+
+ /* skip ending whitespaces */
+ while (--end != res->ls_str) {
+ if (!isspace(*end))
+ break;
+ }
+
+ res->ls_len = end - res->ls_str + 1;
+ return 1;
+}
+
+/**
+ * Converts string to integer.
+ *
+ * Accepts decimal and hexadecimal number recordings.
+ *
+ * \retval 1 if first \a nob chars of \a str convert to decimal or
+ * hexadecimal integer in the range [\a min, \a max]
+ * \retval 0 otherwise
+ */
+int
+cfs_str2num_check(char *str, int nob, unsigned *num,
+ unsigned min, unsigned max)
+{
+ char *endp;
+
+ str = cfs_trimwhite(str);
+ *num = strtoul(str, &endp, 0);
+ if (endp == str)
+ return 0;
+
+ for (; endp < str + nob; endp++) {
+ if (!isspace(*endp))
+ return 0;
+ }
+
+ return (*num >= min && *num <= max);
+}
+
+/**
+ * Parses \<range_expr\> token of the syntax. If \a bracketed is false,
+ * \a src should only have a single token which can be \<number\> or \*
+ *
+ * \retval pointer to allocated range_expr and initialized
+ * range_expr::re_lo, range_expr::re_hi and range_expr:re_stride if \a
+ `* src parses to
+ * \<number\> |
+ * \<number\> '-' \<number\> |
+ * \<number\> '-' \<number\> '/' \<number\>
+ * \retval 0 will be returned if it can be parsed, otherwise -EINVAL or
+ * -ENOMEM will be returned.
+ */
+static int
+cfs_range_expr_parse(struct cfs_lstr *src, unsigned min, unsigned max,
+ int bracketed, struct cfs_range_expr **expr)
+{
+ struct cfs_range_expr *re;
+ struct cfs_lstr tok;
+
+ LIBCFS_ALLOC(re, sizeof(*re));
+ if (re == NULL)
+ return -ENOMEM;
+
+ if (src->ls_len == 1 && src->ls_str[0] == '*') {
+ re->re_lo = min;
+ re->re_hi = max;
+ re->re_stride = 1;
+ goto out;
+ }
+
+ if (cfs_str2num_check(src->ls_str, src->ls_len,
+ &re->re_lo, min, max)) {
+ /* <number> is parsed */
+ re->re_hi = re->re_lo;
+ re->re_stride = 1;
+ goto out;
+ }
+
+ if (!bracketed || !cfs_gettok(src, '-', &tok))
+ goto failed;
+
+ if (!cfs_str2num_check(tok.ls_str, tok.ls_len,
+ &re->re_lo, min, max))
+ goto failed;
+
+ /* <number> - */
+ if (cfs_str2num_check(src->ls_str, src->ls_len,
+ &re->re_hi, min, max)) {
+ /* <number> - <number> is parsed */
+ re->re_stride = 1;
+ goto out;
+ }
+
+ /* go to check <number> '-' <number> '/' <number> */
+ if (cfs_gettok(src, '/', &tok)) {
+ if (!cfs_str2num_check(tok.ls_str, tok.ls_len,
+ &re->re_hi, min, max))
+ goto failed;
+
+ /* <number> - <number> / ... */
+ if (cfs_str2num_check(src->ls_str, src->ls_len,
+ &re->re_stride, min, max)) {
+ /* <number> - <number> / <number> is parsed */
+ goto out;
+ }
+ }
+
+ out:
+ *expr = re;
+ return 0;
+
+ failed:
+ LIBCFS_FREE(re, sizeof(*re));
+ return -EINVAL;
+}
+
+/**
+ * Matches value (\a value) against ranges expression list \a expr_list.
+ *
+ * \retval 1 if \a value matches
+ * \retval 0 otherwise
+ */
+int
+cfs_expr_list_match(__u32 value, struct cfs_expr_list *expr_list)
+{
+ struct cfs_range_expr *expr;
+
+ list_for_each_entry(expr, &expr_list->el_exprs, re_link) {
+ if (value >= expr->re_lo && value <= expr->re_hi &&
+ ((value - expr->re_lo) % expr->re_stride) == 0)
+ return 1;
+ }
+
+ return 0;
+}
+
+/**
+ * Convert express list (\a expr_list) to an array of all matched values
+ *
+ * \retval N N is total number of all matched values
+ * \retval 0 if expression list is empty
+ * \retval < 0 for failure
+ */
+int
+cfs_expr_list_values(struct cfs_expr_list *expr_list, int max, __u32 **valpp)
+{
+ struct cfs_range_expr *expr;
+ __u32 *val;
+ int count = 0;
+ int i;
+
+ list_for_each_entry(expr, &expr_list->el_exprs, re_link) {
+ for (i = expr->re_lo; i <= expr->re_hi; i++) {
+ if (((i - expr->re_lo) % expr->re_stride) == 0)
+ count++;
+ }
+ }
+
+ if (count == 0) /* empty expression list */
+ return 0;
+
+ if (count > max) {
+ CERROR("Number of values %d exceeds max allowed %d\n",
+ max, count);
+ return -EINVAL;
+ }
+
+ LIBCFS_ALLOC(val, sizeof(val[0]) * count);
+ if (val == NULL)
+ return -ENOMEM;
+
+ count = 0;
+ list_for_each_entry(expr, &expr_list->el_exprs, re_link) {
+ for (i = expr->re_lo; i <= expr->re_hi; i++) {
+ if (((i - expr->re_lo) % expr->re_stride) == 0)
+ val[count++] = i;
+ }
+ }
+
+ *valpp = val;
+ return count;
+}
+EXPORT_SYMBOL(cfs_expr_list_values);
+
+/**
+ * Frees cfs_range_expr structures of \a expr_list.
+ *
+ * \retval none
+ */
+void
+cfs_expr_list_free(struct cfs_expr_list *expr_list)
+{
+ while (!list_empty(&expr_list->el_exprs)) {
+ struct cfs_range_expr *expr;
+
+ expr = list_entry(expr_list->el_exprs.next,
+ struct cfs_range_expr, re_link),
+ list_del(&expr->re_link);
+ LIBCFS_FREE(expr, sizeof(*expr));
+ }
+
+ LIBCFS_FREE(expr_list, sizeof(*expr_list));
+}
+EXPORT_SYMBOL(cfs_expr_list_free);
+
+/**
+ * Parses \<cfs_expr_list\> token of the syntax.
+ *
+ * \retval 1 if \a str parses to \<number\> | \<expr_list\>
+ * \retval 0 otherwise
+ */
+int
+cfs_expr_list_parse(char *str, int len, unsigned min, unsigned max,
+ struct cfs_expr_list **elpp)
+{
+ struct cfs_expr_list *expr_list;
+ struct cfs_range_expr *expr;
+ struct cfs_lstr src;
+ int rc;
+
+ LIBCFS_ALLOC(expr_list, sizeof(*expr_list));
+ if (expr_list == NULL)
+ return -ENOMEM;
+
+ src.ls_str = str;
+ src.ls_len = len;
+
+ INIT_LIST_HEAD(&expr_list->el_exprs);
+
+ if (src.ls_str[0] == '[' &&
+ src.ls_str[src.ls_len - 1] == ']') {
+ src.ls_str++;
+ src.ls_len -= 2;
+
+ rc = -EINVAL;
+ while (src.ls_str != NULL) {
+ struct cfs_lstr tok;
+
+ if (!cfs_gettok(&src, ',', &tok)) {
+ rc = -EINVAL;
+ break;
+ }
+
+ rc = cfs_range_expr_parse(&tok, min, max, 1, &expr);
+ if (rc != 0)
+ break;
+
+ list_add_tail(&expr->re_link,
+ &expr_list->el_exprs);
+ }
+ } else {
+ rc = cfs_range_expr_parse(&src, min, max, 0, &expr);
+ if (rc == 0) {
+ list_add_tail(&expr->re_link,
+ &expr_list->el_exprs);
+ }
+ }
+
+ if (rc != 0)
+ cfs_expr_list_free(expr_list);
+ else
+ *elpp = expr_list;
+
+ return rc;
+}
+EXPORT_SYMBOL(cfs_expr_list_parse);
+
+/**
+ * Frees cfs_expr_list structures of \a list.
+ *
+ * For each struct cfs_expr_list structure found on \a list it frees
+ * range_expr list attached to it and frees the cfs_expr_list itself.
+ *
+ * \retval none
+ */
+void
+cfs_expr_list_free_list(struct list_head *list)
+{
+ struct cfs_expr_list *el;
+
+ while (!list_empty(list)) {
+ el = list_entry(list->next,
+ struct cfs_expr_list, el_link);
+ list_del(&el->el_link);
+ cfs_expr_list_free(el);
+ }
+}
+
+int
+cfs_ip_addr_parse(char *str, int len, struct list_head *list)
+{
+ struct cfs_expr_list *el;
+ struct cfs_lstr src;
+ int rc;
+ int i;
+
+ src.ls_str = str;
+ src.ls_len = len;
+ i = 0;
+
+ while (src.ls_str != NULL) {
+ struct cfs_lstr res;
+
+ if (!cfs_gettok(&src, '.', &res)) {
+ rc = -EINVAL;
+ goto out;
+ }
+
+ rc = cfs_expr_list_parse(res.ls_str, res.ls_len, 0, 255, &el);
+ if (rc != 0)
+ goto out;
+
+ list_add_tail(&el->el_link, list);
+ i++;
+ }
+
+ if (i == 4)
+ return 0;
+
+ rc = -EINVAL;
+ out:
+ cfs_expr_list_free_list(list);
+
+ return rc;
+}
+EXPORT_SYMBOL(cfs_ip_addr_parse);
+
+/**
+ * Matches address (\a addr) against address set encoded in \a list.
+ *
+ * \retval 1 if \a addr matches
+ * \retval 0 otherwise
+ */
+int
+cfs_ip_addr_match(__u32 addr, struct list_head *list)
+{
+ struct cfs_expr_list *el;
+ int i = 0;
+
+ list_for_each_entry_reverse(el, list, el_link) {
+ if (!cfs_expr_list_match(addr & 0xff, el))
+ return 0;
+ addr >>= 8;
+ i++;
+ }
+
+ return i == 4;
+}
+EXPORT_SYMBOL(cfs_ip_addr_match);
+
+void
+cfs_ip_addr_free(struct list_head *list)
+{
+ cfs_expr_list_free_list(list);
+}
+EXPORT_SYMBOL(cfs_ip_addr_free);
diff --git a/drivers/staging/lustre/lustre/libcfs/linux/linux-cpu.c b/drivers/staging/lustre/lustre/libcfs/linux/linux-cpu.c
new file mode 100644
index 000000000..cc3ab3519
--- /dev/null
+++ b/drivers/staging/lustre/lustre/libcfs/linux/linux-cpu.c
@@ -0,0 +1,1056 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 021110-1307, USA
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ *
+ * Author: liang@whamcloud.com
+ */
+
+#define DEBUG_SUBSYSTEM S_LNET
+
+#include <linux/cpu.h>
+#include <linux/sched.h>
+#include "../../../include/linux/libcfs/libcfs.h"
+
+#ifdef CONFIG_SMP
+
+/**
+ * modparam for setting number of partitions
+ *
+ * 0 : estimate best value based on cores or NUMA nodes
+ * 1 : disable multiple partitions
+ * >1 : specify number of partitions
+ */
+static int cpu_npartitions;
+module_param(cpu_npartitions, int, 0444);
+MODULE_PARM_DESC(cpu_npartitions, "# of CPU partitions");
+
+/**
+ * modparam for setting CPU partitions patterns:
+ *
+ * i.e: "0[0,1,2,3] 1[4,5,6,7]", number before bracket is CPU partition ID,
+ * number in bracket is processor ID (core or HT)
+ *
+ * i.e: "N 0[0,1] 1[2,3]" the first character 'N' means numbers in bracket
+ * are NUMA node ID, number before bracket is CPU partition ID.
+ *
+ * NB: If user specified cpu_pattern, cpu_npartitions will be ignored
+ */
+static char *cpu_pattern = "";
+module_param(cpu_pattern, charp, 0444);
+MODULE_PARM_DESC(cpu_pattern, "CPU partitions pattern");
+
+struct cfs_cpt_data {
+ /* serialize hotplug etc */
+ spinlock_t cpt_lock;
+ /* reserved for hotplug */
+ unsigned long cpt_version;
+ /* mutex to protect cpt_cpumask */
+ struct mutex cpt_mutex;
+ /* scratch buffer for set/unset_node */
+ cpumask_t *cpt_cpumask;
+};
+
+static struct cfs_cpt_data cpt_data;
+
+static void cfs_cpu_core_siblings(int cpu, cpumask_t *mask)
+{
+ /* return cpumask of cores in the same socket */
+ cpumask_copy(mask, topology_core_cpumask(cpu));
+}
+
+/* return cpumask of HTs in the same core */
+static void cfs_cpu_ht_siblings(int cpu, cpumask_t *mask)
+{
+ cpumask_copy(mask, topology_thread_cpumask(cpu));
+}
+
+static void cfs_node_to_cpumask(int node, cpumask_t *mask)
+{
+ cpumask_copy(mask, cpumask_of_node(node));
+}
+
+void
+cfs_cpt_table_free(struct cfs_cpt_table *cptab)
+{
+ int i;
+
+ if (cptab->ctb_cpu2cpt != NULL) {
+ LIBCFS_FREE(cptab->ctb_cpu2cpt,
+ num_possible_cpus() *
+ sizeof(cptab->ctb_cpu2cpt[0]));
+ }
+
+ for (i = 0; cptab->ctb_parts != NULL && i < cptab->ctb_nparts; i++) {
+ struct cfs_cpu_partition *part = &cptab->ctb_parts[i];
+
+ if (part->cpt_nodemask != NULL) {
+ LIBCFS_FREE(part->cpt_nodemask,
+ sizeof(*part->cpt_nodemask));
+ }
+
+ if (part->cpt_cpumask != NULL)
+ LIBCFS_FREE(part->cpt_cpumask, cpumask_size());
+ }
+
+ if (cptab->ctb_parts != NULL) {
+ LIBCFS_FREE(cptab->ctb_parts,
+ cptab->ctb_nparts * sizeof(cptab->ctb_parts[0]));
+ }
+
+ if (cptab->ctb_nodemask != NULL)
+ LIBCFS_FREE(cptab->ctb_nodemask, sizeof(*cptab->ctb_nodemask));
+ if (cptab->ctb_cpumask != NULL)
+ LIBCFS_FREE(cptab->ctb_cpumask, cpumask_size());
+
+ LIBCFS_FREE(cptab, sizeof(*cptab));
+}
+EXPORT_SYMBOL(cfs_cpt_table_free);
+
+struct cfs_cpt_table *
+cfs_cpt_table_alloc(unsigned int ncpt)
+{
+ struct cfs_cpt_table *cptab;
+ int i;
+
+ LIBCFS_ALLOC(cptab, sizeof(*cptab));
+ if (cptab == NULL)
+ return NULL;
+
+ cptab->ctb_nparts = ncpt;
+
+ LIBCFS_ALLOC(cptab->ctb_cpumask, cpumask_size());
+ LIBCFS_ALLOC(cptab->ctb_nodemask, sizeof(*cptab->ctb_nodemask));
+
+ if (cptab->ctb_cpumask == NULL || cptab->ctb_nodemask == NULL)
+ goto failed;
+
+ LIBCFS_ALLOC(cptab->ctb_cpu2cpt,
+ num_possible_cpus() * sizeof(cptab->ctb_cpu2cpt[0]));
+ if (cptab->ctb_cpu2cpt == NULL)
+ goto failed;
+
+ memset(cptab->ctb_cpu2cpt, -1,
+ num_possible_cpus() * sizeof(cptab->ctb_cpu2cpt[0]));
+
+ LIBCFS_ALLOC(cptab->ctb_parts, ncpt * sizeof(cptab->ctb_parts[0]));
+ if (cptab->ctb_parts == NULL)
+ goto failed;
+
+ for (i = 0; i < ncpt; i++) {
+ struct cfs_cpu_partition *part = &cptab->ctb_parts[i];
+
+ LIBCFS_ALLOC(part->cpt_cpumask, cpumask_size());
+ LIBCFS_ALLOC(part->cpt_nodemask, sizeof(*part->cpt_nodemask));
+ if (part->cpt_cpumask == NULL || part->cpt_nodemask == NULL)
+ goto failed;
+ }
+
+ spin_lock(&cpt_data.cpt_lock);
+ /* Reserved for hotplug */
+ cptab->ctb_version = cpt_data.cpt_version;
+ spin_unlock(&cpt_data.cpt_lock);
+
+ return cptab;
+
+ failed:
+ cfs_cpt_table_free(cptab);
+ return NULL;
+}
+EXPORT_SYMBOL(cfs_cpt_table_alloc);
+
+int
+cfs_cpt_table_print(struct cfs_cpt_table *cptab, char *buf, int len)
+{
+ char *tmp = buf;
+ int rc = 0;
+ int i;
+ int j;
+
+ for (i = 0; i < cptab->ctb_nparts; i++) {
+ if (len > 0) {
+ rc = snprintf(tmp, len, "%d\t: ", i);
+ len -= rc;
+ }
+
+ if (len <= 0) {
+ rc = -EFBIG;
+ goto out;
+ }
+
+ tmp += rc;
+ for_each_cpu(j, cptab->ctb_parts[i].cpt_cpumask) {
+ rc = snprintf(tmp, len, "%d ", j);
+ len -= rc;
+ if (len <= 0) {
+ rc = -EFBIG;
+ goto out;
+ }
+ tmp += rc;
+ }
+
+ *tmp = '\n';
+ tmp++;
+ len--;
+ }
+
+ out:
+ if (rc < 0)
+ return rc;
+
+ return tmp - buf;
+}
+EXPORT_SYMBOL(cfs_cpt_table_print);
+
+int
+cfs_cpt_number(struct cfs_cpt_table *cptab)
+{
+ return cptab->ctb_nparts;
+}
+EXPORT_SYMBOL(cfs_cpt_number);
+
+int
+cfs_cpt_weight(struct cfs_cpt_table *cptab, int cpt)
+{
+ LASSERT(cpt == CFS_CPT_ANY || (cpt >= 0 && cpt < cptab->ctb_nparts));
+
+ return cpt == CFS_CPT_ANY ?
+ cpumask_weight(cptab->ctb_cpumask) :
+ cpumask_weight(cptab->ctb_parts[cpt].cpt_cpumask);
+}
+EXPORT_SYMBOL(cfs_cpt_weight);
+
+int
+cfs_cpt_online(struct cfs_cpt_table *cptab, int cpt)
+{
+ LASSERT(cpt == CFS_CPT_ANY || (cpt >= 0 && cpt < cptab->ctb_nparts));
+
+ return cpt == CFS_CPT_ANY ?
+ cpumask_any_and(cptab->ctb_cpumask,
+ cpu_online_mask) < nr_cpu_ids :
+ cpumask_any_and(cptab->ctb_parts[cpt].cpt_cpumask,
+ cpu_online_mask) < nr_cpu_ids;
+}
+EXPORT_SYMBOL(cfs_cpt_online);
+
+cpumask_t *
+cfs_cpt_cpumask(struct cfs_cpt_table *cptab, int cpt)
+{
+ LASSERT(cpt == CFS_CPT_ANY || (cpt >= 0 && cpt < cptab->ctb_nparts));
+
+ return cpt == CFS_CPT_ANY ?
+ cptab->ctb_cpumask : cptab->ctb_parts[cpt].cpt_cpumask;
+}
+EXPORT_SYMBOL(cfs_cpt_cpumask);
+
+nodemask_t *
+cfs_cpt_nodemask(struct cfs_cpt_table *cptab, int cpt)
+{
+ LASSERT(cpt == CFS_CPT_ANY || (cpt >= 0 && cpt < cptab->ctb_nparts));
+
+ return cpt == CFS_CPT_ANY ?
+ cptab->ctb_nodemask : cptab->ctb_parts[cpt].cpt_nodemask;
+}
+EXPORT_SYMBOL(cfs_cpt_nodemask);
+
+int
+cfs_cpt_set_cpu(struct cfs_cpt_table *cptab, int cpt, int cpu)
+{
+ int node;
+
+ LASSERT(cpt >= 0 && cpt < cptab->ctb_nparts);
+
+ if (cpu < 0 || cpu >= nr_cpu_ids || !cpu_online(cpu)) {
+ CDEBUG(D_INFO, "CPU %d is invalid or it's offline\n", cpu);
+ return 0;
+ }
+
+ if (cptab->ctb_cpu2cpt[cpu] != -1) {
+ CDEBUG(D_INFO, "CPU %d is already in partition %d\n",
+ cpu, cptab->ctb_cpu2cpt[cpu]);
+ return 0;
+ }
+
+ cptab->ctb_cpu2cpt[cpu] = cpt;
+
+ LASSERT(!cpumask_test_cpu(cpu, cptab->ctb_cpumask));
+ LASSERT(!cpumask_test_cpu(cpu, cptab->ctb_parts[cpt].cpt_cpumask));
+
+ cpumask_set_cpu(cpu, cptab->ctb_cpumask);
+ cpumask_set_cpu(cpu, cptab->ctb_parts[cpt].cpt_cpumask);
+
+ node = cpu_to_node(cpu);
+
+ /* first CPU of @node in this CPT table */
+ if (!node_isset(node, *cptab->ctb_nodemask))
+ node_set(node, *cptab->ctb_nodemask);
+
+ /* first CPU of @node in this partition */
+ if (!node_isset(node, *cptab->ctb_parts[cpt].cpt_nodemask))
+ node_set(node, *cptab->ctb_parts[cpt].cpt_nodemask);
+
+ return 1;
+}
+EXPORT_SYMBOL(cfs_cpt_set_cpu);
+
+void
+cfs_cpt_unset_cpu(struct cfs_cpt_table *cptab, int cpt, int cpu)
+{
+ int node;
+ int i;
+
+ LASSERT(cpt == CFS_CPT_ANY || (cpt >= 0 && cpt < cptab->ctb_nparts));
+
+ if (cpu < 0 || cpu >= nr_cpu_ids) {
+ CDEBUG(D_INFO, "Invalid CPU id %d\n", cpu);
+ return;
+ }
+
+ if (cpt == CFS_CPT_ANY) {
+ /* caller doesn't know the partition ID */
+ cpt = cptab->ctb_cpu2cpt[cpu];
+ if (cpt < 0) { /* not set in this CPT-table */
+ CDEBUG(D_INFO, "Try to unset cpu %d which is not in CPT-table %p\n",
+ cpt, cptab);
+ return;
+ }
+
+ } else if (cpt != cptab->ctb_cpu2cpt[cpu]) {
+ CDEBUG(D_INFO,
+ "CPU %d is not in cpu-partition %d\n", cpu, cpt);
+ return;
+ }
+
+ LASSERT(cpumask_test_cpu(cpu, cptab->ctb_parts[cpt].cpt_cpumask));
+ LASSERT(cpumask_test_cpu(cpu, cptab->ctb_cpumask));
+
+ cpumask_clear_cpu(cpu, cptab->ctb_parts[cpt].cpt_cpumask);
+ cpumask_clear_cpu(cpu, cptab->ctb_cpumask);
+ cptab->ctb_cpu2cpt[cpu] = -1;
+
+ node = cpu_to_node(cpu);
+
+ LASSERT(node_isset(node, *cptab->ctb_parts[cpt].cpt_nodemask));
+ LASSERT(node_isset(node, *cptab->ctb_nodemask));
+
+ for_each_cpu(i, cptab->ctb_parts[cpt].cpt_cpumask) {
+ /* this CPT has other CPU belonging to this node? */
+ if (cpu_to_node(i) == node)
+ break;
+ }
+
+ if (i >= nr_cpu_ids)
+ node_clear(node, *cptab->ctb_parts[cpt].cpt_nodemask);
+
+ for_each_cpu(i, cptab->ctb_cpumask) {
+ /* this CPT-table has other CPU belonging to this node? */
+ if (cpu_to_node(i) == node)
+ break;
+ }
+
+ if (i >= nr_cpu_ids)
+ node_clear(node, *cptab->ctb_nodemask);
+
+ return;
+}
+EXPORT_SYMBOL(cfs_cpt_unset_cpu);
+
+int
+cfs_cpt_set_cpumask(struct cfs_cpt_table *cptab, int cpt, cpumask_t *mask)
+{
+ int i;
+
+ if (cpumask_weight(mask) == 0 ||
+ cpumask_any_and(mask, cpu_online_mask) >= nr_cpu_ids) {
+ CDEBUG(D_INFO, "No online CPU is found in the CPU mask for CPU partition %d\n",
+ cpt);
+ return 0;
+ }
+
+ for_each_cpu(i, mask) {
+ if (!cfs_cpt_set_cpu(cptab, cpt, i))
+ return 0;
+ }
+
+ return 1;
+}
+EXPORT_SYMBOL(cfs_cpt_set_cpumask);
+
+void
+cfs_cpt_unset_cpumask(struct cfs_cpt_table *cptab, int cpt, cpumask_t *mask)
+{
+ int i;
+
+ for_each_cpu(i, mask)
+ cfs_cpt_unset_cpu(cptab, cpt, i);
+}
+EXPORT_SYMBOL(cfs_cpt_unset_cpumask);
+
+int
+cfs_cpt_set_node(struct cfs_cpt_table *cptab, int cpt, int node)
+{
+ cpumask_t *mask;
+ int rc;
+
+ if (node < 0 || node >= MAX_NUMNODES) {
+ CDEBUG(D_INFO,
+ "Invalid NUMA id %d for CPU partition %d\n", node, cpt);
+ return 0;
+ }
+
+ mutex_lock(&cpt_data.cpt_mutex);
+
+ mask = cpt_data.cpt_cpumask;
+ cfs_node_to_cpumask(node, mask);
+
+ rc = cfs_cpt_set_cpumask(cptab, cpt, mask);
+
+ mutex_unlock(&cpt_data.cpt_mutex);
+
+ return rc;
+}
+EXPORT_SYMBOL(cfs_cpt_set_node);
+
+void
+cfs_cpt_unset_node(struct cfs_cpt_table *cptab, int cpt, int node)
+{
+ cpumask_t *mask;
+
+ if (node < 0 || node >= MAX_NUMNODES) {
+ CDEBUG(D_INFO,
+ "Invalid NUMA id %d for CPU partition %d\n", node, cpt);
+ return;
+ }
+
+ mutex_lock(&cpt_data.cpt_mutex);
+
+ mask = cpt_data.cpt_cpumask;
+ cfs_node_to_cpumask(node, mask);
+
+ cfs_cpt_unset_cpumask(cptab, cpt, mask);
+
+ mutex_unlock(&cpt_data.cpt_mutex);
+}
+EXPORT_SYMBOL(cfs_cpt_unset_node);
+
+int
+cfs_cpt_set_nodemask(struct cfs_cpt_table *cptab, int cpt, nodemask_t *mask)
+{
+ int i;
+
+ for_each_node_mask(i, *mask) {
+ if (!cfs_cpt_set_node(cptab, cpt, i))
+ return 0;
+ }
+
+ return 1;
+}
+EXPORT_SYMBOL(cfs_cpt_set_nodemask);
+
+void
+cfs_cpt_unset_nodemask(struct cfs_cpt_table *cptab, int cpt, nodemask_t *mask)
+{
+ int i;
+
+ for_each_node_mask(i, *mask)
+ cfs_cpt_unset_node(cptab, cpt, i);
+}
+EXPORT_SYMBOL(cfs_cpt_unset_nodemask);
+
+void
+cfs_cpt_clear(struct cfs_cpt_table *cptab, int cpt)
+{
+ int last;
+ int i;
+
+ if (cpt == CFS_CPT_ANY) {
+ last = cptab->ctb_nparts - 1;
+ cpt = 0;
+ } else {
+ last = cpt;
+ }
+
+ for (; cpt <= last; cpt++) {
+ for_each_cpu(i, cptab->ctb_parts[cpt].cpt_cpumask)
+ cfs_cpt_unset_cpu(cptab, cpt, i);
+ }
+}
+EXPORT_SYMBOL(cfs_cpt_clear);
+
+int
+cfs_cpt_spread_node(struct cfs_cpt_table *cptab, int cpt)
+{
+ nodemask_t *mask;
+ int weight;
+ int rotor;
+ int node;
+
+ /* convert CPU partition ID to HW node id */
+
+ if (cpt < 0 || cpt >= cptab->ctb_nparts) {
+ mask = cptab->ctb_nodemask;
+ rotor = cptab->ctb_spread_rotor++;
+ } else {
+ mask = cptab->ctb_parts[cpt].cpt_nodemask;
+ rotor = cptab->ctb_parts[cpt].cpt_spread_rotor++;
+ }
+
+ weight = nodes_weight(*mask);
+ LASSERT(weight > 0);
+
+ rotor %= weight;
+
+ for_each_node_mask(node, *mask) {
+ if (rotor-- == 0)
+ return node;
+ }
+
+ LBUG();
+ return 0;
+}
+EXPORT_SYMBOL(cfs_cpt_spread_node);
+
+int
+cfs_cpt_current(struct cfs_cpt_table *cptab, int remap)
+{
+ int cpu = smp_processor_id();
+ int cpt = cptab->ctb_cpu2cpt[cpu];
+
+ if (cpt < 0) {
+ if (!remap)
+ return cpt;
+
+ /* don't return negative value for safety of upper layer,
+ * instead we shadow the unknown cpu to a valid partition ID */
+ cpt = cpu % cptab->ctb_nparts;
+ }
+
+ return cpt;
+}
+EXPORT_SYMBOL(cfs_cpt_current);
+
+int
+cfs_cpt_of_cpu(struct cfs_cpt_table *cptab, int cpu)
+{
+ LASSERT(cpu >= 0 && cpu < nr_cpu_ids);
+
+ return cptab->ctb_cpu2cpt[cpu];
+}
+EXPORT_SYMBOL(cfs_cpt_of_cpu);
+
+int
+cfs_cpt_bind(struct cfs_cpt_table *cptab, int cpt)
+{
+ cpumask_t *cpumask;
+ nodemask_t *nodemask;
+ int rc;
+ int i;
+
+ LASSERT(cpt == CFS_CPT_ANY || (cpt >= 0 && cpt < cptab->ctb_nparts));
+
+ if (cpt == CFS_CPT_ANY) {
+ cpumask = cptab->ctb_cpumask;
+ nodemask = cptab->ctb_nodemask;
+ } else {
+ cpumask = cptab->ctb_parts[cpt].cpt_cpumask;
+ nodemask = cptab->ctb_parts[cpt].cpt_nodemask;
+ }
+
+ if (cpumask_any_and(cpumask, cpu_online_mask) >= nr_cpu_ids) {
+ CERROR("No online CPU found in CPU partition %d, did someone do CPU hotplug on system? You might need to reload Lustre modules to keep system working well.\n",
+ cpt);
+ return -EINVAL;
+ }
+
+ for_each_online_cpu(i) {
+ if (cpumask_test_cpu(i, cpumask))
+ continue;
+
+ rc = set_cpus_allowed_ptr(current, cpumask);
+ set_mems_allowed(*nodemask);
+ if (rc == 0)
+ schedule(); /* switch to allowed CPU */
+
+ return rc;
+ }
+
+ /* don't need to set affinity because all online CPUs are covered */
+ return 0;
+}
+EXPORT_SYMBOL(cfs_cpt_bind);
+
+/**
+ * Choose max to \a number CPUs from \a node and set them in \a cpt.
+ * We always prefer to choose CPU in the same core/socket.
+ */
+static int
+cfs_cpt_choose_ncpus(struct cfs_cpt_table *cptab, int cpt,
+ cpumask_t *node, int number)
+{
+ cpumask_t *socket = NULL;
+ cpumask_t *core = NULL;
+ int rc = 0;
+ int cpu;
+
+ LASSERT(number > 0);
+
+ if (number >= cpumask_weight(node)) {
+ while (!cpumask_empty(node)) {
+ cpu = cpumask_first(node);
+
+ rc = cfs_cpt_set_cpu(cptab, cpt, cpu);
+ if (!rc)
+ return -EINVAL;
+ cpumask_clear_cpu(cpu, node);
+ }
+ return 0;
+ }
+
+ /* allocate scratch buffer */
+ LIBCFS_ALLOC(socket, cpumask_size());
+ LIBCFS_ALLOC(core, cpumask_size());
+ if (socket == NULL || core == NULL) {
+ rc = -ENOMEM;
+ goto out;
+ }
+
+ while (!cpumask_empty(node)) {
+ cpu = cpumask_first(node);
+
+ /* get cpumask for cores in the same socket */
+ cfs_cpu_core_siblings(cpu, socket);
+ cpumask_and(socket, socket, node);
+
+ LASSERT(!cpumask_empty(socket));
+
+ while (!cpumask_empty(socket)) {
+ int i;
+
+ /* get cpumask for hts in the same core */
+ cfs_cpu_ht_siblings(cpu, core);
+ cpumask_and(core, core, node);
+
+ LASSERT(!cpumask_empty(core));
+
+ for_each_cpu(i, core) {
+ cpumask_clear_cpu(i, socket);
+ cpumask_clear_cpu(i, node);
+
+ rc = cfs_cpt_set_cpu(cptab, cpt, i);
+ if (!rc) {
+ rc = -EINVAL;
+ goto out;
+ }
+
+ if (--number == 0)
+ goto out;
+ }
+ cpu = cpumask_first(socket);
+ }
+ }
+
+ out:
+ if (socket != NULL)
+ LIBCFS_FREE(socket, cpumask_size());
+ if (core != NULL)
+ LIBCFS_FREE(core, cpumask_size());
+ return rc;
+}
+
+#define CPT_WEIGHT_MIN 4u
+
+static unsigned int
+cfs_cpt_num_estimate(void)
+{
+ unsigned nnode = num_online_nodes();
+ unsigned ncpu = num_online_cpus();
+ unsigned ncpt;
+
+ if (ncpu <= CPT_WEIGHT_MIN) {
+ ncpt = 1;
+ goto out;
+ }
+
+ /* generate reasonable number of CPU partitions based on total number
+ * of CPUs, Preferred N should be power2 and match this condition:
+ * 2 * (N - 1)^2 < NCPUS <= 2 * N^2 */
+ for (ncpt = 2; ncpu > 2 * ncpt * ncpt; ncpt <<= 1) {}
+
+ if (ncpt <= nnode) { /* fat numa system */
+ while (nnode > ncpt)
+ nnode >>= 1;
+
+ } else { /* ncpt > nnode */
+ while ((nnode << 1) <= ncpt)
+ nnode <<= 1;
+ }
+
+ ncpt = nnode;
+
+ out:
+#if (BITS_PER_LONG == 32)
+ /* config many CPU partitions on 32-bit system could consume
+ * too much memory */
+ ncpt = min(2U, ncpt);
+#endif
+ while (ncpu % ncpt != 0)
+ ncpt--; /* worst case is 1 */
+
+ return ncpt;
+}
+
+static struct cfs_cpt_table *
+cfs_cpt_table_create(int ncpt)
+{
+ struct cfs_cpt_table *cptab = NULL;
+ cpumask_t *mask = NULL;
+ int cpt = 0;
+ int num;
+ int rc;
+ int i;
+
+ rc = cfs_cpt_num_estimate();
+ if (ncpt <= 0)
+ ncpt = rc;
+
+ if (ncpt > num_online_cpus() || ncpt > 4 * rc) {
+ CWARN("CPU partition number %d is larger than suggested value (%d), your system may have performance issue or run out of memory while under pressure\n",
+ ncpt, rc);
+ }
+
+ if (num_online_cpus() % ncpt != 0) {
+ CERROR("CPU number %d is not multiple of cpu_npartition %d, please try different cpu_npartitions value or set pattern string by cpu_pattern=STRING\n",
+ (int)num_online_cpus(), ncpt);
+ goto failed;
+ }
+
+ cptab = cfs_cpt_table_alloc(ncpt);
+ if (cptab == NULL) {
+ CERROR("Failed to allocate CPU map(%d)\n", ncpt);
+ goto failed;
+ }
+
+ num = num_online_cpus() / ncpt;
+ if (num == 0) {
+ CERROR("CPU changed while setting CPU partition\n");
+ goto failed;
+ }
+
+ LIBCFS_ALLOC(mask, cpumask_size());
+ if (mask == NULL) {
+ CERROR("Failed to allocate scratch cpumask\n");
+ goto failed;
+ }
+
+ for_each_online_node(i) {
+ cfs_node_to_cpumask(i, mask);
+
+ while (!cpumask_empty(mask)) {
+ struct cfs_cpu_partition *part;
+ int n;
+
+ if (cpt >= ncpt)
+ goto failed;
+
+ part = &cptab->ctb_parts[cpt];
+
+ n = num - cpumask_weight(part->cpt_cpumask);
+ LASSERT(n > 0);
+
+ rc = cfs_cpt_choose_ncpus(cptab, cpt, mask, n);
+ if (rc < 0)
+ goto failed;
+
+ LASSERT(num >= cpumask_weight(part->cpt_cpumask));
+ if (num == cpumask_weight(part->cpt_cpumask))
+ cpt++;
+ }
+ }
+
+ if (cpt != ncpt ||
+ num != cpumask_weight(cptab->ctb_parts[ncpt - 1].cpt_cpumask)) {
+ CERROR("Expect %d(%d) CPU partitions but got %d(%d), CPU hotplug/unplug while setting?\n",
+ cptab->ctb_nparts, num, cpt,
+ cpumask_weight(cptab->ctb_parts[ncpt - 1].cpt_cpumask));
+ goto failed;
+ }
+
+ LIBCFS_FREE(mask, cpumask_size());
+
+ return cptab;
+
+ failed:
+ CERROR("Failed to setup CPU-partition-table with %d CPU-partitions, online HW nodes: %d, HW cpus: %d.\n",
+ ncpt, num_online_nodes(), num_online_cpus());
+
+ if (mask != NULL)
+ LIBCFS_FREE(mask, cpumask_size());
+
+ if (cptab != NULL)
+ cfs_cpt_table_free(cptab);
+
+ return NULL;
+}
+
+static struct cfs_cpt_table *
+cfs_cpt_table_create_pattern(char *pattern)
+{
+ struct cfs_cpt_table *cptab;
+ char *str = pattern;
+ int node = 0;
+ int high;
+ int ncpt;
+ int c;
+
+ for (ncpt = 0;; ncpt++) { /* quick scan bracket */
+ str = strchr(str, '[');
+ if (str == NULL)
+ break;
+ str++;
+ }
+
+ str = cfs_trimwhite(pattern);
+ if (*str == 'n' || *str == 'N') {
+ pattern = str + 1;
+ node = 1;
+ }
+
+ if (ncpt == 0 ||
+ (node && ncpt > num_online_nodes()) ||
+ (!node && ncpt > num_online_cpus())) {
+ CERROR("Invalid pattern %s, or too many partitions %d\n",
+ pattern, ncpt);
+ return NULL;
+ }
+
+ high = node ? MAX_NUMNODES - 1 : nr_cpu_ids - 1;
+
+ cptab = cfs_cpt_table_alloc(ncpt);
+ if (cptab == NULL) {
+ CERROR("Failed to allocate cpu partition table\n");
+ return NULL;
+ }
+
+ for (str = cfs_trimwhite(pattern), c = 0;; c++) {
+ struct cfs_range_expr *range;
+ struct cfs_expr_list *el;
+ char *bracket = strchr(str, '[');
+ int cpt;
+ int rc;
+ int i;
+ int n;
+
+ if (bracket == NULL) {
+ if (*str != 0) {
+ CERROR("Invalid pattern %s\n", str);
+ goto failed;
+ } else if (c != ncpt) {
+ CERROR("expect %d partitions but found %d\n",
+ ncpt, c);
+ goto failed;
+ }
+ break;
+ }
+
+ if (sscanf(str, "%d%n", &cpt, &n) < 1) {
+ CERROR("Invalid cpu pattern %s\n", str);
+ goto failed;
+ }
+
+ if (cpt < 0 || cpt >= ncpt) {
+ CERROR("Invalid partition id %d, total partitions %d\n",
+ cpt, ncpt);
+ goto failed;
+ }
+
+ if (cfs_cpt_weight(cptab, cpt) != 0) {
+ CERROR("Partition %d has already been set.\n", cpt);
+ goto failed;
+ }
+
+ str = cfs_trimwhite(str + n);
+ if (str != bracket) {
+ CERROR("Invalid pattern %s\n", str);
+ goto failed;
+ }
+
+ bracket = strchr(str, ']');
+ if (bracket == NULL) {
+ CERROR("missing right bracket for cpt %d, %s\n",
+ cpt, str);
+ goto failed;
+ }
+
+ if (cfs_expr_list_parse(str, (bracket - str) + 1,
+ 0, high, &el) != 0) {
+ CERROR("Can't parse number range: %s\n", str);
+ goto failed;
+ }
+
+ list_for_each_entry(range, &el->el_exprs, re_link) {
+ for (i = range->re_lo; i <= range->re_hi; i++) {
+ if ((i - range->re_lo) % range->re_stride != 0)
+ continue;
+
+ rc = node ? cfs_cpt_set_node(cptab, cpt, i) :
+ cfs_cpt_set_cpu(cptab, cpt, i);
+ if (!rc) {
+ cfs_expr_list_free(el);
+ goto failed;
+ }
+ }
+ }
+
+ cfs_expr_list_free(el);
+
+ if (!cfs_cpt_online(cptab, cpt)) {
+ CERROR("No online CPU is found on partition %d\n", cpt);
+ goto failed;
+ }
+
+ str = cfs_trimwhite(bracket + 1);
+ }
+
+ return cptab;
+
+ failed:
+ cfs_cpt_table_free(cptab);
+ return NULL;
+}
+
+#ifdef CONFIG_HOTPLUG_CPU
+static int
+cfs_cpu_notify(struct notifier_block *self, unsigned long action, void *hcpu)
+{
+ unsigned int cpu = (unsigned long)hcpu;
+ bool warn;
+
+ switch (action) {
+ case CPU_DEAD:
+ case CPU_DEAD_FROZEN:
+ case CPU_ONLINE:
+ case CPU_ONLINE_FROZEN:
+ spin_lock(&cpt_data.cpt_lock);
+ cpt_data.cpt_version++;
+ spin_unlock(&cpt_data.cpt_lock);
+ default:
+ if (action != CPU_DEAD && action != CPU_DEAD_FROZEN) {
+ CDEBUG(D_INFO, "CPU changed [cpu %u action %lx]\n",
+ cpu, action);
+ break;
+ }
+
+ mutex_lock(&cpt_data.cpt_mutex);
+ /* if all HTs in a core are offline, it may break affinity */
+ cfs_cpu_ht_siblings(cpu, cpt_data.cpt_cpumask);
+ warn = cpumask_any_and(cpt_data.cpt_cpumask,
+ cpu_online_mask) >= nr_cpu_ids;
+ mutex_unlock(&cpt_data.cpt_mutex);
+ CDEBUG(warn ? D_WARNING : D_INFO,
+ "Lustre: can't support CPU plug-out well now, performance and stability could be impacted [CPU %u action: %lx]\n",
+ cpu, action);
+ }
+
+ return NOTIFY_OK;
+}
+
+static struct notifier_block cfs_cpu_notifier = {
+ .notifier_call = cfs_cpu_notify,
+ .priority = 0
+};
+
+#endif
+
+void
+cfs_cpu_fini(void)
+{
+ if (cfs_cpt_table != NULL)
+ cfs_cpt_table_free(cfs_cpt_table);
+
+#ifdef CONFIG_HOTPLUG_CPU
+ unregister_hotcpu_notifier(&cfs_cpu_notifier);
+#endif
+ if (cpt_data.cpt_cpumask != NULL)
+ LIBCFS_FREE(cpt_data.cpt_cpumask, cpumask_size());
+}
+
+int
+cfs_cpu_init(void)
+{
+ LASSERT(cfs_cpt_table == NULL);
+
+ memset(&cpt_data, 0, sizeof(cpt_data));
+
+ LIBCFS_ALLOC(cpt_data.cpt_cpumask, cpumask_size());
+ if (cpt_data.cpt_cpumask == NULL) {
+ CERROR("Failed to allocate scratch buffer\n");
+ return -1;
+ }
+
+ spin_lock_init(&cpt_data.cpt_lock);
+ mutex_init(&cpt_data.cpt_mutex);
+
+#ifdef CONFIG_HOTPLUG_CPU
+ register_hotcpu_notifier(&cfs_cpu_notifier);
+#endif
+
+ if (*cpu_pattern != 0) {
+ cfs_cpt_table = cfs_cpt_table_create_pattern(cpu_pattern);
+ if (cfs_cpt_table == NULL) {
+ CERROR("Failed to create cptab from pattern %s\n",
+ cpu_pattern);
+ goto failed;
+ }
+
+ } else {
+ cfs_cpt_table = cfs_cpt_table_create(cpu_npartitions);
+ if (cfs_cpt_table == NULL) {
+ CERROR("Failed to create ptable with npartitions %d\n",
+ cpu_npartitions);
+ goto failed;
+ }
+ }
+
+ spin_lock(&cpt_data.cpt_lock);
+ if (cfs_cpt_table->ctb_version != cpt_data.cpt_version) {
+ spin_unlock(&cpt_data.cpt_lock);
+ CERROR("CPU hotplug/unplug during setup\n");
+ goto failed;
+ }
+ spin_unlock(&cpt_data.cpt_lock);
+
+ LCONSOLE(0, "HW CPU cores: %d, npartitions: %d\n",
+ num_online_cpus(), cfs_cpt_number(cfs_cpt_table));
+ return 0;
+
+ failed:
+ cfs_cpu_fini();
+ return -1;
+}
+
+#endif
diff --git a/drivers/staging/lustre/lustre/libcfs/linux/linux-crypto-adler.c b/drivers/staging/lustre/lustre/libcfs/linux/linux-crypto-adler.c
new file mode 100644
index 000000000..5e185fa59
--- /dev/null
+++ b/drivers/staging/lustre/lustre/libcfs/linux/linux-crypto-adler.c
@@ -0,0 +1,141 @@
+/* GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see http://www.gnu.org/licenses
+ *
+ * Please visit http://www.xyratex.com/contact if you need additional
+ * information or have any questions.
+ *
+ * GPL HEADER END
+ */
+
+/*
+ * Copyright 2012 Xyratex Technology Limited
+ */
+
+/*
+ * This is crypto api shash wrappers to zlib_adler32.
+ */
+
+#include <linux/module.h>
+#include <linux/zutil.h>
+#include <crypto/internal/hash.h>
+#include "linux-crypto.h"
+
+#define CHKSUM_BLOCK_SIZE 1
+#define CHKSUM_DIGEST_SIZE 4
+
+static u32 __adler32(u32 cksum, unsigned char const *p, size_t len)
+{
+ return zlib_adler32(cksum, p, len);
+}
+
+static int adler32_cra_init(struct crypto_tfm *tfm)
+{
+ u32 *key = crypto_tfm_ctx(tfm);
+
+ *key = 1;
+
+ return 0;
+}
+
+static int adler32_setkey(struct crypto_shash *hash, const u8 *key,
+ unsigned int keylen)
+{
+ u32 *mctx = crypto_shash_ctx(hash);
+
+ if (keylen != sizeof(u32)) {
+ crypto_shash_set_flags(hash, CRYPTO_TFM_RES_BAD_KEY_LEN);
+ return -EINVAL;
+ }
+ *mctx = *(u32 *)key;
+ return 0;
+}
+
+static int adler32_init(struct shash_desc *desc)
+{
+ u32 *mctx = crypto_shash_ctx(desc->tfm);
+ u32 *cksump = shash_desc_ctx(desc);
+
+ *cksump = *mctx;
+
+ return 0;
+}
+
+static int adler32_update(struct shash_desc *desc, const u8 *data,
+ unsigned int len)
+{
+ u32 *cksump = shash_desc_ctx(desc);
+
+ *cksump = __adler32(*cksump, data, len);
+ return 0;
+}
+static int __adler32_finup(u32 *cksump, const u8 *data, unsigned int len,
+ u8 *out)
+{
+ *(u32 *)out = __adler32(*cksump, data, len);
+ return 0;
+}
+
+static int adler32_finup(struct shash_desc *desc, const u8 *data,
+ unsigned int len, u8 *out)
+{
+ return __adler32_finup(shash_desc_ctx(desc), data, len, out);
+}
+
+static int adler32_final(struct shash_desc *desc, u8 *out)
+{
+ u32 *cksump = shash_desc_ctx(desc);
+
+ *(u32 *)out = *cksump;
+ return 0;
+}
+
+static int adler32_digest(struct shash_desc *desc, const u8 *data,
+ unsigned int len, u8 *out)
+{
+ return __adler32_finup(crypto_shash_ctx(desc->tfm), data, len,
+ out);
+}
+static struct shash_alg alg = {
+ .setkey = adler32_setkey,
+ .init = adler32_init,
+ .update = adler32_update,
+ .final = adler32_final,
+ .finup = adler32_finup,
+ .digest = adler32_digest,
+ .descsize = sizeof(u32),
+ .digestsize = CHKSUM_DIGEST_SIZE,
+ .base = {
+ .cra_name = "adler32",
+ .cra_driver_name = "adler32-zlib",
+ .cra_priority = 100,
+ .cra_blocksize = CHKSUM_BLOCK_SIZE,
+ .cra_ctxsize = sizeof(u32),
+ .cra_module = THIS_MODULE,
+ .cra_init = adler32_cra_init,
+ }
+};
+
+
+int cfs_crypto_adler32_register(void)
+{
+ return crypto_register_shash(&alg);
+}
+
+void cfs_crypto_adler32_unregister(void)
+{
+ crypto_unregister_shash(&alg);
+}
diff --git a/drivers/staging/lustre/lustre/libcfs/linux/linux-crypto.c b/drivers/staging/lustre/lustre/libcfs/linux/linux-crypto.c
new file mode 100644
index 000000000..aa3fffed1
--- /dev/null
+++ b/drivers/staging/lustre/lustre/libcfs/linux/linux-crypto.c
@@ -0,0 +1,291 @@
+/* GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see http://www.gnu.org/licenses
+ *
+ * Please visit http://www.xyratex.com/contact if you need additional
+ * information or have any questions.
+ *
+ * GPL HEADER END
+ */
+
+/*
+ * Copyright 2012 Xyratex Technology Limited
+ *
+ * Copyright (c) 2012, Intel Corporation.
+ */
+
+#include <linux/crypto.h>
+#include <linux/scatterlist.h>
+#include "../../../include/linux/libcfs/libcfs.h"
+#include "linux-crypto.h"
+/**
+ * Array of hash algorithm speed in MByte per second
+ */
+static int cfs_crypto_hash_speeds[CFS_HASH_ALG_MAX];
+
+
+
+static int cfs_crypto_hash_alloc(unsigned char alg_id,
+ const struct cfs_crypto_hash_type **type,
+ struct hash_desc *desc, unsigned char *key,
+ unsigned int key_len)
+{
+ int err = 0;
+
+ *type = cfs_crypto_hash_type(alg_id);
+
+ if (*type == NULL) {
+ CWARN("Unsupported hash algorithm id = %d, max id is %d\n",
+ alg_id, CFS_HASH_ALG_MAX);
+ return -EINVAL;
+ }
+ desc->tfm = crypto_alloc_hash((*type)->cht_name, 0, 0);
+
+ if (desc->tfm == NULL)
+ return -EINVAL;
+
+ if (IS_ERR(desc->tfm)) {
+ CDEBUG(D_INFO, "Failed to alloc crypto hash %s\n",
+ (*type)->cht_name);
+ return PTR_ERR(desc->tfm);
+ }
+
+ desc->flags = 0;
+
+ /** Shash have different logic for initialization then digest
+ * shash: crypto_hash_setkey, crypto_hash_init
+ * digest: crypto_digest_init, crypto_digest_setkey
+ * Skip this function for digest, because we use shash logic at
+ * cfs_crypto_hash_alloc.
+ */
+ if (key != NULL) {
+ err = crypto_hash_setkey(desc->tfm, key, key_len);
+ } else if ((*type)->cht_key != 0) {
+ err = crypto_hash_setkey(desc->tfm,
+ (unsigned char *)&((*type)->cht_key),
+ (*type)->cht_size);
+ }
+
+ if (err != 0) {
+ crypto_free_hash(desc->tfm);
+ return err;
+ }
+
+ CDEBUG(D_INFO, "Using crypto hash: %s (%s) speed %d MB/s\n",
+ (crypto_hash_tfm(desc->tfm))->__crt_alg->cra_name,
+ (crypto_hash_tfm(desc->tfm))->__crt_alg->cra_driver_name,
+ cfs_crypto_hash_speeds[alg_id]);
+
+ return crypto_hash_init(desc);
+}
+
+int cfs_crypto_hash_digest(unsigned char alg_id,
+ const void *buf, unsigned int buf_len,
+ unsigned char *key, unsigned int key_len,
+ unsigned char *hash, unsigned int *hash_len)
+{
+ struct scatterlist sl;
+ struct hash_desc hdesc;
+ int err;
+ const struct cfs_crypto_hash_type *type;
+
+ if (buf == NULL || buf_len == 0 || hash_len == NULL)
+ return -EINVAL;
+
+ err = cfs_crypto_hash_alloc(alg_id, &type, &hdesc, key, key_len);
+ if (err != 0)
+ return err;
+
+ if (hash == NULL || *hash_len < type->cht_size) {
+ *hash_len = type->cht_size;
+ crypto_free_hash(hdesc.tfm);
+ return -ENOSPC;
+ }
+ sg_init_one(&sl, (void *)buf, buf_len);
+
+ hdesc.flags = 0;
+ err = crypto_hash_digest(&hdesc, &sl, sl.length, hash);
+ crypto_free_hash(hdesc.tfm);
+
+ return err;
+}
+EXPORT_SYMBOL(cfs_crypto_hash_digest);
+
+struct cfs_crypto_hash_desc *
+ cfs_crypto_hash_init(unsigned char alg_id,
+ unsigned char *key, unsigned int key_len)
+{
+
+ struct hash_desc *hdesc;
+ int err;
+ const struct cfs_crypto_hash_type *type;
+
+ hdesc = kmalloc(sizeof(*hdesc), 0);
+ if (hdesc == NULL)
+ return ERR_PTR(-ENOMEM);
+
+ err = cfs_crypto_hash_alloc(alg_id, &type, hdesc, key, key_len);
+
+ if (err) {
+ kfree(hdesc);
+ return ERR_PTR(err);
+ }
+ return (struct cfs_crypto_hash_desc *)hdesc;
+}
+EXPORT_SYMBOL(cfs_crypto_hash_init);
+
+int cfs_crypto_hash_update_page(struct cfs_crypto_hash_desc *hdesc,
+ struct page *page, unsigned int offset,
+ unsigned int len)
+{
+ struct scatterlist sl;
+
+ sg_init_table(&sl, 1);
+ sg_set_page(&sl, page, len, offset & ~CFS_PAGE_MASK);
+
+ return crypto_hash_update((struct hash_desc *)hdesc, &sl, sl.length);
+}
+EXPORT_SYMBOL(cfs_crypto_hash_update_page);
+
+int cfs_crypto_hash_update(struct cfs_crypto_hash_desc *hdesc,
+ const void *buf, unsigned int buf_len)
+{
+ struct scatterlist sl;
+
+ sg_init_one(&sl, (void *)buf, buf_len);
+
+ return crypto_hash_update((struct hash_desc *)hdesc, &sl, sl.length);
+}
+EXPORT_SYMBOL(cfs_crypto_hash_update);
+
+/* If hash_len pointer is NULL - destroy descriptor. */
+int cfs_crypto_hash_final(struct cfs_crypto_hash_desc *hdesc,
+ unsigned char *hash, unsigned int *hash_len)
+{
+ int err;
+ int size = crypto_hash_digestsize(((struct hash_desc *)hdesc)->tfm);
+
+ if (hash_len == NULL) {
+ crypto_free_hash(((struct hash_desc *)hdesc)->tfm);
+ kfree(hdesc);
+ return 0;
+ }
+ if (hash == NULL || *hash_len < size) {
+ *hash_len = size;
+ return -ENOSPC;
+ }
+ err = crypto_hash_final((struct hash_desc *) hdesc, hash);
+
+ if (err < 0) {
+ /* May be caller can fix error */
+ return err;
+ }
+ crypto_free_hash(((struct hash_desc *)hdesc)->tfm);
+ kfree(hdesc);
+ return err;
+}
+EXPORT_SYMBOL(cfs_crypto_hash_final);
+
+static void cfs_crypto_performance_test(unsigned char alg_id,
+ const unsigned char *buf,
+ unsigned int buf_len)
+{
+ unsigned long start, end;
+ int bcount, err = 0;
+ int sec = 1; /* do test only 1 sec */
+ unsigned char hash[64];
+ unsigned int hash_len = 64;
+
+ for (start = jiffies, end = start + sec * HZ, bcount = 0;
+ time_before(jiffies, end); bcount++) {
+ err = cfs_crypto_hash_digest(alg_id, buf, buf_len, NULL, 0,
+ hash, &hash_len);
+ if (err)
+ break;
+
+ }
+ end = jiffies;
+
+ if (err) {
+ cfs_crypto_hash_speeds[alg_id] = -1;
+ CDEBUG(D_INFO, "Crypto hash algorithm %s, err = %d\n",
+ cfs_crypto_hash_name(alg_id), err);
+ } else {
+ unsigned long tmp;
+ tmp = ((bcount * buf_len / jiffies_to_msecs(end - start)) *
+ 1000) / (1024 * 1024);
+ cfs_crypto_hash_speeds[alg_id] = (int)tmp;
+ }
+ CDEBUG(D_INFO, "Crypto hash algorithm %s speed = %d MB/s\n",
+ cfs_crypto_hash_name(alg_id), cfs_crypto_hash_speeds[alg_id]);
+}
+
+int cfs_crypto_hash_speed(unsigned char hash_alg)
+{
+ if (hash_alg < CFS_HASH_ALG_MAX)
+ return cfs_crypto_hash_speeds[hash_alg];
+ else
+ return -1;
+}
+EXPORT_SYMBOL(cfs_crypto_hash_speed);
+
+/**
+ * Do performance test for all hash algorithms.
+ */
+static int cfs_crypto_test_hashes(void)
+{
+ unsigned char i;
+ unsigned char *data;
+ unsigned int j;
+ /* Data block size for testing hash. Maximum
+ * kmalloc size for 2.6.18 kernel is 128K */
+ unsigned int data_len = 1 * 128 * 1024;
+
+ data = kmalloc(data_len, 0);
+ if (data == NULL) {
+ CERROR("Failed to allocate mem\n");
+ return -ENOMEM;
+ }
+
+ for (j = 0; j < data_len; j++)
+ data[j] = j & 0xff;
+
+ for (i = 0; i < CFS_HASH_ALG_MAX; i++)
+ cfs_crypto_performance_test(i, data, data_len);
+
+ kfree(data);
+ return 0;
+}
+
+static int adler32;
+
+int cfs_crypto_register(void)
+{
+ request_module("crc32c");
+
+ adler32 = cfs_crypto_adler32_register();
+
+ /* check all algorithms and do performance test */
+ cfs_crypto_test_hashes();
+ return 0;
+}
+void cfs_crypto_unregister(void)
+{
+ if (adler32 == 0)
+ cfs_crypto_adler32_unregister();
+
+ return;
+}
diff --git a/drivers/staging/lustre/lustre/libcfs/linux/linux-crypto.h b/drivers/staging/lustre/lustre/libcfs/linux/linux-crypto.h
new file mode 100644
index 000000000..18e8cd4d8
--- /dev/null
+++ b/drivers/staging/lustre/lustre/libcfs/linux/linux-crypto.h
@@ -0,0 +1,29 @@
+ /*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see http://www.gnu.org/licenses
+ *
+ * Please visit http://www.xyratex.com/contact if you need additional
+ * information or have any questions.
+ *
+ * GPL HEADER END
+ */
+
+/**
+ * Functions for start/stop shash adler32 algorithm.
+ */
+int cfs_crypto_adler32_register(void);
+void cfs_crypto_adler32_unregister(void);
diff --git a/drivers/staging/lustre/lustre/libcfs/linux/linux-curproc.c b/drivers/staging/lustre/lustre/libcfs/linux/linux-curproc.c
new file mode 100644
index 000000000..277f6b890
--- /dev/null
+++ b/drivers/staging/lustre/lustre/libcfs/linux/linux-curproc.c
@@ -0,0 +1,111 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2011, 2012, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ *
+ * libcfs/libcfs/linux/linux-curproc.c
+ *
+ * Lustre curproc API implementation for Linux kernel
+ *
+ * Author: Nikita Danilov <nikita@clusterfs.com>
+ */
+
+#include <linux/sched.h>
+#include <linux/fs_struct.h>
+
+#include <linux/compat.h>
+#include <linux/thread_info.h>
+
+#define DEBUG_SUBSYSTEM S_LNET
+
+#include "../../../include/linux/libcfs/libcfs.h"
+
+/*
+ * Implementation of cfs_curproc API (see portals/include/libcfs/curproc.h)
+ * for Linux kernel.
+ */
+
+void cfs_cap_raise(cfs_cap_t cap)
+{
+ struct cred *cred;
+
+ cred = prepare_creds();
+ if (cred) {
+ cap_raise(cred->cap_effective, cap);
+ commit_creds(cred);
+ }
+}
+
+void cfs_cap_lower(cfs_cap_t cap)
+{
+ struct cred *cred;
+
+ cred = prepare_creds();
+ if (cred) {
+ cap_lower(cred->cap_effective, cap);
+ commit_creds(cred);
+ }
+}
+
+int cfs_cap_raised(cfs_cap_t cap)
+{
+ return cap_raised(current_cap(), cap);
+}
+
+static void cfs_kernel_cap_pack(kernel_cap_t kcap, cfs_cap_t *cap)
+{
+ /* XXX lost high byte */
+ *cap = kcap.cap[0];
+}
+
+cfs_cap_t cfs_curproc_cap_pack(void)
+{
+ cfs_cap_t cap;
+ cfs_kernel_cap_pack(current_cap(), &cap);
+ return cap;
+}
+
+EXPORT_SYMBOL(cfs_cap_raise);
+EXPORT_SYMBOL(cfs_cap_lower);
+EXPORT_SYMBOL(cfs_cap_raised);
+EXPORT_SYMBOL(cfs_curproc_cap_pack);
+
+/*
+ * Local variables:
+ * c-indentation-style: "K&R"
+ * c-basic-offset: 8
+ * tab-width: 8
+ * fill-column: 80
+ * scroll-step: 1
+ * End:
+ */
diff --git a/drivers/staging/lustre/lustre/libcfs/linux/linux-debug.c b/drivers/staging/lustre/lustre/libcfs/linux/linux-debug.c
new file mode 100644
index 000000000..4545d54f7
--- /dev/null
+++ b/drivers/staging/lustre/lustre/libcfs/linux/linux-debug.c
@@ -0,0 +1,200 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2012, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ *
+ * libcfs/libcfs/linux/linux-debug.c
+ *
+ * Author: Phil Schwan <phil@clusterfs.com>
+ */
+
+#include <linux/module.h>
+#include <linux/kmod.h>
+#include <linux/notifier.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/string.h>
+#include <linux/stat.h>
+#include <linux/errno.h>
+#include <linux/unistd.h>
+#include <linux/interrupt.h>
+#include <linux/completion.h>
+#include <linux/fs.h>
+#include <linux/uaccess.h>
+#include <linux/miscdevice.h>
+
+# define DEBUG_SUBSYSTEM S_LNET
+
+#include "../../../include/linux/libcfs/libcfs.h"
+
+#include "../tracefile.h"
+
+#include <linux/kallsyms.h>
+
+char lnet_upcall[1024] = "/usr/lib/lustre/lnet_upcall";
+char lnet_debug_log_upcall[1024] = "/usr/lib/lustre/lnet_debug_log_upcall";
+
+/**
+ * Upcall function once a Lustre log has been dumped.
+ *
+ * \param file path of the dumped log
+ */
+void libcfs_run_debug_log_upcall(char *file)
+{
+ char *argv[3];
+ int rc;
+ char *envp[] = {
+ "HOME=/",
+ "PATH=/sbin:/bin:/usr/sbin:/usr/bin",
+ NULL};
+
+ argv[0] = lnet_debug_log_upcall;
+
+ LASSERTF(file != NULL, "called on a null filename\n");
+ argv[1] = file; /* only need to pass the path of the file */
+
+ argv[2] = NULL;
+
+ rc = call_usermodehelper(argv[0], argv, envp, 1);
+ if (rc < 0 && rc != -ENOENT) {
+ CERROR("Error %d invoking LNET debug log upcall %s %s; check /proc/sys/lnet/debug_log_upcall\n",
+ rc, argv[0], argv[1]);
+ } else {
+ CDEBUG(D_HA, "Invoked LNET debug log upcall %s %s\n",
+ argv[0], argv[1]);
+ }
+}
+
+void libcfs_run_upcall(char **argv)
+{
+ int rc;
+ int argc;
+ char *envp[] = {
+ "HOME=/",
+ "PATH=/sbin:/bin:/usr/sbin:/usr/bin",
+ NULL};
+
+ argv[0] = lnet_upcall;
+ argc = 1;
+ while (argv[argc] != NULL)
+ argc++;
+
+ LASSERT(argc >= 2);
+
+ rc = call_usermodehelper(argv[0], argv, envp, 1);
+ if (rc < 0 && rc != -ENOENT) {
+ CERROR("Error %d invoking LNET upcall %s %s%s%s%s%s%s%s%s; check /proc/sys/lnet/upcall\n",
+ rc, argv[0], argv[1],
+ argc < 3 ? "" : ",", argc < 3 ? "" : argv[2],
+ argc < 4 ? "" : ",", argc < 4 ? "" : argv[3],
+ argc < 5 ? "" : ",", argc < 5 ? "" : argv[4],
+ argc < 6 ? "" : ",...");
+ } else {
+ CDEBUG(D_HA, "Invoked LNET upcall %s %s%s%s%s%s%s%s%s\n",
+ argv[0], argv[1],
+ argc < 3 ? "" : ",", argc < 3 ? "" : argv[2],
+ argc < 4 ? "" : ",", argc < 4 ? "" : argv[3],
+ argc < 5 ? "" : ",", argc < 5 ? "" : argv[4],
+ argc < 6 ? "" : ",...");
+ }
+}
+
+void libcfs_run_lbug_upcall(struct libcfs_debug_msg_data *msgdata)
+{
+ char *argv[6];
+ char buf[32];
+
+ snprintf(buf, sizeof(buf), "%d", msgdata->msg_line);
+
+ argv[1] = "LBUG";
+ argv[2] = (char *)msgdata->msg_file;
+ argv[3] = (char *)msgdata->msg_fn;
+ argv[4] = buf;
+ argv[5] = NULL;
+
+ libcfs_run_upcall (argv);
+}
+
+/* coverity[+kill] */
+void lbug_with_loc(struct libcfs_debug_msg_data *msgdata)
+{
+ libcfs_catastrophe = 1;
+ libcfs_debug_msg(msgdata, "LBUG\n");
+
+ if (in_interrupt()) {
+ panic("LBUG in interrupt.\n");
+ /* not reached */
+ }
+
+ dump_stack();
+ if (!libcfs_panic_on_lbug)
+ libcfs_debug_dumplog();
+ libcfs_run_lbug_upcall(msgdata);
+ if (libcfs_panic_on_lbug)
+ panic("LBUG");
+ set_task_state(current, TASK_UNINTERRUPTIBLE);
+ while (1)
+ schedule();
+}
+
+static int panic_notifier(struct notifier_block *self, unsigned long unused1,
+ void *unused2)
+{
+ if (libcfs_panic_in_progress)
+ return 0;
+
+ libcfs_panic_in_progress = 1;
+ mb();
+
+ return 0;
+}
+
+static struct notifier_block libcfs_panic_notifier = {
+ .notifier_call = panic_notifier,
+ .next = NULL,
+ .priority = 10000,
+};
+
+void libcfs_register_panic_notifier(void)
+{
+ atomic_notifier_chain_register(&panic_notifier_list, &libcfs_panic_notifier);
+}
+
+void libcfs_unregister_panic_notifier(void)
+{
+ atomic_notifier_chain_unregister(&panic_notifier_list, &libcfs_panic_notifier);
+}
+
+EXPORT_SYMBOL(libcfs_run_upcall);
+EXPORT_SYMBOL(libcfs_run_lbug_upcall);
+EXPORT_SYMBOL(lbug_with_loc);
diff --git a/drivers/staging/lustre/lustre/libcfs/linux/linux-module.c b/drivers/staging/lustre/lustre/libcfs/linux/linux-module.c
new file mode 100644
index 000000000..e962f8968
--- /dev/null
+++ b/drivers/staging/lustre/lustre/libcfs/linux/linux-module.c
@@ -0,0 +1,183 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2012, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ */
+
+#define DEBUG_SUBSYSTEM S_LNET
+
+#include "../../../include/linux/libcfs/libcfs.h"
+
+#define LNET_MINOR 240
+
+int libcfs_ioctl_getdata(char *buf, char *end, void *arg)
+{
+ struct libcfs_ioctl_hdr *hdr;
+ struct libcfs_ioctl_data *data;
+ int orig_len;
+
+ hdr = (struct libcfs_ioctl_hdr *)buf;
+ data = (struct libcfs_ioctl_data *)buf;
+
+ if (copy_from_user(buf, (void *)arg, sizeof(*hdr)))
+ return -EFAULT;
+
+ if (hdr->ioc_version != LIBCFS_IOCTL_VERSION) {
+ CERROR("PORTALS: version mismatch kernel vs application\n");
+ return -EINVAL;
+ }
+
+ if (hdr->ioc_len >= end - buf) {
+ CERROR("PORTALS: user buffer exceeds kernel buffer\n");
+ return -EINVAL;
+ }
+
+
+ if (hdr->ioc_len < sizeof(struct libcfs_ioctl_data)) {
+ CERROR("PORTALS: user buffer too small for ioctl\n");
+ return -EINVAL;
+ }
+
+ orig_len = hdr->ioc_len;
+ if (copy_from_user(buf, (void *)arg, hdr->ioc_len))
+ return -EFAULT;
+ if (orig_len != data->ioc_len)
+ return -EINVAL;
+
+ if (libcfs_ioctl_is_invalid(data)) {
+ CERROR("PORTALS: ioctl not correctly formatted\n");
+ return -EINVAL;
+ }
+
+ if (data->ioc_inllen1)
+ data->ioc_inlbuf1 = &data->ioc_bulk[0];
+
+ if (data->ioc_inllen2)
+ data->ioc_inlbuf2 = &data->ioc_bulk[0] +
+ cfs_size_round(data->ioc_inllen1);
+
+ return 0;
+}
+
+int libcfs_ioctl_popdata(void *arg, void *data, int size)
+{
+ if (copy_to_user((char *)arg, data, size))
+ return -EFAULT;
+ return 0;
+}
+
+extern struct cfs_psdev_ops libcfs_psdev_ops;
+
+static int
+libcfs_psdev_open(struct inode *inode, struct file *file)
+{
+ struct libcfs_device_userstate **pdu = NULL;
+ int rc = 0;
+
+ if (!inode)
+ return -EINVAL;
+ pdu = (struct libcfs_device_userstate **)&file->private_data;
+ if (libcfs_psdev_ops.p_open != NULL)
+ rc = libcfs_psdev_ops.p_open(0, (void *)pdu);
+ else
+ return -EPERM;
+ return rc;
+}
+
+/* called when closing /dev/device */
+static int
+libcfs_psdev_release(struct inode *inode, struct file *file)
+{
+ struct libcfs_device_userstate *pdu;
+ int rc = 0;
+
+ if (!inode)
+ return -EINVAL;
+ pdu = file->private_data;
+ if (libcfs_psdev_ops.p_close != NULL)
+ rc = libcfs_psdev_ops.p_close(0, (void *)pdu);
+ else
+ rc = -EPERM;
+ return rc;
+}
+
+static long libcfs_ioctl(struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ struct cfs_psdev_file pfile;
+ int rc = 0;
+
+ if (!capable(CAP_SYS_ADMIN))
+ return -EACCES;
+
+ if (_IOC_TYPE(cmd) != IOC_LIBCFS_TYPE ||
+ _IOC_NR(cmd) < IOC_LIBCFS_MIN_NR ||
+ _IOC_NR(cmd) > IOC_LIBCFS_MAX_NR) {
+ CDEBUG(D_IOCTL, "invalid ioctl ( type %d, nr %d, size %d )\n",
+ _IOC_TYPE(cmd), _IOC_NR(cmd), _IOC_SIZE(cmd));
+ return -EINVAL;
+ }
+
+ /* Handle platform-dependent IOC requests */
+ switch (cmd) {
+ case IOC_LIBCFS_PANIC:
+ if (!capable(CFS_CAP_SYS_BOOT))
+ return -EPERM;
+ panic("debugctl-invoked panic");
+ return 0;
+ case IOC_LIBCFS_MEMHOG:
+ if (!capable(CFS_CAP_SYS_ADMIN))
+ return -EPERM;
+ /* go thought */
+ }
+
+ pfile.off = 0;
+ pfile.private_data = file->private_data;
+ if (libcfs_psdev_ops.p_ioctl != NULL)
+ rc = libcfs_psdev_ops.p_ioctl(&pfile, cmd, (void *)arg);
+ else
+ rc = -EPERM;
+ return rc;
+}
+
+static const struct file_operations libcfs_fops = {
+ .unlocked_ioctl = libcfs_ioctl,
+ .open = libcfs_psdev_open,
+ .release = libcfs_psdev_release,
+};
+
+struct miscdevice libcfs_dev = {
+ .minor = LNET_MINOR,
+ .name = "lnet",
+ .fops = &libcfs_fops,
+};
diff --git a/drivers/staging/lustre/lustre/libcfs/linux/linux-prim.c b/drivers/staging/lustre/lustre/libcfs/linux/linux-prim.c
new file mode 100644
index 000000000..838f5f3bd
--- /dev/null
+++ b/drivers/staging/lustre/lustre/libcfs/linux/linux-prim.c
@@ -0,0 +1,217 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2011, 2012, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ */
+
+#define DEBUG_SUBSYSTEM S_LNET
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/fs_struct.h>
+#include <linux/sched.h>
+
+#include "../../../include/linux/libcfs/libcfs.h"
+
+#if defined(CONFIG_KGDB)
+#include <linux/kgdb.h>
+#endif
+
+/**
+ * wait_queue_t of Linux (version < 2.6.34) is a FIFO list for exclusively
+ * waiting threads, which is not always desirable because all threads will
+ * be waken up again and again, even user only needs a few of them to be
+ * active most time. This is not good for performance because cache can
+ * be polluted by different threads.
+ *
+ * LIFO list can resolve this problem because we always wakeup the most
+ * recent active thread by default.
+ *
+ * NB: please don't call non-exclusive & exclusive wait on the same
+ * waitq if add_wait_queue_exclusive_head is used.
+ */
+void
+add_wait_queue_exclusive_head(wait_queue_head_t *waitq, wait_queue_t *link)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&waitq->lock, flags);
+ __add_wait_queue_exclusive(waitq, link);
+ spin_unlock_irqrestore(&waitq->lock, flags);
+}
+EXPORT_SYMBOL(add_wait_queue_exclusive_head);
+
+void cfs_init_timer(struct timer_list *t)
+{
+ init_timer(t);
+}
+EXPORT_SYMBOL(cfs_init_timer);
+
+void cfs_timer_init(struct timer_list *t, cfs_timer_func_t *func, void *arg)
+{
+ init_timer(t);
+ t->function = func;
+ t->data = (unsigned long)arg;
+}
+EXPORT_SYMBOL(cfs_timer_init);
+
+void cfs_timer_done(struct timer_list *t)
+{
+ return;
+}
+EXPORT_SYMBOL(cfs_timer_done);
+
+void cfs_timer_arm(struct timer_list *t, unsigned long deadline)
+{
+ mod_timer(t, deadline);
+}
+EXPORT_SYMBOL(cfs_timer_arm);
+
+void cfs_timer_disarm(struct timer_list *t)
+{
+ del_timer(t);
+}
+EXPORT_SYMBOL(cfs_timer_disarm);
+
+int cfs_timer_is_armed(struct timer_list *t)
+{
+ return timer_pending(t);
+}
+EXPORT_SYMBOL(cfs_timer_is_armed);
+
+unsigned long cfs_timer_deadline(struct timer_list *t)
+{
+ return t->expires;
+}
+EXPORT_SYMBOL(cfs_timer_deadline);
+
+void cfs_enter_debugger(void)
+{
+#if defined(CONFIG_KGDB)
+ /* BREAKPOINT(); */
+#else
+ /* nothing */
+#endif
+}
+EXPORT_SYMBOL(cfs_enter_debugger);
+
+
+sigset_t
+cfs_block_allsigs(void)
+{
+ unsigned long flags;
+ sigset_t old;
+
+ spin_lock_irqsave(&current->sighand->siglock, flags);
+ old = current->blocked;
+ sigfillset(&current->blocked);
+ recalc_sigpending();
+ spin_unlock_irqrestore(&current->sighand->siglock, flags);
+
+ return old;
+}
+EXPORT_SYMBOL(cfs_block_allsigs);
+
+sigset_t cfs_block_sigs(unsigned long sigs)
+{
+ unsigned long flags;
+ sigset_t old;
+
+ spin_lock_irqsave(&current->sighand->siglock, flags);
+ old = current->blocked;
+ sigaddsetmask(&current->blocked, sigs);
+ recalc_sigpending();
+ spin_unlock_irqrestore(&current->sighand->siglock, flags);
+ return old;
+}
+EXPORT_SYMBOL(cfs_block_sigs);
+
+/* Block all signals except for the @sigs */
+sigset_t cfs_block_sigsinv(unsigned long sigs)
+{
+ unsigned long flags;
+ sigset_t old;
+
+ spin_lock_irqsave(&current->sighand->siglock, flags);
+ old = current->blocked;
+ sigaddsetmask(&current->blocked, ~sigs);
+ recalc_sigpending();
+ spin_unlock_irqrestore(&current->sighand->siglock, flags);
+
+ return old;
+}
+EXPORT_SYMBOL(cfs_block_sigsinv);
+
+void
+cfs_restore_sigs(sigset_t old)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&current->sighand->siglock, flags);
+ current->blocked = old;
+ recalc_sigpending();
+ spin_unlock_irqrestore(&current->sighand->siglock, flags);
+}
+EXPORT_SYMBOL(cfs_restore_sigs);
+
+int
+cfs_signal_pending(void)
+{
+ return signal_pending(current);
+}
+EXPORT_SYMBOL(cfs_signal_pending);
+
+void
+cfs_clear_sigpending(void)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&current->sighand->siglock, flags);
+ clear_tsk_thread_flag(current, TIF_SIGPENDING);
+ spin_unlock_irqrestore(&current->sighand->siglock, flags);
+}
+EXPORT_SYMBOL(cfs_clear_sigpending);
+
+int
+libcfs_arch_init(void)
+{
+ return 0;
+}
+EXPORT_SYMBOL(libcfs_arch_init);
+
+void
+libcfs_arch_cleanup(void)
+{
+ return;
+}
+EXPORT_SYMBOL(libcfs_arch_cleanup);
+
diff --git a/drivers/staging/lustre/lustre/libcfs/linux/linux-tcpip.c b/drivers/staging/lustre/lustre/libcfs/linux/linux-tcpip.c
new file mode 100644
index 000000000..f2462e7f0
--- /dev/null
+++ b/drivers/staging/lustre/lustre/libcfs/linux/linux-tcpip.c
@@ -0,0 +1,623 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2012, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ */
+#define DEBUG_SUBSYSTEM S_LNET
+
+#include "../../../include/linux/libcfs/libcfs.h"
+
+#include <linux/if.h>
+#include <linux/in.h>
+#include <linux/file.h>
+/* For sys_open & sys_close */
+#include <linux/syscalls.h>
+
+static int
+libcfs_sock_ioctl(int cmd, unsigned long arg)
+{
+ mm_segment_t oldmm = get_fs();
+ struct socket *sock;
+ int rc;
+ struct file *sock_filp;
+
+ rc = sock_create (PF_INET, SOCK_STREAM, 0, &sock);
+ if (rc != 0) {
+ CERROR ("Can't create socket: %d\n", rc);
+ return rc;
+ }
+
+ sock_filp = sock_alloc_file(sock, 0, NULL);
+ if (IS_ERR(sock_filp)) {
+ sock_release(sock);
+ rc = PTR_ERR(sock_filp);
+ goto out;
+ }
+
+ set_fs(KERNEL_DS);
+ if (sock_filp->f_op->unlocked_ioctl)
+ rc = sock_filp->f_op->unlocked_ioctl(sock_filp, cmd, arg);
+ set_fs(oldmm);
+
+ fput(sock_filp);
+out:
+ return rc;
+}
+
+int
+libcfs_ipif_query (char *name, int *up, __u32 *ip, __u32 *mask)
+{
+ struct ifreq ifr;
+ int nob;
+ int rc;
+ __u32 val;
+
+ nob = strnlen(name, IFNAMSIZ);
+ if (nob == IFNAMSIZ) {
+ CERROR("Interface name %s too long\n", name);
+ return -EINVAL;
+ }
+
+ CLASSERT (sizeof(ifr.ifr_name) >= IFNAMSIZ);
+
+ strcpy(ifr.ifr_name, name);
+ rc = libcfs_sock_ioctl(SIOCGIFFLAGS, (unsigned long)&ifr);
+
+ if (rc != 0) {
+ CERROR("Can't get flags for interface %s\n", name);
+ return rc;
+ }
+
+ if ((ifr.ifr_flags & IFF_UP) == 0) {
+ CDEBUG(D_NET, "Interface %s down\n", name);
+ *up = 0;
+ *ip = *mask = 0;
+ return 0;
+ }
+
+ *up = 1;
+
+ strcpy(ifr.ifr_name, name);
+ ifr.ifr_addr.sa_family = AF_INET;
+ rc = libcfs_sock_ioctl(SIOCGIFADDR, (unsigned long)&ifr);
+
+ if (rc != 0) {
+ CERROR("Can't get IP address for interface %s\n", name);
+ return rc;
+ }
+
+ val = ((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr.s_addr;
+ *ip = ntohl(val);
+
+ strcpy(ifr.ifr_name, name);
+ ifr.ifr_addr.sa_family = AF_INET;
+ rc = libcfs_sock_ioctl(SIOCGIFNETMASK, (unsigned long)&ifr);
+
+ if (rc != 0) {
+ CERROR("Can't get netmask for interface %s\n", name);
+ return rc;
+ }
+
+ val = ((struct sockaddr_in *)&ifr.ifr_netmask)->sin_addr.s_addr;
+ *mask = ntohl(val);
+
+ return 0;
+}
+
+EXPORT_SYMBOL(libcfs_ipif_query);
+
+int
+libcfs_ipif_enumerate (char ***namesp)
+{
+ /* Allocate and fill in 'names', returning # interfaces/error */
+ char **names;
+ int toobig;
+ int nalloc;
+ int nfound;
+ struct ifreq *ifr;
+ struct ifconf ifc;
+ int rc;
+ int nob;
+ int i;
+
+
+ nalloc = 16; /* first guess at max interfaces */
+ toobig = 0;
+ for (;;) {
+ if (nalloc * sizeof(*ifr) > PAGE_CACHE_SIZE) {
+ toobig = 1;
+ nalloc = PAGE_CACHE_SIZE/sizeof(*ifr);
+ CWARN("Too many interfaces: only enumerating first %d\n",
+ nalloc);
+ }
+
+ LIBCFS_ALLOC(ifr, nalloc * sizeof(*ifr));
+ if (ifr == NULL) {
+ CERROR ("ENOMEM enumerating up to %d interfaces\n", nalloc);
+ rc = -ENOMEM;
+ goto out0;
+ }
+
+ ifc.ifc_buf = (char *)ifr;
+ ifc.ifc_len = nalloc * sizeof(*ifr);
+
+ rc = libcfs_sock_ioctl(SIOCGIFCONF, (unsigned long)&ifc);
+
+ if (rc < 0) {
+ CERROR ("Error %d enumerating interfaces\n", rc);
+ goto out1;
+ }
+
+ LASSERT (rc == 0);
+
+ nfound = ifc.ifc_len/sizeof(*ifr);
+ LASSERT (nfound <= nalloc);
+
+ if (nfound < nalloc || toobig)
+ break;
+
+ LIBCFS_FREE(ifr, nalloc * sizeof(*ifr));
+ nalloc *= 2;
+ }
+
+ if (nfound == 0)
+ goto out1;
+
+ LIBCFS_ALLOC(names, nfound * sizeof(*names));
+ if (names == NULL) {
+ rc = -ENOMEM;
+ goto out1;
+ }
+
+ for (i = 0; i < nfound; i++) {
+
+ nob = strnlen (ifr[i].ifr_name, IFNAMSIZ);
+ if (nob == IFNAMSIZ) {
+ /* no space for terminating NULL */
+ CERROR("interface name %.*s too long (%d max)\n",
+ nob, ifr[i].ifr_name, IFNAMSIZ);
+ rc = -ENAMETOOLONG;
+ goto out2;
+ }
+
+ LIBCFS_ALLOC(names[i], IFNAMSIZ);
+ if (names[i] == NULL) {
+ rc = -ENOMEM;
+ goto out2;
+ }
+
+ memcpy(names[i], ifr[i].ifr_name, nob);
+ names[i][nob] = 0;
+ }
+
+ *namesp = names;
+ rc = nfound;
+
+ out2:
+ if (rc < 0)
+ libcfs_ipif_free_enumeration(names, nfound);
+ out1:
+ LIBCFS_FREE(ifr, nalloc * sizeof(*ifr));
+ out0:
+ return rc;
+}
+
+EXPORT_SYMBOL(libcfs_ipif_enumerate);
+
+void
+libcfs_ipif_free_enumeration (char **names, int n)
+{
+ int i;
+
+ LASSERT (n > 0);
+
+ for (i = 0; i < n && names[i] != NULL; i++)
+ LIBCFS_FREE(names[i], IFNAMSIZ);
+
+ LIBCFS_FREE(names, n * sizeof(*names));
+}
+
+EXPORT_SYMBOL(libcfs_ipif_free_enumeration);
+
+int
+libcfs_sock_write (struct socket *sock, void *buffer, int nob, int timeout)
+{
+ int rc;
+ long ticks = timeout * HZ;
+ unsigned long then;
+ struct timeval tv;
+
+ LASSERT (nob > 0);
+ /* Caller may pass a zero timeout if she thinks the socket buffer is
+ * empty enough to take the whole message immediately */
+
+ for (;;) {
+ struct kvec iov = {
+ .iov_base = buffer,
+ .iov_len = nob
+ };
+ struct msghdr msg = {
+ .msg_flags = (timeout == 0) ? MSG_DONTWAIT : 0
+ };
+
+ if (timeout != 0) {
+ /* Set send timeout to remaining time */
+ tv = (struct timeval) {
+ .tv_sec = ticks / HZ,
+ .tv_usec = ((ticks % HZ) * 1000000) / HZ
+ };
+ rc = kernel_setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO,
+ (char *)&tv, sizeof(tv));
+ if (rc != 0) {
+ CERROR("Can't set socket send timeout %ld.%06d: %d\n",
+ (long)tv.tv_sec, (int)tv.tv_usec, rc);
+ return rc;
+ }
+ }
+
+ then = jiffies;
+ rc = kernel_sendmsg(sock, &msg, &iov, 1, nob);
+ ticks -= jiffies - then;
+
+ if (rc == nob)
+ return 0;
+
+ if (rc < 0)
+ return rc;
+
+ if (rc == 0) {
+ CERROR ("Unexpected zero rc\n");
+ return -ECONNABORTED;
+ }
+
+ if (ticks <= 0)
+ return -EAGAIN;
+
+ buffer = ((char *)buffer) + rc;
+ nob -= rc;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL(libcfs_sock_write);
+
+int
+libcfs_sock_read (struct socket *sock, void *buffer, int nob, int timeout)
+{
+ int rc;
+ long ticks = timeout * HZ;
+ unsigned long then;
+ struct timeval tv;
+
+ LASSERT (nob > 0);
+ LASSERT (ticks > 0);
+
+ for (;;) {
+ struct kvec iov = {
+ .iov_base = buffer,
+ .iov_len = nob
+ };
+ struct msghdr msg = {
+ .msg_flags = 0
+ };
+
+ /* Set receive timeout to remaining time */
+ tv = (struct timeval) {
+ .tv_sec = ticks / HZ,
+ .tv_usec = ((ticks % HZ) * 1000000) / HZ
+ };
+ rc = kernel_setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO,
+ (char *)&tv, sizeof(tv));
+ if (rc != 0) {
+ CERROR("Can't set socket recv timeout %ld.%06d: %d\n",
+ (long)tv.tv_sec, (int)tv.tv_usec, rc);
+ return rc;
+ }
+
+ then = jiffies;
+ rc = kernel_recvmsg(sock, &msg, &iov, 1, nob, 0);
+ ticks -= jiffies - then;
+
+ if (rc < 0)
+ return rc;
+
+ if (rc == 0)
+ return -ECONNRESET;
+
+ buffer = ((char *)buffer) + rc;
+ nob -= rc;
+
+ if (nob == 0)
+ return 0;
+
+ if (ticks <= 0)
+ return -ETIMEDOUT;
+ }
+}
+
+EXPORT_SYMBOL(libcfs_sock_read);
+
+static int
+libcfs_sock_create (struct socket **sockp, int *fatal,
+ __u32 local_ip, int local_port)
+{
+ struct sockaddr_in locaddr;
+ struct socket *sock;
+ int rc;
+ int option;
+
+ /* All errors are fatal except bind failure if the port is in use */
+ *fatal = 1;
+
+ rc = sock_create (PF_INET, SOCK_STREAM, 0, &sock);
+ *sockp = sock;
+ if (rc != 0) {
+ CERROR ("Can't create socket: %d\n", rc);
+ return rc;
+ }
+
+ option = 1;
+ rc = kernel_setsockopt(sock, SOL_SOCKET, SO_REUSEADDR,
+ (char *)&option, sizeof (option));
+ if (rc != 0) {
+ CERROR("Can't set SO_REUSEADDR for socket: %d\n", rc);
+ goto failed;
+ }
+
+ if (local_ip != 0 || local_port != 0) {
+ memset(&locaddr, 0, sizeof(locaddr));
+ locaddr.sin_family = AF_INET;
+ locaddr.sin_port = htons(local_port);
+ locaddr.sin_addr.s_addr = (local_ip == 0) ?
+ INADDR_ANY : htonl(local_ip);
+
+ rc = sock->ops->bind(sock, (struct sockaddr *)&locaddr,
+ sizeof(locaddr));
+ if (rc == -EADDRINUSE) {
+ CDEBUG(D_NET, "Port %d already in use\n", local_port);
+ *fatal = 0;
+ goto failed;
+ }
+ if (rc != 0) {
+ CERROR("Error trying to bind to port %d: %d\n",
+ local_port, rc);
+ goto failed;
+ }
+ }
+
+ return 0;
+
+ failed:
+ sock_release(sock);
+ return rc;
+}
+
+int
+libcfs_sock_setbuf (struct socket *sock, int txbufsize, int rxbufsize)
+{
+ int option;
+ int rc;
+
+ if (txbufsize != 0) {
+ option = txbufsize;
+ rc = kernel_setsockopt(sock, SOL_SOCKET, SO_SNDBUF,
+ (char *)&option, sizeof (option));
+ if (rc != 0) {
+ CERROR ("Can't set send buffer %d: %d\n",
+ option, rc);
+ return rc;
+ }
+ }
+
+ if (rxbufsize != 0) {
+ option = rxbufsize;
+ rc = kernel_setsockopt(sock, SOL_SOCKET, SO_RCVBUF,
+ (char *)&option, sizeof (option));
+ if (rc != 0) {
+ CERROR ("Can't set receive buffer %d: %d\n",
+ option, rc);
+ return rc;
+ }
+ }
+
+ return 0;
+}
+
+EXPORT_SYMBOL(libcfs_sock_setbuf);
+
+int
+libcfs_sock_getaddr (struct socket *sock, int remote, __u32 *ip, int *port)
+{
+ struct sockaddr_in sin;
+ int len = sizeof (sin);
+ int rc;
+
+ rc = sock->ops->getname (sock, (struct sockaddr *)&sin, &len,
+ remote ? 2 : 0);
+ if (rc != 0) {
+ CERROR ("Error %d getting sock %s IP/port\n",
+ rc, remote ? "peer" : "local");
+ return rc;
+ }
+
+ if (ip != NULL)
+ *ip = ntohl (sin.sin_addr.s_addr);
+
+ if (port != NULL)
+ *port = ntohs (sin.sin_port);
+
+ return 0;
+}
+
+EXPORT_SYMBOL(libcfs_sock_getaddr);
+
+int
+libcfs_sock_getbuf (struct socket *sock, int *txbufsize, int *rxbufsize)
+{
+
+ if (txbufsize != NULL) {
+ *txbufsize = sock->sk->sk_sndbuf;
+ }
+
+ if (rxbufsize != NULL) {
+ *rxbufsize = sock->sk->sk_rcvbuf;
+ }
+
+ return 0;
+}
+
+EXPORT_SYMBOL(libcfs_sock_getbuf);
+
+int
+libcfs_sock_listen (struct socket **sockp,
+ __u32 local_ip, int local_port, int backlog)
+{
+ int fatal;
+ int rc;
+
+ rc = libcfs_sock_create(sockp, &fatal, local_ip, local_port);
+ if (rc != 0) {
+ if (!fatal)
+ CERROR("Can't create socket: port %d already in use\n",
+ local_port);
+ return rc;
+ }
+
+ rc = (*sockp)->ops->listen(*sockp, backlog);
+ if (rc == 0)
+ return 0;
+
+ CERROR("Can't set listen backlog %d: %d\n", backlog, rc);
+ sock_release(*sockp);
+ return rc;
+}
+
+EXPORT_SYMBOL(libcfs_sock_listen);
+
+int
+libcfs_sock_accept (struct socket **newsockp, struct socket *sock)
+{
+ wait_queue_t wait;
+ struct socket *newsock;
+ int rc;
+
+ init_waitqueue_entry(&wait, current);
+
+ /* XXX this should add a ref to sock->ops->owner, if
+ * TCP could be a module */
+ rc = sock_create_lite(PF_PACKET, sock->type, IPPROTO_TCP, &newsock);
+ if (rc) {
+ CERROR("Can't allocate socket\n");
+ return rc;
+ }
+
+ newsock->ops = sock->ops;
+
+ rc = sock->ops->accept(sock, newsock, O_NONBLOCK);
+ if (rc == -EAGAIN) {
+ /* Nothing ready, so wait for activity */
+ set_current_state(TASK_INTERRUPTIBLE);
+ add_wait_queue(sk_sleep(sock->sk), &wait);
+ schedule();
+ remove_wait_queue(sk_sleep(sock->sk), &wait);
+ set_current_state(TASK_RUNNING);
+ rc = sock->ops->accept(sock, newsock, O_NONBLOCK);
+ }
+
+ if (rc != 0)
+ goto failed;
+
+ *newsockp = newsock;
+ return 0;
+
+ failed:
+ sock_release(newsock);
+ return rc;
+}
+
+EXPORT_SYMBOL(libcfs_sock_accept);
+
+void
+libcfs_sock_abort_accept (struct socket *sock)
+{
+ wake_up_all(sk_sleep(sock->sk));
+}
+
+EXPORT_SYMBOL(libcfs_sock_abort_accept);
+
+int
+libcfs_sock_connect (struct socket **sockp, int *fatal,
+ __u32 local_ip, int local_port,
+ __u32 peer_ip, int peer_port)
+{
+ struct sockaddr_in srvaddr;
+ int rc;
+
+ rc = libcfs_sock_create(sockp, fatal, local_ip, local_port);
+ if (rc != 0)
+ return rc;
+
+ memset (&srvaddr, 0, sizeof (srvaddr));
+ srvaddr.sin_family = AF_INET;
+ srvaddr.sin_port = htons(peer_port);
+ srvaddr.sin_addr.s_addr = htonl(peer_ip);
+
+ rc = (*sockp)->ops->connect(*sockp,
+ (struct sockaddr *)&srvaddr, sizeof(srvaddr),
+ 0);
+ if (rc == 0)
+ return 0;
+
+ /* EADDRNOTAVAIL probably means we're already connected to the same
+ * peer/port on the same local port on a differently typed
+ * connection. Let our caller retry with a different local
+ * port... */
+ *fatal = !(rc == -EADDRNOTAVAIL);
+
+ CDEBUG_LIMIT(*fatal ? D_NETERROR : D_NET,
+ "Error %d connecting %pI4h/%d -> %pI4h/%d\n", rc,
+ &local_ip, local_port, &peer_ip, peer_port);
+
+ sock_release(*sockp);
+ return rc;
+}
+
+EXPORT_SYMBOL(libcfs_sock_connect);
+
+void
+libcfs_sock_release (struct socket *sock)
+{
+ sock_release(sock);
+}
+
+EXPORT_SYMBOL(libcfs_sock_release);
diff --git a/drivers/staging/lustre/lustre/libcfs/linux/linux-tracefile.c b/drivers/staging/lustre/lustre/libcfs/linux/linux-tracefile.c
new file mode 100644
index 000000000..c8e293002
--- /dev/null
+++ b/drivers/staging/lustre/lustre/libcfs/linux/linux-tracefile.c
@@ -0,0 +1,275 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2012, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ */
+
+#define DEBUG_SUBSYSTEM S_LNET
+#define LUSTRE_TRACEFILE_PRIVATE
+
+#include "../../../include/linux/libcfs/libcfs.h"
+#include "../tracefile.h"
+
+/* percents to share the total debug memory for each type */
+static unsigned int pages_factor[CFS_TCD_TYPE_MAX] = {
+ 80, /* 80% pages for CFS_TCD_TYPE_PROC */
+ 10, /* 10% pages for CFS_TCD_TYPE_SOFTIRQ */
+ 10 /* 10% pages for CFS_TCD_TYPE_IRQ */
+};
+
+char *cfs_trace_console_buffers[NR_CPUS][CFS_TCD_TYPE_MAX];
+
+struct rw_semaphore cfs_tracefile_sem;
+
+int cfs_tracefile_init_arch(void)
+{
+ int i;
+ int j;
+ struct cfs_trace_cpu_data *tcd;
+
+ init_rwsem(&cfs_tracefile_sem);
+
+ /* initialize trace_data */
+ memset(cfs_trace_data, 0, sizeof(cfs_trace_data));
+ for (i = 0; i < CFS_TCD_TYPE_MAX; i++) {
+ cfs_trace_data[i] =
+ kmalloc(sizeof(union cfs_trace_data_union) *
+ num_possible_cpus(), GFP_KERNEL);
+ if (cfs_trace_data[i] == NULL)
+ goto out;
+
+ }
+
+ /* arch related info initialized */
+ cfs_tcd_for_each(tcd, i, j) {
+ spin_lock_init(&tcd->tcd_lock);
+ tcd->tcd_pages_factor = pages_factor[i];
+ tcd->tcd_type = i;
+ tcd->tcd_cpu = j;
+ }
+
+ for (i = 0; i < num_possible_cpus(); i++)
+ for (j = 0; j < 3; j++) {
+ cfs_trace_console_buffers[i][j] =
+ kmalloc(CFS_TRACE_CONSOLE_BUFFER_SIZE,
+ GFP_KERNEL);
+
+ if (cfs_trace_console_buffers[i][j] == NULL)
+ goto out;
+ }
+
+ return 0;
+
+out:
+ cfs_tracefile_fini_arch();
+ printk(KERN_ERR "lnet: Not enough memory\n");
+ return -ENOMEM;
+}
+
+void cfs_tracefile_fini_arch(void)
+{
+ int i;
+ int j;
+
+ for (i = 0; i < num_possible_cpus(); i++)
+ for (j = 0; j < 3; j++)
+ if (cfs_trace_console_buffers[i][j] != NULL) {
+ kfree(cfs_trace_console_buffers[i][j]);
+ cfs_trace_console_buffers[i][j] = NULL;
+ }
+
+ for (i = 0; cfs_trace_data[i] != NULL; i++) {
+ kfree(cfs_trace_data[i]);
+ cfs_trace_data[i] = NULL;
+ }
+}
+
+void cfs_tracefile_read_lock(void)
+{
+ down_read(&cfs_tracefile_sem);
+}
+
+void cfs_tracefile_read_unlock(void)
+{
+ up_read(&cfs_tracefile_sem);
+}
+
+void cfs_tracefile_write_lock(void)
+{
+ down_write(&cfs_tracefile_sem);
+}
+
+void cfs_tracefile_write_unlock(void)
+{
+ up_write(&cfs_tracefile_sem);
+}
+
+cfs_trace_buf_type_t cfs_trace_buf_idx_get(void)
+{
+ if (in_irq())
+ return CFS_TCD_TYPE_IRQ;
+ else if (in_softirq())
+ return CFS_TCD_TYPE_SOFTIRQ;
+ else
+ return CFS_TCD_TYPE_PROC;
+}
+
+/*
+ * The walking argument indicates the locking comes from all tcd types
+ * iterator and we must lock it and dissable local irqs to avoid deadlocks
+ * with other interrupt locks that might be happening. See LU-1311
+ * for details.
+ */
+int cfs_trace_lock_tcd(struct cfs_trace_cpu_data *tcd, int walking)
+ __acquires(&tcd->tc_lock)
+{
+ __LASSERT(tcd->tcd_type < CFS_TCD_TYPE_MAX);
+ if (tcd->tcd_type == CFS_TCD_TYPE_IRQ)
+ spin_lock_irqsave(&tcd->tcd_lock, tcd->tcd_lock_flags);
+ else if (tcd->tcd_type == CFS_TCD_TYPE_SOFTIRQ)
+ spin_lock_bh(&tcd->tcd_lock);
+ else if (unlikely(walking))
+ spin_lock_irq(&tcd->tcd_lock);
+ else
+ spin_lock(&tcd->tcd_lock);
+ return 1;
+}
+
+void cfs_trace_unlock_tcd(struct cfs_trace_cpu_data *tcd, int walking)
+ __releases(&tcd->tcd_lock)
+{
+ __LASSERT(tcd->tcd_type < CFS_TCD_TYPE_MAX);
+ if (tcd->tcd_type == CFS_TCD_TYPE_IRQ)
+ spin_unlock_irqrestore(&tcd->tcd_lock, tcd->tcd_lock_flags);
+ else if (tcd->tcd_type == CFS_TCD_TYPE_SOFTIRQ)
+ spin_unlock_bh(&tcd->tcd_lock);
+ else if (unlikely(walking))
+ spin_unlock_irq(&tcd->tcd_lock);
+ else
+ spin_unlock(&tcd->tcd_lock);
+}
+
+int cfs_tcd_owns_tage(struct cfs_trace_cpu_data *tcd,
+ struct cfs_trace_page *tage)
+{
+ /*
+ * XXX nikita: do NOT call portals_debug_msg() (CDEBUG/ENTRY/EXIT)
+ * from here: this will lead to infinite recursion.
+ */
+ return tcd->tcd_cpu == tage->cpu;
+}
+
+void
+cfs_set_ptldebug_header(struct ptldebug_header *header,
+ struct libcfs_debug_msg_data *msgdata,
+ unsigned long stack)
+{
+ struct timeval tv;
+
+ do_gettimeofday(&tv);
+
+ header->ph_subsys = msgdata->msg_subsys;
+ header->ph_mask = msgdata->msg_mask;
+ header->ph_cpu_id = smp_processor_id();
+ header->ph_type = cfs_trace_buf_idx_get();
+ header->ph_sec = (__u32)tv.tv_sec;
+ header->ph_usec = tv.tv_usec;
+ header->ph_stack = stack;
+ header->ph_pid = current->pid;
+ header->ph_line_num = msgdata->msg_line;
+ header->ph_extern_pid = 0;
+ return;
+}
+
+static char *
+dbghdr_to_err_string(struct ptldebug_header *hdr)
+{
+ switch (hdr->ph_subsys) {
+
+ case S_LND:
+ case S_LNET:
+ return "LNetError";
+ default:
+ return "LustreError";
+ }
+}
+
+static char *
+dbghdr_to_info_string(struct ptldebug_header *hdr)
+{
+ switch (hdr->ph_subsys) {
+
+ case S_LND:
+ case S_LNET:
+ return "LNet";
+ default:
+ return "Lustre";
+ }
+}
+
+void cfs_print_to_console(struct ptldebug_header *hdr, int mask,
+ const char *buf, int len, const char *file,
+ const char *fn)
+{
+ char *prefix = "Lustre", *ptype = NULL;
+
+ if ((mask & D_EMERG) != 0) {
+ prefix = dbghdr_to_err_string(hdr);
+ ptype = KERN_EMERG;
+ } else if ((mask & D_ERROR) != 0) {
+ prefix = dbghdr_to_err_string(hdr);
+ ptype = KERN_ERR;
+ } else if ((mask & D_WARNING) != 0) {
+ prefix = dbghdr_to_info_string(hdr);
+ ptype = KERN_WARNING;
+ } else if ((mask & (D_CONSOLE | libcfs_printk)) != 0) {
+ prefix = dbghdr_to_info_string(hdr);
+ ptype = KERN_INFO;
+ }
+
+ if ((mask & D_CONSOLE) != 0) {
+ printk("%s%s: %.*s", ptype, prefix, len, buf);
+ } else {
+ printk("%s%s: %d:%d:(%s:%d:%s()) %.*s", ptype, prefix,
+ hdr->ph_pid, hdr->ph_extern_pid, file, hdr->ph_line_num,
+ fn, len, buf);
+ }
+ return;
+}
+
+int cfs_trace_max_debug_mb(void)
+{
+ int total_mb = (totalram_pages >> (20 - PAGE_SHIFT));
+
+ return max(512, (total_mb * 80)/100);
+}
diff --git a/drivers/staging/lustre/lustre/libcfs/linux/linux-tracefile.h b/drivers/staging/lustre/lustre/libcfs/linux/linux-tracefile.h
new file mode 100644
index 000000000..ba84e4ffd
--- /dev/null
+++ b/drivers/staging/lustre/lustre/libcfs/linux/linux-tracefile.h
@@ -0,0 +1,48 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ */
+
+#ifndef __LIBCFS_LINUX_TRACEFILE_H__
+#define __LIBCFS_LINUX_TRACEFILE_H__
+
+/**
+ * three types of trace_data in linux
+ */
+typedef enum {
+ CFS_TCD_TYPE_PROC = 0,
+ CFS_TCD_TYPE_SOFTIRQ,
+ CFS_TCD_TYPE_IRQ,
+ CFS_TCD_TYPE_MAX
+} cfs_trace_buf_type_t;
+
+#endif
diff --git a/drivers/staging/lustre/lustre/libcfs/module.c b/drivers/staging/lustre/lustre/libcfs/module.c
new file mode 100644
index 000000000..f0ee76abf
--- /dev/null
+++ b/drivers/staging/lustre/lustre/libcfs/module.c
@@ -0,0 +1,976 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2012, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ */
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/string.h>
+#include <linux/stat.h>
+#include <linux/errno.h>
+#include <linux/unistd.h>
+#include <net/sock.h>
+#include <linux/uio.h>
+
+#include <linux/uaccess.h>
+
+#include <linux/fs.h>
+#include <linux/file.h>
+#include <linux/list.h>
+
+#include <linux/proc_fs.h>
+#include <linux/sysctl.h>
+
+# define DEBUG_SUBSYSTEM S_LNET
+
+#include "../../include/linux/libcfs/libcfs.h"
+#include <asm/div64.h>
+
+#include "../../include/linux/libcfs/libcfs_crypto.h"
+#include "../../include/linux/lnet/lib-lnet.h"
+#include "../../include/linux/lnet/lnet.h"
+#include "tracefile.h"
+
+MODULE_AUTHOR("Peter J. Braam <braam@clusterfs.com>");
+MODULE_DESCRIPTION("Portals v3.1");
+MODULE_LICENSE("GPL");
+
+extern struct miscdevice libcfs_dev;
+extern struct rw_semaphore cfs_tracefile_sem;
+extern struct mutex cfs_trace_thread_mutex;
+extern struct cfs_wi_sched *cfs_sched_rehash;
+extern void libcfs_init_nidstrings(void);
+
+static int insert_proc(void);
+static void remove_proc(void);
+
+static struct ctl_table_header *lnet_table_header;
+extern char lnet_upcall[1024];
+/**
+ * The path of debug log dump upcall script.
+ */
+extern char lnet_debug_log_upcall[1024];
+
+#define CTL_LNET (0x100)
+
+enum {
+ PSDEV_DEBUG = 1, /* control debugging */
+ PSDEV_SUBSYSTEM_DEBUG, /* control debugging */
+ PSDEV_PRINTK, /* force all messages to console */
+ PSDEV_CONSOLE_RATELIMIT, /* ratelimit console messages */
+ PSDEV_CONSOLE_MAX_DELAY_CS, /* maximum delay over which we skip messages */
+ PSDEV_CONSOLE_MIN_DELAY_CS, /* initial delay over which we skip messages */
+ PSDEV_CONSOLE_BACKOFF, /* delay increase factor */
+ PSDEV_DEBUG_PATH, /* crashdump log location */
+ PSDEV_DEBUG_DUMP_PATH, /* crashdump tracelog location */
+ PSDEV_CPT_TABLE, /* information about cpu partitions */
+ PSDEV_LNET_UPCALL, /* User mode upcall script */
+ PSDEV_LNET_MEMUSED, /* bytes currently PORTAL_ALLOCated */
+ PSDEV_LNET_CATASTROPHE, /* if we have LBUGged or panic'd */
+ PSDEV_LNET_PANIC_ON_LBUG, /* flag to panic on LBUG */
+ PSDEV_LNET_DUMP_KERNEL, /* snapshot kernel debug buffer to file */
+ PSDEV_LNET_DAEMON_FILE, /* spool kernel debug buffer to file */
+ PSDEV_LNET_DEBUG_MB, /* size of debug buffer */
+ PSDEV_LNET_DEBUG_LOG_UPCALL, /* debug log upcall script */
+ PSDEV_LNET_WATCHDOG_RATELIMIT, /* ratelimit watchdog messages */
+ PSDEV_LNET_FORCE_LBUG, /* hook to force an LBUG */
+ PSDEV_LNET_FAIL_LOC, /* control test failures instrumentation */
+ PSDEV_LNET_FAIL_VAL, /* userdata for fail loc */
+};
+
+static void kportal_memhog_free (struct libcfs_device_userstate *ldu)
+{
+ struct page **level0p = &ldu->ldu_memhog_root_page;
+ struct page **level1p;
+ struct page **level2p;
+ int count1;
+ int count2;
+
+ if (*level0p != NULL) {
+
+ level1p = (struct page **)page_address(*level0p);
+ count1 = 0;
+
+ while (count1 < PAGE_CACHE_SIZE/sizeof(struct page *) &&
+ *level1p != NULL) {
+
+ level2p = (struct page **)page_address(*level1p);
+ count2 = 0;
+
+ while (count2 < PAGE_CACHE_SIZE/sizeof(struct page *) &&
+ *level2p != NULL) {
+
+ __free_page(*level2p);
+ ldu->ldu_memhog_pages--;
+ level2p++;
+ count2++;
+ }
+
+ __free_page(*level1p);
+ ldu->ldu_memhog_pages--;
+ level1p++;
+ count1++;
+ }
+
+ __free_page(*level0p);
+ ldu->ldu_memhog_pages--;
+
+ *level0p = NULL;
+ }
+
+ LASSERT (ldu->ldu_memhog_pages == 0);
+}
+
+static int kportal_memhog_alloc(struct libcfs_device_userstate *ldu, int npages,
+ gfp_t flags)
+{
+ struct page **level0p;
+ struct page **level1p;
+ struct page **level2p;
+ int count1;
+ int count2;
+
+ LASSERT (ldu->ldu_memhog_pages == 0);
+ LASSERT (ldu->ldu_memhog_root_page == NULL);
+
+ if (npages < 0)
+ return -EINVAL;
+
+ if (npages == 0)
+ return 0;
+
+ level0p = &ldu->ldu_memhog_root_page;
+ *level0p = alloc_page(flags);
+ if (*level0p == NULL)
+ return -ENOMEM;
+ ldu->ldu_memhog_pages++;
+
+ level1p = (struct page **)page_address(*level0p);
+ count1 = 0;
+ memset(level1p, 0, PAGE_CACHE_SIZE);
+
+ while (ldu->ldu_memhog_pages < npages &&
+ count1 < PAGE_CACHE_SIZE/sizeof(struct page *)) {
+
+ if (cfs_signal_pending())
+ return -EINTR;
+
+ *level1p = alloc_page(flags);
+ if (*level1p == NULL)
+ return -ENOMEM;
+ ldu->ldu_memhog_pages++;
+
+ level2p = (struct page **)page_address(*level1p);
+ count2 = 0;
+ memset(level2p, 0, PAGE_CACHE_SIZE);
+
+ while (ldu->ldu_memhog_pages < npages &&
+ count2 < PAGE_CACHE_SIZE/sizeof(struct page *)) {
+
+ if (cfs_signal_pending())
+ return -EINTR;
+
+ *level2p = alloc_page(flags);
+ if (*level2p == NULL)
+ return -ENOMEM;
+ ldu->ldu_memhog_pages++;
+
+ level2p++;
+ count2++;
+ }
+
+ level1p++;
+ count1++;
+ }
+
+ return 0;
+}
+
+/* called when opening /dev/device */
+static int libcfs_psdev_open(unsigned long flags, void *args)
+{
+ struct libcfs_device_userstate *ldu;
+
+ try_module_get(THIS_MODULE);
+
+ LIBCFS_ALLOC(ldu, sizeof(*ldu));
+ if (ldu != NULL) {
+ ldu->ldu_memhog_pages = 0;
+ ldu->ldu_memhog_root_page = NULL;
+ }
+ *(struct libcfs_device_userstate **)args = ldu;
+
+ return 0;
+}
+
+/* called when closing /dev/device */
+static int libcfs_psdev_release(unsigned long flags, void *args)
+{
+ struct libcfs_device_userstate *ldu;
+
+ ldu = (struct libcfs_device_userstate *)args;
+ if (ldu != NULL) {
+ kportal_memhog_free(ldu);
+ LIBCFS_FREE(ldu, sizeof(*ldu));
+ }
+
+ module_put(THIS_MODULE);
+ return 0;
+}
+
+static struct rw_semaphore ioctl_list_sem;
+static struct list_head ioctl_list;
+
+int libcfs_register_ioctl(struct libcfs_ioctl_handler *hand)
+{
+ int rc = 0;
+
+ down_write(&ioctl_list_sem);
+ if (!list_empty(&hand->item))
+ rc = -EBUSY;
+ else
+ list_add_tail(&hand->item, &ioctl_list);
+ up_write(&ioctl_list_sem);
+
+ return rc;
+}
+EXPORT_SYMBOL(libcfs_register_ioctl);
+
+int libcfs_deregister_ioctl(struct libcfs_ioctl_handler *hand)
+{
+ int rc = 0;
+
+ down_write(&ioctl_list_sem);
+ if (list_empty(&hand->item))
+ rc = -ENOENT;
+ else
+ list_del_init(&hand->item);
+ up_write(&ioctl_list_sem);
+
+ return rc;
+}
+EXPORT_SYMBOL(libcfs_deregister_ioctl);
+
+static int libcfs_ioctl_int(struct cfs_psdev_file *pfile, unsigned long cmd,
+ void *arg, struct libcfs_ioctl_data *data)
+{
+ int err = -EINVAL;
+
+ switch (cmd) {
+ case IOC_LIBCFS_CLEAR_DEBUG:
+ libcfs_debug_clear_buffer();
+ return 0;
+ /*
+ * case IOC_LIBCFS_PANIC:
+ * Handled in arch/cfs_module.c
+ */
+ case IOC_LIBCFS_MARK_DEBUG:
+ if (data->ioc_inlbuf1 == NULL ||
+ data->ioc_inlbuf1[data->ioc_inllen1 - 1] != '\0')
+ return -EINVAL;
+ libcfs_debug_mark_buffer(data->ioc_inlbuf1);
+ return 0;
+ case IOC_LIBCFS_MEMHOG:
+ if (pfile->private_data == NULL) {
+ err = -EINVAL;
+ } else {
+ kportal_memhog_free(pfile->private_data);
+ /* XXX The ioc_flags is not GFP flags now, need to be fixed */
+ err = kportal_memhog_alloc(pfile->private_data,
+ data->ioc_count,
+ data->ioc_flags);
+ if (err != 0)
+ kportal_memhog_free(pfile->private_data);
+ }
+ break;
+
+ case IOC_LIBCFS_PING_TEST: {
+ extern void (kping_client)(struct libcfs_ioctl_data *);
+ void (*ping)(struct libcfs_ioctl_data *);
+
+ CDEBUG(D_IOCTL, "doing %d pings to nid %s (%s)\n",
+ data->ioc_count, libcfs_nid2str(data->ioc_nid),
+ libcfs_nid2str(data->ioc_nid));
+ ping = symbol_get(kping_client);
+ if (!ping)
+ CERROR("symbol_get failed\n");
+ else {
+ ping(data);
+ symbol_put(kping_client);
+ }
+ return 0;
+ }
+
+ default: {
+ struct libcfs_ioctl_handler *hand;
+ err = -EINVAL;
+ down_read(&ioctl_list_sem);
+ list_for_each_entry(hand, &ioctl_list, item) {
+ err = hand->handle_ioctl(cmd, data);
+ if (err != -EINVAL) {
+ if (err == 0)
+ err = libcfs_ioctl_popdata(arg,
+ data, sizeof (*data));
+ break;
+ }
+ }
+ up_read(&ioctl_list_sem);
+ break;
+ }
+ }
+
+ return err;
+}
+
+static int libcfs_ioctl(struct cfs_psdev_file *pfile, unsigned long cmd, void *arg)
+{
+ char *buf;
+ struct libcfs_ioctl_data *data;
+ int err = 0;
+
+ LIBCFS_ALLOC_GFP(buf, 1024, GFP_IOFS);
+ if (buf == NULL)
+ return -ENOMEM;
+
+ /* 'cmd' and permissions get checked in our arch-specific caller */
+ if (libcfs_ioctl_getdata(buf, buf + 800, (void *)arg)) {
+ CERROR("PORTALS ioctl: data error\n");
+ err = -EINVAL;
+ goto out;
+ }
+ data = (struct libcfs_ioctl_data *)buf;
+
+ err = libcfs_ioctl_int(pfile, cmd, arg, data);
+
+out:
+ LIBCFS_FREE(buf, 1024);
+ return err;
+}
+
+
+struct cfs_psdev_ops libcfs_psdev_ops = {
+ libcfs_psdev_open,
+ libcfs_psdev_release,
+ NULL,
+ NULL,
+ libcfs_ioctl
+};
+
+static int init_libcfs_module(void)
+{
+ int rc;
+
+ libcfs_arch_init();
+ libcfs_init_nidstrings();
+ init_rwsem(&cfs_tracefile_sem);
+ mutex_init(&cfs_trace_thread_mutex);
+ init_rwsem(&ioctl_list_sem);
+ INIT_LIST_HEAD(&ioctl_list);
+ init_waitqueue_head(&cfs_race_waitq);
+
+ rc = libcfs_debug_init(5 * 1024 * 1024);
+ if (rc < 0) {
+ pr_err("LustreError: libcfs_debug_init: %d\n", rc);
+ return rc;
+ }
+
+ rc = cfs_cpu_init();
+ if (rc != 0)
+ goto cleanup_debug;
+
+ rc = misc_register(&libcfs_dev);
+ if (rc) {
+ CERROR("misc_register: error %d\n", rc);
+ goto cleanup_cpu;
+ }
+
+ rc = cfs_wi_startup();
+ if (rc) {
+ CERROR("initialize workitem: error %d\n", rc);
+ goto cleanup_deregister;
+ }
+
+ /* max to 4 threads, should be enough for rehash */
+ rc = min(cfs_cpt_weight(cfs_cpt_table, CFS_CPT_ANY), 4);
+ rc = cfs_wi_sched_create("cfs_rh", cfs_cpt_table, CFS_CPT_ANY,
+ rc, &cfs_sched_rehash);
+ if (rc != 0) {
+ CERROR("Startup workitem scheduler: error: %d\n", rc);
+ goto cleanup_deregister;
+ }
+
+ rc = cfs_crypto_register();
+ if (rc) {
+ CERROR("cfs_crypto_register: error %d\n", rc);
+ goto cleanup_wi;
+ }
+
+
+ rc = insert_proc();
+ if (rc) {
+ CERROR("insert_proc: error %d\n", rc);
+ goto cleanup_crypto;
+ }
+
+ CDEBUG (D_OTHER, "portals setup OK\n");
+ return 0;
+ cleanup_crypto:
+ cfs_crypto_unregister();
+ cleanup_wi:
+ cfs_wi_shutdown();
+ cleanup_deregister:
+ misc_deregister(&libcfs_dev);
+cleanup_cpu:
+ cfs_cpu_fini();
+ cleanup_debug:
+ libcfs_debug_cleanup();
+ return rc;
+}
+
+static void exit_libcfs_module(void)
+{
+ int rc;
+
+ remove_proc();
+
+ CDEBUG(D_MALLOC, "before Portals cleanup: kmem %d\n",
+ atomic_read(&libcfs_kmemory));
+
+ if (cfs_sched_rehash != NULL) {
+ cfs_wi_sched_destroy(cfs_sched_rehash);
+ cfs_sched_rehash = NULL;
+ }
+
+ cfs_crypto_unregister();
+ cfs_wi_shutdown();
+
+ rc = misc_deregister(&libcfs_dev);
+ if (rc)
+ CERROR("misc_deregister error %d\n", rc);
+
+ cfs_cpu_fini();
+
+ if (atomic_read(&libcfs_kmemory) != 0)
+ CERROR("Portals memory leaked: %d bytes\n",
+ atomic_read(&libcfs_kmemory));
+
+ rc = libcfs_debug_cleanup();
+ if (rc)
+ pr_err("LustreError: libcfs_debug_cleanup: %d\n", rc);
+
+ libcfs_arch_cleanup();
+}
+
+static int proc_call_handler(void *data, int write, loff_t *ppos,
+ void __user *buffer, size_t *lenp,
+ int (*handler)(void *data, int write,
+ loff_t pos, void __user *buffer, int len))
+{
+ int rc = handler(data, write, *ppos, buffer, *lenp);
+
+ if (rc < 0)
+ return rc;
+
+ if (write) {
+ *ppos += *lenp;
+ } else {
+ *lenp = rc;
+ *ppos += rc;
+ }
+ return 0;
+}
+
+static int __proc_dobitmasks(void *data, int write,
+ loff_t pos, void __user *buffer, int nob)
+{
+ const int tmpstrlen = 512;
+ char *tmpstr;
+ int rc;
+ unsigned int *mask = data;
+ int is_subsys = (mask == &libcfs_subsystem_debug) ? 1 : 0;
+ int is_printk = (mask == &libcfs_printk) ? 1 : 0;
+
+ rc = cfs_trace_allocate_string_buffer(&tmpstr, tmpstrlen);
+ if (rc < 0)
+ return rc;
+
+ if (!write) {
+ libcfs_debug_mask2str(tmpstr, tmpstrlen, *mask, is_subsys);
+ rc = strlen(tmpstr);
+
+ if (pos >= rc) {
+ rc = 0;
+ } else {
+ rc = cfs_trace_copyout_string(buffer, nob,
+ tmpstr + pos, "\n");
+ }
+ } else {
+ rc = cfs_trace_copyin_string(tmpstr, tmpstrlen, buffer, nob);
+ if (rc < 0) {
+ cfs_trace_free_string_buffer(tmpstr, tmpstrlen);
+ return rc;
+ }
+
+ rc = libcfs_debug_str2mask(mask, tmpstr, is_subsys);
+ /* Always print LBUG/LASSERT to console, so keep this mask */
+ if (is_printk)
+ *mask |= D_EMERG;
+ }
+
+ cfs_trace_free_string_buffer(tmpstr, tmpstrlen);
+ return rc;
+}
+
+static int proc_dobitmasks(struct ctl_table *table, int write,
+ void __user *buffer, size_t *lenp, loff_t *ppos)
+{
+ return proc_call_handler(table->data, write, ppos, buffer, lenp,
+ __proc_dobitmasks);
+}
+
+static int min_watchdog_ratelimit; /* disable ratelimiting */
+static int max_watchdog_ratelimit = (24*60*60); /* limit to once per day */
+
+static int __proc_dump_kernel(void *data, int write,
+ loff_t pos, void __user *buffer, int nob)
+{
+ if (!write)
+ return 0;
+
+ return cfs_trace_dump_debug_buffer_usrstr(buffer, nob);
+}
+
+static int proc_dump_kernel(struct ctl_table *table, int write,
+ void __user *buffer, size_t *lenp, loff_t *ppos)
+{
+ return proc_call_handler(table->data, write, ppos, buffer, lenp,
+ __proc_dump_kernel);
+}
+
+static int __proc_daemon_file(void *data, int write,
+ loff_t pos, void __user *buffer, int nob)
+{
+ if (!write) {
+ int len = strlen(cfs_tracefile);
+
+ if (pos >= len)
+ return 0;
+
+ return cfs_trace_copyout_string(buffer, nob,
+ cfs_tracefile + pos, "\n");
+ }
+
+ return cfs_trace_daemon_command_usrstr(buffer, nob);
+}
+
+static int proc_daemon_file(struct ctl_table *table, int write,
+ void __user *buffer, size_t *lenp, loff_t *ppos)
+{
+ return proc_call_handler(table->data, write, ppos, buffer, lenp,
+ __proc_daemon_file);
+}
+
+static int __proc_debug_mb(void *data, int write,
+ loff_t pos, void __user *buffer, int nob)
+{
+ if (!write) {
+ char tmpstr[32];
+ int len = snprintf(tmpstr, sizeof(tmpstr), "%d",
+ cfs_trace_get_debug_mb());
+
+ if (pos >= len)
+ return 0;
+
+ return cfs_trace_copyout_string(buffer, nob, tmpstr + pos,
+ "\n");
+ }
+
+ return cfs_trace_set_debug_mb_usrstr(buffer, nob);
+}
+
+static int proc_debug_mb(struct ctl_table *table, int write,
+ void __user *buffer, size_t *lenp, loff_t *ppos)
+{
+ return proc_call_handler(table->data, write, ppos, buffer, lenp,
+ __proc_debug_mb);
+}
+
+static int proc_console_max_delay_cs(struct ctl_table *table, int write,
+ void __user *buffer, size_t *lenp,
+ loff_t *ppos)
+{
+ int rc, max_delay_cs;
+ struct ctl_table dummy = *table;
+ long d;
+
+ dummy.data = &max_delay_cs;
+ dummy.proc_handler = &proc_dointvec;
+
+ if (!write) { /* read */
+ max_delay_cs = cfs_duration_sec(libcfs_console_max_delay * 100);
+ rc = proc_dointvec(&dummy, write, buffer, lenp, ppos);
+ return rc;
+ }
+
+ /* write */
+ max_delay_cs = 0;
+ rc = proc_dointvec(&dummy, write, buffer, lenp, ppos);
+ if (rc < 0)
+ return rc;
+ if (max_delay_cs <= 0)
+ return -EINVAL;
+
+ d = cfs_time_seconds(max_delay_cs) / 100;
+ if (d == 0 || d < libcfs_console_min_delay)
+ return -EINVAL;
+ libcfs_console_max_delay = d;
+
+ return rc;
+}
+
+static int proc_console_min_delay_cs(struct ctl_table *table, int write,
+ void __user *buffer, size_t *lenp,
+ loff_t *ppos)
+{
+ int rc, min_delay_cs;
+ struct ctl_table dummy = *table;
+ long d;
+
+ dummy.data = &min_delay_cs;
+ dummy.proc_handler = &proc_dointvec;
+
+ if (!write) { /* read */
+ min_delay_cs = cfs_duration_sec(libcfs_console_min_delay * 100);
+ rc = proc_dointvec(&dummy, write, buffer, lenp, ppos);
+ return rc;
+ }
+
+ /* write */
+ min_delay_cs = 0;
+ rc = proc_dointvec(&dummy, write, buffer, lenp, ppos);
+ if (rc < 0)
+ return rc;
+ if (min_delay_cs <= 0)
+ return -EINVAL;
+
+ d = cfs_time_seconds(min_delay_cs) / 100;
+ if (d == 0 || d > libcfs_console_max_delay)
+ return -EINVAL;
+ libcfs_console_min_delay = d;
+
+ return rc;
+}
+
+static int proc_console_backoff(struct ctl_table *table, int write,
+ void __user *buffer, size_t *lenp, loff_t *ppos)
+{
+ int rc, backoff;
+ struct ctl_table dummy = *table;
+
+ dummy.data = &backoff;
+ dummy.proc_handler = &proc_dointvec;
+
+ if (!write) { /* read */
+ backoff = libcfs_console_backoff;
+ rc = proc_dointvec(&dummy, write, buffer, lenp, ppos);
+ return rc;
+ }
+
+ /* write */
+ backoff = 0;
+ rc = proc_dointvec(&dummy, write, buffer, lenp, ppos);
+ if (rc < 0)
+ return rc;
+ if (backoff <= 0)
+ return -EINVAL;
+
+ libcfs_console_backoff = backoff;
+
+ return rc;
+}
+
+static int libcfs_force_lbug(struct ctl_table *table, int write,
+ void __user *buffer,
+ size_t *lenp, loff_t *ppos)
+{
+ if (write)
+ LBUG();
+ return 0;
+}
+
+static int proc_fail_loc(struct ctl_table *table, int write,
+ void __user *buffer,
+ size_t *lenp, loff_t *ppos)
+{
+ int rc;
+ long old_fail_loc = cfs_fail_loc;
+
+ rc = proc_doulongvec_minmax(table, write, buffer, lenp, ppos);
+ if (old_fail_loc != cfs_fail_loc)
+ wake_up(&cfs_race_waitq);
+ return rc;
+}
+
+static int __proc_cpt_table(void *data, int write,
+ loff_t pos, void __user *buffer, int nob)
+{
+ char *buf = NULL;
+ int len = 4096;
+ int rc = 0;
+
+ if (write)
+ return -EPERM;
+
+ LASSERT(cfs_cpt_table != NULL);
+
+ while (1) {
+ LIBCFS_ALLOC(buf, len);
+ if (buf == NULL)
+ return -ENOMEM;
+
+ rc = cfs_cpt_table_print(cfs_cpt_table, buf, len);
+ if (rc >= 0)
+ break;
+
+ if (rc == -EFBIG) {
+ LIBCFS_FREE(buf, len);
+ len <<= 1;
+ continue;
+ }
+ goto out;
+ }
+
+ if (pos >= rc) {
+ rc = 0;
+ goto out;
+ }
+
+ rc = cfs_trace_copyout_string(buffer, nob, buf + pos, NULL);
+ out:
+ if (buf != NULL)
+ LIBCFS_FREE(buf, len);
+ return rc;
+}
+
+static int proc_cpt_table(struct ctl_table *table, int write,
+ void __user *buffer, size_t *lenp, loff_t *ppos)
+{
+ return proc_call_handler(table->data, write, ppos, buffer, lenp,
+ __proc_cpt_table);
+}
+
+static struct ctl_table lnet_table[] = {
+ /*
+ * NB No .strategy entries have been provided since sysctl(8) prefers
+ * to go via /proc for portability.
+ */
+ {
+ .procname = "debug",
+ .data = &libcfs_debug,
+ .maxlen = sizeof(int),
+ .mode = 0644,
+ .proc_handler = &proc_dobitmasks,
+ },
+ {
+ .procname = "subsystem_debug",
+ .data = &libcfs_subsystem_debug,
+ .maxlen = sizeof(int),
+ .mode = 0644,
+ .proc_handler = &proc_dobitmasks,
+ },
+ {
+ .procname = "printk",
+ .data = &libcfs_printk,
+ .maxlen = sizeof(int),
+ .mode = 0644,
+ .proc_handler = &proc_dobitmasks,
+ },
+ {
+ .procname = "console_ratelimit",
+ .data = &libcfs_console_ratelimit,
+ .maxlen = sizeof(int),
+ .mode = 0644,
+ .proc_handler = &proc_dointvec
+ },
+ {
+ .procname = "console_max_delay_centisecs",
+ .maxlen = sizeof(int),
+ .mode = 0644,
+ .proc_handler = &proc_console_max_delay_cs
+ },
+ {
+ .procname = "console_min_delay_centisecs",
+ .maxlen = sizeof(int),
+ .mode = 0644,
+ .proc_handler = &proc_console_min_delay_cs
+ },
+ {
+ .procname = "console_backoff",
+ .maxlen = sizeof(int),
+ .mode = 0644,
+ .proc_handler = &proc_console_backoff
+ },
+
+ {
+ .procname = "debug_path",
+ .data = libcfs_debug_file_path_arr,
+ .maxlen = sizeof(libcfs_debug_file_path_arr),
+ .mode = 0644,
+ .proc_handler = &proc_dostring,
+ },
+
+ {
+ .procname = "cpu_partition_table",
+ .maxlen = 128,
+ .mode = 0444,
+ .proc_handler = &proc_cpt_table,
+ },
+
+ {
+ .procname = "upcall",
+ .data = lnet_upcall,
+ .maxlen = sizeof(lnet_upcall),
+ .mode = 0644,
+ .proc_handler = &proc_dostring,
+ },
+ {
+ .procname = "debug_log_upcall",
+ .data = lnet_debug_log_upcall,
+ .maxlen = sizeof(lnet_debug_log_upcall),
+ .mode = 0644,
+ .proc_handler = &proc_dostring,
+ },
+ {
+ .procname = "lnet_memused",
+ .data = (int *)&libcfs_kmemory.counter,
+ .maxlen = sizeof(int),
+ .mode = 0444,
+ .proc_handler = &proc_dointvec,
+ },
+ {
+ .procname = "catastrophe",
+ .data = &libcfs_catastrophe,
+ .maxlen = sizeof(int),
+ .mode = 0444,
+ .proc_handler = &proc_dointvec,
+ },
+ {
+ .procname = "panic_on_lbug",
+ .data = &libcfs_panic_on_lbug,
+ .maxlen = sizeof(int),
+ .mode = 0644,
+ .proc_handler = &proc_dointvec,
+ },
+ {
+ .procname = "dump_kernel",
+ .maxlen = 256,
+ .mode = 0200,
+ .proc_handler = &proc_dump_kernel,
+ },
+ {
+ .procname = "daemon_file",
+ .mode = 0644,
+ .maxlen = 256,
+ .proc_handler = &proc_daemon_file,
+ },
+ {
+ .procname = "debug_mb",
+ .mode = 0644,
+ .proc_handler = &proc_debug_mb,
+ },
+ {
+ .procname = "watchdog_ratelimit",
+ .data = &libcfs_watchdog_ratelimit,
+ .maxlen = sizeof(int),
+ .mode = 0644,
+ .proc_handler = &proc_dointvec_minmax,
+ .extra1 = &min_watchdog_ratelimit,
+ .extra2 = &max_watchdog_ratelimit,
+ },
+ {
+ .procname = "force_lbug",
+ .data = NULL,
+ .maxlen = 0,
+ .mode = 0200,
+ .proc_handler = &libcfs_force_lbug
+ },
+ {
+ .procname = "fail_loc",
+ .data = &cfs_fail_loc,
+ .maxlen = sizeof(cfs_fail_loc),
+ .mode = 0644,
+ .proc_handler = &proc_fail_loc
+ },
+ {
+ .procname = "fail_val",
+ .data = &cfs_fail_val,
+ .maxlen = sizeof(int),
+ .mode = 0644,
+ .proc_handler = &proc_dointvec
+ },
+ {
+ }
+};
+
+static struct ctl_table top_table[] = {
+ {
+ .procname = "lnet",
+ .mode = 0555,
+ .data = NULL,
+ .maxlen = 0,
+ .child = lnet_table,
+ },
+ {
+ }
+};
+
+static int insert_proc(void)
+{
+ if (lnet_table_header == NULL)
+ lnet_table_header = register_sysctl_table(top_table);
+ return 0;
+}
+
+static void remove_proc(void)
+{
+ if (lnet_table_header != NULL)
+ unregister_sysctl_table(lnet_table_header);
+
+ lnet_table_header = NULL;
+}
+
+MODULE_VERSION("1.0.0");
+
+module_init(init_libcfs_module);
+module_exit(exit_libcfs_module);
diff --git a/drivers/staging/lustre/lustre/libcfs/nidstrings.c b/drivers/staging/lustre/lustre/libcfs/nidstrings.c
new file mode 100644
index 000000000..087449f4e
--- /dev/null
+++ b/drivers/staging/lustre/lustre/libcfs/nidstrings.c
@@ -0,0 +1,842 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2011, 2012, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ *
+ * libcfs/libcfs/nidstrings.c
+ *
+ * Author: Phil Schwan <phil@clusterfs.com>
+ */
+
+#define DEBUG_SUBSYSTEM S_LNET
+
+#include "../../include/linux/libcfs/libcfs.h"
+#include "../../include/linux/lnet/lnet.h"
+
+/* CAVEAT VENDITOR! Keep the canonical string representation of nets/nids
+ * consistent in all conversion functions. Some code fragments are copied
+ * around for the sake of clarity...
+ */
+
+/* CAVEAT EMPTOR! Racey temporary buffer allocation!
+ * Choose the number of nidstrings to support the MAXIMUM expected number of
+ * concurrent users. If there are more, the returned string will be volatile.
+ * NB this number must allow for a process to be descheduled for a timeslice
+ * between getting its string and using it.
+ */
+
+static char libcfs_nidstrings[LNET_NIDSTR_COUNT][LNET_NIDSTR_SIZE];
+static int libcfs_nidstring_idx;
+
+static spinlock_t libcfs_nidstring_lock;
+
+void libcfs_init_nidstrings(void)
+{
+ spin_lock_init(&libcfs_nidstring_lock);
+}
+
+static char *
+libcfs_next_nidstring(void)
+{
+ char *str;
+ unsigned long flags;
+
+ spin_lock_irqsave(&libcfs_nidstring_lock, flags);
+
+ str = libcfs_nidstrings[libcfs_nidstring_idx++];
+ if (libcfs_nidstring_idx == ARRAY_SIZE(libcfs_nidstrings))
+ libcfs_nidstring_idx = 0;
+
+ spin_unlock_irqrestore(&libcfs_nidstring_lock, flags);
+ return str;
+}
+
+static int libcfs_lo_str2addr(const char *str, int nob, __u32 *addr)
+{
+ *addr = 0;
+ return 1;
+}
+
+static void libcfs_ip_addr2str(__u32 addr, char *str)
+{
+ snprintf(str, LNET_NIDSTR_SIZE, "%u.%u.%u.%u",
+ (addr >> 24) & 0xff, (addr >> 16) & 0xff,
+ (addr >> 8) & 0xff, addr & 0xff);
+}
+
+static int libcfs_ip_str2addr(const char *str, int nob, __u32 *addr)
+{
+ unsigned int a;
+ unsigned int b;
+ unsigned int c;
+ unsigned int d;
+ int n = nob; /* XscanfX */
+
+ /* numeric IP? */
+ if (sscanf(str, "%u.%u.%u.%u%n", &a, &b, &c, &d, &n) >= 4 &&
+ n == nob &&
+ (a & ~0xff) == 0 && (b & ~0xff) == 0 &&
+ (c & ~0xff) == 0 && (d & ~0xff) == 0) {
+ *addr = ((a<<24)|(b<<16)|(c<<8)|d);
+ return 1;
+ }
+
+ return 0;
+}
+
+static void libcfs_decnum_addr2str(__u32 addr, char *str)
+{
+ snprintf(str, LNET_NIDSTR_SIZE, "%u", addr);
+}
+
+static void libcfs_hexnum_addr2str(__u32 addr, char *str)
+{
+ snprintf(str, LNET_NIDSTR_SIZE, "0x%x", addr);
+}
+
+static int libcfs_num_str2addr(const char *str, int nob, __u32 *addr)
+{
+ int n;
+
+ n = nob;
+ if (sscanf(str, "0x%x%n", addr, &n) >= 1 && n == nob)
+ return 1;
+
+ n = nob;
+ if (sscanf(str, "0X%x%n", addr, &n) >= 1 && n == nob)
+ return 1;
+
+ n = nob;
+ if (sscanf(str, "%u%n", addr, &n) >= 1 && n == nob)
+ return 1;
+
+ return 0;
+}
+
+/**
+ * Nf_parse_addrlist method for networks using numeric addresses.
+ *
+ * Examples of such networks are gm and elan.
+ *
+ * \retval 0 if \a str parsed to numeric address
+ * \retval errno otherwise
+ */
+static int
+libcfs_num_parse(char *str, int len, struct list_head *list)
+{
+ struct cfs_expr_list *el;
+ int rc;
+
+ rc = cfs_expr_list_parse(str, len, 0, MAX_NUMERIC_VALUE, &el);
+ if (rc == 0)
+ list_add_tail(&el->el_link, list);
+
+ return rc;
+}
+
+/*
+ * Nf_match_addr method for networks using numeric addresses
+ *
+ * \retval 1 on match
+ * \retval 0 otherwise
+ */
+static int
+libcfs_num_match(__u32 addr, struct list_head *numaddr)
+{
+ struct cfs_expr_list *el;
+
+ LASSERT(!list_empty(numaddr));
+ el = list_entry(numaddr->next, struct cfs_expr_list, el_link);
+
+ return cfs_expr_list_match(addr, el);
+}
+
+struct netstrfns {
+ int nf_type;
+ char *nf_name;
+ char *nf_modname;
+ void (*nf_addr2str)(__u32 addr, char *str);
+ int (*nf_str2addr)(const char *str, int nob, __u32 *addr);
+ int (*nf_parse_addrlist)(char *str, int len,
+ struct list_head *list);
+ int (*nf_match_addr)(__u32 addr, struct list_head *list);
+};
+
+static struct netstrfns libcfs_netstrfns[] = {
+ {/* .nf_type */ LOLND,
+ /* .nf_name */ "lo",
+ /* .nf_modname */ "klolnd",
+ /* .nf_addr2str */ libcfs_decnum_addr2str,
+ /* .nf_str2addr */ libcfs_lo_str2addr,
+ /* .nf_parse_addr*/ libcfs_num_parse,
+ /* .nf_match_addr*/ libcfs_num_match},
+ {/* .nf_type */ SOCKLND,
+ /* .nf_name */ "tcp",
+ /* .nf_modname */ "ksocklnd",
+ /* .nf_addr2str */ libcfs_ip_addr2str,
+ /* .nf_str2addr */ libcfs_ip_str2addr,
+ /* .nf_parse_addrlist*/ cfs_ip_addr_parse,
+ /* .nf_match_addr*/ cfs_ip_addr_match},
+ {/* .nf_type */ O2IBLND,
+ /* .nf_name */ "o2ib",
+ /* .nf_modname */ "ko2iblnd",
+ /* .nf_addr2str */ libcfs_ip_addr2str,
+ /* .nf_str2addr */ libcfs_ip_str2addr,
+ /* .nf_parse_addrlist*/ cfs_ip_addr_parse,
+ /* .nf_match_addr*/ cfs_ip_addr_match},
+ {/* .nf_type */ CIBLND,
+ /* .nf_name */ "cib",
+ /* .nf_modname */ "kciblnd",
+ /* .nf_addr2str */ libcfs_ip_addr2str,
+ /* .nf_str2addr */ libcfs_ip_str2addr,
+ /* .nf_parse_addrlist*/ cfs_ip_addr_parse,
+ /* .nf_match_addr*/ cfs_ip_addr_match},
+ {/* .nf_type */ OPENIBLND,
+ /* .nf_name */ "openib",
+ /* .nf_modname */ "kopeniblnd",
+ /* .nf_addr2str */ libcfs_ip_addr2str,
+ /* .nf_str2addr */ libcfs_ip_str2addr,
+ /* .nf_parse_addrlist*/ cfs_ip_addr_parse,
+ /* .nf_match_addr*/ cfs_ip_addr_match},
+ {/* .nf_type */ IIBLND,
+ /* .nf_name */ "iib",
+ /* .nf_modname */ "kiiblnd",
+ /* .nf_addr2str */ libcfs_ip_addr2str,
+ /* .nf_str2addr */ libcfs_ip_str2addr,
+ /* .nf_parse_addrlist*/ cfs_ip_addr_parse,
+ /* .nf_match_addr*/ cfs_ip_addr_match},
+ {/* .nf_type */ VIBLND,
+ /* .nf_name */ "vib",
+ /* .nf_modname */ "kviblnd",
+ /* .nf_addr2str */ libcfs_ip_addr2str,
+ /* .nf_str2addr */ libcfs_ip_str2addr,
+ /* .nf_parse_addrlist*/ cfs_ip_addr_parse,
+ /* .nf_match_addr*/ cfs_ip_addr_match},
+ {/* .nf_type */ RALND,
+ /* .nf_name */ "ra",
+ /* .nf_modname */ "kralnd",
+ /* .nf_addr2str */ libcfs_ip_addr2str,
+ /* .nf_str2addr */ libcfs_ip_str2addr,
+ /* .nf_parse_addrlist*/ cfs_ip_addr_parse,
+ /* .nf_match_addr*/ cfs_ip_addr_match},
+ {/* .nf_type */ QSWLND,
+ /* .nf_name */ "elan",
+ /* .nf_modname */ "kqswlnd",
+ /* .nf_addr2str */ libcfs_decnum_addr2str,
+ /* .nf_str2addr */ libcfs_num_str2addr,
+ /* .nf_parse_addrlist*/ libcfs_num_parse,
+ /* .nf_match_addr*/ libcfs_num_match},
+ {/* .nf_type */ GMLND,
+ /* .nf_name */ "gm",
+ /* .nf_modname */ "kgmlnd",
+ /* .nf_addr2str */ libcfs_hexnum_addr2str,
+ /* .nf_str2addr */ libcfs_num_str2addr,
+ /* .nf_parse_addrlist*/ libcfs_num_parse,
+ /* .nf_match_addr*/ libcfs_num_match},
+ {/* .nf_type */ MXLND,
+ /* .nf_name */ "mx",
+ /* .nf_modname */ "kmxlnd",
+ /* .nf_addr2str */ libcfs_ip_addr2str,
+ /* .nf_str2addr */ libcfs_ip_str2addr,
+ /* .nf_parse_addrlist*/ cfs_ip_addr_parse,
+ /* .nf_match_addr*/ cfs_ip_addr_match},
+ {/* .nf_type */ PTLLND,
+ /* .nf_name */ "ptl",
+ /* .nf_modname */ "kptllnd",
+ /* .nf_addr2str */ libcfs_decnum_addr2str,
+ /* .nf_str2addr */ libcfs_num_str2addr,
+ /* .nf_parse_addrlist*/ libcfs_num_parse,
+ /* .nf_match_addr*/ libcfs_num_match},
+ {/* .nf_type */ GNILND,
+ /* .nf_name */ "gni",
+ /* .nf_modname */ "kgnilnd",
+ /* .nf_addr2str */ libcfs_decnum_addr2str,
+ /* .nf_str2addr */ libcfs_num_str2addr,
+ /* .nf_parse_addrlist*/ libcfs_num_parse,
+ /* .nf_match_addr*/ libcfs_num_match},
+ /* placeholder for net0 alias. It MUST BE THE LAST ENTRY */
+ {/* .nf_type */ -1},
+};
+
+static const int libcfs_nnetstrfns = ARRAY_SIZE(libcfs_netstrfns);
+
+/* CAVEAT EMPTOR XscanfX
+ * I use "%n" at the end of a sscanf format to detect trailing junk. However
+ * sscanf may return immediately if it sees the terminating '0' in a string, so
+ * I initialise the %n variable to the expected length. If sscanf sets it;
+ * fine, if it doesn't, then the scan ended at the end of the string, which is
+ * fine too :) */
+
+static struct netstrfns *
+libcfs_lnd2netstrfns(int lnd)
+{
+ int i;
+
+ if (lnd >= 0)
+ for (i = 0; i < libcfs_nnetstrfns; i++)
+ if (lnd == libcfs_netstrfns[i].nf_type)
+ return &libcfs_netstrfns[i];
+
+ return NULL;
+}
+
+static struct netstrfns *
+libcfs_namenum2netstrfns(const char *name)
+{
+ struct netstrfns *nf;
+ int i;
+
+ for (i = 0; i < libcfs_nnetstrfns; i++) {
+ nf = &libcfs_netstrfns[i];
+ if (nf->nf_type >= 0 &&
+ !strncmp(name, nf->nf_name, strlen(nf->nf_name)))
+ return nf;
+ }
+ return NULL;
+}
+
+static struct netstrfns *
+libcfs_name2netstrfns(const char *name)
+{
+ int i;
+
+ for (i = 0; i < libcfs_nnetstrfns; i++)
+ if (libcfs_netstrfns[i].nf_type >= 0 &&
+ !strcmp(libcfs_netstrfns[i].nf_name, name))
+ return &libcfs_netstrfns[i];
+
+ return NULL;
+}
+
+int
+libcfs_isknown_lnd(int type)
+{
+ return libcfs_lnd2netstrfns(type) != NULL;
+}
+EXPORT_SYMBOL(libcfs_isknown_lnd);
+
+char *
+libcfs_lnd2modname(int lnd)
+{
+ struct netstrfns *nf = libcfs_lnd2netstrfns(lnd);
+
+ return (nf == NULL) ? NULL : nf->nf_modname;
+}
+EXPORT_SYMBOL(libcfs_lnd2modname);
+
+char *
+libcfs_lnd2str(int lnd)
+{
+ char *str;
+ struct netstrfns *nf = libcfs_lnd2netstrfns(lnd);
+
+ if (nf != NULL)
+ return nf->nf_name;
+
+ str = libcfs_next_nidstring();
+ snprintf(str, LNET_NIDSTR_SIZE, "?%d?", lnd);
+ return str;
+}
+EXPORT_SYMBOL(libcfs_lnd2str);
+
+int
+libcfs_str2lnd(const char *str)
+{
+ struct netstrfns *nf = libcfs_name2netstrfns(str);
+
+ if (nf != NULL)
+ return nf->nf_type;
+
+ return -1;
+}
+EXPORT_SYMBOL(libcfs_str2lnd);
+
+char *
+libcfs_net2str(__u32 net)
+{
+ int lnd = LNET_NETTYP(net);
+ int num = LNET_NETNUM(net);
+ struct netstrfns *nf = libcfs_lnd2netstrfns(lnd);
+ char *str = libcfs_next_nidstring();
+
+ if (nf == NULL)
+ snprintf(str, LNET_NIDSTR_SIZE, "<%d:%d>", lnd, num);
+ else if (num == 0)
+ snprintf(str, LNET_NIDSTR_SIZE, "%s", nf->nf_name);
+ else
+ snprintf(str, LNET_NIDSTR_SIZE, "%s%d", nf->nf_name, num);
+
+ return str;
+}
+EXPORT_SYMBOL(libcfs_net2str);
+
+char *
+libcfs_nid2str(lnet_nid_t nid)
+{
+ __u32 addr = LNET_NIDADDR(nid);
+ __u32 net = LNET_NIDNET(nid);
+ int lnd = LNET_NETTYP(net);
+ int nnum = LNET_NETNUM(net);
+ struct netstrfns *nf;
+ char *str;
+ int nob;
+
+ if (nid == LNET_NID_ANY)
+ return "<?>";
+
+ nf = libcfs_lnd2netstrfns(lnd);
+ str = libcfs_next_nidstring();
+
+ if (nf == NULL)
+ snprintf(str, LNET_NIDSTR_SIZE, "%x@<%d:%d>", addr, lnd, nnum);
+ else {
+ nf->nf_addr2str(addr, str);
+ nob = strlen(str);
+ if (nnum == 0)
+ snprintf(str + nob, LNET_NIDSTR_SIZE - nob, "@%s",
+ nf->nf_name);
+ else
+ snprintf(str + nob, LNET_NIDSTR_SIZE - nob, "@%s%d",
+ nf->nf_name, nnum);
+ }
+
+ return str;
+}
+EXPORT_SYMBOL(libcfs_nid2str);
+
+static struct netstrfns *
+libcfs_str2net_internal(const char *str, __u32 *net)
+{
+ struct netstrfns *uninitialized_var(nf);
+ int nob;
+ unsigned int netnum;
+ int i;
+
+ for (i = 0; i < libcfs_nnetstrfns; i++) {
+ nf = &libcfs_netstrfns[i];
+ if (nf->nf_type >= 0 &&
+ !strncmp(str, nf->nf_name, strlen(nf->nf_name)))
+ break;
+ }
+
+ if (i == libcfs_nnetstrfns)
+ return NULL;
+
+ nob = strlen(nf->nf_name);
+
+ if (strlen(str) == (unsigned int)nob) {
+ netnum = 0;
+ } else {
+ if (nf->nf_type == LOLND) /* net number not allowed */
+ return NULL;
+
+ str += nob;
+ i = strlen(str);
+ if (sscanf(str, "%u%n", &netnum, &i) < 1 ||
+ i != (int)strlen(str))
+ return NULL;
+ }
+
+ *net = LNET_MKNET(nf->nf_type, netnum);
+ return nf;
+}
+
+__u32
+libcfs_str2net(const char *str)
+{
+ __u32 net;
+
+ if (libcfs_str2net_internal(str, &net) != NULL)
+ return net;
+
+ return LNET_NIDNET(LNET_NID_ANY);
+}
+EXPORT_SYMBOL(libcfs_str2net);
+
+lnet_nid_t
+libcfs_str2nid(const char *str)
+{
+ const char *sep = strchr(str, '@');
+ struct netstrfns *nf;
+ __u32 net;
+ __u32 addr;
+
+ if (sep != NULL) {
+ nf = libcfs_str2net_internal(sep + 1, &net);
+ if (nf == NULL)
+ return LNET_NID_ANY;
+ } else {
+ sep = str + strlen(str);
+ net = LNET_MKNET(SOCKLND, 0);
+ nf = libcfs_lnd2netstrfns(SOCKLND);
+ LASSERT(nf != NULL);
+ }
+
+ if (!nf->nf_str2addr(str, (int)(sep - str), &addr))
+ return LNET_NID_ANY;
+
+ return LNET_MKNID(net, addr);
+}
+EXPORT_SYMBOL(libcfs_str2nid);
+
+char *
+libcfs_id2str(lnet_process_id_t id)
+{
+ char *str = libcfs_next_nidstring();
+
+ if (id.pid == LNET_PID_ANY) {
+ snprintf(str, LNET_NIDSTR_SIZE,
+ "LNET_PID_ANY-%s", libcfs_nid2str(id.nid));
+ return str;
+ }
+
+ snprintf(str, LNET_NIDSTR_SIZE, "%s%u-%s",
+ ((id.pid & LNET_PID_USERFLAG) != 0) ? "U" : "",
+ (id.pid & ~LNET_PID_USERFLAG), libcfs_nid2str(id.nid));
+ return str;
+}
+EXPORT_SYMBOL(libcfs_id2str);
+
+int
+libcfs_str2anynid(lnet_nid_t *nidp, const char *str)
+{
+ if (!strcmp(str, "*")) {
+ *nidp = LNET_NID_ANY;
+ return 1;
+ }
+
+ *nidp = libcfs_str2nid(str);
+ return *nidp != LNET_NID_ANY;
+}
+EXPORT_SYMBOL(libcfs_str2anynid);
+
+/**
+ * Nid range list syntax.
+ * \verbatim
+ *
+ * <nidlist> :== <nidrange> [ ' ' <nidrange> ]
+ * <nidrange> :== <addrrange> '@' <net>
+ * <addrrange> :== '*' |
+ * <ipaddr_range> |
+ * <cfs_expr_list>
+ * <ipaddr_range> :== <cfs_expr_list>.<cfs_expr_list>.<cfs_expr_list>.
+ * <cfs_expr_list>
+ * <cfs_expr_list> :== <number> |
+ * <expr_list>
+ * <expr_list> :== '[' <range_expr> [ ',' <range_expr>] ']'
+ * <range_expr> :== <number> |
+ * <number> '-' <number> |
+ * <number> '-' <number> '/' <number>
+ * <net> :== <netname> | <netname><number>
+ * <netname> :== "lo" | "tcp" | "o2ib" | "cib" | "openib" | "iib" |
+ * "vib" | "ra" | "elan" | "mx" | "ptl"
+ * \endverbatim
+ */
+
+/**
+ * Structure to represent \<nidrange\> token of the syntax.
+ *
+ * One of this is created for each \<net\> parsed.
+ */
+struct nidrange {
+ /**
+ * Link to list of this structures which is built on nid range
+ * list parsing.
+ */
+ struct list_head nr_link;
+ /**
+ * List head for addrrange::ar_link.
+ */
+ struct list_head nr_addrranges;
+ /**
+ * Flag indicating that *@<net> is found.
+ */
+ int nr_all;
+ /**
+ * Pointer to corresponding element of libcfs_netstrfns.
+ */
+ struct netstrfns *nr_netstrfns;
+ /**
+ * Number of network. E.g. 5 if \<net\> is "elan5".
+ */
+ int nr_netnum;
+};
+
+/**
+ * Structure to represent \<addrrange\> token of the syntax.
+ */
+struct addrrange {
+ /**
+ * Link to nidrange::nr_addrranges.
+ */
+ struct list_head ar_link;
+ /**
+ * List head for cfs_expr_list::el_list.
+ */
+ struct list_head ar_numaddr_ranges;
+};
+
+/**
+ * Parses \<addrrange\> token on the syntax.
+ *
+ * Allocates struct addrrange and links to \a nidrange via
+ * (nidrange::nr_addrranges)
+ *
+ * \retval 1 if \a src parses to '*' | \<ipaddr_range\> | \<cfs_expr_list\>
+ * \retval 0 otherwise
+ */
+static int
+parse_addrange(const struct cfs_lstr *src, struct nidrange *nidrange)
+{
+ struct addrrange *addrrange;
+
+ if (src->ls_len == 1 && src->ls_str[0] == '*') {
+ nidrange->nr_all = 1;
+ return 1;
+ }
+
+ LIBCFS_ALLOC(addrrange, sizeof(struct addrrange));
+ if (addrrange == NULL)
+ return 0;
+ list_add_tail(&addrrange->ar_link, &nidrange->nr_addrranges);
+ INIT_LIST_HEAD(&addrrange->ar_numaddr_ranges);
+
+ return nidrange->nr_netstrfns->nf_parse_addrlist(src->ls_str,
+ src->ls_len,
+ &addrrange->ar_numaddr_ranges);
+}
+
+/**
+ * Finds or creates struct nidrange.
+ *
+ * Checks if \a src is a valid network name, looks for corresponding
+ * nidrange on the ist of nidranges (\a nidlist), creates new struct
+ * nidrange if it is not found.
+ *
+ * \retval pointer to struct nidrange matching network specified via \a src
+ * \retval NULL if \a src does not match any network
+ */
+static struct nidrange *
+add_nidrange(const struct cfs_lstr *src,
+ struct list_head *nidlist)
+{
+ struct netstrfns *nf;
+ struct nidrange *nr;
+ int endlen;
+ unsigned netnum;
+
+ if (src->ls_len >= LNET_NIDSTR_SIZE)
+ return NULL;
+
+ nf = libcfs_namenum2netstrfns(src->ls_str);
+ if (nf == NULL)
+ return NULL;
+ endlen = src->ls_len - strlen(nf->nf_name);
+ if (endlen == 0)
+ /* network name only, e.g. "elan" or "tcp" */
+ netnum = 0;
+ else {
+ /* e.g. "elan25" or "tcp23", refuse to parse if
+ * network name is not appended with decimal or
+ * hexadecimal number */
+ if (!cfs_str2num_check(src->ls_str + strlen(nf->nf_name),
+ endlen, &netnum, 0, MAX_NUMERIC_VALUE))
+ return NULL;
+ }
+
+ list_for_each_entry(nr, nidlist, nr_link) {
+ if (nr->nr_netstrfns != nf)
+ continue;
+ if (nr->nr_netnum != netnum)
+ continue;
+ return nr;
+ }
+
+ LIBCFS_ALLOC(nr, sizeof(struct nidrange));
+ if (nr == NULL)
+ return NULL;
+ list_add_tail(&nr->nr_link, nidlist);
+ INIT_LIST_HEAD(&nr->nr_addrranges);
+ nr->nr_netstrfns = nf;
+ nr->nr_all = 0;
+ nr->nr_netnum = netnum;
+
+ return nr;
+}
+
+/**
+ * Parses \<nidrange\> token of the syntax.
+ *
+ * \retval 1 if \a src parses to \<addrrange\> '@' \<net\>
+ * \retval 0 otherwise
+ */
+static int
+parse_nidrange(struct cfs_lstr *src, struct list_head *nidlist)
+{
+ struct cfs_lstr addrrange;
+ struct cfs_lstr net;
+ struct cfs_lstr tmp;
+ struct nidrange *nr;
+
+ tmp = *src;
+ if (cfs_gettok(src, '@', &addrrange) == 0)
+ goto failed;
+
+ if (cfs_gettok(src, '@', &net) == 0 || src->ls_str != NULL)
+ goto failed;
+
+ nr = add_nidrange(&net, nidlist);
+ if (nr == NULL)
+ goto failed;
+
+ if (parse_addrange(&addrrange, nr) != 0)
+ goto failed;
+
+ return 1;
+ failed:
+ CWARN("can't parse nidrange: \"%.*s\"\n", tmp.ls_len, tmp.ls_str);
+ return 0;
+}
+
+/**
+ * Frees addrrange structures of \a list.
+ *
+ * For each struct addrrange structure found on \a list it frees
+ * cfs_expr_list list attached to it and frees the addrrange itself.
+ *
+ * \retval none
+ */
+static void
+free_addrranges(struct list_head *list)
+{
+ while (!list_empty(list)) {
+ struct addrrange *ar;
+
+ ar = list_entry(list->next, struct addrrange, ar_link);
+
+ cfs_expr_list_free_list(&ar->ar_numaddr_ranges);
+ list_del(&ar->ar_link);
+ LIBCFS_FREE(ar, sizeof(struct addrrange));
+ }
+}
+
+/**
+ * Frees nidrange strutures of \a list.
+ *
+ * For each struct nidrange structure found on \a list it frees
+ * addrrange list attached to it and frees the nidrange itself.
+ *
+ * \retval none
+ */
+void
+cfs_free_nidlist(struct list_head *list)
+{
+ struct list_head *pos, *next;
+ struct nidrange *nr;
+
+ list_for_each_safe(pos, next, list) {
+ nr = list_entry(pos, struct nidrange, nr_link);
+ free_addrranges(&nr->nr_addrranges);
+ list_del(pos);
+ LIBCFS_FREE(nr, sizeof(struct nidrange));
+ }
+}
+EXPORT_SYMBOL(cfs_free_nidlist);
+
+/**
+ * Parses nid range list.
+ *
+ * Parses with rigorous syntax and overflow checking \a str into
+ * \<nidrange\> [ ' ' \<nidrange\> ], compiles \a str into set of
+ * structures and links that structure to \a nidlist. The resulting
+ * list can be used to match a NID againts set of NIDS defined by \a
+ * str.
+ * \see cfs_match_nid
+ *
+ * \retval 1 on success
+ * \retval 0 otherwise
+ */
+int
+cfs_parse_nidlist(char *str, int len, struct list_head *nidlist)
+{
+ struct cfs_lstr src;
+ struct cfs_lstr res;
+ int rc;
+
+ src.ls_str = str;
+ src.ls_len = len;
+ INIT_LIST_HEAD(nidlist);
+ while (src.ls_str) {
+ rc = cfs_gettok(&src, ' ', &res);
+ if (rc == 0) {
+ cfs_free_nidlist(nidlist);
+ return 0;
+ }
+ rc = parse_nidrange(&res, nidlist);
+ if (rc == 0) {
+ cfs_free_nidlist(nidlist);
+ return 0;
+ }
+ }
+ return 1;
+}
+EXPORT_SYMBOL(cfs_parse_nidlist);
+
+/**
+ * Matches a nid (\a nid) against the compiled list of nidranges (\a nidlist).
+ *
+ * \see cfs_parse_nidlist()
+ *
+ * \retval 1 on match
+ * \retval 0 otherwises
+ */
+int cfs_match_nid(lnet_nid_t nid, struct list_head *nidlist)
+{
+ struct nidrange *nr;
+ struct addrrange *ar;
+
+ list_for_each_entry(nr, nidlist, nr_link) {
+ if (nr->nr_netstrfns->nf_type != LNET_NETTYP(LNET_NIDNET(nid)))
+ continue;
+ if (nr->nr_netnum != LNET_NETNUM(LNET_NIDNET(nid)))
+ continue;
+ if (nr->nr_all)
+ return 1;
+ list_for_each_entry(ar, &nr->nr_addrranges, ar_link)
+ if (nr->nr_netstrfns->nf_match_addr(LNET_NIDADDR(nid),
+ &ar->ar_numaddr_ranges))
+ return 1;
+ }
+ return 0;
+}
+EXPORT_SYMBOL(cfs_match_nid);
diff --git a/drivers/staging/lustre/lustre/libcfs/prng.c b/drivers/staging/lustre/lustre/libcfs/prng.c
new file mode 100644
index 000000000..4147664ff
--- /dev/null
+++ b/drivers/staging/lustre/lustre/libcfs/prng.c
@@ -0,0 +1,139 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ *
+ * libcfs/libcfs/prng.c
+ *
+ * concatenation of following two 16-bit multiply with carry generators
+ * x(n)=a*x(n-1)+carry mod 2^16 and y(n)=b*y(n-1)+carry mod 2^16,
+ * number and carry packed within the same 32 bit integer.
+ * algorithm recommended by Marsaglia
+*/
+
+#include "../../include/linux/libcfs/libcfs.h"
+
+/*
+From: George Marsaglia <geo@stat.fsu.edu>
+Newsgroups: sci.math
+Subject: Re: A RANDOM NUMBER GENERATOR FOR C
+Date: Tue, 30 Sep 1997 05:29:35 -0700
+
+ * You may replace the two constants 36969 and 18000 by any
+ * pair of distinct constants from this list:
+ * 18000 18030 18273 18513 18879 19074 19098 19164 19215 19584
+ * 19599 19950 20088 20508 20544 20664 20814 20970 21153 21243
+ * 21423 21723 21954 22125 22188 22293 22860 22938 22965 22974
+ * 23109 23124 23163 23208 23508 23520 23553 23658 23865 24114
+ * 24219 24660 24699 24864 24948 25023 25308 25443 26004 26088
+ * 26154 26550 26679 26838 27183 27258 27753 27795 27810 27834
+ * 27960 28320 28380 28689 28710 28794 28854 28959 28980 29013
+ * 29379 29889 30135 30345 30459 30714 30903 30963 31059 31083
+ * (or any other 16-bit constants k for which both k*2^16-1
+ * and k*2^15-1 are prime) */
+
+#define RANDOM_CONST_A 18030
+#define RANDOM_CONST_B 29013
+
+static unsigned int seed_x = 521288629;
+static unsigned int seed_y = 362436069;
+
+/**
+ * cfs_rand - creates new seeds
+ *
+ * First it creates new seeds from the previous seeds. Then it generates a
+ * new pseudo random number for use.
+ *
+ * Returns a pseudo-random 32-bit integer
+ */
+unsigned int cfs_rand(void)
+{
+ seed_x = RANDOM_CONST_A * (seed_x & 65535) + (seed_x >> 16);
+ seed_y = RANDOM_CONST_B * (seed_y & 65535) + (seed_y >> 16);
+
+ return ((seed_x << 16) + (seed_y & 65535));
+}
+EXPORT_SYMBOL(cfs_rand);
+
+/**
+ * cfs_srand - sets the initial seed
+ * @seed1 : (seed_x) should have the most entropy in the low bits of the word
+ * @seed2 : (seed_y) should have the most entropy in the high bits of the word
+ *
+ * Replaces the original seeds with new values. Used to generate a new pseudo
+ * random numbers.
+ */
+void cfs_srand(unsigned int seed1, unsigned int seed2)
+{
+ if (seed1)
+ seed_x = seed1; /* use default seeds if parameter is 0 */
+ if (seed2)
+ seed_y = seed2;
+}
+EXPORT_SYMBOL(cfs_srand);
+
+/**
+ * cfs_get_random_bytes - generate a bunch of random numbers
+ * @buf : buffer to fill with random numbers
+ * @size: size of passed in buffer
+ *
+ * Fills a buffer with random bytes
+ */
+void cfs_get_random_bytes(void *buf, int size)
+{
+ int *p = buf;
+ int rem, tmp;
+
+ LASSERT(size >= 0);
+
+ rem = min((int)((unsigned long)buf & (sizeof(int) - 1)), size);
+ if (rem) {
+ get_random_bytes(&tmp, sizeof(tmp));
+ tmp ^= cfs_rand();
+ memcpy(buf, &tmp, rem);
+ p = buf + rem;
+ size -= rem;
+ }
+
+ while (size >= sizeof(int)) {
+ get_random_bytes(&tmp, sizeof(tmp));
+ *p = cfs_rand() ^ tmp;
+ size -= sizeof(int);
+ p++;
+ }
+ buf = p;
+ if (size) {
+ get_random_bytes(&tmp, sizeof(tmp));
+ tmp ^= cfs_rand();
+ memcpy(buf, &tmp, size);
+ }
+}
+EXPORT_SYMBOL(cfs_get_random_bytes);
diff --git a/drivers/staging/lustre/lustre/libcfs/tracefile.c b/drivers/staging/lustre/lustre/libcfs/tracefile.c
new file mode 100644
index 000000000..c86394f7f
--- /dev/null
+++ b/drivers/staging/lustre/lustre/libcfs/tracefile.c
@@ -0,0 +1,1196 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2012, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ *
+ * libcfs/libcfs/tracefile.c
+ *
+ * Author: Zach Brown <zab@clusterfs.com>
+ * Author: Phil Schwan <phil@clusterfs.com>
+ */
+
+
+#define DEBUG_SUBSYSTEM S_LNET
+#define LUSTRE_TRACEFILE_PRIVATE
+#include "tracefile.h"
+
+#include "../../include/linux/libcfs/libcfs.h"
+
+/* XXX move things up to the top, comment */
+union cfs_trace_data_union (*cfs_trace_data[TCD_MAX_TYPES])[NR_CPUS] __cacheline_aligned;
+
+char cfs_tracefile[TRACEFILE_NAME_SIZE];
+long long cfs_tracefile_size = CFS_TRACEFILE_SIZE;
+static struct tracefiled_ctl trace_tctl;
+struct mutex cfs_trace_thread_mutex;
+static int thread_running;
+
+static atomic_t cfs_tage_allocated = ATOMIC_INIT(0);
+
+static void put_pages_on_tcd_daemon_list(struct page_collection *pc,
+ struct cfs_trace_cpu_data *tcd);
+
+static inline struct cfs_trace_page *
+cfs_tage_from_list(struct list_head *list)
+{
+ return list_entry(list, struct cfs_trace_page, linkage);
+}
+
+static struct cfs_trace_page *cfs_tage_alloc(gfp_t gfp)
+{
+ struct page *page;
+ struct cfs_trace_page *tage;
+
+ /* My caller is trying to free memory */
+ if (!in_interrupt() && memory_pressure_get())
+ return NULL;
+
+ /*
+ * Don't spam console with allocation failures: they will be reported
+ * by upper layer anyway.
+ */
+ gfp |= __GFP_NOWARN;
+ page = alloc_page(gfp);
+ if (page == NULL)
+ return NULL;
+
+ tage = kmalloc(sizeof(*tage), gfp);
+ if (tage == NULL) {
+ __free_page(page);
+ return NULL;
+ }
+
+ tage->page = page;
+ atomic_inc(&cfs_tage_allocated);
+ return tage;
+}
+
+static void cfs_tage_free(struct cfs_trace_page *tage)
+{
+ __LASSERT(tage != NULL);
+ __LASSERT(tage->page != NULL);
+
+ __free_page(tage->page);
+ kfree(tage);
+ atomic_dec(&cfs_tage_allocated);
+}
+
+static void cfs_tage_to_tail(struct cfs_trace_page *tage,
+ struct list_head *queue)
+{
+ __LASSERT(tage != NULL);
+ __LASSERT(queue != NULL);
+
+ list_move_tail(&tage->linkage, queue);
+}
+
+int cfs_trace_refill_stock(struct cfs_trace_cpu_data *tcd, gfp_t gfp,
+ struct list_head *stock)
+{
+ int i;
+
+ /*
+ * XXX nikita: do NOT call portals_debug_msg() (CDEBUG/ENTRY/EXIT)
+ * from here: this will lead to infinite recursion.
+ */
+
+ for (i = 0; i + tcd->tcd_cur_stock_pages < TCD_STOCK_PAGES ; ++ i) {
+ struct cfs_trace_page *tage;
+
+ tage = cfs_tage_alloc(gfp);
+ if (tage == NULL)
+ break;
+ list_add_tail(&tage->linkage, stock);
+ }
+ return i;
+}
+
+/* return a page that has 'len' bytes left at the end */
+static struct cfs_trace_page *
+cfs_trace_get_tage_try(struct cfs_trace_cpu_data *tcd, unsigned long len)
+{
+ struct cfs_trace_page *tage;
+
+ if (tcd->tcd_cur_pages > 0) {
+ __LASSERT(!list_empty(&tcd->tcd_pages));
+ tage = cfs_tage_from_list(tcd->tcd_pages.prev);
+ if (tage->used + len <= PAGE_CACHE_SIZE)
+ return tage;
+ }
+
+ if (tcd->tcd_cur_pages < tcd->tcd_max_pages) {
+ if (tcd->tcd_cur_stock_pages > 0) {
+ tage = cfs_tage_from_list(tcd->tcd_stock_pages.prev);
+ --tcd->tcd_cur_stock_pages;
+ list_del_init(&tage->linkage);
+ } else {
+ tage = cfs_tage_alloc(GFP_ATOMIC);
+ if (unlikely(tage == NULL)) {
+ if ((!memory_pressure_get() ||
+ in_interrupt()) && printk_ratelimit())
+ printk(KERN_WARNING
+ "cannot allocate a tage (%ld)\n",
+ tcd->tcd_cur_pages);
+ return NULL;
+ }
+ }
+
+ tage->used = 0;
+ tage->cpu = smp_processor_id();
+ tage->type = tcd->tcd_type;
+ list_add_tail(&tage->linkage, &tcd->tcd_pages);
+ tcd->tcd_cur_pages++;
+
+ if (tcd->tcd_cur_pages > 8 && thread_running) {
+ struct tracefiled_ctl *tctl = &trace_tctl;
+ /*
+ * wake up tracefiled to process some pages.
+ */
+ wake_up(&tctl->tctl_waitq);
+ }
+ return tage;
+ }
+ return NULL;
+}
+
+static void cfs_tcd_shrink(struct cfs_trace_cpu_data *tcd)
+{
+ int pgcount = tcd->tcd_cur_pages / 10;
+ struct page_collection pc;
+ struct cfs_trace_page *tage;
+ struct cfs_trace_page *tmp;
+
+ /*
+ * XXX nikita: do NOT call portals_debug_msg() (CDEBUG/ENTRY/EXIT)
+ * from here: this will lead to infinite recursion.
+ */
+
+ if (printk_ratelimit())
+ printk(KERN_WARNING "debug daemon buffer overflowed; discarding 10%% of pages (%d of %ld)\n",
+ pgcount + 1, tcd->tcd_cur_pages);
+
+ INIT_LIST_HEAD(&pc.pc_pages);
+ spin_lock_init(&pc.pc_lock);
+
+ list_for_each_entry_safe(tage, tmp, &tcd->tcd_pages, linkage) {
+ if (pgcount-- == 0)
+ break;
+
+ list_move_tail(&tage->linkage, &pc.pc_pages);
+ tcd->tcd_cur_pages--;
+ }
+ put_pages_on_tcd_daemon_list(&pc, tcd);
+}
+
+/* return a page that has 'len' bytes left at the end */
+static struct cfs_trace_page *cfs_trace_get_tage(struct cfs_trace_cpu_data *tcd,
+ unsigned long len)
+{
+ struct cfs_trace_page *tage;
+
+ /*
+ * XXX nikita: do NOT call portals_debug_msg() (CDEBUG/ENTRY/EXIT)
+ * from here: this will lead to infinite recursion.
+ */
+
+ if (len > PAGE_CACHE_SIZE) {
+ pr_err("cowardly refusing to write %lu bytes in a page\n", len);
+ return NULL;
+ }
+
+ tage = cfs_trace_get_tage_try(tcd, len);
+ if (tage != NULL)
+ return tage;
+ if (thread_running)
+ cfs_tcd_shrink(tcd);
+ if (tcd->tcd_cur_pages > 0) {
+ tage = cfs_tage_from_list(tcd->tcd_pages.next);
+ tage->used = 0;
+ cfs_tage_to_tail(tage, &tcd->tcd_pages);
+ }
+ return tage;
+}
+
+int libcfs_debug_msg(struct libcfs_debug_msg_data *msgdata,
+ const char *format, ...)
+{
+ va_list args;
+ int rc;
+
+ va_start(args, format);
+ rc = libcfs_debug_vmsg2(msgdata, format, args, NULL);
+ va_end(args);
+
+ return rc;
+}
+EXPORT_SYMBOL(libcfs_debug_msg);
+
+int libcfs_debug_vmsg2(struct libcfs_debug_msg_data *msgdata,
+ const char *format1, va_list args,
+ const char *format2, ...)
+{
+ struct cfs_trace_cpu_data *tcd = NULL;
+ struct ptldebug_header header = {0};
+ struct cfs_trace_page *tage;
+ /* string_buf is used only if tcd != NULL, and is always set then */
+ char *string_buf = NULL;
+ char *debug_buf;
+ int known_size;
+ int needed = 85; /* average message length */
+ int max_nob;
+ va_list ap;
+ int depth;
+ int i;
+ int remain;
+ int mask = msgdata->msg_mask;
+ const char *file = kbasename(msgdata->msg_file);
+ struct cfs_debug_limit_state *cdls = msgdata->msg_cdls;
+
+ tcd = cfs_trace_get_tcd();
+
+ /* cfs_trace_get_tcd() grabs a lock, which disables preemption and
+ * pins us to a particular CPU. This avoids an smp_processor_id()
+ * warning on Linux when debugging is enabled. */
+ cfs_set_ptldebug_header(&header, msgdata, CDEBUG_STACK());
+
+ if (tcd == NULL) /* arch may not log in IRQ context */
+ goto console;
+
+ if (tcd->tcd_cur_pages == 0)
+ header.ph_flags |= PH_FLAG_FIRST_RECORD;
+
+ if (tcd->tcd_shutting_down) {
+ cfs_trace_put_tcd(tcd);
+ tcd = NULL;
+ goto console;
+ }
+
+ depth = __current_nesting_level();
+ known_size = strlen(file) + 1 + depth;
+ if (msgdata->msg_fn)
+ known_size += strlen(msgdata->msg_fn) + 1;
+
+ if (libcfs_debug_binary)
+ known_size += sizeof(header);
+
+ /*/
+ * '2' used because vsnprintf return real size required for output
+ * _without_ terminating NULL.
+ * if needed is to small for this format.
+ */
+ for (i = 0; i < 2; i++) {
+ tage = cfs_trace_get_tage(tcd, needed + known_size + 1);
+ if (tage == NULL) {
+ if (needed + known_size > PAGE_CACHE_SIZE)
+ mask |= D_ERROR;
+
+ cfs_trace_put_tcd(tcd);
+ tcd = NULL;
+ goto console;
+ }
+
+ string_buf = (char *)page_address(tage->page) +
+ tage->used + known_size;
+
+ max_nob = PAGE_CACHE_SIZE - tage->used - known_size;
+ if (max_nob <= 0) {
+ printk(KERN_EMERG "negative max_nob: %d\n",
+ max_nob);
+ mask |= D_ERROR;
+ cfs_trace_put_tcd(tcd);
+ tcd = NULL;
+ goto console;
+ }
+
+ needed = 0;
+ if (format1) {
+ va_copy(ap, args);
+ needed = vsnprintf(string_buf, max_nob, format1, ap);
+ va_end(ap);
+ }
+
+ if (format2) {
+ remain = max_nob - needed;
+ if (remain < 0)
+ remain = 0;
+
+ va_start(ap, format2);
+ needed += vsnprintf(string_buf + needed, remain,
+ format2, ap);
+ va_end(ap);
+ }
+
+ if (needed < max_nob) /* well. printing ok.. */
+ break;
+ }
+
+ if (*(string_buf+needed-1) != '\n')
+ printk(KERN_INFO "format at %s:%d:%s doesn't end in newline\n",
+ file, msgdata->msg_line, msgdata->msg_fn);
+
+ header.ph_len = known_size + needed;
+ debug_buf = (char *)page_address(tage->page) + tage->used;
+
+ if (libcfs_debug_binary) {
+ memcpy(debug_buf, &header, sizeof(header));
+ tage->used += sizeof(header);
+ debug_buf += sizeof(header);
+ }
+
+ /* indent message according to the nesting level */
+ while (depth-- > 0) {
+ *(debug_buf++) = '.';
+ ++ tage->used;
+ }
+
+ strcpy(debug_buf, file);
+ tage->used += strlen(file) + 1;
+ debug_buf += strlen(file) + 1;
+
+ if (msgdata->msg_fn) {
+ strcpy(debug_buf, msgdata->msg_fn);
+ tage->used += strlen(msgdata->msg_fn) + 1;
+ debug_buf += strlen(msgdata->msg_fn) + 1;
+ }
+
+ __LASSERT(debug_buf == string_buf);
+
+ tage->used += needed;
+ __LASSERT (tage->used <= PAGE_CACHE_SIZE);
+
+console:
+ if ((mask & libcfs_printk) == 0) {
+ /* no console output requested */
+ if (tcd != NULL)
+ cfs_trace_put_tcd(tcd);
+ return 1;
+ }
+
+ if (cdls != NULL) {
+ if (libcfs_console_ratelimit &&
+ cdls->cdls_next != 0 && /* not first time ever */
+ !cfs_time_after(cfs_time_current(), cdls->cdls_next)) {
+ /* skipping a console message */
+ cdls->cdls_count++;
+ if (tcd != NULL)
+ cfs_trace_put_tcd(tcd);
+ return 1;
+ }
+
+ if (cfs_time_after(cfs_time_current(), cdls->cdls_next +
+ libcfs_console_max_delay
+ + cfs_time_seconds(10))) {
+ /* last timeout was a long time ago */
+ cdls->cdls_delay /= libcfs_console_backoff * 4;
+ } else {
+ cdls->cdls_delay *= libcfs_console_backoff;
+ }
+
+ if (cdls->cdls_delay < libcfs_console_min_delay)
+ cdls->cdls_delay = libcfs_console_min_delay;
+ else if (cdls->cdls_delay > libcfs_console_max_delay)
+ cdls->cdls_delay = libcfs_console_max_delay;
+
+ /* ensure cdls_next is never zero after it's been seen */
+ cdls->cdls_next = (cfs_time_current() + cdls->cdls_delay) | 1;
+ }
+
+ if (tcd != NULL) {
+ cfs_print_to_console(&header, mask, string_buf, needed, file,
+ msgdata->msg_fn);
+ cfs_trace_put_tcd(tcd);
+ } else {
+ string_buf = cfs_trace_get_console_buffer();
+
+ needed = 0;
+ if (format1 != NULL) {
+ va_copy(ap, args);
+ needed = vsnprintf(string_buf,
+ CFS_TRACE_CONSOLE_BUFFER_SIZE,
+ format1, ap);
+ va_end(ap);
+ }
+ if (format2 != NULL) {
+ remain = CFS_TRACE_CONSOLE_BUFFER_SIZE - needed;
+ if (remain > 0) {
+ va_start(ap, format2);
+ needed += vsnprintf(string_buf+needed, remain,
+ format2, ap);
+ va_end(ap);
+ }
+ }
+ cfs_print_to_console(&header, mask,
+ string_buf, needed, file, msgdata->msg_fn);
+
+ cfs_trace_put_console_buffer(string_buf);
+ }
+
+ if (cdls != NULL && cdls->cdls_count != 0) {
+ string_buf = cfs_trace_get_console_buffer();
+
+ needed = snprintf(string_buf, CFS_TRACE_CONSOLE_BUFFER_SIZE,
+ "Skipped %d previous similar message%s\n",
+ cdls->cdls_count,
+ (cdls->cdls_count > 1) ? "s" : "");
+
+ cfs_print_to_console(&header, mask,
+ string_buf, needed, file, msgdata->msg_fn);
+
+ cfs_trace_put_console_buffer(string_buf);
+ cdls->cdls_count = 0;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL(libcfs_debug_vmsg2);
+
+void
+cfs_trace_assertion_failed(const char *str,
+ struct libcfs_debug_msg_data *msgdata)
+{
+ struct ptldebug_header hdr;
+
+ libcfs_panic_in_progress = 1;
+ libcfs_catastrophe = 1;
+ mb();
+
+ cfs_set_ptldebug_header(&hdr, msgdata, CDEBUG_STACK());
+
+ cfs_print_to_console(&hdr, D_EMERG, str, strlen(str),
+ msgdata->msg_file, msgdata->msg_fn);
+
+ panic("Lustre debug assertion failure\n");
+
+ /* not reached */
+}
+
+static void
+panic_collect_pages(struct page_collection *pc)
+{
+ /* Do the collect_pages job on a single CPU: assumes that all other
+ * CPUs have been stopped during a panic. If this isn't true for some
+ * arch, this will have to be implemented separately in each arch. */
+ int i;
+ int j;
+ struct cfs_trace_cpu_data *tcd;
+
+ INIT_LIST_HEAD(&pc->pc_pages);
+
+ cfs_tcd_for_each(tcd, i, j) {
+ list_splice_init(&tcd->tcd_pages, &pc->pc_pages);
+ tcd->tcd_cur_pages = 0;
+
+ if (pc->pc_want_daemon_pages) {
+ list_splice_init(&tcd->tcd_daemon_pages,
+ &pc->pc_pages);
+ tcd->tcd_cur_daemon_pages = 0;
+ }
+ }
+}
+
+static void collect_pages_on_all_cpus(struct page_collection *pc)
+{
+ struct cfs_trace_cpu_data *tcd;
+ int i, cpu;
+
+ spin_lock(&pc->pc_lock);
+ for_each_possible_cpu(cpu) {
+ cfs_tcd_for_each_type_lock(tcd, i, cpu) {
+ list_splice_init(&tcd->tcd_pages, &pc->pc_pages);
+ tcd->tcd_cur_pages = 0;
+ if (pc->pc_want_daemon_pages) {
+ list_splice_init(&tcd->tcd_daemon_pages,
+ &pc->pc_pages);
+ tcd->tcd_cur_daemon_pages = 0;
+ }
+ }
+ }
+ spin_unlock(&pc->pc_lock);
+}
+
+static void collect_pages(struct page_collection *pc)
+{
+ INIT_LIST_HEAD(&pc->pc_pages);
+
+ if (libcfs_panic_in_progress)
+ panic_collect_pages(pc);
+ else
+ collect_pages_on_all_cpus(pc);
+}
+
+static void put_pages_back_on_all_cpus(struct page_collection *pc)
+{
+ struct cfs_trace_cpu_data *tcd;
+ struct list_head *cur_head;
+ struct cfs_trace_page *tage;
+ struct cfs_trace_page *tmp;
+ int i, cpu;
+
+ spin_lock(&pc->pc_lock);
+ for_each_possible_cpu(cpu) {
+ cfs_tcd_for_each_type_lock(tcd, i, cpu) {
+ cur_head = tcd->tcd_pages.next;
+
+ list_for_each_entry_safe(tage, tmp, &pc->pc_pages,
+ linkage) {
+
+ __LASSERT_TAGE_INVARIANT(tage);
+
+ if (tage->cpu != cpu || tage->type != i)
+ continue;
+
+ cfs_tage_to_tail(tage, cur_head);
+ tcd->tcd_cur_pages++;
+ }
+ }
+ }
+ spin_unlock(&pc->pc_lock);
+}
+
+static void put_pages_back(struct page_collection *pc)
+{
+ if (!libcfs_panic_in_progress)
+ put_pages_back_on_all_cpus(pc);
+}
+
+/* Add pages to a per-cpu debug daemon ringbuffer. This buffer makes sure that
+ * we have a good amount of data at all times for dumping during an LBUG, even
+ * if we have been steadily writing (and otherwise discarding) pages via the
+ * debug daemon. */
+static void put_pages_on_tcd_daemon_list(struct page_collection *pc,
+ struct cfs_trace_cpu_data *tcd)
+{
+ struct cfs_trace_page *tage;
+ struct cfs_trace_page *tmp;
+
+ spin_lock(&pc->pc_lock);
+ list_for_each_entry_safe(tage, tmp, &pc->pc_pages, linkage) {
+
+ __LASSERT_TAGE_INVARIANT(tage);
+
+ if (tage->cpu != tcd->tcd_cpu || tage->type != tcd->tcd_type)
+ continue;
+
+ cfs_tage_to_tail(tage, &tcd->tcd_daemon_pages);
+ tcd->tcd_cur_daemon_pages++;
+
+ if (tcd->tcd_cur_daemon_pages > tcd->tcd_max_pages) {
+ struct cfs_trace_page *victim;
+
+ __LASSERT(!list_empty(&tcd->tcd_daemon_pages));
+ victim = cfs_tage_from_list(tcd->tcd_daemon_pages.next);
+
+ __LASSERT_TAGE_INVARIANT(victim);
+
+ list_del(&victim->linkage);
+ cfs_tage_free(victim);
+ tcd->tcd_cur_daemon_pages--;
+ }
+ }
+ spin_unlock(&pc->pc_lock);
+}
+
+static void put_pages_on_daemon_list(struct page_collection *pc)
+{
+ struct cfs_trace_cpu_data *tcd;
+ int i, cpu;
+
+ for_each_possible_cpu(cpu) {
+ cfs_tcd_for_each_type_lock(tcd, i, cpu)
+ put_pages_on_tcd_daemon_list(pc, tcd);
+ }
+}
+
+void cfs_trace_debug_print(void)
+{
+ struct page_collection pc;
+ struct cfs_trace_page *tage;
+ struct cfs_trace_page *tmp;
+
+ spin_lock_init(&pc.pc_lock);
+
+ pc.pc_want_daemon_pages = 1;
+ collect_pages(&pc);
+ list_for_each_entry_safe(tage, tmp, &pc.pc_pages, linkage) {
+ char *p, *file, *fn;
+ struct page *page;
+
+ __LASSERT_TAGE_INVARIANT(tage);
+
+ page = tage->page;
+ p = page_address(page);
+ while (p < ((char *)page_address(page) + tage->used)) {
+ struct ptldebug_header *hdr;
+ int len;
+ hdr = (void *)p;
+ p += sizeof(*hdr);
+ file = p;
+ p += strlen(file) + 1;
+ fn = p;
+ p += strlen(fn) + 1;
+ len = hdr->ph_len - (int)(p - (char *)hdr);
+
+ cfs_print_to_console(hdr, D_EMERG, p, len, file, fn);
+
+ p += len;
+ }
+
+ list_del(&tage->linkage);
+ cfs_tage_free(tage);
+ }
+}
+
+int cfs_tracefile_dump_all_pages(char *filename)
+{
+ struct page_collection pc;
+ struct file *filp;
+ struct cfs_trace_page *tage;
+ struct cfs_trace_page *tmp;
+ char *buf;
+ int rc;
+
+ DECL_MMSPACE;
+
+ cfs_tracefile_write_lock();
+
+ filp = filp_open(filename, O_CREAT|O_EXCL|O_WRONLY|O_LARGEFILE, 0600);
+ if (IS_ERR(filp)) {
+ rc = PTR_ERR(filp);
+ filp = NULL;
+ pr_err("LustreError: can't open %s for dump: rc %d\n",
+ filename, rc);
+ goto out;
+ }
+
+ spin_lock_init(&pc.pc_lock);
+ pc.pc_want_daemon_pages = 1;
+ collect_pages(&pc);
+ if (list_empty(&pc.pc_pages)) {
+ rc = 0;
+ goto close;
+ }
+
+ /* ok, for now, just write the pages. in the future we'll be building
+ * iobufs with the pages and calling generic_direct_IO */
+ MMSPACE_OPEN;
+ list_for_each_entry_safe(tage, tmp, &pc.pc_pages, linkage) {
+
+ __LASSERT_TAGE_INVARIANT(tage);
+
+ buf = kmap(tage->page);
+ rc = vfs_write(filp, (__force const char __user *)buf,
+ tage->used, &filp->f_pos);
+ kunmap(tage->page);
+
+ if (rc != (int)tage->used) {
+ printk(KERN_WARNING "wanted to write %u but wrote %d\n",
+ tage->used, rc);
+ put_pages_back(&pc);
+ __LASSERT(list_empty(&pc.pc_pages));
+ break;
+ }
+ list_del(&tage->linkage);
+ cfs_tage_free(tage);
+ }
+ MMSPACE_CLOSE;
+ rc = vfs_fsync(filp, 1);
+ if (rc)
+ pr_err("sync returns %d\n", rc);
+close:
+ filp_close(filp, NULL);
+out:
+ cfs_tracefile_write_unlock();
+ return rc;
+}
+
+void cfs_trace_flush_pages(void)
+{
+ struct page_collection pc;
+ struct cfs_trace_page *tage;
+ struct cfs_trace_page *tmp;
+
+ spin_lock_init(&pc.pc_lock);
+
+ pc.pc_want_daemon_pages = 1;
+ collect_pages(&pc);
+ list_for_each_entry_safe(tage, tmp, &pc.pc_pages, linkage) {
+
+ __LASSERT_TAGE_INVARIANT(tage);
+
+ list_del(&tage->linkage);
+ cfs_tage_free(tage);
+ }
+}
+
+int cfs_trace_copyin_string(char *knl_buffer, int knl_buffer_nob,
+ const char __user *usr_buffer, int usr_buffer_nob)
+{
+ int nob;
+
+ if (usr_buffer_nob > knl_buffer_nob)
+ return -EOVERFLOW;
+
+ if (copy_from_user((void *)knl_buffer,
+ usr_buffer, usr_buffer_nob))
+ return -EFAULT;
+
+ nob = strnlen(knl_buffer, usr_buffer_nob);
+ while (nob-- >= 0) /* strip trailing whitespace */
+ if (!isspace(knl_buffer[nob]))
+ break;
+
+ if (nob < 0) /* empty string */
+ return -EINVAL;
+
+ if (nob == knl_buffer_nob) /* no space to terminate */
+ return -EOVERFLOW;
+
+ knl_buffer[nob + 1] = 0; /* terminate */
+ return 0;
+}
+EXPORT_SYMBOL(cfs_trace_copyin_string);
+
+int cfs_trace_copyout_string(char __user *usr_buffer, int usr_buffer_nob,
+ const char *knl_buffer, char *append)
+{
+ /* NB if 'append' != NULL, it's a single character to append to the
+ * copied out string - usually "\n", for /proc entries and "" (i.e. a
+ * terminating zero byte) for sysctl entries */
+ int nob = strlen(knl_buffer);
+
+ if (nob > usr_buffer_nob)
+ nob = usr_buffer_nob;
+
+ if (copy_to_user(usr_buffer, knl_buffer, nob))
+ return -EFAULT;
+
+ if (append != NULL && nob < usr_buffer_nob) {
+ if (copy_to_user(usr_buffer + nob, append, 1))
+ return -EFAULT;
+
+ nob++;
+ }
+
+ return nob;
+}
+EXPORT_SYMBOL(cfs_trace_copyout_string);
+
+int cfs_trace_allocate_string_buffer(char **str, int nob)
+{
+ if (nob > 2 * PAGE_CACHE_SIZE) /* string must be "sensible" */
+ return -EINVAL;
+
+ *str = kmalloc(nob, GFP_IOFS | __GFP_ZERO);
+ if (*str == NULL)
+ return -ENOMEM;
+
+ return 0;
+}
+
+void cfs_trace_free_string_buffer(char *str, int nob)
+{
+ kfree(str);
+}
+
+int cfs_trace_dump_debug_buffer_usrstr(void __user *usr_str, int usr_str_nob)
+{
+ char *str;
+ int rc;
+
+ rc = cfs_trace_allocate_string_buffer(&str, usr_str_nob + 1);
+ if (rc != 0)
+ return rc;
+
+ rc = cfs_trace_copyin_string(str, usr_str_nob + 1,
+ usr_str, usr_str_nob);
+ if (rc != 0)
+ goto out;
+
+ if (str[0] != '/') {
+ rc = -EINVAL;
+ goto out;
+ }
+ rc = cfs_tracefile_dump_all_pages(str);
+out:
+ cfs_trace_free_string_buffer(str, usr_str_nob + 1);
+ return rc;
+}
+
+int cfs_trace_daemon_command(char *str)
+{
+ int rc = 0;
+
+ cfs_tracefile_write_lock();
+
+ if (strcmp(str, "stop") == 0) {
+ cfs_tracefile_write_unlock();
+ cfs_trace_stop_thread();
+ cfs_tracefile_write_lock();
+ memset(cfs_tracefile, 0, sizeof(cfs_tracefile));
+
+ } else if (strncmp(str, "size=", 5) == 0) {
+ cfs_tracefile_size = simple_strtoul(str + 5, NULL, 0);
+ if (cfs_tracefile_size < 10 || cfs_tracefile_size > 20480)
+ cfs_tracefile_size = CFS_TRACEFILE_SIZE;
+ else
+ cfs_tracefile_size <<= 20;
+
+ } else if (strlen(str) >= sizeof(cfs_tracefile)) {
+ rc = -ENAMETOOLONG;
+ } else if (str[0] != '/') {
+ rc = -EINVAL;
+ } else {
+ strcpy(cfs_tracefile, str);
+
+ printk(KERN_INFO
+ "Lustre: debug daemon will attempt to start writing to %s (%lukB max)\n",
+ cfs_tracefile,
+ (long)(cfs_tracefile_size >> 10));
+
+ cfs_trace_start_thread();
+ }
+
+ cfs_tracefile_write_unlock();
+ return rc;
+}
+
+int cfs_trace_daemon_command_usrstr(void __user *usr_str, int usr_str_nob)
+{
+ char *str;
+ int rc;
+
+ rc = cfs_trace_allocate_string_buffer(&str, usr_str_nob + 1);
+ if (rc != 0)
+ return rc;
+
+ rc = cfs_trace_copyin_string(str, usr_str_nob + 1,
+ usr_str, usr_str_nob);
+ if (rc == 0)
+ rc = cfs_trace_daemon_command(str);
+
+ cfs_trace_free_string_buffer(str, usr_str_nob + 1);
+ return rc;
+}
+
+int cfs_trace_set_debug_mb(int mb)
+{
+ int i;
+ int j;
+ int pages;
+ int limit = cfs_trace_max_debug_mb();
+ struct cfs_trace_cpu_data *tcd;
+
+ if (mb < num_possible_cpus()) {
+ printk(KERN_WARNING
+ "Lustre: %d MB is too small for debug buffer size, setting it to %d MB.\n",
+ mb, num_possible_cpus());
+ mb = num_possible_cpus();
+ }
+
+ if (mb > limit) {
+ printk(KERN_WARNING
+ "Lustre: %d MB is too large for debug buffer size, setting it to %d MB.\n",
+ mb, limit);
+ mb = limit;
+ }
+
+ mb /= num_possible_cpus();
+ pages = mb << (20 - PAGE_CACHE_SHIFT);
+
+ cfs_tracefile_write_lock();
+
+ cfs_tcd_for_each(tcd, i, j)
+ tcd->tcd_max_pages = (pages * tcd->tcd_pages_factor) / 100;
+
+ cfs_tracefile_write_unlock();
+
+ return 0;
+}
+
+int cfs_trace_set_debug_mb_usrstr(void __user *usr_str, int usr_str_nob)
+{
+ char str[32];
+ int rc;
+
+ rc = cfs_trace_copyin_string(str, sizeof(str), usr_str, usr_str_nob);
+ if (rc < 0)
+ return rc;
+
+ return cfs_trace_set_debug_mb(simple_strtoul(str, NULL, 0));
+}
+
+int cfs_trace_get_debug_mb(void)
+{
+ int i;
+ int j;
+ struct cfs_trace_cpu_data *tcd;
+ int total_pages = 0;
+
+ cfs_tracefile_read_lock();
+
+ cfs_tcd_for_each(tcd, i, j)
+ total_pages += tcd->tcd_max_pages;
+
+ cfs_tracefile_read_unlock();
+
+ return (total_pages >> (20 - PAGE_CACHE_SHIFT)) + 1;
+}
+
+static int tracefiled(void *arg)
+{
+ struct page_collection pc;
+ struct tracefiled_ctl *tctl = arg;
+ struct cfs_trace_page *tage;
+ struct cfs_trace_page *tmp;
+ struct file *filp;
+ char *buf;
+ int last_loop = 0;
+ int rc;
+
+ DECL_MMSPACE;
+
+ /* we're started late enough that we pick up init's fs context */
+ /* this is so broken in uml? what on earth is going on? */
+
+ spin_lock_init(&pc.pc_lock);
+ complete(&tctl->tctl_start);
+
+ while (1) {
+ wait_queue_t __wait;
+
+ pc.pc_want_daemon_pages = 0;
+ collect_pages(&pc);
+ if (list_empty(&pc.pc_pages))
+ goto end_loop;
+
+ filp = NULL;
+ cfs_tracefile_read_lock();
+ if (cfs_tracefile[0] != 0) {
+ filp = filp_open(cfs_tracefile,
+ O_CREAT | O_RDWR | O_LARGEFILE,
+ 0600);
+ if (IS_ERR(filp)) {
+ rc = PTR_ERR(filp);
+ filp = NULL;
+ printk(KERN_WARNING "couldn't open %s: %d\n",
+ cfs_tracefile, rc);
+ }
+ }
+ cfs_tracefile_read_unlock();
+ if (filp == NULL) {
+ put_pages_on_daemon_list(&pc);
+ __LASSERT(list_empty(&pc.pc_pages));
+ goto end_loop;
+ }
+
+ MMSPACE_OPEN;
+
+ list_for_each_entry_safe(tage, tmp, &pc.pc_pages,
+ linkage) {
+ static loff_t f_pos;
+
+ __LASSERT_TAGE_INVARIANT(tage);
+
+ if (f_pos >= (off_t)cfs_tracefile_size)
+ f_pos = 0;
+ else if (f_pos > i_size_read(file_inode(filp)))
+ f_pos = i_size_read(file_inode(filp));
+
+ buf = kmap(tage->page);
+ rc = vfs_write(filp, (__force const char __user *)buf,
+ tage->used, &f_pos);
+ kunmap(tage->page);
+
+ if (rc != (int)tage->used) {
+ printk(KERN_WARNING "wanted to write %u but wrote %d\n",
+ tage->used, rc);
+ put_pages_back(&pc);
+ __LASSERT(list_empty(&pc.pc_pages));
+ break;
+ }
+ }
+ MMSPACE_CLOSE;
+
+ filp_close(filp, NULL);
+ put_pages_on_daemon_list(&pc);
+ if (!list_empty(&pc.pc_pages)) {
+ int i;
+
+ printk(KERN_ALERT "Lustre: trace pages aren't empty\n");
+ pr_err("total cpus(%d): ",
+ num_possible_cpus());
+ for (i = 0; i < num_possible_cpus(); i++)
+ if (cpu_online(i))
+ pr_cont("%d(on) ", i);
+ else
+ pr_cont("%d(off) ", i);
+ pr_cont("\n");
+
+ i = 0;
+ list_for_each_entry_safe(tage, tmp, &pc.pc_pages,
+ linkage)
+ pr_err("page %d belongs to cpu %d\n",
+ ++i, tage->cpu);
+ pr_err("There are %d pages unwritten\n", i);
+ }
+ __LASSERT(list_empty(&pc.pc_pages));
+end_loop:
+ if (atomic_read(&tctl->tctl_shutdown)) {
+ if (last_loop == 0) {
+ last_loop = 1;
+ continue;
+ } else {
+ break;
+ }
+ }
+ init_waitqueue_entry(&__wait, current);
+ add_wait_queue(&tctl->tctl_waitq, &__wait);
+ set_current_state(TASK_INTERRUPTIBLE);
+ schedule_timeout(cfs_time_seconds(1));
+ remove_wait_queue(&tctl->tctl_waitq, &__wait);
+ }
+ complete(&tctl->tctl_stop);
+ return 0;
+}
+
+int cfs_trace_start_thread(void)
+{
+ struct tracefiled_ctl *tctl = &trace_tctl;
+ int rc = 0;
+
+ mutex_lock(&cfs_trace_thread_mutex);
+ if (thread_running)
+ goto out;
+
+ init_completion(&tctl->tctl_start);
+ init_completion(&tctl->tctl_stop);
+ init_waitqueue_head(&tctl->tctl_waitq);
+ atomic_set(&tctl->tctl_shutdown, 0);
+
+ if (IS_ERR(kthread_run(tracefiled, tctl, "ktracefiled"))) {
+ rc = -ECHILD;
+ goto out;
+ }
+
+ wait_for_completion(&tctl->tctl_start);
+ thread_running = 1;
+out:
+ mutex_unlock(&cfs_trace_thread_mutex);
+ return rc;
+}
+
+void cfs_trace_stop_thread(void)
+{
+ struct tracefiled_ctl *tctl = &trace_tctl;
+
+ mutex_lock(&cfs_trace_thread_mutex);
+ if (thread_running) {
+ printk(KERN_INFO
+ "Lustre: shutting down debug daemon thread...\n");
+ atomic_set(&tctl->tctl_shutdown, 1);
+ wait_for_completion(&tctl->tctl_stop);
+ thread_running = 0;
+ }
+ mutex_unlock(&cfs_trace_thread_mutex);
+}
+
+int cfs_tracefile_init(int max_pages)
+{
+ struct cfs_trace_cpu_data *tcd;
+ int i;
+ int j;
+ int rc;
+ int factor;
+
+ rc = cfs_tracefile_init_arch();
+ if (rc != 0)
+ return rc;
+
+ cfs_tcd_for_each(tcd, i, j) {
+ /* tcd_pages_factor is initialized int tracefile_init_arch. */
+ factor = tcd->tcd_pages_factor;
+ INIT_LIST_HEAD(&tcd->tcd_pages);
+ INIT_LIST_HEAD(&tcd->tcd_stock_pages);
+ INIT_LIST_HEAD(&tcd->tcd_daemon_pages);
+ tcd->tcd_cur_pages = 0;
+ tcd->tcd_cur_stock_pages = 0;
+ tcd->tcd_cur_daemon_pages = 0;
+ tcd->tcd_max_pages = (max_pages * factor) / 100;
+ LASSERT(tcd->tcd_max_pages > 0);
+ tcd->tcd_shutting_down = 0;
+ }
+
+ return 0;
+}
+
+static void trace_cleanup_on_all_cpus(void)
+{
+ struct cfs_trace_cpu_data *tcd;
+ struct cfs_trace_page *tage;
+ struct cfs_trace_page *tmp;
+ int i, cpu;
+
+ for_each_possible_cpu(cpu) {
+ cfs_tcd_for_each_type_lock(tcd, i, cpu) {
+ tcd->tcd_shutting_down = 1;
+
+ list_for_each_entry_safe(tage, tmp, &tcd->tcd_pages,
+ linkage) {
+ __LASSERT_TAGE_INVARIANT(tage);
+
+ list_del(&tage->linkage);
+ cfs_tage_free(tage);
+ }
+
+ tcd->tcd_cur_pages = 0;
+ }
+ }
+}
+
+static void cfs_trace_cleanup(void)
+{
+ struct page_collection pc;
+
+ INIT_LIST_HEAD(&pc.pc_pages);
+ spin_lock_init(&pc.pc_lock);
+
+ trace_cleanup_on_all_cpus();
+
+ cfs_tracefile_fini_arch();
+}
+
+void cfs_tracefile_exit(void)
+{
+ cfs_trace_stop_thread();
+ cfs_trace_cleanup();
+}
diff --git a/drivers/staging/lustre/lustre/libcfs/tracefile.h b/drivers/staging/lustre/lustre/libcfs/tracefile.h
new file mode 100644
index 000000000..0601476e1
--- /dev/null
+++ b/drivers/staging/lustre/lustre/libcfs/tracefile.h
@@ -0,0 +1,340 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2012, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ */
+
+#ifndef __LIBCFS_TRACEFILE_H__
+#define __LIBCFS_TRACEFILE_H__
+
+#include "../../include/linux/libcfs/libcfs.h"
+
+#include "linux/linux-tracefile.h"
+
+/* trace file lock routines */
+
+#define TRACEFILE_NAME_SIZE 1024
+extern char cfs_tracefile[TRACEFILE_NAME_SIZE];
+extern long long cfs_tracefile_size;
+
+extern void libcfs_run_debug_log_upcall(char *file);
+
+int cfs_tracefile_init_arch(void);
+void cfs_tracefile_fini_arch(void);
+
+void cfs_tracefile_read_lock(void);
+void cfs_tracefile_read_unlock(void);
+void cfs_tracefile_write_lock(void);
+void cfs_tracefile_write_unlock(void);
+
+int cfs_tracefile_dump_all_pages(char *filename);
+void cfs_trace_debug_print(void);
+void cfs_trace_flush_pages(void);
+int cfs_trace_start_thread(void);
+void cfs_trace_stop_thread(void);
+int cfs_tracefile_init(int max_pages);
+void cfs_tracefile_exit(void);
+
+
+
+int cfs_trace_copyin_string(char *knl_buffer, int knl_buffer_nob,
+ const char __user *usr_buffer, int usr_buffer_nob);
+int cfs_trace_copyout_string(char __user *usr_buffer, int usr_buffer_nob,
+ const char *knl_str, char *append);
+int cfs_trace_allocate_string_buffer(char **str, int nob);
+void cfs_trace_free_string_buffer(char *str, int nob);
+int cfs_trace_dump_debug_buffer_usrstr(void __user *usr_str, int usr_str_nob);
+int cfs_trace_daemon_command(char *str);
+int cfs_trace_daemon_command_usrstr(void __user *usr_str, int usr_str_nob);
+int cfs_trace_set_debug_mb(int mb);
+int cfs_trace_set_debug_mb_usrstr(void __user *usr_str, int usr_str_nob);
+int cfs_trace_get_debug_mb(void);
+
+extern void libcfs_debug_dumplog_internal(void *arg);
+extern void libcfs_register_panic_notifier(void);
+extern void libcfs_unregister_panic_notifier(void);
+extern int libcfs_panic_in_progress;
+extern int cfs_trace_max_debug_mb(void);
+
+#define TCD_MAX_PAGES (5 << (20 - PAGE_CACHE_SHIFT))
+#define TCD_STOCK_PAGES (TCD_MAX_PAGES)
+#define CFS_TRACEFILE_SIZE (500 << 20)
+
+#ifdef LUSTRE_TRACEFILE_PRIVATE
+
+/*
+ * Private declare for tracefile
+ */
+#define TCD_MAX_PAGES (5 << (20 - PAGE_CACHE_SHIFT))
+#define TCD_STOCK_PAGES (TCD_MAX_PAGES)
+
+#define CFS_TRACEFILE_SIZE (500 << 20)
+
+/* Size of a buffer for sprinting console messages if we can't get a page
+ * from system */
+#define CFS_TRACE_CONSOLE_BUFFER_SIZE 1024
+
+union cfs_trace_data_union {
+ struct cfs_trace_cpu_data {
+ /*
+ * Even though this structure is meant to be per-CPU, locking
+ * is needed because in some places the data may be accessed
+ * from other CPUs. This lock is directly used in trace_get_tcd
+ * and trace_put_tcd, which are called in libcfs_debug_vmsg2 and
+ * tcd_for_each_type_lock
+ */
+ spinlock_t tcd_lock;
+ unsigned long tcd_lock_flags;
+
+ /*
+ * pages with trace records not yet processed by tracefiled.
+ */
+ struct list_head tcd_pages;
+ /* number of pages on ->tcd_pages */
+ unsigned long tcd_cur_pages;
+
+ /*
+ * pages with trace records already processed by
+ * tracefiled. These pages are kept in memory, so that some
+ * portion of log can be written in the event of LBUG. This
+ * list is maintained in LRU order.
+ *
+ * Pages are moved to ->tcd_daemon_pages by tracefiled()
+ * (put_pages_on_daemon_list()). LRU pages from this list are
+ * discarded when list grows too large.
+ */
+ struct list_head tcd_daemon_pages;
+ /* number of pages on ->tcd_daemon_pages */
+ unsigned long tcd_cur_daemon_pages;
+
+ /*
+ * Maximal number of pages allowed on ->tcd_pages and
+ * ->tcd_daemon_pages each.
+ * Always TCD_MAX_PAGES * tcd_pages_factor / 100 in current
+ * implementation.
+ */
+ unsigned long tcd_max_pages;
+
+ /*
+ * preallocated pages to write trace records into. Pages from
+ * ->tcd_stock_pages are moved to ->tcd_pages by
+ * portals_debug_msg().
+ *
+ * This list is necessary, because on some platforms it's
+ * impossible to perform efficient atomic page allocation in a
+ * non-blockable context.
+ *
+ * Such platforms fill ->tcd_stock_pages "on occasion", when
+ * tracing code is entered in blockable context.
+ *
+ * trace_get_tage_try() tries to get a page from
+ * ->tcd_stock_pages first and resorts to atomic page
+ * allocation only if this queue is empty. ->tcd_stock_pages
+ * is replenished when tracing code is entered in blocking
+ * context (darwin-tracefile.c:trace_get_tcd()). We try to
+ * maintain TCD_STOCK_PAGES (40 by default) pages in this
+ * queue. Atomic allocation is only required if more than
+ * TCD_STOCK_PAGES pagesful are consumed by trace records all
+ * emitted in non-blocking contexts. Which is quite unlikely.
+ */
+ struct list_head tcd_stock_pages;
+ /* number of pages on ->tcd_stock_pages */
+ unsigned long tcd_cur_stock_pages;
+
+ unsigned short tcd_shutting_down;
+ unsigned short tcd_cpu;
+ unsigned short tcd_type;
+ /* The factors to share debug memory. */
+ unsigned short tcd_pages_factor;
+ } tcd;
+ char __pad[L1_CACHE_ALIGN(sizeof(struct cfs_trace_cpu_data))];
+};
+
+#define TCD_MAX_TYPES 8
+extern union cfs_trace_data_union (*cfs_trace_data[TCD_MAX_TYPES])[NR_CPUS];
+
+#define cfs_tcd_for_each(tcd, i, j) \
+ for (i = 0; cfs_trace_data[i] != NULL; i++) \
+ for (j = 0, ((tcd) = &(*cfs_trace_data[i])[j].tcd); \
+ j < num_possible_cpus(); \
+ j++, (tcd) = &(*cfs_trace_data[i])[j].tcd)
+
+#define cfs_tcd_for_each_type_lock(tcd, i, cpu) \
+ for (i = 0; cfs_trace_data[i] && \
+ (tcd = &(*cfs_trace_data[i])[cpu].tcd) && \
+ cfs_trace_lock_tcd(tcd, 1); cfs_trace_unlock_tcd(tcd, 1), i++)
+
+/* XXX nikita: this declaration is internal to tracefile.c and should probably
+ * be moved there */
+struct page_collection {
+ struct list_head pc_pages;
+ /*
+ * spin-lock protecting ->pc_pages. It is taken by smp_call_function()
+ * call-back functions. XXX nikita: Which is horrible: all processors
+ * receive NMI at the same time only to be serialized by this
+ * lock. Probably ->pc_pages should be replaced with an array of
+ * NR_CPUS elements accessed locklessly.
+ */
+ spinlock_t pc_lock;
+ /*
+ * if this flag is set, collect_pages() will spill both
+ * ->tcd_daemon_pages and ->tcd_pages to the ->pc_pages. Otherwise,
+ * only ->tcd_pages are spilled.
+ */
+ int pc_want_daemon_pages;
+};
+
+/* XXX nikita: this declaration is internal to tracefile.c and should probably
+ * be moved there */
+struct tracefiled_ctl {
+ struct completion tctl_start;
+ struct completion tctl_stop;
+ wait_queue_head_t tctl_waitq;
+ pid_t tctl_pid;
+ atomic_t tctl_shutdown;
+};
+
+/*
+ * small data-structure for each page owned by tracefiled.
+ */
+/* XXX nikita: this declaration is internal to tracefile.c and should probably
+ * be moved there */
+struct cfs_trace_page {
+ /*
+ * page itself
+ */
+ struct page *page;
+ /*
+ * linkage into one of the lists in trace_data_union or
+ * page_collection
+ */
+ struct list_head linkage;
+ /*
+ * number of bytes used within this page
+ */
+ unsigned int used;
+ /*
+ * cpu that owns this page
+ */
+ unsigned short cpu;
+ /*
+ * type(context) of this page
+ */
+ unsigned short type;
+};
+
+extern void cfs_set_ptldebug_header(struct ptldebug_header *header,
+ struct libcfs_debug_msg_data *m,
+ unsigned long stack);
+extern void cfs_print_to_console(struct ptldebug_header *hdr, int mask,
+ const char *buf, int len, const char *file,
+ const char *fn);
+
+extern int cfs_trace_lock_tcd(struct cfs_trace_cpu_data *tcd, int walking);
+extern void cfs_trace_unlock_tcd(struct cfs_trace_cpu_data *tcd, int walking);
+
+/**
+ * trace_buf_type_t, trace_buf_idx_get() and trace_console_buffers[][]
+ * are not public libcfs API; they should be defined in
+ * platform-specific tracefile include files
+ * (see, for example, linux-tracefile.h).
+ */
+
+extern char *cfs_trace_console_buffers[NR_CPUS][CFS_TCD_TYPE_MAX];
+extern cfs_trace_buf_type_t cfs_trace_buf_idx_get(void);
+
+static inline char *
+cfs_trace_get_console_buffer(void)
+{
+ unsigned int i = get_cpu();
+ unsigned int j = cfs_trace_buf_idx_get();
+
+ return cfs_trace_console_buffers[i][j];
+}
+
+static inline void
+cfs_trace_put_console_buffer(char *buffer)
+{
+ put_cpu();
+}
+
+static inline struct cfs_trace_cpu_data *
+cfs_trace_get_tcd(void)
+{
+ struct cfs_trace_cpu_data *tcd =
+ &(*cfs_trace_data[cfs_trace_buf_idx_get()])[get_cpu()].tcd;
+
+ cfs_trace_lock_tcd(tcd, 0);
+
+ return tcd;
+}
+
+static inline void
+cfs_trace_put_tcd (struct cfs_trace_cpu_data *tcd)
+{
+ cfs_trace_unlock_tcd(tcd, 0);
+
+ put_cpu();
+}
+
+int cfs_trace_refill_stock(struct cfs_trace_cpu_data *tcd, gfp_t gfp,
+ struct list_head *stock);
+
+
+int cfs_tcd_owns_tage(struct cfs_trace_cpu_data *tcd,
+ struct cfs_trace_page *tage);
+
+extern void cfs_trace_assertion_failed(const char *str,
+ struct libcfs_debug_msg_data *m);
+
+/* ASSERTION that is safe to use within the debug system */
+#define __LASSERT(cond) \
+do { \
+ if (unlikely(!(cond))) { \
+ LIBCFS_DEBUG_MSG_DATA_DECL(msgdata, D_EMERG, NULL); \
+ cfs_trace_assertion_failed("ASSERTION("#cond") failed", \
+ &msgdata); \
+ } \
+} while (0)
+
+#define __LASSERT_TAGE_INVARIANT(tage) \
+do { \
+ __LASSERT(tage != NULL); \
+ __LASSERT(tage->page != NULL); \
+ __LASSERT(tage->used <= PAGE_CACHE_SIZE); \
+ __LASSERT(page_count(tage->page) > 0); \
+} while (0)
+
+#endif /* LUSTRE_TRACEFILE_PRIVATE */
+
+#endif /* __LIBCFS_TRACEFILE_H__ */
diff --git a/drivers/staging/lustre/lustre/libcfs/workitem.c b/drivers/staging/lustre/lustre/libcfs/workitem.c
new file mode 100644
index 000000000..48009b775
--- /dev/null
+++ b/drivers/staging/lustre/lustre/libcfs/workitem.c
@@ -0,0 +1,479 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2011, 2012, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ *
+ * libcfs/libcfs/workitem.c
+ *
+ * Author: Isaac Huang <isaac@clusterfs.com>
+ * Liang Zhen <zhen.liang@sun.com>
+ */
+
+#define DEBUG_SUBSYSTEM S_LNET
+
+#include "../../include/linux/libcfs/libcfs.h"
+
+#define CFS_WS_NAME_LEN 16
+
+typedef struct cfs_wi_sched {
+ struct list_head ws_list; /* chain on global list */
+ /** serialised workitems */
+ spinlock_t ws_lock;
+ /** where schedulers sleep */
+ wait_queue_head_t ws_waitq;
+ /** concurrent workitems */
+ struct list_head ws_runq;
+ /** rescheduled running-workitems, a workitem can be rescheduled
+ * while running in wi_action(), but we don't to execute it again
+ * unless it returns from wi_action(), so we put it on ws_rerunq
+ * while rescheduling, and move it to runq after it returns
+ * from wi_action() */
+ struct list_head ws_rerunq;
+ /** CPT-table for this scheduler */
+ struct cfs_cpt_table *ws_cptab;
+ /** CPT id for affinity */
+ int ws_cpt;
+ /** number of scheduled workitems */
+ int ws_nscheduled;
+ /** started scheduler thread, protected by cfs_wi_data::wi_glock */
+ unsigned int ws_nthreads:30;
+ /** shutting down, protected by cfs_wi_data::wi_glock */
+ unsigned int ws_stopping:1;
+ /** serialize starting thread, protected by cfs_wi_data::wi_glock */
+ unsigned int ws_starting:1;
+ /** scheduler name */
+ char ws_name[CFS_WS_NAME_LEN];
+} cfs_wi_sched_t;
+
+static struct cfs_workitem_data {
+ /** serialize */
+ spinlock_t wi_glock;
+ /** list of all schedulers */
+ struct list_head wi_scheds;
+ /** WI module is initialized */
+ int wi_init;
+ /** shutting down the whole WI module */
+ int wi_stopping;
+} cfs_wi_data;
+
+static inline void
+cfs_wi_sched_lock(cfs_wi_sched_t *sched)
+{
+ spin_lock(&sched->ws_lock);
+}
+
+static inline void
+cfs_wi_sched_unlock(cfs_wi_sched_t *sched)
+{
+ spin_unlock(&sched->ws_lock);
+}
+
+static inline int
+cfs_wi_sched_cansleep(cfs_wi_sched_t *sched)
+{
+ cfs_wi_sched_lock(sched);
+ if (sched->ws_stopping) {
+ cfs_wi_sched_unlock(sched);
+ return 0;
+ }
+
+ if (!list_empty(&sched->ws_runq)) {
+ cfs_wi_sched_unlock(sched);
+ return 0;
+ }
+ cfs_wi_sched_unlock(sched);
+ return 1;
+}
+
+
+/* XXX:
+ * 0. it only works when called from wi->wi_action.
+ * 1. when it returns no one shall try to schedule the workitem.
+ */
+void
+cfs_wi_exit(struct cfs_wi_sched *sched, cfs_workitem_t *wi)
+{
+ LASSERT(!in_interrupt()); /* because we use plain spinlock */
+ LASSERT(!sched->ws_stopping);
+
+ cfs_wi_sched_lock(sched);
+
+ LASSERT(wi->wi_running);
+ if (wi->wi_scheduled) { /* cancel pending schedules */
+ LASSERT(!list_empty(&wi->wi_list));
+ list_del_init(&wi->wi_list);
+
+ LASSERT(sched->ws_nscheduled > 0);
+ sched->ws_nscheduled--;
+ }
+
+ LASSERT(list_empty(&wi->wi_list));
+
+ wi->wi_scheduled = 1; /* LBUG future schedule attempts */
+ cfs_wi_sched_unlock(sched);
+
+ return;
+}
+EXPORT_SYMBOL(cfs_wi_exit);
+
+/**
+ * cancel schedule request of workitem \a wi
+ */
+int
+cfs_wi_deschedule(struct cfs_wi_sched *sched, cfs_workitem_t *wi)
+{
+ int rc;
+
+ LASSERT(!in_interrupt()); /* because we use plain spinlock */
+ LASSERT(!sched->ws_stopping);
+
+ /*
+ * return 0 if it's running already, otherwise return 1, which
+ * means the workitem will not be scheduled and will not have
+ * any race with wi_action.
+ */
+ cfs_wi_sched_lock(sched);
+
+ rc = !(wi->wi_running);
+
+ if (wi->wi_scheduled) { /* cancel pending schedules */
+ LASSERT(!list_empty(&wi->wi_list));
+ list_del_init(&wi->wi_list);
+
+ LASSERT(sched->ws_nscheduled > 0);
+ sched->ws_nscheduled--;
+
+ wi->wi_scheduled = 0;
+ }
+
+ LASSERT (list_empty(&wi->wi_list));
+
+ cfs_wi_sched_unlock(sched);
+ return rc;
+}
+EXPORT_SYMBOL(cfs_wi_deschedule);
+
+/*
+ * Workitem scheduled with (serial == 1) is strictly serialised not only with
+ * itself, but also with others scheduled this way.
+ *
+ * Now there's only one static serialised queue, but in the future more might
+ * be added, and even dynamic creation of serialised queues might be supported.
+ */
+void
+cfs_wi_schedule(struct cfs_wi_sched *sched, cfs_workitem_t *wi)
+{
+ LASSERT(!in_interrupt()); /* because we use plain spinlock */
+ LASSERT(!sched->ws_stopping);
+
+ cfs_wi_sched_lock(sched);
+
+ if (!wi->wi_scheduled) {
+ LASSERT (list_empty(&wi->wi_list));
+
+ wi->wi_scheduled = 1;
+ sched->ws_nscheduled++;
+ if (!wi->wi_running) {
+ list_add_tail(&wi->wi_list, &sched->ws_runq);
+ wake_up(&sched->ws_waitq);
+ } else {
+ list_add(&wi->wi_list, &sched->ws_rerunq);
+ }
+ }
+
+ LASSERT (!list_empty(&wi->wi_list));
+ cfs_wi_sched_unlock(sched);
+ return;
+}
+EXPORT_SYMBOL(cfs_wi_schedule);
+
+
+static int
+cfs_wi_scheduler (void *arg)
+{
+ struct cfs_wi_sched *sched = (cfs_wi_sched_t *)arg;
+
+ cfs_block_allsigs();
+
+ /* CPT affinity scheduler? */
+ if (sched->ws_cptab != NULL)
+ cfs_cpt_bind(sched->ws_cptab, sched->ws_cpt);
+
+ spin_lock(&cfs_wi_data.wi_glock);
+
+ LASSERT(sched->ws_starting == 1);
+ sched->ws_starting--;
+ sched->ws_nthreads++;
+
+ spin_unlock(&cfs_wi_data.wi_glock);
+
+ cfs_wi_sched_lock(sched);
+
+ while (!sched->ws_stopping) {
+ int nloops = 0;
+ int rc;
+ cfs_workitem_t *wi;
+
+ while (!list_empty(&sched->ws_runq) &&
+ nloops < CFS_WI_RESCHED) {
+ wi = list_entry(sched->ws_runq.next,
+ cfs_workitem_t, wi_list);
+ LASSERT(wi->wi_scheduled && !wi->wi_running);
+
+ list_del_init(&wi->wi_list);
+
+ LASSERT(sched->ws_nscheduled > 0);
+ sched->ws_nscheduled--;
+
+ wi->wi_running = 1;
+ wi->wi_scheduled = 0;
+
+
+ cfs_wi_sched_unlock(sched);
+ nloops++;
+
+ rc = (*wi->wi_action) (wi);
+
+ cfs_wi_sched_lock(sched);
+ if (rc != 0) /* WI should be dead, even be freed! */
+ continue;
+
+ wi->wi_running = 0;
+ if (list_empty(&wi->wi_list))
+ continue;
+
+ LASSERT(wi->wi_scheduled);
+ /* wi is rescheduled, should be on rerunq now, we
+ * move it to runq so it can run action now */
+ list_move_tail(&wi->wi_list, &sched->ws_runq);
+ }
+
+ if (!list_empty(&sched->ws_runq)) {
+ cfs_wi_sched_unlock(sched);
+ /* don't sleep because some workitems still
+ * expect me to come back soon */
+ cond_resched();
+ cfs_wi_sched_lock(sched);
+ continue;
+ }
+
+ cfs_wi_sched_unlock(sched);
+ rc = wait_event_interruptible_exclusive(sched->ws_waitq,
+ !cfs_wi_sched_cansleep(sched));
+ cfs_wi_sched_lock(sched);
+ }
+
+ cfs_wi_sched_unlock(sched);
+
+ spin_lock(&cfs_wi_data.wi_glock);
+ sched->ws_nthreads--;
+ spin_unlock(&cfs_wi_data.wi_glock);
+
+ return 0;
+}
+
+
+void
+cfs_wi_sched_destroy(struct cfs_wi_sched *sched)
+{
+ int i;
+
+ LASSERT(cfs_wi_data.wi_init);
+ LASSERT(!cfs_wi_data.wi_stopping);
+
+ spin_lock(&cfs_wi_data.wi_glock);
+ if (sched->ws_stopping) {
+ CDEBUG(D_INFO, "%s is in progress of stopping\n",
+ sched->ws_name);
+ spin_unlock(&cfs_wi_data.wi_glock);
+ return;
+ }
+
+ LASSERT(!list_empty(&sched->ws_list));
+ sched->ws_stopping = 1;
+
+ spin_unlock(&cfs_wi_data.wi_glock);
+
+ i = 2;
+ wake_up_all(&sched->ws_waitq);
+
+ spin_lock(&cfs_wi_data.wi_glock);
+ while (sched->ws_nthreads > 0) {
+ CDEBUG(IS_PO2(++i) ? D_WARNING : D_NET,
+ "waiting for %d threads of WI sched[%s] to terminate\n",
+ sched->ws_nthreads, sched->ws_name);
+
+ spin_unlock(&cfs_wi_data.wi_glock);
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ schedule_timeout(cfs_time_seconds(1) / 20);
+ spin_lock(&cfs_wi_data.wi_glock);
+ }
+
+ list_del(&sched->ws_list);
+
+ spin_unlock(&cfs_wi_data.wi_glock);
+ LASSERT(sched->ws_nscheduled == 0);
+
+ LIBCFS_FREE(sched, sizeof(*sched));
+}
+EXPORT_SYMBOL(cfs_wi_sched_destroy);
+
+int
+cfs_wi_sched_create(char *name, struct cfs_cpt_table *cptab,
+ int cpt, int nthrs, struct cfs_wi_sched **sched_pp)
+{
+ struct cfs_wi_sched *sched;
+ int rc;
+
+ LASSERT(cfs_wi_data.wi_init);
+ LASSERT(!cfs_wi_data.wi_stopping);
+ LASSERT(cptab == NULL || cpt == CFS_CPT_ANY ||
+ (cpt >= 0 && cpt < cfs_cpt_number(cptab)));
+
+ LIBCFS_ALLOC(sched, sizeof(*sched));
+ if (sched == NULL)
+ return -ENOMEM;
+
+ strncpy(sched->ws_name, name, CFS_WS_NAME_LEN);
+ sched->ws_name[CFS_WS_NAME_LEN - 1] = '\0';
+ sched->ws_cptab = cptab;
+ sched->ws_cpt = cpt;
+
+ spin_lock_init(&sched->ws_lock);
+ init_waitqueue_head(&sched->ws_waitq);
+ INIT_LIST_HEAD(&sched->ws_runq);
+ INIT_LIST_HEAD(&sched->ws_rerunq);
+ INIT_LIST_HEAD(&sched->ws_list);
+
+ rc = 0;
+ while (nthrs > 0) {
+ char name[16];
+ struct task_struct *task;
+
+ spin_lock(&cfs_wi_data.wi_glock);
+ while (sched->ws_starting > 0) {
+ spin_unlock(&cfs_wi_data.wi_glock);
+ schedule();
+ spin_lock(&cfs_wi_data.wi_glock);
+ }
+
+ sched->ws_starting++;
+ spin_unlock(&cfs_wi_data.wi_glock);
+
+ if (sched->ws_cptab != NULL && sched->ws_cpt >= 0) {
+ snprintf(name, sizeof(name), "%s_%02d_%02u",
+ sched->ws_name, sched->ws_cpt,
+ sched->ws_nthreads);
+ } else {
+ snprintf(name, sizeof(name), "%s_%02u",
+ sched->ws_name, sched->ws_nthreads);
+ }
+
+ task = kthread_run(cfs_wi_scheduler, sched, "%s", name);
+ if (!IS_ERR(task)) {
+ nthrs--;
+ continue;
+ }
+ rc = PTR_ERR(task);
+
+ CERROR("Failed to create thread for WI scheduler %s: %d\n",
+ name, rc);
+
+ spin_lock(&cfs_wi_data.wi_glock);
+
+ /* make up for cfs_wi_sched_destroy */
+ list_add(&sched->ws_list, &cfs_wi_data.wi_scheds);
+ sched->ws_starting--;
+
+ spin_unlock(&cfs_wi_data.wi_glock);
+
+ cfs_wi_sched_destroy(sched);
+ return rc;
+ }
+ spin_lock(&cfs_wi_data.wi_glock);
+ list_add(&sched->ws_list, &cfs_wi_data.wi_scheds);
+ spin_unlock(&cfs_wi_data.wi_glock);
+
+ *sched_pp = sched;
+ return 0;
+}
+EXPORT_SYMBOL(cfs_wi_sched_create);
+
+int
+cfs_wi_startup(void)
+{
+ memset(&cfs_wi_data, 0, sizeof(cfs_wi_data));
+
+ spin_lock_init(&cfs_wi_data.wi_glock);
+ INIT_LIST_HEAD(&cfs_wi_data.wi_scheds);
+ cfs_wi_data.wi_init = 1;
+
+ return 0;
+}
+
+void
+cfs_wi_shutdown(void)
+{
+ struct cfs_wi_sched *sched;
+
+ spin_lock(&cfs_wi_data.wi_glock);
+ cfs_wi_data.wi_stopping = 1;
+ spin_unlock(&cfs_wi_data.wi_glock);
+
+ /* nobody should contend on this list */
+ list_for_each_entry(sched, &cfs_wi_data.wi_scheds, ws_list) {
+ sched->ws_stopping = 1;
+ wake_up_all(&sched->ws_waitq);
+ }
+
+ list_for_each_entry(sched, &cfs_wi_data.wi_scheds, ws_list) {
+ spin_lock(&cfs_wi_data.wi_glock);
+
+ while (sched->ws_nthreads != 0) {
+ spin_unlock(&cfs_wi_data.wi_glock);
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ schedule_timeout(cfs_time_seconds(1) / 20);
+ spin_lock(&cfs_wi_data.wi_glock);
+ }
+ spin_unlock(&cfs_wi_data.wi_glock);
+ }
+ while (!list_empty(&cfs_wi_data.wi_scheds)) {
+ sched = list_entry(cfs_wi_data.wi_scheds.next,
+ struct cfs_wi_sched, ws_list);
+ list_del(&sched->ws_list);
+ LIBCFS_FREE(sched, sizeof(*sched));
+ }
+
+ cfs_wi_data.wi_stopping = 0;
+ cfs_wi_data.wi_init = 0;
+}
diff --git a/drivers/staging/lustre/lustre/llite/Makefile b/drivers/staging/lustre/lustre/llite/Makefile
new file mode 100644
index 000000000..7d70115d5
--- /dev/null
+++ b/drivers/staging/lustre/lustre/llite/Makefile
@@ -0,0 +1,11 @@
+obj-$(CONFIG_LUSTRE_FS) += lustre.o
+obj-$(CONFIG_LUSTRE_LLITE_LLOOP) += llite_lloop.o
+lustre-y := dcache.o dir.o file.o llite_close.o llite_lib.o llite_nfs.o \
+ rw.o namei.o symlink.o llite_mmap.o \
+ xattr.o xattr_cache.o remote_perm.o llite_rmtacl.o llite_capa.o \
+ rw26.o super25.o statahead.o \
+ ../lclient/glimpse.o ../lclient/lcommon_cl.o ../lclient/lcommon_misc.o \
+ vvp_dev.o vvp_page.o vvp_lock.o vvp_io.o vvp_object.o
+
+lustre-$(CONFIG_PROC_FS) += lproc_llite.o
+llite_lloop-y := lloop.o
diff --git a/drivers/staging/lustre/lustre/llite/dcache.c b/drivers/staging/lustre/lustre/llite/dcache.c
new file mode 100644
index 000000000..5af013513
--- /dev/null
+++ b/drivers/staging/lustre/lustre/llite/dcache.c
@@ -0,0 +1,363 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2002, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2011, 2012, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ */
+
+#include <linux/fs.h>
+#include <linux/sched.h>
+#include <linux/quotaops.h>
+
+#define DEBUG_SUBSYSTEM S_LLITE
+
+#include "../include/obd_support.h"
+#include "../include/lustre_lite.h"
+#include "../include/lustre/lustre_idl.h"
+#include "../include/lustre_dlm.h"
+
+#include "llite_internal.h"
+
+static void free_dentry_data(struct rcu_head *head)
+{
+ struct ll_dentry_data *lld;
+
+ lld = container_of(head, struct ll_dentry_data, lld_rcu_head);
+ OBD_FREE_PTR(lld);
+}
+
+/* should NOT be called with the dcache lock, see fs/dcache.c */
+static void ll_release(struct dentry *de)
+{
+ struct ll_dentry_data *lld;
+
+ LASSERT(de != NULL);
+ lld = ll_d2d(de);
+ if (lld == NULL) /* NFS copies the de->d_op methods (bug 4655) */
+ return;
+
+ if (lld->lld_it) {
+ ll_intent_release(lld->lld_it);
+ OBD_FREE(lld->lld_it, sizeof(*lld->lld_it));
+ }
+
+ de->d_fsdata = NULL;
+ call_rcu(&lld->lld_rcu_head, free_dentry_data);
+}
+
+/* Compare if two dentries are the same. Don't match if the existing dentry
+ * is marked invalid. Returns 1 if different, 0 if the same.
+ *
+ * This avoids a race where ll_lookup_it() instantiates a dentry, but we get
+ * an AST before calling d_revalidate_it(). The dentry still exists (marked
+ * INVALID) so d_lookup() matches it, but we have no lock on it (so
+ * lock_match() fails) and we spin around real_lookup(). */
+static int ll_dcompare(const struct dentry *parent, const struct dentry *dentry,
+ unsigned int len, const char *str,
+ const struct qstr *name)
+{
+ if (len != name->len)
+ return 1;
+
+ if (memcmp(str, name->name, len))
+ return 1;
+
+ CDEBUG(D_DENTRY, "found name %.*s(%p) flags %#x refc %d\n",
+ name->len, name->name, dentry, dentry->d_flags,
+ d_count(dentry));
+
+ /* mountpoint is always valid */
+ if (d_mountpoint((struct dentry *)dentry))
+ return 0;
+
+ if (d_lustre_invalid(dentry))
+ return 1;
+
+ return 0;
+}
+
+static inline int return_if_equal(struct ldlm_lock *lock, void *data)
+{
+ if ((lock->l_flags &
+ (LDLM_FL_CANCELING | LDLM_FL_DISCARD_DATA)) ==
+ (LDLM_FL_CANCELING | LDLM_FL_DISCARD_DATA))
+ return LDLM_ITER_CONTINUE;
+ return LDLM_ITER_STOP;
+}
+
+/* find any ldlm lock of the inode in mdc and lov
+ * return 0 not find
+ * 1 find one
+ * < 0 error */
+static int find_cbdata(struct inode *inode)
+{
+ struct ll_sb_info *sbi = ll_i2sbi(inode);
+ struct lov_stripe_md *lsm;
+ int rc = 0;
+
+ LASSERT(inode);
+ rc = md_find_cbdata(sbi->ll_md_exp, ll_inode2fid(inode),
+ return_if_equal, NULL);
+ if (rc != 0)
+ return rc;
+
+ lsm = ccc_inode_lsm_get(inode);
+ if (lsm == NULL)
+ return rc;
+
+ rc = obd_find_cbdata(sbi->ll_dt_exp, lsm, return_if_equal, NULL);
+ ccc_inode_lsm_put(inode, lsm);
+
+ return rc;
+}
+
+/**
+ * Called when last reference to a dentry is dropped and dcache wants to know
+ * whether or not it should cache it:
+ * - return 1 to delete the dentry immediately
+ * - return 0 to cache the dentry
+ * Should NOT be called with the dcache lock, see fs/dcache.c
+ */
+static int ll_ddelete(const struct dentry *de)
+{
+ LASSERT(de);
+
+ CDEBUG(D_DENTRY, "%s dentry %pd (%p, parent %p, inode %p) %s%s\n",
+ d_lustre_invalid((struct dentry *)de) ? "deleting" : "keeping",
+ de, de, de->d_parent, d_inode(de),
+ d_unhashed(de) ? "" : "hashed,",
+ list_empty(&de->d_subdirs) ? "" : "subdirs");
+
+ /* kernel >= 2.6.38 last refcount is decreased after this function. */
+ LASSERT(d_count(de) == 1);
+
+ /* Disable this piece of code temporarily because this is called
+ * inside dcache_lock so it's not appropriate to do lots of work
+ * here. ATTENTION: Before this piece of code enabling, LU-2487 must be
+ * resolved. */
+#if 0
+ /* if not ldlm lock for this inode, set i_nlink to 0 so that
+ * this inode can be recycled later b=20433 */
+ if (d_really_is_positive(de) && !find_cbdata(d_inode(de)))
+ clear_nlink(d_inode(de));
+#endif
+
+ if (d_lustre_invalid((struct dentry *)de))
+ return 1;
+ return 0;
+}
+
+int ll_d_init(struct dentry *de)
+{
+ LASSERT(de != NULL);
+
+ CDEBUG(D_DENTRY, "ldd on dentry %pd (%p) parent %p inode %p refc %d\n",
+ de, de, de->d_parent, d_inode(de),
+ d_count(de));
+
+ if (de->d_fsdata == NULL) {
+ struct ll_dentry_data *lld;
+
+ lld = kzalloc(sizeof(*lld), GFP_NOFS);
+ if (likely(lld)) {
+ spin_lock(&de->d_lock);
+ if (likely(de->d_fsdata == NULL)) {
+ de->d_fsdata = lld;
+ __d_lustre_invalidate(de);
+ } else {
+ OBD_FREE_PTR(lld);
+ }
+ spin_unlock(&de->d_lock);
+ } else {
+ return -ENOMEM;
+ }
+ }
+ LASSERT(de->d_op == &ll_d_ops);
+
+ return 0;
+}
+
+void ll_intent_drop_lock(struct lookup_intent *it)
+{
+ if (it->it_op && it->d.lustre.it_lock_mode) {
+ struct lustre_handle handle;
+
+ handle.cookie = it->d.lustre.it_lock_handle;
+
+ CDEBUG(D_DLMTRACE, "releasing lock with cookie %#llx from it %p\n",
+ handle.cookie, it);
+ ldlm_lock_decref(&handle, it->d.lustre.it_lock_mode);
+
+ /* bug 494: intent_release may be called multiple times, from
+ * this thread and we don't want to double-decref this lock */
+ it->d.lustre.it_lock_mode = 0;
+ if (it->d.lustre.it_remote_lock_mode != 0) {
+ handle.cookie = it->d.lustre.it_remote_lock_handle;
+
+ CDEBUG(D_DLMTRACE, "releasing remote lock with cookie%#llx from it %p\n",
+ handle.cookie, it);
+ ldlm_lock_decref(&handle,
+ it->d.lustre.it_remote_lock_mode);
+ it->d.lustre.it_remote_lock_mode = 0;
+ }
+ }
+}
+
+void ll_intent_release(struct lookup_intent *it)
+{
+ CDEBUG(D_INFO, "intent %p released\n", it);
+ ll_intent_drop_lock(it);
+ /* We are still holding extra reference on a request, need to free it */
+ if (it_disposition(it, DISP_ENQ_OPEN_REF))
+ ptlrpc_req_finished(it->d.lustre.it_data); /* ll_file_open */
+
+ if (it_disposition(it, DISP_ENQ_CREATE_REF)) /* create rec */
+ ptlrpc_req_finished(it->d.lustre.it_data);
+
+ it->d.lustre.it_disposition = 0;
+ it->d.lustre.it_data = NULL;
+}
+
+void ll_invalidate_aliases(struct inode *inode)
+{
+ struct dentry *dentry;
+ struct ll_d_hlist_node *p;
+
+ LASSERT(inode != NULL);
+
+ CDEBUG(D_INODE, "marking dentries for ino %lu/%u(%p) invalid\n",
+ inode->i_ino, inode->i_generation, inode);
+
+ ll_lock_dcache(inode);
+ ll_d_hlist_for_each_entry(dentry, p, &inode->i_dentry, d_u.d_alias) {
+ CDEBUG(D_DENTRY, "dentry in drop %pd (%p) parent %p inode %p flags %d\n",
+ dentry, dentry, dentry->d_parent,
+ d_inode(dentry), dentry->d_flags);
+
+ d_lustre_invalidate(dentry, 0);
+ }
+ ll_unlock_dcache(inode);
+}
+
+int ll_revalidate_it_finish(struct ptlrpc_request *request,
+ struct lookup_intent *it,
+ struct inode *inode)
+{
+ int rc = 0;
+
+ if (!request)
+ return 0;
+
+ if (it_disposition(it, DISP_LOOKUP_NEG))
+ return -ENOENT;
+
+ rc = ll_prep_inode(&inode, request, NULL, it);
+
+ return rc;
+}
+
+void ll_lookup_finish_locks(struct lookup_intent *it, struct inode *inode)
+{
+ LASSERT(it != NULL);
+
+ if (it->d.lustre.it_lock_mode && inode != NULL) {
+ struct ll_sb_info *sbi = ll_i2sbi(inode);
+
+ CDEBUG(D_DLMTRACE, "setting l_data to inode %p (%lu/%u)\n",
+ inode, inode->i_ino, inode->i_generation);
+ ll_set_lock_data(sbi->ll_md_exp, inode, it, NULL);
+ }
+
+ /* drop lookup or getattr locks immediately */
+ if (it->it_op == IT_LOOKUP || it->it_op == IT_GETATTR) {
+ /* on 2.6 there are situation when several lookups and
+ * revalidations may be requested during single operation.
+ * therefore, we don't release intent here -bzzz */
+ ll_intent_drop_lock(it);
+ }
+}
+
+static int ll_revalidate_dentry(struct dentry *dentry,
+ unsigned int lookup_flags)
+{
+ struct inode *dir = d_inode(dentry->d_parent);
+
+ /*
+ * if open&create is set, talk to MDS to make sure file is created if
+ * necessary, because we can't do this in ->open() later since that's
+ * called on an inode. return 0 here to let lookup to handle this.
+ */
+ if ((lookup_flags & (LOOKUP_OPEN | LOOKUP_CREATE)) ==
+ (LOOKUP_OPEN | LOOKUP_CREATE))
+ return 0;
+
+ if (lookup_flags & (LOOKUP_PARENT | LOOKUP_OPEN | LOOKUP_CREATE))
+ return 1;
+
+ if (d_need_statahead(dir, dentry) <= 0)
+ return 1;
+
+ if (lookup_flags & LOOKUP_RCU)
+ return -ECHILD;
+
+ do_statahead_enter(dir, &dentry, d_inode(dentry) == NULL);
+ ll_statahead_mark(dir, dentry);
+ return 1;
+}
+
+/*
+ * Always trust cached dentries. Update statahead window if necessary.
+ */
+static int ll_revalidate_nd(struct dentry *dentry, unsigned int flags)
+{
+ CDEBUG(D_VFSTRACE, "VFS Op:name=%pd, flags=%u\n",
+ dentry, flags);
+
+ return ll_revalidate_dentry(dentry, flags);
+}
+
+
+static void ll_d_iput(struct dentry *de, struct inode *inode)
+{
+ LASSERT(inode);
+ if (!find_cbdata(inode))
+ clear_nlink(inode);
+ iput(inode);
+}
+
+const struct dentry_operations ll_d_ops = {
+ .d_revalidate = ll_revalidate_nd,
+ .d_release = ll_release,
+ .d_delete = ll_ddelete,
+ .d_iput = ll_d_iput,
+ .d_compare = ll_dcompare,
+};
diff --git a/drivers/staging/lustre/lustre/llite/dir.c b/drivers/staging/lustre/lustre/llite/dir.c
new file mode 100644
index 000000000..a5bc694dc
--- /dev/null
+++ b/drivers/staging/lustre/lustre/llite/dir.c
@@ -0,0 +1,1971 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2002, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2011, 2012, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ *
+ * lustre/llite/dir.c
+ *
+ * Directory code for lustre client.
+ */
+
+#include <linux/fs.h>
+#include <linux/pagemap.h>
+#include <linux/mm.h>
+#include <linux/uaccess.h>
+#include <linux/buffer_head.h> /* for wait_on_buffer */
+#include <linux/pagevec.h>
+#include <linux/prefetch.h>
+
+#define DEBUG_SUBSYSTEM S_LLITE
+
+#include "../include/obd_support.h"
+#include "../include/obd_class.h"
+#include "../include/lustre_lib.h"
+#include "../include/lustre/lustre_idl.h"
+#include "../include/lustre_lite.h"
+#include "../include/lustre_dlm.h"
+#include "../include/lustre_fid.h"
+#include "llite_internal.h"
+
+/*
+ * (new) readdir implementation overview.
+ *
+ * Original lustre readdir implementation cached exact copy of raw directory
+ * pages on the client. These pages were indexed in client page cache by
+ * logical offset in the directory file. This design, while very simple and
+ * intuitive had some inherent problems:
+ *
+ * . it implies that byte offset to the directory entry serves as a
+ * telldir(3)/seekdir(3) cookie, but that offset is not stable: in
+ * ext3/htree directory entries may move due to splits, and more
+ * importantly,
+ *
+ * . it is incompatible with the design of split directories for cmd3,
+ * that assumes that names are distributed across nodes based on their
+ * hash, and so readdir should be done in hash order.
+ *
+ * New readdir implementation does readdir in hash order, and uses hash of a
+ * file name as a telldir/seekdir cookie. This led to number of complications:
+ *
+ * . hash is not unique, so it cannot be used to index cached directory
+ * pages on the client (note, that it requires a whole pageful of hash
+ * collided entries to cause two pages to have identical hashes);
+ *
+ * . hash is not unique, so it cannot, strictly speaking, be used as an
+ * entry cookie. ext3/htree has the same problem and lustre implementation
+ * mimics their solution: seekdir(hash) positions directory at the first
+ * entry with the given hash.
+ *
+ * Client side.
+ *
+ * 0. caching
+ *
+ * Client caches directory pages using hash of the first entry as an index. As
+ * noted above hash is not unique, so this solution doesn't work as is:
+ * special processing is needed for "page hash chains" (i.e., sequences of
+ * pages filled with entries all having the same hash value).
+ *
+ * First, such chains have to be detected. To this end, server returns to the
+ * client the hash of the first entry on the page next to one returned. When
+ * client detects that this hash is the same as hash of the first entry on the
+ * returned page, page hash collision has to be handled. Pages in the
+ * hash chain, except first one, are termed "overflow pages".
+ *
+ * Solution to index uniqueness problem is to not cache overflow
+ * pages. Instead, when page hash collision is detected, all overflow pages
+ * from emerging chain are immediately requested from the server and placed in
+ * a special data structure (struct ll_dir_chain). This data structure is used
+ * by ll_readdir() to process entries from overflow pages. When readdir
+ * invocation finishes, overflow pages are discarded. If page hash collision
+ * chain weren't completely processed, next call to readdir will again detect
+ * page hash collision, again read overflow pages in, process next portion of
+ * entries and again discard the pages. This is not as wasteful as it looks,
+ * because, given reasonable hash, page hash collisions are extremely rare.
+ *
+ * 1. directory positioning
+ *
+ * When seekdir(hash) is called, original
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ * Server.
+ *
+ * identification of and access to overflow pages
+ *
+ * page format
+ *
+ * Page in MDS_READPAGE RPC is packed in LU_PAGE_SIZE, and each page contains
+ * a header lu_dirpage which describes the start/end hash, and whether this
+ * page is empty (contains no dir entry) or hash collide with next page.
+ * After client receives reply, several pages will be integrated into dir page
+ * in PAGE_CACHE_SIZE (if PAGE_CACHE_SIZE greater than LU_PAGE_SIZE), and the
+ * lu_dirpage for this integrated page will be adjusted. See
+ * lmv_adjust_dirpages().
+ *
+ */
+
+/* returns the page unlocked, but with a reference */
+static int ll_dir_filler(void *_hash, struct page *page0)
+{
+ struct inode *inode = page0->mapping->host;
+ int hash64 = ll_i2sbi(inode)->ll_flags & LL_SBI_64BIT_HASH;
+ struct obd_export *exp = ll_i2sbi(inode)->ll_md_exp;
+ struct ptlrpc_request *request;
+ struct mdt_body *body;
+ struct md_op_data *op_data;
+ __u64 hash = *((__u64 *)_hash);
+ struct page **page_pool;
+ struct page *page;
+ struct lu_dirpage *dp;
+ int max_pages = ll_i2sbi(inode)->ll_md_brw_size >> PAGE_CACHE_SHIFT;
+ int nrdpgs = 0; /* number of pages read actually */
+ int npages;
+ int i;
+ int rc;
+
+ CDEBUG(D_VFSTRACE, "VFS Op:inode=%lu/%u(%p) hash %llu\n",
+ inode->i_ino, inode->i_generation, inode, hash);
+
+ LASSERT(max_pages > 0 && max_pages <= MD_MAX_BRW_PAGES);
+
+ page_pool = kcalloc(max_pages, sizeof(page), GFP_NOFS);
+ if (page_pool) {
+ page_pool[0] = page0;
+ } else {
+ page_pool = &page0;
+ max_pages = 1;
+ }
+ for (npages = 1; npages < max_pages; npages++) {
+ page = page_cache_alloc_cold(inode->i_mapping);
+ if (!page)
+ break;
+ page_pool[npages] = page;
+ }
+
+ op_data = ll_prep_md_op_data(NULL, inode, NULL, NULL, 0, 0,
+ LUSTRE_OPC_ANY, NULL);
+ op_data->op_npages = npages;
+ op_data->op_offset = hash;
+ rc = md_readpage(exp, op_data, page_pool, &request);
+ ll_finish_md_op_data(op_data);
+ if (rc < 0) {
+ /* page0 is special, which was added into page cache early */
+ delete_from_page_cache(page0);
+ } else if (rc == 0) {
+ body = req_capsule_server_get(&request->rq_pill, &RMF_MDT_BODY);
+ /* Checked by mdc_readpage() */
+ LASSERT(body != NULL);
+
+ if (body->valid & OBD_MD_FLSIZE)
+ cl_isize_write(inode, body->size);
+
+ nrdpgs = (request->rq_bulk->bd_nob_transferred+PAGE_CACHE_SIZE-1)
+ >> PAGE_CACHE_SHIFT;
+ SetPageUptodate(page0);
+ }
+ unlock_page(page0);
+ ptlrpc_req_finished(request);
+
+ CDEBUG(D_VFSTRACE, "read %d/%d pages\n", nrdpgs, npages);
+
+ ll_pagevec_init(&lru_pvec, 0);
+ for (i = 1; i < npages; i++) {
+ unsigned long offset;
+ int ret;
+
+ page = page_pool[i];
+
+ if (rc < 0 || i >= nrdpgs) {
+ page_cache_release(page);
+ continue;
+ }
+
+ SetPageUptodate(page);
+
+ dp = kmap(page);
+ hash = le64_to_cpu(dp->ldp_hash_start);
+ kunmap(page);
+
+ offset = hash_x_index(hash, hash64);
+
+ prefetchw(&page->flags);
+ ret = add_to_page_cache_lru(page, inode->i_mapping, offset,
+ GFP_KERNEL);
+ if (ret == 0) {
+ unlock_page(page);
+ if (ll_pagevec_add(&lru_pvec, page) == 0)
+ ll_pagevec_lru_add_file(&lru_pvec);
+ } else {
+ CDEBUG(D_VFSTRACE, "page %lu add to page cache failed: %d\n",
+ offset, ret);
+ }
+ page_cache_release(page);
+ }
+ ll_pagevec_lru_add_file(&lru_pvec);
+
+ if (page_pool != &page0)
+ OBD_FREE(page_pool, sizeof(struct page *) * max_pages);
+ return rc;
+}
+
+static void ll_check_page(struct inode *dir, struct page *page)
+{
+ /* XXX: check page format later */
+ SetPageChecked(page);
+}
+
+void ll_release_page(struct page *page, int remove)
+{
+ kunmap(page);
+ if (remove) {
+ lock_page(page);
+ if (likely(page->mapping != NULL))
+ truncate_complete_page(page->mapping, page);
+ unlock_page(page);
+ }
+ page_cache_release(page);
+}
+
+/*
+ * Find, kmap and return page that contains given hash.
+ */
+static struct page *ll_dir_page_locate(struct inode *dir, __u64 *hash,
+ __u64 *start, __u64 *end)
+{
+ int hash64 = ll_i2sbi(dir)->ll_flags & LL_SBI_64BIT_HASH;
+ struct address_space *mapping = dir->i_mapping;
+ /*
+ * Complement of hash is used as an index so that
+ * radix_tree_gang_lookup() can be used to find a page with starting
+ * hash _smaller_ than one we are looking for.
+ */
+ unsigned long offset = hash_x_index(*hash, hash64);
+ struct page *page;
+ int found;
+
+ spin_lock_irq(&mapping->tree_lock);
+ found = radix_tree_gang_lookup(&mapping->page_tree,
+ (void **)&page, offset, 1);
+ if (found > 0 && !radix_tree_exceptional_entry(page)) {
+ struct lu_dirpage *dp;
+
+ page_cache_get(page);
+ spin_unlock_irq(&mapping->tree_lock);
+ /*
+ * In contrast to find_lock_page() we are sure that directory
+ * page cannot be truncated (while DLM lock is held) and,
+ * hence, can avoid restart.
+ *
+ * In fact, page cannot be locked here at all, because
+ * ll_dir_filler() does synchronous io.
+ */
+ wait_on_page_locked(page);
+ if (PageUptodate(page)) {
+ dp = kmap(page);
+ if (BITS_PER_LONG == 32 && hash64) {
+ *start = le64_to_cpu(dp->ldp_hash_start) >> 32;
+ *end = le64_to_cpu(dp->ldp_hash_end) >> 32;
+ *hash = *hash >> 32;
+ } else {
+ *start = le64_to_cpu(dp->ldp_hash_start);
+ *end = le64_to_cpu(dp->ldp_hash_end);
+ }
+ LASSERTF(*start <= *hash, "start = %#llx,end = %#llx,hash = %#llx\n",
+ *start, *end, *hash);
+ CDEBUG(D_VFSTRACE, "page %lu [%llu %llu], hash %llu\n",
+ offset, *start, *end, *hash);
+ if (*hash > *end) {
+ ll_release_page(page, 0);
+ page = NULL;
+ } else if (*end != *start && *hash == *end) {
+ /*
+ * upon hash collision, remove this page,
+ * otherwise put page reference, and
+ * ll_get_dir_page() will issue RPC to fetch
+ * the page we want.
+ */
+ ll_release_page(page,
+ le32_to_cpu(dp->ldp_flags) & LDF_COLLIDE);
+ page = NULL;
+ }
+ } else {
+ page_cache_release(page);
+ page = ERR_PTR(-EIO);
+ }
+
+ } else {
+ spin_unlock_irq(&mapping->tree_lock);
+ page = NULL;
+ }
+ return page;
+}
+
+struct page *ll_get_dir_page(struct inode *dir, __u64 hash,
+ struct ll_dir_chain *chain)
+{
+ ldlm_policy_data_t policy = {.l_inodebits = {MDS_INODELOCK_UPDATE} };
+ struct address_space *mapping = dir->i_mapping;
+ struct lustre_handle lockh;
+ struct lu_dirpage *dp;
+ struct page *page;
+ ldlm_mode_t mode;
+ int rc;
+ __u64 start = 0;
+ __u64 end = 0;
+ __u64 lhash = hash;
+ struct ll_inode_info *lli = ll_i2info(dir);
+ int hash64 = ll_i2sbi(dir)->ll_flags & LL_SBI_64BIT_HASH;
+
+ mode = LCK_PR;
+ rc = md_lock_match(ll_i2sbi(dir)->ll_md_exp, LDLM_FL_BLOCK_GRANTED,
+ ll_inode2fid(dir), LDLM_IBITS, &policy, mode, &lockh);
+ if (!rc) {
+ struct ldlm_enqueue_info einfo = {
+ .ei_type = LDLM_IBITS,
+ .ei_mode = mode,
+ .ei_cb_bl = ll_md_blocking_ast,
+ .ei_cb_cp = ldlm_completion_ast,
+ };
+ struct lookup_intent it = { .it_op = IT_READDIR };
+ struct ptlrpc_request *request;
+ struct md_op_data *op_data;
+
+ op_data = ll_prep_md_op_data(NULL, dir, dir, NULL, 0, 0,
+ LUSTRE_OPC_ANY, NULL);
+ if (IS_ERR(op_data))
+ return (void *)op_data;
+
+ rc = md_enqueue(ll_i2sbi(dir)->ll_md_exp, &einfo, &it,
+ op_data, &lockh, NULL, 0, NULL, 0);
+
+ ll_finish_md_op_data(op_data);
+
+ request = (struct ptlrpc_request *)it.d.lustre.it_data;
+ if (request)
+ ptlrpc_req_finished(request);
+ if (rc < 0) {
+ CERROR("lock enqueue: "DFID" at %llu: rc %d\n",
+ PFID(ll_inode2fid(dir)), hash, rc);
+ return ERR_PTR(rc);
+ }
+
+ CDEBUG(D_INODE, "setting lr_lvb_inode to inode %p (%lu/%u)\n",
+ dir, dir->i_ino, dir->i_generation);
+ md_set_lock_data(ll_i2sbi(dir)->ll_md_exp,
+ &it.d.lustre.it_lock_handle, dir, NULL);
+ } else {
+ /* for cross-ref object, l_ast_data of the lock may not be set,
+ * we reset it here */
+ md_set_lock_data(ll_i2sbi(dir)->ll_md_exp, &lockh.cookie,
+ dir, NULL);
+ }
+ ldlm_lock_dump_handle(D_OTHER, &lockh);
+
+ mutex_lock(&lli->lli_readdir_mutex);
+ page = ll_dir_page_locate(dir, &lhash, &start, &end);
+ if (IS_ERR(page)) {
+ CERROR("dir page locate: "DFID" at %llu: rc %ld\n",
+ PFID(ll_inode2fid(dir)), lhash, PTR_ERR(page));
+ goto out_unlock;
+ } else if (page != NULL) {
+ /*
+ * XXX nikita: not entirely correct handling of a corner case:
+ * suppose hash chain of entries with hash value HASH crosses
+ * border between pages P0 and P1. First both P0 and P1 are
+ * cached, seekdir() is called for some entry from the P0 part
+ * of the chain. Later P0 goes out of cache. telldir(HASH)
+ * happens and finds P1, as it starts with matching hash
+ * value. Remaining entries from P0 part of the chain are
+ * skipped. (Is that really a bug?)
+ *
+ * Possible solutions: 0. don't cache P1 is such case, handle
+ * it as an "overflow" page. 1. invalidate all pages at
+ * once. 2. use HASH|1 as an index for P1.
+ */
+ goto hash_collision;
+ }
+
+ page = read_cache_page(mapping, hash_x_index(hash, hash64),
+ ll_dir_filler, &lhash);
+ if (IS_ERR(page)) {
+ CERROR("read cache page: "DFID" at %llu: rc %ld\n",
+ PFID(ll_inode2fid(dir)), hash, PTR_ERR(page));
+ goto out_unlock;
+ }
+
+ wait_on_page_locked(page);
+ (void)kmap(page);
+ if (!PageUptodate(page)) {
+ CERROR("page not updated: "DFID" at %llu: rc %d\n",
+ PFID(ll_inode2fid(dir)), hash, -5);
+ goto fail;
+ }
+ if (!PageChecked(page))
+ ll_check_page(dir, page);
+ if (PageError(page)) {
+ CERROR("page error: "DFID" at %llu: rc %d\n",
+ PFID(ll_inode2fid(dir)), hash, -5);
+ goto fail;
+ }
+hash_collision:
+ dp = page_address(page);
+ if (BITS_PER_LONG == 32 && hash64) {
+ start = le64_to_cpu(dp->ldp_hash_start) >> 32;
+ end = le64_to_cpu(dp->ldp_hash_end) >> 32;
+ lhash = hash >> 32;
+ } else {
+ start = le64_to_cpu(dp->ldp_hash_start);
+ end = le64_to_cpu(dp->ldp_hash_end);
+ lhash = hash;
+ }
+ if (end == start) {
+ LASSERT(start == lhash);
+ CWARN("Page-wide hash collision: %llu\n", end);
+ if (BITS_PER_LONG == 32 && hash64)
+ CWARN("Real page-wide hash collision at [%llu %llu] with hash %llu\n",
+ le64_to_cpu(dp->ldp_hash_start),
+ le64_to_cpu(dp->ldp_hash_end), hash);
+ /*
+ * Fetch whole overflow chain...
+ *
+ * XXX not yet.
+ */
+ goto fail;
+ }
+out_unlock:
+ mutex_unlock(&lli->lli_readdir_mutex);
+ ldlm_lock_decref(&lockh, mode);
+ return page;
+
+fail:
+ ll_release_page(page, 1);
+ page = ERR_PTR(-EIO);
+ goto out_unlock;
+}
+
+int ll_dir_read(struct inode *inode, struct dir_context *ctx)
+{
+ struct ll_inode_info *info = ll_i2info(inode);
+ struct ll_sb_info *sbi = ll_i2sbi(inode);
+ __u64 pos = ctx->pos;
+ int api32 = ll_need_32bit_api(sbi);
+ int hash64 = sbi->ll_flags & LL_SBI_64BIT_HASH;
+ struct page *page;
+ struct ll_dir_chain chain;
+ int done = 0;
+ int rc = 0;
+
+ ll_dir_chain_init(&chain);
+
+ page = ll_get_dir_page(inode, pos, &chain);
+
+ while (rc == 0 && !done) {
+ struct lu_dirpage *dp;
+ struct lu_dirent *ent;
+
+ if (!IS_ERR(page)) {
+ /*
+ * If page is empty (end of directory is reached),
+ * use this value.
+ */
+ __u64 hash = MDS_DIR_END_OFF;
+ __u64 next;
+
+ dp = page_address(page);
+ for (ent = lu_dirent_start(dp); ent != NULL && !done;
+ ent = lu_dirent_next(ent)) {
+ __u16 type;
+ int namelen;
+ struct lu_fid fid;
+ __u64 lhash;
+ __u64 ino;
+
+ /*
+ * XXX: implement correct swabbing here.
+ */
+
+ hash = le64_to_cpu(ent->lde_hash);
+ if (hash < pos)
+ /*
+ * Skip until we find target hash
+ * value.
+ */
+ continue;
+
+ namelen = le16_to_cpu(ent->lde_namelen);
+ if (namelen == 0)
+ /*
+ * Skip dummy record.
+ */
+ continue;
+
+ if (api32 && hash64)
+ lhash = hash >> 32;
+ else
+ lhash = hash;
+ fid_le_to_cpu(&fid, &ent->lde_fid);
+ ino = cl_fid_build_ino(&fid, api32);
+ type = ll_dirent_type_get(ent);
+ ctx->pos = lhash;
+ /* For 'll_nfs_get_name_filldir()', it will try
+ * to access the 'ent' through its 'lde_name',
+ * so the parameter 'name' for 'ctx->actor()'
+ * must be part of the 'ent'.
+ */
+ done = !dir_emit(ctx, ent->lde_name,
+ namelen, ino, type);
+ }
+ next = le64_to_cpu(dp->ldp_hash_end);
+ if (!done) {
+ pos = next;
+ if (pos == MDS_DIR_END_OFF) {
+ /*
+ * End of directory reached.
+ */
+ done = 1;
+ ll_release_page(page, 0);
+ } else if (1 /* chain is exhausted*/) {
+ /*
+ * Normal case: continue to the next
+ * page.
+ */
+ ll_release_page(page,
+ le32_to_cpu(dp->ldp_flags) &
+ LDF_COLLIDE);
+ next = pos;
+ page = ll_get_dir_page(inode, pos,
+ &chain);
+ } else {
+ /*
+ * go into overflow page.
+ */
+ LASSERT(le32_to_cpu(dp->ldp_flags) &
+ LDF_COLLIDE);
+ ll_release_page(page, 1);
+ }
+ } else {
+ pos = hash;
+ ll_release_page(page, 0);
+ }
+ } else {
+ rc = PTR_ERR(page);
+ CERROR("error reading dir "DFID" at %lu: rc %d\n",
+ PFID(&info->lli_fid), (unsigned long)pos, rc);
+ }
+ }
+
+ ctx->pos = pos;
+ ll_dir_chain_fini(&chain);
+ return rc;
+}
+
+static int ll_readdir(struct file *filp, struct dir_context *ctx)
+{
+ struct inode *inode = file_inode(filp);
+ struct ll_file_data *lfd = LUSTRE_FPRIVATE(filp);
+ struct ll_sb_info *sbi = ll_i2sbi(inode);
+ int hash64 = sbi->ll_flags & LL_SBI_64BIT_HASH;
+ int api32 = ll_need_32bit_api(sbi);
+ int rc;
+
+ CDEBUG(D_VFSTRACE, "VFS Op:inode=%lu/%u(%p) pos %lu/%llu 32bit_api %d\n",
+ inode->i_ino, inode->i_generation,
+ inode, (unsigned long)lfd->lfd_pos, i_size_read(inode), api32);
+
+ if (lfd->lfd_pos == MDS_DIR_END_OFF) {
+ /*
+ * end-of-file.
+ */
+ rc = 0;
+ goto out;
+ }
+
+ ctx->pos = lfd->lfd_pos;
+ rc = ll_dir_read(inode, ctx);
+ lfd->lfd_pos = ctx->pos;
+ if (ctx->pos == MDS_DIR_END_OFF) {
+ if (api32)
+ ctx->pos = LL_DIR_END_OFF_32BIT;
+ else
+ ctx->pos = LL_DIR_END_OFF;
+ } else {
+ if (api32 && hash64)
+ ctx->pos >>= 32;
+ }
+ filp->f_version = inode->i_version;
+
+out:
+ if (!rc)
+ ll_stats_ops_tally(sbi, LPROC_LL_READDIR, 1);
+
+ return rc;
+}
+
+static int ll_send_mgc_param(struct obd_export *mgc, char *string)
+{
+ struct mgs_send_param *msp;
+ int rc = 0;
+
+ msp = kzalloc(sizeof(*msp), GFP_NOFS);
+ if (!msp)
+ return -ENOMEM;
+
+ strncpy(msp->mgs_param, string, MGS_PARAM_MAXLEN);
+ rc = obd_set_info_async(NULL, mgc, sizeof(KEY_SET_INFO), KEY_SET_INFO,
+ sizeof(struct mgs_send_param), msp, NULL);
+ if (rc)
+ CERROR("Failed to set parameter: %d\n", rc);
+ OBD_FREE_PTR(msp);
+
+ return rc;
+}
+
+static int ll_dir_setdirstripe(struct inode *dir, struct lmv_user_md *lump,
+ char *filename)
+{
+ struct ptlrpc_request *request = NULL;
+ struct md_op_data *op_data;
+ struct ll_sb_info *sbi = ll_i2sbi(dir);
+ int mode;
+ int err;
+
+ mode = (0755 & ~current_umask()) | S_IFDIR;
+ op_data = ll_prep_md_op_data(NULL, dir, NULL, filename,
+ strlen(filename), mode, LUSTRE_OPC_MKDIR,
+ lump);
+ if (IS_ERR(op_data)) {
+ err = PTR_ERR(op_data);
+ goto err_exit;
+ }
+
+ op_data->op_cli_flags |= CLI_SET_MEA;
+ err = md_create(sbi->ll_md_exp, op_data, lump, sizeof(*lump), mode,
+ from_kuid(&init_user_ns, current_fsuid()),
+ from_kgid(&init_user_ns, current_fsgid()),
+ cfs_curproc_cap_pack(), 0, &request);
+ ll_finish_md_op_data(op_data);
+ if (err)
+ goto err_exit;
+err_exit:
+ ptlrpc_req_finished(request);
+ return err;
+}
+
+int ll_dir_setstripe(struct inode *inode, struct lov_user_md *lump,
+ int set_default)
+{
+ struct ll_sb_info *sbi = ll_i2sbi(inode);
+ struct md_op_data *op_data;
+ struct ptlrpc_request *req = NULL;
+ int rc = 0;
+ struct lustre_sb_info *lsi = s2lsi(inode->i_sb);
+ struct obd_device *mgc = lsi->lsi_mgc;
+ int lum_size;
+
+ if (lump != NULL) {
+ /*
+ * This is coming from userspace, so should be in
+ * local endian. But the MDS would like it in little
+ * endian, so we swab it before we send it.
+ */
+ switch (lump->lmm_magic) {
+ case LOV_USER_MAGIC_V1: {
+ if (lump->lmm_magic != cpu_to_le32(LOV_USER_MAGIC_V1))
+ lustre_swab_lov_user_md_v1(lump);
+ lum_size = sizeof(struct lov_user_md_v1);
+ break;
+ }
+ case LOV_USER_MAGIC_V3: {
+ if (lump->lmm_magic != cpu_to_le32(LOV_USER_MAGIC_V3))
+ lustre_swab_lov_user_md_v3(
+ (struct lov_user_md_v3 *)lump);
+ lum_size = sizeof(struct lov_user_md_v3);
+ break;
+ }
+ default: {
+ CDEBUG(D_IOCTL, "bad userland LOV MAGIC: %#08x != %#08x nor %#08x\n",
+ lump->lmm_magic, LOV_USER_MAGIC_V1,
+ LOV_USER_MAGIC_V3);
+ return -EINVAL;
+ }
+ }
+ } else {
+ lum_size = sizeof(struct lov_user_md_v1);
+ }
+
+ op_data = ll_prep_md_op_data(NULL, inode, NULL, NULL, 0, 0,
+ LUSTRE_OPC_ANY, NULL);
+ if (IS_ERR(op_data))
+ return PTR_ERR(op_data);
+
+ if (lump != NULL && lump->lmm_magic == cpu_to_le32(LMV_USER_MAGIC))
+ op_data->op_cli_flags |= CLI_SET_MEA;
+
+ /* swabbing is done in lov_setstripe() on server side */
+ rc = md_setattr(sbi->ll_md_exp, op_data, lump, lum_size,
+ NULL, 0, &req, NULL);
+ ll_finish_md_op_data(op_data);
+ ptlrpc_req_finished(req);
+ if (rc) {
+ if (rc != -EPERM && rc != -EACCES)
+ CERROR("mdc_setattr fails: rc = %d\n", rc);
+ }
+
+ /* In the following we use the fact that LOV_USER_MAGIC_V1 and
+ LOV_USER_MAGIC_V3 have the same initial fields so we do not
+ need to make the distinction between the 2 versions */
+ if (set_default && mgc->u.cli.cl_mgc_mgsexp) {
+ char *param = NULL;
+ char *buf;
+
+ param = kzalloc(MGS_PARAM_MAXLEN, GFP_NOFS);
+ if (!param) {
+ rc = -ENOMEM;
+ goto end;
+ }
+
+ buf = param;
+ /* Get fsname and assume devname to be -MDT0000. */
+ ll_get_fsname(inode->i_sb, buf, MTI_NAME_MAXLEN);
+ strcat(buf, "-MDT0000.lov");
+ buf += strlen(buf);
+
+ /* Set root stripesize */
+ sprintf(buf, ".stripesize=%u",
+ lump ? le32_to_cpu(lump->lmm_stripe_size) : 0);
+ rc = ll_send_mgc_param(mgc->u.cli.cl_mgc_mgsexp, param);
+ if (rc)
+ goto end;
+
+ /* Set root stripecount */
+ sprintf(buf, ".stripecount=%hd",
+ lump ? le16_to_cpu(lump->lmm_stripe_count) : 0);
+ rc = ll_send_mgc_param(mgc->u.cli.cl_mgc_mgsexp, param);
+ if (rc)
+ goto end;
+
+ /* Set root stripeoffset */
+ sprintf(buf, ".stripeoffset=%hd",
+ lump ? le16_to_cpu(lump->lmm_stripe_offset) :
+ (typeof(lump->lmm_stripe_offset))(-1));
+ rc = ll_send_mgc_param(mgc->u.cli.cl_mgc_mgsexp, param);
+
+end:
+ if (param != NULL)
+ OBD_FREE(param, MGS_PARAM_MAXLEN);
+ }
+ return rc;
+}
+
+int ll_dir_getstripe(struct inode *inode, struct lov_mds_md **lmmp,
+ int *lmm_size, struct ptlrpc_request **request)
+{
+ struct ll_sb_info *sbi = ll_i2sbi(inode);
+ struct mdt_body *body;
+ struct lov_mds_md *lmm = NULL;
+ struct ptlrpc_request *req = NULL;
+ int rc, lmmsize;
+ struct md_op_data *op_data;
+
+ rc = ll_get_default_mdsize(sbi, &lmmsize);
+ if (rc)
+ return rc;
+
+ op_data = ll_prep_md_op_data(NULL, inode, NULL, NULL,
+ 0, lmmsize, LUSTRE_OPC_ANY,
+ NULL);
+ if (IS_ERR(op_data))
+ return PTR_ERR(op_data);
+
+ op_data->op_valid = OBD_MD_FLEASIZE | OBD_MD_FLDIREA;
+ rc = md_getattr(sbi->ll_md_exp, op_data, &req);
+ ll_finish_md_op_data(op_data);
+ if (rc < 0) {
+ CDEBUG(D_INFO, "md_getattr failed on inode %lu/%u: rc %d\n",
+ inode->i_ino,
+ inode->i_generation, rc);
+ goto out;
+ }
+
+ body = req_capsule_server_get(&req->rq_pill, &RMF_MDT_BODY);
+ LASSERT(body != NULL);
+
+ lmmsize = body->eadatasize;
+
+ if (!(body->valid & (OBD_MD_FLEASIZE | OBD_MD_FLDIREA)) ||
+ lmmsize == 0) {
+ rc = -ENODATA;
+ goto out;
+ }
+
+ lmm = req_capsule_server_sized_get(&req->rq_pill,
+ &RMF_MDT_MD, lmmsize);
+ LASSERT(lmm != NULL);
+
+ /*
+ * This is coming from the MDS, so is probably in
+ * little endian. We convert it to host endian before
+ * passing it to userspace.
+ */
+ /* We don't swab objects for directories */
+ switch (le32_to_cpu(lmm->lmm_magic)) {
+ case LOV_MAGIC_V1:
+ if (LOV_MAGIC != cpu_to_le32(LOV_MAGIC))
+ lustre_swab_lov_user_md_v1((struct lov_user_md_v1 *)lmm);
+ break;
+ case LOV_MAGIC_V3:
+ if (LOV_MAGIC != cpu_to_le32(LOV_MAGIC))
+ lustre_swab_lov_user_md_v3((struct lov_user_md_v3 *)lmm);
+ break;
+ default:
+ CERROR("unknown magic: %lX\n", (unsigned long)lmm->lmm_magic);
+ rc = -EPROTO;
+ }
+out:
+ *lmmp = lmm;
+ *lmm_size = lmmsize;
+ *request = req;
+ return rc;
+}
+
+/*
+ * Get MDT index for the inode.
+ */
+int ll_get_mdt_idx(struct inode *inode)
+{
+ struct ll_sb_info *sbi = ll_i2sbi(inode);
+ struct md_op_data *op_data;
+ int rc, mdtidx;
+
+ op_data = ll_prep_md_op_data(NULL, inode, NULL, NULL, 0,
+ 0, LUSTRE_OPC_ANY, NULL);
+ if (IS_ERR(op_data))
+ return PTR_ERR(op_data);
+
+ op_data->op_flags |= MF_GET_MDT_IDX;
+ rc = md_getattr(sbi->ll_md_exp, op_data, NULL);
+ mdtidx = op_data->op_mds;
+ ll_finish_md_op_data(op_data);
+ if (rc < 0) {
+ CDEBUG(D_INFO, "md_getattr_name: %d\n", rc);
+ return rc;
+ }
+ return mdtidx;
+}
+
+/**
+ * Generic handler to do any pre-copy work.
+ *
+ * It send a first hsm_progress (with extent length == 0) to coordinator as a
+ * first information for it that real work has started.
+ *
+ * Moreover, for a ARCHIVE request, it will sample the file data version and
+ * store it in \a copy.
+ *
+ * \return 0 on success.
+ */
+static int ll_ioc_copy_start(struct super_block *sb, struct hsm_copy *copy)
+{
+ struct ll_sb_info *sbi = ll_s2sbi(sb);
+ struct hsm_progress_kernel hpk;
+ int rc;
+
+ /* Forge a hsm_progress based on data from copy. */
+ hpk.hpk_fid = copy->hc_hai.hai_fid;
+ hpk.hpk_cookie = copy->hc_hai.hai_cookie;
+ hpk.hpk_extent.offset = copy->hc_hai.hai_extent.offset;
+ hpk.hpk_extent.length = 0;
+ hpk.hpk_flags = 0;
+ hpk.hpk_errval = 0;
+ hpk.hpk_data_version = 0;
+
+
+ /* For archive request, we need to read the current file version. */
+ if (copy->hc_hai.hai_action == HSMA_ARCHIVE) {
+ struct inode *inode;
+ __u64 data_version = 0;
+
+ /* Get inode for this fid */
+ inode = search_inode_for_lustre(sb, &copy->hc_hai.hai_fid);
+ if (IS_ERR(inode)) {
+ hpk.hpk_flags |= HP_FLAG_RETRY;
+ /* hpk_errval is >= 0 */
+ hpk.hpk_errval = -PTR_ERR(inode);
+ rc = PTR_ERR(inode);
+ goto progress;
+ }
+
+ /* Read current file data version */
+ rc = ll_data_version(inode, &data_version, 1);
+ iput(inode);
+ if (rc != 0) {
+ CDEBUG(D_HSM, "Could not read file data version of "
+ DFID" (rc = %d). Archive request (%#llx) could not be done.\n",
+ PFID(&copy->hc_hai.hai_fid), rc,
+ copy->hc_hai.hai_cookie);
+ hpk.hpk_flags |= HP_FLAG_RETRY;
+ /* hpk_errval must be >= 0 */
+ hpk.hpk_errval = -rc;
+ goto progress;
+ }
+
+ /* Store it the hsm_copy for later copytool use.
+ * Always modified even if no lsm. */
+ copy->hc_data_version = data_version;
+ }
+
+progress:
+ rc = obd_iocontrol(LL_IOC_HSM_PROGRESS, sbi->ll_md_exp, sizeof(hpk),
+ &hpk, NULL);
+
+ return rc;
+}
+
+/**
+ * Generic handler to do any post-copy work.
+ *
+ * It will send the last hsm_progress update to coordinator to inform it
+ * that copy is finished and whether it was successful or not.
+ *
+ * Moreover,
+ * - for ARCHIVE request, it will sample the file data version and compare it
+ * with the version saved in ll_ioc_copy_start(). If they do not match, copy
+ * will be considered as failed.
+ * - for RESTORE request, it will sample the file data version and send it to
+ * coordinator which is useful if the file was imported as 'released'.
+ *
+ * \return 0 on success.
+ */
+static int ll_ioc_copy_end(struct super_block *sb, struct hsm_copy *copy)
+{
+ struct ll_sb_info *sbi = ll_s2sbi(sb);
+ struct hsm_progress_kernel hpk;
+ int rc;
+
+ /* If you modify the logic here, also check llapi_hsm_copy_end(). */
+ /* Take care: copy->hc_hai.hai_action, len, gid and data are not
+ * initialized if copy_end was called with copy == NULL.
+ */
+
+ /* Forge a hsm_progress based on data from copy. */
+ hpk.hpk_fid = copy->hc_hai.hai_fid;
+ hpk.hpk_cookie = copy->hc_hai.hai_cookie;
+ hpk.hpk_extent = copy->hc_hai.hai_extent;
+ hpk.hpk_flags = copy->hc_flags | HP_FLAG_COMPLETED;
+ hpk.hpk_errval = copy->hc_errval;
+ hpk.hpk_data_version = 0;
+
+ /* For archive request, we need to check the file data was not changed.
+ *
+ * For restore request, we need to send the file data version, this is
+ * useful when the file was created using hsm_import.
+ */
+ if (((copy->hc_hai.hai_action == HSMA_ARCHIVE) ||
+ (copy->hc_hai.hai_action == HSMA_RESTORE)) &&
+ (copy->hc_errval == 0)) {
+ struct inode *inode;
+ __u64 data_version = 0;
+
+ /* Get lsm for this fid */
+ inode = search_inode_for_lustre(sb, &copy->hc_hai.hai_fid);
+ if (IS_ERR(inode)) {
+ hpk.hpk_flags |= HP_FLAG_RETRY;
+ /* hpk_errval must be >= 0 */
+ hpk.hpk_errval = -PTR_ERR(inode);
+ rc = PTR_ERR(inode);
+ goto progress;
+ }
+
+ rc = ll_data_version(inode, &data_version,
+ copy->hc_hai.hai_action == HSMA_ARCHIVE);
+ iput(inode);
+ if (rc) {
+ CDEBUG(D_HSM, "Could not read file data version. Request could not be confirmed.\n");
+ if (hpk.hpk_errval == 0)
+ hpk.hpk_errval = -rc;
+ goto progress;
+ }
+
+ /* Store it the hsm_copy for later copytool use.
+ * Always modified even if no lsm. */
+ hpk.hpk_data_version = data_version;
+
+ /* File could have been stripped during archiving, so we need
+ * to check anyway. */
+ if ((copy->hc_hai.hai_action == HSMA_ARCHIVE) &&
+ (copy->hc_data_version != data_version)) {
+ CDEBUG(D_HSM, "File data version mismatched. File content was changed during archiving. "
+ DFID", start:%#llx current:%#llx\n",
+ PFID(&copy->hc_hai.hai_fid),
+ copy->hc_data_version, data_version);
+ /* File was changed, send error to cdt. Do not ask for
+ * retry because if a file is modified frequently,
+ * the cdt will loop on retried archive requests.
+ * The policy engine will ask for a new archive later
+ * when the file will not be modified for some tunable
+ * time */
+ /* we do not notify caller */
+ hpk.hpk_flags &= ~HP_FLAG_RETRY;
+ /* hpk_errval must be >= 0 */
+ hpk.hpk_errval = EBUSY;
+ }
+
+ }
+
+progress:
+ rc = obd_iocontrol(LL_IOC_HSM_PROGRESS, sbi->ll_md_exp, sizeof(hpk),
+ &hpk, NULL);
+
+ return rc;
+}
+
+
+static int copy_and_ioctl(int cmd, struct obd_export *exp,
+ const void __user *data, size_t size)
+{
+ void *copy;
+ int rc;
+
+ copy = kzalloc(size, GFP_NOFS);
+ if (!copy)
+ return -ENOMEM;
+
+ if (copy_from_user(copy, data, size)) {
+ rc = -EFAULT;
+ goto out;
+ }
+
+ rc = obd_iocontrol(cmd, exp, size, copy, NULL);
+out:
+ OBD_FREE(copy, size);
+
+ return rc;
+}
+
+static int quotactl_ioctl(struct ll_sb_info *sbi, struct if_quotactl *qctl)
+{
+ int cmd = qctl->qc_cmd;
+ int type = qctl->qc_type;
+ int id = qctl->qc_id;
+ int valid = qctl->qc_valid;
+ int rc = 0;
+
+ switch (cmd) {
+ case LUSTRE_Q_INVALIDATE:
+ case LUSTRE_Q_FINVALIDATE:
+ case Q_QUOTAON:
+ case Q_QUOTAOFF:
+ case Q_SETQUOTA:
+ case Q_SETINFO:
+ if (!capable(CFS_CAP_SYS_ADMIN) ||
+ sbi->ll_flags & LL_SBI_RMT_CLIENT)
+ return -EPERM;
+ break;
+ case Q_GETQUOTA:
+ if (((type == USRQUOTA &&
+ !uid_eq(current_euid(), make_kuid(&init_user_ns, id))) ||
+ (type == GRPQUOTA &&
+ !in_egroup_p(make_kgid(&init_user_ns, id)))) &&
+ (!capable(CFS_CAP_SYS_ADMIN) ||
+ sbi->ll_flags & LL_SBI_RMT_CLIENT))
+ return -EPERM;
+ break;
+ case Q_GETINFO:
+ break;
+ default:
+ CERROR("unsupported quotactl op: %#x\n", cmd);
+ return -ENOTTY;
+ }
+
+ if (valid != QC_GENERAL) {
+ if (sbi->ll_flags & LL_SBI_RMT_CLIENT)
+ return -EOPNOTSUPP;
+
+ if (cmd == Q_GETINFO)
+ qctl->qc_cmd = Q_GETOINFO;
+ else if (cmd == Q_GETQUOTA)
+ qctl->qc_cmd = Q_GETOQUOTA;
+ else
+ return -EINVAL;
+
+ switch (valid) {
+ case QC_MDTIDX:
+ rc = obd_iocontrol(OBD_IOC_QUOTACTL, sbi->ll_md_exp,
+ sizeof(*qctl), qctl, NULL);
+ break;
+ case QC_OSTIDX:
+ rc = obd_iocontrol(OBD_IOC_QUOTACTL, sbi->ll_dt_exp,
+ sizeof(*qctl), qctl, NULL);
+ break;
+ case QC_UUID:
+ rc = obd_iocontrol(OBD_IOC_QUOTACTL, sbi->ll_md_exp,
+ sizeof(*qctl), qctl, NULL);
+ if (rc == -EAGAIN)
+ rc = obd_iocontrol(OBD_IOC_QUOTACTL,
+ sbi->ll_dt_exp,
+ sizeof(*qctl), qctl, NULL);
+ break;
+ default:
+ rc = -EINVAL;
+ break;
+ }
+
+ if (rc)
+ return rc;
+
+ qctl->qc_cmd = cmd;
+ } else {
+ struct obd_quotactl *oqctl;
+
+ oqctl = kzalloc(sizeof(*oqctl), GFP_NOFS);
+ if (!oqctl)
+ return -ENOMEM;
+
+ QCTL_COPY(oqctl, qctl);
+ rc = obd_quotactl(sbi->ll_md_exp, oqctl);
+ if (rc) {
+ if (rc != -EALREADY && cmd == Q_QUOTAON) {
+ oqctl->qc_cmd = Q_QUOTAOFF;
+ obd_quotactl(sbi->ll_md_exp, oqctl);
+ }
+ OBD_FREE_PTR(oqctl);
+ return rc;
+ }
+ /* If QIF_SPACE is not set, client should collect the
+ * space usage from OSSs by itself */
+ if (cmd == Q_GETQUOTA &&
+ !(oqctl->qc_dqblk.dqb_valid & QIF_SPACE) &&
+ !oqctl->qc_dqblk.dqb_curspace) {
+ struct obd_quotactl *oqctl_tmp;
+
+ oqctl_tmp = kzalloc(sizeof(*oqctl_tmp), GFP_NOFS);
+ if (!oqctl_tmp) {
+ rc = -ENOMEM;
+ goto out;
+ }
+
+ oqctl_tmp->qc_cmd = Q_GETOQUOTA;
+ oqctl_tmp->qc_id = oqctl->qc_id;
+ oqctl_tmp->qc_type = oqctl->qc_type;
+
+ /* collect space usage from OSTs */
+ oqctl_tmp->qc_dqblk.dqb_curspace = 0;
+ rc = obd_quotactl(sbi->ll_dt_exp, oqctl_tmp);
+ if (!rc || rc == -EREMOTEIO) {
+ oqctl->qc_dqblk.dqb_curspace =
+ oqctl_tmp->qc_dqblk.dqb_curspace;
+ oqctl->qc_dqblk.dqb_valid |= QIF_SPACE;
+ }
+
+ /* collect space & inode usage from MDTs */
+ oqctl_tmp->qc_dqblk.dqb_curspace = 0;
+ oqctl_tmp->qc_dqblk.dqb_curinodes = 0;
+ rc = obd_quotactl(sbi->ll_md_exp, oqctl_tmp);
+ if (!rc || rc == -EREMOTEIO) {
+ oqctl->qc_dqblk.dqb_curspace +=
+ oqctl_tmp->qc_dqblk.dqb_curspace;
+ oqctl->qc_dqblk.dqb_curinodes =
+ oqctl_tmp->qc_dqblk.dqb_curinodes;
+ oqctl->qc_dqblk.dqb_valid |= QIF_INODES;
+ } else {
+ oqctl->qc_dqblk.dqb_valid &= ~QIF_SPACE;
+ }
+
+ OBD_FREE_PTR(oqctl_tmp);
+ }
+out:
+ QCTL_COPY(qctl, oqctl);
+ OBD_FREE_PTR(oqctl);
+ }
+
+ return rc;
+}
+
+static char *
+ll_getname(const char __user *filename)
+{
+ int ret = 0, len;
+ char *tmp = __getname();
+
+ if (!tmp)
+ return ERR_PTR(-ENOMEM);
+
+ len = strncpy_from_user(tmp, filename, PATH_MAX);
+ if (len == 0)
+ ret = -ENOENT;
+ else if (len > PATH_MAX)
+ ret = -ENAMETOOLONG;
+
+ if (ret) {
+ __putname(tmp);
+ tmp = ERR_PTR(ret);
+ }
+ return tmp;
+}
+
+#define ll_putname(filename) __putname(filename)
+
+static long ll_dir_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+ struct inode *inode = file_inode(file);
+ struct ll_sb_info *sbi = ll_i2sbi(inode);
+ struct obd_ioctl_data *data;
+ int rc = 0;
+
+ CDEBUG(D_VFSTRACE, "VFS Op:inode=%lu/%u(%p), cmd=%#x\n",
+ inode->i_ino, inode->i_generation, inode, cmd);
+
+ /* asm-ppc{,64} declares TCGETS, et. al. as type 't' not 'T' */
+ if (_IOC_TYPE(cmd) == 'T' || _IOC_TYPE(cmd) == 't') /* tty ioctls */
+ return -ENOTTY;
+
+ ll_stats_ops_tally(ll_i2sbi(inode), LPROC_LL_IOCTL, 1);
+ switch (cmd) {
+ case FSFILT_IOC_GETFLAGS:
+ case FSFILT_IOC_SETFLAGS:
+ return ll_iocontrol(inode, file, cmd, arg);
+ case FSFILT_IOC_GETVERSION_OLD:
+ case FSFILT_IOC_GETVERSION:
+ return put_user(inode->i_generation, (int *)arg);
+ /* We need to special case any other ioctls we want to handle,
+ * to send them to the MDS/OST as appropriate and to properly
+ * network encode the arg field.
+ case FSFILT_IOC_SETVERSION_OLD:
+ case FSFILT_IOC_SETVERSION:
+ */
+ case LL_IOC_GET_MDTIDX: {
+ int mdtidx;
+
+ mdtidx = ll_get_mdt_idx(inode);
+ if (mdtidx < 0)
+ return mdtidx;
+
+ if (put_user((int)mdtidx, (int *)arg))
+ return -EFAULT;
+
+ return 0;
+ }
+ case IOC_MDC_LOOKUP: {
+ struct ptlrpc_request *request = NULL;
+ int namelen, len = 0;
+ char *buf = NULL;
+ char *filename;
+ struct md_op_data *op_data;
+
+ rc = obd_ioctl_getdata(&buf, &len, (void *)arg);
+ if (rc)
+ return rc;
+ data = (void *)buf;
+
+ filename = data->ioc_inlbuf1;
+ namelen = strlen(filename);
+
+ if (namelen < 1) {
+ CDEBUG(D_INFO, "IOC_MDC_LOOKUP missing filename\n");
+ rc = -EINVAL;
+ goto out_free;
+ }
+
+ op_data = ll_prep_md_op_data(NULL, inode, NULL, filename, namelen,
+ 0, LUSTRE_OPC_ANY, NULL);
+ if (IS_ERR(op_data)) {
+ rc = PTR_ERR(op_data);
+ goto out_free;
+ }
+
+ op_data->op_valid = OBD_MD_FLID;
+ rc = md_getattr_name(sbi->ll_md_exp, op_data, &request);
+ ll_finish_md_op_data(op_data);
+ if (rc < 0) {
+ CDEBUG(D_INFO, "md_getattr_name: %d\n", rc);
+ goto out_free;
+ }
+ ptlrpc_req_finished(request);
+out_free:
+ obd_ioctl_freedata(buf, len);
+ return rc;
+ }
+ case LL_IOC_LMV_SETSTRIPE: {
+ struct lmv_user_md *lum;
+ char *buf = NULL;
+ char *filename;
+ int namelen = 0;
+ int lumlen = 0;
+ int len;
+ int rc;
+
+ rc = obd_ioctl_getdata(&buf, &len, (void *)arg);
+ if (rc)
+ return rc;
+
+ data = (void *)buf;
+ if (data->ioc_inlbuf1 == NULL || data->ioc_inlbuf2 == NULL ||
+ data->ioc_inllen1 == 0 || data->ioc_inllen2 == 0) {
+ rc = -EINVAL;
+ goto lmv_out_free;
+ }
+
+ filename = data->ioc_inlbuf1;
+ namelen = data->ioc_inllen1;
+
+ if (namelen < 1) {
+ CDEBUG(D_INFO, "IOC_MDC_LOOKUP missing filename\n");
+ rc = -EINVAL;
+ goto lmv_out_free;
+ }
+ lum = (struct lmv_user_md *)data->ioc_inlbuf2;
+ lumlen = data->ioc_inllen2;
+
+ if (lum->lum_magic != LMV_USER_MAGIC ||
+ lumlen != sizeof(*lum)) {
+ CERROR("%s: wrong lum magic %x or size %d: rc = %d\n",
+ filename, lum->lum_magic, lumlen, -EFAULT);
+ rc = -EINVAL;
+ goto lmv_out_free;
+ }
+
+ /**
+ * ll_dir_setdirstripe will be used to set dir stripe
+ * mdc_create--->mdt_reint_create (with dirstripe)
+ */
+ rc = ll_dir_setdirstripe(inode, lum, filename);
+lmv_out_free:
+ obd_ioctl_freedata(buf, len);
+ return rc;
+
+ }
+ case LL_IOC_LOV_SETSTRIPE: {
+ struct lov_user_md_v3 lumv3;
+ struct lov_user_md_v1 *lumv1 = (struct lov_user_md_v1 *)&lumv3;
+ struct lov_user_md_v1 *lumv1p = (struct lov_user_md_v1 *)arg;
+ struct lov_user_md_v3 *lumv3p = (struct lov_user_md_v3 *)arg;
+
+ int set_default = 0;
+
+ LASSERT(sizeof(lumv3) == sizeof(*lumv3p));
+ LASSERT(sizeof(lumv3.lmm_objects[0]) ==
+ sizeof(lumv3p->lmm_objects[0]));
+ /* first try with v1 which is smaller than v3 */
+ if (copy_from_user(lumv1, lumv1p, sizeof(*lumv1)))
+ return -EFAULT;
+
+ if (lumv1->lmm_magic == LOV_USER_MAGIC_V3) {
+ if (copy_from_user(&lumv3, lumv3p, sizeof(lumv3)))
+ return -EFAULT;
+ }
+
+ if (is_root_inode(inode))
+ set_default = 1;
+
+ /* in v1 and v3 cases lumv1 points to data */
+ rc = ll_dir_setstripe(inode, lumv1, set_default);
+
+ return rc;
+ }
+ case LL_IOC_LMV_GETSTRIPE: {
+ struct lmv_user_md *lump = (struct lmv_user_md *)arg;
+ struct lmv_user_md lum;
+ struct lmv_user_md *tmp;
+ int lum_size;
+ int rc = 0;
+ int mdtindex;
+
+ if (copy_from_user(&lum, lump, sizeof(struct lmv_user_md)))
+ return -EFAULT;
+
+ if (lum.lum_magic != LMV_MAGIC_V1)
+ return -EINVAL;
+
+ lum_size = lmv_user_md_size(1, LMV_MAGIC_V1);
+ tmp = kzalloc(lum_size, GFP_NOFS);
+ if (!tmp) {
+ rc = -ENOMEM;
+ goto free_lmv;
+ }
+
+ *tmp = lum;
+ tmp->lum_type = LMV_STRIPE_TYPE;
+ tmp->lum_stripe_count = 1;
+ mdtindex = ll_get_mdt_idx(inode);
+ if (mdtindex < 0) {
+ rc = -ENOMEM;
+ goto free_lmv;
+ }
+
+ tmp->lum_stripe_offset = mdtindex;
+ tmp->lum_objects[0].lum_mds = mdtindex;
+ memcpy(&tmp->lum_objects[0].lum_fid, ll_inode2fid(inode),
+ sizeof(struct lu_fid));
+ if (copy_to_user((void *)arg, tmp, lum_size)) {
+ rc = -EFAULT;
+ goto free_lmv;
+ }
+free_lmv:
+ if (tmp)
+ OBD_FREE(tmp, lum_size);
+ return rc;
+ }
+ case LL_IOC_REMOVE_ENTRY: {
+ char *filename = NULL;
+ int namelen = 0;
+ int rc;
+
+ /* Here is a little hack to avoid sending REINT_RMENTRY to
+ * unsupported server, which might crash the server(LU-2730),
+ * Because both LVB_TYPE and REINT_RMENTRY will be supported
+ * on 2.4, we use OBD_CONNECT_LVB_TYPE to detect whether the
+ * server will support REINT_RMENTRY XXX*/
+ if (!(exp_connect_flags(sbi->ll_md_exp) & OBD_CONNECT_LVB_TYPE))
+ return -ENOTSUPP;
+
+ filename = ll_getname((const char *)arg);
+ if (IS_ERR(filename))
+ return PTR_ERR(filename);
+
+ namelen = strlen(filename);
+ if (namelen < 1) {
+ rc = -EINVAL;
+ goto out_rmdir;
+ }
+
+ rc = ll_rmdir_entry(inode, filename, namelen);
+out_rmdir:
+ if (filename)
+ ll_putname(filename);
+ return rc;
+ }
+ case LL_IOC_LOV_SWAP_LAYOUTS:
+ return -EPERM;
+ case LL_IOC_OBD_STATFS:
+ return ll_obd_statfs(inode, (void *)arg);
+ case LL_IOC_LOV_GETSTRIPE:
+ case LL_IOC_MDC_GETINFO:
+ case IOC_MDC_GETFILEINFO:
+ case IOC_MDC_GETFILESTRIPE: {
+ struct ptlrpc_request *request = NULL;
+ struct lov_user_md *lump;
+ struct lov_mds_md *lmm = NULL;
+ struct mdt_body *body;
+ char *filename = NULL;
+ int lmmsize;
+
+ if (cmd == IOC_MDC_GETFILEINFO ||
+ cmd == IOC_MDC_GETFILESTRIPE) {
+ filename = ll_getname((const char *)arg);
+ if (IS_ERR(filename))
+ return PTR_ERR(filename);
+
+ rc = ll_lov_getstripe_ea_info(inode, filename, &lmm,
+ &lmmsize, &request);
+ } else {
+ rc = ll_dir_getstripe(inode, &lmm, &lmmsize, &request);
+ }
+
+ if (request) {
+ body = req_capsule_server_get(&request->rq_pill,
+ &RMF_MDT_BODY);
+ LASSERT(body != NULL);
+ } else {
+ goto out_req;
+ }
+
+ if (rc < 0) {
+ if (rc == -ENODATA && (cmd == IOC_MDC_GETFILEINFO ||
+ cmd == LL_IOC_MDC_GETINFO)) {
+ rc = 0;
+ goto skip_lmm;
+ } else
+ goto out_req;
+ }
+
+ if (cmd == IOC_MDC_GETFILESTRIPE ||
+ cmd == LL_IOC_LOV_GETSTRIPE) {
+ lump = (struct lov_user_md *)arg;
+ } else {
+ struct lov_user_mds_data *lmdp;
+
+ lmdp = (struct lov_user_mds_data *)arg;
+ lump = &lmdp->lmd_lmm;
+ }
+ if (copy_to_user(lump, lmm, lmmsize)) {
+ if (copy_to_user(lump, lmm, sizeof(*lump))) {
+ rc = -EFAULT;
+ goto out_req;
+ }
+ rc = -EOVERFLOW;
+ }
+skip_lmm:
+ if (cmd == IOC_MDC_GETFILEINFO || cmd == LL_IOC_MDC_GETINFO) {
+ struct lov_user_mds_data *lmdp;
+ lstat_t st = { 0 };
+
+ st.st_dev = inode->i_sb->s_dev;
+ st.st_mode = body->mode;
+ st.st_nlink = body->nlink;
+ st.st_uid = body->uid;
+ st.st_gid = body->gid;
+ st.st_rdev = body->rdev;
+ st.st_size = body->size;
+ st.st_blksize = PAGE_CACHE_SIZE;
+ st.st_blocks = body->blocks;
+ st.st_atime = body->atime;
+ st.st_mtime = body->mtime;
+ st.st_ctime = body->ctime;
+ st.st_ino = inode->i_ino;
+
+ lmdp = (struct lov_user_mds_data *)arg;
+ if (copy_to_user(&lmdp->lmd_st, &st, sizeof(st))) {
+ rc = -EFAULT;
+ goto out_req;
+ }
+ }
+
+out_req:
+ ptlrpc_req_finished(request);
+ if (filename)
+ ll_putname(filename);
+ return rc;
+ }
+ case IOC_LOV_GETINFO: {
+ struct lov_user_mds_data *lumd;
+ struct lov_stripe_md *lsm;
+ struct lov_user_md *lum;
+ struct lov_mds_md *lmm;
+ int lmmsize;
+ lstat_t st;
+
+ lumd = (struct lov_user_mds_data *)arg;
+ lum = &lumd->lmd_lmm;
+
+ rc = ll_get_max_mdsize(sbi, &lmmsize);
+ if (rc)
+ return rc;
+
+ OBD_ALLOC_LARGE(lmm, lmmsize);
+ if (lmm == NULL)
+ return -ENOMEM;
+ if (copy_from_user(lmm, lum, lmmsize)) {
+ rc = -EFAULT;
+ goto free_lmm;
+ }
+
+ switch (lmm->lmm_magic) {
+ case LOV_USER_MAGIC_V1:
+ if (LOV_USER_MAGIC_V1 == cpu_to_le32(LOV_USER_MAGIC_V1))
+ break;
+ /* swab objects first so that stripes num will be sane */
+ lustre_swab_lov_user_md_objects(
+ ((struct lov_user_md_v1 *)lmm)->lmm_objects,
+ ((struct lov_user_md_v1 *)lmm)->lmm_stripe_count);
+ lustre_swab_lov_user_md_v1((struct lov_user_md_v1 *)lmm);
+ break;
+ case LOV_USER_MAGIC_V3:
+ if (LOV_USER_MAGIC_V3 == cpu_to_le32(LOV_USER_MAGIC_V3))
+ break;
+ /* swab objects first so that stripes num will be sane */
+ lustre_swab_lov_user_md_objects(
+ ((struct lov_user_md_v3 *)lmm)->lmm_objects,
+ ((struct lov_user_md_v3 *)lmm)->lmm_stripe_count);
+ lustre_swab_lov_user_md_v3((struct lov_user_md_v3 *)lmm);
+ break;
+ default:
+ rc = -EINVAL;
+ goto free_lmm;
+ }
+
+ rc = obd_unpackmd(sbi->ll_dt_exp, &lsm, lmm, lmmsize);
+ if (rc < 0) {
+ rc = -ENOMEM;
+ goto free_lmm;
+ }
+
+ /* Perform glimpse_size operation. */
+ memset(&st, 0, sizeof(st));
+
+ rc = ll_glimpse_ioctl(sbi, lsm, &st);
+ if (rc)
+ goto free_lsm;
+
+ if (copy_to_user(&lumd->lmd_st, &st, sizeof(st))) {
+ rc = -EFAULT;
+ goto free_lsm;
+ }
+
+free_lsm:
+ obd_free_memmd(sbi->ll_dt_exp, &lsm);
+free_lmm:
+ OBD_FREE_LARGE(lmm, lmmsize);
+ return rc;
+ }
+ case OBD_IOC_LLOG_CATINFO: {
+ return -EOPNOTSUPP;
+ }
+ case OBD_IOC_QUOTACHECK: {
+ struct obd_quotactl *oqctl;
+ int error = 0;
+
+ if (!capable(CFS_CAP_SYS_ADMIN) ||
+ sbi->ll_flags & LL_SBI_RMT_CLIENT)
+ return -EPERM;
+
+ oqctl = kzalloc(sizeof(*oqctl), GFP_NOFS);
+ if (!oqctl)
+ return -ENOMEM;
+ oqctl->qc_type = arg;
+ rc = obd_quotacheck(sbi->ll_md_exp, oqctl);
+ if (rc < 0) {
+ CDEBUG(D_INFO, "md_quotacheck failed: rc %d\n", rc);
+ error = rc;
+ }
+
+ rc = obd_quotacheck(sbi->ll_dt_exp, oqctl);
+ if (rc < 0)
+ CDEBUG(D_INFO, "obd_quotacheck failed: rc %d\n", rc);
+
+ OBD_FREE_PTR(oqctl);
+ return error ?: rc;
+ }
+ case OBD_IOC_POLL_QUOTACHECK: {
+ struct if_quotacheck *check;
+
+ if (!capable(CFS_CAP_SYS_ADMIN) ||
+ sbi->ll_flags & LL_SBI_RMT_CLIENT)
+ return -EPERM;
+
+ check = kzalloc(sizeof(*check), GFP_NOFS);
+ if (!check)
+ return -ENOMEM;
+
+ rc = obd_iocontrol(cmd, sbi->ll_md_exp, 0, (void *)check,
+ NULL);
+ if (rc) {
+ CDEBUG(D_QUOTA, "mdc ioctl %d failed: %d\n", cmd, rc);
+ if (copy_to_user((void *)arg, check,
+ sizeof(*check)))
+ CDEBUG(D_QUOTA, "copy_to_user failed\n");
+ goto out_poll;
+ }
+
+ rc = obd_iocontrol(cmd, sbi->ll_dt_exp, 0, (void *)check,
+ NULL);
+ if (rc) {
+ CDEBUG(D_QUOTA, "osc ioctl %d failed: %d\n", cmd, rc);
+ if (copy_to_user((void *)arg, check,
+ sizeof(*check)))
+ CDEBUG(D_QUOTA, "copy_to_user failed\n");
+ goto out_poll;
+ }
+out_poll:
+ OBD_FREE_PTR(check);
+ return rc;
+ }
+ case LL_IOC_QUOTACTL: {
+ struct if_quotactl *qctl;
+
+ qctl = kzalloc(sizeof(*qctl), GFP_NOFS);
+ if (!qctl)
+ return -ENOMEM;
+
+ if (copy_from_user(qctl, (void *)arg, sizeof(*qctl))) {
+ rc = -EFAULT;
+ goto out_quotactl;
+ }
+
+ rc = quotactl_ioctl(sbi, qctl);
+
+ if (rc == 0 && copy_to_user((void *)arg, qctl, sizeof(*qctl)))
+ rc = -EFAULT;
+
+out_quotactl:
+ OBD_FREE_PTR(qctl);
+ return rc;
+ }
+ case OBD_IOC_GETDTNAME:
+ case OBD_IOC_GETMDNAME:
+ return ll_get_obd_name(inode, cmd, arg);
+ case LL_IOC_FLUSHCTX:
+ return ll_flush_ctx(inode);
+#ifdef CONFIG_FS_POSIX_ACL
+ case LL_IOC_RMTACL: {
+ if (sbi->ll_flags & LL_SBI_RMT_CLIENT && is_root_inode(inode)) {
+ struct ll_file_data *fd = LUSTRE_FPRIVATE(file);
+
+ LASSERT(fd != NULL);
+ rc = rct_add(&sbi->ll_rct, current_pid(), arg);
+ if (!rc)
+ fd->fd_flags |= LL_FILE_RMTACL;
+ return rc;
+ } else
+ return 0;
+ }
+#endif
+ case LL_IOC_GETOBDCOUNT: {
+ int count, vallen;
+ struct obd_export *exp;
+
+ if (copy_from_user(&count, (int *)arg, sizeof(int)))
+ return -EFAULT;
+
+ /* get ost count when count is zero, get mdt count otherwise */
+ exp = count ? sbi->ll_md_exp : sbi->ll_dt_exp;
+ vallen = sizeof(count);
+ rc = obd_get_info(NULL, exp, sizeof(KEY_TGT_COUNT),
+ KEY_TGT_COUNT, &vallen, &count, NULL);
+ if (rc) {
+ CERROR("get target count failed: %d\n", rc);
+ return rc;
+ }
+
+ if (copy_to_user((int *)arg, &count, sizeof(int)))
+ return -EFAULT;
+
+ return 0;
+ }
+ case LL_IOC_PATH2FID:
+ if (copy_to_user((void *)arg, ll_inode2fid(inode),
+ sizeof(struct lu_fid)))
+ return -EFAULT;
+ return 0;
+ case LL_IOC_GET_CONNECT_FLAGS: {
+ return obd_iocontrol(cmd, sbi->ll_md_exp, 0, NULL, (void *)arg);
+ }
+ case OBD_IOC_CHANGELOG_SEND:
+ case OBD_IOC_CHANGELOG_CLEAR:
+ rc = copy_and_ioctl(cmd, sbi->ll_md_exp, (void *)arg,
+ sizeof(struct ioc_changelog));
+ return rc;
+ case OBD_IOC_FID2PATH:
+ return ll_fid2path(inode, (void *)arg);
+ case LL_IOC_HSM_REQUEST: {
+ struct hsm_user_request *hur;
+ ssize_t totalsize;
+
+ hur = kzalloc(sizeof(*hur), GFP_NOFS);
+ if (!hur)
+ return -ENOMEM;
+
+ /* We don't know the true size yet; copy the fixed-size part */
+ if (copy_from_user(hur, (void *)arg, sizeof(*hur))) {
+ OBD_FREE_PTR(hur);
+ return -EFAULT;
+ }
+
+ /* Compute the whole struct size */
+ totalsize = hur_len(hur);
+ OBD_FREE_PTR(hur);
+ if (totalsize < 0)
+ return -E2BIG;
+
+ /* Final size will be more than double totalsize */
+ if (totalsize >= MDS_MAXREQSIZE / 3)
+ return -E2BIG;
+
+ OBD_ALLOC_LARGE(hur, totalsize);
+ if (hur == NULL)
+ return -ENOMEM;
+
+ /* Copy the whole struct */
+ if (copy_from_user(hur, (void *)arg, totalsize)) {
+ OBD_FREE_LARGE(hur, totalsize);
+ return -EFAULT;
+ }
+
+ if (hur->hur_request.hr_action == HUA_RELEASE) {
+ const struct lu_fid *fid;
+ struct inode *f;
+ int i;
+
+ for (i = 0; i < hur->hur_request.hr_itemcount; i++) {
+ fid = &hur->hur_user_item[i].hui_fid;
+ f = search_inode_for_lustre(inode->i_sb, fid);
+ if (IS_ERR(f)) {
+ rc = PTR_ERR(f);
+ break;
+ }
+
+ rc = ll_hsm_release(f);
+ iput(f);
+ if (rc != 0)
+ break;
+ }
+ } else {
+ rc = obd_iocontrol(cmd, ll_i2mdexp(inode), totalsize,
+ hur, NULL);
+ }
+
+ OBD_FREE_LARGE(hur, totalsize);
+
+ return rc;
+ }
+ case LL_IOC_HSM_PROGRESS: {
+ struct hsm_progress_kernel hpk;
+ struct hsm_progress hp;
+
+ if (copy_from_user(&hp, (void *)arg, sizeof(hp)))
+ return -EFAULT;
+
+ hpk.hpk_fid = hp.hp_fid;
+ hpk.hpk_cookie = hp.hp_cookie;
+ hpk.hpk_extent = hp.hp_extent;
+ hpk.hpk_flags = hp.hp_flags;
+ hpk.hpk_errval = hp.hp_errval;
+ hpk.hpk_data_version = 0;
+
+ /* File may not exist in Lustre; all progress
+ * reported to Lustre root */
+ rc = obd_iocontrol(cmd, sbi->ll_md_exp, sizeof(hpk), &hpk,
+ NULL);
+ return rc;
+ }
+ case LL_IOC_HSM_CT_START:
+ rc = copy_and_ioctl(cmd, sbi->ll_md_exp, (void *)arg,
+ sizeof(struct lustre_kernelcomm));
+ return rc;
+
+ case LL_IOC_HSM_COPY_START: {
+ struct hsm_copy *copy;
+ int rc;
+
+ copy = kzalloc(sizeof(*copy), GFP_NOFS);
+ if (!copy)
+ return -ENOMEM;
+ if (copy_from_user(copy, (char *)arg, sizeof(*copy))) {
+ OBD_FREE_PTR(copy);
+ return -EFAULT;
+ }
+
+ rc = ll_ioc_copy_start(inode->i_sb, copy);
+ if (copy_to_user((char *)arg, copy, sizeof(*copy)))
+ rc = -EFAULT;
+
+ OBD_FREE_PTR(copy);
+ return rc;
+ }
+ case LL_IOC_HSM_COPY_END: {
+ struct hsm_copy *copy;
+ int rc;
+
+ copy = kzalloc(sizeof(*copy), GFP_NOFS);
+ if (!copy)
+ return -ENOMEM;
+ if (copy_from_user(copy, (char *)arg, sizeof(*copy))) {
+ OBD_FREE_PTR(copy);
+ return -EFAULT;
+ }
+
+ rc = ll_ioc_copy_end(inode->i_sb, copy);
+ if (copy_to_user((char *)arg, copy, sizeof(*copy)))
+ rc = -EFAULT;
+
+ OBD_FREE_PTR(copy);
+ return rc;
+ }
+ default:
+ return obd_iocontrol(cmd, sbi->ll_dt_exp, 0, NULL, (void *)arg);
+ }
+}
+
+static loff_t ll_dir_seek(struct file *file, loff_t offset, int origin)
+{
+ struct inode *inode = file->f_mapping->host;
+ struct ll_file_data *fd = LUSTRE_FPRIVATE(file);
+ struct ll_sb_info *sbi = ll_i2sbi(inode);
+ int api32 = ll_need_32bit_api(sbi);
+ loff_t ret = -EINVAL;
+
+ mutex_lock(&inode->i_mutex);
+ switch (origin) {
+ case SEEK_SET:
+ break;
+ case SEEK_CUR:
+ offset += file->f_pos;
+ break;
+ case SEEK_END:
+ if (offset > 0)
+ goto out;
+ if (api32)
+ offset += LL_DIR_END_OFF_32BIT;
+ else
+ offset += LL_DIR_END_OFF;
+ break;
+ default:
+ goto out;
+ }
+
+ if (offset >= 0 &&
+ ((api32 && offset <= LL_DIR_END_OFF_32BIT) ||
+ (!api32 && offset <= LL_DIR_END_OFF))) {
+ if (offset != file->f_pos) {
+ if ((api32 && offset == LL_DIR_END_OFF_32BIT) ||
+ (!api32 && offset == LL_DIR_END_OFF))
+ fd->lfd_pos = MDS_DIR_END_OFF;
+ else if (api32 && sbi->ll_flags & LL_SBI_64BIT_HASH)
+ fd->lfd_pos = offset << 32;
+ else
+ fd->lfd_pos = offset;
+ file->f_pos = offset;
+ file->f_version = 0;
+ }
+ ret = offset;
+ }
+ goto out;
+
+out:
+ mutex_unlock(&inode->i_mutex);
+ return ret;
+}
+
+static int ll_dir_open(struct inode *inode, struct file *file)
+{
+ return ll_file_open(inode, file);
+}
+
+static int ll_dir_release(struct inode *inode, struct file *file)
+{
+ return ll_file_release(inode, file);
+}
+
+const struct file_operations ll_dir_operations = {
+ .llseek = ll_dir_seek,
+ .open = ll_dir_open,
+ .release = ll_dir_release,
+ .read = generic_read_dir,
+ .iterate = ll_readdir,
+ .unlocked_ioctl = ll_dir_ioctl,
+ .fsync = ll_fsync,
+};
diff --git a/drivers/staging/lustre/lustre/llite/file.c b/drivers/staging/lustre/lustre/llite/file.c
new file mode 100644
index 000000000..4b44c634f
--- /dev/null
+++ b/drivers/staging/lustre/lustre/llite/file.c
@@ -0,0 +1,3624 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2002, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2011, 2012, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ *
+ * lustre/llite/file.c
+ *
+ * Author: Peter Braam <braam@clusterfs.com>
+ * Author: Phil Schwan <phil@clusterfs.com>
+ * Author: Andreas Dilger <adilger@clusterfs.com>
+ */
+
+#define DEBUG_SUBSYSTEM S_LLITE
+#include "../include/lustre_dlm.h"
+#include "../include/lustre_lite.h"
+#include <linux/pagemap.h>
+#include <linux/file.h>
+#include "llite_internal.h"
+#include "../include/lustre/ll_fiemap.h"
+
+#include "../include/cl_object.h"
+
+static int
+ll_put_grouplock(struct inode *inode, struct file *file, unsigned long arg);
+
+static int ll_lease_close(struct obd_client_handle *och, struct inode *inode,
+ bool *lease_broken);
+
+static enum llioc_iter
+ll_iocontrol_call(struct inode *inode, struct file *file,
+ unsigned int cmd, unsigned long arg, int *rcp);
+
+static struct ll_file_data *ll_file_data_get(void)
+{
+ struct ll_file_data *fd;
+
+ OBD_SLAB_ALLOC_PTR_GFP(fd, ll_file_data_slab, GFP_NOFS);
+ if (fd == NULL)
+ return NULL;
+ fd->fd_write_failed = false;
+ return fd;
+}
+
+static void ll_file_data_put(struct ll_file_data *fd)
+{
+ if (fd != NULL)
+ OBD_SLAB_FREE_PTR(fd, ll_file_data_slab);
+}
+
+void ll_pack_inode2opdata(struct inode *inode, struct md_op_data *op_data,
+ struct lustre_handle *fh)
+{
+ op_data->op_fid1 = ll_i2info(inode)->lli_fid;
+ op_data->op_attr.ia_mode = inode->i_mode;
+ op_data->op_attr.ia_atime = inode->i_atime;
+ op_data->op_attr.ia_mtime = inode->i_mtime;
+ op_data->op_attr.ia_ctime = inode->i_ctime;
+ op_data->op_attr.ia_size = i_size_read(inode);
+ op_data->op_attr_blocks = inode->i_blocks;
+ ((struct ll_iattr *)&op_data->op_attr)->ia_attr_flags =
+ ll_inode_to_ext_flags(inode->i_flags);
+ op_data->op_ioepoch = ll_i2info(inode)->lli_ioepoch;
+ if (fh)
+ op_data->op_handle = *fh;
+ op_data->op_capa1 = ll_mdscapa_get(inode);
+
+ if (LLIF_DATA_MODIFIED & ll_i2info(inode)->lli_flags)
+ op_data->op_bias |= MDS_DATA_MODIFIED;
+}
+
+/**
+ * Closes the IO epoch and packs all the attributes into @op_data for
+ * the CLOSE rpc.
+ */
+static void ll_prepare_close(struct inode *inode, struct md_op_data *op_data,
+ struct obd_client_handle *och)
+{
+ op_data->op_attr.ia_valid = ATTR_MODE | ATTR_ATIME | ATTR_ATIME_SET |
+ ATTR_MTIME | ATTR_MTIME_SET |
+ ATTR_CTIME | ATTR_CTIME_SET;
+
+ if (!(och->och_flags & FMODE_WRITE))
+ goto out;
+
+ if (!exp_connect_som(ll_i2mdexp(inode)) || !S_ISREG(inode->i_mode))
+ op_data->op_attr.ia_valid |= ATTR_SIZE | ATTR_BLOCKS;
+ else
+ ll_ioepoch_close(inode, op_data, &och, 0);
+
+out:
+ ll_pack_inode2opdata(inode, op_data, &och->och_fh);
+ ll_prep_md_op_data(op_data, inode, NULL, NULL,
+ 0, 0, LUSTRE_OPC_ANY, NULL);
+}
+
+static int ll_close_inode_openhandle(struct obd_export *md_exp,
+ struct inode *inode,
+ struct obd_client_handle *och,
+ const __u64 *data_version)
+{
+ struct obd_export *exp = ll_i2mdexp(inode);
+ struct md_op_data *op_data;
+ struct ptlrpc_request *req = NULL;
+ struct obd_device *obd = class_exp2obd(exp);
+ int epoch_close = 1;
+ int rc;
+
+ if (obd == NULL) {
+ /*
+ * XXX: in case of LMV, is this correct to access
+ * ->exp_handle?
+ */
+ CERROR("Invalid MDC connection handle %#llx\n",
+ ll_i2mdexp(inode)->exp_handle.h_cookie);
+ rc = 0;
+ goto out;
+ }
+
+ op_data = kzalloc(sizeof(*op_data), GFP_NOFS);
+ if (!op_data) {
+ /* XXX We leak openhandle and request here. */
+ rc = -ENOMEM;
+ goto out;
+ }
+
+ ll_prepare_close(inode, op_data, och);
+ if (data_version != NULL) {
+ /* Pass in data_version implies release. */
+ op_data->op_bias |= MDS_HSM_RELEASE;
+ op_data->op_data_version = *data_version;
+ op_data->op_lease_handle = och->och_lease_handle;
+ op_data->op_attr.ia_valid |= ATTR_SIZE | ATTR_BLOCKS;
+ }
+ epoch_close = op_data->op_flags & MF_EPOCH_CLOSE;
+ rc = md_close(md_exp, op_data, och->och_mod, &req);
+ if (rc == -EAGAIN) {
+ /* This close must have the epoch closed. */
+ LASSERT(epoch_close);
+ /* MDS has instructed us to obtain Size-on-MDS attribute from
+ * OSTs and send setattr to back to MDS. */
+ rc = ll_som_update(inode, op_data);
+ if (rc) {
+ CERROR("inode %lu mdc Size-on-MDS update failed: rc = %d\n",
+ inode->i_ino, rc);
+ rc = 0;
+ }
+ } else if (rc) {
+ CERROR("inode %lu mdc close failed: rc = %d\n",
+ inode->i_ino, rc);
+ }
+
+ /* DATA_MODIFIED flag was successfully sent on close, cancel data
+ * modification flag. */
+ if (rc == 0 && (op_data->op_bias & MDS_DATA_MODIFIED)) {
+ struct ll_inode_info *lli = ll_i2info(inode);
+
+ spin_lock(&lli->lli_lock);
+ lli->lli_flags &= ~LLIF_DATA_MODIFIED;
+ spin_unlock(&lli->lli_lock);
+ }
+
+ if (rc == 0) {
+ rc = ll_objects_destroy(req, inode);
+ if (rc)
+ CERROR("inode %lu ll_objects destroy: rc = %d\n",
+ inode->i_ino, rc);
+ }
+ if (rc == 0 && op_data->op_bias & MDS_HSM_RELEASE) {
+ struct mdt_body *body;
+
+ body = req_capsule_server_get(&req->rq_pill, &RMF_MDT_BODY);
+ if (!(body->valid & OBD_MD_FLRELEASED))
+ rc = -EBUSY;
+ }
+
+ ll_finish_md_op_data(op_data);
+
+out:
+ if (exp_connect_som(exp) && !epoch_close &&
+ S_ISREG(inode->i_mode) && (och->och_flags & FMODE_WRITE)) {
+ ll_queue_done_writing(inode, LLIF_DONE_WRITING);
+ } else {
+ md_clear_open_replay_data(md_exp, och);
+ /* Free @och if it is not waiting for DONE_WRITING. */
+ och->och_fh.cookie = DEAD_HANDLE_MAGIC;
+ OBD_FREE_PTR(och);
+ }
+ if (req) /* This is close request */
+ ptlrpc_req_finished(req);
+ return rc;
+}
+
+int ll_md_real_close(struct inode *inode, fmode_t fmode)
+{
+ struct ll_inode_info *lli = ll_i2info(inode);
+ struct obd_client_handle **och_p;
+ struct obd_client_handle *och;
+ __u64 *och_usecount;
+ int rc = 0;
+
+ if (fmode & FMODE_WRITE) {
+ och_p = &lli->lli_mds_write_och;
+ och_usecount = &lli->lli_open_fd_write_count;
+ } else if (fmode & FMODE_EXEC) {
+ och_p = &lli->lli_mds_exec_och;
+ och_usecount = &lli->lli_open_fd_exec_count;
+ } else {
+ LASSERT(fmode & FMODE_READ);
+ och_p = &lli->lli_mds_read_och;
+ och_usecount = &lli->lli_open_fd_read_count;
+ }
+
+ mutex_lock(&lli->lli_och_mutex);
+ if (*och_usecount > 0) {
+ /* There are still users of this handle, so skip
+ * freeing it. */
+ mutex_unlock(&lli->lli_och_mutex);
+ return 0;
+ }
+
+ och = *och_p;
+ *och_p = NULL;
+ mutex_unlock(&lli->lli_och_mutex);
+
+ if (och != NULL) {
+ /* There might be a race and this handle may already
+ be closed. */
+ rc = ll_close_inode_openhandle(ll_i2sbi(inode)->ll_md_exp,
+ inode, och, NULL);
+ }
+
+ return rc;
+}
+
+static int ll_md_close(struct obd_export *md_exp, struct inode *inode,
+ struct file *file)
+{
+ struct ll_file_data *fd = LUSTRE_FPRIVATE(file);
+ struct ll_inode_info *lli = ll_i2info(inode);
+ int lockmode;
+ __u64 flags = LDLM_FL_BLOCK_GRANTED | LDLM_FL_TEST_LOCK;
+ struct lustre_handle lockh;
+ ldlm_policy_data_t policy = {.l_inodebits = {MDS_INODELOCK_OPEN}};
+ int rc = 0;
+
+ /* clear group lock, if present */
+ if (unlikely(fd->fd_flags & LL_FILE_GROUP_LOCKED))
+ ll_put_grouplock(inode, file, fd->fd_grouplock.cg_gid);
+
+ if (fd->fd_lease_och != NULL) {
+ bool lease_broken;
+
+ /* Usually the lease is not released when the
+ * application crashed, we need to release here. */
+ rc = ll_lease_close(fd->fd_lease_och, inode, &lease_broken);
+ CDEBUG(rc ? D_ERROR : D_INODE, "Clean up lease "DFID" %d/%d\n",
+ PFID(&lli->lli_fid), rc, lease_broken);
+
+ fd->fd_lease_och = NULL;
+ }
+
+ if (fd->fd_och != NULL) {
+ rc = ll_close_inode_openhandle(md_exp, inode, fd->fd_och, NULL);
+ fd->fd_och = NULL;
+ goto out;
+ }
+
+ /* Let's see if we have good enough OPEN lock on the file and if
+ we can skip talking to MDS */
+
+ mutex_lock(&lli->lli_och_mutex);
+ if (fd->fd_omode & FMODE_WRITE) {
+ lockmode = LCK_CW;
+ LASSERT(lli->lli_open_fd_write_count);
+ lli->lli_open_fd_write_count--;
+ } else if (fd->fd_omode & FMODE_EXEC) {
+ lockmode = LCK_PR;
+ LASSERT(lli->lli_open_fd_exec_count);
+ lli->lli_open_fd_exec_count--;
+ } else {
+ lockmode = LCK_CR;
+ LASSERT(lli->lli_open_fd_read_count);
+ lli->lli_open_fd_read_count--;
+ }
+ mutex_unlock(&lli->lli_och_mutex);
+
+ if (!md_lock_match(md_exp, flags, ll_inode2fid(inode),
+ LDLM_IBITS, &policy, lockmode, &lockh))
+ rc = ll_md_real_close(inode, fd->fd_omode);
+
+out:
+ LUSTRE_FPRIVATE(file) = NULL;
+ ll_file_data_put(fd);
+ ll_capa_close(inode);
+
+ return rc;
+}
+
+/* While this returns an error code, fput() the caller does not, so we need
+ * to make every effort to clean up all of our state here. Also, applications
+ * rarely check close errors and even if an error is returned they will not
+ * re-try the close call.
+ */
+int ll_file_release(struct inode *inode, struct file *file)
+{
+ struct ll_file_data *fd;
+ struct ll_sb_info *sbi = ll_i2sbi(inode);
+ struct ll_inode_info *lli = ll_i2info(inode);
+ int rc;
+
+ CDEBUG(D_VFSTRACE, "VFS Op:inode=%lu/%u(%p)\n", inode->i_ino,
+ inode->i_generation, inode);
+
+#ifdef CONFIG_FS_POSIX_ACL
+ if (sbi->ll_flags & LL_SBI_RMT_CLIENT && is_root_inode(inode)) {
+ struct ll_file_data *fd = LUSTRE_FPRIVATE(file);
+
+ LASSERT(fd != NULL);
+ if (unlikely(fd->fd_flags & LL_FILE_RMTACL)) {
+ fd->fd_flags &= ~LL_FILE_RMTACL;
+ rct_del(&sbi->ll_rct, current_pid());
+ et_search_free(&sbi->ll_et, current_pid());
+ }
+ }
+#endif
+
+ if (!is_root_inode(inode))
+ ll_stats_ops_tally(sbi, LPROC_LL_RELEASE, 1);
+ fd = LUSTRE_FPRIVATE(file);
+ LASSERT(fd != NULL);
+
+ /* The last ref on @file, maybe not the owner pid of statahead.
+ * Different processes can open the same dir, "ll_opendir_key" means:
+ * it is me that should stop the statahead thread. */
+ if (S_ISDIR(inode->i_mode) && lli->lli_opendir_key == fd &&
+ lli->lli_opendir_pid != 0)
+ ll_stop_statahead(inode, lli->lli_opendir_key);
+
+ if (is_root_inode(inode)) {
+ LUSTRE_FPRIVATE(file) = NULL;
+ ll_file_data_put(fd);
+ return 0;
+ }
+
+ if (!S_ISDIR(inode->i_mode)) {
+ lov_read_and_clear_async_rc(lli->lli_clob);
+ lli->lli_async_rc = 0;
+ }
+
+ rc = ll_md_close(sbi->ll_md_exp, inode, file);
+
+ if (CFS_FAIL_TIMEOUT_MS(OBD_FAIL_PTLRPC_DUMP_LOG, cfs_fail_val))
+ libcfs_debug_dumplog();
+
+ return rc;
+}
+
+static int ll_intent_file_open(struct dentry *dentry, void *lmm,
+ int lmmsize, struct lookup_intent *itp)
+{
+ struct inode *inode = d_inode(dentry);
+ struct ll_sb_info *sbi = ll_i2sbi(inode);
+ struct dentry *parent = dentry->d_parent;
+ const char *name = dentry->d_name.name;
+ const int len = dentry->d_name.len;
+ struct md_op_data *op_data;
+ struct ptlrpc_request *req;
+ __u32 opc = LUSTRE_OPC_ANY;
+ int rc;
+
+ /* Usually we come here only for NFSD, and we want open lock.
+ But we can also get here with pre 2.6.15 patchless kernels, and in
+ that case that lock is also ok */
+ /* We can also get here if there was cached open handle in revalidate_it
+ * but it disappeared while we were getting from there to ll_file_open.
+ * But this means this file was closed and immediately opened which
+ * makes a good candidate for using OPEN lock */
+ /* If lmmsize & lmm are not 0, we are just setting stripe info
+ * parameters. No need for the open lock */
+ if (lmm == NULL && lmmsize == 0) {
+ itp->it_flags |= MDS_OPEN_LOCK;
+ if (itp->it_flags & FMODE_WRITE)
+ opc = LUSTRE_OPC_CREATE;
+ }
+
+ op_data = ll_prep_md_op_data(NULL, d_inode(parent),
+ inode, name, len,
+ O_RDWR, opc, NULL);
+ if (IS_ERR(op_data))
+ return PTR_ERR(op_data);
+
+ itp->it_flags |= MDS_OPEN_BY_FID;
+ rc = md_intent_lock(sbi->ll_md_exp, op_data, lmm, lmmsize, itp,
+ 0 /*unused */, &req, ll_md_blocking_ast, 0);
+ ll_finish_md_op_data(op_data);
+ if (rc == -ESTALE) {
+ /* reason for keep own exit path - don`t flood log
+ * with messages with -ESTALE errors.
+ */
+ if (!it_disposition(itp, DISP_OPEN_OPEN) ||
+ it_open_error(DISP_OPEN_OPEN, itp))
+ goto out;
+ ll_release_openhandle(inode, itp);
+ goto out;
+ }
+
+ if (it_disposition(itp, DISP_LOOKUP_NEG)) {
+ rc = -ENOENT;
+ goto out;
+ }
+
+ if (rc != 0 || it_open_error(DISP_OPEN_OPEN, itp)) {
+ rc = rc ? rc : it_open_error(DISP_OPEN_OPEN, itp);
+ CDEBUG(D_VFSTRACE, "lock enqueue: err: %d\n", rc);
+ goto out;
+ }
+
+ rc = ll_prep_inode(&inode, req, NULL, itp);
+ if (!rc && itp->d.lustre.it_lock_mode)
+ ll_set_lock_data(sbi->ll_md_exp, inode, itp, NULL);
+
+out:
+ ptlrpc_req_finished(req);
+ ll_intent_drop_lock(itp);
+
+ return rc;
+}
+
+/**
+ * Assign an obtained @ioepoch to client's inode. No lock is needed, MDS does
+ * not believe attributes if a few ioepoch holders exist. Attributes for
+ * previous ioepoch if new one is opened are also skipped by MDS.
+ */
+void ll_ioepoch_open(struct ll_inode_info *lli, __u64 ioepoch)
+{
+ if (ioepoch && lli->lli_ioepoch != ioepoch) {
+ lli->lli_ioepoch = ioepoch;
+ CDEBUG(D_INODE, "Epoch %llu opened on "DFID"\n",
+ ioepoch, PFID(&lli->lli_fid));
+ }
+}
+
+static int ll_och_fill(struct obd_export *md_exp, struct lookup_intent *it,
+ struct obd_client_handle *och)
+{
+ struct ptlrpc_request *req = it->d.lustre.it_data;
+ struct mdt_body *body;
+
+ body = req_capsule_server_get(&req->rq_pill, &RMF_MDT_BODY);
+ och->och_fh = body->handle;
+ och->och_fid = body->fid1;
+ och->och_lease_handle.cookie = it->d.lustre.it_lock_handle;
+ och->och_magic = OBD_CLIENT_HANDLE_MAGIC;
+ och->och_flags = it->it_flags;
+
+ return md_set_open_replay_data(md_exp, och, it);
+}
+
+static int ll_local_open(struct file *file, struct lookup_intent *it,
+ struct ll_file_data *fd, struct obd_client_handle *och)
+{
+ struct inode *inode = file_inode(file);
+ struct ll_inode_info *lli = ll_i2info(inode);
+
+ LASSERT(!LUSTRE_FPRIVATE(file));
+
+ LASSERT(fd != NULL);
+
+ if (och) {
+ struct ptlrpc_request *req = it->d.lustre.it_data;
+ struct mdt_body *body;
+ int rc;
+
+ rc = ll_och_fill(ll_i2sbi(inode)->ll_md_exp, it, och);
+ if (rc != 0)
+ return rc;
+
+ body = req_capsule_server_get(&req->rq_pill, &RMF_MDT_BODY);
+ ll_ioepoch_open(lli, body->ioepoch);
+ }
+
+ LUSTRE_FPRIVATE(file) = fd;
+ ll_readahead_init(inode, &fd->fd_ras);
+ fd->fd_omode = it->it_flags & (FMODE_READ | FMODE_WRITE | FMODE_EXEC);
+ return 0;
+}
+
+/* Open a file, and (for the very first open) create objects on the OSTs at
+ * this time. If opened with O_LOV_DELAY_CREATE, then we don't do the object
+ * creation or open until ll_lov_setstripe() ioctl is called.
+ *
+ * If we already have the stripe MD locally then we don't request it in
+ * md_open(), by passing a lmm_size = 0.
+ *
+ * It is up to the application to ensure no other processes open this file
+ * in the O_LOV_DELAY_CREATE case, or the default striping pattern will be
+ * used. We might be able to avoid races of that sort by getting lli_open_sem
+ * before returning in the O_LOV_DELAY_CREATE case and dropping it here
+ * or in ll_file_release(), but I'm not sure that is desirable/necessary.
+ */
+int ll_file_open(struct inode *inode, struct file *file)
+{
+ struct ll_inode_info *lli = ll_i2info(inode);
+ struct lookup_intent *it, oit = { .it_op = IT_OPEN,
+ .it_flags = file->f_flags };
+ struct obd_client_handle **och_p = NULL;
+ __u64 *och_usecount = NULL;
+ struct ll_file_data *fd;
+ int rc = 0, opendir_set = 0;
+
+ CDEBUG(D_VFSTRACE, "VFS Op:inode=%lu/%u(%p), flags %o\n", inode->i_ino,
+ inode->i_generation, inode, file->f_flags);
+
+ it = file->private_data; /* XXX: compat macro */
+ file->private_data = NULL; /* prevent ll_local_open assertion */
+
+ fd = ll_file_data_get();
+ if (fd == NULL) {
+ rc = -ENOMEM;
+ goto out_openerr;
+ }
+
+ fd->fd_file = file;
+ if (S_ISDIR(inode->i_mode)) {
+ spin_lock(&lli->lli_sa_lock);
+ if (lli->lli_opendir_key == NULL && lli->lli_sai == NULL &&
+ lli->lli_opendir_pid == 0) {
+ lli->lli_opendir_key = fd;
+ lli->lli_opendir_pid = current_pid();
+ opendir_set = 1;
+ }
+ spin_unlock(&lli->lli_sa_lock);
+ }
+
+ if (is_root_inode(inode)) {
+ LUSTRE_FPRIVATE(file) = fd;
+ return 0;
+ }
+
+ if (!it || !it->d.lustre.it_disposition) {
+ /* Convert f_flags into access mode. We cannot use file->f_mode,
+ * because everything but O_ACCMODE mask was stripped from
+ * there */
+ if ((oit.it_flags + 1) & O_ACCMODE)
+ oit.it_flags++;
+ if (file->f_flags & O_TRUNC)
+ oit.it_flags |= FMODE_WRITE;
+
+ /* kernel only call f_op->open in dentry_open. filp_open calls
+ * dentry_open after call to open_namei that checks permissions.
+ * Only nfsd_open call dentry_open directly without checking
+ * permissions and because of that this code below is safe. */
+ if (oit.it_flags & (FMODE_WRITE | FMODE_READ))
+ oit.it_flags |= MDS_OPEN_OWNEROVERRIDE;
+
+ /* We do not want O_EXCL here, presumably we opened the file
+ * already? XXX - NFS implications? */
+ oit.it_flags &= ~O_EXCL;
+
+ /* bug20584, if "it_flags" contains O_CREAT, the file will be
+ * created if necessary, then "IT_CREAT" should be set to keep
+ * consistent with it */
+ if (oit.it_flags & O_CREAT)
+ oit.it_op |= IT_CREAT;
+
+ it = &oit;
+ }
+
+restart:
+ /* Let's see if we have file open on MDS already. */
+ if (it->it_flags & FMODE_WRITE) {
+ och_p = &lli->lli_mds_write_och;
+ och_usecount = &lli->lli_open_fd_write_count;
+ } else if (it->it_flags & FMODE_EXEC) {
+ och_p = &lli->lli_mds_exec_och;
+ och_usecount = &lli->lli_open_fd_exec_count;
+ } else {
+ och_p = &lli->lli_mds_read_och;
+ och_usecount = &lli->lli_open_fd_read_count;
+ }
+
+ mutex_lock(&lli->lli_och_mutex);
+ if (*och_p) { /* Open handle is present */
+ if (it_disposition(it, DISP_OPEN_OPEN)) {
+ /* Well, there's extra open request that we do not need,
+ let's close it somehow. This will decref request. */
+ rc = it_open_error(DISP_OPEN_OPEN, it);
+ if (rc) {
+ mutex_unlock(&lli->lli_och_mutex);
+ goto out_openerr;
+ }
+
+ ll_release_openhandle(inode, it);
+ }
+ (*och_usecount)++;
+
+ rc = ll_local_open(file, it, fd, NULL);
+ if (rc) {
+ (*och_usecount)--;
+ mutex_unlock(&lli->lli_och_mutex);
+ goto out_openerr;
+ }
+ } else {
+ LASSERT(*och_usecount == 0);
+ if (!it->d.lustre.it_disposition) {
+ /* We cannot just request lock handle now, new ELC code
+ means that one of other OPEN locks for this file
+ could be cancelled, and since blocking ast handler
+ would attempt to grab och_mutex as well, that would
+ result in a deadlock */
+ mutex_unlock(&lli->lli_och_mutex);
+ it->it_create_mode |= M_CHECK_STALE;
+ rc = ll_intent_file_open(file->f_path.dentry, NULL, 0, it);
+ it->it_create_mode &= ~M_CHECK_STALE;
+ if (rc)
+ goto out_openerr;
+
+ goto restart;
+ }
+ *och_p = kzalloc(sizeof(struct obd_client_handle), GFP_NOFS);
+ if (!*och_p) {
+ rc = -ENOMEM;
+ goto out_och_free;
+ }
+
+ (*och_usecount)++;
+
+ /* md_intent_lock() didn't get a request ref if there was an
+ * open error, so don't do cleanup on the request here
+ * (bug 3430) */
+ /* XXX (green): Should not we bail out on any error here, not
+ * just open error? */
+ rc = it_open_error(DISP_OPEN_OPEN, it);
+ if (rc)
+ goto out_och_free;
+
+ LASSERT(it_disposition(it, DISP_ENQ_OPEN_REF));
+
+ rc = ll_local_open(file, it, fd, *och_p);
+ if (rc)
+ goto out_och_free;
+ }
+ mutex_unlock(&lli->lli_och_mutex);
+ fd = NULL;
+
+ /* Must do this outside lli_och_mutex lock to prevent deadlock where
+ different kind of OPEN lock for this same inode gets cancelled
+ by ldlm_cancel_lru */
+ if (!S_ISREG(inode->i_mode))
+ goto out_och_free;
+
+ ll_capa_open(inode);
+
+ if (!lli->lli_has_smd &&
+ (cl_is_lov_delay_create(file->f_flags) ||
+ (file->f_mode & FMODE_WRITE) == 0)) {
+ CDEBUG(D_INODE, "object creation was delayed\n");
+ goto out_och_free;
+ }
+ cl_lov_delay_create_clear(&file->f_flags);
+ goto out_och_free;
+
+out_och_free:
+ if (rc) {
+ if (och_p && *och_p) {
+ OBD_FREE(*och_p, sizeof(struct obd_client_handle));
+ *och_p = NULL; /* OBD_FREE writes some magic there */
+ (*och_usecount)--;
+ }
+ mutex_unlock(&lli->lli_och_mutex);
+
+out_openerr:
+ if (opendir_set != 0)
+ ll_stop_statahead(inode, lli->lli_opendir_key);
+ if (fd != NULL)
+ ll_file_data_put(fd);
+ } else {
+ ll_stats_ops_tally(ll_i2sbi(inode), LPROC_LL_OPEN, 1);
+ }
+
+ if (it && it_disposition(it, DISP_ENQ_OPEN_REF)) {
+ ptlrpc_req_finished(it->d.lustre.it_data);
+ it_clear_disposition(it, DISP_ENQ_OPEN_REF);
+ }
+
+ return rc;
+}
+
+static int ll_md_blocking_lease_ast(struct ldlm_lock *lock,
+ struct ldlm_lock_desc *desc, void *data, int flag)
+{
+ int rc;
+ struct lustre_handle lockh;
+
+ switch (flag) {
+ case LDLM_CB_BLOCKING:
+ ldlm_lock2handle(lock, &lockh);
+ rc = ldlm_cli_cancel(&lockh, LCF_ASYNC);
+ if (rc < 0) {
+ CDEBUG(D_INODE, "ldlm_cli_cancel: %d\n", rc);
+ return rc;
+ }
+ break;
+ case LDLM_CB_CANCELING:
+ /* do nothing */
+ break;
+ }
+ return 0;
+}
+
+/**
+ * Acquire a lease and open the file.
+ */
+static struct obd_client_handle *
+ll_lease_open(struct inode *inode, struct file *file, fmode_t fmode,
+ __u64 open_flags)
+{
+ struct lookup_intent it = { .it_op = IT_OPEN };
+ struct ll_sb_info *sbi = ll_i2sbi(inode);
+ struct md_op_data *op_data;
+ struct ptlrpc_request *req;
+ struct lustre_handle old_handle = { 0 };
+ struct obd_client_handle *och = NULL;
+ int rc;
+ int rc2;
+
+ if (fmode != FMODE_WRITE && fmode != FMODE_READ)
+ return ERR_PTR(-EINVAL);
+
+ if (file != NULL) {
+ struct ll_inode_info *lli = ll_i2info(inode);
+ struct ll_file_data *fd = LUSTRE_FPRIVATE(file);
+ struct obd_client_handle **och_p;
+ __u64 *och_usecount;
+
+ if (!(fmode & file->f_mode) || (file->f_mode & FMODE_EXEC))
+ return ERR_PTR(-EPERM);
+
+ /* Get the openhandle of the file */
+ rc = -EBUSY;
+ mutex_lock(&lli->lli_och_mutex);
+ if (fd->fd_lease_och != NULL) {
+ mutex_unlock(&lli->lli_och_mutex);
+ return ERR_PTR(rc);
+ }
+
+ if (fd->fd_och == NULL) {
+ if (file->f_mode & FMODE_WRITE) {
+ LASSERT(lli->lli_mds_write_och != NULL);
+ och_p = &lli->lli_mds_write_och;
+ och_usecount = &lli->lli_open_fd_write_count;
+ } else {
+ LASSERT(lli->lli_mds_read_och != NULL);
+ och_p = &lli->lli_mds_read_och;
+ och_usecount = &lli->lli_open_fd_read_count;
+ }
+ if (*och_usecount == 1) {
+ fd->fd_och = *och_p;
+ *och_p = NULL;
+ *och_usecount = 0;
+ rc = 0;
+ }
+ }
+ mutex_unlock(&lli->lli_och_mutex);
+ if (rc < 0) /* more than 1 opener */
+ return ERR_PTR(rc);
+
+ LASSERT(fd->fd_och != NULL);
+ old_handle = fd->fd_och->och_fh;
+ }
+
+ och = kzalloc(sizeof(*och), GFP_NOFS);
+ if (!och)
+ return ERR_PTR(-ENOMEM);
+
+ op_data = ll_prep_md_op_data(NULL, inode, inode, NULL, 0, 0,
+ LUSTRE_OPC_ANY, NULL);
+ if (IS_ERR(op_data)) {
+ rc = PTR_ERR(op_data);
+ goto out;
+ }
+
+ /* To tell the MDT this openhandle is from the same owner */
+ op_data->op_handle = old_handle;
+
+ it.it_flags = fmode | open_flags;
+ it.it_flags |= MDS_OPEN_LOCK | MDS_OPEN_BY_FID | MDS_OPEN_LEASE;
+ rc = md_intent_lock(sbi->ll_md_exp, op_data, NULL, 0, &it, 0, &req,
+ ll_md_blocking_lease_ast,
+ /* LDLM_FL_NO_LRU: To not put the lease lock into LRU list, otherwise
+ * it can be cancelled which may mislead applications that the lease is
+ * broken;
+ * LDLM_FL_EXCL: Set this flag so that it won't be matched by normal
+ * open in ll_md_blocking_ast(). Otherwise as ll_md_blocking_lease_ast
+ * doesn't deal with openhandle, so normal openhandle will be leaked. */
+ LDLM_FL_NO_LRU | LDLM_FL_EXCL);
+ ll_finish_md_op_data(op_data);
+ ptlrpc_req_finished(req);
+ if (rc < 0)
+ goto out_release_it;
+
+ if (it_disposition(&it, DISP_LOOKUP_NEG)) {
+ rc = -ENOENT;
+ goto out_release_it;
+ }
+
+ rc = it_open_error(DISP_OPEN_OPEN, &it);
+ if (rc)
+ goto out_release_it;
+
+ LASSERT(it_disposition(&it, DISP_ENQ_OPEN_REF));
+ ll_och_fill(sbi->ll_md_exp, &it, och);
+
+ if (!it_disposition(&it, DISP_OPEN_LEASE)) /* old server? */ {
+ rc = -EOPNOTSUPP;
+ goto out_close;
+ }
+
+ /* already get lease, handle lease lock */
+ ll_set_lock_data(sbi->ll_md_exp, inode, &it, NULL);
+ if (it.d.lustre.it_lock_mode == 0 ||
+ it.d.lustre.it_lock_bits != MDS_INODELOCK_OPEN) {
+ /* open lock must return for lease */
+ CERROR(DFID "lease granted but no open lock, %d/%llu.\n",
+ PFID(ll_inode2fid(inode)), it.d.lustre.it_lock_mode,
+ it.d.lustre.it_lock_bits);
+ rc = -EPROTO;
+ goto out_close;
+ }
+
+ ll_intent_release(&it);
+ return och;
+
+out_close:
+ rc2 = ll_close_inode_openhandle(sbi->ll_md_exp, inode, och, NULL);
+ if (rc2)
+ CERROR("Close openhandle returned %d\n", rc2);
+
+ /* cancel open lock */
+ if (it.d.lustre.it_lock_mode != 0) {
+ ldlm_lock_decref_and_cancel(&och->och_lease_handle,
+ it.d.lustre.it_lock_mode);
+ it.d.lustre.it_lock_mode = 0;
+ }
+out_release_it:
+ ll_intent_release(&it);
+out:
+ OBD_FREE_PTR(och);
+ return ERR_PTR(rc);
+}
+
+/**
+ * Release lease and close the file.
+ * It will check if the lease has ever broken.
+ */
+static int ll_lease_close(struct obd_client_handle *och, struct inode *inode,
+ bool *lease_broken)
+{
+ struct ldlm_lock *lock;
+ bool cancelled = true;
+ int rc;
+
+ lock = ldlm_handle2lock(&och->och_lease_handle);
+ if (lock != NULL) {
+ lock_res_and_lock(lock);
+ cancelled = ldlm_is_cancel(lock);
+ unlock_res_and_lock(lock);
+ ldlm_lock_put(lock);
+ }
+
+ CDEBUG(D_INODE, "lease for "DFID" broken? %d\n",
+ PFID(&ll_i2info(inode)->lli_fid), cancelled);
+
+ if (!cancelled)
+ ldlm_cli_cancel(&och->och_lease_handle, 0);
+ if (lease_broken != NULL)
+ *lease_broken = cancelled;
+
+ rc = ll_close_inode_openhandle(ll_i2sbi(inode)->ll_md_exp, inode, och,
+ NULL);
+ return rc;
+}
+
+/* Fills the obdo with the attributes for the lsm */
+static int ll_lsm_getattr(struct lov_stripe_md *lsm, struct obd_export *exp,
+ struct obd_capa *capa, struct obdo *obdo,
+ __u64 ioepoch, int sync)
+{
+ struct ptlrpc_request_set *set;
+ struct obd_info oinfo = { { { 0 } } };
+ int rc;
+
+ LASSERT(lsm != NULL);
+
+ oinfo.oi_md = lsm;
+ oinfo.oi_oa = obdo;
+ oinfo.oi_oa->o_oi = lsm->lsm_oi;
+ oinfo.oi_oa->o_mode = S_IFREG;
+ oinfo.oi_oa->o_ioepoch = ioepoch;
+ oinfo.oi_oa->o_valid = OBD_MD_FLID | OBD_MD_FLTYPE |
+ OBD_MD_FLSIZE | OBD_MD_FLBLOCKS |
+ OBD_MD_FLBLKSZ | OBD_MD_FLATIME |
+ OBD_MD_FLMTIME | OBD_MD_FLCTIME |
+ OBD_MD_FLGROUP | OBD_MD_FLEPOCH |
+ OBD_MD_FLDATAVERSION;
+ oinfo.oi_capa = capa;
+ if (sync) {
+ oinfo.oi_oa->o_valid |= OBD_MD_FLFLAGS;
+ oinfo.oi_oa->o_flags |= OBD_FL_SRVLOCK;
+ }
+
+ set = ptlrpc_prep_set();
+ if (set == NULL) {
+ CERROR("can't allocate ptlrpc set\n");
+ rc = -ENOMEM;
+ } else {
+ rc = obd_getattr_async(exp, &oinfo, set);
+ if (rc == 0)
+ rc = ptlrpc_set_wait(set);
+ ptlrpc_set_destroy(set);
+ }
+ if (rc == 0)
+ oinfo.oi_oa->o_valid &= (OBD_MD_FLBLOCKS | OBD_MD_FLBLKSZ |
+ OBD_MD_FLATIME | OBD_MD_FLMTIME |
+ OBD_MD_FLCTIME | OBD_MD_FLSIZE |
+ OBD_MD_FLDATAVERSION);
+ return rc;
+}
+
+/**
+ * Performs the getattr on the inode and updates its fields.
+ * If @sync != 0, perform the getattr under the server-side lock.
+ */
+int ll_inode_getattr(struct inode *inode, struct obdo *obdo,
+ __u64 ioepoch, int sync)
+{
+ struct obd_capa *capa = ll_mdscapa_get(inode);
+ struct lov_stripe_md *lsm;
+ int rc;
+
+ lsm = ccc_inode_lsm_get(inode);
+ rc = ll_lsm_getattr(lsm, ll_i2dtexp(inode),
+ capa, obdo, ioepoch, sync);
+ capa_put(capa);
+ if (rc == 0) {
+ struct ost_id *oi = lsm ? &lsm->lsm_oi : &obdo->o_oi;
+
+ obdo_refresh_inode(inode, obdo, obdo->o_valid);
+ CDEBUG(D_INODE, "objid " DOSTID " size %llu, blocks %llu, blksize %lu\n",
+ POSTID(oi), i_size_read(inode),
+ (unsigned long long)inode->i_blocks,
+ 1UL << inode->i_blkbits);
+ }
+ ccc_inode_lsm_put(inode, lsm);
+ return rc;
+}
+
+int ll_merge_lvb(const struct lu_env *env, struct inode *inode)
+{
+ struct ll_inode_info *lli = ll_i2info(inode);
+ struct cl_object *obj = lli->lli_clob;
+ struct cl_attr *attr = ccc_env_thread_attr(env);
+ struct ost_lvb lvb;
+ int rc = 0;
+
+ ll_inode_size_lock(inode);
+ /* merge timestamps the most recently obtained from mds with
+ timestamps obtained from osts */
+ LTIME_S(inode->i_atime) = lli->lli_lvb.lvb_atime;
+ LTIME_S(inode->i_mtime) = lli->lli_lvb.lvb_mtime;
+ LTIME_S(inode->i_ctime) = lli->lli_lvb.lvb_ctime;
+
+ lvb.lvb_size = i_size_read(inode);
+ lvb.lvb_blocks = inode->i_blocks;
+ lvb.lvb_mtime = LTIME_S(inode->i_mtime);
+ lvb.lvb_atime = LTIME_S(inode->i_atime);
+ lvb.lvb_ctime = LTIME_S(inode->i_ctime);
+
+ cl_object_attr_lock(obj);
+ rc = cl_object_attr_get(env, obj, attr);
+ cl_object_attr_unlock(obj);
+
+ if (rc == 0) {
+ if (lvb.lvb_atime < attr->cat_atime)
+ lvb.lvb_atime = attr->cat_atime;
+ if (lvb.lvb_ctime < attr->cat_ctime)
+ lvb.lvb_ctime = attr->cat_ctime;
+ if (lvb.lvb_mtime < attr->cat_mtime)
+ lvb.lvb_mtime = attr->cat_mtime;
+
+ CDEBUG(D_VFSTRACE, DFID" updating i_size %llu\n",
+ PFID(&lli->lli_fid), attr->cat_size);
+ cl_isize_write_nolock(inode, attr->cat_size);
+
+ inode->i_blocks = attr->cat_blocks;
+
+ LTIME_S(inode->i_mtime) = lvb.lvb_mtime;
+ LTIME_S(inode->i_atime) = lvb.lvb_atime;
+ LTIME_S(inode->i_ctime) = lvb.lvb_ctime;
+ }
+ ll_inode_size_unlock(inode);
+
+ return rc;
+}
+
+int ll_glimpse_ioctl(struct ll_sb_info *sbi, struct lov_stripe_md *lsm,
+ lstat_t *st)
+{
+ struct obdo obdo = { 0 };
+ int rc;
+
+ rc = ll_lsm_getattr(lsm, sbi->ll_dt_exp, NULL, &obdo, 0, 0);
+ if (rc == 0) {
+ st->st_size = obdo.o_size;
+ st->st_blocks = obdo.o_blocks;
+ st->st_mtime = obdo.o_mtime;
+ st->st_atime = obdo.o_atime;
+ st->st_ctime = obdo.o_ctime;
+ }
+ return rc;
+}
+
+static bool file_is_noatime(const struct file *file)
+{
+ const struct vfsmount *mnt = file->f_path.mnt;
+ const struct inode *inode = file_inode(file);
+
+ /* Adapted from file_accessed() and touch_atime().*/
+ if (file->f_flags & O_NOATIME)
+ return true;
+
+ if (inode->i_flags & S_NOATIME)
+ return true;
+
+ if (IS_NOATIME(inode))
+ return true;
+
+ if (mnt->mnt_flags & (MNT_NOATIME | MNT_READONLY))
+ return true;
+
+ if ((mnt->mnt_flags & MNT_NODIRATIME) && S_ISDIR(inode->i_mode))
+ return true;
+
+ if ((inode->i_sb->s_flags & MS_NODIRATIME) && S_ISDIR(inode->i_mode))
+ return true;
+
+ return false;
+}
+
+void ll_io_init(struct cl_io *io, const struct file *file, int write)
+{
+ struct inode *inode = file_inode(file);
+
+ io->u.ci_rw.crw_nonblock = file->f_flags & O_NONBLOCK;
+ if (write) {
+ io->u.ci_wr.wr_append = !!(file->f_flags & O_APPEND);
+ io->u.ci_wr.wr_sync = file->f_flags & O_SYNC ||
+ file->f_flags & O_DIRECT ||
+ IS_SYNC(inode);
+ }
+ io->ci_obj = ll_i2info(inode)->lli_clob;
+ io->ci_lockreq = CILR_MAYBE;
+ if (ll_file_nolock(file)) {
+ io->ci_lockreq = CILR_NEVER;
+ io->ci_no_srvlock = 1;
+ } else if (file->f_flags & O_APPEND) {
+ io->ci_lockreq = CILR_MANDATORY;
+ }
+
+ io->ci_noatime = file_is_noatime(file);
+}
+
+static ssize_t
+ll_file_io_generic(const struct lu_env *env, struct vvp_io_args *args,
+ struct file *file, enum cl_io_type iot,
+ loff_t *ppos, size_t count)
+{
+ struct ll_inode_info *lli = ll_i2info(file_inode(file));
+ struct ll_file_data *fd = LUSTRE_FPRIVATE(file);
+ struct cl_io *io;
+ ssize_t result;
+
+restart:
+ io = ccc_env_thread_io(env);
+ ll_io_init(io, file, iot == CIT_WRITE);
+
+ if (cl_io_rw_init(env, io, iot, *ppos, count) == 0) {
+ struct vvp_io *vio = vvp_env_io(env);
+ struct ccc_io *cio = ccc_env_io(env);
+ int write_mutex_locked = 0;
+
+ cio->cui_fd = LUSTRE_FPRIVATE(file);
+ vio->cui_io_subtype = args->via_io_subtype;
+
+ switch (vio->cui_io_subtype) {
+ case IO_NORMAL:
+ cio->cui_iter = args->u.normal.via_iter;
+ cio->cui_iocb = args->u.normal.via_iocb;
+ if ((iot == CIT_WRITE) &&
+ !(cio->cui_fd->fd_flags & LL_FILE_GROUP_LOCKED)) {
+ if (mutex_lock_interruptible(&lli->
+ lli_write_mutex)) {
+ result = -ERESTARTSYS;
+ goto out;
+ }
+ write_mutex_locked = 1;
+ } else if (iot == CIT_READ) {
+ down_read(&lli->lli_trunc_sem);
+ }
+ break;
+ case IO_SPLICE:
+ vio->u.splice.cui_pipe = args->u.splice.via_pipe;
+ vio->u.splice.cui_flags = args->u.splice.via_flags;
+ break;
+ default:
+ CERROR("Unknown IO type - %u\n", vio->cui_io_subtype);
+ LBUG();
+ }
+ result = cl_io_loop(env, io);
+ if (write_mutex_locked)
+ mutex_unlock(&lli->lli_write_mutex);
+ else if (args->via_io_subtype == IO_NORMAL && iot == CIT_READ)
+ up_read(&lli->lli_trunc_sem);
+ } else {
+ /* cl_io_rw_init() handled IO */
+ result = io->ci_result;
+ }
+
+ if (io->ci_nob > 0) {
+ result = io->ci_nob;
+ *ppos = io->u.ci_wr.wr.crw_pos;
+ }
+ goto out;
+out:
+ cl_io_fini(env, io);
+ /* If any bit been read/written (result != 0), we just return
+ * short read/write instead of restart io. */
+ if ((result == 0 || result == -ENODATA) && io->ci_need_restart) {
+ CDEBUG(D_VFSTRACE, "Restart %s on %pD from %lld, count:%zd\n",
+ iot == CIT_READ ? "read" : "write",
+ file, *ppos, count);
+ LASSERTF(io->ci_nob == 0, "%zd", io->ci_nob);
+ goto restart;
+ }
+
+ if (iot == CIT_READ) {
+ if (result >= 0)
+ ll_stats_ops_tally(ll_i2sbi(file_inode(file)),
+ LPROC_LL_READ_BYTES, result);
+ } else if (iot == CIT_WRITE) {
+ if (result >= 0) {
+ ll_stats_ops_tally(ll_i2sbi(file_inode(file)),
+ LPROC_LL_WRITE_BYTES, result);
+ fd->fd_write_failed = false;
+ } else if (result != -ERESTARTSYS) {
+ fd->fd_write_failed = true;
+ }
+ }
+
+ return result;
+}
+
+static ssize_t ll_file_read_iter(struct kiocb *iocb, struct iov_iter *to)
+{
+ struct lu_env *env;
+ struct vvp_io_args *args;
+ ssize_t result;
+ int refcheck;
+
+ env = cl_env_get(&refcheck);
+ if (IS_ERR(env))
+ return PTR_ERR(env);
+
+ args = vvp_env_args(env, IO_NORMAL);
+ args->u.normal.via_iter = to;
+ args->u.normal.via_iocb = iocb;
+
+ result = ll_file_io_generic(env, args, iocb->ki_filp, CIT_READ,
+ &iocb->ki_pos, iov_iter_count(to));
+ cl_env_put(env, &refcheck);
+ return result;
+}
+
+/*
+ * Write to a file (through the page cache).
+ */
+static ssize_t ll_file_write_iter(struct kiocb *iocb, struct iov_iter *from)
+{
+ struct lu_env *env;
+ struct vvp_io_args *args;
+ ssize_t result;
+ int refcheck;
+
+ env = cl_env_get(&refcheck);
+ if (IS_ERR(env))
+ return PTR_ERR(env);
+
+ args = vvp_env_args(env, IO_NORMAL);
+ args->u.normal.via_iter = from;
+ args->u.normal.via_iocb = iocb;
+
+ result = ll_file_io_generic(env, args, iocb->ki_filp, CIT_WRITE,
+ &iocb->ki_pos, iov_iter_count(from));
+ cl_env_put(env, &refcheck);
+ return result;
+}
+
+/*
+ * Send file content (through pagecache) somewhere with helper
+ */
+static ssize_t ll_file_splice_read(struct file *in_file, loff_t *ppos,
+ struct pipe_inode_info *pipe, size_t count,
+ unsigned int flags)
+{
+ struct lu_env *env;
+ struct vvp_io_args *args;
+ ssize_t result;
+ int refcheck;
+
+ env = cl_env_get(&refcheck);
+ if (IS_ERR(env))
+ return PTR_ERR(env);
+
+ args = vvp_env_args(env, IO_SPLICE);
+ args->u.splice.via_pipe = pipe;
+ args->u.splice.via_flags = flags;
+
+ result = ll_file_io_generic(env, args, in_file, CIT_READ, ppos, count);
+ cl_env_put(env, &refcheck);
+ return result;
+}
+
+static int ll_lov_recreate(struct inode *inode, struct ost_id *oi, u32 ost_idx)
+{
+ struct obd_export *exp = ll_i2dtexp(inode);
+ struct obd_trans_info oti = { 0 };
+ struct obdo *oa = NULL;
+ int lsm_size;
+ int rc = 0;
+ struct lov_stripe_md *lsm = NULL, *lsm2;
+
+ OBDO_ALLOC(oa);
+ if (oa == NULL)
+ return -ENOMEM;
+
+ lsm = ccc_inode_lsm_get(inode);
+ if (!lsm_has_objects(lsm)) {
+ rc = -ENOENT;
+ goto out;
+ }
+
+ lsm_size = sizeof(*lsm) + (sizeof(struct lov_oinfo) *
+ (lsm->lsm_stripe_count));
+
+ OBD_ALLOC_LARGE(lsm2, lsm_size);
+ if (lsm2 == NULL) {
+ rc = -ENOMEM;
+ goto out;
+ }
+
+ oa->o_oi = *oi;
+ oa->o_nlink = ost_idx;
+ oa->o_flags |= OBD_FL_RECREATE_OBJS;
+ oa->o_valid = OBD_MD_FLID | OBD_MD_FLFLAGS | OBD_MD_FLGROUP;
+ obdo_from_inode(oa, inode, OBD_MD_FLTYPE | OBD_MD_FLATIME |
+ OBD_MD_FLMTIME | OBD_MD_FLCTIME);
+ obdo_set_parent_fid(oa, &ll_i2info(inode)->lli_fid);
+ memcpy(lsm2, lsm, lsm_size);
+ ll_inode_size_lock(inode);
+ rc = obd_create(NULL, exp, oa, &lsm2, &oti);
+ ll_inode_size_unlock(inode);
+
+ OBD_FREE_LARGE(lsm2, lsm_size);
+ goto out;
+out:
+ ccc_inode_lsm_put(inode, lsm);
+ OBDO_FREE(oa);
+ return rc;
+}
+
+static int ll_lov_recreate_obj(struct inode *inode, unsigned long arg)
+{
+ struct ll_recreate_obj ucreat;
+ struct ost_id oi;
+
+ if (!capable(CFS_CAP_SYS_ADMIN))
+ return -EPERM;
+
+ if (copy_from_user(&ucreat, (struct ll_recreate_obj *)arg,
+ sizeof(ucreat)))
+ return -EFAULT;
+
+ ostid_set_seq_mdt0(&oi);
+ ostid_set_id(&oi, ucreat.lrc_id);
+ return ll_lov_recreate(inode, &oi, ucreat.lrc_ost_idx);
+}
+
+static int ll_lov_recreate_fid(struct inode *inode, unsigned long arg)
+{
+ struct lu_fid fid;
+ struct ost_id oi;
+ u32 ost_idx;
+
+ if (!capable(CFS_CAP_SYS_ADMIN))
+ return -EPERM;
+
+ if (copy_from_user(&fid, (struct lu_fid *)arg, sizeof(fid)))
+ return -EFAULT;
+
+ fid_to_ostid(&fid, &oi);
+ ost_idx = (fid_seq(&fid) >> 16) & 0xffff;
+ return ll_lov_recreate(inode, &oi, ost_idx);
+}
+
+int ll_lov_setstripe_ea_info(struct inode *inode, struct dentry *dentry,
+ int flags, struct lov_user_md *lum, int lum_size)
+{
+ struct lov_stripe_md *lsm = NULL;
+ struct lookup_intent oit = {.it_op = IT_OPEN, .it_flags = flags};
+ int rc = 0;
+
+ lsm = ccc_inode_lsm_get(inode);
+ if (lsm != NULL) {
+ ccc_inode_lsm_put(inode, lsm);
+ CDEBUG(D_IOCTL, "stripe already exists for ino %lu\n",
+ inode->i_ino);
+ rc = -EEXIST;
+ goto out;
+ }
+
+ ll_inode_size_lock(inode);
+ rc = ll_intent_file_open(dentry, lum, lum_size, &oit);
+ if (rc)
+ goto out_unlock;
+ rc = oit.d.lustre.it_status;
+ if (rc < 0)
+ goto out_req_free;
+
+ ll_release_openhandle(inode, &oit);
+
+out_unlock:
+ ll_inode_size_unlock(inode);
+ ll_intent_release(&oit);
+ ccc_inode_lsm_put(inode, lsm);
+out:
+ return rc;
+out_req_free:
+ ptlrpc_req_finished((struct ptlrpc_request *) oit.d.lustre.it_data);
+ goto out;
+}
+
+int ll_lov_getstripe_ea_info(struct inode *inode, const char *filename,
+ struct lov_mds_md **lmmp, int *lmm_size,
+ struct ptlrpc_request **request)
+{
+ struct ll_sb_info *sbi = ll_i2sbi(inode);
+ struct mdt_body *body;
+ struct lov_mds_md *lmm = NULL;
+ struct ptlrpc_request *req = NULL;
+ struct md_op_data *op_data;
+ int rc, lmmsize;
+
+ rc = ll_get_default_mdsize(sbi, &lmmsize);
+ if (rc)
+ return rc;
+
+ op_data = ll_prep_md_op_data(NULL, inode, NULL, filename,
+ strlen(filename), lmmsize,
+ LUSTRE_OPC_ANY, NULL);
+ if (IS_ERR(op_data))
+ return PTR_ERR(op_data);
+
+ op_data->op_valid = OBD_MD_FLEASIZE | OBD_MD_FLDIREA;
+ rc = md_getattr_name(sbi->ll_md_exp, op_data, &req);
+ ll_finish_md_op_data(op_data);
+ if (rc < 0) {
+ CDEBUG(D_INFO, "md_getattr_name failed on %s: rc %d\n",
+ filename, rc);
+ goto out;
+ }
+
+ body = req_capsule_server_get(&req->rq_pill, &RMF_MDT_BODY);
+ LASSERT(body != NULL); /* checked by mdc_getattr_name */
+
+ lmmsize = body->eadatasize;
+
+ if (!(body->valid & (OBD_MD_FLEASIZE | OBD_MD_FLDIREA)) ||
+ lmmsize == 0) {
+ rc = -ENODATA;
+ goto out;
+ }
+
+ lmm = req_capsule_server_sized_get(&req->rq_pill, &RMF_MDT_MD, lmmsize);
+ LASSERT(lmm != NULL);
+
+ if ((lmm->lmm_magic != cpu_to_le32(LOV_MAGIC_V1)) &&
+ (lmm->lmm_magic != cpu_to_le32(LOV_MAGIC_V3))) {
+ rc = -EPROTO;
+ goto out;
+ }
+
+ /*
+ * This is coming from the MDS, so is probably in
+ * little endian. We convert it to host endian before
+ * passing it to userspace.
+ */
+ if (LOV_MAGIC != cpu_to_le32(LOV_MAGIC)) {
+ int stripe_count;
+
+ stripe_count = le16_to_cpu(lmm->lmm_stripe_count);
+ if (le32_to_cpu(lmm->lmm_pattern) & LOV_PATTERN_F_RELEASED)
+ stripe_count = 0;
+
+ /* if function called for directory - we should
+ * avoid swab not existent lsm objects */
+ if (lmm->lmm_magic == cpu_to_le32(LOV_MAGIC_V1)) {
+ lustre_swab_lov_user_md_v1((struct lov_user_md_v1 *)lmm);
+ if (S_ISREG(body->mode))
+ lustre_swab_lov_user_md_objects(
+ ((struct lov_user_md_v1 *)lmm)->lmm_objects,
+ stripe_count);
+ } else if (lmm->lmm_magic == cpu_to_le32(LOV_MAGIC_V3)) {
+ lustre_swab_lov_user_md_v3((struct lov_user_md_v3 *)lmm);
+ if (S_ISREG(body->mode))
+ lustre_swab_lov_user_md_objects(
+ ((struct lov_user_md_v3 *)lmm)->lmm_objects,
+ stripe_count);
+ }
+ }
+
+out:
+ *lmmp = lmm;
+ *lmm_size = lmmsize;
+ *request = req;
+ return rc;
+}
+
+static int ll_lov_setea(struct inode *inode, struct file *file,
+ unsigned long arg)
+{
+ int flags = MDS_OPEN_HAS_OBJS | FMODE_WRITE;
+ struct lov_user_md *lump;
+ int lum_size = sizeof(struct lov_user_md) +
+ sizeof(struct lov_user_ost_data);
+ int rc;
+
+ if (!capable(CFS_CAP_SYS_ADMIN))
+ return -EPERM;
+
+ OBD_ALLOC_LARGE(lump, lum_size);
+ if (lump == NULL)
+ return -ENOMEM;
+
+ if (copy_from_user(lump, (struct lov_user_md *)arg, lum_size)) {
+ OBD_FREE_LARGE(lump, lum_size);
+ return -EFAULT;
+ }
+
+ rc = ll_lov_setstripe_ea_info(inode, file->f_path.dentry, flags, lump,
+ lum_size);
+ cl_lov_delay_create_clear(&file->f_flags);
+
+ OBD_FREE_LARGE(lump, lum_size);
+ return rc;
+}
+
+static int ll_lov_setstripe(struct inode *inode, struct file *file,
+ unsigned long arg)
+{
+ struct lov_user_md_v3 lumv3;
+ struct lov_user_md_v1 *lumv1 = (struct lov_user_md_v1 *)&lumv3;
+ struct lov_user_md_v1 *lumv1p = (struct lov_user_md_v1 *)arg;
+ struct lov_user_md_v3 *lumv3p = (struct lov_user_md_v3 *)arg;
+ int lum_size, rc;
+ int flags = FMODE_WRITE;
+
+ /* first try with v1 which is smaller than v3 */
+ lum_size = sizeof(struct lov_user_md_v1);
+ if (copy_from_user(lumv1, lumv1p, lum_size))
+ return -EFAULT;
+
+ if (lumv1->lmm_magic == LOV_USER_MAGIC_V3) {
+ lum_size = sizeof(struct lov_user_md_v3);
+ if (copy_from_user(&lumv3, lumv3p, lum_size))
+ return -EFAULT;
+ }
+
+ rc = ll_lov_setstripe_ea_info(inode, file->f_path.dentry, flags, lumv1,
+ lum_size);
+ cl_lov_delay_create_clear(&file->f_flags);
+ if (rc == 0) {
+ struct lov_stripe_md *lsm;
+ __u32 gen;
+
+ put_user(0, &lumv1p->lmm_stripe_count);
+
+ ll_layout_refresh(inode, &gen);
+ lsm = ccc_inode_lsm_get(inode);
+ rc = obd_iocontrol(LL_IOC_LOV_GETSTRIPE, ll_i2dtexp(inode),
+ 0, lsm, (void *)arg);
+ ccc_inode_lsm_put(inode, lsm);
+ }
+ return rc;
+}
+
+static int ll_lov_getstripe(struct inode *inode, unsigned long arg)
+{
+ struct lov_stripe_md *lsm;
+ int rc = -ENODATA;
+
+ lsm = ccc_inode_lsm_get(inode);
+ if (lsm != NULL)
+ rc = obd_iocontrol(LL_IOC_LOV_GETSTRIPE, ll_i2dtexp(inode), 0,
+ lsm, (void *)arg);
+ ccc_inode_lsm_put(inode, lsm);
+ return rc;
+}
+
+static int
+ll_get_grouplock(struct inode *inode, struct file *file, unsigned long arg)
+{
+ struct ll_inode_info *lli = ll_i2info(inode);
+ struct ll_file_data *fd = LUSTRE_FPRIVATE(file);
+ struct ccc_grouplock grouplock;
+ int rc;
+
+ if (arg == 0) {
+ CWARN("group id for group lock must not be 0\n");
+ return -EINVAL;
+ }
+
+ if (ll_file_nolock(file))
+ return -EOPNOTSUPP;
+
+ spin_lock(&lli->lli_lock);
+ if (fd->fd_flags & LL_FILE_GROUP_LOCKED) {
+ CWARN("group lock already existed with gid %lu\n",
+ fd->fd_grouplock.cg_gid);
+ spin_unlock(&lli->lli_lock);
+ return -EINVAL;
+ }
+ LASSERT(fd->fd_grouplock.cg_lock == NULL);
+ spin_unlock(&lli->lli_lock);
+
+ rc = cl_get_grouplock(cl_i2info(inode)->lli_clob,
+ arg, (file->f_flags & O_NONBLOCK), &grouplock);
+ if (rc)
+ return rc;
+
+ spin_lock(&lli->lli_lock);
+ if (fd->fd_flags & LL_FILE_GROUP_LOCKED) {
+ spin_unlock(&lli->lli_lock);
+ CERROR("another thread just won the race\n");
+ cl_put_grouplock(&grouplock);
+ return -EINVAL;
+ }
+
+ fd->fd_flags |= LL_FILE_GROUP_LOCKED;
+ fd->fd_grouplock = grouplock;
+ spin_unlock(&lli->lli_lock);
+
+ CDEBUG(D_INFO, "group lock %lu obtained\n", arg);
+ return 0;
+}
+
+static int ll_put_grouplock(struct inode *inode, struct file *file,
+ unsigned long arg)
+{
+ struct ll_inode_info *lli = ll_i2info(inode);
+ struct ll_file_data *fd = LUSTRE_FPRIVATE(file);
+ struct ccc_grouplock grouplock;
+
+ spin_lock(&lli->lli_lock);
+ if (!(fd->fd_flags & LL_FILE_GROUP_LOCKED)) {
+ spin_unlock(&lli->lli_lock);
+ CWARN("no group lock held\n");
+ return -EINVAL;
+ }
+ LASSERT(fd->fd_grouplock.cg_lock != NULL);
+
+ if (fd->fd_grouplock.cg_gid != arg) {
+ CWARN("group lock %lu doesn't match current id %lu\n",
+ arg, fd->fd_grouplock.cg_gid);
+ spin_unlock(&lli->lli_lock);
+ return -EINVAL;
+ }
+
+ grouplock = fd->fd_grouplock;
+ memset(&fd->fd_grouplock, 0, sizeof(fd->fd_grouplock));
+ fd->fd_flags &= ~LL_FILE_GROUP_LOCKED;
+ spin_unlock(&lli->lli_lock);
+
+ cl_put_grouplock(&grouplock);
+ CDEBUG(D_INFO, "group lock %lu released\n", arg);
+ return 0;
+}
+
+/**
+ * Close inode open handle
+ *
+ * \param inode [in] inode in question
+ * \param it [in,out] intent which contains open info and result
+ *
+ * \retval 0 success
+ * \retval <0 failure
+ */
+int ll_release_openhandle(struct inode *inode, struct lookup_intent *it)
+{
+ struct obd_client_handle *och;
+ int rc;
+
+ LASSERT(inode);
+
+ /* Root ? Do nothing. */
+ if (is_root_inode(inode))
+ return 0;
+
+ /* No open handle to close? Move away */
+ if (!it_disposition(it, DISP_OPEN_OPEN))
+ return 0;
+
+ LASSERT(it_open_error(DISP_OPEN_OPEN, it) == 0);
+
+ och = kzalloc(sizeof(*och), GFP_NOFS);
+ if (!och) {
+ rc = -ENOMEM;
+ goto out;
+ }
+
+ ll_och_fill(ll_i2sbi(inode)->ll_md_exp, it, och);
+
+ rc = ll_close_inode_openhandle(ll_i2sbi(inode)->ll_md_exp,
+ inode, och, NULL);
+out:
+ /* this one is in place of ll_file_open */
+ if (it_disposition(it, DISP_ENQ_OPEN_REF)) {
+ ptlrpc_req_finished(it->d.lustre.it_data);
+ it_clear_disposition(it, DISP_ENQ_OPEN_REF);
+ }
+ return rc;
+}
+
+/**
+ * Get size for inode for which FIEMAP mapping is requested.
+ * Make the FIEMAP get_info call and returns the result.
+ */
+static int ll_do_fiemap(struct inode *inode, struct ll_user_fiemap *fiemap,
+ size_t num_bytes)
+{
+ struct obd_export *exp = ll_i2dtexp(inode);
+ struct lov_stripe_md *lsm = NULL;
+ struct ll_fiemap_info_key fm_key = { .name = KEY_FIEMAP, };
+ __u32 vallen = num_bytes;
+ int rc;
+
+ /* Checks for fiemap flags */
+ if (fiemap->fm_flags & ~LUSTRE_FIEMAP_FLAGS_COMPAT) {
+ fiemap->fm_flags &= ~LUSTRE_FIEMAP_FLAGS_COMPAT;
+ return -EBADR;
+ }
+
+ /* Check for FIEMAP_FLAG_SYNC */
+ if (fiemap->fm_flags & FIEMAP_FLAG_SYNC) {
+ rc = filemap_fdatawrite(inode->i_mapping);
+ if (rc)
+ return rc;
+ }
+
+ lsm = ccc_inode_lsm_get(inode);
+ if (lsm == NULL)
+ return -ENOENT;
+
+ /* If the stripe_count > 1 and the application does not understand
+ * DEVICE_ORDER flag, then it cannot interpret the extents correctly.
+ */
+ if (lsm->lsm_stripe_count > 1 &&
+ !(fiemap->fm_flags & FIEMAP_FLAG_DEVICE_ORDER)) {
+ rc = -EOPNOTSUPP;
+ goto out;
+ }
+
+ fm_key.oa.o_oi = lsm->lsm_oi;
+ fm_key.oa.o_valid = OBD_MD_FLID | OBD_MD_FLGROUP;
+
+ if (i_size_read(inode) == 0) {
+ rc = ll_glimpse_size(inode);
+ if (rc)
+ goto out;
+ }
+
+ obdo_from_inode(&fm_key.oa, inode, OBD_MD_FLSIZE);
+ obdo_set_parent_fid(&fm_key.oa, &ll_i2info(inode)->lli_fid);
+ /* If filesize is 0, then there would be no objects for mapping */
+ if (fm_key.oa.o_size == 0) {
+ fiemap->fm_mapped_extents = 0;
+ rc = 0;
+ goto out;
+ }
+
+ memcpy(&fm_key.fiemap, fiemap, sizeof(*fiemap));
+
+ rc = obd_get_info(NULL, exp, sizeof(fm_key), &fm_key, &vallen,
+ fiemap, lsm);
+ if (rc)
+ CERROR("obd_get_info failed: rc = %d\n", rc);
+
+out:
+ ccc_inode_lsm_put(inode, lsm);
+ return rc;
+}
+
+int ll_fid2path(struct inode *inode, void __user *arg)
+{
+ struct obd_export *exp = ll_i2mdexp(inode);
+ const struct getinfo_fid2path __user *gfin = arg;
+ struct getinfo_fid2path *gfout;
+ u32 pathlen;
+ size_t outsize;
+ int rc;
+
+ if (!capable(CFS_CAP_DAC_READ_SEARCH) &&
+ !(ll_i2sbi(inode)->ll_flags & LL_SBI_USER_FID2PATH))
+ return -EPERM;
+
+ /* Only need to get the buflen */
+ if (get_user(pathlen, &gfin->gf_pathlen))
+ return -EFAULT;
+
+ if (pathlen > PATH_MAX)
+ return -EINVAL;
+
+ outsize = sizeof(*gfout) + pathlen;
+
+ gfout = kzalloc(outsize, GFP_NOFS);
+ if (!gfout)
+ return -ENOMEM;
+
+ if (copy_from_user(gfout, arg, sizeof(*gfout))) {
+ rc = -EFAULT;
+ goto gf_free;
+ }
+
+ /* Call mdc_iocontrol */
+ rc = obd_iocontrol(OBD_IOC_FID2PATH, exp, outsize, gfout, NULL);
+ if (rc != 0)
+ goto gf_free;
+
+ if (copy_to_user(arg, gfout, outsize))
+ rc = -EFAULT;
+
+gf_free:
+ OBD_FREE(gfout, outsize);
+ return rc;
+}
+
+static int ll_ioctl_fiemap(struct inode *inode, unsigned long arg)
+{
+ struct ll_user_fiemap *fiemap_s;
+ size_t num_bytes, ret_bytes;
+ unsigned int extent_count;
+ int rc = 0;
+
+ /* Get the extent count so we can calculate the size of
+ * required fiemap buffer */
+ if (get_user(extent_count,
+ &((struct ll_user_fiemap __user *)arg)->fm_extent_count))
+ return -EFAULT;
+
+ if (extent_count >=
+ (SIZE_MAX - sizeof(*fiemap_s)) / sizeof(struct ll_fiemap_extent))
+ return -EINVAL;
+ num_bytes = sizeof(*fiemap_s) + (extent_count *
+ sizeof(struct ll_fiemap_extent));
+
+ OBD_ALLOC_LARGE(fiemap_s, num_bytes);
+ if (fiemap_s == NULL)
+ return -ENOMEM;
+
+ /* get the fiemap value */
+ if (copy_from_user(fiemap_s, (struct ll_user_fiemap __user *)arg,
+ sizeof(*fiemap_s))) {
+ rc = -EFAULT;
+ goto error;
+ }
+
+ /* If fm_extent_count is non-zero, read the first extent since
+ * it is used to calculate end_offset and device from previous
+ * fiemap call. */
+ if (extent_count) {
+ if (copy_from_user(&fiemap_s->fm_extents[0],
+ (char __user *)arg + sizeof(*fiemap_s),
+ sizeof(struct ll_fiemap_extent))) {
+ rc = -EFAULT;
+ goto error;
+ }
+ }
+
+ rc = ll_do_fiemap(inode, fiemap_s, num_bytes);
+ if (rc)
+ goto error;
+
+ ret_bytes = sizeof(struct ll_user_fiemap);
+
+ if (extent_count != 0)
+ ret_bytes += (fiemap_s->fm_mapped_extents *
+ sizeof(struct ll_fiemap_extent));
+
+ if (copy_to_user((void *)arg, fiemap_s, ret_bytes))
+ rc = -EFAULT;
+
+error:
+ OBD_FREE_LARGE(fiemap_s, num_bytes);
+ return rc;
+}
+
+/*
+ * Read the data_version for inode.
+ *
+ * This value is computed using stripe object version on OST.
+ * Version is computed using server side locking.
+ *
+ * @param extent_lock Take extent lock. Not needed if a process is already
+ * holding the OST object group locks.
+ */
+int ll_data_version(struct inode *inode, __u64 *data_version,
+ int extent_lock)
+{
+ struct lov_stripe_md *lsm = NULL;
+ struct ll_sb_info *sbi = ll_i2sbi(inode);
+ struct obdo *obdo = NULL;
+ int rc;
+
+ /* If no stripe, we consider version is 0. */
+ lsm = ccc_inode_lsm_get(inode);
+ if (!lsm_has_objects(lsm)) {
+ *data_version = 0;
+ CDEBUG(D_INODE, "No object for inode\n");
+ rc = 0;
+ goto out;
+ }
+
+ obdo = kzalloc(sizeof(*obdo), GFP_NOFS);
+ if (!obdo) {
+ rc = -ENOMEM;
+ goto out;
+ }
+
+ rc = ll_lsm_getattr(lsm, sbi->ll_dt_exp, NULL, obdo, 0, extent_lock);
+ if (rc == 0) {
+ if (!(obdo->o_valid & OBD_MD_FLDATAVERSION))
+ rc = -EOPNOTSUPP;
+ else
+ *data_version = obdo->o_data_version;
+ }
+
+ OBD_FREE_PTR(obdo);
+out:
+ ccc_inode_lsm_put(inode, lsm);
+ return rc;
+}
+
+/*
+ * Trigger a HSM release request for the provided inode.
+ */
+int ll_hsm_release(struct inode *inode)
+{
+ struct cl_env_nest nest;
+ struct lu_env *env;
+ struct obd_client_handle *och = NULL;
+ __u64 data_version = 0;
+ int rc;
+
+
+ CDEBUG(D_INODE, "%s: Releasing file "DFID".\n",
+ ll_get_fsname(inode->i_sb, NULL, 0),
+ PFID(&ll_i2info(inode)->lli_fid));
+
+ och = ll_lease_open(inode, NULL, FMODE_WRITE, MDS_OPEN_RELEASE);
+ if (IS_ERR(och)) {
+ rc = PTR_ERR(och);
+ goto out;
+ }
+
+ /* Grab latest data_version and [am]time values */
+ rc = ll_data_version(inode, &data_version, 1);
+ if (rc != 0)
+ goto out;
+
+ env = cl_env_nested_get(&nest);
+ if (IS_ERR(env)) {
+ rc = PTR_ERR(env);
+ goto out;
+ }
+
+ ll_merge_lvb(env, inode);
+ cl_env_nested_put(&nest, env);
+
+ /* Release the file.
+ * NB: lease lock handle is released in mdc_hsm_release_pack() because
+ * we still need it to pack l_remote_handle to MDT. */
+ rc = ll_close_inode_openhandle(ll_i2sbi(inode)->ll_md_exp, inode, och,
+ &data_version);
+ och = NULL;
+
+
+out:
+ if (och != NULL && !IS_ERR(och)) /* close the file */
+ ll_lease_close(och, inode, NULL);
+
+ return rc;
+}
+
+struct ll_swap_stack {
+ struct iattr ia1, ia2;
+ __u64 dv1, dv2;
+ struct inode *inode1, *inode2;
+ bool check_dv1, check_dv2;
+};
+
+static int ll_swap_layouts(struct file *file1, struct file *file2,
+ struct lustre_swap_layouts *lsl)
+{
+ struct mdc_swap_layouts msl;
+ struct md_op_data *op_data;
+ __u32 gid;
+ __u64 dv;
+ struct ll_swap_stack *llss = NULL;
+ int rc;
+
+ llss = kzalloc(sizeof(*llss), GFP_NOFS);
+ if (!llss)
+ return -ENOMEM;
+
+ llss->inode1 = file_inode(file1);
+ llss->inode2 = file_inode(file2);
+
+ if (!S_ISREG(llss->inode2->i_mode)) {
+ rc = -EINVAL;
+ goto free;
+ }
+
+ if (inode_permission(llss->inode1, MAY_WRITE) ||
+ inode_permission(llss->inode2, MAY_WRITE)) {
+ rc = -EPERM;
+ goto free;
+ }
+
+ if (llss->inode2->i_sb != llss->inode1->i_sb) {
+ rc = -EXDEV;
+ goto free;
+ }
+
+ /* we use 2 bool because it is easier to swap than 2 bits */
+ if (lsl->sl_flags & SWAP_LAYOUTS_CHECK_DV1)
+ llss->check_dv1 = true;
+
+ if (lsl->sl_flags & SWAP_LAYOUTS_CHECK_DV2)
+ llss->check_dv2 = true;
+
+ /* we cannot use lsl->sl_dvX directly because we may swap them */
+ llss->dv1 = lsl->sl_dv1;
+ llss->dv2 = lsl->sl_dv2;
+
+ rc = lu_fid_cmp(ll_inode2fid(llss->inode1), ll_inode2fid(llss->inode2));
+ if (rc == 0) /* same file, done! */ {
+ rc = 0;
+ goto free;
+ }
+
+ if (rc < 0) { /* sequentialize it */
+ swap(llss->inode1, llss->inode2);
+ swap(file1, file2);
+ swap(llss->dv1, llss->dv2);
+ swap(llss->check_dv1, llss->check_dv2);
+ }
+
+ gid = lsl->sl_gid;
+ if (gid != 0) { /* application asks to flush dirty cache */
+ rc = ll_get_grouplock(llss->inode1, file1, gid);
+ if (rc < 0)
+ goto free;
+
+ rc = ll_get_grouplock(llss->inode2, file2, gid);
+ if (rc < 0) {
+ ll_put_grouplock(llss->inode1, file1, gid);
+ goto free;
+ }
+ }
+
+ /* to be able to restore mtime and atime after swap
+ * we need to first save them */
+ if (lsl->sl_flags &
+ (SWAP_LAYOUTS_KEEP_MTIME | SWAP_LAYOUTS_KEEP_ATIME)) {
+ llss->ia1.ia_mtime = llss->inode1->i_mtime;
+ llss->ia1.ia_atime = llss->inode1->i_atime;
+ llss->ia1.ia_valid = ATTR_MTIME | ATTR_ATIME;
+ llss->ia2.ia_mtime = llss->inode2->i_mtime;
+ llss->ia2.ia_atime = llss->inode2->i_atime;
+ llss->ia2.ia_valid = ATTR_MTIME | ATTR_ATIME;
+ }
+
+ /* ultimate check, before swapping the layouts we check if
+ * dataversion has changed (if requested) */
+ if (llss->check_dv1) {
+ rc = ll_data_version(llss->inode1, &dv, 0);
+ if (rc)
+ goto putgl;
+ if (dv != llss->dv1) {
+ rc = -EAGAIN;
+ goto putgl;
+ }
+ }
+
+ if (llss->check_dv2) {
+ rc = ll_data_version(llss->inode2, &dv, 0);
+ if (rc)
+ goto putgl;
+ if (dv != llss->dv2) {
+ rc = -EAGAIN;
+ goto putgl;
+ }
+ }
+
+ /* struct md_op_data is used to send the swap args to the mdt
+ * only flags is missing, so we use struct mdc_swap_layouts
+ * through the md_op_data->op_data */
+ /* flags from user space have to be converted before they are send to
+ * server, no flag is sent today, they are only used on the client */
+ msl.msl_flags = 0;
+ rc = -ENOMEM;
+ op_data = ll_prep_md_op_data(NULL, llss->inode1, llss->inode2, NULL, 0,
+ 0, LUSTRE_OPC_ANY, &msl);
+ if (IS_ERR(op_data)) {
+ rc = PTR_ERR(op_data);
+ goto free;
+ }
+
+ rc = obd_iocontrol(LL_IOC_LOV_SWAP_LAYOUTS, ll_i2mdexp(llss->inode1),
+ sizeof(*op_data), op_data, NULL);
+ ll_finish_md_op_data(op_data);
+
+putgl:
+ if (gid != 0) {
+ ll_put_grouplock(llss->inode2, file2, gid);
+ ll_put_grouplock(llss->inode1, file1, gid);
+ }
+
+ /* rc can be set from obd_iocontrol() or from a GOTO(putgl, ...) */
+ if (rc != 0)
+ goto free;
+
+ /* clear useless flags */
+ if (!(lsl->sl_flags & SWAP_LAYOUTS_KEEP_MTIME)) {
+ llss->ia1.ia_valid &= ~ATTR_MTIME;
+ llss->ia2.ia_valid &= ~ATTR_MTIME;
+ }
+
+ if (!(lsl->sl_flags & SWAP_LAYOUTS_KEEP_ATIME)) {
+ llss->ia1.ia_valid &= ~ATTR_ATIME;
+ llss->ia2.ia_valid &= ~ATTR_ATIME;
+ }
+
+ /* update time if requested */
+ rc = 0;
+ if (llss->ia2.ia_valid != 0) {
+ mutex_lock(&llss->inode1->i_mutex);
+ rc = ll_setattr(file1->f_path.dentry, &llss->ia2);
+ mutex_unlock(&llss->inode1->i_mutex);
+ }
+
+ if (llss->ia1.ia_valid != 0) {
+ int rc1;
+
+ mutex_lock(&llss->inode2->i_mutex);
+ rc1 = ll_setattr(file2->f_path.dentry, &llss->ia1);
+ mutex_unlock(&llss->inode2->i_mutex);
+ if (rc == 0)
+ rc = rc1;
+ }
+
+free:
+ if (llss != NULL)
+ OBD_FREE_PTR(llss);
+
+ return rc;
+}
+
+static int ll_hsm_state_set(struct inode *inode, struct hsm_state_set *hss)
+{
+ struct md_op_data *op_data;
+ int rc;
+
+ /* Non-root users are forbidden to set or clear flags which are
+ * NOT defined in HSM_USER_MASK. */
+ if (((hss->hss_setmask | hss->hss_clearmask) & ~HSM_USER_MASK) &&
+ !capable(CFS_CAP_SYS_ADMIN))
+ return -EPERM;
+
+ op_data = ll_prep_md_op_data(NULL, inode, NULL, NULL, 0, 0,
+ LUSTRE_OPC_ANY, hss);
+ if (IS_ERR(op_data))
+ return PTR_ERR(op_data);
+
+ rc = obd_iocontrol(LL_IOC_HSM_STATE_SET, ll_i2mdexp(inode),
+ sizeof(*op_data), op_data, NULL);
+
+ ll_finish_md_op_data(op_data);
+
+ return rc;
+}
+
+static int ll_hsm_import(struct inode *inode, struct file *file,
+ struct hsm_user_import *hui)
+{
+ struct hsm_state_set *hss = NULL;
+ struct iattr *attr = NULL;
+ int rc;
+
+
+ if (!S_ISREG(inode->i_mode))
+ return -EINVAL;
+
+ /* set HSM flags */
+ hss = kzalloc(sizeof(*hss), GFP_NOFS);
+ if (!hss) {
+ rc = -ENOMEM;
+ goto out;
+ }
+
+ hss->hss_valid = HSS_SETMASK | HSS_ARCHIVE_ID;
+ hss->hss_archive_id = hui->hui_archive_id;
+ hss->hss_setmask = HS_ARCHIVED | HS_EXISTS | HS_RELEASED;
+ rc = ll_hsm_state_set(inode, hss);
+ if (rc != 0)
+ goto out;
+
+ attr = kzalloc(sizeof(*attr), GFP_NOFS);
+ if (!attr) {
+ rc = -ENOMEM;
+ goto out;
+ }
+
+ attr->ia_mode = hui->hui_mode & (S_IRWXU | S_IRWXG | S_IRWXO);
+ attr->ia_mode |= S_IFREG;
+ attr->ia_uid = make_kuid(&init_user_ns, hui->hui_uid);
+ attr->ia_gid = make_kgid(&init_user_ns, hui->hui_gid);
+ attr->ia_size = hui->hui_size;
+ attr->ia_mtime.tv_sec = hui->hui_mtime;
+ attr->ia_mtime.tv_nsec = hui->hui_mtime_ns;
+ attr->ia_atime.tv_sec = hui->hui_atime;
+ attr->ia_atime.tv_nsec = hui->hui_atime_ns;
+
+ attr->ia_valid = ATTR_SIZE | ATTR_MODE | ATTR_FORCE |
+ ATTR_UID | ATTR_GID |
+ ATTR_MTIME | ATTR_MTIME_SET |
+ ATTR_ATIME | ATTR_ATIME_SET;
+
+ mutex_lock(&inode->i_mutex);
+
+ rc = ll_setattr_raw(file->f_path.dentry, attr, true);
+ if (rc == -ENODATA)
+ rc = 0;
+
+ mutex_unlock(&inode->i_mutex);
+
+out:
+ if (hss != NULL)
+ OBD_FREE_PTR(hss);
+
+ if (attr != NULL)
+ OBD_FREE_PTR(attr);
+
+ return rc;
+}
+
+static long
+ll_file_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+ struct inode *inode = file_inode(file);
+ struct ll_file_data *fd = LUSTRE_FPRIVATE(file);
+ int flags, rc;
+
+ CDEBUG(D_VFSTRACE, "VFS Op:inode=%lu/%u(%p),cmd=%x\n", inode->i_ino,
+ inode->i_generation, inode, cmd);
+ ll_stats_ops_tally(ll_i2sbi(inode), LPROC_LL_IOCTL, 1);
+
+ /* asm-ppc{,64} declares TCGETS, et. al. as type 't' not 'T' */
+ if (_IOC_TYPE(cmd) == 'T' || _IOC_TYPE(cmd) == 't') /* tty ioctls */
+ return -ENOTTY;
+
+ switch (cmd) {
+ case LL_IOC_GETFLAGS:
+ /* Get the current value of the file flags */
+ return put_user(fd->fd_flags, (int *)arg);
+ case LL_IOC_SETFLAGS:
+ case LL_IOC_CLRFLAGS:
+ /* Set or clear specific file flags */
+ /* XXX This probably needs checks to ensure the flags are
+ * not abused, and to handle any flag side effects.
+ */
+ if (get_user(flags, (int *) arg))
+ return -EFAULT;
+
+ if (cmd == LL_IOC_SETFLAGS) {
+ if ((flags & LL_FILE_IGNORE_LOCK) &&
+ !(file->f_flags & O_DIRECT)) {
+ CERROR("%s: unable to disable locking on non-O_DIRECT file\n",
+ current->comm);
+ return -EINVAL;
+ }
+
+ fd->fd_flags |= flags;
+ } else {
+ fd->fd_flags &= ~flags;
+ }
+ return 0;
+ case LL_IOC_LOV_SETSTRIPE:
+ return ll_lov_setstripe(inode, file, arg);
+ case LL_IOC_LOV_SETEA:
+ return ll_lov_setea(inode, file, arg);
+ case LL_IOC_LOV_SWAP_LAYOUTS: {
+ struct file *file2;
+ struct lustre_swap_layouts lsl;
+
+ if (copy_from_user(&lsl, (char *)arg,
+ sizeof(struct lustre_swap_layouts)))
+ return -EFAULT;
+
+ if ((file->f_flags & O_ACCMODE) == 0) /* O_RDONLY */
+ return -EPERM;
+
+ file2 = fget(lsl.sl_fd);
+ if (file2 == NULL)
+ return -EBADF;
+
+ rc = -EPERM;
+ if ((file2->f_flags & O_ACCMODE) != 0) /* O_WRONLY or O_RDWR */
+ rc = ll_swap_layouts(file, file2, &lsl);
+ fput(file2);
+ return rc;
+ }
+ case LL_IOC_LOV_GETSTRIPE:
+ return ll_lov_getstripe(inode, arg);
+ case LL_IOC_RECREATE_OBJ:
+ return ll_lov_recreate_obj(inode, arg);
+ case LL_IOC_RECREATE_FID:
+ return ll_lov_recreate_fid(inode, arg);
+ case FSFILT_IOC_FIEMAP:
+ return ll_ioctl_fiemap(inode, arg);
+ case FSFILT_IOC_GETFLAGS:
+ case FSFILT_IOC_SETFLAGS:
+ return ll_iocontrol(inode, file, cmd, arg);
+ case FSFILT_IOC_GETVERSION_OLD:
+ case FSFILT_IOC_GETVERSION:
+ return put_user(inode->i_generation, (int *)arg);
+ case LL_IOC_GROUP_LOCK:
+ return ll_get_grouplock(inode, file, arg);
+ case LL_IOC_GROUP_UNLOCK:
+ return ll_put_grouplock(inode, file, arg);
+ case IOC_OBD_STATFS:
+ return ll_obd_statfs(inode, (void *)arg);
+
+ /* We need to special case any other ioctls we want to handle,
+ * to send them to the MDS/OST as appropriate and to properly
+ * network encode the arg field.
+ case FSFILT_IOC_SETVERSION_OLD:
+ case FSFILT_IOC_SETVERSION:
+ */
+ case LL_IOC_FLUSHCTX:
+ return ll_flush_ctx(inode);
+ case LL_IOC_PATH2FID: {
+ if (copy_to_user((void *)arg, ll_inode2fid(inode),
+ sizeof(struct lu_fid)))
+ return -EFAULT;
+
+ return 0;
+ }
+ case OBD_IOC_FID2PATH:
+ return ll_fid2path(inode, (void *)arg);
+ case LL_IOC_DATA_VERSION: {
+ struct ioc_data_version idv;
+ int rc;
+
+ if (copy_from_user(&idv, (char *)arg, sizeof(idv)))
+ return -EFAULT;
+
+ rc = ll_data_version(inode, &idv.idv_version,
+ !(idv.idv_flags & LL_DV_NOFLUSH));
+
+ if (rc == 0 && copy_to_user((char *) arg, &idv, sizeof(idv)))
+ return -EFAULT;
+
+ return rc;
+ }
+
+ case LL_IOC_GET_MDTIDX: {
+ int mdtidx;
+
+ mdtidx = ll_get_mdt_idx(inode);
+ if (mdtidx < 0)
+ return mdtidx;
+
+ if (put_user((int)mdtidx, (int *)arg))
+ return -EFAULT;
+
+ return 0;
+ }
+ case OBD_IOC_GETDTNAME:
+ case OBD_IOC_GETMDNAME:
+ return ll_get_obd_name(inode, cmd, arg);
+ case LL_IOC_HSM_STATE_GET: {
+ struct md_op_data *op_data;
+ struct hsm_user_state *hus;
+ int rc;
+
+ hus = kzalloc(sizeof(*hus), GFP_NOFS);
+ if (!hus)
+ return -ENOMEM;
+
+ op_data = ll_prep_md_op_data(NULL, inode, NULL, NULL, 0, 0,
+ LUSTRE_OPC_ANY, hus);
+ if (IS_ERR(op_data)) {
+ OBD_FREE_PTR(hus);
+ return PTR_ERR(op_data);
+ }
+
+ rc = obd_iocontrol(cmd, ll_i2mdexp(inode), sizeof(*op_data),
+ op_data, NULL);
+
+ if (copy_to_user((void *)arg, hus, sizeof(*hus)))
+ rc = -EFAULT;
+
+ ll_finish_md_op_data(op_data);
+ OBD_FREE_PTR(hus);
+ return rc;
+ }
+ case LL_IOC_HSM_STATE_SET: {
+ struct hsm_state_set *hss;
+ int rc;
+
+ hss = kzalloc(sizeof(*hss), GFP_NOFS);
+ if (!hss)
+ return -ENOMEM;
+
+ if (copy_from_user(hss, (char *)arg, sizeof(*hss))) {
+ OBD_FREE_PTR(hss);
+ return -EFAULT;
+ }
+
+ rc = ll_hsm_state_set(inode, hss);
+
+ OBD_FREE_PTR(hss);
+ return rc;
+ }
+ case LL_IOC_HSM_ACTION: {
+ struct md_op_data *op_data;
+ struct hsm_current_action *hca;
+ int rc;
+
+ hca = kzalloc(sizeof(*hca), GFP_NOFS);
+ if (!hca)
+ return -ENOMEM;
+
+ op_data = ll_prep_md_op_data(NULL, inode, NULL, NULL, 0, 0,
+ LUSTRE_OPC_ANY, hca);
+ if (IS_ERR(op_data)) {
+ OBD_FREE_PTR(hca);
+ return PTR_ERR(op_data);
+ }
+
+ rc = obd_iocontrol(cmd, ll_i2mdexp(inode), sizeof(*op_data),
+ op_data, NULL);
+
+ if (copy_to_user((char *)arg, hca, sizeof(*hca)))
+ rc = -EFAULT;
+
+ ll_finish_md_op_data(op_data);
+ OBD_FREE_PTR(hca);
+ return rc;
+ }
+ case LL_IOC_SET_LEASE: {
+ struct ll_inode_info *lli = ll_i2info(inode);
+ struct obd_client_handle *och = NULL;
+ bool lease_broken;
+ fmode_t mode = 0;
+
+ switch (arg) {
+ case F_WRLCK:
+ if (!(file->f_mode & FMODE_WRITE))
+ return -EPERM;
+ mode = FMODE_WRITE;
+ break;
+ case F_RDLCK:
+ if (!(file->f_mode & FMODE_READ))
+ return -EPERM;
+ mode = FMODE_READ;
+ break;
+ case F_UNLCK:
+ mutex_lock(&lli->lli_och_mutex);
+ if (fd->fd_lease_och != NULL) {
+ och = fd->fd_lease_och;
+ fd->fd_lease_och = NULL;
+ }
+ mutex_unlock(&lli->lli_och_mutex);
+
+ if (och != NULL) {
+ mode = och->och_flags &
+ (FMODE_READ|FMODE_WRITE);
+ rc = ll_lease_close(och, inode, &lease_broken);
+ if (rc == 0 && lease_broken)
+ mode = 0;
+ } else {
+ rc = -ENOLCK;
+ }
+
+ /* return the type of lease or error */
+ return rc < 0 ? rc : (int)mode;
+ default:
+ return -EINVAL;
+ }
+
+ CDEBUG(D_INODE, "Set lease with mode %d\n", mode);
+
+ /* apply for lease */
+ och = ll_lease_open(inode, file, mode, 0);
+ if (IS_ERR(och))
+ return PTR_ERR(och);
+
+ rc = 0;
+ mutex_lock(&lli->lli_och_mutex);
+ if (fd->fd_lease_och == NULL) {
+ fd->fd_lease_och = och;
+ och = NULL;
+ }
+ mutex_unlock(&lli->lli_och_mutex);
+ if (och != NULL) {
+ /* impossible now that only excl is supported for now */
+ ll_lease_close(och, inode, &lease_broken);
+ rc = -EBUSY;
+ }
+ return rc;
+ }
+ case LL_IOC_GET_LEASE: {
+ struct ll_inode_info *lli = ll_i2info(inode);
+ struct ldlm_lock *lock = NULL;
+
+ rc = 0;
+ mutex_lock(&lli->lli_och_mutex);
+ if (fd->fd_lease_och != NULL) {
+ struct obd_client_handle *och = fd->fd_lease_och;
+
+ lock = ldlm_handle2lock(&och->och_lease_handle);
+ if (lock != NULL) {
+ lock_res_and_lock(lock);
+ if (!ldlm_is_cancel(lock))
+ rc = och->och_flags &
+ (FMODE_READ | FMODE_WRITE);
+ unlock_res_and_lock(lock);
+ ldlm_lock_put(lock);
+ }
+ }
+ mutex_unlock(&lli->lli_och_mutex);
+ return rc;
+ }
+ case LL_IOC_HSM_IMPORT: {
+ struct hsm_user_import *hui;
+
+ hui = kzalloc(sizeof(*hui), GFP_NOFS);
+ if (!hui)
+ return -ENOMEM;
+
+ if (copy_from_user(hui, (void *)arg, sizeof(*hui))) {
+ OBD_FREE_PTR(hui);
+ return -EFAULT;
+ }
+
+ rc = ll_hsm_import(inode, file, hui);
+
+ OBD_FREE_PTR(hui);
+ return rc;
+ }
+ default: {
+ int err;
+
+ if (LLIOC_STOP ==
+ ll_iocontrol_call(inode, file, cmd, arg, &err))
+ return err;
+
+ return obd_iocontrol(cmd, ll_i2dtexp(inode), 0, NULL,
+ (void *)arg);
+ }
+ }
+}
+
+
+static loff_t ll_file_seek(struct file *file, loff_t offset, int origin)
+{
+ struct inode *inode = file_inode(file);
+ loff_t retval, eof = 0;
+
+ retval = offset + ((origin == SEEK_END) ? i_size_read(inode) :
+ (origin == SEEK_CUR) ? file->f_pos : 0);
+ CDEBUG(D_VFSTRACE, "VFS Op:inode=%lu/%u(%p), to=%llu=%#llx(%d)\n",
+ inode->i_ino, inode->i_generation, inode, retval, retval,
+ origin);
+ ll_stats_ops_tally(ll_i2sbi(inode), LPROC_LL_LLSEEK, 1);
+
+ if (origin == SEEK_END || origin == SEEK_HOLE || origin == SEEK_DATA) {
+ retval = ll_glimpse_size(inode);
+ if (retval != 0)
+ return retval;
+ eof = i_size_read(inode);
+ }
+
+ retval = generic_file_llseek_size(file, offset, origin,
+ ll_file_maxbytes(inode), eof);
+ return retval;
+}
+
+static int ll_flush(struct file *file, fl_owner_t id)
+{
+ struct inode *inode = file_inode(file);
+ struct ll_inode_info *lli = ll_i2info(inode);
+ struct ll_file_data *fd = LUSTRE_FPRIVATE(file);
+ int rc, err;
+
+ LASSERT(!S_ISDIR(inode->i_mode));
+
+ /* catch async errors that were recorded back when async writeback
+ * failed for pages in this mapping. */
+ rc = lli->lli_async_rc;
+ lli->lli_async_rc = 0;
+ err = lov_read_and_clear_async_rc(lli->lli_clob);
+ if (rc == 0)
+ rc = err;
+
+ /* The application has been told write failure already.
+ * Do not report failure again. */
+ if (fd->fd_write_failed)
+ return 0;
+ return rc ? -EIO : 0;
+}
+
+/**
+ * Called to make sure a portion of file has been written out.
+ * if @mode is not CL_FSYNC_LOCAL, it will send OST_SYNC RPCs to OST.
+ *
+ * Return how many pages have been written.
+ */
+int cl_sync_file_range(struct inode *inode, loff_t start, loff_t end,
+ enum cl_fsync_mode mode, int ignore_layout)
+{
+ struct cl_env_nest nest;
+ struct lu_env *env;
+ struct cl_io *io;
+ struct obd_capa *capa = NULL;
+ struct cl_fsync_io *fio;
+ int result;
+
+ if (mode != CL_FSYNC_NONE && mode != CL_FSYNC_LOCAL &&
+ mode != CL_FSYNC_DISCARD && mode != CL_FSYNC_ALL)
+ return -EINVAL;
+
+ env = cl_env_nested_get(&nest);
+ if (IS_ERR(env))
+ return PTR_ERR(env);
+
+ capa = ll_osscapa_get(inode, CAPA_OPC_OSS_WRITE);
+
+ io = ccc_env_thread_io(env);
+ io->ci_obj = cl_i2info(inode)->lli_clob;
+ io->ci_ignore_layout = ignore_layout;
+
+ /* initialize parameters for sync */
+ fio = &io->u.ci_fsync;
+ fio->fi_capa = capa;
+ fio->fi_start = start;
+ fio->fi_end = end;
+ fio->fi_fid = ll_inode2fid(inode);
+ fio->fi_mode = mode;
+ fio->fi_nr_written = 0;
+
+ if (cl_io_init(env, io, CIT_FSYNC, io->ci_obj) == 0)
+ result = cl_io_loop(env, io);
+ else
+ result = io->ci_result;
+ if (result == 0)
+ result = fio->fi_nr_written;
+ cl_io_fini(env, io);
+ cl_env_nested_put(&nest, env);
+
+ capa_put(capa);
+
+ return result;
+}
+
+int ll_fsync(struct file *file, loff_t start, loff_t end, int datasync)
+{
+ struct inode *inode = file_inode(file);
+ struct ll_inode_info *lli = ll_i2info(inode);
+ struct ptlrpc_request *req;
+ struct obd_capa *oc;
+ int rc, err;
+
+ CDEBUG(D_VFSTRACE, "VFS Op:inode=%lu/%u(%p)\n", inode->i_ino,
+ inode->i_generation, inode);
+ ll_stats_ops_tally(ll_i2sbi(inode), LPROC_LL_FSYNC, 1);
+
+ rc = filemap_write_and_wait_range(inode->i_mapping, start, end);
+ mutex_lock(&inode->i_mutex);
+
+ /* catch async errors that were recorded back when async writeback
+ * failed for pages in this mapping. */
+ if (!S_ISDIR(inode->i_mode)) {
+ err = lli->lli_async_rc;
+ lli->lli_async_rc = 0;
+ if (rc == 0)
+ rc = err;
+ err = lov_read_and_clear_async_rc(lli->lli_clob);
+ if (rc == 0)
+ rc = err;
+ }
+
+ oc = ll_mdscapa_get(inode);
+ err = md_sync(ll_i2sbi(inode)->ll_md_exp, ll_inode2fid(inode), oc,
+ &req);
+ capa_put(oc);
+ if (!rc)
+ rc = err;
+ if (!err)
+ ptlrpc_req_finished(req);
+
+ if (S_ISREG(inode->i_mode)) {
+ struct ll_file_data *fd = LUSTRE_FPRIVATE(file);
+
+ err = cl_sync_file_range(inode, start, end, CL_FSYNC_ALL, 0);
+ if (rc == 0 && err < 0)
+ rc = err;
+ if (rc < 0)
+ fd->fd_write_failed = true;
+ else
+ fd->fd_write_failed = false;
+ }
+
+ mutex_unlock(&inode->i_mutex);
+ return rc;
+}
+
+static int
+ll_file_flock(struct file *file, int cmd, struct file_lock *file_lock)
+{
+ struct inode *inode = file_inode(file);
+ struct ll_sb_info *sbi = ll_i2sbi(inode);
+ struct ldlm_enqueue_info einfo = {
+ .ei_type = LDLM_FLOCK,
+ .ei_cb_cp = ldlm_flock_completion_ast,
+ .ei_cbdata = file_lock,
+ };
+ struct md_op_data *op_data;
+ struct lustre_handle lockh = {0};
+ ldlm_policy_data_t flock = {{0}};
+ __u64 flags = 0;
+ int rc;
+ int rc2 = 0;
+
+ CDEBUG(D_VFSTRACE, "VFS Op:inode=%lu file_lock=%p\n",
+ inode->i_ino, file_lock);
+
+ ll_stats_ops_tally(ll_i2sbi(inode), LPROC_LL_FLOCK, 1);
+
+ if (file_lock->fl_flags & FL_FLOCK)
+ LASSERT((cmd == F_SETLKW) || (cmd == F_SETLK));
+ else if (!(file_lock->fl_flags & FL_POSIX))
+ return -EINVAL;
+
+ flock.l_flock.owner = (unsigned long)file_lock->fl_owner;
+ flock.l_flock.pid = file_lock->fl_pid;
+ flock.l_flock.start = file_lock->fl_start;
+ flock.l_flock.end = file_lock->fl_end;
+
+ /* Somewhat ugly workaround for svc lockd.
+ * lockd installs custom fl_lmops->lm_compare_owner that checks
+ * for the fl_owner to be the same (which it always is on local node
+ * I guess between lockd processes) and then compares pid.
+ * As such we assign pid to the owner field to make it all work,
+ * conflict with normal locks is unlikely since pid space and
+ * pointer space for current->files are not intersecting */
+ if (file_lock->fl_lmops && file_lock->fl_lmops->lm_compare_owner)
+ flock.l_flock.owner = (unsigned long)file_lock->fl_pid;
+
+ switch (file_lock->fl_type) {
+ case F_RDLCK:
+ einfo.ei_mode = LCK_PR;
+ break;
+ case F_UNLCK:
+ /* An unlock request may or may not have any relation to
+ * existing locks so we may not be able to pass a lock handle
+ * via a normal ldlm_lock_cancel() request. The request may even
+ * unlock a byte range in the middle of an existing lock. In
+ * order to process an unlock request we need all of the same
+ * information that is given with a normal read or write record
+ * lock request. To avoid creating another ldlm unlock (cancel)
+ * message we'll treat a LCK_NL flock request as an unlock. */
+ einfo.ei_mode = LCK_NL;
+ break;
+ case F_WRLCK:
+ einfo.ei_mode = LCK_PW;
+ break;
+ default:
+ CDEBUG(D_INFO, "Unknown fcntl lock type: %d\n",
+ file_lock->fl_type);
+ return -ENOTSUPP;
+ }
+
+ switch (cmd) {
+ case F_SETLKW:
+#ifdef F_SETLKW64
+ case F_SETLKW64:
+#endif
+ flags = 0;
+ break;
+ case F_SETLK:
+#ifdef F_SETLK64
+ case F_SETLK64:
+#endif
+ flags = LDLM_FL_BLOCK_NOWAIT;
+ break;
+ case F_GETLK:
+#ifdef F_GETLK64
+ case F_GETLK64:
+#endif
+ flags = LDLM_FL_TEST_LOCK;
+ /* Save the old mode so that if the mode in the lock changes we
+ * can decrement the appropriate reader or writer refcount. */
+ file_lock->fl_type = einfo.ei_mode;
+ break;
+ default:
+ CERROR("unknown fcntl lock command: %d\n", cmd);
+ return -EINVAL;
+ }
+
+ op_data = ll_prep_md_op_data(NULL, inode, NULL, NULL, 0, 0,
+ LUSTRE_OPC_ANY, NULL);
+ if (IS_ERR(op_data))
+ return PTR_ERR(op_data);
+
+ CDEBUG(D_DLMTRACE, "inode=%lu, pid=%u, flags=%#llx, mode=%u, start=%llu, end=%llu\n",
+ inode->i_ino, flock.l_flock.pid, flags, einfo.ei_mode,
+ flock.l_flock.start, flock.l_flock.end);
+
+ rc = md_enqueue(sbi->ll_md_exp, &einfo, NULL,
+ op_data, &lockh, &flock, 0, NULL /* req */, flags);
+
+ if ((file_lock->fl_flags & FL_FLOCK) &&
+ (rc == 0 || file_lock->fl_type == F_UNLCK))
+ rc2 = flock_lock_file_wait(file, file_lock);
+ if ((file_lock->fl_flags & FL_POSIX) &&
+ (rc == 0 || file_lock->fl_type == F_UNLCK) &&
+ !(flags & LDLM_FL_TEST_LOCK))
+ rc2 = posix_lock_file_wait(file, file_lock);
+
+ if (rc2 && file_lock->fl_type != F_UNLCK) {
+ einfo.ei_mode = LCK_NL;
+ md_enqueue(sbi->ll_md_exp, &einfo, NULL,
+ op_data, &lockh, &flock, 0, NULL /* req */, flags);
+ rc = rc2;
+ }
+
+ ll_finish_md_op_data(op_data);
+
+ return rc;
+}
+
+static int
+ll_file_noflock(struct file *file, int cmd, struct file_lock *file_lock)
+{
+ return -ENOSYS;
+}
+
+/**
+ * test if some locks matching bits and l_req_mode are acquired
+ * - bits can be in different locks
+ * - if found clear the common lock bits in *bits
+ * - the bits not found, are kept in *bits
+ * \param inode [IN]
+ * \param bits [IN] searched lock bits [IN]
+ * \param l_req_mode [IN] searched lock mode
+ * \retval boolean, true iff all bits are found
+ */
+int ll_have_md_lock(struct inode *inode, __u64 *bits, ldlm_mode_t l_req_mode)
+{
+ struct lustre_handle lockh;
+ ldlm_policy_data_t policy;
+ ldlm_mode_t mode = (l_req_mode == LCK_MINMODE) ?
+ (LCK_CR|LCK_CW|LCK_PR|LCK_PW) : l_req_mode;
+ struct lu_fid *fid;
+ __u64 flags;
+ int i;
+
+ if (!inode)
+ return 0;
+
+ fid = &ll_i2info(inode)->lli_fid;
+ CDEBUG(D_INFO, "trying to match res "DFID" mode %s\n", PFID(fid),
+ ldlm_lockname[mode]);
+
+ flags = LDLM_FL_BLOCK_GRANTED | LDLM_FL_CBPENDING | LDLM_FL_TEST_LOCK;
+ for (i = 0; i <= MDS_INODELOCK_MAXSHIFT && *bits != 0; i++) {
+ policy.l_inodebits.bits = *bits & (1 << i);
+ if (policy.l_inodebits.bits == 0)
+ continue;
+
+ if (md_lock_match(ll_i2mdexp(inode), flags, fid, LDLM_IBITS,
+ &policy, mode, &lockh)) {
+ struct ldlm_lock *lock;
+
+ lock = ldlm_handle2lock(&lockh);
+ if (lock) {
+ *bits &=
+ ~(lock->l_policy_data.l_inodebits.bits);
+ LDLM_LOCK_PUT(lock);
+ } else {
+ *bits &= ~policy.l_inodebits.bits;
+ }
+ }
+ }
+ return *bits == 0;
+}
+
+ldlm_mode_t ll_take_md_lock(struct inode *inode, __u64 bits,
+ struct lustre_handle *lockh, __u64 flags,
+ ldlm_mode_t mode)
+{
+ ldlm_policy_data_t policy = { .l_inodebits = {bits} };
+ struct lu_fid *fid;
+ ldlm_mode_t rc;
+
+ fid = &ll_i2info(inode)->lli_fid;
+ CDEBUG(D_INFO, "trying to match res "DFID"\n", PFID(fid));
+
+ rc = md_lock_match(ll_i2mdexp(inode), LDLM_FL_BLOCK_GRANTED|flags,
+ fid, LDLM_IBITS, &policy, mode, lockh);
+
+ return rc;
+}
+
+static int ll_inode_revalidate_fini(struct inode *inode, int rc)
+{
+ /* Already unlinked. Just update nlink and return success */
+ if (rc == -ENOENT) {
+ clear_nlink(inode);
+ /* This path cannot be hit for regular files unless in
+ * case of obscure races, so no need to validate size.
+ */
+ if (!S_ISREG(inode->i_mode) && !S_ISDIR(inode->i_mode))
+ return 0;
+ } else if (rc != 0) {
+ CDEBUG_LIMIT((rc == -EACCES || rc == -EIDRM) ? D_INFO : D_ERROR,
+ "%s: revalidate FID "DFID" error: rc = %d\n",
+ ll_get_fsname(inode->i_sb, NULL, 0),
+ PFID(ll_inode2fid(inode)), rc);
+ }
+
+ return rc;
+}
+
+static int __ll_inode_revalidate(struct dentry *dentry, __u64 ibits)
+{
+ struct inode *inode = d_inode(dentry);
+ struct ptlrpc_request *req = NULL;
+ struct obd_export *exp;
+ int rc = 0;
+
+ LASSERT(inode != NULL);
+
+ CDEBUG(D_VFSTRACE, "VFS Op:inode=%lu/%u(%p),name=%pd\n",
+ inode->i_ino, inode->i_generation, inode, dentry);
+
+ exp = ll_i2mdexp(inode);
+
+ /* XXX: Enable OBD_CONNECT_ATTRFID to reduce unnecessary getattr RPC.
+ * But under CMD case, it caused some lock issues, should be fixed
+ * with new CMD ibits lock. See bug 12718 */
+ if (exp_connect_flags(exp) & OBD_CONNECT_ATTRFID) {
+ struct lookup_intent oit = { .it_op = IT_GETATTR };
+ struct md_op_data *op_data;
+
+ if (ibits == MDS_INODELOCK_LOOKUP)
+ oit.it_op = IT_LOOKUP;
+
+ /* Call getattr by fid, so do not provide name at all. */
+ op_data = ll_prep_md_op_data(NULL, inode,
+ inode, NULL, 0, 0,
+ LUSTRE_OPC_ANY, NULL);
+ if (IS_ERR(op_data))
+ return PTR_ERR(op_data);
+
+ oit.it_create_mode |= M_CHECK_STALE;
+ rc = md_intent_lock(exp, op_data, NULL, 0,
+ /* we are not interested in name
+ based lookup */
+ &oit, 0, &req,
+ ll_md_blocking_ast, 0);
+ ll_finish_md_op_data(op_data);
+ oit.it_create_mode &= ~M_CHECK_STALE;
+ if (rc < 0) {
+ rc = ll_inode_revalidate_fini(inode, rc);
+ goto out;
+ }
+
+ rc = ll_revalidate_it_finish(req, &oit, inode);
+ if (rc != 0) {
+ ll_intent_release(&oit);
+ goto out;
+ }
+
+ /* Unlinked? Unhash dentry, so it is not picked up later by
+ do_lookup() -> ll_revalidate_it(). We cannot use d_drop
+ here to preserve get_cwd functionality on 2.6.
+ Bug 10503 */
+ if (!d_inode(dentry)->i_nlink)
+ d_lustre_invalidate(dentry, 0);
+
+ ll_lookup_finish_locks(&oit, inode);
+ } else if (!ll_have_md_lock(d_inode(dentry), &ibits, LCK_MINMODE)) {
+ struct ll_sb_info *sbi = ll_i2sbi(d_inode(dentry));
+ u64 valid = OBD_MD_FLGETATTR;
+ struct md_op_data *op_data;
+ int ealen = 0;
+
+ if (S_ISREG(inode->i_mode)) {
+ rc = ll_get_default_mdsize(sbi, &ealen);
+ if (rc)
+ return rc;
+ valid |= OBD_MD_FLEASIZE | OBD_MD_FLMODEASIZE;
+ }
+
+ op_data = ll_prep_md_op_data(NULL, inode, NULL, NULL,
+ 0, ealen, LUSTRE_OPC_ANY,
+ NULL);
+ if (IS_ERR(op_data))
+ return PTR_ERR(op_data);
+
+ op_data->op_valid = valid;
+ /* Once OBD_CONNECT_ATTRFID is not supported, we can't find one
+ * capa for this inode. Because we only keep capas of dirs
+ * fresh. */
+ rc = md_getattr(sbi->ll_md_exp, op_data, &req);
+ ll_finish_md_op_data(op_data);
+ if (rc) {
+ rc = ll_inode_revalidate_fini(inode, rc);
+ return rc;
+ }
+
+ rc = ll_prep_inode(&inode, req, NULL, NULL);
+ }
+out:
+ ptlrpc_req_finished(req);
+ return rc;
+}
+
+static int ll_inode_revalidate(struct dentry *dentry, __u64 ibits)
+{
+ struct inode *inode = d_inode(dentry);
+ int rc;
+
+ rc = __ll_inode_revalidate(dentry, ibits);
+ if (rc != 0)
+ return rc;
+
+ /* if object isn't regular file, don't validate size */
+ if (!S_ISREG(inode->i_mode)) {
+ LTIME_S(inode->i_atime) = ll_i2info(inode)->lli_lvb.lvb_atime;
+ LTIME_S(inode->i_mtime) = ll_i2info(inode)->lli_lvb.lvb_mtime;
+ LTIME_S(inode->i_ctime) = ll_i2info(inode)->lli_lvb.lvb_ctime;
+ } else {
+ /* In case of restore, the MDT has the right size and has
+ * already send it back without granting the layout lock,
+ * inode is up-to-date so glimpse is useless.
+ * Also to glimpse we need the layout, in case of a running
+ * restore the MDT holds the layout lock so the glimpse will
+ * block up to the end of restore (getattr will block)
+ */
+ if (!(ll_i2info(inode)->lli_flags & LLIF_FILE_RESTORING))
+ rc = ll_glimpse_size(inode);
+ }
+ return rc;
+}
+
+int ll_getattr(struct vfsmount *mnt, struct dentry *de, struct kstat *stat)
+{
+ struct inode *inode = d_inode(de);
+ struct ll_sb_info *sbi = ll_i2sbi(inode);
+ struct ll_inode_info *lli = ll_i2info(inode);
+ int res = 0;
+
+ res = ll_inode_revalidate(de, MDS_INODELOCK_UPDATE |
+ MDS_INODELOCK_LOOKUP);
+ ll_stats_ops_tally(sbi, LPROC_LL_GETATTR, 1);
+
+ if (res)
+ return res;
+
+ stat->dev = inode->i_sb->s_dev;
+ if (ll_need_32bit_api(sbi))
+ stat->ino = cl_fid_build_ino(&lli->lli_fid, 1);
+ else
+ stat->ino = inode->i_ino;
+ stat->mode = inode->i_mode;
+ stat->nlink = inode->i_nlink;
+ stat->uid = inode->i_uid;
+ stat->gid = inode->i_gid;
+ stat->rdev = inode->i_rdev;
+ stat->atime = inode->i_atime;
+ stat->mtime = inode->i_mtime;
+ stat->ctime = inode->i_ctime;
+ stat->blksize = 1 << inode->i_blkbits;
+
+ stat->size = i_size_read(inode);
+ stat->blocks = inode->i_blocks;
+
+ return 0;
+}
+
+static int ll_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo,
+ __u64 start, __u64 len)
+{
+ int rc;
+ size_t num_bytes;
+ struct ll_user_fiemap *fiemap;
+ unsigned int extent_count = fieinfo->fi_extents_max;
+
+ num_bytes = sizeof(*fiemap) + (extent_count *
+ sizeof(struct ll_fiemap_extent));
+ OBD_ALLOC_LARGE(fiemap, num_bytes);
+
+ if (fiemap == NULL)
+ return -ENOMEM;
+
+ fiemap->fm_flags = fieinfo->fi_flags;
+ fiemap->fm_extent_count = fieinfo->fi_extents_max;
+ fiemap->fm_start = start;
+ fiemap->fm_length = len;
+ if (extent_count > 0)
+ memcpy(&fiemap->fm_extents[0], fieinfo->fi_extents_start,
+ sizeof(struct ll_fiemap_extent));
+
+ rc = ll_do_fiemap(inode, fiemap, num_bytes);
+
+ fieinfo->fi_flags = fiemap->fm_flags;
+ fieinfo->fi_extents_mapped = fiemap->fm_mapped_extents;
+ if (extent_count > 0)
+ memcpy(fieinfo->fi_extents_start, &fiemap->fm_extents[0],
+ fiemap->fm_mapped_extents *
+ sizeof(struct ll_fiemap_extent));
+
+ OBD_FREE_LARGE(fiemap, num_bytes);
+ return rc;
+}
+
+struct posix_acl *ll_get_acl(struct inode *inode, int type)
+{
+ struct ll_inode_info *lli = ll_i2info(inode);
+ struct posix_acl *acl = NULL;
+
+ spin_lock(&lli->lli_lock);
+ /* VFS' acl_permission_check->check_acl will release the refcount */
+ acl = posix_acl_dup(lli->lli_posix_acl);
+ spin_unlock(&lli->lli_lock);
+
+ return acl;
+}
+
+
+int ll_inode_permission(struct inode *inode, int mask)
+{
+ int rc = 0;
+
+#ifdef MAY_NOT_BLOCK
+ if (mask & MAY_NOT_BLOCK)
+ return -ECHILD;
+#endif
+
+ /* as root inode are NOT getting validated in lookup operation,
+ * need to do it before permission check. */
+
+ if (is_root_inode(inode)) {
+ rc = __ll_inode_revalidate(inode->i_sb->s_root,
+ MDS_INODELOCK_LOOKUP);
+ if (rc)
+ return rc;
+ }
+
+ CDEBUG(D_VFSTRACE, "VFS Op:inode=%lu/%u(%p), inode mode %x mask %o\n",
+ inode->i_ino, inode->i_generation, inode, inode->i_mode, mask);
+
+ if (ll_i2sbi(inode)->ll_flags & LL_SBI_RMT_CLIENT)
+ return lustre_check_remote_perm(inode, mask);
+
+ ll_stats_ops_tally(ll_i2sbi(inode), LPROC_LL_INODE_PERM, 1);
+ rc = generic_permission(inode, mask);
+
+ return rc;
+}
+
+/* -o localflock - only provides locally consistent flock locks */
+struct file_operations ll_file_operations = {
+ .read_iter = ll_file_read_iter,
+ .write_iter = ll_file_write_iter,
+ .unlocked_ioctl = ll_file_ioctl,
+ .open = ll_file_open,
+ .release = ll_file_release,
+ .mmap = ll_file_mmap,
+ .llseek = ll_file_seek,
+ .splice_read = ll_file_splice_read,
+ .fsync = ll_fsync,
+ .flush = ll_flush
+};
+
+struct file_operations ll_file_operations_flock = {
+ .read_iter = ll_file_read_iter,
+ .write_iter = ll_file_write_iter,
+ .unlocked_ioctl = ll_file_ioctl,
+ .open = ll_file_open,
+ .release = ll_file_release,
+ .mmap = ll_file_mmap,
+ .llseek = ll_file_seek,
+ .splice_read = ll_file_splice_read,
+ .fsync = ll_fsync,
+ .flush = ll_flush,
+ .flock = ll_file_flock,
+ .lock = ll_file_flock
+};
+
+/* These are for -o noflock - to return ENOSYS on flock calls */
+struct file_operations ll_file_operations_noflock = {
+ .read_iter = ll_file_read_iter,
+ .write_iter = ll_file_write_iter,
+ .unlocked_ioctl = ll_file_ioctl,
+ .open = ll_file_open,
+ .release = ll_file_release,
+ .mmap = ll_file_mmap,
+ .llseek = ll_file_seek,
+ .splice_read = ll_file_splice_read,
+ .fsync = ll_fsync,
+ .flush = ll_flush,
+ .flock = ll_file_noflock,
+ .lock = ll_file_noflock
+};
+
+struct inode_operations ll_file_inode_operations = {
+ .setattr = ll_setattr,
+ .getattr = ll_getattr,
+ .permission = ll_inode_permission,
+ .setxattr = ll_setxattr,
+ .getxattr = ll_getxattr,
+ .listxattr = ll_listxattr,
+ .removexattr = ll_removexattr,
+ .fiemap = ll_fiemap,
+ .get_acl = ll_get_acl,
+};
+
+/* dynamic ioctl number support routines */
+static struct llioc_ctl_data {
+ struct rw_semaphore ioc_sem;
+ struct list_head ioc_head;
+} llioc = {
+ __RWSEM_INITIALIZER(llioc.ioc_sem),
+ LIST_HEAD_INIT(llioc.ioc_head)
+};
+
+
+struct llioc_data {
+ struct list_head iocd_list;
+ unsigned int iocd_size;
+ llioc_callback_t iocd_cb;
+ unsigned int iocd_count;
+ unsigned int iocd_cmd[0];
+};
+
+void *ll_iocontrol_register(llioc_callback_t cb, int count, unsigned int *cmd)
+{
+ unsigned int size;
+ struct llioc_data *in_data = NULL;
+
+ if (cb == NULL || cmd == NULL ||
+ count > LLIOC_MAX_CMD || count < 0)
+ return NULL;
+
+ size = sizeof(*in_data) + count * sizeof(unsigned int);
+ in_data = kzalloc(size, GFP_NOFS);
+ if (!in_data)
+ return NULL;
+
+ memset(in_data, 0, sizeof(*in_data));
+ in_data->iocd_size = size;
+ in_data->iocd_cb = cb;
+ in_data->iocd_count = count;
+ memcpy(in_data->iocd_cmd, cmd, sizeof(unsigned int) * count);
+
+ down_write(&llioc.ioc_sem);
+ list_add_tail(&in_data->iocd_list, &llioc.ioc_head);
+ up_write(&llioc.ioc_sem);
+
+ return in_data;
+}
+EXPORT_SYMBOL(ll_iocontrol_register);
+
+void ll_iocontrol_unregister(void *magic)
+{
+ struct llioc_data *tmp;
+
+ if (magic == NULL)
+ return;
+
+ down_write(&llioc.ioc_sem);
+ list_for_each_entry(tmp, &llioc.ioc_head, iocd_list) {
+ if (tmp == magic) {
+ unsigned int size = tmp->iocd_size;
+
+ list_del(&tmp->iocd_list);
+ up_write(&llioc.ioc_sem);
+
+ OBD_FREE(tmp, size);
+ return;
+ }
+ }
+ up_write(&llioc.ioc_sem);
+
+ CWARN("didn't find iocontrol register block with magic: %p\n", magic);
+}
+EXPORT_SYMBOL(ll_iocontrol_unregister);
+
+static enum llioc_iter
+ll_iocontrol_call(struct inode *inode, struct file *file,
+ unsigned int cmd, unsigned long arg, int *rcp)
+{
+ enum llioc_iter ret = LLIOC_CONT;
+ struct llioc_data *data;
+ int rc = -EINVAL, i;
+
+ down_read(&llioc.ioc_sem);
+ list_for_each_entry(data, &llioc.ioc_head, iocd_list) {
+ for (i = 0; i < data->iocd_count; i++) {
+ if (cmd != data->iocd_cmd[i])
+ continue;
+
+ ret = data->iocd_cb(inode, file, cmd, arg, data, &rc);
+ break;
+ }
+
+ if (ret == LLIOC_STOP)
+ break;
+ }
+ up_read(&llioc.ioc_sem);
+
+ if (rcp)
+ *rcp = rc;
+ return ret;
+}
+
+int ll_layout_conf(struct inode *inode, const struct cl_object_conf *conf)
+{
+ struct ll_inode_info *lli = ll_i2info(inode);
+ struct cl_env_nest nest;
+ struct lu_env *env;
+ int result;
+
+ if (lli->lli_clob == NULL)
+ return 0;
+
+ env = cl_env_nested_get(&nest);
+ if (IS_ERR(env))
+ return PTR_ERR(env);
+
+ result = cl_conf_set(env, lli->lli_clob, conf);
+ cl_env_nested_put(&nest, env);
+
+ if (conf->coc_opc == OBJECT_CONF_SET) {
+ struct ldlm_lock *lock = conf->coc_lock;
+
+ LASSERT(lock != NULL);
+ LASSERT(ldlm_has_layout(lock));
+ if (result == 0) {
+ /* it can only be allowed to match after layout is
+ * applied to inode otherwise false layout would be
+ * seen. Applying layout should happen before dropping
+ * the intent lock. */
+ ldlm_lock_allow_match(lock);
+ }
+ }
+ return result;
+}
+
+/* Fetch layout from MDT with getxattr request, if it's not ready yet */
+static int ll_layout_fetch(struct inode *inode, struct ldlm_lock *lock)
+
+{
+ struct ll_sb_info *sbi = ll_i2sbi(inode);
+ struct obd_capa *oc;
+ struct ptlrpc_request *req;
+ struct mdt_body *body;
+ void *lvbdata;
+ void *lmm;
+ int lmmsize;
+ int rc;
+
+ CDEBUG(D_INODE, DFID" LVB_READY=%d l_lvb_data=%p l_lvb_len=%d\n",
+ PFID(ll_inode2fid(inode)), !!(lock->l_flags & LDLM_FL_LVB_READY),
+ lock->l_lvb_data, lock->l_lvb_len);
+
+ if ((lock->l_lvb_data != NULL) && (lock->l_flags & LDLM_FL_LVB_READY))
+ return 0;
+
+ /* if layout lock was granted right away, the layout is returned
+ * within DLM_LVB of dlm reply; otherwise if the lock was ever
+ * blocked and then granted via completion ast, we have to fetch
+ * layout here. Please note that we can't use the LVB buffer in
+ * completion AST because it doesn't have a large enough buffer */
+ oc = ll_mdscapa_get(inode);
+ rc = ll_get_default_mdsize(sbi, &lmmsize);
+ if (rc == 0)
+ rc = md_getxattr(sbi->ll_md_exp, ll_inode2fid(inode), oc,
+ OBD_MD_FLXATTR, XATTR_NAME_LOV, NULL, 0,
+ lmmsize, 0, &req);
+ capa_put(oc);
+ if (rc < 0)
+ return rc;
+
+ body = req_capsule_server_get(&req->rq_pill, &RMF_MDT_BODY);
+ if (body == NULL) {
+ rc = -EPROTO;
+ goto out;
+ }
+
+ lmmsize = body->eadatasize;
+ if (lmmsize == 0) /* empty layout */ {
+ rc = 0;
+ goto out;
+ }
+
+ lmm = req_capsule_server_sized_get(&req->rq_pill, &RMF_EADATA, lmmsize);
+ if (lmm == NULL) {
+ rc = -EFAULT;
+ goto out;
+ }
+
+ OBD_ALLOC_LARGE(lvbdata, lmmsize);
+ if (lvbdata == NULL) {
+ rc = -ENOMEM;
+ goto out;
+ }
+
+ memcpy(lvbdata, lmm, lmmsize);
+ lock_res_and_lock(lock);
+ if (lock->l_lvb_data != NULL)
+ OBD_FREE_LARGE(lock->l_lvb_data, lock->l_lvb_len);
+
+ lock->l_lvb_data = lvbdata;
+ lock->l_lvb_len = lmmsize;
+ unlock_res_and_lock(lock);
+
+out:
+ ptlrpc_req_finished(req);
+ return rc;
+}
+
+/**
+ * Apply the layout to the inode. Layout lock is held and will be released
+ * in this function.
+ */
+static int ll_layout_lock_set(struct lustre_handle *lockh, ldlm_mode_t mode,
+ struct inode *inode, __u32 *gen, bool reconf)
+{
+ struct ll_inode_info *lli = ll_i2info(inode);
+ struct ll_sb_info *sbi = ll_i2sbi(inode);
+ struct ldlm_lock *lock;
+ struct lustre_md md = { NULL };
+ struct cl_object_conf conf;
+ int rc = 0;
+ bool lvb_ready;
+ bool wait_layout = false;
+
+ LASSERT(lustre_handle_is_used(lockh));
+
+ lock = ldlm_handle2lock(lockh);
+ LASSERT(lock != NULL);
+ LASSERT(ldlm_has_layout(lock));
+
+ LDLM_DEBUG(lock, "File %p/"DFID" being reconfigured: %d.\n",
+ inode, PFID(&lli->lli_fid), reconf);
+
+ /* in case this is a caching lock and reinstate with new inode */
+ md_set_lock_data(sbi->ll_md_exp, &lockh->cookie, inode, NULL);
+
+ lock_res_and_lock(lock);
+ lvb_ready = !!(lock->l_flags & LDLM_FL_LVB_READY);
+ unlock_res_and_lock(lock);
+ /* checking lvb_ready is racy but this is okay. The worst case is
+ * that multi processes may configure the file on the same time. */
+ if (lvb_ready || !reconf) {
+ rc = -ENODATA;
+ if (lvb_ready) {
+ /* layout_gen must be valid if layout lock is not
+ * cancelled and stripe has already set */
+ *gen = ll_layout_version_get(lli);
+ rc = 0;
+ }
+ goto out;
+ }
+
+ rc = ll_layout_fetch(inode, lock);
+ if (rc < 0)
+ goto out;
+
+ /* for layout lock, lmm is returned in lock's lvb.
+ * lvb_data is immutable if the lock is held so it's safe to access it
+ * without res lock. See the description in ldlm_lock_decref_internal()
+ * for the condition to free lvb_data of layout lock */
+ if (lock->l_lvb_data != NULL) {
+ rc = obd_unpackmd(sbi->ll_dt_exp, &md.lsm,
+ lock->l_lvb_data, lock->l_lvb_len);
+ if (rc >= 0) {
+ *gen = LL_LAYOUT_GEN_EMPTY;
+ if (md.lsm != NULL)
+ *gen = md.lsm->lsm_layout_gen;
+ rc = 0;
+ } else {
+ CERROR("%s: file "DFID" unpackmd error: %d\n",
+ ll_get_fsname(inode->i_sb, NULL, 0),
+ PFID(&lli->lli_fid), rc);
+ }
+ }
+ if (rc < 0)
+ goto out;
+
+ /* set layout to file. Unlikely this will fail as old layout was
+ * surely eliminated */
+ memset(&conf, 0, sizeof(conf));
+ conf.coc_opc = OBJECT_CONF_SET;
+ conf.coc_inode = inode;
+ conf.coc_lock = lock;
+ conf.u.coc_md = &md;
+ rc = ll_layout_conf(inode, &conf);
+
+ if (md.lsm != NULL)
+ obd_free_memmd(sbi->ll_dt_exp, &md.lsm);
+
+ /* refresh layout failed, need to wait */
+ wait_layout = rc == -EBUSY;
+
+out:
+ LDLM_LOCK_PUT(lock);
+ ldlm_lock_decref(lockh, mode);
+
+ /* wait for IO to complete if it's still being used. */
+ if (wait_layout) {
+ CDEBUG(D_INODE, "%s: %p/"DFID" wait for layout reconf.\n",
+ ll_get_fsname(inode->i_sb, NULL, 0),
+ inode, PFID(&lli->lli_fid));
+
+ memset(&conf, 0, sizeof(conf));
+ conf.coc_opc = OBJECT_CONF_WAIT;
+ conf.coc_inode = inode;
+ rc = ll_layout_conf(inode, &conf);
+ if (rc == 0)
+ rc = -EAGAIN;
+
+ CDEBUG(D_INODE, "file: "DFID" waiting layout return: %d.\n",
+ PFID(&lli->lli_fid), rc);
+ }
+ return rc;
+}
+
+/**
+ * This function checks if there exists a LAYOUT lock on the client side,
+ * or enqueues it if it doesn't have one in cache.
+ *
+ * This function will not hold layout lock so it may be revoked any time after
+ * this function returns. Any operations depend on layout should be redone
+ * in that case.
+ *
+ * This function should be called before lov_io_init() to get an uptodate
+ * layout version, the caller should save the version number and after IO
+ * is finished, this function should be called again to verify that layout
+ * is not changed during IO time.
+ */
+int ll_layout_refresh(struct inode *inode, __u32 *gen)
+{
+ struct ll_inode_info *lli = ll_i2info(inode);
+ struct ll_sb_info *sbi = ll_i2sbi(inode);
+ struct md_op_data *op_data;
+ struct lookup_intent it;
+ struct lustre_handle lockh;
+ ldlm_mode_t mode;
+ struct ldlm_enqueue_info einfo = {
+ .ei_type = LDLM_IBITS,
+ .ei_mode = LCK_CR,
+ .ei_cb_bl = ll_md_blocking_ast,
+ .ei_cb_cp = ldlm_completion_ast,
+ };
+ int rc;
+
+ *gen = ll_layout_version_get(lli);
+ if (!(sbi->ll_flags & LL_SBI_LAYOUT_LOCK) || *gen != LL_LAYOUT_GEN_NONE)
+ return 0;
+
+ /* sanity checks */
+ LASSERT(fid_is_sane(ll_inode2fid(inode)));
+ LASSERT(S_ISREG(inode->i_mode));
+
+ /* take layout lock mutex to enqueue layout lock exclusively. */
+ mutex_lock(&lli->lli_layout_mutex);
+
+again:
+ /* mostly layout lock is caching on the local side, so try to match
+ * it before grabbing layout lock mutex. */
+ mode = ll_take_md_lock(inode, MDS_INODELOCK_LAYOUT, &lockh, 0,
+ LCK_CR | LCK_CW | LCK_PR | LCK_PW);
+ if (mode != 0) { /* hit cached lock */
+ rc = ll_layout_lock_set(&lockh, mode, inode, gen, true);
+ if (rc == -EAGAIN)
+ goto again;
+
+ mutex_unlock(&lli->lli_layout_mutex);
+ return rc;
+ }
+
+ op_data = ll_prep_md_op_data(NULL, inode, inode, NULL,
+ 0, 0, LUSTRE_OPC_ANY, NULL);
+ if (IS_ERR(op_data)) {
+ mutex_unlock(&lli->lli_layout_mutex);
+ return PTR_ERR(op_data);
+ }
+
+ /* have to enqueue one */
+ memset(&it, 0, sizeof(it));
+ it.it_op = IT_LAYOUT;
+ lockh.cookie = 0ULL;
+
+ LDLM_DEBUG_NOLOCK("%s: requeue layout lock for file %p/"DFID".\n",
+ ll_get_fsname(inode->i_sb, NULL, 0), inode,
+ PFID(&lli->lli_fid));
+
+ rc = md_enqueue(sbi->ll_md_exp, &einfo, &it, op_data, &lockh,
+ NULL, 0, NULL, 0);
+ if (it.d.lustre.it_data != NULL)
+ ptlrpc_req_finished(it.d.lustre.it_data);
+ it.d.lustre.it_data = NULL;
+
+ ll_finish_md_op_data(op_data);
+
+ mode = it.d.lustre.it_lock_mode;
+ it.d.lustre.it_lock_mode = 0;
+ ll_intent_drop_lock(&it);
+
+ if (rc == 0) {
+ /* set lock data in case this is a new lock */
+ ll_set_lock_data(sbi->ll_md_exp, inode, &it, NULL);
+ rc = ll_layout_lock_set(&lockh, mode, inode, gen, true);
+ if (rc == -EAGAIN)
+ goto again;
+ }
+ mutex_unlock(&lli->lli_layout_mutex);
+
+ return rc;
+}
+
+/**
+ * This function send a restore request to the MDT
+ */
+int ll_layout_restore(struct inode *inode)
+{
+ struct hsm_user_request *hur;
+ int len, rc;
+
+ len = sizeof(struct hsm_user_request) +
+ sizeof(struct hsm_user_item);
+ hur = kzalloc(len, GFP_NOFS);
+ if (!hur)
+ return -ENOMEM;
+
+ hur->hur_request.hr_action = HUA_RESTORE;
+ hur->hur_request.hr_archive_id = 0;
+ hur->hur_request.hr_flags = 0;
+ memcpy(&hur->hur_user_item[0].hui_fid, &ll_i2info(inode)->lli_fid,
+ sizeof(hur->hur_user_item[0].hui_fid));
+ hur->hur_user_item[0].hui_extent.length = -1;
+ hur->hur_request.hr_itemcount = 1;
+ rc = obd_iocontrol(LL_IOC_HSM_REQUEST, cl_i2sbi(inode)->ll_md_exp,
+ len, hur, NULL);
+ OBD_FREE(hur, len);
+ return rc;
+}
diff --git a/drivers/staging/lustre/lustre/llite/llite_capa.c b/drivers/staging/lustre/lustre/llite/llite_capa.c
new file mode 100644
index 000000000..aec9a4412
--- /dev/null
+++ b/drivers/staging/lustre/lustre/llite/llite_capa.c
@@ -0,0 +1,654 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2011, 2012, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ *
+ * lustre/llite/llite_capa.c
+ *
+ * Author: Lai Siyao <lsy@clusterfs.com>
+ */
+
+#define DEBUG_SUBSYSTEM S_LLITE
+
+#include <linux/fs.h>
+#include <linux/uaccess.h>
+#include <linux/file.h>
+#include <linux/kmod.h>
+
+#include "../include/lustre_lite.h"
+#include "llite_internal.h"
+
+/* for obd_capa.c_list, client capa might stay in three places:
+ * 1. ll_capa_list.
+ * 2. ll_idle_capas.
+ * 3. stand alone: just allocated.
+ */
+
+/* capas for oss writeback and those failed to renew */
+static LIST_HEAD(ll_idle_capas);
+static struct ptlrpc_thread ll_capa_thread;
+static struct list_head *ll_capa_list = &capa_list[CAPA_SITE_CLIENT];
+
+/* llite capa renewal timer */
+struct timer_list ll_capa_timer;
+/* for debug: indicate whether capa on llite is enabled or not */
+static atomic_t ll_capa_debug = ATOMIC_INIT(0);
+static unsigned long long ll_capa_renewed;
+static unsigned long long ll_capa_renewal_noent;
+static unsigned long long ll_capa_renewal_failed;
+static unsigned long long ll_capa_renewal_retries;
+
+static int ll_update_capa(struct obd_capa *ocapa, struct lustre_capa *capa);
+
+static inline void update_capa_timer(struct obd_capa *ocapa, unsigned long expiry)
+{
+ if (time_before(expiry, ll_capa_timer.expires) ||
+ !timer_pending(&ll_capa_timer)) {
+ mod_timer(&ll_capa_timer, expiry);
+ DEBUG_CAPA(D_SEC, &ocapa->c_capa,
+ "ll_capa_timer update: %lu/%lu by", expiry, jiffies);
+ }
+}
+
+static inline unsigned long capa_renewal_time(struct obd_capa *ocapa)
+{
+ return cfs_time_sub(ocapa->c_expiry,
+ cfs_time_seconds(ocapa->c_capa.lc_timeout) / 2);
+}
+
+static inline int capa_is_to_expire(struct obd_capa *ocapa)
+{
+ return time_before_eq(capa_renewal_time(ocapa), cfs_time_current());
+}
+
+static inline int have_expired_capa(void)
+{
+ struct obd_capa *ocapa = NULL;
+ int expired = 0;
+
+ /* if ll_capa_list has client capa to expire or ll_idle_capas has
+ * expired capa, return 1.
+ */
+ spin_lock(&capa_lock);
+ if (!list_empty(ll_capa_list)) {
+ ocapa = list_entry(ll_capa_list->next, struct obd_capa,
+ c_list);
+ expired = capa_is_to_expire(ocapa);
+ if (!expired)
+ update_capa_timer(ocapa, capa_renewal_time(ocapa));
+ } else if (!list_empty(&ll_idle_capas)) {
+ ocapa = list_entry(ll_idle_capas.next, struct obd_capa,
+ c_list);
+ expired = capa_is_expired(ocapa);
+ if (!expired)
+ update_capa_timer(ocapa, ocapa->c_expiry);
+ }
+ spin_unlock(&capa_lock);
+
+ if (expired)
+ DEBUG_CAPA(D_SEC, &ocapa->c_capa, "expired");
+ return expired;
+}
+
+static void sort_add_capa(struct obd_capa *ocapa, struct list_head *head)
+{
+ struct obd_capa *tmp;
+ struct list_head *before = NULL;
+
+ /* TODO: client capa is sorted by expiry, this could be optimized */
+ list_for_each_entry_reverse(tmp, head, c_list) {
+ if (cfs_time_aftereq(ocapa->c_expiry, tmp->c_expiry)) {
+ before = &tmp->c_list;
+ break;
+ }
+ }
+
+ LASSERT(&ocapa->c_list != before);
+ list_add(&ocapa->c_list, before ?: head);
+}
+
+static inline int obd_capa_open_count(struct obd_capa *oc)
+{
+ struct ll_inode_info *lli = ll_i2info(oc->u.cli.inode);
+ return atomic_read(&lli->lli_open_count);
+}
+
+static void ll_delete_capa(struct obd_capa *ocapa)
+{
+ struct ll_inode_info *lli = ll_i2info(ocapa->u.cli.inode);
+
+ if (capa_for_mds(&ocapa->c_capa)) {
+ LASSERT(lli->lli_mds_capa == ocapa);
+ lli->lli_mds_capa = NULL;
+ } else if (capa_for_oss(&ocapa->c_capa)) {
+ list_del_init(&ocapa->u.cli.lli_list);
+ }
+
+ DEBUG_CAPA(D_SEC, &ocapa->c_capa, "free client");
+ list_del_init(&ocapa->c_list);
+ capa_count[CAPA_SITE_CLIENT]--;
+ /* release the ref when alloc */
+ capa_put(ocapa);
+}
+
+/* three places where client capa is deleted:
+ * 1. capa_thread_main(), main place to delete expired capa.
+ * 2. ll_clear_inode_capas() in ll_clear_inode().
+ * 3. ll_truncate_free_capa() delete truncate capa explicitly in ll_setattr_ost().
+ */
+static int capa_thread_main(void *unused)
+{
+ struct obd_capa *ocapa, *tmp, *next;
+ struct inode *inode = NULL;
+ struct l_wait_info lwi = { 0 };
+ int rc;
+
+ thread_set_flags(&ll_capa_thread, SVC_RUNNING);
+ wake_up(&ll_capa_thread.t_ctl_waitq);
+
+ while (1) {
+ l_wait_event(ll_capa_thread.t_ctl_waitq,
+ !thread_is_running(&ll_capa_thread) ||
+ have_expired_capa(),
+ &lwi);
+
+ if (!thread_is_running(&ll_capa_thread))
+ break;
+
+ next = NULL;
+
+ spin_lock(&capa_lock);
+ list_for_each_entry_safe(ocapa, tmp, ll_capa_list, c_list) {
+ __u64 ibits;
+
+ LASSERT(ocapa->c_capa.lc_opc != CAPA_OPC_OSS_TRUNC);
+
+ if (!capa_is_to_expire(ocapa)) {
+ next = ocapa;
+ break;
+ }
+
+ list_del_init(&ocapa->c_list);
+
+ /* for MDS capability, only renew those which belong to
+ * dir, or its inode is opened, or client holds LOOKUP
+ * lock.
+ */
+ /* ibits may be changed by ll_have_md_lock() so we have
+ * to set it each time */
+ ibits = MDS_INODELOCK_LOOKUP;
+ if (capa_for_mds(&ocapa->c_capa) &&
+ !S_ISDIR(ocapa->u.cli.inode->i_mode) &&
+ obd_capa_open_count(ocapa) == 0 &&
+ !ll_have_md_lock(ocapa->u.cli.inode,
+ &ibits, LCK_MINMODE)) {
+ DEBUG_CAPA(D_SEC, &ocapa->c_capa,
+ "skip renewal for");
+ sort_add_capa(ocapa, &ll_idle_capas);
+ continue;
+ }
+
+ /* for OSS capability, only renew those whose inode is
+ * opened.
+ */
+ if (capa_for_oss(&ocapa->c_capa) &&
+ obd_capa_open_count(ocapa) == 0) {
+ /* oss capa with open count == 0 won't renew,
+ * move to idle list */
+ sort_add_capa(ocapa, &ll_idle_capas);
+ continue;
+ }
+
+ /* NB iput() is in ll_update_capa() */
+ inode = igrab(ocapa->u.cli.inode);
+ if (inode == NULL) {
+ DEBUG_CAPA(D_ERROR, &ocapa->c_capa,
+ "igrab failed for");
+ continue;
+ }
+
+ capa_get(ocapa);
+ ll_capa_renewed++;
+ spin_unlock(&capa_lock);
+ rc = md_renew_capa(ll_i2mdexp(inode), ocapa,
+ ll_update_capa);
+ spin_lock(&capa_lock);
+ if (rc) {
+ DEBUG_CAPA(D_ERROR, &ocapa->c_capa,
+ "renew failed: %d", rc);
+ ll_capa_renewal_failed++;
+ }
+ }
+
+ if (next)
+ update_capa_timer(next, capa_renewal_time(next));
+
+ list_for_each_entry_safe(ocapa, tmp, &ll_idle_capas,
+ c_list) {
+ if (!capa_is_expired(ocapa)) {
+ if (!next)
+ update_capa_timer(ocapa,
+ ocapa->c_expiry);
+ break;
+ }
+
+ if (atomic_read(&ocapa->c_refc) > 1) {
+ DEBUG_CAPA(D_SEC, &ocapa->c_capa,
+ "expired(c_refc %d), don't release",
+ atomic_read(&ocapa->c_refc));
+ /* don't try to renew any more */
+ list_del_init(&ocapa->c_list);
+ continue;
+ }
+
+ /* expired capa is released. */
+ DEBUG_CAPA(D_SEC, &ocapa->c_capa, "release expired");
+ ll_delete_capa(ocapa);
+ }
+
+ spin_unlock(&capa_lock);
+ }
+
+ thread_set_flags(&ll_capa_thread, SVC_STOPPED);
+ wake_up(&ll_capa_thread.t_ctl_waitq);
+ return 0;
+}
+
+void ll_capa_timer_callback(unsigned long unused)
+{
+ wake_up(&ll_capa_thread.t_ctl_waitq);
+}
+
+int ll_capa_thread_start(void)
+{
+ struct task_struct *task;
+
+ init_waitqueue_head(&ll_capa_thread.t_ctl_waitq);
+
+ task = kthread_run(capa_thread_main, NULL, "ll_capa");
+ if (IS_ERR(task)) {
+ CERROR("cannot start expired capa thread: rc %ld\n",
+ PTR_ERR(task));
+ return PTR_ERR(task);
+ }
+ wait_event(ll_capa_thread.t_ctl_waitq,
+ thread_is_running(&ll_capa_thread));
+
+ return 0;
+}
+
+void ll_capa_thread_stop(void)
+{
+ thread_set_flags(&ll_capa_thread, SVC_STOPPING);
+ wake_up(&ll_capa_thread.t_ctl_waitq);
+ wait_event(ll_capa_thread.t_ctl_waitq,
+ thread_is_stopped(&ll_capa_thread));
+}
+
+struct obd_capa *ll_osscapa_get(struct inode *inode, __u64 opc)
+{
+ struct ll_inode_info *lli = ll_i2info(inode);
+ struct obd_capa *ocapa;
+ int found = 0;
+
+ if ((ll_i2sbi(inode)->ll_flags & LL_SBI_OSS_CAPA) == 0)
+ return NULL;
+
+ LASSERT(opc == CAPA_OPC_OSS_WRITE || opc == CAPA_OPC_OSS_RW ||
+ opc == CAPA_OPC_OSS_TRUNC);
+
+ spin_lock(&capa_lock);
+ list_for_each_entry(ocapa, &lli->lli_oss_capas, u.cli.lli_list) {
+ if (capa_is_expired(ocapa))
+ continue;
+ if ((opc & CAPA_OPC_OSS_WRITE) &&
+ capa_opc_supported(&ocapa->c_capa, CAPA_OPC_OSS_WRITE)) {
+ found = 1;
+ break;
+ } else if ((opc & CAPA_OPC_OSS_READ) &&
+ capa_opc_supported(&ocapa->c_capa,
+ CAPA_OPC_OSS_READ)) {
+ found = 1;
+ break;
+ } else if ((opc & CAPA_OPC_OSS_TRUNC) &&
+ capa_opc_supported(&ocapa->c_capa, opc)) {
+ found = 1;
+ break;
+ }
+ }
+
+ if (found) {
+ LASSERT(lu_fid_eq(capa_fid(&ocapa->c_capa),
+ ll_inode2fid(inode)));
+ LASSERT(ocapa->c_site == CAPA_SITE_CLIENT);
+
+ capa_get(ocapa);
+
+ DEBUG_CAPA(D_SEC, &ocapa->c_capa, "found client");
+ } else {
+ ocapa = NULL;
+
+ if (atomic_read(&ll_capa_debug)) {
+ CERROR("no capability for "DFID" opc %#llx\n",
+ PFID(&lli->lli_fid), opc);
+ atomic_set(&ll_capa_debug, 0);
+ }
+ }
+ spin_unlock(&capa_lock);
+
+ return ocapa;
+}
+EXPORT_SYMBOL(ll_osscapa_get);
+
+struct obd_capa *ll_mdscapa_get(struct inode *inode)
+{
+ struct ll_inode_info *lli = ll_i2info(inode);
+ struct obd_capa *ocapa;
+
+ LASSERT(inode != NULL);
+
+ if ((ll_i2sbi(inode)->ll_flags & LL_SBI_MDS_CAPA) == 0)
+ return NULL;
+
+ spin_lock(&capa_lock);
+ ocapa = capa_get(lli->lli_mds_capa);
+ spin_unlock(&capa_lock);
+ if (!ocapa && atomic_read(&ll_capa_debug)) {
+ CERROR("no mds capability for "DFID"\n", PFID(&lli->lli_fid));
+ atomic_set(&ll_capa_debug, 0);
+ }
+
+ return ocapa;
+}
+
+static struct obd_capa *do_add_mds_capa(struct inode *inode,
+ struct obd_capa *ocapa)
+{
+ struct ll_inode_info *lli = ll_i2info(inode);
+ struct obd_capa *old = lli->lli_mds_capa;
+ struct lustre_capa *capa = &ocapa->c_capa;
+
+ if (!old) {
+ ocapa->u.cli.inode = inode;
+ lli->lli_mds_capa = ocapa;
+ capa_count[CAPA_SITE_CLIENT]++;
+
+ DEBUG_CAPA(D_SEC, capa, "add MDS");
+ } else {
+ spin_lock(&old->c_lock);
+ old->c_capa = *capa;
+ spin_unlock(&old->c_lock);
+
+ DEBUG_CAPA(D_SEC, capa, "update MDS");
+
+ capa_put(ocapa);
+ ocapa = old;
+ }
+ return ocapa;
+}
+
+static struct obd_capa *do_lookup_oss_capa(struct inode *inode, int opc)
+{
+ struct ll_inode_info *lli = ll_i2info(inode);
+ struct obd_capa *ocapa;
+
+ /* inside capa_lock */
+ list_for_each_entry(ocapa, &lli->lli_oss_capas, u.cli.lli_list) {
+ if ((capa_opc(&ocapa->c_capa) & opc) != opc)
+ continue;
+
+ LASSERT(lu_fid_eq(capa_fid(&ocapa->c_capa),
+ ll_inode2fid(inode)));
+ LASSERT(ocapa->c_site == CAPA_SITE_CLIENT);
+
+ DEBUG_CAPA(D_SEC, &ocapa->c_capa, "found client");
+ return ocapa;
+ }
+
+ return NULL;
+}
+
+static inline void inode_add_oss_capa(struct inode *inode,
+ struct obd_capa *ocapa)
+{
+ struct ll_inode_info *lli = ll_i2info(inode);
+ struct obd_capa *tmp;
+ struct list_head *next = NULL;
+
+ /* capa is sorted in lli_oss_capas so lookup can always find the
+ * latest one */
+ list_for_each_entry(tmp, &lli->lli_oss_capas, u.cli.lli_list) {
+ if (cfs_time_after(ocapa->c_expiry, tmp->c_expiry)) {
+ next = &tmp->u.cli.lli_list;
+ break;
+ }
+ }
+ LASSERT(&ocapa->u.cli.lli_list != next);
+ list_move_tail(&ocapa->u.cli.lli_list, next ?: &lli->lli_oss_capas);
+}
+
+static struct obd_capa *do_add_oss_capa(struct inode *inode,
+ struct obd_capa *ocapa)
+{
+ struct obd_capa *old;
+ struct lustre_capa *capa = &ocapa->c_capa;
+
+ LASSERTF(S_ISREG(inode->i_mode),
+ "inode has oss capa, but not regular file, mode: %d\n",
+ inode->i_mode);
+
+ /* FIXME: can't replace it so easily with fine-grained opc */
+ old = do_lookup_oss_capa(inode, capa_opc(capa) & CAPA_OPC_OSS_ONLY);
+ if (!old) {
+ ocapa->u.cli.inode = inode;
+ INIT_LIST_HEAD(&ocapa->u.cli.lli_list);
+ capa_count[CAPA_SITE_CLIENT]++;
+
+ DEBUG_CAPA(D_SEC, capa, "add OSS");
+ } else {
+ spin_lock(&old->c_lock);
+ old->c_capa = *capa;
+ spin_unlock(&old->c_lock);
+
+ DEBUG_CAPA(D_SEC, capa, "update OSS");
+
+ capa_put(ocapa);
+ ocapa = old;
+ }
+
+ inode_add_oss_capa(inode, ocapa);
+ return ocapa;
+}
+
+struct obd_capa *ll_add_capa(struct inode *inode, struct obd_capa *ocapa)
+{
+ spin_lock(&capa_lock);
+ ocapa = capa_for_mds(&ocapa->c_capa) ? do_add_mds_capa(inode, ocapa) :
+ do_add_oss_capa(inode, ocapa);
+
+ /* truncate capa won't renew */
+ if (ocapa->c_capa.lc_opc != CAPA_OPC_OSS_TRUNC) {
+ set_capa_expiry(ocapa);
+ list_del_init(&ocapa->c_list);
+ sort_add_capa(ocapa, ll_capa_list);
+
+ update_capa_timer(ocapa, capa_renewal_time(ocapa));
+ }
+
+ spin_unlock(&capa_lock);
+
+ atomic_set(&ll_capa_debug, 1);
+ return ocapa;
+}
+
+static inline void delay_capa_renew(struct obd_capa *oc, unsigned long delay)
+{
+ /* NB: set a fake expiry for this capa to prevent it renew too soon */
+ oc->c_expiry = cfs_time_add(oc->c_expiry, cfs_time_seconds(delay));
+}
+
+static int ll_update_capa(struct obd_capa *ocapa, struct lustre_capa *capa)
+{
+ struct inode *inode = ocapa->u.cli.inode;
+ int rc = 0;
+
+ LASSERT(ocapa);
+
+ if (IS_ERR(capa)) {
+ /* set error code */
+ rc = PTR_ERR(capa);
+ spin_lock(&capa_lock);
+ if (rc == -ENOENT) {
+ DEBUG_CAPA(D_SEC, &ocapa->c_capa,
+ "renewal canceled because object removed");
+ ll_capa_renewal_noent++;
+ } else {
+ ll_capa_renewal_failed++;
+
+ /* failed capa won't be renewed any longer, but if -EIO,
+ * client might be doing recovery, retry in 2 min. */
+ if (rc == -EIO && !capa_is_expired(ocapa)) {
+ delay_capa_renew(ocapa, 120);
+ DEBUG_CAPA(D_ERROR, &ocapa->c_capa,
+ "renewal failed: -EIO, retry in 2 mins");
+ ll_capa_renewal_retries++;
+ goto retry;
+ } else {
+ DEBUG_CAPA(D_ERROR, &ocapa->c_capa,
+ "renewal failed(rc: %d) for", rc);
+ }
+ }
+
+ list_del_init(&ocapa->c_list);
+ sort_add_capa(ocapa, &ll_idle_capas);
+ spin_unlock(&capa_lock);
+
+ capa_put(ocapa);
+ iput(inode);
+ return rc;
+ }
+
+ spin_lock(&ocapa->c_lock);
+ LASSERT(!memcmp(&ocapa->c_capa, capa,
+ offsetof(struct lustre_capa, lc_opc)));
+ ocapa->c_capa = *capa;
+ set_capa_expiry(ocapa);
+ spin_unlock(&ocapa->c_lock);
+
+ spin_lock(&capa_lock);
+ if (capa_for_oss(capa))
+ inode_add_oss_capa(inode, ocapa);
+ DEBUG_CAPA(D_SEC, capa, "renew");
+retry:
+ list_del_init(&ocapa->c_list);
+ sort_add_capa(ocapa, ll_capa_list);
+ update_capa_timer(ocapa, capa_renewal_time(ocapa));
+ spin_unlock(&capa_lock);
+
+ capa_put(ocapa);
+ iput(inode);
+ return rc;
+}
+
+void ll_capa_open(struct inode *inode)
+{
+ struct ll_inode_info *lli = ll_i2info(inode);
+
+ if ((ll_i2sbi(inode)->ll_flags & (LL_SBI_MDS_CAPA | LL_SBI_OSS_CAPA))
+ == 0)
+ return;
+
+ if (!S_ISREG(inode->i_mode))
+ return;
+
+ atomic_inc(&lli->lli_open_count);
+}
+
+void ll_capa_close(struct inode *inode)
+{
+ struct ll_inode_info *lli = ll_i2info(inode);
+
+ if ((ll_i2sbi(inode)->ll_flags & (LL_SBI_MDS_CAPA | LL_SBI_OSS_CAPA))
+ == 0)
+ return;
+
+ if (!S_ISREG(inode->i_mode))
+ return;
+
+ atomic_dec(&lli->lli_open_count);
+}
+
+/* delete CAPA_OPC_OSS_TRUNC only */
+void ll_truncate_free_capa(struct obd_capa *ocapa)
+{
+ if (!ocapa)
+ return;
+
+ LASSERT(ocapa->c_capa.lc_opc & CAPA_OPC_OSS_TRUNC);
+ DEBUG_CAPA(D_SEC, &ocapa->c_capa, "free truncate");
+
+ /* release ref when find */
+ capa_put(ocapa);
+ if (likely(ocapa->c_capa.lc_opc == CAPA_OPC_OSS_TRUNC)) {
+ spin_lock(&capa_lock);
+ ll_delete_capa(ocapa);
+ spin_unlock(&capa_lock);
+ }
+}
+
+void ll_clear_inode_capas(struct inode *inode)
+{
+ struct ll_inode_info *lli = ll_i2info(inode);
+ struct obd_capa *ocapa, *tmp;
+
+ spin_lock(&capa_lock);
+ ocapa = lli->lli_mds_capa;
+ if (ocapa)
+ ll_delete_capa(ocapa);
+
+ list_for_each_entry_safe(ocapa, tmp, &lli->lli_oss_capas,
+ u.cli.lli_list)
+ ll_delete_capa(ocapa);
+ spin_unlock(&capa_lock);
+}
+
+void ll_print_capa_stat(struct ll_sb_info *sbi)
+{
+ if (sbi->ll_flags & (LL_SBI_MDS_CAPA | LL_SBI_OSS_CAPA))
+ LCONSOLE_INFO("Fid capabilities renewed: %llu\n"
+ "Fid capabilities renewal ENOENT: %llu\n"
+ "Fid capabilities failed to renew: %llu\n"
+ "Fid capabilities renewal retries: %llu\n",
+ ll_capa_renewed, ll_capa_renewal_noent,
+ ll_capa_renewal_failed, ll_capa_renewal_retries);
+}
diff --git a/drivers/staging/lustre/lustre/llite/llite_close.c b/drivers/staging/lustre/lustre/llite/llite_close.c
new file mode 100644
index 000000000..a94ba02cc
--- /dev/null
+++ b/drivers/staging/lustre/lustre/llite/llite_close.c
@@ -0,0 +1,393 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2011, 2012, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ *
+ * lustre/llite/llite_close.c
+ *
+ * Lustre Lite routines to issue a secondary close after writeback
+ */
+
+#include <linux/module.h>
+
+#define DEBUG_SUBSYSTEM S_LLITE
+
+#include "../include/lustre_lite.h"
+#include "llite_internal.h"
+
+/** records that a write is in flight */
+void vvp_write_pending(struct ccc_object *club, struct ccc_page *page)
+{
+ struct ll_inode_info *lli = ll_i2info(club->cob_inode);
+
+ spin_lock(&lli->lli_lock);
+ lli->lli_flags |= LLIF_SOM_DIRTY;
+ if (page != NULL && list_empty(&page->cpg_pending_linkage))
+ list_add(&page->cpg_pending_linkage,
+ &club->cob_pending_list);
+ spin_unlock(&lli->lli_lock);
+}
+
+/** records that a write has completed */
+void vvp_write_complete(struct ccc_object *club, struct ccc_page *page)
+{
+ struct ll_inode_info *lli = ll_i2info(club->cob_inode);
+ int rc = 0;
+
+ spin_lock(&lli->lli_lock);
+ if (page != NULL && !list_empty(&page->cpg_pending_linkage)) {
+ list_del_init(&page->cpg_pending_linkage);
+ rc = 1;
+ }
+ spin_unlock(&lli->lli_lock);
+ if (rc)
+ ll_queue_done_writing(club->cob_inode, 0);
+}
+
+/** Queues DONE_WRITING if
+ * - done writing is allowed;
+ * - inode has no no dirty pages; */
+void ll_queue_done_writing(struct inode *inode, unsigned long flags)
+{
+ struct ll_inode_info *lli = ll_i2info(inode);
+ struct ccc_object *club = cl2ccc(ll_i2info(inode)->lli_clob);
+
+ spin_lock(&lli->lli_lock);
+ lli->lli_flags |= flags;
+
+ if ((lli->lli_flags & LLIF_DONE_WRITING) &&
+ list_empty(&club->cob_pending_list)) {
+ struct ll_close_queue *lcq = ll_i2sbi(inode)->ll_lcq;
+
+ if (lli->lli_flags & LLIF_MDS_SIZE_LOCK)
+ CWARN("ino %lu/%u(flags %u) som valid it just after recovery\n",
+ inode->i_ino, inode->i_generation,
+ lli->lli_flags);
+ /* DONE_WRITING is allowed and inode has no dirty page. */
+ spin_lock(&lcq->lcq_lock);
+
+ LASSERT(list_empty(&lli->lli_close_list));
+ CDEBUG(D_INODE, "adding inode %lu/%u to close list\n",
+ inode->i_ino, inode->i_generation);
+ list_add_tail(&lli->lli_close_list, &lcq->lcq_head);
+
+ /* Avoid a concurrent insertion into the close thread queue:
+ * an inode is already in the close thread, open(), write(),
+ * close() happen, epoch is closed as the inode is marked as
+ * LLIF_EPOCH_PENDING. When pages are written inode should not
+ * be inserted into the queue again, clear this flag to avoid
+ * it. */
+ lli->lli_flags &= ~LLIF_DONE_WRITING;
+
+ wake_up(&lcq->lcq_waitq);
+ spin_unlock(&lcq->lcq_lock);
+ }
+ spin_unlock(&lli->lli_lock);
+}
+
+/** Pack SOM attributes info @opdata for CLOSE, DONE_WRITING rpc. */
+void ll_done_writing_attr(struct inode *inode, struct md_op_data *op_data)
+{
+ struct ll_inode_info *lli = ll_i2info(inode);
+
+ op_data->op_flags |= MF_SOM_CHANGE;
+ /* Check if Size-on-MDS attributes are valid. */
+ if (lli->lli_flags & LLIF_MDS_SIZE_LOCK)
+ CERROR("ino %lu/%u(flags %u) som valid it just after recovery\n",
+ inode->i_ino, inode->i_generation,
+ lli->lli_flags);
+
+ if (!cl_local_size(inode)) {
+ /* Send Size-on-MDS Attributes if valid. */
+ op_data->op_attr.ia_valid |= ATTR_MTIME_SET | ATTR_CTIME_SET |
+ ATTR_ATIME_SET | ATTR_SIZE | ATTR_BLOCKS;
+ }
+}
+
+/** Closes ioepoch and packs Size-on-MDS attribute if needed into @op_data. */
+void ll_ioepoch_close(struct inode *inode, struct md_op_data *op_data,
+ struct obd_client_handle **och, unsigned long flags)
+{
+ struct ll_inode_info *lli = ll_i2info(inode);
+ struct ccc_object *club = cl2ccc(ll_i2info(inode)->lli_clob);
+
+ spin_lock(&lli->lli_lock);
+ if (!(list_empty(&club->cob_pending_list))) {
+ if (!(lli->lli_flags & LLIF_EPOCH_PENDING)) {
+ LASSERT(*och != NULL);
+ LASSERT(lli->lli_pending_och == NULL);
+ /* Inode is dirty and there is no pending write done
+ * request yet, DONE_WRITE is to be sent later. */
+ lli->lli_flags |= LLIF_EPOCH_PENDING;
+ lli->lli_pending_och = *och;
+ spin_unlock(&lli->lli_lock);
+
+ inode = igrab(inode);
+ LASSERT(inode);
+ goto out;
+ }
+ if (flags & LLIF_DONE_WRITING) {
+ /* Some pages are still dirty, it is early to send
+ * DONE_WRITE. Wait until all pages will be flushed
+ * and try DONE_WRITE again later. */
+ LASSERT(!(lli->lli_flags & LLIF_DONE_WRITING));
+ lli->lli_flags |= LLIF_DONE_WRITING;
+ spin_unlock(&lli->lli_lock);
+
+ inode = igrab(inode);
+ LASSERT(inode);
+ goto out;
+ }
+ }
+ CDEBUG(D_INODE, "Epoch %llu closed on "DFID"\n",
+ ll_i2info(inode)->lli_ioepoch, PFID(&lli->lli_fid));
+ op_data->op_flags |= MF_EPOCH_CLOSE;
+
+ if (flags & LLIF_DONE_WRITING) {
+ LASSERT(lli->lli_flags & LLIF_SOM_DIRTY);
+ LASSERT(!(lli->lli_flags & LLIF_DONE_WRITING));
+ *och = lli->lli_pending_och;
+ lli->lli_pending_och = NULL;
+ lli->lli_flags &= ~LLIF_EPOCH_PENDING;
+ } else {
+ /* Pack Size-on-MDS inode attributes only if they has changed */
+ if (!(lli->lli_flags & LLIF_SOM_DIRTY)) {
+ spin_unlock(&lli->lli_lock);
+ goto out;
+ }
+
+ /* There is a pending DONE_WRITE -- close epoch with no
+ * attribute change. */
+ if (lli->lli_flags & LLIF_EPOCH_PENDING) {
+ spin_unlock(&lli->lli_lock);
+ goto out;
+ }
+ }
+
+ LASSERT(list_empty(&club->cob_pending_list));
+ lli->lli_flags &= ~LLIF_SOM_DIRTY;
+ spin_unlock(&lli->lli_lock);
+ ll_done_writing_attr(inode, op_data);
+
+out:
+ return;
+}
+
+/**
+ * Cliens updates SOM attributes on MDS (including llog cookies):
+ * obd_getattr with no lock and md_setattr.
+ */
+int ll_som_update(struct inode *inode, struct md_op_data *op_data)
+{
+ struct ll_inode_info *lli = ll_i2info(inode);
+ struct ptlrpc_request *request = NULL;
+ __u32 old_flags;
+ struct obdo *oa;
+ int rc;
+
+ LASSERT(op_data != NULL);
+ if (lli->lli_flags & LLIF_MDS_SIZE_LOCK)
+ CERROR("ino %lu/%u(flags %u) som valid it just after recovery\n",
+ inode->i_ino, inode->i_generation,
+ lli->lli_flags);
+
+ OBDO_ALLOC(oa);
+ if (!oa) {
+ CERROR("can't allocate memory for Size-on-MDS update.\n");
+ return -ENOMEM;
+ }
+
+ old_flags = op_data->op_flags;
+ op_data->op_flags = MF_SOM_CHANGE;
+
+ /* If inode is already in another epoch, skip getattr from OSTs. */
+ if (lli->lli_ioepoch == op_data->op_ioepoch) {
+ rc = ll_inode_getattr(inode, oa, op_data->op_ioepoch,
+ old_flags & MF_GETATTR_LOCK);
+ if (rc) {
+ oa->o_valid = 0;
+ if (rc != -ENOENT)
+ CERROR("inode_getattr failed (%d): unable to send a Size-on-MDS attribute update for inode %lu/%u\n",
+ rc, inode->i_ino,
+ inode->i_generation);
+ } else {
+ CDEBUG(D_INODE, "Size-on-MDS update on "DFID"\n",
+ PFID(&lli->lli_fid));
+ }
+ /* Install attributes into op_data. */
+ md_from_obdo(op_data, oa, oa->o_valid);
+ }
+
+ rc = md_setattr(ll_i2sbi(inode)->ll_md_exp, op_data,
+ NULL, 0, NULL, 0, &request, NULL);
+ ptlrpc_req_finished(request);
+
+ OBDO_FREE(oa);
+ return rc;
+}
+
+/**
+ * Closes the ioepoch and packs all the attributes into @op_data for
+ * DONE_WRITING rpc.
+ */
+static void ll_prepare_done_writing(struct inode *inode,
+ struct md_op_data *op_data,
+ struct obd_client_handle **och)
+{
+ ll_ioepoch_close(inode, op_data, och, LLIF_DONE_WRITING);
+ /* If there is no @och, we do not do D_W yet. */
+ if (*och == NULL)
+ return;
+
+ ll_pack_inode2opdata(inode, op_data, &(*och)->och_fh);
+ ll_prep_md_op_data(op_data, inode, NULL, NULL,
+ 0, 0, LUSTRE_OPC_ANY, NULL);
+}
+
+/** Send a DONE_WRITING rpc. */
+static void ll_done_writing(struct inode *inode)
+{
+ struct obd_client_handle *och = NULL;
+ struct md_op_data *op_data;
+ int rc;
+
+ LASSERT(exp_connect_som(ll_i2mdexp(inode)));
+
+ op_data = kzalloc(sizeof(*op_data), GFP_NOFS);
+ if (!op_data)
+ return;
+
+ ll_prepare_done_writing(inode, op_data, &och);
+ /* If there is no @och, we do not do D_W yet. */
+ if (och == NULL)
+ goto out;
+
+ rc = md_done_writing(ll_i2sbi(inode)->ll_md_exp, op_data, NULL);
+ if (rc == -EAGAIN) {
+ /* MDS has instructed us to obtain Size-on-MDS attribute from
+ * OSTs and send setattr to back to MDS. */
+ rc = ll_som_update(inode, op_data);
+ } else if (rc) {
+ CERROR("inode %lu mdc done_writing failed: rc = %d\n",
+ inode->i_ino, rc);
+ }
+out:
+ ll_finish_md_op_data(op_data);
+ if (och) {
+ md_clear_open_replay_data(ll_i2sbi(inode)->ll_md_exp, och);
+ OBD_FREE_PTR(och);
+ }
+}
+
+static struct ll_inode_info *ll_close_next_lli(struct ll_close_queue *lcq)
+{
+ struct ll_inode_info *lli = NULL;
+
+ spin_lock(&lcq->lcq_lock);
+
+ if (!list_empty(&lcq->lcq_head)) {
+ lli = list_entry(lcq->lcq_head.next, struct ll_inode_info,
+ lli_close_list);
+ list_del_init(&lli->lli_close_list);
+ } else if (atomic_read(&lcq->lcq_stop))
+ lli = ERR_PTR(-EALREADY);
+
+ spin_unlock(&lcq->lcq_lock);
+ return lli;
+}
+
+static int ll_close_thread(void *arg)
+{
+ struct ll_close_queue *lcq = arg;
+
+ complete(&lcq->lcq_comp);
+
+ while (1) {
+ struct l_wait_info lwi = { 0 };
+ struct ll_inode_info *lli;
+ struct inode *inode;
+
+ l_wait_event_exclusive(lcq->lcq_waitq,
+ (lli = ll_close_next_lli(lcq)) != NULL,
+ &lwi);
+ if (IS_ERR(lli))
+ break;
+
+ inode = ll_info2i(lli);
+ CDEBUG(D_INFO, "done_writing for inode %lu/%u\n",
+ inode->i_ino, inode->i_generation);
+ ll_done_writing(inode);
+ iput(inode);
+ }
+
+ CDEBUG(D_INFO, "ll_close exiting\n");
+ complete(&lcq->lcq_comp);
+ return 0;
+}
+
+int ll_close_thread_start(struct ll_close_queue **lcq_ret)
+{
+ struct ll_close_queue *lcq;
+ struct task_struct *task;
+
+ if (OBD_FAIL_CHECK(OBD_FAIL_LDLM_CLOSE_THREAD))
+ return -EINTR;
+
+ lcq = kzalloc(sizeof(*lcq), GFP_NOFS);
+ if (!lcq)
+ return -ENOMEM;
+
+ spin_lock_init(&lcq->lcq_lock);
+ INIT_LIST_HEAD(&lcq->lcq_head);
+ init_waitqueue_head(&lcq->lcq_waitq);
+ init_completion(&lcq->lcq_comp);
+
+ task = kthread_run(ll_close_thread, lcq, "ll_close");
+ if (IS_ERR(task)) {
+ OBD_FREE(lcq, sizeof(*lcq));
+ return PTR_ERR(task);
+ }
+
+ wait_for_completion(&lcq->lcq_comp);
+ *lcq_ret = lcq;
+ return 0;
+}
+
+void ll_close_thread_shutdown(struct ll_close_queue *lcq)
+{
+ init_completion(&lcq->lcq_comp);
+ atomic_inc(&lcq->lcq_stop);
+ wake_up(&lcq->lcq_waitq);
+ wait_for_completion(&lcq->lcq_comp);
+ OBD_FREE(lcq, sizeof(*lcq));
+}
diff --git a/drivers/staging/lustre/lustre/llite/llite_internal.h b/drivers/staging/lustre/lustre/llite/llite_internal.h
new file mode 100644
index 000000000..5f918e3c4
--- /dev/null
+++ b/drivers/staging/lustre/lustre/llite/llite_internal.h
@@ -0,0 +1,1521 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2011, 2012, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ */
+
+#ifndef LLITE_INTERNAL_H
+#define LLITE_INTERNAL_H
+#include "../include/lustre_debug.h"
+#include "../include/lustre_ver.h"
+#include "../include/lustre_disk.h" /* for s2sbi */
+#include "../include/lustre_eacl.h"
+
+/* for struct cl_lock_descr and struct cl_io */
+#include "../include/cl_object.h"
+#include "../include/lclient.h"
+#include "../include/lustre_mdc.h"
+#include "../include/lustre_intent.h"
+#include <linux/compat.h>
+#include <linux/posix_acl_xattr.h>
+
+#ifndef FMODE_EXEC
+#define FMODE_EXEC 0
+#endif
+
+#ifndef VM_FAULT_RETRY
+#define VM_FAULT_RETRY 0
+#endif
+
+/* Kernel 3.1 kills LOOKUP_CONTINUE, LOOKUP_PARENT is equivalent to it.
+ * seem kernel commit 49084c3bb2055c401f3493c13edae14d49128ca0 */
+#ifndef LOOKUP_CONTINUE
+#define LOOKUP_CONTINUE LOOKUP_PARENT
+#endif
+
+/** Only used on client-side for indicating the tail of dir hash/offset. */
+#define LL_DIR_END_OFF 0x7fffffffffffffffULL
+#define LL_DIR_END_OFF_32BIT 0x7fffffffUL
+
+#define LL_IT2STR(it) ((it) ? ldlm_it2str((it)->it_op) : "0")
+#define LUSTRE_FPRIVATE(file) ((file)->private_data)
+
+struct ll_dentry_data {
+ struct lookup_intent *lld_it;
+ unsigned int lld_sa_generation;
+ unsigned int lld_invalid:1;
+ struct rcu_head lld_rcu_head;
+};
+
+#define ll_d2d(de) ((struct ll_dentry_data*)((de)->d_fsdata))
+
+#define LLI_INODE_MAGIC 0x111d0de5
+#define LLI_INODE_DEAD 0xdeadd00d
+
+/* remote client permission cache */
+#define REMOTE_PERM_HASHSIZE 16
+
+struct ll_getname_data {
+ struct dir_context ctx;
+ char *lgd_name; /* points to a buffer with NAME_MAX+1 size */
+ struct lu_fid lgd_fid; /* target fid we are looking for */
+ int lgd_found; /* inode matched? */
+};
+
+/* llite setxid/access permission for user on remote client */
+struct ll_remote_perm {
+ struct hlist_node lrp_list;
+ uid_t lrp_uid;
+ gid_t lrp_gid;
+ uid_t lrp_fsuid;
+ gid_t lrp_fsgid;
+ int lrp_access_perm; /* MAY_READ/WRITE/EXEC, this
+ is access permission with
+ lrp_fsuid/lrp_fsgid. */
+};
+
+enum lli_flags {
+ /* MDS has an authority for the Size-on-MDS attributes. */
+ LLIF_MDS_SIZE_LOCK = (1 << 0),
+ /* Epoch close is postponed. */
+ LLIF_EPOCH_PENDING = (1 << 1),
+ /* DONE WRITING is allowed. */
+ LLIF_DONE_WRITING = (1 << 2),
+ /* Sizeon-on-MDS attributes are changed. An attribute update needs to
+ * be sent to MDS. */
+ LLIF_SOM_DIRTY = (1 << 3),
+ /* File data is modified. */
+ LLIF_DATA_MODIFIED = (1 << 4),
+ /* File is being restored */
+ LLIF_FILE_RESTORING = (1 << 5),
+ /* Xattr cache is attached to the file */
+ LLIF_XATTR_CACHE = (1 << 6),
+};
+
+struct ll_inode_info {
+ __u32 lli_inode_magic;
+ __u32 lli_flags;
+ __u64 lli_ioepoch;
+
+ spinlock_t lli_lock;
+ struct posix_acl *lli_posix_acl;
+
+ struct hlist_head *lli_remote_perms;
+ struct mutex lli_rmtperm_mutex;
+
+ /* identifying fields for both metadata and data stacks. */
+ struct lu_fid lli_fid;
+ /* Parent fid for accessing default stripe data on parent directory
+ * for allocating OST objects after a mknod() and later open-by-FID. */
+ struct lu_fid lli_pfid;
+
+ struct list_head lli_close_list;
+ struct list_head lli_oss_capas;
+ /* open count currently used by capability only, indicate whether
+ * capability needs renewal */
+ atomic_t lli_open_count;
+ struct obd_capa *lli_mds_capa;
+ unsigned long lli_rmtperm_time;
+
+ /* handle is to be sent to MDS later on done_writing and setattr.
+ * Open handle data are needed for the recovery to reconstruct
+ * the inode state on the MDS. XXX: recovery is not ready yet. */
+ struct obd_client_handle *lli_pending_och;
+
+ /* We need all three because every inode may be opened in different
+ * modes */
+ struct obd_client_handle *lli_mds_read_och;
+ struct obd_client_handle *lli_mds_write_och;
+ struct obd_client_handle *lli_mds_exec_och;
+ __u64 lli_open_fd_read_count;
+ __u64 lli_open_fd_write_count;
+ __u64 lli_open_fd_exec_count;
+ /* Protects access to och pointers and their usage counters */
+ struct mutex lli_och_mutex;
+
+ struct inode lli_vfs_inode;
+
+ /* the most recent timestamps obtained from mds */
+ struct ost_lvb lli_lvb;
+ spinlock_t lli_agl_lock;
+
+ /* Try to make the d::member and f::member are aligned. Before using
+ * these members, make clear whether it is directory or not. */
+ union {
+ /* for directory */
+ struct {
+ /* serialize normal readdir and statahead-readdir. */
+ struct mutex d_readdir_mutex;
+
+ /* metadata statahead */
+ /* since parent-child threads can share the same @file
+ * struct, "opendir_key" is the token when dir close for
+ * case of parent exit before child -- it is me should
+ * cleanup the dir readahead. */
+ void *d_opendir_key;
+ struct ll_statahead_info *d_sai;
+ /* protect statahead stuff. */
+ spinlock_t d_sa_lock;
+ /* "opendir_pid" is the token when lookup/revalid
+ * -- I am the owner of dir statahead. */
+ pid_t d_opendir_pid;
+ } d;
+
+#define lli_readdir_mutex u.d.d_readdir_mutex
+#define lli_opendir_key u.d.d_opendir_key
+#define lli_sai u.d.d_sai
+#define lli_sa_lock u.d.d_sa_lock
+#define lli_opendir_pid u.d.d_opendir_pid
+
+ /* for non-directory */
+ struct {
+ struct mutex f_size_mutex;
+ char *f_symlink_name;
+ __u64 f_maxbytes;
+ /*
+ * struct rw_semaphore {
+ * signed long count; // align d.d_def_acl
+ * spinlock_t wait_lock; // align d.d_sa_lock
+ * struct list_head wait_list;
+ * }
+ */
+ struct rw_semaphore f_trunc_sem;
+ struct mutex f_write_mutex;
+
+ struct rw_semaphore f_glimpse_sem;
+ unsigned long f_glimpse_time;
+ struct list_head f_agl_list;
+ __u64 f_agl_index;
+
+ /* for writepage() only to communicate to fsync */
+ int f_async_rc;
+
+ /*
+ * whenever a process try to read/write the file, the
+ * jobid of the process will be saved here, and it'll
+ * be packed into the write PRC when flush later.
+ *
+ * so the read/write statistics for jobid will not be
+ * accurate if the file is shared by different jobs.
+ */
+ char f_jobid[JOBSTATS_JOBID_SIZE];
+ } f;
+
+#define lli_size_mutex u.f.f_size_mutex
+#define lli_symlink_name u.f.f_symlink_name
+#define lli_maxbytes u.f.f_maxbytes
+#define lli_trunc_sem u.f.f_trunc_sem
+#define lli_write_mutex u.f.f_write_mutex
+#define lli_glimpse_sem u.f.f_glimpse_sem
+#define lli_glimpse_time u.f.f_glimpse_time
+#define lli_agl_list u.f.f_agl_list
+#define lli_agl_index u.f.f_agl_index
+#define lli_async_rc u.f.f_async_rc
+#define lli_jobid u.f.f_jobid
+
+ } u;
+
+ /* XXX: For following frequent used members, although they maybe special
+ * used for non-directory object, it is some time-wasting to check
+ * whether the object is directory or not before using them. On the
+ * other hand, currently, sizeof(f) > sizeof(d), it cannot reduce
+ * the "ll_inode_info" size even if moving those members into u.f.
+ * So keep them out side.
+ *
+ * In the future, if more members are added only for directory,
+ * some of the following members can be moved into u.f.
+ */
+ bool lli_has_smd;
+ struct cl_object *lli_clob;
+
+ /* mutex to request for layout lock exclusively. */
+ struct mutex lli_layout_mutex;
+ /* Layout version, protected by lli_layout_lock */
+ __u32 lli_layout_gen;
+ spinlock_t lli_layout_lock;
+
+ struct rw_semaphore lli_xattrs_list_rwsem;
+ struct mutex lli_xattrs_enq_lock;
+ struct list_head lli_xattrs;/* ll_xattr_entry->xe_list */
+};
+
+static inline __u32 ll_layout_version_get(struct ll_inode_info *lli)
+{
+ __u32 gen;
+
+ spin_lock(&lli->lli_layout_lock);
+ gen = lli->lli_layout_gen;
+ spin_unlock(&lli->lli_layout_lock);
+
+ return gen;
+}
+
+static inline void ll_layout_version_set(struct ll_inode_info *lli, __u32 gen)
+{
+ spin_lock(&lli->lli_layout_lock);
+ lli->lli_layout_gen = gen;
+ spin_unlock(&lli->lli_layout_lock);
+}
+
+int ll_xattr_cache_destroy(struct inode *inode);
+
+int ll_xattr_cache_get(struct inode *inode,
+ const char *name,
+ char *buffer,
+ size_t size,
+ __u64 valid);
+
+/*
+ * Locking to guarantee consistency of non-atomic updates to long long i_size,
+ * consistency between file size and KMS.
+ *
+ * Implemented by ->lli_size_mutex and ->lsm_lock, nested in that order.
+ */
+
+void ll_inode_size_lock(struct inode *inode);
+void ll_inode_size_unlock(struct inode *inode);
+
+/* FIXME: replace the name of this with LL_I to conform to kernel stuff */
+/* static inline struct ll_inode_info *LL_I(struct inode *inode) */
+static inline struct ll_inode_info *ll_i2info(struct inode *inode)
+{
+ return container_of(inode, struct ll_inode_info, lli_vfs_inode);
+}
+
+/* default to about 40meg of readahead on a given system. That much tied
+ * up in 512k readahead requests serviced at 40ms each is about 1GB/s. */
+#define SBI_DEFAULT_READAHEAD_MAX (40UL << (20 - PAGE_CACHE_SHIFT))
+
+/* default to read-ahead full files smaller than 2MB on the second read */
+#define SBI_DEFAULT_READAHEAD_WHOLE_MAX (2UL << (20 - PAGE_CACHE_SHIFT))
+
+enum ra_stat {
+ RA_STAT_HIT = 0,
+ RA_STAT_MISS,
+ RA_STAT_DISTANT_READPAGE,
+ RA_STAT_MISS_IN_WINDOW,
+ RA_STAT_FAILED_GRAB_PAGE,
+ RA_STAT_FAILED_MATCH,
+ RA_STAT_DISCARDED,
+ RA_STAT_ZERO_LEN,
+ RA_STAT_ZERO_WINDOW,
+ RA_STAT_EOF,
+ RA_STAT_MAX_IN_FLIGHT,
+ RA_STAT_WRONG_GRAB_PAGE,
+ _NR_RA_STAT,
+};
+
+struct ll_ra_info {
+ atomic_t ra_cur_pages;
+ unsigned long ra_max_pages;
+ unsigned long ra_max_pages_per_file;
+ unsigned long ra_max_read_ahead_whole_pages;
+};
+
+/* ra_io_arg will be filled in the beginning of ll_readahead with
+ * ras_lock, then the following ll_read_ahead_pages will read RA
+ * pages according to this arg, all the items in this structure are
+ * counted by page index.
+ */
+struct ra_io_arg {
+ unsigned long ria_start; /* start offset of read-ahead*/
+ unsigned long ria_end; /* end offset of read-ahead*/
+ /* If stride read pattern is detected, ria_stoff means where
+ * stride read is started. Note: for normal read-ahead, the
+ * value here is meaningless, and also it will not be accessed*/
+ pgoff_t ria_stoff;
+ /* ria_length and ria_pages are the length and pages length in the
+ * stride I/O mode. And they will also be used to check whether
+ * it is stride I/O read-ahead in the read-ahead pages*/
+ unsigned long ria_length;
+ unsigned long ria_pages;
+};
+
+/* LL_HIST_MAX=32 causes an overflow */
+#define LL_HIST_MAX 28
+#define LL_HIST_START 12 /* buckets start at 2^12 = 4k */
+#define LL_PROCESS_HIST_MAX 10
+struct per_process_info {
+ pid_t pid;
+ struct obd_histogram pp_r_hist;
+ struct obd_histogram pp_w_hist;
+};
+
+/* pp_extents[LL_PROCESS_HIST_MAX] will hold the combined process info */
+struct ll_rw_extents_info {
+ struct per_process_info pp_extents[LL_PROCESS_HIST_MAX + 1];
+};
+
+#define LL_OFFSET_HIST_MAX 100
+struct ll_rw_process_info {
+ pid_t rw_pid;
+ int rw_op;
+ loff_t rw_range_start;
+ loff_t rw_range_end;
+ loff_t rw_last_file_pos;
+ loff_t rw_offset;
+ size_t rw_smallest_extent;
+ size_t rw_largest_extent;
+ struct ll_file_data *rw_last_file;
+};
+
+enum stats_track_type {
+ STATS_TRACK_ALL = 0, /* track all processes */
+ STATS_TRACK_PID, /* track process with this pid */
+ STATS_TRACK_PPID, /* track processes with this ppid */
+ STATS_TRACK_GID, /* track processes with this gid */
+ STATS_TRACK_LAST,
+};
+
+/* flags for sbi->ll_flags */
+#define LL_SBI_NOLCK 0x01 /* DLM locking disabled (directio-only) */
+#define LL_SBI_CHECKSUM 0x02 /* checksum each page as it's written */
+#define LL_SBI_FLOCK 0x04
+#define LL_SBI_USER_XATTR 0x08 /* support user xattr */
+#define LL_SBI_ACL 0x10 /* support ACL */
+#define LL_SBI_RMT_CLIENT 0x40 /* remote client */
+#define LL_SBI_MDS_CAPA 0x80 /* support mds capa */
+#define LL_SBI_OSS_CAPA 0x100 /* support oss capa */
+#define LL_SBI_LOCALFLOCK 0x200 /* Local flocks support by kernel */
+#define LL_SBI_LRU_RESIZE 0x400 /* lru resize support */
+#define LL_SBI_LAZYSTATFS 0x800 /* lazystatfs mount option */
+#define LL_SBI_SOM_PREVIEW 0x1000 /* SOM preview mount option */
+#define LL_SBI_32BIT_API 0x2000 /* generate 32 bit inodes. */
+#define LL_SBI_64BIT_HASH 0x4000 /* support 64-bits dir hash/offset */
+#define LL_SBI_AGL_ENABLED 0x8000 /* enable agl */
+#define LL_SBI_VERBOSE 0x10000 /* verbose mount/umount */
+#define LL_SBI_LAYOUT_LOCK 0x20000 /* layout lock support */
+#define LL_SBI_USER_FID2PATH 0x40000 /* allow fid2path by unprivileged users */
+#define LL_SBI_XATTR_CACHE 0x80000 /* support for xattr cache */
+
+#define LL_SBI_FLAGS { \
+ "nolck", \
+ "checksum", \
+ "flock", \
+ "xattr", \
+ "acl", \
+ "???", \
+ "rmt_client", \
+ "mds_capa", \
+ "oss_capa", \
+ "flock", \
+ "lru_resize", \
+ "lazy_statfs", \
+ "som", \
+ "32bit_api", \
+ "64bit_hash", \
+ "agl", \
+ "verbose", \
+ "layout", \
+ "user_fid2path",\
+ "xattr", \
+}
+
+#define RCE_HASHES 32
+
+struct rmtacl_ctl_entry {
+ struct list_head rce_list;
+ pid_t rce_key; /* hash key */
+ int rce_ops; /* acl operation type */
+};
+
+struct rmtacl_ctl_table {
+ spinlock_t rct_lock;
+ struct list_head rct_entries[RCE_HASHES];
+};
+
+#define EE_HASHES 32
+
+struct eacl_table {
+ spinlock_t et_lock;
+ struct list_head et_entries[EE_HASHES];
+};
+
+struct ll_sb_info {
+ struct list_head ll_list;
+ /* this protects pglist and ra_info. It isn't safe to
+ * grab from interrupt contexts */
+ spinlock_t ll_lock;
+ spinlock_t ll_pp_extent_lock; /* pp_extent entry*/
+ spinlock_t ll_process_lock; /* ll_rw_process_info */
+ struct obd_uuid ll_sb_uuid;
+ struct obd_export *ll_md_exp;
+ struct obd_export *ll_dt_exp;
+ struct proc_dir_entry* ll_proc_root;
+ struct lu_fid ll_root_fid; /* root object fid */
+
+ int ll_flags;
+ unsigned int ll_umounting:1,
+ ll_xattr_cache_enabled:1;
+ struct list_head ll_conn_chain; /* per-conn chain of SBs */
+ struct lustre_client_ocd ll_lco;
+
+ struct list_head ll_orphan_dentry_list; /*please don't ask -p*/
+ struct ll_close_queue *ll_lcq;
+
+ struct lprocfs_stats *ll_stats; /* lprocfs stats counter */
+
+ struct cl_client_cache ll_cache;
+
+ struct lprocfs_stats *ll_ra_stats;
+
+ struct ll_ra_info ll_ra_info;
+ unsigned int ll_namelen;
+ struct file_operations *ll_fop;
+
+ /* =0 - hold lock over whole read/write
+ * >0 - max. chunk to be read/written w/o lock re-acquiring */
+ unsigned long ll_max_rw_chunk;
+ unsigned int ll_md_brw_size; /* used by readdir */
+
+ struct lu_site *ll_site;
+ struct cl_device *ll_cl;
+ /* Statistics */
+ struct ll_rw_extents_info ll_rw_extents_info;
+ int ll_extent_process_count;
+ struct ll_rw_process_info ll_rw_process_info[LL_PROCESS_HIST_MAX];
+ unsigned int ll_offset_process_count;
+ struct ll_rw_process_info ll_rw_offset_info[LL_OFFSET_HIST_MAX];
+ unsigned int ll_rw_offset_entry_count;
+ int ll_stats_track_id;
+ enum stats_track_type ll_stats_track_type;
+ int ll_rw_stats_on;
+
+ /* metadata stat-ahead */
+ unsigned int ll_sa_max; /* max statahead RPCs */
+ atomic_t ll_sa_total; /* statahead thread started
+ * count */
+ atomic_t ll_sa_wrong; /* statahead thread stopped for
+ * low hit ratio */
+ atomic_t ll_agl_total; /* AGL thread started count */
+
+ dev_t ll_sdev_orig; /* save s_dev before assign for
+ * clustered nfs */
+ struct rmtacl_ctl_table ll_rct;
+ struct eacl_table ll_et;
+ __kernel_fsid_t ll_fsid;
+};
+
+#define LL_DEFAULT_MAX_RW_CHUNK (32 * 1024 * 1024)
+
+struct ll_ra_read {
+ pgoff_t lrr_start;
+ pgoff_t lrr_count;
+ struct task_struct *lrr_reader;
+ struct list_head lrr_linkage;
+};
+
+/*
+ * per file-descriptor read-ahead data.
+ */
+struct ll_readahead_state {
+ spinlock_t ras_lock;
+ /*
+ * index of the last page that read(2) needed and that wasn't in the
+ * cache. Used by ras_update() to detect seeks.
+ *
+ * XXX nikita: if access seeks into cached region, Lustre doesn't see
+ * this.
+ */
+ unsigned long ras_last_readpage;
+ /*
+ * number of pages read after last read-ahead window reset. As window
+ * is reset on each seek, this is effectively a number of consecutive
+ * accesses. Maybe ->ras_accessed_in_window is better name.
+ *
+ * XXX nikita: window is also reset (by ras_update()) when Lustre
+ * believes that memory pressure evicts read-ahead pages. In that
+ * case, it probably doesn't make sense to expand window to
+ * PTLRPC_MAX_BRW_PAGES on the third access.
+ */
+ unsigned long ras_consecutive_pages;
+ /*
+ * number of read requests after the last read-ahead window reset
+ * As window is reset on each seek, this is effectively the number
+ * on consecutive read request and is used to trigger read-ahead.
+ */
+ unsigned long ras_consecutive_requests;
+ /*
+ * Parameters of current read-ahead window. Handled by
+ * ras_update(). On the initial access to the file or after a seek,
+ * window is reset to 0. After 3 consecutive accesses, window is
+ * expanded to PTLRPC_MAX_BRW_PAGES. Afterwards, window is enlarged by
+ * PTLRPC_MAX_BRW_PAGES chunks up to ->ra_max_pages.
+ */
+ unsigned long ras_window_start, ras_window_len;
+ /*
+ * Where next read-ahead should start at. This lies within read-ahead
+ * window. Read-ahead window is read in pieces rather than at once
+ * because: 1. lustre limits total number of pages under read-ahead by
+ * ->ra_max_pages (see ll_ra_count_get()), 2. client cannot read pages
+ * not covered by DLM lock.
+ */
+ unsigned long ras_next_readahead;
+ /*
+ * Total number of ll_file_read requests issued, reads originating
+ * due to mmap are not counted in this total. This value is used to
+ * trigger full file read-ahead after multiple reads to a small file.
+ */
+ unsigned long ras_requests;
+ /*
+ * Page index with respect to the current request, these value
+ * will not be accurate when dealing with reads issued via mmap.
+ */
+ unsigned long ras_request_index;
+ /*
+ * list of struct ll_ra_read's one per read(2) call current in
+ * progress against this file descriptor. Used by read-ahead code,
+ * protected by ->ras_lock.
+ */
+ struct list_head ras_read_beads;
+ /*
+ * The following 3 items are used for detecting the stride I/O
+ * mode.
+ * In stride I/O mode,
+ * ...............|-----data-----|****gap*****|--------|******|....
+ * offset |-stride_pages-|-stride_gap-|
+ * ras_stride_offset = offset;
+ * ras_stride_length = stride_pages + stride_gap;
+ * ras_stride_pages = stride_pages;
+ * Note: all these three items are counted by pages.
+ */
+ unsigned long ras_stride_length;
+ unsigned long ras_stride_pages;
+ pgoff_t ras_stride_offset;
+ /*
+ * number of consecutive stride request count, and it is similar as
+ * ras_consecutive_requests, but used for stride I/O mode.
+ * Note: only more than 2 consecutive stride request are detected,
+ * stride read-ahead will be enable
+ */
+ unsigned long ras_consecutive_stride_requests;
+};
+
+extern struct kmem_cache *ll_file_data_slab;
+struct lustre_handle;
+struct ll_file_data {
+ struct ll_readahead_state fd_ras;
+ struct ccc_grouplock fd_grouplock;
+ __u64 lfd_pos;
+ __u32 fd_flags;
+ fmode_t fd_omode;
+ /* openhandle if lease exists for this file.
+ * Borrow lli->lli_och_mutex to protect assignment */
+ struct obd_client_handle *fd_lease_och;
+ struct obd_client_handle *fd_och;
+ struct file *fd_file;
+ /* Indicate whether need to report failure when close.
+ * true: failure is known, not report again.
+ * false: unknown failure, should report. */
+ bool fd_write_failed;
+};
+
+struct lov_stripe_md;
+
+extern spinlock_t inode_lock;
+
+extern struct proc_dir_entry *proc_lustre_fs_root;
+
+static inline struct inode *ll_info2i(struct ll_inode_info *lli)
+{
+ return &lli->lli_vfs_inode;
+}
+
+__u32 ll_i2suppgid(struct inode *i);
+void ll_i2gids(__u32 *suppgids, struct inode *i1, struct inode *i2);
+
+static inline int ll_need_32bit_api(struct ll_sb_info *sbi)
+{
+#if BITS_PER_LONG == 32
+ return 1;
+#elif defined(CONFIG_COMPAT)
+ return unlikely(is_compat_task() || (sbi->ll_flags & LL_SBI_32BIT_API));
+#else
+ return unlikely(sbi->ll_flags & LL_SBI_32BIT_API);
+#endif
+}
+
+void ll_ra_read_in(struct file *f, struct ll_ra_read *rar);
+void ll_ra_read_ex(struct file *f, struct ll_ra_read *rar);
+struct ll_ra_read *ll_ra_read_get(struct file *f);
+
+/* llite/lproc_llite.c */
+#if defined (CONFIG_PROC_FS)
+int lprocfs_register_mountpoint(struct proc_dir_entry *parent,
+ struct super_block *sb, char *osc, char *mdc);
+void lprocfs_unregister_mountpoint(struct ll_sb_info *sbi);
+void ll_stats_ops_tally(struct ll_sb_info *sbi, int op, int count);
+void lprocfs_llite_init_vars(struct lprocfs_static_vars *lvars);
+void ll_rw_stats_tally(struct ll_sb_info *sbi, pid_t pid,
+ struct ll_file_data *file, loff_t pos,
+ size_t count, int rw);
+#else
+static inline int lprocfs_register_mountpoint(struct proc_dir_entry *parent,
+ struct super_block *sb, char *osc, char *mdc){return 0;}
+static inline void lprocfs_unregister_mountpoint(struct ll_sb_info *sbi) {}
+static inline
+void ll_stats_ops_tally(struct ll_sb_info *sbi, int op, int count) {}
+static inline void lprocfs_llite_init_vars(struct lprocfs_static_vars *lvars)
+{
+ memset(lvars, 0, sizeof(*lvars));
+}
+static inline void ll_rw_stats_tally(struct ll_sb_info *sbi, pid_t pid,
+ struct ll_file_data *file, loff_t pos,
+ size_t count, int rw) {}
+#endif
+
+
+/* llite/dir.c */
+void ll_release_page(struct page *page, int remove);
+extern const struct file_operations ll_dir_operations;
+extern const struct inode_operations ll_dir_inode_operations;
+struct page *ll_get_dir_page(struct inode *dir, __u64 hash,
+ struct ll_dir_chain *chain);
+int ll_dir_read(struct inode *inode, struct dir_context *ctx);
+
+int ll_get_mdt_idx(struct inode *inode);
+/* llite/namei.c */
+extern const struct inode_operations ll_special_inode_operations;
+
+int ll_objects_destroy(struct ptlrpc_request *request,
+ struct inode *dir);
+struct inode *ll_iget(struct super_block *sb, ino_t hash,
+ struct lustre_md *lic);
+int ll_md_blocking_ast(struct ldlm_lock *, struct ldlm_lock_desc *,
+ void *data, int flag);
+struct dentry *ll_splice_alias(struct inode *inode, struct dentry *de);
+int ll_rmdir_entry(struct inode *dir, char *name, int namelen);
+
+/* llite/rw.c */
+int ll_prepare_write(struct file *, struct page *, unsigned from, unsigned to);
+int ll_commit_write(struct file *, struct page *, unsigned from, unsigned to);
+int ll_writepage(struct page *page, struct writeback_control *wbc);
+int ll_writepages(struct address_space *, struct writeback_control *wbc);
+int ll_readpage(struct file *file, struct page *page);
+void ll_readahead_init(struct inode *inode, struct ll_readahead_state *ras);
+int ll_readahead(const struct lu_env *env, struct cl_io *io,
+ struct ll_readahead_state *ras, struct address_space *mapping,
+ struct cl_page_list *queue, int flags);
+
+#ifndef MS_HAS_NEW_AOPS
+extern const struct address_space_operations ll_aops;
+#else
+extern const struct address_space_operations_ext ll_aops;
+#endif
+
+/* llite/file.c */
+extern struct file_operations ll_file_operations;
+extern struct file_operations ll_file_operations_flock;
+extern struct file_operations ll_file_operations_noflock;
+extern struct inode_operations ll_file_inode_operations;
+extern int ll_have_md_lock(struct inode *inode, __u64 *bits,
+ ldlm_mode_t l_req_mode);
+extern ldlm_mode_t ll_take_md_lock(struct inode *inode, __u64 bits,
+ struct lustre_handle *lockh, __u64 flags,
+ ldlm_mode_t mode);
+int ll_file_open(struct inode *inode, struct file *file);
+int ll_file_release(struct inode *inode, struct file *file);
+int ll_glimpse_ioctl(struct ll_sb_info *sbi,
+ struct lov_stripe_md *lsm, lstat_t *st);
+void ll_ioepoch_open(struct ll_inode_info *lli, __u64 ioepoch);
+int ll_release_openhandle(struct inode *, struct lookup_intent *);
+int ll_md_real_close(struct inode *inode, fmode_t fmode);
+void ll_ioepoch_close(struct inode *inode, struct md_op_data *op_data,
+ struct obd_client_handle **och, unsigned long flags);
+void ll_done_writing_attr(struct inode *inode, struct md_op_data *op_data);
+int ll_som_update(struct inode *inode, struct md_op_data *op_data);
+int ll_inode_getattr(struct inode *inode, struct obdo *obdo,
+ __u64 ioepoch, int sync);
+void ll_pack_inode2opdata(struct inode *inode, struct md_op_data *op_data,
+ struct lustre_handle *fh);
+int ll_getattr(struct vfsmount *mnt, struct dentry *de, struct kstat *stat);
+struct posix_acl *ll_get_acl(struct inode *inode, int type);
+
+int ll_inode_permission(struct inode *inode, int mask);
+
+int ll_lov_setstripe_ea_info(struct inode *inode, struct dentry *dentry,
+ int flags, struct lov_user_md *lum,
+ int lum_size);
+int ll_lov_getstripe_ea_info(struct inode *inode, const char *filename,
+ struct lov_mds_md **lmm, int *lmm_size,
+ struct ptlrpc_request **request);
+int ll_dir_setstripe(struct inode *inode, struct lov_user_md *lump,
+ int set_default);
+int ll_dir_getstripe(struct inode *inode, struct lov_mds_md **lmmp,
+ int *lmm_size, struct ptlrpc_request **request);
+int ll_fsync(struct file *file, loff_t start, loff_t end, int data);
+int ll_merge_lvb(const struct lu_env *env, struct inode *inode);
+int ll_fid2path(struct inode *inode, void __user *arg);
+int ll_data_version(struct inode *inode, __u64 *data_version, int extent_lock);
+int ll_hsm_release(struct inode *inode);
+
+/* llite/dcache.c */
+
+int ll_d_init(struct dentry *de);
+extern const struct dentry_operations ll_d_ops;
+void ll_intent_drop_lock(struct lookup_intent *);
+void ll_intent_release(struct lookup_intent *);
+void ll_invalidate_aliases(struct inode *);
+void ll_lookup_finish_locks(struct lookup_intent *it, struct inode *inode);
+int ll_revalidate_it_finish(struct ptlrpc_request *request,
+ struct lookup_intent *it, struct inode *inode);
+
+/* llite/llite_lib.c */
+extern struct super_operations lustre_super_operations;
+
+void ll_lli_init(struct ll_inode_info *lli);
+int ll_fill_super(struct super_block *sb, struct vfsmount *mnt);
+void ll_put_super(struct super_block *sb);
+void ll_kill_super(struct super_block *sb);
+struct inode *ll_inode_from_resource_lock(struct ldlm_lock *lock);
+void ll_clear_inode(struct inode *inode);
+int ll_setattr_raw(struct dentry *dentry, struct iattr *attr, bool hsm_import);
+int ll_setattr(struct dentry *de, struct iattr *attr);
+int ll_statfs(struct dentry *de, struct kstatfs *sfs);
+int ll_statfs_internal(struct super_block *sb, struct obd_statfs *osfs,
+ __u64 max_age, __u32 flags);
+void ll_update_inode(struct inode *inode, struct lustre_md *md);
+void ll_read_inode2(struct inode *inode, void *opaque);
+void ll_delete_inode(struct inode *inode);
+int ll_iocontrol(struct inode *inode, struct file *file,
+ unsigned int cmd, unsigned long arg);
+int ll_flush_ctx(struct inode *inode);
+void ll_umount_begin(struct super_block *sb);
+int ll_remount_fs(struct super_block *sb, int *flags, char *data);
+int ll_show_options(struct seq_file *seq, struct dentry *dentry);
+void ll_dirty_page_discard_warn(struct page *page, int ioret);
+int ll_prep_inode(struct inode **inode, struct ptlrpc_request *req,
+ struct super_block *, struct lookup_intent *);
+int ll_obd_statfs(struct inode *inode, void *arg);
+int ll_get_max_mdsize(struct ll_sb_info *sbi, int *max_mdsize);
+int ll_get_default_mdsize(struct ll_sb_info *sbi, int *default_mdsize);
+int ll_get_max_cookiesize(struct ll_sb_info *sbi, int *max_cookiesize);
+int ll_get_default_cookiesize(struct ll_sb_info *sbi, int *default_cookiesize);
+int ll_process_config(struct lustre_cfg *lcfg);
+struct md_op_data *ll_prep_md_op_data(struct md_op_data *op_data,
+ struct inode *i1, struct inode *i2,
+ const char *name, int namelen,
+ int mode, __u32 opc, void *data);
+void ll_finish_md_op_data(struct md_op_data *op_data);
+int ll_get_obd_name(struct inode *inode, unsigned int cmd, unsigned long arg);
+char *ll_get_fsname(struct super_block *sb, char *buf, int buflen);
+
+/* llite/llite_nfs.c */
+extern struct export_operations lustre_export_operations;
+__u32 get_uuid2int(const char *name, int len);
+void get_uuid2fsid(const char *name, int len, __kernel_fsid_t *fsid);
+struct inode *search_inode_for_lustre(struct super_block *sb,
+ const struct lu_fid *fid);
+
+/* llite/symlink.c */
+extern struct inode_operations ll_fast_symlink_inode_operations;
+
+/* llite/llite_close.c */
+struct ll_close_queue {
+ spinlock_t lcq_lock;
+ struct list_head lcq_head;
+ wait_queue_head_t lcq_waitq;
+ struct completion lcq_comp;
+ atomic_t lcq_stop;
+};
+
+struct ccc_object *cl_inode2ccc(struct inode *inode);
+
+
+void vvp_write_pending (struct ccc_object *club, struct ccc_page *page);
+void vvp_write_complete(struct ccc_object *club, struct ccc_page *page);
+
+/* specific architecture can implement only part of this list */
+enum vvp_io_subtype {
+ /** normal IO */
+ IO_NORMAL,
+ /** io started from splice_{read|write} */
+ IO_SPLICE
+};
+
+/* IO subtypes */
+struct vvp_io {
+ /** io subtype */
+ enum vvp_io_subtype cui_io_subtype;
+
+ union {
+ struct {
+ struct pipe_inode_info *cui_pipe;
+ unsigned int cui_flags;
+ } splice;
+ struct vvp_fault_io {
+ /**
+ * Inode modification time that is checked across DLM
+ * lock request.
+ */
+ time_t ft_mtime;
+ struct vm_area_struct *ft_vma;
+ /**
+ * locked page returned from vvp_io
+ */
+ struct page *ft_vmpage;
+ struct vm_fault_api {
+ /**
+ * kernel fault info
+ */
+ struct vm_fault *ft_vmf;
+ /**
+ * fault API used bitflags for return code.
+ */
+ unsigned int ft_flags;
+ /**
+ * check that flags are from filemap_fault
+ */
+ bool ft_flags_valid;
+ } fault;
+ } fault;
+ } u;
+ /**
+ * Read-ahead state used by read and page-fault IO contexts.
+ */
+ struct ll_ra_read cui_bead;
+ /**
+ * Set when cui_bead has been initialized.
+ */
+ int cui_ra_window_set;
+};
+
+/**
+ * IO arguments for various VFS I/O interfaces.
+ */
+struct vvp_io_args {
+ /** normal/splice */
+ enum vvp_io_subtype via_io_subtype;
+
+ union {
+ struct {
+ struct kiocb *via_iocb;
+ struct iov_iter *via_iter;
+ } normal;
+ struct {
+ struct pipe_inode_info *via_pipe;
+ unsigned int via_flags;
+ } splice;
+ } u;
+};
+
+struct ll_cl_context {
+ void *lcc_cookie;
+ struct cl_io *lcc_io;
+ struct cl_page *lcc_page;
+ struct lu_env *lcc_env;
+ int lcc_refcheck;
+};
+
+struct vvp_thread_info {
+ struct vvp_io_args vti_args;
+ struct ra_io_arg vti_ria;
+ struct ll_cl_context vti_io_ctx;
+};
+
+static inline struct vvp_thread_info *vvp_env_info(const struct lu_env *env)
+{
+ extern struct lu_context_key vvp_key;
+ struct vvp_thread_info *info;
+
+ info = lu_context_key_get(&env->le_ctx, &vvp_key);
+ LASSERT(info != NULL);
+ return info;
+}
+
+static inline struct vvp_io_args *vvp_env_args(const struct lu_env *env,
+ enum vvp_io_subtype type)
+{
+ struct vvp_io_args *ret = &vvp_env_info(env)->vti_args;
+
+ ret->via_io_subtype = type;
+
+ return ret;
+}
+
+struct vvp_session {
+ struct vvp_io vs_ios;
+};
+
+static inline struct vvp_session *vvp_env_session(const struct lu_env *env)
+{
+ extern struct lu_context_key vvp_session_key;
+ struct vvp_session *ses;
+
+ ses = lu_context_key_get(env->le_ses, &vvp_session_key);
+ LASSERT(ses != NULL);
+ return ses;
+}
+
+static inline struct vvp_io *vvp_env_io(const struct lu_env *env)
+{
+ return &vvp_env_session(env)->vs_ios;
+}
+
+int vvp_global_init(void);
+void vvp_global_fini(void);
+
+void ll_queue_done_writing(struct inode *inode, unsigned long flags);
+void ll_close_thread_shutdown(struct ll_close_queue *lcq);
+int ll_close_thread_start(struct ll_close_queue **lcq_ret);
+
+/* llite/llite_mmap.c */
+
+int ll_teardown_mmaps(struct address_space *mapping, __u64 first, __u64 last);
+int ll_file_mmap(struct file *file, struct vm_area_struct *vma);
+void policy_from_vma(ldlm_policy_data_t *policy,
+ struct vm_area_struct *vma, unsigned long addr, size_t count);
+struct vm_area_struct *our_vma(struct mm_struct *mm, unsigned long addr,
+ size_t count);
+
+static inline void ll_invalidate_page(struct page *vmpage)
+{
+ struct address_space *mapping = vmpage->mapping;
+ loff_t offset = vmpage->index << PAGE_CACHE_SHIFT;
+
+ LASSERT(PageLocked(vmpage));
+ if (mapping == NULL)
+ return;
+
+ ll_teardown_mmaps(mapping, offset, offset + PAGE_CACHE_SIZE);
+ truncate_complete_page(mapping, vmpage);
+}
+
+#define ll_s2sbi(sb) (s2lsi(sb)->lsi_llsbi)
+
+/* don't need an addref as the sb_info should be holding one */
+static inline struct obd_export *ll_s2dtexp(struct super_block *sb)
+{
+ return ll_s2sbi(sb)->ll_dt_exp;
+}
+
+/* don't need an addref as the sb_info should be holding one */
+static inline struct obd_export *ll_s2mdexp(struct super_block *sb)
+{
+ return ll_s2sbi(sb)->ll_md_exp;
+}
+
+static inline struct client_obd *sbi2mdc(struct ll_sb_info *sbi)
+{
+ struct obd_device *obd = sbi->ll_md_exp->exp_obd;
+ if (obd == NULL)
+ LBUG();
+ return &obd->u.cli;
+}
+
+/* FIXME: replace the name of this with LL_SB to conform to kernel stuff */
+static inline struct ll_sb_info *ll_i2sbi(struct inode *inode)
+{
+ return ll_s2sbi(inode->i_sb);
+}
+
+static inline struct obd_export *ll_i2dtexp(struct inode *inode)
+{
+ return ll_s2dtexp(inode->i_sb);
+}
+
+static inline struct obd_export *ll_i2mdexp(struct inode *inode)
+{
+ return ll_s2mdexp(inode->i_sb);
+}
+
+static inline struct lu_fid *ll_inode2fid(struct inode *inode)
+{
+ struct lu_fid *fid;
+
+ LASSERT(inode != NULL);
+ fid = &ll_i2info(inode)->lli_fid;
+
+ return fid;
+}
+
+static inline __u64 ll_file_maxbytes(struct inode *inode)
+{
+ return ll_i2info(inode)->lli_maxbytes;
+}
+
+/* llite/xattr.c */
+int ll_setxattr(struct dentry *dentry, const char *name,
+ const void *value, size_t size, int flags);
+ssize_t ll_getxattr(struct dentry *dentry, const char *name,
+ void *buffer, size_t size);
+ssize_t ll_listxattr(struct dentry *dentry, char *buffer, size_t size);
+int ll_removexattr(struct dentry *dentry, const char *name);
+
+/* llite/remote_perm.c */
+extern struct kmem_cache *ll_remote_perm_cachep;
+extern struct kmem_cache *ll_rmtperm_hash_cachep;
+
+void free_rmtperm_hash(struct hlist_head *hash);
+int ll_update_remote_perm(struct inode *inode, struct mdt_remote_perm *perm);
+int lustre_check_remote_perm(struct inode *inode, int mask);
+
+/* llite/llite_capa.c */
+extern struct timer_list ll_capa_timer;
+
+int ll_capa_thread_start(void);
+void ll_capa_thread_stop(void);
+void ll_capa_timer_callback(unsigned long unused);
+
+struct obd_capa *ll_add_capa(struct inode *inode, struct obd_capa *ocapa);
+
+void ll_capa_open(struct inode *inode);
+void ll_capa_close(struct inode *inode);
+
+struct obd_capa *ll_mdscapa_get(struct inode *inode);
+struct obd_capa *ll_osscapa_get(struct inode *inode, __u64 opc);
+
+void ll_truncate_free_capa(struct obd_capa *ocapa);
+void ll_clear_inode_capas(struct inode *inode);
+void ll_print_capa_stat(struct ll_sb_info *sbi);
+
+/* llite/llite_cl.c */
+extern struct lu_device_type vvp_device_type;
+
+/**
+ * Common IO arguments for various VFS I/O interfaces.
+ */
+int cl_sb_init(struct super_block *sb);
+int cl_sb_fini(struct super_block *sb);
+void ll_io_init(struct cl_io *io, const struct file *file, int write);
+
+void ras_update(struct ll_sb_info *sbi, struct inode *inode,
+ struct ll_readahead_state *ras, unsigned long index,
+ unsigned hit);
+void ll_ra_count_put(struct ll_sb_info *sbi, unsigned long len);
+void ll_ra_stats_inc(struct address_space *mapping, enum ra_stat which);
+
+/* llite/llite_rmtacl.c */
+#ifdef CONFIG_FS_POSIX_ACL
+struct eacl_entry {
+ struct list_head ee_list;
+ pid_t ee_key; /* hash key */
+ struct lu_fid ee_fid;
+ int ee_type; /* ACL type for ACCESS or DEFAULT */
+ ext_acl_xattr_header *ee_acl;
+};
+
+u64 rce_ops2valid(int ops);
+struct rmtacl_ctl_entry *rct_search(struct rmtacl_ctl_table *rct, pid_t key);
+int rct_add(struct rmtacl_ctl_table *rct, pid_t key, int ops);
+int rct_del(struct rmtacl_ctl_table *rct, pid_t key);
+void rct_init(struct rmtacl_ctl_table *rct);
+void rct_fini(struct rmtacl_ctl_table *rct);
+
+void ee_free(struct eacl_entry *ee);
+int ee_add(struct eacl_table *et, pid_t key, struct lu_fid *fid, int type,
+ ext_acl_xattr_header *header);
+struct eacl_entry *et_search_del(struct eacl_table *et, pid_t key,
+ struct lu_fid *fid, int type);
+void et_search_free(struct eacl_table *et, pid_t key);
+void et_init(struct eacl_table *et);
+void et_fini(struct eacl_table *et);
+#else
+static inline u64 rce_ops2valid(int ops)
+{
+ return 0;
+}
+#endif
+
+/* statahead.c */
+
+#define LL_SA_RPC_MIN 2
+#define LL_SA_RPC_DEF 32
+#define LL_SA_RPC_MAX 8192
+
+#define LL_SA_CACHE_BIT 5
+#define LL_SA_CACHE_SIZE (1 << LL_SA_CACHE_BIT)
+#define LL_SA_CACHE_MASK (LL_SA_CACHE_SIZE - 1)
+
+/* per inode struct, for dir only */
+struct ll_statahead_info {
+ struct inode *sai_inode;
+ atomic_t sai_refcount; /* when access this struct, hold
+ * refcount */
+ unsigned int sai_generation; /* generation for statahead */
+ unsigned int sai_max; /* max ahead of lookup */
+ __u64 sai_sent; /* stat requests sent count */
+ __u64 sai_replied; /* stat requests which received
+ * reply */
+ __u64 sai_index; /* index of statahead entry */
+ __u64 sai_index_wait; /* index of entry which is the
+ * caller is waiting for */
+ __u64 sai_hit; /* hit count */
+ __u64 sai_miss; /* miss count:
+ * for "ls -al" case, it includes
+ * hidden dentry miss;
+ * for "ls -l" case, it does not
+ * include hidden dentry miss.
+ * "sai_miss_hidden" is used for
+ * the later case.
+ */
+ unsigned int sai_consecutive_miss; /* consecutive miss */
+ unsigned int sai_miss_hidden;/* "ls -al", but first dentry
+ * is not a hidden one */
+ unsigned int sai_skip_hidden;/* skipped hidden dentry count */
+ unsigned int sai_ls_all:1, /* "ls -al", do stat-ahead for
+ * hidden entries */
+ sai_agl_valid:1;/* AGL is valid for the dir */
+ wait_queue_head_t sai_waitq; /* stat-ahead wait queue */
+ struct ptlrpc_thread sai_thread; /* stat-ahead thread */
+ struct ptlrpc_thread sai_agl_thread; /* AGL thread */
+ struct list_head sai_entries; /* entry list */
+ struct list_head sai_entries_received; /* entries returned */
+ struct list_head sai_entries_stated; /* entries stated */
+ struct list_head sai_entries_agl; /* AGL entries to be sent */
+ struct list_head sai_cache[LL_SA_CACHE_SIZE];
+ spinlock_t sai_cache_lock[LL_SA_CACHE_SIZE];
+ atomic_t sai_cache_count; /* entry count in cache */
+};
+
+int do_statahead_enter(struct inode *dir, struct dentry **dentry,
+ int only_unplug);
+void ll_stop_statahead(struct inode *dir, void *key);
+
+static inline int ll_glimpse_size(struct inode *inode)
+{
+ struct ll_inode_info *lli = ll_i2info(inode);
+ int rc;
+
+ down_read(&lli->lli_glimpse_sem);
+ rc = cl_glimpse_size(inode);
+ lli->lli_glimpse_time = cfs_time_current();
+ up_read(&lli->lli_glimpse_sem);
+ return rc;
+}
+
+static inline void
+ll_statahead_mark(struct inode *dir, struct dentry *dentry)
+{
+ struct ll_inode_info *lli = ll_i2info(dir);
+ struct ll_statahead_info *sai = lli->lli_sai;
+ struct ll_dentry_data *ldd = ll_d2d(dentry);
+
+ /* not the same process, don't mark */
+ if (lli->lli_opendir_pid != current_pid())
+ return;
+
+ LASSERT(ldd != NULL);
+ if (sai != NULL)
+ ldd->lld_sa_generation = sai->sai_generation;
+}
+
+static inline int
+d_need_statahead(struct inode *dir, struct dentry *dentryp)
+{
+ struct ll_inode_info *lli;
+ struct ll_dentry_data *ldd;
+
+ if (ll_i2sbi(dir)->ll_sa_max == 0)
+ return -EAGAIN;
+
+ lli = ll_i2info(dir);
+ /* not the same process, don't statahead */
+ if (lli->lli_opendir_pid != current_pid())
+ return -EAGAIN;
+
+ /* statahead has been stopped */
+ if (lli->lli_opendir_key == NULL)
+ return -EAGAIN;
+
+ ldd = ll_d2d(dentryp);
+ /*
+ * When stats a dentry, the system trigger more than once "revalidate"
+ * or "lookup", for "getattr", for "getxattr", and maybe for others.
+ * Under patchless client mode, the operation intent is not accurate,
+ * which maybe misguide the statahead thread. For example:
+ * The "revalidate" call for "getattr" and "getxattr" of a dentry maybe
+ * have the same operation intent -- "IT_GETATTR".
+ * In fact, one dentry should has only one chance to interact with the
+ * statahead thread, otherwise the statahead windows will be confused.
+ * The solution is as following:
+ * Assign "lld_sa_generation" with "sai_generation" when a dentry
+ * "IT_GETATTR" for the first time, and the subsequent "IT_GETATTR"
+ * will bypass interacting with statahead thread for checking:
+ * "lld_sa_generation == lli_sai->sai_generation"
+ */
+ if (ldd && lli->lli_sai &&
+ ldd->lld_sa_generation == lli->lli_sai->sai_generation)
+ return -EAGAIN;
+
+ return 1;
+}
+
+static inline int
+ll_statahead_enter(struct inode *dir, struct dentry **dentryp, int only_unplug)
+{
+ int ret;
+
+ ret = d_need_statahead(dir, *dentryp);
+ if (ret <= 0)
+ return ret;
+
+ return do_statahead_enter(dir, dentryp, only_unplug);
+}
+
+/* llite ioctl register support routine */
+enum llioc_iter {
+ LLIOC_CONT = 0,
+ LLIOC_STOP
+};
+
+#define LLIOC_MAX_CMD 256
+
+/*
+ * Rules to write a callback function:
+ *
+ * Parameters:
+ * @magic: Dynamic ioctl call routine will feed this value with the pointer
+ * returned to ll_iocontrol_register. Callback functions should use this
+ * data to check the potential collasion of ioctl cmd. If collasion is
+ * found, callback function should return LLIOC_CONT.
+ * @rcp: The result of ioctl command.
+ *
+ * Return values:
+ * If @magic matches the pointer returned by ll_iocontrol_data, the
+ * callback should return LLIOC_STOP; return LLIOC_STOP otherwise.
+ */
+typedef enum llioc_iter (*llioc_callback_t)(struct inode *inode,
+ struct file *file, unsigned int cmd, unsigned long arg,
+ void *magic, int *rcp);
+
+/* export functions */
+/* Register ioctl block dynamatically for a regular file.
+ *
+ * @cmd: the array of ioctl command set
+ * @count: number of commands in the @cmd
+ * @cb: callback function, it will be called if an ioctl command is found to
+ * belong to the command list @cmd.
+ *
+ * Return value:
+ * A magic pointer will be returned if success;
+ * otherwise, NULL will be returned.
+ * */
+void *ll_iocontrol_register(llioc_callback_t cb, int count, unsigned int *cmd);
+void ll_iocontrol_unregister(void *magic);
+
+
+/* lclient compat stuff */
+#define cl_inode_info ll_inode_info
+#define cl_i2info(info) ll_i2info(info)
+#define cl_inode_mode(inode) ((inode)->i_mode)
+#define cl_i2sbi ll_i2sbi
+
+static inline struct ll_file_data *cl_iattr2fd(struct inode *inode,
+ const struct iattr *attr)
+{
+ LASSERT(attr->ia_valid & ATTR_FILE);
+ return LUSTRE_FPRIVATE(attr->ia_file);
+}
+
+static inline void cl_isize_lock(struct inode *inode)
+{
+ ll_inode_size_lock(inode);
+}
+
+static inline void cl_isize_unlock(struct inode *inode)
+{
+ ll_inode_size_unlock(inode);
+}
+
+static inline void cl_isize_write_nolock(struct inode *inode, loff_t kms)
+{
+ LASSERT(mutex_is_locked(&ll_i2info(inode)->lli_size_mutex));
+ i_size_write(inode, kms);
+}
+
+static inline void cl_isize_write(struct inode *inode, loff_t kms)
+{
+ ll_inode_size_lock(inode);
+ i_size_write(inode, kms);
+ ll_inode_size_unlock(inode);
+}
+
+#define cl_isize_read(inode) i_size_read(inode)
+
+static inline int cl_merge_lvb(const struct lu_env *env, struct inode *inode)
+{
+ return ll_merge_lvb(env, inode);
+}
+
+#define cl_inode_atime(inode) LTIME_S((inode)->i_atime)
+#define cl_inode_ctime(inode) LTIME_S((inode)->i_ctime)
+#define cl_inode_mtime(inode) LTIME_S((inode)->i_mtime)
+
+struct obd_capa *cl_capa_lookup(struct inode *inode, enum cl_req_type crt);
+
+int cl_sync_file_range(struct inode *inode, loff_t start, loff_t end,
+ enum cl_fsync_mode mode, int ignore_layout);
+
+/** direct write pages */
+struct ll_dio_pages {
+ /** page array to be written. we don't support
+ * partial pages except the last one. */
+ struct page **ldp_pages;
+ /* offset of each page */
+ loff_t *ldp_offsets;
+ /** if ldp_offsets is NULL, it means a sequential
+ * pages to be written, then this is the file offset
+ * of the * first page. */
+ loff_t ldp_start_offset;
+ /** how many bytes are to be written. */
+ size_t ldp_size;
+ /** # of pages in the array. */
+ int ldp_nr;
+};
+
+static inline void cl_stats_tally(struct cl_device *dev, enum cl_req_type crt,
+ int rc)
+{
+ int opc = (crt == CRT_READ) ? LPROC_LL_OSC_READ :
+ LPROC_LL_OSC_WRITE;
+
+ ll_stats_ops_tally(ll_s2sbi(cl2ccc_dev(dev)->cdv_sb), opc, rc);
+}
+
+extern ssize_t ll_direct_rw_pages(const struct lu_env *env, struct cl_io *io,
+ int rw, struct inode *inode,
+ struct ll_dio_pages *pv);
+
+static inline int ll_file_nolock(const struct file *file)
+{
+ struct ll_file_data *fd = LUSTRE_FPRIVATE(file);
+ struct inode *inode = file_inode(file);
+
+ LASSERT(fd != NULL);
+ return ((fd->fd_flags & LL_FILE_IGNORE_LOCK) ||
+ (ll_i2sbi(inode)->ll_flags & LL_SBI_NOLCK));
+}
+
+static inline void ll_set_lock_data(struct obd_export *exp, struct inode *inode,
+ struct lookup_intent *it, __u64 *bits)
+{
+ if (!it->d.lustre.it_lock_set) {
+ struct lustre_handle handle;
+
+ /* If this inode is a remote object, it will get two
+ * separate locks in different namespaces, Master MDT,
+ * where the name entry is, will grant LOOKUP lock,
+ * remote MDT, where the object is, will grant
+ * UPDATE|PERM lock. The inode will be attached to both
+ * LOOKUP and PERM locks, so revoking either locks will
+ * case the dcache being cleared */
+ if (it->d.lustre.it_remote_lock_mode) {
+ handle.cookie = it->d.lustre.it_remote_lock_handle;
+ CDEBUG(D_DLMTRACE, "setting l_data to inode %p(%lu/%u) for remote lock %#llx\n",
+ inode,
+ inode->i_ino, inode->i_generation,
+ handle.cookie);
+ md_set_lock_data(exp, &handle.cookie, inode, NULL);
+ }
+
+ handle.cookie = it->d.lustre.it_lock_handle;
+
+ CDEBUG(D_DLMTRACE, "setting l_data to inode %p (%lu/%u) for lock %#llx\n",
+ inode, inode->i_ino,
+ inode->i_generation, handle.cookie);
+
+ md_set_lock_data(exp, &handle.cookie, inode,
+ &it->d.lustre.it_lock_bits);
+ it->d.lustre.it_lock_set = 1;
+ }
+
+ if (bits != NULL)
+ *bits = it->d.lustre.it_lock_bits;
+}
+
+static inline void ll_lock_dcache(struct inode *inode)
+{
+ spin_lock(&inode->i_lock);
+}
+
+static inline void ll_unlock_dcache(struct inode *inode)
+{
+ spin_unlock(&inode->i_lock);
+}
+
+static inline int d_lustre_invalid(const struct dentry *dentry)
+{
+ struct ll_dentry_data *lld = ll_d2d(dentry);
+
+ return (lld == NULL) || lld->lld_invalid;
+}
+
+static inline void __d_lustre_invalidate(struct dentry *dentry)
+{
+ struct ll_dentry_data *lld = ll_d2d(dentry);
+
+ if (lld != NULL)
+ lld->lld_invalid = 1;
+}
+
+/*
+ * Mark dentry INVALID, if dentry refcount is zero (this is normally case for
+ * ll_md_blocking_ast), unhash this dentry, and let dcache to reclaim it later;
+ * else dput() of the last refcount will unhash this dentry and kill it.
+ */
+static inline void d_lustre_invalidate(struct dentry *dentry, int nested)
+{
+ CDEBUG(D_DENTRY, "invalidate dentry %pd (%p) parent %p inode %p refc %d\n",
+ dentry, dentry,
+ dentry->d_parent, d_inode(dentry), d_count(dentry));
+
+ spin_lock_nested(&dentry->d_lock,
+ nested ? DENTRY_D_LOCK_NESTED : DENTRY_D_LOCK_NORMAL);
+ __d_lustre_invalidate(dentry);
+ if (d_count(dentry) == 0)
+ __d_drop(dentry);
+ spin_unlock(&dentry->d_lock);
+}
+
+static inline void d_lustre_revalidate(struct dentry *dentry)
+{
+ spin_lock(&dentry->d_lock);
+ LASSERT(ll_d2d(dentry) != NULL);
+ ll_d2d(dentry)->lld_invalid = 0;
+ spin_unlock(&dentry->d_lock);
+}
+
+enum {
+ LL_LAYOUT_GEN_NONE = ((__u32)-2), /* layout lock was cancelled */
+ LL_LAYOUT_GEN_EMPTY = ((__u32)-1) /* for empty layout */
+};
+
+int ll_layout_conf(struct inode *inode, const struct cl_object_conf *conf);
+int ll_layout_refresh(struct inode *inode, __u32 *gen);
+int ll_layout_restore(struct inode *inode);
+
+int ll_xattr_init(void);
+void ll_xattr_fini(void);
+
+#endif /* LLITE_INTERNAL_H */
diff --git a/drivers/staging/lustre/lustre/llite/llite_lib.c b/drivers/staging/lustre/lustre/llite/llite_lib.c
new file mode 100644
index 000000000..a27af7882
--- /dev/null
+++ b/drivers/staging/lustre/lustre/llite/llite_lib.c
@@ -0,0 +1,2354 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2011, 2012, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ *
+ * lustre/llite/llite_lib.c
+ *
+ * Lustre Light Super operations
+ */
+
+#define DEBUG_SUBSYSTEM S_LLITE
+
+#include <linux/module.h>
+#include <linux/statfs.h>
+#include <linux/types.h>
+#include <linux/mm.h>
+
+#include "../include/lustre_lite.h"
+#include "../include/lustre_ha.h"
+#include "../include/lustre_dlm.h"
+#include "../include/lprocfs_status.h"
+#include "../include/lustre_disk.h"
+#include "../include/lustre_param.h"
+#include "../include/lustre_log.h"
+#include "../include/cl_object.h"
+#include "../include/obd_cksum.h"
+#include "llite_internal.h"
+
+struct kmem_cache *ll_file_data_slab;
+struct proc_dir_entry *proc_lustre_fs_root;
+
+static LIST_HEAD(ll_super_blocks);
+static DEFINE_SPINLOCK(ll_sb_lock);
+
+#ifndef log2
+#define log2(n) ffz(~(n))
+#endif
+
+static struct ll_sb_info *ll_init_sbi(void)
+{
+ struct ll_sb_info *sbi = NULL;
+ unsigned long pages;
+ unsigned long lru_page_max;
+ struct sysinfo si;
+ class_uuid_t uuid;
+ int i;
+
+ sbi = kzalloc(sizeof(*sbi), GFP_NOFS);
+ if (!sbi)
+ return NULL;
+
+ spin_lock_init(&sbi->ll_lock);
+ mutex_init(&sbi->ll_lco.lco_lock);
+ spin_lock_init(&sbi->ll_pp_extent_lock);
+ spin_lock_init(&sbi->ll_process_lock);
+ sbi->ll_rw_stats_on = 0;
+
+ si_meminfo(&si);
+ pages = si.totalram - si.totalhigh;
+ if (pages >> (20 - PAGE_CACHE_SHIFT) < 512)
+ lru_page_max = pages / 2;
+ else
+ lru_page_max = (pages / 4) * 3;
+
+ /* initialize lru data */
+ atomic_set(&sbi->ll_cache.ccc_users, 0);
+ sbi->ll_cache.ccc_lru_max = lru_page_max;
+ atomic_set(&sbi->ll_cache.ccc_lru_left, lru_page_max);
+ spin_lock_init(&sbi->ll_cache.ccc_lru_lock);
+ INIT_LIST_HEAD(&sbi->ll_cache.ccc_lru);
+
+ sbi->ll_ra_info.ra_max_pages_per_file = min(pages / 32,
+ SBI_DEFAULT_READAHEAD_MAX);
+ sbi->ll_ra_info.ra_max_pages = sbi->ll_ra_info.ra_max_pages_per_file;
+ sbi->ll_ra_info.ra_max_read_ahead_whole_pages =
+ SBI_DEFAULT_READAHEAD_WHOLE_MAX;
+ INIT_LIST_HEAD(&sbi->ll_conn_chain);
+ INIT_LIST_HEAD(&sbi->ll_orphan_dentry_list);
+
+ ll_generate_random_uuid(uuid);
+ class_uuid_unparse(uuid, &sbi->ll_sb_uuid);
+ CDEBUG(D_CONFIG, "generated uuid: %s\n", sbi->ll_sb_uuid.uuid);
+
+ spin_lock(&ll_sb_lock);
+ list_add_tail(&sbi->ll_list, &ll_super_blocks);
+ spin_unlock(&ll_sb_lock);
+
+ sbi->ll_flags |= LL_SBI_VERBOSE;
+ sbi->ll_flags |= LL_SBI_CHECKSUM;
+
+ sbi->ll_flags |= LL_SBI_LRU_RESIZE;
+
+ for (i = 0; i <= LL_PROCESS_HIST_MAX; i++) {
+ spin_lock_init(&sbi->ll_rw_extents_info.pp_extents[i].
+ pp_r_hist.oh_lock);
+ spin_lock_init(&sbi->ll_rw_extents_info.pp_extents[i].
+ pp_w_hist.oh_lock);
+ }
+
+ /* metadata statahead is enabled by default */
+ sbi->ll_sa_max = LL_SA_RPC_DEF;
+ atomic_set(&sbi->ll_sa_total, 0);
+ atomic_set(&sbi->ll_sa_wrong, 0);
+ atomic_set(&sbi->ll_agl_total, 0);
+ sbi->ll_flags |= LL_SBI_AGL_ENABLED;
+
+ return sbi;
+}
+
+static void ll_free_sbi(struct super_block *sb)
+{
+ struct ll_sb_info *sbi = ll_s2sbi(sb);
+
+ if (sbi != NULL) {
+ spin_lock(&ll_sb_lock);
+ list_del(&sbi->ll_list);
+ spin_unlock(&ll_sb_lock);
+ OBD_FREE(sbi, sizeof(*sbi));
+ }
+}
+
+static int client_common_fill_super(struct super_block *sb, char *md, char *dt,
+ struct vfsmount *mnt)
+{
+ struct inode *root = NULL;
+ struct ll_sb_info *sbi = ll_s2sbi(sb);
+ struct obd_device *obd;
+ struct obd_capa *oc = NULL;
+ struct obd_statfs *osfs = NULL;
+ struct ptlrpc_request *request = NULL;
+ struct obd_connect_data *data = NULL;
+ struct obd_uuid *uuid;
+ struct md_op_data *op_data;
+ struct lustre_md lmd;
+ u64 valid;
+ int size, err, checksum;
+
+ obd = class_name2obd(md);
+ if (!obd) {
+ CERROR("MD %s: not setup or attached\n", md);
+ return -EINVAL;
+ }
+
+ data = kzalloc(sizeof(*data), GFP_NOFS);
+ if (!data)
+ return -ENOMEM;
+
+ osfs = kzalloc(sizeof(*osfs), GFP_NOFS);
+ if (!osfs) {
+ OBD_FREE_PTR(data);
+ return -ENOMEM;
+ }
+
+ if (proc_lustre_fs_root) {
+ err = lprocfs_register_mountpoint(proc_lustre_fs_root, sb,
+ dt, md);
+ if (err < 0)
+ CERROR("could not register mount in /proc/fs/lustre\n");
+ }
+
+ /* indicate the features supported by this client */
+ data->ocd_connect_flags = OBD_CONNECT_IBITS | OBD_CONNECT_NODEVOH |
+ OBD_CONNECT_ATTRFID |
+ OBD_CONNECT_VERSION | OBD_CONNECT_BRW_SIZE |
+ OBD_CONNECT_MDS_CAPA | OBD_CONNECT_OSS_CAPA |
+ OBD_CONNECT_CANCELSET | OBD_CONNECT_FID |
+ OBD_CONNECT_AT | OBD_CONNECT_LOV_V3 |
+ OBD_CONNECT_RMT_CLIENT | OBD_CONNECT_VBR |
+ OBD_CONNECT_FULL20 | OBD_CONNECT_64BITHASH|
+ OBD_CONNECT_EINPROGRESS |
+ OBD_CONNECT_JOBSTATS | OBD_CONNECT_LVB_TYPE |
+ OBD_CONNECT_LAYOUTLOCK |
+ OBD_CONNECT_PINGLESS |
+ OBD_CONNECT_MAX_EASIZE |
+ OBD_CONNECT_FLOCK_DEAD |
+ OBD_CONNECT_DISP_STRIPE;
+
+ if (sbi->ll_flags & LL_SBI_SOM_PREVIEW)
+ data->ocd_connect_flags |= OBD_CONNECT_SOM;
+
+ if (sbi->ll_flags & LL_SBI_LRU_RESIZE)
+ data->ocd_connect_flags |= OBD_CONNECT_LRU_RESIZE;
+#ifdef CONFIG_FS_POSIX_ACL
+ data->ocd_connect_flags |= OBD_CONNECT_ACL | OBD_CONNECT_UMASK;
+#endif
+
+ if (OBD_FAIL_CHECK(OBD_FAIL_MDC_LIGHTWEIGHT))
+ /* flag mdc connection as lightweight, only used for test
+ * purpose, use with care */
+ data->ocd_connect_flags |= OBD_CONNECT_LIGHTWEIGHT;
+
+ data->ocd_ibits_known = MDS_INODELOCK_FULL;
+ data->ocd_version = LUSTRE_VERSION_CODE;
+
+ if (sb->s_flags & MS_RDONLY)
+ data->ocd_connect_flags |= OBD_CONNECT_RDONLY;
+ if (sbi->ll_flags & LL_SBI_USER_XATTR)
+ data->ocd_connect_flags |= OBD_CONNECT_XATTR;
+
+#ifdef HAVE_MS_FLOCK_LOCK
+ /* force vfs to use lustre handler for flock() calls - bug 10743 */
+ sb->s_flags |= MS_FLOCK_LOCK;
+#endif
+#ifdef MS_HAS_NEW_AOPS
+ sb->s_flags |= MS_HAS_NEW_AOPS;
+#endif
+
+ if (sbi->ll_flags & LL_SBI_FLOCK)
+ sbi->ll_fop = &ll_file_operations_flock;
+ else if (sbi->ll_flags & LL_SBI_LOCALFLOCK)
+ sbi->ll_fop = &ll_file_operations;
+ else
+ sbi->ll_fop = &ll_file_operations_noflock;
+
+ /* real client */
+ data->ocd_connect_flags |= OBD_CONNECT_REAL;
+ if (sbi->ll_flags & LL_SBI_RMT_CLIENT)
+ data->ocd_connect_flags |= OBD_CONNECT_RMT_CLIENT_FORCE;
+
+ data->ocd_brw_size = MD_MAX_BRW_SIZE;
+
+ err = obd_connect(NULL, &sbi->ll_md_exp, obd, &sbi->ll_sb_uuid,
+ data, NULL);
+ if (err == -EBUSY) {
+ LCONSOLE_ERROR_MSG(0x14f, "An MDT (md %s) is performing recovery, of which this client is not a part. Please wait for recovery to complete, abort, or time out.\n",
+ md);
+ goto out;
+ } else if (err) {
+ CERROR("cannot connect to %s: rc = %d\n", md, err);
+ goto out;
+ }
+
+ sbi->ll_md_exp->exp_connect_data = *data;
+
+ err = obd_fid_init(sbi->ll_md_exp->exp_obd, sbi->ll_md_exp,
+ LUSTRE_SEQ_METADATA);
+ if (err) {
+ CERROR("%s: Can't init metadata layer FID infrastructure, rc = %d\n",
+ sbi->ll_md_exp->exp_obd->obd_name, err);
+ goto out_md;
+ }
+
+ /* For mount, we only need fs info from MDT0, and also in DNE, it
+ * can make sure the client can be mounted as long as MDT0 is
+ * available */
+ err = obd_statfs(NULL, sbi->ll_md_exp, osfs,
+ cfs_time_shift_64(-OBD_STATFS_CACHE_SECONDS),
+ OBD_STATFS_FOR_MDT0);
+ if (err)
+ goto out_md_fid;
+
+ /* This needs to be after statfs to ensure connect has finished.
+ * Note that "data" does NOT contain the valid connect reply.
+ * If connecting to a 1.8 server there will be no LMV device, so
+ * we can access the MDC export directly and exp_connect_flags will
+ * be non-zero, but if accessing an upgraded 2.1 server it will
+ * have the correct flags filled in.
+ * XXX: fill in the LMV exp_connect_flags from MDC(s). */
+ valid = exp_connect_flags(sbi->ll_md_exp) & CLIENT_CONNECT_MDT_REQD;
+ if (exp_connect_flags(sbi->ll_md_exp) != 0 &&
+ valid != CLIENT_CONNECT_MDT_REQD) {
+ char *buf;
+
+ buf = kzalloc(PAGE_CACHE_SIZE, GFP_KERNEL);
+ obd_connect_flags2str(buf, PAGE_CACHE_SIZE,
+ valid ^ CLIENT_CONNECT_MDT_REQD, ",");
+ LCONSOLE_ERROR_MSG(0x170, "Server %s does not support feature(s) needed for correct operation of this client (%s). Please upgrade server or downgrade client.\n",
+ sbi->ll_md_exp->exp_obd->obd_name, buf);
+ OBD_FREE(buf, PAGE_CACHE_SIZE);
+ err = -EPROTO;
+ goto out_md_fid;
+ }
+
+ size = sizeof(*data);
+ err = obd_get_info(NULL, sbi->ll_md_exp, sizeof(KEY_CONN_DATA),
+ KEY_CONN_DATA, &size, data, NULL);
+ if (err) {
+ CERROR("%s: Get connect data failed: rc = %d\n",
+ sbi->ll_md_exp->exp_obd->obd_name, err);
+ goto out_md_fid;
+ }
+
+ LASSERT(osfs->os_bsize);
+ sb->s_blocksize = osfs->os_bsize;
+ sb->s_blocksize_bits = log2(osfs->os_bsize);
+ sb->s_magic = LL_SUPER_MAGIC;
+ sb->s_maxbytes = MAX_LFS_FILESIZE;
+ sbi->ll_namelen = osfs->os_namelen;
+ sbi->ll_max_rw_chunk = LL_DEFAULT_MAX_RW_CHUNK;
+
+ if ((sbi->ll_flags & LL_SBI_USER_XATTR) &&
+ !(data->ocd_connect_flags & OBD_CONNECT_XATTR)) {
+ LCONSOLE_INFO("Disabling user_xattr feature because it is not supported on the server\n");
+ sbi->ll_flags &= ~LL_SBI_USER_XATTR;
+ }
+
+ if (data->ocd_connect_flags & OBD_CONNECT_ACL) {
+#ifdef MS_POSIXACL
+ sb->s_flags |= MS_POSIXACL;
+#endif
+ sbi->ll_flags |= LL_SBI_ACL;
+ } else {
+ LCONSOLE_INFO("client wants to enable acl, but mdt not!\n");
+#ifdef MS_POSIXACL
+ sb->s_flags &= ~MS_POSIXACL;
+#endif
+ sbi->ll_flags &= ~LL_SBI_ACL;
+ }
+
+ if (data->ocd_connect_flags & OBD_CONNECT_RMT_CLIENT) {
+ if (!(sbi->ll_flags & LL_SBI_RMT_CLIENT)) {
+ sbi->ll_flags |= LL_SBI_RMT_CLIENT;
+ LCONSOLE_INFO("client is set as remote by default.\n");
+ }
+ } else {
+ if (sbi->ll_flags & LL_SBI_RMT_CLIENT) {
+ sbi->ll_flags &= ~LL_SBI_RMT_CLIENT;
+ LCONSOLE_INFO("client claims to be remote, but server rejected, forced to be local.\n");
+ }
+ }
+
+ if (data->ocd_connect_flags & OBD_CONNECT_MDS_CAPA) {
+ LCONSOLE_INFO("client enabled MDS capability!\n");
+ sbi->ll_flags |= LL_SBI_MDS_CAPA;
+ }
+
+ if (data->ocd_connect_flags & OBD_CONNECT_OSS_CAPA) {
+ LCONSOLE_INFO("client enabled OSS capability!\n");
+ sbi->ll_flags |= LL_SBI_OSS_CAPA;
+ }
+
+ if (data->ocd_connect_flags & OBD_CONNECT_64BITHASH)
+ sbi->ll_flags |= LL_SBI_64BIT_HASH;
+
+ if (data->ocd_connect_flags & OBD_CONNECT_BRW_SIZE)
+ sbi->ll_md_brw_size = data->ocd_brw_size;
+ else
+ sbi->ll_md_brw_size = PAGE_CACHE_SIZE;
+
+ if (data->ocd_connect_flags & OBD_CONNECT_LAYOUTLOCK) {
+ LCONSOLE_INFO("Layout lock feature supported.\n");
+ sbi->ll_flags |= LL_SBI_LAYOUT_LOCK;
+ }
+
+ if (data->ocd_ibits_known & MDS_INODELOCK_XATTR) {
+ if (!(data->ocd_connect_flags & OBD_CONNECT_MAX_EASIZE)) {
+ LCONSOLE_INFO(
+ "%s: disabling xattr cache due to unknown maximum xattr size.\n",
+ dt);
+ } else {
+ sbi->ll_flags |= LL_SBI_XATTR_CACHE;
+ sbi->ll_xattr_cache_enabled = 1;
+ }
+ }
+
+ obd = class_name2obd(dt);
+ if (!obd) {
+ CERROR("DT %s: not setup or attached\n", dt);
+ err = -ENODEV;
+ goto out_md_fid;
+ }
+
+ data->ocd_connect_flags = OBD_CONNECT_GRANT | OBD_CONNECT_VERSION |
+ OBD_CONNECT_REQPORTAL | OBD_CONNECT_BRW_SIZE |
+ OBD_CONNECT_CANCELSET | OBD_CONNECT_FID |
+ OBD_CONNECT_SRVLOCK | OBD_CONNECT_TRUNCLOCK|
+ OBD_CONNECT_AT | OBD_CONNECT_RMT_CLIENT |
+ OBD_CONNECT_OSS_CAPA | OBD_CONNECT_VBR|
+ OBD_CONNECT_FULL20 | OBD_CONNECT_64BITHASH |
+ OBD_CONNECT_MAXBYTES |
+ OBD_CONNECT_EINPROGRESS |
+ OBD_CONNECT_JOBSTATS | OBD_CONNECT_LVB_TYPE |
+ OBD_CONNECT_LAYOUTLOCK | OBD_CONNECT_PINGLESS;
+
+ if (sbi->ll_flags & LL_SBI_SOM_PREVIEW)
+ data->ocd_connect_flags |= OBD_CONNECT_SOM;
+
+ if (!OBD_FAIL_CHECK(OBD_FAIL_OSC_CONNECT_CKSUM)) {
+ /* OBD_CONNECT_CKSUM should always be set, even if checksums are
+ * disabled by default, because it can still be enabled on the
+ * fly via /proc. As a consequence, we still need to come to an
+ * agreement on the supported algorithms at connect time */
+ data->ocd_connect_flags |= OBD_CONNECT_CKSUM;
+
+ if (OBD_FAIL_CHECK(OBD_FAIL_OSC_CKSUM_ADLER_ONLY))
+ data->ocd_cksum_types = OBD_CKSUM_ADLER;
+ else
+ data->ocd_cksum_types = cksum_types_supported_client();
+ }
+
+ data->ocd_connect_flags |= OBD_CONNECT_LRU_RESIZE;
+ if (sbi->ll_flags & LL_SBI_RMT_CLIENT)
+ data->ocd_connect_flags |= OBD_CONNECT_RMT_CLIENT_FORCE;
+
+ CDEBUG(D_RPCTRACE, "ocd_connect_flags: %#llx ocd_version: %d ocd_grant: %d\n",
+ data->ocd_connect_flags,
+ data->ocd_version, data->ocd_grant);
+
+ obd->obd_upcall.onu_owner = &sbi->ll_lco;
+ obd->obd_upcall.onu_upcall = cl_ocd_update;
+
+ data->ocd_brw_size = DT_MAX_BRW_SIZE;
+
+ err = obd_connect(NULL, &sbi->ll_dt_exp, obd, &sbi->ll_sb_uuid, data,
+ NULL);
+ if (err == -EBUSY) {
+ LCONSOLE_ERROR_MSG(0x150, "An OST (dt %s) is performing recovery, of which this client is not a part. Please wait for recovery to complete, abort, or time out.\n",
+ dt);
+ goto out_md;
+ } else if (err) {
+ CERROR("%s: Cannot connect to %s: rc = %d\n",
+ sbi->ll_dt_exp->exp_obd->obd_name, dt, err);
+ goto out_md;
+ }
+
+ sbi->ll_dt_exp->exp_connect_data = *data;
+
+ err = obd_fid_init(sbi->ll_dt_exp->exp_obd, sbi->ll_dt_exp,
+ LUSTRE_SEQ_METADATA);
+ if (err) {
+ CERROR("%s: Can't init data layer FID infrastructure, rc = %d\n",
+ sbi->ll_dt_exp->exp_obd->obd_name, err);
+ goto out_dt;
+ }
+
+ mutex_lock(&sbi->ll_lco.lco_lock);
+ sbi->ll_lco.lco_flags = data->ocd_connect_flags;
+ sbi->ll_lco.lco_md_exp = sbi->ll_md_exp;
+ sbi->ll_lco.lco_dt_exp = sbi->ll_dt_exp;
+ mutex_unlock(&sbi->ll_lco.lco_lock);
+
+ fid_zero(&sbi->ll_root_fid);
+ err = md_getstatus(sbi->ll_md_exp, &sbi->ll_root_fid, &oc);
+ if (err) {
+ CERROR("cannot mds_connect: rc = %d\n", err);
+ goto out_lock_cn_cb;
+ }
+ if (!fid_is_sane(&sbi->ll_root_fid)) {
+ CERROR("%s: Invalid root fid "DFID" during mount\n",
+ sbi->ll_md_exp->exp_obd->obd_name,
+ PFID(&sbi->ll_root_fid));
+ err = -EINVAL;
+ goto out_lock_cn_cb;
+ }
+ CDEBUG(D_SUPER, "rootfid "DFID"\n", PFID(&sbi->ll_root_fid));
+
+ sb->s_op = &lustre_super_operations;
+#if THREAD_SIZE >= 8192 /*b=17630*/
+ sb->s_export_op = &lustre_export_operations;
+#endif
+
+ /* make root inode
+ * XXX: move this to after cbd setup? */
+ valid = OBD_MD_FLGETATTR | OBD_MD_FLBLOCKS | OBD_MD_FLMDSCAPA;
+ if (sbi->ll_flags & LL_SBI_RMT_CLIENT)
+ valid |= OBD_MD_FLRMTPERM;
+ else if (sbi->ll_flags & LL_SBI_ACL)
+ valid |= OBD_MD_FLACL;
+
+ op_data = kzalloc(sizeof(*op_data), GFP_NOFS);
+ if (!op_data) {
+ err = -ENOMEM;
+ goto out_lock_cn_cb;
+ }
+
+ op_data->op_fid1 = sbi->ll_root_fid;
+ op_data->op_mode = 0;
+ op_data->op_capa1 = oc;
+ op_data->op_valid = valid;
+
+ err = md_getattr(sbi->ll_md_exp, op_data, &request);
+ if (oc)
+ capa_put(oc);
+ OBD_FREE_PTR(op_data);
+ if (err) {
+ CERROR("%s: md_getattr failed for root: rc = %d\n",
+ sbi->ll_md_exp->exp_obd->obd_name, err);
+ goto out_lock_cn_cb;
+ }
+
+ err = md_get_lustre_md(sbi->ll_md_exp, request, sbi->ll_dt_exp,
+ sbi->ll_md_exp, &lmd);
+ if (err) {
+ CERROR("failed to understand root inode md: rc = %d\n", err);
+ ptlrpc_req_finished(request);
+ goto out_lock_cn_cb;
+ }
+
+ LASSERT(fid_is_sane(&sbi->ll_root_fid));
+ root = ll_iget(sb, cl_fid_build_ino(&sbi->ll_root_fid,
+ sbi->ll_flags & LL_SBI_32BIT_API),
+ &lmd);
+ md_free_lustre_md(sbi->ll_md_exp, &lmd);
+ ptlrpc_req_finished(request);
+
+ if (root == NULL || IS_ERR(root)) {
+ if (lmd.lsm)
+ obd_free_memmd(sbi->ll_dt_exp, &lmd.lsm);
+#ifdef CONFIG_FS_POSIX_ACL
+ if (lmd.posix_acl) {
+ posix_acl_release(lmd.posix_acl);
+ lmd.posix_acl = NULL;
+ }
+#endif
+ err = IS_ERR(root) ? PTR_ERR(root) : -EBADF;
+ root = NULL;
+ CERROR("lustre_lite: bad iget4 for root\n");
+ goto out_root;
+ }
+
+ err = ll_close_thread_start(&sbi->ll_lcq);
+ if (err) {
+ CERROR("cannot start close thread: rc %d\n", err);
+ goto out_root;
+ }
+
+#ifdef CONFIG_FS_POSIX_ACL
+ if (sbi->ll_flags & LL_SBI_RMT_CLIENT) {
+ rct_init(&sbi->ll_rct);
+ et_init(&sbi->ll_et);
+ }
+#endif
+
+ checksum = sbi->ll_flags & LL_SBI_CHECKSUM;
+ err = obd_set_info_async(NULL, sbi->ll_dt_exp, sizeof(KEY_CHECKSUM),
+ KEY_CHECKSUM, sizeof(checksum), &checksum,
+ NULL);
+ cl_sb_init(sb);
+
+ err = obd_set_info_async(NULL, sbi->ll_dt_exp, sizeof(KEY_CACHE_SET),
+ KEY_CACHE_SET, sizeof(sbi->ll_cache),
+ &sbi->ll_cache, NULL);
+
+ sb->s_root = d_make_root(root);
+ if (sb->s_root == NULL) {
+ CERROR("%s: can't make root dentry\n",
+ ll_get_fsname(sb, NULL, 0));
+ err = -ENOMEM;
+ goto out_lock_cn_cb;
+ }
+
+ sbi->ll_sdev_orig = sb->s_dev;
+
+ /* We set sb->s_dev equal on all lustre clients in order to support
+ * NFS export clustering. NFSD requires that the FSID be the same
+ * on all clients. */
+ /* s_dev is also used in lt_compare() to compare two fs, but that is
+ * only a node-local comparison. */
+ uuid = obd_get_uuid(sbi->ll_md_exp);
+ if (uuid != NULL) {
+ sb->s_dev = get_uuid2int(uuid->uuid, strlen(uuid->uuid));
+ get_uuid2fsid(uuid->uuid, strlen(uuid->uuid), &sbi->ll_fsid);
+ }
+
+ if (data != NULL)
+ OBD_FREE_PTR(data);
+ if (osfs != NULL)
+ OBD_FREE_PTR(osfs);
+
+ return err;
+out_root:
+ iput(root);
+out_lock_cn_cb:
+ obd_fid_fini(sbi->ll_dt_exp->exp_obd);
+out_dt:
+ obd_disconnect(sbi->ll_dt_exp);
+ sbi->ll_dt_exp = NULL;
+ /* Make sure all OScs are gone, since cl_cache is accessing sbi. */
+ obd_zombie_barrier();
+out_md_fid:
+ obd_fid_fini(sbi->ll_md_exp->exp_obd);
+out_md:
+ obd_disconnect(sbi->ll_md_exp);
+ sbi->ll_md_exp = NULL;
+out:
+ if (data != NULL)
+ OBD_FREE_PTR(data);
+ if (osfs != NULL)
+ OBD_FREE_PTR(osfs);
+ lprocfs_unregister_mountpoint(sbi);
+ return err;
+}
+
+int ll_get_max_mdsize(struct ll_sb_info *sbi, int *lmmsize)
+{
+ int size, rc;
+
+ *lmmsize = obd_size_diskmd(sbi->ll_dt_exp, NULL);
+ size = sizeof(int);
+ rc = obd_get_info(NULL, sbi->ll_md_exp, sizeof(KEY_MAX_EASIZE),
+ KEY_MAX_EASIZE, &size, lmmsize, NULL);
+ if (rc)
+ CERROR("Get max mdsize error rc %d\n", rc);
+
+ return rc;
+}
+
+int ll_get_default_mdsize(struct ll_sb_info *sbi, int *lmmsize)
+{
+ int size, rc;
+
+ size = sizeof(int);
+ rc = obd_get_info(NULL, sbi->ll_md_exp, sizeof(KEY_DEFAULT_EASIZE),
+ KEY_DEFAULT_EASIZE, &size, lmmsize, NULL);
+ if (rc)
+ CERROR("Get default mdsize error rc %d\n", rc);
+
+ return rc;
+}
+
+int ll_get_max_cookiesize(struct ll_sb_info *sbi, int *lmmsize)
+{
+ int size, rc;
+
+ size = sizeof(int);
+ rc = obd_get_info(NULL, sbi->ll_md_exp, sizeof(KEY_MAX_COOKIESIZE),
+ KEY_MAX_COOKIESIZE, &size, lmmsize, NULL);
+ if (rc)
+ CERROR("Get max cookiesize error rc %d\n", rc);
+
+ return rc;
+}
+
+int ll_get_default_cookiesize(struct ll_sb_info *sbi, int *lmmsize)
+{
+ int size, rc;
+
+ size = sizeof(int);
+ rc = obd_get_info(NULL, sbi->ll_md_exp, sizeof(KEY_DEFAULT_COOKIESIZE),
+ KEY_DEFAULT_COOKIESIZE, &size, lmmsize, NULL);
+ if (rc)
+ CERROR("Get default cookiesize error rc %d\n", rc);
+
+ return rc;
+}
+
+static void client_common_put_super(struct super_block *sb)
+{
+ struct ll_sb_info *sbi = ll_s2sbi(sb);
+
+#ifdef CONFIG_FS_POSIX_ACL
+ if (sbi->ll_flags & LL_SBI_RMT_CLIENT) {
+ et_fini(&sbi->ll_et);
+ rct_fini(&sbi->ll_rct);
+ }
+#endif
+
+ ll_close_thread_shutdown(sbi->ll_lcq);
+
+ cl_sb_fini(sb);
+
+ list_del(&sbi->ll_conn_chain);
+
+ obd_fid_fini(sbi->ll_dt_exp->exp_obd);
+ obd_disconnect(sbi->ll_dt_exp);
+ sbi->ll_dt_exp = NULL;
+ /* wait till all OSCs are gone, since cl_cache is accessing sbi.
+ * see LU-2543. */
+ obd_zombie_barrier();
+
+ lprocfs_unregister_mountpoint(sbi);
+
+ obd_fid_fini(sbi->ll_md_exp->exp_obd);
+ obd_disconnect(sbi->ll_md_exp);
+ sbi->ll_md_exp = NULL;
+}
+
+void ll_kill_super(struct super_block *sb)
+{
+ struct ll_sb_info *sbi;
+
+ /* not init sb ?*/
+ if (!(sb->s_flags & MS_ACTIVE))
+ return;
+
+ sbi = ll_s2sbi(sb);
+ /* we need to restore s_dev from changed for clustered NFS before
+ * put_super because new kernels have cached s_dev and change sb->s_dev
+ * in put_super not affected real removing devices */
+ if (sbi) {
+ sb->s_dev = sbi->ll_sdev_orig;
+ sbi->ll_umounting = 1;
+ }
+}
+
+static inline int ll_set_opt(const char *opt, char *data, int fl)
+{
+ if (strncmp(opt, data, strlen(opt)) != 0)
+ return 0;
+ else
+ return fl;
+}
+
+/* non-client-specific mount options are parsed in lmd_parse */
+static int ll_options(char *options, int *flags)
+{
+ int tmp;
+ char *s1 = options, *s2;
+
+ if (!options)
+ return 0;
+
+ CDEBUG(D_CONFIG, "Parsing opts %s\n", options);
+
+ while (*s1) {
+ CDEBUG(D_SUPER, "next opt=%s\n", s1);
+ tmp = ll_set_opt("nolock", s1, LL_SBI_NOLCK);
+ if (tmp) {
+ *flags |= tmp;
+ goto next;
+ }
+ tmp = ll_set_opt("flock", s1, LL_SBI_FLOCK);
+ if (tmp) {
+ *flags |= tmp;
+ goto next;
+ }
+ tmp = ll_set_opt("localflock", s1, LL_SBI_LOCALFLOCK);
+ if (tmp) {
+ *flags |= tmp;
+ goto next;
+ }
+ tmp = ll_set_opt("noflock", s1, LL_SBI_FLOCK|LL_SBI_LOCALFLOCK);
+ if (tmp) {
+ *flags &= ~tmp;
+ goto next;
+ }
+ tmp = ll_set_opt("user_xattr", s1, LL_SBI_USER_XATTR);
+ if (tmp) {
+ *flags |= tmp;
+ goto next;
+ }
+ tmp = ll_set_opt("nouser_xattr", s1, LL_SBI_USER_XATTR);
+ if (tmp) {
+ *flags &= ~tmp;
+ goto next;
+ }
+ tmp = ll_set_opt("remote_client", s1, LL_SBI_RMT_CLIENT);
+ if (tmp) {
+ *flags |= tmp;
+ goto next;
+ }
+ tmp = ll_set_opt("user_fid2path", s1, LL_SBI_USER_FID2PATH);
+ if (tmp) {
+ *flags |= tmp;
+ goto next;
+ }
+ tmp = ll_set_opt("nouser_fid2path", s1, LL_SBI_USER_FID2PATH);
+ if (tmp) {
+ *flags &= ~tmp;
+ goto next;
+ }
+
+ tmp = ll_set_opt("checksum", s1, LL_SBI_CHECKSUM);
+ if (tmp) {
+ *flags |= tmp;
+ goto next;
+ }
+ tmp = ll_set_opt("nochecksum", s1, LL_SBI_CHECKSUM);
+ if (tmp) {
+ *flags &= ~tmp;
+ goto next;
+ }
+ tmp = ll_set_opt("lruresize", s1, LL_SBI_LRU_RESIZE);
+ if (tmp) {
+ *flags |= tmp;
+ goto next;
+ }
+ tmp = ll_set_opt("nolruresize", s1, LL_SBI_LRU_RESIZE);
+ if (tmp) {
+ *flags &= ~tmp;
+ goto next;
+ }
+ tmp = ll_set_opt("lazystatfs", s1, LL_SBI_LAZYSTATFS);
+ if (tmp) {
+ *flags |= tmp;
+ goto next;
+ }
+ tmp = ll_set_opt("nolazystatfs", s1, LL_SBI_LAZYSTATFS);
+ if (tmp) {
+ *flags &= ~tmp;
+ goto next;
+ }
+ tmp = ll_set_opt("som_preview", s1, LL_SBI_SOM_PREVIEW);
+ if (tmp) {
+ *flags |= tmp;
+ goto next;
+ }
+ tmp = ll_set_opt("32bitapi", s1, LL_SBI_32BIT_API);
+ if (tmp) {
+ *flags |= tmp;
+ goto next;
+ }
+ tmp = ll_set_opt("verbose", s1, LL_SBI_VERBOSE);
+ if (tmp) {
+ *flags |= tmp;
+ goto next;
+ }
+ tmp = ll_set_opt("noverbose", s1, LL_SBI_VERBOSE);
+ if (tmp) {
+ *flags &= ~tmp;
+ goto next;
+ }
+ LCONSOLE_ERROR_MSG(0x152, "Unknown option '%s', won't mount.\n",
+ s1);
+ return -EINVAL;
+
+next:
+ /* Find next opt */
+ s2 = strchr(s1, ',');
+ if (s2 == NULL)
+ break;
+ s1 = s2 + 1;
+ }
+ return 0;
+}
+
+void ll_lli_init(struct ll_inode_info *lli)
+{
+ lli->lli_inode_magic = LLI_INODE_MAGIC;
+ lli->lli_flags = 0;
+ lli->lli_ioepoch = 0;
+ lli->lli_maxbytes = MAX_LFS_FILESIZE;
+ spin_lock_init(&lli->lli_lock);
+ lli->lli_posix_acl = NULL;
+ lli->lli_remote_perms = NULL;
+ mutex_init(&lli->lli_rmtperm_mutex);
+ /* Do not set lli_fid, it has been initialized already. */
+ fid_zero(&lli->lli_pfid);
+ INIT_LIST_HEAD(&lli->lli_close_list);
+ INIT_LIST_HEAD(&lli->lli_oss_capas);
+ atomic_set(&lli->lli_open_count, 0);
+ lli->lli_mds_capa = NULL;
+ lli->lli_rmtperm_time = 0;
+ lli->lli_pending_och = NULL;
+ lli->lli_mds_read_och = NULL;
+ lli->lli_mds_write_och = NULL;
+ lli->lli_mds_exec_och = NULL;
+ lli->lli_open_fd_read_count = 0;
+ lli->lli_open_fd_write_count = 0;
+ lli->lli_open_fd_exec_count = 0;
+ mutex_init(&lli->lli_och_mutex);
+ spin_lock_init(&lli->lli_agl_lock);
+ lli->lli_has_smd = false;
+ spin_lock_init(&lli->lli_layout_lock);
+ ll_layout_version_set(lli, LL_LAYOUT_GEN_NONE);
+ lli->lli_clob = NULL;
+
+ init_rwsem(&lli->lli_xattrs_list_rwsem);
+ mutex_init(&lli->lli_xattrs_enq_lock);
+
+ LASSERT(lli->lli_vfs_inode.i_mode != 0);
+ if (S_ISDIR(lli->lli_vfs_inode.i_mode)) {
+ mutex_init(&lli->lli_readdir_mutex);
+ lli->lli_opendir_key = NULL;
+ lli->lli_sai = NULL;
+ spin_lock_init(&lli->lli_sa_lock);
+ lli->lli_opendir_pid = 0;
+ } else {
+ mutex_init(&lli->lli_size_mutex);
+ lli->lli_symlink_name = NULL;
+ init_rwsem(&lli->lli_trunc_sem);
+ mutex_init(&lli->lli_write_mutex);
+ init_rwsem(&lli->lli_glimpse_sem);
+ lli->lli_glimpse_time = 0;
+ INIT_LIST_HEAD(&lli->lli_agl_list);
+ lli->lli_agl_index = 0;
+ lli->lli_async_rc = 0;
+ }
+ mutex_init(&lli->lli_layout_mutex);
+}
+
+static inline int ll_bdi_register(struct backing_dev_info *bdi)
+{
+ static atomic_t ll_bdi_num = ATOMIC_INIT(0);
+
+ bdi->name = "lustre";
+ return bdi_register(bdi, NULL, "lustre-%d",
+ atomic_inc_return(&ll_bdi_num));
+}
+
+int ll_fill_super(struct super_block *sb, struct vfsmount *mnt)
+{
+ struct lustre_profile *lprof = NULL;
+ struct lustre_sb_info *lsi = s2lsi(sb);
+ struct ll_sb_info *sbi;
+ char *dt = NULL, *md = NULL;
+ char *profilenm = get_profile_name(sb);
+ struct config_llog_instance *cfg;
+ /* %p for void* in printf needs 16+2 characters: 0xffffffffffffffff */
+ const int instlen = sizeof(cfg->cfg_instance) * 2 + 2;
+ int err;
+
+ CDEBUG(D_VFSTRACE, "VFS Op: sb %p\n", sb);
+
+ cfg = kzalloc(sizeof(*cfg), GFP_NOFS);
+ if (!cfg)
+ return -ENOMEM;
+
+ try_module_get(THIS_MODULE);
+
+ /* client additional sb info */
+ lsi->lsi_llsbi = sbi = ll_init_sbi();
+ if (!sbi) {
+ module_put(THIS_MODULE);
+ OBD_FREE_PTR(cfg);
+ return -ENOMEM;
+ }
+
+ err = ll_options(lsi->lsi_lmd->lmd_opts, &sbi->ll_flags);
+ if (err)
+ goto out_free;
+
+ err = bdi_init(&lsi->lsi_bdi);
+ if (err)
+ goto out_free;
+ lsi->lsi_flags |= LSI_BDI_INITIALIZED;
+ lsi->lsi_bdi.capabilities = 0;
+ err = ll_bdi_register(&lsi->lsi_bdi);
+ if (err)
+ goto out_free;
+
+ sb->s_bdi = &lsi->lsi_bdi;
+ /* kernel >= 2.6.38 store dentry operations in sb->s_d_op. */
+ sb->s_d_op = &ll_d_ops;
+
+ /* Generate a string unique to this super, in case some joker tries
+ to mount the same fs at two mount points.
+ Use the address of the super itself.*/
+ cfg->cfg_instance = sb;
+ cfg->cfg_uuid = lsi->lsi_llsbi->ll_sb_uuid;
+ cfg->cfg_callback = class_config_llog_handler;
+ /* set up client obds */
+ err = lustre_process_log(sb, profilenm, cfg);
+ if (err < 0) {
+ CERROR("Unable to process log: %d\n", err);
+ goto out_free;
+ }
+
+ /* Profile set with LCFG_MOUNTOPT so we can find our mdc and osc obds */
+ lprof = class_get_profile(profilenm);
+ if (lprof == NULL) {
+ LCONSOLE_ERROR_MSG(0x156, "The client profile '%s' could not be read from the MGS. Does that filesystem exist?\n",
+ profilenm);
+ err = -EINVAL;
+ goto out_free;
+ }
+ CDEBUG(D_CONFIG, "Found profile %s: mdc=%s osc=%s\n", profilenm,
+ lprof->lp_md, lprof->lp_dt);
+
+ dt = kasprintf(GFP_NOFS, "%s-%p", lprof->lp_dt, cfg->cfg_instance);
+ if (!dt) {
+ err = -ENOMEM;
+ goto out_free;
+ }
+
+ md = kasprintf(GFP_NOFS, "%s-%p", lprof->lp_md, cfg->cfg_instance);
+ if (!md) {
+ err = -ENOMEM;
+ goto out_free;
+ }
+
+ /* connections, registrations, sb setup */
+ err = client_common_fill_super(sb, md, dt, mnt);
+
+out_free:
+ if (md)
+ OBD_FREE(md, strlen(lprof->lp_md) + instlen + 2);
+ if (dt)
+ OBD_FREE(dt, strlen(lprof->lp_dt) + instlen + 2);
+ if (err)
+ ll_put_super(sb);
+ else if (sbi->ll_flags & LL_SBI_VERBOSE)
+ LCONSOLE_WARN("Mounted %s\n", profilenm);
+
+ OBD_FREE_PTR(cfg);
+ return err;
+} /* ll_fill_super */
+
+void ll_put_super(struct super_block *sb)
+{
+ struct config_llog_instance cfg, params_cfg;
+ struct obd_device *obd;
+ struct lustre_sb_info *lsi = s2lsi(sb);
+ struct ll_sb_info *sbi = ll_s2sbi(sb);
+ char *profilenm = get_profile_name(sb);
+ int next, force = 1;
+
+ CDEBUG(D_VFSTRACE, "VFS Op: sb %p - %s\n", sb, profilenm);
+
+ ll_print_capa_stat(sbi);
+
+ cfg.cfg_instance = sb;
+ lustre_end_log(sb, profilenm, &cfg);
+
+ params_cfg.cfg_instance = sb;
+ lustre_end_log(sb, PARAMS_FILENAME, &params_cfg);
+
+ if (sbi->ll_md_exp) {
+ obd = class_exp2obd(sbi->ll_md_exp);
+ if (obd)
+ force = obd->obd_force;
+ }
+
+ /* We need to set force before the lov_disconnect in
+ lustre_common_put_super, since l_d cleans up osc's as well. */
+ if (force) {
+ next = 0;
+ while ((obd = class_devices_in_group(&sbi->ll_sb_uuid,
+ &next)) != NULL) {
+ obd->obd_force = force;
+ }
+ }
+
+ if (sbi->ll_lcq) {
+ /* Only if client_common_fill_super succeeded */
+ client_common_put_super(sb);
+ }
+
+ next = 0;
+ while ((obd = class_devices_in_group(&sbi->ll_sb_uuid, &next)))
+ class_manual_cleanup(obd);
+
+ if (sbi->ll_flags & LL_SBI_VERBOSE)
+ LCONSOLE_WARN("Unmounted %s\n", profilenm ? profilenm : "");
+
+ if (profilenm)
+ class_del_profile(profilenm);
+
+ if (lsi->lsi_flags & LSI_BDI_INITIALIZED) {
+ bdi_destroy(&lsi->lsi_bdi);
+ lsi->lsi_flags &= ~LSI_BDI_INITIALIZED;
+ }
+
+ ll_free_sbi(sb);
+ lsi->lsi_llsbi = NULL;
+
+ lustre_common_put_super(sb);
+
+ module_put(THIS_MODULE);
+} /* client_put_super */
+
+struct inode *ll_inode_from_resource_lock(struct ldlm_lock *lock)
+{
+ struct inode *inode = NULL;
+
+ /* NOTE: we depend on atomic igrab() -bzzz */
+ lock_res_and_lock(lock);
+ if (lock->l_resource->lr_lvb_inode) {
+ struct ll_inode_info *lli;
+
+ lli = ll_i2info(lock->l_resource->lr_lvb_inode);
+ if (lli->lli_inode_magic == LLI_INODE_MAGIC) {
+ inode = igrab(lock->l_resource->lr_lvb_inode);
+ } else {
+ inode = lock->l_resource->lr_lvb_inode;
+ LDLM_DEBUG_LIMIT(inode->i_state & I_FREEING ? D_INFO :
+ D_WARNING, lock, "lr_lvb_inode %p is bogus: magic %08x",
+ lock->l_resource->lr_lvb_inode,
+ lli->lli_inode_magic);
+ inode = NULL;
+ }
+ }
+ unlock_res_and_lock(lock);
+ return inode;
+}
+
+void ll_clear_inode(struct inode *inode)
+{
+ struct ll_inode_info *lli = ll_i2info(inode);
+ struct ll_sb_info *sbi = ll_i2sbi(inode);
+
+ CDEBUG(D_VFSTRACE, "VFS Op:inode=%lu/%u(%p)\n", inode->i_ino,
+ inode->i_generation, inode);
+
+ if (S_ISDIR(inode->i_mode)) {
+ /* these should have been cleared in ll_file_release */
+ LASSERT(lli->lli_opendir_key == NULL);
+ LASSERT(lli->lli_sai == NULL);
+ LASSERT(lli->lli_opendir_pid == 0);
+ }
+
+ spin_lock(&lli->lli_lock);
+ ll_i2info(inode)->lli_flags &= ~LLIF_MDS_SIZE_LOCK;
+ spin_unlock(&lli->lli_lock);
+ md_null_inode(sbi->ll_md_exp, ll_inode2fid(inode));
+
+ LASSERT(!lli->lli_open_fd_write_count);
+ LASSERT(!lli->lli_open_fd_read_count);
+ LASSERT(!lli->lli_open_fd_exec_count);
+
+ if (lli->lli_mds_write_och)
+ ll_md_real_close(inode, FMODE_WRITE);
+ if (lli->lli_mds_exec_och)
+ ll_md_real_close(inode, FMODE_EXEC);
+ if (lli->lli_mds_read_och)
+ ll_md_real_close(inode, FMODE_READ);
+
+ if (S_ISLNK(inode->i_mode) && lli->lli_symlink_name) {
+ OBD_FREE(lli->lli_symlink_name,
+ strlen(lli->lli_symlink_name) + 1);
+ lli->lli_symlink_name = NULL;
+ }
+
+ ll_xattr_cache_destroy(inode);
+
+ if (sbi->ll_flags & LL_SBI_RMT_CLIENT) {
+ LASSERT(lli->lli_posix_acl == NULL);
+ if (lli->lli_remote_perms) {
+ free_rmtperm_hash(lli->lli_remote_perms);
+ lli->lli_remote_perms = NULL;
+ }
+ }
+#ifdef CONFIG_FS_POSIX_ACL
+ else if (lli->lli_posix_acl) {
+ LASSERT(atomic_read(&lli->lli_posix_acl->a_refcount) == 1);
+ LASSERT(lli->lli_remote_perms == NULL);
+ posix_acl_release(lli->lli_posix_acl);
+ lli->lli_posix_acl = NULL;
+ }
+#endif
+ lli->lli_inode_magic = LLI_INODE_DEAD;
+
+ ll_clear_inode_capas(inode);
+ if (!S_ISDIR(inode->i_mode))
+ LASSERT(list_empty(&lli->lli_agl_list));
+
+ /*
+ * XXX This has to be done before lsm is freed below, because
+ * cl_object still uses inode lsm.
+ */
+ cl_inode_fini(inode);
+ lli->lli_has_smd = false;
+}
+
+static int ll_md_setattr(struct dentry *dentry, struct md_op_data *op_data,
+ struct md_open_data **mod)
+{
+ struct lustre_md md;
+ struct inode *inode = d_inode(dentry);
+ struct ll_sb_info *sbi = ll_i2sbi(inode);
+ struct ptlrpc_request *request = NULL;
+ int rc, ia_valid;
+
+ op_data = ll_prep_md_op_data(op_data, inode, NULL, NULL, 0, 0,
+ LUSTRE_OPC_ANY, NULL);
+ if (IS_ERR(op_data))
+ return PTR_ERR(op_data);
+
+ rc = md_setattr(sbi->ll_md_exp, op_data, NULL, 0, NULL, 0,
+ &request, mod);
+ if (rc) {
+ ptlrpc_req_finished(request);
+ if (rc == -ENOENT) {
+ clear_nlink(inode);
+ /* Unlinked special device node? Or just a race?
+ * Pretend we done everything. */
+ if (!S_ISREG(inode->i_mode) &&
+ !S_ISDIR(inode->i_mode)) {
+ ia_valid = op_data->op_attr.ia_valid;
+ op_data->op_attr.ia_valid &= ~TIMES_SET_FLAGS;
+ rc = simple_setattr(dentry, &op_data->op_attr);
+ op_data->op_attr.ia_valid = ia_valid;
+ }
+ } else if (rc != -EPERM && rc != -EACCES && rc != -ETXTBSY) {
+ CERROR("md_setattr fails: rc = %d\n", rc);
+ }
+ return rc;
+ }
+
+ rc = md_get_lustre_md(sbi->ll_md_exp, request, sbi->ll_dt_exp,
+ sbi->ll_md_exp, &md);
+ if (rc) {
+ ptlrpc_req_finished(request);
+ return rc;
+ }
+
+ ia_valid = op_data->op_attr.ia_valid;
+ /* inode size will be in ll_setattr_ost, can't do it now since dirty
+ * cache is not cleared yet. */
+ op_data->op_attr.ia_valid &= ~(TIMES_SET_FLAGS | ATTR_SIZE);
+ rc = simple_setattr(dentry, &op_data->op_attr);
+ op_data->op_attr.ia_valid = ia_valid;
+
+ /* Extract epoch data if obtained. */
+ op_data->op_handle = md.body->handle;
+ op_data->op_ioepoch = md.body->ioepoch;
+
+ ll_update_inode(inode, &md);
+ ptlrpc_req_finished(request);
+
+ return rc;
+}
+
+/* Close IO epoch and send Size-on-MDS attribute update. */
+static int ll_setattr_done_writing(struct inode *inode,
+ struct md_op_data *op_data,
+ struct md_open_data *mod)
+{
+ struct ll_inode_info *lli = ll_i2info(inode);
+ int rc = 0;
+
+ LASSERT(op_data != NULL);
+ if (!S_ISREG(inode->i_mode))
+ return 0;
+
+ CDEBUG(D_INODE, "Epoch %llu closed on "DFID" for truncate\n",
+ op_data->op_ioepoch, PFID(&lli->lli_fid));
+
+ op_data->op_flags = MF_EPOCH_CLOSE;
+ ll_done_writing_attr(inode, op_data);
+ ll_pack_inode2opdata(inode, op_data, NULL);
+
+ rc = md_done_writing(ll_i2sbi(inode)->ll_md_exp, op_data, mod);
+ if (rc == -EAGAIN) {
+ /* MDS has instructed us to obtain Size-on-MDS attribute
+ * from OSTs and send setattr to back to MDS. */
+ rc = ll_som_update(inode, op_data);
+ } else if (rc) {
+ CERROR("inode %lu mdc truncate failed: rc = %d\n",
+ inode->i_ino, rc);
+ }
+ return rc;
+}
+
+static int ll_setattr_ost(struct inode *inode, struct iattr *attr)
+{
+ struct obd_capa *capa;
+ int rc;
+
+ if (attr->ia_valid & ATTR_SIZE)
+ capa = ll_osscapa_get(inode, CAPA_OPC_OSS_TRUNC);
+ else
+ capa = ll_mdscapa_get(inode);
+
+ rc = cl_setattr_ost(inode, attr, capa);
+
+ if (attr->ia_valid & ATTR_SIZE)
+ ll_truncate_free_capa(capa);
+ else
+ capa_put(capa);
+
+ return rc;
+}
+
+
+/* If this inode has objects allocated to it (lsm != NULL), then the OST
+ * object(s) determine the file size and mtime. Otherwise, the MDS will
+ * keep these values until such a time that objects are allocated for it.
+ * We do the MDS operations first, as it is checking permissions for us.
+ * We don't to the MDS RPC if there is nothing that we want to store there,
+ * otherwise there is no harm in updating mtime/atime on the MDS if we are
+ * going to do an RPC anyways.
+ *
+ * If we are doing a truncate, we will send the mtime and ctime updates
+ * to the OST with the punch RPC, otherwise we do an explicit setattr RPC.
+ * I don't believe it is possible to get e.g. ATTR_MTIME_SET and ATTR_SIZE
+ * at the same time.
+ *
+ * In case of HSMimport, we only set attr on MDS.
+ */
+int ll_setattr_raw(struct dentry *dentry, struct iattr *attr, bool hsm_import)
+{
+ struct inode *inode = d_inode(dentry);
+ struct ll_inode_info *lli = ll_i2info(inode);
+ struct md_op_data *op_data = NULL;
+ struct md_open_data *mod = NULL;
+ bool file_is_released = false;
+ int rc = 0, rc1 = 0;
+
+ CDEBUG(D_VFSTRACE,
+ "%s: setattr inode %p/fid:"DFID
+ " from %llu to %llu, valid %x, hsm_import %d\n",
+ ll_get_fsname(inode->i_sb, NULL, 0), inode,
+ PFID(&lli->lli_fid), i_size_read(inode), attr->ia_size,
+ attr->ia_valid, hsm_import);
+
+ if (attr->ia_valid & ATTR_SIZE) {
+ /* Check new size against VFS/VM file size limit and rlimit */
+ rc = inode_newsize_ok(inode, attr->ia_size);
+ if (rc)
+ return rc;
+
+ /* The maximum Lustre file size is variable, based on the
+ * OST maximum object size and number of stripes. This
+ * needs another check in addition to the VFS check above. */
+ if (attr->ia_size > ll_file_maxbytes(inode)) {
+ CDEBUG(D_INODE, "file "DFID" too large %llu > %llu\n",
+ PFID(&lli->lli_fid), attr->ia_size,
+ ll_file_maxbytes(inode));
+ return -EFBIG;
+ }
+
+ attr->ia_valid |= ATTR_MTIME | ATTR_CTIME;
+ }
+
+ /* POSIX: check before ATTR_*TIME_SET set (from inode_change_ok) */
+ if (attr->ia_valid & TIMES_SET_FLAGS) {
+ if ((!uid_eq(current_fsuid(), inode->i_uid)) &&
+ !capable(CFS_CAP_FOWNER))
+ return -EPERM;
+ }
+
+ /* We mark all of the fields "set" so MDS/OST does not re-set them */
+ if (attr->ia_valid & ATTR_CTIME) {
+ attr->ia_ctime = CURRENT_TIME;
+ attr->ia_valid |= ATTR_CTIME_SET;
+ }
+ if (!(attr->ia_valid & ATTR_ATIME_SET) &&
+ (attr->ia_valid & ATTR_ATIME)) {
+ attr->ia_atime = CURRENT_TIME;
+ attr->ia_valid |= ATTR_ATIME_SET;
+ }
+ if (!(attr->ia_valid & ATTR_MTIME_SET) &&
+ (attr->ia_valid & ATTR_MTIME)) {
+ attr->ia_mtime = CURRENT_TIME;
+ attr->ia_valid |= ATTR_MTIME_SET;
+ }
+
+ if (attr->ia_valid & (ATTR_MTIME | ATTR_CTIME))
+ CDEBUG(D_INODE, "setting mtime %lu, ctime %lu, now = %lu\n",
+ LTIME_S(attr->ia_mtime), LTIME_S(attr->ia_ctime),
+ get_seconds());
+
+ /* If we are changing file size, file content is modified, flag it. */
+ if (attr->ia_valid & ATTR_SIZE) {
+ attr->ia_valid |= MDS_OPEN_OWNEROVERRIDE;
+ spin_lock(&lli->lli_lock);
+ lli->lli_flags |= LLIF_DATA_MODIFIED;
+ spin_unlock(&lli->lli_lock);
+ }
+
+ /* We always do an MDS RPC, even if we're only changing the size;
+ * only the MDS knows whether truncate() should fail with -ETXTBUSY */
+
+ op_data = kzalloc(sizeof(*op_data), GFP_NOFS);
+ if (!op_data)
+ return -ENOMEM;
+
+ if (!S_ISDIR(inode->i_mode)) {
+ if (attr->ia_valid & ATTR_SIZE)
+ inode_dio_write_done(inode);
+ mutex_unlock(&inode->i_mutex);
+ }
+
+ memcpy(&op_data->op_attr, attr, sizeof(*attr));
+
+ /* Open epoch for truncate. */
+ if (exp_connect_som(ll_i2mdexp(inode)) &&
+ (attr->ia_valid & (ATTR_SIZE | ATTR_MTIME | ATTR_MTIME_SET)))
+ op_data->op_flags = MF_EPOCH_OPEN;
+
+ /* truncate on a released file must failed with -ENODATA,
+ * so size must not be set on MDS for released file
+ * but other attributes must be set
+ */
+ if (S_ISREG(inode->i_mode)) {
+ struct lov_stripe_md *lsm;
+ __u32 gen;
+
+ ll_layout_refresh(inode, &gen);
+ lsm = ccc_inode_lsm_get(inode);
+ if (lsm && lsm->lsm_pattern & LOV_PATTERN_F_RELEASED)
+ file_is_released = true;
+ ccc_inode_lsm_put(inode, lsm);
+ }
+
+ /* if not in HSM import mode, clear size attr for released file
+ * we clear the attribute send to MDT in op_data, not the original
+ * received from caller in attr which is used later to
+ * decide return code */
+ if (file_is_released && (attr->ia_valid & ATTR_SIZE) && !hsm_import)
+ op_data->op_attr.ia_valid &= ~ATTR_SIZE;
+
+ rc = ll_md_setattr(dentry, op_data, &mod);
+ if (rc)
+ goto out;
+
+ /* truncate failed (only when non HSM import), others succeed */
+ if (file_is_released) {
+ if ((attr->ia_valid & ATTR_SIZE) && !hsm_import)
+ rc = -ENODATA;
+ else
+ rc = 0;
+ goto out;
+ }
+
+ /* RPC to MDT is sent, cancel data modification flag */
+ if (rc == 0 && (op_data->op_bias & MDS_DATA_MODIFIED)) {
+ spin_lock(&lli->lli_lock);
+ lli->lli_flags &= ~LLIF_DATA_MODIFIED;
+ spin_unlock(&lli->lli_lock);
+ }
+
+ ll_ioepoch_open(lli, op_data->op_ioepoch);
+ if (!S_ISREG(inode->i_mode)) {
+ rc = 0;
+ goto out;
+ }
+
+ if (attr->ia_valid & (ATTR_SIZE |
+ ATTR_ATIME | ATTR_ATIME_SET |
+ ATTR_MTIME | ATTR_MTIME_SET)) {
+ /* For truncate and utimes sending attributes to OSTs, setting
+ * mtime/atime to the past will be performed under PW [0:EOF]
+ * extent lock (new_size:EOF for truncate). It may seem
+ * excessive to send mtime/atime updates to OSTs when not
+ * setting times to past, but it is necessary due to possible
+ * time de-synchronization between MDT inode and OST objects */
+ if (attr->ia_valid & ATTR_SIZE)
+ down_write(&lli->lli_trunc_sem);
+ rc = ll_setattr_ost(inode, attr);
+ if (attr->ia_valid & ATTR_SIZE)
+ up_write(&lli->lli_trunc_sem);
+ }
+out:
+ if (op_data) {
+ if (op_data->op_ioepoch) {
+ rc1 = ll_setattr_done_writing(inode, op_data, mod);
+ if (!rc)
+ rc = rc1;
+ }
+ ll_finish_md_op_data(op_data);
+ }
+ if (!S_ISDIR(inode->i_mode)) {
+ mutex_lock(&inode->i_mutex);
+ if ((attr->ia_valid & ATTR_SIZE) && !hsm_import)
+ inode_dio_wait(inode);
+ }
+
+ ll_stats_ops_tally(ll_i2sbi(inode), (attr->ia_valid & ATTR_SIZE) ?
+ LPROC_LL_TRUNC : LPROC_LL_SETATTR, 1);
+
+ return rc;
+}
+
+int ll_setattr(struct dentry *de, struct iattr *attr)
+{
+ int mode = d_inode(de)->i_mode;
+
+ if ((attr->ia_valid & (ATTR_CTIME|ATTR_SIZE|ATTR_MODE)) ==
+ (ATTR_CTIME|ATTR_SIZE|ATTR_MODE))
+ attr->ia_valid |= MDS_OPEN_OWNEROVERRIDE;
+
+ if (((attr->ia_valid & (ATTR_MODE|ATTR_FORCE|ATTR_SIZE)) ==
+ (ATTR_SIZE|ATTR_MODE)) &&
+ (((mode & S_ISUID) && !(attr->ia_mode & S_ISUID)) ||
+ (((mode & (S_ISGID|S_IXGRP)) == (S_ISGID|S_IXGRP)) &&
+ !(attr->ia_mode & S_ISGID))))
+ attr->ia_valid |= ATTR_FORCE;
+
+ if ((attr->ia_valid & ATTR_MODE) &&
+ (mode & S_ISUID) &&
+ !(attr->ia_mode & S_ISUID) &&
+ !(attr->ia_valid & ATTR_KILL_SUID))
+ attr->ia_valid |= ATTR_KILL_SUID;
+
+ if ((attr->ia_valid & ATTR_MODE) &&
+ ((mode & (S_ISGID|S_IXGRP)) == (S_ISGID|S_IXGRP)) &&
+ !(attr->ia_mode & S_ISGID) &&
+ !(attr->ia_valid & ATTR_KILL_SGID))
+ attr->ia_valid |= ATTR_KILL_SGID;
+
+ return ll_setattr_raw(de, attr, false);
+}
+
+int ll_statfs_internal(struct super_block *sb, struct obd_statfs *osfs,
+ __u64 max_age, __u32 flags)
+{
+ struct ll_sb_info *sbi = ll_s2sbi(sb);
+ struct obd_statfs obd_osfs;
+ int rc;
+
+ rc = obd_statfs(NULL, sbi->ll_md_exp, osfs, max_age, flags);
+ if (rc) {
+ CERROR("md_statfs fails: rc = %d\n", rc);
+ return rc;
+ }
+
+ osfs->os_type = sb->s_magic;
+
+ CDEBUG(D_SUPER, "MDC blocks %llu/%llu objects %llu/%llu\n",
+ osfs->os_bavail, osfs->os_blocks, osfs->os_ffree,
+ osfs->os_files);
+
+ if (sbi->ll_flags & LL_SBI_LAZYSTATFS)
+ flags |= OBD_STATFS_NODELAY;
+
+ rc = obd_statfs_rqset(sbi->ll_dt_exp, &obd_osfs, max_age, flags);
+ if (rc) {
+ CERROR("obd_statfs fails: rc = %d\n", rc);
+ return rc;
+ }
+
+ CDEBUG(D_SUPER, "OSC blocks %llu/%llu objects %llu/%llu\n",
+ obd_osfs.os_bavail, obd_osfs.os_blocks, obd_osfs.os_ffree,
+ obd_osfs.os_files);
+
+ osfs->os_bsize = obd_osfs.os_bsize;
+ osfs->os_blocks = obd_osfs.os_blocks;
+ osfs->os_bfree = obd_osfs.os_bfree;
+ osfs->os_bavail = obd_osfs.os_bavail;
+
+ /* If we don't have as many objects free on the OST as inodes
+ * on the MDS, we reduce the total number of inodes to
+ * compensate, so that the "inodes in use" number is correct.
+ */
+ if (obd_osfs.os_ffree < osfs->os_ffree) {
+ osfs->os_files = (osfs->os_files - osfs->os_ffree) +
+ obd_osfs.os_ffree;
+ osfs->os_ffree = obd_osfs.os_ffree;
+ }
+
+ return rc;
+}
+int ll_statfs(struct dentry *de, struct kstatfs *sfs)
+{
+ struct super_block *sb = de->d_sb;
+ struct obd_statfs osfs;
+ int rc;
+
+ CDEBUG(D_VFSTRACE, "VFS Op: at %llu jiffies\n", get_jiffies_64());
+ ll_stats_ops_tally(ll_s2sbi(sb), LPROC_LL_STAFS, 1);
+
+ /* Some amount of caching on the client is allowed */
+ rc = ll_statfs_internal(sb, &osfs,
+ cfs_time_shift_64(-OBD_STATFS_CACHE_SECONDS),
+ 0);
+ if (rc)
+ return rc;
+
+ statfs_unpack(sfs, &osfs);
+
+ /* We need to downshift for all 32-bit kernels, because we can't
+ * tell if the kernel is being called via sys_statfs64() or not.
+ * Stop before overflowing f_bsize - in which case it is better
+ * to just risk EOVERFLOW if caller is using old sys_statfs(). */
+ if (sizeof(long) < 8) {
+ while (osfs.os_blocks > ~0UL && sfs->f_bsize < 0x40000000) {
+ sfs->f_bsize <<= 1;
+
+ osfs.os_blocks >>= 1;
+ osfs.os_bfree >>= 1;
+ osfs.os_bavail >>= 1;
+ }
+ }
+
+ sfs->f_blocks = osfs.os_blocks;
+ sfs->f_bfree = osfs.os_bfree;
+ sfs->f_bavail = osfs.os_bavail;
+ sfs->f_fsid = ll_s2sbi(sb)->ll_fsid;
+ return 0;
+}
+
+void ll_inode_size_lock(struct inode *inode)
+{
+ struct ll_inode_info *lli;
+
+ LASSERT(!S_ISDIR(inode->i_mode));
+
+ lli = ll_i2info(inode);
+ mutex_lock(&lli->lli_size_mutex);
+}
+
+void ll_inode_size_unlock(struct inode *inode)
+{
+ struct ll_inode_info *lli;
+
+ lli = ll_i2info(inode);
+ mutex_unlock(&lli->lli_size_mutex);
+}
+
+void ll_update_inode(struct inode *inode, struct lustre_md *md)
+{
+ struct ll_inode_info *lli = ll_i2info(inode);
+ struct mdt_body *body = md->body;
+ struct lov_stripe_md *lsm = md->lsm;
+ struct ll_sb_info *sbi = ll_i2sbi(inode);
+
+ LASSERT((lsm != NULL) == ((body->valid & OBD_MD_FLEASIZE) != 0));
+ if (lsm != NULL) {
+ if (!lli->lli_has_smd &&
+ !(sbi->ll_flags & LL_SBI_LAYOUT_LOCK))
+ cl_file_inode_init(inode, md);
+
+ lli->lli_maxbytes = lsm->lsm_maxbytes;
+ if (lli->lli_maxbytes > MAX_LFS_FILESIZE)
+ lli->lli_maxbytes = MAX_LFS_FILESIZE;
+ }
+
+ if (sbi->ll_flags & LL_SBI_RMT_CLIENT) {
+ if (body->valid & OBD_MD_FLRMTPERM)
+ ll_update_remote_perm(inode, md->remote_perm);
+ }
+#ifdef CONFIG_FS_POSIX_ACL
+ else if (body->valid & OBD_MD_FLACL) {
+ spin_lock(&lli->lli_lock);
+ if (lli->lli_posix_acl)
+ posix_acl_release(lli->lli_posix_acl);
+ lli->lli_posix_acl = md->posix_acl;
+ spin_unlock(&lli->lli_lock);
+ }
+#endif
+ inode->i_ino = cl_fid_build_ino(&body->fid1,
+ sbi->ll_flags & LL_SBI_32BIT_API);
+ inode->i_generation = cl_fid_build_gen(&body->fid1);
+
+ if (body->valid & OBD_MD_FLATIME) {
+ if (body->atime > LTIME_S(inode->i_atime))
+ LTIME_S(inode->i_atime) = body->atime;
+ lli->lli_lvb.lvb_atime = body->atime;
+ }
+ if (body->valid & OBD_MD_FLMTIME) {
+ if (body->mtime > LTIME_S(inode->i_mtime)) {
+ CDEBUG(D_INODE, "setting ino %lu mtime from %lu to %llu\n",
+ inode->i_ino, LTIME_S(inode->i_mtime),
+ body->mtime);
+ LTIME_S(inode->i_mtime) = body->mtime;
+ }
+ lli->lli_lvb.lvb_mtime = body->mtime;
+ }
+ if (body->valid & OBD_MD_FLCTIME) {
+ if (body->ctime > LTIME_S(inode->i_ctime))
+ LTIME_S(inode->i_ctime) = body->ctime;
+ lli->lli_lvb.lvb_ctime = body->ctime;
+ }
+ if (body->valid & OBD_MD_FLMODE)
+ inode->i_mode = (inode->i_mode & S_IFMT)|(body->mode & ~S_IFMT);
+ if (body->valid & OBD_MD_FLTYPE)
+ inode->i_mode = (inode->i_mode & ~S_IFMT)|(body->mode & S_IFMT);
+ LASSERT(inode->i_mode != 0);
+ if (S_ISREG(inode->i_mode))
+ inode->i_blkbits = min(PTLRPC_MAX_BRW_BITS + 1,
+ LL_MAX_BLKSIZE_BITS);
+ else
+ inode->i_blkbits = inode->i_sb->s_blocksize_bits;
+ if (body->valid & OBD_MD_FLUID)
+ inode->i_uid = make_kuid(&init_user_ns, body->uid);
+ if (body->valid & OBD_MD_FLGID)
+ inode->i_gid = make_kgid(&init_user_ns, body->gid);
+ if (body->valid & OBD_MD_FLFLAGS)
+ inode->i_flags = ll_ext_to_inode_flags(body->flags);
+ if (body->valid & OBD_MD_FLNLINK)
+ set_nlink(inode, body->nlink);
+ if (body->valid & OBD_MD_FLRDEV)
+ inode->i_rdev = old_decode_dev(body->rdev);
+
+ if (body->valid & OBD_MD_FLID) {
+ /* FID shouldn't be changed! */
+ if (fid_is_sane(&lli->lli_fid)) {
+ LASSERTF(lu_fid_eq(&lli->lli_fid, &body->fid1),
+ "Trying to change FID "DFID
+ " to the "DFID", inode %lu/%u(%p)\n",
+ PFID(&lli->lli_fid), PFID(&body->fid1),
+ inode->i_ino, inode->i_generation, inode);
+ } else
+ lli->lli_fid = body->fid1;
+ }
+
+ LASSERT(fid_seq(&lli->lli_fid) != 0);
+
+ if (body->valid & OBD_MD_FLSIZE) {
+ if (exp_connect_som(ll_i2mdexp(inode)) &&
+ S_ISREG(inode->i_mode)) {
+ struct lustre_handle lockh;
+ ldlm_mode_t mode;
+
+ /* As it is possible a blocking ast has been processed
+ * by this time, we need to check there is an UPDATE
+ * lock on the client and set LLIF_MDS_SIZE_LOCK holding
+ * it. */
+ mode = ll_take_md_lock(inode, MDS_INODELOCK_UPDATE,
+ &lockh, LDLM_FL_CBPENDING,
+ LCK_CR | LCK_CW |
+ LCK_PR | LCK_PW);
+ if (mode) {
+ if (lli->lli_flags & (LLIF_DONE_WRITING |
+ LLIF_EPOCH_PENDING |
+ LLIF_SOM_DIRTY)) {
+ CERROR("ino %lu flags %u still has size authority! do not trust the size got from MDS\n",
+ inode->i_ino, lli->lli_flags);
+ } else {
+ /* Use old size assignment to avoid
+ * deadlock bz14138 & bz14326 */
+ i_size_write(inode, body->size);
+ spin_lock(&lli->lli_lock);
+ lli->lli_flags |= LLIF_MDS_SIZE_LOCK;
+ spin_unlock(&lli->lli_lock);
+ }
+ ldlm_lock_decref(&lockh, mode);
+ }
+ } else {
+ /* Use old size assignment to avoid
+ * deadlock bz14138 & bz14326 */
+ i_size_write(inode, body->size);
+
+ CDEBUG(D_VFSTRACE, "inode=%lu, updating i_size %llu\n",
+ inode->i_ino, (unsigned long long)body->size);
+ }
+
+ if (body->valid & OBD_MD_FLBLOCKS)
+ inode->i_blocks = body->blocks;
+ }
+
+ if (body->valid & OBD_MD_FLMDSCAPA) {
+ LASSERT(md->mds_capa);
+ ll_add_capa(inode, md->mds_capa);
+ }
+ if (body->valid & OBD_MD_FLOSSCAPA) {
+ LASSERT(md->oss_capa);
+ ll_add_capa(inode, md->oss_capa);
+ }
+
+ if (body->valid & OBD_MD_TSTATE) {
+ if (body->t_state & MS_RESTORE)
+ lli->lli_flags |= LLIF_FILE_RESTORING;
+ }
+}
+
+void ll_read_inode2(struct inode *inode, void *opaque)
+{
+ struct lustre_md *md = opaque;
+ struct ll_inode_info *lli = ll_i2info(inode);
+
+ CDEBUG(D_VFSTRACE, "VFS Op:inode="DFID"(%p)\n",
+ PFID(&lli->lli_fid), inode);
+
+ LASSERT(!lli->lli_has_smd);
+
+ /* Core attributes from the MDS first. This is a new inode, and
+ * the VFS doesn't zero times in the core inode so we have to do
+ * it ourselves. They will be overwritten by either MDS or OST
+ * attributes - we just need to make sure they aren't newer. */
+ LTIME_S(inode->i_mtime) = 0;
+ LTIME_S(inode->i_atime) = 0;
+ LTIME_S(inode->i_ctime) = 0;
+ inode->i_rdev = 0;
+ ll_update_inode(inode, md);
+
+ /* OIDEBUG(inode); */
+
+ if (S_ISREG(inode->i_mode)) {
+ struct ll_sb_info *sbi = ll_i2sbi(inode);
+
+ inode->i_op = &ll_file_inode_operations;
+ inode->i_fop = sbi->ll_fop;
+ inode->i_mapping->a_ops = (struct address_space_operations *)&ll_aops;
+ } else if (S_ISDIR(inode->i_mode)) {
+ inode->i_op = &ll_dir_inode_operations;
+ inode->i_fop = &ll_dir_operations;
+ } else if (S_ISLNK(inode->i_mode)) {
+ inode->i_op = &ll_fast_symlink_inode_operations;
+ } else {
+ inode->i_op = &ll_special_inode_operations;
+
+ init_special_inode(inode, inode->i_mode,
+ inode->i_rdev);
+ }
+}
+
+void ll_delete_inode(struct inode *inode)
+{
+ struct cl_inode_info *lli = cl_i2info(inode);
+
+ if (S_ISREG(inode->i_mode) && lli->lli_clob != NULL)
+ /* discard all dirty pages before truncating them, required by
+ * osc_extent implementation at LU-1030. */
+ cl_sync_file_range(inode, 0, OBD_OBJECT_EOF,
+ CL_FSYNC_DISCARD, 1);
+
+ truncate_inode_pages_final(&inode->i_data);
+
+ /* Workaround for LU-118 */
+ if (inode->i_data.nrpages) {
+ spin_lock_irq(&inode->i_data.tree_lock);
+ spin_unlock_irq(&inode->i_data.tree_lock);
+ LASSERTF(inode->i_data.nrpages == 0,
+ "inode=%lu/%u(%p) nrpages=%lu, see http://jira.whamcloud.com/browse/LU-118\n",
+ inode->i_ino, inode->i_generation, inode,
+ inode->i_data.nrpages);
+ }
+ /* Workaround end */
+
+ ll_clear_inode(inode);
+ clear_inode(inode);
+}
+
+int ll_iocontrol(struct inode *inode, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ struct ll_sb_info *sbi = ll_i2sbi(inode);
+ struct ptlrpc_request *req = NULL;
+ int rc, flags = 0;
+
+ switch (cmd) {
+ case FSFILT_IOC_GETFLAGS: {
+ struct mdt_body *body;
+ struct md_op_data *op_data;
+
+ op_data = ll_prep_md_op_data(NULL, inode, NULL, NULL,
+ 0, 0, LUSTRE_OPC_ANY,
+ NULL);
+ if (IS_ERR(op_data))
+ return PTR_ERR(op_data);
+
+ op_data->op_valid = OBD_MD_FLFLAGS;
+ rc = md_getattr(sbi->ll_md_exp, op_data, &req);
+ ll_finish_md_op_data(op_data);
+ if (rc) {
+ CERROR("failure %d inode %lu\n", rc, inode->i_ino);
+ return -abs(rc);
+ }
+
+ body = req_capsule_server_get(&req->rq_pill, &RMF_MDT_BODY);
+
+ flags = body->flags;
+
+ ptlrpc_req_finished(req);
+
+ return put_user(flags, (int *)arg);
+ }
+ case FSFILT_IOC_SETFLAGS: {
+ struct lov_stripe_md *lsm;
+ struct obd_info oinfo = { { { 0 } } };
+ struct md_op_data *op_data;
+
+ if (get_user(flags, (int *)arg))
+ return -EFAULT;
+
+ op_data = ll_prep_md_op_data(NULL, inode, NULL, NULL, 0, 0,
+ LUSTRE_OPC_ANY, NULL);
+ if (IS_ERR(op_data))
+ return PTR_ERR(op_data);
+
+ ((struct ll_iattr *)&op_data->op_attr)->ia_attr_flags = flags;
+ op_data->op_attr.ia_valid |= ATTR_ATTR_FLAG;
+ rc = md_setattr(sbi->ll_md_exp, op_data,
+ NULL, 0, NULL, 0, &req, NULL);
+ ll_finish_md_op_data(op_data);
+ ptlrpc_req_finished(req);
+ if (rc)
+ return rc;
+
+ inode->i_flags = ll_ext_to_inode_flags(flags);
+
+ lsm = ccc_inode_lsm_get(inode);
+ if (!lsm_has_objects(lsm)) {
+ ccc_inode_lsm_put(inode, lsm);
+ return 0;
+ }
+
+ OBDO_ALLOC(oinfo.oi_oa);
+ if (!oinfo.oi_oa) {
+ ccc_inode_lsm_put(inode, lsm);
+ return -ENOMEM;
+ }
+ oinfo.oi_md = lsm;
+ oinfo.oi_oa->o_oi = lsm->lsm_oi;
+ oinfo.oi_oa->o_flags = flags;
+ oinfo.oi_oa->o_valid = OBD_MD_FLID | OBD_MD_FLFLAGS |
+ OBD_MD_FLGROUP;
+ oinfo.oi_capa = ll_mdscapa_get(inode);
+ obdo_set_parent_fid(oinfo.oi_oa, &ll_i2info(inode)->lli_fid);
+ rc = obd_setattr_rqset(sbi->ll_dt_exp, &oinfo, NULL);
+ capa_put(oinfo.oi_capa);
+ OBDO_FREE(oinfo.oi_oa);
+ ccc_inode_lsm_put(inode, lsm);
+
+ if (rc && rc != -EPERM && rc != -EACCES)
+ CERROR("osc_setattr_async fails: rc = %d\n", rc);
+
+ return rc;
+ }
+ default:
+ return -ENOSYS;
+ }
+
+ return 0;
+}
+
+int ll_flush_ctx(struct inode *inode)
+{
+ struct ll_sb_info *sbi = ll_i2sbi(inode);
+
+ CDEBUG(D_SEC, "flush context for user %d\n",
+ from_kuid(&init_user_ns, current_uid()));
+
+ obd_set_info_async(NULL, sbi->ll_md_exp,
+ sizeof(KEY_FLUSH_CTX), KEY_FLUSH_CTX,
+ 0, NULL, NULL);
+ obd_set_info_async(NULL, sbi->ll_dt_exp,
+ sizeof(KEY_FLUSH_CTX), KEY_FLUSH_CTX,
+ 0, NULL, NULL);
+ return 0;
+}
+
+/* umount -f client means force down, don't save state */
+void ll_umount_begin(struct super_block *sb)
+{
+ struct ll_sb_info *sbi = ll_s2sbi(sb);
+ struct obd_device *obd;
+ struct obd_ioctl_data *ioc_data;
+
+ CDEBUG(D_VFSTRACE, "VFS Op: superblock %p count %d active %d\n", sb,
+ sb->s_count, atomic_read(&sb->s_active));
+
+ obd = class_exp2obd(sbi->ll_md_exp);
+ if (obd == NULL) {
+ CERROR("Invalid MDC connection handle %#llx\n",
+ sbi->ll_md_exp->exp_handle.h_cookie);
+ return;
+ }
+ obd->obd_force = 1;
+
+ obd = class_exp2obd(sbi->ll_dt_exp);
+ if (obd == NULL) {
+ CERROR("Invalid LOV connection handle %#llx\n",
+ sbi->ll_dt_exp->exp_handle.h_cookie);
+ return;
+ }
+ obd->obd_force = 1;
+
+ ioc_data = kzalloc(sizeof(*ioc_data), GFP_NOFS);
+ if (ioc_data) {
+ obd_iocontrol(IOC_OSC_SET_ACTIVE, sbi->ll_md_exp,
+ sizeof(*ioc_data), ioc_data, NULL);
+
+ obd_iocontrol(IOC_OSC_SET_ACTIVE, sbi->ll_dt_exp,
+ sizeof(*ioc_data), ioc_data, NULL);
+
+ OBD_FREE_PTR(ioc_data);
+ }
+
+ /* Really, we'd like to wait until there are no requests outstanding,
+ * and then continue. For now, we just invalidate the requests,
+ * schedule() and sleep one second if needed, and hope.
+ */
+ schedule();
+}
+
+int ll_remount_fs(struct super_block *sb, int *flags, char *data)
+{
+ struct ll_sb_info *sbi = ll_s2sbi(sb);
+ char *profilenm = get_profile_name(sb);
+ int err;
+ __u32 read_only;
+
+ if ((*flags & MS_RDONLY) != (sb->s_flags & MS_RDONLY)) {
+ read_only = *flags & MS_RDONLY;
+ err = obd_set_info_async(NULL, sbi->ll_md_exp,
+ sizeof(KEY_READ_ONLY),
+ KEY_READ_ONLY, sizeof(read_only),
+ &read_only, NULL);
+ if (err) {
+ LCONSOLE_WARN("Failed to remount %s %s (%d)\n",
+ profilenm, read_only ?
+ "read-only" : "read-write", err);
+ return err;
+ }
+
+ if (read_only)
+ sb->s_flags |= MS_RDONLY;
+ else
+ sb->s_flags &= ~MS_RDONLY;
+
+ if (sbi->ll_flags & LL_SBI_VERBOSE)
+ LCONSOLE_WARN("Remounted %s %s\n", profilenm,
+ read_only ? "read-only" : "read-write");
+ }
+ return 0;
+}
+
+int ll_prep_inode(struct inode **inode, struct ptlrpc_request *req,
+ struct super_block *sb, struct lookup_intent *it)
+{
+ struct ll_sb_info *sbi = NULL;
+ struct lustre_md md;
+ int rc;
+
+ LASSERT(*inode || sb);
+ sbi = sb ? ll_s2sbi(sb) : ll_i2sbi(*inode);
+ rc = md_get_lustre_md(sbi->ll_md_exp, req, sbi->ll_dt_exp,
+ sbi->ll_md_exp, &md);
+ if (rc)
+ return rc;
+
+ if (*inode) {
+ ll_update_inode(*inode, &md);
+ } else {
+ LASSERT(sb != NULL);
+
+ /*
+ * At this point server returns to client's same fid as client
+ * generated for creating. So using ->fid1 is okay here.
+ */
+ LASSERT(fid_is_sane(&md.body->fid1));
+
+ *inode = ll_iget(sb, cl_fid_build_ino(&md.body->fid1,
+ sbi->ll_flags & LL_SBI_32BIT_API),
+ &md);
+ if (*inode == NULL || IS_ERR(*inode)) {
+#ifdef CONFIG_FS_POSIX_ACL
+ if (md.posix_acl) {
+ posix_acl_release(md.posix_acl);
+ md.posix_acl = NULL;
+ }
+#endif
+ rc = IS_ERR(*inode) ? PTR_ERR(*inode) : -ENOMEM;
+ *inode = NULL;
+ CERROR("new_inode -fatal: rc %d\n", rc);
+ goto out;
+ }
+ }
+
+ /* Handling piggyback layout lock.
+ * Layout lock can be piggybacked by getattr and open request.
+ * The lsm can be applied to inode only if it comes with a layout lock
+ * otherwise correct layout may be overwritten, for example:
+ * 1. proc1: mdt returns a lsm but not granting layout
+ * 2. layout was changed by another client
+ * 3. proc2: refresh layout and layout lock granted
+ * 4. proc1: to apply a stale layout */
+ if (it != NULL && it->d.lustre.it_lock_mode != 0) {
+ struct lustre_handle lockh;
+ struct ldlm_lock *lock;
+
+ lockh.cookie = it->d.lustre.it_lock_handle;
+ lock = ldlm_handle2lock(&lockh);
+ LASSERT(lock != NULL);
+ if (ldlm_has_layout(lock)) {
+ struct cl_object_conf conf;
+
+ memset(&conf, 0, sizeof(conf));
+ conf.coc_opc = OBJECT_CONF_SET;
+ conf.coc_inode = *inode;
+ conf.coc_lock = lock;
+ conf.u.coc_md = &md;
+ (void)ll_layout_conf(*inode, &conf);
+ }
+ LDLM_LOCK_PUT(lock);
+ }
+
+out:
+ if (md.lsm != NULL)
+ obd_free_memmd(sbi->ll_dt_exp, &md.lsm);
+ md_free_lustre_md(sbi->ll_md_exp, &md);
+ return rc;
+}
+
+int ll_obd_statfs(struct inode *inode, void *arg)
+{
+ struct ll_sb_info *sbi = NULL;
+ struct obd_export *exp;
+ char *buf = NULL;
+ struct obd_ioctl_data *data = NULL;
+ __u32 type;
+ __u32 flags;
+ int len = 0, rc;
+
+ if (!inode) {
+ rc = -EINVAL;
+ goto out_statfs;
+ }
+
+ sbi = ll_i2sbi(inode);
+ if (!sbi) {
+ rc = -EINVAL;
+ goto out_statfs;
+ }
+
+ rc = obd_ioctl_getdata(&buf, &len, arg);
+ if (rc)
+ goto out_statfs;
+
+ data = (void *)buf;
+ if (!data->ioc_inlbuf1 || !data->ioc_inlbuf2 ||
+ !data->ioc_pbuf1 || !data->ioc_pbuf2) {
+ rc = -EINVAL;
+ goto out_statfs;
+ }
+
+ if (data->ioc_inllen1 != sizeof(__u32) ||
+ data->ioc_inllen2 != sizeof(__u32) ||
+ data->ioc_plen1 != sizeof(struct obd_statfs) ||
+ data->ioc_plen2 != sizeof(struct obd_uuid)) {
+ rc = -EINVAL;
+ goto out_statfs;
+ }
+
+ memcpy(&type, data->ioc_inlbuf1, sizeof(__u32));
+ if (type & LL_STATFS_LMV)
+ exp = sbi->ll_md_exp;
+ else if (type & LL_STATFS_LOV)
+ exp = sbi->ll_dt_exp;
+ else {
+ rc = -ENODEV;
+ goto out_statfs;
+ }
+
+ flags = (type & LL_STATFS_NODELAY) ? OBD_STATFS_NODELAY : 0;
+ rc = obd_iocontrol(IOC_OBD_STATFS, exp, len, buf, &flags);
+ if (rc)
+ goto out_statfs;
+out_statfs:
+ if (buf)
+ obd_ioctl_freedata(buf, len);
+ return rc;
+}
+
+int ll_process_config(struct lustre_cfg *lcfg)
+{
+ char *ptr;
+ void *sb;
+ struct lprocfs_static_vars lvars;
+ unsigned long x;
+ int rc = 0;
+
+ lprocfs_llite_init_vars(&lvars);
+
+ /* The instance name contains the sb: lustre-client-aacfe000 */
+ ptr = strrchr(lustre_cfg_string(lcfg, 0), '-');
+ if (!ptr || !*(++ptr))
+ return -EINVAL;
+ rc = kstrtoul(ptr, 16, &x);
+ if (rc != 0)
+ return -EINVAL;
+ sb = (void *)x;
+ /* This better be a real Lustre superblock! */
+ LASSERT(s2lsi((struct super_block *)sb)->lsi_lmd->lmd_magic == LMD_MAGIC);
+
+ /* Note we have not called client_common_fill_super yet, so
+ proc fns must be able to handle that! */
+ rc = class_process_proc_param(PARAM_LLITE, lvars.obd_vars,
+ lcfg, sb);
+ if (rc > 0)
+ rc = 0;
+ return rc;
+}
+
+/* this function prepares md_op_data hint for passing ot down to MD stack. */
+struct md_op_data *ll_prep_md_op_data(struct md_op_data *op_data,
+ struct inode *i1, struct inode *i2,
+ const char *name, int namelen,
+ int mode, __u32 opc, void *data)
+{
+ LASSERT(i1 != NULL);
+
+ if (namelen > ll_i2sbi(i1)->ll_namelen)
+ return ERR_PTR(-ENAMETOOLONG);
+
+ if (op_data == NULL)
+ op_data = kzalloc(sizeof(*op_data), GFP_NOFS);
+
+ if (op_data == NULL)
+ return ERR_PTR(-ENOMEM);
+
+ ll_i2gids(op_data->op_suppgids, i1, i2);
+ op_data->op_fid1 = *ll_inode2fid(i1);
+ op_data->op_capa1 = ll_mdscapa_get(i1);
+
+ if (i2) {
+ op_data->op_fid2 = *ll_inode2fid(i2);
+ op_data->op_capa2 = ll_mdscapa_get(i2);
+ } else {
+ fid_zero(&op_data->op_fid2);
+ op_data->op_capa2 = NULL;
+ }
+
+ op_data->op_name = name;
+ op_data->op_namelen = namelen;
+ op_data->op_mode = mode;
+ op_data->op_mod_time = get_seconds();
+ op_data->op_fsuid = from_kuid(&init_user_ns, current_fsuid());
+ op_data->op_fsgid = from_kgid(&init_user_ns, current_fsgid());
+ op_data->op_cap = cfs_curproc_cap_pack();
+ op_data->op_bias = 0;
+ op_data->op_cli_flags = 0;
+ if ((opc == LUSTRE_OPC_CREATE) && (name != NULL) &&
+ filename_is_volatile(name, namelen, NULL))
+ op_data->op_bias |= MDS_CREATE_VOLATILE;
+ op_data->op_opc = opc;
+ op_data->op_mds = 0;
+ op_data->op_data = data;
+
+ /* If the file is being opened after mknod() (normally due to NFS)
+ * try to use the default stripe data from parent directory for
+ * allocating OST objects. Try to pass the parent FID to MDS. */
+ if (opc == LUSTRE_OPC_CREATE && i1 == i2 && S_ISREG(i2->i_mode) &&
+ !ll_i2info(i2)->lli_has_smd) {
+ struct ll_inode_info *lli = ll_i2info(i2);
+
+ spin_lock(&lli->lli_lock);
+ if (likely(!lli->lli_has_smd && !fid_is_zero(&lli->lli_pfid)))
+ op_data->op_fid1 = lli->lli_pfid;
+ spin_unlock(&lli->lli_lock);
+ /** We ignore parent's capability temporary. */
+ }
+
+ /* When called by ll_setattr_raw, file is i1. */
+ if (LLIF_DATA_MODIFIED & ll_i2info(i1)->lli_flags)
+ op_data->op_bias |= MDS_DATA_MODIFIED;
+
+ return op_data;
+}
+
+void ll_finish_md_op_data(struct md_op_data *op_data)
+{
+ capa_put(op_data->op_capa1);
+ capa_put(op_data->op_capa2);
+ OBD_FREE_PTR(op_data);
+}
+
+int ll_show_options(struct seq_file *seq, struct dentry *dentry)
+{
+ struct ll_sb_info *sbi;
+
+ LASSERT((seq != NULL) && (dentry != NULL));
+ sbi = ll_s2sbi(dentry->d_sb);
+
+ if (sbi->ll_flags & LL_SBI_NOLCK)
+ seq_puts(seq, ",nolock");
+
+ if (sbi->ll_flags & LL_SBI_FLOCK)
+ seq_puts(seq, ",flock");
+
+ if (sbi->ll_flags & LL_SBI_LOCALFLOCK)
+ seq_puts(seq, ",localflock");
+
+ if (sbi->ll_flags & LL_SBI_USER_XATTR)
+ seq_puts(seq, ",user_xattr");
+
+ if (sbi->ll_flags & LL_SBI_LAZYSTATFS)
+ seq_puts(seq, ",lazystatfs");
+
+ if (sbi->ll_flags & LL_SBI_USER_FID2PATH)
+ seq_puts(seq, ",user_fid2path");
+
+ return 0;
+}
+
+/**
+ * Get obd name by cmd, and copy out to user space
+ */
+int ll_get_obd_name(struct inode *inode, unsigned int cmd, unsigned long arg)
+{
+ struct ll_sb_info *sbi = ll_i2sbi(inode);
+ struct obd_device *obd;
+
+ if (cmd == OBD_IOC_GETDTNAME)
+ obd = class_exp2obd(sbi->ll_dt_exp);
+ else if (cmd == OBD_IOC_GETMDNAME)
+ obd = class_exp2obd(sbi->ll_md_exp);
+ else
+ return -EINVAL;
+
+ if (!obd)
+ return -ENOENT;
+
+ if (copy_to_user((void *)arg, obd->obd_name,
+ strlen(obd->obd_name) + 1))
+ return -EFAULT;
+
+ return 0;
+}
+
+/**
+ * Get lustre file system name by \a sbi. If \a buf is provided(non-NULL), the
+ * fsname will be returned in this buffer; otherwise, a static buffer will be
+ * used to store the fsname and returned to caller.
+ */
+char *ll_get_fsname(struct super_block *sb, char *buf, int buflen)
+{
+ static char fsname_static[MTI_NAME_MAXLEN];
+ struct lustre_sb_info *lsi = s2lsi(sb);
+ char *ptr;
+ int len;
+
+ if (buf == NULL) {
+ /* this means the caller wants to use static buffer
+ * and it doesn't care about race. Usually this is
+ * in error reporting path */
+ buf = fsname_static;
+ buflen = sizeof(fsname_static);
+ }
+
+ len = strlen(lsi->lsi_lmd->lmd_profile);
+ ptr = strrchr(lsi->lsi_lmd->lmd_profile, '-');
+ if (ptr && (strcmp(ptr, "-client") == 0))
+ len -= 7;
+
+ if (unlikely(len >= buflen))
+ len = buflen - 1;
+ strncpy(buf, lsi->lsi_lmd->lmd_profile, len);
+ buf[len] = '\0';
+
+ return buf;
+}
+
+void ll_dirty_page_discard_warn(struct page *page, int ioret)
+{
+ char *buf, *path = NULL;
+ struct dentry *dentry = NULL;
+ struct ccc_object *obj = cl_inode2ccc(page->mapping->host);
+
+ /* this can be called inside spin lock so use GFP_ATOMIC. */
+ buf = (char *)__get_free_page(GFP_ATOMIC);
+ if (buf != NULL) {
+ dentry = d_find_alias(page->mapping->host);
+ if (dentry != NULL)
+ path = dentry_path_raw(dentry, buf, PAGE_SIZE);
+ }
+
+ CDEBUG(D_WARNING,
+ "%s: dirty page discard: %s/fid: " DFID "/%s may get corrupted (rc %d)\n",
+ ll_get_fsname(page->mapping->host->i_sb, NULL, 0),
+ s2lsi(page->mapping->host->i_sb)->lsi_lmd->lmd_dev,
+ PFID(&obj->cob_header.coh_lu.loh_fid),
+ (path && !IS_ERR(path)) ? path : "", ioret);
+
+ if (dentry != NULL)
+ dput(dentry);
+
+ if (buf != NULL)
+ free_page((unsigned long)buf);
+}
diff --git a/drivers/staging/lustre/lustre/llite/llite_mmap.c b/drivers/staging/lustre/lustre/llite/llite_mmap.c
new file mode 100644
index 000000000..a90214bb8
--- /dev/null
+++ b/drivers/staging/lustre/lustre/llite/llite_mmap.c
@@ -0,0 +1,492 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2011, 2012, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ */
+
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/string.h>
+#include <linux/stat.h>
+#include <linux/errno.h>
+#include <linux/unistd.h>
+#include <linux/uaccess.h>
+
+#include <linux/fs.h>
+#include <linux/pagemap.h>
+
+#define DEBUG_SUBSYSTEM S_LLITE
+
+#include "../include/lustre_lite.h"
+#include "llite_internal.h"
+#include "../include/linux/lustre_compat25.h"
+
+static const struct vm_operations_struct ll_file_vm_ops;
+
+void policy_from_vma(ldlm_policy_data_t *policy,
+ struct vm_area_struct *vma, unsigned long addr,
+ size_t count)
+{
+ policy->l_extent.start = ((addr - vma->vm_start) & CFS_PAGE_MASK) +
+ (vma->vm_pgoff << PAGE_CACHE_SHIFT);
+ policy->l_extent.end = (policy->l_extent.start + count - 1) |
+ ~CFS_PAGE_MASK;
+}
+
+struct vm_area_struct *our_vma(struct mm_struct *mm, unsigned long addr,
+ size_t count)
+{
+ struct vm_area_struct *vma, *ret = NULL;
+
+ /* mmap_sem must have been held by caller. */
+ LASSERT(!down_write_trylock(&mm->mmap_sem));
+
+ for (vma = find_vma(mm, addr);
+ vma != NULL && vma->vm_start < (addr + count); vma = vma->vm_next) {
+ if (vma->vm_ops && vma->vm_ops == &ll_file_vm_ops &&
+ vma->vm_flags & VM_SHARED) {
+ ret = vma;
+ break;
+ }
+ }
+ return ret;
+}
+
+/**
+ * API independent part for page fault initialization.
+ * \param vma - virtual memory area addressed to page fault
+ * \param env - corespondent lu_env to processing
+ * \param nest - nested level
+ * \param index - page index corespondent to fault.
+ * \parm ra_flags - vma readahead flags.
+ *
+ * \return allocated and initialized env for fault operation.
+ * \retval EINVAL if env can't allocated
+ * \return other error codes from cl_io_init.
+ */
+static struct cl_io *
+ll_fault_io_init(struct vm_area_struct *vma, struct lu_env **env_ret,
+ struct cl_env_nest *nest, pgoff_t index,
+ unsigned long *ra_flags)
+{
+ struct file *file = vma->vm_file;
+ struct inode *inode = file_inode(file);
+ struct cl_io *io;
+ struct cl_fault_io *fio;
+ struct lu_env *env;
+ int rc;
+
+ *env_ret = NULL;
+ if (ll_file_nolock(file))
+ return ERR_PTR(-EOPNOTSUPP);
+
+ /*
+ * page fault can be called when lustre IO is
+ * already active for the current thread, e.g., when doing read/write
+ * against user level buffer mapped from Lustre buffer. To avoid
+ * stomping on existing context, optionally force an allocation of a new
+ * one.
+ */
+ env = cl_env_nested_get(nest);
+ if (IS_ERR(env))
+ return ERR_PTR(-EINVAL);
+
+ *env_ret = env;
+
+ io = ccc_env_thread_io(env);
+ io->ci_obj = ll_i2info(inode)->lli_clob;
+ LASSERT(io->ci_obj != NULL);
+
+ fio = &io->u.ci_fault;
+ fio->ft_index = index;
+ fio->ft_executable = vma->vm_flags&VM_EXEC;
+
+ /*
+ * disable VM_SEQ_READ and use VM_RAND_READ to make sure that
+ * the kernel will not read other pages not covered by ldlm in
+ * filemap_nopage. we do our readahead in ll_readpage.
+ */
+ if (ra_flags != NULL)
+ *ra_flags = vma->vm_flags & (VM_RAND_READ|VM_SEQ_READ);
+ vma->vm_flags &= ~VM_SEQ_READ;
+ vma->vm_flags |= VM_RAND_READ;
+
+ CDEBUG(D_MMAP, "vm_flags: %lx (%lu %d)\n", vma->vm_flags,
+ fio->ft_index, fio->ft_executable);
+
+ rc = cl_io_init(env, io, CIT_FAULT, io->ci_obj);
+ if (rc == 0) {
+ struct ccc_io *cio = ccc_env_io(env);
+ struct ll_file_data *fd = LUSTRE_FPRIVATE(file);
+
+ LASSERT(cio->cui_cl.cis_io == io);
+
+ /* mmap lock must be MANDATORY it has to cache
+ * pages. */
+ io->ci_lockreq = CILR_MANDATORY;
+ cio->cui_fd = fd;
+ } else {
+ LASSERT(rc < 0);
+ cl_io_fini(env, io);
+ cl_env_nested_put(nest, env);
+ io = ERR_PTR(rc);
+ }
+
+ return io;
+}
+
+/* Sharing code of page_mkwrite method for rhel5 and rhel6 */
+static int ll_page_mkwrite0(struct vm_area_struct *vma, struct page *vmpage,
+ bool *retry)
+{
+ struct lu_env *env;
+ struct cl_io *io;
+ struct vvp_io *vio;
+ struct cl_env_nest nest;
+ int result;
+ sigset_t set;
+ struct inode *inode;
+ struct ll_inode_info *lli;
+
+ LASSERT(vmpage != NULL);
+
+ io = ll_fault_io_init(vma, &env, &nest, vmpage->index, NULL);
+ if (IS_ERR(io)) {
+ result = PTR_ERR(io);
+ goto out;
+ }
+
+ result = io->ci_result;
+ if (result < 0)
+ goto out_io;
+
+ io->u.ci_fault.ft_mkwrite = 1;
+ io->u.ci_fault.ft_writable = 1;
+
+ vio = vvp_env_io(env);
+ vio->u.fault.ft_vma = vma;
+ vio->u.fault.ft_vmpage = vmpage;
+
+ set = cfs_block_sigsinv(sigmask(SIGKILL) | sigmask(SIGTERM));
+
+ /* we grab lli_trunc_sem to exclude truncate case.
+ * Otherwise, we could add dirty pages into osc cache
+ * while truncate is on-going. */
+ inode = ccc_object_inode(io->ci_obj);
+ lli = ll_i2info(inode);
+ down_read(&lli->lli_trunc_sem);
+
+ result = cl_io_loop(env, io);
+
+ up_read(&lli->lli_trunc_sem);
+
+ cfs_restore_sigs(set);
+
+ if (result == 0) {
+ struct inode *inode = file_inode(vma->vm_file);
+ struct ll_inode_info *lli = ll_i2info(inode);
+
+ lock_page(vmpage);
+ if (vmpage->mapping == NULL) {
+ unlock_page(vmpage);
+
+ /* page was truncated and lock was cancelled, return
+ * ENODATA so that VM_FAULT_NOPAGE will be returned
+ * to handle_mm_fault(). */
+ if (result == 0)
+ result = -ENODATA;
+ } else if (!PageDirty(vmpage)) {
+ /* race, the page has been cleaned by ptlrpcd after
+ * it was unlocked, it has to be added into dirty
+ * cache again otherwise this soon-to-dirty page won't
+ * consume any grants, even worse if this page is being
+ * transferred because it will break RPC checksum.
+ */
+ unlock_page(vmpage);
+
+ CDEBUG(D_MMAP, "Race on page_mkwrite %p/%lu, page has been written out, retry.\n",
+ vmpage, vmpage->index);
+
+ *retry = true;
+ result = -EAGAIN;
+ }
+
+ if (result == 0) {
+ spin_lock(&lli->lli_lock);
+ lli->lli_flags |= LLIF_DATA_MODIFIED;
+ spin_unlock(&lli->lli_lock);
+ }
+ }
+
+out_io:
+ cl_io_fini(env, io);
+ cl_env_nested_put(&nest, env);
+out:
+ CDEBUG(D_MMAP, "%s mkwrite with %d\n", current->comm, result);
+ LASSERT(ergo(result == 0, PageLocked(vmpage)));
+
+ return result;
+}
+
+
+
+static inline int to_fault_error(int result)
+{
+ switch (result) {
+ case 0:
+ result = VM_FAULT_LOCKED;
+ break;
+ case -EFAULT:
+ result = VM_FAULT_NOPAGE;
+ break;
+ case -ENOMEM:
+ result = VM_FAULT_OOM;
+ break;
+ default:
+ result = VM_FAULT_SIGBUS;
+ break;
+ }
+ return result;
+}
+
+/**
+ * Lustre implementation of a vm_operations_struct::fault() method, called by
+ * VM to server page fault (both in kernel and user space).
+ *
+ * \param vma - is virtual area struct related to page fault
+ * \param vmf - structure which describe type and address where hit fault
+ *
+ * \return allocated and filled _locked_ page for address
+ * \retval VM_FAULT_ERROR on general error
+ * \retval NOPAGE_OOM not have memory for allocate new page
+ */
+static int ll_fault0(struct vm_area_struct *vma, struct vm_fault *vmf)
+{
+ struct lu_env *env;
+ struct cl_io *io;
+ struct vvp_io *vio = NULL;
+ struct page *vmpage;
+ unsigned long ra_flags;
+ struct cl_env_nest nest;
+ int result;
+ int fault_ret = 0;
+
+ io = ll_fault_io_init(vma, &env, &nest, vmf->pgoff, &ra_flags);
+ if (IS_ERR(io))
+ return to_fault_error(PTR_ERR(io));
+
+ result = io->ci_result;
+ if (result == 0) {
+ vio = vvp_env_io(env);
+ vio->u.fault.ft_vma = vma;
+ vio->u.fault.ft_vmpage = NULL;
+ vio->u.fault.fault.ft_vmf = vmf;
+ vio->u.fault.fault.ft_flags = 0;
+ vio->u.fault.fault.ft_flags_valid = false;
+
+ result = cl_io_loop(env, io);
+
+ /* ft_flags are only valid if we reached
+ * the call to filemap_fault */
+ if (vio->u.fault.fault.ft_flags_valid)
+ fault_ret = vio->u.fault.fault.ft_flags;
+
+ vmpage = vio->u.fault.ft_vmpage;
+ if (result != 0 && vmpage != NULL) {
+ page_cache_release(vmpage);
+ vmf->page = NULL;
+ }
+ }
+ cl_io_fini(env, io);
+ cl_env_nested_put(&nest, env);
+
+ vma->vm_flags |= ra_flags;
+ if (result != 0 && !(fault_ret & VM_FAULT_RETRY))
+ fault_ret |= to_fault_error(result);
+
+ CDEBUG(D_MMAP, "%s fault %d/%d\n",
+ current->comm, fault_ret, result);
+ return fault_ret;
+}
+
+static int ll_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
+{
+ int count = 0;
+ bool printed = false;
+ int result;
+ sigset_t set;
+
+ /* Only SIGKILL and SIGTERM is allowed for fault/nopage/mkwrite
+ * so that it can be killed by admin but not cause segfault by
+ * other signals. */
+ set = cfs_block_sigsinv(sigmask(SIGKILL) | sigmask(SIGTERM));
+
+restart:
+ result = ll_fault0(vma, vmf);
+ LASSERT(!(result & VM_FAULT_LOCKED));
+ if (result == 0) {
+ struct page *vmpage = vmf->page;
+
+ /* check if this page has been truncated */
+ lock_page(vmpage);
+ if (unlikely(vmpage->mapping == NULL)) { /* unlucky */
+ unlock_page(vmpage);
+ page_cache_release(vmpage);
+ vmf->page = NULL;
+
+ if (!printed && ++count > 16) {
+ CWARN("the page is under heavy contention, maybe your app(%s) needs revising :-)\n",
+ current->comm);
+ printed = true;
+ }
+
+ goto restart;
+ }
+
+ result = VM_FAULT_LOCKED;
+ }
+ cfs_restore_sigs(set);
+ return result;
+}
+
+static int ll_page_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf)
+{
+ int count = 0;
+ bool printed = false;
+ bool retry;
+ int result;
+
+ do {
+ retry = false;
+ result = ll_page_mkwrite0(vma, vmf->page, &retry);
+
+ if (!printed && ++count > 16) {
+ CWARN("app(%s): the page %lu of file %lu is under heavy contention.\n",
+ current->comm, vmf->pgoff,
+ file_inode(vma->vm_file)->i_ino);
+ printed = true;
+ }
+ } while (retry);
+
+ switch (result) {
+ case 0:
+ LASSERT(PageLocked(vmf->page));
+ result = VM_FAULT_LOCKED;
+ break;
+ case -ENODATA:
+ case -EFAULT:
+ result = VM_FAULT_NOPAGE;
+ break;
+ case -ENOMEM:
+ result = VM_FAULT_OOM;
+ break;
+ case -EAGAIN:
+ result = VM_FAULT_RETRY;
+ break;
+ default:
+ result = VM_FAULT_SIGBUS;
+ break;
+ }
+
+ return result;
+}
+
+/**
+ * To avoid cancel the locks covering mmapped region for lock cache pressure,
+ * we track the mapped vma count in ccc_object::cob_mmap_cnt.
+ */
+static void ll_vm_open(struct vm_area_struct *vma)
+{
+ struct inode *inode = file_inode(vma->vm_file);
+ struct ccc_object *vob = cl_inode2ccc(inode);
+
+ LASSERT(vma->vm_file);
+ LASSERT(atomic_read(&vob->cob_mmap_cnt) >= 0);
+ atomic_inc(&vob->cob_mmap_cnt);
+}
+
+/**
+ * Dual to ll_vm_open().
+ */
+static void ll_vm_close(struct vm_area_struct *vma)
+{
+ struct inode *inode = file_inode(vma->vm_file);
+ struct ccc_object *vob = cl_inode2ccc(inode);
+
+ LASSERT(vma->vm_file);
+ atomic_dec(&vob->cob_mmap_cnt);
+ LASSERT(atomic_read(&vob->cob_mmap_cnt) >= 0);
+}
+
+/* XXX put nice comment here. talk about __free_pte -> dirty pages and
+ * nopage's reference passing to the pte */
+int ll_teardown_mmaps(struct address_space *mapping, __u64 first, __u64 last)
+{
+ int rc = -ENOENT;
+
+ LASSERTF(last > first, "last %llu first %llu\n", last, first);
+ if (mapping_mapped(mapping)) {
+ rc = 0;
+ unmap_mapping_range(mapping, first + PAGE_CACHE_SIZE - 1,
+ last - first + 1, 0);
+ }
+
+ return rc;
+}
+
+static const struct vm_operations_struct ll_file_vm_ops = {
+ .fault = ll_fault,
+ .page_mkwrite = ll_page_mkwrite,
+ .open = ll_vm_open,
+ .close = ll_vm_close,
+};
+
+int ll_file_mmap(struct file *file, struct vm_area_struct *vma)
+{
+ struct inode *inode = file_inode(file);
+ int rc;
+
+ if (ll_file_nolock(file))
+ return -EOPNOTSUPP;
+
+ ll_stats_ops_tally(ll_i2sbi(inode), LPROC_LL_MAP, 1);
+ rc = generic_file_mmap(file, vma);
+ if (rc == 0) {
+ vma->vm_ops = &ll_file_vm_ops;
+ vma->vm_ops->open(vma);
+ /* update the inode's size and mtime */
+ rc = ll_glimpse_size(inode);
+ }
+
+ return rc;
+}
diff --git a/drivers/staging/lustre/lustre/llite/llite_nfs.c b/drivers/staging/lustre/lustre/llite/llite_nfs.c
new file mode 100644
index 000000000..db43b8138
--- /dev/null
+++ b/drivers/staging/lustre/lustre/llite/llite_nfs.c
@@ -0,0 +1,335 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2011, 2012, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ *
+ * lustre/lustre/llite/llite_nfs.c
+ *
+ * NFS export of Lustre Light File System
+ *
+ * Author: Yury Umanets <umka@clusterfs.com>
+ * Author: Huang Hua <huanghua@clusterfs.com>
+ */
+
+#define DEBUG_SUBSYSTEM S_LLITE
+#include "../include/lustre_lite.h"
+#include "llite_internal.h"
+#include <linux/exportfs.h>
+
+__u32 get_uuid2int(const char *name, int len)
+{
+ __u32 key0 = 0x12a3fe2d, key1 = 0x37abe8f9;
+ while (len--) {
+ __u32 key = key1 + (key0 ^ (*name++ * 7152373));
+
+ if (key & 0x80000000)
+ key -= 0x7fffffff;
+ key1 = key0;
+ key0 = key;
+ }
+ return (key0 << 1);
+}
+
+void get_uuid2fsid(const char *name, int len, __kernel_fsid_t *fsid)
+{
+ __u64 key = 0, key0 = 0x12a3fe2d, key1 = 0x37abe8f9;
+
+ while (len--) {
+ key = key1 + (key0 ^ (*name++ * 7152373));
+ if (key & 0x8000000000000000ULL)
+ key -= 0x7fffffffffffffffULL;
+ key1 = key0;
+ key0 = key;
+ }
+
+ fsid->val[0] = key;
+ fsid->val[1] = key >> 32;
+}
+
+static int ll_nfs_test_inode(struct inode *inode, void *opaque)
+{
+ return lu_fid_eq(&ll_i2info(inode)->lli_fid,
+ (struct lu_fid *)opaque);
+}
+
+struct inode *search_inode_for_lustre(struct super_block *sb,
+ const struct lu_fid *fid)
+{
+ struct ll_sb_info *sbi = ll_s2sbi(sb);
+ struct ptlrpc_request *req = NULL;
+ struct inode *inode = NULL;
+ int eadatalen = 0;
+ unsigned long hash = cl_fid_build_ino(fid,
+ ll_need_32bit_api(sbi));
+ struct md_op_data *op_data;
+ int rc;
+
+ CDEBUG(D_INFO, "searching inode for:(%lu,"DFID")\n", hash, PFID(fid));
+
+ inode = ilookup5(sb, hash, ll_nfs_test_inode, (void *)fid);
+ if (inode)
+ return inode;
+
+ rc = ll_get_default_mdsize(sbi, &eadatalen);
+ if (rc)
+ return ERR_PTR(rc);
+
+ /* Because inode is NULL, ll_prep_md_op_data can not
+ * be used here. So we allocate op_data ourselves */
+ op_data = kzalloc(sizeof(*op_data), GFP_NOFS);
+ if (!op_data)
+ return ERR_PTR(-ENOMEM);
+
+ op_data->op_fid1 = *fid;
+ op_data->op_mode = eadatalen;
+ op_data->op_valid = OBD_MD_FLEASIZE;
+
+ /* mds_fid2dentry ignores f_type */
+ rc = md_getattr(sbi->ll_md_exp, op_data, &req);
+ OBD_FREE_PTR(op_data);
+ if (rc) {
+ CERROR("can't get object attrs, fid "DFID", rc %d\n",
+ PFID(fid), rc);
+ return ERR_PTR(rc);
+ }
+ rc = ll_prep_inode(&inode, req, sb, NULL);
+ ptlrpc_req_finished(req);
+ if (rc)
+ return ERR_PTR(rc);
+
+ return inode;
+}
+
+struct lustre_nfs_fid {
+ struct lu_fid lnf_child;
+ struct lu_fid lnf_parent;
+};
+
+static struct dentry *
+ll_iget_for_nfs(struct super_block *sb, struct lu_fid *fid, struct lu_fid *parent)
+{
+ struct inode *inode;
+ struct dentry *result;
+
+ CDEBUG(D_INFO, "Get dentry for fid: "DFID"\n", PFID(fid));
+ if (!fid_is_sane(fid))
+ return ERR_PTR(-ESTALE);
+
+ inode = search_inode_for_lustre(sb, fid);
+ if (IS_ERR(inode))
+ return ERR_CAST(inode);
+
+ if (is_bad_inode(inode)) {
+ /* we didn't find the right inode.. */
+ iput(inode);
+ return ERR_PTR(-ESTALE);
+ }
+
+ /**
+ * It is an anonymous dentry without OST objects created yet.
+ * We have to find the parent to tell MDS how to init lov objects.
+ */
+ if (S_ISREG(inode->i_mode) && !ll_i2info(inode)->lli_has_smd &&
+ parent != NULL) {
+ struct ll_inode_info *lli = ll_i2info(inode);
+
+ spin_lock(&lli->lli_lock);
+ lli->lli_pfid = *parent;
+ spin_unlock(&lli->lli_lock);
+ }
+
+ result = d_obtain_alias(inode);
+ if (IS_ERR(result)) {
+ iput(inode);
+ return result;
+ }
+
+ return result;
+}
+
+#define LUSTRE_NFS_FID 0x97
+
+/**
+ * \a connectable - is nfsd will connect himself or this should be done
+ * at lustre
+ *
+ * The return value is file handle type:
+ * 1 -- contains child file handle;
+ * 2 -- contains child file handle and parent file handle;
+ * 255 -- error.
+ */
+static int ll_encode_fh(struct inode *inode, __u32 *fh, int *plen,
+ struct inode *parent)
+{
+ struct lustre_nfs_fid *nfs_fid = (void *)fh;
+
+ CDEBUG(D_INFO, "encoding for (%lu,"DFID") maxlen=%d minlen=%d\n",
+ inode->i_ino, PFID(ll_inode2fid(inode)), *plen,
+ (int)sizeof(struct lustre_nfs_fid));
+
+ if (*plen < sizeof(struct lustre_nfs_fid) / 4)
+ return 255;
+
+ nfs_fid->lnf_child = *ll_inode2fid(inode);
+ nfs_fid->lnf_parent = *ll_inode2fid(parent);
+ *plen = sizeof(struct lustre_nfs_fid) / 4;
+
+ return LUSTRE_NFS_FID;
+}
+
+static int ll_nfs_get_name_filldir(struct dir_context *ctx, const char *name,
+ int namelen, loff_t hash, u64 ino,
+ unsigned type)
+{
+ /* It is hack to access lde_fid for comparison with lgd_fid.
+ * So the input 'name' must be part of the 'lu_dirent'. */
+ struct lu_dirent *lde = container_of0(name, struct lu_dirent, lde_name);
+ struct ll_getname_data *lgd =
+ container_of(ctx, struct ll_getname_data, ctx);
+ struct lu_fid fid;
+
+ fid_le_to_cpu(&fid, &lde->lde_fid);
+ if (lu_fid_eq(&fid, &lgd->lgd_fid)) {
+ memcpy(lgd->lgd_name, name, namelen);
+ lgd->lgd_name[namelen] = 0;
+ lgd->lgd_found = 1;
+ }
+ return lgd->lgd_found;
+}
+
+static int ll_get_name(struct dentry *dentry, char *name,
+ struct dentry *child)
+{
+ struct inode *dir = d_inode(dentry);
+ int rc;
+ struct ll_getname_data lgd = {
+ .lgd_name = name,
+ .lgd_fid = ll_i2info(d_inode(child))->lli_fid,
+ .ctx.actor = ll_nfs_get_name_filldir,
+ };
+
+ if (!dir || !S_ISDIR(dir->i_mode)) {
+ rc = -ENOTDIR;
+ goto out;
+ }
+
+ if (!dir->i_fop) {
+ rc = -EINVAL;
+ goto out;
+ }
+
+ mutex_lock(&dir->i_mutex);
+ rc = ll_dir_read(dir, &lgd.ctx);
+ mutex_unlock(&dir->i_mutex);
+ if (!rc && !lgd.lgd_found)
+ rc = -ENOENT;
+out:
+ return rc;
+}
+
+static struct dentry *ll_fh_to_dentry(struct super_block *sb, struct fid *fid,
+ int fh_len, int fh_type)
+{
+ struct lustre_nfs_fid *nfs_fid = (struct lustre_nfs_fid *)fid;
+
+ if (fh_type != LUSTRE_NFS_FID)
+ return ERR_PTR(-EPROTO);
+
+ return ll_iget_for_nfs(sb, &nfs_fid->lnf_child, &nfs_fid->lnf_parent);
+}
+
+static struct dentry *ll_fh_to_parent(struct super_block *sb, struct fid *fid,
+ int fh_len, int fh_type)
+{
+ struct lustre_nfs_fid *nfs_fid = (struct lustre_nfs_fid *)fid;
+
+ if (fh_type != LUSTRE_NFS_FID)
+ return ERR_PTR(-EPROTO);
+
+ return ll_iget_for_nfs(sb, &nfs_fid->lnf_parent, NULL);
+}
+
+static struct dentry *ll_get_parent(struct dentry *dchild)
+{
+ struct ptlrpc_request *req = NULL;
+ struct inode *dir = d_inode(dchild);
+ struct ll_sb_info *sbi;
+ struct dentry *result = NULL;
+ struct mdt_body *body;
+ static char dotdot[] = "..";
+ struct md_op_data *op_data;
+ int rc;
+ int lmmsize;
+
+ LASSERT(dir && S_ISDIR(dir->i_mode));
+
+ sbi = ll_s2sbi(dir->i_sb);
+
+ CDEBUG(D_INFO, "getting parent for (%lu,"DFID")\n",
+ dir->i_ino, PFID(ll_inode2fid(dir)));
+
+ rc = ll_get_default_mdsize(sbi, &lmmsize);
+ if (rc != 0)
+ return ERR_PTR(rc);
+
+ op_data = ll_prep_md_op_data(NULL, dir, NULL, dotdot,
+ strlen(dotdot), lmmsize,
+ LUSTRE_OPC_ANY, NULL);
+ if (IS_ERR(op_data))
+ return (void *)op_data;
+
+ rc = md_getattr_name(sbi->ll_md_exp, op_data, &req);
+ ll_finish_md_op_data(op_data);
+ if (rc) {
+ CERROR("failure %d inode %lu get parent\n", rc, dir->i_ino);
+ return ERR_PTR(rc);
+ }
+ body = req_capsule_server_get(&req->rq_pill, &RMF_MDT_BODY);
+ LASSERT(body->valid & OBD_MD_FLID);
+
+ CDEBUG(D_INFO, "parent for "DFID" is "DFID"\n",
+ PFID(ll_inode2fid(dir)), PFID(&body->fid1));
+
+ result = ll_iget_for_nfs(dir->i_sb, &body->fid1, NULL);
+
+ ptlrpc_req_finished(req);
+ return result;
+}
+
+struct export_operations lustre_export_operations = {
+ .get_parent = ll_get_parent,
+ .encode_fh = ll_encode_fh,
+ .get_name = ll_get_name,
+ .fh_to_dentry = ll_fh_to_dentry,
+ .fh_to_parent = ll_fh_to_parent,
+};
diff --git a/drivers/staging/lustre/lustre/llite/llite_rmtacl.c b/drivers/staging/lustre/lustre/llite/llite_rmtacl.c
new file mode 100644
index 000000000..f4da156f3
--- /dev/null
+++ b/drivers/staging/lustre/lustre/llite/llite_rmtacl.c
@@ -0,0 +1,300 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2012, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ *
+ * lustre/llite/llite_rmtacl.c
+ *
+ * Lustre Remote User Access Control List.
+ *
+ * Author: Fan Yong <fanyong@clusterfs.com>
+ */
+
+#define DEBUG_SUBSYSTEM S_LLITE
+
+#ifdef CONFIG_FS_POSIX_ACL
+
+#include "../include/lustre_lite.h"
+#include "../include/lustre_eacl.h"
+#include "llite_internal.h"
+
+static inline __u32 rce_hashfunc(uid_t id)
+{
+ return id & (RCE_HASHES - 1);
+}
+
+static inline __u32 ee_hashfunc(uid_t id)
+{
+ return id & (EE_HASHES - 1);
+}
+
+u64 rce_ops2valid(int ops)
+{
+ switch (ops) {
+ case RMT_LSETFACL:
+ return OBD_MD_FLRMTLSETFACL;
+ case RMT_LGETFACL:
+ return OBD_MD_FLRMTLGETFACL;
+ case RMT_RSETFACL:
+ return OBD_MD_FLRMTRSETFACL;
+ case RMT_RGETFACL:
+ return OBD_MD_FLRMTRGETFACL;
+ default:
+ return 0;
+ }
+}
+
+static struct rmtacl_ctl_entry *rce_alloc(pid_t key, int ops)
+{
+ struct rmtacl_ctl_entry *rce;
+
+ rce = kzalloc(sizeof(*rce), GFP_NOFS);
+ if (!rce)
+ return NULL;
+
+ INIT_LIST_HEAD(&rce->rce_list);
+ rce->rce_key = key;
+ rce->rce_ops = ops;
+
+ return rce;
+}
+
+static void rce_free(struct rmtacl_ctl_entry *rce)
+{
+ if (!list_empty(&rce->rce_list))
+ list_del(&rce->rce_list);
+
+ OBD_FREE_PTR(rce);
+}
+
+static struct rmtacl_ctl_entry *__rct_search(struct rmtacl_ctl_table *rct,
+ pid_t key)
+{
+ struct rmtacl_ctl_entry *rce;
+ struct list_head *head = &rct->rct_entries[rce_hashfunc(key)];
+
+ list_for_each_entry(rce, head, rce_list)
+ if (rce->rce_key == key)
+ return rce;
+
+ return NULL;
+}
+
+struct rmtacl_ctl_entry *rct_search(struct rmtacl_ctl_table *rct, pid_t key)
+{
+ struct rmtacl_ctl_entry *rce;
+
+ spin_lock(&rct->rct_lock);
+ rce = __rct_search(rct, key);
+ spin_unlock(&rct->rct_lock);
+ return rce;
+}
+
+int rct_add(struct rmtacl_ctl_table *rct, pid_t key, int ops)
+{
+ struct rmtacl_ctl_entry *rce, *e;
+
+ rce = rce_alloc(key, ops);
+ if (rce == NULL)
+ return -ENOMEM;
+
+ spin_lock(&rct->rct_lock);
+ e = __rct_search(rct, key);
+ if (unlikely(e != NULL)) {
+ CWARN("Unexpected stale rmtacl_entry found: [key: %d] [ops: %d]\n",
+ (int)key, ops);
+ rce_free(e);
+ }
+ list_add_tail(&rce->rce_list, &rct->rct_entries[rce_hashfunc(key)]);
+ spin_unlock(&rct->rct_lock);
+
+ return 0;
+}
+
+int rct_del(struct rmtacl_ctl_table *rct, pid_t key)
+{
+ struct rmtacl_ctl_entry *rce;
+
+ spin_lock(&rct->rct_lock);
+ rce = __rct_search(rct, key);
+ if (rce)
+ rce_free(rce);
+ spin_unlock(&rct->rct_lock);
+
+ return rce ? 0 : -ENOENT;
+}
+
+void rct_init(struct rmtacl_ctl_table *rct)
+{
+ int i;
+
+ spin_lock_init(&rct->rct_lock);
+ for (i = 0; i < RCE_HASHES; i++)
+ INIT_LIST_HEAD(&rct->rct_entries[i]);
+}
+
+void rct_fini(struct rmtacl_ctl_table *rct)
+{
+ struct rmtacl_ctl_entry *rce;
+ int i;
+
+ spin_lock(&rct->rct_lock);
+ for (i = 0; i < RCE_HASHES; i++)
+ while (!list_empty(&rct->rct_entries[i])) {
+ rce = list_entry(rct->rct_entries[i].next,
+ struct rmtacl_ctl_entry, rce_list);
+ rce_free(rce);
+ }
+ spin_unlock(&rct->rct_lock);
+}
+
+
+static struct eacl_entry *ee_alloc(pid_t key, struct lu_fid *fid, int type,
+ ext_acl_xattr_header *header)
+{
+ struct eacl_entry *ee;
+
+ ee = kzalloc(sizeof(*ee), GFP_NOFS);
+ if (!ee)
+ return NULL;
+
+ INIT_LIST_HEAD(&ee->ee_list);
+ ee->ee_key = key;
+ ee->ee_fid = *fid;
+ ee->ee_type = type;
+ ee->ee_acl = header;
+
+ return ee;
+}
+
+void ee_free(struct eacl_entry *ee)
+{
+ if (!list_empty(&ee->ee_list))
+ list_del(&ee->ee_list);
+
+ if (ee->ee_acl)
+ lustre_ext_acl_xattr_free(ee->ee_acl);
+
+ OBD_FREE_PTR(ee);
+}
+
+static struct eacl_entry *__et_search_del(struct eacl_table *et, pid_t key,
+ struct lu_fid *fid, int type)
+{
+ struct eacl_entry *ee;
+ struct list_head *head = &et->et_entries[ee_hashfunc(key)];
+
+ LASSERT(fid != NULL);
+ list_for_each_entry(ee, head, ee_list)
+ if (ee->ee_key == key) {
+ if (lu_fid_eq(&ee->ee_fid, fid) &&
+ ee->ee_type == type) {
+ list_del_init(&ee->ee_list);
+ return ee;
+ }
+ }
+
+ return NULL;
+}
+
+struct eacl_entry *et_search_del(struct eacl_table *et, pid_t key,
+ struct lu_fid *fid, int type)
+{
+ struct eacl_entry *ee;
+
+ spin_lock(&et->et_lock);
+ ee = __et_search_del(et, key, fid, type);
+ spin_unlock(&et->et_lock);
+ return ee;
+}
+
+void et_search_free(struct eacl_table *et, pid_t key)
+{
+ struct eacl_entry *ee, *next;
+ struct list_head *head = &et->et_entries[ee_hashfunc(key)];
+
+ spin_lock(&et->et_lock);
+ list_for_each_entry_safe(ee, next, head, ee_list)
+ if (ee->ee_key == key)
+ ee_free(ee);
+
+ spin_unlock(&et->et_lock);
+}
+
+int ee_add(struct eacl_table *et, pid_t key, struct lu_fid *fid, int type,
+ ext_acl_xattr_header *header)
+{
+ struct eacl_entry *ee, *e;
+
+ ee = ee_alloc(key, fid, type, header);
+ if (ee == NULL)
+ return -ENOMEM;
+
+ spin_lock(&et->et_lock);
+ e = __et_search_del(et, key, fid, type);
+ if (unlikely(e != NULL)) {
+ CWARN("Unexpected stale eacl_entry found: [key: %d] [fid: " DFID "] [type: %d]\n",
+ (int)key, PFID(fid), type);
+ ee_free(e);
+ }
+ list_add_tail(&ee->ee_list, &et->et_entries[ee_hashfunc(key)]);
+ spin_unlock(&et->et_lock);
+
+ return 0;
+}
+
+void et_init(struct eacl_table *et)
+{
+ int i;
+
+ spin_lock_init(&et->et_lock);
+ for (i = 0; i < EE_HASHES; i++)
+ INIT_LIST_HEAD(&et->et_entries[i]);
+}
+
+void et_fini(struct eacl_table *et)
+{
+ struct eacl_entry *ee;
+ int i;
+
+ spin_lock(&et->et_lock);
+ for (i = 0; i < EE_HASHES; i++)
+ while (!list_empty(&et->et_entries[i])) {
+ ee = list_entry(et->et_entries[i].next,
+ struct eacl_entry, ee_list);
+ ee_free(ee);
+ }
+ spin_unlock(&et->et_lock);
+}
+
+#endif
diff --git a/drivers/staging/lustre/lustre/llite/lloop.c b/drivers/staging/lustre/lustre/llite/lloop.c
new file mode 100644
index 000000000..413a8408e
--- /dev/null
+++ b/drivers/staging/lustre/lustre/llite/lloop.c
@@ -0,0 +1,877 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2011, 2012, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ */
+
+/*
+ * linux/drivers/block/loop.c
+ *
+ * Written by Theodore Ts'o, 3/29/93
+ *
+ * Copyright 1993 by Theodore Ts'o. Redistribution of this file is
+ * permitted under the GNU General Public License.
+ *
+ * Modularized and updated for 1.1.16 kernel - Mitch Dsouza 28th May 1994
+ * Adapted for 1.3.59 kernel - Andries Brouwer, 1 Feb 1996
+ *
+ * Fixed do_loop_request() re-entrancy - Vincent.Renardias@waw.com Mar 20, 1997
+ *
+ * Added devfs support - Richard Gooch <rgooch@atnf.csiro.au> 16-Jan-1998
+ *
+ * Handle sparse backing files correctly - Kenn Humborg, Jun 28, 1998
+ *
+ * Loadable modules and other fixes by AK, 1998
+ *
+ * Maximum number of loop devices now dynamic via max_loop module parameter.
+ * Russell Kroll <rkroll@exploits.org> 19990701
+ *
+ * Maximum number of loop devices when compiled-in now selectable by passing
+ * max_loop=<1-255> to the kernel on boot.
+ * Erik I. Bols?, <eriki@himolde.no>, Oct 31, 1999
+ *
+ * Completely rewrite request handling to be make_request_fn style and
+ * non blocking, pushing work to a helper thread. Lots of fixes from
+ * Al Viro too.
+ * Jens Axboe <axboe@suse.de>, Nov 2000
+ *
+ * Support up to 256 loop devices
+ * Heinz Mauelshagen <mge@sistina.com>, Feb 2002
+ *
+ * Support for falling back on the write file operation when the address space
+ * operations prepare_write and/or commit_write are not available on the
+ * backing filesystem.
+ * Anton Altaparmakov, 16 Feb 2005
+ *
+ * Still To Fix:
+ * - Advisory locking is ignored here.
+ * - Should use an own CAP_* category instead of CAP_SYS_ADMIN
+ *
+ */
+
+#include <linux/module.h>
+
+#include <linux/sched.h>
+#include <linux/fs.h>
+#include <linux/file.h>
+#include <linux/stat.h>
+#include <linux/errno.h>
+#include <linux/major.h>
+#include <linux/wait.h>
+#include <linux/blkdev.h>
+#include <linux/blkpg.h>
+#include <linux/init.h>
+#include <linux/swap.h>
+#include <linux/slab.h>
+#include <linux/suspend.h>
+#include <linux/writeback.h>
+#include <linux/buffer_head.h> /* for invalidate_bdev() */
+#include <linux/completion.h>
+#include <linux/highmem.h>
+#include <linux/gfp.h>
+#include <linux/pagevec.h>
+#include <linux/uaccess.h>
+
+#include "../include/lustre_lib.h"
+#include "../include/lustre_lite.h"
+#include "llite_internal.h"
+
+#define LLOOP_MAX_SEGMENTS LNET_MAX_IOV
+
+/* Possible states of device */
+enum {
+ LLOOP_UNBOUND,
+ LLOOP_BOUND,
+ LLOOP_RUNDOWN,
+};
+
+struct lloop_device {
+ int lo_number;
+ int lo_refcnt;
+ loff_t lo_offset;
+ loff_t lo_sizelimit;
+ int lo_flags;
+ struct file *lo_backing_file;
+ struct block_device *lo_device;
+ unsigned lo_blocksize;
+
+ gfp_t old_gfp_mask;
+
+ spinlock_t lo_lock;
+ struct bio *lo_bio;
+ struct bio *lo_biotail;
+ int lo_state;
+ struct semaphore lo_sem;
+ struct mutex lo_ctl_mutex;
+ atomic_t lo_pending;
+ wait_queue_head_t lo_bh_wait;
+
+ struct request_queue *lo_queue;
+
+ const struct lu_env *lo_env;
+ struct cl_io lo_io;
+ struct ll_dio_pages lo_pvec;
+
+ /* data to handle bio for lustre. */
+ struct lo_request_data {
+ struct page *lrd_pages[LLOOP_MAX_SEGMENTS];
+ loff_t lrd_offsets[LLOOP_MAX_SEGMENTS];
+ } lo_requests[1];
+};
+
+/*
+ * Loop flags
+ */
+enum {
+ LO_FLAGS_READ_ONLY = 1,
+};
+
+static int lloop_major;
+#define MAX_LOOP_DEFAULT 16
+static int max_loop = MAX_LOOP_DEFAULT;
+static struct lloop_device *loop_dev;
+static struct gendisk **disks;
+static struct mutex lloop_mutex;
+static void *ll_iocontrol_magic = NULL;
+
+static loff_t get_loop_size(struct lloop_device *lo, struct file *file)
+{
+ loff_t size, offset, loopsize;
+
+ /* Compute loopsize in bytes */
+ size = i_size_read(file->f_mapping->host);
+ offset = lo->lo_offset;
+ loopsize = size - offset;
+ if (lo->lo_sizelimit > 0 && lo->lo_sizelimit < loopsize)
+ loopsize = lo->lo_sizelimit;
+
+ /*
+ * Unfortunately, if we want to do I/O on the device,
+ * the number of 512-byte sectors has to fit into a sector_t.
+ */
+ return loopsize >> 9;
+}
+
+static int do_bio_lustrebacked(struct lloop_device *lo, struct bio *head)
+{
+ const struct lu_env *env = lo->lo_env;
+ struct cl_io *io = &lo->lo_io;
+ struct inode *inode = file_inode(lo->lo_backing_file);
+ struct cl_object *obj = ll_i2info(inode)->lli_clob;
+ pgoff_t offset;
+ int ret;
+ int rw;
+ u32 page_count = 0;
+ struct bio_vec bvec;
+ struct bvec_iter iter;
+ struct bio *bio;
+ ssize_t bytes;
+
+ struct ll_dio_pages *pvec = &lo->lo_pvec;
+ struct page **pages = pvec->ldp_pages;
+ loff_t *offsets = pvec->ldp_offsets;
+
+ truncate_inode_pages(inode->i_mapping, 0);
+
+ /* initialize the IO */
+ memset(io, 0, sizeof(*io));
+ io->ci_obj = obj;
+ ret = cl_io_init(env, io, CIT_MISC, obj);
+ if (ret)
+ return io->ci_result;
+ io->ci_lockreq = CILR_NEVER;
+
+ LASSERT(head != NULL);
+ rw = head->bi_rw;
+ for (bio = head; bio != NULL; bio = bio->bi_next) {
+ LASSERT(rw == bio->bi_rw);
+
+ offset = (pgoff_t)(bio->bi_iter.bi_sector << 9) + lo->lo_offset;
+ bio_for_each_segment(bvec, bio, iter) {
+ BUG_ON(bvec.bv_offset != 0);
+ BUG_ON(bvec.bv_len != PAGE_CACHE_SIZE);
+
+ pages[page_count] = bvec.bv_page;
+ offsets[page_count] = offset;
+ page_count++;
+ offset += bvec.bv_len;
+ }
+ LASSERT(page_count <= LLOOP_MAX_SEGMENTS);
+ }
+
+ ll_stats_ops_tally(ll_i2sbi(inode),
+ (rw == WRITE) ? LPROC_LL_BRW_WRITE : LPROC_LL_BRW_READ,
+ page_count);
+
+ pvec->ldp_size = page_count << PAGE_CACHE_SHIFT;
+ pvec->ldp_nr = page_count;
+
+ /* FIXME: in ll_direct_rw_pages, it has to allocate many cl_page{}s to
+ * write those pages into OST. Even worse case is that more pages
+ * would be asked to write out to swap space, and then finally get here
+ * again.
+ * Unfortunately this is NOT easy to fix.
+ * Thoughts on solution:
+ * 0. Define a reserved pool for cl_pages, which could be a list of
+ * pre-allocated cl_pages;
+ * 1. Define a new operation in cl_object_operations{}, says clo_depth,
+ * which measures how many layers for this lustre object. Generally
+ * speaking, the depth would be 2, one for llite, and one for lovsub.
+ * However, for SNS, there will be more since we need additional page
+ * to store parity;
+ * 2. Reserve the # of (page_count * depth) cl_pages from the reserved
+ * pool. Afterwards, the clio would allocate the pages from reserved
+ * pool, this guarantees we needn't allocate the cl_pages from
+ * generic cl_page slab cache.
+ * Of course, if there is NOT enough pages in the pool, we might
+ * be asked to write less pages once, this purely depends on
+ * implementation. Anyway, we should be careful to avoid deadlocking.
+ */
+ mutex_lock(&inode->i_mutex);
+ bytes = ll_direct_rw_pages(env, io, rw, inode, pvec);
+ mutex_unlock(&inode->i_mutex);
+ cl_io_fini(env, io);
+ return (bytes == pvec->ldp_size) ? 0 : (int)bytes;
+}
+
+/*
+ * Add bio to back of pending list
+ */
+static void loop_add_bio(struct lloop_device *lo, struct bio *bio)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&lo->lo_lock, flags);
+ if (lo->lo_biotail) {
+ lo->lo_biotail->bi_next = bio;
+ lo->lo_biotail = bio;
+ } else
+ lo->lo_bio = lo->lo_biotail = bio;
+ spin_unlock_irqrestore(&lo->lo_lock, flags);
+
+ atomic_inc(&lo->lo_pending);
+ if (waitqueue_active(&lo->lo_bh_wait))
+ wake_up(&lo->lo_bh_wait);
+}
+
+/*
+ * Grab first pending buffer
+ */
+static unsigned int loop_get_bio(struct lloop_device *lo, struct bio **req)
+{
+ struct bio *first;
+ struct bio **bio;
+ unsigned int count = 0;
+ unsigned int page_count = 0;
+ int rw;
+
+ spin_lock_irq(&lo->lo_lock);
+ first = lo->lo_bio;
+ if (unlikely(first == NULL)) {
+ spin_unlock_irq(&lo->lo_lock);
+ return 0;
+ }
+
+ /* TODO: need to split the bio, too bad. */
+ LASSERT(first->bi_vcnt <= LLOOP_MAX_SEGMENTS);
+
+ rw = first->bi_rw;
+ bio = &lo->lo_bio;
+ while (*bio && (*bio)->bi_rw == rw) {
+ CDEBUG(D_INFO, "bio sector %llu size %u count %u vcnt%u \n",
+ (unsigned long long)(*bio)->bi_iter.bi_sector,
+ (*bio)->bi_iter.bi_size,
+ page_count, (*bio)->bi_vcnt);
+ if (page_count + (*bio)->bi_vcnt > LLOOP_MAX_SEGMENTS)
+ break;
+
+
+ page_count += (*bio)->bi_vcnt;
+ count++;
+ bio = &(*bio)->bi_next;
+ }
+ if (*bio) {
+ /* Some of bios can't be mergeable. */
+ lo->lo_bio = *bio;
+ *bio = NULL;
+ } else {
+ /* Hit the end of queue */
+ lo->lo_biotail = NULL;
+ lo->lo_bio = NULL;
+ }
+ *req = first;
+ spin_unlock_irq(&lo->lo_lock);
+ return count;
+}
+
+static void loop_make_request(struct request_queue *q, struct bio *old_bio)
+{
+ struct lloop_device *lo = q->queuedata;
+ int rw = bio_rw(old_bio);
+ int inactive;
+
+ if (!lo)
+ goto err;
+
+ CDEBUG(D_INFO, "submit bio sector %llu size %u\n",
+ (unsigned long long)old_bio->bi_iter.bi_sector,
+ old_bio->bi_iter.bi_size);
+
+ spin_lock_irq(&lo->lo_lock);
+ inactive = lo->lo_state != LLOOP_BOUND;
+ spin_unlock_irq(&lo->lo_lock);
+ if (inactive)
+ goto err;
+
+ if (rw == WRITE) {
+ if (lo->lo_flags & LO_FLAGS_READ_ONLY)
+ goto err;
+ } else if (rw == READA) {
+ rw = READ;
+ } else if (rw != READ) {
+ CERROR("lloop: unknown command (%x)\n", rw);
+ goto err;
+ }
+ loop_add_bio(lo, old_bio);
+ return;
+err:
+ cfs_bio_io_error(old_bio, old_bio->bi_iter.bi_size);
+}
+
+
+static inline void loop_handle_bio(struct lloop_device *lo, struct bio *bio)
+{
+ int ret;
+ ret = do_bio_lustrebacked(lo, bio);
+ while (bio) {
+ struct bio *tmp = bio->bi_next;
+ bio->bi_next = NULL;
+ cfs_bio_endio(bio, bio->bi_iter.bi_size, ret);
+ bio = tmp;
+ }
+}
+
+static inline int loop_active(struct lloop_device *lo)
+{
+ return atomic_read(&lo->lo_pending) ||
+ (lo->lo_state == LLOOP_RUNDOWN);
+}
+
+/*
+ * worker thread that handles reads/writes to file backed loop devices,
+ * to avoid blocking in our make_request_fn.
+ */
+static int loop_thread(void *data)
+{
+ struct lloop_device *lo = data;
+ struct bio *bio;
+ unsigned int count;
+ unsigned long times = 0;
+ unsigned long total_count = 0;
+
+ struct lu_env *env;
+ int refcheck;
+ int ret = 0;
+
+ set_user_nice(current, MIN_NICE);
+
+ lo->lo_state = LLOOP_BOUND;
+
+ env = cl_env_get(&refcheck);
+ if (IS_ERR(env)) {
+ ret = PTR_ERR(env);
+ goto out;
+ }
+
+ lo->lo_env = env;
+ memset(&lo->lo_pvec, 0, sizeof(lo->lo_pvec));
+ lo->lo_pvec.ldp_pages = lo->lo_requests[0].lrd_pages;
+ lo->lo_pvec.ldp_offsets = lo->lo_requests[0].lrd_offsets;
+
+ /*
+ * up sem, we are running
+ */
+ up(&lo->lo_sem);
+
+ for (;;) {
+ wait_event(lo->lo_bh_wait, loop_active(lo));
+ if (!atomic_read(&lo->lo_pending)) {
+ int exiting = 0;
+ spin_lock_irq(&lo->lo_lock);
+ exiting = (lo->lo_state == LLOOP_RUNDOWN);
+ spin_unlock_irq(&lo->lo_lock);
+ if (exiting)
+ break;
+ }
+
+ bio = NULL;
+ count = loop_get_bio(lo, &bio);
+ if (!count) {
+ CWARN("lloop(minor: %d): missing bio\n", lo->lo_number);
+ continue;
+ }
+
+ total_count += count;
+ if (total_count < count) { /* overflow */
+ total_count = count;
+ times = 1;
+ } else {
+ times++;
+ }
+ if ((times & 127) == 0) {
+ CDEBUG(D_INFO, "total: %lu, count: %lu, avg: %lu\n",
+ total_count, times, total_count / times);
+ }
+
+ LASSERT(bio != NULL);
+ LASSERT(count <= atomic_read(&lo->lo_pending));
+ loop_handle_bio(lo, bio);
+ atomic_sub(count, &lo->lo_pending);
+ }
+ cl_env_put(env, &refcheck);
+
+out:
+ up(&lo->lo_sem);
+ return ret;
+}
+
+static int loop_set_fd(struct lloop_device *lo, struct file *unused,
+ struct block_device *bdev, struct file *file)
+{
+ struct inode *inode;
+ struct address_space *mapping;
+ int lo_flags = 0;
+ int error;
+ loff_t size;
+
+ if (!try_module_get(THIS_MODULE))
+ return -ENODEV;
+
+ error = -EBUSY;
+ if (lo->lo_state != LLOOP_UNBOUND)
+ goto out;
+
+ mapping = file->f_mapping;
+ inode = mapping->host;
+
+ error = -EINVAL;
+ if (!S_ISREG(inode->i_mode) || inode->i_sb->s_magic != LL_SUPER_MAGIC)
+ goto out;
+
+ if (!(file->f_mode & FMODE_WRITE))
+ lo_flags |= LO_FLAGS_READ_ONLY;
+
+ size = get_loop_size(lo, file);
+
+ if ((loff_t)(sector_t)size != size) {
+ error = -EFBIG;
+ goto out;
+ }
+
+ /* remove all pages in cache so as dirty pages not to be existent. */
+ truncate_inode_pages(mapping, 0);
+
+ set_device_ro(bdev, (lo_flags & LO_FLAGS_READ_ONLY) != 0);
+
+ lo->lo_blocksize = PAGE_CACHE_SIZE;
+ lo->lo_device = bdev;
+ lo->lo_flags = lo_flags;
+ lo->lo_backing_file = file;
+ lo->lo_sizelimit = 0;
+ lo->old_gfp_mask = mapping_gfp_mask(mapping);
+ mapping_set_gfp_mask(mapping, lo->old_gfp_mask & ~(__GFP_IO|__GFP_FS));
+
+ lo->lo_bio = lo->lo_biotail = NULL;
+
+ /*
+ * set queue make_request_fn, and add limits based on lower level
+ * device
+ */
+ blk_queue_make_request(lo->lo_queue, loop_make_request);
+ lo->lo_queue->queuedata = lo;
+
+ /* queue parameters */
+ CLASSERT(PAGE_CACHE_SIZE < (1 << (sizeof(unsigned short) * 8)));
+ blk_queue_logical_block_size(lo->lo_queue,
+ (unsigned short)PAGE_CACHE_SIZE);
+ blk_queue_max_hw_sectors(lo->lo_queue,
+ LLOOP_MAX_SEGMENTS << (PAGE_CACHE_SHIFT - 9));
+ blk_queue_max_segments(lo->lo_queue, LLOOP_MAX_SEGMENTS);
+
+ set_capacity(disks[lo->lo_number], size);
+ bd_set_size(bdev, size << 9);
+
+ set_blocksize(bdev, lo->lo_blocksize);
+
+ kthread_run(loop_thread, lo, "lloop%d", lo->lo_number);
+ down(&lo->lo_sem);
+ return 0;
+
+out:
+ /* This is safe: open() is still holding a reference. */
+ module_put(THIS_MODULE);
+ return error;
+}
+
+static int loop_clr_fd(struct lloop_device *lo, struct block_device *bdev,
+ int count)
+{
+ struct file *filp = lo->lo_backing_file;
+ gfp_t gfp = lo->old_gfp_mask;
+
+ if (lo->lo_state != LLOOP_BOUND)
+ return -ENXIO;
+
+ if (lo->lo_refcnt > count) /* we needed one fd for the ioctl */
+ return -EBUSY;
+
+ if (filp == NULL)
+ return -EINVAL;
+
+ spin_lock_irq(&lo->lo_lock);
+ lo->lo_state = LLOOP_RUNDOWN;
+ spin_unlock_irq(&lo->lo_lock);
+ wake_up(&lo->lo_bh_wait);
+
+ down(&lo->lo_sem);
+ lo->lo_backing_file = NULL;
+ lo->lo_device = NULL;
+ lo->lo_offset = 0;
+ lo->lo_sizelimit = 0;
+ lo->lo_flags = 0;
+ invalidate_bdev(bdev);
+ set_capacity(disks[lo->lo_number], 0);
+ bd_set_size(bdev, 0);
+ mapping_set_gfp_mask(filp->f_mapping, gfp);
+ lo->lo_state = LLOOP_UNBOUND;
+ fput(filp);
+ /* This is safe: open() is still holding a reference. */
+ module_put(THIS_MODULE);
+ return 0;
+}
+
+static int lo_open(struct block_device *bdev, fmode_t mode)
+{
+ struct lloop_device *lo = bdev->bd_disk->private_data;
+
+ mutex_lock(&lo->lo_ctl_mutex);
+ lo->lo_refcnt++;
+ mutex_unlock(&lo->lo_ctl_mutex);
+
+ return 0;
+}
+
+static void lo_release(struct gendisk *disk, fmode_t mode)
+{
+ struct lloop_device *lo = disk->private_data;
+
+ mutex_lock(&lo->lo_ctl_mutex);
+ --lo->lo_refcnt;
+ mutex_unlock(&lo->lo_ctl_mutex);
+}
+
+/* lloop device node's ioctl function. */
+static int lo_ioctl(struct block_device *bdev, fmode_t mode,
+ unsigned int cmd, unsigned long arg)
+{
+ struct lloop_device *lo = bdev->bd_disk->private_data;
+ struct inode *inode = NULL;
+ int err = 0;
+
+ mutex_lock(&lloop_mutex);
+ switch (cmd) {
+ case LL_IOC_LLOOP_DETACH: {
+ err = loop_clr_fd(lo, bdev, 2);
+ if (err == 0)
+ blkdev_put(bdev, 0); /* grabbed in LLOOP_ATTACH */
+ break;
+ }
+
+ case LL_IOC_LLOOP_INFO: {
+ struct lu_fid fid;
+
+ if (lo->lo_backing_file == NULL) {
+ err = -ENOENT;
+ break;
+ }
+ if (inode == NULL)
+ inode = file_inode(lo->lo_backing_file);
+ if (lo->lo_state == LLOOP_BOUND)
+ fid = ll_i2info(inode)->lli_fid;
+ else
+ fid_zero(&fid);
+
+ if (copy_to_user((struct lu_fid *)arg, &fid, sizeof(fid)))
+ err = -EFAULT;
+ break;
+ }
+
+ default:
+ err = -EINVAL;
+ break;
+ }
+ mutex_unlock(&lloop_mutex);
+
+ return err;
+}
+
+static struct block_device_operations lo_fops = {
+ .owner = THIS_MODULE,
+ .open = lo_open,
+ .release = lo_release,
+ .ioctl = lo_ioctl,
+};
+
+/* dynamic iocontrol callback.
+ * This callback is registered in lloop_init and will be called by
+ * ll_iocontrol_call.
+ *
+ * This is a llite regular file ioctl function. It takes the responsibility
+ * of attaching or detaching a file by a lloop's device number.
+ */
+static enum llioc_iter lloop_ioctl(struct inode *unused, struct file *file,
+ unsigned int cmd, unsigned long arg,
+ void *magic, int *rcp)
+{
+ struct lloop_device *lo = NULL;
+ struct block_device *bdev = NULL;
+ int err = 0;
+ dev_t dev;
+
+ if (magic != ll_iocontrol_magic)
+ return LLIOC_CONT;
+
+ if (disks == NULL) {
+ err = -ENODEV;
+ goto out1;
+ }
+
+ CWARN("Enter llop_ioctl\n");
+
+ mutex_lock(&lloop_mutex);
+ switch (cmd) {
+ case LL_IOC_LLOOP_ATTACH: {
+ struct lloop_device *lo_free = NULL;
+ int i;
+
+ for (i = 0; i < max_loop; i++, lo = NULL) {
+ lo = &loop_dev[i];
+ if (lo->lo_state == LLOOP_UNBOUND) {
+ if (!lo_free)
+ lo_free = lo;
+ continue;
+ }
+ if (file_inode(lo->lo_backing_file) == file_inode(file))
+ break;
+ }
+ if (lo || !lo_free) {
+ err = -EBUSY;
+ goto out;
+ }
+
+ lo = lo_free;
+ dev = MKDEV(lloop_major, lo->lo_number);
+
+ /* quit if the used pointer is writable */
+ if (put_user((long)old_encode_dev(dev), (long *)arg)) {
+ err = -EFAULT;
+ goto out;
+ }
+
+ bdev = blkdev_get_by_dev(dev, file->f_mode, NULL);
+ if (IS_ERR(bdev)) {
+ err = PTR_ERR(bdev);
+ goto out;
+ }
+
+ get_file(file);
+ err = loop_set_fd(lo, NULL, bdev, file);
+ if (err) {
+ fput(file);
+ blkdev_put(bdev, 0);
+ }
+
+ break;
+ }
+
+ case LL_IOC_LLOOP_DETACH_BYDEV: {
+ int minor;
+
+ dev = old_decode_dev(arg);
+ if (MAJOR(dev) != lloop_major) {
+ err = -EINVAL;
+ goto out;
+ }
+
+ minor = MINOR(dev);
+ if (minor > max_loop - 1) {
+ err = -EINVAL;
+ goto out;
+ }
+
+ lo = &loop_dev[minor];
+ if (lo->lo_state != LLOOP_BOUND) {
+ err = -EINVAL;
+ goto out;
+ }
+
+ bdev = lo->lo_device;
+ err = loop_clr_fd(lo, bdev, 1);
+ if (err == 0)
+ blkdev_put(bdev, 0); /* grabbed in LLOOP_ATTACH */
+
+ break;
+ }
+
+ default:
+ err = -EINVAL;
+ break;
+ }
+
+out:
+ mutex_unlock(&lloop_mutex);
+out1:
+ if (rcp)
+ *rcp = err;
+ return LLIOC_STOP;
+}
+
+static int __init lloop_init(void)
+{
+ int i;
+ unsigned int cmdlist[] = {
+ LL_IOC_LLOOP_ATTACH,
+ LL_IOC_LLOOP_DETACH_BYDEV,
+ };
+
+ if (max_loop < 1 || max_loop > 256) {
+ max_loop = MAX_LOOP_DEFAULT;
+ CWARN("lloop: invalid max_loop (must be between 1 and 256), using default (%u)\n",
+ max_loop);
+ }
+
+ lloop_major = register_blkdev(0, "lloop");
+ if (lloop_major < 0)
+ return -EIO;
+
+ CDEBUG(D_CONFIG, "registered lloop major %d with %u minors\n",
+ lloop_major, max_loop);
+
+ ll_iocontrol_magic = ll_iocontrol_register(lloop_ioctl, 2, cmdlist);
+ if (ll_iocontrol_magic == NULL)
+ goto out_mem1;
+
+ loop_dev = kcalloc(max_loop, sizeof(*loop_dev), GFP_KERNEL);
+ if (!loop_dev)
+ goto out_mem1;
+
+ disks = kcalloc(max_loop, sizeof(*disks), GFP_KERNEL);
+ if (!disks)
+ goto out_mem2;
+
+ for (i = 0; i < max_loop; i++) {
+ disks[i] = alloc_disk(1);
+ if (!disks[i])
+ goto out_mem3;
+ }
+
+ mutex_init(&lloop_mutex);
+
+ for (i = 0; i < max_loop; i++) {
+ struct lloop_device *lo = &loop_dev[i];
+ struct gendisk *disk = disks[i];
+
+ lo->lo_queue = blk_alloc_queue(GFP_KERNEL);
+ if (!lo->lo_queue)
+ goto out_mem4;
+
+ mutex_init(&lo->lo_ctl_mutex);
+ sema_init(&lo->lo_sem, 0);
+ init_waitqueue_head(&lo->lo_bh_wait);
+ lo->lo_number = i;
+ spin_lock_init(&lo->lo_lock);
+ disk->major = lloop_major;
+ disk->first_minor = i;
+ disk->fops = &lo_fops;
+ sprintf(disk->disk_name, "lloop%d", i);
+ disk->private_data = lo;
+ disk->queue = lo->lo_queue;
+ }
+
+ /* We cannot fail after we call this, so another loop!*/
+ for (i = 0; i < max_loop; i++)
+ add_disk(disks[i]);
+ return 0;
+
+out_mem4:
+ while (i--)
+ blk_cleanup_queue(loop_dev[i].lo_queue);
+ i = max_loop;
+out_mem3:
+ while (i--)
+ put_disk(disks[i]);
+ OBD_FREE(disks, max_loop * sizeof(*disks));
+out_mem2:
+ OBD_FREE(loop_dev, max_loop * sizeof(*loop_dev));
+out_mem1:
+ unregister_blkdev(lloop_major, "lloop");
+ ll_iocontrol_unregister(ll_iocontrol_magic);
+ CERROR("lloop: ran out of memory\n");
+ return -ENOMEM;
+}
+
+static void lloop_exit(void)
+{
+ int i;
+
+ ll_iocontrol_unregister(ll_iocontrol_magic);
+ for (i = 0; i < max_loop; i++) {
+ del_gendisk(disks[i]);
+ blk_cleanup_queue(loop_dev[i].lo_queue);
+ put_disk(disks[i]);
+ }
+
+ unregister_blkdev(lloop_major, "lloop");
+
+ OBD_FREE(disks, max_loop * sizeof(*disks));
+ OBD_FREE(loop_dev, max_loop * sizeof(*loop_dev));
+}
+
+module_init(lloop_init);
+module_exit(lloop_exit);
+
+module_param(max_loop, int, 0444);
+MODULE_PARM_DESC(max_loop, "maximum of lloop_device");
+MODULE_AUTHOR("Sun Microsystems, Inc. <http://www.lustre.org/>");
+MODULE_DESCRIPTION("Lustre virtual block device");
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/lustre/lustre/llite/lproc_llite.c b/drivers/staging/lustre/lustre/llite/lproc_llite.c
new file mode 100644
index 000000000..83a9b8547
--- /dev/null
+++ b/drivers/staging/lustre/lustre/llite/lproc_llite.c
@@ -0,0 +1,1536 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2002, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2011, 2012, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ */
+#define DEBUG_SUBSYSTEM S_LLITE
+
+#include "../include/lustre_lite.h"
+#include "../include/lprocfs_status.h"
+#include <linux/seq_file.h>
+#include "../include/obd_support.h"
+
+#include "llite_internal.h"
+#include "vvp_internal.h"
+
+/* /proc/lustre/llite mount point registration */
+static struct file_operations ll_rw_extents_stats_fops;
+static struct file_operations ll_rw_extents_stats_pp_fops;
+static struct file_operations ll_rw_offset_stats_fops;
+
+static int ll_blksize_seq_show(struct seq_file *m, void *v)
+{
+ struct super_block *sb = (struct super_block *)m->private;
+ struct obd_statfs osfs;
+ int rc;
+
+ LASSERT(sb != NULL);
+ rc = ll_statfs_internal(sb, &osfs,
+ cfs_time_shift_64(-OBD_STATFS_CACHE_SECONDS),
+ OBD_STATFS_NODELAY);
+ if (!rc)
+ seq_printf(m, "%u\n", osfs.os_bsize);
+
+ return rc;
+}
+LPROC_SEQ_FOPS_RO(ll_blksize);
+
+static int ll_kbytestotal_seq_show(struct seq_file *m, void *v)
+{
+ struct super_block *sb = (struct super_block *)m->private;
+ struct obd_statfs osfs;
+ int rc;
+
+ LASSERT(sb != NULL);
+ rc = ll_statfs_internal(sb, &osfs,
+ cfs_time_shift_64(-OBD_STATFS_CACHE_SECONDS),
+ OBD_STATFS_NODELAY);
+ if (!rc) {
+ __u32 blk_size = osfs.os_bsize >> 10;
+ __u64 result = osfs.os_blocks;
+
+ while (blk_size >>= 1)
+ result <<= 1;
+
+ seq_printf(m, "%llu\n", result);
+ }
+
+ return rc;
+}
+LPROC_SEQ_FOPS_RO(ll_kbytestotal);
+
+static int ll_kbytesfree_seq_show(struct seq_file *m, void *v)
+{
+ struct super_block *sb = (struct super_block *)m->private;
+ struct obd_statfs osfs;
+ int rc;
+
+ LASSERT(sb != NULL);
+ rc = ll_statfs_internal(sb, &osfs,
+ cfs_time_shift_64(-OBD_STATFS_CACHE_SECONDS),
+ OBD_STATFS_NODELAY);
+ if (!rc) {
+ __u32 blk_size = osfs.os_bsize >> 10;
+ __u64 result = osfs.os_bfree;
+
+ while (blk_size >>= 1)
+ result <<= 1;
+
+ seq_printf(m, "%llu\n", result);
+ }
+
+ return rc;
+}
+LPROC_SEQ_FOPS_RO(ll_kbytesfree);
+
+static int ll_kbytesavail_seq_show(struct seq_file *m, void *v)
+{
+ struct super_block *sb = (struct super_block *)m->private;
+ struct obd_statfs osfs;
+ int rc;
+
+ LASSERT(sb != NULL);
+ rc = ll_statfs_internal(sb, &osfs,
+ cfs_time_shift_64(-OBD_STATFS_CACHE_SECONDS),
+ OBD_STATFS_NODELAY);
+ if (!rc) {
+ __u32 blk_size = osfs.os_bsize >> 10;
+ __u64 result = osfs.os_bavail;
+
+ while (blk_size >>= 1)
+ result <<= 1;
+
+ seq_printf(m, "%llu\n", result);
+ }
+
+ return rc;
+}
+LPROC_SEQ_FOPS_RO(ll_kbytesavail);
+
+static int ll_filestotal_seq_show(struct seq_file *m, void *v)
+{
+ struct super_block *sb = (struct super_block *)m->private;
+ struct obd_statfs osfs;
+ int rc;
+
+ LASSERT(sb != NULL);
+ rc = ll_statfs_internal(sb, &osfs,
+ cfs_time_shift_64(-OBD_STATFS_CACHE_SECONDS),
+ OBD_STATFS_NODELAY);
+ if (!rc)
+ seq_printf(m, "%llu\n", osfs.os_files);
+
+ return rc;
+}
+LPROC_SEQ_FOPS_RO(ll_filestotal);
+
+static int ll_filesfree_seq_show(struct seq_file *m, void *v)
+{
+ struct super_block *sb = (struct super_block *)m->private;
+ struct obd_statfs osfs;
+ int rc;
+
+ LASSERT(sb != NULL);
+ rc = ll_statfs_internal(sb, &osfs,
+ cfs_time_shift_64(-OBD_STATFS_CACHE_SECONDS),
+ OBD_STATFS_NODELAY);
+ if (!rc)
+ seq_printf(m, "%llu\n", osfs.os_ffree);
+
+ return rc;
+}
+LPROC_SEQ_FOPS_RO(ll_filesfree);
+
+static int ll_client_type_seq_show(struct seq_file *m, void *v)
+{
+ struct ll_sb_info *sbi = ll_s2sbi((struct super_block *)m->private);
+
+ LASSERT(sbi != NULL);
+
+ if (sbi->ll_flags & LL_SBI_RMT_CLIENT)
+ seq_puts(m, "remote client\n");
+ else
+ seq_puts(m, "local client\n");
+
+ return 0;
+}
+LPROC_SEQ_FOPS_RO(ll_client_type);
+
+static int ll_fstype_seq_show(struct seq_file *m, void *v)
+{
+ struct super_block *sb = (struct super_block *)m->private;
+
+ LASSERT(sb != NULL);
+ seq_printf(m, "%s\n", sb->s_type->name);
+ return 0;
+}
+LPROC_SEQ_FOPS_RO(ll_fstype);
+
+static int ll_sb_uuid_seq_show(struct seq_file *m, void *v)
+{
+ struct super_block *sb = (struct super_block *)m->private;
+
+ LASSERT(sb != NULL);
+ seq_printf(m, "%s\n", ll_s2sbi(sb)->ll_sb_uuid.uuid);
+ return 0;
+}
+LPROC_SEQ_FOPS_RO(ll_sb_uuid);
+
+static int ll_site_stats_seq_show(struct seq_file *m, void *v)
+{
+ struct super_block *sb = m->private;
+
+ /*
+ * See description of statistical counters in struct cl_site, and
+ * struct lu_site.
+ */
+ return cl_site_stats_print(lu2cl_site(ll_s2sbi(sb)->ll_site), m);
+}
+LPROC_SEQ_FOPS_RO(ll_site_stats);
+
+static int ll_max_readahead_mb_seq_show(struct seq_file *m, void *v)
+{
+ struct super_block *sb = m->private;
+ struct ll_sb_info *sbi = ll_s2sbi(sb);
+ long pages_number;
+ int mult;
+
+ spin_lock(&sbi->ll_lock);
+ pages_number = sbi->ll_ra_info.ra_max_pages;
+ spin_unlock(&sbi->ll_lock);
+
+ mult = 1 << (20 - PAGE_CACHE_SHIFT);
+ return lprocfs_seq_read_frac_helper(m, pages_number, mult);
+}
+
+static ssize_t ll_max_readahead_mb_seq_write(struct file *file,
+ const char __user *buffer,
+ size_t count, loff_t *off)
+{
+ struct super_block *sb = ((struct seq_file *)file->private_data)->private;
+ struct ll_sb_info *sbi = ll_s2sbi(sb);
+ int mult, rc, pages_number;
+
+ mult = 1 << (20 - PAGE_CACHE_SHIFT);
+ rc = lprocfs_write_frac_helper(buffer, count, &pages_number, mult);
+ if (rc)
+ return rc;
+
+ if (pages_number < 0 || pages_number > totalram_pages / 2) {
+ CERROR("can't set file readahead more than %lu MB\n",
+ totalram_pages >> (20 - PAGE_CACHE_SHIFT + 1)); /*1/2 of RAM*/
+ return -ERANGE;
+ }
+
+ spin_lock(&sbi->ll_lock);
+ sbi->ll_ra_info.ra_max_pages = pages_number;
+ spin_unlock(&sbi->ll_lock);
+
+ return count;
+}
+LPROC_SEQ_FOPS(ll_max_readahead_mb);
+
+static int ll_max_readahead_per_file_mb_seq_show(struct seq_file *m, void *v)
+{
+ struct super_block *sb = m->private;
+ struct ll_sb_info *sbi = ll_s2sbi(sb);
+ long pages_number;
+ int mult;
+
+ spin_lock(&sbi->ll_lock);
+ pages_number = sbi->ll_ra_info.ra_max_pages_per_file;
+ spin_unlock(&sbi->ll_lock);
+
+ mult = 1 << (20 - PAGE_CACHE_SHIFT);
+ return lprocfs_seq_read_frac_helper(m, pages_number, mult);
+}
+
+static ssize_t ll_max_readahead_per_file_mb_seq_write(struct file *file,
+ const char __user *buffer,
+ size_t count, loff_t *off)
+{
+ struct super_block *sb = ((struct seq_file *)file->private_data)->private;
+ struct ll_sb_info *sbi = ll_s2sbi(sb);
+ int mult, rc, pages_number;
+
+ mult = 1 << (20 - PAGE_CACHE_SHIFT);
+ rc = lprocfs_write_frac_helper(buffer, count, &pages_number, mult);
+ if (rc)
+ return rc;
+
+ if (pages_number < 0 ||
+ pages_number > sbi->ll_ra_info.ra_max_pages) {
+ CERROR("can't set file readahead more than max_read_ahead_mb %lu MB\n",
+ sbi->ll_ra_info.ra_max_pages);
+ return -ERANGE;
+ }
+
+ spin_lock(&sbi->ll_lock);
+ sbi->ll_ra_info.ra_max_pages_per_file = pages_number;
+ spin_unlock(&sbi->ll_lock);
+
+ return count;
+}
+LPROC_SEQ_FOPS(ll_max_readahead_per_file_mb);
+
+static int ll_max_read_ahead_whole_mb_seq_show(struct seq_file *m, void *unused)
+{
+ struct super_block *sb = m->private;
+ struct ll_sb_info *sbi = ll_s2sbi(sb);
+ long pages_number;
+ int mult;
+
+ spin_lock(&sbi->ll_lock);
+ pages_number = sbi->ll_ra_info.ra_max_read_ahead_whole_pages;
+ spin_unlock(&sbi->ll_lock);
+
+ mult = 1 << (20 - PAGE_CACHE_SHIFT);
+ return lprocfs_seq_read_frac_helper(m, pages_number, mult);
+}
+
+static ssize_t ll_max_read_ahead_whole_mb_seq_write(struct file *file,
+ const char __user *buffer,
+ size_t count, loff_t *off)
+{
+ struct super_block *sb = ((struct seq_file *)file->private_data)->private;
+ struct ll_sb_info *sbi = ll_s2sbi(sb);
+ int mult, rc, pages_number;
+
+ mult = 1 << (20 - PAGE_CACHE_SHIFT);
+ rc = lprocfs_write_frac_helper(buffer, count, &pages_number, mult);
+ if (rc)
+ return rc;
+
+ /* Cap this at the current max readahead window size, the readahead
+ * algorithm does this anyway so it's pointless to set it larger. */
+ if (pages_number < 0 ||
+ pages_number > sbi->ll_ra_info.ra_max_pages_per_file) {
+ CERROR("can't set max_read_ahead_whole_mb more than max_read_ahead_per_file_mb: %lu\n",
+ sbi->ll_ra_info.ra_max_pages_per_file >> (20 - PAGE_CACHE_SHIFT));
+ return -ERANGE;
+ }
+
+ spin_lock(&sbi->ll_lock);
+ sbi->ll_ra_info.ra_max_read_ahead_whole_pages = pages_number;
+ spin_unlock(&sbi->ll_lock);
+
+ return count;
+}
+LPROC_SEQ_FOPS(ll_max_read_ahead_whole_mb);
+
+static int ll_max_cached_mb_seq_show(struct seq_file *m, void *v)
+{
+ struct super_block *sb = m->private;
+ struct ll_sb_info *sbi = ll_s2sbi(sb);
+ struct cl_client_cache *cache = &sbi->ll_cache;
+ int shift = 20 - PAGE_CACHE_SHIFT;
+ int max_cached_mb;
+ int unused_mb;
+
+ max_cached_mb = cache->ccc_lru_max >> shift;
+ unused_mb = atomic_read(&cache->ccc_lru_left) >> shift;
+ seq_printf(m,
+ "users: %d\n"
+ "max_cached_mb: %d\n"
+ "used_mb: %d\n"
+ "unused_mb: %d\n"
+ "reclaim_count: %u\n",
+ atomic_read(&cache->ccc_users),
+ max_cached_mb,
+ max_cached_mb - unused_mb,
+ unused_mb,
+ cache->ccc_lru_shrinkers);
+ return 0;
+}
+
+static ssize_t ll_max_cached_mb_seq_write(struct file *file,
+ const char __user *buffer,
+ size_t count, loff_t *off)
+{
+ struct super_block *sb = ((struct seq_file *)file->private_data)->private;
+ struct ll_sb_info *sbi = ll_s2sbi(sb);
+ struct cl_client_cache *cache = &sbi->ll_cache;
+ int mult, rc, pages_number;
+ int diff = 0;
+ int nrpages = 0;
+ char kernbuf[128];
+
+ if (count >= sizeof(kernbuf))
+ return -EINVAL;
+
+ if (copy_from_user(kernbuf, buffer, count))
+ return -EFAULT;
+ kernbuf[count] = 0;
+
+ mult = 1 << (20 - PAGE_CACHE_SHIFT);
+ buffer += lprocfs_find_named_value(kernbuf, "max_cached_mb:", &count) -
+ kernbuf;
+ rc = lprocfs_write_frac_helper(buffer, count, &pages_number, mult);
+ if (rc)
+ return rc;
+
+ if (pages_number < 0 || pages_number > totalram_pages) {
+ CERROR("%s: can't set max cache more than %lu MB\n",
+ ll_get_fsname(sb, NULL, 0),
+ totalram_pages >> (20 - PAGE_CACHE_SHIFT));
+ return -ERANGE;
+ }
+
+ spin_lock(&sbi->ll_lock);
+ diff = pages_number - cache->ccc_lru_max;
+ spin_unlock(&sbi->ll_lock);
+
+ /* easy - add more LRU slots. */
+ if (diff >= 0) {
+ atomic_add(diff, &cache->ccc_lru_left);
+ rc = 0;
+ goto out;
+ }
+
+ diff = -diff;
+ while (diff > 0) {
+ int tmp;
+
+ /* reduce LRU budget from free slots. */
+ do {
+ int ov, nv;
+
+ ov = atomic_read(&cache->ccc_lru_left);
+ if (ov == 0)
+ break;
+
+ nv = ov > diff ? ov - diff : 0;
+ rc = atomic_cmpxchg(&cache->ccc_lru_left, ov, nv);
+ if (likely(ov == rc)) {
+ diff -= ov - nv;
+ nrpages += ov - nv;
+ break;
+ }
+ } while (1);
+
+ if (diff <= 0)
+ break;
+
+ if (sbi->ll_dt_exp == NULL) { /* being initialized */
+ rc = -ENODEV;
+ break;
+ }
+
+ /* difficult - have to ask OSCs to drop LRU slots. */
+ tmp = diff << 1;
+ rc = obd_set_info_async(NULL, sbi->ll_dt_exp,
+ sizeof(KEY_CACHE_LRU_SHRINK),
+ KEY_CACHE_LRU_SHRINK,
+ sizeof(tmp), &tmp, NULL);
+ if (rc < 0)
+ break;
+ }
+
+out:
+ if (rc >= 0) {
+ spin_lock(&sbi->ll_lock);
+ cache->ccc_lru_max = pages_number;
+ spin_unlock(&sbi->ll_lock);
+ rc = count;
+ } else {
+ atomic_add(nrpages, &cache->ccc_lru_left);
+ }
+ return rc;
+}
+LPROC_SEQ_FOPS(ll_max_cached_mb);
+
+static int ll_checksum_seq_show(struct seq_file *m, void *v)
+{
+ struct super_block *sb = m->private;
+ struct ll_sb_info *sbi = ll_s2sbi(sb);
+
+ seq_printf(m, "%u\n", (sbi->ll_flags & LL_SBI_CHECKSUM) ? 1 : 0);
+ return 0;
+}
+
+static ssize_t ll_checksum_seq_write(struct file *file,
+ const char __user *buffer,
+ size_t count, loff_t *off)
+{
+ struct super_block *sb = ((struct seq_file *)file->private_data)->private;
+ struct ll_sb_info *sbi = ll_s2sbi(sb);
+ int val, rc;
+
+ if (!sbi->ll_dt_exp)
+ /* Not set up yet */
+ return -EAGAIN;
+
+ rc = lprocfs_write_helper(buffer, count, &val);
+ if (rc)
+ return rc;
+ if (val)
+ sbi->ll_flags |= LL_SBI_CHECKSUM;
+ else
+ sbi->ll_flags &= ~LL_SBI_CHECKSUM;
+
+ rc = obd_set_info_async(NULL, sbi->ll_dt_exp, sizeof(KEY_CHECKSUM),
+ KEY_CHECKSUM, sizeof(val), &val, NULL);
+ if (rc)
+ CWARN("Failed to set OSC checksum flags: %d\n", rc);
+
+ return count;
+}
+LPROC_SEQ_FOPS(ll_checksum);
+
+static int ll_max_rw_chunk_seq_show(struct seq_file *m, void *v)
+{
+ struct super_block *sb = m->private;
+
+ seq_printf(m, "%lu\n", ll_s2sbi(sb)->ll_max_rw_chunk);
+ return 0;
+}
+
+static ssize_t ll_max_rw_chunk_seq_write(struct file *file,
+ const char __user *buffer,
+ size_t count, loff_t *off)
+{
+ struct super_block *sb = ((struct seq_file *)file->private_data)->private;
+ int rc, val;
+
+ rc = lprocfs_write_helper(buffer, count, &val);
+ if (rc)
+ return rc;
+ ll_s2sbi(sb)->ll_max_rw_chunk = val;
+ return count;
+}
+LPROC_SEQ_FOPS(ll_max_rw_chunk);
+
+static int ll_rd_track_id(struct seq_file *m, enum stats_track_type type)
+{
+ struct super_block *sb = m->private;
+
+ if (ll_s2sbi(sb)->ll_stats_track_type == type)
+ seq_printf(m, "%d\n", ll_s2sbi(sb)->ll_stats_track_id);
+ else if (ll_s2sbi(sb)->ll_stats_track_type == STATS_TRACK_ALL)
+ seq_puts(m, "0 (all)\n");
+ else
+ seq_puts(m, "untracked\n");
+
+ return 0;
+}
+
+static int ll_wr_track_id(const char __user *buffer, unsigned long count,
+ void *data, enum stats_track_type type)
+{
+ struct super_block *sb = data;
+ int rc, pid;
+
+ rc = lprocfs_write_helper(buffer, count, &pid);
+ if (rc)
+ return rc;
+ ll_s2sbi(sb)->ll_stats_track_id = pid;
+ if (pid == 0)
+ ll_s2sbi(sb)->ll_stats_track_type = STATS_TRACK_ALL;
+ else
+ ll_s2sbi(sb)->ll_stats_track_type = type;
+ lprocfs_clear_stats(ll_s2sbi(sb)->ll_stats);
+ return count;
+}
+
+static int ll_track_pid_seq_show(struct seq_file *m, void *v)
+{
+ return ll_rd_track_id(m, STATS_TRACK_PID);
+}
+
+static ssize_t ll_track_pid_seq_write(struct file *file,
+ const char __user *buffer,
+ size_t count, loff_t *off)
+{
+ struct seq_file *seq = file->private_data;
+ return ll_wr_track_id(buffer, count, seq->private, STATS_TRACK_PID);
+}
+LPROC_SEQ_FOPS(ll_track_pid);
+
+static int ll_track_ppid_seq_show(struct seq_file *m, void *v)
+{
+ return ll_rd_track_id(m, STATS_TRACK_PPID);
+}
+
+static ssize_t ll_track_ppid_seq_write(struct file *file,
+ const char __user *buffer,
+ size_t count, loff_t *off)
+{
+ struct seq_file *seq = file->private_data;
+ return ll_wr_track_id(buffer, count, seq->private, STATS_TRACK_PPID);
+}
+LPROC_SEQ_FOPS(ll_track_ppid);
+
+static int ll_track_gid_seq_show(struct seq_file *m, void *v)
+{
+ return ll_rd_track_id(m, STATS_TRACK_GID);
+}
+
+static ssize_t ll_track_gid_seq_write(struct file *file,
+ const char __user *buffer,
+ size_t count, loff_t *off)
+{
+ struct seq_file *seq = file->private_data;
+ return ll_wr_track_id(buffer, count, seq->private, STATS_TRACK_GID);
+}
+LPROC_SEQ_FOPS(ll_track_gid);
+
+static int ll_statahead_max_seq_show(struct seq_file *m, void *v)
+{
+ struct super_block *sb = m->private;
+ struct ll_sb_info *sbi = ll_s2sbi(sb);
+
+ seq_printf(m, "%u\n", sbi->ll_sa_max);
+ return 0;
+}
+
+static ssize_t ll_statahead_max_seq_write(struct file *file,
+ const char __user *buffer,
+ size_t count, loff_t *off)
+{
+ struct super_block *sb = ((struct seq_file *)file->private_data)->private;
+ struct ll_sb_info *sbi = ll_s2sbi(sb);
+ int val, rc;
+
+ rc = lprocfs_write_helper(buffer, count, &val);
+ if (rc)
+ return rc;
+
+ if (val >= 0 && val <= LL_SA_RPC_MAX)
+ sbi->ll_sa_max = val;
+ else
+ CERROR("Bad statahead_max value %d. Valid values are in the range [0, %d]\n",
+ val, LL_SA_RPC_MAX);
+
+ return count;
+}
+LPROC_SEQ_FOPS(ll_statahead_max);
+
+static int ll_statahead_agl_seq_show(struct seq_file *m, void *v)
+{
+ struct super_block *sb = m->private;
+ struct ll_sb_info *sbi = ll_s2sbi(sb);
+
+ seq_printf(m, "%u\n", sbi->ll_flags & LL_SBI_AGL_ENABLED ? 1 : 0);
+ return 0;
+}
+
+static ssize_t ll_statahead_agl_seq_write(struct file *file,
+ const char __user *buffer,
+ size_t count, loff_t *off)
+{
+ struct super_block *sb = ((struct seq_file *)file->private_data)->private;
+ struct ll_sb_info *sbi = ll_s2sbi(sb);
+ int val, rc;
+
+ rc = lprocfs_write_helper(buffer, count, &val);
+ if (rc)
+ return rc;
+
+ if (val)
+ sbi->ll_flags |= LL_SBI_AGL_ENABLED;
+ else
+ sbi->ll_flags &= ~LL_SBI_AGL_ENABLED;
+
+ return count;
+}
+LPROC_SEQ_FOPS(ll_statahead_agl);
+
+static int ll_statahead_stats_seq_show(struct seq_file *m, void *v)
+{
+ struct super_block *sb = m->private;
+ struct ll_sb_info *sbi = ll_s2sbi(sb);
+
+ seq_printf(m,
+ "statahead total: %u\n"
+ "statahead wrong: %u\n"
+ "agl total: %u\n",
+ atomic_read(&sbi->ll_sa_total),
+ atomic_read(&sbi->ll_sa_wrong),
+ atomic_read(&sbi->ll_agl_total));
+ return 0;
+}
+LPROC_SEQ_FOPS_RO(ll_statahead_stats);
+
+static int ll_lazystatfs_seq_show(struct seq_file *m, void *v)
+{
+ struct super_block *sb = m->private;
+ struct ll_sb_info *sbi = ll_s2sbi(sb);
+
+ seq_printf(m, "%u\n", sbi->ll_flags & LL_SBI_LAZYSTATFS ? 1 : 0);
+ return 0;
+}
+
+static ssize_t ll_lazystatfs_seq_write(struct file *file,
+ const char __user *buffer,
+ size_t count, loff_t *off)
+{
+ struct super_block *sb = ((struct seq_file *)file->private_data)->private;
+ struct ll_sb_info *sbi = ll_s2sbi(sb);
+ int val, rc;
+
+ rc = lprocfs_write_helper(buffer, count, &val);
+ if (rc)
+ return rc;
+
+ if (val)
+ sbi->ll_flags |= LL_SBI_LAZYSTATFS;
+ else
+ sbi->ll_flags &= ~LL_SBI_LAZYSTATFS;
+
+ return count;
+}
+LPROC_SEQ_FOPS(ll_lazystatfs);
+
+static int ll_max_easize_seq_show(struct seq_file *m, void *v)
+{
+ struct super_block *sb = m->private;
+ struct ll_sb_info *sbi = ll_s2sbi(sb);
+ unsigned int ealen;
+ int rc;
+
+ rc = ll_get_max_mdsize(sbi, &ealen);
+ if (rc)
+ return rc;
+
+ seq_printf(m, "%u\n", ealen);
+ return 0;
+}
+LPROC_SEQ_FOPS_RO(ll_max_easize);
+
+static int ll_default_easize_seq_show(struct seq_file *m, void *v)
+{
+ struct super_block *sb = m->private;
+ struct ll_sb_info *sbi = ll_s2sbi(sb);
+ unsigned int ealen;
+ int rc;
+
+ rc = ll_get_default_mdsize(sbi, &ealen);
+ if (rc)
+ return rc;
+
+ seq_printf(m, "%u\n", ealen);
+ return 0;
+}
+LPROC_SEQ_FOPS_RO(ll_default_easize);
+
+static int ll_max_cookiesize_seq_show(struct seq_file *m, void *v)
+{
+ struct super_block *sb = m->private;
+ struct ll_sb_info *sbi = ll_s2sbi(sb);
+ unsigned int cookielen;
+ int rc;
+
+ rc = ll_get_max_cookiesize(sbi, &cookielen);
+ if (rc)
+ return rc;
+
+ seq_printf(m, "%u\n", cookielen);
+ return 0;
+}
+LPROC_SEQ_FOPS_RO(ll_max_cookiesize);
+
+static int ll_default_cookiesize_seq_show(struct seq_file *m, void *v)
+{
+ struct super_block *sb = m->private;
+ struct ll_sb_info *sbi = ll_s2sbi(sb);
+ unsigned int cookielen;
+ int rc;
+
+ rc = ll_get_default_cookiesize(sbi, &cookielen);
+ if (rc)
+ return rc;
+
+ seq_printf(m, "%u\n", cookielen);
+ return 0;
+}
+LPROC_SEQ_FOPS_RO(ll_default_cookiesize);
+
+static int ll_sbi_flags_seq_show(struct seq_file *m, void *v)
+{
+ const char *str[] = LL_SBI_FLAGS;
+ struct super_block *sb = m->private;
+ int flags = ll_s2sbi(sb)->ll_flags;
+ int i = 0;
+
+ while (flags != 0) {
+ if (ARRAY_SIZE(str) <= i) {
+ CERROR("%s: Revise array LL_SBI_FLAGS to match sbi flags please.\n",
+ ll_get_fsname(sb, NULL, 0));
+ return -EINVAL;
+ }
+
+ if (flags & 0x1)
+ seq_printf(m, "%s ", str[i]);
+ flags >>= 1;
+ ++i;
+ }
+ seq_printf(m, "\b\n");
+ return 0;
+}
+LPROC_SEQ_FOPS_RO(ll_sbi_flags);
+
+static int ll_xattr_cache_seq_show(struct seq_file *m, void *v)
+{
+ struct super_block *sb = m->private;
+ struct ll_sb_info *sbi = ll_s2sbi(sb);
+
+ seq_printf(m, "%u\n", sbi->ll_xattr_cache_enabled);
+
+ return 0;
+}
+
+static ssize_t ll_xattr_cache_seq_write(struct file *file,
+ const char __user *buffer,
+ size_t count, loff_t *off)
+{
+ struct seq_file *seq = file->private_data;
+ struct super_block *sb = seq->private;
+ struct ll_sb_info *sbi = ll_s2sbi(sb);
+ int val, rc;
+
+ rc = lprocfs_write_helper(buffer, count, &val);
+ if (rc)
+ return rc;
+
+ if (val != 0 && val != 1)
+ return -ERANGE;
+
+ if (val == 1 && !(sbi->ll_flags & LL_SBI_XATTR_CACHE))
+ return -ENOTSUPP;
+
+ sbi->ll_xattr_cache_enabled = val;
+
+ return count;
+}
+LPROC_SEQ_FOPS(ll_xattr_cache);
+
+static struct lprocfs_vars lprocfs_llite_obd_vars[] = {
+ { "uuid", &ll_sb_uuid_fops, NULL, 0 },
+ /* { "mntpt_path", ll_rd_path, 0, 0 }, */
+ { "fstype", &ll_fstype_fops, NULL, 0 },
+ { "site", &ll_site_stats_fops, NULL, 0 },
+ { "blocksize", &ll_blksize_fops, NULL, 0 },
+ { "kbytestotal", &ll_kbytestotal_fops, NULL, 0 },
+ { "kbytesfree", &ll_kbytesfree_fops, NULL, 0 },
+ { "kbytesavail", &ll_kbytesavail_fops, NULL, 0 },
+ { "filestotal", &ll_filestotal_fops, NULL, 0 },
+ { "filesfree", &ll_filesfree_fops, NULL, 0 },
+ { "client_type", &ll_client_type_fops, NULL, 0 },
+ /* { "filegroups", lprocfs_rd_filegroups, 0, 0 }, */
+ { "max_read_ahead_mb", &ll_max_readahead_mb_fops, NULL },
+ { "max_read_ahead_per_file_mb", &ll_max_readahead_per_file_mb_fops,
+ NULL },
+ { "max_read_ahead_whole_mb", &ll_max_read_ahead_whole_mb_fops, NULL },
+ { "max_cached_mb", &ll_max_cached_mb_fops, NULL },
+ { "checksum_pages", &ll_checksum_fops, NULL },
+ { "max_rw_chunk", &ll_max_rw_chunk_fops, NULL },
+ { "stats_track_pid", &ll_track_pid_fops, NULL },
+ { "stats_track_ppid", &ll_track_ppid_fops, NULL },
+ { "stats_track_gid", &ll_track_gid_fops, NULL },
+ { "statahead_max", &ll_statahead_max_fops, NULL },
+ { "statahead_agl", &ll_statahead_agl_fops, NULL },
+ { "statahead_stats", &ll_statahead_stats_fops, NULL, 0 },
+ { "lazystatfs", &ll_lazystatfs_fops, NULL },
+ { "max_easize", &ll_max_easize_fops, NULL, 0 },
+ { "default_easize", &ll_default_easize_fops, NULL, 0 },
+ { "max_cookiesize", &ll_max_cookiesize_fops, NULL, 0 },
+ { "default_cookiesize", &ll_default_cookiesize_fops, NULL, 0 },
+ { "sbi_flags", &ll_sbi_flags_fops, NULL, 0 },
+ { "xattr_cache", &ll_xattr_cache_fops, NULL, 0 },
+ { NULL }
+};
+
+#define MAX_STRING_SIZE 128
+
+static const struct llite_file_opcode {
+ __u32 opcode;
+ __u32 type;
+ const char *opname;
+} llite_opcode_table[LPROC_LL_FILE_OPCODES] = {
+ /* file operation */
+ { LPROC_LL_DIRTY_HITS, LPROCFS_TYPE_REGS, "dirty_pages_hits" },
+ { LPROC_LL_DIRTY_MISSES, LPROCFS_TYPE_REGS, "dirty_pages_misses" },
+ { LPROC_LL_READ_BYTES, LPROCFS_CNTR_AVGMINMAX|LPROCFS_TYPE_BYTES,
+ "read_bytes" },
+ { LPROC_LL_WRITE_BYTES, LPROCFS_CNTR_AVGMINMAX|LPROCFS_TYPE_BYTES,
+ "write_bytes" },
+ { LPROC_LL_BRW_READ, LPROCFS_CNTR_AVGMINMAX|LPROCFS_TYPE_PAGES,
+ "brw_read" },
+ { LPROC_LL_BRW_WRITE, LPROCFS_CNTR_AVGMINMAX|LPROCFS_TYPE_PAGES,
+ "brw_write" },
+ { LPROC_LL_OSC_READ, LPROCFS_CNTR_AVGMINMAX|LPROCFS_TYPE_BYTES,
+ "osc_read" },
+ { LPROC_LL_OSC_WRITE, LPROCFS_CNTR_AVGMINMAX|LPROCFS_TYPE_BYTES,
+ "osc_write" },
+ { LPROC_LL_IOCTL, LPROCFS_TYPE_REGS, "ioctl" },
+ { LPROC_LL_OPEN, LPROCFS_TYPE_REGS, "open" },
+ { LPROC_LL_RELEASE, LPROCFS_TYPE_REGS, "close" },
+ { LPROC_LL_MAP, LPROCFS_TYPE_REGS, "mmap" },
+ { LPROC_LL_LLSEEK, LPROCFS_TYPE_REGS, "seek" },
+ { LPROC_LL_FSYNC, LPROCFS_TYPE_REGS, "fsync" },
+ { LPROC_LL_READDIR, LPROCFS_TYPE_REGS, "readdir" },
+ /* inode operation */
+ { LPROC_LL_SETATTR, LPROCFS_TYPE_REGS, "setattr" },
+ { LPROC_LL_TRUNC, LPROCFS_TYPE_REGS, "truncate" },
+ { LPROC_LL_FLOCK, LPROCFS_TYPE_REGS, "flock" },
+ { LPROC_LL_GETATTR, LPROCFS_TYPE_REGS, "getattr" },
+ /* dir inode operation */
+ { LPROC_LL_CREATE, LPROCFS_TYPE_REGS, "create" },
+ { LPROC_LL_LINK, LPROCFS_TYPE_REGS, "link" },
+ { LPROC_LL_UNLINK, LPROCFS_TYPE_REGS, "unlink" },
+ { LPROC_LL_SYMLINK, LPROCFS_TYPE_REGS, "symlink" },
+ { LPROC_LL_MKDIR, LPROCFS_TYPE_REGS, "mkdir" },
+ { LPROC_LL_RMDIR, LPROCFS_TYPE_REGS, "rmdir" },
+ { LPROC_LL_MKNOD, LPROCFS_TYPE_REGS, "mknod" },
+ { LPROC_LL_RENAME, LPROCFS_TYPE_REGS, "rename" },
+ /* special inode operation */
+ { LPROC_LL_STAFS, LPROCFS_TYPE_REGS, "statfs" },
+ { LPROC_LL_ALLOC_INODE, LPROCFS_TYPE_REGS, "alloc_inode" },
+ { LPROC_LL_SETXATTR, LPROCFS_TYPE_REGS, "setxattr" },
+ { LPROC_LL_GETXATTR, LPROCFS_TYPE_REGS, "getxattr" },
+ { LPROC_LL_GETXATTR_HITS, LPROCFS_TYPE_REGS, "getxattr_hits" },
+ { LPROC_LL_LISTXATTR, LPROCFS_TYPE_REGS, "listxattr" },
+ { LPROC_LL_REMOVEXATTR, LPROCFS_TYPE_REGS, "removexattr" },
+ { LPROC_LL_INODE_PERM, LPROCFS_TYPE_REGS, "inode_permission" },
+};
+
+void ll_stats_ops_tally(struct ll_sb_info *sbi, int op, int count)
+{
+ if (!sbi->ll_stats)
+ return;
+ if (sbi->ll_stats_track_type == STATS_TRACK_ALL)
+ lprocfs_counter_add(sbi->ll_stats, op, count);
+ else if (sbi->ll_stats_track_type == STATS_TRACK_PID &&
+ sbi->ll_stats_track_id == current->pid)
+ lprocfs_counter_add(sbi->ll_stats, op, count);
+ else if (sbi->ll_stats_track_type == STATS_TRACK_PPID &&
+ sbi->ll_stats_track_id == current->real_parent->pid)
+ lprocfs_counter_add(sbi->ll_stats, op, count);
+ else if (sbi->ll_stats_track_type == STATS_TRACK_GID &&
+ sbi->ll_stats_track_id ==
+ from_kgid(&init_user_ns, current_gid()))
+ lprocfs_counter_add(sbi->ll_stats, op, count);
+}
+EXPORT_SYMBOL(ll_stats_ops_tally);
+
+static const char *ra_stat_string[] = {
+ [RA_STAT_HIT] = "hits",
+ [RA_STAT_MISS] = "misses",
+ [RA_STAT_DISTANT_READPAGE] = "readpage not consecutive",
+ [RA_STAT_MISS_IN_WINDOW] = "miss inside window",
+ [RA_STAT_FAILED_GRAB_PAGE] = "failed grab_cache_page",
+ [RA_STAT_FAILED_MATCH] = "failed lock match",
+ [RA_STAT_DISCARDED] = "read but discarded",
+ [RA_STAT_ZERO_LEN] = "zero length file",
+ [RA_STAT_ZERO_WINDOW] = "zero size window",
+ [RA_STAT_EOF] = "read-ahead to EOF",
+ [RA_STAT_MAX_IN_FLIGHT] = "hit max r-a issue",
+ [RA_STAT_WRONG_GRAB_PAGE] = "wrong page from grab_cache_page",
+};
+
+LPROC_SEQ_FOPS_RO_TYPE(llite, name);
+LPROC_SEQ_FOPS_RO_TYPE(llite, uuid);
+
+int lprocfs_register_mountpoint(struct proc_dir_entry *parent,
+ struct super_block *sb, char *osc, char *mdc)
+{
+ struct lprocfs_vars lvars[2];
+ struct lustre_sb_info *lsi = s2lsi(sb);
+ struct ll_sb_info *sbi = ll_s2sbi(sb);
+ struct obd_device *obd;
+ struct proc_dir_entry *dir;
+ char name[MAX_STRING_SIZE + 1], *ptr;
+ int err, id, len, rc;
+
+ memset(lvars, 0, sizeof(lvars));
+
+ name[MAX_STRING_SIZE] = '\0';
+ lvars[0].name = name;
+
+ LASSERT(sbi != NULL);
+ LASSERT(mdc != NULL);
+ LASSERT(osc != NULL);
+
+ /* Get fsname */
+ len = strlen(lsi->lsi_lmd->lmd_profile);
+ ptr = strrchr(lsi->lsi_lmd->lmd_profile, '-');
+ if (ptr && (strcmp(ptr, "-client") == 0))
+ len -= 7;
+
+ /* Mount info */
+ snprintf(name, MAX_STRING_SIZE, "%.*s-%p", len,
+ lsi->lsi_lmd->lmd_profile, sb);
+
+ sbi->ll_proc_root = lprocfs_register(name, parent, NULL, NULL);
+ if (IS_ERR(sbi->ll_proc_root)) {
+ err = PTR_ERR(sbi->ll_proc_root);
+ sbi->ll_proc_root = NULL;
+ return err;
+ }
+
+ rc = lprocfs_seq_create(sbi->ll_proc_root, "dump_page_cache", 0444,
+ &vvp_dump_pgcache_file_ops, sbi);
+ if (rc)
+ CWARN("Error adding the dump_page_cache file\n");
+
+ rc = lprocfs_seq_create(sbi->ll_proc_root, "extents_stats", 0644,
+ &ll_rw_extents_stats_fops, sbi);
+ if (rc)
+ CWARN("Error adding the extent_stats file\n");
+
+ rc = lprocfs_seq_create(sbi->ll_proc_root, "extents_stats_per_process",
+ 0644, &ll_rw_extents_stats_pp_fops, sbi);
+ if (rc)
+ CWARN("Error adding the extents_stats_per_process file\n");
+
+ rc = lprocfs_seq_create(sbi->ll_proc_root, "offset_stats", 0644,
+ &ll_rw_offset_stats_fops, sbi);
+ if (rc)
+ CWARN("Error adding the offset_stats file\n");
+
+ /* File operations stats */
+ sbi->ll_stats = lprocfs_alloc_stats(LPROC_LL_FILE_OPCODES,
+ LPROCFS_STATS_FLAG_NONE);
+ if (sbi->ll_stats == NULL) {
+ err = -ENOMEM;
+ goto out;
+ }
+ /* do counter init */
+ for (id = 0; id < LPROC_LL_FILE_OPCODES; id++) {
+ __u32 type = llite_opcode_table[id].type;
+ void *ptr = NULL;
+ if (type & LPROCFS_TYPE_REGS)
+ ptr = "regs";
+ else if (type & LPROCFS_TYPE_BYTES)
+ ptr = "bytes";
+ else if (type & LPROCFS_TYPE_PAGES)
+ ptr = "pages";
+ lprocfs_counter_init(sbi->ll_stats,
+ llite_opcode_table[id].opcode,
+ (type & LPROCFS_CNTR_AVGMINMAX),
+ llite_opcode_table[id].opname, ptr);
+ }
+ err = lprocfs_register_stats(sbi->ll_proc_root, "stats", sbi->ll_stats);
+ if (err)
+ goto out;
+
+ sbi->ll_ra_stats = lprocfs_alloc_stats(ARRAY_SIZE(ra_stat_string),
+ LPROCFS_STATS_FLAG_NONE);
+ if (sbi->ll_ra_stats == NULL) {
+ err = -ENOMEM;
+ goto out;
+ }
+
+ for (id = 0; id < ARRAY_SIZE(ra_stat_string); id++)
+ lprocfs_counter_init(sbi->ll_ra_stats, id, 0,
+ ra_stat_string[id], "pages");
+ err = lprocfs_register_stats(sbi->ll_proc_root, "read_ahead_stats",
+ sbi->ll_ra_stats);
+ if (err)
+ goto out;
+
+
+ err = lprocfs_add_vars(sbi->ll_proc_root, lprocfs_llite_obd_vars, sb);
+ if (err)
+ goto out;
+
+ /* MDC info */
+ obd = class_name2obd(mdc);
+
+ LASSERT(obd != NULL);
+ LASSERT(obd->obd_magic == OBD_DEVICE_MAGIC);
+ LASSERT(obd->obd_type->typ_name != NULL);
+
+ dir = proc_mkdir(obd->obd_type->typ_name, sbi->ll_proc_root);
+ if (dir == NULL) {
+ err = -ENOMEM;
+ goto out;
+ }
+
+ snprintf(name, MAX_STRING_SIZE, "common_name");
+ lvars[0].fops = &llite_name_fops;
+ err = lprocfs_add_vars(dir, lvars, obd);
+ if (err)
+ goto out;
+
+ snprintf(name, MAX_STRING_SIZE, "uuid");
+ lvars[0].fops = &llite_uuid_fops;
+ err = lprocfs_add_vars(dir, lvars, obd);
+ if (err)
+ goto out;
+
+ /* OSC */
+ obd = class_name2obd(osc);
+
+ LASSERT(obd != NULL);
+ LASSERT(obd->obd_magic == OBD_DEVICE_MAGIC);
+ LASSERT(obd->obd_type->typ_name != NULL);
+
+ dir = proc_mkdir(obd->obd_type->typ_name, sbi->ll_proc_root);
+ if (dir == NULL) {
+ err = -ENOMEM;
+ goto out;
+ }
+
+ snprintf(name, MAX_STRING_SIZE, "common_name");
+ lvars[0].fops = &llite_name_fops;
+ err = lprocfs_add_vars(dir, lvars, obd);
+ if (err)
+ goto out;
+
+ snprintf(name, MAX_STRING_SIZE, "uuid");
+ lvars[0].fops = &llite_uuid_fops;
+ err = lprocfs_add_vars(dir, lvars, obd);
+out:
+ if (err) {
+ lprocfs_remove(&sbi->ll_proc_root);
+ lprocfs_free_stats(&sbi->ll_ra_stats);
+ lprocfs_free_stats(&sbi->ll_stats);
+ }
+ return err;
+}
+
+void lprocfs_unregister_mountpoint(struct ll_sb_info *sbi)
+{
+ if (sbi->ll_proc_root) {
+ lprocfs_remove(&sbi->ll_proc_root);
+ lprocfs_free_stats(&sbi->ll_ra_stats);
+ lprocfs_free_stats(&sbi->ll_stats);
+ }
+}
+#undef MAX_STRING_SIZE
+
+#define pct(a, b) (b ? a * 100 / b : 0)
+
+static void ll_display_extents_info(struct ll_rw_extents_info *io_extents,
+ struct seq_file *seq, int which)
+{
+ unsigned long read_tot = 0, write_tot = 0, read_cum, write_cum;
+ unsigned long start, end, r, w;
+ char *unitp = "KMGTPEZY";
+ int i, units = 10;
+ struct per_process_info *pp_info = &io_extents->pp_extents[which];
+
+ read_cum = 0;
+ write_cum = 0;
+ start = 0;
+
+ for (i = 0; i < LL_HIST_MAX; i++) {
+ read_tot += pp_info->pp_r_hist.oh_buckets[i];
+ write_tot += pp_info->pp_w_hist.oh_buckets[i];
+ }
+
+ for (i = 0; i < LL_HIST_MAX; i++) {
+ r = pp_info->pp_r_hist.oh_buckets[i];
+ w = pp_info->pp_w_hist.oh_buckets[i];
+ read_cum += r;
+ write_cum += w;
+ end = 1 << (i + LL_HIST_START - units);
+ seq_printf(seq, "%4lu%c - %4lu%c%c: %14lu %4lu %4lu | %14lu %4lu %4lu\n",
+ start, *unitp, end, *unitp,
+ (i == LL_HIST_MAX - 1) ? '+' : ' ',
+ r, pct(r, read_tot), pct(read_cum, read_tot),
+ w, pct(w, write_tot), pct(write_cum, write_tot));
+ start = end;
+ if (start == 1<<10) {
+ start = 1;
+ units += 10;
+ unitp++;
+ }
+ if (read_cum == read_tot && write_cum == write_tot)
+ break;
+ }
+}
+
+static int ll_rw_extents_stats_pp_seq_show(struct seq_file *seq, void *v)
+{
+ struct timeval now;
+ struct ll_sb_info *sbi = seq->private;
+ struct ll_rw_extents_info *io_extents = &sbi->ll_rw_extents_info;
+ int k;
+
+ do_gettimeofday(&now);
+
+ if (!sbi->ll_rw_stats_on) {
+ seq_printf(seq, "disabled\n"
+ "write anything in this file to activate, then 0 or \"[D/d]isabled\" to deactivate\n");
+ return 0;
+ }
+ seq_printf(seq, "snapshot_time: %lu.%lu (secs.usecs)\n",
+ now.tv_sec, (unsigned long)now.tv_usec);
+ seq_printf(seq, "%15s %19s | %20s\n", " ", "read", "write");
+ seq_printf(seq, "%13s %14s %4s %4s | %14s %4s %4s\n",
+ "extents", "calls", "%", "cum%",
+ "calls", "%", "cum%");
+ spin_lock(&sbi->ll_pp_extent_lock);
+ for (k = 0; k < LL_PROCESS_HIST_MAX; k++) {
+ if (io_extents->pp_extents[k].pid != 0) {
+ seq_printf(seq, "\nPID: %d\n",
+ io_extents->pp_extents[k].pid);
+ ll_display_extents_info(io_extents, seq, k);
+ }
+ }
+ spin_unlock(&sbi->ll_pp_extent_lock);
+ return 0;
+}
+
+static ssize_t ll_rw_extents_stats_pp_seq_write(struct file *file,
+ const char __user *buf,
+ size_t len,
+ loff_t *off)
+{
+ struct seq_file *seq = file->private_data;
+ struct ll_sb_info *sbi = seq->private;
+ struct ll_rw_extents_info *io_extents = &sbi->ll_rw_extents_info;
+ int i;
+ int value = 1, rc = 0;
+
+ if (len == 0)
+ return -EINVAL;
+
+ rc = lprocfs_write_helper(buf, len, &value);
+ if (rc < 0 && len < 16) {
+ char kernbuf[16];
+
+ if (copy_from_user(kernbuf, buf, len))
+ return -EFAULT;
+ kernbuf[len] = 0;
+
+ if (kernbuf[len - 1] == '\n')
+ kernbuf[len - 1] = 0;
+
+ if (strcmp(kernbuf, "disabled") == 0 ||
+ strcmp(kernbuf, "Disabled") == 0)
+ value = 0;
+ }
+
+ if (value == 0)
+ sbi->ll_rw_stats_on = 0;
+ else
+ sbi->ll_rw_stats_on = 1;
+
+ spin_lock(&sbi->ll_pp_extent_lock);
+ for (i = 0; i < LL_PROCESS_HIST_MAX; i++) {
+ io_extents->pp_extents[i].pid = 0;
+ lprocfs_oh_clear(&io_extents->pp_extents[i].pp_r_hist);
+ lprocfs_oh_clear(&io_extents->pp_extents[i].pp_w_hist);
+ }
+ spin_unlock(&sbi->ll_pp_extent_lock);
+ return len;
+}
+
+LPROC_SEQ_FOPS(ll_rw_extents_stats_pp);
+
+static int ll_rw_extents_stats_seq_show(struct seq_file *seq, void *v)
+{
+ struct timeval now;
+ struct ll_sb_info *sbi = seq->private;
+ struct ll_rw_extents_info *io_extents = &sbi->ll_rw_extents_info;
+
+ do_gettimeofday(&now);
+
+ if (!sbi->ll_rw_stats_on) {
+ seq_printf(seq, "disabled\n"
+ "write anything in this file to activate, then 0 or \"[D/d]isabled\" to deactivate\n");
+ return 0;
+ }
+ seq_printf(seq, "snapshot_time: %lu.%lu (secs.usecs)\n",
+ now.tv_sec, (unsigned long)now.tv_usec);
+
+ seq_printf(seq, "%15s %19s | %20s\n", " ", "read", "write");
+ seq_printf(seq, "%13s %14s %4s %4s | %14s %4s %4s\n",
+ "extents", "calls", "%", "cum%",
+ "calls", "%", "cum%");
+ spin_lock(&sbi->ll_lock);
+ ll_display_extents_info(io_extents, seq, LL_PROCESS_HIST_MAX);
+ spin_unlock(&sbi->ll_lock);
+
+ return 0;
+}
+
+static ssize_t ll_rw_extents_stats_seq_write(struct file *file,
+ const char __user *buf,
+ size_t len, loff_t *off)
+{
+ struct seq_file *seq = file->private_data;
+ struct ll_sb_info *sbi = seq->private;
+ struct ll_rw_extents_info *io_extents = &sbi->ll_rw_extents_info;
+ int i;
+ int value = 1, rc = 0;
+
+ if (len == 0)
+ return -EINVAL;
+
+ rc = lprocfs_write_helper(buf, len, &value);
+ if (rc < 0 && len < 16) {
+ char kernbuf[16];
+
+ if (copy_from_user(kernbuf, buf, len))
+ return -EFAULT;
+ kernbuf[len] = 0;
+
+ if (kernbuf[len - 1] == '\n')
+ kernbuf[len - 1] = 0;
+
+ if (strcmp(kernbuf, "disabled") == 0 ||
+ strcmp(kernbuf, "Disabled") == 0)
+ value = 0;
+ }
+
+ if (value == 0)
+ sbi->ll_rw_stats_on = 0;
+ else
+ sbi->ll_rw_stats_on = 1;
+
+ spin_lock(&sbi->ll_pp_extent_lock);
+ for (i = 0; i <= LL_PROCESS_HIST_MAX; i++) {
+ io_extents->pp_extents[i].pid = 0;
+ lprocfs_oh_clear(&io_extents->pp_extents[i].pp_r_hist);
+ lprocfs_oh_clear(&io_extents->pp_extents[i].pp_w_hist);
+ }
+ spin_unlock(&sbi->ll_pp_extent_lock);
+
+ return len;
+}
+LPROC_SEQ_FOPS(ll_rw_extents_stats);
+
+void ll_rw_stats_tally(struct ll_sb_info *sbi, pid_t pid,
+ struct ll_file_data *file, loff_t pos,
+ size_t count, int rw)
+{
+ int i, cur = -1;
+ struct ll_rw_process_info *process;
+ struct ll_rw_process_info *offset;
+ int *off_count = &sbi->ll_rw_offset_entry_count;
+ int *process_count = &sbi->ll_offset_process_count;
+ struct ll_rw_extents_info *io_extents = &sbi->ll_rw_extents_info;
+
+ if (!sbi->ll_rw_stats_on)
+ return;
+ process = sbi->ll_rw_process_info;
+ offset = sbi->ll_rw_offset_info;
+
+ spin_lock(&sbi->ll_pp_extent_lock);
+ /* Extent statistics */
+ for (i = 0; i < LL_PROCESS_HIST_MAX; i++) {
+ if (io_extents->pp_extents[i].pid == pid) {
+ cur = i;
+ break;
+ }
+ }
+
+ if (cur == -1) {
+ /* new process */
+ sbi->ll_extent_process_count =
+ (sbi->ll_extent_process_count + 1) % LL_PROCESS_HIST_MAX;
+ cur = sbi->ll_extent_process_count;
+ io_extents->pp_extents[cur].pid = pid;
+ lprocfs_oh_clear(&io_extents->pp_extents[cur].pp_r_hist);
+ lprocfs_oh_clear(&io_extents->pp_extents[cur].pp_w_hist);
+ }
+
+ for(i = 0; (count >= (1 << LL_HIST_START << i)) &&
+ (i < (LL_HIST_MAX - 1)); i++);
+ if (rw == 0) {
+ io_extents->pp_extents[cur].pp_r_hist.oh_buckets[i]++;
+ io_extents->pp_extents[LL_PROCESS_HIST_MAX].pp_r_hist.oh_buckets[i]++;
+ } else {
+ io_extents->pp_extents[cur].pp_w_hist.oh_buckets[i]++;
+ io_extents->pp_extents[LL_PROCESS_HIST_MAX].pp_w_hist.oh_buckets[i]++;
+ }
+ spin_unlock(&sbi->ll_pp_extent_lock);
+
+ spin_lock(&sbi->ll_process_lock);
+ /* Offset statistics */
+ for (i = 0; i < LL_PROCESS_HIST_MAX; i++) {
+ if (process[i].rw_pid == pid) {
+ if (process[i].rw_last_file != file) {
+ process[i].rw_range_start = pos;
+ process[i].rw_last_file_pos = pos + count;
+ process[i].rw_smallest_extent = count;
+ process[i].rw_largest_extent = count;
+ process[i].rw_offset = 0;
+ process[i].rw_last_file = file;
+ spin_unlock(&sbi->ll_process_lock);
+ return;
+ }
+ if (process[i].rw_last_file_pos != pos) {
+ *off_count =
+ (*off_count + 1) % LL_OFFSET_HIST_MAX;
+ offset[*off_count].rw_op = process[i].rw_op;
+ offset[*off_count].rw_pid = pid;
+ offset[*off_count].rw_range_start =
+ process[i].rw_range_start;
+ offset[*off_count].rw_range_end =
+ process[i].rw_last_file_pos;
+ offset[*off_count].rw_smallest_extent =
+ process[i].rw_smallest_extent;
+ offset[*off_count].rw_largest_extent =
+ process[i].rw_largest_extent;
+ offset[*off_count].rw_offset =
+ process[i].rw_offset;
+ process[i].rw_op = rw;
+ process[i].rw_range_start = pos;
+ process[i].rw_smallest_extent = count;
+ process[i].rw_largest_extent = count;
+ process[i].rw_offset = pos -
+ process[i].rw_last_file_pos;
+ }
+ if (process[i].rw_smallest_extent > count)
+ process[i].rw_smallest_extent = count;
+ if (process[i].rw_largest_extent < count)
+ process[i].rw_largest_extent = count;
+ process[i].rw_last_file_pos = pos + count;
+ spin_unlock(&sbi->ll_process_lock);
+ return;
+ }
+ }
+ *process_count = (*process_count + 1) % LL_PROCESS_HIST_MAX;
+ process[*process_count].rw_pid = pid;
+ process[*process_count].rw_op = rw;
+ process[*process_count].rw_range_start = pos;
+ process[*process_count].rw_last_file_pos = pos + count;
+ process[*process_count].rw_smallest_extent = count;
+ process[*process_count].rw_largest_extent = count;
+ process[*process_count].rw_offset = 0;
+ process[*process_count].rw_last_file = file;
+ spin_unlock(&sbi->ll_process_lock);
+}
+
+static int ll_rw_offset_stats_seq_show(struct seq_file *seq, void *v)
+{
+ struct timeval now;
+ struct ll_sb_info *sbi = seq->private;
+ struct ll_rw_process_info *offset = sbi->ll_rw_offset_info;
+ struct ll_rw_process_info *process = sbi->ll_rw_process_info;
+ int i;
+
+ do_gettimeofday(&now);
+
+ if (!sbi->ll_rw_stats_on) {
+ seq_printf(seq, "disabled\n"
+ "write anything in this file to activate, then 0 or \"[D/d]isabled\" to deactivate\n");
+ return 0;
+ }
+ spin_lock(&sbi->ll_process_lock);
+
+ seq_printf(seq, "snapshot_time: %lu.%lu (secs.usecs)\n",
+ now.tv_sec, (unsigned long)now.tv_usec);
+ seq_printf(seq, "%3s %10s %14s %14s %17s %17s %14s\n",
+ "R/W", "PID", "RANGE START", "RANGE END",
+ "SMALLEST EXTENT", "LARGEST EXTENT", "OFFSET");
+ /* We stored the discontiguous offsets here; print them first */
+ for (i = 0; i < LL_OFFSET_HIST_MAX; i++) {
+ if (offset[i].rw_pid != 0)
+ seq_printf(seq,
+ "%3c %10d %14Lu %14Lu %17lu %17lu %14Lu",
+ offset[i].rw_op == READ ? 'R' : 'W',
+ offset[i].rw_pid,
+ offset[i].rw_range_start,
+ offset[i].rw_range_end,
+ (unsigned long)offset[i].rw_smallest_extent,
+ (unsigned long)offset[i].rw_largest_extent,
+ offset[i].rw_offset);
+ }
+ /* Then print the current offsets for each process */
+ for (i = 0; i < LL_PROCESS_HIST_MAX; i++) {
+ if (process[i].rw_pid != 0)
+ seq_printf(seq,
+ "%3c %10d %14Lu %14Lu %17lu %17lu %14Lu",
+ process[i].rw_op == READ ? 'R' : 'W',
+ process[i].rw_pid,
+ process[i].rw_range_start,
+ process[i].rw_last_file_pos,
+ (unsigned long)process[i].rw_smallest_extent,
+ (unsigned long)process[i].rw_largest_extent,
+ process[i].rw_offset);
+ }
+ spin_unlock(&sbi->ll_process_lock);
+
+ return 0;
+}
+
+static ssize_t ll_rw_offset_stats_seq_write(struct file *file,
+ const char __user *buf,
+ size_t len, loff_t *off)
+{
+ struct seq_file *seq = file->private_data;
+ struct ll_sb_info *sbi = seq->private;
+ struct ll_rw_process_info *process_info = sbi->ll_rw_process_info;
+ struct ll_rw_process_info *offset_info = sbi->ll_rw_offset_info;
+ int value = 1, rc = 0;
+
+ if (len == 0)
+ return -EINVAL;
+
+ rc = lprocfs_write_helper(buf, len, &value);
+
+ if (rc < 0 && len < 16) {
+ char kernbuf[16];
+
+ if (copy_from_user(kernbuf, buf, len))
+ return -EFAULT;
+ kernbuf[len] = 0;
+
+ if (kernbuf[len - 1] == '\n')
+ kernbuf[len - 1] = 0;
+
+ if (strcmp(kernbuf, "disabled") == 0 ||
+ strcmp(kernbuf, "Disabled") == 0)
+ value = 0;
+ }
+
+ if (value == 0)
+ sbi->ll_rw_stats_on = 0;
+ else
+ sbi->ll_rw_stats_on = 1;
+
+ spin_lock(&sbi->ll_process_lock);
+ sbi->ll_offset_process_count = 0;
+ sbi->ll_rw_offset_entry_count = 0;
+ memset(process_info, 0, sizeof(struct ll_rw_process_info) *
+ LL_PROCESS_HIST_MAX);
+ memset(offset_info, 0, sizeof(struct ll_rw_process_info) *
+ LL_OFFSET_HIST_MAX);
+ spin_unlock(&sbi->ll_process_lock);
+
+ return len;
+}
+
+LPROC_SEQ_FOPS(ll_rw_offset_stats);
+
+void lprocfs_llite_init_vars(struct lprocfs_static_vars *lvars)
+{
+ lvars->module_vars = NULL;
+ lvars->obd_vars = lprocfs_llite_obd_vars;
+}
diff --git a/drivers/staging/lustre/lustre/llite/namei.c b/drivers/staging/lustre/lustre/llite/namei.c
new file mode 100644
index 000000000..5a25dcd10
--- /dev/null
+++ b/drivers/staging/lustre/lustre/llite/namei.c
@@ -0,0 +1,1178 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2002, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2011, 2012, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ */
+
+#include <linux/fs.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/quotaops.h>
+#include <linux/highmem.h>
+#include <linux/pagemap.h>
+#include <linux/security.h>
+
+#define DEBUG_SUBSYSTEM S_LLITE
+
+#include "../include/obd_support.h"
+#include "../include/lustre_fid.h"
+#include "../include/lustre_lite.h"
+#include "../include/lustre_dlm.h"
+#include "../include/lustre_ver.h"
+#include "llite_internal.h"
+
+static int ll_create_it(struct inode *, struct dentry *,
+ int, struct lookup_intent *);
+
+/* called from iget5_locked->find_inode() under inode_hash_lock spinlock */
+static int ll_test_inode(struct inode *inode, void *opaque)
+{
+ struct ll_inode_info *lli = ll_i2info(inode);
+ struct lustre_md *md = opaque;
+
+ if (unlikely(!(md->body->valid & OBD_MD_FLID))) {
+ CERROR("MDS body missing FID\n");
+ return 0;
+ }
+
+ if (!lu_fid_eq(&lli->lli_fid, &md->body->fid1))
+ return 0;
+
+ return 1;
+}
+
+static int ll_set_inode(struct inode *inode, void *opaque)
+{
+ struct ll_inode_info *lli = ll_i2info(inode);
+ struct mdt_body *body = ((struct lustre_md *)opaque)->body;
+
+ if (unlikely(!(body->valid & OBD_MD_FLID))) {
+ CERROR("MDS body missing FID\n");
+ return -EINVAL;
+ }
+
+ lli->lli_fid = body->fid1;
+ if (unlikely(!(body->valid & OBD_MD_FLTYPE))) {
+ CERROR("Can not initialize inode " DFID
+ " without object type: valid = %#llx\n",
+ PFID(&lli->lli_fid), body->valid);
+ return -EINVAL;
+ }
+
+ inode->i_mode = (inode->i_mode & ~S_IFMT) | (body->mode & S_IFMT);
+ if (unlikely(inode->i_mode == 0)) {
+ CERROR("Invalid inode "DFID" type\n", PFID(&lli->lli_fid));
+ return -EINVAL;
+ }
+
+ ll_lli_init(lli);
+
+ return 0;
+}
+
+
+/*
+ * Get an inode by inode number (already instantiated by the intent lookup).
+ * Returns inode or NULL
+ */
+struct inode *ll_iget(struct super_block *sb, ino_t hash,
+ struct lustre_md *md)
+{
+ struct inode *inode;
+
+ LASSERT(hash != 0);
+ inode = iget5_locked(sb, hash, ll_test_inode, ll_set_inode, md);
+
+ if (inode) {
+ if (inode->i_state & I_NEW) {
+ int rc = 0;
+
+ ll_read_inode2(inode, md);
+ if (S_ISREG(inode->i_mode) &&
+ ll_i2info(inode)->lli_clob == NULL) {
+ CDEBUG(D_INODE,
+ "%s: apply lsm %p to inode "DFID".\n",
+ ll_get_fsname(sb, NULL, 0), md->lsm,
+ PFID(ll_inode2fid(inode)));
+ rc = cl_file_inode_init(inode, md);
+ }
+ if (rc != 0) {
+ make_bad_inode(inode);
+ unlock_new_inode(inode);
+ iput(inode);
+ inode = ERR_PTR(rc);
+ } else
+ unlock_new_inode(inode);
+ } else if (!(inode->i_state & (I_FREEING | I_CLEAR)))
+ ll_update_inode(inode, md);
+ CDEBUG(D_VFSTRACE, "got inode: %p for "DFID"\n",
+ inode, PFID(&md->body->fid1));
+ }
+ return inode;
+}
+
+static void ll_invalidate_negative_children(struct inode *dir)
+{
+ struct dentry *dentry, *tmp_subdir;
+ struct ll_d_hlist_node *p;
+
+ ll_lock_dcache(dir);
+ ll_d_hlist_for_each_entry(dentry, p, &dir->i_dentry, d_u.d_alias) {
+ spin_lock(&dentry->d_lock);
+ if (!list_empty(&dentry->d_subdirs)) {
+ struct dentry *child;
+
+ list_for_each_entry_safe(child, tmp_subdir,
+ &dentry->d_subdirs,
+ d_child) {
+ if (d_really_is_negative(child))
+ d_lustre_invalidate(child, 1);
+ }
+ }
+ spin_unlock(&dentry->d_lock);
+ }
+ ll_unlock_dcache(dir);
+}
+
+int ll_md_blocking_ast(struct ldlm_lock *lock, struct ldlm_lock_desc *desc,
+ void *data, int flag)
+{
+ struct lustre_handle lockh;
+ int rc;
+
+ switch (flag) {
+ case LDLM_CB_BLOCKING:
+ ldlm_lock2handle(lock, &lockh);
+ rc = ldlm_cli_cancel(&lockh, LCF_ASYNC);
+ if (rc < 0) {
+ CDEBUG(D_INODE, "ldlm_cli_cancel: rc = %d\n", rc);
+ return rc;
+ }
+ break;
+ case LDLM_CB_CANCELING: {
+ struct inode *inode = ll_inode_from_resource_lock(lock);
+ __u64 bits = lock->l_policy_data.l_inodebits.bits;
+
+ /* Inode is set to lock->l_resource->lr_lvb_inode
+ * for mdc - bug 24555 */
+ LASSERT(lock->l_ast_data == NULL);
+
+ if (inode == NULL)
+ break;
+
+ /* Invalidate all dentries associated with this inode */
+ LASSERT(lock->l_flags & LDLM_FL_CANCELING);
+
+ if (!fid_res_name_eq(ll_inode2fid(inode),
+ &lock->l_resource->lr_name)) {
+ LDLM_ERROR(lock, "data mismatch with object "DFID"(%p)",
+ PFID(ll_inode2fid(inode)), inode);
+ LBUG();
+ }
+
+ if (bits & MDS_INODELOCK_XATTR) {
+ ll_xattr_cache_destroy(inode);
+ bits &= ~MDS_INODELOCK_XATTR;
+ }
+
+ /* For OPEN locks we differentiate between lock modes
+ * LCK_CR, LCK_CW, LCK_PR - bug 22891 */
+ if (bits & MDS_INODELOCK_OPEN)
+ ll_have_md_lock(inode, &bits, lock->l_req_mode);
+
+ if (bits & MDS_INODELOCK_OPEN) {
+ fmode_t fmode;
+
+ switch (lock->l_req_mode) {
+ case LCK_CW:
+ fmode = FMODE_WRITE;
+ break;
+ case LCK_PR:
+ fmode = FMODE_EXEC;
+ break;
+ case LCK_CR:
+ fmode = FMODE_READ;
+ break;
+ default:
+ LDLM_ERROR(lock, "bad lock mode for OPEN lock");
+ LBUG();
+ }
+
+ ll_md_real_close(inode, fmode);
+ }
+
+ if (bits & (MDS_INODELOCK_LOOKUP | MDS_INODELOCK_UPDATE |
+ MDS_INODELOCK_LAYOUT | MDS_INODELOCK_PERM))
+ ll_have_md_lock(inode, &bits, LCK_MINMODE);
+
+ if (bits & MDS_INODELOCK_LAYOUT) {
+ struct cl_object_conf conf = {
+ .coc_opc = OBJECT_CONF_INVALIDATE,
+ .coc_inode = inode,
+ };
+
+ rc = ll_layout_conf(inode, &conf);
+ if (rc < 0)
+ CDEBUG(D_INODE, "cannot invalidate layout of "
+ DFID": rc = %d\n",
+ PFID(ll_inode2fid(inode)), rc);
+ }
+
+ if (bits & MDS_INODELOCK_UPDATE) {
+ struct ll_inode_info *lli = ll_i2info(inode);
+
+ spin_lock(&lli->lli_lock);
+ lli->lli_flags &= ~LLIF_MDS_SIZE_LOCK;
+ spin_unlock(&lli->lli_lock);
+ }
+
+ if ((bits & MDS_INODELOCK_UPDATE) && S_ISDIR(inode->i_mode)) {
+ CDEBUG(D_INODE, "invalidating inode %lu\n",
+ inode->i_ino);
+ truncate_inode_pages(inode->i_mapping, 0);
+ ll_invalidate_negative_children(inode);
+ }
+
+ if ((bits & (MDS_INODELOCK_LOOKUP | MDS_INODELOCK_PERM)) &&
+ inode->i_sb->s_root != NULL &&
+ !is_root_inode(inode))
+ ll_invalidate_aliases(inode);
+
+ iput(inode);
+ break;
+ }
+ default:
+ LBUG();
+ }
+
+ return 0;
+}
+
+__u32 ll_i2suppgid(struct inode *i)
+{
+ if (in_group_p(i->i_gid))
+ return (__u32)from_kgid(&init_user_ns, i->i_gid);
+ else
+ return (__u32)(-1);
+}
+
+/* Pack the required supplementary groups into the supplied groups array.
+ * If we don't need to use the groups from the target inode(s) then we
+ * instead pack one or more groups from the user's supplementary group
+ * array in case it might be useful. Not needed if doing an MDS-side upcall. */
+void ll_i2gids(__u32 *suppgids, struct inode *i1, struct inode *i2)
+{
+#if 0
+ int i;
+#endif
+
+ LASSERT(i1 != NULL);
+ LASSERT(suppgids != NULL);
+
+ suppgids[0] = ll_i2suppgid(i1);
+
+ if (i2)
+ suppgids[1] = ll_i2suppgid(i2);
+ else
+ suppgids[1] = -1;
+
+#if 0
+ for (i = 0; i < current_ngroups; i++) {
+ if (suppgids[0] == -1) {
+ if (current_groups[i] != suppgids[1])
+ suppgids[0] = current_groups[i];
+ continue;
+ }
+ if (suppgids[1] == -1) {
+ if (current_groups[i] != suppgids[0])
+ suppgids[1] = current_groups[i];
+ continue;
+ }
+ break;
+ }
+#endif
+}
+
+/*
+ * try to reuse three types of dentry:
+ * 1. unhashed alias, this one is unhashed by d_invalidate (but it may be valid
+ * by concurrent .revalidate).
+ * 2. INVALID alias (common case for no valid ldlm lock held, but this flag may
+ * be cleared by others calling d_lustre_revalidate).
+ * 3. DISCONNECTED alias.
+ */
+static struct dentry *ll_find_alias(struct inode *inode, struct dentry *dentry)
+{
+ struct dentry *alias, *discon_alias, *invalid_alias;
+ struct ll_d_hlist_node *p;
+
+ if (ll_d_hlist_empty(&inode->i_dentry))
+ return NULL;
+
+ discon_alias = invalid_alias = NULL;
+
+ ll_lock_dcache(inode);
+ ll_d_hlist_for_each_entry(alias, p, &inode->i_dentry, d_u.d_alias) {
+ LASSERT(alias != dentry);
+
+ spin_lock(&alias->d_lock);
+ if (alias->d_flags & DCACHE_DISCONNECTED)
+ /* LASSERT(last_discon == NULL); LU-405, bz 20055 */
+ discon_alias = alias;
+ else if (alias->d_parent == dentry->d_parent &&
+ alias->d_name.hash == dentry->d_name.hash &&
+ alias->d_name.len == dentry->d_name.len &&
+ memcmp(alias->d_name.name, dentry->d_name.name,
+ dentry->d_name.len) == 0)
+ invalid_alias = alias;
+ spin_unlock(&alias->d_lock);
+
+ if (invalid_alias)
+ break;
+ }
+ alias = invalid_alias ?: discon_alias ?: NULL;
+ if (alias) {
+ spin_lock(&alias->d_lock);
+ dget_dlock(alias);
+ spin_unlock(&alias->d_lock);
+ }
+ ll_unlock_dcache(inode);
+
+ return alias;
+}
+
+/*
+ * Similar to d_splice_alias(), but lustre treats invalid alias
+ * similar to DCACHE_DISCONNECTED, and tries to use it anyway.
+ */
+struct dentry *ll_splice_alias(struct inode *inode, struct dentry *de)
+{
+ struct dentry *new;
+ int rc;
+
+ if (inode) {
+ new = ll_find_alias(inode, de);
+ if (new) {
+ rc = ll_d_init(new);
+ if (rc < 0) {
+ dput(new);
+ return ERR_PTR(rc);
+ }
+ d_move(new, de);
+ iput(inode);
+ CDEBUG(D_DENTRY,
+ "Reuse dentry %p inode %p refc %d flags %#x\n",
+ new, d_inode(new), d_count(new), new->d_flags);
+ return new;
+ }
+ }
+ rc = ll_d_init(de);
+ if (rc < 0)
+ return ERR_PTR(rc);
+ d_add(de, inode);
+ CDEBUG(D_DENTRY, "Add dentry %p inode %p refc %d flags %#x\n",
+ de, d_inode(de), d_count(de), de->d_flags);
+ return de;
+}
+
+static int ll_lookup_it_finish(struct ptlrpc_request *request,
+ struct lookup_intent *it,
+ struct inode *parent, struct dentry **de)
+{
+ struct inode *inode = NULL;
+ __u64 bits = 0;
+ int rc;
+
+ /* NB 1 request reference will be taken away by ll_intent_lock()
+ * when I return */
+ CDEBUG(D_DENTRY, "it %p it_disposition %x\n", it,
+ it->d.lustre.it_disposition);
+ if (!it_disposition(it, DISP_LOOKUP_NEG)) {
+ rc = ll_prep_inode(&inode, request, (*de)->d_sb, it);
+ if (rc)
+ return rc;
+
+ ll_set_lock_data(ll_i2sbi(parent)->ll_md_exp, inode, it, &bits);
+
+ /* We used to query real size from OSTs here, but actually
+ this is not needed. For stat() calls size would be updated
+ from subsequent do_revalidate()->ll_inode_revalidate_it() in
+ 2.4 and
+ vfs_getattr_it->ll_getattr()->ll_inode_revalidate_it() in 2.6
+ Everybody else who needs correct file size would call
+ ll_glimpse_size or some equivalent themselves anyway.
+ Also see bug 7198. */
+ }
+
+ /* Only hash *de if it is unhashed (new dentry).
+ * Atoimc_open may passing hashed dentries for open.
+ */
+ if (d_unhashed(*de)) {
+ struct dentry *alias;
+
+ alias = ll_splice_alias(inode, *de);
+ if (IS_ERR(alias))
+ return PTR_ERR(alias);
+ *de = alias;
+ } else if (!it_disposition(it, DISP_LOOKUP_NEG) &&
+ !it_disposition(it, DISP_OPEN_CREATE)) {
+ /* With DISP_OPEN_CREATE dentry will
+ instantiated in ll_create_it. */
+ LASSERT(d_inode(*de) == NULL);
+ d_instantiate(*de, inode);
+ }
+
+ if (!it_disposition(it, DISP_LOOKUP_NEG)) {
+ /* we have lookup look - unhide dentry */
+ if (bits & MDS_INODELOCK_LOOKUP)
+ d_lustre_revalidate(*de);
+ } else if (!it_disposition(it, DISP_OPEN_CREATE)) {
+ /* If file created on server, don't depend on parent UPDATE
+ * lock to unhide it. It is left hidden and next lookup can
+ * find it in ll_splice_alias.
+ */
+ /* Check that parent has UPDATE lock. */
+ struct lookup_intent parent_it = {
+ .it_op = IT_GETATTR,
+ .d.lustre.it_lock_handle = 0 };
+
+ if (md_revalidate_lock(ll_i2mdexp(parent), &parent_it,
+ &ll_i2info(parent)->lli_fid, NULL)) {
+ d_lustre_revalidate(*de);
+ ll_intent_release(&parent_it);
+ }
+ }
+
+ return 0;
+}
+
+static struct dentry *ll_lookup_it(struct inode *parent, struct dentry *dentry,
+ struct lookup_intent *it, int lookup_flags)
+{
+ struct lookup_intent lookup_it = { .it_op = IT_LOOKUP };
+ struct dentry *save = dentry, *retval;
+ struct ptlrpc_request *req = NULL;
+ struct inode *inode;
+ struct md_op_data *op_data;
+ __u32 opc;
+ int rc;
+
+ if (dentry->d_name.len > ll_i2sbi(parent)->ll_namelen)
+ return ERR_PTR(-ENAMETOOLONG);
+
+ CDEBUG(D_VFSTRACE, "VFS Op:name=%pd,dir=%lu/%u(%p),intent=%s\n",
+ dentry, parent->i_ino,
+ parent->i_generation, parent, LL_IT2STR(it));
+
+ if (d_mountpoint(dentry))
+ CERROR("Tell Peter, lookup on mtpt, it %s\n", LL_IT2STR(it));
+
+ if (it == NULL || it->it_op == IT_GETXATTR)
+ it = &lookup_it;
+
+ if (it->it_op == IT_GETATTR) {
+ rc = ll_statahead_enter(parent, &dentry, 0);
+ if (rc == 1) {
+ if (dentry == save)
+ retval = NULL;
+ else
+ retval = dentry;
+ goto out;
+ }
+ }
+
+ if (it->it_op & IT_CREAT)
+ opc = LUSTRE_OPC_CREATE;
+ else
+ opc = LUSTRE_OPC_ANY;
+
+ op_data = ll_prep_md_op_data(NULL, parent, NULL, dentry->d_name.name,
+ dentry->d_name.len, lookup_flags, opc,
+ NULL);
+ if (IS_ERR(op_data))
+ return (void *)op_data;
+
+ /* enforce umask if acl disabled or MDS doesn't support umask */
+ if (!IS_POSIXACL(parent) || !exp_connect_umask(ll_i2mdexp(parent)))
+ it->it_create_mode &= ~current_umask();
+
+ rc = md_intent_lock(ll_i2mdexp(parent), op_data, NULL, 0, it,
+ lookup_flags, &req, ll_md_blocking_ast, 0);
+ ll_finish_md_op_data(op_data);
+ if (rc < 0) {
+ retval = ERR_PTR(rc);
+ goto out;
+ }
+
+ rc = ll_lookup_it_finish(req, it, parent, &dentry);
+ if (rc != 0) {
+ ll_intent_release(it);
+ retval = ERR_PTR(rc);
+ goto out;
+ }
+
+ inode = d_inode(dentry);
+ if ((it->it_op & IT_OPEN) && inode &&
+ !S_ISREG(inode->i_mode) &&
+ !S_ISDIR(inode->i_mode)) {
+ ll_release_openhandle(inode, it);
+ }
+ ll_lookup_finish_locks(it, inode);
+
+ if (dentry == save)
+ retval = NULL;
+ else
+ retval = dentry;
+ goto out;
+ out:
+ if (req)
+ ptlrpc_req_finished(req);
+ if (it->it_op == IT_GETATTR && (retval == NULL || retval == dentry))
+ ll_statahead_mark(parent, dentry);
+ return retval;
+}
+
+static struct dentry *ll_lookup_nd(struct inode *parent, struct dentry *dentry,
+ unsigned int flags)
+{
+ struct lookup_intent *itp, it = { .it_op = IT_GETATTR };
+ struct dentry *de;
+
+ CDEBUG(D_VFSTRACE, "VFS Op:name=%pd,dir=%lu/%u(%p),flags=%u\n",
+ dentry, parent->i_ino,
+ parent->i_generation, parent, flags);
+
+ /* Optimize away (CREATE && !OPEN). Let .create handle the race. */
+ if ((flags & LOOKUP_CREATE) && !(flags & LOOKUP_OPEN))
+ return NULL;
+
+ if (flags & (LOOKUP_PARENT|LOOKUP_OPEN|LOOKUP_CREATE))
+ itp = NULL;
+ else
+ itp = &it;
+ de = ll_lookup_it(parent, dentry, itp, 0);
+
+ if (itp != NULL)
+ ll_intent_release(itp);
+
+ return de;
+}
+
+/*
+ * For cached negative dentry and new dentry, handle lookup/create/open
+ * together.
+ */
+static int ll_atomic_open(struct inode *dir, struct dentry *dentry,
+ struct file *file, unsigned open_flags,
+ umode_t mode, int *opened)
+{
+ struct lookup_intent *it;
+ struct dentry *de;
+ long long lookup_flags = LOOKUP_OPEN;
+ int rc = 0;
+
+ CDEBUG(D_VFSTRACE,
+ "VFS Op:name=%pd,dir=%lu/%u(%p),file %p,open_flags %x,mode %x opened %d\n",
+ dentry, dir->i_ino,
+ dir->i_generation, dir, file, open_flags, mode, *opened);
+
+ it = kzalloc(sizeof(*it), GFP_NOFS);
+ if (!it)
+ return -ENOMEM;
+
+ it->it_op = IT_OPEN;
+ if (open_flags & O_CREAT) {
+ it->it_op |= IT_CREAT;
+ lookup_flags |= LOOKUP_CREATE;
+ }
+ it->it_create_mode = (mode & S_IALLUGO) | S_IFREG;
+ it->it_flags = (open_flags & ~O_ACCMODE) | OPEN_FMODE(open_flags);
+
+ /* Dentry added to dcache tree in ll_lookup_it */
+ de = ll_lookup_it(dir, dentry, it, lookup_flags);
+ if (IS_ERR(de))
+ rc = PTR_ERR(de);
+ else if (de != NULL)
+ dentry = de;
+
+ if (!rc) {
+ if (it_disposition(it, DISP_OPEN_CREATE)) {
+ /* Dentry instantiated in ll_create_it. */
+ rc = ll_create_it(dir, dentry, mode, it);
+ if (rc) {
+ /* We dget in ll_splice_alias. */
+ if (de != NULL)
+ dput(de);
+ goto out_release;
+ }
+
+ *opened |= FILE_CREATED;
+ }
+ if (d_really_is_positive(dentry) && it_disposition(it, DISP_OPEN_OPEN)) {
+ /* Open dentry. */
+ if (S_ISFIFO(d_inode(dentry)->i_mode)) {
+ /* We cannot call open here as it would
+ * deadlock.
+ */
+ if (it_disposition(it, DISP_ENQ_OPEN_REF))
+ ptlrpc_req_finished(
+ (struct ptlrpc_request *)
+ it->d.lustre.it_data);
+ rc = finish_no_open(file, de);
+ } else {
+ file->private_data = it;
+ rc = finish_open(file, dentry, NULL, opened);
+ /* We dget in ll_splice_alias. finish_open takes
+ * care of dget for fd open.
+ */
+ if (de != NULL)
+ dput(de);
+ }
+ } else {
+ rc = finish_no_open(file, de);
+ }
+ }
+
+out_release:
+ ll_intent_release(it);
+ OBD_FREE(it, sizeof(*it));
+
+ return rc;
+}
+
+
+/* We depend on "mode" being set with the proper file type/umask by now */
+static struct inode *ll_create_node(struct inode *dir, struct lookup_intent *it)
+{
+ struct inode *inode = NULL;
+ struct ptlrpc_request *request = NULL;
+ struct ll_sb_info *sbi = ll_i2sbi(dir);
+ int rc;
+
+ LASSERT(it && it->d.lustre.it_disposition);
+
+ LASSERT(it_disposition(it, DISP_ENQ_CREATE_REF));
+ request = it->d.lustre.it_data;
+ it_clear_disposition(it, DISP_ENQ_CREATE_REF);
+ rc = ll_prep_inode(&inode, request, dir->i_sb, it);
+ if (rc) {
+ inode = ERR_PTR(rc);
+ goto out;
+ }
+
+ LASSERT(ll_d_hlist_empty(&inode->i_dentry));
+
+ /* We asked for a lock on the directory, but were granted a
+ * lock on the inode. Since we finally have an inode pointer,
+ * stuff it in the lock. */
+ CDEBUG(D_DLMTRACE, "setting l_ast_data to inode %p (%lu/%u)\n",
+ inode, inode->i_ino, inode->i_generation);
+ ll_set_lock_data(sbi->ll_md_exp, inode, it, NULL);
+ out:
+ ptlrpc_req_finished(request);
+ return inode;
+}
+
+/*
+ * By the time this is called, we already have created the directory cache
+ * entry for the new file, but it is so far negative - it has no inode.
+ *
+ * We defer creating the OBD object(s) until open, to keep the intent and
+ * non-intent code paths similar, and also because we do not have the MDS
+ * inode number before calling ll_create_node() (which is needed for LOV),
+ * so we would need to do yet another RPC to the MDS to store the LOV EA
+ * data on the MDS. If needed, we would pass the PACKED lmm as data and
+ * lmm_size in datalen (the MDS still has code which will handle that).
+ *
+ * If the create succeeds, we fill in the inode information
+ * with d_instantiate().
+ */
+static int ll_create_it(struct inode *dir, struct dentry *dentry, int mode,
+ struct lookup_intent *it)
+{
+ struct inode *inode;
+ int rc = 0;
+
+ CDEBUG(D_VFSTRACE, "VFS Op:name=%pd,dir=%lu/%u(%p),intent=%s\n",
+ dentry, dir->i_ino,
+ dir->i_generation, dir, LL_IT2STR(it));
+
+ rc = it_open_error(DISP_OPEN_CREATE, it);
+ if (rc)
+ return rc;
+
+ inode = ll_create_node(dir, it);
+ if (IS_ERR(inode))
+ return PTR_ERR(inode);
+
+ d_instantiate(dentry, inode);
+ return 0;
+}
+
+static void ll_update_times(struct ptlrpc_request *request,
+ struct inode *inode)
+{
+ struct mdt_body *body = req_capsule_server_get(&request->rq_pill,
+ &RMF_MDT_BODY);
+
+ LASSERT(body);
+ if (body->valid & OBD_MD_FLMTIME &&
+ body->mtime > LTIME_S(inode->i_mtime)) {
+ CDEBUG(D_INODE, "setting ino %lu mtime from %lu to %llu\n",
+ inode->i_ino, LTIME_S(inode->i_mtime), body->mtime);
+ LTIME_S(inode->i_mtime) = body->mtime;
+ }
+ if (body->valid & OBD_MD_FLCTIME &&
+ body->ctime > LTIME_S(inode->i_ctime))
+ LTIME_S(inode->i_ctime) = body->ctime;
+}
+
+static int ll_new_node(struct inode *dir, struct dentry *dentry,
+ const char *tgt, int mode, int rdev,
+ __u32 opc)
+{
+ struct ptlrpc_request *request = NULL;
+ struct md_op_data *op_data;
+ struct inode *inode = NULL;
+ struct ll_sb_info *sbi = ll_i2sbi(dir);
+ int tgt_len = 0;
+ int err;
+
+ if (unlikely(tgt != NULL))
+ tgt_len = strlen(tgt) + 1;
+
+ op_data = ll_prep_md_op_data(NULL, dir, NULL,
+ dentry->d_name.name,
+ dentry->d_name.len,
+ 0, opc, NULL);
+ if (IS_ERR(op_data)) {
+ err = PTR_ERR(op_data);
+ goto err_exit;
+ }
+
+ err = md_create(sbi->ll_md_exp, op_data, tgt, tgt_len, mode,
+ from_kuid(&init_user_ns, current_fsuid()),
+ from_kgid(&init_user_ns, current_fsgid()),
+ cfs_curproc_cap_pack(), rdev, &request);
+ ll_finish_md_op_data(op_data);
+ if (err)
+ goto err_exit;
+
+ ll_update_times(request, dir);
+
+ err = ll_prep_inode(&inode, request, dir->i_sb, NULL);
+ if (err)
+ goto err_exit;
+
+ d_instantiate(dentry, inode);
+err_exit:
+ ptlrpc_req_finished(request);
+
+ return err;
+}
+
+static int ll_mknod(struct inode *dir, struct dentry *dchild,
+ umode_t mode, dev_t rdev)
+{
+ int err;
+
+ CDEBUG(D_VFSTRACE, "VFS Op:name=%pd,dir=%lu/%u(%p) mode %o dev %x\n",
+ dchild, dir->i_ino, dir->i_generation, dir,
+ mode, old_encode_dev(rdev));
+
+ if (!IS_POSIXACL(dir) || !exp_connect_umask(ll_i2mdexp(dir)))
+ mode &= ~current_umask();
+
+ switch (mode & S_IFMT) {
+ case 0:
+ mode |= S_IFREG; /* for mode = 0 case, fallthrough */
+ case S_IFREG:
+ case S_IFCHR:
+ case S_IFBLK:
+ case S_IFIFO:
+ case S_IFSOCK:
+ err = ll_new_node(dir, dchild, NULL, mode,
+ old_encode_dev(rdev),
+ LUSTRE_OPC_MKNOD);
+ break;
+ case S_IFDIR:
+ err = -EPERM;
+ break;
+ default:
+ err = -EINVAL;
+ }
+
+ if (!err)
+ ll_stats_ops_tally(ll_i2sbi(dir), LPROC_LL_MKNOD, 1);
+
+ return err;
+}
+
+/*
+ * Plain create. Intent create is handled in atomic_open.
+ */
+static int ll_create_nd(struct inode *dir, struct dentry *dentry,
+ umode_t mode, bool want_excl)
+{
+ int rc;
+
+ CDEBUG(D_VFSTRACE, "VFS Op:name=%pd,dir=%lu/%u(%p),flags=%u, excl=%d\n",
+ dentry, dir->i_ino,
+ dir->i_generation, dir, mode, want_excl);
+
+ rc = ll_mknod(dir, dentry, mode, 0);
+
+ ll_stats_ops_tally(ll_i2sbi(dir), LPROC_LL_CREATE, 1);
+
+ CDEBUG(D_VFSTRACE, "VFS Op:name=%pd, unhashed %d\n",
+ dentry, d_unhashed(dentry));
+
+ return rc;
+}
+
+static inline void ll_get_child_fid(struct dentry *child, struct lu_fid *fid)
+{
+ if (d_really_is_positive(child))
+ *fid = *ll_inode2fid(d_inode(child));
+}
+
+/**
+ * Remove dir entry
+ **/
+int ll_rmdir_entry(struct inode *dir, char *name, int namelen)
+{
+ struct ptlrpc_request *request = NULL;
+ struct md_op_data *op_data;
+ int rc;
+
+ CDEBUG(D_VFSTRACE, "VFS Op:name=%.*s,dir=%lu/%u(%p)\n",
+ namelen, name, dir->i_ino, dir->i_generation, dir);
+
+ op_data = ll_prep_md_op_data(NULL, dir, NULL, name, strlen(name),
+ S_IFDIR, LUSTRE_OPC_ANY, NULL);
+ if (IS_ERR(op_data))
+ return PTR_ERR(op_data);
+ op_data->op_cli_flags |= CLI_RM_ENTRY;
+ rc = md_unlink(ll_i2sbi(dir)->ll_md_exp, op_data, &request);
+ ll_finish_md_op_data(op_data);
+ if (rc == 0) {
+ ll_update_times(request, dir);
+ ll_stats_ops_tally(ll_i2sbi(dir), LPROC_LL_RMDIR, 1);
+ }
+
+ ptlrpc_req_finished(request);
+ return rc;
+}
+
+int ll_objects_destroy(struct ptlrpc_request *request, struct inode *dir)
+{
+ struct mdt_body *body;
+ struct lov_mds_md *eadata;
+ struct lov_stripe_md *lsm = NULL;
+ struct obd_trans_info oti = { 0 };
+ struct obdo *oa;
+ struct obd_capa *oc = NULL;
+ int rc;
+
+ /* req is swabbed so this is safe */
+ body = req_capsule_server_get(&request->rq_pill, &RMF_MDT_BODY);
+ if (!(body->valid & OBD_MD_FLEASIZE))
+ return 0;
+
+ if (body->eadatasize == 0) {
+ CERROR("OBD_MD_FLEASIZE set but eadatasize zero\n");
+ rc = -EPROTO;
+ goto out;
+ }
+
+ /* The MDS sent back the EA because we unlinked the last reference
+ * to this file. Use this EA to unlink the objects on the OST.
+ * It's opaque so we don't swab here; we leave it to obd_unpackmd() to
+ * check it is complete and sensible. */
+ eadata = req_capsule_server_sized_get(&request->rq_pill, &RMF_MDT_MD,
+ body->eadatasize);
+ LASSERT(eadata != NULL);
+
+ rc = obd_unpackmd(ll_i2dtexp(dir), &lsm, eadata, body->eadatasize);
+ if (rc < 0) {
+ CERROR("obd_unpackmd: %d\n", rc);
+ goto out;
+ }
+ LASSERT(rc >= sizeof(*lsm));
+
+ OBDO_ALLOC(oa);
+ if (oa == NULL) {
+ rc = -ENOMEM;
+ goto out_free_memmd;
+ }
+
+ oa->o_oi = lsm->lsm_oi;
+ oa->o_mode = body->mode & S_IFMT;
+ oa->o_valid = OBD_MD_FLID | OBD_MD_FLTYPE | OBD_MD_FLGROUP;
+
+ if (body->valid & OBD_MD_FLCOOKIE) {
+ oa->o_valid |= OBD_MD_FLCOOKIE;
+ oti.oti_logcookies =
+ req_capsule_server_sized_get(&request->rq_pill,
+ &RMF_LOGCOOKIES,
+ sizeof(struct llog_cookie) *
+ lsm->lsm_stripe_count);
+ if (oti.oti_logcookies == NULL) {
+ oa->o_valid &= ~OBD_MD_FLCOOKIE;
+ body->valid &= ~OBD_MD_FLCOOKIE;
+ }
+ }
+
+ if (body->valid & OBD_MD_FLOSSCAPA) {
+ rc = md_unpack_capa(ll_i2mdexp(dir), request, &RMF_CAPA2, &oc);
+ if (rc)
+ goto out_free_memmd;
+ }
+
+ rc = obd_destroy(NULL, ll_i2dtexp(dir), oa, lsm, &oti,
+ ll_i2mdexp(dir), oc);
+ capa_put(oc);
+ if (rc)
+ CERROR("obd destroy objid "DOSTID" error %d\n",
+ POSTID(&lsm->lsm_oi), rc);
+out_free_memmd:
+ obd_free_memmd(ll_i2dtexp(dir), &lsm);
+ OBDO_FREE(oa);
+out:
+ return rc;
+}
+
+/* ll_unlink() doesn't update the inode with the new link count.
+ * Instead, ll_ddelete() and ll_d_iput() will update it based upon if there
+ * is any lock existing. They will recycle dentries and inodes based upon locks
+ * too. b=20433 */
+static int ll_unlink(struct inode *dir, struct dentry *dentry)
+{
+ struct ptlrpc_request *request = NULL;
+ struct md_op_data *op_data;
+ int rc;
+
+ CDEBUG(D_VFSTRACE, "VFS Op:name=%pd,dir=%lu/%u(%p)\n",
+ dentry, dir->i_ino, dir->i_generation, dir);
+
+ op_data = ll_prep_md_op_data(NULL, dir, NULL,
+ dentry->d_name.name,
+ dentry->d_name.len,
+ 0, LUSTRE_OPC_ANY, NULL);
+ if (IS_ERR(op_data))
+ return PTR_ERR(op_data);
+
+ ll_get_child_fid(dentry, &op_data->op_fid3);
+ op_data->op_fid2 = op_data->op_fid3;
+ rc = md_unlink(ll_i2sbi(dir)->ll_md_exp, op_data, &request);
+ ll_finish_md_op_data(op_data);
+ if (rc)
+ goto out;
+
+ ll_update_times(request, dir);
+ ll_stats_ops_tally(ll_i2sbi(dir), LPROC_LL_UNLINK, 1);
+
+ rc = ll_objects_destroy(request, dir);
+ out:
+ ptlrpc_req_finished(request);
+ return rc;
+}
+
+static int ll_mkdir(struct inode *dir, struct dentry *dentry, ll_umode_t mode)
+{
+ int err;
+
+ CDEBUG(D_VFSTRACE, "VFS Op:name=%pd,dir=%lu/%u(%p)\n",
+ dentry, dir->i_ino, dir->i_generation, dir);
+
+ if (!IS_POSIXACL(dir) || !exp_connect_umask(ll_i2mdexp(dir)))
+ mode &= ~current_umask();
+ mode = (mode & (S_IRWXUGO|S_ISVTX)) | S_IFDIR;
+ err = ll_new_node(dir, dentry, NULL, mode, 0, LUSTRE_OPC_MKDIR);
+
+ if (!err)
+ ll_stats_ops_tally(ll_i2sbi(dir), LPROC_LL_MKDIR, 1);
+
+ return err;
+}
+
+static int ll_rmdir(struct inode *dir, struct dentry *dentry)
+{
+ struct ptlrpc_request *request = NULL;
+ struct md_op_data *op_data;
+ int rc;
+
+ CDEBUG(D_VFSTRACE, "VFS Op:name=%pd,dir=%lu/%u(%p)\n",
+ dentry, dir->i_ino, dir->i_generation, dir);
+
+ op_data = ll_prep_md_op_data(NULL, dir, NULL,
+ dentry->d_name.name,
+ dentry->d_name.len,
+ S_IFDIR, LUSTRE_OPC_ANY, NULL);
+ if (IS_ERR(op_data))
+ return PTR_ERR(op_data);
+
+ ll_get_child_fid(dentry, &op_data->op_fid3);
+ op_data->op_fid2 = op_data->op_fid3;
+ rc = md_unlink(ll_i2sbi(dir)->ll_md_exp, op_data, &request);
+ ll_finish_md_op_data(op_data);
+ if (rc == 0) {
+ ll_update_times(request, dir);
+ ll_stats_ops_tally(ll_i2sbi(dir), LPROC_LL_RMDIR, 1);
+ }
+
+ ptlrpc_req_finished(request);
+ return rc;
+}
+
+static int ll_symlink(struct inode *dir, struct dentry *dentry,
+ const char *oldname)
+{
+ int err;
+
+ CDEBUG(D_VFSTRACE, "VFS Op:name=%pd,dir=%lu/%u(%p),target=%.*s\n",
+ dentry, dir->i_ino, dir->i_generation,
+ dir, 3000, oldname);
+
+ err = ll_new_node(dir, dentry, oldname, S_IFLNK | S_IRWXUGO,
+ 0, LUSTRE_OPC_SYMLINK);
+
+ if (!err)
+ ll_stats_ops_tally(ll_i2sbi(dir), LPROC_LL_SYMLINK, 1);
+
+ return err;
+}
+
+static int ll_link(struct dentry *old_dentry, struct inode *dir,
+ struct dentry *new_dentry)
+{
+ struct inode *src = d_inode(old_dentry);
+ struct ll_sb_info *sbi = ll_i2sbi(dir);
+ struct ptlrpc_request *request = NULL;
+ struct md_op_data *op_data;
+ int err;
+
+ CDEBUG(D_VFSTRACE,
+ "VFS Op: inode=%lu/%u(%p), dir=%lu/%u(%p), target=%pd\n",
+ src->i_ino, src->i_generation, src, dir->i_ino,
+ dir->i_generation, dir, new_dentry);
+
+ op_data = ll_prep_md_op_data(NULL, src, dir, new_dentry->d_name.name,
+ new_dentry->d_name.len,
+ 0, LUSTRE_OPC_ANY, NULL);
+ if (IS_ERR(op_data))
+ return PTR_ERR(op_data);
+
+ err = md_link(sbi->ll_md_exp, op_data, &request);
+ ll_finish_md_op_data(op_data);
+ if (err)
+ goto out;
+
+ ll_update_times(request, dir);
+ ll_stats_ops_tally(sbi, LPROC_LL_LINK, 1);
+out:
+ ptlrpc_req_finished(request);
+ return err;
+}
+
+static int ll_rename(struct inode *old_dir, struct dentry *old_dentry,
+ struct inode *new_dir, struct dentry *new_dentry)
+{
+ struct ptlrpc_request *request = NULL;
+ struct ll_sb_info *sbi = ll_i2sbi(old_dir);
+ struct md_op_data *op_data;
+ int err;
+
+ CDEBUG(D_VFSTRACE,
+ "VFS Op:oldname=%pd,src_dir=%lu/%u(%p),newname=%pd,tgt_dir=%lu/%u(%p)\n",
+ old_dentry, old_dir->i_ino, old_dir->i_generation, old_dir,
+ new_dentry, new_dir->i_ino, new_dir->i_generation, new_dir);
+
+ op_data = ll_prep_md_op_data(NULL, old_dir, new_dir, NULL, 0, 0,
+ LUSTRE_OPC_ANY, NULL);
+ if (IS_ERR(op_data))
+ return PTR_ERR(op_data);
+
+ ll_get_child_fid(old_dentry, &op_data->op_fid3);
+ ll_get_child_fid(new_dentry, &op_data->op_fid4);
+ err = md_rename(sbi->ll_md_exp, op_data,
+ old_dentry->d_name.name,
+ old_dentry->d_name.len,
+ new_dentry->d_name.name,
+ new_dentry->d_name.len, &request);
+ ll_finish_md_op_data(op_data);
+ if (!err) {
+ ll_update_times(request, old_dir);
+ ll_update_times(request, new_dir);
+ ll_stats_ops_tally(sbi, LPROC_LL_RENAME, 1);
+ err = ll_objects_destroy(request, old_dir);
+ }
+
+ ptlrpc_req_finished(request);
+ if (!err)
+ d_move(old_dentry, new_dentry);
+ return err;
+}
+
+const struct inode_operations ll_dir_inode_operations = {
+ .mknod = ll_mknod,
+ .atomic_open = ll_atomic_open,
+ .lookup = ll_lookup_nd,
+ .create = ll_create_nd,
+ /* We need all these non-raw things for NFSD, to not patch it. */
+ .unlink = ll_unlink,
+ .mkdir = ll_mkdir,
+ .rmdir = ll_rmdir,
+ .symlink = ll_symlink,
+ .link = ll_link,
+ .rename = ll_rename,
+ .setattr = ll_setattr,
+ .getattr = ll_getattr,
+ .permission = ll_inode_permission,
+ .setxattr = ll_setxattr,
+ .getxattr = ll_getxattr,
+ .listxattr = ll_listxattr,
+ .removexattr = ll_removexattr,
+ .get_acl = ll_get_acl,
+};
+
+const struct inode_operations ll_special_inode_operations = {
+ .setattr = ll_setattr,
+ .getattr = ll_getattr,
+ .permission = ll_inode_permission,
+ .setxattr = ll_setxattr,
+ .getxattr = ll_getxattr,
+ .listxattr = ll_listxattr,
+ .removexattr = ll_removexattr,
+ .get_acl = ll_get_acl,
+};
diff --git a/drivers/staging/lustre/lustre/llite/remote_perm.c b/drivers/staging/lustre/lustre/llite/remote_perm.c
new file mode 100644
index 000000000..a58182600
--- /dev/null
+++ b/drivers/staging/lustre/lustre/llite/remote_perm.c
@@ -0,0 +1,331 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2011, 2012, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ *
+ * lustre/llite/remote_perm.c
+ *
+ * Lustre Permission Cache for Remote Client
+ *
+ * Author: Lai Siyao <lsy@clusterfs.com>
+ * Author: Fan Yong <fanyong@clusterfs.com>
+ */
+
+#define DEBUG_SUBSYSTEM S_LLITE
+
+#include <linux/module.h>
+#include <linux/types.h>
+
+#include "../include/lustre_lite.h"
+#include "../include/lustre_ha.h"
+#include "../include/lustre_dlm.h"
+#include "../include/lprocfs_status.h"
+#include "../include/lustre_disk.h"
+#include "../include/lustre_param.h"
+#include "llite_internal.h"
+
+struct kmem_cache *ll_remote_perm_cachep = NULL;
+struct kmem_cache *ll_rmtperm_hash_cachep = NULL;
+
+static inline struct ll_remote_perm *alloc_ll_remote_perm(void)
+{
+ struct ll_remote_perm *lrp;
+
+ OBD_SLAB_ALLOC_PTR_GFP(lrp, ll_remote_perm_cachep, GFP_KERNEL);
+ if (lrp)
+ INIT_HLIST_NODE(&lrp->lrp_list);
+ return lrp;
+}
+
+static inline void free_ll_remote_perm(struct ll_remote_perm *lrp)
+{
+ if (!lrp)
+ return;
+
+ if (!hlist_unhashed(&lrp->lrp_list))
+ hlist_del(&lrp->lrp_list);
+ OBD_SLAB_FREE(lrp, ll_remote_perm_cachep, sizeof(*lrp));
+}
+
+static struct hlist_head *alloc_rmtperm_hash(void)
+{
+ struct hlist_head *hash;
+ int i;
+
+ OBD_SLAB_ALLOC_GFP(hash, ll_rmtperm_hash_cachep,
+ REMOTE_PERM_HASHSIZE * sizeof(*hash),
+ GFP_IOFS);
+ if (!hash)
+ return NULL;
+
+ for (i = 0; i < REMOTE_PERM_HASHSIZE; i++)
+ INIT_HLIST_HEAD(hash + i);
+
+ return hash;
+}
+
+void free_rmtperm_hash(struct hlist_head *hash)
+{
+ int i;
+ struct ll_remote_perm *lrp;
+ struct hlist_node *next;
+
+ if (!hash)
+ return;
+
+ for (i = 0; i < REMOTE_PERM_HASHSIZE; i++)
+ hlist_for_each_entry_safe(lrp, next, hash + i,
+ lrp_list)
+ free_ll_remote_perm(lrp);
+ OBD_SLAB_FREE(hash, ll_rmtperm_hash_cachep,
+ REMOTE_PERM_HASHSIZE * sizeof(*hash));
+}
+
+static inline int remote_perm_hashfunc(uid_t uid)
+{
+ return uid & (REMOTE_PERM_HASHSIZE - 1);
+}
+
+/* NB: setxid permission is not checked here, instead it's done on
+ * MDT when client get remote permission. */
+static int do_check_remote_perm(struct ll_inode_info *lli, int mask)
+{
+ struct hlist_head *head;
+ struct ll_remote_perm *lrp;
+ int found = 0, rc;
+
+ if (!lli->lli_remote_perms)
+ return -ENOENT;
+
+ head = lli->lli_remote_perms +
+ remote_perm_hashfunc(from_kuid(&init_user_ns, current_uid()));
+
+ spin_lock(&lli->lli_lock);
+ hlist_for_each_entry(lrp, head, lrp_list) {
+ if (lrp->lrp_uid != from_kuid(&init_user_ns, current_uid()))
+ continue;
+ if (lrp->lrp_gid != from_kgid(&init_user_ns, current_gid()))
+ continue;
+ if (lrp->lrp_fsuid != from_kuid(&init_user_ns, current_fsuid()))
+ continue;
+ if (lrp->lrp_fsgid != from_kgid(&init_user_ns, current_fsgid()))
+ continue;
+ found = 1;
+ break;
+ }
+
+ if (!found) {
+ rc = -ENOENT;
+ goto out;
+ }
+
+ CDEBUG(D_SEC, "found remote perm: %u/%u/%u/%u - %#x\n",
+ lrp->lrp_uid, lrp->lrp_gid, lrp->lrp_fsuid, lrp->lrp_fsgid,
+ lrp->lrp_access_perm);
+ rc = ((lrp->lrp_access_perm & mask) == mask) ? 0 : -EACCES;
+
+out:
+ spin_unlock(&lli->lli_lock);
+ return rc;
+}
+
+int ll_update_remote_perm(struct inode *inode, struct mdt_remote_perm *perm)
+{
+ struct ll_inode_info *lli = ll_i2info(inode);
+ struct ll_remote_perm *lrp = NULL, *tmp = NULL;
+ struct hlist_head *head, *perm_hash = NULL;
+
+ LASSERT(ll_i2sbi(inode)->ll_flags & LL_SBI_RMT_CLIENT);
+
+#if 0
+ if (perm->rp_uid != current->uid ||
+ perm->rp_gid != current->gid ||
+ perm->rp_fsuid != current->fsuid ||
+ perm->rp_fsgid != current->fsgid) {
+ /* user might setxid in this small period */
+ CDEBUG(D_SEC,
+ "remote perm user %u/%u/%u/%u != current %u/%u/%u/%u\n",
+ perm->rp_uid, perm->rp_gid, perm->rp_fsuid,
+ perm->rp_fsgid, current->uid, current->gid,
+ current->fsuid, current->fsgid);
+ return -EAGAIN;
+ }
+#endif
+
+ if (!lli->lli_remote_perms) {
+ perm_hash = alloc_rmtperm_hash();
+ if (perm_hash == NULL) {
+ CERROR("alloc lli_remote_perms failed!\n");
+ return -ENOMEM;
+ }
+ }
+
+ spin_lock(&lli->lli_lock);
+
+ if (!lli->lli_remote_perms)
+ lli->lli_remote_perms = perm_hash;
+ else
+ free_rmtperm_hash(perm_hash);
+
+ head = lli->lli_remote_perms + remote_perm_hashfunc(perm->rp_uid);
+
+again:
+ hlist_for_each_entry(tmp, head, lrp_list) {
+ if (tmp->lrp_uid != perm->rp_uid)
+ continue;
+ if (tmp->lrp_gid != perm->rp_gid)
+ continue;
+ if (tmp->lrp_fsuid != perm->rp_fsuid)
+ continue;
+ if (tmp->lrp_fsgid != perm->rp_fsgid)
+ continue;
+ free_ll_remote_perm(lrp);
+ lrp = tmp;
+ break;
+ }
+
+ if (!lrp) {
+ spin_unlock(&lli->lli_lock);
+ lrp = alloc_ll_remote_perm();
+ if (!lrp) {
+ CERROR("alloc memory for ll_remote_perm failed!\n");
+ return -ENOMEM;
+ }
+ spin_lock(&lli->lli_lock);
+ goto again;
+ }
+
+ lrp->lrp_access_perm = perm->rp_access_perm;
+ if (lrp != tmp) {
+ lrp->lrp_uid = perm->rp_uid;
+ lrp->lrp_gid = perm->rp_gid;
+ lrp->lrp_fsuid = perm->rp_fsuid;
+ lrp->lrp_fsgid = perm->rp_fsgid;
+ hlist_add_head(&lrp->lrp_list, head);
+ }
+ lli->lli_rmtperm_time = cfs_time_current();
+ spin_unlock(&lli->lli_lock);
+
+ CDEBUG(D_SEC, "new remote perm@%p: %u/%u/%u/%u - %#x\n",
+ lrp, lrp->lrp_uid, lrp->lrp_gid, lrp->lrp_fsuid, lrp->lrp_fsgid,
+ lrp->lrp_access_perm);
+
+ return 0;
+}
+
+int lustre_check_remote_perm(struct inode *inode, int mask)
+{
+ struct ll_inode_info *lli = ll_i2info(inode);
+ struct ll_sb_info *sbi = ll_i2sbi(inode);
+ struct ptlrpc_request *req = NULL;
+ struct mdt_remote_perm *perm;
+ struct obd_capa *oc;
+ unsigned long save;
+ int i = 0, rc;
+
+ do {
+ save = lli->lli_rmtperm_time;
+ rc = do_check_remote_perm(lli, mask);
+ if (!rc || (rc != -ENOENT && i))
+ break;
+
+ might_sleep();
+
+ mutex_lock(&lli->lli_rmtperm_mutex);
+ /* check again */
+ if (save != lli->lli_rmtperm_time) {
+ rc = do_check_remote_perm(lli, mask);
+ if (!rc || (rc != -ENOENT && i)) {
+ mutex_unlock(&lli->lli_rmtperm_mutex);
+ break;
+ }
+ }
+
+ if (i++ > 5) {
+ CERROR("check remote perm falls in dead loop!\n");
+ LBUG();
+ }
+
+ oc = ll_mdscapa_get(inode);
+ rc = md_get_remote_perm(sbi->ll_md_exp, ll_inode2fid(inode), oc,
+ ll_i2suppgid(inode), &req);
+ capa_put(oc);
+ if (rc) {
+ mutex_unlock(&lli->lli_rmtperm_mutex);
+ break;
+ }
+
+ perm = req_capsule_server_swab_get(&req->rq_pill, &RMF_ACL,
+ lustre_swab_mdt_remote_perm);
+ if (unlikely(perm == NULL)) {
+ mutex_unlock(&lli->lli_rmtperm_mutex);
+ rc = -EPROTO;
+ break;
+ }
+
+ rc = ll_update_remote_perm(inode, perm);
+ mutex_unlock(&lli->lli_rmtperm_mutex);
+ if (rc == -ENOMEM)
+ break;
+
+ ptlrpc_req_finished(req);
+ req = NULL;
+ } while (1);
+ ptlrpc_req_finished(req);
+ return rc;
+}
+
+#if 0 /* NB: remote perms can't be freed in ll_mdc_blocking_ast of UPDATE lock,
+ * because it will fail sanity test 48.
+ */
+void ll_free_remote_perms(struct inode *inode)
+{
+ struct ll_inode_info *lli = ll_i2info(inode);
+ struct hlist_head *hash = lli->lli_remote_perms;
+ struct ll_remote_perm *lrp;
+ struct hlist_node *node, *next;
+ int i;
+
+ LASSERT(hash);
+
+ spin_lock(&lli->lli_lock);
+
+ for (i = 0; i < REMOTE_PERM_HASHSIZE; i++) {
+ hlist_for_each_entry_safe(lrp, node, next, hash + i,
+ lrp_list)
+ free_ll_remote_perm(lrp);
+ }
+
+ spin_unlock(&lli->lli_lock);
+}
+#endif
diff --git a/drivers/staging/lustre/lustre/llite/rw.c b/drivers/staging/lustre/lustre/llite/rw.c
new file mode 100644
index 000000000..991d20c50
--- /dev/null
+++ b/drivers/staging/lustre/lustre/llite/rw.c
@@ -0,0 +1,1289 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2002, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2011, 2012, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ *
+ * lustre/llite/rw.c
+ *
+ * Lustre Lite I/O page cache routines shared by different kernel revs
+ */
+
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/string.h>
+#include <linux/stat.h>
+#include <linux/errno.h>
+#include <linux/unistd.h>
+#include <linux/writeback.h>
+#include <linux/uaccess.h>
+
+#include <linux/fs.h>
+#include <linux/pagemap.h>
+/* current_is_kswapd() */
+#include <linux/swap.h>
+
+#define DEBUG_SUBSYSTEM S_LLITE
+
+#include "../include/lustre_lite.h"
+#include "../include/obd_cksum.h"
+#include "llite_internal.h"
+#include "../include/linux/lustre_compat25.h"
+
+/**
+ * Finalizes cl-data before exiting typical address_space operation. Dual to
+ * ll_cl_init().
+ */
+static void ll_cl_fini(struct ll_cl_context *lcc)
+{
+ struct lu_env *env = lcc->lcc_env;
+ struct cl_io *io = lcc->lcc_io;
+ struct cl_page *page = lcc->lcc_page;
+
+ LASSERT(lcc->lcc_cookie == current);
+ LASSERT(env != NULL);
+
+ if (page != NULL) {
+ lu_ref_del(&page->cp_reference, "cl_io", io);
+ cl_page_put(env, page);
+ }
+
+ cl_env_put(env, &lcc->lcc_refcheck);
+}
+
+/**
+ * Initializes common cl-data at the typical address_space operation entry
+ * point.
+ */
+static struct ll_cl_context *ll_cl_init(struct file *file,
+ struct page *vmpage, int create)
+{
+ struct ll_cl_context *lcc;
+ struct lu_env *env;
+ struct cl_io *io;
+ struct cl_object *clob;
+ struct ccc_io *cio;
+
+ int refcheck;
+ int result = 0;
+
+ clob = ll_i2info(vmpage->mapping->host)->lli_clob;
+ LASSERT(clob != NULL);
+
+ env = cl_env_get(&refcheck);
+ if (IS_ERR(env))
+ return ERR_CAST(env);
+
+ lcc = &vvp_env_info(env)->vti_io_ctx;
+ memset(lcc, 0, sizeof(*lcc));
+ lcc->lcc_env = env;
+ lcc->lcc_refcheck = refcheck;
+ lcc->lcc_cookie = current;
+
+ cio = ccc_env_io(env);
+ io = cio->cui_cl.cis_io;
+ if (io == NULL && create) {
+ struct inode *inode = vmpage->mapping->host;
+ loff_t pos;
+
+ if (mutex_trylock(&inode->i_mutex)) {
+ mutex_unlock(&(inode)->i_mutex);
+
+ /* this is too bad. Someone is trying to write the
+ * page w/o holding inode mutex. This means we can
+ * add dirty pages into cache during truncate */
+ CERROR("Proc %s is dirtying page w/o inode lock, this will break truncate\n",
+ current->comm);
+ dump_stack();
+ LBUG();
+ return ERR_PTR(-EIO);
+ }
+
+ /*
+ * Loop-back driver calls ->prepare_write().
+ * methods directly, bypassing file system ->write() operation,
+ * so cl_io has to be created here.
+ */
+ io = ccc_env_thread_io(env);
+ ll_io_init(io, file, 1);
+
+ /* No lock at all for this kind of IO - we can't do it because
+ * we have held page lock, it would cause deadlock.
+ * XXX: This causes poor performance to loop device - One page
+ * per RPC.
+ * In order to get better performance, users should use
+ * lloop driver instead.
+ */
+ io->ci_lockreq = CILR_NEVER;
+
+ pos = vmpage->index << PAGE_CACHE_SHIFT;
+
+ /* Create a temp IO to serve write. */
+ result = cl_io_rw_init(env, io, CIT_WRITE, pos, PAGE_CACHE_SIZE);
+ if (result == 0) {
+ cio->cui_fd = LUSTRE_FPRIVATE(file);
+ cio->cui_iter = NULL;
+ result = cl_io_iter_init(env, io);
+ if (result == 0) {
+ result = cl_io_lock(env, io);
+ if (result == 0)
+ result = cl_io_start(env, io);
+ }
+ } else
+ result = io->ci_result;
+ }
+
+ lcc->lcc_io = io;
+ if (io == NULL)
+ result = -EIO;
+ if (result == 0) {
+ struct cl_page *page;
+
+ LASSERT(io != NULL);
+ LASSERT(io->ci_state == CIS_IO_GOING);
+ LASSERT(cio->cui_fd == LUSTRE_FPRIVATE(file));
+ page = cl_page_find(env, clob, vmpage->index, vmpage,
+ CPT_CACHEABLE);
+ if (!IS_ERR(page)) {
+ lcc->lcc_page = page;
+ lu_ref_add(&page->cp_reference, "cl_io", io);
+ result = 0;
+ } else
+ result = PTR_ERR(page);
+ }
+ if (result) {
+ ll_cl_fini(lcc);
+ lcc = ERR_PTR(result);
+ }
+
+ CDEBUG(D_VFSTRACE, "%lu@"DFID" -> %d %p %p\n",
+ vmpage->index, PFID(lu_object_fid(&clob->co_lu)), result,
+ env, io);
+ return lcc;
+}
+
+static struct ll_cl_context *ll_cl_get(void)
+{
+ struct ll_cl_context *lcc;
+ struct lu_env *env;
+ int refcheck;
+
+ env = cl_env_get(&refcheck);
+ LASSERT(!IS_ERR(env));
+ lcc = &vvp_env_info(env)->vti_io_ctx;
+ LASSERT(env == lcc->lcc_env);
+ LASSERT(current == lcc->lcc_cookie);
+ cl_env_put(env, &refcheck);
+
+ /* env has got in ll_cl_init, so it is still usable. */
+ return lcc;
+}
+
+/**
+ * ->prepare_write() address space operation called by generic_file_write()
+ * for every page during write.
+ */
+int ll_prepare_write(struct file *file, struct page *vmpage, unsigned from,
+ unsigned to)
+{
+ struct ll_cl_context *lcc;
+ int result;
+
+ lcc = ll_cl_init(file, vmpage, 1);
+ if (!IS_ERR(lcc)) {
+ struct lu_env *env = lcc->lcc_env;
+ struct cl_io *io = lcc->lcc_io;
+ struct cl_page *page = lcc->lcc_page;
+
+ cl_page_assume(env, io, page);
+
+ result = cl_io_prepare_write(env, io, page, from, to);
+ if (result == 0) {
+ /*
+ * Add a reference, so that page is not evicted from
+ * the cache until ->commit_write() is called.
+ */
+ cl_page_get(page);
+ lu_ref_add(&page->cp_reference, "prepare_write",
+ current);
+ } else {
+ cl_page_unassume(env, io, page);
+ ll_cl_fini(lcc);
+ }
+ /* returning 0 in prepare assumes commit must be called
+ * afterwards */
+ } else {
+ result = PTR_ERR(lcc);
+ }
+ return result;
+}
+
+int ll_commit_write(struct file *file, struct page *vmpage, unsigned from,
+ unsigned to)
+{
+ struct ll_cl_context *lcc;
+ struct lu_env *env;
+ struct cl_io *io;
+ struct cl_page *page;
+ int result = 0;
+
+ lcc = ll_cl_get();
+ env = lcc->lcc_env;
+ page = lcc->lcc_page;
+ io = lcc->lcc_io;
+
+ LASSERT(cl_page_is_owned(page, io));
+ LASSERT(from <= to);
+ if (from != to) /* handle short write case. */
+ result = cl_io_commit_write(env, io, page, from, to);
+ if (cl_page_is_owned(page, io))
+ cl_page_unassume(env, io, page);
+
+ /*
+ * Release reference acquired by ll_prepare_write().
+ */
+ lu_ref_del(&page->cp_reference, "prepare_write", current);
+ cl_page_put(env, page);
+ ll_cl_fini(lcc);
+ return result;
+}
+
+struct obd_capa *cl_capa_lookup(struct inode *inode, enum cl_req_type crt)
+{
+ __u64 opc;
+
+ opc = crt == CRT_WRITE ? CAPA_OPC_OSS_WRITE : CAPA_OPC_OSS_RW;
+ return ll_osscapa_get(inode, opc);
+}
+
+static void ll_ra_stats_inc_sbi(struct ll_sb_info *sbi, enum ra_stat which);
+
+/**
+ * Get readahead pages from the filesystem readahead pool of the client for a
+ * thread.
+ *
+ * /param sbi superblock for filesystem readahead state ll_ra_info
+ * /param ria per-thread readahead state
+ * /param pages number of pages requested for readahead for the thread.
+ *
+ * WARNING: This algorithm is used to reduce contention on sbi->ll_lock.
+ * It should work well if the ra_max_pages is much greater than the single
+ * file's read-ahead window, and not too many threads contending for
+ * these readahead pages.
+ *
+ * TODO: There may be a 'global sync problem' if many threads are trying
+ * to get an ra budget that is larger than the remaining readahead pages
+ * and reach here at exactly the same time. They will compute /a ret to
+ * consume the remaining pages, but will fail at atomic_add_return() and
+ * get a zero ra window, although there is still ra space remaining. - Jay */
+
+static unsigned long ll_ra_count_get(struct ll_sb_info *sbi,
+ struct ra_io_arg *ria,
+ unsigned long pages)
+{
+ struct ll_ra_info *ra = &sbi->ll_ra_info;
+ long ret;
+
+ /* If read-ahead pages left are less than 1M, do not do read-ahead,
+ * otherwise it will form small read RPC(< 1M), which hurt server
+ * performance a lot. */
+ ret = min(ra->ra_max_pages - atomic_read(&ra->ra_cur_pages), pages);
+ if (ret < 0 || ret < min_t(long, PTLRPC_MAX_BRW_PAGES, pages)) {
+ ret = 0;
+ goto out;
+ }
+
+ /* If the non-strided (ria_pages == 0) readahead window
+ * (ria_start + ret) has grown across an RPC boundary, then trim
+ * readahead size by the amount beyond the RPC so it ends on an
+ * RPC boundary. If the readahead window is already ending on
+ * an RPC boundary (beyond_rpc == 0), or smaller than a full
+ * RPC (beyond_rpc < ret) the readahead size is unchanged.
+ * The (beyond_rpc != 0) check is skipped since the conditional
+ * branch is more expensive than subtracting zero from the result.
+ *
+ * Strided read is left unaligned to avoid small fragments beyond
+ * the RPC boundary from needing an extra read RPC. */
+ if (ria->ria_pages == 0) {
+ long beyond_rpc = (ria->ria_start + ret) % PTLRPC_MAX_BRW_PAGES;
+ if (/* beyond_rpc != 0 && */ beyond_rpc < ret)
+ ret -= beyond_rpc;
+ }
+
+ if (atomic_add_return(ret, &ra->ra_cur_pages) > ra->ra_max_pages) {
+ atomic_sub(ret, &ra->ra_cur_pages);
+ ret = 0;
+ }
+
+out:
+ return ret;
+}
+
+void ll_ra_count_put(struct ll_sb_info *sbi, unsigned long len)
+{
+ struct ll_ra_info *ra = &sbi->ll_ra_info;
+ atomic_sub(len, &ra->ra_cur_pages);
+}
+
+static void ll_ra_stats_inc_sbi(struct ll_sb_info *sbi, enum ra_stat which)
+{
+ LASSERTF(which >= 0 && which < _NR_RA_STAT, "which: %u\n", which);
+ lprocfs_counter_incr(sbi->ll_ra_stats, which);
+}
+
+void ll_ra_stats_inc(struct address_space *mapping, enum ra_stat which)
+{
+ struct ll_sb_info *sbi = ll_i2sbi(mapping->host);
+ ll_ra_stats_inc_sbi(sbi, which);
+}
+
+#define RAS_CDEBUG(ras) \
+ CDEBUG(D_READA, \
+ "lrp %lu cr %lu cp %lu ws %lu wl %lu nra %lu r %lu ri %lu" \
+ "csr %lu sf %lu sp %lu sl %lu \n", \
+ ras->ras_last_readpage, ras->ras_consecutive_requests, \
+ ras->ras_consecutive_pages, ras->ras_window_start, \
+ ras->ras_window_len, ras->ras_next_readahead, \
+ ras->ras_requests, ras->ras_request_index, \
+ ras->ras_consecutive_stride_requests, ras->ras_stride_offset, \
+ ras->ras_stride_pages, ras->ras_stride_length)
+
+static int index_in_window(unsigned long index, unsigned long point,
+ unsigned long before, unsigned long after)
+{
+ unsigned long start = point - before, end = point + after;
+
+ if (start > point)
+ start = 0;
+ if (end < point)
+ end = ~0;
+
+ return start <= index && index <= end;
+}
+
+static struct ll_readahead_state *ll_ras_get(struct file *f)
+{
+ struct ll_file_data *fd;
+
+ fd = LUSTRE_FPRIVATE(f);
+ return &fd->fd_ras;
+}
+
+void ll_ra_read_in(struct file *f, struct ll_ra_read *rar)
+{
+ struct ll_readahead_state *ras;
+
+ ras = ll_ras_get(f);
+
+ spin_lock(&ras->ras_lock);
+ ras->ras_requests++;
+ ras->ras_request_index = 0;
+ ras->ras_consecutive_requests++;
+ rar->lrr_reader = current;
+
+ list_add(&rar->lrr_linkage, &ras->ras_read_beads);
+ spin_unlock(&ras->ras_lock);
+}
+
+void ll_ra_read_ex(struct file *f, struct ll_ra_read *rar)
+{
+ struct ll_readahead_state *ras;
+
+ ras = ll_ras_get(f);
+
+ spin_lock(&ras->ras_lock);
+ list_del_init(&rar->lrr_linkage);
+ spin_unlock(&ras->ras_lock);
+}
+
+static struct ll_ra_read *ll_ra_read_get_locked(struct ll_readahead_state *ras)
+{
+ struct ll_ra_read *scan;
+
+ list_for_each_entry(scan, &ras->ras_read_beads, lrr_linkage) {
+ if (scan->lrr_reader == current)
+ return scan;
+ }
+ return NULL;
+}
+
+struct ll_ra_read *ll_ra_read_get(struct file *f)
+{
+ struct ll_readahead_state *ras;
+ struct ll_ra_read *bead;
+
+ ras = ll_ras_get(f);
+
+ spin_lock(&ras->ras_lock);
+ bead = ll_ra_read_get_locked(ras);
+ spin_unlock(&ras->ras_lock);
+ return bead;
+}
+
+static int cl_read_ahead_page(const struct lu_env *env, struct cl_io *io,
+ struct cl_page_list *queue, struct cl_page *page,
+ struct page *vmpage)
+{
+ struct ccc_page *cp;
+ int rc;
+
+ rc = 0;
+ cl_page_assume(env, io, page);
+ lu_ref_add(&page->cp_reference, "ra", current);
+ cp = cl2ccc_page(cl_page_at(page, &vvp_device_type));
+ if (!cp->cpg_defer_uptodate && !PageUptodate(vmpage)) {
+ rc = cl_page_is_under_lock(env, io, page);
+ if (rc == -EBUSY) {
+ cp->cpg_defer_uptodate = 1;
+ cp->cpg_ra_used = 0;
+ cl_page_list_add(queue, page);
+ rc = 1;
+ } else {
+ cl_page_delete(env, page);
+ rc = -ENOLCK;
+ }
+ } else {
+ /* skip completed pages */
+ cl_page_unassume(env, io, page);
+ }
+ lu_ref_del(&page->cp_reference, "ra", current);
+ cl_page_put(env, page);
+ return rc;
+}
+
+/**
+ * Initiates read-ahead of a page with given index.
+ *
+ * \retval +ve: page was added to \a queue.
+ *
+ * \retval -ENOLCK: there is no extent lock for this part of a file, stop
+ * read-ahead.
+ *
+ * \retval -ve, 0: page wasn't added to \a queue for other reason.
+ */
+static int ll_read_ahead_page(const struct lu_env *env, struct cl_io *io,
+ struct cl_page_list *queue,
+ pgoff_t index, struct address_space *mapping)
+{
+ struct page *vmpage;
+ struct cl_object *clob = ll_i2info(mapping->host)->lli_clob;
+ struct cl_page *page;
+ enum ra_stat which = _NR_RA_STAT; /* keep gcc happy */
+ int rc = 0;
+ const char *msg = NULL;
+
+ vmpage = grab_cache_page_nowait(mapping, index);
+ if (vmpage != NULL) {
+ /* Check if vmpage was truncated or reclaimed */
+ if (vmpage->mapping == mapping) {
+ page = cl_page_find(env, clob, vmpage->index,
+ vmpage, CPT_CACHEABLE);
+ if (!IS_ERR(page)) {
+ rc = cl_read_ahead_page(env, io, queue,
+ page, vmpage);
+ if (rc == -ENOLCK) {
+ which = RA_STAT_FAILED_MATCH;
+ msg = "lock match failed";
+ }
+ } else {
+ which = RA_STAT_FAILED_GRAB_PAGE;
+ msg = "cl_page_find failed";
+ }
+ } else {
+ which = RA_STAT_WRONG_GRAB_PAGE;
+ msg = "g_c_p_n returned invalid page";
+ }
+ if (rc != 1)
+ unlock_page(vmpage);
+ page_cache_release(vmpage);
+ } else {
+ which = RA_STAT_FAILED_GRAB_PAGE;
+ msg = "g_c_p_n failed";
+ }
+ if (msg != NULL) {
+ ll_ra_stats_inc(mapping, which);
+ CDEBUG(D_READA, "%s\n", msg);
+ }
+ return rc;
+}
+
+#define RIA_DEBUG(ria) \
+ CDEBUG(D_READA, "rs %lu re %lu ro %lu rl %lu rp %lu\n", \
+ ria->ria_start, ria->ria_end, ria->ria_stoff, ria->ria_length,\
+ ria->ria_pages)
+
+/* Limit this to the blocksize instead of PTLRPC_BRW_MAX_SIZE, since we don't
+ * know what the actual RPC size is. If this needs to change, it makes more
+ * sense to tune the i_blkbits value for the file based on the OSTs it is
+ * striped over, rather than having a constant value for all files here. */
+
+/* RAS_INCREASE_STEP should be (1UL << (inode->i_blkbits - PAGE_CACHE_SHIFT)).
+ * Temporarily set RAS_INCREASE_STEP to 1MB. After 4MB RPC is enabled
+ * by default, this should be adjusted corresponding with max_read_ahead_mb
+ * and max_read_ahead_per_file_mb otherwise the readahead budget can be used
+ * up quickly which will affect read performance significantly. See LU-2816 */
+#define RAS_INCREASE_STEP(inode) (ONE_MB_BRW_SIZE >> PAGE_CACHE_SHIFT)
+
+static inline int stride_io_mode(struct ll_readahead_state *ras)
+{
+ return ras->ras_consecutive_stride_requests > 1;
+}
+/* The function calculates how much pages will be read in
+ * [off, off + length], in such stride IO area,
+ * stride_offset = st_off, stride_length = st_len,
+ * stride_pages = st_pgs
+ *
+ * |------------------|*****|------------------|*****|------------|*****|....
+ * st_off
+ * |--- st_pgs ---|
+ * |----- st_len -----|
+ *
+ * How many pages it should read in such pattern
+ * |-------------------------------------------------------------|
+ * off
+ * |<------ length ------->|
+ *
+ * = |<----->| + |-------------------------------------| + |---|
+ * start_left st_pgs * i end_left
+ */
+static unsigned long
+stride_pg_count(pgoff_t st_off, unsigned long st_len, unsigned long st_pgs,
+ unsigned long off, unsigned long length)
+{
+ __u64 start = off > st_off ? off - st_off : 0;
+ __u64 end = off + length > st_off ? off + length - st_off : 0;
+ unsigned long start_left = 0;
+ unsigned long end_left = 0;
+ unsigned long pg_count;
+
+ if (st_len == 0 || length == 0 || end == 0)
+ return length;
+
+ start_left = do_div(start, st_len);
+ if (start_left < st_pgs)
+ start_left = st_pgs - start_left;
+ else
+ start_left = 0;
+
+ end_left = do_div(end, st_len);
+ if (end_left > st_pgs)
+ end_left = st_pgs;
+
+ CDEBUG(D_READA, "start %llu, end %llu start_left %lu end_left %lu \n",
+ start, end, start_left, end_left);
+
+ if (start == end)
+ pg_count = end_left - (st_pgs - start_left);
+ else
+ pg_count = start_left + st_pgs * (end - start - 1) + end_left;
+
+ CDEBUG(D_READA, "st_off %lu, st_len %lu st_pgs %lu off %lu length %lu pgcount %lu\n",
+ st_off, st_len, st_pgs, off, length, pg_count);
+
+ return pg_count;
+}
+
+static int ria_page_count(struct ra_io_arg *ria)
+{
+ __u64 length = ria->ria_end >= ria->ria_start ?
+ ria->ria_end - ria->ria_start + 1 : 0;
+
+ return stride_pg_count(ria->ria_stoff, ria->ria_length,
+ ria->ria_pages, ria->ria_start,
+ length);
+}
+
+/*Check whether the index is in the defined ra-window */
+static int ras_inside_ra_window(unsigned long idx, struct ra_io_arg *ria)
+{
+ /* If ria_length == ria_pages, it means non-stride I/O mode,
+ * idx should always inside read-ahead window in this case
+ * For stride I/O mode, just check whether the idx is inside
+ * the ria_pages. */
+ return ria->ria_length == 0 || ria->ria_length == ria->ria_pages ||
+ (idx >= ria->ria_stoff && (idx - ria->ria_stoff) %
+ ria->ria_length < ria->ria_pages);
+}
+
+static int ll_read_ahead_pages(const struct lu_env *env,
+ struct cl_io *io, struct cl_page_list *queue,
+ struct ra_io_arg *ria,
+ unsigned long *reserved_pages,
+ struct address_space *mapping,
+ unsigned long *ra_end)
+{
+ int rc, count = 0, stride_ria;
+ unsigned long page_idx;
+
+ LASSERT(ria != NULL);
+ RIA_DEBUG(ria);
+
+ stride_ria = ria->ria_length > ria->ria_pages && ria->ria_pages > 0;
+ for (page_idx = ria->ria_start; page_idx <= ria->ria_end &&
+ *reserved_pages > 0; page_idx++) {
+ if (ras_inside_ra_window(page_idx, ria)) {
+ /* If the page is inside the read-ahead window*/
+ rc = ll_read_ahead_page(env, io, queue,
+ page_idx, mapping);
+ if (rc == 1) {
+ (*reserved_pages)--;
+ count ++;
+ } else if (rc == -ENOLCK)
+ break;
+ } else if (stride_ria) {
+ /* If it is not in the read-ahead window, and it is
+ * read-ahead mode, then check whether it should skip
+ * the stride gap */
+ pgoff_t offset;
+ /* FIXME: This assertion only is valid when it is for
+ * forward read-ahead, it will be fixed when backward
+ * read-ahead is implemented */
+ LASSERTF(page_idx > ria->ria_stoff, "Invalid page_idx %lu rs %lu re %lu ro %lu rl %lu rp %lu\n",
+ page_idx,
+ ria->ria_start, ria->ria_end, ria->ria_stoff,
+ ria->ria_length, ria->ria_pages);
+ offset = page_idx - ria->ria_stoff;
+ offset = offset % (ria->ria_length);
+ if (offset > ria->ria_pages) {
+ page_idx += ria->ria_length - offset;
+ CDEBUG(D_READA, "i %lu skip %lu \n", page_idx,
+ ria->ria_length - offset);
+ continue;
+ }
+ }
+ }
+ *ra_end = page_idx;
+ return count;
+}
+
+int ll_readahead(const struct lu_env *env, struct cl_io *io,
+ struct ll_readahead_state *ras, struct address_space *mapping,
+ struct cl_page_list *queue, int flags)
+{
+ struct vvp_io *vio = vvp_env_io(env);
+ struct vvp_thread_info *vti = vvp_env_info(env);
+ struct cl_attr *attr = ccc_env_thread_attr(env);
+ unsigned long start = 0, end = 0, reserved;
+ unsigned long ra_end, len;
+ struct inode *inode;
+ struct ll_ra_read *bead;
+ struct ra_io_arg *ria = &vti->vti_ria;
+ struct ll_inode_info *lli;
+ struct cl_object *clob;
+ int ret = 0;
+ __u64 kms;
+
+ inode = mapping->host;
+ lli = ll_i2info(inode);
+ clob = lli->lli_clob;
+
+ memset(ria, 0, sizeof(*ria));
+
+ cl_object_attr_lock(clob);
+ ret = cl_object_attr_get(env, clob, attr);
+ cl_object_attr_unlock(clob);
+
+ if (ret != 0)
+ return ret;
+ kms = attr->cat_kms;
+ if (kms == 0) {
+ ll_ra_stats_inc(mapping, RA_STAT_ZERO_LEN);
+ return 0;
+ }
+
+ spin_lock(&ras->ras_lock);
+ if (vio->cui_ra_window_set)
+ bead = &vio->cui_bead;
+ else
+ bead = NULL;
+
+ /* Enlarge the RA window to encompass the full read */
+ if (bead != NULL && ras->ras_window_start + ras->ras_window_len <
+ bead->lrr_start + bead->lrr_count) {
+ ras->ras_window_len = bead->lrr_start + bead->lrr_count -
+ ras->ras_window_start;
+ }
+ /* Reserve a part of the read-ahead window that we'll be issuing */
+ if (ras->ras_window_len) {
+ start = ras->ras_next_readahead;
+ end = ras->ras_window_start + ras->ras_window_len - 1;
+ }
+ if (end != 0) {
+ unsigned long rpc_boundary;
+ /*
+ * Align RA window to an optimal boundary.
+ *
+ * XXX This would be better to align to cl_max_pages_per_rpc
+ * instead of PTLRPC_MAX_BRW_PAGES, because the RPC size may
+ * be aligned to the RAID stripe size in the future and that
+ * is more important than the RPC size.
+ */
+ /* Note: we only trim the RPC, instead of extending the RPC
+ * to the boundary, so to avoid reading too much pages during
+ * random reading. */
+ rpc_boundary = (end + 1) & (~(PTLRPC_MAX_BRW_PAGES - 1));
+ if (rpc_boundary > 0)
+ rpc_boundary--;
+
+ if (rpc_boundary > start)
+ end = rpc_boundary;
+
+ /* Truncate RA window to end of file */
+ end = min(end, (unsigned long)((kms - 1) >> PAGE_CACHE_SHIFT));
+
+ ras->ras_next_readahead = max(end, end + 1);
+ RAS_CDEBUG(ras);
+ }
+ ria->ria_start = start;
+ ria->ria_end = end;
+ /* If stride I/O mode is detected, get stride window*/
+ if (stride_io_mode(ras)) {
+ ria->ria_stoff = ras->ras_stride_offset;
+ ria->ria_length = ras->ras_stride_length;
+ ria->ria_pages = ras->ras_stride_pages;
+ }
+ spin_unlock(&ras->ras_lock);
+
+ if (end == 0) {
+ ll_ra_stats_inc(mapping, RA_STAT_ZERO_WINDOW);
+ return 0;
+ }
+ len = ria_page_count(ria);
+ if (len == 0)
+ return 0;
+
+ reserved = ll_ra_count_get(ll_i2sbi(inode), ria, len);
+ if (reserved < len)
+ ll_ra_stats_inc(mapping, RA_STAT_MAX_IN_FLIGHT);
+
+ CDEBUG(D_READA, "reserved page %lu ra_cur %d ra_max %lu\n", reserved,
+ atomic_read(&ll_i2sbi(inode)->ll_ra_info.ra_cur_pages),
+ ll_i2sbi(inode)->ll_ra_info.ra_max_pages);
+
+ ret = ll_read_ahead_pages(env, io, queue,
+ ria, &reserved, mapping, &ra_end);
+
+ LASSERTF(reserved >= 0, "reserved %lu\n", reserved);
+ if (reserved != 0)
+ ll_ra_count_put(ll_i2sbi(inode), reserved);
+
+ if (ra_end == end + 1 && ra_end == (kms >> PAGE_CACHE_SHIFT))
+ ll_ra_stats_inc(mapping, RA_STAT_EOF);
+
+ /* if we didn't get to the end of the region we reserved from
+ * the ras we need to go back and update the ras so that the
+ * next read-ahead tries from where we left off. we only do so
+ * if the region we failed to issue read-ahead on is still ahead
+ * of the app and behind the next index to start read-ahead from */
+ CDEBUG(D_READA, "ra_end %lu end %lu stride end %lu \n",
+ ra_end, end, ria->ria_end);
+
+ if (ra_end != end + 1) {
+ spin_lock(&ras->ras_lock);
+ if (ra_end < ras->ras_next_readahead &&
+ index_in_window(ra_end, ras->ras_window_start, 0,
+ ras->ras_window_len)) {
+ ras->ras_next_readahead = ra_end;
+ RAS_CDEBUG(ras);
+ }
+ spin_unlock(&ras->ras_lock);
+ }
+
+ return ret;
+}
+
+static void ras_set_start(struct inode *inode, struct ll_readahead_state *ras,
+ unsigned long index)
+{
+ ras->ras_window_start = index & (~(RAS_INCREASE_STEP(inode) - 1));
+}
+
+/* called with the ras_lock held or from places where it doesn't matter */
+static void ras_reset(struct inode *inode, struct ll_readahead_state *ras,
+ unsigned long index)
+{
+ ras->ras_last_readpage = index;
+ ras->ras_consecutive_requests = 0;
+ ras->ras_consecutive_pages = 0;
+ ras->ras_window_len = 0;
+ ras_set_start(inode, ras, index);
+ ras->ras_next_readahead = max(ras->ras_window_start, index);
+
+ RAS_CDEBUG(ras);
+}
+
+/* called with the ras_lock held or from places where it doesn't matter */
+static void ras_stride_reset(struct ll_readahead_state *ras)
+{
+ ras->ras_consecutive_stride_requests = 0;
+ ras->ras_stride_length = 0;
+ ras->ras_stride_pages = 0;
+ RAS_CDEBUG(ras);
+}
+
+void ll_readahead_init(struct inode *inode, struct ll_readahead_state *ras)
+{
+ spin_lock_init(&ras->ras_lock);
+ ras_reset(inode, ras, 0);
+ ras->ras_requests = 0;
+ INIT_LIST_HEAD(&ras->ras_read_beads);
+}
+
+/*
+ * Check whether the read request is in the stride window.
+ * If it is in the stride window, return 1, otherwise return 0.
+ */
+static int index_in_stride_window(struct ll_readahead_state *ras,
+ unsigned long index)
+{
+ unsigned long stride_gap;
+
+ if (ras->ras_stride_length == 0 || ras->ras_stride_pages == 0 ||
+ ras->ras_stride_pages == ras->ras_stride_length)
+ return 0;
+
+ stride_gap = index - ras->ras_last_readpage - 1;
+
+ /* If it is contiguous read */
+ if (stride_gap == 0)
+ return ras->ras_consecutive_pages + 1 <= ras->ras_stride_pages;
+
+ /* Otherwise check the stride by itself */
+ return (ras->ras_stride_length - ras->ras_stride_pages) == stride_gap &&
+ ras->ras_consecutive_pages == ras->ras_stride_pages;
+}
+
+static void ras_update_stride_detector(struct ll_readahead_state *ras,
+ unsigned long index)
+{
+ unsigned long stride_gap = index - ras->ras_last_readpage - 1;
+
+ if (!stride_io_mode(ras) && (stride_gap != 0 ||
+ ras->ras_consecutive_stride_requests == 0)) {
+ ras->ras_stride_pages = ras->ras_consecutive_pages;
+ ras->ras_stride_length = stride_gap +ras->ras_consecutive_pages;
+ }
+ LASSERT(ras->ras_request_index == 0);
+ LASSERT(ras->ras_consecutive_stride_requests == 0);
+
+ if (index <= ras->ras_last_readpage) {
+ /*Reset stride window for forward read*/
+ ras_stride_reset(ras);
+ return;
+ }
+
+ ras->ras_stride_pages = ras->ras_consecutive_pages;
+ ras->ras_stride_length = stride_gap +ras->ras_consecutive_pages;
+
+ RAS_CDEBUG(ras);
+ return;
+}
+
+static unsigned long
+stride_page_count(struct ll_readahead_state *ras, unsigned long len)
+{
+ return stride_pg_count(ras->ras_stride_offset, ras->ras_stride_length,
+ ras->ras_stride_pages, ras->ras_stride_offset,
+ len);
+}
+
+/* Stride Read-ahead window will be increased inc_len according to
+ * stride I/O pattern */
+static void ras_stride_increase_window(struct ll_readahead_state *ras,
+ struct ll_ra_info *ra,
+ unsigned long inc_len)
+{
+ unsigned long left, step, window_len;
+ unsigned long stride_len;
+
+ LASSERT(ras->ras_stride_length > 0);
+ LASSERTF(ras->ras_window_start + ras->ras_window_len
+ >= ras->ras_stride_offset, "window_start %lu, window_len %lu stride_offset %lu\n",
+ ras->ras_window_start,
+ ras->ras_window_len, ras->ras_stride_offset);
+
+ stride_len = ras->ras_window_start + ras->ras_window_len -
+ ras->ras_stride_offset;
+
+ left = stride_len % ras->ras_stride_length;
+ window_len = ras->ras_window_len - left;
+
+ if (left < ras->ras_stride_pages)
+ left += inc_len;
+ else
+ left = ras->ras_stride_pages + inc_len;
+
+ LASSERT(ras->ras_stride_pages != 0);
+
+ step = left / ras->ras_stride_pages;
+ left %= ras->ras_stride_pages;
+
+ window_len += step * ras->ras_stride_length + left;
+
+ if (stride_page_count(ras, window_len) <= ra->ra_max_pages_per_file)
+ ras->ras_window_len = window_len;
+
+ RAS_CDEBUG(ras);
+}
+
+static void ras_increase_window(struct inode *inode,
+ struct ll_readahead_state *ras,
+ struct ll_ra_info *ra)
+{
+ /* The stretch of ra-window should be aligned with max rpc_size
+ * but current clio architecture does not support retrieve such
+ * information from lower layer. FIXME later
+ */
+ if (stride_io_mode(ras))
+ ras_stride_increase_window(ras, ra, RAS_INCREASE_STEP(inode));
+ else
+ ras->ras_window_len = min(ras->ras_window_len +
+ RAS_INCREASE_STEP(inode),
+ ra->ra_max_pages_per_file);
+}
+
+void ras_update(struct ll_sb_info *sbi, struct inode *inode,
+ struct ll_readahead_state *ras, unsigned long index,
+ unsigned hit)
+{
+ struct ll_ra_info *ra = &sbi->ll_ra_info;
+ int zero = 0, stride_detect = 0, ra_miss = 0;
+
+ spin_lock(&ras->ras_lock);
+
+ ll_ra_stats_inc_sbi(sbi, hit ? RA_STAT_HIT : RA_STAT_MISS);
+
+ /* reset the read-ahead window in two cases. First when the app seeks
+ * or reads to some other part of the file. Secondly if we get a
+ * read-ahead miss that we think we've previously issued. This can
+ * be a symptom of there being so many read-ahead pages that the VM is
+ * reclaiming it before we get to it. */
+ if (!index_in_window(index, ras->ras_last_readpage, 8, 8)) {
+ zero = 1;
+ ll_ra_stats_inc_sbi(sbi, RA_STAT_DISTANT_READPAGE);
+ } else if (!hit && ras->ras_window_len &&
+ index < ras->ras_next_readahead &&
+ index_in_window(index, ras->ras_window_start, 0,
+ ras->ras_window_len)) {
+ ra_miss = 1;
+ ll_ra_stats_inc_sbi(sbi, RA_STAT_MISS_IN_WINDOW);
+ }
+
+ /* On the second access to a file smaller than the tunable
+ * ra_max_read_ahead_whole_pages trigger RA on all pages in the
+ * file up to ra_max_pages_per_file. This is simply a best effort
+ * and only occurs once per open file. Normal RA behavior is reverted
+ * to for subsequent IO. The mmap case does not increment
+ * ras_requests and thus can never trigger this behavior. */
+ if (ras->ras_requests == 2 && !ras->ras_request_index) {
+ __u64 kms_pages;
+
+ kms_pages = (i_size_read(inode) + PAGE_CACHE_SIZE - 1) >>
+ PAGE_CACHE_SHIFT;
+
+ CDEBUG(D_READA, "kmsp %llu mwp %lu mp %lu\n", kms_pages,
+ ra->ra_max_read_ahead_whole_pages, ra->ra_max_pages_per_file);
+
+ if (kms_pages &&
+ kms_pages <= ra->ra_max_read_ahead_whole_pages) {
+ ras->ras_window_start = 0;
+ ras->ras_last_readpage = 0;
+ ras->ras_next_readahead = 0;
+ ras->ras_window_len = min(ra->ra_max_pages_per_file,
+ ra->ra_max_read_ahead_whole_pages);
+ goto out_unlock;
+ }
+ }
+ if (zero) {
+ /* check whether it is in stride I/O mode*/
+ if (!index_in_stride_window(ras, index)) {
+ if (ras->ras_consecutive_stride_requests == 0 &&
+ ras->ras_request_index == 0) {
+ ras_update_stride_detector(ras, index);
+ ras->ras_consecutive_stride_requests++;
+ } else {
+ ras_stride_reset(ras);
+ }
+ ras_reset(inode, ras, index);
+ ras->ras_consecutive_pages++;
+ goto out_unlock;
+ } else {
+ ras->ras_consecutive_pages = 0;
+ ras->ras_consecutive_requests = 0;
+ if (++ras->ras_consecutive_stride_requests > 1)
+ stride_detect = 1;
+ RAS_CDEBUG(ras);
+ }
+ } else {
+ if (ra_miss) {
+ if (index_in_stride_window(ras, index) &&
+ stride_io_mode(ras)) {
+ /*If stride-RA hit cache miss, the stride dector
+ *will not be reset to avoid the overhead of
+ *redetecting read-ahead mode */
+ if (index != ras->ras_last_readpage + 1)
+ ras->ras_consecutive_pages = 0;
+ ras_reset(inode, ras, index);
+ RAS_CDEBUG(ras);
+ } else {
+ /* Reset both stride window and normal RA
+ * window */
+ ras_reset(inode, ras, index);
+ ras->ras_consecutive_pages++;
+ ras_stride_reset(ras);
+ goto out_unlock;
+ }
+ } else if (stride_io_mode(ras)) {
+ /* If this is contiguous read but in stride I/O mode
+ * currently, check whether stride step still is valid,
+ * if invalid, it will reset the stride ra window*/
+ if (!index_in_stride_window(ras, index)) {
+ /* Shrink stride read-ahead window to be zero */
+ ras_stride_reset(ras);
+ ras->ras_window_len = 0;
+ ras->ras_next_readahead = index;
+ }
+ }
+ }
+ ras->ras_consecutive_pages++;
+ ras->ras_last_readpage = index;
+ ras_set_start(inode, ras, index);
+
+ if (stride_io_mode(ras))
+ /* Since stride readahead is sensitive to the offset
+ * of read-ahead, so we use original offset here,
+ * instead of ras_window_start, which is RPC aligned */
+ ras->ras_next_readahead = max(index, ras->ras_next_readahead);
+ else
+ ras->ras_next_readahead = max(ras->ras_window_start,
+ ras->ras_next_readahead);
+ RAS_CDEBUG(ras);
+
+ /* Trigger RA in the mmap case where ras_consecutive_requests
+ * is not incremented and thus can't be used to trigger RA */
+ if (!ras->ras_window_len && ras->ras_consecutive_pages == 4) {
+ ras->ras_window_len = RAS_INCREASE_STEP(inode);
+ goto out_unlock;
+ }
+
+ /* Initially reset the stride window offset to next_readahead*/
+ if (ras->ras_consecutive_stride_requests == 2 && stride_detect) {
+ /**
+ * Once stride IO mode is detected, next_readahead should be
+ * reset to make sure next_readahead > stride offset
+ */
+ ras->ras_next_readahead = max(index, ras->ras_next_readahead);
+ ras->ras_stride_offset = index;
+ ras->ras_window_len = RAS_INCREASE_STEP(inode);
+ }
+
+ /* The initial ras_window_len is set to the request size. To avoid
+ * uselessly reading and discarding pages for random IO the window is
+ * only increased once per consecutive request received. */
+ if ((ras->ras_consecutive_requests > 1 || stride_detect) &&
+ !ras->ras_request_index)
+ ras_increase_window(inode, ras, ra);
+out_unlock:
+ RAS_CDEBUG(ras);
+ ras->ras_request_index++;
+ spin_unlock(&ras->ras_lock);
+ return;
+}
+
+int ll_writepage(struct page *vmpage, struct writeback_control *wbc)
+{
+ struct inode *inode = vmpage->mapping->host;
+ struct ll_inode_info *lli = ll_i2info(inode);
+ struct lu_env *env;
+ struct cl_io *io;
+ struct cl_page *page;
+ struct cl_object *clob;
+ struct cl_env_nest nest;
+ bool redirtied = false;
+ bool unlocked = false;
+ int result;
+
+ LASSERT(PageLocked(vmpage));
+ LASSERT(!PageWriteback(vmpage));
+
+ LASSERT(ll_i2dtexp(inode) != NULL);
+
+ env = cl_env_nested_get(&nest);
+ if (IS_ERR(env)) {
+ result = PTR_ERR(env);
+ goto out;
+ }
+
+ clob = ll_i2info(inode)->lli_clob;
+ LASSERT(clob != NULL);
+
+ io = ccc_env_thread_io(env);
+ io->ci_obj = clob;
+ io->ci_ignore_layout = 1;
+ result = cl_io_init(env, io, CIT_MISC, clob);
+ if (result == 0) {
+ page = cl_page_find(env, clob, vmpage->index,
+ vmpage, CPT_CACHEABLE);
+ if (!IS_ERR(page)) {
+ lu_ref_add(&page->cp_reference, "writepage",
+ current);
+ cl_page_assume(env, io, page);
+ result = cl_page_flush(env, io, page);
+ if (result != 0) {
+ /*
+ * Re-dirty page on error so it retries write,
+ * but not in case when IO has actually
+ * occurred and completed with an error.
+ */
+ if (!PageError(vmpage)) {
+ redirty_page_for_writepage(wbc, vmpage);
+ result = 0;
+ redirtied = true;
+ }
+ }
+ cl_page_disown(env, io, page);
+ unlocked = true;
+ lu_ref_del(&page->cp_reference,
+ "writepage", current);
+ cl_page_put(env, page);
+ } else {
+ result = PTR_ERR(page);
+ }
+ }
+ cl_io_fini(env, io);
+
+ if (redirtied && wbc->sync_mode == WB_SYNC_ALL) {
+ loff_t offset = cl_offset(clob, vmpage->index);
+
+ /* Flush page failed because the extent is being written out.
+ * Wait for the write of extent to be finished to avoid
+ * breaking kernel which assumes ->writepage should mark
+ * PageWriteback or clean the page. */
+ result = cl_sync_file_range(inode, offset,
+ offset + PAGE_CACHE_SIZE - 1,
+ CL_FSYNC_LOCAL, 1);
+ if (result > 0) {
+ /* actually we may have written more than one page.
+ * decreasing this page because the caller will count
+ * it. */
+ wbc->nr_to_write -= result - 1;
+ result = 0;
+ }
+ }
+
+ cl_env_nested_put(&nest, env);
+ goto out;
+
+out:
+ if (result < 0) {
+ if (!lli->lli_async_rc)
+ lli->lli_async_rc = result;
+ SetPageError(vmpage);
+ if (!unlocked)
+ unlock_page(vmpage);
+ }
+ return result;
+}
+
+int ll_writepages(struct address_space *mapping, struct writeback_control *wbc)
+{
+ struct inode *inode = mapping->host;
+ struct ll_sb_info *sbi = ll_i2sbi(inode);
+ loff_t start;
+ loff_t end;
+ enum cl_fsync_mode mode;
+ int range_whole = 0;
+ int result;
+ int ignore_layout = 0;
+
+ if (wbc->range_cyclic) {
+ start = mapping->writeback_index << PAGE_CACHE_SHIFT;
+ end = OBD_OBJECT_EOF;
+ } else {
+ start = wbc->range_start;
+ end = wbc->range_end;
+ if (end == LLONG_MAX) {
+ end = OBD_OBJECT_EOF;
+ range_whole = start == 0;
+ }
+ }
+
+ mode = CL_FSYNC_NONE;
+ if (wbc->sync_mode == WB_SYNC_ALL)
+ mode = CL_FSYNC_LOCAL;
+
+ if (sbi->ll_umounting)
+ /* if the mountpoint is being umounted, all pages have to be
+ * evicted to avoid hitting LBUG when truncate_inode_pages()
+ * is called later on. */
+ ignore_layout = 1;
+ result = cl_sync_file_range(inode, start, end, mode, ignore_layout);
+ if (result > 0) {
+ wbc->nr_to_write -= result;
+ result = 0;
+ }
+
+ if (wbc->range_cyclic || (range_whole && wbc->nr_to_write > 0)) {
+ if (end == OBD_OBJECT_EOF)
+ end = i_size_read(inode);
+ mapping->writeback_index = (end >> PAGE_CACHE_SHIFT) + 1;
+ }
+ return result;
+}
+
+int ll_readpage(struct file *file, struct page *vmpage)
+{
+ struct ll_cl_context *lcc;
+ int result;
+
+ lcc = ll_cl_init(file, vmpage, 0);
+ if (!IS_ERR(lcc)) {
+ struct lu_env *env = lcc->lcc_env;
+ struct cl_io *io = lcc->lcc_io;
+ struct cl_page *page = lcc->lcc_page;
+
+ LASSERT(page->cp_type == CPT_CACHEABLE);
+ if (likely(!PageUptodate(vmpage))) {
+ cl_page_assume(env, io, page);
+ result = cl_io_read_page(env, io, page);
+ } else {
+ /* Page from a non-object file. */
+ unlock_page(vmpage);
+ result = 0;
+ }
+ ll_cl_fini(lcc);
+ } else {
+ unlock_page(vmpage);
+ result = PTR_ERR(lcc);
+ }
+ return result;
+}
diff --git a/drivers/staging/lustre/lustre/llite/rw26.c b/drivers/staging/lustre/lustre/llite/rw26.c
new file mode 100644
index 000000000..c6c824356
--- /dev/null
+++ b/drivers/staging/lustre/lustre/llite/rw26.c
@@ -0,0 +1,553 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2011, 2012, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ *
+ * lustre/lustre/llite/rw26.c
+ *
+ * Lustre Lite I/O page cache routines for the 2.5/2.6 kernel version
+ */
+
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/string.h>
+#include <linux/stat.h>
+#include <linux/errno.h>
+#include <linux/unistd.h>
+#include <linux/uaccess.h>
+
+#include <linux/migrate.h>
+#include <linux/fs.h>
+#include <linux/buffer_head.h>
+#include <linux/mpage.h>
+#include <linux/writeback.h>
+#include <linux/pagemap.h>
+
+#define DEBUG_SUBSYSTEM S_LLITE
+
+#include "../include/lustre_lite.h"
+#include "llite_internal.h"
+#include "../include/linux/lustre_compat25.h"
+
+/**
+ * Implements Linux VM address_space::invalidatepage() method. This method is
+ * called when the page is truncate from a file, either as a result of
+ * explicit truncate, or when inode is removed from memory (as a result of
+ * final iput(), umount, or memory pressure induced icache shrinking).
+ *
+ * [0, offset] bytes of the page remain valid (this is for a case of not-page
+ * aligned truncate). Lustre leaves partially truncated page in the cache,
+ * relying on struct inode::i_size to limit further accesses.
+ */
+static void ll_invalidatepage(struct page *vmpage, unsigned int offset,
+ unsigned int length)
+{
+ struct inode *inode;
+ struct lu_env *env;
+ struct cl_page *page;
+ struct cl_object *obj;
+
+ int refcheck;
+
+ LASSERT(PageLocked(vmpage));
+ LASSERT(!PageWriteback(vmpage));
+
+ /*
+ * It is safe to not check anything in invalidatepage/releasepage
+ * below because they are run with page locked and all our io is
+ * happening with locked page too
+ */
+ if (offset == 0 && length == PAGE_CACHE_SIZE) {
+ env = cl_env_get(&refcheck);
+ if (!IS_ERR(env)) {
+ inode = vmpage->mapping->host;
+ obj = ll_i2info(inode)->lli_clob;
+ if (obj != NULL) {
+ page = cl_vmpage_page(vmpage, obj);
+ if (page != NULL) {
+ lu_ref_add(&page->cp_reference,
+ "delete", vmpage);
+ cl_page_delete(env, page);
+ lu_ref_del(&page->cp_reference,
+ "delete", vmpage);
+ cl_page_put(env, page);
+ }
+ } else
+ LASSERT(vmpage->private == 0);
+ cl_env_put(env, &refcheck);
+ }
+ }
+}
+
+#ifdef HAVE_RELEASEPAGE_WITH_INT
+#define RELEASEPAGE_ARG_TYPE int
+#else
+#define RELEASEPAGE_ARG_TYPE gfp_t
+#endif
+static int ll_releasepage(struct page *vmpage, RELEASEPAGE_ARG_TYPE gfp_mask)
+{
+ struct cl_env_nest nest;
+ struct lu_env *env;
+ struct cl_object *obj;
+ struct cl_page *page;
+ struct address_space *mapping;
+ int result;
+
+ LASSERT(PageLocked(vmpage));
+ if (PageWriteback(vmpage) || PageDirty(vmpage))
+ return 0;
+
+ mapping = vmpage->mapping;
+ if (mapping == NULL)
+ return 1;
+
+ obj = ll_i2info(mapping->host)->lli_clob;
+ if (obj == NULL)
+ return 1;
+
+ /* 1 for page allocator, 1 for cl_page and 1 for page cache */
+ if (page_count(vmpage) > 3)
+ return 0;
+
+ /* TODO: determine what gfp should be used by @gfp_mask. */
+ env = cl_env_nested_get(&nest);
+ if (IS_ERR(env))
+ /* If we can't allocate an env we won't call cl_page_put()
+ * later on which further means it's impossible to drop
+ * page refcount by cl_page, so ask kernel to not free
+ * this page. */
+ return 0;
+
+ page = cl_vmpage_page(vmpage, obj);
+ result = page == NULL;
+ if (page != NULL) {
+ if (!cl_page_in_use(page)) {
+ result = 1;
+ cl_page_delete(env, page);
+ }
+ cl_page_put(env, page);
+ }
+ cl_env_nested_put(&nest, env);
+ return result;
+}
+
+static int ll_set_page_dirty(struct page *vmpage)
+{
+#if 0
+ struct cl_page *page = vvp_vmpage_page_transient(vmpage);
+ struct vvp_object *obj = cl_inode2vvp(vmpage->mapping->host);
+ struct vvp_page *cpg;
+
+ /*
+ * XXX should page method be called here?
+ */
+ LASSERT(&obj->co_cl == page->cp_obj);
+ cpg = cl2vvp_page(cl_page_at(page, &vvp_device_type));
+ /*
+ * XXX cannot do much here, because page is possibly not locked:
+ * sys_munmap()->...
+ * ->unmap_page_range()->zap_pte_range()->set_page_dirty().
+ */
+ vvp_write_pending(obj, cpg);
+#endif
+ return __set_page_dirty_nobuffers(vmpage);
+}
+
+#define MAX_DIRECTIO_SIZE (2*1024*1024*1024UL)
+
+static inline int ll_get_user_pages(int rw, unsigned long user_addr,
+ size_t size, struct page ***pages,
+ int *max_pages)
+{
+ int result = -ENOMEM;
+
+ /* set an arbitrary limit to prevent arithmetic overflow */
+ if (size > MAX_DIRECTIO_SIZE) {
+ *pages = NULL;
+ return -EFBIG;
+ }
+
+ *max_pages = (user_addr + size + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT;
+ *max_pages -= user_addr >> PAGE_CACHE_SHIFT;
+
+ OBD_ALLOC_LARGE(*pages, *max_pages * sizeof(**pages));
+ if (*pages) {
+ result = get_user_pages_fast(user_addr, *max_pages,
+ (rw == READ), *pages);
+ if (unlikely(result <= 0))
+ OBD_FREE_LARGE(*pages, *max_pages * sizeof(**pages));
+ }
+
+ return result;
+}
+
+/* ll_free_user_pages - tear down page struct array
+ * @pages: array of page struct pointers underlying target buffer */
+static void ll_free_user_pages(struct page **pages, int npages, int do_dirty)
+{
+ int i;
+
+ for (i = 0; i < npages; i++) {
+ if (do_dirty)
+ set_page_dirty_lock(pages[i]);
+ page_cache_release(pages[i]);
+ }
+ kvfree(pages);
+}
+
+ssize_t ll_direct_rw_pages(const struct lu_env *env, struct cl_io *io,
+ int rw, struct inode *inode,
+ struct ll_dio_pages *pv)
+{
+ struct cl_page *clp;
+ struct cl_2queue *queue;
+ struct cl_object *obj = io->ci_obj;
+ int i;
+ ssize_t rc = 0;
+ loff_t file_offset = pv->ldp_start_offset;
+ long size = pv->ldp_size;
+ int page_count = pv->ldp_nr;
+ struct page **pages = pv->ldp_pages;
+ long page_size = cl_page_size(obj);
+ bool do_io;
+ int io_pages = 0;
+
+ queue = &io->ci_queue;
+ cl_2queue_init(queue);
+ for (i = 0; i < page_count; i++) {
+ if (pv->ldp_offsets)
+ file_offset = pv->ldp_offsets[i];
+
+ LASSERT(!(file_offset & (page_size - 1)));
+ clp = cl_page_find(env, obj, cl_index(obj, file_offset),
+ pv->ldp_pages[i], CPT_TRANSIENT);
+ if (IS_ERR(clp)) {
+ rc = PTR_ERR(clp);
+ break;
+ }
+
+ rc = cl_page_own(env, io, clp);
+ if (rc) {
+ LASSERT(clp->cp_state == CPS_FREEING);
+ cl_page_put(env, clp);
+ break;
+ }
+
+ do_io = true;
+
+ /* check the page type: if the page is a host page, then do
+ * write directly */
+ if (clp->cp_type == CPT_CACHEABLE) {
+ struct page *vmpage = cl_page_vmpage(env, clp);
+ struct page *src_page;
+ struct page *dst_page;
+ void *src;
+ void *dst;
+
+ src_page = (rw == WRITE) ? pages[i] : vmpage;
+ dst_page = (rw == WRITE) ? vmpage : pages[i];
+
+ src = kmap_atomic(src_page);
+ dst = kmap_atomic(dst_page);
+ memcpy(dst, src, min(page_size, size));
+ kunmap_atomic(dst);
+ kunmap_atomic(src);
+
+ /* make sure page will be added to the transfer by
+ * cl_io_submit()->...->vvp_page_prep_write(). */
+ if (rw == WRITE)
+ set_page_dirty(vmpage);
+
+ if (rw == READ) {
+ /* do not issue the page for read, since it
+ * may reread a ra page which has NOT uptodate
+ * bit set. */
+ cl_page_disown(env, io, clp);
+ do_io = false;
+ }
+ }
+
+ if (likely(do_io)) {
+ cl_2queue_add(queue, clp);
+
+ /*
+ * Set page clip to tell transfer formation engine
+ * that page has to be sent even if it is beyond KMS.
+ */
+ cl_page_clip(env, clp, 0, min(size, page_size));
+
+ ++io_pages;
+ }
+
+ /* drop the reference count for cl_page_find */
+ cl_page_put(env, clp);
+ size -= page_size;
+ file_offset += page_size;
+ }
+
+ if (rc == 0 && io_pages) {
+ rc = cl_io_submit_sync(env, io,
+ rw == READ ? CRT_READ : CRT_WRITE,
+ queue, 0);
+ }
+ if (rc == 0)
+ rc = pv->ldp_size;
+
+ cl_2queue_discard(env, io, queue);
+ cl_2queue_disown(env, io, queue);
+ cl_2queue_fini(env, queue);
+ return rc;
+}
+EXPORT_SYMBOL(ll_direct_rw_pages);
+
+static ssize_t ll_direct_IO_26_seg(const struct lu_env *env, struct cl_io *io,
+ int rw, struct inode *inode,
+ struct address_space *mapping,
+ size_t size, loff_t file_offset,
+ struct page **pages, int page_count)
+{
+ struct ll_dio_pages pvec = { .ldp_pages = pages,
+ .ldp_nr = page_count,
+ .ldp_size = size,
+ .ldp_offsets = NULL,
+ .ldp_start_offset = file_offset
+ };
+
+ return ll_direct_rw_pages(env, io, rw, inode, &pvec);
+}
+
+#ifdef KMALLOC_MAX_SIZE
+#define MAX_MALLOC KMALLOC_MAX_SIZE
+#else
+#define MAX_MALLOC (128 * 1024)
+#endif
+
+/* This is the maximum size of a single O_DIRECT request, based on the
+ * kmalloc limit. We need to fit all of the brw_page structs, each one
+ * representing PAGE_SIZE worth of user data, into a single buffer, and
+ * then truncate this to be a full-sized RPC. For 4kB PAGE_SIZE this is
+ * up to 22MB for 128kB kmalloc and up to 682MB for 4MB kmalloc. */
+#define MAX_DIO_SIZE ((MAX_MALLOC / sizeof(struct brw_page) * PAGE_CACHE_SIZE) & \
+ ~(DT_MAX_BRW_SIZE - 1))
+static ssize_t ll_direct_IO_26(struct kiocb *iocb, struct iov_iter *iter,
+ loff_t file_offset)
+{
+ struct lu_env *env;
+ struct cl_io *io;
+ struct file *file = iocb->ki_filp;
+ struct inode *inode = file->f_mapping->host;
+ struct ccc_object *obj = cl_inode2ccc(inode);
+ ssize_t count = iov_iter_count(iter);
+ ssize_t tot_bytes = 0, result = 0;
+ struct ll_inode_info *lli = ll_i2info(inode);
+ long size = MAX_DIO_SIZE;
+ int refcheck;
+
+ if (!lli->lli_has_smd)
+ return -EBADF;
+
+ /* FIXME: io smaller than PAGE_SIZE is broken on ia64 ??? */
+ if ((file_offset & ~CFS_PAGE_MASK) || (count & ~CFS_PAGE_MASK))
+ return -EINVAL;
+
+ CDEBUG(D_VFSTRACE,
+ "VFS Op:inode=%lu/%u(%p), size=%zd (max %lu), offset=%lld=%llx, pages %zd (max %lu)\n",
+ inode->i_ino, inode->i_generation, inode, count, MAX_DIO_SIZE,
+ file_offset, file_offset, count >> PAGE_CACHE_SHIFT,
+ MAX_DIO_SIZE >> PAGE_CACHE_SHIFT);
+
+ /* Check that all user buffers are aligned as well */
+ if (iov_iter_alignment(iter) & ~CFS_PAGE_MASK)
+ return -EINVAL;
+
+ env = cl_env_get(&refcheck);
+ LASSERT(!IS_ERR(env));
+ io = ccc_env_io(env)->cui_cl.cis_io;
+ LASSERT(io != NULL);
+
+ /* 0. Need locking between buffered and direct access. and race with
+ * size changing by concurrent truncates and writes.
+ * 1. Need inode mutex to operate transient pages.
+ */
+ if (iov_iter_rw(iter) == READ)
+ mutex_lock(&inode->i_mutex);
+
+ LASSERT(obj->cob_transient_pages == 0);
+ while (iov_iter_count(iter)) {
+ struct page **pages;
+ size_t offs;
+
+ count = min_t(size_t, iov_iter_count(iter), size);
+ if (iov_iter_rw(iter) == READ) {
+ if (file_offset >= i_size_read(inode))
+ break;
+ if (file_offset + count > i_size_read(inode))
+ count = i_size_read(inode) - file_offset;
+ }
+
+ result = iov_iter_get_pages_alloc(iter, &pages, count, &offs);
+ if (likely(result > 0)) {
+ int n = DIV_ROUND_UP(result + offs, PAGE_SIZE);
+ result = ll_direct_IO_26_seg(env, io, iov_iter_rw(iter),
+ inode, file->f_mapping,
+ result, file_offset, pages,
+ n);
+ ll_free_user_pages(pages, n, iov_iter_rw(iter) == READ);
+ }
+ if (unlikely(result <= 0)) {
+ /* If we can't allocate a large enough buffer
+ * for the request, shrink it to a smaller
+ * PAGE_SIZE multiple and try again.
+ * We should always be able to kmalloc for a
+ * page worth of page pointers = 4MB on i386. */
+ if (result == -ENOMEM &&
+ size > (PAGE_CACHE_SIZE / sizeof(*pages)) *
+ PAGE_CACHE_SIZE) {
+ size = ((((size / 2) - 1) |
+ ~CFS_PAGE_MASK) + 1) &
+ CFS_PAGE_MASK;
+ CDEBUG(D_VFSTRACE, "DIO size now %lu\n",
+ size);
+ continue;
+ }
+
+ goto out;
+ }
+ iov_iter_advance(iter, result);
+ tot_bytes += result;
+ file_offset += result;
+ }
+out:
+ LASSERT(obj->cob_transient_pages == 0);
+ if (iov_iter_rw(iter) == READ)
+ mutex_unlock(&inode->i_mutex);
+
+ if (tot_bytes > 0) {
+ if (iov_iter_rw(iter) == WRITE) {
+ struct lov_stripe_md *lsm;
+
+ lsm = ccc_inode_lsm_get(inode);
+ LASSERT(lsm != NULL);
+ lov_stripe_lock(lsm);
+ obd_adjust_kms(ll_i2dtexp(inode), lsm, file_offset, 0);
+ lov_stripe_unlock(lsm);
+ ccc_inode_lsm_put(inode, lsm);
+ }
+ }
+
+ cl_env_put(env, &refcheck);
+ return tot_bytes ? : result;
+}
+
+static int ll_write_begin(struct file *file, struct address_space *mapping,
+ loff_t pos, unsigned len, unsigned flags,
+ struct page **pagep, void **fsdata)
+{
+ pgoff_t index = pos >> PAGE_CACHE_SHIFT;
+ struct page *page;
+ int rc;
+ unsigned from = pos & (PAGE_CACHE_SIZE - 1);
+
+ page = grab_cache_page_write_begin(mapping, index, flags);
+ if (!page)
+ return -ENOMEM;
+
+ *pagep = page;
+
+ rc = ll_prepare_write(file, page, from, from + len);
+ if (rc) {
+ unlock_page(page);
+ page_cache_release(page);
+ }
+ return rc;
+}
+
+static int ll_write_end(struct file *file, struct address_space *mapping,
+ loff_t pos, unsigned len, unsigned copied,
+ struct page *page, void *fsdata)
+{
+ unsigned from = pos & (PAGE_CACHE_SIZE - 1);
+ int rc;
+
+ rc = ll_commit_write(file, page, from, from + copied);
+ unlock_page(page);
+ page_cache_release(page);
+
+ return rc ?: copied;
+}
+
+#ifdef CONFIG_MIGRATION
+static int ll_migratepage(struct address_space *mapping,
+ struct page *newpage, struct page *page,
+ enum migrate_mode mode
+ )
+{
+ /* Always fail page migration until we have a proper implementation */
+ return -EIO;
+}
+#endif
+
+#ifndef MS_HAS_NEW_AOPS
+const struct address_space_operations ll_aops = {
+ .readpage = ll_readpage,
+ .direct_IO = ll_direct_IO_26,
+ .writepage = ll_writepage,
+ .writepages = ll_writepages,
+ .set_page_dirty = ll_set_page_dirty,
+ .write_begin = ll_write_begin,
+ .write_end = ll_write_end,
+ .invalidatepage = ll_invalidatepage,
+ .releasepage = (void *)ll_releasepage,
+#ifdef CONFIG_MIGRATION
+ .migratepage = ll_migratepage,
+#endif
+};
+#else
+const struct address_space_operations_ext ll_aops = {
+ .orig_aops.readpage = ll_readpage,
+/* .orig_aops.readpages = ll_readpages, */
+ .orig_aops.direct_IO = ll_direct_IO_26,
+ .orig_aops.writepage = ll_writepage,
+ .orig_aops.writepages = ll_writepages,
+ .orig_aops.set_page_dirty = ll_set_page_dirty,
+ .orig_aops.prepare_write = ll_prepare_write,
+ .orig_aops.commit_write = ll_commit_write,
+ .orig_aops.invalidatepage = ll_invalidatepage,
+ .orig_aops.releasepage = ll_releasepage,
+#ifdef CONFIG_MIGRATION
+ .orig_aops.migratepage = ll_migratepage,
+#endif
+ .write_begin = ll_write_begin,
+ .write_end = ll_write_end
+};
+#endif
diff --git a/drivers/staging/lustre/lustre/llite/statahead.c b/drivers/staging/lustre/lustre/llite/statahead.c
new file mode 100644
index 000000000..7f8071242
--- /dev/null
+++ b/drivers/staging/lustre/lustre/llite/statahead.c
@@ -0,0 +1,1729 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2011, 2012, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ */
+
+#include <linux/fs.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/highmem.h>
+#include <linux/pagemap.h>
+
+#define DEBUG_SUBSYSTEM S_LLITE
+
+#include "../include/obd_support.h"
+#include "../include/lustre_lite.h"
+#include "../include/lustre_dlm.h"
+#include "llite_internal.h"
+
+#define SA_OMITTED_ENTRY_MAX 8ULL
+
+typedef enum {
+ /** negative values are for error cases */
+ SA_ENTRY_INIT = 0, /** init entry */
+ SA_ENTRY_SUCC = 1, /** stat succeed */
+ SA_ENTRY_INVA = 2, /** invalid entry */
+ SA_ENTRY_DEST = 3, /** entry to be destroyed */
+} se_stat_t;
+
+struct ll_sa_entry {
+ /* link into sai->sai_entries */
+ struct list_head se_link;
+ /* link into sai->sai_entries_{received,stated} */
+ struct list_head se_list;
+ /* link into sai hash table locally */
+ struct list_head se_hash;
+ /* entry reference count */
+ atomic_t se_refcount;
+ /* entry index in the sai */
+ __u64 se_index;
+ /* low layer ldlm lock handle */
+ __u64 se_handle;
+ /* entry status */
+ se_stat_t se_stat;
+ /* entry size, contains name */
+ int se_size;
+ /* pointer to async getattr enqueue info */
+ struct md_enqueue_info *se_minfo;
+ /* pointer to the async getattr request */
+ struct ptlrpc_request *se_req;
+ /* pointer to the target inode */
+ struct inode *se_inode;
+ /* entry name */
+ struct qstr se_qstr;
+};
+
+static unsigned int sai_generation;
+static DEFINE_SPINLOCK(sai_generation_lock);
+
+static inline int ll_sa_entry_unhashed(struct ll_sa_entry *entry)
+{
+ return list_empty(&entry->se_hash);
+}
+
+/*
+ * The entry only can be released by the caller, it is necessary to hold lock.
+ */
+static inline int ll_sa_entry_stated(struct ll_sa_entry *entry)
+{
+ smp_rmb();
+ return (entry->se_stat != SA_ENTRY_INIT);
+}
+
+static inline int ll_sa_entry_hash(int val)
+{
+ return val & LL_SA_CACHE_MASK;
+}
+
+/*
+ * Insert entry to hash SA table.
+ */
+static inline void
+ll_sa_entry_enhash(struct ll_statahead_info *sai, struct ll_sa_entry *entry)
+{
+ int i = ll_sa_entry_hash(entry->se_qstr.hash);
+
+ spin_lock(&sai->sai_cache_lock[i]);
+ list_add_tail(&entry->se_hash, &sai->sai_cache[i]);
+ spin_unlock(&sai->sai_cache_lock[i]);
+}
+
+/*
+ * Remove entry from SA table.
+ */
+static inline void
+ll_sa_entry_unhash(struct ll_statahead_info *sai, struct ll_sa_entry *entry)
+{
+ int i = ll_sa_entry_hash(entry->se_qstr.hash);
+
+ spin_lock(&sai->sai_cache_lock[i]);
+ list_del_init(&entry->se_hash);
+ spin_unlock(&sai->sai_cache_lock[i]);
+}
+
+static inline int agl_should_run(struct ll_statahead_info *sai,
+ struct inode *inode)
+{
+ return (inode != NULL && S_ISREG(inode->i_mode) && sai->sai_agl_valid);
+}
+
+static inline struct ll_sa_entry *
+sa_first_received_entry(struct ll_statahead_info *sai)
+{
+ return list_entry(sai->sai_entries_received.next,
+ struct ll_sa_entry, se_list);
+}
+
+static inline struct ll_inode_info *
+agl_first_entry(struct ll_statahead_info *sai)
+{
+ return list_entry(sai->sai_entries_agl.next,
+ struct ll_inode_info, lli_agl_list);
+}
+
+static inline int sa_sent_full(struct ll_statahead_info *sai)
+{
+ return atomic_read(&sai->sai_cache_count) >= sai->sai_max;
+}
+
+static inline int sa_received_empty(struct ll_statahead_info *sai)
+{
+ return list_empty(&sai->sai_entries_received);
+}
+
+static inline int agl_list_empty(struct ll_statahead_info *sai)
+{
+ return list_empty(&sai->sai_entries_agl);
+}
+
+/**
+ * (1) hit ratio less than 80%
+ * or
+ * (2) consecutive miss more than 8
+ * then means low hit.
+ */
+static inline int sa_low_hit(struct ll_statahead_info *sai)
+{
+ return ((sai->sai_hit > 7 && sai->sai_hit < 4 * sai->sai_miss) ||
+ (sai->sai_consecutive_miss > 8));
+}
+
+/*
+ * If the given index is behind of statahead window more than
+ * SA_OMITTED_ENTRY_MAX, then it is old.
+ */
+static inline int is_omitted_entry(struct ll_statahead_info *sai, __u64 index)
+{
+ return ((__u64)sai->sai_max + index + SA_OMITTED_ENTRY_MAX <
+ sai->sai_index);
+}
+
+/*
+ * Insert it into sai_entries tail when init.
+ */
+static struct ll_sa_entry *
+ll_sa_entry_alloc(struct ll_statahead_info *sai, __u64 index,
+ const char *name, int len)
+{
+ struct ll_inode_info *lli;
+ struct ll_sa_entry *entry;
+ int entry_size;
+ char *dname;
+
+ entry_size = sizeof(struct ll_sa_entry) + (len & ~3) + 4;
+ entry = kzalloc(entry_size, GFP_NOFS);
+ if (unlikely(!entry))
+ return ERR_PTR(-ENOMEM);
+
+ CDEBUG(D_READA, "alloc sa entry %.*s(%p) index %llu\n",
+ len, name, entry, index);
+
+ entry->se_index = index;
+
+ /*
+ * Statahead entry reference rules:
+ *
+ * 1) When statahead entry is initialized, its reference is set as 2.
+ * One reference is used by the directory scanner. When the scanner
+ * searches the statahead cache for the given name, it can perform
+ * lockless hash lookup (only the scanner can remove entry from hash
+ * list), and once found, it needn't to call "atomic_inc()" for the
+ * entry reference. So the performance is improved. After using the
+ * statahead entry, the scanner will call "atomic_dec()" to drop the
+ * reference held when initialization. If it is the last reference,
+ * the statahead entry will be freed.
+ *
+ * 2) All other threads, including statahead thread and ptlrpcd thread,
+ * when they process the statahead entry, the reference for target
+ * should be held to guarantee the entry will not be released by the
+ * directory scanner. After processing the entry, these threads will
+ * drop the entry reference. If it is the last reference, the entry
+ * will be freed.
+ *
+ * The second reference when initializes the statahead entry is used
+ * by the statahead thread, following the rule 2).
+ */
+ atomic_set(&entry->se_refcount, 2);
+ entry->se_stat = SA_ENTRY_INIT;
+ entry->se_size = entry_size;
+ dname = (char *)entry + sizeof(struct ll_sa_entry);
+ memcpy(dname, name, len);
+ dname[len] = 0;
+ entry->se_qstr.hash = full_name_hash(name, len);
+ entry->se_qstr.len = len;
+ entry->se_qstr.name = dname;
+
+ lli = ll_i2info(sai->sai_inode);
+ spin_lock(&lli->lli_sa_lock);
+ list_add_tail(&entry->se_link, &sai->sai_entries);
+ INIT_LIST_HEAD(&entry->se_list);
+ ll_sa_entry_enhash(sai, entry);
+ spin_unlock(&lli->lli_sa_lock);
+
+ atomic_inc(&sai->sai_cache_count);
+
+ return entry;
+}
+
+/*
+ * Used by the directory scanner to search entry with name.
+ *
+ * Only the caller can remove the entry from hash, so it is unnecessary to hold
+ * hash lock. It is caller's duty to release the init refcount on the entry, so
+ * it is also unnecessary to increase refcount on the entry.
+ */
+static struct ll_sa_entry *
+ll_sa_entry_get_byname(struct ll_statahead_info *sai, const struct qstr *qstr)
+{
+ struct ll_sa_entry *entry;
+ int i = ll_sa_entry_hash(qstr->hash);
+
+ list_for_each_entry(entry, &sai->sai_cache[i], se_hash) {
+ if (entry->se_qstr.hash == qstr->hash &&
+ entry->se_qstr.len == qstr->len &&
+ memcmp(entry->se_qstr.name, qstr->name, qstr->len) == 0)
+ return entry;
+ }
+ return NULL;
+}
+
+/*
+ * Used by the async getattr request callback to find entry with index.
+ *
+ * Inside lli_sa_lock to prevent others to change the list during the search.
+ * It needs to increase entry refcount before returning to guarantee that the
+ * entry cannot be freed by others.
+ */
+static struct ll_sa_entry *
+ll_sa_entry_get_byindex(struct ll_statahead_info *sai, __u64 index)
+{
+ struct ll_sa_entry *entry;
+
+ list_for_each_entry(entry, &sai->sai_entries, se_link) {
+ if (entry->se_index == index) {
+ LASSERT(atomic_read(&entry->se_refcount) > 0);
+ atomic_inc(&entry->se_refcount);
+ return entry;
+ }
+ if (entry->se_index > index)
+ break;
+ }
+ return NULL;
+}
+
+static void ll_sa_entry_cleanup(struct ll_statahead_info *sai,
+ struct ll_sa_entry *entry)
+{
+ struct md_enqueue_info *minfo = entry->se_minfo;
+ struct ptlrpc_request *req = entry->se_req;
+
+ if (minfo) {
+ entry->se_minfo = NULL;
+ ll_intent_release(&minfo->mi_it);
+ iput(minfo->mi_dir);
+ OBD_FREE_PTR(minfo);
+ }
+
+ if (req) {
+ entry->se_req = NULL;
+ ptlrpc_req_finished(req);
+ }
+}
+
+static void ll_sa_entry_put(struct ll_statahead_info *sai,
+ struct ll_sa_entry *entry)
+{
+ if (atomic_dec_and_test(&entry->se_refcount)) {
+ CDEBUG(D_READA, "free sa entry %.*s(%p) index %llu\n",
+ entry->se_qstr.len, entry->se_qstr.name, entry,
+ entry->se_index);
+
+ LASSERT(list_empty(&entry->se_link));
+ LASSERT(list_empty(&entry->se_list));
+ LASSERT(ll_sa_entry_unhashed(entry));
+
+ ll_sa_entry_cleanup(sai, entry);
+ iput(entry->se_inode);
+
+ OBD_FREE(entry, entry->se_size);
+ atomic_dec(&sai->sai_cache_count);
+ }
+}
+
+static inline void
+do_sa_entry_fini(struct ll_statahead_info *sai, struct ll_sa_entry *entry)
+{
+ struct ll_inode_info *lli = ll_i2info(sai->sai_inode);
+
+ LASSERT(!ll_sa_entry_unhashed(entry));
+ LASSERT(!list_empty(&entry->se_link));
+
+ ll_sa_entry_unhash(sai, entry);
+
+ spin_lock(&lli->lli_sa_lock);
+ entry->se_stat = SA_ENTRY_DEST;
+ list_del_init(&entry->se_link);
+ if (likely(!list_empty(&entry->se_list)))
+ list_del_init(&entry->se_list);
+ spin_unlock(&lli->lli_sa_lock);
+
+ ll_sa_entry_put(sai, entry);
+}
+
+/*
+ * Delete it from sai_entries_stated list when fini.
+ */
+static void
+ll_sa_entry_fini(struct ll_statahead_info *sai, struct ll_sa_entry *entry)
+{
+ struct ll_sa_entry *pos, *next;
+
+ if (entry)
+ do_sa_entry_fini(sai, entry);
+
+ /* drop old entry, only 'scanner' process does this, no need to lock */
+ list_for_each_entry_safe(pos, next, &sai->sai_entries, se_link) {
+ if (!is_omitted_entry(sai, pos->se_index))
+ break;
+ do_sa_entry_fini(sai, pos);
+ }
+}
+
+/*
+ * Inside lli_sa_lock.
+ */
+static void
+do_sa_entry_to_stated(struct ll_statahead_info *sai,
+ struct ll_sa_entry *entry, se_stat_t stat)
+{
+ struct ll_sa_entry *se;
+ struct list_head *pos = &sai->sai_entries_stated;
+
+ if (!list_empty(&entry->se_list))
+ list_del_init(&entry->se_list);
+
+ list_for_each_entry_reverse(se, &sai->sai_entries_stated, se_list) {
+ if (se->se_index < entry->se_index) {
+ pos = &se->se_list;
+ break;
+ }
+ }
+
+ list_add(&entry->se_list, pos);
+ entry->se_stat = stat;
+}
+
+/*
+ * Move entry to sai_entries_stated and sort with the index.
+ * \retval 1 -- entry to be destroyed.
+ * \retval 0 -- entry is inserted into stated list.
+ */
+static int
+ll_sa_entry_to_stated(struct ll_statahead_info *sai,
+ struct ll_sa_entry *entry, se_stat_t stat)
+{
+ struct ll_inode_info *lli = ll_i2info(sai->sai_inode);
+ int ret = 1;
+
+ ll_sa_entry_cleanup(sai, entry);
+
+ spin_lock(&lli->lli_sa_lock);
+ if (likely(entry->se_stat != SA_ENTRY_DEST)) {
+ do_sa_entry_to_stated(sai, entry, stat);
+ ret = 0;
+ }
+ spin_unlock(&lli->lli_sa_lock);
+
+ return ret;
+}
+
+/*
+ * Insert inode into the list of sai_entries_agl.
+ */
+static void ll_agl_add(struct ll_statahead_info *sai,
+ struct inode *inode, int index)
+{
+ struct ll_inode_info *child = ll_i2info(inode);
+ struct ll_inode_info *parent = ll_i2info(sai->sai_inode);
+ int added = 0;
+
+ spin_lock(&child->lli_agl_lock);
+ if (child->lli_agl_index == 0) {
+ child->lli_agl_index = index;
+ spin_unlock(&child->lli_agl_lock);
+
+ LASSERT(list_empty(&child->lli_agl_list));
+
+ igrab(inode);
+ spin_lock(&parent->lli_agl_lock);
+ if (agl_list_empty(sai))
+ added = 1;
+ list_add_tail(&child->lli_agl_list, &sai->sai_entries_agl);
+ spin_unlock(&parent->lli_agl_lock);
+ } else {
+ spin_unlock(&child->lli_agl_lock);
+ }
+
+ if (added > 0)
+ wake_up(&sai->sai_agl_thread.t_ctl_waitq);
+}
+
+static struct ll_statahead_info *ll_sai_alloc(void)
+{
+ struct ll_statahead_info *sai;
+ int i;
+
+ sai = kzalloc(sizeof(*sai), GFP_NOFS);
+ if (!sai)
+ return NULL;
+
+ atomic_set(&sai->sai_refcount, 1);
+
+ spin_lock(&sai_generation_lock);
+ sai->sai_generation = ++sai_generation;
+ if (unlikely(sai_generation == 0))
+ sai->sai_generation = ++sai_generation;
+ spin_unlock(&sai_generation_lock);
+
+ sai->sai_max = LL_SA_RPC_MIN;
+ sai->sai_index = 1;
+ init_waitqueue_head(&sai->sai_waitq);
+ init_waitqueue_head(&sai->sai_thread.t_ctl_waitq);
+ init_waitqueue_head(&sai->sai_agl_thread.t_ctl_waitq);
+
+ INIT_LIST_HEAD(&sai->sai_entries);
+ INIT_LIST_HEAD(&sai->sai_entries_received);
+ INIT_LIST_HEAD(&sai->sai_entries_stated);
+ INIT_LIST_HEAD(&sai->sai_entries_agl);
+
+ for (i = 0; i < LL_SA_CACHE_SIZE; i++) {
+ INIT_LIST_HEAD(&sai->sai_cache[i]);
+ spin_lock_init(&sai->sai_cache_lock[i]);
+ }
+ atomic_set(&sai->sai_cache_count, 0);
+
+ return sai;
+}
+
+static inline struct ll_statahead_info *
+ll_sai_get(struct ll_statahead_info *sai)
+{
+ atomic_inc(&sai->sai_refcount);
+ return sai;
+}
+
+static void ll_sai_put(struct ll_statahead_info *sai)
+{
+ struct inode *inode = sai->sai_inode;
+ struct ll_inode_info *lli = ll_i2info(inode);
+
+ if (atomic_dec_and_lock(&sai->sai_refcount, &lli->lli_sa_lock)) {
+ struct ll_sa_entry *entry, *next;
+
+ if (unlikely(atomic_read(&sai->sai_refcount) > 0)) {
+ /* It is race case, the interpret callback just hold
+ * a reference count */
+ spin_unlock(&lli->lli_sa_lock);
+ return;
+ }
+
+ LASSERT(lli->lli_opendir_key == NULL);
+ LASSERT(thread_is_stopped(&sai->sai_thread));
+ LASSERT(thread_is_stopped(&sai->sai_agl_thread));
+
+ lli->lli_sai = NULL;
+ lli->lli_opendir_pid = 0;
+ spin_unlock(&lli->lli_sa_lock);
+
+ if (sai->sai_sent > sai->sai_replied)
+ CDEBUG(D_READA, "statahead for dir "DFID
+ " does not finish: [sent:%llu] [replied:%llu]\n",
+ PFID(&lli->lli_fid),
+ sai->sai_sent, sai->sai_replied);
+
+ list_for_each_entry_safe(entry, next,
+ &sai->sai_entries, se_link)
+ do_sa_entry_fini(sai, entry);
+
+ LASSERT(list_empty(&sai->sai_entries));
+ LASSERT(sa_received_empty(sai));
+ LASSERT(list_empty(&sai->sai_entries_stated));
+
+ LASSERT(atomic_read(&sai->sai_cache_count) == 0);
+ LASSERT(agl_list_empty(sai));
+
+ iput(inode);
+ OBD_FREE_PTR(sai);
+ }
+}
+
+/* Do NOT forget to drop inode refcount when into sai_entries_agl. */
+static void ll_agl_trigger(struct inode *inode, struct ll_statahead_info *sai)
+{
+ struct ll_inode_info *lli = ll_i2info(inode);
+ __u64 index = lli->lli_agl_index;
+ int rc;
+
+ LASSERT(list_empty(&lli->lli_agl_list));
+
+ /* AGL maybe fall behind statahead with one entry */
+ if (is_omitted_entry(sai, index + 1)) {
+ lli->lli_agl_index = 0;
+ iput(inode);
+ return;
+ }
+
+ /* Someone is in glimpse (sync or async), do nothing. */
+ rc = down_write_trylock(&lli->lli_glimpse_sem);
+ if (rc == 0) {
+ lli->lli_agl_index = 0;
+ iput(inode);
+ return;
+ }
+
+ /*
+ * Someone triggered glimpse within 1 sec before.
+ * 1) The former glimpse succeeded with glimpse lock granted by OST, and
+ * if the lock is still cached on client, AGL needs to do nothing. If
+ * it is cancelled by other client, AGL maybe cannot obtain new lock
+ * for no glimpse callback triggered by AGL.
+ * 2) The former glimpse succeeded, but OST did not grant glimpse lock.
+ * Under such case, it is quite possible that the OST will not grant
+ * glimpse lock for AGL also.
+ * 3) The former glimpse failed, compared with other two cases, it is
+ * relative rare. AGL can ignore such case, and it will not muchly
+ * affect the performance.
+ */
+ if (lli->lli_glimpse_time != 0 &&
+ time_before(cfs_time_shift(-1), lli->lli_glimpse_time)) {
+ up_write(&lli->lli_glimpse_sem);
+ lli->lli_agl_index = 0;
+ iput(inode);
+ return;
+ }
+
+ CDEBUG(D_READA, "Handling (init) async glimpse: inode = "
+ DFID", idx = %llu\n", PFID(&lli->lli_fid), index);
+
+ cl_agl(inode);
+ lli->lli_agl_index = 0;
+ lli->lli_glimpse_time = cfs_time_current();
+ up_write(&lli->lli_glimpse_sem);
+
+ CDEBUG(D_READA, "Handled (init) async glimpse: inode= "
+ DFID", idx = %llu, rc = %d\n",
+ PFID(&lli->lli_fid), index, rc);
+
+ iput(inode);
+}
+
+static void ll_post_statahead(struct ll_statahead_info *sai)
+{
+ struct inode *dir = sai->sai_inode;
+ struct inode *child;
+ struct ll_inode_info *lli = ll_i2info(dir);
+ struct ll_sa_entry *entry;
+ struct md_enqueue_info *minfo;
+ struct lookup_intent *it;
+ struct ptlrpc_request *req;
+ struct mdt_body *body;
+ int rc = 0;
+
+ spin_lock(&lli->lli_sa_lock);
+ if (unlikely(sa_received_empty(sai))) {
+ spin_unlock(&lli->lli_sa_lock);
+ return;
+ }
+ entry = sa_first_received_entry(sai);
+ atomic_inc(&entry->se_refcount);
+ list_del_init(&entry->se_list);
+ spin_unlock(&lli->lli_sa_lock);
+
+ LASSERT(entry->se_handle != 0);
+
+ minfo = entry->se_minfo;
+ it = &minfo->mi_it;
+ req = entry->se_req;
+ body = req_capsule_server_get(&req->rq_pill, &RMF_MDT_BODY);
+ if (body == NULL) {
+ rc = -EFAULT;
+ goto out;
+ }
+
+ child = entry->se_inode;
+ if (child == NULL) {
+ /*
+ * lookup.
+ */
+ LASSERT(fid_is_zero(&minfo->mi_data.op_fid2));
+
+ /* XXX: No fid in reply, this is probably cross-ref case.
+ * SA can't handle it yet. */
+ if (body->valid & OBD_MD_MDS) {
+ rc = -EAGAIN;
+ goto out;
+ }
+ } else {
+ /*
+ * revalidate.
+ */
+ /* unlinked and re-created with the same name */
+ if (unlikely(!lu_fid_eq(&minfo->mi_data.op_fid2, &body->fid1))){
+ entry->se_inode = NULL;
+ iput(child);
+ child = NULL;
+ }
+ }
+
+ it->d.lustre.it_lock_handle = entry->se_handle;
+ rc = md_revalidate_lock(ll_i2mdexp(dir), it, ll_inode2fid(dir), NULL);
+ if (rc != 1) {
+ rc = -EAGAIN;
+ goto out;
+ }
+
+ rc = ll_prep_inode(&child, req, dir->i_sb, it);
+ if (rc)
+ goto out;
+
+ CDEBUG(D_DLMTRACE, "setting l_data to inode %p (%lu/%u)\n",
+ child, child->i_ino, child->i_generation);
+ ll_set_lock_data(ll_i2sbi(dir)->ll_md_exp, child, it, NULL);
+
+ entry->se_inode = child;
+
+ if (agl_should_run(sai, child))
+ ll_agl_add(sai, child, entry->se_index);
+
+out:
+ /* The "ll_sa_entry_to_stated()" will drop related ldlm ibits lock
+ * reference count by calling "ll_intent_drop_lock()" in spite of the
+ * above operations failed or not. Do not worry about calling
+ * "ll_intent_drop_lock()" more than once. */
+ rc = ll_sa_entry_to_stated(sai, entry,
+ rc < 0 ? SA_ENTRY_INVA : SA_ENTRY_SUCC);
+ if (rc == 0 && entry->se_index == sai->sai_index_wait)
+ wake_up(&sai->sai_waitq);
+ ll_sa_entry_put(sai, entry);
+}
+
+static int ll_statahead_interpret(struct ptlrpc_request *req,
+ struct md_enqueue_info *minfo, int rc)
+{
+ struct lookup_intent *it = &minfo->mi_it;
+ struct inode *dir = minfo->mi_dir;
+ struct ll_inode_info *lli = ll_i2info(dir);
+ struct ll_statahead_info *sai = NULL;
+ struct ll_sa_entry *entry;
+ __u64 handle = 0;
+ int wakeup;
+
+ if (it_disposition(it, DISP_LOOKUP_NEG))
+ rc = -ENOENT;
+
+ if (rc == 0) {
+ /* release ibits lock ASAP to avoid deadlock when statahead
+ * thread enqueues lock on parent in readdir and another
+ * process enqueues lock on child with parent lock held, eg.
+ * unlink. */
+ handle = it->d.lustre.it_lock_handle;
+ ll_intent_drop_lock(it);
+ }
+
+ spin_lock(&lli->lli_sa_lock);
+ /* stale entry */
+ if (unlikely(lli->lli_sai == NULL ||
+ lli->lli_sai->sai_generation != minfo->mi_generation)) {
+ spin_unlock(&lli->lli_sa_lock);
+ rc = -ESTALE;
+ goto out;
+ } else {
+ sai = ll_sai_get(lli->lli_sai);
+ if (unlikely(!thread_is_running(&sai->sai_thread))) {
+ sai->sai_replied++;
+ spin_unlock(&lli->lli_sa_lock);
+ rc = -EBADFD;
+ goto out;
+ }
+
+ entry = ll_sa_entry_get_byindex(sai, minfo->mi_cbdata);
+ if (entry == NULL) {
+ sai->sai_replied++;
+ spin_unlock(&lli->lli_sa_lock);
+ rc = -EIDRM;
+ goto out;
+ }
+
+ if (rc != 0) {
+ do_sa_entry_to_stated(sai, entry, SA_ENTRY_INVA);
+ wakeup = (entry->se_index == sai->sai_index_wait);
+ } else {
+ entry->se_minfo = minfo;
+ entry->se_req = ptlrpc_request_addref(req);
+ /* Release the async ibits lock ASAP to avoid deadlock
+ * when statahead thread tries to enqueue lock on parent
+ * for readpage and other tries to enqueue lock on child
+ * with parent's lock held, for example: unlink. */
+ entry->se_handle = handle;
+ wakeup = sa_received_empty(sai);
+ list_add_tail(&entry->se_list,
+ &sai->sai_entries_received);
+ }
+ sai->sai_replied++;
+ spin_unlock(&lli->lli_sa_lock);
+
+ ll_sa_entry_put(sai, entry);
+ if (wakeup)
+ wake_up(&sai->sai_thread.t_ctl_waitq);
+ }
+
+out:
+ if (rc != 0) {
+ ll_intent_release(it);
+ iput(dir);
+ OBD_FREE_PTR(minfo);
+ }
+ if (sai != NULL)
+ ll_sai_put(sai);
+ return rc;
+}
+
+static void sa_args_fini(struct md_enqueue_info *minfo,
+ struct ldlm_enqueue_info *einfo)
+{
+ LASSERT(minfo && einfo);
+ iput(minfo->mi_dir);
+ capa_put(minfo->mi_data.op_capa1);
+ capa_put(minfo->mi_data.op_capa2);
+ OBD_FREE_PTR(minfo);
+ OBD_FREE_PTR(einfo);
+}
+
+/**
+ * There is race condition between "capa_put" and "ll_statahead_interpret" for
+ * accessing "op_data.op_capa[1,2]" as following:
+ * "capa_put" releases "op_data.op_capa[1,2]"'s reference count after calling
+ * "md_intent_getattr_async". But "ll_statahead_interpret" maybe run first, and
+ * fill "op_data.op_capa[1,2]" as POISON, then cause "capa_put" access invalid
+ * "ocapa". So here reserve "op_data.op_capa[1,2]" in "pcapa" before calling
+ * "md_intent_getattr_async".
+ */
+static int sa_args_init(struct inode *dir, struct inode *child,
+ struct ll_sa_entry *entry, struct md_enqueue_info **pmi,
+ struct ldlm_enqueue_info **pei,
+ struct obd_capa **pcapa)
+{
+ struct qstr *qstr = &entry->se_qstr;
+ struct ll_inode_info *lli = ll_i2info(dir);
+ struct md_enqueue_info *minfo;
+ struct ldlm_enqueue_info *einfo;
+ struct md_op_data *op_data;
+
+ einfo = kzalloc(sizeof(*einfo), GFP_NOFS);
+ if (!einfo)
+ return -ENOMEM;
+
+ minfo = kzalloc(sizeof(*minfo), GFP_NOFS);
+ if (!minfo) {
+ OBD_FREE_PTR(einfo);
+ return -ENOMEM;
+ }
+
+ op_data = ll_prep_md_op_data(&minfo->mi_data, dir, child, qstr->name,
+ qstr->len, 0, LUSTRE_OPC_ANY, NULL);
+ if (IS_ERR(op_data)) {
+ OBD_FREE_PTR(einfo);
+ OBD_FREE_PTR(minfo);
+ return PTR_ERR(op_data);
+ }
+
+ minfo->mi_it.it_op = IT_GETATTR;
+ minfo->mi_dir = igrab(dir);
+ minfo->mi_cb = ll_statahead_interpret;
+ minfo->mi_generation = lli->lli_sai->sai_generation;
+ minfo->mi_cbdata = entry->se_index;
+
+ einfo->ei_type = LDLM_IBITS;
+ einfo->ei_mode = it_to_lock_mode(&minfo->mi_it);
+ einfo->ei_cb_bl = ll_md_blocking_ast;
+ einfo->ei_cb_cp = ldlm_completion_ast;
+ einfo->ei_cb_gl = NULL;
+ einfo->ei_cbdata = NULL;
+
+ *pmi = minfo;
+ *pei = einfo;
+ pcapa[0] = op_data->op_capa1;
+ pcapa[1] = op_data->op_capa2;
+
+ return 0;
+}
+
+static int do_sa_lookup(struct inode *dir, struct ll_sa_entry *entry)
+{
+ struct md_enqueue_info *minfo;
+ struct ldlm_enqueue_info *einfo;
+ struct obd_capa *capas[2];
+ int rc;
+
+ rc = sa_args_init(dir, NULL, entry, &minfo, &einfo, capas);
+ if (rc)
+ return rc;
+
+ rc = md_intent_getattr_async(ll_i2mdexp(dir), minfo, einfo);
+ if (!rc) {
+ capa_put(capas[0]);
+ capa_put(capas[1]);
+ } else {
+ sa_args_fini(minfo, einfo);
+ }
+
+ return rc;
+}
+
+/**
+ * similar to ll_revalidate_it().
+ * \retval 1 -- dentry valid
+ * \retval 0 -- will send stat-ahead request
+ * \retval others -- prepare stat-ahead request failed
+ */
+static int do_sa_revalidate(struct inode *dir, struct ll_sa_entry *entry,
+ struct dentry *dentry)
+{
+ struct inode *inode = d_inode(dentry);
+ struct lookup_intent it = { .it_op = IT_GETATTR,
+ .d.lustre.it_lock_handle = 0 };
+ struct md_enqueue_info *minfo;
+ struct ldlm_enqueue_info *einfo;
+ struct obd_capa *capas[2];
+ int rc;
+
+ if (unlikely(inode == NULL))
+ return 1;
+
+ if (d_mountpoint(dentry))
+ return 1;
+
+ entry->se_inode = igrab(inode);
+ rc = md_revalidate_lock(ll_i2mdexp(dir), &it, ll_inode2fid(inode),
+ NULL);
+ if (rc == 1) {
+ entry->se_handle = it.d.lustre.it_lock_handle;
+ ll_intent_release(&it);
+ return 1;
+ }
+
+ rc = sa_args_init(dir, inode, entry, &minfo, &einfo, capas);
+ if (rc) {
+ entry->se_inode = NULL;
+ iput(inode);
+ return rc;
+ }
+
+ rc = md_intent_getattr_async(ll_i2mdexp(dir), minfo, einfo);
+ if (!rc) {
+ capa_put(capas[0]);
+ capa_put(capas[1]);
+ } else {
+ entry->se_inode = NULL;
+ iput(inode);
+ sa_args_fini(minfo, einfo);
+ }
+
+ return rc;
+}
+
+static void ll_statahead_one(struct dentry *parent, const char *entry_name,
+ int entry_name_len)
+{
+ struct inode *dir = d_inode(parent);
+ struct ll_inode_info *lli = ll_i2info(dir);
+ struct ll_statahead_info *sai = lli->lli_sai;
+ struct dentry *dentry = NULL;
+ struct ll_sa_entry *entry;
+ int rc;
+ int rc1;
+
+ entry = ll_sa_entry_alloc(sai, sai->sai_index, entry_name,
+ entry_name_len);
+ if (IS_ERR(entry))
+ return;
+
+ dentry = d_lookup(parent, &entry->se_qstr);
+ if (!dentry) {
+ rc = do_sa_lookup(dir, entry);
+ } else {
+ rc = do_sa_revalidate(dir, entry, dentry);
+ if (rc == 1 && agl_should_run(sai, d_inode(dentry)))
+ ll_agl_add(sai, d_inode(dentry), entry->se_index);
+ }
+
+ if (dentry != NULL)
+ dput(dentry);
+
+ if (rc) {
+ rc1 = ll_sa_entry_to_stated(sai, entry,
+ rc < 0 ? SA_ENTRY_INVA : SA_ENTRY_SUCC);
+ if (rc1 == 0 && entry->se_index == sai->sai_index_wait)
+ wake_up(&sai->sai_waitq);
+ } else {
+ sai->sai_sent++;
+ }
+
+ sai->sai_index++;
+ /* drop one refcount on entry by ll_sa_entry_alloc */
+ ll_sa_entry_put(sai, entry);
+}
+
+static int ll_agl_thread(void *arg)
+{
+ struct dentry *parent = (struct dentry *)arg;
+ struct inode *dir = d_inode(parent);
+ struct ll_inode_info *plli = ll_i2info(dir);
+ struct ll_inode_info *clli;
+ struct ll_sb_info *sbi = ll_i2sbi(dir);
+ struct ll_statahead_info *sai = ll_sai_get(plli->lli_sai);
+ struct ptlrpc_thread *thread = &sai->sai_agl_thread;
+ struct l_wait_info lwi = { 0 };
+
+ thread->t_pid = current_pid();
+ CDEBUG(D_READA, "agl thread started: sai %p, parent %pd\n",
+ sai, parent);
+
+ atomic_inc(&sbi->ll_agl_total);
+ spin_lock(&plli->lli_agl_lock);
+ sai->sai_agl_valid = 1;
+ if (thread_is_init(thread))
+ /* If someone else has changed the thread state
+ * (e.g. already changed to SVC_STOPPING), we can't just
+ * blindly overwrite that setting. */
+ thread_set_flags(thread, SVC_RUNNING);
+ spin_unlock(&plli->lli_agl_lock);
+ wake_up(&thread->t_ctl_waitq);
+
+ while (1) {
+ l_wait_event(thread->t_ctl_waitq,
+ !agl_list_empty(sai) ||
+ !thread_is_running(thread),
+ &lwi);
+
+ if (!thread_is_running(thread))
+ break;
+
+ spin_lock(&plli->lli_agl_lock);
+ /* The statahead thread maybe help to process AGL entries,
+ * so check whether list empty again. */
+ if (!agl_list_empty(sai)) {
+ clli = agl_first_entry(sai);
+ list_del_init(&clli->lli_agl_list);
+ spin_unlock(&plli->lli_agl_lock);
+ ll_agl_trigger(&clli->lli_vfs_inode, sai);
+ } else {
+ spin_unlock(&plli->lli_agl_lock);
+ }
+ }
+
+ spin_lock(&plli->lli_agl_lock);
+ sai->sai_agl_valid = 0;
+ while (!agl_list_empty(sai)) {
+ clli = agl_first_entry(sai);
+ list_del_init(&clli->lli_agl_list);
+ spin_unlock(&plli->lli_agl_lock);
+ clli->lli_agl_index = 0;
+ iput(&clli->lli_vfs_inode);
+ spin_lock(&plli->lli_agl_lock);
+ }
+ thread_set_flags(thread, SVC_STOPPED);
+ spin_unlock(&plli->lli_agl_lock);
+ wake_up(&thread->t_ctl_waitq);
+ ll_sai_put(sai);
+ CDEBUG(D_READA, "agl thread stopped: sai %p, parent %pd\n",
+ sai, parent);
+ return 0;
+}
+
+static void ll_start_agl(struct dentry *parent, struct ll_statahead_info *sai)
+{
+ struct ptlrpc_thread *thread = &sai->sai_agl_thread;
+ struct l_wait_info lwi = { 0 };
+ struct ll_inode_info *plli;
+ struct task_struct *task;
+
+ CDEBUG(D_READA, "start agl thread: sai %p, parent %pd\n",
+ sai, parent);
+
+ plli = ll_i2info(d_inode(parent));
+ task = kthread_run(ll_agl_thread, parent,
+ "ll_agl_%u", plli->lli_opendir_pid);
+ if (IS_ERR(task)) {
+ CERROR("can't start ll_agl thread, rc: %ld\n", PTR_ERR(task));
+ thread_set_flags(thread, SVC_STOPPED);
+ return;
+ }
+
+ l_wait_event(thread->t_ctl_waitq,
+ thread_is_running(thread) || thread_is_stopped(thread),
+ &lwi);
+}
+
+static int ll_statahead_thread(void *arg)
+{
+ struct dentry *parent = (struct dentry *)arg;
+ struct inode *dir = d_inode(parent);
+ struct ll_inode_info *plli = ll_i2info(dir);
+ struct ll_inode_info *clli;
+ struct ll_sb_info *sbi = ll_i2sbi(dir);
+ struct ll_statahead_info *sai = ll_sai_get(plli->lli_sai);
+ struct ptlrpc_thread *thread = &sai->sai_thread;
+ struct ptlrpc_thread *agl_thread = &sai->sai_agl_thread;
+ struct page *page;
+ __u64 pos = 0;
+ int first = 0;
+ int rc = 0;
+ struct ll_dir_chain chain;
+ struct l_wait_info lwi = { 0 };
+
+ thread->t_pid = current_pid();
+ CDEBUG(D_READA, "statahead thread starting: sai %p, parent %pd\n",
+ sai, parent);
+
+ if (sbi->ll_flags & LL_SBI_AGL_ENABLED)
+ ll_start_agl(parent, sai);
+
+ atomic_inc(&sbi->ll_sa_total);
+ spin_lock(&plli->lli_sa_lock);
+ if (thread_is_init(thread))
+ /* If someone else has changed the thread state
+ * (e.g. already changed to SVC_STOPPING), we can't just
+ * blindly overwrite that setting. */
+ thread_set_flags(thread, SVC_RUNNING);
+ spin_unlock(&plli->lli_sa_lock);
+ wake_up(&thread->t_ctl_waitq);
+
+ ll_dir_chain_init(&chain);
+ page = ll_get_dir_page(dir, pos, &chain);
+
+ while (1) {
+ struct lu_dirpage *dp;
+ struct lu_dirent *ent;
+
+ if (IS_ERR(page)) {
+ rc = PTR_ERR(page);
+ CDEBUG(D_READA, "error reading dir "DFID" at %llu/%llu: [rc %d] [parent %u]\n",
+ PFID(ll_inode2fid(dir)), pos, sai->sai_index,
+ rc, plli->lli_opendir_pid);
+ goto out;
+ }
+
+ dp = page_address(page);
+ for (ent = lu_dirent_start(dp); ent != NULL;
+ ent = lu_dirent_next(ent)) {
+ __u64 hash;
+ int namelen;
+ char *name;
+
+ hash = le64_to_cpu(ent->lde_hash);
+ if (unlikely(hash < pos))
+ /*
+ * Skip until we find target hash value.
+ */
+ continue;
+
+ namelen = le16_to_cpu(ent->lde_namelen);
+ if (unlikely(namelen == 0))
+ /*
+ * Skip dummy record.
+ */
+ continue;
+
+ name = ent->lde_name;
+ if (name[0] == '.') {
+ if (namelen == 1) {
+ /*
+ * skip "."
+ */
+ continue;
+ } else if (name[1] == '.' && namelen == 2) {
+ /*
+ * skip ".."
+ */
+ continue;
+ } else if (!sai->sai_ls_all) {
+ /*
+ * skip hidden files.
+ */
+ sai->sai_skip_hidden++;
+ continue;
+ }
+ }
+
+ /*
+ * don't stat-ahead first entry.
+ */
+ if (unlikely(++first == 1))
+ continue;
+
+keep_it:
+ l_wait_event(thread->t_ctl_waitq,
+ !sa_sent_full(sai) ||
+ !sa_received_empty(sai) ||
+ !agl_list_empty(sai) ||
+ !thread_is_running(thread),
+ &lwi);
+
+interpret_it:
+ while (!sa_received_empty(sai))
+ ll_post_statahead(sai);
+
+ if (unlikely(!thread_is_running(thread))) {
+ ll_release_page(page, 0);
+ rc = 0;
+ goto out;
+ }
+
+ /* If no window for metadata statahead, but there are
+ * some AGL entries to be triggered, then try to help
+ * to process the AGL entries. */
+ if (sa_sent_full(sai)) {
+ spin_lock(&plli->lli_agl_lock);
+ while (!agl_list_empty(sai)) {
+ clli = agl_first_entry(sai);
+ list_del_init(&clli->lli_agl_list);
+ spin_unlock(&plli->lli_agl_lock);
+ ll_agl_trigger(&clli->lli_vfs_inode,
+ sai);
+
+ if (!sa_received_empty(sai))
+ goto interpret_it;
+
+ if (unlikely(
+ !thread_is_running(thread))) {
+ ll_release_page(page, 0);
+ rc = 0;
+ goto out;
+ }
+
+ if (!sa_sent_full(sai))
+ goto do_it;
+
+ spin_lock(&plli->lli_agl_lock);
+ }
+ spin_unlock(&plli->lli_agl_lock);
+
+ goto keep_it;
+ }
+
+do_it:
+ ll_statahead_one(parent, name, namelen);
+ }
+ pos = le64_to_cpu(dp->ldp_hash_end);
+ if (pos == MDS_DIR_END_OFF) {
+ /*
+ * End of directory reached.
+ */
+ ll_release_page(page, 0);
+ while (1) {
+ l_wait_event(thread->t_ctl_waitq,
+ !sa_received_empty(sai) ||
+ sai->sai_sent == sai->sai_replied||
+ !thread_is_running(thread),
+ &lwi);
+
+ while (!sa_received_empty(sai))
+ ll_post_statahead(sai);
+
+ if (unlikely(!thread_is_running(thread))) {
+ rc = 0;
+ goto out;
+ }
+
+ if (sai->sai_sent == sai->sai_replied &&
+ sa_received_empty(sai))
+ break;
+ }
+
+ spin_lock(&plli->lli_agl_lock);
+ while (!agl_list_empty(sai) &&
+ thread_is_running(thread)) {
+ clli = agl_first_entry(sai);
+ list_del_init(&clli->lli_agl_list);
+ spin_unlock(&plli->lli_agl_lock);
+ ll_agl_trigger(&clli->lli_vfs_inode, sai);
+ spin_lock(&plli->lli_agl_lock);
+ }
+ spin_unlock(&plli->lli_agl_lock);
+
+ rc = 0;
+ goto out;
+ } else if (1) {
+ /*
+ * chain is exhausted.
+ * Normal case: continue to the next page.
+ */
+ ll_release_page(page, le32_to_cpu(dp->ldp_flags) &
+ LDF_COLLIDE);
+ page = ll_get_dir_page(dir, pos, &chain);
+ } else {
+ LASSERT(le32_to_cpu(dp->ldp_flags) & LDF_COLLIDE);
+ ll_release_page(page, 1);
+ /*
+ * go into overflow page.
+ */
+ }
+ }
+
+out:
+ if (sai->sai_agl_valid) {
+ spin_lock(&plli->lli_agl_lock);
+ thread_set_flags(agl_thread, SVC_STOPPING);
+ spin_unlock(&plli->lli_agl_lock);
+ wake_up(&agl_thread->t_ctl_waitq);
+
+ CDEBUG(D_READA, "stop agl thread: sai %p pid %u\n",
+ sai, (unsigned int)agl_thread->t_pid);
+ l_wait_event(agl_thread->t_ctl_waitq,
+ thread_is_stopped(agl_thread),
+ &lwi);
+ } else {
+ /* Set agl_thread flags anyway. */
+ thread_set_flags(&sai->sai_agl_thread, SVC_STOPPED);
+ }
+ ll_dir_chain_fini(&chain);
+ spin_lock(&plli->lli_sa_lock);
+ if (!sa_received_empty(sai)) {
+ thread_set_flags(thread, SVC_STOPPING);
+ spin_unlock(&plli->lli_sa_lock);
+
+ /* To release the resources held by received entries. */
+ while (!sa_received_empty(sai))
+ ll_post_statahead(sai);
+
+ spin_lock(&plli->lli_sa_lock);
+ }
+ thread_set_flags(thread, SVC_STOPPED);
+ spin_unlock(&plli->lli_sa_lock);
+ wake_up(&sai->sai_waitq);
+ wake_up(&thread->t_ctl_waitq);
+ ll_sai_put(sai);
+ dput(parent);
+ CDEBUG(D_READA, "statahead thread stopped: sai %p, parent %pd\n",
+ sai, parent);
+ return rc;
+}
+
+/**
+ * called in ll_file_release().
+ */
+void ll_stop_statahead(struct inode *dir, void *key)
+{
+ struct ll_inode_info *lli = ll_i2info(dir);
+
+ if (unlikely(key == NULL))
+ return;
+
+ spin_lock(&lli->lli_sa_lock);
+ if (lli->lli_opendir_key != key || lli->lli_opendir_pid == 0) {
+ spin_unlock(&lli->lli_sa_lock);
+ return;
+ }
+
+ lli->lli_opendir_key = NULL;
+
+ if (lli->lli_sai) {
+ struct l_wait_info lwi = { 0 };
+ struct ptlrpc_thread *thread = &lli->lli_sai->sai_thread;
+
+ if (!thread_is_stopped(thread)) {
+ thread_set_flags(thread, SVC_STOPPING);
+ spin_unlock(&lli->lli_sa_lock);
+ wake_up(&thread->t_ctl_waitq);
+
+ CDEBUG(D_READA, "stop statahead thread: sai %p pid %u\n",
+ lli->lli_sai, (unsigned int)thread->t_pid);
+ l_wait_event(thread->t_ctl_waitq,
+ thread_is_stopped(thread),
+ &lwi);
+ } else {
+ spin_unlock(&lli->lli_sa_lock);
+ }
+
+ /*
+ * Put the ref which was held when first statahead_enter.
+ * It maybe not the last ref for some statahead requests
+ * maybe inflight.
+ */
+ ll_sai_put(lli->lli_sai);
+ } else {
+ lli->lli_opendir_pid = 0;
+ spin_unlock(&lli->lli_sa_lock);
+ }
+}
+
+enum {
+ /**
+ * not first dirent, or is "."
+ */
+ LS_NONE_FIRST_DE = 0,
+ /**
+ * the first non-hidden dirent
+ */
+ LS_FIRST_DE,
+ /**
+ * the first hidden dirent, that is "."
+ */
+ LS_FIRST_DOT_DE
+};
+
+static int is_first_dirent(struct inode *dir, struct dentry *dentry)
+{
+ struct ll_dir_chain chain;
+ struct qstr *target = &dentry->d_name;
+ struct page *page;
+ __u64 pos = 0;
+ int dot_de;
+ int rc = LS_NONE_FIRST_DE;
+
+ ll_dir_chain_init(&chain);
+ page = ll_get_dir_page(dir, pos, &chain);
+
+ while (1) {
+ struct lu_dirpage *dp;
+ struct lu_dirent *ent;
+
+ if (IS_ERR(page)) {
+ struct ll_inode_info *lli = ll_i2info(dir);
+
+ rc = PTR_ERR(page);
+ CERROR("error reading dir "DFID" at %llu: [rc %d] [parent %u]\n",
+ PFID(ll_inode2fid(dir)), pos,
+ rc, lli->lli_opendir_pid);
+ break;
+ }
+
+ dp = page_address(page);
+ for (ent = lu_dirent_start(dp); ent != NULL;
+ ent = lu_dirent_next(ent)) {
+ __u64 hash;
+ int namelen;
+ char *name;
+
+ hash = le64_to_cpu(ent->lde_hash);
+ /* The ll_get_dir_page() can return any page containing
+ * the given hash which may be not the start hash. */
+ if (unlikely(hash < pos))
+ continue;
+
+ namelen = le16_to_cpu(ent->lde_namelen);
+ if (unlikely(namelen == 0))
+ /*
+ * skip dummy record.
+ */
+ continue;
+
+ name = ent->lde_name;
+ if (name[0] == '.') {
+ if (namelen == 1)
+ /*
+ * skip "."
+ */
+ continue;
+ else if (name[1] == '.' && namelen == 2)
+ /*
+ * skip ".."
+ */
+ continue;
+ else
+ dot_de = 1;
+ } else {
+ dot_de = 0;
+ }
+
+ if (dot_de && target->name[0] != '.') {
+ CDEBUG(D_READA, "%.*s skip hidden file %.*s\n",
+ target->len, target->name,
+ namelen, name);
+ continue;
+ }
+
+ if (target->len != namelen ||
+ memcmp(target->name, name, namelen) != 0)
+ rc = LS_NONE_FIRST_DE;
+ else if (!dot_de)
+ rc = LS_FIRST_DE;
+ else
+ rc = LS_FIRST_DOT_DE;
+
+ ll_release_page(page, 0);
+ goto out;
+ }
+ pos = le64_to_cpu(dp->ldp_hash_end);
+ if (pos == MDS_DIR_END_OFF) {
+ /*
+ * End of directory reached.
+ */
+ ll_release_page(page, 0);
+ break;
+ } else if (1) {
+ /*
+ * chain is exhausted
+ * Normal case: continue to the next page.
+ */
+ ll_release_page(page, le32_to_cpu(dp->ldp_flags) &
+ LDF_COLLIDE);
+ page = ll_get_dir_page(dir, pos, &chain);
+ } else {
+ /*
+ * go into overflow page.
+ */
+ LASSERT(le32_to_cpu(dp->ldp_flags) & LDF_COLLIDE);
+ ll_release_page(page, 1);
+ }
+ }
+
+out:
+ ll_dir_chain_fini(&chain);
+ return rc;
+}
+
+static void
+ll_sai_unplug(struct ll_statahead_info *sai, struct ll_sa_entry *entry)
+{
+ struct ptlrpc_thread *thread = &sai->sai_thread;
+ struct ll_sb_info *sbi = ll_i2sbi(sai->sai_inode);
+ int hit;
+
+ if (entry != NULL && entry->se_stat == SA_ENTRY_SUCC)
+ hit = 1;
+ else
+ hit = 0;
+
+ ll_sa_entry_fini(sai, entry);
+ if (hit) {
+ sai->sai_hit++;
+ sai->sai_consecutive_miss = 0;
+ sai->sai_max = min(2 * sai->sai_max, sbi->ll_sa_max);
+ } else {
+ struct ll_inode_info *lli = ll_i2info(sai->sai_inode);
+
+ sai->sai_miss++;
+ sai->sai_consecutive_miss++;
+ if (sa_low_hit(sai) && thread_is_running(thread)) {
+ atomic_inc(&sbi->ll_sa_wrong);
+ CDEBUG(D_READA, "Statahead for dir " DFID " hit ratio too low: hit/miss %llu/%llu, sent/replied %llu/%llu, stopping statahead thread\n",
+ PFID(&lli->lli_fid), sai->sai_hit,
+ sai->sai_miss, sai->sai_sent,
+ sai->sai_replied);
+ spin_lock(&lli->lli_sa_lock);
+ if (!thread_is_stopped(thread))
+ thread_set_flags(thread, SVC_STOPPING);
+ spin_unlock(&lli->lli_sa_lock);
+ }
+ }
+
+ if (!thread_is_stopped(thread))
+ wake_up(&thread->t_ctl_waitq);
+}
+
+/**
+ * Start statahead thread if this is the first dir entry.
+ * Otherwise if a thread is started already, wait it until it is ahead of me.
+ * \retval 1 -- find entry with lock in cache, the caller needs to do
+ * nothing.
+ * \retval 0 -- find entry in cache, but without lock, the caller needs
+ * refresh from MDS.
+ * \retval others -- the caller need to process as non-statahead.
+ */
+int do_statahead_enter(struct inode *dir, struct dentry **dentryp,
+ int only_unplug)
+{
+ struct ll_inode_info *lli = ll_i2info(dir);
+ struct ll_statahead_info *sai = lli->lli_sai;
+ struct dentry *parent;
+ struct ll_sa_entry *entry;
+ struct ptlrpc_thread *thread;
+ struct l_wait_info lwi = { 0 };
+ int rc = 0;
+ struct ll_inode_info *plli;
+
+ LASSERT(lli->lli_opendir_pid == current_pid());
+
+ if (sai) {
+ thread = &sai->sai_thread;
+ if (unlikely(thread_is_stopped(thread) &&
+ list_empty(&sai->sai_entries_stated))) {
+ /* to release resource */
+ ll_stop_statahead(dir, lli->lli_opendir_key);
+ return -EAGAIN;
+ }
+
+ if ((*dentryp)->d_name.name[0] == '.') {
+ if (sai->sai_ls_all ||
+ sai->sai_miss_hidden >= sai->sai_skip_hidden) {
+ /*
+ * Hidden dentry is the first one, or statahead
+ * thread does not skip so many hidden dentries
+ * before "sai_ls_all" enabled as below.
+ */
+ } else {
+ if (!sai->sai_ls_all)
+ /*
+ * It maybe because hidden dentry is not
+ * the first one, "sai_ls_all" was not
+ * set, then "ls -al" missed. Enable
+ * "sai_ls_all" for such case.
+ */
+ sai->sai_ls_all = 1;
+
+ /*
+ * Such "getattr" has been skipped before
+ * "sai_ls_all" enabled as above.
+ */
+ sai->sai_miss_hidden++;
+ return -EAGAIN;
+ }
+ }
+
+ entry = ll_sa_entry_get_byname(sai, &(*dentryp)->d_name);
+ if (entry == NULL || only_unplug) {
+ ll_sai_unplug(sai, entry);
+ return entry ? 1 : -EAGAIN;
+ }
+
+ if (!ll_sa_entry_stated(entry)) {
+ sai->sai_index_wait = entry->se_index;
+ lwi = LWI_TIMEOUT_INTR(cfs_time_seconds(30), NULL,
+ LWI_ON_SIGNAL_NOOP, NULL);
+ rc = l_wait_event(sai->sai_waitq,
+ ll_sa_entry_stated(entry) ||
+ thread_is_stopped(thread),
+ &lwi);
+ if (rc < 0) {
+ ll_sai_unplug(sai, entry);
+ return -EAGAIN;
+ }
+ }
+
+ if (entry->se_stat == SA_ENTRY_SUCC &&
+ entry->se_inode != NULL) {
+ struct inode *inode = entry->se_inode;
+ struct lookup_intent it = { .it_op = IT_GETATTR,
+ .d.lustre.it_lock_handle =
+ entry->se_handle };
+ __u64 bits;
+
+ rc = md_revalidate_lock(ll_i2mdexp(dir), &it,
+ ll_inode2fid(inode), &bits);
+ if (rc == 1) {
+ if (d_inode(*dentryp) == NULL) {
+ struct dentry *alias;
+
+ alias = ll_splice_alias(inode,
+ *dentryp);
+ if (IS_ERR(alias)) {
+ ll_sai_unplug(sai, entry);
+ return PTR_ERR(alias);
+ }
+ *dentryp = alias;
+ } else if (d_inode(*dentryp) != inode) {
+ /* revalidate, but inode is recreated */
+ CDEBUG(D_READA,
+ "stale dentry %pd inode %lu/%u, statahead inode %lu/%u\n",
+ *dentryp,
+ d_inode(*dentryp)->i_ino,
+ d_inode(*dentryp)->i_generation,
+ inode->i_ino,
+ inode->i_generation);
+ ll_sai_unplug(sai, entry);
+ return -ESTALE;
+ } else {
+ iput(inode);
+ }
+ entry->se_inode = NULL;
+
+ if ((bits & MDS_INODELOCK_LOOKUP) &&
+ d_lustre_invalid(*dentryp))
+ d_lustre_revalidate(*dentryp);
+ ll_intent_release(&it);
+ }
+ }
+
+ ll_sai_unplug(sai, entry);
+ return rc;
+ }
+
+ /* I am the "lli_opendir_pid" owner, only me can set "lli_sai". */
+ rc = is_first_dirent(dir, *dentryp);
+ if (rc == LS_NONE_FIRST_DE) {
+ /* It is not "ls -{a}l" operation, no need statahead for it. */
+ rc = -EAGAIN;
+ goto out;
+ }
+
+ sai = ll_sai_alloc();
+ if (sai == NULL) {
+ rc = -ENOMEM;
+ goto out;
+ }
+
+ sai->sai_ls_all = (rc == LS_FIRST_DOT_DE);
+ sai->sai_inode = igrab(dir);
+ if (unlikely(sai->sai_inode == NULL)) {
+ CWARN("Do not start stat ahead on dying inode "DFID"\n",
+ PFID(&lli->lli_fid));
+ rc = -ESTALE;
+ goto out;
+ }
+
+ /* get parent reference count here, and put it in ll_statahead_thread */
+ parent = dget((*dentryp)->d_parent);
+ if (unlikely(sai->sai_inode != d_inode(parent))) {
+ struct ll_inode_info *nlli = ll_i2info(d_inode(parent));
+
+ CWARN("Race condition, someone changed %pd just now: old parent "DFID", new parent "DFID"\n",
+ *dentryp,
+ PFID(&lli->lli_fid), PFID(&nlli->lli_fid));
+ dput(parent);
+ iput(sai->sai_inode);
+ rc = -EAGAIN;
+ goto out;
+ }
+
+ CDEBUG(D_READA, "start statahead thread: sai %p, parent %pd\n",
+ sai, parent);
+
+ /* The sai buffer already has one reference taken at allocation time,
+ * but as soon as we expose the sai by attaching it to the lli that
+ * default reference can be dropped by another thread calling
+ * ll_stop_statahead. We need to take a local reference to protect
+ * the sai buffer while we intend to access it. */
+ ll_sai_get(sai);
+ lli->lli_sai = sai;
+
+ plli = ll_i2info(d_inode(parent));
+ rc = PTR_ERR(kthread_run(ll_statahead_thread, parent,
+ "ll_sa_%u", plli->lli_opendir_pid));
+ thread = &sai->sai_thread;
+ if (IS_ERR_VALUE(rc)) {
+ CERROR("can't start ll_sa thread, rc: %d\n", rc);
+ dput(parent);
+ lli->lli_opendir_key = NULL;
+ thread_set_flags(thread, SVC_STOPPED);
+ thread_set_flags(&sai->sai_agl_thread, SVC_STOPPED);
+ /* Drop both our own local reference and the default
+ * reference from allocation time. */
+ ll_sai_put(sai);
+ ll_sai_put(sai);
+ LASSERT(lli->lli_sai == NULL);
+ return -EAGAIN;
+ }
+
+ l_wait_event(thread->t_ctl_waitq,
+ thread_is_running(thread) || thread_is_stopped(thread),
+ &lwi);
+ ll_sai_put(sai);
+
+ /*
+ * We don't stat-ahead for the first dirent since we are already in
+ * lookup.
+ */
+ return -EAGAIN;
+
+out:
+ if (sai != NULL)
+ OBD_FREE_PTR(sai);
+ spin_lock(&lli->lli_sa_lock);
+ lli->lli_opendir_key = NULL;
+ lli->lli_opendir_pid = 0;
+ spin_unlock(&lli->lli_sa_lock);
+ return rc;
+}
diff --git a/drivers/staging/lustre/lustre/llite/super25.c b/drivers/staging/lustre/lustre/llite/super25.c
new file mode 100644
index 000000000..a494f6271
--- /dev/null
+++ b/drivers/staging/lustre/lustre/llite/super25.c
@@ -0,0 +1,226 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2002, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2011, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ */
+
+#define DEBUG_SUBSYSTEM S_LLITE
+
+#include <linux/module.h>
+#include <linux/types.h>
+#include "../include/lustre_lite.h"
+#include "../include/lustre_ha.h"
+#include "../include/lustre_dlm.h"
+#include <linux/init.h>
+#include <linux/fs.h>
+#include "../include/lprocfs_status.h"
+#include "llite_internal.h"
+
+static struct kmem_cache *ll_inode_cachep;
+
+static struct inode *ll_alloc_inode(struct super_block *sb)
+{
+ struct ll_inode_info *lli;
+ ll_stats_ops_tally(ll_s2sbi(sb), LPROC_LL_ALLOC_INODE, 1);
+ OBD_SLAB_ALLOC_PTR_GFP(lli, ll_inode_cachep, GFP_NOFS);
+ if (lli == NULL)
+ return NULL;
+
+ inode_init_once(&lli->lli_vfs_inode);
+ return &lli->lli_vfs_inode;
+}
+
+static void ll_inode_destroy_callback(struct rcu_head *head)
+{
+ struct inode *inode = container_of(head, struct inode, i_rcu);
+ struct ll_inode_info *ptr = ll_i2info(inode);
+ OBD_SLAB_FREE_PTR(ptr, ll_inode_cachep);
+}
+
+static void ll_destroy_inode(struct inode *inode)
+{
+ call_rcu(&inode->i_rcu, ll_inode_destroy_callback);
+}
+
+/* exported operations */
+struct super_operations lustre_super_operations = {
+ .alloc_inode = ll_alloc_inode,
+ .destroy_inode = ll_destroy_inode,
+ .evict_inode = ll_delete_inode,
+ .put_super = ll_put_super,
+ .statfs = ll_statfs,
+ .umount_begin = ll_umount_begin,
+ .remount_fs = ll_remount_fs,
+ .show_options = ll_show_options,
+};
+MODULE_ALIAS_FS("lustre");
+
+void lustre_register_client_process_config(int (*cpc)(struct lustre_cfg *lcfg));
+
+static int __init init_lustre_lite(void)
+{
+ struct proc_dir_entry *entry;
+ lnet_process_id_t lnet_id;
+ struct timeval tv;
+ int i, rc, seed[2];
+
+ CLASSERT(sizeof(LUSTRE_VOLATILE_HDR) == LUSTRE_VOLATILE_HDR_LEN + 1);
+
+ /* print an address of _any_ initialized kernel symbol from this
+ * module, to allow debugging with gdb that doesn't support data
+ * symbols from modules.*/
+ CDEBUG(D_INFO, "Lustre client module (%p).\n",
+ &lustre_super_operations);
+
+ rc = -ENOMEM;
+ ll_inode_cachep = kmem_cache_create("lustre_inode_cache",
+ sizeof(struct ll_inode_info),
+ 0, SLAB_HWCACHE_ALIGN, NULL);
+ if (ll_inode_cachep == NULL)
+ goto out_cache;
+
+ ll_file_data_slab = kmem_cache_create("ll_file_data",
+ sizeof(struct ll_file_data), 0,
+ SLAB_HWCACHE_ALIGN, NULL);
+ if (ll_file_data_slab == NULL)
+ goto out_cache;
+
+ ll_remote_perm_cachep = kmem_cache_create("ll_remote_perm_cache",
+ sizeof(struct ll_remote_perm),
+ 0, 0, NULL);
+ if (ll_remote_perm_cachep == NULL)
+ goto out_cache;
+
+ ll_rmtperm_hash_cachep = kmem_cache_create("ll_rmtperm_hash_cache",
+ REMOTE_PERM_HASHSIZE *
+ sizeof(struct list_head),
+ 0, 0, NULL);
+ if (ll_rmtperm_hash_cachep == NULL)
+ goto out_cache;
+
+ entry = lprocfs_register("llite", proc_lustre_root, NULL, NULL);
+ if (IS_ERR(entry)) {
+ rc = PTR_ERR(entry);
+ CERROR("cannot register '/proc/fs/lustre/llite': rc = %d\n",
+ rc);
+ goto out_cache;
+ }
+
+ proc_lustre_fs_root = entry;
+
+ cfs_get_random_bytes(seed, sizeof(seed));
+
+ /* Nodes with small feet have little entropy. The NID for this
+ * node gives the most entropy in the low bits */
+ for (i = 0;; i++) {
+ if (LNetGetId(i, &lnet_id) == -ENOENT)
+ break;
+
+ if (LNET_NETTYP(LNET_NIDNET(lnet_id.nid)) != LOLND)
+ seed[0] ^= LNET_NIDADDR(lnet_id.nid);
+ }
+
+ do_gettimeofday(&tv);
+ cfs_srand(tv.tv_sec ^ seed[0], tv.tv_usec ^ seed[1]);
+ setup_timer(&ll_capa_timer, ll_capa_timer_callback, 0);
+ rc = ll_capa_thread_start();
+ if (rc != 0)
+ goto out_proc;
+
+ rc = vvp_global_init();
+ if (rc != 0)
+ goto out_capa;
+
+ rc = ll_xattr_init();
+ if (rc != 0)
+ goto out_vvp;
+
+ lustre_register_client_fill_super(ll_fill_super);
+ lustre_register_kill_super_cb(ll_kill_super);
+ lustre_register_client_process_config(ll_process_config);
+
+ return 0;
+
+out_vvp:
+ vvp_global_fini();
+out_capa:
+ del_timer(&ll_capa_timer);
+ ll_capa_thread_stop();
+out_proc:
+ lprocfs_remove(&proc_lustre_fs_root);
+out_cache:
+ if (ll_inode_cachep != NULL)
+ kmem_cache_destroy(ll_inode_cachep);
+
+ if (ll_file_data_slab != NULL)
+ kmem_cache_destroy(ll_file_data_slab);
+
+ if (ll_remote_perm_cachep != NULL)
+ kmem_cache_destroy(ll_remote_perm_cachep);
+
+ if (ll_rmtperm_hash_cachep != NULL)
+ kmem_cache_destroy(ll_rmtperm_hash_cachep);
+
+ return rc;
+}
+
+static void __exit exit_lustre_lite(void)
+{
+ lustre_register_client_fill_super(NULL);
+ lustre_register_kill_super_cb(NULL);
+ lustre_register_client_process_config(NULL);
+
+ lprocfs_remove(&proc_lustre_fs_root);
+
+ ll_xattr_fini();
+ vvp_global_fini();
+ del_timer(&ll_capa_timer);
+ ll_capa_thread_stop();
+ LASSERTF(capa_count[CAPA_SITE_CLIENT] == 0,
+ "client remaining capa count %d\n",
+ capa_count[CAPA_SITE_CLIENT]);
+
+ kmem_cache_destroy(ll_inode_cachep);
+ kmem_cache_destroy(ll_rmtperm_hash_cachep);
+
+ kmem_cache_destroy(ll_remote_perm_cachep);
+
+ kmem_cache_destroy(ll_file_data_slab);
+}
+
+MODULE_AUTHOR("Sun Microsystems, Inc. <http://www.lustre.org/>");
+MODULE_DESCRIPTION("Lustre Lite Client File System");
+MODULE_LICENSE("GPL");
+
+module_init(init_lustre_lite);
+module_exit(exit_lustre_lite);
diff --git a/drivers/staging/lustre/lustre/llite/symlink.c b/drivers/staging/lustre/lustre/llite/symlink.c
new file mode 100644
index 000000000..3711e671a
--- /dev/null
+++ b/drivers/staging/lustre/lustre/llite/symlink.c
@@ -0,0 +1,170 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2002, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2011, 2012, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ */
+
+#include <linux/fs.h>
+#include <linux/mm.h>
+#include <linux/stat.h>
+#define DEBUG_SUBSYSTEM S_LLITE
+
+#include "../include/lustre_lite.h"
+#include "llite_internal.h"
+
+static int ll_readlink_internal(struct inode *inode,
+ struct ptlrpc_request **request, char **symname)
+{
+ struct ll_inode_info *lli = ll_i2info(inode);
+ struct ll_sb_info *sbi = ll_i2sbi(inode);
+ int rc, symlen = i_size_read(inode) + 1;
+ struct mdt_body *body;
+ struct md_op_data *op_data;
+
+ *request = NULL;
+
+ if (lli->lli_symlink_name) {
+ int print_limit = min_t(int, PAGE_SIZE - 128, symlen);
+
+ *symname = lli->lli_symlink_name;
+ /* If the total CDEBUG() size is larger than a page, it
+ * will print a warning to the console, avoid this by
+ * printing just the last part of the symlink. */
+ CDEBUG(D_INODE, "using cached symlink %s%.*s, len = %d\n",
+ print_limit < symlen ? "..." : "", print_limit,
+ (*symname) + symlen - print_limit, symlen);
+ return 0;
+ }
+
+ op_data = ll_prep_md_op_data(NULL, inode, NULL, NULL, 0, symlen,
+ LUSTRE_OPC_ANY, NULL);
+ if (IS_ERR(op_data))
+ return PTR_ERR(op_data);
+
+ op_data->op_valid = OBD_MD_LINKNAME;
+ rc = md_getattr(sbi->ll_md_exp, op_data, request);
+ ll_finish_md_op_data(op_data);
+ if (rc) {
+ if (rc != -ENOENT)
+ CERROR("inode %lu: rc = %d\n", inode->i_ino, rc);
+ goto failed;
+ }
+
+ body = req_capsule_server_get(&(*request)->rq_pill, &RMF_MDT_BODY);
+ LASSERT(body != NULL);
+ if ((body->valid & OBD_MD_LINKNAME) == 0) {
+ CERROR("OBD_MD_LINKNAME not set on reply\n");
+ rc = -EPROTO;
+ goto failed;
+ }
+
+ LASSERT(symlen != 0);
+ if (body->eadatasize != symlen) {
+ CERROR("inode %lu: symlink length %d not expected %d\n",
+ inode->i_ino, body->eadatasize - 1, symlen - 1);
+ rc = -EPROTO;
+ goto failed;
+ }
+
+ *symname = req_capsule_server_get(&(*request)->rq_pill, &RMF_MDT_MD);
+ if (*symname == NULL ||
+ strnlen(*symname, symlen) != symlen - 1) {
+ /* not full/NULL terminated */
+ CERROR("inode %lu: symlink not NULL terminated string of length %d\n",
+ inode->i_ino, symlen - 1);
+ rc = -EPROTO;
+ goto failed;
+ }
+
+ lli->lli_symlink_name = kzalloc(symlen, GFP_NOFS);
+ /* do not return an error if we cannot cache the symlink locally */
+ if (lli->lli_symlink_name) {
+ memcpy(lli->lli_symlink_name, *symname, symlen);
+ *symname = lli->lli_symlink_name;
+ }
+ return 0;
+
+failed:
+ return rc;
+}
+
+static void *ll_follow_link(struct dentry *dentry, struct nameidata *nd)
+{
+ struct inode *inode = d_inode(dentry);
+ struct ptlrpc_request *request = NULL;
+ int rc;
+ char *symname = NULL;
+
+ CDEBUG(D_VFSTRACE, "VFS Op\n");
+ /* Limit the recursive symlink depth to 5 instead of default
+ * 8 links when kernel has 4k stack to prevent stack overflow.
+ * For 8k stacks we need to limit it to 7 for local servers. */
+ if (THREAD_SIZE < 8192 && current->link_count >= 6) {
+ rc = -ELOOP;
+ } else if (THREAD_SIZE == 8192 && current->link_count >= 8) {
+ rc = -ELOOP;
+ } else {
+ ll_inode_size_lock(inode);
+ rc = ll_readlink_internal(inode, &request, &symname);
+ ll_inode_size_unlock(inode);
+ }
+ if (rc) {
+ ptlrpc_req_finished(request);
+ request = NULL;
+ symname = ERR_PTR(rc);
+ }
+
+ nd_set_link(nd, symname);
+ /* symname may contain a pointer to the request message buffer,
+ * we delay request releasing until ll_put_link then.
+ */
+ return request;
+}
+
+static void ll_put_link(struct dentry *dentry, struct nameidata *nd, void *cookie)
+{
+ ptlrpc_req_finished(cookie);
+}
+
+struct inode_operations ll_fast_symlink_inode_operations = {
+ .readlink = generic_readlink,
+ .setattr = ll_setattr,
+ .follow_link = ll_follow_link,
+ .put_link = ll_put_link,
+ .getattr = ll_getattr,
+ .permission = ll_inode_permission,
+ .setxattr = ll_setxattr,
+ .getxattr = ll_getxattr,
+ .listxattr = ll_listxattr,
+ .removexattr = ll_removexattr,
+};
diff --git a/drivers/staging/lustre/lustre/llite/vvp_dev.c b/drivers/staging/lustre/lustre/llite/vvp_dev.c
new file mode 100644
index 000000000..fde41d7c5
--- /dev/null
+++ b/drivers/staging/lustre/lustre/llite/vvp_dev.c
@@ -0,0 +1,547 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2012, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ *
+ * cl_device and cl_device_type implementation for VVP layer.
+ *
+ * Author: Nikita Danilov <nikita.danilov@sun.com>
+ */
+
+#define DEBUG_SUBSYSTEM S_LLITE
+
+
+#include "../include/obd.h"
+#include "../include/lustre_lite.h"
+#include "llite_internal.h"
+#include "vvp_internal.h"
+
+/*****************************************************************************
+ *
+ * Vvp device and device type functions.
+ *
+ */
+
+/*
+ * vvp_ prefix stands for "Vfs Vm Posix". It corresponds to historical
+ * "llite_" (var. "ll_") prefix.
+ */
+
+static struct kmem_cache *vvp_thread_kmem;
+static struct kmem_cache *vvp_session_kmem;
+static struct lu_kmem_descr vvp_caches[] = {
+ {
+ .ckd_cache = &vvp_thread_kmem,
+ .ckd_name = "vvp_thread_kmem",
+ .ckd_size = sizeof(struct vvp_thread_info),
+ },
+ {
+ .ckd_cache = &vvp_session_kmem,
+ .ckd_name = "vvp_session_kmem",
+ .ckd_size = sizeof(struct vvp_session)
+ },
+ {
+ .ckd_cache = NULL
+ }
+};
+
+static void *vvp_key_init(const struct lu_context *ctx,
+ struct lu_context_key *key)
+{
+ struct vvp_thread_info *info;
+
+ OBD_SLAB_ALLOC_PTR_GFP(info, vvp_thread_kmem, GFP_NOFS);
+ if (info == NULL)
+ info = ERR_PTR(-ENOMEM);
+ return info;
+}
+
+static void vvp_key_fini(const struct lu_context *ctx,
+ struct lu_context_key *key, void *data)
+{
+ struct vvp_thread_info *info = data;
+
+ OBD_SLAB_FREE_PTR(info, vvp_thread_kmem);
+}
+
+static void *vvp_session_key_init(const struct lu_context *ctx,
+ struct lu_context_key *key)
+{
+ struct vvp_session *session;
+
+ OBD_SLAB_ALLOC_PTR_GFP(session, vvp_session_kmem, GFP_NOFS);
+ if (session == NULL)
+ session = ERR_PTR(-ENOMEM);
+ return session;
+}
+
+static void vvp_session_key_fini(const struct lu_context *ctx,
+ struct lu_context_key *key, void *data)
+{
+ struct vvp_session *session = data;
+
+ OBD_SLAB_FREE_PTR(session, vvp_session_kmem);
+}
+
+
+struct lu_context_key vvp_key = {
+ .lct_tags = LCT_CL_THREAD,
+ .lct_init = vvp_key_init,
+ .lct_fini = vvp_key_fini
+};
+
+struct lu_context_key vvp_session_key = {
+ .lct_tags = LCT_SESSION,
+ .lct_init = vvp_session_key_init,
+ .lct_fini = vvp_session_key_fini
+};
+
+/* type constructor/destructor: vvp_type_{init,fini,start,stop}(). */
+LU_TYPE_INIT_FINI(vvp, &ccc_key, &ccc_session_key, &vvp_key, &vvp_session_key);
+
+static const struct lu_device_operations vvp_lu_ops = {
+ .ldo_object_alloc = vvp_object_alloc
+};
+
+static const struct cl_device_operations vvp_cl_ops = {
+ .cdo_req_init = ccc_req_init
+};
+
+static struct lu_device *vvp_device_alloc(const struct lu_env *env,
+ struct lu_device_type *t,
+ struct lustre_cfg *cfg)
+{
+ return ccc_device_alloc(env, t, cfg, &vvp_lu_ops, &vvp_cl_ops);
+}
+
+static const struct lu_device_type_operations vvp_device_type_ops = {
+ .ldto_init = vvp_type_init,
+ .ldto_fini = vvp_type_fini,
+
+ .ldto_start = vvp_type_start,
+ .ldto_stop = vvp_type_stop,
+
+ .ldto_device_alloc = vvp_device_alloc,
+ .ldto_device_free = ccc_device_free,
+ .ldto_device_init = ccc_device_init,
+ .ldto_device_fini = ccc_device_fini
+};
+
+struct lu_device_type vvp_device_type = {
+ .ldt_tags = LU_DEVICE_CL,
+ .ldt_name = LUSTRE_VVP_NAME,
+ .ldt_ops = &vvp_device_type_ops,
+ .ldt_ctx_tags = LCT_CL_THREAD
+};
+
+/**
+ * A mutex serializing calls to vvp_inode_fini() under extreme memory
+ * pressure, when environments cannot be allocated.
+ */
+int vvp_global_init(void)
+{
+ int result;
+
+ result = lu_kmem_init(vvp_caches);
+ if (result == 0) {
+ result = ccc_global_init(&vvp_device_type);
+ if (result != 0)
+ lu_kmem_fini(vvp_caches);
+ }
+ return result;
+}
+
+void vvp_global_fini(void)
+{
+ ccc_global_fini(&vvp_device_type);
+ lu_kmem_fini(vvp_caches);
+}
+
+
+/*****************************************************************************
+ *
+ * mirror obd-devices into cl devices.
+ *
+ */
+
+int cl_sb_init(struct super_block *sb)
+{
+ struct ll_sb_info *sbi;
+ struct cl_device *cl;
+ struct lu_env *env;
+ int rc = 0;
+ int refcheck;
+
+ sbi = ll_s2sbi(sb);
+ env = cl_env_get(&refcheck);
+ if (!IS_ERR(env)) {
+ cl = cl_type_setup(env, NULL, &vvp_device_type,
+ sbi->ll_dt_exp->exp_obd->obd_lu_dev);
+ if (!IS_ERR(cl)) {
+ cl2ccc_dev(cl)->cdv_sb = sb;
+ sbi->ll_cl = cl;
+ sbi->ll_site = cl2lu_dev(cl)->ld_site;
+ }
+ cl_env_put(env, &refcheck);
+ } else
+ rc = PTR_ERR(env);
+ return rc;
+}
+
+int cl_sb_fini(struct super_block *sb)
+{
+ struct ll_sb_info *sbi;
+ struct lu_env *env;
+ struct cl_device *cld;
+ int refcheck;
+ int result;
+
+ sbi = ll_s2sbi(sb);
+ env = cl_env_get(&refcheck);
+ if (!IS_ERR(env)) {
+ cld = sbi->ll_cl;
+
+ if (cld != NULL) {
+ cl_stack_fini(env, cld);
+ sbi->ll_cl = NULL;
+ sbi->ll_site = NULL;
+ }
+ cl_env_put(env, &refcheck);
+ result = 0;
+ } else {
+ CERROR("Cannot cleanup cl-stack due to memory shortage.\n");
+ result = PTR_ERR(env);
+ }
+ /*
+ * If mount failed (sbi->ll_cl == NULL), and this there are no other
+ * mounts, stop device types manually (this usually happens
+ * automatically when last device is destroyed).
+ */
+ lu_types_stop();
+ return result;
+}
+
+/****************************************************************************
+ *
+ * /proc/fs/lustre/llite/$MNT/dump_page_cache
+ *
+ ****************************************************************************/
+
+/*
+ * To represent contents of a page cache as a byte stream, following
+ * information if encoded in 64bit offset:
+ *
+ * - file hash bucket in lu_site::ls_hash[] 28bits
+ *
+ * - how far file is from bucket head 4bits
+ *
+ * - page index 32bits
+ *
+ * First two data identify a file in the cache uniquely.
+ */
+
+#define PGC_OBJ_SHIFT (32 + 4)
+#define PGC_DEPTH_SHIFT (32)
+
+struct vvp_pgcache_id {
+ unsigned vpi_bucket;
+ unsigned vpi_depth;
+ uint32_t vpi_index;
+
+ unsigned vpi_curdep;
+ struct lu_object_header *vpi_obj;
+};
+
+static void vvp_pgcache_id_unpack(loff_t pos, struct vvp_pgcache_id *id)
+{
+ CLASSERT(sizeof(pos) == sizeof(__u64));
+
+ id->vpi_index = pos & 0xffffffff;
+ id->vpi_depth = (pos >> PGC_DEPTH_SHIFT) & 0xf;
+ id->vpi_bucket = (unsigned long long)pos >> PGC_OBJ_SHIFT;
+}
+
+static loff_t vvp_pgcache_id_pack(struct vvp_pgcache_id *id)
+{
+ return
+ ((__u64)id->vpi_index) |
+ ((__u64)id->vpi_depth << PGC_DEPTH_SHIFT) |
+ ((__u64)id->vpi_bucket << PGC_OBJ_SHIFT);
+}
+
+static int vvp_pgcache_obj_get(struct cfs_hash *hs, struct cfs_hash_bd *bd,
+ struct hlist_node *hnode, void *data)
+{
+ struct vvp_pgcache_id *id = data;
+ struct lu_object_header *hdr = cfs_hash_object(hs, hnode);
+
+ if (id->vpi_curdep-- > 0)
+ return 0; /* continue */
+
+ if (lu_object_is_dying(hdr))
+ return 1;
+
+ cfs_hash_get(hs, hnode);
+ id->vpi_obj = hdr;
+ return 1;
+}
+
+static struct cl_object *vvp_pgcache_obj(const struct lu_env *env,
+ struct lu_device *dev,
+ struct vvp_pgcache_id *id)
+{
+ LASSERT(lu_device_is_cl(dev));
+
+ id->vpi_depth &= 0xf;
+ id->vpi_obj = NULL;
+ id->vpi_curdep = id->vpi_depth;
+
+ cfs_hash_hlist_for_each(dev->ld_site->ls_obj_hash, id->vpi_bucket,
+ vvp_pgcache_obj_get, id);
+ if (id->vpi_obj != NULL) {
+ struct lu_object *lu_obj;
+
+ lu_obj = lu_object_locate(id->vpi_obj, dev->ld_type);
+ if (lu_obj != NULL) {
+ lu_object_ref_add(lu_obj, "dump", current);
+ return lu2cl(lu_obj);
+ }
+ lu_object_put(env, lu_object_top(id->vpi_obj));
+
+ } else if (id->vpi_curdep > 0) {
+ id->vpi_depth = 0xf;
+ }
+ return NULL;
+}
+
+static loff_t vvp_pgcache_find(const struct lu_env *env,
+ struct lu_device *dev, loff_t pos)
+{
+ struct cl_object *clob;
+ struct lu_site *site;
+ struct vvp_pgcache_id id;
+
+ site = dev->ld_site;
+ vvp_pgcache_id_unpack(pos, &id);
+
+ while (1) {
+ if (id.vpi_bucket >= CFS_HASH_NHLIST(site->ls_obj_hash))
+ return ~0ULL;
+ clob = vvp_pgcache_obj(env, dev, &id);
+ if (clob != NULL) {
+ struct cl_object_header *hdr;
+ int nr;
+ struct cl_page *pg;
+
+ /* got an object. Find next page. */
+ hdr = cl_object_header(clob);
+
+ spin_lock(&hdr->coh_page_guard);
+ nr = radix_tree_gang_lookup(&hdr->coh_tree,
+ (void **)&pg,
+ id.vpi_index, 1);
+ if (nr > 0) {
+ id.vpi_index = pg->cp_index;
+ /* Cant support over 16T file */
+ nr = !(pg->cp_index > 0xffffffff);
+ }
+ spin_unlock(&hdr->coh_page_guard);
+
+ lu_object_ref_del(&clob->co_lu, "dump", current);
+ cl_object_put(env, clob);
+ if (nr > 0)
+ return vvp_pgcache_id_pack(&id);
+ }
+ /* to the next object. */
+ ++id.vpi_depth;
+ id.vpi_depth &= 0xf;
+ if (id.vpi_depth == 0 && ++id.vpi_bucket == 0)
+ return ~0ULL;
+ id.vpi_index = 0;
+ }
+}
+
+#define seq_page_flag(seq, page, flag, has_flags) do { \
+ if (test_bit(PG_##flag, &(page)->flags)) { \
+ seq_printf(seq, "%s"#flag, has_flags ? "|" : ""); \
+ has_flags = 1; \
+ } \
+} while (0)
+
+static void vvp_pgcache_page_show(const struct lu_env *env,
+ struct seq_file *seq, struct cl_page *page)
+{
+ struct ccc_page *cpg;
+ struct page *vmpage;
+ int has_flags;
+
+ cpg = cl2ccc_page(cl_page_at(page, &vvp_device_type));
+ vmpage = cpg->cpg_page;
+ seq_printf(seq, " %5i | %p %p %s %s %s %s | %p %lu/%u(%p) %lu %u [",
+ 0 /* gen */,
+ cpg, page,
+ "none",
+ cpg->cpg_write_queued ? "wq" : "- ",
+ cpg->cpg_defer_uptodate ? "du" : "- ",
+ PageWriteback(vmpage) ? "wb" : "-",
+ vmpage, vmpage->mapping->host->i_ino,
+ vmpage->mapping->host->i_generation,
+ vmpage->mapping->host, vmpage->index,
+ page_count(vmpage));
+ has_flags = 0;
+ seq_page_flag(seq, vmpage, locked, has_flags);
+ seq_page_flag(seq, vmpage, error, has_flags);
+ seq_page_flag(seq, vmpage, referenced, has_flags);
+ seq_page_flag(seq, vmpage, uptodate, has_flags);
+ seq_page_flag(seq, vmpage, dirty, has_flags);
+ seq_page_flag(seq, vmpage, writeback, has_flags);
+ seq_printf(seq, "%s]\n", has_flags ? "" : "-");
+}
+
+static int vvp_pgcache_show(struct seq_file *f, void *v)
+{
+ loff_t pos;
+ struct ll_sb_info *sbi;
+ struct cl_object *clob;
+ struct lu_env *env;
+ struct cl_page *page;
+ struct cl_object_header *hdr;
+ struct vvp_pgcache_id id;
+ int refcheck;
+ int result;
+
+ env = cl_env_get(&refcheck);
+ if (!IS_ERR(env)) {
+ pos = *(loff_t *) v;
+ vvp_pgcache_id_unpack(pos, &id);
+ sbi = f->private;
+ clob = vvp_pgcache_obj(env, &sbi->ll_cl->cd_lu_dev, &id);
+ if (clob != NULL) {
+ hdr = cl_object_header(clob);
+
+ spin_lock(&hdr->coh_page_guard);
+ page = cl_page_lookup(hdr, id.vpi_index);
+ spin_unlock(&hdr->coh_page_guard);
+
+ seq_printf(f, "%8x@"DFID": ",
+ id.vpi_index, PFID(&hdr->coh_lu.loh_fid));
+ if (page != NULL) {
+ vvp_pgcache_page_show(env, f, page);
+ cl_page_put(env, page);
+ } else
+ seq_puts(f, "missing\n");
+ lu_object_ref_del(&clob->co_lu, "dump", current);
+ cl_object_put(env, clob);
+ } else
+ seq_printf(f, "%llx missing\n", pos);
+ cl_env_put(env, &refcheck);
+ result = 0;
+ } else
+ result = PTR_ERR(env);
+ return result;
+}
+
+static void *vvp_pgcache_start(struct seq_file *f, loff_t *pos)
+{
+ struct ll_sb_info *sbi;
+ struct lu_env *env;
+ int refcheck;
+
+ sbi = f->private;
+
+ env = cl_env_get(&refcheck);
+ if (!IS_ERR(env)) {
+ sbi = f->private;
+ if (sbi->ll_site->ls_obj_hash->hs_cur_bits > 64 - PGC_OBJ_SHIFT)
+ pos = ERR_PTR(-EFBIG);
+ else {
+ *pos = vvp_pgcache_find(env, &sbi->ll_cl->cd_lu_dev,
+ *pos);
+ if (*pos == ~0ULL)
+ pos = NULL;
+ }
+ cl_env_put(env, &refcheck);
+ }
+ return pos;
+}
+
+static void *vvp_pgcache_next(struct seq_file *f, void *v, loff_t *pos)
+{
+ struct ll_sb_info *sbi;
+ struct lu_env *env;
+ int refcheck;
+
+ env = cl_env_get(&refcheck);
+ if (!IS_ERR(env)) {
+ sbi = f->private;
+ *pos = vvp_pgcache_find(env, &sbi->ll_cl->cd_lu_dev, *pos + 1);
+ if (*pos == ~0ULL)
+ pos = NULL;
+ cl_env_put(env, &refcheck);
+ }
+ return pos;
+}
+
+static void vvp_pgcache_stop(struct seq_file *f, void *v)
+{
+ /* Nothing to do */
+}
+
+static struct seq_operations vvp_pgcache_ops = {
+ .start = vvp_pgcache_start,
+ .next = vvp_pgcache_next,
+ .stop = vvp_pgcache_stop,
+ .show = vvp_pgcache_show
+};
+
+static int vvp_dump_pgcache_seq_open(struct inode *inode, struct file *filp)
+{
+ struct ll_sb_info *sbi = PDE_DATA(inode);
+ struct seq_file *seq;
+ int result;
+
+ result = seq_open(filp, &vvp_pgcache_ops);
+ if (result == 0) {
+ seq = filp->private_data;
+ seq->private = sbi;
+ }
+ return result;
+}
+
+const struct file_operations vvp_dump_pgcache_file_ops = {
+ .owner = THIS_MODULE,
+ .open = vvp_dump_pgcache_seq_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = seq_release,
+};
diff --git a/drivers/staging/lustre/lustre/llite/vvp_internal.h b/drivers/staging/lustre/lustre/llite/vvp_internal.h
new file mode 100644
index 000000000..2162bf6c0
--- /dev/null
+++ b/drivers/staging/lustre/lustre/llite/vvp_internal.h
@@ -0,0 +1,62 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2002, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ *
+ * Internal definitions for VVP layer.
+ *
+ * Author: Nikita Danilov <nikita.danilov@sun.com>
+ */
+
+#ifndef VVP_INTERNAL_H
+#define VVP_INTERNAL_H
+
+
+#include "../include/cl_object.h"
+#include "llite_internal.h"
+
+int vvp_io_init (const struct lu_env *env,
+ struct cl_object *obj, struct cl_io *io);
+int vvp_lock_init (const struct lu_env *env,
+ struct cl_object *obj, struct cl_lock *lock,
+ const struct cl_io *io);
+int vvp_page_init (const struct lu_env *env,
+ struct cl_object *obj,
+ struct cl_page *page, struct page *vmpage);
+struct lu_object *vvp_object_alloc(const struct lu_env *env,
+ const struct lu_object_header *hdr,
+ struct lu_device *dev);
+
+struct ccc_object *cl_inode2ccc(struct inode *inode);
+
+extern const struct file_operations vvp_dump_pgcache_file_ops;
+
+#endif /* VVP_INTERNAL_H */
diff --git a/drivers/staging/lustre/lustre/llite/vvp_io.c b/drivers/staging/lustre/lustre/llite/vvp_io.c
new file mode 100644
index 000000000..91bba7967
--- /dev/null
+++ b/drivers/staging/lustre/lustre/llite/vvp_io.c
@@ -0,0 +1,1209 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2011, 2012, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ *
+ * Implementation of cl_io for VVP layer.
+ *
+ * Author: Nikita Danilov <nikita.danilov@sun.com>
+ * Author: Jinshan Xiong <jinshan.xiong@whamcloud.com>
+ */
+
+#define DEBUG_SUBSYSTEM S_LLITE
+
+
+#include "../include/obd.h"
+#include "../include/lustre_lite.h"
+
+#include "vvp_internal.h"
+
+static struct vvp_io *cl2vvp_io(const struct lu_env *env,
+ const struct cl_io_slice *slice);
+
+/**
+ * True, if \a io is a normal io, False for splice_{read,write}
+ */
+int cl_is_normalio(const struct lu_env *env, const struct cl_io *io)
+{
+ struct vvp_io *vio = vvp_env_io(env);
+
+ LASSERT(io->ci_type == CIT_READ || io->ci_type == CIT_WRITE);
+
+ return vio->cui_io_subtype == IO_NORMAL;
+}
+
+/**
+ * For swapping layout. The file's layout may have changed.
+ * To avoid populating pages to a wrong stripe, we have to verify the
+ * correctness of layout. It works because swapping layout processes
+ * have to acquire group lock.
+ */
+static bool can_populate_pages(const struct lu_env *env, struct cl_io *io,
+ struct inode *inode)
+{
+ struct ll_inode_info *lli = ll_i2info(inode);
+ struct ccc_io *cio = ccc_env_io(env);
+ bool rc = true;
+
+ switch (io->ci_type) {
+ case CIT_READ:
+ case CIT_WRITE:
+ /* don't need lock here to check lli_layout_gen as we have held
+ * extent lock and GROUP lock has to hold to swap layout */
+ if (ll_layout_version_get(lli) != cio->cui_layout_gen) {
+ io->ci_need_restart = 1;
+ /* this will return application a short read/write */
+ io->ci_continue = 0;
+ rc = false;
+ }
+ case CIT_FAULT:
+ /* fault is okay because we've already had a page. */
+ default:
+ break;
+ }
+
+ return rc;
+}
+
+/*****************************************************************************
+ *
+ * io operations.
+ *
+ */
+
+static int vvp_io_fault_iter_init(const struct lu_env *env,
+ const struct cl_io_slice *ios)
+{
+ struct vvp_io *vio = cl2vvp_io(env, ios);
+ struct inode *inode = ccc_object_inode(ios->cis_obj);
+
+ LASSERT(inode ==
+ file_inode(cl2ccc_io(env, ios)->cui_fd->fd_file));
+ vio->u.fault.ft_mtime = LTIME_S(inode->i_mtime);
+ return 0;
+}
+
+static void vvp_io_fini(const struct lu_env *env, const struct cl_io_slice *ios)
+{
+ struct cl_io *io = ios->cis_io;
+ struct cl_object *obj = io->ci_obj;
+ struct ccc_io *cio = cl2ccc_io(env, ios);
+
+ CLOBINVRNT(env, obj, ccc_object_invariant(obj));
+
+ CDEBUG(D_VFSTRACE, DFID
+ " ignore/verify layout %d/%d, layout version %d restore needed %d\n",
+ PFID(lu_object_fid(&obj->co_lu)),
+ io->ci_ignore_layout, io->ci_verify_layout,
+ cio->cui_layout_gen, io->ci_restore_needed);
+
+ if (io->ci_restore_needed == 1) {
+ int rc;
+
+ /* file was detected release, we need to restore it
+ * before finishing the io
+ */
+ rc = ll_layout_restore(ccc_object_inode(obj));
+ /* if restore registration failed, no restart,
+ * we will return -ENODATA */
+ /* The layout will change after restore, so we need to
+ * block on layout lock hold by the MDT
+ * as MDT will not send new layout in lvb (see LU-3124)
+ * we have to explicitly fetch it, all this will be done
+ * by ll_layout_refresh()
+ */
+ if (rc == 0) {
+ io->ci_restore_needed = 0;
+ io->ci_need_restart = 1;
+ io->ci_verify_layout = 1;
+ } else {
+ io->ci_restore_needed = 1;
+ io->ci_need_restart = 0;
+ io->ci_verify_layout = 0;
+ io->ci_result = rc;
+ }
+ }
+
+ if (!io->ci_ignore_layout && io->ci_verify_layout) {
+ __u32 gen = 0;
+
+ /* check layout version */
+ ll_layout_refresh(ccc_object_inode(obj), &gen);
+ io->ci_need_restart = cio->cui_layout_gen != gen;
+ if (io->ci_need_restart) {
+ CDEBUG(D_VFSTRACE,
+ DFID" layout changed from %d to %d.\n",
+ PFID(lu_object_fid(&obj->co_lu)),
+ cio->cui_layout_gen, gen);
+ /* today successful restore is the only possible
+ * case */
+ /* restore was done, clear restoring state */
+ ll_i2info(ccc_object_inode(obj))->lli_flags &=
+ ~LLIF_FILE_RESTORING;
+ }
+ }
+}
+
+static void vvp_io_fault_fini(const struct lu_env *env,
+ const struct cl_io_slice *ios)
+{
+ struct cl_io *io = ios->cis_io;
+ struct cl_page *page = io->u.ci_fault.ft_page;
+
+ CLOBINVRNT(env, io->ci_obj, ccc_object_invariant(io->ci_obj));
+
+ if (page != NULL) {
+ lu_ref_del(&page->cp_reference, "fault", io);
+ cl_page_put(env, page);
+ io->u.ci_fault.ft_page = NULL;
+ }
+ vvp_io_fini(env, ios);
+}
+
+static enum cl_lock_mode vvp_mode_from_vma(struct vm_area_struct *vma)
+{
+ /*
+ * we only want to hold PW locks if the mmap() can generate
+ * writes back to the file and that only happens in shared
+ * writable vmas
+ */
+ if ((vma->vm_flags & VM_SHARED) && (vma->vm_flags & VM_WRITE))
+ return CLM_WRITE;
+ return CLM_READ;
+}
+
+static int vvp_mmap_locks(const struct lu_env *env,
+ struct ccc_io *vio, struct cl_io *io)
+{
+ struct ccc_thread_info *cti = ccc_env_info(env);
+ struct mm_struct *mm = current->mm;
+ struct vm_area_struct *vma;
+ struct cl_lock_descr *descr = &cti->cti_descr;
+ ldlm_policy_data_t policy;
+ unsigned long addr;
+ ssize_t count;
+ int result;
+ struct iov_iter i;
+ struct iovec iov;
+
+ LASSERT(io->ci_type == CIT_READ || io->ci_type == CIT_WRITE);
+
+ if (!cl_is_normalio(env, io))
+ return 0;
+
+ if (vio->cui_iter == NULL) /* nfs or loop back device write */
+ return 0;
+
+ /* No MM (e.g. NFS)? No vmas too. */
+ if (mm == NULL)
+ return 0;
+
+ iov_for_each(iov, i, *(vio->cui_iter)) {
+ addr = (unsigned long)iov.iov_base;
+ count = iov.iov_len;
+ if (count == 0)
+ continue;
+
+ count += addr & (~CFS_PAGE_MASK);
+ addr &= CFS_PAGE_MASK;
+
+ down_read(&mm->mmap_sem);
+ while ((vma = our_vma(mm, addr, count)) != NULL) {
+ struct inode *inode = file_inode(vma->vm_file);
+ int flags = CEF_MUST;
+
+ if (ll_file_nolock(vma->vm_file)) {
+ /*
+ * For no lock case, a lockless lock will be
+ * generated.
+ */
+ flags = CEF_NEVER;
+ }
+
+ /*
+ * XXX: Required lock mode can be weakened: CIT_WRITE
+ * io only ever reads user level buffer, and CIT_READ
+ * only writes on it.
+ */
+ policy_from_vma(&policy, vma, addr, count);
+ descr->cld_mode = vvp_mode_from_vma(vma);
+ descr->cld_obj = ll_i2info(inode)->lli_clob;
+ descr->cld_start = cl_index(descr->cld_obj,
+ policy.l_extent.start);
+ descr->cld_end = cl_index(descr->cld_obj,
+ policy.l_extent.end);
+ descr->cld_enq_flags = flags;
+ result = cl_io_lock_alloc_add(env, io, descr);
+
+ CDEBUG(D_VFSTRACE, "lock: %d: [%lu, %lu]\n",
+ descr->cld_mode, descr->cld_start,
+ descr->cld_end);
+
+ if (result < 0) {
+ up_read(&mm->mmap_sem);
+ return result;
+ }
+
+ if (vma->vm_end - addr >= count)
+ break;
+
+ count -= vma->vm_end - addr;
+ addr = vma->vm_end;
+ }
+ up_read(&mm->mmap_sem);
+ }
+ return 0;
+}
+
+static int vvp_io_rw_lock(const struct lu_env *env, struct cl_io *io,
+ enum cl_lock_mode mode, loff_t start, loff_t end)
+{
+ struct ccc_io *cio = ccc_env_io(env);
+ int result;
+ int ast_flags = 0;
+
+ LASSERT(io->ci_type == CIT_READ || io->ci_type == CIT_WRITE);
+
+ ccc_io_update_iov(env, cio, io);
+
+ if (io->u.ci_rw.crw_nonblock)
+ ast_flags |= CEF_NONBLOCK;
+ result = vvp_mmap_locks(env, cio, io);
+ if (result == 0)
+ result = ccc_io_one_lock(env, io, ast_flags, mode, start, end);
+ return result;
+}
+
+static int vvp_io_read_lock(const struct lu_env *env,
+ const struct cl_io_slice *ios)
+{
+ struct cl_io *io = ios->cis_io;
+ struct cl_io_rw_common *rd = &io->u.ci_rd.rd;
+ int result;
+
+ result = vvp_io_rw_lock(env, io, CLM_READ, rd->crw_pos,
+ rd->crw_pos + rd->crw_count - 1);
+
+ return result;
+}
+
+static int vvp_io_fault_lock(const struct lu_env *env,
+ const struct cl_io_slice *ios)
+{
+ struct cl_io *io = ios->cis_io;
+ struct vvp_io *vio = cl2vvp_io(env, ios);
+ /*
+ * XXX LDLM_FL_CBPENDING
+ */
+ return ccc_io_one_lock_index
+ (env, io, 0, vvp_mode_from_vma(vio->u.fault.ft_vma),
+ io->u.ci_fault.ft_index, io->u.ci_fault.ft_index);
+}
+
+static int vvp_io_write_lock(const struct lu_env *env,
+ const struct cl_io_slice *ios)
+{
+ struct cl_io *io = ios->cis_io;
+ loff_t start;
+ loff_t end;
+
+ if (io->u.ci_wr.wr_append) {
+ start = 0;
+ end = OBD_OBJECT_EOF;
+ } else {
+ start = io->u.ci_wr.wr.crw_pos;
+ end = start + io->u.ci_wr.wr.crw_count - 1;
+ }
+ return vvp_io_rw_lock(env, io, CLM_WRITE, start, end);
+}
+
+static int vvp_io_setattr_iter_init(const struct lu_env *env,
+ const struct cl_io_slice *ios)
+{
+ return 0;
+}
+
+/**
+ * Implementation of cl_io_operations::cio_lock() method for CIT_SETATTR io.
+ *
+ * Handles "lockless io" mode when extent locking is done by server.
+ */
+static int vvp_io_setattr_lock(const struct lu_env *env,
+ const struct cl_io_slice *ios)
+{
+ struct ccc_io *cio = ccc_env_io(env);
+ struct cl_io *io = ios->cis_io;
+ __u64 new_size;
+ __u32 enqflags = 0;
+
+ if (cl_io_is_trunc(io)) {
+ new_size = io->u.ci_setattr.sa_attr.lvb_size;
+ if (new_size == 0)
+ enqflags = CEF_DISCARD_DATA;
+ } else {
+ if ((io->u.ci_setattr.sa_attr.lvb_mtime >=
+ io->u.ci_setattr.sa_attr.lvb_ctime) ||
+ (io->u.ci_setattr.sa_attr.lvb_atime >=
+ io->u.ci_setattr.sa_attr.lvb_ctime))
+ return 0;
+ new_size = 0;
+ }
+ cio->u.setattr.cui_local_lock = SETATTR_EXTENT_LOCK;
+ return ccc_io_one_lock(env, io, enqflags, CLM_WRITE,
+ new_size, OBD_OBJECT_EOF);
+}
+
+static int vvp_do_vmtruncate(struct inode *inode, size_t size)
+{
+ int result;
+ /*
+ * Only ll_inode_size_lock is taken at this level.
+ */
+ ll_inode_size_lock(inode);
+ result = inode_newsize_ok(inode, size);
+ if (result < 0) {
+ ll_inode_size_unlock(inode);
+ return result;
+ }
+ truncate_setsize(inode, size);
+ ll_inode_size_unlock(inode);
+ return result;
+}
+
+static int vvp_io_setattr_trunc(const struct lu_env *env,
+ const struct cl_io_slice *ios,
+ struct inode *inode, loff_t size)
+{
+ inode_dio_wait(inode);
+ return 0;
+}
+
+static int vvp_io_setattr_time(const struct lu_env *env,
+ const struct cl_io_slice *ios)
+{
+ struct cl_io *io = ios->cis_io;
+ struct cl_object *obj = io->ci_obj;
+ struct cl_attr *attr = ccc_env_thread_attr(env);
+ int result;
+ unsigned valid = CAT_CTIME;
+
+ cl_object_attr_lock(obj);
+ attr->cat_ctime = io->u.ci_setattr.sa_attr.lvb_ctime;
+ if (io->u.ci_setattr.sa_valid & ATTR_ATIME_SET) {
+ attr->cat_atime = io->u.ci_setattr.sa_attr.lvb_atime;
+ valid |= CAT_ATIME;
+ }
+ if (io->u.ci_setattr.sa_valid & ATTR_MTIME_SET) {
+ attr->cat_mtime = io->u.ci_setattr.sa_attr.lvb_mtime;
+ valid |= CAT_MTIME;
+ }
+ result = cl_object_attr_set(env, obj, attr, valid);
+ cl_object_attr_unlock(obj);
+
+ return result;
+}
+
+static int vvp_io_setattr_start(const struct lu_env *env,
+ const struct cl_io_slice *ios)
+{
+ struct cl_io *io = ios->cis_io;
+ struct inode *inode = ccc_object_inode(io->ci_obj);
+ int result = 0;
+
+ mutex_lock(&inode->i_mutex);
+ if (cl_io_is_trunc(io))
+ result = vvp_io_setattr_trunc(env, ios, inode,
+ io->u.ci_setattr.sa_attr.lvb_size);
+ if (result == 0)
+ result = vvp_io_setattr_time(env, ios);
+ return result;
+}
+
+static void vvp_io_setattr_end(const struct lu_env *env,
+ const struct cl_io_slice *ios)
+{
+ struct cl_io *io = ios->cis_io;
+ struct inode *inode = ccc_object_inode(io->ci_obj);
+
+ if (cl_io_is_trunc(io)) {
+ /* Truncate in memory pages - they must be clean pages
+ * because osc has already notified to destroy osc_extents. */
+ vvp_do_vmtruncate(inode, io->u.ci_setattr.sa_attr.lvb_size);
+ inode_dio_write_done(inode);
+ }
+ mutex_unlock(&inode->i_mutex);
+}
+
+static void vvp_io_setattr_fini(const struct lu_env *env,
+ const struct cl_io_slice *ios)
+{
+ vvp_io_fini(env, ios);
+}
+
+static int vvp_io_read_start(const struct lu_env *env,
+ const struct cl_io_slice *ios)
+{
+ struct vvp_io *vio = cl2vvp_io(env, ios);
+ struct ccc_io *cio = cl2ccc_io(env, ios);
+ struct cl_io *io = ios->cis_io;
+ struct cl_object *obj = io->ci_obj;
+ struct inode *inode = ccc_object_inode(obj);
+ struct ll_ra_read *bead = &vio->cui_bead;
+ struct file *file = cio->cui_fd->fd_file;
+
+ int result;
+ loff_t pos = io->u.ci_rd.rd.crw_pos;
+ long cnt = io->u.ci_rd.rd.crw_count;
+ long tot = cio->cui_tot_count;
+ int exceed = 0;
+
+ CLOBINVRNT(env, obj, ccc_object_invariant(obj));
+
+ CDEBUG(D_VFSTRACE, "read: -> [%lli, %lli)\n", pos, pos + cnt);
+
+ if (!can_populate_pages(env, io, inode))
+ return 0;
+
+ result = ccc_prep_size(env, obj, io, pos, tot, &exceed);
+ if (result != 0)
+ return result;
+ else if (exceed != 0)
+ goto out;
+
+ LU_OBJECT_HEADER(D_INODE, env, &obj->co_lu,
+ "Read ino %lu, %lu bytes, offset %lld, size %llu\n",
+ inode->i_ino, cnt, pos, i_size_read(inode));
+
+ /* turn off the kernel's read-ahead */
+ cio->cui_fd->fd_file->f_ra.ra_pages = 0;
+
+ /* initialize read-ahead window once per syscall */
+ if (!vio->cui_ra_window_set) {
+ vio->cui_ra_window_set = 1;
+ bead->lrr_start = cl_index(obj, pos);
+ /*
+ * XXX: explicit PAGE_CACHE_SIZE
+ */
+ bead->lrr_count = cl_index(obj, tot + PAGE_CACHE_SIZE - 1);
+ ll_ra_read_in(file, bead);
+ }
+
+ /* BUG: 5972 */
+ file_accessed(file);
+ switch (vio->cui_io_subtype) {
+ case IO_NORMAL:
+ LASSERT(cio->cui_iocb->ki_pos == pos);
+ result = generic_file_read_iter(cio->cui_iocb, cio->cui_iter);
+ break;
+ case IO_SPLICE:
+ result = generic_file_splice_read(file, &pos,
+ vio->u.splice.cui_pipe, cnt,
+ vio->u.splice.cui_flags);
+ /* LU-1109: do splice read stripe by stripe otherwise if it
+ * may make nfsd stuck if this read occupied all internal pipe
+ * buffers. */
+ io->ci_continue = 0;
+ break;
+ default:
+ CERROR("Wrong IO type %u\n", vio->cui_io_subtype);
+ LBUG();
+ }
+
+out:
+ if (result >= 0) {
+ if (result < cnt)
+ io->ci_continue = 0;
+ io->ci_nob += result;
+ ll_rw_stats_tally(ll_i2sbi(inode), current->pid,
+ cio->cui_fd, pos, result, READ);
+ result = 0;
+ }
+ return result;
+}
+
+static void vvp_io_read_fini(const struct lu_env *env, const struct cl_io_slice *ios)
+{
+ struct vvp_io *vio = cl2vvp_io(env, ios);
+ struct ccc_io *cio = cl2ccc_io(env, ios);
+
+ if (vio->cui_ra_window_set)
+ ll_ra_read_ex(cio->cui_fd->fd_file, &vio->cui_bead);
+
+ vvp_io_fini(env, ios);
+}
+
+static int vvp_io_write_start(const struct lu_env *env,
+ const struct cl_io_slice *ios)
+{
+ struct ccc_io *cio = cl2ccc_io(env, ios);
+ struct cl_io *io = ios->cis_io;
+ struct cl_object *obj = io->ci_obj;
+ struct inode *inode = ccc_object_inode(obj);
+ ssize_t result = 0;
+ loff_t pos = io->u.ci_wr.wr.crw_pos;
+ size_t cnt = io->u.ci_wr.wr.crw_count;
+
+ if (!can_populate_pages(env, io, inode))
+ return 0;
+
+ if (cl_io_is_append(io)) {
+ /*
+ * PARALLEL IO This has to be changed for parallel IO doing
+ * out-of-order writes.
+ */
+ pos = io->u.ci_wr.wr.crw_pos = i_size_read(inode);
+ cio->cui_iocb->ki_pos = pos;
+ } else {
+ LASSERT(cio->cui_iocb->ki_pos == pos);
+ }
+
+ CDEBUG(D_VFSTRACE, "write: [%lli, %lli)\n", pos, pos + (long long)cnt);
+
+ if (cio->cui_iter == NULL) /* from a temp io in ll_cl_init(). */
+ result = 0;
+ else
+ result = generic_file_write_iter(cio->cui_iocb, cio->cui_iter);
+
+ if (result > 0) {
+ if (result < cnt)
+ io->ci_continue = 0;
+ io->ci_nob += result;
+ ll_rw_stats_tally(ll_i2sbi(inode), current->pid,
+ cio->cui_fd, pos, result, WRITE);
+ result = 0;
+ }
+ return result;
+}
+
+static int vvp_io_kernel_fault(struct vvp_fault_io *cfio)
+{
+ struct vm_fault *vmf = cfio->fault.ft_vmf;
+
+ cfio->fault.ft_flags = filemap_fault(cfio->ft_vma, vmf);
+ cfio->fault.ft_flags_valid = 1;
+
+ if (vmf->page) {
+ CDEBUG(D_PAGE,
+ "page %p map %p index %lu flags %lx count %u priv %0lx: got addr %p type NOPAGE\n",
+ vmf->page, vmf->page->mapping, vmf->page->index,
+ (long)vmf->page->flags, page_count(vmf->page),
+ page_private(vmf->page), vmf->virtual_address);
+ if (unlikely(!(cfio->fault.ft_flags & VM_FAULT_LOCKED))) {
+ lock_page(vmf->page);
+ cfio->fault.ft_flags |= VM_FAULT_LOCKED;
+ }
+
+ cfio->ft_vmpage = vmf->page;
+ return 0;
+ }
+
+ if (cfio->fault.ft_flags & (VM_FAULT_SIGBUS | VM_FAULT_SIGSEGV)) {
+ CDEBUG(D_PAGE, "got addr %p - SIGBUS\n", vmf->virtual_address);
+ return -EFAULT;
+ }
+
+ if (cfio->fault.ft_flags & VM_FAULT_OOM) {
+ CDEBUG(D_PAGE, "got addr %p - OOM\n", vmf->virtual_address);
+ return -ENOMEM;
+ }
+
+ if (cfio->fault.ft_flags & VM_FAULT_RETRY)
+ return -EAGAIN;
+
+ CERROR("Unknown error in page fault %d!\n", cfio->fault.ft_flags);
+ return -EINVAL;
+}
+
+
+static int vvp_io_fault_start(const struct lu_env *env,
+ const struct cl_io_slice *ios)
+{
+ struct vvp_io *vio = cl2vvp_io(env, ios);
+ struct cl_io *io = ios->cis_io;
+ struct cl_object *obj = io->ci_obj;
+ struct inode *inode = ccc_object_inode(obj);
+ struct cl_fault_io *fio = &io->u.ci_fault;
+ struct vvp_fault_io *cfio = &vio->u.fault;
+ loff_t offset;
+ int result = 0;
+ struct page *vmpage = NULL;
+ struct cl_page *page;
+ loff_t size;
+ pgoff_t last; /* last page in a file data region */
+
+ if (fio->ft_executable &&
+ LTIME_S(inode->i_mtime) != vio->u.fault.ft_mtime)
+ CWARN("binary "DFID
+ " changed while waiting for the page fault lock\n",
+ PFID(lu_object_fid(&obj->co_lu)));
+
+ /* offset of the last byte on the page */
+ offset = cl_offset(obj, fio->ft_index + 1) - 1;
+ LASSERT(cl_index(obj, offset) == fio->ft_index);
+ result = ccc_prep_size(env, obj, io, 0, offset + 1, NULL);
+ if (result != 0)
+ return result;
+
+ /* must return locked page */
+ if (fio->ft_mkwrite) {
+ LASSERT(cfio->ft_vmpage != NULL);
+ lock_page(cfio->ft_vmpage);
+ } else {
+ result = vvp_io_kernel_fault(cfio);
+ if (result != 0)
+ return result;
+ }
+
+ vmpage = cfio->ft_vmpage;
+ LASSERT(PageLocked(vmpage));
+
+ if (OBD_FAIL_CHECK(OBD_FAIL_LLITE_FAULT_TRUNC_RACE))
+ ll_invalidate_page(vmpage);
+
+ size = i_size_read(inode);
+ /* Though we have already held a cl_lock upon this page, but
+ * it still can be truncated locally. */
+ if (unlikely((vmpage->mapping != inode->i_mapping) ||
+ (page_offset(vmpage) > size))) {
+ CDEBUG(D_PAGE, "llite: fault and truncate race happened!\n");
+
+ /* return +1 to stop cl_io_loop() and ll_fault() will catch
+ * and retry. */
+ result = +1;
+ goto out;
+ }
+
+
+ if (fio->ft_mkwrite) {
+ pgoff_t last_index;
+ /*
+ * Capture the size while holding the lli_trunc_sem from above
+ * we want to make sure that we complete the mkwrite action
+ * while holding this lock. We need to make sure that we are
+ * not past the end of the file.
+ */
+ last_index = cl_index(obj, size - 1);
+ if (last_index < fio->ft_index) {
+ CDEBUG(D_PAGE,
+ "llite: mkwrite and truncate race happened: %p: 0x%lx 0x%lx\n",
+ vmpage->mapping, fio->ft_index, last_index);
+ /*
+ * We need to return if we are
+ * passed the end of the file. This will propagate
+ * up the call stack to ll_page_mkwrite where
+ * we will return VM_FAULT_NOPAGE. Any non-negative
+ * value returned here will be silently
+ * converted to 0. If the vmpage->mapping is null
+ * the error code would be converted back to ENODATA
+ * in ll_page_mkwrite0. Thus we return -ENODATA
+ * to handle both cases
+ */
+ result = -ENODATA;
+ goto out;
+ }
+ }
+
+ page = cl_page_find(env, obj, fio->ft_index, vmpage, CPT_CACHEABLE);
+ if (IS_ERR(page)) {
+ result = PTR_ERR(page);
+ goto out;
+ }
+
+ /* if page is going to be written, we should add this page into cache
+ * earlier. */
+ if (fio->ft_mkwrite) {
+ wait_on_page_writeback(vmpage);
+ if (set_page_dirty(vmpage)) {
+ struct ccc_page *cp;
+
+ /* vvp_page_assume() calls wait_on_page_writeback(). */
+ cl_page_assume(env, io, page);
+
+ cp = cl2ccc_page(cl_page_at(page, &vvp_device_type));
+ vvp_write_pending(cl2ccc(obj), cp);
+
+ /* Do not set Dirty bit here so that in case IO is
+ * started before the page is really made dirty, we
+ * still have chance to detect it. */
+ result = cl_page_cache_add(env, io, page, CRT_WRITE);
+ LASSERT(cl_page_is_owned(page, io));
+
+ vmpage = NULL;
+ if (result < 0) {
+ cl_page_unmap(env, io, page);
+ cl_page_discard(env, io, page);
+ cl_page_disown(env, io, page);
+
+ cl_page_put(env, page);
+
+ /* we're in big trouble, what can we do now? */
+ if (result == -EDQUOT)
+ result = -ENOSPC;
+ goto out;
+ } else
+ cl_page_disown(env, io, page);
+ }
+ }
+
+ last = cl_index(obj, size - 1);
+ /*
+ * The ft_index is only used in the case of
+ * a mkwrite action. We need to check
+ * our assertions are correct, since
+ * we should have caught this above
+ */
+ LASSERT(!fio->ft_mkwrite || fio->ft_index <= last);
+ if (fio->ft_index == last)
+ /*
+ * Last page is mapped partially.
+ */
+ fio->ft_nob = size - cl_offset(obj, fio->ft_index);
+ else
+ fio->ft_nob = cl_page_size(obj);
+
+ lu_ref_add(&page->cp_reference, "fault", io);
+ fio->ft_page = page;
+
+out:
+ /* return unlocked vmpage to avoid deadlocking */
+ if (vmpage != NULL)
+ unlock_page(vmpage);
+ cfio->fault.ft_flags &= ~VM_FAULT_LOCKED;
+ return result;
+}
+
+static int vvp_io_fsync_start(const struct lu_env *env,
+ const struct cl_io_slice *ios)
+{
+ /* we should mark TOWRITE bit to each dirty page in radix tree to
+ * verify pages have been written, but this is difficult because of
+ * race. */
+ return 0;
+}
+
+static int vvp_io_read_page(const struct lu_env *env,
+ const struct cl_io_slice *ios,
+ const struct cl_page_slice *slice)
+{
+ struct cl_io *io = ios->cis_io;
+ struct cl_object *obj = slice->cpl_obj;
+ struct ccc_page *cp = cl2ccc_page(slice);
+ struct cl_page *page = slice->cpl_page;
+ struct inode *inode = ccc_object_inode(obj);
+ struct ll_sb_info *sbi = ll_i2sbi(inode);
+ struct ll_file_data *fd = cl2ccc_io(env, ios)->cui_fd;
+ struct ll_readahead_state *ras = &fd->fd_ras;
+ struct page *vmpage = cp->cpg_page;
+ struct cl_2queue *queue = &io->ci_queue;
+ int rc;
+
+ CLOBINVRNT(env, obj, ccc_object_invariant(obj));
+ LASSERT(slice->cpl_obj == obj);
+
+ if (sbi->ll_ra_info.ra_max_pages_per_file &&
+ sbi->ll_ra_info.ra_max_pages)
+ ras_update(sbi, inode, ras, page->cp_index,
+ cp->cpg_defer_uptodate);
+
+ /* Sanity check whether the page is protected by a lock. */
+ rc = cl_page_is_under_lock(env, io, page);
+ if (rc != -EBUSY) {
+ CL_PAGE_HEADER(D_WARNING, env, page, "%s: %d\n",
+ rc == -ENODATA ? "without a lock" :
+ "match failed", rc);
+ if (rc != -ENODATA)
+ return rc;
+ }
+
+ if (cp->cpg_defer_uptodate) {
+ cp->cpg_ra_used = 1;
+ cl_page_export(env, page, 1);
+ }
+ /*
+ * Add page into the queue even when it is marked uptodate above.
+ * this will unlock it automatically as part of cl_page_list_disown().
+ */
+ cl_2queue_add(queue, page);
+ if (sbi->ll_ra_info.ra_max_pages_per_file &&
+ sbi->ll_ra_info.ra_max_pages)
+ ll_readahead(env, io, ras,
+ vmpage->mapping, &queue->c2_qin, fd->fd_flags);
+
+ return 0;
+}
+
+static int vvp_page_sync_io(const struct lu_env *env, struct cl_io *io,
+ struct cl_page *page, struct ccc_page *cp,
+ enum cl_req_type crt)
+{
+ struct cl_2queue *queue;
+ int result;
+
+ LASSERT(io->ci_type == CIT_READ || io->ci_type == CIT_WRITE);
+
+ queue = &io->ci_queue;
+ cl_2queue_init_page(queue, page);
+
+ result = cl_io_submit_sync(env, io, crt, queue, 0);
+ LASSERT(cl_page_is_owned(page, io));
+
+ if (crt == CRT_READ)
+ /*
+ * in CRT_WRITE case page is left locked even in case of
+ * error.
+ */
+ cl_page_list_disown(env, io, &queue->c2_qin);
+ cl_2queue_fini(env, queue);
+
+ return result;
+}
+
+/**
+ * Prepare partially written-to page for a write.
+ */
+static int vvp_io_prepare_partial(const struct lu_env *env, struct cl_io *io,
+ struct cl_object *obj, struct cl_page *pg,
+ struct ccc_page *cp,
+ unsigned from, unsigned to)
+{
+ struct cl_attr *attr = ccc_env_thread_attr(env);
+ loff_t offset = cl_offset(obj, pg->cp_index);
+ int result;
+
+ cl_object_attr_lock(obj);
+ result = cl_object_attr_get(env, obj, attr);
+ cl_object_attr_unlock(obj);
+ if (result == 0) {
+ /*
+ * If are writing to a new page, no need to read old data.
+ * The extent locking will have updated the KMS, and for our
+ * purposes here we can treat it like i_size.
+ */
+ if (attr->cat_kms <= offset) {
+ char *kaddr = kmap_atomic(cp->cpg_page);
+
+ memset(kaddr, 0, cl_page_size(obj));
+ kunmap_atomic(kaddr);
+ } else if (cp->cpg_defer_uptodate)
+ cp->cpg_ra_used = 1;
+ else
+ result = vvp_page_sync_io(env, io, pg, cp, CRT_READ);
+ /*
+ * In older implementations, obdo_refresh_inode is called here
+ * to update the inode because the write might modify the
+ * object info at OST. However, this has been proven useless,
+ * since LVB functions will be called when user space program
+ * tries to retrieve inode attribute. Also, see bug 15909 for
+ * details. -jay
+ */
+ if (result == 0)
+ cl_page_export(env, pg, 1);
+ }
+ return result;
+}
+
+static int vvp_io_prepare_write(const struct lu_env *env,
+ const struct cl_io_slice *ios,
+ const struct cl_page_slice *slice,
+ unsigned from, unsigned to)
+{
+ struct cl_object *obj = slice->cpl_obj;
+ struct ccc_page *cp = cl2ccc_page(slice);
+ struct cl_page *pg = slice->cpl_page;
+ struct page *vmpage = cp->cpg_page;
+
+ int result;
+
+ LINVRNT(cl_page_is_vmlocked(env, pg));
+ LASSERT(vmpage->mapping->host == ccc_object_inode(obj));
+
+ result = 0;
+
+ CL_PAGE_HEADER(D_PAGE, env, pg, "preparing: [%d, %d]\n", from, to);
+ if (!PageUptodate(vmpage)) {
+ /*
+ * We're completely overwriting an existing page, so _don't_
+ * set it up to date until commit_write
+ */
+ if (from == 0 && to == PAGE_CACHE_SIZE) {
+ CL_PAGE_HEADER(D_PAGE, env, pg, "full page write\n");
+ POISON_PAGE(page, 0x11);
+ } else
+ result = vvp_io_prepare_partial(env, ios->cis_io, obj,
+ pg, cp, from, to);
+ } else
+ CL_PAGE_HEADER(D_PAGE, env, pg, "uptodate\n");
+ return result;
+}
+
+static int vvp_io_commit_write(const struct lu_env *env,
+ const struct cl_io_slice *ios,
+ const struct cl_page_slice *slice,
+ unsigned from, unsigned to)
+{
+ struct cl_object *obj = slice->cpl_obj;
+ struct cl_io *io = ios->cis_io;
+ struct ccc_page *cp = cl2ccc_page(slice);
+ struct cl_page *pg = slice->cpl_page;
+ struct inode *inode = ccc_object_inode(obj);
+ struct ll_sb_info *sbi = ll_i2sbi(inode);
+ struct ll_inode_info *lli = ll_i2info(inode);
+ struct page *vmpage = cp->cpg_page;
+
+ int result;
+ int tallyop;
+ loff_t size;
+
+ LINVRNT(cl_page_is_vmlocked(env, pg));
+ LASSERT(vmpage->mapping->host == inode);
+
+ LU_OBJECT_HEADER(D_INODE, env, &obj->co_lu, "committing page write\n");
+ CL_PAGE_HEADER(D_PAGE, env, pg, "committing: [%d, %d]\n", from, to);
+
+ /*
+ * queue a write for some time in the future the first time we
+ * dirty the page.
+ *
+ * This is different from what other file systems do: they usually
+ * just mark page (and some of its buffers) dirty and rely on
+ * balance_dirty_pages() to start a write-back. Lustre wants write-back
+ * to be started earlier for the following reasons:
+ *
+ * (1) with a large number of clients we need to limit the amount
+ * of cached data on the clients a lot;
+ *
+ * (2) large compute jobs generally want compute-only then io-only
+ * and the IO should complete as quickly as possible;
+ *
+ * (3) IO is batched up to the RPC size and is async until the
+ * client max cache is hit
+ * (/proc/fs/lustre/osc/OSC.../max_dirty_mb)
+ *
+ */
+ if (!PageDirty(vmpage)) {
+ tallyop = LPROC_LL_DIRTY_MISSES;
+ result = cl_page_cache_add(env, io, pg, CRT_WRITE);
+ if (result == 0) {
+ /* page was added into cache successfully. */
+ set_page_dirty(vmpage);
+ vvp_write_pending(cl2ccc(obj), cp);
+ } else if (result == -EDQUOT) {
+ pgoff_t last_index = i_size_read(inode) >> PAGE_CACHE_SHIFT;
+ bool need_clip = true;
+
+ /*
+ * Client ran out of disk space grant. Possible
+ * strategies are:
+ *
+ * (a) do a sync write, renewing grant;
+ *
+ * (b) stop writing on this stripe, switch to the
+ * next one.
+ *
+ * (b) is a part of "parallel io" design that is the
+ * ultimate goal. (a) is what "old" client did, and
+ * what the new code continues to do for the time
+ * being.
+ */
+ if (last_index > pg->cp_index) {
+ to = PAGE_CACHE_SIZE;
+ need_clip = false;
+ } else if (last_index == pg->cp_index) {
+ int size_to = i_size_read(inode) & ~CFS_PAGE_MASK;
+ if (to < size_to)
+ to = size_to;
+ }
+ if (need_clip)
+ cl_page_clip(env, pg, 0, to);
+ result = vvp_page_sync_io(env, io, pg, cp, CRT_WRITE);
+ if (result)
+ CERROR("Write page %lu of inode %p failed %d\n",
+ pg->cp_index, inode, result);
+ }
+ } else {
+ tallyop = LPROC_LL_DIRTY_HITS;
+ result = 0;
+ }
+ ll_stats_ops_tally(sbi, tallyop, 1);
+
+ /* Inode should be marked DIRTY even if no new page was marked DIRTY
+ * because page could have been not flushed between 2 modifications.
+ * It is important the file is marked DIRTY as soon as the I/O is done
+ * Indeed, when cache is flushed, file could be already closed and it
+ * is too late to warn the MDT.
+ * It is acceptable that file is marked DIRTY even if I/O is dropped
+ * for some reasons before being flushed to OST.
+ */
+ if (result == 0) {
+ spin_lock(&lli->lli_lock);
+ lli->lli_flags |= LLIF_DATA_MODIFIED;
+ spin_unlock(&lli->lli_lock);
+ }
+
+ size = cl_offset(obj, pg->cp_index) + to;
+
+ ll_inode_size_lock(inode);
+ if (result == 0) {
+ if (size > i_size_read(inode)) {
+ cl_isize_write_nolock(inode, size);
+ CDEBUG(D_VFSTRACE, DFID" updating i_size %lu\n",
+ PFID(lu_object_fid(&obj->co_lu)),
+ (unsigned long)size);
+ }
+ cl_page_export(env, pg, 1);
+ } else {
+ if (size > i_size_read(inode))
+ cl_page_discard(env, io, pg);
+ }
+ ll_inode_size_unlock(inode);
+ return result;
+}
+
+static const struct cl_io_operations vvp_io_ops = {
+ .op = {
+ [CIT_READ] = {
+ .cio_fini = vvp_io_read_fini,
+ .cio_lock = vvp_io_read_lock,
+ .cio_start = vvp_io_read_start,
+ .cio_advance = ccc_io_advance
+ },
+ [CIT_WRITE] = {
+ .cio_fini = vvp_io_fini,
+ .cio_lock = vvp_io_write_lock,
+ .cio_start = vvp_io_write_start,
+ .cio_advance = ccc_io_advance
+ },
+ [CIT_SETATTR] = {
+ .cio_fini = vvp_io_setattr_fini,
+ .cio_iter_init = vvp_io_setattr_iter_init,
+ .cio_lock = vvp_io_setattr_lock,
+ .cio_start = vvp_io_setattr_start,
+ .cio_end = vvp_io_setattr_end
+ },
+ [CIT_FAULT] = {
+ .cio_fini = vvp_io_fault_fini,
+ .cio_iter_init = vvp_io_fault_iter_init,
+ .cio_lock = vvp_io_fault_lock,
+ .cio_start = vvp_io_fault_start,
+ .cio_end = ccc_io_end
+ },
+ [CIT_FSYNC] = {
+ .cio_start = vvp_io_fsync_start,
+ .cio_fini = vvp_io_fini
+ },
+ [CIT_MISC] = {
+ .cio_fini = vvp_io_fini
+ }
+ },
+ .cio_read_page = vvp_io_read_page,
+ .cio_prepare_write = vvp_io_prepare_write,
+ .cio_commit_write = vvp_io_commit_write
+};
+
+int vvp_io_init(const struct lu_env *env, struct cl_object *obj,
+ struct cl_io *io)
+{
+ struct vvp_io *vio = vvp_env_io(env);
+ struct ccc_io *cio = ccc_env_io(env);
+ struct inode *inode = ccc_object_inode(obj);
+ int result;
+
+ CLOBINVRNT(env, obj, ccc_object_invariant(obj));
+
+ CDEBUG(D_VFSTRACE, DFID
+ " ignore/verify layout %d/%d, layout version %d restore needed %d\n",
+ PFID(lu_object_fid(&obj->co_lu)),
+ io->ci_ignore_layout, io->ci_verify_layout,
+ cio->cui_layout_gen, io->ci_restore_needed);
+
+ CL_IO_SLICE_CLEAN(cio, cui_cl);
+ cl_io_slice_add(io, &cio->cui_cl, obj, &vvp_io_ops);
+ vio->cui_ra_window_set = 0;
+ result = 0;
+ if (io->ci_type == CIT_READ || io->ci_type == CIT_WRITE) {
+ size_t count;
+ struct ll_inode_info *lli = ll_i2info(inode);
+
+ count = io->u.ci_rw.crw_count;
+ /* "If nbyte is 0, read() will return 0 and have no other
+ * results." -- Single Unix Spec */
+ if (count == 0)
+ result = 1;
+ else
+ cio->cui_tot_count = count;
+
+ /* for read/write, we store the jobid in the inode, and
+ * it'll be fetched by osc when building RPC.
+ *
+ * it's not accurate if the file is shared by different
+ * jobs.
+ */
+ lustre_get_jobid(lli->lli_jobid);
+ } else if (io->ci_type == CIT_SETATTR) {
+ if (!cl_io_is_trunc(io))
+ io->ci_lockreq = CILR_MANDATORY;
+ }
+
+ /* ignore layout change for generic CIT_MISC but not for glimpse.
+ * io context for glimpse must set ci_verify_layout to true,
+ * see cl_glimpse_size0() for details. */
+ if (io->ci_type == CIT_MISC && !io->ci_verify_layout)
+ io->ci_ignore_layout = 1;
+
+ /* Enqueue layout lock and get layout version. We need to do this
+ * even for operations requiring to open file, such as read and write,
+ * because it might not grant layout lock in IT_OPEN. */
+ if (result == 0 && !io->ci_ignore_layout) {
+ result = ll_layout_refresh(inode, &cio->cui_layout_gen);
+ if (result == -ENOENT)
+ /* If the inode on MDS has been removed, but the objects
+ * on OSTs haven't been destroyed (async unlink), layout
+ * fetch will return -ENOENT, we'd ignore this error
+ * and continue with dirty flush. LU-3230. */
+ result = 0;
+ if (result < 0)
+ CERROR("%s: refresh file layout " DFID " error %d.\n",
+ ll_get_fsname(inode->i_sb, NULL, 0),
+ PFID(lu_object_fid(&obj->co_lu)), result);
+ }
+
+ return result;
+}
+
+static struct vvp_io *cl2vvp_io(const struct lu_env *env,
+ const struct cl_io_slice *slice)
+{
+ /* Calling just for assertion */
+ cl2ccc_io(env, slice);
+ return vvp_env_io(env);
+}
diff --git a/drivers/staging/lustre/lustre/llite/vvp_lock.c b/drivers/staging/lustre/lustre/llite/vvp_lock.c
new file mode 100644
index 000000000..f354e82d4
--- /dev/null
+++ b/drivers/staging/lustre/lustre/llite/vvp_lock.c
@@ -0,0 +1,85 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ *
+ * Implementation of cl_lock for VVP layer.
+ *
+ * Author: Nikita Danilov <nikita.danilov@sun.com>
+ */
+
+#define DEBUG_SUBSYSTEM S_LLITE
+
+
+#include "../include/obd.h"
+#include "../include/lustre_lite.h"
+
+#include "vvp_internal.h"
+
+/*****************************************************************************
+ *
+ * Vvp lock functions.
+ *
+ */
+
+/**
+ * Estimates lock value for the purpose of managing the lock cache during
+ * memory shortages.
+ *
+ * Locks for memory mapped files are almost infinitely precious, others are
+ * junk. "Mapped locks" are heavy, but not infinitely heavy, so that they are
+ * ordered within themselves by weights assigned from other layers.
+ */
+static unsigned long vvp_lock_weigh(const struct lu_env *env,
+ const struct cl_lock_slice *slice)
+{
+ struct ccc_object *cob = cl2ccc(slice->cls_obj);
+
+ return atomic_read(&cob->cob_mmap_cnt) > 0 ? ~0UL >> 2 : 0;
+}
+
+static const struct cl_lock_operations vvp_lock_ops = {
+ .clo_delete = ccc_lock_delete,
+ .clo_fini = ccc_lock_fini,
+ .clo_enqueue = ccc_lock_enqueue,
+ .clo_wait = ccc_lock_wait,
+ .clo_use = ccc_lock_use,
+ .clo_unuse = ccc_lock_unuse,
+ .clo_fits_into = ccc_lock_fits_into,
+ .clo_state = ccc_lock_state,
+ .clo_weigh = vvp_lock_weigh
+};
+
+int vvp_lock_init(const struct lu_env *env, struct cl_object *obj,
+ struct cl_lock *lock, const struct cl_io *io)
+{
+ return ccc_lock_init(env, obj, lock, io, &vvp_lock_ops);
+}
diff --git a/drivers/staging/lustre/lustre/llite/vvp_object.c b/drivers/staging/lustre/lustre/llite/vvp_object.c
new file mode 100644
index 000000000..b6f6d4cb6
--- /dev/null
+++ b/drivers/staging/lustre/lustre/llite/vvp_object.c
@@ -0,0 +1,201 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2012, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ *
+ * cl_object implementation for VVP layer.
+ *
+ * Author: Nikita Danilov <nikita.danilov@sun.com>
+ */
+
+#define DEBUG_SUBSYSTEM S_LLITE
+
+
+#include "../../include/linux/libcfs/libcfs.h"
+
+#include "../include/obd.h"
+#include "../include/lustre_lite.h"
+
+#include "vvp_internal.h"
+
+/*****************************************************************************
+ *
+ * Object operations.
+ *
+ */
+
+static int vvp_object_print(const struct lu_env *env, void *cookie,
+ lu_printer_t p, const struct lu_object *o)
+{
+ struct ccc_object *obj = lu2ccc(o);
+ struct inode *inode = obj->cob_inode;
+ struct ll_inode_info *lli;
+
+ (*p)(env, cookie, "(%s %d %d) inode: %p ",
+ list_empty(&obj->cob_pending_list) ? "-" : "+",
+ obj->cob_transient_pages, atomic_read(&obj->cob_mmap_cnt),
+ inode);
+ if (inode) {
+ lli = ll_i2info(inode);
+ (*p)(env, cookie, "%lu/%u %o %u %d %p "DFID,
+ inode->i_ino, inode->i_generation, inode->i_mode,
+ inode->i_nlink, atomic_read(&inode->i_count),
+ lli->lli_clob, PFID(&lli->lli_fid));
+ }
+ return 0;
+}
+
+static int vvp_attr_get(const struct lu_env *env, struct cl_object *obj,
+ struct cl_attr *attr)
+{
+ struct inode *inode = ccc_object_inode(obj);
+
+ /*
+ * lov overwrites most of these fields in
+ * lov_attr_get()->...lov_merge_lvb_kms(), except when inode
+ * attributes are newer.
+ */
+
+ attr->cat_size = i_size_read(inode);
+ attr->cat_mtime = LTIME_S(inode->i_mtime);
+ attr->cat_atime = LTIME_S(inode->i_atime);
+ attr->cat_ctime = LTIME_S(inode->i_ctime);
+ attr->cat_blocks = inode->i_blocks;
+ attr->cat_uid = from_kuid(&init_user_ns, inode->i_uid);
+ attr->cat_gid = from_kgid(&init_user_ns, inode->i_gid);
+ /* KMS is not known by this layer */
+ return 0; /* layers below have to fill in the rest */
+}
+
+static int vvp_attr_set(const struct lu_env *env, struct cl_object *obj,
+ const struct cl_attr *attr, unsigned valid)
+{
+ struct inode *inode = ccc_object_inode(obj);
+
+ if (valid & CAT_UID)
+ inode->i_uid = make_kuid(&init_user_ns, attr->cat_uid);
+ if (valid & CAT_GID)
+ inode->i_gid = make_kgid(&init_user_ns, attr->cat_gid);
+ if (valid & CAT_ATIME)
+ LTIME_S(inode->i_atime) = attr->cat_atime;
+ if (valid & CAT_MTIME)
+ LTIME_S(inode->i_mtime) = attr->cat_mtime;
+ if (valid & CAT_CTIME)
+ LTIME_S(inode->i_ctime) = attr->cat_ctime;
+ if (0 && valid & CAT_SIZE)
+ cl_isize_write_nolock(inode, attr->cat_size);
+ /* not currently necessary */
+ if (0 && valid & (CAT_UID|CAT_GID|CAT_SIZE))
+ mark_inode_dirty(inode);
+ return 0;
+}
+
+static int vvp_conf_set(const struct lu_env *env, struct cl_object *obj,
+ const struct cl_object_conf *conf)
+{
+ struct ll_inode_info *lli = ll_i2info(conf->coc_inode);
+
+ if (conf->coc_opc == OBJECT_CONF_INVALIDATE) {
+ CDEBUG(D_VFSTRACE, DFID ": losing layout lock\n",
+ PFID(&lli->lli_fid));
+
+ ll_layout_version_set(lli, LL_LAYOUT_GEN_NONE);
+
+ /* Clean up page mmap for this inode.
+ * The reason for us to do this is that if the page has
+ * already been installed into memory space, the process
+ * can access it without interacting with lustre, so this
+ * page may be stale due to layout change, and the process
+ * will never be notified.
+ * This operation is expensive but mmap processes have to pay
+ * a price themselves. */
+ unmap_mapping_range(conf->coc_inode->i_mapping,
+ 0, OBD_OBJECT_EOF, 0);
+
+ return 0;
+ }
+
+ if (conf->coc_opc != OBJECT_CONF_SET)
+ return 0;
+
+ if (conf->u.coc_md != NULL && conf->u.coc_md->lsm != NULL) {
+ CDEBUG(D_VFSTRACE, DFID ": layout version change: %u -> %u\n",
+ PFID(&lli->lli_fid), lli->lli_layout_gen,
+ conf->u.coc_md->lsm->lsm_layout_gen);
+
+ lli->lli_has_smd = lsm_has_objects(conf->u.coc_md->lsm);
+ ll_layout_version_set(lli, conf->u.coc_md->lsm->lsm_layout_gen);
+ } else {
+ CDEBUG(D_VFSTRACE, DFID ": layout nuked: %u.\n",
+ PFID(&lli->lli_fid), lli->lli_layout_gen);
+
+ lli->lli_has_smd = false;
+ ll_layout_version_set(lli, LL_LAYOUT_GEN_EMPTY);
+ }
+ return 0;
+}
+
+static const struct cl_object_operations vvp_ops = {
+ .coo_page_init = vvp_page_init,
+ .coo_lock_init = vvp_lock_init,
+ .coo_io_init = vvp_io_init,
+ .coo_attr_get = vvp_attr_get,
+ .coo_attr_set = vvp_attr_set,
+ .coo_conf_set = vvp_conf_set,
+ .coo_glimpse = ccc_object_glimpse
+};
+
+static const struct lu_object_operations vvp_lu_obj_ops = {
+ .loo_object_init = ccc_object_init,
+ .loo_object_free = ccc_object_free,
+ .loo_object_print = vvp_object_print
+};
+
+struct ccc_object *cl_inode2ccc(struct inode *inode)
+{
+ struct cl_inode_info *lli = cl_i2info(inode);
+ struct cl_object *obj = lli->lli_clob;
+ struct lu_object *lu;
+
+ LASSERT(obj != NULL);
+ lu = lu_object_locate(obj->co_lu.lo_header, &vvp_device_type);
+ LASSERT(lu != NULL);
+ return lu2ccc(lu);
+}
+
+struct lu_object *vvp_object_alloc(const struct lu_env *env,
+ const struct lu_object_header *hdr,
+ struct lu_device *dev)
+{
+ return ccc_object_alloc(env, hdr, dev, &vvp_ops, &vvp_lu_obj_ops);
+}
diff --git a/drivers/staging/lustre/lustre/llite/vvp_page.c b/drivers/staging/lustre/lustre/llite/vvp_page.c
new file mode 100644
index 000000000..954ed08c6
--- /dev/null
+++ b/drivers/staging/lustre/lustre/llite/vvp_page.c
@@ -0,0 +1,551 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2011, 2012, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ *
+ * Implementation of cl_page for VVP layer.
+ *
+ * Author: Nikita Danilov <nikita.danilov@sun.com>
+ * Author: Jinshan Xiong <jinshan.xiong@whamcloud.com>
+ */
+
+#define DEBUG_SUBSYSTEM S_LLITE
+
+
+#include "../include/obd.h"
+#include "../include/lustre_lite.h"
+
+#include "vvp_internal.h"
+
+/*****************************************************************************
+ *
+ * Page operations.
+ *
+ */
+
+static void vvp_page_fini_common(struct ccc_page *cp)
+{
+ struct page *vmpage = cp->cpg_page;
+
+ LASSERT(vmpage != NULL);
+ page_cache_release(vmpage);
+}
+
+static void vvp_page_fini(const struct lu_env *env,
+ struct cl_page_slice *slice)
+{
+ struct ccc_page *cp = cl2ccc_page(slice);
+ struct page *vmpage = cp->cpg_page;
+
+ /*
+ * vmpage->private was already cleared when page was moved into
+ * VPG_FREEING state.
+ */
+ LASSERT((struct cl_page *)vmpage->private != slice->cpl_page);
+ vvp_page_fini_common(cp);
+}
+
+static int vvp_page_own(const struct lu_env *env,
+ const struct cl_page_slice *slice, struct cl_io *io,
+ int nonblock)
+{
+ struct ccc_page *vpg = cl2ccc_page(slice);
+ struct page *vmpage = vpg->cpg_page;
+
+ LASSERT(vmpage != NULL);
+ if (nonblock) {
+ if (!trylock_page(vmpage))
+ return -EAGAIN;
+
+ if (unlikely(PageWriteback(vmpage))) {
+ unlock_page(vmpage);
+ return -EAGAIN;
+ }
+
+ return 0;
+ }
+
+ lock_page(vmpage);
+ wait_on_page_writeback(vmpage);
+ return 0;
+}
+
+static void vvp_page_assume(const struct lu_env *env,
+ const struct cl_page_slice *slice,
+ struct cl_io *unused)
+{
+ struct page *vmpage = cl2vm_page(slice);
+
+ LASSERT(vmpage != NULL);
+ LASSERT(PageLocked(vmpage));
+ wait_on_page_writeback(vmpage);
+}
+
+static void vvp_page_unassume(const struct lu_env *env,
+ const struct cl_page_slice *slice,
+ struct cl_io *unused)
+{
+ struct page *vmpage = cl2vm_page(slice);
+
+ LASSERT(vmpage != NULL);
+ LASSERT(PageLocked(vmpage));
+}
+
+static void vvp_page_disown(const struct lu_env *env,
+ const struct cl_page_slice *slice, struct cl_io *io)
+{
+ struct page *vmpage = cl2vm_page(slice);
+
+ LASSERT(vmpage != NULL);
+ LASSERT(PageLocked(vmpage));
+
+ unlock_page(cl2vm_page(slice));
+}
+
+static void vvp_page_discard(const struct lu_env *env,
+ const struct cl_page_slice *slice,
+ struct cl_io *unused)
+{
+ struct page *vmpage = cl2vm_page(slice);
+ struct address_space *mapping;
+ struct ccc_page *cpg = cl2ccc_page(slice);
+
+ LASSERT(vmpage != NULL);
+ LASSERT(PageLocked(vmpage));
+
+ mapping = vmpage->mapping;
+
+ if (cpg->cpg_defer_uptodate && !cpg->cpg_ra_used)
+ ll_ra_stats_inc(mapping, RA_STAT_DISCARDED);
+
+ /*
+ * truncate_complete_page() calls
+ * a_ops->invalidatepage()->cl_page_delete()->vvp_page_delete().
+ */
+ truncate_complete_page(mapping, vmpage);
+}
+
+static int vvp_page_unmap(const struct lu_env *env,
+ const struct cl_page_slice *slice,
+ struct cl_io *unused)
+{
+ struct page *vmpage = cl2vm_page(slice);
+ __u64 offset;
+
+ LASSERT(vmpage != NULL);
+ LASSERT(PageLocked(vmpage));
+
+ offset = vmpage->index << PAGE_CACHE_SHIFT;
+
+ /*
+ * XXX is it safe to call this with the page lock held?
+ */
+ ll_teardown_mmaps(vmpage->mapping, offset, offset + PAGE_CACHE_SIZE);
+ return 0;
+}
+
+static void vvp_page_delete(const struct lu_env *env,
+ const struct cl_page_slice *slice)
+{
+ struct page *vmpage = cl2vm_page(slice);
+ struct inode *inode = vmpage->mapping->host;
+ struct cl_object *obj = slice->cpl_obj;
+
+ LASSERT(PageLocked(vmpage));
+ LASSERT((struct cl_page *)vmpage->private == slice->cpl_page);
+ LASSERT(inode == ccc_object_inode(obj));
+
+ vvp_write_complete(cl2ccc(obj), cl2ccc_page(slice));
+ ClearPagePrivate(vmpage);
+ vmpage->private = 0;
+ /*
+ * Reference from vmpage to cl_page is removed, but the reference back
+ * is still here. It is removed later in vvp_page_fini().
+ */
+}
+
+static void vvp_page_export(const struct lu_env *env,
+ const struct cl_page_slice *slice,
+ int uptodate)
+{
+ struct page *vmpage = cl2vm_page(slice);
+
+ LASSERT(vmpage != NULL);
+ LASSERT(PageLocked(vmpage));
+ if (uptodate)
+ SetPageUptodate(vmpage);
+ else
+ ClearPageUptodate(vmpage);
+}
+
+static int vvp_page_is_vmlocked(const struct lu_env *env,
+ const struct cl_page_slice *slice)
+{
+ return PageLocked(cl2vm_page(slice)) ? -EBUSY : -ENODATA;
+}
+
+static int vvp_page_prep_read(const struct lu_env *env,
+ const struct cl_page_slice *slice,
+ struct cl_io *unused)
+{
+ /* Skip the page already marked as PG_uptodate. */
+ return PageUptodate(cl2vm_page(slice)) ? -EALREADY : 0;
+}
+
+static int vvp_page_prep_write(const struct lu_env *env,
+ const struct cl_page_slice *slice,
+ struct cl_io *unused)
+{
+ struct page *vmpage = cl2vm_page(slice);
+
+ LASSERT(PageLocked(vmpage));
+ LASSERT(!PageDirty(vmpage));
+
+ set_page_writeback(vmpage);
+ vvp_write_pending(cl2ccc(slice->cpl_obj), cl2ccc_page(slice));
+
+ return 0;
+}
+
+/**
+ * Handles page transfer errors at VM level.
+ *
+ * This takes inode as a separate argument, because inode on which error is to
+ * be set can be different from \a vmpage inode in case of direct-io.
+ */
+static void vvp_vmpage_error(struct inode *inode, struct page *vmpage, int ioret)
+{
+ struct ccc_object *obj = cl_inode2ccc(inode);
+
+ if (ioret == 0) {
+ ClearPageError(vmpage);
+ obj->cob_discard_page_warned = 0;
+ } else {
+ SetPageError(vmpage);
+ if (ioret == -ENOSPC)
+ set_bit(AS_ENOSPC, &inode->i_mapping->flags);
+ else
+ set_bit(AS_EIO, &inode->i_mapping->flags);
+
+ if ((ioret == -ESHUTDOWN || ioret == -EINTR) &&
+ obj->cob_discard_page_warned == 0) {
+ obj->cob_discard_page_warned = 1;
+ ll_dirty_page_discard_warn(vmpage, ioret);
+ }
+ }
+}
+
+static void vvp_page_completion_read(const struct lu_env *env,
+ const struct cl_page_slice *slice,
+ int ioret)
+{
+ struct ccc_page *cp = cl2ccc_page(slice);
+ struct page *vmpage = cp->cpg_page;
+ struct cl_page *page = cl_page_top(slice->cpl_page);
+ struct inode *inode = ccc_object_inode(page->cp_obj);
+
+ LASSERT(PageLocked(vmpage));
+ CL_PAGE_HEADER(D_PAGE, env, page, "completing READ with %d\n", ioret);
+
+ if (cp->cpg_defer_uptodate)
+ ll_ra_count_put(ll_i2sbi(inode), 1);
+
+ if (ioret == 0) {
+ if (!cp->cpg_defer_uptodate)
+ cl_page_export(env, page, 1);
+ } else
+ cp->cpg_defer_uptodate = 0;
+
+ if (page->cp_sync_io == NULL)
+ unlock_page(vmpage);
+}
+
+static void vvp_page_completion_write(const struct lu_env *env,
+ const struct cl_page_slice *slice,
+ int ioret)
+{
+ struct ccc_page *cp = cl2ccc_page(slice);
+ struct cl_page *pg = slice->cpl_page;
+ struct page *vmpage = cp->cpg_page;
+
+ LASSERT(ergo(pg->cp_sync_io != NULL, PageLocked(vmpage)));
+ LASSERT(PageWriteback(vmpage));
+
+ CL_PAGE_HEADER(D_PAGE, env, pg, "completing WRITE with %d\n", ioret);
+
+ /*
+ * TODO: Actually it makes sense to add the page into oap pending
+ * list again and so that we don't need to take the page out from
+ * SoM write pending list, if we just meet a recoverable error,
+ * -ENOMEM, etc.
+ * To implement this, we just need to return a non zero value in
+ * ->cpo_completion method. The underlying transfer should be notified
+ * and then re-add the page into pending transfer queue. -jay
+ */
+
+ cp->cpg_write_queued = 0;
+ vvp_write_complete(cl2ccc(slice->cpl_obj), cp);
+
+ /*
+ * Only mark the page error only when it's an async write because
+ * applications won't wait for IO to finish.
+ */
+ if (pg->cp_sync_io == NULL)
+ vvp_vmpage_error(ccc_object_inode(pg->cp_obj), vmpage, ioret);
+
+ end_page_writeback(vmpage);
+}
+
+/**
+ * Implements cl_page_operations::cpo_make_ready() method.
+ *
+ * This is called to yank a page from the transfer cache and to send it out as
+ * a part of transfer. This function try-locks the page. If try-lock failed,
+ * page is owned by some concurrent IO, and should be skipped (this is bad,
+ * but hopefully rare situation, as it usually results in transfer being
+ * shorter than possible).
+ *
+ * \retval 0 success, page can be placed into transfer
+ *
+ * \retval -EAGAIN page is either used by concurrent IO has been
+ * truncated. Skip it.
+ */
+static int vvp_page_make_ready(const struct lu_env *env,
+ const struct cl_page_slice *slice)
+{
+ struct page *vmpage = cl2vm_page(slice);
+ struct cl_page *pg = slice->cpl_page;
+ int result = 0;
+
+ lock_page(vmpage);
+ if (clear_page_dirty_for_io(vmpage)) {
+ LASSERT(pg->cp_state == CPS_CACHED);
+ /* This actually clears the dirty bit in the radix
+ * tree. */
+ set_page_writeback(vmpage);
+ vvp_write_pending(cl2ccc(slice->cpl_obj),
+ cl2ccc_page(slice));
+ CL_PAGE_HEADER(D_PAGE, env, pg, "readied\n");
+ } else if (pg->cp_state == CPS_PAGEOUT) {
+ /* is it possible for osc_flush_async_page() to already
+ * make it ready? */
+ result = -EALREADY;
+ } else {
+ CL_PAGE_DEBUG(D_ERROR, env, pg, "Unexpecting page state %d.\n",
+ pg->cp_state);
+ LBUG();
+ }
+ unlock_page(vmpage);
+ return result;
+}
+
+static int vvp_page_print(const struct lu_env *env,
+ const struct cl_page_slice *slice,
+ void *cookie, lu_printer_t printer)
+{
+ struct ccc_page *vp = cl2ccc_page(slice);
+ struct page *vmpage = vp->cpg_page;
+
+ (*printer)(env, cookie, LUSTRE_VVP_NAME "-page@%p(%d:%d:%d) vm@%p ",
+ vp, vp->cpg_defer_uptodate, vp->cpg_ra_used,
+ vp->cpg_write_queued, vmpage);
+ if (vmpage != NULL) {
+ (*printer)(env, cookie, "%lx %d:%d %lx %lu %slru",
+ (long)vmpage->flags, page_count(vmpage),
+ page_mapcount(vmpage), vmpage->private,
+ page_index(vmpage),
+ list_empty(&vmpage->lru) ? "not-" : "");
+ }
+ (*printer)(env, cookie, "\n");
+ return 0;
+}
+
+static const struct cl_page_operations vvp_page_ops = {
+ .cpo_own = vvp_page_own,
+ .cpo_assume = vvp_page_assume,
+ .cpo_unassume = vvp_page_unassume,
+ .cpo_disown = vvp_page_disown,
+ .cpo_vmpage = ccc_page_vmpage,
+ .cpo_discard = vvp_page_discard,
+ .cpo_delete = vvp_page_delete,
+ .cpo_unmap = vvp_page_unmap,
+ .cpo_export = vvp_page_export,
+ .cpo_is_vmlocked = vvp_page_is_vmlocked,
+ .cpo_fini = vvp_page_fini,
+ .cpo_print = vvp_page_print,
+ .cpo_is_under_lock = ccc_page_is_under_lock,
+ .io = {
+ [CRT_READ] = {
+ .cpo_prep = vvp_page_prep_read,
+ .cpo_completion = vvp_page_completion_read,
+ .cpo_make_ready = ccc_fail,
+ },
+ [CRT_WRITE] = {
+ .cpo_prep = vvp_page_prep_write,
+ .cpo_completion = vvp_page_completion_write,
+ .cpo_make_ready = vvp_page_make_ready,
+ }
+ }
+};
+
+static void vvp_transient_page_verify(const struct cl_page *page)
+{
+ struct inode *inode = ccc_object_inode(page->cp_obj);
+
+ LASSERT(!mutex_trylock(&inode->i_mutex));
+}
+
+static int vvp_transient_page_own(const struct lu_env *env,
+ const struct cl_page_slice *slice,
+ struct cl_io *unused, int nonblock)
+{
+ vvp_transient_page_verify(slice->cpl_page);
+ return 0;
+}
+
+static void vvp_transient_page_assume(const struct lu_env *env,
+ const struct cl_page_slice *slice,
+ struct cl_io *unused)
+{
+ vvp_transient_page_verify(slice->cpl_page);
+}
+
+static void vvp_transient_page_unassume(const struct lu_env *env,
+ const struct cl_page_slice *slice,
+ struct cl_io *unused)
+{
+ vvp_transient_page_verify(slice->cpl_page);
+}
+
+static void vvp_transient_page_disown(const struct lu_env *env,
+ const struct cl_page_slice *slice,
+ struct cl_io *unused)
+{
+ vvp_transient_page_verify(slice->cpl_page);
+}
+
+static void vvp_transient_page_discard(const struct lu_env *env,
+ const struct cl_page_slice *slice,
+ struct cl_io *unused)
+{
+ struct cl_page *page = slice->cpl_page;
+
+ vvp_transient_page_verify(slice->cpl_page);
+
+ /*
+ * For transient pages, remove it from the radix tree.
+ */
+ cl_page_delete(env, page);
+}
+
+static int vvp_transient_page_is_vmlocked(const struct lu_env *env,
+ const struct cl_page_slice *slice)
+{
+ struct inode *inode = ccc_object_inode(slice->cpl_obj);
+ int locked;
+
+ locked = !mutex_trylock(&inode->i_mutex);
+ if (!locked)
+ mutex_unlock(&inode->i_mutex);
+ return locked ? -EBUSY : -ENODATA;
+}
+
+static void
+vvp_transient_page_completion(const struct lu_env *env,
+ const struct cl_page_slice *slice,
+ int ioret)
+{
+ vvp_transient_page_verify(slice->cpl_page);
+}
+
+static void vvp_transient_page_fini(const struct lu_env *env,
+ struct cl_page_slice *slice)
+{
+ struct ccc_page *cp = cl2ccc_page(slice);
+ struct cl_page *clp = slice->cpl_page;
+ struct ccc_object *clobj = cl2ccc(clp->cp_obj);
+
+ vvp_page_fini_common(cp);
+ LASSERT(!mutex_trylock(&clobj->cob_inode->i_mutex));
+ clobj->cob_transient_pages--;
+}
+
+static const struct cl_page_operations vvp_transient_page_ops = {
+ .cpo_own = vvp_transient_page_own,
+ .cpo_assume = vvp_transient_page_assume,
+ .cpo_unassume = vvp_transient_page_unassume,
+ .cpo_disown = vvp_transient_page_disown,
+ .cpo_discard = vvp_transient_page_discard,
+ .cpo_vmpage = ccc_page_vmpage,
+ .cpo_fini = vvp_transient_page_fini,
+ .cpo_is_vmlocked = vvp_transient_page_is_vmlocked,
+ .cpo_print = vvp_page_print,
+ .cpo_is_under_lock = ccc_page_is_under_lock,
+ .io = {
+ [CRT_READ] = {
+ .cpo_prep = ccc_transient_page_prep,
+ .cpo_completion = vvp_transient_page_completion,
+ },
+ [CRT_WRITE] = {
+ .cpo_prep = ccc_transient_page_prep,
+ .cpo_completion = vvp_transient_page_completion,
+ }
+ }
+};
+
+int vvp_page_init(const struct lu_env *env, struct cl_object *obj,
+ struct cl_page *page, struct page *vmpage)
+{
+ struct ccc_page *cpg = cl_object_page_slice(obj, page);
+
+ CLOBINVRNT(env, obj, ccc_object_invariant(obj));
+
+ cpg->cpg_page = vmpage;
+ page_cache_get(vmpage);
+
+ INIT_LIST_HEAD(&cpg->cpg_pending_linkage);
+ if (page->cp_type == CPT_CACHEABLE) {
+ SetPagePrivate(vmpage);
+ vmpage->private = (unsigned long)page;
+ cl_page_slice_add(page, &cpg->cpg_cl, obj,
+ &vvp_page_ops);
+ } else {
+ struct ccc_object *clobj = cl2ccc(obj);
+
+ LASSERT(!mutex_trylock(&clobj->cob_inode->i_mutex));
+ cl_page_slice_add(page, &cpg->cpg_cl, obj,
+ &vvp_transient_page_ops);
+ clobj->cob_transient_pages++;
+ }
+ return 0;
+}
diff --git a/drivers/staging/lustre/lustre/llite/xattr.c b/drivers/staging/lustre/lustre/llite/xattr.c
new file mode 100644
index 000000000..e0fcbe139
--- /dev/null
+++ b/drivers/staging/lustre/lustre/llite/xattr.c
@@ -0,0 +1,621 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2011, 2012, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ */
+
+#include <linux/fs.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/selinux.h>
+
+#define DEBUG_SUBSYSTEM S_LLITE
+
+#include "../include/obd_support.h"
+#include "../include/lustre_lite.h"
+#include "../include/lustre_dlm.h"
+#include "../include/lustre_ver.h"
+#include "../include/lustre_eacl.h"
+
+#include "llite_internal.h"
+
+#define XATTR_USER_T (1)
+#define XATTR_TRUSTED_T (2)
+#define XATTR_SECURITY_T (3)
+#define XATTR_ACL_ACCESS_T (4)
+#define XATTR_ACL_DEFAULT_T (5)
+#define XATTR_LUSTRE_T (6)
+#define XATTR_OTHER_T (7)
+
+static
+int get_xattr_type(const char *name)
+{
+ if (!strcmp(name, POSIX_ACL_XATTR_ACCESS))
+ return XATTR_ACL_ACCESS_T;
+
+ if (!strcmp(name, POSIX_ACL_XATTR_DEFAULT))
+ return XATTR_ACL_DEFAULT_T;
+
+ if (!strncmp(name, XATTR_USER_PREFIX,
+ sizeof(XATTR_USER_PREFIX) - 1))
+ return XATTR_USER_T;
+
+ if (!strncmp(name, XATTR_TRUSTED_PREFIX,
+ sizeof(XATTR_TRUSTED_PREFIX) - 1))
+ return XATTR_TRUSTED_T;
+
+ if (!strncmp(name, XATTR_SECURITY_PREFIX,
+ sizeof(XATTR_SECURITY_PREFIX) - 1))
+ return XATTR_SECURITY_T;
+
+ if (!strncmp(name, XATTR_LUSTRE_PREFIX,
+ sizeof(XATTR_LUSTRE_PREFIX) - 1))
+ return XATTR_LUSTRE_T;
+
+ return XATTR_OTHER_T;
+}
+
+static
+int xattr_type_filter(struct ll_sb_info *sbi, int xattr_type)
+{
+ if ((xattr_type == XATTR_ACL_ACCESS_T ||
+ xattr_type == XATTR_ACL_DEFAULT_T) &&
+ !(sbi->ll_flags & LL_SBI_ACL))
+ return -EOPNOTSUPP;
+
+ if (xattr_type == XATTR_USER_T && !(sbi->ll_flags & LL_SBI_USER_XATTR))
+ return -EOPNOTSUPP;
+ if (xattr_type == XATTR_TRUSTED_T && !capable(CFS_CAP_SYS_ADMIN))
+ return -EPERM;
+ if (xattr_type == XATTR_OTHER_T)
+ return -EOPNOTSUPP;
+
+ return 0;
+}
+
+static
+int ll_setxattr_common(struct inode *inode, const char *name,
+ const void *value, size_t size,
+ int flags, __u64 valid)
+{
+ struct ll_sb_info *sbi = ll_i2sbi(inode);
+ struct ptlrpc_request *req = NULL;
+ int xattr_type, rc;
+ struct obd_capa *oc;
+#ifdef CONFIG_FS_POSIX_ACL
+ struct rmtacl_ctl_entry *rce = NULL;
+ posix_acl_xattr_header *new_value = NULL;
+ ext_acl_xattr_header *acl = NULL;
+#endif
+ const char *pv = value;
+
+ xattr_type = get_xattr_type(name);
+ rc = xattr_type_filter(sbi, xattr_type);
+ if (rc)
+ return rc;
+
+ if ((xattr_type == XATTR_ACL_ACCESS_T ||
+ xattr_type == XATTR_ACL_DEFAULT_T) &&
+ !inode_owner_or_capable(inode))
+ return -EPERM;
+
+ /* b10667: ignore lustre special xattr for now */
+ if ((xattr_type == XATTR_TRUSTED_T && strcmp(name, "trusted.lov") == 0) ||
+ (xattr_type == XATTR_LUSTRE_T && strcmp(name, "lustre.lov") == 0))
+ return 0;
+
+ /* b15587: ignore security.capability xattr for now */
+ if ((xattr_type == XATTR_SECURITY_T &&
+ strcmp(name, "security.capability") == 0))
+ return 0;
+
+ /* LU-549: Disable security.selinux when selinux is disabled */
+ if (xattr_type == XATTR_SECURITY_T && !selinux_is_enabled() &&
+ strcmp(name, "security.selinux") == 0)
+ return -EOPNOTSUPP;
+
+#ifdef CONFIG_FS_POSIX_ACL
+ if (sbi->ll_flags & LL_SBI_RMT_CLIENT &&
+ (xattr_type == XATTR_ACL_ACCESS_T ||
+ xattr_type == XATTR_ACL_DEFAULT_T)) {
+ rce = rct_search(&sbi->ll_rct, current_pid());
+ if (rce == NULL ||
+ (rce->rce_ops != RMT_LSETFACL &&
+ rce->rce_ops != RMT_RSETFACL))
+ return -EOPNOTSUPP;
+
+ if (rce->rce_ops == RMT_LSETFACL) {
+ struct eacl_entry *ee;
+
+ ee = et_search_del(&sbi->ll_et, current_pid(),
+ ll_inode2fid(inode), xattr_type);
+ LASSERT(ee != NULL);
+ if (valid & OBD_MD_FLXATTR) {
+ acl = lustre_acl_xattr_merge2ext(
+ (posix_acl_xattr_header *)value,
+ size, ee->ee_acl);
+ if (IS_ERR(acl)) {
+ ee_free(ee);
+ return PTR_ERR(acl);
+ }
+ size = CFS_ACL_XATTR_SIZE(\
+ le32_to_cpu(acl->a_count), \
+ ext_acl_xattr);
+ pv = (const char *)acl;
+ }
+ ee_free(ee);
+ } else if (rce->rce_ops == RMT_RSETFACL) {
+ size = lustre_posix_acl_xattr_filter(
+ (posix_acl_xattr_header *)value,
+ size, &new_value);
+ if (unlikely(size < 0))
+ return size;
+
+ pv = (const char *)new_value;
+ } else
+ return -EOPNOTSUPP;
+
+ valid |= rce_ops2valid(rce->rce_ops);
+ }
+#endif
+ oc = ll_mdscapa_get(inode);
+ rc = md_setxattr(sbi->ll_md_exp, ll_inode2fid(inode), oc,
+ valid, name, pv, size, 0, flags,
+ ll_i2suppgid(inode), &req);
+ capa_put(oc);
+#ifdef CONFIG_FS_POSIX_ACL
+ if (new_value != NULL)
+ lustre_posix_acl_xattr_free(new_value, size);
+ if (acl != NULL)
+ lustre_ext_acl_xattr_free(acl);
+#endif
+ if (rc) {
+ if (rc == -EOPNOTSUPP && xattr_type == XATTR_USER_T) {
+ LCONSOLE_INFO("Disabling user_xattr feature because it is not supported on the server\n");
+ sbi->ll_flags &= ~LL_SBI_USER_XATTR;
+ }
+ return rc;
+ }
+
+ ptlrpc_req_finished(req);
+ return 0;
+}
+
+int ll_setxattr(struct dentry *dentry, const char *name,
+ const void *value, size_t size, int flags)
+{
+ struct inode *inode = d_inode(dentry);
+
+ LASSERT(inode);
+ LASSERT(name);
+
+ CDEBUG(D_VFSTRACE, "VFS Op:inode=%lu/%u(%p), xattr %s\n",
+ inode->i_ino, inode->i_generation, inode, name);
+
+ ll_stats_ops_tally(ll_i2sbi(inode), LPROC_LL_SETXATTR, 1);
+
+ if ((strncmp(name, XATTR_TRUSTED_PREFIX,
+ sizeof(XATTR_TRUSTED_PREFIX) - 1) == 0 &&
+ strcmp(name + sizeof(XATTR_TRUSTED_PREFIX) - 1, "lov") == 0) ||
+ (strncmp(name, XATTR_LUSTRE_PREFIX,
+ sizeof(XATTR_LUSTRE_PREFIX) - 1) == 0 &&
+ strcmp(name + sizeof(XATTR_LUSTRE_PREFIX) - 1, "lov") == 0)) {
+ struct lov_user_md *lump = (struct lov_user_md *)value;
+ int rc = 0;
+
+ if (size != 0 && size < sizeof(struct lov_user_md))
+ return -EINVAL;
+
+ /* Attributes that are saved via getxattr will always have
+ * the stripe_offset as 0. Instead, the MDS should be
+ * allowed to pick the starting OST index. b=17846 */
+ if (lump != NULL && lump->lmm_stripe_offset == 0)
+ lump->lmm_stripe_offset = -1;
+
+ if (lump != NULL && S_ISREG(inode->i_mode)) {
+ int flags = FMODE_WRITE;
+ int lum_size = (lump->lmm_magic == LOV_USER_MAGIC_V1) ?
+ sizeof(*lump) : sizeof(struct lov_user_md_v3);
+
+ rc = ll_lov_setstripe_ea_info(inode, dentry, flags, lump,
+ lum_size);
+ /* b10667: rc always be 0 here for now */
+ rc = 0;
+ } else if (S_ISDIR(inode->i_mode)) {
+ rc = ll_dir_setstripe(inode, lump, 0);
+ }
+
+ return rc;
+
+ } else if (strcmp(name, XATTR_NAME_LMA) == 0 ||
+ strcmp(name, XATTR_NAME_LINK) == 0)
+ return 0;
+
+ return ll_setxattr_common(inode, name, value, size, flags,
+ OBD_MD_FLXATTR);
+}
+
+int ll_removexattr(struct dentry *dentry, const char *name)
+{
+ struct inode *inode = d_inode(dentry);
+
+ LASSERT(inode);
+ LASSERT(name);
+
+ CDEBUG(D_VFSTRACE, "VFS Op:inode=%lu/%u(%p), xattr %s\n",
+ inode->i_ino, inode->i_generation, inode, name);
+
+ ll_stats_ops_tally(ll_i2sbi(inode), LPROC_LL_REMOVEXATTR, 1);
+ return ll_setxattr_common(inode, name, NULL, 0, 0,
+ OBD_MD_FLXATTRRM);
+}
+
+static
+int ll_getxattr_common(struct inode *inode, const char *name,
+ void *buffer, size_t size, __u64 valid)
+{
+ struct ll_sb_info *sbi = ll_i2sbi(inode);
+ struct ptlrpc_request *req = NULL;
+ struct mdt_body *body;
+ int xattr_type, rc;
+ void *xdata;
+ struct obd_capa *oc;
+ struct rmtacl_ctl_entry *rce = NULL;
+ struct ll_inode_info *lli = ll_i2info(inode);
+
+ CDEBUG(D_VFSTRACE, "VFS Op:inode=%lu/%u(%p)\n",
+ inode->i_ino, inode->i_generation, inode);
+
+ /* listxattr have slightly different behavior from of ext3:
+ * without 'user_xattr' ext3 will list all xattr names but
+ * filtered out "^user..*"; we list them all for simplicity.
+ */
+ if (!name) {
+ xattr_type = XATTR_OTHER_T;
+ goto do_getxattr;
+ }
+
+ xattr_type = get_xattr_type(name);
+ rc = xattr_type_filter(sbi, xattr_type);
+ if (rc)
+ return rc;
+
+ /* b15587: ignore security.capability xattr for now */
+ if ((xattr_type == XATTR_SECURITY_T &&
+ strcmp(name, "security.capability") == 0))
+ return -ENODATA;
+
+ /* LU-549: Disable security.selinux when selinux is disabled */
+ if (xattr_type == XATTR_SECURITY_T && !selinux_is_enabled() &&
+ strcmp(name, "security.selinux") == 0)
+ return -EOPNOTSUPP;
+
+#ifdef CONFIG_FS_POSIX_ACL
+ if (sbi->ll_flags & LL_SBI_RMT_CLIENT &&
+ (xattr_type == XATTR_ACL_ACCESS_T ||
+ xattr_type == XATTR_ACL_DEFAULT_T)) {
+ rce = rct_search(&sbi->ll_rct, current_pid());
+ if (rce == NULL ||
+ (rce->rce_ops != RMT_LSETFACL &&
+ rce->rce_ops != RMT_LGETFACL &&
+ rce->rce_ops != RMT_RSETFACL &&
+ rce->rce_ops != RMT_RGETFACL))
+ return -EOPNOTSUPP;
+ }
+
+ /* posix acl is under protection of LOOKUP lock. when calling to this,
+ * we just have path resolution to the target inode, so we have great
+ * chance that cached ACL is uptodate.
+ */
+ if (xattr_type == XATTR_ACL_ACCESS_T &&
+ !(sbi->ll_flags & LL_SBI_RMT_CLIENT)) {
+
+ struct posix_acl *acl;
+
+ spin_lock(&lli->lli_lock);
+ acl = posix_acl_dup(lli->lli_posix_acl);
+ spin_unlock(&lli->lli_lock);
+
+ if (!acl)
+ return -ENODATA;
+
+ rc = posix_acl_to_xattr(&init_user_ns, acl, buffer, size);
+ posix_acl_release(acl);
+ return rc;
+ }
+ if (xattr_type == XATTR_ACL_DEFAULT_T && !S_ISDIR(inode->i_mode))
+ return -ENODATA;
+#endif
+
+do_getxattr:
+ if (sbi->ll_xattr_cache_enabled && xattr_type != XATTR_ACL_ACCESS_T) {
+ rc = ll_xattr_cache_get(inode, name, buffer, size, valid);
+ if (rc == -EAGAIN)
+ goto getxattr_nocache;
+ if (rc < 0)
+ goto out_xattr;
+
+ /* Add "system.posix_acl_access" to the list */
+ if (lli->lli_posix_acl != NULL && valid & OBD_MD_FLXATTRLS) {
+ if (size == 0) {
+ rc += sizeof(XATTR_NAME_ACL_ACCESS);
+ } else if (size - rc >= sizeof(XATTR_NAME_ACL_ACCESS)) {
+ memcpy(buffer + rc, XATTR_NAME_ACL_ACCESS,
+ sizeof(XATTR_NAME_ACL_ACCESS));
+ rc += sizeof(XATTR_NAME_ACL_ACCESS);
+ } else {
+ rc = -ERANGE;
+ goto out_xattr;
+ }
+ }
+ } else {
+getxattr_nocache:
+ oc = ll_mdscapa_get(inode);
+ rc = md_getxattr(sbi->ll_md_exp, ll_inode2fid(inode), oc,
+ valid | (rce ? rce_ops2valid(rce->rce_ops) : 0),
+ name, NULL, 0, size, 0, &req);
+ capa_put(oc);
+
+ if (rc < 0)
+ goto out_xattr;
+
+ body = req_capsule_server_get(&req->rq_pill, &RMF_MDT_BODY);
+ LASSERT(body);
+
+ /* only detect the xattr size */
+ if (size == 0) {
+ rc = body->eadatasize;
+ goto out;
+ }
+
+ if (size < body->eadatasize) {
+ CERROR("server bug: replied size %u > %u\n",
+ body->eadatasize, (int)size);
+ rc = -ERANGE;
+ goto out;
+ }
+
+ if (body->eadatasize == 0) {
+ rc = -ENODATA;
+ goto out;
+ }
+
+ /* do not need swab xattr data */
+ xdata = req_capsule_server_sized_get(&req->rq_pill, &RMF_EADATA,
+ body->eadatasize);
+ if (!xdata) {
+ rc = -EFAULT;
+ goto out;
+ }
+
+ memcpy(buffer, xdata, body->eadatasize);
+ rc = body->eadatasize;
+ }
+
+#ifdef CONFIG_FS_POSIX_ACL
+ if (rce && rce->rce_ops == RMT_LSETFACL) {
+ ext_acl_xattr_header *acl;
+
+ acl = lustre_posix_acl_xattr_2ext(
+ (posix_acl_xattr_header *)buffer, rc);
+ if (IS_ERR(acl)) {
+ rc = PTR_ERR(acl);
+ goto out;
+ }
+
+ rc = ee_add(&sbi->ll_et, current_pid(), ll_inode2fid(inode),
+ xattr_type, acl);
+ if (unlikely(rc < 0)) {
+ lustre_ext_acl_xattr_free(acl);
+ goto out;
+ }
+ }
+#endif
+
+out_xattr:
+ if (rc == -EOPNOTSUPP && xattr_type == XATTR_USER_T) {
+ LCONSOLE_INFO(
+ "%s: disabling user_xattr feature because it is not supported on the server: rc = %d\n",
+ ll_get_fsname(inode->i_sb, NULL, 0), rc);
+ sbi->ll_flags &= ~LL_SBI_USER_XATTR;
+ }
+out:
+ ptlrpc_req_finished(req);
+ return rc;
+}
+
+ssize_t ll_getxattr(struct dentry *dentry, const char *name,
+ void *buffer, size_t size)
+{
+ struct inode *inode = d_inode(dentry);
+
+ LASSERT(inode);
+ LASSERT(name);
+
+ CDEBUG(D_VFSTRACE, "VFS Op:inode=%lu/%u(%p), xattr %s\n",
+ inode->i_ino, inode->i_generation, inode, name);
+
+ ll_stats_ops_tally(ll_i2sbi(inode), LPROC_LL_GETXATTR, 1);
+
+ if ((strncmp(name, XATTR_TRUSTED_PREFIX,
+ sizeof(XATTR_TRUSTED_PREFIX) - 1) == 0 &&
+ strcmp(name + sizeof(XATTR_TRUSTED_PREFIX) - 1, "lov") == 0) ||
+ (strncmp(name, XATTR_LUSTRE_PREFIX,
+ sizeof(XATTR_LUSTRE_PREFIX) - 1) == 0 &&
+ strcmp(name + sizeof(XATTR_LUSTRE_PREFIX) - 1, "lov") == 0)) {
+ struct lov_stripe_md *lsm;
+ struct lov_user_md *lump;
+ struct lov_mds_md *lmm = NULL;
+ struct ptlrpc_request *request = NULL;
+ int rc = 0, lmmsize = 0;
+
+ if (!S_ISREG(inode->i_mode) && !S_ISDIR(inode->i_mode))
+ return -ENODATA;
+
+ if (size == 0 && S_ISDIR(inode->i_mode)) {
+ /* XXX directory EA is fix for now, optimize to save
+ * RPC transfer */
+ rc = sizeof(struct lov_user_md);
+ goto out;
+ }
+
+ lsm = ccc_inode_lsm_get(inode);
+ if (lsm == NULL) {
+ if (S_ISDIR(inode->i_mode)) {
+ rc = ll_dir_getstripe(inode, &lmm,
+ &lmmsize, &request);
+ } else {
+ rc = -ENODATA;
+ }
+ } else {
+ /* LSM is present already after lookup/getattr call.
+ * we need to grab layout lock once it is implemented */
+ rc = obd_packmd(ll_i2dtexp(inode), &lmm, lsm);
+ lmmsize = rc;
+ }
+ ccc_inode_lsm_put(inode, lsm);
+
+ if (rc < 0)
+ goto out;
+
+ if (size == 0) {
+ /* used to call ll_get_max_mdsize() forward to get
+ * the maximum buffer size, while some apps (such as
+ * rsync 3.0.x) care much about the exact xattr value
+ * size */
+ rc = lmmsize;
+ goto out;
+ }
+
+ if (size < lmmsize) {
+ CERROR("server bug: replied size %d > %d for %pd (%s)\n",
+ lmmsize, (int)size, dentry, name);
+ rc = -ERANGE;
+ goto out;
+ }
+
+ lump = (struct lov_user_md *)buffer;
+ memcpy(lump, lmm, lmmsize);
+ /* do not return layout gen for getxattr otherwise it would
+ * confuse tar --xattr by recognizing layout gen as stripe
+ * offset when the file is restored. See LU-2809. */
+ lump->lmm_layout_gen = 0;
+
+ rc = lmmsize;
+out:
+ if (request)
+ ptlrpc_req_finished(request);
+ else if (lmm)
+ obd_free_diskmd(ll_i2dtexp(inode), &lmm);
+ return rc;
+ }
+
+ return ll_getxattr_common(inode, name, buffer, size, OBD_MD_FLXATTR);
+}
+
+ssize_t ll_listxattr(struct dentry *dentry, char *buffer, size_t size)
+{
+ struct inode *inode = d_inode(dentry);
+ int rc = 0, rc2 = 0;
+ struct lov_mds_md *lmm = NULL;
+ struct ptlrpc_request *request = NULL;
+ int lmmsize;
+
+ LASSERT(inode);
+
+ CDEBUG(D_VFSTRACE, "VFS Op:inode=%lu/%u(%p)\n",
+ inode->i_ino, inode->i_generation, inode);
+
+ ll_stats_ops_tally(ll_i2sbi(inode), LPROC_LL_LISTXATTR, 1);
+
+ rc = ll_getxattr_common(inode, NULL, buffer, size, OBD_MD_FLXATTRLS);
+ if (rc < 0)
+ goto out;
+
+ if (buffer != NULL) {
+ struct ll_sb_info *sbi = ll_i2sbi(inode);
+ char *xattr_name = buffer;
+ int xlen, rem = rc;
+
+ while (rem > 0) {
+ xlen = strnlen(xattr_name, rem - 1) + 1;
+ rem -= xlen;
+ if (xattr_type_filter(sbi,
+ get_xattr_type(xattr_name)) == 0) {
+ /* skip OK xattr type
+ * leave it in buffer
+ */
+ xattr_name += xlen;
+ continue;
+ }
+ /* move up remaining xattrs in buffer
+ * removing the xattr that is not OK
+ */
+ memmove(xattr_name, xattr_name + xlen, rem);
+ rc -= xlen;
+ }
+ }
+ if (S_ISREG(inode->i_mode)) {
+ if (!ll_i2info(inode)->lli_has_smd)
+ rc2 = -1;
+ } else if (S_ISDIR(inode->i_mode)) {
+ rc2 = ll_dir_getstripe(inode, &lmm, &lmmsize, &request);
+ }
+
+ if (rc2 < 0) {
+ rc2 = 0;
+ goto out;
+ } else if (S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode)) {
+ const int prefix_len = sizeof(XATTR_LUSTRE_PREFIX) - 1;
+ const size_t name_len = sizeof("lov") - 1;
+ const size_t total_len = prefix_len + name_len + 1;
+
+ if (((rc + total_len) > size) && (buffer != NULL)) {
+ ptlrpc_req_finished(request);
+ return -ERANGE;
+ }
+
+ if (buffer != NULL) {
+ buffer += rc;
+ memcpy(buffer, XATTR_LUSTRE_PREFIX, prefix_len);
+ memcpy(buffer + prefix_len, "lov", name_len);
+ buffer[prefix_len + name_len] = '\0';
+ }
+ rc2 = total_len;
+ }
+out:
+ ptlrpc_req_finished(request);
+ rc = rc + rc2;
+
+ return rc;
+}
diff --git a/drivers/staging/lustre/lustre/llite/xattr_cache.c b/drivers/staging/lustre/lustre/llite/xattr_cache.c
new file mode 100644
index 000000000..69ea92adf
--- /dev/null
+++ b/drivers/staging/lustre/lustre/llite/xattr_cache.c
@@ -0,0 +1,538 @@
+/*
+ * Copyright 2012 Xyratex Technology Limited
+ *
+ * Author: Andrew Perepechko <Andrew_Perepechko@xyratex.com>
+ *
+ */
+
+#define DEBUG_SUBSYSTEM S_LLITE
+
+#include <linux/fs.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include "../include/obd_support.h"
+#include "../include/lustre_lite.h"
+#include "../include/lustre_dlm.h"
+#include "../include/lustre_ver.h"
+#include "llite_internal.h"
+
+/* If we ever have hundreds of extended attributes, we might want to consider
+ * using a hash or a tree structure instead of list for faster lookups.
+ */
+struct ll_xattr_entry {
+ struct list_head xe_list; /* protected with
+ * lli_xattrs_list_rwsem */
+ char *xe_name; /* xattr name, \0-terminated */
+ char *xe_value; /* xattr value */
+ unsigned xe_namelen; /* strlen(xe_name) + 1 */
+ unsigned xe_vallen; /* xattr value length */
+};
+
+static struct kmem_cache *xattr_kmem;
+static struct lu_kmem_descr xattr_caches[] = {
+ {
+ .ckd_cache = &xattr_kmem,
+ .ckd_name = "xattr_kmem",
+ .ckd_size = sizeof(struct ll_xattr_entry)
+ },
+ {
+ .ckd_cache = NULL
+ }
+};
+
+int ll_xattr_init(void)
+{
+ return lu_kmem_init(xattr_caches);
+}
+
+void ll_xattr_fini(void)
+{
+ lu_kmem_fini(xattr_caches);
+}
+
+/**
+ * Initializes xattr cache for an inode.
+ *
+ * This initializes the xattr list and marks cache presence.
+ */
+static void ll_xattr_cache_init(struct ll_inode_info *lli)
+{
+
+
+ LASSERT(lli != NULL);
+
+ INIT_LIST_HEAD(&lli->lli_xattrs);
+ lli->lli_flags |= LLIF_XATTR_CACHE;
+}
+
+/**
+ * This looks for a specific extended attribute.
+ *
+ * Find in @cache and return @xattr_name attribute in @xattr,
+ * for the NULL @xattr_name return the first cached @xattr.
+ *
+ * \retval 0 success
+ * \retval -ENODATA if not found
+ */
+static int ll_xattr_cache_find(struct list_head *cache,
+ const char *xattr_name,
+ struct ll_xattr_entry **xattr)
+{
+ struct ll_xattr_entry *entry;
+
+
+
+ list_for_each_entry(entry, cache, xe_list) {
+ /* xattr_name == NULL means look for any entry */
+ if (xattr_name == NULL ||
+ strcmp(xattr_name, entry->xe_name) == 0) {
+ *xattr = entry;
+ CDEBUG(D_CACHE, "find: [%s]=%.*s\n",
+ entry->xe_name, entry->xe_vallen,
+ entry->xe_value);
+ return 0;
+ }
+ }
+
+ return -ENODATA;
+}
+
+/**
+ * This adds an xattr.
+ *
+ * Add @xattr_name attr with @xattr_val value and @xattr_val_len length,
+ *
+ * \retval 0 success
+ * \retval -ENOMEM if no memory could be allocated for the cached attr
+ * \retval -EPROTO if duplicate xattr is being added
+ */
+static int ll_xattr_cache_add(struct list_head *cache,
+ const char *xattr_name,
+ const char *xattr_val,
+ unsigned xattr_val_len)
+{
+ struct ll_xattr_entry *xattr;
+
+
+
+ if (ll_xattr_cache_find(cache, xattr_name, &xattr) == 0) {
+ CDEBUG(D_CACHE, "duplicate xattr: [%s]\n", xattr_name);
+ return -EPROTO;
+ }
+
+ OBD_SLAB_ALLOC_PTR_GFP(xattr, xattr_kmem, GFP_NOFS);
+ if (xattr == NULL) {
+ CDEBUG(D_CACHE, "failed to allocate xattr\n");
+ return -ENOMEM;
+ }
+
+ xattr->xe_name = kstrdup(xattr_name, GFP_NOFS);
+ if (!xattr->xe_name) {
+ CDEBUG(D_CACHE, "failed to alloc xattr name %u\n",
+ xattr->xe_namelen);
+ goto err_name;
+ }
+ xattr->xe_value = kmemdup(xattr_val, xattr_val_len, GFP_NOFS);
+ if (!xattr->xe_value)
+ goto err_value;
+
+ xattr->xe_vallen = xattr_val_len;
+ list_add(&xattr->xe_list, cache);
+
+ CDEBUG(D_CACHE, "set: [%s]=%.*s\n", xattr_name,
+ xattr_val_len, xattr_val);
+
+ return 0;
+err_value:
+ OBD_FREE(xattr->xe_name, xattr->xe_namelen);
+err_name:
+ OBD_SLAB_FREE_PTR(xattr, xattr_kmem);
+
+ return -ENOMEM;
+}
+
+/**
+ * This removes an extended attribute from cache.
+ *
+ * Remove @xattr_name attribute from @cache.
+ *
+ * \retval 0 success
+ * \retval -ENODATA if @xattr_name is not cached
+ */
+static int ll_xattr_cache_del(struct list_head *cache,
+ const char *xattr_name)
+{
+ struct ll_xattr_entry *xattr;
+
+
+
+ CDEBUG(D_CACHE, "del xattr: %s\n", xattr_name);
+
+ if (ll_xattr_cache_find(cache, xattr_name, &xattr) == 0) {
+ list_del(&xattr->xe_list);
+ OBD_FREE(xattr->xe_name, xattr->xe_namelen);
+ OBD_FREE(xattr->xe_value, xattr->xe_vallen);
+ OBD_SLAB_FREE_PTR(xattr, xattr_kmem);
+
+ return 0;
+ }
+
+ return -ENODATA;
+}
+
+/**
+ * This iterates cached extended attributes.
+ *
+ * Walk over cached attributes in @cache and
+ * fill in @xld_buffer or only calculate buffer
+ * size if @xld_buffer is NULL.
+ *
+ * \retval >= 0 buffer list size
+ * \retval -ENODATA if the list cannot fit @xld_size buffer
+ */
+static int ll_xattr_cache_list(struct list_head *cache,
+ char *xld_buffer,
+ int xld_size)
+{
+ struct ll_xattr_entry *xattr, *tmp;
+ int xld_tail = 0;
+
+
+
+ list_for_each_entry_safe(xattr, tmp, cache, xe_list) {
+ CDEBUG(D_CACHE, "list: buffer=%p[%d] name=%s\n",
+ xld_buffer, xld_tail, xattr->xe_name);
+
+ if (xld_buffer) {
+ xld_size -= xattr->xe_namelen;
+ if (xld_size < 0)
+ break;
+ memcpy(&xld_buffer[xld_tail],
+ xattr->xe_name, xattr->xe_namelen);
+ }
+ xld_tail += xattr->xe_namelen;
+ }
+
+ if (xld_size < 0)
+ return -ERANGE;
+
+ return xld_tail;
+}
+
+/**
+ * Check if the xattr cache is initialized (filled).
+ *
+ * \retval 0 @cache is not initialized
+ * \retval 1 @cache is initialized
+ */
+static int ll_xattr_cache_valid(struct ll_inode_info *lli)
+{
+ return !!(lli->lli_flags & LLIF_XATTR_CACHE);
+}
+
+/**
+ * This finalizes the xattr cache.
+ *
+ * Free all xattr memory. @lli is the inode info pointer.
+ *
+ * \retval 0 no error occurred
+ */
+static int ll_xattr_cache_destroy_locked(struct ll_inode_info *lli)
+{
+
+
+ if (!ll_xattr_cache_valid(lli))
+ return 0;
+
+ while (ll_xattr_cache_del(&lli->lli_xattrs, NULL) == 0)
+ ; /* empty loop */
+ lli->lli_flags &= ~LLIF_XATTR_CACHE;
+
+ return 0;
+}
+
+int ll_xattr_cache_destroy(struct inode *inode)
+{
+ struct ll_inode_info *lli = ll_i2info(inode);
+ int rc;
+
+
+
+ down_write(&lli->lli_xattrs_list_rwsem);
+ rc = ll_xattr_cache_destroy_locked(lli);
+ up_write(&lli->lli_xattrs_list_rwsem);
+
+ return rc;
+}
+
+/**
+ * Match or enqueue a PR lock.
+ *
+ * Find or request an LDLM lock with xattr data.
+ * Since LDLM does not provide API for atomic match_or_enqueue,
+ * the function handles it with a separate enq lock.
+ * If successful, the function exits with the list lock held.
+ *
+ * \retval 0 no error occurred
+ * \retval -ENOMEM not enough memory
+ */
+static int ll_xattr_find_get_lock(struct inode *inode,
+ struct lookup_intent *oit,
+ struct ptlrpc_request **req)
+{
+ ldlm_mode_t mode;
+ struct lustre_handle lockh = { 0 };
+ struct md_op_data *op_data;
+ struct ll_inode_info *lli = ll_i2info(inode);
+ struct ldlm_enqueue_info einfo = { .ei_type = LDLM_IBITS,
+ .ei_mode = it_to_lock_mode(oit),
+ .ei_cb_bl = ll_md_blocking_ast,
+ .ei_cb_cp = ldlm_completion_ast };
+ struct ll_sb_info *sbi = ll_i2sbi(inode);
+ struct obd_export *exp = sbi->ll_md_exp;
+ int rc;
+
+
+
+ mutex_lock(&lli->lli_xattrs_enq_lock);
+ /* inode may have been shrunk and recreated, so data is gone, match lock
+ * only when data exists. */
+ if (ll_xattr_cache_valid(lli)) {
+ /* Try matching first. */
+ mode = ll_take_md_lock(inode, MDS_INODELOCK_XATTR, &lockh, 0,
+ LCK_PR);
+ if (mode != 0) {
+ /* fake oit in mdc_revalidate_lock() manner */
+ oit->d.lustre.it_lock_handle = lockh.cookie;
+ oit->d.lustre.it_lock_mode = mode;
+ goto out;
+ }
+ }
+
+ /* Enqueue if the lock isn't cached locally. */
+ op_data = ll_prep_md_op_data(NULL, inode, NULL, NULL, 0, 0,
+ LUSTRE_OPC_ANY, NULL);
+ if (IS_ERR(op_data)) {
+ mutex_unlock(&lli->lli_xattrs_enq_lock);
+ return PTR_ERR(op_data);
+ }
+
+ op_data->op_valid = OBD_MD_FLXATTR | OBD_MD_FLXATTRLS;
+
+ rc = md_enqueue(exp, &einfo, oit, op_data, &lockh, NULL, 0, NULL, 0);
+ ll_finish_md_op_data(op_data);
+
+ if (rc < 0) {
+ CDEBUG(D_CACHE,
+ "md_intent_lock failed with %d for fid "DFID"\n",
+ rc, PFID(ll_inode2fid(inode)));
+ mutex_unlock(&lli->lli_xattrs_enq_lock);
+ return rc;
+ }
+
+ *req = (struct ptlrpc_request *)oit->d.lustre.it_data;
+out:
+ down_write(&lli->lli_xattrs_list_rwsem);
+ mutex_unlock(&lli->lli_xattrs_enq_lock);
+
+ return 0;
+}
+
+/**
+ * Refill the xattr cache.
+ *
+ * Fetch and cache the whole of xattrs for @inode, acquiring
+ * a read or a write xattr lock depending on operation in @oit.
+ * Intent is dropped on exit unless the operation is setxattr.
+ *
+ * \retval 0 no error occurred
+ * \retval -EPROTO network protocol error
+ * \retval -ENOMEM not enough memory for the cache
+ */
+static int ll_xattr_cache_refill(struct inode *inode, struct lookup_intent *oit)
+{
+ struct ll_sb_info *sbi = ll_i2sbi(inode);
+ struct ptlrpc_request *req = NULL;
+ const char *xdata, *xval, *xtail, *xvtail;
+ struct ll_inode_info *lli = ll_i2info(inode);
+ struct mdt_body *body;
+ __u32 *xsizes;
+ int rc = 0, i;
+
+
+
+ rc = ll_xattr_find_get_lock(inode, oit, &req);
+ if (rc)
+ goto out_no_unlock;
+
+ /* Do we have the data at this point? */
+ if (ll_xattr_cache_valid(lli)) {
+ ll_stats_ops_tally(sbi, LPROC_LL_GETXATTR_HITS, 1);
+ rc = 0;
+ goto out_maybe_drop;
+ }
+
+ /* Matched but no cache? Cancelled on error by a parallel refill. */
+ if (unlikely(req == NULL)) {
+ CDEBUG(D_CACHE, "cancelled by a parallel getxattr\n");
+ rc = -EIO;
+ goto out_maybe_drop;
+ }
+
+ if (oit->d.lustre.it_status < 0) {
+ CDEBUG(D_CACHE, "getxattr intent returned %d for fid "DFID"\n",
+ oit->d.lustre.it_status, PFID(ll_inode2fid(inode)));
+ rc = oit->d.lustre.it_status;
+ /* xattr data is so large that we don't want to cache it */
+ if (rc == -ERANGE)
+ rc = -EAGAIN;
+ goto out_destroy;
+ }
+
+ body = req_capsule_server_get(&req->rq_pill, &RMF_MDT_BODY);
+ if (body == NULL) {
+ CERROR("no MDT BODY in the refill xattr reply\n");
+ rc = -EPROTO;
+ goto out_destroy;
+ }
+ /* do not need swab xattr data */
+ xdata = req_capsule_server_sized_get(&req->rq_pill, &RMF_EADATA,
+ body->eadatasize);
+ xval = req_capsule_server_sized_get(&req->rq_pill, &RMF_EAVALS,
+ body->aclsize);
+ xsizes = req_capsule_server_sized_get(&req->rq_pill, &RMF_EAVALS_LENS,
+ body->max_mdsize * sizeof(__u32));
+ if (xdata == NULL || xval == NULL || xsizes == NULL) {
+ CERROR("wrong setxattr reply\n");
+ rc = -EPROTO;
+ goto out_destroy;
+ }
+
+ xtail = xdata + body->eadatasize;
+ xvtail = xval + body->aclsize;
+
+ CDEBUG(D_CACHE, "caching: xdata=%p xtail=%p\n", xdata, xtail);
+
+ ll_xattr_cache_init(lli);
+
+ for (i = 0; i < body->max_mdsize; i++) {
+ CDEBUG(D_CACHE, "caching [%s]=%.*s\n", xdata, *xsizes, xval);
+ /* Perform consistency checks: attr names and vals in pill */
+ if (memchr(xdata, 0, xtail - xdata) == NULL) {
+ CERROR("xattr protocol violation (names are broken)\n");
+ rc = -EPROTO;
+ } else if (xval + *xsizes > xvtail) {
+ CERROR("xattr protocol violation (vals are broken)\n");
+ rc = -EPROTO;
+ } else if (OBD_FAIL_CHECK(OBD_FAIL_LLITE_XATTR_ENOMEM)) {
+ rc = -ENOMEM;
+ } else if (!strcmp(xdata, XATTR_NAME_ACL_ACCESS)) {
+ /* Filter out ACL ACCESS since it's cached separately */
+ CDEBUG(D_CACHE, "not caching %s\n",
+ XATTR_NAME_ACL_ACCESS);
+ rc = 0;
+ } else {
+ rc = ll_xattr_cache_add(&lli->lli_xattrs, xdata, xval,
+ *xsizes);
+ }
+ if (rc < 0) {
+ ll_xattr_cache_destroy_locked(lli);
+ goto out_destroy;
+ }
+ xdata += strlen(xdata) + 1;
+ xval += *xsizes;
+ xsizes++;
+ }
+
+ if (xdata != xtail || xval != xvtail)
+ CERROR("a hole in xattr data\n");
+
+ ll_set_lock_data(sbi->ll_md_exp, inode, oit, NULL);
+
+ goto out_maybe_drop;
+out_maybe_drop:
+
+ ll_intent_drop_lock(oit);
+
+ if (rc != 0)
+ up_write(&lli->lli_xattrs_list_rwsem);
+out_no_unlock:
+ ptlrpc_req_finished(req);
+
+ return rc;
+
+out_destroy:
+ up_write(&lli->lli_xattrs_list_rwsem);
+
+ ldlm_lock_decref_and_cancel((struct lustre_handle *)
+ &oit->d.lustre.it_lock_handle,
+ oit->d.lustre.it_lock_mode);
+
+ goto out_no_unlock;
+}
+
+/**
+ * Get an xattr value or list xattrs using the write-through cache.
+ *
+ * Get the xattr value (@valid has OBD_MD_FLXATTR set) of @name or
+ * list xattr names (@valid has OBD_MD_FLXATTRLS set) for @inode.
+ * The resulting value/list is stored in @buffer if the former
+ * is not larger than @size.
+ *
+ * \retval 0 no error occurred
+ * \retval -EPROTO network protocol error
+ * \retval -ENOMEM not enough memory for the cache
+ * \retval -ERANGE the buffer is not large enough
+ * \retval -ENODATA no such attr or the list is empty
+ */
+int ll_xattr_cache_get(struct inode *inode,
+ const char *name,
+ char *buffer,
+ size_t size,
+ __u64 valid)
+{
+ struct lookup_intent oit = { .it_op = IT_GETXATTR };
+ struct ll_inode_info *lli = ll_i2info(inode);
+ int rc = 0;
+
+
+
+ LASSERT(!!(valid & OBD_MD_FLXATTR) ^ !!(valid & OBD_MD_FLXATTRLS));
+
+ down_read(&lli->lli_xattrs_list_rwsem);
+ if (!ll_xattr_cache_valid(lli)) {
+ up_read(&lli->lli_xattrs_list_rwsem);
+ rc = ll_xattr_cache_refill(inode, &oit);
+ if (rc)
+ return rc;
+ downgrade_write(&lli->lli_xattrs_list_rwsem);
+ } else {
+ ll_stats_ops_tally(ll_i2sbi(inode), LPROC_LL_GETXATTR_HITS, 1);
+ }
+
+ if (valid & OBD_MD_FLXATTR) {
+ struct ll_xattr_entry *xattr;
+
+ rc = ll_xattr_cache_find(&lli->lli_xattrs, name, &xattr);
+ if (rc == 0) {
+ rc = xattr->xe_vallen;
+ /* zero size means we are only requested size in rc */
+ if (size != 0) {
+ if (size >= xattr->xe_vallen)
+ memcpy(buffer, xattr->xe_value,
+ xattr->xe_vallen);
+ else
+ rc = -ERANGE;
+ }
+ }
+ } else if (valid & OBD_MD_FLXATTRLS) {
+ rc = ll_xattr_cache_list(&lli->lli_xattrs,
+ size ? buffer : NULL, size);
+ }
+
+ goto out;
+out:
+ up_read(&lli->lli_xattrs_list_rwsem);
+
+ return rc;
+}
diff --git a/drivers/staging/lustre/lustre/lmv/Makefile b/drivers/staging/lustre/lustre/lmv/Makefile
new file mode 100644
index 000000000..a7a15369a
--- /dev/null
+++ b/drivers/staging/lustre/lustre/lmv/Makefile
@@ -0,0 +1,3 @@
+obj-$(CONFIG_LUSTRE_FS) += lmv.o
+lmv-y := lmv_obd.o lmv_intent.o lmv_fld.o
+lmv-$(CONFIG_PROC_FS) += lproc_lmv.o
diff --git a/drivers/staging/lustre/lustre/lmv/lmv_fld.c b/drivers/staging/lustre/lustre/lmv/lmv_fld.c
new file mode 100644
index 000000000..ee235926f
--- /dev/null
+++ b/drivers/staging/lustre/lustre/lmv/lmv_fld.c
@@ -0,0 +1,83 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2013, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ */
+
+#define DEBUG_SUBSYSTEM S_LMV
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/pagemap.h>
+#include <asm/div64.h>
+#include <linux/seq_file.h>
+
+#include "../include/obd_support.h"
+#include "../include/lustre/lustre_idl.h"
+#include "../include/lustre_fid.h"
+#include "../include/lustre_lib.h"
+#include "../include/lustre_net.h"
+#include "../include/lustre_dlm.h"
+#include "../include/obd_class.h"
+#include "../include/lprocfs_status.h"
+#include "lmv_internal.h"
+
+int lmv_fld_lookup(struct lmv_obd *lmv,
+ const struct lu_fid *fid,
+ u32 *mds)
+{
+ int rc;
+
+ /* FIXME: Currently ZFS still use local seq for ROOT unfortunately, and
+ * this fid_is_local check should be removed once LU-2240 is fixed */
+ LASSERTF((fid_seq_in_fldb(fid_seq(fid)) ||
+ fid_seq_is_local_file(fid_seq(fid))) &&
+ fid_is_sane(fid), DFID" is insane!\n", PFID(fid));
+
+ rc = fld_client_lookup(&lmv->lmv_fld, fid_seq(fid), mds,
+ LU_SEQ_RANGE_MDT, NULL);
+ if (rc) {
+ CERROR("Error while looking for mds number. Seq %#llx, err = %d\n",
+ fid_seq(fid), rc);
+ return rc;
+ }
+
+ CDEBUG(D_INODE, "FLD lookup got mds #%x for fid="DFID"\n",
+ *mds, PFID(fid));
+
+ if (*mds >= lmv->desc.ld_tgt_count) {
+ CERROR("FLD lookup got invalid mds #%x (max: %x) for fid=" DFID "\n", *mds, lmv->desc.ld_tgt_count,
+ PFID(fid));
+ rc = -EINVAL;
+ }
+ return rc;
+}
diff --git a/drivers/staging/lustre/lustre/lmv/lmv_intent.c b/drivers/staging/lustre/lustre/lmv/lmv_intent.c
new file mode 100644
index 000000000..d22d57b4f
--- /dev/null
+++ b/drivers/staging/lustre/lustre/lmv/lmv_intent.c
@@ -0,0 +1,323 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2011, 2012, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ */
+
+#define DEBUG_SUBSYSTEM S_LMV
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/pagemap.h>
+#include <asm/div64.h>
+#include <linux/seq_file.h>
+#include <linux/namei.h>
+#include "../include/lustre_intent.h"
+#include "../include/obd_support.h"
+#include "../include/lustre/lustre_idl.h"
+#include "../include/lustre_lib.h"
+#include "../include/lustre_net.h"
+#include "../include/lustre_dlm.h"
+#include "../include/obd_class.h"
+#include "../include/lprocfs_status.h"
+#include "lmv_internal.h"
+
+static int lmv_intent_remote(struct obd_export *exp, void *lmm,
+ int lmmsize, struct lookup_intent *it,
+ const struct lu_fid *parent_fid, int flags,
+ struct ptlrpc_request **reqp,
+ ldlm_blocking_callback cb_blocking,
+ __u64 extra_lock_flags)
+{
+ struct obd_device *obd = exp->exp_obd;
+ struct lmv_obd *lmv = &obd->u.lmv;
+ struct ptlrpc_request *req = NULL;
+ struct lustre_handle plock;
+ struct md_op_data *op_data;
+ struct lmv_tgt_desc *tgt;
+ struct mdt_body *body;
+ int pmode;
+ int rc = 0;
+
+ body = req_capsule_server_get(&(*reqp)->rq_pill, &RMF_MDT_BODY);
+ if (body == NULL)
+ return -EPROTO;
+
+ LASSERT((body->valid & OBD_MD_MDS));
+
+ /*
+ * Unfortunately, we have to lie to MDC/MDS to retrieve
+ * attributes llite needs and provideproper locking.
+ */
+ if (it->it_op & IT_LOOKUP)
+ it->it_op = IT_GETATTR;
+
+ /*
+ * We got LOOKUP lock, but we really need attrs.
+ */
+ pmode = it->d.lustre.it_lock_mode;
+ if (pmode) {
+ plock.cookie = it->d.lustre.it_lock_handle;
+ it->d.lustre.it_lock_mode = 0;
+ it->d.lustre.it_data = NULL;
+ }
+
+ LASSERT(fid_is_sane(&body->fid1));
+
+ tgt = lmv_find_target(lmv, &body->fid1);
+ if (IS_ERR(tgt)) {
+ rc = PTR_ERR(tgt);
+ goto out;
+ }
+
+ OBD_ALLOC_PTR(op_data);
+ if (op_data == NULL) {
+ rc = -ENOMEM;
+ goto out;
+ }
+
+ op_data->op_fid1 = body->fid1;
+ /* Sent the parent FID to the remote MDT */
+ if (parent_fid != NULL) {
+ /* The parent fid is only for remote open to
+ * check whether the open is from OBF,
+ * see mdt_cross_open */
+ LASSERT(it->it_op & IT_OPEN);
+ op_data->op_fid2 = *parent_fid;
+ /* Add object FID to op_fid3, in case it needs to check stale
+ * (M_CHECK_STALE), see mdc_finish_intent_lock */
+ op_data->op_fid3 = body->fid1;
+ }
+
+ op_data->op_bias = MDS_CROSS_REF;
+ CDEBUG(D_INODE, "REMOTE_INTENT with fid="DFID" -> mds #%d\n",
+ PFID(&body->fid1), tgt->ltd_idx);
+
+ rc = md_intent_lock(tgt->ltd_exp, op_data, lmm, lmmsize, it,
+ flags, &req, cb_blocking, extra_lock_flags);
+ if (rc)
+ goto out_free_op_data;
+
+ /*
+ * LLite needs LOOKUP lock to track dentry revocation in order to
+ * maintain dcache consistency. Thus drop UPDATE|PERM lock here
+ * and put LOOKUP in request.
+ */
+ if (it->d.lustre.it_lock_mode != 0) {
+ it->d.lustre.it_remote_lock_handle =
+ it->d.lustre.it_lock_handle;
+ it->d.lustre.it_remote_lock_mode = it->d.lustre.it_lock_mode;
+ }
+
+ it->d.lustre.it_lock_handle = plock.cookie;
+ it->d.lustre.it_lock_mode = pmode;
+
+out_free_op_data:
+ OBD_FREE_PTR(op_data);
+out:
+ if (rc && pmode)
+ ldlm_lock_decref(&plock, pmode);
+
+ ptlrpc_req_finished(*reqp);
+ *reqp = req;
+ return rc;
+}
+
+/*
+ * IT_OPEN is intended to open (and create, possible) an object. Parent (pid)
+ * may be split dir.
+ */
+int lmv_intent_open(struct obd_export *exp, struct md_op_data *op_data,
+ void *lmm, int lmmsize, struct lookup_intent *it,
+ int flags, struct ptlrpc_request **reqp,
+ ldlm_blocking_callback cb_blocking,
+ __u64 extra_lock_flags)
+{
+ struct obd_device *obd = exp->exp_obd;
+ struct lmv_obd *lmv = &obd->u.lmv;
+ struct lmv_tgt_desc *tgt;
+ struct mdt_body *body;
+ int rc;
+
+ tgt = lmv_locate_mds(lmv, op_data, &op_data->op_fid1);
+ if (IS_ERR(tgt))
+ return PTR_ERR(tgt);
+
+ /* If it is ready to open the file by FID, do not need
+ * allocate FID at all, otherwise it will confuse MDT */
+ if ((it->it_op & IT_CREAT) &&
+ !(it->it_flags & MDS_OPEN_BY_FID)) {
+ /*
+ * For open with IT_CREATE and for IT_CREATE cases allocate new
+ * fid and setup FLD for it.
+ */
+ op_data->op_fid3 = op_data->op_fid2;
+ rc = lmv_fid_alloc(exp, &op_data->op_fid2, op_data);
+ if (rc != 0)
+ return rc;
+ }
+
+ CDEBUG(D_INODE, "OPEN_INTENT with fid1=" DFID ", fid2=" DFID ", name='%s' -> mds #%d\n",
+ PFID(&op_data->op_fid1),
+ PFID(&op_data->op_fid2), op_data->op_name, tgt->ltd_idx);
+
+ rc = md_intent_lock(tgt->ltd_exp, op_data, lmm, lmmsize, it, flags,
+ reqp, cb_blocking, extra_lock_flags);
+ if (rc != 0)
+ return rc;
+ /*
+ * Nothing is found, do not access body->fid1 as it is zero and thus
+ * pointless.
+ */
+ if ((it->d.lustre.it_disposition & DISP_LOOKUP_NEG) &&
+ !(it->d.lustre.it_disposition & DISP_OPEN_CREATE) &&
+ !(it->d.lustre.it_disposition & DISP_OPEN_OPEN))
+ return rc;
+
+ body = req_capsule_server_get(&(*reqp)->rq_pill, &RMF_MDT_BODY);
+ if (body == NULL)
+ return -EPROTO;
+ /*
+ * Not cross-ref case, just get out of here.
+ */
+ if (likely(!(body->valid & OBD_MD_MDS)))
+ return 0;
+
+ /*
+ * Okay, MDS has returned success. Probably name has been resolved in
+ * remote inode.
+ */
+ rc = lmv_intent_remote(exp, lmm, lmmsize, it, &op_data->op_fid1, flags,
+ reqp, cb_blocking, extra_lock_flags);
+ if (rc != 0) {
+ LASSERT(rc < 0);
+ /*
+ * This is possible, that some userspace application will try to
+ * open file as directory and we will have -ENOTDIR here. As
+ * this is normal situation, we should not print error here,
+ * only debug info.
+ */
+ CDEBUG(D_INODE, "Can't handle remote %s: dir " DFID "(" DFID "):%*s: %d\n",
+ LL_IT2STR(it), PFID(&op_data->op_fid2),
+ PFID(&op_data->op_fid1), op_data->op_namelen,
+ op_data->op_name, rc);
+ return rc;
+ }
+
+ return rc;
+}
+
+/*
+ * Handler for: getattr, lookup and revalidate cases.
+ */
+int lmv_intent_lookup(struct obd_export *exp, struct md_op_data *op_data,
+ void *lmm, int lmmsize, struct lookup_intent *it,
+ int flags, struct ptlrpc_request **reqp,
+ ldlm_blocking_callback cb_blocking,
+ __u64 extra_lock_flags)
+{
+ struct obd_device *obd = exp->exp_obd;
+ struct lmv_obd *lmv = &obd->u.lmv;
+ struct lmv_tgt_desc *tgt = NULL;
+ struct mdt_body *body;
+ int rc = 0;
+
+ tgt = lmv_locate_mds(lmv, op_data, &op_data->op_fid1);
+ if (IS_ERR(tgt))
+ return PTR_ERR(tgt);
+
+ if (!fid_is_sane(&op_data->op_fid2))
+ fid_zero(&op_data->op_fid2);
+
+ CDEBUG(D_INODE, "LOOKUP_INTENT with fid1="DFID", fid2="DFID
+ ", name='%s' -> mds #%d\n", PFID(&op_data->op_fid1),
+ PFID(&op_data->op_fid2),
+ op_data->op_name ? op_data->op_name : "<NULL>",
+ tgt->ltd_idx);
+
+ op_data->op_bias &= ~MDS_CROSS_REF;
+
+ rc = md_intent_lock(tgt->ltd_exp, op_data, lmm, lmmsize, it,
+ flags, reqp, cb_blocking, extra_lock_flags);
+
+ if (rc < 0 || *reqp == NULL)
+ return rc;
+
+ /*
+ * MDS has returned success. Probably name has been resolved in
+ * remote inode. Let's check this.
+ */
+ body = req_capsule_server_get(&(*reqp)->rq_pill, &RMF_MDT_BODY);
+ if (body == NULL)
+ return -EPROTO;
+ /* Not cross-ref case, just get out of here. */
+ if (likely(!(body->valid & OBD_MD_MDS)))
+ return 0;
+
+ rc = lmv_intent_remote(exp, lmm, lmmsize, it, NULL, flags, reqp,
+ cb_blocking, extra_lock_flags);
+
+ return rc;
+}
+
+int lmv_intent_lock(struct obd_export *exp, struct md_op_data *op_data,
+ void *lmm, int lmmsize, struct lookup_intent *it,
+ int flags, struct ptlrpc_request **reqp,
+ ldlm_blocking_callback cb_blocking,
+ __u64 extra_lock_flags)
+{
+ struct obd_device *obd = exp->exp_obd;
+ int rc;
+
+ LASSERT(it != NULL);
+ LASSERT(fid_is_sane(&op_data->op_fid1));
+
+ CDEBUG(D_INODE, "INTENT LOCK '%s' for '%*s' on "DFID"\n",
+ LL_IT2STR(it), op_data->op_namelen, op_data->op_name,
+ PFID(&op_data->op_fid1));
+
+ rc = lmv_check_connect(obd);
+ if (rc)
+ return rc;
+
+ if (it->it_op & (IT_LOOKUP | IT_GETATTR | IT_LAYOUT))
+ rc = lmv_intent_lookup(exp, op_data, lmm, lmmsize, it,
+ flags, reqp, cb_blocking,
+ extra_lock_flags);
+ else if (it->it_op & IT_OPEN)
+ rc = lmv_intent_open(exp, op_data, lmm, lmmsize, it,
+ flags, reqp, cb_blocking,
+ extra_lock_flags);
+ else
+ LBUG();
+ return rc;
+}
diff --git a/drivers/staging/lustre/lustre/lmv/lmv_internal.h b/drivers/staging/lustre/lustre/lmv/lmv_internal.h
new file mode 100644
index 000000000..852d78721
--- /dev/null
+++ b/drivers/staging/lustre/lustre/lmv/lmv_internal.h
@@ -0,0 +1,157 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2011, 2012, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ */
+
+#ifndef _LMV_INTERNAL_H_
+#define _LMV_INTERNAL_H_
+
+#include "../include/lustre/lustre_idl.h"
+#include "../include/obd.h"
+
+#define LMV_MAX_TGT_COUNT 128
+
+#define lmv_init_lock(lmv) mutex_lock(&lmv->init_mutex)
+#define lmv_init_unlock(lmv) mutex_unlock(&lmv->init_mutex)
+
+#define LL_IT2STR(it) \
+ ((it) ? ldlm_it2str((it)->it_op) : "0")
+
+int lmv_check_connect(struct obd_device *obd);
+
+int lmv_intent_lock(struct obd_export *exp, struct md_op_data *op_data,
+ void *lmm, int lmmsize, struct lookup_intent *it,
+ int flags, struct ptlrpc_request **reqp,
+ ldlm_blocking_callback cb_blocking,
+ __u64 extra_lock_flags);
+
+int lmv_intent_lookup(struct obd_export *exp, struct md_op_data *op_data,
+ void *lmm, int lmmsize, struct lookup_intent *it,
+ int flags, struct ptlrpc_request **reqp,
+ ldlm_blocking_callback cb_blocking,
+ __u64 extra_lock_flags);
+
+int lmv_intent_open(struct obd_export *exp, struct md_op_data *op_data,
+ void *lmm, int lmmsize, struct lookup_intent *it,
+ int flags, struct ptlrpc_request **reqp,
+ ldlm_blocking_callback cb_blocking,
+ __u64 extra_lock_flags);
+
+int lmv_blocking_ast(struct ldlm_lock *, struct ldlm_lock_desc *,
+ void *, int);
+int lmv_fld_lookup(struct lmv_obd *lmv, const struct lu_fid *fid, u32 *mds);
+int __lmv_fid_alloc(struct lmv_obd *lmv, struct lu_fid *fid, u32 mds);
+int lmv_fid_alloc(struct obd_export *exp, struct lu_fid *fid,
+ struct md_op_data *op_data);
+
+static inline struct lmv_stripe_md *lmv_get_mea(struct ptlrpc_request *req)
+{
+ struct mdt_body *body;
+ struct lmv_stripe_md *mea;
+
+ LASSERT(req != NULL);
+
+ body = req_capsule_server_get(&req->rq_pill, &RMF_MDT_BODY);
+
+ if (!body || !S_ISDIR(body->mode) || !body->eadatasize)
+ return NULL;
+
+ mea = req_capsule_server_sized_get(&req->rq_pill, &RMF_MDT_MD,
+ body->eadatasize);
+ LASSERT(mea != NULL);
+
+ if (mea->mea_count == 0)
+ return NULL;
+ if (mea->mea_magic != MEA_MAGIC_LAST_CHAR &&
+ mea->mea_magic != MEA_MAGIC_ALL_CHARS &&
+ mea->mea_magic != MEA_MAGIC_HASH_SEGMENT)
+ return NULL;
+
+ return mea;
+}
+
+static inline int lmv_get_easize(struct lmv_obd *lmv)
+{
+ return sizeof(struct lmv_stripe_md) +
+ lmv->desc.ld_tgt_count *
+ sizeof(struct lu_fid);
+}
+
+static inline struct lmv_tgt_desc *
+lmv_get_target(struct lmv_obd *lmv, u32 mds)
+{
+ int count = lmv->desc.ld_tgt_count;
+ int i;
+
+ for (i = 0; i < count; i++) {
+ if (lmv->tgts[i] == NULL)
+ continue;
+
+ if (lmv->tgts[i]->ltd_idx == mds)
+ return lmv->tgts[i];
+ }
+
+ return ERR_PTR(-ENODEV);
+}
+
+static inline struct lmv_tgt_desc *
+lmv_find_target(struct lmv_obd *lmv, const struct lu_fid *fid)
+{
+ u32 mds = 0;
+ int rc;
+
+ if (lmv->desc.ld_tgt_count > 1) {
+ rc = lmv_fld_lookup(lmv, fid, &mds);
+ if (rc)
+ return ERR_PTR(rc);
+ }
+
+ return lmv_get_target(lmv, mds);
+}
+
+struct lmv_tgt_desc
+*lmv_locate_mds(struct lmv_obd *lmv, struct md_op_data *op_data,
+ struct lu_fid *fid);
+/* lproc_lmv.c */
+#if defined(CONFIG_PROC_FS)
+void lprocfs_lmv_init_vars(struct lprocfs_static_vars *lvars);
+#else
+static inline void lprocfs_lmv_init_vars(struct lprocfs_static_vars *lvars)
+{
+ memset(lvars, 0, sizeof(*lvars));
+}
+#endif
+extern struct file_operations lmv_proc_target_fops;
+
+#endif
diff --git a/drivers/staging/lustre/lustre/lmv/lmv_obd.c b/drivers/staging/lustre/lustre/lmv/lmv_obd.c
new file mode 100644
index 000000000..b9459faf8
--- /dev/null
+++ b/drivers/staging/lustre/lustre/lmv/lmv_obd.c
@@ -0,0 +1,2892 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2011, 2012, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ */
+
+#define DEBUG_SUBSYSTEM S_LMV
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/pagemap.h>
+#include <linux/mm.h>
+#include <asm/div64.h>
+#include <linux/seq_file.h>
+#include <linux/namei.h>
+#include <linux/uaccess.h>
+
+#include "../include/lustre/lustre_idl.h"
+#include "../include/obd_support.h"
+#include "../include/lustre_lib.h"
+#include "../include/lustre_net.h"
+#include "../include/obd_class.h"
+#include "../include/lprocfs_status.h"
+#include "../include/lustre_lite.h"
+#include "../include/lustre_fid.h"
+#include "lmv_internal.h"
+
+static void lmv_activate_target(struct lmv_obd *lmv,
+ struct lmv_tgt_desc *tgt,
+ int activate)
+{
+ if (tgt->ltd_active == activate)
+ return;
+
+ tgt->ltd_active = activate;
+ lmv->desc.ld_active_tgt_count += (activate ? 1 : -1);
+}
+
+/**
+ * Error codes:
+ *
+ * -EINVAL : UUID can't be found in the LMV's target list
+ * -ENOTCONN: The UUID is found, but the target connection is bad (!)
+ * -EBADF : The UUID is found, but the OBD of the wrong type (!)
+ */
+static int lmv_set_mdc_active(struct lmv_obd *lmv, struct obd_uuid *uuid,
+ int activate)
+{
+ struct lmv_tgt_desc *uninitialized_var(tgt);
+ struct obd_device *obd;
+ int i;
+ int rc = 0;
+
+ CDEBUG(D_INFO, "Searching in lmv %p for uuid %s (activate=%d)\n",
+ lmv, uuid->uuid, activate);
+
+ spin_lock(&lmv->lmv_lock);
+ for (i = 0; i < lmv->desc.ld_tgt_count; i++) {
+ tgt = lmv->tgts[i];
+ if (tgt == NULL || tgt->ltd_exp == NULL)
+ continue;
+
+ CDEBUG(D_INFO, "Target idx %d is %s conn %#llx\n", i,
+ tgt->ltd_uuid.uuid, tgt->ltd_exp->exp_handle.h_cookie);
+
+ if (obd_uuid_equals(uuid, &tgt->ltd_uuid))
+ break;
+ }
+
+ if (i == lmv->desc.ld_tgt_count) {
+ rc = -EINVAL;
+ goto out_lmv_lock;
+ }
+
+ obd = class_exp2obd(tgt->ltd_exp);
+ if (obd == NULL) {
+ rc = -ENOTCONN;
+ goto out_lmv_lock;
+ }
+
+ CDEBUG(D_INFO, "Found OBD %s=%s device %d (%p) type %s at LMV idx %d\n",
+ obd->obd_name, obd->obd_uuid.uuid, obd->obd_minor, obd,
+ obd->obd_type->typ_name, i);
+ LASSERT(strcmp(obd->obd_type->typ_name, LUSTRE_MDC_NAME) == 0);
+
+ if (tgt->ltd_active == activate) {
+ CDEBUG(D_INFO, "OBD %p already %sactive!\n", obd,
+ activate ? "" : "in");
+ goto out_lmv_lock;
+ }
+
+ CDEBUG(D_INFO, "Marking OBD %p %sactive\n", obd,
+ activate ? "" : "in");
+ lmv_activate_target(lmv, tgt, activate);
+
+ out_lmv_lock:
+ spin_unlock(&lmv->lmv_lock);
+ return rc;
+}
+
+static struct obd_uuid *lmv_get_uuid(struct obd_export *exp)
+{
+ struct lmv_obd *lmv = &exp->exp_obd->u.lmv;
+
+ return obd_get_uuid(lmv->tgts[0]->ltd_exp);
+}
+
+static int lmv_notify(struct obd_device *obd, struct obd_device *watched,
+ enum obd_notify_event ev, void *data)
+{
+ struct obd_connect_data *conn_data;
+ struct lmv_obd *lmv = &obd->u.lmv;
+ struct obd_uuid *uuid;
+ int rc = 0;
+
+ if (strcmp(watched->obd_type->typ_name, LUSTRE_MDC_NAME)) {
+ CERROR("unexpected notification of %s %s!\n",
+ watched->obd_type->typ_name,
+ watched->obd_name);
+ return -EINVAL;
+ }
+
+ uuid = &watched->u.cli.cl_target_uuid;
+ if (ev == OBD_NOTIFY_ACTIVE || ev == OBD_NOTIFY_INACTIVE) {
+ /*
+ * Set MDC as active before notifying the observer, so the
+ * observer can use the MDC normally.
+ */
+ rc = lmv_set_mdc_active(lmv, uuid,
+ ev == OBD_NOTIFY_ACTIVE);
+ if (rc) {
+ CERROR("%sactivation of %s failed: %d\n",
+ ev == OBD_NOTIFY_ACTIVE ? "" : "de",
+ uuid->uuid, rc);
+ return rc;
+ }
+ } else if (ev == OBD_NOTIFY_OCD) {
+ conn_data = &watched->u.cli.cl_import->imp_connect_data;
+ /*
+ * XXX: Make sure that ocd_connect_flags from all targets are
+ * the same. Otherwise one of MDTs runs wrong version or
+ * something like this. --umka
+ */
+ obd->obd_self_export->exp_connect_data = *conn_data;
+ }
+#if 0
+ else if (ev == OBD_NOTIFY_DISCON) {
+ /*
+ * For disconnect event, flush fld cache for failout MDS case.
+ */
+ fld_client_flush(&lmv->lmv_fld);
+ }
+#endif
+ /*
+ * Pass the notification up the chain.
+ */
+ if (obd->obd_observer)
+ rc = obd_notify(obd->obd_observer, watched, ev, data);
+
+ return rc;
+}
+
+/**
+ * This is fake connect function. Its purpose is to initialize lmv and say
+ * caller that everything is okay. Real connection will be performed later.
+ */
+static int lmv_connect(const struct lu_env *env,
+ struct obd_export **exp, struct obd_device *obd,
+ struct obd_uuid *cluuid, struct obd_connect_data *data,
+ void *localdata)
+{
+ struct proc_dir_entry *lmv_proc_dir;
+ struct lmv_obd *lmv = &obd->u.lmv;
+ struct lustre_handle conn = { 0 };
+ int rc = 0;
+
+ /*
+ * We don't want to actually do the underlying connections more than
+ * once, so keep track.
+ */
+ lmv->refcount++;
+ if (lmv->refcount > 1) {
+ *exp = NULL;
+ return 0;
+ }
+
+ rc = class_connect(&conn, obd, cluuid);
+ if (rc) {
+ CERROR("class_connection() returned %d\n", rc);
+ return rc;
+ }
+
+ *exp = class_conn2export(&conn);
+ class_export_get(*exp);
+
+ lmv->exp = *exp;
+ lmv->connected = 0;
+ lmv->cluuid = *cluuid;
+
+ if (data)
+ lmv->conn_data = *data;
+
+ if (obd->obd_proc_private != NULL) {
+ lmv_proc_dir = obd->obd_proc_private;
+ } else {
+ lmv_proc_dir = lprocfs_register("target_obds", obd->obd_proc_entry,
+ NULL, NULL);
+ if (IS_ERR(lmv_proc_dir)) {
+ CERROR("could not register /proc/fs/lustre/%s/%s/target_obds.",
+ obd->obd_type->typ_name, obd->obd_name);
+ lmv_proc_dir = NULL;
+ }
+ obd->obd_proc_private = lmv_proc_dir;
+ }
+
+ /*
+ * All real clients should perform actual connection right away, because
+ * it is possible, that LMV will not have opportunity to connect targets
+ * and MDC stuff will be called directly, for instance while reading
+ * ../mdc/../kbytesfree procfs file, etc.
+ */
+ if (data->ocd_connect_flags & OBD_CONNECT_REAL)
+ rc = lmv_check_connect(obd);
+
+ if (rc && lmv_proc_dir) {
+ lprocfs_remove(&lmv_proc_dir);
+ obd->obd_proc_private = NULL;
+ }
+
+ return rc;
+}
+
+static void lmv_set_timeouts(struct obd_device *obd)
+{
+ struct lmv_tgt_desc *tgt;
+ struct lmv_obd *lmv;
+ int i;
+
+ lmv = &obd->u.lmv;
+ if (lmv->server_timeout == 0)
+ return;
+
+ if (lmv->connected == 0)
+ return;
+
+ for (i = 0; i < lmv->desc.ld_tgt_count; i++) {
+ tgt = lmv->tgts[i];
+ if (tgt == NULL || tgt->ltd_exp == NULL || tgt->ltd_active == 0)
+ continue;
+
+ obd_set_info_async(NULL, tgt->ltd_exp, sizeof(KEY_INTERMDS),
+ KEY_INTERMDS, 0, NULL, NULL);
+ }
+}
+
+static int lmv_init_ea_size(struct obd_export *exp, int easize,
+ int def_easize, int cookiesize, int def_cookiesize)
+{
+ struct obd_device *obd = exp->exp_obd;
+ struct lmv_obd *lmv = &obd->u.lmv;
+ int i;
+ int rc = 0;
+ int change = 0;
+
+ if (lmv->max_easize < easize) {
+ lmv->max_easize = easize;
+ change = 1;
+ }
+ if (lmv->max_def_easize < def_easize) {
+ lmv->max_def_easize = def_easize;
+ change = 1;
+ }
+ if (lmv->max_cookiesize < cookiesize) {
+ lmv->max_cookiesize = cookiesize;
+ change = 1;
+ }
+ if (lmv->max_def_cookiesize < def_cookiesize) {
+ lmv->max_def_cookiesize = def_cookiesize;
+ change = 1;
+ }
+ if (change == 0)
+ return 0;
+
+ if (lmv->connected == 0)
+ return 0;
+
+ for (i = 0; i < lmv->desc.ld_tgt_count; i++) {
+ if (lmv->tgts[i] == NULL ||
+ lmv->tgts[i]->ltd_exp == NULL ||
+ lmv->tgts[i]->ltd_active == 0) {
+ CWARN("%s: NULL export for %d\n", obd->obd_name, i);
+ continue;
+ }
+
+ rc = md_init_ea_size(lmv->tgts[i]->ltd_exp, easize, def_easize,
+ cookiesize, def_cookiesize);
+ if (rc) {
+ CERROR("%s: obd_init_ea_size() failed on MDT target %d: rc = %d.\n",
+ obd->obd_name, i, rc);
+ break;
+ }
+ }
+ return rc;
+}
+
+#define MAX_STRING_SIZE 128
+
+static int lmv_connect_mdc(struct obd_device *obd, struct lmv_tgt_desc *tgt)
+{
+ struct proc_dir_entry *lmv_proc_dir;
+ struct lmv_obd *lmv = &obd->u.lmv;
+ struct obd_uuid *cluuid = &lmv->cluuid;
+ struct obd_uuid lmv_mdc_uuid = { "LMV_MDC_UUID" };
+ struct obd_device *mdc_obd;
+ struct obd_export *mdc_exp;
+ struct lu_fld_target target;
+ int rc;
+
+ mdc_obd = class_find_client_obd(&tgt->ltd_uuid, LUSTRE_MDC_NAME,
+ &obd->obd_uuid);
+ if (!mdc_obd) {
+ CERROR("target %s not attached\n", tgt->ltd_uuid.uuid);
+ return -EINVAL;
+ }
+
+ CDEBUG(D_CONFIG, "connect to %s(%s) - %s, %s FOR %s\n",
+ mdc_obd->obd_name, mdc_obd->obd_uuid.uuid,
+ tgt->ltd_uuid.uuid, obd->obd_uuid.uuid,
+ cluuid->uuid);
+
+ if (!mdc_obd->obd_set_up) {
+ CERROR("target %s is not set up\n", tgt->ltd_uuid.uuid);
+ return -EINVAL;
+ }
+
+ rc = obd_connect(NULL, &mdc_exp, mdc_obd, &lmv_mdc_uuid,
+ &lmv->conn_data, NULL);
+ if (rc) {
+ CERROR("target %s connect error %d\n", tgt->ltd_uuid.uuid, rc);
+ return rc;
+ }
+
+ /*
+ * Init fid sequence client for this mdc and add new fld target.
+ */
+ rc = obd_fid_init(mdc_obd, mdc_exp, LUSTRE_SEQ_METADATA);
+ if (rc)
+ return rc;
+
+ target.ft_srv = NULL;
+ target.ft_exp = mdc_exp;
+ target.ft_idx = tgt->ltd_idx;
+
+ fld_client_add_target(&lmv->lmv_fld, &target);
+
+ rc = obd_register_observer(mdc_obd, obd);
+ if (rc) {
+ obd_disconnect(mdc_exp);
+ CERROR("target %s register_observer error %d\n",
+ tgt->ltd_uuid.uuid, rc);
+ return rc;
+ }
+
+ if (obd->obd_observer) {
+ /*
+ * Tell the observer about the new target.
+ */
+ rc = obd_notify(obd->obd_observer, mdc_exp->exp_obd,
+ OBD_NOTIFY_ACTIVE,
+ (void *)(tgt - lmv->tgts[0]));
+ if (rc) {
+ obd_disconnect(mdc_exp);
+ return rc;
+ }
+ }
+
+ tgt->ltd_active = 1;
+ tgt->ltd_exp = mdc_exp;
+ lmv->desc.ld_active_tgt_count++;
+
+ md_init_ea_size(tgt->ltd_exp, lmv->max_easize, lmv->max_def_easize,
+ lmv->max_cookiesize, lmv->max_def_cookiesize);
+
+ CDEBUG(D_CONFIG, "Connected to %s(%s) successfully (%d)\n",
+ mdc_obd->obd_name, mdc_obd->obd_uuid.uuid,
+ atomic_read(&obd->obd_refcount));
+
+ lmv_proc_dir = obd->obd_proc_private;
+ if (lmv_proc_dir) {
+ struct proc_dir_entry *mdc_symlink;
+
+ LASSERT(mdc_obd->obd_type != NULL);
+ LASSERT(mdc_obd->obd_type->typ_name != NULL);
+ mdc_symlink = lprocfs_add_symlink(mdc_obd->obd_name,
+ lmv_proc_dir,
+ "../../../%s/%s",
+ mdc_obd->obd_type->typ_name,
+ mdc_obd->obd_name);
+ if (mdc_symlink == NULL) {
+ CERROR("Could not register LMV target /proc/fs/lustre/%s/%s/target_obds/%s.",
+ obd->obd_type->typ_name, obd->obd_name,
+ mdc_obd->obd_name);
+ lprocfs_remove(&lmv_proc_dir);
+ obd->obd_proc_private = NULL;
+ }
+ }
+ return 0;
+}
+
+static void lmv_del_target(struct lmv_obd *lmv, int index)
+{
+ if (lmv->tgts[index] == NULL)
+ return;
+
+ OBD_FREE_PTR(lmv->tgts[index]);
+ lmv->tgts[index] = NULL;
+ return;
+}
+
+static int lmv_add_target(struct obd_device *obd, struct obd_uuid *uuidp,
+ __u32 index, int gen)
+{
+ struct lmv_obd *lmv = &obd->u.lmv;
+ struct lmv_tgt_desc *tgt;
+ int rc = 0;
+
+ CDEBUG(D_CONFIG, "Target uuid: %s. index %d\n", uuidp->uuid, index);
+
+ lmv_init_lock(lmv);
+
+ if (lmv->desc.ld_tgt_count == 0) {
+ struct obd_device *mdc_obd;
+
+ mdc_obd = class_find_client_obd(uuidp, LUSTRE_MDC_NAME,
+ &obd->obd_uuid);
+ if (!mdc_obd) {
+ lmv_init_unlock(lmv);
+ CERROR("%s: Target %s not attached: rc = %d\n",
+ obd->obd_name, uuidp->uuid, -EINVAL);
+ return -EINVAL;
+ }
+ }
+
+ if ((index < lmv->tgts_size) && (lmv->tgts[index] != NULL)) {
+ tgt = lmv->tgts[index];
+ CERROR("%s: UUID %s already assigned at LOV target index %d: rc = %d\n",
+ obd->obd_name,
+ obd_uuid2str(&tgt->ltd_uuid), index, -EEXIST);
+ lmv_init_unlock(lmv);
+ return -EEXIST;
+ }
+
+ if (index >= lmv->tgts_size) {
+ /* We need to reallocate the lmv target array. */
+ struct lmv_tgt_desc **newtgts, **old = NULL;
+ __u32 newsize = 1;
+ __u32 oldsize = 0;
+
+ while (newsize < index + 1)
+ newsize <<= 1;
+ OBD_ALLOC(newtgts, sizeof(*newtgts) * newsize);
+ if (newtgts == NULL) {
+ lmv_init_unlock(lmv);
+ return -ENOMEM;
+ }
+
+ if (lmv->tgts_size) {
+ memcpy(newtgts, lmv->tgts,
+ sizeof(*newtgts) * lmv->tgts_size);
+ old = lmv->tgts;
+ oldsize = lmv->tgts_size;
+ }
+
+ lmv->tgts = newtgts;
+ lmv->tgts_size = newsize;
+ smp_rmb();
+ if (old)
+ OBD_FREE(old, sizeof(*old) * oldsize);
+
+ CDEBUG(D_CONFIG, "tgts: %p size: %d\n", lmv->tgts,
+ lmv->tgts_size);
+ }
+
+ OBD_ALLOC_PTR(tgt);
+ if (!tgt) {
+ lmv_init_unlock(lmv);
+ return -ENOMEM;
+ }
+
+ mutex_init(&tgt->ltd_fid_mutex);
+ tgt->ltd_idx = index;
+ tgt->ltd_uuid = *uuidp;
+ tgt->ltd_active = 0;
+ lmv->tgts[index] = tgt;
+ if (index >= lmv->desc.ld_tgt_count)
+ lmv->desc.ld_tgt_count = index + 1;
+
+ if (lmv->connected) {
+ rc = lmv_connect_mdc(obd, tgt);
+ if (rc) {
+ spin_lock(&lmv->lmv_lock);
+ lmv->desc.ld_tgt_count--;
+ memset(tgt, 0, sizeof(*tgt));
+ spin_unlock(&lmv->lmv_lock);
+ } else {
+ int easize = sizeof(struct lmv_stripe_md) +
+ lmv->desc.ld_tgt_count * sizeof(struct lu_fid);
+ lmv_init_ea_size(obd->obd_self_export, easize, 0, 0, 0);
+ }
+ }
+
+ lmv_init_unlock(lmv);
+ return rc;
+}
+
+int lmv_check_connect(struct obd_device *obd)
+{
+ struct lmv_obd *lmv = &obd->u.lmv;
+ struct lmv_tgt_desc *tgt;
+ int i;
+ int rc;
+ int easize;
+
+ if (lmv->connected)
+ return 0;
+
+ lmv_init_lock(lmv);
+ if (lmv->connected) {
+ lmv_init_unlock(lmv);
+ return 0;
+ }
+
+ if (lmv->desc.ld_tgt_count == 0) {
+ lmv_init_unlock(lmv);
+ CERROR("%s: no targets configured.\n", obd->obd_name);
+ return -EINVAL;
+ }
+
+ CDEBUG(D_CONFIG, "Time to connect %s to %s\n",
+ lmv->cluuid.uuid, obd->obd_name);
+
+ LASSERT(lmv->tgts != NULL);
+
+ for (i = 0; i < lmv->desc.ld_tgt_count; i++) {
+ tgt = lmv->tgts[i];
+ if (tgt == NULL)
+ continue;
+ rc = lmv_connect_mdc(obd, tgt);
+ if (rc)
+ goto out_disc;
+ }
+
+ lmv_set_timeouts(obd);
+ class_export_put(lmv->exp);
+ lmv->connected = 1;
+ easize = lmv_get_easize(lmv);
+ lmv_init_ea_size(obd->obd_self_export, easize, 0, 0, 0);
+ lmv_init_unlock(lmv);
+ return 0;
+
+ out_disc:
+ while (i-- > 0) {
+ int rc2;
+ tgt = lmv->tgts[i];
+ if (tgt == NULL)
+ continue;
+ tgt->ltd_active = 0;
+ if (tgt->ltd_exp) {
+ --lmv->desc.ld_active_tgt_count;
+ rc2 = obd_disconnect(tgt->ltd_exp);
+ if (rc2) {
+ CERROR("LMV target %s disconnect on MDC idx %d: error %d\n",
+ tgt->ltd_uuid.uuid, i, rc2);
+ }
+ }
+ }
+ class_disconnect(lmv->exp);
+ lmv_init_unlock(lmv);
+ return rc;
+}
+
+static int lmv_disconnect_mdc(struct obd_device *obd, struct lmv_tgt_desc *tgt)
+{
+ struct proc_dir_entry *lmv_proc_dir;
+ struct lmv_obd *lmv = &obd->u.lmv;
+ struct obd_device *mdc_obd;
+ int rc;
+
+ LASSERT(tgt != NULL);
+ LASSERT(obd != NULL);
+
+ mdc_obd = class_exp2obd(tgt->ltd_exp);
+
+ if (mdc_obd) {
+ mdc_obd->obd_force = obd->obd_force;
+ mdc_obd->obd_fail = obd->obd_fail;
+ mdc_obd->obd_no_recov = obd->obd_no_recov;
+ }
+
+ lmv_proc_dir = obd->obd_proc_private;
+ if (lmv_proc_dir)
+ lprocfs_remove_proc_entry(mdc_obd->obd_name, lmv_proc_dir);
+
+ rc = obd_fid_fini(tgt->ltd_exp->exp_obd);
+ if (rc)
+ CERROR("Can't finalize fids factory\n");
+
+ CDEBUG(D_INFO, "Disconnected from %s(%s) successfully\n",
+ tgt->ltd_exp->exp_obd->obd_name,
+ tgt->ltd_exp->exp_obd->obd_uuid.uuid);
+
+ obd_register_observer(tgt->ltd_exp->exp_obd, NULL);
+ rc = obd_disconnect(tgt->ltd_exp);
+ if (rc) {
+ if (tgt->ltd_active) {
+ CERROR("Target %s disconnect error %d\n",
+ tgt->ltd_uuid.uuid, rc);
+ }
+ }
+
+ lmv_activate_target(lmv, tgt, 0);
+ tgt->ltd_exp = NULL;
+ return 0;
+}
+
+static int lmv_disconnect(struct obd_export *exp)
+{
+ struct obd_device *obd = class_exp2obd(exp);
+ struct lmv_obd *lmv = &obd->u.lmv;
+ int rc;
+ int i;
+
+ if (!lmv->tgts)
+ goto out_local;
+
+ /*
+ * Only disconnect the underlying layers on the final disconnect.
+ */
+ lmv->refcount--;
+ if (lmv->refcount != 0)
+ goto out_local;
+
+ for (i = 0; i < lmv->desc.ld_tgt_count; i++) {
+ if (lmv->tgts[i] == NULL || lmv->tgts[i]->ltd_exp == NULL)
+ continue;
+
+ lmv_disconnect_mdc(obd, lmv->tgts[i]);
+ }
+
+ if (obd->obd_proc_private)
+ lprocfs_remove((struct proc_dir_entry **)&obd->obd_proc_private);
+ else
+ CERROR("/proc/fs/lustre/%s/%s/target_obds missing\n",
+ obd->obd_type->typ_name, obd->obd_name);
+
+out_local:
+ /*
+ * This is the case when no real connection is established by
+ * lmv_check_connect().
+ */
+ if (!lmv->connected)
+ class_export_put(exp);
+ rc = class_disconnect(exp);
+ if (lmv->refcount == 0)
+ lmv->connected = 0;
+ return rc;
+}
+
+static int lmv_fid2path(struct obd_export *exp, int len, void *karg, void *uarg)
+{
+ struct obd_device *obddev = class_exp2obd(exp);
+ struct lmv_obd *lmv = &obddev->u.lmv;
+ struct getinfo_fid2path *gf;
+ struct lmv_tgt_desc *tgt;
+ struct getinfo_fid2path *remote_gf = NULL;
+ int remote_gf_size = 0;
+ int rc;
+
+ gf = (struct getinfo_fid2path *)karg;
+ tgt = lmv_find_target(lmv, &gf->gf_fid);
+ if (IS_ERR(tgt))
+ return PTR_ERR(tgt);
+
+repeat_fid2path:
+ rc = obd_iocontrol(OBD_IOC_FID2PATH, tgt->ltd_exp, len, gf, uarg);
+ if (rc != 0 && rc != -EREMOTE)
+ goto out_fid2path;
+
+ /* If remote_gf != NULL, it means just building the
+ * path on the remote MDT, copy this path segment to gf */
+ if (remote_gf != NULL) {
+ struct getinfo_fid2path *ori_gf;
+ char *ptr;
+
+ ori_gf = (struct getinfo_fid2path *)karg;
+ if (strlen(ori_gf->gf_path) +
+ strlen(gf->gf_path) > ori_gf->gf_pathlen) {
+ rc = -EOVERFLOW;
+ goto out_fid2path;
+ }
+
+ ptr = ori_gf->gf_path;
+
+ memmove(ptr + strlen(gf->gf_path) + 1, ptr,
+ strlen(ori_gf->gf_path));
+
+ strncpy(ptr, gf->gf_path, strlen(gf->gf_path));
+ ptr += strlen(gf->gf_path);
+ *ptr = '/';
+ }
+
+ CDEBUG(D_INFO, "%s: get path %s "DFID" rec: %llu ln: %u\n",
+ tgt->ltd_exp->exp_obd->obd_name,
+ gf->gf_path, PFID(&gf->gf_fid), gf->gf_recno,
+ gf->gf_linkno);
+
+ if (rc == 0)
+ goto out_fid2path;
+
+ /* sigh, has to go to another MDT to do path building further */
+ if (remote_gf == NULL) {
+ remote_gf_size = sizeof(*remote_gf) + PATH_MAX;
+ OBD_ALLOC(remote_gf, remote_gf_size);
+ if (remote_gf == NULL) {
+ rc = -ENOMEM;
+ goto out_fid2path;
+ }
+ remote_gf->gf_pathlen = PATH_MAX;
+ }
+
+ if (!fid_is_sane(&gf->gf_fid)) {
+ CERROR("%s: invalid FID "DFID": rc = %d\n",
+ tgt->ltd_exp->exp_obd->obd_name,
+ PFID(&gf->gf_fid), -EINVAL);
+ rc = -EINVAL;
+ goto out_fid2path;
+ }
+
+ tgt = lmv_find_target(lmv, &gf->gf_fid);
+ if (IS_ERR(tgt)) {
+ rc = -EINVAL;
+ goto out_fid2path;
+ }
+
+ remote_gf->gf_fid = gf->gf_fid;
+ remote_gf->gf_recno = -1;
+ remote_gf->gf_linkno = -1;
+ memset(remote_gf->gf_path, 0, remote_gf->gf_pathlen);
+ gf = remote_gf;
+ goto repeat_fid2path;
+
+out_fid2path:
+ if (remote_gf != NULL)
+ OBD_FREE(remote_gf, remote_gf_size);
+ return rc;
+}
+
+static int lmv_hsm_req_count(struct lmv_obd *lmv,
+ const struct hsm_user_request *hur,
+ const struct lmv_tgt_desc *tgt_mds)
+{
+ int i, nr = 0;
+ struct lmv_tgt_desc *curr_tgt;
+
+ /* count how many requests must be sent to the given target */
+ for (i = 0; i < hur->hur_request.hr_itemcount; i++) {
+ curr_tgt = lmv_find_target(lmv, &hur->hur_user_item[i].hui_fid);
+ if (obd_uuid_equals(&curr_tgt->ltd_uuid, &tgt_mds->ltd_uuid))
+ nr++;
+ }
+ return nr;
+}
+
+static void lmv_hsm_req_build(struct lmv_obd *lmv,
+ struct hsm_user_request *hur_in,
+ const struct lmv_tgt_desc *tgt_mds,
+ struct hsm_user_request *hur_out)
+{
+ int i, nr_out;
+ struct lmv_tgt_desc *curr_tgt;
+
+ /* build the hsm_user_request for the given target */
+ hur_out->hur_request = hur_in->hur_request;
+ nr_out = 0;
+ for (i = 0; i < hur_in->hur_request.hr_itemcount; i++) {
+ curr_tgt = lmv_find_target(lmv,
+ &hur_in->hur_user_item[i].hui_fid);
+ if (obd_uuid_equals(&curr_tgt->ltd_uuid, &tgt_mds->ltd_uuid)) {
+ hur_out->hur_user_item[nr_out] =
+ hur_in->hur_user_item[i];
+ nr_out++;
+ }
+ }
+ hur_out->hur_request.hr_itemcount = nr_out;
+ memcpy(hur_data(hur_out), hur_data(hur_in),
+ hur_in->hur_request.hr_data_len);
+}
+
+static int lmv_hsm_ct_unregister(struct lmv_obd *lmv, unsigned int cmd, int len,
+ struct lustre_kernelcomm *lk, void *uarg)
+{
+ int i, rc = 0;
+
+ /* unregister request (call from llapi_hsm_copytool_fini) */
+ for (i = 0; i < lmv->desc.ld_tgt_count; i++) {
+ /* best effort: try to clean as much as possible
+ * (continue on error) */
+ obd_iocontrol(cmd, lmv->tgts[i]->ltd_exp, len, lk, uarg);
+ }
+
+ /* Whatever the result, remove copytool from kuc groups.
+ * Unreached coordinators will get EPIPE on next requests
+ * and will unregister automatically.
+ */
+ rc = libcfs_kkuc_group_rem(lk->lk_uid, lk->lk_group);
+ return rc;
+}
+
+static int lmv_hsm_ct_register(struct lmv_obd *lmv, unsigned int cmd, int len,
+ struct lustre_kernelcomm *lk, void *uarg)
+{
+ struct file *filp;
+ int i, j, err;
+ int rc = 0;
+ bool any_set = false;
+
+ /* All or nothing: try to register to all MDS.
+ * In case of failure, unregister from previous MDS,
+ * except if it because of inactive target. */
+ for (i = 0; i < lmv->desc.ld_tgt_count; i++) {
+ err = obd_iocontrol(cmd, lmv->tgts[i]->ltd_exp,
+ len, lk, uarg);
+ if (err) {
+ if (lmv->tgts[i]->ltd_active) {
+ /* permanent error */
+ CERROR("error: iocontrol MDC %s on MDTidx %d cmd %x: err = %d\n",
+ lmv->tgts[i]->ltd_uuid.uuid,
+ i, cmd, err);
+ rc = err;
+ lk->lk_flags |= LK_FLG_STOP;
+ /* unregister from previous MDS */
+ for (j = 0; j < i; j++)
+ obd_iocontrol(cmd,
+ lmv->tgts[j]->ltd_exp,
+ len, lk, uarg);
+ return rc;
+ }
+ /* else: transient error.
+ * kuc will register to the missing MDT
+ * when it is back */
+ } else {
+ any_set = true;
+ }
+ }
+
+ if (!any_set)
+ /* no registration done: return error */
+ return -ENOTCONN;
+
+ /* at least one registration done, with no failure */
+ filp = fget(lk->lk_wfd);
+ if (filp == NULL) {
+ return -EBADF;
+ }
+ rc = libcfs_kkuc_group_add(filp, lk->lk_uid, lk->lk_group, lk->lk_data);
+ if (rc != 0 && filp != NULL)
+ fput(filp);
+ return rc;
+}
+
+
+
+
+static int lmv_iocontrol(unsigned int cmd, struct obd_export *exp,
+ int len, void *karg, void *uarg)
+{
+ struct obd_device *obddev = class_exp2obd(exp);
+ struct lmv_obd *lmv = &obddev->u.lmv;
+ int i = 0;
+ int rc = 0;
+ int set = 0;
+ int count = lmv->desc.ld_tgt_count;
+
+ if (count == 0)
+ return -ENOTTY;
+
+ switch (cmd) {
+ case IOC_OBD_STATFS: {
+ struct obd_ioctl_data *data = karg;
+ struct obd_device *mdc_obd;
+ struct obd_statfs stat_buf = {0};
+ __u32 index;
+
+ memcpy(&index, data->ioc_inlbuf2, sizeof(__u32));
+ if (index >= count)
+ return -ENODEV;
+
+ if (lmv->tgts[index] == NULL ||
+ lmv->tgts[index]->ltd_active == 0)
+ return -ENODATA;
+
+ mdc_obd = class_exp2obd(lmv->tgts[index]->ltd_exp);
+ if (!mdc_obd)
+ return -EINVAL;
+
+ /* copy UUID */
+ if (copy_to_user(data->ioc_pbuf2, obd2cli_tgt(mdc_obd),
+ min((int) data->ioc_plen2,
+ (int) sizeof(struct obd_uuid))))
+ return -EFAULT;
+
+ rc = obd_statfs(NULL, lmv->tgts[index]->ltd_exp, &stat_buf,
+ cfs_time_shift_64(-OBD_STATFS_CACHE_SECONDS),
+ 0);
+ if (rc)
+ return rc;
+ if (copy_to_user(data->ioc_pbuf1, &stat_buf,
+ min((int) data->ioc_plen1,
+ (int) sizeof(stat_buf))))
+ return -EFAULT;
+ break;
+ }
+ case OBD_IOC_QUOTACTL: {
+ struct if_quotactl *qctl = karg;
+ struct lmv_tgt_desc *tgt = NULL;
+ struct obd_quotactl *oqctl;
+
+ if (qctl->qc_valid == QC_MDTIDX) {
+ if (qctl->qc_idx < 0 || count <= qctl->qc_idx)
+ return -EINVAL;
+
+ tgt = lmv->tgts[qctl->qc_idx];
+ if (tgt == NULL || tgt->ltd_exp == NULL)
+ return -EINVAL;
+ } else if (qctl->qc_valid == QC_UUID) {
+ for (i = 0; i < count; i++) {
+ tgt = lmv->tgts[i];
+ if (tgt == NULL)
+ continue;
+ if (!obd_uuid_equals(&tgt->ltd_uuid,
+ &qctl->obd_uuid))
+ continue;
+
+ if (tgt->ltd_exp == NULL)
+ return -EINVAL;
+
+ break;
+ }
+ } else {
+ return -EINVAL;
+ }
+
+ if (i >= count)
+ return -EAGAIN;
+
+ LASSERT(tgt && tgt->ltd_exp);
+ OBD_ALLOC_PTR(oqctl);
+ if (!oqctl)
+ return -ENOMEM;
+
+ QCTL_COPY(oqctl, qctl);
+ rc = obd_quotactl(tgt->ltd_exp, oqctl);
+ if (rc == 0) {
+ QCTL_COPY(qctl, oqctl);
+ qctl->qc_valid = QC_MDTIDX;
+ qctl->obd_uuid = tgt->ltd_uuid;
+ }
+ OBD_FREE_PTR(oqctl);
+ break;
+ }
+ case OBD_IOC_CHANGELOG_SEND:
+ case OBD_IOC_CHANGELOG_CLEAR: {
+ struct ioc_changelog *icc = karg;
+
+ if (icc->icc_mdtindex >= count)
+ return -ENODEV;
+
+ if (lmv->tgts[icc->icc_mdtindex] == NULL ||
+ lmv->tgts[icc->icc_mdtindex]->ltd_exp == NULL ||
+ lmv->tgts[icc->icc_mdtindex]->ltd_active == 0)
+ return -ENODEV;
+ rc = obd_iocontrol(cmd, lmv->tgts[icc->icc_mdtindex]->ltd_exp,
+ sizeof(*icc), icc, NULL);
+ break;
+ }
+ case LL_IOC_GET_CONNECT_FLAGS: {
+ if (lmv->tgts[0] == NULL)
+ return -ENODATA;
+ rc = obd_iocontrol(cmd, lmv->tgts[0]->ltd_exp, len, karg, uarg);
+ break;
+ }
+ case OBD_IOC_FID2PATH: {
+ rc = lmv_fid2path(exp, len, karg, uarg);
+ break;
+ }
+ case LL_IOC_HSM_STATE_GET:
+ case LL_IOC_HSM_STATE_SET:
+ case LL_IOC_HSM_ACTION: {
+ struct md_op_data *op_data = karg;
+ struct lmv_tgt_desc *tgt;
+
+ tgt = lmv_find_target(lmv, &op_data->op_fid1);
+ if (IS_ERR(tgt))
+ return PTR_ERR(tgt);
+
+ if (tgt->ltd_exp == NULL)
+ return -EINVAL;
+
+ rc = obd_iocontrol(cmd, tgt->ltd_exp, len, karg, uarg);
+ break;
+ }
+ case LL_IOC_HSM_PROGRESS: {
+ const struct hsm_progress_kernel *hpk = karg;
+ struct lmv_tgt_desc *tgt;
+
+ tgt = lmv_find_target(lmv, &hpk->hpk_fid);
+ if (IS_ERR(tgt))
+ return PTR_ERR(tgt);
+ rc = obd_iocontrol(cmd, tgt->ltd_exp, len, karg, uarg);
+ break;
+ }
+ case LL_IOC_HSM_REQUEST: {
+ struct hsm_user_request *hur = karg;
+ struct lmv_tgt_desc *tgt;
+ unsigned int reqcount = hur->hur_request.hr_itemcount;
+
+ if (reqcount == 0)
+ return 0;
+
+ /* if the request is about a single fid
+ * or if there is a single MDS, no need to split
+ * the request. */
+ if (reqcount == 1 || count == 1) {
+ tgt = lmv_find_target(lmv,
+ &hur->hur_user_item[0].hui_fid);
+ if (IS_ERR(tgt))
+ return PTR_ERR(tgt);
+ rc = obd_iocontrol(cmd, tgt->ltd_exp, len, karg, uarg);
+ } else {
+ /* split fid list to their respective MDS */
+ for (i = 0; i < count; i++) {
+ unsigned int nr, reqlen;
+ int rc1;
+ struct hsm_user_request *req;
+
+ nr = lmv_hsm_req_count(lmv, hur, lmv->tgts[i]);
+ if (nr == 0) /* nothing for this MDS */
+ continue;
+
+ /* build a request with fids for this MDS */
+ reqlen = offsetof(typeof(*hur),
+ hur_user_item[nr])
+ + hur->hur_request.hr_data_len;
+ OBD_ALLOC_LARGE(req, reqlen);
+ if (req == NULL)
+ return -ENOMEM;
+
+ lmv_hsm_req_build(lmv, hur, lmv->tgts[i], req);
+
+ rc1 = obd_iocontrol(cmd, lmv->tgts[i]->ltd_exp,
+ reqlen, req, uarg);
+ if (rc1 != 0 && rc == 0)
+ rc = rc1;
+ OBD_FREE_LARGE(req, reqlen);
+ }
+ }
+ break;
+ }
+ case LL_IOC_LOV_SWAP_LAYOUTS: {
+ struct md_op_data *op_data = karg;
+ struct lmv_tgt_desc *tgt1, *tgt2;
+
+ tgt1 = lmv_find_target(lmv, &op_data->op_fid1);
+ if (IS_ERR(tgt1))
+ return PTR_ERR(tgt1);
+
+ tgt2 = lmv_find_target(lmv, &op_data->op_fid2);
+ if (IS_ERR(tgt2))
+ return PTR_ERR(tgt2);
+
+ if ((tgt1->ltd_exp == NULL) || (tgt2->ltd_exp == NULL))
+ return -EINVAL;
+
+ /* only files on same MDT can have their layouts swapped */
+ if (tgt1->ltd_idx != tgt2->ltd_idx)
+ return -EPERM;
+
+ rc = obd_iocontrol(cmd, tgt1->ltd_exp, len, karg, uarg);
+ break;
+ }
+ case LL_IOC_HSM_CT_START: {
+ struct lustre_kernelcomm *lk = karg;
+ if (lk->lk_flags & LK_FLG_STOP)
+ rc = lmv_hsm_ct_unregister(lmv, cmd, len, lk, uarg);
+ else
+ rc = lmv_hsm_ct_register(lmv, cmd, len, lk, uarg);
+ break;
+ }
+ default:
+ for (i = 0; i < count; i++) {
+ struct obd_device *mdc_obd;
+ int err;
+
+ if (lmv->tgts[i] == NULL ||
+ lmv->tgts[i]->ltd_exp == NULL)
+ continue;
+ /* ll_umount_begin() sets force flag but for lmv, not
+ * mdc. Let's pass it through */
+ mdc_obd = class_exp2obd(lmv->tgts[i]->ltd_exp);
+ mdc_obd->obd_force = obddev->obd_force;
+ err = obd_iocontrol(cmd, lmv->tgts[i]->ltd_exp, len,
+ karg, uarg);
+ if (err == -ENODATA && cmd == OBD_IOC_POLL_QUOTACHECK) {
+ return err;
+ } else if (err) {
+ if (lmv->tgts[i]->ltd_active) {
+ CERROR("error: iocontrol MDC %s on MDTidx %d cmd %x: err = %d\n",
+ lmv->tgts[i]->ltd_uuid.uuid,
+ i, cmd, err);
+ if (!rc)
+ rc = err;
+ }
+ } else
+ set = 1;
+ }
+ if (!set && !rc)
+ rc = -EIO;
+ }
+ return rc;
+}
+
+#if 0
+static int lmv_all_chars_policy(int count, const char *name,
+ int len)
+{
+ unsigned int c = 0;
+
+ while (len > 0)
+ c += name[--len];
+ c = c % count;
+ return c;
+}
+
+static int lmv_nid_policy(struct lmv_obd *lmv)
+{
+ struct obd_import *imp;
+ __u32 id;
+
+ /*
+ * XXX: To get nid we assume that underlying obd device is mdc.
+ */
+ imp = class_exp2cliimp(lmv->tgts[0].ltd_exp);
+ id = imp->imp_connection->c_self ^ (imp->imp_connection->c_self >> 32);
+ return id % lmv->desc.ld_tgt_count;
+}
+
+static int lmv_choose_mds(struct lmv_obd *lmv, struct md_op_data *op_data,
+ enum placement_policy placement)
+{
+ switch (placement) {
+ case PLACEMENT_CHAR_POLICY:
+ return lmv_all_chars_policy(lmv->desc.ld_tgt_count,
+ op_data->op_name,
+ op_data->op_namelen);
+ case PLACEMENT_NID_POLICY:
+ return lmv_nid_policy(lmv);
+
+ default:
+ break;
+ }
+
+ CERROR("Unsupported placement policy %x\n", placement);
+ return -EINVAL;
+}
+#endif
+
+/**
+ * This is _inode_ placement policy function (not name).
+ */
+static int lmv_placement_policy(struct obd_device *obd,
+ struct md_op_data *op_data, u32 *mds)
+{
+ struct lmv_obd *lmv = &obd->u.lmv;
+
+ LASSERT(mds != NULL);
+
+ if (lmv->desc.ld_tgt_count == 1) {
+ *mds = 0;
+ return 0;
+ }
+
+ /**
+ * If stripe_offset is provided during setdirstripe
+ * (setdirstripe -i xx), xx MDS will be chosen.
+ */
+ if (op_data->op_cli_flags & CLI_SET_MEA) {
+ struct lmv_user_md *lum;
+
+ lum = (struct lmv_user_md *)op_data->op_data;
+ if (lum->lum_type == LMV_STRIPE_TYPE &&
+ lum->lum_stripe_offset != -1) {
+ if (lum->lum_stripe_offset >= lmv->desc.ld_tgt_count) {
+ CERROR("%s: Stripe_offset %d > MDT count %d: rc = %d\n",
+ obd->obd_name,
+ lum->lum_stripe_offset,
+ lmv->desc.ld_tgt_count, -ERANGE);
+ return -ERANGE;
+ }
+ *mds = lum->lum_stripe_offset;
+ return 0;
+ }
+ }
+
+ /* Allocate new fid on target according to operation type and parent
+ * home mds. */
+ *mds = op_data->op_mds;
+ return 0;
+}
+
+int __lmv_fid_alloc(struct lmv_obd *lmv, struct lu_fid *fid, u32 mds)
+{
+ struct lmv_tgt_desc *tgt;
+ int rc;
+
+ tgt = lmv_get_target(lmv, mds);
+ if (IS_ERR(tgt))
+ return PTR_ERR(tgt);
+
+ /*
+ * New seq alloc and FLD setup should be atomic. Otherwise we may find
+ * on server that seq in new allocated fid is not yet known.
+ */
+ mutex_lock(&tgt->ltd_fid_mutex);
+
+ if (tgt->ltd_active == 0 || tgt->ltd_exp == NULL) {
+ rc = -ENODEV;
+ goto out;
+ }
+
+ /*
+ * Asking underlaying tgt layer to allocate new fid.
+ */
+ rc = obd_fid_alloc(tgt->ltd_exp, fid, NULL);
+ if (rc > 0) {
+ LASSERT(fid_is_sane(fid));
+ rc = 0;
+ }
+
+out:
+ mutex_unlock(&tgt->ltd_fid_mutex);
+ return rc;
+}
+
+int lmv_fid_alloc(struct obd_export *exp, struct lu_fid *fid,
+ struct md_op_data *op_data)
+{
+ struct obd_device *obd = class_exp2obd(exp);
+ struct lmv_obd *lmv = &obd->u.lmv;
+ u32 mds = 0;
+ int rc;
+
+ LASSERT(op_data != NULL);
+ LASSERT(fid != NULL);
+
+ rc = lmv_placement_policy(obd, op_data, &mds);
+ if (rc) {
+ CERROR("Can't get target for allocating fid, rc %d\n",
+ rc);
+ return rc;
+ }
+
+ rc = __lmv_fid_alloc(lmv, fid, mds);
+ if (rc) {
+ CERROR("Can't alloc new fid, rc %d\n", rc);
+ return rc;
+ }
+
+ return rc;
+}
+
+static int lmv_setup(struct obd_device *obd, struct lustre_cfg *lcfg)
+{
+ struct lmv_obd *lmv = &obd->u.lmv;
+ struct lprocfs_static_vars lvars;
+ struct lmv_desc *desc;
+ int rc;
+
+ if (LUSTRE_CFG_BUFLEN(lcfg, 1) < 1) {
+ CERROR("LMV setup requires a descriptor\n");
+ return -EINVAL;
+ }
+
+ desc = (struct lmv_desc *)lustre_cfg_buf(lcfg, 1);
+ if (sizeof(*desc) > LUSTRE_CFG_BUFLEN(lcfg, 1)) {
+ CERROR("Lmv descriptor size wrong: %d > %d\n",
+ (int)sizeof(*desc), LUSTRE_CFG_BUFLEN(lcfg, 1));
+ return -EINVAL;
+ }
+
+ OBD_ALLOC(lmv->tgts, sizeof(*lmv->tgts) * 32);
+ if (lmv->tgts == NULL)
+ return -ENOMEM;
+ lmv->tgts_size = 32;
+
+ obd_str2uuid(&lmv->desc.ld_uuid, desc->ld_uuid.uuid);
+ lmv->desc.ld_tgt_count = 0;
+ lmv->desc.ld_active_tgt_count = 0;
+ lmv->max_cookiesize = 0;
+ lmv->max_def_easize = 0;
+ lmv->max_easize = 0;
+ lmv->lmv_placement = PLACEMENT_CHAR_POLICY;
+
+ spin_lock_init(&lmv->lmv_lock);
+ mutex_init(&lmv->init_mutex);
+
+ lprocfs_lmv_init_vars(&lvars);
+
+ lprocfs_obd_setup(obd, lvars.obd_vars);
+#if defined (CONFIG_PROC_FS)
+ {
+ rc = lprocfs_seq_create(obd->obd_proc_entry, "target_obd",
+ 0444, &lmv_proc_target_fops, obd);
+ if (rc)
+ CWARN("%s: error adding LMV target_obd file: rc = %d\n",
+ obd->obd_name, rc);
+ }
+#endif
+ rc = fld_client_init(&lmv->lmv_fld, obd->obd_name,
+ LUSTRE_CLI_FLD_HASH_DHT);
+ if (rc) {
+ CERROR("Can't init FLD, err %d\n", rc);
+ goto out;
+ }
+
+ return 0;
+
+out:
+ return rc;
+}
+
+static int lmv_cleanup(struct obd_device *obd)
+{
+ struct lmv_obd *lmv = &obd->u.lmv;
+
+ fld_client_fini(&lmv->lmv_fld);
+ if (lmv->tgts != NULL) {
+ int i;
+ for (i = 0; i < lmv->desc.ld_tgt_count; i++) {
+ if (lmv->tgts[i] == NULL)
+ continue;
+ lmv_del_target(lmv, i);
+ }
+ OBD_FREE(lmv->tgts, sizeof(*lmv->tgts) * lmv->tgts_size);
+ lmv->tgts_size = 0;
+ }
+ return 0;
+}
+
+static int lmv_process_config(struct obd_device *obd, u32 len, void *buf)
+{
+ struct lustre_cfg *lcfg = buf;
+ struct obd_uuid obd_uuid;
+ int gen;
+ __u32 index;
+ int rc;
+
+ switch (lcfg->lcfg_command) {
+ case LCFG_ADD_MDC:
+ /* modify_mdc_tgts add 0:lustre-clilmv 1:lustre-MDT0000_UUID
+ * 2:0 3:1 4:lustre-MDT0000-mdc_UUID */
+ if (LUSTRE_CFG_BUFLEN(lcfg, 1) > sizeof(obd_uuid.uuid)) {
+ rc = -EINVAL;
+ goto out;
+ }
+
+ obd_str2uuid(&obd_uuid, lustre_cfg_buf(lcfg, 1));
+
+ if (sscanf(lustre_cfg_buf(lcfg, 2), "%d", &index) != 1) {
+ rc = -EINVAL;
+ goto out;
+ }
+ if (sscanf(lustre_cfg_buf(lcfg, 3), "%d", &gen) != 1) {
+ rc = -EINVAL;
+ goto out;
+ }
+ rc = lmv_add_target(obd, &obd_uuid, index, gen);
+ goto out;
+ default:
+ CERROR("Unknown command: %d\n", lcfg->lcfg_command);
+ rc = -EINVAL;
+ goto out;
+ }
+out:
+ return rc;
+}
+
+static int lmv_statfs(const struct lu_env *env, struct obd_export *exp,
+ struct obd_statfs *osfs, __u64 max_age, __u32 flags)
+{
+ struct obd_device *obd = class_exp2obd(exp);
+ struct lmv_obd *lmv = &obd->u.lmv;
+ struct obd_statfs *temp;
+ int rc = 0;
+ int i;
+
+ rc = lmv_check_connect(obd);
+ if (rc)
+ return rc;
+
+ OBD_ALLOC(temp, sizeof(*temp));
+ if (temp == NULL)
+ return -ENOMEM;
+
+ for (i = 0; i < lmv->desc.ld_tgt_count; i++) {
+ if (lmv->tgts[i] == NULL || lmv->tgts[i]->ltd_exp == NULL)
+ continue;
+
+ rc = obd_statfs(env, lmv->tgts[i]->ltd_exp, temp,
+ max_age, flags);
+ if (rc) {
+ CERROR("can't stat MDS #%d (%s), error %d\n", i,
+ lmv->tgts[i]->ltd_exp->exp_obd->obd_name,
+ rc);
+ goto out_free_temp;
+ }
+
+ if (i == 0) {
+ *osfs = *temp;
+ /* If the statfs is from mount, it will needs
+ * retrieve necessary information from MDT0.
+ * i.e. mount does not need the merged osfs
+ * from all of MDT.
+ * And also clients can be mounted as long as
+ * MDT0 is in service*/
+ if (flags & OBD_STATFS_FOR_MDT0)
+ goto out_free_temp;
+ } else {
+ osfs->os_bavail += temp->os_bavail;
+ osfs->os_blocks += temp->os_blocks;
+ osfs->os_ffree += temp->os_ffree;
+ osfs->os_files += temp->os_files;
+ }
+ }
+
+out_free_temp:
+ OBD_FREE(temp, sizeof(*temp));
+ return rc;
+}
+
+static int lmv_getstatus(struct obd_export *exp,
+ struct lu_fid *fid,
+ struct obd_capa **pc)
+{
+ struct obd_device *obd = exp->exp_obd;
+ struct lmv_obd *lmv = &obd->u.lmv;
+ int rc;
+
+ rc = lmv_check_connect(obd);
+ if (rc)
+ return rc;
+
+ rc = md_getstatus(lmv->tgts[0]->ltd_exp, fid, pc);
+ return rc;
+}
+
+static int lmv_getxattr(struct obd_export *exp, const struct lu_fid *fid,
+ struct obd_capa *oc, u64 valid, const char *name,
+ const char *input, int input_size, int output_size,
+ int flags, struct ptlrpc_request **request)
+{
+ struct obd_device *obd = exp->exp_obd;
+ struct lmv_obd *lmv = &obd->u.lmv;
+ struct lmv_tgt_desc *tgt;
+ int rc;
+
+ rc = lmv_check_connect(obd);
+ if (rc)
+ return rc;
+
+ tgt = lmv_find_target(lmv, fid);
+ if (IS_ERR(tgt))
+ return PTR_ERR(tgt);
+
+ rc = md_getxattr(tgt->ltd_exp, fid, oc, valid, name, input,
+ input_size, output_size, flags, request);
+
+ return rc;
+}
+
+static int lmv_setxattr(struct obd_export *exp, const struct lu_fid *fid,
+ struct obd_capa *oc, u64 valid, const char *name,
+ const char *input, int input_size, int output_size,
+ int flags, __u32 suppgid,
+ struct ptlrpc_request **request)
+{
+ struct obd_device *obd = exp->exp_obd;
+ struct lmv_obd *lmv = &obd->u.lmv;
+ struct lmv_tgt_desc *tgt;
+ int rc;
+
+ rc = lmv_check_connect(obd);
+ if (rc)
+ return rc;
+
+ tgt = lmv_find_target(lmv, fid);
+ if (IS_ERR(tgt))
+ return PTR_ERR(tgt);
+
+ rc = md_setxattr(tgt->ltd_exp, fid, oc, valid, name, input,
+ input_size, output_size, flags, suppgid,
+ request);
+
+ return rc;
+}
+
+static int lmv_getattr(struct obd_export *exp, struct md_op_data *op_data,
+ struct ptlrpc_request **request)
+{
+ struct obd_device *obd = exp->exp_obd;
+ struct lmv_obd *lmv = &obd->u.lmv;
+ struct lmv_tgt_desc *tgt;
+ int rc;
+
+ rc = lmv_check_connect(obd);
+ if (rc)
+ return rc;
+
+ tgt = lmv_find_target(lmv, &op_data->op_fid1);
+ if (IS_ERR(tgt))
+ return PTR_ERR(tgt);
+
+ if (op_data->op_flags & MF_GET_MDT_IDX) {
+ op_data->op_mds = tgt->ltd_idx;
+ return 0;
+ }
+
+ rc = md_getattr(tgt->ltd_exp, op_data, request);
+
+ return rc;
+}
+
+static int lmv_null_inode(struct obd_export *exp, const struct lu_fid *fid)
+{
+ struct obd_device *obd = exp->exp_obd;
+ struct lmv_obd *lmv = &obd->u.lmv;
+ int i;
+ int rc;
+
+ rc = lmv_check_connect(obd);
+ if (rc)
+ return rc;
+
+ CDEBUG(D_INODE, "CBDATA for "DFID"\n", PFID(fid));
+
+ /*
+ * With DNE every object can have two locks in different namespaces:
+ * lookup lock in space of MDT storing direntry and update/open lock in
+ * space of MDT storing inode.
+ */
+ for (i = 0; i < lmv->desc.ld_tgt_count; i++) {
+ if (lmv->tgts[i] == NULL || lmv->tgts[i]->ltd_exp == NULL)
+ continue;
+ md_null_inode(lmv->tgts[i]->ltd_exp, fid);
+ }
+
+ return 0;
+}
+
+static int lmv_find_cbdata(struct obd_export *exp, const struct lu_fid *fid,
+ ldlm_iterator_t it, void *data)
+{
+ struct obd_device *obd = exp->exp_obd;
+ struct lmv_obd *lmv = &obd->u.lmv;
+ int i;
+ int rc;
+
+ rc = lmv_check_connect(obd);
+ if (rc)
+ return rc;
+
+ CDEBUG(D_INODE, "CBDATA for "DFID"\n", PFID(fid));
+
+ /*
+ * With DNE every object can have two locks in different namespaces:
+ * lookup lock in space of MDT storing direntry and update/open lock in
+ * space of MDT storing inode.
+ */
+ for (i = 0; i < lmv->desc.ld_tgt_count; i++) {
+ if (lmv->tgts[i] == NULL || lmv->tgts[i]->ltd_exp == NULL)
+ continue;
+ rc = md_find_cbdata(lmv->tgts[i]->ltd_exp, fid, it, data);
+ if (rc)
+ return rc;
+ }
+
+ return rc;
+}
+
+
+static int lmv_close(struct obd_export *exp, struct md_op_data *op_data,
+ struct md_open_data *mod, struct ptlrpc_request **request)
+{
+ struct obd_device *obd = exp->exp_obd;
+ struct lmv_obd *lmv = &obd->u.lmv;
+ struct lmv_tgt_desc *tgt;
+ int rc;
+
+ rc = lmv_check_connect(obd);
+ if (rc)
+ return rc;
+
+ tgt = lmv_find_target(lmv, &op_data->op_fid1);
+ if (IS_ERR(tgt))
+ return PTR_ERR(tgt);
+
+ CDEBUG(D_INODE, "CLOSE "DFID"\n", PFID(&op_data->op_fid1));
+ rc = md_close(tgt->ltd_exp, op_data, mod, request);
+ return rc;
+}
+
+struct lmv_tgt_desc
+*lmv_locate_mds(struct lmv_obd *lmv, struct md_op_data *op_data,
+ struct lu_fid *fid)
+{
+ struct lmv_tgt_desc *tgt;
+
+ tgt = lmv_find_target(lmv, fid);
+ if (IS_ERR(tgt))
+ return tgt;
+
+ op_data->op_mds = tgt->ltd_idx;
+
+ return tgt;
+}
+
+static int lmv_create(struct obd_export *exp, struct md_op_data *op_data,
+ const void *data, int datalen, int mode, __u32 uid,
+ __u32 gid, cfs_cap_t cap_effective, __u64 rdev,
+ struct ptlrpc_request **request)
+{
+ struct obd_device *obd = exp->exp_obd;
+ struct lmv_obd *lmv = &obd->u.lmv;
+ struct lmv_tgt_desc *tgt;
+ int rc;
+
+ rc = lmv_check_connect(obd);
+ if (rc)
+ return rc;
+
+ if (!lmv->desc.ld_active_tgt_count)
+ return -EIO;
+
+ tgt = lmv_locate_mds(lmv, op_data, &op_data->op_fid1);
+ if (IS_ERR(tgt))
+ return PTR_ERR(tgt);
+
+ rc = lmv_fid_alloc(exp, &op_data->op_fid2, op_data);
+ if (rc)
+ return rc;
+
+ CDEBUG(D_INODE, "CREATE '%*s' on "DFID" -> mds #%x\n",
+ op_data->op_namelen, op_data->op_name, PFID(&op_data->op_fid1),
+ op_data->op_mds);
+
+ op_data->op_flags |= MF_MDC_CANCEL_FID1;
+ rc = md_create(tgt->ltd_exp, op_data, data, datalen, mode, uid, gid,
+ cap_effective, rdev, request);
+
+ if (rc == 0) {
+ if (*request == NULL)
+ return rc;
+ CDEBUG(D_INODE, "Created - "DFID"\n", PFID(&op_data->op_fid2));
+ }
+ return rc;
+}
+
+static int lmv_done_writing(struct obd_export *exp,
+ struct md_op_data *op_data,
+ struct md_open_data *mod)
+{
+ struct obd_device *obd = exp->exp_obd;
+ struct lmv_obd *lmv = &obd->u.lmv;
+ struct lmv_tgt_desc *tgt;
+ int rc;
+
+ rc = lmv_check_connect(obd);
+ if (rc)
+ return rc;
+
+ tgt = lmv_find_target(lmv, &op_data->op_fid1);
+ if (IS_ERR(tgt))
+ return PTR_ERR(tgt);
+
+ rc = md_done_writing(tgt->ltd_exp, op_data, mod);
+ return rc;
+}
+
+static int
+lmv_enqueue_remote(struct obd_export *exp, struct ldlm_enqueue_info *einfo,
+ struct lookup_intent *it, struct md_op_data *op_data,
+ struct lustre_handle *lockh, void *lmm, int lmmsize,
+ __u64 extra_lock_flags)
+{
+ struct ptlrpc_request *req = it->d.lustre.it_data;
+ struct obd_device *obd = exp->exp_obd;
+ struct lmv_obd *lmv = &obd->u.lmv;
+ struct lustre_handle plock;
+ struct lmv_tgt_desc *tgt;
+ struct md_op_data *rdata;
+ struct lu_fid fid1;
+ struct mdt_body *body;
+ int rc = 0;
+ int pmode;
+
+ body = req_capsule_server_get(&req->rq_pill, &RMF_MDT_BODY);
+ LASSERT(body != NULL);
+
+ if (!(body->valid & OBD_MD_MDS))
+ return 0;
+
+ CDEBUG(D_INODE, "REMOTE_ENQUEUE '%s' on "DFID" -> "DFID"\n",
+ LL_IT2STR(it), PFID(&op_data->op_fid1), PFID(&body->fid1));
+
+ /*
+ * We got LOOKUP lock, but we really need attrs.
+ */
+ pmode = it->d.lustre.it_lock_mode;
+ LASSERT(pmode != 0);
+ memcpy(&plock, lockh, sizeof(plock));
+ it->d.lustre.it_lock_mode = 0;
+ it->d.lustre.it_data = NULL;
+ fid1 = body->fid1;
+
+ ptlrpc_req_finished(req);
+
+ tgt = lmv_find_target(lmv, &fid1);
+ if (IS_ERR(tgt)) {
+ rc = PTR_ERR(tgt);
+ goto out;
+ }
+
+ OBD_ALLOC_PTR(rdata);
+ if (rdata == NULL) {
+ rc = -ENOMEM;
+ goto out;
+ }
+
+ rdata->op_fid1 = fid1;
+ rdata->op_bias = MDS_CROSS_REF;
+
+ rc = md_enqueue(tgt->ltd_exp, einfo, it, rdata, lockh,
+ lmm, lmmsize, NULL, extra_lock_flags);
+ OBD_FREE_PTR(rdata);
+out:
+ ldlm_lock_decref(&plock, pmode);
+ return rc;
+}
+
+static int
+lmv_enqueue(struct obd_export *exp, struct ldlm_enqueue_info *einfo,
+ struct lookup_intent *it, struct md_op_data *op_data,
+ struct lustre_handle *lockh, void *lmm, int lmmsize,
+ struct ptlrpc_request **req, __u64 extra_lock_flags)
+{
+ struct obd_device *obd = exp->exp_obd;
+ struct lmv_obd *lmv = &obd->u.lmv;
+ struct lmv_tgt_desc *tgt;
+ int rc;
+
+ rc = lmv_check_connect(obd);
+ if (rc)
+ return rc;
+
+ CDEBUG(D_INODE, "ENQUEUE '%s' on "DFID"\n",
+ LL_IT2STR(it), PFID(&op_data->op_fid1));
+
+ tgt = lmv_locate_mds(lmv, op_data, &op_data->op_fid1);
+ if (IS_ERR(tgt))
+ return PTR_ERR(tgt);
+
+ CDEBUG(D_INODE, "ENQUEUE '%s' on "DFID" -> mds #%d\n",
+ LL_IT2STR(it), PFID(&op_data->op_fid1), tgt->ltd_idx);
+
+ rc = md_enqueue(tgt->ltd_exp, einfo, it, op_data, lockh,
+ lmm, lmmsize, req, extra_lock_flags);
+
+ if (rc == 0 && it && it->it_op == IT_OPEN) {
+ rc = lmv_enqueue_remote(exp, einfo, it, op_data, lockh,
+ lmm, lmmsize, extra_lock_flags);
+ }
+ return rc;
+}
+
+static int
+lmv_getattr_name(struct obd_export *exp, struct md_op_data *op_data,
+ struct ptlrpc_request **request)
+{
+ struct ptlrpc_request *req = NULL;
+ struct obd_device *obd = exp->exp_obd;
+ struct lmv_obd *lmv = &obd->u.lmv;
+ struct lmv_tgt_desc *tgt;
+ struct mdt_body *body;
+ int rc;
+
+ rc = lmv_check_connect(obd);
+ if (rc)
+ return rc;
+
+ tgt = lmv_locate_mds(lmv, op_data, &op_data->op_fid1);
+ if (IS_ERR(tgt))
+ return PTR_ERR(tgt);
+
+ CDEBUG(D_INODE, "GETATTR_NAME for %*s on "DFID" -> mds #%d\n",
+ op_data->op_namelen, op_data->op_name, PFID(&op_data->op_fid1),
+ tgt->ltd_idx);
+
+ rc = md_getattr_name(tgt->ltd_exp, op_data, request);
+ if (rc != 0)
+ return rc;
+
+ body = req_capsule_server_get(&(*request)->rq_pill,
+ &RMF_MDT_BODY);
+ LASSERT(body != NULL);
+
+ if (body->valid & OBD_MD_MDS) {
+ struct lu_fid rid = body->fid1;
+ CDEBUG(D_INODE, "Request attrs for "DFID"\n",
+ PFID(&rid));
+
+ tgt = lmv_find_target(lmv, &rid);
+ if (IS_ERR(tgt)) {
+ ptlrpc_req_finished(*request);
+ return PTR_ERR(tgt);
+ }
+
+ op_data->op_fid1 = rid;
+ op_data->op_valid |= OBD_MD_FLCROSSREF;
+ op_data->op_namelen = 0;
+ op_data->op_name = NULL;
+ rc = md_getattr_name(tgt->ltd_exp, op_data, &req);
+ ptlrpc_req_finished(*request);
+ *request = req;
+ }
+
+ return rc;
+}
+
+#define md_op_data_fid(op_data, fl) \
+ (fl == MF_MDC_CANCEL_FID1 ? &op_data->op_fid1 : \
+ fl == MF_MDC_CANCEL_FID2 ? &op_data->op_fid2 : \
+ fl == MF_MDC_CANCEL_FID3 ? &op_data->op_fid3 : \
+ fl == MF_MDC_CANCEL_FID4 ? &op_data->op_fid4 : \
+ NULL)
+
+static int lmv_early_cancel(struct obd_export *exp, struct md_op_data *op_data,
+ int op_tgt, ldlm_mode_t mode, int bits, int flag)
+{
+ struct lu_fid *fid = md_op_data_fid(op_data, flag);
+ struct obd_device *obd = exp->exp_obd;
+ struct lmv_obd *lmv = &obd->u.lmv;
+ struct lmv_tgt_desc *tgt;
+ ldlm_policy_data_t policy = {{0}};
+ int rc = 0;
+
+ if (!fid_is_sane(fid))
+ return 0;
+
+ tgt = lmv_find_target(lmv, fid);
+ if (IS_ERR(tgt))
+ return PTR_ERR(tgt);
+
+ if (tgt->ltd_idx != op_tgt) {
+ CDEBUG(D_INODE, "EARLY_CANCEL on "DFID"\n", PFID(fid));
+ policy.l_inodebits.bits = bits;
+ rc = md_cancel_unused(tgt->ltd_exp, fid, &policy,
+ mode, LCF_ASYNC, NULL);
+ } else {
+ CDEBUG(D_INODE,
+ "EARLY_CANCEL skip operation target %d on "DFID"\n",
+ op_tgt, PFID(fid));
+ op_data->op_flags |= flag;
+ rc = 0;
+ }
+
+ return rc;
+}
+
+/*
+ * llite passes fid of an target inode in op_data->op_fid1 and id of directory in
+ * op_data->op_fid2
+ */
+static int lmv_link(struct obd_export *exp, struct md_op_data *op_data,
+ struct ptlrpc_request **request)
+{
+ struct obd_device *obd = exp->exp_obd;
+ struct lmv_obd *lmv = &obd->u.lmv;
+ struct lmv_tgt_desc *tgt;
+ int rc;
+
+ rc = lmv_check_connect(obd);
+ if (rc)
+ return rc;
+
+ LASSERT(op_data->op_namelen != 0);
+
+ CDEBUG(D_INODE, "LINK "DFID":%*s to "DFID"\n",
+ PFID(&op_data->op_fid2), op_data->op_namelen,
+ op_data->op_name, PFID(&op_data->op_fid1));
+
+ op_data->op_fsuid = from_kuid(&init_user_ns, current_fsuid());
+ op_data->op_fsgid = from_kgid(&init_user_ns, current_fsgid());
+ op_data->op_cap = cfs_curproc_cap_pack();
+ tgt = lmv_locate_mds(lmv, op_data, &op_data->op_fid2);
+ if (IS_ERR(tgt))
+ return PTR_ERR(tgt);
+
+ /*
+ * Cancel UPDATE lock on child (fid1).
+ */
+ op_data->op_flags |= MF_MDC_CANCEL_FID2;
+ rc = lmv_early_cancel(exp, op_data, tgt->ltd_idx, LCK_EX,
+ MDS_INODELOCK_UPDATE, MF_MDC_CANCEL_FID1);
+ if (rc != 0)
+ return rc;
+
+ rc = md_link(tgt->ltd_exp, op_data, request);
+
+ return rc;
+}
+
+static int lmv_rename(struct obd_export *exp, struct md_op_data *op_data,
+ const char *old, int oldlen, const char *new, int newlen,
+ struct ptlrpc_request **request)
+{
+ struct obd_device *obd = exp->exp_obd;
+ struct lmv_obd *lmv = &obd->u.lmv;
+ struct lmv_tgt_desc *src_tgt;
+ struct lmv_tgt_desc *tgt_tgt;
+ int rc;
+
+ LASSERT(oldlen != 0);
+
+ CDEBUG(D_INODE, "RENAME %*s in "DFID" to %*s in "DFID"\n",
+ oldlen, old, PFID(&op_data->op_fid1),
+ newlen, new, PFID(&op_data->op_fid2));
+
+ rc = lmv_check_connect(obd);
+ if (rc)
+ return rc;
+
+ op_data->op_fsuid = from_kuid(&init_user_ns, current_fsuid());
+ op_data->op_fsgid = from_kgid(&init_user_ns, current_fsgid());
+ op_data->op_cap = cfs_curproc_cap_pack();
+ src_tgt = lmv_locate_mds(lmv, op_data, &op_data->op_fid1);
+ if (IS_ERR(src_tgt))
+ return PTR_ERR(src_tgt);
+
+ tgt_tgt = lmv_locate_mds(lmv, op_data, &op_data->op_fid2);
+ if (IS_ERR(tgt_tgt))
+ return PTR_ERR(tgt_tgt);
+ /*
+ * LOOKUP lock on src child (fid3) should also be cancelled for
+ * src_tgt in mdc_rename.
+ */
+ op_data->op_flags |= MF_MDC_CANCEL_FID1 | MF_MDC_CANCEL_FID3;
+
+ /*
+ * Cancel UPDATE locks on tgt parent (fid2), tgt_tgt is its
+ * own target.
+ */
+ rc = lmv_early_cancel(exp, op_data, src_tgt->ltd_idx,
+ LCK_EX, MDS_INODELOCK_UPDATE,
+ MF_MDC_CANCEL_FID2);
+
+ /*
+ * Cancel LOOKUP locks on tgt child (fid4) for parent tgt_tgt.
+ */
+ if (rc == 0) {
+ rc = lmv_early_cancel(exp, op_data, src_tgt->ltd_idx,
+ LCK_EX, MDS_INODELOCK_LOOKUP,
+ MF_MDC_CANCEL_FID4);
+ }
+
+ /*
+ * Cancel all the locks on tgt child (fid4).
+ */
+ if (rc == 0)
+ rc = lmv_early_cancel(exp, op_data, src_tgt->ltd_idx,
+ LCK_EX, MDS_INODELOCK_FULL,
+ MF_MDC_CANCEL_FID4);
+
+ if (rc == 0)
+ rc = md_rename(src_tgt->ltd_exp, op_data, old, oldlen,
+ new, newlen, request);
+ return rc;
+}
+
+static int lmv_setattr(struct obd_export *exp, struct md_op_data *op_data,
+ void *ea, int ealen, void *ea2, int ea2len,
+ struct ptlrpc_request **request,
+ struct md_open_data **mod)
+{
+ struct obd_device *obd = exp->exp_obd;
+ struct lmv_obd *lmv = &obd->u.lmv;
+ struct lmv_tgt_desc *tgt;
+ int rc = 0;
+
+ rc = lmv_check_connect(obd);
+ if (rc)
+ return rc;
+
+ CDEBUG(D_INODE, "SETATTR for "DFID", valid 0x%x\n",
+ PFID(&op_data->op_fid1), op_data->op_attr.ia_valid);
+
+ op_data->op_flags |= MF_MDC_CANCEL_FID1;
+ tgt = lmv_find_target(lmv, &op_data->op_fid1);
+ if (IS_ERR(tgt))
+ return PTR_ERR(tgt);
+
+ rc = md_setattr(tgt->ltd_exp, op_data, ea, ealen, ea2,
+ ea2len, request, mod);
+
+ return rc;
+}
+
+static int lmv_sync(struct obd_export *exp, const struct lu_fid *fid,
+ struct obd_capa *oc, struct ptlrpc_request **request)
+{
+ struct obd_device *obd = exp->exp_obd;
+ struct lmv_obd *lmv = &obd->u.lmv;
+ struct lmv_tgt_desc *tgt;
+ int rc;
+
+ rc = lmv_check_connect(obd);
+ if (rc)
+ return rc;
+
+ tgt = lmv_find_target(lmv, fid);
+ if (IS_ERR(tgt))
+ return PTR_ERR(tgt);
+
+ rc = md_sync(tgt->ltd_exp, fid, oc, request);
+ return rc;
+}
+
+/*
+ * Adjust a set of pages, each page containing an array of lu_dirpages,
+ * so that each page can be used as a single logical lu_dirpage.
+ *
+ * A lu_dirpage is laid out as follows, where s = ldp_hash_start,
+ * e = ldp_hash_end, f = ldp_flags, p = padding, and each "ent" is a
+ * struct lu_dirent. It has size up to LU_PAGE_SIZE. The ldp_hash_end
+ * value is used as a cookie to request the next lu_dirpage in a
+ * directory listing that spans multiple pages (two in this example):
+ * ________
+ * | |
+ * .|--------v------- -----.
+ * |s|e|f|p|ent|ent| ... |ent|
+ * '--|-------------- -----' Each CFS_PAGE contains a single
+ * '------. lu_dirpage.
+ * .---------v------- -----.
+ * |s|e|f|p|ent| 0 | ... | 0 |
+ * '----------------- -----'
+ *
+ * However, on hosts where the native VM page size (PAGE_CACHE_SIZE) is
+ * larger than LU_PAGE_SIZE, a single host page may contain multiple
+ * lu_dirpages. After reading the lu_dirpages from the MDS, the
+ * ldp_hash_end of the first lu_dirpage refers to the one immediately
+ * after it in the same CFS_PAGE (arrows simplified for brevity, but
+ * in general e0==s1, e1==s2, etc.):
+ *
+ * .-------------------- -----.
+ * |s0|e0|f0|p|ent|ent| ... |ent|
+ * |---v---------------- -----|
+ * |s1|e1|f1|p|ent|ent| ... |ent|
+ * |---v---------------- -----| Here, each CFS_PAGE contains
+ * ... multiple lu_dirpages.
+ * |---v---------------- -----|
+ * |s'|e'|f'|p|ent|ent| ... |ent|
+ * '---|---------------- -----'
+ * v
+ * .----------------------------.
+ * | next CFS_PAGE |
+ *
+ * This structure is transformed into a single logical lu_dirpage as follows:
+ *
+ * - Replace e0 with e' so the request for the next lu_dirpage gets the page
+ * labeled 'next CFS_PAGE'.
+ *
+ * - Copy the LDF_COLLIDE flag from f' to f0 to correctly reflect whether
+ * a hash collision with the next page exists.
+ *
+ * - Adjust the lde_reclen of the ending entry of each lu_dirpage to span
+ * to the first entry of the next lu_dirpage.
+ */
+#if PAGE_CACHE_SIZE > LU_PAGE_SIZE
+static void lmv_adjust_dirpages(struct page **pages, int ncfspgs, int nlupgs)
+{
+ int i;
+
+ for (i = 0; i < ncfspgs; i++) {
+ struct lu_dirpage *dp = kmap(pages[i]);
+ struct lu_dirpage *first = dp;
+ struct lu_dirent *end_dirent = NULL;
+ struct lu_dirent *ent;
+ __u64 hash_end = dp->ldp_hash_end;
+ __u32 flags = dp->ldp_flags;
+
+ while (--nlupgs > 0) {
+ ent = lu_dirent_start(dp);
+ for (end_dirent = ent; ent != NULL;
+ end_dirent = ent, ent = lu_dirent_next(ent));
+
+ /* Advance dp to next lu_dirpage. */
+ dp = (struct lu_dirpage *)((char *)dp + LU_PAGE_SIZE);
+
+ /* Check if we've reached the end of the CFS_PAGE. */
+ if (!((unsigned long)dp & ~CFS_PAGE_MASK))
+ break;
+
+ /* Save the hash and flags of this lu_dirpage. */
+ hash_end = dp->ldp_hash_end;
+ flags = dp->ldp_flags;
+
+ /* Check if lu_dirpage contains no entries. */
+ if (!end_dirent)
+ break;
+
+ /* Enlarge the end entry lde_reclen from 0 to
+ * first entry of next lu_dirpage. */
+ LASSERT(le16_to_cpu(end_dirent->lde_reclen) == 0);
+ end_dirent->lde_reclen =
+ cpu_to_le16((char *)(dp->ldp_entries) -
+ (char *)end_dirent);
+ }
+
+ first->ldp_hash_end = hash_end;
+ first->ldp_flags &= ~cpu_to_le32(LDF_COLLIDE);
+ first->ldp_flags |= flags & cpu_to_le32(LDF_COLLIDE);
+
+ kunmap(pages[i]);
+ }
+ LASSERTF(nlupgs == 0, "left = %d", nlupgs);
+}
+#else
+#define lmv_adjust_dirpages(pages, ncfspgs, nlupgs) do {} while (0)
+#endif /* PAGE_CACHE_SIZE > LU_PAGE_SIZE */
+
+static int lmv_readpage(struct obd_export *exp, struct md_op_data *op_data,
+ struct page **pages, struct ptlrpc_request **request)
+{
+ struct obd_device *obd = exp->exp_obd;
+ struct lmv_obd *lmv = &obd->u.lmv;
+ __u64 offset = op_data->op_offset;
+ int rc;
+ int ncfspgs; /* pages read in PAGE_CACHE_SIZE */
+ int nlupgs; /* pages read in LU_PAGE_SIZE */
+ struct lmv_tgt_desc *tgt;
+
+ rc = lmv_check_connect(obd);
+ if (rc)
+ return rc;
+
+ CDEBUG(D_INODE, "READPAGE at %#llx from "DFID"\n",
+ offset, PFID(&op_data->op_fid1));
+
+ tgt = lmv_find_target(lmv, &op_data->op_fid1);
+ if (IS_ERR(tgt))
+ return PTR_ERR(tgt);
+
+ rc = md_readpage(tgt->ltd_exp, op_data, pages, request);
+ if (rc != 0)
+ return rc;
+
+ ncfspgs = ((*request)->rq_bulk->bd_nob_transferred + PAGE_CACHE_SIZE - 1)
+ >> PAGE_CACHE_SHIFT;
+ nlupgs = (*request)->rq_bulk->bd_nob_transferred >> LU_PAGE_SHIFT;
+ LASSERT(!((*request)->rq_bulk->bd_nob_transferred & ~LU_PAGE_MASK));
+ LASSERT(ncfspgs > 0 && ncfspgs <= op_data->op_npages);
+
+ CDEBUG(D_INODE, "read %d(%d)/%d pages\n", ncfspgs, nlupgs,
+ op_data->op_npages);
+
+ lmv_adjust_dirpages(pages, ncfspgs, nlupgs);
+
+ return rc;
+}
+
+static int lmv_unlink(struct obd_export *exp, struct md_op_data *op_data,
+ struct ptlrpc_request **request)
+{
+ struct obd_device *obd = exp->exp_obd;
+ struct lmv_obd *lmv = &obd->u.lmv;
+ struct lmv_tgt_desc *tgt = NULL;
+ struct mdt_body *body;
+ int rc;
+
+ rc = lmv_check_connect(obd);
+ if (rc)
+ return rc;
+retry:
+ /* Send unlink requests to the MDT where the child is located */
+ if (likely(!fid_is_zero(&op_data->op_fid2)))
+ tgt = lmv_locate_mds(lmv, op_data, &op_data->op_fid2);
+ else
+ tgt = lmv_locate_mds(lmv, op_data, &op_data->op_fid1);
+ if (IS_ERR(tgt))
+ return PTR_ERR(tgt);
+
+ op_data->op_fsuid = from_kuid(&init_user_ns, current_fsuid());
+ op_data->op_fsgid = from_kgid(&init_user_ns, current_fsgid());
+ op_data->op_cap = cfs_curproc_cap_pack();
+
+ /*
+ * If child's fid is given, cancel unused locks for it if it is from
+ * another export than parent.
+ *
+ * LOOKUP lock for child (fid3) should also be cancelled on parent
+ * tgt_tgt in mdc_unlink().
+ */
+ op_data->op_flags |= MF_MDC_CANCEL_FID1 | MF_MDC_CANCEL_FID3;
+
+ /*
+ * Cancel FULL locks on child (fid3).
+ */
+ rc = lmv_early_cancel(exp, op_data, tgt->ltd_idx, LCK_EX,
+ MDS_INODELOCK_FULL, MF_MDC_CANCEL_FID3);
+
+ if (rc != 0)
+ return rc;
+
+ CDEBUG(D_INODE, "unlink with fid="DFID"/"DFID" -> mds #%d\n",
+ PFID(&op_data->op_fid1), PFID(&op_data->op_fid2), tgt->ltd_idx);
+
+ rc = md_unlink(tgt->ltd_exp, op_data, request);
+ if (rc != 0 && rc != -EREMOTE)
+ return rc;
+
+ body = req_capsule_server_get(&(*request)->rq_pill, &RMF_MDT_BODY);
+ if (body == NULL)
+ return -EPROTO;
+
+ /* Not cross-ref case, just get out of here. */
+ if (likely(!(body->valid & OBD_MD_MDS)))
+ return 0;
+
+ CDEBUG(D_INODE, "%s: try unlink to another MDT for "DFID"\n",
+ exp->exp_obd->obd_name, PFID(&body->fid1));
+
+ /* This is a remote object, try remote MDT, Note: it may
+ * try more than 1 time here, Considering following case
+ * /mnt/lustre is root on MDT0, remote1 is on MDT1
+ * 1. Initially A does not know where remote1 is, it send
+ * unlink RPC to MDT0, MDT0 return -EREMOTE, it will
+ * resend unlink RPC to MDT1 (retry 1st time).
+ *
+ * 2. During the unlink RPC in flight,
+ * client B mv /mnt/lustre/remote1 /mnt/lustre/remote2
+ * and create new remote1, but on MDT0
+ *
+ * 3. MDT1 get unlink RPC(from A), then do remote lock on
+ * /mnt/lustre, then lookup get fid of remote1, and find
+ * it is remote dir again, and replay -EREMOTE again.
+ *
+ * 4. Then A will resend unlink RPC to MDT0. (retry 2nd times).
+ *
+ * In theory, it might try unlimited time here, but it should
+ * be very rare case. */
+ op_data->op_fid2 = body->fid1;
+ ptlrpc_req_finished(*request);
+ *request = NULL;
+
+ goto retry;
+}
+
+static int lmv_precleanup(struct obd_device *obd, enum obd_cleanup_stage stage)
+{
+ struct lmv_obd *lmv = &obd->u.lmv;
+
+ switch (stage) {
+ case OBD_CLEANUP_EARLY:
+ /* XXX: here should be calling obd_precleanup() down to
+ * stack. */
+ break;
+ case OBD_CLEANUP_EXPORTS:
+ fld_client_proc_fini(&lmv->lmv_fld);
+ lprocfs_obd_cleanup(obd);
+ break;
+ default:
+ break;
+ }
+ return 0;
+}
+
+static int lmv_get_info(const struct lu_env *env, struct obd_export *exp,
+ __u32 keylen, void *key, __u32 *vallen, void *val,
+ struct lov_stripe_md *lsm)
+{
+ struct obd_device *obd;
+ struct lmv_obd *lmv;
+ int rc = 0;
+
+ obd = class_exp2obd(exp);
+ if (obd == NULL) {
+ CDEBUG(D_IOCTL, "Invalid client cookie %#llx\n",
+ exp->exp_handle.h_cookie);
+ return -EINVAL;
+ }
+
+ lmv = &obd->u.lmv;
+ if (keylen >= strlen("remote_flag") && !strcmp(key, "remote_flag")) {
+ struct lmv_tgt_desc *tgt;
+ int i;
+
+ rc = lmv_check_connect(obd);
+ if (rc)
+ return rc;
+
+ LASSERT(*vallen == sizeof(__u32));
+ for (i = 0; i < lmv->desc.ld_tgt_count; i++) {
+ tgt = lmv->tgts[i];
+ /*
+ * All tgts should be connected when this gets called.
+ */
+ if (tgt == NULL || tgt->ltd_exp == NULL)
+ continue;
+
+ if (!obd_get_info(env, tgt->ltd_exp, keylen, key,
+ vallen, val, NULL))
+ return 0;
+ }
+ return -EINVAL;
+ } else if (KEY_IS(KEY_MAX_EASIZE) ||
+ KEY_IS(KEY_DEFAULT_EASIZE) ||
+ KEY_IS(KEY_MAX_COOKIESIZE) ||
+ KEY_IS(KEY_DEFAULT_COOKIESIZE) ||
+ KEY_IS(KEY_CONN_DATA)) {
+ rc = lmv_check_connect(obd);
+ if (rc)
+ return rc;
+
+ /*
+ * Forwarding this request to first MDS, it should know LOV
+ * desc.
+ */
+ rc = obd_get_info(env, lmv->tgts[0]->ltd_exp, keylen, key,
+ vallen, val, NULL);
+ if (!rc && KEY_IS(KEY_CONN_DATA))
+ exp->exp_connect_data = *(struct obd_connect_data *)val;
+ return rc;
+ } else if (KEY_IS(KEY_TGT_COUNT)) {
+ *((int *)val) = lmv->desc.ld_tgt_count;
+ return 0;
+ }
+
+ CDEBUG(D_IOCTL, "Invalid key\n");
+ return -EINVAL;
+}
+
+static int lmv_set_info_async(const struct lu_env *env, struct obd_export *exp,
+ u32 keylen, void *key, u32 vallen,
+ void *val, struct ptlrpc_request_set *set)
+{
+ struct lmv_tgt_desc *tgt;
+ struct obd_device *obd;
+ struct lmv_obd *lmv;
+ int rc = 0;
+
+ obd = class_exp2obd(exp);
+ if (obd == NULL) {
+ CDEBUG(D_IOCTL, "Invalid client cookie %#llx\n",
+ exp->exp_handle.h_cookie);
+ return -EINVAL;
+ }
+ lmv = &obd->u.lmv;
+
+ if (KEY_IS(KEY_READ_ONLY) || KEY_IS(KEY_FLUSH_CTX)) {
+ int i, err = 0;
+
+ for (i = 0; i < lmv->desc.ld_tgt_count; i++) {
+ tgt = lmv->tgts[i];
+
+ if (tgt == NULL || tgt->ltd_exp == NULL)
+ continue;
+
+ err = obd_set_info_async(env, tgt->ltd_exp,
+ keylen, key, vallen, val, set);
+ if (err && rc == 0)
+ rc = err;
+ }
+
+ return rc;
+ }
+
+ return -EINVAL;
+}
+
+static int lmv_packmd(struct obd_export *exp, struct lov_mds_md **lmmp,
+ struct lov_stripe_md *lsm)
+{
+ struct obd_device *obd = class_exp2obd(exp);
+ struct lmv_obd *lmv = &obd->u.lmv;
+ struct lmv_stripe_md *meap;
+ struct lmv_stripe_md *lsmp;
+ int mea_size;
+ int i;
+
+ mea_size = lmv_get_easize(lmv);
+ if (!lmmp)
+ return mea_size;
+
+ if (*lmmp && !lsm) {
+ OBD_FREE_LARGE(*lmmp, mea_size);
+ *lmmp = NULL;
+ return 0;
+ }
+
+ if (*lmmp == NULL) {
+ OBD_ALLOC_LARGE(*lmmp, mea_size);
+ if (*lmmp == NULL)
+ return -ENOMEM;
+ }
+
+ if (!lsm)
+ return mea_size;
+
+ lsmp = (struct lmv_stripe_md *)lsm;
+ meap = (struct lmv_stripe_md *)*lmmp;
+
+ if (lsmp->mea_magic != MEA_MAGIC_LAST_CHAR &&
+ lsmp->mea_magic != MEA_MAGIC_ALL_CHARS)
+ return -EINVAL;
+
+ meap->mea_magic = cpu_to_le32(lsmp->mea_magic);
+ meap->mea_count = cpu_to_le32(lsmp->mea_count);
+ meap->mea_master = cpu_to_le32(lsmp->mea_master);
+
+ for (i = 0; i < lmv->desc.ld_tgt_count; i++) {
+ meap->mea_ids[i] = lsmp->mea_ids[i];
+ fid_cpu_to_le(&meap->mea_ids[i], &lsmp->mea_ids[i]);
+ }
+
+ return mea_size;
+}
+
+static int lmv_unpackmd(struct obd_export *exp, struct lov_stripe_md **lsmp,
+ struct lov_mds_md *lmm, int lmm_size)
+{
+ struct obd_device *obd = class_exp2obd(exp);
+ struct lmv_stripe_md **tmea = (struct lmv_stripe_md **)lsmp;
+ struct lmv_stripe_md *mea = (struct lmv_stripe_md *)lmm;
+ struct lmv_obd *lmv = &obd->u.lmv;
+ int mea_size;
+ int i;
+ __u32 magic;
+
+ mea_size = lmv_get_easize(lmv);
+ if (lsmp == NULL)
+ return mea_size;
+
+ if (*lsmp != NULL && lmm == NULL) {
+ OBD_FREE_LARGE(*tmea, mea_size);
+ *lsmp = NULL;
+ return 0;
+ }
+
+ LASSERT(mea_size == lmm_size);
+
+ OBD_ALLOC_LARGE(*tmea, mea_size);
+ if (*tmea == NULL)
+ return -ENOMEM;
+
+ if (!lmm)
+ return mea_size;
+
+ if (mea->mea_magic == MEA_MAGIC_LAST_CHAR ||
+ mea->mea_magic == MEA_MAGIC_ALL_CHARS ||
+ mea->mea_magic == MEA_MAGIC_HASH_SEGMENT) {
+ magic = le32_to_cpu(mea->mea_magic);
+ } else {
+ /*
+ * Old mea is not handled here.
+ */
+ CERROR("Old not supportable EA is found\n");
+ LBUG();
+ }
+
+ (*tmea)->mea_magic = magic;
+ (*tmea)->mea_count = le32_to_cpu(mea->mea_count);
+ (*tmea)->mea_master = le32_to_cpu(mea->mea_master);
+
+ for (i = 0; i < (*tmea)->mea_count; i++) {
+ (*tmea)->mea_ids[i] = mea->mea_ids[i];
+ fid_le_to_cpu(&(*tmea)->mea_ids[i], &(*tmea)->mea_ids[i]);
+ }
+ return mea_size;
+}
+
+static int lmv_cancel_unused(struct obd_export *exp, const struct lu_fid *fid,
+ ldlm_policy_data_t *policy, ldlm_mode_t mode,
+ ldlm_cancel_flags_t flags, void *opaque)
+{
+ struct obd_device *obd = exp->exp_obd;
+ struct lmv_obd *lmv = &obd->u.lmv;
+ int rc = 0;
+ int err;
+ int i;
+
+ LASSERT(fid != NULL);
+
+ for (i = 0; i < lmv->desc.ld_tgt_count; i++) {
+ if (lmv->tgts[i] == NULL || lmv->tgts[i]->ltd_exp == NULL ||
+ lmv->tgts[i]->ltd_active == 0)
+ continue;
+
+ err = md_cancel_unused(lmv->tgts[i]->ltd_exp, fid,
+ policy, mode, flags, opaque);
+ if (!rc)
+ rc = err;
+ }
+ return rc;
+}
+
+static int lmv_set_lock_data(struct obd_export *exp, __u64 *lockh, void *data,
+ __u64 *bits)
+{
+ struct lmv_obd *lmv = &exp->exp_obd->u.lmv;
+ int rc;
+
+ rc = md_set_lock_data(lmv->tgts[0]->ltd_exp, lockh, data, bits);
+ return rc;
+}
+
+static ldlm_mode_t lmv_lock_match(struct obd_export *exp, __u64 flags,
+ const struct lu_fid *fid, ldlm_type_t type,
+ ldlm_policy_data_t *policy, ldlm_mode_t mode,
+ struct lustre_handle *lockh)
+{
+ struct obd_device *obd = exp->exp_obd;
+ struct lmv_obd *lmv = &obd->u.lmv;
+ ldlm_mode_t rc;
+ int i;
+
+ CDEBUG(D_INODE, "Lock match for "DFID"\n", PFID(fid));
+
+ /*
+ * With CMD every object can have two locks in different namespaces:
+ * lookup lock in space of mds storing direntry and update/open lock in
+ * space of mds storing inode. Thus we check all targets, not only that
+ * one fid was created in.
+ */
+ for (i = 0; i < lmv->desc.ld_tgt_count; i++) {
+ if (lmv->tgts[i] == NULL ||
+ lmv->tgts[i]->ltd_exp == NULL ||
+ lmv->tgts[i]->ltd_active == 0)
+ continue;
+
+ rc = md_lock_match(lmv->tgts[i]->ltd_exp, flags, fid,
+ type, policy, mode, lockh);
+ if (rc)
+ return rc;
+ }
+
+ return 0;
+}
+
+static int lmv_get_lustre_md(struct obd_export *exp,
+ struct ptlrpc_request *req,
+ struct obd_export *dt_exp,
+ struct obd_export *md_exp,
+ struct lustre_md *md)
+{
+ struct lmv_obd *lmv = &exp->exp_obd->u.lmv;
+
+ return md_get_lustre_md(lmv->tgts[0]->ltd_exp, req, dt_exp, md_exp, md);
+}
+
+static int lmv_free_lustre_md(struct obd_export *exp, struct lustre_md *md)
+{
+ struct obd_device *obd = exp->exp_obd;
+ struct lmv_obd *lmv = &obd->u.lmv;
+
+ if (md->mea)
+ obd_free_memmd(exp, (void *)&md->mea);
+ return md_free_lustre_md(lmv->tgts[0]->ltd_exp, md);
+}
+
+static int lmv_set_open_replay_data(struct obd_export *exp,
+ struct obd_client_handle *och,
+ struct lookup_intent *it)
+{
+ struct obd_device *obd = exp->exp_obd;
+ struct lmv_obd *lmv = &obd->u.lmv;
+ struct lmv_tgt_desc *tgt;
+
+ tgt = lmv_find_target(lmv, &och->och_fid);
+ if (IS_ERR(tgt))
+ return PTR_ERR(tgt);
+
+ return md_set_open_replay_data(tgt->ltd_exp, och, it);
+}
+
+static int lmv_clear_open_replay_data(struct obd_export *exp,
+ struct obd_client_handle *och)
+{
+ struct obd_device *obd = exp->exp_obd;
+ struct lmv_obd *lmv = &obd->u.lmv;
+ struct lmv_tgt_desc *tgt;
+
+ tgt = lmv_find_target(lmv, &och->och_fid);
+ if (IS_ERR(tgt))
+ return PTR_ERR(tgt);
+
+ return md_clear_open_replay_data(tgt->ltd_exp, och);
+}
+
+static int lmv_get_remote_perm(struct obd_export *exp,
+ const struct lu_fid *fid,
+ struct obd_capa *oc, __u32 suppgid,
+ struct ptlrpc_request **request)
+{
+ struct obd_device *obd = exp->exp_obd;
+ struct lmv_obd *lmv = &obd->u.lmv;
+ struct lmv_tgt_desc *tgt;
+ int rc;
+
+ rc = lmv_check_connect(obd);
+ if (rc)
+ return rc;
+
+ tgt = lmv_find_target(lmv, fid);
+ if (IS_ERR(tgt))
+ return PTR_ERR(tgt);
+
+ rc = md_get_remote_perm(tgt->ltd_exp, fid, oc, suppgid, request);
+ return rc;
+}
+
+static int lmv_renew_capa(struct obd_export *exp, struct obd_capa *oc,
+ renew_capa_cb_t cb)
+{
+ struct obd_device *obd = exp->exp_obd;
+ struct lmv_obd *lmv = &obd->u.lmv;
+ struct lmv_tgt_desc *tgt;
+ int rc;
+
+ rc = lmv_check_connect(obd);
+ if (rc)
+ return rc;
+
+ tgt = lmv_find_target(lmv, &oc->c_capa.lc_fid);
+ if (IS_ERR(tgt))
+ return PTR_ERR(tgt);
+
+ rc = md_renew_capa(tgt->ltd_exp, oc, cb);
+ return rc;
+}
+
+static int lmv_unpack_capa(struct obd_export *exp, struct ptlrpc_request *req,
+ const struct req_msg_field *field,
+ struct obd_capa **oc)
+{
+ struct lmv_obd *lmv = &exp->exp_obd->u.lmv;
+
+ return md_unpack_capa(lmv->tgts[0]->ltd_exp, req, field, oc);
+}
+
+static int lmv_intent_getattr_async(struct obd_export *exp,
+ struct md_enqueue_info *minfo,
+ struct ldlm_enqueue_info *einfo)
+{
+ struct md_op_data *op_data = &minfo->mi_data;
+ struct obd_device *obd = exp->exp_obd;
+ struct lmv_obd *lmv = &obd->u.lmv;
+ struct lmv_tgt_desc *tgt = NULL;
+ int rc;
+
+ rc = lmv_check_connect(obd);
+ if (rc)
+ return rc;
+
+ tgt = lmv_find_target(lmv, &op_data->op_fid1);
+ if (IS_ERR(tgt))
+ return PTR_ERR(tgt);
+
+ rc = md_intent_getattr_async(tgt->ltd_exp, minfo, einfo);
+ return rc;
+}
+
+static int lmv_revalidate_lock(struct obd_export *exp, struct lookup_intent *it,
+ struct lu_fid *fid, __u64 *bits)
+{
+ struct obd_device *obd = exp->exp_obd;
+ struct lmv_obd *lmv = &obd->u.lmv;
+ struct lmv_tgt_desc *tgt;
+ int rc;
+
+ rc = lmv_check_connect(obd);
+ if (rc)
+ return rc;
+
+ tgt = lmv_find_target(lmv, fid);
+ if (IS_ERR(tgt))
+ return PTR_ERR(tgt);
+
+ rc = md_revalidate_lock(tgt->ltd_exp, it, fid, bits);
+ return rc;
+}
+
+/**
+ * For lmv, only need to send request to master MDT, and the master MDT will
+ * process with other slave MDTs. The only exception is Q_GETOQUOTA for which
+ * we directly fetch data from the slave MDTs.
+ */
+static int lmv_quotactl(struct obd_device *unused, struct obd_export *exp,
+ struct obd_quotactl *oqctl)
+{
+ struct obd_device *obd = class_exp2obd(exp);
+ struct lmv_obd *lmv = &obd->u.lmv;
+ struct lmv_tgt_desc *tgt = lmv->tgts[0];
+ int rc = 0, i;
+ __u64 curspace, curinodes;
+
+ if (!lmv->desc.ld_tgt_count || !tgt->ltd_active) {
+ CERROR("master lmv inactive\n");
+ return -EIO;
+ }
+
+ if (oqctl->qc_cmd != Q_GETOQUOTA) {
+ rc = obd_quotactl(tgt->ltd_exp, oqctl);
+ return rc;
+ }
+
+ curspace = curinodes = 0;
+ for (i = 0; i < lmv->desc.ld_tgt_count; i++) {
+ int err;
+ tgt = lmv->tgts[i];
+
+ if (tgt == NULL || tgt->ltd_exp == NULL || tgt->ltd_active == 0)
+ continue;
+ if (!tgt->ltd_active) {
+ CDEBUG(D_HA, "mdt %d is inactive.\n", i);
+ continue;
+ }
+
+ err = obd_quotactl(tgt->ltd_exp, oqctl);
+ if (err) {
+ CERROR("getquota on mdt %d failed. %d\n", i, err);
+ if (!rc)
+ rc = err;
+ } else {
+ curspace += oqctl->qc_dqblk.dqb_curspace;
+ curinodes += oqctl->qc_dqblk.dqb_curinodes;
+ }
+ }
+ oqctl->qc_dqblk.dqb_curspace = curspace;
+ oqctl->qc_dqblk.dqb_curinodes = curinodes;
+
+ return rc;
+}
+
+static int lmv_quotacheck(struct obd_device *unused, struct obd_export *exp,
+ struct obd_quotactl *oqctl)
+{
+ struct obd_device *obd = class_exp2obd(exp);
+ struct lmv_obd *lmv = &obd->u.lmv;
+ struct lmv_tgt_desc *tgt;
+ int i, rc = 0;
+
+ for (i = 0; i < lmv->desc.ld_tgt_count; i++) {
+ int err;
+ tgt = lmv->tgts[i];
+ if (tgt == NULL || tgt->ltd_exp == NULL || !tgt->ltd_active) {
+ CERROR("lmv idx %d inactive\n", i);
+ return -EIO;
+ }
+
+ err = obd_quotacheck(tgt->ltd_exp, oqctl);
+ if (err && !rc)
+ rc = err;
+ }
+
+ return rc;
+}
+
+static struct obd_ops lmv_obd_ops = {
+ .o_owner = THIS_MODULE,
+ .o_setup = lmv_setup,
+ .o_cleanup = lmv_cleanup,
+ .o_precleanup = lmv_precleanup,
+ .o_process_config = lmv_process_config,
+ .o_connect = lmv_connect,
+ .o_disconnect = lmv_disconnect,
+ .o_statfs = lmv_statfs,
+ .o_get_info = lmv_get_info,
+ .o_set_info_async = lmv_set_info_async,
+ .o_packmd = lmv_packmd,
+ .o_unpackmd = lmv_unpackmd,
+ .o_notify = lmv_notify,
+ .o_get_uuid = lmv_get_uuid,
+ .o_iocontrol = lmv_iocontrol,
+ .o_quotacheck = lmv_quotacheck,
+ .o_quotactl = lmv_quotactl
+};
+
+static struct md_ops lmv_md_ops = {
+ .m_getstatus = lmv_getstatus,
+ .m_null_inode = lmv_null_inode,
+ .m_find_cbdata = lmv_find_cbdata,
+ .m_close = lmv_close,
+ .m_create = lmv_create,
+ .m_done_writing = lmv_done_writing,
+ .m_enqueue = lmv_enqueue,
+ .m_getattr = lmv_getattr,
+ .m_getxattr = lmv_getxattr,
+ .m_getattr_name = lmv_getattr_name,
+ .m_intent_lock = lmv_intent_lock,
+ .m_link = lmv_link,
+ .m_rename = lmv_rename,
+ .m_setattr = lmv_setattr,
+ .m_setxattr = lmv_setxattr,
+ .m_sync = lmv_sync,
+ .m_readpage = lmv_readpage,
+ .m_unlink = lmv_unlink,
+ .m_init_ea_size = lmv_init_ea_size,
+ .m_cancel_unused = lmv_cancel_unused,
+ .m_set_lock_data = lmv_set_lock_data,
+ .m_lock_match = lmv_lock_match,
+ .m_get_lustre_md = lmv_get_lustre_md,
+ .m_free_lustre_md = lmv_free_lustre_md,
+ .m_set_open_replay_data = lmv_set_open_replay_data,
+ .m_clear_open_replay_data = lmv_clear_open_replay_data,
+ .m_renew_capa = lmv_renew_capa,
+ .m_unpack_capa = lmv_unpack_capa,
+ .m_get_remote_perm = lmv_get_remote_perm,
+ .m_intent_getattr_async = lmv_intent_getattr_async,
+ .m_revalidate_lock = lmv_revalidate_lock
+};
+
+static int __init lmv_init(void)
+{
+ struct lprocfs_static_vars lvars;
+ int rc;
+
+ lprocfs_lmv_init_vars(&lvars);
+
+ rc = class_register_type(&lmv_obd_ops, &lmv_md_ops,
+ lvars.module_vars, LUSTRE_LMV_NAME, NULL);
+ return rc;
+}
+
+static void lmv_exit(void)
+{
+ class_unregister_type(LUSTRE_LMV_NAME);
+}
+
+MODULE_AUTHOR("Sun Microsystems, Inc. <http://www.lustre.org/>");
+MODULE_DESCRIPTION("Lustre Logical Metadata Volume OBD driver");
+MODULE_LICENSE("GPL");
+
+module_init(lmv_init);
+module_exit(lmv_exit);
diff --git a/drivers/staging/lustre/lustre/lmv/lproc_lmv.c b/drivers/staging/lustre/lustre/lmv/lproc_lmv.c
new file mode 100644
index 000000000..22e5c315f
--- /dev/null
+++ b/drivers/staging/lustre/lustre/lmv/lproc_lmv.c
@@ -0,0 +1,237 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2012, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ */
+
+#define DEBUG_SUBSYSTEM S_CLASS
+
+#include <linux/seq_file.h>
+#include <linux/statfs.h>
+#include "../include/lprocfs_status.h"
+#include "../include/obd_class.h"
+#include "lmv_internal.h"
+
+static int lmv_numobd_seq_show(struct seq_file *m, void *v)
+{
+ struct obd_device *dev = (struct obd_device *)m->private;
+ struct lmv_desc *desc;
+
+ LASSERT(dev != NULL);
+ desc = &dev->u.lmv.desc;
+ seq_printf(m, "%u\n", desc->ld_tgt_count);
+ return 0;
+}
+LPROC_SEQ_FOPS_RO(lmv_numobd);
+
+static const char *placement_name[] = {
+ [PLACEMENT_CHAR_POLICY] = "CHAR",
+ [PLACEMENT_NID_POLICY] = "NID",
+ [PLACEMENT_INVAL_POLICY] = "INVAL"
+};
+
+static enum placement_policy placement_name2policy(char *name, int len)
+{
+ int i;
+
+ for (i = 0; i < PLACEMENT_MAX_POLICY; i++) {
+ if (!strncmp(placement_name[i], name, len))
+ return i;
+ }
+ return PLACEMENT_INVAL_POLICY;
+}
+
+static const char *placement_policy2name(enum placement_policy placement)
+{
+ LASSERT(placement < PLACEMENT_MAX_POLICY);
+ return placement_name[placement];
+}
+
+static int lmv_placement_seq_show(struct seq_file *m, void *v)
+{
+ struct obd_device *dev = (struct obd_device *)m->private;
+ struct lmv_obd *lmv;
+
+ LASSERT(dev != NULL);
+ lmv = &dev->u.lmv;
+ seq_printf(m, "%s\n", placement_policy2name(lmv->lmv_placement));
+ return 0;
+}
+
+#define MAX_POLICY_STRING_SIZE 64
+
+static ssize_t lmv_placement_seq_write(struct file *file,
+ const char __user *buffer,
+ size_t count, loff_t *off)
+{
+ struct obd_device *dev = ((struct seq_file *)file->private_data)->private;
+ char dummy[MAX_POLICY_STRING_SIZE + 1];
+ int len = count;
+ enum placement_policy policy;
+ struct lmv_obd *lmv;
+
+ if (copy_from_user(dummy, buffer, MAX_POLICY_STRING_SIZE))
+ return -EFAULT;
+
+ LASSERT(dev != NULL);
+ lmv = &dev->u.lmv;
+
+ if (len > MAX_POLICY_STRING_SIZE)
+ len = MAX_POLICY_STRING_SIZE;
+
+ if (dummy[len - 1] == '\n')
+ len--;
+ dummy[len] = '\0';
+
+ policy = placement_name2policy(dummy, len);
+ if (policy != PLACEMENT_INVAL_POLICY) {
+ spin_lock(&lmv->lmv_lock);
+ lmv->lmv_placement = policy;
+ spin_unlock(&lmv->lmv_lock);
+ } else {
+ CERROR("Invalid placement policy \"%s\"!\n", dummy);
+ return -EINVAL;
+ }
+ return count;
+}
+LPROC_SEQ_FOPS(lmv_placement);
+
+static int lmv_activeobd_seq_show(struct seq_file *m, void *v)
+{
+ struct obd_device *dev = (struct obd_device *)m->private;
+ struct lmv_desc *desc;
+
+ LASSERT(dev != NULL);
+ desc = &dev->u.lmv.desc;
+ seq_printf(m, "%u\n", desc->ld_active_tgt_count);
+ return 0;
+}
+LPROC_SEQ_FOPS_RO(lmv_activeobd);
+
+static int lmv_desc_uuid_seq_show(struct seq_file *m, void *v)
+{
+ struct obd_device *dev = (struct obd_device *)m->private;
+ struct lmv_obd *lmv;
+
+ LASSERT(dev != NULL);
+ lmv = &dev->u.lmv;
+ seq_printf(m, "%s\n", lmv->desc.ld_uuid.uuid);
+ return 0;
+}
+LPROC_SEQ_FOPS_RO(lmv_desc_uuid);
+
+static void *lmv_tgt_seq_start(struct seq_file *p, loff_t *pos)
+{
+ struct obd_device *dev = p->private;
+ struct lmv_obd *lmv = &dev->u.lmv;
+ return (*pos >= lmv->desc.ld_tgt_count) ? NULL : lmv->tgts[*pos];
+}
+
+static void lmv_tgt_seq_stop(struct seq_file *p, void *v)
+{
+ return;
+}
+
+static void *lmv_tgt_seq_next(struct seq_file *p, void *v, loff_t *pos)
+{
+ struct obd_device *dev = p->private;
+ struct lmv_obd *lmv = &dev->u.lmv;
+ ++*pos;
+ return (*pos >= lmv->desc.ld_tgt_count) ? NULL : lmv->tgts[*pos];
+}
+
+static int lmv_tgt_seq_show(struct seq_file *p, void *v)
+{
+ struct lmv_tgt_desc *tgt = v;
+
+ if (tgt == NULL)
+ return 0;
+ seq_printf(p, "%d: %s %sACTIVE\n",
+ tgt->ltd_idx, tgt->ltd_uuid.uuid,
+ tgt->ltd_active ? "" : "IN");
+ return 0;
+}
+
+static struct seq_operations lmv_tgt_sops = {
+ .start = lmv_tgt_seq_start,
+ .stop = lmv_tgt_seq_stop,
+ .next = lmv_tgt_seq_next,
+ .show = lmv_tgt_seq_show,
+};
+
+static int lmv_target_seq_open(struct inode *inode, struct file *file)
+{
+ struct seq_file *seq;
+ int rc;
+
+ rc = seq_open(file, &lmv_tgt_sops);
+ if (rc)
+ return rc;
+
+ seq = file->private_data;
+ seq->private = PDE_DATA(inode);
+
+ return 0;
+}
+
+LPROC_SEQ_FOPS_RO_TYPE(lmv, uuid);
+
+static struct lprocfs_vars lprocfs_lmv_obd_vars[] = {
+ { "numobd", &lmv_numobd_fops, NULL, 0 },
+ { "placement", &lmv_placement_fops, NULL, 0 },
+ { "activeobd", &lmv_activeobd_fops, NULL, 0 },
+ { "uuid", &lmv_uuid_fops, NULL, 0 },
+ { "desc_uuid", &lmv_desc_uuid_fops, NULL, 0 },
+ { NULL }
+};
+
+LPROC_SEQ_FOPS_RO_TYPE(lmv, numrefs);
+
+static struct lprocfs_vars lprocfs_lmv_module_vars[] = {
+ { "num_refs", &lmv_numrefs_fops, NULL, 0 },
+ { NULL }
+};
+
+struct file_operations lmv_proc_target_fops = {
+ .owner = THIS_MODULE,
+ .open = lmv_target_seq_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = seq_release,
+};
+
+void lprocfs_lmv_init_vars(struct lprocfs_static_vars *lvars)
+{
+ lvars->module_vars = lprocfs_lmv_module_vars;
+ lvars->obd_vars = lprocfs_lmv_obd_vars;
+}
diff --git a/drivers/staging/lustre/lustre/lov/Makefile b/drivers/staging/lustre/lustre/lov/Makefile
new file mode 100644
index 000000000..6fe56a24b
--- /dev/null
+++ b/drivers/staging/lustre/lustre/lov/Makefile
@@ -0,0 +1,6 @@
+obj-$(CONFIG_LUSTRE_FS) += lov.o
+lov-y := lov_obd.o lov_pack.o lov_offset.o lov_merge.o \
+ lov_request.o lov_ea.o lov_dev.o lov_object.o lov_page.o \
+ lov_lock.o lov_io.o lovsub_dev.o lovsub_object.o lovsub_page.o \
+ lovsub_lock.o lovsub_io.o lov_pool.o
+lov-$(CONFIG_PROC_FS) += lproc_lov.o
diff --git a/drivers/staging/lustre/lustre/lov/lov_cl_internal.h b/drivers/staging/lustre/lustre/lov/lov_cl_internal.h
new file mode 100644
index 000000000..314ce8525
--- /dev/null
+++ b/drivers/staging/lustre/lustre/lov/lov_cl_internal.h
@@ -0,0 +1,839 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2012, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ *
+ * Internal interfaces of LOV layer.
+ *
+ * Author: Nikita Danilov <nikita.danilov@sun.com>
+ * Author: Jinshan Xiong <jinshan.xiong@intel.com>
+ */
+
+#ifndef LOV_CL_INTERNAL_H
+#define LOV_CL_INTERNAL_H
+
+#include "../../include/linux/libcfs/libcfs.h"
+
+#include "../include/obd.h"
+#include "../include/cl_object.h"
+#include "lov_internal.h"
+
+/** \defgroup lov lov
+ * Logical object volume layer. This layer implements data striping (raid0).
+ *
+ * At the lov layer top-entity (object, page, lock, io) is connected to one or
+ * more sub-entities: top-object, representing a file is connected to a set of
+ * sub-objects, each representing a stripe, file-level top-lock is connected
+ * to a set of per-stripe sub-locks, top-page is connected to a (single)
+ * sub-page, and a top-level IO is connected to a set of (potentially
+ * concurrent) sub-IO's.
+ *
+ * Sub-object, sub-page, and sub-io have well-defined top-object and top-page
+ * respectively, while a single sub-lock can be part of multiple top-locks.
+ *
+ * Reference counting models are different for different types of entities:
+ *
+ * - top-object keeps a reference to its sub-objects, and destroys them
+ * when it is destroyed.
+ *
+ * - top-page keeps a reference to its sub-page, and destroys it when it
+ * is destroyed.
+ *
+ * - sub-lock keep a reference to its top-locks. Top-lock keeps a
+ * reference (and a hold, see cl_lock_hold()) on its sub-locks when it
+ * actively using them (that is, in cl_lock_state::CLS_QUEUING,
+ * cl_lock_state::CLS_ENQUEUED, cl_lock_state::CLS_HELD states). When
+ * moving into cl_lock_state::CLS_CACHED state, top-lock releases a
+ * hold. From this moment top-lock has only a 'weak' reference to its
+ * sub-locks. This reference is protected by top-lock
+ * cl_lock::cll_guard, and will be automatically cleared by the sub-lock
+ * when the latter is destroyed. When a sub-lock is canceled, a
+ * reference to it is removed from the top-lock array, and top-lock is
+ * moved into CLS_NEW state. It is guaranteed that all sub-locks exist
+ * while their top-lock is in CLS_HELD or CLS_CACHED states.
+ *
+ * - IO's are not reference counted.
+ *
+ * To implement a connection between top and sub entities, lov layer is split
+ * into two pieces: lov ("upper half"), and lovsub ("bottom half"), both
+ * implementing full set of cl-interfaces. For example, top-object has vvp and
+ * lov layers, and it's sub-object has lovsub and osc layers. lovsub layer is
+ * used to track child-parent relationship.
+ *
+ * @{
+ */
+
+struct lovsub_device;
+struct lovsub_object;
+struct lovsub_lock;
+
+enum lov_device_flags {
+ LOV_DEV_INITIALIZED = 1 << 0
+};
+
+/*
+ * Upper half.
+ */
+
+/**
+ * Resources that are used in memory-cleaning path, and whose allocation
+ * cannot fail even when memory is tight. They are preallocated in sufficient
+ * quantities in lov_device::ld_emerg[], and access to them is serialized
+ * lov_device::ld_mutex.
+ */
+struct lov_device_emerg {
+ /**
+ * Page list used to submit IO when memory is in pressure.
+ */
+ struct cl_page_list emrg_page_list;
+ /**
+ * sub-io's shared by all threads accessing this device when memory is
+ * too low to allocate sub-io's dynamically.
+ */
+ struct cl_io emrg_subio;
+ /**
+ * Environments used by sub-io's in
+ * lov_device_emerg::emrg_subio.
+ */
+ struct lu_env *emrg_env;
+ /**
+ * Refchecks for lov_device_emerg::emrg_env.
+ *
+ * \see cl_env_get()
+ */
+ int emrg_refcheck;
+};
+
+struct lov_device {
+ /*
+ * XXX Locking of lov-private data is missing.
+ */
+ struct cl_device ld_cl;
+ struct lov_obd *ld_lov;
+ /** size of lov_device::ld_target[] array */
+ __u32 ld_target_nr;
+ struct lovsub_device **ld_target;
+ __u32 ld_flags;
+
+ /** Emergency resources used in memory-cleansing paths. */
+ struct lov_device_emerg **ld_emrg;
+ /**
+ * Serializes access to lov_device::ld_emrg in low-memory
+ * conditions.
+ */
+ struct mutex ld_mutex;
+};
+
+/**
+ * Layout type.
+ */
+enum lov_layout_type {
+ LLT_EMPTY, /** empty file without body (mknod + truncate) */
+ LLT_RAID0, /** striped file */
+ LLT_RELEASED, /** file with no objects (data in HSM) */
+ LLT_NR
+};
+
+static inline char *llt2str(enum lov_layout_type llt)
+{
+ switch (llt) {
+ case LLT_EMPTY:
+ return "EMPTY";
+ case LLT_RAID0:
+ return "RAID0";
+ case LLT_RELEASED:
+ return "RELEASED";
+ case LLT_NR:
+ LBUG();
+ }
+ LBUG();
+ return "";
+}
+
+/**
+ * lov-specific file state.
+ *
+ * lov object has particular layout type, determining how top-object is built
+ * on top of sub-objects. Layout type can change dynamically. When this
+ * happens, lov_object::lo_type_guard semaphore is taken in exclusive mode,
+ * all state pertaining to the old layout type is destroyed, and new state is
+ * constructed. All object methods take said semaphore in the shared mode,
+ * providing serialization against transition between layout types.
+ *
+ * To avoid multiple `if' or `switch' statements, selecting behavior for the
+ * current layout type, object methods perform double-dispatch, invoking
+ * function corresponding to the current layout type.
+ */
+struct lov_object {
+ struct cl_object lo_cl;
+ /**
+ * Serializes object operations with transitions between layout types.
+ *
+ * This semaphore is taken in shared mode by all object methods, and
+ * is taken in exclusive mode when object type is changed.
+ *
+ * \see lov_object::lo_type
+ */
+ struct rw_semaphore lo_type_guard;
+ /**
+ * Type of an object. Protected by lov_object::lo_type_guard.
+ */
+ enum lov_layout_type lo_type;
+ /**
+ * True if layout is invalid. This bit is cleared when layout lock
+ * is lost.
+ */
+ bool lo_layout_invalid;
+ /**
+ * How many IOs are on going on this object. Layout can be changed
+ * only if there is no active IO.
+ */
+ atomic_t lo_active_ios;
+ /**
+ * Waitq - wait for no one else is using lo_lsm
+ */
+ wait_queue_head_t lo_waitq;
+ /**
+ * Layout metadata. NULL if empty layout.
+ */
+ struct lov_stripe_md *lo_lsm;
+
+ union lov_layout_state {
+ struct lov_layout_raid0 {
+ unsigned lo_nr;
+ /**
+ * When this is true, lov_object::lo_attr contains
+ * valid up to date attributes for a top-level
+ * object. This field is reset to 0 when attributes of
+ * any sub-object change.
+ */
+ int lo_attr_valid;
+ /**
+ * Array of sub-objects. Allocated when top-object is
+ * created (lov_init_raid0()).
+ *
+ * Top-object is a strict master of its sub-objects:
+ * it is created before them, and outlives its
+ * children (this later is necessary so that basic
+ * functions like cl_object_top() always
+ * work). Top-object keeps a reference on every
+ * sub-object.
+ *
+ * When top-object is destroyed (lov_delete_raid0())
+ * it releases its reference to a sub-object and waits
+ * until the latter is finally destroyed.
+ */
+ struct lovsub_object **lo_sub;
+ /**
+ * protect lo_sub
+ */
+ spinlock_t lo_sub_lock;
+ /**
+ * Cached object attribute, built from sub-object
+ * attributes.
+ */
+ struct cl_attr lo_attr;
+ } raid0;
+ struct lov_layout_state_empty {
+ } empty;
+ struct lov_layout_state_released {
+ } released;
+ } u;
+ /**
+ * Thread that acquired lov_object::lo_type_guard in an exclusive
+ * mode.
+ */
+ struct task_struct *lo_owner;
+};
+
+/**
+ * Flags that top-lock can set on each of its sub-locks.
+ */
+enum lov_sub_flags {
+ /** Top-lock acquired a hold (cl_lock_hold()) on a sub-lock. */
+ LSF_HELD = 1 << 0
+};
+
+/**
+ * State lov_lock keeps for each sub-lock.
+ */
+struct lov_lock_sub {
+ /** sub-lock itself */
+ struct lovsub_lock *sub_lock;
+ /** An array of per-sub-lock flags, taken from enum lov_sub_flags */
+ unsigned sub_flags;
+ int sub_stripe;
+ struct cl_lock_descr sub_descr;
+ struct cl_lock_descr sub_got;
+};
+
+/**
+ * lov-specific lock state.
+ */
+struct lov_lock {
+ struct cl_lock_slice lls_cl;
+ /** Number of sub-locks in this lock */
+ int lls_nr;
+ /**
+ * Number of existing sub-locks.
+ */
+ unsigned lls_nr_filled;
+ /**
+ * Set when sub-lock was canceled, while top-lock was being
+ * used, or unused.
+ */
+ unsigned int lls_cancel_race:1;
+ /**
+ * An array of sub-locks
+ *
+ * There are two issues with managing sub-locks:
+ *
+ * - sub-locks are concurrently canceled, and
+ *
+ * - sub-locks are shared with other top-locks.
+ *
+ * To manage cancellation, top-lock acquires a hold on a sublock
+ * (lov_sublock_adopt()) when the latter is inserted into
+ * lov_lock::lls_sub[]. This hold is released (lov_sublock_release())
+ * when top-lock is going into CLS_CACHED state or destroyed. Hold
+ * prevents sub-lock from cancellation.
+ *
+ * Sub-lock sharing means, among other things, that top-lock that is
+ * in the process of creation (i.e., not yet inserted into lock list)
+ * is already accessible to other threads once at least one of its
+ * sub-locks is created, see lov_lock_sub_init().
+ *
+ * Sub-lock can be in one of the following states:
+ *
+ * - doesn't exist, lov_lock::lls_sub[]::sub_lock == NULL. Such
+ * sub-lock was either never created (top-lock is in CLS_NEW
+ * state), or it was created, then canceled, then destroyed
+ * (lov_lock_unlink() cleared sub-lock pointer in the top-lock).
+ *
+ * - sub-lock exists and is on
+ * hold. (lov_lock::lls_sub[]::sub_flags & LSF_HELD). This is a
+ * normal state of a sub-lock in CLS_HELD and CLS_CACHED states
+ * of a top-lock.
+ *
+ * - sub-lock exists, but is not held by the top-lock. This
+ * happens after top-lock released a hold on sub-locks before
+ * going into cache (lov_lock_unuse()).
+ *
+ * \todo To support wide-striping, array has to be replaced with a set
+ * of queues to avoid scanning.
+ */
+ struct lov_lock_sub *lls_sub;
+ /**
+ * Original description with which lock was enqueued.
+ */
+ struct cl_lock_descr lls_orig;
+};
+
+struct lov_page {
+ struct cl_page_slice lps_cl;
+ int lps_invalid;
+};
+
+/*
+ * Bottom half.
+ */
+
+struct lovsub_device {
+ struct cl_device acid_cl;
+ struct lov_device *acid_super;
+ int acid_idx;
+ struct cl_device *acid_next;
+};
+
+struct lovsub_object {
+ struct cl_object_header lso_header;
+ struct cl_object lso_cl;
+ struct lov_object *lso_super;
+ int lso_index;
+};
+
+/**
+ * A link between a top-lock and a sub-lock. Separate data-structure is
+ * necessary, because top-locks and sub-locks are in M:N relationship.
+ *
+ * \todo This can be optimized for a (by far) most frequent case of a single
+ * top-lock per sub-lock.
+ */
+struct lov_lock_link {
+ struct lov_lock *lll_super;
+ /** An index within parent lock. */
+ int lll_idx;
+ /**
+ * A linkage into per sub-lock list of all corresponding top-locks,
+ * hanging off lovsub_lock::lss_parents.
+ */
+ struct list_head lll_list;
+};
+
+/**
+ * Lock state at lovsub layer.
+ */
+struct lovsub_lock {
+ struct cl_lock_slice lss_cl;
+ /**
+ * List of top-locks that have given sub-lock as their part. Protected
+ * by cl_lock::cll_guard mutex.
+ */
+ struct list_head lss_parents;
+ /**
+ * Top-lock that initiated current operation on this sub-lock. This is
+ * only set during top-to-bottom lock operations like enqueue, and is
+ * used to optimize state change notification. Protected by
+ * cl_lock::cll_guard mutex.
+ *
+ * \see lovsub_lock_state_one().
+ */
+ struct cl_lock *lss_active;
+};
+
+/**
+ * Describe the environment settings for sublocks.
+ */
+struct lov_sublock_env {
+ const struct lu_env *lse_env;
+ struct cl_io *lse_io;
+ struct lov_io_sub *lse_sub;
+};
+
+struct lovsub_page {
+ struct cl_page_slice lsb_cl;
+};
+
+
+struct lov_thread_info {
+ struct cl_object_conf lti_stripe_conf;
+ struct lu_fid lti_fid;
+ struct cl_lock_descr lti_ldescr;
+ struct ost_lvb lti_lvb;
+ struct cl_2queue lti_cl2q;
+ struct cl_lock_closure lti_closure;
+ wait_queue_t lti_waiter;
+};
+
+/**
+ * State that lov_io maintains for every sub-io.
+ */
+struct lov_io_sub {
+ int sub_stripe;
+ /**
+ * sub-io for a stripe. Ideally sub-io's can be stopped and resumed
+ * independently, with lov acting as a scheduler to maximize overall
+ * throughput.
+ */
+ struct cl_io *sub_io;
+ /**
+ * Linkage into a list (hanging off lov_io::lis_active) of all
+ * sub-io's active for the current IO iteration.
+ */
+ struct list_head sub_linkage;
+ /**
+ * true, iff cl_io_init() was successfully executed against
+ * lov_io_sub::sub_io.
+ */
+ int sub_io_initialized;
+ /**
+ * True, iff lov_io_sub::sub_io and lov_io_sub::sub_env weren't
+ * allocated, but borrowed from a per-device emergency pool.
+ */
+ int sub_borrowed;
+ /**
+ * environment, in which sub-io executes.
+ */
+ struct lu_env *sub_env;
+ /**
+ * environment's refcheck.
+ *
+ * \see cl_env_get()
+ */
+ int sub_refcheck;
+ int sub_refcheck2;
+ int sub_reenter;
+ void *sub_cookie;
+};
+
+/**
+ * IO state private for LOV.
+ */
+struct lov_io {
+ /** super-class */
+ struct cl_io_slice lis_cl;
+ /**
+ * Pointer to the object slice. This is a duplicate of
+ * lov_io::lis_cl::cis_object.
+ */
+ struct lov_object *lis_object;
+ /**
+ * Original end-of-io position for this IO, set by the upper layer as
+ * cl_io::u::ci_rw::pos + cl_io::u::ci_rw::count. lov remembers this,
+ * changes pos and count to fit IO into a single stripe and uses saved
+ * value to determine when IO iterations have to stop.
+ *
+ * This is used only for CIT_READ and CIT_WRITE io's.
+ */
+ loff_t lis_io_endpos;
+
+ /**
+ * starting position within a file, for the current io loop iteration
+ * (stripe), used by ci_io_loop().
+ */
+ u64 lis_pos;
+ /**
+ * end position with in a file, for the current stripe io. This is
+ * exclusive (i.e., next offset after last byte affected by io).
+ */
+ u64 lis_endpos;
+
+ int lis_mem_frozen;
+ int lis_stripe_count;
+ int lis_active_subios;
+
+ /**
+ * the index of ls_single_subio in ls_subios array
+ */
+ int lis_single_subio_index;
+ struct cl_io lis_single_subio;
+
+ /**
+ * size of ls_subios array, actually the highest stripe #
+ */
+ int lis_nr_subios;
+ struct lov_io_sub *lis_subs;
+ /**
+ * List of active sub-io's.
+ */
+ struct list_head lis_active;
+};
+
+struct lov_session {
+ struct lov_io ls_io;
+ struct lov_sublock_env ls_subenv;
+};
+
+/**
+ * State of transfer for lov.
+ */
+struct lov_req {
+ struct cl_req_slice lr_cl;
+};
+
+/**
+ * State of transfer for lovsub.
+ */
+struct lovsub_req {
+ struct cl_req_slice lsrq_cl;
+};
+
+extern struct lu_device_type lov_device_type;
+extern struct lu_device_type lovsub_device_type;
+
+extern struct lu_context_key lov_key;
+extern struct lu_context_key lov_session_key;
+
+extern struct kmem_cache *lov_lock_kmem;
+extern struct kmem_cache *lov_object_kmem;
+extern struct kmem_cache *lov_thread_kmem;
+extern struct kmem_cache *lov_session_kmem;
+extern struct kmem_cache *lov_req_kmem;
+
+extern struct kmem_cache *lovsub_lock_kmem;
+extern struct kmem_cache *lovsub_object_kmem;
+extern struct kmem_cache *lovsub_req_kmem;
+
+extern struct kmem_cache *lov_lock_link_kmem;
+
+int lov_object_init(const struct lu_env *env, struct lu_object *obj,
+ const struct lu_object_conf *conf);
+int lovsub_object_init(const struct lu_env *env, struct lu_object *obj,
+ const struct lu_object_conf *conf);
+int lov_lock_init(const struct lu_env *env, struct cl_object *obj,
+ struct cl_lock *lock, const struct cl_io *io);
+int lov_io_init(const struct lu_env *env, struct cl_object *obj,
+ struct cl_io *io);
+int lovsub_lock_init(const struct lu_env *env, struct cl_object *obj,
+ struct cl_lock *lock, const struct cl_io *io);
+
+int lov_lock_init_raid0(const struct lu_env *env, struct cl_object *obj,
+ struct cl_lock *lock, const struct cl_io *io);
+int lov_lock_init_empty(const struct lu_env *env, struct cl_object *obj,
+ struct cl_lock *lock, const struct cl_io *io);
+int lov_io_init_raid0(const struct lu_env *env, struct cl_object *obj,
+ struct cl_io *io);
+int lov_io_init_empty(const struct lu_env *env, struct cl_object *obj,
+ struct cl_io *io);
+int lov_io_init_released(const struct lu_env *env, struct cl_object *obj,
+ struct cl_io *io);
+void lov_lock_unlink(const struct lu_env *env, struct lov_lock_link *link,
+ struct lovsub_lock *sub);
+
+struct lov_io_sub *lov_sub_get(const struct lu_env *env, struct lov_io *lio,
+ int stripe);
+void lov_sub_put(struct lov_io_sub *sub);
+int lov_sublock_modify(const struct lu_env *env, struct lov_lock *lov,
+ struct lovsub_lock *sublock,
+ const struct cl_lock_descr *d, int idx);
+
+
+int lov_page_init(const struct lu_env *env, struct cl_object *ob,
+ struct cl_page *page, struct page *vmpage);
+int lovsub_page_init(const struct lu_env *env, struct cl_object *ob,
+ struct cl_page *page, struct page *vmpage);
+
+int lov_page_init_empty(const struct lu_env *env,
+ struct cl_object *obj,
+ struct cl_page *page, struct page *vmpage);
+int lov_page_init_raid0(const struct lu_env *env,
+ struct cl_object *obj,
+ struct cl_page *page, struct page *vmpage);
+struct lu_object *lov_object_alloc(const struct lu_env *env,
+ const struct lu_object_header *hdr,
+ struct lu_device *dev);
+struct lu_object *lovsub_object_alloc(const struct lu_env *env,
+ const struct lu_object_header *hdr,
+ struct lu_device *dev);
+
+struct lov_lock_link *lov_lock_link_find(const struct lu_env *env,
+ struct lov_lock *lck,
+ struct lovsub_lock *sub);
+struct lov_io_sub *lov_page_subio(const struct lu_env *env,
+ struct lov_io *lio,
+ const struct cl_page_slice *slice);
+
+void lov_lsm_decref(struct lov_object *lov, struct lov_stripe_md *lsm);
+struct lov_stripe_md *lov_lsm_addref(struct lov_object *lov);
+
+#define lov_foreach_target(lov, var) \
+ for (var = 0; var < lov_targets_nr(lov); ++var)
+
+/*****************************************************************************
+ *
+ * Type conversions.
+ *
+ * Accessors.
+ *
+ */
+
+static inline struct lov_session *lov_env_session(const struct lu_env *env)
+{
+ struct lov_session *ses;
+
+ ses = lu_context_key_get(env->le_ses, &lov_session_key);
+ LASSERT(ses != NULL);
+ return ses;
+}
+
+static inline struct lov_io *lov_env_io(const struct lu_env *env)
+{
+ return &lov_env_session(env)->ls_io;
+}
+
+static inline int lov_is_object(const struct lu_object *obj)
+{
+ return obj->lo_dev->ld_type == &lov_device_type;
+}
+
+static inline int lovsub_is_object(const struct lu_object *obj)
+{
+ return obj->lo_dev->ld_type == &lovsub_device_type;
+}
+
+static inline struct lu_device *lov2lu_dev(struct lov_device *lov)
+{
+ return &lov->ld_cl.cd_lu_dev;
+}
+
+static inline struct lov_device *lu2lov_dev(const struct lu_device *d)
+{
+ LINVRNT(d->ld_type == &lov_device_type);
+ return container_of0(d, struct lov_device, ld_cl.cd_lu_dev);
+}
+
+static inline struct cl_device *lovsub2cl_dev(struct lovsub_device *lovsub)
+{
+ return &lovsub->acid_cl;
+}
+
+static inline struct lu_device *lovsub2lu_dev(struct lovsub_device *lovsub)
+{
+ return &lovsub2cl_dev(lovsub)->cd_lu_dev;
+}
+
+static inline struct lovsub_device *lu2lovsub_dev(const struct lu_device *d)
+{
+ LINVRNT(d->ld_type == &lovsub_device_type);
+ return container_of0(d, struct lovsub_device, acid_cl.cd_lu_dev);
+}
+
+static inline struct lovsub_device *cl2lovsub_dev(const struct cl_device *d)
+{
+ LINVRNT(d->cd_lu_dev.ld_type == &lovsub_device_type);
+ return container_of0(d, struct lovsub_device, acid_cl);
+}
+
+static inline struct lu_object *lov2lu(struct lov_object *lov)
+{
+ return &lov->lo_cl.co_lu;
+}
+
+static inline struct cl_object *lov2cl(struct lov_object *lov)
+{
+ return &lov->lo_cl;
+}
+
+static inline struct lov_object *lu2lov(const struct lu_object *obj)
+{
+ LINVRNT(lov_is_object(obj));
+ return container_of0(obj, struct lov_object, lo_cl.co_lu);
+}
+
+static inline struct lov_object *cl2lov(const struct cl_object *obj)
+{
+ LINVRNT(lov_is_object(&obj->co_lu));
+ return container_of0(obj, struct lov_object, lo_cl);
+}
+
+static inline struct lu_object *lovsub2lu(struct lovsub_object *los)
+{
+ return &los->lso_cl.co_lu;
+}
+
+static inline struct cl_object *lovsub2cl(struct lovsub_object *los)
+{
+ return &los->lso_cl;
+}
+
+static inline struct lovsub_object *cl2lovsub(const struct cl_object *obj)
+{
+ LINVRNT(lovsub_is_object(&obj->co_lu));
+ return container_of0(obj, struct lovsub_object, lso_cl);
+}
+
+static inline struct lovsub_object *lu2lovsub(const struct lu_object *obj)
+{
+ LINVRNT(lovsub_is_object(obj));
+ return container_of0(obj, struct lovsub_object, lso_cl.co_lu);
+}
+
+static inline struct lovsub_lock *
+cl2lovsub_lock(const struct cl_lock_slice *slice)
+{
+ LINVRNT(lovsub_is_object(&slice->cls_obj->co_lu));
+ return container_of(slice, struct lovsub_lock, lss_cl);
+}
+
+static inline struct lovsub_lock *cl2sub_lock(const struct cl_lock *lock)
+{
+ const struct cl_lock_slice *slice;
+
+ slice = cl_lock_at(lock, &lovsub_device_type);
+ LASSERT(slice != NULL);
+ return cl2lovsub_lock(slice);
+}
+
+static inline struct lov_lock *cl2lov_lock(const struct cl_lock_slice *slice)
+{
+ LINVRNT(lov_is_object(&slice->cls_obj->co_lu));
+ return container_of(slice, struct lov_lock, lls_cl);
+}
+
+static inline struct lov_page *cl2lov_page(const struct cl_page_slice *slice)
+{
+ LINVRNT(lov_is_object(&slice->cpl_obj->co_lu));
+ return container_of0(slice, struct lov_page, lps_cl);
+}
+
+static inline struct lov_req *cl2lov_req(const struct cl_req_slice *slice)
+{
+ return container_of0(slice, struct lov_req, lr_cl);
+}
+
+static inline struct lovsub_page *
+cl2lovsub_page(const struct cl_page_slice *slice)
+{
+ LINVRNT(lovsub_is_object(&slice->cpl_obj->co_lu));
+ return container_of0(slice, struct lovsub_page, lsb_cl);
+}
+
+static inline struct lovsub_req *cl2lovsub_req(const struct cl_req_slice *slice)
+{
+ return container_of0(slice, struct lovsub_req, lsrq_cl);
+}
+
+static inline struct cl_page *lov_sub_page(const struct cl_page_slice *slice)
+{
+ return slice->cpl_page->cp_child;
+}
+
+static inline struct lov_io *cl2lov_io(const struct lu_env *env,
+ const struct cl_io_slice *ios)
+{
+ struct lov_io *lio;
+
+ lio = container_of(ios, struct lov_io, lis_cl);
+ LASSERT(lio == lov_env_io(env));
+ return lio;
+}
+
+static inline int lov_targets_nr(const struct lov_device *lov)
+{
+ return lov->ld_lov->desc.ld_tgt_count;
+}
+
+static inline struct lov_thread_info *lov_env_info(const struct lu_env *env)
+{
+ struct lov_thread_info *info;
+
+ info = lu_context_key_get(&env->le_ctx, &lov_key);
+ LASSERT(info != NULL);
+ return info;
+}
+
+static inline struct lov_layout_raid0 *lov_r0(struct lov_object *lov)
+{
+ LASSERT(lov->lo_type == LLT_RAID0);
+ LASSERT(lov->lo_lsm->lsm_wire.lw_magic == LOV_MAGIC ||
+ lov->lo_lsm->lsm_wire.lw_magic == LOV_MAGIC_V3);
+ return &lov->u.raid0;
+}
+
+/** @} lov */
+
+#endif
diff --git a/drivers/staging/lustre/lustre/lov/lov_dev.c b/drivers/staging/lustre/lustre/lov/lov_dev.c
new file mode 100644
index 000000000..711b837dd
--- /dev/null
+++ b/drivers/staging/lustre/lustre/lov/lov_dev.c
@@ -0,0 +1,528 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2012, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ *
+ * Implementation of cl_device and cl_device_type for LOV layer.
+ *
+ * Author: Nikita Danilov <nikita.danilov@sun.com>
+ */
+
+#define DEBUG_SUBSYSTEM S_LOV
+
+/* class_name2obd() */
+#include "../include/obd_class.h"
+
+#include "lov_cl_internal.h"
+#include "lov_internal.h"
+
+
+struct kmem_cache *lov_lock_kmem;
+struct kmem_cache *lov_object_kmem;
+struct kmem_cache *lov_thread_kmem;
+struct kmem_cache *lov_session_kmem;
+struct kmem_cache *lov_req_kmem;
+
+struct kmem_cache *lovsub_lock_kmem;
+struct kmem_cache *lovsub_object_kmem;
+struct kmem_cache *lovsub_req_kmem;
+
+struct kmem_cache *lov_lock_link_kmem;
+
+/** Lock class of lov_device::ld_mutex. */
+static struct lock_class_key cl_lov_device_mutex_class;
+
+struct lu_kmem_descr lov_caches[] = {
+ {
+ .ckd_cache = &lov_lock_kmem,
+ .ckd_name = "lov_lock_kmem",
+ .ckd_size = sizeof(struct lov_lock)
+ },
+ {
+ .ckd_cache = &lov_object_kmem,
+ .ckd_name = "lov_object_kmem",
+ .ckd_size = sizeof(struct lov_object)
+ },
+ {
+ .ckd_cache = &lov_thread_kmem,
+ .ckd_name = "lov_thread_kmem",
+ .ckd_size = sizeof(struct lov_thread_info)
+ },
+ {
+ .ckd_cache = &lov_session_kmem,
+ .ckd_name = "lov_session_kmem",
+ .ckd_size = sizeof(struct lov_session)
+ },
+ {
+ .ckd_cache = &lov_req_kmem,
+ .ckd_name = "lov_req_kmem",
+ .ckd_size = sizeof(struct lov_req)
+ },
+ {
+ .ckd_cache = &lovsub_lock_kmem,
+ .ckd_name = "lovsub_lock_kmem",
+ .ckd_size = sizeof(struct lovsub_lock)
+ },
+ {
+ .ckd_cache = &lovsub_object_kmem,
+ .ckd_name = "lovsub_object_kmem",
+ .ckd_size = sizeof(struct lovsub_object)
+ },
+ {
+ .ckd_cache = &lovsub_req_kmem,
+ .ckd_name = "lovsub_req_kmem",
+ .ckd_size = sizeof(struct lovsub_req)
+ },
+ {
+ .ckd_cache = &lov_lock_link_kmem,
+ .ckd_name = "lov_lock_link_kmem",
+ .ckd_size = sizeof(struct lov_lock_link)
+ },
+ {
+ .ckd_cache = NULL
+ }
+};
+
+/*****************************************************************************
+ *
+ * Lov transfer operations.
+ *
+ */
+
+static void lov_req_completion(const struct lu_env *env,
+ const struct cl_req_slice *slice, int ioret)
+{
+ struct lov_req *lr;
+
+ lr = cl2lov_req(slice);
+ OBD_SLAB_FREE_PTR(lr, lov_req_kmem);
+}
+
+static const struct cl_req_operations lov_req_ops = {
+ .cro_completion = lov_req_completion
+};
+
+/*****************************************************************************
+ *
+ * Lov device and device type functions.
+ *
+ */
+
+static void *lov_key_init(const struct lu_context *ctx,
+ struct lu_context_key *key)
+{
+ struct lov_thread_info *info;
+
+ OBD_SLAB_ALLOC_PTR_GFP(info, lov_thread_kmem, GFP_NOFS);
+ if (info != NULL)
+ INIT_LIST_HEAD(&info->lti_closure.clc_list);
+ else
+ info = ERR_PTR(-ENOMEM);
+ return info;
+}
+
+static void lov_key_fini(const struct lu_context *ctx,
+ struct lu_context_key *key, void *data)
+{
+ struct lov_thread_info *info = data;
+ LINVRNT(list_empty(&info->lti_closure.clc_list));
+ OBD_SLAB_FREE_PTR(info, lov_thread_kmem);
+}
+
+struct lu_context_key lov_key = {
+ .lct_tags = LCT_CL_THREAD,
+ .lct_init = lov_key_init,
+ .lct_fini = lov_key_fini
+};
+
+static void *lov_session_key_init(const struct lu_context *ctx,
+ struct lu_context_key *key)
+{
+ struct lov_session *info;
+
+ OBD_SLAB_ALLOC_PTR_GFP(info, lov_session_kmem, GFP_NOFS);
+ if (info == NULL)
+ info = ERR_PTR(-ENOMEM);
+ return info;
+}
+
+static void lov_session_key_fini(const struct lu_context *ctx,
+ struct lu_context_key *key, void *data)
+{
+ struct lov_session *info = data;
+ OBD_SLAB_FREE_PTR(info, lov_session_kmem);
+}
+
+struct lu_context_key lov_session_key = {
+ .lct_tags = LCT_SESSION,
+ .lct_init = lov_session_key_init,
+ .lct_fini = lov_session_key_fini
+};
+
+/* type constructor/destructor: lov_type_{init,fini,start,stop}() */
+LU_TYPE_INIT_FINI(lov, &lov_key, &lov_session_key);
+
+static struct lu_device *lov_device_fini(const struct lu_env *env,
+ struct lu_device *d)
+{
+ int i;
+ struct lov_device *ld = lu2lov_dev(d);
+
+ LASSERT(ld->ld_lov != NULL);
+ if (ld->ld_target == NULL)
+ return NULL;
+
+ lov_foreach_target(ld, i) {
+ struct lovsub_device *lsd;
+
+ lsd = ld->ld_target[i];
+ if (lsd != NULL) {
+ cl_stack_fini(env, lovsub2cl_dev(lsd));
+ ld->ld_target[i] = NULL;
+ }
+ }
+ return NULL;
+}
+
+static int lov_device_init(const struct lu_env *env, struct lu_device *d,
+ const char *name, struct lu_device *next)
+{
+ struct lov_device *ld = lu2lov_dev(d);
+ int i;
+ int rc = 0;
+
+ LASSERT(d->ld_site != NULL);
+ if (ld->ld_target == NULL)
+ return rc;
+
+ lov_foreach_target(ld, i) {
+ struct lovsub_device *lsd;
+ struct cl_device *cl;
+ struct lov_tgt_desc *desc;
+
+ desc = ld->ld_lov->lov_tgts[i];
+ if (desc == NULL)
+ continue;
+
+ cl = cl_type_setup(env, d->ld_site, &lovsub_device_type,
+ desc->ltd_obd->obd_lu_dev);
+ if (IS_ERR(cl)) {
+ rc = PTR_ERR(cl);
+ break;
+ }
+ lsd = cl2lovsub_dev(cl);
+ lsd->acid_idx = i;
+ lsd->acid_super = ld;
+ ld->ld_target[i] = lsd;
+ }
+
+ if (rc)
+ lov_device_fini(env, d);
+ else
+ ld->ld_flags |= LOV_DEV_INITIALIZED;
+
+ return rc;
+}
+
+static int lov_req_init(const struct lu_env *env, struct cl_device *dev,
+ struct cl_req *req)
+{
+ struct lov_req *lr;
+ int result;
+
+ OBD_SLAB_ALLOC_PTR_GFP(lr, lov_req_kmem, GFP_NOFS);
+ if (lr != NULL) {
+ cl_req_slice_add(req, &lr->lr_cl, dev, &lov_req_ops);
+ result = 0;
+ } else
+ result = -ENOMEM;
+ return result;
+}
+
+static const struct cl_device_operations lov_cl_ops = {
+ .cdo_req_init = lov_req_init
+};
+
+static void lov_emerg_free(struct lov_device_emerg **emrg, int nr)
+{
+ int i;
+
+ for (i = 0; i < nr; ++i) {
+ struct lov_device_emerg *em;
+
+ em = emrg[i];
+ if (em != NULL) {
+ LASSERT(em->emrg_page_list.pl_nr == 0);
+ if (em->emrg_env != NULL)
+ cl_env_put(em->emrg_env, &em->emrg_refcheck);
+ OBD_FREE_PTR(em);
+ }
+ }
+ OBD_FREE(emrg, nr * sizeof(emrg[0]));
+}
+
+static struct lu_device *lov_device_free(const struct lu_env *env,
+ struct lu_device *d)
+{
+ struct lov_device *ld = lu2lov_dev(d);
+ const int nr = ld->ld_target_nr;
+
+ cl_device_fini(lu2cl_dev(d));
+ if (ld->ld_target != NULL)
+ OBD_FREE(ld->ld_target, nr * sizeof(ld->ld_target[0]));
+ if (ld->ld_emrg != NULL)
+ lov_emerg_free(ld->ld_emrg, nr);
+ OBD_FREE_PTR(ld);
+ return NULL;
+}
+
+static void lov_cl_del_target(const struct lu_env *env, struct lu_device *dev,
+ __u32 index)
+{
+ struct lov_device *ld = lu2lov_dev(dev);
+
+ if (ld->ld_target[index] != NULL) {
+ cl_stack_fini(env, lovsub2cl_dev(ld->ld_target[index]));
+ ld->ld_target[index] = NULL;
+ }
+}
+
+static struct lov_device_emerg **lov_emerg_alloc(int nr)
+{
+ struct lov_device_emerg **emerg;
+ int i;
+ int result;
+
+ OBD_ALLOC(emerg, nr * sizeof(emerg[0]));
+ if (emerg == NULL)
+ return ERR_PTR(-ENOMEM);
+ for (result = i = 0; i < nr && result == 0; i++) {
+ struct lov_device_emerg *em;
+
+ OBD_ALLOC_PTR(em);
+ if (em != NULL) {
+ emerg[i] = em;
+ cl_page_list_init(&em->emrg_page_list);
+ em->emrg_env = cl_env_alloc(&em->emrg_refcheck,
+ LCT_REMEMBER|LCT_NOREF);
+ if (!IS_ERR(em->emrg_env))
+ em->emrg_env->le_ctx.lc_cookie = 0x2;
+ else {
+ result = PTR_ERR(em->emrg_env);
+ em->emrg_env = NULL;
+ }
+ } else
+ result = -ENOMEM;
+ }
+ if (result != 0) {
+ lov_emerg_free(emerg, nr);
+ emerg = ERR_PTR(result);
+ }
+ return emerg;
+}
+
+static int lov_expand_targets(const struct lu_env *env, struct lov_device *dev)
+{
+ int result;
+ __u32 tgt_size;
+ __u32 sub_size;
+
+ result = 0;
+ tgt_size = dev->ld_lov->lov_tgt_size;
+ sub_size = dev->ld_target_nr;
+ if (sub_size < tgt_size) {
+ struct lovsub_device **newd;
+ struct lov_device_emerg **emerg;
+ const size_t sz = sizeof(newd[0]);
+
+ emerg = lov_emerg_alloc(tgt_size);
+ if (IS_ERR(emerg))
+ return PTR_ERR(emerg);
+
+ OBD_ALLOC(newd, tgt_size * sz);
+ if (newd != NULL) {
+ mutex_lock(&dev->ld_mutex);
+ if (sub_size > 0) {
+ memcpy(newd, dev->ld_target, sub_size * sz);
+ OBD_FREE(dev->ld_target, sub_size * sz);
+ }
+ dev->ld_target = newd;
+ dev->ld_target_nr = tgt_size;
+
+ if (dev->ld_emrg != NULL)
+ lov_emerg_free(dev->ld_emrg, sub_size);
+ dev->ld_emrg = emerg;
+ mutex_unlock(&dev->ld_mutex);
+ } else {
+ lov_emerg_free(emerg, tgt_size);
+ result = -ENOMEM;
+ }
+ }
+ return result;
+}
+
+static int lov_cl_add_target(const struct lu_env *env, struct lu_device *dev,
+ __u32 index)
+{
+ struct obd_device *obd = dev->ld_obd;
+ struct lov_device *ld = lu2lov_dev(dev);
+ struct lov_tgt_desc *tgt;
+ struct lovsub_device *lsd;
+ struct cl_device *cl;
+ int rc;
+
+ obd_getref(obd);
+
+ tgt = obd->u.lov.lov_tgts[index];
+ LASSERT(tgt != NULL);
+ LASSERT(tgt->ltd_obd != NULL);
+
+ if (!tgt->ltd_obd->obd_set_up) {
+ CERROR("Target %s not set up\n", obd_uuid2str(&tgt->ltd_uuid));
+ return -EINVAL;
+ }
+
+ rc = lov_expand_targets(env, ld);
+ if (rc == 0 && ld->ld_flags & LOV_DEV_INITIALIZED) {
+ LASSERT(dev->ld_site != NULL);
+
+ cl = cl_type_setup(env, dev->ld_site, &lovsub_device_type,
+ tgt->ltd_obd->obd_lu_dev);
+ if (!IS_ERR(cl)) {
+ lsd = cl2lovsub_dev(cl);
+ lsd->acid_idx = index;
+ lsd->acid_super = ld;
+ ld->ld_target[index] = lsd;
+ } else {
+ CERROR("add failed (%d), deleting %s\n", rc,
+ obd_uuid2str(&tgt->ltd_uuid));
+ lov_cl_del_target(env, dev, index);
+ rc = PTR_ERR(cl);
+ }
+ }
+ obd_putref(obd);
+ return rc;
+}
+
+static int lov_process_config(const struct lu_env *env,
+ struct lu_device *d, struct lustre_cfg *cfg)
+{
+ struct obd_device *obd = d->ld_obd;
+ int cmd;
+ int rc;
+ int gen;
+ __u32 index;
+
+ obd_getref(obd);
+
+ cmd = cfg->lcfg_command;
+ rc = lov_process_config_base(d->ld_obd, cfg, &index, &gen);
+ if (rc == 0) {
+ switch (cmd) {
+ case LCFG_LOV_ADD_OBD:
+ case LCFG_LOV_ADD_INA:
+ rc = lov_cl_add_target(env, d, index);
+ if (rc != 0)
+ lov_del_target(d->ld_obd, index, NULL, 0);
+ break;
+ case LCFG_LOV_DEL_OBD:
+ lov_cl_del_target(env, d, index);
+ break;
+ }
+ }
+ obd_putref(obd);
+ return rc;
+}
+
+static const struct lu_device_operations lov_lu_ops = {
+ .ldo_object_alloc = lov_object_alloc,
+ .ldo_process_config = lov_process_config,
+};
+
+static struct lu_device *lov_device_alloc(const struct lu_env *env,
+ struct lu_device_type *t,
+ struct lustre_cfg *cfg)
+{
+ struct lu_device *d;
+ struct lov_device *ld;
+ struct obd_device *obd;
+ int rc;
+
+ OBD_ALLOC_PTR(ld);
+ if (ld == NULL)
+ return ERR_PTR(-ENOMEM);
+
+ cl_device_init(&ld->ld_cl, t);
+ d = lov2lu_dev(ld);
+ d->ld_ops = &lov_lu_ops;
+ ld->ld_cl.cd_ops = &lov_cl_ops;
+
+ mutex_init(&ld->ld_mutex);
+ lockdep_set_class(&ld->ld_mutex, &cl_lov_device_mutex_class);
+
+ /* setup the LOV OBD */
+ obd = class_name2obd(lustre_cfg_string(cfg, 0));
+ LASSERT(obd != NULL);
+ rc = lov_setup(obd, cfg);
+ if (rc) {
+ lov_device_free(env, d);
+ return ERR_PTR(rc);
+ }
+
+ ld->ld_lov = &obd->u.lov;
+ return d;
+}
+
+static const struct lu_device_type_operations lov_device_type_ops = {
+ .ldto_init = lov_type_init,
+ .ldto_fini = lov_type_fini,
+
+ .ldto_start = lov_type_start,
+ .ldto_stop = lov_type_stop,
+
+ .ldto_device_alloc = lov_device_alloc,
+ .ldto_device_free = lov_device_free,
+
+ .ldto_device_init = lov_device_init,
+ .ldto_device_fini = lov_device_fini
+};
+
+struct lu_device_type lov_device_type = {
+ .ldt_tags = LU_DEVICE_CL,
+ .ldt_name = LUSTRE_LOV_NAME,
+ .ldt_ops = &lov_device_type_ops,
+ .ldt_ctx_tags = LCT_CL_THREAD
+};
+EXPORT_SYMBOL(lov_device_type);
+
+/** @} lov */
diff --git a/drivers/staging/lustre/lustre/lov/lov_ea.c b/drivers/staging/lustre/lustre/lov/lov_ea.c
new file mode 100644
index 000000000..2bcfaeaff
--- /dev/null
+++ b/drivers/staging/lustre/lustre/lov/lov_ea.c
@@ -0,0 +1,363 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2011, 2012, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ *
+ * lustre/lov/lov_ea.c
+ *
+ * Author: Wang Di <wangdi@clusterfs.com>
+ */
+
+#define DEBUG_SUBSYSTEM S_LOV
+
+#include <asm/div64.h>
+#include "../../include/linux/libcfs/libcfs.h"
+
+#include "../include/obd_class.h"
+#include "../include/lustre/lustre_idl.h"
+
+#include "lov_internal.h"
+
+struct lovea_unpack_args {
+ struct lov_stripe_md *lsm;
+ int cursor;
+};
+
+static int lsm_lmm_verify_common(struct lov_mds_md *lmm, int lmm_bytes,
+ __u16 stripe_count)
+{
+ if (stripe_count > LOV_V1_INSANE_STRIPE_COUNT) {
+ CERROR("bad stripe count %d\n", stripe_count);
+ lov_dump_lmm_common(D_WARNING, lmm);
+ return -EINVAL;
+ }
+
+ if (lmm_oi_id(&lmm->lmm_oi) == 0) {
+ CERROR("zero object id\n");
+ lov_dump_lmm_common(D_WARNING, lmm);
+ return -EINVAL;
+ }
+
+ if (lov_pattern(le32_to_cpu(lmm->lmm_pattern)) != LOV_PATTERN_RAID0) {
+ CERROR("bad striping pattern\n");
+ lov_dump_lmm_common(D_WARNING, lmm);
+ return -EINVAL;
+ }
+
+ if (lmm->lmm_stripe_size == 0 ||
+ (le32_to_cpu(lmm->lmm_stripe_size)&(LOV_MIN_STRIPE_SIZE-1)) != 0) {
+ CERROR("bad stripe size %u\n",
+ le32_to_cpu(lmm->lmm_stripe_size));
+ lov_dump_lmm_common(D_WARNING, lmm);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+struct lov_stripe_md *lsm_alloc_plain(__u16 stripe_count, int *size)
+{
+ struct lov_stripe_md *lsm;
+ struct lov_oinfo *loi;
+ int i, oinfo_ptrs_size;
+
+ LASSERT(stripe_count <= LOV_MAX_STRIPE_COUNT);
+
+ oinfo_ptrs_size = sizeof(struct lov_oinfo *) * stripe_count;
+ *size = sizeof(struct lov_stripe_md) + oinfo_ptrs_size;
+
+ OBD_ALLOC_LARGE(lsm, *size);
+ if (!lsm)
+ return NULL;
+
+ for (i = 0; i < stripe_count; i++) {
+ OBD_SLAB_ALLOC_PTR_GFP(loi, lov_oinfo_slab, GFP_NOFS);
+ if (loi == NULL)
+ goto err;
+ lsm->lsm_oinfo[i] = loi;
+ }
+ lsm->lsm_stripe_count = stripe_count;
+ return lsm;
+
+err:
+ while (--i >= 0)
+ OBD_SLAB_FREE(lsm->lsm_oinfo[i], lov_oinfo_slab, sizeof(*loi));
+ OBD_FREE_LARGE(lsm, *size);
+ return NULL;
+}
+
+void lsm_free_plain(struct lov_stripe_md *lsm)
+{
+ __u16 stripe_count = lsm->lsm_stripe_count;
+ int i;
+
+ for (i = 0; i < stripe_count; i++)
+ OBD_SLAB_FREE(lsm->lsm_oinfo[i], lov_oinfo_slab,
+ sizeof(struct lov_oinfo));
+ OBD_FREE_LARGE(lsm, sizeof(struct lov_stripe_md) +
+ stripe_count * sizeof(struct lov_oinfo *));
+}
+
+static void lsm_unpackmd_common(struct lov_stripe_md *lsm,
+ struct lov_mds_md *lmm)
+{
+ /*
+ * This supposes lov_mds_md_v1/v3 first fields are
+ * are the same
+ */
+ lmm_oi_le_to_cpu(&lsm->lsm_oi, &lmm->lmm_oi);
+ lsm->lsm_stripe_size = le32_to_cpu(lmm->lmm_stripe_size);
+ lsm->lsm_pattern = le32_to_cpu(lmm->lmm_pattern);
+ lsm->lsm_layout_gen = le16_to_cpu(lmm->lmm_layout_gen);
+ lsm->lsm_pool_name[0] = '\0';
+}
+
+static void
+lsm_stripe_by_index_plain(struct lov_stripe_md *lsm, int *stripeno,
+ u64 *lov_off, u64 *swidth)
+{
+ if (swidth)
+ *swidth = (u64)lsm->lsm_stripe_size * lsm->lsm_stripe_count;
+}
+
+static void
+lsm_stripe_by_offset_plain(struct lov_stripe_md *lsm, int *stripeno,
+ u64 *lov_off, u64 *swidth)
+{
+ if (swidth)
+ *swidth = (u64)lsm->lsm_stripe_size * lsm->lsm_stripe_count;
+}
+
+static int lsm_destroy_plain(struct lov_stripe_md *lsm, struct obdo *oa,
+ struct obd_export *md_exp)
+{
+ return 0;
+}
+
+/* Find minimum stripe maxbytes value. For inactive or
+ * reconnecting targets use LUSTRE_STRIPE_MAXBYTES. */
+static void lov_tgt_maxbytes(struct lov_tgt_desc *tgt, __u64 *stripe_maxbytes)
+{
+ struct obd_import *imp = tgt->ltd_obd->u.cli.cl_import;
+
+ if (imp == NULL || !tgt->ltd_active) {
+ *stripe_maxbytes = LUSTRE_STRIPE_MAXBYTES;
+ return;
+ }
+
+ spin_lock(&imp->imp_lock);
+ if (imp->imp_state == LUSTRE_IMP_FULL &&
+ (imp->imp_connect_data.ocd_connect_flags & OBD_CONNECT_MAXBYTES) &&
+ imp->imp_connect_data.ocd_maxbytes > 0) {
+ if (*stripe_maxbytes > imp->imp_connect_data.ocd_maxbytes)
+ *stripe_maxbytes = imp->imp_connect_data.ocd_maxbytes;
+ } else {
+ *stripe_maxbytes = LUSTRE_STRIPE_MAXBYTES;
+ }
+ spin_unlock(&imp->imp_lock);
+}
+
+static int lsm_lmm_verify_v1(struct lov_mds_md_v1 *lmm, int lmm_bytes,
+ __u16 *stripe_count)
+{
+ if (lmm_bytes < sizeof(*lmm)) {
+ CERROR("lov_mds_md_v1 too small: %d, need at least %d\n",
+ lmm_bytes, (int)sizeof(*lmm));
+ return -EINVAL;
+ }
+
+ *stripe_count = le16_to_cpu(lmm->lmm_stripe_count);
+ if (le32_to_cpu(lmm->lmm_pattern) & LOV_PATTERN_F_RELEASED)
+ *stripe_count = 0;
+
+ if (lmm_bytes < lov_mds_md_size(*stripe_count, LOV_MAGIC_V1)) {
+ CERROR("LOV EA V1 too small: %d, need %d\n",
+ lmm_bytes, lov_mds_md_size(*stripe_count, LOV_MAGIC_V1));
+ lov_dump_lmm_common(D_WARNING, lmm);
+ return -EINVAL;
+ }
+
+ return lsm_lmm_verify_common(lmm, lmm_bytes, *stripe_count);
+}
+
+static int lsm_unpackmd_v1(struct lov_obd *lov, struct lov_stripe_md *lsm,
+ struct lov_mds_md_v1 *lmm)
+{
+ struct lov_oinfo *loi;
+ int i;
+ int stripe_count;
+ __u64 stripe_maxbytes = OBD_OBJECT_EOF;
+
+ lsm_unpackmd_common(lsm, lmm);
+
+ stripe_count = lsm_is_released(lsm) ? 0 : lsm->lsm_stripe_count;
+
+ for (i = 0; i < stripe_count; i++) {
+ /* XXX LOV STACKING call down to osc_unpackmd() */
+ loi = lsm->lsm_oinfo[i];
+ ostid_le_to_cpu(&lmm->lmm_objects[i].l_ost_oi, &loi->loi_oi);
+ loi->loi_ost_idx = le32_to_cpu(lmm->lmm_objects[i].l_ost_idx);
+ loi->loi_ost_gen = le32_to_cpu(lmm->lmm_objects[i].l_ost_gen);
+ if (lov_oinfo_is_dummy(loi))
+ continue;
+
+ if (loi->loi_ost_idx >= lov->desc.ld_tgt_count) {
+ CERROR("OST index %d more than OST count %d\n",
+ loi->loi_ost_idx, lov->desc.ld_tgt_count);
+ lov_dump_lmm_v1(D_WARNING, lmm);
+ return -EINVAL;
+ }
+ if (!lov->lov_tgts[loi->loi_ost_idx]) {
+ CERROR("OST index %d missing\n", loi->loi_ost_idx);
+ lov_dump_lmm_v1(D_WARNING, lmm);
+ return -EINVAL;
+ }
+ /* calculate the minimum stripe max bytes */
+ lov_tgt_maxbytes(lov->lov_tgts[loi->loi_ost_idx],
+ &stripe_maxbytes);
+ }
+
+ lsm->lsm_maxbytes = stripe_maxbytes * lsm->lsm_stripe_count;
+ if (lsm->lsm_stripe_count == 0)
+ lsm->lsm_maxbytes = stripe_maxbytes * lov->desc.ld_tgt_count;
+
+ return 0;
+}
+
+const struct lsm_operations lsm_v1_ops = {
+ .lsm_free = lsm_free_plain,
+ .lsm_destroy = lsm_destroy_plain,
+ .lsm_stripe_by_index = lsm_stripe_by_index_plain,
+ .lsm_stripe_by_offset = lsm_stripe_by_offset_plain,
+ .lsm_lmm_verify = lsm_lmm_verify_v1,
+ .lsm_unpackmd = lsm_unpackmd_v1,
+};
+
+static int lsm_lmm_verify_v3(struct lov_mds_md *lmmv1, int lmm_bytes,
+ __u16 *stripe_count)
+{
+ struct lov_mds_md_v3 *lmm;
+
+ lmm = (struct lov_mds_md_v3 *)lmmv1;
+
+ if (lmm_bytes < sizeof(*lmm)) {
+ CERROR("lov_mds_md_v3 too small: %d, need at least %d\n",
+ lmm_bytes, (int)sizeof(*lmm));
+ return -EINVAL;
+ }
+
+ *stripe_count = le16_to_cpu(lmm->lmm_stripe_count);
+ if (le32_to_cpu(lmm->lmm_pattern) & LOV_PATTERN_F_RELEASED)
+ *stripe_count = 0;
+
+ if (lmm_bytes < lov_mds_md_size(*stripe_count, LOV_MAGIC_V3)) {
+ CERROR("LOV EA V3 too small: %d, need %d\n",
+ lmm_bytes, lov_mds_md_size(*stripe_count, LOV_MAGIC_V3));
+ lov_dump_lmm_common(D_WARNING, lmm);
+ return -EINVAL;
+ }
+
+ return lsm_lmm_verify_common((struct lov_mds_md_v1 *)lmm, lmm_bytes,
+ *stripe_count);
+}
+
+static int lsm_unpackmd_v3(struct lov_obd *lov, struct lov_stripe_md *lsm,
+ struct lov_mds_md *lmmv1)
+{
+ struct lov_mds_md_v3 *lmm;
+ struct lov_oinfo *loi;
+ int i;
+ int stripe_count;
+ __u64 stripe_maxbytes = OBD_OBJECT_EOF;
+ int cplen = 0;
+
+ lmm = (struct lov_mds_md_v3 *)lmmv1;
+
+ lsm_unpackmd_common(lsm, (struct lov_mds_md_v1 *)lmm);
+
+ stripe_count = lsm_is_released(lsm) ? 0 : lsm->lsm_stripe_count;
+
+ cplen = strlcpy(lsm->lsm_pool_name, lmm->lmm_pool_name,
+ sizeof(lsm->lsm_pool_name));
+ if (cplen >= sizeof(lsm->lsm_pool_name))
+ return -E2BIG;
+
+ for (i = 0; i < stripe_count; i++) {
+ /* XXX LOV STACKING call down to osc_unpackmd() */
+ loi = lsm->lsm_oinfo[i];
+ ostid_le_to_cpu(&lmm->lmm_objects[i].l_ost_oi, &loi->loi_oi);
+ loi->loi_ost_idx = le32_to_cpu(lmm->lmm_objects[i].l_ost_idx);
+ loi->loi_ost_gen = le32_to_cpu(lmm->lmm_objects[i].l_ost_gen);
+ if (lov_oinfo_is_dummy(loi))
+ continue;
+
+ if (loi->loi_ost_idx >= lov->desc.ld_tgt_count) {
+ CERROR("OST index %d more than OST count %d\n",
+ loi->loi_ost_idx, lov->desc.ld_tgt_count);
+ lov_dump_lmm_v3(D_WARNING, lmm);
+ return -EINVAL;
+ }
+ if (!lov->lov_tgts[loi->loi_ost_idx]) {
+ CERROR("OST index %d missing\n", loi->loi_ost_idx);
+ lov_dump_lmm_v3(D_WARNING, lmm);
+ return -EINVAL;
+ }
+ /* calculate the minimum stripe max bytes */
+ lov_tgt_maxbytes(lov->lov_tgts[loi->loi_ost_idx],
+ &stripe_maxbytes);
+ }
+
+ lsm->lsm_maxbytes = stripe_maxbytes * lsm->lsm_stripe_count;
+ if (lsm->lsm_stripe_count == 0)
+ lsm->lsm_maxbytes = stripe_maxbytes * lov->desc.ld_tgt_count;
+
+ return 0;
+}
+
+const struct lsm_operations lsm_v3_ops = {
+ .lsm_free = lsm_free_plain,
+ .lsm_destroy = lsm_destroy_plain,
+ .lsm_stripe_by_index = lsm_stripe_by_index_plain,
+ .lsm_stripe_by_offset = lsm_stripe_by_offset_plain,
+ .lsm_lmm_verify = lsm_lmm_verify_v3,
+ .lsm_unpackmd = lsm_unpackmd_v3,
+};
+
+void dump_lsm(unsigned int level, const struct lov_stripe_md *lsm)
+{
+ CDEBUG(level, "lsm %p, objid " DOSTID ", maxbytes %#llx, magic 0x%08X, stripe_size %u, stripe_count %u, refc: %d, layout_gen %u, pool [" LOV_POOLNAMEF "]\n",
+ lsm,
+ POSTID(&lsm->lsm_oi), lsm->lsm_maxbytes, lsm->lsm_magic,
+ lsm->lsm_stripe_size, lsm->lsm_stripe_count,
+ atomic_read(&lsm->lsm_refc), lsm->lsm_layout_gen,
+ lsm->lsm_pool_name);
+}
diff --git a/drivers/staging/lustre/lustre/lov/lov_internal.h b/drivers/staging/lustre/lustre/lov/lov_internal.h
new file mode 100644
index 000000000..b644acc9b
--- /dev/null
+++ b/drivers/staging/lustre/lustre/lov/lov_internal.h
@@ -0,0 +1,319 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2011, 2012, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ */
+
+#ifndef LOV_INTERNAL_H
+#define LOV_INTERNAL_H
+
+#include "../include/obd_class.h"
+#include "../include/lustre/lustre_user.h"
+
+/* lov_do_div64(a, b) returns a % b, and a = a / b.
+ * The 32-bit code is LOV-specific due to knowing about stripe limits in
+ * order to reduce the divisor to a 32-bit number. If the divisor is
+ * already a 32-bit value the compiler handles this directly. */
+#if BITS_PER_LONG == 64
+# define lov_do_div64(n, base) ({ \
+ uint64_t __base = (base); \
+ uint64_t __rem; \
+ __rem = ((uint64_t)(n)) % __base; \
+ (n) = ((uint64_t)(n)) / __base; \
+ __rem; \
+})
+#elif BITS_PER_LONG == 32
+# define lov_do_div64(n, base) ({ \
+ uint64_t __rem; \
+ if ((sizeof(base) > 4) && (((base) & 0xffffffff00000000ULL) != 0)) { \
+ int __remainder; \
+ LASSERTF(!((base) & (LOV_MIN_STRIPE_SIZE - 1)), "64 bit lov " \
+ "division %llu / %llu\n", (n), (uint64_t)(base)); \
+ __remainder = (n) & (LOV_MIN_STRIPE_SIZE - 1); \
+ (n) >>= LOV_MIN_STRIPE_BITS; \
+ __rem = do_div(n, (base) >> LOV_MIN_STRIPE_BITS); \
+ __rem <<= LOV_MIN_STRIPE_BITS; \
+ __rem += __remainder; \
+ } else { \
+ __rem = do_div(n, base); \
+ } \
+ __rem; \
+})
+#endif
+
+struct lov_lock_handles {
+ struct portals_handle llh_handle;
+ atomic_t llh_refcount;
+ int llh_stripe_count;
+ struct lustre_handle llh_handles[0];
+};
+
+struct lov_request {
+ struct obd_info rq_oi;
+ struct lov_request_set *rq_rqset;
+
+ struct list_head rq_link;
+
+ int rq_idx; /* index in lov->tgts array */
+ int rq_stripe; /* stripe number */
+ int rq_complete;
+ int rq_rc;
+ int rq_buflen; /* length of sub_md */
+
+ u32 rq_oabufs;
+ u32 rq_pgaidx;
+};
+
+struct lov_request_set {
+ struct ldlm_enqueue_info *set_ei;
+ struct obd_info *set_oi;
+ atomic_t set_refcount;
+ struct obd_export *set_exp;
+ /* XXX: There is @set_exp already, however obd_statfs gets obd_device
+ only. */
+ struct obd_device *set_obd;
+ int set_count;
+ atomic_t set_completes;
+ atomic_t set_success;
+ atomic_t set_finish_checked;
+ struct llog_cookie *set_cookies;
+ int set_cookie_sent;
+ struct obd_trans_info *set_oti;
+ u32 set_oabufs;
+ struct brw_page *set_pga;
+ struct lov_lock_handles *set_lockh;
+ struct list_head set_list;
+ wait_queue_head_t set_waitq;
+ spinlock_t set_lock;
+};
+
+extern struct kmem_cache *lov_oinfo_slab;
+
+extern struct lu_kmem_descr lov_caches[];
+
+void lov_finish_set(struct lov_request_set *set);
+
+static inline void lov_get_reqset(struct lov_request_set *set)
+{
+ LASSERT(set != NULL);
+ LASSERT(atomic_read(&set->set_refcount) > 0);
+ atomic_inc(&set->set_refcount);
+}
+
+static inline void lov_put_reqset(struct lov_request_set *set)
+{
+ if (atomic_dec_and_test(&set->set_refcount))
+ lov_finish_set(set);
+}
+
+static inline struct lov_lock_handles *
+lov_handle2llh(struct lustre_handle *handle)
+{
+ LASSERT(handle != NULL);
+ return class_handle2object(handle->cookie);
+}
+
+static inline void lov_llh_put(struct lov_lock_handles *llh)
+{
+ CDEBUG(D_INFO, "PUTting llh %p : new refcount %d\n", llh,
+ atomic_read(&llh->llh_refcount) - 1);
+ LASSERT(atomic_read(&llh->llh_refcount) > 0 &&
+ atomic_read(&llh->llh_refcount) < 0x5a5a);
+ if (atomic_dec_and_test(&llh->llh_refcount)) {
+ class_handle_unhash(&llh->llh_handle);
+ /* The structure may be held by other threads because RCU.
+ * -jxiong */
+ if (atomic_read(&llh->llh_refcount))
+ return;
+
+ OBD_FREE_RCU(llh, sizeof(*llh) +
+ sizeof(*llh->llh_handles) * llh->llh_stripe_count,
+ &llh->llh_handle);
+ }
+}
+
+#define lov_uuid2str(lv, index) \
+ (char *)((lv)->lov_tgts[index]->ltd_uuid.uuid)
+
+/* lov_merge.c */
+void lov_merge_attrs(struct obdo *tgt, struct obdo *src, u64 valid,
+ struct lov_stripe_md *lsm, int stripeno, int *set);
+int lov_adjust_kms(struct obd_export *exp, struct lov_stripe_md *lsm,
+ u64 size, int shrink);
+int lov_merge_lvb_kms(struct lov_stripe_md *lsm,
+ struct ost_lvb *lvb, __u64 *kms_place);
+
+/* lov_offset.c */
+u64 lov_stripe_size(struct lov_stripe_md *lsm, u64 ost_size,
+ int stripeno);
+int lov_stripe_offset(struct lov_stripe_md *lsm, u64 lov_off,
+ int stripeno, u64 *u64);
+u64 lov_size_to_stripe(struct lov_stripe_md *lsm, u64 file_size,
+ int stripeno);
+int lov_stripe_intersects(struct lov_stripe_md *lsm, int stripeno,
+ u64 start, u64 end,
+ u64 *obd_start, u64 *obd_end);
+int lov_stripe_number(struct lov_stripe_md *lsm, u64 lov_off);
+
+/* lov_qos.c */
+#define LOV_USES_ASSIGNED_STRIPE 0
+#define LOV_USES_DEFAULT_STRIPE 1
+int qos_add_tgt(struct obd_device *obd, __u32 index);
+int qos_del_tgt(struct obd_device *obd, struct lov_tgt_desc *tgt);
+void qos_shrink_lsm(struct lov_request_set *set);
+int qos_prep_create(struct obd_export *exp, struct lov_request_set *set);
+void qos_update(struct lov_obd *lov);
+void qos_statfs_done(struct lov_obd *lov);
+void qos_statfs_update(struct obd_device *obd, __u64 max_age, int wait);
+int qos_remedy_create(struct lov_request_set *set, struct lov_request *req);
+
+/* lov_request.c */
+void lov_set_add_req(struct lov_request *req, struct lov_request_set *set);
+int lov_set_finished(struct lov_request_set *set, int idempotent);
+void lov_update_set(struct lov_request_set *set,
+ struct lov_request *req, int rc);
+int lov_update_common_set(struct lov_request_set *set,
+ struct lov_request *req, int rc);
+int lov_check_and_wait_active(struct lov_obd *lov, int ost_idx);
+int lov_prep_getattr_set(struct obd_export *exp, struct obd_info *oinfo,
+ struct lov_request_set **reqset);
+int lov_fini_getattr_set(struct lov_request_set *set);
+int lov_prep_destroy_set(struct obd_export *exp, struct obd_info *oinfo,
+ struct obdo *src_oa, struct lov_stripe_md *lsm,
+ struct obd_trans_info *oti,
+ struct lov_request_set **reqset);
+int lov_fini_destroy_set(struct lov_request_set *set);
+int lov_prep_setattr_set(struct obd_export *exp, struct obd_info *oinfo,
+ struct obd_trans_info *oti,
+ struct lov_request_set **reqset);
+int lov_update_setattr_set(struct lov_request_set *set,
+ struct lov_request *req, int rc);
+int lov_fini_setattr_set(struct lov_request_set *set);
+int lov_prep_statfs_set(struct obd_device *obd, struct obd_info *oinfo,
+ struct lov_request_set **reqset);
+void lov_update_statfs(struct obd_statfs *osfs, struct obd_statfs *lov_sfs,
+ int success);
+int lov_fini_statfs(struct obd_device *obd, struct obd_statfs *osfs,
+ int success);
+int lov_fini_statfs_set(struct lov_request_set *set);
+int lov_statfs_interpret(struct ptlrpc_request_set *rqset, void *data, int rc);
+
+/* lov_obd.c */
+void lov_fix_desc(struct lov_desc *desc);
+void lov_fix_desc_stripe_size(__u64 *val);
+void lov_fix_desc_stripe_count(__u32 *val);
+void lov_fix_desc_pattern(__u32 *val);
+void lov_fix_desc_qos_maxage(__u32 *val);
+__u16 lov_get_stripecnt(struct lov_obd *lov, __u32 magic, __u16 stripe_count);
+int lov_connect_obd(struct obd_device *obd, __u32 index, int activate,
+ struct obd_connect_data *data);
+int lov_setup(struct obd_device *obd, struct lustre_cfg *lcfg);
+int lov_process_config_base(struct obd_device *obd, struct lustre_cfg *lcfg,
+ __u32 *indexp, int *genp);
+int lov_del_target(struct obd_device *obd, __u32 index,
+ struct obd_uuid *uuidp, int gen);
+
+/* lov_pack.c */
+int lov_packmd(struct obd_export *exp, struct lov_mds_md **lmm,
+ struct lov_stripe_md *lsm);
+int lov_unpackmd(struct obd_export *exp, struct lov_stripe_md **lsmp,
+ struct lov_mds_md *lmm, int lmm_bytes);
+int lov_getstripe(struct obd_export *exp,
+ struct lov_stripe_md *lsm, struct lov_user_md *lump);
+int lov_alloc_memmd(struct lov_stripe_md **lsmp, __u16 stripe_count,
+ int pattern, int magic);
+int lov_free_memmd(struct lov_stripe_md **lsmp);
+
+void lov_dump_lmm_v1(int level, struct lov_mds_md_v1 *lmm);
+void lov_dump_lmm_v3(int level, struct lov_mds_md_v3 *lmm);
+void lov_dump_lmm_common(int level, void *lmmp);
+void lov_dump_lmm(int level, void *lmm);
+
+/* lov_ea.c */
+struct lov_stripe_md *lsm_alloc_plain(__u16 stripe_count, int *size);
+void lsm_free_plain(struct lov_stripe_md *lsm);
+void dump_lsm(unsigned int level, const struct lov_stripe_md *lsm);
+
+/* lproc_lov.c */
+#if defined (CONFIG_PROC_FS)
+extern const struct file_operations lov_proc_target_fops;
+void lprocfs_lov_init_vars(struct lprocfs_static_vars *lvars);
+#else
+static inline void lprocfs_lov_init_vars(struct lprocfs_static_vars *lvars)
+{
+ memset(lvars, 0, sizeof(*lvars));
+}
+#endif
+
+/* lov_cl.c */
+extern struct lu_device_type lov_device_type;
+
+/* pools */
+extern cfs_hash_ops_t pool_hash_operations;
+/* ost_pool methods */
+int lov_ost_pool_init(struct ost_pool *op, unsigned int count);
+int lov_ost_pool_extend(struct ost_pool *op, unsigned int min_count);
+int lov_ost_pool_add(struct ost_pool *op, __u32 idx, unsigned int min_count);
+int lov_ost_pool_remove(struct ost_pool *op, __u32 idx);
+int lov_ost_pool_free(struct ost_pool *op);
+
+/* high level pool methods */
+int lov_pool_new(struct obd_device *obd, char *poolname);
+int lov_pool_del(struct obd_device *obd, char *poolname);
+int lov_pool_add(struct obd_device *obd, char *poolname, char *ostname);
+int lov_pool_remove(struct obd_device *obd, char *poolname, char *ostname);
+void lov_dump_pool(int level, struct pool_desc *pool);
+struct pool_desc *lov_find_pool(struct lov_obd *lov, char *poolname);
+int lov_check_index_in_pool(__u32 idx, struct pool_desc *pool);
+void lov_pool_putref(struct pool_desc *pool);
+
+static inline struct lov_stripe_md *lsm_addref(struct lov_stripe_md *lsm)
+{
+ LASSERT(atomic_read(&lsm->lsm_refc) > 0);
+ atomic_inc(&lsm->lsm_refc);
+ return lsm;
+}
+
+static inline bool lov_oinfo_is_dummy(const struct lov_oinfo *loi)
+{
+ if (unlikely(loi->loi_oi.oi.oi_id == 0 &&
+ loi->loi_oi.oi.oi_seq == 0 &&
+ loi->loi_ost_idx == 0 &&
+ loi->loi_ost_gen == 0))
+ return true;
+
+ return false;
+}
+
+
+#endif
diff --git a/drivers/staging/lustre/lustre/lov/lov_io.c b/drivers/staging/lustre/lustre/lov/lov_io.c
new file mode 100644
index 000000000..cf96e0d01
--- /dev/null
+++ b/drivers/staging/lustre/lustre/lov/lov_io.c
@@ -0,0 +1,990 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2011, 2012, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ *
+ * Implementation of cl_io for LOV layer.
+ *
+ * Author: Nikita Danilov <nikita.danilov@sun.com>
+ * Author: Jinshan Xiong <jinshan.xiong@whamcloud.com>
+ */
+
+#define DEBUG_SUBSYSTEM S_LOV
+
+#include "lov_cl_internal.h"
+
+/** \addtogroup lov
+ * @{
+ */
+
+static inline void lov_sub_enter(struct lov_io_sub *sub)
+{
+ sub->sub_reenter++;
+}
+static inline void lov_sub_exit(struct lov_io_sub *sub)
+{
+ sub->sub_reenter--;
+}
+
+static void lov_io_sub_fini(const struct lu_env *env, struct lov_io *lio,
+ struct lov_io_sub *sub)
+{
+ if (sub->sub_io != NULL) {
+ if (sub->sub_io_initialized) {
+ lov_sub_enter(sub);
+ cl_io_fini(sub->sub_env, sub->sub_io);
+ lov_sub_exit(sub);
+ sub->sub_io_initialized = 0;
+ lio->lis_active_subios--;
+ }
+ if (sub->sub_stripe == lio->lis_single_subio_index)
+ lio->lis_single_subio_index = -1;
+ else if (!sub->sub_borrowed)
+ OBD_FREE_PTR(sub->sub_io);
+ sub->sub_io = NULL;
+ }
+ if (sub->sub_env != NULL && !IS_ERR(sub->sub_env)) {
+ if (!sub->sub_borrowed)
+ cl_env_put(sub->sub_env, &sub->sub_refcheck);
+ sub->sub_env = NULL;
+ }
+}
+
+static void lov_io_sub_inherit(struct cl_io *io, struct lov_io *lio,
+ int stripe, loff_t start, loff_t end)
+{
+ struct lov_stripe_md *lsm = lio->lis_object->lo_lsm;
+ struct cl_io *parent = lio->lis_cl.cis_io;
+
+ switch (io->ci_type) {
+ case CIT_SETATTR: {
+ io->u.ci_setattr.sa_attr = parent->u.ci_setattr.sa_attr;
+ io->u.ci_setattr.sa_valid = parent->u.ci_setattr.sa_valid;
+ io->u.ci_setattr.sa_capa = parent->u.ci_setattr.sa_capa;
+ if (cl_io_is_trunc(io)) {
+ loff_t new_size = parent->u.ci_setattr.sa_attr.lvb_size;
+
+ new_size = lov_size_to_stripe(lsm, new_size, stripe);
+ io->u.ci_setattr.sa_attr.lvb_size = new_size;
+ }
+ break;
+ }
+ case CIT_FAULT: {
+ struct cl_object *obj = parent->ci_obj;
+ loff_t off = cl_offset(obj, parent->u.ci_fault.ft_index);
+
+ io->u.ci_fault = parent->u.ci_fault;
+ off = lov_size_to_stripe(lsm, off, stripe);
+ io->u.ci_fault.ft_index = cl_index(obj, off);
+ break;
+ }
+ case CIT_FSYNC: {
+ io->u.ci_fsync.fi_start = start;
+ io->u.ci_fsync.fi_end = end;
+ io->u.ci_fsync.fi_capa = parent->u.ci_fsync.fi_capa;
+ io->u.ci_fsync.fi_fid = parent->u.ci_fsync.fi_fid;
+ io->u.ci_fsync.fi_mode = parent->u.ci_fsync.fi_mode;
+ break;
+ }
+ case CIT_READ:
+ case CIT_WRITE: {
+ io->u.ci_wr.wr_sync = cl_io_is_sync_write(parent);
+ if (cl_io_is_append(parent)) {
+ io->u.ci_wr.wr_append = 1;
+ } else {
+ io->u.ci_rw.crw_pos = start;
+ io->u.ci_rw.crw_count = end - start;
+ }
+ break;
+ }
+ default:
+ break;
+ }
+}
+
+static int lov_io_sub_init(const struct lu_env *env, struct lov_io *lio,
+ struct lov_io_sub *sub)
+{
+ struct lov_object *lov = lio->lis_object;
+ struct lov_device *ld = lu2lov_dev(lov2cl(lov)->co_lu.lo_dev);
+ struct cl_io *sub_io;
+ struct cl_object *sub_obj;
+ struct cl_io *io = lio->lis_cl.cis_io;
+
+ int stripe = sub->sub_stripe;
+ int result;
+
+ LASSERT(sub->sub_io == NULL);
+ LASSERT(sub->sub_env == NULL);
+ LASSERT(sub->sub_stripe < lio->lis_stripe_count);
+
+ if (unlikely(lov_r0(lov)->lo_sub[stripe] == NULL))
+ return -EIO;
+
+ result = 0;
+ sub->sub_io_initialized = 0;
+ sub->sub_borrowed = 0;
+
+ if (lio->lis_mem_frozen) {
+ LASSERT(mutex_is_locked(&ld->ld_mutex));
+ sub->sub_io = &ld->ld_emrg[stripe]->emrg_subio;
+ sub->sub_env = ld->ld_emrg[stripe]->emrg_env;
+ sub->sub_borrowed = 1;
+ } else {
+ void *cookie;
+
+ /* obtain new environment */
+ cookie = cl_env_reenter();
+ sub->sub_env = cl_env_get(&sub->sub_refcheck);
+ cl_env_reexit(cookie);
+ if (IS_ERR(sub->sub_env))
+ result = PTR_ERR(sub->sub_env);
+
+ if (result == 0) {
+ /*
+ * First sub-io. Use ->lis_single_subio to
+ * avoid dynamic allocation.
+ */
+ if (lio->lis_active_subios == 0) {
+ sub->sub_io = &lio->lis_single_subio;
+ lio->lis_single_subio_index = stripe;
+ } else {
+ OBD_ALLOC_PTR(sub->sub_io);
+ if (sub->sub_io == NULL)
+ result = -ENOMEM;
+ }
+ }
+ }
+
+ if (result == 0) {
+ sub_obj = lovsub2cl(lov_r0(lov)->lo_sub[stripe]);
+ sub_io = sub->sub_io;
+
+ sub_io->ci_obj = sub_obj;
+ sub_io->ci_result = 0;
+
+ sub_io->ci_parent = io;
+ sub_io->ci_lockreq = io->ci_lockreq;
+ sub_io->ci_type = io->ci_type;
+ sub_io->ci_no_srvlock = io->ci_no_srvlock;
+ sub_io->ci_noatime = io->ci_noatime;
+
+ lov_sub_enter(sub);
+ result = cl_io_sub_init(sub->sub_env, sub_io,
+ io->ci_type, sub_obj);
+ lov_sub_exit(sub);
+ if (result >= 0) {
+ lio->lis_active_subios++;
+ sub->sub_io_initialized = 1;
+ result = 0;
+ }
+ }
+ if (result != 0)
+ lov_io_sub_fini(env, lio, sub);
+ return result;
+}
+
+struct lov_io_sub *lov_sub_get(const struct lu_env *env,
+ struct lov_io *lio, int stripe)
+{
+ int rc;
+ struct lov_io_sub *sub = &lio->lis_subs[stripe];
+
+ LASSERT(stripe < lio->lis_stripe_count);
+
+ if (!sub->sub_io_initialized) {
+ sub->sub_stripe = stripe;
+ rc = lov_io_sub_init(env, lio, sub);
+ } else
+ rc = 0;
+ if (rc == 0)
+ lov_sub_enter(sub);
+ else
+ sub = ERR_PTR(rc);
+ return sub;
+}
+
+void lov_sub_put(struct lov_io_sub *sub)
+{
+ lov_sub_exit(sub);
+}
+
+/*****************************************************************************
+ *
+ * Lov io operations.
+ *
+ */
+
+static int lov_page_stripe(const struct cl_page *page)
+{
+ struct lovsub_object *subobj;
+
+ subobj = lu2lovsub(
+ lu_object_locate(page->cp_child->cp_obj->co_lu.lo_header,
+ &lovsub_device_type));
+ LASSERT(subobj != NULL);
+ return subobj->lso_index;
+}
+
+struct lov_io_sub *lov_page_subio(const struct lu_env *env, struct lov_io *lio,
+ const struct cl_page_slice *slice)
+{
+ struct lov_stripe_md *lsm = lio->lis_object->lo_lsm;
+ struct cl_page *page = slice->cpl_page;
+ int stripe;
+
+ LASSERT(lio->lis_cl.cis_io != NULL);
+ LASSERT(cl2lov(slice->cpl_obj) == lio->lis_object);
+ LASSERT(lsm != NULL);
+ LASSERT(lio->lis_nr_subios > 0);
+
+ stripe = lov_page_stripe(page);
+ return lov_sub_get(env, lio, stripe);
+}
+
+
+static int lov_io_subio_init(const struct lu_env *env, struct lov_io *lio,
+ struct cl_io *io)
+{
+ struct lov_stripe_md *lsm = lio->lis_object->lo_lsm;
+ int result;
+
+ LASSERT(lio->lis_object != NULL);
+
+ /*
+ * Need to be optimized, we can't afford to allocate a piece of memory
+ * when writing a page. -jay
+ */
+ OBD_ALLOC_LARGE(lio->lis_subs,
+ lsm->lsm_stripe_count * sizeof(lio->lis_subs[0]));
+ if (lio->lis_subs != NULL) {
+ lio->lis_nr_subios = lio->lis_stripe_count;
+ lio->lis_single_subio_index = -1;
+ lio->lis_active_subios = 0;
+ result = 0;
+ } else
+ result = -ENOMEM;
+ return result;
+}
+
+static void lov_io_slice_init(struct lov_io *lio,
+ struct lov_object *obj, struct cl_io *io)
+{
+ io->ci_result = 0;
+ lio->lis_object = obj;
+
+ LASSERT(obj->lo_lsm != NULL);
+ lio->lis_stripe_count = obj->lo_lsm->lsm_stripe_count;
+
+ switch (io->ci_type) {
+ case CIT_READ:
+ case CIT_WRITE:
+ lio->lis_pos = io->u.ci_rw.crw_pos;
+ lio->lis_endpos = io->u.ci_rw.crw_pos + io->u.ci_rw.crw_count;
+ lio->lis_io_endpos = lio->lis_endpos;
+ if (cl_io_is_append(io)) {
+ LASSERT(io->ci_type == CIT_WRITE);
+ lio->lis_pos = 0;
+ lio->lis_endpos = OBD_OBJECT_EOF;
+ }
+ break;
+
+ case CIT_SETATTR:
+ if (cl_io_is_trunc(io))
+ lio->lis_pos = io->u.ci_setattr.sa_attr.lvb_size;
+ else
+ lio->lis_pos = 0;
+ lio->lis_endpos = OBD_OBJECT_EOF;
+ break;
+
+ case CIT_FAULT: {
+ pgoff_t index = io->u.ci_fault.ft_index;
+ lio->lis_pos = cl_offset(io->ci_obj, index);
+ lio->lis_endpos = cl_offset(io->ci_obj, index + 1);
+ break;
+ }
+
+ case CIT_FSYNC: {
+ lio->lis_pos = io->u.ci_fsync.fi_start;
+ lio->lis_endpos = io->u.ci_fsync.fi_end;
+ break;
+ }
+
+ case CIT_MISC:
+ lio->lis_pos = 0;
+ lio->lis_endpos = OBD_OBJECT_EOF;
+ break;
+
+ default:
+ LBUG();
+ }
+}
+
+static void lov_io_fini(const struct lu_env *env, const struct cl_io_slice *ios)
+{
+ struct lov_io *lio = cl2lov_io(env, ios);
+ struct lov_object *lov = cl2lov(ios->cis_obj);
+ int i;
+
+ if (lio->lis_subs != NULL) {
+ for (i = 0; i < lio->lis_nr_subios; i++)
+ lov_io_sub_fini(env, lio, &lio->lis_subs[i]);
+ OBD_FREE_LARGE(lio->lis_subs,
+ lio->lis_nr_subios * sizeof(lio->lis_subs[0]));
+ lio->lis_nr_subios = 0;
+ }
+
+ LASSERT(atomic_read(&lov->lo_active_ios) > 0);
+ if (atomic_dec_and_test(&lov->lo_active_ios))
+ wake_up_all(&lov->lo_waitq);
+}
+
+static u64 lov_offset_mod(u64 val, int delta)
+{
+ if (val != OBD_OBJECT_EOF)
+ val += delta;
+ return val;
+}
+
+static int lov_io_iter_init(const struct lu_env *env,
+ const struct cl_io_slice *ios)
+{
+ struct lov_io *lio = cl2lov_io(env, ios);
+ struct lov_stripe_md *lsm = lio->lis_object->lo_lsm;
+ struct lov_io_sub *sub;
+ u64 endpos;
+ u64 start;
+ u64 end;
+ int stripe;
+ int rc = 0;
+
+ endpos = lov_offset_mod(lio->lis_endpos, -1);
+ for (stripe = 0; stripe < lio->lis_stripe_count; stripe++) {
+ if (!lov_stripe_intersects(lsm, stripe, lio->lis_pos,
+ endpos, &start, &end))
+ continue;
+
+ if (unlikely(lov_r0(lio->lis_object)->lo_sub[stripe] == NULL)) {
+ if (ios->cis_io->ci_type == CIT_READ ||
+ ios->cis_io->ci_type == CIT_WRITE ||
+ ios->cis_io->ci_type == CIT_FAULT)
+ return -EIO;
+
+ continue;
+ }
+
+ end = lov_offset_mod(end, 1);
+ sub = lov_sub_get(env, lio, stripe);
+ if (!IS_ERR(sub)) {
+ lov_io_sub_inherit(sub->sub_io, lio, stripe,
+ start, end);
+ rc = cl_io_iter_init(sub->sub_env, sub->sub_io);
+ lov_sub_put(sub);
+ CDEBUG(D_VFSTRACE, "shrink: %d [%llu, %llu)\n",
+ stripe, start, end);
+ } else
+ rc = PTR_ERR(sub);
+
+ if (!rc)
+ list_add_tail(&sub->sub_linkage, &lio->lis_active);
+ else
+ break;
+ }
+ return rc;
+}
+
+static int lov_io_rw_iter_init(const struct lu_env *env,
+ const struct cl_io_slice *ios)
+{
+ struct lov_io *lio = cl2lov_io(env, ios);
+ struct cl_io *io = ios->cis_io;
+ struct lov_stripe_md *lsm = lio->lis_object->lo_lsm;
+ __u64 start = io->u.ci_rw.crw_pos;
+ loff_t next;
+ unsigned long ssize = lsm->lsm_stripe_size;
+
+ LASSERT(io->ci_type == CIT_READ || io->ci_type == CIT_WRITE);
+
+ /* fast path for common case. */
+ if (lio->lis_nr_subios != 1 && !cl_io_is_append(io)) {
+
+ lov_do_div64(start, ssize);
+ next = (start + 1) * ssize;
+ if (next <= start * ssize)
+ next = ~0ull;
+
+ io->ci_continue = next < lio->lis_io_endpos;
+ io->u.ci_rw.crw_count = min_t(loff_t, lio->lis_io_endpos,
+ next) - io->u.ci_rw.crw_pos;
+ lio->lis_pos = io->u.ci_rw.crw_pos;
+ lio->lis_endpos = io->u.ci_rw.crw_pos + io->u.ci_rw.crw_count;
+ CDEBUG(D_VFSTRACE, "stripe: %llu chunk: [%llu, %llu) %llu\n",
+ (__u64)start, lio->lis_pos, lio->lis_endpos,
+ (__u64)lio->lis_io_endpos);
+ }
+ /*
+ * XXX The following call should be optimized: we know, that
+ * [lio->lis_pos, lio->lis_endpos) intersects with exactly one stripe.
+ */
+ return lov_io_iter_init(env, ios);
+}
+
+static int lov_io_call(const struct lu_env *env, struct lov_io *lio,
+ int (*iofunc)(const struct lu_env *, struct cl_io *))
+{
+ struct cl_io *parent = lio->lis_cl.cis_io;
+ struct lov_io_sub *sub;
+ int rc = 0;
+
+ list_for_each_entry(sub, &lio->lis_active, sub_linkage) {
+ lov_sub_enter(sub);
+ rc = iofunc(sub->sub_env, sub->sub_io);
+ lov_sub_exit(sub);
+ if (rc)
+ break;
+
+ if (parent->ci_result == 0)
+ parent->ci_result = sub->sub_io->ci_result;
+ }
+ return rc;
+}
+
+static int lov_io_lock(const struct lu_env *env, const struct cl_io_slice *ios)
+{
+ return lov_io_call(env, cl2lov_io(env, ios), cl_io_lock);
+}
+
+static int lov_io_start(const struct lu_env *env, const struct cl_io_slice *ios)
+{
+ return lov_io_call(env, cl2lov_io(env, ios), cl_io_start);
+}
+
+static int lov_io_end_wrapper(const struct lu_env *env, struct cl_io *io)
+{
+ /*
+ * It's possible that lov_io_start() wasn't called against this
+ * sub-io, either because previous sub-io failed, or upper layer
+ * completed IO.
+ */
+ if (io->ci_state == CIS_IO_GOING)
+ cl_io_end(env, io);
+ else
+ io->ci_state = CIS_IO_FINISHED;
+ return 0;
+}
+
+static int lov_io_iter_fini_wrapper(const struct lu_env *env, struct cl_io *io)
+{
+ cl_io_iter_fini(env, io);
+ return 0;
+}
+
+static int lov_io_unlock_wrapper(const struct lu_env *env, struct cl_io *io)
+{
+ cl_io_unlock(env, io);
+ return 0;
+}
+
+static void lov_io_end(const struct lu_env *env, const struct cl_io_slice *ios)
+{
+ int rc;
+
+ rc = lov_io_call(env, cl2lov_io(env, ios), lov_io_end_wrapper);
+ LASSERT(rc == 0);
+}
+
+static void lov_io_iter_fini(const struct lu_env *env,
+ const struct cl_io_slice *ios)
+{
+ struct lov_io *lio = cl2lov_io(env, ios);
+ int rc;
+
+ rc = lov_io_call(env, lio, lov_io_iter_fini_wrapper);
+ LASSERT(rc == 0);
+ while (!list_empty(&lio->lis_active))
+ list_del_init(lio->lis_active.next);
+}
+
+static void lov_io_unlock(const struct lu_env *env,
+ const struct cl_io_slice *ios)
+{
+ int rc;
+
+ rc = lov_io_call(env, cl2lov_io(env, ios), lov_io_unlock_wrapper);
+ LASSERT(rc == 0);
+}
+
+
+static struct cl_page_list *lov_io_submit_qin(struct lov_device *ld,
+ struct cl_page_list *qin,
+ int idx, int alloc)
+{
+ return alloc ? &qin[idx] : &ld->ld_emrg[idx]->emrg_page_list;
+}
+
+/**
+ * lov implementation of cl_operations::cio_submit() method. It takes a list
+ * of pages in \a queue, splits it into per-stripe sub-lists, invokes
+ * cl_io_submit() on underlying devices to submit sub-lists, and then splices
+ * everything back.
+ *
+ * Major complication of this function is a need to handle memory cleansing:
+ * cl_io_submit() is called to write out pages as a part of VM memory
+ * reclamation, and hence it may not fail due to memory shortages (system
+ * dead-locks otherwise). To deal with this, some resources (sub-lists,
+ * sub-environment, etc.) are allocated per-device on "startup" (i.e., in a
+ * not-memory cleansing context), and in case of memory shortage, these
+ * pre-allocated resources are used by lov_io_submit() under
+ * lov_device::ld_mutex mutex.
+ */
+static int lov_io_submit(const struct lu_env *env,
+ const struct cl_io_slice *ios,
+ enum cl_req_type crt, struct cl_2queue *queue)
+{
+ struct lov_io *lio = cl2lov_io(env, ios);
+ struct lov_object *obj = lio->lis_object;
+ struct lov_device *ld = lu2lov_dev(lov2cl(obj)->co_lu.lo_dev);
+ struct cl_page_list *qin = &queue->c2_qin;
+ struct cl_2queue *cl2q = &lov_env_info(env)->lti_cl2q;
+ struct cl_page_list *stripes_qin = NULL;
+ struct cl_page *page;
+ struct cl_page *tmp;
+ int stripe;
+
+#define QIN(stripe) lov_io_submit_qin(ld, stripes_qin, stripe, alloc)
+
+ int rc = 0;
+ int alloc =
+ !(current->flags & PF_MEMALLOC);
+
+ if (lio->lis_active_subios == 1) {
+ int idx = lio->lis_single_subio_index;
+ struct lov_io_sub *sub;
+
+ LASSERT(idx < lio->lis_nr_subios);
+ sub = lov_sub_get(env, lio, idx);
+ LASSERT(!IS_ERR(sub));
+ LASSERT(sub->sub_io == &lio->lis_single_subio);
+ rc = cl_io_submit_rw(sub->sub_env, sub->sub_io,
+ crt, queue);
+ lov_sub_put(sub);
+ return rc;
+ }
+
+ LASSERT(lio->lis_subs != NULL);
+ if (alloc) {
+ OBD_ALLOC_LARGE(stripes_qin,
+ sizeof(*stripes_qin) * lio->lis_nr_subios);
+ if (stripes_qin == NULL)
+ return -ENOMEM;
+
+ for (stripe = 0; stripe < lio->lis_nr_subios; stripe++)
+ cl_page_list_init(&stripes_qin[stripe]);
+ } else {
+ /*
+ * If we get here, it means pageout & swap doesn't help.
+ * In order to not make things worse, even don't try to
+ * allocate the memory with __GFP_NOWARN. -jay
+ */
+ mutex_lock(&ld->ld_mutex);
+ lio->lis_mem_frozen = 1;
+ }
+
+ cl_2queue_init(cl2q);
+ cl_page_list_for_each_safe(page, tmp, qin) {
+ stripe = lov_page_stripe(page);
+ cl_page_list_move(QIN(stripe), qin, page);
+ }
+
+ for (stripe = 0; stripe < lio->lis_nr_subios; stripe++) {
+ struct lov_io_sub *sub;
+ struct cl_page_list *sub_qin = QIN(stripe);
+
+ if (list_empty(&sub_qin->pl_pages))
+ continue;
+
+ cl_page_list_splice(sub_qin, &cl2q->c2_qin);
+ sub = lov_sub_get(env, lio, stripe);
+ if (!IS_ERR(sub)) {
+ rc = cl_io_submit_rw(sub->sub_env, sub->sub_io,
+ crt, cl2q);
+ lov_sub_put(sub);
+ } else
+ rc = PTR_ERR(sub);
+ cl_page_list_splice(&cl2q->c2_qin, &queue->c2_qin);
+ cl_page_list_splice(&cl2q->c2_qout, &queue->c2_qout);
+ if (rc != 0)
+ break;
+ }
+
+ for (stripe = 0; stripe < lio->lis_nr_subios; stripe++) {
+ struct cl_page_list *sub_qin = QIN(stripe);
+
+ if (list_empty(&sub_qin->pl_pages))
+ continue;
+
+ cl_page_list_splice(sub_qin, qin);
+ }
+
+ if (alloc) {
+ OBD_FREE_LARGE(stripes_qin,
+ sizeof(*stripes_qin) * lio->lis_nr_subios);
+ } else {
+ int i;
+
+ for (i = 0; i < lio->lis_nr_subios; i++) {
+ struct cl_io *cio = lio->lis_subs[i].sub_io;
+
+ if (cio && cio == &ld->ld_emrg[i]->emrg_subio)
+ lov_io_sub_fini(env, lio, &lio->lis_subs[i]);
+ }
+ lio->lis_mem_frozen = 0;
+ mutex_unlock(&ld->ld_mutex);
+ }
+
+ return rc;
+#undef QIN
+}
+
+static int lov_io_prepare_write(const struct lu_env *env,
+ const struct cl_io_slice *ios,
+ const struct cl_page_slice *slice,
+ unsigned from, unsigned to)
+{
+ struct lov_io *lio = cl2lov_io(env, ios);
+ struct cl_page *sub_page = lov_sub_page(slice);
+ struct lov_io_sub *sub;
+ int result;
+
+ sub = lov_page_subio(env, lio, slice);
+ if (!IS_ERR(sub)) {
+ result = cl_io_prepare_write(sub->sub_env, sub->sub_io,
+ sub_page, from, to);
+ lov_sub_put(sub);
+ } else
+ result = PTR_ERR(sub);
+ return result;
+}
+
+static int lov_io_commit_write(const struct lu_env *env,
+ const struct cl_io_slice *ios,
+ const struct cl_page_slice *slice,
+ unsigned from, unsigned to)
+{
+ struct lov_io *lio = cl2lov_io(env, ios);
+ struct cl_page *sub_page = lov_sub_page(slice);
+ struct lov_io_sub *sub;
+ int result;
+
+ sub = lov_page_subio(env, lio, slice);
+ if (!IS_ERR(sub)) {
+ result = cl_io_commit_write(sub->sub_env, sub->sub_io,
+ sub_page, from, to);
+ lov_sub_put(sub);
+ } else
+ result = PTR_ERR(sub);
+ return result;
+}
+
+static int lov_io_fault_start(const struct lu_env *env,
+ const struct cl_io_slice *ios)
+{
+ struct cl_fault_io *fio;
+ struct lov_io *lio;
+ struct lov_io_sub *sub;
+
+ fio = &ios->cis_io->u.ci_fault;
+ lio = cl2lov_io(env, ios);
+ sub = lov_sub_get(env, lio, lov_page_stripe(fio->ft_page));
+ sub->sub_io->u.ci_fault.ft_nob = fio->ft_nob;
+ lov_sub_put(sub);
+ return lov_io_start(env, ios);
+}
+
+static void lov_io_fsync_end(const struct lu_env *env,
+ const struct cl_io_slice *ios)
+{
+ struct lov_io *lio = cl2lov_io(env, ios);
+ struct lov_io_sub *sub;
+ unsigned int *written = &ios->cis_io->u.ci_fsync.fi_nr_written;
+
+ *written = 0;
+ list_for_each_entry(sub, &lio->lis_active, sub_linkage) {
+ struct cl_io *subio = sub->sub_io;
+
+ lov_sub_enter(sub);
+ lov_io_end_wrapper(sub->sub_env, subio);
+ lov_sub_exit(sub);
+
+ if (subio->ci_result == 0)
+ *written += subio->u.ci_fsync.fi_nr_written;
+ }
+}
+
+static const struct cl_io_operations lov_io_ops = {
+ .op = {
+ [CIT_READ] = {
+ .cio_fini = lov_io_fini,
+ .cio_iter_init = lov_io_rw_iter_init,
+ .cio_iter_fini = lov_io_iter_fini,
+ .cio_lock = lov_io_lock,
+ .cio_unlock = lov_io_unlock,
+ .cio_start = lov_io_start,
+ .cio_end = lov_io_end
+ },
+ [CIT_WRITE] = {
+ .cio_fini = lov_io_fini,
+ .cio_iter_init = lov_io_rw_iter_init,
+ .cio_iter_fini = lov_io_iter_fini,
+ .cio_lock = lov_io_lock,
+ .cio_unlock = lov_io_unlock,
+ .cio_start = lov_io_start,
+ .cio_end = lov_io_end
+ },
+ [CIT_SETATTR] = {
+ .cio_fini = lov_io_fini,
+ .cio_iter_init = lov_io_iter_init,
+ .cio_iter_fini = lov_io_iter_fini,
+ .cio_lock = lov_io_lock,
+ .cio_unlock = lov_io_unlock,
+ .cio_start = lov_io_start,
+ .cio_end = lov_io_end
+ },
+ [CIT_FAULT] = {
+ .cio_fini = lov_io_fini,
+ .cio_iter_init = lov_io_iter_init,
+ .cio_iter_fini = lov_io_iter_fini,
+ .cio_lock = lov_io_lock,
+ .cio_unlock = lov_io_unlock,
+ .cio_start = lov_io_fault_start,
+ .cio_end = lov_io_end
+ },
+ [CIT_FSYNC] = {
+ .cio_fini = lov_io_fini,
+ .cio_iter_init = lov_io_iter_init,
+ .cio_iter_fini = lov_io_iter_fini,
+ .cio_lock = lov_io_lock,
+ .cio_unlock = lov_io_unlock,
+ .cio_start = lov_io_start,
+ .cio_end = lov_io_fsync_end
+ },
+ [CIT_MISC] = {
+ .cio_fini = lov_io_fini
+ }
+ },
+ .req_op = {
+ [CRT_READ] = {
+ .cio_submit = lov_io_submit
+ },
+ [CRT_WRITE] = {
+ .cio_submit = lov_io_submit
+ }
+ },
+ .cio_prepare_write = lov_io_prepare_write,
+ .cio_commit_write = lov_io_commit_write
+};
+
+/*****************************************************************************
+ *
+ * Empty lov io operations.
+ *
+ */
+
+static void lov_empty_io_fini(const struct lu_env *env,
+ const struct cl_io_slice *ios)
+{
+ struct lov_object *lov = cl2lov(ios->cis_obj);
+
+ if (atomic_dec_and_test(&lov->lo_active_ios))
+ wake_up_all(&lov->lo_waitq);
+}
+
+static void lov_empty_impossible(const struct lu_env *env,
+ struct cl_io_slice *ios)
+{
+ LBUG();
+}
+
+#define LOV_EMPTY_IMPOSSIBLE ((void *)lov_empty_impossible)
+
+/**
+ * An io operation vector for files without stripes.
+ */
+static const struct cl_io_operations lov_empty_io_ops = {
+ .op = {
+ [CIT_READ] = {
+ .cio_fini = lov_empty_io_fini,
+#if 0
+ .cio_iter_init = LOV_EMPTY_IMPOSSIBLE,
+ .cio_lock = LOV_EMPTY_IMPOSSIBLE,
+ .cio_start = LOV_EMPTY_IMPOSSIBLE,
+ .cio_end = LOV_EMPTY_IMPOSSIBLE
+#endif
+ },
+ [CIT_WRITE] = {
+ .cio_fini = lov_empty_io_fini,
+ .cio_iter_init = LOV_EMPTY_IMPOSSIBLE,
+ .cio_lock = LOV_EMPTY_IMPOSSIBLE,
+ .cio_start = LOV_EMPTY_IMPOSSIBLE,
+ .cio_end = LOV_EMPTY_IMPOSSIBLE
+ },
+ [CIT_SETATTR] = {
+ .cio_fini = lov_empty_io_fini,
+ .cio_iter_init = LOV_EMPTY_IMPOSSIBLE,
+ .cio_lock = LOV_EMPTY_IMPOSSIBLE,
+ .cio_start = LOV_EMPTY_IMPOSSIBLE,
+ .cio_end = LOV_EMPTY_IMPOSSIBLE
+ },
+ [CIT_FAULT] = {
+ .cio_fini = lov_empty_io_fini,
+ .cio_iter_init = LOV_EMPTY_IMPOSSIBLE,
+ .cio_lock = LOV_EMPTY_IMPOSSIBLE,
+ .cio_start = LOV_EMPTY_IMPOSSIBLE,
+ .cio_end = LOV_EMPTY_IMPOSSIBLE
+ },
+ [CIT_FSYNC] = {
+ .cio_fini = lov_empty_io_fini
+ },
+ [CIT_MISC] = {
+ .cio_fini = lov_empty_io_fini
+ }
+ },
+ .req_op = {
+ [CRT_READ] = {
+ .cio_submit = LOV_EMPTY_IMPOSSIBLE
+ },
+ [CRT_WRITE] = {
+ .cio_submit = LOV_EMPTY_IMPOSSIBLE
+ }
+ },
+ .cio_commit_write = LOV_EMPTY_IMPOSSIBLE
+};
+
+int lov_io_init_raid0(const struct lu_env *env, struct cl_object *obj,
+ struct cl_io *io)
+{
+ struct lov_io *lio = lov_env_io(env);
+ struct lov_object *lov = cl2lov(obj);
+
+ INIT_LIST_HEAD(&lio->lis_active);
+ lov_io_slice_init(lio, lov, io);
+ if (io->ci_result == 0) {
+ io->ci_result = lov_io_subio_init(env, lio, io);
+ if (io->ci_result == 0) {
+ cl_io_slice_add(io, &lio->lis_cl, obj, &lov_io_ops);
+ atomic_inc(&lov->lo_active_ios);
+ }
+ }
+ return io->ci_result;
+}
+
+int lov_io_init_empty(const struct lu_env *env, struct cl_object *obj,
+ struct cl_io *io)
+{
+ struct lov_object *lov = cl2lov(obj);
+ struct lov_io *lio = lov_env_io(env);
+ int result;
+
+ lio->lis_object = lov;
+ switch (io->ci_type) {
+ default:
+ LBUG();
+ case CIT_MISC:
+ case CIT_READ:
+ result = 0;
+ break;
+ case CIT_FSYNC:
+ case CIT_SETATTR:
+ result = 1;
+ break;
+ case CIT_WRITE:
+ result = -EBADF;
+ break;
+ case CIT_FAULT:
+ result = -EFAULT;
+ CERROR("Page fault on a file without stripes: "DFID"\n",
+ PFID(lu_object_fid(&obj->co_lu)));
+ break;
+ }
+ if (result == 0) {
+ cl_io_slice_add(io, &lio->lis_cl, obj, &lov_empty_io_ops);
+ atomic_inc(&lov->lo_active_ios);
+ }
+
+ io->ci_result = result < 0 ? result : 0;
+ return result != 0;
+}
+
+int lov_io_init_released(const struct lu_env *env, struct cl_object *obj,
+ struct cl_io *io)
+{
+ struct lov_object *lov = cl2lov(obj);
+ struct lov_io *lio = lov_env_io(env);
+ int result;
+
+ LASSERT(lov->lo_lsm != NULL);
+ lio->lis_object = lov;
+
+ switch (io->ci_type) {
+ default:
+ LASSERTF(0, "invalid type %d\n", io->ci_type);
+ case CIT_MISC:
+ case CIT_FSYNC:
+ result = 1;
+ break;
+ case CIT_SETATTR:
+ /* the truncate to 0 is managed by MDT:
+ * - in open, for open O_TRUNC
+ * - in setattr, for truncate
+ */
+ /* the truncate is for size > 0 so triggers a restore */
+ if (cl_io_is_trunc(io))
+ io->ci_restore_needed = 1;
+ result = -ENODATA;
+ break;
+ case CIT_READ:
+ case CIT_WRITE:
+ case CIT_FAULT:
+ io->ci_restore_needed = 1;
+ result = -ENODATA;
+ break;
+ }
+ if (result == 0) {
+ cl_io_slice_add(io, &lio->lis_cl, obj, &lov_empty_io_ops);
+ atomic_inc(&lov->lo_active_ios);
+ }
+
+ io->ci_result = result < 0 ? result : 0;
+ return result != 0;
+}
+/** @} lov */
diff --git a/drivers/staging/lustre/lustre/lov/lov_lock.c b/drivers/staging/lustre/lustre/lov/lov_lock.c
new file mode 100644
index 000000000..f2eca565b
--- /dev/null
+++ b/drivers/staging/lustre/lustre/lov/lov_lock.c
@@ -0,0 +1,1198 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2011, 2012, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ *
+ * Implementation of cl_lock for LOV layer.
+ *
+ * Author: Nikita Danilov <nikita.danilov@sun.com>
+ */
+
+#define DEBUG_SUBSYSTEM S_LOV
+
+#include "lov_cl_internal.h"
+
+/** \addtogroup lov
+ * @{
+ */
+
+static struct cl_lock_closure *lov_closure_get(const struct lu_env *env,
+ struct cl_lock *parent);
+
+static int lov_lock_unuse(const struct lu_env *env,
+ const struct cl_lock_slice *slice);
+/*****************************************************************************
+ *
+ * Lov lock operations.
+ *
+ */
+
+static struct lov_sublock_env *lov_sublock_env_get(const struct lu_env *env,
+ struct cl_lock *parent,
+ struct lov_lock_sub *lls)
+{
+ struct lov_sublock_env *subenv;
+ struct lov_io *lio = lov_env_io(env);
+ struct cl_io *io = lio->lis_cl.cis_io;
+ struct lov_io_sub *sub;
+
+ subenv = &lov_env_session(env)->ls_subenv;
+
+ /*
+ * FIXME: We tend to use the subio's env & io to call the sublock
+ * lock operations because osc lock sometimes stores some control
+ * variables in thread's IO information(Now only lockless information).
+ * However, if the lock's host(object) is different from the object
+ * for current IO, we have no way to get the subenv and subio because
+ * they are not initialized at all. As a temp fix, in this case,
+ * we still borrow the parent's env to call sublock operations.
+ */
+ if (!io || !cl_object_same(io->ci_obj, parent->cll_descr.cld_obj)) {
+ subenv->lse_env = env;
+ subenv->lse_io = io;
+ subenv->lse_sub = NULL;
+ } else {
+ sub = lov_sub_get(env, lio, lls->sub_stripe);
+ if (!IS_ERR(sub)) {
+ subenv->lse_env = sub->sub_env;
+ subenv->lse_io = sub->sub_io;
+ subenv->lse_sub = sub;
+ } else {
+ subenv = (void *)sub;
+ }
+ }
+ return subenv;
+}
+
+static void lov_sublock_env_put(struct lov_sublock_env *subenv)
+{
+ if (subenv && subenv->lse_sub)
+ lov_sub_put(subenv->lse_sub);
+}
+
+static void lov_sublock_adopt(const struct lu_env *env, struct lov_lock *lck,
+ struct cl_lock *sublock, int idx,
+ struct lov_lock_link *link)
+{
+ struct lovsub_lock *lsl;
+ struct cl_lock *parent = lck->lls_cl.cls_lock;
+ int rc;
+
+ LASSERT(cl_lock_is_mutexed(parent));
+ LASSERT(cl_lock_is_mutexed(sublock));
+
+ lsl = cl2sub_lock(sublock);
+ /*
+ * check that sub-lock doesn't have lock link to this top-lock.
+ */
+ LASSERT(lov_lock_link_find(env, lck, lsl) == NULL);
+ LASSERT(idx < lck->lls_nr);
+
+ lck->lls_sub[idx].sub_lock = lsl;
+ lck->lls_nr_filled++;
+ LASSERT(lck->lls_nr_filled <= lck->lls_nr);
+ list_add_tail(&link->lll_list, &lsl->lss_parents);
+ link->lll_idx = idx;
+ link->lll_super = lck;
+ cl_lock_get(parent);
+ lu_ref_add(&parent->cll_reference, "lov-child", sublock);
+ lck->lls_sub[idx].sub_flags |= LSF_HELD;
+ cl_lock_user_add(env, sublock);
+
+ rc = lov_sublock_modify(env, lck, lsl, &sublock->cll_descr, idx);
+ LASSERT(rc == 0); /* there is no way this can fail, currently */
+}
+
+static struct cl_lock *lov_sublock_alloc(const struct lu_env *env,
+ const struct cl_io *io,
+ struct lov_lock *lck,
+ int idx, struct lov_lock_link **out)
+{
+ struct cl_lock *sublock;
+ struct cl_lock *parent;
+ struct lov_lock_link *link;
+
+ LASSERT(idx < lck->lls_nr);
+
+ OBD_SLAB_ALLOC_PTR_GFP(link, lov_lock_link_kmem, GFP_NOFS);
+ if (link != NULL) {
+ struct lov_sublock_env *subenv;
+ struct lov_lock_sub *lls;
+ struct cl_lock_descr *descr;
+
+ parent = lck->lls_cl.cls_lock;
+ lls = &lck->lls_sub[idx];
+ descr = &lls->sub_got;
+
+ subenv = lov_sublock_env_get(env, parent, lls);
+ if (!IS_ERR(subenv)) {
+ /* CAVEAT: Don't try to add a field in lov_lock_sub
+ * to remember the subio. This is because lock is able
+ * to be cached, but this is not true for IO. This
+ * further means a sublock might be referenced in
+ * different io context. -jay */
+
+ sublock = cl_lock_hold(subenv->lse_env, subenv->lse_io,
+ descr, "lov-parent", parent);
+ lov_sublock_env_put(subenv);
+ } else {
+ /* error occurs. */
+ sublock = (void *)subenv;
+ }
+
+ if (!IS_ERR(sublock))
+ *out = link;
+ else
+ OBD_SLAB_FREE_PTR(link, lov_lock_link_kmem);
+ } else
+ sublock = ERR_PTR(-ENOMEM);
+ return sublock;
+}
+
+static void lov_sublock_unlock(const struct lu_env *env,
+ struct lovsub_lock *lsl,
+ struct cl_lock_closure *closure,
+ struct lov_sublock_env *subenv)
+{
+ lov_sublock_env_put(subenv);
+ lsl->lss_active = NULL;
+ cl_lock_disclosure(env, closure);
+}
+
+static int lov_sublock_lock(const struct lu_env *env,
+ struct lov_lock *lck,
+ struct lov_lock_sub *lls,
+ struct cl_lock_closure *closure,
+ struct lov_sublock_env **lsep)
+{
+ struct lovsub_lock *sublock;
+ struct cl_lock *child;
+ int result = 0;
+
+ LASSERT(list_empty(&closure->clc_list));
+
+ sublock = lls->sub_lock;
+ child = sublock->lss_cl.cls_lock;
+ result = cl_lock_closure_build(env, child, closure);
+ if (result == 0) {
+ struct cl_lock *parent = closure->clc_origin;
+
+ LASSERT(cl_lock_is_mutexed(child));
+ sublock->lss_active = parent;
+
+ if (unlikely((child->cll_state == CLS_FREEING) ||
+ (child->cll_flags & CLF_CANCELLED))) {
+ struct lov_lock_link *link;
+ /*
+ * we could race with lock deletion which temporarily
+ * put the lock in freeing state, bug 19080.
+ */
+ LASSERT(!(lls->sub_flags & LSF_HELD));
+
+ link = lov_lock_link_find(env, lck, sublock);
+ LASSERT(link != NULL);
+ lov_lock_unlink(env, link, sublock);
+ lov_sublock_unlock(env, sublock, closure, NULL);
+ lck->lls_cancel_race = 1;
+ result = CLO_REPEAT;
+ } else if (lsep) {
+ struct lov_sublock_env *subenv;
+ subenv = lov_sublock_env_get(env, parent, lls);
+ if (IS_ERR(subenv)) {
+ lov_sublock_unlock(env, sublock,
+ closure, NULL);
+ result = PTR_ERR(subenv);
+ } else {
+ *lsep = subenv;
+ }
+ }
+ }
+ return result;
+}
+
+/**
+ * Updates the result of a top-lock operation from a result of sub-lock
+ * sub-operations. Top-operations like lov_lock_{enqueue,use,unuse}() iterate
+ * over sub-locks and lov_subresult() is used to calculate return value of a
+ * top-operation. To this end, possible return values of sub-operations are
+ * ordered as
+ *
+ * - 0 success
+ * - CLO_WAIT wait for event
+ * - CLO_REPEAT repeat top-operation
+ * - -ne fundamental error
+ *
+ * Top-level return code can only go down through this list. CLO_REPEAT
+ * overwrites CLO_WAIT, because lock mutex was released and sleeping condition
+ * has to be rechecked by the upper layer.
+ */
+static int lov_subresult(int result, int rc)
+{
+ int result_rank;
+ int rc_rank;
+
+ LASSERTF(result <= 0 || result == CLO_REPEAT || result == CLO_WAIT,
+ "result = %d", result);
+ LASSERTF(rc <= 0 || rc == CLO_REPEAT || rc == CLO_WAIT,
+ "rc = %d\n", rc);
+ CLASSERT(CLO_WAIT < CLO_REPEAT);
+
+ /* calculate ranks in the ordering above */
+ result_rank = result < 0 ? 1 + CLO_REPEAT : result;
+ rc_rank = rc < 0 ? 1 + CLO_REPEAT : rc;
+
+ if (result_rank < rc_rank)
+ result = rc;
+ return result;
+}
+
+/**
+ * Creates sub-locks for a given lov_lock for the first time.
+ *
+ * Goes through all sub-objects of top-object, and creates sub-locks on every
+ * sub-object intersecting with top-lock extent. This is complicated by the
+ * fact that top-lock (that is being created) can be accessed concurrently
+ * through already created sub-locks (possibly shared with other top-locks).
+ */
+static int lov_lock_sub_init(const struct lu_env *env,
+ struct lov_lock *lck, const struct cl_io *io)
+{
+ int result = 0;
+ int i;
+ int nr;
+ u64 start;
+ u64 end;
+ u64 file_start;
+ u64 file_end;
+
+ struct lov_object *loo = cl2lov(lck->lls_cl.cls_obj);
+ struct lov_layout_raid0 *r0 = lov_r0(loo);
+ struct cl_lock *parent = lck->lls_cl.cls_lock;
+
+ lck->lls_orig = parent->cll_descr;
+ file_start = cl_offset(lov2cl(loo), parent->cll_descr.cld_start);
+ file_end = cl_offset(lov2cl(loo), parent->cll_descr.cld_end + 1) - 1;
+
+ for (i = 0, nr = 0; i < r0->lo_nr; i++) {
+ /*
+ * XXX for wide striping smarter algorithm is desirable,
+ * breaking out of the loop, early.
+ */
+ if (likely(r0->lo_sub[i] != NULL) &&
+ lov_stripe_intersects(loo->lo_lsm, i,
+ file_start, file_end, &start, &end))
+ nr++;
+ }
+ LASSERT(nr > 0);
+ OBD_ALLOC_LARGE(lck->lls_sub, nr * sizeof(lck->lls_sub[0]));
+ if (lck->lls_sub == NULL)
+ return -ENOMEM;
+
+ lck->lls_nr = nr;
+ /*
+ * First, fill in sub-lock descriptions in
+ * lck->lls_sub[].sub_descr. They are used by lov_sublock_alloc()
+ * (called below in this function, and by lov_lock_enqueue()) to
+ * create sub-locks. At this moment, no other thread can access
+ * top-lock.
+ */
+ for (i = 0, nr = 0; i < r0->lo_nr; ++i) {
+ if (likely(r0->lo_sub[i] != NULL) &&
+ lov_stripe_intersects(loo->lo_lsm, i,
+ file_start, file_end, &start, &end)) {
+ struct cl_lock_descr *descr;
+
+ descr = &lck->lls_sub[nr].sub_descr;
+
+ LASSERT(descr->cld_obj == NULL);
+ descr->cld_obj = lovsub2cl(r0->lo_sub[i]);
+ descr->cld_start = cl_index(descr->cld_obj, start);
+ descr->cld_end = cl_index(descr->cld_obj, end);
+ descr->cld_mode = parent->cll_descr.cld_mode;
+ descr->cld_gid = parent->cll_descr.cld_gid;
+ descr->cld_enq_flags = parent->cll_descr.cld_enq_flags;
+ /* XXX has no effect */
+ lck->lls_sub[nr].sub_got = *descr;
+ lck->lls_sub[nr].sub_stripe = i;
+ nr++;
+ }
+ }
+ LASSERT(nr == lck->lls_nr);
+
+ /*
+ * Some sub-locks can be missing at this point. This is not a problem,
+ * because enqueue will create them anyway. Main duty of this function
+ * is to fill in sub-lock descriptions in a race free manner.
+ */
+ return result;
+}
+
+static int lov_sublock_release(const struct lu_env *env, struct lov_lock *lck,
+ int i, int deluser, int rc)
+{
+ struct cl_lock *parent = lck->lls_cl.cls_lock;
+
+ LASSERT(cl_lock_is_mutexed(parent));
+
+ if (lck->lls_sub[i].sub_flags & LSF_HELD) {
+ struct cl_lock *sublock;
+ int dying;
+
+ LASSERT(lck->lls_sub[i].sub_lock != NULL);
+ sublock = lck->lls_sub[i].sub_lock->lss_cl.cls_lock;
+ LASSERT(cl_lock_is_mutexed(sublock));
+
+ lck->lls_sub[i].sub_flags &= ~LSF_HELD;
+ if (deluser)
+ cl_lock_user_del(env, sublock);
+ /*
+ * If the last hold is released, and cancellation is pending
+ * for a sub-lock, release parent mutex, to avoid keeping it
+ * while sub-lock is being paged out.
+ */
+ dying = (sublock->cll_descr.cld_mode == CLM_PHANTOM ||
+ sublock->cll_descr.cld_mode == CLM_GROUP ||
+ (sublock->cll_flags & (CLF_CANCELPEND|CLF_DOOMED))) &&
+ sublock->cll_holds == 1;
+ if (dying)
+ cl_lock_mutex_put(env, parent);
+ cl_lock_unhold(env, sublock, "lov-parent", parent);
+ if (dying) {
+ cl_lock_mutex_get(env, parent);
+ rc = lov_subresult(rc, CLO_REPEAT);
+ }
+ /*
+ * From now on lck->lls_sub[i].sub_lock is a "weak" pointer,
+ * not backed by a reference on a
+ * sub-lock. lovsub_lock_delete() will clear
+ * lck->lls_sub[i].sub_lock under semaphores, just before
+ * sub-lock is destroyed.
+ */
+ }
+ return rc;
+}
+
+static void lov_sublock_hold(const struct lu_env *env, struct lov_lock *lck,
+ int i)
+{
+ struct cl_lock *parent = lck->lls_cl.cls_lock;
+
+ LASSERT(cl_lock_is_mutexed(parent));
+
+ if (!(lck->lls_sub[i].sub_flags & LSF_HELD)) {
+ struct cl_lock *sublock;
+
+ LASSERT(lck->lls_sub[i].sub_lock != NULL);
+ sublock = lck->lls_sub[i].sub_lock->lss_cl.cls_lock;
+ LASSERT(cl_lock_is_mutexed(sublock));
+ LASSERT(sublock->cll_state != CLS_FREEING);
+
+ lck->lls_sub[i].sub_flags |= LSF_HELD;
+
+ cl_lock_get_trust(sublock);
+ cl_lock_hold_add(env, sublock, "lov-parent", parent);
+ cl_lock_user_add(env, sublock);
+ cl_lock_put(env, sublock);
+ }
+}
+
+static void lov_lock_fini(const struct lu_env *env,
+ struct cl_lock_slice *slice)
+{
+ struct lov_lock *lck;
+ int i;
+
+ lck = cl2lov_lock(slice);
+ LASSERT(lck->lls_nr_filled == 0);
+ if (lck->lls_sub != NULL) {
+ for (i = 0; i < lck->lls_nr; ++i)
+ /*
+ * No sub-locks exists at this point, as sub-lock has
+ * a reference on its parent.
+ */
+ LASSERT(lck->lls_sub[i].sub_lock == NULL);
+ OBD_FREE_LARGE(lck->lls_sub,
+ lck->lls_nr * sizeof(lck->lls_sub[0]));
+ }
+ OBD_SLAB_FREE_PTR(lck, lov_lock_kmem);
+}
+
+static int lov_lock_enqueue_wait(const struct lu_env *env,
+ struct lov_lock *lck,
+ struct cl_lock *sublock)
+{
+ struct cl_lock *lock = lck->lls_cl.cls_lock;
+ int result;
+
+ LASSERT(cl_lock_is_mutexed(lock));
+
+ cl_lock_mutex_put(env, lock);
+ result = cl_lock_enqueue_wait(env, sublock, 0);
+ cl_lock_mutex_get(env, lock);
+ return result ?: CLO_REPEAT;
+}
+
+/**
+ * Tries to advance a state machine of a given sub-lock toward enqueuing of
+ * the top-lock.
+ *
+ * \retval 0 if state-transition can proceed
+ * \retval -ve otherwise.
+ */
+static int lov_lock_enqueue_one(const struct lu_env *env, struct lov_lock *lck,
+ struct cl_lock *sublock,
+ struct cl_io *io, __u32 enqflags, int last)
+{
+ int result;
+
+ /* first, try to enqueue a sub-lock ... */
+ result = cl_enqueue_try(env, sublock, io, enqflags);
+ if ((sublock->cll_state == CLS_ENQUEUED) && !(enqflags & CEF_AGL)) {
+ /* if it is enqueued, try to `wait' on it---maybe it's already
+ * granted */
+ result = cl_wait_try(env, sublock);
+ if (result == CLO_REENQUEUED)
+ result = CLO_WAIT;
+ }
+ /*
+ * If CEF_ASYNC flag is set, then all sub-locks can be enqueued in
+ * parallel, otherwise---enqueue has to wait until sub-lock is granted
+ * before proceeding to the next one.
+ */
+ if ((result == CLO_WAIT) && (sublock->cll_state <= CLS_HELD) &&
+ (enqflags & CEF_ASYNC) && (!last || (enqflags & CEF_AGL)))
+ result = 0;
+ return result;
+}
+
+/**
+ * Helper function for lov_lock_enqueue() that creates missing sub-lock.
+ */
+static int lov_sublock_fill(const struct lu_env *env, struct cl_lock *parent,
+ struct cl_io *io, struct lov_lock *lck, int idx)
+{
+ struct lov_lock_link *link = NULL;
+ struct cl_lock *sublock;
+ int result;
+
+ LASSERT(parent->cll_depth == 1);
+ cl_lock_mutex_put(env, parent);
+ sublock = lov_sublock_alloc(env, io, lck, idx, &link);
+ if (!IS_ERR(sublock))
+ cl_lock_mutex_get(env, sublock);
+ cl_lock_mutex_get(env, parent);
+
+ if (!IS_ERR(sublock)) {
+ cl_lock_get_trust(sublock);
+ if (parent->cll_state == CLS_QUEUING &&
+ lck->lls_sub[idx].sub_lock == NULL) {
+ lov_sublock_adopt(env, lck, sublock, idx, link);
+ } else {
+ OBD_SLAB_FREE_PTR(link, lov_lock_link_kmem);
+ /* other thread allocated sub-lock, or enqueue is no
+ * longer going on */
+ cl_lock_mutex_put(env, parent);
+ cl_lock_unhold(env, sublock, "lov-parent", parent);
+ cl_lock_mutex_get(env, parent);
+ }
+ cl_lock_mutex_put(env, sublock);
+ cl_lock_put(env, sublock);
+ result = CLO_REPEAT;
+ } else
+ result = PTR_ERR(sublock);
+ return result;
+}
+
+/**
+ * Implementation of cl_lock_operations::clo_enqueue() for lov layer. This
+ * function is rather subtle, as it enqueues top-lock (i.e., advances top-lock
+ * state machine from CLS_QUEUING to CLS_ENQUEUED states) by juggling sub-lock
+ * state machines in the face of sub-locks sharing (by multiple top-locks),
+ * and concurrent sub-lock cancellations.
+ */
+static int lov_lock_enqueue(const struct lu_env *env,
+ const struct cl_lock_slice *slice,
+ struct cl_io *io, __u32 enqflags)
+{
+ struct cl_lock *lock = slice->cls_lock;
+ struct lov_lock *lck = cl2lov_lock(slice);
+ struct cl_lock_closure *closure = lov_closure_get(env, lock);
+ int i;
+ int result;
+ enum cl_lock_state minstate;
+
+ for (result = 0, minstate = CLS_FREEING, i = 0; i < lck->lls_nr; ++i) {
+ int rc;
+ struct lovsub_lock *sub;
+ struct lov_lock_sub *lls;
+ struct cl_lock *sublock;
+ struct lov_sublock_env *subenv;
+
+ if (lock->cll_state != CLS_QUEUING) {
+ /*
+ * Lock might have left QUEUING state if previous
+ * iteration released its mutex. Stop enqueing in this
+ * case and let the upper layer to decide what to do.
+ */
+ LASSERT(i > 0 && result != 0);
+ break;
+ }
+
+ lls = &lck->lls_sub[i];
+ sub = lls->sub_lock;
+ /*
+ * Sub-lock might have been canceled, while top-lock was
+ * cached.
+ */
+ if (sub == NULL) {
+ result = lov_sublock_fill(env, lock, io, lck, i);
+ /* lov_sublock_fill() released @lock mutex,
+ * restart. */
+ break;
+ }
+ sublock = sub->lss_cl.cls_lock;
+ rc = lov_sublock_lock(env, lck, lls, closure, &subenv);
+ if (rc == 0) {
+ lov_sublock_hold(env, lck, i);
+ rc = lov_lock_enqueue_one(subenv->lse_env, lck, sublock,
+ subenv->lse_io, enqflags,
+ i == lck->lls_nr - 1);
+ minstate = min(minstate, sublock->cll_state);
+ if (rc == CLO_WAIT) {
+ switch (sublock->cll_state) {
+ case CLS_QUEUING:
+ /* take recursive mutex, the lock is
+ * released in lov_lock_enqueue_wait.
+ */
+ cl_lock_mutex_get(env, sublock);
+ lov_sublock_unlock(env, sub, closure,
+ subenv);
+ rc = lov_lock_enqueue_wait(env, lck,
+ sublock);
+ break;
+ case CLS_CACHED:
+ cl_lock_get(sublock);
+ /* take recursive mutex of sublock */
+ cl_lock_mutex_get(env, sublock);
+ /* need to release all locks in closure
+ * otherwise it may deadlock. LU-2683.*/
+ lov_sublock_unlock(env, sub, closure,
+ subenv);
+ /* sublock and parent are held. */
+ rc = lov_sublock_release(env, lck, i,
+ 1, rc);
+ cl_lock_mutex_put(env, sublock);
+ cl_lock_put(env, sublock);
+ break;
+ default:
+ lov_sublock_unlock(env, sub, closure,
+ subenv);
+ break;
+ }
+ } else {
+ LASSERT(sublock->cll_conflict == NULL);
+ lov_sublock_unlock(env, sub, closure, subenv);
+ }
+ }
+ result = lov_subresult(result, rc);
+ if (result != 0)
+ break;
+ }
+ cl_lock_closure_fini(closure);
+ return result ?: minstate >= CLS_ENQUEUED ? 0 : CLO_WAIT;
+}
+
+static int lov_lock_unuse(const struct lu_env *env,
+ const struct cl_lock_slice *slice)
+{
+ struct lov_lock *lck = cl2lov_lock(slice);
+ struct cl_lock_closure *closure = lov_closure_get(env, slice->cls_lock);
+ int i;
+ int result;
+
+ for (result = 0, i = 0; i < lck->lls_nr; ++i) {
+ int rc;
+ struct lovsub_lock *sub;
+ struct cl_lock *sublock;
+ struct lov_lock_sub *lls;
+ struct lov_sublock_env *subenv;
+
+ /* top-lock state cannot change concurrently, because single
+ * thread (one that released the last hold) carries unlocking
+ * to the completion. */
+ LASSERT(slice->cls_lock->cll_state == CLS_INTRANSIT);
+ lls = &lck->lls_sub[i];
+ sub = lls->sub_lock;
+ if (sub == NULL)
+ continue;
+
+ sublock = sub->lss_cl.cls_lock;
+ rc = lov_sublock_lock(env, lck, lls, closure, &subenv);
+ if (rc == 0) {
+ if (lls->sub_flags & LSF_HELD) {
+ LASSERT(sublock->cll_state == CLS_HELD ||
+ sublock->cll_state == CLS_ENQUEUED);
+ rc = cl_unuse_try(subenv->lse_env, sublock);
+ rc = lov_sublock_release(env, lck, i, 0, rc);
+ }
+ lov_sublock_unlock(env, sub, closure, subenv);
+ }
+ result = lov_subresult(result, rc);
+ }
+
+ if (result == 0 && lck->lls_cancel_race) {
+ lck->lls_cancel_race = 0;
+ result = -ESTALE;
+ }
+ cl_lock_closure_fini(closure);
+ return result;
+}
+
+
+static void lov_lock_cancel(const struct lu_env *env,
+ const struct cl_lock_slice *slice)
+{
+ struct lov_lock *lck = cl2lov_lock(slice);
+ struct cl_lock_closure *closure = lov_closure_get(env, slice->cls_lock);
+ int i;
+ int result;
+
+ for (result = 0, i = 0; i < lck->lls_nr; ++i) {
+ int rc;
+ struct lovsub_lock *sub;
+ struct cl_lock *sublock;
+ struct lov_lock_sub *lls;
+ struct lov_sublock_env *subenv;
+
+ /* top-lock state cannot change concurrently, because single
+ * thread (one that released the last hold) carries unlocking
+ * to the completion. */
+ lls = &lck->lls_sub[i];
+ sub = lls->sub_lock;
+ if (sub == NULL)
+ continue;
+
+ sublock = sub->lss_cl.cls_lock;
+ rc = lov_sublock_lock(env, lck, lls, closure, &subenv);
+ if (rc == 0) {
+ if (!(lls->sub_flags & LSF_HELD)) {
+ lov_sublock_unlock(env, sub, closure, subenv);
+ continue;
+ }
+
+ switch (sublock->cll_state) {
+ case CLS_HELD:
+ rc = cl_unuse_try(subenv->lse_env, sublock);
+ lov_sublock_release(env, lck, i, 0, 0);
+ break;
+ default:
+ lov_sublock_release(env, lck, i, 1, 0);
+ break;
+ }
+ lov_sublock_unlock(env, sub, closure, subenv);
+ }
+
+ if (rc == CLO_REPEAT) {
+ --i;
+ continue;
+ }
+
+ result = lov_subresult(result, rc);
+ }
+
+ if (result)
+ CL_LOCK_DEBUG(D_ERROR, env, slice->cls_lock,
+ "lov_lock_cancel fails with %d.\n", result);
+
+ cl_lock_closure_fini(closure);
+}
+
+static int lov_lock_wait(const struct lu_env *env,
+ const struct cl_lock_slice *slice)
+{
+ struct lov_lock *lck = cl2lov_lock(slice);
+ struct cl_lock_closure *closure = lov_closure_get(env, slice->cls_lock);
+ enum cl_lock_state minstate;
+ int reenqueued;
+ int result;
+ int i;
+
+again:
+ for (result = 0, minstate = CLS_FREEING, i = 0, reenqueued = 0;
+ i < lck->lls_nr; ++i) {
+ int rc;
+ struct lovsub_lock *sub;
+ struct cl_lock *sublock;
+ struct lov_lock_sub *lls;
+ struct lov_sublock_env *subenv;
+
+ lls = &lck->lls_sub[i];
+ sub = lls->sub_lock;
+ LASSERT(sub != NULL);
+ sublock = sub->lss_cl.cls_lock;
+ rc = lov_sublock_lock(env, lck, lls, closure, &subenv);
+ if (rc == 0) {
+ LASSERT(sublock->cll_state >= CLS_ENQUEUED);
+ if (sublock->cll_state < CLS_HELD)
+ rc = cl_wait_try(env, sublock);
+
+ minstate = min(minstate, sublock->cll_state);
+ lov_sublock_unlock(env, sub, closure, subenv);
+ }
+ if (rc == CLO_REENQUEUED) {
+ reenqueued++;
+ rc = 0;
+ }
+ result = lov_subresult(result, rc);
+ if (result != 0)
+ break;
+ }
+ /* Each sublock only can be reenqueued once, so will not loop for
+ * ever. */
+ if (result == 0 && reenqueued != 0)
+ goto again;
+ cl_lock_closure_fini(closure);
+ return result ?: minstate >= CLS_HELD ? 0 : CLO_WAIT;
+}
+
+static int lov_lock_use(const struct lu_env *env,
+ const struct cl_lock_slice *slice)
+{
+ struct lov_lock *lck = cl2lov_lock(slice);
+ struct cl_lock_closure *closure = lov_closure_get(env, slice->cls_lock);
+ int result;
+ int i;
+
+ LASSERT(slice->cls_lock->cll_state == CLS_INTRANSIT);
+
+ for (result = 0, i = 0; i < lck->lls_nr; ++i) {
+ int rc;
+ struct lovsub_lock *sub;
+ struct cl_lock *sublock;
+ struct lov_lock_sub *lls;
+ struct lov_sublock_env *subenv;
+
+ LASSERT(slice->cls_lock->cll_state == CLS_INTRANSIT);
+
+ lls = &lck->lls_sub[i];
+ sub = lls->sub_lock;
+ if (sub == NULL) {
+ /*
+ * Sub-lock might have been canceled, while top-lock was
+ * cached.
+ */
+ result = -ESTALE;
+ break;
+ }
+
+ sublock = sub->lss_cl.cls_lock;
+ rc = lov_sublock_lock(env, lck, lls, closure, &subenv);
+ if (rc == 0) {
+ LASSERT(sublock->cll_state != CLS_FREEING);
+ lov_sublock_hold(env, lck, i);
+ if (sublock->cll_state == CLS_CACHED) {
+ rc = cl_use_try(subenv->lse_env, sublock, 0);
+ if (rc != 0)
+ rc = lov_sublock_release(env, lck,
+ i, 1, rc);
+ } else if (sublock->cll_state == CLS_NEW) {
+ /* Sub-lock might have been canceled, while
+ * top-lock was cached. */
+ result = -ESTALE;
+ lov_sublock_release(env, lck, i, 1, result);
+ }
+ lov_sublock_unlock(env, sub, closure, subenv);
+ }
+ result = lov_subresult(result, rc);
+ if (result != 0)
+ break;
+ }
+
+ if (lck->lls_cancel_race) {
+ /*
+ * If there is unlocking happened at the same time, then
+ * sublock_lock state should be FREEING, and lov_sublock_lock
+ * should return CLO_REPEAT. In this case, it should return
+ * ESTALE, and up layer should reset the lock state to be NEW.
+ */
+ lck->lls_cancel_race = 0;
+ LASSERT(result != 0);
+ result = -ESTALE;
+ }
+ cl_lock_closure_fini(closure);
+ return result;
+}
+
+#if 0
+static int lock_lock_multi_match()
+{
+ struct cl_lock *lock = slice->cls_lock;
+ struct cl_lock_descr *subneed = &lov_env_info(env)->lti_ldescr;
+ struct lov_object *loo = cl2lov(lov->lls_cl.cls_obj);
+ struct lov_layout_raid0 *r0 = lov_r0(loo);
+ struct lov_lock_sub *sub;
+ struct cl_object *subobj;
+ u64 fstart;
+ u64 fend;
+ u64 start;
+ u64 end;
+ int i;
+
+ fstart = cl_offset(need->cld_obj, need->cld_start);
+ fend = cl_offset(need->cld_obj, need->cld_end + 1) - 1;
+ subneed->cld_mode = need->cld_mode;
+ cl_lock_mutex_get(env, lock);
+ for (i = 0; i < lov->lls_nr; ++i) {
+ sub = &lov->lls_sub[i];
+ if (sub->sub_lock == NULL)
+ continue;
+ subobj = sub->sub_descr.cld_obj;
+ if (!lov_stripe_intersects(loo->lo_lsm, sub->sub_stripe,
+ fstart, fend, &start, &end))
+ continue;
+ subneed->cld_start = cl_index(subobj, start);
+ subneed->cld_end = cl_index(subobj, end);
+ subneed->cld_obj = subobj;
+ if (!cl_lock_ext_match(&sub->sub_got, subneed)) {
+ result = 0;
+ break;
+ }
+ }
+ cl_lock_mutex_put(env, lock);
+}
+#endif
+
+/**
+ * Check if the extent region \a descr is covered by \a child against the
+ * specific \a stripe.
+ */
+static int lov_lock_stripe_is_matching(const struct lu_env *env,
+ struct lov_object *lov, int stripe,
+ const struct cl_lock_descr *child,
+ const struct cl_lock_descr *descr)
+{
+ struct lov_stripe_md *lsm = lov->lo_lsm;
+ u64 start;
+ u64 end;
+ int result;
+
+ if (lov_r0(lov)->lo_nr == 1)
+ return cl_lock_ext_match(child, descr);
+
+ /*
+ * For a multi-stripes object:
+ * - make sure the descr only covers child's stripe, and
+ * - check if extent is matching.
+ */
+ start = cl_offset(&lov->lo_cl, descr->cld_start);
+ end = cl_offset(&lov->lo_cl, descr->cld_end + 1) - 1;
+ result = 0;
+ /* glimpse should work on the object with LOV EA hole. */
+ if (end - start <= lsm->lsm_stripe_size) {
+ int idx;
+
+ idx = lov_stripe_number(lsm, start);
+ if (idx == stripe ||
+ unlikely(lov_r0(lov)->lo_sub[idx] == NULL)) {
+ idx = lov_stripe_number(lsm, end);
+ if (idx == stripe ||
+ unlikely(lov_r0(lov)->lo_sub[idx] == NULL))
+ result = 1;
+ }
+ }
+
+ if (result != 0) {
+ struct cl_lock_descr *subd = &lov_env_info(env)->lti_ldescr;
+ u64 sub_start;
+ u64 sub_end;
+
+ subd->cld_obj = NULL; /* don't need sub object at all */
+ subd->cld_mode = descr->cld_mode;
+ subd->cld_gid = descr->cld_gid;
+ result = lov_stripe_intersects(lsm, stripe, start, end,
+ &sub_start, &sub_end);
+ LASSERT(result);
+ subd->cld_start = cl_index(child->cld_obj, sub_start);
+ subd->cld_end = cl_index(child->cld_obj, sub_end);
+ result = cl_lock_ext_match(child, subd);
+ }
+ return result;
+}
+
+/**
+ * An implementation of cl_lock_operations::clo_fits_into() method.
+ *
+ * Checks whether a lock (given by \a slice) is suitable for \a
+ * io. Multi-stripe locks can be used only for "quick" io, like truncate, or
+ * O_APPEND write.
+ *
+ * \see ccc_lock_fits_into().
+ */
+static int lov_lock_fits_into(const struct lu_env *env,
+ const struct cl_lock_slice *slice,
+ const struct cl_lock_descr *need,
+ const struct cl_io *io)
+{
+ struct lov_lock *lov = cl2lov_lock(slice);
+ struct lov_object *obj = cl2lov(slice->cls_obj);
+ int result;
+
+ LASSERT(cl_object_same(need->cld_obj, slice->cls_obj));
+ LASSERT(lov->lls_nr > 0);
+
+ /* for top lock, it's necessary to match enq flags otherwise it will
+ * run into problem if a sublock is missing and reenqueue. */
+ if (need->cld_enq_flags != lov->lls_orig.cld_enq_flags)
+ return 0;
+
+ if (need->cld_mode == CLM_GROUP)
+ /*
+ * always allow to match group lock.
+ */
+ result = cl_lock_ext_match(&lov->lls_orig, need);
+ else if (lov->lls_nr == 1) {
+ struct cl_lock_descr *got = &lov->lls_sub[0].sub_got;
+ result = lov_lock_stripe_is_matching(env,
+ cl2lov(slice->cls_obj),
+ lov->lls_sub[0].sub_stripe,
+ got, need);
+ } else if (io->ci_type != CIT_SETATTR && io->ci_type != CIT_MISC &&
+ !cl_io_is_append(io) && need->cld_mode != CLM_PHANTOM)
+ /*
+ * Multi-stripe locks are only suitable for `quick' IO and for
+ * glimpse.
+ */
+ result = 0;
+ else
+ /*
+ * Most general case: multi-stripe existing lock, and
+ * (potentially) multi-stripe @need lock. Check that @need is
+ * covered by @lov's sub-locks.
+ *
+ * For now, ignore lock expansions made by the server, and
+ * match against original lock extent.
+ */
+ result = cl_lock_ext_match(&lov->lls_orig, need);
+ CDEBUG(D_DLMTRACE, DDESCR"/"DDESCR" %d %d/%d: %d\n",
+ PDESCR(&lov->lls_orig), PDESCR(&lov->lls_sub[0].sub_got),
+ lov->lls_sub[0].sub_stripe, lov->lls_nr, lov_r0(obj)->lo_nr,
+ result);
+ return result;
+}
+
+void lov_lock_unlink(const struct lu_env *env,
+ struct lov_lock_link *link, struct lovsub_lock *sub)
+{
+ struct lov_lock *lck = link->lll_super;
+ struct cl_lock *parent = lck->lls_cl.cls_lock;
+
+ LASSERT(cl_lock_is_mutexed(parent));
+ LASSERT(cl_lock_is_mutexed(sub->lss_cl.cls_lock));
+
+ list_del_init(&link->lll_list);
+ LASSERT(lck->lls_sub[link->lll_idx].sub_lock == sub);
+ /* yank this sub-lock from parent's array */
+ lck->lls_sub[link->lll_idx].sub_lock = NULL;
+ LASSERT(lck->lls_nr_filled > 0);
+ lck->lls_nr_filled--;
+ lu_ref_del(&parent->cll_reference, "lov-child", sub->lss_cl.cls_lock);
+ cl_lock_put(env, parent);
+ OBD_SLAB_FREE_PTR(link, lov_lock_link_kmem);
+}
+
+struct lov_lock_link *lov_lock_link_find(const struct lu_env *env,
+ struct lov_lock *lck,
+ struct lovsub_lock *sub)
+{
+ struct lov_lock_link *scan;
+
+ LASSERT(cl_lock_is_mutexed(sub->lss_cl.cls_lock));
+
+ list_for_each_entry(scan, &sub->lss_parents, lll_list) {
+ if (scan->lll_super == lck)
+ return scan;
+ }
+ return NULL;
+}
+
+/**
+ * An implementation of cl_lock_operations::clo_delete() method. This is
+ * invoked for "top-to-bottom" delete, when lock destruction starts from the
+ * top-lock, e.g., as a result of inode destruction.
+ *
+ * Unlinks top-lock from all its sub-locks. Sub-locks are not deleted there:
+ * this is done separately elsewhere:
+ *
+ * - for inode destruction, lov_object_delete() calls cl_object_kill() for
+ * each sub-object, purging its locks;
+ *
+ * - in other cases (e.g., a fatal error with a top-lock) sub-locks are
+ * left in the cache.
+ */
+static void lov_lock_delete(const struct lu_env *env,
+ const struct cl_lock_slice *slice)
+{
+ struct lov_lock *lck = cl2lov_lock(slice);
+ struct cl_lock_closure *closure = lov_closure_get(env, slice->cls_lock);
+ struct lov_lock_link *link;
+ int rc;
+ int i;
+
+ LASSERT(slice->cls_lock->cll_state == CLS_FREEING);
+
+ for (i = 0; i < lck->lls_nr; ++i) {
+ struct lov_lock_sub *lls = &lck->lls_sub[i];
+ struct lovsub_lock *lsl = lls->sub_lock;
+
+ if (lsl == NULL) /* already removed */
+ continue;
+
+ rc = lov_sublock_lock(env, lck, lls, closure, NULL);
+ if (rc == CLO_REPEAT) {
+ --i;
+ continue;
+ }
+
+ LASSERT(rc == 0);
+ LASSERT(lsl->lss_cl.cls_lock->cll_state < CLS_FREEING);
+
+ if (lls->sub_flags & LSF_HELD)
+ lov_sublock_release(env, lck, i, 1, 0);
+
+ link = lov_lock_link_find(env, lck, lsl);
+ LASSERT(link != NULL);
+ lov_lock_unlink(env, link, lsl);
+ LASSERT(lck->lls_sub[i].sub_lock == NULL);
+
+ lov_sublock_unlock(env, lsl, closure, NULL);
+ }
+
+ cl_lock_closure_fini(closure);
+}
+
+static int lov_lock_print(const struct lu_env *env, void *cookie,
+ lu_printer_t p, const struct cl_lock_slice *slice)
+{
+ struct lov_lock *lck = cl2lov_lock(slice);
+ int i;
+
+ (*p)(env, cookie, "%d\n", lck->lls_nr);
+ for (i = 0; i < lck->lls_nr; ++i) {
+ struct lov_lock_sub *sub;
+
+ sub = &lck->lls_sub[i];
+ (*p)(env, cookie, " %d %x: ", i, sub->sub_flags);
+ if (sub->sub_lock != NULL)
+ cl_lock_print(env, cookie, p,
+ sub->sub_lock->lss_cl.cls_lock);
+ else
+ (*p)(env, cookie, "---\n");
+ }
+ return 0;
+}
+
+static const struct cl_lock_operations lov_lock_ops = {
+ .clo_fini = lov_lock_fini,
+ .clo_enqueue = lov_lock_enqueue,
+ .clo_wait = lov_lock_wait,
+ .clo_use = lov_lock_use,
+ .clo_unuse = lov_lock_unuse,
+ .clo_cancel = lov_lock_cancel,
+ .clo_fits_into = lov_lock_fits_into,
+ .clo_delete = lov_lock_delete,
+ .clo_print = lov_lock_print
+};
+
+int lov_lock_init_raid0(const struct lu_env *env, struct cl_object *obj,
+ struct cl_lock *lock, const struct cl_io *io)
+{
+ struct lov_lock *lck;
+ int result;
+
+ OBD_SLAB_ALLOC_PTR_GFP(lck, lov_lock_kmem, GFP_NOFS);
+ if (lck != NULL) {
+ cl_lock_slice_add(lock, &lck->lls_cl, obj, &lov_lock_ops);
+ result = lov_lock_sub_init(env, lck, io);
+ } else
+ result = -ENOMEM;
+ return result;
+}
+
+static void lov_empty_lock_fini(const struct lu_env *env,
+ struct cl_lock_slice *slice)
+{
+ struct lov_lock *lck = cl2lov_lock(slice);
+ OBD_SLAB_FREE_PTR(lck, lov_lock_kmem);
+}
+
+static int lov_empty_lock_print(const struct lu_env *env, void *cookie,
+ lu_printer_t p, const struct cl_lock_slice *slice)
+{
+ (*p)(env, cookie, "empty\n");
+ return 0;
+}
+
+/* XXX: more methods will be added later. */
+static const struct cl_lock_operations lov_empty_lock_ops = {
+ .clo_fini = lov_empty_lock_fini,
+ .clo_print = lov_empty_lock_print
+};
+
+int lov_lock_init_empty(const struct lu_env *env, struct cl_object *obj,
+ struct cl_lock *lock, const struct cl_io *io)
+{
+ struct lov_lock *lck;
+ int result = -ENOMEM;
+
+ OBD_SLAB_ALLOC_PTR_GFP(lck, lov_lock_kmem, GFP_NOFS);
+ if (lck != NULL) {
+ cl_lock_slice_add(lock, &lck->lls_cl, obj, &lov_empty_lock_ops);
+ lck->lls_orig = lock->cll_descr;
+ result = 0;
+ }
+ return result;
+}
+
+static struct cl_lock_closure *lov_closure_get(const struct lu_env *env,
+ struct cl_lock *parent)
+{
+ struct cl_lock_closure *closure;
+
+ closure = &lov_env_info(env)->lti_closure;
+ LASSERT(list_empty(&closure->clc_list));
+ cl_lock_closure_init(env, closure, parent, 1);
+ return closure;
+}
+
+
+/** @} lov */
diff --git a/drivers/staging/lustre/lustre/lov/lov_merge.c b/drivers/staging/lustre/lustre/lov/lov_merge.c
new file mode 100644
index 000000000..b7e7bfabe
--- /dev/null
+++ b/drivers/staging/lustre/lustre/lov/lov_merge.c
@@ -0,0 +1,186 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2012, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ */
+
+#define DEBUG_SUBSYSTEM S_LOV
+
+#include "../../include/linux/libcfs/libcfs.h"
+
+#include "../include/obd_class.h"
+#include "lov_internal.h"
+
+/** Merge the lock value block(&lvb) attributes and KMS from each of the
+ * stripes in a file into a single lvb. It is expected that the caller
+ * initializes the current atime, mtime, ctime to avoid regressing a more
+ * uptodate time on the local client.
+ */
+int lov_merge_lvb_kms(struct lov_stripe_md *lsm,
+ struct ost_lvb *lvb, __u64 *kms_place)
+{
+ __u64 size = 0;
+ __u64 kms = 0;
+ __u64 blocks = 0;
+ s64 current_mtime = lvb->lvb_mtime;
+ s64 current_atime = lvb->lvb_atime;
+ s64 current_ctime = lvb->lvb_ctime;
+ int i;
+ int rc = 0;
+
+ assert_spin_locked(&lsm->lsm_lock);
+ LASSERT(lsm->lsm_lock_owner == current_pid());
+
+ CDEBUG(D_INODE, "MDT ID "DOSTID" initial value: s=%llu m=%llu a=%llu c=%llu b=%llu\n",
+ POSTID(&lsm->lsm_oi), lvb->lvb_size, lvb->lvb_mtime,
+ lvb->lvb_atime, lvb->lvb_ctime, lvb->lvb_blocks);
+ for (i = 0; i < lsm->lsm_stripe_count; i++) {
+ struct lov_oinfo *loi = lsm->lsm_oinfo[i];
+ u64 lov_size, tmpsize;
+
+ if (OST_LVB_IS_ERR(loi->loi_lvb.lvb_blocks)) {
+ rc = OST_LVB_GET_ERR(loi->loi_lvb.lvb_blocks);
+ continue;
+ }
+
+ tmpsize = loi->loi_kms;
+ lov_size = lov_stripe_size(lsm, tmpsize, i);
+ if (lov_size > kms)
+ kms = lov_size;
+
+ if (loi->loi_lvb.lvb_size > tmpsize)
+ tmpsize = loi->loi_lvb.lvb_size;
+
+ lov_size = lov_stripe_size(lsm, tmpsize, i);
+ if (lov_size > size)
+ size = lov_size;
+ /* merge blocks, mtime, atime */
+ blocks += loi->loi_lvb.lvb_blocks;
+ if (loi->loi_lvb.lvb_mtime > current_mtime)
+ current_mtime = loi->loi_lvb.lvb_mtime;
+ if (loi->loi_lvb.lvb_atime > current_atime)
+ current_atime = loi->loi_lvb.lvb_atime;
+ if (loi->loi_lvb.lvb_ctime > current_ctime)
+ current_ctime = loi->loi_lvb.lvb_ctime;
+
+ CDEBUG(D_INODE, "MDT ID "DOSTID" on OST[%u]: s=%llu m=%llu a=%llu c=%llu b=%llu\n",
+ POSTID(&lsm->lsm_oi), loi->loi_ost_idx,
+ loi->loi_lvb.lvb_size, loi->loi_lvb.lvb_mtime,
+ loi->loi_lvb.lvb_atime, loi->loi_lvb.lvb_ctime,
+ loi->loi_lvb.lvb_blocks);
+ }
+
+ *kms_place = kms;
+ lvb->lvb_size = size;
+ lvb->lvb_blocks = blocks;
+ lvb->lvb_mtime = current_mtime;
+ lvb->lvb_atime = current_atime;
+ lvb->lvb_ctime = current_ctime;
+ return rc;
+}
+
+/* Must be called under the lov_stripe_lock() */
+int lov_adjust_kms(struct obd_export *exp, struct lov_stripe_md *lsm,
+ u64 size, int shrink)
+{
+ struct lov_oinfo *loi;
+ int stripe = 0;
+ __u64 kms;
+
+ assert_spin_locked(&lsm->lsm_lock);
+ LASSERT(lsm->lsm_lock_owner == current_pid());
+
+ if (shrink) {
+ for (; stripe < lsm->lsm_stripe_count; stripe++) {
+ struct lov_oinfo *loi = lsm->lsm_oinfo[stripe];
+ kms = lov_size_to_stripe(lsm, size, stripe);
+ CDEBUG(D_INODE,
+ "stripe %d KMS %sing %llu->%llu\n",
+ stripe, kms > loi->loi_kms ? "increase":"shrink",
+ loi->loi_kms, kms);
+ loi_kms_set(loi, loi->loi_lvb.lvb_size = kms);
+ }
+ return 0;
+ }
+
+ if (size > 0)
+ stripe = lov_stripe_number(lsm, size - 1);
+ kms = lov_size_to_stripe(lsm, size, stripe);
+ loi = lsm->lsm_oinfo[stripe];
+
+ CDEBUG(D_INODE, "stripe %d KMS %sincreasing %llu->%llu\n",
+ stripe, kms > loi->loi_kms ? "" : "not ", loi->loi_kms, kms);
+ if (kms > loi->loi_kms)
+ loi_kms_set(loi, kms);
+
+ return 0;
+}
+
+void lov_merge_attrs(struct obdo *tgt, struct obdo *src, u64 valid,
+ struct lov_stripe_md *lsm, int stripeno, int *set)
+{
+ valid &= src->o_valid;
+
+ if (*set) {
+ if (valid & OBD_MD_FLSIZE) {
+ /* this handles sparse files properly */
+ u64 lov_size;
+
+ lov_size = lov_stripe_size(lsm, src->o_size, stripeno);
+ if (lov_size > tgt->o_size)
+ tgt->o_size = lov_size;
+ }
+ if (valid & OBD_MD_FLBLOCKS)
+ tgt->o_blocks += src->o_blocks;
+ if (valid & OBD_MD_FLBLKSZ)
+ tgt->o_blksize += src->o_blksize;
+ if (valid & OBD_MD_FLCTIME && tgt->o_ctime < src->o_ctime)
+ tgt->o_ctime = src->o_ctime;
+ if (valid & OBD_MD_FLMTIME && tgt->o_mtime < src->o_mtime)
+ tgt->o_mtime = src->o_mtime;
+ if (valid & OBD_MD_FLDATAVERSION)
+ tgt->o_data_version += src->o_data_version;
+ } else {
+ memcpy(tgt, src, sizeof(*tgt));
+ tgt->o_oi = lsm->lsm_oi;
+ if (valid & OBD_MD_FLSIZE)
+ tgt->o_size = lov_stripe_size(lsm, src->o_size,
+ stripeno);
+ }
+
+ /* data_version needs to be valid on all stripes to be correct! */
+ if (!(valid & OBD_MD_FLDATAVERSION))
+ tgt->o_valid &= ~OBD_MD_FLDATAVERSION;
+
+ *set += 1;
+}
diff --git a/drivers/staging/lustre/lustre/lov/lov_obd.c b/drivers/staging/lustre/lustre/lov/lov_obd.c
new file mode 100644
index 000000000..027815766
--- /dev/null
+++ b/drivers/staging/lustre/lustre/lov/lov_obd.c
@@ -0,0 +1,2395 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2002, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2011, 2012, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ *
+ * lustre/lov/lov_obd.c
+ *
+ * Author: Phil Schwan <phil@clusterfs.com>
+ * Author: Peter Braam <braam@clusterfs.com>
+ * Author: Mike Shaver <shaver@clusterfs.com>
+ * Author: Nathan Rutman <nathan@clusterfs.com>
+ */
+
+#define DEBUG_SUBSYSTEM S_LOV
+#include "../../include/linux/libcfs/libcfs.h"
+
+#include "../include/obd_support.h"
+#include "../include/lustre_lib.h"
+#include "../include/lustre_net.h"
+#include "../include/lustre/lustre_idl.h"
+#include "../include/lustre_dlm.h"
+#include "../include/lustre_mds.h"
+#include "../include/obd_class.h"
+#include "../include/lprocfs_status.h"
+#include "../include/lustre_param.h"
+#include "../include/cl_object.h"
+#include "../include/lclient.h" /* for cl_client_lru */
+#include "../include/lustre/ll_fiemap.h"
+#include "../include/lustre_fid.h"
+
+#include "lov_internal.h"
+
+/* Keep a refcount of lov->tgt usage to prevent racing with addition/deletion.
+ Any function that expects lov_tgts to remain stationary must take a ref. */
+static void lov_getref(struct obd_device *obd)
+{
+ struct lov_obd *lov = &obd->u.lov;
+
+ /* nobody gets through here until lov_putref is done */
+ mutex_lock(&lov->lov_lock);
+ atomic_inc(&lov->lov_refcount);
+ mutex_unlock(&lov->lov_lock);
+ return;
+}
+
+static void __lov_del_obd(struct obd_device *obd, struct lov_tgt_desc *tgt);
+
+static void lov_putref(struct obd_device *obd)
+{
+ struct lov_obd *lov = &obd->u.lov;
+
+ mutex_lock(&lov->lov_lock);
+ /* ok to dec to 0 more than once -- ltd_exp's will be null */
+ if (atomic_dec_and_test(&lov->lov_refcount) && lov->lov_death_row) {
+ LIST_HEAD(kill);
+ int i;
+ struct lov_tgt_desc *tgt, *n;
+ CDEBUG(D_CONFIG, "destroying %d lov targets\n",
+ lov->lov_death_row);
+ for (i = 0; i < lov->desc.ld_tgt_count; i++) {
+ tgt = lov->lov_tgts[i];
+
+ if (!tgt || !tgt->ltd_reap)
+ continue;
+ list_add(&tgt->ltd_kill, &kill);
+ /* XXX - right now there is a dependency on ld_tgt_count
+ * being the maximum tgt index for computing the
+ * mds_max_easize. So we can't shrink it. */
+ lov_ost_pool_remove(&lov->lov_packed, i);
+ lov->lov_tgts[i] = NULL;
+ lov->lov_death_row--;
+ }
+ mutex_unlock(&lov->lov_lock);
+
+ list_for_each_entry_safe(tgt, n, &kill, ltd_kill) {
+ list_del(&tgt->ltd_kill);
+ /* Disconnect */
+ __lov_del_obd(obd, tgt);
+ }
+ } else {
+ mutex_unlock(&lov->lov_lock);
+ }
+}
+
+static int lov_set_osc_active(struct obd_device *obd, struct obd_uuid *uuid,
+ enum obd_notify_event ev);
+static int lov_notify(struct obd_device *obd, struct obd_device *watched,
+ enum obd_notify_event ev, void *data);
+
+
+#define MAX_STRING_SIZE 128
+int lov_connect_obd(struct obd_device *obd, __u32 index, int activate,
+ struct obd_connect_data *data)
+{
+ struct lov_obd *lov = &obd->u.lov;
+ struct obd_uuid *tgt_uuid;
+ struct obd_device *tgt_obd;
+ static struct obd_uuid lov_osc_uuid = { "LOV_OSC_UUID" };
+ struct obd_import *imp;
+ struct proc_dir_entry *lov_proc_dir;
+ int rc;
+
+ if (!lov->lov_tgts[index])
+ return -EINVAL;
+
+ tgt_uuid = &lov->lov_tgts[index]->ltd_uuid;
+ tgt_obd = lov->lov_tgts[index]->ltd_obd;
+
+ if (!tgt_obd->obd_set_up) {
+ CERROR("Target %s not set up\n", obd_uuid2str(tgt_uuid));
+ return -EINVAL;
+ }
+
+ /* override the sp_me from lov */
+ tgt_obd->u.cli.cl_sp_me = lov->lov_sp_me;
+
+ if (data && (data->ocd_connect_flags & OBD_CONNECT_INDEX))
+ data->ocd_index = index;
+
+ /*
+ * Divine LOV knows that OBDs under it are OSCs.
+ */
+ imp = tgt_obd->u.cli.cl_import;
+
+ if (activate) {
+ tgt_obd->obd_no_recov = 0;
+ /* FIXME this is probably supposed to be
+ ptlrpc_set_import_active. Horrible naming. */
+ ptlrpc_activate_import(imp);
+ }
+
+ rc = obd_register_observer(tgt_obd, obd);
+ if (rc) {
+ CERROR("Target %s register_observer error %d\n",
+ obd_uuid2str(tgt_uuid), rc);
+ return rc;
+ }
+
+
+ if (imp->imp_invalid) {
+ CDEBUG(D_CONFIG, "not connecting OSC %s; administratively disabled\n",
+ obd_uuid2str(tgt_uuid));
+ return 0;
+ }
+
+ rc = obd_connect(NULL, &lov->lov_tgts[index]->ltd_exp, tgt_obd,
+ &lov_osc_uuid, data, NULL);
+ if (rc || !lov->lov_tgts[index]->ltd_exp) {
+ CERROR("Target %s connect error %d\n",
+ obd_uuid2str(tgt_uuid), rc);
+ return -ENODEV;
+ }
+
+ lov->lov_tgts[index]->ltd_reap = 0;
+
+ CDEBUG(D_CONFIG, "Connected tgt idx %d %s (%s) %sactive\n", index,
+ obd_uuid2str(tgt_uuid), tgt_obd->obd_name, activate ? "":"in");
+
+ lov_proc_dir = obd->obd_proc_private;
+ if (lov_proc_dir) {
+ struct obd_device *osc_obd = lov->lov_tgts[index]->ltd_exp->exp_obd;
+ struct proc_dir_entry *osc_symlink;
+
+ LASSERT(osc_obd != NULL);
+ LASSERT(osc_obd->obd_magic == OBD_DEVICE_MAGIC);
+ LASSERT(osc_obd->obd_type->typ_name != NULL);
+
+ osc_symlink = lprocfs_add_symlink(osc_obd->obd_name,
+ lov_proc_dir,
+ "../../../%s/%s",
+ osc_obd->obd_type->typ_name,
+ osc_obd->obd_name);
+ if (osc_symlink == NULL) {
+ CERROR("could not register LOV target /proc/fs/lustre/%s/%s/target_obds/%s.",
+ obd->obd_type->typ_name, obd->obd_name,
+ osc_obd->obd_name);
+ lprocfs_remove(&lov_proc_dir);
+ obd->obd_proc_private = NULL;
+ }
+ }
+
+ return 0;
+}
+
+static int lov_connect(const struct lu_env *env,
+ struct obd_export **exp, struct obd_device *obd,
+ struct obd_uuid *cluuid, struct obd_connect_data *data,
+ void *localdata)
+{
+ struct lov_obd *lov = &obd->u.lov;
+ struct lov_tgt_desc *tgt;
+ struct lustre_handle conn;
+ int i, rc;
+
+ CDEBUG(D_CONFIG, "connect #%d\n", lov->lov_connects);
+
+ rc = class_connect(&conn, obd, cluuid);
+ if (rc)
+ return rc;
+
+ *exp = class_conn2export(&conn);
+
+ /* Why should there ever be more than 1 connect? */
+ lov->lov_connects++;
+ LASSERT(lov->lov_connects == 1);
+
+ memset(&lov->lov_ocd, 0, sizeof(lov->lov_ocd));
+ if (data)
+ lov->lov_ocd = *data;
+
+ obd_getref(obd);
+ for (i = 0; i < lov->desc.ld_tgt_count; i++) {
+ tgt = lov->lov_tgts[i];
+ if (!tgt || obd_uuid_empty(&tgt->ltd_uuid))
+ continue;
+ /* Flags will be lowest common denominator */
+ rc = lov_connect_obd(obd, i, tgt->ltd_activate, &lov->lov_ocd);
+ if (rc) {
+ CERROR("%s: lov connect tgt %d failed: %d\n",
+ obd->obd_name, i, rc);
+ continue;
+ }
+ /* connect to administrative disabled ost */
+ if (!lov->lov_tgts[i]->ltd_exp)
+ continue;
+
+ rc = lov_notify(obd, lov->lov_tgts[i]->ltd_exp->exp_obd,
+ OBD_NOTIFY_CONNECT, (void *)&i);
+ if (rc) {
+ CERROR("%s error sending notify %d\n",
+ obd->obd_name, rc);
+ }
+ }
+ obd_putref(obd);
+
+ return 0;
+}
+
+static int lov_disconnect_obd(struct obd_device *obd, struct lov_tgt_desc *tgt)
+{
+ struct proc_dir_entry *lov_proc_dir;
+ struct lov_obd *lov = &obd->u.lov;
+ struct obd_device *osc_obd;
+ int rc;
+
+ osc_obd = class_exp2obd(tgt->ltd_exp);
+ CDEBUG(D_CONFIG, "%s: disconnecting target %s\n",
+ obd->obd_name, osc_obd ? osc_obd->obd_name : "NULL");
+
+ if (tgt->ltd_active) {
+ tgt->ltd_active = 0;
+ lov->desc.ld_active_tgt_count--;
+ tgt->ltd_exp->exp_obd->obd_inactive = 1;
+ }
+
+ if (osc_obd) {
+ lov_proc_dir = obd->obd_proc_private;
+ if (lov_proc_dir) {
+ lprocfs_remove_proc_entry(osc_obd->obd_name, lov_proc_dir);
+ }
+ /* Pass it on to our clients.
+ * XXX This should be an argument to disconnect,
+ * XXX not a back-door flag on the OBD. Ah well.
+ */
+ osc_obd->obd_force = obd->obd_force;
+ osc_obd->obd_fail = obd->obd_fail;
+ osc_obd->obd_no_recov = obd->obd_no_recov;
+ }
+
+ obd_register_observer(osc_obd, NULL);
+
+ rc = obd_disconnect(tgt->ltd_exp);
+ if (rc) {
+ CERROR("Target %s disconnect error %d\n",
+ tgt->ltd_uuid.uuid, rc);
+ rc = 0;
+ }
+
+ tgt->ltd_exp = NULL;
+ return 0;
+}
+
+static int lov_disconnect(struct obd_export *exp)
+{
+ struct obd_device *obd = class_exp2obd(exp);
+ struct lov_obd *lov = &obd->u.lov;
+ int i, rc;
+
+ if (!lov->lov_tgts)
+ goto out;
+
+ /* Only disconnect the underlying layers on the final disconnect. */
+ lov->lov_connects--;
+ if (lov->lov_connects != 0) {
+ /* why should there be more than 1 connect? */
+ CERROR("disconnect #%d\n", lov->lov_connects);
+ goto out;
+ }
+
+ /* Let's hold another reference so lov_del_obd doesn't spin through
+ putref every time */
+ obd_getref(obd);
+
+ for (i = 0; i < lov->desc.ld_tgt_count; i++) {
+ if (lov->lov_tgts[i] && lov->lov_tgts[i]->ltd_exp) {
+ /* Disconnection is the last we know about an obd */
+ lov_del_target(obd, i, NULL, lov->lov_tgts[i]->ltd_gen);
+ }
+ }
+ obd_putref(obd);
+
+out:
+ rc = class_disconnect(exp); /* bz 9811 */
+ return rc;
+}
+
+/* Error codes:
+ *
+ * -EINVAL : UUID can't be found in the LOV's target list
+ * -ENOTCONN: The UUID is found, but the target connection is bad (!)
+ * -EBADF : The UUID is found, but the OBD is the wrong type (!)
+ * any >= 0 : is log target index
+ */
+static int lov_set_osc_active(struct obd_device *obd, struct obd_uuid *uuid,
+ enum obd_notify_event ev)
+{
+ struct lov_obd *lov = &obd->u.lov;
+ struct lov_tgt_desc *tgt;
+ int index, activate, active;
+
+ CDEBUG(D_INFO, "Searching in lov %p for uuid %s event(%d)\n",
+ lov, uuid->uuid, ev);
+
+ obd_getref(obd);
+ for (index = 0; index < lov->desc.ld_tgt_count; index++) {
+ tgt = lov->lov_tgts[index];
+ if (!tgt)
+ continue;
+ /*
+ * LU-642, initially inactive OSC could miss the obd_connect,
+ * we make up for it here.
+ */
+ if (ev == OBD_NOTIFY_ACTIVATE && tgt->ltd_exp == NULL &&
+ obd_uuid_equals(uuid, &tgt->ltd_uuid)) {
+ struct obd_uuid lov_osc_uuid = {"LOV_OSC_UUID"};
+
+ obd_connect(NULL, &tgt->ltd_exp, tgt->ltd_obd,
+ &lov_osc_uuid, &lov->lov_ocd, NULL);
+ }
+ if (!tgt->ltd_exp)
+ continue;
+
+ CDEBUG(D_INFO, "lov idx %d is %s conn %#llx\n",
+ index, obd_uuid2str(&tgt->ltd_uuid),
+ tgt->ltd_exp->exp_handle.h_cookie);
+ if (obd_uuid_equals(uuid, &tgt->ltd_uuid))
+ break;
+ }
+
+ if (index == lov->desc.ld_tgt_count) {
+ index = -EINVAL;
+ goto out;
+ }
+
+ if (ev == OBD_NOTIFY_DEACTIVATE || ev == OBD_NOTIFY_ACTIVATE) {
+ activate = (ev == OBD_NOTIFY_ACTIVATE) ? 1 : 0;
+
+ if (lov->lov_tgts[index]->ltd_activate == activate) {
+ CDEBUG(D_INFO, "OSC %s already %sactivate!\n",
+ uuid->uuid, activate ? "" : "de");
+ } else {
+ lov->lov_tgts[index]->ltd_activate = activate;
+ CDEBUG(D_CONFIG, "%sactivate OSC %s\n",
+ activate ? "" : "de", obd_uuid2str(uuid));
+ }
+
+ } else if (ev == OBD_NOTIFY_INACTIVE || ev == OBD_NOTIFY_ACTIVE) {
+ active = (ev == OBD_NOTIFY_ACTIVE) ? 1 : 0;
+
+ if (lov->lov_tgts[index]->ltd_active == active) {
+ CDEBUG(D_INFO, "OSC %s already %sactive!\n",
+ uuid->uuid, active ? "" : "in");
+ goto out;
+ } else {
+ CDEBUG(D_CONFIG, "Marking OSC %s %sactive\n",
+ obd_uuid2str(uuid), active ? "" : "in");
+ }
+
+ lov->lov_tgts[index]->ltd_active = active;
+ if (active) {
+ lov->desc.ld_active_tgt_count++;
+ lov->lov_tgts[index]->ltd_exp->exp_obd->obd_inactive = 0;
+ } else {
+ lov->desc.ld_active_tgt_count--;
+ lov->lov_tgts[index]->ltd_exp->exp_obd->obd_inactive = 1;
+ }
+ } else {
+ CERROR("Unknown event(%d) for uuid %s", ev, uuid->uuid);
+ }
+
+ out:
+ obd_putref(obd);
+ return index;
+}
+
+static int lov_notify(struct obd_device *obd, struct obd_device *watched,
+ enum obd_notify_event ev, void *data)
+{
+ int rc = 0;
+ struct lov_obd *lov = &obd->u.lov;
+
+ down_read(&lov->lov_notify_lock);
+ if (!lov->lov_connects) {
+ up_read(&lov->lov_notify_lock);
+ return rc;
+ }
+
+ if (ev == OBD_NOTIFY_ACTIVE || ev == OBD_NOTIFY_INACTIVE ||
+ ev == OBD_NOTIFY_ACTIVATE || ev == OBD_NOTIFY_DEACTIVATE) {
+ struct obd_uuid *uuid;
+
+ LASSERT(watched);
+
+ if (strcmp(watched->obd_type->typ_name, LUSTRE_OSC_NAME)) {
+ up_read(&lov->lov_notify_lock);
+ CERROR("unexpected notification of %s %s!\n",
+ watched->obd_type->typ_name,
+ watched->obd_name);
+ return -EINVAL;
+ }
+ uuid = &watched->u.cli.cl_target_uuid;
+
+ /* Set OSC as active before notifying the observer, so the
+ * observer can use the OSC normally.
+ */
+ rc = lov_set_osc_active(obd, uuid, ev);
+ if (rc < 0) {
+ up_read(&lov->lov_notify_lock);
+ CERROR("event(%d) of %s failed: %d\n", ev,
+ obd_uuid2str(uuid), rc);
+ return rc;
+ }
+ /* active event should be pass lov target index as data */
+ data = &rc;
+ }
+
+ /* Pass the notification up the chain. */
+ if (watched) {
+ rc = obd_notify_observer(obd, watched, ev, data);
+ } else {
+ /* NULL watched means all osc's in the lov (only for syncs) */
+ /* sync event should be send lov idx as data */
+ struct lov_obd *lov = &obd->u.lov;
+ int i, is_sync;
+
+ data = &i;
+ is_sync = (ev == OBD_NOTIFY_SYNC) ||
+ (ev == OBD_NOTIFY_SYNC_NONBLOCK);
+
+ obd_getref(obd);
+ for (i = 0; i < lov->desc.ld_tgt_count; i++) {
+ if (!lov->lov_tgts[i])
+ continue;
+
+ /* don't send sync event if target not
+ * connected/activated */
+ if (is_sync && !lov->lov_tgts[i]->ltd_active)
+ continue;
+
+ rc = obd_notify_observer(obd, lov->lov_tgts[i]->ltd_obd,
+ ev, data);
+ if (rc) {
+ CERROR("%s: notify %s of %s failed %d\n",
+ obd->obd_name,
+ obd->obd_observer->obd_name,
+ lov->lov_tgts[i]->ltd_obd->obd_name,
+ rc);
+ }
+ }
+ obd_putref(obd);
+ }
+
+ up_read(&lov->lov_notify_lock);
+ return rc;
+}
+
+static int lov_add_target(struct obd_device *obd, struct obd_uuid *uuidp,
+ __u32 index, int gen, int active)
+{
+ struct lov_obd *lov = &obd->u.lov;
+ struct lov_tgt_desc *tgt;
+ struct obd_device *tgt_obd;
+ int rc;
+
+ CDEBUG(D_CONFIG, "uuid:%s idx:%d gen:%d active:%d\n",
+ uuidp->uuid, index, gen, active);
+
+ if (gen <= 0) {
+ CERROR("request to add OBD %s with invalid generation: %d\n",
+ uuidp->uuid, gen);
+ return -EINVAL;
+ }
+
+ tgt_obd = class_find_client_obd(uuidp, LUSTRE_OSC_NAME,
+ &obd->obd_uuid);
+ if (tgt_obd == NULL)
+ return -EINVAL;
+
+ mutex_lock(&lov->lov_lock);
+
+ if ((index < lov->lov_tgt_size) && (lov->lov_tgts[index] != NULL)) {
+ tgt = lov->lov_tgts[index];
+ CERROR("UUID %s already assigned at LOV target index %d\n",
+ obd_uuid2str(&tgt->ltd_uuid), index);
+ mutex_unlock(&lov->lov_lock);
+ return -EEXIST;
+ }
+
+ if (index >= lov->lov_tgt_size) {
+ /* We need to reallocate the lov target array. */
+ struct lov_tgt_desc **newtgts, **old = NULL;
+ __u32 newsize, oldsize = 0;
+
+ newsize = max_t(__u32, lov->lov_tgt_size, 2);
+ while (newsize < index + 1)
+ newsize <<= 1;
+ OBD_ALLOC(newtgts, sizeof(*newtgts) * newsize);
+ if (newtgts == NULL) {
+ mutex_unlock(&lov->lov_lock);
+ return -ENOMEM;
+ }
+
+ if (lov->lov_tgt_size) {
+ memcpy(newtgts, lov->lov_tgts, sizeof(*newtgts) *
+ lov->lov_tgt_size);
+ old = lov->lov_tgts;
+ oldsize = lov->lov_tgt_size;
+ }
+
+ lov->lov_tgts = newtgts;
+ lov->lov_tgt_size = newsize;
+ smp_rmb();
+ if (old)
+ OBD_FREE(old, sizeof(*old) * oldsize);
+
+ CDEBUG(D_CONFIG, "tgts: %p size: %d\n",
+ lov->lov_tgts, lov->lov_tgt_size);
+ }
+
+ OBD_ALLOC_PTR(tgt);
+ if (!tgt) {
+ mutex_unlock(&lov->lov_lock);
+ return -ENOMEM;
+ }
+
+ rc = lov_ost_pool_add(&lov->lov_packed, index, lov->lov_tgt_size);
+ if (rc) {
+ mutex_unlock(&lov->lov_lock);
+ OBD_FREE_PTR(tgt);
+ return rc;
+ }
+
+ tgt->ltd_uuid = *uuidp;
+ tgt->ltd_obd = tgt_obd;
+ /* XXX - add a sanity check on the generation number. */
+ tgt->ltd_gen = gen;
+ tgt->ltd_index = index;
+ tgt->ltd_activate = active;
+ lov->lov_tgts[index] = tgt;
+ if (index >= lov->desc.ld_tgt_count)
+ lov->desc.ld_tgt_count = index + 1;
+
+ mutex_unlock(&lov->lov_lock);
+
+ CDEBUG(D_CONFIG, "idx=%d ltd_gen=%d ld_tgt_count=%d\n",
+ index, tgt->ltd_gen, lov->desc.ld_tgt_count);
+
+ rc = obd_notify(obd, tgt_obd, OBD_NOTIFY_CREATE, &index);
+
+ if (lov->lov_connects == 0) {
+ /* lov_connect hasn't been called yet. We'll do the
+ lov_connect_obd on this target when that fn first runs,
+ because we don't know the connect flags yet. */
+ return 0;
+ }
+
+ obd_getref(obd);
+
+ rc = lov_connect_obd(obd, index, active, &lov->lov_ocd);
+ if (rc)
+ goto out;
+
+ /* connect to administrative disabled ost */
+ if (!tgt->ltd_exp) {
+ rc = 0;
+ goto out;
+ }
+
+ if (lov->lov_cache != NULL) {
+ rc = obd_set_info_async(NULL, tgt->ltd_exp,
+ sizeof(KEY_CACHE_SET), KEY_CACHE_SET,
+ sizeof(struct cl_client_cache), lov->lov_cache,
+ NULL);
+ if (rc < 0)
+ goto out;
+ }
+
+ rc = lov_notify(obd, tgt->ltd_exp->exp_obd,
+ active ? OBD_NOTIFY_CONNECT : OBD_NOTIFY_INACTIVE,
+ (void *)&index);
+
+out:
+ if (rc) {
+ CERROR("add failed (%d), deleting %s\n", rc,
+ obd_uuid2str(&tgt->ltd_uuid));
+ lov_del_target(obd, index, NULL, 0);
+ }
+ obd_putref(obd);
+ return rc;
+}
+
+/* Schedule a target for deletion */
+int lov_del_target(struct obd_device *obd, __u32 index,
+ struct obd_uuid *uuidp, int gen)
+{
+ struct lov_obd *lov = &obd->u.lov;
+ int count = lov->desc.ld_tgt_count;
+ int rc = 0;
+
+ if (index >= count) {
+ CERROR("LOV target index %d >= number of LOV OBDs %d.\n",
+ index, count);
+ return -EINVAL;
+ }
+
+ /* to make sure there's no ongoing lov_notify() now */
+ down_write(&lov->lov_notify_lock);
+ obd_getref(obd);
+
+ if (!lov->lov_tgts[index]) {
+ CERROR("LOV target at index %d is not setup.\n", index);
+ rc = -EINVAL;
+ goto out;
+ }
+
+ if (uuidp && !obd_uuid_equals(uuidp, &lov->lov_tgts[index]->ltd_uuid)) {
+ CERROR("LOV target UUID %s at index %d doesn't match %s.\n",
+ lov_uuid2str(lov, index), index,
+ obd_uuid2str(uuidp));
+ rc = -EINVAL;
+ goto out;
+ }
+
+ CDEBUG(D_CONFIG, "uuid: %s idx: %d gen: %d exp: %p active: %d\n",
+ lov_uuid2str(lov, index), index,
+ lov->lov_tgts[index]->ltd_gen, lov->lov_tgts[index]->ltd_exp,
+ lov->lov_tgts[index]->ltd_active);
+
+ lov->lov_tgts[index]->ltd_reap = 1;
+ lov->lov_death_row++;
+ /* we really delete it from obd_putref */
+out:
+ obd_putref(obd);
+ up_write(&lov->lov_notify_lock);
+
+ return rc;
+}
+
+static void __lov_del_obd(struct obd_device *obd, struct lov_tgt_desc *tgt)
+{
+ struct obd_device *osc_obd;
+
+ LASSERT(tgt);
+ LASSERT(tgt->ltd_reap);
+
+ osc_obd = class_exp2obd(tgt->ltd_exp);
+
+ CDEBUG(D_CONFIG, "Removing tgt %s : %s\n",
+ tgt->ltd_uuid.uuid,
+ osc_obd ? osc_obd->obd_name : "<no obd>");
+
+ if (tgt->ltd_exp)
+ lov_disconnect_obd(obd, tgt);
+
+ OBD_FREE_PTR(tgt);
+
+ /* Manual cleanup - no cleanup logs to clean up the osc's. We must
+ do it ourselves. And we can't do it from lov_cleanup,
+ because we just lost our only reference to it. */
+ if (osc_obd)
+ class_manual_cleanup(osc_obd);
+}
+
+void lov_fix_desc_stripe_size(__u64 *val)
+{
+ if (*val < LOV_MIN_STRIPE_SIZE) {
+ if (*val != 0)
+ LCONSOLE_INFO("Increasing default stripe size to minimum %u\n",
+ LOV_DESC_STRIPE_SIZE_DEFAULT);
+ *val = LOV_DESC_STRIPE_SIZE_DEFAULT;
+ } else if (*val & (LOV_MIN_STRIPE_SIZE - 1)) {
+ *val &= ~(LOV_MIN_STRIPE_SIZE - 1);
+ LCONSOLE_WARN("Changing default stripe size to %llu (a multiple of %u)\n",
+ *val, LOV_MIN_STRIPE_SIZE);
+ }
+}
+
+void lov_fix_desc_stripe_count(__u32 *val)
+{
+ if (*val == 0)
+ *val = 1;
+}
+
+void lov_fix_desc_pattern(__u32 *val)
+{
+ /* from lov_setstripe */
+ if ((*val != 0) && (*val != LOV_PATTERN_RAID0)) {
+ LCONSOLE_WARN("Unknown stripe pattern: %#x\n", *val);
+ *val = 0;
+ }
+}
+
+void lov_fix_desc_qos_maxage(__u32 *val)
+{
+ if (*val == 0)
+ *val = LOV_DESC_QOS_MAXAGE_DEFAULT;
+}
+
+void lov_fix_desc(struct lov_desc *desc)
+{
+ lov_fix_desc_stripe_size(&desc->ld_default_stripe_size);
+ lov_fix_desc_stripe_count(&desc->ld_default_stripe_count);
+ lov_fix_desc_pattern(&desc->ld_pattern);
+ lov_fix_desc_qos_maxage(&desc->ld_qos_maxage);
+}
+
+int lov_setup(struct obd_device *obd, struct lustre_cfg *lcfg)
+{
+ struct lprocfs_static_vars lvars = { NULL };
+ struct lov_desc *desc;
+ struct lov_obd *lov = &obd->u.lov;
+ int rc;
+
+ if (LUSTRE_CFG_BUFLEN(lcfg, 1) < 1) {
+ CERROR("LOV setup requires a descriptor\n");
+ return -EINVAL;
+ }
+
+ desc = (struct lov_desc *)lustre_cfg_buf(lcfg, 1);
+
+ if (sizeof(*desc) > LUSTRE_CFG_BUFLEN(lcfg, 1)) {
+ CERROR("descriptor size wrong: %d > %d\n",
+ (int)sizeof(*desc), LUSTRE_CFG_BUFLEN(lcfg, 1));
+ return -EINVAL;
+ }
+
+ if (desc->ld_magic != LOV_DESC_MAGIC) {
+ if (desc->ld_magic == __swab32(LOV_DESC_MAGIC)) {
+ CDEBUG(D_OTHER, "%s: Swabbing lov desc %p\n",
+ obd->obd_name, desc);
+ lustre_swab_lov_desc(desc);
+ } else {
+ CERROR("%s: Bad lov desc magic: %#x\n",
+ obd->obd_name, desc->ld_magic);
+ return -EINVAL;
+ }
+ }
+
+ lov_fix_desc(desc);
+
+ desc->ld_active_tgt_count = 0;
+ lov->desc = *desc;
+ lov->lov_tgt_size = 0;
+
+ mutex_init(&lov->lov_lock);
+ atomic_set(&lov->lov_refcount, 0);
+ lov->lov_sp_me = LUSTRE_SP_CLI;
+
+ init_rwsem(&lov->lov_notify_lock);
+
+ lov->lov_pools_hash_body = cfs_hash_create("POOLS", HASH_POOLS_CUR_BITS,
+ HASH_POOLS_MAX_BITS,
+ HASH_POOLS_BKT_BITS, 0,
+ CFS_HASH_MIN_THETA,
+ CFS_HASH_MAX_THETA,
+ &pool_hash_operations,
+ CFS_HASH_DEFAULT);
+ INIT_LIST_HEAD(&lov->lov_pool_list);
+ lov->lov_pool_count = 0;
+ rc = lov_ost_pool_init(&lov->lov_packed, 0);
+ if (rc)
+ goto out;
+
+ lprocfs_lov_init_vars(&lvars);
+ lprocfs_obd_setup(obd, lvars.obd_vars);
+#if defined (CONFIG_PROC_FS)
+ {
+ int rc1;
+
+ rc1 = lprocfs_seq_create(obd->obd_proc_entry, "target_obd",
+ 0444, &lov_proc_target_fops, obd);
+ if (rc1)
+ CWARN("Error adding the target_obd file\n");
+ }
+#endif
+ lov->lov_pool_proc_entry = lprocfs_register("pools",
+ obd->obd_proc_entry,
+ NULL, NULL);
+
+ return 0;
+
+out:
+ return rc;
+}
+
+static int lov_precleanup(struct obd_device *obd, enum obd_cleanup_stage stage)
+{
+ struct lov_obd *lov = &obd->u.lov;
+
+ switch (stage) {
+ case OBD_CLEANUP_EARLY: {
+ int i;
+ for (i = 0; i < lov->desc.ld_tgt_count; i++) {
+ if (!lov->lov_tgts[i] || !lov->lov_tgts[i]->ltd_active)
+ continue;
+ obd_precleanup(class_exp2obd(lov->lov_tgts[i]->ltd_exp),
+ OBD_CLEANUP_EARLY);
+ }
+ break;
+ }
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int lov_cleanup(struct obd_device *obd)
+{
+ struct lov_obd *lov = &obd->u.lov;
+ struct list_head *pos, *tmp;
+ struct pool_desc *pool;
+
+ list_for_each_safe(pos, tmp, &lov->lov_pool_list) {
+ pool = list_entry(pos, struct pool_desc, pool_list);
+ /* free pool structs */
+ CDEBUG(D_INFO, "delete pool %p\n", pool);
+ /* In the function below, .hs_keycmp resolves to
+ * pool_hashkey_keycmp() */
+ /* coverity[overrun-buffer-val] */
+ lov_pool_del(obd, pool->pool_name);
+ }
+ cfs_hash_putref(lov->lov_pools_hash_body);
+ lov_ost_pool_free(&lov->lov_packed);
+
+ lprocfs_obd_cleanup(obd);
+ if (lov->lov_tgts) {
+ int i;
+ obd_getref(obd);
+ for (i = 0; i < lov->desc.ld_tgt_count; i++) {
+ if (!lov->lov_tgts[i])
+ continue;
+
+ /* Inactive targets may never have connected */
+ if (lov->lov_tgts[i]->ltd_active ||
+ atomic_read(&lov->lov_refcount))
+ /* We should never get here - these
+ should have been removed in the
+ disconnect. */
+ CERROR("lov tgt %d not cleaned! deathrow=%d, lovrc=%d\n",
+ i, lov->lov_death_row,
+ atomic_read(&lov->lov_refcount));
+ lov_del_target(obd, i, NULL, 0);
+ }
+ obd_putref(obd);
+ OBD_FREE(lov->lov_tgts, sizeof(*lov->lov_tgts) *
+ lov->lov_tgt_size);
+ lov->lov_tgt_size = 0;
+ }
+ return 0;
+}
+
+int lov_process_config_base(struct obd_device *obd, struct lustre_cfg *lcfg,
+ __u32 *indexp, int *genp)
+{
+ struct obd_uuid obd_uuid;
+ int cmd;
+ int rc = 0;
+
+ switch (cmd = lcfg->lcfg_command) {
+ case LCFG_LOV_ADD_OBD:
+ case LCFG_LOV_ADD_INA:
+ case LCFG_LOV_DEL_OBD: {
+ __u32 index;
+ int gen;
+ /* lov_modify_tgts add 0:lov_mdsA 1:ost1_UUID 2:0 3:1 */
+ if (LUSTRE_CFG_BUFLEN(lcfg, 1) > sizeof(obd_uuid.uuid)) {
+ rc = -EINVAL;
+ goto out;
+ }
+
+ obd_str2uuid(&obd_uuid, lustre_cfg_buf(lcfg, 1));
+
+ if (sscanf(lustre_cfg_buf(lcfg, 2), "%d", indexp) != 1) {
+ rc = -EINVAL;
+ goto out;
+ }
+ if (sscanf(lustre_cfg_buf(lcfg, 3), "%d", genp) != 1) {
+ rc = -EINVAL;
+ goto out;
+ }
+ index = *indexp;
+ gen = *genp;
+ if (cmd == LCFG_LOV_ADD_OBD)
+ rc = lov_add_target(obd, &obd_uuid, index, gen, 1);
+ else if (cmd == LCFG_LOV_ADD_INA)
+ rc = lov_add_target(obd, &obd_uuid, index, gen, 0);
+ else
+ rc = lov_del_target(obd, index, &obd_uuid, gen);
+ goto out;
+ }
+ case LCFG_PARAM: {
+ struct lprocfs_static_vars lvars = { NULL };
+ struct lov_desc *desc = &(obd->u.lov.desc);
+
+ if (!desc) {
+ rc = -EINVAL;
+ goto out;
+ }
+
+ lprocfs_lov_init_vars(&lvars);
+
+ rc = class_process_proc_param(PARAM_LOV, lvars.obd_vars,
+ lcfg, obd);
+ if (rc > 0)
+ rc = 0;
+ goto out;
+ }
+ case LCFG_POOL_NEW:
+ case LCFG_POOL_ADD:
+ case LCFG_POOL_DEL:
+ case LCFG_POOL_REM:
+ goto out;
+
+ default: {
+ CERROR("Unknown command: %d\n", lcfg->lcfg_command);
+ rc = -EINVAL;
+ goto out;
+
+ }
+ }
+out:
+ return rc;
+}
+
+static int lov_recreate(struct obd_export *exp, struct obdo *src_oa,
+ struct lov_stripe_md **ea, struct obd_trans_info *oti)
+{
+ struct lov_stripe_md *obj_mdp, *lsm;
+ struct lov_obd *lov = &exp->exp_obd->u.lov;
+ unsigned ost_idx;
+ int rc, i;
+
+ LASSERT(src_oa->o_valid & OBD_MD_FLFLAGS &&
+ src_oa->o_flags & OBD_FL_RECREATE_OBJS);
+
+ OBD_ALLOC(obj_mdp, sizeof(*obj_mdp));
+ if (obj_mdp == NULL)
+ return -ENOMEM;
+
+ ost_idx = src_oa->o_nlink;
+ lsm = *ea;
+ if (lsm == NULL) {
+ rc = -EINVAL;
+ goto out;
+ }
+ if (ost_idx >= lov->desc.ld_tgt_count ||
+ !lov->lov_tgts[ost_idx]) {
+ rc = -EINVAL;
+ goto out;
+ }
+
+ for (i = 0; i < lsm->lsm_stripe_count; i++) {
+ struct lov_oinfo *loi = lsm->lsm_oinfo[i];
+
+ if (lov_oinfo_is_dummy(loi))
+ continue;
+
+ if (loi->loi_ost_idx == ost_idx) {
+ if (ostid_id(&loi->loi_oi) != ostid_id(&src_oa->o_oi)) {
+ rc = -EINVAL;
+ goto out;
+ }
+ break;
+ }
+ }
+ if (i == lsm->lsm_stripe_count) {
+ rc = -EINVAL;
+ goto out;
+ }
+
+ rc = obd_create(NULL, lov->lov_tgts[ost_idx]->ltd_exp,
+ src_oa, &obj_mdp, oti);
+out:
+ OBD_FREE(obj_mdp, sizeof(*obj_mdp));
+ return rc;
+}
+
+/* the LOV expects oa->o_id to be set to the LOV object id */
+static int lov_create(const struct lu_env *env, struct obd_export *exp,
+ struct obdo *src_oa, struct lov_stripe_md **ea,
+ struct obd_trans_info *oti)
+{
+ struct lov_obd *lov;
+ int rc = 0;
+
+ LASSERT(ea != NULL);
+ if (exp == NULL)
+ return -EINVAL;
+
+ if ((src_oa->o_valid & OBD_MD_FLFLAGS) &&
+ src_oa->o_flags == OBD_FL_DELORPHAN) {
+ /* should be used with LOV anymore */
+ LBUG();
+ }
+
+ lov = &exp->exp_obd->u.lov;
+ if (!lov->desc.ld_active_tgt_count)
+ return -EIO;
+
+ obd_getref(exp->exp_obd);
+ /* Recreate a specific object id at the given OST index */
+ if ((src_oa->o_valid & OBD_MD_FLFLAGS) &&
+ (src_oa->o_flags & OBD_FL_RECREATE_OBJS)) {
+ rc = lov_recreate(exp, src_oa, ea, oti);
+ }
+
+ obd_putref(exp->exp_obd);
+ return rc;
+}
+
+#define ASSERT_LSM_MAGIC(lsmp) \
+do { \
+ LASSERT((lsmp) != NULL); \
+ LASSERTF(((lsmp)->lsm_magic == LOV_MAGIC_V1 || \
+ (lsmp)->lsm_magic == LOV_MAGIC_V3), \
+ "%p->lsm_magic=%x\n", (lsmp), (lsmp)->lsm_magic); \
+} while (0)
+
+static int lov_destroy(const struct lu_env *env, struct obd_export *exp,
+ struct obdo *oa, struct lov_stripe_md *lsm,
+ struct obd_trans_info *oti, struct obd_export *md_exp,
+ void *capa)
+{
+ struct lov_request_set *set;
+ struct obd_info oinfo;
+ struct lov_request *req;
+ struct list_head *pos;
+ struct lov_obd *lov;
+ int rc = 0, err = 0;
+
+ ASSERT_LSM_MAGIC(lsm);
+
+ if (!exp || !exp->exp_obd)
+ return -ENODEV;
+
+ if (oa->o_valid & OBD_MD_FLCOOKIE) {
+ LASSERT(oti);
+ LASSERT(oti->oti_logcookies);
+ }
+
+ lov = &exp->exp_obd->u.lov;
+ obd_getref(exp->exp_obd);
+ rc = lov_prep_destroy_set(exp, &oinfo, oa, lsm, oti, &set);
+ if (rc)
+ goto out;
+
+ list_for_each(pos, &set->set_list) {
+ req = list_entry(pos, struct lov_request, rq_link);
+
+ if (oa->o_valid & OBD_MD_FLCOOKIE)
+ oti->oti_logcookies = set->set_cookies + req->rq_stripe;
+
+ err = obd_destroy(env, lov->lov_tgts[req->rq_idx]->ltd_exp,
+ req->rq_oi.oi_oa, NULL, oti, NULL, capa);
+ err = lov_update_common_set(set, req, err);
+ if (err) {
+ CERROR("%s: destroying objid "DOSTID" subobj "
+ DOSTID" on OST idx %d: rc = %d\n",
+ exp->exp_obd->obd_name, POSTID(&oa->o_oi),
+ POSTID(&req->rq_oi.oi_oa->o_oi),
+ req->rq_idx, err);
+ if (!rc)
+ rc = err;
+ }
+ }
+
+ if (rc == 0) {
+ LASSERT(lsm_op_find(lsm->lsm_magic) != NULL);
+ rc = lsm_op_find(lsm->lsm_magic)->lsm_destroy(lsm, oa, md_exp);
+ }
+ err = lov_fini_destroy_set(set);
+out:
+ obd_putref(exp->exp_obd);
+ return rc ? rc : err;
+}
+
+static int lov_getattr_interpret(struct ptlrpc_request_set *rqset,
+ void *data, int rc)
+{
+ struct lov_request_set *lovset = (struct lov_request_set *)data;
+ int err;
+
+ /* don't do attribute merge if this async op failed */
+ if (rc)
+ atomic_set(&lovset->set_completes, 0);
+ err = lov_fini_getattr_set(lovset);
+ return rc ? rc : err;
+}
+
+static int lov_getattr_async(struct obd_export *exp, struct obd_info *oinfo,
+ struct ptlrpc_request_set *rqset)
+{
+ struct lov_request_set *lovset;
+ struct lov_obd *lov;
+ struct list_head *pos;
+ struct lov_request *req;
+ int rc = 0, err;
+
+ LASSERT(oinfo);
+ ASSERT_LSM_MAGIC(oinfo->oi_md);
+
+ if (!exp || !exp->exp_obd)
+ return -ENODEV;
+
+ lov = &exp->exp_obd->u.lov;
+
+ rc = lov_prep_getattr_set(exp, oinfo, &lovset);
+ if (rc)
+ return rc;
+
+ CDEBUG(D_INFO, "objid "DOSTID": %ux%u byte stripes\n",
+ POSTID(&oinfo->oi_md->lsm_oi), oinfo->oi_md->lsm_stripe_count,
+ oinfo->oi_md->lsm_stripe_size);
+
+ list_for_each(pos, &lovset->set_list) {
+ req = list_entry(pos, struct lov_request, rq_link);
+
+ CDEBUG(D_INFO, "objid " DOSTID "[%d] has subobj " DOSTID " at idx%u\n",
+ POSTID(&oinfo->oi_oa->o_oi), req->rq_stripe,
+ POSTID(&req->rq_oi.oi_oa->o_oi), req->rq_idx);
+ rc = obd_getattr_async(lov->lov_tgts[req->rq_idx]->ltd_exp,
+ &req->rq_oi, rqset);
+ if (rc) {
+ CERROR("%s: getattr objid "DOSTID" subobj"
+ DOSTID" on OST idx %d: rc = %d\n",
+ exp->exp_obd->obd_name,
+ POSTID(&oinfo->oi_oa->o_oi),
+ POSTID(&req->rq_oi.oi_oa->o_oi),
+ req->rq_idx, rc);
+ goto out;
+ }
+ }
+
+ if (!list_empty(&rqset->set_requests)) {
+ LASSERT(rc == 0);
+ LASSERT(rqset->set_interpret == NULL);
+ rqset->set_interpret = lov_getattr_interpret;
+ rqset->set_arg = (void *)lovset;
+ return rc;
+ }
+out:
+ if (rc)
+ atomic_set(&lovset->set_completes, 0);
+ err = lov_fini_getattr_set(lovset);
+ return rc ? rc : err;
+}
+
+static int lov_setattr_interpret(struct ptlrpc_request_set *rqset,
+ void *data, int rc)
+{
+ struct lov_request_set *lovset = (struct lov_request_set *)data;
+ int err;
+
+ if (rc)
+ atomic_set(&lovset->set_completes, 0);
+ err = lov_fini_setattr_set(lovset);
+ return rc ? rc : err;
+}
+
+/* If @oti is given, the request goes from MDS and responses from OSTs are not
+ needed. Otherwise, a client is waiting for responses. */
+static int lov_setattr_async(struct obd_export *exp, struct obd_info *oinfo,
+ struct obd_trans_info *oti,
+ struct ptlrpc_request_set *rqset)
+{
+ struct lov_request_set *set;
+ struct lov_request *req;
+ struct list_head *pos;
+ struct lov_obd *lov;
+ int rc = 0;
+
+ LASSERT(oinfo);
+ ASSERT_LSM_MAGIC(oinfo->oi_md);
+ if (oinfo->oi_oa->o_valid & OBD_MD_FLCOOKIE) {
+ LASSERT(oti);
+ LASSERT(oti->oti_logcookies);
+ }
+
+ if (!exp || !exp->exp_obd)
+ return -ENODEV;
+
+ lov = &exp->exp_obd->u.lov;
+ rc = lov_prep_setattr_set(exp, oinfo, oti, &set);
+ if (rc)
+ return rc;
+
+ CDEBUG(D_INFO, "objid "DOSTID": %ux%u byte stripes\n",
+ POSTID(&oinfo->oi_md->lsm_oi),
+ oinfo->oi_md->lsm_stripe_count,
+ oinfo->oi_md->lsm_stripe_size);
+
+ list_for_each(pos, &set->set_list) {
+ req = list_entry(pos, struct lov_request, rq_link);
+
+ if (oinfo->oi_oa->o_valid & OBD_MD_FLCOOKIE)
+ oti->oti_logcookies = set->set_cookies + req->rq_stripe;
+
+ CDEBUG(D_INFO, "objid " DOSTID "[%d] has subobj " DOSTID " at idx%u\n",
+ POSTID(&oinfo->oi_oa->o_oi), req->rq_stripe,
+ POSTID(&req->rq_oi.oi_oa->o_oi), req->rq_idx);
+
+ rc = obd_setattr_async(lov->lov_tgts[req->rq_idx]->ltd_exp,
+ &req->rq_oi, oti, rqset);
+ if (rc) {
+ CERROR("error: setattr objid "DOSTID" subobj"
+ DOSTID" on OST idx %d: rc = %d\n",
+ POSTID(&set->set_oi->oi_oa->o_oi),
+ POSTID(&req->rq_oi.oi_oa->o_oi),
+ req->rq_idx, rc);
+ break;
+ }
+ }
+
+ /* If we are not waiting for responses on async requests, return. */
+ if (rc || !rqset || list_empty(&rqset->set_requests)) {
+ int err;
+ if (rc)
+ atomic_set(&set->set_completes, 0);
+ err = lov_fini_setattr_set(set);
+ return rc ? rc : err;
+ }
+
+ LASSERT(rqset->set_interpret == NULL);
+ rqset->set_interpret = lov_setattr_interpret;
+ rqset->set_arg = (void *)set;
+
+ return 0;
+}
+
+/* find any ldlm lock of the inode in lov
+ * return 0 not find
+ * 1 find one
+ * < 0 error */
+static int lov_find_cbdata(struct obd_export *exp,
+ struct lov_stripe_md *lsm, ldlm_iterator_t it,
+ void *data)
+{
+ struct lov_obd *lov;
+ int rc = 0, i;
+
+ ASSERT_LSM_MAGIC(lsm);
+
+ if (!exp || !exp->exp_obd)
+ return -ENODEV;
+
+ lov = &exp->exp_obd->u.lov;
+ for (i = 0; i < lsm->lsm_stripe_count; i++) {
+ struct lov_stripe_md submd;
+ struct lov_oinfo *loi = lsm->lsm_oinfo[i];
+
+ if (lov_oinfo_is_dummy(loi))
+ continue;
+
+ if (!lov->lov_tgts[loi->loi_ost_idx]) {
+ CDEBUG(D_HA, "lov idx %d NULL\n", loi->loi_ost_idx);
+ continue;
+ }
+
+ submd.lsm_oi = loi->loi_oi;
+ submd.lsm_stripe_count = 0;
+ rc = obd_find_cbdata(lov->lov_tgts[loi->loi_ost_idx]->ltd_exp,
+ &submd, it, data);
+ if (rc != 0)
+ return rc;
+ }
+ return rc;
+}
+
+int lov_statfs_interpret(struct ptlrpc_request_set *rqset, void *data, int rc)
+{
+ struct lov_request_set *lovset = (struct lov_request_set *)data;
+ int err;
+
+ if (rc)
+ atomic_set(&lovset->set_completes, 0);
+
+ err = lov_fini_statfs_set(lovset);
+ return rc ? rc : err;
+}
+
+static int lov_statfs_async(struct obd_export *exp, struct obd_info *oinfo,
+ __u64 max_age, struct ptlrpc_request_set *rqset)
+{
+ struct obd_device *obd = class_exp2obd(exp);
+ struct lov_request_set *set;
+ struct lov_request *req;
+ struct list_head *pos;
+ struct lov_obd *lov;
+ int rc = 0;
+
+ LASSERT(oinfo != NULL);
+ LASSERT(oinfo->oi_osfs != NULL);
+
+ lov = &obd->u.lov;
+ rc = lov_prep_statfs_set(obd, oinfo, &set);
+ if (rc)
+ return rc;
+
+ list_for_each(pos, &set->set_list) {
+ req = list_entry(pos, struct lov_request, rq_link);
+ rc = obd_statfs_async(lov->lov_tgts[req->rq_idx]->ltd_exp,
+ &req->rq_oi, max_age, rqset);
+ if (rc)
+ break;
+ }
+
+ if (rc || list_empty(&rqset->set_requests)) {
+ int err;
+ if (rc)
+ atomic_set(&set->set_completes, 0);
+ err = lov_fini_statfs_set(set);
+ return rc ? rc : err;
+ }
+
+ LASSERT(rqset->set_interpret == NULL);
+ rqset->set_interpret = lov_statfs_interpret;
+ rqset->set_arg = (void *)set;
+ return 0;
+}
+
+static int lov_statfs(const struct lu_env *env, struct obd_export *exp,
+ struct obd_statfs *osfs, __u64 max_age, __u32 flags)
+{
+ struct ptlrpc_request_set *set = NULL;
+ struct obd_info oinfo = { { { 0 } } };
+ int rc = 0;
+
+ /* for obdclass we forbid using obd_statfs_rqset, but prefer using async
+ * statfs requests */
+ set = ptlrpc_prep_set();
+ if (set == NULL)
+ return -ENOMEM;
+
+ oinfo.oi_osfs = osfs;
+ oinfo.oi_flags = flags;
+ rc = lov_statfs_async(exp, &oinfo, max_age, set);
+ if (rc == 0)
+ rc = ptlrpc_set_wait(set);
+ ptlrpc_set_destroy(set);
+
+ return rc;
+}
+
+static int lov_iocontrol(unsigned int cmd, struct obd_export *exp, int len,
+ void *karg, void *uarg)
+{
+ struct obd_device *obddev = class_exp2obd(exp);
+ struct lov_obd *lov = &obddev->u.lov;
+ int i = 0, rc = 0, count = lov->desc.ld_tgt_count;
+ struct obd_uuid *uuidp;
+
+ switch (cmd) {
+ case IOC_OBD_STATFS: {
+ struct obd_ioctl_data *data = karg;
+ struct obd_device *osc_obd;
+ struct obd_statfs stat_buf = {0};
+ __u32 index;
+ __u32 flags;
+
+ memcpy(&index, data->ioc_inlbuf2, sizeof(__u32));
+ if ((index >= count))
+ return -ENODEV;
+
+ if (!lov->lov_tgts[index])
+ /* Try again with the next index */
+ return -EAGAIN;
+ if (!lov->lov_tgts[index]->ltd_active)
+ return -ENODATA;
+
+ osc_obd = class_exp2obd(lov->lov_tgts[index]->ltd_exp);
+ if (!osc_obd)
+ return -EINVAL;
+
+ /* copy UUID */
+ if (copy_to_user(data->ioc_pbuf2, obd2cli_tgt(osc_obd),
+ min((int) data->ioc_plen2,
+ (int) sizeof(struct obd_uuid))))
+ return -EFAULT;
+
+ flags = uarg ? *(__u32 *)uarg : 0;
+ /* got statfs data */
+ rc = obd_statfs(NULL, lov->lov_tgts[index]->ltd_exp, &stat_buf,
+ cfs_time_shift_64(-OBD_STATFS_CACHE_SECONDS),
+ flags);
+ if (rc)
+ return rc;
+ if (copy_to_user(data->ioc_pbuf1, &stat_buf,
+ min((int) data->ioc_plen1,
+ (int) sizeof(stat_buf))))
+ return -EFAULT;
+ break;
+ }
+ case OBD_IOC_LOV_GET_CONFIG: {
+ struct obd_ioctl_data *data;
+ struct lov_desc *desc;
+ char *buf = NULL;
+ __u32 *genp;
+
+ len = 0;
+ if (obd_ioctl_getdata(&buf, &len, (void *)uarg))
+ return -EINVAL;
+
+ data = (struct obd_ioctl_data *)buf;
+
+ if (sizeof(*desc) > data->ioc_inllen1) {
+ obd_ioctl_freedata(buf, len);
+ return -EINVAL;
+ }
+
+ if (sizeof(uuidp->uuid) * count > data->ioc_inllen2) {
+ obd_ioctl_freedata(buf, len);
+ return -EINVAL;
+ }
+
+ if (sizeof(__u32) * count > data->ioc_inllen3) {
+ obd_ioctl_freedata(buf, len);
+ return -EINVAL;
+ }
+
+ desc = (struct lov_desc *)data->ioc_inlbuf1;
+ memcpy(desc, &(lov->desc), sizeof(*desc));
+
+ uuidp = (struct obd_uuid *)data->ioc_inlbuf2;
+ genp = (__u32 *)data->ioc_inlbuf3;
+ /* the uuid will be empty for deleted OSTs */
+ for (i = 0; i < count; i++, uuidp++, genp++) {
+ if (!lov->lov_tgts[i])
+ continue;
+ *uuidp = lov->lov_tgts[i]->ltd_uuid;
+ *genp = lov->lov_tgts[i]->ltd_gen;
+ }
+
+ if (copy_to_user((void *)uarg, buf, len))
+ rc = -EFAULT;
+ obd_ioctl_freedata(buf, len);
+ break;
+ }
+ case LL_IOC_LOV_GETSTRIPE:
+ rc = lov_getstripe(exp, karg, uarg);
+ break;
+ case OBD_IOC_QUOTACTL: {
+ struct if_quotactl *qctl = karg;
+ struct lov_tgt_desc *tgt = NULL;
+ struct obd_quotactl *oqctl;
+
+ if (qctl->qc_valid == QC_OSTIDX) {
+ if (qctl->qc_idx < 0 || count <= qctl->qc_idx)
+ return -EINVAL;
+
+ tgt = lov->lov_tgts[qctl->qc_idx];
+ if (!tgt || !tgt->ltd_exp)
+ return -EINVAL;
+ } else if (qctl->qc_valid == QC_UUID) {
+ for (i = 0; i < count; i++) {
+ tgt = lov->lov_tgts[i];
+ if (!tgt ||
+ !obd_uuid_equals(&tgt->ltd_uuid,
+ &qctl->obd_uuid))
+ continue;
+
+ if (tgt->ltd_exp == NULL)
+ return -EINVAL;
+
+ break;
+ }
+ } else {
+ return -EINVAL;
+ }
+
+ if (i >= count)
+ return -EAGAIN;
+
+ LASSERT(tgt && tgt->ltd_exp);
+ OBD_ALLOC_PTR(oqctl);
+ if (!oqctl)
+ return -ENOMEM;
+
+ QCTL_COPY(oqctl, qctl);
+ rc = obd_quotactl(tgt->ltd_exp, oqctl);
+ if (rc == 0) {
+ QCTL_COPY(qctl, oqctl);
+ qctl->qc_valid = QC_OSTIDX;
+ qctl->obd_uuid = tgt->ltd_uuid;
+ }
+ OBD_FREE_PTR(oqctl);
+ break;
+ }
+ default: {
+ int set = 0;
+
+ if (count == 0)
+ return -ENOTTY;
+
+ for (i = 0; i < count; i++) {
+ int err;
+ struct obd_device *osc_obd;
+
+ /* OST was disconnected */
+ if (!lov->lov_tgts[i] || !lov->lov_tgts[i]->ltd_exp)
+ continue;
+
+ /* ll_umount_begin() sets force flag but for lov, not
+ * osc. Let's pass it through */
+ osc_obd = class_exp2obd(lov->lov_tgts[i]->ltd_exp);
+ osc_obd->obd_force = obddev->obd_force;
+ err = obd_iocontrol(cmd, lov->lov_tgts[i]->ltd_exp,
+ len, karg, uarg);
+ if (err == -ENODATA && cmd == OBD_IOC_POLL_QUOTACHECK) {
+ return err;
+ } else if (err) {
+ if (lov->lov_tgts[i]->ltd_active) {
+ CDEBUG(err == -ENOTTY ?
+ D_IOCTL : D_WARNING,
+ "iocontrol OSC %s on OST idx %d cmd %x: err = %d\n",
+ lov_uuid2str(lov, i),
+ i, cmd, err);
+ if (!rc)
+ rc = err;
+ }
+ } else {
+ set = 1;
+ }
+ }
+ if (!set && !rc)
+ rc = -EIO;
+ }
+ }
+
+ return rc;
+}
+
+#define FIEMAP_BUFFER_SIZE 4096
+
+/**
+ * Non-zero fe_logical indicates that this is a continuation FIEMAP
+ * call. The local end offset and the device are sent in the first
+ * fm_extent. This function calculates the stripe number from the index.
+ * This function returns a stripe_no on which mapping is to be restarted.
+ *
+ * This function returns fm_end_offset which is the in-OST offset at which
+ * mapping should be restarted. If fm_end_offset=0 is returned then caller
+ * will re-calculate proper offset in next stripe.
+ * Note that the first extent is passed to lov_get_info via the value field.
+ *
+ * \param fiemap fiemap request header
+ * \param lsm striping information for the file
+ * \param fm_start logical start of mapping
+ * \param fm_end logical end of mapping
+ * \param start_stripe starting stripe will be returned in this
+ */
+static u64 fiemap_calc_fm_end_offset(struct ll_user_fiemap *fiemap,
+ struct lov_stripe_md *lsm, u64 fm_start,
+ u64 fm_end, int *start_stripe)
+{
+ u64 local_end = fiemap->fm_extents[0].fe_logical;
+ u64 lun_start, lun_end;
+ u64 fm_end_offset;
+ int stripe_no = -1, i;
+
+ if (fiemap->fm_extent_count == 0 ||
+ fiemap->fm_extents[0].fe_logical == 0)
+ return 0;
+
+ /* Find out stripe_no from ost_index saved in the fe_device */
+ for (i = 0; i < lsm->lsm_stripe_count; i++) {
+ struct lov_oinfo *oinfo = lsm->lsm_oinfo[i];
+
+ if (lov_oinfo_is_dummy(oinfo))
+ continue;
+
+ if (oinfo->loi_ost_idx == fiemap->fm_extents[0].fe_device) {
+ stripe_no = i;
+ break;
+ }
+ }
+ if (stripe_no == -1)
+ return -EINVAL;
+
+ /* If we have finished mapping on previous device, shift logical
+ * offset to start of next device */
+ if ((lov_stripe_intersects(lsm, stripe_no, fm_start, fm_end,
+ &lun_start, &lun_end)) != 0 &&
+ local_end < lun_end) {
+ fm_end_offset = local_end;
+ *start_stripe = stripe_no;
+ } else {
+ /* This is a special value to indicate that caller should
+ * calculate offset in next stripe. */
+ fm_end_offset = 0;
+ *start_stripe = (stripe_no + 1) % lsm->lsm_stripe_count;
+ }
+
+ return fm_end_offset;
+}
+
+/**
+ * We calculate on which OST the mapping will end. If the length of mapping
+ * is greater than (stripe_size * stripe_count) then the last_stripe will
+ * will be one just before start_stripe. Else we check if the mapping
+ * intersects each OST and find last_stripe.
+ * This function returns the last_stripe and also sets the stripe_count
+ * over which the mapping is spread
+ *
+ * \param lsm striping information for the file
+ * \param fm_start logical start of mapping
+ * \param fm_end logical end of mapping
+ * \param start_stripe starting stripe of the mapping
+ * \param stripe_count the number of stripes across which to map is returned
+ *
+ * \retval last_stripe return the last stripe of the mapping
+ */
+static int fiemap_calc_last_stripe(struct lov_stripe_md *lsm, u64 fm_start,
+ u64 fm_end, int start_stripe,
+ int *stripe_count)
+{
+ int last_stripe;
+ u64 obd_start, obd_end;
+ int i, j;
+
+ if (fm_end - fm_start > lsm->lsm_stripe_size * lsm->lsm_stripe_count) {
+ last_stripe = start_stripe < 1 ? lsm->lsm_stripe_count - 1 :
+ start_stripe - 1;
+ *stripe_count = lsm->lsm_stripe_count;
+ } else {
+ for (j = 0, i = start_stripe; j < lsm->lsm_stripe_count;
+ i = (i + 1) % lsm->lsm_stripe_count, j++) {
+ if ((lov_stripe_intersects(lsm, i, fm_start, fm_end,
+ &obd_start, &obd_end)) == 0)
+ break;
+ }
+ *stripe_count = j;
+ last_stripe = (start_stripe + j - 1) %lsm->lsm_stripe_count;
+ }
+
+ return last_stripe;
+}
+
+/**
+ * Set fe_device and copy extents from local buffer into main return buffer.
+ *
+ * \param fiemap fiemap request header
+ * \param lcl_fm_ext array of local fiemap extents to be copied
+ * \param ost_index OST index to be written into the fm_device field for each
+ extent
+ * \param ext_count number of extents to be copied
+ * \param current_extent where to start copying in main extent array
+ */
+static void fiemap_prepare_and_copy_exts(struct ll_user_fiemap *fiemap,
+ struct ll_fiemap_extent *lcl_fm_ext,
+ int ost_index, unsigned int ext_count,
+ int current_extent)
+{
+ char *to;
+ int ext;
+
+ for (ext = 0; ext < ext_count; ext++) {
+ lcl_fm_ext[ext].fe_device = ost_index;
+ lcl_fm_ext[ext].fe_flags |= FIEMAP_EXTENT_NET;
+ }
+
+ /* Copy fm_extent's from fm_local to return buffer */
+ to = (char *)fiemap + fiemap_count_to_size(current_extent);
+ memcpy(to, lcl_fm_ext, ext_count * sizeof(struct ll_fiemap_extent));
+}
+
+/**
+ * Break down the FIEMAP request and send appropriate calls to individual OSTs.
+ * This also handles the restarting of FIEMAP calls in case mapping overflows
+ * the available number of extents in single call.
+ */
+static int lov_fiemap(struct lov_obd *lov, __u32 keylen, void *key,
+ __u32 *vallen, void *val, struct lov_stripe_md *lsm)
+{
+ struct ll_fiemap_info_key *fm_key = key;
+ struct ll_user_fiemap *fiemap = val;
+ struct ll_user_fiemap *fm_local = NULL;
+ struct ll_fiemap_extent *lcl_fm_ext;
+ int count_local;
+ unsigned int get_num_extents = 0;
+ int ost_index = 0, actual_start_stripe, start_stripe;
+ u64 fm_start, fm_end, fm_length, fm_end_offset;
+ u64 curr_loc;
+ int current_extent = 0, rc = 0, i;
+ int ost_eof = 0; /* EOF for object */
+ int ost_done = 0; /* done with required mapping for this OST? */
+ int last_stripe;
+ int cur_stripe = 0, cur_stripe_wrap = 0, stripe_count;
+ unsigned int buffer_size = FIEMAP_BUFFER_SIZE;
+
+ if (!lsm_has_objects(lsm)) {
+ rc = 0;
+ goto out;
+ }
+
+ if (fiemap_count_to_size(fm_key->fiemap.fm_extent_count) < buffer_size)
+ buffer_size = fiemap_count_to_size(fm_key->fiemap.fm_extent_count);
+
+ OBD_ALLOC_LARGE(fm_local, buffer_size);
+ if (fm_local == NULL) {
+ rc = -ENOMEM;
+ goto out;
+ }
+ lcl_fm_ext = &fm_local->fm_extents[0];
+
+ count_local = fiemap_size_to_count(buffer_size);
+
+ memcpy(fiemap, &fm_key->fiemap, sizeof(*fiemap));
+ fm_start = fiemap->fm_start;
+ fm_length = fiemap->fm_length;
+ /* Calculate start stripe, last stripe and length of mapping */
+ actual_start_stripe = start_stripe = lov_stripe_number(lsm, fm_start);
+ fm_end = (fm_length == ~0ULL ? fm_key->oa.o_size :
+ fm_start + fm_length - 1);
+ /* If fm_length != ~0ULL but fm_start+fm_length-1 exceeds file size */
+ if (fm_end > fm_key->oa.o_size)
+ fm_end = fm_key->oa.o_size;
+
+ last_stripe = fiemap_calc_last_stripe(lsm, fm_start, fm_end,
+ actual_start_stripe, &stripe_count);
+
+ fm_end_offset = fiemap_calc_fm_end_offset(fiemap, lsm, fm_start,
+ fm_end, &start_stripe);
+ if (fm_end_offset == -EINVAL) {
+ rc = -EINVAL;
+ goto out;
+ }
+
+ if (fiemap_count_to_size(fiemap->fm_extent_count) > *vallen)
+ fiemap->fm_extent_count = fiemap_size_to_count(*vallen);
+ if (fiemap->fm_extent_count == 0) {
+ get_num_extents = 1;
+ count_local = 0;
+ }
+ /* Check each stripe */
+ for (cur_stripe = start_stripe, i = 0; i < stripe_count;
+ i++, cur_stripe = (cur_stripe + 1) % lsm->lsm_stripe_count) {
+ u64 req_fm_len; /* Stores length of required mapping */
+ u64 len_mapped_single_call;
+ u64 lun_start, lun_end, obd_object_end;
+ unsigned int ext_count;
+
+ cur_stripe_wrap = cur_stripe;
+
+ /* Find out range of mapping on this stripe */
+ if ((lov_stripe_intersects(lsm, cur_stripe, fm_start, fm_end,
+ &lun_start, &obd_object_end)) == 0)
+ continue;
+
+ if (lov_oinfo_is_dummy(lsm->lsm_oinfo[cur_stripe])) {
+ rc = -EIO;
+ goto out;
+ }
+
+ /* If this is a continuation FIEMAP call and we are on
+ * starting stripe then lun_start needs to be set to
+ * fm_end_offset */
+ if (fm_end_offset != 0 && cur_stripe == start_stripe)
+ lun_start = fm_end_offset;
+
+ if (fm_length != ~0ULL) {
+ /* Handle fm_start + fm_length overflow */
+ if (fm_start + fm_length < fm_start)
+ fm_length = ~0ULL - fm_start;
+ lun_end = lov_size_to_stripe(lsm, fm_start + fm_length,
+ cur_stripe);
+ } else {
+ lun_end = ~0ULL;
+ }
+
+ if (lun_start == lun_end)
+ continue;
+
+ req_fm_len = obd_object_end - lun_start;
+ fm_local->fm_length = 0;
+ len_mapped_single_call = 0;
+
+ /* If the output buffer is very large and the objects have many
+ * extents we may need to loop on a single OST repeatedly */
+ ost_eof = 0;
+ ost_done = 0;
+ do {
+ if (get_num_extents == 0) {
+ /* Don't get too many extents. */
+ if (current_extent + count_local >
+ fiemap->fm_extent_count)
+ count_local = fiemap->fm_extent_count -
+ current_extent;
+ }
+
+ lun_start += len_mapped_single_call;
+ fm_local->fm_length = req_fm_len - len_mapped_single_call;
+ req_fm_len = fm_local->fm_length;
+ fm_local->fm_extent_count = count_local;
+ fm_local->fm_mapped_extents = 0;
+ fm_local->fm_flags = fiemap->fm_flags;
+
+ fm_key->oa.o_oi = lsm->lsm_oinfo[cur_stripe]->loi_oi;
+ ost_index = lsm->lsm_oinfo[cur_stripe]->loi_ost_idx;
+
+ if (ost_index < 0 ||
+ ost_index >= lov->desc.ld_tgt_count) {
+ rc = -EINVAL;
+ goto out;
+ }
+
+ /* If OST is inactive, return extent with UNKNOWN flag */
+ if (!lov->lov_tgts[ost_index]->ltd_active) {
+ fm_local->fm_flags |= FIEMAP_EXTENT_LAST;
+ fm_local->fm_mapped_extents = 1;
+
+ lcl_fm_ext[0].fe_logical = lun_start;
+ lcl_fm_ext[0].fe_length = obd_object_end -
+ lun_start;
+ lcl_fm_ext[0].fe_flags |= FIEMAP_EXTENT_UNKNOWN;
+
+ goto inactive_tgt;
+ }
+
+ fm_local->fm_start = lun_start;
+ fm_local->fm_flags &= ~FIEMAP_FLAG_DEVICE_ORDER;
+ memcpy(&fm_key->fiemap, fm_local, sizeof(*fm_local));
+ *vallen=fiemap_count_to_size(fm_local->fm_extent_count);
+ rc = obd_get_info(NULL,
+ lov->lov_tgts[ost_index]->ltd_exp,
+ keylen, key, vallen, fm_local, lsm);
+ if (rc != 0)
+ goto out;
+
+inactive_tgt:
+ ext_count = fm_local->fm_mapped_extents;
+ if (ext_count == 0) {
+ ost_done = 1;
+ /* If last stripe has hole at the end,
+ * then we need to return */
+ if (cur_stripe_wrap == last_stripe) {
+ fiemap->fm_mapped_extents = 0;
+ goto finish;
+ }
+ break;
+ }
+
+ /* If we just need num of extents then go to next device */
+ if (get_num_extents) {
+ current_extent += ext_count;
+ break;
+ }
+
+ len_mapped_single_call = lcl_fm_ext[ext_count-1].fe_logical -
+ lun_start + lcl_fm_ext[ext_count - 1].fe_length;
+
+ /* Have we finished mapping on this device? */
+ if (req_fm_len <= len_mapped_single_call)
+ ost_done = 1;
+
+ /* Clear the EXTENT_LAST flag which can be present on
+ * last extent */
+ if (lcl_fm_ext[ext_count-1].fe_flags & FIEMAP_EXTENT_LAST)
+ lcl_fm_ext[ext_count - 1].fe_flags &=
+ ~FIEMAP_EXTENT_LAST;
+
+ curr_loc = lov_stripe_size(lsm,
+ lcl_fm_ext[ext_count - 1].fe_logical+
+ lcl_fm_ext[ext_count - 1].fe_length,
+ cur_stripe);
+ if (curr_loc >= fm_key->oa.o_size)
+ ost_eof = 1;
+
+ fiemap_prepare_and_copy_exts(fiemap, lcl_fm_ext,
+ ost_index, ext_count,
+ current_extent);
+
+ current_extent += ext_count;
+
+ /* Ran out of available extents? */
+ if (current_extent >= fiemap->fm_extent_count)
+ goto finish;
+ } while (ost_done == 0 && ost_eof == 0);
+
+ if (cur_stripe_wrap == last_stripe)
+ goto finish;
+ }
+
+finish:
+ /* Indicate that we are returning device offsets unless file just has
+ * single stripe */
+ if (lsm->lsm_stripe_count > 1)
+ fiemap->fm_flags |= FIEMAP_FLAG_DEVICE_ORDER;
+
+ if (get_num_extents)
+ goto skip_last_device_calc;
+
+ /* Check if we have reached the last stripe and whether mapping for that
+ * stripe is done. */
+ if (cur_stripe_wrap == last_stripe) {
+ if (ost_done || ost_eof)
+ fiemap->fm_extents[current_extent - 1].fe_flags |=
+ FIEMAP_EXTENT_LAST;
+ }
+
+skip_last_device_calc:
+ fiemap->fm_mapped_extents = current_extent;
+
+out:
+ OBD_FREE_LARGE(fm_local, buffer_size);
+ return rc;
+}
+
+static int lov_get_info(const struct lu_env *env, struct obd_export *exp,
+ __u32 keylen, void *key, __u32 *vallen, void *val,
+ struct lov_stripe_md *lsm)
+{
+ struct obd_device *obddev = class_exp2obd(exp);
+ struct lov_obd *lov = &obddev->u.lov;
+ int i, rc;
+
+ if (!vallen || !val)
+ return -EFAULT;
+
+ obd_getref(obddev);
+
+ if (KEY_IS(KEY_LOCK_TO_STRIPE)) {
+ struct {
+ char name[16];
+ struct ldlm_lock *lock;
+ } *data = key;
+ struct ldlm_res_id *res_id = &data->lock->l_resource->lr_name;
+ struct lov_oinfo *loi;
+ __u32 *stripe = val;
+
+ if (*vallen < sizeof(*stripe)) {
+ rc = -EFAULT;
+ goto out;
+ }
+ *vallen = sizeof(*stripe);
+
+ /* XXX This is another one of those bits that will need to
+ * change if we ever actually support nested LOVs. It uses
+ * the lock's export to find out which stripe it is. */
+ /* XXX - it's assumed all the locks for deleted OSTs have
+ * been cancelled. Also, the export for deleted OSTs will
+ * be NULL and won't match the lock's export. */
+ for (i = 0; i < lsm->lsm_stripe_count; i++) {
+ loi = lsm->lsm_oinfo[i];
+ if (lov_oinfo_is_dummy(loi))
+ continue;
+
+ if (!lov->lov_tgts[loi->loi_ost_idx])
+ continue;
+ if (lov->lov_tgts[loi->loi_ost_idx]->ltd_exp ==
+ data->lock->l_conn_export &&
+ ostid_res_name_eq(&loi->loi_oi, res_id)) {
+ *stripe = i;
+ rc = 0;
+ goto out;
+ }
+ }
+ LDLM_ERROR(data->lock, "lock on inode without such object");
+ dump_lsm(D_ERROR, lsm);
+ rc = -ENXIO;
+ goto out;
+ } else if (KEY_IS(KEY_LAST_ID)) {
+ struct obd_id_info *info = val;
+ __u32 size = sizeof(u64);
+ struct lov_tgt_desc *tgt;
+
+ LASSERT(*vallen == sizeof(struct obd_id_info));
+ tgt = lov->lov_tgts[info->idx];
+
+ if (!tgt || !tgt->ltd_active) {
+ rc = -ESRCH;
+ goto out;
+ }
+
+ rc = obd_get_info(env, tgt->ltd_exp, keylen, key,
+ &size, info->data, NULL);
+ rc = 0;
+ goto out;
+ } else if (KEY_IS(KEY_LOVDESC)) {
+ struct lov_desc *desc_ret = val;
+ *desc_ret = lov->desc;
+
+ rc = 0;
+ goto out;
+ } else if (KEY_IS(KEY_FIEMAP)) {
+ rc = lov_fiemap(lov, keylen, key, vallen, val, lsm);
+ goto out;
+ } else if (KEY_IS(KEY_CONNECT_FLAG)) {
+ struct lov_tgt_desc *tgt;
+ __u64 ost_idx = *((__u64 *)val);
+
+ LASSERT(*vallen == sizeof(__u64));
+ LASSERT(ost_idx < lov->desc.ld_tgt_count);
+ tgt = lov->lov_tgts[ost_idx];
+
+ if (!tgt || !tgt->ltd_exp) {
+ rc = -ESRCH;
+ goto out;
+ }
+
+ *((__u64 *)val) = exp_connect_flags(tgt->ltd_exp);
+ rc = 0;
+ goto out;
+ } else if (KEY_IS(KEY_TGT_COUNT)) {
+ *((int *)val) = lov->desc.ld_tgt_count;
+ rc = 0;
+ goto out;
+ }
+
+ rc = -EINVAL;
+
+out:
+ obd_putref(obddev);
+ return rc;
+}
+
+static int lov_set_info_async(const struct lu_env *env, struct obd_export *exp,
+ u32 keylen, void *key, u32 vallen,
+ void *val, struct ptlrpc_request_set *set)
+{
+ struct obd_device *obddev = class_exp2obd(exp);
+ struct lov_obd *lov = &obddev->u.lov;
+ u32 count;
+ int i, rc = 0, err;
+ struct lov_tgt_desc *tgt;
+ unsigned incr, check_uuid,
+ do_inactive, no_set;
+ unsigned next_id = 0, mds_con = 0, capa = 0;
+
+ incr = check_uuid = do_inactive = no_set = 0;
+ if (set == NULL) {
+ no_set = 1;
+ set = ptlrpc_prep_set();
+ if (!set)
+ return -ENOMEM;
+ }
+
+ obd_getref(obddev);
+ count = lov->desc.ld_tgt_count;
+
+ if (KEY_IS(KEY_NEXT_ID)) {
+ count = vallen / sizeof(struct obd_id_info);
+ vallen = sizeof(u64);
+ incr = sizeof(struct obd_id_info);
+ do_inactive = 1;
+ next_id = 1;
+ } else if (KEY_IS(KEY_CHECKSUM)) {
+ do_inactive = 1;
+ } else if (KEY_IS(KEY_EVICT_BY_NID)) {
+ /* use defaults: do_inactive = incr = 0; */
+ } else if (KEY_IS(KEY_MDS_CONN)) {
+ mds_con = 1;
+ } else if (KEY_IS(KEY_CAPA_KEY)) {
+ capa = 1;
+ } else if (KEY_IS(KEY_CACHE_SET)) {
+ LASSERT(lov->lov_cache == NULL);
+ lov->lov_cache = val;
+ do_inactive = 1;
+ }
+
+ for (i = 0; i < count; i++, val = (char *)val + incr) {
+ if (next_id) {
+ tgt = lov->lov_tgts[((struct obd_id_info *)val)->idx];
+ } else {
+ tgt = lov->lov_tgts[i];
+ }
+ /* OST was disconnected */
+ if (!tgt || !tgt->ltd_exp)
+ continue;
+
+ /* OST is inactive and we don't want inactive OSCs */
+ if (!tgt->ltd_active && !do_inactive)
+ continue;
+
+ if (mds_con) {
+ struct mds_group_info *mgi;
+
+ LASSERT(vallen == sizeof(*mgi));
+ mgi = (struct mds_group_info *)val;
+
+ /* Only want a specific OSC */
+ if (mgi->uuid && !obd_uuid_equals(mgi->uuid,
+ &tgt->ltd_uuid))
+ continue;
+
+ err = obd_set_info_async(env, tgt->ltd_exp,
+ keylen, key, sizeof(int),
+ &mgi->group, set);
+ } else if (next_id) {
+ err = obd_set_info_async(env, tgt->ltd_exp,
+ keylen, key, vallen,
+ ((struct obd_id_info *)val)->data, set);
+ } else if (capa) {
+ struct mds_capa_info *info = (struct mds_capa_info *)val;
+
+ LASSERT(vallen == sizeof(*info));
+
+ /* Only want a specific OSC */
+ if (info->uuid &&
+ !obd_uuid_equals(info->uuid, &tgt->ltd_uuid))
+ continue;
+
+ err = obd_set_info_async(env, tgt->ltd_exp, keylen,
+ key, sizeof(*info->capa),
+ info->capa, set);
+ } else {
+ /* Only want a specific OSC */
+ if (check_uuid &&
+ !obd_uuid_equals(val, &tgt->ltd_uuid))
+ continue;
+
+ err = obd_set_info_async(env, tgt->ltd_exp,
+ keylen, key, vallen, val, set);
+ }
+
+ if (!rc)
+ rc = err;
+ }
+
+ obd_putref(obddev);
+ if (no_set) {
+ err = ptlrpc_set_wait(set);
+ if (!rc)
+ rc = err;
+ ptlrpc_set_destroy(set);
+ }
+ return rc;
+}
+
+void lov_stripe_lock(struct lov_stripe_md *md)
+ __acquires(&md->lsm_lock)
+{
+ LASSERT(md->lsm_lock_owner != current_pid());
+ spin_lock(&md->lsm_lock);
+ LASSERT(md->lsm_lock_owner == 0);
+ md->lsm_lock_owner = current_pid();
+}
+EXPORT_SYMBOL(lov_stripe_lock);
+
+void lov_stripe_unlock(struct lov_stripe_md *md)
+ __releases(&md->lsm_lock)
+{
+ LASSERT(md->lsm_lock_owner == current_pid());
+ md->lsm_lock_owner = 0;
+ spin_unlock(&md->lsm_lock);
+}
+EXPORT_SYMBOL(lov_stripe_unlock);
+
+static int lov_quotactl(struct obd_device *obd, struct obd_export *exp,
+ struct obd_quotactl *oqctl)
+{
+ struct lov_obd *lov = &obd->u.lov;
+ struct lov_tgt_desc *tgt;
+ __u64 curspace = 0;
+ __u64 bhardlimit = 0;
+ int i, rc = 0;
+
+ if (oqctl->qc_cmd != LUSTRE_Q_QUOTAON &&
+ oqctl->qc_cmd != LUSTRE_Q_QUOTAOFF &&
+ oqctl->qc_cmd != Q_GETOQUOTA &&
+ oqctl->qc_cmd != Q_INITQUOTA &&
+ oqctl->qc_cmd != LUSTRE_Q_SETQUOTA &&
+ oqctl->qc_cmd != Q_FINVALIDATE) {
+ CERROR("bad quota opc %x for lov obd", oqctl->qc_cmd);
+ return -EFAULT;
+ }
+
+ /* for lov tgt */
+ obd_getref(obd);
+ for (i = 0; i < lov->desc.ld_tgt_count; i++) {
+ int err;
+
+ tgt = lov->lov_tgts[i];
+
+ if (!tgt)
+ continue;
+
+ if (!tgt->ltd_active || tgt->ltd_reap) {
+ if (oqctl->qc_cmd == Q_GETOQUOTA &&
+ lov->lov_tgts[i]->ltd_activate) {
+ rc = -EREMOTEIO;
+ CERROR("ost %d is inactive\n", i);
+ } else {
+ CDEBUG(D_HA, "ost %d is inactive\n", i);
+ }
+ continue;
+ }
+
+ err = obd_quotactl(tgt->ltd_exp, oqctl);
+ if (err) {
+ if (tgt->ltd_active && !rc)
+ rc = err;
+ continue;
+ }
+
+ if (oqctl->qc_cmd == Q_GETOQUOTA) {
+ curspace += oqctl->qc_dqblk.dqb_curspace;
+ bhardlimit += oqctl->qc_dqblk.dqb_bhardlimit;
+ }
+ }
+ obd_putref(obd);
+
+ if (oqctl->qc_cmd == Q_GETOQUOTA) {
+ oqctl->qc_dqblk.dqb_curspace = curspace;
+ oqctl->qc_dqblk.dqb_bhardlimit = bhardlimit;
+ }
+ return rc;
+}
+
+static int lov_quotacheck(struct obd_device *obd, struct obd_export *exp,
+ struct obd_quotactl *oqctl)
+{
+ struct lov_obd *lov = &obd->u.lov;
+ int i, rc = 0;
+
+ obd_getref(obd);
+
+ for (i = 0; i < lov->desc.ld_tgt_count; i++) {
+ if (!lov->lov_tgts[i])
+ continue;
+
+ /* Skip quota check on the administratively disabled OSTs. */
+ if (!lov->lov_tgts[i]->ltd_activate) {
+ CWARN("lov idx %d was administratively disabled, skip quotacheck on it.\n",
+ i);
+ continue;
+ }
+
+ if (!lov->lov_tgts[i]->ltd_active) {
+ CERROR("lov idx %d inactive\n", i);
+ rc = -EIO;
+ goto out;
+ }
+ }
+
+ for (i = 0; i < lov->desc.ld_tgt_count; i++) {
+ int err;
+
+ if (!lov->lov_tgts[i] || !lov->lov_tgts[i]->ltd_activate)
+ continue;
+
+ err = obd_quotacheck(lov->lov_tgts[i]->ltd_exp, oqctl);
+ if (err && !rc)
+ rc = err;
+ }
+
+out:
+ obd_putref(obd);
+
+ return rc;
+}
+
+static struct obd_ops lov_obd_ops = {
+ .o_owner = THIS_MODULE,
+ .o_setup = lov_setup,
+ .o_precleanup = lov_precleanup,
+ .o_cleanup = lov_cleanup,
+ /*.o_process_config = lov_process_config,*/
+ .o_connect = lov_connect,
+ .o_disconnect = lov_disconnect,
+ .o_statfs = lov_statfs,
+ .o_statfs_async = lov_statfs_async,
+ .o_packmd = lov_packmd,
+ .o_unpackmd = lov_unpackmd,
+ .o_create = lov_create,
+ .o_destroy = lov_destroy,
+ .o_getattr_async = lov_getattr_async,
+ .o_setattr_async = lov_setattr_async,
+ .o_adjust_kms = lov_adjust_kms,
+ .o_find_cbdata = lov_find_cbdata,
+ .o_iocontrol = lov_iocontrol,
+ .o_get_info = lov_get_info,
+ .o_set_info_async = lov_set_info_async,
+ .o_notify = lov_notify,
+ .o_pool_new = lov_pool_new,
+ .o_pool_rem = lov_pool_remove,
+ .o_pool_add = lov_pool_add,
+ .o_pool_del = lov_pool_del,
+ .o_getref = lov_getref,
+ .o_putref = lov_putref,
+ .o_quotactl = lov_quotactl,
+ .o_quotacheck = lov_quotacheck,
+};
+
+struct kmem_cache *lov_oinfo_slab;
+
+static int __init lov_init(void)
+{
+ struct lprocfs_static_vars lvars = { NULL };
+ int rc;
+
+ /* print an address of _any_ initialized kernel symbol from this
+ * module, to allow debugging with gdb that doesn't support data
+ * symbols from modules.*/
+ CDEBUG(D_INFO, "Lustre LOV module (%p).\n", &lov_caches);
+
+ rc = lu_kmem_init(lov_caches);
+ if (rc)
+ return rc;
+
+ lov_oinfo_slab = kmem_cache_create("lov_oinfo",
+ sizeof(struct lov_oinfo),
+ 0, SLAB_HWCACHE_ALIGN, NULL);
+ if (lov_oinfo_slab == NULL) {
+ lu_kmem_fini(lov_caches);
+ return -ENOMEM;
+ }
+ lprocfs_lov_init_vars(&lvars);
+
+ rc = class_register_type(&lov_obd_ops, NULL, lvars.module_vars,
+ LUSTRE_LOV_NAME, &lov_device_type);
+
+ if (rc) {
+ kmem_cache_destroy(lov_oinfo_slab);
+ lu_kmem_fini(lov_caches);
+ }
+
+ return rc;
+}
+
+static void /*__exit*/ lov_exit(void)
+{
+ class_unregister_type(LUSTRE_LOV_NAME);
+ kmem_cache_destroy(lov_oinfo_slab);
+
+ lu_kmem_fini(lov_caches);
+}
+
+MODULE_AUTHOR("Sun Microsystems, Inc. <http://www.lustre.org/>");
+MODULE_DESCRIPTION("Lustre Logical Object Volume OBD driver");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(LUSTRE_VERSION_STRING);
+
+module_init(lov_init);
+module_exit(lov_exit);
diff --git a/drivers/staging/lustre/lustre/lov/lov_object.c b/drivers/staging/lustre/lustre/lov/lov_object.c
new file mode 100644
index 000000000..a22342fa7
--- /dev/null
+++ b/drivers/staging/lustre/lustre/lov/lov_object.c
@@ -0,0 +1,1001 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2011, 2012, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ *
+ * Implementation of cl_object for LOV layer.
+ *
+ * Author: Nikita Danilov <nikita.danilov@sun.com>
+ * Author: Jinshan Xiong <jinshan.xiong@whamcloud.com>
+ */
+
+#define DEBUG_SUBSYSTEM S_LOV
+
+#include "lov_cl_internal.h"
+#include "../include/lclient.h"
+
+/** \addtogroup lov
+ * @{
+ */
+
+/*****************************************************************************
+ *
+ * Layout operations.
+ *
+ */
+
+struct lov_layout_operations {
+ int (*llo_init)(const struct lu_env *env, struct lov_device *dev,
+ struct lov_object *lov,
+ const struct cl_object_conf *conf,
+ union lov_layout_state *state);
+ int (*llo_delete)(const struct lu_env *env, struct lov_object *lov,
+ union lov_layout_state *state);
+ void (*llo_fini)(const struct lu_env *env, struct lov_object *lov,
+ union lov_layout_state *state);
+ void (*llo_install)(const struct lu_env *env, struct lov_object *lov,
+ union lov_layout_state *state);
+ int (*llo_print)(const struct lu_env *env, void *cookie,
+ lu_printer_t p, const struct lu_object *o);
+ int (*llo_page_init)(const struct lu_env *env, struct cl_object *obj,
+ struct cl_page *page, struct page *vmpage);
+ int (*llo_lock_init)(const struct lu_env *env,
+ struct cl_object *obj, struct cl_lock *lock,
+ const struct cl_io *io);
+ int (*llo_io_init)(const struct lu_env *env,
+ struct cl_object *obj, struct cl_io *io);
+ int (*llo_getattr)(const struct lu_env *env, struct cl_object *obj,
+ struct cl_attr *attr);
+};
+
+static int lov_layout_wait(const struct lu_env *env, struct lov_object *lov);
+
+/*****************************************************************************
+ *
+ * Lov object layout operations.
+ *
+ */
+
+static void lov_install_empty(const struct lu_env *env,
+ struct lov_object *lov,
+ union lov_layout_state *state)
+{
+ /*
+ * File without objects.
+ */
+}
+
+static int lov_init_empty(const struct lu_env *env,
+ struct lov_device *dev, struct lov_object *lov,
+ const struct cl_object_conf *conf,
+ union lov_layout_state *state)
+{
+ return 0;
+}
+
+static void lov_install_raid0(const struct lu_env *env,
+ struct lov_object *lov,
+ union lov_layout_state *state)
+{
+}
+
+static struct cl_object *lov_sub_find(const struct lu_env *env,
+ struct cl_device *dev,
+ const struct lu_fid *fid,
+ const struct cl_object_conf *conf)
+{
+ struct lu_object *o;
+
+ o = lu_object_find_at(env, cl2lu_dev(dev), fid, &conf->coc_lu);
+ LASSERT(ergo(!IS_ERR(o), o->lo_dev->ld_type == &lovsub_device_type));
+ return lu2cl(o);
+}
+
+static int lov_init_sub(const struct lu_env *env, struct lov_object *lov,
+ struct cl_object *stripe, struct lov_layout_raid0 *r0,
+ int idx)
+{
+ struct cl_object_header *hdr;
+ struct cl_object_header *subhdr;
+ struct cl_object_header *parent;
+ struct lov_oinfo *oinfo;
+ int result;
+
+ if (OBD_FAIL_CHECK(OBD_FAIL_LOV_INIT)) {
+ /* For sanity:test_206.
+ * Do not leave the object in cache to avoid accessing
+ * freed memory. This is because osc_object is referring to
+ * lov_oinfo of lsm_stripe_data which will be freed due to
+ * this failure. */
+ cl_object_kill(env, stripe);
+ cl_object_put(env, stripe);
+ return -EIO;
+ }
+
+ hdr = cl_object_header(lov2cl(lov));
+ subhdr = cl_object_header(stripe);
+
+ oinfo = lov->lo_lsm->lsm_oinfo[idx];
+ CDEBUG(D_INODE, DFID"@%p[%d] -> "DFID"@%p: ostid: "DOSTID
+ " idx: %d gen: %d\n",
+ PFID(&subhdr->coh_lu.loh_fid), subhdr, idx,
+ PFID(&hdr->coh_lu.loh_fid), hdr, POSTID(&oinfo->loi_oi),
+ oinfo->loi_ost_idx, oinfo->loi_ost_gen);
+
+ /* reuse ->coh_attr_guard to protect coh_parent change */
+ spin_lock(&subhdr->coh_attr_guard);
+ parent = subhdr->coh_parent;
+ if (parent == NULL) {
+ subhdr->coh_parent = hdr;
+ spin_unlock(&subhdr->coh_attr_guard);
+ subhdr->coh_nesting = hdr->coh_nesting + 1;
+ lu_object_ref_add(&stripe->co_lu, "lov-parent", lov);
+ r0->lo_sub[idx] = cl2lovsub(stripe);
+ r0->lo_sub[idx]->lso_super = lov;
+ r0->lo_sub[idx]->lso_index = idx;
+ result = 0;
+ } else {
+ struct lu_object *old_obj;
+ struct lov_object *old_lov;
+ unsigned int mask = D_INODE;
+
+ spin_unlock(&subhdr->coh_attr_guard);
+ old_obj = lu_object_locate(&parent->coh_lu, &lov_device_type);
+ LASSERT(old_obj != NULL);
+ old_lov = cl2lov(lu2cl(old_obj));
+ if (old_lov->lo_layout_invalid) {
+ /* the object's layout has already changed but isn't
+ * refreshed */
+ lu_object_unhash(env, &stripe->co_lu);
+ result = -EAGAIN;
+ } else {
+ mask = D_ERROR;
+ result = -EIO;
+ }
+
+ LU_OBJECT_DEBUG(mask, env, &stripe->co_lu,
+ "stripe %d is already owned.\n", idx);
+ LU_OBJECT_DEBUG(mask, env, old_obj, "owned.\n");
+ LU_OBJECT_HEADER(mask, env, lov2lu(lov), "try to own.\n");
+ cl_object_put(env, stripe);
+ }
+ return result;
+}
+
+static int lov_init_raid0(const struct lu_env *env,
+ struct lov_device *dev, struct lov_object *lov,
+ const struct cl_object_conf *conf,
+ union lov_layout_state *state)
+{
+ int result;
+ int i;
+
+ struct cl_object *stripe;
+ struct lov_thread_info *lti = lov_env_info(env);
+ struct cl_object_conf *subconf = &lti->lti_stripe_conf;
+ struct lov_stripe_md *lsm = conf->u.coc_md->lsm;
+ struct lu_fid *ofid = &lti->lti_fid;
+ struct lov_layout_raid0 *r0 = &state->raid0;
+
+ if (lsm->lsm_magic != LOV_MAGIC_V1 && lsm->lsm_magic != LOV_MAGIC_V3) {
+ dump_lsm(D_ERROR, lsm);
+ LASSERTF(0, "magic mismatch, expected %d/%d, actual %d.\n",
+ LOV_MAGIC_V1, LOV_MAGIC_V3, lsm->lsm_magic);
+ }
+
+ LASSERT(lov->lo_lsm == NULL);
+ lov->lo_lsm = lsm_addref(lsm);
+ r0->lo_nr = lsm->lsm_stripe_count;
+ LASSERT(r0->lo_nr <= lov_targets_nr(dev));
+
+ OBD_ALLOC_LARGE(r0->lo_sub, r0->lo_nr * sizeof(r0->lo_sub[0]));
+ if (r0->lo_sub != NULL) {
+ result = 0;
+ subconf->coc_inode = conf->coc_inode;
+ spin_lock_init(&r0->lo_sub_lock);
+ /*
+ * Create stripe cl_objects.
+ */
+ for (i = 0; i < r0->lo_nr && result == 0; ++i) {
+ struct cl_device *subdev;
+ struct lov_oinfo *oinfo = lsm->lsm_oinfo[i];
+ int ost_idx = oinfo->loi_ost_idx;
+
+ if (lov_oinfo_is_dummy(oinfo))
+ continue;
+
+ result = ostid_to_fid(ofid, &oinfo->loi_oi,
+ oinfo->loi_ost_idx);
+ if (result != 0)
+ goto out;
+
+ subdev = lovsub2cl_dev(dev->ld_target[ost_idx]);
+ subconf->u.coc_oinfo = oinfo;
+ LASSERTF(subdev != NULL, "not init ost %d\n", ost_idx);
+ /* In the function below, .hs_keycmp resolves to
+ * lu_obj_hop_keycmp() */
+ /* coverity[overrun-buffer-val] */
+ stripe = lov_sub_find(env, subdev, ofid, subconf);
+ if (!IS_ERR(stripe)) {
+ result = lov_init_sub(env, lov, stripe, r0, i);
+ if (result == -EAGAIN) { /* try again */
+ --i;
+ result = 0;
+ }
+ } else {
+ result = PTR_ERR(stripe);
+ }
+ }
+ } else
+ result = -ENOMEM;
+out:
+ return result;
+}
+
+static int lov_init_released(const struct lu_env *env,
+ struct lov_device *dev, struct lov_object *lov,
+ const struct cl_object_conf *conf,
+ union lov_layout_state *state)
+{
+ struct lov_stripe_md *lsm = conf->u.coc_md->lsm;
+
+ LASSERT(lsm != NULL);
+ LASSERT(lsm_is_released(lsm));
+ LASSERT(lov->lo_lsm == NULL);
+
+ lov->lo_lsm = lsm_addref(lsm);
+ return 0;
+}
+
+static int lov_delete_empty(const struct lu_env *env, struct lov_object *lov,
+ union lov_layout_state *state)
+{
+ LASSERT(lov->lo_type == LLT_EMPTY || lov->lo_type == LLT_RELEASED);
+
+ lov_layout_wait(env, lov);
+
+ cl_object_prune(env, &lov->lo_cl);
+ return 0;
+}
+
+static void lov_subobject_kill(const struct lu_env *env, struct lov_object *lov,
+ struct lovsub_object *los, int idx)
+{
+ struct cl_object *sub;
+ struct lov_layout_raid0 *r0;
+ struct lu_site *site;
+ struct lu_site_bkt_data *bkt;
+ wait_queue_t *waiter;
+
+ r0 = &lov->u.raid0;
+ LASSERT(r0->lo_sub[idx] == los);
+
+ sub = lovsub2cl(los);
+ site = sub->co_lu.lo_dev->ld_site;
+ bkt = lu_site_bkt_from_fid(site, &sub->co_lu.lo_header->loh_fid);
+
+ cl_object_kill(env, sub);
+ /* release a reference to the sub-object and ... */
+ lu_object_ref_del(&sub->co_lu, "lov-parent", lov);
+ cl_object_put(env, sub);
+
+ /* ... wait until it is actually destroyed---sub-object clears its
+ * ->lo_sub[] slot in lovsub_object_fini() */
+ if (r0->lo_sub[idx] == los) {
+ waiter = &lov_env_info(env)->lti_waiter;
+ init_waitqueue_entry(waiter, current);
+ add_wait_queue(&bkt->lsb_marche_funebre, waiter);
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ while (1) {
+ /* this wait-queue is signaled at the end of
+ * lu_object_free(). */
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ spin_lock(&r0->lo_sub_lock);
+ if (r0->lo_sub[idx] == los) {
+ spin_unlock(&r0->lo_sub_lock);
+ schedule();
+ } else {
+ spin_unlock(&r0->lo_sub_lock);
+ set_current_state(TASK_RUNNING);
+ break;
+ }
+ }
+ remove_wait_queue(&bkt->lsb_marche_funebre, waiter);
+ }
+ LASSERT(r0->lo_sub[idx] == NULL);
+}
+
+static int lov_delete_raid0(const struct lu_env *env, struct lov_object *lov,
+ union lov_layout_state *state)
+{
+ struct lov_layout_raid0 *r0 = &state->raid0;
+ struct lov_stripe_md *lsm = lov->lo_lsm;
+ int i;
+
+ dump_lsm(D_INODE, lsm);
+
+ lov_layout_wait(env, lov);
+ if (r0->lo_sub != NULL) {
+ for (i = 0; i < r0->lo_nr; ++i) {
+ struct lovsub_object *los = r0->lo_sub[i];
+
+ if (los != NULL) {
+ cl_locks_prune(env, &los->lso_cl, 1);
+ /*
+ * If top-level object is to be evicted from
+ * the cache, so are its sub-objects.
+ */
+ lov_subobject_kill(env, lov, los, i);
+ }
+ }
+ }
+ cl_object_prune(env, &lov->lo_cl);
+ return 0;
+}
+
+static void lov_fini_empty(const struct lu_env *env, struct lov_object *lov,
+ union lov_layout_state *state)
+{
+ LASSERT(lov->lo_type == LLT_EMPTY || lov->lo_type == LLT_RELEASED);
+}
+
+static void lov_fini_raid0(const struct lu_env *env, struct lov_object *lov,
+ union lov_layout_state *state)
+{
+ struct lov_layout_raid0 *r0 = &state->raid0;
+
+ if (r0->lo_sub != NULL) {
+ OBD_FREE_LARGE(r0->lo_sub, r0->lo_nr * sizeof(r0->lo_sub[0]));
+ r0->lo_sub = NULL;
+ }
+
+ dump_lsm(D_INODE, lov->lo_lsm);
+ lov_free_memmd(&lov->lo_lsm);
+}
+
+static void lov_fini_released(const struct lu_env *env, struct lov_object *lov,
+ union lov_layout_state *state)
+{
+ dump_lsm(D_INODE, lov->lo_lsm);
+ lov_free_memmd(&lov->lo_lsm);
+}
+
+static int lov_print_empty(const struct lu_env *env, void *cookie,
+ lu_printer_t p, const struct lu_object *o)
+{
+ (*p)(env, cookie, "empty %d\n", lu2lov(o)->lo_layout_invalid);
+ return 0;
+}
+
+static int lov_print_raid0(const struct lu_env *env, void *cookie,
+ lu_printer_t p, const struct lu_object *o)
+{
+ struct lov_object *lov = lu2lov(o);
+ struct lov_layout_raid0 *r0 = lov_r0(lov);
+ struct lov_stripe_md *lsm = lov->lo_lsm;
+ int i;
+
+ (*p)(env, cookie, "stripes: %d, %s, lsm{%p 0x%08X %d %u %u}:\n",
+ r0->lo_nr, lov->lo_layout_invalid ? "invalid" : "valid", lsm,
+ lsm->lsm_magic, atomic_read(&lsm->lsm_refc),
+ lsm->lsm_stripe_count, lsm->lsm_layout_gen);
+ for (i = 0; i < r0->lo_nr; ++i) {
+ struct lu_object *sub;
+
+ if (r0->lo_sub[i] != NULL) {
+ sub = lovsub2lu(r0->lo_sub[i]);
+ lu_object_print(env, cookie, p, sub);
+ } else {
+ (*p)(env, cookie, "sub %d absent\n", i);
+ }
+ }
+ return 0;
+}
+
+static int lov_print_released(const struct lu_env *env, void *cookie,
+ lu_printer_t p, const struct lu_object *o)
+{
+ struct lov_object *lov = lu2lov(o);
+ struct lov_stripe_md *lsm = lov->lo_lsm;
+
+ (*p)(env, cookie,
+ "released: %s, lsm{%p 0x%08X %d %u %u}:\n",
+ lov->lo_layout_invalid ? "invalid" : "valid", lsm,
+ lsm->lsm_magic, atomic_read(&lsm->lsm_refc),
+ lsm->lsm_stripe_count, lsm->lsm_layout_gen);
+ return 0;
+}
+
+/**
+ * Implements cl_object_operations::coo_attr_get() method for an object
+ * without stripes (LLT_EMPTY layout type).
+ *
+ * The only attributes this layer is authoritative in this case is
+ * cl_attr::cat_blocks---it's 0.
+ */
+static int lov_attr_get_empty(const struct lu_env *env, struct cl_object *obj,
+ struct cl_attr *attr)
+{
+ attr->cat_blocks = 0;
+ return 0;
+}
+
+static int lov_attr_get_raid0(const struct lu_env *env, struct cl_object *obj,
+ struct cl_attr *attr)
+{
+ struct lov_object *lov = cl2lov(obj);
+ struct lov_layout_raid0 *r0 = lov_r0(lov);
+ struct cl_attr *lov_attr = &r0->lo_attr;
+ int result = 0;
+
+ /* this is called w/o holding type guard mutex, so it must be inside
+ * an on going IO otherwise lsm may be replaced.
+ * LU-2117: it turns out there exists one exception. For mmaped files,
+ * the lock of those files may be requested in the other file's IO
+ * context, and this function is called in ccc_lock_state(), it will
+ * hit this assertion.
+ * Anyway, it's still okay to call attr_get w/o type guard as layout
+ * can't go if locks exist. */
+ /* LASSERT(atomic_read(&lsm->lsm_refc) > 1); */
+
+ if (!r0->lo_attr_valid) {
+ struct lov_stripe_md *lsm = lov->lo_lsm;
+ struct ost_lvb *lvb = &lov_env_info(env)->lti_lvb;
+ __u64 kms = 0;
+
+ memset(lvb, 0, sizeof(*lvb));
+ /* XXX: timestamps can be negative by sanity:test_39m,
+ * how can it be? */
+ lvb->lvb_atime = LLONG_MIN;
+ lvb->lvb_ctime = LLONG_MIN;
+ lvb->lvb_mtime = LLONG_MIN;
+
+ /*
+ * XXX that should be replaced with a loop over sub-objects,
+ * doing cl_object_attr_get() on them. But for now, let's
+ * reuse old lov code.
+ */
+
+ /*
+ * XXX take lsm spin-lock to keep lov_merge_lvb_kms()
+ * happy. It's not needed, because new code uses
+ * ->coh_attr_guard spin-lock to protect consistency of
+ * sub-object attributes.
+ */
+ lov_stripe_lock(lsm);
+ result = lov_merge_lvb_kms(lsm, lvb, &kms);
+ lov_stripe_unlock(lsm);
+ if (result == 0) {
+ cl_lvb2attr(lov_attr, lvb);
+ lov_attr->cat_kms = kms;
+ r0->lo_attr_valid = 1;
+ }
+ }
+ if (result == 0) { /* merge results */
+ attr->cat_blocks = lov_attr->cat_blocks;
+ attr->cat_size = lov_attr->cat_size;
+ attr->cat_kms = lov_attr->cat_kms;
+ if (attr->cat_atime < lov_attr->cat_atime)
+ attr->cat_atime = lov_attr->cat_atime;
+ if (attr->cat_ctime < lov_attr->cat_ctime)
+ attr->cat_ctime = lov_attr->cat_ctime;
+ if (attr->cat_mtime < lov_attr->cat_mtime)
+ attr->cat_mtime = lov_attr->cat_mtime;
+ }
+ return result;
+}
+
+static const struct lov_layout_operations lov_dispatch[] = {
+ [LLT_EMPTY] = {
+ .llo_init = lov_init_empty,
+ .llo_delete = lov_delete_empty,
+ .llo_fini = lov_fini_empty,
+ .llo_install = lov_install_empty,
+ .llo_print = lov_print_empty,
+ .llo_page_init = lov_page_init_empty,
+ .llo_lock_init = lov_lock_init_empty,
+ .llo_io_init = lov_io_init_empty,
+ .llo_getattr = lov_attr_get_empty
+ },
+ [LLT_RAID0] = {
+ .llo_init = lov_init_raid0,
+ .llo_delete = lov_delete_raid0,
+ .llo_fini = lov_fini_raid0,
+ .llo_install = lov_install_raid0,
+ .llo_print = lov_print_raid0,
+ .llo_page_init = lov_page_init_raid0,
+ .llo_lock_init = lov_lock_init_raid0,
+ .llo_io_init = lov_io_init_raid0,
+ .llo_getattr = lov_attr_get_raid0
+ },
+ [LLT_RELEASED] = {
+ .llo_init = lov_init_released,
+ .llo_delete = lov_delete_empty,
+ .llo_fini = lov_fini_released,
+ .llo_install = lov_install_empty,
+ .llo_print = lov_print_released,
+ .llo_page_init = lov_page_init_empty,
+ .llo_lock_init = lov_lock_init_empty,
+ .llo_io_init = lov_io_init_released,
+ .llo_getattr = lov_attr_get_empty
+ }
+};
+
+/**
+ * Performs a double-dispatch based on the layout type of an object.
+ */
+#define LOV_2DISPATCH_NOLOCK(obj, op, ...) \
+({ \
+ struct lov_object *__obj = (obj); \
+ enum lov_layout_type __llt; \
+ \
+ __llt = __obj->lo_type; \
+ LASSERT(0 <= __llt && __llt < ARRAY_SIZE(lov_dispatch)); \
+ lov_dispatch[__llt].op(__VA_ARGS__); \
+})
+
+/**
+ * Return lov_layout_type associated with a given lsm
+ */
+static enum lov_layout_type lov_type(struct lov_stripe_md *lsm)
+{
+ if (lsm == NULL)
+ return LLT_EMPTY;
+ if (lsm_is_released(lsm))
+ return LLT_RELEASED;
+ return LLT_RAID0;
+}
+
+static inline void lov_conf_freeze(struct lov_object *lov)
+{
+ if (lov->lo_owner != current)
+ down_read(&lov->lo_type_guard);
+}
+
+static inline void lov_conf_thaw(struct lov_object *lov)
+{
+ if (lov->lo_owner != current)
+ up_read(&lov->lo_type_guard);
+}
+
+#define LOV_2DISPATCH_MAYLOCK(obj, op, lock, ...) \
+({ \
+ struct lov_object *__obj = (obj); \
+ int __lock = !!(lock); \
+ typeof(lov_dispatch[0].op(__VA_ARGS__)) __result; \
+ \
+ if (__lock) \
+ lov_conf_freeze(__obj); \
+ __result = LOV_2DISPATCH_NOLOCK(obj, op, __VA_ARGS__); \
+ if (__lock) \
+ lov_conf_thaw(__obj); \
+ __result; \
+})
+
+/**
+ * Performs a locked double-dispatch based on the layout type of an object.
+ */
+#define LOV_2DISPATCH(obj, op, ...) \
+ LOV_2DISPATCH_MAYLOCK(obj, op, 1, __VA_ARGS__)
+
+#define LOV_2DISPATCH_VOID(obj, op, ...) \
+do { \
+ struct lov_object *__obj = (obj); \
+ enum lov_layout_type __llt; \
+ \
+ lov_conf_freeze(__obj); \
+ __llt = __obj->lo_type; \
+ LASSERT(0 <= __llt && __llt < ARRAY_SIZE(lov_dispatch)); \
+ lov_dispatch[__llt].op(__VA_ARGS__); \
+ lov_conf_thaw(__obj); \
+} while (0)
+
+static void lov_conf_lock(struct lov_object *lov)
+{
+ LASSERT(lov->lo_owner != current);
+ down_write(&lov->lo_type_guard);
+ LASSERT(lov->lo_owner == NULL);
+ lov->lo_owner = current;
+}
+
+static void lov_conf_unlock(struct lov_object *lov)
+{
+ lov->lo_owner = NULL;
+ up_write(&lov->lo_type_guard);
+}
+
+static int lov_layout_wait(const struct lu_env *env, struct lov_object *lov)
+{
+ struct l_wait_info lwi = { 0 };
+
+ while (atomic_read(&lov->lo_active_ios) > 0) {
+ CDEBUG(D_INODE, "file:"DFID" wait for active IO, now: %d.\n",
+ PFID(lu_object_fid(lov2lu(lov))),
+ atomic_read(&lov->lo_active_ios));
+
+ l_wait_event(lov->lo_waitq,
+ atomic_read(&lov->lo_active_ios) == 0, &lwi);
+ }
+ return 0;
+}
+
+static int lov_layout_change(const struct lu_env *unused,
+ struct lov_object *lov,
+ const struct cl_object_conf *conf)
+{
+ int result;
+ enum lov_layout_type llt = LLT_EMPTY;
+ union lov_layout_state *state = &lov->u;
+ const struct lov_layout_operations *old_ops;
+ const struct lov_layout_operations *new_ops;
+
+ struct cl_object_header *hdr = cl_object_header(&lov->lo_cl);
+ void *cookie;
+ struct lu_env *env;
+ int refcheck;
+
+ LASSERT(0 <= lov->lo_type && lov->lo_type < ARRAY_SIZE(lov_dispatch));
+
+ if (conf->u.coc_md != NULL)
+ llt = lov_type(conf->u.coc_md->lsm);
+ LASSERT(0 <= llt && llt < ARRAY_SIZE(lov_dispatch));
+
+ cookie = cl_env_reenter();
+ env = cl_env_get(&refcheck);
+ if (IS_ERR(env)) {
+ cl_env_reexit(cookie);
+ return PTR_ERR(env);
+ }
+
+ CDEBUG(D_INODE, DFID" from %s to %s\n",
+ PFID(lu_object_fid(lov2lu(lov))),
+ llt2str(lov->lo_type), llt2str(llt));
+
+ old_ops = &lov_dispatch[lov->lo_type];
+ new_ops = &lov_dispatch[llt];
+
+ result = old_ops->llo_delete(env, lov, &lov->u);
+ if (result == 0) {
+ old_ops->llo_fini(env, lov, &lov->u);
+
+ LASSERT(atomic_read(&lov->lo_active_ios) == 0);
+ LASSERT(hdr->coh_tree.rnode == NULL);
+ LASSERT(hdr->coh_pages == 0);
+
+ lov->lo_type = LLT_EMPTY;
+ result = new_ops->llo_init(env,
+ lu2lov_dev(lov->lo_cl.co_lu.lo_dev),
+ lov, conf, state);
+ if (result == 0) {
+ new_ops->llo_install(env, lov, state);
+ lov->lo_type = llt;
+ } else {
+ new_ops->llo_delete(env, lov, state);
+ new_ops->llo_fini(env, lov, state);
+ /* this file becomes an EMPTY file. */
+ }
+ }
+
+ cl_env_put(env, &refcheck);
+ cl_env_reexit(cookie);
+ return result;
+}
+
+/*****************************************************************************
+ *
+ * Lov object operations.
+ *
+ */
+int lov_object_init(const struct lu_env *env, struct lu_object *obj,
+ const struct lu_object_conf *conf)
+{
+ struct lov_device *dev = lu2lov_dev(obj->lo_dev);
+ struct lov_object *lov = lu2lov(obj);
+ const struct cl_object_conf *cconf = lu2cl_conf(conf);
+ union lov_layout_state *set = &lov->u;
+ const struct lov_layout_operations *ops;
+ int result;
+
+ init_rwsem(&lov->lo_type_guard);
+ atomic_set(&lov->lo_active_ios, 0);
+ init_waitqueue_head(&lov->lo_waitq);
+
+ cl_object_page_init(lu2cl(obj), sizeof(struct lov_page));
+
+ /* no locking is necessary, as object is being created */
+ lov->lo_type = lov_type(cconf->u.coc_md->lsm);
+ ops = &lov_dispatch[lov->lo_type];
+ result = ops->llo_init(env, dev, lov, cconf, set);
+ if (result == 0)
+ ops->llo_install(env, lov, set);
+ return result;
+}
+
+static int lov_conf_set(const struct lu_env *env, struct cl_object *obj,
+ const struct cl_object_conf *conf)
+{
+ struct lov_stripe_md *lsm = NULL;
+ struct lov_object *lov = cl2lov(obj);
+ int result = 0;
+
+ lov_conf_lock(lov);
+ if (conf->coc_opc == OBJECT_CONF_INVALIDATE) {
+ lov->lo_layout_invalid = true;
+ result = 0;
+ goto out;
+ }
+
+ if (conf->coc_opc == OBJECT_CONF_WAIT) {
+ if (lov->lo_layout_invalid &&
+ atomic_read(&lov->lo_active_ios) > 0) {
+ lov_conf_unlock(lov);
+ result = lov_layout_wait(env, lov);
+ lov_conf_lock(lov);
+ }
+ goto out;
+ }
+
+ LASSERT(conf->coc_opc == OBJECT_CONF_SET);
+
+ if (conf->u.coc_md != NULL)
+ lsm = conf->u.coc_md->lsm;
+ if ((lsm == NULL && lov->lo_lsm == NULL) ||
+ ((lsm != NULL && lov->lo_lsm != NULL) &&
+ (lov->lo_lsm->lsm_layout_gen == lsm->lsm_layout_gen) &&
+ (lov->lo_lsm->lsm_pattern == lsm->lsm_pattern))) {
+ /* same version of layout */
+ lov->lo_layout_invalid = false;
+ result = 0;
+ goto out;
+ }
+
+ /* will change layout - check if there still exists active IO. */
+ if (atomic_read(&lov->lo_active_ios) > 0) {
+ lov->lo_layout_invalid = true;
+ result = -EBUSY;
+ goto out;
+ }
+
+ lov->lo_layout_invalid = lov_layout_change(env, lov, conf);
+
+out:
+ lov_conf_unlock(lov);
+ CDEBUG(D_INODE, DFID" lo_layout_invalid=%d\n",
+ PFID(lu_object_fid(lov2lu(lov))), lov->lo_layout_invalid);
+ return result;
+}
+
+static void lov_object_delete(const struct lu_env *env, struct lu_object *obj)
+{
+ struct lov_object *lov = lu2lov(obj);
+
+ LOV_2DISPATCH_VOID(lov, llo_delete, env, lov, &lov->u);
+}
+
+static void lov_object_free(const struct lu_env *env, struct lu_object *obj)
+{
+ struct lov_object *lov = lu2lov(obj);
+
+ LOV_2DISPATCH_VOID(lov, llo_fini, env, lov, &lov->u);
+ lu_object_fini(obj);
+ OBD_SLAB_FREE_PTR(lov, lov_object_kmem);
+}
+
+static int lov_object_print(const struct lu_env *env, void *cookie,
+ lu_printer_t p, const struct lu_object *o)
+{
+ return LOV_2DISPATCH_NOLOCK(lu2lov(o), llo_print, env, cookie, p, o);
+}
+
+int lov_page_init(const struct lu_env *env, struct cl_object *obj,
+ struct cl_page *page, struct page *vmpage)
+{
+ return LOV_2DISPATCH_NOLOCK(cl2lov(obj),
+ llo_page_init, env, obj, page, vmpage);
+}
+
+/**
+ * Implements cl_object_operations::clo_io_init() method for lov
+ * layer. Dispatches to the appropriate layout io initialization method.
+ */
+int lov_io_init(const struct lu_env *env, struct cl_object *obj,
+ struct cl_io *io)
+{
+ CL_IO_SLICE_CLEAN(lov_env_io(env), lis_cl);
+ return LOV_2DISPATCH_MAYLOCK(cl2lov(obj), llo_io_init,
+ !io->ci_ignore_layout, env, obj, io);
+}
+
+/**
+ * An implementation of cl_object_operations::clo_attr_get() method for lov
+ * layer. For raid0 layout this collects and merges attributes of all
+ * sub-objects.
+ */
+static int lov_attr_get(const struct lu_env *env, struct cl_object *obj,
+ struct cl_attr *attr)
+{
+ /* do not take lock, as this function is called under a
+ * spin-lock. Layout is protected from changing by ongoing IO. */
+ return LOV_2DISPATCH_NOLOCK(cl2lov(obj), llo_getattr, env, obj, attr);
+}
+
+static int lov_attr_set(const struct lu_env *env, struct cl_object *obj,
+ const struct cl_attr *attr, unsigned valid)
+{
+ /*
+ * No dispatch is required here, as no layout implements this.
+ */
+ return 0;
+}
+
+int lov_lock_init(const struct lu_env *env, struct cl_object *obj,
+ struct cl_lock *lock, const struct cl_io *io)
+{
+ /* No need to lock because we've taken one refcount of layout. */
+ return LOV_2DISPATCH_NOLOCK(cl2lov(obj), llo_lock_init, env, obj, lock,
+ io);
+}
+
+static const struct cl_object_operations lov_ops = {
+ .coo_page_init = lov_page_init,
+ .coo_lock_init = lov_lock_init,
+ .coo_io_init = lov_io_init,
+ .coo_attr_get = lov_attr_get,
+ .coo_attr_set = lov_attr_set,
+ .coo_conf_set = lov_conf_set
+};
+
+static const struct lu_object_operations lov_lu_obj_ops = {
+ .loo_object_init = lov_object_init,
+ .loo_object_delete = lov_object_delete,
+ .loo_object_release = NULL,
+ .loo_object_free = lov_object_free,
+ .loo_object_print = lov_object_print,
+ .loo_object_invariant = NULL
+};
+
+struct lu_object *lov_object_alloc(const struct lu_env *env,
+ const struct lu_object_header *unused,
+ struct lu_device *dev)
+{
+ struct lov_object *lov;
+ struct lu_object *obj;
+
+ OBD_SLAB_ALLOC_PTR_GFP(lov, lov_object_kmem, GFP_NOFS);
+ if (lov != NULL) {
+ obj = lov2lu(lov);
+ lu_object_init(obj, NULL, dev);
+ lov->lo_cl.co_ops = &lov_ops;
+ lov->lo_type = -1; /* invalid, to catch uninitialized type */
+ /*
+ * object io operation vector (cl_object::co_iop) is installed
+ * later in lov_object_init(), as different vectors are used
+ * for object with different layouts.
+ */
+ obj->lo_ops = &lov_lu_obj_ops;
+ } else
+ obj = NULL;
+ return obj;
+}
+
+struct lov_stripe_md *lov_lsm_addref(struct lov_object *lov)
+{
+ struct lov_stripe_md *lsm = NULL;
+
+ lov_conf_freeze(lov);
+ if (lov->lo_lsm != NULL) {
+ lsm = lsm_addref(lov->lo_lsm);
+ CDEBUG(D_INODE, "lsm %p addref %d/%d by %p.\n",
+ lsm, atomic_read(&lsm->lsm_refc),
+ lov->lo_layout_invalid, current);
+ }
+ lov_conf_thaw(lov);
+ return lsm;
+}
+
+void lov_lsm_decref(struct lov_object *lov, struct lov_stripe_md *lsm)
+{
+ if (lsm == NULL)
+ return;
+
+ CDEBUG(D_INODE, "lsm %p decref %d by %p.\n",
+ lsm, atomic_read(&lsm->lsm_refc), current);
+
+ lov_free_memmd(&lsm);
+}
+
+struct lov_stripe_md *lov_lsm_get(struct cl_object *clobj)
+{
+ struct lu_object *luobj;
+ struct lov_stripe_md *lsm = NULL;
+
+ if (clobj == NULL)
+ return NULL;
+
+ luobj = lu_object_locate(&cl_object_header(clobj)->coh_lu,
+ &lov_device_type);
+ if (luobj != NULL)
+ lsm = lov_lsm_addref(lu2lov(luobj));
+ return lsm;
+}
+EXPORT_SYMBOL(lov_lsm_get);
+
+void lov_lsm_put(struct cl_object *unused, struct lov_stripe_md *lsm)
+{
+ if (lsm != NULL)
+ lov_free_memmd(&lsm);
+}
+EXPORT_SYMBOL(lov_lsm_put);
+
+int lov_read_and_clear_async_rc(struct cl_object *clob)
+{
+ struct lu_object *luobj;
+ int rc = 0;
+
+ luobj = lu_object_locate(&cl_object_header(clob)->coh_lu,
+ &lov_device_type);
+ if (luobj != NULL) {
+ struct lov_object *lov = lu2lov(luobj);
+
+ lov_conf_freeze(lov);
+ switch (lov->lo_type) {
+ case LLT_RAID0: {
+ struct lov_stripe_md *lsm;
+ int i;
+
+ lsm = lov->lo_lsm;
+ LASSERT(lsm != NULL);
+ for (i = 0; i < lsm->lsm_stripe_count; i++) {
+ struct lov_oinfo *loi = lsm->lsm_oinfo[i];
+
+ if (lov_oinfo_is_dummy(loi))
+ continue;
+
+ if (loi->loi_ar.ar_rc && !rc)
+ rc = loi->loi_ar.ar_rc;
+ loi->loi_ar.ar_rc = 0;
+ }
+ }
+ case LLT_RELEASED:
+ case LLT_EMPTY:
+ break;
+ default:
+ LBUG();
+ }
+ lov_conf_thaw(lov);
+ }
+ return rc;
+}
+EXPORT_SYMBOL(lov_read_and_clear_async_rc);
+
+/** @} lov */
diff --git a/drivers/staging/lustre/lustre/lov/lov_offset.c b/drivers/staging/lustre/lustre/lov/lov_offset.c
new file mode 100644
index 000000000..9c8c77c05
--- /dev/null
+++ b/drivers/staging/lustre/lustre/lov/lov_offset.c
@@ -0,0 +1,264 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2011, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ */
+
+#define DEBUG_SUBSYSTEM S_LOV
+
+#include "../../include/linux/libcfs/libcfs.h"
+
+#include "../include/obd_class.h"
+
+#include "lov_internal.h"
+
+/* compute object size given "stripeno" and the ost size */
+u64 lov_stripe_size(struct lov_stripe_md *lsm, u64 ost_size,
+ int stripeno)
+{
+ unsigned long ssize = lsm->lsm_stripe_size;
+ unsigned long stripe_size;
+ u64 swidth;
+ u64 lov_size;
+ int magic = lsm->lsm_magic;
+
+ if (ost_size == 0)
+ return 0;
+
+ LASSERT(lsm_op_find(magic) != NULL);
+ lsm_op_find(magic)->lsm_stripe_by_index(lsm, &stripeno, NULL, &swidth);
+
+ /* lov_do_div64(a, b) returns a % b, and a = a / b */
+ stripe_size = lov_do_div64(ost_size, ssize);
+ if (stripe_size)
+ lov_size = ost_size * swidth + stripeno * ssize + stripe_size;
+ else
+ lov_size = (ost_size - 1) * swidth + (stripeno + 1) * ssize;
+
+ return lov_size;
+}
+
+/* we have an offset in file backed by an lov and want to find out where
+ * that offset lands in our given stripe of the file. for the easy
+ * case where the offset is within the stripe, we just have to scale the
+ * offset down to make it relative to the stripe instead of the lov.
+ *
+ * the harder case is what to do when the offset doesn't intersect the
+ * stripe. callers will want start offsets clamped ahead to the start
+ * of the nearest stripe in the file. end offsets similarly clamped to the
+ * nearest ending byte of a stripe in the file:
+ *
+ * all this function does is move offsets to the nearest region of the
+ * stripe, and it does its work "mod" the full length of all the stripes.
+ * consider a file with 3 stripes:
+ *
+ * S E
+ * ---------------------------------------------------------------------
+ * | 0 | 1 | 2 | 0 | 1 | 2 |
+ * ---------------------------------------------------------------------
+ *
+ * to find stripe 1's offsets for S and E, it divides by the full stripe
+ * width and does its math in the context of a single set of stripes:
+ *
+ * S E
+ * -----------------------------------
+ * | 0 | 1 | 2 |
+ * -----------------------------------
+ *
+ * it'll notice that E is outside stripe 1 and clamp it to the end of the
+ * stripe, then multiply it back out by lov_off to give the real offsets in
+ * the stripe:
+ *
+ * S E
+ * ---------------------------------------------------------------------
+ * | 1 | 1 | 1 | 1 | 1 | 1 |
+ * ---------------------------------------------------------------------
+ *
+ * it would have done similarly and pulled S forward to the start of a 1
+ * stripe if, say, S had landed in a 0 stripe.
+ *
+ * this rounding isn't always correct. consider an E lov offset that lands
+ * on a 0 stripe, the "mod stripe width" math will pull it forward to the
+ * start of a 1 stripe, when in fact it wanted to be rounded back to the end
+ * of a previous 1 stripe. this logic is handled by callers and this is why:
+ *
+ * this function returns < 0 when the offset was "before" the stripe and
+ * was moved forward to the start of the stripe in question; 0 when it
+ * falls in the stripe and no shifting was done; > 0 when the offset
+ * was outside the stripe and was pulled back to its final byte. */
+int lov_stripe_offset(struct lov_stripe_md *lsm, u64 lov_off,
+ int stripeno, u64 *obdoff)
+{
+ unsigned long ssize = lsm->lsm_stripe_size;
+ u64 stripe_off, this_stripe, swidth;
+ int magic = lsm->lsm_magic;
+ int ret = 0;
+
+ if (lov_off == OBD_OBJECT_EOF) {
+ *obdoff = OBD_OBJECT_EOF;
+ return 0;
+ }
+
+ LASSERT(lsm_op_find(magic) != NULL);
+
+ lsm_op_find(magic)->lsm_stripe_by_index(lsm, &stripeno, &lov_off,
+ &swidth);
+
+ /* lov_do_div64(a, b) returns a % b, and a = a / b */
+ stripe_off = lov_do_div64(lov_off, swidth);
+
+ this_stripe = (u64)stripeno * ssize;
+ if (stripe_off < this_stripe) {
+ stripe_off = 0;
+ ret = -1;
+ } else {
+ stripe_off -= this_stripe;
+
+ if (stripe_off >= ssize) {
+ stripe_off = ssize;
+ ret = 1;
+ }
+ }
+
+ *obdoff = lov_off * ssize + stripe_off;
+ return ret;
+}
+
+/* Given a whole-file size and a stripe number, give the file size which
+ * corresponds to the individual object of that stripe.
+ *
+ * This behaves basically in the same was as lov_stripe_offset, except that
+ * file sizes falling before the beginning of a stripe are clamped to the end
+ * of the previous stripe, not the beginning of the next:
+ *
+ * S
+ * ---------------------------------------------------------------------
+ * | 0 | 1 | 2 | 0 | 1 | 2 |
+ * ---------------------------------------------------------------------
+ *
+ * if clamped to stripe 2 becomes:
+ *
+ * S
+ * ---------------------------------------------------------------------
+ * | 0 | 1 | 2 | 0 | 1 | 2 |
+ * ---------------------------------------------------------------------
+ */
+u64 lov_size_to_stripe(struct lov_stripe_md *lsm, u64 file_size,
+ int stripeno)
+{
+ unsigned long ssize = lsm->lsm_stripe_size;
+ u64 stripe_off, this_stripe, swidth;
+ int magic = lsm->lsm_magic;
+
+ if (file_size == OBD_OBJECT_EOF)
+ return OBD_OBJECT_EOF;
+
+ LASSERT(lsm_op_find(magic) != NULL);
+ lsm_op_find(magic)->lsm_stripe_by_index(lsm, &stripeno, &file_size,
+ &swidth);
+
+ /* lov_do_div64(a, b) returns a % b, and a = a / b */
+ stripe_off = lov_do_div64(file_size, swidth);
+
+ this_stripe = (u64)stripeno * ssize;
+ if (stripe_off < this_stripe) {
+ /* Move to end of previous stripe, or zero */
+ if (file_size > 0) {
+ file_size--;
+ stripe_off = ssize;
+ } else {
+ stripe_off = 0;
+ }
+ } else {
+ stripe_off -= this_stripe;
+
+ if (stripe_off >= ssize) {
+ /* Clamp to end of this stripe */
+ stripe_off = ssize;
+ }
+ }
+
+ return (file_size * ssize + stripe_off);
+}
+
+/* given an extent in an lov and a stripe, calculate the extent of the stripe
+ * that is contained within the lov extent. this returns true if the given
+ * stripe does intersect with the lov extent. */
+int lov_stripe_intersects(struct lov_stripe_md *lsm, int stripeno,
+ u64 start, u64 end, u64 *obd_start, u64 *obd_end)
+{
+ int start_side, end_side;
+
+ start_side = lov_stripe_offset(lsm, start, stripeno, obd_start);
+ end_side = lov_stripe_offset(lsm, end, stripeno, obd_end);
+
+ CDEBUG(D_INODE, "[%llu->%llu] -> [(%d) %llu->%llu (%d)]\n",
+ start, end, start_side, *obd_start, *obd_end, end_side);
+
+ /* this stripe doesn't intersect the file extent when neither
+ * start or the end intersected the stripe and obd_start and
+ * obd_end got rounded up to the save value. */
+ if (start_side != 0 && end_side != 0 && *obd_start == *obd_end)
+ return 0;
+
+ /* as mentioned in the lov_stripe_offset commentary, end
+ * might have been shifted in the wrong direction. This
+ * happens when an end offset is before the stripe when viewed
+ * through the "mod stripe size" math. we detect it being shifted
+ * in the wrong direction and touch it up.
+ * interestingly, this can't underflow since end must be > start
+ * if we passed through the previous check.
+ * (should we assert for that somewhere?) */
+ if (end_side != 0)
+ (*obd_end)--;
+
+ return 1;
+}
+
+/* compute which stripe number "lov_off" will be written into */
+int lov_stripe_number(struct lov_stripe_md *lsm, u64 lov_off)
+{
+ unsigned long ssize = lsm->lsm_stripe_size;
+ u64 stripe_off, swidth;
+ int magic = lsm->lsm_magic;
+
+ LASSERT(lsm_op_find(magic) != NULL);
+ lsm_op_find(magic)->lsm_stripe_by_offset(lsm, NULL, &lov_off, &swidth);
+
+ stripe_off = lov_do_div64(lov_off, swidth);
+
+ /* Puts stripe_off/ssize result into stripe_off */
+ lov_do_div64(stripe_off, ssize);
+
+ return stripe_off;
+}
diff --git a/drivers/staging/lustre/lustre/lov/lov_pack.c b/drivers/staging/lustre/lustre/lov/lov_pack.c
new file mode 100644
index 000000000..5356d5324
--- /dev/null
+++ b/drivers/staging/lustre/lustre/lov/lov_pack.c
@@ -0,0 +1,511 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2002, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2011, 2012, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ *
+ * lustre/lov/lov_pack.c
+ *
+ * (Un)packing of OST/MDS requests
+ *
+ * Author: Andreas Dilger <adilger@clusterfs.com>
+ */
+
+#define DEBUG_SUBSYSTEM S_LOV
+
+#include "../include/lustre_net.h"
+#include "../include/obd.h"
+#include "../include/obd_class.h"
+#include "../include/obd_support.h"
+#include "../include/lustre/lustre_user.h"
+
+#include "lov_internal.h"
+
+void lov_dump_lmm_common(int level, void *lmmp)
+{
+ struct lov_mds_md *lmm = lmmp;
+ struct ost_id oi;
+
+ lmm_oi_le_to_cpu(&oi, &lmm->lmm_oi);
+ CDEBUG(level, "objid "DOSTID", magic 0x%08x, pattern %#x\n",
+ POSTID(&oi), le32_to_cpu(lmm->lmm_magic),
+ le32_to_cpu(lmm->lmm_pattern));
+ CDEBUG(level, "stripe_size %u, stripe_count %u, layout_gen %u\n",
+ le32_to_cpu(lmm->lmm_stripe_size),
+ le16_to_cpu(lmm->lmm_stripe_count),
+ le16_to_cpu(lmm->lmm_layout_gen));
+}
+
+static void lov_dump_lmm_objects(int level, struct lov_ost_data *lod,
+ int stripe_count)
+{
+ int i;
+
+ if (stripe_count > LOV_V1_INSANE_STRIPE_COUNT) {
+ CDEBUG(level, "bad stripe_count %u > max_stripe_count %u\n",
+ stripe_count, LOV_V1_INSANE_STRIPE_COUNT);
+ return;
+ }
+
+ for (i = 0; i < stripe_count; ++i, ++lod) {
+ struct ost_id oi;
+
+ ostid_le_to_cpu(&lod->l_ost_oi, &oi);
+ CDEBUG(level, "stripe %u idx %u subobj "DOSTID"\n", i,
+ le32_to_cpu(lod->l_ost_idx), POSTID(&oi));
+ }
+}
+
+void lov_dump_lmm_v1(int level, struct lov_mds_md_v1 *lmm)
+{
+ lov_dump_lmm_common(level, lmm);
+ lov_dump_lmm_objects(level, lmm->lmm_objects,
+ le16_to_cpu(lmm->lmm_stripe_count));
+}
+
+void lov_dump_lmm_v3(int level, struct lov_mds_md_v3 *lmm)
+{
+ lov_dump_lmm_common(level, lmm);
+ CDEBUG(level, "pool_name "LOV_POOLNAMEF"\n", lmm->lmm_pool_name);
+ lov_dump_lmm_objects(level, lmm->lmm_objects,
+ le16_to_cpu(lmm->lmm_stripe_count));
+}
+
+void lov_dump_lmm(int level, void *lmm)
+{
+ int magic;
+
+ magic = le32_to_cpu(((struct lov_mds_md *)lmm)->lmm_magic);
+ switch (magic) {
+ case LOV_MAGIC_V1:
+ lov_dump_lmm_v1(level, (struct lov_mds_md_v1 *)lmm);
+ break;
+ case LOV_MAGIC_V3:
+ lov_dump_lmm_v3(level, (struct lov_mds_md_v3 *)lmm);
+ break;
+ default:
+ CDEBUG(level, "unrecognized lmm_magic %x, assuming %x\n",
+ magic, LOV_MAGIC_V1);
+ lov_dump_lmm_common(level, lmm);
+ break;
+ }
+}
+
+/* Pack LOV object metadata for disk storage. It is packed in LE byte
+ * order and is opaque to the networking layer.
+ *
+ * XXX In the future, this will be enhanced to get the EA size from the
+ * underlying OSC device(s) to get their EA sizes so we can stack
+ * LOVs properly. For now lov_mds_md_size() just assumes one u64
+ * per stripe.
+ */
+int lov_packmd(struct obd_export *exp, struct lov_mds_md **lmmp,
+ struct lov_stripe_md *lsm)
+{
+ struct obd_device *obd = class_exp2obd(exp);
+ struct lov_obd *lov = &obd->u.lov;
+ struct lov_mds_md_v1 *lmmv1;
+ struct lov_mds_md_v3 *lmmv3;
+ __u16 stripe_count;
+ struct lov_ost_data_v1 *lmm_objects;
+ int lmm_size, lmm_magic;
+ int i;
+ int cplen = 0;
+
+ if (lsm) {
+ lmm_magic = lsm->lsm_magic;
+ } else {
+ if (lmmp && *lmmp)
+ lmm_magic = le32_to_cpu((*lmmp)->lmm_magic);
+ else
+ /* lsm == NULL and lmmp == NULL */
+ lmm_magic = LOV_MAGIC;
+ }
+
+ if ((lmm_magic != LOV_MAGIC_V1) &&
+ (lmm_magic != LOV_MAGIC_V3)) {
+ CERROR("bad mem LOV MAGIC: 0x%08X != 0x%08X nor 0x%08X\n",
+ lmm_magic, LOV_MAGIC_V1, LOV_MAGIC_V3);
+ return -EINVAL;
+
+ }
+
+ if (lsm) {
+ /* If we are just sizing the EA, limit the stripe count
+ * to the actual number of OSTs in this filesystem. */
+ if (!lmmp) {
+ stripe_count = lov_get_stripecnt(lov, lmm_magic,
+ lsm->lsm_stripe_count);
+ lsm->lsm_stripe_count = stripe_count;
+ } else if (!lsm_is_released(lsm)) {
+ stripe_count = lsm->lsm_stripe_count;
+ } else {
+ stripe_count = 0;
+ }
+ } else {
+ /* No need to allocate more than maximum supported stripes.
+ * Anyway, this is pretty inaccurate since ld_tgt_count now
+ * represents max index and we should rely on the actual number
+ * of OSTs instead */
+ stripe_count = lov_mds_md_max_stripe_count(
+ lov->lov_ocd.ocd_max_easize, lmm_magic);
+
+ if (stripe_count > lov->desc.ld_tgt_count)
+ stripe_count = lov->desc.ld_tgt_count;
+ }
+
+ /* XXX LOV STACKING call into osc for sizes */
+ lmm_size = lov_mds_md_size(stripe_count, lmm_magic);
+
+ if (!lmmp)
+ return lmm_size;
+
+ if (*lmmp && !lsm) {
+ stripe_count = le16_to_cpu((*lmmp)->lmm_stripe_count);
+ lmm_size = lov_mds_md_size(stripe_count, lmm_magic);
+ OBD_FREE_LARGE(*lmmp, lmm_size);
+ *lmmp = NULL;
+ return 0;
+ }
+
+ if (!*lmmp) {
+ OBD_ALLOC_LARGE(*lmmp, lmm_size);
+ if (!*lmmp)
+ return -ENOMEM;
+ }
+
+ CDEBUG(D_INFO, "lov_packmd: LOV_MAGIC 0x%08X, lmm_size = %d \n",
+ lmm_magic, lmm_size);
+
+ lmmv1 = *lmmp;
+ lmmv3 = (struct lov_mds_md_v3 *)*lmmp;
+ if (lmm_magic == LOV_MAGIC_V3)
+ lmmv3->lmm_magic = cpu_to_le32(LOV_MAGIC_V3);
+ else
+ lmmv1->lmm_magic = cpu_to_le32(LOV_MAGIC_V1);
+
+ if (!lsm)
+ return lmm_size;
+
+ /* lmmv1 and lmmv3 point to the same struct and have the
+ * same first fields
+ */
+ lmm_oi_cpu_to_le(&lmmv1->lmm_oi, &lsm->lsm_oi);
+ lmmv1->lmm_stripe_size = cpu_to_le32(lsm->lsm_stripe_size);
+ lmmv1->lmm_stripe_count = cpu_to_le16(stripe_count);
+ lmmv1->lmm_pattern = cpu_to_le32(lsm->lsm_pattern);
+ lmmv1->lmm_layout_gen = cpu_to_le16(lsm->lsm_layout_gen);
+ if (lsm->lsm_magic == LOV_MAGIC_V3) {
+ cplen = strlcpy(lmmv3->lmm_pool_name, lsm->lsm_pool_name,
+ sizeof(lmmv3->lmm_pool_name));
+ if (cplen >= sizeof(lmmv3->lmm_pool_name))
+ return -E2BIG;
+ lmm_objects = lmmv3->lmm_objects;
+ } else {
+ lmm_objects = lmmv1->lmm_objects;
+ }
+
+ for (i = 0; i < stripe_count; i++) {
+ struct lov_oinfo *loi = lsm->lsm_oinfo[i];
+ /* XXX LOV STACKING call down to osc_packmd() to do packing */
+ LASSERTF(ostid_id(&loi->loi_oi) != 0, "lmm_oi "DOSTID
+ " stripe %u/%u idx %u\n", POSTID(&lmmv1->lmm_oi),
+ i, stripe_count, loi->loi_ost_idx);
+ ostid_cpu_to_le(&loi->loi_oi, &lmm_objects[i].l_ost_oi);
+ lmm_objects[i].l_ost_gen = cpu_to_le32(loi->loi_ost_gen);
+ lmm_objects[i].l_ost_idx = cpu_to_le32(loi->loi_ost_idx);
+ }
+
+ return lmm_size;
+}
+
+/* Find the max stripecount we should use */
+__u16 lov_get_stripecnt(struct lov_obd *lov, __u32 magic, __u16 stripe_count)
+{
+ __u32 max_stripes = LOV_MAX_STRIPE_COUNT_OLD;
+
+ if (!stripe_count)
+ stripe_count = lov->desc.ld_default_stripe_count;
+ if (stripe_count > lov->desc.ld_active_tgt_count)
+ stripe_count = lov->desc.ld_active_tgt_count;
+ if (!stripe_count)
+ stripe_count = 1;
+
+ /* stripe count is based on whether ldiskfs can handle
+ * larger EA sizes */
+ if (lov->lov_ocd.ocd_connect_flags & OBD_CONNECT_MAX_EASIZE &&
+ lov->lov_ocd.ocd_max_easize)
+ max_stripes = lov_mds_md_max_stripe_count(
+ lov->lov_ocd.ocd_max_easize, magic);
+
+ if (stripe_count > max_stripes)
+ stripe_count = max_stripes;
+
+ return stripe_count;
+}
+
+
+static int lov_verify_lmm(void *lmm, int lmm_bytes, __u16 *stripe_count)
+{
+ int rc;
+
+ if (lsm_op_find(le32_to_cpu(*(__u32 *)lmm)) == NULL) {
+ char *buffer;
+ int sz;
+
+ CERROR("bad disk LOV MAGIC: 0x%08X; dumping LMM (size=%d):\n",
+ le32_to_cpu(*(__u32 *)lmm), lmm_bytes);
+ sz = lmm_bytes * 2 + 1;
+ OBD_ALLOC_LARGE(buffer, sz);
+ if (buffer != NULL) {
+ int i;
+
+ for (i = 0; i < lmm_bytes; i++)
+ sprintf(buffer+2*i, "%.2X", ((char *)lmm)[i]);
+ buffer[sz - 1] = '\0';
+ CERROR("%s\n", buffer);
+ OBD_FREE_LARGE(buffer, sz);
+ }
+ return -EINVAL;
+ }
+ rc = lsm_op_find(le32_to_cpu(*(__u32 *)lmm))->lsm_lmm_verify(lmm,
+ lmm_bytes, stripe_count);
+ return rc;
+}
+
+int lov_alloc_memmd(struct lov_stripe_md **lsmp, __u16 stripe_count,
+ int pattern, int magic)
+{
+ int i, lsm_size;
+
+ CDEBUG(D_INFO, "alloc lsm, stripe_count %d\n", stripe_count);
+
+ *lsmp = lsm_alloc_plain(stripe_count, &lsm_size);
+ if (!*lsmp) {
+ CERROR("can't allocate lsmp stripe_count %d\n", stripe_count);
+ return -ENOMEM;
+ }
+
+ atomic_set(&(*lsmp)->lsm_refc, 1);
+ spin_lock_init(&(*lsmp)->lsm_lock);
+ (*lsmp)->lsm_magic = magic;
+ (*lsmp)->lsm_stripe_count = stripe_count;
+ (*lsmp)->lsm_maxbytes = LUSTRE_STRIPE_MAXBYTES * stripe_count;
+ (*lsmp)->lsm_pattern = pattern;
+ (*lsmp)->lsm_pool_name[0] = '\0';
+ (*lsmp)->lsm_layout_gen = 0;
+ if (stripe_count > 0)
+ (*lsmp)->lsm_oinfo[0]->loi_ost_idx = ~0;
+
+ for (i = 0; i < stripe_count; i++)
+ loi_init((*lsmp)->lsm_oinfo[i]);
+
+ return lsm_size;
+}
+
+int lov_free_memmd(struct lov_stripe_md **lsmp)
+{
+ struct lov_stripe_md *lsm = *lsmp;
+ int refc;
+
+ *lsmp = NULL;
+ LASSERT(atomic_read(&lsm->lsm_refc) > 0);
+ refc = atomic_dec_return(&lsm->lsm_refc);
+ if (refc == 0) {
+ LASSERT(lsm_op_find(lsm->lsm_magic) != NULL);
+ lsm_op_find(lsm->lsm_magic)->lsm_free(lsm);
+ }
+ return refc;
+}
+
+
+/* Unpack LOV object metadata from disk storage. It is packed in LE byte
+ * order and is opaque to the networking layer.
+ */
+int lov_unpackmd(struct obd_export *exp, struct lov_stripe_md **lsmp,
+ struct lov_mds_md *lmm, int lmm_bytes)
+{
+ struct obd_device *obd = class_exp2obd(exp);
+ struct lov_obd *lov = &obd->u.lov;
+ int rc = 0, lsm_size;
+ __u16 stripe_count;
+ __u32 magic;
+ __u32 pattern;
+
+ /* If passed an MDS struct use values from there, otherwise defaults */
+ if (lmm) {
+ rc = lov_verify_lmm(lmm, lmm_bytes, &stripe_count);
+ if (rc)
+ return rc;
+ magic = le32_to_cpu(lmm->lmm_magic);
+ } else {
+ magic = LOV_MAGIC;
+ stripe_count = lov_get_stripecnt(lov, magic, 0);
+ }
+
+ /* If we aren't passed an lsmp struct, we just want the size */
+ if (!lsmp) {
+ /* XXX LOV STACKING call into osc for sizes */
+ LBUG();
+ return lov_stripe_md_size(stripe_count);
+ }
+ /* If we are passed an allocated struct but nothing to unpack, free */
+ if (*lsmp && !lmm) {
+ lov_free_memmd(lsmp);
+ return 0;
+ }
+
+ pattern = le32_to_cpu(lmm->lmm_pattern);
+ lsm_size = lov_alloc_memmd(lsmp, stripe_count, pattern, magic);
+ if (lsm_size < 0)
+ return lsm_size;
+
+ /* If we are passed a pointer but nothing to unpack, we only alloc */
+ if (!lmm)
+ return lsm_size;
+
+ LASSERT(lsm_op_find(magic) != NULL);
+ rc = lsm_op_find(magic)->lsm_unpackmd(lov, *lsmp, lmm);
+ if (rc) {
+ lov_free_memmd(lsmp);
+ return rc;
+ }
+
+ return lsm_size;
+}
+
+/* Retrieve object striping information.
+ *
+ * @lump is a pointer to an in-core struct with lmm_ost_count indicating
+ * the maximum number of OST indices which will fit in the user buffer.
+ * lmm_magic must be LOV_USER_MAGIC.
+ */
+int lov_getstripe(struct obd_export *exp, struct lov_stripe_md *lsm,
+ struct lov_user_md *lump)
+{
+ /*
+ * XXX huge struct allocated on stack.
+ */
+ /* we use lov_user_md_v3 because it is larger than lov_user_md_v1 */
+ struct lov_user_md_v3 lum;
+ struct lov_mds_md *lmmk = NULL;
+ int rc, lmm_size;
+ int lum_size;
+ mm_segment_t seg;
+
+ if (!lsm)
+ return -ENODATA;
+
+ /*
+ * "Switch to kernel segment" to allow copying from kernel space by
+ * copy_{to,from}_user().
+ */
+ seg = get_fs();
+ set_fs(KERNEL_DS);
+
+ /* we only need the header part from user space to get lmm_magic and
+ * lmm_stripe_count, (the header part is common to v1 and v3) */
+ lum_size = sizeof(struct lov_user_md_v1);
+ if (copy_from_user(&lum, lump, lum_size)) {
+ rc = -EFAULT;
+ goto out_set;
+ } else if ((lum.lmm_magic != LOV_USER_MAGIC) &&
+ (lum.lmm_magic != LOV_USER_MAGIC_V3)) {
+ rc = -EINVAL;
+ goto out_set;
+ }
+
+ if (lum.lmm_stripe_count &&
+ (lum.lmm_stripe_count < lsm->lsm_stripe_count)) {
+ /* Return right size of stripe to user */
+ lum.lmm_stripe_count = lsm->lsm_stripe_count;
+ rc = copy_to_user(lump, &lum, lum_size);
+ rc = -EOVERFLOW;
+ goto out_set;
+ }
+ rc = lov_packmd(exp, &lmmk, lsm);
+ if (rc < 0)
+ goto out_set;
+ lmm_size = rc;
+ rc = 0;
+
+ /* FIXME: Bug 1185 - copy fields properly when structs change */
+ /* struct lov_user_md_v3 and struct lov_mds_md_v3 must be the same */
+ CLASSERT(sizeof(lum) == sizeof(struct lov_mds_md_v3));
+ CLASSERT(sizeof(lum.lmm_objects[0]) == sizeof(lmmk->lmm_objects[0]));
+
+ if ((cpu_to_le32(LOV_MAGIC) != LOV_MAGIC) &&
+ ((lmmk->lmm_magic == cpu_to_le32(LOV_MAGIC_V1)) ||
+ (lmmk->lmm_magic == cpu_to_le32(LOV_MAGIC_V3)))) {
+ lustre_swab_lov_mds_md(lmmk);
+ lustre_swab_lov_user_md_objects(
+ (struct lov_user_ost_data *)lmmk->lmm_objects,
+ lmmk->lmm_stripe_count);
+ }
+ if (lum.lmm_magic == LOV_USER_MAGIC) {
+ /* User request for v1, we need skip lmm_pool_name */
+ if (lmmk->lmm_magic == LOV_MAGIC_V3) {
+ memmove((char *)(&lmmk->lmm_stripe_count) +
+ sizeof(lmmk->lmm_stripe_count),
+ ((struct lov_mds_md_v3 *)lmmk)->lmm_objects,
+ lmmk->lmm_stripe_count *
+ sizeof(struct lov_ost_data_v1));
+ lmm_size -= LOV_MAXPOOLNAME;
+ }
+ } else {
+ /* if v3 we just have to update the lum_size */
+ lum_size = sizeof(struct lov_user_md_v3);
+ }
+
+ /* User wasn't expecting this many OST entries */
+ if (lum.lmm_stripe_count == 0)
+ lmm_size = lum_size;
+ else if (lum.lmm_stripe_count < lmmk->lmm_stripe_count) {
+ rc = -EOVERFLOW;
+ goto out_set;
+ }
+ /*
+ * Have a difference between lov_mds_md & lov_user_md.
+ * So we have to re-order the data before copy to user.
+ */
+ lum.lmm_stripe_count = lmmk->lmm_stripe_count;
+ lum.lmm_layout_gen = lmmk->lmm_layout_gen;
+ ((struct lov_user_md *)lmmk)->lmm_layout_gen = lum.lmm_layout_gen;
+ ((struct lov_user_md *)lmmk)->lmm_stripe_count = lum.lmm_stripe_count;
+ if (copy_to_user(lump, lmmk, lmm_size))
+ rc = -EFAULT;
+
+ obd_free_diskmd(exp, &lmmk);
+out_set:
+ set_fs(seg);
+ return rc;
+}
diff --git a/drivers/staging/lustre/lustre/lov/lov_page.c b/drivers/staging/lustre/lustre/lov/lov_page.c
new file mode 100644
index 000000000..c4596e8e5
--- /dev/null
+++ b/drivers/staging/lustre/lustre/lov/lov_page.c
@@ -0,0 +1,232 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2011, 2012, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ *
+ * Implementation of cl_page for LOV layer.
+ *
+ * Author: Nikita Danilov <nikita.danilov@sun.com>
+ */
+
+#define DEBUG_SUBSYSTEM S_LOV
+
+#include "lov_cl_internal.h"
+
+/** \addtogroup lov
+ * @{
+ */
+
+/*****************************************************************************
+ *
+ * Lov page operations.
+ *
+ */
+
+static int lov_page_invariant(const struct cl_page_slice *slice)
+{
+ const struct cl_page *page = slice->cpl_page;
+ const struct cl_page *sub = lov_sub_page(slice);
+
+ return ergo(sub != NULL,
+ page->cp_child == sub &&
+ sub->cp_parent == page &&
+ page->cp_state == sub->cp_state);
+}
+
+static void lov_page_fini(const struct lu_env *env,
+ struct cl_page_slice *slice)
+{
+ struct cl_page *sub = lov_sub_page(slice);
+
+ LINVRNT(lov_page_invariant(slice));
+
+ if (sub != NULL) {
+ LASSERT(sub->cp_state == CPS_FREEING);
+ lu_ref_del(&sub->cp_reference, "lov", sub->cp_parent);
+ sub->cp_parent = NULL;
+ slice->cpl_page->cp_child = NULL;
+ cl_page_put(env, sub);
+ }
+}
+
+static int lov_page_own(const struct lu_env *env,
+ const struct cl_page_slice *slice, struct cl_io *io,
+ int nonblock)
+{
+ struct lov_io *lio = lov_env_io(env);
+ struct lov_io_sub *sub;
+
+ LINVRNT(lov_page_invariant(slice));
+ LINVRNT(!cl2lov_page(slice)->lps_invalid);
+
+ sub = lov_page_subio(env, lio, slice);
+ if (!IS_ERR(sub)) {
+ lov_sub_page(slice)->cp_owner = sub->sub_io;
+ lov_sub_put(sub);
+ } else
+ LBUG(); /* Arrgh */
+ return 0;
+}
+
+static void lov_page_assume(const struct lu_env *env,
+ const struct cl_page_slice *slice, struct cl_io *io)
+{
+ lov_page_own(env, slice, io, 0);
+}
+
+static int lov_page_cache_add(const struct lu_env *env,
+ const struct cl_page_slice *slice,
+ struct cl_io *io)
+{
+ struct lov_io *lio = lov_env_io(env);
+ struct lov_io_sub *sub;
+ int rc = 0;
+
+ LINVRNT(lov_page_invariant(slice));
+ LINVRNT(!cl2lov_page(slice)->lps_invalid);
+
+ sub = lov_page_subio(env, lio, slice);
+ if (!IS_ERR(sub)) {
+ rc = cl_page_cache_add(sub->sub_env, sub->sub_io,
+ slice->cpl_page->cp_child, CRT_WRITE);
+ lov_sub_put(sub);
+ } else {
+ rc = PTR_ERR(sub);
+ CL_PAGE_DEBUG(D_ERROR, env, slice->cpl_page, "rc = %d\n", rc);
+ }
+ return rc;
+}
+
+static int lov_page_print(const struct lu_env *env,
+ const struct cl_page_slice *slice,
+ void *cookie, lu_printer_t printer)
+{
+ struct lov_page *lp = cl2lov_page(slice);
+
+ return (*printer)(env, cookie, LUSTRE_LOV_NAME"-page@%p\n", lp);
+}
+
+static const struct cl_page_operations lov_page_ops = {
+ .cpo_fini = lov_page_fini,
+ .cpo_own = lov_page_own,
+ .cpo_assume = lov_page_assume,
+ .io = {
+ [CRT_WRITE] = {
+ .cpo_cache_add = lov_page_cache_add
+ }
+ },
+ .cpo_print = lov_page_print
+};
+
+static void lov_empty_page_fini(const struct lu_env *env,
+ struct cl_page_slice *slice)
+{
+ LASSERT(slice->cpl_page->cp_child == NULL);
+}
+
+int lov_page_init_raid0(const struct lu_env *env, struct cl_object *obj,
+ struct cl_page *page, struct page *vmpage)
+{
+ struct lov_object *loo = cl2lov(obj);
+ struct lov_layout_raid0 *r0 = lov_r0(loo);
+ struct lov_io *lio = lov_env_io(env);
+ struct cl_page *subpage;
+ struct cl_object *subobj;
+ struct lov_io_sub *sub;
+ struct lov_page *lpg = cl_object_page_slice(obj, page);
+ loff_t offset;
+ u64 suboff;
+ int stripe;
+ int rc;
+
+ offset = cl_offset(obj, page->cp_index);
+ stripe = lov_stripe_number(loo->lo_lsm, offset);
+ LASSERT(stripe < r0->lo_nr);
+ rc = lov_stripe_offset(loo->lo_lsm, offset, stripe,
+ &suboff);
+ LASSERT(rc == 0);
+
+ lpg->lps_invalid = 1;
+ cl_page_slice_add(page, &lpg->lps_cl, obj, &lov_page_ops);
+
+ sub = lov_sub_get(env, lio, stripe);
+ if (IS_ERR(sub)) {
+ rc = PTR_ERR(sub);
+ goto out;
+ }
+
+ subobj = lovsub2cl(r0->lo_sub[stripe]);
+ subpage = cl_page_find_sub(sub->sub_env, subobj,
+ cl_index(subobj, suboff), vmpage, page);
+ lov_sub_put(sub);
+ if (IS_ERR(subpage)) {
+ rc = PTR_ERR(subpage);
+ goto out;
+ }
+
+ if (likely(subpage->cp_parent == page)) {
+ lu_ref_add(&subpage->cp_reference, "lov", page);
+ lpg->lps_invalid = 0;
+ rc = 0;
+ } else {
+ CL_PAGE_DEBUG(D_ERROR, env, page, "parent page\n");
+ CL_PAGE_DEBUG(D_ERROR, env, subpage, "child page\n");
+ LASSERT(0);
+ }
+
+out:
+ return rc;
+}
+
+
+static const struct cl_page_operations lov_empty_page_ops = {
+ .cpo_fini = lov_empty_page_fini,
+ .cpo_print = lov_page_print
+};
+
+int lov_page_init_empty(const struct lu_env *env, struct cl_object *obj,
+ struct cl_page *page, struct page *vmpage)
+{
+ struct lov_page *lpg = cl_object_page_slice(obj, page);
+ void *addr;
+
+ cl_page_slice_add(page, &lpg->lps_cl, obj, &lov_empty_page_ops);
+ addr = kmap(vmpage);
+ memset(addr, 0, cl_page_size(obj));
+ kunmap(vmpage);
+ cl_page_export(env, page, 1);
+ return 0;
+}
+
+
+/** @} lov */
diff --git a/drivers/staging/lustre/lustre/lov/lov_pool.c b/drivers/staging/lustre/lustre/lov/lov_pool.c
new file mode 100644
index 000000000..d96163de7
--- /dev/null
+++ b/drivers/staging/lustre/lustre/lov/lov_pool.c
@@ -0,0 +1,673 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see [sun.com URL with a
+ * copy of GPLv2].
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2012, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ *
+ * lustre/lov/lov_pool.c
+ *
+ * OST pool methods
+ *
+ * Author: Jacques-Charles LAFOUCRIERE <jc.lafoucriere@cea.fr>
+ * Author: Alex Lyashkov <Alexey.Lyashkov@Sun.COM>
+ * Author: Nathaniel Rutman <Nathan.Rutman@Sun.COM>
+ */
+
+#define DEBUG_SUBSYSTEM S_LOV
+
+#include "../../include/linux/libcfs/libcfs.h"
+
+#include "../include/obd.h"
+#include "lov_internal.h"
+
+#define pool_tgt(_p, _i) \
+ _p->pool_lobd->u.lov.lov_tgts[_p->pool_obds.op_array[_i]]
+
+static void lov_pool_getref(struct pool_desc *pool)
+{
+ CDEBUG(D_INFO, "pool %p\n", pool);
+ atomic_inc(&pool->pool_refcount);
+}
+
+void lov_pool_putref(struct pool_desc *pool)
+{
+ CDEBUG(D_INFO, "pool %p\n", pool);
+ if (atomic_dec_and_test(&pool->pool_refcount)) {
+ LASSERT(hlist_unhashed(&pool->pool_hash));
+ LASSERT(list_empty(&pool->pool_list));
+ LASSERT(pool->pool_proc_entry == NULL);
+ lov_ost_pool_free(&(pool->pool_rr.lqr_pool));
+ lov_ost_pool_free(&(pool->pool_obds));
+ OBD_FREE_PTR(pool);
+ }
+}
+
+static void lov_pool_putref_locked(struct pool_desc *pool)
+{
+ CDEBUG(D_INFO, "pool %p\n", pool);
+ LASSERT(atomic_read(&pool->pool_refcount) > 1);
+
+ atomic_dec(&pool->pool_refcount);
+}
+
+/*
+ * hash function using a Rotating Hash algorithm
+ * Knuth, D. The Art of Computer Programming,
+ * Volume 3: Sorting and Searching,
+ * Chapter 6.4.
+ * Addison Wesley, 1973
+ */
+static __u32 pool_hashfn(struct cfs_hash *hash_body, const void *key, unsigned mask)
+{
+ int i;
+ __u32 result;
+ char *poolname;
+
+ result = 0;
+ poolname = (char *)key;
+ for (i = 0; i < LOV_MAXPOOLNAME; i++) {
+ if (poolname[i] == '\0')
+ break;
+ result = (result << 4)^(result >> 28) ^ poolname[i];
+ }
+ return (result % mask);
+}
+
+static void *pool_key(struct hlist_node *hnode)
+{
+ struct pool_desc *pool;
+
+ pool = hlist_entry(hnode, struct pool_desc, pool_hash);
+ return pool->pool_name;
+}
+
+static int pool_hashkey_keycmp(const void *key, struct hlist_node *compared_hnode)
+{
+ char *pool_name;
+ struct pool_desc *pool;
+
+ pool_name = (char *)key;
+ pool = hlist_entry(compared_hnode, struct pool_desc, pool_hash);
+ return !strncmp(pool_name, pool->pool_name, LOV_MAXPOOLNAME);
+}
+
+static void *pool_hashobject(struct hlist_node *hnode)
+{
+ return hlist_entry(hnode, struct pool_desc, pool_hash);
+}
+
+static void pool_hashrefcount_get(struct cfs_hash *hs, struct hlist_node *hnode)
+{
+ struct pool_desc *pool;
+
+ pool = hlist_entry(hnode, struct pool_desc, pool_hash);
+ lov_pool_getref(pool);
+}
+
+static void pool_hashrefcount_put_locked(struct cfs_hash *hs,
+ struct hlist_node *hnode)
+{
+ struct pool_desc *pool;
+
+ pool = hlist_entry(hnode, struct pool_desc, pool_hash);
+ lov_pool_putref_locked(pool);
+}
+
+cfs_hash_ops_t pool_hash_operations = {
+ .hs_hash = pool_hashfn,
+ .hs_key = pool_key,
+ .hs_keycmp = pool_hashkey_keycmp,
+ .hs_object = pool_hashobject,
+ .hs_get = pool_hashrefcount_get,
+ .hs_put_locked = pool_hashrefcount_put_locked,
+
+};
+
+#if defined (CONFIG_PROC_FS)
+/* ifdef needed for liblustre support */
+/*
+ * pool /proc seq_file methods
+ */
+/*
+ * iterator is used to go through the target pool entries
+ * index is the current entry index in the lp_array[] array
+ * index >= pos returned to the seq_file interface
+ * pos is from 0 to (pool->pool_obds.op_count - 1)
+ */
+#define POOL_IT_MAGIC 0xB001CEA0
+struct pool_iterator {
+ int magic;
+ struct pool_desc *pool;
+ int idx; /* from 0 to pool_tgt_size - 1 */
+};
+
+static void *pool_proc_next(struct seq_file *s, void *v, loff_t *pos)
+{
+ struct pool_iterator *iter = (struct pool_iterator *)s->private;
+ int prev_idx;
+
+ LASSERTF(iter->magic == POOL_IT_MAGIC, "%08X", iter->magic);
+
+ /* test if end of file */
+ if (*pos >= pool_tgt_count(iter->pool))
+ return NULL;
+
+ /* iterate to find a non empty entry */
+ prev_idx = iter->idx;
+ down_read(&pool_tgt_rw_sem(iter->pool));
+ iter->idx++;
+ if (iter->idx == pool_tgt_count(iter->pool)) {
+ iter->idx = prev_idx; /* we stay on the last entry */
+ up_read(&pool_tgt_rw_sem(iter->pool));
+ return NULL;
+ }
+ up_read(&pool_tgt_rw_sem(iter->pool));
+ (*pos)++;
+ /* return != NULL to continue */
+ return iter;
+}
+
+static void *pool_proc_start(struct seq_file *s, loff_t *pos)
+{
+ struct pool_desc *pool = (struct pool_desc *)s->private;
+ struct pool_iterator *iter;
+
+ lov_pool_getref(pool);
+ if ((pool_tgt_count(pool) == 0) ||
+ (*pos >= pool_tgt_count(pool))) {
+ /* iter is not created, so stop() has no way to
+ * find pool to dec ref */
+ lov_pool_putref(pool);
+ return NULL;
+ }
+
+ OBD_ALLOC_PTR(iter);
+ if (!iter)
+ return ERR_PTR(-ENOMEM);
+ iter->magic = POOL_IT_MAGIC;
+ iter->pool = pool;
+ iter->idx = 0;
+
+ /* we use seq_file private field to memorized iterator so
+ * we can free it at stop() */
+ /* /!\ do not forget to restore it to pool before freeing it */
+ s->private = iter;
+ if (*pos > 0) {
+ loff_t i;
+ void *ptr;
+
+ i = 0;
+ do {
+ ptr = pool_proc_next(s, &iter, &i);
+ } while ((i < *pos) && (ptr != NULL));
+ return ptr;
+ }
+ return iter;
+}
+
+static void pool_proc_stop(struct seq_file *s, void *v)
+{
+ struct pool_iterator *iter = (struct pool_iterator *)s->private;
+
+ /* in some cases stop() method is called 2 times, without
+ * calling start() method (see seq_read() from fs/seq_file.c)
+ * we have to free only if s->private is an iterator */
+ if ((iter) && (iter->magic == POOL_IT_MAGIC)) {
+ /* we restore s->private so next call to pool_proc_start()
+ * will work */
+ s->private = iter->pool;
+ lov_pool_putref(iter->pool);
+ OBD_FREE_PTR(iter);
+ }
+ return;
+}
+
+static int pool_proc_show(struct seq_file *s, void *v)
+{
+ struct pool_iterator *iter = (struct pool_iterator *)v;
+ struct lov_tgt_desc *tgt;
+
+ LASSERTF(iter->magic == POOL_IT_MAGIC, "%08X", iter->magic);
+ LASSERT(iter->pool != NULL);
+ LASSERT(iter->idx <= pool_tgt_count(iter->pool));
+
+ down_read(&pool_tgt_rw_sem(iter->pool));
+ tgt = pool_tgt(iter->pool, iter->idx);
+ up_read(&pool_tgt_rw_sem(iter->pool));
+ if (tgt)
+ seq_printf(s, "%s\n", obd_uuid2str(&(tgt->ltd_uuid)));
+
+ return 0;
+}
+
+static struct seq_operations pool_proc_ops = {
+ .start = pool_proc_start,
+ .next = pool_proc_next,
+ .stop = pool_proc_stop,
+ .show = pool_proc_show,
+};
+
+static int pool_proc_open(struct inode *inode, struct file *file)
+{
+ int rc;
+
+ rc = seq_open(file, &pool_proc_ops);
+ if (!rc) {
+ struct seq_file *s = file->private_data;
+ s->private = PDE_DATA(inode);
+ }
+ return rc;
+}
+
+static struct file_operations pool_proc_operations = {
+ .open = pool_proc_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = seq_release,
+};
+#endif /* CONFIG_PROC_FS */
+
+void lov_dump_pool(int level, struct pool_desc *pool)
+{
+ int i;
+
+ lov_pool_getref(pool);
+
+ CDEBUG(level, "pool "LOV_POOLNAMEF" has %d members\n",
+ pool->pool_name, pool->pool_obds.op_count);
+ down_read(&pool_tgt_rw_sem(pool));
+
+ for (i = 0; i < pool_tgt_count(pool) ; i++) {
+ if (!pool_tgt(pool, i) || !(pool_tgt(pool, i))->ltd_exp)
+ continue;
+ CDEBUG(level, "pool "LOV_POOLNAMEF"[%d] = %s\n",
+ pool->pool_name, i,
+ obd_uuid2str(&((pool_tgt(pool, i))->ltd_uuid)));
+ }
+
+ up_read(&pool_tgt_rw_sem(pool));
+ lov_pool_putref(pool);
+}
+
+#define LOV_POOL_INIT_COUNT 2
+int lov_ost_pool_init(struct ost_pool *op, unsigned int count)
+{
+ if (count == 0)
+ count = LOV_POOL_INIT_COUNT;
+ op->op_array = NULL;
+ op->op_count = 0;
+ init_rwsem(&op->op_rw_sem);
+ op->op_size = count;
+ OBD_ALLOC(op->op_array, op->op_size * sizeof(op->op_array[0]));
+ if (op->op_array == NULL) {
+ op->op_size = 0;
+ return -ENOMEM;
+ }
+ return 0;
+}
+
+/* Caller must hold write op_rwlock */
+int lov_ost_pool_extend(struct ost_pool *op, unsigned int min_count)
+{
+ __u32 *new;
+ int new_size;
+
+ LASSERT(min_count != 0);
+
+ if (op->op_count < op->op_size)
+ return 0;
+
+ new_size = max(min_count, 2 * op->op_size);
+ OBD_ALLOC(new, new_size * sizeof(op->op_array[0]));
+ if (new == NULL)
+ return -ENOMEM;
+
+ /* copy old array to new one */
+ memcpy(new, op->op_array, op->op_size * sizeof(op->op_array[0]));
+ OBD_FREE(op->op_array, op->op_size * sizeof(op->op_array[0]));
+ op->op_array = new;
+ op->op_size = new_size;
+ return 0;
+}
+
+int lov_ost_pool_add(struct ost_pool *op, __u32 idx, unsigned int min_count)
+{
+ int rc = 0, i;
+
+ down_write(&op->op_rw_sem);
+
+ rc = lov_ost_pool_extend(op, min_count);
+ if (rc)
+ goto out;
+
+ /* search ost in pool array */
+ for (i = 0; i < op->op_count; i++) {
+ if (op->op_array[i] == idx) {
+ rc = -EEXIST;
+ goto out;
+ }
+ }
+ /* ost not found we add it */
+ op->op_array[op->op_count] = idx;
+ op->op_count++;
+out:
+ up_write(&op->op_rw_sem);
+ return rc;
+}
+
+int lov_ost_pool_remove(struct ost_pool *op, __u32 idx)
+{
+ int i;
+
+ down_write(&op->op_rw_sem);
+
+ for (i = 0; i < op->op_count; i++) {
+ if (op->op_array[i] == idx) {
+ memmove(&op->op_array[i], &op->op_array[i + 1],
+ (op->op_count - i - 1) * sizeof(op->op_array[0]));
+ op->op_count--;
+ up_write(&op->op_rw_sem);
+ return 0;
+ }
+ }
+
+ up_write(&op->op_rw_sem);
+ return -EINVAL;
+}
+
+int lov_ost_pool_free(struct ost_pool *op)
+{
+ if (op->op_size == 0)
+ return 0;
+
+ down_write(&op->op_rw_sem);
+
+ OBD_FREE(op->op_array, op->op_size * sizeof(op->op_array[0]));
+ op->op_array = NULL;
+ op->op_count = 0;
+ op->op_size = 0;
+
+ up_write(&op->op_rw_sem);
+ return 0;
+}
+
+
+int lov_pool_new(struct obd_device *obd, char *poolname)
+{
+ struct lov_obd *lov;
+ struct pool_desc *new_pool;
+ int rc;
+
+ lov = &(obd->u.lov);
+
+ if (strlen(poolname) > LOV_MAXPOOLNAME)
+ return -ENAMETOOLONG;
+
+ OBD_ALLOC_PTR(new_pool);
+ if (new_pool == NULL)
+ return -ENOMEM;
+
+ strncpy(new_pool->pool_name, poolname, LOV_MAXPOOLNAME);
+ new_pool->pool_name[LOV_MAXPOOLNAME] = '\0';
+ new_pool->pool_lobd = obd;
+ /* ref count init to 1 because when created a pool is always used
+ * up to deletion
+ */
+ atomic_set(&new_pool->pool_refcount, 1);
+ rc = lov_ost_pool_init(&new_pool->pool_obds, 0);
+ if (rc)
+ goto out_err;
+
+ memset(&(new_pool->pool_rr), 0, sizeof(struct lov_qos_rr));
+ rc = lov_ost_pool_init(&new_pool->pool_rr.lqr_pool, 0);
+ if (rc)
+ goto out_free_pool_obds;
+
+ INIT_HLIST_NODE(&new_pool->pool_hash);
+
+#if defined (CONFIG_PROC_FS)
+ /* we need this assert seq_file is not implemented for liblustre */
+ /* get ref for /proc file */
+ lov_pool_getref(new_pool);
+ new_pool->pool_proc_entry = lprocfs_add_simple(lov->lov_pool_proc_entry,
+ poolname, new_pool,
+ &pool_proc_operations);
+ if (IS_ERR(new_pool->pool_proc_entry)) {
+ CWARN("Cannot add proc pool entry "LOV_POOLNAMEF"\n", poolname);
+ new_pool->pool_proc_entry = NULL;
+ lov_pool_putref(new_pool);
+ }
+ CDEBUG(D_INFO, "pool %p - proc %p\n", new_pool, new_pool->pool_proc_entry);
+#endif
+
+ spin_lock(&obd->obd_dev_lock);
+ list_add_tail(&new_pool->pool_list, &lov->lov_pool_list);
+ lov->lov_pool_count++;
+ spin_unlock(&obd->obd_dev_lock);
+
+ /* add to find only when it fully ready */
+ rc = cfs_hash_add_unique(lov->lov_pools_hash_body, poolname,
+ &new_pool->pool_hash);
+ if (rc) {
+ rc = -EEXIST;
+ goto out_err;
+ }
+
+ CDEBUG(D_CONFIG, LOV_POOLNAMEF" is pool #%d\n",
+ poolname, lov->lov_pool_count);
+
+ return 0;
+
+out_err:
+ spin_lock(&obd->obd_dev_lock);
+ list_del_init(&new_pool->pool_list);
+ lov->lov_pool_count--;
+ spin_unlock(&obd->obd_dev_lock);
+
+ lprocfs_remove(&new_pool->pool_proc_entry);
+
+ lov_ost_pool_free(&new_pool->pool_rr.lqr_pool);
+out_free_pool_obds:
+ lov_ost_pool_free(&new_pool->pool_obds);
+ OBD_FREE_PTR(new_pool);
+ return rc;
+}
+
+int lov_pool_del(struct obd_device *obd, char *poolname)
+{
+ struct lov_obd *lov;
+ struct pool_desc *pool;
+
+ lov = &(obd->u.lov);
+
+ /* lookup and kill hash reference */
+ pool = cfs_hash_del_key(lov->lov_pools_hash_body, poolname);
+ if (pool == NULL)
+ return -ENOENT;
+
+ if (pool->pool_proc_entry != NULL) {
+ CDEBUG(D_INFO, "proc entry %p\n", pool->pool_proc_entry);
+ lprocfs_remove(&pool->pool_proc_entry);
+ lov_pool_putref(pool);
+ }
+
+ spin_lock(&obd->obd_dev_lock);
+ list_del_init(&pool->pool_list);
+ lov->lov_pool_count--;
+ spin_unlock(&obd->obd_dev_lock);
+
+ /* release last reference */
+ lov_pool_putref(pool);
+
+ return 0;
+}
+
+
+int lov_pool_add(struct obd_device *obd, char *poolname, char *ostname)
+{
+ struct obd_uuid ost_uuid;
+ struct lov_obd *lov;
+ struct pool_desc *pool;
+ unsigned int lov_idx;
+ int rc;
+
+ lov = &(obd->u.lov);
+
+ pool = cfs_hash_lookup(lov->lov_pools_hash_body, poolname);
+ if (pool == NULL)
+ return -ENOENT;
+
+ obd_str2uuid(&ost_uuid, ostname);
+
+
+ /* search ost in lov array */
+ obd_getref(obd);
+ for (lov_idx = 0; lov_idx < lov->desc.ld_tgt_count; lov_idx++) {
+ if (!lov->lov_tgts[lov_idx])
+ continue;
+ if (obd_uuid_equals(&ost_uuid,
+ &(lov->lov_tgts[lov_idx]->ltd_uuid)))
+ break;
+ }
+ /* test if ost found in lov */
+ if (lov_idx == lov->desc.ld_tgt_count) {
+ rc = -EINVAL;
+ goto out;
+ }
+
+ rc = lov_ost_pool_add(&pool->pool_obds, lov_idx, lov->lov_tgt_size);
+ if (rc)
+ goto out;
+
+ pool->pool_rr.lqr_dirty = 1;
+
+ CDEBUG(D_CONFIG, "Added %s to "LOV_POOLNAMEF" as member %d\n",
+ ostname, poolname, pool_tgt_count(pool));
+
+out:
+ obd_putref(obd);
+ lov_pool_putref(pool);
+ return rc;
+}
+
+int lov_pool_remove(struct obd_device *obd, char *poolname, char *ostname)
+{
+ struct obd_uuid ost_uuid;
+ struct lov_obd *lov;
+ struct pool_desc *pool;
+ unsigned int lov_idx;
+ int rc = 0;
+
+ lov = &(obd->u.lov);
+
+ pool = cfs_hash_lookup(lov->lov_pools_hash_body, poolname);
+ if (pool == NULL)
+ return -ENOENT;
+
+ obd_str2uuid(&ost_uuid, ostname);
+
+ obd_getref(obd);
+ /* search ost in lov array, to get index */
+ for (lov_idx = 0; lov_idx < lov->desc.ld_tgt_count; lov_idx++) {
+ if (!lov->lov_tgts[lov_idx])
+ continue;
+
+ if (obd_uuid_equals(&ost_uuid,
+ &(lov->lov_tgts[lov_idx]->ltd_uuid)))
+ break;
+ }
+
+ /* test if ost found in lov */
+ if (lov_idx == lov->desc.ld_tgt_count) {
+ rc = -EINVAL;
+ goto out;
+ }
+
+ lov_ost_pool_remove(&pool->pool_obds, lov_idx);
+
+ pool->pool_rr.lqr_dirty = 1;
+
+ CDEBUG(D_CONFIG, "%s removed from "LOV_POOLNAMEF"\n", ostname,
+ poolname);
+
+out:
+ obd_putref(obd);
+ lov_pool_putref(pool);
+ return rc;
+}
+
+int lov_check_index_in_pool(__u32 idx, struct pool_desc *pool)
+{
+ int i, rc;
+
+ /* caller may no have a ref on pool if it got the pool
+ * without calling lov_find_pool() (e.g. go through the lov pool
+ * list)
+ */
+ lov_pool_getref(pool);
+
+ down_read(&pool_tgt_rw_sem(pool));
+
+ for (i = 0; i < pool_tgt_count(pool); i++) {
+ if (pool_tgt_array(pool)[i] == idx) {
+ rc = 0;
+ goto out;
+ }
+ }
+ rc = -ENOENT;
+out:
+ up_read(&pool_tgt_rw_sem(pool));
+
+ lov_pool_putref(pool);
+ return rc;
+}
+
+struct pool_desc *lov_find_pool(struct lov_obd *lov, char *poolname)
+{
+ struct pool_desc *pool;
+
+ pool = NULL;
+ if (poolname[0] != '\0') {
+ pool = cfs_hash_lookup(lov->lov_pools_hash_body, poolname);
+ if (pool == NULL)
+ CWARN("Request for an unknown pool ("LOV_POOLNAMEF")\n",
+ poolname);
+ if ((pool != NULL) && (pool_tgt_count(pool) == 0)) {
+ CWARN("Request for an empty pool ("LOV_POOLNAMEF")\n",
+ poolname);
+ /* pool is ignored, so we remove ref on it */
+ lov_pool_putref(pool);
+ pool = NULL;
+ }
+ }
+ return pool;
+}
diff --git a/drivers/staging/lustre/lustre/lov/lov_request.c b/drivers/staging/lustre/lustre/lov/lov_request.c
new file mode 100644
index 000000000..933e2d1f8
--- /dev/null
+++ b/drivers/staging/lustre/lustre/lov/lov_request.c
@@ -0,0 +1,773 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2011, 2012, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ */
+
+#define DEBUG_SUBSYSTEM S_LOV
+
+#include "../../include/linux/libcfs/libcfs.h"
+
+#include "../include/obd_class.h"
+#include "../include/lustre/lustre_idl.h"
+#include "lov_internal.h"
+
+static void lov_init_set(struct lov_request_set *set)
+{
+ set->set_count = 0;
+ atomic_set(&set->set_completes, 0);
+ atomic_set(&set->set_success, 0);
+ atomic_set(&set->set_finish_checked, 0);
+ set->set_cookies = NULL;
+ INIT_LIST_HEAD(&set->set_list);
+ atomic_set(&set->set_refcount, 1);
+ init_waitqueue_head(&set->set_waitq);
+ spin_lock_init(&set->set_lock);
+}
+
+void lov_finish_set(struct lov_request_set *set)
+{
+ struct list_head *pos, *n;
+
+ LASSERT(set);
+ list_for_each_safe(pos, n, &set->set_list) {
+ struct lov_request *req = list_entry(pos,
+ struct lov_request,
+ rq_link);
+ list_del_init(&req->rq_link);
+
+ if (req->rq_oi.oi_oa)
+ OBDO_FREE(req->rq_oi.oi_oa);
+ if (req->rq_oi.oi_md)
+ OBD_FREE_LARGE(req->rq_oi.oi_md, req->rq_buflen);
+ if (req->rq_oi.oi_osfs)
+ OBD_FREE(req->rq_oi.oi_osfs,
+ sizeof(*req->rq_oi.oi_osfs));
+ OBD_FREE(req, sizeof(*req));
+ }
+
+ if (set->set_pga) {
+ int len = set->set_oabufs * sizeof(*set->set_pga);
+ OBD_FREE_LARGE(set->set_pga, len);
+ }
+ if (set->set_lockh)
+ lov_llh_put(set->set_lockh);
+
+ OBD_FREE(set, sizeof(*set));
+}
+
+int lov_set_finished(struct lov_request_set *set, int idempotent)
+{
+ int completes = atomic_read(&set->set_completes);
+
+ CDEBUG(D_INFO, "check set %d/%d\n", completes, set->set_count);
+
+ if (completes == set->set_count) {
+ if (idempotent)
+ return 1;
+ if (atomic_inc_return(&set->set_finish_checked) == 1)
+ return 1;
+ }
+ return 0;
+}
+
+void lov_update_set(struct lov_request_set *set,
+ struct lov_request *req, int rc)
+{
+ req->rq_complete = 1;
+ req->rq_rc = rc;
+
+ atomic_inc(&set->set_completes);
+ if (rc == 0)
+ atomic_inc(&set->set_success);
+
+ wake_up(&set->set_waitq);
+}
+
+int lov_update_common_set(struct lov_request_set *set,
+ struct lov_request *req, int rc)
+{
+ struct lov_obd *lov = &set->set_exp->exp_obd->u.lov;
+
+ lov_update_set(set, req, rc);
+
+ /* grace error on inactive ost */
+ if (rc && !(lov->lov_tgts[req->rq_idx] &&
+ lov->lov_tgts[req->rq_idx]->ltd_active))
+ rc = 0;
+
+ /* FIXME in raid1 regime, should return 0 */
+ return rc;
+}
+
+void lov_set_add_req(struct lov_request *req, struct lov_request_set *set)
+{
+ list_add_tail(&req->rq_link, &set->set_list);
+ set->set_count++;
+ req->rq_rqset = set;
+}
+
+static int lov_check_set(struct lov_obd *lov, int idx)
+{
+ int rc;
+ struct lov_tgt_desc *tgt;
+
+ mutex_lock(&lov->lov_lock);
+ tgt = lov->lov_tgts[idx];
+ rc = !tgt || tgt->ltd_active ||
+ (tgt->ltd_exp &&
+ class_exp2cliimp(tgt->ltd_exp)->imp_connect_tried);
+ mutex_unlock(&lov->lov_lock);
+
+ return rc;
+}
+
+/* Check if the OSC connection exists and is active.
+ * If the OSC has not yet had a chance to connect to the OST the first time,
+ * wait once for it to connect instead of returning an error.
+ */
+int lov_check_and_wait_active(struct lov_obd *lov, int ost_idx)
+{
+ wait_queue_head_t waitq;
+ struct l_wait_info lwi;
+ struct lov_tgt_desc *tgt;
+ int rc = 0;
+
+ mutex_lock(&lov->lov_lock);
+
+ tgt = lov->lov_tgts[ost_idx];
+
+ if (unlikely(tgt == NULL)) {
+ rc = 0;
+ goto out;
+ }
+
+ if (likely(tgt->ltd_active)) {
+ rc = 1;
+ goto out;
+ }
+
+ if (tgt->ltd_exp && class_exp2cliimp(tgt->ltd_exp)->imp_connect_tried) {
+ rc = 0;
+ goto out;
+ }
+
+ mutex_unlock(&lov->lov_lock);
+
+ init_waitqueue_head(&waitq);
+ lwi = LWI_TIMEOUT_INTERVAL(cfs_time_seconds(obd_timeout),
+ cfs_time_seconds(1), NULL, NULL);
+
+ rc = l_wait_event(waitq, lov_check_set(lov, ost_idx), &lwi);
+ if (tgt != NULL && tgt->ltd_active)
+ return 1;
+
+ return 0;
+
+out:
+ mutex_unlock(&lov->lov_lock);
+ return rc;
+}
+
+static int common_attr_done(struct lov_request_set *set)
+{
+ struct list_head *pos;
+ struct lov_request *req;
+ struct obdo *tmp_oa;
+ int rc = 0, attrset = 0;
+
+ LASSERT(set->set_oi != NULL);
+
+ if (set->set_oi->oi_oa == NULL)
+ return 0;
+
+ if (!atomic_read(&set->set_success))
+ return -EIO;
+
+ OBDO_ALLOC(tmp_oa);
+ if (tmp_oa == NULL) {
+ rc = -ENOMEM;
+ goto out;
+ }
+
+ list_for_each(pos, &set->set_list) {
+ req = list_entry(pos, struct lov_request, rq_link);
+
+ if (!req->rq_complete || req->rq_rc)
+ continue;
+ if (req->rq_oi.oi_oa->o_valid == 0) /* inactive stripe */
+ continue;
+ lov_merge_attrs(tmp_oa, req->rq_oi.oi_oa,
+ req->rq_oi.oi_oa->o_valid,
+ set->set_oi->oi_md, req->rq_stripe, &attrset);
+ }
+ if (!attrset) {
+ CERROR("No stripes had valid attrs\n");
+ rc = -EIO;
+ }
+ if ((set->set_oi->oi_oa->o_valid & OBD_MD_FLEPOCH) &&
+ (set->set_oi->oi_md->lsm_stripe_count != attrset)) {
+ /* When we take attributes of some epoch, we require all the
+ * ost to be active. */
+ CERROR("Not all the stripes had valid attrs\n");
+ rc = -EIO;
+ goto out;
+ }
+
+ tmp_oa->o_oi = set->set_oi->oi_oa->o_oi;
+ memcpy(set->set_oi->oi_oa, tmp_oa, sizeof(*set->set_oi->oi_oa));
+out:
+ if (tmp_oa)
+ OBDO_FREE(tmp_oa);
+ return rc;
+
+}
+
+int lov_fini_getattr_set(struct lov_request_set *set)
+{
+ int rc = 0;
+
+ if (set == NULL)
+ return 0;
+ LASSERT(set->set_exp);
+ if (atomic_read(&set->set_completes))
+ rc = common_attr_done(set);
+
+ lov_put_reqset(set);
+
+ return rc;
+}
+
+/* The callback for osc_getattr_async that finalizes a request info when a
+ * response is received. */
+static int cb_getattr_update(void *cookie, int rc)
+{
+ struct obd_info *oinfo = cookie;
+ struct lov_request *lovreq;
+
+ lovreq = container_of(oinfo, struct lov_request, rq_oi);
+ return lov_update_common_set(lovreq->rq_rqset, lovreq, rc);
+}
+
+int lov_prep_getattr_set(struct obd_export *exp, struct obd_info *oinfo,
+ struct lov_request_set **reqset)
+{
+ struct lov_request_set *set;
+ struct lov_obd *lov = &exp->exp_obd->u.lov;
+ int rc = 0, i;
+
+ OBD_ALLOC(set, sizeof(*set));
+ if (set == NULL)
+ return -ENOMEM;
+ lov_init_set(set);
+
+ set->set_exp = exp;
+ set->set_oi = oinfo;
+
+ for (i = 0; i < oinfo->oi_md->lsm_stripe_count; i++) {
+ struct lov_oinfo *loi;
+ struct lov_request *req;
+
+ loi = oinfo->oi_md->lsm_oinfo[i];
+ if (lov_oinfo_is_dummy(loi))
+ continue;
+
+ if (!lov_check_and_wait_active(lov, loi->loi_ost_idx)) {
+ CDEBUG(D_HA, "lov idx %d inactive\n", loi->loi_ost_idx);
+ if (oinfo->oi_oa->o_valid & OBD_MD_FLEPOCH) {
+ /* SOM requires all the OSTs to be active. */
+ rc = -EIO;
+ goto out_set;
+ }
+ continue;
+ }
+
+ OBD_ALLOC(req, sizeof(*req));
+ if (req == NULL) {
+ rc = -ENOMEM;
+ goto out_set;
+ }
+
+ req->rq_stripe = i;
+ req->rq_idx = loi->loi_ost_idx;
+
+ OBDO_ALLOC(req->rq_oi.oi_oa);
+ if (req->rq_oi.oi_oa == NULL) {
+ OBD_FREE(req, sizeof(*req));
+ rc = -ENOMEM;
+ goto out_set;
+ }
+ memcpy(req->rq_oi.oi_oa, oinfo->oi_oa,
+ sizeof(*req->rq_oi.oi_oa));
+ req->rq_oi.oi_oa->o_oi = loi->loi_oi;
+ req->rq_oi.oi_cb_up = cb_getattr_update;
+ req->rq_oi.oi_capa = oinfo->oi_capa;
+
+ lov_set_add_req(req, set);
+ }
+ if (!set->set_count) {
+ rc = -EIO;
+ goto out_set;
+ }
+ *reqset = set;
+ return rc;
+out_set:
+ lov_fini_getattr_set(set);
+ return rc;
+}
+
+int lov_fini_destroy_set(struct lov_request_set *set)
+{
+ if (set == NULL)
+ return 0;
+ LASSERT(set->set_exp);
+ if (atomic_read(&set->set_completes)) {
+ /* FIXME update qos data here */
+ }
+
+ lov_put_reqset(set);
+
+ return 0;
+}
+
+int lov_prep_destroy_set(struct obd_export *exp, struct obd_info *oinfo,
+ struct obdo *src_oa, struct lov_stripe_md *lsm,
+ struct obd_trans_info *oti,
+ struct lov_request_set **reqset)
+{
+ struct lov_request_set *set;
+ struct lov_obd *lov = &exp->exp_obd->u.lov;
+ int rc = 0, i;
+
+ OBD_ALLOC(set, sizeof(*set));
+ if (set == NULL)
+ return -ENOMEM;
+ lov_init_set(set);
+
+ set->set_exp = exp;
+ set->set_oi = oinfo;
+ set->set_oi->oi_md = lsm;
+ set->set_oi->oi_oa = src_oa;
+ set->set_oti = oti;
+ if (oti != NULL && src_oa->o_valid & OBD_MD_FLCOOKIE)
+ set->set_cookies = oti->oti_logcookies;
+
+ for (i = 0; i < lsm->lsm_stripe_count; i++) {
+ struct lov_oinfo *loi;
+ struct lov_request *req;
+
+ loi = lsm->lsm_oinfo[i];
+ if (lov_oinfo_is_dummy(loi))
+ continue;
+
+ if (!lov_check_and_wait_active(lov, loi->loi_ost_idx)) {
+ CDEBUG(D_HA, "lov idx %d inactive\n", loi->loi_ost_idx);
+ continue;
+ }
+
+ OBD_ALLOC(req, sizeof(*req));
+ if (req == NULL) {
+ rc = -ENOMEM;
+ goto out_set;
+ }
+
+ req->rq_stripe = i;
+ req->rq_idx = loi->loi_ost_idx;
+
+ OBDO_ALLOC(req->rq_oi.oi_oa);
+ if (req->rq_oi.oi_oa == NULL) {
+ OBD_FREE(req, sizeof(*req));
+ rc = -ENOMEM;
+ goto out_set;
+ }
+ memcpy(req->rq_oi.oi_oa, src_oa, sizeof(*req->rq_oi.oi_oa));
+ req->rq_oi.oi_oa->o_oi = loi->loi_oi;
+ lov_set_add_req(req, set);
+ }
+ if (!set->set_count) {
+ rc = -EIO;
+ goto out_set;
+ }
+ *reqset = set;
+ return rc;
+out_set:
+ lov_fini_destroy_set(set);
+ return rc;
+}
+
+int lov_fini_setattr_set(struct lov_request_set *set)
+{
+ int rc = 0;
+
+ if (set == NULL)
+ return 0;
+ LASSERT(set->set_exp);
+ if (atomic_read(&set->set_completes)) {
+ rc = common_attr_done(set);
+ /* FIXME update qos data here */
+ }
+
+ lov_put_reqset(set);
+ return rc;
+}
+
+int lov_update_setattr_set(struct lov_request_set *set,
+ struct lov_request *req, int rc)
+{
+ struct lov_obd *lov = &req->rq_rqset->set_exp->exp_obd->u.lov;
+ struct lov_stripe_md *lsm = req->rq_rqset->set_oi->oi_md;
+
+ lov_update_set(set, req, rc);
+
+ /* grace error on inactive ost */
+ if (rc && !(lov->lov_tgts[req->rq_idx] &&
+ lov->lov_tgts[req->rq_idx]->ltd_active))
+ rc = 0;
+
+ if (rc == 0) {
+ if (req->rq_oi.oi_oa->o_valid & OBD_MD_FLCTIME)
+ lsm->lsm_oinfo[req->rq_stripe]->loi_lvb.lvb_ctime =
+ req->rq_oi.oi_oa->o_ctime;
+ if (req->rq_oi.oi_oa->o_valid & OBD_MD_FLMTIME)
+ lsm->lsm_oinfo[req->rq_stripe]->loi_lvb.lvb_mtime =
+ req->rq_oi.oi_oa->o_mtime;
+ if (req->rq_oi.oi_oa->o_valid & OBD_MD_FLATIME)
+ lsm->lsm_oinfo[req->rq_stripe]->loi_lvb.lvb_atime =
+ req->rq_oi.oi_oa->o_atime;
+ }
+
+ return rc;
+}
+
+/* The callback for osc_setattr_async that finalizes a request info when a
+ * response is received. */
+static int cb_setattr_update(void *cookie, int rc)
+{
+ struct obd_info *oinfo = cookie;
+ struct lov_request *lovreq;
+
+ lovreq = container_of(oinfo, struct lov_request, rq_oi);
+ return lov_update_setattr_set(lovreq->rq_rqset, lovreq, rc);
+}
+
+int lov_prep_setattr_set(struct obd_export *exp, struct obd_info *oinfo,
+ struct obd_trans_info *oti,
+ struct lov_request_set **reqset)
+{
+ struct lov_request_set *set;
+ struct lov_obd *lov = &exp->exp_obd->u.lov;
+ int rc = 0, i;
+
+ OBD_ALLOC(set, sizeof(*set));
+ if (set == NULL)
+ return -ENOMEM;
+ lov_init_set(set);
+
+ set->set_exp = exp;
+ set->set_oti = oti;
+ set->set_oi = oinfo;
+ if (oti != NULL && oinfo->oi_oa->o_valid & OBD_MD_FLCOOKIE)
+ set->set_cookies = oti->oti_logcookies;
+
+ for (i = 0; i < oinfo->oi_md->lsm_stripe_count; i++) {
+ struct lov_oinfo *loi = oinfo->oi_md->lsm_oinfo[i];
+ struct lov_request *req;
+
+ if (lov_oinfo_is_dummy(loi))
+ continue;
+
+ if (!lov_check_and_wait_active(lov, loi->loi_ost_idx)) {
+ CDEBUG(D_HA, "lov idx %d inactive\n", loi->loi_ost_idx);
+ continue;
+ }
+
+ OBD_ALLOC(req, sizeof(*req));
+ if (req == NULL) {
+ rc = -ENOMEM;
+ goto out_set;
+ }
+ req->rq_stripe = i;
+ req->rq_idx = loi->loi_ost_idx;
+
+ OBDO_ALLOC(req->rq_oi.oi_oa);
+ if (req->rq_oi.oi_oa == NULL) {
+ OBD_FREE(req, sizeof(*req));
+ rc = -ENOMEM;
+ goto out_set;
+ }
+ memcpy(req->rq_oi.oi_oa, oinfo->oi_oa,
+ sizeof(*req->rq_oi.oi_oa));
+ req->rq_oi.oi_oa->o_oi = loi->loi_oi;
+ req->rq_oi.oi_oa->o_stripe_idx = i;
+ req->rq_oi.oi_cb_up = cb_setattr_update;
+ req->rq_oi.oi_capa = oinfo->oi_capa;
+
+ if (oinfo->oi_oa->o_valid & OBD_MD_FLSIZE) {
+ int off = lov_stripe_offset(oinfo->oi_md,
+ oinfo->oi_oa->o_size, i,
+ &req->rq_oi.oi_oa->o_size);
+
+ if (off < 0 && req->rq_oi.oi_oa->o_size)
+ req->rq_oi.oi_oa->o_size--;
+
+ CDEBUG(D_INODE, "stripe %d has size %llu/%llu\n",
+ i, req->rq_oi.oi_oa->o_size,
+ oinfo->oi_oa->o_size);
+ }
+ lov_set_add_req(req, set);
+ }
+ if (!set->set_count) {
+ rc = -EIO;
+ goto out_set;
+ }
+ *reqset = set;
+ return rc;
+out_set:
+ lov_fini_setattr_set(set);
+ return rc;
+}
+
+#define LOV_U64_MAX ((__u64)~0ULL)
+#define LOV_SUM_MAX(tot, add) \
+ do { \
+ if ((tot) + (add) < (tot)) \
+ (tot) = LOV_U64_MAX; \
+ else \
+ (tot) += (add); \
+ } while (0)
+
+int lov_fini_statfs(struct obd_device *obd, struct obd_statfs *osfs,
+ int success)
+{
+ if (success) {
+ __u32 expected_stripes = lov_get_stripecnt(&obd->u.lov,
+ LOV_MAGIC, 0);
+ if (osfs->os_files != LOV_U64_MAX)
+ lov_do_div64(osfs->os_files, expected_stripes);
+ if (osfs->os_ffree != LOV_U64_MAX)
+ lov_do_div64(osfs->os_ffree, expected_stripes);
+
+ spin_lock(&obd->obd_osfs_lock);
+ memcpy(&obd->obd_osfs, osfs, sizeof(*osfs));
+ obd->obd_osfs_age = cfs_time_current_64();
+ spin_unlock(&obd->obd_osfs_lock);
+ return 0;
+ }
+
+ return -EIO;
+}
+
+int lov_fini_statfs_set(struct lov_request_set *set)
+{
+ int rc = 0;
+
+ if (set == NULL)
+ return 0;
+
+ if (atomic_read(&set->set_completes)) {
+ rc = lov_fini_statfs(set->set_obd, set->set_oi->oi_osfs,
+ atomic_read(&set->set_success));
+ }
+ lov_put_reqset(set);
+ return rc;
+}
+
+void lov_update_statfs(struct obd_statfs *osfs, struct obd_statfs *lov_sfs,
+ int success)
+{
+ int shift = 0, quit = 0;
+ __u64 tmp;
+
+ if (success == 0) {
+ memcpy(osfs, lov_sfs, sizeof(*lov_sfs));
+ } else {
+ if (osfs->os_bsize != lov_sfs->os_bsize) {
+ /* assume all block sizes are always powers of 2 */
+ /* get the bits difference */
+ tmp = osfs->os_bsize | lov_sfs->os_bsize;
+ for (shift = 0; shift <= 64; ++shift) {
+ if (tmp & 1) {
+ if (quit)
+ break;
+ else
+ quit = 1;
+ shift = 0;
+ }
+ tmp >>= 1;
+ }
+ }
+
+ if (osfs->os_bsize < lov_sfs->os_bsize) {
+ osfs->os_bsize = lov_sfs->os_bsize;
+
+ osfs->os_bfree >>= shift;
+ osfs->os_bavail >>= shift;
+ osfs->os_blocks >>= shift;
+ } else if (shift != 0) {
+ lov_sfs->os_bfree >>= shift;
+ lov_sfs->os_bavail >>= shift;
+ lov_sfs->os_blocks >>= shift;
+ }
+ osfs->os_bfree += lov_sfs->os_bfree;
+ osfs->os_bavail += lov_sfs->os_bavail;
+ osfs->os_blocks += lov_sfs->os_blocks;
+ /* XXX not sure about this one - depends on policy.
+ * - could be minimum if we always stripe on all OBDs
+ * (but that would be wrong for any other policy,
+ * if one of the OBDs has no more objects left)
+ * - could be sum if we stripe whole objects
+ * - could be average, just to give a nice number
+ *
+ * To give a "reasonable" (if not wholly accurate)
+ * number, we divide the total number of free objects
+ * by expected stripe count (watch out for overflow).
+ */
+ LOV_SUM_MAX(osfs->os_files, lov_sfs->os_files);
+ LOV_SUM_MAX(osfs->os_ffree, lov_sfs->os_ffree);
+ }
+}
+
+/* The callback for osc_statfs_async that finalizes a request info when a
+ * response is received. */
+static int cb_statfs_update(void *cookie, int rc)
+{
+ struct obd_info *oinfo = cookie;
+ struct lov_request *lovreq;
+ struct lov_request_set *set;
+ struct obd_statfs *osfs, *lov_sfs;
+ struct lov_obd *lov;
+ struct lov_tgt_desc *tgt;
+ struct obd_device *lovobd, *tgtobd;
+ int success;
+
+ lovreq = container_of(oinfo, struct lov_request, rq_oi);
+ set = lovreq->rq_rqset;
+ lovobd = set->set_obd;
+ lov = &lovobd->u.lov;
+ osfs = set->set_oi->oi_osfs;
+ lov_sfs = oinfo->oi_osfs;
+ success = atomic_read(&set->set_success);
+ /* XXX: the same is done in lov_update_common_set, however
+ lovset->set_exp is not initialized. */
+ lov_update_set(set, lovreq, rc);
+ if (rc)
+ goto out;
+
+ obd_getref(lovobd);
+ tgt = lov->lov_tgts[lovreq->rq_idx];
+ if (!tgt || !tgt->ltd_active)
+ goto out_update;
+
+ tgtobd = class_exp2obd(tgt->ltd_exp);
+ spin_lock(&tgtobd->obd_osfs_lock);
+ memcpy(&tgtobd->obd_osfs, lov_sfs, sizeof(*lov_sfs));
+ if ((oinfo->oi_flags & OBD_STATFS_FROM_CACHE) == 0)
+ tgtobd->obd_osfs_age = cfs_time_current_64();
+ spin_unlock(&tgtobd->obd_osfs_lock);
+
+out_update:
+ lov_update_statfs(osfs, lov_sfs, success);
+ obd_putref(lovobd);
+
+out:
+ if (set->set_oi->oi_flags & OBD_STATFS_PTLRPCD &&
+ lov_set_finished(set, 0)) {
+ lov_statfs_interpret(NULL, set, set->set_count !=
+ atomic_read(&set->set_success));
+ }
+
+ return 0;
+}
+
+int lov_prep_statfs_set(struct obd_device *obd, struct obd_info *oinfo,
+ struct lov_request_set **reqset)
+{
+ struct lov_request_set *set;
+ struct lov_obd *lov = &obd->u.lov;
+ int rc = 0, i;
+
+ OBD_ALLOC(set, sizeof(*set));
+ if (set == NULL)
+ return -ENOMEM;
+ lov_init_set(set);
+
+ set->set_obd = obd;
+ set->set_oi = oinfo;
+
+ /* We only get block data from the OBD */
+ for (i = 0; i < lov->desc.ld_tgt_count; i++) {
+ struct lov_request *req;
+
+ if (lov->lov_tgts[i] == NULL ||
+ (!lov_check_and_wait_active(lov, i) &&
+ (oinfo->oi_flags & OBD_STATFS_NODELAY))) {
+ CDEBUG(D_HA, "lov idx %d inactive\n", i);
+ continue;
+ }
+
+ /* skip targets that have been explicitly disabled by the
+ * administrator */
+ if (!lov->lov_tgts[i]->ltd_exp) {
+ CDEBUG(D_HA, "lov idx %d administratively disabled\n", i);
+ continue;
+ }
+
+ OBD_ALLOC(req, sizeof(*req));
+ if (req == NULL) {
+ rc = -ENOMEM;
+ goto out_set;
+ }
+
+ OBD_ALLOC(req->rq_oi.oi_osfs, sizeof(*req->rq_oi.oi_osfs));
+ if (req->rq_oi.oi_osfs == NULL) {
+ OBD_FREE(req, sizeof(*req));
+ rc = -ENOMEM;
+ goto out_set;
+ }
+
+ req->rq_idx = i;
+ req->rq_oi.oi_cb_up = cb_statfs_update;
+ req->rq_oi.oi_flags = oinfo->oi_flags;
+
+ lov_set_add_req(req, set);
+ }
+ if (!set->set_count) {
+ rc = -EIO;
+ goto out_set;
+ }
+ *reqset = set;
+ return rc;
+out_set:
+ lov_fini_statfs_set(set);
+ return rc;
+}
diff --git a/drivers/staging/lustre/lustre/lov/lovsub_dev.c b/drivers/staging/lustre/lustre/lov/lovsub_dev.c
new file mode 100644
index 000000000..42336f13a
--- /dev/null
+++ b/drivers/staging/lustre/lustre/lov/lovsub_dev.c
@@ -0,0 +1,209 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ *
+ * Implementation of cl_device and cl_device_type for LOVSUB layer.
+ *
+ * Author: Nikita Danilov <nikita.danilov@sun.com>
+ */
+
+#define DEBUG_SUBSYSTEM S_LOV
+
+#include "lov_cl_internal.h"
+
+/** \addtogroup lov
+ * @{
+ */
+
+/*****************************************************************************
+ *
+ * Lovsub transfer operations.
+ *
+ */
+
+static void lovsub_req_completion(const struct lu_env *env,
+ const struct cl_req_slice *slice, int ioret)
+{
+ struct lovsub_req *lsr;
+
+ lsr = cl2lovsub_req(slice);
+ OBD_SLAB_FREE_PTR(lsr, lovsub_req_kmem);
+}
+
+/**
+ * Implementation of struct cl_req_operations::cro_attr_set() for lovsub
+ * layer. Lov and lovsub are responsible only for struct obdo::o_stripe_idx
+ * field, which is filled there.
+ */
+static void lovsub_req_attr_set(const struct lu_env *env,
+ const struct cl_req_slice *slice,
+ const struct cl_object *obj,
+ struct cl_req_attr *attr, u64 flags)
+{
+ struct lovsub_object *subobj;
+
+ subobj = cl2lovsub(obj);
+ /*
+ * There is no OBD_MD_* flag for obdo::o_stripe_idx, so set it
+ * unconditionally. It never changes anyway.
+ */
+ attr->cra_oa->o_stripe_idx = subobj->lso_index;
+}
+
+static const struct cl_req_operations lovsub_req_ops = {
+ .cro_attr_set = lovsub_req_attr_set,
+ .cro_completion = lovsub_req_completion
+};
+
+/*****************************************************************************
+ *
+ * Lov-sub device and device type functions.
+ *
+ */
+
+static int lovsub_device_init(const struct lu_env *env, struct lu_device *d,
+ const char *name, struct lu_device *next)
+{
+ struct lovsub_device *lsd = lu2lovsub_dev(d);
+ struct lu_device_type *ldt;
+ int rc;
+
+ next->ld_site = d->ld_site;
+ ldt = next->ld_type;
+ LASSERT(ldt != NULL);
+ rc = ldt->ldt_ops->ldto_device_init(env, next, ldt->ldt_name, NULL);
+ if (rc) {
+ next->ld_site = NULL;
+ return rc;
+ }
+
+ lu_device_get(next);
+ lu_ref_add(&next->ld_reference, "lu-stack", &lu_site_init);
+ lsd->acid_next = lu2cl_dev(next);
+ return rc;
+}
+
+static struct lu_device *lovsub_device_fini(const struct lu_env *env,
+ struct lu_device *d)
+{
+ struct lu_device *next;
+ struct lovsub_device *lsd;
+
+ lsd = lu2lovsub_dev(d);
+ next = cl2lu_dev(lsd->acid_next);
+ lsd->acid_super = NULL;
+ lsd->acid_next = NULL;
+ return next;
+}
+
+static struct lu_device *lovsub_device_free(const struct lu_env *env,
+ struct lu_device *d)
+{
+ struct lovsub_device *lsd = lu2lovsub_dev(d);
+ struct lu_device *next = cl2lu_dev(lsd->acid_next);
+
+ if (atomic_read(&d->ld_ref) && d->ld_site) {
+ LIBCFS_DEBUG_MSG_DATA_DECL(msgdata, D_ERROR, NULL);
+ lu_site_print(env, d->ld_site, &msgdata, lu_cdebug_printer);
+ }
+ cl_device_fini(lu2cl_dev(d));
+ OBD_FREE_PTR(lsd);
+ return next;
+}
+
+static int lovsub_req_init(const struct lu_env *env, struct cl_device *dev,
+ struct cl_req *req)
+{
+ struct lovsub_req *lsr;
+ int result;
+
+ OBD_SLAB_ALLOC_PTR_GFP(lsr, lovsub_req_kmem, GFP_NOFS);
+ if (lsr != NULL) {
+ cl_req_slice_add(req, &lsr->lsrq_cl, dev, &lovsub_req_ops);
+ result = 0;
+ } else
+ result = -ENOMEM;
+ return result;
+}
+
+static const struct lu_device_operations lovsub_lu_ops = {
+ .ldo_object_alloc = lovsub_object_alloc,
+ .ldo_process_config = NULL,
+ .ldo_recovery_complete = NULL
+};
+
+static const struct cl_device_operations lovsub_cl_ops = {
+ .cdo_req_init = lovsub_req_init
+};
+
+static struct lu_device *lovsub_device_alloc(const struct lu_env *env,
+ struct lu_device_type *t,
+ struct lustre_cfg *cfg)
+{
+ struct lu_device *d;
+ struct lovsub_device *lsd;
+
+ OBD_ALLOC_PTR(lsd);
+ if (lsd != NULL) {
+ int result;
+
+ result = cl_device_init(&lsd->acid_cl, t);
+ if (result == 0) {
+ d = lovsub2lu_dev(lsd);
+ d->ld_ops = &lovsub_lu_ops;
+ lsd->acid_cl.cd_ops = &lovsub_cl_ops;
+ } else
+ d = ERR_PTR(result);
+ } else
+ d = ERR_PTR(-ENOMEM);
+ return d;
+}
+
+static const struct lu_device_type_operations lovsub_device_type_ops = {
+ .ldto_device_alloc = lovsub_device_alloc,
+ .ldto_device_free = lovsub_device_free,
+
+ .ldto_device_init = lovsub_device_init,
+ .ldto_device_fini = lovsub_device_fini
+};
+
+#define LUSTRE_LOVSUB_NAME "lovsub"
+
+struct lu_device_type lovsub_device_type = {
+ .ldt_tags = LU_DEVICE_CL,
+ .ldt_name = LUSTRE_LOVSUB_NAME,
+ .ldt_ops = &lovsub_device_type_ops,
+ .ldt_ctx_tags = LCT_CL_THREAD
+};
+
+
+/** @} lov */
diff --git a/drivers/staging/lustre/lustre/lov/lovsub_io.c b/drivers/staging/lustre/lustre/lov/lovsub_io.c
new file mode 100644
index 000000000..783ec687a
--- /dev/null
+++ b/drivers/staging/lustre/lustre/lov/lovsub_io.c
@@ -0,0 +1,55 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2002, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ *
+ * Implementation of cl_io for LOVSUB layer.
+ *
+ * Author: Nikita Danilov <nikita.danilov@sun.com>
+ */
+
+#define DEBUG_SUBSYSTEM S_LOV
+
+#include "lov_cl_internal.h"
+
+/** \addtogroup lov
+ * @{
+ */
+
+/*****************************************************************************
+ *
+ * Lovsub io operations.
+ *
+ */
+
+/* All trivial */
+
+/** @} lov */
diff --git a/drivers/staging/lustre/lustre/lov/lovsub_lock.c b/drivers/staging/lustre/lustre/lov/lovsub_lock.c
new file mode 100644
index 000000000..62b696d25
--- /dev/null
+++ b/drivers/staging/lustre/lustre/lov/lovsub_lock.c
@@ -0,0 +1,466 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2011, 2012, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ *
+ * Implementation of cl_lock for LOVSUB layer.
+ *
+ * Author: Nikita Danilov <nikita.danilov@sun.com>
+ */
+
+#define DEBUG_SUBSYSTEM S_LOV
+
+#include "lov_cl_internal.h"
+
+/** \addtogroup lov
+ * @{
+ */
+
+/*****************************************************************************
+ *
+ * Lovsub lock operations.
+ *
+ */
+
+static void lovsub_lock_fini(const struct lu_env *env,
+ struct cl_lock_slice *slice)
+{
+ struct lovsub_lock *lsl;
+
+ lsl = cl2lovsub_lock(slice);
+ LASSERT(list_empty(&lsl->lss_parents));
+ OBD_SLAB_FREE_PTR(lsl, lovsub_lock_kmem);
+}
+
+static void lovsub_parent_lock(const struct lu_env *env, struct lov_lock *lov)
+{
+ struct cl_lock *parent;
+
+ parent = lov->lls_cl.cls_lock;
+ cl_lock_get(parent);
+ lu_ref_add(&parent->cll_reference, "lovsub-parent", current);
+ cl_lock_mutex_get(env, parent);
+}
+
+static void lovsub_parent_unlock(const struct lu_env *env, struct lov_lock *lov)
+{
+ struct cl_lock *parent;
+
+ parent = lov->lls_cl.cls_lock;
+ cl_lock_mutex_put(env, lov->lls_cl.cls_lock);
+ lu_ref_del(&parent->cll_reference, "lovsub-parent", current);
+ cl_lock_put(env, parent);
+}
+
+/**
+ * Implements cl_lock_operations::clo_state() method for lovsub layer, which
+ * method is called whenever sub-lock state changes. Propagates state change
+ * to the top-locks.
+ */
+static void lovsub_lock_state(const struct lu_env *env,
+ const struct cl_lock_slice *slice,
+ enum cl_lock_state state)
+{
+ struct lovsub_lock *sub = cl2lovsub_lock(slice);
+ struct lov_lock_link *scan;
+
+ LASSERT(cl_lock_is_mutexed(slice->cls_lock));
+
+ list_for_each_entry(scan, &sub->lss_parents, lll_list) {
+ struct lov_lock *lov = scan->lll_super;
+ struct cl_lock *parent = lov->lls_cl.cls_lock;
+
+ if (sub->lss_active != parent) {
+ lovsub_parent_lock(env, lov);
+ cl_lock_signal(env, parent);
+ lovsub_parent_unlock(env, lov);
+ }
+ }
+}
+
+/**
+ * Implementation of cl_lock_operation::clo_weigh() estimating lock weight by
+ * asking parent lock.
+ */
+static unsigned long lovsub_lock_weigh(const struct lu_env *env,
+ const struct cl_lock_slice *slice)
+{
+ struct lovsub_lock *lock = cl2lovsub_lock(slice);
+ struct lov_lock *lov;
+ unsigned long dumbbell;
+
+ LASSERT(cl_lock_is_mutexed(slice->cls_lock));
+
+ if (!list_empty(&lock->lss_parents)) {
+ /*
+ * It is not clear whether all parents have to be asked and
+ * their estimations summed, or it is enough to ask one. For
+ * the current usages, one is always enough.
+ */
+ lov = container_of(lock->lss_parents.next,
+ struct lov_lock_link, lll_list)->lll_super;
+
+ lovsub_parent_lock(env, lov);
+ dumbbell = cl_lock_weigh(env, lov->lls_cl.cls_lock);
+ lovsub_parent_unlock(env, lov);
+ } else
+ dumbbell = 0;
+
+ return dumbbell;
+}
+
+/**
+ * Maps start/end offsets within a stripe, to offsets within a file.
+ */
+static void lovsub_lock_descr_map(const struct cl_lock_descr *in,
+ struct lov_object *lov,
+ int stripe, struct cl_lock_descr *out)
+{
+ pgoff_t size; /* stripe size in pages */
+ pgoff_t skip; /* how many pages in every stripe are occupied by
+ * "other" stripes */
+ pgoff_t start;
+ pgoff_t end;
+
+ start = in->cld_start;
+ end = in->cld_end;
+
+ if (lov->lo_lsm->lsm_stripe_count > 1) {
+ size = cl_index(lov2cl(lov), lov->lo_lsm->lsm_stripe_size);
+ skip = (lov->lo_lsm->lsm_stripe_count - 1) * size;
+
+ /* XXX overflow check here? */
+ start += start/size * skip + stripe * size;
+
+ if (end != CL_PAGE_EOF) {
+ end += end/size * skip + stripe * size;
+ /*
+ * And check for overflow...
+ */
+ if (end < in->cld_end)
+ end = CL_PAGE_EOF;
+ }
+ }
+ out->cld_start = start;
+ out->cld_end = end;
+}
+
+/**
+ * Adjusts parent lock extent when a sub-lock is attached to a parent. This is
+ * called in two ways:
+ *
+ * - as part of receive call-back, when server returns granted extent to
+ * the client, and
+ *
+ * - when top-lock finds existing sub-lock in the cache.
+ *
+ * Note, that lock mode is not propagated to the parent: i.e., if CLM_READ
+ * top-lock matches CLM_WRITE sub-lock, top-lock is still CLM_READ.
+ */
+int lov_sublock_modify(const struct lu_env *env, struct lov_lock *lov,
+ struct lovsub_lock *sublock,
+ const struct cl_lock_descr *d, int idx)
+{
+ struct cl_lock *parent;
+ struct lovsub_object *subobj;
+ struct cl_lock_descr *pd;
+ struct cl_lock_descr *parent_descr;
+ int result;
+
+ parent = lov->lls_cl.cls_lock;
+ parent_descr = &parent->cll_descr;
+ LASSERT(cl_lock_mode_match(d->cld_mode, parent_descr->cld_mode));
+
+ subobj = cl2lovsub(sublock->lss_cl.cls_obj);
+ pd = &lov_env_info(env)->lti_ldescr;
+
+ pd->cld_obj = parent_descr->cld_obj;
+ pd->cld_mode = parent_descr->cld_mode;
+ pd->cld_gid = parent_descr->cld_gid;
+ lovsub_lock_descr_map(d, subobj->lso_super, subobj->lso_index, pd);
+ lov->lls_sub[idx].sub_got = *d;
+ /*
+ * Notify top-lock about modification, if lock description changes
+ * materially.
+ */
+ if (!cl_lock_ext_match(parent_descr, pd))
+ result = cl_lock_modify(env, parent, pd);
+ else
+ result = 0;
+ return result;
+}
+
+static int lovsub_lock_modify(const struct lu_env *env,
+ const struct cl_lock_slice *s,
+ const struct cl_lock_descr *d)
+{
+ struct lovsub_lock *lock = cl2lovsub_lock(s);
+ struct lov_lock_link *scan;
+ struct lov_lock *lov;
+ int result = 0;
+
+ LASSERT(cl_lock_mode_match(d->cld_mode,
+ s->cls_lock->cll_descr.cld_mode));
+ list_for_each_entry(scan, &lock->lss_parents, lll_list) {
+ int rc;
+
+ lov = scan->lll_super;
+ lovsub_parent_lock(env, lov);
+ rc = lov_sublock_modify(env, lov, lock, d, scan->lll_idx);
+ lovsub_parent_unlock(env, lov);
+ result = result ?: rc;
+ }
+ return result;
+}
+
+static int lovsub_lock_closure(const struct lu_env *env,
+ const struct cl_lock_slice *slice,
+ struct cl_lock_closure *closure)
+{
+ struct lovsub_lock *sub;
+ struct cl_lock *parent;
+ struct lov_lock_link *scan;
+ int result;
+
+ LASSERT(cl_lock_is_mutexed(slice->cls_lock));
+
+ sub = cl2lovsub_lock(slice);
+ result = 0;
+
+ list_for_each_entry(scan, &sub->lss_parents, lll_list) {
+ parent = scan->lll_super->lls_cl.cls_lock;
+ result = cl_lock_closure_build(env, parent, closure);
+ if (result != 0)
+ break;
+ }
+ return result;
+}
+
+/**
+ * A helper function for lovsub_lock_delete() that deals with a given parent
+ * top-lock.
+ */
+static int lovsub_lock_delete_one(const struct lu_env *env,
+ struct cl_lock *child, struct lov_lock *lov)
+{
+ struct cl_lock *parent;
+ int result;
+
+ parent = lov->lls_cl.cls_lock;
+ if (parent->cll_error)
+ return 0;
+
+ result = 0;
+ switch (parent->cll_state) {
+ case CLS_ENQUEUED:
+ /* See LU-1355 for the case that a glimpse lock is
+ * interrupted by signal */
+ LASSERT(parent->cll_flags & CLF_CANCELLED);
+ break;
+ case CLS_QUEUING:
+ case CLS_FREEING:
+ cl_lock_signal(env, parent);
+ break;
+ case CLS_INTRANSIT:
+ /*
+ * Here lies a problem: a sub-lock is canceled while top-lock
+ * is being unlocked. Top-lock cannot be moved into CLS_NEW
+ * state, because unlocking has to succeed eventually by
+ * placing lock into CLS_CACHED (or failing it), see
+ * cl_unuse_try(). Nor can top-lock be left in CLS_CACHED
+ * state, because lov maintains an invariant that all
+ * sub-locks exist in CLS_CACHED (this allows cached top-lock
+ * to be reused immediately). Nor can we wait for top-lock
+ * state to change, because this can be synchronous to the
+ * current thread.
+ *
+ * We know for sure that lov_lock_unuse() will be called at
+ * least one more time to finish un-using, so leave a mark on
+ * the top-lock, that will be seen by the next call to
+ * lov_lock_unuse().
+ */
+ if (cl_lock_is_intransit(parent))
+ lov->lls_cancel_race = 1;
+ break;
+ case CLS_CACHED:
+ /*
+ * if a sub-lock is canceled move its top-lock into CLS_NEW
+ * state to preserve an invariant that a top-lock in
+ * CLS_CACHED is immediately ready for re-use (i.e., has all
+ * sub-locks), and so that next attempt to re-use the top-lock
+ * enqueues missing sub-lock.
+ */
+ cl_lock_state_set(env, parent, CLS_NEW);
+ /* fall through */
+ case CLS_NEW:
+ /*
+ * if last sub-lock is canceled, destroy the top-lock (which
+ * is now `empty') proactively.
+ */
+ if (lov->lls_nr_filled == 0) {
+ /* ... but unfortunately, this cannot be done easily,
+ * as cancellation of a top-lock might acquire mutices
+ * of its other sub-locks, violating lock ordering,
+ * see cl_lock_{cancel,delete}() preconditions.
+ *
+ * To work around this, the mutex of this sub-lock is
+ * released, top-lock is destroyed, and sub-lock mutex
+ * acquired again. The list of parents has to be
+ * re-scanned from the beginning after this.
+ *
+ * Only do this if no mutices other than on @child and
+ * @parent are held by the current thread.
+ *
+ * TODO: The lock modal here is too complex, because
+ * the lock may be canceled and deleted by voluntarily:
+ * cl_lock_request
+ * -> osc_lock_enqueue_wait
+ * -> osc_lock_cancel_wait
+ * -> cl_lock_delete
+ * -> lovsub_lock_delete
+ * -> cl_lock_cancel/delete
+ * -> ...
+ *
+ * The better choice is to spawn a kernel thread for
+ * this purpose. -jay
+ */
+ if (cl_lock_nr_mutexed(env) == 2) {
+ cl_lock_mutex_put(env, child);
+ cl_lock_cancel(env, parent);
+ cl_lock_delete(env, parent);
+ result = 1;
+ }
+ }
+ break;
+ case CLS_HELD:
+ CL_LOCK_DEBUG(D_ERROR, env, parent, "Delete CLS_HELD lock\n");
+ default:
+ CERROR("Impossible state: %d\n", parent->cll_state);
+ LBUG();
+ break;
+ }
+
+ return result;
+}
+
+/**
+ * An implementation of cl_lock_operations::clo_delete() method. This is
+ * invoked in "bottom-to-top" delete, when lock destruction starts from the
+ * sub-lock (e.g, as a result of ldlm lock LRU policy).
+ */
+static void lovsub_lock_delete(const struct lu_env *env,
+ const struct cl_lock_slice *slice)
+{
+ struct cl_lock *child = slice->cls_lock;
+ struct lovsub_lock *sub = cl2lovsub_lock(slice);
+ int restart;
+
+ LASSERT(cl_lock_is_mutexed(child));
+
+ /*
+ * Destruction of a sub-lock might take multiple iterations, because
+ * when the last sub-lock of a given top-lock is deleted, top-lock is
+ * canceled proactively, and this requires to release sub-lock
+ * mutex. Once sub-lock mutex has been released, list of its parents
+ * has to be re-scanned from the beginning.
+ */
+ do {
+ struct lov_lock *lov;
+ struct lov_lock_link *scan;
+ struct lov_lock_link *temp;
+ struct lov_lock_sub *subdata;
+
+ restart = 0;
+ list_for_each_entry_safe(scan, temp,
+ &sub->lss_parents, lll_list) {
+ lov = scan->lll_super;
+ subdata = &lov->lls_sub[scan->lll_idx];
+ lovsub_parent_lock(env, lov);
+ subdata->sub_got = subdata->sub_descr;
+ lov_lock_unlink(env, scan, sub);
+ restart = lovsub_lock_delete_one(env, child, lov);
+ lovsub_parent_unlock(env, lov);
+
+ if (restart) {
+ cl_lock_mutex_get(env, child);
+ break;
+ }
+ }
+ } while (restart);
+}
+
+static int lovsub_lock_print(const struct lu_env *env, void *cookie,
+ lu_printer_t p, const struct cl_lock_slice *slice)
+{
+ struct lovsub_lock *sub = cl2lovsub_lock(slice);
+ struct lov_lock *lov;
+ struct lov_lock_link *scan;
+
+ list_for_each_entry(scan, &sub->lss_parents, lll_list) {
+ lov = scan->lll_super;
+ (*p)(env, cookie, "[%d %p ", scan->lll_idx, lov);
+ if (lov != NULL)
+ cl_lock_descr_print(env, cookie, p,
+ &lov->lls_cl.cls_lock->cll_descr);
+ (*p)(env, cookie, "] ");
+ }
+ return 0;
+}
+
+static const struct cl_lock_operations lovsub_lock_ops = {
+ .clo_fini = lovsub_lock_fini,
+ .clo_state = lovsub_lock_state,
+ .clo_delete = lovsub_lock_delete,
+ .clo_modify = lovsub_lock_modify,
+ .clo_closure = lovsub_lock_closure,
+ .clo_weigh = lovsub_lock_weigh,
+ .clo_print = lovsub_lock_print
+};
+
+int lovsub_lock_init(const struct lu_env *env, struct cl_object *obj,
+ struct cl_lock *lock, const struct cl_io *io)
+{
+ struct lovsub_lock *lsk;
+ int result;
+
+ OBD_SLAB_ALLOC_PTR_GFP(lsk, lovsub_lock_kmem, GFP_NOFS);
+ if (lsk != NULL) {
+ INIT_LIST_HEAD(&lsk->lss_parents);
+ cl_lock_slice_add(lock, &lsk->lss_cl, obj, &lovsub_lock_ops);
+ result = 0;
+ } else
+ result = -ENOMEM;
+ return result;
+}
+
+/** @} lov */
diff --git a/drivers/staging/lustre/lustre/lov/lovsub_object.c b/drivers/staging/lustre/lustre/lov/lovsub_object.c
new file mode 100644
index 000000000..57e3629fc
--- /dev/null
+++ b/drivers/staging/lustre/lustre/lov/lovsub_object.c
@@ -0,0 +1,164 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2012, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ *
+ * Implementation of cl_object for LOVSUB layer.
+ *
+ * Author: Nikita Danilov <nikita.danilov@sun.com>
+ */
+
+#define DEBUG_SUBSYSTEM S_LOV
+
+#include "lov_cl_internal.h"
+
+/** \addtogroup lov
+ * @{
+ */
+
+/*****************************************************************************
+ *
+ * Lovsub object operations.
+ *
+ */
+
+int lovsub_object_init(const struct lu_env *env, struct lu_object *obj,
+ const struct lu_object_conf *conf)
+{
+ struct lovsub_device *dev = lu2lovsub_dev(obj->lo_dev);
+ struct lu_object *below;
+ struct lu_device *under;
+
+ int result;
+
+ under = &dev->acid_next->cd_lu_dev;
+ below = under->ld_ops->ldo_object_alloc(env, obj->lo_header, under);
+ if (below != NULL) {
+ lu_object_add(obj, below);
+ cl_object_page_init(lu2cl(obj), sizeof(struct lovsub_page));
+ result = 0;
+ } else
+ result = -ENOMEM;
+ return result;
+
+}
+
+static void lovsub_object_free(const struct lu_env *env, struct lu_object *obj)
+{
+ struct lovsub_object *los = lu2lovsub(obj);
+ struct lov_object *lov = los->lso_super;
+
+ /* We can't assume lov was assigned here, because of the shadow
+ * object handling in lu_object_find.
+ */
+ if (lov) {
+ LASSERT(lov->lo_type == LLT_RAID0);
+ LASSERT(lov->u.raid0.lo_sub[los->lso_index] == los);
+ spin_lock(&lov->u.raid0.lo_sub_lock);
+ lov->u.raid0.lo_sub[los->lso_index] = NULL;
+ spin_unlock(&lov->u.raid0.lo_sub_lock);
+ }
+
+ lu_object_fini(obj);
+ lu_object_header_fini(&los->lso_header.coh_lu);
+ OBD_SLAB_FREE_PTR(los, lovsub_object_kmem);
+}
+
+static int lovsub_object_print(const struct lu_env *env, void *cookie,
+ lu_printer_t p, const struct lu_object *obj)
+{
+ struct lovsub_object *los = lu2lovsub(obj);
+
+ return (*p)(env, cookie, "[%d]", los->lso_index);
+}
+
+static int lovsub_attr_set(const struct lu_env *env, struct cl_object *obj,
+ const struct cl_attr *attr, unsigned valid)
+{
+ struct lov_object *lov = cl2lovsub(obj)->lso_super;
+
+ lov_r0(lov)->lo_attr_valid = 0;
+ return 0;
+}
+
+static int lovsub_object_glimpse(const struct lu_env *env,
+ const struct cl_object *obj,
+ struct ost_lvb *lvb)
+{
+ struct lovsub_object *los = cl2lovsub(obj);
+
+ return cl_object_glimpse(env, &los->lso_super->lo_cl, lvb);
+}
+
+
+
+static const struct cl_object_operations lovsub_ops = {
+ .coo_page_init = lovsub_page_init,
+ .coo_lock_init = lovsub_lock_init,
+ .coo_attr_set = lovsub_attr_set,
+ .coo_glimpse = lovsub_object_glimpse
+};
+
+static const struct lu_object_operations lovsub_lu_obj_ops = {
+ .loo_object_init = lovsub_object_init,
+ .loo_object_delete = NULL,
+ .loo_object_release = NULL,
+ .loo_object_free = lovsub_object_free,
+ .loo_object_print = lovsub_object_print,
+ .loo_object_invariant = NULL
+};
+
+struct lu_object *lovsub_object_alloc(const struct lu_env *env,
+ const struct lu_object_header *unused,
+ struct lu_device *dev)
+{
+ struct lovsub_object *los;
+ struct lu_object *obj;
+
+ OBD_SLAB_ALLOC_PTR_GFP(los, lovsub_object_kmem, GFP_NOFS);
+ if (los != NULL) {
+ struct cl_object_header *hdr;
+
+ obj = lovsub2lu(los);
+ hdr = &los->lso_header;
+ cl_object_header_init(hdr);
+ lu_object_init(obj, &hdr->coh_lu, dev);
+ lu_object_add_top(&hdr->coh_lu, obj);
+ los->lso_cl.co_ops = &lovsub_ops;
+ obj->lo_ops = &lovsub_lu_obj_ops;
+ } else
+ obj = NULL;
+ return obj;
+}
+
+/** @} lov */
diff --git a/drivers/staging/lustre/lustre/lov/lovsub_page.c b/drivers/staging/lustre/lustre/lov/lovsub_page.c
new file mode 100644
index 000000000..3f00ce967
--- /dev/null
+++ b/drivers/staging/lustre/lustre/lov/lovsub_page.c
@@ -0,0 +1,71 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2002, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ *
+ * Implementation of cl_page for LOVSUB layer.
+ *
+ * Author: Nikita Danilov <nikita.danilov@sun.com>
+ */
+
+#define DEBUG_SUBSYSTEM S_LOV
+
+#include "lov_cl_internal.h"
+
+/** \addtogroup lov
+ * @{
+ */
+
+/*****************************************************************************
+ *
+ * Lovsub page operations.
+ *
+ */
+
+static void lovsub_page_fini(const struct lu_env *env,
+ struct cl_page_slice *slice)
+{
+}
+
+static const struct cl_page_operations lovsub_page_ops = {
+ .cpo_fini = lovsub_page_fini
+};
+
+int lovsub_page_init(const struct lu_env *env, struct cl_object *obj,
+ struct cl_page *page, struct page *unused)
+{
+ struct lovsub_page *lsb = cl_object_page_slice(obj, page);
+
+ cl_page_slice_add(page, &lsb->lsb_cl, obj, &lovsub_page_ops);
+ return 0;
+}
+
+/** @} lov */
diff --git a/drivers/staging/lustre/lustre/lov/lproc_lov.c b/drivers/staging/lustre/lustre/lov/lproc_lov.c
new file mode 100644
index 000000000..174cbf5c1
--- /dev/null
+++ b/drivers/staging/lustre/lustre/lov/lproc_lov.c
@@ -0,0 +1,311 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2002, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2012, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ */
+#define DEBUG_SUBSYSTEM S_CLASS
+
+#include <linux/statfs.h>
+#include "../include/lprocfs_status.h"
+#include "../include/obd_class.h"
+#include <linux/seq_file.h>
+#include "lov_internal.h"
+
+static int lov_stripesize_seq_show(struct seq_file *m, void *v)
+{
+ struct obd_device *dev = (struct obd_device *)m->private;
+ struct lov_desc *desc;
+
+ LASSERT(dev != NULL);
+ desc = &dev->u.lov.desc;
+ seq_printf(m, "%llu\n", desc->ld_default_stripe_size);
+ return 0;
+}
+
+static ssize_t lov_stripesize_seq_write(struct file *file,
+ const char __user *buffer,
+ size_t count, loff_t *off)
+{
+ struct obd_device *dev = ((struct seq_file *)file->private_data)->private;
+ struct lov_desc *desc;
+ __u64 val;
+ int rc;
+
+ LASSERT(dev != NULL);
+ desc = &dev->u.lov.desc;
+ rc = lprocfs_write_u64_helper(buffer, count, &val);
+ if (rc)
+ return rc;
+
+ lov_fix_desc_stripe_size(&val);
+ desc->ld_default_stripe_size = val;
+ return count;
+}
+LPROC_SEQ_FOPS(lov_stripesize);
+
+static int lov_stripeoffset_seq_show(struct seq_file *m, void *v)
+{
+ struct obd_device *dev = (struct obd_device *)m->private;
+ struct lov_desc *desc;
+
+ LASSERT(dev != NULL);
+ desc = &dev->u.lov.desc;
+ seq_printf(m, "%llu\n", desc->ld_default_stripe_offset);
+ return 0;
+}
+
+static ssize_t lov_stripeoffset_seq_write(struct file *file,
+ const char __user *buffer,
+ size_t count, loff_t *off)
+{
+ struct obd_device *dev = ((struct seq_file *)file->private_data)->private;
+ struct lov_desc *desc;
+ __u64 val;
+ int rc;
+
+ LASSERT(dev != NULL);
+ desc = &dev->u.lov.desc;
+ rc = lprocfs_write_u64_helper(buffer, count, &val);
+ if (rc)
+ return rc;
+
+ desc->ld_default_stripe_offset = val;
+ return count;
+}
+LPROC_SEQ_FOPS(lov_stripeoffset);
+
+static int lov_stripetype_seq_show(struct seq_file *m, void *v)
+{
+ struct obd_device *dev = (struct obd_device *)m->private;
+ struct lov_desc *desc;
+
+ LASSERT(dev != NULL);
+ desc = &dev->u.lov.desc;
+ seq_printf(m, "%u\n", desc->ld_pattern);
+ return 0;
+}
+
+static ssize_t lov_stripetype_seq_write(struct file *file,
+ const char __user *buffer,
+ size_t count, loff_t *off)
+{
+ struct obd_device *dev = ((struct seq_file *)file->private_data)->private;
+ struct lov_desc *desc;
+ int val, rc;
+
+ LASSERT(dev != NULL);
+ desc = &dev->u.lov.desc;
+ rc = lprocfs_write_helper(buffer, count, &val);
+ if (rc)
+ return rc;
+
+ lov_fix_desc_pattern(&val);
+ desc->ld_pattern = val;
+ return count;
+}
+LPROC_SEQ_FOPS(lov_stripetype);
+
+static int lov_stripecount_seq_show(struct seq_file *m, void *v)
+{
+ struct obd_device *dev = (struct obd_device *)m->private;
+ struct lov_desc *desc;
+
+ LASSERT(dev != NULL);
+ desc = &dev->u.lov.desc;
+ seq_printf(m, "%d\n", (__s16)(desc->ld_default_stripe_count + 1) - 1);
+ return 0;
+}
+
+static ssize_t lov_stripecount_seq_write(struct file *file,
+ const char __user *buffer,
+ size_t count, loff_t *off)
+{
+ struct obd_device *dev = ((struct seq_file *)file->private_data)->private;
+ struct lov_desc *desc;
+ int val, rc;
+
+ LASSERT(dev != NULL);
+ desc = &dev->u.lov.desc;
+ rc = lprocfs_write_helper(buffer, count, &val);
+ if (rc)
+ return rc;
+
+ lov_fix_desc_stripe_count(&val);
+ desc->ld_default_stripe_count = val;
+ return count;
+}
+LPROC_SEQ_FOPS(lov_stripecount);
+
+static int lov_numobd_seq_show(struct seq_file *m, void *v)
+{
+ struct obd_device *dev = (struct obd_device *)m->private;
+ struct lov_desc *desc;
+
+ LASSERT(dev != NULL);
+ desc = &dev->u.lov.desc;
+ seq_printf(m, "%u\n", desc->ld_tgt_count);
+ return 0;
+}
+LPROC_SEQ_FOPS_RO(lov_numobd);
+
+static int lov_activeobd_seq_show(struct seq_file *m, void *v)
+{
+ struct obd_device *dev = (struct obd_device *)m->private;
+ struct lov_desc *desc;
+
+ LASSERT(dev != NULL);
+ desc = &dev->u.lov.desc;
+ seq_printf(m, "%u\n", desc->ld_active_tgt_count);
+ return 0;
+}
+LPROC_SEQ_FOPS_RO(lov_activeobd);
+
+static int lov_desc_uuid_seq_show(struct seq_file *m, void *v)
+{
+ struct obd_device *dev = (struct obd_device *)m->private;
+ struct lov_obd *lov;
+
+ LASSERT(dev != NULL);
+ lov = &dev->u.lov;
+ seq_printf(m, "%s\n", lov->desc.ld_uuid.uuid);
+ return 0;
+}
+LPROC_SEQ_FOPS_RO(lov_desc_uuid);
+
+static void *lov_tgt_seq_start(struct seq_file *p, loff_t *pos)
+{
+ struct obd_device *dev = p->private;
+ struct lov_obd *lov = &dev->u.lov;
+
+ while (*pos < lov->desc.ld_tgt_count) {
+ if (lov->lov_tgts[*pos])
+ return lov->lov_tgts[*pos];
+ ++*pos;
+ }
+ return NULL;
+}
+
+static void lov_tgt_seq_stop(struct seq_file *p, void *v)
+{
+}
+
+static void *lov_tgt_seq_next(struct seq_file *p, void *v, loff_t *pos)
+{
+ struct obd_device *dev = p->private;
+ struct lov_obd *lov = &dev->u.lov;
+
+ while (++*pos < lov->desc.ld_tgt_count) {
+ if (lov->lov_tgts[*pos])
+ return lov->lov_tgts[*pos];
+ }
+ return NULL;
+}
+
+static int lov_tgt_seq_show(struct seq_file *p, void *v)
+{
+ struct lov_tgt_desc *tgt = v;
+
+ seq_printf(p, "%d: %s %sACTIVE\n",
+ tgt->ltd_index, obd_uuid2str(&tgt->ltd_uuid),
+ tgt->ltd_active ? "" : "IN");
+ return 0;
+}
+
+static const struct seq_operations lov_tgt_sops = {
+ .start = lov_tgt_seq_start,
+ .stop = lov_tgt_seq_stop,
+ .next = lov_tgt_seq_next,
+ .show = lov_tgt_seq_show,
+};
+
+static int lov_target_seq_open(struct inode *inode, struct file *file)
+{
+ struct seq_file *seq;
+ int rc;
+
+ rc = seq_open(file, &lov_tgt_sops);
+ if (rc)
+ return rc;
+
+ seq = file->private_data;
+ seq->private = PDE_DATA(inode);
+ return 0;
+}
+
+LPROC_SEQ_FOPS_RO_TYPE(lov, uuid);
+LPROC_SEQ_FOPS_RO_TYPE(lov, filestotal);
+LPROC_SEQ_FOPS_RO_TYPE(lov, filesfree);
+LPROC_SEQ_FOPS_RO_TYPE(lov, blksize);
+LPROC_SEQ_FOPS_RO_TYPE(lov, kbytestotal);
+LPROC_SEQ_FOPS_RO_TYPE(lov, kbytesfree);
+LPROC_SEQ_FOPS_RO_TYPE(lov, kbytesavail);
+
+static struct lprocfs_vars lprocfs_lov_obd_vars[] = {
+ { "uuid", &lov_uuid_fops, NULL, 0 },
+ { "stripesize", &lov_stripesize_fops, NULL },
+ { "stripeoffset", &lov_stripeoffset_fops, NULL },
+ { "stripecount", &lov_stripecount_fops, NULL },
+ { "stripetype", &lov_stripetype_fops, NULL },
+ { "numobd", &lov_numobd_fops, NULL, 0 },
+ { "activeobd", &lov_activeobd_fops, NULL, 0 },
+ { "filestotal", &lov_filestotal_fops, NULL, 0 },
+ { "filesfree", &lov_filesfree_fops, NULL, 0 },
+ /*{ "filegroups", lprocfs_rd_filegroups, NULL, 0 },*/
+ { "blocksize", &lov_blksize_fops, NULL, 0 },
+ { "kbytestotal", &lov_kbytestotal_fops, NULL, 0 },
+ { "kbytesfree", &lov_kbytesfree_fops, NULL, 0 },
+ { "kbytesavail", &lov_kbytesavail_fops, NULL, 0 },
+ { "desc_uuid", &lov_desc_uuid_fops, NULL, 0 },
+ { NULL }
+};
+
+LPROC_SEQ_FOPS_RO_TYPE(lov, numrefs);
+
+static struct lprocfs_vars lprocfs_lov_module_vars[] = {
+ { "num_refs", &lov_numrefs_fops, NULL, 0 },
+ { NULL }
+};
+
+void lprocfs_lov_init_vars(struct lprocfs_static_vars *lvars)
+{
+ lvars->module_vars = lprocfs_lov_module_vars;
+ lvars->obd_vars = lprocfs_lov_obd_vars;
+}
+
+const struct file_operations lov_proc_target_fops = {
+ .owner = THIS_MODULE,
+ .open = lov_target_seq_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = lprocfs_seq_release,
+};
diff --git a/drivers/staging/lustre/lustre/mdc/Makefile b/drivers/staging/lustre/lustre/mdc/Makefile
new file mode 100644
index 000000000..2516551a6
--- /dev/null
+++ b/drivers/staging/lustre/lustre/mdc/Makefile
@@ -0,0 +1,3 @@
+obj-$(CONFIG_LUSTRE_FS) += mdc.o
+mdc-y := mdc_request.o mdc_reint.o mdc_lib.o mdc_locks.o
+mdc-$(CONFIG_PROC_FS) += lproc_mdc.o
diff --git a/drivers/staging/lustre/lustre/mdc/lproc_mdc.c b/drivers/staging/lustre/lustre/mdc/lproc_mdc.c
new file mode 100644
index 000000000..acfe08e45
--- /dev/null
+++ b/drivers/staging/lustre/lustre/mdc/lproc_mdc.c
@@ -0,0 +1,220 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2002, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2011, 2012, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ */
+#define DEBUG_SUBSYSTEM S_CLASS
+
+#include <linux/vfs.h>
+#include "../include/obd_class.h"
+#include "../include/lprocfs_status.h"
+#include "mdc_internal.h"
+
+static int mdc_max_rpcs_in_flight_seq_show(struct seq_file *m, void *v)
+{
+ struct obd_device *dev = m->private;
+ struct client_obd *cli = &dev->u.cli;
+
+ client_obd_list_lock(&cli->cl_loi_list_lock);
+ seq_printf(m, "%u\n", cli->cl_max_rpcs_in_flight);
+ client_obd_list_unlock(&cli->cl_loi_list_lock);
+
+ return 0;
+}
+
+static ssize_t mdc_max_rpcs_in_flight_seq_write(struct file *file,
+ const char __user *buffer,
+ size_t count,
+ loff_t *off)
+{
+ struct obd_device *dev =
+ ((struct seq_file *)file->private_data)->private;
+ struct client_obd *cli = &dev->u.cli;
+ int val, rc;
+
+ rc = lprocfs_write_helper(buffer, count, &val);
+ if (rc)
+ return rc;
+
+ if (val < 1 || val > MDC_MAX_RIF_MAX)
+ return -ERANGE;
+
+ client_obd_list_lock(&cli->cl_loi_list_lock);
+ cli->cl_max_rpcs_in_flight = val;
+ client_obd_list_unlock(&cli->cl_loi_list_lock);
+
+ return count;
+}
+LPROC_SEQ_FOPS(mdc_max_rpcs_in_flight);
+
+static int mdc_kuc_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, NULL, PDE_DATA(inode));
+}
+
+/* temporary for testing */
+static ssize_t mdc_kuc_write(struct file *file,
+ const char __user *buffer,
+ size_t count, loff_t *off)
+{
+ struct obd_device *obd =
+ ((struct seq_file *)file->private_data)->private;
+ struct kuc_hdr *lh;
+ struct hsm_action_list *hal;
+ struct hsm_action_item *hai;
+ int len;
+ int fd, rc;
+
+ rc = lprocfs_write_helper(buffer, count, &fd);
+ if (rc)
+ return rc;
+
+ if (fd < 0)
+ return -ERANGE;
+ CWARN("message to fd %d\n", fd);
+
+ len = sizeof(*lh) + sizeof(*hal) + MTI_NAME_MAXLEN +
+ /* for mockup below */ 2 * cfs_size_round(sizeof(*hai));
+
+ OBD_ALLOC(lh, len);
+ if (!lh)
+ return -ENOMEM;
+
+ lh->kuc_magic = KUC_MAGIC;
+ lh->kuc_transport = KUC_TRANSPORT_HSM;
+ lh->kuc_msgtype = HMT_ACTION_LIST;
+ lh->kuc_msglen = len;
+
+ hal = (struct hsm_action_list *)(lh + 1);
+ hal->hal_version = HAL_VERSION;
+ hal->hal_archive_id = 1;
+ hal->hal_flags = 0;
+ obd_uuid2fsname(hal->hal_fsname, obd->obd_name, MTI_NAME_MAXLEN);
+
+ /* mock up an action list */
+ hal->hal_count = 2;
+ hai = hai_zero(hal);
+ hai->hai_action = HSMA_ARCHIVE;
+ hai->hai_fid.f_oid = 5;
+ hai->hai_len = sizeof(*hai);
+ hai = hai_next(hai);
+ hai->hai_action = HSMA_RESTORE;
+ hai->hai_fid.f_oid = 10;
+ hai->hai_len = sizeof(*hai);
+
+ /* This works for either broadcast or unicast to a single fd */
+ if (fd == 0) {
+ rc = libcfs_kkuc_group_put(KUC_GRP_HSM, lh);
+ } else {
+ struct file *fp = fget(fd);
+
+ rc = libcfs_kkuc_msg_put(fp, lh);
+ fput(fp);
+ }
+ OBD_FREE(lh, len);
+ if (rc < 0)
+ return rc;
+ return count;
+}
+
+struct file_operations mdc_kuc_fops = {
+ .open = mdc_kuc_open,
+ .write = mdc_kuc_write,
+ .release = single_release,
+};
+
+LPROC_SEQ_FOPS_WR_ONLY(mdc, ping);
+
+LPROC_SEQ_FOPS_RO_TYPE(mdc, uuid);
+LPROC_SEQ_FOPS_RO_TYPE(mdc, connect_flags);
+LPROC_SEQ_FOPS_RO_TYPE(mdc, blksize);
+LPROC_SEQ_FOPS_RO_TYPE(mdc, kbytestotal);
+LPROC_SEQ_FOPS_RO_TYPE(mdc, kbytesfree);
+LPROC_SEQ_FOPS_RO_TYPE(mdc, kbytesavail);
+LPROC_SEQ_FOPS_RO_TYPE(mdc, filestotal);
+LPROC_SEQ_FOPS_RO_TYPE(mdc, filesfree);
+LPROC_SEQ_FOPS_RO_TYPE(mdc, server_uuid);
+LPROC_SEQ_FOPS_RO_TYPE(mdc, conn_uuid);
+LPROC_SEQ_FOPS_RO_TYPE(mdc, timeouts);
+LPROC_SEQ_FOPS_RO_TYPE(mdc, state);
+
+static int mdc_obd_max_pages_per_rpc_seq_show(struct seq_file *m, void *v)
+{
+ return lprocfs_obd_rd_max_pages_per_rpc(m, m->private);
+}
+LPROC_SEQ_FOPS_RO(mdc_obd_max_pages_per_rpc);
+
+LPROC_SEQ_FOPS_RW_TYPE(mdc, import);
+LPROC_SEQ_FOPS_RW_TYPE(mdc, pinger_recov);
+
+static struct lprocfs_vars lprocfs_mdc_obd_vars[] = {
+ { "uuid", &mdc_uuid_fops, NULL, 0 },
+ { "ping", &mdc_ping_fops, NULL, 0222 },
+ { "connect_flags", &mdc_connect_flags_fops, NULL, 0 },
+ { "blocksize", &mdc_blksize_fops, NULL, 0 },
+ { "kbytestotal", &mdc_kbytestotal_fops, NULL, 0 },
+ { "kbytesfree", &mdc_kbytesfree_fops, NULL, 0 },
+ { "kbytesavail", &mdc_kbytesavail_fops, NULL, 0 },
+ { "filestotal", &mdc_filestotal_fops, NULL, 0 },
+ { "filesfree", &mdc_filesfree_fops, NULL, 0 },
+ /*{ "filegroups", lprocfs_rd_filegroups, NULL, 0 },*/
+ { "mds_server_uuid", &mdc_server_uuid_fops, NULL, 0 },
+ { "mds_conn_uuid", &mdc_conn_uuid_fops, NULL, 0 },
+ /*
+ * FIXME: below proc entry is provided, but not in used, instead
+ * sbi->sb_md_brw_size is used, the per obd variable should be used
+ * when CMD is enabled, and dir pages are managed in MDC layer.
+ * Remember to enable proc write function.
+ */
+ { "max_pages_per_rpc", &mdc_obd_max_pages_per_rpc_fops, NULL, 0 },
+ { "max_rpcs_in_flight", &mdc_max_rpcs_in_flight_fops, NULL, 0 },
+ { "timeouts", &mdc_timeouts_fops, NULL, 0 },
+ { "import", &mdc_import_fops, NULL, 0 },
+ { "state", &mdc_state_fops, NULL, 0 },
+ { "hsm_nl", &mdc_kuc_fops, NULL, 0200 },
+ { "pinger_recov", &mdc_pinger_recov_fops, NULL, 0 },
+ { NULL }
+};
+
+LPROC_SEQ_FOPS_RO_TYPE(mdc, numrefs);
+
+static struct lprocfs_vars lprocfs_mdc_module_vars[] = {
+ { "num_refs", &mdc_numrefs_fops, NULL, 0 },
+ { NULL }
+};
+
+void lprocfs_mdc_init_vars(struct lprocfs_static_vars *lvars)
+{
+ lvars->module_vars = lprocfs_mdc_module_vars;
+ lvars->obd_vars = lprocfs_mdc_obd_vars;
+}
diff --git a/drivers/staging/lustre/lustre/mdc/mdc_internal.h b/drivers/staging/lustre/lustre/mdc/mdc_internal.h
new file mode 100644
index 000000000..81780c943
--- /dev/null
+++ b/drivers/staging/lustre/lustre/mdc/mdc_internal.h
@@ -0,0 +1,181 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2011, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ */
+
+#ifndef _MDC_INTERNAL_H
+#define _MDC_INTERNAL_H
+
+#include "../include/lustre_mdc.h"
+#include "../include/lustre_mds.h"
+
+#if defined CONFIG_PROC_FS
+void lprocfs_mdc_init_vars(struct lprocfs_static_vars *lvars);
+#else
+static inline void lprocfs_mdc_init_vars(struct lprocfs_static_vars *lvars)
+{
+ memset(lvars, 0, sizeof(*lvars));
+}
+#endif
+
+void mdc_pack_body(struct ptlrpc_request *req, const struct lu_fid *fid,
+ struct obd_capa *oc, __u64 valid, int ea_size,
+ __u32 suppgid, int flags);
+void mdc_pack_capa(struct ptlrpc_request *req,
+ const struct req_msg_field *field, struct obd_capa *oc);
+int mdc_pack_req(struct ptlrpc_request *req, int version, int opc);
+void mdc_is_subdir_pack(struct ptlrpc_request *req, const struct lu_fid *pfid,
+ const struct lu_fid *cfid, int flags);
+void mdc_swap_layouts_pack(struct ptlrpc_request *req,
+ struct md_op_data *op_data);
+void mdc_readdir_pack(struct ptlrpc_request *req, __u64 pgoff, __u32 size,
+ const struct lu_fid *fid, struct obd_capa *oc);
+void mdc_getattr_pack(struct ptlrpc_request *req, __u64 valid, int flags,
+ struct md_op_data *data, int ea_size);
+void mdc_setattr_pack(struct ptlrpc_request *req, struct md_op_data *op_data,
+ void *ea, int ealen, void *ea2, int ea2len);
+void mdc_create_pack(struct ptlrpc_request *req, struct md_op_data *op_data,
+ const void *data, int datalen, __u32 mode, __u32 uid,
+ __u32 gid, cfs_cap_t capability, __u64 rdev);
+void mdc_open_pack(struct ptlrpc_request *req, struct md_op_data *op_data,
+ __u32 mode, __u64 rdev, __u64 flags, const void *data,
+ int datalen);
+void mdc_unlink_pack(struct ptlrpc_request *req, struct md_op_data *op_data);
+void mdc_getxattr_pack(struct ptlrpc_request *req, struct md_op_data *op_data);
+void mdc_link_pack(struct ptlrpc_request *req, struct md_op_data *op_data);
+void mdc_rename_pack(struct ptlrpc_request *req, struct md_op_data *op_data,
+ const char *old, int oldlen, const char *new, int newlen);
+void mdc_close_pack(struct ptlrpc_request *req, struct md_op_data *op_data);
+int mdc_enter_request(struct client_obd *cli);
+void mdc_exit_request(struct client_obd *cli);
+
+/* mdc/mdc_locks.c */
+int mdc_set_lock_data(struct obd_export *exp,
+ __u64 *lockh, void *data, __u64 *bits);
+
+int mdc_null_inode(struct obd_export *exp, const struct lu_fid *fid);
+
+int mdc_find_cbdata(struct obd_export *exp, const struct lu_fid *fid,
+ ldlm_iterator_t it, void *data);
+
+int mdc_intent_lock(struct obd_export *exp,
+ struct md_op_data *,
+ void *lmm, int lmmsize,
+ struct lookup_intent *, int,
+ struct ptlrpc_request **reqp,
+ ldlm_blocking_callback cb_blocking,
+ __u64 extra_lock_flags);
+int mdc_enqueue(struct obd_export *exp, struct ldlm_enqueue_info *einfo,
+ struct lookup_intent *it, struct md_op_data *op_data,
+ struct lustre_handle *lockh, void *lmm, int lmmsize,
+ struct ptlrpc_request **req, __u64 extra_lock_flags);
+
+int mdc_resource_get_unused(struct obd_export *exp, const struct lu_fid *fid,
+ struct list_head *cancels, ldlm_mode_t mode,
+ __u64 bits);
+/* mdc/mdc_request.c */
+int mdc_fid_alloc(struct obd_export *exp, struct lu_fid *fid,
+ struct md_op_data *op_data);
+
+int mdc_open(struct obd_export *exp, u64 ino, int type, int flags,
+ struct lov_mds_md *lmm, int lmm_size, struct lustre_handle *fh,
+ struct ptlrpc_request **);
+
+struct obd_client_handle;
+
+int mdc_get_lustre_md(struct obd_export *md_exp, struct ptlrpc_request *req,
+ struct obd_export *dt_exp, struct obd_export *lmv_exp,
+ struct lustre_md *md);
+
+int mdc_free_lustre_md(struct obd_export *exp, struct lustre_md *md);
+
+int mdc_set_open_replay_data(struct obd_export *exp,
+ struct obd_client_handle *och,
+ struct lookup_intent *it);
+
+int mdc_clear_open_replay_data(struct obd_export *exp,
+ struct obd_client_handle *och);
+void mdc_commit_open(struct ptlrpc_request *req);
+void mdc_replay_open(struct ptlrpc_request *req);
+
+int mdc_create(struct obd_export *exp, struct md_op_data *op_data,
+ const void *data, int datalen, int mode, __u32 uid, __u32 gid,
+ cfs_cap_t capability, __u64 rdev,
+ struct ptlrpc_request **request);
+int mdc_link(struct obd_export *exp, struct md_op_data *op_data,
+ struct ptlrpc_request **request);
+int mdc_rename(struct obd_export *exp, struct md_op_data *op_data,
+ const char *old, int oldlen, const char *new, int newlen,
+ struct ptlrpc_request **request);
+int mdc_setattr(struct obd_export *exp, struct md_op_data *op_data,
+ void *ea, int ealen, void *ea2, int ea2len,
+ struct ptlrpc_request **request, struct md_open_data **mod);
+int mdc_unlink(struct obd_export *exp, struct md_op_data *op_data,
+ struct ptlrpc_request **request);
+int mdc_cancel_unused(struct obd_export *exp, const struct lu_fid *fid,
+ ldlm_policy_data_t *policy, ldlm_mode_t mode,
+ ldlm_cancel_flags_t flags, void *opaque);
+
+static inline void mdc_set_capa_size(struct ptlrpc_request *req,
+ const struct req_msg_field *field,
+ struct obd_capa *oc)
+{
+ if (oc == NULL)
+ req_capsule_set_size(&req->rq_pill, field, RCL_CLIENT, 0);
+ else
+ /* it is already calculated as sizeof struct obd_capa */
+ ;
+}
+
+int mdc_revalidate_lock(struct obd_export *exp, struct lookup_intent *it,
+ struct lu_fid *fid, __u64 *bits);
+
+int mdc_intent_getattr_async(struct obd_export *exp,
+ struct md_enqueue_info *minfo,
+ struct ldlm_enqueue_info *einfo);
+
+ldlm_mode_t mdc_lock_match(struct obd_export *exp, __u64 flags,
+ const struct lu_fid *fid, ldlm_type_t type,
+ ldlm_policy_data_t *policy, ldlm_mode_t mode,
+ struct lustre_handle *lockh);
+
+static inline int mdc_prep_elc_req(struct obd_export *exp,
+ struct ptlrpc_request *req, int opc,
+ struct list_head *cancels, int count)
+{
+ return ldlm_prep_elc_req(exp, req, LUSTRE_MDS_VERSION, opc, 0, cancels,
+ count);
+}
+
+#endif
diff --git a/drivers/staging/lustre/lustre/mdc/mdc_lib.c b/drivers/staging/lustre/lustre/mdc/mdc_lib.c
new file mode 100644
index 000000000..d3234cb1e
--- /dev/null
+++ b/drivers/staging/lustre/lustre/mdc/mdc_lib.c
@@ -0,0 +1,593 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2011, 2012, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ */
+
+#define DEBUG_SUBSYSTEM S_MDC
+#include "../include/lustre_net.h"
+#include "../include/lustre/lustre_idl.h"
+#include "mdc_internal.h"
+
+
+static void __mdc_pack_body(struct mdt_body *b, __u32 suppgid)
+{
+ LASSERT(b != NULL);
+
+ b->suppgid = suppgid;
+ b->uid = from_kuid(&init_user_ns, current_uid());
+ b->gid = from_kgid(&init_user_ns, current_gid());
+ b->fsuid = from_kuid(&init_user_ns, current_fsuid());
+ b->fsgid = from_kgid(&init_user_ns, current_fsgid());
+ b->capability = cfs_curproc_cap_pack();
+}
+
+void mdc_pack_capa(struct ptlrpc_request *req,
+ const struct req_msg_field *field,
+ struct obd_capa *oc)
+{
+ struct req_capsule *pill = &req->rq_pill;
+ struct lustre_capa *c;
+
+ if (oc == NULL) {
+ LASSERT(req_capsule_get_size(pill, field, RCL_CLIENT) == 0);
+ return;
+ }
+
+ c = req_capsule_client_get(pill, field);
+ LASSERT(c != NULL);
+ capa_cpy(c, oc);
+ DEBUG_CAPA(D_SEC, c, "pack");
+}
+
+void mdc_is_subdir_pack(struct ptlrpc_request *req, const struct lu_fid *pfid,
+ const struct lu_fid *cfid, int flags)
+{
+ struct mdt_body *b = req_capsule_client_get(&req->rq_pill,
+ &RMF_MDT_BODY);
+
+ if (pfid) {
+ b->fid1 = *pfid;
+ b->valid = OBD_MD_FLID;
+ }
+ if (cfid)
+ b->fid2 = *cfid;
+ b->flags = flags;
+}
+
+void mdc_swap_layouts_pack(struct ptlrpc_request *req,
+ struct md_op_data *op_data)
+{
+ struct mdt_body *b = req_capsule_client_get(&req->rq_pill,
+ &RMF_MDT_BODY);
+
+ __mdc_pack_body(b, op_data->op_suppgids[0]);
+ b->fid1 = op_data->op_fid1;
+ b->fid2 = op_data->op_fid2;
+ b->valid |= OBD_MD_FLID;
+
+ mdc_pack_capa(req, &RMF_CAPA1, op_data->op_capa1);
+ mdc_pack_capa(req, &RMF_CAPA2, op_data->op_capa2);
+}
+
+void mdc_pack_body(struct ptlrpc_request *req,
+ const struct lu_fid *fid, struct obd_capa *oc,
+ __u64 valid, int ea_size, __u32 suppgid, int flags)
+{
+ struct mdt_body *b = req_capsule_client_get(&req->rq_pill,
+ &RMF_MDT_BODY);
+ LASSERT(b != NULL);
+ b->valid = valid;
+ b->eadatasize = ea_size;
+ b->flags = flags;
+ __mdc_pack_body(b, suppgid);
+ if (fid) {
+ b->fid1 = *fid;
+ b->valid |= OBD_MD_FLID;
+ mdc_pack_capa(req, &RMF_CAPA1, oc);
+ }
+}
+
+void mdc_readdir_pack(struct ptlrpc_request *req, __u64 pgoff,
+ __u32 size, const struct lu_fid *fid, struct obd_capa *oc)
+{
+ struct mdt_body *b = req_capsule_client_get(&req->rq_pill,
+ &RMF_MDT_BODY);
+ b->fid1 = *fid;
+ b->valid |= OBD_MD_FLID;
+ b->size = pgoff; /* !! */
+ b->nlink = size; /* !! */
+ __mdc_pack_body(b, -1);
+ b->mode = LUDA_FID | LUDA_TYPE;
+
+ mdc_pack_capa(req, &RMF_CAPA1, oc);
+}
+
+/* packing of MDS records */
+void mdc_create_pack(struct ptlrpc_request *req, struct md_op_data *op_data,
+ const void *data, int datalen, __u32 mode,
+ __u32 uid, __u32 gid, cfs_cap_t cap_effective, __u64 rdev)
+{
+ struct mdt_rec_create *rec;
+ char *tmp;
+ __u64 flags;
+
+ CLASSERT(sizeof(struct mdt_rec_reint) == sizeof(struct mdt_rec_create));
+ rec = req_capsule_client_get(&req->rq_pill, &RMF_REC_REINT);
+
+
+ rec->cr_opcode = REINT_CREATE;
+ rec->cr_fsuid = uid;
+ rec->cr_fsgid = gid;
+ rec->cr_cap = cap_effective;
+ rec->cr_fid1 = op_data->op_fid1;
+ rec->cr_fid2 = op_data->op_fid2;
+ rec->cr_mode = mode;
+ rec->cr_rdev = rdev;
+ rec->cr_time = op_data->op_mod_time;
+ rec->cr_suppgid1 = op_data->op_suppgids[0];
+ rec->cr_suppgid2 = op_data->op_suppgids[1];
+ flags = op_data->op_flags & MF_SOM_LOCAL_FLAGS;
+ if (op_data->op_bias & MDS_CREATE_VOLATILE)
+ flags |= MDS_OPEN_VOLATILE;
+ set_mrc_cr_flags(rec, flags);
+ rec->cr_bias = op_data->op_bias;
+ rec->cr_umask = current_umask();
+
+ mdc_pack_capa(req, &RMF_CAPA1, op_data->op_capa1);
+
+ tmp = req_capsule_client_get(&req->rq_pill, &RMF_NAME);
+ LOGL0(op_data->op_name, op_data->op_namelen, tmp);
+
+ if (data) {
+ tmp = req_capsule_client_get(&req->rq_pill, &RMF_EADATA);
+ memcpy(tmp, data, datalen);
+ }
+}
+
+static __u64 mds_pack_open_flags(__u64 flags, __u32 mode)
+{
+ __u64 cr_flags = (flags & (FMODE_READ | FMODE_WRITE |
+ MDS_OPEN_HAS_EA | MDS_OPEN_HAS_OBJS |
+ MDS_OPEN_OWNEROVERRIDE | MDS_OPEN_LOCK |
+ MDS_OPEN_BY_FID | MDS_OPEN_LEASE |
+ MDS_OPEN_RELEASE));
+ if (flags & O_CREAT)
+ cr_flags |= MDS_OPEN_CREAT;
+ if (flags & O_EXCL)
+ cr_flags |= MDS_OPEN_EXCL;
+ if (flags & O_TRUNC)
+ cr_flags |= MDS_OPEN_TRUNC;
+ if (flags & O_APPEND)
+ cr_flags |= MDS_OPEN_APPEND;
+ if (flags & O_SYNC)
+ cr_flags |= MDS_OPEN_SYNC;
+ if (flags & O_DIRECTORY)
+ cr_flags |= MDS_OPEN_DIRECTORY;
+ if (flags & __FMODE_EXEC)
+ cr_flags |= MDS_FMODE_EXEC;
+ if (cl_is_lov_delay_create(flags))
+ cr_flags |= MDS_OPEN_DELAY_CREATE;
+
+ if (flags & O_NONBLOCK)
+ cr_flags |= MDS_OPEN_NORESTORE;
+
+ return cr_flags;
+}
+
+/* packing of MDS records */
+void mdc_open_pack(struct ptlrpc_request *req, struct md_op_data *op_data,
+ __u32 mode, __u64 rdev, __u64 flags, const void *lmm,
+ int lmmlen)
+{
+ struct mdt_rec_create *rec;
+ char *tmp;
+ __u64 cr_flags;
+
+ CLASSERT(sizeof(struct mdt_rec_reint) == sizeof(struct mdt_rec_create));
+ rec = req_capsule_client_get(&req->rq_pill, &RMF_REC_REINT);
+
+ /* XXX do something about time, uid, gid */
+ rec->cr_opcode = REINT_OPEN;
+ rec->cr_fsuid = from_kuid(&init_user_ns, current_fsuid());
+ rec->cr_fsgid = from_kgid(&init_user_ns, current_fsgid());
+ rec->cr_cap = cfs_curproc_cap_pack();
+ rec->cr_fid1 = op_data->op_fid1;
+ rec->cr_fid2 = op_data->op_fid2;
+
+ rec->cr_mode = mode;
+ cr_flags = mds_pack_open_flags(flags, mode);
+ rec->cr_rdev = rdev;
+ rec->cr_time = op_data->op_mod_time;
+ rec->cr_suppgid1 = op_data->op_suppgids[0];
+ rec->cr_suppgid2 = op_data->op_suppgids[1];
+ rec->cr_bias = op_data->op_bias;
+ rec->cr_umask = current_umask();
+ rec->cr_old_handle = op_data->op_handle;
+
+ mdc_pack_capa(req, &RMF_CAPA1, op_data->op_capa1);
+ /* the next buffer is child capa, which is used for replay,
+ * will be packed from the data in reply message. */
+
+ if (op_data->op_name) {
+ tmp = req_capsule_client_get(&req->rq_pill, &RMF_NAME);
+ LOGL0(op_data->op_name, op_data->op_namelen, tmp);
+ if (op_data->op_bias & MDS_CREATE_VOLATILE)
+ cr_flags |= MDS_OPEN_VOLATILE;
+ }
+
+ if (lmm) {
+ cr_flags |= MDS_OPEN_HAS_EA;
+ tmp = req_capsule_client_get(&req->rq_pill, &RMF_EADATA);
+ memcpy(tmp, lmm, lmmlen);
+ }
+ set_mrc_cr_flags(rec, cr_flags);
+}
+
+static inline __u64 attr_pack(unsigned int ia_valid)
+{
+ __u64 sa_valid = 0;
+
+ if (ia_valid & ATTR_MODE)
+ sa_valid |= MDS_ATTR_MODE;
+ if (ia_valid & ATTR_UID)
+ sa_valid |= MDS_ATTR_UID;
+ if (ia_valid & ATTR_GID)
+ sa_valid |= MDS_ATTR_GID;
+ if (ia_valid & ATTR_SIZE)
+ sa_valid |= MDS_ATTR_SIZE;
+ if (ia_valid & ATTR_ATIME)
+ sa_valid |= MDS_ATTR_ATIME;
+ if (ia_valid & ATTR_MTIME)
+ sa_valid |= MDS_ATTR_MTIME;
+ if (ia_valid & ATTR_CTIME)
+ sa_valid |= MDS_ATTR_CTIME;
+ if (ia_valid & ATTR_ATIME_SET)
+ sa_valid |= MDS_ATTR_ATIME_SET;
+ if (ia_valid & ATTR_MTIME_SET)
+ sa_valid |= MDS_ATTR_MTIME_SET;
+ if (ia_valid & ATTR_FORCE)
+ sa_valid |= MDS_ATTR_FORCE;
+ if (ia_valid & ATTR_ATTR_FLAG)
+ sa_valid |= MDS_ATTR_ATTR_FLAG;
+ if (ia_valid & ATTR_KILL_SUID)
+ sa_valid |= MDS_ATTR_KILL_SUID;
+ if (ia_valid & ATTR_KILL_SGID)
+ sa_valid |= MDS_ATTR_KILL_SGID;
+ if (ia_valid & ATTR_CTIME_SET)
+ sa_valid |= MDS_ATTR_CTIME_SET;
+ if (ia_valid & ATTR_FROM_OPEN)
+ sa_valid |= MDS_ATTR_FROM_OPEN;
+ if (ia_valid & ATTR_BLOCKS)
+ sa_valid |= MDS_ATTR_BLOCKS;
+ if (ia_valid & MDS_OPEN_OWNEROVERRIDE)
+ /* NFSD hack (see bug 5781) */
+ sa_valid |= MDS_OPEN_OWNEROVERRIDE;
+ return sa_valid;
+}
+
+static void mdc_setattr_pack_rec(struct mdt_rec_setattr *rec,
+ struct md_op_data *op_data)
+{
+ rec->sa_opcode = REINT_SETATTR;
+ rec->sa_fsuid = from_kuid(&init_user_ns, current_fsuid());
+ rec->sa_fsgid = from_kgid(&init_user_ns, current_fsgid());
+ rec->sa_cap = cfs_curproc_cap_pack();
+ rec->sa_suppgid = -1;
+
+ rec->sa_fid = op_data->op_fid1;
+ rec->sa_valid = attr_pack(op_data->op_attr.ia_valid);
+ rec->sa_mode = op_data->op_attr.ia_mode;
+ rec->sa_uid = from_kuid(&init_user_ns, op_data->op_attr.ia_uid);
+ rec->sa_gid = from_kgid(&init_user_ns, op_data->op_attr.ia_gid);
+ rec->sa_size = op_data->op_attr.ia_size;
+ rec->sa_blocks = op_data->op_attr_blocks;
+ rec->sa_atime = LTIME_S(op_data->op_attr.ia_atime);
+ rec->sa_mtime = LTIME_S(op_data->op_attr.ia_mtime);
+ rec->sa_ctime = LTIME_S(op_data->op_attr.ia_ctime);
+ rec->sa_attr_flags =
+ ((struct ll_iattr *)&op_data->op_attr)->ia_attr_flags;
+ if ((op_data->op_attr.ia_valid & ATTR_GID) &&
+ in_group_p(op_data->op_attr.ia_gid))
+ rec->sa_suppgid =
+ from_kgid(&init_user_ns, op_data->op_attr.ia_gid);
+ else
+ rec->sa_suppgid = op_data->op_suppgids[0];
+
+ rec->sa_bias = op_data->op_bias;
+}
+
+static void mdc_ioepoch_pack(struct mdt_ioepoch *epoch,
+ struct md_op_data *op_data)
+{
+ memcpy(&epoch->handle, &op_data->op_handle, sizeof(epoch->handle));
+ epoch->ioepoch = op_data->op_ioepoch;
+ epoch->flags = op_data->op_flags & MF_SOM_LOCAL_FLAGS;
+}
+
+void mdc_setattr_pack(struct ptlrpc_request *req, struct md_op_data *op_data,
+ void *ea, int ealen, void *ea2, int ea2len)
+{
+ struct mdt_rec_setattr *rec;
+ struct mdt_ioepoch *epoch;
+ struct lov_user_md *lum = NULL;
+
+ CLASSERT(sizeof(struct mdt_rec_reint) ==
+ sizeof(struct mdt_rec_setattr));
+ rec = req_capsule_client_get(&req->rq_pill, &RMF_REC_REINT);
+ mdc_setattr_pack_rec(rec, op_data);
+
+ mdc_pack_capa(req, &RMF_CAPA1, op_data->op_capa1);
+
+ if (op_data->op_flags & (MF_SOM_CHANGE | MF_EPOCH_OPEN)) {
+ epoch = req_capsule_client_get(&req->rq_pill, &RMF_MDT_EPOCH);
+ mdc_ioepoch_pack(epoch, op_data);
+ }
+
+ if (ealen == 0)
+ return;
+
+ lum = req_capsule_client_get(&req->rq_pill, &RMF_EADATA);
+ if (ea == NULL) { /* Remove LOV EA */
+ lum->lmm_magic = LOV_USER_MAGIC_V1;
+ lum->lmm_stripe_size = 0;
+ lum->lmm_stripe_count = 0;
+ lum->lmm_stripe_offset = (typeof(lum->lmm_stripe_offset))(-1);
+ } else {
+ memcpy(lum, ea, ealen);
+ }
+
+ if (ea2len == 0)
+ return;
+
+ memcpy(req_capsule_client_get(&req->rq_pill, &RMF_LOGCOOKIES), ea2,
+ ea2len);
+}
+
+void mdc_unlink_pack(struct ptlrpc_request *req, struct md_op_data *op_data)
+{
+ struct mdt_rec_unlink *rec;
+ char *tmp;
+
+ CLASSERT(sizeof(struct mdt_rec_reint) == sizeof(struct mdt_rec_unlink));
+ rec = req_capsule_client_get(&req->rq_pill, &RMF_REC_REINT);
+ LASSERT(rec != NULL);
+
+ rec->ul_opcode = op_data->op_cli_flags & CLI_RM_ENTRY ?
+ REINT_RMENTRY : REINT_UNLINK;
+ rec->ul_fsuid = op_data->op_fsuid;
+ rec->ul_fsgid = op_data->op_fsgid;
+ rec->ul_cap = op_data->op_cap;
+ rec->ul_mode = op_data->op_mode;
+ rec->ul_suppgid1 = op_data->op_suppgids[0];
+ rec->ul_suppgid2 = -1;
+ rec->ul_fid1 = op_data->op_fid1;
+ rec->ul_fid2 = op_data->op_fid2;
+ rec->ul_time = op_data->op_mod_time;
+ rec->ul_bias = op_data->op_bias;
+
+ mdc_pack_capa(req, &RMF_CAPA1, op_data->op_capa1);
+
+ tmp = req_capsule_client_get(&req->rq_pill, &RMF_NAME);
+ LASSERT(tmp != NULL);
+ LOGL0(op_data->op_name, op_data->op_namelen, tmp);
+}
+
+void mdc_link_pack(struct ptlrpc_request *req, struct md_op_data *op_data)
+{
+ struct mdt_rec_link *rec;
+ char *tmp;
+
+ CLASSERT(sizeof(struct mdt_rec_reint) == sizeof(struct mdt_rec_link));
+ rec = req_capsule_client_get(&req->rq_pill, &RMF_REC_REINT);
+ LASSERT(rec != NULL);
+
+ rec->lk_opcode = REINT_LINK;
+ rec->lk_fsuid = op_data->op_fsuid; /* current->fsuid; */
+ rec->lk_fsgid = op_data->op_fsgid; /* current->fsgid; */
+ rec->lk_cap = op_data->op_cap; /* current->cap_effective; */
+ rec->lk_suppgid1 = op_data->op_suppgids[0];
+ rec->lk_suppgid2 = op_data->op_suppgids[1];
+ rec->lk_fid1 = op_data->op_fid1;
+ rec->lk_fid2 = op_data->op_fid2;
+ rec->lk_time = op_data->op_mod_time;
+ rec->lk_bias = op_data->op_bias;
+
+ mdc_pack_capa(req, &RMF_CAPA1, op_data->op_capa1);
+ mdc_pack_capa(req, &RMF_CAPA2, op_data->op_capa2);
+
+ tmp = req_capsule_client_get(&req->rq_pill, &RMF_NAME);
+ LOGL0(op_data->op_name, op_data->op_namelen, tmp);
+}
+
+void mdc_rename_pack(struct ptlrpc_request *req, struct md_op_data *op_data,
+ const char *old, int oldlen, const char *new, int newlen)
+{
+ struct mdt_rec_rename *rec;
+ char *tmp;
+
+ CLASSERT(sizeof(struct mdt_rec_reint) == sizeof(struct mdt_rec_rename));
+ rec = req_capsule_client_get(&req->rq_pill, &RMF_REC_REINT);
+
+ /* XXX do something about time, uid, gid */
+ rec->rn_opcode = REINT_RENAME;
+ rec->rn_fsuid = op_data->op_fsuid;
+ rec->rn_fsgid = op_data->op_fsgid;
+ rec->rn_cap = op_data->op_cap;
+ rec->rn_suppgid1 = op_data->op_suppgids[0];
+ rec->rn_suppgid2 = op_data->op_suppgids[1];
+ rec->rn_fid1 = op_data->op_fid1;
+ rec->rn_fid2 = op_data->op_fid2;
+ rec->rn_time = op_data->op_mod_time;
+ rec->rn_mode = op_data->op_mode;
+ rec->rn_bias = op_data->op_bias;
+
+ mdc_pack_capa(req, &RMF_CAPA1, op_data->op_capa1);
+ mdc_pack_capa(req, &RMF_CAPA2, op_data->op_capa2);
+
+ tmp = req_capsule_client_get(&req->rq_pill, &RMF_NAME);
+ LOGL0(old, oldlen, tmp);
+
+ if (new) {
+ tmp = req_capsule_client_get(&req->rq_pill, &RMF_SYMTGT);
+ LOGL0(new, newlen, tmp);
+ }
+}
+
+void mdc_getattr_pack(struct ptlrpc_request *req, __u64 valid, int flags,
+ struct md_op_data *op_data, int ea_size)
+{
+ struct mdt_body *b = req_capsule_client_get(&req->rq_pill,
+ &RMF_MDT_BODY);
+
+ b->valid = valid;
+ if (op_data->op_bias & MDS_CHECK_SPLIT)
+ b->valid |= OBD_MD_FLCKSPLIT;
+ if (op_data->op_bias & MDS_CROSS_REF)
+ b->valid |= OBD_MD_FLCROSSREF;
+ b->eadatasize = ea_size;
+ b->flags = flags;
+ __mdc_pack_body(b, op_data->op_suppgids[0]);
+
+ b->fid1 = op_data->op_fid1;
+ b->fid2 = op_data->op_fid2;
+ b->valid |= OBD_MD_FLID;
+
+ mdc_pack_capa(req, &RMF_CAPA1, op_data->op_capa1);
+
+ if (op_data->op_name) {
+ char *tmp = req_capsule_client_get(&req->rq_pill, &RMF_NAME);
+
+ LOGL0(op_data->op_name, op_data->op_namelen, tmp);
+
+ }
+}
+
+static void mdc_hsm_release_pack(struct ptlrpc_request *req,
+ struct md_op_data *op_data)
+{
+ if (op_data->op_bias & MDS_HSM_RELEASE) {
+ struct close_data *data;
+ struct ldlm_lock *lock;
+
+ data = req_capsule_client_get(&req->rq_pill, &RMF_CLOSE_DATA);
+ LASSERT(data != NULL);
+
+ lock = ldlm_handle2lock(&op_data->op_lease_handle);
+ if (lock != NULL) {
+ data->cd_handle = lock->l_remote_handle;
+ ldlm_lock_put(lock);
+ }
+ ldlm_cli_cancel(&op_data->op_lease_handle, LCF_LOCAL);
+
+ data->cd_data_version = op_data->op_data_version;
+ data->cd_fid = op_data->op_fid2;
+ }
+}
+
+void mdc_close_pack(struct ptlrpc_request *req, struct md_op_data *op_data)
+{
+ struct mdt_ioepoch *epoch;
+ struct mdt_rec_setattr *rec;
+
+ epoch = req_capsule_client_get(&req->rq_pill, &RMF_MDT_EPOCH);
+ rec = req_capsule_client_get(&req->rq_pill, &RMF_REC_REINT);
+
+ mdc_setattr_pack_rec(rec, op_data);
+ mdc_pack_capa(req, &RMF_CAPA1, op_data->op_capa1);
+ mdc_ioepoch_pack(epoch, op_data);
+ mdc_hsm_release_pack(req, op_data);
+}
+
+static int mdc_req_avail(struct client_obd *cli, struct mdc_cache_waiter *mcw)
+{
+ int rc;
+
+ client_obd_list_lock(&cli->cl_loi_list_lock);
+ rc = list_empty(&mcw->mcw_entry);
+ client_obd_list_unlock(&cli->cl_loi_list_lock);
+ return rc;
+};
+
+/* We record requests in flight in cli->cl_r_in_flight here.
+ * There is only one write rpc possible in mdc anyway. If this to change
+ * in the future - the code may need to be revisited. */
+int mdc_enter_request(struct client_obd *cli)
+{
+ int rc = 0;
+ struct mdc_cache_waiter mcw;
+ struct l_wait_info lwi = LWI_INTR(LWI_ON_SIGNAL_NOOP, NULL);
+
+ client_obd_list_lock(&cli->cl_loi_list_lock);
+ if (cli->cl_r_in_flight >= cli->cl_max_rpcs_in_flight) {
+ list_add_tail(&mcw.mcw_entry, &cli->cl_cache_waiters);
+ init_waitqueue_head(&mcw.mcw_waitq);
+ client_obd_list_unlock(&cli->cl_loi_list_lock);
+ rc = l_wait_event(mcw.mcw_waitq, mdc_req_avail(cli, &mcw),
+ &lwi);
+ if (rc) {
+ client_obd_list_lock(&cli->cl_loi_list_lock);
+ if (list_empty(&mcw.mcw_entry))
+ cli->cl_r_in_flight--;
+ list_del_init(&mcw.mcw_entry);
+ client_obd_list_unlock(&cli->cl_loi_list_lock);
+ }
+ } else {
+ cli->cl_r_in_flight++;
+ client_obd_list_unlock(&cli->cl_loi_list_lock);
+ }
+ return rc;
+}
+
+void mdc_exit_request(struct client_obd *cli)
+{
+ struct list_head *l, *tmp;
+ struct mdc_cache_waiter *mcw;
+
+ client_obd_list_lock(&cli->cl_loi_list_lock);
+ cli->cl_r_in_flight--;
+ list_for_each_safe(l, tmp, &cli->cl_cache_waiters) {
+ if (cli->cl_r_in_flight >= cli->cl_max_rpcs_in_flight) {
+ /* No free request slots anymore */
+ break;
+ }
+
+ mcw = list_entry(l, struct mdc_cache_waiter, mcw_entry);
+ list_del_init(&mcw->mcw_entry);
+ cli->cl_r_in_flight++;
+ wake_up(&mcw->mcw_waitq);
+ }
+ /* Empty waiting list? Decrease reqs in-flight number */
+
+ client_obd_list_unlock(&cli->cl_loi_list_lock);
+}
diff --git a/drivers/staging/lustre/lustre/mdc/mdc_locks.c b/drivers/staging/lustre/lustre/mdc/mdc_locks.c
new file mode 100644
index 000000000..d1c224ecd
--- /dev/null
+++ b/drivers/staging/lustre/lustre/mdc/mdc_locks.c
@@ -0,0 +1,1313 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2011, 2012, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ */
+
+#define DEBUG_SUBSYSTEM S_MDC
+
+# include <linux/module.h>
+
+#include "../include/lustre_intent.h"
+#include "../include/obd.h"
+#include "../include/obd_class.h"
+#include "../include/lustre_dlm.h"
+#include "../include/lustre_fid.h" /* fid_res_name_eq() */
+#include "../include/lustre_mdc.h"
+#include "../include/lustre_net.h"
+#include "../include/lustre_req_layout.h"
+#include "mdc_internal.h"
+
+struct mdc_getattr_args {
+ struct obd_export *ga_exp;
+ struct md_enqueue_info *ga_minfo;
+ struct ldlm_enqueue_info *ga_einfo;
+};
+
+int it_disposition(struct lookup_intent *it, int flag)
+{
+ return it->d.lustre.it_disposition & flag;
+}
+EXPORT_SYMBOL(it_disposition);
+
+void it_set_disposition(struct lookup_intent *it, int flag)
+{
+ it->d.lustre.it_disposition |= flag;
+}
+EXPORT_SYMBOL(it_set_disposition);
+
+void it_clear_disposition(struct lookup_intent *it, int flag)
+{
+ it->d.lustre.it_disposition &= ~flag;
+}
+EXPORT_SYMBOL(it_clear_disposition);
+
+int it_open_error(int phase, struct lookup_intent *it)
+{
+ if (it_disposition(it, DISP_OPEN_LEASE)) {
+ if (phase >= DISP_OPEN_LEASE)
+ return it->d.lustre.it_status;
+ else
+ return 0;
+ }
+ if (it_disposition(it, DISP_OPEN_OPEN)) {
+ if (phase >= DISP_OPEN_OPEN)
+ return it->d.lustre.it_status;
+ else
+ return 0;
+ }
+
+ if (it_disposition(it, DISP_OPEN_CREATE)) {
+ if (phase >= DISP_OPEN_CREATE)
+ return it->d.lustre.it_status;
+ else
+ return 0;
+ }
+
+ if (it_disposition(it, DISP_LOOKUP_EXECD)) {
+ if (phase >= DISP_LOOKUP_EXECD)
+ return it->d.lustre.it_status;
+ else
+ return 0;
+ }
+
+ if (it_disposition(it, DISP_IT_EXECD)) {
+ if (phase >= DISP_IT_EXECD)
+ return it->d.lustre.it_status;
+ else
+ return 0;
+ }
+ CERROR("it disp: %X, status: %d\n", it->d.lustre.it_disposition,
+ it->d.lustre.it_status);
+ LBUG();
+ return 0;
+}
+EXPORT_SYMBOL(it_open_error);
+
+/* this must be called on a lockh that is known to have a referenced lock */
+int mdc_set_lock_data(struct obd_export *exp, __u64 *lockh, void *data,
+ __u64 *bits)
+{
+ struct ldlm_lock *lock;
+ struct inode *new_inode = data;
+
+ if (bits)
+ *bits = 0;
+
+ if (!*lockh)
+ return 0;
+
+ lock = ldlm_handle2lock((struct lustre_handle *)lockh);
+
+ LASSERT(lock != NULL);
+ lock_res_and_lock(lock);
+ if (lock->l_resource->lr_lvb_inode &&
+ lock->l_resource->lr_lvb_inode != data) {
+ struct inode *old_inode = lock->l_resource->lr_lvb_inode;
+
+ LASSERTF(old_inode->i_state & I_FREEING,
+ "Found existing inode %p/%lu/%u state %lu in lock: setting data to %p/%lu/%u\n",
+ old_inode, old_inode->i_ino, old_inode->i_generation,
+ old_inode->i_state, new_inode, new_inode->i_ino,
+ new_inode->i_generation);
+ }
+ lock->l_resource->lr_lvb_inode = new_inode;
+ if (bits)
+ *bits = lock->l_policy_data.l_inodebits.bits;
+
+ unlock_res_and_lock(lock);
+ LDLM_LOCK_PUT(lock);
+
+ return 0;
+}
+
+ldlm_mode_t mdc_lock_match(struct obd_export *exp, __u64 flags,
+ const struct lu_fid *fid, ldlm_type_t type,
+ ldlm_policy_data_t *policy, ldlm_mode_t mode,
+ struct lustre_handle *lockh)
+{
+ struct ldlm_res_id res_id;
+ ldlm_mode_t rc;
+
+ fid_build_reg_res_name(fid, &res_id);
+ /* LU-4405: Clear bits not supported by server */
+ policy->l_inodebits.bits &= exp_connect_ibits(exp);
+ rc = ldlm_lock_match(class_exp2obd(exp)->obd_namespace, flags,
+ &res_id, type, policy, mode, lockh, 0);
+ return rc;
+}
+
+int mdc_cancel_unused(struct obd_export *exp,
+ const struct lu_fid *fid,
+ ldlm_policy_data_t *policy,
+ ldlm_mode_t mode,
+ ldlm_cancel_flags_t flags,
+ void *opaque)
+{
+ struct ldlm_res_id res_id;
+ struct obd_device *obd = class_exp2obd(exp);
+ int rc;
+
+ fid_build_reg_res_name(fid, &res_id);
+ rc = ldlm_cli_cancel_unused_resource(obd->obd_namespace, &res_id,
+ policy, mode, flags, opaque);
+ return rc;
+}
+
+int mdc_null_inode(struct obd_export *exp,
+ const struct lu_fid *fid)
+{
+ struct ldlm_res_id res_id;
+ struct ldlm_resource *res;
+ struct ldlm_namespace *ns = class_exp2obd(exp)->obd_namespace;
+
+ LASSERTF(ns != NULL, "no namespace passed\n");
+
+ fid_build_reg_res_name(fid, &res_id);
+
+ res = ldlm_resource_get(ns, NULL, &res_id, 0, 0);
+ if (res == NULL)
+ return 0;
+
+ lock_res(res);
+ res->lr_lvb_inode = NULL;
+ unlock_res(res);
+
+ ldlm_resource_putref(res);
+ return 0;
+}
+
+/* find any ldlm lock of the inode in mdc
+ * return 0 not find
+ * 1 find one
+ * < 0 error */
+int mdc_find_cbdata(struct obd_export *exp,
+ const struct lu_fid *fid,
+ ldlm_iterator_t it, void *data)
+{
+ struct ldlm_res_id res_id;
+ int rc = 0;
+
+ fid_build_reg_res_name((struct lu_fid *)fid, &res_id);
+ rc = ldlm_resource_iterate(class_exp2obd(exp)->obd_namespace, &res_id,
+ it, data);
+ if (rc == LDLM_ITER_STOP)
+ return 1;
+ else if (rc == LDLM_ITER_CONTINUE)
+ return 0;
+ return rc;
+}
+
+static inline void mdc_clear_replay_flag(struct ptlrpc_request *req, int rc)
+{
+ /* Don't hold error requests for replay. */
+ if (req->rq_replay) {
+ spin_lock(&req->rq_lock);
+ req->rq_replay = 0;
+ spin_unlock(&req->rq_lock);
+ }
+ if (rc && req->rq_transno != 0) {
+ DEBUG_REQ(D_ERROR, req, "transno returned on error rc %d", rc);
+ LBUG();
+ }
+}
+
+/* Save a large LOV EA into the request buffer so that it is available
+ * for replay. We don't do this in the initial request because the
+ * original request doesn't need this buffer (at most it sends just the
+ * lov_mds_md) and it is a waste of RAM/bandwidth to send the empty
+ * buffer and may also be difficult to allocate and save a very large
+ * request buffer for each open. (bug 5707)
+ *
+ * OOM here may cause recovery failure if lmm is needed (only for the
+ * original open if the MDS crashed just when this client also OOM'd)
+ * but this is incredibly unlikely, and questionable whether the client
+ * could do MDS recovery under OOM anyways... */
+static void mdc_realloc_openmsg(struct ptlrpc_request *req,
+ struct mdt_body *body)
+{
+ int rc;
+
+ /* FIXME: remove this explicit offset. */
+ rc = sptlrpc_cli_enlarge_reqbuf(req, DLM_INTENT_REC_OFF + 4,
+ body->eadatasize);
+ if (rc) {
+ CERROR("Can't enlarge segment %d size to %d\n",
+ DLM_INTENT_REC_OFF + 4, body->eadatasize);
+ body->valid &= ~OBD_MD_FLEASIZE;
+ body->eadatasize = 0;
+ }
+}
+
+static struct ptlrpc_request *mdc_intent_open_pack(struct obd_export *exp,
+ struct lookup_intent *it,
+ struct md_op_data *op_data,
+ void *lmm, int lmmsize,
+ void *cb_data)
+{
+ struct ptlrpc_request *req;
+ struct obd_device *obddev = class_exp2obd(exp);
+ struct ldlm_intent *lit;
+ LIST_HEAD(cancels);
+ int count = 0;
+ int mode;
+ int rc;
+
+ it->it_create_mode = (it->it_create_mode & ~S_IFMT) | S_IFREG;
+
+ /* XXX: openlock is not cancelled for cross-refs. */
+ /* If inode is known, cancel conflicting OPEN locks. */
+ if (fid_is_sane(&op_data->op_fid2)) {
+ if (it->it_flags & MDS_OPEN_LEASE) { /* try to get lease */
+ if (it->it_flags & FMODE_WRITE)
+ mode = LCK_EX;
+ else
+ mode = LCK_PR;
+ } else {
+ if (it->it_flags & (FMODE_WRITE|MDS_OPEN_TRUNC))
+ mode = LCK_CW;
+ else if (it->it_flags & __FMODE_EXEC)
+ mode = LCK_PR;
+ else
+ mode = LCK_CR;
+ }
+ count = mdc_resource_get_unused(exp, &op_data->op_fid2,
+ &cancels, mode,
+ MDS_INODELOCK_OPEN);
+ }
+
+ /* If CREATE, cancel parent's UPDATE lock. */
+ if (it->it_op & IT_CREAT)
+ mode = LCK_EX;
+ else
+ mode = LCK_CR;
+ count += mdc_resource_get_unused(exp, &op_data->op_fid1,
+ &cancels, mode,
+ MDS_INODELOCK_UPDATE);
+
+ req = ptlrpc_request_alloc(class_exp2cliimp(exp),
+ &RQF_LDLM_INTENT_OPEN);
+ if (req == NULL) {
+ ldlm_lock_list_put(&cancels, l_bl_ast, count);
+ return ERR_PTR(-ENOMEM);
+ }
+
+ /* parent capability */
+ mdc_set_capa_size(req, &RMF_CAPA1, op_data->op_capa1);
+ /* child capability, reserve the size according to parent capa, it will
+ * be filled after we get the reply */
+ mdc_set_capa_size(req, &RMF_CAPA2, op_data->op_capa1);
+
+ req_capsule_set_size(&req->rq_pill, &RMF_NAME, RCL_CLIENT,
+ op_data->op_namelen + 1);
+ req_capsule_set_size(&req->rq_pill, &RMF_EADATA, RCL_CLIENT,
+ max(lmmsize, obddev->u.cli.cl_default_mds_easize));
+
+ rc = ldlm_prep_enqueue_req(exp, req, &cancels, count);
+ if (rc < 0) {
+ ptlrpc_request_free(req);
+ return ERR_PTR(rc);
+ }
+
+ spin_lock(&req->rq_lock);
+ req->rq_replay = req->rq_import->imp_replayable;
+ spin_unlock(&req->rq_lock);
+
+ /* pack the intent */
+ lit = req_capsule_client_get(&req->rq_pill, &RMF_LDLM_INTENT);
+ lit->opc = (__u64)it->it_op;
+
+ /* pack the intended request */
+ mdc_open_pack(req, op_data, it->it_create_mode, 0, it->it_flags, lmm,
+ lmmsize);
+
+ /* for remote client, fetch remote perm for current user */
+ if (client_is_remote(exp))
+ req_capsule_set_size(&req->rq_pill, &RMF_ACL, RCL_SERVER,
+ sizeof(struct mdt_remote_perm));
+ ptlrpc_request_set_replen(req);
+ return req;
+}
+
+static struct ptlrpc_request *
+mdc_intent_getxattr_pack(struct obd_export *exp,
+ struct lookup_intent *it,
+ struct md_op_data *op_data)
+{
+ struct ptlrpc_request *req;
+ struct ldlm_intent *lit;
+ int rc, count = 0, maxdata;
+ LIST_HEAD(cancels);
+
+
+
+ req = ptlrpc_request_alloc(class_exp2cliimp(exp),
+ &RQF_LDLM_INTENT_GETXATTR);
+ if (req == NULL)
+ return ERR_PTR(-ENOMEM);
+
+ mdc_set_capa_size(req, &RMF_CAPA1, op_data->op_capa1);
+
+ rc = ldlm_prep_enqueue_req(exp, req, &cancels, count);
+ if (rc) {
+ ptlrpc_request_free(req);
+ return ERR_PTR(rc);
+ }
+
+ /* pack the intent */
+ lit = req_capsule_client_get(&req->rq_pill, &RMF_LDLM_INTENT);
+ lit->opc = IT_GETXATTR;
+
+ maxdata = class_exp2cliimp(exp)->imp_connect_data.ocd_max_easize;
+
+ /* pack the intended request */
+ mdc_pack_body(req, &op_data->op_fid1, op_data->op_capa1,
+ op_data->op_valid, maxdata, -1, 0);
+
+ req_capsule_set_size(&req->rq_pill, &RMF_EADATA,
+ RCL_SERVER, maxdata);
+
+ req_capsule_set_size(&req->rq_pill, &RMF_EAVALS,
+ RCL_SERVER, maxdata);
+
+ req_capsule_set_size(&req->rq_pill, &RMF_EAVALS_LENS,
+ RCL_SERVER, maxdata);
+
+ ptlrpc_request_set_replen(req);
+
+ return req;
+}
+
+static struct ptlrpc_request *mdc_intent_unlink_pack(struct obd_export *exp,
+ struct lookup_intent *it,
+ struct md_op_data *op_data)
+{
+ struct ptlrpc_request *req;
+ struct obd_device *obddev = class_exp2obd(exp);
+ struct ldlm_intent *lit;
+ int rc;
+
+ req = ptlrpc_request_alloc(class_exp2cliimp(exp),
+ &RQF_LDLM_INTENT_UNLINK);
+ if (req == NULL)
+ return ERR_PTR(-ENOMEM);
+
+ mdc_set_capa_size(req, &RMF_CAPA1, op_data->op_capa1);
+ req_capsule_set_size(&req->rq_pill, &RMF_NAME, RCL_CLIENT,
+ op_data->op_namelen + 1);
+
+ rc = ldlm_prep_enqueue_req(exp, req, NULL, 0);
+ if (rc) {
+ ptlrpc_request_free(req);
+ return ERR_PTR(rc);
+ }
+
+ /* pack the intent */
+ lit = req_capsule_client_get(&req->rq_pill, &RMF_LDLM_INTENT);
+ lit->opc = (__u64)it->it_op;
+
+ /* pack the intended request */
+ mdc_unlink_pack(req, op_data);
+
+ req_capsule_set_size(&req->rq_pill, &RMF_MDT_MD, RCL_SERVER,
+ obddev->u.cli.cl_default_mds_easize);
+ req_capsule_set_size(&req->rq_pill, &RMF_ACL, RCL_SERVER,
+ obddev->u.cli.cl_default_mds_cookiesize);
+ ptlrpc_request_set_replen(req);
+ return req;
+}
+
+static struct ptlrpc_request *mdc_intent_getattr_pack(struct obd_export *exp,
+ struct lookup_intent *it,
+ struct md_op_data *op_data)
+{
+ struct ptlrpc_request *req;
+ struct obd_device *obddev = class_exp2obd(exp);
+ u64 valid = OBD_MD_FLGETATTR | OBD_MD_FLEASIZE |
+ OBD_MD_FLMODEASIZE | OBD_MD_FLDIREA |
+ OBD_MD_FLMDSCAPA | OBD_MD_MEA |
+ (client_is_remote(exp) ?
+ OBD_MD_FLRMTPERM : OBD_MD_FLACL);
+ struct ldlm_intent *lit;
+ int rc;
+ int easize;
+
+ req = ptlrpc_request_alloc(class_exp2cliimp(exp),
+ &RQF_LDLM_INTENT_GETATTR);
+ if (req == NULL)
+ return ERR_PTR(-ENOMEM);
+
+ mdc_set_capa_size(req, &RMF_CAPA1, op_data->op_capa1);
+ req_capsule_set_size(&req->rq_pill, &RMF_NAME, RCL_CLIENT,
+ op_data->op_namelen + 1);
+
+ rc = ldlm_prep_enqueue_req(exp, req, NULL, 0);
+ if (rc) {
+ ptlrpc_request_free(req);
+ return ERR_PTR(rc);
+ }
+
+ /* pack the intent */
+ lit = req_capsule_client_get(&req->rq_pill, &RMF_LDLM_INTENT);
+ lit->opc = (__u64)it->it_op;
+
+ if (obddev->u.cli.cl_default_mds_easize > 0)
+ easize = obddev->u.cli.cl_default_mds_easize;
+ else
+ easize = obddev->u.cli.cl_max_mds_easize;
+
+ /* pack the intended request */
+ mdc_getattr_pack(req, valid, it->it_flags, op_data, easize);
+
+ req_capsule_set_size(&req->rq_pill, &RMF_MDT_MD, RCL_SERVER, easize);
+ if (client_is_remote(exp))
+ req_capsule_set_size(&req->rq_pill, &RMF_ACL, RCL_SERVER,
+ sizeof(struct mdt_remote_perm));
+ ptlrpc_request_set_replen(req);
+ return req;
+}
+
+static struct ptlrpc_request *mdc_intent_layout_pack(struct obd_export *exp,
+ struct lookup_intent *it,
+ struct md_op_data *unused)
+{
+ struct obd_device *obd = class_exp2obd(exp);
+ struct ptlrpc_request *req;
+ struct ldlm_intent *lit;
+ struct layout_intent *layout;
+ int rc;
+
+ req = ptlrpc_request_alloc(class_exp2cliimp(exp),
+ &RQF_LDLM_INTENT_LAYOUT);
+ if (req == NULL)
+ return ERR_PTR(-ENOMEM);
+
+ req_capsule_set_size(&req->rq_pill, &RMF_EADATA, RCL_CLIENT, 0);
+ rc = ldlm_prep_enqueue_req(exp, req, NULL, 0);
+ if (rc) {
+ ptlrpc_request_free(req);
+ return ERR_PTR(rc);
+ }
+
+ /* pack the intent */
+ lit = req_capsule_client_get(&req->rq_pill, &RMF_LDLM_INTENT);
+ lit->opc = (__u64)it->it_op;
+
+ /* pack the layout intent request */
+ layout = req_capsule_client_get(&req->rq_pill, &RMF_LAYOUT_INTENT);
+ /* LAYOUT_INTENT_ACCESS is generic, specific operation will be
+ * set for replication */
+ layout->li_opc = LAYOUT_INTENT_ACCESS;
+
+ req_capsule_set_size(&req->rq_pill, &RMF_DLM_LVB, RCL_SERVER,
+ obd->u.cli.cl_default_mds_easize);
+ ptlrpc_request_set_replen(req);
+ return req;
+}
+
+static struct ptlrpc_request *
+mdc_enqueue_pack(struct obd_export *exp, int lvb_len)
+{
+ struct ptlrpc_request *req;
+ int rc;
+
+ req = ptlrpc_request_alloc(class_exp2cliimp(exp), &RQF_LDLM_ENQUEUE);
+ if (req == NULL)
+ return ERR_PTR(-ENOMEM);
+
+ rc = ldlm_prep_enqueue_req(exp, req, NULL, 0);
+ if (rc) {
+ ptlrpc_request_free(req);
+ return ERR_PTR(rc);
+ }
+
+ req_capsule_set_size(&req->rq_pill, &RMF_DLM_LVB, RCL_SERVER, lvb_len);
+ ptlrpc_request_set_replen(req);
+ return req;
+}
+
+static int mdc_finish_enqueue(struct obd_export *exp,
+ struct ptlrpc_request *req,
+ struct ldlm_enqueue_info *einfo,
+ struct lookup_intent *it,
+ struct lustre_handle *lockh,
+ int rc)
+{
+ struct req_capsule *pill = &req->rq_pill;
+ struct ldlm_request *lockreq;
+ struct ldlm_reply *lockrep;
+ struct lustre_intent_data *intent = &it->d.lustre;
+ struct ldlm_lock *lock;
+ void *lvb_data = NULL;
+ int lvb_len = 0;
+
+ LASSERT(rc >= 0);
+ /* Similarly, if we're going to replay this request, we don't want to
+ * actually get a lock, just perform the intent. */
+ if (req->rq_transno || req->rq_replay) {
+ lockreq = req_capsule_client_get(pill, &RMF_DLM_REQ);
+ lockreq->lock_flags |= ldlm_flags_to_wire(LDLM_FL_INTENT_ONLY);
+ }
+
+ if (rc == ELDLM_LOCK_ABORTED) {
+ einfo->ei_mode = 0;
+ memset(lockh, 0, sizeof(*lockh));
+ rc = 0;
+ } else { /* rc = 0 */
+ lock = ldlm_handle2lock(lockh);
+ LASSERT(lock != NULL);
+
+ /* If the server gave us back a different lock mode, we should
+ * fix up our variables. */
+ if (lock->l_req_mode != einfo->ei_mode) {
+ ldlm_lock_addref(lockh, lock->l_req_mode);
+ ldlm_lock_decref(lockh, einfo->ei_mode);
+ einfo->ei_mode = lock->l_req_mode;
+ }
+ LDLM_LOCK_PUT(lock);
+ }
+
+ lockrep = req_capsule_server_get(pill, &RMF_DLM_REP);
+ LASSERT(lockrep != NULL); /* checked by ldlm_cli_enqueue() */
+
+ intent->it_disposition = (int)lockrep->lock_policy_res1;
+ intent->it_status = (int)lockrep->lock_policy_res2;
+ intent->it_lock_mode = einfo->ei_mode;
+ intent->it_lock_handle = lockh->cookie;
+ intent->it_data = req;
+
+ /* Technically speaking rq_transno must already be zero if
+ * it_status is in error, so the check is a bit redundant */
+ if ((!req->rq_transno || intent->it_status < 0) && req->rq_replay)
+ mdc_clear_replay_flag(req, intent->it_status);
+
+ /* If we're doing an IT_OPEN which did not result in an actual
+ * successful open, then we need to remove the bit which saves
+ * this request for unconditional replay.
+ *
+ * It's important that we do this first! Otherwise we might exit the
+ * function without doing so, and try to replay a failed create
+ * (bug 3440) */
+ if (it->it_op & IT_OPEN && req->rq_replay &&
+ (!it_disposition(it, DISP_OPEN_OPEN) || intent->it_status != 0))
+ mdc_clear_replay_flag(req, intent->it_status);
+
+ DEBUG_REQ(D_RPCTRACE, req, "op: %d disposition: %x, status: %d",
+ it->it_op, intent->it_disposition, intent->it_status);
+
+ /* We know what to expect, so we do any byte flipping required here */
+ if (it->it_op & (IT_OPEN | IT_UNLINK | IT_LOOKUP | IT_GETATTR)) {
+ struct mdt_body *body;
+
+ body = req_capsule_server_get(pill, &RMF_MDT_BODY);
+ if (body == NULL) {
+ CERROR("Can't swab mdt_body\n");
+ return -EPROTO;
+ }
+
+ if (it_disposition(it, DISP_OPEN_OPEN) &&
+ !it_open_error(DISP_OPEN_OPEN, it)) {
+ /*
+ * If this is a successful OPEN request, we need to set
+ * replay handler and data early, so that if replay
+ * happens immediately after swabbing below, new reply
+ * is swabbed by that handler correctly.
+ */
+ mdc_set_open_replay_data(NULL, NULL, it);
+ }
+
+ if ((body->valid & (OBD_MD_FLDIREA | OBD_MD_FLEASIZE)) != 0) {
+ void *eadata;
+
+ mdc_update_max_ea_from_body(exp, body);
+
+ /*
+ * The eadata is opaque; just check that it is there.
+ * Eventually, obd_unpackmd() will check the contents.
+ */
+ eadata = req_capsule_server_sized_get(pill, &RMF_MDT_MD,
+ body->eadatasize);
+ if (eadata == NULL)
+ return -EPROTO;
+
+ /* save lvb data and length in case this is for layout
+ * lock */
+ lvb_data = eadata;
+ lvb_len = body->eadatasize;
+
+ /*
+ * We save the reply LOV EA in case we have to replay a
+ * create for recovery. If we didn't allocate a large
+ * enough request buffer above we need to reallocate it
+ * here to hold the actual LOV EA.
+ *
+ * To not save LOV EA if request is not going to replay
+ * (for example error one).
+ */
+ if ((it->it_op & IT_OPEN) && req->rq_replay) {
+ void *lmm;
+
+ if (req_capsule_get_size(pill, &RMF_EADATA,
+ RCL_CLIENT) <
+ body->eadatasize)
+ mdc_realloc_openmsg(req, body);
+ else
+ req_capsule_shrink(pill, &RMF_EADATA,
+ body->eadatasize,
+ RCL_CLIENT);
+
+ req_capsule_set_size(pill, &RMF_EADATA,
+ RCL_CLIENT,
+ body->eadatasize);
+
+ lmm = req_capsule_client_get(pill, &RMF_EADATA);
+ if (lmm)
+ memcpy(lmm, eadata, body->eadatasize);
+ }
+ }
+
+ if (body->valid & OBD_MD_FLRMTPERM) {
+ struct mdt_remote_perm *perm;
+
+ LASSERT(client_is_remote(exp));
+ perm = req_capsule_server_swab_get(pill, &RMF_ACL,
+ lustre_swab_mdt_remote_perm);
+ if (perm == NULL)
+ return -EPROTO;
+ }
+ if (body->valid & OBD_MD_FLMDSCAPA) {
+ struct lustre_capa *capa, *p;
+
+ capa = req_capsule_server_get(pill, &RMF_CAPA1);
+ if (capa == NULL)
+ return -EPROTO;
+
+ if (it->it_op & IT_OPEN) {
+ /* client fid capa will be checked in replay */
+ p = req_capsule_client_get(pill, &RMF_CAPA2);
+ LASSERT(p);
+ *p = *capa;
+ }
+ }
+ if (body->valid & OBD_MD_FLOSSCAPA) {
+ struct lustre_capa *capa;
+
+ capa = req_capsule_server_get(pill, &RMF_CAPA2);
+ if (capa == NULL)
+ return -EPROTO;
+ }
+ } else if (it->it_op & IT_LAYOUT) {
+ /* maybe the lock was granted right away and layout
+ * is packed into RMF_DLM_LVB of req */
+ lvb_len = req_capsule_get_size(pill, &RMF_DLM_LVB, RCL_SERVER);
+ if (lvb_len > 0) {
+ lvb_data = req_capsule_server_sized_get(pill,
+ &RMF_DLM_LVB, lvb_len);
+ if (lvb_data == NULL)
+ return -EPROTO;
+ }
+ }
+
+ /* fill in stripe data for layout lock */
+ lock = ldlm_handle2lock(lockh);
+ if (lock != NULL && ldlm_has_layout(lock) && lvb_data != NULL) {
+ void *lmm;
+
+ LDLM_DEBUG(lock, "layout lock returned by: %s, lvb_len: %d\n",
+ ldlm_it2str(it->it_op), lvb_len);
+
+ OBD_ALLOC_LARGE(lmm, lvb_len);
+ if (lmm == NULL) {
+ LDLM_LOCK_PUT(lock);
+ return -ENOMEM;
+ }
+ memcpy(lmm, lvb_data, lvb_len);
+
+ /* install lvb_data */
+ lock_res_and_lock(lock);
+ if (lock->l_lvb_data == NULL) {
+ lock->l_lvb_type = LVB_T_LAYOUT;
+ lock->l_lvb_data = lmm;
+ lock->l_lvb_len = lvb_len;
+ lmm = NULL;
+ }
+ unlock_res_and_lock(lock);
+ if (lmm != NULL)
+ OBD_FREE_LARGE(lmm, lvb_len);
+ }
+ if (lock != NULL)
+ LDLM_LOCK_PUT(lock);
+
+ return rc;
+}
+
+/* We always reserve enough space in the reply packet for a stripe MD, because
+ * we don't know in advance the file type. */
+int mdc_enqueue(struct obd_export *exp, struct ldlm_enqueue_info *einfo,
+ struct lookup_intent *it, struct md_op_data *op_data,
+ struct lustre_handle *lockh, void *lmm, int lmmsize,
+ struct ptlrpc_request **reqp, u64 extra_lock_flags)
+{
+ static const ldlm_policy_data_t lookup_policy = {
+ .l_inodebits = { MDS_INODELOCK_LOOKUP }
+ };
+ static const ldlm_policy_data_t update_policy = {
+ .l_inodebits = { MDS_INODELOCK_UPDATE }
+ };
+ static const ldlm_policy_data_t layout_policy = {
+ .l_inodebits = { MDS_INODELOCK_LAYOUT }
+ };
+ static const ldlm_policy_data_t getxattr_policy = {
+ .l_inodebits = { MDS_INODELOCK_XATTR }
+ };
+ ldlm_policy_data_t const *policy = &lookup_policy;
+ struct obd_device *obddev = class_exp2obd(exp);
+ struct ptlrpc_request *req;
+ u64 flags, saved_flags = extra_lock_flags;
+ struct ldlm_res_id res_id;
+ int generation, resends = 0;
+ struct ldlm_reply *lockrep;
+ enum lvb_type lvb_type = LVB_T_NONE;
+ int rc;
+
+ LASSERTF(!it || einfo->ei_type == LDLM_IBITS, "lock type %d\n",
+ einfo->ei_type);
+
+ fid_build_reg_res_name(&op_data->op_fid1, &res_id);
+
+ if (it) {
+ saved_flags |= LDLM_FL_HAS_INTENT;
+ if (it->it_op & (IT_UNLINK | IT_GETATTR | IT_READDIR))
+ policy = &update_policy;
+ else if (it->it_op & IT_LAYOUT)
+ policy = &layout_policy;
+ else if (it->it_op & (IT_GETXATTR | IT_SETXATTR))
+ policy = &getxattr_policy;
+ }
+
+ LASSERT(reqp == NULL);
+
+ generation = obddev->u.cli.cl_import->imp_generation;
+resend:
+ flags = saved_flags;
+ if (!it) {
+ /* The only way right now is FLOCK, in this case we hide flock
+ policy as lmm, but lmmsize is 0 */
+ LASSERT(lmm && lmmsize == 0);
+ LASSERTF(einfo->ei_type == LDLM_FLOCK, "lock type %d\n",
+ einfo->ei_type);
+ policy = (ldlm_policy_data_t *)lmm;
+ res_id.name[3] = LDLM_FLOCK;
+ req = NULL;
+ } else if (it->it_op & IT_OPEN) {
+ req = mdc_intent_open_pack(exp, it, op_data, lmm, lmmsize,
+ einfo->ei_cbdata);
+ policy = &update_policy;
+ einfo->ei_cbdata = NULL;
+ lmm = NULL;
+ } else if (it->it_op & IT_UNLINK) {
+ req = mdc_intent_unlink_pack(exp, it, op_data);
+ } else if (it->it_op & (IT_GETATTR | IT_LOOKUP)) {
+ req = mdc_intent_getattr_pack(exp, it, op_data);
+ } else if (it->it_op & IT_READDIR) {
+ req = mdc_enqueue_pack(exp, 0);
+ } else if (it->it_op & IT_LAYOUT) {
+ if (!imp_connect_lvb_type(class_exp2cliimp(exp)))
+ return -EOPNOTSUPP;
+ req = mdc_intent_layout_pack(exp, it, op_data);
+ lvb_type = LVB_T_LAYOUT;
+ } else if (it->it_op & IT_GETXATTR) {
+ req = mdc_intent_getxattr_pack(exp, it, op_data);
+ } else {
+ LBUG();
+ return -EINVAL;
+ }
+
+ if (IS_ERR(req))
+ return PTR_ERR(req);
+
+ if (req != NULL && it && it->it_op & IT_CREAT)
+ /* ask ptlrpc not to resend on EINPROGRESS since we have our own
+ * retry logic */
+ req->rq_no_retry_einprogress = 1;
+
+ if (resends) {
+ req->rq_generation_set = 1;
+ req->rq_import_generation = generation;
+ req->rq_sent = get_seconds() + resends;
+ }
+
+ /* It is important to obtain rpc_lock first (if applicable), so that
+ * threads that are serialised with rpc_lock are not polluting our
+ * rpcs in flight counter. We do not do flock request limiting, though*/
+ if (it) {
+ mdc_get_rpc_lock(obddev->u.cli.cl_rpc_lock, it);
+ rc = mdc_enter_request(&obddev->u.cli);
+ if (rc != 0) {
+ mdc_put_rpc_lock(obddev->u.cli.cl_rpc_lock, it);
+ mdc_clear_replay_flag(req, 0);
+ ptlrpc_req_finished(req);
+ return rc;
+ }
+ }
+
+ rc = ldlm_cli_enqueue(exp, &req, einfo, &res_id, policy, &flags, NULL,
+ 0, lvb_type, lockh, 0);
+ if (!it) {
+ /* For flock requests we immediately return without further
+ delay and let caller deal with the rest, since rest of
+ this function metadata processing makes no sense for flock
+ requests anyway. But in case of problem during comms with
+ Server (ETIMEDOUT) or any signal/kill attempt (EINTR), we
+ can not rely on caller and this mainly for F_UNLCKs
+ (explicits or automatically generated by Kernel to clean
+ current FLocks upon exit) that can't be trashed */
+ if ((rc == -EINTR) || (rc == -ETIMEDOUT))
+ goto resend;
+ return rc;
+ }
+
+ mdc_exit_request(&obddev->u.cli);
+ mdc_put_rpc_lock(obddev->u.cli.cl_rpc_lock, it);
+
+ if (rc < 0) {
+ CDEBUG_LIMIT((rc == -EACCES || rc == -EIDRM) ? D_INFO : D_ERROR,
+ "%s: ldlm_cli_enqueue failed: rc = %d\n",
+ obddev->obd_name, rc);
+
+ mdc_clear_replay_flag(req, rc);
+ ptlrpc_req_finished(req);
+ return rc;
+ }
+
+ lockrep = req_capsule_server_get(&req->rq_pill, &RMF_DLM_REP);
+ LASSERT(lockrep != NULL);
+
+ lockrep->lock_policy_res2 =
+ ptlrpc_status_ntoh(lockrep->lock_policy_res2);
+
+ /* Retry the create infinitely when we get -EINPROGRESS from
+ * server. This is required by the new quota design. */
+ if (it && it->it_op & IT_CREAT &&
+ (int)lockrep->lock_policy_res2 == -EINPROGRESS) {
+ mdc_clear_replay_flag(req, rc);
+ ptlrpc_req_finished(req);
+ resends++;
+
+ CDEBUG(D_HA, "%s: resend:%d op:%d "DFID"/"DFID"\n",
+ obddev->obd_name, resends, it->it_op,
+ PFID(&op_data->op_fid1), PFID(&op_data->op_fid2));
+
+ if (generation == obddev->u.cli.cl_import->imp_generation) {
+ goto resend;
+ } else {
+ CDEBUG(D_HA, "resend cross eviction\n");
+ return -EIO;
+ }
+ }
+
+ rc = mdc_finish_enqueue(exp, req, einfo, it, lockh, rc);
+ if (rc < 0) {
+ if (lustre_handle_is_used(lockh)) {
+ ldlm_lock_decref(lockh, einfo->ei_mode);
+ memset(lockh, 0, sizeof(*lockh));
+ }
+ ptlrpc_req_finished(req);
+
+ it->d.lustre.it_lock_handle = 0;
+ it->d.lustre.it_lock_mode = 0;
+ it->d.lustre.it_data = NULL;
+ }
+
+ return rc;
+}
+
+static int mdc_finish_intent_lock(struct obd_export *exp,
+ struct ptlrpc_request *request,
+ struct md_op_data *op_data,
+ struct lookup_intent *it,
+ struct lustre_handle *lockh)
+{
+ struct lustre_handle old_lock;
+ struct mdt_body *mdt_body;
+ struct ldlm_lock *lock;
+ int rc;
+
+ LASSERT(request != NULL);
+ LASSERT(request != LP_POISON);
+ LASSERT(request->rq_repmsg != LP_POISON);
+
+ if (!it_disposition(it, DISP_IT_EXECD)) {
+ /* The server failed before it even started executing the
+ * intent, i.e. because it couldn't unpack the request. */
+ LASSERT(it->d.lustre.it_status != 0);
+ return it->d.lustre.it_status;
+ }
+ rc = it_open_error(DISP_IT_EXECD, it);
+ if (rc)
+ return rc;
+
+ mdt_body = req_capsule_server_get(&request->rq_pill, &RMF_MDT_BODY);
+ LASSERT(mdt_body != NULL); /* mdc_enqueue checked */
+
+ /* If we were revalidating a fid/name pair, mark the intent in
+ * case we fail and get called again from lookup */
+ if (fid_is_sane(&op_data->op_fid2) &&
+ it->it_create_mode & M_CHECK_STALE &&
+ it->it_op != IT_GETATTR) {
+
+ /* Also: did we find the same inode? */
+ /* sever can return one of two fids:
+ * op_fid2 - new allocated fid - if file is created.
+ * op_fid3 - existent fid - if file only open.
+ * op_fid3 is saved in lmv_intent_open */
+ if ((!lu_fid_eq(&op_data->op_fid2, &mdt_body->fid1)) &&
+ (!lu_fid_eq(&op_data->op_fid3, &mdt_body->fid1))) {
+ CDEBUG(D_DENTRY, "Found stale data "DFID"("DFID")/"DFID
+ "\n", PFID(&op_data->op_fid2),
+ PFID(&op_data->op_fid2), PFID(&mdt_body->fid1));
+ return -ESTALE;
+ }
+ }
+
+ rc = it_open_error(DISP_LOOKUP_EXECD, it);
+ if (rc)
+ return rc;
+
+ /* keep requests around for the multiple phases of the call
+ * this shows the DISP_XX must guarantee we make it into the call
+ */
+ if (!it_disposition(it, DISP_ENQ_CREATE_REF) &&
+ it_disposition(it, DISP_OPEN_CREATE) &&
+ !it_open_error(DISP_OPEN_CREATE, it)) {
+ it_set_disposition(it, DISP_ENQ_CREATE_REF);
+ ptlrpc_request_addref(request); /* balanced in ll_create_node */
+ }
+ if (!it_disposition(it, DISP_ENQ_OPEN_REF) &&
+ it_disposition(it, DISP_OPEN_OPEN) &&
+ !it_open_error(DISP_OPEN_OPEN, it)) {
+ it_set_disposition(it, DISP_ENQ_OPEN_REF);
+ ptlrpc_request_addref(request); /* balanced in ll_file_open */
+ /* BUG 11546 - eviction in the middle of open rpc processing */
+ OBD_FAIL_TIMEOUT(OBD_FAIL_MDC_ENQUEUE_PAUSE, obd_timeout);
+ }
+
+ if (it->it_op & IT_CREAT) {
+ /* XXX this belongs in ll_create_it */
+ } else if (it->it_op == IT_OPEN) {
+ LASSERT(!it_disposition(it, DISP_OPEN_CREATE));
+ } else {
+ LASSERT(it->it_op & (IT_GETATTR | IT_LOOKUP | IT_LAYOUT));
+ }
+
+ /* If we already have a matching lock, then cancel the new
+ * one. We have to set the data here instead of in
+ * mdc_enqueue, because we need to use the child's inode as
+ * the l_ast_data to match, and that's not available until
+ * intent_finish has performed the iget().) */
+ lock = ldlm_handle2lock(lockh);
+ if (lock) {
+ ldlm_policy_data_t policy = lock->l_policy_data;
+
+ LDLM_DEBUG(lock, "matching against this");
+
+ LASSERTF(fid_res_name_eq(&mdt_body->fid1,
+ &lock->l_resource->lr_name),
+ "Lock res_id: "DLDLMRES", fid: "DFID"\n",
+ PLDLMRES(lock->l_resource), PFID(&mdt_body->fid1));
+ LDLM_LOCK_PUT(lock);
+
+ memcpy(&old_lock, lockh, sizeof(*lockh));
+ if (ldlm_lock_match(NULL, LDLM_FL_BLOCK_GRANTED, NULL,
+ LDLM_IBITS, &policy, LCK_NL,
+ &old_lock, 0)) {
+ ldlm_lock_decref_and_cancel(lockh,
+ it->d.lustre.it_lock_mode);
+ memcpy(lockh, &old_lock, sizeof(old_lock));
+ it->d.lustre.it_lock_handle = lockh->cookie;
+ }
+ }
+ CDEBUG(D_DENTRY,
+ "D_IT dentry %.*s intent: %s status %d disp %x rc %d\n",
+ op_data->op_namelen, op_data->op_name, ldlm_it2str(it->it_op),
+ it->d.lustre.it_status, it->d.lustre.it_disposition, rc);
+ return rc;
+}
+
+int mdc_revalidate_lock(struct obd_export *exp, struct lookup_intent *it,
+ struct lu_fid *fid, __u64 *bits)
+{
+ /* We could just return 1 immediately, but since we should only
+ * be called in revalidate_it if we already have a lock, let's
+ * verify that. */
+ struct ldlm_res_id res_id;
+ struct lustre_handle lockh;
+ ldlm_policy_data_t policy;
+ ldlm_mode_t mode;
+
+ if (it->d.lustre.it_lock_handle) {
+ lockh.cookie = it->d.lustre.it_lock_handle;
+ mode = ldlm_revalidate_lock_handle(&lockh, bits);
+ } else {
+ fid_build_reg_res_name(fid, &res_id);
+ switch (it->it_op) {
+ case IT_GETATTR:
+ /* File attributes are held under multiple bits:
+ * nlink is under lookup lock, size and times are
+ * under UPDATE lock and recently we've also got
+ * a separate permissions lock for owner/group/acl that
+ * were protected by lookup lock before.
+ * Getattr must provide all of that information,
+ * so we need to ensure we have all of those locks.
+ * Unfortunately, if the bits are split across multiple
+ * locks, there's no easy way to match all of them here,
+ * so an extra RPC would be performed to fetch all
+ * of those bits at once for now. */
+ /* For new MDTs(> 2.4), UPDATE|PERM should be enough,
+ * but for old MDTs (< 2.4), permission is covered
+ * by LOOKUP lock, so it needs to match all bits here.*/
+ policy.l_inodebits.bits = MDS_INODELOCK_UPDATE |
+ MDS_INODELOCK_LOOKUP |
+ MDS_INODELOCK_PERM;
+ break;
+ case IT_LAYOUT:
+ policy.l_inodebits.bits = MDS_INODELOCK_LAYOUT;
+ break;
+ default:
+ policy.l_inodebits.bits = MDS_INODELOCK_LOOKUP;
+ break;
+ }
+
+ mode = mdc_lock_match(exp, LDLM_FL_BLOCK_GRANTED, fid,
+ LDLM_IBITS, &policy,
+ LCK_CR | LCK_CW | LCK_PR | LCK_PW,
+ &lockh);
+ }
+
+ if (mode) {
+ it->d.lustre.it_lock_handle = lockh.cookie;
+ it->d.lustre.it_lock_mode = mode;
+ } else {
+ it->d.lustre.it_lock_handle = 0;
+ it->d.lustre.it_lock_mode = 0;
+ }
+
+ return !!mode;
+}
+
+/*
+ * This long block is all about fixing up the lock and request state
+ * so that it is correct as of the moment _before_ the operation was
+ * applied; that way, the VFS will think that everything is normal and
+ * call Lustre's regular VFS methods.
+ *
+ * If we're performing a creation, that means that unless the creation
+ * failed with EEXIST, we should fake up a negative dentry.
+ *
+ * For everything else, we want to lookup to succeed.
+ *
+ * One additional note: if CREATE or OPEN succeeded, we add an extra
+ * reference to the request because we need to keep it around until
+ * ll_create/ll_open gets called.
+ *
+ * The server will return to us, in it_disposition, an indication of
+ * exactly what d.lustre.it_status refers to.
+ *
+ * If DISP_OPEN_OPEN is set, then d.lustre.it_status refers to the open() call,
+ * otherwise if DISP_OPEN_CREATE is set, then it status is the
+ * creation failure mode. In either case, one of DISP_LOOKUP_NEG or
+ * DISP_LOOKUP_POS will be set, indicating whether the child lookup
+ * was successful.
+ *
+ * Else, if DISP_LOOKUP_EXECD then d.lustre.it_status is the rc of the
+ * child lookup.
+ */
+int mdc_intent_lock(struct obd_export *exp, struct md_op_data *op_data,
+ void *lmm, int lmmsize, struct lookup_intent *it,
+ int lookup_flags, struct ptlrpc_request **reqp,
+ ldlm_blocking_callback cb_blocking,
+ __u64 extra_lock_flags)
+{
+ struct ldlm_enqueue_info einfo = {
+ .ei_type = LDLM_IBITS,
+ .ei_mode = it_to_lock_mode(it),
+ .ei_cb_bl = cb_blocking,
+ .ei_cb_cp = ldlm_completion_ast,
+ };
+ struct lustre_handle lockh;
+ int rc = 0;
+
+ LASSERT(it);
+
+ CDEBUG(D_DLMTRACE, "(name: %.*s,"DFID") in obj "DFID
+ ", intent: %s flags %#Lo\n", op_data->op_namelen,
+ op_data->op_name, PFID(&op_data->op_fid2),
+ PFID(&op_data->op_fid1), ldlm_it2str(it->it_op),
+ it->it_flags);
+
+ lockh.cookie = 0;
+ if (fid_is_sane(&op_data->op_fid2) &&
+ (it->it_op & (IT_LOOKUP | IT_GETATTR))) {
+ /* We could just return 1 immediately, but since we should only
+ * be called in revalidate_it if we already have a lock, let's
+ * verify that. */
+ it->d.lustre.it_lock_handle = 0;
+ rc = mdc_revalidate_lock(exp, it, &op_data->op_fid2, NULL);
+ /* Only return failure if it was not GETATTR by cfid
+ (from inode_revalidate) */
+ if (rc || op_data->op_namelen != 0)
+ return rc;
+ }
+
+ /* For case if upper layer did not alloc fid, do it now. */
+ if (!fid_is_sane(&op_data->op_fid2) && it->it_op & IT_CREAT) {
+ rc = mdc_fid_alloc(exp, &op_data->op_fid2, op_data);
+ if (rc < 0) {
+ CERROR("Can't alloc new fid, rc %d\n", rc);
+ return rc;
+ }
+ }
+ rc = mdc_enqueue(exp, &einfo, it, op_data, &lockh, lmm, lmmsize, NULL,
+ extra_lock_flags);
+ if (rc < 0)
+ return rc;
+
+ *reqp = it->d.lustre.it_data;
+ rc = mdc_finish_intent_lock(exp, *reqp, op_data, it, &lockh);
+ return rc;
+}
+
+static int mdc_intent_getattr_async_interpret(const struct lu_env *env,
+ struct ptlrpc_request *req,
+ void *args, int rc)
+{
+ struct mdc_getattr_args *ga = args;
+ struct obd_export *exp = ga->ga_exp;
+ struct md_enqueue_info *minfo = ga->ga_minfo;
+ struct ldlm_enqueue_info *einfo = ga->ga_einfo;
+ struct lookup_intent *it;
+ struct lustre_handle *lockh;
+ struct obd_device *obddev;
+ struct ldlm_reply *lockrep;
+ __u64 flags = LDLM_FL_HAS_INTENT;
+
+ it = &minfo->mi_it;
+ lockh = &minfo->mi_lockh;
+
+ obddev = class_exp2obd(exp);
+
+ mdc_exit_request(&obddev->u.cli);
+ if (OBD_FAIL_CHECK(OBD_FAIL_MDC_GETATTR_ENQUEUE))
+ rc = -ETIMEDOUT;
+
+ rc = ldlm_cli_enqueue_fini(exp, req, einfo->ei_type, 1, einfo->ei_mode,
+ &flags, NULL, 0, lockh, rc);
+ if (rc < 0) {
+ CERROR("ldlm_cli_enqueue_fini: %d\n", rc);
+ mdc_clear_replay_flag(req, rc);
+ goto out;
+ }
+
+ lockrep = req_capsule_server_get(&req->rq_pill, &RMF_DLM_REP);
+ LASSERT(lockrep != NULL);
+
+ lockrep->lock_policy_res2 =
+ ptlrpc_status_ntoh(lockrep->lock_policy_res2);
+
+ rc = mdc_finish_enqueue(exp, req, einfo, it, lockh, rc);
+ if (rc)
+ goto out;
+
+ rc = mdc_finish_intent_lock(exp, req, &minfo->mi_data, it, lockh);
+
+out:
+ OBD_FREE_PTR(einfo);
+ minfo->mi_cb(req, minfo, rc);
+ return 0;
+}
+
+int mdc_intent_getattr_async(struct obd_export *exp,
+ struct md_enqueue_info *minfo,
+ struct ldlm_enqueue_info *einfo)
+{
+ struct md_op_data *op_data = &minfo->mi_data;
+ struct lookup_intent *it = &minfo->mi_it;
+ struct ptlrpc_request *req;
+ struct mdc_getattr_args *ga;
+ struct obd_device *obddev = class_exp2obd(exp);
+ struct ldlm_res_id res_id;
+ /*XXX: Both MDS_INODELOCK_LOOKUP and MDS_INODELOCK_UPDATE are needed
+ * for statahead currently. Consider CMD in future, such two bits
+ * maybe managed by different MDS, should be adjusted then. */
+ ldlm_policy_data_t policy = {
+ .l_inodebits = { MDS_INODELOCK_LOOKUP |
+ MDS_INODELOCK_UPDATE }
+ };
+ int rc = 0;
+ __u64 flags = LDLM_FL_HAS_INTENT;
+
+ CDEBUG(D_DLMTRACE,
+ "name: %.*s in inode "DFID", intent: %s flags %#Lo\n",
+ op_data->op_namelen, op_data->op_name, PFID(&op_data->op_fid1),
+ ldlm_it2str(it->it_op), it->it_flags);
+
+ fid_build_reg_res_name(&op_data->op_fid1, &res_id);
+ req = mdc_intent_getattr_pack(exp, it, op_data);
+ if (IS_ERR(req))
+ return PTR_ERR(req);
+
+ rc = mdc_enter_request(&obddev->u.cli);
+ if (rc != 0) {
+ ptlrpc_req_finished(req);
+ return rc;
+ }
+
+ rc = ldlm_cli_enqueue(exp, &req, einfo, &res_id, &policy, &flags, NULL,
+ 0, LVB_T_NONE, &minfo->mi_lockh, 1);
+ if (rc < 0) {
+ mdc_exit_request(&obddev->u.cli);
+ ptlrpc_req_finished(req);
+ return rc;
+ }
+
+ CLASSERT(sizeof(*ga) <= sizeof(req->rq_async_args));
+ ga = ptlrpc_req_async_args(req);
+ ga->ga_exp = exp;
+ ga->ga_minfo = minfo;
+ ga->ga_einfo = einfo;
+
+ req->rq_interpret_reply = mdc_intent_getattr_async_interpret;
+ ptlrpcd_add_req(req, PDL_POLICY_LOCAL, -1);
+
+ return 0;
+}
diff --git a/drivers/staging/lustre/lustre/mdc/mdc_reint.c b/drivers/staging/lustre/lustre/mdc/mdc_reint.c
new file mode 100644
index 000000000..5e9c6296c
--- /dev/null
+++ b/drivers/staging/lustre/lustre/mdc/mdc_reint.c
@@ -0,0 +1,483 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2002, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2011, 2012, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ */
+
+#define DEBUG_SUBSYSTEM S_MDC
+
+# include <linux/module.h>
+# include <linux/kernel.h>
+
+#include "../include/obd_class.h"
+#include "mdc_internal.h"
+#include "../include/lustre_fid.h"
+
+/* mdc_setattr does its own semaphore handling */
+static int mdc_reint(struct ptlrpc_request *request,
+ struct mdc_rpc_lock *rpc_lock,
+ int level)
+{
+ int rc;
+
+ request->rq_send_state = level;
+
+ mdc_get_rpc_lock(rpc_lock, NULL);
+ rc = ptlrpc_queue_wait(request);
+ mdc_put_rpc_lock(rpc_lock, NULL);
+ if (rc)
+ CDEBUG(D_INFO, "error in handling %d\n", rc);
+ else if (!req_capsule_server_get(&request->rq_pill, &RMF_MDT_BODY))
+ rc = -EPROTO;
+
+ return rc;
+}
+
+/* Find and cancel locally locks matched by inode @bits & @mode in the resource
+ * found by @fid. Found locks are added into @cancel list. Returns the amount of
+ * locks added to @cancels list. */
+int mdc_resource_get_unused(struct obd_export *exp, const struct lu_fid *fid,
+ struct list_head *cancels, ldlm_mode_t mode,
+ __u64 bits)
+{
+ struct ldlm_namespace *ns = exp->exp_obd->obd_namespace;
+ ldlm_policy_data_t policy = {};
+ struct ldlm_res_id res_id;
+ struct ldlm_resource *res;
+ int count;
+
+ /* Return, i.e. cancel nothing, only if ELC is supported (flag in
+ * export) but disabled through procfs (flag in NS).
+ *
+ * This distinguishes from a case when ELC is not supported originally,
+ * when we still want to cancel locks in advance and just cancel them
+ * locally, without sending any RPC. */
+ if (exp_connect_cancelset(exp) && !ns_connect_cancelset(ns))
+ return 0;
+
+ fid_build_reg_res_name(fid, &res_id);
+ res = ldlm_resource_get(exp->exp_obd->obd_namespace,
+ NULL, &res_id, 0, 0);
+ if (res == NULL)
+ return 0;
+ LDLM_RESOURCE_ADDREF(res);
+ /* Initialize ibits lock policy. */
+ policy.l_inodebits.bits = bits;
+ count = ldlm_cancel_resource_local(res, cancels, &policy,
+ mode, 0, 0, NULL);
+ LDLM_RESOURCE_DELREF(res);
+ ldlm_resource_putref(res);
+ return count;
+}
+
+int mdc_setattr(struct obd_export *exp, struct md_op_data *op_data,
+ void *ea, int ealen, void *ea2, int ea2len,
+ struct ptlrpc_request **request, struct md_open_data **mod)
+{
+ LIST_HEAD(cancels);
+ struct ptlrpc_request *req;
+ struct mdc_rpc_lock *rpc_lock;
+ struct obd_device *obd = exp->exp_obd;
+ int count = 0, rc;
+ __u64 bits;
+
+ LASSERT(op_data != NULL);
+
+ bits = MDS_INODELOCK_UPDATE;
+ if (op_data->op_attr.ia_valid & (ATTR_MODE|ATTR_UID|ATTR_GID))
+ bits |= MDS_INODELOCK_LOOKUP;
+ if ((op_data->op_flags & MF_MDC_CANCEL_FID1) &&
+ (fid_is_sane(&op_data->op_fid1)) &&
+ !OBD_FAIL_CHECK(OBD_FAIL_LDLM_BL_CALLBACK_NET))
+ count = mdc_resource_get_unused(exp, &op_data->op_fid1,
+ &cancels, LCK_EX, bits);
+ req = ptlrpc_request_alloc(class_exp2cliimp(exp),
+ &RQF_MDS_REINT_SETATTR);
+ if (req == NULL) {
+ ldlm_lock_list_put(&cancels, l_bl_ast, count);
+ return -ENOMEM;
+ }
+ mdc_set_capa_size(req, &RMF_CAPA1, op_data->op_capa1);
+ if ((op_data->op_flags & (MF_SOM_CHANGE | MF_EPOCH_OPEN)) == 0)
+ req_capsule_set_size(&req->rq_pill, &RMF_MDT_EPOCH, RCL_CLIENT,
+ 0);
+ req_capsule_set_size(&req->rq_pill, &RMF_EADATA, RCL_CLIENT, ealen);
+ req_capsule_set_size(&req->rq_pill, &RMF_LOGCOOKIES, RCL_CLIENT,
+ ea2len);
+
+ rc = mdc_prep_elc_req(exp, req, MDS_REINT, &cancels, count);
+ if (rc) {
+ ptlrpc_request_free(req);
+ return rc;
+ }
+
+ rpc_lock = obd->u.cli.cl_rpc_lock;
+
+ if (op_data->op_attr.ia_valid & (ATTR_MTIME | ATTR_CTIME))
+ CDEBUG(D_INODE, "setting mtime "CFS_TIME_T
+ ", ctime "CFS_TIME_T"\n",
+ LTIME_S(op_data->op_attr.ia_mtime),
+ LTIME_S(op_data->op_attr.ia_ctime));
+ mdc_setattr_pack(req, op_data, ea, ealen, ea2, ea2len);
+
+ ptlrpc_request_set_replen(req);
+ if (mod && (op_data->op_flags & MF_EPOCH_OPEN) &&
+ req->rq_import->imp_replayable) {
+ LASSERT(*mod == NULL);
+
+ *mod = obd_mod_alloc();
+ if (*mod == NULL) {
+ DEBUG_REQ(D_ERROR, req, "Can't allocate md_open_data");
+ } else {
+ req->rq_replay = 1;
+ req->rq_cb_data = *mod;
+ (*mod)->mod_open_req = req;
+ req->rq_commit_cb = mdc_commit_open;
+ (*mod)->mod_is_create = true;
+ /**
+ * Take an extra reference on \var mod, it protects \var
+ * mod from being freed on eviction (commit callback is
+ * called despite rq_replay flag).
+ * Will be put on mdc_done_writing().
+ */
+ obd_mod_get(*mod);
+ }
+ }
+
+ rc = mdc_reint(req, rpc_lock, LUSTRE_IMP_FULL);
+
+ /* Save the obtained info in the original RPC for the replay case. */
+ if (rc == 0 && (op_data->op_flags & MF_EPOCH_OPEN)) {
+ struct mdt_ioepoch *epoch;
+ struct mdt_body *body;
+
+ epoch = req_capsule_client_get(&req->rq_pill, &RMF_MDT_EPOCH);
+ body = req_capsule_server_get(&req->rq_pill, &RMF_MDT_BODY);
+ LASSERT(epoch != NULL);
+ LASSERT(body != NULL);
+ epoch->handle = body->handle;
+ epoch->ioepoch = body->ioepoch;
+ req->rq_replay_cb = mdc_replay_open;
+ /** bug 3633, open may be committed and estale answer is not error */
+ } else if (rc == -ESTALE && (op_data->op_flags & MF_SOM_CHANGE)) {
+ rc = 0;
+ } else if (rc == -ERESTARTSYS) {
+ rc = 0;
+ }
+ *request = req;
+ if (rc && req->rq_commit_cb) {
+ /* Put an extra reference on \var mod on error case. */
+ if (mod != NULL && *mod != NULL)
+ obd_mod_put(*mod);
+ req->rq_commit_cb(req);
+ }
+ return rc;
+}
+
+int mdc_create(struct obd_export *exp, struct md_op_data *op_data,
+ const void *data, int datalen, int mode, __u32 uid, __u32 gid,
+ cfs_cap_t cap_effective, __u64 rdev,
+ struct ptlrpc_request **request)
+{
+ struct ptlrpc_request *req;
+ int level, rc;
+ int count, resends = 0;
+ struct obd_import *import = exp->exp_obd->u.cli.cl_import;
+ int generation = import->imp_generation;
+ LIST_HEAD(cancels);
+
+ /* For case if upper layer did not alloc fid, do it now. */
+ if (!fid_is_sane(&op_data->op_fid2)) {
+ /*
+ * mdc_fid_alloc() may return errno 1 in case of switch to new
+ * sequence, handle this.
+ */
+ rc = mdc_fid_alloc(exp, &op_data->op_fid2, op_data);
+ if (rc < 0) {
+ CERROR("Can't alloc new fid, rc %d\n", rc);
+ return rc;
+ }
+ }
+
+rebuild:
+ count = 0;
+ if ((op_data->op_flags & MF_MDC_CANCEL_FID1) &&
+ (fid_is_sane(&op_data->op_fid1)))
+ count = mdc_resource_get_unused(exp, &op_data->op_fid1,
+ &cancels, LCK_EX,
+ MDS_INODELOCK_UPDATE);
+
+ req = ptlrpc_request_alloc(class_exp2cliimp(exp),
+ &RQF_MDS_REINT_CREATE_RMT_ACL);
+ if (req == NULL) {
+ ldlm_lock_list_put(&cancels, l_bl_ast, count);
+ return -ENOMEM;
+ }
+ mdc_set_capa_size(req, &RMF_CAPA1, op_data->op_capa1);
+ req_capsule_set_size(&req->rq_pill, &RMF_NAME, RCL_CLIENT,
+ op_data->op_namelen + 1);
+ req_capsule_set_size(&req->rq_pill, &RMF_EADATA, RCL_CLIENT,
+ data && datalen ? datalen : 0);
+
+ rc = mdc_prep_elc_req(exp, req, MDS_REINT, &cancels, count);
+ if (rc) {
+ ptlrpc_request_free(req);
+ return rc;
+ }
+
+ /*
+ * mdc_create_pack() fills msg->bufs[1] with name and msg->bufs[2] with
+ * tgt, for symlinks or lov MD data.
+ */
+ mdc_create_pack(req, op_data, data, datalen, mode, uid,
+ gid, cap_effective, rdev);
+
+ ptlrpc_request_set_replen(req);
+
+ /* ask ptlrpc not to resend on EINPROGRESS since we have our own retry
+ * logic here */
+ req->rq_no_retry_einprogress = 1;
+
+ if (resends) {
+ req->rq_generation_set = 1;
+ req->rq_import_generation = generation;
+ req->rq_sent = get_seconds() + resends;
+ }
+ level = LUSTRE_IMP_FULL;
+ resend:
+ rc = mdc_reint(req, exp->exp_obd->u.cli.cl_rpc_lock, level);
+
+ /* Resend if we were told to. */
+ if (rc == -ERESTARTSYS) {
+ level = LUSTRE_IMP_RECOVER;
+ goto resend;
+ } else if (rc == -EINPROGRESS) {
+ /* Retry create infinitely until succeed or get other
+ * error code. */
+ ptlrpc_req_finished(req);
+ resends++;
+
+ CDEBUG(D_HA, "%s: resend:%d create on "DFID"/"DFID"\n",
+ exp->exp_obd->obd_name, resends,
+ PFID(&op_data->op_fid1), PFID(&op_data->op_fid2));
+
+ if (generation == import->imp_generation) {
+ goto rebuild;
+ } else {
+ CDEBUG(D_HA, "resend cross eviction\n");
+ return -EIO;
+ }
+ } else if (rc == 0) {
+ struct mdt_body *body;
+ struct lustre_capa *capa;
+
+ body = req_capsule_server_get(&req->rq_pill, &RMF_MDT_BODY);
+ LASSERT(body);
+ if (body->valid & OBD_MD_FLMDSCAPA) {
+ capa = req_capsule_server_get(&req->rq_pill,
+ &RMF_CAPA1);
+ if (capa == NULL)
+ rc = -EPROTO;
+ }
+ }
+
+ *request = req;
+ return rc;
+}
+
+int mdc_unlink(struct obd_export *exp, struct md_op_data *op_data,
+ struct ptlrpc_request **request)
+{
+ LIST_HEAD(cancels);
+ struct obd_device *obd = class_exp2obd(exp);
+ struct ptlrpc_request *req = *request;
+ int count = 0, rc;
+
+ LASSERT(req == NULL);
+
+ if ((op_data->op_flags & MF_MDC_CANCEL_FID1) &&
+ (fid_is_sane(&op_data->op_fid1)) &&
+ !OBD_FAIL_CHECK(OBD_FAIL_LDLM_BL_CALLBACK_NET))
+ count = mdc_resource_get_unused(exp, &op_data->op_fid1,
+ &cancels, LCK_EX,
+ MDS_INODELOCK_UPDATE);
+ if ((op_data->op_flags & MF_MDC_CANCEL_FID3) &&
+ (fid_is_sane(&op_data->op_fid3)) &&
+ !OBD_FAIL_CHECK(OBD_FAIL_LDLM_BL_CALLBACK_NET))
+ count += mdc_resource_get_unused(exp, &op_data->op_fid3,
+ &cancels, LCK_EX,
+ MDS_INODELOCK_FULL);
+ req = ptlrpc_request_alloc(class_exp2cliimp(exp),
+ &RQF_MDS_REINT_UNLINK);
+ if (req == NULL) {
+ ldlm_lock_list_put(&cancels, l_bl_ast, count);
+ return -ENOMEM;
+ }
+ mdc_set_capa_size(req, &RMF_CAPA1, op_data->op_capa1);
+ req_capsule_set_size(&req->rq_pill, &RMF_NAME, RCL_CLIENT,
+ op_data->op_namelen + 1);
+
+ rc = mdc_prep_elc_req(exp, req, MDS_REINT, &cancels, count);
+ if (rc) {
+ ptlrpc_request_free(req);
+ return rc;
+ }
+
+ mdc_unlink_pack(req, op_data);
+
+ req_capsule_set_size(&req->rq_pill, &RMF_MDT_MD, RCL_SERVER,
+ obd->u.cli.cl_default_mds_easize);
+ req_capsule_set_size(&req->rq_pill, &RMF_LOGCOOKIES, RCL_SERVER,
+ obd->u.cli.cl_default_mds_cookiesize);
+ ptlrpc_request_set_replen(req);
+
+ *request = req;
+
+ rc = mdc_reint(req, obd->u.cli.cl_rpc_lock, LUSTRE_IMP_FULL);
+ if (rc == -ERESTARTSYS)
+ rc = 0;
+ return rc;
+}
+
+int mdc_link(struct obd_export *exp, struct md_op_data *op_data,
+ struct ptlrpc_request **request)
+{
+ LIST_HEAD(cancels);
+ struct obd_device *obd = exp->exp_obd;
+ struct ptlrpc_request *req;
+ int count = 0, rc;
+
+ if ((op_data->op_flags & MF_MDC_CANCEL_FID2) &&
+ (fid_is_sane(&op_data->op_fid2)))
+ count = mdc_resource_get_unused(exp, &op_data->op_fid2,
+ &cancels, LCK_EX,
+ MDS_INODELOCK_UPDATE);
+ if ((op_data->op_flags & MF_MDC_CANCEL_FID1) &&
+ (fid_is_sane(&op_data->op_fid1)))
+ count += mdc_resource_get_unused(exp, &op_data->op_fid1,
+ &cancels, LCK_EX,
+ MDS_INODELOCK_UPDATE);
+
+ req = ptlrpc_request_alloc(class_exp2cliimp(exp), &RQF_MDS_REINT_LINK);
+ if (req == NULL) {
+ ldlm_lock_list_put(&cancels, l_bl_ast, count);
+ return -ENOMEM;
+ }
+ mdc_set_capa_size(req, &RMF_CAPA1, op_data->op_capa1);
+ mdc_set_capa_size(req, &RMF_CAPA2, op_data->op_capa2);
+ req_capsule_set_size(&req->rq_pill, &RMF_NAME, RCL_CLIENT,
+ op_data->op_namelen + 1);
+
+ rc = mdc_prep_elc_req(exp, req, MDS_REINT, &cancels, count);
+ if (rc) {
+ ptlrpc_request_free(req);
+ return rc;
+ }
+
+ mdc_link_pack(req, op_data);
+ ptlrpc_request_set_replen(req);
+
+ rc = mdc_reint(req, obd->u.cli.cl_rpc_lock, LUSTRE_IMP_FULL);
+ *request = req;
+ if (rc == -ERESTARTSYS)
+ rc = 0;
+
+ return rc;
+}
+
+int mdc_rename(struct obd_export *exp, struct md_op_data *op_data,
+ const char *old, int oldlen, const char *new, int newlen,
+ struct ptlrpc_request **request)
+{
+ LIST_HEAD(cancels);
+ struct obd_device *obd = exp->exp_obd;
+ struct ptlrpc_request *req;
+ int count = 0, rc;
+
+ if ((op_data->op_flags & MF_MDC_CANCEL_FID1) &&
+ (fid_is_sane(&op_data->op_fid1)))
+ count = mdc_resource_get_unused(exp, &op_data->op_fid1,
+ &cancels, LCK_EX,
+ MDS_INODELOCK_UPDATE);
+ if ((op_data->op_flags & MF_MDC_CANCEL_FID2) &&
+ (fid_is_sane(&op_data->op_fid2)))
+ count += mdc_resource_get_unused(exp, &op_data->op_fid2,
+ &cancels, LCK_EX,
+ MDS_INODELOCK_UPDATE);
+ if ((op_data->op_flags & MF_MDC_CANCEL_FID3) &&
+ (fid_is_sane(&op_data->op_fid3)))
+ count += mdc_resource_get_unused(exp, &op_data->op_fid3,
+ &cancels, LCK_EX,
+ MDS_INODELOCK_LOOKUP);
+ if ((op_data->op_flags & MF_MDC_CANCEL_FID4) &&
+ (fid_is_sane(&op_data->op_fid4)))
+ count += mdc_resource_get_unused(exp, &op_data->op_fid4,
+ &cancels, LCK_EX,
+ MDS_INODELOCK_FULL);
+
+ req = ptlrpc_request_alloc(class_exp2cliimp(exp),
+ &RQF_MDS_REINT_RENAME);
+ if (req == NULL) {
+ ldlm_lock_list_put(&cancels, l_bl_ast, count);
+ return -ENOMEM;
+ }
+
+ mdc_set_capa_size(req, &RMF_CAPA1, op_data->op_capa1);
+ mdc_set_capa_size(req, &RMF_CAPA2, op_data->op_capa2);
+ req_capsule_set_size(&req->rq_pill, &RMF_NAME, RCL_CLIENT, oldlen + 1);
+ req_capsule_set_size(&req->rq_pill, &RMF_SYMTGT, RCL_CLIENT, newlen+1);
+
+ rc = mdc_prep_elc_req(exp, req, MDS_REINT, &cancels, count);
+ if (rc) {
+ ptlrpc_request_free(req);
+ return rc;
+ }
+
+ if (exp_connect_cancelset(exp) && req)
+ ldlm_cli_cancel_list(&cancels, count, req, 0);
+
+ mdc_rename_pack(req, op_data, old, oldlen, new, newlen);
+
+ req_capsule_set_size(&req->rq_pill, &RMF_MDT_MD, RCL_SERVER,
+ obd->u.cli.cl_default_mds_easize);
+ req_capsule_set_size(&req->rq_pill, &RMF_LOGCOOKIES, RCL_SERVER,
+ obd->u.cli.cl_default_mds_cookiesize);
+ ptlrpc_request_set_replen(req);
+
+ rc = mdc_reint(req, obd->u.cli.cl_rpc_lock, LUSTRE_IMP_FULL);
+ *request = req;
+ if (rc == -ERESTARTSYS)
+ rc = 0;
+
+ return rc;
+}
diff --git a/drivers/staging/lustre/lustre/mdc/mdc_request.c b/drivers/staging/lustre/lustre/mdc/mdc_request.c
new file mode 100644
index 000000000..f8ef5fe5e
--- /dev/null
+++ b/drivers/staging/lustre/lustre/mdc/mdc_request.c
@@ -0,0 +1,2731 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2001, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2011, 2012, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ */
+
+#define DEBUG_SUBSYSTEM S_MDC
+
+# include <linux/module.h>
+# include <linux/pagemap.h>
+# include <linux/miscdevice.h>
+# include <linux/init.h>
+# include <linux/utsname.h>
+
+#include "../include/lustre_acl.h"
+#include "../include/obd_class.h"
+#include "../include/lustre_fid.h"
+#include "../include/lprocfs_status.h"
+#include "../include/lustre_param.h"
+#include "../include/lustre_log.h"
+
+#include "mdc_internal.h"
+
+#define REQUEST_MINOR 244
+
+struct mdc_renew_capa_args {
+ struct obd_capa *ra_oc;
+ renew_capa_cb_t ra_cb;
+};
+
+static int mdc_cleanup(struct obd_device *obd);
+
+static int mdc_unpack_capa(struct obd_export *exp, struct ptlrpc_request *req,
+ const struct req_msg_field *field, struct obd_capa **oc)
+{
+ struct lustre_capa *capa;
+ struct obd_capa *c;
+
+ /* swabbed already in mdc_enqueue */
+ capa = req_capsule_server_get(&req->rq_pill, field);
+ if (capa == NULL)
+ return -EPROTO;
+
+ c = alloc_capa(CAPA_SITE_CLIENT);
+ if (IS_ERR(c)) {
+ CDEBUG(D_INFO, "alloc capa failed!\n");
+ return PTR_ERR(c);
+ } else {
+ c->c_capa = *capa;
+ *oc = c;
+ return 0;
+ }
+}
+
+static inline int mdc_queue_wait(struct ptlrpc_request *req)
+{
+ struct client_obd *cli = &req->rq_import->imp_obd->u.cli;
+ int rc;
+
+ /* mdc_enter_request() ensures that this client has no more
+ * than cl_max_rpcs_in_flight RPCs simultaneously inf light
+ * against an MDT. */
+ rc = mdc_enter_request(cli);
+ if (rc != 0)
+ return rc;
+
+ rc = ptlrpc_queue_wait(req);
+ mdc_exit_request(cli);
+
+ return rc;
+}
+
+/* Helper that implements most of mdc_getstatus and signal_completed_replay. */
+/* XXX this should become mdc_get_info("key"), sending MDS_GET_INFO RPC */
+static int send_getstatus(struct obd_import *imp, struct lu_fid *rootfid,
+ struct obd_capa **pc, int level, int msg_flags)
+{
+ struct ptlrpc_request *req;
+ struct mdt_body *body;
+ int rc;
+
+ req = ptlrpc_request_alloc_pack(imp, &RQF_MDS_GETSTATUS,
+ LUSTRE_MDS_VERSION, MDS_GETSTATUS);
+ if (req == NULL)
+ return -ENOMEM;
+
+ mdc_pack_body(req, NULL, NULL, 0, 0, -1, 0);
+ lustre_msg_add_flags(req->rq_reqmsg, msg_flags);
+ req->rq_send_state = level;
+
+ ptlrpc_request_set_replen(req);
+
+ rc = ptlrpc_queue_wait(req);
+ if (rc)
+ goto out;
+
+ body = req_capsule_server_get(&req->rq_pill, &RMF_MDT_BODY);
+ if (body == NULL) {
+ rc = -EPROTO;
+ goto out;
+ }
+
+ if (body->valid & OBD_MD_FLMDSCAPA) {
+ rc = mdc_unpack_capa(NULL, req, &RMF_CAPA1, pc);
+ if (rc)
+ goto out;
+ }
+
+ *rootfid = body->fid1;
+ CDEBUG(D_NET,
+ "root fid="DFID", last_committed=%llu\n",
+ PFID(rootfid),
+ lustre_msg_get_last_committed(req->rq_repmsg));
+out:
+ ptlrpc_req_finished(req);
+ return rc;
+}
+
+/* This should be mdc_get_info("rootfid") */
+static int mdc_getstatus(struct obd_export *exp, struct lu_fid *rootfid,
+ struct obd_capa **pc)
+{
+ return send_getstatus(class_exp2cliimp(exp), rootfid, pc,
+ LUSTRE_IMP_FULL, 0);
+}
+
+/*
+ * This function now is known to always saying that it will receive 4 buffers
+ * from server. Even for cases when acl_size and md_size is zero, RPC header
+ * will contain 4 fields and RPC itself will contain zero size fields. This is
+ * because mdt_getattr*() _always_ returns 4 fields, but if acl is not needed
+ * and thus zero, it shrinks it, making zero size. The same story about
+ * md_size. And this is course of problem when client waits for smaller number
+ * of fields. This issue will be fixed later when client gets aware of RPC
+ * layouts. --umka
+ */
+static int mdc_getattr_common(struct obd_export *exp,
+ struct ptlrpc_request *req)
+{
+ struct req_capsule *pill = &req->rq_pill;
+ struct mdt_body *body;
+ void *eadata;
+ int rc;
+
+ /* Request message already built. */
+ rc = ptlrpc_queue_wait(req);
+ if (rc != 0)
+ return rc;
+
+ /* sanity check for the reply */
+ body = req_capsule_server_get(pill, &RMF_MDT_BODY);
+ if (body == NULL)
+ return -EPROTO;
+
+ CDEBUG(D_NET, "mode: %o\n", body->mode);
+
+ if (body->eadatasize != 0) {
+ mdc_update_max_ea_from_body(exp, body);
+
+ eadata = req_capsule_server_sized_get(pill, &RMF_MDT_MD,
+ body->eadatasize);
+ if (eadata == NULL)
+ return -EPROTO;
+ }
+
+ if (body->valid & OBD_MD_FLRMTPERM) {
+ struct mdt_remote_perm *perm;
+
+ LASSERT(client_is_remote(exp));
+ perm = req_capsule_server_swab_get(pill, &RMF_ACL,
+ lustre_swab_mdt_remote_perm);
+ if (perm == NULL)
+ return -EPROTO;
+ }
+
+ if (body->valid & OBD_MD_FLMDSCAPA) {
+ struct lustre_capa *capa;
+
+ capa = req_capsule_server_get(pill, &RMF_CAPA1);
+ if (capa == NULL)
+ return -EPROTO;
+ }
+
+ return 0;
+}
+
+static int mdc_getattr(struct obd_export *exp, struct md_op_data *op_data,
+ struct ptlrpc_request **request)
+{
+ struct ptlrpc_request *req;
+ int rc;
+
+ /* Single MDS without an LMV case */
+ if (op_data->op_flags & MF_GET_MDT_IDX) {
+ op_data->op_mds = 0;
+ return 0;
+ }
+ *request = NULL;
+ req = ptlrpc_request_alloc(class_exp2cliimp(exp), &RQF_MDS_GETATTR);
+ if (req == NULL)
+ return -ENOMEM;
+
+ mdc_set_capa_size(req, &RMF_CAPA1, op_data->op_capa1);
+
+ rc = ptlrpc_request_pack(req, LUSTRE_MDS_VERSION, MDS_GETATTR);
+ if (rc) {
+ ptlrpc_request_free(req);
+ return rc;
+ }
+
+ mdc_pack_body(req, &op_data->op_fid1, op_data->op_capa1,
+ op_data->op_valid, op_data->op_mode, -1, 0);
+
+ req_capsule_set_size(&req->rq_pill, &RMF_MDT_MD, RCL_SERVER,
+ op_data->op_mode);
+ if (op_data->op_valid & OBD_MD_FLRMTPERM) {
+ LASSERT(client_is_remote(exp));
+ req_capsule_set_size(&req->rq_pill, &RMF_ACL, RCL_SERVER,
+ sizeof(struct mdt_remote_perm));
+ }
+ ptlrpc_request_set_replen(req);
+
+ rc = mdc_getattr_common(exp, req);
+ if (rc)
+ ptlrpc_req_finished(req);
+ else
+ *request = req;
+ return rc;
+}
+
+static int mdc_getattr_name(struct obd_export *exp, struct md_op_data *op_data,
+ struct ptlrpc_request **request)
+{
+ struct ptlrpc_request *req;
+ int rc;
+
+ *request = NULL;
+ req = ptlrpc_request_alloc(class_exp2cliimp(exp),
+ &RQF_MDS_GETATTR_NAME);
+ if (req == NULL)
+ return -ENOMEM;
+
+ mdc_set_capa_size(req, &RMF_CAPA1, op_data->op_capa1);
+ req_capsule_set_size(&req->rq_pill, &RMF_NAME, RCL_CLIENT,
+ op_data->op_namelen + 1);
+
+ rc = ptlrpc_request_pack(req, LUSTRE_MDS_VERSION, MDS_GETATTR_NAME);
+ if (rc) {
+ ptlrpc_request_free(req);
+ return rc;
+ }
+
+ mdc_pack_body(req, &op_data->op_fid1, op_data->op_capa1,
+ op_data->op_valid, op_data->op_mode,
+ op_data->op_suppgids[0], 0);
+
+ if (op_data->op_name) {
+ char *name = req_capsule_client_get(&req->rq_pill, &RMF_NAME);
+
+ LASSERT(strnlen(op_data->op_name, op_data->op_namelen) ==
+ op_data->op_namelen);
+ memcpy(name, op_data->op_name, op_data->op_namelen);
+ }
+
+ req_capsule_set_size(&req->rq_pill, &RMF_MDT_MD, RCL_SERVER,
+ op_data->op_mode);
+ ptlrpc_request_set_replen(req);
+
+ rc = mdc_getattr_common(exp, req);
+ if (rc)
+ ptlrpc_req_finished(req);
+ else
+ *request = req;
+ return rc;
+}
+
+static int mdc_is_subdir(struct obd_export *exp,
+ const struct lu_fid *pfid,
+ const struct lu_fid *cfid,
+ struct ptlrpc_request **request)
+{
+ struct ptlrpc_request *req;
+ int rc;
+
+ *request = NULL;
+ req = ptlrpc_request_alloc_pack(class_exp2cliimp(exp),
+ &RQF_MDS_IS_SUBDIR, LUSTRE_MDS_VERSION,
+ MDS_IS_SUBDIR);
+ if (req == NULL)
+ return -ENOMEM;
+
+ mdc_is_subdir_pack(req, pfid, cfid, 0);
+ ptlrpc_request_set_replen(req);
+
+ rc = ptlrpc_queue_wait(req);
+ if (rc && rc != -EREMOTE)
+ ptlrpc_req_finished(req);
+ else
+ *request = req;
+ return rc;
+}
+
+static int mdc_xattr_common(struct obd_export *exp,
+ const struct req_format *fmt,
+ const struct lu_fid *fid,
+ struct obd_capa *oc, int opcode, u64 valid,
+ const char *xattr_name, const char *input,
+ int input_size, int output_size, int flags,
+ __u32 suppgid, struct ptlrpc_request **request)
+{
+ struct ptlrpc_request *req;
+ int xattr_namelen = 0;
+ char *tmp;
+ int rc;
+
+ *request = NULL;
+ req = ptlrpc_request_alloc(class_exp2cliimp(exp), fmt);
+ if (req == NULL)
+ return -ENOMEM;
+
+ mdc_set_capa_size(req, &RMF_CAPA1, oc);
+ if (xattr_name) {
+ xattr_namelen = strlen(xattr_name) + 1;
+ req_capsule_set_size(&req->rq_pill, &RMF_NAME, RCL_CLIENT,
+ xattr_namelen);
+ }
+ if (input_size) {
+ LASSERT(input);
+ req_capsule_set_size(&req->rq_pill, &RMF_EADATA, RCL_CLIENT,
+ input_size);
+ }
+
+ /* Flush local XATTR locks to get rid of a possible cancel RPC */
+ if (opcode == MDS_REINT && fid_is_sane(fid) &&
+ exp->exp_connect_data.ocd_ibits_known & MDS_INODELOCK_XATTR) {
+ LIST_HEAD(cancels);
+ int count;
+
+ /* Without that packing would fail */
+ if (input_size == 0)
+ req_capsule_set_size(&req->rq_pill, &RMF_EADATA,
+ RCL_CLIENT, 0);
+
+ count = mdc_resource_get_unused(exp, fid,
+ &cancels, LCK_EX,
+ MDS_INODELOCK_XATTR);
+
+ rc = mdc_prep_elc_req(exp, req, MDS_REINT, &cancels, count);
+ if (rc) {
+ ptlrpc_request_free(req);
+ return rc;
+ }
+ } else {
+ rc = ptlrpc_request_pack(req, LUSTRE_MDS_VERSION, opcode);
+ if (rc) {
+ ptlrpc_request_free(req);
+ return rc;
+ }
+ }
+
+ if (opcode == MDS_REINT) {
+ struct mdt_rec_setxattr *rec;
+
+ CLASSERT(sizeof(struct mdt_rec_setxattr) ==
+ sizeof(struct mdt_rec_reint));
+ rec = req_capsule_client_get(&req->rq_pill, &RMF_REC_REINT);
+ rec->sx_opcode = REINT_SETXATTR;
+ rec->sx_fsuid = from_kuid(&init_user_ns, current_fsuid());
+ rec->sx_fsgid = from_kgid(&init_user_ns, current_fsgid());
+ rec->sx_cap = cfs_curproc_cap_pack();
+ rec->sx_suppgid1 = suppgid;
+ rec->sx_suppgid2 = -1;
+ rec->sx_fid = *fid;
+ rec->sx_valid = valid | OBD_MD_FLCTIME;
+ rec->sx_time = get_seconds();
+ rec->sx_size = output_size;
+ rec->sx_flags = flags;
+
+ mdc_pack_capa(req, &RMF_CAPA1, oc);
+ } else {
+ mdc_pack_body(req, fid, oc, valid, output_size, suppgid, flags);
+ }
+
+ if (xattr_name) {
+ tmp = req_capsule_client_get(&req->rq_pill, &RMF_NAME);
+ memcpy(tmp, xattr_name, xattr_namelen);
+ }
+ if (input_size) {
+ tmp = req_capsule_client_get(&req->rq_pill, &RMF_EADATA);
+ memcpy(tmp, input, input_size);
+ }
+
+ if (req_capsule_has_field(&req->rq_pill, &RMF_EADATA, RCL_SERVER))
+ req_capsule_set_size(&req->rq_pill, &RMF_EADATA,
+ RCL_SERVER, output_size);
+ ptlrpc_request_set_replen(req);
+
+ /* make rpc */
+ if (opcode == MDS_REINT)
+ mdc_get_rpc_lock(exp->exp_obd->u.cli.cl_rpc_lock, NULL);
+
+ rc = ptlrpc_queue_wait(req);
+
+ if (opcode == MDS_REINT)
+ mdc_put_rpc_lock(exp->exp_obd->u.cli.cl_rpc_lock, NULL);
+
+ if (rc)
+ ptlrpc_req_finished(req);
+ else
+ *request = req;
+ return rc;
+}
+
+static int mdc_setxattr(struct obd_export *exp, const struct lu_fid *fid,
+ struct obd_capa *oc, u64 valid, const char *xattr_name,
+ const char *input, int input_size, int output_size,
+ int flags, __u32 suppgid, struct ptlrpc_request **request)
+{
+ return mdc_xattr_common(exp, &RQF_MDS_REINT_SETXATTR,
+ fid, oc, MDS_REINT, valid, xattr_name,
+ input, input_size, output_size, flags,
+ suppgid, request);
+}
+
+static int mdc_getxattr(struct obd_export *exp, const struct lu_fid *fid,
+ struct obd_capa *oc, u64 valid, const char *xattr_name,
+ const char *input, int input_size, int output_size,
+ int flags, struct ptlrpc_request **request)
+{
+ return mdc_xattr_common(exp, &RQF_MDS_GETXATTR,
+ fid, oc, MDS_GETXATTR, valid, xattr_name,
+ input, input_size, output_size, flags,
+ -1, request);
+}
+
+#ifdef CONFIG_FS_POSIX_ACL
+static int mdc_unpack_acl(struct ptlrpc_request *req, struct lustre_md *md)
+{
+ struct req_capsule *pill = &req->rq_pill;
+ struct mdt_body *body = md->body;
+ struct posix_acl *acl;
+ void *buf;
+ int rc;
+
+ if (!body->aclsize)
+ return 0;
+
+ buf = req_capsule_server_sized_get(pill, &RMF_ACL, body->aclsize);
+
+ if (!buf)
+ return -EPROTO;
+
+ acl = posix_acl_from_xattr(&init_user_ns, buf, body->aclsize);
+ if (acl == NULL)
+ return 0;
+
+ if (IS_ERR(acl)) {
+ rc = PTR_ERR(acl);
+ CERROR("convert xattr to acl: %d\n", rc);
+ return rc;
+ }
+
+ rc = posix_acl_valid(acl);
+ if (rc) {
+ CERROR("validate acl: %d\n", rc);
+ posix_acl_release(acl);
+ return rc;
+ }
+
+ md->posix_acl = acl;
+ return 0;
+}
+#else
+#define mdc_unpack_acl(req, md) 0
+#endif
+
+int mdc_get_lustre_md(struct obd_export *exp, struct ptlrpc_request *req,
+ struct obd_export *dt_exp, struct obd_export *md_exp,
+ struct lustre_md *md)
+{
+ struct req_capsule *pill = &req->rq_pill;
+ int rc;
+
+ LASSERT(md);
+ memset(md, 0, sizeof(*md));
+
+ md->body = req_capsule_server_get(pill, &RMF_MDT_BODY);
+ LASSERT(md->body != NULL);
+
+ if (md->body->valid & OBD_MD_FLEASIZE) {
+ int lmmsize;
+ struct lov_mds_md *lmm;
+
+ if (!S_ISREG(md->body->mode)) {
+ CDEBUG(D_INFO,
+ "OBD_MD_FLEASIZE set, should be a regular file, but is not\n");
+ rc = -EPROTO;
+ goto out;
+ }
+
+ if (md->body->eadatasize == 0) {
+ CDEBUG(D_INFO,
+ "OBD_MD_FLEASIZE set, but eadatasize 0\n");
+ rc = -EPROTO;
+ goto out;
+ }
+ lmmsize = md->body->eadatasize;
+ lmm = req_capsule_server_sized_get(pill, &RMF_MDT_MD, lmmsize);
+ if (!lmm) {
+ rc = -EPROTO;
+ goto out;
+ }
+
+ rc = obd_unpackmd(dt_exp, &md->lsm, lmm, lmmsize);
+ if (rc < 0)
+ goto out;
+
+ if (rc < sizeof(*md->lsm)) {
+ CDEBUG(D_INFO,
+ "lsm size too small: rc < sizeof (*md->lsm) (%d < %d)\n",
+ rc, (int)sizeof(*md->lsm));
+ rc = -EPROTO;
+ goto out;
+ }
+
+ } else if (md->body->valid & OBD_MD_FLDIREA) {
+ int lmvsize;
+ struct lov_mds_md *lmv;
+
+ if (!S_ISDIR(md->body->mode)) {
+ CDEBUG(D_INFO,
+ "OBD_MD_FLDIREA set, should be a directory, but is not\n");
+ rc = -EPROTO;
+ goto out;
+ }
+
+ if (md->body->eadatasize == 0) {
+ CDEBUG(D_INFO,
+ "OBD_MD_FLDIREA is set, but eadatasize 0\n");
+ return -EPROTO;
+ }
+ if (md->body->valid & OBD_MD_MEA) {
+ lmvsize = md->body->eadatasize;
+ lmv = req_capsule_server_sized_get(pill, &RMF_MDT_MD,
+ lmvsize);
+ if (!lmv) {
+ rc = -EPROTO;
+ goto out;
+ }
+
+ rc = obd_unpackmd(md_exp, (void *)&md->mea, lmv,
+ lmvsize);
+ if (rc < 0)
+ goto out;
+
+ if (rc < sizeof(*md->mea)) {
+ CDEBUG(D_INFO,
+ "size too small: rc < sizeof(*md->mea) (%d < %d)\n",
+ rc, (int)sizeof(*md->mea));
+ rc = -EPROTO;
+ goto out;
+ }
+ }
+ }
+ rc = 0;
+
+ if (md->body->valid & OBD_MD_FLRMTPERM) {
+ /* remote permission */
+ LASSERT(client_is_remote(exp));
+ md->remote_perm = req_capsule_server_swab_get(pill, &RMF_ACL,
+ lustre_swab_mdt_remote_perm);
+ if (!md->remote_perm) {
+ rc = -EPROTO;
+ goto out;
+ }
+ } else if (md->body->valid & OBD_MD_FLACL) {
+ /* for ACL, it's possible that FLACL is set but aclsize is zero.
+ * only when aclsize != 0 there's an actual segment for ACL
+ * in reply buffer.
+ */
+ if (md->body->aclsize) {
+ rc = mdc_unpack_acl(req, md);
+ if (rc)
+ goto out;
+#ifdef CONFIG_FS_POSIX_ACL
+ } else {
+ md->posix_acl = NULL;
+#endif
+ }
+ }
+ if (md->body->valid & OBD_MD_FLMDSCAPA) {
+ struct obd_capa *oc = NULL;
+
+ rc = mdc_unpack_capa(NULL, req, &RMF_CAPA1, &oc);
+ if (rc)
+ goto out;
+ md->mds_capa = oc;
+ }
+
+ if (md->body->valid & OBD_MD_FLOSSCAPA) {
+ struct obd_capa *oc = NULL;
+
+ rc = mdc_unpack_capa(NULL, req, &RMF_CAPA2, &oc);
+ if (rc)
+ goto out;
+ md->oss_capa = oc;
+ }
+
+out:
+ if (rc) {
+ if (md->oss_capa) {
+ capa_put(md->oss_capa);
+ md->oss_capa = NULL;
+ }
+ if (md->mds_capa) {
+ capa_put(md->mds_capa);
+ md->mds_capa = NULL;
+ }
+#ifdef CONFIG_FS_POSIX_ACL
+ posix_acl_release(md->posix_acl);
+#endif
+ if (md->lsm)
+ obd_free_memmd(dt_exp, &md->lsm);
+ }
+ return rc;
+}
+
+int mdc_free_lustre_md(struct obd_export *exp, struct lustre_md *md)
+{
+ return 0;
+}
+
+/**
+ * Handles both OPEN and SETATTR RPCs for OPEN-CLOSE and SETATTR-DONE_WRITING
+ * RPC chains.
+ */
+void mdc_replay_open(struct ptlrpc_request *req)
+{
+ struct md_open_data *mod = req->rq_cb_data;
+ struct ptlrpc_request *close_req;
+ struct obd_client_handle *och;
+ struct lustre_handle old;
+ struct mdt_body *body;
+
+ if (mod == NULL) {
+ DEBUG_REQ(D_ERROR, req,
+ "Can't properly replay without open data.");
+ return;
+ }
+
+ body = req_capsule_server_get(&req->rq_pill, &RMF_MDT_BODY);
+ LASSERT(body != NULL);
+
+ och = mod->mod_och;
+ if (och != NULL) {
+ struct lustre_handle *file_fh;
+
+ LASSERT(och->och_magic == OBD_CLIENT_HANDLE_MAGIC);
+
+ file_fh = &och->och_fh;
+ CDEBUG(D_HA, "updating handle from %#llx to %#llx\n",
+ file_fh->cookie, body->handle.cookie);
+ old = *file_fh;
+ *file_fh = body->handle;
+ }
+ close_req = mod->mod_close_req;
+ if (close_req != NULL) {
+ __u32 opc = lustre_msg_get_opc(close_req->rq_reqmsg);
+ struct mdt_ioepoch *epoch;
+
+ LASSERT(opc == MDS_CLOSE || opc == MDS_DONE_WRITING);
+ epoch = req_capsule_client_get(&close_req->rq_pill,
+ &RMF_MDT_EPOCH);
+ LASSERT(epoch);
+
+ if (och != NULL)
+ LASSERT(!memcmp(&old, &epoch->handle, sizeof(old)));
+ DEBUG_REQ(D_HA, close_req, "updating close body with new fh");
+ epoch->handle = body->handle;
+ }
+}
+
+void mdc_commit_open(struct ptlrpc_request *req)
+{
+ struct md_open_data *mod = req->rq_cb_data;
+
+ if (mod == NULL)
+ return;
+
+ /**
+ * No need to touch md_open_data::mod_och, it holds a reference on
+ * \var mod and will zero references to each other, \var mod will be
+ * freed after that when md_open_data::mod_och will put the reference.
+ */
+
+ /**
+ * Do not let open request to disappear as it still may be needed
+ * for close rpc to happen (it may happen on evict only, otherwise
+ * ptlrpc_request::rq_replay does not let mdc_commit_open() to be
+ * called), just mark this rpc as committed to distinguish these 2
+ * cases, see mdc_close() for details. The open request reference will
+ * be put along with freeing \var mod.
+ */
+ ptlrpc_request_addref(req);
+ spin_lock(&req->rq_lock);
+ req->rq_committed = 1;
+ spin_unlock(&req->rq_lock);
+ req->rq_cb_data = NULL;
+ obd_mod_put(mod);
+}
+
+int mdc_set_open_replay_data(struct obd_export *exp,
+ struct obd_client_handle *och,
+ struct lookup_intent *it)
+{
+ struct md_open_data *mod;
+ struct mdt_rec_create *rec;
+ struct mdt_body *body;
+ struct ptlrpc_request *open_req = it->d.lustre.it_data;
+ struct obd_import *imp = open_req->rq_import;
+
+ if (!open_req->rq_replay)
+ return 0;
+
+ rec = req_capsule_client_get(&open_req->rq_pill, &RMF_REC_REINT);
+ body = req_capsule_server_get(&open_req->rq_pill, &RMF_MDT_BODY);
+ LASSERT(rec != NULL);
+ /* Incoming message in my byte order (it's been swabbed). */
+ /* Outgoing messages always in my byte order. */
+ LASSERT(body != NULL);
+
+ /* Only if the import is replayable, we set replay_open data */
+ if (och && imp->imp_replayable) {
+ mod = obd_mod_alloc();
+ if (mod == NULL) {
+ DEBUG_REQ(D_ERROR, open_req,
+ "Can't allocate md_open_data");
+ return 0;
+ }
+
+ /**
+ * Take a reference on \var mod, to be freed on mdc_close().
+ * It protects \var mod from being freed on eviction (commit
+ * callback is called despite rq_replay flag).
+ * Another reference for \var och.
+ */
+ obd_mod_get(mod);
+ obd_mod_get(mod);
+
+ spin_lock(&open_req->rq_lock);
+ och->och_mod = mod;
+ mod->mod_och = och;
+ mod->mod_is_create = it_disposition(it, DISP_OPEN_CREATE) ||
+ it_disposition(it, DISP_OPEN_STRIPE);
+ mod->mod_open_req = open_req;
+ open_req->rq_cb_data = mod;
+ open_req->rq_commit_cb = mdc_commit_open;
+ spin_unlock(&open_req->rq_lock);
+ }
+
+ rec->cr_fid2 = body->fid1;
+ rec->cr_ioepoch = body->ioepoch;
+ rec->cr_old_handle.cookie = body->handle.cookie;
+ open_req->rq_replay_cb = mdc_replay_open;
+ if (!fid_is_sane(&body->fid1)) {
+ DEBUG_REQ(D_ERROR, open_req,
+ "Saving replay request with insane fid");
+ LBUG();
+ }
+
+ DEBUG_REQ(D_RPCTRACE, open_req, "Set up open replay data");
+ return 0;
+}
+
+static void mdc_free_open(struct md_open_data *mod)
+{
+ int committed = 0;
+
+ if (mod->mod_is_create == 0 &&
+ imp_connect_disp_stripe(mod->mod_open_req->rq_import))
+ committed = 1;
+
+ LASSERT(mod->mod_open_req->rq_replay == 0);
+
+ DEBUG_REQ(D_RPCTRACE, mod->mod_open_req, "free open request\n");
+
+ ptlrpc_request_committed(mod->mod_open_req, committed);
+ if (mod->mod_close_req)
+ ptlrpc_request_committed(mod->mod_close_req, committed);
+}
+
+int mdc_clear_open_replay_data(struct obd_export *exp,
+ struct obd_client_handle *och)
+{
+ struct md_open_data *mod = och->och_mod;
+
+ /**
+ * It is possible to not have \var mod in a case of eviction between
+ * lookup and ll_file_open().
+ **/
+ if (mod == NULL)
+ return 0;
+
+ LASSERT(mod != LP_POISON);
+ LASSERT(mod->mod_open_req != NULL);
+ mdc_free_open(mod);
+
+ mod->mod_och = NULL;
+ och->och_mod = NULL;
+ obd_mod_put(mod);
+
+ return 0;
+}
+
+/* Prepares the request for the replay by the given reply */
+static void mdc_close_handle_reply(struct ptlrpc_request *req,
+ struct md_op_data *op_data, int rc) {
+ struct mdt_body *repbody;
+ struct mdt_ioepoch *epoch;
+
+ if (req && rc == -EAGAIN) {
+ repbody = req_capsule_server_get(&req->rq_pill, &RMF_MDT_BODY);
+ epoch = req_capsule_client_get(&req->rq_pill, &RMF_MDT_EPOCH);
+
+ epoch->flags |= MF_SOM_AU;
+ if (repbody->valid & OBD_MD_FLGETATTRLOCK)
+ op_data->op_flags |= MF_GETATTR_LOCK;
+ }
+}
+
+static int mdc_close(struct obd_export *exp, struct md_op_data *op_data,
+ struct md_open_data *mod, struct ptlrpc_request **request)
+{
+ struct obd_device *obd = class_exp2obd(exp);
+ struct ptlrpc_request *req;
+ struct req_format *req_fmt;
+ int rc;
+ int saved_rc = 0;
+
+
+ req_fmt = &RQF_MDS_CLOSE;
+ if (op_data->op_bias & MDS_HSM_RELEASE) {
+ req_fmt = &RQF_MDS_RELEASE_CLOSE;
+
+ /* allocate a FID for volatile file */
+ rc = mdc_fid_alloc(exp, &op_data->op_fid2, op_data);
+ if (rc < 0) {
+ CERROR("%s: "DFID" failed to allocate FID: %d\n",
+ obd->obd_name, PFID(&op_data->op_fid1), rc);
+ /* save the errcode and proceed to close */
+ saved_rc = rc;
+ }
+ }
+
+ *request = NULL;
+ req = ptlrpc_request_alloc(class_exp2cliimp(exp), req_fmt);
+ if (req == NULL)
+ return -ENOMEM;
+
+ mdc_set_capa_size(req, &RMF_CAPA1, op_data->op_capa1);
+
+ rc = ptlrpc_request_pack(req, LUSTRE_MDS_VERSION, MDS_CLOSE);
+ if (rc) {
+ ptlrpc_request_free(req);
+ return rc;
+ }
+
+ /* To avoid a livelock (bug 7034), we need to send CLOSE RPCs to a
+ * portal whose threads are not taking any DLM locks and are therefore
+ * always progressing */
+ req->rq_request_portal = MDS_READPAGE_PORTAL;
+ ptlrpc_at_set_req_timeout(req);
+
+ /* Ensure that this close's handle is fixed up during replay. */
+ if (likely(mod != NULL)) {
+ LASSERTF(mod->mod_open_req != NULL &&
+ mod->mod_open_req->rq_type != LI_POISON,
+ "POISONED open %p!\n", mod->mod_open_req);
+
+ mod->mod_close_req = req;
+
+ DEBUG_REQ(D_HA, mod->mod_open_req, "matched open");
+ /* We no longer want to preserve this open for replay even
+ * though the open was committed. b=3632, b=3633 */
+ spin_lock(&mod->mod_open_req->rq_lock);
+ mod->mod_open_req->rq_replay = 0;
+ spin_unlock(&mod->mod_open_req->rq_lock);
+ } else {
+ CDEBUG(D_HA,
+ "couldn't find open req; expecting close error\n");
+ }
+
+ mdc_close_pack(req, op_data);
+
+ req_capsule_set_size(&req->rq_pill, &RMF_MDT_MD, RCL_SERVER,
+ obd->u.cli.cl_default_mds_easize);
+ req_capsule_set_size(&req->rq_pill, &RMF_LOGCOOKIES, RCL_SERVER,
+ obd->u.cli.cl_default_mds_cookiesize);
+
+ ptlrpc_request_set_replen(req);
+
+ mdc_get_rpc_lock(obd->u.cli.cl_close_lock, NULL);
+ rc = ptlrpc_queue_wait(req);
+ mdc_put_rpc_lock(obd->u.cli.cl_close_lock, NULL);
+
+ if (req->rq_repmsg == NULL) {
+ CDEBUG(D_RPCTRACE, "request failed to send: %p, %d\n", req,
+ req->rq_status);
+ if (rc == 0)
+ rc = req->rq_status ?: -EIO;
+ } else if (rc == 0 || rc == -EAGAIN) {
+ struct mdt_body *body;
+
+ rc = lustre_msg_get_status(req->rq_repmsg);
+ if (lustre_msg_get_type(req->rq_repmsg) == PTL_RPC_MSG_ERR) {
+ DEBUG_REQ(D_ERROR, req,
+ "type == PTL_RPC_MSG_ERR, err = %d", rc);
+ if (rc > 0)
+ rc = -rc;
+ }
+ body = req_capsule_server_get(&req->rq_pill, &RMF_MDT_BODY);
+ if (body == NULL)
+ rc = -EPROTO;
+ } else if (rc == -ESTALE) {
+ /**
+ * it can be allowed error after 3633 if open was committed and
+ * server failed before close was sent. Let's check if mod
+ * exists and return no error in that case
+ */
+ if (mod) {
+ DEBUG_REQ(D_HA, req, "Reset ESTALE = %d", rc);
+ LASSERT(mod->mod_open_req != NULL);
+ if (mod->mod_open_req->rq_committed)
+ rc = 0;
+ }
+ }
+
+ if (mod) {
+ if (rc != 0)
+ mod->mod_close_req = NULL;
+ /* Since now, mod is accessed through open_req only,
+ * thus close req does not keep a reference on mod anymore. */
+ obd_mod_put(mod);
+ }
+ *request = req;
+ mdc_close_handle_reply(req, op_data, rc);
+ return rc < 0 ? rc : saved_rc;
+}
+
+static int mdc_done_writing(struct obd_export *exp, struct md_op_data *op_data,
+ struct md_open_data *mod)
+{
+ struct obd_device *obd = class_exp2obd(exp);
+ struct ptlrpc_request *req;
+ int rc;
+
+ req = ptlrpc_request_alloc(class_exp2cliimp(exp),
+ &RQF_MDS_DONE_WRITING);
+ if (req == NULL)
+ return -ENOMEM;
+
+ mdc_set_capa_size(req, &RMF_CAPA1, op_data->op_capa1);
+ rc = ptlrpc_request_pack(req, LUSTRE_MDS_VERSION, MDS_DONE_WRITING);
+ if (rc) {
+ ptlrpc_request_free(req);
+ return rc;
+ }
+
+ if (mod != NULL) {
+ LASSERTF(mod->mod_open_req != NULL &&
+ mod->mod_open_req->rq_type != LI_POISON,
+ "POISONED setattr %p!\n", mod->mod_open_req);
+
+ mod->mod_close_req = req;
+ DEBUG_REQ(D_HA, mod->mod_open_req, "matched setattr");
+ /* We no longer want to preserve this setattr for replay even
+ * though the open was committed. b=3632, b=3633 */
+ spin_lock(&mod->mod_open_req->rq_lock);
+ mod->mod_open_req->rq_replay = 0;
+ spin_unlock(&mod->mod_open_req->rq_lock);
+ }
+
+ mdc_close_pack(req, op_data);
+ ptlrpc_request_set_replen(req);
+
+ mdc_get_rpc_lock(obd->u.cli.cl_close_lock, NULL);
+ rc = ptlrpc_queue_wait(req);
+ mdc_put_rpc_lock(obd->u.cli.cl_close_lock, NULL);
+
+ if (rc == -ESTALE) {
+ /**
+ * it can be allowed error after 3633 if open or setattr were
+ * committed and server failed before close was sent.
+ * Let's check if mod exists and return no error in that case
+ */
+ if (mod) {
+ LASSERT(mod->mod_open_req != NULL);
+ if (mod->mod_open_req->rq_committed)
+ rc = 0;
+ }
+ }
+
+ if (mod) {
+ if (rc != 0)
+ mod->mod_close_req = NULL;
+ LASSERT(mod->mod_open_req != NULL);
+ mdc_free_open(mod);
+
+ /* Since now, mod is accessed through setattr req only,
+ * thus DW req does not keep a reference on mod anymore. */
+ obd_mod_put(mod);
+ }
+
+ mdc_close_handle_reply(req, op_data, rc);
+ ptlrpc_req_finished(req);
+ return rc;
+}
+
+
+static int mdc_readpage(struct obd_export *exp, struct md_op_data *op_data,
+ struct page **pages, struct ptlrpc_request **request)
+{
+ struct ptlrpc_request *req;
+ struct ptlrpc_bulk_desc *desc;
+ int i;
+ wait_queue_head_t waitq;
+ int resends = 0;
+ struct l_wait_info lwi;
+ int rc;
+
+ *request = NULL;
+ init_waitqueue_head(&waitq);
+
+restart_bulk:
+ req = ptlrpc_request_alloc(class_exp2cliimp(exp), &RQF_MDS_READPAGE);
+ if (req == NULL)
+ return -ENOMEM;
+
+ mdc_set_capa_size(req, &RMF_CAPA1, op_data->op_capa1);
+
+ rc = ptlrpc_request_pack(req, LUSTRE_MDS_VERSION, MDS_READPAGE);
+ if (rc) {
+ ptlrpc_request_free(req);
+ return rc;
+ }
+
+ req->rq_request_portal = MDS_READPAGE_PORTAL;
+ ptlrpc_at_set_req_timeout(req);
+
+ desc = ptlrpc_prep_bulk_imp(req, op_data->op_npages, 1, BULK_PUT_SINK,
+ MDS_BULK_PORTAL);
+ if (desc == NULL) {
+ ptlrpc_request_free(req);
+ return -ENOMEM;
+ }
+
+ /* NB req now owns desc and will free it when it gets freed */
+ for (i = 0; i < op_data->op_npages; i++)
+ ptlrpc_prep_bulk_page_pin(desc, pages[i], 0, PAGE_CACHE_SIZE);
+
+ mdc_readdir_pack(req, op_data->op_offset,
+ PAGE_CACHE_SIZE * op_data->op_npages,
+ &op_data->op_fid1, op_data->op_capa1);
+
+ ptlrpc_request_set_replen(req);
+ rc = ptlrpc_queue_wait(req);
+ if (rc) {
+ ptlrpc_req_finished(req);
+ if (rc != -ETIMEDOUT)
+ return rc;
+
+ resends++;
+ if (!client_should_resend(resends, &exp->exp_obd->u.cli)) {
+ CERROR("too many resend retries, returning error\n");
+ return -EIO;
+ }
+ lwi = LWI_TIMEOUT_INTR(cfs_time_seconds(resends),
+ NULL, NULL, NULL);
+ l_wait_event(waitq, 0, &lwi);
+
+ goto restart_bulk;
+ }
+
+ rc = sptlrpc_cli_unwrap_bulk_read(req, req->rq_bulk,
+ req->rq_bulk->bd_nob_transferred);
+ if (rc < 0) {
+ ptlrpc_req_finished(req);
+ return rc;
+ }
+
+ if (req->rq_bulk->bd_nob_transferred & ~LU_PAGE_MASK) {
+ CERROR("Unexpected # bytes transferred: %d (%ld expected)\n",
+ req->rq_bulk->bd_nob_transferred,
+ PAGE_CACHE_SIZE * op_data->op_npages);
+ ptlrpc_req_finished(req);
+ return -EPROTO;
+ }
+
+ *request = req;
+ return 0;
+}
+
+static int mdc_statfs(const struct lu_env *env,
+ struct obd_export *exp, struct obd_statfs *osfs,
+ __u64 max_age, __u32 flags)
+{
+ struct obd_device *obd = class_exp2obd(exp);
+ struct ptlrpc_request *req;
+ struct obd_statfs *msfs;
+ struct obd_import *imp = NULL;
+ int rc;
+
+ /*
+ * Since the request might also come from lprocfs, so we need
+ * sync this with client_disconnect_export Bug15684
+ */
+ down_read(&obd->u.cli.cl_sem);
+ if (obd->u.cli.cl_import)
+ imp = class_import_get(obd->u.cli.cl_import);
+ up_read(&obd->u.cli.cl_sem);
+ if (!imp)
+ return -ENODEV;
+
+ req = ptlrpc_request_alloc_pack(imp, &RQF_MDS_STATFS,
+ LUSTRE_MDS_VERSION, MDS_STATFS);
+ if (req == NULL) {
+ rc = -ENOMEM;
+ goto output;
+ }
+
+ ptlrpc_request_set_replen(req);
+
+ if (flags & OBD_STATFS_NODELAY) {
+ /* procfs requests not want stay in wait for avoid deadlock */
+ req->rq_no_resend = 1;
+ req->rq_no_delay = 1;
+ }
+
+ rc = ptlrpc_queue_wait(req);
+ if (rc) {
+ /* check connection error first */
+ if (imp->imp_connect_error)
+ rc = imp->imp_connect_error;
+ goto out;
+ }
+
+ msfs = req_capsule_server_get(&req->rq_pill, &RMF_OBD_STATFS);
+ if (msfs == NULL) {
+ rc = -EPROTO;
+ goto out;
+ }
+
+ *osfs = *msfs;
+out:
+ ptlrpc_req_finished(req);
+output:
+ class_import_put(imp);
+ return rc;
+}
+
+static int mdc_ioc_fid2path(struct obd_export *exp, struct getinfo_fid2path *gf)
+{
+ __u32 keylen, vallen;
+ void *key;
+ int rc;
+
+ if (gf->gf_pathlen > PATH_MAX)
+ return -ENAMETOOLONG;
+ if (gf->gf_pathlen < 2)
+ return -EOVERFLOW;
+
+ /* Key is KEY_FID2PATH + getinfo_fid2path description */
+ keylen = cfs_size_round(sizeof(KEY_FID2PATH)) + sizeof(*gf);
+ OBD_ALLOC(key, keylen);
+ if (key == NULL)
+ return -ENOMEM;
+ memcpy(key, KEY_FID2PATH, sizeof(KEY_FID2PATH));
+ memcpy(key + cfs_size_round(sizeof(KEY_FID2PATH)), gf, sizeof(*gf));
+
+ CDEBUG(D_IOCTL, "path get "DFID" from %llu #%d\n",
+ PFID(&gf->gf_fid), gf->gf_recno, gf->gf_linkno);
+
+ if (!fid_is_sane(&gf->gf_fid)) {
+ rc = -EINVAL;
+ goto out;
+ }
+
+ /* Val is struct getinfo_fid2path result plus path */
+ vallen = sizeof(*gf) + gf->gf_pathlen;
+
+ rc = obd_get_info(NULL, exp, keylen, key, &vallen, gf, NULL);
+ if (rc != 0 && rc != -EREMOTE)
+ goto out;
+
+ if (vallen <= sizeof(*gf)) {
+ rc = -EPROTO;
+ goto out;
+ } else if (vallen > sizeof(*gf) + gf->gf_pathlen) {
+ rc = -EOVERFLOW;
+ goto out;
+ }
+
+ CDEBUG(D_IOCTL, "path get "DFID" from %llu #%d\n%s\n",
+ PFID(&gf->gf_fid), gf->gf_recno, gf->gf_linkno, gf->gf_path);
+
+out:
+ OBD_FREE(key, keylen);
+ return rc;
+}
+
+static int mdc_ioc_hsm_progress(struct obd_export *exp,
+ struct hsm_progress_kernel *hpk)
+{
+ struct obd_import *imp = class_exp2cliimp(exp);
+ struct hsm_progress_kernel *req_hpk;
+ struct ptlrpc_request *req;
+ int rc;
+
+ req = ptlrpc_request_alloc_pack(imp, &RQF_MDS_HSM_PROGRESS,
+ LUSTRE_MDS_VERSION, MDS_HSM_PROGRESS);
+ if (req == NULL) {
+ rc = -ENOMEM;
+ goto out;
+ }
+
+ mdc_pack_body(req, NULL, NULL, OBD_MD_FLRMTPERM, 0, 0, 0);
+
+ /* Copy hsm_progress struct */
+ req_hpk = req_capsule_client_get(&req->rq_pill, &RMF_MDS_HSM_PROGRESS);
+ if (req_hpk == NULL) {
+ rc = -EPROTO;
+ goto out;
+ }
+
+ *req_hpk = *hpk;
+ req_hpk->hpk_errval = lustre_errno_hton(hpk->hpk_errval);
+
+ ptlrpc_request_set_replen(req);
+
+ rc = mdc_queue_wait(req);
+ goto out;
+out:
+ ptlrpc_req_finished(req);
+ return rc;
+}
+
+static int mdc_ioc_hsm_ct_register(struct obd_import *imp, __u32 archives)
+{
+ __u32 *archive_mask;
+ struct ptlrpc_request *req;
+ int rc;
+
+ req = ptlrpc_request_alloc_pack(imp, &RQF_MDS_HSM_CT_REGISTER,
+ LUSTRE_MDS_VERSION,
+ MDS_HSM_CT_REGISTER);
+ if (req == NULL) {
+ rc = -ENOMEM;
+ goto out;
+ }
+
+ mdc_pack_body(req, NULL, NULL, OBD_MD_FLRMTPERM, 0, 0, 0);
+
+ /* Copy hsm_progress struct */
+ archive_mask = req_capsule_client_get(&req->rq_pill,
+ &RMF_MDS_HSM_ARCHIVE);
+ if (archive_mask == NULL) {
+ rc = -EPROTO;
+ goto out;
+ }
+
+ *archive_mask = archives;
+
+ ptlrpc_request_set_replen(req);
+
+ rc = mdc_queue_wait(req);
+ goto out;
+out:
+ ptlrpc_req_finished(req);
+ return rc;
+}
+
+static int mdc_ioc_hsm_current_action(struct obd_export *exp,
+ struct md_op_data *op_data)
+{
+ struct hsm_current_action *hca = op_data->op_data;
+ struct hsm_current_action *req_hca;
+ struct ptlrpc_request *req;
+ int rc;
+
+ req = ptlrpc_request_alloc(class_exp2cliimp(exp),
+ &RQF_MDS_HSM_ACTION);
+ if (req == NULL)
+ return -ENOMEM;
+
+ mdc_set_capa_size(req, &RMF_CAPA1, op_data->op_capa1);
+
+ rc = ptlrpc_request_pack(req, LUSTRE_MDS_VERSION, MDS_HSM_ACTION);
+ if (rc) {
+ ptlrpc_request_free(req);
+ return rc;
+ }
+
+ mdc_pack_body(req, &op_data->op_fid1, op_data->op_capa1,
+ OBD_MD_FLRMTPERM, 0, op_data->op_suppgids[0], 0);
+
+ ptlrpc_request_set_replen(req);
+
+ rc = mdc_queue_wait(req);
+ if (rc)
+ goto out;
+
+ req_hca = req_capsule_server_get(&req->rq_pill,
+ &RMF_MDS_HSM_CURRENT_ACTION);
+ if (req_hca == NULL) {
+ rc = -EPROTO;
+ goto out;
+ }
+
+ *hca = *req_hca;
+
+out:
+ ptlrpc_req_finished(req);
+ return rc;
+}
+
+static int mdc_ioc_hsm_ct_unregister(struct obd_import *imp)
+{
+ struct ptlrpc_request *req;
+ int rc;
+
+ req = ptlrpc_request_alloc_pack(imp, &RQF_MDS_HSM_CT_UNREGISTER,
+ LUSTRE_MDS_VERSION,
+ MDS_HSM_CT_UNREGISTER);
+ if (req == NULL) {
+ rc = -ENOMEM;
+ goto out;
+ }
+
+ mdc_pack_body(req, NULL, NULL, OBD_MD_FLRMTPERM, 0, 0, 0);
+
+ ptlrpc_request_set_replen(req);
+
+ rc = mdc_queue_wait(req);
+ goto out;
+out:
+ ptlrpc_req_finished(req);
+ return rc;
+}
+
+static int mdc_ioc_hsm_state_get(struct obd_export *exp,
+ struct md_op_data *op_data)
+{
+ struct hsm_user_state *hus = op_data->op_data;
+ struct hsm_user_state *req_hus;
+ struct ptlrpc_request *req;
+ int rc;
+
+ req = ptlrpc_request_alloc(class_exp2cliimp(exp),
+ &RQF_MDS_HSM_STATE_GET);
+ if (req == NULL)
+ return -ENOMEM;
+
+ mdc_set_capa_size(req, &RMF_CAPA1, op_data->op_capa1);
+
+ rc = ptlrpc_request_pack(req, LUSTRE_MDS_VERSION, MDS_HSM_STATE_GET);
+ if (rc != 0) {
+ ptlrpc_request_free(req);
+ return rc;
+ }
+
+ mdc_pack_body(req, &op_data->op_fid1, op_data->op_capa1,
+ OBD_MD_FLRMTPERM, 0, op_data->op_suppgids[0], 0);
+
+ ptlrpc_request_set_replen(req);
+
+ rc = mdc_queue_wait(req);
+ if (rc)
+ goto out;
+
+ req_hus = req_capsule_server_get(&req->rq_pill, &RMF_HSM_USER_STATE);
+ if (req_hus == NULL) {
+ rc = -EPROTO;
+ goto out;
+ }
+
+ *hus = *req_hus;
+
+out:
+ ptlrpc_req_finished(req);
+ return rc;
+}
+
+static int mdc_ioc_hsm_state_set(struct obd_export *exp,
+ struct md_op_data *op_data)
+{
+ struct hsm_state_set *hss = op_data->op_data;
+ struct hsm_state_set *req_hss;
+ struct ptlrpc_request *req;
+ int rc;
+
+ req = ptlrpc_request_alloc(class_exp2cliimp(exp),
+ &RQF_MDS_HSM_STATE_SET);
+ if (req == NULL)
+ return -ENOMEM;
+
+ mdc_set_capa_size(req, &RMF_CAPA1, op_data->op_capa1);
+
+ rc = ptlrpc_request_pack(req, LUSTRE_MDS_VERSION, MDS_HSM_STATE_SET);
+ if (rc) {
+ ptlrpc_request_free(req);
+ return rc;
+ }
+
+ mdc_pack_body(req, &op_data->op_fid1, op_data->op_capa1,
+ OBD_MD_FLRMTPERM, 0, op_data->op_suppgids[0], 0);
+
+ /* Copy states */
+ req_hss = req_capsule_client_get(&req->rq_pill, &RMF_HSM_STATE_SET);
+ if (req_hss == NULL) {
+ rc = -EPROTO;
+ goto out;
+ }
+ *req_hss = *hss;
+
+ ptlrpc_request_set_replen(req);
+
+ rc = mdc_queue_wait(req);
+ goto out;
+
+out:
+ ptlrpc_req_finished(req);
+ return rc;
+}
+
+static int mdc_ioc_hsm_request(struct obd_export *exp,
+ struct hsm_user_request *hur)
+{
+ struct obd_import *imp = class_exp2cliimp(exp);
+ struct ptlrpc_request *req;
+ struct hsm_request *req_hr;
+ struct hsm_user_item *req_hui;
+ char *req_opaque;
+ int rc;
+
+ req = ptlrpc_request_alloc(imp, &RQF_MDS_HSM_REQUEST);
+ if (req == NULL) {
+ rc = -ENOMEM;
+ goto out;
+ }
+
+ req_capsule_set_size(&req->rq_pill, &RMF_MDS_HSM_USER_ITEM, RCL_CLIENT,
+ hur->hur_request.hr_itemcount
+ * sizeof(struct hsm_user_item));
+ req_capsule_set_size(&req->rq_pill, &RMF_GENERIC_DATA, RCL_CLIENT,
+ hur->hur_request.hr_data_len);
+
+ rc = ptlrpc_request_pack(req, LUSTRE_MDS_VERSION, MDS_HSM_REQUEST);
+ if (rc) {
+ ptlrpc_request_free(req);
+ return rc;
+ }
+
+ mdc_pack_body(req, NULL, NULL, OBD_MD_FLRMTPERM, 0, 0, 0);
+
+ /* Copy hsm_request struct */
+ req_hr = req_capsule_client_get(&req->rq_pill, &RMF_MDS_HSM_REQUEST);
+ if (req_hr == NULL) {
+ rc = -EPROTO;
+ goto out;
+ }
+ *req_hr = hur->hur_request;
+
+ /* Copy hsm_user_item structs */
+ req_hui = req_capsule_client_get(&req->rq_pill, &RMF_MDS_HSM_USER_ITEM);
+ if (req_hui == NULL) {
+ rc = -EPROTO;
+ goto out;
+ }
+ memcpy(req_hui, hur->hur_user_item,
+ hur->hur_request.hr_itemcount * sizeof(struct hsm_user_item));
+
+ /* Copy opaque field */
+ req_opaque = req_capsule_client_get(&req->rq_pill, &RMF_GENERIC_DATA);
+ if (req_opaque == NULL) {
+ rc = -EPROTO;
+ goto out;
+ }
+ memcpy(req_opaque, hur_data(hur), hur->hur_request.hr_data_len);
+
+ ptlrpc_request_set_replen(req);
+
+ rc = mdc_queue_wait(req);
+ goto out;
+
+out:
+ ptlrpc_req_finished(req);
+ return rc;
+}
+
+static struct kuc_hdr *changelog_kuc_hdr(char *buf, int len, int flags)
+{
+ struct kuc_hdr *lh = (struct kuc_hdr *)buf;
+
+ LASSERT(len <= KUC_CHANGELOG_MSG_MAXSIZE);
+
+ lh->kuc_magic = KUC_MAGIC;
+ lh->kuc_transport = KUC_TRANSPORT_CHANGELOG;
+ lh->kuc_flags = flags;
+ lh->kuc_msgtype = CL_RECORD;
+ lh->kuc_msglen = len;
+ return lh;
+}
+
+#define D_CHANGELOG 0
+
+struct changelog_show {
+ __u64 cs_startrec;
+ __u32 cs_flags;
+ struct file *cs_fp;
+ char *cs_buf;
+ struct obd_device *cs_obd;
+};
+
+static int changelog_kkuc_cb(const struct lu_env *env, struct llog_handle *llh,
+ struct llog_rec_hdr *hdr, void *data)
+{
+ struct changelog_show *cs = data;
+ struct llog_changelog_rec *rec = (struct llog_changelog_rec *)hdr;
+ struct kuc_hdr *lh;
+ int len, rc;
+
+ if (rec->cr_hdr.lrh_type != CHANGELOG_REC) {
+ rc = -EINVAL;
+ CERROR("%s: not a changelog rec %x/%d: rc = %d\n",
+ cs->cs_obd->obd_name, rec->cr_hdr.lrh_type,
+ rec->cr.cr_type, rc);
+ return rc;
+ }
+
+ if (rec->cr.cr_index < cs->cs_startrec) {
+ /* Skip entries earlier than what we are interested in */
+ CDEBUG(D_CHANGELOG, "rec=%llu start=%llu\n",
+ rec->cr.cr_index, cs->cs_startrec);
+ return 0;
+ }
+
+ CDEBUG(D_CHANGELOG, "%llu %02d%-5s %llu 0x%x t="DFID" p="DFID
+ " %.*s\n", rec->cr.cr_index, rec->cr.cr_type,
+ changelog_type2str(rec->cr.cr_type), rec->cr.cr_time,
+ rec->cr.cr_flags & CLF_FLAGMASK,
+ PFID(&rec->cr.cr_tfid), PFID(&rec->cr.cr_pfid),
+ rec->cr.cr_namelen, changelog_rec_name(&rec->cr));
+
+ len = sizeof(*lh) + changelog_rec_size(&rec->cr) + rec->cr.cr_namelen;
+
+ /* Set up the message */
+ lh = changelog_kuc_hdr(cs->cs_buf, len, cs->cs_flags);
+ memcpy(lh + 1, &rec->cr, len - sizeof(*lh));
+
+ rc = libcfs_kkuc_msg_put(cs->cs_fp, lh);
+ CDEBUG(D_CHANGELOG, "kucmsg fp %p len %d rc %d\n", cs->cs_fp, len, rc);
+
+ return rc;
+}
+
+static int mdc_changelog_send_thread(void *csdata)
+{
+ struct changelog_show *cs = csdata;
+ struct llog_ctxt *ctxt = NULL;
+ struct llog_handle *llh = NULL;
+ struct kuc_hdr *kuch;
+ int rc;
+
+ CDEBUG(D_CHANGELOG, "changelog to fp=%p start %llu\n",
+ cs->cs_fp, cs->cs_startrec);
+
+ OBD_ALLOC(cs->cs_buf, KUC_CHANGELOG_MSG_MAXSIZE);
+ if (cs->cs_buf == NULL) {
+ rc = -ENOMEM;
+ goto out;
+ }
+
+ /* Set up the remote catalog handle */
+ ctxt = llog_get_context(cs->cs_obd, LLOG_CHANGELOG_REPL_CTXT);
+ if (ctxt == NULL) {
+ rc = -ENOENT;
+ goto out;
+ }
+ rc = llog_open(NULL, ctxt, &llh, NULL, CHANGELOG_CATALOG,
+ LLOG_OPEN_EXISTS);
+ if (rc) {
+ CERROR("%s: fail to open changelog catalog: rc = %d\n",
+ cs->cs_obd->obd_name, rc);
+ goto out;
+ }
+ rc = llog_init_handle(NULL, llh, LLOG_F_IS_CAT, NULL);
+ if (rc) {
+ CERROR("llog_init_handle failed %d\n", rc);
+ goto out;
+ }
+
+ rc = llog_cat_process(NULL, llh, changelog_kkuc_cb, cs, 0, 0);
+
+ /* Send EOF no matter what our result */
+ kuch = changelog_kuc_hdr(cs->cs_buf, sizeof(*kuch), cs->cs_flags);
+ if (kuch) {
+ kuch->kuc_msgtype = CL_EOF;
+ libcfs_kkuc_msg_put(cs->cs_fp, kuch);
+ }
+
+out:
+ fput(cs->cs_fp);
+ if (llh)
+ llog_cat_close(NULL, llh);
+ if (ctxt)
+ llog_ctxt_put(ctxt);
+ if (cs->cs_buf)
+ OBD_FREE(cs->cs_buf, KUC_CHANGELOG_MSG_MAXSIZE);
+ OBD_FREE_PTR(cs);
+ return rc;
+}
+
+static int mdc_ioc_changelog_send(struct obd_device *obd,
+ struct ioc_changelog *icc)
+{
+ struct changelog_show *cs;
+ int rc;
+
+ /* Freed in mdc_changelog_send_thread */
+ OBD_ALLOC_PTR(cs);
+ if (!cs)
+ return -ENOMEM;
+
+ cs->cs_obd = obd;
+ cs->cs_startrec = icc->icc_recno;
+ /* matching fput in mdc_changelog_send_thread */
+ cs->cs_fp = fget(icc->icc_id);
+ cs->cs_flags = icc->icc_flags;
+
+ /*
+ * New thread because we should return to user app before
+ * writing into our pipe
+ */
+ rc = PTR_ERR(kthread_run(mdc_changelog_send_thread, cs,
+ "mdc_clg_send_thread"));
+ if (!IS_ERR_VALUE(rc)) {
+ CDEBUG(D_CHANGELOG, "start changelog thread\n");
+ return 0;
+ }
+
+ CERROR("Failed to start changelog thread: %d\n", rc);
+ OBD_FREE_PTR(cs);
+ return rc;
+}
+
+static int mdc_ioc_hsm_ct_start(struct obd_export *exp,
+ struct lustre_kernelcomm *lk);
+
+static int mdc_quotacheck(struct obd_device *unused, struct obd_export *exp,
+ struct obd_quotactl *oqctl)
+{
+ struct client_obd *cli = &exp->exp_obd->u.cli;
+ struct ptlrpc_request *req;
+ struct obd_quotactl *body;
+ int rc;
+
+ req = ptlrpc_request_alloc_pack(class_exp2cliimp(exp),
+ &RQF_MDS_QUOTACHECK, LUSTRE_MDS_VERSION,
+ MDS_QUOTACHECK);
+ if (req == NULL)
+ return -ENOMEM;
+
+ body = req_capsule_client_get(&req->rq_pill, &RMF_OBD_QUOTACTL);
+ *body = *oqctl;
+
+ ptlrpc_request_set_replen(req);
+
+ /* the next poll will find -ENODATA, that means quotacheck is
+ * going on */
+ cli->cl_qchk_stat = -ENODATA;
+ rc = ptlrpc_queue_wait(req);
+ if (rc)
+ cli->cl_qchk_stat = rc;
+ ptlrpc_req_finished(req);
+ return rc;
+}
+
+static int mdc_quota_poll_check(struct obd_export *exp,
+ struct if_quotacheck *qchk)
+{
+ struct client_obd *cli = &exp->exp_obd->u.cli;
+ int rc;
+
+ qchk->obd_uuid = cli->cl_target_uuid;
+ memcpy(qchk->obd_type, LUSTRE_MDS_NAME, strlen(LUSTRE_MDS_NAME));
+
+ rc = cli->cl_qchk_stat;
+ /* the client is not the previous one */
+ if (rc == CL_NOT_QUOTACHECKED)
+ rc = -EINTR;
+ return rc;
+}
+
+static int mdc_quotactl(struct obd_device *unused, struct obd_export *exp,
+ struct obd_quotactl *oqctl)
+{
+ struct ptlrpc_request *req;
+ struct obd_quotactl *oqc;
+ int rc;
+
+ req = ptlrpc_request_alloc_pack(class_exp2cliimp(exp),
+ &RQF_MDS_QUOTACTL, LUSTRE_MDS_VERSION,
+ MDS_QUOTACTL);
+ if (req == NULL)
+ return -ENOMEM;
+
+ oqc = req_capsule_client_get(&req->rq_pill, &RMF_OBD_QUOTACTL);
+ *oqc = *oqctl;
+
+ ptlrpc_request_set_replen(req);
+ ptlrpc_at_set_req_timeout(req);
+ req->rq_no_resend = 1;
+
+ rc = ptlrpc_queue_wait(req);
+ if (rc)
+ CERROR("ptlrpc_queue_wait failed, rc: %d\n", rc);
+
+ if (req->rq_repmsg) {
+ oqc = req_capsule_server_get(&req->rq_pill, &RMF_OBD_QUOTACTL);
+ if (oqc) {
+ *oqctl = *oqc;
+ } else if (!rc) {
+ CERROR("Can't unpack obd_quotactl\n");
+ rc = -EPROTO;
+ }
+ } else if (!rc) {
+ CERROR("Can't unpack obd_quotactl\n");
+ rc = -EPROTO;
+ }
+ ptlrpc_req_finished(req);
+
+ return rc;
+}
+
+static int mdc_ioc_swap_layouts(struct obd_export *exp,
+ struct md_op_data *op_data)
+{
+ LIST_HEAD(cancels);
+ struct ptlrpc_request *req;
+ int rc, count;
+ struct mdc_swap_layouts *msl, *payload;
+
+ msl = op_data->op_data;
+
+ /* When the MDT will get the MDS_SWAP_LAYOUTS RPC the
+ * first thing it will do is to cancel the 2 layout
+ * locks hold by this client.
+ * So the client must cancel its layout locks on the 2 fids
+ * with the request RPC to avoid extra RPC round trips
+ */
+ count = mdc_resource_get_unused(exp, &op_data->op_fid1, &cancels,
+ LCK_CR, MDS_INODELOCK_LAYOUT);
+ count += mdc_resource_get_unused(exp, &op_data->op_fid2, &cancels,
+ LCK_CR, MDS_INODELOCK_LAYOUT);
+
+ req = ptlrpc_request_alloc(class_exp2cliimp(exp),
+ &RQF_MDS_SWAP_LAYOUTS);
+ if (req == NULL) {
+ ldlm_lock_list_put(&cancels, l_bl_ast, count);
+ return -ENOMEM;
+ }
+
+ mdc_set_capa_size(req, &RMF_CAPA1, op_data->op_capa1);
+ mdc_set_capa_size(req, &RMF_CAPA2, op_data->op_capa2);
+
+ rc = mdc_prep_elc_req(exp, req, MDS_SWAP_LAYOUTS, &cancels, count);
+ if (rc) {
+ ptlrpc_request_free(req);
+ return rc;
+ }
+
+ mdc_swap_layouts_pack(req, op_data);
+
+ payload = req_capsule_client_get(&req->rq_pill, &RMF_SWAP_LAYOUTS);
+ LASSERT(payload);
+
+ *payload = *msl;
+
+ ptlrpc_request_set_replen(req);
+
+ rc = ptlrpc_queue_wait(req);
+ if (rc)
+ goto out;
+
+out:
+ ptlrpc_req_finished(req);
+ return rc;
+}
+
+static int mdc_iocontrol(unsigned int cmd, struct obd_export *exp, int len,
+ void *karg, void *uarg)
+{
+ struct obd_device *obd = exp->exp_obd;
+ struct obd_ioctl_data *data = karg;
+ struct obd_import *imp = obd->u.cli.cl_import;
+ int rc;
+
+ if (!try_module_get(THIS_MODULE)) {
+ CERROR("Can't get module. Is it alive?");
+ return -EINVAL;
+ }
+ switch (cmd) {
+ case OBD_IOC_CHANGELOG_SEND:
+ rc = mdc_ioc_changelog_send(obd, karg);
+ goto out;
+ case OBD_IOC_CHANGELOG_CLEAR: {
+ struct ioc_changelog *icc = karg;
+ struct changelog_setinfo cs = {
+ .cs_recno = icc->icc_recno,
+ .cs_id = icc->icc_id
+ };
+
+ rc = obd_set_info_async(NULL, exp, strlen(KEY_CHANGELOG_CLEAR),
+ KEY_CHANGELOG_CLEAR, sizeof(cs), &cs,
+ NULL);
+ goto out;
+ }
+ case OBD_IOC_FID2PATH:
+ rc = mdc_ioc_fid2path(exp, karg);
+ goto out;
+ case LL_IOC_HSM_CT_START:
+ rc = mdc_ioc_hsm_ct_start(exp, karg);
+ /* ignore if it was already registered on this MDS. */
+ if (rc == -EEXIST)
+ rc = 0;
+ goto out;
+ case LL_IOC_HSM_PROGRESS:
+ rc = mdc_ioc_hsm_progress(exp, karg);
+ goto out;
+ case LL_IOC_HSM_STATE_GET:
+ rc = mdc_ioc_hsm_state_get(exp, karg);
+ goto out;
+ case LL_IOC_HSM_STATE_SET:
+ rc = mdc_ioc_hsm_state_set(exp, karg);
+ goto out;
+ case LL_IOC_HSM_ACTION:
+ rc = mdc_ioc_hsm_current_action(exp, karg);
+ goto out;
+ case LL_IOC_HSM_REQUEST:
+ rc = mdc_ioc_hsm_request(exp, karg);
+ goto out;
+ case OBD_IOC_CLIENT_RECOVER:
+ rc = ptlrpc_recover_import(imp, data->ioc_inlbuf1, 0);
+ if (rc < 0)
+ goto out;
+ rc = 0;
+ goto out;
+ case IOC_OSC_SET_ACTIVE:
+ rc = ptlrpc_set_import_active(imp, data->ioc_offset);
+ goto out;
+ case OBD_IOC_POLL_QUOTACHECK:
+ rc = mdc_quota_poll_check(exp, (struct if_quotacheck *)karg);
+ goto out;
+ case OBD_IOC_PING_TARGET:
+ rc = ptlrpc_obd_ping(obd);
+ goto out;
+ /*
+ * Normally IOC_OBD_STATFS, OBD_IOC_QUOTACTL iocontrol are handled by
+ * LMV instead of MDC. But when the cluster is upgraded from 1.8,
+ * there'd be no LMV layer thus we might be called here. Eventually
+ * this code should be removed.
+ * bz20731, LU-592.
+ */
+ case IOC_OBD_STATFS: {
+ struct obd_statfs stat_buf = {0};
+
+ if (*((__u32 *) data->ioc_inlbuf2) != 0) {
+ rc = -ENODEV;
+ goto out;
+ }
+
+ /* copy UUID */
+ if (copy_to_user(data->ioc_pbuf2, obd2cli_tgt(obd),
+ min_t(size_t, data->ioc_plen2,
+ sizeof(struct obd_uuid)))) {
+ rc = -EFAULT;
+ goto out;
+ }
+
+ rc = mdc_statfs(NULL, obd->obd_self_export, &stat_buf,
+ cfs_time_shift_64(-OBD_STATFS_CACHE_SECONDS),
+ 0);
+ if (rc != 0)
+ goto out;
+
+ if (copy_to_user(data->ioc_pbuf1, &stat_buf,
+ min_t(size_t, data->ioc_plen1,
+ sizeof(stat_buf)))) {
+ rc = -EFAULT;
+ goto out;
+ }
+
+ rc = 0;
+ goto out;
+ }
+ case OBD_IOC_QUOTACTL: {
+ struct if_quotactl *qctl = karg;
+ struct obd_quotactl *oqctl;
+
+ OBD_ALLOC_PTR(oqctl);
+ if (oqctl == NULL) {
+ rc = -ENOMEM;
+ goto out;
+ }
+
+ QCTL_COPY(oqctl, qctl);
+ rc = obd_quotactl(exp, oqctl);
+ if (rc == 0) {
+ QCTL_COPY(qctl, oqctl);
+ qctl->qc_valid = QC_MDTIDX;
+ qctl->obd_uuid = obd->u.cli.cl_target_uuid;
+ }
+
+ OBD_FREE_PTR(oqctl);
+ goto out;
+ }
+ case LL_IOC_GET_CONNECT_FLAGS:
+ if (copy_to_user(uarg, exp_connect_flags_ptr(exp),
+ sizeof(*exp_connect_flags_ptr(exp)))) {
+ rc = -EFAULT;
+ goto out;
+ }
+
+ rc = 0;
+ goto out;
+ case LL_IOC_LOV_SWAP_LAYOUTS:
+ rc = mdc_ioc_swap_layouts(exp, karg);
+ goto out;
+ default:
+ CERROR("unrecognised ioctl: cmd = %#x\n", cmd);
+ rc = -ENOTTY;
+ goto out;
+ }
+out:
+ module_put(THIS_MODULE);
+
+ return rc;
+}
+
+static int mdc_get_info_rpc(struct obd_export *exp,
+ u32 keylen, void *key,
+ int vallen, void *val)
+{
+ struct obd_import *imp = class_exp2cliimp(exp);
+ struct ptlrpc_request *req;
+ char *tmp;
+ int rc = -EINVAL;
+
+ req = ptlrpc_request_alloc(imp, &RQF_MDS_GET_INFO);
+ if (req == NULL)
+ return -ENOMEM;
+
+ req_capsule_set_size(&req->rq_pill, &RMF_GETINFO_KEY,
+ RCL_CLIENT, keylen);
+ req_capsule_set_size(&req->rq_pill, &RMF_GETINFO_VALLEN,
+ RCL_CLIENT, sizeof(__u32));
+
+ rc = ptlrpc_request_pack(req, LUSTRE_MDS_VERSION, MDS_GET_INFO);
+ if (rc) {
+ ptlrpc_request_free(req);
+ return rc;
+ }
+
+ tmp = req_capsule_client_get(&req->rq_pill, &RMF_GETINFO_KEY);
+ memcpy(tmp, key, keylen);
+ tmp = req_capsule_client_get(&req->rq_pill, &RMF_GETINFO_VALLEN);
+ memcpy(tmp, &vallen, sizeof(__u32));
+
+ req_capsule_set_size(&req->rq_pill, &RMF_GETINFO_VAL,
+ RCL_SERVER, vallen);
+ ptlrpc_request_set_replen(req);
+
+ rc = ptlrpc_queue_wait(req);
+ /* -EREMOTE means the get_info result is partial, and it needs to
+ * continue on another MDT, see fid2path part in lmv_iocontrol */
+ if (rc == 0 || rc == -EREMOTE) {
+ tmp = req_capsule_server_get(&req->rq_pill, &RMF_GETINFO_VAL);
+ memcpy(val, tmp, vallen);
+ if (ptlrpc_rep_need_swab(req)) {
+ if (KEY_IS(KEY_FID2PATH))
+ lustre_swab_fid2path(val);
+ }
+ }
+ ptlrpc_req_finished(req);
+
+ return rc;
+}
+
+static void lustre_swab_hai(struct hsm_action_item *h)
+{
+ __swab32s(&h->hai_len);
+ __swab32s(&h->hai_action);
+ lustre_swab_lu_fid(&h->hai_fid);
+ lustre_swab_lu_fid(&h->hai_dfid);
+ __swab64s(&h->hai_cookie);
+ __swab64s(&h->hai_extent.offset);
+ __swab64s(&h->hai_extent.length);
+ __swab64s(&h->hai_gid);
+}
+
+static void lustre_swab_hal(struct hsm_action_list *h)
+{
+ struct hsm_action_item *hai;
+ int i;
+
+ __swab32s(&h->hal_version);
+ __swab32s(&h->hal_count);
+ __swab32s(&h->hal_archive_id);
+ __swab64s(&h->hal_flags);
+ hai = hai_zero(h);
+ for (i = 0; i < h->hal_count; i++, hai = hai_next(hai))
+ lustre_swab_hai(hai);
+}
+
+static void lustre_swab_kuch(struct kuc_hdr *l)
+{
+ __swab16s(&l->kuc_magic);
+ /* __u8 l->kuc_transport */
+ __swab16s(&l->kuc_msgtype);
+ __swab16s(&l->kuc_msglen);
+}
+
+static int mdc_ioc_hsm_ct_start(struct obd_export *exp,
+ struct lustre_kernelcomm *lk)
+{
+ struct obd_import *imp = class_exp2cliimp(exp);
+ __u32 archive = lk->lk_data;
+ int rc = 0;
+
+ if (lk->lk_group != KUC_GRP_HSM) {
+ CERROR("Bad copytool group %d\n", lk->lk_group);
+ return -EINVAL;
+ }
+
+ CDEBUG(D_HSM, "CT start r%d w%d u%d g%d f%#x\n", lk->lk_rfd, lk->lk_wfd,
+ lk->lk_uid, lk->lk_group, lk->lk_flags);
+
+ if (lk->lk_flags & LK_FLG_STOP) {
+ /* Unregister with the coordinator */
+ rc = mdc_ioc_hsm_ct_unregister(imp);
+ } else {
+ rc = mdc_ioc_hsm_ct_register(imp, archive);
+ }
+
+ return rc;
+}
+
+/**
+ * Send a message to any listening copytools
+ * @param val KUC message (kuc_hdr + hsm_action_list)
+ * @param len total length of message
+ */
+static int mdc_hsm_copytool_send(int len, void *val)
+{
+ struct kuc_hdr *lh = (struct kuc_hdr *)val;
+ struct hsm_action_list *hal = (struct hsm_action_list *)(lh + 1);
+ int rc;
+
+ if (len < sizeof(*lh) + sizeof(*hal)) {
+ CERROR("Short HSM message %d < %d\n", len,
+ (int) (sizeof(*lh) + sizeof(*hal)));
+ return -EPROTO;
+ }
+ if (lh->kuc_magic == __swab16(KUC_MAGIC)) {
+ lustre_swab_kuch(lh);
+ lustre_swab_hal(hal);
+ } else if (lh->kuc_magic != KUC_MAGIC) {
+ CERROR("Bad magic %x!=%x\n", lh->kuc_magic, KUC_MAGIC);
+ return -EPROTO;
+ }
+
+ CDEBUG(D_HSM,
+ "Received message mg=%x t=%d m=%d l=%d actions=%d on %s\n",
+ lh->kuc_magic, lh->kuc_transport, lh->kuc_msgtype,
+ lh->kuc_msglen, hal->hal_count, hal->hal_fsname);
+
+ /* Broadcast to HSM listeners */
+ rc = libcfs_kkuc_group_put(KUC_GRP_HSM, lh);
+
+ return rc;
+}
+
+/**
+ * callback function passed to kuc for re-registering each HSM copytool
+ * running on MDC, after MDT shutdown/recovery.
+ * @param data archive id served by the copytool
+ * @param cb_arg callback argument (obd_import)
+ */
+static int mdc_hsm_ct_reregister(__u32 data, void *cb_arg)
+{
+ struct obd_import *imp = (struct obd_import *)cb_arg;
+ __u32 archive = data;
+ int rc;
+
+ CDEBUG(D_HA, "recover copytool registration to MDT (archive=%#x)\n",
+ archive);
+ rc = mdc_ioc_hsm_ct_register(imp, archive);
+
+ /* ignore error if the copytool is already registered */
+ return ((rc != 0) && (rc != -EEXIST)) ? rc : 0;
+}
+
+/**
+ * Re-establish all kuc contexts with MDT
+ * after MDT shutdown/recovery.
+ */
+static int mdc_kuc_reregister(struct obd_import *imp)
+{
+ /* re-register HSM agents */
+ return libcfs_kkuc_group_foreach(KUC_GRP_HSM, mdc_hsm_ct_reregister,
+ (void *)imp);
+}
+
+static int mdc_set_info_async(const struct lu_env *env,
+ struct obd_export *exp,
+ u32 keylen, void *key,
+ u32 vallen, void *val,
+ struct ptlrpc_request_set *set)
+{
+ struct obd_import *imp = class_exp2cliimp(exp);
+ int rc;
+
+ if (KEY_IS(KEY_READ_ONLY)) {
+ if (vallen != sizeof(int))
+ return -EINVAL;
+
+ spin_lock(&imp->imp_lock);
+ if (*((int *)val)) {
+ imp->imp_connect_flags_orig |= OBD_CONNECT_RDONLY;
+ imp->imp_connect_data.ocd_connect_flags |=
+ OBD_CONNECT_RDONLY;
+ } else {
+ imp->imp_connect_flags_orig &= ~OBD_CONNECT_RDONLY;
+ imp->imp_connect_data.ocd_connect_flags &=
+ ~OBD_CONNECT_RDONLY;
+ }
+ spin_unlock(&imp->imp_lock);
+
+ rc = do_set_info_async(imp, MDS_SET_INFO, LUSTRE_MDS_VERSION,
+ keylen, key, vallen, val, set);
+ return rc;
+ }
+ if (KEY_IS(KEY_SPTLRPC_CONF)) {
+ sptlrpc_conf_client_adapt(exp->exp_obd);
+ return 0;
+ }
+ if (KEY_IS(KEY_FLUSH_CTX)) {
+ sptlrpc_import_flush_my_ctx(imp);
+ return 0;
+ }
+ if (KEY_IS(KEY_CHANGELOG_CLEAR)) {
+ rc = do_set_info_async(imp, MDS_SET_INFO, LUSTRE_MDS_VERSION,
+ keylen, key, vallen, val, set);
+ return rc;
+ }
+ if (KEY_IS(KEY_HSM_COPYTOOL_SEND)) {
+ rc = mdc_hsm_copytool_send(vallen, val);
+ return rc;
+ }
+
+ CERROR("Unknown key %s\n", (char *)key);
+ return -EINVAL;
+}
+
+static int mdc_get_info(const struct lu_env *env, struct obd_export *exp,
+ __u32 keylen, void *key, __u32 *vallen, void *val,
+ struct lov_stripe_md *lsm)
+{
+ int rc = -EINVAL;
+
+ if (KEY_IS(KEY_MAX_EASIZE)) {
+ int mdsize, *max_easize;
+
+ if (*vallen != sizeof(int))
+ return -EINVAL;
+ mdsize = *(int *)val;
+ if (mdsize > exp->exp_obd->u.cli.cl_max_mds_easize)
+ exp->exp_obd->u.cli.cl_max_mds_easize = mdsize;
+ max_easize = val;
+ *max_easize = exp->exp_obd->u.cli.cl_max_mds_easize;
+ return 0;
+ } else if (KEY_IS(KEY_DEFAULT_EASIZE)) {
+ int *default_easize;
+
+ if (*vallen != sizeof(int))
+ return -EINVAL;
+ default_easize = val;
+ *default_easize = exp->exp_obd->u.cli.cl_default_mds_easize;
+ return 0;
+ } else if (KEY_IS(KEY_MAX_COOKIESIZE)) {
+ int mdsize, *max_cookiesize;
+
+ if (*vallen != sizeof(int))
+ return -EINVAL;
+ mdsize = *(int *)val;
+ if (mdsize > exp->exp_obd->u.cli.cl_max_mds_cookiesize)
+ exp->exp_obd->u.cli.cl_max_mds_cookiesize = mdsize;
+ max_cookiesize = val;
+ *max_cookiesize = exp->exp_obd->u.cli.cl_max_mds_cookiesize;
+ return 0;
+ } else if (KEY_IS(KEY_DEFAULT_COOKIESIZE)) {
+ int *default_cookiesize;
+
+ if (*vallen != sizeof(int))
+ return -EINVAL;
+ default_cookiesize = val;
+ *default_cookiesize =
+ exp->exp_obd->u.cli.cl_default_mds_cookiesize;
+ return 0;
+ } else if (KEY_IS(KEY_CONN_DATA)) {
+ struct obd_import *imp = class_exp2cliimp(exp);
+ struct obd_connect_data *data = val;
+
+ if (*vallen != sizeof(*data))
+ return -EINVAL;
+
+ *data = imp->imp_connect_data;
+ return 0;
+ } else if (KEY_IS(KEY_TGT_COUNT)) {
+ *((int *)val) = 1;
+ return 0;
+ }
+
+ rc = mdc_get_info_rpc(exp, keylen, key, *vallen, val);
+
+ return rc;
+}
+
+static int mdc_sync(struct obd_export *exp, const struct lu_fid *fid,
+ struct obd_capa *oc, struct ptlrpc_request **request)
+{
+ struct ptlrpc_request *req;
+ int rc;
+
+ *request = NULL;
+ req = ptlrpc_request_alloc(class_exp2cliimp(exp), &RQF_MDS_SYNC);
+ if (req == NULL)
+ return -ENOMEM;
+
+ mdc_set_capa_size(req, &RMF_CAPA1, oc);
+
+ rc = ptlrpc_request_pack(req, LUSTRE_MDS_VERSION, MDS_SYNC);
+ if (rc) {
+ ptlrpc_request_free(req);
+ return rc;
+ }
+
+ mdc_pack_body(req, fid, oc, 0, 0, -1, 0);
+
+ ptlrpc_request_set_replen(req);
+
+ rc = ptlrpc_queue_wait(req);
+ if (rc)
+ ptlrpc_req_finished(req);
+ else
+ *request = req;
+ return rc;
+}
+
+static int mdc_import_event(struct obd_device *obd, struct obd_import *imp,
+ enum obd_import_event event)
+{
+ int rc = 0;
+
+ LASSERT(imp->imp_obd == obd);
+
+ switch (event) {
+ case IMP_EVENT_DISCON: {
+#if 0
+ /* XXX Pass event up to OBDs stack. used only for FLD now */
+ rc = obd_notify_observer(obd, obd, OBD_NOTIFY_DISCON, NULL);
+#endif
+ break;
+ }
+ case IMP_EVENT_INACTIVE: {
+ struct client_obd *cli = &obd->u.cli;
+ /*
+ * Flush current sequence to make client obtain new one
+ * from server in case of disconnect/reconnect.
+ */
+ if (cli->cl_seq != NULL)
+ seq_client_flush(cli->cl_seq);
+
+ rc = obd_notify_observer(obd, obd, OBD_NOTIFY_INACTIVE, NULL);
+ break;
+ }
+ case IMP_EVENT_INVALIDATE: {
+ struct ldlm_namespace *ns = obd->obd_namespace;
+
+ ldlm_namespace_cleanup(ns, LDLM_FL_LOCAL_ONLY);
+
+ break;
+ }
+ case IMP_EVENT_ACTIVE:
+ rc = obd_notify_observer(obd, obd, OBD_NOTIFY_ACTIVE, NULL);
+ /* redo the kuc registration after reconnecting */
+ if (rc == 0)
+ rc = mdc_kuc_reregister(imp);
+ break;
+ case IMP_EVENT_OCD:
+ rc = obd_notify_observer(obd, obd, OBD_NOTIFY_OCD, NULL);
+ break;
+ case IMP_EVENT_DEACTIVATE:
+ case IMP_EVENT_ACTIVATE:
+ break;
+ default:
+ CERROR("Unknown import event %x\n", event);
+ LBUG();
+ }
+ return rc;
+}
+
+int mdc_fid_alloc(struct obd_export *exp, struct lu_fid *fid,
+ struct md_op_data *op_data)
+{
+ struct client_obd *cli = &exp->exp_obd->u.cli;
+ struct lu_client_seq *seq = cli->cl_seq;
+
+ return seq_client_alloc_fid(NULL, seq, fid);
+}
+
+static struct obd_uuid *mdc_get_uuid(struct obd_export *exp)
+{
+ struct client_obd *cli = &exp->exp_obd->u.cli;
+
+ return &cli->cl_target_uuid;
+}
+
+/**
+ * Determine whether the lock can be canceled before replaying it during
+ * recovery, non zero value will be return if the lock can be canceled,
+ * or zero returned for not
+ */
+static int mdc_cancel_for_recovery(struct ldlm_lock *lock)
+{
+ if (lock->l_resource->lr_type != LDLM_IBITS)
+ return 0;
+
+ /* FIXME: if we ever get into a situation where there are too many
+ * opened files with open locks on a single node, then we really
+ * should replay these open locks to reget it */
+ if (lock->l_policy_data.l_inodebits.bits & MDS_INODELOCK_OPEN)
+ return 0;
+
+ return 1;
+}
+
+static int mdc_resource_inode_free(struct ldlm_resource *res)
+{
+ if (res->lr_lvb_inode)
+ res->lr_lvb_inode = NULL;
+
+ return 0;
+}
+
+static struct ldlm_valblock_ops inode_lvbo = {
+ .lvbo_free = mdc_resource_inode_free,
+};
+
+static int mdc_llog_init(struct obd_device *obd)
+{
+ struct obd_llog_group *olg = &obd->obd_olg;
+ struct llog_ctxt *ctxt;
+ int rc;
+
+ rc = llog_setup(NULL, obd, olg, LLOG_CHANGELOG_REPL_CTXT, obd,
+ &llog_client_ops);
+ if (rc)
+ return rc;
+
+ ctxt = llog_group_get_ctxt(olg, LLOG_CHANGELOG_REPL_CTXT);
+ llog_initiator_connect(ctxt);
+ llog_ctxt_put(ctxt);
+
+ return 0;
+}
+
+static void mdc_llog_finish(struct obd_device *obd)
+{
+ struct llog_ctxt *ctxt;
+
+ ctxt = llog_get_context(obd, LLOG_CHANGELOG_REPL_CTXT);
+ if (ctxt)
+ llog_cleanup(NULL, ctxt);
+}
+
+static int mdc_setup(struct obd_device *obd, struct lustre_cfg *cfg)
+{
+ struct client_obd *cli = &obd->u.cli;
+ struct lprocfs_static_vars lvars = { NULL };
+ int rc;
+
+ OBD_ALLOC(cli->cl_rpc_lock, sizeof(*cli->cl_rpc_lock));
+ if (!cli->cl_rpc_lock)
+ return -ENOMEM;
+ mdc_init_rpc_lock(cli->cl_rpc_lock);
+
+ ptlrpcd_addref();
+
+ OBD_ALLOC(cli->cl_close_lock, sizeof(*cli->cl_close_lock));
+ if (!cli->cl_close_lock) {
+ rc = -ENOMEM;
+ goto err_rpc_lock;
+ }
+ mdc_init_rpc_lock(cli->cl_close_lock);
+
+ rc = client_obd_setup(obd, cfg);
+ if (rc)
+ goto err_close_lock;
+ lprocfs_mdc_init_vars(&lvars);
+ lprocfs_obd_setup(obd, lvars.obd_vars);
+ sptlrpc_lprocfs_cliobd_attach(obd);
+ ptlrpc_lprocfs_register_obd(obd);
+
+ ns_register_cancel(obd->obd_namespace, mdc_cancel_for_recovery);
+
+ obd->obd_namespace->ns_lvbo = &inode_lvbo;
+
+ rc = mdc_llog_init(obd);
+ if (rc) {
+ mdc_cleanup(obd);
+ CERROR("failed to setup llogging subsystems\n");
+ }
+
+ return rc;
+
+err_close_lock:
+ OBD_FREE(cli->cl_close_lock, sizeof(*cli->cl_close_lock));
+err_rpc_lock:
+ OBD_FREE(cli->cl_rpc_lock, sizeof(*cli->cl_rpc_lock));
+ ptlrpcd_decref();
+ return rc;
+}
+
+/* Initialize the default and maximum LOV EA and cookie sizes. This allows
+ * us to make MDS RPCs with large enough reply buffers to hold a default
+ * sized EA and cookie without having to calculate this (via a call into the
+ * LOV + OSCs) each time we make an RPC. The maximum size is also tracked
+ * but not used to avoid wastefully vmalloc()'ing large reply buffers when
+ * a large number of stripes is possible. If a larger reply buffer is
+ * required it will be reallocated in the ptlrpc layer due to overflow.
+ */
+static int mdc_init_ea_size(struct obd_export *exp, int easize,
+ int def_easize, int cookiesize, int def_cookiesize)
+{
+ struct obd_device *obd = exp->exp_obd;
+ struct client_obd *cli = &obd->u.cli;
+
+ if (cli->cl_max_mds_easize < easize)
+ cli->cl_max_mds_easize = easize;
+
+ if (cli->cl_default_mds_easize < def_easize)
+ cli->cl_default_mds_easize = def_easize;
+
+ if (cli->cl_max_mds_cookiesize < cookiesize)
+ cli->cl_max_mds_cookiesize = cookiesize;
+
+ if (cli->cl_default_mds_cookiesize < def_cookiesize)
+ cli->cl_default_mds_cookiesize = def_cookiesize;
+
+ return 0;
+}
+
+static int mdc_precleanup(struct obd_device *obd, enum obd_cleanup_stage stage)
+{
+ switch (stage) {
+ case OBD_CLEANUP_EARLY:
+ break;
+ case OBD_CLEANUP_EXPORTS:
+ /* Failsafe, ok if racy */
+ if (obd->obd_type->typ_refcnt <= 1)
+ libcfs_kkuc_group_rem(0, KUC_GRP_HSM);
+
+ obd_cleanup_client_import(obd);
+ ptlrpc_lprocfs_unregister_obd(obd);
+ lprocfs_obd_cleanup(obd);
+
+ mdc_llog_finish(obd);
+ break;
+ }
+ return 0;
+}
+
+static int mdc_cleanup(struct obd_device *obd)
+{
+ struct client_obd *cli = &obd->u.cli;
+
+ OBD_FREE(cli->cl_rpc_lock, sizeof(*cli->cl_rpc_lock));
+ OBD_FREE(cli->cl_close_lock, sizeof(*cli->cl_close_lock));
+
+ ptlrpcd_decref();
+
+ return client_obd_cleanup(obd);
+}
+
+static int mdc_process_config(struct obd_device *obd, u32 len, void *buf)
+{
+ struct lustre_cfg *lcfg = buf;
+ struct lprocfs_static_vars lvars = { NULL };
+ int rc = 0;
+
+ lprocfs_mdc_init_vars(&lvars);
+ switch (lcfg->lcfg_command) {
+ default:
+ rc = class_process_proc_param(PARAM_MDC, lvars.obd_vars,
+ lcfg, obd);
+ if (rc > 0)
+ rc = 0;
+ break;
+ }
+ return rc;
+}
+
+
+/* get remote permission for current user on fid */
+static int mdc_get_remote_perm(struct obd_export *exp, const struct lu_fid *fid,
+ struct obd_capa *oc, __u32 suppgid,
+ struct ptlrpc_request **request)
+{
+ struct ptlrpc_request *req;
+ int rc;
+
+ LASSERT(client_is_remote(exp));
+
+ *request = NULL;
+ req = ptlrpc_request_alloc(class_exp2cliimp(exp), &RQF_MDS_GETATTR);
+ if (req == NULL)
+ return -ENOMEM;
+
+ mdc_set_capa_size(req, &RMF_CAPA1, oc);
+
+ rc = ptlrpc_request_pack(req, LUSTRE_MDS_VERSION, MDS_GETATTR);
+ if (rc) {
+ ptlrpc_request_free(req);
+ return rc;
+ }
+
+ mdc_pack_body(req, fid, oc, OBD_MD_FLRMTPERM, 0, suppgid, 0);
+
+ req_capsule_set_size(&req->rq_pill, &RMF_ACL, RCL_SERVER,
+ sizeof(struct mdt_remote_perm));
+
+ ptlrpc_request_set_replen(req);
+
+ rc = ptlrpc_queue_wait(req);
+ if (rc)
+ ptlrpc_req_finished(req);
+ else
+ *request = req;
+ return rc;
+}
+
+static int mdc_interpret_renew_capa(const struct lu_env *env,
+ struct ptlrpc_request *req, void *args,
+ int status)
+{
+ struct mdc_renew_capa_args *ra = args;
+ struct mdt_body *body = NULL;
+ struct lustre_capa *capa;
+
+ if (status) {
+ capa = ERR_PTR(status);
+ goto out;
+ }
+
+ body = req_capsule_server_get(&req->rq_pill, &RMF_MDT_BODY);
+ if (body == NULL) {
+ capa = ERR_PTR(-EFAULT);
+ goto out;
+ }
+
+ if ((body->valid & OBD_MD_FLOSSCAPA) == 0) {
+ capa = ERR_PTR(-ENOENT);
+ goto out;
+ }
+
+ capa = req_capsule_server_get(&req->rq_pill, &RMF_CAPA2);
+ if (!capa) {
+ capa = ERR_PTR(-EFAULT);
+ goto out;
+ }
+out:
+ ra->ra_cb(ra->ra_oc, capa);
+ return 0;
+}
+
+static int mdc_renew_capa(struct obd_export *exp, struct obd_capa *oc,
+ renew_capa_cb_t cb)
+{
+ struct ptlrpc_request *req;
+ struct mdc_renew_capa_args *ra;
+
+ req = ptlrpc_request_alloc_pack(class_exp2cliimp(exp), &RQF_MDS_GETATTR,
+ LUSTRE_MDS_VERSION, MDS_GETATTR);
+ if (req == NULL)
+ return -ENOMEM;
+
+ /* NB, OBD_MD_FLOSSCAPA is set here, but it doesn't necessarily mean the
+ * capa to renew is oss capa.
+ */
+ mdc_pack_body(req, &oc->c_capa.lc_fid, oc, OBD_MD_FLOSSCAPA, 0, -1, 0);
+ ptlrpc_request_set_replen(req);
+
+ CLASSERT(sizeof(*ra) <= sizeof(req->rq_async_args));
+ ra = ptlrpc_req_async_args(req);
+ ra->ra_oc = oc;
+ ra->ra_cb = cb;
+ req->rq_interpret_reply = mdc_interpret_renew_capa;
+ ptlrpcd_add_req(req, PDL_POLICY_LOCAL, -1);
+ return 0;
+}
+
+static struct obd_ops mdc_obd_ops = {
+ .o_owner = THIS_MODULE,
+ .o_setup = mdc_setup,
+ .o_precleanup = mdc_precleanup,
+ .o_cleanup = mdc_cleanup,
+ .o_add_conn = client_import_add_conn,
+ .o_del_conn = client_import_del_conn,
+ .o_connect = client_connect_import,
+ .o_disconnect = client_disconnect_export,
+ .o_iocontrol = mdc_iocontrol,
+ .o_set_info_async = mdc_set_info_async,
+ .o_statfs = mdc_statfs,
+ .o_fid_init = client_fid_init,
+ .o_fid_fini = client_fid_fini,
+ .o_fid_alloc = mdc_fid_alloc,
+ .o_import_event = mdc_import_event,
+ .o_get_info = mdc_get_info,
+ .o_process_config = mdc_process_config,
+ .o_get_uuid = mdc_get_uuid,
+ .o_quotactl = mdc_quotactl,
+ .o_quotacheck = mdc_quotacheck
+};
+
+static struct md_ops mdc_md_ops = {
+ .m_getstatus = mdc_getstatus,
+ .m_null_inode = mdc_null_inode,
+ .m_find_cbdata = mdc_find_cbdata,
+ .m_close = mdc_close,
+ .m_create = mdc_create,
+ .m_done_writing = mdc_done_writing,
+ .m_enqueue = mdc_enqueue,
+ .m_getattr = mdc_getattr,
+ .m_getattr_name = mdc_getattr_name,
+ .m_intent_lock = mdc_intent_lock,
+ .m_link = mdc_link,
+ .m_is_subdir = mdc_is_subdir,
+ .m_rename = mdc_rename,
+ .m_setattr = mdc_setattr,
+ .m_setxattr = mdc_setxattr,
+ .m_getxattr = mdc_getxattr,
+ .m_sync = mdc_sync,
+ .m_readpage = mdc_readpage,
+ .m_unlink = mdc_unlink,
+ .m_cancel_unused = mdc_cancel_unused,
+ .m_init_ea_size = mdc_init_ea_size,
+ .m_set_lock_data = mdc_set_lock_data,
+ .m_lock_match = mdc_lock_match,
+ .m_get_lustre_md = mdc_get_lustre_md,
+ .m_free_lustre_md = mdc_free_lustre_md,
+ .m_set_open_replay_data = mdc_set_open_replay_data,
+ .m_clear_open_replay_data = mdc_clear_open_replay_data,
+ .m_renew_capa = mdc_renew_capa,
+ .m_unpack_capa = mdc_unpack_capa,
+ .m_get_remote_perm = mdc_get_remote_perm,
+ .m_intent_getattr_async = mdc_intent_getattr_async,
+ .m_revalidate_lock = mdc_revalidate_lock
+};
+
+static int __init mdc_init(void)
+{
+ struct lprocfs_static_vars lvars = { NULL };
+
+ lprocfs_mdc_init_vars(&lvars);
+
+ return class_register_type(&mdc_obd_ops, &mdc_md_ops, lvars.module_vars,
+ LUSTRE_MDC_NAME, NULL);
+}
+
+static void /*__exit*/ mdc_exit(void)
+{
+ class_unregister_type(LUSTRE_MDC_NAME);
+}
+
+MODULE_AUTHOR("Sun Microsystems, Inc. <http://www.lustre.org/>");
+MODULE_DESCRIPTION("Lustre Metadata Client");
+MODULE_LICENSE("GPL");
+
+module_init(mdc_init);
+module_exit(mdc_exit);
diff --git a/drivers/staging/lustre/lustre/mgc/Makefile b/drivers/staging/lustre/lustre/mgc/Makefile
new file mode 100644
index 000000000..cc6e9f51a
--- /dev/null
+++ b/drivers/staging/lustre/lustre/mgc/Makefile
@@ -0,0 +1,3 @@
+obj-$(CONFIG_LUSTRE_FS) += mgc.o
+mgc-y := mgc_request.o
+mgc-$(CONFIG_PROC_FS) += lproc_mgc.o
diff --git a/drivers/staging/lustre/lustre/mgc/lproc_mgc.c b/drivers/staging/lustre/lustre/mgc/lproc_mgc.c
new file mode 100644
index 000000000..c4ea38e5f
--- /dev/null
+++ b/drivers/staging/lustre/lustre/mgc/lproc_mgc.c
@@ -0,0 +1,80 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2011, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ */
+#define DEBUG_SUBSYSTEM S_CLASS
+
+#include <linux/vfs.h>
+#include "../include/obd_class.h"
+#include "../include/lprocfs_status.h"
+#include "mgc_internal.h"
+
+LPROC_SEQ_FOPS_RO_TYPE(mgc, uuid);
+LPROC_SEQ_FOPS_RO_TYPE(mgc, connect_flags);
+LPROC_SEQ_FOPS_RO_TYPE(mgc, server_uuid);
+LPROC_SEQ_FOPS_RO_TYPE(mgc, conn_uuid);
+LPROC_SEQ_FOPS_RO_TYPE(mgc, import);
+LPROC_SEQ_FOPS_RO_TYPE(mgc, state);
+
+LPROC_SEQ_FOPS_WR_ONLY(mgc, ping);
+
+static int mgc_ir_state_seq_show(struct seq_file *m, void *v)
+{
+ return lprocfs_mgc_rd_ir_state(m, m->private);
+}
+LPROC_SEQ_FOPS_RO(mgc_ir_state);
+
+static struct lprocfs_vars lprocfs_mgc_obd_vars[] = {
+ { "uuid", &mgc_uuid_fops, NULL, 0 },
+ { "ping", &mgc_ping_fops, NULL, 0222 },
+ { "connect_flags", &mgc_connect_flags_fops, NULL, 0 },
+ { "mgs_server_uuid", &mgc_server_uuid_fops, NULL, 0 },
+ { "mgs_conn_uuid", &mgc_conn_uuid_fops, NULL, 0 },
+ { "import", &mgc_import_fops, NULL, 0 },
+ { "state", &mgc_state_fops, NULL, 0 },
+ { "ir_state", &mgc_ir_state_fops, NULL, 0 },
+ { NULL }
+};
+
+LPROC_SEQ_FOPS_RO_TYPE(mgc, numrefs);
+static struct lprocfs_vars lprocfs_mgc_module_vars[] = {
+ { "num_refs", &mgc_numrefs_fops, NULL, 0 },
+ { NULL }
+};
+
+void lprocfs_mgc_init_vars(struct lprocfs_static_vars *lvars)
+{
+ lvars->module_vars = lprocfs_mgc_module_vars;
+ lvars->obd_vars = lprocfs_mgc_obd_vars;
+}
diff --git a/drivers/staging/lustre/lustre/mgc/mgc_internal.h b/drivers/staging/lustre/lustre/mgc/mgc_internal.h
new file mode 100644
index 000000000..a6f8b3ced
--- /dev/null
+++ b/drivers/staging/lustre/lustre/mgc/mgc_internal.h
@@ -0,0 +1,73 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2011, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ */
+
+#ifndef _MGC_INTERNAL_H
+#define _MGC_INTERNAL_H
+
+#include "../../include/linux/libcfs/libcfs.h"
+#include "../include/lustre/lustre_idl.h"
+#include "../include/lustre_lib.h"
+#include "../include/lustre_dlm.h"
+#include "../include/lustre_log.h"
+#include "../include/lustre_export.h"
+
+#if defined (CONFIG_PROC_FS)
+void lprocfs_mgc_init_vars(struct lprocfs_static_vars *lvars);
+int lprocfs_mgc_rd_ir_state(struct seq_file *m, void *data);
+#else
+static inline void lprocfs_mgc_init_vars(struct lprocfs_static_vars *lvars)
+{
+ memset(lvars, 0, sizeof(*lvars));
+}
+static inline int lprocfs_mgc_rd_ir_state(struct seq_file *m, void *data)
+{
+ return 0;
+}
+#endif /* CONFIG_PROC_FS */
+
+int mgc_process_log(struct obd_device *mgc, struct config_llog_data *cld);
+
+static inline int cld_is_sptlrpc(struct config_llog_data *cld)
+{
+ return cld->cld_type == CONFIG_T_SPTLRPC;
+}
+
+static inline int cld_is_recover(struct config_llog_data *cld)
+{
+ return cld->cld_type == CONFIG_T_RECOVER;
+}
+
+#endif /* _MGC_INTERNAL_H */
diff --git a/drivers/staging/lustre/lustre/mgc/mgc_request.c b/drivers/staging/lustre/lustre/mgc/mgc_request.c
new file mode 100644
index 000000000..7947aec5c
--- /dev/null
+++ b/drivers/staging/lustre/lustre/mgc/mgc_request.c
@@ -0,0 +1,1762 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2011, 2012, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ *
+ * lustre/mgc/mgc_request.c
+ *
+ * Author: Nathan Rutman <nathan@clusterfs.com>
+ */
+
+#define DEBUG_SUBSYSTEM S_MGC
+#define D_MGC D_CONFIG /*|D_WARNING*/
+
+#include <linux/module.h>
+#include "../include/obd_class.h"
+#include "../include/lustre_dlm.h"
+#include "../include/lprocfs_status.h"
+#include "../include/lustre_log.h"
+#include "../include/lustre_disk.h"
+
+#include "mgc_internal.h"
+
+static int mgc_name2resid(char *name, int len, struct ldlm_res_id *res_id,
+ int type)
+{
+ __u64 resname = 0;
+
+ if (len > sizeof(resname)) {
+ CERROR("name too long: %s\n", name);
+ return -EINVAL;
+ }
+ if (len <= 0) {
+ CERROR("missing name: %s\n", name);
+ return -EINVAL;
+ }
+ memcpy(&resname, name, len);
+
+ /* Always use the same endianness for the resid */
+ memset(res_id, 0, sizeof(*res_id));
+ res_id->name[0] = cpu_to_le64(resname);
+ /* XXX: unfortunately, sptlprc and config llog share one lock */
+ switch (type) {
+ case CONFIG_T_CONFIG:
+ case CONFIG_T_SPTLRPC:
+ resname = 0;
+ break;
+ case CONFIG_T_RECOVER:
+ case CONFIG_T_PARAMS:
+ resname = type;
+ break;
+ default:
+ LBUG();
+ }
+ res_id->name[1] = cpu_to_le64(resname);
+ CDEBUG(D_MGC, "log %s to resid %#llx/%#llx (%.8s)\n", name,
+ res_id->name[0], res_id->name[1], (char *)&res_id->name[0]);
+ return 0;
+}
+
+int mgc_fsname2resid(char *fsname, struct ldlm_res_id *res_id, int type)
+{
+ /* fsname is at most 8 chars long, maybe contain "-".
+ * e.g. "lustre", "SUN-000" */
+ return mgc_name2resid(fsname, strlen(fsname), res_id, type);
+}
+EXPORT_SYMBOL(mgc_fsname2resid);
+
+static int mgc_logname2resid(char *logname, struct ldlm_res_id *res_id, int type)
+{
+ char *name_end;
+ int len;
+
+ /* logname consists of "fsname-nodetype".
+ * e.g. "lustre-MDT0001", "SUN-000-client"
+ * there is an exception: llog "params" */
+ name_end = strrchr(logname, '-');
+ if (!name_end)
+ len = strlen(logname);
+ else
+ len = name_end - logname;
+ return mgc_name2resid(logname, len, res_id, type);
+}
+
+/********************** config llog list **********************/
+static LIST_HEAD(config_llog_list);
+static DEFINE_SPINLOCK(config_list_lock);
+
+/* Take a reference to a config log */
+static int config_log_get(struct config_llog_data *cld)
+{
+ atomic_inc(&cld->cld_refcount);
+ CDEBUG(D_INFO, "log %s refs %d\n", cld->cld_logname,
+ atomic_read(&cld->cld_refcount));
+ return 0;
+}
+
+/* Drop a reference to a config log. When no longer referenced,
+ we can free the config log data */
+static void config_log_put(struct config_llog_data *cld)
+{
+ CDEBUG(D_INFO, "log %s refs %d\n", cld->cld_logname,
+ atomic_read(&cld->cld_refcount));
+ LASSERT(atomic_read(&cld->cld_refcount) > 0);
+
+ /* spinlock to make sure no item with 0 refcount in the list */
+ if (atomic_dec_and_lock(&cld->cld_refcount, &config_list_lock)) {
+ list_del(&cld->cld_list_chain);
+ spin_unlock(&config_list_lock);
+
+ CDEBUG(D_MGC, "dropping config log %s\n", cld->cld_logname);
+
+ if (cld->cld_recover)
+ config_log_put(cld->cld_recover);
+ if (cld->cld_sptlrpc)
+ config_log_put(cld->cld_sptlrpc);
+ if (cld->cld_params)
+ config_log_put(cld->cld_params);
+ if (cld_is_sptlrpc(cld))
+ sptlrpc_conf_log_stop(cld->cld_logname);
+
+ class_export_put(cld->cld_mgcexp);
+ OBD_FREE(cld, sizeof(*cld) + strlen(cld->cld_logname) + 1);
+ }
+}
+
+/* Find a config log by name */
+static
+struct config_llog_data *config_log_find(char *logname,
+ struct config_llog_instance *cfg)
+{
+ struct config_llog_data *cld;
+ struct config_llog_data *found = NULL;
+ void *instance;
+
+ LASSERT(logname != NULL);
+
+ instance = cfg ? cfg->cfg_instance : NULL;
+ spin_lock(&config_list_lock);
+ list_for_each_entry(cld, &config_llog_list, cld_list_chain) {
+ /* check if instance equals */
+ if (instance != cld->cld_cfg.cfg_instance)
+ continue;
+
+ /* instance may be NULL, should check name */
+ if (strcmp(logname, cld->cld_logname) == 0) {
+ found = cld;
+ break;
+ }
+ }
+ if (found) {
+ atomic_inc(&found->cld_refcount);
+ LASSERT(found->cld_stopping == 0 || cld_is_sptlrpc(found) == 0);
+ }
+ spin_unlock(&config_list_lock);
+ return found;
+}
+
+static
+struct config_llog_data *do_config_log_add(struct obd_device *obd,
+ char *logname,
+ int type,
+ struct config_llog_instance *cfg,
+ struct super_block *sb)
+{
+ struct config_llog_data *cld;
+ int rc;
+
+ CDEBUG(D_MGC, "do adding config log %s:%p\n", logname,
+ cfg ? cfg->cfg_instance : NULL);
+
+ OBD_ALLOC(cld, sizeof(*cld) + strlen(logname) + 1);
+ if (!cld)
+ return ERR_PTR(-ENOMEM);
+
+ strcpy(cld->cld_logname, logname);
+ if (cfg)
+ cld->cld_cfg = *cfg;
+ else
+ cld->cld_cfg.cfg_callback = class_config_llog_handler;
+ mutex_init(&cld->cld_lock);
+ cld->cld_cfg.cfg_last_idx = 0;
+ cld->cld_cfg.cfg_flags = 0;
+ cld->cld_cfg.cfg_sb = sb;
+ cld->cld_type = type;
+ atomic_set(&cld->cld_refcount, 1);
+
+ /* Keep the mgc around until we are done */
+ cld->cld_mgcexp = class_export_get(obd->obd_self_export);
+
+ if (cld_is_sptlrpc(cld)) {
+ sptlrpc_conf_log_start(logname);
+ cld->cld_cfg.cfg_obdname = obd->obd_name;
+ }
+
+ rc = mgc_logname2resid(logname, &cld->cld_resid, type);
+
+ spin_lock(&config_list_lock);
+ list_add(&cld->cld_list_chain, &config_llog_list);
+ spin_unlock(&config_list_lock);
+
+ if (rc) {
+ config_log_put(cld);
+ return ERR_PTR(rc);
+ }
+
+ if (cld_is_sptlrpc(cld)) {
+ rc = mgc_process_log(obd, cld);
+ if (rc && rc != -ENOENT)
+ CERROR("failed processing sptlrpc log: %d\n", rc);
+ }
+
+ return cld;
+}
+
+static struct config_llog_data *config_recover_log_add(struct obd_device *obd,
+ char *fsname,
+ struct config_llog_instance *cfg,
+ struct super_block *sb)
+{
+ struct config_llog_instance lcfg = *cfg;
+ struct lustre_sb_info *lsi = s2lsi(sb);
+ struct config_llog_data *cld;
+ char logname[32];
+
+ if (IS_OST(lsi))
+ return NULL;
+
+ /* for osp-on-ost, see lustre_start_osp() */
+ if (IS_MDT(lsi) && lcfg.cfg_instance)
+ return NULL;
+
+ /* we have to use different llog for clients and mdts for cmd
+ * where only clients are notified if one of cmd server restarts */
+ LASSERT(strlen(fsname) < sizeof(logname) / 2);
+ strcpy(logname, fsname);
+ if (IS_SERVER(lsi)) { /* mdt */
+ LASSERT(lcfg.cfg_instance == NULL);
+ lcfg.cfg_instance = sb;
+ strcat(logname, "-mdtir");
+ } else {
+ LASSERT(lcfg.cfg_instance != NULL);
+ strcat(logname, "-cliir");
+ }
+
+ cld = do_config_log_add(obd, logname, CONFIG_T_RECOVER, &lcfg, sb);
+ return cld;
+}
+
+static struct config_llog_data *config_params_log_add(struct obd_device *obd,
+ struct config_llog_instance *cfg, struct super_block *sb)
+{
+ struct config_llog_instance lcfg = *cfg;
+ struct config_llog_data *cld;
+
+ lcfg.cfg_instance = sb;
+
+ cld = do_config_log_add(obd, PARAMS_FILENAME, CONFIG_T_PARAMS,
+ &lcfg, sb);
+
+ return cld;
+}
+
+/** Add this log to the list of active logs watched by an MGC.
+ * Active means we're watching for updates.
+ * We have one active log per "mount" - client instance or servername.
+ * Each instance may be at a different point in the log.
+ */
+static int config_log_add(struct obd_device *obd, char *logname,
+ struct config_llog_instance *cfg,
+ struct super_block *sb)
+{
+ struct lustre_sb_info *lsi = s2lsi(sb);
+ struct config_llog_data *cld;
+ struct config_llog_data *sptlrpc_cld;
+ struct config_llog_data *params_cld;
+ char seclogname[32];
+ char *ptr;
+ int rc;
+
+ CDEBUG(D_MGC, "adding config log %s:%p\n", logname, cfg->cfg_instance);
+
+ /*
+ * for each regular log, the depended sptlrpc log name is
+ * <fsname>-sptlrpc. multiple regular logs may share one sptlrpc log.
+ */
+ ptr = strrchr(logname, '-');
+ if (ptr == NULL || ptr - logname > 8) {
+ CERROR("logname %s is too long\n", logname);
+ return -EINVAL;
+ }
+
+ memcpy(seclogname, logname, ptr - logname);
+ strcpy(seclogname + (ptr - logname), "-sptlrpc");
+
+ sptlrpc_cld = config_log_find(seclogname, NULL);
+ if (sptlrpc_cld == NULL) {
+ sptlrpc_cld = do_config_log_add(obd, seclogname,
+ CONFIG_T_SPTLRPC, NULL, NULL);
+ if (IS_ERR(sptlrpc_cld)) {
+ CERROR("can't create sptlrpc log: %s\n", seclogname);
+ rc = PTR_ERR(sptlrpc_cld);
+ goto out_err;
+ }
+ }
+ params_cld = config_params_log_add(obd, cfg, sb);
+ if (IS_ERR(params_cld)) {
+ rc = PTR_ERR(params_cld);
+ CERROR("%s: can't create params log: rc = %d\n",
+ obd->obd_name, rc);
+ goto out_err1;
+ }
+
+ cld = do_config_log_add(obd, logname, CONFIG_T_CONFIG, cfg, sb);
+ if (IS_ERR(cld)) {
+ CERROR("can't create log: %s\n", logname);
+ rc = PTR_ERR(cld);
+ goto out_err2;
+ }
+
+ cld->cld_sptlrpc = sptlrpc_cld;
+ cld->cld_params = params_cld;
+
+ LASSERT(lsi->lsi_lmd);
+ if (!(lsi->lsi_lmd->lmd_flags & LMD_FLG_NOIR)) {
+ struct config_llog_data *recover_cld;
+ *strrchr(seclogname, '-') = 0;
+ recover_cld = config_recover_log_add(obd, seclogname, cfg, sb);
+ if (IS_ERR(recover_cld)) {
+ rc = PTR_ERR(recover_cld);
+ goto out_err3;
+ }
+ cld->cld_recover = recover_cld;
+ }
+
+ return 0;
+
+out_err3:
+ config_log_put(cld);
+
+out_err2:
+ config_log_put(params_cld);
+
+out_err1:
+ config_log_put(sptlrpc_cld);
+
+out_err:
+ return rc;
+}
+
+DEFINE_MUTEX(llog_process_lock);
+
+/** Stop watching for updates on this log.
+ */
+static int config_log_end(char *logname, struct config_llog_instance *cfg)
+{
+ struct config_llog_data *cld;
+ struct config_llog_data *cld_sptlrpc = NULL;
+ struct config_llog_data *cld_params = NULL;
+ struct config_llog_data *cld_recover = NULL;
+ int rc = 0;
+
+ cld = config_log_find(logname, cfg);
+ if (cld == NULL)
+ return -ENOENT;
+
+ mutex_lock(&cld->cld_lock);
+ /*
+ * if cld_stopping is set, it means we didn't start the log thus
+ * not owning the start ref. this can happen after previous umount:
+ * the cld still hanging there waiting for lock cancel, and we
+ * remount again but failed in the middle and call log_end without
+ * calling start_log.
+ */
+ if (unlikely(cld->cld_stopping)) {
+ mutex_unlock(&cld->cld_lock);
+ /* drop the ref from the find */
+ config_log_put(cld);
+ return rc;
+ }
+
+ cld->cld_stopping = 1;
+
+ cld_recover = cld->cld_recover;
+ cld->cld_recover = NULL;
+ mutex_unlock(&cld->cld_lock);
+
+ if (cld_recover) {
+ mutex_lock(&cld_recover->cld_lock);
+ cld_recover->cld_stopping = 1;
+ mutex_unlock(&cld_recover->cld_lock);
+ config_log_put(cld_recover);
+ }
+
+ spin_lock(&config_list_lock);
+ cld_sptlrpc = cld->cld_sptlrpc;
+ cld->cld_sptlrpc = NULL;
+ cld_params = cld->cld_params;
+ cld->cld_params = NULL;
+ spin_unlock(&config_list_lock);
+
+ if (cld_sptlrpc)
+ config_log_put(cld_sptlrpc);
+
+ if (cld_params) {
+ mutex_lock(&cld_params->cld_lock);
+ cld_params->cld_stopping = 1;
+ mutex_unlock(&cld_params->cld_lock);
+ config_log_put(cld_params);
+ }
+
+ /* drop the ref from the find */
+ config_log_put(cld);
+ /* drop the start ref */
+ config_log_put(cld);
+
+ CDEBUG(D_MGC, "end config log %s (%d)\n", logname ? logname : "client",
+ rc);
+ return rc;
+}
+
+#if defined (CONFIG_PROC_FS)
+int lprocfs_mgc_rd_ir_state(struct seq_file *m, void *data)
+{
+ struct obd_device *obd = data;
+ struct obd_import *imp;
+ struct obd_connect_data *ocd;
+ struct config_llog_data *cld;
+
+ LPROCFS_CLIMP_CHECK(obd);
+ imp = obd->u.cli.cl_import;
+ ocd = &imp->imp_connect_data;
+
+ seq_printf(m, "imperative_recovery: %s\n",
+ OCD_HAS_FLAG(ocd, IMP_RECOV) ? "ENABLED" : "DISABLED");
+ seq_printf(m, "client_state:\n");
+
+ spin_lock(&config_list_lock);
+ list_for_each_entry(cld, &config_llog_list, cld_list_chain) {
+ if (cld->cld_recover == NULL)
+ continue;
+ seq_printf(m, " - { client: %s, nidtbl_version: %u }\n",
+ cld->cld_logname,
+ cld->cld_recover->cld_cfg.cfg_last_idx);
+ }
+ spin_unlock(&config_list_lock);
+
+ LPROCFS_CLIMP_EXIT(obd);
+ return 0;
+}
+#endif
+
+/* reenqueue any lost locks */
+#define RQ_RUNNING 0x1
+#define RQ_NOW 0x2
+#define RQ_LATER 0x4
+#define RQ_STOP 0x8
+#define RQ_PRECLEANUP 0x10
+static int rq_state;
+static wait_queue_head_t rq_waitq;
+static DECLARE_COMPLETION(rq_exit);
+static DECLARE_COMPLETION(rq_start);
+
+static void do_requeue(struct config_llog_data *cld)
+{
+ LASSERT(atomic_read(&cld->cld_refcount) > 0);
+
+ /* Do not run mgc_process_log on a disconnected export or an
+ export which is being disconnected. Take the client
+ semaphore to make the check non-racy. */
+ down_read(&cld->cld_mgcexp->exp_obd->u.cli.cl_sem);
+ if (cld->cld_mgcexp->exp_obd->u.cli.cl_conn_count != 0) {
+ CDEBUG(D_MGC, "updating log %s\n", cld->cld_logname);
+ mgc_process_log(cld->cld_mgcexp->exp_obd, cld);
+ } else {
+ CDEBUG(D_MGC, "disconnecting, won't update log %s\n",
+ cld->cld_logname);
+ }
+ up_read(&cld->cld_mgcexp->exp_obd->u.cli.cl_sem);
+}
+
+/* this timeout represents how many seconds MGC should wait before
+ * requeue config and recover lock to the MGS. We need to randomize this
+ * in order to not flood the MGS.
+ */
+#define MGC_TIMEOUT_MIN_SECONDS 5
+#define MGC_TIMEOUT_RAND_CENTISEC 0x1ff /* ~500 */
+
+static int mgc_requeue_thread(void *data)
+{
+ bool first = true;
+
+ CDEBUG(D_MGC, "Starting requeue thread\n");
+
+ /* Keep trying failed locks periodically */
+ spin_lock(&config_list_lock);
+ rq_state |= RQ_RUNNING;
+ while (1) {
+ struct l_wait_info lwi;
+ struct config_llog_data *cld, *cld_prev;
+ int rand = cfs_rand() & MGC_TIMEOUT_RAND_CENTISEC;
+ int stopped = !!(rq_state & RQ_STOP);
+ int to;
+
+ /* Any new or requeued lostlocks will change the state */
+ rq_state &= ~(RQ_NOW | RQ_LATER);
+ spin_unlock(&config_list_lock);
+
+ if (first) {
+ first = false;
+ complete(&rq_start);
+ }
+
+ /* Always wait a few seconds to allow the server who
+ caused the lock revocation to finish its setup, plus some
+ random so everyone doesn't try to reconnect at once. */
+ to = MGC_TIMEOUT_MIN_SECONDS * HZ;
+ to += rand * HZ / 100; /* rand is centi-seconds */
+ lwi = LWI_TIMEOUT(to, NULL, NULL);
+ l_wait_event(rq_waitq, rq_state & (RQ_STOP | RQ_PRECLEANUP),
+ &lwi);
+
+ /*
+ * iterate & processing through the list. for each cld, process
+ * its depending sptlrpc cld firstly (if any) and then itself.
+ *
+ * it's guaranteed any item in the list must have
+ * reference > 0; and if cld_lostlock is set, at
+ * least one reference is taken by the previous enqueue.
+ */
+ cld_prev = NULL;
+
+ spin_lock(&config_list_lock);
+ rq_state &= ~RQ_PRECLEANUP;
+ list_for_each_entry(cld, &config_llog_list,
+ cld_list_chain) {
+ if (!cld->cld_lostlock)
+ continue;
+
+ spin_unlock(&config_list_lock);
+
+ LASSERT(atomic_read(&cld->cld_refcount) > 0);
+
+ /* Whether we enqueued again or not in mgc_process_log,
+ * we're done with the ref from the old enqueue */
+ if (cld_prev)
+ config_log_put(cld_prev);
+ cld_prev = cld;
+
+ cld->cld_lostlock = 0;
+ if (likely(!stopped))
+ do_requeue(cld);
+
+ spin_lock(&config_list_lock);
+ }
+ spin_unlock(&config_list_lock);
+ if (cld_prev)
+ config_log_put(cld_prev);
+
+ /* break after scanning the list so that we can drop
+ * refcount to losing lock clds */
+ if (unlikely(stopped)) {
+ spin_lock(&config_list_lock);
+ break;
+ }
+
+ /* Wait a bit to see if anyone else needs a requeue */
+ lwi = (struct l_wait_info) { 0 };
+ l_wait_event(rq_waitq, rq_state & (RQ_NOW | RQ_STOP),
+ &lwi);
+ spin_lock(&config_list_lock);
+ }
+ /* spinlock and while guarantee RQ_NOW and RQ_LATER are not set */
+ rq_state &= ~RQ_RUNNING;
+ spin_unlock(&config_list_lock);
+
+ complete(&rq_exit);
+
+ CDEBUG(D_MGC, "Ending requeue thread\n");
+ return 0;
+}
+
+/* Add a cld to the list to requeue. Start the requeue thread if needed.
+ We are responsible for dropping the config log reference from here on out. */
+static void mgc_requeue_add(struct config_llog_data *cld)
+{
+ CDEBUG(D_INFO, "log %s: requeue (r=%d sp=%d st=%x)\n",
+ cld->cld_logname, atomic_read(&cld->cld_refcount),
+ cld->cld_stopping, rq_state);
+ LASSERT(atomic_read(&cld->cld_refcount) > 0);
+
+ mutex_lock(&cld->cld_lock);
+ if (cld->cld_stopping || cld->cld_lostlock) {
+ mutex_unlock(&cld->cld_lock);
+ return;
+ }
+ /* this refcount will be released in mgc_requeue_thread. */
+ config_log_get(cld);
+ cld->cld_lostlock = 1;
+ mutex_unlock(&cld->cld_lock);
+
+ /* Hold lock for rq_state */
+ spin_lock(&config_list_lock);
+ if (rq_state & RQ_STOP) {
+ spin_unlock(&config_list_lock);
+ cld->cld_lostlock = 0;
+ config_log_put(cld);
+ } else {
+ rq_state |= RQ_NOW;
+ spin_unlock(&config_list_lock);
+ wake_up(&rq_waitq);
+ }
+}
+
+static int mgc_llog_init(const struct lu_env *env, struct obd_device *obd)
+{
+ struct llog_ctxt *ctxt;
+ int rc;
+
+ /* setup only remote ctxt, the local disk context is switched per each
+ * filesystem during mgc_fs_setup() */
+ rc = llog_setup(env, obd, &obd->obd_olg, LLOG_CONFIG_REPL_CTXT, obd,
+ &llog_client_ops);
+ if (rc)
+ return rc;
+
+ ctxt = llog_get_context(obd, LLOG_CONFIG_REPL_CTXT);
+ LASSERT(ctxt);
+
+ llog_initiator_connect(ctxt);
+ llog_ctxt_put(ctxt);
+
+ return 0;
+}
+
+static int mgc_llog_fini(const struct lu_env *env, struct obd_device *obd)
+{
+ struct llog_ctxt *ctxt;
+
+ ctxt = llog_get_context(obd, LLOG_CONFIG_REPL_CTXT);
+ if (ctxt)
+ llog_cleanup(env, ctxt);
+
+ return 0;
+}
+
+static atomic_t mgc_count = ATOMIC_INIT(0);
+static int mgc_precleanup(struct obd_device *obd, enum obd_cleanup_stage stage)
+{
+ int rc = 0;
+ int temp;
+
+ switch (stage) {
+ case OBD_CLEANUP_EARLY:
+ break;
+ case OBD_CLEANUP_EXPORTS:
+ if (atomic_dec_and_test(&mgc_count)) {
+ LASSERT(rq_state & RQ_RUNNING);
+ /* stop requeue thread */
+ temp = RQ_STOP;
+ } else {
+ /* wakeup requeue thread to clean our cld */
+ temp = RQ_NOW | RQ_PRECLEANUP;
+ }
+ spin_lock(&config_list_lock);
+ rq_state |= temp;
+ spin_unlock(&config_list_lock);
+ wake_up(&rq_waitq);
+ if (temp & RQ_STOP)
+ wait_for_completion(&rq_exit);
+ obd_cleanup_client_import(obd);
+ rc = mgc_llog_fini(NULL, obd);
+ if (rc != 0)
+ CERROR("failed to cleanup llogging subsystems\n");
+ break;
+ }
+ return rc;
+}
+
+static int mgc_cleanup(struct obd_device *obd)
+{
+ /* COMPAT_146 - old config logs may have added profiles we don't
+ know about */
+ if (obd->obd_type->typ_refcnt <= 1)
+ /* Only for the last mgc */
+ class_del_profiles();
+
+ lprocfs_obd_cleanup(obd);
+ ptlrpcd_decref();
+
+ return client_obd_cleanup(obd);
+}
+
+static int mgc_setup(struct obd_device *obd, struct lustre_cfg *lcfg)
+{
+ struct lprocfs_static_vars lvars;
+ int rc;
+
+ ptlrpcd_addref();
+
+ rc = client_obd_setup(obd, lcfg);
+ if (rc)
+ goto err_decref;
+
+ rc = mgc_llog_init(NULL, obd);
+ if (rc) {
+ CERROR("failed to setup llogging subsystems\n");
+ goto err_cleanup;
+ }
+
+ lprocfs_mgc_init_vars(&lvars);
+ lprocfs_obd_setup(obd, lvars.obd_vars);
+ sptlrpc_lprocfs_cliobd_attach(obd);
+
+ if (atomic_inc_return(&mgc_count) == 1) {
+ rq_state = 0;
+ init_waitqueue_head(&rq_waitq);
+
+ /* start requeue thread */
+ rc = PTR_ERR(kthread_run(mgc_requeue_thread, NULL,
+ "ll_cfg_requeue"));
+ if (IS_ERR_VALUE(rc)) {
+ CERROR("%s: Cannot start requeue thread (%d),no more log updates!\n",
+ obd->obd_name, rc);
+ goto err_cleanup;
+ }
+ /* rc is the task_struct pointer of mgc_requeue_thread. */
+ rc = 0;
+ wait_for_completion(&rq_start);
+ }
+
+ return rc;
+
+err_cleanup:
+ client_obd_cleanup(obd);
+err_decref:
+ ptlrpcd_decref();
+ return rc;
+}
+
+/* based on ll_mdc_blocking_ast */
+static int mgc_blocking_ast(struct ldlm_lock *lock, struct ldlm_lock_desc *desc,
+ void *data, int flag)
+{
+ struct lustre_handle lockh;
+ struct config_llog_data *cld = (struct config_llog_data *)data;
+ int rc = 0;
+
+ switch (flag) {
+ case LDLM_CB_BLOCKING:
+ /* mgs wants the lock, give it up... */
+ LDLM_DEBUG(lock, "MGC blocking CB");
+ ldlm_lock2handle(lock, &lockh);
+ rc = ldlm_cli_cancel(&lockh, LCF_ASYNC);
+ break;
+ case LDLM_CB_CANCELING:
+ /* We've given up the lock, prepare ourselves to update. */
+ LDLM_DEBUG(lock, "MGC cancel CB");
+
+ CDEBUG(D_MGC, "Lock res "DLDLMRES" (%.8s)\n",
+ PLDLMRES(lock->l_resource),
+ (char *)&lock->l_resource->lr_name.name[0]);
+
+ if (!cld) {
+ CDEBUG(D_INFO, "missing data, won't requeue\n");
+ break;
+ }
+
+ /* held at mgc_process_log(). */
+ LASSERT(atomic_read(&cld->cld_refcount) > 0);
+ /* Are we done with this log? */
+ if (cld->cld_stopping) {
+ CDEBUG(D_MGC, "log %s: stopping, won't requeue\n",
+ cld->cld_logname);
+ config_log_put(cld);
+ break;
+ }
+ /* Make sure not to re-enqueue when the mgc is stopping
+ (we get called from client_disconnect_export) */
+ if (!lock->l_conn_export ||
+ !lock->l_conn_export->exp_obd->u.cli.cl_conn_count) {
+ CDEBUG(D_MGC, "log %.8s: disconnecting, won't requeue\n",
+ cld->cld_logname);
+ config_log_put(cld);
+ break;
+ }
+
+ /* Re-enqueue now */
+ mgc_requeue_add(cld);
+ config_log_put(cld);
+ break;
+ default:
+ LBUG();
+ }
+
+ return rc;
+}
+
+/* Not sure where this should go... */
+/* This is the timeout value for MGS_CONNECT request plus a ping interval, such
+ * that we can have a chance to try the secondary MGS if any. */
+#define MGC_ENQUEUE_LIMIT (INITIAL_CONNECT_TIMEOUT + (AT_OFF ? 0 : at_min) \
+ + PING_INTERVAL)
+#define MGC_TARGET_REG_LIMIT 10
+#define MGC_SEND_PARAM_LIMIT 10
+
+/* Send parameter to MGS*/
+static int mgc_set_mgs_param(struct obd_export *exp,
+ struct mgs_send_param *msp)
+{
+ struct ptlrpc_request *req;
+ struct mgs_send_param *req_msp, *rep_msp;
+ int rc;
+
+ req = ptlrpc_request_alloc_pack(class_exp2cliimp(exp),
+ &RQF_MGS_SET_INFO, LUSTRE_MGS_VERSION,
+ MGS_SET_INFO);
+ if (!req)
+ return -ENOMEM;
+
+ req_msp = req_capsule_client_get(&req->rq_pill, &RMF_MGS_SEND_PARAM);
+ if (!req_msp) {
+ ptlrpc_req_finished(req);
+ return -ENOMEM;
+ }
+
+ memcpy(req_msp, msp, sizeof(*req_msp));
+ ptlrpc_request_set_replen(req);
+
+ /* Limit how long we will wait for the enqueue to complete */
+ req->rq_delay_limit = MGC_SEND_PARAM_LIMIT;
+ rc = ptlrpc_queue_wait(req);
+ if (!rc) {
+ rep_msp = req_capsule_server_get(&req->rq_pill, &RMF_MGS_SEND_PARAM);
+ memcpy(msp, rep_msp, sizeof(*rep_msp));
+ }
+
+ ptlrpc_req_finished(req);
+
+ return rc;
+}
+
+/* Take a config lock so we can get cancel notifications */
+static int mgc_enqueue(struct obd_export *exp, struct lov_stripe_md *lsm,
+ __u32 type, ldlm_policy_data_t *policy, __u32 mode,
+ __u64 *flags, void *bl_cb, void *cp_cb, void *gl_cb,
+ void *data, __u32 lvb_len, void *lvb_swabber,
+ struct lustre_handle *lockh)
+{
+ struct config_llog_data *cld = (struct config_llog_data *)data;
+ struct ldlm_enqueue_info einfo = {
+ .ei_type = type,
+ .ei_mode = mode,
+ .ei_cb_bl = mgc_blocking_ast,
+ .ei_cb_cp = ldlm_completion_ast,
+ };
+ struct ptlrpc_request *req;
+ int short_limit = cld_is_sptlrpc(cld);
+ int rc;
+
+ CDEBUG(D_MGC, "Enqueue for %s (res %#llx)\n", cld->cld_logname,
+ cld->cld_resid.name[0]);
+
+ /* We need a callback for every lockholder, so don't try to
+ ldlm_lock_match (see rev 1.1.2.11.2.47) */
+ req = ptlrpc_request_alloc_pack(class_exp2cliimp(exp),
+ &RQF_LDLM_ENQUEUE, LUSTRE_DLM_VERSION,
+ LDLM_ENQUEUE);
+ if (req == NULL)
+ return -ENOMEM;
+
+ req_capsule_set_size(&req->rq_pill, &RMF_DLM_LVB, RCL_SERVER, 0);
+ ptlrpc_request_set_replen(req);
+
+ /* check if this is server or client */
+ if (cld->cld_cfg.cfg_sb) {
+ struct lustre_sb_info *lsi = s2lsi(cld->cld_cfg.cfg_sb);
+ if (lsi && IS_SERVER(lsi))
+ short_limit = 1;
+ }
+ /* Limit how long we will wait for the enqueue to complete */
+ req->rq_delay_limit = short_limit ? 5 : MGC_ENQUEUE_LIMIT;
+ rc = ldlm_cli_enqueue(exp, &req, &einfo, &cld->cld_resid, NULL, flags,
+ NULL, 0, LVB_T_NONE, lockh, 0);
+ /* A failed enqueue should still call the mgc_blocking_ast,
+ where it will be requeued if needed ("grant failed"). */
+ ptlrpc_req_finished(req);
+ return rc;
+}
+
+static void mgc_notify_active(struct obd_device *unused)
+{
+ /* wakeup mgc_requeue_thread to requeue mgc lock */
+ spin_lock(&config_list_lock);
+ rq_state |= RQ_NOW;
+ spin_unlock(&config_list_lock);
+ wake_up(&rq_waitq);
+
+ /* TODO: Help the MGS rebuild nidtbl. -jay */
+}
+
+/* Send target_reg message to MGS */
+static int mgc_target_register(struct obd_export *exp,
+ struct mgs_target_info *mti)
+{
+ struct ptlrpc_request *req;
+ struct mgs_target_info *req_mti, *rep_mti;
+ int rc;
+
+ req = ptlrpc_request_alloc_pack(class_exp2cliimp(exp),
+ &RQF_MGS_TARGET_REG, LUSTRE_MGS_VERSION,
+ MGS_TARGET_REG);
+ if (req == NULL)
+ return -ENOMEM;
+
+ req_mti = req_capsule_client_get(&req->rq_pill, &RMF_MGS_TARGET_INFO);
+ if (!req_mti) {
+ ptlrpc_req_finished(req);
+ return -ENOMEM;
+ }
+
+ memcpy(req_mti, mti, sizeof(*req_mti));
+ ptlrpc_request_set_replen(req);
+ CDEBUG(D_MGC, "register %s\n", mti->mti_svname);
+ /* Limit how long we will wait for the enqueue to complete */
+ req->rq_delay_limit = MGC_TARGET_REG_LIMIT;
+
+ rc = ptlrpc_queue_wait(req);
+ if (!rc) {
+ rep_mti = req_capsule_server_get(&req->rq_pill,
+ &RMF_MGS_TARGET_INFO);
+ memcpy(mti, rep_mti, sizeof(*rep_mti));
+ CDEBUG(D_MGC, "register %s got index = %d\n",
+ mti->mti_svname, mti->mti_stripe_index);
+ }
+ ptlrpc_req_finished(req);
+
+ return rc;
+}
+
+static int mgc_set_info_async(const struct lu_env *env, struct obd_export *exp,
+ u32 keylen, void *key, u32 vallen,
+ void *val, struct ptlrpc_request_set *set)
+{
+ int rc = -EINVAL;
+
+ /* Turn off initial_recov after we try all backup servers once */
+ if (KEY_IS(KEY_INIT_RECOV_BACKUP)) {
+ struct obd_import *imp = class_exp2cliimp(exp);
+ int value;
+ if (vallen != sizeof(int))
+ return -EINVAL;
+ value = *(int *)val;
+ CDEBUG(D_MGC, "InitRecov %s %d/d%d:i%d:r%d:or%d:%s\n",
+ imp->imp_obd->obd_name, value,
+ imp->imp_deactive, imp->imp_invalid,
+ imp->imp_replayable, imp->imp_obd->obd_replayable,
+ ptlrpc_import_state_name(imp->imp_state));
+ /* Resurrect if we previously died */
+ if ((imp->imp_state != LUSTRE_IMP_FULL &&
+ imp->imp_state != LUSTRE_IMP_NEW) || value > 1)
+ ptlrpc_reconnect_import(imp);
+ return 0;
+ }
+ if (KEY_IS(KEY_SET_INFO)) {
+ struct mgs_send_param *msp;
+
+ msp = (struct mgs_send_param *)val;
+ rc = mgc_set_mgs_param(exp, msp);
+ return rc;
+ }
+ if (KEY_IS(KEY_MGSSEC)) {
+ struct client_obd *cli = &exp->exp_obd->u.cli;
+ struct sptlrpc_flavor flvr;
+
+ /*
+ * empty string means using current flavor, if which haven't
+ * been set yet, set it as null.
+ *
+ * if flavor has been set previously, check the asking flavor
+ * must match the existing one.
+ */
+ if (vallen == 0) {
+ if (cli->cl_flvr_mgc.sf_rpc != SPTLRPC_FLVR_INVALID)
+ return 0;
+ val = "null";
+ vallen = 4;
+ }
+
+ rc = sptlrpc_parse_flavor(val, &flvr);
+ if (rc) {
+ CERROR("invalid sptlrpc flavor %s to MGS\n",
+ (char *) val);
+ return rc;
+ }
+
+ /*
+ * caller already hold a mutex
+ */
+ if (cli->cl_flvr_mgc.sf_rpc == SPTLRPC_FLVR_INVALID) {
+ cli->cl_flvr_mgc = flvr;
+ } else if (memcmp(&cli->cl_flvr_mgc, &flvr,
+ sizeof(flvr)) != 0) {
+ char str[20];
+
+ sptlrpc_flavor2name(&cli->cl_flvr_mgc,
+ str, sizeof(str));
+ LCONSOLE_ERROR("asking sptlrpc flavor %s to MGS but currently %s is in use\n",
+ (char *) val, str);
+ rc = -EPERM;
+ }
+ return rc;
+ }
+
+ return rc;
+}
+
+static int mgc_get_info(const struct lu_env *env, struct obd_export *exp,
+ __u32 keylen, void *key, __u32 *vallen, void *val,
+ struct lov_stripe_md *unused)
+{
+ int rc = -EINVAL;
+
+ if (KEY_IS(KEY_CONN_DATA)) {
+ struct obd_import *imp = class_exp2cliimp(exp);
+ struct obd_connect_data *data = val;
+
+ if (*vallen == sizeof(*data)) {
+ *data = imp->imp_connect_data;
+ rc = 0;
+ }
+ }
+
+ return rc;
+}
+
+static int mgc_import_event(struct obd_device *obd,
+ struct obd_import *imp,
+ enum obd_import_event event)
+{
+ LASSERT(imp->imp_obd == obd);
+ CDEBUG(D_MGC, "import event %#x\n", event);
+
+ switch (event) {
+ case IMP_EVENT_DISCON:
+ /* MGC imports should not wait for recovery */
+ if (OCD_HAS_FLAG(&imp->imp_connect_data, IMP_RECOV))
+ ptlrpc_pinger_ir_down();
+ break;
+ case IMP_EVENT_INACTIVE:
+ break;
+ case IMP_EVENT_INVALIDATE: {
+ struct ldlm_namespace *ns = obd->obd_namespace;
+ ldlm_namespace_cleanup(ns, LDLM_FL_LOCAL_ONLY);
+ break;
+ }
+ case IMP_EVENT_ACTIVE:
+ CDEBUG(D_INFO, "%s: Reactivating import\n", obd->obd_name);
+ /* Clearing obd_no_recov allows us to continue pinging */
+ obd->obd_no_recov = 0;
+ mgc_notify_active(obd);
+ if (OCD_HAS_FLAG(&imp->imp_connect_data, IMP_RECOV))
+ ptlrpc_pinger_ir_up();
+ break;
+ case IMP_EVENT_OCD:
+ break;
+ case IMP_EVENT_DEACTIVATE:
+ case IMP_EVENT_ACTIVATE:
+ break;
+ default:
+ CERROR("Unknown import event %#x\n", event);
+ LBUG();
+ }
+ return 0;
+}
+
+enum {
+ CONFIG_READ_NRPAGES_INIT = 1 << (20 - PAGE_CACHE_SHIFT),
+ CONFIG_READ_NRPAGES = 4
+};
+
+static int mgc_apply_recover_logs(struct obd_device *mgc,
+ struct config_llog_data *cld,
+ __u64 max_version,
+ void *data, int datalen, bool mne_swab)
+{
+ struct config_llog_instance *cfg = &cld->cld_cfg;
+ struct lustre_sb_info *lsi = s2lsi(cfg->cfg_sb);
+ struct mgs_nidtbl_entry *entry;
+ struct lustre_cfg *lcfg;
+ struct lustre_cfg_bufs bufs;
+ u64 prev_version = 0;
+ char *inst;
+ char *buf;
+ int bufsz;
+ int pos;
+ int rc = 0;
+ int off = 0;
+
+ LASSERT(cfg->cfg_instance != NULL);
+ LASSERT(cfg->cfg_sb == cfg->cfg_instance);
+
+ OBD_ALLOC(inst, PAGE_CACHE_SIZE);
+ if (inst == NULL)
+ return -ENOMEM;
+
+ if (!IS_SERVER(lsi)) {
+ pos = snprintf(inst, PAGE_CACHE_SIZE, "%p", cfg->cfg_instance);
+ if (pos >= PAGE_CACHE_SIZE) {
+ OBD_FREE(inst, PAGE_CACHE_SIZE);
+ return -E2BIG;
+ }
+ } else {
+ LASSERT(IS_MDT(lsi));
+ rc = server_name2svname(lsi->lsi_svname, inst, NULL,
+ PAGE_CACHE_SIZE);
+ if (rc) {
+ OBD_FREE(inst, PAGE_CACHE_SIZE);
+ return -EINVAL;
+ }
+ pos = strlen(inst);
+ }
+
+ ++pos;
+ buf = inst + pos;
+ bufsz = PAGE_CACHE_SIZE - pos;
+
+ while (datalen > 0) {
+ int entry_len = sizeof(*entry);
+ int is_ost;
+ struct obd_device *obd;
+ char *obdname;
+ char *cname;
+ char *params;
+ char *uuid;
+
+ rc = -EINVAL;
+ if (datalen < sizeof(*entry))
+ break;
+
+ entry = (typeof(entry))(data + off);
+
+ /* sanity check */
+ if (entry->mne_nid_type != 0) /* only support type 0 for ipv4 */
+ break;
+ if (entry->mne_nid_count == 0) /* at least one nid entry */
+ break;
+ if (entry->mne_nid_size != sizeof(lnet_nid_t))
+ break;
+
+ entry_len += entry->mne_nid_count * entry->mne_nid_size;
+ if (datalen < entry_len) /* must have entry_len at least */
+ break;
+
+ /* Keep this swab for normal mixed endian handling. LU-1644 */
+ if (mne_swab)
+ lustre_swab_mgs_nidtbl_entry(entry);
+ if (entry->mne_length > PAGE_CACHE_SIZE) {
+ CERROR("MNE too large (%u)\n", entry->mne_length);
+ break;
+ }
+
+ if (entry->mne_length < entry_len)
+ break;
+
+ off += entry->mne_length;
+ datalen -= entry->mne_length;
+ if (datalen < 0)
+ break;
+
+ if (entry->mne_version > max_version) {
+ CERROR("entry index(%lld) is over max_index(%lld)\n",
+ entry->mne_version, max_version);
+ break;
+ }
+
+ if (prev_version >= entry->mne_version) {
+ CERROR("index unsorted, prev %lld, now %lld\n",
+ prev_version, entry->mne_version);
+ break;
+ }
+ prev_version = entry->mne_version;
+
+ /*
+ * Write a string with format "nid::instance" to
+ * lustre/<osc|mdc>/<target>-<osc|mdc>-<instance>/import.
+ */
+
+ is_ost = entry->mne_type == LDD_F_SV_TYPE_OST;
+ memset(buf, 0, bufsz);
+ obdname = buf;
+ pos = 0;
+
+ /* lustre-OST0001-osc-<instance #> */
+ strcpy(obdname, cld->cld_logname);
+ cname = strrchr(obdname, '-');
+ if (cname == NULL) {
+ CERROR("mgc %s: invalid logname %s\n",
+ mgc->obd_name, obdname);
+ break;
+ }
+
+ pos = cname - obdname;
+ obdname[pos] = 0;
+ pos += sprintf(obdname + pos, "-%s%04x",
+ is_ost ? "OST" : "MDT", entry->mne_index);
+
+ cname = is_ost ? "osc" : "mdc",
+ pos += sprintf(obdname + pos, "-%s-%s", cname, inst);
+ lustre_cfg_bufs_reset(&bufs, obdname);
+
+ /* find the obd by obdname */
+ obd = class_name2obd(obdname);
+ if (obd == NULL) {
+ CDEBUG(D_INFO, "mgc %s: cannot find obdname %s\n",
+ mgc->obd_name, obdname);
+ rc = 0;
+ /* this is a safe race, when the ost is starting up...*/
+ continue;
+ }
+
+ /* osc.import = "connection=<Conn UUID>::<target instance>" */
+ ++pos;
+ params = buf + pos;
+ pos += sprintf(params, "%s.import=%s", cname, "connection=");
+ uuid = buf + pos;
+
+ down_read(&obd->u.cli.cl_sem);
+ if (obd->u.cli.cl_import == NULL) {
+ /* client does not connect to the OST yet */
+ up_read(&obd->u.cli.cl_sem);
+ rc = 0;
+ continue;
+ }
+
+ /* TODO: iterate all nids to find one */
+ /* find uuid by nid */
+ rc = client_import_find_conn(obd->u.cli.cl_import,
+ entry->u.nids[0],
+ (struct obd_uuid *)uuid);
+ up_read(&obd->u.cli.cl_sem);
+ if (rc < 0) {
+ CERROR("mgc: cannot find uuid by nid %s\n",
+ libcfs_nid2str(entry->u.nids[0]));
+ break;
+ }
+
+ CDEBUG(D_INFO, "Find uuid %s by nid %s\n",
+ uuid, libcfs_nid2str(entry->u.nids[0]));
+
+ pos += strlen(uuid);
+ pos += sprintf(buf + pos, "::%u", entry->mne_instance);
+ LASSERT(pos < bufsz);
+
+ lustre_cfg_bufs_set_string(&bufs, 1, params);
+
+ rc = -ENOMEM;
+ lcfg = lustre_cfg_new(LCFG_PARAM, &bufs);
+ if (lcfg == NULL) {
+ CERROR("mgc: cannot allocate memory\n");
+ break;
+ }
+
+ CDEBUG(D_INFO, "ir apply logs %lld/%lld for %s -> %s\n",
+ prev_version, max_version, obdname, params);
+
+ rc = class_process_config(lcfg);
+ lustre_cfg_free(lcfg);
+ if (rc)
+ CDEBUG(D_INFO, "process config for %s error %d\n",
+ obdname, rc);
+
+ /* continue, even one with error */
+ }
+
+ OBD_FREE(inst, PAGE_CACHE_SIZE);
+ return rc;
+}
+
+/**
+ * This function is called if this client was notified for target restarting
+ * by the MGS. A CONFIG_READ RPC is going to send to fetch recovery logs.
+ */
+static int mgc_process_recover_log(struct obd_device *obd,
+ struct config_llog_data *cld)
+{
+ struct ptlrpc_request *req = NULL;
+ struct config_llog_instance *cfg = &cld->cld_cfg;
+ struct mgs_config_body *body;
+ struct mgs_config_res *res;
+ struct ptlrpc_bulk_desc *desc;
+ struct page **pages;
+ int nrpages;
+ bool eof = true;
+ bool mne_swab = false;
+ int i;
+ int ealen;
+ int rc;
+
+ /* allocate buffer for bulk transfer.
+ * if this is the first time for this mgs to read logs,
+ * CONFIG_READ_NRPAGES_INIT will be used since it will read all logs
+ * once; otherwise, it only reads increment of logs, this should be
+ * small and CONFIG_READ_NRPAGES will be used.
+ */
+ nrpages = CONFIG_READ_NRPAGES;
+ if (cfg->cfg_last_idx == 0) /* the first time */
+ nrpages = CONFIG_READ_NRPAGES_INIT;
+
+ OBD_ALLOC(pages, sizeof(*pages) * nrpages);
+ if (pages == NULL) {
+ rc = -ENOMEM;
+ goto out;
+ }
+
+ for (i = 0; i < nrpages; i++) {
+ pages[i] = alloc_page(GFP_IOFS);
+ if (pages[i] == NULL) {
+ rc = -ENOMEM;
+ goto out;
+ }
+ }
+
+again:
+ LASSERT(cld_is_recover(cld));
+ LASSERT(mutex_is_locked(&cld->cld_lock));
+ req = ptlrpc_request_alloc(class_exp2cliimp(cld->cld_mgcexp),
+ &RQF_MGS_CONFIG_READ);
+ if (req == NULL) {
+ rc = -ENOMEM;
+ goto out;
+ }
+
+ rc = ptlrpc_request_pack(req, LUSTRE_MGS_VERSION, MGS_CONFIG_READ);
+ if (rc)
+ goto out;
+
+ /* pack request */
+ body = req_capsule_client_get(&req->rq_pill, &RMF_MGS_CONFIG_BODY);
+ LASSERT(body != NULL);
+ LASSERT(sizeof(body->mcb_name) > strlen(cld->cld_logname));
+ if (strlcpy(body->mcb_name, cld->cld_logname, sizeof(body->mcb_name))
+ >= sizeof(body->mcb_name)) {
+ rc = -E2BIG;
+ goto out;
+ }
+ body->mcb_offset = cfg->cfg_last_idx + 1;
+ body->mcb_type = cld->cld_type;
+ body->mcb_bits = PAGE_CACHE_SHIFT;
+ body->mcb_units = nrpages;
+
+ /* allocate bulk transfer descriptor */
+ desc = ptlrpc_prep_bulk_imp(req, nrpages, 1, BULK_PUT_SINK,
+ MGS_BULK_PORTAL);
+ if (desc == NULL) {
+ rc = -ENOMEM;
+ goto out;
+ }
+
+ for (i = 0; i < nrpages; i++)
+ ptlrpc_prep_bulk_page_pin(desc, pages[i], 0, PAGE_CACHE_SIZE);
+
+ ptlrpc_request_set_replen(req);
+ rc = ptlrpc_queue_wait(req);
+ if (rc)
+ goto out;
+
+ res = req_capsule_server_get(&req->rq_pill, &RMF_MGS_CONFIG_RES);
+ if (res->mcr_size < res->mcr_offset) {
+ rc = -EINVAL;
+ goto out;
+ }
+
+ /* always update the index even though it might have errors with
+ * handling the recover logs */
+ cfg->cfg_last_idx = res->mcr_offset;
+ eof = res->mcr_offset == res->mcr_size;
+
+ CDEBUG(D_INFO, "Latest version %lld, more %d.\n",
+ res->mcr_offset, eof == false);
+
+ ealen = sptlrpc_cli_unwrap_bulk_read(req, req->rq_bulk, 0);
+ if (ealen < 0) {
+ rc = ealen;
+ goto out;
+ }
+
+ if (ealen > nrpages << PAGE_CACHE_SHIFT) {
+ rc = -EINVAL;
+ goto out;
+ }
+
+ if (ealen == 0) { /* no logs transferred */
+ if (!eof)
+ rc = -EINVAL;
+ goto out;
+ }
+
+ mne_swab = !!ptlrpc_rep_need_swab(req);
+#if LUSTRE_VERSION_CODE < OBD_OCD_VERSION(3, 2, 50, 0)
+ /* This import flag means the server did an extra swab of IR MNE
+ * records (fixed in LU-1252), reverse it here if needed. LU-1644 */
+ if (unlikely(req->rq_import->imp_need_mne_swab))
+ mne_swab = !mne_swab;
+#else
+#warning "LU-1644: Remove old OBD_CONNECT_MNE_SWAB fixup and imp_need_mne_swab"
+#endif
+
+ for (i = 0; i < nrpages && ealen > 0; i++) {
+ int rc2;
+ void *ptr;
+
+ ptr = kmap(pages[i]);
+ rc2 = mgc_apply_recover_logs(obd, cld, res->mcr_offset, ptr,
+ min_t(int, ealen, PAGE_CACHE_SIZE),
+ mne_swab);
+ kunmap(pages[i]);
+ if (rc2 < 0) {
+ CWARN("Process recover log %s error %d\n",
+ cld->cld_logname, rc2);
+ break;
+ }
+
+ ealen -= PAGE_CACHE_SIZE;
+ }
+
+out:
+ if (req)
+ ptlrpc_req_finished(req);
+
+ if (rc == 0 && !eof)
+ goto again;
+
+ if (pages) {
+ for (i = 0; i < nrpages; i++) {
+ if (pages[i] == NULL)
+ break;
+ __free_page(pages[i]);
+ }
+ OBD_FREE(pages, sizeof(*pages) * nrpages);
+ }
+ return rc;
+}
+
+/* local_only means it cannot get remote llogs */
+static int mgc_process_cfg_log(struct obd_device *mgc,
+ struct config_llog_data *cld, int local_only)
+{
+ struct llog_ctxt *ctxt;
+ struct lustre_sb_info *lsi = NULL;
+ int rc = 0;
+ bool sptlrpc_started = false;
+ struct lu_env *env;
+
+ LASSERT(cld);
+ LASSERT(mutex_is_locked(&cld->cld_lock));
+
+ /*
+ * local copy of sptlrpc log is controlled elsewhere, don't try to
+ * read it up here.
+ */
+ if (cld_is_sptlrpc(cld) && local_only)
+ return 0;
+
+ if (cld->cld_cfg.cfg_sb)
+ lsi = s2lsi(cld->cld_cfg.cfg_sb);
+
+ OBD_ALLOC_PTR(env);
+ if (env == NULL)
+ return -ENOMEM;
+
+ rc = lu_env_init(env, LCT_MG_THREAD);
+ if (rc)
+ goto out_free;
+
+ ctxt = llog_get_context(mgc, LLOG_CONFIG_REPL_CTXT);
+ LASSERT(ctxt);
+
+ if (local_only) /* no local log at client side */ {
+ rc = -EIO;
+ goto out_pop;
+ }
+
+ if (cld_is_sptlrpc(cld)) {
+ sptlrpc_conf_log_update_begin(cld->cld_logname);
+ sptlrpc_started = true;
+ }
+
+ /* logname and instance info should be the same, so use our
+ * copy of the instance for the update. The cfg_last_idx will
+ * be updated here. */
+ rc = class_config_parse_llog(env, ctxt, cld->cld_logname,
+ &cld->cld_cfg);
+
+out_pop:
+ __llog_ctxt_put(env, ctxt);
+
+ /*
+ * update settings on existing OBDs. doing it inside
+ * of llog_process_lock so no device is attaching/detaching
+ * in parallel.
+ * the logname must be <fsname>-sptlrpc
+ */
+ if (sptlrpc_started) {
+ LASSERT(cld_is_sptlrpc(cld));
+ sptlrpc_conf_log_update_end(cld->cld_logname);
+ class_notify_sptlrpc_conf(cld->cld_logname,
+ strlen(cld->cld_logname) -
+ strlen("-sptlrpc"));
+ }
+
+ lu_env_fini(env);
+out_free:
+ OBD_FREE_PTR(env);
+ return rc;
+}
+
+/** Get a config log from the MGS and process it.
+ * This func is called for both clients and servers.
+ * Copy the log locally before parsing it if appropriate (non-MGS server)
+ */
+int mgc_process_log(struct obd_device *mgc, struct config_llog_data *cld)
+{
+ struct lustre_handle lockh = { 0 };
+ __u64 flags = LDLM_FL_NO_LRU;
+ int rc = 0, rcl;
+
+ LASSERT(cld);
+
+ /* I don't want multiple processes running process_log at once --
+ sounds like badness. It actually might be fine, as long as
+ we're not trying to update from the same log
+ simultaneously (in which case we should use a per-log sem.) */
+ mutex_lock(&cld->cld_lock);
+ if (cld->cld_stopping) {
+ mutex_unlock(&cld->cld_lock);
+ return 0;
+ }
+
+ OBD_FAIL_TIMEOUT(OBD_FAIL_MGC_PAUSE_PROCESS_LOG, 20);
+
+ CDEBUG(D_MGC, "Process log %s:%p from %d\n", cld->cld_logname,
+ cld->cld_cfg.cfg_instance, cld->cld_cfg.cfg_last_idx + 1);
+
+ /* Get the cfg lock on the llog */
+ rcl = mgc_enqueue(mgc->u.cli.cl_mgc_mgsexp, NULL, LDLM_PLAIN, NULL,
+ LCK_CR, &flags, NULL, NULL, NULL,
+ cld, 0, NULL, &lockh);
+ if (rcl == 0) {
+ /* Get the cld, it will be released in mgc_blocking_ast. */
+ config_log_get(cld);
+ rc = ldlm_lock_set_data(&lockh, (void *)cld);
+ LASSERT(rc == 0);
+ } else {
+ CDEBUG(D_MGC, "Can't get cfg lock: %d\n", rcl);
+
+ /* mark cld_lostlock so that it will requeue
+ * after MGC becomes available. */
+ cld->cld_lostlock = 1;
+ /* Get extra reference, it will be put in requeue thread */
+ config_log_get(cld);
+ }
+
+
+ if (cld_is_recover(cld)) {
+ rc = 0; /* this is not a fatal error for recover log */
+ if (rcl == 0)
+ rc = mgc_process_recover_log(mgc, cld);
+ } else {
+ rc = mgc_process_cfg_log(mgc, cld, rcl != 0);
+ }
+
+ CDEBUG(D_MGC, "%s: configuration from log '%s' %sed (%d).\n",
+ mgc->obd_name, cld->cld_logname, rc ? "fail" : "succeed", rc);
+
+ mutex_unlock(&cld->cld_lock);
+
+ /* Now drop the lock so MGS can revoke it */
+ if (!rcl)
+ ldlm_lock_decref(&lockh, LCK_CR);
+
+ return rc;
+}
+
+
+/** Called from lustre_process_log.
+ * LCFG_LOG_START gets the config log from the MGS, processes it to start
+ * any services, and adds it to the list logs to watch (follow).
+ */
+static int mgc_process_config(struct obd_device *obd, u32 len, void *buf)
+{
+ struct lustre_cfg *lcfg = buf;
+ struct config_llog_instance *cfg = NULL;
+ char *logname;
+ int rc = 0;
+
+ switch (lcfg->lcfg_command) {
+ case LCFG_LOV_ADD_OBD: {
+ /* Overloading this cfg command: register a new target */
+ struct mgs_target_info *mti;
+
+ if (LUSTRE_CFG_BUFLEN(lcfg, 1) !=
+ sizeof(struct mgs_target_info)) {
+ rc = -EINVAL;
+ goto out;
+ }
+
+ mti = (struct mgs_target_info *)lustre_cfg_buf(lcfg, 1);
+ CDEBUG(D_MGC, "add_target %s %#x\n",
+ mti->mti_svname, mti->mti_flags);
+ rc = mgc_target_register(obd->u.cli.cl_mgc_mgsexp, mti);
+ break;
+ }
+ case LCFG_LOV_DEL_OBD:
+ /* Unregister has no meaning at the moment. */
+ CERROR("lov_del_obd unimplemented\n");
+ rc = -ENOSYS;
+ break;
+ case LCFG_SPTLRPC_CONF: {
+ rc = sptlrpc_process_config(lcfg);
+ break;
+ }
+ case LCFG_LOG_START: {
+ struct config_llog_data *cld;
+ struct super_block *sb;
+
+ logname = lustre_cfg_string(lcfg, 1);
+ cfg = (struct config_llog_instance *)lustre_cfg_buf(lcfg, 2);
+ sb = *(struct super_block **)lustre_cfg_buf(lcfg, 3);
+
+ CDEBUG(D_MGC, "parse_log %s from %d\n", logname,
+ cfg->cfg_last_idx);
+
+ /* We're only called through here on the initial mount */
+ rc = config_log_add(obd, logname, cfg, sb);
+ if (rc)
+ break;
+ cld = config_log_find(logname, cfg);
+ if (cld == NULL) {
+ rc = -ENOENT;
+ break;
+ }
+
+ /* COMPAT_146 */
+ /* FIXME only set this for old logs! Right now this forces
+ us to always skip the "inside markers" check */
+ cld->cld_cfg.cfg_flags |= CFG_F_COMPAT146;
+
+ rc = mgc_process_log(obd, cld);
+ if (rc == 0 && cld->cld_recover != NULL) {
+ if (OCD_HAS_FLAG(&obd->u.cli.cl_import->
+ imp_connect_data, IMP_RECOV)) {
+ rc = mgc_process_log(obd, cld->cld_recover);
+ } else {
+ struct config_llog_data *cir = cld->cld_recover;
+ cld->cld_recover = NULL;
+ config_log_put(cir);
+ }
+ if (rc)
+ CERROR("Cannot process recover llog %d\n", rc);
+ }
+
+ if (rc == 0 && cld->cld_params != NULL) {
+ rc = mgc_process_log(obd, cld->cld_params);
+ if (rc == -ENOENT) {
+ CDEBUG(D_MGC,
+ "There is no params config file yet\n");
+ rc = 0;
+ }
+ /* params log is optional */
+ if (rc)
+ CERROR(
+ "%s: can't process params llog: rc = %d\n",
+ obd->obd_name, rc);
+ }
+ config_log_put(cld);
+
+ break;
+ }
+ case LCFG_LOG_END: {
+ logname = lustre_cfg_string(lcfg, 1);
+
+ if (lcfg->lcfg_bufcount >= 2)
+ cfg = (struct config_llog_instance *)lustre_cfg_buf(
+ lcfg, 2);
+ rc = config_log_end(logname, cfg);
+ break;
+ }
+ default: {
+ CERROR("Unknown command: %d\n", lcfg->lcfg_command);
+ rc = -EINVAL;
+ goto out;
+
+ }
+ }
+out:
+ return rc;
+}
+
+struct obd_ops mgc_obd_ops = {
+ .o_owner = THIS_MODULE,
+ .o_setup = mgc_setup,
+ .o_precleanup = mgc_precleanup,
+ .o_cleanup = mgc_cleanup,
+ .o_add_conn = client_import_add_conn,
+ .o_del_conn = client_import_del_conn,
+ .o_connect = client_connect_import,
+ .o_disconnect = client_disconnect_export,
+ /* .o_enqueue = mgc_enqueue, */
+ /* .o_iocontrol = mgc_iocontrol, */
+ .o_set_info_async = mgc_set_info_async,
+ .o_get_info = mgc_get_info,
+ .o_import_event = mgc_import_event,
+ .o_process_config = mgc_process_config,
+};
+
+static int __init mgc_init(void)
+{
+ return class_register_type(&mgc_obd_ops, NULL, NULL,
+ LUSTRE_MGC_NAME, NULL);
+}
+
+static void /*__exit*/ mgc_exit(void)
+{
+ class_unregister_type(LUSTRE_MGC_NAME);
+}
+
+MODULE_AUTHOR("Sun Microsystems, Inc. <http://www.lustre.org/>");
+MODULE_DESCRIPTION("Lustre Management Client");
+MODULE_LICENSE("GPL");
+
+module_init(mgc_init);
+module_exit(mgc_exit);
diff --git a/drivers/staging/lustre/lustre/obdclass/Makefile b/drivers/staging/lustre/lustre/obdclass/Makefile
new file mode 100644
index 000000000..e89468179
--- /dev/null
+++ b/drivers/staging/lustre/lustre/obdclass/Makefile
@@ -0,0 +1,11 @@
+obj-$(CONFIG_LUSTRE_FS) += obdclass.o
+
+obdclass-y := linux/linux-module.o linux/linux-obdo.o linux/linux-sysctl.o \
+ llog.o llog_cat.o llog_obd.o llog_swab.o class_obd.o debug.o \
+ genops.o uuid.o lprocfs_status.o \
+ lustre_handles.o lustre_peer.o \
+ statfs_pack.o obdo.o obd_config.o obd_mount.o \
+ lu_object.o dt_object.o capa.o cl_object.o \
+ cl_page.o cl_lock.o cl_io.o lu_ref.o acl.o
+
+obdclass-$(CONFIG_PROC_FS) += lprocfs_counters.o
diff --git a/drivers/staging/lustre/lustre/obdclass/acl.c b/drivers/staging/lustre/lustre/obdclass/acl.c
new file mode 100644
index 000000000..9a69f6b35
--- /dev/null
+++ b/drivers/staging/lustre/lustre/obdclass/acl.c
@@ -0,0 +1,548 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2012, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ *
+ * lustre/obdclass/acl.c
+ *
+ * Lustre Access Control List.
+ *
+ * Author: Fan Yong <fanyong@clusterfs.com>
+ */
+
+#define DEBUG_SUBSYSTEM S_SEC
+#include "../include/lu_object.h"
+#include "../include/lustre_acl.h"
+#include "../include/lustre_eacl.h"
+#include "../include/obd_support.h"
+
+#ifdef CONFIG_FS_POSIX_ACL
+
+#define CFS_ACL_XATTR_VERSION POSIX_ACL_XATTR_VERSION
+
+enum {
+ ES_UNK = 0, /* unknown stat */
+ ES_UNC = 1, /* ACL entry is not changed */
+ ES_MOD = 2, /* ACL entry is modified */
+ ES_ADD = 3, /* ACL entry is added */
+ ES_DEL = 4 /* ACL entry is deleted */
+};
+
+static inline void lustre_ext_acl_le_to_cpu(ext_acl_xattr_entry *d,
+ ext_acl_xattr_entry *s)
+{
+ d->e_tag = le16_to_cpu(s->e_tag);
+ d->e_perm = le16_to_cpu(s->e_perm);
+ d->e_id = le32_to_cpu(s->e_id);
+ d->e_stat = le32_to_cpu(s->e_stat);
+}
+
+static inline void lustre_ext_acl_cpu_to_le(ext_acl_xattr_entry *d,
+ ext_acl_xattr_entry *s)
+{
+ d->e_tag = cpu_to_le16(s->e_tag);
+ d->e_perm = cpu_to_le16(s->e_perm);
+ d->e_id = cpu_to_le32(s->e_id);
+ d->e_stat = cpu_to_le32(s->e_stat);
+}
+
+static inline void lustre_posix_acl_le_to_cpu(posix_acl_xattr_entry *d,
+ posix_acl_xattr_entry *s)
+{
+ d->e_tag = le16_to_cpu(s->e_tag);
+ d->e_perm = le16_to_cpu(s->e_perm);
+ d->e_id = le32_to_cpu(s->e_id);
+}
+
+static inline void lustre_posix_acl_cpu_to_le(posix_acl_xattr_entry *d,
+ posix_acl_xattr_entry *s)
+{
+ d->e_tag = cpu_to_le16(s->e_tag);
+ d->e_perm = cpu_to_le16(s->e_perm);
+ d->e_id = cpu_to_le32(s->e_id);
+}
+
+
+/* if "new_count == 0", then "new = {a_version, NULL}", NOT NULL. */
+static int lustre_posix_acl_xattr_reduce_space(posix_acl_xattr_header **header,
+ int old_count, int new_count)
+{
+ int old_size = CFS_ACL_XATTR_SIZE(old_count, posix_acl_xattr);
+ int new_size = CFS_ACL_XATTR_SIZE(new_count, posix_acl_xattr);
+ posix_acl_xattr_header *new;
+
+ if (unlikely(old_count <= new_count))
+ return old_size;
+
+ OBD_ALLOC(new, new_size);
+ if (unlikely(new == NULL))
+ return -ENOMEM;
+
+ memcpy(new, *header, new_size);
+ OBD_FREE(*header, old_size);
+ *header = new;
+ return new_size;
+}
+
+/* if "new_count == 0", then "new = {0, NULL}", NOT NULL. */
+static int lustre_ext_acl_xattr_reduce_space(ext_acl_xattr_header **header,
+ int old_count)
+{
+ int ext_count = le32_to_cpu((*header)->a_count);
+ int ext_size = CFS_ACL_XATTR_SIZE(ext_count, ext_acl_xattr);
+ int old_size = CFS_ACL_XATTR_SIZE(old_count, ext_acl_xattr);
+ ext_acl_xattr_header *new;
+
+ if (unlikely(old_count <= ext_count))
+ return 0;
+
+ OBD_ALLOC(new, ext_size);
+ if (unlikely(new == NULL))
+ return -ENOMEM;
+
+ memcpy(new, *header, ext_size);
+ OBD_FREE(*header, old_size);
+ *header = new;
+ return 0;
+}
+
+/*
+ * Generate new extended ACL based on the posix ACL.
+ */
+ext_acl_xattr_header *
+lustre_posix_acl_xattr_2ext(posix_acl_xattr_header *header, int size)
+{
+ int count, i, esize;
+ ext_acl_xattr_header *new;
+
+ if (unlikely(size < 0))
+ return ERR_PTR(-EINVAL);
+ else if (!size)
+ count = 0;
+ else
+ count = CFS_ACL_XATTR_COUNT(size, posix_acl_xattr);
+ esize = CFS_ACL_XATTR_SIZE(count, ext_acl_xattr);
+ OBD_ALLOC(new, esize);
+ if (unlikely(new == NULL))
+ return ERR_PTR(-ENOMEM);
+
+ new->a_count = cpu_to_le32(count);
+ for (i = 0; i < count; i++) {
+ new->a_entries[i].e_tag = header->a_entries[i].e_tag;
+ new->a_entries[i].e_perm = header->a_entries[i].e_perm;
+ new->a_entries[i].e_id = header->a_entries[i].e_id;
+ new->a_entries[i].e_stat = cpu_to_le32(ES_UNK);
+ }
+
+ return new;
+}
+EXPORT_SYMBOL(lustre_posix_acl_xattr_2ext);
+
+/*
+ * Filter out the "nobody" entries in the posix ACL.
+ */
+int lustre_posix_acl_xattr_filter(posix_acl_xattr_header *header, size_t size,
+ posix_acl_xattr_header **out)
+{
+ int count, i, j, rc = 0;
+ __u32 id;
+ posix_acl_xattr_header *new;
+
+ if (!size)
+ return 0;
+ if (size < sizeof(*new))
+ return -EINVAL;
+
+ OBD_ALLOC(new, size);
+ if (unlikely(new == NULL))
+ return -ENOMEM;
+
+ new->a_version = cpu_to_le32(CFS_ACL_XATTR_VERSION);
+ count = CFS_ACL_XATTR_COUNT(size, posix_acl_xattr);
+ for (i = 0, j = 0; i < count; i++) {
+ id = le32_to_cpu(header->a_entries[i].e_id);
+ switch (le16_to_cpu(header->a_entries[i].e_tag)) {
+ case ACL_USER_OBJ:
+ case ACL_GROUP_OBJ:
+ case ACL_MASK:
+ case ACL_OTHER:
+ if (id != ACL_UNDEFINED_ID) {
+ rc = -EIO;
+ goto _out;
+ }
+
+ memcpy(&new->a_entries[j++], &header->a_entries[i],
+ sizeof(posix_acl_xattr_entry));
+ break;
+ case ACL_USER:
+ if (id != NOBODY_UID)
+ memcpy(&new->a_entries[j++],
+ &header->a_entries[i],
+ sizeof(posix_acl_xattr_entry));
+ break;
+ case ACL_GROUP:
+ if (id != NOBODY_GID)
+ memcpy(&new->a_entries[j++],
+ &header->a_entries[i],
+ sizeof(posix_acl_xattr_entry));
+ break;
+ default:
+ rc = -EIO;
+ goto _out;
+ }
+ }
+
+ /* free unused space. */
+ rc = lustre_posix_acl_xattr_reduce_space(&new, count, j);
+ if (rc >= 0) {
+ size = rc;
+ *out = new;
+ rc = 0;
+ }
+
+_out:
+ if (rc) {
+ OBD_FREE(new, size);
+ size = rc;
+ }
+ return size;
+}
+EXPORT_SYMBOL(lustre_posix_acl_xattr_filter);
+
+/*
+ * Release the posix ACL space.
+ */
+void lustre_posix_acl_xattr_free(posix_acl_xattr_header *header, int size)
+{
+ OBD_FREE(header, size);
+}
+EXPORT_SYMBOL(lustre_posix_acl_xattr_free);
+
+/*
+ * Release the extended ACL space.
+ */
+void lustre_ext_acl_xattr_free(ext_acl_xattr_header *header)
+{
+ OBD_FREE(header, CFS_ACL_XATTR_SIZE(le32_to_cpu(header->a_count), \
+ ext_acl_xattr));
+}
+EXPORT_SYMBOL(lustre_ext_acl_xattr_free);
+
+static ext_acl_xattr_entry *
+lustre_ext_acl_xattr_search(ext_acl_xattr_header *header,
+ posix_acl_xattr_entry *entry, int *pos)
+{
+ int once, start, end, i, j, count = le32_to_cpu(header->a_count);
+
+ once = 0;
+ start = *pos;
+ end = count;
+
+again:
+ for (i = start; i < end; i++) {
+ if (header->a_entries[i].e_tag == entry->e_tag &&
+ header->a_entries[i].e_id == entry->e_id) {
+ j = i;
+ if (++i >= count)
+ i = 0;
+ *pos = i;
+ return &header->a_entries[j];
+ }
+ }
+
+ if (!once) {
+ once = 1;
+ start = 0;
+ end = *pos;
+ goto again;
+ }
+
+ return NULL;
+}
+
+/*
+ * Merge the posix ACL and the extended ACL into new posix ACL.
+ */
+int lustre_acl_xattr_merge2posix(posix_acl_xattr_header *posix_header, int size,
+ ext_acl_xattr_header *ext_header,
+ posix_acl_xattr_header **out)
+{
+ int posix_count, posix_size, i, j;
+ int ext_count = le32_to_cpu(ext_header->a_count), pos = 0, rc = 0;
+ posix_acl_xattr_entry pe = {ACL_MASK, 0, ACL_UNDEFINED_ID};
+ posix_acl_xattr_header *new;
+ ext_acl_xattr_entry *ee, ae;
+
+ lustre_posix_acl_cpu_to_le(&pe, &pe);
+ ee = lustre_ext_acl_xattr_search(ext_header, &pe, &pos);
+ if (ee == NULL || le32_to_cpu(ee->e_stat) == ES_DEL) {
+ /* there are only base ACL entries at most. */
+ posix_count = 3;
+ posix_size = CFS_ACL_XATTR_SIZE(posix_count, posix_acl_xattr);
+ OBD_ALLOC(new, posix_size);
+ if (unlikely(new == NULL))
+ return -ENOMEM;
+
+ new->a_version = cpu_to_le32(CFS_ACL_XATTR_VERSION);
+ for (i = 0, j = 0; i < ext_count; i++) {
+ lustre_ext_acl_le_to_cpu(&ae,
+ &ext_header->a_entries[i]);
+ switch (ae.e_tag) {
+ case ACL_USER_OBJ:
+ case ACL_GROUP_OBJ:
+ case ACL_OTHER:
+ if (ae.e_id != ACL_UNDEFINED_ID) {
+ rc = -EIO;
+ goto _out;
+ }
+
+ if (ae.e_stat != ES_DEL) {
+ new->a_entries[j].e_tag =
+ ext_header->a_entries[i].e_tag;
+ new->a_entries[j].e_perm =
+ ext_header->a_entries[i].e_perm;
+ new->a_entries[j++].e_id =
+ ext_header->a_entries[i].e_id;
+ }
+ break;
+ case ACL_MASK:
+ case ACL_USER:
+ case ACL_GROUP:
+ if (ae.e_stat == ES_DEL)
+ break;
+ default:
+ rc = -EIO;
+ goto _out;
+ }
+ }
+ } else {
+ /* maybe there are valid ACL_USER or ACL_GROUP entries in the
+ * original server-side ACL, they are regarded as ES_UNC stat.*/
+ int ori_posix_count;
+
+ if (unlikely(size < 0))
+ return -EINVAL;
+ else if (!size)
+ ori_posix_count = 0;
+ else
+ ori_posix_count =
+ CFS_ACL_XATTR_COUNT(size, posix_acl_xattr);
+ posix_count = ori_posix_count + ext_count;
+ posix_size =
+ CFS_ACL_XATTR_SIZE(posix_count, posix_acl_xattr);
+ OBD_ALLOC(new, posix_size);
+ if (unlikely(new == NULL))
+ return -ENOMEM;
+
+ new->a_version = cpu_to_le32(CFS_ACL_XATTR_VERSION);
+ /* 1. process the unchanged ACL entries
+ * in the original server-side ACL. */
+ pos = 0;
+ for (i = 0, j = 0; i < ori_posix_count; i++) {
+ ee = lustre_ext_acl_xattr_search(ext_header,
+ &posix_header->a_entries[i], &pos);
+ if (ee == NULL)
+ memcpy(&new->a_entries[j++],
+ &posix_header->a_entries[i],
+ sizeof(posix_acl_xattr_entry));
+ }
+
+ /* 2. process the non-deleted entries
+ * from client-side extended ACL. */
+ for (i = 0; i < ext_count; i++) {
+ if (le16_to_cpu(ext_header->a_entries[i].e_stat) !=
+ ES_DEL) {
+ new->a_entries[j].e_tag =
+ ext_header->a_entries[i].e_tag;
+ new->a_entries[j].e_perm =
+ ext_header->a_entries[i].e_perm;
+ new->a_entries[j++].e_id =
+ ext_header->a_entries[i].e_id;
+ }
+ }
+ }
+
+ /* free unused space. */
+ rc = lustre_posix_acl_xattr_reduce_space(&new, posix_count, j);
+ if (rc >= 0) {
+ posix_size = rc;
+ *out = new;
+ rc = 0;
+ }
+
+_out:
+ if (rc) {
+ OBD_FREE(new, posix_size);
+ posix_size = rc;
+ }
+ return posix_size;
+}
+EXPORT_SYMBOL(lustre_acl_xattr_merge2posix);
+
+/*
+ * Merge the posix ACL and the extended ACL into new extended ACL.
+ */
+ext_acl_xattr_header *
+lustre_acl_xattr_merge2ext(posix_acl_xattr_header *posix_header, int size,
+ ext_acl_xattr_header *ext_header)
+{
+ int ori_ext_count, posix_count, ext_count, ext_size;
+ int i, j, pos = 0, rc = 0;
+ posix_acl_xattr_entry pae;
+ ext_acl_xattr_header *new;
+ ext_acl_xattr_entry *ee, eae;
+
+ if (unlikely(size < 0))
+ return ERR_PTR(-EINVAL);
+ else if (!size)
+ posix_count = 0;
+ else
+ posix_count = CFS_ACL_XATTR_COUNT(size, posix_acl_xattr);
+ ori_ext_count = le32_to_cpu(ext_header->a_count);
+ ext_count = posix_count + ori_ext_count;
+ ext_size = CFS_ACL_XATTR_SIZE(ext_count, ext_acl_xattr);
+
+ OBD_ALLOC(new, ext_size);
+ if (unlikely(new == NULL))
+ return ERR_PTR(-ENOMEM);
+
+ for (i = 0, j = 0; i < posix_count; i++) {
+ lustre_posix_acl_le_to_cpu(&pae, &posix_header->a_entries[i]);
+ switch (pae.e_tag) {
+ case ACL_USER_OBJ:
+ case ACL_GROUP_OBJ:
+ case ACL_MASK:
+ case ACL_OTHER:
+ if (pae.e_id != ACL_UNDEFINED_ID) {
+ rc = -EIO;
+ goto out;
+ }
+ case ACL_USER:
+ /* ignore "nobody" entry. */
+ if (pae.e_id == NOBODY_UID)
+ break;
+
+ new->a_entries[j].e_tag =
+ posix_header->a_entries[i].e_tag;
+ new->a_entries[j].e_perm =
+ posix_header->a_entries[i].e_perm;
+ new->a_entries[j].e_id =
+ posix_header->a_entries[i].e_id;
+ ee = lustre_ext_acl_xattr_search(ext_header,
+ &posix_header->a_entries[i], &pos);
+ if (ee) {
+ if (posix_header->a_entries[i].e_perm !=
+ ee->e_perm)
+ /* entry modified. */
+ ee->e_stat =
+ new->a_entries[j++].e_stat =
+ cpu_to_le32(ES_MOD);
+ else
+ /* entry unchanged. */
+ ee->e_stat =
+ new->a_entries[j++].e_stat =
+ cpu_to_le32(ES_UNC);
+ } else {
+ /* new entry. */
+ new->a_entries[j++].e_stat =
+ cpu_to_le32(ES_ADD);
+ }
+ break;
+ case ACL_GROUP:
+ /* ignore "nobody" entry. */
+ if (pae.e_id == NOBODY_GID)
+ break;
+ new->a_entries[j].e_tag =
+ posix_header->a_entries[i].e_tag;
+ new->a_entries[j].e_perm =
+ posix_header->a_entries[i].e_perm;
+ new->a_entries[j].e_id =
+ posix_header->a_entries[i].e_id;
+ ee = lustre_ext_acl_xattr_search(ext_header,
+ &posix_header->a_entries[i], &pos);
+ if (ee) {
+ if (posix_header->a_entries[i].e_perm !=
+ ee->e_perm)
+ /* entry modified. */
+ ee->e_stat =
+ new->a_entries[j++].e_stat =
+ cpu_to_le32(ES_MOD);
+ else
+ /* entry unchanged. */
+ ee->e_stat =
+ new->a_entries[j++].e_stat =
+ cpu_to_le32(ES_UNC);
+ } else {
+ /* new entry. */
+ new->a_entries[j++].e_stat =
+ cpu_to_le32(ES_ADD);
+ }
+ break;
+ default:
+ rc = -EIO;
+ goto out;
+ }
+ }
+
+ /* process deleted entries. */
+ for (i = 0; i < ori_ext_count; i++) {
+ lustre_ext_acl_le_to_cpu(&eae, &ext_header->a_entries[i]);
+ if (eae.e_stat == ES_UNK) {
+ /* ignore "nobody" entry. */
+ if ((eae.e_tag == ACL_USER && eae.e_id == NOBODY_UID) ||
+ (eae.e_tag == ACL_GROUP && eae.e_id == NOBODY_GID))
+ continue;
+
+ new->a_entries[j].e_tag =
+ ext_header->a_entries[i].e_tag;
+ new->a_entries[j].e_perm =
+ ext_header->a_entries[i].e_perm;
+ new->a_entries[j].e_id = ext_header->a_entries[i].e_id;
+ new->a_entries[j++].e_stat = cpu_to_le32(ES_DEL);
+ }
+ }
+
+ new->a_count = cpu_to_le32(j);
+ /* free unused space. */
+ rc = lustre_ext_acl_xattr_reduce_space(&new, ext_count);
+
+out:
+ if (rc) {
+ OBD_FREE(new, ext_size);
+ new = ERR_PTR(rc);
+ }
+ return new;
+}
+EXPORT_SYMBOL(lustre_acl_xattr_merge2ext);
+
+#endif
diff --git a/drivers/staging/lustre/lustre/obdclass/capa.c b/drivers/staging/lustre/lustre/obdclass/capa.c
new file mode 100644
index 000000000..d206b1046
--- /dev/null
+++ b/drivers/staging/lustre/lustre/obdclass/capa.c
@@ -0,0 +1,421 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2012, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ *
+ * lustre/obdclass/capa.c
+ *
+ * Lustre Capability Hash Management
+ *
+ * Author: Lai Siyao<lsy@clusterfs.com>
+ */
+
+#define DEBUG_SUBSYSTEM S_SEC
+
+#include <linux/fs.h>
+#include <asm/unistd.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/crypto.h>
+
+#include "../include/obd_class.h"
+#include "../include/lustre_debug.h"
+#include "../include/lustre/lustre_idl.h"
+
+#include <linux/list.h>
+#include "../include/lustre_capa.h"
+
+#define NR_CAPAHASH 32
+#define CAPA_HASH_SIZE 3000 /* for MDS & OSS */
+
+struct kmem_cache *capa_cachep = NULL;
+
+/* lock for capa hash/capa_list/fo_capa_keys */
+DEFINE_SPINLOCK(capa_lock);
+
+struct list_head capa_list[CAPA_SITE_MAX];
+
+static struct capa_hmac_alg capa_hmac_algs[] = {
+ DEF_CAPA_HMAC_ALG("sha1", SHA1, 20, 20),
+};
+/* capa count */
+int capa_count[CAPA_SITE_MAX] = { 0, };
+
+EXPORT_SYMBOL(capa_cachep);
+EXPORT_SYMBOL(capa_list);
+EXPORT_SYMBOL(capa_lock);
+EXPORT_SYMBOL(capa_count);
+
+static inline
+unsigned int ll_crypto_tfm_alg_min_keysize(struct crypto_blkcipher *tfm)
+{
+ return crypto_blkcipher_tfm(tfm)->__crt_alg->cra_blkcipher.min_keysize;
+}
+
+struct hlist_head *init_capa_hash(void)
+{
+ struct hlist_head *hash;
+ int nr_hash, i;
+
+ OBD_ALLOC(hash, PAGE_CACHE_SIZE);
+ if (!hash)
+ return NULL;
+
+ nr_hash = PAGE_CACHE_SIZE / sizeof(struct hlist_head);
+ LASSERT(nr_hash > NR_CAPAHASH);
+
+ for (i = 0; i < NR_CAPAHASH; i++)
+ INIT_HLIST_HEAD(hash + i);
+ return hash;
+}
+EXPORT_SYMBOL(init_capa_hash);
+
+static inline int capa_on_server(struct obd_capa *ocapa)
+{
+ return ocapa->c_site == CAPA_SITE_SERVER;
+}
+
+static inline void capa_delete(struct obd_capa *ocapa)
+{
+ LASSERT(capa_on_server(ocapa));
+ hlist_del_init(&ocapa->u.tgt.c_hash);
+ list_del_init(&ocapa->c_list);
+ capa_count[ocapa->c_site]--;
+ /* release the ref when alloc */
+ capa_put(ocapa);
+}
+
+void cleanup_capa_hash(struct hlist_head *hash)
+{
+ int i;
+ struct hlist_node *next;
+ struct obd_capa *oc;
+
+ spin_lock(&capa_lock);
+ for (i = 0; i < NR_CAPAHASH; i++) {
+ hlist_for_each_entry_safe(oc, next, hash + i,
+ u.tgt.c_hash)
+ capa_delete(oc);
+ }
+ spin_unlock(&capa_lock);
+
+ OBD_FREE(hash, PAGE_CACHE_SIZE);
+}
+EXPORT_SYMBOL(cleanup_capa_hash);
+
+static inline int capa_hashfn(struct lu_fid *fid)
+{
+ return (fid_oid(fid) ^ fid_ver(fid)) *
+ (unsigned long)(fid_seq(fid) + 1) % NR_CAPAHASH;
+}
+
+/* capa renewal time check is earlier than that on client, which is to prevent
+ * client renew right after obtaining it. */
+static inline int capa_is_to_expire(struct obd_capa *oc)
+{
+ return time_before(cfs_time_sub(oc->c_expiry,
+ cfs_time_seconds(oc->c_capa.lc_timeout)*2/3),
+ cfs_time_current());
+}
+
+static struct obd_capa *find_capa(struct lustre_capa *capa,
+ struct hlist_head *head, int alive)
+{
+ struct obd_capa *ocapa;
+ int len = alive ? offsetof(struct lustre_capa, lc_keyid):sizeof(*capa);
+
+ hlist_for_each_entry(ocapa, head, u.tgt.c_hash) {
+ if (memcmp(&ocapa->c_capa, capa, len))
+ continue;
+ /* don't return one that will expire soon in this case */
+ if (alive && capa_is_to_expire(ocapa))
+ continue;
+
+ LASSERT(capa_on_server(ocapa));
+
+ DEBUG_CAPA(D_SEC, &ocapa->c_capa, "found");
+ return ocapa;
+ }
+
+ return NULL;
+}
+
+#define LRU_CAPA_DELETE_COUNT 12
+static inline void capa_delete_lru(struct list_head *head)
+{
+ struct obd_capa *ocapa;
+ struct list_head *node = head->next;
+ int count = 0;
+
+ /* free LRU_CAPA_DELETE_COUNT unused capa from head */
+ while (count++ < LRU_CAPA_DELETE_COUNT) {
+ ocapa = list_entry(node, struct obd_capa, c_list);
+ node = node->next;
+ if (atomic_read(&ocapa->c_refc))
+ continue;
+
+ DEBUG_CAPA(D_SEC, &ocapa->c_capa, "free lru");
+ capa_delete(ocapa);
+ }
+}
+
+/* add or update */
+struct obd_capa *capa_add(struct hlist_head *hash, struct lustre_capa *capa)
+{
+ struct hlist_head *head = hash + capa_hashfn(&capa->lc_fid);
+ struct obd_capa *ocapa, *old = NULL;
+ struct list_head *list = &capa_list[CAPA_SITE_SERVER];
+
+ ocapa = alloc_capa(CAPA_SITE_SERVER);
+ if (IS_ERR(ocapa))
+ return NULL;
+
+ spin_lock(&capa_lock);
+ old = find_capa(capa, head, 0);
+ if (!old) {
+ ocapa->c_capa = *capa;
+ set_capa_expiry(ocapa);
+ hlist_add_head(&ocapa->u.tgt.c_hash, head);
+ list_add_tail(&ocapa->c_list, list);
+ capa_get(ocapa);
+ capa_count[CAPA_SITE_SERVER]++;
+ if (capa_count[CAPA_SITE_SERVER] > CAPA_HASH_SIZE)
+ capa_delete_lru(list);
+ spin_unlock(&capa_lock);
+ return ocapa;
+ }
+ capa_get(old);
+ spin_unlock(&capa_lock);
+ capa_put(ocapa);
+ return old;
+}
+EXPORT_SYMBOL(capa_add);
+
+struct obd_capa *capa_lookup(struct hlist_head *hash, struct lustre_capa *capa,
+ int alive)
+{
+ struct obd_capa *ocapa;
+
+ spin_lock(&capa_lock);
+ ocapa = find_capa(capa, hash + capa_hashfn(&capa->lc_fid), alive);
+ if (ocapa) {
+ list_move_tail(&ocapa->c_list,
+ &capa_list[CAPA_SITE_SERVER]);
+ capa_get(ocapa);
+ }
+ spin_unlock(&capa_lock);
+
+ return ocapa;
+}
+EXPORT_SYMBOL(capa_lookup);
+
+static inline int ll_crypto_hmac(struct crypto_hash *tfm,
+ u8 *key, unsigned int *keylen,
+ struct scatterlist *sg,
+ unsigned int size, u8 *result)
+{
+ struct hash_desc desc;
+ int rv;
+ desc.tfm = tfm;
+ desc.flags = 0;
+ rv = crypto_hash_setkey(desc.tfm, key, *keylen);
+ if (rv) {
+ CERROR("failed to hash setkey: %d\n", rv);
+ return rv;
+ }
+ return crypto_hash_digest(&desc, sg, size, result);
+}
+
+int capa_hmac(__u8 *hmac, struct lustre_capa *capa, __u8 *key)
+{
+ struct crypto_hash *tfm;
+ struct capa_hmac_alg *alg;
+ int keylen;
+ struct scatterlist sl;
+
+ if (capa_alg(capa) != CAPA_HMAC_ALG_SHA1) {
+ CERROR("unknown capability hmac algorithm!\n");
+ return -EFAULT;
+ }
+
+ alg = &capa_hmac_algs[capa_alg(capa)];
+
+ tfm = crypto_alloc_hash(alg->ha_name, 0, 0);
+ if (IS_ERR(tfm)) {
+ CERROR("crypto_alloc_tfm failed, check whether your kernel has crypto support!\n");
+ return PTR_ERR(tfm);
+ }
+ keylen = alg->ha_keylen;
+
+ sg_init_table(&sl, 1);
+ sg_set_page(&sl, virt_to_page(capa),
+ offsetof(struct lustre_capa, lc_hmac),
+ (unsigned long)(capa) % PAGE_CACHE_SIZE);
+
+ ll_crypto_hmac(tfm, key, &keylen, &sl, sl.length, hmac);
+ crypto_free_hash(tfm);
+
+ return 0;
+}
+EXPORT_SYMBOL(capa_hmac);
+
+int capa_encrypt_id(__u32 *d, __u32 *s, __u8 *key, int keylen)
+{
+ struct crypto_blkcipher *tfm;
+ struct scatterlist sd;
+ struct scatterlist ss;
+ struct blkcipher_desc desc;
+ unsigned int min;
+ int rc;
+ char alg[CRYPTO_MAX_ALG_NAME+1] = "aes";
+
+ /* passing "aes" in a variable instead of a constant string keeps gcc
+ * 4.3.2 happy */
+ tfm = crypto_alloc_blkcipher(alg, 0, 0);
+ if (IS_ERR(tfm)) {
+ CERROR("failed to load transform for aes\n");
+ return PTR_ERR(tfm);
+ }
+
+ min = ll_crypto_tfm_alg_min_keysize(tfm);
+ if (keylen < min) {
+ CERROR("keylen at least %d bits for aes\n", min * 8);
+ rc = -EINVAL;
+ goto out;
+ }
+
+ rc = crypto_blkcipher_setkey(tfm, key, min);
+ if (rc) {
+ CERROR("failed to setting key for aes\n");
+ goto out;
+ }
+
+ sg_init_table(&sd, 1);
+ sg_set_page(&sd, virt_to_page(d), 16,
+ (unsigned long)(d) % PAGE_CACHE_SIZE);
+
+ sg_init_table(&ss, 1);
+ sg_set_page(&ss, virt_to_page(s), 16,
+ (unsigned long)(s) % PAGE_CACHE_SIZE);
+ desc.tfm = tfm;
+ desc.info = NULL;
+ desc.flags = 0;
+ rc = crypto_blkcipher_encrypt(&desc, &sd, &ss, 16);
+ if (rc) {
+ CERROR("failed to encrypt for aes\n");
+ goto out;
+ }
+
+out:
+ crypto_free_blkcipher(tfm);
+ return rc;
+}
+EXPORT_SYMBOL(capa_encrypt_id);
+
+int capa_decrypt_id(__u32 *d, __u32 *s, __u8 *key, int keylen)
+{
+ struct crypto_blkcipher *tfm;
+ struct scatterlist sd;
+ struct scatterlist ss;
+ struct blkcipher_desc desc;
+ unsigned int min;
+ int rc;
+ char alg[CRYPTO_MAX_ALG_NAME+1] = "aes";
+
+ /* passing "aes" in a variable instead of a constant string keeps gcc
+ * 4.3.2 happy */
+ tfm = crypto_alloc_blkcipher(alg, 0, 0);
+ if (IS_ERR(tfm)) {
+ CERROR("failed to load transform for aes\n");
+ return PTR_ERR(tfm);
+ }
+
+ min = ll_crypto_tfm_alg_min_keysize(tfm);
+ if (keylen < min) {
+ CERROR("keylen at least %d bits for aes\n", min * 8);
+ rc = -EINVAL;
+ goto out;
+ }
+
+ rc = crypto_blkcipher_setkey(tfm, key, min);
+ if (rc) {
+ CERROR("failed to setting key for aes\n");
+ goto out;
+ }
+
+ sg_init_table(&sd, 1);
+ sg_set_page(&sd, virt_to_page(d), 16,
+ (unsigned long)(d) % PAGE_CACHE_SIZE);
+
+ sg_init_table(&ss, 1);
+ sg_set_page(&ss, virt_to_page(s), 16,
+ (unsigned long)(s) % PAGE_CACHE_SIZE);
+
+ desc.tfm = tfm;
+ desc.info = NULL;
+ desc.flags = 0;
+ rc = crypto_blkcipher_decrypt(&desc, &sd, &ss, 16);
+ if (rc) {
+ CERROR("failed to decrypt for aes\n");
+ goto out;
+ }
+
+out:
+ crypto_free_blkcipher(tfm);
+ return rc;
+}
+EXPORT_SYMBOL(capa_decrypt_id);
+
+void capa_cpy(void *capa, struct obd_capa *ocapa)
+{
+ spin_lock(&ocapa->c_lock);
+ *(struct lustre_capa *)capa = ocapa->c_capa;
+ spin_unlock(&ocapa->c_lock);
+}
+EXPORT_SYMBOL(capa_cpy);
+
+void _debug_capa(struct lustre_capa *c,
+ struct libcfs_debug_msg_data *msgdata,
+ const char *fmt, ...)
+{
+ va_list args;
+ va_start(args, fmt);
+ libcfs_debug_vmsg2(msgdata, fmt, args,
+ " capability@%p fid " DFID " opc %#llx uid %llu gid %llu flags %u alg %d keyid %u timeout %u expiry %u\n",
+ c, PFID(capa_fid(c)), capa_opc(c),
+ capa_uid(c), capa_gid(c), capa_flags(c),
+ capa_alg(c), capa_keyid(c), capa_timeout(c),
+ capa_expiry(c));
+ va_end(args);
+}
+EXPORT_SYMBOL(_debug_capa);
diff --git a/drivers/staging/lustre/lustre/obdclass/cl_internal.h b/drivers/staging/lustre/lustre/obdclass/cl_internal.h
new file mode 100644
index 000000000..7eb0ad7b3
--- /dev/null
+++ b/drivers/staging/lustre/lustre/obdclass/cl_internal.h
@@ -0,0 +1,121 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2012, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ *
+ * Internal cl interfaces.
+ *
+ * Author: Nikita Danilov <nikita.danilov@sun.com>
+ */
+#ifndef _CL_INTERNAL_H
+#define _CL_INTERNAL_H
+
+#define CLT_PVEC_SIZE (14)
+
+/**
+ * Possible levels of the nesting. Currently this is 2: there are "top"
+ * entities (files, extent locks), and "sub" entities (stripes and stripe
+ * locks). This is used only for debugging counters right now.
+ */
+enum clt_nesting_level {
+ CNL_TOP,
+ CNL_SUB,
+ CNL_NR
+};
+
+/**
+ * Counters used to check correctness of cl_lock interface usage.
+ */
+struct cl_thread_counters {
+ /**
+ * Number of outstanding calls to cl_lock_mutex_get() made by the
+ * current thread. For debugging.
+ */
+ int ctc_nr_locks_locked;
+ /** List of locked locks. */
+ struct lu_ref ctc_locks_locked;
+ /** Number of outstanding holds on locks. */
+ int ctc_nr_held;
+ /** Number of outstanding uses on locks. */
+ int ctc_nr_used;
+ /** Number of held extent locks. */
+ int ctc_nr_locks_acquired;
+};
+
+/**
+ * Thread local state internal for generic cl-code.
+ */
+struct cl_thread_info {
+ /*
+ * Common fields.
+ */
+ struct cl_io clt_io;
+ struct cl_2queue clt_queue;
+
+ /*
+ * Fields used by cl_lock.c
+ */
+ struct cl_lock_descr clt_descr;
+ struct cl_page_list clt_list;
+ /**
+ * Counters for every level of lock nesting.
+ */
+ struct cl_thread_counters clt_counters[CNL_NR];
+ /** @} debugging */
+
+ /*
+ * Fields used by cl_page.c
+ */
+ struct cl_page *clt_pvec[CLT_PVEC_SIZE];
+
+ /*
+ * Fields used by cl_io.c
+ */
+ /**
+ * Pointer to the topmost ongoing IO in this thread.
+ */
+ struct cl_io *clt_current_io;
+ /**
+ * Used for submitting a sync io.
+ */
+ struct cl_sync_io clt_anchor;
+ /**
+ * Fields used by cl_lock_discard_pages().
+ */
+ pgoff_t clt_next_index;
+ pgoff_t clt_fn_index; /* first non-overlapped index */
+};
+
+struct cl_thread_info *cl_env_info(const struct lu_env *env);
+
+#endif /* _CL_INTERNAL_H */
diff --git a/drivers/staging/lustre/lustre/obdclass/cl_io.c b/drivers/staging/lustre/lustre/obdclass/cl_io.c
new file mode 100644
index 000000000..3141b6043
--- /dev/null
+++ b/drivers/staging/lustre/lustre/obdclass/cl_io.c
@@ -0,0 +1,1669 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2011, 2012, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ *
+ * Client IO.
+ *
+ * Author: Nikita Danilov <nikita.danilov@sun.com>
+ */
+
+#define DEBUG_SUBSYSTEM S_CLASS
+
+#include "../include/obd_class.h"
+#include "../include/obd_support.h"
+#include "../include/lustre_fid.h"
+#include <linux/list.h>
+#include "../include/cl_object.h"
+#include "cl_internal.h"
+
+/*****************************************************************************
+ *
+ * cl_io interface.
+ *
+ */
+
+#define cl_io_for_each(slice, io) \
+ list_for_each_entry((slice), &io->ci_layers, cis_linkage)
+#define cl_io_for_each_reverse(slice, io) \
+ list_for_each_entry_reverse((slice), &io->ci_layers, cis_linkage)
+
+static inline int cl_io_type_is_valid(enum cl_io_type type)
+{
+ return CIT_READ <= type && type < CIT_OP_NR;
+}
+
+static inline int cl_io_is_loopable(const struct cl_io *io)
+{
+ return cl_io_type_is_valid(io->ci_type) && io->ci_type != CIT_MISC;
+}
+
+/**
+ * Returns true iff there is an IO ongoing in the given environment.
+ */
+int cl_io_is_going(const struct lu_env *env)
+{
+ return cl_env_info(env)->clt_current_io != NULL;
+}
+EXPORT_SYMBOL(cl_io_is_going);
+
+/**
+ * cl_io invariant that holds at all times when exported cl_io_*() functions
+ * are entered and left.
+ */
+static int cl_io_invariant(const struct cl_io *io)
+{
+ struct cl_io *up;
+
+ up = io->ci_parent;
+ return
+ /*
+ * io can own pages only when it is ongoing. Sub-io might
+ * still be in CIS_LOCKED state when top-io is in
+ * CIS_IO_GOING.
+ */
+ ergo(io->ci_owned_nr > 0, io->ci_state == CIS_IO_GOING ||
+ (io->ci_state == CIS_LOCKED && up != NULL));
+}
+
+/**
+ * Finalize \a io, by calling cl_io_operations::cio_fini() bottom-to-top.
+ */
+void cl_io_fini(const struct lu_env *env, struct cl_io *io)
+{
+ struct cl_io_slice *slice;
+ struct cl_thread_info *info;
+
+ LINVRNT(cl_io_type_is_valid(io->ci_type));
+ LINVRNT(cl_io_invariant(io));
+
+ while (!list_empty(&io->ci_layers)) {
+ slice = container_of(io->ci_layers.prev, struct cl_io_slice,
+ cis_linkage);
+ list_del_init(&slice->cis_linkage);
+ if (slice->cis_iop->op[io->ci_type].cio_fini != NULL)
+ slice->cis_iop->op[io->ci_type].cio_fini(env, slice);
+ /*
+ * Invalidate slice to catch use after free. This assumes that
+ * slices are allocated within session and can be touched
+ * after ->cio_fini() returns.
+ */
+ slice->cis_io = NULL;
+ }
+ io->ci_state = CIS_FINI;
+ info = cl_env_info(env);
+ if (info->clt_current_io == io)
+ info->clt_current_io = NULL;
+
+ /* sanity check for layout change */
+ switch (io->ci_type) {
+ case CIT_READ:
+ case CIT_WRITE:
+ break;
+ case CIT_FAULT:
+ case CIT_FSYNC:
+ LASSERT(!io->ci_need_restart);
+ break;
+ case CIT_SETATTR:
+ case CIT_MISC:
+ /* Check ignore layout change conf */
+ LASSERT(ergo(io->ci_ignore_layout || !io->ci_verify_layout,
+ !io->ci_need_restart));
+ break;
+ default:
+ LBUG();
+ }
+}
+EXPORT_SYMBOL(cl_io_fini);
+
+static int cl_io_init0(const struct lu_env *env, struct cl_io *io,
+ enum cl_io_type iot, struct cl_object *obj)
+{
+ struct cl_object *scan;
+ int result;
+
+ LINVRNT(io->ci_state == CIS_ZERO || io->ci_state == CIS_FINI);
+ LINVRNT(cl_io_type_is_valid(iot));
+ LINVRNT(cl_io_invariant(io));
+
+ io->ci_type = iot;
+ INIT_LIST_HEAD(&io->ci_lockset.cls_todo);
+ INIT_LIST_HEAD(&io->ci_lockset.cls_curr);
+ INIT_LIST_HEAD(&io->ci_lockset.cls_done);
+ INIT_LIST_HEAD(&io->ci_layers);
+
+ result = 0;
+ cl_object_for_each(scan, obj) {
+ if (scan->co_ops->coo_io_init != NULL) {
+ result = scan->co_ops->coo_io_init(env, scan, io);
+ if (result != 0)
+ break;
+ }
+ }
+ if (result == 0)
+ io->ci_state = CIS_INIT;
+ return result;
+}
+
+/**
+ * Initialize sub-io, by calling cl_io_operations::cio_init() top-to-bottom.
+ *
+ * \pre obj != cl_object_top(obj)
+ */
+int cl_io_sub_init(const struct lu_env *env, struct cl_io *io,
+ enum cl_io_type iot, struct cl_object *obj)
+{
+ struct cl_thread_info *info = cl_env_info(env);
+
+ LASSERT(obj != cl_object_top(obj));
+ if (info->clt_current_io == NULL)
+ info->clt_current_io = io;
+ return cl_io_init0(env, io, iot, obj);
+}
+EXPORT_SYMBOL(cl_io_sub_init);
+
+/**
+ * Initialize \a io, by calling cl_io_operations::cio_init() top-to-bottom.
+ *
+ * Caller has to call cl_io_fini() after a call to cl_io_init(), no matter
+ * what the latter returned.
+ *
+ * \pre obj == cl_object_top(obj)
+ * \pre cl_io_type_is_valid(iot)
+ * \post cl_io_type_is_valid(io->ci_type) && io->ci_type == iot
+ */
+int cl_io_init(const struct lu_env *env, struct cl_io *io,
+ enum cl_io_type iot, struct cl_object *obj)
+{
+ struct cl_thread_info *info = cl_env_info(env);
+
+ LASSERT(obj == cl_object_top(obj));
+ LASSERT(info->clt_current_io == NULL);
+
+ info->clt_current_io = io;
+ return cl_io_init0(env, io, iot, obj);
+}
+EXPORT_SYMBOL(cl_io_init);
+
+/**
+ * Initialize read or write io.
+ *
+ * \pre iot == CIT_READ || iot == CIT_WRITE
+ */
+int cl_io_rw_init(const struct lu_env *env, struct cl_io *io,
+ enum cl_io_type iot, loff_t pos, size_t count)
+{
+ LINVRNT(iot == CIT_READ || iot == CIT_WRITE);
+ LINVRNT(io->ci_obj != NULL);
+
+ LU_OBJECT_HEADER(D_VFSTRACE, env, &io->ci_obj->co_lu,
+ "io range: %u [%llu, %llu) %u %u\n",
+ iot, (__u64)pos, (__u64)pos + count,
+ io->u.ci_rw.crw_nonblock, io->u.ci_wr.wr_append);
+ io->u.ci_rw.crw_pos = pos;
+ io->u.ci_rw.crw_count = count;
+ return cl_io_init(env, io, iot, io->ci_obj);
+}
+EXPORT_SYMBOL(cl_io_rw_init);
+
+static inline const struct lu_fid *
+cl_lock_descr_fid(const struct cl_lock_descr *descr)
+{
+ return lu_object_fid(&descr->cld_obj->co_lu);
+}
+
+static int cl_lock_descr_sort(const struct cl_lock_descr *d0,
+ const struct cl_lock_descr *d1)
+{
+ return lu_fid_cmp(cl_lock_descr_fid(d0), cl_lock_descr_fid(d1)) ?:
+ __diff_normalize(d0->cld_start, d1->cld_start);
+}
+
+static int cl_lock_descr_cmp(const struct cl_lock_descr *d0,
+ const struct cl_lock_descr *d1)
+{
+ int ret;
+
+ ret = lu_fid_cmp(cl_lock_descr_fid(d0), cl_lock_descr_fid(d1));
+ if (ret)
+ return ret;
+ if (d0->cld_end < d1->cld_start)
+ return -1;
+ if (d0->cld_start > d0->cld_end)
+ return 1;
+ return 0;
+}
+
+static void cl_lock_descr_merge(struct cl_lock_descr *d0,
+ const struct cl_lock_descr *d1)
+{
+ d0->cld_start = min(d0->cld_start, d1->cld_start);
+ d0->cld_end = max(d0->cld_end, d1->cld_end);
+
+ if (d1->cld_mode == CLM_WRITE && d0->cld_mode != CLM_WRITE)
+ d0->cld_mode = CLM_WRITE;
+
+ if (d1->cld_mode == CLM_GROUP && d0->cld_mode != CLM_GROUP)
+ d0->cld_mode = CLM_GROUP;
+}
+
+/*
+ * Sort locks in lexicographical order of their (fid, start-offset) pairs.
+ */
+static void cl_io_locks_sort(struct cl_io *io)
+{
+ int done = 0;
+
+ /* hidden treasure: bubble sort for now. */
+ do {
+ struct cl_io_lock_link *curr;
+ struct cl_io_lock_link *prev;
+ struct cl_io_lock_link *temp;
+
+ done = 1;
+ prev = NULL;
+
+ list_for_each_entry_safe(curr, temp,
+ &io->ci_lockset.cls_todo,
+ cill_linkage) {
+ if (prev != NULL) {
+ switch (cl_lock_descr_sort(&prev->cill_descr,
+ &curr->cill_descr)) {
+ case 0:
+ /*
+ * IMPOSSIBLE: Identical locks are
+ * already removed at
+ * this point.
+ */
+ default:
+ LBUG();
+ case +1:
+ list_move_tail(&curr->cill_linkage,
+ &prev->cill_linkage);
+ done = 0;
+ continue; /* don't change prev: it's
+ * still "previous" */
+ case -1: /* already in order */
+ break;
+ }
+ }
+ prev = curr;
+ }
+ } while (!done);
+}
+
+/**
+ * Check whether \a queue contains locks matching \a need.
+ *
+ * \retval +ve there is a matching lock in the \a queue
+ * \retval 0 there are no matching locks in the \a queue
+ */
+int cl_queue_match(const struct list_head *queue,
+ const struct cl_lock_descr *need)
+{
+ struct cl_io_lock_link *scan;
+
+ list_for_each_entry(scan, queue, cill_linkage) {
+ if (cl_lock_descr_match(&scan->cill_descr, need))
+ return +1;
+ }
+ return 0;
+}
+EXPORT_SYMBOL(cl_queue_match);
+
+static int cl_queue_merge(const struct list_head *queue,
+ const struct cl_lock_descr *need)
+{
+ struct cl_io_lock_link *scan;
+
+ list_for_each_entry(scan, queue, cill_linkage) {
+ if (cl_lock_descr_cmp(&scan->cill_descr, need))
+ continue;
+ cl_lock_descr_merge(&scan->cill_descr, need);
+ CDEBUG(D_VFSTRACE, "lock: %d: [%lu, %lu]\n",
+ scan->cill_descr.cld_mode, scan->cill_descr.cld_start,
+ scan->cill_descr.cld_end);
+ return +1;
+ }
+ return 0;
+
+}
+
+static int cl_lockset_match(const struct cl_lockset *set,
+ const struct cl_lock_descr *need)
+{
+ return cl_queue_match(&set->cls_curr, need) ||
+ cl_queue_match(&set->cls_done, need);
+}
+
+static int cl_lockset_merge(const struct cl_lockset *set,
+ const struct cl_lock_descr *need)
+{
+ return cl_queue_merge(&set->cls_todo, need) ||
+ cl_lockset_match(set, need);
+}
+
+static int cl_lockset_lock_one(const struct lu_env *env,
+ struct cl_io *io, struct cl_lockset *set,
+ struct cl_io_lock_link *link)
+{
+ struct cl_lock *lock;
+ int result;
+
+ lock = cl_lock_request(env, io, &link->cill_descr, "io", io);
+
+ if (!IS_ERR(lock)) {
+ link->cill_lock = lock;
+ list_move(&link->cill_linkage, &set->cls_curr);
+ if (!(link->cill_descr.cld_enq_flags & CEF_ASYNC)) {
+ result = cl_wait(env, lock);
+ if (result == 0)
+ list_move(&link->cill_linkage,
+ &set->cls_done);
+ } else
+ result = 0;
+ } else
+ result = PTR_ERR(lock);
+ return result;
+}
+
+static void cl_lock_link_fini(const struct lu_env *env, struct cl_io *io,
+ struct cl_io_lock_link *link)
+{
+ struct cl_lock *lock = link->cill_lock;
+
+ list_del_init(&link->cill_linkage);
+ if (lock != NULL) {
+ cl_lock_release(env, lock, "io", io);
+ link->cill_lock = NULL;
+ }
+ if (link->cill_fini != NULL)
+ link->cill_fini(env, link);
+}
+
+static int cl_lockset_lock(const struct lu_env *env, struct cl_io *io,
+ struct cl_lockset *set)
+{
+ struct cl_io_lock_link *link;
+ struct cl_io_lock_link *temp;
+ struct cl_lock *lock;
+ int result;
+
+ result = 0;
+ list_for_each_entry_safe(link, temp, &set->cls_todo, cill_linkage) {
+ if (!cl_lockset_match(set, &link->cill_descr)) {
+ /* XXX some locking to guarantee that locks aren't
+ * expanded in between. */
+ result = cl_lockset_lock_one(env, io, set, link);
+ if (result != 0)
+ break;
+ } else
+ cl_lock_link_fini(env, io, link);
+ }
+ if (result == 0) {
+ list_for_each_entry_safe(link, temp,
+ &set->cls_curr, cill_linkage) {
+ lock = link->cill_lock;
+ result = cl_wait(env, lock);
+ if (result == 0)
+ list_move(&link->cill_linkage,
+ &set->cls_done);
+ else
+ break;
+ }
+ }
+ return result;
+}
+
+/**
+ * Takes locks necessary for the current iteration of io.
+ *
+ * Calls cl_io_operations::cio_lock() top-to-bottom to collect locks required
+ * by layers for the current iteration. Then sort locks (to avoid dead-locks),
+ * and acquire them.
+ */
+int cl_io_lock(const struct lu_env *env, struct cl_io *io)
+{
+ const struct cl_io_slice *scan;
+ int result = 0;
+
+ LINVRNT(cl_io_is_loopable(io));
+ LINVRNT(io->ci_state == CIS_IT_STARTED);
+ LINVRNT(cl_io_invariant(io));
+
+ cl_io_for_each(scan, io) {
+ if (scan->cis_iop->op[io->ci_type].cio_lock == NULL)
+ continue;
+ result = scan->cis_iop->op[io->ci_type].cio_lock(env, scan);
+ if (result != 0)
+ break;
+ }
+ if (result == 0) {
+ cl_io_locks_sort(io);
+ result = cl_lockset_lock(env, io, &io->ci_lockset);
+ }
+ if (result != 0)
+ cl_io_unlock(env, io);
+ else
+ io->ci_state = CIS_LOCKED;
+ return result;
+}
+EXPORT_SYMBOL(cl_io_lock);
+
+/**
+ * Release locks takes by io.
+ */
+void cl_io_unlock(const struct lu_env *env, struct cl_io *io)
+{
+ struct cl_lockset *set;
+ struct cl_io_lock_link *link;
+ struct cl_io_lock_link *temp;
+ const struct cl_io_slice *scan;
+
+ LASSERT(cl_io_is_loopable(io));
+ LASSERT(CIS_IT_STARTED <= io->ci_state && io->ci_state < CIS_UNLOCKED);
+ LINVRNT(cl_io_invariant(io));
+
+ set = &io->ci_lockset;
+
+ list_for_each_entry_safe(link, temp, &set->cls_todo, cill_linkage)
+ cl_lock_link_fini(env, io, link);
+
+ list_for_each_entry_safe(link, temp, &set->cls_curr, cill_linkage)
+ cl_lock_link_fini(env, io, link);
+
+ list_for_each_entry_safe(link, temp, &set->cls_done, cill_linkage) {
+ cl_unuse(env, link->cill_lock);
+ cl_lock_link_fini(env, io, link);
+ }
+ cl_io_for_each_reverse(scan, io) {
+ if (scan->cis_iop->op[io->ci_type].cio_unlock != NULL)
+ scan->cis_iop->op[io->ci_type].cio_unlock(env, scan);
+ }
+ io->ci_state = CIS_UNLOCKED;
+ LASSERT(!cl_env_info(env)->clt_counters[CNL_TOP].ctc_nr_locks_acquired);
+}
+EXPORT_SYMBOL(cl_io_unlock);
+
+/**
+ * Prepares next iteration of io.
+ *
+ * Calls cl_io_operations::cio_iter_init() top-to-bottom. This exists to give
+ * layers a chance to modify io parameters, e.g., so that lov can restrict io
+ * to a single stripe.
+ */
+int cl_io_iter_init(const struct lu_env *env, struct cl_io *io)
+{
+ const struct cl_io_slice *scan;
+ int result;
+
+ LINVRNT(cl_io_is_loopable(io));
+ LINVRNT(io->ci_state == CIS_INIT || io->ci_state == CIS_IT_ENDED);
+ LINVRNT(cl_io_invariant(io));
+
+ result = 0;
+ cl_io_for_each(scan, io) {
+ if (scan->cis_iop->op[io->ci_type].cio_iter_init == NULL)
+ continue;
+ result = scan->cis_iop->op[io->ci_type].cio_iter_init(env,
+ scan);
+ if (result != 0)
+ break;
+ }
+ if (result == 0)
+ io->ci_state = CIS_IT_STARTED;
+ return result;
+}
+EXPORT_SYMBOL(cl_io_iter_init);
+
+/**
+ * Finalizes io iteration.
+ *
+ * Calls cl_io_operations::cio_iter_fini() bottom-to-top.
+ */
+void cl_io_iter_fini(const struct lu_env *env, struct cl_io *io)
+{
+ const struct cl_io_slice *scan;
+
+ LINVRNT(cl_io_is_loopable(io));
+ LINVRNT(io->ci_state == CIS_UNLOCKED);
+ LINVRNT(cl_io_invariant(io));
+
+ cl_io_for_each_reverse(scan, io) {
+ if (scan->cis_iop->op[io->ci_type].cio_iter_fini != NULL)
+ scan->cis_iop->op[io->ci_type].cio_iter_fini(env, scan);
+ }
+ io->ci_state = CIS_IT_ENDED;
+}
+EXPORT_SYMBOL(cl_io_iter_fini);
+
+/**
+ * Records that read or write io progressed \a nob bytes forward.
+ */
+void cl_io_rw_advance(const struct lu_env *env, struct cl_io *io, size_t nob)
+{
+ const struct cl_io_slice *scan;
+
+ LINVRNT(io->ci_type == CIT_READ || io->ci_type == CIT_WRITE ||
+ nob == 0);
+ LINVRNT(cl_io_is_loopable(io));
+ LINVRNT(cl_io_invariant(io));
+
+ io->u.ci_rw.crw_pos += nob;
+ io->u.ci_rw.crw_count -= nob;
+
+ /* layers have to be notified. */
+ cl_io_for_each_reverse(scan, io) {
+ if (scan->cis_iop->op[io->ci_type].cio_advance != NULL)
+ scan->cis_iop->op[io->ci_type].cio_advance(env, scan,
+ nob);
+ }
+}
+EXPORT_SYMBOL(cl_io_rw_advance);
+
+/**
+ * Adds a lock to a lockset.
+ */
+int cl_io_lock_add(const struct lu_env *env, struct cl_io *io,
+ struct cl_io_lock_link *link)
+{
+ int result;
+
+ if (cl_lockset_merge(&io->ci_lockset, &link->cill_descr))
+ result = +1;
+ else {
+ list_add(&link->cill_linkage, &io->ci_lockset.cls_todo);
+ result = 0;
+ }
+ return result;
+}
+EXPORT_SYMBOL(cl_io_lock_add);
+
+static void cl_free_io_lock_link(const struct lu_env *env,
+ struct cl_io_lock_link *link)
+{
+ OBD_FREE_PTR(link);
+}
+
+/**
+ * Allocates new lock link, and uses it to add a lock to a lockset.
+ */
+int cl_io_lock_alloc_add(const struct lu_env *env, struct cl_io *io,
+ struct cl_lock_descr *descr)
+{
+ struct cl_io_lock_link *link;
+ int result;
+
+ OBD_ALLOC_PTR(link);
+ if (link != NULL) {
+ link->cill_descr = *descr;
+ link->cill_fini = cl_free_io_lock_link;
+ result = cl_io_lock_add(env, io, link);
+ if (result) /* lock match */
+ link->cill_fini(env, link);
+ } else
+ result = -ENOMEM;
+
+ return result;
+}
+EXPORT_SYMBOL(cl_io_lock_alloc_add);
+
+/**
+ * Starts io by calling cl_io_operations::cio_start() top-to-bottom.
+ */
+int cl_io_start(const struct lu_env *env, struct cl_io *io)
+{
+ const struct cl_io_slice *scan;
+ int result = 0;
+
+ LINVRNT(cl_io_is_loopable(io));
+ LINVRNT(io->ci_state == CIS_LOCKED);
+ LINVRNT(cl_io_invariant(io));
+
+ io->ci_state = CIS_IO_GOING;
+ cl_io_for_each(scan, io) {
+ if (scan->cis_iop->op[io->ci_type].cio_start == NULL)
+ continue;
+ result = scan->cis_iop->op[io->ci_type].cio_start(env, scan);
+ if (result != 0)
+ break;
+ }
+ if (result >= 0)
+ result = 0;
+ return result;
+}
+EXPORT_SYMBOL(cl_io_start);
+
+/**
+ * Wait until current io iteration is finished by calling
+ * cl_io_operations::cio_end() bottom-to-top.
+ */
+void cl_io_end(const struct lu_env *env, struct cl_io *io)
+{
+ const struct cl_io_slice *scan;
+
+ LINVRNT(cl_io_is_loopable(io));
+ LINVRNT(io->ci_state == CIS_IO_GOING);
+ LINVRNT(cl_io_invariant(io));
+
+ cl_io_for_each_reverse(scan, io) {
+ if (scan->cis_iop->op[io->ci_type].cio_end != NULL)
+ scan->cis_iop->op[io->ci_type].cio_end(env, scan);
+ /* TODO: error handling. */
+ }
+ io->ci_state = CIS_IO_FINISHED;
+}
+EXPORT_SYMBOL(cl_io_end);
+
+static const struct cl_page_slice *
+cl_io_slice_page(const struct cl_io_slice *ios, struct cl_page *page)
+{
+ const struct cl_page_slice *slice;
+
+ slice = cl_page_at(page, ios->cis_obj->co_lu.lo_dev->ld_type);
+ LINVRNT(slice != NULL);
+ return slice;
+}
+
+/**
+ * True iff \a page is within \a io range.
+ */
+static int cl_page_in_io(const struct cl_page *page, const struct cl_io *io)
+{
+ int result = 1;
+ loff_t start;
+ loff_t end;
+ pgoff_t idx;
+
+ idx = page->cp_index;
+ switch (io->ci_type) {
+ case CIT_READ:
+ case CIT_WRITE:
+ /*
+ * check that [start, end) and [pos, pos + count) extents
+ * overlap.
+ */
+ if (!cl_io_is_append(io)) {
+ const struct cl_io_rw_common *crw = &(io->u.ci_rw);
+ start = cl_offset(page->cp_obj, idx);
+ end = cl_offset(page->cp_obj, idx + 1);
+ result = crw->crw_pos < end &&
+ start < crw->crw_pos + crw->crw_count;
+ }
+ break;
+ case CIT_FAULT:
+ result = io->u.ci_fault.ft_index == idx;
+ break;
+ default:
+ LBUG();
+ }
+ return result;
+}
+
+/**
+ * Called by read io, when page has to be read from the server.
+ *
+ * \see cl_io_operations::cio_read_page()
+ */
+int cl_io_read_page(const struct lu_env *env, struct cl_io *io,
+ struct cl_page *page)
+{
+ const struct cl_io_slice *scan;
+ struct cl_2queue *queue;
+ int result = 0;
+
+ LINVRNT(io->ci_type == CIT_READ || io->ci_type == CIT_FAULT);
+ LINVRNT(cl_page_is_owned(page, io));
+ LINVRNT(io->ci_state == CIS_IO_GOING || io->ci_state == CIS_LOCKED);
+ LINVRNT(cl_page_in_io(page, io));
+ LINVRNT(cl_io_invariant(io));
+
+ queue = &io->ci_queue;
+
+ cl_2queue_init(queue);
+ /*
+ * ->cio_read_page() methods called in the loop below are supposed to
+ * never block waiting for network (the only subtle point is the
+ * creation of new pages for read-ahead that might result in cache
+ * shrinking, but currently only clean pages are shrunk and this
+ * requires no network io).
+ *
+ * Should this ever starts blocking, retry loop would be needed for
+ * "parallel io" (see CLO_REPEAT loops in cl_lock.c).
+ */
+ cl_io_for_each(scan, io) {
+ if (scan->cis_iop->cio_read_page != NULL) {
+ const struct cl_page_slice *slice;
+
+ slice = cl_io_slice_page(scan, page);
+ LINVRNT(slice != NULL);
+ result = scan->cis_iop->cio_read_page(env, scan, slice);
+ if (result != 0)
+ break;
+ }
+ }
+ if (result == 0)
+ result = cl_io_submit_rw(env, io, CRT_READ, queue);
+ /*
+ * Unlock unsent pages in case of error.
+ */
+ cl_page_list_disown(env, io, &queue->c2_qin);
+ cl_2queue_fini(env, queue);
+ return result;
+}
+EXPORT_SYMBOL(cl_io_read_page);
+
+/**
+ * Called by write io to prepare page to receive data from user buffer.
+ *
+ * \see cl_io_operations::cio_prepare_write()
+ */
+int cl_io_prepare_write(const struct lu_env *env, struct cl_io *io,
+ struct cl_page *page, unsigned from, unsigned to)
+{
+ const struct cl_io_slice *scan;
+ int result = 0;
+
+ LINVRNT(io->ci_type == CIT_WRITE);
+ LINVRNT(cl_page_is_owned(page, io));
+ LINVRNT(io->ci_state == CIS_IO_GOING || io->ci_state == CIS_LOCKED);
+ LINVRNT(cl_io_invariant(io));
+ LASSERT(cl_page_in_io(page, io));
+
+ cl_io_for_each_reverse(scan, io) {
+ if (scan->cis_iop->cio_prepare_write != NULL) {
+ const struct cl_page_slice *slice;
+
+ slice = cl_io_slice_page(scan, page);
+ result = scan->cis_iop->cio_prepare_write(env, scan,
+ slice,
+ from, to);
+ if (result != 0)
+ break;
+ }
+ }
+ return result;
+}
+EXPORT_SYMBOL(cl_io_prepare_write);
+
+/**
+ * Called by write io after user data were copied into a page.
+ *
+ * \see cl_io_operations::cio_commit_write()
+ */
+int cl_io_commit_write(const struct lu_env *env, struct cl_io *io,
+ struct cl_page *page, unsigned from, unsigned to)
+{
+ const struct cl_io_slice *scan;
+ int result = 0;
+
+ LINVRNT(io->ci_type == CIT_WRITE);
+ LINVRNT(io->ci_state == CIS_IO_GOING || io->ci_state == CIS_LOCKED);
+ LINVRNT(cl_io_invariant(io));
+ /*
+ * XXX Uh... not nice. Top level cl_io_commit_write() call (vvp->lov)
+ * already called cl_page_cache_add(), moving page into CPS_CACHED
+ * state. Better (and more general) way of dealing with such situation
+ * is needed.
+ */
+ LASSERT(cl_page_is_owned(page, io) || page->cp_parent != NULL);
+ LASSERT(cl_page_in_io(page, io));
+
+ cl_io_for_each(scan, io) {
+ if (scan->cis_iop->cio_commit_write != NULL) {
+ const struct cl_page_slice *slice;
+
+ slice = cl_io_slice_page(scan, page);
+ result = scan->cis_iop->cio_commit_write(env, scan,
+ slice,
+ from, to);
+ if (result != 0)
+ break;
+ }
+ }
+ LINVRNT(result <= 0);
+ return result;
+}
+EXPORT_SYMBOL(cl_io_commit_write);
+
+/**
+ * Submits a list of pages for immediate io.
+ *
+ * After the function gets returned, The submitted pages are moved to
+ * queue->c2_qout queue, and queue->c2_qin contain both the pages don't need
+ * to be submitted, and the pages are errant to submit.
+ *
+ * \returns 0 if at least one page was submitted, error code otherwise.
+ * \see cl_io_operations::cio_submit()
+ */
+int cl_io_submit_rw(const struct lu_env *env, struct cl_io *io,
+ enum cl_req_type crt, struct cl_2queue *queue)
+{
+ const struct cl_io_slice *scan;
+ int result = 0;
+
+ LINVRNT(crt < ARRAY_SIZE(scan->cis_iop->req_op));
+
+ cl_io_for_each(scan, io) {
+ if (scan->cis_iop->req_op[crt].cio_submit == NULL)
+ continue;
+ result = scan->cis_iop->req_op[crt].cio_submit(env, scan, crt,
+ queue);
+ if (result != 0)
+ break;
+ }
+ /*
+ * If ->cio_submit() failed, no pages were sent.
+ */
+ LASSERT(ergo(result != 0, list_empty(&queue->c2_qout.pl_pages)));
+ return result;
+}
+EXPORT_SYMBOL(cl_io_submit_rw);
+
+/**
+ * Submit a sync_io and wait for the IO to be finished, or error happens.
+ * If \a timeout is zero, it means to wait for the IO unconditionally.
+ */
+int cl_io_submit_sync(const struct lu_env *env, struct cl_io *io,
+ enum cl_req_type iot, struct cl_2queue *queue,
+ long timeout)
+{
+ struct cl_sync_io *anchor = &cl_env_info(env)->clt_anchor;
+ struct cl_page *pg;
+ int rc;
+
+ cl_page_list_for_each(pg, &queue->c2_qin) {
+ LASSERT(pg->cp_sync_io == NULL);
+ pg->cp_sync_io = anchor;
+ }
+
+ cl_sync_io_init(anchor, queue->c2_qin.pl_nr);
+ rc = cl_io_submit_rw(env, io, iot, queue);
+ if (rc == 0) {
+ /*
+ * If some pages weren't sent for any reason (e.g.,
+ * read found up-to-date pages in the cache, or write found
+ * clean pages), count them as completed to avoid infinite
+ * wait.
+ */
+ cl_page_list_for_each(pg, &queue->c2_qin) {
+ pg->cp_sync_io = NULL;
+ cl_sync_io_note(anchor, +1);
+ }
+
+ /* wait for the IO to be finished. */
+ rc = cl_sync_io_wait(env, io, &queue->c2_qout,
+ anchor, timeout);
+ } else {
+ LASSERT(list_empty(&queue->c2_qout.pl_pages));
+ cl_page_list_for_each(pg, &queue->c2_qin)
+ pg->cp_sync_io = NULL;
+ }
+ return rc;
+}
+EXPORT_SYMBOL(cl_io_submit_sync);
+
+/**
+ * Cancel an IO which has been submitted by cl_io_submit_rw.
+ */
+int cl_io_cancel(const struct lu_env *env, struct cl_io *io,
+ struct cl_page_list *queue)
+{
+ struct cl_page *page;
+ int result = 0;
+
+ CERROR("Canceling ongoing page transmission\n");
+ cl_page_list_for_each(page, queue) {
+ int rc;
+
+ LINVRNT(cl_page_in_io(page, io));
+ rc = cl_page_cancel(env, page);
+ result = result ?: rc;
+ }
+ return result;
+}
+EXPORT_SYMBOL(cl_io_cancel);
+
+/**
+ * Main io loop.
+ *
+ * Pumps io through iterations calling
+ *
+ * - cl_io_iter_init()
+ *
+ * - cl_io_lock()
+ *
+ * - cl_io_start()
+ *
+ * - cl_io_end()
+ *
+ * - cl_io_unlock()
+ *
+ * - cl_io_iter_fini()
+ *
+ * repeatedly until there is no more io to do.
+ */
+int cl_io_loop(const struct lu_env *env, struct cl_io *io)
+{
+ int result = 0;
+
+ LINVRNT(cl_io_is_loopable(io));
+
+ do {
+ size_t nob;
+
+ io->ci_continue = 0;
+ result = cl_io_iter_init(env, io);
+ if (result == 0) {
+ nob = io->ci_nob;
+ result = cl_io_lock(env, io);
+ if (result == 0) {
+ /*
+ * Notify layers that locks has been taken,
+ * and do actual i/o.
+ *
+ * - llite: kms, short read;
+ * - llite: generic_file_read();
+ */
+ result = cl_io_start(env, io);
+ /*
+ * Send any remaining pending
+ * io, etc.
+ *
+ * - llite: ll_rw_stats_tally.
+ */
+ cl_io_end(env, io);
+ cl_io_unlock(env, io);
+ cl_io_rw_advance(env, io, io->ci_nob - nob);
+ }
+ }
+ cl_io_iter_fini(env, io);
+ } while (result == 0 && io->ci_continue);
+ if (result == 0)
+ result = io->ci_result;
+ return result < 0 ? result : 0;
+}
+EXPORT_SYMBOL(cl_io_loop);
+
+/**
+ * Adds io slice to the cl_io.
+ *
+ * This is called by cl_object_operations::coo_io_init() methods to add a
+ * per-layer state to the io. New state is added at the end of
+ * cl_io::ci_layers list, that is, it is at the bottom of the stack.
+ *
+ * \see cl_lock_slice_add(), cl_req_slice_add(), cl_page_slice_add()
+ */
+void cl_io_slice_add(struct cl_io *io, struct cl_io_slice *slice,
+ struct cl_object *obj,
+ const struct cl_io_operations *ops)
+{
+ struct list_head *linkage = &slice->cis_linkage;
+
+ LASSERT((linkage->prev == NULL && linkage->next == NULL) ||
+ list_empty(linkage));
+
+ list_add_tail(linkage, &io->ci_layers);
+ slice->cis_io = io;
+ slice->cis_obj = obj;
+ slice->cis_iop = ops;
+}
+EXPORT_SYMBOL(cl_io_slice_add);
+
+
+/**
+ * Initializes page list.
+ */
+void cl_page_list_init(struct cl_page_list *plist)
+{
+ plist->pl_nr = 0;
+ INIT_LIST_HEAD(&plist->pl_pages);
+ plist->pl_owner = current;
+}
+EXPORT_SYMBOL(cl_page_list_init);
+
+/**
+ * Adds a page to a page list.
+ */
+void cl_page_list_add(struct cl_page_list *plist, struct cl_page *page)
+{
+ /* it would be better to check that page is owned by "current" io, but
+ * it is not passed here. */
+ LASSERT(page->cp_owner != NULL);
+ LINVRNT(plist->pl_owner == current);
+
+ lockdep_off();
+ mutex_lock(&page->cp_mutex);
+ lockdep_on();
+ LASSERT(list_empty(&page->cp_batch));
+ list_add_tail(&page->cp_batch, &plist->pl_pages);
+ ++plist->pl_nr;
+ lu_ref_add_at(&page->cp_reference, &page->cp_queue_ref, "queue", plist);
+ cl_page_get(page);
+}
+EXPORT_SYMBOL(cl_page_list_add);
+
+/**
+ * Removes a page from a page list.
+ */
+void cl_page_list_del(const struct lu_env *env,
+ struct cl_page_list *plist, struct cl_page *page)
+{
+ LASSERT(plist->pl_nr > 0);
+ LINVRNT(plist->pl_owner == current);
+
+ list_del_init(&page->cp_batch);
+ lockdep_off();
+ mutex_unlock(&page->cp_mutex);
+ lockdep_on();
+ --plist->pl_nr;
+ lu_ref_del_at(&page->cp_reference, &page->cp_queue_ref, "queue", plist);
+ cl_page_put(env, page);
+}
+EXPORT_SYMBOL(cl_page_list_del);
+
+/**
+ * Moves a page from one page list to another.
+ */
+void cl_page_list_move(struct cl_page_list *dst, struct cl_page_list *src,
+ struct cl_page *page)
+{
+ LASSERT(src->pl_nr > 0);
+ LINVRNT(dst->pl_owner == current);
+ LINVRNT(src->pl_owner == current);
+
+ list_move_tail(&page->cp_batch, &dst->pl_pages);
+ --src->pl_nr;
+ ++dst->pl_nr;
+ lu_ref_set_at(&page->cp_reference, &page->cp_queue_ref, "queue",
+ src, dst);
+}
+EXPORT_SYMBOL(cl_page_list_move);
+
+/**
+ * splice the cl_page_list, just as list head does
+ */
+void cl_page_list_splice(struct cl_page_list *list, struct cl_page_list *head)
+{
+ struct cl_page *page;
+ struct cl_page *tmp;
+
+ LINVRNT(list->pl_owner == current);
+ LINVRNT(head->pl_owner == current);
+
+ cl_page_list_for_each_safe(page, tmp, list)
+ cl_page_list_move(head, list, page);
+}
+EXPORT_SYMBOL(cl_page_list_splice);
+
+void cl_page_disown0(const struct lu_env *env,
+ struct cl_io *io, struct cl_page *pg);
+
+/**
+ * Disowns pages in a queue.
+ */
+void cl_page_list_disown(const struct lu_env *env,
+ struct cl_io *io, struct cl_page_list *plist)
+{
+ struct cl_page *page;
+ struct cl_page *temp;
+
+ LINVRNT(plist->pl_owner == current);
+
+ cl_page_list_for_each_safe(page, temp, plist) {
+ LASSERT(plist->pl_nr > 0);
+
+ list_del_init(&page->cp_batch);
+ lockdep_off();
+ mutex_unlock(&page->cp_mutex);
+ lockdep_on();
+ --plist->pl_nr;
+ /*
+ * cl_page_disown0 rather than usual cl_page_disown() is used,
+ * because pages are possibly in CPS_FREEING state already due
+ * to the call to cl_page_list_discard().
+ */
+ /*
+ * XXX cl_page_disown0() will fail if page is not locked.
+ */
+ cl_page_disown0(env, io, page);
+ lu_ref_del_at(&page->cp_reference, &page->cp_queue_ref, "queue",
+ plist);
+ cl_page_put(env, page);
+ }
+}
+EXPORT_SYMBOL(cl_page_list_disown);
+
+/**
+ * Releases pages from queue.
+ */
+void cl_page_list_fini(const struct lu_env *env, struct cl_page_list *plist)
+{
+ struct cl_page *page;
+ struct cl_page *temp;
+
+ LINVRNT(plist->pl_owner == current);
+
+ cl_page_list_for_each_safe(page, temp, plist)
+ cl_page_list_del(env, plist, page);
+ LASSERT(plist->pl_nr == 0);
+}
+EXPORT_SYMBOL(cl_page_list_fini);
+
+/**
+ * Owns all pages in a queue.
+ */
+int cl_page_list_own(const struct lu_env *env,
+ struct cl_io *io, struct cl_page_list *plist)
+{
+ struct cl_page *page;
+ struct cl_page *temp;
+ pgoff_t index = 0;
+ int result;
+
+ LINVRNT(plist->pl_owner == current);
+
+ result = 0;
+ cl_page_list_for_each_safe(page, temp, plist) {
+ LASSERT(index <= page->cp_index);
+ index = page->cp_index;
+ if (cl_page_own(env, io, page) == 0)
+ result = result ?: page->cp_error;
+ else
+ cl_page_list_del(env, plist, page);
+ }
+ return result;
+}
+EXPORT_SYMBOL(cl_page_list_own);
+
+/**
+ * Assumes all pages in a queue.
+ */
+void cl_page_list_assume(const struct lu_env *env,
+ struct cl_io *io, struct cl_page_list *plist)
+{
+ struct cl_page *page;
+
+ LINVRNT(plist->pl_owner == current);
+
+ cl_page_list_for_each(page, plist)
+ cl_page_assume(env, io, page);
+}
+EXPORT_SYMBOL(cl_page_list_assume);
+
+/**
+ * Discards all pages in a queue.
+ */
+void cl_page_list_discard(const struct lu_env *env, struct cl_io *io,
+ struct cl_page_list *plist)
+{
+ struct cl_page *page;
+
+ LINVRNT(plist->pl_owner == current);
+ cl_page_list_for_each(page, plist)
+ cl_page_discard(env, io, page);
+}
+EXPORT_SYMBOL(cl_page_list_discard);
+
+/**
+ * Unmaps all pages in a queue from user virtual memory.
+ */
+int cl_page_list_unmap(const struct lu_env *env, struct cl_io *io,
+ struct cl_page_list *plist)
+{
+ struct cl_page *page;
+ int result;
+
+ LINVRNT(plist->pl_owner == current);
+ result = 0;
+ cl_page_list_for_each(page, plist) {
+ result = cl_page_unmap(env, io, page);
+ if (result != 0)
+ break;
+ }
+ return result;
+}
+EXPORT_SYMBOL(cl_page_list_unmap);
+
+/**
+ * Initialize dual page queue.
+ */
+void cl_2queue_init(struct cl_2queue *queue)
+{
+ cl_page_list_init(&queue->c2_qin);
+ cl_page_list_init(&queue->c2_qout);
+}
+EXPORT_SYMBOL(cl_2queue_init);
+
+/**
+ * Add a page to the incoming page list of 2-queue.
+ */
+void cl_2queue_add(struct cl_2queue *queue, struct cl_page *page)
+{
+ cl_page_list_add(&queue->c2_qin, page);
+}
+EXPORT_SYMBOL(cl_2queue_add);
+
+/**
+ * Disown pages in both lists of a 2-queue.
+ */
+void cl_2queue_disown(const struct lu_env *env,
+ struct cl_io *io, struct cl_2queue *queue)
+{
+ cl_page_list_disown(env, io, &queue->c2_qin);
+ cl_page_list_disown(env, io, &queue->c2_qout);
+}
+EXPORT_SYMBOL(cl_2queue_disown);
+
+/**
+ * Discard (truncate) pages in both lists of a 2-queue.
+ */
+void cl_2queue_discard(const struct lu_env *env,
+ struct cl_io *io, struct cl_2queue *queue)
+{
+ cl_page_list_discard(env, io, &queue->c2_qin);
+ cl_page_list_discard(env, io, &queue->c2_qout);
+}
+EXPORT_SYMBOL(cl_2queue_discard);
+
+/**
+ * Assume to own the pages in cl_2queue
+ */
+void cl_2queue_assume(const struct lu_env *env,
+ struct cl_io *io, struct cl_2queue *queue)
+{
+ cl_page_list_assume(env, io, &queue->c2_qin);
+ cl_page_list_assume(env, io, &queue->c2_qout);
+}
+EXPORT_SYMBOL(cl_2queue_assume);
+
+/**
+ * Finalize both page lists of a 2-queue.
+ */
+void cl_2queue_fini(const struct lu_env *env, struct cl_2queue *queue)
+{
+ cl_page_list_fini(env, &queue->c2_qout);
+ cl_page_list_fini(env, &queue->c2_qin);
+}
+EXPORT_SYMBOL(cl_2queue_fini);
+
+/**
+ * Initialize a 2-queue to contain \a page in its incoming page list.
+ */
+void cl_2queue_init_page(struct cl_2queue *queue, struct cl_page *page)
+{
+ cl_2queue_init(queue);
+ cl_2queue_add(queue, page);
+}
+EXPORT_SYMBOL(cl_2queue_init_page);
+
+/**
+ * Returns top-level io.
+ *
+ * \see cl_object_top(), cl_page_top().
+ */
+struct cl_io *cl_io_top(struct cl_io *io)
+{
+ while (io->ci_parent != NULL)
+ io = io->ci_parent;
+ return io;
+}
+EXPORT_SYMBOL(cl_io_top);
+
+/**
+ * Prints human readable representation of \a io to the \a f.
+ */
+void cl_io_print(const struct lu_env *env, void *cookie,
+ lu_printer_t printer, const struct cl_io *io)
+{
+}
+
+/**
+ * Adds request slice to the compound request.
+ *
+ * This is called by cl_device_operations::cdo_req_init() methods to add a
+ * per-layer state to the request. New state is added at the end of
+ * cl_req::crq_layers list, that is, it is at the bottom of the stack.
+ *
+ * \see cl_lock_slice_add(), cl_page_slice_add(), cl_io_slice_add()
+ */
+void cl_req_slice_add(struct cl_req *req, struct cl_req_slice *slice,
+ struct cl_device *dev,
+ const struct cl_req_operations *ops)
+{
+ list_add_tail(&slice->crs_linkage, &req->crq_layers);
+ slice->crs_dev = dev;
+ slice->crs_ops = ops;
+ slice->crs_req = req;
+}
+EXPORT_SYMBOL(cl_req_slice_add);
+
+static void cl_req_free(const struct lu_env *env, struct cl_req *req)
+{
+ unsigned i;
+
+ LASSERT(list_empty(&req->crq_pages));
+ LASSERT(req->crq_nrpages == 0);
+ LINVRNT(list_empty(&req->crq_layers));
+ LINVRNT(equi(req->crq_nrobjs > 0, req->crq_o != NULL));
+
+ if (req->crq_o != NULL) {
+ for (i = 0; i < req->crq_nrobjs; ++i) {
+ struct cl_object *obj = req->crq_o[i].ro_obj;
+ if (obj != NULL) {
+ lu_object_ref_del_at(&obj->co_lu,
+ &req->crq_o[i].ro_obj_ref,
+ "cl_req", req);
+ cl_object_put(env, obj);
+ }
+ }
+ OBD_FREE(req->crq_o, req->crq_nrobjs * sizeof(req->crq_o[0]));
+ }
+ OBD_FREE_PTR(req);
+}
+
+static int cl_req_init(const struct lu_env *env, struct cl_req *req,
+ struct cl_page *page)
+{
+ struct cl_device *dev;
+ struct cl_page_slice *slice;
+ int result;
+
+ result = 0;
+ page = cl_page_top(page);
+ do {
+ list_for_each_entry(slice, &page->cp_layers, cpl_linkage) {
+ dev = lu2cl_dev(slice->cpl_obj->co_lu.lo_dev);
+ if (dev->cd_ops->cdo_req_init != NULL) {
+ result = dev->cd_ops->cdo_req_init(env,
+ dev, req);
+ if (result != 0)
+ break;
+ }
+ }
+ page = page->cp_child;
+ } while (page != NULL && result == 0);
+ return result;
+}
+
+/**
+ * Invokes per-request transfer completion call-backs
+ * (cl_req_operations::cro_completion()) bottom-to-top.
+ */
+void cl_req_completion(const struct lu_env *env, struct cl_req *req, int rc)
+{
+ struct cl_req_slice *slice;
+
+ /*
+ * for the lack of list_for_each_entry_reverse_safe()...
+ */
+ while (!list_empty(&req->crq_layers)) {
+ slice = list_entry(req->crq_layers.prev,
+ struct cl_req_slice, crs_linkage);
+ list_del_init(&slice->crs_linkage);
+ if (slice->crs_ops->cro_completion != NULL)
+ slice->crs_ops->cro_completion(env, slice, rc);
+ }
+ cl_req_free(env, req);
+}
+EXPORT_SYMBOL(cl_req_completion);
+
+/**
+ * Allocates new transfer request.
+ */
+struct cl_req *cl_req_alloc(const struct lu_env *env, struct cl_page *page,
+ enum cl_req_type crt, int nr_objects)
+{
+ struct cl_req *req;
+
+ LINVRNT(nr_objects > 0);
+
+ OBD_ALLOC_PTR(req);
+ if (req != NULL) {
+ int result;
+
+ req->crq_type = crt;
+ INIT_LIST_HEAD(&req->crq_pages);
+ INIT_LIST_HEAD(&req->crq_layers);
+
+ OBD_ALLOC(req->crq_o, nr_objects * sizeof(req->crq_o[0]));
+ if (req->crq_o != NULL) {
+ req->crq_nrobjs = nr_objects;
+ result = cl_req_init(env, req, page);
+ } else
+ result = -ENOMEM;
+ if (result != 0) {
+ cl_req_completion(env, req, result);
+ req = ERR_PTR(result);
+ }
+ } else
+ req = ERR_PTR(-ENOMEM);
+ return req;
+}
+EXPORT_SYMBOL(cl_req_alloc);
+
+/**
+ * Adds a page to a request.
+ */
+void cl_req_page_add(const struct lu_env *env,
+ struct cl_req *req, struct cl_page *page)
+{
+ struct cl_object *obj;
+ struct cl_req_obj *rqo;
+ int i;
+
+ page = cl_page_top(page);
+
+ LASSERT(list_empty(&page->cp_flight));
+ LASSERT(page->cp_req == NULL);
+
+ CL_PAGE_DEBUG(D_PAGE, env, page, "req %p, %d, %u\n",
+ req, req->crq_type, req->crq_nrpages);
+
+ list_add_tail(&page->cp_flight, &req->crq_pages);
+ ++req->crq_nrpages;
+ page->cp_req = req;
+ obj = cl_object_top(page->cp_obj);
+ for (i = 0, rqo = req->crq_o; obj != rqo->ro_obj; ++i, ++rqo) {
+ if (rqo->ro_obj == NULL) {
+ rqo->ro_obj = obj;
+ cl_object_get(obj);
+ lu_object_ref_add_at(&obj->co_lu, &rqo->ro_obj_ref,
+ "cl_req", req);
+ break;
+ }
+ }
+ LASSERT(i < req->crq_nrobjs);
+}
+EXPORT_SYMBOL(cl_req_page_add);
+
+/**
+ * Removes a page from a request.
+ */
+void cl_req_page_done(const struct lu_env *env, struct cl_page *page)
+{
+ struct cl_req *req = page->cp_req;
+
+ page = cl_page_top(page);
+
+ LASSERT(!list_empty(&page->cp_flight));
+ LASSERT(req->crq_nrpages > 0);
+
+ list_del_init(&page->cp_flight);
+ --req->crq_nrpages;
+ page->cp_req = NULL;
+}
+EXPORT_SYMBOL(cl_req_page_done);
+
+/**
+ * Notifies layers that request is about to depart by calling
+ * cl_req_operations::cro_prep() top-to-bottom.
+ */
+int cl_req_prep(const struct lu_env *env, struct cl_req *req)
+{
+ int i;
+ int result;
+ const struct cl_req_slice *slice;
+
+ /*
+ * Check that the caller of cl_req_alloc() didn't lie about the number
+ * of objects.
+ */
+ for (i = 0; i < req->crq_nrobjs; ++i)
+ LASSERT(req->crq_o[i].ro_obj != NULL);
+
+ result = 0;
+ list_for_each_entry(slice, &req->crq_layers, crs_linkage) {
+ if (slice->crs_ops->cro_prep != NULL) {
+ result = slice->crs_ops->cro_prep(env, slice);
+ if (result != 0)
+ break;
+ }
+ }
+ return result;
+}
+EXPORT_SYMBOL(cl_req_prep);
+
+/**
+ * Fills in attributes that are passed to server together with transfer. Only
+ * attributes from \a flags may be touched. This can be called multiple times
+ * for the same request.
+ */
+void cl_req_attr_set(const struct lu_env *env, struct cl_req *req,
+ struct cl_req_attr *attr, u64 flags)
+{
+ const struct cl_req_slice *slice;
+ struct cl_page *page;
+ int i;
+
+ LASSERT(!list_empty(&req->crq_pages));
+
+ /* Take any page to use as a model. */
+ page = list_entry(req->crq_pages.next, struct cl_page, cp_flight);
+
+ for (i = 0; i < req->crq_nrobjs; ++i) {
+ list_for_each_entry(slice, &req->crq_layers, crs_linkage) {
+ const struct cl_page_slice *scan;
+ const struct cl_object *obj;
+
+ scan = cl_page_at(page,
+ slice->crs_dev->cd_lu_dev.ld_type);
+ LASSERT(scan != NULL);
+ obj = scan->cpl_obj;
+ if (slice->crs_ops->cro_attr_set != NULL)
+ slice->crs_ops->cro_attr_set(env, slice, obj,
+ attr + i, flags);
+ }
+ }
+}
+EXPORT_SYMBOL(cl_req_attr_set);
+
+/* XXX complete(), init_completion(), and wait_for_completion(), until they are
+ * implemented in libcfs. */
+# include <linux/sched.h>
+
+/**
+ * Initialize synchronous io wait anchor, for transfer of \a nrpages pages.
+ */
+void cl_sync_io_init(struct cl_sync_io *anchor, int nrpages)
+{
+ init_waitqueue_head(&anchor->csi_waitq);
+ atomic_set(&anchor->csi_sync_nr, nrpages);
+ atomic_set(&anchor->csi_barrier, nrpages > 0);
+ anchor->csi_sync_rc = 0;
+}
+EXPORT_SYMBOL(cl_sync_io_init);
+
+/**
+ * Wait until all transfer completes. Transfer completion routine has to call
+ * cl_sync_io_note() for every page.
+ */
+int cl_sync_io_wait(const struct lu_env *env, struct cl_io *io,
+ struct cl_page_list *queue, struct cl_sync_io *anchor,
+ long timeout)
+{
+ struct l_wait_info lwi = LWI_TIMEOUT_INTR(cfs_time_seconds(timeout),
+ NULL, NULL, NULL);
+ int rc;
+
+ LASSERT(timeout >= 0);
+
+ rc = l_wait_event(anchor->csi_waitq,
+ atomic_read(&anchor->csi_sync_nr) == 0,
+ &lwi);
+ if (rc < 0) {
+ CERROR("SYNC IO failed with error: %d, try to cancel %d remaining pages\n",
+ rc, atomic_read(&anchor->csi_sync_nr));
+
+ (void)cl_io_cancel(env, io, queue);
+
+ lwi = (struct l_wait_info) { 0 };
+ (void)l_wait_event(anchor->csi_waitq,
+ atomic_read(&anchor->csi_sync_nr) == 0,
+ &lwi);
+ } else {
+ rc = anchor->csi_sync_rc;
+ }
+ LASSERT(atomic_read(&anchor->csi_sync_nr) == 0);
+ cl_page_list_assume(env, io, queue);
+
+ /* wait until cl_sync_io_note() has done wakeup */
+ while (unlikely(atomic_read(&anchor->csi_barrier) != 0)) {
+ cpu_relax();
+ }
+
+ POISON(anchor, 0x5a, sizeof(*anchor));
+ return rc;
+}
+EXPORT_SYMBOL(cl_sync_io_wait);
+
+/**
+ * Indicate that transfer of a single page completed.
+ */
+void cl_sync_io_note(struct cl_sync_io *anchor, int ioret)
+{
+ if (anchor->csi_sync_rc == 0 && ioret < 0)
+ anchor->csi_sync_rc = ioret;
+ /*
+ * Synchronous IO done without releasing page lock (e.g., as a part of
+ * ->{prepare,commit}_write(). Completion is used to signal the end of
+ * IO.
+ */
+ LASSERT(atomic_read(&anchor->csi_sync_nr) > 0);
+ if (atomic_dec_and_test(&anchor->csi_sync_nr)) {
+ wake_up_all(&anchor->csi_waitq);
+ /* it's safe to nuke or reuse anchor now */
+ atomic_set(&anchor->csi_barrier, 0);
+ }
+}
+EXPORT_SYMBOL(cl_sync_io_note);
diff --git a/drivers/staging/lustre/lustre/obdclass/cl_lock.c b/drivers/staging/lustre/lustre/obdclass/cl_lock.c
new file mode 100644
index 000000000..b081167f9
--- /dev/null
+++ b/drivers/staging/lustre/lustre/obdclass/cl_lock.c
@@ -0,0 +1,2239 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2011, 2012, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ *
+ * Client Extent Lock.
+ *
+ * Author: Nikita Danilov <nikita.danilov@sun.com>
+ */
+
+#define DEBUG_SUBSYSTEM S_CLASS
+
+#include "../include/obd_class.h"
+#include "../include/obd_support.h"
+#include "../include/lustre_fid.h"
+#include <linux/list.h>
+#include "../include/cl_object.h"
+#include "cl_internal.h"
+
+/** Lock class of cl_lock::cll_guard */
+static struct lock_class_key cl_lock_guard_class;
+static struct kmem_cache *cl_lock_kmem;
+
+static struct lu_kmem_descr cl_lock_caches[] = {
+ {
+ .ckd_cache = &cl_lock_kmem,
+ .ckd_name = "cl_lock_kmem",
+ .ckd_size = sizeof (struct cl_lock)
+ },
+ {
+ .ckd_cache = NULL
+ }
+};
+
+#define CS_LOCK_INC(o, item)
+#define CS_LOCK_DEC(o, item)
+#define CS_LOCKSTATE_INC(o, state)
+#define CS_LOCKSTATE_DEC(o, state)
+
+/**
+ * Basic lock invariant that is maintained at all times. Caller either has a
+ * reference to \a lock, or somehow assures that \a lock cannot be freed.
+ *
+ * \see cl_lock_invariant()
+ */
+static int cl_lock_invariant_trusted(const struct lu_env *env,
+ const struct cl_lock *lock)
+{
+ return ergo(lock->cll_state == CLS_FREEING, lock->cll_holds == 0) &&
+ atomic_read(&lock->cll_ref) >= lock->cll_holds &&
+ lock->cll_holds >= lock->cll_users &&
+ lock->cll_holds >= 0 &&
+ lock->cll_users >= 0 &&
+ lock->cll_depth >= 0;
+}
+
+/**
+ * Stronger lock invariant, checking that caller has a reference on a lock.
+ *
+ * \see cl_lock_invariant_trusted()
+ */
+static int cl_lock_invariant(const struct lu_env *env,
+ const struct cl_lock *lock)
+{
+ int result;
+
+ result = atomic_read(&lock->cll_ref) > 0 &&
+ cl_lock_invariant_trusted(env, lock);
+ if (!result && env != NULL)
+ CL_LOCK_DEBUG(D_ERROR, env, lock, "invariant broken");
+ return result;
+}
+
+/**
+ * Returns lock "nesting": 0 for a top-lock and 1 for a sub-lock.
+ */
+static enum clt_nesting_level cl_lock_nesting(const struct cl_lock *lock)
+{
+ return cl_object_header(lock->cll_descr.cld_obj)->coh_nesting;
+}
+
+/**
+ * Returns a set of counters for this lock, depending on a lock nesting.
+ */
+static struct cl_thread_counters *cl_lock_counters(const struct lu_env *env,
+ const struct cl_lock *lock)
+{
+ struct cl_thread_info *info;
+ enum clt_nesting_level nesting;
+
+ info = cl_env_info(env);
+ nesting = cl_lock_nesting(lock);
+ LASSERT(nesting < ARRAY_SIZE(info->clt_counters));
+ return &info->clt_counters[nesting];
+}
+
+static void cl_lock_trace0(int level, const struct lu_env *env,
+ const char *prefix, const struct cl_lock *lock,
+ const char *func, const int line)
+{
+ struct cl_object_header *h = cl_object_header(lock->cll_descr.cld_obj);
+ CDEBUG(level, "%s: %p@(%d %p %d %d %d %d %d %lx)(%p/%d/%d) at %s():%d\n",
+ prefix, lock, atomic_read(&lock->cll_ref),
+ lock->cll_guarder, lock->cll_depth,
+ lock->cll_state, lock->cll_error, lock->cll_holds,
+ lock->cll_users, lock->cll_flags,
+ env, h->coh_nesting, cl_lock_nr_mutexed(env),
+ func, line);
+}
+#define cl_lock_trace(level, env, prefix, lock) \
+ cl_lock_trace0(level, env, prefix, lock, __func__, __LINE__)
+
+#define RETIP ((unsigned long)__builtin_return_address(0))
+
+#ifdef CONFIG_LOCKDEP
+static struct lock_class_key cl_lock_key;
+
+static void cl_lock_lockdep_init(struct cl_lock *lock)
+{
+ lockdep_set_class_and_name(lock, &cl_lock_key, "EXT");
+}
+
+static void cl_lock_lockdep_acquire(const struct lu_env *env,
+ struct cl_lock *lock, __u32 enqflags)
+{
+ cl_lock_counters(env, lock)->ctc_nr_locks_acquired++;
+ lock_map_acquire(&lock->dep_map);
+}
+
+static void cl_lock_lockdep_release(const struct lu_env *env,
+ struct cl_lock *lock)
+{
+ cl_lock_counters(env, lock)->ctc_nr_locks_acquired--;
+ lock_release(&lock->dep_map, 0, RETIP);
+}
+
+#else /* !CONFIG_LOCKDEP */
+
+static void cl_lock_lockdep_init(struct cl_lock *lock)
+{}
+static void cl_lock_lockdep_acquire(const struct lu_env *env,
+ struct cl_lock *lock, __u32 enqflags)
+{}
+static void cl_lock_lockdep_release(const struct lu_env *env,
+ struct cl_lock *lock)
+{}
+
+#endif /* !CONFIG_LOCKDEP */
+
+/**
+ * Adds lock slice to the compound lock.
+ *
+ * This is called by cl_object_operations::coo_lock_init() methods to add a
+ * per-layer state to the lock. New state is added at the end of
+ * cl_lock::cll_layers list, that is, it is at the bottom of the stack.
+ *
+ * \see cl_req_slice_add(), cl_page_slice_add(), cl_io_slice_add()
+ */
+void cl_lock_slice_add(struct cl_lock *lock, struct cl_lock_slice *slice,
+ struct cl_object *obj,
+ const struct cl_lock_operations *ops)
+{
+ slice->cls_lock = lock;
+ list_add_tail(&slice->cls_linkage, &lock->cll_layers);
+ slice->cls_obj = obj;
+ slice->cls_ops = ops;
+}
+EXPORT_SYMBOL(cl_lock_slice_add);
+
+/**
+ * Returns true iff a lock with the mode \a has provides at least the same
+ * guarantees as a lock with the mode \a need.
+ */
+int cl_lock_mode_match(enum cl_lock_mode has, enum cl_lock_mode need)
+{
+ LINVRNT(need == CLM_READ || need == CLM_WRITE ||
+ need == CLM_PHANTOM || need == CLM_GROUP);
+ LINVRNT(has == CLM_READ || has == CLM_WRITE ||
+ has == CLM_PHANTOM || has == CLM_GROUP);
+ CLASSERT(CLM_PHANTOM < CLM_READ);
+ CLASSERT(CLM_READ < CLM_WRITE);
+ CLASSERT(CLM_WRITE < CLM_GROUP);
+
+ if (has != CLM_GROUP)
+ return need <= has;
+ else
+ return need == has;
+}
+EXPORT_SYMBOL(cl_lock_mode_match);
+
+/**
+ * Returns true iff extent portions of lock descriptions match.
+ */
+int cl_lock_ext_match(const struct cl_lock_descr *has,
+ const struct cl_lock_descr *need)
+{
+ return
+ has->cld_start <= need->cld_start &&
+ has->cld_end >= need->cld_end &&
+ cl_lock_mode_match(has->cld_mode, need->cld_mode) &&
+ (has->cld_mode != CLM_GROUP || has->cld_gid == need->cld_gid);
+}
+EXPORT_SYMBOL(cl_lock_ext_match);
+
+/**
+ * Returns true iff a lock with the description \a has provides at least the
+ * same guarantees as a lock with the description \a need.
+ */
+int cl_lock_descr_match(const struct cl_lock_descr *has,
+ const struct cl_lock_descr *need)
+{
+ return
+ cl_object_same(has->cld_obj, need->cld_obj) &&
+ cl_lock_ext_match(has, need);
+}
+EXPORT_SYMBOL(cl_lock_descr_match);
+
+static void cl_lock_free(const struct lu_env *env, struct cl_lock *lock)
+{
+ struct cl_object *obj = lock->cll_descr.cld_obj;
+
+ LINVRNT(!cl_lock_is_mutexed(lock));
+
+ cl_lock_trace(D_DLMTRACE, env, "free lock", lock);
+ might_sleep();
+ while (!list_empty(&lock->cll_layers)) {
+ struct cl_lock_slice *slice;
+
+ slice = list_entry(lock->cll_layers.next,
+ struct cl_lock_slice, cls_linkage);
+ list_del_init(lock->cll_layers.next);
+ slice->cls_ops->clo_fini(env, slice);
+ }
+ CS_LOCK_DEC(obj, total);
+ CS_LOCKSTATE_DEC(obj, lock->cll_state);
+ lu_object_ref_del_at(&obj->co_lu, &lock->cll_obj_ref, "cl_lock", lock);
+ cl_object_put(env, obj);
+ lu_ref_fini(&lock->cll_reference);
+ lu_ref_fini(&lock->cll_holders);
+ mutex_destroy(&lock->cll_guard);
+ OBD_SLAB_FREE_PTR(lock, cl_lock_kmem);
+}
+
+/**
+ * Releases a reference on a lock.
+ *
+ * When last reference is released, lock is returned to the cache, unless it
+ * is in cl_lock_state::CLS_FREEING state, in which case it is destroyed
+ * immediately.
+ *
+ * \see cl_object_put(), cl_page_put()
+ */
+void cl_lock_put(const struct lu_env *env, struct cl_lock *lock)
+{
+ struct cl_object *obj;
+
+ LINVRNT(cl_lock_invariant(env, lock));
+ obj = lock->cll_descr.cld_obj;
+ LINVRNT(obj != NULL);
+
+ CDEBUG(D_TRACE, "releasing reference: %d %p %lu\n",
+ atomic_read(&lock->cll_ref), lock, RETIP);
+
+ if (atomic_dec_and_test(&lock->cll_ref)) {
+ if (lock->cll_state == CLS_FREEING) {
+ LASSERT(list_empty(&lock->cll_linkage));
+ cl_lock_free(env, lock);
+ }
+ CS_LOCK_DEC(obj, busy);
+ }
+}
+EXPORT_SYMBOL(cl_lock_put);
+
+/**
+ * Acquires an additional reference to a lock.
+ *
+ * This can be called only by caller already possessing a reference to \a
+ * lock.
+ *
+ * \see cl_object_get(), cl_page_get()
+ */
+void cl_lock_get(struct cl_lock *lock)
+{
+ LINVRNT(cl_lock_invariant(NULL, lock));
+ CDEBUG(D_TRACE, "acquiring reference: %d %p %lu\n",
+ atomic_read(&lock->cll_ref), lock, RETIP);
+ atomic_inc(&lock->cll_ref);
+}
+EXPORT_SYMBOL(cl_lock_get);
+
+/**
+ * Acquires a reference to a lock.
+ *
+ * This is much like cl_lock_get(), except that this function can be used to
+ * acquire initial reference to the cached lock. Caller has to deal with all
+ * possible races. Use with care!
+ *
+ * \see cl_page_get_trust()
+ */
+void cl_lock_get_trust(struct cl_lock *lock)
+{
+ CDEBUG(D_TRACE, "acquiring trusted reference: %d %p %lu\n",
+ atomic_read(&lock->cll_ref), lock, RETIP);
+ if (atomic_inc_return(&lock->cll_ref) == 1)
+ CS_LOCK_INC(lock->cll_descr.cld_obj, busy);
+}
+EXPORT_SYMBOL(cl_lock_get_trust);
+
+/**
+ * Helper function destroying the lock that wasn't completely initialized.
+ *
+ * Other threads can acquire references to the top-lock through its
+ * sub-locks. Hence, it cannot be cl_lock_free()-ed immediately.
+ */
+static void cl_lock_finish(const struct lu_env *env, struct cl_lock *lock)
+{
+ cl_lock_mutex_get(env, lock);
+ cl_lock_cancel(env, lock);
+ cl_lock_delete(env, lock);
+ cl_lock_mutex_put(env, lock);
+ cl_lock_put(env, lock);
+}
+
+static struct cl_lock *cl_lock_alloc(const struct lu_env *env,
+ struct cl_object *obj,
+ const struct cl_io *io,
+ const struct cl_lock_descr *descr)
+{
+ struct cl_lock *lock;
+ struct lu_object_header *head;
+
+ OBD_SLAB_ALLOC_PTR_GFP(lock, cl_lock_kmem, GFP_NOFS);
+ if (lock != NULL) {
+ atomic_set(&lock->cll_ref, 1);
+ lock->cll_descr = *descr;
+ lock->cll_state = CLS_NEW;
+ cl_object_get(obj);
+ lu_object_ref_add_at(&obj->co_lu, &lock->cll_obj_ref, "cl_lock",
+ lock);
+ INIT_LIST_HEAD(&lock->cll_layers);
+ INIT_LIST_HEAD(&lock->cll_linkage);
+ INIT_LIST_HEAD(&lock->cll_inclosure);
+ lu_ref_init(&lock->cll_reference);
+ lu_ref_init(&lock->cll_holders);
+ mutex_init(&lock->cll_guard);
+ lockdep_set_class(&lock->cll_guard, &cl_lock_guard_class);
+ init_waitqueue_head(&lock->cll_wq);
+ head = obj->co_lu.lo_header;
+ CS_LOCKSTATE_INC(obj, CLS_NEW);
+ CS_LOCK_INC(obj, total);
+ CS_LOCK_INC(obj, create);
+ cl_lock_lockdep_init(lock);
+ list_for_each_entry(obj, &head->loh_layers,
+ co_lu.lo_linkage) {
+ int err;
+
+ err = obj->co_ops->coo_lock_init(env, obj, lock, io);
+ if (err != 0) {
+ cl_lock_finish(env, lock);
+ lock = ERR_PTR(err);
+ break;
+ }
+ }
+ } else
+ lock = ERR_PTR(-ENOMEM);
+ return lock;
+}
+
+/**
+ * Transfer the lock into INTRANSIT state and return the original state.
+ *
+ * \pre state: CLS_CACHED, CLS_HELD or CLS_ENQUEUED
+ * \post state: CLS_INTRANSIT
+ * \see CLS_INTRANSIT
+ */
+enum cl_lock_state cl_lock_intransit(const struct lu_env *env,
+ struct cl_lock *lock)
+{
+ enum cl_lock_state state = lock->cll_state;
+
+ LASSERT(cl_lock_is_mutexed(lock));
+ LASSERT(state != CLS_INTRANSIT);
+ LASSERTF(state >= CLS_ENQUEUED && state <= CLS_CACHED,
+ "Malformed lock state %d.\n", state);
+
+ cl_lock_state_set(env, lock, CLS_INTRANSIT);
+ lock->cll_intransit_owner = current;
+ cl_lock_hold_add(env, lock, "intransit", current);
+ return state;
+}
+EXPORT_SYMBOL(cl_lock_intransit);
+
+/**
+ * Exit the intransit state and restore the lock state to the original state
+ */
+void cl_lock_extransit(const struct lu_env *env, struct cl_lock *lock,
+ enum cl_lock_state state)
+{
+ LASSERT(cl_lock_is_mutexed(lock));
+ LASSERT(lock->cll_state == CLS_INTRANSIT);
+ LASSERT(state != CLS_INTRANSIT);
+ LASSERT(lock->cll_intransit_owner == current);
+
+ lock->cll_intransit_owner = NULL;
+ cl_lock_state_set(env, lock, state);
+ cl_lock_unhold(env, lock, "intransit", current);
+}
+EXPORT_SYMBOL(cl_lock_extransit);
+
+/**
+ * Checking whether the lock is intransit state
+ */
+int cl_lock_is_intransit(struct cl_lock *lock)
+{
+ LASSERT(cl_lock_is_mutexed(lock));
+ return lock->cll_state == CLS_INTRANSIT &&
+ lock->cll_intransit_owner != current;
+}
+EXPORT_SYMBOL(cl_lock_is_intransit);
+/**
+ * Returns true iff lock is "suitable" for given io. E.g., locks acquired by
+ * truncate and O_APPEND cannot be reused for read/non-append-write, as they
+ * cover multiple stripes and can trigger cascading timeouts.
+ */
+static int cl_lock_fits_into(const struct lu_env *env,
+ const struct cl_lock *lock,
+ const struct cl_lock_descr *need,
+ const struct cl_io *io)
+{
+ const struct cl_lock_slice *slice;
+
+ LINVRNT(cl_lock_invariant_trusted(env, lock));
+ list_for_each_entry(slice, &lock->cll_layers, cls_linkage) {
+ if (slice->cls_ops->clo_fits_into != NULL &&
+ !slice->cls_ops->clo_fits_into(env, slice, need, io))
+ return 0;
+ }
+ return 1;
+}
+
+static struct cl_lock *cl_lock_lookup(const struct lu_env *env,
+ struct cl_object *obj,
+ const struct cl_io *io,
+ const struct cl_lock_descr *need)
+{
+ struct cl_lock *lock;
+ struct cl_object_header *head;
+
+ head = cl_object_header(obj);
+ assert_spin_locked(&head->coh_lock_guard);
+ CS_LOCK_INC(obj, lookup);
+ list_for_each_entry(lock, &head->coh_locks, cll_linkage) {
+ int matched;
+
+ matched = cl_lock_ext_match(&lock->cll_descr, need) &&
+ lock->cll_state < CLS_FREEING &&
+ lock->cll_error == 0 &&
+ !(lock->cll_flags & CLF_CANCELLED) &&
+ cl_lock_fits_into(env, lock, need, io);
+ CDEBUG(D_DLMTRACE, "has: "DDESCR"(%d) need: "DDESCR": %d\n",
+ PDESCR(&lock->cll_descr), lock->cll_state, PDESCR(need),
+ matched);
+ if (matched) {
+ cl_lock_get_trust(lock);
+ CS_LOCK_INC(obj, hit);
+ return lock;
+ }
+ }
+ return NULL;
+}
+
+/**
+ * Returns a lock matching description \a need.
+ *
+ * This is the main entry point into the cl_lock caching interface. First, a
+ * cache (implemented as a per-object linked list) is consulted. If lock is
+ * found there, it is returned immediately. Otherwise new lock is allocated
+ * and returned. In any case, additional reference to lock is acquired.
+ *
+ * \see cl_object_find(), cl_page_find()
+ */
+static struct cl_lock *cl_lock_find(const struct lu_env *env,
+ const struct cl_io *io,
+ const struct cl_lock_descr *need)
+{
+ struct cl_object_header *head;
+ struct cl_object *obj;
+ struct cl_lock *lock;
+
+ obj = need->cld_obj;
+ head = cl_object_header(obj);
+
+ spin_lock(&head->coh_lock_guard);
+ lock = cl_lock_lookup(env, obj, io, need);
+ spin_unlock(&head->coh_lock_guard);
+
+ if (lock == NULL) {
+ lock = cl_lock_alloc(env, obj, io, need);
+ if (!IS_ERR(lock)) {
+ struct cl_lock *ghost;
+
+ spin_lock(&head->coh_lock_guard);
+ ghost = cl_lock_lookup(env, obj, io, need);
+ if (ghost == NULL) {
+ cl_lock_get_trust(lock);
+ list_add_tail(&lock->cll_linkage,
+ &head->coh_locks);
+ spin_unlock(&head->coh_lock_guard);
+ CS_LOCK_INC(obj, busy);
+ } else {
+ spin_unlock(&head->coh_lock_guard);
+ /*
+ * Other threads can acquire references to the
+ * top-lock through its sub-locks. Hence, it
+ * cannot be cl_lock_free()-ed immediately.
+ */
+ cl_lock_finish(env, lock);
+ lock = ghost;
+ }
+ }
+ }
+ return lock;
+}
+
+/**
+ * Returns existing lock matching given description. This is similar to
+ * cl_lock_find() except that no new lock is created, and returned lock is
+ * guaranteed to be in enum cl_lock_state::CLS_HELD state.
+ */
+struct cl_lock *cl_lock_peek(const struct lu_env *env, const struct cl_io *io,
+ const struct cl_lock_descr *need,
+ const char *scope, const void *source)
+{
+ struct cl_object_header *head;
+ struct cl_object *obj;
+ struct cl_lock *lock;
+
+ obj = need->cld_obj;
+ head = cl_object_header(obj);
+
+ do {
+ spin_lock(&head->coh_lock_guard);
+ lock = cl_lock_lookup(env, obj, io, need);
+ spin_unlock(&head->coh_lock_guard);
+ if (lock == NULL)
+ return NULL;
+
+ cl_lock_mutex_get(env, lock);
+ if (lock->cll_state == CLS_INTRANSIT)
+ /* Don't care return value. */
+ cl_lock_state_wait(env, lock);
+ if (lock->cll_state == CLS_FREEING) {
+ cl_lock_mutex_put(env, lock);
+ cl_lock_put(env, lock);
+ lock = NULL;
+ }
+ } while (lock == NULL);
+
+ cl_lock_hold_add(env, lock, scope, source);
+ cl_lock_user_add(env, lock);
+ if (lock->cll_state == CLS_CACHED)
+ cl_use_try(env, lock, 1);
+ if (lock->cll_state == CLS_HELD) {
+ cl_lock_mutex_put(env, lock);
+ cl_lock_lockdep_acquire(env, lock, 0);
+ cl_lock_put(env, lock);
+ } else {
+ cl_unuse_try(env, lock);
+ cl_lock_unhold(env, lock, scope, source);
+ cl_lock_mutex_put(env, lock);
+ cl_lock_put(env, lock);
+ lock = NULL;
+ }
+
+ return lock;
+}
+EXPORT_SYMBOL(cl_lock_peek);
+
+/**
+ * Returns a slice within a lock, corresponding to the given layer in the
+ * device stack.
+ *
+ * \see cl_page_at()
+ */
+const struct cl_lock_slice *cl_lock_at(const struct cl_lock *lock,
+ const struct lu_device_type *dtype)
+{
+ const struct cl_lock_slice *slice;
+
+ LINVRNT(cl_lock_invariant_trusted(NULL, lock));
+
+ list_for_each_entry(slice, &lock->cll_layers, cls_linkage) {
+ if (slice->cls_obj->co_lu.lo_dev->ld_type == dtype)
+ return slice;
+ }
+ return NULL;
+}
+EXPORT_SYMBOL(cl_lock_at);
+
+static void cl_lock_mutex_tail(const struct lu_env *env, struct cl_lock *lock)
+{
+ struct cl_thread_counters *counters;
+
+ counters = cl_lock_counters(env, lock);
+ lock->cll_depth++;
+ counters->ctc_nr_locks_locked++;
+ lu_ref_add(&counters->ctc_locks_locked, "cll_guard", lock);
+ cl_lock_trace(D_TRACE, env, "got mutex", lock);
+}
+
+/**
+ * Locks cl_lock object.
+ *
+ * This is used to manipulate cl_lock fields, and to serialize state
+ * transitions in the lock state machine.
+ *
+ * \post cl_lock_is_mutexed(lock)
+ *
+ * \see cl_lock_mutex_put()
+ */
+void cl_lock_mutex_get(const struct lu_env *env, struct cl_lock *lock)
+{
+ LINVRNT(cl_lock_invariant(env, lock));
+
+ if (lock->cll_guarder == current) {
+ LINVRNT(cl_lock_is_mutexed(lock));
+ LINVRNT(lock->cll_depth > 0);
+ } else {
+ struct cl_object_header *hdr;
+ struct cl_thread_info *info;
+ int i;
+
+ LINVRNT(lock->cll_guarder != current);
+ hdr = cl_object_header(lock->cll_descr.cld_obj);
+ /*
+ * Check that mutices are taken in the bottom-to-top order.
+ */
+ info = cl_env_info(env);
+ for (i = 0; i < hdr->coh_nesting; ++i)
+ LASSERT(info->clt_counters[i].ctc_nr_locks_locked == 0);
+ mutex_lock_nested(&lock->cll_guard, hdr->coh_nesting);
+ lock->cll_guarder = current;
+ LINVRNT(lock->cll_depth == 0);
+ }
+ cl_lock_mutex_tail(env, lock);
+}
+EXPORT_SYMBOL(cl_lock_mutex_get);
+
+/**
+ * Try-locks cl_lock object.
+ *
+ * \retval 0 \a lock was successfully locked
+ *
+ * \retval -EBUSY \a lock cannot be locked right now
+ *
+ * \post ergo(result == 0, cl_lock_is_mutexed(lock))
+ *
+ * \see cl_lock_mutex_get()
+ */
+int cl_lock_mutex_try(const struct lu_env *env, struct cl_lock *lock)
+{
+ int result;
+
+ LINVRNT(cl_lock_invariant_trusted(env, lock));
+
+ result = 0;
+ if (lock->cll_guarder == current) {
+ LINVRNT(lock->cll_depth > 0);
+ cl_lock_mutex_tail(env, lock);
+ } else if (mutex_trylock(&lock->cll_guard)) {
+ LINVRNT(lock->cll_depth == 0);
+ lock->cll_guarder = current;
+ cl_lock_mutex_tail(env, lock);
+ } else
+ result = -EBUSY;
+ return result;
+}
+EXPORT_SYMBOL(cl_lock_mutex_try);
+
+/**
+ {* Unlocks cl_lock object.
+ *
+ * \pre cl_lock_is_mutexed(lock)
+ *
+ * \see cl_lock_mutex_get()
+ */
+void cl_lock_mutex_put(const struct lu_env *env, struct cl_lock *lock)
+{
+ struct cl_thread_counters *counters;
+
+ LINVRNT(cl_lock_invariant(env, lock));
+ LINVRNT(cl_lock_is_mutexed(lock));
+ LINVRNT(lock->cll_guarder == current);
+ LINVRNT(lock->cll_depth > 0);
+
+ counters = cl_lock_counters(env, lock);
+ LINVRNT(counters->ctc_nr_locks_locked > 0);
+
+ cl_lock_trace(D_TRACE, env, "put mutex", lock);
+ lu_ref_del(&counters->ctc_locks_locked, "cll_guard", lock);
+ counters->ctc_nr_locks_locked--;
+ if (--lock->cll_depth == 0) {
+ lock->cll_guarder = NULL;
+ mutex_unlock(&lock->cll_guard);
+ }
+}
+EXPORT_SYMBOL(cl_lock_mutex_put);
+
+/**
+ * Returns true iff lock's mutex is owned by the current thread.
+ */
+int cl_lock_is_mutexed(struct cl_lock *lock)
+{
+ return lock->cll_guarder == current;
+}
+EXPORT_SYMBOL(cl_lock_is_mutexed);
+
+/**
+ * Returns number of cl_lock mutices held by the current thread (environment).
+ */
+int cl_lock_nr_mutexed(const struct lu_env *env)
+{
+ struct cl_thread_info *info;
+ int i;
+ int locked;
+
+ /*
+ * NOTE: if summation across all nesting levels (currently 2) proves
+ * too expensive, a summary counter can be added to
+ * struct cl_thread_info.
+ */
+ info = cl_env_info(env);
+ for (i = 0, locked = 0; i < ARRAY_SIZE(info->clt_counters); ++i)
+ locked += info->clt_counters[i].ctc_nr_locks_locked;
+ return locked;
+}
+EXPORT_SYMBOL(cl_lock_nr_mutexed);
+
+static void cl_lock_cancel0(const struct lu_env *env, struct cl_lock *lock)
+{
+ LINVRNT(cl_lock_is_mutexed(lock));
+ LINVRNT(cl_lock_invariant(env, lock));
+ if (!(lock->cll_flags & CLF_CANCELLED)) {
+ const struct cl_lock_slice *slice;
+
+ lock->cll_flags |= CLF_CANCELLED;
+ list_for_each_entry_reverse(slice, &lock->cll_layers,
+ cls_linkage) {
+ if (slice->cls_ops->clo_cancel != NULL)
+ slice->cls_ops->clo_cancel(env, slice);
+ }
+ }
+}
+
+static void cl_lock_delete0(const struct lu_env *env, struct cl_lock *lock)
+{
+ struct cl_object_header *head;
+ const struct cl_lock_slice *slice;
+
+ LINVRNT(cl_lock_is_mutexed(lock));
+ LINVRNT(cl_lock_invariant(env, lock));
+
+ if (lock->cll_state < CLS_FREEING) {
+ bool in_cache;
+
+ LASSERT(lock->cll_state != CLS_INTRANSIT);
+ cl_lock_state_set(env, lock, CLS_FREEING);
+
+ head = cl_object_header(lock->cll_descr.cld_obj);
+
+ spin_lock(&head->coh_lock_guard);
+ in_cache = !list_empty(&lock->cll_linkage);
+ if (in_cache)
+ list_del_init(&lock->cll_linkage);
+ spin_unlock(&head->coh_lock_guard);
+
+ if (in_cache) /* coh_locks cache holds a refcount. */
+ cl_lock_put(env, lock);
+
+ /*
+ * From now on, no new references to this lock can be acquired
+ * by cl_lock_lookup().
+ */
+ list_for_each_entry_reverse(slice, &lock->cll_layers,
+ cls_linkage) {
+ if (slice->cls_ops->clo_delete != NULL)
+ slice->cls_ops->clo_delete(env, slice);
+ }
+ /*
+ * From now on, no new references to this lock can be acquired
+ * by layer-specific means (like a pointer from struct
+ * ldlm_lock in osc, or a pointer from top-lock to sub-lock in
+ * lov).
+ *
+ * Lock will be finally freed in cl_lock_put() when last of
+ * existing references goes away.
+ */
+ }
+}
+
+/**
+ * Mod(ifie)s cl_lock::cll_holds counter for a given lock. Also, for a
+ * top-lock (nesting == 0) accounts for this modification in the per-thread
+ * debugging counters. Sub-lock holds can be released by a thread different
+ * from one that acquired it.
+ */
+static void cl_lock_hold_mod(const struct lu_env *env, struct cl_lock *lock,
+ int delta)
+{
+ struct cl_thread_counters *counters;
+ enum clt_nesting_level nesting;
+
+ lock->cll_holds += delta;
+ nesting = cl_lock_nesting(lock);
+ if (nesting == CNL_TOP) {
+ counters = &cl_env_info(env)->clt_counters[CNL_TOP];
+ counters->ctc_nr_held += delta;
+ LASSERT(counters->ctc_nr_held >= 0);
+ }
+}
+
+/**
+ * Mod(ifie)s cl_lock::cll_users counter for a given lock. See
+ * cl_lock_hold_mod() for the explanation of the debugging code.
+ */
+static void cl_lock_used_mod(const struct lu_env *env, struct cl_lock *lock,
+ int delta)
+{
+ struct cl_thread_counters *counters;
+ enum clt_nesting_level nesting;
+
+ lock->cll_users += delta;
+ nesting = cl_lock_nesting(lock);
+ if (nesting == CNL_TOP) {
+ counters = &cl_env_info(env)->clt_counters[CNL_TOP];
+ counters->ctc_nr_used += delta;
+ LASSERT(counters->ctc_nr_used >= 0);
+ }
+}
+
+void cl_lock_hold_release(const struct lu_env *env, struct cl_lock *lock,
+ const char *scope, const void *source)
+{
+ LINVRNT(cl_lock_is_mutexed(lock));
+ LINVRNT(cl_lock_invariant(env, lock));
+ LASSERT(lock->cll_holds > 0);
+
+ cl_lock_trace(D_DLMTRACE, env, "hold release lock", lock);
+ lu_ref_del(&lock->cll_holders, scope, source);
+ cl_lock_hold_mod(env, lock, -1);
+ if (lock->cll_holds == 0) {
+ CL_LOCK_ASSERT(lock->cll_state != CLS_HELD, env, lock);
+ if (lock->cll_descr.cld_mode == CLM_PHANTOM ||
+ lock->cll_descr.cld_mode == CLM_GROUP ||
+ lock->cll_state != CLS_CACHED)
+ /*
+ * If lock is still phantom or grouplock when user is
+ * done with it---destroy the lock.
+ */
+ lock->cll_flags |= CLF_CANCELPEND|CLF_DOOMED;
+ if (lock->cll_flags & CLF_CANCELPEND) {
+ lock->cll_flags &= ~CLF_CANCELPEND;
+ cl_lock_cancel0(env, lock);
+ }
+ if (lock->cll_flags & CLF_DOOMED) {
+ /* no longer doomed: it's dead... Jim. */
+ lock->cll_flags &= ~CLF_DOOMED;
+ cl_lock_delete0(env, lock);
+ }
+ }
+}
+EXPORT_SYMBOL(cl_lock_hold_release);
+
+/**
+ * Waits until lock state is changed.
+ *
+ * This function is called with cl_lock mutex locked, atomically releases
+ * mutex and goes to sleep, waiting for a lock state change (signaled by
+ * cl_lock_signal()), and re-acquires the mutex before return.
+ *
+ * This function is used to wait until lock state machine makes some progress
+ * and to emulate synchronous operations on top of asynchronous lock
+ * interface.
+ *
+ * \retval -EINTR wait was interrupted
+ *
+ * \retval 0 wait wasn't interrupted
+ *
+ * \pre cl_lock_is_mutexed(lock)
+ *
+ * \see cl_lock_signal()
+ */
+int cl_lock_state_wait(const struct lu_env *env, struct cl_lock *lock)
+{
+ wait_queue_t waiter;
+ sigset_t blocked;
+ int result;
+
+ LINVRNT(cl_lock_is_mutexed(lock));
+ LINVRNT(cl_lock_invariant(env, lock));
+ LASSERT(lock->cll_depth == 1);
+ LASSERT(lock->cll_state != CLS_FREEING); /* too late to wait */
+
+ cl_lock_trace(D_DLMTRACE, env, "state wait lock", lock);
+ result = lock->cll_error;
+ if (result == 0) {
+ /* To avoid being interrupted by the 'non-fatal' signals
+ * (SIGCHLD, for instance), we'd block them temporarily.
+ * LU-305 */
+ blocked = cfs_block_sigsinv(LUSTRE_FATAL_SIGS);
+
+ init_waitqueue_entry(&waiter, current);
+ add_wait_queue(&lock->cll_wq, &waiter);
+ set_current_state(TASK_INTERRUPTIBLE);
+ cl_lock_mutex_put(env, lock);
+
+ LASSERT(cl_lock_nr_mutexed(env) == 0);
+
+ /* Returning ERESTARTSYS instead of EINTR so syscalls
+ * can be restarted if signals are pending here */
+ result = -ERESTARTSYS;
+ if (likely(!OBD_FAIL_CHECK(OBD_FAIL_LOCK_STATE_WAIT_INTR))) {
+ schedule();
+ if (!cfs_signal_pending())
+ result = 0;
+ }
+
+ cl_lock_mutex_get(env, lock);
+ set_current_state(TASK_RUNNING);
+ remove_wait_queue(&lock->cll_wq, &waiter);
+
+ /* Restore old blocked signals */
+ cfs_restore_sigs(blocked);
+ }
+ return result;
+}
+EXPORT_SYMBOL(cl_lock_state_wait);
+
+static void cl_lock_state_signal(const struct lu_env *env, struct cl_lock *lock,
+ enum cl_lock_state state)
+{
+ const struct cl_lock_slice *slice;
+
+ LINVRNT(cl_lock_is_mutexed(lock));
+ LINVRNT(cl_lock_invariant(env, lock));
+
+ list_for_each_entry(slice, &lock->cll_layers, cls_linkage)
+ if (slice->cls_ops->clo_state != NULL)
+ slice->cls_ops->clo_state(env, slice, state);
+ wake_up_all(&lock->cll_wq);
+}
+
+/**
+ * Notifies waiters that lock state changed.
+ *
+ * Wakes up all waiters sleeping in cl_lock_state_wait(), also notifies all
+ * layers about state change by calling cl_lock_operations::clo_state()
+ * top-to-bottom.
+ */
+void cl_lock_signal(const struct lu_env *env, struct cl_lock *lock)
+{
+ cl_lock_trace(D_DLMTRACE, env, "state signal lock", lock);
+ cl_lock_state_signal(env, lock, lock->cll_state);
+}
+EXPORT_SYMBOL(cl_lock_signal);
+
+/**
+ * Changes lock state.
+ *
+ * This function is invoked to notify layers that lock state changed, possible
+ * as a result of an asynchronous event such as call-back reception.
+ *
+ * \post lock->cll_state == state
+ *
+ * \see cl_lock_operations::clo_state()
+ */
+void cl_lock_state_set(const struct lu_env *env, struct cl_lock *lock,
+ enum cl_lock_state state)
+{
+ LASSERT(lock->cll_state <= state ||
+ (lock->cll_state == CLS_CACHED &&
+ (state == CLS_HELD || /* lock found in cache */
+ state == CLS_NEW || /* sub-lock canceled */
+ state == CLS_INTRANSIT)) ||
+ /* lock is in transit state */
+ lock->cll_state == CLS_INTRANSIT);
+
+ if (lock->cll_state != state) {
+ CS_LOCKSTATE_DEC(lock->cll_descr.cld_obj, lock->cll_state);
+ CS_LOCKSTATE_INC(lock->cll_descr.cld_obj, state);
+
+ cl_lock_state_signal(env, lock, state);
+ lock->cll_state = state;
+ }
+}
+EXPORT_SYMBOL(cl_lock_state_set);
+
+static int cl_unuse_try_internal(const struct lu_env *env, struct cl_lock *lock)
+{
+ const struct cl_lock_slice *slice;
+ int result;
+
+ do {
+ result = 0;
+
+ LINVRNT(cl_lock_is_mutexed(lock));
+ LINVRNT(cl_lock_invariant(env, lock));
+ LASSERT(lock->cll_state == CLS_INTRANSIT);
+
+ result = -ENOSYS;
+ list_for_each_entry_reverse(slice, &lock->cll_layers,
+ cls_linkage) {
+ if (slice->cls_ops->clo_unuse != NULL) {
+ result = slice->cls_ops->clo_unuse(env, slice);
+ if (result != 0)
+ break;
+ }
+ }
+ LASSERT(result != -ENOSYS);
+ } while (result == CLO_REPEAT);
+
+ return result;
+}
+
+/**
+ * Yanks lock from the cache (cl_lock_state::CLS_CACHED state) by calling
+ * cl_lock_operations::clo_use() top-to-bottom to notify layers.
+ * @atomic = 1, it must unuse the lock to recovery the lock to keep the
+ * use process atomic
+ */
+int cl_use_try(const struct lu_env *env, struct cl_lock *lock, int atomic)
+{
+ const struct cl_lock_slice *slice;
+ int result;
+ enum cl_lock_state state;
+
+ cl_lock_trace(D_DLMTRACE, env, "use lock", lock);
+
+ LASSERT(lock->cll_state == CLS_CACHED);
+ if (lock->cll_error)
+ return lock->cll_error;
+
+ result = -ENOSYS;
+ state = cl_lock_intransit(env, lock);
+ list_for_each_entry(slice, &lock->cll_layers, cls_linkage) {
+ if (slice->cls_ops->clo_use != NULL) {
+ result = slice->cls_ops->clo_use(env, slice);
+ if (result != 0)
+ break;
+ }
+ }
+ LASSERT(result != -ENOSYS);
+
+ LASSERTF(lock->cll_state == CLS_INTRANSIT, "Wrong state %d.\n",
+ lock->cll_state);
+
+ if (result == 0) {
+ state = CLS_HELD;
+ } else {
+ if (result == -ESTALE) {
+ /*
+ * ESTALE means sublock being cancelled
+ * at this time, and set lock state to
+ * be NEW here and ask the caller to repeat.
+ */
+ state = CLS_NEW;
+ result = CLO_REPEAT;
+ }
+
+ /* @atomic means back-off-on-failure. */
+ if (atomic) {
+ int rc;
+ rc = cl_unuse_try_internal(env, lock);
+ /* Vet the results. */
+ if (rc < 0 && result > 0)
+ result = rc;
+ }
+
+ }
+ cl_lock_extransit(env, lock, state);
+ return result;
+}
+EXPORT_SYMBOL(cl_use_try);
+
+/**
+ * Helper for cl_enqueue_try() that calls ->clo_enqueue() across all layers
+ * top-to-bottom.
+ */
+static int cl_enqueue_kick(const struct lu_env *env,
+ struct cl_lock *lock,
+ struct cl_io *io, __u32 flags)
+{
+ int result;
+ const struct cl_lock_slice *slice;
+
+ result = -ENOSYS;
+ list_for_each_entry(slice, &lock->cll_layers, cls_linkage) {
+ if (slice->cls_ops->clo_enqueue != NULL) {
+ result = slice->cls_ops->clo_enqueue(env,
+ slice, io, flags);
+ if (result != 0)
+ break;
+ }
+ }
+ LASSERT(result != -ENOSYS);
+ return result;
+}
+
+/**
+ * Tries to enqueue a lock.
+ *
+ * This function is called repeatedly by cl_enqueue() until either lock is
+ * enqueued, or error occurs. This function does not block waiting for
+ * networking communication to complete.
+ *
+ * \post ergo(result == 0, lock->cll_state == CLS_ENQUEUED ||
+ * lock->cll_state == CLS_HELD)
+ *
+ * \see cl_enqueue() cl_lock_operations::clo_enqueue()
+ * \see cl_lock_state::CLS_ENQUEUED
+ */
+int cl_enqueue_try(const struct lu_env *env, struct cl_lock *lock,
+ struct cl_io *io, __u32 flags)
+{
+ int result;
+
+ cl_lock_trace(D_DLMTRACE, env, "enqueue lock", lock);
+ do {
+ LINVRNT(cl_lock_is_mutexed(lock));
+
+ result = lock->cll_error;
+ if (result != 0)
+ break;
+
+ switch (lock->cll_state) {
+ case CLS_NEW:
+ cl_lock_state_set(env, lock, CLS_QUEUING);
+ /* fall-through */
+ case CLS_QUEUING:
+ /* kick layers. */
+ result = cl_enqueue_kick(env, lock, io, flags);
+ /* For AGL case, the cl_lock::cll_state may
+ * become CLS_HELD already. */
+ if (result == 0 && lock->cll_state == CLS_QUEUING)
+ cl_lock_state_set(env, lock, CLS_ENQUEUED);
+ break;
+ case CLS_INTRANSIT:
+ LASSERT(cl_lock_is_intransit(lock));
+ result = CLO_WAIT;
+ break;
+ case CLS_CACHED:
+ /* yank lock from the cache. */
+ result = cl_use_try(env, lock, 0);
+ break;
+ case CLS_ENQUEUED:
+ case CLS_HELD:
+ result = 0;
+ break;
+ default:
+ case CLS_FREEING:
+ /*
+ * impossible, only held locks with increased
+ * ->cll_holds can be enqueued, and they cannot be
+ * freed.
+ */
+ LBUG();
+ }
+ } while (result == CLO_REPEAT);
+ return result;
+}
+EXPORT_SYMBOL(cl_enqueue_try);
+
+/**
+ * Cancel the conflicting lock found during previous enqueue.
+ *
+ * \retval 0 conflicting lock has been canceled.
+ * \retval -ve error code.
+ */
+int cl_lock_enqueue_wait(const struct lu_env *env,
+ struct cl_lock *lock,
+ int keep_mutex)
+{
+ struct cl_lock *conflict;
+ int rc = 0;
+
+ LASSERT(cl_lock_is_mutexed(lock));
+ LASSERT(lock->cll_state == CLS_QUEUING);
+ LASSERT(lock->cll_conflict != NULL);
+
+ conflict = lock->cll_conflict;
+ lock->cll_conflict = NULL;
+
+ cl_lock_mutex_put(env, lock);
+ LASSERT(cl_lock_nr_mutexed(env) == 0);
+
+ cl_lock_mutex_get(env, conflict);
+ cl_lock_trace(D_DLMTRACE, env, "enqueue wait", conflict);
+ cl_lock_cancel(env, conflict);
+ cl_lock_delete(env, conflict);
+
+ while (conflict->cll_state != CLS_FREEING) {
+ rc = cl_lock_state_wait(env, conflict);
+ if (rc != 0)
+ break;
+ }
+ cl_lock_mutex_put(env, conflict);
+ lu_ref_del(&conflict->cll_reference, "cancel-wait", lock);
+ cl_lock_put(env, conflict);
+
+ if (keep_mutex)
+ cl_lock_mutex_get(env, lock);
+
+ LASSERT(rc <= 0);
+ return rc;
+}
+EXPORT_SYMBOL(cl_lock_enqueue_wait);
+
+static int cl_enqueue_locked(const struct lu_env *env, struct cl_lock *lock,
+ struct cl_io *io, __u32 enqflags)
+{
+ int result;
+
+ LINVRNT(cl_lock_is_mutexed(lock));
+ LINVRNT(cl_lock_invariant(env, lock));
+ LASSERT(lock->cll_holds > 0);
+
+ cl_lock_user_add(env, lock);
+ do {
+ result = cl_enqueue_try(env, lock, io, enqflags);
+ if (result == CLO_WAIT) {
+ if (lock->cll_conflict != NULL)
+ result = cl_lock_enqueue_wait(env, lock, 1);
+ else
+ result = cl_lock_state_wait(env, lock);
+ if (result == 0)
+ continue;
+ }
+ break;
+ } while (1);
+ if (result != 0)
+ cl_unuse_try(env, lock);
+ LASSERT(ergo(result == 0 && !(enqflags & CEF_AGL),
+ lock->cll_state == CLS_ENQUEUED ||
+ lock->cll_state == CLS_HELD));
+ return result;
+}
+
+/**
+ * Enqueues a lock.
+ *
+ * \pre current thread or io owns a hold on lock.
+ *
+ * \post ergo(result == 0, lock->users increased)
+ * \post ergo(result == 0, lock->cll_state == CLS_ENQUEUED ||
+ * lock->cll_state == CLS_HELD)
+ */
+int cl_enqueue(const struct lu_env *env, struct cl_lock *lock,
+ struct cl_io *io, __u32 enqflags)
+{
+ int result;
+
+ cl_lock_lockdep_acquire(env, lock, enqflags);
+ cl_lock_mutex_get(env, lock);
+ result = cl_enqueue_locked(env, lock, io, enqflags);
+ cl_lock_mutex_put(env, lock);
+ if (result != 0)
+ cl_lock_lockdep_release(env, lock);
+ LASSERT(ergo(result == 0, lock->cll_state == CLS_ENQUEUED ||
+ lock->cll_state == CLS_HELD));
+ return result;
+}
+EXPORT_SYMBOL(cl_enqueue);
+
+/**
+ * Tries to unlock a lock.
+ *
+ * This function is called to release underlying resource:
+ * 1. for top lock, the resource is sublocks it held;
+ * 2. for sublock, the resource is the reference to dlmlock.
+ *
+ * cl_unuse_try is a one-shot operation, so it must NOT return CLO_WAIT.
+ *
+ * \see cl_unuse() cl_lock_operations::clo_unuse()
+ * \see cl_lock_state::CLS_CACHED
+ */
+int cl_unuse_try(const struct lu_env *env, struct cl_lock *lock)
+{
+ int result;
+ enum cl_lock_state state = CLS_NEW;
+
+ cl_lock_trace(D_DLMTRACE, env, "unuse lock", lock);
+
+ if (lock->cll_users > 1) {
+ cl_lock_user_del(env, lock);
+ return 0;
+ }
+
+ /* Only if the lock is in CLS_HELD or CLS_ENQUEUED state, it can hold
+ * underlying resources. */
+ if (!(lock->cll_state == CLS_HELD || lock->cll_state == CLS_ENQUEUED)) {
+ cl_lock_user_del(env, lock);
+ return 0;
+ }
+
+ /*
+ * New lock users (->cll_users) are not protecting unlocking
+ * from proceeding. From this point, lock eventually reaches
+ * CLS_CACHED, is reinitialized to CLS_NEW or fails into
+ * CLS_FREEING.
+ */
+ state = cl_lock_intransit(env, lock);
+
+ result = cl_unuse_try_internal(env, lock);
+ LASSERT(lock->cll_state == CLS_INTRANSIT);
+ LASSERT(result != CLO_WAIT);
+ cl_lock_user_del(env, lock);
+ if (result == 0 || result == -ESTALE) {
+ /*
+ * Return lock back to the cache. This is the only
+ * place where lock is moved into CLS_CACHED state.
+ *
+ * If one of ->clo_unuse() methods returned -ESTALE, lock
+ * cannot be placed into cache and has to be
+ * re-initialized. This happens e.g., when a sub-lock was
+ * canceled while unlocking was in progress.
+ */
+ if (state == CLS_HELD && result == 0)
+ state = CLS_CACHED;
+ else
+ state = CLS_NEW;
+ cl_lock_extransit(env, lock, state);
+
+ /*
+ * Hide -ESTALE error.
+ * If the lock is a glimpse lock, and it has multiple
+ * stripes. Assuming that one of its sublock returned -ENAVAIL,
+ * and other sublocks are matched write locks. In this case,
+ * we can't set this lock to error because otherwise some of
+ * its sublocks may not be canceled. This causes some dirty
+ * pages won't be written to OSTs. -jay
+ */
+ result = 0;
+ } else {
+ CERROR("result = %d, this is unlikely!\n", result);
+ state = CLS_NEW;
+ cl_lock_extransit(env, lock, state);
+ }
+ return result ?: lock->cll_error;
+}
+EXPORT_SYMBOL(cl_unuse_try);
+
+static void cl_unuse_locked(const struct lu_env *env, struct cl_lock *lock)
+{
+ int result;
+
+ result = cl_unuse_try(env, lock);
+ if (result)
+ CL_LOCK_DEBUG(D_ERROR, env, lock, "unuse return %d\n", result);
+}
+
+/**
+ * Unlocks a lock.
+ */
+void cl_unuse(const struct lu_env *env, struct cl_lock *lock)
+{
+ cl_lock_mutex_get(env, lock);
+ cl_unuse_locked(env, lock);
+ cl_lock_mutex_put(env, lock);
+ cl_lock_lockdep_release(env, lock);
+}
+EXPORT_SYMBOL(cl_unuse);
+
+/**
+ * Tries to wait for a lock.
+ *
+ * This function is called repeatedly by cl_wait() until either lock is
+ * granted, or error occurs. This function does not block waiting for network
+ * communication to complete.
+ *
+ * \see cl_wait() cl_lock_operations::clo_wait()
+ * \see cl_lock_state::CLS_HELD
+ */
+int cl_wait_try(const struct lu_env *env, struct cl_lock *lock)
+{
+ const struct cl_lock_slice *slice;
+ int result;
+
+ cl_lock_trace(D_DLMTRACE, env, "wait lock try", lock);
+ do {
+ LINVRNT(cl_lock_is_mutexed(lock));
+ LINVRNT(cl_lock_invariant(env, lock));
+ LASSERTF(lock->cll_state == CLS_QUEUING ||
+ lock->cll_state == CLS_ENQUEUED ||
+ lock->cll_state == CLS_HELD ||
+ lock->cll_state == CLS_INTRANSIT,
+ "lock state: %d\n", lock->cll_state);
+ LASSERT(lock->cll_users > 0);
+ LASSERT(lock->cll_holds > 0);
+
+ result = lock->cll_error;
+ if (result != 0)
+ break;
+
+ if (cl_lock_is_intransit(lock)) {
+ result = CLO_WAIT;
+ break;
+ }
+
+ if (lock->cll_state == CLS_HELD)
+ /* nothing to do */
+ break;
+
+ result = -ENOSYS;
+ list_for_each_entry(slice, &lock->cll_layers, cls_linkage) {
+ if (slice->cls_ops->clo_wait != NULL) {
+ result = slice->cls_ops->clo_wait(env, slice);
+ if (result != 0)
+ break;
+ }
+ }
+ LASSERT(result != -ENOSYS);
+ if (result == 0) {
+ LASSERT(lock->cll_state != CLS_INTRANSIT);
+ cl_lock_state_set(env, lock, CLS_HELD);
+ }
+ } while (result == CLO_REPEAT);
+ return result;
+}
+EXPORT_SYMBOL(cl_wait_try);
+
+/**
+ * Waits until enqueued lock is granted.
+ *
+ * \pre current thread or io owns a hold on the lock
+ * \pre ergo(result == 0, lock->cll_state == CLS_ENQUEUED ||
+ * lock->cll_state == CLS_HELD)
+ *
+ * \post ergo(result == 0, lock->cll_state == CLS_HELD)
+ */
+int cl_wait(const struct lu_env *env, struct cl_lock *lock)
+{
+ int result;
+
+ cl_lock_mutex_get(env, lock);
+
+ LINVRNT(cl_lock_invariant(env, lock));
+ LASSERTF(lock->cll_state == CLS_ENQUEUED || lock->cll_state == CLS_HELD,
+ "Wrong state %d \n", lock->cll_state);
+ LASSERT(lock->cll_holds > 0);
+
+ do {
+ result = cl_wait_try(env, lock);
+ if (result == CLO_WAIT) {
+ result = cl_lock_state_wait(env, lock);
+ if (result == 0)
+ continue;
+ }
+ break;
+ } while (1);
+ if (result < 0) {
+ cl_unuse_try(env, lock);
+ cl_lock_lockdep_release(env, lock);
+ }
+ cl_lock_trace(D_DLMTRACE, env, "wait lock", lock);
+ cl_lock_mutex_put(env, lock);
+ LASSERT(ergo(result == 0, lock->cll_state == CLS_HELD));
+ return result;
+}
+EXPORT_SYMBOL(cl_wait);
+
+/**
+ * Executes cl_lock_operations::clo_weigh(), and sums results to estimate lock
+ * value.
+ */
+unsigned long cl_lock_weigh(const struct lu_env *env, struct cl_lock *lock)
+{
+ const struct cl_lock_slice *slice;
+ unsigned long pound;
+ unsigned long ounce;
+
+ LINVRNT(cl_lock_is_mutexed(lock));
+ LINVRNT(cl_lock_invariant(env, lock));
+
+ pound = 0;
+ list_for_each_entry_reverse(slice, &lock->cll_layers, cls_linkage) {
+ if (slice->cls_ops->clo_weigh != NULL) {
+ ounce = slice->cls_ops->clo_weigh(env, slice);
+ pound += ounce;
+ if (pound < ounce) /* over-weight^Wflow */
+ pound = ~0UL;
+ }
+ }
+ return pound;
+}
+EXPORT_SYMBOL(cl_lock_weigh);
+
+/**
+ * Notifies layers that lock description changed.
+ *
+ * The server can grant client a lock different from one that was requested
+ * (e.g., larger in extent). This method is called when actually granted lock
+ * description becomes known to let layers to accommodate for changed lock
+ * description.
+ *
+ * \see cl_lock_operations::clo_modify()
+ */
+int cl_lock_modify(const struct lu_env *env, struct cl_lock *lock,
+ const struct cl_lock_descr *desc)
+{
+ const struct cl_lock_slice *slice;
+ struct cl_object *obj = lock->cll_descr.cld_obj;
+ struct cl_object_header *hdr = cl_object_header(obj);
+ int result;
+
+ cl_lock_trace(D_DLMTRACE, env, "modify lock", lock);
+ /* don't allow object to change */
+ LASSERT(obj == desc->cld_obj);
+ LINVRNT(cl_lock_is_mutexed(lock));
+ LINVRNT(cl_lock_invariant(env, lock));
+
+ list_for_each_entry_reverse(slice, &lock->cll_layers, cls_linkage) {
+ if (slice->cls_ops->clo_modify != NULL) {
+ result = slice->cls_ops->clo_modify(env, slice, desc);
+ if (result != 0)
+ return result;
+ }
+ }
+ CL_LOCK_DEBUG(D_DLMTRACE, env, lock, " -> "DDESCR"@"DFID"\n",
+ PDESCR(desc), PFID(lu_object_fid(&desc->cld_obj->co_lu)));
+ /*
+ * Just replace description in place. Nothing more is needed for
+ * now. If locks were indexed according to their extent and/or mode,
+ * that index would have to be updated here.
+ */
+ spin_lock(&hdr->coh_lock_guard);
+ lock->cll_descr = *desc;
+ spin_unlock(&hdr->coh_lock_guard);
+ return 0;
+}
+EXPORT_SYMBOL(cl_lock_modify);
+
+/**
+ * Initializes lock closure with a given origin.
+ *
+ * \see cl_lock_closure
+ */
+void cl_lock_closure_init(const struct lu_env *env,
+ struct cl_lock_closure *closure,
+ struct cl_lock *origin, int wait)
+{
+ LINVRNT(cl_lock_is_mutexed(origin));
+ LINVRNT(cl_lock_invariant(env, origin));
+
+ INIT_LIST_HEAD(&closure->clc_list);
+ closure->clc_origin = origin;
+ closure->clc_wait = wait;
+ closure->clc_nr = 0;
+}
+EXPORT_SYMBOL(cl_lock_closure_init);
+
+/**
+ * Builds a closure of \a lock.
+ *
+ * Building of a closure consists of adding initial lock (\a lock) into it,
+ * and calling cl_lock_operations::clo_closure() methods of \a lock. These
+ * methods might call cl_lock_closure_build() recursively again, adding more
+ * locks to the closure, etc.
+ *
+ * \see cl_lock_closure
+ */
+int cl_lock_closure_build(const struct lu_env *env, struct cl_lock *lock,
+ struct cl_lock_closure *closure)
+{
+ const struct cl_lock_slice *slice;
+ int result;
+
+ LINVRNT(cl_lock_is_mutexed(closure->clc_origin));
+ LINVRNT(cl_lock_invariant(env, closure->clc_origin));
+
+ result = cl_lock_enclosure(env, lock, closure);
+ if (result == 0) {
+ list_for_each_entry(slice, &lock->cll_layers, cls_linkage) {
+ if (slice->cls_ops->clo_closure != NULL) {
+ result = slice->cls_ops->clo_closure(env, slice,
+ closure);
+ if (result != 0)
+ break;
+ }
+ }
+ }
+ if (result != 0)
+ cl_lock_disclosure(env, closure);
+ return result;
+}
+EXPORT_SYMBOL(cl_lock_closure_build);
+
+/**
+ * Adds new lock to a closure.
+ *
+ * Try-locks \a lock and if succeeded, adds it to the closure (never more than
+ * once). If try-lock failed, returns CLO_REPEAT, after optionally waiting
+ * until next try-lock is likely to succeed.
+ */
+int cl_lock_enclosure(const struct lu_env *env, struct cl_lock *lock,
+ struct cl_lock_closure *closure)
+{
+ int result = 0;
+
+ cl_lock_trace(D_DLMTRACE, env, "enclosure lock", lock);
+ if (!cl_lock_mutex_try(env, lock)) {
+ /*
+ * If lock->cll_inclosure is not empty, lock is already in
+ * this closure.
+ */
+ if (list_empty(&lock->cll_inclosure)) {
+ cl_lock_get_trust(lock);
+ lu_ref_add(&lock->cll_reference, "closure", closure);
+ list_add(&lock->cll_inclosure, &closure->clc_list);
+ closure->clc_nr++;
+ } else
+ cl_lock_mutex_put(env, lock);
+ result = 0;
+ } else {
+ cl_lock_disclosure(env, closure);
+ if (closure->clc_wait) {
+ cl_lock_get_trust(lock);
+ lu_ref_add(&lock->cll_reference, "closure-w", closure);
+ cl_lock_mutex_put(env, closure->clc_origin);
+
+ LASSERT(cl_lock_nr_mutexed(env) == 0);
+ cl_lock_mutex_get(env, lock);
+ cl_lock_mutex_put(env, lock);
+
+ cl_lock_mutex_get(env, closure->clc_origin);
+ lu_ref_del(&lock->cll_reference, "closure-w", closure);
+ cl_lock_put(env, lock);
+ }
+ result = CLO_REPEAT;
+ }
+ return result;
+}
+EXPORT_SYMBOL(cl_lock_enclosure);
+
+/** Releases mutices of enclosed locks. */
+void cl_lock_disclosure(const struct lu_env *env,
+ struct cl_lock_closure *closure)
+{
+ struct cl_lock *scan;
+ struct cl_lock *temp;
+
+ cl_lock_trace(D_DLMTRACE, env, "disclosure lock", closure->clc_origin);
+ list_for_each_entry_safe(scan, temp, &closure->clc_list,
+ cll_inclosure){
+ list_del_init(&scan->cll_inclosure);
+ cl_lock_mutex_put(env, scan);
+ lu_ref_del(&scan->cll_reference, "closure", closure);
+ cl_lock_put(env, scan);
+ closure->clc_nr--;
+ }
+ LASSERT(closure->clc_nr == 0);
+}
+EXPORT_SYMBOL(cl_lock_disclosure);
+
+/** Finalizes a closure. */
+void cl_lock_closure_fini(struct cl_lock_closure *closure)
+{
+ LASSERT(closure->clc_nr == 0);
+ LASSERT(list_empty(&closure->clc_list));
+}
+EXPORT_SYMBOL(cl_lock_closure_fini);
+
+/**
+ * Destroys this lock. Notifies layers (bottom-to-top) that lock is being
+ * destroyed, then destroy the lock. If there are holds on the lock, postpone
+ * destruction until all holds are released. This is called when a decision is
+ * made to destroy the lock in the future. E.g., when a blocking AST is
+ * received on it, or fatal communication error happens.
+ *
+ * Caller must have a reference on this lock to prevent a situation, when
+ * deleted lock lingers in memory for indefinite time, because nobody calls
+ * cl_lock_put() to finish it.
+ *
+ * \pre atomic_read(&lock->cll_ref) > 0
+ * \pre ergo(cl_lock_nesting(lock) == CNL_TOP,
+ * cl_lock_nr_mutexed(env) == 1)
+ * [i.e., if a top-lock is deleted, mutices of no other locks can be
+ * held, as deletion of sub-locks might require releasing a top-lock
+ * mutex]
+ *
+ * \see cl_lock_operations::clo_delete()
+ * \see cl_lock::cll_holds
+ */
+void cl_lock_delete(const struct lu_env *env, struct cl_lock *lock)
+{
+ LINVRNT(cl_lock_is_mutexed(lock));
+ LINVRNT(cl_lock_invariant(env, lock));
+ LASSERT(ergo(cl_lock_nesting(lock) == CNL_TOP,
+ cl_lock_nr_mutexed(env) == 1));
+
+ cl_lock_trace(D_DLMTRACE, env, "delete lock", lock);
+ if (lock->cll_holds == 0)
+ cl_lock_delete0(env, lock);
+ else
+ lock->cll_flags |= CLF_DOOMED;
+}
+EXPORT_SYMBOL(cl_lock_delete);
+
+/**
+ * Mark lock as irrecoverably failed, and mark it for destruction. This
+ * happens when, e.g., server fails to grant a lock to us, or networking
+ * time-out happens.
+ *
+ * \pre atomic_read(&lock->cll_ref) > 0
+ *
+ * \see clo_lock_delete()
+ * \see cl_lock::cll_holds
+ */
+void cl_lock_error(const struct lu_env *env, struct cl_lock *lock, int error)
+{
+ LINVRNT(cl_lock_is_mutexed(lock));
+ LINVRNT(cl_lock_invariant(env, lock));
+
+ if (lock->cll_error == 0 && error != 0) {
+ cl_lock_trace(D_DLMTRACE, env, "set lock error", lock);
+ lock->cll_error = error;
+ cl_lock_signal(env, lock);
+ cl_lock_cancel(env, lock);
+ cl_lock_delete(env, lock);
+ }
+}
+EXPORT_SYMBOL(cl_lock_error);
+
+/**
+ * Cancels this lock. Notifies layers
+ * (bottom-to-top) that lock is being cancelled, then destroy the lock. If
+ * there are holds on the lock, postpone cancellation until
+ * all holds are released.
+ *
+ * Cancellation notification is delivered to layers at most once.
+ *
+ * \see cl_lock_operations::clo_cancel()
+ * \see cl_lock::cll_holds
+ */
+void cl_lock_cancel(const struct lu_env *env, struct cl_lock *lock)
+{
+ LINVRNT(cl_lock_is_mutexed(lock));
+ LINVRNT(cl_lock_invariant(env, lock));
+
+ cl_lock_trace(D_DLMTRACE, env, "cancel lock", lock);
+ if (lock->cll_holds == 0)
+ cl_lock_cancel0(env, lock);
+ else
+ lock->cll_flags |= CLF_CANCELPEND;
+}
+EXPORT_SYMBOL(cl_lock_cancel);
+
+/**
+ * Finds an existing lock covering given index and optionally different from a
+ * given \a except lock.
+ */
+struct cl_lock *cl_lock_at_pgoff(const struct lu_env *env,
+ struct cl_object *obj, pgoff_t index,
+ struct cl_lock *except,
+ int pending, int canceld)
+{
+ struct cl_object_header *head;
+ struct cl_lock *scan;
+ struct cl_lock *lock;
+ struct cl_lock_descr *need;
+
+ head = cl_object_header(obj);
+ need = &cl_env_info(env)->clt_descr;
+ lock = NULL;
+
+ need->cld_mode = CLM_READ; /* CLM_READ matches both READ & WRITE, but
+ * not PHANTOM */
+ need->cld_start = need->cld_end = index;
+ need->cld_enq_flags = 0;
+
+ spin_lock(&head->coh_lock_guard);
+ /* It is fine to match any group lock since there could be only one
+ * with a uniq gid and it conflicts with all other lock modes too */
+ list_for_each_entry(scan, &head->coh_locks, cll_linkage) {
+ if (scan != except &&
+ (scan->cll_descr.cld_mode == CLM_GROUP ||
+ cl_lock_ext_match(&scan->cll_descr, need)) &&
+ scan->cll_state >= CLS_HELD &&
+ scan->cll_state < CLS_FREEING &&
+ /*
+ * This check is racy as the lock can be canceled right
+ * after it is done, but this is fine, because page exists
+ * already.
+ */
+ (canceld || !(scan->cll_flags & CLF_CANCELLED)) &&
+ (pending || !(scan->cll_flags & CLF_CANCELPEND))) {
+ /* Don't increase cs_hit here since this
+ * is just a helper function. */
+ cl_lock_get_trust(scan);
+ lock = scan;
+ break;
+ }
+ }
+ spin_unlock(&head->coh_lock_guard);
+ return lock;
+}
+EXPORT_SYMBOL(cl_lock_at_pgoff);
+
+/**
+ * Calculate the page offset at the layer of @lock.
+ * At the time of this writing, @page is top page and @lock is sub lock.
+ */
+static pgoff_t pgoff_at_lock(struct cl_page *page, struct cl_lock *lock)
+{
+ struct lu_device_type *dtype;
+ const struct cl_page_slice *slice;
+
+ dtype = lock->cll_descr.cld_obj->co_lu.lo_dev->ld_type;
+ slice = cl_page_at(page, dtype);
+ LASSERT(slice != NULL);
+ return slice->cpl_page->cp_index;
+}
+
+/**
+ * Check if page @page is covered by an extra lock or discard it.
+ */
+static int check_and_discard_cb(const struct lu_env *env, struct cl_io *io,
+ struct cl_page *page, void *cbdata)
+{
+ struct cl_thread_info *info = cl_env_info(env);
+ struct cl_lock *lock = cbdata;
+ pgoff_t index = pgoff_at_lock(page, lock);
+
+ if (index >= info->clt_fn_index) {
+ struct cl_lock *tmp;
+
+ /* refresh non-overlapped index */
+ tmp = cl_lock_at_pgoff(env, lock->cll_descr.cld_obj, index,
+ lock, 1, 0);
+ if (tmp != NULL) {
+ /* Cache the first-non-overlapped index so as to skip
+ * all pages within [index, clt_fn_index). This
+ * is safe because if tmp lock is canceled, it will
+ * discard these pages. */
+ info->clt_fn_index = tmp->cll_descr.cld_end + 1;
+ if (tmp->cll_descr.cld_end == CL_PAGE_EOF)
+ info->clt_fn_index = CL_PAGE_EOF;
+ cl_lock_put(env, tmp);
+ } else if (cl_page_own(env, io, page) == 0) {
+ /* discard the page */
+ cl_page_unmap(env, io, page);
+ cl_page_discard(env, io, page);
+ cl_page_disown(env, io, page);
+ } else {
+ LASSERT(page->cp_state == CPS_FREEING);
+ }
+ }
+
+ info->clt_next_index = index + 1;
+ return CLP_GANG_OKAY;
+}
+
+static int discard_cb(const struct lu_env *env, struct cl_io *io,
+ struct cl_page *page, void *cbdata)
+{
+ struct cl_thread_info *info = cl_env_info(env);
+ struct cl_lock *lock = cbdata;
+
+ LASSERT(lock->cll_descr.cld_mode >= CLM_WRITE);
+ KLASSERT(ergo(page->cp_type == CPT_CACHEABLE,
+ !PageWriteback(cl_page_vmpage(env, page))));
+ KLASSERT(ergo(page->cp_type == CPT_CACHEABLE,
+ !PageDirty(cl_page_vmpage(env, page))));
+
+ info->clt_next_index = pgoff_at_lock(page, lock) + 1;
+ if (cl_page_own(env, io, page) == 0) {
+ /* discard the page */
+ cl_page_unmap(env, io, page);
+ cl_page_discard(env, io, page);
+ cl_page_disown(env, io, page);
+ } else {
+ LASSERT(page->cp_state == CPS_FREEING);
+ }
+
+ return CLP_GANG_OKAY;
+}
+
+/**
+ * Discard pages protected by the given lock. This function traverses radix
+ * tree to find all covering pages and discard them. If a page is being covered
+ * by other locks, it should remain in cache.
+ *
+ * If error happens on any step, the process continues anyway (the reasoning
+ * behind this being that lock cancellation cannot be delayed indefinitely).
+ */
+int cl_lock_discard_pages(const struct lu_env *env, struct cl_lock *lock)
+{
+ struct cl_thread_info *info = cl_env_info(env);
+ struct cl_io *io = &info->clt_io;
+ struct cl_lock_descr *descr = &lock->cll_descr;
+ cl_page_gang_cb_t cb;
+ int res;
+ int result;
+
+ LINVRNT(cl_lock_invariant(env, lock));
+
+ io->ci_obj = cl_object_top(descr->cld_obj);
+ io->ci_ignore_layout = 1;
+ result = cl_io_init(env, io, CIT_MISC, io->ci_obj);
+ if (result != 0)
+ goto out;
+
+ cb = descr->cld_mode == CLM_READ ? check_and_discard_cb : discard_cb;
+ info->clt_fn_index = info->clt_next_index = descr->cld_start;
+ do {
+ res = cl_page_gang_lookup(env, descr->cld_obj, io,
+ info->clt_next_index, descr->cld_end,
+ cb, (void *)lock);
+ if (info->clt_next_index > descr->cld_end)
+ break;
+
+ if (res == CLP_GANG_RESCHED)
+ cond_resched();
+ } while (res != CLP_GANG_OKAY);
+out:
+ cl_io_fini(env, io);
+ return result;
+}
+EXPORT_SYMBOL(cl_lock_discard_pages);
+
+/**
+ * Eliminate all locks for a given object.
+ *
+ * Caller has to guarantee that no lock is in active use.
+ *
+ * \param cancel when this is set, cl_locks_prune() cancels locks before
+ * destroying.
+ */
+void cl_locks_prune(const struct lu_env *env, struct cl_object *obj, int cancel)
+{
+ struct cl_object_header *head;
+ struct cl_lock *lock;
+
+ head = cl_object_header(obj);
+ /*
+ * If locks are destroyed without cancellation, all pages must be
+ * already destroyed (as otherwise they will be left unprotected).
+ */
+ LASSERT(ergo(!cancel,
+ head->coh_tree.rnode == NULL && head->coh_pages == 0));
+
+ spin_lock(&head->coh_lock_guard);
+ while (!list_empty(&head->coh_locks)) {
+ lock = container_of(head->coh_locks.next,
+ struct cl_lock, cll_linkage);
+ cl_lock_get_trust(lock);
+ spin_unlock(&head->coh_lock_guard);
+ lu_ref_add(&lock->cll_reference, "prune", current);
+
+again:
+ cl_lock_mutex_get(env, lock);
+ if (lock->cll_state < CLS_FREEING) {
+ LASSERT(lock->cll_users <= 1);
+ if (unlikely(lock->cll_users == 1)) {
+ struct l_wait_info lwi = { 0 };
+
+ cl_lock_mutex_put(env, lock);
+ l_wait_event(lock->cll_wq,
+ lock->cll_users == 0,
+ &lwi);
+ goto again;
+ }
+
+ if (cancel)
+ cl_lock_cancel(env, lock);
+ cl_lock_delete(env, lock);
+ }
+ cl_lock_mutex_put(env, lock);
+ lu_ref_del(&lock->cll_reference, "prune", current);
+ cl_lock_put(env, lock);
+ spin_lock(&head->coh_lock_guard);
+ }
+ spin_unlock(&head->coh_lock_guard);
+}
+EXPORT_SYMBOL(cl_locks_prune);
+
+static struct cl_lock *cl_lock_hold_mutex(const struct lu_env *env,
+ const struct cl_io *io,
+ const struct cl_lock_descr *need,
+ const char *scope, const void *source)
+{
+ struct cl_lock *lock;
+
+ while (1) {
+ lock = cl_lock_find(env, io, need);
+ if (IS_ERR(lock))
+ break;
+ cl_lock_mutex_get(env, lock);
+ if (lock->cll_state < CLS_FREEING &&
+ !(lock->cll_flags & CLF_CANCELLED)) {
+ cl_lock_hold_mod(env, lock, +1);
+ lu_ref_add(&lock->cll_holders, scope, source);
+ lu_ref_add(&lock->cll_reference, scope, source);
+ break;
+ }
+ cl_lock_mutex_put(env, lock);
+ cl_lock_put(env, lock);
+ }
+ return lock;
+}
+
+/**
+ * Returns a lock matching \a need description with a reference and a hold on
+ * it.
+ *
+ * This is much like cl_lock_find(), except that cl_lock_hold() additionally
+ * guarantees that lock is not in the CLS_FREEING state on return.
+ */
+struct cl_lock *cl_lock_hold(const struct lu_env *env, const struct cl_io *io,
+ const struct cl_lock_descr *need,
+ const char *scope, const void *source)
+{
+ struct cl_lock *lock;
+
+ lock = cl_lock_hold_mutex(env, io, need, scope, source);
+ if (!IS_ERR(lock))
+ cl_lock_mutex_put(env, lock);
+ return lock;
+}
+EXPORT_SYMBOL(cl_lock_hold);
+
+/**
+ * Main high-level entry point of cl_lock interface that finds existing or
+ * enqueues new lock matching given description.
+ */
+struct cl_lock *cl_lock_request(const struct lu_env *env, struct cl_io *io,
+ const struct cl_lock_descr *need,
+ const char *scope, const void *source)
+{
+ struct cl_lock *lock;
+ int rc;
+ __u32 enqflags = need->cld_enq_flags;
+
+ do {
+ lock = cl_lock_hold_mutex(env, io, need, scope, source);
+ if (IS_ERR(lock))
+ break;
+
+ rc = cl_enqueue_locked(env, lock, io, enqflags);
+ if (rc == 0) {
+ if (cl_lock_fits_into(env, lock, need, io)) {
+ if (!(enqflags & CEF_AGL)) {
+ cl_lock_mutex_put(env, lock);
+ cl_lock_lockdep_acquire(env, lock,
+ enqflags);
+ break;
+ }
+ rc = 1;
+ }
+ cl_unuse_locked(env, lock);
+ }
+ cl_lock_trace(D_DLMTRACE, env,
+ rc <= 0 ? "enqueue failed" : "agl succeed", lock);
+ cl_lock_hold_release(env, lock, scope, source);
+ cl_lock_mutex_put(env, lock);
+ lu_ref_del(&lock->cll_reference, scope, source);
+ cl_lock_put(env, lock);
+ if (rc > 0) {
+ LASSERT(enqflags & CEF_AGL);
+ lock = NULL;
+ } else if (rc != 0) {
+ lock = ERR_PTR(rc);
+ }
+ } while (rc == 0);
+ return lock;
+}
+EXPORT_SYMBOL(cl_lock_request);
+
+/**
+ * Adds a hold to a known lock.
+ */
+void cl_lock_hold_add(const struct lu_env *env, struct cl_lock *lock,
+ const char *scope, const void *source)
+{
+ LINVRNT(cl_lock_is_mutexed(lock));
+ LINVRNT(cl_lock_invariant(env, lock));
+ LASSERT(lock->cll_state != CLS_FREEING);
+
+ cl_lock_hold_mod(env, lock, +1);
+ cl_lock_get(lock);
+ lu_ref_add(&lock->cll_holders, scope, source);
+ lu_ref_add(&lock->cll_reference, scope, source);
+}
+EXPORT_SYMBOL(cl_lock_hold_add);
+
+/**
+ * Releases a hold and a reference on a lock, on which caller acquired a
+ * mutex.
+ */
+void cl_lock_unhold(const struct lu_env *env, struct cl_lock *lock,
+ const char *scope, const void *source)
+{
+ LINVRNT(cl_lock_invariant(env, lock));
+ cl_lock_hold_release(env, lock, scope, source);
+ lu_ref_del(&lock->cll_reference, scope, source);
+ cl_lock_put(env, lock);
+}
+EXPORT_SYMBOL(cl_lock_unhold);
+
+/**
+ * Releases a hold and a reference on a lock, obtained by cl_lock_hold().
+ */
+void cl_lock_release(const struct lu_env *env, struct cl_lock *lock,
+ const char *scope, const void *source)
+{
+ LINVRNT(cl_lock_invariant(env, lock));
+ cl_lock_trace(D_DLMTRACE, env, "release lock", lock);
+ cl_lock_mutex_get(env, lock);
+ cl_lock_hold_release(env, lock, scope, source);
+ cl_lock_mutex_put(env, lock);
+ lu_ref_del(&lock->cll_reference, scope, source);
+ cl_lock_put(env, lock);
+}
+EXPORT_SYMBOL(cl_lock_release);
+
+void cl_lock_user_add(const struct lu_env *env, struct cl_lock *lock)
+{
+ LINVRNT(cl_lock_is_mutexed(lock));
+ LINVRNT(cl_lock_invariant(env, lock));
+
+ cl_lock_used_mod(env, lock, +1);
+}
+EXPORT_SYMBOL(cl_lock_user_add);
+
+void cl_lock_user_del(const struct lu_env *env, struct cl_lock *lock)
+{
+ LINVRNT(cl_lock_is_mutexed(lock));
+ LINVRNT(cl_lock_invariant(env, lock));
+ LASSERT(lock->cll_users > 0);
+
+ cl_lock_used_mod(env, lock, -1);
+ if (lock->cll_users == 0)
+ wake_up_all(&lock->cll_wq);
+}
+EXPORT_SYMBOL(cl_lock_user_del);
+
+const char *cl_lock_mode_name(const enum cl_lock_mode mode)
+{
+ static const char *names[] = {
+ [CLM_PHANTOM] = "P",
+ [CLM_READ] = "R",
+ [CLM_WRITE] = "W",
+ [CLM_GROUP] = "G"
+ };
+ if (0 <= mode && mode < ARRAY_SIZE(names))
+ return names[mode];
+ else
+ return "U";
+}
+EXPORT_SYMBOL(cl_lock_mode_name);
+
+/**
+ * Prints human readable representation of a lock description.
+ */
+void cl_lock_descr_print(const struct lu_env *env, void *cookie,
+ lu_printer_t printer,
+ const struct cl_lock_descr *descr)
+{
+ const struct lu_fid *fid;
+
+ fid = lu_object_fid(&descr->cld_obj->co_lu);
+ (*printer)(env, cookie, DDESCR"@"DFID, PDESCR(descr), PFID(fid));
+}
+EXPORT_SYMBOL(cl_lock_descr_print);
+
+/**
+ * Prints human readable representation of \a lock to the \a f.
+ */
+void cl_lock_print(const struct lu_env *env, void *cookie,
+ lu_printer_t printer, const struct cl_lock *lock)
+{
+ const struct cl_lock_slice *slice;
+ (*printer)(env, cookie, "lock@%p[%d %d %d %d %d %08lx] ",
+ lock, atomic_read(&lock->cll_ref),
+ lock->cll_state, lock->cll_error, lock->cll_holds,
+ lock->cll_users, lock->cll_flags);
+ cl_lock_descr_print(env, cookie, printer, &lock->cll_descr);
+ (*printer)(env, cookie, " {\n");
+
+ list_for_each_entry(slice, &lock->cll_layers, cls_linkage) {
+ (*printer)(env, cookie, " %s@%p: ",
+ slice->cls_obj->co_lu.lo_dev->ld_type->ldt_name,
+ slice);
+ if (slice->cls_ops->clo_print != NULL)
+ slice->cls_ops->clo_print(env, cookie, printer, slice);
+ (*printer)(env, cookie, "\n");
+ }
+ (*printer)(env, cookie, "} lock@%p\n", lock);
+}
+EXPORT_SYMBOL(cl_lock_print);
+
+int cl_lock_init(void)
+{
+ return lu_kmem_init(cl_lock_caches);
+}
+
+void cl_lock_fini(void)
+{
+ lu_kmem_fini(cl_lock_caches);
+}
diff --git a/drivers/staging/lustre/lustre/obdclass/cl_object.c b/drivers/staging/lustre/lustre/obdclass/cl_object.c
new file mode 100644
index 000000000..f13d1fbff
--- /dev/null
+++ b/drivers/staging/lustre/lustre/obdclass/cl_object.c
@@ -0,0 +1,1139 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2011, 2012, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ *
+ * Client Lustre Object.
+ *
+ * Author: Nikita Danilov <nikita.danilov@sun.com>
+ */
+
+/*
+ * Locking.
+ *
+ * i_mutex
+ * PG_locked
+ * ->coh_page_guard
+ * ->coh_lock_guard
+ * ->coh_attr_guard
+ * ->ls_guard
+ */
+
+#define DEBUG_SUBSYSTEM S_CLASS
+
+#include "../../include/linux/libcfs/libcfs.h"
+/* class_put_type() */
+#include "../include/obd_class.h"
+#include "../include/obd_support.h"
+#include "../include/lustre_fid.h"
+#include <linux/list.h>
+#include "../../include/linux/libcfs/libcfs_hash.h" /* for cfs_hash stuff */
+#include "../include/cl_object.h"
+#include "cl_internal.h"
+
+static struct kmem_cache *cl_env_kmem;
+
+/** Lock class of cl_object_header::coh_page_guard */
+static struct lock_class_key cl_page_guard_class;
+/** Lock class of cl_object_header::coh_lock_guard */
+static struct lock_class_key cl_lock_guard_class;
+/** Lock class of cl_object_header::coh_attr_guard */
+static struct lock_class_key cl_attr_guard_class;
+
+extern __u32 lu_context_tags_default;
+extern __u32 lu_session_tags_default;
+/**
+ * Initialize cl_object_header.
+ */
+int cl_object_header_init(struct cl_object_header *h)
+{
+ int result;
+
+ result = lu_object_header_init(&h->coh_lu);
+ if (result == 0) {
+ spin_lock_init(&h->coh_page_guard);
+ spin_lock_init(&h->coh_lock_guard);
+ spin_lock_init(&h->coh_attr_guard);
+ lockdep_set_class(&h->coh_page_guard, &cl_page_guard_class);
+ lockdep_set_class(&h->coh_lock_guard, &cl_lock_guard_class);
+ lockdep_set_class(&h->coh_attr_guard, &cl_attr_guard_class);
+ h->coh_pages = 0;
+ /* XXX hard coded GFP_* mask. */
+ INIT_RADIX_TREE(&h->coh_tree, GFP_ATOMIC);
+ INIT_LIST_HEAD(&h->coh_locks);
+ h->coh_page_bufsize = ALIGN(sizeof(struct cl_page), 8);
+ }
+ return result;
+}
+EXPORT_SYMBOL(cl_object_header_init);
+
+/**
+ * Finalize cl_object_header.
+ */
+void cl_object_header_fini(struct cl_object_header *h)
+{
+ LASSERT(list_empty(&h->coh_locks));
+ lu_object_header_fini(&h->coh_lu);
+}
+EXPORT_SYMBOL(cl_object_header_fini);
+
+/**
+ * Returns a cl_object with a given \a fid.
+ *
+ * Returns either cached or newly created object. Additional reference on the
+ * returned object is acquired.
+ *
+ * \see lu_object_find(), cl_page_find(), cl_lock_find()
+ */
+struct cl_object *cl_object_find(const struct lu_env *env,
+ struct cl_device *cd, const struct lu_fid *fid,
+ const struct cl_object_conf *c)
+{
+ might_sleep();
+ return lu2cl(lu_object_find_slice(env, cl2lu_dev(cd), fid, &c->coc_lu));
+}
+EXPORT_SYMBOL(cl_object_find);
+
+/**
+ * Releases a reference on \a o.
+ *
+ * When last reference is released object is returned to the cache, unless
+ * lu_object_header_flags::LU_OBJECT_HEARD_BANSHEE bit is set in its header.
+ *
+ * \see cl_page_put(), cl_lock_put().
+ */
+void cl_object_put(const struct lu_env *env, struct cl_object *o)
+{
+ lu_object_put(env, &o->co_lu);
+}
+EXPORT_SYMBOL(cl_object_put);
+
+/**
+ * Acquire an additional reference to the object \a o.
+ *
+ * This can only be used to acquire _additional_ reference, i.e., caller
+ * already has to possess at least one reference to \a o before calling this.
+ *
+ * \see cl_page_get(), cl_lock_get().
+ */
+void cl_object_get(struct cl_object *o)
+{
+ lu_object_get(&o->co_lu);
+}
+EXPORT_SYMBOL(cl_object_get);
+
+/**
+ * Returns the top-object for a given \a o.
+ *
+ * \see cl_page_top(), cl_io_top()
+ */
+struct cl_object *cl_object_top(struct cl_object *o)
+{
+ struct cl_object_header *hdr = cl_object_header(o);
+ struct cl_object *top;
+
+ while (hdr->coh_parent != NULL)
+ hdr = hdr->coh_parent;
+
+ top = lu2cl(lu_object_top(&hdr->coh_lu));
+ CDEBUG(D_TRACE, "%p -> %p\n", o, top);
+ return top;
+}
+EXPORT_SYMBOL(cl_object_top);
+
+/**
+ * Returns pointer to the lock protecting data-attributes for the given object
+ * \a o.
+ *
+ * Data-attributes are protected by the cl_object_header::coh_attr_guard
+ * spin-lock in the top-object.
+ *
+ * \see cl_attr, cl_object_attr_lock(), cl_object_operations::coo_attr_get().
+ */
+static spinlock_t *cl_object_attr_guard(struct cl_object *o)
+{
+ return &cl_object_header(cl_object_top(o))->coh_attr_guard;
+}
+
+/**
+ * Locks data-attributes.
+ *
+ * Prevents data-attributes from changing, until lock is released by
+ * cl_object_attr_unlock(). This has to be called before calls to
+ * cl_object_attr_get(), cl_object_attr_set().
+ */
+void cl_object_attr_lock(struct cl_object *o)
+ __acquires(cl_object_attr_guard(o))
+{
+ spin_lock(cl_object_attr_guard(o));
+}
+EXPORT_SYMBOL(cl_object_attr_lock);
+
+/**
+ * Releases data-attributes lock, acquired by cl_object_attr_lock().
+ */
+void cl_object_attr_unlock(struct cl_object *o)
+ __releases(cl_object_attr_guard(o))
+{
+ spin_unlock(cl_object_attr_guard(o));
+}
+EXPORT_SYMBOL(cl_object_attr_unlock);
+
+/**
+ * Returns data-attributes of an object \a obj.
+ *
+ * Every layer is asked (by calling cl_object_operations::coo_attr_get())
+ * top-to-bottom to fill in parts of \a attr that this layer is responsible
+ * for.
+ */
+int cl_object_attr_get(const struct lu_env *env, struct cl_object *obj,
+ struct cl_attr *attr)
+{
+ struct lu_object_header *top;
+ int result;
+
+ assert_spin_locked(cl_object_attr_guard(obj));
+
+ top = obj->co_lu.lo_header;
+ result = 0;
+ list_for_each_entry(obj, &top->loh_layers, co_lu.lo_linkage) {
+ if (obj->co_ops->coo_attr_get != NULL) {
+ result = obj->co_ops->coo_attr_get(env, obj, attr);
+ if (result != 0) {
+ if (result > 0)
+ result = 0;
+ break;
+ }
+ }
+ }
+ return result;
+}
+EXPORT_SYMBOL(cl_object_attr_get);
+
+/**
+ * Updates data-attributes of an object \a obj.
+ *
+ * Only attributes, mentioned in a validness bit-mask \a v are
+ * updated. Calls cl_object_operations::coo_attr_set() on every layer, bottom
+ * to top.
+ */
+int cl_object_attr_set(const struct lu_env *env, struct cl_object *obj,
+ const struct cl_attr *attr, unsigned v)
+{
+ struct lu_object_header *top;
+ int result;
+
+ assert_spin_locked(cl_object_attr_guard(obj));
+
+ top = obj->co_lu.lo_header;
+ result = 0;
+ list_for_each_entry_reverse(obj, &top->loh_layers,
+ co_lu.lo_linkage) {
+ if (obj->co_ops->coo_attr_set != NULL) {
+ result = obj->co_ops->coo_attr_set(env, obj, attr, v);
+ if (result != 0) {
+ if (result > 0)
+ result = 0;
+ break;
+ }
+ }
+ }
+ return result;
+}
+EXPORT_SYMBOL(cl_object_attr_set);
+
+/**
+ * Notifies layers (bottom-to-top) that glimpse AST was received.
+ *
+ * Layers have to fill \a lvb fields with information that will be shipped
+ * back to glimpse issuer.
+ *
+ * \see cl_lock_operations::clo_glimpse()
+ */
+int cl_object_glimpse(const struct lu_env *env, struct cl_object *obj,
+ struct ost_lvb *lvb)
+{
+ struct lu_object_header *top;
+ int result;
+
+ top = obj->co_lu.lo_header;
+ result = 0;
+ list_for_each_entry_reverse(obj, &top->loh_layers,
+ co_lu.lo_linkage) {
+ if (obj->co_ops->coo_glimpse != NULL) {
+ result = obj->co_ops->coo_glimpse(env, obj, lvb);
+ if (result != 0)
+ break;
+ }
+ }
+ LU_OBJECT_HEADER(D_DLMTRACE, env, lu_object_top(top),
+ "size: %llu mtime: %llu atime: %llu ctime: %llu blocks: %llu\n",
+ lvb->lvb_size, lvb->lvb_mtime, lvb->lvb_atime,
+ lvb->lvb_ctime, lvb->lvb_blocks);
+ return result;
+}
+EXPORT_SYMBOL(cl_object_glimpse);
+
+/**
+ * Updates a configuration of an object \a obj.
+ */
+int cl_conf_set(const struct lu_env *env, struct cl_object *obj,
+ const struct cl_object_conf *conf)
+{
+ struct lu_object_header *top;
+ int result;
+
+ top = obj->co_lu.lo_header;
+ result = 0;
+ list_for_each_entry(obj, &top->loh_layers, co_lu.lo_linkage) {
+ if (obj->co_ops->coo_conf_set != NULL) {
+ result = obj->co_ops->coo_conf_set(env, obj, conf);
+ if (result != 0)
+ break;
+ }
+ }
+ return result;
+}
+EXPORT_SYMBOL(cl_conf_set);
+
+/**
+ * Helper function removing all object locks, and marking object for
+ * deletion. All object pages must have been deleted at this point.
+ *
+ * This is called by cl_inode_fini() and lov_object_delete() to destroy top-
+ * and sub- objects respectively.
+ */
+void cl_object_kill(const struct lu_env *env, struct cl_object *obj)
+{
+ struct cl_object_header *hdr;
+
+ hdr = cl_object_header(obj);
+ LASSERT(hdr->coh_tree.rnode == NULL);
+ LASSERT(hdr->coh_pages == 0);
+
+ set_bit(LU_OBJECT_HEARD_BANSHEE, &hdr->coh_lu.loh_flags);
+ /*
+ * Destroy all locks. Object destruction (including cl_inode_fini())
+ * cannot cancel the locks, because in the case of a local client,
+ * where client and server share the same thread running
+ * prune_icache(), this can dead-lock with ldlm_cancel_handler()
+ * waiting on __wait_on_freeing_inode().
+ */
+ cl_locks_prune(env, obj, 0);
+}
+EXPORT_SYMBOL(cl_object_kill);
+
+/**
+ * Prunes caches of pages and locks for this object.
+ */
+void cl_object_prune(const struct lu_env *env, struct cl_object *obj)
+{
+ cl_pages_prune(env, obj);
+ cl_locks_prune(env, obj, 1);
+}
+EXPORT_SYMBOL(cl_object_prune);
+
+/**
+ * Check if the object has locks.
+ */
+int cl_object_has_locks(struct cl_object *obj)
+{
+ struct cl_object_header *head = cl_object_header(obj);
+ int has;
+
+ spin_lock(&head->coh_lock_guard);
+ has = list_empty(&head->coh_locks);
+ spin_unlock(&head->coh_lock_guard);
+
+ return (has == 0);
+}
+EXPORT_SYMBOL(cl_object_has_locks);
+
+void cache_stats_init(struct cache_stats *cs, const char *name)
+{
+ int i;
+
+ cs->cs_name = name;
+ for (i = 0; i < CS_NR; i++)
+ atomic_set(&cs->cs_stats[i], 0);
+}
+
+int cache_stats_print(const struct cache_stats *cs, struct seq_file *m, int h)
+{
+ int i;
+ /*
+ * lookup hit total cached create
+ * env: ...... ...... ...... ...... ......
+ */
+ if (h) {
+ const char *names[CS_NR] = CS_NAMES;
+
+ seq_printf(m, "%6s", " ");
+ for (i = 0; i < CS_NR; i++)
+ seq_printf(m, "%8s", names[i]);
+ seq_printf(m, "\n");
+ }
+
+ seq_printf(m, "%5.5s:", cs->cs_name);
+ for (i = 0; i < CS_NR; i++)
+ seq_printf(m, "%8u", atomic_read(&cs->cs_stats[i]));
+ return 0;
+}
+
+/**
+ * Initialize client site.
+ *
+ * Perform common initialization (lu_site_init()), and initialize statistical
+ * counters. Also perform global initializations on the first call.
+ */
+int cl_site_init(struct cl_site *s, struct cl_device *d)
+{
+ int i;
+ int result;
+
+ result = lu_site_init(&s->cs_lu, &d->cd_lu_dev);
+ if (result == 0) {
+ cache_stats_init(&s->cs_pages, "pages");
+ cache_stats_init(&s->cs_locks, "locks");
+ for (i = 0; i < ARRAY_SIZE(s->cs_pages_state); ++i)
+ atomic_set(&s->cs_pages_state[0], 0);
+ for (i = 0; i < ARRAY_SIZE(s->cs_locks_state); ++i)
+ atomic_set(&s->cs_locks_state[i], 0);
+ }
+ return result;
+}
+EXPORT_SYMBOL(cl_site_init);
+
+/**
+ * Finalize client site. Dual to cl_site_init().
+ */
+void cl_site_fini(struct cl_site *s)
+{
+ lu_site_fini(&s->cs_lu);
+}
+EXPORT_SYMBOL(cl_site_fini);
+
+static struct cache_stats cl_env_stats = {
+ .cs_name = "envs",
+ .cs_stats = { ATOMIC_INIT(0), }
+};
+
+/**
+ * Outputs client site statistical counters into a buffer. Suitable for
+ * ll_rd_*()-style functions.
+ */
+int cl_site_stats_print(const struct cl_site *site, struct seq_file *m)
+{
+ int i;
+ static const char *pstate[] = {
+ [CPS_CACHED] = "c",
+ [CPS_OWNED] = "o",
+ [CPS_PAGEOUT] = "w",
+ [CPS_PAGEIN] = "r",
+ [CPS_FREEING] = "f"
+ };
+ static const char *lstate[] = {
+ [CLS_NEW] = "n",
+ [CLS_QUEUING] = "q",
+ [CLS_ENQUEUED] = "e",
+ [CLS_HELD] = "h",
+ [CLS_INTRANSIT] = "t",
+ [CLS_CACHED] = "c",
+ [CLS_FREEING] = "f"
+ };
+/*
+ lookup hit total busy create
+pages: ...... ...... ...... ...... ...... [...... ...... ...... ......]
+locks: ...... ...... ...... ...... ...... [...... ...... ...... ...... ......]
+ env: ...... ...... ...... ...... ......
+ */
+ lu_site_stats_print(&site->cs_lu, m);
+ cache_stats_print(&site->cs_pages, m, 1);
+ seq_printf(m, " [");
+ for (i = 0; i < ARRAY_SIZE(site->cs_pages_state); ++i)
+ seq_printf(m, "%s: %u ", pstate[i],
+ atomic_read(&site->cs_pages_state[i]));
+ seq_printf(m, "]\n");
+ cache_stats_print(&site->cs_locks, m, 0);
+ seq_printf(m, " [");
+ for (i = 0; i < ARRAY_SIZE(site->cs_locks_state); ++i)
+ seq_printf(m, "%s: %u ", lstate[i],
+ atomic_read(&site->cs_locks_state[i]));
+ seq_printf(m, "]\n");
+ cache_stats_print(&cl_env_stats, m, 0);
+ seq_printf(m, "\n");
+ return 0;
+}
+EXPORT_SYMBOL(cl_site_stats_print);
+
+/*****************************************************************************
+ *
+ * lu_env handling on client.
+ *
+ */
+
+/**
+ * The most efficient way is to store cl_env pointer in task specific
+ * structures. On Linux, it wont' be easy to use task_struct->journal_info
+ * because Lustre code may call into other fs which has certain assumptions
+ * about journal_info. Currently following fields in task_struct are identified
+ * can be used for this purpose:
+ * - cl_env: for liblustre.
+ * - tux_info: only on RedHat kernel.
+ * - ...
+ * \note As long as we use task_struct to store cl_env, we assume that once
+ * called into Lustre, we'll never call into the other part of the kernel
+ * which will use those fields in task_struct without explicitly exiting
+ * Lustre.
+ *
+ * If there's no space in task_struct is available, hash will be used.
+ * bz20044, bz22683.
+ */
+
+struct cl_env {
+ void *ce_magic;
+ struct lu_env ce_lu;
+ struct lu_context ce_ses;
+
+ /**
+ * This allows cl_env to be entered into cl_env_hash which implements
+ * the current thread -> client environment lookup.
+ */
+ struct hlist_node ce_node;
+ /**
+ * Owner for the current cl_env.
+ *
+ * If LL_TASK_CL_ENV is defined, this point to the owning current,
+ * only for debugging purpose ;
+ * Otherwise hash is used, and this is the key for cfs_hash.
+ * Now current thread pid is stored. Note using thread pointer would
+ * lead to unbalanced hash because of its specific allocation locality
+ * and could be varied for different platforms and OSes, even different
+ * OS versions.
+ */
+ void *ce_owner;
+
+ /*
+ * Linkage into global list of all client environments. Used for
+ * garbage collection.
+ */
+ struct list_head ce_linkage;
+ /*
+ *
+ */
+ int ce_ref;
+ /*
+ * Debugging field: address of the caller who made original
+ * allocation.
+ */
+ void *ce_debug;
+};
+
+#define CL_ENV_INC(counter)
+#define CL_ENV_DEC(counter)
+
+static void cl_env_init0(struct cl_env *cle, void *debug)
+{
+ LASSERT(cle->ce_ref == 0);
+ LASSERT(cle->ce_magic == &cl_env_init0);
+ LASSERT(cle->ce_debug == NULL && cle->ce_owner == NULL);
+
+ cle->ce_ref = 1;
+ cle->ce_debug = debug;
+ CL_ENV_INC(busy);
+}
+
+
+/*
+ * The implementation of using hash table to connect cl_env and thread
+ */
+
+static struct cfs_hash *cl_env_hash;
+
+static unsigned cl_env_hops_hash(struct cfs_hash *lh,
+ const void *key, unsigned mask)
+{
+#if BITS_PER_LONG == 64
+ return cfs_hash_u64_hash((__u64)key, mask);
+#else
+ return cfs_hash_u32_hash((__u32)key, mask);
+#endif
+}
+
+static void *cl_env_hops_obj(struct hlist_node *hn)
+{
+ struct cl_env *cle = hlist_entry(hn, struct cl_env, ce_node);
+ LASSERT(cle->ce_magic == &cl_env_init0);
+ return (void *)cle;
+}
+
+static int cl_env_hops_keycmp(const void *key, struct hlist_node *hn)
+{
+ struct cl_env *cle = cl_env_hops_obj(hn);
+
+ LASSERT(cle->ce_owner != NULL);
+ return (key == cle->ce_owner);
+}
+
+static void cl_env_hops_noop(struct cfs_hash *hs, struct hlist_node *hn)
+{
+ struct cl_env *cle = hlist_entry(hn, struct cl_env, ce_node);
+ LASSERT(cle->ce_magic == &cl_env_init0);
+}
+
+static cfs_hash_ops_t cl_env_hops = {
+ .hs_hash = cl_env_hops_hash,
+ .hs_key = cl_env_hops_obj,
+ .hs_keycmp = cl_env_hops_keycmp,
+ .hs_object = cl_env_hops_obj,
+ .hs_get = cl_env_hops_noop,
+ .hs_put_locked = cl_env_hops_noop,
+};
+
+static inline struct cl_env *cl_env_fetch(void)
+{
+ struct cl_env *cle;
+
+ cle = cfs_hash_lookup(cl_env_hash, (void *) (long) current->pid);
+ LASSERT(ergo(cle, cle->ce_magic == &cl_env_init0));
+ return cle;
+}
+
+static inline void cl_env_attach(struct cl_env *cle)
+{
+ if (cle) {
+ int rc;
+
+ LASSERT(cle->ce_owner == NULL);
+ cle->ce_owner = (void *) (long) current->pid;
+ rc = cfs_hash_add_unique(cl_env_hash, cle->ce_owner,
+ &cle->ce_node);
+ LASSERT(rc == 0);
+ }
+}
+
+static inline void cl_env_do_detach(struct cl_env *cle)
+{
+ void *cookie;
+
+ LASSERT(cle->ce_owner == (void *) (long) current->pid);
+ cookie = cfs_hash_del(cl_env_hash, cle->ce_owner,
+ &cle->ce_node);
+ LASSERT(cookie == cle);
+ cle->ce_owner = NULL;
+}
+
+static int cl_env_store_init(void) {
+ cl_env_hash = cfs_hash_create("cl_env",
+ HASH_CL_ENV_BITS, HASH_CL_ENV_BITS,
+ HASH_CL_ENV_BKT_BITS, 0,
+ CFS_HASH_MIN_THETA,
+ CFS_HASH_MAX_THETA,
+ &cl_env_hops,
+ CFS_HASH_RW_BKTLOCK);
+ return cl_env_hash != NULL ? 0 :-ENOMEM;
+}
+
+static void cl_env_store_fini(void)
+{
+ cfs_hash_putref(cl_env_hash);
+}
+
+
+static inline struct cl_env *cl_env_detach(struct cl_env *cle)
+{
+ if (cle == NULL)
+ cle = cl_env_fetch();
+
+ if (cle && cle->ce_owner)
+ cl_env_do_detach(cle);
+
+ return cle;
+}
+
+static struct lu_env *cl_env_new(__u32 ctx_tags, __u32 ses_tags, void *debug)
+{
+ struct lu_env *env;
+ struct cl_env *cle;
+
+ OBD_SLAB_ALLOC_PTR_GFP(cle, cl_env_kmem, GFP_NOFS);
+ if (cle != NULL) {
+ int rc;
+
+ INIT_LIST_HEAD(&cle->ce_linkage);
+ cle->ce_magic = &cl_env_init0;
+ env = &cle->ce_lu;
+ rc = lu_env_init(env, LCT_CL_THREAD|ctx_tags);
+ if (rc == 0) {
+ rc = lu_context_init(&cle->ce_ses,
+ LCT_SESSION | ses_tags);
+ if (rc == 0) {
+ lu_context_enter(&cle->ce_ses);
+ env->le_ses = &cle->ce_ses;
+ cl_env_init0(cle, debug);
+ } else
+ lu_env_fini(env);
+ }
+ if (rc != 0) {
+ OBD_SLAB_FREE_PTR(cle, cl_env_kmem);
+ env = ERR_PTR(rc);
+ } else {
+ CL_ENV_INC(create);
+ CL_ENV_INC(total);
+ }
+ } else
+ env = ERR_PTR(-ENOMEM);
+ return env;
+}
+
+static void cl_env_fini(struct cl_env *cle)
+{
+ CL_ENV_DEC(total);
+ lu_context_fini(&cle->ce_lu.le_ctx);
+ lu_context_fini(&cle->ce_ses);
+ OBD_SLAB_FREE_PTR(cle, cl_env_kmem);
+}
+
+static inline struct cl_env *cl_env_container(struct lu_env *env)
+{
+ return container_of(env, struct cl_env, ce_lu);
+}
+
+struct lu_env *cl_env_peek(int *refcheck)
+{
+ struct lu_env *env;
+ struct cl_env *cle;
+
+ CL_ENV_INC(lookup);
+
+ /* check that we don't go far from untrusted pointer */
+ CLASSERT(offsetof(struct cl_env, ce_magic) == 0);
+
+ env = NULL;
+ cle = cl_env_fetch();
+ if (cle != NULL) {
+ CL_ENV_INC(hit);
+ env = &cle->ce_lu;
+ *refcheck = ++cle->ce_ref;
+ }
+ CDEBUG(D_OTHER, "%d@%p\n", cle ? cle->ce_ref : 0, cle);
+ return env;
+}
+EXPORT_SYMBOL(cl_env_peek);
+
+/**
+ * Returns lu_env: if there already is an environment associated with the
+ * current thread, it is returned, otherwise, new environment is allocated.
+ *
+ * \param refcheck pointer to a counter used to detect environment leaks. In
+ * the usual case cl_env_get() and cl_env_put() are called in the same lexical
+ * scope and pointer to the same integer is passed as \a refcheck. This is
+ * used to detect missed cl_env_put().
+ *
+ * \see cl_env_put()
+ */
+struct lu_env *cl_env_get(int *refcheck)
+{
+ struct lu_env *env;
+
+ env = cl_env_peek(refcheck);
+ if (env == NULL) {
+ env = cl_env_new(lu_context_tags_default,
+ lu_session_tags_default,
+ __builtin_return_address(0));
+
+ if (!IS_ERR(env)) {
+ struct cl_env *cle;
+
+ cle = cl_env_container(env);
+ cl_env_attach(cle);
+ *refcheck = cle->ce_ref;
+ CDEBUG(D_OTHER, "%d@%p\n", cle->ce_ref, cle);
+ }
+ }
+ return env;
+}
+EXPORT_SYMBOL(cl_env_get);
+
+/**
+ * Forces an allocation of a fresh environment with given tags.
+ *
+ * \see cl_env_get()
+ */
+struct lu_env *cl_env_alloc(int *refcheck, __u32 tags)
+{
+ struct lu_env *env;
+
+ LASSERT(cl_env_peek(refcheck) == NULL);
+ env = cl_env_new(tags, tags, __builtin_return_address(0));
+ if (!IS_ERR(env)) {
+ struct cl_env *cle;
+
+ cle = cl_env_container(env);
+ *refcheck = cle->ce_ref;
+ CDEBUG(D_OTHER, "%d@%p\n", cle->ce_ref, cle);
+ }
+ return env;
+}
+EXPORT_SYMBOL(cl_env_alloc);
+
+static void cl_env_exit(struct cl_env *cle)
+{
+ LASSERT(cle->ce_owner == NULL);
+ lu_context_exit(&cle->ce_lu.le_ctx);
+ lu_context_exit(&cle->ce_ses);
+}
+
+/**
+ * Release an environment.
+ *
+ * Decrement \a env reference counter. When counter drops to 0, nothing in
+ * this thread is using environment and it is returned to the allocation
+ * cache, or freed straight away, if cache is large enough.
+ */
+void cl_env_put(struct lu_env *env, int *refcheck)
+{
+ struct cl_env *cle;
+
+ cle = cl_env_container(env);
+
+ LASSERT(cle->ce_ref > 0);
+ LASSERT(ergo(refcheck != NULL, cle->ce_ref == *refcheck));
+
+ CDEBUG(D_OTHER, "%d@%p\n", cle->ce_ref, cle);
+ if (--cle->ce_ref == 0) {
+ CL_ENV_DEC(busy);
+ cl_env_detach(cle);
+ cle->ce_debug = NULL;
+ cl_env_exit(cle);
+ cl_env_fini(cle);
+ }
+}
+EXPORT_SYMBOL(cl_env_put);
+
+/**
+ * Declares a point of re-entrancy.
+ *
+ * \see cl_env_reexit()
+ */
+void *cl_env_reenter(void)
+{
+ return cl_env_detach(NULL);
+}
+EXPORT_SYMBOL(cl_env_reenter);
+
+/**
+ * Exits re-entrancy.
+ */
+void cl_env_reexit(void *cookie)
+{
+ cl_env_detach(NULL);
+ cl_env_attach(cookie);
+}
+EXPORT_SYMBOL(cl_env_reexit);
+
+/**
+ * Setup user-supplied \a env as a current environment. This is to be used to
+ * guaranteed that environment exists even when cl_env_get() fails. It is up
+ * to user to ensure proper concurrency control.
+ *
+ * \see cl_env_unplant()
+ */
+void cl_env_implant(struct lu_env *env, int *refcheck)
+{
+ struct cl_env *cle = cl_env_container(env);
+
+ LASSERT(cle->ce_ref > 0);
+
+ cl_env_attach(cle);
+ cl_env_get(refcheck);
+ CDEBUG(D_OTHER, "%d@%p\n", cle->ce_ref, cle);
+}
+EXPORT_SYMBOL(cl_env_implant);
+
+/**
+ * Detach environment installed earlier by cl_env_implant().
+ */
+void cl_env_unplant(struct lu_env *env, int *refcheck)
+{
+ struct cl_env *cle = cl_env_container(env);
+
+ LASSERT(cle->ce_ref > 1);
+
+ CDEBUG(D_OTHER, "%d@%p\n", cle->ce_ref, cle);
+
+ cl_env_detach(cle);
+ cl_env_put(env, refcheck);
+}
+EXPORT_SYMBOL(cl_env_unplant);
+
+struct lu_env *cl_env_nested_get(struct cl_env_nest *nest)
+{
+ struct lu_env *env;
+
+ nest->cen_cookie = NULL;
+ env = cl_env_peek(&nest->cen_refcheck);
+ if (env != NULL) {
+ if (!cl_io_is_going(env))
+ return env;
+ else {
+ cl_env_put(env, &nest->cen_refcheck);
+ nest->cen_cookie = cl_env_reenter();
+ }
+ }
+ env = cl_env_get(&nest->cen_refcheck);
+ if (IS_ERR(env)) {
+ cl_env_reexit(nest->cen_cookie);
+ return env;
+ }
+
+ LASSERT(!cl_io_is_going(env));
+ return env;
+}
+EXPORT_SYMBOL(cl_env_nested_get);
+
+void cl_env_nested_put(struct cl_env_nest *nest, struct lu_env *env)
+{
+ cl_env_put(env, &nest->cen_refcheck);
+ cl_env_reexit(nest->cen_cookie);
+}
+EXPORT_SYMBOL(cl_env_nested_put);
+
+/**
+ * Converts struct cl_attr to struct ost_lvb.
+ *
+ * \see cl_lvb2attr
+ */
+void cl_attr2lvb(struct ost_lvb *lvb, const struct cl_attr *attr)
+{
+ lvb->lvb_size = attr->cat_size;
+ lvb->lvb_mtime = attr->cat_mtime;
+ lvb->lvb_atime = attr->cat_atime;
+ lvb->lvb_ctime = attr->cat_ctime;
+ lvb->lvb_blocks = attr->cat_blocks;
+}
+EXPORT_SYMBOL(cl_attr2lvb);
+
+/**
+ * Converts struct ost_lvb to struct cl_attr.
+ *
+ * \see cl_attr2lvb
+ */
+void cl_lvb2attr(struct cl_attr *attr, const struct ost_lvb *lvb)
+{
+ attr->cat_size = lvb->lvb_size;
+ attr->cat_mtime = lvb->lvb_mtime;
+ attr->cat_atime = lvb->lvb_atime;
+ attr->cat_ctime = lvb->lvb_ctime;
+ attr->cat_blocks = lvb->lvb_blocks;
+}
+EXPORT_SYMBOL(cl_lvb2attr);
+
+/*****************************************************************************
+ *
+ * Temporary prototype thing: mirror obd-devices into cl devices.
+ *
+ */
+
+struct cl_device *cl_type_setup(const struct lu_env *env, struct lu_site *site,
+ struct lu_device_type *ldt,
+ struct lu_device *next)
+{
+ const char *typename;
+ struct lu_device *d;
+
+ LASSERT(ldt != NULL);
+
+ typename = ldt->ldt_name;
+ d = ldt->ldt_ops->ldto_device_alloc(env, ldt, NULL);
+ if (!IS_ERR(d)) {
+ int rc;
+
+ if (site != NULL)
+ d->ld_site = site;
+ rc = ldt->ldt_ops->ldto_device_init(env, d, typename, next);
+ if (rc == 0) {
+ lu_device_get(d);
+ lu_ref_add(&d->ld_reference,
+ "lu-stack", &lu_site_init);
+ } else {
+ ldt->ldt_ops->ldto_device_free(env, d);
+ CERROR("can't init device '%s', %d\n", typename, rc);
+ d = ERR_PTR(rc);
+ }
+ } else
+ CERROR("Cannot allocate device: '%s'\n", typename);
+ return lu2cl_dev(d);
+}
+EXPORT_SYMBOL(cl_type_setup);
+
+/**
+ * Finalize device stack by calling lu_stack_fini().
+ */
+void cl_stack_fini(const struct lu_env *env, struct cl_device *cl)
+{
+ lu_stack_fini(env, cl2lu_dev(cl));
+}
+EXPORT_SYMBOL(cl_stack_fini);
+
+int cl_lock_init(void);
+void cl_lock_fini(void);
+
+int cl_page_init(void);
+void cl_page_fini(void);
+
+static struct lu_context_key cl_key;
+
+struct cl_thread_info *cl_env_info(const struct lu_env *env)
+{
+ return lu_context_key_get(&env->le_ctx, &cl_key);
+}
+
+/* defines cl0_key_{init,fini}() */
+LU_KEY_INIT_FINI(cl0, struct cl_thread_info);
+
+static void *cl_key_init(const struct lu_context *ctx,
+ struct lu_context_key *key)
+{
+ struct cl_thread_info *info;
+
+ info = cl0_key_init(ctx, key);
+ if (!IS_ERR(info)) {
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(info->clt_counters); ++i)
+ lu_ref_init(&info->clt_counters[i].ctc_locks_locked);
+ }
+ return info;
+}
+
+static void cl_key_fini(const struct lu_context *ctx,
+ struct lu_context_key *key, void *data)
+{
+ struct cl_thread_info *info;
+ int i;
+
+ info = data;
+ for (i = 0; i < ARRAY_SIZE(info->clt_counters); ++i)
+ lu_ref_fini(&info->clt_counters[i].ctc_locks_locked);
+ cl0_key_fini(ctx, key, data);
+}
+
+static void cl_key_exit(const struct lu_context *ctx,
+ struct lu_context_key *key, void *data)
+{
+ struct cl_thread_info *info = data;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(info->clt_counters); ++i) {
+ LASSERT(info->clt_counters[i].ctc_nr_held == 0);
+ LASSERT(info->clt_counters[i].ctc_nr_used == 0);
+ LASSERT(info->clt_counters[i].ctc_nr_locks_acquired == 0);
+ LASSERT(info->clt_counters[i].ctc_nr_locks_locked == 0);
+ lu_ref_fini(&info->clt_counters[i].ctc_locks_locked);
+ lu_ref_init(&info->clt_counters[i].ctc_locks_locked);
+ }
+}
+
+static struct lu_context_key cl_key = {
+ .lct_tags = LCT_CL_THREAD,
+ .lct_init = cl_key_init,
+ .lct_fini = cl_key_fini,
+ .lct_exit = cl_key_exit
+};
+
+static struct lu_kmem_descr cl_object_caches[] = {
+ {
+ .ckd_cache = &cl_env_kmem,
+ .ckd_name = "cl_env_kmem",
+ .ckd_size = sizeof (struct cl_env)
+ },
+ {
+ .ckd_cache = NULL
+ }
+};
+
+/**
+ * Global initialization of cl-data. Create kmem caches, register
+ * lu_context_key's, etc.
+ *
+ * \see cl_global_fini()
+ */
+int cl_global_init(void)
+{
+ int result;
+
+ result = cl_env_store_init();
+ if (result)
+ return result;
+
+ result = lu_kmem_init(cl_object_caches);
+ if (result)
+ goto out_store;
+
+ LU_CONTEXT_KEY_INIT(&cl_key);
+ result = lu_context_key_register(&cl_key);
+ if (result)
+ goto out_kmem;
+
+ result = cl_lock_init();
+ if (result)
+ goto out_context;
+
+ result = cl_page_init();
+ if (result)
+ goto out_lock;
+
+ return 0;
+out_lock:
+ cl_lock_fini();
+out_context:
+ lu_context_key_degister(&cl_key);
+out_kmem:
+ lu_kmem_fini(cl_object_caches);
+out_store:
+ cl_env_store_fini();
+ return result;
+}
+
+/**
+ * Finalization of global cl-data. Dual to cl_global_init().
+ */
+void cl_global_fini(void)
+{
+ cl_lock_fini();
+ cl_page_fini();
+ lu_context_key_degister(&cl_key);
+ lu_kmem_fini(cl_object_caches);
+ cl_env_store_fini();
+}
diff --git a/drivers/staging/lustre/lustre/obdclass/cl_page.c b/drivers/staging/lustre/lustre/obdclass/cl_page.c
new file mode 100644
index 000000000..b7dd04808
--- /dev/null
+++ b/drivers/staging/lustre/lustre/obdclass/cl_page.c
@@ -0,0 +1,1553 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2011, 2012, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ *
+ * Client Lustre Page.
+ *
+ * Author: Nikita Danilov <nikita.danilov@sun.com>
+ */
+
+#define DEBUG_SUBSYSTEM S_CLASS
+
+#include "../../include/linux/libcfs/libcfs.h"
+#include "../include/obd_class.h"
+#include "../include/obd_support.h"
+#include <linux/list.h>
+
+#include "../include/cl_object.h"
+#include "cl_internal.h"
+
+static void cl_page_delete0(const struct lu_env *env, struct cl_page *pg,
+ int radix);
+
+# define PASSERT(env, page, expr) \
+ do { \
+ if (unlikely(!(expr))) { \
+ CL_PAGE_DEBUG(D_ERROR, (env), (page), #expr "\n"); \
+ LASSERT(0); \
+ } \
+ } while (0)
+
+# define PINVRNT(env, page, exp) \
+ ((void)sizeof(env), (void)sizeof(page), (void)sizeof !!(exp))
+
+/* Disable page statistic by default due to huge performance penalty. */
+#define CS_PAGE_INC(o, item)
+#define CS_PAGE_DEC(o, item)
+#define CS_PAGESTATE_INC(o, state)
+#define CS_PAGESTATE_DEC(o, state)
+
+/**
+ * Internal version of cl_page_top, it should be called if the page is
+ * known to be not freed, says with page referenced, or radix tree lock held,
+ * or page owned.
+ */
+static struct cl_page *cl_page_top_trusted(struct cl_page *page)
+{
+ while (page->cp_parent != NULL)
+ page = page->cp_parent;
+ return page;
+}
+
+/**
+ * Internal version of cl_page_get().
+ *
+ * This function can be used to obtain initial reference to previously
+ * unreferenced cached object. It can be called only if concurrent page
+ * reclamation is somehow prevented, e.g., by locking page radix-tree
+ * (cl_object_header::hdr->coh_page_guard), or by keeping a lock on a VM page,
+ * associated with \a page.
+ *
+ * Use with care! Not exported.
+ */
+static void cl_page_get_trust(struct cl_page *page)
+{
+ LASSERT(atomic_read(&page->cp_ref) > 0);
+ atomic_inc(&page->cp_ref);
+}
+
+/**
+ * Returns a slice within a page, corresponding to the given layer in the
+ * device stack.
+ *
+ * \see cl_lock_at()
+ */
+static const struct cl_page_slice *
+cl_page_at_trusted(const struct cl_page *page,
+ const struct lu_device_type *dtype)
+{
+ const struct cl_page_slice *slice;
+
+ page = cl_page_top_trusted((struct cl_page *)page);
+ do {
+ list_for_each_entry(slice, &page->cp_layers, cpl_linkage) {
+ if (slice->cpl_obj->co_lu.lo_dev->ld_type == dtype)
+ return slice;
+ }
+ page = page->cp_child;
+ } while (page != NULL);
+ return NULL;
+}
+
+/**
+ * Returns a page with given index in the given object, or NULL if no page is
+ * found. Acquires a reference on \a page.
+ *
+ * Locking: called under cl_object_header::coh_page_guard spin-lock.
+ */
+struct cl_page *cl_page_lookup(struct cl_object_header *hdr, pgoff_t index)
+{
+ struct cl_page *page;
+
+ assert_spin_locked(&hdr->coh_page_guard);
+
+ page = radix_tree_lookup(&hdr->coh_tree, index);
+ if (page != NULL)
+ cl_page_get_trust(page);
+ return page;
+}
+EXPORT_SYMBOL(cl_page_lookup);
+
+/**
+ * Returns a list of pages by a given [start, end] of \a obj.
+ *
+ * \param resched If not NULL, then we give up before hogging CPU for too
+ * long and set *resched = 1, in that case caller should implement a retry
+ * logic.
+ *
+ * Gang tree lookup (radix_tree_gang_lookup()) optimization is absolutely
+ * crucial in the face of [offset, EOF] locks.
+ *
+ * Return at least one page in @queue unless there is no covered page.
+ */
+int cl_page_gang_lookup(const struct lu_env *env, struct cl_object *obj,
+ struct cl_io *io, pgoff_t start, pgoff_t end,
+ cl_page_gang_cb_t cb, void *cbdata)
+{
+ struct cl_object_header *hdr;
+ struct cl_page *page;
+ struct cl_page **pvec;
+ const struct cl_page_slice *slice;
+ const struct lu_device_type *dtype;
+ pgoff_t idx;
+ unsigned int nr;
+ unsigned int i;
+ unsigned int j;
+ int res = CLP_GANG_OKAY;
+ int tree_lock = 1;
+
+ idx = start;
+ hdr = cl_object_header(obj);
+ pvec = cl_env_info(env)->clt_pvec;
+ dtype = cl_object_top(obj)->co_lu.lo_dev->ld_type;
+ spin_lock(&hdr->coh_page_guard);
+ while ((nr = radix_tree_gang_lookup(&hdr->coh_tree, (void **)pvec,
+ idx, CLT_PVEC_SIZE)) > 0) {
+ int end_of_region = 0;
+ idx = pvec[nr - 1]->cp_index + 1;
+ for (i = 0, j = 0; i < nr; ++i) {
+ page = pvec[i];
+ pvec[i] = NULL;
+
+ LASSERT(page->cp_type == CPT_CACHEABLE);
+ if (page->cp_index > end) {
+ end_of_region = 1;
+ break;
+ }
+ if (page->cp_state == CPS_FREEING)
+ continue;
+
+ slice = cl_page_at_trusted(page, dtype);
+ /*
+ * Pages for lsm-less file has no underneath sub-page
+ * for osc, in case of ...
+ */
+ PASSERT(env, page, slice != NULL);
+
+ page = slice->cpl_page;
+ /*
+ * Can safely call cl_page_get_trust() under
+ * radix-tree spin-lock.
+ *
+ * XXX not true, because @page is from object another
+ * than @hdr and protected by different tree lock.
+ */
+ cl_page_get_trust(page);
+ lu_ref_add_atomic(&page->cp_reference,
+ "gang_lookup", current);
+ pvec[j++] = page;
+ }
+
+ /*
+ * Here a delicate locking dance is performed. Current thread
+ * holds a reference to a page, but has to own it before it
+ * can be placed into queue. Owning implies waiting, so
+ * radix-tree lock is to be released. After a wait one has to
+ * check that pages weren't truncated (cl_page_own() returns
+ * error in the latter case).
+ */
+ spin_unlock(&hdr->coh_page_guard);
+ tree_lock = 0;
+
+ for (i = 0; i < j; ++i) {
+ page = pvec[i];
+ if (res == CLP_GANG_OKAY)
+ res = (*cb)(env, io, page, cbdata);
+ lu_ref_del(&page->cp_reference,
+ "gang_lookup", current);
+ cl_page_put(env, page);
+ }
+ if (nr < CLT_PVEC_SIZE || end_of_region)
+ break;
+
+ if (res == CLP_GANG_OKAY && need_resched())
+ res = CLP_GANG_RESCHED;
+ if (res != CLP_GANG_OKAY)
+ break;
+
+ spin_lock(&hdr->coh_page_guard);
+ tree_lock = 1;
+ }
+ if (tree_lock)
+ spin_unlock(&hdr->coh_page_guard);
+ return res;
+}
+EXPORT_SYMBOL(cl_page_gang_lookup);
+
+static void cl_page_free(const struct lu_env *env, struct cl_page *page)
+{
+ struct cl_object *obj = page->cp_obj;
+ int pagesize = cl_object_header(obj)->coh_page_bufsize;
+
+ PASSERT(env, page, list_empty(&page->cp_batch));
+ PASSERT(env, page, page->cp_owner == NULL);
+ PASSERT(env, page, page->cp_req == NULL);
+ PASSERT(env, page, page->cp_parent == NULL);
+ PASSERT(env, page, page->cp_state == CPS_FREEING);
+
+ might_sleep();
+ while (!list_empty(&page->cp_layers)) {
+ struct cl_page_slice *slice;
+
+ slice = list_entry(page->cp_layers.next,
+ struct cl_page_slice, cpl_linkage);
+ list_del_init(page->cp_layers.next);
+ slice->cpl_ops->cpo_fini(env, slice);
+ }
+ CS_PAGE_DEC(obj, total);
+ CS_PAGESTATE_DEC(obj, page->cp_state);
+ lu_object_ref_del_at(&obj->co_lu, &page->cp_obj_ref, "cl_page", page);
+ cl_object_put(env, obj);
+ lu_ref_fini(&page->cp_reference);
+ OBD_FREE(page, pagesize);
+}
+
+/**
+ * Helper function updating page state. This is the only place in the code
+ * where cl_page::cp_state field is mutated.
+ */
+static inline void cl_page_state_set_trust(struct cl_page *page,
+ enum cl_page_state state)
+{
+ /* bypass const. */
+ *(enum cl_page_state *)&page->cp_state = state;
+}
+
+static struct cl_page *cl_page_alloc(const struct lu_env *env,
+ struct cl_object *o, pgoff_t ind, struct page *vmpage,
+ enum cl_page_type type)
+{
+ struct cl_page *page;
+ struct lu_object_header *head;
+
+ OBD_ALLOC_GFP(page, cl_object_header(o)->coh_page_bufsize,
+ GFP_NOFS);
+ if (page != NULL) {
+ int result = 0;
+ atomic_set(&page->cp_ref, 1);
+ if (type == CPT_CACHEABLE) /* for radix tree */
+ atomic_inc(&page->cp_ref);
+ page->cp_obj = o;
+ cl_object_get(o);
+ lu_object_ref_add_at(&o->co_lu, &page->cp_obj_ref, "cl_page",
+ page);
+ page->cp_index = ind;
+ cl_page_state_set_trust(page, CPS_CACHED);
+ page->cp_type = type;
+ INIT_LIST_HEAD(&page->cp_layers);
+ INIT_LIST_HEAD(&page->cp_batch);
+ INIT_LIST_HEAD(&page->cp_flight);
+ mutex_init(&page->cp_mutex);
+ lu_ref_init(&page->cp_reference);
+ head = o->co_lu.lo_header;
+ list_for_each_entry(o, &head->loh_layers,
+ co_lu.lo_linkage) {
+ if (o->co_ops->coo_page_init != NULL) {
+ result = o->co_ops->coo_page_init(env, o,
+ page, vmpage);
+ if (result != 0) {
+ cl_page_delete0(env, page, 0);
+ cl_page_free(env, page);
+ page = ERR_PTR(result);
+ break;
+ }
+ }
+ }
+ if (result == 0) {
+ CS_PAGE_INC(o, total);
+ CS_PAGE_INC(o, create);
+ CS_PAGESTATE_DEC(o, CPS_CACHED);
+ }
+ } else {
+ page = ERR_PTR(-ENOMEM);
+ }
+ return page;
+}
+
+/**
+ * Returns a cl_page with index \a idx at the object \a o, and associated with
+ * the VM page \a vmpage.
+ *
+ * This is the main entry point into the cl_page caching interface. First, a
+ * cache (implemented as a per-object radix tree) is consulted. If page is
+ * found there, it is returned immediately. Otherwise new page is allocated
+ * and returned. In any case, additional reference to page is acquired.
+ *
+ * \see cl_object_find(), cl_lock_find()
+ */
+static struct cl_page *cl_page_find0(const struct lu_env *env,
+ struct cl_object *o,
+ pgoff_t idx, struct page *vmpage,
+ enum cl_page_type type,
+ struct cl_page *parent)
+{
+ struct cl_page *page = NULL;
+ struct cl_page *ghost = NULL;
+ struct cl_object_header *hdr;
+ int err;
+
+ LASSERT(type == CPT_CACHEABLE || type == CPT_TRANSIENT);
+ might_sleep();
+
+ hdr = cl_object_header(o);
+ CS_PAGE_INC(o, lookup);
+
+ CDEBUG(D_PAGE, "%lu@"DFID" %p %lx %d\n",
+ idx, PFID(&hdr->coh_lu.loh_fid), vmpage, vmpage->private, type);
+ /* fast path. */
+ if (type == CPT_CACHEABLE) {
+ /* vmpage lock is used to protect the child/parent
+ * relationship */
+ KLASSERT(PageLocked(vmpage));
+ /*
+ * cl_vmpage_page() can be called here without any locks as
+ *
+ * - "vmpage" is locked (which prevents ->private from
+ * concurrent updates), and
+ *
+ * - "o" cannot be destroyed while current thread holds a
+ * reference on it.
+ */
+ page = cl_vmpage_page(vmpage, o);
+ PINVRNT(env, page,
+ ergo(page != NULL,
+ cl_page_vmpage(env, page) == vmpage &&
+ (void *)radix_tree_lookup(&hdr->coh_tree,
+ idx) == page));
+ }
+
+ if (page != NULL) {
+ CS_PAGE_INC(o, hit);
+ return page;
+ }
+
+ /* allocate and initialize cl_page */
+ page = cl_page_alloc(env, o, idx, vmpage, type);
+ if (IS_ERR(page))
+ return page;
+
+ if (type == CPT_TRANSIENT) {
+ if (parent) {
+ LASSERT(page->cp_parent == NULL);
+ page->cp_parent = parent;
+ parent->cp_child = page;
+ }
+ return page;
+ }
+
+ /*
+ * XXX optimization: use radix_tree_preload() here, and change tree
+ * gfp mask to GFP_KERNEL in cl_object_header_init().
+ */
+ spin_lock(&hdr->coh_page_guard);
+ err = radix_tree_insert(&hdr->coh_tree, idx, page);
+ if (err != 0) {
+ ghost = page;
+ /*
+ * Noted by Jay: a lock on \a vmpage protects cl_page_find()
+ * from this race, but
+ *
+ * 0. it's better to have cl_page interface "locally
+ * consistent" so that its correctness can be reasoned
+ * about without appealing to the (obscure world of) VM
+ * locking.
+ *
+ * 1. handling this race allows ->coh_tree to remain
+ * consistent even when VM locking is somehow busted,
+ * which is very useful during diagnosing and debugging.
+ */
+ page = ERR_PTR(err);
+ CL_PAGE_DEBUG(D_ERROR, env, ghost,
+ "fail to insert into radix tree: %d\n", err);
+ } else {
+ if (parent) {
+ LASSERT(page->cp_parent == NULL);
+ page->cp_parent = parent;
+ parent->cp_child = page;
+ }
+ hdr->coh_pages++;
+ }
+ spin_unlock(&hdr->coh_page_guard);
+
+ if (unlikely(ghost != NULL)) {
+ cl_page_delete0(env, ghost, 0);
+ cl_page_free(env, ghost);
+ }
+ return page;
+}
+
+struct cl_page *cl_page_find(const struct lu_env *env, struct cl_object *o,
+ pgoff_t idx, struct page *vmpage,
+ enum cl_page_type type)
+{
+ return cl_page_find0(env, o, idx, vmpage, type, NULL);
+}
+EXPORT_SYMBOL(cl_page_find);
+
+
+struct cl_page *cl_page_find_sub(const struct lu_env *env, struct cl_object *o,
+ pgoff_t idx, struct page *vmpage,
+ struct cl_page *parent)
+{
+ return cl_page_find0(env, o, idx, vmpage, parent->cp_type, parent);
+}
+EXPORT_SYMBOL(cl_page_find_sub);
+
+static inline int cl_page_invariant(const struct cl_page *pg)
+{
+ struct cl_object_header *header;
+ struct cl_page *parent;
+ struct cl_page *child;
+ struct cl_io *owner;
+
+ /*
+ * Page invariant is protected by a VM lock.
+ */
+ LINVRNT(cl_page_is_vmlocked(NULL, pg));
+
+ header = cl_object_header(pg->cp_obj);
+ parent = pg->cp_parent;
+ child = pg->cp_child;
+ owner = pg->cp_owner;
+
+ return cl_page_in_use(pg) &&
+ ergo(parent != NULL, parent->cp_child == pg) &&
+ ergo(child != NULL, child->cp_parent == pg) &&
+ ergo(child != NULL, pg->cp_obj != child->cp_obj) &&
+ ergo(parent != NULL, pg->cp_obj != parent->cp_obj) &&
+ ergo(owner != NULL && parent != NULL,
+ parent->cp_owner == pg->cp_owner->ci_parent) &&
+ ergo(owner != NULL && child != NULL,
+ child->cp_owner->ci_parent == owner) &&
+ /*
+ * Either page is early in initialization (has neither child
+ * nor parent yet), or it is in the object radix tree.
+ */
+ ergo(pg->cp_state < CPS_FREEING && pg->cp_type == CPT_CACHEABLE,
+ (void *)radix_tree_lookup(&header->coh_tree,
+ pg->cp_index) == pg ||
+ (child == NULL && parent == NULL));
+}
+
+static void cl_page_state_set0(const struct lu_env *env,
+ struct cl_page *page, enum cl_page_state state)
+{
+ enum cl_page_state old;
+
+ /*
+ * Matrix of allowed state transitions [old][new], for sanity
+ * checking.
+ */
+ static const int allowed_transitions[CPS_NR][CPS_NR] = {
+ [CPS_CACHED] = {
+ [CPS_CACHED] = 0,
+ [CPS_OWNED] = 1, /* io finds existing cached page */
+ [CPS_PAGEIN] = 0,
+ [CPS_PAGEOUT] = 1, /* write-out from the cache */
+ [CPS_FREEING] = 1, /* eviction on the memory pressure */
+ },
+ [CPS_OWNED] = {
+ [CPS_CACHED] = 1, /* release to the cache */
+ [CPS_OWNED] = 0,
+ [CPS_PAGEIN] = 1, /* start read immediately */
+ [CPS_PAGEOUT] = 1, /* start write immediately */
+ [CPS_FREEING] = 1, /* lock invalidation or truncate */
+ },
+ [CPS_PAGEIN] = {
+ [CPS_CACHED] = 1, /* io completion */
+ [CPS_OWNED] = 0,
+ [CPS_PAGEIN] = 0,
+ [CPS_PAGEOUT] = 0,
+ [CPS_FREEING] = 0,
+ },
+ [CPS_PAGEOUT] = {
+ [CPS_CACHED] = 1, /* io completion */
+ [CPS_OWNED] = 0,
+ [CPS_PAGEIN] = 0,
+ [CPS_PAGEOUT] = 0,
+ [CPS_FREEING] = 0,
+ },
+ [CPS_FREEING] = {
+ [CPS_CACHED] = 0,
+ [CPS_OWNED] = 0,
+ [CPS_PAGEIN] = 0,
+ [CPS_PAGEOUT] = 0,
+ [CPS_FREEING] = 0,
+ }
+ };
+
+ old = page->cp_state;
+ PASSERT(env, page, allowed_transitions[old][state]);
+ CL_PAGE_HEADER(D_TRACE, env, page, "%d -> %d\n", old, state);
+ for (; page != NULL; page = page->cp_child) {
+ PASSERT(env, page, page->cp_state == old);
+ PASSERT(env, page,
+ equi(state == CPS_OWNED, page->cp_owner != NULL));
+
+ CS_PAGESTATE_DEC(page->cp_obj, page->cp_state);
+ CS_PAGESTATE_INC(page->cp_obj, state);
+ cl_page_state_set_trust(page, state);
+ }
+}
+
+static void cl_page_state_set(const struct lu_env *env,
+ struct cl_page *page, enum cl_page_state state)
+{
+ cl_page_state_set0(env, page, state);
+}
+
+/**
+ * Acquires an additional reference to a page.
+ *
+ * This can be called only by caller already possessing a reference to \a
+ * page.
+ *
+ * \see cl_object_get(), cl_lock_get().
+ */
+void cl_page_get(struct cl_page *page)
+{
+ cl_page_get_trust(page);
+}
+EXPORT_SYMBOL(cl_page_get);
+
+/**
+ * Releases a reference to a page.
+ *
+ * When last reference is released, page is returned to the cache, unless it
+ * is in cl_page_state::CPS_FREEING state, in which case it is immediately
+ * destroyed.
+ *
+ * \see cl_object_put(), cl_lock_put().
+ */
+void cl_page_put(const struct lu_env *env, struct cl_page *page)
+{
+ PASSERT(env, page, atomic_read(&page->cp_ref) > !!page->cp_parent);
+
+ CL_PAGE_HEADER(D_TRACE, env, page, "%d\n",
+ atomic_read(&page->cp_ref));
+
+ if (atomic_dec_and_test(&page->cp_ref)) {
+ LASSERT(page->cp_state == CPS_FREEING);
+
+ LASSERT(atomic_read(&page->cp_ref) == 0);
+ PASSERT(env, page, page->cp_owner == NULL);
+ PASSERT(env, page, list_empty(&page->cp_batch));
+ /*
+ * Page is no longer reachable by other threads. Tear
+ * it down.
+ */
+ cl_page_free(env, page);
+ }
+}
+EXPORT_SYMBOL(cl_page_put);
+
+/**
+ * Returns a VM page associated with a given cl_page.
+ */
+struct page *cl_page_vmpage(const struct lu_env *env, struct cl_page *page)
+{
+ const struct cl_page_slice *slice;
+
+ /*
+ * Find uppermost layer with ->cpo_vmpage() method, and return its
+ * result.
+ */
+ page = cl_page_top(page);
+ do {
+ list_for_each_entry(slice, &page->cp_layers, cpl_linkage) {
+ if (slice->cpl_ops->cpo_vmpage != NULL)
+ return slice->cpl_ops->cpo_vmpage(env, slice);
+ }
+ page = page->cp_child;
+ } while (page != NULL);
+ LBUG(); /* ->cpo_vmpage() has to be defined somewhere in the stack */
+}
+EXPORT_SYMBOL(cl_page_vmpage);
+
+/**
+ * Returns a cl_page associated with a VM page, and given cl_object.
+ */
+struct cl_page *cl_vmpage_page(struct page *vmpage, struct cl_object *obj)
+{
+ struct cl_page *top;
+ struct cl_page *page;
+
+ KLASSERT(PageLocked(vmpage));
+
+ /*
+ * NOTE: absence of races and liveness of data are guaranteed by page
+ * lock on a "vmpage". That works because object destruction has
+ * bottom-to-top pass.
+ */
+
+ /*
+ * This loop assumes that ->private points to the top-most page. This
+ * can be rectified easily.
+ */
+ top = (struct cl_page *)vmpage->private;
+ if (top == NULL)
+ return NULL;
+
+ for (page = top; page != NULL; page = page->cp_child) {
+ if (cl_object_same(page->cp_obj, obj)) {
+ cl_page_get_trust(page);
+ break;
+ }
+ }
+ LASSERT(ergo(page, page->cp_type == CPT_CACHEABLE));
+ return page;
+}
+EXPORT_SYMBOL(cl_vmpage_page);
+
+/**
+ * Returns the top-page for a given page.
+ *
+ * \see cl_object_top(), cl_io_top()
+ */
+struct cl_page *cl_page_top(struct cl_page *page)
+{
+ return cl_page_top_trusted(page);
+}
+EXPORT_SYMBOL(cl_page_top);
+
+const struct cl_page_slice *cl_page_at(const struct cl_page *page,
+ const struct lu_device_type *dtype)
+{
+ return cl_page_at_trusted(page, dtype);
+}
+EXPORT_SYMBOL(cl_page_at);
+
+#define CL_PAGE_OP(opname) offsetof(struct cl_page_operations, opname)
+
+#define CL_PAGE_INVOKE(_env, _page, _op, _proto, ...) \
+({ \
+ const struct lu_env *__env = (_env); \
+ struct cl_page *__page = (_page); \
+ const struct cl_page_slice *__scan; \
+ int __result; \
+ ptrdiff_t __op = (_op); \
+ int (*__method)_proto; \
+ \
+ __result = 0; \
+ __page = cl_page_top(__page); \
+ do { \
+ list_for_each_entry(__scan, &__page->cp_layers, \
+ cpl_linkage) { \
+ __method = *(void **)((char *)__scan->cpl_ops + \
+ __op); \
+ if (__method != NULL) { \
+ __result = (*__method)(__env, __scan, \
+ ## __VA_ARGS__); \
+ if (__result != 0) \
+ break; \
+ } \
+ } \
+ __page = __page->cp_child; \
+ } while (__page != NULL && __result == 0); \
+ if (__result > 0) \
+ __result = 0; \
+ __result; \
+})
+
+#define CL_PAGE_INVOID(_env, _page, _op, _proto, ...) \
+do { \
+ const struct lu_env *__env = (_env); \
+ struct cl_page *__page = (_page); \
+ const struct cl_page_slice *__scan; \
+ ptrdiff_t __op = (_op); \
+ void (*__method)_proto; \
+ \
+ __page = cl_page_top(__page); \
+ do { \
+ list_for_each_entry(__scan, &__page->cp_layers, \
+ cpl_linkage) { \
+ __method = *(void **)((char *)__scan->cpl_ops + \
+ __op); \
+ if (__method != NULL) \
+ (*__method)(__env, __scan, \
+ ## __VA_ARGS__); \
+ } \
+ __page = __page->cp_child; \
+ } while (__page != NULL); \
+} while (0)
+
+#define CL_PAGE_INVOID_REVERSE(_env, _page, _op, _proto, ...) \
+do { \
+ const struct lu_env *__env = (_env); \
+ struct cl_page *__page = (_page); \
+ const struct cl_page_slice *__scan; \
+ ptrdiff_t __op = (_op); \
+ void (*__method)_proto; \
+ \
+ /* get to the bottom page. */ \
+ while (__page->cp_child != NULL) \
+ __page = __page->cp_child; \
+ do { \
+ list_for_each_entry_reverse(__scan, &__page->cp_layers, \
+ cpl_linkage) { \
+ __method = *(void **)((char *)__scan->cpl_ops + \
+ __op); \
+ if (__method != NULL) \
+ (*__method)(__env, __scan, \
+ ## __VA_ARGS__); \
+ } \
+ __page = __page->cp_parent; \
+ } while (__page != NULL); \
+} while (0)
+
+static int cl_page_invoke(const struct lu_env *env,
+ struct cl_io *io, struct cl_page *page, ptrdiff_t op)
+
+{
+ PINVRNT(env, page, cl_object_same(page->cp_obj, io->ci_obj));
+ return CL_PAGE_INVOKE(env, page, op,
+ (const struct lu_env *,
+ const struct cl_page_slice *, struct cl_io *),
+ io);
+}
+
+static void cl_page_invoid(const struct lu_env *env,
+ struct cl_io *io, struct cl_page *page, ptrdiff_t op)
+
+{
+ PINVRNT(env, page, cl_object_same(page->cp_obj, io->ci_obj));
+ CL_PAGE_INVOID(env, page, op,
+ (const struct lu_env *,
+ const struct cl_page_slice *, struct cl_io *), io);
+}
+
+static void cl_page_owner_clear(struct cl_page *page)
+{
+ for (page = cl_page_top(page); page != NULL; page = page->cp_child) {
+ if (page->cp_owner != NULL) {
+ LASSERT(page->cp_owner->ci_owned_nr > 0);
+ page->cp_owner->ci_owned_nr--;
+ page->cp_owner = NULL;
+ page->cp_task = NULL;
+ }
+ }
+}
+
+static void cl_page_owner_set(struct cl_page *page)
+{
+ for (page = cl_page_top(page); page != NULL; page = page->cp_child) {
+ LASSERT(page->cp_owner != NULL);
+ page->cp_owner->ci_owned_nr++;
+ }
+}
+
+void cl_page_disown0(const struct lu_env *env,
+ struct cl_io *io, struct cl_page *pg)
+{
+ enum cl_page_state state;
+
+ state = pg->cp_state;
+ PINVRNT(env, pg, state == CPS_OWNED || state == CPS_FREEING);
+ PINVRNT(env, pg, cl_page_invariant(pg));
+ cl_page_owner_clear(pg);
+
+ if (state == CPS_OWNED)
+ cl_page_state_set(env, pg, CPS_CACHED);
+ /*
+ * Completion call-backs are executed in the bottom-up order, so that
+ * uppermost layer (llite), responsible for VFS/VM interaction runs
+ * last and can release locks safely.
+ */
+ CL_PAGE_INVOID_REVERSE(env, pg, CL_PAGE_OP(cpo_disown),
+ (const struct lu_env *,
+ const struct cl_page_slice *, struct cl_io *),
+ io);
+}
+
+/**
+ * returns true, iff page is owned by the given io.
+ */
+int cl_page_is_owned(const struct cl_page *pg, const struct cl_io *io)
+{
+ LINVRNT(cl_object_same(pg->cp_obj, io->ci_obj));
+ return pg->cp_state == CPS_OWNED && pg->cp_owner == io;
+}
+EXPORT_SYMBOL(cl_page_is_owned);
+
+/**
+ * Try to own a page by IO.
+ *
+ * Waits until page is in cl_page_state::CPS_CACHED state, and then switch it
+ * into cl_page_state::CPS_OWNED state.
+ *
+ * \pre !cl_page_is_owned(pg, io)
+ * \post result == 0 iff cl_page_is_owned(pg, io)
+ *
+ * \retval 0 success
+ *
+ * \retval -ve failure, e.g., page was destroyed (and landed in
+ * cl_page_state::CPS_FREEING instead of cl_page_state::CPS_CACHED).
+ * or, page was owned by another thread, or in IO.
+ *
+ * \see cl_page_disown()
+ * \see cl_page_operations::cpo_own()
+ * \see cl_page_own_try()
+ * \see cl_page_own
+ */
+static int cl_page_own0(const struct lu_env *env, struct cl_io *io,
+ struct cl_page *pg, int nonblock)
+{
+ int result;
+
+ PINVRNT(env, pg, !cl_page_is_owned(pg, io));
+
+ pg = cl_page_top(pg);
+ io = cl_io_top(io);
+
+ if (pg->cp_state == CPS_FREEING) {
+ result = -ENOENT;
+ } else {
+ result = CL_PAGE_INVOKE(env, pg, CL_PAGE_OP(cpo_own),
+ (const struct lu_env *,
+ const struct cl_page_slice *,
+ struct cl_io *, int),
+ io, nonblock);
+ if (result == 0) {
+ PASSERT(env, pg, pg->cp_owner == NULL);
+ PASSERT(env, pg, pg->cp_req == NULL);
+ pg->cp_owner = io;
+ pg->cp_task = current;
+ cl_page_owner_set(pg);
+ if (pg->cp_state != CPS_FREEING) {
+ cl_page_state_set(env, pg, CPS_OWNED);
+ } else {
+ cl_page_disown0(env, io, pg);
+ result = -ENOENT;
+ }
+ }
+ }
+ PINVRNT(env, pg, ergo(result == 0, cl_page_invariant(pg)));
+ return result;
+}
+
+/**
+ * Own a page, might be blocked.
+ *
+ * \see cl_page_own0()
+ */
+int cl_page_own(const struct lu_env *env, struct cl_io *io, struct cl_page *pg)
+{
+ return cl_page_own0(env, io, pg, 0);
+}
+EXPORT_SYMBOL(cl_page_own);
+
+/**
+ * Nonblock version of cl_page_own().
+ *
+ * \see cl_page_own0()
+ */
+int cl_page_own_try(const struct lu_env *env, struct cl_io *io,
+ struct cl_page *pg)
+{
+ return cl_page_own0(env, io, pg, 1);
+}
+EXPORT_SYMBOL(cl_page_own_try);
+
+
+/**
+ * Assume page ownership.
+ *
+ * Called when page is already locked by the hosting VM.
+ *
+ * \pre !cl_page_is_owned(pg, io)
+ * \post cl_page_is_owned(pg, io)
+ *
+ * \see cl_page_operations::cpo_assume()
+ */
+void cl_page_assume(const struct lu_env *env,
+ struct cl_io *io, struct cl_page *pg)
+{
+ PINVRNT(env, pg, cl_object_same(pg->cp_obj, io->ci_obj));
+
+ pg = cl_page_top(pg);
+ io = cl_io_top(io);
+
+ cl_page_invoid(env, io, pg, CL_PAGE_OP(cpo_assume));
+ PASSERT(env, pg, pg->cp_owner == NULL);
+ pg->cp_owner = io;
+ pg->cp_task = current;
+ cl_page_owner_set(pg);
+ cl_page_state_set(env, pg, CPS_OWNED);
+}
+EXPORT_SYMBOL(cl_page_assume);
+
+/**
+ * Releases page ownership without unlocking the page.
+ *
+ * Moves page into cl_page_state::CPS_CACHED without releasing a lock on the
+ * underlying VM page (as VM is supposed to do this itself).
+ *
+ * \pre cl_page_is_owned(pg, io)
+ * \post !cl_page_is_owned(pg, io)
+ *
+ * \see cl_page_assume()
+ */
+void cl_page_unassume(const struct lu_env *env,
+ struct cl_io *io, struct cl_page *pg)
+{
+ PINVRNT(env, pg, cl_page_is_owned(pg, io));
+ PINVRNT(env, pg, cl_page_invariant(pg));
+
+ pg = cl_page_top(pg);
+ io = cl_io_top(io);
+ cl_page_owner_clear(pg);
+ cl_page_state_set(env, pg, CPS_CACHED);
+ CL_PAGE_INVOID_REVERSE(env, pg, CL_PAGE_OP(cpo_unassume),
+ (const struct lu_env *,
+ const struct cl_page_slice *, struct cl_io *),
+ io);
+}
+EXPORT_SYMBOL(cl_page_unassume);
+
+/**
+ * Releases page ownership.
+ *
+ * Moves page into cl_page_state::CPS_CACHED.
+ *
+ * \pre cl_page_is_owned(pg, io)
+ * \post !cl_page_is_owned(pg, io)
+ *
+ * \see cl_page_own()
+ * \see cl_page_operations::cpo_disown()
+ */
+void cl_page_disown(const struct lu_env *env,
+ struct cl_io *io, struct cl_page *pg)
+{
+ PINVRNT(env, pg, cl_page_is_owned(pg, io));
+
+ pg = cl_page_top(pg);
+ io = cl_io_top(io);
+ cl_page_disown0(env, io, pg);
+}
+EXPORT_SYMBOL(cl_page_disown);
+
+/**
+ * Called when page is to be removed from the object, e.g., as a result of
+ * truncate.
+ *
+ * Calls cl_page_operations::cpo_discard() top-to-bottom.
+ *
+ * \pre cl_page_is_owned(pg, io)
+ *
+ * \see cl_page_operations::cpo_discard()
+ */
+void cl_page_discard(const struct lu_env *env,
+ struct cl_io *io, struct cl_page *pg)
+{
+ PINVRNT(env, pg, cl_page_is_owned(pg, io));
+ PINVRNT(env, pg, cl_page_invariant(pg));
+
+ cl_page_invoid(env, io, pg, CL_PAGE_OP(cpo_discard));
+}
+EXPORT_SYMBOL(cl_page_discard);
+
+/**
+ * Version of cl_page_delete() that can be called for not fully constructed
+ * pages, e.g,. in a error handling cl_page_find()->cl_page_delete0()
+ * path. Doesn't check page invariant.
+ */
+static void cl_page_delete0(const struct lu_env *env, struct cl_page *pg,
+ int radix)
+{
+ struct cl_page *tmp = pg;
+
+ PASSERT(env, pg, pg == cl_page_top(pg));
+ PASSERT(env, pg, pg->cp_state != CPS_FREEING);
+
+ /*
+ * Severe all ways to obtain new pointers to @pg.
+ */
+ cl_page_owner_clear(pg);
+
+ /*
+ * unexport the page firstly before freeing it so that
+ * the page content is considered to be invalid.
+ * We have to do this because a CPS_FREEING cl_page may
+ * be NOT under the protection of a cl_lock.
+ * Afterwards, if this page is found by other threads, then this
+ * page will be forced to reread.
+ */
+ cl_page_export(env, pg, 0);
+ cl_page_state_set0(env, pg, CPS_FREEING);
+
+ CL_PAGE_INVOID(env, pg, CL_PAGE_OP(cpo_delete),
+ (const struct lu_env *, const struct cl_page_slice *));
+
+ if (tmp->cp_type == CPT_CACHEABLE) {
+ if (!radix)
+ /* !radix means that @pg is not yet in the radix tree,
+ * skip removing it.
+ */
+ tmp = pg->cp_child;
+ for (; tmp != NULL; tmp = tmp->cp_child) {
+ void *value;
+ struct cl_object_header *hdr;
+
+ hdr = cl_object_header(tmp->cp_obj);
+ spin_lock(&hdr->coh_page_guard);
+ value = radix_tree_delete(&hdr->coh_tree,
+ tmp->cp_index);
+ PASSERT(env, tmp, value == tmp);
+ PASSERT(env, tmp, hdr->coh_pages > 0);
+ hdr->coh_pages--;
+ spin_unlock(&hdr->coh_page_guard);
+ cl_page_put(env, tmp);
+ }
+ }
+}
+
+/**
+ * Called when a decision is made to throw page out of memory.
+ *
+ * Notifies all layers about page destruction by calling
+ * cl_page_operations::cpo_delete() method top-to-bottom.
+ *
+ * Moves page into cl_page_state::CPS_FREEING state (this is the only place
+ * where transition to this state happens).
+ *
+ * Eliminates all venues through which new references to the page can be
+ * obtained:
+ *
+ * - removes page from the radix trees,
+ *
+ * - breaks linkage from VM page to cl_page.
+ *
+ * Once page reaches cl_page_state::CPS_FREEING, all remaining references will
+ * drain after some time, at which point page will be recycled.
+ *
+ * \pre pg == cl_page_top(pg)
+ * \pre VM page is locked
+ * \post pg->cp_state == CPS_FREEING
+ *
+ * \see cl_page_operations::cpo_delete()
+ */
+void cl_page_delete(const struct lu_env *env, struct cl_page *pg)
+{
+ PINVRNT(env, pg, cl_page_invariant(pg));
+ cl_page_delete0(env, pg, 1);
+}
+EXPORT_SYMBOL(cl_page_delete);
+
+/**
+ * Unmaps page from user virtual memory.
+ *
+ * Calls cl_page_operations::cpo_unmap() through all layers top-to-bottom. The
+ * layer responsible for VM interaction has to unmap page from user space
+ * virtual memory.
+ *
+ * \see cl_page_operations::cpo_unmap()
+ */
+int cl_page_unmap(const struct lu_env *env,
+ struct cl_io *io, struct cl_page *pg)
+{
+ PINVRNT(env, pg, cl_page_is_owned(pg, io));
+ PINVRNT(env, pg, cl_page_invariant(pg));
+
+ return cl_page_invoke(env, io, pg, CL_PAGE_OP(cpo_unmap));
+}
+EXPORT_SYMBOL(cl_page_unmap);
+
+/**
+ * Marks page up-to-date.
+ *
+ * Call cl_page_operations::cpo_export() through all layers top-to-bottom. The
+ * layer responsible for VM interaction has to mark/clear page as up-to-date
+ * by the \a uptodate argument.
+ *
+ * \see cl_page_operations::cpo_export()
+ */
+void cl_page_export(const struct lu_env *env, struct cl_page *pg, int uptodate)
+{
+ PINVRNT(env, pg, cl_page_invariant(pg));
+ CL_PAGE_INVOID(env, pg, CL_PAGE_OP(cpo_export),
+ (const struct lu_env *,
+ const struct cl_page_slice *, int), uptodate);
+}
+EXPORT_SYMBOL(cl_page_export);
+
+/**
+ * Returns true, iff \a pg is VM locked in a suitable sense by the calling
+ * thread.
+ */
+int cl_page_is_vmlocked(const struct lu_env *env, const struct cl_page *pg)
+{
+ int result;
+ const struct cl_page_slice *slice;
+
+ pg = cl_page_top_trusted((struct cl_page *)pg);
+ slice = container_of(pg->cp_layers.next,
+ const struct cl_page_slice, cpl_linkage);
+ PASSERT(env, pg, slice->cpl_ops->cpo_is_vmlocked != NULL);
+ /*
+ * Call ->cpo_is_vmlocked() directly instead of going through
+ * CL_PAGE_INVOKE(), because cl_page_is_vmlocked() is used by
+ * cl_page_invariant().
+ */
+ result = slice->cpl_ops->cpo_is_vmlocked(env, slice);
+ PASSERT(env, pg, result == -EBUSY || result == -ENODATA);
+ return result == -EBUSY;
+}
+EXPORT_SYMBOL(cl_page_is_vmlocked);
+
+static enum cl_page_state cl_req_type_state(enum cl_req_type crt)
+{
+ return crt == CRT_WRITE ? CPS_PAGEOUT : CPS_PAGEIN;
+}
+
+static void cl_page_io_start(const struct lu_env *env,
+ struct cl_page *pg, enum cl_req_type crt)
+{
+ /*
+ * Page is queued for IO, change its state.
+ */
+ cl_page_owner_clear(pg);
+ cl_page_state_set(env, pg, cl_req_type_state(crt));
+}
+
+/**
+ * Prepares page for immediate transfer. cl_page_operations::cpo_prep() is
+ * called top-to-bottom. Every layer either agrees to submit this page (by
+ * returning 0), or requests to omit this page (by returning -EALREADY). Layer
+ * handling interactions with the VM also has to inform VM that page is under
+ * transfer now.
+ */
+int cl_page_prep(const struct lu_env *env, struct cl_io *io,
+ struct cl_page *pg, enum cl_req_type crt)
+{
+ int result;
+
+ PINVRNT(env, pg, cl_page_is_owned(pg, io));
+ PINVRNT(env, pg, cl_page_invariant(pg));
+ PINVRNT(env, pg, crt < CRT_NR);
+
+ /*
+ * XXX this has to be called bottom-to-top, so that llite can set up
+ * PG_writeback without risking other layers deciding to skip this
+ * page.
+ */
+ if (crt >= CRT_NR)
+ return -EINVAL;
+ result = cl_page_invoke(env, io, pg, CL_PAGE_OP(io[crt].cpo_prep));
+ if (result == 0)
+ cl_page_io_start(env, pg, crt);
+
+ KLASSERT(ergo(crt == CRT_WRITE && pg->cp_type == CPT_CACHEABLE,
+ equi(result == 0,
+ PageWriteback(cl_page_vmpage(env, pg)))));
+ CL_PAGE_HEADER(D_TRACE, env, pg, "%d %d\n", crt, result);
+ return result;
+}
+EXPORT_SYMBOL(cl_page_prep);
+
+/**
+ * Notify layers about transfer completion.
+ *
+ * Invoked by transfer sub-system (which is a part of osc) to notify layers
+ * that a transfer, of which this page is a part of has completed.
+ *
+ * Completion call-backs are executed in the bottom-up order, so that
+ * uppermost layer (llite), responsible for the VFS/VM interaction runs last
+ * and can release locks safely.
+ *
+ * \pre pg->cp_state == CPS_PAGEIN || pg->cp_state == CPS_PAGEOUT
+ * \post pg->cp_state == CPS_CACHED
+ *
+ * \see cl_page_operations::cpo_completion()
+ */
+void cl_page_completion(const struct lu_env *env,
+ struct cl_page *pg, enum cl_req_type crt, int ioret)
+{
+ struct cl_sync_io *anchor = pg->cp_sync_io;
+
+ PASSERT(env, pg, crt < CRT_NR);
+ /* cl_page::cp_req already cleared by the caller (osc_completion()) */
+ PASSERT(env, pg, pg->cp_req == NULL);
+ PASSERT(env, pg, pg->cp_state == cl_req_type_state(crt));
+
+ CL_PAGE_HEADER(D_TRACE, env, pg, "%d %d\n", crt, ioret);
+ if (crt == CRT_READ && ioret == 0) {
+ PASSERT(env, pg, !(pg->cp_flags & CPF_READ_COMPLETED));
+ pg->cp_flags |= CPF_READ_COMPLETED;
+ }
+
+ cl_page_state_set(env, pg, CPS_CACHED);
+ if (crt >= CRT_NR)
+ return;
+ CL_PAGE_INVOID_REVERSE(env, pg, CL_PAGE_OP(io[crt].cpo_completion),
+ (const struct lu_env *,
+ const struct cl_page_slice *, int), ioret);
+ if (anchor) {
+ LASSERT(cl_page_is_vmlocked(env, pg));
+ LASSERT(pg->cp_sync_io == anchor);
+ pg->cp_sync_io = NULL;
+ }
+ /*
+ * As page->cp_obj is pinned by a reference from page->cp_req, it is
+ * safe to call cl_page_put() without risking object destruction in a
+ * non-blocking context.
+ */
+ cl_page_put(env, pg);
+
+ if (anchor)
+ cl_sync_io_note(anchor, ioret);
+}
+EXPORT_SYMBOL(cl_page_completion);
+
+/**
+ * Notify layers that transfer formation engine decided to yank this page from
+ * the cache and to make it a part of a transfer.
+ *
+ * \pre pg->cp_state == CPS_CACHED
+ * \post pg->cp_state == CPS_PAGEIN || pg->cp_state == CPS_PAGEOUT
+ *
+ * \see cl_page_operations::cpo_make_ready()
+ */
+int cl_page_make_ready(const struct lu_env *env, struct cl_page *pg,
+ enum cl_req_type crt)
+{
+ int result;
+
+ PINVRNT(env, pg, crt < CRT_NR);
+
+ if (crt >= CRT_NR)
+ return -EINVAL;
+ result = CL_PAGE_INVOKE(env, pg, CL_PAGE_OP(io[crt].cpo_make_ready),
+ (const struct lu_env *,
+ const struct cl_page_slice *));
+ if (result == 0) {
+ PASSERT(env, pg, pg->cp_state == CPS_CACHED);
+ cl_page_io_start(env, pg, crt);
+ }
+ CL_PAGE_HEADER(D_TRACE, env, pg, "%d %d\n", crt, result);
+ return result;
+}
+EXPORT_SYMBOL(cl_page_make_ready);
+
+/**
+ * Notify layers that high level io decided to place this page into a cache
+ * for future transfer.
+ *
+ * The layer implementing transfer engine (osc) has to register this page in
+ * its queues.
+ *
+ * \pre cl_page_is_owned(pg, io)
+ * \post cl_page_is_owned(pg, io)
+ *
+ * \see cl_page_operations::cpo_cache_add()
+ */
+int cl_page_cache_add(const struct lu_env *env, struct cl_io *io,
+ struct cl_page *pg, enum cl_req_type crt)
+{
+ const struct cl_page_slice *scan;
+ int result = 0;
+
+ PINVRNT(env, pg, crt < CRT_NR);
+ PINVRNT(env, pg, cl_page_is_owned(pg, io));
+ PINVRNT(env, pg, cl_page_invariant(pg));
+
+ if (crt >= CRT_NR)
+ return -EINVAL;
+
+ list_for_each_entry(scan, &pg->cp_layers, cpl_linkage) {
+ if (scan->cpl_ops->io[crt].cpo_cache_add == NULL)
+ continue;
+
+ result = scan->cpl_ops->io[crt].cpo_cache_add(env, scan, io);
+ if (result != 0)
+ break;
+ }
+ CL_PAGE_HEADER(D_TRACE, env, pg, "%d %d\n", crt, result);
+ return result;
+}
+EXPORT_SYMBOL(cl_page_cache_add);
+
+/**
+ * Called if a pge is being written back by kernel's intention.
+ *
+ * \pre cl_page_is_owned(pg, io)
+ * \post ergo(result == 0, pg->cp_state == CPS_PAGEOUT)
+ *
+ * \see cl_page_operations::cpo_flush()
+ */
+int cl_page_flush(const struct lu_env *env, struct cl_io *io,
+ struct cl_page *pg)
+{
+ int result;
+
+ PINVRNT(env, pg, cl_page_is_owned(pg, io));
+ PINVRNT(env, pg, cl_page_invariant(pg));
+
+ result = cl_page_invoke(env, io, pg, CL_PAGE_OP(cpo_flush));
+
+ CL_PAGE_HEADER(D_TRACE, env, pg, "%d\n", result);
+ return result;
+}
+EXPORT_SYMBOL(cl_page_flush);
+
+/**
+ * Checks whether page is protected by any extent lock is at least required
+ * mode.
+ *
+ * \return the same as in cl_page_operations::cpo_is_under_lock() method.
+ * \see cl_page_operations::cpo_is_under_lock()
+ */
+int cl_page_is_under_lock(const struct lu_env *env, struct cl_io *io,
+ struct cl_page *page)
+{
+ int rc;
+
+ PINVRNT(env, page, cl_page_invariant(page));
+
+ rc = CL_PAGE_INVOKE(env, page, CL_PAGE_OP(cpo_is_under_lock),
+ (const struct lu_env *,
+ const struct cl_page_slice *, struct cl_io *),
+ io);
+ PASSERT(env, page, rc != 0);
+ return rc;
+}
+EXPORT_SYMBOL(cl_page_is_under_lock);
+
+static int page_prune_cb(const struct lu_env *env, struct cl_io *io,
+ struct cl_page *page, void *cbdata)
+{
+ cl_page_own(env, io, page);
+ cl_page_unmap(env, io, page);
+ cl_page_discard(env, io, page);
+ cl_page_disown(env, io, page);
+ return CLP_GANG_OKAY;
+}
+
+/**
+ * Purges all cached pages belonging to the object \a obj.
+ */
+int cl_pages_prune(const struct lu_env *env, struct cl_object *clobj)
+{
+ struct cl_thread_info *info;
+ struct cl_object *obj = cl_object_top(clobj);
+ struct cl_io *io;
+ int result;
+
+ info = cl_env_info(env);
+ io = &info->clt_io;
+
+ /*
+ * initialize the io. This is ugly since we never do IO in this
+ * function, we just make cl_page_list functions happy. -jay
+ */
+ io->ci_obj = obj;
+ io->ci_ignore_layout = 1;
+ result = cl_io_init(env, io, CIT_MISC, obj);
+ if (result != 0) {
+ cl_io_fini(env, io);
+ return io->ci_result;
+ }
+
+ do {
+ result = cl_page_gang_lookup(env, obj, io, 0, CL_PAGE_EOF,
+ page_prune_cb, NULL);
+ if (result == CLP_GANG_RESCHED)
+ cond_resched();
+ } while (result != CLP_GANG_OKAY);
+
+ cl_io_fini(env, io);
+ return result;
+}
+EXPORT_SYMBOL(cl_pages_prune);
+
+/**
+ * Tells transfer engine that only part of a page is to be transmitted.
+ *
+ * \see cl_page_operations::cpo_clip()
+ */
+void cl_page_clip(const struct lu_env *env, struct cl_page *pg,
+ int from, int to)
+{
+ PINVRNT(env, pg, cl_page_invariant(pg));
+
+ CL_PAGE_HEADER(D_TRACE, env, pg, "%d %d\n", from, to);
+ CL_PAGE_INVOID(env, pg, CL_PAGE_OP(cpo_clip),
+ (const struct lu_env *,
+ const struct cl_page_slice *,int, int),
+ from, to);
+}
+EXPORT_SYMBOL(cl_page_clip);
+
+/**
+ * Prints human readable representation of \a pg to the \a f.
+ */
+void cl_page_header_print(const struct lu_env *env, void *cookie,
+ lu_printer_t printer, const struct cl_page *pg)
+{
+ (*printer)(env, cookie,
+ "page@%p[%d %p:%lu ^%p_%p %d %d %d %p %p %#x]\n",
+ pg, atomic_read(&pg->cp_ref), pg->cp_obj,
+ pg->cp_index, pg->cp_parent, pg->cp_child,
+ pg->cp_state, pg->cp_error, pg->cp_type,
+ pg->cp_owner, pg->cp_req, pg->cp_flags);
+}
+EXPORT_SYMBOL(cl_page_header_print);
+
+/**
+ * Prints human readable representation of \a pg to the \a f.
+ */
+void cl_page_print(const struct lu_env *env, void *cookie,
+ lu_printer_t printer, const struct cl_page *pg)
+{
+ struct cl_page *scan;
+
+ for (scan = cl_page_top((struct cl_page *)pg);
+ scan != NULL; scan = scan->cp_child)
+ cl_page_header_print(env, cookie, printer, scan);
+ CL_PAGE_INVOKE(env, (struct cl_page *)pg, CL_PAGE_OP(cpo_print),
+ (const struct lu_env *env,
+ const struct cl_page_slice *slice,
+ void *cookie, lu_printer_t p), cookie, printer);
+ (*printer)(env, cookie, "end page@%p\n", pg);
+}
+EXPORT_SYMBOL(cl_page_print);
+
+/**
+ * Cancel a page which is still in a transfer.
+ */
+int cl_page_cancel(const struct lu_env *env, struct cl_page *page)
+{
+ return CL_PAGE_INVOKE(env, page, CL_PAGE_OP(cpo_cancel),
+ (const struct lu_env *,
+ const struct cl_page_slice *));
+}
+EXPORT_SYMBOL(cl_page_cancel);
+
+/**
+ * Converts a byte offset within object \a obj into a page index.
+ */
+loff_t cl_offset(const struct cl_object *obj, pgoff_t idx)
+{
+ /*
+ * XXX for now.
+ */
+ return (loff_t)idx << PAGE_CACHE_SHIFT;
+}
+EXPORT_SYMBOL(cl_offset);
+
+/**
+ * Converts a page index into a byte offset within object \a obj.
+ */
+pgoff_t cl_index(const struct cl_object *obj, loff_t offset)
+{
+ /*
+ * XXX for now.
+ */
+ return offset >> PAGE_CACHE_SHIFT;
+}
+EXPORT_SYMBOL(cl_index);
+
+int cl_page_size(const struct cl_object *obj)
+{
+ return 1 << PAGE_CACHE_SHIFT;
+}
+EXPORT_SYMBOL(cl_page_size);
+
+/**
+ * Adds page slice to the compound page.
+ *
+ * This is called by cl_object_operations::coo_page_init() methods to add a
+ * per-layer state to the page. New state is added at the end of
+ * cl_page::cp_layers list, that is, it is at the bottom of the stack.
+ *
+ * \see cl_lock_slice_add(), cl_req_slice_add(), cl_io_slice_add()
+ */
+void cl_page_slice_add(struct cl_page *page, struct cl_page_slice *slice,
+ struct cl_object *obj,
+ const struct cl_page_operations *ops)
+{
+ list_add_tail(&slice->cpl_linkage, &page->cp_layers);
+ slice->cpl_obj = obj;
+ slice->cpl_ops = ops;
+ slice->cpl_page = page;
+}
+EXPORT_SYMBOL(cl_page_slice_add);
+
+int cl_page_init(void)
+{
+ return 0;
+}
+
+void cl_page_fini(void)
+{
+}
diff --git a/drivers/staging/lustre/lustre/obdclass/class_obd.c b/drivers/staging/lustre/lustre/obdclass/class_obd.c
new file mode 100644
index 000000000..d4b74b670
--- /dev/null
+++ b/drivers/staging/lustre/lustre/obdclass/class_obd.c
@@ -0,0 +1,704 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2011, 2012, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ */
+
+#define DEBUG_SUBSYSTEM S_CLASS
+# include <linux/atomic.h>
+
+#include "../include/obd_support.h"
+#include "../include/obd_class.h"
+#include "../../include/linux/lnet/lnetctl.h"
+#include "../include/lustre_debug.h"
+#include "../include/lprocfs_status.h"
+#include "../include/lustre/lustre_build_version.h"
+#include <linux/list.h>
+#include "../include/cl_object.h"
+#include "llog_internal.h"
+
+
+struct obd_device *obd_devs[MAX_OBD_DEVICES];
+EXPORT_SYMBOL(obd_devs);
+struct list_head obd_types;
+DEFINE_RWLOCK(obd_dev_lock);
+
+__u64 obd_max_pages = 0;
+EXPORT_SYMBOL(obd_max_pages);
+__u64 obd_max_alloc = 0;
+EXPORT_SYMBOL(obd_max_alloc);
+__u64 obd_alloc;
+EXPORT_SYMBOL(obd_alloc);
+__u64 obd_pages;
+EXPORT_SYMBOL(obd_pages);
+static DEFINE_SPINLOCK(obd_updatemax_lock);
+
+/* The following are visible and mutable through /proc/sys/lustre/. */
+unsigned int obd_alloc_fail_rate = 0;
+EXPORT_SYMBOL(obd_alloc_fail_rate);
+unsigned int obd_debug_peer_on_timeout;
+EXPORT_SYMBOL(obd_debug_peer_on_timeout);
+unsigned int obd_dump_on_timeout;
+EXPORT_SYMBOL(obd_dump_on_timeout);
+unsigned int obd_dump_on_eviction;
+EXPORT_SYMBOL(obd_dump_on_eviction);
+unsigned int obd_max_dirty_pages = 256;
+EXPORT_SYMBOL(obd_max_dirty_pages);
+atomic_t obd_dirty_pages;
+EXPORT_SYMBOL(obd_dirty_pages);
+unsigned int obd_timeout = OBD_TIMEOUT_DEFAULT; /* seconds */
+EXPORT_SYMBOL(obd_timeout);
+unsigned int ldlm_timeout = LDLM_TIMEOUT_DEFAULT; /* seconds */
+EXPORT_SYMBOL(ldlm_timeout);
+unsigned int obd_timeout_set;
+EXPORT_SYMBOL(obd_timeout_set);
+unsigned int ldlm_timeout_set;
+EXPORT_SYMBOL(ldlm_timeout_set);
+/* Adaptive timeout defs here instead of ptlrpc module for /proc/sys/ access */
+unsigned int at_min = 0;
+EXPORT_SYMBOL(at_min);
+unsigned int at_max = 600;
+EXPORT_SYMBOL(at_max);
+unsigned int at_history = 600;
+EXPORT_SYMBOL(at_history);
+int at_early_margin = 5;
+EXPORT_SYMBOL(at_early_margin);
+int at_extra = 30;
+EXPORT_SYMBOL(at_extra);
+
+atomic_t obd_dirty_transit_pages;
+EXPORT_SYMBOL(obd_dirty_transit_pages);
+
+char obd_jobid_var[JOBSTATS_JOBID_VAR_MAX_LEN + 1] = JOBSTATS_DISABLE;
+EXPORT_SYMBOL(obd_jobid_var);
+
+char obd_jobid_node[JOBSTATS_JOBID_SIZE + 1];
+
+/* Get jobid of current process from stored variable or calculate
+ * it from pid and user_id.
+ *
+ * Historically this was also done by reading the environment variable
+ * stored in between the "env_start" & "env_end" of task struct.
+ * This is now deprecated.
+ */
+int lustre_get_jobid(char *jobid)
+{
+ memset(jobid, 0, JOBSTATS_JOBID_SIZE);
+ /* Jobstats isn't enabled */
+ if (strcmp(obd_jobid_var, JOBSTATS_DISABLE) == 0)
+ return 0;
+
+ /* Use process name + fsuid as jobid */
+ if (strcmp(obd_jobid_var, JOBSTATS_PROCNAME_UID) == 0) {
+ snprintf(jobid, JOBSTATS_JOBID_SIZE, "%s.%u",
+ current_comm(),
+ from_kuid(&init_user_ns, current_fsuid()));
+ return 0;
+ }
+
+ /* Whole node dedicated to single job */
+ if (strcmp(obd_jobid_var, JOBSTATS_NODELOCAL) == 0) {
+ strcpy(jobid, obd_jobid_node);
+ return 0;
+ }
+
+ return -ENOENT;
+}
+EXPORT_SYMBOL(lustre_get_jobid);
+
+int obd_alloc_fail(const void *ptr, const char *name, const char *type,
+ size_t size, const char *file, int line)
+{
+ if (ptr == NULL ||
+ (cfs_rand() & OBD_ALLOC_FAIL_MASK) < obd_alloc_fail_rate) {
+ CERROR("%s%salloc of %s (%llu bytes) failed at %s:%d\n",
+ ptr ? "force " :"", type, name, (__u64)size, file,
+ line);
+ CERROR("%llu total bytes and %llu total pages (%llu bytes) allocated by Lustre, %d total bytes by LNET\n",
+ obd_memory_sum(),
+ obd_pages_sum() << PAGE_CACHE_SHIFT,
+ obd_pages_sum(),
+ atomic_read(&libcfs_kmemory));
+ return 1;
+ }
+ return 0;
+}
+EXPORT_SYMBOL(obd_alloc_fail);
+
+static inline void obd_data2conn(struct lustre_handle *conn,
+ struct obd_ioctl_data *data)
+{
+ memset(conn, 0, sizeof(*conn));
+ conn->cookie = data->ioc_cookie;
+}
+
+static inline void obd_conn2data(struct obd_ioctl_data *data,
+ struct lustre_handle *conn)
+{
+ data->ioc_cookie = conn->cookie;
+}
+
+int class_resolve_dev_name(__u32 len, const char *name)
+{
+ int rc;
+ int dev;
+
+ if (!len || !name) {
+ CERROR("No name passed,!\n");
+ rc = -EINVAL;
+ goto out;
+ }
+ if (name[len - 1] != 0) {
+ CERROR("Name not nul terminated!\n");
+ rc = -EINVAL;
+ goto out;
+ }
+
+ CDEBUG(D_IOCTL, "device name %s\n", name);
+ dev = class_name2dev(name);
+ if (dev == -1) {
+ CDEBUG(D_IOCTL, "No device for name %s!\n", name);
+ rc = -EINVAL;
+ goto out;
+ }
+
+ CDEBUG(D_IOCTL, "device name %s, dev %d\n", name, dev);
+ rc = dev;
+
+out:
+ return rc;
+}
+
+int class_handle_ioctl(unsigned int cmd, unsigned long arg)
+{
+ char *buf = NULL;
+ struct obd_ioctl_data *data;
+ struct libcfs_debug_ioctl_data *debug_data;
+ struct obd_device *obd = NULL;
+ int err = 0, len = 0;
+
+ /* only for debugging */
+ if (cmd == LIBCFS_IOC_DEBUG_MASK) {
+ debug_data = (struct libcfs_debug_ioctl_data *)arg;
+ libcfs_subsystem_debug = debug_data->subs;
+ libcfs_debug = debug_data->debug;
+ return 0;
+ }
+
+ CDEBUG(D_IOCTL, "cmd = %x\n", cmd);
+ if (obd_ioctl_getdata(&buf, &len, (void *)arg)) {
+ CERROR("OBD ioctl: data error\n");
+ return -EINVAL;
+ }
+ data = (struct obd_ioctl_data *)buf;
+
+ switch (cmd) {
+ case OBD_IOC_PROCESS_CFG: {
+ struct lustre_cfg *lcfg;
+
+ if (!data->ioc_plen1 || !data->ioc_pbuf1) {
+ CERROR("No config buffer passed!\n");
+ err = -EINVAL;
+ goto out;
+ }
+ OBD_ALLOC(lcfg, data->ioc_plen1);
+ if (lcfg == NULL) {
+ err = -ENOMEM;
+ goto out;
+ }
+ err = copy_from_user(lcfg, data->ioc_pbuf1,
+ data->ioc_plen1);
+ if (!err)
+ err = lustre_cfg_sanity_check(lcfg, data->ioc_plen1);
+ if (!err)
+ err = class_process_config(lcfg);
+
+ OBD_FREE(lcfg, data->ioc_plen1);
+ goto out;
+ }
+
+ case OBD_GET_VERSION:
+ if (!data->ioc_inlbuf1) {
+ CERROR("No buffer passed in ioctl\n");
+ err = -EINVAL;
+ goto out;
+ }
+
+ if (strlen(BUILD_VERSION) + 1 > data->ioc_inllen1) {
+ CERROR("ioctl buffer too small to hold version\n");
+ err = -EINVAL;
+ goto out;
+ }
+
+ memcpy(data->ioc_bulk, BUILD_VERSION,
+ strlen(BUILD_VERSION) + 1);
+
+ err = obd_ioctl_popdata((void *)arg, data, len);
+ if (err)
+ err = -EFAULT;
+ goto out;
+
+ case OBD_IOC_NAME2DEV: {
+ /* Resolve a device name. This does not change the
+ * currently selected device.
+ */
+ int dev;
+
+ dev = class_resolve_dev_name(data->ioc_inllen1,
+ data->ioc_inlbuf1);
+ data->ioc_dev = dev;
+ if (dev < 0) {
+ err = -EINVAL;
+ goto out;
+ }
+
+ err = obd_ioctl_popdata((void *)arg, data, sizeof(*data));
+ if (err)
+ err = -EFAULT;
+ goto out;
+ }
+
+ case OBD_IOC_UUID2DEV: {
+ /* Resolve a device uuid. This does not change the
+ * currently selected device.
+ */
+ int dev;
+ struct obd_uuid uuid;
+
+ if (!data->ioc_inllen1 || !data->ioc_inlbuf1) {
+ CERROR("No UUID passed!\n");
+ err = -EINVAL;
+ goto out;
+ }
+ if (data->ioc_inlbuf1[data->ioc_inllen1 - 1] != 0) {
+ CERROR("UUID not NUL terminated!\n");
+ err = -EINVAL;
+ goto out;
+ }
+
+ CDEBUG(D_IOCTL, "device name %s\n", data->ioc_inlbuf1);
+ obd_str2uuid(&uuid, data->ioc_inlbuf1);
+ dev = class_uuid2dev(&uuid);
+ data->ioc_dev = dev;
+ if (dev == -1) {
+ CDEBUG(D_IOCTL, "No device for UUID %s!\n",
+ data->ioc_inlbuf1);
+ err = -EINVAL;
+ goto out;
+ }
+
+ CDEBUG(D_IOCTL, "device name %s, dev %d\n", data->ioc_inlbuf1,
+ dev);
+ err = obd_ioctl_popdata((void *)arg, data, sizeof(*data));
+ if (err)
+ err = -EFAULT;
+ goto out;
+ }
+
+ case OBD_IOC_CLOSE_UUID: {
+ CDEBUG(D_IOCTL, "closing all connections to uuid %s (NOOP)\n",
+ data->ioc_inlbuf1);
+ err = 0;
+ goto out;
+ }
+
+ case OBD_IOC_GETDEVICE: {
+ int index = data->ioc_count;
+ char *status, *str;
+
+ if (!data->ioc_inlbuf1) {
+ CERROR("No buffer passed in ioctl\n");
+ err = -EINVAL;
+ goto out;
+ }
+ if (data->ioc_inllen1 < 128) {
+ CERROR("ioctl buffer too small to hold version\n");
+ err = -EINVAL;
+ goto out;
+ }
+
+ obd = class_num2obd(index);
+ if (!obd) {
+ err = -ENOENT;
+ goto out;
+ }
+
+ if (obd->obd_stopping)
+ status = "ST";
+ else if (obd->obd_set_up)
+ status = "UP";
+ else if (obd->obd_attached)
+ status = "AT";
+ else
+ status = "--";
+ str = (char *)data->ioc_bulk;
+ snprintf(str, len - sizeof(*data), "%3d %s %s %s %s %d",
+ (int)index, status, obd->obd_type->typ_name,
+ obd->obd_name, obd->obd_uuid.uuid,
+ atomic_read(&obd->obd_refcount));
+ err = obd_ioctl_popdata((void *)arg, data, len);
+
+ err = 0;
+ goto out;
+ }
+
+ }
+
+ if (data->ioc_dev == OBD_DEV_BY_DEVNAME) {
+ if (data->ioc_inllen4 <= 0 || data->ioc_inlbuf4 == NULL) {
+ err = -EINVAL;
+ goto out;
+ }
+ if (strnlen(data->ioc_inlbuf4, MAX_OBD_NAME) >= MAX_OBD_NAME) {
+ err = -EINVAL;
+ goto out;
+ }
+ obd = class_name2obd(data->ioc_inlbuf4);
+ } else if (data->ioc_dev < class_devno_max()) {
+ obd = class_num2obd(data->ioc_dev);
+ } else {
+ CERROR("OBD ioctl: No device\n");
+ err = -EINVAL;
+ goto out;
+ }
+
+ if (obd == NULL) {
+ CERROR("OBD ioctl : No Device %d\n", data->ioc_dev);
+ err = -EINVAL;
+ goto out;
+ }
+ LASSERT(obd->obd_magic == OBD_DEVICE_MAGIC);
+
+ if (!obd->obd_set_up || obd->obd_stopping) {
+ CERROR("OBD ioctl: device not setup %d\n", data->ioc_dev);
+ err = -EINVAL;
+ goto out;
+ }
+
+ switch (cmd) {
+ case OBD_IOC_NO_TRANSNO: {
+ if (!obd->obd_attached) {
+ CERROR("Device %d not attached\n", obd->obd_minor);
+ err = -ENODEV;
+ goto out;
+ }
+ CDEBUG(D_HA, "%s: disabling committed-transno notification\n",
+ obd->obd_name);
+ obd->obd_no_transno = 1;
+ err = 0;
+ goto out;
+ }
+
+ default: {
+ err = obd_iocontrol(cmd, obd->obd_self_export, len, data, NULL);
+ if (err)
+ goto out;
+
+ err = obd_ioctl_popdata((void *)arg, data, len);
+ if (err)
+ err = -EFAULT;
+ goto out;
+ }
+ }
+
+ out:
+ if (buf)
+ obd_ioctl_freedata(buf, len);
+ return err;
+} /* class_handle_ioctl */
+
+#define OBD_INIT_CHECK
+int obd_init_checks(void)
+{
+ __u64 u64val, div64val;
+ char buf[64];
+ int len, ret = 0;
+
+ CDEBUG(D_INFO, "LPU64=%s, LPD64=%s, LPX64=%s\n", "%llu", "%lld", "%#llx");
+
+ CDEBUG(D_INFO, "OBD_OBJECT_EOF = %#llx\n", (__u64)OBD_OBJECT_EOF);
+
+ u64val = OBD_OBJECT_EOF;
+ CDEBUG(D_INFO, "u64val OBD_OBJECT_EOF = %#llx\n", u64val);
+ if (u64val != OBD_OBJECT_EOF) {
+ CERROR("__u64 %#llx(%d) != 0xffffffffffffffff\n",
+ u64val, (int)sizeof(u64val));
+ ret = -EINVAL;
+ }
+ len = snprintf(buf, sizeof(buf), "%#llx", u64val);
+ if (len != 18) {
+ CWARN("LPX64 wrong length! strlen(%s)=%d != 18\n", buf, len);
+ ret = -EINVAL;
+ }
+
+ div64val = OBD_OBJECT_EOF;
+ CDEBUG(D_INFO, "u64val OBD_OBJECT_EOF = %#llx\n", u64val);
+ if (u64val != OBD_OBJECT_EOF) {
+ CERROR("__u64 %#llx(%d) != 0xffffffffffffffff\n",
+ u64val, (int)sizeof(u64val));
+ ret = -EOVERFLOW;
+ }
+ if (u64val >> 8 != OBD_OBJECT_EOF >> 8) {
+ CERROR("__u64 %#llx(%d) != 0xffffffffffffffff\n",
+ u64val, (int)sizeof(u64val));
+ return -EOVERFLOW;
+ }
+ if (do_div(div64val, 256) != (u64val & 255)) {
+ CERROR("do_div(%#llx,256) != %llu\n", u64val, u64val &255);
+ return -EOVERFLOW;
+ }
+ if (u64val >> 8 != div64val) {
+ CERROR("do_div(%#llx,256) %llu != %llu\n",
+ u64val, div64val, u64val >> 8);
+ return -EOVERFLOW;
+ }
+ len = snprintf(buf, sizeof(buf), "%#llx", u64val);
+ if (len != 18) {
+ CWARN("LPX64 wrong length! strlen(%s)=%d != 18\n", buf, len);
+ ret = -EINVAL;
+ }
+ len = snprintf(buf, sizeof(buf), "%llu", u64val);
+ if (len != 20) {
+ CWARN("LPU64 wrong length! strlen(%s)=%d != 20\n", buf, len);
+ ret = -EINVAL;
+ }
+ len = snprintf(buf, sizeof(buf), "%lld", u64val);
+ if (len != 2) {
+ CWARN("LPD64 wrong length! strlen(%s)=%d != 2\n", buf, len);
+ ret = -EINVAL;
+ }
+ if ((u64val & ~CFS_PAGE_MASK) >= PAGE_CACHE_SIZE) {
+ CWARN("mask failed: u64val %llu >= %llu\n", u64val,
+ (__u64)PAGE_CACHE_SIZE);
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+#if defined (CONFIG_PROC_FS)
+extern int class_procfs_init(void);
+extern int class_procfs_clean(void);
+#else
+static inline int class_procfs_init(void)
+{ return 0; }
+static inline int class_procfs_clean(void)
+{ return 0; }
+#endif
+
+static int __init init_obdclass(void)
+{
+ int i, err;
+ int lustre_register_fs(void);
+
+ for (i = CAPA_SITE_CLIENT; i < CAPA_SITE_MAX; i++)
+ INIT_LIST_HEAD(&capa_list[i]);
+
+ LCONSOLE_INFO("Lustre: Build Version: "BUILD_VERSION"\n");
+
+ spin_lock_init(&obd_types_lock);
+ obd_zombie_impexp_init();
+
+ if (IS_ENABLED(CONFIG_PROC_FS)) {
+ obd_memory = lprocfs_alloc_stats(OBD_STATS_NUM,
+ LPROCFS_STATS_FLAG_NONE |
+ LPROCFS_STATS_FLAG_IRQ_SAFE);
+
+ if (obd_memory == NULL) {
+ CERROR("kmalloc of 'obd_memory' failed\n");
+ return -ENOMEM;
+ }
+
+ lprocfs_counter_init(obd_memory, OBD_MEMORY_STAT,
+ LPROCFS_CNTR_AVGMINMAX,
+ "memused", "bytes");
+ lprocfs_counter_init(obd_memory, OBD_MEMORY_PAGES_STAT,
+ LPROCFS_CNTR_AVGMINMAX,
+ "pagesused", "pages");
+ }
+
+ err = obd_init_checks();
+ if (err == -EOVERFLOW)
+ return err;
+
+ class_init_uuidlist();
+ err = class_handle_init();
+ if (err)
+ return err;
+
+ INIT_LIST_HEAD(&obd_types);
+
+ err = misc_register(&obd_psdev);
+ if (err) {
+ CERROR("cannot register %d err %d\n", OBD_DEV_MINOR, err);
+ return err;
+ }
+
+ /* This struct is already zeroed for us (static global) */
+ for (i = 0; i < class_devno_max(); i++)
+ obd_devs[i] = NULL;
+
+ /* Default the dirty page cache cap to 1/2 of system memory.
+ * For clients with less memory, a larger fraction is needed
+ * for other purposes (mostly for BGL). */
+ if (totalram_pages <= 512 << (20 - PAGE_CACHE_SHIFT))
+ obd_max_dirty_pages = totalram_pages / 4;
+ else
+ obd_max_dirty_pages = totalram_pages / 2;
+
+ err = obd_init_caches();
+ if (err)
+ return err;
+
+ obd_sysctl_init();
+
+ err = class_procfs_init();
+ if (err)
+ return err;
+
+ err = lu_global_init();
+ if (err)
+ return err;
+
+ err = cl_global_init();
+ if (err != 0)
+ return err;
+
+
+ err = llog_info_init();
+ if (err)
+ return err;
+
+ err = lustre_register_fs();
+
+ return err;
+}
+
+void obd_update_maxusage(void)
+{
+ __u64 max1, max2;
+
+ max1 = obd_pages_sum();
+ max2 = obd_memory_sum();
+
+ spin_lock(&obd_updatemax_lock);
+ if (max1 > obd_max_pages)
+ obd_max_pages = max1;
+ if (max2 > obd_max_alloc)
+ obd_max_alloc = max2;
+ spin_unlock(&obd_updatemax_lock);
+}
+EXPORT_SYMBOL(obd_update_maxusage);
+
+#if defined (CONFIG_PROC_FS)
+__u64 obd_memory_max(void)
+{
+ __u64 ret;
+
+ spin_lock(&obd_updatemax_lock);
+ ret = obd_max_alloc;
+ spin_unlock(&obd_updatemax_lock);
+
+ return ret;
+}
+EXPORT_SYMBOL(obd_memory_max);
+
+__u64 obd_pages_max(void)
+{
+ __u64 ret;
+
+ spin_lock(&obd_updatemax_lock);
+ ret = obd_max_pages;
+ spin_unlock(&obd_updatemax_lock);
+
+ return ret;
+}
+EXPORT_SYMBOL(obd_pages_max);
+#endif
+
+/* liblustre doesn't call cleanup_obdclass, apparently. we carry on in this
+ * ifdef to the end of the file to cover module and versioning goo.*/
+static void cleanup_obdclass(void)
+{
+ int i;
+ int lustre_unregister_fs(void);
+ __u64 memory_leaked, pages_leaked;
+ __u64 memory_max, pages_max;
+
+ lustre_unregister_fs();
+
+ misc_deregister(&obd_psdev);
+ for (i = 0; i < class_devno_max(); i++) {
+ struct obd_device *obd = class_num2obd(i);
+ if (obd && obd->obd_set_up &&
+ OBT(obd) && OBP(obd, detach)) {
+ /* XXX should this call generic detach otherwise? */
+ LASSERT(obd->obd_magic == OBD_DEVICE_MAGIC);
+ OBP(obd, detach)(obd);
+ }
+ }
+ llog_info_fini();
+ cl_global_fini();
+ lu_global_fini();
+
+ obd_cleanup_caches();
+ obd_sysctl_clean();
+
+ class_procfs_clean();
+
+ class_handle_cleanup();
+ class_exit_uuidlist();
+ obd_zombie_impexp_stop();
+
+ memory_leaked = obd_memory_sum();
+ pages_leaked = obd_pages_sum();
+
+ memory_max = obd_memory_max();
+ pages_max = obd_pages_max();
+
+ lprocfs_free_stats(&obd_memory);
+ CDEBUG((memory_leaked) ? D_ERROR : D_INFO,
+ "obd_memory max: %llu, leaked: %llu\n",
+ memory_max, memory_leaked);
+ CDEBUG((pages_leaked) ? D_ERROR : D_INFO,
+ "obd_memory_pages max: %llu, leaked: %llu\n",
+ pages_max, pages_leaked);
+}
+
+MODULE_AUTHOR("Sun Microsystems, Inc. <http://www.lustre.org/>");
+MODULE_DESCRIPTION("Lustre Class Driver Build Version: " BUILD_VERSION);
+MODULE_LICENSE("GPL");
+MODULE_VERSION(LUSTRE_VERSION_STRING);
+
+module_init(init_obdclass);
+module_exit(cleanup_obdclass);
diff --git a/drivers/staging/lustre/lustre/obdclass/debug.c b/drivers/staging/lustre/lustre/obdclass/debug.c
new file mode 100644
index 000000000..9c934e6d2
--- /dev/null
+++ b/drivers/staging/lustre/lustre/obdclass/debug.c
@@ -0,0 +1,109 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2002, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2011, 2012, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ *
+ * lustre/obdclass/debug.c
+ *
+ * Helper routines for dumping data structs for debugging.
+ */
+
+#define DEBUG_SUBSYSTEM D_OTHER
+
+#include <linux/unaligned/access_ok.h>
+
+#include "../include/obd_support.h"
+#include "../include/lustre_debug.h"
+#include "../include/lustre_net.h"
+
+void dump_lniobuf(struct niobuf_local *nb)
+{
+ CDEBUG(D_RPCTRACE,
+ "niobuf_local: file_offset=%lld, len=%d, page=%p, rc=%d\n",
+ nb->lnb_file_offset, nb->len, nb->page, nb->rc);
+ CDEBUG(D_RPCTRACE, "nb->page: index = %ld\n",
+ nb->page ? page_index(nb->page) : -1);
+}
+EXPORT_SYMBOL(dump_lniobuf);
+
+#define LPDS sizeof(__u64)
+int block_debug_setup(void *addr, int len, __u64 off, __u64 id)
+{
+ LASSERT(addr);
+
+ put_unaligned_le64(off, addr);
+ put_unaligned_le64(id, addr+LPDS);
+ addr += len - LPDS - LPDS;
+ put_unaligned_le64(off, addr);
+ put_unaligned_le64(id, addr+LPDS);
+
+ return 0;
+}
+EXPORT_SYMBOL(block_debug_setup);
+
+int block_debug_check(char *who, void *addr, int end, __u64 off, __u64 id)
+{
+ __u64 ne_off;
+ int err = 0;
+
+ LASSERT(addr);
+
+ ne_off = le64_to_cpu (off);
+ id = le64_to_cpu (id);
+ if (memcmp(addr, (char *)&ne_off, LPDS)) {
+ CDEBUG(D_ERROR, "%s: id %#llx offset %llu off: %#llx != %#llx\n",
+ who, id, off, *(__u64 *)addr, ne_off);
+ err = -EINVAL;
+ }
+ if (memcmp(addr + LPDS, (char *)&id, LPDS)) {
+ CDEBUG(D_ERROR, "%s: id %#llx offset %llu id: %#llx != %#llx\n",
+ who, id, off, *(__u64 *)(addr + LPDS), id);
+ err = -EINVAL;
+ }
+
+ addr += end - LPDS - LPDS;
+ if (memcmp(addr, (char *)&ne_off, LPDS)) {
+ CDEBUG(D_ERROR, "%s: id %#llx offset %llu end off: %#llx != %#llx\n",
+ who, id, off, *(__u64 *)addr, ne_off);
+ err = -EINVAL;
+ }
+ if (memcmp(addr + LPDS, (char *)&id, LPDS)) {
+ CDEBUG(D_ERROR, "%s: id %#llx offset %llu end id: %#llx != %#llx\n",
+ who, id, off, *(__u64 *)(addr + LPDS), id);
+ err = -EINVAL;
+ }
+
+ return err;
+}
+EXPORT_SYMBOL(block_debug_check);
+#undef LPDS
diff --git a/drivers/staging/lustre/lustre/obdclass/dt_object.c b/drivers/staging/lustre/lustre/obdclass/dt_object.c
new file mode 100644
index 000000000..b1eee0a6d
--- /dev/null
+++ b/drivers/staging/lustre/lustre/obdclass/dt_object.c
@@ -0,0 +1,1059 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2011, 2012, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ *
+ * lustre/obdclass/dt_object.c
+ *
+ * Dt Object.
+ * Generic functions from dt_object.h
+ *
+ * Author: Nikita Danilov <nikita@clusterfs.com>
+ */
+
+#define DEBUG_SUBSYSTEM S_CLASS
+
+#include "../include/obd.h"
+#include "../include/dt_object.h"
+#include <linux/list.h>
+/* fid_be_to_cpu() */
+#include "../include/lustre_fid.h"
+
+#include "../include/lustre_quota.h"
+
+/* context key constructor/destructor: dt_global_key_init, dt_global_key_fini */
+LU_KEY_INIT(dt_global, struct dt_thread_info);
+LU_KEY_FINI(dt_global, struct dt_thread_info);
+
+struct lu_context_key dt_key = {
+ .lct_tags = LCT_MD_THREAD | LCT_DT_THREAD | LCT_MG_THREAD | LCT_LOCAL,
+ .lct_init = dt_global_key_init,
+ .lct_fini = dt_global_key_fini
+};
+EXPORT_SYMBOL(dt_key);
+
+/* no lock is necessary to protect the list, because call-backs
+ * are added during system startup. Please refer to "struct dt_device".
+ */
+void dt_txn_callback_add(struct dt_device *dev, struct dt_txn_callback *cb)
+{
+ list_add(&cb->dtc_linkage, &dev->dd_txn_callbacks);
+}
+EXPORT_SYMBOL(dt_txn_callback_add);
+
+void dt_txn_callback_del(struct dt_device *dev, struct dt_txn_callback *cb)
+{
+ list_del_init(&cb->dtc_linkage);
+}
+EXPORT_SYMBOL(dt_txn_callback_del);
+
+int dt_txn_hook_start(const struct lu_env *env,
+ struct dt_device *dev, struct thandle *th)
+{
+ int rc = 0;
+ struct dt_txn_callback *cb;
+
+ if (th->th_local)
+ return 0;
+
+ list_for_each_entry(cb, &dev->dd_txn_callbacks, dtc_linkage) {
+ if (cb->dtc_txn_start == NULL ||
+ !(cb->dtc_tag & env->le_ctx.lc_tags))
+ continue;
+ rc = cb->dtc_txn_start(env, th, cb->dtc_cookie);
+ if (rc < 0)
+ break;
+ }
+ return rc;
+}
+EXPORT_SYMBOL(dt_txn_hook_start);
+
+int dt_txn_hook_stop(const struct lu_env *env, struct thandle *txn)
+{
+ struct dt_device *dev = txn->th_dev;
+ struct dt_txn_callback *cb;
+ int rc = 0;
+
+ if (txn->th_local)
+ return 0;
+
+ list_for_each_entry(cb, &dev->dd_txn_callbacks, dtc_linkage) {
+ if (cb->dtc_txn_stop == NULL ||
+ !(cb->dtc_tag & env->le_ctx.lc_tags))
+ continue;
+ rc = cb->dtc_txn_stop(env, txn, cb->dtc_cookie);
+ if (rc < 0)
+ break;
+ }
+ return rc;
+}
+EXPORT_SYMBOL(dt_txn_hook_stop);
+
+void dt_txn_hook_commit(struct thandle *txn)
+{
+ struct dt_txn_callback *cb;
+
+ if (txn->th_local)
+ return;
+
+ list_for_each_entry(cb, &txn->th_dev->dd_txn_callbacks,
+ dtc_linkage) {
+ if (cb->dtc_txn_commit)
+ cb->dtc_txn_commit(txn, cb->dtc_cookie);
+ }
+}
+EXPORT_SYMBOL(dt_txn_hook_commit);
+
+int dt_device_init(struct dt_device *dev, struct lu_device_type *t)
+{
+
+ INIT_LIST_HEAD(&dev->dd_txn_callbacks);
+ return lu_device_init(&dev->dd_lu_dev, t);
+}
+EXPORT_SYMBOL(dt_device_init);
+
+void dt_device_fini(struct dt_device *dev)
+{
+ lu_device_fini(&dev->dd_lu_dev);
+}
+EXPORT_SYMBOL(dt_device_fini);
+
+int dt_object_init(struct dt_object *obj,
+ struct lu_object_header *h, struct lu_device *d)
+
+{
+ return lu_object_init(&obj->do_lu, h, d);
+}
+EXPORT_SYMBOL(dt_object_init);
+
+void dt_object_fini(struct dt_object *obj)
+{
+ lu_object_fini(&obj->do_lu);
+}
+EXPORT_SYMBOL(dt_object_fini);
+
+int dt_try_as_dir(const struct lu_env *env, struct dt_object *obj)
+{
+ if (obj->do_index_ops == NULL)
+ obj->do_ops->do_index_try(env, obj, &dt_directory_features);
+ return obj->do_index_ops != NULL;
+}
+EXPORT_SYMBOL(dt_try_as_dir);
+
+enum dt_format_type dt_mode_to_dft(__u32 mode)
+{
+ enum dt_format_type result;
+
+ switch (mode & S_IFMT) {
+ case S_IFDIR:
+ result = DFT_DIR;
+ break;
+ case S_IFREG:
+ result = DFT_REGULAR;
+ break;
+ case S_IFLNK:
+ result = DFT_SYM;
+ break;
+ case S_IFCHR:
+ case S_IFBLK:
+ case S_IFIFO:
+ case S_IFSOCK:
+ result = DFT_NODE;
+ break;
+ default:
+ LBUG();
+ break;
+ }
+ return result;
+}
+EXPORT_SYMBOL(dt_mode_to_dft);
+
+/**
+ * lookup fid for object named \a name in directory \a dir.
+ */
+
+int dt_lookup_dir(const struct lu_env *env, struct dt_object *dir,
+ const char *name, struct lu_fid *fid)
+{
+ if (dt_try_as_dir(env, dir))
+ return dt_lookup(env, dir, (struct dt_rec *)fid,
+ (const struct dt_key *)name, BYPASS_CAPA);
+ return -ENOTDIR;
+}
+EXPORT_SYMBOL(dt_lookup_dir);
+
+/* this differs from dt_locate by top_dev as parameter
+ * but not one from lu_site */
+struct dt_object *dt_locate_at(const struct lu_env *env,
+ struct dt_device *dev, const struct lu_fid *fid,
+ struct lu_device *top_dev)
+{
+ struct lu_object *lo, *n;
+
+ lo = lu_object_find_at(env, top_dev, fid, NULL);
+ if (IS_ERR(lo))
+ return (void *)lo;
+
+ LASSERT(lo != NULL);
+
+ list_for_each_entry(n, &lo->lo_header->loh_layers, lo_linkage) {
+ if (n->lo_dev == &dev->dd_lu_dev)
+ return container_of0(n, struct dt_object, do_lu);
+ }
+ return ERR_PTR(-ENOENT);
+}
+EXPORT_SYMBOL(dt_locate_at);
+
+/**
+ * find a object named \a entry in given \a dfh->dfh_o directory.
+ */
+static int dt_find_entry(const struct lu_env *env, const char *entry, void *data)
+{
+ struct dt_find_hint *dfh = data;
+ struct dt_device *dt = dfh->dfh_dt;
+ struct lu_fid *fid = dfh->dfh_fid;
+ struct dt_object *obj = dfh->dfh_o;
+ int result;
+
+ result = dt_lookup_dir(env, obj, entry, fid);
+ lu_object_put(env, &obj->do_lu);
+ if (result == 0) {
+ obj = dt_locate(env, dt, fid);
+ if (IS_ERR(obj))
+ result = PTR_ERR(obj);
+ }
+ dfh->dfh_o = obj;
+ return result;
+}
+
+/**
+ * Abstract function which parses path name. This function feeds
+ * path component to \a entry_func.
+ */
+int dt_path_parser(const struct lu_env *env,
+ char *path, dt_entry_func_t entry_func,
+ void *data)
+{
+ char *e;
+ int rc = 0;
+
+ while (1) {
+ e = strsep(&path, "/");
+ if (e == NULL)
+ break;
+
+ if (e[0] == 0) {
+ if (!path || path[0] == '\0')
+ break;
+ continue;
+ }
+ rc = entry_func(env, e, data);
+ if (rc)
+ break;
+ }
+
+ return rc;
+}
+
+struct dt_object *
+dt_store_resolve(const struct lu_env *env, struct dt_device *dt,
+ const char *path, struct lu_fid *fid)
+{
+ struct dt_thread_info *info = dt_info(env);
+ struct dt_find_hint *dfh = &info->dti_dfh;
+ struct dt_object *obj;
+ char *local = info->dti_buf;
+ int result;
+
+
+ dfh->dfh_dt = dt;
+ dfh->dfh_fid = fid;
+
+ strncpy(local, path, DT_MAX_PATH);
+ local[DT_MAX_PATH - 1] = '\0';
+
+ result = dt->dd_ops->dt_root_get(env, dt, fid);
+ if (result == 0) {
+ obj = dt_locate(env, dt, fid);
+ if (!IS_ERR(obj)) {
+ dfh->dfh_o = obj;
+ result = dt_path_parser(env, local, dt_find_entry, dfh);
+ if (result != 0)
+ obj = ERR_PTR(result);
+ else
+ obj = dfh->dfh_o;
+ }
+ } else {
+ obj = ERR_PTR(result);
+ }
+ return obj;
+}
+EXPORT_SYMBOL(dt_store_resolve);
+
+static struct dt_object *dt_reg_open(const struct lu_env *env,
+ struct dt_device *dt,
+ struct dt_object *p,
+ const char *name,
+ struct lu_fid *fid)
+{
+ struct dt_object *o;
+ int result;
+
+ result = dt_lookup_dir(env, p, name, fid);
+ if (result == 0){
+ o = dt_locate(env, dt, fid);
+ } else
+ o = ERR_PTR(result);
+
+ return o;
+}
+
+/**
+ * Open dt object named \a filename from \a dirname directory.
+ * \param dt dt device
+ * \param fid on success, object fid is stored in *fid
+ */
+struct dt_object *dt_store_open(const struct lu_env *env,
+ struct dt_device *dt,
+ const char *dirname,
+ const char *filename,
+ struct lu_fid *fid)
+{
+ struct dt_object *file;
+ struct dt_object *dir;
+
+ dir = dt_store_resolve(env, dt, dirname, fid);
+ if (!IS_ERR(dir)) {
+ file = dt_reg_open(env, dt, dir,
+ filename, fid);
+ lu_object_put(env, &dir->do_lu);
+ } else {
+ file = dir;
+ }
+ return file;
+}
+EXPORT_SYMBOL(dt_store_open);
+
+struct dt_object *dt_find_or_create(const struct lu_env *env,
+ struct dt_device *dt,
+ const struct lu_fid *fid,
+ struct dt_object_format *dof,
+ struct lu_attr *at)
+{
+ struct dt_object *dto;
+ struct thandle *th;
+ int rc;
+
+ dto = dt_locate(env, dt, fid);
+ if (IS_ERR(dto))
+ return dto;
+
+ LASSERT(dto != NULL);
+ if (dt_object_exists(dto))
+ return dto;
+
+ th = dt_trans_create(env, dt);
+ if (IS_ERR(th)) {
+ rc = PTR_ERR(th);
+ goto out;
+ }
+
+ rc = dt_declare_create(env, dto, at, NULL, dof, th);
+ if (rc)
+ goto trans_stop;
+
+ rc = dt_trans_start_local(env, dt, th);
+ if (rc)
+ goto trans_stop;
+
+ dt_write_lock(env, dto, 0);
+ if (dt_object_exists(dto)) {
+ rc = 0;
+ goto unlock;
+ }
+
+ CDEBUG(D_OTHER, "create new object "DFID"\n", PFID(fid));
+
+ rc = dt_create(env, dto, at, NULL, dof, th);
+ if (rc)
+ goto unlock;
+ LASSERT(dt_object_exists(dto));
+unlock:
+ dt_write_unlock(env, dto);
+trans_stop:
+ dt_trans_stop(env, dt, th);
+out:
+ if (rc) {
+ lu_object_put(env, &dto->do_lu);
+ return ERR_PTR(rc);
+ }
+ return dto;
+}
+EXPORT_SYMBOL(dt_find_or_create);
+
+/* dt class init function. */
+int dt_global_init(void)
+{
+ LU_CONTEXT_KEY_INIT(&dt_key);
+ return lu_context_key_register(&dt_key);
+}
+
+void dt_global_fini(void)
+{
+ lu_context_key_degister(&dt_key);
+}
+
+/**
+ * Generic read helper. May return an error for partial reads.
+ *
+ * \param env lustre environment
+ * \param dt object to be read
+ * \param buf lu_buf to be filled, with buffer pointer and length
+ * \param pos position to start reading, updated as data is read
+ *
+ * \retval real size of data read
+ * \retval -ve errno on failure
+ */
+int dt_read(const struct lu_env *env, struct dt_object *dt,
+ struct lu_buf *buf, loff_t *pos)
+{
+ LASSERTF(dt != NULL, "dt is NULL when we want to read record\n");
+ return dt->do_body_ops->dbo_read(env, dt, buf, pos, BYPASS_CAPA);
+}
+EXPORT_SYMBOL(dt_read);
+
+/**
+ * Read structures of fixed size from storage. Unlike dt_read(), using
+ * dt_record_read() will return an error for partial reads.
+ *
+ * \param env lustre environment
+ * \param dt object to be read
+ * \param buf lu_buf to be filled, with buffer pointer and length
+ * \param pos position to start reading, updated as data is read
+ *
+ * \retval 0 on successfully reading full buffer
+ * \retval -EFAULT on short read
+ * \retval -ve errno on failure
+ */
+int dt_record_read(const struct lu_env *env, struct dt_object *dt,
+ struct lu_buf *buf, loff_t *pos)
+{
+ int rc;
+
+ LASSERTF(dt != NULL, "dt is NULL when we want to read record\n");
+
+ rc = dt->do_body_ops->dbo_read(env, dt, buf, pos, BYPASS_CAPA);
+
+ if (rc == buf->lb_len)
+ rc = 0;
+ else if (rc >= 0)
+ rc = -EFAULT;
+ return rc;
+}
+EXPORT_SYMBOL(dt_record_read);
+
+int dt_record_write(const struct lu_env *env, struct dt_object *dt,
+ const struct lu_buf *buf, loff_t *pos, struct thandle *th)
+{
+ int rc;
+
+ LASSERTF(dt != NULL, "dt is NULL when we want to write record\n");
+ LASSERT(th != NULL);
+ LASSERT(dt->do_body_ops);
+ LASSERT(dt->do_body_ops->dbo_write);
+ rc = dt->do_body_ops->dbo_write(env, dt, buf, pos, th, BYPASS_CAPA, 1);
+ if (rc == buf->lb_len)
+ rc = 0;
+ else if (rc >= 0)
+ rc = -EFAULT;
+ return rc;
+}
+EXPORT_SYMBOL(dt_record_write);
+
+int dt_declare_version_set(const struct lu_env *env, struct dt_object *o,
+ struct thandle *th)
+{
+ struct lu_buf vbuf;
+ char *xname = XATTR_NAME_VERSION;
+
+ LASSERT(o);
+ vbuf.lb_buf = NULL;
+ vbuf.lb_len = sizeof(dt_obj_version_t);
+ return dt_declare_xattr_set(env, o, &vbuf, xname, 0, th);
+
+}
+EXPORT_SYMBOL(dt_declare_version_set);
+
+void dt_version_set(const struct lu_env *env, struct dt_object *o,
+ dt_obj_version_t version, struct thandle *th)
+{
+ struct lu_buf vbuf;
+ char *xname = XATTR_NAME_VERSION;
+ int rc;
+
+ LASSERT(o);
+ vbuf.lb_buf = &version;
+ vbuf.lb_len = sizeof(version);
+
+ rc = dt_xattr_set(env, o, &vbuf, xname, 0, th, BYPASS_CAPA);
+ if (rc < 0)
+ CDEBUG(D_INODE, "Can't set version, rc %d\n", rc);
+ return;
+}
+EXPORT_SYMBOL(dt_version_set);
+
+dt_obj_version_t dt_version_get(const struct lu_env *env, struct dt_object *o)
+{
+ struct lu_buf vbuf;
+ char *xname = XATTR_NAME_VERSION;
+ dt_obj_version_t version;
+ int rc;
+
+ LASSERT(o);
+ vbuf.lb_buf = &version;
+ vbuf.lb_len = sizeof(version);
+ rc = dt_xattr_get(env, o, &vbuf, xname, BYPASS_CAPA);
+ if (rc != sizeof(version)) {
+ CDEBUG(D_INODE, "Can't get version, rc %d\n", rc);
+ version = 0;
+ }
+ return version;
+}
+EXPORT_SYMBOL(dt_version_get);
+
+/* list of all supported index types */
+
+/* directories */
+const struct dt_index_features dt_directory_features;
+EXPORT_SYMBOL(dt_directory_features);
+
+/* scrub iterator */
+const struct dt_index_features dt_otable_features;
+EXPORT_SYMBOL(dt_otable_features);
+
+/* lfsck */
+const struct dt_index_features dt_lfsck_features = {
+ .dif_flags = DT_IND_UPDATE,
+ .dif_keysize_min = sizeof(struct lu_fid),
+ .dif_keysize_max = sizeof(struct lu_fid),
+ .dif_recsize_min = sizeof(__u8),
+ .dif_recsize_max = sizeof(__u8),
+ .dif_ptrsize = 4
+};
+EXPORT_SYMBOL(dt_lfsck_features);
+
+/* accounting indexes */
+const struct dt_index_features dt_acct_features = {
+ .dif_flags = DT_IND_UPDATE,
+ .dif_keysize_min = sizeof(__u64), /* 64-bit uid/gid */
+ .dif_keysize_max = sizeof(__u64), /* 64-bit uid/gid */
+ .dif_recsize_min = sizeof(struct lquota_acct_rec), /* 16 bytes */
+ .dif_recsize_max = sizeof(struct lquota_acct_rec), /* 16 bytes */
+ .dif_ptrsize = 4
+};
+EXPORT_SYMBOL(dt_acct_features);
+
+/* global quota files */
+const struct dt_index_features dt_quota_glb_features = {
+ .dif_flags = DT_IND_UPDATE,
+ /* a different key would have to be used for per-directory quota */
+ .dif_keysize_min = sizeof(__u64), /* 64-bit uid/gid */
+ .dif_keysize_max = sizeof(__u64), /* 64-bit uid/gid */
+ .dif_recsize_min = sizeof(struct lquota_glb_rec), /* 32 bytes */
+ .dif_recsize_max = sizeof(struct lquota_glb_rec), /* 32 bytes */
+ .dif_ptrsize = 4
+};
+EXPORT_SYMBOL(dt_quota_glb_features);
+
+/* slave quota files */
+const struct dt_index_features dt_quota_slv_features = {
+ .dif_flags = DT_IND_UPDATE,
+ /* a different key would have to be used for per-directory quota */
+ .dif_keysize_min = sizeof(__u64), /* 64-bit uid/gid */
+ .dif_keysize_max = sizeof(__u64), /* 64-bit uid/gid */
+ .dif_recsize_min = sizeof(struct lquota_slv_rec), /* 8 bytes */
+ .dif_recsize_max = sizeof(struct lquota_slv_rec), /* 8 bytes */
+ .dif_ptrsize = 4
+};
+EXPORT_SYMBOL(dt_quota_slv_features);
+
+/* helper function returning what dt_index_features structure should be used
+ * based on the FID sequence. This is used by OBD_IDX_READ RPC */
+static inline const struct dt_index_features *dt_index_feat_select(__u64 seq,
+ __u32 mode)
+{
+ if (seq == FID_SEQ_QUOTA_GLB) {
+ /* global quota index */
+ if (!S_ISREG(mode))
+ /* global quota index should be a regular file */
+ return ERR_PTR(-ENOENT);
+ return &dt_quota_glb_features;
+ } else if (seq == FID_SEQ_QUOTA) {
+ /* quota slave index */
+ if (!S_ISREG(mode))
+ /* slave index should be a regular file */
+ return ERR_PTR(-ENOENT);
+ return &dt_quota_slv_features;
+ } else if (seq >= FID_SEQ_NORMAL) {
+ /* object is part of the namespace, verify that it is a
+ * directory */
+ if (!S_ISDIR(mode))
+ /* sorry, we can only deal with directory */
+ return ERR_PTR(-ENOTDIR);
+ return &dt_directory_features;
+ }
+
+ return ERR_PTR(-EOPNOTSUPP);
+}
+
+/*
+ * Fill a lu_idxpage with key/record pairs read for transfer via OBD_IDX_READ
+ * RPC
+ *
+ * \param env - is the environment passed by the caller
+ * \param lp - is a pointer to the lu_page to fill
+ * \param nob - is the maximum number of bytes that should be copied
+ * \param iops - is the index operation vector associated with the index object
+ * \param it - is a pointer to the current iterator
+ * \param attr - is the index attribute to pass to iops->rec()
+ * \param arg - is a pointer to the idx_info structure
+ */
+static int dt_index_page_build(const struct lu_env *env, union lu_page *lp,
+ int nob, const struct dt_it_ops *iops,
+ struct dt_it *it, __u32 attr, void *arg)
+{
+ struct idx_info *ii = (struct idx_info *)arg;
+ struct lu_idxpage *lip = &lp->lp_idx;
+ char *entry;
+ int rc, size;
+
+ /* no support for variable key & record size for now */
+ LASSERT((ii->ii_flags & II_FL_VARKEY) == 0);
+ LASSERT((ii->ii_flags & II_FL_VARREC) == 0);
+
+ /* initialize the header of the new container */
+ memset(lip, 0, LIP_HDR_SIZE);
+ lip->lip_magic = LIP_MAGIC;
+ nob -= LIP_HDR_SIZE;
+
+ /* compute size needed to store a key/record pair */
+ size = ii->ii_recsize + ii->ii_keysize;
+ if ((ii->ii_flags & II_FL_NOHASH) == 0)
+ /* add hash if the client wants it */
+ size += sizeof(__u64);
+
+ entry = lip->lip_entries;
+ do {
+ char *tmp_entry = entry;
+ struct dt_key *key;
+ __u64 hash;
+
+ /* fetch 64-bit hash value */
+ hash = iops->store(env, it);
+ ii->ii_hash_end = hash;
+
+ if (OBD_FAIL_CHECK(OBD_FAIL_OBD_IDX_READ_BREAK)) {
+ if (lip->lip_nr != 0) {
+ rc = 0;
+ goto out;
+ }
+ }
+
+ if (nob < size) {
+ if (lip->lip_nr == 0)
+ rc = -EINVAL;
+ else
+ rc = 0;
+ goto out;
+ }
+
+ if ((ii->ii_flags & II_FL_NOHASH) == 0) {
+ /* client wants to the 64-bit hash value associated with
+ * each record */
+ memcpy(tmp_entry, &hash, sizeof(hash));
+ tmp_entry += sizeof(hash);
+ }
+
+ /* then the key value */
+ LASSERT(iops->key_size(env, it) == ii->ii_keysize);
+ key = iops->key(env, it);
+ memcpy(tmp_entry, key, ii->ii_keysize);
+ tmp_entry += ii->ii_keysize;
+
+ /* and finally the record */
+ rc = iops->rec(env, it, (struct dt_rec *)tmp_entry, attr);
+ if (rc != -ESTALE) {
+ if (rc != 0)
+ goto out;
+
+ /* hash/key/record successfully copied! */
+ lip->lip_nr++;
+ if (unlikely(lip->lip_nr == 1 && ii->ii_count == 0))
+ ii->ii_hash_start = hash;
+ entry = tmp_entry + ii->ii_recsize;
+ nob -= size;
+ }
+
+ /* move on to the next record */
+ do {
+ rc = iops->next(env, it);
+ } while (rc == -ESTALE);
+
+ } while (rc == 0);
+
+ goto out;
+out:
+ if (rc >= 0 && lip->lip_nr > 0)
+ /* one more container */
+ ii->ii_count++;
+ if (rc > 0)
+ /* no more entries */
+ ii->ii_hash_end = II_END_OFF;
+ return rc;
+}
+
+/*
+ * Walk index and fill lu_page containers with key/record pairs
+ *
+ * \param env - is the environment passed by the caller
+ * \param obj - is the index object to parse
+ * \param rdpg - is the lu_rdpg descriptor associated with the transfer
+ * \param filler - is the callback function responsible for filling a lu_page
+ * with key/record pairs in the format wanted by the caller
+ * \param arg - is an opaq argument passed to the filler function
+ *
+ * \retval sum (in bytes) of all filled lu_pages
+ * \retval -ve errno on failure
+ */
+int dt_index_walk(const struct lu_env *env, struct dt_object *obj,
+ const struct lu_rdpg *rdpg, dt_index_page_build_t filler,
+ void *arg)
+{
+ struct dt_it *it;
+ const struct dt_it_ops *iops;
+ unsigned int pageidx, nob, nlupgs = 0;
+ int rc;
+
+ LASSERT(rdpg->rp_pages != NULL);
+ LASSERT(obj->do_index_ops != NULL);
+
+ nob = rdpg->rp_count;
+ if (nob <= 0)
+ return -EFAULT;
+
+ /* Iterate through index and fill containers from @rdpg */
+ iops = &obj->do_index_ops->dio_it;
+ LASSERT(iops != NULL);
+ it = iops->init(env, obj, rdpg->rp_attrs, BYPASS_CAPA);
+ if (IS_ERR(it))
+ return PTR_ERR(it);
+
+ rc = iops->load(env, it, rdpg->rp_hash);
+ if (rc == 0) {
+ /*
+ * Iterator didn't find record with exactly the key requested.
+ *
+ * It is currently either
+ *
+ * - positioned above record with key less than
+ * requested---skip it.
+ * - or not positioned at all (is in IAM_IT_SKEWED
+ * state)---position it on the next item.
+ */
+ rc = iops->next(env, it);
+ } else if (rc > 0) {
+ rc = 0;
+ }
+
+ /* Fill containers one after the other. There might be multiple
+ * containers per physical page.
+ *
+ * At this point and across for-loop:
+ * rc == 0 -> ok, proceed.
+ * rc > 0 -> end of index.
+ * rc < 0 -> error. */
+ for (pageidx = 0; rc == 0 && nob > 0; pageidx++) {
+ union lu_page *lp;
+ int i;
+
+ LASSERT(pageidx < rdpg->rp_npages);
+ lp = kmap(rdpg->rp_pages[pageidx]);
+
+ /* fill lu pages */
+ for (i = 0; i < LU_PAGE_COUNT; i++, lp++, nob -= LU_PAGE_SIZE) {
+ rc = filler(env, lp, min_t(int, nob, LU_PAGE_SIZE),
+ iops, it, rdpg->rp_attrs, arg);
+ if (rc < 0)
+ break;
+ /* one more lu_page */
+ nlupgs++;
+ if (rc > 0)
+ /* end of index */
+ break;
+ }
+ kunmap(rdpg->rp_pages[i]);
+ }
+
+ iops->put(env, it);
+ iops->fini(env, it);
+
+ if (rc >= 0)
+ rc = min_t(unsigned int, nlupgs * LU_PAGE_SIZE, rdpg->rp_count);
+
+ return rc;
+}
+EXPORT_SYMBOL(dt_index_walk);
+
+/**
+ * Walk key/record pairs of an index and copy them into 4KB containers to be
+ * transferred over the network. This is the common handler for OBD_IDX_READ
+ * RPC processing.
+ *
+ * \param env - is the environment passed by the caller
+ * \param dev - is the dt_device storing the index
+ * \param ii - is the idx_info structure packed by the client in the
+ * OBD_IDX_READ request
+ * \param rdpg - is the lu_rdpg descriptor
+ *
+ * \retval on success, return sum (in bytes) of all filled containers
+ * \retval appropriate error otherwise.
+ */
+int dt_index_read(const struct lu_env *env, struct dt_device *dev,
+ struct idx_info *ii, const struct lu_rdpg *rdpg)
+{
+ const struct dt_index_features *feat;
+ struct dt_object *obj;
+ int rc;
+
+ /* rp_count shouldn't be null and should be a multiple of the container
+ * size */
+ if (rdpg->rp_count <= 0 && (rdpg->rp_count & (LU_PAGE_SIZE - 1)) != 0)
+ return -EFAULT;
+
+ if (fid_seq(&ii->ii_fid) >= FID_SEQ_NORMAL)
+ /* we don't support directory transfer via OBD_IDX_READ for the
+ * time being */
+ return -EOPNOTSUPP;
+
+ if (!fid_is_quota(&ii->ii_fid))
+ /* block access to all local files except quota files */
+ return -EPERM;
+
+ /* lookup index object subject to the transfer */
+ obj = dt_locate(env, dev, &ii->ii_fid);
+ if (IS_ERR(obj))
+ return PTR_ERR(obj);
+ if (dt_object_exists(obj) == 0) {
+ rc = -ENOENT;
+ goto out;
+ }
+
+ /* fetch index features associated with index object */
+ feat = dt_index_feat_select(fid_seq(&ii->ii_fid),
+ lu_object_attr(&obj->do_lu));
+ if (IS_ERR(feat)) {
+ rc = PTR_ERR(feat);
+ goto out;
+ }
+
+ /* load index feature if not done already */
+ if (obj->do_index_ops == NULL) {
+ rc = obj->do_ops->do_index_try(env, obj, feat);
+ if (rc)
+ goto out;
+ }
+
+ /* fill ii_flags with supported index features */
+ ii->ii_flags &= II_FL_NOHASH;
+
+ ii->ii_keysize = feat->dif_keysize_max;
+ if ((feat->dif_flags & DT_IND_VARKEY) != 0) {
+ /* key size is variable */
+ ii->ii_flags |= II_FL_VARKEY;
+ /* we don't support variable key size for the time being */
+ rc = -EOPNOTSUPP;
+ goto out;
+ }
+
+ ii->ii_recsize = feat->dif_recsize_max;
+ if ((feat->dif_flags & DT_IND_VARREC) != 0) {
+ /* record size is variable */
+ ii->ii_flags |= II_FL_VARREC;
+ /* we don't support variable record size for the time being */
+ rc = -EOPNOTSUPP;
+ goto out;
+ }
+
+ if ((feat->dif_flags & DT_IND_NONUNQ) != 0)
+ /* key isn't necessarily unique */
+ ii->ii_flags |= II_FL_NONUNQ;
+
+ dt_read_lock(env, obj, 0);
+ /* fetch object version before walking the index */
+ ii->ii_version = dt_version_get(env, obj);
+
+ /* walk the index and fill lu_idxpages with key/record pairs */
+ rc = dt_index_walk(env, obj, rdpg, dt_index_page_build ,ii);
+ dt_read_unlock(env, obj);
+
+ if (rc == 0) {
+ /* index is empty */
+ LASSERT(ii->ii_count == 0);
+ ii->ii_hash_end = II_END_OFF;
+ }
+
+ goto out;
+out:
+ lu_object_put(env, &obj->do_lu);
+ return rc;
+}
+EXPORT_SYMBOL(dt_index_read);
+
+#if defined (CONFIG_PROC_FS)
+
+int lprocfs_dt_rd_blksize(char *page, char **start, off_t off,
+ int count, int *eof, void *data)
+{
+ struct dt_device *dt = data;
+ struct obd_statfs osfs;
+ int rc = dt_statfs(NULL, dt, &osfs);
+
+ if (rc == 0) {
+ *eof = 1;
+ rc = snprintf(page, count, "%u\n",
+ (unsigned) osfs.os_bsize);
+ }
+
+ return rc;
+}
+EXPORT_SYMBOL(lprocfs_dt_rd_blksize);
+
+int lprocfs_dt_rd_kbytestotal(char *page, char **start, off_t off,
+ int count, int *eof, void *data)
+{
+ struct dt_device *dt = data;
+ struct obd_statfs osfs;
+ int rc = dt_statfs(NULL, dt, &osfs);
+
+ if (rc == 0) {
+ __u32 blk_size = osfs.os_bsize >> 10;
+ __u64 result = osfs.os_blocks;
+
+ while (blk_size >>= 1)
+ result <<= 1;
+
+ *eof = 1;
+ rc = snprintf(page, count, "%llu\n", result);
+ }
+
+ return rc;
+}
+EXPORT_SYMBOL(lprocfs_dt_rd_kbytestotal);
+
+int lprocfs_dt_rd_kbytesfree(char *page, char **start, off_t off,
+ int count, int *eof, void *data)
+{
+ struct dt_device *dt = data;
+ struct obd_statfs osfs;
+ int rc = dt_statfs(NULL, dt, &osfs);
+
+ if (rc == 0) {
+ __u32 blk_size = osfs.os_bsize >> 10;
+ __u64 result = osfs.os_bfree;
+
+ while (blk_size >>= 1)
+ result <<= 1;
+
+ *eof = 1;
+ rc = snprintf(page, count, "%llu\n", result);
+ }
+
+ return rc;
+}
+EXPORT_SYMBOL(lprocfs_dt_rd_kbytesfree);
+
+int lprocfs_dt_rd_kbytesavail(char *page, char **start, off_t off,
+ int count, int *eof, void *data)
+{
+ struct dt_device *dt = data;
+ struct obd_statfs osfs;
+ int rc = dt_statfs(NULL, dt, &osfs);
+
+ if (rc == 0) {
+ __u32 blk_size = osfs.os_bsize >> 10;
+ __u64 result = osfs.os_bavail;
+
+ while (blk_size >>= 1)
+ result <<= 1;
+
+ *eof = 1;
+ rc = snprintf(page, count, "%llu\n", result);
+ }
+
+ return rc;
+}
+EXPORT_SYMBOL(lprocfs_dt_rd_kbytesavail);
+
+int lprocfs_dt_rd_filestotal(char *page, char **start, off_t off,
+ int count, int *eof, void *data)
+{
+ struct dt_device *dt = data;
+ struct obd_statfs osfs;
+ int rc = dt_statfs(NULL, dt, &osfs);
+
+ if (rc == 0) {
+ *eof = 1;
+ rc = snprintf(page, count, "%llu\n", osfs.os_files);
+ }
+
+ return rc;
+}
+EXPORT_SYMBOL(lprocfs_dt_rd_filestotal);
+
+int lprocfs_dt_rd_filesfree(char *page, char **start, off_t off,
+ int count, int *eof, void *data)
+{
+ struct dt_device *dt = data;
+ struct obd_statfs osfs;
+ int rc = dt_statfs(NULL, dt, &osfs);
+
+ if (rc == 0) {
+ *eof = 1;
+ rc = snprintf(page, count, "%llu\n", osfs.os_ffree);
+ }
+
+ return rc;
+}
+EXPORT_SYMBOL(lprocfs_dt_rd_filesfree);
+
+#endif /* CONFIG_PROC_FS */
diff --git a/drivers/staging/lustre/lustre/obdclass/genops.c b/drivers/staging/lustre/lustre/obdclass/genops.c
new file mode 100644
index 000000000..66b56784f
--- /dev/null
+++ b/drivers/staging/lustre/lustre/obdclass/genops.c
@@ -0,0 +1,1833 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2011, 2012, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ *
+ * lustre/obdclass/genops.c
+ *
+ * These are the only exported functions, they provide some generic
+ * infrastructure for managing object devices
+ */
+
+#define DEBUG_SUBSYSTEM S_CLASS
+#include "../include/obd_class.h"
+#include "../include/lprocfs_status.h"
+
+spinlock_t obd_types_lock;
+
+struct kmem_cache *obd_device_cachep;
+struct kmem_cache *obdo_cachep;
+EXPORT_SYMBOL(obdo_cachep);
+static struct kmem_cache *import_cachep;
+
+static struct list_head obd_zombie_imports;
+static struct list_head obd_zombie_exports;
+static spinlock_t obd_zombie_impexp_lock;
+static void obd_zombie_impexp_notify(void);
+static void obd_zombie_export_add(struct obd_export *exp);
+static void obd_zombie_import_add(struct obd_import *imp);
+static void print_export_data(struct obd_export *exp,
+ const char *status, int locks);
+
+int (*ptlrpc_put_connection_superhack)(struct ptlrpc_connection *c);
+EXPORT_SYMBOL(ptlrpc_put_connection_superhack);
+
+/*
+ * support functions: we could use inter-module communication, but this
+ * is more portable to other OS's
+ */
+static struct obd_device *obd_device_alloc(void)
+{
+ struct obd_device *obd;
+
+ OBD_SLAB_ALLOC_PTR_GFP(obd, obd_device_cachep, GFP_NOFS);
+ if (obd != NULL) {
+ obd->obd_magic = OBD_DEVICE_MAGIC;
+ }
+ return obd;
+}
+
+static void obd_device_free(struct obd_device *obd)
+{
+ LASSERT(obd != NULL);
+ LASSERTF(obd->obd_magic == OBD_DEVICE_MAGIC, "obd %p obd_magic %08x != %08x\n",
+ obd, obd->obd_magic, OBD_DEVICE_MAGIC);
+ if (obd->obd_namespace != NULL) {
+ CERROR("obd %p: namespace %p was not properly cleaned up (obd_force=%d)!\n",
+ obd, obd->obd_namespace, obd->obd_force);
+ LBUG();
+ }
+ lu_ref_fini(&obd->obd_reference);
+ OBD_SLAB_FREE_PTR(obd, obd_device_cachep);
+}
+
+struct obd_type *class_search_type(const char *name)
+{
+ struct list_head *tmp;
+ struct obd_type *type;
+
+ spin_lock(&obd_types_lock);
+ list_for_each(tmp, &obd_types) {
+ type = list_entry(tmp, struct obd_type, typ_chain);
+ if (strcmp(type->typ_name, name) == 0) {
+ spin_unlock(&obd_types_lock);
+ return type;
+ }
+ }
+ spin_unlock(&obd_types_lock);
+ return NULL;
+}
+EXPORT_SYMBOL(class_search_type);
+
+struct obd_type *class_get_type(const char *name)
+{
+ struct obd_type *type = class_search_type(name);
+
+ if (!type) {
+ const char *modname = name;
+
+ if (strcmp(modname, "obdfilter") == 0)
+ modname = "ofd";
+
+ if (strcmp(modname, LUSTRE_LWP_NAME) == 0)
+ modname = LUSTRE_OSP_NAME;
+
+ if (!strncmp(modname, LUSTRE_MDS_NAME, strlen(LUSTRE_MDS_NAME)))
+ modname = LUSTRE_MDT_NAME;
+
+ if (!request_module("%s", modname)) {
+ CDEBUG(D_INFO, "Loaded module '%s'\n", modname);
+ type = class_search_type(name);
+ } else {
+ LCONSOLE_ERROR_MSG(0x158, "Can't load module '%s'\n",
+ modname);
+ }
+ }
+ if (type) {
+ spin_lock(&type->obd_type_lock);
+ type->typ_refcnt++;
+ try_module_get(type->typ_dt_ops->o_owner);
+ spin_unlock(&type->obd_type_lock);
+ }
+ return type;
+}
+EXPORT_SYMBOL(class_get_type);
+
+void class_put_type(struct obd_type *type)
+{
+ LASSERT(type);
+ spin_lock(&type->obd_type_lock);
+ type->typ_refcnt--;
+ module_put(type->typ_dt_ops->o_owner);
+ spin_unlock(&type->obd_type_lock);
+}
+EXPORT_SYMBOL(class_put_type);
+
+#define CLASS_MAX_NAME 1024
+
+int class_register_type(struct obd_ops *dt_ops, struct md_ops *md_ops,
+ struct lprocfs_vars *vars, const char *name,
+ struct lu_device_type *ldt)
+{
+ struct obd_type *type;
+ int rc = 0;
+
+ /* sanity check */
+ LASSERT(strnlen(name, CLASS_MAX_NAME) < CLASS_MAX_NAME);
+
+ if (class_search_type(name)) {
+ CDEBUG(D_IOCTL, "Type %s already registered\n", name);
+ return -EEXIST;
+ }
+
+ rc = -ENOMEM;
+ OBD_ALLOC(type, sizeof(*type));
+ if (type == NULL)
+ return rc;
+
+ OBD_ALLOC_PTR(type->typ_dt_ops);
+ OBD_ALLOC_PTR(type->typ_md_ops);
+ OBD_ALLOC(type->typ_name, strlen(name) + 1);
+
+ if (type->typ_dt_ops == NULL ||
+ type->typ_md_ops == NULL ||
+ type->typ_name == NULL)
+ goto failed;
+
+ *(type->typ_dt_ops) = *dt_ops;
+ /* md_ops is optional */
+ if (md_ops)
+ *(type->typ_md_ops) = *md_ops;
+ strcpy(type->typ_name, name);
+ spin_lock_init(&type->obd_type_lock);
+
+ type->typ_procroot = lprocfs_register(type->typ_name, proc_lustre_root,
+ vars, type);
+ if (IS_ERR(type->typ_procroot)) {
+ rc = PTR_ERR(type->typ_procroot);
+ type->typ_procroot = NULL;
+ goto failed;
+ }
+
+ if (ldt != NULL) {
+ type->typ_lu = ldt;
+ rc = lu_device_type_init(ldt);
+ if (rc != 0)
+ goto failed;
+ }
+
+ spin_lock(&obd_types_lock);
+ list_add(&type->typ_chain, &obd_types);
+ spin_unlock(&obd_types_lock);
+
+ return 0;
+
+ failed:
+ if (type->typ_name != NULL)
+ OBD_FREE(type->typ_name, strlen(name) + 1);
+ if (type->typ_md_ops != NULL)
+ OBD_FREE_PTR(type->typ_md_ops);
+ if (type->typ_dt_ops != NULL)
+ OBD_FREE_PTR(type->typ_dt_ops);
+ OBD_FREE(type, sizeof(*type));
+ return rc;
+}
+EXPORT_SYMBOL(class_register_type);
+
+int class_unregister_type(const char *name)
+{
+ struct obd_type *type = class_search_type(name);
+
+ if (!type) {
+ CERROR("unknown obd type\n");
+ return -EINVAL;
+ }
+
+ if (type->typ_refcnt) {
+ CERROR("type %s has refcount (%d)\n", name, type->typ_refcnt);
+ /* This is a bad situation, let's make the best of it */
+ /* Remove ops, but leave the name for debugging */
+ OBD_FREE_PTR(type->typ_dt_ops);
+ OBD_FREE_PTR(type->typ_md_ops);
+ return -EBUSY;
+ }
+
+ if (type->typ_procroot) {
+ lprocfs_remove(&type->typ_procroot);
+ }
+
+ if (type->typ_lu)
+ lu_device_type_fini(type->typ_lu);
+
+ spin_lock(&obd_types_lock);
+ list_del(&type->typ_chain);
+ spin_unlock(&obd_types_lock);
+ OBD_FREE(type->typ_name, strlen(name) + 1);
+ if (type->typ_dt_ops != NULL)
+ OBD_FREE_PTR(type->typ_dt_ops);
+ if (type->typ_md_ops != NULL)
+ OBD_FREE_PTR(type->typ_md_ops);
+ OBD_FREE(type, sizeof(*type));
+ return 0;
+} /* class_unregister_type */
+EXPORT_SYMBOL(class_unregister_type);
+
+/**
+ * Create a new obd device.
+ *
+ * Find an empty slot in ::obd_devs[], create a new obd device in it.
+ *
+ * \param[in] type_name obd device type string.
+ * \param[in] name obd device name.
+ *
+ * \retval NULL if create fails, otherwise return the obd device
+ * pointer created.
+ */
+struct obd_device *class_newdev(const char *type_name, const char *name)
+{
+ struct obd_device *result = NULL;
+ struct obd_device *newdev;
+ struct obd_type *type = NULL;
+ int i;
+ int new_obd_minor = 0;
+
+ if (strlen(name) >= MAX_OBD_NAME) {
+ CERROR("name/uuid must be < %u bytes long\n", MAX_OBD_NAME);
+ return ERR_PTR(-EINVAL);
+ }
+
+ type = class_get_type(type_name);
+ if (type == NULL){
+ CERROR("OBD: unknown type: %s\n", type_name);
+ return ERR_PTR(-ENODEV);
+ }
+
+ newdev = obd_device_alloc();
+ if (newdev == NULL) {
+ result = ERR_PTR(-ENOMEM);
+ goto out_type;
+ }
+
+ LASSERT(newdev->obd_magic == OBD_DEVICE_MAGIC);
+
+ write_lock(&obd_dev_lock);
+ for (i = 0; i < class_devno_max(); i++) {
+ struct obd_device *obd = class_num2obd(i);
+
+ if (obd && (strcmp(name, obd->obd_name) == 0)) {
+ CERROR("Device %s already exists at %d, won't add\n",
+ name, i);
+ if (result) {
+ LASSERTF(result->obd_magic == OBD_DEVICE_MAGIC,
+ "%p obd_magic %08x != %08x\n", result,
+ result->obd_magic, OBD_DEVICE_MAGIC);
+ LASSERTF(result->obd_minor == new_obd_minor,
+ "%p obd_minor %d != %d\n", result,
+ result->obd_minor, new_obd_minor);
+
+ obd_devs[result->obd_minor] = NULL;
+ result->obd_name[0] = '\0';
+ }
+ result = ERR_PTR(-EEXIST);
+ break;
+ }
+ if (!result && !obd) {
+ result = newdev;
+ result->obd_minor = i;
+ new_obd_minor = i;
+ result->obd_type = type;
+ strncpy(result->obd_name, name,
+ sizeof(result->obd_name) - 1);
+ obd_devs[i] = result;
+ }
+ }
+ write_unlock(&obd_dev_lock);
+
+ if (result == NULL && i >= class_devno_max()) {
+ CERROR("all %u OBD devices used, increase MAX_OBD_DEVICES\n",
+ class_devno_max());
+ result = ERR_PTR(-EOVERFLOW);
+ goto out;
+ }
+
+ if (IS_ERR(result))
+ goto out;
+
+ CDEBUG(D_IOCTL, "Adding new device %s (%p)\n",
+ result->obd_name, result);
+
+ return result;
+out:
+ obd_device_free(newdev);
+out_type:
+ class_put_type(type);
+ return result;
+}
+
+void class_release_dev(struct obd_device *obd)
+{
+ struct obd_type *obd_type = obd->obd_type;
+
+ LASSERTF(obd->obd_magic == OBD_DEVICE_MAGIC, "%p obd_magic %08x != %08x\n",
+ obd, obd->obd_magic, OBD_DEVICE_MAGIC);
+ LASSERTF(obd == obd_devs[obd->obd_minor], "obd %p != obd_devs[%d] %p\n",
+ obd, obd->obd_minor, obd_devs[obd->obd_minor]);
+ LASSERT(obd_type != NULL);
+
+ CDEBUG(D_INFO, "Release obd device %s at %d obd_type name =%s\n",
+ obd->obd_name, obd->obd_minor, obd->obd_type->typ_name);
+
+ write_lock(&obd_dev_lock);
+ obd_devs[obd->obd_minor] = NULL;
+ write_unlock(&obd_dev_lock);
+ obd_device_free(obd);
+
+ class_put_type(obd_type);
+}
+
+int class_name2dev(const char *name)
+{
+ int i;
+
+ if (!name)
+ return -1;
+
+ read_lock(&obd_dev_lock);
+ for (i = 0; i < class_devno_max(); i++) {
+ struct obd_device *obd = class_num2obd(i);
+
+ if (obd && strcmp(name, obd->obd_name) == 0) {
+ /* Make sure we finished attaching before we give
+ out any references */
+ LASSERT(obd->obd_magic == OBD_DEVICE_MAGIC);
+ if (obd->obd_attached) {
+ read_unlock(&obd_dev_lock);
+ return i;
+ }
+ break;
+ }
+ }
+ read_unlock(&obd_dev_lock);
+
+ return -1;
+}
+EXPORT_SYMBOL(class_name2dev);
+
+struct obd_device *class_name2obd(const char *name)
+{
+ int dev = class_name2dev(name);
+
+ if (dev < 0 || dev > class_devno_max())
+ return NULL;
+ return class_num2obd(dev);
+}
+EXPORT_SYMBOL(class_name2obd);
+
+int class_uuid2dev(struct obd_uuid *uuid)
+{
+ int i;
+
+ read_lock(&obd_dev_lock);
+ for (i = 0; i < class_devno_max(); i++) {
+ struct obd_device *obd = class_num2obd(i);
+
+ if (obd && obd_uuid_equals(uuid, &obd->obd_uuid)) {
+ LASSERT(obd->obd_magic == OBD_DEVICE_MAGIC);
+ read_unlock(&obd_dev_lock);
+ return i;
+ }
+ }
+ read_unlock(&obd_dev_lock);
+
+ return -1;
+}
+EXPORT_SYMBOL(class_uuid2dev);
+
+struct obd_device *class_uuid2obd(struct obd_uuid *uuid)
+{
+ int dev = class_uuid2dev(uuid);
+ if (dev < 0)
+ return NULL;
+ return class_num2obd(dev);
+}
+EXPORT_SYMBOL(class_uuid2obd);
+
+/**
+ * Get obd device from ::obd_devs[]
+ *
+ * \param num [in] array index
+ *
+ * \retval NULL if ::obd_devs[\a num] does not contains an obd device
+ * otherwise return the obd device there.
+ */
+struct obd_device *class_num2obd(int num)
+{
+ struct obd_device *obd = NULL;
+
+ if (num < class_devno_max()) {
+ obd = obd_devs[num];
+ if (obd == NULL)
+ return NULL;
+
+ LASSERTF(obd->obd_magic == OBD_DEVICE_MAGIC,
+ "%p obd_magic %08x != %08x\n",
+ obd, obd->obd_magic, OBD_DEVICE_MAGIC);
+ LASSERTF(obd->obd_minor == num,
+ "%p obd_minor %0d != %0d\n",
+ obd, obd->obd_minor, num);
+ }
+
+ return obd;
+}
+EXPORT_SYMBOL(class_num2obd);
+
+/**
+ * Get obd devices count. Device in any
+ * state are counted
+ * \retval obd device count
+ */
+int get_devices_count(void)
+{
+ int index, max_index = class_devno_max(), dev_count = 0;
+
+ read_lock(&obd_dev_lock);
+ for (index = 0; index <= max_index; index++) {
+ struct obd_device *obd = class_num2obd(index);
+ if (obd != NULL)
+ dev_count++;
+ }
+ read_unlock(&obd_dev_lock);
+
+ return dev_count;
+}
+EXPORT_SYMBOL(get_devices_count);
+
+void class_obd_list(void)
+{
+ char *status;
+ int i;
+
+ read_lock(&obd_dev_lock);
+ for (i = 0; i < class_devno_max(); i++) {
+ struct obd_device *obd = class_num2obd(i);
+
+ if (obd == NULL)
+ continue;
+ if (obd->obd_stopping)
+ status = "ST";
+ else if (obd->obd_set_up)
+ status = "UP";
+ else if (obd->obd_attached)
+ status = "AT";
+ else
+ status = "--";
+ LCONSOLE(D_CONFIG, "%3d %s %s %s %s %d\n",
+ i, status, obd->obd_type->typ_name,
+ obd->obd_name, obd->obd_uuid.uuid,
+ atomic_read(&obd->obd_refcount));
+ }
+ read_unlock(&obd_dev_lock);
+ return;
+}
+
+/* Search for a client OBD connected to tgt_uuid. If grp_uuid is
+ specified, then only the client with that uuid is returned,
+ otherwise any client connected to the tgt is returned. */
+struct obd_device *class_find_client_obd(struct obd_uuid *tgt_uuid,
+ const char *typ_name,
+ struct obd_uuid *grp_uuid)
+{
+ int i;
+
+ read_lock(&obd_dev_lock);
+ for (i = 0; i < class_devno_max(); i++) {
+ struct obd_device *obd = class_num2obd(i);
+
+ if (obd == NULL)
+ continue;
+ if ((strncmp(obd->obd_type->typ_name, typ_name,
+ strlen(typ_name)) == 0)) {
+ if (obd_uuid_equals(tgt_uuid,
+ &obd->u.cli.cl_target_uuid) &&
+ ((grp_uuid)? obd_uuid_equals(grp_uuid,
+ &obd->obd_uuid) : 1)) {
+ read_unlock(&obd_dev_lock);
+ return obd;
+ }
+ }
+ }
+ read_unlock(&obd_dev_lock);
+
+ return NULL;
+}
+EXPORT_SYMBOL(class_find_client_obd);
+
+/* Iterate the obd_device list looking devices have grp_uuid. Start
+ searching at *next, and if a device is found, the next index to look
+ at is saved in *next. If next is NULL, then the first matching device
+ will always be returned. */
+struct obd_device *class_devices_in_group(struct obd_uuid *grp_uuid, int *next)
+{
+ int i;
+
+ if (next == NULL)
+ i = 0;
+ else if (*next >= 0 && *next < class_devno_max())
+ i = *next;
+ else
+ return NULL;
+
+ read_lock(&obd_dev_lock);
+ for (; i < class_devno_max(); i++) {
+ struct obd_device *obd = class_num2obd(i);
+
+ if (obd == NULL)
+ continue;
+ if (obd_uuid_equals(grp_uuid, &obd->obd_uuid)) {
+ if (next != NULL)
+ *next = i+1;
+ read_unlock(&obd_dev_lock);
+ return obd;
+ }
+ }
+ read_unlock(&obd_dev_lock);
+
+ return NULL;
+}
+EXPORT_SYMBOL(class_devices_in_group);
+
+/**
+ * to notify sptlrpc log for \a fsname has changed, let every relevant OBD
+ * adjust sptlrpc settings accordingly.
+ */
+int class_notify_sptlrpc_conf(const char *fsname, int namelen)
+{
+ struct obd_device *obd;
+ const char *type;
+ int i, rc = 0, rc2;
+
+ LASSERT(namelen > 0);
+
+ read_lock(&obd_dev_lock);
+ for (i = 0; i < class_devno_max(); i++) {
+ obd = class_num2obd(i);
+
+ if (obd == NULL || obd->obd_set_up == 0 || obd->obd_stopping)
+ continue;
+
+ /* only notify mdc, osc, mdt, ost */
+ type = obd->obd_type->typ_name;
+ if (strcmp(type, LUSTRE_MDC_NAME) != 0 &&
+ strcmp(type, LUSTRE_OSC_NAME) != 0 &&
+ strcmp(type, LUSTRE_MDT_NAME) != 0 &&
+ strcmp(type, LUSTRE_OST_NAME) != 0)
+ continue;
+
+ if (strncmp(obd->obd_name, fsname, namelen))
+ continue;
+
+ class_incref(obd, __func__, obd);
+ read_unlock(&obd_dev_lock);
+ rc2 = obd_set_info_async(NULL, obd->obd_self_export,
+ sizeof(KEY_SPTLRPC_CONF),
+ KEY_SPTLRPC_CONF, 0, NULL, NULL);
+ rc = rc ? rc : rc2;
+ class_decref(obd, __func__, obd);
+ read_lock(&obd_dev_lock);
+ }
+ read_unlock(&obd_dev_lock);
+ return rc;
+}
+EXPORT_SYMBOL(class_notify_sptlrpc_conf);
+
+void obd_cleanup_caches(void)
+{
+ if (obd_device_cachep) {
+ kmem_cache_destroy(obd_device_cachep);
+ obd_device_cachep = NULL;
+ }
+ if (obdo_cachep) {
+ kmem_cache_destroy(obdo_cachep);
+ obdo_cachep = NULL;
+ }
+ if (import_cachep) {
+ kmem_cache_destroy(import_cachep);
+ import_cachep = NULL;
+ }
+ if (capa_cachep) {
+ kmem_cache_destroy(capa_cachep);
+ capa_cachep = NULL;
+ }
+}
+
+int obd_init_caches(void)
+{
+ LASSERT(obd_device_cachep == NULL);
+ obd_device_cachep = kmem_cache_create("ll_obd_dev_cache",
+ sizeof(struct obd_device),
+ 0, 0, NULL);
+ if (!obd_device_cachep)
+ goto out;
+
+ LASSERT(obdo_cachep == NULL);
+ obdo_cachep = kmem_cache_create("ll_obdo_cache", sizeof(struct obdo),
+ 0, 0, NULL);
+ if (!obdo_cachep)
+ goto out;
+
+ LASSERT(import_cachep == NULL);
+ import_cachep = kmem_cache_create("ll_import_cache",
+ sizeof(struct obd_import),
+ 0, 0, NULL);
+ if (!import_cachep)
+ goto out;
+
+ LASSERT(capa_cachep == NULL);
+ capa_cachep = kmem_cache_create("capa_cache",
+ sizeof(struct obd_capa), 0, 0, NULL);
+ if (!capa_cachep)
+ goto out;
+
+ return 0;
+ out:
+ obd_cleanup_caches();
+ return -ENOMEM;
+
+}
+
+/* map connection to client */
+struct obd_export *class_conn2export(struct lustre_handle *conn)
+{
+ struct obd_export *export;
+
+ if (!conn) {
+ CDEBUG(D_CACHE, "looking for null handle\n");
+ return NULL;
+ }
+
+ if (conn->cookie == -1) { /* this means assign a new connection */
+ CDEBUG(D_CACHE, "want a new connection\n");
+ return NULL;
+ }
+
+ CDEBUG(D_INFO, "looking for export cookie %#llx\n", conn->cookie);
+ export = class_handle2object(conn->cookie);
+ return export;
+}
+EXPORT_SYMBOL(class_conn2export);
+
+struct obd_device *class_exp2obd(struct obd_export *exp)
+{
+ if (exp)
+ return exp->exp_obd;
+ return NULL;
+}
+EXPORT_SYMBOL(class_exp2obd);
+
+struct obd_device *class_conn2obd(struct lustre_handle *conn)
+{
+ struct obd_export *export;
+ export = class_conn2export(conn);
+ if (export) {
+ struct obd_device *obd = export->exp_obd;
+ class_export_put(export);
+ return obd;
+ }
+ return NULL;
+}
+EXPORT_SYMBOL(class_conn2obd);
+
+struct obd_import *class_exp2cliimp(struct obd_export *exp)
+{
+ struct obd_device *obd = exp->exp_obd;
+ if (obd == NULL)
+ return NULL;
+ return obd->u.cli.cl_import;
+}
+EXPORT_SYMBOL(class_exp2cliimp);
+
+struct obd_import *class_conn2cliimp(struct lustre_handle *conn)
+{
+ struct obd_device *obd = class_conn2obd(conn);
+ if (obd == NULL)
+ return NULL;
+ return obd->u.cli.cl_import;
+}
+EXPORT_SYMBOL(class_conn2cliimp);
+
+/* Export management functions */
+static void class_export_destroy(struct obd_export *exp)
+{
+ struct obd_device *obd = exp->exp_obd;
+
+ LASSERT_ATOMIC_ZERO(&exp->exp_refcount);
+ LASSERT(obd != NULL);
+
+ CDEBUG(D_IOCTL, "destroying export %p/%s for %s\n", exp,
+ exp->exp_client_uuid.uuid, obd->obd_name);
+
+ /* "Local" exports (lctl, LOV->{mdc,osc}) have no connection. */
+ if (exp->exp_connection)
+ ptlrpc_put_connection_superhack(exp->exp_connection);
+
+ LASSERT(list_empty(&exp->exp_outstanding_replies));
+ LASSERT(list_empty(&exp->exp_uncommitted_replies));
+ LASSERT(list_empty(&exp->exp_req_replay_queue));
+ LASSERT(list_empty(&exp->exp_hp_rpcs));
+ obd_destroy_export(exp);
+ class_decref(obd, "export", exp);
+
+ OBD_FREE_RCU(exp, sizeof(*exp), &exp->exp_handle);
+}
+
+static void export_handle_addref(void *export)
+{
+ class_export_get(export);
+}
+
+static struct portals_handle_ops export_handle_ops = {
+ .hop_addref = export_handle_addref,
+ .hop_free = NULL,
+};
+
+struct obd_export *class_export_get(struct obd_export *exp)
+{
+ atomic_inc(&exp->exp_refcount);
+ CDEBUG(D_INFO, "GETting export %p : new refcount %d\n", exp,
+ atomic_read(&exp->exp_refcount));
+ return exp;
+}
+EXPORT_SYMBOL(class_export_get);
+
+void class_export_put(struct obd_export *exp)
+{
+ LASSERT(exp != NULL);
+ LASSERT_ATOMIC_GT_LT(&exp->exp_refcount, 0, LI_POISON);
+ CDEBUG(D_INFO, "PUTting export %p : new refcount %d\n", exp,
+ atomic_read(&exp->exp_refcount) - 1);
+
+ if (atomic_dec_and_test(&exp->exp_refcount)) {
+ LASSERT(!list_empty(&exp->exp_obd_chain));
+ CDEBUG(D_IOCTL, "final put %p/%s\n",
+ exp, exp->exp_client_uuid.uuid);
+
+ /* release nid stat refererence */
+ lprocfs_exp_cleanup(exp);
+
+ obd_zombie_export_add(exp);
+ }
+}
+EXPORT_SYMBOL(class_export_put);
+
+/* Creates a new export, adds it to the hash table, and returns a
+ * pointer to it. The refcount is 2: one for the hash reference, and
+ * one for the pointer returned by this function. */
+struct obd_export *class_new_export(struct obd_device *obd,
+ struct obd_uuid *cluuid)
+{
+ struct obd_export *export;
+ struct cfs_hash *hash = NULL;
+ int rc = 0;
+
+ OBD_ALLOC_PTR(export);
+ if (!export)
+ return ERR_PTR(-ENOMEM);
+
+ export->exp_conn_cnt = 0;
+ export->exp_lock_hash = NULL;
+ export->exp_flock_hash = NULL;
+ atomic_set(&export->exp_refcount, 2);
+ atomic_set(&export->exp_rpc_count, 0);
+ atomic_set(&export->exp_cb_count, 0);
+ atomic_set(&export->exp_locks_count, 0);
+#if LUSTRE_TRACKS_LOCK_EXP_REFS
+ INIT_LIST_HEAD(&export->exp_locks_list);
+ spin_lock_init(&export->exp_locks_list_guard);
+#endif
+ atomic_set(&export->exp_replay_count, 0);
+ export->exp_obd = obd;
+ INIT_LIST_HEAD(&export->exp_outstanding_replies);
+ spin_lock_init(&export->exp_uncommitted_replies_lock);
+ INIT_LIST_HEAD(&export->exp_uncommitted_replies);
+ INIT_LIST_HEAD(&export->exp_req_replay_queue);
+ INIT_LIST_HEAD(&export->exp_handle.h_link);
+ INIT_LIST_HEAD(&export->exp_hp_rpcs);
+ class_handle_hash(&export->exp_handle, &export_handle_ops);
+ export->exp_last_request_time = get_seconds();
+ spin_lock_init(&export->exp_lock);
+ spin_lock_init(&export->exp_rpc_lock);
+ INIT_HLIST_NODE(&export->exp_uuid_hash);
+ INIT_HLIST_NODE(&export->exp_nid_hash);
+ spin_lock_init(&export->exp_bl_list_lock);
+ INIT_LIST_HEAD(&export->exp_bl_list);
+
+ export->exp_sp_peer = LUSTRE_SP_ANY;
+ export->exp_flvr.sf_rpc = SPTLRPC_FLVR_INVALID;
+ export->exp_client_uuid = *cluuid;
+ obd_init_export(export);
+
+ spin_lock(&obd->obd_dev_lock);
+ /* shouldn't happen, but might race */
+ if (obd->obd_stopping) {
+ rc = -ENODEV;
+ goto exit_unlock;
+ }
+
+ hash = cfs_hash_getref(obd->obd_uuid_hash);
+ if (hash == NULL) {
+ rc = -ENODEV;
+ goto exit_unlock;
+ }
+ spin_unlock(&obd->obd_dev_lock);
+
+ if (!obd_uuid_equals(cluuid, &obd->obd_uuid)) {
+ rc = cfs_hash_add_unique(hash, cluuid, &export->exp_uuid_hash);
+ if (rc != 0) {
+ LCONSOLE_WARN("%s: denying duplicate export for %s, %d\n",
+ obd->obd_name, cluuid->uuid, rc);
+ rc = -EALREADY;
+ goto exit_err;
+ }
+ }
+
+ spin_lock(&obd->obd_dev_lock);
+ if (obd->obd_stopping) {
+ cfs_hash_del(hash, cluuid, &export->exp_uuid_hash);
+ rc = -ENODEV;
+ goto exit_unlock;
+ }
+
+ class_incref(obd, "export", export);
+ list_add(&export->exp_obd_chain, &export->exp_obd->obd_exports);
+ list_add_tail(&export->exp_obd_chain_timed,
+ &export->exp_obd->obd_exports_timed);
+ export->exp_obd->obd_num_exports++;
+ spin_unlock(&obd->obd_dev_lock);
+ cfs_hash_putref(hash);
+ return export;
+
+exit_unlock:
+ spin_unlock(&obd->obd_dev_lock);
+exit_err:
+ if (hash)
+ cfs_hash_putref(hash);
+ class_handle_unhash(&export->exp_handle);
+ LASSERT(hlist_unhashed(&export->exp_uuid_hash));
+ obd_destroy_export(export);
+ OBD_FREE_PTR(export);
+ return ERR_PTR(rc);
+}
+EXPORT_SYMBOL(class_new_export);
+
+void class_unlink_export(struct obd_export *exp)
+{
+ class_handle_unhash(&exp->exp_handle);
+
+ spin_lock(&exp->exp_obd->obd_dev_lock);
+ /* delete an uuid-export hashitem from hashtables */
+ if (!hlist_unhashed(&exp->exp_uuid_hash))
+ cfs_hash_del(exp->exp_obd->obd_uuid_hash,
+ &exp->exp_client_uuid,
+ &exp->exp_uuid_hash);
+
+ list_move(&exp->exp_obd_chain, &exp->exp_obd->obd_unlinked_exports);
+ list_del_init(&exp->exp_obd_chain_timed);
+ exp->exp_obd->obd_num_exports--;
+ spin_unlock(&exp->exp_obd->obd_dev_lock);
+ class_export_put(exp);
+}
+EXPORT_SYMBOL(class_unlink_export);
+
+/* Import management functions */
+static void class_import_destroy(struct obd_import *imp)
+{
+ CDEBUG(D_IOCTL, "destroying import %p for %s\n", imp,
+ imp->imp_obd->obd_name);
+
+ LASSERT_ATOMIC_ZERO(&imp->imp_refcount);
+
+ ptlrpc_put_connection_superhack(imp->imp_connection);
+
+ while (!list_empty(&imp->imp_conn_list)) {
+ struct obd_import_conn *imp_conn;
+
+ imp_conn = list_entry(imp->imp_conn_list.next,
+ struct obd_import_conn, oic_item);
+ list_del_init(&imp_conn->oic_item);
+ ptlrpc_put_connection_superhack(imp_conn->oic_conn);
+ OBD_FREE(imp_conn, sizeof(*imp_conn));
+ }
+
+ LASSERT(imp->imp_sec == NULL);
+ class_decref(imp->imp_obd, "import", imp);
+ OBD_FREE_RCU(imp, sizeof(*imp), &imp->imp_handle);
+}
+
+static void import_handle_addref(void *import)
+{
+ class_import_get(import);
+}
+
+static struct portals_handle_ops import_handle_ops = {
+ .hop_addref = import_handle_addref,
+ .hop_free = NULL,
+};
+
+struct obd_import *class_import_get(struct obd_import *import)
+{
+ atomic_inc(&import->imp_refcount);
+ CDEBUG(D_INFO, "import %p refcount=%d obd=%s\n", import,
+ atomic_read(&import->imp_refcount),
+ import->imp_obd->obd_name);
+ return import;
+}
+EXPORT_SYMBOL(class_import_get);
+
+void class_import_put(struct obd_import *imp)
+{
+ LASSERT(list_empty(&imp->imp_zombie_chain));
+ LASSERT_ATOMIC_GT_LT(&imp->imp_refcount, 0, LI_POISON);
+
+ CDEBUG(D_INFO, "import %p refcount=%d obd=%s\n", imp,
+ atomic_read(&imp->imp_refcount) - 1,
+ imp->imp_obd->obd_name);
+
+ if (atomic_dec_and_test(&imp->imp_refcount)) {
+ CDEBUG(D_INFO, "final put import %p\n", imp);
+ obd_zombie_import_add(imp);
+ }
+
+ /* catch possible import put race */
+ LASSERT_ATOMIC_GE_LT(&imp->imp_refcount, 0, LI_POISON);
+}
+EXPORT_SYMBOL(class_import_put);
+
+static void init_imp_at(struct imp_at *at) {
+ int i;
+ at_init(&at->iat_net_latency, 0, 0);
+ for (i = 0; i < IMP_AT_MAX_PORTALS; i++) {
+ /* max service estimates are tracked on the server side, so
+ don't use the AT history here, just use the last reported
+ val. (But keep hist for proc histogram, worst_ever) */
+ at_init(&at->iat_service_estimate[i], INITIAL_CONNECT_TIMEOUT,
+ AT_FLG_NOHIST);
+ }
+}
+
+struct obd_import *class_new_import(struct obd_device *obd)
+{
+ struct obd_import *imp;
+
+ OBD_ALLOC(imp, sizeof(*imp));
+ if (imp == NULL)
+ return NULL;
+
+ INIT_LIST_HEAD(&imp->imp_pinger_chain);
+ INIT_LIST_HEAD(&imp->imp_zombie_chain);
+ INIT_LIST_HEAD(&imp->imp_replay_list);
+ INIT_LIST_HEAD(&imp->imp_sending_list);
+ INIT_LIST_HEAD(&imp->imp_delayed_list);
+ INIT_LIST_HEAD(&imp->imp_committed_list);
+ imp->imp_replay_cursor = &imp->imp_committed_list;
+ spin_lock_init(&imp->imp_lock);
+ imp->imp_last_success_conn = 0;
+ imp->imp_state = LUSTRE_IMP_NEW;
+ imp->imp_obd = class_incref(obd, "import", imp);
+ mutex_init(&imp->imp_sec_mutex);
+ init_waitqueue_head(&imp->imp_recovery_waitq);
+
+ atomic_set(&imp->imp_refcount, 2);
+ atomic_set(&imp->imp_unregistering, 0);
+ atomic_set(&imp->imp_inflight, 0);
+ atomic_set(&imp->imp_replay_inflight, 0);
+ atomic_set(&imp->imp_inval_count, 0);
+ INIT_LIST_HEAD(&imp->imp_conn_list);
+ INIT_LIST_HEAD(&imp->imp_handle.h_link);
+ class_handle_hash(&imp->imp_handle, &import_handle_ops);
+ init_imp_at(&imp->imp_at);
+
+ /* the default magic is V2, will be used in connect RPC, and
+ * then adjusted according to the flags in request/reply. */
+ imp->imp_msg_magic = LUSTRE_MSG_MAGIC_V2;
+
+ return imp;
+}
+EXPORT_SYMBOL(class_new_import);
+
+void class_destroy_import(struct obd_import *import)
+{
+ LASSERT(import != NULL);
+ LASSERT(import != LP_POISON);
+
+ class_handle_unhash(&import->imp_handle);
+
+ spin_lock(&import->imp_lock);
+ import->imp_generation++;
+ spin_unlock(&import->imp_lock);
+ class_import_put(import);
+}
+EXPORT_SYMBOL(class_destroy_import);
+
+#if LUSTRE_TRACKS_LOCK_EXP_REFS
+
+void __class_export_add_lock_ref(struct obd_export *exp, struct ldlm_lock *lock)
+{
+ spin_lock(&exp->exp_locks_list_guard);
+
+ LASSERT(lock->l_exp_refs_nr >= 0);
+
+ if (lock->l_exp_refs_target != NULL &&
+ lock->l_exp_refs_target != exp) {
+ LCONSOLE_WARN("setting export %p for lock %p which already has export %p\n",
+ exp, lock, lock->l_exp_refs_target);
+ }
+ if ((lock->l_exp_refs_nr ++) == 0) {
+ list_add(&lock->l_exp_refs_link, &exp->exp_locks_list);
+ lock->l_exp_refs_target = exp;
+ }
+ CDEBUG(D_INFO, "lock = %p, export = %p, refs = %u\n",
+ lock, exp, lock->l_exp_refs_nr);
+ spin_unlock(&exp->exp_locks_list_guard);
+}
+EXPORT_SYMBOL(__class_export_add_lock_ref);
+
+void __class_export_del_lock_ref(struct obd_export *exp, struct ldlm_lock *lock)
+{
+ spin_lock(&exp->exp_locks_list_guard);
+ LASSERT(lock->l_exp_refs_nr > 0);
+ if (lock->l_exp_refs_target != exp) {
+ LCONSOLE_WARN("lock %p, mismatching export pointers: %p, %p\n",
+ lock, lock->l_exp_refs_target, exp);
+ }
+ if (-- lock->l_exp_refs_nr == 0) {
+ list_del_init(&lock->l_exp_refs_link);
+ lock->l_exp_refs_target = NULL;
+ }
+ CDEBUG(D_INFO, "lock = %p, export = %p, refs = %u\n",
+ lock, exp, lock->l_exp_refs_nr);
+ spin_unlock(&exp->exp_locks_list_guard);
+}
+EXPORT_SYMBOL(__class_export_del_lock_ref);
+#endif
+
+/* A connection defines an export context in which preallocation can
+ be managed. This releases the export pointer reference, and returns
+ the export handle, so the export refcount is 1 when this function
+ returns. */
+int class_connect(struct lustre_handle *conn, struct obd_device *obd,
+ struct obd_uuid *cluuid)
+{
+ struct obd_export *export;
+ LASSERT(conn != NULL);
+ LASSERT(obd != NULL);
+ LASSERT(cluuid != NULL);
+
+ export = class_new_export(obd, cluuid);
+ if (IS_ERR(export))
+ return PTR_ERR(export);
+
+ conn->cookie = export->exp_handle.h_cookie;
+ class_export_put(export);
+
+ CDEBUG(D_IOCTL, "connect: client %s, cookie %#llx\n",
+ cluuid->uuid, conn->cookie);
+ return 0;
+}
+EXPORT_SYMBOL(class_connect);
+
+/* if export is involved in recovery then clean up related things */
+static void class_export_recovery_cleanup(struct obd_export *exp)
+{
+ struct obd_device *obd = exp->exp_obd;
+
+ spin_lock(&obd->obd_recovery_task_lock);
+ if (exp->exp_delayed)
+ obd->obd_delayed_clients--;
+ if (obd->obd_recovering) {
+ if (exp->exp_in_recovery) {
+ spin_lock(&exp->exp_lock);
+ exp->exp_in_recovery = 0;
+ spin_unlock(&exp->exp_lock);
+ LASSERT_ATOMIC_POS(&obd->obd_connected_clients);
+ atomic_dec(&obd->obd_connected_clients);
+ }
+
+ /* if called during recovery then should update
+ * obd_stale_clients counter,
+ * lightweight exports are not counted */
+ if (exp->exp_failed &&
+ (exp_connect_flags(exp) & OBD_CONNECT_LIGHTWEIGHT) == 0)
+ exp->exp_obd->obd_stale_clients++;
+ }
+ spin_unlock(&obd->obd_recovery_task_lock);
+
+ spin_lock(&exp->exp_lock);
+ /** Cleanup req replay fields */
+ if (exp->exp_req_replay_needed) {
+ exp->exp_req_replay_needed = 0;
+
+ LASSERT(atomic_read(&obd->obd_req_replay_clients));
+ atomic_dec(&obd->obd_req_replay_clients);
+ }
+
+ /** Cleanup lock replay data */
+ if (exp->exp_lock_replay_needed) {
+ exp->exp_lock_replay_needed = 0;
+
+ LASSERT(atomic_read(&obd->obd_lock_replay_clients));
+ atomic_dec(&obd->obd_lock_replay_clients);
+ }
+ spin_unlock(&exp->exp_lock);
+}
+
+/* This function removes 1-3 references from the export:
+ * 1 - for export pointer passed
+ * and if disconnect really need
+ * 2 - removing from hash
+ * 3 - in client_unlink_export
+ * The export pointer passed to this function can destroyed */
+int class_disconnect(struct obd_export *export)
+{
+ int already_disconnected;
+
+ if (export == NULL) {
+ CWARN("attempting to free NULL export %p\n", export);
+ return -EINVAL;
+ }
+
+ spin_lock(&export->exp_lock);
+ already_disconnected = export->exp_disconnected;
+ export->exp_disconnected = 1;
+ spin_unlock(&export->exp_lock);
+
+ /* class_cleanup(), abort_recovery(), and class_fail_export()
+ * all end up in here, and if any of them race we shouldn't
+ * call extra class_export_puts(). */
+ if (already_disconnected) {
+ LASSERT(hlist_unhashed(&export->exp_nid_hash));
+ goto no_disconn;
+ }
+
+ CDEBUG(D_IOCTL, "disconnect: cookie %#llx\n",
+ export->exp_handle.h_cookie);
+
+ if (!hlist_unhashed(&export->exp_nid_hash))
+ cfs_hash_del(export->exp_obd->obd_nid_hash,
+ &export->exp_connection->c_peer.nid,
+ &export->exp_nid_hash);
+
+ class_export_recovery_cleanup(export);
+ class_unlink_export(export);
+no_disconn:
+ class_export_put(export);
+ return 0;
+}
+EXPORT_SYMBOL(class_disconnect);
+
+/* Return non-zero for a fully connected export */
+int class_connected_export(struct obd_export *exp)
+{
+ if (exp) {
+ int connected;
+ spin_lock(&exp->exp_lock);
+ connected = exp->exp_conn_cnt > 0;
+ spin_unlock(&exp->exp_lock);
+ return connected;
+ }
+ return 0;
+}
+EXPORT_SYMBOL(class_connected_export);
+
+static void class_disconnect_export_list(struct list_head *list,
+ enum obd_option flags)
+{
+ int rc;
+ struct obd_export *exp;
+
+ /* It's possible that an export may disconnect itself, but
+ * nothing else will be added to this list. */
+ while (!list_empty(list)) {
+ exp = list_entry(list->next, struct obd_export,
+ exp_obd_chain);
+ /* need for safe call CDEBUG after obd_disconnect */
+ class_export_get(exp);
+
+ spin_lock(&exp->exp_lock);
+ exp->exp_flags = flags;
+ spin_unlock(&exp->exp_lock);
+
+ if (obd_uuid_equals(&exp->exp_client_uuid,
+ &exp->exp_obd->obd_uuid)) {
+ CDEBUG(D_HA,
+ "exp %p export uuid == obd uuid, don't discon\n",
+ exp);
+ /* Need to delete this now so we don't end up pointing
+ * to work_list later when this export is cleaned up. */
+ list_del_init(&exp->exp_obd_chain);
+ class_export_put(exp);
+ continue;
+ }
+
+ class_export_get(exp);
+ CDEBUG(D_HA, "%s: disconnecting export at %s (%p), last request at " CFS_TIME_T "\n",
+ exp->exp_obd->obd_name, obd_export_nid2str(exp),
+ exp, exp->exp_last_request_time);
+ /* release one export reference anyway */
+ rc = obd_disconnect(exp);
+
+ CDEBUG(D_HA, "disconnected export at %s (%p): rc %d\n",
+ obd_export_nid2str(exp), exp, rc);
+ class_export_put(exp);
+ }
+}
+
+void class_disconnect_exports(struct obd_device *obd)
+{
+ struct list_head work_list;
+
+ /* Move all of the exports from obd_exports to a work list, en masse. */
+ INIT_LIST_HEAD(&work_list);
+ spin_lock(&obd->obd_dev_lock);
+ list_splice_init(&obd->obd_exports, &work_list);
+ list_splice_init(&obd->obd_delayed_exports, &work_list);
+ spin_unlock(&obd->obd_dev_lock);
+
+ if (!list_empty(&work_list)) {
+ CDEBUG(D_HA, "OBD device %d (%p) has exports, disconnecting them\n",
+ obd->obd_minor, obd);
+ class_disconnect_export_list(&work_list,
+ exp_flags_from_obd(obd));
+ } else
+ CDEBUG(D_HA, "OBD device %d (%p) has no exports\n",
+ obd->obd_minor, obd);
+}
+EXPORT_SYMBOL(class_disconnect_exports);
+
+/* Remove exports that have not completed recovery.
+ */
+void class_disconnect_stale_exports(struct obd_device *obd,
+ int (*test_export)(struct obd_export *))
+{
+ struct list_head work_list;
+ struct obd_export *exp, *n;
+ int evicted = 0;
+
+ INIT_LIST_HEAD(&work_list);
+ spin_lock(&obd->obd_dev_lock);
+ list_for_each_entry_safe(exp, n, &obd->obd_exports,
+ exp_obd_chain) {
+ /* don't count self-export as client */
+ if (obd_uuid_equals(&exp->exp_client_uuid,
+ &exp->exp_obd->obd_uuid))
+ continue;
+
+ /* don't evict clients which have no slot in last_rcvd
+ * (e.g. lightweight connection) */
+ if (exp->exp_target_data.ted_lr_idx == -1)
+ continue;
+
+ spin_lock(&exp->exp_lock);
+ if (exp->exp_failed || test_export(exp)) {
+ spin_unlock(&exp->exp_lock);
+ continue;
+ }
+ exp->exp_failed = 1;
+ spin_unlock(&exp->exp_lock);
+
+ list_move(&exp->exp_obd_chain, &work_list);
+ evicted++;
+ CDEBUG(D_HA, "%s: disconnect stale client %s@%s\n",
+ obd->obd_name, exp->exp_client_uuid.uuid,
+ exp->exp_connection == NULL ? "<unknown>" :
+ libcfs_nid2str(exp->exp_connection->c_peer.nid));
+ print_export_data(exp, "EVICTING", 0);
+ }
+ spin_unlock(&obd->obd_dev_lock);
+
+ if (evicted)
+ LCONSOLE_WARN("%s: disconnecting %d stale clients\n",
+ obd->obd_name, evicted);
+
+ class_disconnect_export_list(&work_list, exp_flags_from_obd(obd) |
+ OBD_OPT_ABORT_RECOV);
+}
+EXPORT_SYMBOL(class_disconnect_stale_exports);
+
+void class_fail_export(struct obd_export *exp)
+{
+ int rc, already_failed;
+
+ spin_lock(&exp->exp_lock);
+ already_failed = exp->exp_failed;
+ exp->exp_failed = 1;
+ spin_unlock(&exp->exp_lock);
+
+ if (already_failed) {
+ CDEBUG(D_HA, "disconnecting dead export %p/%s; skipping\n",
+ exp, exp->exp_client_uuid.uuid);
+ return;
+ }
+
+ CDEBUG(D_HA, "disconnecting export %p/%s\n",
+ exp, exp->exp_client_uuid.uuid);
+
+ if (obd_dump_on_timeout)
+ libcfs_debug_dumplog();
+
+ /* need for safe call CDEBUG after obd_disconnect */
+ class_export_get(exp);
+
+ /* Most callers into obd_disconnect are removing their own reference
+ * (request, for example) in addition to the one from the hash table.
+ * We don't have such a reference here, so make one. */
+ class_export_get(exp);
+ rc = obd_disconnect(exp);
+ if (rc)
+ CERROR("disconnecting export %p failed: %d\n", exp, rc);
+ else
+ CDEBUG(D_HA, "disconnected export %p/%s\n",
+ exp, exp->exp_client_uuid.uuid);
+ class_export_put(exp);
+}
+EXPORT_SYMBOL(class_fail_export);
+
+char *obd_export_nid2str(struct obd_export *exp)
+{
+ if (exp->exp_connection != NULL)
+ return libcfs_nid2str(exp->exp_connection->c_peer.nid);
+
+ return "(no nid)";
+}
+EXPORT_SYMBOL(obd_export_nid2str);
+
+int obd_export_evict_by_nid(struct obd_device *obd, const char *nid)
+{
+ struct cfs_hash *nid_hash;
+ struct obd_export *doomed_exp = NULL;
+ int exports_evicted = 0;
+
+ lnet_nid_t nid_key = libcfs_str2nid((char *)nid);
+
+ spin_lock(&obd->obd_dev_lock);
+ /* umount has run already, so evict thread should leave
+ * its task to umount thread now */
+ if (obd->obd_stopping) {
+ spin_unlock(&obd->obd_dev_lock);
+ return exports_evicted;
+ }
+ nid_hash = obd->obd_nid_hash;
+ cfs_hash_getref(nid_hash);
+ spin_unlock(&obd->obd_dev_lock);
+
+ do {
+ doomed_exp = cfs_hash_lookup(nid_hash, &nid_key);
+ if (doomed_exp == NULL)
+ break;
+
+ LASSERTF(doomed_exp->exp_connection->c_peer.nid == nid_key,
+ "nid %s found, wanted nid %s, requested nid %s\n",
+ obd_export_nid2str(doomed_exp),
+ libcfs_nid2str(nid_key), nid);
+ LASSERTF(doomed_exp != obd->obd_self_export,
+ "self-export is hashed by NID?\n");
+ exports_evicted++;
+ LCONSOLE_WARN("%s: evicting %s (at %s) by administrative request\n",
+ obd->obd_name,
+ obd_uuid2str(&doomed_exp->exp_client_uuid),
+ obd_export_nid2str(doomed_exp));
+ class_fail_export(doomed_exp);
+ class_export_put(doomed_exp);
+ } while (1);
+
+ cfs_hash_putref(nid_hash);
+
+ if (!exports_evicted)
+ CDEBUG(D_HA,
+ "%s: can't disconnect NID '%s': no exports found\n",
+ obd->obd_name, nid);
+ return exports_evicted;
+}
+EXPORT_SYMBOL(obd_export_evict_by_nid);
+
+int obd_export_evict_by_uuid(struct obd_device *obd, const char *uuid)
+{
+ struct cfs_hash *uuid_hash;
+ struct obd_export *doomed_exp = NULL;
+ struct obd_uuid doomed_uuid;
+ int exports_evicted = 0;
+
+ spin_lock(&obd->obd_dev_lock);
+ if (obd->obd_stopping) {
+ spin_unlock(&obd->obd_dev_lock);
+ return exports_evicted;
+ }
+ uuid_hash = obd->obd_uuid_hash;
+ cfs_hash_getref(uuid_hash);
+ spin_unlock(&obd->obd_dev_lock);
+
+ obd_str2uuid(&doomed_uuid, uuid);
+ if (obd_uuid_equals(&doomed_uuid, &obd->obd_uuid)) {
+ CERROR("%s: can't evict myself\n", obd->obd_name);
+ cfs_hash_putref(uuid_hash);
+ return exports_evicted;
+ }
+
+ doomed_exp = cfs_hash_lookup(uuid_hash, &doomed_uuid);
+
+ if (doomed_exp == NULL) {
+ CERROR("%s: can't disconnect %s: no exports found\n",
+ obd->obd_name, uuid);
+ } else {
+ CWARN("%s: evicting %s at administrative request\n",
+ obd->obd_name, doomed_exp->exp_client_uuid.uuid);
+ class_fail_export(doomed_exp);
+ class_export_put(doomed_exp);
+ exports_evicted++;
+ }
+ cfs_hash_putref(uuid_hash);
+
+ return exports_evicted;
+}
+EXPORT_SYMBOL(obd_export_evict_by_uuid);
+
+#if LUSTRE_TRACKS_LOCK_EXP_REFS
+void (*class_export_dump_hook)(struct obd_export*) = NULL;
+EXPORT_SYMBOL(class_export_dump_hook);
+#endif
+
+static void print_export_data(struct obd_export *exp, const char *status,
+ int locks)
+{
+ struct ptlrpc_reply_state *rs;
+ struct ptlrpc_reply_state *first_reply = NULL;
+ int nreplies = 0;
+
+ spin_lock(&exp->exp_lock);
+ list_for_each_entry(rs, &exp->exp_outstanding_replies,
+ rs_exp_list) {
+ if (nreplies == 0)
+ first_reply = rs;
+ nreplies++;
+ }
+ spin_unlock(&exp->exp_lock);
+
+ CDEBUG(D_HA, "%s: %s %p %s %s %d (%d %d %d) %d %d %d %d: %p %s %llu\n",
+ exp->exp_obd->obd_name, status, exp, exp->exp_client_uuid.uuid,
+ obd_export_nid2str(exp), atomic_read(&exp->exp_refcount),
+ atomic_read(&exp->exp_rpc_count),
+ atomic_read(&exp->exp_cb_count),
+ atomic_read(&exp->exp_locks_count),
+ exp->exp_disconnected, exp->exp_delayed, exp->exp_failed,
+ nreplies, first_reply, nreplies > 3 ? "..." : "",
+ exp->exp_last_committed);
+#if LUSTRE_TRACKS_LOCK_EXP_REFS
+ if (locks && class_export_dump_hook != NULL)
+ class_export_dump_hook(exp);
+#endif
+}
+
+void dump_exports(struct obd_device *obd, int locks)
+{
+ struct obd_export *exp;
+
+ spin_lock(&obd->obd_dev_lock);
+ list_for_each_entry(exp, &obd->obd_exports, exp_obd_chain)
+ print_export_data(exp, "ACTIVE", locks);
+ list_for_each_entry(exp, &obd->obd_unlinked_exports, exp_obd_chain)
+ print_export_data(exp, "UNLINKED", locks);
+ list_for_each_entry(exp, &obd->obd_delayed_exports, exp_obd_chain)
+ print_export_data(exp, "DELAYED", locks);
+ spin_unlock(&obd->obd_dev_lock);
+ spin_lock(&obd_zombie_impexp_lock);
+ list_for_each_entry(exp, &obd_zombie_exports, exp_obd_chain)
+ print_export_data(exp, "ZOMBIE", locks);
+ spin_unlock(&obd_zombie_impexp_lock);
+}
+EXPORT_SYMBOL(dump_exports);
+
+void obd_exports_barrier(struct obd_device *obd)
+{
+ int waited = 2;
+ LASSERT(list_empty(&obd->obd_exports));
+ spin_lock(&obd->obd_dev_lock);
+ while (!list_empty(&obd->obd_unlinked_exports)) {
+ spin_unlock(&obd->obd_dev_lock);
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ schedule_timeout(cfs_time_seconds(waited));
+ if (waited > 5 && IS_PO2(waited)) {
+ LCONSOLE_WARN("%s is waiting for obd_unlinked_exports more than %d seconds. The obd refcount = %d. Is it stuck?\n",
+ obd->obd_name, waited,
+ atomic_read(&obd->obd_refcount));
+ dump_exports(obd, 1);
+ }
+ waited *= 2;
+ spin_lock(&obd->obd_dev_lock);
+ }
+ spin_unlock(&obd->obd_dev_lock);
+}
+EXPORT_SYMBOL(obd_exports_barrier);
+
+/* Total amount of zombies to be destroyed */
+static int zombies_count;
+
+/**
+ * kill zombie imports and exports
+ */
+void obd_zombie_impexp_cull(void)
+{
+ struct obd_import *import;
+ struct obd_export *export;
+
+ do {
+ spin_lock(&obd_zombie_impexp_lock);
+
+ import = NULL;
+ if (!list_empty(&obd_zombie_imports)) {
+ import = list_entry(obd_zombie_imports.next,
+ struct obd_import,
+ imp_zombie_chain);
+ list_del_init(&import->imp_zombie_chain);
+ }
+
+ export = NULL;
+ if (!list_empty(&obd_zombie_exports)) {
+ export = list_entry(obd_zombie_exports.next,
+ struct obd_export,
+ exp_obd_chain);
+ list_del_init(&export->exp_obd_chain);
+ }
+
+ spin_unlock(&obd_zombie_impexp_lock);
+
+ if (import != NULL) {
+ class_import_destroy(import);
+ spin_lock(&obd_zombie_impexp_lock);
+ zombies_count--;
+ spin_unlock(&obd_zombie_impexp_lock);
+ }
+
+ if (export != NULL) {
+ class_export_destroy(export);
+ spin_lock(&obd_zombie_impexp_lock);
+ zombies_count--;
+ spin_unlock(&obd_zombie_impexp_lock);
+ }
+
+ cond_resched();
+ } while (import != NULL || export != NULL);
+}
+
+static struct completion obd_zombie_start;
+static struct completion obd_zombie_stop;
+static unsigned long obd_zombie_flags;
+static wait_queue_head_t obd_zombie_waitq;
+static pid_t obd_zombie_pid;
+
+enum {
+ OBD_ZOMBIE_STOP = 0x0001,
+};
+
+/**
+ * check for work for kill zombie import/export thread.
+ */
+static int obd_zombie_impexp_check(void *arg)
+{
+ int rc;
+
+ spin_lock(&obd_zombie_impexp_lock);
+ rc = (zombies_count == 0) &&
+ !test_bit(OBD_ZOMBIE_STOP, &obd_zombie_flags);
+ spin_unlock(&obd_zombie_impexp_lock);
+
+ return rc;
+}
+
+/**
+ * Add export to the obd_zombie thread and notify it.
+ */
+static void obd_zombie_export_add(struct obd_export *exp) {
+ spin_lock(&exp->exp_obd->obd_dev_lock);
+ LASSERT(!list_empty(&exp->exp_obd_chain));
+ list_del_init(&exp->exp_obd_chain);
+ spin_unlock(&exp->exp_obd->obd_dev_lock);
+ spin_lock(&obd_zombie_impexp_lock);
+ zombies_count++;
+ list_add(&exp->exp_obd_chain, &obd_zombie_exports);
+ spin_unlock(&obd_zombie_impexp_lock);
+
+ obd_zombie_impexp_notify();
+}
+
+/**
+ * Add import to the obd_zombie thread and notify it.
+ */
+static void obd_zombie_import_add(struct obd_import *imp) {
+ LASSERT(imp->imp_sec == NULL);
+ LASSERT(imp->imp_rq_pool == NULL);
+ spin_lock(&obd_zombie_impexp_lock);
+ LASSERT(list_empty(&imp->imp_zombie_chain));
+ zombies_count++;
+ list_add(&imp->imp_zombie_chain, &obd_zombie_imports);
+ spin_unlock(&obd_zombie_impexp_lock);
+
+ obd_zombie_impexp_notify();
+}
+
+/**
+ * notify import/export destroy thread about new zombie.
+ */
+static void obd_zombie_impexp_notify(void)
+{
+ /*
+ * Make sure obd_zombie_impexp_thread get this notification.
+ * It is possible this signal only get by obd_zombie_barrier, and
+ * barrier gulps this notification and sleeps away and hangs ensues
+ */
+ wake_up_all(&obd_zombie_waitq);
+}
+
+/**
+ * check whether obd_zombie is idle
+ */
+static int obd_zombie_is_idle(void)
+{
+ int rc;
+
+ LASSERT(!test_bit(OBD_ZOMBIE_STOP, &obd_zombie_flags));
+ spin_lock(&obd_zombie_impexp_lock);
+ rc = (zombies_count == 0);
+ spin_unlock(&obd_zombie_impexp_lock);
+ return rc;
+}
+
+/**
+ * wait when obd_zombie import/export queues become empty
+ */
+void obd_zombie_barrier(void)
+{
+ struct l_wait_info lwi = { 0 };
+
+ if (obd_zombie_pid == current_pid())
+ /* don't wait for myself */
+ return;
+ l_wait_event(obd_zombie_waitq, obd_zombie_is_idle(), &lwi);
+}
+EXPORT_SYMBOL(obd_zombie_barrier);
+
+
+/**
+ * destroy zombie export/import thread.
+ */
+static int obd_zombie_impexp_thread(void *unused)
+{
+ unshare_fs_struct();
+ complete(&obd_zombie_start);
+
+ obd_zombie_pid = current_pid();
+
+ while (!test_bit(OBD_ZOMBIE_STOP, &obd_zombie_flags)) {
+ struct l_wait_info lwi = { 0 };
+
+ l_wait_event(obd_zombie_waitq,
+ !obd_zombie_impexp_check(NULL), &lwi);
+ obd_zombie_impexp_cull();
+
+ /*
+ * Notify obd_zombie_barrier callers that queues
+ * may be empty.
+ */
+ wake_up(&obd_zombie_waitq);
+ }
+
+ complete(&obd_zombie_stop);
+
+ return 0;
+}
+
+
+/**
+ * start destroy zombie import/export thread
+ */
+int obd_zombie_impexp_init(void)
+{
+ struct task_struct *task;
+
+ INIT_LIST_HEAD(&obd_zombie_imports);
+ INIT_LIST_HEAD(&obd_zombie_exports);
+ spin_lock_init(&obd_zombie_impexp_lock);
+ init_completion(&obd_zombie_start);
+ init_completion(&obd_zombie_stop);
+ init_waitqueue_head(&obd_zombie_waitq);
+ obd_zombie_pid = 0;
+
+ task = kthread_run(obd_zombie_impexp_thread, NULL, "obd_zombid");
+ if (IS_ERR(task))
+ return PTR_ERR(task);
+
+ wait_for_completion(&obd_zombie_start);
+ return 0;
+}
+/**
+ * stop destroy zombie import/export thread
+ */
+void obd_zombie_impexp_stop(void)
+{
+ set_bit(OBD_ZOMBIE_STOP, &obd_zombie_flags);
+ obd_zombie_impexp_notify();
+ wait_for_completion(&obd_zombie_stop);
+}
+
+/***** Kernel-userspace comm helpers *******/
+
+/* Get length of entire message, including header */
+int kuc_len(int payload_len)
+{
+ return sizeof(struct kuc_hdr) + payload_len;
+}
+EXPORT_SYMBOL(kuc_len);
+
+/* Get a pointer to kuc header, given a ptr to the payload
+ * @param p Pointer to payload area
+ * @returns Pointer to kuc header
+ */
+struct kuc_hdr *kuc_ptr(void *p)
+{
+ struct kuc_hdr *lh = ((struct kuc_hdr *)p) - 1;
+ LASSERT(lh->kuc_magic == KUC_MAGIC);
+ return lh;
+}
+EXPORT_SYMBOL(kuc_ptr);
+
+/* Test if payload is part of kuc message
+ * @param p Pointer to payload area
+ * @returns boolean
+ */
+int kuc_ispayload(void *p)
+{
+ struct kuc_hdr *kh = ((struct kuc_hdr *)p) - 1;
+
+ if (kh->kuc_magic == KUC_MAGIC)
+ return 1;
+ else
+ return 0;
+}
+EXPORT_SYMBOL(kuc_ispayload);
+
+/* Alloc space for a message, and fill in header
+ * @return Pointer to payload area
+ */
+void *kuc_alloc(int payload_len, int transport, int type)
+{
+ struct kuc_hdr *lh;
+ int len = kuc_len(payload_len);
+
+ OBD_ALLOC(lh, len);
+ if (lh == NULL)
+ return ERR_PTR(-ENOMEM);
+
+ lh->kuc_magic = KUC_MAGIC;
+ lh->kuc_transport = transport;
+ lh->kuc_msgtype = type;
+ lh->kuc_msglen = len;
+
+ return (void *)(lh + 1);
+}
+EXPORT_SYMBOL(kuc_alloc);
+
+/* Takes pointer to payload area */
+inline void kuc_free(void *p, int payload_len)
+{
+ struct kuc_hdr *lh = kuc_ptr(p);
+ OBD_FREE(lh, kuc_len(payload_len));
+}
+EXPORT_SYMBOL(kuc_free);
diff --git a/drivers/staging/lustre/lustre/obdclass/linux/linux-module.c b/drivers/staging/lustre/lustre/obdclass/linux/linux-module.c
new file mode 100644
index 000000000..06944b863
--- /dev/null
+++ b/drivers/staging/lustre/lustre/obdclass/linux/linux-module.c
@@ -0,0 +1,449 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2011, 2012, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ *
+ * lustre/obdclass/linux/linux-module.c
+ *
+ * Object Devices Class Driver
+ * These are the only exported functions, they provide some generic
+ * infrastructure for managing object devices
+ */
+
+#define DEBUG_SUBSYSTEM S_CLASS
+
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/major.h>
+#include <linux/sched.h>
+#include <linux/lp.h>
+#include <linux/slab.h>
+#include <linux/ioport.h>
+#include <linux/fcntl.h>
+#include <linux/delay.h>
+#include <linux/skbuff.h>
+#include <linux/proc_fs.h>
+#include <linux/fs.h>
+#include <linux/poll.h>
+#include <linux/list.h>
+#include <linux/highmem.h>
+#include <linux/io.h>
+#include <asm/ioctls.h>
+#include <linux/poll.h>
+#include <linux/uaccess.h>
+#include <linux/miscdevice.h>
+#include <linux/seq_file.h>
+
+#include "../../../include/linux/libcfs/libcfs.h"
+#include "../../../include/linux/lnet/lnetctl.h"
+#include "../../include/obd_support.h"
+#include "../../include/obd_class.h"
+#include "../../include/lprocfs_status.h"
+#include "../../include/lustre_ver.h"
+#include "../../include/lustre/lustre_build_version.h"
+
+int proc_version;
+
+/* buffer MUST be at least the size of obd_ioctl_hdr */
+int obd_ioctl_getdata(char **buf, int *len, void *arg)
+{
+ struct obd_ioctl_hdr hdr;
+ struct obd_ioctl_data *data;
+ int err;
+ int offset = 0;
+
+ if (copy_from_user(&hdr, (void *)arg, sizeof(hdr)))
+ return -EFAULT;
+
+ if (hdr.ioc_version != OBD_IOCTL_VERSION) {
+ CERROR("Version mismatch kernel (%x) vs application (%x)\n",
+ OBD_IOCTL_VERSION, hdr.ioc_version);
+ return -EINVAL;
+ }
+
+ if (hdr.ioc_len > OBD_MAX_IOCTL_BUFFER) {
+ CERROR("User buffer len %d exceeds %d max buffer\n",
+ hdr.ioc_len, OBD_MAX_IOCTL_BUFFER);
+ return -EINVAL;
+ }
+
+ if (hdr.ioc_len < sizeof(struct obd_ioctl_data)) {
+ CERROR("User buffer too small for ioctl (%d)\n", hdr.ioc_len);
+ return -EINVAL;
+ }
+
+ /* When there are lots of processes calling vmalloc on multi-core
+ * system, the high lock contention will hurt performance badly,
+ * obdfilter-survey is an example, which relies on ioctl. So we'd
+ * better avoid vmalloc on ioctl path. LU-66 */
+ OBD_ALLOC_LARGE(*buf, hdr.ioc_len);
+ if (*buf == NULL) {
+ CERROR("Cannot allocate control buffer of len %d\n",
+ hdr.ioc_len);
+ return -EINVAL;
+ }
+ *len = hdr.ioc_len;
+ data = (struct obd_ioctl_data *)*buf;
+
+ if (copy_from_user(*buf, (void *)arg, hdr.ioc_len)) {
+ err = -EFAULT;
+ goto free_buf;
+ }
+ if (hdr.ioc_len != data->ioc_len) {
+ err = -EINVAL;
+ goto free_buf;
+ }
+
+ if (obd_ioctl_is_invalid(data)) {
+ CERROR("ioctl not correctly formatted\n");
+ err = -EINVAL;
+ goto free_buf;
+ }
+
+ if (data->ioc_inllen1) {
+ data->ioc_inlbuf1 = &data->ioc_bulk[0];
+ offset += cfs_size_round(data->ioc_inllen1);
+ }
+
+ if (data->ioc_inllen2) {
+ data->ioc_inlbuf2 = &data->ioc_bulk[0] + offset;
+ offset += cfs_size_round(data->ioc_inllen2);
+ }
+
+ if (data->ioc_inllen3) {
+ data->ioc_inlbuf3 = &data->ioc_bulk[0] + offset;
+ offset += cfs_size_round(data->ioc_inllen3);
+ }
+
+ if (data->ioc_inllen4) {
+ data->ioc_inlbuf4 = &data->ioc_bulk[0] + offset;
+ }
+
+ return 0;
+
+free_buf:
+ OBD_FREE_LARGE(*buf, hdr.ioc_len);
+ return err;
+}
+EXPORT_SYMBOL(obd_ioctl_getdata);
+
+int obd_ioctl_popdata(void *arg, void *data, int len)
+{
+ int err;
+
+ err = copy_to_user(arg, data, len);
+ if (err)
+ err = -EFAULT;
+ return err;
+}
+EXPORT_SYMBOL(obd_ioctl_popdata);
+
+/* opening /dev/obd */
+static int obd_class_open(struct inode *inode, struct file *file)
+{
+ try_module_get(THIS_MODULE);
+ return 0;
+}
+
+/* closing /dev/obd */
+static int obd_class_release(struct inode *inode, struct file *file)
+{
+ module_put(THIS_MODULE);
+ return 0;
+}
+
+/* to control /dev/obd */
+static long obd_class_ioctl(struct file *filp, unsigned int cmd,
+ unsigned long arg)
+{
+ int err = 0;
+
+ /* Allow non-root access for OBD_IOC_PING_TARGET - used by lfs check */
+ if (!capable(CFS_CAP_SYS_ADMIN) && (cmd != OBD_IOC_PING_TARGET))
+ return err = -EACCES;
+ if ((cmd & 0xffffff00) == ((int)'T') << 8) /* ignore all tty ioctls */
+ return err = -ENOTTY;
+
+ err = class_handle_ioctl(cmd, (unsigned long)arg);
+
+ return err;
+}
+
+/* declare character device */
+static struct file_operations obd_psdev_fops = {
+ .owner = THIS_MODULE,
+ .unlocked_ioctl = obd_class_ioctl, /* unlocked_ioctl */
+ .open = obd_class_open, /* open */
+ .release = obd_class_release, /* release */
+};
+
+/* modules setup */
+struct miscdevice obd_psdev = {
+ .minor = OBD_DEV_MINOR,
+ .name = OBD_DEV_NAME,
+ .fops = &obd_psdev_fops,
+};
+
+
+#if defined (CONFIG_PROC_FS)
+static int obd_proc_version_seq_show(struct seq_file *m, void *v)
+{
+ seq_printf(m, "lustre: %s\nkernel: %s\nbuild: %s\n",
+ LUSTRE_VERSION_STRING, "patchless_client", BUILD_VERSION);
+ return 0;
+}
+LPROC_SEQ_FOPS_RO(obd_proc_version);
+
+int obd_proc_pinger_seq_show(struct seq_file *m, void *v)
+{
+ seq_printf(m, "%s\n", "on");
+ return 0;
+}
+LPROC_SEQ_FOPS_RO(obd_proc_pinger);
+
+static int obd_proc_health_seq_show(struct seq_file *m, void *v)
+{
+ bool healthy = true;
+ int i;
+
+ if (libcfs_catastrophe)
+ seq_printf(m, "LBUG\n");
+
+ read_lock(&obd_dev_lock);
+ for (i = 0; i < class_devno_max(); i++) {
+ struct obd_device *obd;
+
+ obd = class_num2obd(i);
+ if (obd == NULL || !obd->obd_attached || !obd->obd_set_up)
+ continue;
+
+ LASSERT(obd->obd_magic == OBD_DEVICE_MAGIC);
+ if (obd->obd_stopping)
+ continue;
+
+ class_incref(obd, __func__, current);
+ read_unlock(&obd_dev_lock);
+
+ if (obd_health_check(NULL, obd)) {
+ seq_printf(m, "device %s reported unhealthy\n",
+ obd->obd_name);
+ healthy = false;
+ }
+ class_decref(obd, __func__, current);
+ read_lock(&obd_dev_lock);
+ }
+ read_unlock(&obd_dev_lock);
+
+ if (healthy)
+ seq_puts(m, "healthy\n");
+ else
+ seq_puts(m, "NOT HEALTHY\n");
+
+ return 0;
+}
+LPROC_SEQ_FOPS_RO(obd_proc_health);
+
+static int obd_proc_jobid_var_seq_show(struct seq_file *m, void *v)
+{
+ seq_printf(m, "%s\n", obd_jobid_var);
+ return 0;
+}
+
+static ssize_t obd_proc_jobid_var_seq_write(struct file *file,
+ const char __user *buffer,
+ size_t count, loff_t *off)
+{
+ if (!count || count > JOBSTATS_JOBID_VAR_MAX_LEN)
+ return -EINVAL;
+
+ memset(obd_jobid_var, 0, JOBSTATS_JOBID_VAR_MAX_LEN + 1);
+
+ /* This might leave the var invalid on error, which is probably fine.*/
+ if (copy_from_user(obd_jobid_var, buffer, count))
+ return -EFAULT;
+
+ /* Trim the trailing '\n' if any */
+ if (obd_jobid_var[count - 1] == '\n')
+ obd_jobid_var[count - 1] = 0;
+
+ return count;
+}
+LPROC_SEQ_FOPS(obd_proc_jobid_var);
+
+static int obd_proc_jobid_name_seq_show(struct seq_file *m, void *v)
+{
+ seq_printf(m, "%s\n", obd_jobid_var);
+ return 0;
+}
+
+static ssize_t obd_proc_jobid_name_seq_write(struct file *file,
+ const char __user *buffer,
+ size_t count, loff_t *off)
+{
+ if (!count || count > JOBSTATS_JOBID_SIZE)
+ return -EINVAL;
+
+ if (copy_from_user(obd_jobid_node, buffer, count))
+ return -EFAULT;
+
+ obd_jobid_node[count] = 0;
+
+ /* Trim the trailing '\n' if any */
+ if (obd_jobid_node[count - 1] == '\n')
+ obd_jobid_node[count - 1] = 0;
+
+ return count;
+}
+LPROC_SEQ_FOPS(obd_proc_jobid_name);
+
+/* Root for /proc/fs/lustre */
+struct proc_dir_entry *proc_lustre_root = NULL;
+EXPORT_SYMBOL(proc_lustre_root);
+
+struct lprocfs_vars lprocfs_base[] = {
+ { "version", &obd_proc_version_fops },
+ { "pinger", &obd_proc_pinger_fops },
+ { "health_check", &obd_proc_health_fops },
+ { "jobid_var", &obd_proc_jobid_var_fops },
+ { .name = "jobid_name",
+ .fops = &obd_proc_jobid_name_fops},
+ { NULL }
+};
+
+static void *obd_device_list_seq_start(struct seq_file *p, loff_t *pos)
+{
+ if (*pos >= class_devno_max())
+ return NULL;
+
+ return pos;
+}
+
+static void obd_device_list_seq_stop(struct seq_file *p, void *v)
+{
+}
+
+static void *obd_device_list_seq_next(struct seq_file *p, void *v, loff_t *pos)
+{
+ ++*pos;
+ if (*pos >= class_devno_max())
+ return NULL;
+
+ return pos;
+}
+
+static int obd_device_list_seq_show(struct seq_file *p, void *v)
+{
+ loff_t index = *(loff_t *)v;
+ struct obd_device *obd = class_num2obd((int)index);
+ char *status;
+
+ if (obd == NULL)
+ return 0;
+
+ LASSERT(obd->obd_magic == OBD_DEVICE_MAGIC);
+ if (obd->obd_stopping)
+ status = "ST";
+ else if (obd->obd_inactive)
+ status = "IN";
+ else if (obd->obd_set_up)
+ status = "UP";
+ else if (obd->obd_attached)
+ status = "AT";
+ else
+ status = "--";
+
+ seq_printf(p, "%3d %s %s %s %s %d\n",
+ (int)index, status, obd->obd_type->typ_name,
+ obd->obd_name, obd->obd_uuid.uuid,
+ atomic_read(&obd->obd_refcount));
+ return 0;
+}
+
+struct seq_operations obd_device_list_sops = {
+ .start = obd_device_list_seq_start,
+ .stop = obd_device_list_seq_stop,
+ .next = obd_device_list_seq_next,
+ .show = obd_device_list_seq_show,
+};
+
+static int obd_device_list_open(struct inode *inode, struct file *file)
+{
+ struct seq_file *seq;
+ int rc = seq_open(file, &obd_device_list_sops);
+
+ if (rc)
+ return rc;
+
+ seq = file->private_data;
+ seq->private = PDE_DATA(inode);
+
+ return 0;
+}
+
+struct file_operations obd_device_list_fops = {
+ .owner = THIS_MODULE,
+ .open = obd_device_list_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = seq_release,
+};
+
+int class_procfs_init(void)
+{
+ int rc = 0;
+
+ proc_lustre_root = lprocfs_register("fs/lustre", NULL,
+ lprocfs_base, NULL);
+ if (IS_ERR(proc_lustre_root)) {
+ rc = PTR_ERR(proc_lustre_root);
+ proc_lustre_root = NULL;
+ goto out;
+ }
+
+ rc = lprocfs_seq_create(proc_lustre_root, "devices", 0444,
+ &obd_device_list_fops, NULL);
+out:
+ if (rc)
+ CERROR("error adding /proc/fs/lustre/devices file\n");
+ return 0;
+}
+
+int class_procfs_clean(void)
+{
+ if (proc_lustre_root) {
+ lprocfs_remove(&proc_lustre_root);
+ }
+ return 0;
+}
+#endif /* CONFIG_PROC_FS */
diff --git a/drivers/staging/lustre/lustre/obdclass/linux/linux-obdo.c b/drivers/staging/lustre/lustre/obdclass/linux/linux-obdo.c
new file mode 100644
index 000000000..62ed706b1
--- /dev/null
+++ b/drivers/staging/lustre/lustre/obdclass/linux/linux-obdo.c
@@ -0,0 +1,222 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2011, 2012, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ *
+ * lustre/obdclass/linux/linux-obdo.c
+ *
+ * Object Devices Class Driver
+ * These are the only exported functions, they provide some generic
+ * infrastructure for managing object devices
+ */
+
+#define DEBUG_SUBSYSTEM S_CLASS
+
+#include <linux/module.h>
+#include "../../include/obd_class.h"
+#include "../../include/lustre/lustre_idl.h"
+
+#include <linux/fs.h>
+#include <linux/pagemap.h> /* for PAGE_CACHE_SIZE */
+
+/*FIXME: Just copy from obdo_from_inode*/
+void obdo_from_la(struct obdo *dst, struct lu_attr *la, __u64 valid)
+{
+ u32 newvalid = 0;
+
+ if (valid & LA_ATIME) {
+ dst->o_atime = la->la_atime;
+ newvalid |= OBD_MD_FLATIME;
+ }
+ if (valid & LA_MTIME) {
+ dst->o_mtime = la->la_mtime;
+ newvalid |= OBD_MD_FLMTIME;
+ }
+ if (valid & LA_CTIME) {
+ dst->o_ctime = la->la_ctime;
+ newvalid |= OBD_MD_FLCTIME;
+ }
+ if (valid & LA_SIZE) {
+ dst->o_size = la->la_size;
+ newvalid |= OBD_MD_FLSIZE;
+ }
+ if (valid & LA_BLOCKS) { /* allocation of space (x512 bytes) */
+ dst->o_blocks = la->la_blocks;
+ newvalid |= OBD_MD_FLBLOCKS;
+ }
+ if (valid & LA_TYPE) {
+ dst->o_mode = (dst->o_mode & S_IALLUGO) |
+ (la->la_mode & S_IFMT);
+ newvalid |= OBD_MD_FLTYPE;
+ }
+ if (valid & LA_MODE) {
+ dst->o_mode = (dst->o_mode & S_IFMT) |
+ (la->la_mode & S_IALLUGO);
+ newvalid |= OBD_MD_FLMODE;
+ }
+ if (valid & LA_UID) {
+ dst->o_uid = la->la_uid;
+ newvalid |= OBD_MD_FLUID;
+ }
+ if (valid & LA_GID) {
+ dst->o_gid = la->la_gid;
+ newvalid |= OBD_MD_FLGID;
+ }
+ dst->o_valid |= newvalid;
+}
+EXPORT_SYMBOL(obdo_from_la);
+
+/*FIXME: Just copy from obdo_from_inode*/
+void la_from_obdo(struct lu_attr *dst, struct obdo *obdo, u32 valid)
+{
+ __u64 newvalid = 0;
+
+ valid &= obdo->o_valid;
+
+ if (valid & OBD_MD_FLATIME) {
+ dst->la_atime = obdo->o_atime;
+ newvalid |= LA_ATIME;
+ }
+ if (valid & OBD_MD_FLMTIME) {
+ dst->la_mtime = obdo->o_mtime;
+ newvalid |= LA_MTIME;
+ }
+ if (valid & OBD_MD_FLCTIME) {
+ dst->la_ctime = obdo->o_ctime;
+ newvalid |= LA_CTIME;
+ }
+ if (valid & OBD_MD_FLSIZE) {
+ dst->la_size = obdo->o_size;
+ newvalid |= LA_SIZE;
+ }
+ if (valid & OBD_MD_FLBLOCKS) {
+ dst->la_blocks = obdo->o_blocks;
+ newvalid |= LA_BLOCKS;
+ }
+ if (valid & OBD_MD_FLTYPE) {
+ dst->la_mode = (dst->la_mode & S_IALLUGO) |
+ (obdo->o_mode & S_IFMT);
+ newvalid |= LA_TYPE;
+ }
+ if (valid & OBD_MD_FLMODE) {
+ dst->la_mode = (dst->la_mode & S_IFMT) |
+ (obdo->o_mode & S_IALLUGO);
+ newvalid |= LA_MODE;
+ }
+ if (valid & OBD_MD_FLUID) {
+ dst->la_uid = obdo->o_uid;
+ newvalid |= LA_UID;
+ }
+ if (valid & OBD_MD_FLGID) {
+ dst->la_gid = obdo->o_gid;
+ newvalid |= LA_GID;
+ }
+ dst->la_valid = newvalid;
+}
+EXPORT_SYMBOL(la_from_obdo);
+
+void obdo_refresh_inode(struct inode *dst, struct obdo *src, u32 valid)
+{
+ valid &= src->o_valid;
+
+ if (valid & (OBD_MD_FLCTIME | OBD_MD_FLMTIME))
+ CDEBUG(D_INODE,
+ "valid %#llx, cur time %lu/%lu, new %llu/%llu\n",
+ src->o_valid, LTIME_S(dst->i_mtime),
+ LTIME_S(dst->i_ctime), src->o_mtime, src->o_ctime);
+
+ if (valid & OBD_MD_FLATIME && src->o_atime > LTIME_S(dst->i_atime))
+ LTIME_S(dst->i_atime) = src->o_atime;
+ if (valid & OBD_MD_FLMTIME && src->o_mtime > LTIME_S(dst->i_mtime))
+ LTIME_S(dst->i_mtime) = src->o_mtime;
+ if (valid & OBD_MD_FLCTIME && src->o_ctime > LTIME_S(dst->i_ctime))
+ LTIME_S(dst->i_ctime) = src->o_ctime;
+ if (valid & OBD_MD_FLSIZE)
+ i_size_write(dst, src->o_size);
+ /* optimum IO size */
+ if (valid & OBD_MD_FLBLKSZ && src->o_blksize > (1 << dst->i_blkbits))
+ dst->i_blkbits = ffs(src->o_blksize) - 1;
+
+ if (dst->i_blkbits < PAGE_CACHE_SHIFT)
+ dst->i_blkbits = PAGE_CACHE_SHIFT;
+
+ /* allocation of space */
+ if (valid & OBD_MD_FLBLOCKS && src->o_blocks > dst->i_blocks)
+ /*
+ * XXX shouldn't overflow be checked here like in
+ * obdo_to_inode().
+ */
+ dst->i_blocks = src->o_blocks;
+}
+EXPORT_SYMBOL(obdo_refresh_inode);
+
+void obdo_to_inode(struct inode *dst, struct obdo *src, u32 valid)
+{
+ valid &= src->o_valid;
+
+ LASSERTF(!(valid & (OBD_MD_FLTYPE | OBD_MD_FLGENER | OBD_MD_FLFID |
+ OBD_MD_FLID | OBD_MD_FLGROUP)),
+ "object "DOSTID", valid %x\n", POSTID(&src->o_oi), valid);
+
+ if (valid & (OBD_MD_FLCTIME | OBD_MD_FLMTIME))
+ CDEBUG(D_INODE,
+ "valid %#llx, cur time %lu/%lu, new %llu/%llu\n",
+ src->o_valid, LTIME_S(dst->i_mtime),
+ LTIME_S(dst->i_ctime), src->o_mtime, src->o_ctime);
+
+ if (valid & OBD_MD_FLATIME)
+ LTIME_S(dst->i_atime) = src->o_atime;
+ if (valid & OBD_MD_FLMTIME)
+ LTIME_S(dst->i_mtime) = src->o_mtime;
+ if (valid & OBD_MD_FLCTIME && src->o_ctime > LTIME_S(dst->i_ctime))
+ LTIME_S(dst->i_ctime) = src->o_ctime;
+ if (valid & OBD_MD_FLSIZE)
+ i_size_write(dst, src->o_size);
+ if (valid & OBD_MD_FLBLOCKS) { /* allocation of space */
+ dst->i_blocks = src->o_blocks;
+ if (dst->i_blocks < src->o_blocks) /* overflow */
+ dst->i_blocks = -1;
+
+ }
+ if (valid & OBD_MD_FLBLKSZ)
+ dst->i_blkbits = ffs(src->o_blksize)-1;
+ if (valid & OBD_MD_FLMODE)
+ dst->i_mode = (dst->i_mode & S_IFMT) | (src->o_mode & ~S_IFMT);
+ if (valid & OBD_MD_FLUID)
+ dst->i_uid = make_kuid(&init_user_ns, src->o_uid);
+ if (valid & OBD_MD_FLGID)
+ dst->i_gid = make_kgid(&init_user_ns, src->o_gid);
+ if (valid & OBD_MD_FLFLAGS)
+ dst->i_flags = src->o_flags;
+}
+EXPORT_SYMBOL(obdo_to_inode);
diff --git a/drivers/staging/lustre/lustre/obdclass/linux/linux-sysctl.c b/drivers/staging/lustre/lustre/obdclass/linux/linux-sysctl.c
new file mode 100644
index 000000000..4b62d2576
--- /dev/null
+++ b/drivers/staging/lustre/lustre/obdclass/linux/linux-sysctl.c
@@ -0,0 +1,405 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2011, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ */
+
+#include <linux/module.h>
+#include <linux/sysctl.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/proc_fs.h>
+#include <linux/slab.h>
+#include <linux/stat.h>
+#include <linux/ctype.h>
+#include <linux/bitops.h>
+#include <linux/uaccess.h>
+#include <linux/utsname.h>
+
+#define DEBUG_SUBSYSTEM S_CLASS
+
+#include "../../include/obd_support.h"
+#include "../../include/lprocfs_status.h"
+
+#ifdef CONFIG_SYSCTL
+static struct ctl_table_header *obd_table_header;
+#endif
+
+
+#define OBD_SYSCTL 300
+
+enum {
+ OBD_TIMEOUT = 3, /* RPC timeout before recovery/intr */
+ OBD_DUMP_ON_TIMEOUT, /* dump kernel debug log upon eviction */
+ OBD_MEMUSED, /* bytes currently OBD_ALLOCated */
+ OBD_PAGESUSED, /* pages currently OBD_PAGE_ALLOCated */
+ OBD_MAXMEMUSED, /* maximum bytes OBD_ALLOCated concurrently */
+ OBD_MAXPAGESUSED, /* maximum pages OBD_PAGE_ALLOCated concurrently */
+ OBD_SYNCFILTER, /* XXX temporary, as we play with sync osts.. */
+ OBD_LDLM_TIMEOUT, /* LDLM timeout for ASTs before client eviction */
+ OBD_DUMP_ON_EVICTION, /* dump kernel debug log upon eviction */
+ OBD_DEBUG_PEER_ON_TIMEOUT, /* dump peer debug when RPC times out */
+ OBD_ALLOC_FAIL_RATE, /* memory allocation random failure rate */
+ OBD_MAX_DIRTY_PAGES, /* maximum dirty pages */
+ OBD_AT_MIN, /* Adaptive timeouts params */
+ OBD_AT_MAX,
+ OBD_AT_EXTRA,
+ OBD_AT_EARLY_MARGIN,
+ OBD_AT_HISTORY,
+};
+
+
+#ifdef CONFIG_SYSCTL
+static int proc_set_timeout(struct ctl_table *table, int write,
+ void __user *buffer, size_t *lenp, loff_t *ppos)
+{
+ int rc;
+
+ rc = proc_dointvec(table, write, buffer, lenp, ppos);
+ if (ldlm_timeout >= obd_timeout)
+ ldlm_timeout = max(obd_timeout / 3, 1U);
+ return rc;
+}
+
+static int proc_memory_alloc(struct ctl_table *table, int write,
+ void __user *buffer, size_t *lenp, loff_t *ppos)
+{
+ char buf[22];
+ int len;
+
+ if (!*lenp || (*ppos && !write)) {
+ *lenp = 0;
+ return 0;
+ }
+ if (write)
+ return -EINVAL;
+
+ len = snprintf(buf, sizeof(buf), "%llu\n", obd_memory_sum());
+ if (len > *lenp)
+ len = *lenp;
+ buf[len] = '\0';
+ if (copy_to_user(buffer, buf, len))
+ return -EFAULT;
+ *lenp = len;
+ *ppos += *lenp;
+ return 0;
+}
+
+static int proc_pages_alloc(struct ctl_table *table, int write,
+ void __user *buffer, size_t *lenp, loff_t *ppos)
+{
+ char buf[22];
+ int len;
+
+ if (!*lenp || (*ppos && !write)) {
+ *lenp = 0;
+ return 0;
+ }
+ if (write)
+ return -EINVAL;
+
+ len = snprintf(buf, sizeof(buf), "%llu\n", obd_pages_sum());
+ if (len > *lenp)
+ len = *lenp;
+ buf[len] = '\0';
+ if (copy_to_user(buffer, buf, len))
+ return -EFAULT;
+ *lenp = len;
+ *ppos += *lenp;
+ return 0;
+}
+
+static int proc_mem_max(struct ctl_table *table, int write, void __user *buffer,
+ size_t *lenp, loff_t *ppos)
+{
+ char buf[22];
+ int len;
+
+ if (!*lenp || (*ppos && !write)) {
+ *lenp = 0;
+ return 0;
+ }
+ if (write)
+ return -EINVAL;
+
+ len = snprintf(buf, sizeof(buf), "%llu\n", obd_memory_max());
+ if (len > *lenp)
+ len = *lenp;
+ buf[len] = '\0';
+ if (copy_to_user(buffer, buf, len))
+ return -EFAULT;
+ *lenp = len;
+ *ppos += *lenp;
+ return 0;
+}
+
+static int proc_pages_max(struct ctl_table *table, int write,
+ void __user *buffer, size_t *lenp, loff_t *ppos)
+{
+ char buf[22];
+ int len;
+
+ if (!*lenp || (*ppos && !write)) {
+ *lenp = 0;
+ return 0;
+ }
+ if (write)
+ return -EINVAL;
+
+ len = snprintf(buf, sizeof(buf), "%llu\n", obd_pages_max());
+ if (len > *lenp)
+ len = *lenp;
+ buf[len] = '\0';
+ if (copy_to_user(buffer, buf, len))
+ return -EFAULT;
+ *lenp = len;
+ *ppos += *lenp;
+ return 0;
+}
+
+static int proc_max_dirty_pages_in_mb(struct ctl_table *table, int write,
+ void __user *buffer, size_t *lenp, loff_t *ppos)
+{
+ int rc = 0;
+
+ if (!table->data || !table->maxlen || !*lenp || (*ppos && !write)) {
+ *lenp = 0;
+ return 0;
+ }
+ if (write) {
+ rc = lprocfs_write_frac_helper(buffer, *lenp,
+ (unsigned int *)table->data,
+ 1 << (20 - PAGE_CACHE_SHIFT));
+ /* Don't allow them to let dirty pages exceed 90% of system
+ * memory and set a hard minimum of 4MB. */
+ if (obd_max_dirty_pages > ((totalram_pages / 10) * 9)) {
+ CERROR("Refusing to set max dirty pages to %u, which is more than 90%% of available RAM; setting to %lu\n",
+ obd_max_dirty_pages,
+ ((totalram_pages / 10) * 9));
+ obd_max_dirty_pages = (totalram_pages / 10) * 9;
+ } else if (obd_max_dirty_pages < 4 << (20 - PAGE_CACHE_SHIFT)) {
+ obd_max_dirty_pages = 4 << (20 - PAGE_CACHE_SHIFT);
+ }
+ } else {
+ char buf[21];
+ int len;
+
+ len = lprocfs_read_frac_helper(buf, sizeof(buf),
+ *(unsigned int *)table->data,
+ 1 << (20 - PAGE_CACHE_SHIFT));
+ if (len > *lenp)
+ len = *lenp;
+ buf[len] = '\0';
+ if (copy_to_user(buffer, buf, len))
+ return -EFAULT;
+ *lenp = len;
+ }
+ *ppos += *lenp;
+ return rc;
+}
+
+static int proc_alloc_fail_rate(struct ctl_table *table, int write,
+ void __user *buffer, size_t *lenp, loff_t *ppos)
+{
+ int rc = 0;
+
+ if (!table->data || !table->maxlen || !*lenp || (*ppos && !write)) {
+ *lenp = 0;
+ return 0;
+ }
+ if (write) {
+ rc = lprocfs_write_frac_helper(buffer, *lenp,
+ (unsigned int *)table->data,
+ OBD_ALLOC_FAIL_MULT);
+ } else {
+ char buf[21];
+ int len;
+
+ len = lprocfs_read_frac_helper(buf, 21,
+ *(unsigned int *)table->data,
+ OBD_ALLOC_FAIL_MULT);
+ if (len > *lenp)
+ len = *lenp;
+ buf[len] = '\0';
+ if (copy_to_user(buffer, buf, len))
+ return -EFAULT;
+ *lenp = len;
+ }
+ *ppos += *lenp;
+ return rc;
+}
+
+static struct ctl_table obd_table[] = {
+ {
+ .procname = "timeout",
+ .data = &obd_timeout,
+ .maxlen = sizeof(int),
+ .mode = 0644,
+ .proc_handler = &proc_set_timeout
+ },
+ {
+ .procname = "debug_peer_on_timeout",
+ .data = &obd_debug_peer_on_timeout,
+ .maxlen = sizeof(int),
+ .mode = 0644,
+ .proc_handler = &proc_dointvec
+ },
+ {
+ .procname = "dump_on_timeout",
+ .data = &obd_dump_on_timeout,
+ .maxlen = sizeof(int),
+ .mode = 0644,
+ .proc_handler = &proc_dointvec
+ },
+ {
+ .procname = "dump_on_eviction",
+ .data = &obd_dump_on_eviction,
+ .maxlen = sizeof(int),
+ .mode = 0644,
+ .proc_handler = &proc_dointvec
+ },
+ {
+ .procname = "memused",
+ .data = NULL,
+ .maxlen = 0,
+ .mode = 0444,
+ .proc_handler = &proc_memory_alloc
+ },
+ {
+ .procname = "pagesused",
+ .data = NULL,
+ .maxlen = 0,
+ .mode = 0444,
+ .proc_handler = &proc_pages_alloc
+ },
+ {
+ .procname = "memused_max",
+ .data = NULL,
+ .maxlen = 0,
+ .mode = 0444,
+ .proc_handler = &proc_mem_max
+ },
+ {
+ .procname = "pagesused_max",
+ .data = NULL,
+ .maxlen = 0,
+ .mode = 0444,
+ .proc_handler = &proc_pages_max
+ },
+ {
+ .procname = "ldlm_timeout",
+ .data = &ldlm_timeout,
+ .maxlen = sizeof(int),
+ .mode = 0644,
+ .proc_handler = &proc_set_timeout
+ },
+ {
+ .procname = "alloc_fail_rate",
+ .data = &obd_alloc_fail_rate,
+ .maxlen = sizeof(int),
+ .mode = 0644,
+ .proc_handler = &proc_alloc_fail_rate
+ },
+ {
+ .procname = "max_dirty_mb",
+ .data = &obd_max_dirty_pages,
+ .maxlen = sizeof(int),
+ .mode = 0644,
+ .proc_handler = &proc_max_dirty_pages_in_mb
+ },
+ {
+ .procname = "at_min",
+ .data = &at_min,
+ .maxlen = sizeof(int),
+ .mode = 0644,
+ .proc_handler = &proc_dointvec,
+ },
+ {
+ .procname = "at_max",
+ .data = &at_max,
+ .maxlen = sizeof(int),
+ .mode = 0644,
+ .proc_handler = &proc_dointvec,
+ },
+ {
+ .procname = "at_extra",
+ .data = &at_extra,
+ .maxlen = sizeof(int),
+ .mode = 0644,
+ .proc_handler = &proc_dointvec,
+ },
+ {
+ .procname = "at_early_margin",
+ .data = &at_early_margin,
+ .maxlen = sizeof(int),
+ .mode = 0644,
+ .proc_handler = &proc_dointvec,
+ },
+ {
+ .procname = "at_history",
+ .data = &at_history,
+ .maxlen = sizeof(int),
+ .mode = 0644,
+ .proc_handler = &proc_dointvec,
+ },
+ {}
+};
+
+static struct ctl_table parent_table[] = {
+ {
+ .procname = "lustre",
+ .data = NULL,
+ .maxlen = 0,
+ .mode = 0555,
+ .child = obd_table
+ },
+ {}
+};
+#endif
+
+void obd_sysctl_init(void)
+{
+#ifdef CONFIG_SYSCTL
+ if (!obd_table_header)
+ obd_table_header = register_sysctl_table(parent_table);
+#endif
+}
+
+void obd_sysctl_clean(void)
+{
+#ifdef CONFIG_SYSCTL
+ if (obd_table_header)
+ unregister_sysctl_table(obd_table_header);
+ obd_table_header = NULL;
+#endif
+}
diff --git a/drivers/staging/lustre/lustre/obdclass/llog.c b/drivers/staging/lustre/lustre/obdclass/llog.c
new file mode 100644
index 000000000..114be4a78
--- /dev/null
+++ b/drivers/staging/lustre/lustre/obdclass/llog.c
@@ -0,0 +1,1007 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2012, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ *
+ * lustre/obdclass/llog.c
+ *
+ * OST<->MDS recovery logging infrastructure.
+ * Invariants in implementation:
+ * - we do not share logs among different OST<->MDS connections, so that
+ * if an OST or MDS fails it need only look at log(s) relevant to itself
+ *
+ * Author: Andreas Dilger <adilger@clusterfs.com>
+ * Author: Alex Zhuravlev <bzzz@whamcloud.com>
+ * Author: Mikhail Pershin <tappro@whamcloud.com>
+ */
+
+#define DEBUG_SUBSYSTEM S_LOG
+
+
+#include "../include/obd_class.h"
+#include "../include/lustre_log.h"
+#include "llog_internal.h"
+
+/*
+ * Allocate a new log or catalog handle
+ * Used inside llog_open().
+ */
+static struct llog_handle *llog_alloc_handle(void)
+{
+ struct llog_handle *loghandle;
+
+ OBD_ALLOC_PTR(loghandle);
+ if (loghandle == NULL)
+ return NULL;
+
+ init_rwsem(&loghandle->lgh_lock);
+ spin_lock_init(&loghandle->lgh_hdr_lock);
+ INIT_LIST_HEAD(&loghandle->u.phd.phd_entry);
+ atomic_set(&loghandle->lgh_refcount, 1);
+
+ return loghandle;
+}
+
+/*
+ * Free llog handle and header data if exists. Used in llog_close() only
+ */
+static void llog_free_handle(struct llog_handle *loghandle)
+{
+ LASSERT(loghandle != NULL);
+
+ /* failed llog_init_handle */
+ if (!loghandle->lgh_hdr)
+ goto out;
+
+ if (loghandle->lgh_hdr->llh_flags & LLOG_F_IS_PLAIN)
+ LASSERT(list_empty(&loghandle->u.phd.phd_entry));
+ else if (loghandle->lgh_hdr->llh_flags & LLOG_F_IS_CAT)
+ LASSERT(list_empty(&loghandle->u.chd.chd_head));
+ LASSERT(sizeof(*(loghandle->lgh_hdr)) == LLOG_CHUNK_SIZE);
+ OBD_FREE(loghandle->lgh_hdr, LLOG_CHUNK_SIZE);
+out:
+ OBD_FREE_PTR(loghandle);
+}
+
+void llog_handle_get(struct llog_handle *loghandle)
+{
+ atomic_inc(&loghandle->lgh_refcount);
+}
+
+void llog_handle_put(struct llog_handle *loghandle)
+{
+ LASSERT(atomic_read(&loghandle->lgh_refcount) > 0);
+ if (atomic_dec_and_test(&loghandle->lgh_refcount))
+ llog_free_handle(loghandle);
+}
+
+/* returns negative on error; 0 if success; 1 if success & log destroyed */
+int llog_cancel_rec(const struct lu_env *env, struct llog_handle *loghandle,
+ int index)
+{
+ struct llog_log_hdr *llh = loghandle->lgh_hdr;
+ int rc = 0;
+
+ CDEBUG(D_RPCTRACE, "Canceling %d in log "DOSTID"\n",
+ index, POSTID(&loghandle->lgh_id.lgl_oi));
+
+ if (index == 0) {
+ CERROR("Can't cancel index 0 which is header\n");
+ return -EINVAL;
+ }
+
+ spin_lock(&loghandle->lgh_hdr_lock);
+ if (!ext2_clear_bit(index, llh->llh_bitmap)) {
+ spin_unlock(&loghandle->lgh_hdr_lock);
+ CDEBUG(D_RPCTRACE, "Catalog index %u already clear?\n", index);
+ return -ENOENT;
+ }
+
+ llh->llh_count--;
+
+ if ((llh->llh_flags & LLOG_F_ZAP_WHEN_EMPTY) &&
+ (llh->llh_count == 1) &&
+ (loghandle->lgh_last_idx == (LLOG_BITMAP_BYTES * 8) - 1)) {
+ spin_unlock(&loghandle->lgh_hdr_lock);
+ rc = llog_destroy(env, loghandle);
+ if (rc < 0) {
+ CERROR("%s: can't destroy empty llog #"DOSTID
+ "#%08x: rc = %d\n",
+ loghandle->lgh_ctxt->loc_obd->obd_name,
+ POSTID(&loghandle->lgh_id.lgl_oi),
+ loghandle->lgh_id.lgl_ogen, rc);
+ goto out_err;
+ }
+ return 1;
+ }
+ spin_unlock(&loghandle->lgh_hdr_lock);
+
+ rc = llog_write(env, loghandle, &llh->llh_hdr, NULL, 0, NULL, 0);
+ if (rc < 0) {
+ CERROR("%s: fail to write header for llog #"DOSTID
+ "#%08x: rc = %d\n",
+ loghandle->lgh_ctxt->loc_obd->obd_name,
+ POSTID(&loghandle->lgh_id.lgl_oi),
+ loghandle->lgh_id.lgl_ogen, rc);
+ goto out_err;
+ }
+ return 0;
+out_err:
+ spin_lock(&loghandle->lgh_hdr_lock);
+ ext2_set_bit(index, llh->llh_bitmap);
+ llh->llh_count++;
+ spin_unlock(&loghandle->lgh_hdr_lock);
+ return rc;
+}
+EXPORT_SYMBOL(llog_cancel_rec);
+
+static int llog_read_header(const struct lu_env *env,
+ struct llog_handle *handle,
+ struct obd_uuid *uuid)
+{
+ struct llog_operations *lop;
+ int rc;
+
+ rc = llog_handle2ops(handle, &lop);
+ if (rc)
+ return rc;
+
+ if (lop->lop_read_header == NULL)
+ return -EOPNOTSUPP;
+
+ rc = lop->lop_read_header(env, handle);
+ if (rc == LLOG_EEMPTY) {
+ struct llog_log_hdr *llh = handle->lgh_hdr;
+
+ handle->lgh_last_idx = 0; /* header is record with index 0 */
+ llh->llh_count = 1; /* for the header record */
+ llh->llh_hdr.lrh_type = LLOG_HDR_MAGIC;
+ llh->llh_hdr.lrh_len = llh->llh_tail.lrt_len = LLOG_CHUNK_SIZE;
+ llh->llh_hdr.lrh_index = llh->llh_tail.lrt_index = 0;
+ llh->llh_timestamp = get_seconds();
+ if (uuid)
+ memcpy(&llh->llh_tgtuuid, uuid,
+ sizeof(llh->llh_tgtuuid));
+ llh->llh_bitmap_offset = offsetof(typeof(*llh), llh_bitmap);
+ ext2_set_bit(0, llh->llh_bitmap);
+ rc = 0;
+ }
+ return rc;
+}
+
+int llog_init_handle(const struct lu_env *env, struct llog_handle *handle,
+ int flags, struct obd_uuid *uuid)
+{
+ struct llog_log_hdr *llh;
+ int rc;
+
+ LASSERT(handle->lgh_hdr == NULL);
+
+ OBD_ALLOC_PTR(llh);
+ if (llh == NULL)
+ return -ENOMEM;
+ handle->lgh_hdr = llh;
+ /* first assign flags to use llog_client_ops */
+ llh->llh_flags = flags;
+ rc = llog_read_header(env, handle, uuid);
+ if (rc == 0) {
+ if (unlikely((llh->llh_flags & LLOG_F_IS_PLAIN &&
+ flags & LLOG_F_IS_CAT) ||
+ (llh->llh_flags & LLOG_F_IS_CAT &&
+ flags & LLOG_F_IS_PLAIN))) {
+ CERROR("%s: llog type is %s but initializing %s\n",
+ handle->lgh_ctxt->loc_obd->obd_name,
+ llh->llh_flags & LLOG_F_IS_CAT ?
+ "catalog" : "plain",
+ flags & LLOG_F_IS_CAT ? "catalog" : "plain");
+ rc = -EINVAL;
+ goto out;
+ } else if (llh->llh_flags &
+ (LLOG_F_IS_PLAIN | LLOG_F_IS_CAT)) {
+ /*
+ * it is possible to open llog without specifying llog
+ * type so it is taken from llh_flags
+ */
+ flags = llh->llh_flags;
+ } else {
+ /* for some reason the llh_flags has no type set */
+ CERROR("llog type is not specified!\n");
+ rc = -EINVAL;
+ goto out;
+ }
+ if (unlikely(uuid &&
+ !obd_uuid_equals(uuid, &llh->llh_tgtuuid))) {
+ CERROR("%s: llog uuid mismatch: %s/%s\n",
+ handle->lgh_ctxt->loc_obd->obd_name,
+ (char *)uuid->uuid,
+ (char *)llh->llh_tgtuuid.uuid);
+ rc = -EEXIST;
+ goto out;
+ }
+ }
+ if (flags & LLOG_F_IS_CAT) {
+ LASSERT(list_empty(&handle->u.chd.chd_head));
+ INIT_LIST_HEAD(&handle->u.chd.chd_head);
+ llh->llh_size = sizeof(struct llog_logid_rec);
+ } else if (!(flags & LLOG_F_IS_PLAIN)) {
+ CERROR("%s: unknown flags: %#x (expected %#x or %#x)\n",
+ handle->lgh_ctxt->loc_obd->obd_name,
+ flags, LLOG_F_IS_CAT, LLOG_F_IS_PLAIN);
+ rc = -EINVAL;
+ }
+out:
+ if (rc) {
+ OBD_FREE_PTR(llh);
+ handle->lgh_hdr = NULL;
+ }
+ return rc;
+}
+EXPORT_SYMBOL(llog_init_handle);
+
+static int llog_process_thread(void *arg)
+{
+ struct llog_process_info *lpi = arg;
+ struct llog_handle *loghandle = lpi->lpi_loghandle;
+ struct llog_log_hdr *llh = loghandle->lgh_hdr;
+ struct llog_process_cat_data *cd = lpi->lpi_catdata;
+ char *buf;
+ __u64 cur_offset = LLOG_CHUNK_SIZE;
+ __u64 last_offset;
+ int rc = 0, index = 1, last_index;
+ int saved_index = 0;
+ int last_called_index = 0;
+
+ LASSERT(llh);
+
+ OBD_ALLOC(buf, LLOG_CHUNK_SIZE);
+ if (!buf) {
+ lpi->lpi_rc = -ENOMEM;
+ return 0;
+ }
+
+ if (cd != NULL) {
+ last_called_index = cd->lpcd_first_idx;
+ index = cd->lpcd_first_idx + 1;
+ }
+ if (cd != NULL && cd->lpcd_last_idx)
+ last_index = cd->lpcd_last_idx;
+ else
+ last_index = LLOG_BITMAP_BYTES * 8 - 1;
+
+ while (rc == 0) {
+ struct llog_rec_hdr *rec;
+
+ /* skip records not set in bitmap */
+ while (index <= last_index &&
+ !ext2_test_bit(index, llh->llh_bitmap))
+ ++index;
+
+ LASSERT(index <= last_index + 1);
+ if (index == last_index + 1)
+ break;
+repeat:
+ CDEBUG(D_OTHER, "index: %d last_index %d\n",
+ index, last_index);
+
+ /* get the buf with our target record; avoid old garbage */
+ memset(buf, 0, LLOG_CHUNK_SIZE);
+ last_offset = cur_offset;
+ rc = llog_next_block(lpi->lpi_env, loghandle, &saved_index,
+ index, &cur_offset, buf, LLOG_CHUNK_SIZE);
+ if (rc)
+ goto out;
+
+ /* NB: when rec->lrh_len is accessed it is already swabbed
+ * since it is used at the "end" of the loop and the rec
+ * swabbing is done at the beginning of the loop. */
+ for (rec = (struct llog_rec_hdr *)buf;
+ (char *)rec < buf + LLOG_CHUNK_SIZE;
+ rec = (struct llog_rec_hdr *)((char *)rec + rec->lrh_len)){
+
+ CDEBUG(D_OTHER, "processing rec 0x%p type %#x\n",
+ rec, rec->lrh_type);
+
+ if (LLOG_REC_HDR_NEEDS_SWABBING(rec))
+ lustre_swab_llog_rec(rec);
+
+ CDEBUG(D_OTHER, "after swabbing, type=%#x idx=%d\n",
+ rec->lrh_type, rec->lrh_index);
+
+ if (rec->lrh_index == 0) {
+ /* probably another rec just got added? */
+ rc = 0;
+ if (index <= loghandle->lgh_last_idx)
+ goto repeat;
+ goto out; /* no more records */
+ }
+ if (rec->lrh_len == 0 ||
+ rec->lrh_len > LLOG_CHUNK_SIZE) {
+ CWARN("invalid length %d in llog record for index %d/%d\n",
+ rec->lrh_len,
+ rec->lrh_index, index);
+ rc = -EINVAL;
+ goto out;
+ }
+
+ if (rec->lrh_index < index) {
+ CDEBUG(D_OTHER, "skipping lrh_index %d\n",
+ rec->lrh_index);
+ continue;
+ }
+
+ CDEBUG(D_OTHER,
+ "lrh_index: %d lrh_len: %d (%d remains)\n",
+ rec->lrh_index, rec->lrh_len,
+ (int)(buf + LLOG_CHUNK_SIZE - (char *)rec));
+
+ loghandle->lgh_cur_idx = rec->lrh_index;
+ loghandle->lgh_cur_offset = (char *)rec - (char *)buf +
+ last_offset;
+
+ /* if set, process the callback on this record */
+ if (ext2_test_bit(index, llh->llh_bitmap)) {
+ rc = lpi->lpi_cb(lpi->lpi_env, loghandle, rec,
+ lpi->lpi_cbdata);
+ last_called_index = index;
+ if (rc == LLOG_PROC_BREAK) {
+ goto out;
+ } else if (rc == LLOG_DEL_RECORD) {
+ llog_cancel_rec(lpi->lpi_env,
+ loghandle,
+ rec->lrh_index);
+ rc = 0;
+ }
+ if (rc)
+ goto out;
+ } else {
+ CDEBUG(D_OTHER, "Skipped index %d\n", index);
+ }
+
+ /* next record, still in buffer? */
+ ++index;
+ if (index > last_index) {
+ rc = 0;
+ goto out;
+ }
+ }
+ }
+
+out:
+ if (cd != NULL)
+ cd->lpcd_last_idx = last_called_index;
+
+ OBD_FREE(buf, LLOG_CHUNK_SIZE);
+ lpi->lpi_rc = rc;
+ return 0;
+}
+
+static int llog_process_thread_daemonize(void *arg)
+{
+ struct llog_process_info *lpi = arg;
+ struct lu_env env;
+ int rc;
+
+ unshare_fs_struct();
+
+ /* client env has no keys, tags is just 0 */
+ rc = lu_env_init(&env, LCT_LOCAL | LCT_MG_THREAD);
+ if (rc)
+ goto out;
+ lpi->lpi_env = &env;
+
+ rc = llog_process_thread(arg);
+
+ lu_env_fini(&env);
+out:
+ complete(&lpi->lpi_completion);
+ return rc;
+}
+
+int llog_process_or_fork(const struct lu_env *env,
+ struct llog_handle *loghandle,
+ llog_cb_t cb, void *data, void *catdata, bool fork)
+{
+ struct llog_process_info *lpi;
+ int rc;
+
+ OBD_ALLOC_PTR(lpi);
+ if (lpi == NULL) {
+ CERROR("cannot alloc pointer\n");
+ return -ENOMEM;
+ }
+ lpi->lpi_loghandle = loghandle;
+ lpi->lpi_cb = cb;
+ lpi->lpi_cbdata = data;
+ lpi->lpi_catdata = catdata;
+
+ if (fork) {
+ /* The new thread can't use parent env,
+ * init the new one in llog_process_thread_daemonize. */
+ lpi->lpi_env = NULL;
+ init_completion(&lpi->lpi_completion);
+ rc = PTR_ERR(kthread_run(llog_process_thread_daemonize, lpi,
+ "llog_process_thread"));
+ if (IS_ERR_VALUE(rc)) {
+ CERROR("%s: cannot start thread: rc = %d\n",
+ loghandle->lgh_ctxt->loc_obd->obd_name, rc);
+ OBD_FREE_PTR(lpi);
+ return rc;
+ }
+ wait_for_completion(&lpi->lpi_completion);
+ } else {
+ lpi->lpi_env = env;
+ llog_process_thread(lpi);
+ }
+ rc = lpi->lpi_rc;
+ OBD_FREE_PTR(lpi);
+ return rc;
+}
+EXPORT_SYMBOL(llog_process_or_fork);
+
+int llog_process(const struct lu_env *env, struct llog_handle *loghandle,
+ llog_cb_t cb, void *data, void *catdata)
+{
+ return llog_process_or_fork(env, loghandle, cb, data, catdata, true);
+}
+EXPORT_SYMBOL(llog_process);
+
+int llog_reverse_process(const struct lu_env *env,
+ struct llog_handle *loghandle, llog_cb_t cb,
+ void *data, void *catdata)
+{
+ struct llog_log_hdr *llh = loghandle->lgh_hdr;
+ struct llog_process_cat_data *cd = catdata;
+ void *buf;
+ int rc = 0, first_index = 1, index, idx;
+
+ OBD_ALLOC(buf, LLOG_CHUNK_SIZE);
+ if (!buf)
+ return -ENOMEM;
+
+ if (cd != NULL)
+ first_index = cd->lpcd_first_idx + 1;
+ if (cd != NULL && cd->lpcd_last_idx)
+ index = cd->lpcd_last_idx;
+ else
+ index = LLOG_BITMAP_BYTES * 8 - 1;
+
+ while (rc == 0) {
+ struct llog_rec_hdr *rec;
+ struct llog_rec_tail *tail;
+
+ /* skip records not set in bitmap */
+ while (index >= first_index &&
+ !ext2_test_bit(index, llh->llh_bitmap))
+ --index;
+
+ LASSERT(index >= first_index - 1);
+ if (index == first_index - 1)
+ break;
+
+ /* get the buf with our target record; avoid old garbage */
+ memset(buf, 0, LLOG_CHUNK_SIZE);
+ rc = llog_prev_block(env, loghandle, index, buf,
+ LLOG_CHUNK_SIZE);
+ if (rc)
+ goto out;
+
+ rec = buf;
+ idx = rec->lrh_index;
+ CDEBUG(D_RPCTRACE, "index %u : idx %u\n", index, idx);
+ while (idx < index) {
+ rec = (void *)rec + rec->lrh_len;
+ if (LLOG_REC_HDR_NEEDS_SWABBING(rec))
+ lustre_swab_llog_rec(rec);
+ idx ++;
+ }
+ LASSERT(idx == index);
+ tail = (void *)rec + rec->lrh_len - sizeof(*tail);
+
+ /* process records in buffer, starting where we found one */
+ while ((void *)tail > buf) {
+ if (tail->lrt_index == 0) {
+ /* no more records */
+ rc = 0;
+ goto out;
+ }
+
+ /* if set, process the callback on this record */
+ if (ext2_test_bit(index, llh->llh_bitmap)) {
+ rec = (void *)tail - tail->lrt_len +
+ sizeof(*tail);
+
+ rc = cb(env, loghandle, rec, data);
+ if (rc == LLOG_PROC_BREAK) {
+ goto out;
+ } else if (rc == LLOG_DEL_RECORD) {
+ llog_cancel_rec(env, loghandle,
+ tail->lrt_index);
+ rc = 0;
+ }
+ if (rc)
+ goto out;
+ }
+
+ /* previous record, still in buffer? */
+ --index;
+ if (index < first_index) {
+ rc = 0;
+ goto out;
+ }
+ tail = (void *)tail - tail->lrt_len;
+ }
+ }
+
+out:
+ if (buf)
+ OBD_FREE(buf, LLOG_CHUNK_SIZE);
+ return rc;
+}
+EXPORT_SYMBOL(llog_reverse_process);
+
+/**
+ * new llog API
+ *
+ * API functions:
+ * llog_open - open llog, may not exist
+ * llog_exist - check if llog exists
+ * llog_close - close opened llog, pair for open, frees llog_handle
+ * llog_declare_create - declare llog creation
+ * llog_create - create new llog on disk, need transaction handle
+ * llog_declare_write_rec - declaration of llog write
+ * llog_write_rec - write llog record on disk, need transaction handle
+ * llog_declare_add - declare llog catalog record addition
+ * llog_add - add llog record in catalog, need transaction handle
+ */
+int llog_exist(struct llog_handle *loghandle)
+{
+ struct llog_operations *lop;
+ int rc;
+
+ rc = llog_handle2ops(loghandle, &lop);
+ if (rc)
+ return rc;
+ if (lop->lop_exist == NULL)
+ return -EOPNOTSUPP;
+
+ rc = lop->lop_exist(loghandle);
+ return rc;
+}
+EXPORT_SYMBOL(llog_exist);
+
+int llog_declare_create(const struct lu_env *env,
+ struct llog_handle *loghandle, struct thandle *th)
+{
+ struct llog_operations *lop;
+ int raised, rc;
+
+ rc = llog_handle2ops(loghandle, &lop);
+ if (rc)
+ return rc;
+ if (lop->lop_declare_create == NULL)
+ return -EOPNOTSUPP;
+
+ raised = cfs_cap_raised(CFS_CAP_SYS_RESOURCE);
+ if (!raised)
+ cfs_cap_raise(CFS_CAP_SYS_RESOURCE);
+ rc = lop->lop_declare_create(env, loghandle, th);
+ if (!raised)
+ cfs_cap_lower(CFS_CAP_SYS_RESOURCE);
+ return rc;
+}
+EXPORT_SYMBOL(llog_declare_create);
+
+int llog_create(const struct lu_env *env, struct llog_handle *handle,
+ struct thandle *th)
+{
+ struct llog_operations *lop;
+ int raised, rc;
+
+ rc = llog_handle2ops(handle, &lop);
+ if (rc)
+ return rc;
+ if (lop->lop_create == NULL)
+ return -EOPNOTSUPP;
+
+ raised = cfs_cap_raised(CFS_CAP_SYS_RESOURCE);
+ if (!raised)
+ cfs_cap_raise(CFS_CAP_SYS_RESOURCE);
+ rc = lop->lop_create(env, handle, th);
+ if (!raised)
+ cfs_cap_lower(CFS_CAP_SYS_RESOURCE);
+ return rc;
+}
+EXPORT_SYMBOL(llog_create);
+
+int llog_declare_write_rec(const struct lu_env *env,
+ struct llog_handle *handle,
+ struct llog_rec_hdr *rec, int idx,
+ struct thandle *th)
+{
+ struct llog_operations *lop;
+ int raised, rc;
+
+ rc = llog_handle2ops(handle, &lop);
+ if (rc)
+ return rc;
+ LASSERT(lop);
+ if (lop->lop_declare_write_rec == NULL)
+ return -EOPNOTSUPP;
+
+ raised = cfs_cap_raised(CFS_CAP_SYS_RESOURCE);
+ if (!raised)
+ cfs_cap_raise(CFS_CAP_SYS_RESOURCE);
+ rc = lop->lop_declare_write_rec(env, handle, rec, idx, th);
+ if (!raised)
+ cfs_cap_lower(CFS_CAP_SYS_RESOURCE);
+ return rc;
+}
+EXPORT_SYMBOL(llog_declare_write_rec);
+
+int llog_write_rec(const struct lu_env *env, struct llog_handle *handle,
+ struct llog_rec_hdr *rec, struct llog_cookie *logcookies,
+ int numcookies, void *buf, int idx, struct thandle *th)
+{
+ struct llog_operations *lop;
+ int raised, rc, buflen;
+
+ rc = llog_handle2ops(handle, &lop);
+ if (rc)
+ return rc;
+
+ LASSERT(lop);
+ if (lop->lop_write_rec == NULL)
+ return -EOPNOTSUPP;
+
+ if (buf)
+ buflen = rec->lrh_len + sizeof(struct llog_rec_hdr) +
+ sizeof(struct llog_rec_tail);
+ else
+ buflen = rec->lrh_len;
+ LASSERT(cfs_size_round(buflen) == buflen);
+
+ raised = cfs_cap_raised(CFS_CAP_SYS_RESOURCE);
+ if (!raised)
+ cfs_cap_raise(CFS_CAP_SYS_RESOURCE);
+ rc = lop->lop_write_rec(env, handle, rec, logcookies, numcookies,
+ buf, idx, th);
+ if (!raised)
+ cfs_cap_lower(CFS_CAP_SYS_RESOURCE);
+ return rc;
+}
+EXPORT_SYMBOL(llog_write_rec);
+
+int llog_add(const struct lu_env *env, struct llog_handle *lgh,
+ struct llog_rec_hdr *rec, struct llog_cookie *logcookies,
+ void *buf, struct thandle *th)
+{
+ int raised, rc;
+
+ if (lgh->lgh_logops->lop_add == NULL)
+ return -EOPNOTSUPP;
+
+ raised = cfs_cap_raised(CFS_CAP_SYS_RESOURCE);
+ if (!raised)
+ cfs_cap_raise(CFS_CAP_SYS_RESOURCE);
+ rc = lgh->lgh_logops->lop_add(env, lgh, rec, logcookies, buf, th);
+ if (!raised)
+ cfs_cap_lower(CFS_CAP_SYS_RESOURCE);
+ return rc;
+}
+EXPORT_SYMBOL(llog_add);
+
+int llog_declare_add(const struct lu_env *env, struct llog_handle *lgh,
+ struct llog_rec_hdr *rec, struct thandle *th)
+{
+ int raised, rc;
+
+ if (lgh->lgh_logops->lop_declare_add == NULL)
+ return -EOPNOTSUPP;
+
+ raised = cfs_cap_raised(CFS_CAP_SYS_RESOURCE);
+ if (!raised)
+ cfs_cap_raise(CFS_CAP_SYS_RESOURCE);
+ rc = lgh->lgh_logops->lop_declare_add(env, lgh, rec, th);
+ if (!raised)
+ cfs_cap_lower(CFS_CAP_SYS_RESOURCE);
+ return rc;
+}
+EXPORT_SYMBOL(llog_declare_add);
+
+/**
+ * Helper function to open llog or create it if doesn't exist.
+ * It hides all transaction handling from caller.
+ */
+int llog_open_create(const struct lu_env *env, struct llog_ctxt *ctxt,
+ struct llog_handle **res, struct llog_logid *logid,
+ char *name)
+{
+ struct dt_device *d;
+ struct thandle *th;
+ int rc;
+
+ rc = llog_open(env, ctxt, res, logid, name, LLOG_OPEN_NEW);
+ if (rc)
+ return rc;
+
+ if (llog_exist(*res))
+ return 0;
+
+ LASSERT((*res)->lgh_obj != NULL);
+
+ d = lu2dt_dev((*res)->lgh_obj->do_lu.lo_dev);
+
+ th = dt_trans_create(env, d);
+ if (IS_ERR(th)) {
+ rc = PTR_ERR(th);
+ goto out;
+ }
+
+ rc = llog_declare_create(env, *res, th);
+ if (rc == 0) {
+ rc = dt_trans_start_local(env, d, th);
+ if (rc == 0)
+ rc = llog_create(env, *res, th);
+ }
+ dt_trans_stop(env, d, th);
+out:
+ if (rc)
+ llog_close(env, *res);
+ return rc;
+}
+EXPORT_SYMBOL(llog_open_create);
+
+/**
+ * Helper function to delete existent llog.
+ */
+int llog_erase(const struct lu_env *env, struct llog_ctxt *ctxt,
+ struct llog_logid *logid, char *name)
+{
+ struct llog_handle *handle;
+ int rc = 0, rc2;
+
+ /* nothing to erase */
+ if (name == NULL && logid == NULL)
+ return 0;
+
+ rc = llog_open(env, ctxt, &handle, logid, name, LLOG_OPEN_EXISTS);
+ if (rc < 0)
+ return rc;
+
+ rc = llog_init_handle(env, handle, LLOG_F_IS_PLAIN, NULL);
+ if (rc == 0)
+ rc = llog_destroy(env, handle);
+
+ rc2 = llog_close(env, handle);
+ if (rc == 0)
+ rc = rc2;
+ return rc;
+}
+EXPORT_SYMBOL(llog_erase);
+
+/*
+ * Helper function for write record in llog.
+ * It hides all transaction handling from caller.
+ * Valid only with local llog.
+ */
+int llog_write(const struct lu_env *env, struct llog_handle *loghandle,
+ struct llog_rec_hdr *rec, struct llog_cookie *reccookie,
+ int cookiecount, void *buf, int idx)
+{
+ struct dt_device *dt;
+ struct thandle *th;
+ int rc;
+
+ LASSERT(loghandle);
+ LASSERT(loghandle->lgh_ctxt);
+ LASSERT(loghandle->lgh_obj != NULL);
+
+ dt = lu2dt_dev(loghandle->lgh_obj->do_lu.lo_dev);
+
+ th = dt_trans_create(env, dt);
+ if (IS_ERR(th))
+ return PTR_ERR(th);
+
+ rc = llog_declare_write_rec(env, loghandle, rec, idx, th);
+ if (rc)
+ goto out_trans;
+
+ rc = dt_trans_start_local(env, dt, th);
+ if (rc)
+ goto out_trans;
+
+ down_write(&loghandle->lgh_lock);
+ rc = llog_write_rec(env, loghandle, rec, reccookie,
+ cookiecount, buf, idx, th);
+ up_write(&loghandle->lgh_lock);
+out_trans:
+ dt_trans_stop(env, dt, th);
+ return rc;
+}
+EXPORT_SYMBOL(llog_write);
+
+int llog_open(const struct lu_env *env, struct llog_ctxt *ctxt,
+ struct llog_handle **lgh, struct llog_logid *logid,
+ char *name, enum llog_open_param open_param)
+{
+ int raised;
+ int rc;
+
+ LASSERT(ctxt);
+ LASSERT(ctxt->loc_logops);
+
+ if (ctxt->loc_logops->lop_open == NULL) {
+ *lgh = NULL;
+ return -EOPNOTSUPP;
+ }
+
+ *lgh = llog_alloc_handle();
+ if (*lgh == NULL)
+ return -ENOMEM;
+ (*lgh)->lgh_ctxt = ctxt;
+ (*lgh)->lgh_logops = ctxt->loc_logops;
+
+ raised = cfs_cap_raised(CFS_CAP_SYS_RESOURCE);
+ if (!raised)
+ cfs_cap_raise(CFS_CAP_SYS_RESOURCE);
+ rc = ctxt->loc_logops->lop_open(env, *lgh, logid, name, open_param);
+ if (!raised)
+ cfs_cap_lower(CFS_CAP_SYS_RESOURCE);
+ if (rc) {
+ llog_free_handle(*lgh);
+ *lgh = NULL;
+ }
+ return rc;
+}
+EXPORT_SYMBOL(llog_open);
+
+int llog_close(const struct lu_env *env, struct llog_handle *loghandle)
+{
+ struct llog_operations *lop;
+ int rc;
+
+ rc = llog_handle2ops(loghandle, &lop);
+ if (rc)
+ goto out;
+ if (lop->lop_close == NULL) {
+ rc = -EOPNOTSUPP;
+ goto out;
+ }
+ rc = lop->lop_close(env, loghandle);
+out:
+ llog_handle_put(loghandle);
+ return rc;
+}
+EXPORT_SYMBOL(llog_close);
+
+int llog_is_empty(const struct lu_env *env, struct llog_ctxt *ctxt,
+ char *name)
+{
+ struct llog_handle *llh;
+ int rc = 0;
+
+ rc = llog_open(env, ctxt, &llh, NULL, name, LLOG_OPEN_EXISTS);
+ if (rc < 0) {
+ if (likely(rc == -ENOENT))
+ rc = 0;
+ goto out;
+ }
+
+ rc = llog_init_handle(env, llh, LLOG_F_IS_PLAIN, NULL);
+ if (rc)
+ goto out_close;
+ rc = llog_get_size(llh);
+
+out_close:
+ llog_close(env, llh);
+out:
+ /* header is record 1 */
+ return rc <= 1;
+}
+EXPORT_SYMBOL(llog_is_empty);
+
+int llog_copy_handler(const struct lu_env *env, struct llog_handle *llh,
+ struct llog_rec_hdr *rec, void *data)
+{
+ struct llog_handle *copy_llh = data;
+
+ /* Append all records */
+ return llog_write(env, copy_llh, rec, NULL, 0, NULL, -1);
+}
+EXPORT_SYMBOL(llog_copy_handler);
+
+/* backup plain llog */
+int llog_backup(const struct lu_env *env, struct obd_device *obd,
+ struct llog_ctxt *ctxt, struct llog_ctxt *bctxt,
+ char *name, char *backup)
+{
+ struct llog_handle *llh, *bllh;
+ int rc;
+
+
+
+ /* open original log */
+ rc = llog_open(env, ctxt, &llh, NULL, name, LLOG_OPEN_EXISTS);
+ if (rc < 0) {
+ /* the -ENOENT case is also reported to the caller
+ * but silently so it should handle that if needed.
+ */
+ if (rc != -ENOENT)
+ CERROR("%s: failed to open log %s: rc = %d\n",
+ obd->obd_name, name, rc);
+ return rc;
+ }
+
+ rc = llog_init_handle(env, llh, LLOG_F_IS_PLAIN, NULL);
+ if (rc)
+ goto out_close;
+
+ /* Make sure there's no old backup log */
+ rc = llog_erase(env, bctxt, NULL, backup);
+ if (rc < 0 && rc != -ENOENT)
+ goto out_close;
+
+ /* open backup log */
+ rc = llog_open_create(env, bctxt, &bllh, NULL, backup);
+ if (rc) {
+ CERROR("%s: failed to open backup logfile %s: rc = %d\n",
+ obd->obd_name, backup, rc);
+ goto out_close;
+ }
+
+ /* check that backup llog is not the same object as original one */
+ if (llh->lgh_obj == bllh->lgh_obj) {
+ CERROR("%s: backup llog %s to itself (%s), objects %p/%p\n",
+ obd->obd_name, name, backup, llh->lgh_obj,
+ bllh->lgh_obj);
+ rc = -EEXIST;
+ goto out_backup;
+ }
+
+ rc = llog_init_handle(env, bllh, LLOG_F_IS_PLAIN, NULL);
+ if (rc)
+ goto out_backup;
+
+ /* Copy log record by record */
+ rc = llog_process_or_fork(env, llh, llog_copy_handler, (void *)bllh,
+ NULL, false);
+ if (rc)
+ CERROR("%s: failed to backup log %s: rc = %d\n",
+ obd->obd_name, name, rc);
+out_backup:
+ llog_close(env, bllh);
+out_close:
+ llog_close(env, llh);
+ return rc;
+}
+EXPORT_SYMBOL(llog_backup);
diff --git a/drivers/staging/lustre/lustre/obdclass/llog_cat.c b/drivers/staging/lustre/lustre/obdclass/llog_cat.c
new file mode 100644
index 000000000..c8f6ab006
--- /dev/null
+++ b/drivers/staging/lustre/lustre/obdclass/llog_cat.c
@@ -0,0 +1,815 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2012, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ *
+ * lustre/obdclass/llog_cat.c
+ *
+ * OST<->MDS recovery logging infrastructure.
+ *
+ * Invariants in implementation:
+ * - we do not share logs among different OST<->MDS connections, so that
+ * if an OST or MDS fails it need only look at log(s) relevant to itself
+ *
+ * Author: Andreas Dilger <adilger@clusterfs.com>
+ * Author: Alexey Zhuravlev <alexey.zhuravlev@intel.com>
+ * Author: Mikhail Pershin <mike.pershin@intel.com>
+ */
+
+#define DEBUG_SUBSYSTEM S_LOG
+
+
+#include "../include/obd_class.h"
+
+#include "llog_internal.h"
+
+/* Create a new log handle and add it to the open list.
+ * This log handle will be closed when all of the records in it are removed.
+ *
+ * Assumes caller has already pushed us into the kernel context and is locking.
+ */
+static int llog_cat_new_log(const struct lu_env *env,
+ struct llog_handle *cathandle,
+ struct llog_handle *loghandle,
+ struct thandle *th)
+{
+
+ struct llog_log_hdr *llh;
+ struct llog_logid_rec rec = { { 0 }, };
+ int rc, index, bitmap_size;
+
+ llh = cathandle->lgh_hdr;
+ bitmap_size = LLOG_BITMAP_SIZE(llh);
+
+ index = (cathandle->lgh_last_idx + 1) % bitmap_size;
+
+ /* maximum number of available slots in catlog is bitmap_size - 2 */
+ if (llh->llh_cat_idx == index) {
+ CERROR("no free catalog slots for log...\n");
+ return -ENOSPC;
+ }
+
+ if (OBD_FAIL_CHECK(OBD_FAIL_MDS_LLOG_CREATE_FAILED))
+ return -ENOSPC;
+
+ rc = llog_create(env, loghandle, th);
+ /* if llog is already created, no need to initialize it */
+ if (rc == -EEXIST) {
+ return 0;
+ } else if (rc != 0) {
+ CERROR("%s: can't create new plain llog in catalog: rc = %d\n",
+ loghandle->lgh_ctxt->loc_obd->obd_name, rc);
+ return rc;
+ }
+
+ rc = llog_init_handle(env, loghandle,
+ LLOG_F_IS_PLAIN | LLOG_F_ZAP_WHEN_EMPTY,
+ &cathandle->lgh_hdr->llh_tgtuuid);
+ if (rc)
+ goto out_destroy;
+
+ if (index == 0)
+ index = 1;
+
+ spin_lock(&loghandle->lgh_hdr_lock);
+ llh->llh_count++;
+ if (ext2_set_bit(index, llh->llh_bitmap)) {
+ CERROR("argh, index %u already set in log bitmap?\n",
+ index);
+ spin_unlock(&loghandle->lgh_hdr_lock);
+ LBUG(); /* should never happen */
+ }
+ spin_unlock(&loghandle->lgh_hdr_lock);
+
+ cathandle->lgh_last_idx = index;
+ llh->llh_tail.lrt_index = index;
+
+ CDEBUG(D_RPCTRACE,
+ "new recovery log "DOSTID":%x for index %u of catalog"
+ DOSTID"\n", POSTID(&loghandle->lgh_id.lgl_oi),
+ loghandle->lgh_id.lgl_ogen, index,
+ POSTID(&cathandle->lgh_id.lgl_oi));
+ /* build the record for this log in the catalog */
+ rec.lid_hdr.lrh_len = sizeof(rec);
+ rec.lid_hdr.lrh_index = index;
+ rec.lid_hdr.lrh_type = LLOG_LOGID_MAGIC;
+ rec.lid_id = loghandle->lgh_id;
+ rec.lid_tail.lrt_len = sizeof(rec);
+ rec.lid_tail.lrt_index = index;
+
+ /* update the catalog: header and record */
+ rc = llog_write_rec(env, cathandle, &rec.lid_hdr,
+ &loghandle->u.phd.phd_cookie, 1, NULL, index, th);
+ if (rc < 0)
+ goto out_destroy;
+
+ loghandle->lgh_hdr->llh_cat_idx = index;
+ return 0;
+out_destroy:
+ llog_destroy(env, loghandle);
+ return rc;
+}
+
+/* Open an existent log handle and add it to the open list.
+ * This log handle will be closed when all of the records in it are removed.
+ *
+ * Assumes caller has already pushed us into the kernel context and is locking.
+ * We return a lock on the handle to ensure nobody yanks it from us.
+ *
+ * This takes extra reference on llog_handle via llog_handle_get() and require
+ * this reference to be put by caller using llog_handle_put()
+ */
+int llog_cat_id2handle(const struct lu_env *env, struct llog_handle *cathandle,
+ struct llog_handle **res, struct llog_logid *logid)
+{
+ struct llog_handle *loghandle;
+ int rc = 0;
+
+ if (cathandle == NULL)
+ return -EBADF;
+
+ down_write(&cathandle->lgh_lock);
+ list_for_each_entry(loghandle, &cathandle->u.chd.chd_head,
+ u.phd.phd_entry) {
+ struct llog_logid *cgl = &loghandle->lgh_id;
+
+ if (ostid_id(&cgl->lgl_oi) == ostid_id(&logid->lgl_oi) &&
+ ostid_seq(&cgl->lgl_oi) == ostid_seq(&logid->lgl_oi)) {
+ if (cgl->lgl_ogen != logid->lgl_ogen) {
+ CERROR("%s: log "DOSTID" generation %x != %x\n",
+ loghandle->lgh_ctxt->loc_obd->obd_name,
+ POSTID(&logid->lgl_oi), cgl->lgl_ogen,
+ logid->lgl_ogen);
+ continue;
+ }
+ loghandle->u.phd.phd_cat_handle = cathandle;
+ up_write(&cathandle->lgh_lock);
+ rc = 0;
+ goto out;
+ }
+ }
+ up_write(&cathandle->lgh_lock);
+
+ rc = llog_open(env, cathandle->lgh_ctxt, &loghandle, logid, NULL,
+ LLOG_OPEN_EXISTS);
+ if (rc < 0) {
+ CERROR("%s: error opening log id "DOSTID":%x: rc = %d\n",
+ cathandle->lgh_ctxt->loc_obd->obd_name,
+ POSTID(&logid->lgl_oi), logid->lgl_ogen, rc);
+ return rc;
+ }
+
+ rc = llog_init_handle(env, loghandle, LLOG_F_IS_PLAIN, NULL);
+ if (rc < 0) {
+ llog_close(env, loghandle);
+ loghandle = NULL;
+ return rc;
+ }
+
+ down_write(&cathandle->lgh_lock);
+ list_add(&loghandle->u.phd.phd_entry, &cathandle->u.chd.chd_head);
+ up_write(&cathandle->lgh_lock);
+
+ loghandle->u.phd.phd_cat_handle = cathandle;
+ loghandle->u.phd.phd_cookie.lgc_lgl = cathandle->lgh_id;
+ loghandle->u.phd.phd_cookie.lgc_index =
+ loghandle->lgh_hdr->llh_cat_idx;
+out:
+ llog_handle_get(loghandle);
+ *res = loghandle;
+ return 0;
+}
+
+int llog_cat_close(const struct lu_env *env, struct llog_handle *cathandle)
+{
+ struct llog_handle *loghandle, *n;
+ int rc;
+
+ list_for_each_entry_safe(loghandle, n, &cathandle->u.chd.chd_head,
+ u.phd.phd_entry) {
+ struct llog_log_hdr *llh = loghandle->lgh_hdr;
+ int index;
+
+ /* unlink open-not-created llogs */
+ list_del_init(&loghandle->u.phd.phd_entry);
+ llh = loghandle->lgh_hdr;
+ if (loghandle->lgh_obj != NULL && llh != NULL &&
+ (llh->llh_flags & LLOG_F_ZAP_WHEN_EMPTY) &&
+ (llh->llh_count == 1)) {
+ rc = llog_destroy(env, loghandle);
+ if (rc)
+ CERROR("%s: failure destroying log during cleanup: rc = %d\n",
+ loghandle->lgh_ctxt->loc_obd->obd_name,
+ rc);
+
+ index = loghandle->u.phd.phd_cookie.lgc_index;
+ llog_cat_cleanup(env, cathandle, NULL, index);
+ }
+ llog_close(env, loghandle);
+ }
+ /* if handle was stored in ctxt, remove it too */
+ if (cathandle->lgh_ctxt->loc_handle == cathandle)
+ cathandle->lgh_ctxt->loc_handle = NULL;
+ rc = llog_close(env, cathandle);
+ return rc;
+}
+EXPORT_SYMBOL(llog_cat_close);
+
+/**
+ * lockdep markers for nested struct llog_handle::lgh_lock locking.
+ */
+enum {
+ LLOGH_CAT,
+ LLOGH_LOG
+};
+
+/** Return the currently active log handle. If the current log handle doesn't
+ * have enough space left for the current record, start a new one.
+ *
+ * If reclen is 0, we only want to know what the currently active log is,
+ * otherwise we get a lock on this log so nobody can steal our space.
+ *
+ * Assumes caller has already pushed us into the kernel context and is locking.
+ *
+ * NOTE: loghandle is write-locked upon successful return
+ */
+static struct llog_handle *llog_cat_current_log(struct llog_handle *cathandle,
+ struct thandle *th)
+{
+ struct llog_handle *loghandle = NULL;
+
+ down_read_nested(&cathandle->lgh_lock, LLOGH_CAT);
+ loghandle = cathandle->u.chd.chd_current_log;
+ if (loghandle) {
+ struct llog_log_hdr *llh;
+
+ down_write_nested(&loghandle->lgh_lock, LLOGH_LOG);
+ llh = loghandle->lgh_hdr;
+ if (llh == NULL ||
+ loghandle->lgh_last_idx < LLOG_BITMAP_SIZE(llh) - 1) {
+ up_read(&cathandle->lgh_lock);
+ return loghandle;
+ } else {
+ up_write(&loghandle->lgh_lock);
+ }
+ }
+ up_read(&cathandle->lgh_lock);
+
+ /* time to use next log */
+
+ /* first, we have to make sure the state hasn't changed */
+ down_write_nested(&cathandle->lgh_lock, LLOGH_CAT);
+ loghandle = cathandle->u.chd.chd_current_log;
+ if (loghandle) {
+ struct llog_log_hdr *llh;
+
+ down_write_nested(&loghandle->lgh_lock, LLOGH_LOG);
+ llh = loghandle->lgh_hdr;
+ LASSERT(llh);
+ if (loghandle->lgh_last_idx < LLOG_BITMAP_SIZE(llh) - 1) {
+ up_write(&cathandle->lgh_lock);
+ return loghandle;
+ } else {
+ up_write(&loghandle->lgh_lock);
+ }
+ }
+
+ CDEBUG(D_INODE, "use next log\n");
+
+ loghandle = cathandle->u.chd.chd_next_log;
+ cathandle->u.chd.chd_current_log = loghandle;
+ cathandle->u.chd.chd_next_log = NULL;
+ down_write_nested(&loghandle->lgh_lock, LLOGH_LOG);
+ up_write(&cathandle->lgh_lock);
+ LASSERT(loghandle);
+ return loghandle;
+}
+
+/* Add a single record to the recovery log(s) using a catalog
+ * Returns as llog_write_record
+ *
+ * Assumes caller has already pushed us into the kernel context.
+ */
+int llog_cat_add_rec(const struct lu_env *env, struct llog_handle *cathandle,
+ struct llog_rec_hdr *rec, struct llog_cookie *reccookie,
+ void *buf, struct thandle *th)
+{
+ struct llog_handle *loghandle;
+ int rc;
+
+ LASSERT(rec->lrh_len <= LLOG_CHUNK_SIZE);
+ loghandle = llog_cat_current_log(cathandle, th);
+ LASSERT(!IS_ERR(loghandle));
+
+ /* loghandle is already locked by llog_cat_current_log() for us */
+ if (!llog_exist(loghandle)) {
+ rc = llog_cat_new_log(env, cathandle, loghandle, th);
+ if (rc < 0) {
+ up_write(&loghandle->lgh_lock);
+ return rc;
+ }
+ }
+ /* now let's try to add the record */
+ rc = llog_write_rec(env, loghandle, rec, reccookie, 1, buf, -1, th);
+ if (rc < 0)
+ CDEBUG_LIMIT(rc == -ENOSPC ? D_HA : D_ERROR,
+ "llog_write_rec %d: lh=%p\n", rc, loghandle);
+ up_write(&loghandle->lgh_lock);
+ if (rc == -ENOSPC) {
+ /* try to use next log */
+ loghandle = llog_cat_current_log(cathandle, th);
+ LASSERT(!IS_ERR(loghandle));
+ /* new llog can be created concurrently */
+ if (!llog_exist(loghandle)) {
+ rc = llog_cat_new_log(env, cathandle, loghandle, th);
+ if (rc < 0) {
+ up_write(&loghandle->lgh_lock);
+ return rc;
+ }
+ }
+ /* now let's try to add the record */
+ rc = llog_write_rec(env, loghandle, rec, reccookie, 1, buf,
+ -1, th);
+ if (rc < 0)
+ CERROR("llog_write_rec %d: lh=%p\n", rc, loghandle);
+ up_write(&loghandle->lgh_lock);
+ }
+
+ return rc;
+}
+EXPORT_SYMBOL(llog_cat_add_rec);
+
+int llog_cat_declare_add_rec(const struct lu_env *env,
+ struct llog_handle *cathandle,
+ struct llog_rec_hdr *rec, struct thandle *th)
+{
+ struct llog_handle *loghandle, *next;
+ int rc = 0;
+
+ if (cathandle->u.chd.chd_current_log == NULL) {
+ /* declare new plain llog */
+ down_write(&cathandle->lgh_lock);
+ if (cathandle->u.chd.chd_current_log == NULL) {
+ rc = llog_open(env, cathandle->lgh_ctxt, &loghandle,
+ NULL, NULL, LLOG_OPEN_NEW);
+ if (rc == 0) {
+ cathandle->u.chd.chd_current_log = loghandle;
+ list_add_tail(&loghandle->u.phd.phd_entry,
+ &cathandle->u.chd.chd_head);
+ }
+ }
+ up_write(&cathandle->lgh_lock);
+ } else if (cathandle->u.chd.chd_next_log == NULL) {
+ /* declare next plain llog */
+ down_write(&cathandle->lgh_lock);
+ if (cathandle->u.chd.chd_next_log == NULL) {
+ rc = llog_open(env, cathandle->lgh_ctxt, &loghandle,
+ NULL, NULL, LLOG_OPEN_NEW);
+ if (rc == 0) {
+ cathandle->u.chd.chd_next_log = loghandle;
+ list_add_tail(&loghandle->u.phd.phd_entry,
+ &cathandle->u.chd.chd_head);
+ }
+ }
+ up_write(&cathandle->lgh_lock);
+ }
+ if (rc)
+ goto out;
+
+ if (!llog_exist(cathandle->u.chd.chd_current_log)) {
+ rc = llog_declare_create(env, cathandle->u.chd.chd_current_log,
+ th);
+ if (rc)
+ goto out;
+ llog_declare_write_rec(env, cathandle, NULL, -1, th);
+ }
+ /* declare records in the llogs */
+ rc = llog_declare_write_rec(env, cathandle->u.chd.chd_current_log,
+ rec, -1, th);
+ if (rc)
+ goto out;
+
+ next = cathandle->u.chd.chd_next_log;
+ if (next) {
+ if (!llog_exist(next)) {
+ rc = llog_declare_create(env, next, th);
+ llog_declare_write_rec(env, cathandle, NULL, -1, th);
+ }
+ llog_declare_write_rec(env, next, rec, -1, th);
+ }
+out:
+ return rc;
+}
+EXPORT_SYMBOL(llog_cat_declare_add_rec);
+
+int llog_cat_add(const struct lu_env *env, struct llog_handle *cathandle,
+ struct llog_rec_hdr *rec, struct llog_cookie *reccookie,
+ void *buf)
+{
+ struct llog_ctxt *ctxt;
+ struct dt_device *dt;
+ struct thandle *th = NULL;
+ int rc;
+
+ ctxt = cathandle->lgh_ctxt;
+ LASSERT(ctxt);
+ LASSERT(ctxt->loc_exp);
+
+ if (cathandle->lgh_obj != NULL) {
+ dt = ctxt->loc_exp->exp_obd->obd_lvfs_ctxt.dt;
+ LASSERT(dt);
+
+ th = dt_trans_create(env, dt);
+ if (IS_ERR(th))
+ return PTR_ERR(th);
+
+ rc = llog_cat_declare_add_rec(env, cathandle, rec, th);
+ if (rc)
+ goto out_trans;
+
+ rc = dt_trans_start_local(env, dt, th);
+ if (rc)
+ goto out_trans;
+ rc = llog_cat_add_rec(env, cathandle, rec, reccookie, buf, th);
+out_trans:
+ dt_trans_stop(env, dt, th);
+ } else { /* lvfs compat code */
+ LASSERT(cathandle->lgh_file != NULL);
+ rc = llog_cat_declare_add_rec(env, cathandle, rec, th);
+ if (rc == 0)
+ rc = llog_cat_add_rec(env, cathandle, rec, reccookie,
+ buf, th);
+ }
+ return rc;
+}
+EXPORT_SYMBOL(llog_cat_add);
+
+/* For each cookie in the cookie array, we clear the log in-use bit and either:
+ * - the log is empty, so mark it free in the catalog header and delete it
+ * - the log is not empty, just write out the log header
+ *
+ * The cookies may be in different log files, so we need to get new logs
+ * each time.
+ *
+ * Assumes caller has already pushed us into the kernel context.
+ */
+int llog_cat_cancel_records(const struct lu_env *env,
+ struct llog_handle *cathandle, int count,
+ struct llog_cookie *cookies)
+{
+ int i, index, rc = 0, failed = 0;
+
+ for (i = 0; i < count; i++, cookies++) {
+ struct llog_handle *loghandle;
+ struct llog_logid *lgl = &cookies->lgc_lgl;
+ int lrc;
+
+ rc = llog_cat_id2handle(env, cathandle, &loghandle, lgl);
+ if (rc) {
+ CERROR("%s: cannot find handle for llog "DOSTID": %d\n",
+ cathandle->lgh_ctxt->loc_obd->obd_name,
+ POSTID(&lgl->lgl_oi), rc);
+ failed++;
+ continue;
+ }
+
+ lrc = llog_cancel_rec(env, loghandle, cookies->lgc_index);
+ if (lrc == 1) { /* log has been destroyed */
+ index = loghandle->u.phd.phd_cookie.lgc_index;
+ rc = llog_cat_cleanup(env, cathandle, loghandle,
+ index);
+ } else if (lrc == -ENOENT) {
+ if (rc == 0) /* ENOENT shouldn't rewrite any error */
+ rc = lrc;
+ } else if (lrc < 0) {
+ failed++;
+ rc = lrc;
+ }
+ llog_handle_put(loghandle);
+ }
+ if (rc)
+ CERROR("%s: fail to cancel %d of %d llog-records: rc = %d\n",
+ cathandle->lgh_ctxt->loc_obd->obd_name, failed, count,
+ rc);
+
+ return rc;
+}
+EXPORT_SYMBOL(llog_cat_cancel_records);
+
+static int llog_cat_process_cb(const struct lu_env *env,
+ struct llog_handle *cat_llh,
+ struct llog_rec_hdr *rec, void *data)
+{
+ struct llog_process_data *d = data;
+ struct llog_logid_rec *lir = (struct llog_logid_rec *)rec;
+ struct llog_handle *llh;
+ int rc;
+
+ if (rec->lrh_type != LLOG_LOGID_MAGIC) {
+ CERROR("invalid record in catalog\n");
+ return -EINVAL;
+ }
+ CDEBUG(D_HA, "processing log "DOSTID":%x at index %u of catalog "
+ DOSTID"\n", POSTID(&lir->lid_id.lgl_oi), lir->lid_id.lgl_ogen,
+ rec->lrh_index, POSTID(&cat_llh->lgh_id.lgl_oi));
+
+ rc = llog_cat_id2handle(env, cat_llh, &llh, &lir->lid_id);
+ if (rc) {
+ CERROR("%s: cannot find handle for llog "DOSTID": %d\n",
+ cat_llh->lgh_ctxt->loc_obd->obd_name,
+ POSTID(&lir->lid_id.lgl_oi), rc);
+ return rc;
+ }
+
+ if (rec->lrh_index < d->lpd_startcat)
+ /* Skip processing of the logs until startcat */
+ rc = 0;
+ else if (d->lpd_startidx > 0) {
+ struct llog_process_cat_data cd;
+
+ cd.lpcd_first_idx = d->lpd_startidx;
+ cd.lpcd_last_idx = 0;
+ rc = llog_process_or_fork(env, llh, d->lpd_cb, d->lpd_data,
+ &cd, false);
+ /* Continue processing the next log from idx 0 */
+ d->lpd_startidx = 0;
+ } else {
+ rc = llog_process_or_fork(env, llh, d->lpd_cb, d->lpd_data,
+ NULL, false);
+ }
+
+ llog_handle_put(llh);
+
+ return rc;
+}
+
+int llog_cat_process_or_fork(const struct lu_env *env,
+ struct llog_handle *cat_llh,
+ llog_cb_t cb, void *data, int startcat,
+ int startidx, bool fork)
+{
+ struct llog_process_data d;
+ struct llog_log_hdr *llh = cat_llh->lgh_hdr;
+ int rc;
+
+ LASSERT(llh->llh_flags & LLOG_F_IS_CAT);
+ d.lpd_data = data;
+ d.lpd_cb = cb;
+ d.lpd_startcat = startcat;
+ d.lpd_startidx = startidx;
+
+ if (llh->llh_cat_idx > cat_llh->lgh_last_idx) {
+ struct llog_process_cat_data cd;
+
+ CWARN("catlog "DOSTID" crosses index zero\n",
+ POSTID(&cat_llh->lgh_id.lgl_oi));
+
+ cd.lpcd_first_idx = llh->llh_cat_idx;
+ cd.lpcd_last_idx = 0;
+ rc = llog_process_or_fork(env, cat_llh, llog_cat_process_cb,
+ &d, &cd, fork);
+ if (rc != 0)
+ return rc;
+
+ cd.lpcd_first_idx = 0;
+ cd.lpcd_last_idx = cat_llh->lgh_last_idx;
+ rc = llog_process_or_fork(env, cat_llh, llog_cat_process_cb,
+ &d, &cd, fork);
+ } else {
+ rc = llog_process_or_fork(env, cat_llh, llog_cat_process_cb,
+ &d, NULL, fork);
+ }
+
+ return rc;
+}
+EXPORT_SYMBOL(llog_cat_process_or_fork);
+
+int llog_cat_process(const struct lu_env *env, struct llog_handle *cat_llh,
+ llog_cb_t cb, void *data, int startcat, int startidx)
+{
+ return llog_cat_process_or_fork(env, cat_llh, cb, data, startcat,
+ startidx, false);
+}
+EXPORT_SYMBOL(llog_cat_process);
+
+static int llog_cat_reverse_process_cb(const struct lu_env *env,
+ struct llog_handle *cat_llh,
+ struct llog_rec_hdr *rec, void *data)
+{
+ struct llog_process_data *d = data;
+ struct llog_logid_rec *lir = (struct llog_logid_rec *)rec;
+ struct llog_handle *llh;
+ int rc;
+
+ if (le32_to_cpu(rec->lrh_type) != LLOG_LOGID_MAGIC) {
+ CERROR("invalid record in catalog\n");
+ return -EINVAL;
+ }
+ CDEBUG(D_HA, "processing log "DOSTID":%x at index %u of catalog "
+ DOSTID"\n", POSTID(&lir->lid_id.lgl_oi), lir->lid_id.lgl_ogen,
+ le32_to_cpu(rec->lrh_index), POSTID(&cat_llh->lgh_id.lgl_oi));
+
+ rc = llog_cat_id2handle(env, cat_llh, &llh, &lir->lid_id);
+ if (rc) {
+ CERROR("%s: cannot find handle for llog "DOSTID": %d\n",
+ cat_llh->lgh_ctxt->loc_obd->obd_name,
+ POSTID(&lir->lid_id.lgl_oi), rc);
+ return rc;
+ }
+
+ rc = llog_reverse_process(env, llh, d->lpd_cb, d->lpd_data, NULL);
+ llog_handle_put(llh);
+ return rc;
+}
+
+int llog_cat_reverse_process(const struct lu_env *env,
+ struct llog_handle *cat_llh,
+ llog_cb_t cb, void *data)
+{
+ struct llog_process_data d;
+ struct llog_process_cat_data cd;
+ struct llog_log_hdr *llh = cat_llh->lgh_hdr;
+ int rc;
+
+ LASSERT(llh->llh_flags & LLOG_F_IS_CAT);
+ d.lpd_data = data;
+ d.lpd_cb = cb;
+
+ if (llh->llh_cat_idx > cat_llh->lgh_last_idx) {
+ CWARN("catalog "DOSTID" crosses index zero\n",
+ POSTID(&cat_llh->lgh_id.lgl_oi));
+
+ cd.lpcd_first_idx = 0;
+ cd.lpcd_last_idx = cat_llh->lgh_last_idx;
+ rc = llog_reverse_process(env, cat_llh,
+ llog_cat_reverse_process_cb,
+ &d, &cd);
+ if (rc != 0)
+ return rc;
+
+ cd.lpcd_first_idx = le32_to_cpu(llh->llh_cat_idx);
+ cd.lpcd_last_idx = 0;
+ rc = llog_reverse_process(env, cat_llh,
+ llog_cat_reverse_process_cb,
+ &d, &cd);
+ } else {
+ rc = llog_reverse_process(env, cat_llh,
+ llog_cat_reverse_process_cb,
+ &d, NULL);
+ }
+
+ return rc;
+}
+EXPORT_SYMBOL(llog_cat_reverse_process);
+
+static int llog_cat_set_first_idx(struct llog_handle *cathandle, int index)
+{
+ struct llog_log_hdr *llh = cathandle->lgh_hdr;
+ int i, bitmap_size, idx;
+
+ bitmap_size = LLOG_BITMAP_SIZE(llh);
+ if (llh->llh_cat_idx == (index - 1)) {
+ idx = llh->llh_cat_idx + 1;
+ llh->llh_cat_idx = idx;
+ if (idx == cathandle->lgh_last_idx)
+ goto out;
+ for (i = (index + 1) % bitmap_size;
+ i != cathandle->lgh_last_idx;
+ i = (i + 1) % bitmap_size) {
+ if (!ext2_test_bit(i, llh->llh_bitmap)) {
+ idx = llh->llh_cat_idx + 1;
+ llh->llh_cat_idx = idx;
+ } else if (i == 0) {
+ llh->llh_cat_idx = 0;
+ } else {
+ break;
+ }
+ }
+out:
+ CDEBUG(D_RPCTRACE, "set catlog "DOSTID" first idx %u\n",
+ POSTID(&cathandle->lgh_id.lgl_oi), llh->llh_cat_idx);
+ }
+
+ return 0;
+}
+
+/* Cleanup deleted plain llog traces from catalog */
+int llog_cat_cleanup(const struct lu_env *env, struct llog_handle *cathandle,
+ struct llog_handle *loghandle, int index)
+{
+ int rc;
+
+ LASSERT(index);
+ if (loghandle != NULL) {
+ /* remove destroyed llog from catalog list and
+ * chd_current_log variable */
+ down_write(&cathandle->lgh_lock);
+ if (cathandle->u.chd.chd_current_log == loghandle)
+ cathandle->u.chd.chd_current_log = NULL;
+ list_del_init(&loghandle->u.phd.phd_entry);
+ up_write(&cathandle->lgh_lock);
+ LASSERT(index == loghandle->u.phd.phd_cookie.lgc_index);
+ /* llog was opened and keep in a list, close it now */
+ llog_close(env, loghandle);
+ }
+ /* remove plain llog entry from catalog by index */
+ llog_cat_set_first_idx(cathandle, index);
+ rc = llog_cancel_rec(env, cathandle, index);
+ if (rc == 0)
+ CDEBUG(D_HA, "cancel plain log at index %u of catalog " DOSTID "\n",
+ index, POSTID(&cathandle->lgh_id.lgl_oi));
+ return rc;
+}
+
+static int cat_cancel_cb(const struct lu_env *env, struct llog_handle *cathandle,
+ struct llog_rec_hdr *rec, void *data)
+{
+ struct llog_logid_rec *lir = (struct llog_logid_rec *)rec;
+ struct llog_handle *loghandle;
+ struct llog_log_hdr *llh;
+ int rc;
+
+ if (rec->lrh_type != LLOG_LOGID_MAGIC) {
+ CERROR("invalid record in catalog\n");
+ return -EINVAL;
+ }
+
+ CDEBUG(D_HA, "processing log "DOSTID":%x at index %u of catalog "
+ DOSTID"\n", POSTID(&lir->lid_id.lgl_oi), lir->lid_id.lgl_ogen,
+ rec->lrh_index, POSTID(&cathandle->lgh_id.lgl_oi));
+
+ rc = llog_cat_id2handle(env, cathandle, &loghandle, &lir->lid_id);
+ if (rc) {
+ CERROR("%s: cannot find handle for llog "DOSTID": %d\n",
+ cathandle->lgh_ctxt->loc_obd->obd_name,
+ POSTID(&lir->lid_id.lgl_oi), rc);
+ if (rc == -ENOENT || rc == -ESTALE) {
+ /* remove index from catalog */
+ llog_cat_cleanup(env, cathandle, NULL, rec->lrh_index);
+ }
+ return rc;
+ }
+
+ llh = loghandle->lgh_hdr;
+ if ((llh->llh_flags & LLOG_F_ZAP_WHEN_EMPTY) &&
+ (llh->llh_count == 1)) {
+ rc = llog_destroy(env, loghandle);
+ if (rc)
+ CERROR("%s: fail to destroy empty log: rc = %d\n",
+ loghandle->lgh_ctxt->loc_obd->obd_name, rc);
+
+ llog_cat_cleanup(env, cathandle, loghandle,
+ loghandle->u.phd.phd_cookie.lgc_index);
+ }
+ llog_handle_put(loghandle);
+
+ return rc;
+}
+
+/* helper to initialize catalog llog and process it to cancel */
+int llog_cat_init_and_process(const struct lu_env *env,
+ struct llog_handle *llh)
+{
+ int rc;
+
+ rc = llog_init_handle(env, llh, LLOG_F_IS_CAT, NULL);
+ if (rc)
+ return rc;
+
+ rc = llog_process_or_fork(env, llh, cat_cancel_cb, NULL, NULL, false);
+ if (rc)
+ CERROR("%s: llog_process() with cat_cancel_cb failed: rc = %d\n",
+ llh->lgh_ctxt->loc_obd->obd_name, rc);
+ return 0;
+}
+EXPORT_SYMBOL(llog_cat_init_and_process);
diff --git a/drivers/staging/lustre/lustre/obdclass/llog_internal.h b/drivers/staging/lustre/lustre/obdclass/llog_internal.h
new file mode 100644
index 000000000..5332131a2
--- /dev/null
+++ b/drivers/staging/lustre/lustre/obdclass/llog_internal.h
@@ -0,0 +1,98 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2012, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ */
+
+#ifndef __LLOG_INTERNAL_H__
+#define __LLOG_INTERNAL_H__
+
+#include "../include/lustre_log.h"
+
+struct llog_process_info {
+ struct llog_handle *lpi_loghandle;
+ llog_cb_t lpi_cb;
+ void *lpi_cbdata;
+ void *lpi_catdata;
+ int lpi_rc;
+ struct completion lpi_completion;
+ const struct lu_env *lpi_env;
+
+};
+
+struct llog_thread_info {
+ struct lu_attr lgi_attr;
+ struct lu_fid lgi_fid;
+ struct dt_object_format lgi_dof;
+ struct lu_buf lgi_buf;
+ loff_t lgi_off;
+ struct llog_rec_hdr lgi_lrh;
+ struct llog_rec_tail lgi_tail;
+};
+
+extern struct lu_context_key llog_thread_key;
+
+static inline struct llog_thread_info *llog_info(const struct lu_env *env)
+{
+ struct llog_thread_info *lgi;
+
+ lgi = lu_context_key_get(&env->le_ctx, &llog_thread_key);
+ LASSERT(lgi);
+ return lgi;
+}
+
+static inline void
+lustre_build_llog_lvfs_oid(struct llog_logid *logid, __u64 ino, __u32 gen)
+{
+ ostid_set_seq_llog(&logid->lgl_oi);
+ ostid_set_id(&logid->lgl_oi, ino);
+ logid->lgl_ogen = gen;
+}
+
+int llog_info_init(void);
+void llog_info_fini(void);
+
+void llog_handle_get(struct llog_handle *loghandle);
+void llog_handle_put(struct llog_handle *loghandle);
+int llog_cat_id2handle(const struct lu_env *env, struct llog_handle *cathandle,
+ struct llog_handle **res, struct llog_logid *logid);
+int class_config_dump_handler(const struct lu_env *env,
+ struct llog_handle *handle,
+ struct llog_rec_hdr *rec, void *data);
+int class_config_parse_rec(struct llog_rec_hdr *rec, char *buf, int size);
+int llog_process_or_fork(const struct lu_env *env,
+ struct llog_handle *loghandle,
+ llog_cb_t cb, void *data, void *catdata, bool fork);
+int llog_cat_cleanup(const struct lu_env *env, struct llog_handle *cathandle,
+ struct llog_handle *loghandle, int index);
+#endif
diff --git a/drivers/staging/lustre/lustre/obdclass/llog_obd.c b/drivers/staging/lustre/lustre/obdclass/llog_obd.c
new file mode 100644
index 000000000..978d886a1
--- /dev/null
+++ b/drivers/staging/lustre/lustre/obdclass/llog_obd.c
@@ -0,0 +1,262 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2012, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ */
+
+#define DEBUG_SUBSYSTEM S_LOG
+
+
+#include "../include/obd_class.h"
+#include "../include/lustre_log.h"
+#include "llog_internal.h"
+
+/* helper functions for calling the llog obd methods */
+static struct llog_ctxt *llog_new_ctxt(struct obd_device *obd)
+{
+ struct llog_ctxt *ctxt;
+
+ OBD_ALLOC_PTR(ctxt);
+ if (!ctxt)
+ return NULL;
+
+ ctxt->loc_obd = obd;
+ atomic_set(&ctxt->loc_refcount, 1);
+
+ return ctxt;
+}
+
+static void llog_ctxt_destroy(struct llog_ctxt *ctxt)
+{
+ if (ctxt->loc_exp) {
+ class_export_put(ctxt->loc_exp);
+ ctxt->loc_exp = NULL;
+ }
+ if (ctxt->loc_imp) {
+ class_import_put(ctxt->loc_imp);
+ ctxt->loc_imp = NULL;
+ }
+ OBD_FREE_PTR(ctxt);
+}
+
+int __llog_ctxt_put(const struct lu_env *env, struct llog_ctxt *ctxt)
+{
+ struct obd_llog_group *olg = ctxt->loc_olg;
+ struct obd_device *obd;
+ int rc = 0;
+
+ spin_lock(&olg->olg_lock);
+ if (!atomic_dec_and_test(&ctxt->loc_refcount)) {
+ spin_unlock(&olg->olg_lock);
+ return rc;
+ }
+ olg->olg_ctxts[ctxt->loc_idx] = NULL;
+ spin_unlock(&olg->olg_lock);
+
+ obd = ctxt->loc_obd;
+ spin_lock(&obd->obd_dev_lock);
+ /* sync with llog ctxt user thread */
+ spin_unlock(&obd->obd_dev_lock);
+
+ /* obd->obd_starting is needed for the case of cleanup
+ * in error case while obd is starting up. */
+ LASSERTF(obd->obd_starting == 1 ||
+ obd->obd_stopping == 1 || obd->obd_set_up == 0,
+ "wrong obd state: %d/%d/%d\n", !!obd->obd_starting,
+ !!obd->obd_stopping, !!obd->obd_set_up);
+
+ /* cleanup the llog ctxt here */
+ if (CTXTP(ctxt, cleanup))
+ rc = CTXTP(ctxt, cleanup)(env, ctxt);
+
+ llog_ctxt_destroy(ctxt);
+ wake_up(&olg->olg_waitq);
+ return rc;
+}
+EXPORT_SYMBOL(__llog_ctxt_put);
+
+int llog_cleanup(const struct lu_env *env, struct llog_ctxt *ctxt)
+{
+ struct l_wait_info lwi = LWI_INTR(LWI_ON_SIGNAL_NOOP, NULL);
+ struct obd_llog_group *olg;
+ int rc, idx;
+
+ LASSERT(ctxt != NULL);
+ LASSERT(ctxt != LP_POISON);
+
+ olg = ctxt->loc_olg;
+ LASSERT(olg != NULL);
+ LASSERT(olg != LP_POISON);
+
+ idx = ctxt->loc_idx;
+
+ /*
+ * Banlance the ctxt get when calling llog_cleanup()
+ */
+ LASSERT(atomic_read(&ctxt->loc_refcount) < LI_POISON);
+ LASSERT(atomic_read(&ctxt->loc_refcount) > 1);
+ llog_ctxt_put(ctxt);
+
+ /*
+ * Try to free the ctxt.
+ */
+ rc = __llog_ctxt_put(env, ctxt);
+ if (rc)
+ CERROR("Error %d while cleaning up ctxt %p\n",
+ rc, ctxt);
+
+ l_wait_event(olg->olg_waitq,
+ llog_group_ctxt_null(olg, idx), &lwi);
+
+ return rc;
+}
+EXPORT_SYMBOL(llog_cleanup);
+
+int llog_setup(const struct lu_env *env, struct obd_device *obd,
+ struct obd_llog_group *olg, int index,
+ struct obd_device *disk_obd, struct llog_operations *op)
+{
+ struct llog_ctxt *ctxt;
+ int rc = 0;
+
+ if (index < 0 || index >= LLOG_MAX_CTXTS)
+ return -EINVAL;
+
+ LASSERT(olg != NULL);
+
+ ctxt = llog_new_ctxt(obd);
+ if (!ctxt)
+ return -ENOMEM;
+
+ ctxt->loc_obd = obd;
+ ctxt->loc_olg = olg;
+ ctxt->loc_idx = index;
+ ctxt->loc_logops = op;
+ mutex_init(&ctxt->loc_mutex);
+ ctxt->loc_exp = class_export_get(disk_obd->obd_self_export);
+ ctxt->loc_flags = LLOG_CTXT_FLAG_UNINITIALIZED;
+
+ rc = llog_group_set_ctxt(olg, ctxt, index);
+ if (rc) {
+ llog_ctxt_destroy(ctxt);
+ if (rc == -EEXIST) {
+ ctxt = llog_group_get_ctxt(olg, index);
+ if (ctxt) {
+ /*
+ * mds_lov_update_desc() might call here multiple
+ * times. So if the llog is already set up then
+ * don't to do it again.
+ */
+ CDEBUG(D_CONFIG, "obd %s ctxt %d already set up\n",
+ obd->obd_name, index);
+ LASSERT(ctxt->loc_olg == olg);
+ LASSERT(ctxt->loc_obd == obd);
+ LASSERT(ctxt->loc_exp == disk_obd->obd_self_export);
+ LASSERT(ctxt->loc_logops == op);
+ llog_ctxt_put(ctxt);
+ }
+ rc = 0;
+ }
+ return rc;
+ }
+
+ if (op->lop_setup) {
+ if (OBD_FAIL_CHECK(OBD_FAIL_OBD_LLOG_SETUP))
+ rc = -EOPNOTSUPP;
+ else
+ rc = op->lop_setup(env, obd, olg, index, disk_obd);
+ }
+
+ if (rc) {
+ CERROR("%s: ctxt %d lop_setup=%p failed: rc = %d\n",
+ obd->obd_name, index, op->lop_setup, rc);
+ llog_group_clear_ctxt(olg, index);
+ llog_ctxt_destroy(ctxt);
+ } else {
+ CDEBUG(D_CONFIG, "obd %s ctxt %d is initialized\n",
+ obd->obd_name, index);
+ ctxt->loc_flags &= ~LLOG_CTXT_FLAG_UNINITIALIZED;
+ }
+
+ return rc;
+}
+EXPORT_SYMBOL(llog_setup);
+
+int llog_sync(struct llog_ctxt *ctxt, struct obd_export *exp, int flags)
+{
+ int rc = 0;
+
+ if (!ctxt)
+ return 0;
+
+ if (CTXTP(ctxt, sync))
+ rc = CTXTP(ctxt, sync)(ctxt, exp, flags);
+
+ return rc;
+}
+EXPORT_SYMBOL(llog_sync);
+
+int llog_cancel(const struct lu_env *env, struct llog_ctxt *ctxt,
+ struct llog_cookie *cookies, int flags)
+{
+ int rc;
+
+ if (!ctxt) {
+ CERROR("No ctxt\n");
+ return -ENODEV;
+ }
+
+ CTXT_CHECK_OP(ctxt, cancel, -EOPNOTSUPP);
+ rc = CTXTP(ctxt, cancel)(env, ctxt, cookies, flags);
+ return rc;
+}
+EXPORT_SYMBOL(llog_cancel);
+
+/* context key constructor/destructor: llog_key_init, llog_key_fini */
+LU_KEY_INIT_FINI(llog, struct llog_thread_info);
+/* context key: llog_thread_key */
+LU_CONTEXT_KEY_DEFINE(llog, LCT_MD_THREAD | LCT_MG_THREAD | LCT_LOCAL);
+LU_KEY_INIT_GENERIC(llog);
+EXPORT_SYMBOL(llog_thread_key);
+
+int llog_info_init(void)
+{
+ llog_key_init_generic(&llog_thread_key, NULL);
+ lu_context_key_register(&llog_thread_key);
+ return 0;
+}
+
+void llog_info_fini(void)
+{
+ lu_context_key_degister(&llog_thread_key);
+}
diff --git a/drivers/staging/lustre/lustre/obdclass/llog_swab.c b/drivers/staging/lustre/lustre/obdclass/llog_swab.c
new file mode 100644
index 000000000..a2d5aa105
--- /dev/null
+++ b/drivers/staging/lustre/lustre/obdclass/llog_swab.c
@@ -0,0 +1,415 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2012, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ *
+ * lustre/obdclass/llog_swab.c
+ *
+ * Swabbing of llog datatypes (from disk or over the wire).
+ *
+ * Author: jacob berkman <jacob@clusterfs.com>
+ */
+
+#define DEBUG_SUBSYSTEM S_LOG
+
+
+#include "../include/lustre_log.h"
+
+static void print_llogd_body(struct llogd_body *d)
+{
+ CDEBUG(D_OTHER, "llogd body: %p\n", d);
+ CDEBUG(D_OTHER, "\tlgd_logid.lgl_oi: "DOSTID"\n",
+ POSTID(&d->lgd_logid.lgl_oi));
+ CDEBUG(D_OTHER, "\tlgd_logid.lgl_ogen: %#x\n", d->lgd_logid.lgl_ogen);
+ CDEBUG(D_OTHER, "\tlgd_ctxt_idx: %#x\n", d->lgd_ctxt_idx);
+ CDEBUG(D_OTHER, "\tlgd_llh_flags: %#x\n", d->lgd_llh_flags);
+ CDEBUG(D_OTHER, "\tlgd_index: %#x\n", d->lgd_index);
+ CDEBUG(D_OTHER, "\tlgd_saved_index: %#x\n", d->lgd_saved_index);
+ CDEBUG(D_OTHER, "\tlgd_len: %#x\n", d->lgd_len);
+ CDEBUG(D_OTHER, "\tlgd_cur_offset: %#llx\n", d->lgd_cur_offset);
+}
+
+void lustre_swab_lu_fid(struct lu_fid *fid)
+{
+ __swab64s(&fid->f_seq);
+ __swab32s(&fid->f_oid);
+ __swab32s(&fid->f_ver);
+}
+EXPORT_SYMBOL(lustre_swab_lu_fid);
+
+void lustre_swab_ost_id(struct ost_id *oid)
+{
+ if (fid_seq_is_mdt0(oid->oi.oi_seq)) {
+ __swab64s(&oid->oi.oi_id);
+ __swab64s(&oid->oi.oi_seq);
+ } else {
+ lustre_swab_lu_fid(&oid->oi_fid);
+ }
+}
+EXPORT_SYMBOL(lustre_swab_ost_id);
+
+void lustre_swab_llog_id(struct llog_logid *log_id)
+{
+ __swab64s(&log_id->lgl_oi.oi.oi_id);
+ __swab64s(&log_id->lgl_oi.oi.oi_seq);
+ __swab32s(&log_id->lgl_ogen);
+}
+EXPORT_SYMBOL(lustre_swab_llog_id);
+
+void lustre_swab_llogd_body(struct llogd_body *d)
+{
+ print_llogd_body(d);
+ lustre_swab_llog_id(&d->lgd_logid);
+ __swab32s(&d->lgd_ctxt_idx);
+ __swab32s(&d->lgd_llh_flags);
+ __swab32s(&d->lgd_index);
+ __swab32s(&d->lgd_saved_index);
+ __swab32s(&d->lgd_len);
+ __swab64s(&d->lgd_cur_offset);
+ print_llogd_body(d);
+}
+EXPORT_SYMBOL(lustre_swab_llogd_body);
+
+void lustre_swab_llogd_conn_body(struct llogd_conn_body *d)
+{
+ __swab64s(&d->lgdc_gen.mnt_cnt);
+ __swab64s(&d->lgdc_gen.conn_cnt);
+ lustre_swab_llog_id(&d->lgdc_logid);
+ __swab32s(&d->lgdc_ctxt_idx);
+}
+EXPORT_SYMBOL(lustre_swab_llogd_conn_body);
+
+void lustre_swab_ll_fid(struct ll_fid *fid)
+{
+ __swab64s(&fid->id);
+ __swab32s(&fid->generation);
+ __swab32s(&fid->f_type);
+}
+EXPORT_SYMBOL(lustre_swab_ll_fid);
+
+void lustre_swab_lu_seq_range(struct lu_seq_range *range)
+{
+ __swab64s(&range->lsr_start);
+ __swab64s(&range->lsr_end);
+ __swab32s(&range->lsr_index);
+ __swab32s(&range->lsr_flags);
+}
+EXPORT_SYMBOL(lustre_swab_lu_seq_range);
+
+void lustre_swab_llog_rec(struct llog_rec_hdr *rec)
+{
+ struct llog_rec_tail *tail = NULL;
+
+ __swab32s(&rec->lrh_len);
+ __swab32s(&rec->lrh_index);
+ __swab32s(&rec->lrh_type);
+ __swab32s(&rec->lrh_id);
+
+ switch (rec->lrh_type) {
+ case OST_SZ_REC:
+ {
+ struct llog_size_change_rec *lsc =
+ (struct llog_size_change_rec *)rec;
+
+ lustre_swab_ll_fid(&lsc->lsc_fid);
+ __swab32s(&lsc->lsc_ioepoch);
+ tail = &lsc->lsc_tail;
+ break;
+ }
+ case MDS_UNLINK_REC:
+ {
+ struct llog_unlink_rec *lur = (struct llog_unlink_rec *)rec;
+
+ __swab64s(&lur->lur_oid);
+ __swab32s(&lur->lur_oseq);
+ __swab32s(&lur->lur_count);
+ tail = &lur->lur_tail;
+ break;
+ }
+ case MDS_UNLINK64_REC:
+ {
+ struct llog_unlink64_rec *lur =
+ (struct llog_unlink64_rec *)rec;
+
+ lustre_swab_lu_fid(&lur->lur_fid);
+ __swab32s(&lur->lur_count);
+ tail = &lur->lur_tail;
+ break;
+ }
+ case CHANGELOG_REC:
+ {
+ struct llog_changelog_rec *cr =
+ (struct llog_changelog_rec *)rec;
+
+ __swab16s(&cr->cr.cr_namelen);
+ __swab16s(&cr->cr.cr_flags);
+ __swab32s(&cr->cr.cr_type);
+ __swab64s(&cr->cr.cr_index);
+ __swab64s(&cr->cr.cr_prev);
+ __swab64s(&cr->cr.cr_time);
+ lustre_swab_lu_fid(&cr->cr.cr_tfid);
+ lustre_swab_lu_fid(&cr->cr.cr_pfid);
+ if (CHANGELOG_REC_EXTENDED(&cr->cr)) {
+ struct llog_changelog_ext_rec *ext =
+ (struct llog_changelog_ext_rec *)rec;
+
+ lustre_swab_lu_fid(&ext->cr.cr_sfid);
+ lustre_swab_lu_fid(&ext->cr.cr_spfid);
+ tail = &ext->cr_tail;
+ } else {
+ tail = &cr->cr_tail;
+ }
+ tail = (struct llog_rec_tail *)((char *)tail +
+ cr->cr.cr_namelen);
+ break;
+ }
+ case CHANGELOG_USER_REC:
+ {
+ struct llog_changelog_user_rec *cur =
+ (struct llog_changelog_user_rec *)rec;
+
+ __swab32s(&cur->cur_id);
+ __swab64s(&cur->cur_endrec);
+ tail = &cur->cur_tail;
+ break;
+ }
+
+ case HSM_AGENT_REC: {
+ struct llog_agent_req_rec *arr =
+ (struct llog_agent_req_rec *)rec;
+
+ __swab32s(&arr->arr_hai.hai_len);
+ __swab32s(&arr->arr_hai.hai_action);
+ lustre_swab_lu_fid(&arr->arr_hai.hai_fid);
+ lustre_swab_lu_fid(&arr->arr_hai.hai_dfid);
+ __swab64s(&arr->arr_hai.hai_cookie);
+ __swab64s(&arr->arr_hai.hai_extent.offset);
+ __swab64s(&arr->arr_hai.hai_extent.length);
+ __swab64s(&arr->arr_hai.hai_gid);
+ /* no swabing for opaque data */
+ /* hai_data[0]; */
+ break;
+ }
+
+ case MDS_SETATTR64_REC:
+ {
+ struct llog_setattr64_rec *lsr =
+ (struct llog_setattr64_rec *)rec;
+
+ lustre_swab_ost_id(&lsr->lsr_oi);
+ __swab32s(&lsr->lsr_uid);
+ __swab32s(&lsr->lsr_uid_h);
+ __swab32s(&lsr->lsr_gid);
+ __swab32s(&lsr->lsr_gid_h);
+ tail = &lsr->lsr_tail;
+ break;
+ }
+ case OBD_CFG_REC:
+ /* these are swabbed as they are consumed */
+ break;
+ case LLOG_HDR_MAGIC:
+ {
+ struct llog_log_hdr *llh = (struct llog_log_hdr *)rec;
+
+ __swab64s(&llh->llh_timestamp);
+ __swab32s(&llh->llh_count);
+ __swab32s(&llh->llh_bitmap_offset);
+ __swab32s(&llh->llh_flags);
+ __swab32s(&llh->llh_size);
+ __swab32s(&llh->llh_cat_idx);
+ tail = &llh->llh_tail;
+ break;
+ }
+ case LLOG_LOGID_MAGIC:
+ {
+ struct llog_logid_rec *lid = (struct llog_logid_rec *)rec;
+
+ lustre_swab_llog_id(&lid->lid_id);
+ tail = &lid->lid_tail;
+ break;
+ }
+ case LLOG_GEN_REC:
+ {
+ struct llog_gen_rec *lgr = (struct llog_gen_rec *)rec;
+
+ __swab64s(&lgr->lgr_gen.mnt_cnt);
+ __swab64s(&lgr->lgr_gen.conn_cnt);
+ tail = &lgr->lgr_tail;
+ break;
+ }
+ case LLOG_PAD_MAGIC:
+ break;
+ default:
+ CERROR("Unknown llog rec type %#x swabbing rec %p\n",
+ rec->lrh_type, rec);
+ }
+
+ if (tail) {
+ __swab32s(&tail->lrt_len);
+ __swab32s(&tail->lrt_index);
+ }
+}
+EXPORT_SYMBOL(lustre_swab_llog_rec);
+
+static void print_llog_hdr(struct llog_log_hdr *h)
+{
+ CDEBUG(D_OTHER, "llog header: %p\n", h);
+ CDEBUG(D_OTHER, "\tllh_hdr.lrh_index: %#x\n", h->llh_hdr.lrh_index);
+ CDEBUG(D_OTHER, "\tllh_hdr.lrh_len: %#x\n", h->llh_hdr.lrh_len);
+ CDEBUG(D_OTHER, "\tllh_hdr.lrh_type: %#x\n", h->llh_hdr.lrh_type);
+ CDEBUG(D_OTHER, "\tllh_timestamp: %#llx\n", h->llh_timestamp);
+ CDEBUG(D_OTHER, "\tllh_count: %#x\n", h->llh_count);
+ CDEBUG(D_OTHER, "\tllh_bitmap_offset: %#x\n", h->llh_bitmap_offset);
+ CDEBUG(D_OTHER, "\tllh_flags: %#x\n", h->llh_flags);
+ CDEBUG(D_OTHER, "\tllh_size: %#x\n", h->llh_size);
+ CDEBUG(D_OTHER, "\tllh_cat_idx: %#x\n", h->llh_cat_idx);
+ CDEBUG(D_OTHER, "\tllh_tail.lrt_index: %#x\n", h->llh_tail.lrt_index);
+ CDEBUG(D_OTHER, "\tllh_tail.lrt_len: %#x\n", h->llh_tail.lrt_len);
+}
+
+void lustre_swab_llog_hdr(struct llog_log_hdr *h)
+{
+ print_llog_hdr(h);
+
+ lustre_swab_llog_rec(&h->llh_hdr);
+
+ print_llog_hdr(h);
+}
+EXPORT_SYMBOL(lustre_swab_llog_hdr);
+
+static void print_lustre_cfg(struct lustre_cfg *lcfg)
+{
+ int i;
+
+ if (!(libcfs_debug & D_OTHER)) /* don't loop on nothing */
+ return;
+ CDEBUG(D_OTHER, "lustre_cfg: %p\n", lcfg);
+ CDEBUG(D_OTHER, "\tlcfg->lcfg_version: %#x\n", lcfg->lcfg_version);
+
+ CDEBUG(D_OTHER, "\tlcfg->lcfg_command: %#x\n", lcfg->lcfg_command);
+ CDEBUG(D_OTHER, "\tlcfg->lcfg_num: %#x\n", lcfg->lcfg_num);
+ CDEBUG(D_OTHER, "\tlcfg->lcfg_flags: %#x\n", lcfg->lcfg_flags);
+ CDEBUG(D_OTHER, "\tlcfg->lcfg_nid: %s\n", libcfs_nid2str(lcfg->lcfg_nid));
+
+ CDEBUG(D_OTHER, "\tlcfg->lcfg_bufcount: %d\n", lcfg->lcfg_bufcount);
+ if (lcfg->lcfg_bufcount < LUSTRE_CFG_MAX_BUFCOUNT)
+ for (i = 0; i < lcfg->lcfg_bufcount; i++)
+ CDEBUG(D_OTHER, "\tlcfg->lcfg_buflens[%d]: %d\n",
+ i, lcfg->lcfg_buflens[i]);
+}
+
+void lustre_swab_lustre_cfg(struct lustre_cfg *lcfg)
+{
+ int i;
+
+ __swab32s(&lcfg->lcfg_version);
+
+ if (lcfg->lcfg_version != LUSTRE_CFG_VERSION) {
+ CERROR("not swabbing lustre_cfg version %#x (expecting %#x)\n",
+ lcfg->lcfg_version, LUSTRE_CFG_VERSION);
+ return;
+ }
+
+ __swab32s(&lcfg->lcfg_command);
+ __swab32s(&lcfg->lcfg_num);
+ __swab32s(&lcfg->lcfg_flags);
+ __swab64s(&lcfg->lcfg_nid);
+ __swab32s(&lcfg->lcfg_bufcount);
+ for (i = 0; i < lcfg->lcfg_bufcount && i < LUSTRE_CFG_MAX_BUFCOUNT; i++)
+ __swab32s(&lcfg->lcfg_buflens[i]);
+
+ print_lustre_cfg(lcfg);
+ return;
+}
+EXPORT_SYMBOL(lustre_swab_lustre_cfg);
+
+/* used only for compatibility with old on-disk cfg_marker data */
+struct cfg_marker32 {
+ __u32 cm_step;
+ __u32 cm_flags;
+ __u32 cm_vers;
+ __u32 padding;
+ __u32 cm_createtime;
+ __u32 cm_canceltime;
+ char cm_tgtname[MTI_NAME_MAXLEN];
+ char cm_comment[MTI_NAME_MAXLEN];
+};
+
+#define MTI_NAMELEN32 (MTI_NAME_MAXLEN - \
+ (sizeof(struct cfg_marker) - sizeof(struct cfg_marker32)))
+
+void lustre_swab_cfg_marker(struct cfg_marker *marker, int swab, int size)
+{
+ struct cfg_marker32 *cm32 = (struct cfg_marker32 *)marker;
+
+ if (swab) {
+ __swab32s(&marker->cm_step);
+ __swab32s(&marker->cm_flags);
+ __swab32s(&marker->cm_vers);
+ }
+ if (size == sizeof(*cm32)) {
+ __u32 createtime, canceltime;
+ /* There was a problem with the original declaration of
+ * cfg_marker on 32-bit systems because it used time_t as
+ * a wire protocol structure, and didn't verify this in
+ * wirecheck. We now have to convert the offsets of the
+ * later fields in order to work on 32- and 64-bit systems.
+ *
+ * Fortunately, the cm_comment field has no functional use
+ * so can be sacrificed when converting the timestamp size.
+ *
+ * Overwrite fields from the end first, so they are not
+ * clobbered, and use memmove() instead of memcpy() because
+ * the source and target buffers overlap. bug 16771 */
+ createtime = cm32->cm_createtime;
+ canceltime = cm32->cm_canceltime;
+ memmove(marker->cm_comment, cm32->cm_comment, MTI_NAMELEN32);
+ marker->cm_comment[MTI_NAMELEN32 - 1] = '\0';
+ memmove(marker->cm_tgtname, cm32->cm_tgtname,
+ sizeof(marker->cm_tgtname));
+ if (swab) {
+ __swab32s(&createtime);
+ __swab32s(&canceltime);
+ }
+ marker->cm_createtime = createtime;
+ marker->cm_canceltime = canceltime;
+ CDEBUG(D_CONFIG, "Find old cfg_marker(Srv32b,Clt64b) for target %s, converting\n",
+ marker->cm_tgtname);
+ } else if (swab) {
+ __swab64s(&marker->cm_createtime);
+ __swab64s(&marker->cm_canceltime);
+ }
+
+ return;
+}
+EXPORT_SYMBOL(lustre_swab_cfg_marker);
diff --git a/drivers/staging/lustre/lustre/obdclass/lprocfs_counters.c b/drivers/staging/lustre/lustre/obdclass/lprocfs_counters.c
new file mode 100644
index 000000000..c49dfe541
--- /dev/null
+++ b/drivers/staging/lustre/lustre/obdclass/lprocfs_counters.c
@@ -0,0 +1,139 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ *
+ * http://www.gnu.org/licenses/gpl-2.0.html
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2012, 2013, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ *
+ * lustre/obdclass/lprocfs_counters.c
+ *
+ * Lustre lprocfs counter routines
+ *
+ * Author: Andreas Dilger <andreas.dilger@intel.com>
+ */
+
+#include <linux/module.h>
+#include "../include/lprocfs_status.h"
+#include "../include/obd_support.h"
+
+struct lprocfs_stats *obd_memory = NULL;
+EXPORT_SYMBOL(obd_memory);
+
+void lprocfs_counter_add(struct lprocfs_stats *stats, int idx, long amount)
+{
+ struct lprocfs_counter *percpu_cntr;
+ struct lprocfs_counter_header *header;
+ int smp_id;
+ unsigned long flags = 0;
+
+ if (stats == NULL)
+ return;
+
+ LASSERTF(0 <= idx && idx < stats->ls_num,
+ "idx %d, ls_num %hu\n", idx, stats->ls_num);
+
+ /* With per-client stats, statistics are allocated only for
+ * single CPU area, so the smp_id should be 0 always. */
+ smp_id = lprocfs_stats_lock(stats, LPROCFS_GET_SMP_ID, &flags);
+ if (smp_id < 0)
+ return;
+
+ header = &stats->ls_cnt_header[idx];
+ percpu_cntr = lprocfs_stats_counter_get(stats, smp_id, idx);
+ percpu_cntr->lc_count++;
+
+ if (header->lc_config & LPROCFS_CNTR_AVGMINMAX) {
+ /*
+ * lprocfs_counter_add() can be called in interrupt context,
+ * as memory allocation could trigger memory shrinker call
+ * ldlm_pool_shrink(), which calls lprocfs_counter_add().
+ * LU-1727.
+ *
+ * Only obd_memory uses LPROCFS_STATS_FLAG_IRQ_SAFE
+ * flag, because it needs accurate counting lest memory leak
+ * check reports error.
+ */
+ if (in_interrupt() &&
+ (stats->ls_flags & LPROCFS_STATS_FLAG_IRQ_SAFE) != 0)
+ percpu_cntr->lc_sum_irq += amount;
+ else
+ percpu_cntr->lc_sum += amount;
+
+ if (header->lc_config & LPROCFS_CNTR_STDDEV)
+ percpu_cntr->lc_sumsquare += (__s64)amount * amount;
+ if (amount < percpu_cntr->lc_min)
+ percpu_cntr->lc_min = amount;
+ if (amount > percpu_cntr->lc_max)
+ percpu_cntr->lc_max = amount;
+ }
+ lprocfs_stats_unlock(stats, LPROCFS_GET_SMP_ID, &flags);
+}
+EXPORT_SYMBOL(lprocfs_counter_add);
+
+void lprocfs_counter_sub(struct lprocfs_stats *stats, int idx, long amount)
+{
+ struct lprocfs_counter *percpu_cntr;
+ struct lprocfs_counter_header *header;
+ int smp_id;
+ unsigned long flags = 0;
+
+ if (stats == NULL)
+ return;
+
+ LASSERTF(0 <= idx && idx < stats->ls_num,
+ "idx %d, ls_num %hu\n", idx, stats->ls_num);
+
+ /* With per-client stats, statistics are allocated only for
+ * single CPU area, so the smp_id should be 0 always. */
+ smp_id = lprocfs_stats_lock(stats, LPROCFS_GET_SMP_ID, &flags);
+ if (smp_id < 0)
+ return;
+
+ header = &stats->ls_cnt_header[idx];
+ percpu_cntr = lprocfs_stats_counter_get(stats, smp_id, idx);
+ if (header->lc_config & LPROCFS_CNTR_AVGMINMAX) {
+ /*
+ * Sometimes we use RCU callbacks to free memory which calls
+ * lprocfs_counter_sub(), and RCU callbacks may execute in
+ * softirq context - right now that's the only case we're in
+ * softirq context here, use separate counter for that.
+ * bz20650.
+ *
+ * Only obd_memory uses LPROCFS_STATS_FLAG_IRQ_SAFE
+ * flag, because it needs accurate counting lest memory leak
+ * check reports error.
+ */
+ if (in_interrupt() &&
+ (stats->ls_flags & LPROCFS_STATS_FLAG_IRQ_SAFE) != 0)
+ percpu_cntr->lc_sum_irq -= amount;
+ else
+ percpu_cntr->lc_sum -= amount;
+ }
+ lprocfs_stats_unlock(stats, LPROCFS_GET_SMP_ID, &flags);
+}
+EXPORT_SYMBOL(lprocfs_counter_sub);
diff --git a/drivers/staging/lustre/lustre/obdclass/lprocfs_status.c b/drivers/staging/lustre/lustre/obdclass/lprocfs_status.c
new file mode 100644
index 000000000..c171c6c6c
--- /dev/null
+++ b/drivers/staging/lustre/lustre/obdclass/lprocfs_status.c
@@ -0,0 +1,2059 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2002, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2011, 2012, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ *
+ * lustre/obdclass/lprocfs_status.c
+ *
+ * Author: Hariharan Thantry <thantry@users.sourceforge.net>
+ */
+
+#define DEBUG_SUBSYSTEM S_CLASS
+
+
+#include "../include/obd_class.h"
+#include "../include/lprocfs_status.h"
+#include "../include/lustre/lustre_idl.h"
+#include <linux/seq_file.h>
+#include <linux/ctype.h>
+
+static const char * const obd_connect_names[] = {
+ "read_only",
+ "lov_index",
+ "unused",
+ "write_grant",
+ "server_lock",
+ "version",
+ "request_portal",
+ "acl",
+ "xattr",
+ "create_on_write",
+ "truncate_lock",
+ "initial_transno",
+ "inode_bit_locks",
+ "join_file(obsolete)",
+ "getattr_by_fid",
+ "no_oh_for_devices",
+ "remote_client",
+ "remote_client_by_force",
+ "max_byte_per_rpc",
+ "64bit_qdata",
+ "mds_capability",
+ "oss_capability",
+ "early_lock_cancel",
+ "som",
+ "adaptive_timeouts",
+ "lru_resize",
+ "mds_mds_connection",
+ "real_conn",
+ "change_qunit_size",
+ "alt_checksum_algorithm",
+ "fid_is_enabled",
+ "version_recovery",
+ "pools",
+ "grant_shrink",
+ "skip_orphan",
+ "large_ea",
+ "full20",
+ "layout_lock",
+ "64bithash",
+ "object_max_bytes",
+ "imp_recov",
+ "jobstats",
+ "umask",
+ "einprogress",
+ "grant_param",
+ "flock_owner",
+ "lvb_type",
+ "nanoseconds_times",
+ "lightweight_conn",
+ "short_io",
+ "pingless",
+ "flock_deadlock",
+ "disp_stripe",
+ "unknown",
+ NULL
+};
+
+int obd_connect_flags2str(char *page, int count, __u64 flags, char *sep)
+{
+ __u64 mask = 1;
+ int i, ret = 0;
+
+ for (i = 0; obd_connect_names[i] != NULL; i++, mask <<= 1) {
+ if (flags & mask)
+ ret += snprintf(page + ret, count - ret, "%s%s",
+ ret ? sep : "", obd_connect_names[i]);
+ }
+ if (flags & ~(mask - 1))
+ ret += snprintf(page + ret, count - ret,
+ "%sunknown flags %#llx",
+ ret ? sep : "", flags & ~(mask - 1));
+ return ret;
+}
+EXPORT_SYMBOL(obd_connect_flags2str);
+
+int lprocfs_read_frac_helper(char *buffer, unsigned long count, long val,
+ int mult)
+{
+ long decimal_val, frac_val;
+ int prtn;
+
+ if (count < 10)
+ return -EINVAL;
+
+ decimal_val = val / mult;
+ prtn = snprintf(buffer, count, "%ld", decimal_val);
+ frac_val = val % mult;
+
+ if (prtn < (count - 4) && frac_val > 0) {
+ long temp_frac;
+ int i, temp_mult = 1, frac_bits = 0;
+
+ temp_frac = frac_val * 10;
+ buffer[prtn++] = '.';
+ while (frac_bits < 2 && (temp_frac / mult) < 1) {
+ /* only reserved 2 bits fraction */
+ buffer[prtn++] = '0';
+ temp_frac *= 10;
+ frac_bits++;
+ }
+ /*
+ * Need to think these cases :
+ * 1. #echo x.00 > /proc/xxx output result : x
+ * 2. #echo x.0x > /proc/xxx output result : x.0x
+ * 3. #echo x.x0 > /proc/xxx output result : x.x
+ * 4. #echo x.xx > /proc/xxx output result : x.xx
+ * Only reserved 2 bits fraction.
+ */
+ for (i = 0; i < (5 - prtn); i++)
+ temp_mult *= 10;
+
+ frac_bits = min((int)count - prtn, 3 - frac_bits);
+ prtn += snprintf(buffer + prtn, frac_bits, "%ld",
+ frac_val * temp_mult / mult);
+
+ prtn--;
+ while (buffer[prtn] < '1' || buffer[prtn] > '9') {
+ prtn--;
+ if (buffer[prtn] == '.') {
+ prtn--;
+ break;
+ }
+ }
+ prtn++;
+ }
+ buffer[prtn++] = '\n';
+ return prtn;
+}
+EXPORT_SYMBOL(lprocfs_read_frac_helper);
+
+int lprocfs_write_frac_helper(const char __user *buffer, unsigned long count,
+ int *val, int mult)
+{
+ char kernbuf[20], *end, *pbuf;
+
+ if (count > (sizeof(kernbuf) - 1))
+ return -EINVAL;
+
+ if (copy_from_user(kernbuf, buffer, count))
+ return -EFAULT;
+
+ kernbuf[count] = '\0';
+ pbuf = kernbuf;
+ if (*pbuf == '-') {
+ mult = -mult;
+ pbuf++;
+ }
+
+ *val = (int)simple_strtoul(pbuf, &end, 10) * mult;
+ if (pbuf == end)
+ return -EINVAL;
+
+ if (end != NULL && *end == '.') {
+ int temp_val, pow = 1;
+ int i;
+
+ pbuf = end + 1;
+ if (strlen(pbuf) > 5)
+ pbuf[5] = '\0'; /*only allow 5bits fractional*/
+
+ temp_val = (int)simple_strtoul(pbuf, &end, 10) * mult;
+
+ if (pbuf < end) {
+ for (i = 0; i < (end - pbuf); i++)
+ pow *= 10;
+
+ *val += temp_val / pow;
+ }
+ }
+ return 0;
+}
+EXPORT_SYMBOL(lprocfs_write_frac_helper);
+
+#if defined (CONFIG_PROC_FS)
+
+static int lprocfs_no_percpu_stats;
+module_param(lprocfs_no_percpu_stats, int, 0644);
+MODULE_PARM_DESC(lprocfs_no_percpu_stats, "Do not alloc percpu data for lprocfs stats");
+
+#define MAX_STRING_SIZE 128
+
+int lprocfs_single_release(struct inode *inode, struct file *file)
+{
+ return single_release(inode, file);
+}
+EXPORT_SYMBOL(lprocfs_single_release);
+
+int lprocfs_seq_release(struct inode *inode, struct file *file)
+{
+ return seq_release(inode, file);
+}
+EXPORT_SYMBOL(lprocfs_seq_release);
+
+/* lprocfs API calls */
+
+struct proc_dir_entry *lprocfs_add_simple(struct proc_dir_entry *root,
+ char *name, void *data,
+ struct file_operations *fops)
+{
+ struct proc_dir_entry *proc;
+ umode_t mode = 0;
+
+ if (root == NULL || name == NULL || fops == NULL)
+ return ERR_PTR(-EINVAL);
+
+ if (fops->read)
+ mode = 0444;
+ if (fops->write)
+ mode |= 0200;
+ proc = proc_create_data(name, mode, root, fops, data);
+ if (!proc) {
+ CERROR("LprocFS: No memory to create /proc entry %s", name);
+ return ERR_PTR(-ENOMEM);
+ }
+ return proc;
+}
+EXPORT_SYMBOL(lprocfs_add_simple);
+
+struct proc_dir_entry *lprocfs_add_symlink(const char *name,
+ struct proc_dir_entry *parent, const char *format, ...)
+{
+ struct proc_dir_entry *entry;
+ char *dest;
+ va_list ap;
+
+ if (parent == NULL || format == NULL)
+ return NULL;
+
+ OBD_ALLOC_WAIT(dest, MAX_STRING_SIZE + 1);
+ if (dest == NULL)
+ return NULL;
+
+ va_start(ap, format);
+ vsnprintf(dest, MAX_STRING_SIZE, format, ap);
+ va_end(ap);
+
+ entry = proc_symlink(name, parent, dest);
+ if (entry == NULL)
+ CERROR("LprocFS: Could not create symbolic link from %s to %s",
+ name, dest);
+
+ OBD_FREE(dest, MAX_STRING_SIZE + 1);
+ return entry;
+}
+EXPORT_SYMBOL(lprocfs_add_symlink);
+
+static struct file_operations lprocfs_generic_fops = { };
+
+/**
+ * Add /proc entries.
+ *
+ * \param root [in] The parent proc entry on which new entry will be added.
+ * \param list [in] Array of proc entries to be added.
+ * \param data [in] The argument to be passed when entries read/write routines
+ * are called through /proc file.
+ *
+ * \retval 0 on success
+ * < 0 on error
+ */
+int lprocfs_add_vars(struct proc_dir_entry *root, struct lprocfs_vars *list,
+ void *data)
+{
+ if (root == NULL || list == NULL)
+ return -EINVAL;
+
+ while (list->name != NULL) {
+ struct proc_dir_entry *proc;
+ umode_t mode = 0;
+
+ if (list->proc_mode != 0000) {
+ mode = list->proc_mode;
+ } else if (list->fops) {
+ if (list->fops->read)
+ mode = 0444;
+ if (list->fops->write)
+ mode |= 0200;
+ }
+ proc = proc_create_data(list->name, mode, root,
+ list->fops ?: &lprocfs_generic_fops,
+ list->data ?: data);
+ if (proc == NULL)
+ return -ENOMEM;
+ list++;
+ }
+ return 0;
+}
+EXPORT_SYMBOL(lprocfs_add_vars);
+
+void lprocfs_remove(struct proc_dir_entry **rooth)
+{
+ proc_remove(*rooth);
+ *rooth = NULL;
+}
+EXPORT_SYMBOL(lprocfs_remove);
+
+void lprocfs_remove_proc_entry(const char *name, struct proc_dir_entry *parent)
+{
+ LASSERT(parent != NULL);
+ remove_proc_entry(name, parent);
+}
+EXPORT_SYMBOL(lprocfs_remove_proc_entry);
+
+struct proc_dir_entry *lprocfs_register(const char *name,
+ struct proc_dir_entry *parent,
+ struct lprocfs_vars *list, void *data)
+{
+ struct proc_dir_entry *entry;
+
+ entry = proc_mkdir(name, parent);
+ if (entry == NULL) {
+ entry = ERR_PTR(-ENOMEM);
+ goto out;
+ }
+
+ if (list != NULL) {
+ int rc = lprocfs_add_vars(entry, list, data);
+ if (rc != 0) {
+ lprocfs_remove(&entry);
+ entry = ERR_PTR(rc);
+ }
+ }
+out:
+ return entry;
+}
+EXPORT_SYMBOL(lprocfs_register);
+
+/* Generic callbacks */
+int lprocfs_rd_uint(struct seq_file *m, void *data)
+{
+ seq_printf(m, "%u\n", *(unsigned int *)data);
+ return 0;
+}
+EXPORT_SYMBOL(lprocfs_rd_uint);
+
+int lprocfs_wr_uint(struct file *file, const char __user *buffer,
+ unsigned long count, void *data)
+{
+ unsigned *p = data;
+ char dummy[MAX_STRING_SIZE + 1], *end;
+ unsigned long tmp;
+
+ dummy[MAX_STRING_SIZE] = '\0';
+ if (copy_from_user(dummy, buffer, MAX_STRING_SIZE))
+ return -EFAULT;
+
+ tmp = simple_strtoul(dummy, &end, 0);
+ if (dummy == end)
+ return -EINVAL;
+
+ *p = (unsigned int)tmp;
+ return count;
+}
+EXPORT_SYMBOL(lprocfs_wr_uint);
+
+int lprocfs_rd_u64(struct seq_file *m, void *data)
+{
+ seq_printf(m, "%llu\n", *(__u64 *)data);
+ return 0;
+}
+EXPORT_SYMBOL(lprocfs_rd_u64);
+
+int lprocfs_rd_atomic(struct seq_file *m, void *data)
+{
+ atomic_t *atom = data;
+ LASSERT(atom != NULL);
+ seq_printf(m, "%d\n", atomic_read(atom));
+ return 0;
+}
+EXPORT_SYMBOL(lprocfs_rd_atomic);
+
+int lprocfs_wr_atomic(struct file *file, const char __user *buffer,
+ unsigned long count, void *data)
+{
+ atomic_t *atm = data;
+ int val = 0;
+ int rc;
+
+ rc = lprocfs_write_helper(buffer, count, &val);
+ if (rc < 0)
+ return rc;
+
+ if (val <= 0)
+ return -ERANGE;
+
+ atomic_set(atm, val);
+ return count;
+}
+EXPORT_SYMBOL(lprocfs_wr_atomic);
+
+int lprocfs_rd_uuid(struct seq_file *m, void *data)
+{
+ struct obd_device *obd = data;
+
+ LASSERT(obd != NULL);
+ seq_printf(m, "%s\n", obd->obd_uuid.uuid);
+ return 0;
+}
+EXPORT_SYMBOL(lprocfs_rd_uuid);
+
+int lprocfs_rd_name(struct seq_file *m, void *data)
+{
+ struct obd_device *dev = data;
+
+ LASSERT(dev != NULL);
+ seq_printf(m, "%s\n", dev->obd_name);
+ return 0;
+}
+EXPORT_SYMBOL(lprocfs_rd_name);
+
+int lprocfs_rd_blksize(struct seq_file *m, void *data)
+{
+ struct obd_device *obd = data;
+ struct obd_statfs osfs;
+ int rc = obd_statfs(NULL, obd->obd_self_export, &osfs,
+ cfs_time_shift_64(-OBD_STATFS_CACHE_SECONDS),
+ OBD_STATFS_NODELAY);
+ if (!rc)
+ seq_printf(m, "%u\n", osfs.os_bsize);
+
+ return rc;
+}
+EXPORT_SYMBOL(lprocfs_rd_blksize);
+
+int lprocfs_rd_kbytestotal(struct seq_file *m, void *data)
+{
+ struct obd_device *obd = data;
+ struct obd_statfs osfs;
+ int rc = obd_statfs(NULL, obd->obd_self_export, &osfs,
+ cfs_time_shift_64(-OBD_STATFS_CACHE_SECONDS),
+ OBD_STATFS_NODELAY);
+ if (!rc) {
+ __u32 blk_size = osfs.os_bsize >> 10;
+ __u64 result = osfs.os_blocks;
+
+ while (blk_size >>= 1)
+ result <<= 1;
+
+ seq_printf(m, "%llu\n", result);
+ }
+
+ return rc;
+}
+EXPORT_SYMBOL(lprocfs_rd_kbytestotal);
+
+int lprocfs_rd_kbytesfree(struct seq_file *m, void *data)
+{
+ struct obd_device *obd = data;
+ struct obd_statfs osfs;
+ int rc = obd_statfs(NULL, obd->obd_self_export, &osfs,
+ cfs_time_shift_64(-OBD_STATFS_CACHE_SECONDS),
+ OBD_STATFS_NODELAY);
+ if (!rc) {
+ __u32 blk_size = osfs.os_bsize >> 10;
+ __u64 result = osfs.os_bfree;
+
+ while (blk_size >>= 1)
+ result <<= 1;
+
+ seq_printf(m, "%llu\n", result);
+ }
+
+ return rc;
+}
+EXPORT_SYMBOL(lprocfs_rd_kbytesfree);
+
+int lprocfs_rd_kbytesavail(struct seq_file *m, void *data)
+{
+ struct obd_device *obd = data;
+ struct obd_statfs osfs;
+ int rc = obd_statfs(NULL, obd->obd_self_export, &osfs,
+ cfs_time_shift_64(-OBD_STATFS_CACHE_SECONDS),
+ OBD_STATFS_NODELAY);
+ if (!rc) {
+ __u32 blk_size = osfs.os_bsize >> 10;
+ __u64 result = osfs.os_bavail;
+
+ while (blk_size >>= 1)
+ result <<= 1;
+
+ seq_printf(m, "%llu\n", result);
+ }
+
+ return rc;
+}
+EXPORT_SYMBOL(lprocfs_rd_kbytesavail);
+
+int lprocfs_rd_filestotal(struct seq_file *m, void *data)
+{
+ struct obd_device *obd = data;
+ struct obd_statfs osfs;
+ int rc = obd_statfs(NULL, obd->obd_self_export, &osfs,
+ cfs_time_shift_64(-OBD_STATFS_CACHE_SECONDS),
+ OBD_STATFS_NODELAY);
+ if (!rc)
+ seq_printf(m, "%llu\n", osfs.os_files);
+
+ return rc;
+}
+EXPORT_SYMBOL(lprocfs_rd_filestotal);
+
+int lprocfs_rd_filesfree(struct seq_file *m, void *data)
+{
+ struct obd_device *obd = data;
+ struct obd_statfs osfs;
+ int rc = obd_statfs(NULL, obd->obd_self_export, &osfs,
+ cfs_time_shift_64(-OBD_STATFS_CACHE_SECONDS),
+ OBD_STATFS_NODELAY);
+ if (!rc)
+ seq_printf(m, "%llu\n", osfs.os_ffree);
+
+ return rc;
+}
+EXPORT_SYMBOL(lprocfs_rd_filesfree);
+
+int lprocfs_rd_server_uuid(struct seq_file *m, void *data)
+{
+ struct obd_device *obd = data;
+ struct obd_import *imp;
+ char *imp_state_name = NULL;
+
+ LASSERT(obd != NULL);
+ LPROCFS_CLIMP_CHECK(obd);
+ imp = obd->u.cli.cl_import;
+ imp_state_name = ptlrpc_import_state_name(imp->imp_state);
+ seq_printf(m, "%s\t%s%s\n",
+ obd2cli_tgt(obd), imp_state_name,
+ imp->imp_deactive ? "\tDEACTIVATED" : "");
+
+ LPROCFS_CLIMP_EXIT(obd);
+
+ return 0;
+}
+EXPORT_SYMBOL(lprocfs_rd_server_uuid);
+
+int lprocfs_rd_conn_uuid(struct seq_file *m, void *data)
+{
+ struct obd_device *obd = data;
+ struct ptlrpc_connection *conn;
+
+ LASSERT(obd != NULL);
+
+ LPROCFS_CLIMP_CHECK(obd);
+ conn = obd->u.cli.cl_import->imp_connection;
+ if (conn && obd->u.cli.cl_import)
+ seq_printf(m, "%s\n", conn->c_remote_uuid.uuid);
+ else
+ seq_puts(m, "<none>\n");
+
+ LPROCFS_CLIMP_EXIT(obd);
+
+ return 0;
+}
+EXPORT_SYMBOL(lprocfs_rd_conn_uuid);
+
+/** add up per-cpu counters */
+void lprocfs_stats_collect(struct lprocfs_stats *stats, int idx,
+ struct lprocfs_counter *cnt)
+{
+ unsigned int num_entry;
+ struct lprocfs_counter *percpu_cntr;
+ int i;
+ unsigned long flags = 0;
+
+ memset(cnt, 0, sizeof(*cnt));
+
+ if (stats == NULL) {
+ /* set count to 1 to avoid divide-by-zero errs in callers */
+ cnt->lc_count = 1;
+ return;
+ }
+
+ cnt->lc_min = LC_MIN_INIT;
+
+ num_entry = lprocfs_stats_lock(stats, LPROCFS_GET_NUM_CPU, &flags);
+
+ for (i = 0; i < num_entry; i++) {
+ if (stats->ls_percpu[i] == NULL)
+ continue;
+ percpu_cntr = lprocfs_stats_counter_get(stats, i, idx);
+
+ cnt->lc_count += percpu_cntr->lc_count;
+ cnt->lc_sum += percpu_cntr->lc_sum;
+ if (percpu_cntr->lc_min < cnt->lc_min)
+ cnt->lc_min = percpu_cntr->lc_min;
+ if (percpu_cntr->lc_max > cnt->lc_max)
+ cnt->lc_max = percpu_cntr->lc_max;
+ cnt->lc_sumsquare += percpu_cntr->lc_sumsquare;
+ }
+
+ lprocfs_stats_unlock(stats, LPROCFS_GET_NUM_CPU, &flags);
+}
+EXPORT_SYMBOL(lprocfs_stats_collect);
+
+/**
+ * Append a space separated list of current set flags to str.
+ */
+#define flag2str(flag, first) \
+ do { \
+ if (imp->imp_##flag) \
+ seq_printf(m, "%s" #flag, first ? "" : ", "); \
+ } while (0)
+static int obd_import_flags2str(struct obd_import *imp, struct seq_file *m)
+{
+ bool first = true;
+
+ if (imp->imp_obd->obd_no_recov) {
+ seq_printf(m, "no_recov");
+ first = false;
+ }
+
+ flag2str(invalid, first);
+ first = false;
+ flag2str(deactive, first);
+ flag2str(replayable, first);
+ flag2str(pingable, first);
+ return 0;
+}
+#undef flags2str
+
+static void obd_connect_seq_flags2str(struct seq_file *m, __u64 flags, char *sep)
+{
+ __u64 mask = 1;
+ int i;
+ bool first = true;
+
+ for (i = 0; obd_connect_names[i] != NULL; i++, mask <<= 1) {
+ if (flags & mask) {
+ seq_printf(m, "%s%s",
+ first ? sep : "", obd_connect_names[i]);
+ first = false;
+ }
+ }
+ if (flags & ~(mask - 1))
+ seq_printf(m, "%sunknown flags %#llx",
+ first ? sep : "", flags & ~(mask - 1));
+}
+
+int lprocfs_rd_import(struct seq_file *m, void *data)
+{
+ struct lprocfs_counter ret;
+ struct lprocfs_counter_header *header;
+ struct obd_device *obd = (struct obd_device *)data;
+ struct obd_import *imp;
+ struct obd_import_conn *conn;
+ int j;
+ int k;
+ int rw = 0;
+
+ LASSERT(obd != NULL);
+ LPROCFS_CLIMP_CHECK(obd);
+ imp = obd->u.cli.cl_import;
+
+ seq_printf(m,
+ "import:\n"
+ " name: %s\n"
+ " target: %s\n"
+ " state: %s\n"
+ " instance: %u\n"
+ " connect_flags: [",
+ obd->obd_name,
+ obd2cli_tgt(obd),
+ ptlrpc_import_state_name(imp->imp_state),
+ imp->imp_connect_data.ocd_instance);
+ obd_connect_seq_flags2str(m, imp->imp_connect_data.ocd_connect_flags, ", ");
+ seq_printf(m,
+ "]\n"
+ " import_flags: [");
+ obd_import_flags2str(imp, m);
+
+ seq_printf(m,
+ "]\n"
+ " connection:\n"
+ " failover_nids: [");
+ spin_lock(&imp->imp_lock);
+ j = 0;
+ list_for_each_entry(conn, &imp->imp_conn_list, oic_item) {
+ seq_printf(m, "%s%s", j ? ", " : "",
+ libcfs_nid2str(conn->oic_conn->c_peer.nid));
+ j++;
+ }
+ seq_printf(m,
+ "]\n"
+ " current_connection: %s\n"
+ " connection_attempts: %u\n"
+ " generation: %u\n"
+ " in-progress_invalidations: %u\n",
+ imp->imp_connection == NULL ? "<none>" :
+ libcfs_nid2str(imp->imp_connection->c_peer.nid),
+ imp->imp_conn_cnt,
+ imp->imp_generation,
+ atomic_read(&imp->imp_inval_count));
+ spin_unlock(&imp->imp_lock);
+
+ if (obd->obd_svc_stats == NULL)
+ goto out_climp;
+
+ header = &obd->obd_svc_stats->ls_cnt_header[PTLRPC_REQWAIT_CNTR];
+ lprocfs_stats_collect(obd->obd_svc_stats, PTLRPC_REQWAIT_CNTR, &ret);
+ if (ret.lc_count != 0) {
+ /* first argument to do_div MUST be __u64 */
+ __u64 sum = ret.lc_sum;
+ do_div(sum, ret.lc_count);
+ ret.lc_sum = sum;
+ } else
+ ret.lc_sum = 0;
+ seq_printf(m,
+ " rpcs:\n"
+ " inflight: %u\n"
+ " unregistering: %u\n"
+ " timeouts: %u\n"
+ " avg_waittime: %llu %s\n",
+ atomic_read(&imp->imp_inflight),
+ atomic_read(&imp->imp_unregistering),
+ atomic_read(&imp->imp_timeouts),
+ ret.lc_sum, header->lc_units);
+
+ k = 0;
+ for (j = 0; j < IMP_AT_MAX_PORTALS; j++) {
+ if (imp->imp_at.iat_portal[j] == 0)
+ break;
+ k = max_t(unsigned int, k,
+ at_get(&imp->imp_at.iat_service_estimate[j]));
+ }
+ seq_printf(m,
+ " service_estimates:\n"
+ " services: %u sec\n"
+ " network: %u sec\n",
+ k,
+ at_get(&imp->imp_at.iat_net_latency));
+
+ seq_printf(m,
+ " transactions:\n"
+ " last_replay: %llu\n"
+ " peer_committed: %llu\n"
+ " last_checked: %llu\n",
+ imp->imp_last_replay_transno,
+ imp->imp_peer_committed_transno,
+ imp->imp_last_transno_checked);
+
+ /* avg data rates */
+ for (rw = 0; rw <= 1; rw++) {
+ lprocfs_stats_collect(obd->obd_svc_stats,
+ PTLRPC_LAST_CNTR + BRW_READ_BYTES + rw,
+ &ret);
+ if (ret.lc_sum > 0 && ret.lc_count > 0) {
+ /* first argument to do_div MUST be __u64 */
+ __u64 sum = ret.lc_sum;
+ do_div(sum, ret.lc_count);
+ ret.lc_sum = sum;
+ seq_printf(m,
+ " %s_data_averages:\n"
+ " bytes_per_rpc: %llu\n",
+ rw ? "write" : "read",
+ ret.lc_sum);
+ }
+ k = (int)ret.lc_sum;
+ j = opcode_offset(OST_READ + rw) + EXTRA_MAX_OPCODES;
+ header = &obd->obd_svc_stats->ls_cnt_header[j];
+ lprocfs_stats_collect(obd->obd_svc_stats, j, &ret);
+ if (ret.lc_sum > 0 && ret.lc_count != 0) {
+ /* first argument to do_div MUST be __u64 */
+ __u64 sum = ret.lc_sum;
+ do_div(sum, ret.lc_count);
+ ret.lc_sum = sum;
+ seq_printf(m,
+ " %s_per_rpc: %llu\n",
+ header->lc_units, ret.lc_sum);
+ j = (int)ret.lc_sum;
+ if (j > 0)
+ seq_printf(m,
+ " MB_per_sec: %u.%.02u\n",
+ k / j, (100 * k / j) % 100);
+ }
+ }
+
+out_climp:
+ LPROCFS_CLIMP_EXIT(obd);
+ return 0;
+}
+EXPORT_SYMBOL(lprocfs_rd_import);
+
+int lprocfs_rd_state(struct seq_file *m, void *data)
+{
+ struct obd_device *obd = (struct obd_device *)data;
+ struct obd_import *imp;
+ int j, k;
+
+ LASSERT(obd != NULL);
+ LPROCFS_CLIMP_CHECK(obd);
+ imp = obd->u.cli.cl_import;
+
+ seq_printf(m, "current_state: %s\n",
+ ptlrpc_import_state_name(imp->imp_state));
+ seq_printf(m, "state_history:\n");
+ k = imp->imp_state_hist_idx;
+ for (j = 0; j < IMP_STATE_HIST_LEN; j++) {
+ struct import_state_hist *ish =
+ &imp->imp_state_hist[(k + j) % IMP_STATE_HIST_LEN];
+ if (ish->ish_state == 0)
+ continue;
+ seq_printf(m, " - ["CFS_TIME_T", %s]\n",
+ ish->ish_time,
+ ptlrpc_import_state_name(ish->ish_state));
+ }
+
+ LPROCFS_CLIMP_EXIT(obd);
+ return 0;
+}
+EXPORT_SYMBOL(lprocfs_rd_state);
+
+int lprocfs_at_hist_helper(struct seq_file *m, struct adaptive_timeout *at)
+{
+ int i;
+ for (i = 0; i < AT_BINS; i++)
+ seq_printf(m, "%3u ", at->at_hist[i]);
+ seq_printf(m, "\n");
+ return 0;
+}
+EXPORT_SYMBOL(lprocfs_at_hist_helper);
+
+/* See also ptlrpc_lprocfs_rd_timeouts */
+int lprocfs_rd_timeouts(struct seq_file *m, void *data)
+{
+ struct obd_device *obd = (struct obd_device *)data;
+ struct obd_import *imp;
+ unsigned int cur, worst;
+ time_t now, worstt;
+ struct dhms ts;
+ int i;
+
+ LASSERT(obd != NULL);
+ LPROCFS_CLIMP_CHECK(obd);
+ imp = obd->u.cli.cl_import;
+
+ now = get_seconds();
+
+ /* Some network health info for kicks */
+ s2dhms(&ts, now - imp->imp_last_reply_time);
+ seq_printf(m, "%-10s : %ld, "DHMS_FMT" ago\n",
+ "last reply", imp->imp_last_reply_time, DHMS_VARS(&ts));
+
+ cur = at_get(&imp->imp_at.iat_net_latency);
+ worst = imp->imp_at.iat_net_latency.at_worst_ever;
+ worstt = imp->imp_at.iat_net_latency.at_worst_time;
+ s2dhms(&ts, now - worstt);
+ seq_printf(m, "%-10s : cur %3u worst %3u (at %ld, "DHMS_FMT" ago) ",
+ "network", cur, worst, worstt, DHMS_VARS(&ts));
+ lprocfs_at_hist_helper(m, &imp->imp_at.iat_net_latency);
+
+ for (i = 0; i < IMP_AT_MAX_PORTALS; i++) {
+ if (imp->imp_at.iat_portal[i] == 0)
+ break;
+ cur = at_get(&imp->imp_at.iat_service_estimate[i]);
+ worst = imp->imp_at.iat_service_estimate[i].at_worst_ever;
+ worstt = imp->imp_at.iat_service_estimate[i].at_worst_time;
+ s2dhms(&ts, now - worstt);
+ seq_printf(m, "portal %-2d : cur %3u worst %3u (at %ld, "
+ DHMS_FMT" ago) ", imp->imp_at.iat_portal[i],
+ cur, worst, worstt, DHMS_VARS(&ts));
+ lprocfs_at_hist_helper(m, &imp->imp_at.iat_service_estimate[i]);
+ }
+
+ LPROCFS_CLIMP_EXIT(obd);
+ return 0;
+}
+EXPORT_SYMBOL(lprocfs_rd_timeouts);
+
+int lprocfs_rd_connect_flags(struct seq_file *m, void *data)
+{
+ struct obd_device *obd = data;
+ __u64 flags;
+
+ LPROCFS_CLIMP_CHECK(obd);
+ flags = obd->u.cli.cl_import->imp_connect_data.ocd_connect_flags;
+ seq_printf(m, "flags=%#llx\n", flags);
+ obd_connect_seq_flags2str(m, flags, "\n");
+ seq_printf(m, "\n");
+ LPROCFS_CLIMP_EXIT(obd);
+ return 0;
+}
+EXPORT_SYMBOL(lprocfs_rd_connect_flags);
+
+int lprocfs_rd_num_exports(struct seq_file *m, void *data)
+{
+ struct obd_device *obd = data;
+
+ LASSERT(obd != NULL);
+ seq_printf(m, "%u\n", obd->obd_num_exports);
+ return 0;
+}
+EXPORT_SYMBOL(lprocfs_rd_num_exports);
+
+int lprocfs_rd_numrefs(struct seq_file *m, void *data)
+{
+ struct obd_type *class = (struct obd_type *) data;
+
+ LASSERT(class != NULL);
+ seq_printf(m, "%d\n", class->typ_refcnt);
+ return 0;
+}
+EXPORT_SYMBOL(lprocfs_rd_numrefs);
+
+int lprocfs_obd_setup(struct obd_device *obd, struct lprocfs_vars *list)
+{
+ int rc = 0;
+
+ LASSERT(obd != NULL);
+ LASSERT(obd->obd_magic == OBD_DEVICE_MAGIC);
+ LASSERT(obd->obd_type->typ_procroot != NULL);
+
+ obd->obd_proc_entry = lprocfs_register(obd->obd_name,
+ obd->obd_type->typ_procroot,
+ list, obd);
+ if (IS_ERR(obd->obd_proc_entry)) {
+ rc = PTR_ERR(obd->obd_proc_entry);
+ CERROR("error %d setting up lprocfs for %s\n",
+ rc, obd->obd_name);
+ obd->obd_proc_entry = NULL;
+ }
+ return rc;
+}
+EXPORT_SYMBOL(lprocfs_obd_setup);
+
+int lprocfs_obd_cleanup(struct obd_device *obd)
+{
+ if (!obd)
+ return -EINVAL;
+ if (obd->obd_proc_exports_entry) {
+ /* Should be no exports left */
+ lprocfs_remove(&obd->obd_proc_exports_entry);
+ obd->obd_proc_exports_entry = NULL;
+ }
+ if (obd->obd_proc_entry) {
+ lprocfs_remove(&obd->obd_proc_entry);
+ obd->obd_proc_entry = NULL;
+ }
+ return 0;
+}
+EXPORT_SYMBOL(lprocfs_obd_cleanup);
+
+static void lprocfs_free_client_stats(struct nid_stat *client_stat)
+{
+ CDEBUG(D_CONFIG, "stat %p - data %p/%p\n", client_stat,
+ client_stat->nid_proc, client_stat->nid_stats);
+
+ LASSERTF(atomic_read(&client_stat->nid_exp_ref_count) == 0,
+ "nid %s:count %d\n", libcfs_nid2str(client_stat->nid),
+ atomic_read(&client_stat->nid_exp_ref_count));
+
+ if (client_stat->nid_proc)
+ lprocfs_remove(&client_stat->nid_proc);
+
+ if (client_stat->nid_stats)
+ lprocfs_free_stats(&client_stat->nid_stats);
+
+ if (client_stat->nid_ldlm_stats)
+ lprocfs_free_stats(&client_stat->nid_ldlm_stats);
+
+ OBD_FREE_PTR(client_stat);
+ return;
+
+}
+
+void lprocfs_free_per_client_stats(struct obd_device *obd)
+{
+ struct cfs_hash *hash = obd->obd_nid_stats_hash;
+ struct nid_stat *stat;
+
+ /* we need extra list - because hash_exit called to early */
+ /* not need locking because all clients is died */
+ while (!list_empty(&obd->obd_nid_stats)) {
+ stat = list_entry(obd->obd_nid_stats.next,
+ struct nid_stat, nid_list);
+ list_del_init(&stat->nid_list);
+ cfs_hash_del(hash, &stat->nid, &stat->nid_hash);
+ lprocfs_free_client_stats(stat);
+ }
+}
+EXPORT_SYMBOL(lprocfs_free_per_client_stats);
+
+int lprocfs_stats_alloc_one(struct lprocfs_stats *stats, unsigned int cpuid)
+{
+ struct lprocfs_counter *cntr;
+ unsigned int percpusize;
+ int rc = -ENOMEM;
+ unsigned long flags = 0;
+ int i;
+
+ LASSERT(stats->ls_percpu[cpuid] == NULL);
+ LASSERT((stats->ls_flags & LPROCFS_STATS_FLAG_NOPERCPU) == 0);
+
+ percpusize = lprocfs_stats_counter_size(stats);
+ LIBCFS_ALLOC_ATOMIC(stats->ls_percpu[cpuid], percpusize);
+ if (stats->ls_percpu[cpuid] != NULL) {
+ rc = 0;
+ if (unlikely(stats->ls_biggest_alloc_num <= cpuid)) {
+ if (stats->ls_flags & LPROCFS_STATS_FLAG_IRQ_SAFE)
+ spin_lock_irqsave(&stats->ls_lock, flags);
+ else
+ spin_lock(&stats->ls_lock);
+ if (stats->ls_biggest_alloc_num <= cpuid)
+ stats->ls_biggest_alloc_num = cpuid + 1;
+ if (stats->ls_flags & LPROCFS_STATS_FLAG_IRQ_SAFE)
+ spin_unlock_irqrestore(&stats->ls_lock, flags);
+ else
+ spin_unlock(&stats->ls_lock);
+ }
+ /* initialize the ls_percpu[cpuid] non-zero counter */
+ for (i = 0; i < stats->ls_num; ++i) {
+ cntr = lprocfs_stats_counter_get(stats, cpuid, i);
+ cntr->lc_min = LC_MIN_INIT;
+ }
+ }
+ return rc;
+}
+EXPORT_SYMBOL(lprocfs_stats_alloc_one);
+
+struct lprocfs_stats *lprocfs_alloc_stats(unsigned int num,
+ enum lprocfs_stats_flags flags)
+{
+ struct lprocfs_stats *stats;
+ unsigned int num_entry;
+ unsigned int percpusize = 0;
+ int i;
+
+ if (num == 0)
+ return NULL;
+
+ if (lprocfs_no_percpu_stats != 0)
+ flags |= LPROCFS_STATS_FLAG_NOPERCPU;
+
+ if (flags & LPROCFS_STATS_FLAG_NOPERCPU)
+ num_entry = 1;
+ else
+ num_entry = num_possible_cpus();
+
+ /* alloc percpu pointers for all possible cpu slots */
+ LIBCFS_ALLOC(stats, offsetof(typeof(*stats), ls_percpu[num_entry]));
+ if (stats == NULL)
+ return NULL;
+
+ stats->ls_num = num;
+ stats->ls_flags = flags;
+ spin_lock_init(&stats->ls_lock);
+
+ /* alloc num of counter headers */
+ LIBCFS_ALLOC(stats->ls_cnt_header,
+ stats->ls_num * sizeof(struct lprocfs_counter_header));
+ if (stats->ls_cnt_header == NULL)
+ goto fail;
+
+ if ((flags & LPROCFS_STATS_FLAG_NOPERCPU) != 0) {
+ /* contains only one set counters */
+ percpusize = lprocfs_stats_counter_size(stats);
+ LIBCFS_ALLOC_ATOMIC(stats->ls_percpu[0], percpusize);
+ if (stats->ls_percpu[0] == NULL)
+ goto fail;
+ stats->ls_biggest_alloc_num = 1;
+ } else if ((flags & LPROCFS_STATS_FLAG_IRQ_SAFE) != 0) {
+ /* alloc all percpu data, currently only obd_memory use this */
+ for (i = 0; i < num_entry; ++i)
+ if (lprocfs_stats_alloc_one(stats, i) < 0)
+ goto fail;
+ }
+
+ return stats;
+
+fail:
+ lprocfs_free_stats(&stats);
+ return NULL;
+}
+EXPORT_SYMBOL(lprocfs_alloc_stats);
+
+void lprocfs_free_stats(struct lprocfs_stats **statsh)
+{
+ struct lprocfs_stats *stats = *statsh;
+ unsigned int num_entry;
+ unsigned int percpusize;
+ unsigned int i;
+
+ if (stats == NULL || stats->ls_num == 0)
+ return;
+ *statsh = NULL;
+
+ if (stats->ls_flags & LPROCFS_STATS_FLAG_NOPERCPU)
+ num_entry = 1;
+ else
+ num_entry = num_possible_cpus();
+
+ percpusize = lprocfs_stats_counter_size(stats);
+ for (i = 0; i < num_entry; i++)
+ if (stats->ls_percpu[i] != NULL)
+ LIBCFS_FREE(stats->ls_percpu[i], percpusize);
+ if (stats->ls_cnt_header != NULL)
+ LIBCFS_FREE(stats->ls_cnt_header, stats->ls_num *
+ sizeof(struct lprocfs_counter_header));
+ LIBCFS_FREE(stats, offsetof(typeof(*stats), ls_percpu[num_entry]));
+}
+EXPORT_SYMBOL(lprocfs_free_stats);
+
+void lprocfs_clear_stats(struct lprocfs_stats *stats)
+{
+ struct lprocfs_counter *percpu_cntr;
+ int i;
+ int j;
+ unsigned int num_entry;
+ unsigned long flags = 0;
+
+ num_entry = lprocfs_stats_lock(stats, LPROCFS_GET_NUM_CPU, &flags);
+
+ for (i = 0; i < num_entry; i++) {
+ if (stats->ls_percpu[i] == NULL)
+ continue;
+ for (j = 0; j < stats->ls_num; j++) {
+ percpu_cntr = lprocfs_stats_counter_get(stats, i, j);
+ percpu_cntr->lc_count = 0;
+ percpu_cntr->lc_min = LC_MIN_INIT;
+ percpu_cntr->lc_max = 0;
+ percpu_cntr->lc_sumsquare = 0;
+ percpu_cntr->lc_sum = 0;
+ if (stats->ls_flags & LPROCFS_STATS_FLAG_IRQ_SAFE)
+ percpu_cntr->lc_sum_irq = 0;
+ }
+ }
+
+ lprocfs_stats_unlock(stats, LPROCFS_GET_NUM_CPU, &flags);
+}
+EXPORT_SYMBOL(lprocfs_clear_stats);
+
+static ssize_t lprocfs_stats_seq_write(struct file *file,
+ const char __user *buf,
+ size_t len, loff_t *off)
+{
+ struct seq_file *seq = file->private_data;
+ struct lprocfs_stats *stats = seq->private;
+
+ lprocfs_clear_stats(stats);
+
+ return len;
+}
+
+static void *lprocfs_stats_seq_start(struct seq_file *p, loff_t *pos)
+{
+ struct lprocfs_stats *stats = p->private;
+
+ return (*pos < stats->ls_num) ? pos : NULL;
+}
+
+static void lprocfs_stats_seq_stop(struct seq_file *p, void *v)
+{
+}
+
+static void *lprocfs_stats_seq_next(struct seq_file *p, void *v, loff_t *pos)
+{
+ (*pos)++;
+ return lprocfs_stats_seq_start(p, pos);
+}
+
+/* seq file export of one lprocfs counter */
+static int lprocfs_stats_seq_show(struct seq_file *p, void *v)
+{
+ struct lprocfs_stats *stats = p->private;
+ struct lprocfs_counter_header *hdr;
+ struct lprocfs_counter ctr;
+ int idx = *(loff_t *)v;
+
+ if (idx == 0) {
+ struct timeval now;
+ do_gettimeofday(&now);
+ seq_printf(p, "%-25s %lu.%lu secs.usecs\n",
+ "snapshot_time",
+ now.tv_sec, (unsigned long)now.tv_usec);
+ }
+
+ hdr = &stats->ls_cnt_header[idx];
+ lprocfs_stats_collect(stats, idx, &ctr);
+
+ if (ctr.lc_count != 0) {
+ seq_printf(p, "%-25s %lld samples [%s]",
+ hdr->lc_name, ctr.lc_count, hdr->lc_units);
+
+ if ((hdr->lc_config & LPROCFS_CNTR_AVGMINMAX) &&
+ (ctr.lc_count > 0)) {
+ seq_printf(p, " %lld %lld %lld",
+ ctr.lc_min, ctr.lc_max, ctr.lc_sum);
+ if (hdr->lc_config & LPROCFS_CNTR_STDDEV)
+ seq_printf(p, " %lld", ctr.lc_sumsquare);
+ }
+ seq_putc(p, '\n');
+ }
+
+ return 0;
+}
+
+static const struct seq_operations lprocfs_stats_seq_sops = {
+ .start = lprocfs_stats_seq_start,
+ .stop = lprocfs_stats_seq_stop,
+ .next = lprocfs_stats_seq_next,
+ .show = lprocfs_stats_seq_show,
+};
+
+static int lprocfs_stats_seq_open(struct inode *inode, struct file *file)
+{
+ struct seq_file *seq;
+ int rc;
+
+ rc = seq_open(file, &lprocfs_stats_seq_sops);
+ if (rc)
+ return rc;
+ seq = file->private_data;
+ seq->private = PDE_DATA(inode);
+ return 0;
+}
+
+struct file_operations lprocfs_stats_seq_fops = {
+ .owner = THIS_MODULE,
+ .open = lprocfs_stats_seq_open,
+ .read = seq_read,
+ .write = lprocfs_stats_seq_write,
+ .llseek = seq_lseek,
+ .release = lprocfs_seq_release,
+};
+
+int lprocfs_register_stats(struct proc_dir_entry *root, const char *name,
+ struct lprocfs_stats *stats)
+{
+ struct proc_dir_entry *entry;
+ LASSERT(root != NULL);
+
+ entry = proc_create_data(name, 0644, root,
+ &lprocfs_stats_seq_fops, stats);
+ if (entry == NULL)
+ return -ENOMEM;
+
+ return 0;
+}
+EXPORT_SYMBOL(lprocfs_register_stats);
+
+void lprocfs_counter_init(struct lprocfs_stats *stats, int index,
+ unsigned conf, const char *name, const char *units)
+{
+ struct lprocfs_counter_header *header;
+ struct lprocfs_counter *percpu_cntr;
+ unsigned long flags = 0;
+ unsigned int i;
+ unsigned int num_cpu;
+
+ LASSERT(stats != NULL);
+
+ header = &stats->ls_cnt_header[index];
+ LASSERTF(header != NULL, "Failed to allocate stats header:[%d]%s/%s\n",
+ index, name, units);
+
+ header->lc_config = conf;
+ header->lc_name = name;
+ header->lc_units = units;
+
+ num_cpu = lprocfs_stats_lock(stats, LPROCFS_GET_NUM_CPU, &flags);
+ for (i = 0; i < num_cpu; ++i) {
+ if (stats->ls_percpu[i] == NULL)
+ continue;
+ percpu_cntr = lprocfs_stats_counter_get(stats, i, index);
+ percpu_cntr->lc_count = 0;
+ percpu_cntr->lc_min = LC_MIN_INIT;
+ percpu_cntr->lc_max = 0;
+ percpu_cntr->lc_sumsquare = 0;
+ percpu_cntr->lc_sum = 0;
+ if ((stats->ls_flags & LPROCFS_STATS_FLAG_IRQ_SAFE) != 0)
+ percpu_cntr->lc_sum_irq = 0;
+ }
+ lprocfs_stats_unlock(stats, LPROCFS_GET_NUM_CPU, &flags);
+}
+EXPORT_SYMBOL(lprocfs_counter_init);
+
+#define LPROCFS_OBD_OP_INIT(base, stats, op) \
+do { \
+ unsigned int coffset = base + OBD_COUNTER_OFFSET(op); \
+ LASSERT(coffset < stats->ls_num); \
+ lprocfs_counter_init(stats, coffset, 0, #op, "reqs"); \
+} while (0)
+
+void lprocfs_init_ops_stats(int num_private_stats, struct lprocfs_stats *stats)
+{
+ LPROCFS_OBD_OP_INIT(num_private_stats, stats, iocontrol);
+ LPROCFS_OBD_OP_INIT(num_private_stats, stats, get_info);
+ LPROCFS_OBD_OP_INIT(num_private_stats, stats, set_info_async);
+ LPROCFS_OBD_OP_INIT(num_private_stats, stats, attach);
+ LPROCFS_OBD_OP_INIT(num_private_stats, stats, detach);
+ LPROCFS_OBD_OP_INIT(num_private_stats, stats, setup);
+ LPROCFS_OBD_OP_INIT(num_private_stats, stats, precleanup);
+ LPROCFS_OBD_OP_INIT(num_private_stats, stats, cleanup);
+ LPROCFS_OBD_OP_INIT(num_private_stats, stats, process_config);
+ LPROCFS_OBD_OP_INIT(num_private_stats, stats, postrecov);
+ LPROCFS_OBD_OP_INIT(num_private_stats, stats, add_conn);
+ LPROCFS_OBD_OP_INIT(num_private_stats, stats, del_conn);
+ LPROCFS_OBD_OP_INIT(num_private_stats, stats, connect);
+ LPROCFS_OBD_OP_INIT(num_private_stats, stats, reconnect);
+ LPROCFS_OBD_OP_INIT(num_private_stats, stats, disconnect);
+ LPROCFS_OBD_OP_INIT(num_private_stats, stats, fid_init);
+ LPROCFS_OBD_OP_INIT(num_private_stats, stats, fid_fini);
+ LPROCFS_OBD_OP_INIT(num_private_stats, stats, fid_alloc);
+ LPROCFS_OBD_OP_INIT(num_private_stats, stats, statfs);
+ LPROCFS_OBD_OP_INIT(num_private_stats, stats, statfs_async);
+ LPROCFS_OBD_OP_INIT(num_private_stats, stats, packmd);
+ LPROCFS_OBD_OP_INIT(num_private_stats, stats, unpackmd);
+ LPROCFS_OBD_OP_INIT(num_private_stats, stats, preallocate);
+ LPROCFS_OBD_OP_INIT(num_private_stats, stats, create);
+ LPROCFS_OBD_OP_INIT(num_private_stats, stats, destroy);
+ LPROCFS_OBD_OP_INIT(num_private_stats, stats, setattr);
+ LPROCFS_OBD_OP_INIT(num_private_stats, stats, setattr_async);
+ LPROCFS_OBD_OP_INIT(num_private_stats, stats, getattr);
+ LPROCFS_OBD_OP_INIT(num_private_stats, stats, getattr_async);
+ LPROCFS_OBD_OP_INIT(num_private_stats, stats, adjust_kms);
+ LPROCFS_OBD_OP_INIT(num_private_stats, stats, preprw);
+ LPROCFS_OBD_OP_INIT(num_private_stats, stats, commitrw);
+ LPROCFS_OBD_OP_INIT(num_private_stats, stats, find_cbdata);
+ LPROCFS_OBD_OP_INIT(num_private_stats, stats, init_export);
+ LPROCFS_OBD_OP_INIT(num_private_stats, stats, destroy_export);
+ LPROCFS_OBD_OP_INIT(num_private_stats, stats, import_event);
+ LPROCFS_OBD_OP_INIT(num_private_stats, stats, notify);
+ LPROCFS_OBD_OP_INIT(num_private_stats, stats, health_check);
+ LPROCFS_OBD_OP_INIT(num_private_stats, stats, get_uuid);
+ LPROCFS_OBD_OP_INIT(num_private_stats, stats, quotacheck);
+ LPROCFS_OBD_OP_INIT(num_private_stats, stats, quotactl);
+ LPROCFS_OBD_OP_INIT(num_private_stats, stats, pool_new);
+ LPROCFS_OBD_OP_INIT(num_private_stats, stats, pool_rem);
+ LPROCFS_OBD_OP_INIT(num_private_stats, stats, pool_add);
+ LPROCFS_OBD_OP_INIT(num_private_stats, stats, pool_del);
+ LPROCFS_OBD_OP_INIT(num_private_stats, stats, getref);
+ LPROCFS_OBD_OP_INIT(num_private_stats, stats, putref);
+}
+EXPORT_SYMBOL(lprocfs_init_ops_stats);
+
+int lprocfs_alloc_obd_stats(struct obd_device *obd, unsigned num_private_stats)
+{
+ struct lprocfs_stats *stats;
+ unsigned int num_stats;
+ int rc, i;
+
+ LASSERT(obd->obd_stats == NULL);
+ LASSERT(obd->obd_proc_entry != NULL);
+ LASSERT(obd->obd_cntr_base == 0);
+
+ num_stats = ((int)sizeof(*obd->obd_type->typ_dt_ops) / sizeof(void *)) +
+ num_private_stats - 1 /* o_owner */;
+ stats = lprocfs_alloc_stats(num_stats, 0);
+ if (stats == NULL)
+ return -ENOMEM;
+
+ lprocfs_init_ops_stats(num_private_stats, stats);
+
+ for (i = num_private_stats; i < num_stats; i++) {
+ /* If this LBUGs, it is likely that an obd
+ * operation was added to struct obd_ops in
+ * <obd.h>, and that the corresponding line item
+ * LPROCFS_OBD_OP_INIT(.., .., opname)
+ * is missing from the list above. */
+ LASSERTF(stats->ls_cnt_header[i].lc_name != NULL,
+ "Missing obd_stat initializer obd_op operation at offset %d.\n",
+ i - num_private_stats);
+ }
+ rc = lprocfs_register_stats(obd->obd_proc_entry, "stats", stats);
+ if (rc < 0) {
+ lprocfs_free_stats(&stats);
+ } else {
+ obd->obd_stats = stats;
+ obd->obd_cntr_base = num_private_stats;
+ }
+ return rc;
+}
+EXPORT_SYMBOL(lprocfs_alloc_obd_stats);
+
+void lprocfs_free_obd_stats(struct obd_device *obd)
+{
+ if (obd->obd_stats)
+ lprocfs_free_stats(&obd->obd_stats);
+}
+EXPORT_SYMBOL(lprocfs_free_obd_stats);
+
+#define LPROCFS_MD_OP_INIT(base, stats, op) \
+do { \
+ unsigned int coffset = base + MD_COUNTER_OFFSET(op); \
+ LASSERT(coffset < stats->ls_num); \
+ lprocfs_counter_init(stats, coffset, 0, #op, "reqs"); \
+} while (0)
+
+void lprocfs_init_mps_stats(int num_private_stats, struct lprocfs_stats *stats)
+{
+ LPROCFS_MD_OP_INIT(num_private_stats, stats, getstatus);
+ LPROCFS_MD_OP_INIT(num_private_stats, stats, null_inode);
+ LPROCFS_MD_OP_INIT(num_private_stats, stats, find_cbdata);
+ LPROCFS_MD_OP_INIT(num_private_stats, stats, close);
+ LPROCFS_MD_OP_INIT(num_private_stats, stats, create);
+ LPROCFS_MD_OP_INIT(num_private_stats, stats, done_writing);
+ LPROCFS_MD_OP_INIT(num_private_stats, stats, enqueue);
+ LPROCFS_MD_OP_INIT(num_private_stats, stats, getattr);
+ LPROCFS_MD_OP_INIT(num_private_stats, stats, getattr_name);
+ LPROCFS_MD_OP_INIT(num_private_stats, stats, intent_lock);
+ LPROCFS_MD_OP_INIT(num_private_stats, stats, link);
+ LPROCFS_MD_OP_INIT(num_private_stats, stats, rename);
+ LPROCFS_MD_OP_INIT(num_private_stats, stats, is_subdir);
+ LPROCFS_MD_OP_INIT(num_private_stats, stats, setattr);
+ LPROCFS_MD_OP_INIT(num_private_stats, stats, sync);
+ LPROCFS_MD_OP_INIT(num_private_stats, stats, readpage);
+ LPROCFS_MD_OP_INIT(num_private_stats, stats, unlink);
+ LPROCFS_MD_OP_INIT(num_private_stats, stats, setxattr);
+ LPROCFS_MD_OP_INIT(num_private_stats, stats, getxattr);
+ LPROCFS_MD_OP_INIT(num_private_stats, stats, init_ea_size);
+ LPROCFS_MD_OP_INIT(num_private_stats, stats, get_lustre_md);
+ LPROCFS_MD_OP_INIT(num_private_stats, stats, free_lustre_md);
+ LPROCFS_MD_OP_INIT(num_private_stats, stats, set_open_replay_data);
+ LPROCFS_MD_OP_INIT(num_private_stats, stats, clear_open_replay_data);
+ LPROCFS_MD_OP_INIT(num_private_stats, stats, set_lock_data);
+ LPROCFS_MD_OP_INIT(num_private_stats, stats, lock_match);
+ LPROCFS_MD_OP_INIT(num_private_stats, stats, cancel_unused);
+ LPROCFS_MD_OP_INIT(num_private_stats, stats, renew_capa);
+ LPROCFS_MD_OP_INIT(num_private_stats, stats, unpack_capa);
+ LPROCFS_MD_OP_INIT(num_private_stats, stats, get_remote_perm);
+ LPROCFS_MD_OP_INIT(num_private_stats, stats, intent_getattr_async);
+ LPROCFS_MD_OP_INIT(num_private_stats, stats, revalidate_lock);
+}
+EXPORT_SYMBOL(lprocfs_init_mps_stats);
+
+int lprocfs_alloc_md_stats(struct obd_device *obd,
+ unsigned num_private_stats)
+{
+ struct lprocfs_stats *stats;
+ unsigned int num_stats;
+ int rc, i;
+
+ LASSERT(obd->md_stats == NULL);
+ LASSERT(obd->obd_proc_entry != NULL);
+ LASSERT(obd->md_cntr_base == 0);
+
+ num_stats = 1 + MD_COUNTER_OFFSET(revalidate_lock) +
+ num_private_stats;
+ stats = lprocfs_alloc_stats(num_stats, 0);
+ if (stats == NULL)
+ return -ENOMEM;
+
+ lprocfs_init_mps_stats(num_private_stats, stats);
+
+ for (i = num_private_stats; i < num_stats; i++) {
+ if (stats->ls_cnt_header[i].lc_name == NULL) {
+ CERROR("Missing md_stat initializer md_op operation at offset %d. Aborting.\n",
+ i - num_private_stats);
+ LBUG();
+ }
+ }
+ rc = lprocfs_register_stats(obd->obd_proc_entry, "md_stats", stats);
+ if (rc < 0) {
+ lprocfs_free_stats(&stats);
+ } else {
+ obd->md_stats = stats;
+ obd->md_cntr_base = num_private_stats;
+ }
+ return rc;
+}
+EXPORT_SYMBOL(lprocfs_alloc_md_stats);
+
+void lprocfs_free_md_stats(struct obd_device *obd)
+{
+ struct lprocfs_stats *stats = obd->md_stats;
+
+ if (stats != NULL) {
+ obd->md_stats = NULL;
+ obd->md_cntr_base = 0;
+ lprocfs_free_stats(&stats);
+ }
+}
+EXPORT_SYMBOL(lprocfs_free_md_stats);
+
+void lprocfs_init_ldlm_stats(struct lprocfs_stats *ldlm_stats)
+{
+ lprocfs_counter_init(ldlm_stats,
+ LDLM_ENQUEUE - LDLM_FIRST_OPC,
+ 0, "ldlm_enqueue", "reqs");
+ lprocfs_counter_init(ldlm_stats,
+ LDLM_CONVERT - LDLM_FIRST_OPC,
+ 0, "ldlm_convert", "reqs");
+ lprocfs_counter_init(ldlm_stats,
+ LDLM_CANCEL - LDLM_FIRST_OPC,
+ 0, "ldlm_cancel", "reqs");
+ lprocfs_counter_init(ldlm_stats,
+ LDLM_BL_CALLBACK - LDLM_FIRST_OPC,
+ 0, "ldlm_bl_callback", "reqs");
+ lprocfs_counter_init(ldlm_stats,
+ LDLM_CP_CALLBACK - LDLM_FIRST_OPC,
+ 0, "ldlm_cp_callback", "reqs");
+ lprocfs_counter_init(ldlm_stats,
+ LDLM_GL_CALLBACK - LDLM_FIRST_OPC,
+ 0, "ldlm_gl_callback", "reqs");
+}
+EXPORT_SYMBOL(lprocfs_init_ldlm_stats);
+
+int lprocfs_exp_print_uuid(struct cfs_hash *hs, struct cfs_hash_bd *bd,
+ struct hlist_node *hnode, void *data)
+
+{
+ struct obd_export *exp = cfs_hash_object(hs, hnode);
+ struct seq_file *m = (struct seq_file *)data;
+
+ if (exp->exp_nid_stats)
+ seq_printf(m, "%s\n", obd_uuid2str(&exp->exp_client_uuid));
+
+ return 0;
+}
+
+static int
+lproc_exp_uuid_seq_show(struct seq_file *m, void *unused)
+{
+ struct nid_stat *stats = (struct nid_stat *)m->private;
+ struct obd_device *obd = stats->nid_obd;
+
+ cfs_hash_for_each_key(obd->obd_nid_hash, &stats->nid,
+ lprocfs_exp_print_uuid, m);
+ return 0;
+}
+
+LPROC_SEQ_FOPS_RO(lproc_exp_uuid);
+
+struct exp_hash_cb_data {
+ struct seq_file *m;
+ bool first;
+};
+
+int lprocfs_exp_print_hash(struct cfs_hash *hs, struct cfs_hash_bd *bd,
+ struct hlist_node *hnode, void *cb_data)
+
+{
+ struct exp_hash_cb_data *data = (struct exp_hash_cb_data *)cb_data;
+ struct obd_export *exp = cfs_hash_object(hs, hnode);
+
+ if (exp->exp_lock_hash != NULL) {
+ if (data->first) {
+ cfs_hash_debug_header(data->m);
+ data->first = false;
+ }
+ cfs_hash_debug_str(hs, data->m);
+ }
+
+ return 0;
+}
+
+static int
+lproc_exp_hash_seq_show(struct seq_file *m, void *unused)
+{
+ struct nid_stat *stats = (struct nid_stat *)m->private;
+ struct obd_device *obd = stats->nid_obd;
+ struct exp_hash_cb_data cb_data = {
+ .m = m,
+ .first = true
+ };
+
+ cfs_hash_for_each_key(obd->obd_nid_hash, &stats->nid,
+ lprocfs_exp_print_hash, &cb_data);
+ return 0;
+}
+
+LPROC_SEQ_FOPS_RO(lproc_exp_hash);
+
+int lprocfs_nid_stats_clear_read(struct seq_file *m, void *data)
+{
+ seq_printf(m, "%s\n",
+ "Write into this file to clear all nid stats and stale nid entries");
+ return 0;
+}
+EXPORT_SYMBOL(lprocfs_nid_stats_clear_read);
+
+static int lprocfs_nid_stats_clear_write_cb(void *obj, void *data)
+{
+ struct nid_stat *stat = obj;
+
+ CDEBUG(D_INFO, "refcnt %d\n", atomic_read(&stat->nid_exp_ref_count));
+ if (atomic_read(&stat->nid_exp_ref_count) == 1) {
+ /* object has only hash references. */
+ spin_lock(&stat->nid_obd->obd_nid_lock);
+ list_move(&stat->nid_list, data);
+ spin_unlock(&stat->nid_obd->obd_nid_lock);
+ return 1;
+ }
+ /* we has reference to object - only clear data*/
+ if (stat->nid_stats)
+ lprocfs_clear_stats(stat->nid_stats);
+
+ return 0;
+}
+
+int lprocfs_nid_stats_clear_write(struct file *file, const char *buffer,
+ unsigned long count, void *data)
+{
+ struct obd_device *obd = (struct obd_device *)data;
+ struct nid_stat *client_stat;
+ LIST_HEAD(free_list);
+
+ cfs_hash_cond_del(obd->obd_nid_stats_hash,
+ lprocfs_nid_stats_clear_write_cb, &free_list);
+
+ while (!list_empty(&free_list)) {
+ client_stat = list_entry(free_list.next, struct nid_stat,
+ nid_list);
+ list_del_init(&client_stat->nid_list);
+ lprocfs_free_client_stats(client_stat);
+ }
+
+ return count;
+}
+EXPORT_SYMBOL(lprocfs_nid_stats_clear_write);
+
+int lprocfs_exp_setup(struct obd_export *exp, lnet_nid_t *nid, int *newnid)
+{
+ struct nid_stat *new_stat, *old_stat;
+ struct obd_device *obd = NULL;
+ struct proc_dir_entry *entry;
+ char *buffer = NULL;
+ int rc = 0;
+
+ *newnid = 0;
+
+ if (!exp || !exp->exp_obd || !exp->exp_obd->obd_proc_exports_entry ||
+ !exp->exp_obd->obd_nid_stats_hash)
+ return -EINVAL;
+
+ /* not test against zero because eric say:
+ * You may only test nid against another nid, or LNET_NID_ANY.
+ * Anything else is nonsense.*/
+ if (!nid || *nid == LNET_NID_ANY)
+ return 0;
+
+ obd = exp->exp_obd;
+
+ CDEBUG(D_CONFIG, "using hash %p\n", obd->obd_nid_stats_hash);
+
+ OBD_ALLOC_PTR(new_stat);
+ if (new_stat == NULL)
+ return -ENOMEM;
+
+ new_stat->nid = *nid;
+ new_stat->nid_obd = exp->exp_obd;
+ /* we need set default refcount to 1 to balance obd_disconnect */
+ atomic_set(&new_stat->nid_exp_ref_count, 1);
+
+ old_stat = cfs_hash_findadd_unique(obd->obd_nid_stats_hash,
+ nid, &new_stat->nid_hash);
+ CDEBUG(D_INFO, "Found stats %p for nid %s - ref %d\n",
+ old_stat, libcfs_nid2str(*nid),
+ atomic_read(&new_stat->nid_exp_ref_count));
+
+ /* We need to release old stats because lprocfs_exp_cleanup() hasn't
+ * been and will never be called. */
+ if (exp->exp_nid_stats) {
+ nidstat_putref(exp->exp_nid_stats);
+ exp->exp_nid_stats = NULL;
+ }
+
+ /* Return -EALREADY here so that we know that the /proc
+ * entry already has been created */
+ if (old_stat != new_stat) {
+ exp->exp_nid_stats = old_stat;
+ rc = -EALREADY;
+ goto destroy_new;
+ }
+ /* not found - create */
+ OBD_ALLOC(buffer, LNET_NIDSTR_SIZE);
+ if (buffer == NULL) {
+ rc = -ENOMEM;
+ goto destroy_new;
+ }
+
+ memcpy(buffer, libcfs_nid2str(*nid), LNET_NIDSTR_SIZE);
+ new_stat->nid_proc = lprocfs_register(buffer,
+ obd->obd_proc_exports_entry,
+ NULL, NULL);
+ OBD_FREE(buffer, LNET_NIDSTR_SIZE);
+
+ if (IS_ERR(new_stat->nid_proc)) {
+ CERROR("Error making export directory for nid %s\n",
+ libcfs_nid2str(*nid));
+ rc = PTR_ERR(new_stat->nid_proc);
+ new_stat->nid_proc = NULL;
+ goto destroy_new_ns;
+ }
+
+ entry = lprocfs_add_simple(new_stat->nid_proc, "uuid",
+ new_stat, &lproc_exp_uuid_fops);
+ if (IS_ERR(entry)) {
+ CWARN("Error adding the NID stats file\n");
+ rc = PTR_ERR(entry);
+ goto destroy_new_ns;
+ }
+
+ entry = lprocfs_add_simple(new_stat->nid_proc, "hash",
+ new_stat, &lproc_exp_hash_fops);
+ if (IS_ERR(entry)) {
+ CWARN("Error adding the hash file\n");
+ rc = PTR_ERR(entry);
+ goto destroy_new_ns;
+ }
+
+ exp->exp_nid_stats = new_stat;
+ *newnid = 1;
+ /* protect competitive add to list, not need locking on destroy */
+ spin_lock(&obd->obd_nid_lock);
+ list_add(&new_stat->nid_list, &obd->obd_nid_stats);
+ spin_unlock(&obd->obd_nid_lock);
+
+ return rc;
+
+destroy_new_ns:
+ if (new_stat->nid_proc != NULL)
+ lprocfs_remove(&new_stat->nid_proc);
+ cfs_hash_del(obd->obd_nid_stats_hash, nid, &new_stat->nid_hash);
+
+destroy_new:
+ nidstat_putref(new_stat);
+ OBD_FREE_PTR(new_stat);
+ return rc;
+}
+EXPORT_SYMBOL(lprocfs_exp_setup);
+
+int lprocfs_exp_cleanup(struct obd_export *exp)
+{
+ struct nid_stat *stat = exp->exp_nid_stats;
+
+ if (!stat || !exp->exp_obd)
+ return 0;
+
+ nidstat_putref(exp->exp_nid_stats);
+ exp->exp_nid_stats = NULL;
+
+ return 0;
+}
+EXPORT_SYMBOL(lprocfs_exp_cleanup);
+
+__s64 lprocfs_read_helper(struct lprocfs_counter *lc,
+ struct lprocfs_counter_header *header,
+ enum lprocfs_stats_flags flags,
+ enum lprocfs_fields_flags field)
+{
+ __s64 ret = 0;
+
+ if (lc == NULL || header == NULL)
+ return 0;
+
+ switch (field) {
+ case LPROCFS_FIELDS_FLAGS_CONFIG:
+ ret = header->lc_config;
+ break;
+ case LPROCFS_FIELDS_FLAGS_SUM:
+ ret = lc->lc_sum;
+ if ((flags & LPROCFS_STATS_FLAG_IRQ_SAFE) != 0)
+ ret += lc->lc_sum_irq;
+ break;
+ case LPROCFS_FIELDS_FLAGS_MIN:
+ ret = lc->lc_min;
+ break;
+ case LPROCFS_FIELDS_FLAGS_MAX:
+ ret = lc->lc_max;
+ break;
+ case LPROCFS_FIELDS_FLAGS_AVG:
+ ret = (lc->lc_max - lc->lc_min) / 2;
+ break;
+ case LPROCFS_FIELDS_FLAGS_SUMSQUARE:
+ ret = lc->lc_sumsquare;
+ break;
+ case LPROCFS_FIELDS_FLAGS_COUNT:
+ ret = lc->lc_count;
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL(lprocfs_read_helper);
+
+int lprocfs_write_helper(const char __user *buffer, unsigned long count,
+ int *val)
+{
+ return lprocfs_write_frac_helper(buffer, count, val, 1);
+}
+EXPORT_SYMBOL(lprocfs_write_helper);
+
+int lprocfs_seq_read_frac_helper(struct seq_file *m, long val, int mult)
+{
+ long decimal_val, frac_val;
+
+ decimal_val = val / mult;
+ seq_printf(m, "%ld", decimal_val);
+ frac_val = val % mult;
+
+ if (frac_val > 0) {
+ frac_val *= 100;
+ frac_val /= mult;
+ }
+ if (frac_val > 0) {
+ /* Three cases: x0, xx, 0x */
+ if ((frac_val % 10) != 0)
+ seq_printf(m, ".%ld", frac_val);
+ else
+ seq_printf(m, ".%ld", frac_val / 10);
+ }
+
+ seq_printf(m, "\n");
+ return 0;
+}
+EXPORT_SYMBOL(lprocfs_seq_read_frac_helper);
+
+int lprocfs_write_u64_helper(const char __user *buffer, unsigned long count,
+ __u64 *val)
+{
+ return lprocfs_write_frac_u64_helper(buffer, count, val, 1);
+}
+EXPORT_SYMBOL(lprocfs_write_u64_helper);
+
+int lprocfs_write_frac_u64_helper(const char *buffer, unsigned long count,
+ __u64 *val, int mult)
+{
+ char kernbuf[22], *end, *pbuf;
+ __u64 whole, frac = 0, units;
+ unsigned frac_d = 1;
+ int sign = 1;
+
+ if (count > (sizeof(kernbuf) - 1))
+ return -EINVAL;
+
+ if (copy_from_user(kernbuf, buffer, count))
+ return -EFAULT;
+
+ kernbuf[count] = '\0';
+ pbuf = kernbuf;
+ if (*pbuf == '-') {
+ sign = -1;
+ pbuf++;
+ }
+
+ whole = simple_strtoull(pbuf, &end, 10);
+ if (pbuf == end)
+ return -EINVAL;
+
+ if (*end == '.') {
+ int i;
+ pbuf = end + 1;
+
+ /* need to limit frac_d to a __u32 */
+ if (strlen(pbuf) > 10)
+ pbuf[10] = '\0';
+
+ frac = simple_strtoull(pbuf, &end, 10);
+ /* count decimal places */
+ for (i = 0; i < (end - pbuf); i++)
+ frac_d *= 10;
+ }
+
+ units = 1;
+ switch (tolower(*end)) {
+ case 'p':
+ units <<= 10;
+ case 't':
+ units <<= 10;
+ case 'g':
+ units <<= 10;
+ case 'm':
+ units <<= 10;
+ case 'k':
+ units <<= 10;
+ }
+ /* Specified units override the multiplier */
+ if (units > 1)
+ mult = units;
+
+ frac *= mult;
+ do_div(frac, frac_d);
+ *val = sign * (whole * mult + frac);
+ return 0;
+}
+EXPORT_SYMBOL(lprocfs_write_frac_u64_helper);
+
+static char *lprocfs_strnstr(const char *s1, const char *s2, size_t len)
+{
+ size_t l2;
+
+ l2 = strlen(s2);
+ if (!l2)
+ return (char *)s1;
+ while (len >= l2) {
+ len--;
+ if (!memcmp(s1, s2, l2))
+ return (char *)s1;
+ s1++;
+ }
+ return NULL;
+}
+
+/**
+ * Find the string \a name in the input \a buffer, and return a pointer to the
+ * value immediately following \a name, reducing \a count appropriately.
+ * If \a name is not found the original \a buffer is returned.
+ */
+char *lprocfs_find_named_value(const char *buffer, const char *name,
+ size_t *count)
+{
+ char *val;
+ size_t buflen = *count;
+
+ /* there is no strnstr() in rhel5 and ubuntu kernels */
+ val = lprocfs_strnstr(buffer, name, buflen);
+ if (val == NULL)
+ return (char *)buffer;
+
+ val += strlen(name); /* skip prefix */
+ while (val < buffer + buflen && isspace(*val)) /* skip separator */
+ val++;
+
+ *count = 0;
+ while (val < buffer + buflen && isalnum(*val)) {
+ ++*count;
+ ++val;
+ }
+
+ return val - *count;
+}
+EXPORT_SYMBOL(lprocfs_find_named_value);
+
+int lprocfs_seq_create(struct proc_dir_entry *parent,
+ const char *name,
+ umode_t mode,
+ const struct file_operations *seq_fops,
+ void *data)
+{
+ struct proc_dir_entry *entry;
+
+ /* Disallow secretly (un)writable entries. */
+ LASSERT((seq_fops->write == NULL) == ((mode & 0222) == 0));
+ entry = proc_create_data(name, mode, parent, seq_fops, data);
+
+ if (entry == NULL)
+ return -ENOMEM;
+
+ return 0;
+}
+EXPORT_SYMBOL(lprocfs_seq_create);
+
+int lprocfs_obd_seq_create(struct obd_device *dev,
+ const char *name,
+ umode_t mode,
+ const struct file_operations *seq_fops,
+ void *data)
+{
+ return lprocfs_seq_create(dev->obd_proc_entry, name,
+ mode, seq_fops, data);
+}
+EXPORT_SYMBOL(lprocfs_obd_seq_create);
+
+void lprocfs_oh_tally(struct obd_histogram *oh, unsigned int value)
+{
+ if (value >= OBD_HIST_MAX)
+ value = OBD_HIST_MAX - 1;
+
+ spin_lock(&oh->oh_lock);
+ oh->oh_buckets[value]++;
+ spin_unlock(&oh->oh_lock);
+}
+EXPORT_SYMBOL(lprocfs_oh_tally);
+
+void lprocfs_oh_tally_log2(struct obd_histogram *oh, unsigned int value)
+{
+ unsigned int val;
+
+ for (val = 0; ((1 << val) < value) && (val <= OBD_HIST_MAX); val++)
+ ;
+
+ lprocfs_oh_tally(oh, val);
+}
+EXPORT_SYMBOL(lprocfs_oh_tally_log2);
+
+unsigned long lprocfs_oh_sum(struct obd_histogram *oh)
+{
+ unsigned long ret = 0;
+ int i;
+
+ for (i = 0; i < OBD_HIST_MAX; i++)
+ ret += oh->oh_buckets[i];
+ return ret;
+}
+EXPORT_SYMBOL(lprocfs_oh_sum);
+
+void lprocfs_oh_clear(struct obd_histogram *oh)
+{
+ spin_lock(&oh->oh_lock);
+ memset(oh->oh_buckets, 0, sizeof(oh->oh_buckets));
+ spin_unlock(&oh->oh_lock);
+}
+EXPORT_SYMBOL(lprocfs_oh_clear);
+
+int lprocfs_obd_rd_max_pages_per_rpc(struct seq_file *m, void *data)
+{
+ struct obd_device *dev = data;
+ struct client_obd *cli = &dev->u.cli;
+
+ client_obd_list_lock(&cli->cl_loi_list_lock);
+ seq_printf(m, "%d\n", cli->cl_max_pages_per_rpc);
+ client_obd_list_unlock(&cli->cl_loi_list_lock);
+
+ return 0;
+}
+EXPORT_SYMBOL(lprocfs_obd_rd_max_pages_per_rpc);
+
+#endif
diff --git a/drivers/staging/lustre/lustre/obdclass/lu_object.c b/drivers/staging/lustre/lustre/obdclass/lu_object.c
new file mode 100644
index 000000000..20c077995
--- /dev/null
+++ b/drivers/staging/lustre/lustre/obdclass/lu_object.c
@@ -0,0 +1,2192 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2011, 2012, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ *
+ * lustre/obdclass/lu_object.c
+ *
+ * Lustre Object.
+ * These are the only exported functions, they provide some generic
+ * infrastructure for managing object devices
+ *
+ * Author: Nikita Danilov <nikita.danilov@sun.com>
+ */
+
+#define DEBUG_SUBSYSTEM S_CLASS
+
+#include "../../include/linux/libcfs/libcfs.h"
+
+# include <linux/module.h>
+
+/* hash_long() */
+#include "../../include/linux/libcfs/libcfs_hash.h"
+#include "../include/obd_class.h"
+#include "../include/obd_support.h"
+#include "../include/lustre_disk.h"
+#include "../include/lustre_fid.h"
+#include "../include/lu_object.h"
+#include "../include/lu_ref.h"
+#include <linux/list.h>
+
+static void lu_object_free(const struct lu_env *env, struct lu_object *o);
+
+/**
+ * Decrease reference counter on object. If last reference is freed, return
+ * object to the cache, unless lu_object_is_dying(o) holds. In the latter
+ * case, free object immediately.
+ */
+void lu_object_put(const struct lu_env *env, struct lu_object *o)
+{
+ struct lu_site_bkt_data *bkt;
+ struct lu_object_header *top;
+ struct lu_site *site;
+ struct lu_object *orig;
+ struct cfs_hash_bd bd;
+ const struct lu_fid *fid;
+
+ top = o->lo_header;
+ site = o->lo_dev->ld_site;
+ orig = o;
+
+ /*
+ * till we have full fids-on-OST implemented anonymous objects
+ * are possible in OSP. such an object isn't listed in the site
+ * so we should not remove it from the site.
+ */
+ fid = lu_object_fid(o);
+ if (fid_is_zero(fid)) {
+ LASSERT(top->loh_hash.next == NULL
+ && top->loh_hash.pprev == NULL);
+ LASSERT(list_empty(&top->loh_lru));
+ if (!atomic_dec_and_test(&top->loh_ref))
+ return;
+ list_for_each_entry_reverse(o, &top->loh_layers, lo_linkage) {
+ if (o->lo_ops->loo_object_release != NULL)
+ o->lo_ops->loo_object_release(env, o);
+ }
+ lu_object_free(env, orig);
+ return;
+ }
+
+ cfs_hash_bd_get(site->ls_obj_hash, &top->loh_fid, &bd);
+ bkt = cfs_hash_bd_extra_get(site->ls_obj_hash, &bd);
+
+ if (!cfs_hash_bd_dec_and_lock(site->ls_obj_hash, &bd, &top->loh_ref)) {
+ if (lu_object_is_dying(top)) {
+
+ /*
+ * somebody may be waiting for this, currently only
+ * used for cl_object, see cl_object_put_last().
+ */
+ wake_up_all(&bkt->lsb_marche_funebre);
+ }
+ return;
+ }
+
+ LASSERT(bkt->lsb_busy > 0);
+ bkt->lsb_busy--;
+ /*
+ * When last reference is released, iterate over object
+ * layers, and notify them that object is no longer busy.
+ */
+ list_for_each_entry_reverse(o, &top->loh_layers, lo_linkage) {
+ if (o->lo_ops->loo_object_release != NULL)
+ o->lo_ops->loo_object_release(env, o);
+ }
+
+ if (!lu_object_is_dying(top)) {
+ LASSERT(list_empty(&top->loh_lru));
+ list_add_tail(&top->loh_lru, &bkt->lsb_lru);
+ cfs_hash_bd_unlock(site->ls_obj_hash, &bd, 1);
+ return;
+ }
+
+ /*
+ * If object is dying (will not be cached), removed it
+ * from hash table and LRU.
+ *
+ * This is done with hash table and LRU lists locked. As the only
+ * way to acquire first reference to previously unreferenced
+ * object is through hash-table lookup (lu_object_find()),
+ * or LRU scanning (lu_site_purge()), that are done under hash-table
+ * and LRU lock, no race with concurrent object lookup is possible
+ * and we can safely destroy object below.
+ */
+ if (!test_and_set_bit(LU_OBJECT_UNHASHED, &top->loh_flags))
+ cfs_hash_bd_del_locked(site->ls_obj_hash, &bd, &top->loh_hash);
+ cfs_hash_bd_unlock(site->ls_obj_hash, &bd, 1);
+ /*
+ * Object was already removed from hash and lru above, can
+ * kill it.
+ */
+ lu_object_free(env, orig);
+}
+EXPORT_SYMBOL(lu_object_put);
+
+/**
+ * Put object and don't keep in cache. This is temporary solution for
+ * multi-site objects when its layering is not constant.
+ */
+void lu_object_put_nocache(const struct lu_env *env, struct lu_object *o)
+{
+ set_bit(LU_OBJECT_HEARD_BANSHEE, &o->lo_header->loh_flags);
+ return lu_object_put(env, o);
+}
+EXPORT_SYMBOL(lu_object_put_nocache);
+
+/**
+ * Kill the object and take it out of LRU cache.
+ * Currently used by client code for layout change.
+ */
+void lu_object_unhash(const struct lu_env *env, struct lu_object *o)
+{
+ struct lu_object_header *top;
+
+ top = o->lo_header;
+ set_bit(LU_OBJECT_HEARD_BANSHEE, &top->loh_flags);
+ if (!test_and_set_bit(LU_OBJECT_UNHASHED, &top->loh_flags)) {
+ struct cfs_hash *obj_hash = o->lo_dev->ld_site->ls_obj_hash;
+ struct cfs_hash_bd bd;
+
+ cfs_hash_bd_get_and_lock(obj_hash, &top->loh_fid, &bd, 1);
+ list_del_init(&top->loh_lru);
+ cfs_hash_bd_del_locked(obj_hash, &bd, &top->loh_hash);
+ cfs_hash_bd_unlock(obj_hash, &bd, 1);
+ }
+}
+EXPORT_SYMBOL(lu_object_unhash);
+
+/**
+ * Allocate new object.
+ *
+ * This follows object creation protocol, described in the comment within
+ * struct lu_device_operations definition.
+ */
+static struct lu_object *lu_object_alloc(const struct lu_env *env,
+ struct lu_device *dev,
+ const struct lu_fid *f,
+ const struct lu_object_conf *conf)
+{
+ struct lu_object *scan;
+ struct lu_object *top;
+ struct list_head *layers;
+ unsigned int init_mask = 0;
+ unsigned int init_flag;
+ int clean;
+ int result;
+
+ /*
+ * Create top-level object slice. This will also create
+ * lu_object_header.
+ */
+ top = dev->ld_ops->ldo_object_alloc(env, NULL, dev);
+ if (top == NULL)
+ return ERR_PTR(-ENOMEM);
+ if (IS_ERR(top))
+ return top;
+ /*
+ * This is the only place where object fid is assigned. It's constant
+ * after this point.
+ */
+ top->lo_header->loh_fid = *f;
+ layers = &top->lo_header->loh_layers;
+
+ do {
+ /*
+ * Call ->loo_object_init() repeatedly, until no more new
+ * object slices are created.
+ */
+ clean = 1;
+ init_flag = 1;
+ list_for_each_entry(scan, layers, lo_linkage) {
+ if (init_mask & init_flag)
+ goto next;
+ clean = 0;
+ scan->lo_header = top->lo_header;
+ result = scan->lo_ops->loo_object_init(env, scan, conf);
+ if (result != 0) {
+ lu_object_free(env, top);
+ return ERR_PTR(result);
+ }
+ init_mask |= init_flag;
+next:
+ init_flag <<= 1;
+ }
+ } while (!clean);
+
+ list_for_each_entry_reverse(scan, layers, lo_linkage) {
+ if (scan->lo_ops->loo_object_start != NULL) {
+ result = scan->lo_ops->loo_object_start(env, scan);
+ if (result != 0) {
+ lu_object_free(env, top);
+ return ERR_PTR(result);
+ }
+ }
+ }
+
+ lprocfs_counter_incr(dev->ld_site->ls_stats, LU_SS_CREATED);
+ return top;
+}
+
+/**
+ * Free an object.
+ */
+static void lu_object_free(const struct lu_env *env, struct lu_object *o)
+{
+ struct lu_site_bkt_data *bkt;
+ struct lu_site *site;
+ struct lu_object *scan;
+ struct list_head *layers;
+ struct list_head splice;
+
+ site = o->lo_dev->ld_site;
+ layers = &o->lo_header->loh_layers;
+ bkt = lu_site_bkt_from_fid(site, &o->lo_header->loh_fid);
+ /*
+ * First call ->loo_object_delete() method to release all resources.
+ */
+ list_for_each_entry_reverse(scan, layers, lo_linkage) {
+ if (scan->lo_ops->loo_object_delete != NULL)
+ scan->lo_ops->loo_object_delete(env, scan);
+ }
+
+ /*
+ * Then, splice object layers into stand-alone list, and call
+ * ->loo_object_free() on all layers to free memory. Splice is
+ * necessary, because lu_object_header is freed together with the
+ * top-level slice.
+ */
+ INIT_LIST_HEAD(&splice);
+ list_splice_init(layers, &splice);
+ while (!list_empty(&splice)) {
+ /*
+ * Free layers in bottom-to-top order, so that object header
+ * lives as long as possible and ->loo_object_free() methods
+ * can look at its contents.
+ */
+ o = container_of0(splice.prev, struct lu_object, lo_linkage);
+ list_del_init(&o->lo_linkage);
+ LASSERT(o->lo_ops->loo_object_free != NULL);
+ o->lo_ops->loo_object_free(env, o);
+ }
+
+ if (waitqueue_active(&bkt->lsb_marche_funebre))
+ wake_up_all(&bkt->lsb_marche_funebre);
+}
+
+/**
+ * Free \a nr objects from the cold end of the site LRU list.
+ */
+int lu_site_purge(const struct lu_env *env, struct lu_site *s, int nr)
+{
+ struct lu_object_header *h;
+ struct lu_object_header *temp;
+ struct lu_site_bkt_data *bkt;
+ struct cfs_hash_bd bd;
+ struct cfs_hash_bd bd2;
+ struct list_head dispose;
+ int did_sth;
+ int start;
+ int count;
+ int bnr;
+ int i;
+
+ if (OBD_FAIL_CHECK(OBD_FAIL_OBD_NO_LRU))
+ return 0;
+
+ INIT_LIST_HEAD(&dispose);
+ /*
+ * Under LRU list lock, scan LRU list and move unreferenced objects to
+ * the dispose list, removing them from LRU and hash table.
+ */
+ start = s->ls_purge_start;
+ bnr = (nr == ~0) ? -1 : nr / CFS_HASH_NBKT(s->ls_obj_hash) + 1;
+ again:
+ did_sth = 0;
+ cfs_hash_for_each_bucket(s->ls_obj_hash, &bd, i) {
+ if (i < start)
+ continue;
+ count = bnr;
+ cfs_hash_bd_lock(s->ls_obj_hash, &bd, 1);
+ bkt = cfs_hash_bd_extra_get(s->ls_obj_hash, &bd);
+
+ list_for_each_entry_safe(h, temp, &bkt->lsb_lru, loh_lru) {
+ LASSERT(atomic_read(&h->loh_ref) == 0);
+
+ cfs_hash_bd_get(s->ls_obj_hash, &h->loh_fid, &bd2);
+ LASSERT(bd.bd_bucket == bd2.bd_bucket);
+
+ cfs_hash_bd_del_locked(s->ls_obj_hash,
+ &bd2, &h->loh_hash);
+ list_move(&h->loh_lru, &dispose);
+ if (did_sth == 0)
+ did_sth = 1;
+
+ if (nr != ~0 && --nr == 0)
+ break;
+
+ if (count > 0 && --count == 0)
+ break;
+
+ }
+ cfs_hash_bd_unlock(s->ls_obj_hash, &bd, 1);
+ cond_resched();
+ /*
+ * Free everything on the dispose list. This is safe against
+ * races due to the reasons described in lu_object_put().
+ */
+ while (!list_empty(&dispose)) {
+ h = container_of0(dispose.next,
+ struct lu_object_header, loh_lru);
+ list_del_init(&h->loh_lru);
+ lu_object_free(env, lu_object_top(h));
+ lprocfs_counter_incr(s->ls_stats, LU_SS_LRU_PURGED);
+ }
+
+ if (nr == 0)
+ break;
+ }
+
+ if (nr != 0 && did_sth && start != 0) {
+ start = 0; /* restart from the first bucket */
+ goto again;
+ }
+ /* race on s->ls_purge_start, but nobody cares */
+ s->ls_purge_start = i % CFS_HASH_NBKT(s->ls_obj_hash);
+
+ return nr;
+}
+EXPORT_SYMBOL(lu_site_purge);
+
+/*
+ * Object printing.
+ *
+ * Code below has to jump through certain loops to output object description
+ * into libcfs_debug_msg-based log. The problem is that lu_object_print()
+ * composes object description from strings that are parts of _lines_ of
+ * output (i.e., strings that are not terminated by newline). This doesn't fit
+ * very well into libcfs_debug_msg() interface that assumes that each message
+ * supplied to it is a self-contained output line.
+ *
+ * To work around this, strings are collected in a temporary buffer
+ * (implemented as a value of lu_cdebug_key key), until terminating newline
+ * character is detected.
+ *
+ */
+
+enum {
+ /**
+ * Maximal line size.
+ *
+ * XXX overflow is not handled correctly.
+ */
+ LU_CDEBUG_LINE = 512
+};
+
+struct lu_cdebug_data {
+ /**
+ * Temporary buffer.
+ */
+ char lck_area[LU_CDEBUG_LINE];
+};
+
+/* context key constructor/destructor: lu_global_key_init, lu_global_key_fini */
+LU_KEY_INIT_FINI(lu_global, struct lu_cdebug_data);
+
+/**
+ * Key, holding temporary buffer. This key is registered very early by
+ * lu_global_init().
+ */
+struct lu_context_key lu_global_key = {
+ .lct_tags = LCT_MD_THREAD | LCT_DT_THREAD |
+ LCT_MG_THREAD | LCT_CL_THREAD | LCT_LOCAL,
+ .lct_init = lu_global_key_init,
+ .lct_fini = lu_global_key_fini
+};
+
+/**
+ * Printer function emitting messages through libcfs_debug_msg().
+ */
+int lu_cdebug_printer(const struct lu_env *env,
+ void *cookie, const char *format, ...)
+{
+ struct libcfs_debug_msg_data *msgdata = cookie;
+ struct lu_cdebug_data *key;
+ int used;
+ int complete;
+ va_list args;
+
+ va_start(args, format);
+
+ key = lu_context_key_get(&env->le_ctx, &lu_global_key);
+ LASSERT(key != NULL);
+
+ used = strlen(key->lck_area);
+ complete = format[strlen(format) - 1] == '\n';
+ /*
+ * Append new chunk to the buffer.
+ */
+ vsnprintf(key->lck_area + used,
+ ARRAY_SIZE(key->lck_area) - used, format, args);
+ if (complete) {
+ if (cfs_cdebug_show(msgdata->msg_mask, msgdata->msg_subsys))
+ libcfs_debug_msg(msgdata, "%s", key->lck_area);
+ key->lck_area[0] = 0;
+ }
+ va_end(args);
+ return 0;
+}
+EXPORT_SYMBOL(lu_cdebug_printer);
+
+/**
+ * Print object header.
+ */
+void lu_object_header_print(const struct lu_env *env, void *cookie,
+ lu_printer_t printer,
+ const struct lu_object_header *hdr)
+{
+ (*printer)(env, cookie, "header@%p[%#lx, %d, "DFID"%s%s%s]",
+ hdr, hdr->loh_flags, atomic_read(&hdr->loh_ref),
+ PFID(&hdr->loh_fid),
+ hlist_unhashed(&hdr->loh_hash) ? "" : " hash",
+ list_empty((struct list_head *)&hdr->loh_lru) ? \
+ "" : " lru",
+ hdr->loh_attr & LOHA_EXISTS ? " exist":"");
+}
+EXPORT_SYMBOL(lu_object_header_print);
+
+/**
+ * Print human readable representation of the \a o to the \a printer.
+ */
+void lu_object_print(const struct lu_env *env, void *cookie,
+ lu_printer_t printer, const struct lu_object *o)
+{
+ static const char ruler[] = "........................................";
+ struct lu_object_header *top;
+ int depth = 4;
+
+ top = o->lo_header;
+ lu_object_header_print(env, cookie, printer, top);
+ (*printer)(env, cookie, "{\n");
+
+ list_for_each_entry(o, &top->loh_layers, lo_linkage) {
+ /*
+ * print `.' \a depth times followed by type name and address
+ */
+ (*printer)(env, cookie, "%*.*s%s@%p", depth, depth, ruler,
+ o->lo_dev->ld_type->ldt_name, o);
+
+ if (o->lo_ops->loo_object_print != NULL)
+ (*o->lo_ops->loo_object_print)(env, cookie, printer, o);
+
+ (*printer)(env, cookie, "\n");
+ }
+
+ (*printer)(env, cookie, "} header@%p\n", top);
+}
+EXPORT_SYMBOL(lu_object_print);
+
+/**
+ * Check object consistency.
+ */
+int lu_object_invariant(const struct lu_object *o)
+{
+ struct lu_object_header *top;
+
+ top = o->lo_header;
+ list_for_each_entry(o, &top->loh_layers, lo_linkage) {
+ if (o->lo_ops->loo_object_invariant != NULL &&
+ !o->lo_ops->loo_object_invariant(o))
+ return 0;
+ }
+ return 1;
+}
+EXPORT_SYMBOL(lu_object_invariant);
+
+static struct lu_object *htable_lookup(struct lu_site *s,
+ struct cfs_hash_bd *bd,
+ const struct lu_fid *f,
+ wait_queue_t *waiter,
+ __u64 *version)
+{
+ struct lu_site_bkt_data *bkt;
+ struct lu_object_header *h;
+ struct hlist_node *hnode;
+ __u64 ver = cfs_hash_bd_version_get(bd);
+
+ if (*version == ver)
+ return ERR_PTR(-ENOENT);
+
+ *version = ver;
+ bkt = cfs_hash_bd_extra_get(s->ls_obj_hash, bd);
+ /* cfs_hash_bd_peek_locked is a somehow "internal" function
+ * of cfs_hash, it doesn't add refcount on object. */
+ hnode = cfs_hash_bd_peek_locked(s->ls_obj_hash, bd, (void *)f);
+ if (hnode == NULL) {
+ lprocfs_counter_incr(s->ls_stats, LU_SS_CACHE_MISS);
+ return ERR_PTR(-ENOENT);
+ }
+
+ h = container_of0(hnode, struct lu_object_header, loh_hash);
+ if (likely(!lu_object_is_dying(h))) {
+ cfs_hash_get(s->ls_obj_hash, hnode);
+ lprocfs_counter_incr(s->ls_stats, LU_SS_CACHE_HIT);
+ list_del_init(&h->loh_lru);
+ return lu_object_top(h);
+ }
+
+ /*
+ * Lookup found an object being destroyed this object cannot be
+ * returned (to assure that references to dying objects are eventually
+ * drained), and moreover, lookup has to wait until object is freed.
+ */
+
+ init_waitqueue_entry(waiter, current);
+ add_wait_queue(&bkt->lsb_marche_funebre, waiter);
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ lprocfs_counter_incr(s->ls_stats, LU_SS_CACHE_DEATH_RACE);
+ return ERR_PTR(-EAGAIN);
+}
+
+/**
+ * Search cache for an object with the fid \a f. If such object is found,
+ * return it. Otherwise, create new object, insert it into cache and return
+ * it. In any case, additional reference is acquired on the returned object.
+ */
+struct lu_object *lu_object_find(const struct lu_env *env,
+ struct lu_device *dev, const struct lu_fid *f,
+ const struct lu_object_conf *conf)
+{
+ return lu_object_find_at(env, dev->ld_site->ls_top_dev, f, conf);
+}
+EXPORT_SYMBOL(lu_object_find);
+
+static struct lu_object *lu_object_new(const struct lu_env *env,
+ struct lu_device *dev,
+ const struct lu_fid *f,
+ const struct lu_object_conf *conf)
+{
+ struct lu_object *o;
+ struct cfs_hash *hs;
+ struct cfs_hash_bd bd;
+ struct lu_site_bkt_data *bkt;
+
+ o = lu_object_alloc(env, dev, f, conf);
+ if (unlikely(IS_ERR(o)))
+ return o;
+
+ hs = dev->ld_site->ls_obj_hash;
+ cfs_hash_bd_get_and_lock(hs, (void *)f, &bd, 1);
+ bkt = cfs_hash_bd_extra_get(hs, &bd);
+ cfs_hash_bd_add_locked(hs, &bd, &o->lo_header->loh_hash);
+ bkt->lsb_busy++;
+ cfs_hash_bd_unlock(hs, &bd, 1);
+ return o;
+}
+
+/**
+ * Core logic of lu_object_find*() functions.
+ */
+static struct lu_object *lu_object_find_try(const struct lu_env *env,
+ struct lu_device *dev,
+ const struct lu_fid *f,
+ const struct lu_object_conf *conf,
+ wait_queue_t *waiter)
+{
+ struct lu_object *o;
+ struct lu_object *shadow;
+ struct lu_site *s;
+ struct cfs_hash *hs;
+ struct cfs_hash_bd bd;
+ __u64 version = 0;
+
+ /*
+ * This uses standard index maintenance protocol:
+ *
+ * - search index under lock, and return object if found;
+ * - otherwise, unlock index, allocate new object;
+ * - lock index and search again;
+ * - if nothing is found (usual case), insert newly created
+ * object into index;
+ * - otherwise (race: other thread inserted object), free
+ * object just allocated.
+ * - unlock index;
+ * - return object.
+ *
+ * For "LOC_F_NEW" case, we are sure the object is new established.
+ * It is unnecessary to perform lookup-alloc-lookup-insert, instead,
+ * just alloc and insert directly.
+ *
+ * If dying object is found during index search, add @waiter to the
+ * site wait-queue and return ERR_PTR(-EAGAIN).
+ */
+ if (conf != NULL && conf->loc_flags & LOC_F_NEW)
+ return lu_object_new(env, dev, f, conf);
+
+ s = dev->ld_site;
+ hs = s->ls_obj_hash;
+ cfs_hash_bd_get_and_lock(hs, (void *)f, &bd, 1);
+ o = htable_lookup(s, &bd, f, waiter, &version);
+ cfs_hash_bd_unlock(hs, &bd, 1);
+ if (!IS_ERR(o) || PTR_ERR(o) != -ENOENT)
+ return o;
+
+ /*
+ * Allocate new object. This may result in rather complicated
+ * operations, including fld queries, inode loading, etc.
+ */
+ o = lu_object_alloc(env, dev, f, conf);
+ if (unlikely(IS_ERR(o)))
+ return o;
+
+ LASSERT(lu_fid_eq(lu_object_fid(o), f));
+
+ cfs_hash_bd_lock(hs, &bd, 1);
+
+ shadow = htable_lookup(s, &bd, f, waiter, &version);
+ if (likely(IS_ERR(shadow) && PTR_ERR(shadow) == -ENOENT)) {
+ struct lu_site_bkt_data *bkt;
+
+ bkt = cfs_hash_bd_extra_get(hs, &bd);
+ cfs_hash_bd_add_locked(hs, &bd, &o->lo_header->loh_hash);
+ bkt->lsb_busy++;
+ cfs_hash_bd_unlock(hs, &bd, 1);
+ return o;
+ }
+
+ lprocfs_counter_incr(s->ls_stats, LU_SS_CACHE_RACE);
+ cfs_hash_bd_unlock(hs, &bd, 1);
+ lu_object_free(env, o);
+ return shadow;
+}
+
+/**
+ * Much like lu_object_find(), but top level device of object is specifically
+ * \a dev rather than top level device of the site. This interface allows
+ * objects of different "stacking" to be created within the same site.
+ */
+struct lu_object *lu_object_find_at(const struct lu_env *env,
+ struct lu_device *dev,
+ const struct lu_fid *f,
+ const struct lu_object_conf *conf)
+{
+ struct lu_site_bkt_data *bkt;
+ struct lu_object *obj;
+ wait_queue_t wait;
+
+ while (1) {
+ obj = lu_object_find_try(env, dev, f, conf, &wait);
+ if (obj != ERR_PTR(-EAGAIN))
+ return obj;
+ /*
+ * lu_object_find_try() already added waiter into the
+ * wait queue.
+ */
+ schedule();
+ bkt = lu_site_bkt_from_fid(dev->ld_site, (void *)f);
+ remove_wait_queue(&bkt->lsb_marche_funebre, &wait);
+ }
+}
+EXPORT_SYMBOL(lu_object_find_at);
+
+/**
+ * Find object with given fid, and return its slice belonging to given device.
+ */
+struct lu_object *lu_object_find_slice(const struct lu_env *env,
+ struct lu_device *dev,
+ const struct lu_fid *f,
+ const struct lu_object_conf *conf)
+{
+ struct lu_object *top;
+ struct lu_object *obj;
+
+ top = lu_object_find(env, dev, f, conf);
+ if (!IS_ERR(top)) {
+ obj = lu_object_locate(top->lo_header, dev->ld_type);
+ if (obj == NULL)
+ lu_object_put(env, top);
+ } else
+ obj = top;
+ return obj;
+}
+EXPORT_SYMBOL(lu_object_find_slice);
+
+/**
+ * Global list of all device types.
+ */
+static LIST_HEAD(lu_device_types);
+
+int lu_device_type_init(struct lu_device_type *ldt)
+{
+ int result = 0;
+
+ INIT_LIST_HEAD(&ldt->ldt_linkage);
+ if (ldt->ldt_ops->ldto_init)
+ result = ldt->ldt_ops->ldto_init(ldt);
+ if (result == 0)
+ list_add(&ldt->ldt_linkage, &lu_device_types);
+ return result;
+}
+EXPORT_SYMBOL(lu_device_type_init);
+
+void lu_device_type_fini(struct lu_device_type *ldt)
+{
+ list_del_init(&ldt->ldt_linkage);
+ if (ldt->ldt_ops->ldto_fini)
+ ldt->ldt_ops->ldto_fini(ldt);
+}
+EXPORT_SYMBOL(lu_device_type_fini);
+
+void lu_types_stop(void)
+{
+ struct lu_device_type *ldt;
+
+ list_for_each_entry(ldt, &lu_device_types, ldt_linkage) {
+ if (ldt->ldt_device_nr == 0 && ldt->ldt_ops->ldto_stop)
+ ldt->ldt_ops->ldto_stop(ldt);
+ }
+}
+EXPORT_SYMBOL(lu_types_stop);
+
+/**
+ * Global list of all sites on this node
+ */
+static LIST_HEAD(lu_sites);
+static DEFINE_MUTEX(lu_sites_guard);
+
+/**
+ * Global environment used by site shrinker.
+ */
+static struct lu_env lu_shrink_env;
+
+struct lu_site_print_arg {
+ struct lu_env *lsp_env;
+ void *lsp_cookie;
+ lu_printer_t lsp_printer;
+};
+
+static int
+lu_site_obj_print(struct cfs_hash *hs, struct cfs_hash_bd *bd,
+ struct hlist_node *hnode, void *data)
+{
+ struct lu_site_print_arg *arg = (struct lu_site_print_arg *)data;
+ struct lu_object_header *h;
+
+ h = hlist_entry(hnode, struct lu_object_header, loh_hash);
+ if (!list_empty(&h->loh_layers)) {
+ const struct lu_object *o;
+
+ o = lu_object_top(h);
+ lu_object_print(arg->lsp_env, arg->lsp_cookie,
+ arg->lsp_printer, o);
+ } else {
+ lu_object_header_print(arg->lsp_env, arg->lsp_cookie,
+ arg->lsp_printer, h);
+ }
+ return 0;
+}
+
+/**
+ * Print all objects in \a s.
+ */
+void lu_site_print(const struct lu_env *env, struct lu_site *s, void *cookie,
+ lu_printer_t printer)
+{
+ struct lu_site_print_arg arg = {
+ .lsp_env = (struct lu_env *)env,
+ .lsp_cookie = cookie,
+ .lsp_printer = printer,
+ };
+
+ cfs_hash_for_each(s->ls_obj_hash, lu_site_obj_print, &arg);
+}
+EXPORT_SYMBOL(lu_site_print);
+
+enum {
+ LU_CACHE_PERCENT_MAX = 50,
+ LU_CACHE_PERCENT_DEFAULT = 20
+};
+
+static unsigned int lu_cache_percent = LU_CACHE_PERCENT_DEFAULT;
+module_param(lu_cache_percent, int, 0644);
+MODULE_PARM_DESC(lu_cache_percent, "Percentage of memory to be used as lu_object cache");
+
+/**
+ * Return desired hash table order.
+ */
+static int lu_htable_order(void)
+{
+ unsigned long cache_size;
+ int bits;
+
+ /*
+ * Calculate hash table size, assuming that we want reasonable
+ * performance when 20% of total memory is occupied by cache of
+ * lu_objects.
+ *
+ * Size of lu_object is (arbitrary) taken as 1K (together with inode).
+ */
+ cache_size = totalram_pages;
+
+#if BITS_PER_LONG == 32
+ /* limit hashtable size for lowmem systems to low RAM */
+ if (cache_size > 1 << (30 - PAGE_CACHE_SHIFT))
+ cache_size = 1 << (30 - PAGE_CACHE_SHIFT) * 3 / 4;
+#endif
+
+ /* clear off unreasonable cache setting. */
+ if (lu_cache_percent == 0 || lu_cache_percent > LU_CACHE_PERCENT_MAX) {
+ CWARN("obdclass: invalid lu_cache_percent: %u, it must be in the range of (0, %u]. Will use default value: %u.\n",
+ lu_cache_percent, LU_CACHE_PERCENT_MAX,
+ LU_CACHE_PERCENT_DEFAULT);
+
+ lu_cache_percent = LU_CACHE_PERCENT_DEFAULT;
+ }
+ cache_size = cache_size / 100 * lu_cache_percent *
+ (PAGE_CACHE_SIZE / 1024);
+
+ for (bits = 1; (1 << bits) < cache_size; ++bits) {
+ ;
+ }
+ return bits;
+}
+
+static unsigned lu_obj_hop_hash(struct cfs_hash *hs,
+ const void *key, unsigned mask)
+{
+ struct lu_fid *fid = (struct lu_fid *)key;
+ __u32 hash;
+
+ hash = fid_flatten32(fid);
+ hash += (hash >> 4) + (hash << 12); /* mixing oid and seq */
+ hash = hash_long(hash, hs->hs_bkt_bits);
+
+ /* give me another random factor */
+ hash -= hash_long((unsigned long)hs, fid_oid(fid) % 11 + 3);
+
+ hash <<= hs->hs_cur_bits - hs->hs_bkt_bits;
+ hash |= (fid_seq(fid) + fid_oid(fid)) & (CFS_HASH_NBKT(hs) - 1);
+
+ return hash & mask;
+}
+
+static void *lu_obj_hop_object(struct hlist_node *hnode)
+{
+ return hlist_entry(hnode, struct lu_object_header, loh_hash);
+}
+
+static void *lu_obj_hop_key(struct hlist_node *hnode)
+{
+ struct lu_object_header *h;
+
+ h = hlist_entry(hnode, struct lu_object_header, loh_hash);
+ return &h->loh_fid;
+}
+
+static int lu_obj_hop_keycmp(const void *key, struct hlist_node *hnode)
+{
+ struct lu_object_header *h;
+
+ h = hlist_entry(hnode, struct lu_object_header, loh_hash);
+ return lu_fid_eq(&h->loh_fid, (struct lu_fid *)key);
+}
+
+static void lu_obj_hop_get(struct cfs_hash *hs, struct hlist_node *hnode)
+{
+ struct lu_object_header *h;
+
+ h = hlist_entry(hnode, struct lu_object_header, loh_hash);
+ if (atomic_add_return(1, &h->loh_ref) == 1) {
+ struct lu_site_bkt_data *bkt;
+ struct cfs_hash_bd bd;
+
+ cfs_hash_bd_get(hs, &h->loh_fid, &bd);
+ bkt = cfs_hash_bd_extra_get(hs, &bd);
+ bkt->lsb_busy++;
+ }
+}
+
+static void lu_obj_hop_put_locked(struct cfs_hash *hs, struct hlist_node *hnode)
+{
+ LBUG(); /* we should never called it */
+}
+
+cfs_hash_ops_t lu_site_hash_ops = {
+ .hs_hash = lu_obj_hop_hash,
+ .hs_key = lu_obj_hop_key,
+ .hs_keycmp = lu_obj_hop_keycmp,
+ .hs_object = lu_obj_hop_object,
+ .hs_get = lu_obj_hop_get,
+ .hs_put_locked = lu_obj_hop_put_locked,
+};
+
+void lu_dev_add_linkage(struct lu_site *s, struct lu_device *d)
+{
+ spin_lock(&s->ls_ld_lock);
+ if (list_empty(&d->ld_linkage))
+ list_add(&d->ld_linkage, &s->ls_ld_linkage);
+ spin_unlock(&s->ls_ld_lock);
+}
+EXPORT_SYMBOL(lu_dev_add_linkage);
+
+void lu_dev_del_linkage(struct lu_site *s, struct lu_device *d)
+{
+ spin_lock(&s->ls_ld_lock);
+ list_del_init(&d->ld_linkage);
+ spin_unlock(&s->ls_ld_lock);
+}
+EXPORT_SYMBOL(lu_dev_del_linkage);
+
+/**
+ * Initialize site \a s, with \a d as the top level device.
+ */
+#define LU_SITE_BITS_MIN 12
+#define LU_SITE_BITS_MAX 24
+/**
+ * total 256 buckets, we don't want too many buckets because:
+ * - consume too much memory
+ * - avoid unbalanced LRU list
+ */
+#define LU_SITE_BKT_BITS 8
+
+int lu_site_init(struct lu_site *s, struct lu_device *top)
+{
+ struct lu_site_bkt_data *bkt;
+ struct cfs_hash_bd bd;
+ char name[16];
+ int bits;
+ int i;
+
+ memset(s, 0, sizeof(*s));
+ bits = lu_htable_order();
+ snprintf(name, 16, "lu_site_%s", top->ld_type->ldt_name);
+ for (bits = min(max(LU_SITE_BITS_MIN, bits), LU_SITE_BITS_MAX);
+ bits >= LU_SITE_BITS_MIN; bits--) {
+ s->ls_obj_hash = cfs_hash_create(name, bits, bits,
+ bits - LU_SITE_BKT_BITS,
+ sizeof(*bkt), 0, 0,
+ &lu_site_hash_ops,
+ CFS_HASH_SPIN_BKTLOCK |
+ CFS_HASH_NO_ITEMREF |
+ CFS_HASH_DEPTH |
+ CFS_HASH_ASSERT_EMPTY);
+ if (s->ls_obj_hash != NULL)
+ break;
+ }
+
+ if (s->ls_obj_hash == NULL) {
+ CERROR("failed to create lu_site hash with bits: %d\n", bits);
+ return -ENOMEM;
+ }
+
+ cfs_hash_for_each_bucket(s->ls_obj_hash, &bd, i) {
+ bkt = cfs_hash_bd_extra_get(s->ls_obj_hash, &bd);
+ INIT_LIST_HEAD(&bkt->lsb_lru);
+ init_waitqueue_head(&bkt->lsb_marche_funebre);
+ }
+
+ s->ls_stats = lprocfs_alloc_stats(LU_SS_LAST_STAT, 0);
+ if (s->ls_stats == NULL) {
+ cfs_hash_putref(s->ls_obj_hash);
+ s->ls_obj_hash = NULL;
+ return -ENOMEM;
+ }
+
+ lprocfs_counter_init(s->ls_stats, LU_SS_CREATED,
+ 0, "created", "created");
+ lprocfs_counter_init(s->ls_stats, LU_SS_CACHE_HIT,
+ 0, "cache_hit", "cache_hit");
+ lprocfs_counter_init(s->ls_stats, LU_SS_CACHE_MISS,
+ 0, "cache_miss", "cache_miss");
+ lprocfs_counter_init(s->ls_stats, LU_SS_CACHE_RACE,
+ 0, "cache_race", "cache_race");
+ lprocfs_counter_init(s->ls_stats, LU_SS_CACHE_DEATH_RACE,
+ 0, "cache_death_race", "cache_death_race");
+ lprocfs_counter_init(s->ls_stats, LU_SS_LRU_PURGED,
+ 0, "lru_purged", "lru_purged");
+
+ INIT_LIST_HEAD(&s->ls_linkage);
+ s->ls_top_dev = top;
+ top->ld_site = s;
+ lu_device_get(top);
+ lu_ref_add(&top->ld_reference, "site-top", s);
+
+ INIT_LIST_HEAD(&s->ls_ld_linkage);
+ spin_lock_init(&s->ls_ld_lock);
+
+ lu_dev_add_linkage(s, top);
+
+ return 0;
+}
+EXPORT_SYMBOL(lu_site_init);
+
+/**
+ * Finalize \a s and release its resources.
+ */
+void lu_site_fini(struct lu_site *s)
+{
+ mutex_lock(&lu_sites_guard);
+ list_del_init(&s->ls_linkage);
+ mutex_unlock(&lu_sites_guard);
+
+ if (s->ls_obj_hash != NULL) {
+ cfs_hash_putref(s->ls_obj_hash);
+ s->ls_obj_hash = NULL;
+ }
+
+ if (s->ls_top_dev != NULL) {
+ s->ls_top_dev->ld_site = NULL;
+ lu_ref_del(&s->ls_top_dev->ld_reference, "site-top", s);
+ lu_device_put(s->ls_top_dev);
+ s->ls_top_dev = NULL;
+ }
+
+ if (s->ls_stats != NULL)
+ lprocfs_free_stats(&s->ls_stats);
+}
+EXPORT_SYMBOL(lu_site_fini);
+
+/**
+ * Called when initialization of stack for this site is completed.
+ */
+int lu_site_init_finish(struct lu_site *s)
+{
+ int result;
+ mutex_lock(&lu_sites_guard);
+ result = lu_context_refill(&lu_shrink_env.le_ctx);
+ if (result == 0)
+ list_add(&s->ls_linkage, &lu_sites);
+ mutex_unlock(&lu_sites_guard);
+ return result;
+}
+EXPORT_SYMBOL(lu_site_init_finish);
+
+/**
+ * Acquire additional reference on device \a d
+ */
+void lu_device_get(struct lu_device *d)
+{
+ atomic_inc(&d->ld_ref);
+}
+EXPORT_SYMBOL(lu_device_get);
+
+/**
+ * Release reference on device \a d.
+ */
+void lu_device_put(struct lu_device *d)
+{
+ LASSERT(atomic_read(&d->ld_ref) > 0);
+ atomic_dec(&d->ld_ref);
+}
+EXPORT_SYMBOL(lu_device_put);
+
+/**
+ * Initialize device \a d of type \a t.
+ */
+int lu_device_init(struct lu_device *d, struct lu_device_type *t)
+{
+ if (t->ldt_device_nr++ == 0 && t->ldt_ops->ldto_start != NULL)
+ t->ldt_ops->ldto_start(t);
+ memset(d, 0, sizeof(*d));
+ atomic_set(&d->ld_ref, 0);
+ d->ld_type = t;
+ lu_ref_init(&d->ld_reference);
+ INIT_LIST_HEAD(&d->ld_linkage);
+ return 0;
+}
+EXPORT_SYMBOL(lu_device_init);
+
+/**
+ * Finalize device \a d.
+ */
+void lu_device_fini(struct lu_device *d)
+{
+ struct lu_device_type *t;
+
+ t = d->ld_type;
+ if (d->ld_obd != NULL) {
+ d->ld_obd->obd_lu_dev = NULL;
+ d->ld_obd = NULL;
+ }
+
+ lu_ref_fini(&d->ld_reference);
+ LASSERTF(atomic_read(&d->ld_ref) == 0,
+ "Refcount is %u\n", atomic_read(&d->ld_ref));
+ LASSERT(t->ldt_device_nr > 0);
+ if (--t->ldt_device_nr == 0 && t->ldt_ops->ldto_stop != NULL)
+ t->ldt_ops->ldto_stop(t);
+}
+EXPORT_SYMBOL(lu_device_fini);
+
+/**
+ * Initialize object \a o that is part of compound object \a h and was created
+ * by device \a d.
+ */
+int lu_object_init(struct lu_object *o, struct lu_object_header *h,
+ struct lu_device *d)
+{
+ memset(o, 0, sizeof(*o));
+ o->lo_header = h;
+ o->lo_dev = d;
+ lu_device_get(d);
+ lu_ref_add_at(&d->ld_reference, &o->lo_dev_ref, "lu_object", o);
+ INIT_LIST_HEAD(&o->lo_linkage);
+
+ return 0;
+}
+EXPORT_SYMBOL(lu_object_init);
+
+/**
+ * Finalize object and release its resources.
+ */
+void lu_object_fini(struct lu_object *o)
+{
+ struct lu_device *dev = o->lo_dev;
+
+ LASSERT(list_empty(&o->lo_linkage));
+
+ if (dev != NULL) {
+ lu_ref_del_at(&dev->ld_reference, &o->lo_dev_ref,
+ "lu_object", o);
+ lu_device_put(dev);
+ o->lo_dev = NULL;
+ }
+}
+EXPORT_SYMBOL(lu_object_fini);
+
+/**
+ * Add object \a o as first layer of compound object \a h
+ *
+ * This is typically called by the ->ldo_object_alloc() method of top-level
+ * device.
+ */
+void lu_object_add_top(struct lu_object_header *h, struct lu_object *o)
+{
+ list_move(&o->lo_linkage, &h->loh_layers);
+}
+EXPORT_SYMBOL(lu_object_add_top);
+
+/**
+ * Add object \a o as a layer of compound object, going after \a before.
+ *
+ * This is typically called by the ->ldo_object_alloc() method of \a
+ * before->lo_dev.
+ */
+void lu_object_add(struct lu_object *before, struct lu_object *o)
+{
+ list_move(&o->lo_linkage, &before->lo_linkage);
+}
+EXPORT_SYMBOL(lu_object_add);
+
+/**
+ * Initialize compound object.
+ */
+int lu_object_header_init(struct lu_object_header *h)
+{
+ memset(h, 0, sizeof(*h));
+ atomic_set(&h->loh_ref, 1);
+ INIT_HLIST_NODE(&h->loh_hash);
+ INIT_LIST_HEAD(&h->loh_lru);
+ INIT_LIST_HEAD(&h->loh_layers);
+ lu_ref_init(&h->loh_reference);
+ return 0;
+}
+EXPORT_SYMBOL(lu_object_header_init);
+
+/**
+ * Finalize compound object.
+ */
+void lu_object_header_fini(struct lu_object_header *h)
+{
+ LASSERT(list_empty(&h->loh_layers));
+ LASSERT(list_empty(&h->loh_lru));
+ LASSERT(hlist_unhashed(&h->loh_hash));
+ lu_ref_fini(&h->loh_reference);
+}
+EXPORT_SYMBOL(lu_object_header_fini);
+
+/**
+ * Given a compound object, find its slice, corresponding to the device type
+ * \a dtype.
+ */
+struct lu_object *lu_object_locate(struct lu_object_header *h,
+ const struct lu_device_type *dtype)
+{
+ struct lu_object *o;
+
+ list_for_each_entry(o, &h->loh_layers, lo_linkage) {
+ if (o->lo_dev->ld_type == dtype)
+ return o;
+ }
+ return NULL;
+}
+EXPORT_SYMBOL(lu_object_locate);
+
+
+
+/**
+ * Finalize and free devices in the device stack.
+ *
+ * Finalize device stack by purging object cache, and calling
+ * lu_device_type_operations::ldto_device_fini() and
+ * lu_device_type_operations::ldto_device_free() on all devices in the stack.
+ */
+void lu_stack_fini(const struct lu_env *env, struct lu_device *top)
+{
+ struct lu_site *site = top->ld_site;
+ struct lu_device *scan;
+ struct lu_device *next;
+
+ lu_site_purge(env, site, ~0);
+ for (scan = top; scan != NULL; scan = next) {
+ next = scan->ld_type->ldt_ops->ldto_device_fini(env, scan);
+ lu_ref_del(&scan->ld_reference, "lu-stack", &lu_site_init);
+ lu_device_put(scan);
+ }
+
+ /* purge again. */
+ lu_site_purge(env, site, ~0);
+
+ for (scan = top; scan != NULL; scan = next) {
+ const struct lu_device_type *ldt = scan->ld_type;
+ struct obd_type *type;
+
+ next = ldt->ldt_ops->ldto_device_free(env, scan);
+ type = ldt->ldt_obd_type;
+ if (type != NULL) {
+ type->typ_refcnt--;
+ class_put_type(type);
+ }
+ }
+}
+EXPORT_SYMBOL(lu_stack_fini);
+
+enum {
+ /**
+ * Maximal number of tld slots.
+ */
+ LU_CONTEXT_KEY_NR = 40
+};
+
+static struct lu_context_key *lu_keys[LU_CONTEXT_KEY_NR] = { NULL, };
+
+static DEFINE_SPINLOCK(lu_keys_guard);
+
+/**
+ * Global counter incremented whenever key is registered, unregistered,
+ * revived or quiesced. This is used to void unnecessary calls to
+ * lu_context_refill(). No locking is provided, as initialization and shutdown
+ * are supposed to be externally serialized.
+ */
+static unsigned key_set_version;
+
+/**
+ * Register new key.
+ */
+int lu_context_key_register(struct lu_context_key *key)
+{
+ int result;
+ int i;
+
+ LASSERT(key->lct_init != NULL);
+ LASSERT(key->lct_fini != NULL);
+ LASSERT(key->lct_tags != 0);
+
+ result = -ENFILE;
+ spin_lock(&lu_keys_guard);
+ for (i = 0; i < ARRAY_SIZE(lu_keys); ++i) {
+ if (lu_keys[i] == NULL) {
+ key->lct_index = i;
+ atomic_set(&key->lct_used, 1);
+ lu_keys[i] = key;
+ lu_ref_init(&key->lct_reference);
+ result = 0;
+ ++key_set_version;
+ break;
+ }
+ }
+ spin_unlock(&lu_keys_guard);
+ return result;
+}
+EXPORT_SYMBOL(lu_context_key_register);
+
+static void key_fini(struct lu_context *ctx, int index)
+{
+ if (ctx->lc_value != NULL && ctx->lc_value[index] != NULL) {
+ struct lu_context_key *key;
+
+ key = lu_keys[index];
+ LASSERT(key != NULL);
+ LASSERT(key->lct_fini != NULL);
+ LASSERT(atomic_read(&key->lct_used) > 1);
+
+ key->lct_fini(ctx, key, ctx->lc_value[index]);
+ lu_ref_del(&key->lct_reference, "ctx", ctx);
+ atomic_dec(&key->lct_used);
+
+ if ((ctx->lc_tags & LCT_NOREF) == 0) {
+#ifdef CONFIG_MODULE_UNLOAD
+ LINVRNT(module_refcount(key->lct_owner) > 0);
+#endif
+ module_put(key->lct_owner);
+ }
+ ctx->lc_value[index] = NULL;
+ }
+}
+
+/**
+ * Deregister key.
+ */
+void lu_context_key_degister(struct lu_context_key *key)
+{
+ LASSERT(atomic_read(&key->lct_used) >= 1);
+ LINVRNT(0 <= key->lct_index && key->lct_index < ARRAY_SIZE(lu_keys));
+
+ lu_context_key_quiesce(key);
+
+ ++key_set_version;
+ spin_lock(&lu_keys_guard);
+ key_fini(&lu_shrink_env.le_ctx, key->lct_index);
+ if (lu_keys[key->lct_index]) {
+ lu_keys[key->lct_index] = NULL;
+ lu_ref_fini(&key->lct_reference);
+ }
+ spin_unlock(&lu_keys_guard);
+
+ LASSERTF(atomic_read(&key->lct_used) == 1,
+ "key has instances: %d\n",
+ atomic_read(&key->lct_used));
+}
+EXPORT_SYMBOL(lu_context_key_degister);
+
+/**
+ * Register a number of keys. This has to be called after all keys have been
+ * initialized by a call to LU_CONTEXT_KEY_INIT().
+ */
+int lu_context_key_register_many(struct lu_context_key *k, ...)
+{
+ struct lu_context_key *key = k;
+ va_list args;
+ int result;
+
+ va_start(args, k);
+ do {
+ result = lu_context_key_register(key);
+ if (result)
+ break;
+ key = va_arg(args, struct lu_context_key *);
+ } while (key != NULL);
+ va_end(args);
+
+ if (result != 0) {
+ va_start(args, k);
+ while (k != key) {
+ lu_context_key_degister(k);
+ k = va_arg(args, struct lu_context_key *);
+ }
+ va_end(args);
+ }
+
+ return result;
+}
+EXPORT_SYMBOL(lu_context_key_register_many);
+
+/**
+ * De-register a number of keys. This is a dual to
+ * lu_context_key_register_many().
+ */
+void lu_context_key_degister_many(struct lu_context_key *k, ...)
+{
+ va_list args;
+
+ va_start(args, k);
+ do {
+ lu_context_key_degister(k);
+ k = va_arg(args, struct lu_context_key*);
+ } while (k != NULL);
+ va_end(args);
+}
+EXPORT_SYMBOL(lu_context_key_degister_many);
+
+/**
+ * Revive a number of keys.
+ */
+void lu_context_key_revive_many(struct lu_context_key *k, ...)
+{
+ va_list args;
+
+ va_start(args, k);
+ do {
+ lu_context_key_revive(k);
+ k = va_arg(args, struct lu_context_key*);
+ } while (k != NULL);
+ va_end(args);
+}
+EXPORT_SYMBOL(lu_context_key_revive_many);
+
+/**
+ * Quiescent a number of keys.
+ */
+void lu_context_key_quiesce_many(struct lu_context_key *k, ...)
+{
+ va_list args;
+
+ va_start(args, k);
+ do {
+ lu_context_key_quiesce(k);
+ k = va_arg(args, struct lu_context_key*);
+ } while (k != NULL);
+ va_end(args);
+}
+EXPORT_SYMBOL(lu_context_key_quiesce_many);
+
+/**
+ * Return value associated with key \a key in context \a ctx.
+ */
+void *lu_context_key_get(const struct lu_context *ctx,
+ const struct lu_context_key *key)
+{
+ LINVRNT(ctx->lc_state == LCS_ENTERED);
+ LINVRNT(0 <= key->lct_index && key->lct_index < ARRAY_SIZE(lu_keys));
+ LASSERT(lu_keys[key->lct_index] == key);
+ return ctx->lc_value[key->lct_index];
+}
+EXPORT_SYMBOL(lu_context_key_get);
+
+/**
+ * List of remembered contexts. XXX document me.
+ */
+static LIST_HEAD(lu_context_remembered);
+
+/**
+ * Destroy \a key in all remembered contexts. This is used to destroy key
+ * values in "shared" contexts (like service threads), when a module owning
+ * the key is about to be unloaded.
+ */
+void lu_context_key_quiesce(struct lu_context_key *key)
+{
+ struct lu_context *ctx;
+
+ if (!(key->lct_tags & LCT_QUIESCENT)) {
+ /*
+ * XXX layering violation.
+ */
+ key->lct_tags |= LCT_QUIESCENT;
+ /*
+ * XXX memory barrier has to go here.
+ */
+ spin_lock(&lu_keys_guard);
+ list_for_each_entry(ctx, &lu_context_remembered,
+ lc_remember)
+ key_fini(ctx, key->lct_index);
+ spin_unlock(&lu_keys_guard);
+ ++key_set_version;
+ }
+}
+EXPORT_SYMBOL(lu_context_key_quiesce);
+
+void lu_context_key_revive(struct lu_context_key *key)
+{
+ key->lct_tags &= ~LCT_QUIESCENT;
+ ++key_set_version;
+}
+EXPORT_SYMBOL(lu_context_key_revive);
+
+static void keys_fini(struct lu_context *ctx)
+{
+ int i;
+
+ if (ctx->lc_value == NULL)
+ return;
+
+ for (i = 0; i < ARRAY_SIZE(lu_keys); ++i)
+ key_fini(ctx, i);
+
+ OBD_FREE(ctx->lc_value, ARRAY_SIZE(lu_keys) * sizeof(ctx->lc_value[0]));
+ ctx->lc_value = NULL;
+}
+
+static int keys_fill(struct lu_context *ctx)
+{
+ int i;
+
+ LINVRNT(ctx->lc_value != NULL);
+ for (i = 0; i < ARRAY_SIZE(lu_keys); ++i) {
+ struct lu_context_key *key;
+
+ key = lu_keys[i];
+ if (ctx->lc_value[i] == NULL && key != NULL &&
+ (key->lct_tags & ctx->lc_tags) &&
+ /*
+ * Don't create values for a LCT_QUIESCENT key, as this
+ * will pin module owning a key.
+ */
+ !(key->lct_tags & LCT_QUIESCENT)) {
+ void *value;
+
+ LINVRNT(key->lct_init != NULL);
+ LINVRNT(key->lct_index == i);
+
+ value = key->lct_init(ctx, key);
+ if (unlikely(IS_ERR(value)))
+ return PTR_ERR(value);
+
+ if (!(ctx->lc_tags & LCT_NOREF))
+ try_module_get(key->lct_owner);
+ lu_ref_add_atomic(&key->lct_reference, "ctx", ctx);
+ atomic_inc(&key->lct_used);
+ /*
+ * This is the only place in the code, where an
+ * element of ctx->lc_value[] array is set to non-NULL
+ * value.
+ */
+ ctx->lc_value[i] = value;
+ if (key->lct_exit != NULL)
+ ctx->lc_tags |= LCT_HAS_EXIT;
+ }
+ ctx->lc_version = key_set_version;
+ }
+ return 0;
+}
+
+static int keys_init(struct lu_context *ctx)
+{
+ OBD_ALLOC(ctx->lc_value,
+ ARRAY_SIZE(lu_keys) * sizeof(ctx->lc_value[0]));
+ if (likely(ctx->lc_value != NULL))
+ return keys_fill(ctx);
+
+ return -ENOMEM;
+}
+
+/**
+ * Initialize context data-structure. Create values for all keys.
+ */
+int lu_context_init(struct lu_context *ctx, __u32 tags)
+{
+ int rc;
+
+ memset(ctx, 0, sizeof(*ctx));
+ ctx->lc_state = LCS_INITIALIZED;
+ ctx->lc_tags = tags;
+ if (tags & LCT_REMEMBER) {
+ spin_lock(&lu_keys_guard);
+ list_add(&ctx->lc_remember, &lu_context_remembered);
+ spin_unlock(&lu_keys_guard);
+ } else {
+ INIT_LIST_HEAD(&ctx->lc_remember);
+ }
+
+ rc = keys_init(ctx);
+ if (rc != 0)
+ lu_context_fini(ctx);
+
+ return rc;
+}
+EXPORT_SYMBOL(lu_context_init);
+
+/**
+ * Finalize context data-structure. Destroy key values.
+ */
+void lu_context_fini(struct lu_context *ctx)
+{
+ LINVRNT(ctx->lc_state == LCS_INITIALIZED || ctx->lc_state == LCS_LEFT);
+ ctx->lc_state = LCS_FINALIZED;
+
+ if ((ctx->lc_tags & LCT_REMEMBER) == 0) {
+ LASSERT(list_empty(&ctx->lc_remember));
+ keys_fini(ctx);
+
+ } else { /* could race with key degister */
+ spin_lock(&lu_keys_guard);
+ keys_fini(ctx);
+ list_del_init(&ctx->lc_remember);
+ spin_unlock(&lu_keys_guard);
+ }
+}
+EXPORT_SYMBOL(lu_context_fini);
+
+/**
+ * Called before entering context.
+ */
+void lu_context_enter(struct lu_context *ctx)
+{
+ LINVRNT(ctx->lc_state == LCS_INITIALIZED || ctx->lc_state == LCS_LEFT);
+ ctx->lc_state = LCS_ENTERED;
+}
+EXPORT_SYMBOL(lu_context_enter);
+
+/**
+ * Called after exiting from \a ctx
+ */
+void lu_context_exit(struct lu_context *ctx)
+{
+ int i;
+
+ LINVRNT(ctx->lc_state == LCS_ENTERED);
+ ctx->lc_state = LCS_LEFT;
+ if (ctx->lc_tags & LCT_HAS_EXIT && ctx->lc_value != NULL) {
+ for (i = 0; i < ARRAY_SIZE(lu_keys); ++i) {
+ if (ctx->lc_value[i] != NULL) {
+ struct lu_context_key *key;
+
+ key = lu_keys[i];
+ LASSERT(key != NULL);
+ if (key->lct_exit != NULL)
+ key->lct_exit(ctx,
+ key, ctx->lc_value[i]);
+ }
+ }
+ }
+}
+EXPORT_SYMBOL(lu_context_exit);
+
+/**
+ * Allocate for context all missing keys that were registered after context
+ * creation. key_set_version is only changed in rare cases when modules
+ * are loaded and removed.
+ */
+int lu_context_refill(struct lu_context *ctx)
+{
+ return likely(ctx->lc_version == key_set_version) ? 0 : keys_fill(ctx);
+}
+EXPORT_SYMBOL(lu_context_refill);
+
+/**
+ * lu_ctx_tags/lu_ses_tags will be updated if there are new types of
+ * obd being added. Currently, this is only used on client side, specifically
+ * for echo device client, for other stack (like ptlrpc threads), context are
+ * predefined when the lu_device type are registered, during the module probe
+ * phase.
+ */
+__u32 lu_context_tags_default = 0;
+__u32 lu_session_tags_default = 0;
+
+void lu_context_tags_update(__u32 tags)
+{
+ spin_lock(&lu_keys_guard);
+ lu_context_tags_default |= tags;
+ key_set_version++;
+ spin_unlock(&lu_keys_guard);
+}
+EXPORT_SYMBOL(lu_context_tags_update);
+
+void lu_context_tags_clear(__u32 tags)
+{
+ spin_lock(&lu_keys_guard);
+ lu_context_tags_default &= ~tags;
+ key_set_version++;
+ spin_unlock(&lu_keys_guard);
+}
+EXPORT_SYMBOL(lu_context_tags_clear);
+
+void lu_session_tags_update(__u32 tags)
+{
+ spin_lock(&lu_keys_guard);
+ lu_session_tags_default |= tags;
+ key_set_version++;
+ spin_unlock(&lu_keys_guard);
+}
+EXPORT_SYMBOL(lu_session_tags_update);
+
+void lu_session_tags_clear(__u32 tags)
+{
+ spin_lock(&lu_keys_guard);
+ lu_session_tags_default &= ~tags;
+ key_set_version++;
+ spin_unlock(&lu_keys_guard);
+}
+EXPORT_SYMBOL(lu_session_tags_clear);
+
+int lu_env_init(struct lu_env *env, __u32 tags)
+{
+ int result;
+
+ env->le_ses = NULL;
+ result = lu_context_init(&env->le_ctx, tags);
+ if (likely(result == 0))
+ lu_context_enter(&env->le_ctx);
+ return result;
+}
+EXPORT_SYMBOL(lu_env_init);
+
+void lu_env_fini(struct lu_env *env)
+{
+ lu_context_exit(&env->le_ctx);
+ lu_context_fini(&env->le_ctx);
+ env->le_ses = NULL;
+}
+EXPORT_SYMBOL(lu_env_fini);
+
+int lu_env_refill(struct lu_env *env)
+{
+ int result;
+
+ result = lu_context_refill(&env->le_ctx);
+ if (result == 0 && env->le_ses != NULL)
+ result = lu_context_refill(env->le_ses);
+ return result;
+}
+EXPORT_SYMBOL(lu_env_refill);
+
+/**
+ * Currently, this API will only be used by echo client.
+ * Because echo client and normal lustre client will share
+ * same cl_env cache. So echo client needs to refresh
+ * the env context after it get one from the cache, especially
+ * when normal client and echo client co-exist in the same client.
+ */
+int lu_env_refill_by_tags(struct lu_env *env, __u32 ctags,
+ __u32 stags)
+{
+ if ((env->le_ctx.lc_tags & ctags) != ctags) {
+ env->le_ctx.lc_version = 0;
+ env->le_ctx.lc_tags |= ctags;
+ }
+
+ if (env->le_ses && (env->le_ses->lc_tags & stags) != stags) {
+ env->le_ses->lc_version = 0;
+ env->le_ses->lc_tags |= stags;
+ }
+
+ return lu_env_refill(env);
+}
+EXPORT_SYMBOL(lu_env_refill_by_tags);
+
+
+typedef struct lu_site_stats{
+ unsigned lss_populated;
+ unsigned lss_max_search;
+ unsigned lss_total;
+ unsigned lss_busy;
+} lu_site_stats_t;
+
+static void lu_site_stats_get(struct cfs_hash *hs,
+ lu_site_stats_t *stats, int populated)
+{
+ struct cfs_hash_bd bd;
+ int i;
+
+ cfs_hash_for_each_bucket(hs, &bd, i) {
+ struct lu_site_bkt_data *bkt = cfs_hash_bd_extra_get(hs, &bd);
+ struct hlist_head *hhead;
+
+ cfs_hash_bd_lock(hs, &bd, 1);
+ stats->lss_busy += bkt->lsb_busy;
+ stats->lss_total += cfs_hash_bd_count_get(&bd);
+ stats->lss_max_search = max((int)stats->lss_max_search,
+ cfs_hash_bd_depmax_get(&bd));
+ if (!populated) {
+ cfs_hash_bd_unlock(hs, &bd, 1);
+ continue;
+ }
+
+ cfs_hash_bd_for_each_hlist(hs, &bd, hhead) {
+ if (!hlist_empty(hhead))
+ stats->lss_populated++;
+ }
+ cfs_hash_bd_unlock(hs, &bd, 1);
+ }
+}
+
+
+/*
+ * There exists a potential lock inversion deadlock scenario when using
+ * Lustre on top of ZFS. This occurs between one of ZFS's
+ * buf_hash_table.ht_lock's, and Lustre's lu_sites_guard lock. Essentially,
+ * thread A will take the lu_sites_guard lock and sleep on the ht_lock,
+ * while thread B will take the ht_lock and sleep on the lu_sites_guard
+ * lock. Obviously neither thread will wake and drop their respective hold
+ * on their lock.
+ *
+ * To prevent this from happening we must ensure the lu_sites_guard lock is
+ * not taken while down this code path. ZFS reliably does not set the
+ * __GFP_FS bit in its code paths, so this can be used to determine if it
+ * is safe to take the lu_sites_guard lock.
+ *
+ * Ideally we should accurately return the remaining number of cached
+ * objects without taking the lu_sites_guard lock, but this is not
+ * possible in the current implementation.
+ */
+static unsigned long lu_cache_shrink_count(struct shrinker *sk,
+ struct shrink_control *sc)
+{
+ lu_site_stats_t stats;
+ struct lu_site *s;
+ struct lu_site *tmp;
+ unsigned long cached = 0;
+
+ if (!(sc->gfp_mask & __GFP_FS))
+ return 0;
+
+ mutex_lock(&lu_sites_guard);
+ list_for_each_entry_safe(s, tmp, &lu_sites, ls_linkage) {
+ memset(&stats, 0, sizeof(stats));
+ lu_site_stats_get(s->ls_obj_hash, &stats, 0);
+ cached += stats.lss_total - stats.lss_busy;
+ }
+ mutex_unlock(&lu_sites_guard);
+
+ cached = (cached / 100) * sysctl_vfs_cache_pressure;
+ CDEBUG(D_INODE, "%ld objects cached\n", cached);
+ return cached;
+}
+
+static unsigned long lu_cache_shrink_scan(struct shrinker *sk,
+ struct shrink_control *sc)
+{
+ struct lu_site *s;
+ struct lu_site *tmp;
+ unsigned long remain = sc->nr_to_scan, freed = 0;
+ LIST_HEAD(splice);
+
+ if (!(sc->gfp_mask & __GFP_FS))
+ /* We must not take the lu_sites_guard lock when
+ * __GFP_FS is *not* set because of the deadlock
+ * possibility detailed above. Additionally,
+ * since we cannot determine the number of
+ * objects in the cache without taking this
+ * lock, we're in a particularly tough spot. As
+ * a result, we'll just lie and say our cache is
+ * empty. This _should_ be ok, as we can't
+ * reclaim objects when __GFP_FS is *not* set
+ * anyways.
+ */
+ return SHRINK_STOP;
+
+ mutex_lock(&lu_sites_guard);
+ list_for_each_entry_safe(s, tmp, &lu_sites, ls_linkage) {
+ freed = lu_site_purge(&lu_shrink_env, s, remain);
+ remain -= freed;
+ /*
+ * Move just shrunk site to the tail of site list to
+ * assure shrinking fairness.
+ */
+ list_move_tail(&s->ls_linkage, &splice);
+ }
+ list_splice(&splice, lu_sites.prev);
+ mutex_unlock(&lu_sites_guard);
+
+ return sc->nr_to_scan - remain;
+}
+
+/*
+ * Debugging stuff.
+ */
+
+/**
+ * Environment to be used in debugger, contains all tags.
+ */
+struct lu_env lu_debugging_env;
+
+/**
+ * Debugging printer function using printk().
+ */
+int lu_printk_printer(const struct lu_env *env,
+ void *unused, const char *format, ...)
+{
+ va_list args;
+
+ va_start(args, format);
+ vprintk(format, args);
+ va_end(args);
+ return 0;
+}
+
+static struct shrinker lu_site_shrinker = {
+ .count_objects = lu_cache_shrink_count,
+ .scan_objects = lu_cache_shrink_scan,
+ .seeks = DEFAULT_SEEKS,
+};
+
+/**
+ * Initialization of global lu_* data.
+ */
+int lu_global_init(void)
+{
+ int result;
+
+ CDEBUG(D_INFO, "Lustre LU module (%p).\n", &lu_keys);
+
+ result = lu_ref_global_init();
+ if (result != 0)
+ return result;
+
+ LU_CONTEXT_KEY_INIT(&lu_global_key);
+ result = lu_context_key_register(&lu_global_key);
+ if (result != 0)
+ return result;
+
+ /*
+ * At this level, we don't know what tags are needed, so allocate them
+ * conservatively. This should not be too bad, because this
+ * environment is global.
+ */
+ mutex_lock(&lu_sites_guard);
+ result = lu_env_init(&lu_shrink_env, LCT_SHRINKER);
+ mutex_unlock(&lu_sites_guard);
+ if (result != 0)
+ return result;
+
+ /*
+ * seeks estimation: 3 seeks to read a record from oi, one to read
+ * inode, one for ea. Unfortunately setting this high value results in
+ * lu_object/inode cache consuming all the memory.
+ */
+ register_shrinker(&lu_site_shrinker);
+
+ return result;
+}
+
+/**
+ * Dual to lu_global_init().
+ */
+void lu_global_fini(void)
+{
+ unregister_shrinker(&lu_site_shrinker);
+ lu_context_key_degister(&lu_global_key);
+
+ /*
+ * Tear shrinker environment down _after_ de-registering
+ * lu_global_key, because the latter has a value in the former.
+ */
+ mutex_lock(&lu_sites_guard);
+ lu_env_fini(&lu_shrink_env);
+ mutex_unlock(&lu_sites_guard);
+
+ lu_ref_global_fini();
+}
+
+static __u32 ls_stats_read(struct lprocfs_stats *stats, int idx)
+{
+#if defined (CONFIG_PROC_FS)
+ struct lprocfs_counter ret;
+
+ lprocfs_stats_collect(stats, idx, &ret);
+ return (__u32)ret.lc_count;
+#else
+ return 0;
+#endif
+}
+
+/**
+ * Output site statistical counters into a buffer. Suitable for
+ * lprocfs_rd_*()-style functions.
+ */
+int lu_site_stats_print(const struct lu_site *s, struct seq_file *m)
+{
+ lu_site_stats_t stats;
+
+ memset(&stats, 0, sizeof(stats));
+ lu_site_stats_get(s->ls_obj_hash, &stats, 1);
+
+ seq_printf(m, "%d/%d %d/%d %d %d %d %d %d %d %d\n",
+ stats.lss_busy,
+ stats.lss_total,
+ stats.lss_populated,
+ CFS_HASH_NHLIST(s->ls_obj_hash),
+ stats.lss_max_search,
+ ls_stats_read(s->ls_stats, LU_SS_CREATED),
+ ls_stats_read(s->ls_stats, LU_SS_CACHE_HIT),
+ ls_stats_read(s->ls_stats, LU_SS_CACHE_MISS),
+ ls_stats_read(s->ls_stats, LU_SS_CACHE_RACE),
+ ls_stats_read(s->ls_stats, LU_SS_CACHE_DEATH_RACE),
+ ls_stats_read(s->ls_stats, LU_SS_LRU_PURGED));
+ return 0;
+}
+EXPORT_SYMBOL(lu_site_stats_print);
+
+/**
+ * Helper function to initialize a number of kmem slab caches at once.
+ */
+int lu_kmem_init(struct lu_kmem_descr *caches)
+{
+ int result;
+ struct lu_kmem_descr *iter = caches;
+
+ for (result = 0; iter->ckd_cache != NULL; ++iter) {
+ *iter->ckd_cache = kmem_cache_create(iter->ckd_name,
+ iter->ckd_size,
+ 0, 0, NULL);
+ if (*iter->ckd_cache == NULL) {
+ result = -ENOMEM;
+ /* free all previously allocated caches */
+ lu_kmem_fini(caches);
+ break;
+ }
+ }
+ return result;
+}
+EXPORT_SYMBOL(lu_kmem_init);
+
+/**
+ * Helper function to finalize a number of kmem slab cached at once. Dual to
+ * lu_kmem_init().
+ */
+void lu_kmem_fini(struct lu_kmem_descr *caches)
+{
+ for (; caches->ckd_cache != NULL; ++caches) {
+ if (*caches->ckd_cache != NULL) {
+ kmem_cache_destroy(*caches->ckd_cache);
+ *caches->ckd_cache = NULL;
+ }
+ }
+}
+EXPORT_SYMBOL(lu_kmem_fini);
+
+/**
+ * Temporary solution to be able to assign fid in ->do_create()
+ * till we have fully-functional OST fids
+ */
+void lu_object_assign_fid(const struct lu_env *env, struct lu_object *o,
+ const struct lu_fid *fid)
+{
+ struct lu_site *s = o->lo_dev->ld_site;
+ struct lu_fid *old = &o->lo_header->loh_fid;
+ struct lu_site_bkt_data *bkt;
+ struct lu_object *shadow;
+ wait_queue_t waiter;
+ struct cfs_hash *hs;
+ struct cfs_hash_bd bd;
+ __u64 version = 0;
+
+ LASSERT(fid_is_zero(old));
+
+ hs = s->ls_obj_hash;
+ cfs_hash_bd_get_and_lock(hs, (void *)fid, &bd, 1);
+ shadow = htable_lookup(s, &bd, fid, &waiter, &version);
+ /* supposed to be unique */
+ LASSERT(IS_ERR(shadow) && PTR_ERR(shadow) == -ENOENT);
+ *old = *fid;
+ bkt = cfs_hash_bd_extra_get(hs, &bd);
+ cfs_hash_bd_add_locked(hs, &bd, &o->lo_header->loh_hash);
+ bkt->lsb_busy++;
+ cfs_hash_bd_unlock(hs, &bd, 1);
+}
+EXPORT_SYMBOL(lu_object_assign_fid);
+
+/**
+ * allocates object with 0 (non-assigned) fid
+ * XXX: temporary solution to be able to assign fid in ->do_create()
+ * till we have fully-functional OST fids
+ */
+struct lu_object *lu_object_anon(const struct lu_env *env,
+ struct lu_device *dev,
+ const struct lu_object_conf *conf)
+{
+ struct lu_fid fid;
+ struct lu_object *o;
+
+ fid_zero(&fid);
+ o = lu_object_alloc(env, dev, &fid, conf);
+
+ return o;
+}
+EXPORT_SYMBOL(lu_object_anon);
+
+struct lu_buf LU_BUF_NULL = {
+ .lb_buf = NULL,
+ .lb_len = 0
+};
+EXPORT_SYMBOL(LU_BUF_NULL);
+
+void lu_buf_free(struct lu_buf *buf)
+{
+ LASSERT(buf);
+ if (buf->lb_buf) {
+ LASSERT(buf->lb_len > 0);
+ OBD_FREE_LARGE(buf->lb_buf, buf->lb_len);
+ buf->lb_buf = NULL;
+ buf->lb_len = 0;
+ }
+}
+EXPORT_SYMBOL(lu_buf_free);
+
+void lu_buf_alloc(struct lu_buf *buf, int size)
+{
+ LASSERT(buf);
+ LASSERT(buf->lb_buf == NULL);
+ LASSERT(buf->lb_len == 0);
+ OBD_ALLOC_LARGE(buf->lb_buf, size);
+ if (likely(buf->lb_buf))
+ buf->lb_len = size;
+}
+EXPORT_SYMBOL(lu_buf_alloc);
+
+void lu_buf_realloc(struct lu_buf *buf, int size)
+{
+ lu_buf_free(buf);
+ lu_buf_alloc(buf, size);
+}
+EXPORT_SYMBOL(lu_buf_realloc);
+
+struct lu_buf *lu_buf_check_and_alloc(struct lu_buf *buf, int len)
+{
+ if (buf->lb_buf == NULL && buf->lb_len == 0)
+ lu_buf_alloc(buf, len);
+
+ if ((len > buf->lb_len) && (buf->lb_buf != NULL))
+ lu_buf_realloc(buf, len);
+
+ return buf;
+}
+EXPORT_SYMBOL(lu_buf_check_and_alloc);
+
+/**
+ * Increase the size of the \a buf.
+ * preserves old data in buffer
+ * old buffer remains unchanged on error
+ * \retval 0 or -ENOMEM
+ */
+int lu_buf_check_and_grow(struct lu_buf *buf, int len)
+{
+ char *ptr;
+
+ if (len <= buf->lb_len)
+ return 0;
+
+ OBD_ALLOC_LARGE(ptr, len);
+ if (ptr == NULL)
+ return -ENOMEM;
+
+ /* Free the old buf */
+ if (buf->lb_buf != NULL) {
+ memcpy(ptr, buf->lb_buf, buf->lb_len);
+ OBD_FREE_LARGE(buf->lb_buf, buf->lb_len);
+ }
+
+ buf->lb_buf = ptr;
+ buf->lb_len = len;
+ return 0;
+}
+EXPORT_SYMBOL(lu_buf_check_and_grow);
diff --git a/drivers/staging/lustre/lustre/obdclass/lu_ref.c b/drivers/staging/lustre/lustre/obdclass/lu_ref.c
new file mode 100644
index 000000000..993697b66
--- /dev/null
+++ b/drivers/staging/lustre/lustre/obdclass/lu_ref.c
@@ -0,0 +1,50 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2012, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ *
+ * lustre/obdclass/lu_ref.c
+ *
+ * Lustre reference.
+ *
+ * Author: Nikita Danilov <nikita.danilov@sun.com>
+ */
+
+#define DEBUG_SUBSYSTEM S_CLASS
+
+#include "../../include/linux/libcfs/libcfs.h"
+
+#include "../include/obd.h"
+#include "../include/obd_class.h"
+#include "../include/obd_support.h"
+#include "../include/lu_ref.h"
diff --git a/drivers/staging/lustre/lustre/obdclass/lustre_handles.c b/drivers/staging/lustre/lustre/obdclass/lustre_handles.c
new file mode 100644
index 000000000..f720e3183
--- /dev/null
+++ b/drivers/staging/lustre/lustre/obdclass/lustre_handles.c
@@ -0,0 +1,257 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2011, 2012, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ *
+ * lustre/obdclass/lustre_handles.c
+ *
+ * Author: Phil Schwan <phil@clusterfs.com>
+ */
+
+#define DEBUG_SUBSYSTEM S_CLASS
+
+#include "../include/obd_support.h"
+#include "../include/lustre_handles.h"
+#include "../include/lustre_lib.h"
+
+
+static __u64 handle_base;
+#define HANDLE_INCR 7
+static spinlock_t handle_base_lock;
+
+static struct handle_bucket {
+ spinlock_t lock;
+ struct list_head head;
+} *handle_hash;
+
+#define HANDLE_HASH_SIZE (1 << 16)
+#define HANDLE_HASH_MASK (HANDLE_HASH_SIZE - 1)
+
+/*
+ * Generate a unique 64bit cookie (hash) for a handle and insert it into
+ * global (per-node) hash-table.
+ */
+void class_handle_hash(struct portals_handle *h,
+ struct portals_handle_ops *ops)
+{
+ struct handle_bucket *bucket;
+
+ LASSERT(h != NULL);
+ LASSERT(list_empty(&h->h_link));
+
+ /*
+ * This is fast, but simplistic cookie generation algorithm, it will
+ * need a re-do at some point in the future for security.
+ */
+ spin_lock(&handle_base_lock);
+ handle_base += HANDLE_INCR;
+
+ if (unlikely(handle_base == 0)) {
+ /*
+ * Cookie of zero is "dangerous", because in many places it's
+ * assumed that 0 means "unassigned" handle, not bound to any
+ * object.
+ */
+ CWARN("The universe has been exhausted: cookie wrap-around.\n");
+ handle_base += HANDLE_INCR;
+ }
+ h->h_cookie = handle_base;
+ spin_unlock(&handle_base_lock);
+
+ h->h_ops = ops;
+ spin_lock_init(&h->h_lock);
+
+ bucket = &handle_hash[h->h_cookie & HANDLE_HASH_MASK];
+ spin_lock(&bucket->lock);
+ list_add_rcu(&h->h_link, &bucket->head);
+ h->h_in = 1;
+ spin_unlock(&bucket->lock);
+
+ CDEBUG(D_INFO, "added object %p with handle %#llx to hash\n",
+ h, h->h_cookie);
+}
+EXPORT_SYMBOL(class_handle_hash);
+
+static void class_handle_unhash_nolock(struct portals_handle *h)
+{
+ if (list_empty(&h->h_link)) {
+ CERROR("removing an already-removed handle (%#llx)\n",
+ h->h_cookie);
+ return;
+ }
+
+ CDEBUG(D_INFO, "removing object %p with handle %#llx from hash\n",
+ h, h->h_cookie);
+
+ spin_lock(&h->h_lock);
+ if (h->h_in == 0) {
+ spin_unlock(&h->h_lock);
+ return;
+ }
+ h->h_in = 0;
+ spin_unlock(&h->h_lock);
+ list_del_rcu(&h->h_link);
+}
+
+void class_handle_unhash(struct portals_handle *h)
+{
+ struct handle_bucket *bucket;
+ bucket = handle_hash + (h->h_cookie & HANDLE_HASH_MASK);
+
+ spin_lock(&bucket->lock);
+ class_handle_unhash_nolock(h);
+ spin_unlock(&bucket->lock);
+}
+EXPORT_SYMBOL(class_handle_unhash);
+
+void class_handle_hash_back(struct portals_handle *h)
+{
+ struct handle_bucket *bucket;
+
+ bucket = handle_hash + (h->h_cookie & HANDLE_HASH_MASK);
+
+ spin_lock(&bucket->lock);
+ list_add_rcu(&h->h_link, &bucket->head);
+ h->h_in = 1;
+ spin_unlock(&bucket->lock);
+}
+EXPORT_SYMBOL(class_handle_hash_back);
+
+void *class_handle2object(__u64 cookie)
+{
+ struct handle_bucket *bucket;
+ struct portals_handle *h;
+ void *retval = NULL;
+
+ LASSERT(handle_hash != NULL);
+
+ /* Be careful when you want to change this code. See the
+ * rcu_read_lock() definition on top this file. - jxiong */
+ bucket = handle_hash + (cookie & HANDLE_HASH_MASK);
+
+ rcu_read_lock();
+ list_for_each_entry_rcu(h, &bucket->head, h_link) {
+ if (h->h_cookie != cookie)
+ continue;
+
+ spin_lock(&h->h_lock);
+ if (likely(h->h_in != 0)) {
+ h->h_ops->hop_addref(h);
+ retval = h;
+ }
+ spin_unlock(&h->h_lock);
+ break;
+ }
+ rcu_read_unlock();
+
+ return retval;
+}
+EXPORT_SYMBOL(class_handle2object);
+
+void class_handle_free_cb(struct rcu_head *rcu)
+{
+ struct portals_handle *h = RCU2HANDLE(rcu);
+ void *ptr = (void *)(unsigned long)h->h_cookie;
+
+ if (h->h_ops->hop_free != NULL)
+ h->h_ops->hop_free(ptr, h->h_size);
+ else
+ OBD_FREE(ptr, h->h_size);
+}
+EXPORT_SYMBOL(class_handle_free_cb);
+
+int class_handle_init(void)
+{
+ struct handle_bucket *bucket;
+ struct timeval tv;
+ int seed[2];
+
+ LASSERT(handle_hash == NULL);
+
+ OBD_ALLOC_LARGE(handle_hash, sizeof(*bucket) * HANDLE_HASH_SIZE);
+ if (handle_hash == NULL)
+ return -ENOMEM;
+
+ spin_lock_init(&handle_base_lock);
+ for (bucket = handle_hash + HANDLE_HASH_SIZE - 1; bucket >= handle_hash;
+ bucket--) {
+ INIT_LIST_HEAD(&bucket->head);
+ spin_lock_init(&bucket->lock);
+ }
+
+ /** bug 21430: add randomness to the initial base */
+ cfs_get_random_bytes(seed, sizeof(seed));
+ do_gettimeofday(&tv);
+ cfs_srand(tv.tv_sec ^ seed[0], tv.tv_usec ^ seed[1]);
+
+ cfs_get_random_bytes(&handle_base, sizeof(handle_base));
+ LASSERT(handle_base != 0ULL);
+
+ return 0;
+}
+
+static int cleanup_all_handles(void)
+{
+ int rc;
+ int i;
+
+ for (rc = i = 0; i < HANDLE_HASH_SIZE; i++) {
+ struct portals_handle *h;
+
+ spin_lock(&handle_hash[i].lock);
+ list_for_each_entry_rcu(h, &(handle_hash[i].head), h_link) {
+ CERROR("force clean handle %#llx addr %p ops %p\n",
+ h->h_cookie, h, h->h_ops);
+
+ class_handle_unhash_nolock(h);
+ rc++;
+ }
+ spin_unlock(&handle_hash[i].lock);
+ }
+
+ return rc;
+}
+
+void class_handle_cleanup(void)
+{
+ int count;
+ LASSERT(handle_hash != NULL);
+
+ count = cleanup_all_handles();
+
+ OBD_FREE_LARGE(handle_hash, sizeof(*handle_hash) * HANDLE_HASH_SIZE);
+ handle_hash = NULL;
+
+ if (count != 0)
+ CERROR("handle_count at cleanup: %d\n", count);
+}
diff --git a/drivers/staging/lustre/lustre/obdclass/lustre_peer.c b/drivers/staging/lustre/lustre/obdclass/lustre_peer.c
new file mode 100644
index 000000000..64b2f35e2
--- /dev/null
+++ b/drivers/staging/lustre/lustre/obdclass/lustre_peer.c
@@ -0,0 +1,217 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2011, 2012, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ */
+
+#define DEBUG_SUBSYSTEM S_RPC
+
+#include "../include/obd.h"
+#include "../include/obd_support.h"
+#include "../include/obd_class.h"
+#include "../include/lustre_lib.h"
+#include "../include/lustre_ha.h"
+#include "../include/lustre_net.h"
+#include "../include/lprocfs_status.h"
+
+#define NIDS_MAX 32
+
+struct uuid_nid_data {
+ struct list_head un_list;
+ struct obd_uuid un_uuid;
+ int un_nid_count;
+ lnet_nid_t un_nids[NIDS_MAX];
+};
+
+/* FIXME: This should probably become more elegant than a global linked list */
+static struct list_head g_uuid_list;
+static spinlock_t g_uuid_lock;
+
+void class_init_uuidlist(void)
+{
+ INIT_LIST_HEAD(&g_uuid_list);
+ spin_lock_init(&g_uuid_lock);
+}
+
+void class_exit_uuidlist(void)
+{
+ /* delete all */
+ class_del_uuid(NULL);
+}
+
+int lustre_uuid_to_peer(const char *uuid, lnet_nid_t *peer_nid, int index)
+{
+ struct uuid_nid_data *data;
+ struct obd_uuid tmp;
+ int rc = -ENOENT;
+
+ obd_str2uuid(&tmp, uuid);
+ spin_lock(&g_uuid_lock);
+ list_for_each_entry(data, &g_uuid_list, un_list) {
+ if (obd_uuid_equals(&data->un_uuid, &tmp)) {
+ if (index >= data->un_nid_count)
+ break;
+
+ rc = 0;
+ *peer_nid = data->un_nids[index];
+ break;
+ }
+ }
+ spin_unlock(&g_uuid_lock);
+ return rc;
+}
+EXPORT_SYMBOL(lustre_uuid_to_peer);
+
+/* Add a nid to a niduuid. Multiple nids can be added to a single uuid;
+ LNET will choose the best one. */
+int class_add_uuid(const char *uuid, __u64 nid)
+{
+ struct uuid_nid_data *data, *entry;
+ int found = 0;
+
+ LASSERT(nid != 0); /* valid newconfig NID is never zero */
+
+ if (strlen(uuid) > UUID_MAX - 1)
+ return -EOVERFLOW;
+
+ OBD_ALLOC_PTR(data);
+ if (data == NULL)
+ return -ENOMEM;
+
+ obd_str2uuid(&data->un_uuid, uuid);
+ data->un_nids[0] = nid;
+ data->un_nid_count = 1;
+
+ spin_lock(&g_uuid_lock);
+ list_for_each_entry(entry, &g_uuid_list, un_list) {
+ if (obd_uuid_equals(&entry->un_uuid, &data->un_uuid)) {
+ int i;
+
+ found = 1;
+ for (i = 0; i < entry->un_nid_count; i++)
+ if (nid == entry->un_nids[i])
+ break;
+
+ if (i == entry->un_nid_count) {
+ LASSERT(entry->un_nid_count < NIDS_MAX);
+ entry->un_nids[entry->un_nid_count++] = nid;
+ }
+ break;
+ }
+ }
+ if (!found)
+ list_add(&data->un_list, &g_uuid_list);
+ spin_unlock(&g_uuid_lock);
+
+ if (found) {
+ CDEBUG(D_INFO, "found uuid %s %s cnt=%d\n", uuid,
+ libcfs_nid2str(nid), entry->un_nid_count);
+ OBD_FREE(data, sizeof(*data));
+ } else {
+ CDEBUG(D_INFO, "add uuid %s %s\n", uuid, libcfs_nid2str(nid));
+ }
+ return 0;
+}
+EXPORT_SYMBOL(class_add_uuid);
+
+/* Delete the nids for one uuid if specified, otherwise delete all */
+int class_del_uuid(const char *uuid)
+{
+ LIST_HEAD(deathrow);
+ struct uuid_nid_data *data;
+
+ spin_lock(&g_uuid_lock);
+ if (uuid != NULL) {
+ struct obd_uuid tmp;
+
+ obd_str2uuid(&tmp, uuid);
+ list_for_each_entry(data, &g_uuid_list, un_list) {
+ if (obd_uuid_equals(&data->un_uuid, &tmp)) {
+ list_move(&data->un_list, &deathrow);
+ break;
+ }
+ }
+ } else
+ list_splice_init(&g_uuid_list, &deathrow);
+ spin_unlock(&g_uuid_lock);
+
+ if (uuid != NULL && list_empty(&deathrow)) {
+ CDEBUG(D_INFO, "Try to delete a non-existent uuid %s\n", uuid);
+ return -EINVAL;
+ }
+
+ while (!list_empty(&deathrow)) {
+ data = list_entry(deathrow.next, struct uuid_nid_data,
+ un_list);
+ list_del(&data->un_list);
+
+ CDEBUG(D_INFO, "del uuid %s %s/%d\n",
+ obd_uuid2str(&data->un_uuid),
+ libcfs_nid2str(data->un_nids[0]),
+ data->un_nid_count);
+
+ OBD_FREE(data, sizeof(*data));
+ }
+
+ return 0;
+}
+
+/* check if @nid exists in nid list of @uuid */
+int class_check_uuid(struct obd_uuid *uuid, __u64 nid)
+{
+ struct uuid_nid_data *entry;
+ int found = 0;
+
+ CDEBUG(D_INFO, "check if uuid %s has %s.\n",
+ obd_uuid2str(uuid), libcfs_nid2str(nid));
+
+ spin_lock(&g_uuid_lock);
+ list_for_each_entry(entry, &g_uuid_list, un_list) {
+ int i;
+
+ if (!obd_uuid_equals(&entry->un_uuid, uuid))
+ continue;
+
+ /* found the uuid, check if it has @nid */
+ for (i = 0; i < entry->un_nid_count; i++) {
+ if (entry->un_nids[i] == nid) {
+ found = 1;
+ break;
+ }
+ }
+ break;
+ }
+ spin_unlock(&g_uuid_lock);
+ return found;
+}
+EXPORT_SYMBOL(class_check_uuid);
diff --git a/drivers/staging/lustre/lustre/obdclass/obd_config.c b/drivers/staging/lustre/lustre/obdclass/obd_config.c
new file mode 100644
index 000000000..6ce9adc2f
--- /dev/null
+++ b/drivers/staging/lustre/lustre/obdclass/obd_config.c
@@ -0,0 +1,1953 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2011, 2012, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ *
+ * lustre/obdclass/obd_config.c
+ *
+ * Config API
+ */
+
+#define DEBUG_SUBSYSTEM S_CLASS
+#include "../include/obd_class.h"
+#include <linux/string.h>
+#include "../include/lustre_log.h"
+#include "../include/lprocfs_status.h"
+#include "../include/lustre_param.h"
+
+#include "llog_internal.h"
+
+static cfs_hash_ops_t uuid_hash_ops;
+static cfs_hash_ops_t nid_hash_ops;
+static cfs_hash_ops_t nid_stat_hash_ops;
+
+/*********** string parsing utils *********/
+
+/* returns 0 if we find this key in the buffer, else 1 */
+int class_find_param(char *buf, char *key, char **valp)
+{
+ char *ptr;
+
+ if (!buf)
+ return 1;
+
+ ptr = strstr(buf, key);
+ if (ptr == NULL)
+ return 1;
+
+ if (valp)
+ *valp = ptr + strlen(key);
+
+ return 0;
+}
+EXPORT_SYMBOL(class_find_param);
+
+/**
+ * Check whether the proc parameter \a param is an old parameter or not from
+ * the array \a ptr which contains the mapping from old parameters to new ones.
+ * If it's an old one, then return the pointer to the cfg_interop_param struc-
+ * ture which contains both the old and new parameters.
+ *
+ * \param param proc parameter
+ * \param ptr an array which contains the mapping from
+ * old parameters to new ones
+ *
+ * \retval valid-pointer pointer to the cfg_interop_param structure
+ * which contains the old and new parameters
+ * \retval NULL \a param or \a ptr is NULL,
+ * or \a param is not an old parameter
+ */
+struct cfg_interop_param *class_find_old_param(const char *param,
+ struct cfg_interop_param *ptr)
+{
+ char *value = NULL;
+ int name_len = 0;
+
+ if (param == NULL || ptr == NULL)
+ return NULL;
+
+ value = strchr(param, '=');
+ if (value == NULL)
+ name_len = strlen(param);
+ else
+ name_len = value - param;
+
+ while (ptr->old_param != NULL) {
+ if (strncmp(param, ptr->old_param, name_len) == 0 &&
+ name_len == strlen(ptr->old_param))
+ return ptr;
+ ptr++;
+ }
+
+ return NULL;
+}
+EXPORT_SYMBOL(class_find_old_param);
+
+/**
+ * Finds a parameter in \a params and copies it to \a copy.
+ *
+ * Leading spaces are skipped. Next space or end of string is the
+ * parameter terminator with the exception that spaces inside single or double
+ * quotes get included into a parameter. The parameter is copied into \a copy
+ * which has to be allocated big enough by a caller, quotes are stripped in
+ * the copy and the copy is terminated by 0.
+ *
+ * On return \a params is set to next parameter or to NULL if last
+ * parameter is returned.
+ *
+ * \retval 0 if parameter is returned in \a copy
+ * \retval 1 otherwise
+ * \retval -EINVAL if unbalanced quota is found
+ */
+int class_get_next_param(char **params, char *copy)
+{
+ char *q1, *q2, *str;
+ int len;
+
+ str = *params;
+ while (*str == ' ')
+ str++;
+
+ if (*str == '\0') {
+ *params = NULL;
+ return 1;
+ }
+
+ while (1) {
+ q1 = strpbrk(str, " '\"");
+ if (q1 == NULL) {
+ len = strlen(str);
+ memcpy(copy, str, len);
+ copy[len] = '\0';
+ *params = NULL;
+ return 0;
+ }
+ len = q1 - str;
+ if (*q1 == ' ') {
+ memcpy(copy, str, len);
+ copy[len] = '\0';
+ *params = str + len;
+ return 0;
+ }
+
+ memcpy(copy, str, len);
+ copy += len;
+
+ /* search for the matching closing quote */
+ str = q1 + 1;
+ q2 = strchr(str, *q1);
+ if (q2 == NULL) {
+ CERROR("Unbalanced quota in parameters: \"%s\"\n",
+ *params);
+ return -EINVAL;
+ }
+ len = q2 - str;
+ memcpy(copy, str, len);
+ copy += len;
+ str = q2 + 1;
+ }
+ return 1;
+}
+EXPORT_SYMBOL(class_get_next_param);
+
+/* returns 0 if this is the first key in the buffer, else 1.
+ valp points to first char after key. */
+int class_match_param(char *buf, char *key, char **valp)
+{
+ if (!buf)
+ return 1;
+
+ if (memcmp(buf, key, strlen(key)) != 0)
+ return 1;
+
+ if (valp)
+ *valp = buf + strlen(key);
+
+ return 0;
+}
+EXPORT_SYMBOL(class_match_param);
+
+static int parse_nid(char *buf, void *value, int quiet)
+{
+ lnet_nid_t *nid = (lnet_nid_t *)value;
+
+ *nid = libcfs_str2nid(buf);
+ if (*nid != LNET_NID_ANY)
+ return 0;
+
+ if (!quiet)
+ LCONSOLE_ERROR_MSG(0x159, "Can't parse NID '%s'\n", buf);
+ return -EINVAL;
+}
+
+static int parse_net(char *buf, void *value)
+{
+ __u32 *net = (__u32 *)value;
+
+ *net = libcfs_str2net(buf);
+ CDEBUG(D_INFO, "Net %s\n", libcfs_net2str(*net));
+ return 0;
+}
+
+enum {
+ CLASS_PARSE_NID = 1,
+ CLASS_PARSE_NET,
+};
+
+/* 0 is good nid,
+ 1 not found
+ < 0 error
+ endh is set to next separator */
+static int class_parse_value(char *buf, int opc, void *value, char **endh,
+ int quiet)
+{
+ char *endp;
+ char tmp;
+ int rc = 0;
+
+ if (!buf)
+ return 1;
+ while (*buf == ',' || *buf == ':')
+ buf++;
+ if (*buf == ' ' || *buf == '/' || *buf == '\0')
+ return 1;
+
+ /* nid separators or end of nids */
+ endp = strpbrk(buf, ",: /");
+ if (endp == NULL)
+ endp = buf + strlen(buf);
+
+ tmp = *endp;
+ *endp = '\0';
+ switch (opc) {
+ default:
+ LBUG();
+ case CLASS_PARSE_NID:
+ rc = parse_nid(buf, value, quiet);
+ break;
+ case CLASS_PARSE_NET:
+ rc = parse_net(buf, value);
+ break;
+ }
+ *endp = tmp;
+ if (rc != 0)
+ return rc;
+ if (endh)
+ *endh = endp;
+ return 0;
+}
+
+int class_parse_nid(char *buf, lnet_nid_t *nid, char **endh)
+{
+ return class_parse_value(buf, CLASS_PARSE_NID, (void *)nid, endh, 0);
+}
+EXPORT_SYMBOL(class_parse_nid);
+
+int class_parse_nid_quiet(char *buf, lnet_nid_t *nid, char **endh)
+{
+ return class_parse_value(buf, CLASS_PARSE_NID, (void *)nid, endh, 1);
+}
+EXPORT_SYMBOL(class_parse_nid_quiet);
+
+int class_parse_net(char *buf, __u32 *net, char **endh)
+{
+ return class_parse_value(buf, CLASS_PARSE_NET, (void *)net, endh, 0);
+}
+EXPORT_SYMBOL(class_parse_net);
+
+/* 1 param contains key and match
+ * 0 param contains key and not match
+ * -1 param does not contain key
+ */
+int class_match_nid(char *buf, char *key, lnet_nid_t nid)
+{
+ lnet_nid_t tmp;
+ int rc = -1;
+
+ while (class_find_param(buf, key, &buf) == 0) {
+ /* please restrict to the nids pertaining to
+ * the specified nids */
+ while (class_parse_nid(buf, &tmp, &buf) == 0) {
+ if (tmp == nid)
+ return 1;
+ }
+ rc = 0;
+ }
+ return rc;
+}
+EXPORT_SYMBOL(class_match_nid);
+
+int class_match_net(char *buf, char *key, __u32 net)
+{
+ __u32 tmp;
+ int rc = -1;
+
+ while (class_find_param(buf, key, &buf) == 0) {
+ /* please restrict to the nids pertaining to
+ * the specified networks */
+ while (class_parse_net(buf, &tmp, &buf) == 0) {
+ if (tmp == net)
+ return 1;
+ }
+ rc = 0;
+ }
+ return rc;
+}
+EXPORT_SYMBOL(class_match_net);
+
+/********************** class fns **********************/
+
+/**
+ * Create a new obd device and set the type, name and uuid. If successful,
+ * the new device can be accessed by either name or uuid.
+ */
+int class_attach(struct lustre_cfg *lcfg)
+{
+ struct obd_device *obd = NULL;
+ char *typename, *name, *uuid;
+ int rc, len;
+
+ if (!LUSTRE_CFG_BUFLEN(lcfg, 1)) {
+ CERROR("No type passed!\n");
+ return -EINVAL;
+ }
+ typename = lustre_cfg_string(lcfg, 1);
+
+ if (!LUSTRE_CFG_BUFLEN(lcfg, 0)) {
+ CERROR("No name passed!\n");
+ return -EINVAL;
+ }
+ name = lustre_cfg_string(lcfg, 0);
+
+ if (!LUSTRE_CFG_BUFLEN(lcfg, 2)) {
+ CERROR("No UUID passed!\n");
+ return -EINVAL;
+ }
+ uuid = lustre_cfg_string(lcfg, 2);
+
+ CDEBUG(D_IOCTL, "attach type %s name: %s uuid: %s\n",
+ MKSTR(typename), MKSTR(name), MKSTR(uuid));
+
+ obd = class_newdev(typename, name);
+ if (IS_ERR(obd)) {
+ /* Already exists or out of obds */
+ rc = PTR_ERR(obd);
+ obd = NULL;
+ CERROR("Cannot create device %s of type %s : %d\n",
+ name, typename, rc);
+ goto out;
+ }
+ LASSERTF(obd != NULL, "Cannot get obd device %s of type %s\n",
+ name, typename);
+ LASSERTF(obd->obd_magic == OBD_DEVICE_MAGIC,
+ "obd %p obd_magic %08X != %08X\n",
+ obd, obd->obd_magic, OBD_DEVICE_MAGIC);
+ LASSERTF(strncmp(obd->obd_name, name, strlen(name)) == 0,
+ "%p obd_name %s != %s\n", obd, obd->obd_name, name);
+
+ rwlock_init(&obd->obd_pool_lock);
+ obd->obd_pool_limit = 0;
+ obd->obd_pool_slv = 0;
+
+ INIT_LIST_HEAD(&obd->obd_exports);
+ INIT_LIST_HEAD(&obd->obd_unlinked_exports);
+ INIT_LIST_HEAD(&obd->obd_delayed_exports);
+ INIT_LIST_HEAD(&obd->obd_exports_timed);
+ INIT_LIST_HEAD(&obd->obd_nid_stats);
+ spin_lock_init(&obd->obd_nid_lock);
+ spin_lock_init(&obd->obd_dev_lock);
+ mutex_init(&obd->obd_dev_mutex);
+ spin_lock_init(&obd->obd_osfs_lock);
+ /* obd->obd_osfs_age must be set to a value in the distant
+ * past to guarantee a fresh statfs is fetched on mount. */
+ obd->obd_osfs_age = cfs_time_shift_64(-1000);
+
+ /* XXX belongs in setup not attach */
+ init_rwsem(&obd->obd_observer_link_sem);
+ /* recovery data */
+ cfs_init_timer(&obd->obd_recovery_timer);
+ spin_lock_init(&obd->obd_recovery_task_lock);
+ init_waitqueue_head(&obd->obd_next_transno_waitq);
+ init_waitqueue_head(&obd->obd_evict_inprogress_waitq);
+ INIT_LIST_HEAD(&obd->obd_req_replay_queue);
+ INIT_LIST_HEAD(&obd->obd_lock_replay_queue);
+ INIT_LIST_HEAD(&obd->obd_final_req_queue);
+ INIT_LIST_HEAD(&obd->obd_evict_list);
+
+ llog_group_init(&obd->obd_olg, FID_SEQ_LLOG);
+
+ obd->obd_conn_inprogress = 0;
+
+ len = strlen(uuid);
+ if (len >= sizeof(obd->obd_uuid)) {
+ CERROR("uuid must be < %d bytes long\n",
+ (int)sizeof(obd->obd_uuid));
+ rc = -EINVAL;
+ goto out;
+ }
+ memcpy(obd->obd_uuid.uuid, uuid, len);
+
+ /* do the attach */
+ if (OBP(obd, attach)) {
+ rc = OBP(obd, attach)(obd, sizeof(*lcfg), lcfg);
+ if (rc) {
+ rc = -EINVAL;
+ goto out;
+ }
+ }
+
+ /* Detach drops this */
+ spin_lock(&obd->obd_dev_lock);
+ atomic_set(&obd->obd_refcount, 1);
+ spin_unlock(&obd->obd_dev_lock);
+ lu_ref_init(&obd->obd_reference);
+ lu_ref_add(&obd->obd_reference, "attach", obd);
+
+ obd->obd_attached = 1;
+ CDEBUG(D_IOCTL, "OBD: dev %d attached type %s with refcount %d\n",
+ obd->obd_minor, typename, atomic_read(&obd->obd_refcount));
+ return 0;
+ out:
+ if (obd != NULL) {
+ class_release_dev(obd);
+ }
+ return rc;
+}
+EXPORT_SYMBOL(class_attach);
+
+/** Create hashes, self-export, and call type-specific setup.
+ * Setup is effectively the "start this obd" call.
+ */
+int class_setup(struct obd_device *obd, struct lustre_cfg *lcfg)
+{
+ int err = 0;
+ struct obd_export *exp;
+
+ LASSERT(obd != NULL);
+ LASSERTF(obd == class_num2obd(obd->obd_minor),
+ "obd %p != obd_devs[%d] %p\n",
+ obd, obd->obd_minor, class_num2obd(obd->obd_minor));
+ LASSERTF(obd->obd_magic == OBD_DEVICE_MAGIC,
+ "obd %p obd_magic %08x != %08x\n",
+ obd, obd->obd_magic, OBD_DEVICE_MAGIC);
+
+ /* have we attached a type to this device? */
+ if (!obd->obd_attached) {
+ CERROR("Device %d not attached\n", obd->obd_minor);
+ return -ENODEV;
+ }
+
+ if (obd->obd_set_up) {
+ CERROR("Device %d already setup (type %s)\n",
+ obd->obd_minor, obd->obd_type->typ_name);
+ return -EEXIST;
+ }
+
+ /* is someone else setting us up right now? (attach inits spinlock) */
+ spin_lock(&obd->obd_dev_lock);
+ if (obd->obd_starting) {
+ spin_unlock(&obd->obd_dev_lock);
+ CERROR("Device %d setup in progress (type %s)\n",
+ obd->obd_minor, obd->obd_type->typ_name);
+ return -EEXIST;
+ }
+ /* just leave this on forever. I can't use obd_set_up here because
+ other fns check that status, and we're not actually set up yet. */
+ obd->obd_starting = 1;
+ obd->obd_uuid_hash = NULL;
+ obd->obd_nid_hash = NULL;
+ obd->obd_nid_stats_hash = NULL;
+ spin_unlock(&obd->obd_dev_lock);
+
+ /* create an uuid-export lustre hash */
+ obd->obd_uuid_hash = cfs_hash_create("UUID_HASH",
+ HASH_UUID_CUR_BITS,
+ HASH_UUID_MAX_BITS,
+ HASH_UUID_BKT_BITS, 0,
+ CFS_HASH_MIN_THETA,
+ CFS_HASH_MAX_THETA,
+ &uuid_hash_ops, CFS_HASH_DEFAULT);
+ if (!obd->obd_uuid_hash) {
+ err = -ENOMEM;
+ goto err_hash;
+ }
+
+ /* create a nid-export lustre hash */
+ obd->obd_nid_hash = cfs_hash_create("NID_HASH",
+ HASH_NID_CUR_BITS,
+ HASH_NID_MAX_BITS,
+ HASH_NID_BKT_BITS, 0,
+ CFS_HASH_MIN_THETA,
+ CFS_HASH_MAX_THETA,
+ &nid_hash_ops, CFS_HASH_DEFAULT);
+ if (!obd->obd_nid_hash) {
+ err = -ENOMEM;
+ goto err_hash;
+ }
+
+ /* create a nid-stats lustre hash */
+ obd->obd_nid_stats_hash = cfs_hash_create("NID_STATS",
+ HASH_NID_STATS_CUR_BITS,
+ HASH_NID_STATS_MAX_BITS,
+ HASH_NID_STATS_BKT_BITS, 0,
+ CFS_HASH_MIN_THETA,
+ CFS_HASH_MAX_THETA,
+ &nid_stat_hash_ops, CFS_HASH_DEFAULT);
+ if (!obd->obd_nid_stats_hash) {
+ err = -ENOMEM;
+ goto err_hash;
+ }
+
+ exp = class_new_export(obd, &obd->obd_uuid);
+ if (IS_ERR(exp)) {
+ err = PTR_ERR(exp);
+ goto err_hash;
+ }
+
+ obd->obd_self_export = exp;
+ list_del_init(&exp->exp_obd_chain_timed);
+ class_export_put(exp);
+
+ err = obd_setup(obd, lcfg);
+ if (err)
+ goto err_exp;
+
+ obd->obd_set_up = 1;
+
+ spin_lock(&obd->obd_dev_lock);
+ /* cleanup drops this */
+ class_incref(obd, "setup", obd);
+ spin_unlock(&obd->obd_dev_lock);
+
+ CDEBUG(D_IOCTL, "finished setup of obd %s (uuid %s)\n",
+ obd->obd_name, obd->obd_uuid.uuid);
+
+ return 0;
+err_exp:
+ if (obd->obd_self_export) {
+ class_unlink_export(obd->obd_self_export);
+ obd->obd_self_export = NULL;
+ }
+err_hash:
+ if (obd->obd_uuid_hash) {
+ cfs_hash_putref(obd->obd_uuid_hash);
+ obd->obd_uuid_hash = NULL;
+ }
+ if (obd->obd_nid_hash) {
+ cfs_hash_putref(obd->obd_nid_hash);
+ obd->obd_nid_hash = NULL;
+ }
+ if (obd->obd_nid_stats_hash) {
+ cfs_hash_putref(obd->obd_nid_stats_hash);
+ obd->obd_nid_stats_hash = NULL;
+ }
+ obd->obd_starting = 0;
+ CERROR("setup %s failed (%d)\n", obd->obd_name, err);
+ return err;
+}
+EXPORT_SYMBOL(class_setup);
+
+/** We have finished using this obd and are ready to destroy it.
+ * There can be no more references to this obd.
+ */
+int class_detach(struct obd_device *obd, struct lustre_cfg *lcfg)
+{
+ if (obd->obd_set_up) {
+ CERROR("OBD device %d still set up\n", obd->obd_minor);
+ return -EBUSY;
+ }
+
+ spin_lock(&obd->obd_dev_lock);
+ if (!obd->obd_attached) {
+ spin_unlock(&obd->obd_dev_lock);
+ CERROR("OBD device %d not attached\n", obd->obd_minor);
+ return -ENODEV;
+ }
+ obd->obd_attached = 0;
+ spin_unlock(&obd->obd_dev_lock);
+
+ CDEBUG(D_IOCTL, "detach on obd %s (uuid %s)\n",
+ obd->obd_name, obd->obd_uuid.uuid);
+
+ class_decref(obd, "attach", obd);
+ return 0;
+}
+EXPORT_SYMBOL(class_detach);
+
+/** Start shutting down the obd. There may be in-progress ops when
+ * this is called. We tell them to start shutting down with a call
+ * to class_disconnect_exports().
+ */
+int class_cleanup(struct obd_device *obd, struct lustre_cfg *lcfg)
+{
+ int err = 0;
+ char *flag;
+
+ OBD_RACE(OBD_FAIL_LDLM_RECOV_CLIENTS);
+
+ if (!obd->obd_set_up) {
+ CERROR("Device %d not setup\n", obd->obd_minor);
+ return -ENODEV;
+ }
+
+ spin_lock(&obd->obd_dev_lock);
+ if (obd->obd_stopping) {
+ spin_unlock(&obd->obd_dev_lock);
+ CERROR("OBD %d already stopping\n", obd->obd_minor);
+ return -ENODEV;
+ }
+ /* Leave this on forever */
+ obd->obd_stopping = 1;
+
+ /* wait for already-arrived-connections to finish. */
+ while (obd->obd_conn_inprogress > 0) {
+ spin_unlock(&obd->obd_dev_lock);
+
+ cond_resched();
+
+ spin_lock(&obd->obd_dev_lock);
+ }
+ spin_unlock(&obd->obd_dev_lock);
+
+ if (lcfg->lcfg_bufcount >= 2 && LUSTRE_CFG_BUFLEN(lcfg, 1) > 0) {
+ for (flag = lustre_cfg_string(lcfg, 1); *flag != 0; flag++)
+ switch (*flag) {
+ case 'F':
+ obd->obd_force = 1;
+ break;
+ case 'A':
+ LCONSOLE_WARN("Failing over %s\n",
+ obd->obd_name);
+ obd->obd_fail = 1;
+ obd->obd_no_transno = 1;
+ obd->obd_no_recov = 1;
+ if (OBP(obd, iocontrol)) {
+ obd_iocontrol(OBD_IOC_SYNC,
+ obd->obd_self_export,
+ 0, NULL, NULL);
+ }
+ break;
+ default:
+ CERROR("Unrecognised flag '%c'\n", *flag);
+ }
+ }
+
+ LASSERT(obd->obd_self_export);
+
+ /* The three references that should be remaining are the
+ * obd_self_export and the attach and setup references. */
+ if (atomic_read(&obd->obd_refcount) > 3) {
+ /* refcount - 3 might be the number of real exports
+ (excluding self export). But class_incref is called
+ by other things as well, so don't count on it. */
+ CDEBUG(D_IOCTL, "%s: forcing exports to disconnect: %d\n",
+ obd->obd_name, atomic_read(&obd->obd_refcount) - 3);
+ dump_exports(obd, 0);
+ class_disconnect_exports(obd);
+ }
+
+ /* Precleanup, we must make sure all exports get destroyed. */
+ err = obd_precleanup(obd, OBD_CLEANUP_EXPORTS);
+ if (err)
+ CERROR("Precleanup %s returned %d\n",
+ obd->obd_name, err);
+
+ /* destroy an uuid-export hash body */
+ if (obd->obd_uuid_hash) {
+ cfs_hash_putref(obd->obd_uuid_hash);
+ obd->obd_uuid_hash = NULL;
+ }
+
+ /* destroy a nid-export hash body */
+ if (obd->obd_nid_hash) {
+ cfs_hash_putref(obd->obd_nid_hash);
+ obd->obd_nid_hash = NULL;
+ }
+
+ /* destroy a nid-stats hash body */
+ if (obd->obd_nid_stats_hash) {
+ cfs_hash_putref(obd->obd_nid_stats_hash);
+ obd->obd_nid_stats_hash = NULL;
+ }
+
+ class_decref(obd, "setup", obd);
+ obd->obd_set_up = 0;
+
+ return 0;
+}
+EXPORT_SYMBOL(class_cleanup);
+
+struct obd_device *class_incref(struct obd_device *obd,
+ const char *scope, const void *source)
+{
+ lu_ref_add_atomic(&obd->obd_reference, scope, source);
+ atomic_inc(&obd->obd_refcount);
+ CDEBUG(D_INFO, "incref %s (%p) now %d\n", obd->obd_name, obd,
+ atomic_read(&obd->obd_refcount));
+
+ return obd;
+}
+EXPORT_SYMBOL(class_incref);
+
+void class_decref(struct obd_device *obd, const char *scope, const void *source)
+{
+ int err;
+ int refs;
+
+ spin_lock(&obd->obd_dev_lock);
+ atomic_dec(&obd->obd_refcount);
+ refs = atomic_read(&obd->obd_refcount);
+ spin_unlock(&obd->obd_dev_lock);
+ lu_ref_del(&obd->obd_reference, scope, source);
+
+ CDEBUG(D_INFO, "Decref %s (%p) now %d\n", obd->obd_name, obd, refs);
+
+ if ((refs == 1) && obd->obd_stopping) {
+ /* All exports have been destroyed; there should
+ be no more in-progress ops by this point.*/
+
+ spin_lock(&obd->obd_self_export->exp_lock);
+ obd->obd_self_export->exp_flags |= exp_flags_from_obd(obd);
+ spin_unlock(&obd->obd_self_export->exp_lock);
+
+ /* note that we'll recurse into class_decref again */
+ class_unlink_export(obd->obd_self_export);
+ return;
+ }
+
+ if (refs == 0) {
+ CDEBUG(D_CONFIG, "finishing cleanup of obd %s (%s)\n",
+ obd->obd_name, obd->obd_uuid.uuid);
+ LASSERT(!obd->obd_attached);
+ if (obd->obd_stopping) {
+ /* If we're not stopping, we were never set up */
+ err = obd_cleanup(obd);
+ if (err)
+ CERROR("Cleanup %s returned %d\n",
+ obd->obd_name, err);
+ }
+ if (OBP(obd, detach)) {
+ err = OBP(obd, detach)(obd);
+ if (err)
+ CERROR("Detach returned %d\n", err);
+ }
+ class_release_dev(obd);
+ }
+}
+EXPORT_SYMBOL(class_decref);
+
+/** Add a failover nid location.
+ * Client obd types contact server obd types using this nid list.
+ */
+int class_add_conn(struct obd_device *obd, struct lustre_cfg *lcfg)
+{
+ struct obd_import *imp;
+ struct obd_uuid uuid;
+ int rc;
+
+ if (LUSTRE_CFG_BUFLEN(lcfg, 1) < 1 ||
+ LUSTRE_CFG_BUFLEN(lcfg, 1) > sizeof(struct obd_uuid)) {
+ CERROR("invalid conn_uuid\n");
+ return -EINVAL;
+ }
+ if (strcmp(obd->obd_type->typ_name, LUSTRE_MDC_NAME) &&
+ strcmp(obd->obd_type->typ_name, LUSTRE_OSC_NAME) &&
+ strcmp(obd->obd_type->typ_name, LUSTRE_OSP_NAME) &&
+ strcmp(obd->obd_type->typ_name, LUSTRE_LWP_NAME) &&
+ strcmp(obd->obd_type->typ_name, LUSTRE_MGC_NAME)) {
+ CERROR("can't add connection on non-client dev\n");
+ return -EINVAL;
+ }
+
+ imp = obd->u.cli.cl_import;
+ if (!imp) {
+ CERROR("try to add conn on immature client dev\n");
+ return -EINVAL;
+ }
+
+ obd_str2uuid(&uuid, lustre_cfg_string(lcfg, 1));
+ rc = obd_add_conn(imp, &uuid, lcfg->lcfg_num);
+
+ return rc;
+}
+EXPORT_SYMBOL(class_add_conn);
+
+/** Remove a failover nid location.
+ */
+int class_del_conn(struct obd_device *obd, struct lustre_cfg *lcfg)
+{
+ struct obd_import *imp;
+ struct obd_uuid uuid;
+ int rc;
+
+ if (LUSTRE_CFG_BUFLEN(lcfg, 1) < 1 ||
+ LUSTRE_CFG_BUFLEN(lcfg, 1) > sizeof(struct obd_uuid)) {
+ CERROR("invalid conn_uuid\n");
+ return -EINVAL;
+ }
+ if (strcmp(obd->obd_type->typ_name, LUSTRE_MDC_NAME) &&
+ strcmp(obd->obd_type->typ_name, LUSTRE_OSC_NAME)) {
+ CERROR("can't del connection on non-client dev\n");
+ return -EINVAL;
+ }
+
+ imp = obd->u.cli.cl_import;
+ if (!imp) {
+ CERROR("try to del conn on immature client dev\n");
+ return -EINVAL;
+ }
+
+ obd_str2uuid(&uuid, lustre_cfg_string(lcfg, 1));
+ rc = obd_del_conn(imp, &uuid);
+
+ return rc;
+}
+
+LIST_HEAD(lustre_profile_list);
+
+struct lustre_profile *class_get_profile(const char *prof)
+{
+ struct lustre_profile *lprof;
+
+ list_for_each_entry(lprof, &lustre_profile_list, lp_list) {
+ if (!strcmp(lprof->lp_profile, prof)) {
+ return lprof;
+ }
+ }
+ return NULL;
+}
+EXPORT_SYMBOL(class_get_profile);
+
+/** Create a named "profile".
+ * This defines the mdc and osc names to use for a client.
+ * This also is used to define the lov to be used by a mdt.
+ */
+int class_add_profile(int proflen, char *prof, int osclen, char *osc,
+ int mdclen, char *mdc)
+{
+ struct lustre_profile *lprof;
+ int err = 0;
+
+ CDEBUG(D_CONFIG, "Add profile %s\n", prof);
+
+ OBD_ALLOC(lprof, sizeof(*lprof));
+ if (lprof == NULL)
+ return -ENOMEM;
+ INIT_LIST_HEAD(&lprof->lp_list);
+
+ LASSERT(proflen == (strlen(prof) + 1));
+ OBD_ALLOC(lprof->lp_profile, proflen);
+ if (lprof->lp_profile == NULL) {
+ err = -ENOMEM;
+ goto out;
+ }
+ memcpy(lprof->lp_profile, prof, proflen);
+
+ LASSERT(osclen == (strlen(osc) + 1));
+ OBD_ALLOC(lprof->lp_dt, osclen);
+ if (lprof->lp_dt == NULL) {
+ err = -ENOMEM;
+ goto out;
+ }
+ memcpy(lprof->lp_dt, osc, osclen);
+
+ if (mdclen > 0) {
+ LASSERT(mdclen == (strlen(mdc) + 1));
+ OBD_ALLOC(lprof->lp_md, mdclen);
+ if (lprof->lp_md == NULL) {
+ err = -ENOMEM;
+ goto out;
+ }
+ memcpy(lprof->lp_md, mdc, mdclen);
+ }
+
+ list_add(&lprof->lp_list, &lustre_profile_list);
+ return err;
+
+out:
+ if (lprof->lp_md)
+ OBD_FREE(lprof->lp_md, mdclen);
+ if (lprof->lp_dt)
+ OBD_FREE(lprof->lp_dt, osclen);
+ if (lprof->lp_profile)
+ OBD_FREE(lprof->lp_profile, proflen);
+ OBD_FREE(lprof, sizeof(*lprof));
+ return err;
+}
+
+void class_del_profile(const char *prof)
+{
+ struct lustre_profile *lprof;
+
+ CDEBUG(D_CONFIG, "Del profile %s\n", prof);
+
+ lprof = class_get_profile(prof);
+ if (lprof) {
+ list_del(&lprof->lp_list);
+ OBD_FREE(lprof->lp_profile, strlen(lprof->lp_profile) + 1);
+ OBD_FREE(lprof->lp_dt, strlen(lprof->lp_dt) + 1);
+ if (lprof->lp_md)
+ OBD_FREE(lprof->lp_md, strlen(lprof->lp_md) + 1);
+ OBD_FREE(lprof, sizeof(*lprof));
+ }
+}
+EXPORT_SYMBOL(class_del_profile);
+
+/* COMPAT_146 */
+void class_del_profiles(void)
+{
+ struct lustre_profile *lprof, *n;
+
+ list_for_each_entry_safe(lprof, n, &lustre_profile_list, lp_list) {
+ list_del(&lprof->lp_list);
+ OBD_FREE(lprof->lp_profile, strlen(lprof->lp_profile) + 1);
+ OBD_FREE(lprof->lp_dt, strlen(lprof->lp_dt) + 1);
+ if (lprof->lp_md)
+ OBD_FREE(lprof->lp_md, strlen(lprof->lp_md) + 1);
+ OBD_FREE(lprof, sizeof(*lprof));
+ }
+}
+EXPORT_SYMBOL(class_del_profiles);
+
+static int class_set_global(char *ptr, int val, struct lustre_cfg *lcfg)
+{
+ if (class_match_param(ptr, PARAM_AT_MIN, NULL) == 0)
+ at_min = val;
+ else if (class_match_param(ptr, PARAM_AT_MAX, NULL) == 0)
+ at_max = val;
+ else if (class_match_param(ptr, PARAM_AT_EXTRA, NULL) == 0)
+ at_extra = val;
+ else if (class_match_param(ptr, PARAM_AT_EARLY_MARGIN, NULL) == 0)
+ at_early_margin = val;
+ else if (class_match_param(ptr, PARAM_AT_HISTORY, NULL) == 0)
+ at_history = val;
+ else if (class_match_param(ptr, PARAM_JOBID_VAR, NULL) == 0)
+ strlcpy(obd_jobid_var, lustre_cfg_string(lcfg, 2),
+ JOBSTATS_JOBID_VAR_MAX_LEN + 1);
+ else
+ return -EINVAL;
+
+ CDEBUG(D_IOCTL, "global %s = %d\n", ptr, val);
+ return 0;
+}
+
+
+/* We can't call ll_process_config or lquota_process_config directly because
+ * it lives in a module that must be loaded after this one. */
+static int (*client_process_config)(struct lustre_cfg *lcfg) = NULL;
+static int (*quota_process_config)(struct lustre_cfg *lcfg) = NULL;
+
+void lustre_register_client_process_config(int (*cpc)(struct lustre_cfg *lcfg))
+{
+ client_process_config = cpc;
+}
+EXPORT_SYMBOL(lustre_register_client_process_config);
+
+/**
+ * Rename the proc parameter in \a cfg with a new name \a new_name.
+ *
+ * \param cfg config structure which contains the proc parameter
+ * \param new_name new name of the proc parameter
+ *
+ * \retval valid-pointer pointer to the newly-allocated config structure
+ * which contains the renamed proc parameter
+ * \retval ERR_PTR(-EINVAL) if \a cfg or \a new_name is NULL, or \a cfg does
+ * not contain a proc parameter
+ * \retval ERR_PTR(-ENOMEM) if memory allocation failure occurs
+ */
+struct lustre_cfg *lustre_cfg_rename(struct lustre_cfg *cfg,
+ const char *new_name)
+{
+ struct lustre_cfg_bufs *bufs = NULL;
+ struct lustre_cfg *new_cfg = NULL;
+ char *param = NULL;
+ char *new_param = NULL;
+ char *value = NULL;
+ int name_len = 0;
+ int new_len = 0;
+
+ if (cfg == NULL || new_name == NULL)
+ return ERR_PTR(-EINVAL);
+
+ param = lustre_cfg_string(cfg, 1);
+ if (param == NULL)
+ return ERR_PTR(-EINVAL);
+
+ value = strchr(param, '=');
+ if (value == NULL)
+ name_len = strlen(param);
+ else
+ name_len = value - param;
+
+ new_len = LUSTRE_CFG_BUFLEN(cfg, 1) + strlen(new_name) - name_len;
+
+ OBD_ALLOC(new_param, new_len);
+ if (new_param == NULL)
+ return ERR_PTR(-ENOMEM);
+
+ strcpy(new_param, new_name);
+ if (value != NULL)
+ strcat(new_param, value);
+
+ OBD_ALLOC_PTR(bufs);
+ if (bufs == NULL) {
+ OBD_FREE(new_param, new_len);
+ return ERR_PTR(-ENOMEM);
+ }
+
+ lustre_cfg_bufs_reset(bufs, NULL);
+ lustre_cfg_bufs_init(bufs, cfg);
+ lustre_cfg_bufs_set_string(bufs, 1, new_param);
+
+ new_cfg = lustre_cfg_new(cfg->lcfg_command, bufs);
+
+ OBD_FREE(new_param, new_len);
+ OBD_FREE_PTR(bufs);
+ if (new_cfg == NULL)
+ return ERR_PTR(-ENOMEM);
+
+ new_cfg->lcfg_num = cfg->lcfg_num;
+ new_cfg->lcfg_flags = cfg->lcfg_flags;
+ new_cfg->lcfg_nid = cfg->lcfg_nid;
+ new_cfg->lcfg_nal = cfg->lcfg_nal;
+
+ return new_cfg;
+}
+EXPORT_SYMBOL(lustre_cfg_rename);
+
+static int process_param2_config(struct lustre_cfg *lcfg)
+{
+ char *param = lustre_cfg_string(lcfg, 1);
+ char *upcall = lustre_cfg_string(lcfg, 2);
+ char *argv[] = {
+ [0] = "/usr/sbin/lctl",
+ [1] = "set_param",
+ [2] = param,
+ [3] = NULL
+ };
+ struct timeval start;
+ struct timeval end;
+ int rc;
+
+
+ /* Add upcall processing here. Now only lctl is supported */
+ if (strcmp(upcall, LCTL_UPCALL) != 0) {
+ CERROR("Unsupported upcall %s\n", upcall);
+ return -EINVAL;
+ }
+
+ do_gettimeofday(&start);
+ rc = call_usermodehelper(argv[0], argv, NULL, 1);
+ do_gettimeofday(&end);
+
+ if (rc < 0) {
+ CERROR(
+ "lctl: error invoking upcall %s %s %s: rc = %d; time %ldus\n",
+ argv[0], argv[1], argv[2], rc,
+ cfs_timeval_sub(&end, &start, NULL));
+ } else {
+ CDEBUG(D_HA, "lctl: invoked upcall %s %s %s, time %ldus\n",
+ argv[0], argv[1], argv[2],
+ cfs_timeval_sub(&end, &start, NULL));
+ rc = 0;
+ }
+
+ return rc;
+}
+
+void lustre_register_quota_process_config(int (*qpc)(struct lustre_cfg *lcfg))
+{
+ quota_process_config = qpc;
+}
+EXPORT_SYMBOL(lustre_register_quota_process_config);
+
+/** Process configuration commands given in lustre_cfg form.
+ * These may come from direct calls (e.g. class_manual_cleanup)
+ * or processing the config llog, or ioctl from lctl.
+ */
+int class_process_config(struct lustre_cfg *lcfg)
+{
+ struct obd_device *obd;
+ int err;
+
+ LASSERT(lcfg && !IS_ERR(lcfg));
+ CDEBUG(D_IOCTL, "processing cmd: %x\n", lcfg->lcfg_command);
+
+ /* Commands that don't need a device */
+ switch (lcfg->lcfg_command) {
+ case LCFG_ATTACH: {
+ err = class_attach(lcfg);
+ goto out;
+ }
+ case LCFG_ADD_UUID: {
+ CDEBUG(D_IOCTL, "adding mapping from uuid %s to nid %#llx (%s)\n",
+ lustre_cfg_string(lcfg, 1), lcfg->lcfg_nid,
+ libcfs_nid2str(lcfg->lcfg_nid));
+
+ err = class_add_uuid(lustre_cfg_string(lcfg, 1), lcfg->lcfg_nid);
+ goto out;
+ }
+ case LCFG_DEL_UUID: {
+ CDEBUG(D_IOCTL, "removing mappings for uuid %s\n",
+ (lcfg->lcfg_bufcount < 2 || LUSTRE_CFG_BUFLEN(lcfg, 1) == 0)
+ ? "<all uuids>" : lustre_cfg_string(lcfg, 1));
+
+ err = class_del_uuid(lustre_cfg_string(lcfg, 1));
+ goto out;
+ }
+ case LCFG_MOUNTOPT: {
+ CDEBUG(D_IOCTL, "mountopt: profile %s osc %s mdc %s\n",
+ lustre_cfg_string(lcfg, 1),
+ lustre_cfg_string(lcfg, 2),
+ lustre_cfg_string(lcfg, 3));
+ /* set these mount options somewhere, so ll_fill_super
+ * can find them. */
+ err = class_add_profile(LUSTRE_CFG_BUFLEN(lcfg, 1),
+ lustre_cfg_string(lcfg, 1),
+ LUSTRE_CFG_BUFLEN(lcfg, 2),
+ lustre_cfg_string(lcfg, 2),
+ LUSTRE_CFG_BUFLEN(lcfg, 3),
+ lustre_cfg_string(lcfg, 3));
+ goto out;
+ }
+ case LCFG_DEL_MOUNTOPT: {
+ CDEBUG(D_IOCTL, "mountopt: profile %s\n",
+ lustre_cfg_string(lcfg, 1));
+ class_del_profile(lustre_cfg_string(lcfg, 1));
+ err = 0;
+ goto out;
+ }
+ case LCFG_SET_TIMEOUT: {
+ CDEBUG(D_IOCTL, "changing lustre timeout from %d to %d\n",
+ obd_timeout, lcfg->lcfg_num);
+ obd_timeout = max(lcfg->lcfg_num, 1U);
+ obd_timeout_set = 1;
+ err = 0;
+ goto out;
+ }
+ case LCFG_SET_LDLM_TIMEOUT: {
+ CDEBUG(D_IOCTL, "changing lustre ldlm_timeout from %d to %d\n",
+ ldlm_timeout, lcfg->lcfg_num);
+ ldlm_timeout = max(lcfg->lcfg_num, 1U);
+ if (ldlm_timeout >= obd_timeout)
+ ldlm_timeout = max(obd_timeout / 3, 1U);
+ ldlm_timeout_set = 1;
+ err = 0;
+ goto out;
+ }
+ case LCFG_SET_UPCALL: {
+ LCONSOLE_ERROR_MSG(0x15a, "recovery upcall is deprecated\n");
+ /* COMPAT_146 Don't fail on old configs */
+ err = 0;
+ goto out;
+ }
+ case LCFG_MARKER: {
+ struct cfg_marker *marker;
+ marker = lustre_cfg_buf(lcfg, 1);
+ CDEBUG(D_IOCTL, "marker %d (%#x) %.16s %s\n", marker->cm_step,
+ marker->cm_flags, marker->cm_tgtname, marker->cm_comment);
+ err = 0;
+ goto out;
+ }
+ case LCFG_PARAM: {
+ char *tmp;
+ /* llite has no obd */
+ if ((class_match_param(lustre_cfg_string(lcfg, 1),
+ PARAM_LLITE, NULL) == 0) &&
+ client_process_config) {
+ err = (*client_process_config)(lcfg);
+ goto out;
+ } else if ((class_match_param(lustre_cfg_string(lcfg, 1),
+ PARAM_SYS, &tmp) == 0)) {
+ /* Global param settings */
+ err = class_set_global(tmp, lcfg->lcfg_num, lcfg);
+ /*
+ * Client or server should not fail to mount if
+ * it hits an unknown configuration parameter.
+ */
+ if (err != 0)
+ CWARN("Ignoring unknown param %s\n", tmp);
+
+ err = 0;
+ goto out;
+ } else if ((class_match_param(lustre_cfg_string(lcfg, 1),
+ PARAM_QUOTA, &tmp) == 0) &&
+ quota_process_config) {
+ err = (*quota_process_config)(lcfg);
+ goto out;
+ }
+
+ break;
+ }
+ case LCFG_SET_PARAM: {
+ err = process_param2_config(lcfg);
+ goto out;
+ }
+ }
+ /* Commands that require a device */
+ obd = class_name2obd(lustre_cfg_string(lcfg, 0));
+ if (obd == NULL) {
+ if (!LUSTRE_CFG_BUFLEN(lcfg, 0))
+ CERROR("this lcfg command requires a device name\n");
+ else
+ CERROR("no device for: %s\n",
+ lustre_cfg_string(lcfg, 0));
+
+ err = -EINVAL;
+ goto out;
+ }
+
+ switch (lcfg->lcfg_command) {
+ case LCFG_SETUP: {
+ err = class_setup(obd, lcfg);
+ goto out;
+ }
+ case LCFG_DETACH: {
+ err = class_detach(obd, lcfg);
+ err = 0;
+ goto out;
+ }
+ case LCFG_CLEANUP: {
+ err = class_cleanup(obd, lcfg);
+ err = 0;
+ goto out;
+ }
+ case LCFG_ADD_CONN: {
+ err = class_add_conn(obd, lcfg);
+ err = 0;
+ goto out;
+ }
+ case LCFG_DEL_CONN: {
+ err = class_del_conn(obd, lcfg);
+ err = 0;
+ goto out;
+ }
+ case LCFG_POOL_NEW: {
+ err = obd_pool_new(obd, lustre_cfg_string(lcfg, 2));
+ err = 0;
+ goto out;
+ }
+ case LCFG_POOL_ADD: {
+ err = obd_pool_add(obd, lustre_cfg_string(lcfg, 2),
+ lustre_cfg_string(lcfg, 3));
+ err = 0;
+ goto out;
+ }
+ case LCFG_POOL_REM: {
+ err = obd_pool_rem(obd, lustre_cfg_string(lcfg, 2),
+ lustre_cfg_string(lcfg, 3));
+ err = 0;
+ goto out;
+ }
+ case LCFG_POOL_DEL: {
+ err = obd_pool_del(obd, lustre_cfg_string(lcfg, 2));
+ err = 0;
+ goto out;
+ }
+ default: {
+ err = obd_process_config(obd, sizeof(*lcfg), lcfg);
+ goto out;
+
+ }
+ }
+out:
+ if ((err < 0) && !(lcfg->lcfg_command & LCFG_REQUIRED)) {
+ CWARN("Ignoring error %d on optional command %#x\n", err,
+ lcfg->lcfg_command);
+ err = 0;
+ }
+ return err;
+}
+EXPORT_SYMBOL(class_process_config);
+
+int class_process_proc_param(char *prefix, struct lprocfs_vars *lvars,
+ struct lustre_cfg *lcfg, void *data)
+{
+ struct lprocfs_vars *var;
+ struct file fakefile;
+ struct seq_file fake_seqfile;
+ char *key, *sval;
+ int i, keylen, vallen;
+ int matched = 0, j = 0;
+ int rc = 0;
+ int skip = 0;
+
+ if (lcfg->lcfg_command != LCFG_PARAM) {
+ CERROR("Unknown command: %d\n", lcfg->lcfg_command);
+ return -EINVAL;
+ }
+
+ /* fake a seq file so that var->fops->write can work... */
+ fakefile.private_data = &fake_seqfile;
+ fake_seqfile.private = data;
+ /* e.g. tunefs.lustre --param mdt.group_upcall=foo /r/tmp/lustre-mdt
+ or lctl conf_param lustre-MDT0000.mdt.group_upcall=bar
+ or lctl conf_param lustre-OST0000.osc.max_dirty_mb=36 */
+ for (i = 1; i < lcfg->lcfg_bufcount; i++) {
+ key = lustre_cfg_buf(lcfg, i);
+ /* Strip off prefix */
+ class_match_param(key, prefix, &key);
+ sval = strchr(key, '=');
+ if (!sval || (*(sval + 1) == 0)) {
+ CERROR("Can't parse param %s (missing '=')\n", key);
+ /* rc = -EINVAL; continue parsing other params */
+ continue;
+ }
+ keylen = sval - key;
+ sval++;
+ vallen = strlen(sval);
+ matched = 0;
+ j = 0;
+ /* Search proc entries */
+ while (lvars[j].name) {
+ var = &lvars[j];
+ if (class_match_param(key, (char *)var->name, NULL) == 0
+ && keylen == strlen(var->name)) {
+ matched++;
+ rc = -EROFS;
+ if (var->fops && var->fops->write) {
+ mm_segment_t oldfs;
+ oldfs = get_fs();
+ set_fs(KERNEL_DS);
+ rc = (var->fops->write)(&fakefile, sval,
+ vallen, NULL);
+ set_fs(oldfs);
+ }
+ break;
+ }
+ j++;
+ }
+ if (!matched) {
+ /* If the prefix doesn't match, return error so we
+ can pass it down the stack */
+ if (strnchr(key, keylen, '.'))
+ return -ENOSYS;
+ CERROR("%s: unknown param %s\n",
+ (char *)lustre_cfg_string(lcfg, 0), key);
+ /* rc = -EINVAL; continue parsing other params */
+ skip++;
+ } else if (rc < 0) {
+ CERROR("writing proc entry %s err %d\n",
+ var->name, rc);
+ rc = 0;
+ } else {
+ CDEBUG(D_CONFIG, "%s.%.*s: Set parameter %.*s=%s\n",
+ lustre_cfg_string(lcfg, 0),
+ (int)strlen(prefix) - 1, prefix,
+ (int)(sval - key - 1), key, sval);
+ }
+ }
+
+ if (rc > 0)
+ rc = 0;
+ if (!rc && skip)
+ rc = skip;
+ return rc;
+}
+EXPORT_SYMBOL(class_process_proc_param);
+
+extern int lustre_check_exclusion(struct super_block *sb, char *svname);
+
+/** Parse a configuration llog, doing various manipulations on them
+ * for various reasons, (modifications for compatibility, skip obsolete
+ * records, change uuids, etc), then class_process_config() resulting
+ * net records.
+ */
+int class_config_llog_handler(const struct lu_env *env,
+ struct llog_handle *handle,
+ struct llog_rec_hdr *rec, void *data)
+{
+ struct config_llog_instance *clli = data;
+ int cfg_len = rec->lrh_len;
+ char *cfg_buf = (char *) (rec + 1);
+ int rc = 0;
+
+ //class_config_dump_handler(handle, rec, data);
+
+ switch (rec->lrh_type) {
+ case OBD_CFG_REC: {
+ struct lustre_cfg *lcfg, *lcfg_new;
+ struct lustre_cfg_bufs bufs;
+ char *inst_name = NULL;
+ int inst_len = 0;
+ int inst = 0, swab = 0;
+
+ lcfg = (struct lustre_cfg *)cfg_buf;
+ if (lcfg->lcfg_version == __swab32(LUSTRE_CFG_VERSION)) {
+ lustre_swab_lustre_cfg(lcfg);
+ swab = 1;
+ }
+
+ rc = lustre_cfg_sanity_check(cfg_buf, cfg_len);
+ if (rc)
+ goto out;
+
+ /* Figure out config state info */
+ if (lcfg->lcfg_command == LCFG_MARKER) {
+ struct cfg_marker *marker = lustre_cfg_buf(lcfg, 1);
+ lustre_swab_cfg_marker(marker, swab,
+ LUSTRE_CFG_BUFLEN(lcfg, 1));
+ CDEBUG(D_CONFIG, "Marker, inst_flg=%#x mark_flg=%#x\n",
+ clli->cfg_flags, marker->cm_flags);
+ if (marker->cm_flags & CM_START) {
+ /* all previous flags off */
+ clli->cfg_flags = CFG_F_MARKER;
+ if (marker->cm_flags & CM_SKIP) {
+ clli->cfg_flags |= CFG_F_SKIP;
+ CDEBUG(D_CONFIG, "SKIP #%d\n",
+ marker->cm_step);
+ } else if ((marker->cm_flags & CM_EXCLUDE) ||
+ (clli->cfg_sb &&
+ lustre_check_exclusion(clli->cfg_sb,
+ marker->cm_tgtname))) {
+ clli->cfg_flags |= CFG_F_EXCLUDE;
+ CDEBUG(D_CONFIG, "EXCLUDE %d\n",
+ marker->cm_step);
+ }
+ } else if (marker->cm_flags & CM_END) {
+ clli->cfg_flags = 0;
+ }
+ }
+ /* A config command without a start marker before it is
+ illegal (post 146) */
+ if (!(clli->cfg_flags & CFG_F_COMPAT146) &&
+ !(clli->cfg_flags & CFG_F_MARKER) &&
+ (lcfg->lcfg_command != LCFG_MARKER)) {
+ CWARN("Config not inside markers, ignoring! (inst: %p, uuid: %s, flags: %#x)\n",
+ clli->cfg_instance,
+ clli->cfg_uuid.uuid, clli->cfg_flags);
+ clli->cfg_flags |= CFG_F_SKIP;
+ }
+ if (clli->cfg_flags & CFG_F_SKIP) {
+ CDEBUG(D_CONFIG, "skipping %#x\n",
+ clli->cfg_flags);
+ rc = 0;
+ /* No processing! */
+ break;
+ }
+
+ /*
+ * For interoperability between 1.8 and 2.0,
+ * rename "mds" obd device type to "mdt".
+ */
+ {
+ char *typename = lustre_cfg_string(lcfg, 1);
+ char *index = lustre_cfg_string(lcfg, 2);
+
+ if ((lcfg->lcfg_command == LCFG_ATTACH && typename &&
+ strcmp(typename, "mds") == 0)) {
+ CWARN("For 1.8 interoperability, rename obd type from mds to mdt\n");
+ typename[2] = 't';
+ }
+ if ((lcfg->lcfg_command == LCFG_SETUP && index &&
+ strcmp(index, "type") == 0)) {
+ CDEBUG(D_INFO, "For 1.8 interoperability, set this index to '0'\n");
+ index[0] = '0';
+ index[1] = 0;
+ }
+ }
+
+
+ if (clli->cfg_flags & CFG_F_EXCLUDE) {
+ CDEBUG(D_CONFIG, "cmd: %x marked EXCLUDED\n",
+ lcfg->lcfg_command);
+ if (lcfg->lcfg_command == LCFG_LOV_ADD_OBD)
+ /* Add inactive instead */
+ lcfg->lcfg_command = LCFG_LOV_ADD_INA;
+ }
+
+ lustre_cfg_bufs_init(&bufs, lcfg);
+
+ if (clli && clli->cfg_instance &&
+ LUSTRE_CFG_BUFLEN(lcfg, 0) > 0){
+ inst = 1;
+ inst_len = LUSTRE_CFG_BUFLEN(lcfg, 0) +
+ sizeof(clli->cfg_instance) * 2 + 4;
+ OBD_ALLOC(inst_name, inst_len);
+ if (inst_name == NULL) {
+ rc = -ENOMEM;
+ goto out;
+ }
+ sprintf(inst_name, "%s-%p",
+ lustre_cfg_string(lcfg, 0),
+ clli->cfg_instance);
+ lustre_cfg_bufs_set_string(&bufs, 0, inst_name);
+ CDEBUG(D_CONFIG, "cmd %x, instance name: %s\n",
+ lcfg->lcfg_command, inst_name);
+ }
+
+ /* we override the llog's uuid for clients, to insure they
+ are unique */
+ if (clli && clli->cfg_instance != NULL &&
+ lcfg->lcfg_command == LCFG_ATTACH) {
+ lustre_cfg_bufs_set_string(&bufs, 2,
+ clli->cfg_uuid.uuid);
+ }
+ /*
+ * sptlrpc config record, we expect 2 data segments:
+ * [0]: fs_name/target_name,
+ * [1]: rule string
+ * moving them to index [1] and [2], and insert MGC's
+ * obdname at index [0].
+ */
+ if (clli && clli->cfg_instance == NULL &&
+ lcfg->lcfg_command == LCFG_SPTLRPC_CONF) {
+ lustre_cfg_bufs_set(&bufs, 2, bufs.lcfg_buf[1],
+ bufs.lcfg_buflen[1]);
+ lustre_cfg_bufs_set(&bufs, 1, bufs.lcfg_buf[0],
+ bufs.lcfg_buflen[0]);
+ lustre_cfg_bufs_set_string(&bufs, 0,
+ clli->cfg_obdname);
+ }
+
+ lcfg_new = lustre_cfg_new(lcfg->lcfg_command, &bufs);
+
+ lcfg_new->lcfg_num = lcfg->lcfg_num;
+ lcfg_new->lcfg_flags = lcfg->lcfg_flags;
+
+ /* XXX Hack to try to remain binary compatible with
+ * pre-newconfig logs */
+ if (lcfg->lcfg_nal != 0 && /* pre-newconfig log? */
+ (lcfg->lcfg_nid >> 32) == 0) {
+ __u32 addr = (__u32)(lcfg->lcfg_nid & 0xffffffff);
+
+ lcfg_new->lcfg_nid =
+ LNET_MKNID(LNET_MKNET(lcfg->lcfg_nal, 0), addr);
+ CWARN("Converted pre-newconfig NAL %d NID %x to %s\n",
+ lcfg->lcfg_nal, addr,
+ libcfs_nid2str(lcfg_new->lcfg_nid));
+ } else {
+ lcfg_new->lcfg_nid = lcfg->lcfg_nid;
+ }
+
+ lcfg_new->lcfg_nal = 0; /* illegal value for obsolete field */
+
+ rc = class_process_config(lcfg_new);
+ lustre_cfg_free(lcfg_new);
+
+ if (inst)
+ OBD_FREE(inst_name, inst_len);
+ break;
+ }
+ default:
+ CERROR("Unknown llog record type %#x encountered\n",
+ rec->lrh_type);
+ break;
+ }
+out:
+ if (rc) {
+ CERROR("%s: cfg command failed: rc = %d\n",
+ handle->lgh_ctxt->loc_obd->obd_name, rc);
+ class_config_dump_handler(NULL, handle, rec, data);
+ }
+ return rc;
+}
+EXPORT_SYMBOL(class_config_llog_handler);
+
+int class_config_parse_llog(const struct lu_env *env, struct llog_ctxt *ctxt,
+ char *name, struct config_llog_instance *cfg)
+{
+ struct llog_process_cat_data cd = {0, 0};
+ struct llog_handle *llh;
+ llog_cb_t callback;
+ int rc;
+
+ CDEBUG(D_INFO, "looking up llog %s\n", name);
+ rc = llog_open(env, ctxt, &llh, NULL, name, LLOG_OPEN_EXISTS);
+ if (rc)
+ return rc;
+
+ rc = llog_init_handle(env, llh, LLOG_F_IS_PLAIN, NULL);
+ if (rc)
+ goto parse_out;
+
+ /* continue processing from where we last stopped to end-of-log */
+ if (cfg) {
+ cd.lpcd_first_idx = cfg->cfg_last_idx;
+ callback = cfg->cfg_callback;
+ LASSERT(callback != NULL);
+ } else {
+ callback = class_config_llog_handler;
+ }
+
+ cd.lpcd_last_idx = 0;
+
+ rc = llog_process(env, llh, callback, cfg, &cd);
+
+ CDEBUG(D_CONFIG, "Processed log %s gen %d-%d (rc=%d)\n", name,
+ cd.lpcd_first_idx + 1, cd.lpcd_last_idx, rc);
+ if (cfg)
+ cfg->cfg_last_idx = cd.lpcd_last_idx;
+
+parse_out:
+ llog_close(env, llh);
+ return rc;
+}
+EXPORT_SYMBOL(class_config_parse_llog);
+
+/**
+ * parse config record and output dump in supplied buffer.
+ * This is separated from class_config_dump_handler() to use
+ * for ioctl needs as well
+ */
+int class_config_parse_rec(struct llog_rec_hdr *rec, char *buf, int size)
+{
+ struct lustre_cfg *lcfg = (struct lustre_cfg *)(rec + 1);
+ char *ptr = buf;
+ char *end = buf + size;
+ int rc = 0;
+
+ LASSERT(rec->lrh_type == OBD_CFG_REC);
+ rc = lustre_cfg_sanity_check(lcfg, rec->lrh_len);
+ if (rc < 0)
+ return rc;
+
+ ptr += snprintf(ptr, end-ptr, "cmd=%05x ", lcfg->lcfg_command);
+ if (lcfg->lcfg_flags)
+ ptr += snprintf(ptr, end-ptr, "flags=%#08x ",
+ lcfg->lcfg_flags);
+
+ if (lcfg->lcfg_num)
+ ptr += snprintf(ptr, end-ptr, "num=%#08x ", lcfg->lcfg_num);
+
+ if (lcfg->lcfg_nid)
+ ptr += snprintf(ptr, end-ptr, "nid=%s(%#llx)\n ",
+ libcfs_nid2str(lcfg->lcfg_nid),
+ lcfg->lcfg_nid);
+
+ if (lcfg->lcfg_command == LCFG_MARKER) {
+ struct cfg_marker *marker = lustre_cfg_buf(lcfg, 1);
+
+ ptr += snprintf(ptr, end-ptr, "marker=%d(%#x)%s '%s'",
+ marker->cm_step, marker->cm_flags,
+ marker->cm_tgtname, marker->cm_comment);
+ } else {
+ int i;
+
+ for (i = 0; i < lcfg->lcfg_bufcount; i++) {
+ ptr += snprintf(ptr, end-ptr, "%d:%s ", i,
+ lustre_cfg_string(lcfg, i));
+ }
+ }
+ /* return consumed bytes */
+ rc = ptr - buf;
+ return rc;
+}
+
+int class_config_dump_handler(const struct lu_env *env,
+ struct llog_handle *handle,
+ struct llog_rec_hdr *rec, void *data)
+{
+ char *outstr;
+ int rc = 0;
+
+ OBD_ALLOC(outstr, 256);
+ if (outstr == NULL)
+ return -ENOMEM;
+
+ if (rec->lrh_type == OBD_CFG_REC) {
+ class_config_parse_rec(rec, outstr, 256);
+ LCONSOLE(D_WARNING, " %s\n", outstr);
+ } else {
+ LCONSOLE(D_WARNING, "unhandled lrh_type: %#x\n", rec->lrh_type);
+ rc = -EINVAL;
+ }
+
+ OBD_FREE(outstr, 256);
+ return rc;
+}
+
+int class_config_dump_llog(const struct lu_env *env, struct llog_ctxt *ctxt,
+ char *name, struct config_llog_instance *cfg)
+{
+ struct llog_handle *llh;
+ int rc;
+
+ LCONSOLE_INFO("Dumping config log %s\n", name);
+
+ rc = llog_open(env, ctxt, &llh, NULL, name, LLOG_OPEN_EXISTS);
+ if (rc)
+ return rc;
+
+ rc = llog_init_handle(env, llh, LLOG_F_IS_PLAIN, NULL);
+ if (rc)
+ goto parse_out;
+
+ rc = llog_process(env, llh, class_config_dump_handler, cfg, NULL);
+parse_out:
+ llog_close(env, llh);
+
+ LCONSOLE_INFO("End config log %s\n", name);
+ return rc;
+}
+EXPORT_SYMBOL(class_config_dump_llog);
+
+/** Call class_cleanup and class_detach.
+ * "Manual" only in the sense that we're faking lcfg commands.
+ */
+int class_manual_cleanup(struct obd_device *obd)
+{
+ char flags[3] = "";
+ struct lustre_cfg *lcfg;
+ struct lustre_cfg_bufs bufs;
+ int rc;
+
+ if (!obd) {
+ CERROR("empty cleanup\n");
+ return -EALREADY;
+ }
+
+ if (obd->obd_force)
+ strcat(flags, "F");
+ if (obd->obd_fail)
+ strcat(flags, "A");
+
+ CDEBUG(D_CONFIG, "Manual cleanup of %s (flags='%s')\n",
+ obd->obd_name, flags);
+
+ lustre_cfg_bufs_reset(&bufs, obd->obd_name);
+ lustre_cfg_bufs_set_string(&bufs, 1, flags);
+ lcfg = lustre_cfg_new(LCFG_CLEANUP, &bufs);
+ if (!lcfg)
+ return -ENOMEM;
+
+ rc = class_process_config(lcfg);
+ if (rc) {
+ CERROR("cleanup failed %d: %s\n", rc, obd->obd_name);
+ goto out;
+ }
+
+ /* the lcfg is almost the same for both ops */
+ lcfg->lcfg_command = LCFG_DETACH;
+ rc = class_process_config(lcfg);
+ if (rc)
+ CERROR("detach failed %d: %s\n", rc, obd->obd_name);
+out:
+ lustre_cfg_free(lcfg);
+ return rc;
+}
+EXPORT_SYMBOL(class_manual_cleanup);
+
+/*
+ * uuid<->export lustre hash operations
+ */
+
+static unsigned
+uuid_hash(struct cfs_hash *hs, const void *key, unsigned mask)
+{
+ return cfs_hash_djb2_hash(((struct obd_uuid *)key)->uuid,
+ sizeof(((struct obd_uuid *)key)->uuid), mask);
+}
+
+static void *
+uuid_key(struct hlist_node *hnode)
+{
+ struct obd_export *exp;
+
+ exp = hlist_entry(hnode, struct obd_export, exp_uuid_hash);
+
+ return &exp->exp_client_uuid;
+}
+
+/*
+ * NOTE: It is impossible to find an export that is in failed
+ * state with this function
+ */
+static int
+uuid_keycmp(const void *key, struct hlist_node *hnode)
+{
+ struct obd_export *exp;
+
+ LASSERT(key);
+ exp = hlist_entry(hnode, struct obd_export, exp_uuid_hash);
+
+ return obd_uuid_equals(key, &exp->exp_client_uuid) &&
+ !exp->exp_failed;
+}
+
+static void *
+uuid_export_object(struct hlist_node *hnode)
+{
+ return hlist_entry(hnode, struct obd_export, exp_uuid_hash);
+}
+
+static void
+uuid_export_get(struct cfs_hash *hs, struct hlist_node *hnode)
+{
+ struct obd_export *exp;
+
+ exp = hlist_entry(hnode, struct obd_export, exp_uuid_hash);
+ class_export_get(exp);
+}
+
+static void
+uuid_export_put_locked(struct cfs_hash *hs, struct hlist_node *hnode)
+{
+ struct obd_export *exp;
+
+ exp = hlist_entry(hnode, struct obd_export, exp_uuid_hash);
+ class_export_put(exp);
+}
+
+static cfs_hash_ops_t uuid_hash_ops = {
+ .hs_hash = uuid_hash,
+ .hs_key = uuid_key,
+ .hs_keycmp = uuid_keycmp,
+ .hs_object = uuid_export_object,
+ .hs_get = uuid_export_get,
+ .hs_put_locked = uuid_export_put_locked,
+};
+
+
+/*
+ * nid<->export hash operations
+ */
+
+static unsigned
+nid_hash(struct cfs_hash *hs, const void *key, unsigned mask)
+{
+ return cfs_hash_djb2_hash(key, sizeof(lnet_nid_t), mask);
+}
+
+static void *
+nid_key(struct hlist_node *hnode)
+{
+ struct obd_export *exp;
+
+ exp = hlist_entry(hnode, struct obd_export, exp_nid_hash);
+
+ return &exp->exp_connection->c_peer.nid;
+}
+
+/*
+ * NOTE: It is impossible to find an export that is in failed
+ * state with this function
+ */
+static int
+nid_kepcmp(const void *key, struct hlist_node *hnode)
+{
+ struct obd_export *exp;
+
+ LASSERT(key);
+ exp = hlist_entry(hnode, struct obd_export, exp_nid_hash);
+
+ return exp->exp_connection->c_peer.nid == *(lnet_nid_t *)key &&
+ !exp->exp_failed;
+}
+
+static void *
+nid_export_object(struct hlist_node *hnode)
+{
+ return hlist_entry(hnode, struct obd_export, exp_nid_hash);
+}
+
+static void
+nid_export_get(struct cfs_hash *hs, struct hlist_node *hnode)
+{
+ struct obd_export *exp;
+
+ exp = hlist_entry(hnode, struct obd_export, exp_nid_hash);
+ class_export_get(exp);
+}
+
+static void
+nid_export_put_locked(struct cfs_hash *hs, struct hlist_node *hnode)
+{
+ struct obd_export *exp;
+
+ exp = hlist_entry(hnode, struct obd_export, exp_nid_hash);
+ class_export_put(exp);
+}
+
+static cfs_hash_ops_t nid_hash_ops = {
+ .hs_hash = nid_hash,
+ .hs_key = nid_key,
+ .hs_keycmp = nid_kepcmp,
+ .hs_object = nid_export_object,
+ .hs_get = nid_export_get,
+ .hs_put_locked = nid_export_put_locked,
+};
+
+
+/*
+ * nid<->nidstats hash operations
+ */
+
+static void *
+nidstats_key(struct hlist_node *hnode)
+{
+ struct nid_stat *ns;
+
+ ns = hlist_entry(hnode, struct nid_stat, nid_hash);
+
+ return &ns->nid;
+}
+
+static int
+nidstats_keycmp(const void *key, struct hlist_node *hnode)
+{
+ return *(lnet_nid_t *)nidstats_key(hnode) == *(lnet_nid_t *)key;
+}
+
+static void *
+nidstats_object(struct hlist_node *hnode)
+{
+ return hlist_entry(hnode, struct nid_stat, nid_hash);
+}
+
+static void
+nidstats_get(struct cfs_hash *hs, struct hlist_node *hnode)
+{
+ struct nid_stat *ns;
+
+ ns = hlist_entry(hnode, struct nid_stat, nid_hash);
+ nidstat_getref(ns);
+}
+
+static void
+nidstats_put_locked(struct cfs_hash *hs, struct hlist_node *hnode)
+{
+ struct nid_stat *ns;
+
+ ns = hlist_entry(hnode, struct nid_stat, nid_hash);
+ nidstat_putref(ns);
+}
+
+static cfs_hash_ops_t nid_stat_hash_ops = {
+ .hs_hash = nid_hash,
+ .hs_key = nidstats_key,
+ .hs_keycmp = nidstats_keycmp,
+ .hs_object = nidstats_object,
+ .hs_get = nidstats_get,
+ .hs_put_locked = nidstats_put_locked,
+};
diff --git a/drivers/staging/lustre/lustre/obdclass/obd_mount.c b/drivers/staging/lustre/lustre/obdclass/obd_mount.c
new file mode 100644
index 000000000..3437b2ecf
--- /dev/null
+++ b/drivers/staging/lustre/lustre/obdclass/obd_mount.c
@@ -0,0 +1,1319 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2011, 2012, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ *
+ * lustre/obdclass/obd_mount.c
+ *
+ * Client mount routines
+ *
+ * Author: Nathan Rutman <nathan@clusterfs.com>
+ */
+
+
+#define DEBUG_SUBSYSTEM S_CLASS
+#define D_MOUNT (D_SUPER|D_CONFIG/*|D_WARNING */)
+#define PRINT_CMD CDEBUG
+
+#include "../include/obd.h"
+#include "../include/linux/lustre_compat25.h"
+#include "../include/obd_class.h"
+#include "../include/lustre/lustre_user.h"
+#include "../include/lustre_log.h"
+#include "../include/lustre_disk.h"
+#include "../include/lustre_param.h"
+
+static int (*client_fill_super)(struct super_block *sb,
+ struct vfsmount *mnt);
+
+static void (*kill_super_cb)(struct super_block *sb);
+
+/**************** config llog ********************/
+
+/** Get a config log from the MGS and process it.
+ * This func is called for both clients and servers.
+ * Continue to process new statements appended to the logs
+ * (whenever the config lock is revoked) until lustre_end_log
+ * is called.
+ * @param sb The superblock is used by the MGC to write to the local copy of
+ * the config log
+ * @param logname The name of the llog to replicate from the MGS
+ * @param cfg Since the same mgc may be used to follow multiple config logs
+ * (e.g. ost1, ost2, client), the config_llog_instance keeps the state for
+ * this log, and is added to the mgc's list of logs to follow.
+ */
+int lustre_process_log(struct super_block *sb, char *logname,
+ struct config_llog_instance *cfg)
+{
+ struct lustre_cfg *lcfg;
+ struct lustre_cfg_bufs *bufs;
+ struct lustre_sb_info *lsi = s2lsi(sb);
+ struct obd_device *mgc = lsi->lsi_mgc;
+ int rc;
+
+ LASSERT(mgc);
+ LASSERT(cfg);
+
+ OBD_ALLOC_PTR(bufs);
+ if (bufs == NULL)
+ return -ENOMEM;
+
+ /* mgc_process_config */
+ lustre_cfg_bufs_reset(bufs, mgc->obd_name);
+ lustre_cfg_bufs_set_string(bufs, 1, logname);
+ lustre_cfg_bufs_set(bufs, 2, cfg, sizeof(*cfg));
+ lustre_cfg_bufs_set(bufs, 3, &sb, sizeof(sb));
+ lcfg = lustre_cfg_new(LCFG_LOG_START, bufs);
+ rc = obd_process_config(mgc, sizeof(*lcfg), lcfg);
+ lustre_cfg_free(lcfg);
+
+ OBD_FREE_PTR(bufs);
+
+ if (rc == -EINVAL)
+ LCONSOLE_ERROR_MSG(0x15b, "%s: The configuration from log '%s' failed from the MGS (%d). Make sure this client and the MGS are running compatible versions of Lustre.\n",
+ mgc->obd_name, logname, rc);
+
+ if (rc)
+ LCONSOLE_ERROR_MSG(0x15c, "%s: The configuration from log '%s' failed (%d). This may be the result of communication errors between this node and the MGS, a bad configuration, or other errors. See the syslog for more information.\n",
+ mgc->obd_name, logname,
+ rc);
+
+ /* class_obd_list(); */
+ return rc;
+}
+EXPORT_SYMBOL(lustre_process_log);
+
+/* Stop watching this config log for updates */
+int lustre_end_log(struct super_block *sb, char *logname,
+ struct config_llog_instance *cfg)
+{
+ struct lustre_cfg *lcfg;
+ struct lustre_cfg_bufs bufs;
+ struct lustre_sb_info *lsi = s2lsi(sb);
+ struct obd_device *mgc = lsi->lsi_mgc;
+ int rc;
+
+ if (!mgc)
+ return -ENOENT;
+
+ /* mgc_process_config */
+ lustre_cfg_bufs_reset(&bufs, mgc->obd_name);
+ lustre_cfg_bufs_set_string(&bufs, 1, logname);
+ if (cfg)
+ lustre_cfg_bufs_set(&bufs, 2, cfg, sizeof(*cfg));
+ lcfg = lustre_cfg_new(LCFG_LOG_END, &bufs);
+ rc = obd_process_config(mgc, sizeof(*lcfg), lcfg);
+ lustre_cfg_free(lcfg);
+ return rc;
+}
+EXPORT_SYMBOL(lustre_end_log);
+
+/**************** obd start *******************/
+
+/** lustre_cfg_bufs are a holdover from 1.4; we can still set these up from
+ * lctl (and do for echo cli/srv.
+ */
+int do_lcfg(char *cfgname, lnet_nid_t nid, int cmd,
+ char *s1, char *s2, char *s3, char *s4)
+{
+ struct lustre_cfg_bufs bufs;
+ struct lustre_cfg * lcfg = NULL;
+ int rc;
+
+ CDEBUG(D_TRACE, "lcfg %s %#x %s %s %s %s\n", cfgname,
+ cmd, s1, s2, s3, s4);
+
+ lustre_cfg_bufs_reset(&bufs, cfgname);
+ if (s1)
+ lustre_cfg_bufs_set_string(&bufs, 1, s1);
+ if (s2)
+ lustre_cfg_bufs_set_string(&bufs, 2, s2);
+ if (s3)
+ lustre_cfg_bufs_set_string(&bufs, 3, s3);
+ if (s4)
+ lustre_cfg_bufs_set_string(&bufs, 4, s4);
+
+ lcfg = lustre_cfg_new(cmd, &bufs);
+ lcfg->lcfg_nid = nid;
+ rc = class_process_config(lcfg);
+ lustre_cfg_free(lcfg);
+ return rc;
+}
+EXPORT_SYMBOL(do_lcfg);
+
+/** Call class_attach and class_setup. These methods in turn call
+ * obd type-specific methods.
+ */
+int lustre_start_simple(char *obdname, char *type, char *uuid,
+ char *s1, char *s2, char *s3, char *s4)
+{
+ int rc;
+ CDEBUG(D_MOUNT, "Starting obd %s (typ=%s)\n", obdname, type);
+
+ rc = do_lcfg(obdname, 0, LCFG_ATTACH, type, uuid, NULL, NULL);
+ if (rc) {
+ CERROR("%s attach error %d\n", obdname, rc);
+ return rc;
+ }
+ rc = do_lcfg(obdname, 0, LCFG_SETUP, s1, s2, s3, s4);
+ if (rc) {
+ CERROR("%s setup error %d\n", obdname, rc);
+ do_lcfg(obdname, 0, LCFG_DETACH, NULL, NULL, NULL, NULL);
+ }
+ return rc;
+}
+
+DEFINE_MUTEX(mgc_start_lock);
+
+/** Set up a mgc obd to process startup logs
+ *
+ * \param sb [in] super block of the mgc obd
+ *
+ * \retval 0 success, otherwise error code
+ */
+int lustre_start_mgc(struct super_block *sb)
+{
+ struct obd_connect_data *data = NULL;
+ struct lustre_sb_info *lsi = s2lsi(sb);
+ struct obd_device *obd;
+ struct obd_export *exp;
+ struct obd_uuid *uuid;
+ class_uuid_t uuidc;
+ lnet_nid_t nid;
+ char *mgcname = NULL, *niduuid = NULL, *mgssec = NULL;
+ char *ptr;
+ int rc = 0, i = 0, j, len;
+
+ LASSERT(lsi->lsi_lmd);
+
+ /* Find the first non-lo MGS nid for our MGC name */
+ if (IS_SERVER(lsi)) {
+ /* mount -o mgsnode=nid */
+ ptr = lsi->lsi_lmd->lmd_mgs;
+ if (lsi->lsi_lmd->lmd_mgs &&
+ (class_parse_nid(lsi->lsi_lmd->lmd_mgs, &nid, &ptr) == 0)) {
+ i++;
+ } else if (IS_MGS(lsi)) {
+ lnet_process_id_t id;
+ while ((rc = LNetGetId(i++, &id)) != -ENOENT) {
+ if (LNET_NETTYP(LNET_NIDNET(id.nid)) == LOLND)
+ continue;
+ nid = id.nid;
+ i++;
+ break;
+ }
+ }
+ } else { /* client */
+ /* Use nids from mount line: uml1,1@elan:uml2,2@elan:/lustre */
+ ptr = lsi->lsi_lmd->lmd_dev;
+ if (class_parse_nid(ptr, &nid, &ptr) == 0)
+ i++;
+ }
+ if (i == 0) {
+ CERROR("No valid MGS nids found.\n");
+ return -EINVAL;
+ }
+
+ mutex_lock(&mgc_start_lock);
+
+ len = strlen(LUSTRE_MGC_OBDNAME) + strlen(libcfs_nid2str(nid)) + 1;
+ OBD_ALLOC(mgcname, len);
+ OBD_ALLOC(niduuid, len + 2);
+ if (!mgcname || !niduuid) {
+ rc = -ENOMEM;
+ goto out_free;
+ }
+ sprintf(mgcname, "%s%s", LUSTRE_MGC_OBDNAME, libcfs_nid2str(nid));
+
+ mgssec = lsi->lsi_lmd->lmd_mgssec ? lsi->lsi_lmd->lmd_mgssec : "";
+
+ OBD_ALLOC_PTR(data);
+ if (data == NULL) {
+ rc = -ENOMEM;
+ goto out_free;
+ }
+
+ obd = class_name2obd(mgcname);
+ if (obd && !obd->obd_stopping) {
+ int recov_bk;
+
+ rc = obd_set_info_async(NULL, obd->obd_self_export,
+ strlen(KEY_MGSSEC), KEY_MGSSEC,
+ strlen(mgssec), mgssec, NULL);
+ if (rc)
+ goto out_free;
+
+ /* Re-using an existing MGC */
+ atomic_inc(&obd->u.cli.cl_mgc_refcount);
+
+ /* IR compatibility check, only for clients */
+ if (lmd_is_client(lsi->lsi_lmd)) {
+ int has_ir;
+ int vallen = sizeof(*data);
+ __u32 *flags = &lsi->lsi_lmd->lmd_flags;
+
+ rc = obd_get_info(NULL, obd->obd_self_export,
+ strlen(KEY_CONN_DATA), KEY_CONN_DATA,
+ &vallen, data, NULL);
+ LASSERT(rc == 0);
+ has_ir = OCD_HAS_FLAG(data, IMP_RECOV);
+ if (has_ir ^ !(*flags & LMD_FLG_NOIR)) {
+ /* LMD_FLG_NOIR is for test purpose only */
+ LCONSOLE_WARN(
+ "Trying to mount a client with IR setting not compatible with current mgc. Force to use current mgc setting that is IR %s.\n",
+ has_ir ? "enabled" : "disabled");
+ if (has_ir)
+ *flags &= ~LMD_FLG_NOIR;
+ else
+ *flags |= LMD_FLG_NOIR;
+ }
+ }
+
+ recov_bk = 0;
+ /* If we are restarting the MGS, don't try to keep the MGC's
+ old connection, or registration will fail. */
+ if (IS_MGS(lsi)) {
+ CDEBUG(D_MOUNT, "New MGS with live MGC\n");
+ recov_bk = 1;
+ }
+
+ /* Try all connections, but only once (again).
+ We don't want to block another target from starting
+ (using its local copy of the log), but we do want to connect
+ if at all possible. */
+ recov_bk++;
+ CDEBUG(D_MOUNT, "%s: Set MGC reconnect %d\n", mgcname,
+ recov_bk);
+ rc = obd_set_info_async(NULL, obd->obd_self_export,
+ sizeof(KEY_INIT_RECOV_BACKUP),
+ KEY_INIT_RECOV_BACKUP,
+ sizeof(recov_bk), &recov_bk, NULL);
+ rc = 0;
+ goto out;
+ }
+
+ CDEBUG(D_MOUNT, "Start MGC '%s'\n", mgcname);
+
+ /* Add the primary nids for the MGS */
+ i = 0;
+ sprintf(niduuid, "%s_%x", mgcname, i);
+ if (IS_SERVER(lsi)) {
+ ptr = lsi->lsi_lmd->lmd_mgs;
+ if (IS_MGS(lsi)) {
+ /* Use local nids (including LO) */
+ lnet_process_id_t id;
+ while ((rc = LNetGetId(i++, &id)) != -ENOENT) {
+ rc = do_lcfg(mgcname, id.nid,
+ LCFG_ADD_UUID, niduuid,
+ NULL, NULL, NULL);
+ }
+ } else {
+ /* Use mgsnode= nids */
+ /* mount -o mgsnode=nid */
+ if (lsi->lsi_lmd->lmd_mgs) {
+ ptr = lsi->lsi_lmd->lmd_mgs;
+ } else if (class_find_param(ptr, PARAM_MGSNODE,
+ &ptr) != 0) {
+ CERROR("No MGS nids given.\n");
+ rc = -EINVAL;
+ goto out_free;
+ }
+ while (class_parse_nid(ptr, &nid, &ptr) == 0) {
+ rc = do_lcfg(mgcname, nid,
+ LCFG_ADD_UUID, niduuid,
+ NULL, NULL, NULL);
+ i++;
+ }
+ }
+ } else { /* client */
+ /* Use nids from mount line: uml1,1@elan:uml2,2@elan:/lustre */
+ ptr = lsi->lsi_lmd->lmd_dev;
+ while (class_parse_nid(ptr, &nid, &ptr) == 0) {
+ rc = do_lcfg(mgcname, nid,
+ LCFG_ADD_UUID, niduuid, NULL, NULL, NULL);
+ i++;
+ /* Stop at the first failover nid */
+ if (*ptr == ':')
+ break;
+ }
+ }
+ if (i == 0) {
+ CERROR("No valid MGS nids found.\n");
+ rc = -EINVAL;
+ goto out_free;
+ }
+ lsi->lsi_lmd->lmd_mgs_failnodes = 1;
+
+ /* Random uuid for MGC allows easier reconnects */
+ OBD_ALLOC_PTR(uuid);
+ if (!uuid) {
+ rc = -ENOMEM;
+ goto out_free;
+ }
+
+ ll_generate_random_uuid(uuidc);
+ class_uuid_unparse(uuidc, uuid);
+
+ /* Start the MGC */
+ rc = lustre_start_simple(mgcname, LUSTRE_MGC_NAME,
+ (char *)uuid->uuid, LUSTRE_MGS_OBDNAME,
+ niduuid, NULL, NULL);
+ OBD_FREE_PTR(uuid);
+ if (rc)
+ goto out_free;
+
+ /* Add any failover MGS nids */
+ i = 1;
+ while (ptr && ((*ptr == ':' ||
+ class_find_param(ptr, PARAM_MGSNODE, &ptr) == 0))) {
+ /* New failover node */
+ sprintf(niduuid, "%s_%x", mgcname, i);
+ j = 0;
+ while (class_parse_nid_quiet(ptr, &nid, &ptr) == 0) {
+ j++;
+ rc = do_lcfg(mgcname, nid,
+ LCFG_ADD_UUID, niduuid, NULL, NULL, NULL);
+ if (*ptr == ':')
+ break;
+ }
+ if (j > 0) {
+ rc = do_lcfg(mgcname, 0, LCFG_ADD_CONN,
+ niduuid, NULL, NULL, NULL);
+ i++;
+ } else {
+ /* at ":/fsname" */
+ break;
+ }
+ }
+ lsi->lsi_lmd->lmd_mgs_failnodes = i;
+
+ obd = class_name2obd(mgcname);
+ if (!obd) {
+ CERROR("Can't find mgcobd %s\n", mgcname);
+ rc = -ENOTCONN;
+ goto out_free;
+ }
+
+ rc = obd_set_info_async(NULL, obd->obd_self_export,
+ strlen(KEY_MGSSEC), KEY_MGSSEC,
+ strlen(mgssec), mgssec, NULL);
+ if (rc)
+ goto out_free;
+
+ /* Keep a refcount of servers/clients who started with "mount",
+ so we know when we can get rid of the mgc. */
+ atomic_set(&obd->u.cli.cl_mgc_refcount, 1);
+
+ /* We connect to the MGS at setup, and don't disconnect until cleanup */
+ data->ocd_connect_flags = OBD_CONNECT_VERSION | OBD_CONNECT_AT |
+ OBD_CONNECT_FULL20 | OBD_CONNECT_IMP_RECOV |
+ OBD_CONNECT_LVB_TYPE;
+
+#if LUSTRE_VERSION_CODE < OBD_OCD_VERSION(3, 2, 50, 0)
+ data->ocd_connect_flags |= OBD_CONNECT_MNE_SWAB;
+#else
+#warning "LU-1644: Remove old OBD_CONNECT_MNE_SWAB fixup and imp_need_mne_swab"
+#endif
+
+ if (lmd_is_client(lsi->lsi_lmd) &&
+ lsi->lsi_lmd->lmd_flags & LMD_FLG_NOIR)
+ data->ocd_connect_flags &= ~OBD_CONNECT_IMP_RECOV;
+ data->ocd_version = LUSTRE_VERSION_CODE;
+ rc = obd_connect(NULL, &exp, obd, &(obd->obd_uuid), data, NULL);
+ if (rc) {
+ CERROR("connect failed %d\n", rc);
+ goto out;
+ }
+
+ obd->u.cli.cl_mgc_mgsexp = exp;
+
+out:
+ /* Keep the mgc info in the sb. Note that many lsi's can point
+ to the same mgc.*/
+ lsi->lsi_mgc = obd;
+out_free:
+ mutex_unlock(&mgc_start_lock);
+
+ if (data)
+ OBD_FREE_PTR(data);
+ if (mgcname)
+ OBD_FREE(mgcname, len);
+ if (niduuid)
+ OBD_FREE(niduuid, len + 2);
+ return rc;
+}
+
+static int lustre_stop_mgc(struct super_block *sb)
+{
+ struct lustre_sb_info *lsi = s2lsi(sb);
+ struct obd_device *obd;
+ char *niduuid = NULL, *ptr = NULL;
+ int i, rc = 0, len = 0;
+
+ if (!lsi)
+ return -ENOENT;
+ obd = lsi->lsi_mgc;
+ if (!obd)
+ return -ENOENT;
+ lsi->lsi_mgc = NULL;
+
+ mutex_lock(&mgc_start_lock);
+ LASSERT(atomic_read(&obd->u.cli.cl_mgc_refcount) > 0);
+ if (!atomic_dec_and_test(&obd->u.cli.cl_mgc_refcount)) {
+ /* This is not fatal, every client that stops
+ will call in here. */
+ CDEBUG(D_MOUNT, "mgc still has %d references.\n",
+ atomic_read(&obd->u.cli.cl_mgc_refcount));
+ rc = -EBUSY;
+ goto out;
+ }
+
+ /* The MGC has no recoverable data in any case.
+ * force shutdown set in umount_begin */
+ obd->obd_no_recov = 1;
+
+ if (obd->u.cli.cl_mgc_mgsexp) {
+ /* An error is not fatal, if we are unable to send the
+ disconnect mgs ping evictor cleans up the export */
+ rc = obd_disconnect(obd->u.cli.cl_mgc_mgsexp);
+ if (rc)
+ CDEBUG(D_MOUNT, "disconnect failed %d\n", rc);
+ }
+
+ /* Save the obdname for cleaning the nid uuids, which are
+ obdname_XX */
+ len = strlen(obd->obd_name) + 6;
+ OBD_ALLOC(niduuid, len);
+ if (niduuid) {
+ strcpy(niduuid, obd->obd_name);
+ ptr = niduuid + strlen(niduuid);
+ }
+
+ rc = class_manual_cleanup(obd);
+ if (rc)
+ goto out;
+
+ /* Clean the nid uuids */
+ if (!niduuid) {
+ rc = -ENOMEM;
+ goto out;
+ }
+
+ for (i = 0; i < lsi->lsi_lmd->lmd_mgs_failnodes; i++) {
+ sprintf(ptr, "_%x", i);
+ rc = do_lcfg(LUSTRE_MGC_OBDNAME, 0, LCFG_DEL_UUID,
+ niduuid, NULL, NULL, NULL);
+ if (rc)
+ CERROR("del MDC UUID %s failed: rc = %d\n",
+ niduuid, rc);
+ }
+out:
+ if (niduuid)
+ OBD_FREE(niduuid, len);
+
+ /* class_import_put will get rid of the additional connections */
+ mutex_unlock(&mgc_start_lock);
+ return rc;
+}
+
+/***************** lustre superblock **************/
+
+struct lustre_sb_info *lustre_init_lsi(struct super_block *sb)
+{
+ struct lustre_sb_info *lsi;
+
+ OBD_ALLOC_PTR(lsi);
+ if (!lsi)
+ return NULL;
+ OBD_ALLOC_PTR(lsi->lsi_lmd);
+ if (!lsi->lsi_lmd) {
+ OBD_FREE_PTR(lsi);
+ return NULL;
+ }
+
+ lsi->lsi_lmd->lmd_exclude_count = 0;
+ lsi->lsi_lmd->lmd_recovery_time_soft = 0;
+ lsi->lsi_lmd->lmd_recovery_time_hard = 0;
+ s2lsi_nocast(sb) = lsi;
+ /* we take 1 extra ref for our setup */
+ atomic_set(&lsi->lsi_mounts, 1);
+
+ /* Default umount style */
+ lsi->lsi_flags = LSI_UMOUNT_FAILOVER;
+
+ return lsi;
+}
+
+static int lustre_free_lsi(struct super_block *sb)
+{
+ struct lustre_sb_info *lsi = s2lsi(sb);
+
+ LASSERT(lsi != NULL);
+ CDEBUG(D_MOUNT, "Freeing lsi %p\n", lsi);
+
+ /* someone didn't call server_put_mount. */
+ LASSERT(atomic_read(&lsi->lsi_mounts) == 0);
+
+ if (lsi->lsi_lmd != NULL) {
+ if (lsi->lsi_lmd->lmd_dev != NULL)
+ OBD_FREE(lsi->lsi_lmd->lmd_dev,
+ strlen(lsi->lsi_lmd->lmd_dev) + 1);
+ if (lsi->lsi_lmd->lmd_profile != NULL)
+ OBD_FREE(lsi->lsi_lmd->lmd_profile,
+ strlen(lsi->lsi_lmd->lmd_profile) + 1);
+ if (lsi->lsi_lmd->lmd_mgssec != NULL)
+ OBD_FREE(lsi->lsi_lmd->lmd_mgssec,
+ strlen(lsi->lsi_lmd->lmd_mgssec) + 1);
+ if (lsi->lsi_lmd->lmd_opts != NULL)
+ OBD_FREE(lsi->lsi_lmd->lmd_opts,
+ strlen(lsi->lsi_lmd->lmd_opts) + 1);
+ if (lsi->lsi_lmd->lmd_exclude_count)
+ OBD_FREE(lsi->lsi_lmd->lmd_exclude,
+ sizeof(lsi->lsi_lmd->lmd_exclude[0]) *
+ lsi->lsi_lmd->lmd_exclude_count);
+ if (lsi->lsi_lmd->lmd_mgs != NULL)
+ OBD_FREE(lsi->lsi_lmd->lmd_mgs,
+ strlen(lsi->lsi_lmd->lmd_mgs) + 1);
+ if (lsi->lsi_lmd->lmd_osd_type != NULL)
+ OBD_FREE(lsi->lsi_lmd->lmd_osd_type,
+ strlen(lsi->lsi_lmd->lmd_osd_type) + 1);
+ if (lsi->lsi_lmd->lmd_params != NULL)
+ OBD_FREE(lsi->lsi_lmd->lmd_params, 4096);
+
+ OBD_FREE(lsi->lsi_lmd, sizeof(*lsi->lsi_lmd));
+ }
+
+ LASSERT(lsi->lsi_llsbi == NULL);
+ OBD_FREE(lsi, sizeof(*lsi));
+ s2lsi_nocast(sb) = NULL;
+
+ return 0;
+}
+
+/* The lsi has one reference for every server that is using the disk -
+ e.g. MDT, MGS, and potentially MGC */
+int lustre_put_lsi(struct super_block *sb)
+{
+ struct lustre_sb_info *lsi = s2lsi(sb);
+
+ LASSERT(lsi != NULL);
+
+ CDEBUG(D_MOUNT, "put %p %d\n", sb, atomic_read(&lsi->lsi_mounts));
+ if (atomic_dec_and_test(&lsi->lsi_mounts)) {
+ if (IS_SERVER(lsi) && lsi->lsi_osd_exp) {
+ lu_device_put(&lsi->lsi_dt_dev->dd_lu_dev);
+ lsi->lsi_osd_exp->exp_obd->obd_lvfs_ctxt.dt = NULL;
+ lsi->lsi_dt_dev = NULL;
+ obd_disconnect(lsi->lsi_osd_exp);
+ /* wait till OSD is gone */
+ obd_zombie_barrier();
+ }
+ lustre_free_lsi(sb);
+ return 1;
+ }
+ return 0;
+}
+
+/*** SERVER NAME ***
+ * <FSNAME><SEPARATOR><TYPE><INDEX>
+ * FSNAME is between 1 and 8 characters (inclusive).
+ * Excluded characters are '/' and ':'
+ * SEPARATOR is either ':' or '-'
+ * TYPE: "OST", "MDT", etc.
+ * INDEX: Hex representation of the index
+ */
+
+/** Get the fsname ("lustre") from the server name ("lustre-OST003F").
+ * @param [in] svname server name including type and index
+ * @param [out] fsname Buffer to copy filesystem name prefix into.
+ * Must have at least 'strlen(fsname) + 1' chars.
+ * @param [out] endptr if endptr isn't NULL it is set to end of fsname
+ * rc < 0 on error
+ */
+int server_name2fsname(const char *svname, char *fsname, const char **endptr)
+{
+ const char *dash;
+
+ dash = svname + strnlen(svname, 8); /* max fsname length is 8 */
+ for (; dash > svname && *dash != '-' && *dash != ':'; dash--)
+ ;
+ if (dash == svname)
+ return -EINVAL;
+
+ if (fsname != NULL) {
+ strncpy(fsname, svname, dash - svname);
+ fsname[dash - svname] = '\0';
+ }
+
+ if (endptr != NULL)
+ *endptr = dash;
+
+ return 0;
+}
+EXPORT_SYMBOL(server_name2fsname);
+
+/**
+ * Get service name (svname) from string
+ * rc < 0 on error
+ * if endptr isn't NULL it is set to end of fsname *
+ */
+int server_name2svname(const char *label, char *svname, const char **endptr,
+ size_t svsize)
+{
+ int rc;
+ const char *dash;
+
+ /* We use server_name2fsname() just for parsing */
+ rc = server_name2fsname(label, NULL, &dash);
+ if (rc != 0)
+ return rc;
+
+ if (endptr != NULL)
+ *endptr = dash;
+
+ if (strlcpy(svname, dash + 1, svsize) >= svsize)
+ return -E2BIG;
+
+ return 0;
+}
+EXPORT_SYMBOL(server_name2svname);
+
+
+/* Get the index from the obd name.
+ rc = server type, or
+ rc < 0 on error
+ if endptr isn't NULL it is set to end of name */
+int server_name2index(const char *svname, __u32 *idx, const char **endptr)
+{
+ unsigned long index;
+ int rc;
+ const char *dash;
+
+ /* We use server_name2fsname() just for parsing */
+ rc = server_name2fsname(svname, NULL, &dash);
+ if (rc != 0)
+ return rc;
+
+ dash++;
+
+ if (strncmp(dash, "MDT", 3) == 0)
+ rc = LDD_F_SV_TYPE_MDT;
+ else if (strncmp(dash, "OST", 3) == 0)
+ rc = LDD_F_SV_TYPE_OST;
+ else
+ return -EINVAL;
+
+ dash += 3;
+
+ if (strncmp(dash, "all", 3) == 0) {
+ if (endptr != NULL)
+ *endptr = dash + 3;
+ return rc | LDD_F_SV_ALL;
+ }
+
+ index = simple_strtoul(dash, (char **)endptr, 16);
+ if (idx != NULL)
+ *idx = index;
+
+ /* Account for -mdc after index that is possible when specifying mdt */
+ if (endptr != NULL && strncmp(LUSTRE_MDC_NAME, *endptr + 1,
+ sizeof(LUSTRE_MDC_NAME)-1) == 0)
+ *endptr += sizeof(LUSTRE_MDC_NAME);
+
+ return rc;
+}
+EXPORT_SYMBOL(server_name2index);
+
+/*************** mount common between server and client ***************/
+
+/* Common umount */
+int lustre_common_put_super(struct super_block *sb)
+{
+ int rc;
+
+ CDEBUG(D_MOUNT, "dropping sb %p\n", sb);
+
+ /* Drop a ref to the MGC */
+ rc = lustre_stop_mgc(sb);
+ if (rc && (rc != -ENOENT)) {
+ if (rc != -EBUSY) {
+ CERROR("Can't stop MGC: %d\n", rc);
+ return rc;
+ }
+ /* BUSY just means that there's some other obd that
+ needs the mgc. Let him clean it up. */
+ CDEBUG(D_MOUNT, "MGC still in use\n");
+ }
+ /* Drop a ref to the mounted disk */
+ lustre_put_lsi(sb);
+ lu_types_stop();
+ return rc;
+}
+EXPORT_SYMBOL(lustre_common_put_super);
+
+static void lmd_print(struct lustre_mount_data *lmd)
+{
+ int i;
+
+ PRINT_CMD(D_MOUNT, " mount data:\n");
+ if (lmd_is_client(lmd))
+ PRINT_CMD(D_MOUNT, "profile: %s\n", lmd->lmd_profile);
+ PRINT_CMD(D_MOUNT, "device: %s\n", lmd->lmd_dev);
+ PRINT_CMD(D_MOUNT, "flags: %x\n", lmd->lmd_flags);
+
+ if (lmd->lmd_opts)
+ PRINT_CMD(D_MOUNT, "options: %s\n", lmd->lmd_opts);
+
+ if (lmd->lmd_recovery_time_soft)
+ PRINT_CMD(D_MOUNT, "recovery time soft: %d\n",
+ lmd->lmd_recovery_time_soft);
+
+ if (lmd->lmd_recovery_time_hard)
+ PRINT_CMD(D_MOUNT, "recovery time hard: %d\n",
+ lmd->lmd_recovery_time_hard);
+
+ for (i = 0; i < lmd->lmd_exclude_count; i++) {
+ PRINT_CMD(D_MOUNT, "exclude %d: OST%04x\n", i,
+ lmd->lmd_exclude[i]);
+ }
+}
+
+/* Is this server on the exclusion list */
+int lustre_check_exclusion(struct super_block *sb, char *svname)
+{
+ struct lustre_sb_info *lsi = s2lsi(sb);
+ struct lustre_mount_data *lmd = lsi->lsi_lmd;
+ __u32 index;
+ int i, rc;
+
+ rc = server_name2index(svname, &index, NULL);
+ if (rc != LDD_F_SV_TYPE_OST)
+ /* Only exclude OSTs */
+ return 0;
+
+ CDEBUG(D_MOUNT, "Check exclusion %s (%d) in %d of %s\n", svname,
+ index, lmd->lmd_exclude_count, lmd->lmd_dev);
+
+ for (i = 0; i < lmd->lmd_exclude_count; i++) {
+ if (index == lmd->lmd_exclude[i]) {
+ CWARN("Excluding %s (on exclusion list)\n", svname);
+ return 1;
+ }
+ }
+ return 0;
+}
+
+/* mount -v -o exclude=lustre-OST0001:lustre-OST0002 -t lustre ... */
+static int lmd_make_exclusion(struct lustre_mount_data *lmd, const char *ptr)
+{
+ const char *s1 = ptr, *s2;
+ __u32 index, *exclude_list;
+ int rc = 0, devmax;
+
+ /* The shortest an ost name can be is 8 chars: -OST0000.
+ We don't actually know the fsname at this time, so in fact
+ a user could specify any fsname. */
+ devmax = strlen(ptr) / 8 + 1;
+
+ /* temp storage until we figure out how many we have */
+ OBD_ALLOC(exclude_list, sizeof(index) * devmax);
+ if (!exclude_list)
+ return -ENOMEM;
+
+ /* we enter this fn pointing at the '=' */
+ while (*s1 && *s1 != ' ' && *s1 != ',') {
+ s1++;
+ rc = server_name2index(s1, &index, &s2);
+ if (rc < 0) {
+ CERROR("Can't parse server name '%s': rc = %d\n",
+ s1, rc);
+ break;
+ }
+ if (rc == LDD_F_SV_TYPE_OST)
+ exclude_list[lmd->lmd_exclude_count++] = index;
+ else
+ CDEBUG(D_MOUNT, "ignoring exclude %.*s: type = %#x\n",
+ (uint)(s2-s1), s1, rc);
+ s1 = s2;
+ /* now we are pointing at ':' (next exclude)
+ or ',' (end of excludes) */
+ if (lmd->lmd_exclude_count >= devmax)
+ break;
+ }
+ if (rc >= 0) /* non-err */
+ rc = 0;
+
+ if (lmd->lmd_exclude_count) {
+ /* permanent, freed in lustre_free_lsi */
+ OBD_ALLOC(lmd->lmd_exclude, sizeof(index) *
+ lmd->lmd_exclude_count);
+ if (lmd->lmd_exclude) {
+ memcpy(lmd->lmd_exclude, exclude_list,
+ sizeof(index) * lmd->lmd_exclude_count);
+ } else {
+ rc = -ENOMEM;
+ lmd->lmd_exclude_count = 0;
+ }
+ }
+ OBD_FREE(exclude_list, sizeof(index) * devmax);
+ return rc;
+}
+
+static int lmd_parse_mgssec(struct lustre_mount_data *lmd, char *ptr)
+{
+ char *tail;
+ int length;
+
+ if (lmd->lmd_mgssec != NULL) {
+ OBD_FREE(lmd->lmd_mgssec, strlen(lmd->lmd_mgssec) + 1);
+ lmd->lmd_mgssec = NULL;
+ }
+
+ tail = strchr(ptr, ',');
+ if (tail == NULL)
+ length = strlen(ptr);
+ else
+ length = tail - ptr;
+
+ OBD_ALLOC(lmd->lmd_mgssec, length + 1);
+ if (lmd->lmd_mgssec == NULL)
+ return -ENOMEM;
+
+ memcpy(lmd->lmd_mgssec, ptr, length);
+ lmd->lmd_mgssec[length] = '\0';
+ return 0;
+}
+
+static int lmd_parse_string(char **handle, char *ptr)
+{
+ char *tail;
+ int length;
+
+ if ((handle == NULL) || (ptr == NULL))
+ return -EINVAL;
+
+ if (*handle != NULL) {
+ OBD_FREE(*handle, strlen(*handle) + 1);
+ *handle = NULL;
+ }
+
+ tail = strchr(ptr, ',');
+ if (tail == NULL)
+ length = strlen(ptr);
+ else
+ length = tail - ptr;
+
+ OBD_ALLOC(*handle, length + 1);
+ if (*handle == NULL)
+ return -ENOMEM;
+
+ memcpy(*handle, ptr, length);
+ (*handle)[length] = '\0';
+
+ return 0;
+}
+
+/* Collect multiple values for mgsnid specifiers */
+static int lmd_parse_mgs(struct lustre_mount_data *lmd, char **ptr)
+{
+ lnet_nid_t nid;
+ char *tail = *ptr;
+ char *mgsnid;
+ int length;
+ int oldlen = 0;
+
+ /* Find end of nidlist */
+ while (class_parse_nid_quiet(tail, &nid, &tail) == 0) {}
+ length = tail - *ptr;
+ if (length == 0) {
+ LCONSOLE_ERROR_MSG(0x159, "Can't parse NID '%s'\n", *ptr);
+ return -EINVAL;
+ }
+
+ if (lmd->lmd_mgs != NULL)
+ oldlen = strlen(lmd->lmd_mgs) + 1;
+
+ OBD_ALLOC(mgsnid, oldlen + length + 1);
+ if (mgsnid == NULL)
+ return -ENOMEM;
+
+ if (lmd->lmd_mgs != NULL) {
+ /* Multiple mgsnid= are taken to mean failover locations */
+ memcpy(mgsnid, lmd->lmd_mgs, oldlen);
+ mgsnid[oldlen - 1] = ':';
+ OBD_FREE(lmd->lmd_mgs, oldlen);
+ }
+ memcpy(mgsnid + oldlen, *ptr, length);
+ mgsnid[oldlen + length] = '\0';
+ lmd->lmd_mgs = mgsnid;
+ *ptr = tail;
+
+ return 0;
+}
+
+/** Parse mount line options
+ * e.g. mount -v -t lustre -o abort_recov uml1:uml2:/lustre-client /mnt/lustre
+ * dev is passed as device=uml1:/lustre by mount.lustre
+ */
+static int lmd_parse(char *options, struct lustre_mount_data *lmd)
+{
+ char *s1, *s2, *devname = NULL;
+ struct lustre_mount_data *raw = (struct lustre_mount_data *)options;
+ int rc = 0;
+
+ LASSERT(lmd);
+ if (!options) {
+ LCONSOLE_ERROR_MSG(0x162, "Missing mount data: check that /sbin/mount.lustre is installed.\n");
+ return -EINVAL;
+ }
+
+ /* Options should be a string - try to detect old lmd data */
+ if ((raw->lmd_magic & 0xffffff00) == (LMD_MAGIC & 0xffffff00)) {
+ LCONSOLE_ERROR_MSG(0x163, "You're using an old version of /sbin/mount.lustre. Please install version %s\n",
+ LUSTRE_VERSION_STRING);
+ return -EINVAL;
+ }
+ lmd->lmd_magic = LMD_MAGIC;
+
+ OBD_ALLOC(lmd->lmd_params, 4096);
+ if (lmd->lmd_params == NULL)
+ return -ENOMEM;
+ lmd->lmd_params[0] = '\0';
+
+ /* Set default flags here */
+
+ s1 = options;
+ while (*s1) {
+ int clear = 0;
+ int time_min = OBD_RECOVERY_TIME_MIN;
+
+ /* Skip whitespace and extra commas */
+ while (*s1 == ' ' || *s1 == ',')
+ s1++;
+
+ /* Client options are parsed in ll_options: eg. flock,
+ user_xattr, acl */
+
+ /* Parse non-ldiskfs options here. Rather than modifying
+ ldiskfs, we just zero these out here */
+ if (strncmp(s1, "abort_recov", 11) == 0) {
+ lmd->lmd_flags |= LMD_FLG_ABORT_RECOV;
+ clear++;
+ } else if (strncmp(s1, "recovery_time_soft=", 19) == 0) {
+ lmd->lmd_recovery_time_soft = max_t(int,
+ simple_strtoul(s1 + 19, NULL, 10), time_min);
+ clear++;
+ } else if (strncmp(s1, "recovery_time_hard=", 19) == 0) {
+ lmd->lmd_recovery_time_hard = max_t(int,
+ simple_strtoul(s1 + 19, NULL, 10), time_min);
+ clear++;
+ } else if (strncmp(s1, "noir", 4) == 0) {
+ lmd->lmd_flags |= LMD_FLG_NOIR; /* test purpose only. */
+ clear++;
+ } else if (strncmp(s1, "nosvc", 5) == 0) {
+ lmd->lmd_flags |= LMD_FLG_NOSVC;
+ clear++;
+ } else if (strncmp(s1, "nomgs", 5) == 0) {
+ lmd->lmd_flags |= LMD_FLG_NOMGS;
+ clear++;
+ } else if (strncmp(s1, "noscrub", 7) == 0) {
+ lmd->lmd_flags |= LMD_FLG_NOSCRUB;
+ clear++;
+ } else if (strncmp(s1, PARAM_MGSNODE,
+ sizeof(PARAM_MGSNODE) - 1) == 0) {
+ s2 = s1 + sizeof(PARAM_MGSNODE) - 1;
+ /* Assume the next mount opt is the first
+ invalid nid we get to. */
+ rc = lmd_parse_mgs(lmd, &s2);
+ if (rc)
+ goto invalid;
+ clear++;
+ } else if (strncmp(s1, "writeconf", 9) == 0) {
+ lmd->lmd_flags |= LMD_FLG_WRITECONF;
+ clear++;
+ } else if (strncmp(s1, "update", 6) == 0) {
+ lmd->lmd_flags |= LMD_FLG_UPDATE;
+ clear++;
+ } else if (strncmp(s1, "virgin", 6) == 0) {
+ lmd->lmd_flags |= LMD_FLG_VIRGIN;
+ clear++;
+ } else if (strncmp(s1, "noprimnode", 10) == 0) {
+ lmd->lmd_flags |= LMD_FLG_NO_PRIMNODE;
+ clear++;
+ } else if (strncmp(s1, "mgssec=", 7) == 0) {
+ rc = lmd_parse_mgssec(lmd, s1 + 7);
+ if (rc)
+ goto invalid;
+ clear++;
+ /* ost exclusion list */
+ } else if (strncmp(s1, "exclude=", 8) == 0) {
+ rc = lmd_make_exclusion(lmd, s1 + 7);
+ if (rc)
+ goto invalid;
+ clear++;
+ } else if (strncmp(s1, "mgs", 3) == 0) {
+ /* We are an MGS */
+ lmd->lmd_flags |= LMD_FLG_MGS;
+ clear++;
+ } else if (strncmp(s1, "svname=", 7) == 0) {
+ rc = lmd_parse_string(&lmd->lmd_profile, s1 + 7);
+ if (rc)
+ goto invalid;
+ clear++;
+ } else if (strncmp(s1, "param=", 6) == 0) {
+ int length;
+ char *tail = strchr(s1 + 6, ',');
+ if (tail == NULL)
+ length = strlen(s1);
+ else
+ length = tail - s1;
+ length -= 6;
+ strncat(lmd->lmd_params, s1 + 6, length);
+ strcat(lmd->lmd_params, " ");
+ clear++;
+ } else if (strncmp(s1, "osd=", 4) == 0) {
+ rc = lmd_parse_string(&lmd->lmd_osd_type, s1 + 4);
+ if (rc)
+ goto invalid;
+ clear++;
+ }
+ /* Linux 2.4 doesn't pass the device, so we stuck it at the
+ end of the options. */
+ else if (strncmp(s1, "device=", 7) == 0) {
+ devname = s1 + 7;
+ /* terminate options right before device. device
+ must be the last one. */
+ *s1 = '\0';
+ break;
+ }
+
+ /* Find next opt */
+ s2 = strchr(s1, ',');
+ if (s2 == NULL) {
+ if (clear)
+ *s1 = '\0';
+ break;
+ }
+ s2++;
+ if (clear)
+ memmove(s1, s2, strlen(s2) + 1);
+ else
+ s1 = s2;
+ }
+
+ if (!devname) {
+ LCONSOLE_ERROR_MSG(0x164, "Can't find the device name (need mount option 'device=...')\n");
+ goto invalid;
+ }
+
+ s1 = strstr(devname, ":/");
+ if (s1) {
+ ++s1;
+ lmd->lmd_flags |= LMD_FLG_CLIENT;
+ /* Remove leading /s from fsname */
+ while (*++s1 == '/') ;
+ /* Freed in lustre_free_lsi */
+ OBD_ALLOC(lmd->lmd_profile, strlen(s1) + 8);
+ if (!lmd->lmd_profile)
+ return -ENOMEM;
+ sprintf(lmd->lmd_profile, "%s-client", s1);
+ }
+
+ /* Freed in lustre_free_lsi */
+ OBD_ALLOC(lmd->lmd_dev, strlen(devname) + 1);
+ if (!lmd->lmd_dev)
+ return -ENOMEM;
+ strcpy(lmd->lmd_dev, devname);
+
+ /* Save mount options */
+ s1 = options + strlen(options) - 1;
+ while (s1 >= options && (*s1 == ',' || *s1 == ' '))
+ *s1-- = 0;
+ if (*options != 0) {
+ /* Freed in lustre_free_lsi */
+ OBD_ALLOC(lmd->lmd_opts, strlen(options) + 1);
+ if (!lmd->lmd_opts)
+ return -ENOMEM;
+ strcpy(lmd->lmd_opts, options);
+ }
+
+ lmd_print(lmd);
+ lmd->lmd_magic = LMD_MAGIC;
+
+ return rc;
+
+invalid:
+ CERROR("Bad mount options %s\n", options);
+ return -EINVAL;
+}
+
+struct lustre_mount_data2 {
+ void *lmd2_data;
+ struct vfsmount *lmd2_mnt;
+};
+
+/** This is the entry point for the mount call into Lustre.
+ * This is called when a server or client is mounted,
+ * and this is where we start setting things up.
+ * @param data Mount options (e.g. -o flock,abort_recov)
+ */
+int lustre_fill_super(struct super_block *sb, void *data, int silent)
+{
+ struct lustre_mount_data *lmd;
+ struct lustre_mount_data2 *lmd2 = data;
+ struct lustre_sb_info *lsi;
+ int rc;
+
+ CDEBUG(D_MOUNT|D_VFSTRACE, "VFS Op: sb %p\n", sb);
+
+ lsi = lustre_init_lsi(sb);
+ if (!lsi)
+ return -ENOMEM;
+ lmd = lsi->lsi_lmd;
+
+ /*
+ * Disable lockdep during mount, because mount locking patterns are
+ * `special'.
+ */
+ lockdep_off();
+
+ /*
+ * LU-639: the obd cleanup of last mount may not finish yet, wait here.
+ */
+ obd_zombie_barrier();
+
+ /* Figure out the lmd from the mount options */
+ if (lmd_parse((char *)(lmd2->lmd2_data), lmd)) {
+ lustre_put_lsi(sb);
+ rc = -EINVAL;
+ goto out;
+ }
+
+ if (lmd_is_client(lmd)) {
+ CDEBUG(D_MOUNT, "Mounting client %s\n", lmd->lmd_profile);
+ if (client_fill_super == NULL)
+ request_module("lustre");
+ if (client_fill_super == NULL) {
+ LCONSOLE_ERROR_MSG(0x165, "Nothing registered for client mount! Is the 'lustre' module loaded?\n");
+ lustre_put_lsi(sb);
+ rc = -ENODEV;
+ } else {
+ rc = lustre_start_mgc(sb);
+ if (rc) {
+ lustre_put_lsi(sb);
+ goto out;
+ }
+ /* Connect and start */
+ /* (should always be ll_fill_super) */
+ rc = (*client_fill_super)(sb, lmd2->lmd2_mnt);
+ /* c_f_s will call lustre_common_put_super on failure */
+ }
+ } else {
+ CERROR("This is client-side-only module, cannot handle server mount.\n");
+ rc = -EINVAL;
+ }
+
+ /* If error happens in fill_super() call, @lsi will be killed there.
+ * This is why we do not put it here. */
+ goto out;
+out:
+ if (rc) {
+ CERROR("Unable to mount %s (%d)\n",
+ s2lsi(sb) ? lmd->lmd_dev : "", rc);
+ } else {
+ CDEBUG(D_SUPER, "Mount %s complete\n",
+ lmd->lmd_dev);
+ }
+ lockdep_on();
+ return rc;
+}
+
+
+/* We can't call ll_fill_super by name because it lives in a module that
+ must be loaded after this one. */
+void lustre_register_client_fill_super(int (*cfs)(struct super_block *sb,
+ struct vfsmount *mnt))
+{
+ client_fill_super = cfs;
+}
+EXPORT_SYMBOL(lustre_register_client_fill_super);
+
+void lustre_register_kill_super_cb(void (*cfs)(struct super_block *sb))
+{
+ kill_super_cb = cfs;
+}
+EXPORT_SYMBOL(lustre_register_kill_super_cb);
+
+/***************** FS registration ******************/
+struct dentry *lustre_mount(struct file_system_type *fs_type, int flags,
+ const char *devname, void *data)
+{
+ struct lustre_mount_data2 lmd2 = {
+ .lmd2_data = data,
+ .lmd2_mnt = NULL
+ };
+
+ return mount_nodev(fs_type, flags, &lmd2, lustre_fill_super);
+}
+
+static void lustre_kill_super(struct super_block *sb)
+{
+ struct lustre_sb_info *lsi = s2lsi(sb);
+
+ if (kill_super_cb && lsi && !IS_SERVER(lsi))
+ (*kill_super_cb)(sb);
+
+ kill_anon_super(sb);
+}
+
+/** Register the "lustre" fs type
+ */
+struct file_system_type lustre_fs_type = {
+ .owner = THIS_MODULE,
+ .name = "lustre",
+ .mount = lustre_mount,
+ .kill_sb = lustre_kill_super,
+ .fs_flags = FS_BINARY_MOUNTDATA | FS_REQUIRES_DEV |
+ FS_HAS_FIEMAP | FS_RENAME_DOES_D_MOVE,
+};
+MODULE_ALIAS_FS("lustre");
+
+int lustre_register_fs(void)
+{
+ return register_filesystem(&lustre_fs_type);
+}
+
+int lustre_unregister_fs(void)
+{
+ return unregister_filesystem(&lustre_fs_type);
+}
diff --git a/drivers/staging/lustre/lustre/obdclass/obdo.c b/drivers/staging/lustre/lustre/obdclass/obdo.c
new file mode 100644
index 000000000..307ffe347
--- /dev/null
+++ b/drivers/staging/lustre/lustre/obdclass/obdo.c
@@ -0,0 +1,362 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2011, 2012, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ *
+ * lustre/obdclass/obdo.c
+ *
+ * Object Devices Class Driver
+ * These are the only exported functions, they provide some generic
+ * infrastructure for managing object devices
+ */
+
+#define DEBUG_SUBSYSTEM S_CLASS
+
+#include "../include/obd_class.h"
+#include "../include/lustre/lustre_idl.h"
+
+void obdo_set_parent_fid(struct obdo *dst, const struct lu_fid *parent)
+{
+ dst->o_parent_oid = fid_oid(parent);
+ dst->o_parent_seq = fid_seq(parent);
+ dst->o_parent_ver = fid_ver(parent);
+ dst->o_valid |= OBD_MD_FLGENER | OBD_MD_FLFID;
+}
+EXPORT_SYMBOL(obdo_set_parent_fid);
+
+/* WARNING: the file systems must take care not to tinker with
+ attributes they don't manage (such as blocks). */
+void obdo_from_inode(struct obdo *dst, struct inode *src, u32 valid)
+{
+ u32 newvalid = 0;
+
+ if (valid & (OBD_MD_FLCTIME | OBD_MD_FLMTIME))
+ CDEBUG(D_INODE, "valid %x, new time %lu/%lu\n",
+ valid, LTIME_S(src->i_mtime),
+ LTIME_S(src->i_ctime));
+
+ if (valid & OBD_MD_FLATIME) {
+ dst->o_atime = LTIME_S(src->i_atime);
+ newvalid |= OBD_MD_FLATIME;
+ }
+ if (valid & OBD_MD_FLMTIME) {
+ dst->o_mtime = LTIME_S(src->i_mtime);
+ newvalid |= OBD_MD_FLMTIME;
+ }
+ if (valid & OBD_MD_FLCTIME) {
+ dst->o_ctime = LTIME_S(src->i_ctime);
+ newvalid |= OBD_MD_FLCTIME;
+ }
+ if (valid & OBD_MD_FLSIZE) {
+ dst->o_size = i_size_read(src);
+ newvalid |= OBD_MD_FLSIZE;
+ }
+ if (valid & OBD_MD_FLBLOCKS) { /* allocation of space (x512 bytes) */
+ dst->o_blocks = src->i_blocks;
+ newvalid |= OBD_MD_FLBLOCKS;
+ }
+ if (valid & OBD_MD_FLBLKSZ) { /* optimal block size */
+ dst->o_blksize = 1 << src->i_blkbits;
+ newvalid |= OBD_MD_FLBLKSZ;
+ }
+ if (valid & OBD_MD_FLTYPE) {
+ dst->o_mode = (dst->o_mode & S_IALLUGO) |
+ (src->i_mode & S_IFMT);
+ newvalid |= OBD_MD_FLTYPE;
+ }
+ if (valid & OBD_MD_FLMODE) {
+ dst->o_mode = (dst->o_mode & S_IFMT) |
+ (src->i_mode & S_IALLUGO);
+ newvalid |= OBD_MD_FLMODE;
+ }
+ if (valid & OBD_MD_FLUID) {
+ dst->o_uid = from_kuid(&init_user_ns, src->i_uid);
+ newvalid |= OBD_MD_FLUID;
+ }
+ if (valid & OBD_MD_FLGID) {
+ dst->o_gid = from_kgid(&init_user_ns, src->i_gid);
+ newvalid |= OBD_MD_FLGID;
+ }
+ if (valid & OBD_MD_FLFLAGS) {
+ dst->o_flags = src->i_flags;
+ newvalid |= OBD_MD_FLFLAGS;
+ }
+ dst->o_valid |= newvalid;
+}
+EXPORT_SYMBOL(obdo_from_inode);
+
+void obdo_cpy_md(struct obdo *dst, struct obdo *src, u32 valid)
+{
+ CDEBUG(D_INODE, "src obdo "DOSTID" valid %#llx, dst obdo "DOSTID"\n",
+ POSTID(&src->o_oi), src->o_valid, POSTID(&dst->o_oi));
+ if (valid & OBD_MD_FLATIME)
+ dst->o_atime = src->o_atime;
+ if (valid & OBD_MD_FLMTIME)
+ dst->o_mtime = src->o_mtime;
+ if (valid & OBD_MD_FLCTIME)
+ dst->o_ctime = src->o_ctime;
+ if (valid & OBD_MD_FLSIZE)
+ dst->o_size = src->o_size;
+ if (valid & OBD_MD_FLBLOCKS) /* allocation of space */
+ dst->o_blocks = src->o_blocks;
+ if (valid & OBD_MD_FLBLKSZ)
+ dst->o_blksize = src->o_blksize;
+ if (valid & OBD_MD_FLTYPE)
+ dst->o_mode = (dst->o_mode & ~S_IFMT) | (src->o_mode & S_IFMT);
+ if (valid & OBD_MD_FLMODE)
+ dst->o_mode = (dst->o_mode & S_IFMT) | (src->o_mode & ~S_IFMT);
+ if (valid & OBD_MD_FLUID)
+ dst->o_uid = src->o_uid;
+ if (valid & OBD_MD_FLGID)
+ dst->o_gid = src->o_gid;
+ if (valid & OBD_MD_FLFLAGS)
+ dst->o_flags = src->o_flags;
+ if (valid & OBD_MD_FLFID) {
+ dst->o_parent_seq = src->o_parent_seq;
+ dst->o_parent_ver = src->o_parent_ver;
+ }
+ if (valid & OBD_MD_FLGENER)
+ dst->o_parent_oid = src->o_parent_oid;
+ if (valid & OBD_MD_FLHANDLE)
+ dst->o_handle = src->o_handle;
+ if (valid & OBD_MD_FLCOOKIE)
+ dst->o_lcookie = src->o_lcookie;
+
+ dst->o_valid |= valid;
+}
+EXPORT_SYMBOL(obdo_cpy_md);
+
+/* returns FALSE if comparison (by flags) is same, TRUE if changed */
+int obdo_cmp_md(struct obdo *dst, struct obdo *src, u32 compare)
+{
+ int res = 0;
+
+ if (compare & OBD_MD_FLATIME)
+ res |= dst->o_atime != src->o_atime;
+ if (compare & OBD_MD_FLMTIME)
+ res |= dst->o_mtime != src->o_mtime;
+ if (compare & OBD_MD_FLCTIME)
+ res |= dst->o_ctime != src->o_ctime;
+ if (compare & OBD_MD_FLSIZE)
+ res |= dst->o_size != src->o_size;
+ if (compare & OBD_MD_FLBLOCKS) /* allocation of space */
+ res |= dst->o_blocks != src->o_blocks;
+ if (compare & OBD_MD_FLBLKSZ)
+ res |= dst->o_blksize != src->o_blksize;
+ if (compare & OBD_MD_FLTYPE)
+ res |= ((dst->o_mode ^ src->o_mode) & S_IFMT) != 0;
+ if (compare & OBD_MD_FLMODE)
+ res |= ((dst->o_mode ^ src->o_mode) & ~S_IFMT) != 0;
+ if (compare & OBD_MD_FLUID)
+ res |= dst->o_uid != src->o_uid;
+ if (compare & OBD_MD_FLGID)
+ res |= dst->o_gid != src->o_gid;
+ if (compare & OBD_MD_FLFLAGS)
+ res |= dst->o_flags != src->o_flags;
+ if (compare & OBD_MD_FLNLINK)
+ res |= dst->o_nlink != src->o_nlink;
+ if (compare & OBD_MD_FLFID) {
+ res |= dst->o_parent_seq != src->o_parent_seq;
+ res |= dst->o_parent_ver != src->o_parent_ver;
+ }
+ if (compare & OBD_MD_FLGENER)
+ res |= dst->o_parent_oid != src->o_parent_oid;
+ /* XXX Don't know if these should be included here - wasn't previously
+ if ( compare & OBD_MD_FLINLINE )
+ res |= memcmp(dst->o_inline, src->o_inline);
+ */
+ return res;
+}
+EXPORT_SYMBOL(obdo_cmp_md);
+
+void obdo_to_ioobj(struct obdo *oa, struct obd_ioobj *ioobj)
+{
+ ioobj->ioo_oid = oa->o_oi;
+ if (unlikely(!(oa->o_valid & OBD_MD_FLGROUP)))
+ ostid_set_seq_mdt0(&ioobj->ioo_oid);
+
+ /* Since 2.4 this does not contain o_mode in the low 16 bits.
+ * Instead, it holds (bd_md_max_brw - 1) for multi-bulk BRW RPCs */
+ ioobj->ioo_max_brw = 0;
+}
+EXPORT_SYMBOL(obdo_to_ioobj);
+
+void obdo_from_iattr(struct obdo *oa, struct iattr *attr, unsigned int ia_valid)
+{
+ if (ia_valid & ATTR_ATIME) {
+ oa->o_atime = LTIME_S(attr->ia_atime);
+ oa->o_valid |= OBD_MD_FLATIME;
+ }
+ if (ia_valid & ATTR_MTIME) {
+ oa->o_mtime = LTIME_S(attr->ia_mtime);
+ oa->o_valid |= OBD_MD_FLMTIME;
+ }
+ if (ia_valid & ATTR_CTIME) {
+ oa->o_ctime = LTIME_S(attr->ia_ctime);
+ oa->o_valid |= OBD_MD_FLCTIME;
+ }
+ if (ia_valid & ATTR_SIZE) {
+ oa->o_size = attr->ia_size;
+ oa->o_valid |= OBD_MD_FLSIZE;
+ }
+ if (ia_valid & ATTR_MODE) {
+ oa->o_mode = attr->ia_mode;
+ oa->o_valid |= OBD_MD_FLTYPE | OBD_MD_FLMODE;
+ if (!in_group_p(make_kgid(&init_user_ns, oa->o_gid)) &&
+ !capable(CFS_CAP_FSETID))
+ oa->o_mode &= ~S_ISGID;
+ }
+ if (ia_valid & ATTR_UID) {
+ oa->o_uid = from_kuid(&init_user_ns, attr->ia_uid);
+ oa->o_valid |= OBD_MD_FLUID;
+ }
+ if (ia_valid & ATTR_GID) {
+ oa->o_gid = from_kgid(&init_user_ns, attr->ia_gid);
+ oa->o_valid |= OBD_MD_FLGID;
+ }
+}
+EXPORT_SYMBOL(obdo_from_iattr);
+
+void iattr_from_obdo(struct iattr *attr, struct obdo *oa, u32 valid)
+{
+ valid &= oa->o_valid;
+
+ if (valid & (OBD_MD_FLCTIME | OBD_MD_FLMTIME))
+ CDEBUG(D_INODE, "valid %#llx, new time %llu/%llu\n",
+ oa->o_valid, oa->o_mtime, oa->o_ctime);
+
+ attr->ia_valid = 0;
+ if (valid & OBD_MD_FLATIME) {
+ LTIME_S(attr->ia_atime) = oa->o_atime;
+ attr->ia_valid |= ATTR_ATIME;
+ }
+ if (valid & OBD_MD_FLMTIME) {
+ LTIME_S(attr->ia_mtime) = oa->o_mtime;
+ attr->ia_valid |= ATTR_MTIME;
+ }
+ if (valid & OBD_MD_FLCTIME) {
+ LTIME_S(attr->ia_ctime) = oa->o_ctime;
+ attr->ia_valid |= ATTR_CTIME;
+ }
+ if (valid & OBD_MD_FLSIZE) {
+ attr->ia_size = oa->o_size;
+ attr->ia_valid |= ATTR_SIZE;
+ }
+#if 0 /* you shouldn't be able to change a file's type with setattr */
+ if (valid & OBD_MD_FLTYPE) {
+ attr->ia_mode = (attr->ia_mode & ~S_IFMT)|(oa->o_mode & S_IFMT);
+ attr->ia_valid |= ATTR_MODE;
+ }
+#endif
+ if (valid & OBD_MD_FLMODE) {
+ attr->ia_mode = (attr->ia_mode & S_IFMT)|(oa->o_mode & ~S_IFMT);
+ attr->ia_valid |= ATTR_MODE;
+ if (!in_group_p(make_kgid(&init_user_ns, oa->o_gid)) &&
+ !capable(CFS_CAP_FSETID))
+ attr->ia_mode &= ~S_ISGID;
+ }
+ if (valid & OBD_MD_FLUID) {
+ attr->ia_uid = make_kuid(&init_user_ns, oa->o_uid);
+ attr->ia_valid |= ATTR_UID;
+ }
+ if (valid & OBD_MD_FLGID) {
+ attr->ia_gid = make_kgid(&init_user_ns, oa->o_gid);
+ attr->ia_valid |= ATTR_GID;
+ }
+}
+EXPORT_SYMBOL(iattr_from_obdo);
+
+void md_from_obdo(struct md_op_data *op_data, struct obdo *oa, u32 valid)
+{
+ iattr_from_obdo(&op_data->op_attr, oa, valid);
+ if (valid & OBD_MD_FLBLOCKS) {
+ op_data->op_attr_blocks = oa->o_blocks;
+ op_data->op_attr.ia_valid |= ATTR_BLOCKS;
+ }
+ if (valid & OBD_MD_FLFLAGS) {
+ ((struct ll_iattr *)&op_data->op_attr)->ia_attr_flags =
+ oa->o_flags;
+ op_data->op_attr.ia_valid |= ATTR_ATTR_FLAG;
+ }
+}
+EXPORT_SYMBOL(md_from_obdo);
+
+void obdo_from_md(struct obdo *oa, struct md_op_data *op_data,
+ unsigned int valid)
+{
+ obdo_from_iattr(oa, &op_data->op_attr, valid);
+ if (valid & ATTR_BLOCKS) {
+ oa->o_blocks = op_data->op_attr_blocks;
+ oa->o_valid |= OBD_MD_FLBLOCKS;
+ }
+ if (valid & ATTR_ATTR_FLAG) {
+ oa->o_flags =
+ ((struct ll_iattr *)&op_data->op_attr)->ia_attr_flags;
+ oa->o_valid |= OBD_MD_FLFLAGS;
+ }
+}
+EXPORT_SYMBOL(obdo_from_md);
+
+void obdo_cpu_to_le(struct obdo *dobdo, struct obdo *sobdo)
+{
+ dobdo->o_size = cpu_to_le64(sobdo->o_size);
+ dobdo->o_mtime = cpu_to_le64(sobdo->o_mtime);
+ dobdo->o_atime = cpu_to_le64(sobdo->o_atime);
+ dobdo->o_ctime = cpu_to_le64(sobdo->o_ctime);
+ dobdo->o_blocks = cpu_to_le64(sobdo->o_blocks);
+ dobdo->o_mode = cpu_to_le32(sobdo->o_mode);
+ dobdo->o_uid = cpu_to_le32(sobdo->o_uid);
+ dobdo->o_gid = cpu_to_le32(sobdo->o_gid);
+ dobdo->o_flags = cpu_to_le32(sobdo->o_flags);
+ dobdo->o_nlink = cpu_to_le32(sobdo->o_nlink);
+ dobdo->o_blksize = cpu_to_le32(sobdo->o_blksize);
+ dobdo->o_valid = cpu_to_le64(sobdo->o_valid);
+}
+EXPORT_SYMBOL(obdo_cpu_to_le);
+
+void obdo_le_to_cpu(struct obdo *dobdo, struct obdo *sobdo)
+{
+ dobdo->o_size = le64_to_cpu(sobdo->o_size);
+ dobdo->o_mtime = le64_to_cpu(sobdo->o_mtime);
+ dobdo->o_atime = le64_to_cpu(sobdo->o_atime);
+ dobdo->o_ctime = le64_to_cpu(sobdo->o_ctime);
+ dobdo->o_blocks = le64_to_cpu(sobdo->o_blocks);
+ dobdo->o_mode = le32_to_cpu(sobdo->o_mode);
+ dobdo->o_uid = le32_to_cpu(sobdo->o_uid);
+ dobdo->o_gid = le32_to_cpu(sobdo->o_gid);
+ dobdo->o_flags = le32_to_cpu(sobdo->o_flags);
+ dobdo->o_nlink = le32_to_cpu(sobdo->o_nlink);
+ dobdo->o_blksize = le32_to_cpu(sobdo->o_blksize);
+ dobdo->o_valid = le64_to_cpu(sobdo->o_valid);
+}
+EXPORT_SYMBOL(obdo_le_to_cpu);
diff --git a/drivers/staging/lustre/lustre/obdclass/statfs_pack.c b/drivers/staging/lustre/lustre/obdclass/statfs_pack.c
new file mode 100644
index 000000000..cc785ab3f
--- /dev/null
+++ b/drivers/staging/lustre/lustre/obdclass/statfs_pack.c
@@ -0,0 +1,75 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2002, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ *
+ * lustre/obdclass/statfs_pack.c
+ *
+ * (Un)packing of OST/MDS requests
+ *
+ * Author: Andreas Dilger <adilger@clusterfs.com>
+ */
+
+#define DEBUG_SUBSYSTEM S_CLASS
+
+#include <linux/statfs.h>
+#include "../include/lustre_export.h"
+#include "../include/lustre_net.h"
+#include "../include/obd_support.h"
+#include "../include/obd_class.h"
+
+void statfs_pack(struct obd_statfs *osfs, struct kstatfs *sfs)
+{
+ memset(osfs, 0, sizeof(*osfs));
+ osfs->os_type = sfs->f_type;
+ osfs->os_blocks = sfs->f_blocks;
+ osfs->os_bfree = sfs->f_bfree;
+ osfs->os_bavail = sfs->f_bavail;
+ osfs->os_files = sfs->f_files;
+ osfs->os_ffree = sfs->f_ffree;
+ osfs->os_bsize = sfs->f_bsize;
+ osfs->os_namelen = sfs->f_namelen;
+}
+EXPORT_SYMBOL(statfs_pack);
+
+void statfs_unpack(struct kstatfs *sfs, struct obd_statfs *osfs)
+{
+ memset(sfs, 0, sizeof(*sfs));
+ sfs->f_type = osfs->os_type;
+ sfs->f_blocks = osfs->os_blocks;
+ sfs->f_bfree = osfs->os_bfree;
+ sfs->f_bavail = osfs->os_bavail;
+ sfs->f_files = osfs->os_files;
+ sfs->f_ffree = osfs->os_ffree;
+ sfs->f_bsize = osfs->os_bsize;
+ sfs->f_namelen = osfs->os_namelen;
+}
+EXPORT_SYMBOL(statfs_unpack);
diff --git a/drivers/staging/lustre/lustre/obdclass/uuid.c b/drivers/staging/lustre/lustre/obdclass/uuid.c
new file mode 100644
index 000000000..ff0a01bcf
--- /dev/null
+++ b/drivers/staging/lustre/lustre/obdclass/uuid.c
@@ -0,0 +1,82 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2002, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ *
+ * lustre/obdclass/uuid.c
+ *
+ * Public include file for the UUID library
+ */
+
+#define DEBUG_SUBSYSTEM S_CLASS
+
+#include "../../include/linux/libcfs/libcfs.h"
+
+#include "../include/obd_support.h"
+#include "../include/obd_class.h"
+
+
+static inline __u32 consume(int nob, __u8 **ptr)
+{
+ __u32 value;
+
+ LASSERT(nob <= sizeof(value));
+
+ for (value = 0; nob > 0; --nob)
+ value = (value << 8) | *((*ptr)++);
+ return value;
+}
+
+#define CONSUME(val, ptr) (val) = consume(sizeof(val), (ptr))
+
+static void uuid_unpack(class_uuid_t in, __u16 *uu, int nr)
+{
+ __u8 *ptr = in;
+
+ LASSERT(nr * sizeof(*uu) == sizeof(class_uuid_t));
+
+ while (nr-- > 0)
+ CONSUME(uu[nr], &ptr);
+}
+
+void class_uuid_unparse(class_uuid_t uu, struct obd_uuid *out)
+{
+ /* uu as an array of __u16's */
+ __u16 uuid[sizeof(class_uuid_t) / sizeof(__u16)];
+
+ CLASSERT(ARRAY_SIZE(uuid) == 8);
+
+ uuid_unpack(uu, uuid, ARRAY_SIZE(uuid));
+ sprintf(out->uuid, "%04x%04x-%04x-%04x-%04x-%04x%04x%04x",
+ uuid[0], uuid[1], uuid[2], uuid[3],
+ uuid[4], uuid[5], uuid[6], uuid[7]);
+}
+EXPORT_SYMBOL(class_uuid_unparse);
diff --git a/drivers/staging/lustre/lustre/obdecho/Makefile b/drivers/staging/lustre/lustre/obdecho/Makefile
new file mode 100644
index 000000000..672028fc7
--- /dev/null
+++ b/drivers/staging/lustre/lustre/obdecho/Makefile
@@ -0,0 +1,2 @@
+obj-$(CONFIG_LUSTRE_FS) += obdecho.o
+obdecho-y := echo_client.o lproc_echo.o
diff --git a/drivers/staging/lustre/lustre/obdecho/echo_client.c b/drivers/staging/lustre/lustre/obdecho/echo_client.c
new file mode 100644
index 000000000..d542e06d6
--- /dev/null
+++ b/drivers/staging/lustre/lustre/obdecho/echo_client.c
@@ -0,0 +1,2197 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2002, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2011, 2012, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ */
+
+#define DEBUG_SUBSYSTEM S_ECHO
+#include "../../include/linux/libcfs/libcfs.h"
+
+#include "../include/obd.h"
+#include "../include/obd_support.h"
+#include "../include/obd_class.h"
+#include "../include/lustre_debug.h"
+#include "../include/lprocfs_status.h"
+#include "../include/cl_object.h"
+#include "../include/lustre_fid.h"
+#include "../include/lustre_acl.h"
+#include "../include/lustre_net.h"
+
+#include "echo_internal.h"
+
+/** \defgroup echo_client Echo Client
+ * @{
+ */
+
+struct echo_device {
+ struct cl_device ed_cl;
+ struct echo_client_obd *ed_ec;
+
+ struct cl_site ed_site_myself;
+ struct cl_site *ed_site;
+ struct lu_device *ed_next;
+ int ed_next_islov;
+};
+
+struct echo_object {
+ struct cl_object eo_cl;
+ struct cl_object_header eo_hdr;
+
+ struct echo_device *eo_dev;
+ struct list_head eo_obj_chain;
+ struct lov_stripe_md *eo_lsm;
+ atomic_t eo_npages;
+ int eo_deleted;
+};
+
+struct echo_object_conf {
+ struct cl_object_conf eoc_cl;
+ struct lov_stripe_md **eoc_md;
+};
+
+struct echo_page {
+ struct cl_page_slice ep_cl;
+ struct mutex ep_lock;
+ struct page *ep_vmpage;
+};
+
+struct echo_lock {
+ struct cl_lock_slice el_cl;
+ struct list_head el_chain;
+ struct echo_object *el_object;
+ __u64 el_cookie;
+ atomic_t el_refcount;
+};
+
+static int echo_client_setup(const struct lu_env *env,
+ struct obd_device *obddev,
+ struct lustre_cfg *lcfg);
+static int echo_client_cleanup(struct obd_device *obddev);
+
+
+/** \defgroup echo_helpers Helper functions
+ * @{
+ */
+static inline struct echo_device *cl2echo_dev(const struct cl_device *dev)
+{
+ return container_of0(dev, struct echo_device, ed_cl);
+}
+
+static inline struct cl_device *echo_dev2cl(struct echo_device *d)
+{
+ return &d->ed_cl;
+}
+
+static inline struct echo_device *obd2echo_dev(const struct obd_device *obd)
+{
+ return cl2echo_dev(lu2cl_dev(obd->obd_lu_dev));
+}
+
+static inline struct cl_object *echo_obj2cl(struct echo_object *eco)
+{
+ return &eco->eo_cl;
+}
+
+static inline struct echo_object *cl2echo_obj(const struct cl_object *o)
+{
+ return container_of(o, struct echo_object, eo_cl);
+}
+
+static inline struct echo_page *cl2echo_page(const struct cl_page_slice *s)
+{
+ return container_of(s, struct echo_page, ep_cl);
+}
+
+static inline struct echo_lock *cl2echo_lock(const struct cl_lock_slice *s)
+{
+ return container_of(s, struct echo_lock, el_cl);
+}
+
+static inline struct cl_lock *echo_lock2cl(const struct echo_lock *ecl)
+{
+ return ecl->el_cl.cls_lock;
+}
+
+static struct lu_context_key echo_thread_key;
+static inline struct echo_thread_info *echo_env_info(const struct lu_env *env)
+{
+ struct echo_thread_info *info;
+
+ info = lu_context_key_get(&env->le_ctx, &echo_thread_key);
+ LASSERT(info != NULL);
+ return info;
+}
+
+static inline
+struct echo_object_conf *cl2echo_conf(const struct cl_object_conf *c)
+{
+ return container_of(c, struct echo_object_conf, eoc_cl);
+}
+
+/** @} echo_helpers */
+
+static struct echo_object *cl_echo_object_find(struct echo_device *d,
+ struct lov_stripe_md **lsm);
+static int cl_echo_object_put(struct echo_object *eco);
+static int cl_echo_enqueue(struct echo_object *eco, u64 start,
+ u64 end, int mode, __u64 *cookie);
+static int cl_echo_cancel(struct echo_device *d, __u64 cookie);
+static int cl_echo_object_brw(struct echo_object *eco, int rw, u64 offset,
+ struct page **pages, int npages, int async);
+
+static struct echo_thread_info *echo_env_info(const struct lu_env *env);
+
+struct echo_thread_info {
+ struct echo_object_conf eti_conf;
+ struct lustre_md eti_md;
+
+ struct cl_2queue eti_queue;
+ struct cl_io eti_io;
+ struct cl_lock_descr eti_descr;
+ struct lu_fid eti_fid;
+ struct lu_fid eti_fid2;
+};
+
+/* No session used right now */
+struct echo_session_info {
+ unsigned long dummy;
+};
+
+static struct kmem_cache *echo_lock_kmem;
+static struct kmem_cache *echo_object_kmem;
+static struct kmem_cache *echo_thread_kmem;
+static struct kmem_cache *echo_session_kmem;
+
+static struct lu_kmem_descr echo_caches[] = {
+ {
+ .ckd_cache = &echo_lock_kmem,
+ .ckd_name = "echo_lock_kmem",
+ .ckd_size = sizeof(struct echo_lock)
+ },
+ {
+ .ckd_cache = &echo_object_kmem,
+ .ckd_name = "echo_object_kmem",
+ .ckd_size = sizeof(struct echo_object)
+ },
+ {
+ .ckd_cache = &echo_thread_kmem,
+ .ckd_name = "echo_thread_kmem",
+ .ckd_size = sizeof(struct echo_thread_info)
+ },
+ {
+ .ckd_cache = &echo_session_kmem,
+ .ckd_name = "echo_session_kmem",
+ .ckd_size = sizeof(struct echo_session_info)
+ },
+ {
+ .ckd_cache = NULL
+ }
+};
+
+/** \defgroup echo_page Page operations
+ *
+ * Echo page operations.
+ *
+ * @{
+ */
+static struct page *echo_page_vmpage(const struct lu_env *env,
+ const struct cl_page_slice *slice)
+{
+ return cl2echo_page(slice)->ep_vmpage;
+}
+
+static int echo_page_own(const struct lu_env *env,
+ const struct cl_page_slice *slice,
+ struct cl_io *io, int nonblock)
+{
+ struct echo_page *ep = cl2echo_page(slice);
+
+ if (!nonblock)
+ mutex_lock(&ep->ep_lock);
+ else if (!mutex_trylock(&ep->ep_lock))
+ return -EAGAIN;
+ return 0;
+}
+
+static void echo_page_disown(const struct lu_env *env,
+ const struct cl_page_slice *slice,
+ struct cl_io *io)
+{
+ struct echo_page *ep = cl2echo_page(slice);
+
+ LASSERT(mutex_is_locked(&ep->ep_lock));
+ mutex_unlock(&ep->ep_lock);
+}
+
+static void echo_page_discard(const struct lu_env *env,
+ const struct cl_page_slice *slice,
+ struct cl_io *unused)
+{
+ cl_page_delete(env, slice->cpl_page);
+}
+
+static int echo_page_is_vmlocked(const struct lu_env *env,
+ const struct cl_page_slice *slice)
+{
+ if (mutex_is_locked(&cl2echo_page(slice)->ep_lock))
+ return -EBUSY;
+ return -ENODATA;
+}
+
+static void echo_page_completion(const struct lu_env *env,
+ const struct cl_page_slice *slice,
+ int ioret)
+{
+ LASSERT(slice->cpl_page->cp_sync_io != NULL);
+}
+
+static void echo_page_fini(const struct lu_env *env,
+ struct cl_page_slice *slice)
+{
+ struct echo_page *ep = cl2echo_page(slice);
+ struct echo_object *eco = cl2echo_obj(slice->cpl_obj);
+ struct page *vmpage = ep->ep_vmpage;
+
+ atomic_dec(&eco->eo_npages);
+ page_cache_release(vmpage);
+}
+
+static int echo_page_prep(const struct lu_env *env,
+ const struct cl_page_slice *slice,
+ struct cl_io *unused)
+{
+ return 0;
+}
+
+static int echo_page_print(const struct lu_env *env,
+ const struct cl_page_slice *slice,
+ void *cookie, lu_printer_t printer)
+{
+ struct echo_page *ep = cl2echo_page(slice);
+
+ (*printer)(env, cookie, LUSTRE_ECHO_CLIENT_NAME"-page@%p %d vm@%p\n",
+ ep, mutex_is_locked(&ep->ep_lock), ep->ep_vmpage);
+ return 0;
+}
+
+static const struct cl_page_operations echo_page_ops = {
+ .cpo_own = echo_page_own,
+ .cpo_disown = echo_page_disown,
+ .cpo_discard = echo_page_discard,
+ .cpo_vmpage = echo_page_vmpage,
+ .cpo_fini = echo_page_fini,
+ .cpo_print = echo_page_print,
+ .cpo_is_vmlocked = echo_page_is_vmlocked,
+ .io = {
+ [CRT_READ] = {
+ .cpo_prep = echo_page_prep,
+ .cpo_completion = echo_page_completion,
+ },
+ [CRT_WRITE] = {
+ .cpo_prep = echo_page_prep,
+ .cpo_completion = echo_page_completion,
+ }
+ }
+};
+/** @} echo_page */
+
+/** \defgroup echo_lock Locking
+ *
+ * echo lock operations
+ *
+ * @{
+ */
+static void echo_lock_fini(const struct lu_env *env,
+ struct cl_lock_slice *slice)
+{
+ struct echo_lock *ecl = cl2echo_lock(slice);
+
+ LASSERT(list_empty(&ecl->el_chain));
+ OBD_SLAB_FREE_PTR(ecl, echo_lock_kmem);
+}
+
+static void echo_lock_delete(const struct lu_env *env,
+ const struct cl_lock_slice *slice)
+{
+ struct echo_lock *ecl = cl2echo_lock(slice);
+
+ LASSERT(list_empty(&ecl->el_chain));
+}
+
+static int echo_lock_fits_into(const struct lu_env *env,
+ const struct cl_lock_slice *slice,
+ const struct cl_lock_descr *need,
+ const struct cl_io *unused)
+{
+ return 1;
+}
+
+static struct cl_lock_operations echo_lock_ops = {
+ .clo_fini = echo_lock_fini,
+ .clo_delete = echo_lock_delete,
+ .clo_fits_into = echo_lock_fits_into
+};
+
+/** @} echo_lock */
+
+/** \defgroup echo_cl_ops cl_object operations
+ *
+ * operations for cl_object
+ *
+ * @{
+ */
+static int echo_page_init(const struct lu_env *env, struct cl_object *obj,
+ struct cl_page *page, struct page *vmpage)
+{
+ struct echo_page *ep = cl_object_page_slice(obj, page);
+ struct echo_object *eco = cl2echo_obj(obj);
+
+ ep->ep_vmpage = vmpage;
+ page_cache_get(vmpage);
+ mutex_init(&ep->ep_lock);
+ cl_page_slice_add(page, &ep->ep_cl, obj, &echo_page_ops);
+ atomic_inc(&eco->eo_npages);
+ return 0;
+}
+
+static int echo_io_init(const struct lu_env *env, struct cl_object *obj,
+ struct cl_io *io)
+{
+ return 0;
+}
+
+static int echo_lock_init(const struct lu_env *env,
+ struct cl_object *obj, struct cl_lock *lock,
+ const struct cl_io *unused)
+{
+ struct echo_lock *el;
+
+ OBD_SLAB_ALLOC_PTR_GFP(el, echo_lock_kmem, GFP_NOFS);
+ if (el != NULL) {
+ cl_lock_slice_add(lock, &el->el_cl, obj, &echo_lock_ops);
+ el->el_object = cl2echo_obj(obj);
+ INIT_LIST_HEAD(&el->el_chain);
+ atomic_set(&el->el_refcount, 0);
+ }
+ return el == NULL ? -ENOMEM : 0;
+}
+
+static int echo_conf_set(const struct lu_env *env, struct cl_object *obj,
+ const struct cl_object_conf *conf)
+{
+ return 0;
+}
+
+static const struct cl_object_operations echo_cl_obj_ops = {
+ .coo_page_init = echo_page_init,
+ .coo_lock_init = echo_lock_init,
+ .coo_io_init = echo_io_init,
+ .coo_conf_set = echo_conf_set
+};
+/** @} echo_cl_ops */
+
+/** \defgroup echo_lu_ops lu_object operations
+ *
+ * operations for echo lu object.
+ *
+ * @{
+ */
+static int echo_object_init(const struct lu_env *env, struct lu_object *obj,
+ const struct lu_object_conf *conf)
+{
+ struct echo_device *ed = cl2echo_dev(lu2cl_dev(obj->lo_dev));
+ struct echo_client_obd *ec = ed->ed_ec;
+ struct echo_object *eco = cl2echo_obj(lu2cl(obj));
+ const struct cl_object_conf *cconf;
+ struct echo_object_conf *econf;
+
+ if (ed->ed_next) {
+ struct lu_object *below;
+ struct lu_device *under;
+
+ under = ed->ed_next;
+ below = under->ld_ops->ldo_object_alloc(env, obj->lo_header,
+ under);
+ if (below == NULL)
+ return -ENOMEM;
+ lu_object_add(obj, below);
+ }
+
+ cconf = lu2cl_conf(conf);
+ econf = cl2echo_conf(cconf);
+
+ LASSERT(econf->eoc_md);
+ eco->eo_lsm = *econf->eoc_md;
+ /* clear the lsm pointer so that it won't get freed. */
+ *econf->eoc_md = NULL;
+
+ eco->eo_dev = ed;
+ atomic_set(&eco->eo_npages, 0);
+ cl_object_page_init(lu2cl(obj), sizeof(struct echo_page));
+
+ spin_lock(&ec->ec_lock);
+ list_add_tail(&eco->eo_obj_chain, &ec->ec_objects);
+ spin_unlock(&ec->ec_lock);
+
+ return 0;
+}
+
+/* taken from osc_unpackmd() */
+static int echo_alloc_memmd(struct echo_device *ed,
+ struct lov_stripe_md **lsmp)
+{
+ int lsm_size;
+
+ /* If export is lov/osc then use their obd method */
+ if (ed->ed_next != NULL)
+ return obd_alloc_memmd(ed->ed_ec->ec_exp, lsmp);
+ /* OFD has no unpackmd method, do everything here */
+ lsm_size = lov_stripe_md_size(1);
+
+ LASSERT(*lsmp == NULL);
+ OBD_ALLOC(*lsmp, lsm_size);
+ if (*lsmp == NULL)
+ return -ENOMEM;
+
+ OBD_ALLOC((*lsmp)->lsm_oinfo[0], sizeof(struct lov_oinfo));
+ if ((*lsmp)->lsm_oinfo[0] == NULL) {
+ OBD_FREE(*lsmp, lsm_size);
+ return -ENOMEM;
+ }
+
+ loi_init((*lsmp)->lsm_oinfo[0]);
+ (*lsmp)->lsm_maxbytes = LUSTRE_STRIPE_MAXBYTES;
+ ostid_set_seq_echo(&(*lsmp)->lsm_oi);
+
+ return lsm_size;
+}
+
+static int echo_free_memmd(struct echo_device *ed, struct lov_stripe_md **lsmp)
+{
+ int lsm_size;
+
+ /* If export is lov/osc then use their obd method */
+ if (ed->ed_next != NULL)
+ return obd_free_memmd(ed->ed_ec->ec_exp, lsmp);
+ /* OFD has no unpackmd method, do everything here */
+ lsm_size = lov_stripe_md_size(1);
+
+ LASSERT(*lsmp != NULL);
+ OBD_FREE((*lsmp)->lsm_oinfo[0], sizeof(struct lov_oinfo));
+ OBD_FREE(*lsmp, lsm_size);
+ *lsmp = NULL;
+ return 0;
+}
+
+static void echo_object_free(const struct lu_env *env, struct lu_object *obj)
+{
+ struct echo_object *eco = cl2echo_obj(lu2cl(obj));
+ struct echo_client_obd *ec = eco->eo_dev->ed_ec;
+
+ LASSERT(atomic_read(&eco->eo_npages) == 0);
+
+ spin_lock(&ec->ec_lock);
+ list_del_init(&eco->eo_obj_chain);
+ spin_unlock(&ec->ec_lock);
+
+ lu_object_fini(obj);
+ lu_object_header_fini(obj->lo_header);
+
+ if (eco->eo_lsm)
+ echo_free_memmd(eco->eo_dev, &eco->eo_lsm);
+ OBD_SLAB_FREE_PTR(eco, echo_object_kmem);
+}
+
+static int echo_object_print(const struct lu_env *env, void *cookie,
+ lu_printer_t p, const struct lu_object *o)
+{
+ struct echo_object *obj = cl2echo_obj(lu2cl(o));
+
+ return (*p)(env, cookie, "echoclient-object@%p", obj);
+}
+
+static const struct lu_object_operations echo_lu_obj_ops = {
+ .loo_object_init = echo_object_init,
+ .loo_object_delete = NULL,
+ .loo_object_release = NULL,
+ .loo_object_free = echo_object_free,
+ .loo_object_print = echo_object_print,
+ .loo_object_invariant = NULL
+};
+/** @} echo_lu_ops */
+
+/** \defgroup echo_lu_dev_ops lu_device operations
+ *
+ * Operations for echo lu device.
+ *
+ * @{
+ */
+static struct lu_object *echo_object_alloc(const struct lu_env *env,
+ const struct lu_object_header *hdr,
+ struct lu_device *dev)
+{
+ struct echo_object *eco;
+ struct lu_object *obj = NULL;
+
+ /* we're the top dev. */
+ LASSERT(hdr == NULL);
+ OBD_SLAB_ALLOC_PTR_GFP(eco, echo_object_kmem, GFP_NOFS);
+ if (eco != NULL) {
+ struct cl_object_header *hdr = &eco->eo_hdr;
+
+ obj = &echo_obj2cl(eco)->co_lu;
+ cl_object_header_init(hdr);
+ lu_object_init(obj, &hdr->coh_lu, dev);
+ lu_object_add_top(&hdr->coh_lu, obj);
+
+ eco->eo_cl.co_ops = &echo_cl_obj_ops;
+ obj->lo_ops = &echo_lu_obj_ops;
+ }
+ return obj;
+}
+
+static struct lu_device_operations echo_device_lu_ops = {
+ .ldo_object_alloc = echo_object_alloc,
+};
+
+/** @} echo_lu_dev_ops */
+
+static struct cl_device_operations echo_device_cl_ops = {
+};
+
+/** \defgroup echo_init Setup and teardown
+ *
+ * Init and fini functions for echo client.
+ *
+ * @{
+ */
+static int echo_site_init(const struct lu_env *env, struct echo_device *ed)
+{
+ struct cl_site *site = &ed->ed_site_myself;
+ int rc;
+
+ /* initialize site */
+ rc = cl_site_init(site, &ed->ed_cl);
+ if (rc) {
+ CERROR("Cannot initialize site for echo client(%d)\n", rc);
+ return rc;
+ }
+
+ rc = lu_site_init_finish(&site->cs_lu);
+ if (rc)
+ return rc;
+
+ ed->ed_site = site;
+ return 0;
+}
+
+static void echo_site_fini(const struct lu_env *env, struct echo_device *ed)
+{
+ if (ed->ed_site) {
+ cl_site_fini(ed->ed_site);
+ ed->ed_site = NULL;
+ }
+}
+
+static void *echo_thread_key_init(const struct lu_context *ctx,
+ struct lu_context_key *key)
+{
+ struct echo_thread_info *info;
+
+ OBD_SLAB_ALLOC_PTR_GFP(info, echo_thread_kmem, GFP_NOFS);
+ if (info == NULL)
+ info = ERR_PTR(-ENOMEM);
+ return info;
+}
+
+static void echo_thread_key_fini(const struct lu_context *ctx,
+ struct lu_context_key *key, void *data)
+{
+ struct echo_thread_info *info = data;
+
+ OBD_SLAB_FREE_PTR(info, echo_thread_kmem);
+}
+
+static void echo_thread_key_exit(const struct lu_context *ctx,
+ struct lu_context_key *key, void *data)
+{
+}
+
+static struct lu_context_key echo_thread_key = {
+ .lct_tags = LCT_CL_THREAD,
+ .lct_init = echo_thread_key_init,
+ .lct_fini = echo_thread_key_fini,
+ .lct_exit = echo_thread_key_exit
+};
+
+static void *echo_session_key_init(const struct lu_context *ctx,
+ struct lu_context_key *key)
+{
+ struct echo_session_info *session;
+
+ OBD_SLAB_ALLOC_PTR_GFP(session, echo_session_kmem, GFP_NOFS);
+ if (session == NULL)
+ session = ERR_PTR(-ENOMEM);
+ return session;
+}
+
+static void echo_session_key_fini(const struct lu_context *ctx,
+ struct lu_context_key *key, void *data)
+{
+ struct echo_session_info *session = data;
+
+ OBD_SLAB_FREE_PTR(session, echo_session_kmem);
+}
+
+static void echo_session_key_exit(const struct lu_context *ctx,
+ struct lu_context_key *key, void *data)
+{
+}
+
+static struct lu_context_key echo_session_key = {
+ .lct_tags = LCT_SESSION,
+ .lct_init = echo_session_key_init,
+ .lct_fini = echo_session_key_fini,
+ .lct_exit = echo_session_key_exit
+};
+
+LU_TYPE_INIT_FINI(echo, &echo_thread_key, &echo_session_key);
+
+static struct lu_device *echo_device_alloc(const struct lu_env *env,
+ struct lu_device_type *t,
+ struct lustre_cfg *cfg)
+{
+ struct lu_device *next;
+ struct echo_device *ed;
+ struct cl_device *cd;
+ struct obd_device *obd = NULL; /* to keep compiler happy */
+ struct obd_device *tgt;
+ const char *tgt_type_name;
+ int rc;
+ int cleanup = 0;
+
+ OBD_ALLOC_PTR(ed);
+ if (ed == NULL) {
+ rc = -ENOMEM;
+ goto out;
+ }
+
+ cleanup = 1;
+ cd = &ed->ed_cl;
+ rc = cl_device_init(cd, t);
+ if (rc)
+ goto out;
+
+ cd->cd_lu_dev.ld_ops = &echo_device_lu_ops;
+ cd->cd_ops = &echo_device_cl_ops;
+
+ cleanup = 2;
+ obd = class_name2obd(lustre_cfg_string(cfg, 0));
+ LASSERT(obd != NULL);
+ LASSERT(env != NULL);
+
+ tgt = class_name2obd(lustre_cfg_string(cfg, 1));
+ if (tgt == NULL) {
+ CERROR("Can not find tgt device %s\n",
+ lustre_cfg_string(cfg, 1));
+ rc = -ENODEV;
+ goto out;
+ }
+
+ next = tgt->obd_lu_dev;
+ if (!strcmp(tgt->obd_type->typ_name, LUSTRE_MDT_NAME)) {
+ CERROR("echo MDT client must be run on server\n");
+ rc = -EOPNOTSUPP;
+ goto out;
+ }
+
+ rc = echo_site_init(env, ed);
+ if (rc)
+ goto out;
+
+ cleanup = 3;
+
+ rc = echo_client_setup(env, obd, cfg);
+ if (rc)
+ goto out;
+
+ ed->ed_ec = &obd->u.echo_client;
+ cleanup = 4;
+
+ /* if echo client is to be stacked upon ost device, the next is
+ * NULL since ost is not a clio device so far */
+ if (next != NULL && !lu_device_is_cl(next))
+ next = NULL;
+
+ tgt_type_name = tgt->obd_type->typ_name;
+ if (next != NULL) {
+ LASSERT(next != NULL);
+ if (next->ld_site != NULL) {
+ rc = -EBUSY;
+ goto out;
+ }
+
+ next->ld_site = &ed->ed_site->cs_lu;
+ rc = next->ld_type->ldt_ops->ldto_device_init(env, next,
+ next->ld_type->ldt_name,
+ NULL);
+ if (rc)
+ goto out;
+
+ /* Tricky case, I have to determine the obd type since
+ * CLIO uses the different parameters to initialize
+ * objects for lov & osc. */
+ if (strcmp(tgt_type_name, LUSTRE_LOV_NAME) == 0)
+ ed->ed_next_islov = 1;
+ else
+ LASSERT(strcmp(tgt_type_name,
+ LUSTRE_OSC_NAME) == 0);
+ } else {
+ LASSERT(strcmp(tgt_type_name, LUSTRE_OST_NAME) == 0);
+ }
+
+ ed->ed_next = next;
+ return &cd->cd_lu_dev;
+out:
+ switch (cleanup) {
+ case 4: {
+ int rc2;
+
+ rc2 = echo_client_cleanup(obd);
+ if (rc2)
+ CERROR("Cleanup obd device %s error(%d)\n",
+ obd->obd_name, rc2);
+ }
+
+ case 3:
+ echo_site_fini(env, ed);
+ case 2:
+ cl_device_fini(&ed->ed_cl);
+ case 1:
+ OBD_FREE_PTR(ed);
+ case 0:
+ default:
+ break;
+ }
+ return ERR_PTR(rc);
+}
+
+static int echo_device_init(const struct lu_env *env, struct lu_device *d,
+ const char *name, struct lu_device *next)
+{
+ LBUG();
+ return 0;
+}
+
+static struct lu_device *echo_device_fini(const struct lu_env *env,
+ struct lu_device *d)
+{
+ struct echo_device *ed = cl2echo_dev(lu2cl_dev(d));
+ struct lu_device *next = ed->ed_next;
+
+ while (next)
+ next = next->ld_type->ldt_ops->ldto_device_fini(env, next);
+ return NULL;
+}
+
+static void echo_lock_release(const struct lu_env *env,
+ struct echo_lock *ecl,
+ int still_used)
+{
+ struct cl_lock *clk = echo_lock2cl(ecl);
+
+ cl_lock_get(clk);
+ cl_unuse(env, clk);
+ cl_lock_release(env, clk, "ec enqueue", ecl->el_object);
+ if (!still_used) {
+ cl_lock_mutex_get(env, clk);
+ cl_lock_cancel(env, clk);
+ cl_lock_delete(env, clk);
+ cl_lock_mutex_put(env, clk);
+ }
+ cl_lock_put(env, clk);
+}
+
+static struct lu_device *echo_device_free(const struct lu_env *env,
+ struct lu_device *d)
+{
+ struct echo_device *ed = cl2echo_dev(lu2cl_dev(d));
+ struct echo_client_obd *ec = ed->ed_ec;
+ struct echo_object *eco;
+ struct lu_device *next = ed->ed_next;
+
+ CDEBUG(D_INFO, "echo device:%p is going to be freed, next = %p\n",
+ ed, next);
+
+ lu_site_purge(env, &ed->ed_site->cs_lu, -1);
+
+ /* check if there are objects still alive.
+ * It shouldn't have any object because lu_site_purge would cleanup
+ * all of cached objects. Anyway, probably the echo device is being
+ * parallelly accessed.
+ */
+ spin_lock(&ec->ec_lock);
+ list_for_each_entry(eco, &ec->ec_objects, eo_obj_chain)
+ eco->eo_deleted = 1;
+ spin_unlock(&ec->ec_lock);
+
+ /* purge again */
+ lu_site_purge(env, &ed->ed_site->cs_lu, -1);
+
+ CDEBUG(D_INFO,
+ "Waiting for the reference of echo object to be dropped\n");
+
+ /* Wait for the last reference to be dropped. */
+ spin_lock(&ec->ec_lock);
+ while (!list_empty(&ec->ec_objects)) {
+ spin_unlock(&ec->ec_lock);
+ CERROR("echo_client still has objects at cleanup time, wait for 1 second\n");
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ schedule_timeout(cfs_time_seconds(1));
+ lu_site_purge(env, &ed->ed_site->cs_lu, -1);
+ spin_lock(&ec->ec_lock);
+ }
+ spin_unlock(&ec->ec_lock);
+
+ LASSERT(list_empty(&ec->ec_locks));
+
+ CDEBUG(D_INFO, "No object exists, exiting...\n");
+
+ echo_client_cleanup(d->ld_obd);
+
+ while (next)
+ next = next->ld_type->ldt_ops->ldto_device_free(env, next);
+
+ LASSERT(ed->ed_site == lu2cl_site(d->ld_site));
+ echo_site_fini(env, ed);
+ cl_device_fini(&ed->ed_cl);
+ OBD_FREE_PTR(ed);
+
+ return NULL;
+}
+
+static const struct lu_device_type_operations echo_device_type_ops = {
+ .ldto_init = echo_type_init,
+ .ldto_fini = echo_type_fini,
+
+ .ldto_start = echo_type_start,
+ .ldto_stop = echo_type_stop,
+
+ .ldto_device_alloc = echo_device_alloc,
+ .ldto_device_free = echo_device_free,
+ .ldto_device_init = echo_device_init,
+ .ldto_device_fini = echo_device_fini
+};
+
+static struct lu_device_type echo_device_type = {
+ .ldt_tags = LU_DEVICE_CL,
+ .ldt_name = LUSTRE_ECHO_CLIENT_NAME,
+ .ldt_ops = &echo_device_type_ops,
+ .ldt_ctx_tags = LCT_CL_THREAD,
+};
+/** @} echo_init */
+
+/** \defgroup echo_exports Exported operations
+ *
+ * exporting functions to echo client
+ *
+ * @{
+ */
+
+/* Interfaces to echo client obd device */
+static struct echo_object *cl_echo_object_find(struct echo_device *d,
+ struct lov_stripe_md **lsmp)
+{
+ struct lu_env *env;
+ struct echo_thread_info *info;
+ struct echo_object_conf *conf;
+ struct lov_stripe_md *lsm;
+ struct echo_object *eco;
+ struct cl_object *obj;
+ struct lu_fid *fid;
+ int refcheck;
+ int rc;
+
+ LASSERT(lsmp);
+ lsm = *lsmp;
+ LASSERT(lsm);
+ LASSERTF(ostid_id(&lsm->lsm_oi) != 0, DOSTID"\n", POSTID(&lsm->lsm_oi));
+ LASSERTF(ostid_seq(&lsm->lsm_oi) == FID_SEQ_ECHO, DOSTID"\n",
+ POSTID(&lsm->lsm_oi));
+
+ /* Never return an object if the obd is to be freed. */
+ if (echo_dev2cl(d)->cd_lu_dev.ld_obd->obd_stopping)
+ return ERR_PTR(-ENODEV);
+
+ env = cl_env_get(&refcheck);
+ if (IS_ERR(env))
+ return (void *)env;
+
+ info = echo_env_info(env);
+ conf = &info->eti_conf;
+ if (d->ed_next) {
+ if (!d->ed_next_islov) {
+ struct lov_oinfo *oinfo = lsm->lsm_oinfo[0];
+
+ LASSERT(oinfo != NULL);
+ oinfo->loi_oi = lsm->lsm_oi;
+ conf->eoc_cl.u.coc_oinfo = oinfo;
+ } else {
+ struct lustre_md *md;
+
+ md = &info->eti_md;
+ memset(md, 0, sizeof(*md));
+ md->lsm = lsm;
+ conf->eoc_cl.u.coc_md = md;
+ }
+ }
+ conf->eoc_md = lsmp;
+
+ fid = &info->eti_fid;
+ rc = ostid_to_fid(fid, &lsm->lsm_oi, 0);
+ if (rc != 0) {
+ eco = ERR_PTR(rc);
+ goto out;
+ }
+
+ /* In the function below, .hs_keycmp resolves to
+ * lu_obj_hop_keycmp() */
+ /* coverity[overrun-buffer-val] */
+ obj = cl_object_find(env, echo_dev2cl(d), fid, &conf->eoc_cl);
+ if (IS_ERR(obj)) {
+ eco = (void *)obj;
+ goto out;
+ }
+
+ eco = cl2echo_obj(obj);
+ if (eco->eo_deleted) {
+ cl_object_put(env, obj);
+ eco = ERR_PTR(-EAGAIN);
+ }
+
+out:
+ cl_env_put(env, &refcheck);
+ return eco;
+}
+
+static int cl_echo_object_put(struct echo_object *eco)
+{
+ struct lu_env *env;
+ struct cl_object *obj = echo_obj2cl(eco);
+ int refcheck;
+
+ env = cl_env_get(&refcheck);
+ if (IS_ERR(env))
+ return PTR_ERR(env);
+
+ /* an external function to kill an object? */
+ if (eco->eo_deleted) {
+ struct lu_object_header *loh = obj->co_lu.lo_header;
+
+ LASSERT(&eco->eo_hdr == luh2coh(loh));
+ set_bit(LU_OBJECT_HEARD_BANSHEE, &loh->loh_flags);
+ }
+
+ cl_object_put(env, obj);
+ cl_env_put(env, &refcheck);
+ return 0;
+}
+
+static int cl_echo_enqueue0(struct lu_env *env, struct echo_object *eco,
+ u64 start, u64 end, int mode,
+ __u64 *cookie, __u32 enqflags)
+{
+ struct cl_io *io;
+ struct cl_lock *lck;
+ struct cl_object *obj;
+ struct cl_lock_descr *descr;
+ struct echo_thread_info *info;
+ int rc = -ENOMEM;
+
+ info = echo_env_info(env);
+ io = &info->eti_io;
+ descr = &info->eti_descr;
+ obj = echo_obj2cl(eco);
+
+ descr->cld_obj = obj;
+ descr->cld_start = cl_index(obj, start);
+ descr->cld_end = cl_index(obj, end);
+ descr->cld_mode = mode == LCK_PW ? CLM_WRITE : CLM_READ;
+ descr->cld_enq_flags = enqflags;
+ io->ci_obj = obj;
+
+ lck = cl_lock_request(env, io, descr, "ec enqueue", eco);
+ if (lck) {
+ struct echo_client_obd *ec = eco->eo_dev->ed_ec;
+ struct echo_lock *el;
+
+ rc = cl_wait(env, lck);
+ if (rc == 0) {
+ el = cl2echo_lock(cl_lock_at(lck, &echo_device_type));
+ spin_lock(&ec->ec_lock);
+ if (list_empty(&el->el_chain)) {
+ list_add(&el->el_chain, &ec->ec_locks);
+ el->el_cookie = ++ec->ec_unique;
+ }
+ atomic_inc(&el->el_refcount);
+ *cookie = el->el_cookie;
+ spin_unlock(&ec->ec_lock);
+ } else {
+ cl_lock_release(env, lck, "ec enqueue", current);
+ }
+ }
+ return rc;
+}
+
+static int cl_echo_enqueue(struct echo_object *eco, u64 start, u64 end,
+ int mode, __u64 *cookie)
+{
+ struct echo_thread_info *info;
+ struct lu_env *env;
+ struct cl_io *io;
+ int refcheck;
+ int result;
+
+ env = cl_env_get(&refcheck);
+ if (IS_ERR(env))
+ return PTR_ERR(env);
+
+ info = echo_env_info(env);
+ io = &info->eti_io;
+
+ io->ci_ignore_layout = 1;
+ result = cl_io_init(env, io, CIT_MISC, echo_obj2cl(eco));
+ if (result < 0)
+ goto out;
+ LASSERT(result == 0);
+
+ result = cl_echo_enqueue0(env, eco, start, end, mode, cookie, 0);
+ cl_io_fini(env, io);
+
+out:
+ cl_env_put(env, &refcheck);
+ return result;
+}
+
+static int cl_echo_cancel0(struct lu_env *env, struct echo_device *ed,
+ __u64 cookie)
+{
+ struct echo_client_obd *ec = ed->ed_ec;
+ struct echo_lock *ecl = NULL;
+ struct list_head *el;
+ int found = 0, still_used = 0;
+
+ LASSERT(ec != NULL);
+ spin_lock(&ec->ec_lock);
+ list_for_each(el, &ec->ec_locks) {
+ ecl = list_entry(el, struct echo_lock, el_chain);
+ CDEBUG(D_INFO, "ecl: %p, cookie: %#llx\n", ecl, ecl->el_cookie);
+ found = (ecl->el_cookie == cookie);
+ if (found) {
+ if (atomic_dec_and_test(&ecl->el_refcount))
+ list_del_init(&ecl->el_chain);
+ else
+ still_used = 1;
+ break;
+ }
+ }
+ spin_unlock(&ec->ec_lock);
+
+ if (!found)
+ return -ENOENT;
+
+ echo_lock_release(env, ecl, still_used);
+ return 0;
+}
+
+static int cl_echo_cancel(struct echo_device *ed, __u64 cookie)
+{
+ struct lu_env *env;
+ int refcheck;
+ int rc;
+
+ env = cl_env_get(&refcheck);
+ if (IS_ERR(env))
+ return PTR_ERR(env);
+
+ rc = cl_echo_cancel0(env, ed, cookie);
+
+ cl_env_put(env, &refcheck);
+ return rc;
+}
+
+static int cl_echo_async_brw(const struct lu_env *env, struct cl_io *io,
+ enum cl_req_type unused, struct cl_2queue *queue)
+{
+ struct cl_page *clp;
+ struct cl_page *temp;
+ int result = 0;
+
+ cl_page_list_for_each_safe(clp, temp, &queue->c2_qin) {
+ int rc;
+
+ rc = cl_page_cache_add(env, io, clp, CRT_WRITE);
+ if (rc == 0)
+ continue;
+ result = result ?: rc;
+ }
+ return result;
+}
+
+static int cl_echo_object_brw(struct echo_object *eco, int rw, u64 offset,
+ struct page **pages, int npages, int async)
+{
+ struct lu_env *env;
+ struct echo_thread_info *info;
+ struct cl_object *obj = echo_obj2cl(eco);
+ struct echo_device *ed = eco->eo_dev;
+ struct cl_2queue *queue;
+ struct cl_io *io;
+ struct cl_page *clp;
+ struct lustre_handle lh = { 0 };
+ int page_size = cl_page_size(obj);
+ int refcheck;
+ int rc;
+ int i;
+
+ LASSERT((offset & ~CFS_PAGE_MASK) == 0);
+ LASSERT(ed->ed_next != NULL);
+ env = cl_env_get(&refcheck);
+ if (IS_ERR(env))
+ return PTR_ERR(env);
+
+ info = echo_env_info(env);
+ io = &info->eti_io;
+ queue = &info->eti_queue;
+
+ cl_2queue_init(queue);
+
+ io->ci_ignore_layout = 1;
+ rc = cl_io_init(env, io, CIT_MISC, obj);
+ if (rc < 0)
+ goto out;
+ LASSERT(rc == 0);
+
+
+ rc = cl_echo_enqueue0(env, eco, offset,
+ offset + npages * PAGE_CACHE_SIZE - 1,
+ rw == READ ? LCK_PR : LCK_PW, &lh.cookie,
+ CEF_NEVER);
+ if (rc < 0)
+ goto error_lock;
+
+ for (i = 0; i < npages; i++) {
+ LASSERT(pages[i]);
+ clp = cl_page_find(env, obj, cl_index(obj, offset),
+ pages[i], CPT_TRANSIENT);
+ if (IS_ERR(clp)) {
+ rc = PTR_ERR(clp);
+ break;
+ }
+ LASSERT(clp->cp_type == CPT_TRANSIENT);
+
+ rc = cl_page_own(env, io, clp);
+ if (rc) {
+ LASSERT(clp->cp_state == CPS_FREEING);
+ cl_page_put(env, clp);
+ break;
+ }
+
+ cl_2queue_add(queue, clp);
+
+ /* drop the reference count for cl_page_find, so that the page
+ * will be freed in cl_2queue_fini. */
+ cl_page_put(env, clp);
+ cl_page_clip(env, clp, 0, page_size);
+
+ offset += page_size;
+ }
+
+ if (rc == 0) {
+ enum cl_req_type typ = rw == READ ? CRT_READ : CRT_WRITE;
+
+ async = async && (typ == CRT_WRITE);
+ if (async)
+ rc = cl_echo_async_brw(env, io, typ, queue);
+ else
+ rc = cl_io_submit_sync(env, io, typ, queue, 0);
+ CDEBUG(D_INFO, "echo_client %s write returns %d\n",
+ async ? "async" : "sync", rc);
+ }
+
+ cl_echo_cancel0(env, ed, lh.cookie);
+error_lock:
+ cl_2queue_discard(env, io, queue);
+ cl_2queue_disown(env, io, queue);
+ cl_2queue_fini(env, queue);
+ cl_io_fini(env, io);
+out:
+ cl_env_put(env, &refcheck);
+ return rc;
+}
+/** @} echo_exports */
+
+
+static u64 last_object_id;
+
+static int
+echo_copyout_lsm(struct lov_stripe_md *lsm, void *_ulsm, int ulsm_nob)
+{
+ struct lov_stripe_md *ulsm = _ulsm;
+ int nob, i;
+
+ nob = offsetof(struct lov_stripe_md, lsm_oinfo[lsm->lsm_stripe_count]);
+ if (nob > ulsm_nob)
+ return -EINVAL;
+
+ if (copy_to_user(ulsm, lsm, sizeof(*ulsm)))
+ return -EFAULT;
+
+ for (i = 0; i < lsm->lsm_stripe_count; i++) {
+ if (copy_to_user(ulsm->lsm_oinfo[i], lsm->lsm_oinfo[i],
+ sizeof(lsm->lsm_oinfo[0])))
+ return -EFAULT;
+ }
+ return 0;
+}
+
+static int
+echo_copyin_lsm(struct echo_device *ed, struct lov_stripe_md *lsm,
+ void *ulsm, int ulsm_nob)
+{
+ struct echo_client_obd *ec = ed->ed_ec;
+ int i;
+
+ if (ulsm_nob < sizeof(*lsm))
+ return -EINVAL;
+
+ if (copy_from_user(lsm, ulsm, sizeof(*lsm)))
+ return -EFAULT;
+
+ if (lsm->lsm_stripe_count > ec->ec_nstripes ||
+ lsm->lsm_magic != LOV_MAGIC ||
+ (lsm->lsm_stripe_size & (~CFS_PAGE_MASK)) != 0 ||
+ ((__u64)lsm->lsm_stripe_size * lsm->lsm_stripe_count > ~0UL))
+ return -EINVAL;
+
+
+ for (i = 0; i < lsm->lsm_stripe_count; i++) {
+ if (copy_from_user(lsm->lsm_oinfo[i],
+ ((struct lov_stripe_md *)ulsm)-> \
+ lsm_oinfo[i],
+ sizeof(lsm->lsm_oinfo[0])))
+ return -EFAULT;
+ }
+ return 0;
+}
+
+static int echo_create_object(const struct lu_env *env, struct echo_device *ed,
+ int on_target, struct obdo *oa, void *ulsm,
+ int ulsm_nob, struct obd_trans_info *oti)
+{
+ struct echo_object *eco;
+ struct echo_client_obd *ec = ed->ed_ec;
+ struct lov_stripe_md *lsm = NULL;
+ int rc;
+ int created = 0;
+
+ if ((oa->o_valid & OBD_MD_FLID) == 0 && /* no obj id */
+ (on_target || /* set_stripe */
+ ec->ec_nstripes != 0)) { /* LOV */
+ CERROR("No valid oid\n");
+ return -EINVAL;
+ }
+
+ rc = echo_alloc_memmd(ed, &lsm);
+ if (rc < 0) {
+ CERROR("Cannot allocate md: rc = %d\n", rc);
+ goto failed;
+ }
+
+ if (ulsm != NULL) {
+ int i, idx;
+
+ rc = echo_copyin_lsm(ed, lsm, ulsm, ulsm_nob);
+ if (rc != 0)
+ goto failed;
+
+ if (lsm->lsm_stripe_count == 0)
+ lsm->lsm_stripe_count = ec->ec_nstripes;
+
+ if (lsm->lsm_stripe_size == 0)
+ lsm->lsm_stripe_size = PAGE_CACHE_SIZE;
+
+ idx = cfs_rand();
+
+ /* setup stripes: indices + default ids if required */
+ for (i = 0; i < lsm->lsm_stripe_count; i++) {
+ if (ostid_id(&lsm->lsm_oinfo[i]->loi_oi) == 0)
+ lsm->lsm_oinfo[i]->loi_oi = lsm->lsm_oi;
+
+ lsm->lsm_oinfo[i]->loi_ost_idx =
+ (idx + i) % ec->ec_nstripes;
+ }
+ }
+
+ /* setup object ID here for !on_target and LOV hint */
+ if (oa->o_valid & OBD_MD_FLID) {
+ LASSERT(oa->o_valid & OBD_MD_FLGROUP);
+ lsm->lsm_oi = oa->o_oi;
+ }
+
+ if (ostid_id(&lsm->lsm_oi) == 0)
+ ostid_set_id(&lsm->lsm_oi, ++last_object_id);
+
+ rc = 0;
+ if (on_target) {
+ /* Only echo objects are allowed to be created */
+ LASSERT((oa->o_valid & OBD_MD_FLGROUP) &&
+ (ostid_seq(&oa->o_oi) == FID_SEQ_ECHO));
+ rc = obd_create(env, ec->ec_exp, oa, &lsm, oti);
+ if (rc != 0) {
+ CERROR("Cannot create objects: rc = %d\n", rc);
+ goto failed;
+ }
+ created = 1;
+ }
+
+ /* See what object ID we were given */
+ oa->o_oi = lsm->lsm_oi;
+ oa->o_valid |= OBD_MD_FLID;
+
+ eco = cl_echo_object_find(ed, &lsm);
+ if (IS_ERR(eco)) {
+ rc = PTR_ERR(eco);
+ goto failed;
+ }
+ cl_echo_object_put(eco);
+
+ CDEBUG(D_INFO, "oa oid "DOSTID"\n", POSTID(&oa->o_oi));
+
+ failed:
+ if (created && rc)
+ obd_destroy(env, ec->ec_exp, oa, lsm, oti, NULL, NULL);
+ if (lsm)
+ echo_free_memmd(ed, &lsm);
+ if (rc)
+ CERROR("create object failed with: rc = %d\n", rc);
+ return rc;
+}
+
+static int echo_get_object(struct echo_object **ecop, struct echo_device *ed,
+ struct obdo *oa)
+{
+ struct lov_stripe_md *lsm = NULL;
+ struct echo_object *eco;
+ int rc;
+
+ if ((oa->o_valid & OBD_MD_FLID) == 0 || ostid_id(&oa->o_oi) == 0) {
+ /* disallow use of object id 0 */
+ CERROR("No valid oid\n");
+ return -EINVAL;
+ }
+
+ rc = echo_alloc_memmd(ed, &lsm);
+ if (rc < 0)
+ return rc;
+
+ lsm->lsm_oi = oa->o_oi;
+ if (!(oa->o_valid & OBD_MD_FLGROUP))
+ ostid_set_seq_echo(&lsm->lsm_oi);
+
+ rc = 0;
+ eco = cl_echo_object_find(ed, &lsm);
+ if (!IS_ERR(eco))
+ *ecop = eco;
+ else
+ rc = PTR_ERR(eco);
+ if (lsm)
+ echo_free_memmd(ed, &lsm);
+ return rc;
+}
+
+static void echo_put_object(struct echo_object *eco)
+{
+ if (cl_echo_object_put(eco))
+ CERROR("echo client: drop an object failed");
+}
+
+static void
+echo_get_stripe_off_id(struct lov_stripe_md *lsm, u64 *offp, u64 *idp)
+{
+ unsigned long stripe_count;
+ unsigned long stripe_size;
+ unsigned long width;
+ unsigned long woffset;
+ int stripe_index;
+ u64 offset;
+
+ if (lsm->lsm_stripe_count <= 1)
+ return;
+
+ offset = *offp;
+ stripe_size = lsm->lsm_stripe_size;
+ stripe_count = lsm->lsm_stripe_count;
+
+ /* width = # bytes in all stripes */
+ width = stripe_size * stripe_count;
+
+ /* woffset = offset within a width; offset = whole number of widths */
+ woffset = do_div(offset, width);
+
+ stripe_index = woffset / stripe_size;
+
+ *idp = ostid_id(&lsm->lsm_oinfo[stripe_index]->loi_oi);
+ *offp = offset * stripe_size + woffset % stripe_size;
+}
+
+static void
+echo_client_page_debug_setup(struct lov_stripe_md *lsm,
+ struct page *page, int rw, u64 id,
+ u64 offset, u64 count)
+{
+ char *addr;
+ u64 stripe_off;
+ u64 stripe_id;
+ int delta;
+
+ /* no partial pages on the client */
+ LASSERT(count == PAGE_CACHE_SIZE);
+
+ addr = kmap(page);
+
+ for (delta = 0; delta < PAGE_CACHE_SIZE; delta += OBD_ECHO_BLOCK_SIZE) {
+ if (rw == OBD_BRW_WRITE) {
+ stripe_off = offset + delta;
+ stripe_id = id;
+ echo_get_stripe_off_id(lsm, &stripe_off, &stripe_id);
+ } else {
+ stripe_off = 0xdeadbeef00c0ffeeULL;
+ stripe_id = 0xdeadbeef00c0ffeeULL;
+ }
+ block_debug_setup(addr + delta, OBD_ECHO_BLOCK_SIZE,
+ stripe_off, stripe_id);
+ }
+
+ kunmap(page);
+}
+
+static int echo_client_page_debug_check(struct lov_stripe_md *lsm,
+ struct page *page, u64 id,
+ u64 offset, u64 count)
+{
+ u64 stripe_off;
+ u64 stripe_id;
+ char *addr;
+ int delta;
+ int rc;
+ int rc2;
+
+ /* no partial pages on the client */
+ LASSERT(count == PAGE_CACHE_SIZE);
+
+ addr = kmap(page);
+
+ for (rc = delta = 0; delta < PAGE_CACHE_SIZE; delta += OBD_ECHO_BLOCK_SIZE) {
+ stripe_off = offset + delta;
+ stripe_id = id;
+ echo_get_stripe_off_id(lsm, &stripe_off, &stripe_id);
+
+ rc2 = block_debug_check("test_brw",
+ addr + delta, OBD_ECHO_BLOCK_SIZE,
+ stripe_off, stripe_id);
+ if (rc2 != 0) {
+ CERROR("Error in echo object %#llx\n", id);
+ rc = rc2;
+ }
+ }
+
+ kunmap(page);
+ return rc;
+}
+
+static int echo_client_kbrw(struct echo_device *ed, int rw, struct obdo *oa,
+ struct echo_object *eco, u64 offset,
+ u64 count, int async,
+ struct obd_trans_info *oti)
+{
+ struct lov_stripe_md *lsm = eco->eo_lsm;
+ u32 npages;
+ struct brw_page *pga;
+ struct brw_page *pgp;
+ struct page **pages;
+ u64 off;
+ int i;
+ int rc;
+ int verify;
+ gfp_t gfp_mask;
+ int brw_flags = 0;
+
+ verify = (ostid_id(&oa->o_oi) != ECHO_PERSISTENT_OBJID &&
+ (oa->o_valid & OBD_MD_FLFLAGS) != 0 &&
+ (oa->o_flags & OBD_FL_DEBUG_CHECK) != 0);
+
+ gfp_mask = ((ostid_id(&oa->o_oi) & 2) == 0) ? GFP_IOFS : GFP_HIGHUSER;
+
+ LASSERT(rw == OBD_BRW_WRITE || rw == OBD_BRW_READ);
+ LASSERT(lsm != NULL);
+ LASSERT(ostid_id(&lsm->lsm_oi) == ostid_id(&oa->o_oi));
+
+ if (count <= 0 ||
+ (count & (~CFS_PAGE_MASK)) != 0)
+ return -EINVAL;
+
+ /* XXX think again with misaligned I/O */
+ npages = count >> PAGE_CACHE_SHIFT;
+
+ if (rw == OBD_BRW_WRITE)
+ brw_flags = OBD_BRW_ASYNC;
+
+ OBD_ALLOC(pga, npages * sizeof(*pga));
+ if (pga == NULL)
+ return -ENOMEM;
+
+ OBD_ALLOC(pages, npages * sizeof(*pages));
+ if (pages == NULL) {
+ OBD_FREE(pga, npages * sizeof(*pga));
+ return -ENOMEM;
+ }
+
+ for (i = 0, pgp = pga, off = offset;
+ i < npages;
+ i++, pgp++, off += PAGE_CACHE_SIZE) {
+
+ LASSERT(pgp->pg == NULL); /* for cleanup */
+
+ rc = -ENOMEM;
+ OBD_PAGE_ALLOC(pgp->pg, gfp_mask);
+ if (pgp->pg == NULL)
+ goto out;
+
+ pages[i] = pgp->pg;
+ pgp->count = PAGE_CACHE_SIZE;
+ pgp->off = off;
+ pgp->flag = brw_flags;
+
+ if (verify)
+ echo_client_page_debug_setup(lsm, pgp->pg, rw,
+ ostid_id(&oa->o_oi), off,
+ pgp->count);
+ }
+
+ /* brw mode can only be used at client */
+ LASSERT(ed->ed_next != NULL);
+ rc = cl_echo_object_brw(eco, rw, offset, pages, npages, async);
+
+ out:
+ if (rc != 0 || rw != OBD_BRW_READ)
+ verify = 0;
+
+ for (i = 0, pgp = pga; i < npages; i++, pgp++) {
+ if (pgp->pg == NULL)
+ continue;
+
+ if (verify) {
+ int vrc;
+
+ vrc = echo_client_page_debug_check(lsm, pgp->pg,
+ ostid_id(&oa->o_oi),
+ pgp->off, pgp->count);
+ if (vrc != 0 && rc == 0)
+ rc = vrc;
+ }
+ OBD_PAGE_FREE(pgp->pg);
+ }
+ OBD_FREE(pga, npages * sizeof(*pga));
+ OBD_FREE(pages, npages * sizeof(*pages));
+ return rc;
+}
+
+static int echo_client_prep_commit(const struct lu_env *env,
+ struct obd_export *exp, int rw,
+ struct obdo *oa, struct echo_object *eco,
+ u64 offset, u64 count,
+ u64 batch, struct obd_trans_info *oti,
+ int async)
+{
+ struct lov_stripe_md *lsm = eco->eo_lsm;
+ struct obd_ioobj ioo;
+ struct niobuf_local *lnb;
+ struct niobuf_remote *rnb;
+ u64 off;
+ u64 npages, tot_pages;
+ int i, ret = 0, brw_flags = 0;
+
+ if (count <= 0 || (count & (~CFS_PAGE_MASK)) != 0 ||
+ (lsm != NULL && ostid_id(&lsm->lsm_oi) != ostid_id(&oa->o_oi)))
+ return -EINVAL;
+
+ npages = batch >> PAGE_CACHE_SHIFT;
+ tot_pages = count >> PAGE_CACHE_SHIFT;
+
+ OBD_ALLOC(lnb, npages * sizeof(struct niobuf_local));
+ OBD_ALLOC(rnb, npages * sizeof(struct niobuf_remote));
+
+ if (lnb == NULL || rnb == NULL) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ if (rw == OBD_BRW_WRITE && async)
+ brw_flags |= OBD_BRW_ASYNC;
+
+ obdo_to_ioobj(oa, &ioo);
+
+ off = offset;
+
+ for (; tot_pages; tot_pages -= npages) {
+ int lpages;
+
+ if (tot_pages < npages)
+ npages = tot_pages;
+
+ for (i = 0; i < npages; i++, off += PAGE_CACHE_SIZE) {
+ rnb[i].offset = off;
+ rnb[i].len = PAGE_CACHE_SIZE;
+ rnb[i].flags = brw_flags;
+ }
+
+ ioo.ioo_bufcnt = npages;
+ oti->oti_transno = 0;
+
+ lpages = npages;
+ ret = obd_preprw(env, rw, exp, oa, 1, &ioo, rnb, &lpages,
+ lnb, oti, NULL);
+ if (ret != 0)
+ goto out;
+ LASSERT(lpages == npages);
+
+ for (i = 0; i < lpages; i++) {
+ struct page *page = lnb[i].page;
+
+ /* read past eof? */
+ if (page == NULL && lnb[i].rc == 0)
+ continue;
+
+ if (async)
+ lnb[i].flags |= OBD_BRW_ASYNC;
+
+ if (ostid_id(&oa->o_oi) == ECHO_PERSISTENT_OBJID ||
+ (oa->o_valid & OBD_MD_FLFLAGS) == 0 ||
+ (oa->o_flags & OBD_FL_DEBUG_CHECK) == 0)
+ continue;
+
+ if (rw == OBD_BRW_WRITE)
+ echo_client_page_debug_setup(lsm, page, rw,
+ ostid_id(&oa->o_oi),
+ rnb[i].offset,
+ rnb[i].len);
+ else
+ echo_client_page_debug_check(lsm, page,
+ ostid_id(&oa->o_oi),
+ rnb[i].offset,
+ rnb[i].len);
+ }
+
+ ret = obd_commitrw(env, rw, exp, oa, 1, &ioo,
+ rnb, npages, lnb, oti, ret);
+ if (ret != 0)
+ goto out;
+
+ /* Reset oti otherwise it would confuse ldiskfs. */
+ memset(oti, 0, sizeof(*oti));
+
+ /* Reuse env context. */
+ lu_context_exit((struct lu_context *)&env->le_ctx);
+ lu_context_enter((struct lu_context *)&env->le_ctx);
+ }
+
+out:
+ if (lnb)
+ OBD_FREE(lnb, npages * sizeof(struct niobuf_local));
+ if (rnb)
+ OBD_FREE(rnb, npages * sizeof(struct niobuf_remote));
+ return ret;
+}
+
+static int echo_client_brw_ioctl(const struct lu_env *env, int rw,
+ struct obd_export *exp,
+ struct obd_ioctl_data *data,
+ struct obd_trans_info *dummy_oti)
+{
+ struct obd_device *obd = class_exp2obd(exp);
+ struct echo_device *ed = obd2echo_dev(obd);
+ struct echo_client_obd *ec = ed->ed_ec;
+ struct obdo *oa = &data->ioc_obdo1;
+ struct echo_object *eco;
+ int rc;
+ int async = 1;
+ long test_mode;
+
+ LASSERT(oa->o_valid & OBD_MD_FLGROUP);
+
+ rc = echo_get_object(&eco, ed, oa);
+ if (rc)
+ return rc;
+
+ oa->o_valid &= ~OBD_MD_FLHANDLE;
+
+ /* OFD/obdfilter works only via prep/commit */
+ test_mode = (long)data->ioc_pbuf1;
+ if (test_mode == 1)
+ async = 0;
+
+ if (ed->ed_next == NULL && test_mode != 3) {
+ test_mode = 3;
+ data->ioc_plen1 = data->ioc_count;
+ }
+
+ /* Truncate batch size to maximum */
+ if (data->ioc_plen1 > PTLRPC_MAX_BRW_SIZE)
+ data->ioc_plen1 = PTLRPC_MAX_BRW_SIZE;
+
+ switch (test_mode) {
+ case 1:
+ /* fall through */
+ case 2:
+ rc = echo_client_kbrw(ed, rw, oa,
+ eco, data->ioc_offset,
+ data->ioc_count, async, dummy_oti);
+ break;
+ case 3:
+ rc = echo_client_prep_commit(env, ec->ec_exp, rw, oa,
+ eco, data->ioc_offset,
+ data->ioc_count, data->ioc_plen1,
+ dummy_oti, async);
+ break;
+ default:
+ rc = -EINVAL;
+ }
+ echo_put_object(eco);
+ return rc;
+}
+
+static int
+echo_client_enqueue(struct obd_export *exp, struct obdo *oa,
+ int mode, u64 offset, u64 nob)
+{
+ struct echo_device *ed = obd2echo_dev(exp->exp_obd);
+ struct lustre_handle *ulh = &oa->o_handle;
+ struct echo_object *eco;
+ u64 end;
+ int rc;
+
+ if (ed->ed_next == NULL)
+ return -EOPNOTSUPP;
+
+ if (!(mode == LCK_PR || mode == LCK_PW))
+ return -EINVAL;
+
+ if ((offset & (~CFS_PAGE_MASK)) != 0 ||
+ (nob & (~CFS_PAGE_MASK)) != 0)
+ return -EINVAL;
+
+ rc = echo_get_object(&eco, ed, oa);
+ if (rc != 0)
+ return rc;
+
+ end = (nob == 0) ? ((u64) -1) : (offset + nob - 1);
+ rc = cl_echo_enqueue(eco, offset, end, mode, &ulh->cookie);
+ if (rc == 0) {
+ oa->o_valid |= OBD_MD_FLHANDLE;
+ CDEBUG(D_INFO, "Cookie is %#llx\n", ulh->cookie);
+ }
+ echo_put_object(eco);
+ return rc;
+}
+
+static int
+echo_client_cancel(struct obd_export *exp, struct obdo *oa)
+{
+ struct echo_device *ed = obd2echo_dev(exp->exp_obd);
+ __u64 cookie = oa->o_handle.cookie;
+
+ if ((oa->o_valid & OBD_MD_FLHANDLE) == 0)
+ return -EINVAL;
+
+ CDEBUG(D_INFO, "Cookie is %#llx\n", cookie);
+ return cl_echo_cancel(ed, cookie);
+}
+
+static int
+echo_client_iocontrol(unsigned int cmd, struct obd_export *exp, int len,
+ void *karg, void *uarg)
+{
+ struct obd_device *obd = exp->exp_obd;
+ struct echo_device *ed = obd2echo_dev(obd);
+ struct echo_client_obd *ec = ed->ed_ec;
+ struct echo_object *eco;
+ struct obd_ioctl_data *data = karg;
+ struct obd_trans_info dummy_oti;
+ struct lu_env *env;
+ struct oti_req_ack_lock *ack_lock;
+ struct obdo *oa;
+ struct lu_fid fid;
+ int rw = OBD_BRW_READ;
+ int rc = 0;
+ int i;
+
+ memset(&dummy_oti, 0, sizeof(dummy_oti));
+
+ oa = &data->ioc_obdo1;
+ if (!(oa->o_valid & OBD_MD_FLGROUP)) {
+ oa->o_valid |= OBD_MD_FLGROUP;
+ ostid_set_seq_echo(&oa->o_oi);
+ }
+
+ /* This FID is unpacked just for validation at this point */
+ rc = ostid_to_fid(&fid, &oa->o_oi, 0);
+ if (rc < 0)
+ return rc;
+
+ OBD_ALLOC_PTR(env);
+ if (env == NULL)
+ return -ENOMEM;
+
+ rc = lu_env_init(env, LCT_DT_THREAD);
+ if (rc) {
+ rc = -ENOMEM;
+ goto out;
+ }
+
+ switch (cmd) {
+ case OBD_IOC_CREATE: /* may create echo object */
+ if (!capable(CFS_CAP_SYS_ADMIN)) {
+ rc = -EPERM;
+ goto out;
+ }
+
+ rc = echo_create_object(env, ed, 1, oa, data->ioc_pbuf1,
+ data->ioc_plen1, &dummy_oti);
+ goto out;
+
+ case OBD_IOC_DESTROY:
+ if (!capable(CFS_CAP_SYS_ADMIN)) {
+ rc = -EPERM;
+ goto out;
+ }
+
+ rc = echo_get_object(&eco, ed, oa);
+ if (rc == 0) {
+ rc = obd_destroy(env, ec->ec_exp, oa, eco->eo_lsm,
+ &dummy_oti, NULL, NULL);
+ if (rc == 0)
+ eco->eo_deleted = 1;
+ echo_put_object(eco);
+ }
+ goto out;
+
+ case OBD_IOC_GETATTR:
+ rc = echo_get_object(&eco, ed, oa);
+ if (rc == 0) {
+ struct obd_info oinfo = { { { 0 } } };
+
+ oinfo.oi_md = eco->eo_lsm;
+ oinfo.oi_oa = oa;
+ rc = obd_getattr(env, ec->ec_exp, &oinfo);
+ echo_put_object(eco);
+ }
+ goto out;
+
+ case OBD_IOC_SETATTR:
+ if (!capable(CFS_CAP_SYS_ADMIN)) {
+ rc = -EPERM;
+ goto out;
+ }
+
+ rc = echo_get_object(&eco, ed, oa);
+ if (rc == 0) {
+ struct obd_info oinfo = { { { 0 } } };
+
+ oinfo.oi_oa = oa;
+ oinfo.oi_md = eco->eo_lsm;
+
+ rc = obd_setattr(env, ec->ec_exp, &oinfo, NULL);
+ echo_put_object(eco);
+ }
+ goto out;
+
+ case OBD_IOC_BRW_WRITE:
+ if (!capable(CFS_CAP_SYS_ADMIN)) {
+ rc = -EPERM;
+ goto out;
+ }
+
+ rw = OBD_BRW_WRITE;
+ /* fall through */
+ case OBD_IOC_BRW_READ:
+ rc = echo_client_brw_ioctl(env, rw, exp, data, &dummy_oti);
+ goto out;
+
+ case ECHO_IOC_GET_STRIPE:
+ rc = echo_get_object(&eco, ed, oa);
+ if (rc == 0) {
+ rc = echo_copyout_lsm(eco->eo_lsm, data->ioc_pbuf1,
+ data->ioc_plen1);
+ echo_put_object(eco);
+ }
+ goto out;
+
+ case ECHO_IOC_SET_STRIPE:
+ if (!capable(CFS_CAP_SYS_ADMIN)) {
+ rc = -EPERM;
+ goto out;
+ }
+
+ if (data->ioc_pbuf1 == NULL) { /* unset */
+ rc = echo_get_object(&eco, ed, oa);
+ if (rc == 0) {
+ eco->eo_deleted = 1;
+ echo_put_object(eco);
+ }
+ } else {
+ rc = echo_create_object(env, ed, 0, oa,
+ data->ioc_pbuf1,
+ data->ioc_plen1, &dummy_oti);
+ }
+ goto out;
+
+ case ECHO_IOC_ENQUEUE:
+ if (!capable(CFS_CAP_SYS_ADMIN)) {
+ rc = -EPERM;
+ goto out;
+ }
+
+ rc = echo_client_enqueue(exp, oa,
+ data->ioc_conn1, /* lock mode */
+ data->ioc_offset,
+ data->ioc_count);/*extent*/
+ goto out;
+
+ case ECHO_IOC_CANCEL:
+ rc = echo_client_cancel(exp, oa);
+ goto out;
+
+ default:
+ CERROR("echo_ioctl(): unrecognised ioctl %#x\n", cmd);
+ rc = -ENOTTY;
+ goto out;
+ }
+
+out:
+ lu_env_fini(env);
+ OBD_FREE_PTR(env);
+
+ /* XXX this should be in a helper also called by target_send_reply */
+ for (ack_lock = dummy_oti.oti_ack_locks, i = 0; i < 4;
+ i++, ack_lock++) {
+ if (!ack_lock->mode)
+ break;
+ ldlm_lock_decref(&ack_lock->lock, ack_lock->mode);
+ }
+
+ return rc;
+}
+
+static int echo_client_setup(const struct lu_env *env,
+ struct obd_device *obddev, struct lustre_cfg *lcfg)
+{
+ struct echo_client_obd *ec = &obddev->u.echo_client;
+ struct obd_device *tgt;
+ struct obd_uuid echo_uuid = { "ECHO_UUID" };
+ struct obd_connect_data *ocd = NULL;
+ int rc;
+
+ if (lcfg->lcfg_bufcount < 2 || LUSTRE_CFG_BUFLEN(lcfg, 1) < 1) {
+ CERROR("requires a TARGET OBD name\n");
+ return -EINVAL;
+ }
+
+ tgt = class_name2obd(lustre_cfg_string(lcfg, 1));
+ if (!tgt || !tgt->obd_attached || !tgt->obd_set_up) {
+ CERROR("device not attached or not set up (%s)\n",
+ lustre_cfg_string(lcfg, 1));
+ return -EINVAL;
+ }
+
+ spin_lock_init(&ec->ec_lock);
+ INIT_LIST_HEAD(&ec->ec_objects);
+ INIT_LIST_HEAD(&ec->ec_locks);
+ ec->ec_unique = 0;
+ ec->ec_nstripes = 0;
+
+ OBD_ALLOC(ocd, sizeof(*ocd));
+ if (ocd == NULL) {
+ CERROR("Can't alloc ocd connecting to %s\n",
+ lustre_cfg_string(lcfg, 1));
+ return -ENOMEM;
+ }
+
+ ocd->ocd_connect_flags = OBD_CONNECT_VERSION | OBD_CONNECT_REQPORTAL |
+ OBD_CONNECT_BRW_SIZE |
+ OBD_CONNECT_GRANT | OBD_CONNECT_FULL20 |
+ OBD_CONNECT_64BITHASH | OBD_CONNECT_LVB_TYPE |
+ OBD_CONNECT_FID;
+ ocd->ocd_brw_size = DT_MAX_BRW_SIZE;
+ ocd->ocd_version = LUSTRE_VERSION_CODE;
+ ocd->ocd_group = FID_SEQ_ECHO;
+
+ rc = obd_connect(env, &ec->ec_exp, tgt, &echo_uuid, ocd, NULL);
+ if (rc == 0) {
+ /* Turn off pinger because it connects to tgt obd directly. */
+ spin_lock(&tgt->obd_dev_lock);
+ list_del_init(&ec->ec_exp->exp_obd_chain_timed);
+ spin_unlock(&tgt->obd_dev_lock);
+ }
+
+ OBD_FREE(ocd, sizeof(*ocd));
+
+ if (rc != 0) {
+ CERROR("fail to connect to device %s\n",
+ lustre_cfg_string(lcfg, 1));
+ return rc;
+ }
+
+ return rc;
+}
+
+static int echo_client_cleanup(struct obd_device *obddev)
+{
+ struct echo_client_obd *ec = &obddev->u.echo_client;
+ int rc;
+
+ if (!list_empty(&obddev->obd_exports)) {
+ CERROR("still has clients!\n");
+ return -EBUSY;
+ }
+
+ LASSERT(atomic_read(&ec->ec_exp->exp_refcount) > 0);
+ rc = obd_disconnect(ec->ec_exp);
+ if (rc != 0)
+ CERROR("fail to disconnect device: %d\n", rc);
+
+ return rc;
+}
+
+static int echo_client_connect(const struct lu_env *env,
+ struct obd_export **exp,
+ struct obd_device *src, struct obd_uuid *cluuid,
+ struct obd_connect_data *data, void *localdata)
+{
+ int rc;
+ struct lustre_handle conn = { 0 };
+
+ rc = class_connect(&conn, src, cluuid);
+ if (rc == 0) {
+ *exp = class_conn2export(&conn);
+ }
+
+ return rc;
+}
+
+static int echo_client_disconnect(struct obd_export *exp)
+{
+ int rc;
+
+ if (exp == NULL) {
+ rc = -EINVAL;
+ goto out;
+ }
+
+ rc = class_disconnect(exp);
+ goto out;
+ out:
+ return rc;
+}
+
+static struct obd_ops echo_client_obd_ops = {
+ .o_owner = THIS_MODULE,
+ .o_iocontrol = echo_client_iocontrol,
+ .o_connect = echo_client_connect,
+ .o_disconnect = echo_client_disconnect
+};
+
+int echo_client_init(void)
+{
+ struct lprocfs_static_vars lvars = { NULL };
+ int rc;
+
+ lprocfs_echo_init_vars(&lvars);
+
+ rc = lu_kmem_init(echo_caches);
+ if (rc == 0) {
+ rc = class_register_type(&echo_client_obd_ops, NULL,
+ lvars.module_vars,
+ LUSTRE_ECHO_CLIENT_NAME,
+ &echo_device_type);
+ if (rc)
+ lu_kmem_fini(echo_caches);
+ }
+ return rc;
+}
+
+void echo_client_exit(void)
+{
+ class_unregister_type(LUSTRE_ECHO_CLIENT_NAME);
+ lu_kmem_fini(echo_caches);
+}
+
+static int __init obdecho_init(void)
+{
+ struct lprocfs_static_vars lvars;
+
+ LCONSOLE_INFO("Echo OBD driver; http://www.lustre.org/\n");
+
+ LASSERT(PAGE_CACHE_SIZE % OBD_ECHO_BLOCK_SIZE == 0);
+
+ lprocfs_echo_init_vars(&lvars);
+
+
+ return echo_client_init();
+}
+
+static void /*__exit*/ obdecho_exit(void)
+{
+ echo_client_exit();
+
+}
+
+MODULE_AUTHOR("Sun Microsystems, Inc. <http://www.lustre.org/>");
+MODULE_DESCRIPTION("Lustre Testing Echo OBD driver");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(LUSTRE_VERSION_STRING);
+
+module_init(obdecho_init);
+module_exit(obdecho_exit);
+
+/** @} echo_client */
diff --git a/drivers/staging/lustre/lustre/obdecho/echo_internal.h b/drivers/staging/lustre/lustre/obdecho/echo_internal.h
new file mode 100644
index 000000000..8e9dbc235
--- /dev/null
+++ b/drivers/staging/lustre/lustre/obdecho/echo_internal.h
@@ -0,0 +1,47 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 021110-1307, USA
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2012, Whamcloud, Inc.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ *
+ * lustre/obdecho/echo_internal.h
+ */
+
+#ifndef _ECHO_INTERNAL_H
+#define _ECHO_INTERNAL_H
+
+/* The persistent object (i.e. actually stores stuff!) */
+#define ECHO_PERSISTENT_OBJID 1ULL
+#define ECHO_PERSISTENT_SIZE ((__u64)(1<<20))
+
+/* block size to use for data verification */
+#define OBD_ECHO_BLOCK_SIZE (4<<10)
+
+
+#endif
diff --git a/drivers/staging/lustre/lustre/obdecho/lproc_echo.c b/drivers/staging/lustre/lustre/obdecho/lproc_echo.c
new file mode 100644
index 000000000..0beb97db7
--- /dev/null
+++ b/drivers/staging/lustre/lustre/obdecho/lproc_echo.c
@@ -0,0 +1,57 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2002, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ */
+#define DEBUG_SUBSYSTEM S_ECHO
+
+#include "../include/lprocfs_status.h"
+#include "../include/obd_class.h"
+
+#if defined(CONFIG_PROC_FS)
+LPROC_SEQ_FOPS_RO_TYPE(echo, uuid);
+static struct lprocfs_vars lprocfs_echo_obd_vars[] = {
+ { "uuid", &echo_uuid_fops, NULL, 0 },
+ { NULL }
+};
+
+LPROC_SEQ_FOPS_RO_TYPE(echo, numrefs);
+static struct lprocfs_vars lprocfs_echo_module_vars[] = {
+ { "num_refs", &echo_numrefs_fops, NULL, 0 },
+ { NULL }
+};
+
+void lprocfs_echo_init_vars(struct lprocfs_static_vars *lvars)
+{
+ lvars->module_vars = lprocfs_echo_module_vars;
+ lvars->obd_vars = lprocfs_echo_obd_vars;
+}
+#endif /* CONFIG_PROC_FS */
diff --git a/drivers/staging/lustre/lustre/osc/Makefile b/drivers/staging/lustre/lustre/osc/Makefile
new file mode 100644
index 000000000..54927fba4
--- /dev/null
+++ b/drivers/staging/lustre/lustre/osc/Makefile
@@ -0,0 +1,4 @@
+obj-$(CONFIG_LUSTRE_FS) += osc.o
+osc-y := osc_request.o osc_dev.o osc_object.o \
+ osc_page.o osc_lock.o osc_io.o osc_quota.o osc_cache.o
+osc-$(CONFIG_PROC_FS) += lproc_osc.o
diff --git a/drivers/staging/lustre/lustre/osc/lproc_osc.c b/drivers/staging/lustre/lustre/osc/lproc_osc.c
new file mode 100644
index 000000000..15a662098
--- /dev/null
+++ b/drivers/staging/lustre/lustre/osc/lproc_osc.c
@@ -0,0 +1,751 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2002, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2011, 2012, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ */
+#define DEBUG_SUBSYSTEM S_CLASS
+
+#include <linux/statfs.h>
+#include "../include/obd_cksum.h"
+#include "../include/obd_class.h"
+#include "../include/lprocfs_status.h"
+#include <linux/seq_file.h>
+#include "osc_internal.h"
+
+static int osc_active_seq_show(struct seq_file *m, void *v)
+{
+ struct obd_device *dev = m->private;
+
+ LPROCFS_CLIMP_CHECK(dev);
+ seq_printf(m, "%d\n", !dev->u.cli.cl_import->imp_deactive);
+ LPROCFS_CLIMP_EXIT(dev);
+
+ return 0;
+}
+
+static ssize_t osc_active_seq_write(struct file *file,
+ const char __user *buffer,
+ size_t count, loff_t *off)
+{
+ struct obd_device *dev = ((struct seq_file *)file->private_data)->private;
+ int val, rc;
+
+ rc = lprocfs_write_helper(buffer, count, &val);
+ if (rc)
+ return rc;
+ if (val < 0 || val > 1)
+ return -ERANGE;
+
+ /* opposite senses */
+ if (dev->u.cli.cl_import->imp_deactive == val)
+ rc = ptlrpc_set_import_active(dev->u.cli.cl_import, val);
+ else
+ CDEBUG(D_CONFIG, "activate %d: ignoring repeat request\n", val);
+
+ return count;
+}
+LPROC_SEQ_FOPS(osc_active);
+
+static int osc_max_rpcs_in_flight_seq_show(struct seq_file *m, void *v)
+{
+ struct obd_device *dev = m->private;
+ struct client_obd *cli = &dev->u.cli;
+
+ client_obd_list_lock(&cli->cl_loi_list_lock);
+ seq_printf(m, "%u\n", cli->cl_max_rpcs_in_flight);
+ client_obd_list_unlock(&cli->cl_loi_list_lock);
+
+ return 0;
+}
+
+static ssize_t osc_max_rpcs_in_flight_seq_write(struct file *file,
+ const char __user *buffer,
+ size_t count, loff_t *off)
+{
+ struct obd_device *dev = ((struct seq_file *)file->private_data)->private;
+ struct client_obd *cli = &dev->u.cli;
+ struct ptlrpc_request_pool *pool = cli->cl_import->imp_rq_pool;
+ int val, rc;
+
+ rc = lprocfs_write_helper(buffer, count, &val);
+ if (rc)
+ return rc;
+
+ if (val < 1 || val > OSC_MAX_RIF_MAX)
+ return -ERANGE;
+
+ LPROCFS_CLIMP_CHECK(dev);
+ if (pool && val > cli->cl_max_rpcs_in_flight)
+ pool->prp_populate(pool, val-cli->cl_max_rpcs_in_flight);
+
+ client_obd_list_lock(&cli->cl_loi_list_lock);
+ cli->cl_max_rpcs_in_flight = val;
+ client_obd_list_unlock(&cli->cl_loi_list_lock);
+
+ LPROCFS_CLIMP_EXIT(dev);
+ return count;
+}
+LPROC_SEQ_FOPS(osc_max_rpcs_in_flight);
+
+static int osc_max_dirty_mb_seq_show(struct seq_file *m, void *v)
+{
+ struct obd_device *dev = m->private;
+ struct client_obd *cli = &dev->u.cli;
+ long val;
+ int mult;
+
+ client_obd_list_lock(&cli->cl_loi_list_lock);
+ val = cli->cl_dirty_max;
+ client_obd_list_unlock(&cli->cl_loi_list_lock);
+
+ mult = 1 << 20;
+ return lprocfs_seq_read_frac_helper(m, val, mult);
+}
+
+static ssize_t osc_max_dirty_mb_seq_write(struct file *file,
+ const char __user *buffer,
+ size_t count, loff_t *off)
+{
+ struct obd_device *dev = ((struct seq_file *)file->private_data)->private;
+ struct client_obd *cli = &dev->u.cli;
+ int pages_number, mult, rc;
+
+ mult = 1 << (20 - PAGE_CACHE_SHIFT);
+ rc = lprocfs_write_frac_helper(buffer, count, &pages_number, mult);
+ if (rc)
+ return rc;
+
+ if (pages_number <= 0 ||
+ pages_number > OSC_MAX_DIRTY_MB_MAX << (20 - PAGE_CACHE_SHIFT) ||
+ pages_number > totalram_pages / 4) /* 1/4 of RAM */
+ return -ERANGE;
+
+ client_obd_list_lock(&cli->cl_loi_list_lock);
+ cli->cl_dirty_max = (u32)(pages_number << PAGE_CACHE_SHIFT);
+ osc_wake_cache_waiters(cli);
+ client_obd_list_unlock(&cli->cl_loi_list_lock);
+
+ return count;
+}
+LPROC_SEQ_FOPS(osc_max_dirty_mb);
+
+static int osc_cached_mb_seq_show(struct seq_file *m, void *v)
+{
+ struct obd_device *dev = m->private;
+ struct client_obd *cli = &dev->u.cli;
+ int shift = 20 - PAGE_CACHE_SHIFT;
+
+ seq_printf(m,
+ "used_mb: %d\n"
+ "busy_cnt: %d\n",
+ (atomic_read(&cli->cl_lru_in_list) +
+ atomic_read(&cli->cl_lru_busy)) >> shift,
+ atomic_read(&cli->cl_lru_busy));
+
+ return 0;
+}
+
+/* shrink the number of caching pages to a specific number */
+static ssize_t osc_cached_mb_seq_write(struct file *file,
+ const char __user *buffer,
+ size_t count, loff_t *off)
+{
+ struct obd_device *dev = ((struct seq_file *)file->private_data)->private;
+ struct client_obd *cli = &dev->u.cli;
+ int pages_number, mult, rc;
+ char kernbuf[128];
+
+ if (count >= sizeof(kernbuf))
+ return -EINVAL;
+
+ if (copy_from_user(kernbuf, buffer, count))
+ return -EFAULT;
+ kernbuf[count] = 0;
+
+ mult = 1 << (20 - PAGE_CACHE_SHIFT);
+ buffer += lprocfs_find_named_value(kernbuf, "used_mb:", &count) -
+ kernbuf;
+ rc = lprocfs_write_frac_helper(buffer, count, &pages_number, mult);
+ if (rc)
+ return rc;
+
+ if (pages_number < 0)
+ return -ERANGE;
+
+ rc = atomic_read(&cli->cl_lru_in_list) - pages_number;
+ if (rc > 0)
+ (void)osc_lru_shrink(cli, rc);
+
+ return count;
+}
+LPROC_SEQ_FOPS(osc_cached_mb);
+
+static int osc_cur_dirty_bytes_seq_show(struct seq_file *m, void *v)
+{
+ struct obd_device *dev = m->private;
+ struct client_obd *cli = &dev->u.cli;
+
+ client_obd_list_lock(&cli->cl_loi_list_lock);
+ seq_printf(m, "%lu\n", cli->cl_dirty);
+ client_obd_list_unlock(&cli->cl_loi_list_lock);
+
+ return 0;
+}
+LPROC_SEQ_FOPS_RO(osc_cur_dirty_bytes);
+
+static int osc_cur_grant_bytes_seq_show(struct seq_file *m, void *v)
+{
+ struct obd_device *dev = m->private;
+ struct client_obd *cli = &dev->u.cli;
+
+ client_obd_list_lock(&cli->cl_loi_list_lock);
+ seq_printf(m, "%lu\n", cli->cl_avail_grant);
+ client_obd_list_unlock(&cli->cl_loi_list_lock);
+
+ return 0;
+}
+
+static ssize_t osc_cur_grant_bytes_seq_write(struct file *file,
+ const char __user *buffer,
+ size_t count, loff_t *off)
+{
+ struct obd_device *obd = ((struct seq_file *)file->private_data)->private;
+ struct client_obd *cli = &obd->u.cli;
+ int rc;
+ __u64 val;
+
+ if (obd == NULL)
+ return 0;
+
+ rc = lprocfs_write_u64_helper(buffer, count, &val);
+ if (rc)
+ return rc;
+
+ /* this is only for shrinking grant */
+ client_obd_list_lock(&cli->cl_loi_list_lock);
+ if (val >= cli->cl_avail_grant) {
+ client_obd_list_unlock(&cli->cl_loi_list_lock);
+ return 0;
+ }
+ client_obd_list_unlock(&cli->cl_loi_list_lock);
+
+ LPROCFS_CLIMP_CHECK(obd);
+ if (cli->cl_import->imp_state == LUSTRE_IMP_FULL)
+ rc = osc_shrink_grant_to_target(cli, val);
+ LPROCFS_CLIMP_EXIT(obd);
+ if (rc)
+ return rc;
+ return count;
+}
+LPROC_SEQ_FOPS(osc_cur_grant_bytes);
+
+static int osc_cur_lost_grant_bytes_seq_show(struct seq_file *m, void *v)
+{
+ struct obd_device *dev = m->private;
+ struct client_obd *cli = &dev->u.cli;
+
+ client_obd_list_lock(&cli->cl_loi_list_lock);
+ seq_printf(m, "%lu\n", cli->cl_lost_grant);
+ client_obd_list_unlock(&cli->cl_loi_list_lock);
+
+ return 0;
+}
+LPROC_SEQ_FOPS_RO(osc_cur_lost_grant_bytes);
+
+static int osc_grant_shrink_interval_seq_show(struct seq_file *m, void *v)
+{
+ struct obd_device *obd = m->private;
+
+ if (obd == NULL)
+ return 0;
+ seq_printf(m, "%d\n", obd->u.cli.cl_grant_shrink_interval);
+ return 0;
+}
+
+static ssize_t osc_grant_shrink_interval_seq_write(struct file *file,
+ const char __user *buffer,
+ size_t count, loff_t *off)
+{
+ struct obd_device *obd = ((struct seq_file *)file->private_data)->private;
+ int val, rc;
+
+ if (obd == NULL)
+ return 0;
+
+ rc = lprocfs_write_helper(buffer, count, &val);
+ if (rc)
+ return rc;
+
+ if (val <= 0)
+ return -ERANGE;
+
+ obd->u.cli.cl_grant_shrink_interval = val;
+
+ return count;
+}
+LPROC_SEQ_FOPS(osc_grant_shrink_interval);
+
+static int osc_checksum_seq_show(struct seq_file *m, void *v)
+{
+ struct obd_device *obd = m->private;
+
+ if (obd == NULL)
+ return 0;
+
+ seq_printf(m, "%d\n", obd->u.cli.cl_checksum ? 1 : 0);
+ return 0;
+}
+
+static ssize_t osc_checksum_seq_write(struct file *file,
+ const char __user *buffer,
+ size_t count, loff_t *off)
+{
+ struct obd_device *obd = ((struct seq_file *)file->private_data)->private;
+ int val, rc;
+
+ if (obd == NULL)
+ return 0;
+
+ rc = lprocfs_write_helper(buffer, count, &val);
+ if (rc)
+ return rc;
+
+ obd->u.cli.cl_checksum = (val ? 1 : 0);
+
+ return count;
+}
+LPROC_SEQ_FOPS(osc_checksum);
+
+static int osc_checksum_type_seq_show(struct seq_file *m, void *v)
+{
+ struct obd_device *obd = m->private;
+ int i;
+ DECLARE_CKSUM_NAME;
+
+ if (obd == NULL)
+ return 0;
+
+ for (i = 0; i < ARRAY_SIZE(cksum_name); i++) {
+ if (((1 << i) & obd->u.cli.cl_supp_cksum_types) == 0)
+ continue;
+ if (obd->u.cli.cl_cksum_type == (1 << i))
+ seq_printf(m, "[%s] ", cksum_name[i]);
+ else
+ seq_printf(m, "%s ", cksum_name[i]);
+ }
+ seq_putc(m, '\n');
+ return 0;
+}
+
+static ssize_t osc_checksum_type_seq_write(struct file *file,
+ const char __user *buffer,
+ size_t count, loff_t *off)
+{
+ struct obd_device *obd = ((struct seq_file *)file->private_data)->private;
+ int i;
+ DECLARE_CKSUM_NAME;
+ char kernbuf[10];
+
+ if (obd == NULL)
+ return 0;
+
+ if (count > sizeof(kernbuf) - 1)
+ return -EINVAL;
+ if (copy_from_user(kernbuf, buffer, count))
+ return -EFAULT;
+ if (count > 0 && kernbuf[count - 1] == '\n')
+ kernbuf[count - 1] = '\0';
+ else
+ kernbuf[count] = '\0';
+
+ for (i = 0; i < ARRAY_SIZE(cksum_name); i++) {
+ if (((1 << i) & obd->u.cli.cl_supp_cksum_types) == 0)
+ continue;
+ if (!strcmp(kernbuf, cksum_name[i])) {
+ obd->u.cli.cl_cksum_type = 1 << i;
+ return count;
+ }
+ }
+ return -EINVAL;
+}
+LPROC_SEQ_FOPS(osc_checksum_type);
+
+static int osc_resend_count_seq_show(struct seq_file *m, void *v)
+{
+ struct obd_device *obd = m->private;
+
+ seq_printf(m, "%u\n", atomic_read(&obd->u.cli.cl_resends));
+ return 0;
+}
+
+static ssize_t osc_resend_count_seq_write(struct file *file,
+ const char __user *buffer,
+ size_t count, loff_t *off)
+{
+ struct obd_device *obd = ((struct seq_file *)file->private_data)->private;
+ int val, rc;
+
+ rc = lprocfs_write_helper(buffer, count, &val);
+ if (rc)
+ return rc;
+
+ if (val < 0)
+ return -EINVAL;
+
+ atomic_set(&obd->u.cli.cl_resends, val);
+
+ return count;
+}
+LPROC_SEQ_FOPS(osc_resend_count);
+
+static int osc_contention_seconds_seq_show(struct seq_file *m, void *v)
+{
+ struct obd_device *obd = m->private;
+ struct osc_device *od = obd2osc_dev(obd);
+
+ seq_printf(m, "%u\n", od->od_contention_time);
+ return 0;
+}
+
+static ssize_t osc_contention_seconds_seq_write(struct file *file,
+ const char __user *buffer,
+ size_t count, loff_t *off)
+{
+ struct obd_device *obd = ((struct seq_file *)file->private_data)->private;
+ struct osc_device *od = obd2osc_dev(obd);
+
+ return lprocfs_write_helper(buffer, count, &od->od_contention_time) ?:
+ count;
+}
+LPROC_SEQ_FOPS(osc_contention_seconds);
+
+static int osc_lockless_truncate_seq_show(struct seq_file *m, void *v)
+{
+ struct obd_device *obd = m->private;
+ struct osc_device *od = obd2osc_dev(obd);
+
+ seq_printf(m, "%u\n", od->od_lockless_truncate);
+ return 0;
+}
+
+static ssize_t osc_lockless_truncate_seq_write(struct file *file,
+ const char __user *buffer,
+ size_t count, loff_t *off)
+{
+ struct obd_device *obd = ((struct seq_file *)file->private_data)->private;
+ struct osc_device *od = obd2osc_dev(obd);
+
+ return lprocfs_write_helper(buffer, count, &od->od_lockless_truncate) ?:
+ count;
+}
+LPROC_SEQ_FOPS(osc_lockless_truncate);
+
+static int osc_destroys_in_flight_seq_show(struct seq_file *m, void *v)
+{
+ struct obd_device *obd = m->private;
+
+ seq_printf(m, "%u\n", atomic_read(&obd->u.cli.cl_destroy_in_flight));
+ return 0;
+}
+LPROC_SEQ_FOPS_RO(osc_destroys_in_flight);
+
+static int osc_obd_max_pages_per_rpc_seq_show(struct seq_file *m, void *v)
+{
+ return lprocfs_obd_rd_max_pages_per_rpc(m, m->private);
+}
+
+static ssize_t osc_obd_max_pages_per_rpc_seq_write(struct file *file,
+ const char __user *buffer,
+ size_t count, loff_t *off)
+{
+ struct obd_device *dev = ((struct seq_file *)file->private_data)->private;
+ struct client_obd *cli = &dev->u.cli;
+ struct obd_connect_data *ocd = &cli->cl_import->imp_connect_data;
+ int chunk_mask, rc;
+ __u64 val;
+
+ rc = lprocfs_write_u64_helper(buffer, count, &val);
+ if (rc)
+ return rc;
+
+ /* if the max_pages is specified in bytes, convert to pages */
+ if (val >= ONE_MB_BRW_SIZE)
+ val >>= PAGE_CACHE_SHIFT;
+
+ LPROCFS_CLIMP_CHECK(dev);
+
+ chunk_mask = ~((1 << (cli->cl_chunkbits - PAGE_CACHE_SHIFT)) - 1);
+ /* max_pages_per_rpc must be chunk aligned */
+ val = (val + ~chunk_mask) & chunk_mask;
+ if (val == 0 || val > ocd->ocd_brw_size >> PAGE_CACHE_SHIFT) {
+ LPROCFS_CLIMP_EXIT(dev);
+ return -ERANGE;
+ }
+ client_obd_list_lock(&cli->cl_loi_list_lock);
+ cli->cl_max_pages_per_rpc = val;
+ client_obd_list_unlock(&cli->cl_loi_list_lock);
+
+ LPROCFS_CLIMP_EXIT(dev);
+ return count;
+}
+LPROC_SEQ_FOPS(osc_obd_max_pages_per_rpc);
+
+LPROC_SEQ_FOPS_RO_TYPE(osc, uuid);
+LPROC_SEQ_FOPS_RO_TYPE(osc, connect_flags);
+LPROC_SEQ_FOPS_RO_TYPE(osc, blksize);
+LPROC_SEQ_FOPS_RO_TYPE(osc, kbytestotal);
+LPROC_SEQ_FOPS_RO_TYPE(osc, kbytesfree);
+LPROC_SEQ_FOPS_RO_TYPE(osc, kbytesavail);
+LPROC_SEQ_FOPS_RO_TYPE(osc, filestotal);
+LPROC_SEQ_FOPS_RO_TYPE(osc, filesfree);
+LPROC_SEQ_FOPS_RO_TYPE(osc, server_uuid);
+LPROC_SEQ_FOPS_RO_TYPE(osc, conn_uuid);
+LPROC_SEQ_FOPS_RO_TYPE(osc, timeouts);
+LPROC_SEQ_FOPS_RO_TYPE(osc, state);
+
+LPROC_SEQ_FOPS_WR_ONLY(osc, ping);
+
+LPROC_SEQ_FOPS_RW_TYPE(osc, import);
+LPROC_SEQ_FOPS_RW_TYPE(osc, pinger_recov);
+
+static struct lprocfs_vars lprocfs_osc_obd_vars[] = {
+ { "uuid", &osc_uuid_fops, NULL, 0 },
+ { "ping", &osc_ping_fops, NULL, 0222 },
+ { "connect_flags", &osc_connect_flags_fops, NULL, 0 },
+ { "blocksize", &osc_blksize_fops, NULL, 0 },
+ { "kbytestotal", &osc_kbytestotal_fops, NULL, 0 },
+ { "kbytesfree", &osc_kbytesfree_fops, NULL, 0 },
+ { "kbytesavail", &osc_kbytesavail_fops, NULL, 0 },
+ { "filestotal", &osc_filestotal_fops, NULL, 0 },
+ { "filesfree", &osc_filesfree_fops, NULL, 0 },
+ /*{ "filegroups", lprocfs_rd_filegroups, NULL, 0 },*/
+ { "ost_server_uuid", &osc_server_uuid_fops, NULL, 0 },
+ { "ost_conn_uuid", &osc_conn_uuid_fops, NULL, 0 },
+ { "active", &osc_active_fops, NULL },
+ { "max_pages_per_rpc", &osc_obd_max_pages_per_rpc_fops, NULL },
+ { "max_rpcs_in_flight", &osc_max_rpcs_in_flight_fops, NULL },
+ { "destroys_in_flight", &osc_destroys_in_flight_fops, NULL, 0 },
+ { "max_dirty_mb", &osc_max_dirty_mb_fops, NULL },
+ { "osc_cached_mb", &osc_cached_mb_fops, NULL },
+ { "cur_dirty_bytes", &osc_cur_dirty_bytes_fops, NULL, 0 },
+ { "cur_grant_bytes", &osc_cur_grant_bytes_fops, NULL },
+ { "cur_lost_grant_bytes", &osc_cur_lost_grant_bytes_fops, NULL, 0},
+ { "grant_shrink_interval", &osc_grant_shrink_interval_fops, NULL },
+ { "checksums", &osc_checksum_fops, NULL },
+ { "checksum_type", &osc_checksum_type_fops, NULL },
+ { "resend_count", &osc_resend_count_fops, NULL},
+ { "timeouts", &osc_timeouts_fops, NULL, 0 },
+ { "contention_seconds", &osc_contention_seconds_fops, NULL },
+ { "lockless_truncate", &osc_lockless_truncate_fops, NULL },
+ { "import", &osc_import_fops, NULL },
+ { "state", &osc_state_fops, NULL, 0 },
+ { "pinger_recov", &osc_pinger_recov_fops, NULL },
+ { NULL }
+};
+
+LPROC_SEQ_FOPS_RO_TYPE(osc, numrefs);
+static struct lprocfs_vars lprocfs_osc_module_vars[] = {
+ { "num_refs", &osc_numrefs_fops, NULL, 0 },
+ { NULL }
+};
+
+#define pct(a, b) (b ? a * 100 / b : 0)
+
+static int osc_rpc_stats_seq_show(struct seq_file *seq, void *v)
+{
+ struct timeval now;
+ struct obd_device *dev = seq->private;
+ struct client_obd *cli = &dev->u.cli;
+ unsigned long read_tot = 0, write_tot = 0, read_cum, write_cum;
+ int i;
+
+ do_gettimeofday(&now);
+
+ client_obd_list_lock(&cli->cl_loi_list_lock);
+
+ seq_printf(seq, "snapshot_time: %lu.%lu (secs.usecs)\n",
+ now.tv_sec, (unsigned long)now.tv_usec);
+ seq_printf(seq, "read RPCs in flight: %d\n",
+ cli->cl_r_in_flight);
+ seq_printf(seq, "write RPCs in flight: %d\n",
+ cli->cl_w_in_flight);
+ seq_printf(seq, "pending write pages: %d\n",
+ atomic_read(&cli->cl_pending_w_pages));
+ seq_printf(seq, "pending read pages: %d\n",
+ atomic_read(&cli->cl_pending_r_pages));
+
+ seq_puts(seq, "\n\t\t\tread\t\t\twrite\n");
+ seq_puts(seq, "pages per rpc rpcs % cum % |");
+ seq_puts(seq, " rpcs % cum %\n");
+
+ read_tot = lprocfs_oh_sum(&cli->cl_read_page_hist);
+ write_tot = lprocfs_oh_sum(&cli->cl_write_page_hist);
+
+ read_cum = 0;
+ write_cum = 0;
+ for (i = 0; i < OBD_HIST_MAX; i++) {
+ unsigned long r = cli->cl_read_page_hist.oh_buckets[i];
+ unsigned long w = cli->cl_write_page_hist.oh_buckets[i];
+ read_cum += r;
+ write_cum += w;
+ seq_printf(seq, "%d:\t\t%10lu %3lu %3lu | %10lu %3lu %3lu\n",
+ 1 << i, r, pct(r, read_tot),
+ pct(read_cum, read_tot), w,
+ pct(w, write_tot),
+ pct(write_cum, write_tot));
+ if (read_cum == read_tot && write_cum == write_tot)
+ break;
+ }
+
+ seq_puts(seq, "\n\t\t\tread\t\t\twrite\n");
+ seq_puts(seq, "rpcs in flight rpcs % cum % |");
+ seq_puts(seq, " rpcs % cum %\n");
+
+ read_tot = lprocfs_oh_sum(&cli->cl_read_rpc_hist);
+ write_tot = lprocfs_oh_sum(&cli->cl_write_rpc_hist);
+
+ read_cum = 0;
+ write_cum = 0;
+ for (i = 0; i < OBD_HIST_MAX; i++) {
+ unsigned long r = cli->cl_read_rpc_hist.oh_buckets[i];
+ unsigned long w = cli->cl_write_rpc_hist.oh_buckets[i];
+ read_cum += r;
+ write_cum += w;
+ seq_printf(seq, "%d:\t\t%10lu %3lu %3lu | %10lu %3lu %3lu\n",
+ i, r, pct(r, read_tot),
+ pct(read_cum, read_tot), w,
+ pct(w, write_tot),
+ pct(write_cum, write_tot));
+ if (read_cum == read_tot && write_cum == write_tot)
+ break;
+ }
+
+ seq_puts(seq, "\n\t\t\tread\t\t\twrite\n");
+ seq_puts(seq, "offset rpcs % cum % |");
+ seq_puts(seq, " rpcs % cum %\n");
+
+ read_tot = lprocfs_oh_sum(&cli->cl_read_offset_hist);
+ write_tot = lprocfs_oh_sum(&cli->cl_write_offset_hist);
+
+ read_cum = 0;
+ write_cum = 0;
+ for (i = 0; i < OBD_HIST_MAX; i++) {
+ unsigned long r = cli->cl_read_offset_hist.oh_buckets[i];
+ unsigned long w = cli->cl_write_offset_hist.oh_buckets[i];
+ read_cum += r;
+ write_cum += w;
+ seq_printf(seq, "%d:\t\t%10lu %3lu %3lu | %10lu %3lu %3lu\n",
+ (i == 0) ? 0 : 1 << (i - 1),
+ r, pct(r, read_tot), pct(read_cum, read_tot),
+ w, pct(w, write_tot), pct(write_cum, write_tot));
+ if (read_cum == read_tot && write_cum == write_tot)
+ break;
+ }
+
+ client_obd_list_unlock(&cli->cl_loi_list_lock);
+
+ return 0;
+}
+#undef pct
+
+static ssize_t osc_rpc_stats_seq_write(struct file *file,
+ const char __user *buf,
+ size_t len, loff_t *off)
+{
+ struct seq_file *seq = file->private_data;
+ struct obd_device *dev = seq->private;
+ struct client_obd *cli = &dev->u.cli;
+
+ lprocfs_oh_clear(&cli->cl_read_rpc_hist);
+ lprocfs_oh_clear(&cli->cl_write_rpc_hist);
+ lprocfs_oh_clear(&cli->cl_read_page_hist);
+ lprocfs_oh_clear(&cli->cl_write_page_hist);
+ lprocfs_oh_clear(&cli->cl_read_offset_hist);
+ lprocfs_oh_clear(&cli->cl_write_offset_hist);
+
+ return len;
+}
+
+LPROC_SEQ_FOPS(osc_rpc_stats);
+
+static int osc_stats_seq_show(struct seq_file *seq, void *v)
+{
+ struct timeval now;
+ struct obd_device *dev = seq->private;
+ struct osc_stats *stats = &obd2osc_dev(dev)->od_stats;
+
+ do_gettimeofday(&now);
+
+ seq_printf(seq, "snapshot_time: %lu.%lu (secs.usecs)\n",
+ now.tv_sec, (unsigned long)now.tv_usec);
+ seq_printf(seq, "lockless_write_bytes\t\t%llu\n",
+ stats->os_lockless_writes);
+ seq_printf(seq, "lockless_read_bytes\t\t%llu\n",
+ stats->os_lockless_reads);
+ seq_printf(seq, "lockless_truncate\t\t%llu\n",
+ stats->os_lockless_truncates);
+ return 0;
+}
+
+static ssize_t osc_stats_seq_write(struct file *file,
+ const char __user *buf,
+ size_t len, loff_t *off)
+{
+ struct seq_file *seq = file->private_data;
+ struct obd_device *dev = seq->private;
+ struct osc_stats *stats = &obd2osc_dev(dev)->od_stats;
+
+ memset(stats, 0, sizeof(*stats));
+ return len;
+}
+
+LPROC_SEQ_FOPS(osc_stats);
+
+int lproc_osc_attach_seqstat(struct obd_device *dev)
+{
+ int rc;
+
+ rc = lprocfs_seq_create(dev->obd_proc_entry, "osc_stats", 0644,
+ &osc_stats_fops, dev);
+ if (rc == 0)
+ rc = lprocfs_obd_seq_create(dev, "rpc_stats", 0644,
+ &osc_rpc_stats_fops, dev);
+
+ return rc;
+}
+
+void lprocfs_osc_init_vars(struct lprocfs_static_vars *lvars)
+{
+ lvars->module_vars = lprocfs_osc_module_vars;
+ lvars->obd_vars = lprocfs_osc_obd_vars;
+}
diff --git a/drivers/staging/lustre/lustre/osc/osc_cache.c b/drivers/staging/lustre/lustre/osc/osc_cache.c
new file mode 100644
index 000000000..d44b3d4ff
--- /dev/null
+++ b/drivers/staging/lustre/lustre/osc/osc_cache.c
@@ -0,0 +1,2944 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2012, Intel Corporation.
+ *
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ *
+ * osc cache management.
+ *
+ * Author: Jinshan Xiong <jinshan.xiong@whamcloud.com>
+ */
+
+#define DEBUG_SUBSYSTEM S_OSC
+
+#include "osc_cl_internal.h"
+#include "osc_internal.h"
+
+static int extent_debug; /* set it to be true for more debug */
+
+static void osc_update_pending(struct osc_object *obj, int cmd, int delta);
+static int osc_extent_wait(const struct lu_env *env, struct osc_extent *ext,
+ int state);
+static void osc_ap_completion(const struct lu_env *env, struct client_obd *cli,
+ struct osc_async_page *oap, int sent, int rc);
+static int osc_make_ready(const struct lu_env *env, struct osc_async_page *oap,
+ int cmd);
+static int osc_refresh_count(const struct lu_env *env,
+ struct osc_async_page *oap, int cmd);
+static int osc_io_unplug_async(const struct lu_env *env,
+ struct client_obd *cli, struct osc_object *osc);
+static void osc_free_grant(struct client_obd *cli, unsigned int nr_pages,
+ unsigned int lost_grant);
+
+static void osc_extent_tree_dump0(int level, struct osc_object *obj,
+ const char *func, int line);
+#define osc_extent_tree_dump(lvl, obj) \
+ osc_extent_tree_dump0(lvl, obj, __func__, __LINE__)
+
+/** \addtogroup osc
+ * @{
+ */
+
+/* ------------------ osc extent ------------------ */
+static inline char *ext_flags(struct osc_extent *ext, char *flags)
+{
+ char *buf = flags;
+ *buf++ = ext->oe_rw ? 'r' : 'w';
+ if (ext->oe_intree)
+ *buf++ = 'i';
+ if (ext->oe_srvlock)
+ *buf++ = 's';
+ if (ext->oe_hp)
+ *buf++ = 'h';
+ if (ext->oe_urgent)
+ *buf++ = 'u';
+ if (ext->oe_memalloc)
+ *buf++ = 'm';
+ if (ext->oe_trunc_pending)
+ *buf++ = 't';
+ if (ext->oe_fsync_wait)
+ *buf++ = 'Y';
+ *buf = 0;
+ return flags;
+}
+
+static inline char list_empty_marker(struct list_head *list)
+{
+ return list_empty(list) ? '-' : '+';
+}
+
+#define EXTSTR "[%lu -> %lu/%lu]"
+#define EXTPARA(ext) (ext)->oe_start, (ext)->oe_end, (ext)->oe_max_end
+static const char *oes_strings[] = {
+ "inv", "active", "cache", "locking", "lockdone", "rpc", "trunc", NULL };
+
+#define OSC_EXTENT_DUMP(lvl, extent, fmt, ...) do { \
+ struct osc_extent *__ext = (extent); \
+ char __buf[16]; \
+ \
+ CDEBUG(lvl, \
+ "extent %p@{" EXTSTR ", " \
+ "[%d|%d|%c|%s|%s|%p], [%d|%d|%c|%c|%p|%u|%p]} " fmt, \
+ /* ----- extent part 0 ----- */ \
+ __ext, EXTPARA(__ext), \
+ /* ----- part 1 ----- */ \
+ atomic_read(&__ext->oe_refc), \
+ atomic_read(&__ext->oe_users), \
+ list_empty_marker(&__ext->oe_link), \
+ oes_strings[__ext->oe_state], ext_flags(__ext, __buf), \
+ __ext->oe_obj, \
+ /* ----- part 2 ----- */ \
+ __ext->oe_grants, __ext->oe_nr_pages, \
+ list_empty_marker(&__ext->oe_pages), \
+ waitqueue_active(&__ext->oe_waitq) ? '+' : '-', \
+ __ext->oe_osclock, __ext->oe_mppr, __ext->oe_owner, \
+ /* ----- part 4 ----- */ \
+ ## __VA_ARGS__); \
+} while (0)
+
+#undef EASSERTF
+#define EASSERTF(expr, ext, fmt, args...) do { \
+ if (!(expr)) { \
+ OSC_EXTENT_DUMP(D_ERROR, (ext), fmt, ##args); \
+ osc_extent_tree_dump(D_ERROR, (ext)->oe_obj); \
+ LASSERT(expr); \
+ } \
+} while (0)
+
+#undef EASSERT
+#define EASSERT(expr, ext) EASSERTF(expr, ext, "\n")
+
+static inline struct osc_extent *rb_extent(struct rb_node *n)
+{
+ if (n == NULL)
+ return NULL;
+
+ return container_of(n, struct osc_extent, oe_node);
+}
+
+static inline struct osc_extent *next_extent(struct osc_extent *ext)
+{
+ if (ext == NULL)
+ return NULL;
+
+ LASSERT(ext->oe_intree);
+ return rb_extent(rb_next(&ext->oe_node));
+}
+
+static inline struct osc_extent *prev_extent(struct osc_extent *ext)
+{
+ if (ext == NULL)
+ return NULL;
+
+ LASSERT(ext->oe_intree);
+ return rb_extent(rb_prev(&ext->oe_node));
+}
+
+static inline struct osc_extent *first_extent(struct osc_object *obj)
+{
+ return rb_extent(rb_first(&obj->oo_root));
+}
+
+/* object must be locked by caller. */
+static int osc_extent_sanity_check0(struct osc_extent *ext,
+ const char *func, const int line)
+{
+ struct osc_object *obj = ext->oe_obj;
+ struct osc_async_page *oap;
+ int page_count;
+ int rc = 0;
+
+ if (!osc_object_is_locked(obj)) {
+ rc = 9;
+ goto out;
+ }
+
+ if (ext->oe_state >= OES_STATE_MAX) {
+ rc = 10;
+ goto out;
+ }
+
+ if (atomic_read(&ext->oe_refc) <= 0) {
+ rc = 20;
+ goto out;
+ }
+
+ if (atomic_read(&ext->oe_refc) < atomic_read(&ext->oe_users)) {
+ rc = 30;
+ goto out;
+ }
+
+ switch (ext->oe_state) {
+ case OES_INV:
+ if (ext->oe_nr_pages > 0 || !list_empty(&ext->oe_pages))
+ rc = 35;
+ else
+ rc = 0;
+ goto out;
+ case OES_ACTIVE:
+ if (atomic_read(&ext->oe_users) == 0) {
+ rc = 40;
+ goto out;
+ }
+ if (ext->oe_hp) {
+ rc = 50;
+ goto out;
+ }
+ if (ext->oe_fsync_wait && !ext->oe_urgent) {
+ rc = 55;
+ goto out;
+ }
+ break;
+ case OES_CACHE:
+ if (ext->oe_grants == 0) {
+ rc = 60;
+ goto out;
+ }
+ if (ext->oe_fsync_wait && !ext->oe_urgent && !ext->oe_hp) {
+ rc = 65;
+ goto out;
+ }
+ default:
+ if (atomic_read(&ext->oe_users) > 0) {
+ rc = 70;
+ goto out;
+ }
+ }
+
+ if (ext->oe_max_end < ext->oe_end || ext->oe_end < ext->oe_start) {
+ rc = 80;
+ goto out;
+ }
+
+ if (ext->oe_osclock == NULL && ext->oe_grants > 0) {
+ rc = 90;
+ goto out;
+ }
+
+ if (ext->oe_osclock) {
+ struct cl_lock_descr *descr;
+ descr = &ext->oe_osclock->cll_descr;
+ if (!(descr->cld_start <= ext->oe_start &&
+ descr->cld_end >= ext->oe_max_end)) {
+ rc = 100;
+ goto out;
+ }
+ }
+
+ if (ext->oe_nr_pages > ext->oe_mppr) {
+ rc = 105;
+ goto out;
+ }
+
+ /* Do not verify page list if extent is in RPC. This is because an
+ * in-RPC extent is supposed to be exclusively accessible w/o lock. */
+ if (ext->oe_state > OES_CACHE) {
+ rc = 0;
+ goto out;
+ }
+
+ if (!extent_debug) {
+ rc = 0;
+ goto out;
+ }
+
+ page_count = 0;
+ list_for_each_entry(oap, &ext->oe_pages, oap_pending_item) {
+ pgoff_t index = oap2cl_page(oap)->cp_index;
+ ++page_count;
+ if (index > ext->oe_end || index < ext->oe_start) {
+ rc = 110;
+ goto out;
+ }
+ }
+ if (page_count != ext->oe_nr_pages) {
+ rc = 120;
+ goto out;
+ }
+
+out:
+ if (rc != 0)
+ OSC_EXTENT_DUMP(D_ERROR, ext,
+ "%s:%d sanity check %p failed with rc = %d\n",
+ func, line, ext, rc);
+ return rc;
+}
+
+#define sanity_check_nolock(ext) \
+ osc_extent_sanity_check0(ext, __func__, __LINE__)
+
+#define sanity_check(ext) ({ \
+ int __res; \
+ osc_object_lock((ext)->oe_obj); \
+ __res = sanity_check_nolock(ext); \
+ osc_object_unlock((ext)->oe_obj); \
+ __res; \
+})
+
+
+/**
+ * sanity check - to make sure there is no overlapped extent in the tree.
+ */
+static int osc_extent_is_overlapped(struct osc_object *obj,
+ struct osc_extent *ext)
+{
+ struct osc_extent *tmp;
+
+ LASSERT(osc_object_is_locked(obj));
+
+ if (!extent_debug)
+ return 0;
+
+ for (tmp = first_extent(obj); tmp != NULL; tmp = next_extent(tmp)) {
+ if (tmp == ext)
+ continue;
+ if (tmp->oe_end >= ext->oe_start &&
+ tmp->oe_start <= ext->oe_end)
+ return 1;
+ }
+ return 0;
+}
+
+static void osc_extent_state_set(struct osc_extent *ext, int state)
+{
+ LASSERT(osc_object_is_locked(ext->oe_obj));
+ LASSERT(state >= OES_INV && state < OES_STATE_MAX);
+
+ /* Never try to sanity check a state changing extent :-) */
+ /* LASSERT(sanity_check_nolock(ext) == 0); */
+
+ /* TODO: validate the state machine */
+ ext->oe_state = state;
+ wake_up_all(&ext->oe_waitq);
+}
+
+static struct osc_extent *osc_extent_alloc(struct osc_object *obj)
+{
+ struct osc_extent *ext;
+
+ OBD_SLAB_ALLOC_PTR_GFP(ext, osc_extent_kmem, GFP_IOFS);
+ if (ext == NULL)
+ return NULL;
+
+ RB_CLEAR_NODE(&ext->oe_node);
+ ext->oe_obj = obj;
+ atomic_set(&ext->oe_refc, 1);
+ atomic_set(&ext->oe_users, 0);
+ INIT_LIST_HEAD(&ext->oe_link);
+ ext->oe_state = OES_INV;
+ INIT_LIST_HEAD(&ext->oe_pages);
+ init_waitqueue_head(&ext->oe_waitq);
+ ext->oe_osclock = NULL;
+
+ return ext;
+}
+
+static void osc_extent_free(struct osc_extent *ext)
+{
+ OBD_SLAB_FREE_PTR(ext, osc_extent_kmem);
+}
+
+static struct osc_extent *osc_extent_get(struct osc_extent *ext)
+{
+ LASSERT(atomic_read(&ext->oe_refc) >= 0);
+ atomic_inc(&ext->oe_refc);
+ return ext;
+}
+
+static void osc_extent_put(const struct lu_env *env, struct osc_extent *ext)
+{
+ LASSERT(atomic_read(&ext->oe_refc) > 0);
+ if (atomic_dec_and_test(&ext->oe_refc)) {
+ LASSERT(list_empty(&ext->oe_link));
+ LASSERT(atomic_read(&ext->oe_users) == 0);
+ LASSERT(ext->oe_state == OES_INV);
+ LASSERT(!ext->oe_intree);
+
+ if (ext->oe_osclock) {
+ cl_lock_put(env, ext->oe_osclock);
+ ext->oe_osclock = NULL;
+ }
+ osc_extent_free(ext);
+ }
+}
+
+/**
+ * osc_extent_put_trust() is a special version of osc_extent_put() when
+ * it's known that the caller is not the last user. This is to address the
+ * problem of lacking of lu_env ;-).
+ */
+static void osc_extent_put_trust(struct osc_extent *ext)
+{
+ LASSERT(atomic_read(&ext->oe_refc) > 1);
+ LASSERT(osc_object_is_locked(ext->oe_obj));
+ atomic_dec(&ext->oe_refc);
+}
+
+/**
+ * Return the extent which includes pgoff @index, or return the greatest
+ * previous extent in the tree.
+ */
+static struct osc_extent *osc_extent_search(struct osc_object *obj,
+ pgoff_t index)
+{
+ struct rb_node *n = obj->oo_root.rb_node;
+ struct osc_extent *tmp, *p = NULL;
+
+ LASSERT(osc_object_is_locked(obj));
+ while (n != NULL) {
+ tmp = rb_extent(n);
+ if (index < tmp->oe_start) {
+ n = n->rb_left;
+ } else if (index > tmp->oe_end) {
+ p = rb_extent(n);
+ n = n->rb_right;
+ } else {
+ return tmp;
+ }
+ }
+ return p;
+}
+
+/*
+ * Return the extent covering @index, otherwise return NULL.
+ * caller must have held object lock.
+ */
+static struct osc_extent *osc_extent_lookup(struct osc_object *obj,
+ pgoff_t index)
+{
+ struct osc_extent *ext;
+
+ ext = osc_extent_search(obj, index);
+ if (ext != NULL && ext->oe_start <= index && index <= ext->oe_end)
+ return osc_extent_get(ext);
+ return NULL;
+}
+
+/* caller must have held object lock. */
+static void osc_extent_insert(struct osc_object *obj, struct osc_extent *ext)
+{
+ struct rb_node **n = &obj->oo_root.rb_node;
+ struct rb_node *parent = NULL;
+ struct osc_extent *tmp;
+
+ LASSERT(ext->oe_intree == 0);
+ LASSERT(ext->oe_obj == obj);
+ LASSERT(osc_object_is_locked(obj));
+ while (*n != NULL) {
+ tmp = rb_extent(*n);
+ parent = *n;
+
+ if (ext->oe_end < tmp->oe_start)
+ n = &(*n)->rb_left;
+ else if (ext->oe_start > tmp->oe_end)
+ n = &(*n)->rb_right;
+ else
+ EASSERTF(0, tmp, EXTSTR, EXTPARA(ext));
+ }
+ rb_link_node(&ext->oe_node, parent, n);
+ rb_insert_color(&ext->oe_node, &obj->oo_root);
+ osc_extent_get(ext);
+ ext->oe_intree = 1;
+}
+
+/* caller must have held object lock. */
+static void osc_extent_erase(struct osc_extent *ext)
+{
+ struct osc_object *obj = ext->oe_obj;
+ LASSERT(osc_object_is_locked(obj));
+ if (ext->oe_intree) {
+ rb_erase(&ext->oe_node, &obj->oo_root);
+ ext->oe_intree = 0;
+ /* rbtree held a refcount */
+ osc_extent_put_trust(ext);
+ }
+}
+
+static struct osc_extent *osc_extent_hold(struct osc_extent *ext)
+{
+ struct osc_object *obj = ext->oe_obj;
+
+ LASSERT(osc_object_is_locked(obj));
+ LASSERT(ext->oe_state == OES_ACTIVE || ext->oe_state == OES_CACHE);
+ if (ext->oe_state == OES_CACHE) {
+ osc_extent_state_set(ext, OES_ACTIVE);
+ osc_update_pending(obj, OBD_BRW_WRITE, -ext->oe_nr_pages);
+ }
+ atomic_inc(&ext->oe_users);
+ list_del_init(&ext->oe_link);
+ return osc_extent_get(ext);
+}
+
+static void __osc_extent_remove(struct osc_extent *ext)
+{
+ LASSERT(osc_object_is_locked(ext->oe_obj));
+ LASSERT(list_empty(&ext->oe_pages));
+ osc_extent_erase(ext);
+ list_del_init(&ext->oe_link);
+ osc_extent_state_set(ext, OES_INV);
+ OSC_EXTENT_DUMP(D_CACHE, ext, "destroyed.\n");
+}
+
+static void osc_extent_remove(struct osc_extent *ext)
+{
+ struct osc_object *obj = ext->oe_obj;
+
+ osc_object_lock(obj);
+ __osc_extent_remove(ext);
+ osc_object_unlock(obj);
+}
+
+/**
+ * This function is used to merge extents to get better performance. It checks
+ * if @cur and @victim are contiguous at chunk level.
+ */
+static int osc_extent_merge(const struct lu_env *env, struct osc_extent *cur,
+ struct osc_extent *victim)
+{
+ struct osc_object *obj = cur->oe_obj;
+ pgoff_t chunk_start;
+ pgoff_t chunk_end;
+ int ppc_bits;
+
+ LASSERT(cur->oe_state == OES_CACHE);
+ LASSERT(osc_object_is_locked(obj));
+ if (victim == NULL)
+ return -EINVAL;
+
+ if (victim->oe_state != OES_CACHE || victim->oe_fsync_wait)
+ return -EBUSY;
+
+ if (cur->oe_max_end != victim->oe_max_end)
+ return -ERANGE;
+
+ LASSERT(cur->oe_osclock == victim->oe_osclock);
+ ppc_bits = osc_cli(obj)->cl_chunkbits - PAGE_CACHE_SHIFT;
+ chunk_start = cur->oe_start >> ppc_bits;
+ chunk_end = cur->oe_end >> ppc_bits;
+ if (chunk_start != (victim->oe_end >> ppc_bits) + 1 &&
+ chunk_end + 1 != victim->oe_start >> ppc_bits)
+ return -ERANGE;
+
+ OSC_EXTENT_DUMP(D_CACHE, victim, "will be merged by %p.\n", cur);
+
+ cur->oe_start = min(cur->oe_start, victim->oe_start);
+ cur->oe_end = max(cur->oe_end, victim->oe_end);
+ cur->oe_grants += victim->oe_grants;
+ cur->oe_nr_pages += victim->oe_nr_pages;
+ /* only the following bits are needed to merge */
+ cur->oe_urgent |= victim->oe_urgent;
+ cur->oe_memalloc |= victim->oe_memalloc;
+ list_splice_init(&victim->oe_pages, &cur->oe_pages);
+ list_del_init(&victim->oe_link);
+ victim->oe_nr_pages = 0;
+
+ osc_extent_get(victim);
+ __osc_extent_remove(victim);
+ osc_extent_put(env, victim);
+
+ OSC_EXTENT_DUMP(D_CACHE, cur, "after merging %p.\n", victim);
+ return 0;
+}
+
+/**
+ * Drop user count of osc_extent, and unplug IO asynchronously.
+ */
+void osc_extent_release(const struct lu_env *env, struct osc_extent *ext)
+{
+ struct osc_object *obj = ext->oe_obj;
+
+ LASSERT(atomic_read(&ext->oe_users) > 0);
+ LASSERT(sanity_check(ext) == 0);
+ LASSERT(ext->oe_grants > 0);
+
+ if (atomic_dec_and_lock(&ext->oe_users, &obj->oo_lock)) {
+ LASSERT(ext->oe_state == OES_ACTIVE);
+ if (ext->oe_trunc_pending) {
+ /* a truncate process is waiting for this extent.
+ * This may happen due to a race, check
+ * osc_cache_truncate_start(). */
+ osc_extent_state_set(ext, OES_TRUNC);
+ ext->oe_trunc_pending = 0;
+ } else {
+ osc_extent_state_set(ext, OES_CACHE);
+ osc_update_pending(obj, OBD_BRW_WRITE,
+ ext->oe_nr_pages);
+
+ /* try to merge the previous and next extent. */
+ osc_extent_merge(env, ext, prev_extent(ext));
+ osc_extent_merge(env, ext, next_extent(ext));
+
+ if (ext->oe_urgent)
+ list_move_tail(&ext->oe_link,
+ &obj->oo_urgent_exts);
+ }
+ osc_object_unlock(obj);
+
+ osc_io_unplug_async(env, osc_cli(obj), obj);
+ }
+ osc_extent_put(env, ext);
+}
+
+static inline int overlapped(struct osc_extent *ex1, struct osc_extent *ex2)
+{
+ return !(ex1->oe_end < ex2->oe_start || ex2->oe_end < ex1->oe_start);
+}
+
+/**
+ * Find or create an extent which includes @index, core function to manage
+ * extent tree.
+ */
+struct osc_extent *osc_extent_find(const struct lu_env *env,
+ struct osc_object *obj, pgoff_t index,
+ int *grants)
+
+{
+ struct client_obd *cli = osc_cli(obj);
+ struct cl_lock *lock;
+ struct osc_extent *cur;
+ struct osc_extent *ext;
+ struct osc_extent *conflict = NULL;
+ struct osc_extent *found = NULL;
+ pgoff_t chunk;
+ pgoff_t max_end;
+ int max_pages; /* max_pages_per_rpc */
+ int chunksize;
+ int ppc_bits; /* pages per chunk bits */
+ int chunk_mask;
+ int rc;
+
+ cur = osc_extent_alloc(obj);
+ if (cur == NULL)
+ return ERR_PTR(-ENOMEM);
+
+ lock = cl_lock_at_pgoff(env, osc2cl(obj), index, NULL, 1, 0);
+ LASSERT(lock != NULL);
+ LASSERT(lock->cll_descr.cld_mode >= CLM_WRITE);
+
+ LASSERT(cli->cl_chunkbits >= PAGE_CACHE_SHIFT);
+ ppc_bits = cli->cl_chunkbits - PAGE_CACHE_SHIFT;
+ chunk_mask = ~((1 << ppc_bits) - 1);
+ chunksize = 1 << cli->cl_chunkbits;
+ chunk = index >> ppc_bits;
+
+ /* align end to rpc edge, rpc size may not be a power 2 integer. */
+ max_pages = cli->cl_max_pages_per_rpc;
+ LASSERT((max_pages & ~chunk_mask) == 0);
+ max_end = index - (index % max_pages) + max_pages - 1;
+ max_end = min_t(pgoff_t, max_end, lock->cll_descr.cld_end);
+
+ /* initialize new extent by parameters so far */
+ cur->oe_max_end = max_end;
+ cur->oe_start = index & chunk_mask;
+ cur->oe_end = ((index + ~chunk_mask + 1) & chunk_mask) - 1;
+ if (cur->oe_start < lock->cll_descr.cld_start)
+ cur->oe_start = lock->cll_descr.cld_start;
+ if (cur->oe_end > max_end)
+ cur->oe_end = max_end;
+ cur->oe_osclock = lock;
+ cur->oe_grants = 0;
+ cur->oe_mppr = max_pages;
+
+ /* grants has been allocated by caller */
+ LASSERTF(*grants >= chunksize + cli->cl_extent_tax,
+ "%u/%u/%u.\n", *grants, chunksize, cli->cl_extent_tax);
+ LASSERTF((max_end - cur->oe_start) < max_pages, EXTSTR, EXTPARA(cur));
+
+restart:
+ osc_object_lock(obj);
+ ext = osc_extent_search(obj, cur->oe_start);
+ if (ext == NULL)
+ ext = first_extent(obj);
+ while (ext != NULL) {
+ loff_t ext_chk_start = ext->oe_start >> ppc_bits;
+ loff_t ext_chk_end = ext->oe_end >> ppc_bits;
+
+ LASSERT(sanity_check_nolock(ext) == 0);
+ if (chunk > ext_chk_end + 1)
+ break;
+
+ /* if covering by different locks, no chance to match */
+ if (lock != ext->oe_osclock) {
+ EASSERTF(!overlapped(ext, cur), ext,
+ EXTSTR, EXTPARA(cur));
+
+ ext = next_extent(ext);
+ continue;
+ }
+
+ /* discontiguous chunks? */
+ if (chunk + 1 < ext_chk_start) {
+ ext = next_extent(ext);
+ continue;
+ }
+
+ /* ok, from now on, ext and cur have these attrs:
+ * 1. covered by the same lock
+ * 2. contiguous at chunk level or overlapping. */
+
+ if (overlapped(ext, cur)) {
+ /* cur is the minimum unit, so overlapping means
+ * full contain. */
+ EASSERTF((ext->oe_start <= cur->oe_start &&
+ ext->oe_end >= cur->oe_end),
+ ext, EXTSTR, EXTPARA(cur));
+
+ if (ext->oe_state > OES_CACHE || ext->oe_fsync_wait) {
+ /* for simplicity, we wait for this extent to
+ * finish before going forward. */
+ conflict = osc_extent_get(ext);
+ break;
+ }
+
+ found = osc_extent_hold(ext);
+ break;
+ }
+
+ /* non-overlapped extent */
+ if (ext->oe_state != OES_CACHE || ext->oe_fsync_wait) {
+ /* we can't do anything for a non OES_CACHE extent, or
+ * if there is someone waiting for this extent to be
+ * flushed, try next one. */
+ ext = next_extent(ext);
+ continue;
+ }
+
+ /* check if they belong to the same rpc slot before trying to
+ * merge. the extents are not overlapped and contiguous at
+ * chunk level to get here. */
+ if (ext->oe_max_end != max_end) {
+ /* if they don't belong to the same RPC slot or
+ * max_pages_per_rpc has ever changed, do not merge. */
+ ext = next_extent(ext);
+ continue;
+ }
+
+ /* it's required that an extent must be contiguous at chunk
+ * level so that we know the whole extent is covered by grant
+ * (the pages in the extent are NOT required to be contiguous).
+ * Otherwise, it will be too much difficult to know which
+ * chunks have grants allocated. */
+
+ /* try to do front merge - extend ext's start */
+ if (chunk + 1 == ext_chk_start) {
+ /* ext must be chunk size aligned */
+ EASSERT((ext->oe_start & ~chunk_mask) == 0, ext);
+
+ /* pull ext's start back to cover cur */
+ ext->oe_start = cur->oe_start;
+ ext->oe_grants += chunksize;
+ *grants -= chunksize;
+
+ found = osc_extent_hold(ext);
+ } else if (chunk == ext_chk_end + 1) {
+ /* rear merge */
+ ext->oe_end = cur->oe_end;
+ ext->oe_grants += chunksize;
+ *grants -= chunksize;
+
+ /* try to merge with the next one because we just fill
+ * in a gap */
+ if (osc_extent_merge(env, ext, next_extent(ext)) == 0)
+ /* we can save extent tax from next extent */
+ *grants += cli->cl_extent_tax;
+
+ found = osc_extent_hold(ext);
+ }
+ if (found != NULL)
+ break;
+
+ ext = next_extent(ext);
+ }
+
+ osc_extent_tree_dump(D_CACHE, obj);
+ if (found != NULL) {
+ LASSERT(conflict == NULL);
+ if (!IS_ERR(found)) {
+ LASSERT(found->oe_osclock == cur->oe_osclock);
+ OSC_EXTENT_DUMP(D_CACHE, found,
+ "found caching ext for %lu.\n", index);
+ }
+ } else if (conflict == NULL) {
+ /* create a new extent */
+ EASSERT(osc_extent_is_overlapped(obj, cur) == 0, cur);
+ cur->oe_grants = chunksize + cli->cl_extent_tax;
+ *grants -= cur->oe_grants;
+ LASSERT(*grants >= 0);
+
+ cur->oe_state = OES_CACHE;
+ found = osc_extent_hold(cur);
+ osc_extent_insert(obj, cur);
+ OSC_EXTENT_DUMP(D_CACHE, cur, "add into tree %lu/%lu.\n",
+ index, lock->cll_descr.cld_end);
+ }
+ osc_object_unlock(obj);
+
+ if (conflict != NULL) {
+ LASSERT(found == NULL);
+
+ /* waiting for IO to finish. Please notice that it's impossible
+ * to be an OES_TRUNC extent. */
+ rc = osc_extent_wait(env, conflict, OES_INV);
+ osc_extent_put(env, conflict);
+ conflict = NULL;
+ if (rc < 0) {
+ found = ERR_PTR(rc);
+ goto out;
+ }
+
+ goto restart;
+ }
+
+out:
+ osc_extent_put(env, cur);
+ LASSERT(*grants >= 0);
+ return found;
+}
+
+/**
+ * Called when IO is finished to an extent.
+ */
+int osc_extent_finish(const struct lu_env *env, struct osc_extent *ext,
+ int sent, int rc)
+{
+ struct client_obd *cli = osc_cli(ext->oe_obj);
+ struct osc_async_page *oap;
+ struct osc_async_page *tmp;
+ int nr_pages = ext->oe_nr_pages;
+ int lost_grant = 0;
+ int blocksize = cli->cl_import->imp_obd->obd_osfs.os_bsize ? : 4096;
+ __u64 last_off = 0;
+ int last_count = -1;
+
+ OSC_EXTENT_DUMP(D_CACHE, ext, "extent finished.\n");
+
+ ext->oe_rc = rc ?: ext->oe_nr_pages;
+ EASSERT(ergo(rc == 0, ext->oe_state == OES_RPC), ext);
+ list_for_each_entry_safe(oap, tmp, &ext->oe_pages,
+ oap_pending_item) {
+ list_del_init(&oap->oap_rpc_item);
+ list_del_init(&oap->oap_pending_item);
+ if (last_off <= oap->oap_obj_off) {
+ last_off = oap->oap_obj_off;
+ last_count = oap->oap_count;
+ }
+
+ --ext->oe_nr_pages;
+ osc_ap_completion(env, cli, oap, sent, rc);
+ }
+ EASSERT(ext->oe_nr_pages == 0, ext);
+
+ if (!sent) {
+ lost_grant = ext->oe_grants;
+ } else if (blocksize < PAGE_CACHE_SIZE &&
+ last_count != PAGE_CACHE_SIZE) {
+ /* For short writes we shouldn't count parts of pages that
+ * span a whole chunk on the OST side, or our accounting goes
+ * wrong. Should match the code in filter_grant_check. */
+ int offset = oap->oap_page_off & ~CFS_PAGE_MASK;
+ int count = oap->oap_count + (offset & (blocksize - 1));
+ int end = (offset + oap->oap_count) & (blocksize - 1);
+ if (end)
+ count += blocksize - end;
+
+ lost_grant = PAGE_CACHE_SIZE - count;
+ }
+ if (ext->oe_grants > 0)
+ osc_free_grant(cli, nr_pages, lost_grant);
+
+ osc_extent_remove(ext);
+ /* put the refcount for RPC */
+ osc_extent_put(env, ext);
+ return 0;
+}
+
+static int extent_wait_cb(struct osc_extent *ext, int state)
+{
+ int ret;
+
+ osc_object_lock(ext->oe_obj);
+ ret = ext->oe_state == state;
+ osc_object_unlock(ext->oe_obj);
+
+ return ret;
+}
+
+/**
+ * Wait for the extent's state to become @state.
+ */
+static int osc_extent_wait(const struct lu_env *env, struct osc_extent *ext,
+ int state)
+{
+ struct osc_object *obj = ext->oe_obj;
+ struct l_wait_info lwi = LWI_TIMEOUT_INTR(cfs_time_seconds(600), NULL,
+ LWI_ON_SIGNAL_NOOP, NULL);
+ int rc = 0;
+
+ osc_object_lock(obj);
+ LASSERT(sanity_check_nolock(ext) == 0);
+ /* `Kick' this extent only if the caller is waiting for it to be
+ * written out. */
+ if (state == OES_INV && !ext->oe_urgent && !ext->oe_hp &&
+ !ext->oe_trunc_pending) {
+ if (ext->oe_state == OES_ACTIVE) {
+ ext->oe_urgent = 1;
+ } else if (ext->oe_state == OES_CACHE) {
+ ext->oe_urgent = 1;
+ osc_extent_hold(ext);
+ rc = 1;
+ }
+ }
+ osc_object_unlock(obj);
+ if (rc == 1)
+ osc_extent_release(env, ext);
+
+ /* wait for the extent until its state becomes @state */
+ rc = l_wait_event(ext->oe_waitq, extent_wait_cb(ext, state), &lwi);
+ if (rc == -ETIMEDOUT) {
+ OSC_EXTENT_DUMP(D_ERROR, ext,
+ "%s: wait ext to %d timedout, recovery in progress?\n",
+ osc_export(obj)->exp_obd->obd_name, state);
+
+ lwi = LWI_INTR(LWI_ON_SIGNAL_NOOP, NULL);
+ rc = l_wait_event(ext->oe_waitq, extent_wait_cb(ext, state),
+ &lwi);
+ }
+ if (rc == 0 && ext->oe_rc < 0)
+ rc = ext->oe_rc;
+ return rc;
+}
+
+/**
+ * Discard pages with index greater than @size. If @ext is overlapped with
+ * @size, then partial truncate happens.
+ */
+static int osc_extent_truncate(struct osc_extent *ext, pgoff_t trunc_index,
+ bool partial)
+{
+ struct cl_env_nest nest;
+ struct lu_env *env;
+ struct cl_io *io;
+ struct osc_object *obj = ext->oe_obj;
+ struct client_obd *cli = osc_cli(obj);
+ struct osc_async_page *oap;
+ struct osc_async_page *tmp;
+ int pages_in_chunk = 0;
+ int ppc_bits = cli->cl_chunkbits - PAGE_CACHE_SHIFT;
+ __u64 trunc_chunk = trunc_index >> ppc_bits;
+ int grants = 0;
+ int nr_pages = 0;
+ int rc = 0;
+
+ LASSERT(sanity_check(ext) == 0);
+ EASSERT(ext->oe_state == OES_TRUNC, ext);
+ EASSERT(!ext->oe_urgent, ext);
+
+ /* Request new lu_env.
+ * We can't use that env from osc_cache_truncate_start() because
+ * it's from lov_io_sub and not fully initialized. */
+ env = cl_env_nested_get(&nest);
+ io = &osc_env_info(env)->oti_io;
+ io->ci_obj = cl_object_top(osc2cl(obj));
+ rc = cl_io_init(env, io, CIT_MISC, io->ci_obj);
+ if (rc < 0)
+ goto out;
+
+ /* discard all pages with index greater then trunc_index */
+ list_for_each_entry_safe(oap, tmp, &ext->oe_pages,
+ oap_pending_item) {
+ struct cl_page *sub = oap2cl_page(oap);
+ struct cl_page *page = cl_page_top(sub);
+
+ LASSERT(list_empty(&oap->oap_rpc_item));
+
+ /* only discard the pages with their index greater than
+ * trunc_index, and ... */
+ if (sub->cp_index < trunc_index ||
+ (sub->cp_index == trunc_index && partial)) {
+ /* accounting how many pages remaining in the chunk
+ * so that we can calculate grants correctly. */
+ if (sub->cp_index >> ppc_bits == trunc_chunk)
+ ++pages_in_chunk;
+ continue;
+ }
+
+ list_del_init(&oap->oap_pending_item);
+
+ cl_page_get(page);
+ lu_ref_add(&page->cp_reference, "truncate", current);
+
+ if (cl_page_own(env, io, page) == 0) {
+ cl_page_unmap(env, io, page);
+ cl_page_discard(env, io, page);
+ cl_page_disown(env, io, page);
+ } else {
+ LASSERT(page->cp_state == CPS_FREEING);
+ LASSERT(0);
+ }
+
+ lu_ref_del(&page->cp_reference, "truncate", current);
+ cl_page_put(env, page);
+
+ --ext->oe_nr_pages;
+ ++nr_pages;
+ }
+ EASSERTF(ergo(ext->oe_start >= trunc_index + !!partial,
+ ext->oe_nr_pages == 0),
+ ext, "trunc_index %lu, partial %d\n", trunc_index, partial);
+
+ osc_object_lock(obj);
+ if (ext->oe_nr_pages == 0) {
+ LASSERT(pages_in_chunk == 0);
+ grants = ext->oe_grants;
+ ext->oe_grants = 0;
+ } else { /* calculate how many grants we can free */
+ int chunks = (ext->oe_end >> ppc_bits) - trunc_chunk;
+ pgoff_t last_index;
+
+
+ /* if there is no pages in this chunk, we can also free grants
+ * for the last chunk */
+ if (pages_in_chunk == 0) {
+ /* if this is the 1st chunk and no pages in this chunk,
+ * ext->oe_nr_pages must be zero, so we should be in
+ * the other if-clause. */
+ LASSERT(trunc_chunk > 0);
+ --trunc_chunk;
+ ++chunks;
+ }
+
+ /* this is what we can free from this extent */
+ grants = chunks << cli->cl_chunkbits;
+ ext->oe_grants -= grants;
+ last_index = ((trunc_chunk + 1) << ppc_bits) - 1;
+ ext->oe_end = min(last_index, ext->oe_max_end);
+ LASSERT(ext->oe_end >= ext->oe_start);
+ LASSERT(ext->oe_grants > 0);
+ }
+ osc_object_unlock(obj);
+
+ if (grants > 0 || nr_pages > 0)
+ osc_free_grant(cli, nr_pages, grants);
+
+out:
+ cl_io_fini(env, io);
+ cl_env_nested_put(&nest, env);
+ return rc;
+}
+
+/**
+ * This function is used to make the extent prepared for transfer.
+ * A race with flushing page - ll_writepage() has to be handled cautiously.
+ */
+static int osc_extent_make_ready(const struct lu_env *env,
+ struct osc_extent *ext)
+{
+ struct osc_async_page *oap;
+ struct osc_async_page *last = NULL;
+ struct osc_object *obj = ext->oe_obj;
+ int page_count = 0;
+ int rc;
+
+ /* we're going to grab page lock, so object lock must not be taken. */
+ LASSERT(sanity_check(ext) == 0);
+ /* in locking state, any process should not touch this extent. */
+ EASSERT(ext->oe_state == OES_LOCKING, ext);
+ EASSERT(ext->oe_owner != NULL, ext);
+
+ OSC_EXTENT_DUMP(D_CACHE, ext, "make ready\n");
+
+ list_for_each_entry(oap, &ext->oe_pages, oap_pending_item) {
+ ++page_count;
+ if (last == NULL || last->oap_obj_off < oap->oap_obj_off)
+ last = oap;
+
+ /* checking ASYNC_READY is race safe */
+ if ((oap->oap_async_flags & ASYNC_READY) != 0)
+ continue;
+
+ rc = osc_make_ready(env, oap, OBD_BRW_WRITE);
+ switch (rc) {
+ case 0:
+ spin_lock(&oap->oap_lock);
+ oap->oap_async_flags |= ASYNC_READY;
+ spin_unlock(&oap->oap_lock);
+ break;
+ case -EALREADY:
+ LASSERT((oap->oap_async_flags & ASYNC_READY) != 0);
+ break;
+ default:
+ LASSERTF(0, "unknown return code: %d\n", rc);
+ }
+ }
+
+ LASSERT(page_count == ext->oe_nr_pages);
+ LASSERT(last != NULL);
+ /* the last page is the only one we need to refresh its count by
+ * the size of file. */
+ if (!(last->oap_async_flags & ASYNC_COUNT_STABLE)) {
+ last->oap_count = osc_refresh_count(env, last, OBD_BRW_WRITE);
+ LASSERT(last->oap_count > 0);
+ LASSERT(last->oap_page_off + last->oap_count <= PAGE_CACHE_SIZE);
+ last->oap_async_flags |= ASYNC_COUNT_STABLE;
+ }
+
+ /* for the rest of pages, we don't need to call osf_refresh_count()
+ * because it's known they are not the last page */
+ list_for_each_entry(oap, &ext->oe_pages, oap_pending_item) {
+ if (!(oap->oap_async_flags & ASYNC_COUNT_STABLE)) {
+ oap->oap_count = PAGE_CACHE_SIZE - oap->oap_page_off;
+ oap->oap_async_flags |= ASYNC_COUNT_STABLE;
+ }
+ }
+
+ osc_object_lock(obj);
+ osc_extent_state_set(ext, OES_RPC);
+ osc_object_unlock(obj);
+ /* get a refcount for RPC. */
+ osc_extent_get(ext);
+
+ return 0;
+}
+
+/**
+ * Quick and simple version of osc_extent_find(). This function is frequently
+ * called to expand the extent for the same IO. To expand the extent, the
+ * page index must be in the same or next chunk of ext->oe_end.
+ */
+static int osc_extent_expand(struct osc_extent *ext, pgoff_t index, int *grants)
+{
+ struct osc_object *obj = ext->oe_obj;
+ struct client_obd *cli = osc_cli(obj);
+ struct osc_extent *next;
+ int ppc_bits = cli->cl_chunkbits - PAGE_CACHE_SHIFT;
+ pgoff_t chunk = index >> ppc_bits;
+ pgoff_t end_chunk;
+ pgoff_t end_index;
+ int chunksize = 1 << cli->cl_chunkbits;
+ int rc = 0;
+
+ LASSERT(ext->oe_max_end >= index && ext->oe_start <= index);
+ osc_object_lock(obj);
+ LASSERT(sanity_check_nolock(ext) == 0);
+ end_chunk = ext->oe_end >> ppc_bits;
+ if (chunk > end_chunk + 1) {
+ rc = -ERANGE;
+ goto out;
+ }
+
+ if (end_chunk >= chunk) {
+ rc = 0;
+ goto out;
+ }
+
+ LASSERT(end_chunk + 1 == chunk);
+ /* try to expand this extent to cover @index */
+ end_index = min(ext->oe_max_end, ((chunk + 1) << ppc_bits) - 1);
+
+ next = next_extent(ext);
+ if (next != NULL && next->oe_start <= end_index) {
+ /* complex mode - overlapped with the next extent,
+ * this case will be handled by osc_extent_find() */
+ rc = -EAGAIN;
+ goto out;
+ }
+
+ ext->oe_end = end_index;
+ ext->oe_grants += chunksize;
+ *grants -= chunksize;
+ LASSERT(*grants >= 0);
+ EASSERTF(osc_extent_is_overlapped(obj, ext) == 0, ext,
+ "overlapped after expanding for %lu.\n", index);
+
+out:
+ osc_object_unlock(obj);
+ return rc;
+}
+
+static void osc_extent_tree_dump0(int level, struct osc_object *obj,
+ const char *func, int line)
+{
+ struct osc_extent *ext;
+ int cnt;
+
+ CDEBUG(level, "Dump object %p extents at %s:%d, mppr: %u.\n",
+ obj, func, line, osc_cli(obj)->cl_max_pages_per_rpc);
+
+ /* osc_object_lock(obj); */
+ cnt = 1;
+ for (ext = first_extent(obj); ext != NULL; ext = next_extent(ext))
+ OSC_EXTENT_DUMP(level, ext, "in tree %d.\n", cnt++);
+
+ cnt = 1;
+ list_for_each_entry(ext, &obj->oo_hp_exts, oe_link)
+ OSC_EXTENT_DUMP(level, ext, "hp %d.\n", cnt++);
+
+ cnt = 1;
+ list_for_each_entry(ext, &obj->oo_urgent_exts, oe_link)
+ OSC_EXTENT_DUMP(level, ext, "urgent %d.\n", cnt++);
+
+ cnt = 1;
+ list_for_each_entry(ext, &obj->oo_reading_exts, oe_link)
+ OSC_EXTENT_DUMP(level, ext, "reading %d.\n", cnt++);
+ /* osc_object_unlock(obj); */
+}
+
+/* ------------------ osc extent end ------------------ */
+
+static inline int osc_is_ready(struct osc_object *osc)
+{
+ return !list_empty(&osc->oo_ready_item) ||
+ !list_empty(&osc->oo_hp_ready_item);
+}
+
+#define OSC_IO_DEBUG(OSC, STR, args...) \
+ CDEBUG(D_CACHE, "obj %p ready %d|%c|%c wr %d|%c|%c rd %d|%c " STR, \
+ (OSC), osc_is_ready(OSC), \
+ list_empty_marker(&(OSC)->oo_hp_ready_item), \
+ list_empty_marker(&(OSC)->oo_ready_item), \
+ atomic_read(&(OSC)->oo_nr_writes), \
+ list_empty_marker(&(OSC)->oo_hp_exts), \
+ list_empty_marker(&(OSC)->oo_urgent_exts), \
+ atomic_read(&(OSC)->oo_nr_reads), \
+ list_empty_marker(&(OSC)->oo_reading_exts), \
+ ##args)
+
+static int osc_make_ready(const struct lu_env *env, struct osc_async_page *oap,
+ int cmd)
+{
+ struct osc_page *opg = oap2osc_page(oap);
+ struct cl_page *page = cl_page_top(oap2cl_page(oap));
+ int result;
+
+ LASSERT(cmd == OBD_BRW_WRITE); /* no cached reads */
+
+ result = cl_page_make_ready(env, page, CRT_WRITE);
+ if (result == 0)
+ opg->ops_submit_time = cfs_time_current();
+ return result;
+}
+
+static int osc_refresh_count(const struct lu_env *env,
+ struct osc_async_page *oap, int cmd)
+{
+ struct osc_page *opg = oap2osc_page(oap);
+ struct cl_page *page = oap2cl_page(oap);
+ struct cl_object *obj;
+ struct cl_attr *attr = &osc_env_info(env)->oti_attr;
+
+ int result;
+ loff_t kms;
+
+ /* readpage queues with _COUNT_STABLE, shouldn't get here. */
+ LASSERT(!(cmd & OBD_BRW_READ));
+ LASSERT(opg != NULL);
+ obj = opg->ops_cl.cpl_obj;
+
+ cl_object_attr_lock(obj);
+ result = cl_object_attr_get(env, obj, attr);
+ cl_object_attr_unlock(obj);
+ if (result < 0)
+ return result;
+ kms = attr->cat_kms;
+ if (cl_offset(obj, page->cp_index) >= kms)
+ /* catch race with truncate */
+ return 0;
+ else if (cl_offset(obj, page->cp_index + 1) > kms)
+ /* catch sub-page write at end of file */
+ return kms % PAGE_CACHE_SIZE;
+ else
+ return PAGE_CACHE_SIZE;
+}
+
+static int osc_completion(const struct lu_env *env, struct osc_async_page *oap,
+ int cmd, int rc)
+{
+ struct osc_page *opg = oap2osc_page(oap);
+ struct cl_page *page = cl_page_top(oap2cl_page(oap));
+ struct osc_object *obj = cl2osc(opg->ops_cl.cpl_obj);
+ enum cl_req_type crt;
+ int srvlock;
+
+ cmd &= ~OBD_BRW_NOQUOTA;
+ LASSERT(equi(page->cp_state == CPS_PAGEIN, cmd == OBD_BRW_READ));
+ LASSERT(equi(page->cp_state == CPS_PAGEOUT, cmd == OBD_BRW_WRITE));
+ LASSERT(opg->ops_transfer_pinned);
+
+ /*
+ * page->cp_req can be NULL if io submission failed before
+ * cl_req was allocated.
+ */
+ if (page->cp_req != NULL)
+ cl_req_page_done(env, page);
+ LASSERT(page->cp_req == NULL);
+
+ crt = cmd == OBD_BRW_READ ? CRT_READ : CRT_WRITE;
+ /* Clear opg->ops_transfer_pinned before VM lock is released. */
+ opg->ops_transfer_pinned = 0;
+
+ spin_lock(&obj->oo_seatbelt);
+ LASSERT(opg->ops_submitter != NULL);
+ LASSERT(!list_empty(&opg->ops_inflight));
+ list_del_init(&opg->ops_inflight);
+ opg->ops_submitter = NULL;
+ spin_unlock(&obj->oo_seatbelt);
+
+ opg->ops_submit_time = 0;
+ srvlock = oap->oap_brw_flags & OBD_BRW_SRVLOCK;
+
+ /* statistic */
+ if (rc == 0 && srvlock) {
+ struct lu_device *ld = opg->ops_cl.cpl_obj->co_lu.lo_dev;
+ struct osc_stats *stats = &lu2osc_dev(ld)->od_stats;
+ int bytes = oap->oap_count;
+
+ if (crt == CRT_READ)
+ stats->os_lockless_reads += bytes;
+ else
+ stats->os_lockless_writes += bytes;
+ }
+
+ /*
+ * This has to be the last operation with the page, as locks are
+ * released in cl_page_completion() and nothing except for the
+ * reference counter protects page from concurrent reclaim.
+ */
+ lu_ref_del(&page->cp_reference, "transfer", page);
+
+ cl_page_completion(env, page, crt, rc);
+
+ return 0;
+}
+
+#define OSC_DUMP_GRANT(cli, fmt, args...) do { \
+ struct client_obd *__tmp = (cli); \
+ CDEBUG(D_CACHE, "%s: { dirty: %ld/%ld dirty_pages: %d/%d " \
+ "dropped: %ld avail: %ld, reserved: %ld, flight: %d } " fmt, \
+ __tmp->cl_import->imp_obd->obd_name, \
+ __tmp->cl_dirty, __tmp->cl_dirty_max, \
+ atomic_read(&obd_dirty_pages), obd_max_dirty_pages, \
+ __tmp->cl_lost_grant, __tmp->cl_avail_grant, \
+ __tmp->cl_reserved_grant, __tmp->cl_w_in_flight, ##args); \
+} while (0)
+
+/* caller must hold loi_list_lock */
+static void osc_consume_write_grant(struct client_obd *cli,
+ struct brw_page *pga)
+{
+ assert_spin_locked(&cli->cl_loi_list_lock.lock);
+ LASSERT(!(pga->flag & OBD_BRW_FROM_GRANT));
+ atomic_inc(&obd_dirty_pages);
+ cli->cl_dirty += PAGE_CACHE_SIZE;
+ pga->flag |= OBD_BRW_FROM_GRANT;
+ CDEBUG(D_CACHE, "using %lu grant credits for brw %p page %p\n",
+ PAGE_CACHE_SIZE, pga, pga->pg);
+ osc_update_next_shrink(cli);
+}
+
+/* the companion to osc_consume_write_grant, called when a brw has completed.
+ * must be called with the loi lock held. */
+static void osc_release_write_grant(struct client_obd *cli,
+ struct brw_page *pga)
+{
+ assert_spin_locked(&cli->cl_loi_list_lock.lock);
+ if (!(pga->flag & OBD_BRW_FROM_GRANT)) {
+ return;
+ }
+
+ pga->flag &= ~OBD_BRW_FROM_GRANT;
+ atomic_dec(&obd_dirty_pages);
+ cli->cl_dirty -= PAGE_CACHE_SIZE;
+ if (pga->flag & OBD_BRW_NOCACHE) {
+ pga->flag &= ~OBD_BRW_NOCACHE;
+ atomic_dec(&obd_dirty_transit_pages);
+ cli->cl_dirty_transit -= PAGE_CACHE_SIZE;
+ }
+}
+
+/**
+ * To avoid sleeping with object lock held, it's good for us allocate enough
+ * grants before entering into critical section.
+ *
+ * client_obd_list_lock held by caller
+ */
+static int osc_reserve_grant(struct client_obd *cli, unsigned int bytes)
+{
+ int rc = -EDQUOT;
+
+ if (cli->cl_avail_grant >= bytes) {
+ cli->cl_avail_grant -= bytes;
+ cli->cl_reserved_grant += bytes;
+ rc = 0;
+ }
+ return rc;
+}
+
+static void __osc_unreserve_grant(struct client_obd *cli,
+ unsigned int reserved, unsigned int unused)
+{
+ /* it's quite normal for us to get more grant than reserved.
+ * Thinking about a case that two extents merged by adding a new
+ * chunk, we can save one extent tax. If extent tax is greater than
+ * one chunk, we can save more grant by adding a new chunk */
+ cli->cl_reserved_grant -= reserved;
+ if (unused > reserved) {
+ cli->cl_avail_grant += reserved;
+ cli->cl_lost_grant += unused - reserved;
+ } else {
+ cli->cl_avail_grant += unused;
+ }
+}
+
+void osc_unreserve_grant(struct client_obd *cli,
+ unsigned int reserved, unsigned int unused)
+{
+ client_obd_list_lock(&cli->cl_loi_list_lock);
+ __osc_unreserve_grant(cli, reserved, unused);
+ if (unused > 0)
+ osc_wake_cache_waiters(cli);
+ client_obd_list_unlock(&cli->cl_loi_list_lock);
+}
+
+/**
+ * Free grant after IO is finished or canceled.
+ *
+ * @lost_grant is used to remember how many grants we have allocated but not
+ * used, we should return these grants to OST. There're two cases where grants
+ * can be lost:
+ * 1. truncate;
+ * 2. blocksize at OST is less than PAGE_CACHE_SIZE and a partial page was
+ * written. In this case OST may use less chunks to serve this partial
+ * write. OSTs don't actually know the page size on the client side. so
+ * clients have to calculate lost grant by the blocksize on the OST.
+ * See filter_grant_check() for details.
+ */
+static void osc_free_grant(struct client_obd *cli, unsigned int nr_pages,
+ unsigned int lost_grant)
+{
+ int grant = (1 << cli->cl_chunkbits) + cli->cl_extent_tax;
+
+ client_obd_list_lock(&cli->cl_loi_list_lock);
+ atomic_sub(nr_pages, &obd_dirty_pages);
+ cli->cl_dirty -= nr_pages << PAGE_CACHE_SHIFT;
+ cli->cl_lost_grant += lost_grant;
+ if (cli->cl_avail_grant < grant && cli->cl_lost_grant >= grant) {
+ /* borrow some grant from truncate to avoid the case that
+ * truncate uses up all avail grant */
+ cli->cl_lost_grant -= grant;
+ cli->cl_avail_grant += grant;
+ }
+ osc_wake_cache_waiters(cli);
+ client_obd_list_unlock(&cli->cl_loi_list_lock);
+ CDEBUG(D_CACHE, "lost %u grant: %lu avail: %lu dirty: %lu\n",
+ lost_grant, cli->cl_lost_grant,
+ cli->cl_avail_grant, cli->cl_dirty);
+}
+
+/**
+ * The companion to osc_enter_cache(), called when @oap is no longer part of
+ * the dirty accounting due to error.
+ */
+static void osc_exit_cache(struct client_obd *cli, struct osc_async_page *oap)
+{
+ client_obd_list_lock(&cli->cl_loi_list_lock);
+ osc_release_write_grant(cli, &oap->oap_brw_page);
+ client_obd_list_unlock(&cli->cl_loi_list_lock);
+}
+
+/**
+ * Non-blocking version of osc_enter_cache() that consumes grant only when it
+ * is available.
+ */
+static int osc_enter_cache_try(struct client_obd *cli,
+ struct osc_async_page *oap,
+ int bytes, int transient)
+{
+ int rc;
+
+ OSC_DUMP_GRANT(cli, "need:%d.\n", bytes);
+
+ rc = osc_reserve_grant(cli, bytes);
+ if (rc < 0)
+ return 0;
+
+ if (cli->cl_dirty + PAGE_CACHE_SIZE <= cli->cl_dirty_max &&
+ atomic_read(&obd_dirty_pages) + 1 <= obd_max_dirty_pages) {
+ osc_consume_write_grant(cli, &oap->oap_brw_page);
+ if (transient) {
+ cli->cl_dirty_transit += PAGE_CACHE_SIZE;
+ atomic_inc(&obd_dirty_transit_pages);
+ oap->oap_brw_flags |= OBD_BRW_NOCACHE;
+ }
+ rc = 1;
+ } else {
+ __osc_unreserve_grant(cli, bytes, bytes);
+ rc = 0;
+ }
+ return rc;
+}
+
+static int ocw_granted(struct client_obd *cli, struct osc_cache_waiter *ocw)
+{
+ int rc;
+ client_obd_list_lock(&cli->cl_loi_list_lock);
+ rc = list_empty(&ocw->ocw_entry);
+ client_obd_list_unlock(&cli->cl_loi_list_lock);
+ return rc;
+}
+
+/**
+ * The main entry to reserve dirty page accounting. Usually the grant reserved
+ * in this function will be freed in bulk in osc_free_grant() unless it fails
+ * to add osc cache, in that case, it will be freed in osc_exit_cache().
+ *
+ * The process will be put into sleep if it's already run out of grant.
+ */
+static int osc_enter_cache(const struct lu_env *env, struct client_obd *cli,
+ struct osc_async_page *oap, int bytes)
+{
+ struct osc_object *osc = oap->oap_obj;
+ struct lov_oinfo *loi = osc->oo_oinfo;
+ struct osc_cache_waiter ocw;
+ struct l_wait_info lwi = LWI_INTR(LWI_ON_SIGNAL_NOOP, NULL);
+ int rc = -EDQUOT;
+
+ OSC_DUMP_GRANT(cli, "need:%d.\n", bytes);
+
+ client_obd_list_lock(&cli->cl_loi_list_lock);
+
+ /* force the caller to try sync io. this can jump the list
+ * of queued writes and create a discontiguous rpc stream */
+ if (OBD_FAIL_CHECK(OBD_FAIL_OSC_NO_GRANT) ||
+ cli->cl_dirty_max < PAGE_CACHE_SIZE ||
+ cli->cl_ar.ar_force_sync || loi->loi_ar.ar_force_sync) {
+ rc = -EDQUOT;
+ goto out;
+ }
+
+ /* Hopefully normal case - cache space and write credits available */
+ if (osc_enter_cache_try(cli, oap, bytes, 0)) {
+ rc = 0;
+ goto out;
+ }
+
+ /* We can get here for two reasons: too many dirty pages in cache, or
+ * run out of grants. In both cases we should write dirty pages out.
+ * Adding a cache waiter will trigger urgent write-out no matter what
+ * RPC size will be.
+ * The exiting condition is no avail grants and no dirty pages caching,
+ * that really means there is no space on the OST. */
+ init_waitqueue_head(&ocw.ocw_waitq);
+ ocw.ocw_oap = oap;
+ ocw.ocw_grant = bytes;
+ while (cli->cl_dirty > 0 || cli->cl_w_in_flight > 0) {
+ list_add_tail(&ocw.ocw_entry, &cli->cl_cache_waiters);
+ ocw.ocw_rc = 0;
+ client_obd_list_unlock(&cli->cl_loi_list_lock);
+
+ osc_io_unplug_async(env, cli, NULL);
+
+ CDEBUG(D_CACHE, "%s: sleeping for cache space @ %p for %p\n",
+ cli->cl_import->imp_obd->obd_name, &ocw, oap);
+
+ rc = l_wait_event(ocw.ocw_waitq, ocw_granted(cli, &ocw), &lwi);
+
+ client_obd_list_lock(&cli->cl_loi_list_lock);
+
+ /* l_wait_event is interrupted by signal */
+ if (rc < 0) {
+ list_del_init(&ocw.ocw_entry);
+ goto out;
+ }
+
+ LASSERT(list_empty(&ocw.ocw_entry));
+ rc = ocw.ocw_rc;
+
+ if (rc != -EDQUOT)
+ goto out;
+ if (osc_enter_cache_try(cli, oap, bytes, 0)) {
+ rc = 0;
+ goto out;
+ }
+ }
+out:
+ client_obd_list_unlock(&cli->cl_loi_list_lock);
+ OSC_DUMP_GRANT(cli, "returned %d.\n", rc);
+ return rc;
+}
+
+/* caller must hold loi_list_lock */
+void osc_wake_cache_waiters(struct client_obd *cli)
+{
+ struct list_head *l, *tmp;
+ struct osc_cache_waiter *ocw;
+
+ list_for_each_safe(l, tmp, &cli->cl_cache_waiters) {
+ ocw = list_entry(l, struct osc_cache_waiter, ocw_entry);
+ list_del_init(&ocw->ocw_entry);
+
+ ocw->ocw_rc = -EDQUOT;
+ /* we can't dirty more */
+ if ((cli->cl_dirty + PAGE_CACHE_SIZE > cli->cl_dirty_max) ||
+ (atomic_read(&obd_dirty_pages) + 1 >
+ obd_max_dirty_pages)) {
+ CDEBUG(D_CACHE, "no dirty room: dirty: %ld osc max %ld, sys max %d\n",
+ cli->cl_dirty,
+ cli->cl_dirty_max, obd_max_dirty_pages);
+ goto wakeup;
+ }
+
+ ocw->ocw_rc = 0;
+ if (!osc_enter_cache_try(cli, ocw->ocw_oap, ocw->ocw_grant, 0))
+ ocw->ocw_rc = -EDQUOT;
+
+wakeup:
+ CDEBUG(D_CACHE, "wake up %p for oap %p, avail grant %ld, %d\n",
+ ocw, ocw->ocw_oap, cli->cl_avail_grant, ocw->ocw_rc);
+
+ wake_up(&ocw->ocw_waitq);
+ }
+}
+
+static int osc_max_rpc_in_flight(struct client_obd *cli, struct osc_object *osc)
+{
+ int hprpc = !!list_empty(&osc->oo_hp_exts);
+ return rpcs_in_flight(cli) >= cli->cl_max_rpcs_in_flight + hprpc;
+}
+
+/* This maintains the lists of pending pages to read/write for a given object
+ * (lop). This is used by osc_check_rpcs->osc_next_obj() and osc_list_maint()
+ * to quickly find objects that are ready to send an RPC. */
+static int osc_makes_rpc(struct client_obd *cli, struct osc_object *osc,
+ int cmd)
+{
+ int invalid_import = 0;
+
+ /* if we have an invalid import we want to drain the queued pages
+ * by forcing them through rpcs that immediately fail and complete
+ * the pages. recovery relies on this to empty the queued pages
+ * before canceling the locks and evicting down the llite pages */
+ if ((cli->cl_import == NULL || cli->cl_import->imp_invalid))
+ invalid_import = 1;
+
+ if (cmd & OBD_BRW_WRITE) {
+ if (atomic_read(&osc->oo_nr_writes) == 0)
+ return 0;
+ if (invalid_import) {
+ CDEBUG(D_CACHE, "invalid import forcing RPC\n");
+ return 1;
+ }
+ if (!list_empty(&osc->oo_hp_exts)) {
+ CDEBUG(D_CACHE, "high prio request forcing RPC\n");
+ return 1;
+ }
+ if (!list_empty(&osc->oo_urgent_exts)) {
+ CDEBUG(D_CACHE, "urgent request forcing RPC\n");
+ return 1;
+ }
+ /* trigger a write rpc stream as long as there are dirtiers
+ * waiting for space. as they're waiting, they're not going to
+ * create more pages to coalesce with what's waiting.. */
+ if (!list_empty(&cli->cl_cache_waiters)) {
+ CDEBUG(D_CACHE, "cache waiters forcing RPC\n");
+ return 1;
+ }
+ if (atomic_read(&osc->oo_nr_writes) >=
+ cli->cl_max_pages_per_rpc)
+ return 1;
+ } else {
+ if (atomic_read(&osc->oo_nr_reads) == 0)
+ return 0;
+ if (invalid_import) {
+ CDEBUG(D_CACHE, "invalid import forcing RPC\n");
+ return 1;
+ }
+ /* all read are urgent. */
+ if (!list_empty(&osc->oo_reading_exts))
+ return 1;
+ }
+
+ return 0;
+}
+
+static void osc_update_pending(struct osc_object *obj, int cmd, int delta)
+{
+ struct client_obd *cli = osc_cli(obj);
+ if (cmd & OBD_BRW_WRITE) {
+ atomic_add(delta, &obj->oo_nr_writes);
+ atomic_add(delta, &cli->cl_pending_w_pages);
+ LASSERT(atomic_read(&obj->oo_nr_writes) >= 0);
+ } else {
+ atomic_add(delta, &obj->oo_nr_reads);
+ atomic_add(delta, &cli->cl_pending_r_pages);
+ LASSERT(atomic_read(&obj->oo_nr_reads) >= 0);
+ }
+ OSC_IO_DEBUG(obj, "update pending cmd %d delta %d.\n", cmd, delta);
+}
+
+static int osc_makes_hprpc(struct osc_object *obj)
+{
+ return !list_empty(&obj->oo_hp_exts);
+}
+
+static void on_list(struct list_head *item, struct list_head *list, int should_be_on)
+{
+ if (list_empty(item) && should_be_on)
+ list_add_tail(item, list);
+ else if (!list_empty(item) && !should_be_on)
+ list_del_init(item);
+}
+
+/* maintain the osc's cli list membership invariants so that osc_send_oap_rpc
+ * can find pages to build into rpcs quickly */
+static int __osc_list_maint(struct client_obd *cli, struct osc_object *osc)
+{
+ if (osc_makes_hprpc(osc)) {
+ /* HP rpc */
+ on_list(&osc->oo_ready_item, &cli->cl_loi_ready_list, 0);
+ on_list(&osc->oo_hp_ready_item, &cli->cl_loi_hp_ready_list, 1);
+ } else {
+ on_list(&osc->oo_hp_ready_item, &cli->cl_loi_hp_ready_list, 0);
+ on_list(&osc->oo_ready_item, &cli->cl_loi_ready_list,
+ osc_makes_rpc(cli, osc, OBD_BRW_WRITE) ||
+ osc_makes_rpc(cli, osc, OBD_BRW_READ));
+ }
+
+ on_list(&osc->oo_write_item, &cli->cl_loi_write_list,
+ atomic_read(&osc->oo_nr_writes) > 0);
+
+ on_list(&osc->oo_read_item, &cli->cl_loi_read_list,
+ atomic_read(&osc->oo_nr_reads) > 0);
+
+ return osc_is_ready(osc);
+}
+
+static int osc_list_maint(struct client_obd *cli, struct osc_object *osc)
+{
+ int is_ready;
+
+ client_obd_list_lock(&cli->cl_loi_list_lock);
+ is_ready = __osc_list_maint(cli, osc);
+ client_obd_list_unlock(&cli->cl_loi_list_lock);
+
+ return is_ready;
+}
+
+/* this is trying to propagate async writeback errors back up to the
+ * application. As an async write fails we record the error code for later if
+ * the app does an fsync. As long as errors persist we force future rpcs to be
+ * sync so that the app can get a sync error and break the cycle of queueing
+ * pages for which writeback will fail. */
+static void osc_process_ar(struct osc_async_rc *ar, __u64 xid,
+ int rc)
+{
+ if (rc) {
+ if (!ar->ar_rc)
+ ar->ar_rc = rc;
+
+ ar->ar_force_sync = 1;
+ ar->ar_min_xid = ptlrpc_sample_next_xid();
+ return;
+
+ }
+
+ if (ar->ar_force_sync && (xid >= ar->ar_min_xid))
+ ar->ar_force_sync = 0;
+}
+
+
+/* this must be called holding the loi list lock to give coverage to exit_cache,
+ * async_flag maintenance, and oap_request */
+static void osc_ap_completion(const struct lu_env *env, struct client_obd *cli,
+ struct osc_async_page *oap, int sent, int rc)
+{
+ struct osc_object *osc = oap->oap_obj;
+ struct lov_oinfo *loi = osc->oo_oinfo;
+ __u64 xid = 0;
+
+ if (oap->oap_request != NULL) {
+ xid = ptlrpc_req_xid(oap->oap_request);
+ ptlrpc_req_finished(oap->oap_request);
+ oap->oap_request = NULL;
+ }
+
+ /* As the transfer for this page is being done, clear the flags */
+ spin_lock(&oap->oap_lock);
+ oap->oap_async_flags = 0;
+ spin_unlock(&oap->oap_lock);
+ oap->oap_interrupted = 0;
+
+ if (oap->oap_cmd & OBD_BRW_WRITE && xid > 0) {
+ client_obd_list_lock(&cli->cl_loi_list_lock);
+ osc_process_ar(&cli->cl_ar, xid, rc);
+ osc_process_ar(&loi->loi_ar, xid, rc);
+ client_obd_list_unlock(&cli->cl_loi_list_lock);
+ }
+
+ rc = osc_completion(env, oap, oap->oap_cmd, rc);
+ if (rc)
+ CERROR("completion on oap %p obj %p returns %d.\n",
+ oap, osc, rc);
+}
+
+/**
+ * Try to add extent to one RPC. We need to think about the following things:
+ * - # of pages must not be over max_pages_per_rpc
+ * - extent must be compatible with previous ones
+ */
+static int try_to_add_extent_for_io(struct client_obd *cli,
+ struct osc_extent *ext, struct list_head *rpclist,
+ int *pc, unsigned int *max_pages)
+{
+ struct osc_extent *tmp;
+ struct osc_async_page *oap = list_first_entry(&ext->oe_pages,
+ struct osc_async_page,
+ oap_pending_item);
+
+ EASSERT((ext->oe_state == OES_CACHE || ext->oe_state == OES_LOCK_DONE),
+ ext);
+
+ *max_pages = max(ext->oe_mppr, *max_pages);
+ if (*pc + ext->oe_nr_pages > *max_pages)
+ return 0;
+
+ list_for_each_entry(tmp, rpclist, oe_link) {
+ struct osc_async_page *oap2;
+
+ oap2 = list_first_entry(&tmp->oe_pages, struct osc_async_page,
+ oap_pending_item);
+ EASSERT(tmp->oe_owner == current, tmp);
+#if 0
+ if (overlapped(tmp, ext)) {
+ OSC_EXTENT_DUMP(D_ERROR, tmp, "overlapped %p.\n", ext);
+ EASSERT(0, ext);
+ }
+#endif
+ if (oap2cl_page(oap)->cp_type != oap2cl_page(oap2)->cp_type) {
+ CDEBUG(D_CACHE, "Do not permit different type of IO"
+ " for a same RPC\n");
+ return 0;
+ }
+
+ if (tmp->oe_srvlock != ext->oe_srvlock ||
+ !tmp->oe_grants != !ext->oe_grants)
+ return 0;
+
+ /* remove break for strict check */
+ break;
+ }
+
+ *pc += ext->oe_nr_pages;
+ list_move_tail(&ext->oe_link, rpclist);
+ ext->oe_owner = current;
+ return 1;
+}
+
+/**
+ * In order to prevent multiple ptlrpcd from breaking contiguous extents,
+ * get_write_extent() takes all appropriate extents in atomic.
+ *
+ * The following policy is used to collect extents for IO:
+ * 1. Add as many HP extents as possible;
+ * 2. Add the first urgent extent in urgent extent list and take it out of
+ * urgent list;
+ * 3. Add subsequent extents of this urgent extent;
+ * 4. If urgent list is not empty, goto 2;
+ * 5. Traverse the extent tree from the 1st extent;
+ * 6. Above steps exit if there is no space in this RPC.
+ */
+static int get_write_extents(struct osc_object *obj, struct list_head *rpclist)
+{
+ struct client_obd *cli = osc_cli(obj);
+ struct osc_extent *ext;
+ int page_count = 0;
+ unsigned int max_pages = cli->cl_max_pages_per_rpc;
+
+ LASSERT(osc_object_is_locked(obj));
+ while (!list_empty(&obj->oo_hp_exts)) {
+ ext = list_entry(obj->oo_hp_exts.next, struct osc_extent,
+ oe_link);
+ LASSERT(ext->oe_state == OES_CACHE);
+ if (!try_to_add_extent_for_io(cli, ext, rpclist, &page_count,
+ &max_pages))
+ return page_count;
+ EASSERT(ext->oe_nr_pages <= max_pages, ext);
+ }
+ if (page_count == max_pages)
+ return page_count;
+
+ while (!list_empty(&obj->oo_urgent_exts)) {
+ ext = list_entry(obj->oo_urgent_exts.next,
+ struct osc_extent, oe_link);
+ if (!try_to_add_extent_for_io(cli, ext, rpclist, &page_count,
+ &max_pages))
+ return page_count;
+
+ if (!ext->oe_intree)
+ continue;
+
+ while ((ext = next_extent(ext)) != NULL) {
+ if ((ext->oe_state != OES_CACHE) ||
+ (!list_empty(&ext->oe_link) &&
+ ext->oe_owner != NULL))
+ continue;
+
+ if (!try_to_add_extent_for_io(cli, ext, rpclist,
+ &page_count, &max_pages))
+ return page_count;
+ }
+ }
+ if (page_count == max_pages)
+ return page_count;
+
+ ext = first_extent(obj);
+ while (ext != NULL) {
+ if ((ext->oe_state != OES_CACHE) ||
+ /* this extent may be already in current rpclist */
+ (!list_empty(&ext->oe_link) && ext->oe_owner != NULL)) {
+ ext = next_extent(ext);
+ continue;
+ }
+
+ if (!try_to_add_extent_for_io(cli, ext, rpclist, &page_count,
+ &max_pages))
+ return page_count;
+
+ ext = next_extent(ext);
+ }
+ return page_count;
+}
+
+static int
+osc_send_write_rpc(const struct lu_env *env, struct client_obd *cli,
+ struct osc_object *osc, pdl_policy_t pol)
+{
+ LIST_HEAD(rpclist);
+ struct osc_extent *ext;
+ struct osc_extent *tmp;
+ struct osc_extent *first = NULL;
+ u32 page_count = 0;
+ int srvlock = 0;
+ int rc = 0;
+
+ LASSERT(osc_object_is_locked(osc));
+
+ page_count = get_write_extents(osc, &rpclist);
+ LASSERT(equi(page_count == 0, list_empty(&rpclist)));
+
+ if (list_empty(&rpclist))
+ return 0;
+
+ osc_update_pending(osc, OBD_BRW_WRITE, -page_count);
+
+ list_for_each_entry(ext, &rpclist, oe_link) {
+ LASSERT(ext->oe_state == OES_CACHE ||
+ ext->oe_state == OES_LOCK_DONE);
+ if (ext->oe_state == OES_CACHE)
+ osc_extent_state_set(ext, OES_LOCKING);
+ else
+ osc_extent_state_set(ext, OES_RPC);
+ }
+
+ /* we're going to grab page lock, so release object lock because
+ * lock order is page lock -> object lock. */
+ osc_object_unlock(osc);
+
+ list_for_each_entry_safe(ext, tmp, &rpclist, oe_link) {
+ if (ext->oe_state == OES_LOCKING) {
+ rc = osc_extent_make_ready(env, ext);
+ if (unlikely(rc < 0)) {
+ list_del_init(&ext->oe_link);
+ osc_extent_finish(env, ext, 0, rc);
+ continue;
+ }
+ }
+ if (first == NULL) {
+ first = ext;
+ srvlock = ext->oe_srvlock;
+ } else {
+ LASSERT(srvlock == ext->oe_srvlock);
+ }
+ }
+
+ if (!list_empty(&rpclist)) {
+ LASSERT(page_count > 0);
+ rc = osc_build_rpc(env, cli, &rpclist, OBD_BRW_WRITE, pol);
+ LASSERT(list_empty(&rpclist));
+ }
+
+ osc_object_lock(osc);
+ return rc;
+}
+
+/**
+ * prepare pages for ASYNC io and put pages in send queue.
+ *
+ * \param cmd OBD_BRW_* macroses
+ * \param lop pending pages
+ *
+ * \return zero if no page added to send queue.
+ * \return 1 if pages successfully added to send queue.
+ * \return negative on errors.
+ */
+static int
+osc_send_read_rpc(const struct lu_env *env, struct client_obd *cli,
+ struct osc_object *osc, pdl_policy_t pol)
+{
+ struct osc_extent *ext;
+ struct osc_extent *next;
+ LIST_HEAD(rpclist);
+ int page_count = 0;
+ unsigned int max_pages = cli->cl_max_pages_per_rpc;
+ int rc = 0;
+
+ LASSERT(osc_object_is_locked(osc));
+ list_for_each_entry_safe(ext, next,
+ &osc->oo_reading_exts, oe_link) {
+ EASSERT(ext->oe_state == OES_LOCK_DONE, ext);
+ if (!try_to_add_extent_for_io(cli, ext, &rpclist, &page_count,
+ &max_pages))
+ break;
+ osc_extent_state_set(ext, OES_RPC);
+ EASSERT(ext->oe_nr_pages <= max_pages, ext);
+ }
+ LASSERT(page_count <= max_pages);
+
+ osc_update_pending(osc, OBD_BRW_READ, -page_count);
+
+ if (!list_empty(&rpclist)) {
+ osc_object_unlock(osc);
+
+ LASSERT(page_count > 0);
+ rc = osc_build_rpc(env, cli, &rpclist, OBD_BRW_READ, pol);
+ LASSERT(list_empty(&rpclist));
+
+ osc_object_lock(osc);
+ }
+ return rc;
+}
+
+#define list_to_obj(list, item) ({ \
+ struct list_head *__tmp = (list)->next; \
+ list_del_init(__tmp); \
+ list_entry(__tmp, struct osc_object, oo_##item); \
+})
+
+/* This is called by osc_check_rpcs() to find which objects have pages that
+ * we could be sending. These lists are maintained by osc_makes_rpc(). */
+static struct osc_object *osc_next_obj(struct client_obd *cli)
+{
+ /* First return objects that have blocked locks so that they
+ * will be flushed quickly and other clients can get the lock,
+ * then objects which have pages ready to be stuffed into RPCs */
+ if (!list_empty(&cli->cl_loi_hp_ready_list))
+ return list_to_obj(&cli->cl_loi_hp_ready_list, hp_ready_item);
+ if (!list_empty(&cli->cl_loi_ready_list))
+ return list_to_obj(&cli->cl_loi_ready_list, ready_item);
+
+ /* then if we have cache waiters, return all objects with queued
+ * writes. This is especially important when many small files
+ * have filled up the cache and not been fired into rpcs because
+ * they don't pass the nr_pending/object threshold */
+ if (!list_empty(&cli->cl_cache_waiters) &&
+ !list_empty(&cli->cl_loi_write_list))
+ return list_to_obj(&cli->cl_loi_write_list, write_item);
+
+ /* then return all queued objects when we have an invalid import
+ * so that they get flushed */
+ if (cli->cl_import == NULL || cli->cl_import->imp_invalid) {
+ if (!list_empty(&cli->cl_loi_write_list))
+ return list_to_obj(&cli->cl_loi_write_list, write_item);
+ if (!list_empty(&cli->cl_loi_read_list))
+ return list_to_obj(&cli->cl_loi_read_list, read_item);
+ }
+ return NULL;
+}
+
+/* called with the loi list lock held */
+static void osc_check_rpcs(const struct lu_env *env, struct client_obd *cli,
+ pdl_policy_t pol)
+{
+ struct osc_object *osc;
+ int rc = 0;
+
+ while ((osc = osc_next_obj(cli)) != NULL) {
+ struct cl_object *obj = osc2cl(osc);
+ struct lu_ref_link link;
+
+ OSC_IO_DEBUG(osc, "%lu in flight\n", rpcs_in_flight(cli));
+
+ if (osc_max_rpc_in_flight(cli, osc)) {
+ __osc_list_maint(cli, osc);
+ break;
+ }
+
+ cl_object_get(obj);
+ client_obd_list_unlock(&cli->cl_loi_list_lock);
+ lu_object_ref_add_at(&obj->co_lu, &link, "check",
+ current);
+
+ /* attempt some read/write balancing by alternating between
+ * reads and writes in an object. The makes_rpc checks here
+ * would be redundant if we were getting read/write work items
+ * instead of objects. we don't want send_oap_rpc to drain a
+ * partial read pending queue when we're given this object to
+ * do io on writes while there are cache waiters */
+ osc_object_lock(osc);
+ if (osc_makes_rpc(cli, osc, OBD_BRW_WRITE)) {
+ rc = osc_send_write_rpc(env, cli, osc, pol);
+ if (rc < 0) {
+ CERROR("Write request failed with %d\n", rc);
+
+ /* osc_send_write_rpc failed, mostly because of
+ * memory pressure.
+ *
+ * It can't break here, because if:
+ * - a page was submitted by osc_io_submit, so
+ * page locked;
+ * - no request in flight
+ * - no subsequent request
+ * The system will be in live-lock state,
+ * because there is no chance to call
+ * osc_io_unplug() and osc_check_rpcs() any
+ * more. pdflush can't help in this case,
+ * because it might be blocked at grabbing
+ * the page lock as we mentioned.
+ *
+ * Anyway, continue to drain pages. */
+ /* break; */
+ }
+ }
+ if (osc_makes_rpc(cli, osc, OBD_BRW_READ)) {
+ rc = osc_send_read_rpc(env, cli, osc, pol);
+ if (rc < 0)
+ CERROR("Read request failed with %d\n", rc);
+ }
+ osc_object_unlock(osc);
+
+ osc_list_maint(cli, osc);
+ lu_object_ref_del_at(&obj->co_lu, &link, "check",
+ current);
+ cl_object_put(env, obj);
+
+ client_obd_list_lock(&cli->cl_loi_list_lock);
+ }
+}
+
+static int osc_io_unplug0(const struct lu_env *env, struct client_obd *cli,
+ struct osc_object *osc, pdl_policy_t pol, int async)
+{
+ int rc = 0;
+
+ if (osc != NULL && osc_list_maint(cli, osc) == 0)
+ return 0;
+
+ if (!async) {
+ /* disable osc_lru_shrink() temporarily to avoid
+ * potential stack overrun problem. LU-2859 */
+ atomic_inc(&cli->cl_lru_shrinkers);
+ client_obd_list_lock(&cli->cl_loi_list_lock);
+ osc_check_rpcs(env, cli, pol);
+ client_obd_list_unlock(&cli->cl_loi_list_lock);
+ atomic_dec(&cli->cl_lru_shrinkers);
+ } else {
+ CDEBUG(D_CACHE, "Queue writeback work for client %p.\n", cli);
+ LASSERT(cli->cl_writeback_work != NULL);
+ rc = ptlrpcd_queue_work(cli->cl_writeback_work);
+ }
+ return rc;
+}
+
+static int osc_io_unplug_async(const struct lu_env *env,
+ struct client_obd *cli, struct osc_object *osc)
+{
+ /* XXX: policy is no use actually. */
+ return osc_io_unplug0(env, cli, osc, PDL_POLICY_ROUND, 1);
+}
+
+void osc_io_unplug(const struct lu_env *env, struct client_obd *cli,
+ struct osc_object *osc, pdl_policy_t pol)
+{
+ (void)osc_io_unplug0(env, cli, osc, pol, 0);
+}
+
+int osc_prep_async_page(struct osc_object *osc, struct osc_page *ops,
+ struct page *page, loff_t offset)
+{
+ struct obd_export *exp = osc_export(osc);
+ struct osc_async_page *oap = &ops->ops_oap;
+
+ if (!page)
+ return cfs_size_round(sizeof(*oap));
+
+ oap->oap_magic = OAP_MAGIC;
+ oap->oap_cli = &exp->exp_obd->u.cli;
+ oap->oap_obj = osc;
+
+ oap->oap_page = page;
+ oap->oap_obj_off = offset;
+ LASSERT(!(offset & ~CFS_PAGE_MASK));
+
+ if (!client_is_remote(exp) && capable(CFS_CAP_SYS_RESOURCE))
+ oap->oap_brw_flags = OBD_BRW_NOQUOTA;
+
+ INIT_LIST_HEAD(&oap->oap_pending_item);
+ INIT_LIST_HEAD(&oap->oap_rpc_item);
+
+ spin_lock_init(&oap->oap_lock);
+ CDEBUG(D_INFO, "oap %p page %p obj off %llu\n",
+ oap, page, oap->oap_obj_off);
+ return 0;
+}
+
+int osc_queue_async_io(const struct lu_env *env, struct cl_io *io,
+ struct osc_page *ops)
+{
+ struct osc_io *oio = osc_env_io(env);
+ struct osc_extent *ext = NULL;
+ struct osc_async_page *oap = &ops->ops_oap;
+ struct client_obd *cli = oap->oap_cli;
+ struct osc_object *osc = oap->oap_obj;
+ pgoff_t index;
+ int grants = 0;
+ int brw_flags = OBD_BRW_ASYNC;
+ int cmd = OBD_BRW_WRITE;
+ int need_release = 0;
+ int rc = 0;
+
+ if (oap->oap_magic != OAP_MAGIC)
+ return -EINVAL;
+
+ if (cli->cl_import == NULL || cli->cl_import->imp_invalid)
+ return -EIO;
+
+ if (!list_empty(&oap->oap_pending_item) ||
+ !list_empty(&oap->oap_rpc_item))
+ return -EBUSY;
+
+ /* Set the OBD_BRW_SRVLOCK before the page is queued. */
+ brw_flags |= ops->ops_srvlock ? OBD_BRW_SRVLOCK : 0;
+ if (!client_is_remote(osc_export(osc)) &&
+ capable(CFS_CAP_SYS_RESOURCE)) {
+ brw_flags |= OBD_BRW_NOQUOTA;
+ cmd |= OBD_BRW_NOQUOTA;
+ }
+
+ /* check if the file's owner/group is over quota */
+ if (!(cmd & OBD_BRW_NOQUOTA)) {
+ struct cl_object *obj;
+ struct cl_attr *attr;
+ unsigned int qid[MAXQUOTAS];
+
+ obj = cl_object_top(&osc->oo_cl);
+ attr = &osc_env_info(env)->oti_attr;
+
+ cl_object_attr_lock(obj);
+ rc = cl_object_attr_get(env, obj, attr);
+ cl_object_attr_unlock(obj);
+
+ qid[USRQUOTA] = attr->cat_uid;
+ qid[GRPQUOTA] = attr->cat_gid;
+ if (rc == 0 && osc_quota_chkdq(cli, qid) == NO_QUOTA)
+ rc = -EDQUOT;
+ if (rc)
+ return rc;
+ }
+
+ oap->oap_cmd = cmd;
+ oap->oap_page_off = ops->ops_from;
+ oap->oap_count = ops->ops_to - ops->ops_from;
+ oap->oap_async_flags = 0;
+ oap->oap_brw_flags = brw_flags;
+
+ OSC_IO_DEBUG(osc, "oap %p page %p added for cmd %d\n",
+ oap, oap->oap_page, oap->oap_cmd & OBD_BRW_RWMASK);
+
+ index = oap2cl_page(oap)->cp_index;
+
+ /* Add this page into extent by the following steps:
+ * 1. if there exists an active extent for this IO, mostly this page
+ * can be added to the active extent and sometimes we need to
+ * expand extent to accommodate this page;
+ * 2. otherwise, a new extent will be allocated. */
+
+ ext = oio->oi_active;
+ if (ext != NULL && ext->oe_start <= index && ext->oe_max_end >= index) {
+ /* one chunk plus extent overhead must be enough to write this
+ * page */
+ grants = (1 << cli->cl_chunkbits) + cli->cl_extent_tax;
+ if (ext->oe_end >= index)
+ grants = 0;
+
+ /* it doesn't need any grant to dirty this page */
+ client_obd_list_lock(&cli->cl_loi_list_lock);
+ rc = osc_enter_cache_try(cli, oap, grants, 0);
+ client_obd_list_unlock(&cli->cl_loi_list_lock);
+ if (rc == 0) { /* try failed */
+ grants = 0;
+ need_release = 1;
+ } else if (ext->oe_end < index) {
+ int tmp = grants;
+ /* try to expand this extent */
+ rc = osc_extent_expand(ext, index, &tmp);
+ if (rc < 0) {
+ need_release = 1;
+ /* don't free reserved grant */
+ } else {
+ OSC_EXTENT_DUMP(D_CACHE, ext,
+ "expanded for %lu.\n", index);
+ osc_unreserve_grant(cli, grants, tmp);
+ grants = 0;
+ }
+ }
+ rc = 0;
+ } else if (ext != NULL) {
+ /* index is located outside of active extent */
+ need_release = 1;
+ }
+ if (need_release) {
+ osc_extent_release(env, ext);
+ oio->oi_active = NULL;
+ ext = NULL;
+ }
+
+ if (ext == NULL) {
+ int tmp = (1 << cli->cl_chunkbits) + cli->cl_extent_tax;
+
+ /* try to find new extent to cover this page */
+ LASSERT(oio->oi_active == NULL);
+ /* we may have allocated grant for this page if we failed
+ * to expand the previous active extent. */
+ LASSERT(ergo(grants > 0, grants >= tmp));
+
+ rc = 0;
+ if (grants == 0) {
+ /* we haven't allocated grant for this page. */
+ rc = osc_enter_cache(env, cli, oap, tmp);
+ if (rc == 0)
+ grants = tmp;
+ }
+
+ tmp = grants;
+ if (rc == 0) {
+ ext = osc_extent_find(env, osc, index, &tmp);
+ if (IS_ERR(ext)) {
+ LASSERT(tmp == grants);
+ osc_exit_cache(cli, oap);
+ rc = PTR_ERR(ext);
+ ext = NULL;
+ } else {
+ oio->oi_active = ext;
+ }
+ }
+ if (grants > 0)
+ osc_unreserve_grant(cli, grants, tmp);
+ }
+
+ LASSERT(ergo(rc == 0, ext != NULL));
+ if (ext != NULL) {
+ EASSERTF(ext->oe_end >= index && ext->oe_start <= index,
+ ext, "index = %lu.\n", index);
+ LASSERT((oap->oap_brw_flags & OBD_BRW_FROM_GRANT) != 0);
+
+ osc_object_lock(osc);
+ if (ext->oe_nr_pages == 0)
+ ext->oe_srvlock = ops->ops_srvlock;
+ else
+ LASSERT(ext->oe_srvlock == ops->ops_srvlock);
+ ++ext->oe_nr_pages;
+ list_add_tail(&oap->oap_pending_item, &ext->oe_pages);
+ osc_object_unlock(osc);
+ }
+ return rc;
+}
+
+int osc_teardown_async_page(const struct lu_env *env,
+ struct osc_object *obj, struct osc_page *ops)
+{
+ struct osc_async_page *oap = &ops->ops_oap;
+ struct osc_extent *ext = NULL;
+ int rc = 0;
+
+ LASSERT(oap->oap_magic == OAP_MAGIC);
+
+ CDEBUG(D_INFO, "teardown oap %p page %p at index %lu.\n",
+ oap, ops, oap2cl_page(oap)->cp_index);
+
+ osc_object_lock(obj);
+ if (!list_empty(&oap->oap_rpc_item)) {
+ CDEBUG(D_CACHE, "oap %p is not in cache.\n", oap);
+ rc = -EBUSY;
+ } else if (!list_empty(&oap->oap_pending_item)) {
+ ext = osc_extent_lookup(obj, oap2cl_page(oap)->cp_index);
+ /* only truncated pages are allowed to be taken out.
+ * See osc_extent_truncate() and osc_cache_truncate_start()
+ * for details. */
+ if (ext != NULL && ext->oe_state != OES_TRUNC) {
+ OSC_EXTENT_DUMP(D_ERROR, ext, "trunc at %lu.\n",
+ oap2cl_page(oap)->cp_index);
+ rc = -EBUSY;
+ }
+ }
+ osc_object_unlock(obj);
+ if (ext != NULL)
+ osc_extent_put(env, ext);
+ return rc;
+}
+
+/**
+ * This is called when a page is picked up by kernel to write out.
+ *
+ * We should find out the corresponding extent and add the whole extent
+ * into urgent list. The extent may be being truncated or used, handle it
+ * carefully.
+ */
+int osc_flush_async_page(const struct lu_env *env, struct cl_io *io,
+ struct osc_page *ops)
+{
+ struct osc_extent *ext = NULL;
+ struct osc_object *obj = cl2osc(ops->ops_cl.cpl_obj);
+ struct cl_page *cp = ops->ops_cl.cpl_page;
+ pgoff_t index = cp->cp_index;
+ struct osc_async_page *oap = &ops->ops_oap;
+ bool unplug = false;
+ int rc = 0;
+
+ osc_object_lock(obj);
+ ext = osc_extent_lookup(obj, index);
+ if (ext == NULL) {
+ osc_extent_tree_dump(D_ERROR, obj);
+ LASSERTF(0, "page index %lu is NOT covered.\n", index);
+ }
+
+ switch (ext->oe_state) {
+ case OES_RPC:
+ case OES_LOCK_DONE:
+ CL_PAGE_DEBUG(D_ERROR, env, cl_page_top(cp),
+ "flush an in-rpc page?\n");
+ LASSERT(0);
+ break;
+ case OES_LOCKING:
+ /* If we know this extent is being written out, we should abort
+ * so that the writer can make this page ready. Otherwise, there
+ * exists a deadlock problem because other process can wait for
+ * page writeback bit holding page lock; and meanwhile in
+ * vvp_page_make_ready(), we need to grab page lock before
+ * really sending the RPC. */
+ case OES_TRUNC:
+ /* race with truncate, page will be redirtied */
+ case OES_ACTIVE:
+ /* The extent is active so we need to abort and let the caller
+ * re-dirty the page. If we continued on here, and we were the
+ * one making the extent active, we could deadlock waiting for
+ * the page writeback to clear but it won't because the extent
+ * is active and won't be written out. */
+ rc = -EAGAIN;
+ goto out;
+ default:
+ break;
+ }
+
+ rc = cl_page_prep(env, io, cl_page_top(cp), CRT_WRITE);
+ if (rc)
+ goto out;
+
+ spin_lock(&oap->oap_lock);
+ oap->oap_async_flags |= ASYNC_READY|ASYNC_URGENT;
+ spin_unlock(&oap->oap_lock);
+
+ if (memory_pressure_get())
+ ext->oe_memalloc = 1;
+
+ ext->oe_urgent = 1;
+ if (ext->oe_state == OES_CACHE) {
+ OSC_EXTENT_DUMP(D_CACHE, ext,
+ "flush page %p make it urgent.\n", oap);
+ if (list_empty(&ext->oe_link))
+ list_add_tail(&ext->oe_link, &obj->oo_urgent_exts);
+ unplug = true;
+ }
+ rc = 0;
+
+out:
+ osc_object_unlock(obj);
+ osc_extent_put(env, ext);
+ if (unplug)
+ osc_io_unplug_async(env, osc_cli(obj), obj);
+ return rc;
+}
+
+/**
+ * this is called when a sync waiter receives an interruption. Its job is to
+ * get the caller woken as soon as possible. If its page hasn't been put in an
+ * rpc yet it can dequeue immediately. Otherwise it has to mark the rpc as
+ * desiring interruption which will forcefully complete the rpc once the rpc
+ * has timed out.
+ */
+int osc_cancel_async_page(const struct lu_env *env, struct osc_page *ops)
+{
+ struct osc_async_page *oap = &ops->ops_oap;
+ struct osc_object *obj = oap->oap_obj;
+ struct client_obd *cli = osc_cli(obj);
+ struct osc_extent *ext;
+ struct osc_extent *found = NULL;
+ struct list_head *plist;
+ pgoff_t index = oap2cl_page(oap)->cp_index;
+ int rc = -EBUSY;
+ int cmd;
+
+ LASSERT(!oap->oap_interrupted);
+ oap->oap_interrupted = 1;
+
+ /* Find out the caching extent */
+ osc_object_lock(obj);
+ if (oap->oap_cmd & OBD_BRW_WRITE) {
+ plist = &obj->oo_urgent_exts;
+ cmd = OBD_BRW_WRITE;
+ } else {
+ plist = &obj->oo_reading_exts;
+ cmd = OBD_BRW_READ;
+ }
+ list_for_each_entry(ext, plist, oe_link) {
+ if (ext->oe_start <= index && ext->oe_end >= index) {
+ LASSERT(ext->oe_state == OES_LOCK_DONE);
+ /* For OES_LOCK_DONE state extent, it has already held
+ * a refcount for RPC. */
+ found = osc_extent_get(ext);
+ break;
+ }
+ }
+ if (found != NULL) {
+ list_del_init(&found->oe_link);
+ osc_update_pending(obj, cmd, -found->oe_nr_pages);
+ osc_object_unlock(obj);
+
+ osc_extent_finish(env, found, 0, -EINTR);
+ osc_extent_put(env, found);
+ rc = 0;
+ } else {
+ osc_object_unlock(obj);
+ /* ok, it's been put in an rpc. only one oap gets a request
+ * reference */
+ if (oap->oap_request != NULL) {
+ ptlrpc_mark_interrupted(oap->oap_request);
+ ptlrpcd_wake(oap->oap_request);
+ ptlrpc_req_finished(oap->oap_request);
+ oap->oap_request = NULL;
+ }
+ }
+
+ osc_list_maint(cli, obj);
+ return rc;
+}
+
+int osc_queue_sync_pages(const struct lu_env *env, struct osc_object *obj,
+ struct list_head *list, int cmd, int brw_flags)
+{
+ struct client_obd *cli = osc_cli(obj);
+ struct osc_extent *ext;
+ struct osc_async_page *oap, *tmp;
+ int page_count = 0;
+ int mppr = cli->cl_max_pages_per_rpc;
+ pgoff_t start = CL_PAGE_EOF;
+ pgoff_t end = 0;
+
+ list_for_each_entry(oap, list, oap_pending_item) {
+ struct cl_page *cp = oap2cl_page(oap);
+ if (cp->cp_index > end)
+ end = cp->cp_index;
+ if (cp->cp_index < start)
+ start = cp->cp_index;
+ ++page_count;
+ mppr <<= (page_count > mppr);
+ }
+
+ ext = osc_extent_alloc(obj);
+ if (ext == NULL) {
+ list_for_each_entry_safe(oap, tmp, list, oap_pending_item) {
+ list_del_init(&oap->oap_pending_item);
+ osc_ap_completion(env, cli, oap, 0, -ENOMEM);
+ }
+ return -ENOMEM;
+ }
+
+ ext->oe_rw = !!(cmd & OBD_BRW_READ);
+ ext->oe_urgent = 1;
+ ext->oe_start = start;
+ ext->oe_end = ext->oe_max_end = end;
+ ext->oe_obj = obj;
+ ext->oe_srvlock = !!(brw_flags & OBD_BRW_SRVLOCK);
+ ext->oe_nr_pages = page_count;
+ ext->oe_mppr = mppr;
+ list_splice_init(list, &ext->oe_pages);
+
+ osc_object_lock(obj);
+ /* Reuse the initial refcount for RPC, don't drop it */
+ osc_extent_state_set(ext, OES_LOCK_DONE);
+ if (cmd & OBD_BRW_WRITE) {
+ list_add_tail(&ext->oe_link, &obj->oo_urgent_exts);
+ osc_update_pending(obj, OBD_BRW_WRITE, page_count);
+ } else {
+ list_add_tail(&ext->oe_link, &obj->oo_reading_exts);
+ osc_update_pending(obj, OBD_BRW_READ, page_count);
+ }
+ osc_object_unlock(obj);
+
+ osc_io_unplug_async(env, cli, obj);
+ return 0;
+}
+
+/**
+ * Called by osc_io_setattr_start() to freeze and destroy covering extents.
+ */
+int osc_cache_truncate_start(const struct lu_env *env, struct osc_io *oio,
+ struct osc_object *obj, __u64 size)
+{
+ struct client_obd *cli = osc_cli(obj);
+ struct osc_extent *ext;
+ struct osc_extent *waiting = NULL;
+ pgoff_t index;
+ LIST_HEAD(list);
+ int result = 0;
+ bool partial;
+
+ /* pages with index greater or equal to index will be truncated. */
+ index = cl_index(osc2cl(obj), size);
+ partial = size > cl_offset(osc2cl(obj), index);
+
+again:
+ osc_object_lock(obj);
+ ext = osc_extent_search(obj, index);
+ if (ext == NULL)
+ ext = first_extent(obj);
+ else if (ext->oe_end < index)
+ ext = next_extent(ext);
+ while (ext != NULL) {
+ EASSERT(ext->oe_state != OES_TRUNC, ext);
+
+ if (ext->oe_state > OES_CACHE || ext->oe_urgent) {
+ /* if ext is in urgent state, it means there must exist
+ * a page already having been flushed by write_page().
+ * We have to wait for this extent because we can't
+ * truncate that page. */
+ LASSERT(!ext->oe_hp);
+ OSC_EXTENT_DUMP(D_CACHE, ext,
+ "waiting for busy extent\n");
+ waiting = osc_extent_get(ext);
+ break;
+ }
+
+ OSC_EXTENT_DUMP(D_CACHE, ext, "try to trunc:%llu.\n", size);
+
+ osc_extent_get(ext);
+ if (ext->oe_state == OES_ACTIVE) {
+ /* though we grab inode mutex for write path, but we
+ * release it before releasing extent(in osc_io_end()),
+ * so there is a race window that an extent is still
+ * in OES_ACTIVE when truncate starts. */
+ LASSERT(!ext->oe_trunc_pending);
+ ext->oe_trunc_pending = 1;
+ } else {
+ EASSERT(ext->oe_state == OES_CACHE, ext);
+ osc_extent_state_set(ext, OES_TRUNC);
+ osc_update_pending(obj, OBD_BRW_WRITE,
+ -ext->oe_nr_pages);
+ }
+ EASSERT(list_empty(&ext->oe_link), ext);
+ list_add_tail(&ext->oe_link, &list);
+
+ ext = next_extent(ext);
+ }
+ osc_object_unlock(obj);
+
+ osc_list_maint(cli, obj);
+
+ while (!list_empty(&list)) {
+ int rc;
+
+ ext = list_entry(list.next, struct osc_extent, oe_link);
+ list_del_init(&ext->oe_link);
+
+ /* extent may be in OES_ACTIVE state because inode mutex
+ * is released before osc_io_end() in file write case */
+ if (ext->oe_state != OES_TRUNC)
+ osc_extent_wait(env, ext, OES_TRUNC);
+
+ rc = osc_extent_truncate(ext, index, partial);
+ if (rc < 0) {
+ if (result == 0)
+ result = rc;
+
+ OSC_EXTENT_DUMP(D_ERROR, ext,
+ "truncate error %d\n", rc);
+ } else if (ext->oe_nr_pages == 0) {
+ osc_extent_remove(ext);
+ } else {
+ /* this must be an overlapped extent which means only
+ * part of pages in this extent have been truncated.
+ */
+ EASSERTF(ext->oe_start <= index, ext,
+ "trunc index = %lu/%d.\n", index, partial);
+ /* fix index to skip this partially truncated extent */
+ index = ext->oe_end + 1;
+ partial = false;
+
+ /* we need to hold this extent in OES_TRUNC state so
+ * that no writeback will happen. This is to avoid
+ * BUG 17397. */
+ LASSERT(oio->oi_trunc == NULL);
+ oio->oi_trunc = osc_extent_get(ext);
+ OSC_EXTENT_DUMP(D_CACHE, ext,
+ "trunc at %llu\n", size);
+ }
+ osc_extent_put(env, ext);
+ }
+ if (waiting != NULL) {
+ int rc;
+
+ /* ignore the result of osc_extent_wait the write initiator
+ * should take care of it. */
+ rc = osc_extent_wait(env, waiting, OES_INV);
+ if (rc < 0)
+ OSC_EXTENT_DUMP(D_CACHE, waiting, "error: %d.\n", rc);
+
+ osc_extent_put(env, waiting);
+ waiting = NULL;
+ goto again;
+ }
+ return result;
+}
+
+/**
+ * Called after osc_io_setattr_end to add oio->oi_trunc back to cache.
+ */
+void osc_cache_truncate_end(const struct lu_env *env, struct osc_io *oio,
+ struct osc_object *obj)
+{
+ struct osc_extent *ext = oio->oi_trunc;
+
+ oio->oi_trunc = NULL;
+ if (ext != NULL) {
+ bool unplug = false;
+
+ EASSERT(ext->oe_nr_pages > 0, ext);
+ EASSERT(ext->oe_state == OES_TRUNC, ext);
+ EASSERT(!ext->oe_urgent, ext);
+
+ OSC_EXTENT_DUMP(D_CACHE, ext, "trunc -> cache.\n");
+ osc_object_lock(obj);
+ osc_extent_state_set(ext, OES_CACHE);
+ if (ext->oe_fsync_wait && !ext->oe_urgent) {
+ ext->oe_urgent = 1;
+ list_move_tail(&ext->oe_link, &obj->oo_urgent_exts);
+ unplug = true;
+ }
+ osc_update_pending(obj, OBD_BRW_WRITE, ext->oe_nr_pages);
+ osc_object_unlock(obj);
+ osc_extent_put(env, ext);
+
+ if (unplug)
+ osc_io_unplug_async(env, osc_cli(obj), obj);
+ }
+}
+
+/**
+ * Wait for extents in a specific range to be written out.
+ * The caller must have called osc_cache_writeback_range() to issue IO
+ * otherwise it will take a long time for this function to finish.
+ *
+ * Caller must hold inode_mutex , or cancel exclusive dlm lock so that
+ * nobody else can dirty this range of file while we're waiting for
+ * extents to be written.
+ */
+int osc_cache_wait_range(const struct lu_env *env, struct osc_object *obj,
+ pgoff_t start, pgoff_t end)
+{
+ struct osc_extent *ext;
+ pgoff_t index = start;
+ int result = 0;
+
+again:
+ osc_object_lock(obj);
+ ext = osc_extent_search(obj, index);
+ if (ext == NULL)
+ ext = first_extent(obj);
+ else if (ext->oe_end < index)
+ ext = next_extent(ext);
+ while (ext != NULL) {
+ int rc;
+
+ if (ext->oe_start > end)
+ break;
+
+ if (!ext->oe_fsync_wait) {
+ ext = next_extent(ext);
+ continue;
+ }
+
+ EASSERT(ergo(ext->oe_state == OES_CACHE,
+ ext->oe_hp || ext->oe_urgent), ext);
+ EASSERT(ergo(ext->oe_state == OES_ACTIVE,
+ !ext->oe_hp && ext->oe_urgent), ext);
+
+ index = ext->oe_end + 1;
+ osc_extent_get(ext);
+ osc_object_unlock(obj);
+
+ rc = osc_extent_wait(env, ext, OES_INV);
+ if (result == 0)
+ result = rc;
+ osc_extent_put(env, ext);
+ goto again;
+ }
+ osc_object_unlock(obj);
+
+ OSC_IO_DEBUG(obj, "sync file range.\n");
+ return result;
+}
+
+/**
+ * Called to write out a range of osc object.
+ *
+ * @hp : should be set this is caused by lock cancel;
+ * @discard: is set if dirty pages should be dropped - file will be deleted or
+ * truncated, this implies there is no partially discarding extents.
+ *
+ * Return how many pages will be issued, or error code if error occurred.
+ */
+int osc_cache_writeback_range(const struct lu_env *env, struct osc_object *obj,
+ pgoff_t start, pgoff_t end, int hp, int discard)
+{
+ struct osc_extent *ext;
+ LIST_HEAD(discard_list);
+ bool unplug = false;
+ int result = 0;
+
+ osc_object_lock(obj);
+ ext = osc_extent_search(obj, start);
+ if (ext == NULL)
+ ext = first_extent(obj);
+ else if (ext->oe_end < start)
+ ext = next_extent(ext);
+ while (ext != NULL) {
+ if (ext->oe_start > end)
+ break;
+
+ ext->oe_fsync_wait = 1;
+ switch (ext->oe_state) {
+ case OES_CACHE:
+ result += ext->oe_nr_pages;
+ if (!discard) {
+ struct list_head *list = NULL;
+ if (hp) {
+ EASSERT(!ext->oe_hp, ext);
+ ext->oe_hp = 1;
+ list = &obj->oo_hp_exts;
+ } else if (!ext->oe_urgent) {
+ ext->oe_urgent = 1;
+ list = &obj->oo_urgent_exts;
+ }
+ if (list != NULL)
+ list_move_tail(&ext->oe_link, list);
+ unplug = true;
+ } else {
+ /* the only discarder is lock cancelling, so
+ * [start, end] must contain this extent */
+ EASSERT(ext->oe_start >= start &&
+ ext->oe_max_end <= end, ext);
+ osc_extent_state_set(ext, OES_LOCKING);
+ ext->oe_owner = current;
+ list_move_tail(&ext->oe_link,
+ &discard_list);
+ osc_update_pending(obj, OBD_BRW_WRITE,
+ -ext->oe_nr_pages);
+ }
+ break;
+ case OES_ACTIVE:
+ /* It's pretty bad to wait for ACTIVE extents, because
+ * we don't know how long we will wait for it to be
+ * flushed since it may be blocked at awaiting more
+ * grants. We do this for the correctness of fsync. */
+ LASSERT(hp == 0 && discard == 0);
+ ext->oe_urgent = 1;
+ break;
+ case OES_TRUNC:
+ /* this extent is being truncated, can't do anything
+ * for it now. it will be set to urgent after truncate
+ * is finished in osc_cache_truncate_end(). */
+ default:
+ break;
+ }
+ ext = next_extent(ext);
+ }
+ osc_object_unlock(obj);
+
+ LASSERT(ergo(!discard, list_empty(&discard_list)));
+ if (!list_empty(&discard_list)) {
+ struct osc_extent *tmp;
+ int rc;
+
+ osc_list_maint(osc_cli(obj), obj);
+ list_for_each_entry_safe(ext, tmp, &discard_list, oe_link) {
+ list_del_init(&ext->oe_link);
+ EASSERT(ext->oe_state == OES_LOCKING, ext);
+
+ /* Discard caching pages. We don't actually write this
+ * extent out but we complete it as if we did. */
+ rc = osc_extent_make_ready(env, ext);
+ if (unlikely(rc < 0)) {
+ OSC_EXTENT_DUMP(D_ERROR, ext,
+ "make_ready returned %d\n", rc);
+ if (result >= 0)
+ result = rc;
+ }
+
+ /* finish the extent as if the pages were sent */
+ osc_extent_finish(env, ext, 0, 0);
+ }
+ }
+
+ if (unplug)
+ osc_io_unplug(env, osc_cli(obj), obj, PDL_POLICY_ROUND);
+
+ if (hp || discard) {
+ int rc;
+ rc = osc_cache_wait_range(env, obj, start, end);
+ if (result >= 0 && rc < 0)
+ result = rc;
+ }
+
+ OSC_IO_DEBUG(obj, "cache page out.\n");
+ return result;
+}
+
+/** @} osc */
diff --git a/drivers/staging/lustre/lustre/osc/osc_cl_internal.h b/drivers/staging/lustre/lustre/osc/osc_cl_internal.h
new file mode 100644
index 000000000..365b2787b
--- /dev/null
+++ b/drivers/staging/lustre/lustre/osc/osc_cl_internal.h
@@ -0,0 +1,685 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2012, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ *
+ * Internal interfaces of OSC layer.
+ *
+ * Author: Nikita Danilov <nikita.danilov@sun.com>
+ * Author: Jinshan Xiong <jinshan.xiong@whamcloud.com>
+ */
+
+#ifndef OSC_CL_INTERNAL_H
+#define OSC_CL_INTERNAL_H
+
+#include "../../include/linux/libcfs/libcfs.h"
+
+#include "../include/obd.h"
+/* osc_build_res_name() */
+#include "../include/cl_object.h"
+#include "../include/lclient.h"
+#include "osc_internal.h"
+
+/** \defgroup osc osc
+ * @{
+ */
+
+struct osc_extent;
+
+/**
+ * State maintained by osc layer for each IO context.
+ */
+struct osc_io {
+ /** super class */
+ struct cl_io_slice oi_cl;
+ /** true if this io is lockless. */
+ int oi_lockless;
+ /** active extents, we know how many bytes is going to be written,
+ * so having an active extent will prevent it from being fragmented */
+ struct osc_extent *oi_active;
+ /** partially truncated extent, we need to hold this extent to prevent
+ * page writeback from happening. */
+ struct osc_extent *oi_trunc;
+
+ struct obd_info oi_info;
+ struct obdo oi_oa;
+ struct osc_async_cbargs {
+ bool opc_rpc_sent;
+ int opc_rc;
+ struct completion opc_sync;
+ } oi_cbarg;
+};
+
+/**
+ * State of transfer for osc.
+ */
+struct osc_req {
+ struct cl_req_slice or_cl;
+};
+
+/**
+ * State maintained by osc layer for the duration of a system call.
+ */
+struct osc_session {
+ struct osc_io os_io;
+};
+
+#define OTI_PVEC_SIZE 64
+struct osc_thread_info {
+ struct ldlm_res_id oti_resname;
+ ldlm_policy_data_t oti_policy;
+ struct cl_lock_descr oti_descr;
+ struct cl_attr oti_attr;
+ struct lustre_handle oti_handle;
+ struct cl_page_list oti_plist;
+ struct cl_io oti_io;
+ struct cl_page *oti_pvec[OTI_PVEC_SIZE];
+};
+
+struct osc_object {
+ struct cl_object oo_cl;
+ struct lov_oinfo *oo_oinfo;
+ /**
+ * True if locking against this stripe got -EUSERS.
+ */
+ int oo_contended;
+ unsigned long oo_contention_time;
+ /**
+ * List of pages in transfer.
+ */
+ struct list_head oo_inflight[CRT_NR];
+ /**
+ * Lock, protecting ccc_object::cob_inflight, because a seat-belt is
+ * locked during take-off and landing.
+ */
+ spinlock_t oo_seatbelt;
+
+ /**
+ * used by the osc to keep track of what objects to build into rpcs.
+ * Protected by client_obd->cli_loi_list_lock.
+ */
+ struct list_head oo_ready_item;
+ struct list_head oo_hp_ready_item;
+ struct list_head oo_write_item;
+ struct list_head oo_read_item;
+
+ /**
+ * extent is a red black tree to manage (async) dirty pages.
+ */
+ struct rb_root oo_root;
+ /**
+ * Manage write(dirty) extents.
+ */
+ struct list_head oo_hp_exts; /* list of hp extents */
+ struct list_head oo_urgent_exts; /* list of writeback extents */
+ struct list_head oo_rpc_exts;
+
+ struct list_head oo_reading_exts;
+
+ atomic_t oo_nr_reads;
+ atomic_t oo_nr_writes;
+
+ /** Protect extent tree. Will be used to protect
+ * oo_{read|write}_pages soon. */
+ spinlock_t oo_lock;
+};
+
+static inline void osc_object_lock(struct osc_object *obj)
+{
+ spin_lock(&obj->oo_lock);
+}
+
+static inline int osc_object_trylock(struct osc_object *obj)
+{
+ return spin_trylock(&obj->oo_lock);
+}
+
+static inline void osc_object_unlock(struct osc_object *obj)
+{
+ spin_unlock(&obj->oo_lock);
+}
+
+static inline int osc_object_is_locked(struct osc_object *obj)
+{
+#if defined(CONFIG_SMP) || defined(CONFIG_DEBUG_SPINLOCK)
+ return spin_is_locked(&obj->oo_lock);
+#else
+ /*
+ * It is not perfect to return true all the time.
+ * But since this function is only used for assertion
+ * and checking, it seems OK.
+ */
+ return 1;
+#endif
+}
+
+/*
+ * Lock "micro-states" for osc layer.
+ */
+enum osc_lock_state {
+ OLS_NEW,
+ OLS_ENQUEUED,
+ OLS_UPCALL_RECEIVED,
+ OLS_GRANTED,
+ OLS_RELEASED,
+ OLS_BLOCKED,
+ OLS_CANCELLED
+};
+
+/**
+ * osc-private state of cl_lock.
+ *
+ * Interaction with DLM.
+ *
+ * CLIO enqueues all DLM locks through ptlrpcd (that is, in "async" mode).
+ *
+ * Once receive upcall is invoked, osc_lock remembers a handle of DLM lock in
+ * osc_lock::ols_handle and a pointer to that lock in osc_lock::ols_lock.
+ *
+ * This pointer is protected through a reference, acquired by
+ * osc_lock_upcall0(). Also, an additional reference is acquired by
+ * ldlm_lock_addref() call protecting the lock from cancellation, until
+ * osc_lock_unuse() releases it.
+ *
+ * Below is a description of how lock references are acquired and released
+ * inside of DLM.
+ *
+ * - When new lock is created and enqueued to the server (ldlm_cli_enqueue())
+ * - ldlm_lock_create()
+ * - ldlm_lock_new(): initializes a lock with 2 references. One for
+ * the caller (released when reply from the server is received, or on
+ * error), and another for the hash table.
+ * - ldlm_lock_addref_internal(): protects the lock from cancellation.
+ *
+ * - When reply is received from the server (osc_enqueue_interpret())
+ * - ldlm_cli_enqueue_fini()
+ * - LDLM_LOCK_PUT(): releases caller reference acquired by
+ * ldlm_lock_new().
+ * - if (rc != 0)
+ * ldlm_lock_decref(): error case: matches ldlm_cli_enqueue().
+ * - ldlm_lock_decref(): for async locks, matches ldlm_cli_enqueue().
+ *
+ * - When lock is being cancelled (ldlm_lock_cancel())
+ * - ldlm_lock_destroy()
+ * - LDLM_LOCK_PUT(): releases hash-table reference acquired by
+ * ldlm_lock_new().
+ *
+ * osc_lock is detached from ldlm_lock by osc_lock_detach() that is called
+ * either when lock is cancelled (osc_lock_blocking()), or when locks is
+ * deleted without cancellation (e.g., from cl_locks_prune()). In the latter
+ * case ldlm lock remains in memory, and can be re-attached to osc_lock in the
+ * future.
+ */
+struct osc_lock {
+ struct cl_lock_slice ols_cl;
+ /** underlying DLM lock */
+ struct ldlm_lock *ols_lock;
+ /** lock value block */
+ struct ost_lvb ols_lvb;
+ /** DLM flags with which osc_lock::ols_lock was enqueued */
+ __u64 ols_flags;
+ /** osc_lock::ols_lock handle */
+ struct lustre_handle ols_handle;
+ struct ldlm_enqueue_info ols_einfo;
+ enum osc_lock_state ols_state;
+
+ /**
+ * How many pages are using this lock for io, currently only used by
+ * read-ahead. If non-zero, the underlying dlm lock won't be cancelled
+ * during recovery to avoid deadlock. see bz16774.
+ *
+ * \see osc_page::ops_lock
+ * \see osc_page_addref_lock(), osc_page_putref_lock()
+ */
+ atomic_t ols_pageref;
+
+ /**
+ * true, if ldlm_lock_addref() was called against
+ * osc_lock::ols_lock. This is used for sanity checking.
+ *
+ * \see osc_lock::ols_has_ref
+ */
+ unsigned ols_hold :1,
+ /**
+ * this is much like osc_lock::ols_hold, except that this bit is
+ * cleared _after_ reference in released in osc_lock_unuse(). This
+ * fine distinction is needed because:
+ *
+ * - if ldlm lock still has a reference, osc_ast_data_get() needs
+ * to return associated cl_lock (so that a flag is needed that is
+ * cleared after ldlm_lock_decref() returned), and
+ *
+ * - ldlm_lock_decref() can invoke blocking ast (for a
+ * LDLM_FL_CBPENDING lock), and osc_lock functions like
+ * osc_lock_cancel() called from there need to know whether to
+ * release lock reference (so that a flag is needed that is
+ * cleared before ldlm_lock_decref() is called).
+ */
+ ols_has_ref:1,
+ /**
+ * inherit the lockless attribute from top level cl_io.
+ * If true, osc_lock_enqueue is able to tolerate the -EUSERS error.
+ */
+ ols_locklessable:1,
+ /**
+ * set by osc_lock_use() to wait until blocking AST enters into
+ * osc_ldlm_blocking_ast0(), so that cl_lock mutex can be used for
+ * further synchronization.
+ */
+ ols_ast_wait:1,
+ /**
+ * If the data of this lock has been flushed to server side.
+ */
+ ols_flush:1,
+ /**
+ * if set, the osc_lock is a glimpse lock. For glimpse locks, we treat
+ * the EVAVAIL error as tolerable, this will make upper logic happy
+ * to wait all glimpse locks to each OSTs to be completed.
+ * Glimpse lock converts to normal lock if the server lock is
+ * granted.
+ * Glimpse lock should be destroyed immediately after use.
+ */
+ ols_glimpse:1,
+ /**
+ * For async glimpse lock.
+ */
+ ols_agl:1;
+ /**
+ * IO that owns this lock. This field is used for a dead-lock
+ * avoidance by osc_lock_enqueue_wait().
+ *
+ * XXX: unfortunately, the owner of a osc_lock is not unique,
+ * the lock may have multiple users, if the lock is granted and
+ * then matched.
+ */
+ struct osc_io *ols_owner;
+};
+
+
+/**
+ * Page state private for osc layer.
+ */
+struct osc_page {
+ struct cl_page_slice ops_cl;
+ /**
+ * Page queues used by osc to detect when RPC can be formed.
+ */
+ struct osc_async_page ops_oap;
+ /**
+ * An offset within page from which next transfer starts. This is used
+ * by cl_page_clip() to submit partial page transfers.
+ */
+ int ops_from;
+ /**
+ * An offset within page at which next transfer ends.
+ *
+ * \see osc_page::ops_from.
+ */
+ int ops_to;
+ /**
+ * Boolean, true iff page is under transfer. Used for sanity checking.
+ */
+ unsigned ops_transfer_pinned:1,
+ /**
+ * True for a `temporary page' created by read-ahead code, probably
+ * outside of any DLM lock.
+ */
+ ops_temp:1,
+ /**
+ * in LRU?
+ */
+ ops_in_lru:1,
+ /**
+ * Set if the page must be transferred with OBD_BRW_SRVLOCK.
+ */
+ ops_srvlock:1;
+ union {
+ /**
+ * lru page list. ops_inflight and ops_lru are exclusive so
+ * that they can share the same data.
+ */
+ struct list_head ops_lru;
+ /**
+ * Linkage into a per-osc_object list of pages in flight. For
+ * debugging.
+ */
+ struct list_head ops_inflight;
+ };
+ /**
+ * Thread that submitted this page for transfer. For debugging.
+ */
+ struct task_struct *ops_submitter;
+ /**
+ * Submit time - the time when the page is starting RPC. For debugging.
+ */
+ unsigned long ops_submit_time;
+
+ /**
+ * A lock of which we hold a reference covers this page. Only used by
+ * read-ahead: for a readahead page, we hold it's covering lock to
+ * prevent it from being canceled during recovery.
+ *
+ * \see osc_lock::ols_pageref
+ * \see osc_page_addref_lock(), osc_page_putref_lock().
+ */
+ struct cl_lock *ops_lock;
+};
+
+extern struct kmem_cache *osc_lock_kmem;
+extern struct kmem_cache *osc_object_kmem;
+extern struct kmem_cache *osc_thread_kmem;
+extern struct kmem_cache *osc_session_kmem;
+extern struct kmem_cache *osc_req_kmem;
+extern struct kmem_cache *osc_extent_kmem;
+
+extern struct lu_device_type osc_device_type;
+extern struct lu_context_key osc_key;
+extern struct lu_context_key osc_session_key;
+
+#define OSC_FLAGS (ASYNC_URGENT|ASYNC_READY)
+
+int osc_lock_init(const struct lu_env *env,
+ struct cl_object *obj, struct cl_lock *lock,
+ const struct cl_io *io);
+int osc_io_init (const struct lu_env *env,
+ struct cl_object *obj, struct cl_io *io);
+int osc_req_init (const struct lu_env *env, struct cl_device *dev,
+ struct cl_req *req);
+struct lu_object *osc_object_alloc(const struct lu_env *env,
+ const struct lu_object_header *hdr,
+ struct lu_device *dev);
+int osc_page_init(const struct lu_env *env, struct cl_object *obj,
+ struct cl_page *page, struct page *vmpage);
+
+void osc_index2policy (ldlm_policy_data_t *policy, const struct cl_object *obj,
+ pgoff_t start, pgoff_t end);
+int osc_lvb_print (const struct lu_env *env, void *cookie,
+ lu_printer_t p, const struct ost_lvb *lvb);
+
+void osc_page_submit(const struct lu_env *env, struct osc_page *opg,
+ enum cl_req_type crt, int brw_flags);
+int osc_cancel_async_page(const struct lu_env *env, struct osc_page *ops);
+int osc_set_async_flags(struct osc_object *obj, struct osc_page *opg,
+ u32 async_flags);
+int osc_prep_async_page(struct osc_object *osc, struct osc_page *ops,
+ struct page *page, loff_t offset);
+int osc_queue_async_io(const struct lu_env *env, struct cl_io *io,
+ struct osc_page *ops);
+int osc_teardown_async_page(const struct lu_env *env, struct osc_object *obj,
+ struct osc_page *ops);
+int osc_flush_async_page(const struct lu_env *env, struct cl_io *io,
+ struct osc_page *ops);
+int osc_queue_sync_pages(const struct lu_env *env, struct osc_object *obj,
+ struct list_head *list, int cmd, int brw_flags);
+int osc_cache_truncate_start(const struct lu_env *env, struct osc_io *oio,
+ struct osc_object *obj, __u64 size);
+void osc_cache_truncate_end(const struct lu_env *env, struct osc_io *oio,
+ struct osc_object *obj);
+int osc_cache_writeback_range(const struct lu_env *env, struct osc_object *obj,
+ pgoff_t start, pgoff_t end, int hp, int discard);
+int osc_cache_wait_range(const struct lu_env *env, struct osc_object *obj,
+ pgoff_t start, pgoff_t end);
+void osc_io_unplug(const struct lu_env *env, struct client_obd *cli,
+ struct osc_object *osc, pdl_policy_t pol);
+
+void osc_object_set_contended (struct osc_object *obj);
+void osc_object_clear_contended(struct osc_object *obj);
+int osc_object_is_contended (struct osc_object *obj);
+
+int osc_lock_is_lockless (const struct osc_lock *olck);
+
+/*****************************************************************************
+ *
+ * Accessors.
+ *
+ */
+
+static inline struct osc_thread_info *osc_env_info(const struct lu_env *env)
+{
+ struct osc_thread_info *info;
+
+ info = lu_context_key_get(&env->le_ctx, &osc_key);
+ LASSERT(info != NULL);
+ return info;
+}
+
+static inline struct osc_session *osc_env_session(const struct lu_env *env)
+{
+ struct osc_session *ses;
+
+ ses = lu_context_key_get(env->le_ses, &osc_session_key);
+ LASSERT(ses != NULL);
+ return ses;
+}
+
+static inline struct osc_io *osc_env_io(const struct lu_env *env)
+{
+ return &osc_env_session(env)->os_io;
+}
+
+static inline int osc_is_object(const struct lu_object *obj)
+{
+ return obj->lo_dev->ld_type == &osc_device_type;
+}
+
+static inline struct osc_device *lu2osc_dev(const struct lu_device *d)
+{
+ LINVRNT(d->ld_type == &osc_device_type);
+ return container_of0(d, struct osc_device, od_cl.cd_lu_dev);
+}
+
+static inline struct obd_export *osc_export(const struct osc_object *obj)
+{
+ return lu2osc_dev(obj->oo_cl.co_lu.lo_dev)->od_exp;
+}
+
+static inline struct client_obd *osc_cli(const struct osc_object *obj)
+{
+ return &osc_export(obj)->exp_obd->u.cli;
+}
+
+static inline struct osc_object *cl2osc(const struct cl_object *obj)
+{
+ LINVRNT(osc_is_object(&obj->co_lu));
+ return container_of0(obj, struct osc_object, oo_cl);
+}
+
+static inline struct cl_object *osc2cl(const struct osc_object *obj)
+{
+ return (struct cl_object *)&obj->oo_cl;
+}
+
+static inline ldlm_mode_t osc_cl_lock2ldlm(enum cl_lock_mode mode)
+{
+ LASSERT(mode == CLM_READ || mode == CLM_WRITE || mode == CLM_GROUP);
+ if (mode == CLM_READ)
+ return LCK_PR;
+ else if (mode == CLM_WRITE)
+ return LCK_PW;
+ else
+ return LCK_GROUP;
+}
+
+static inline enum cl_lock_mode osc_ldlm2cl_lock(ldlm_mode_t mode)
+{
+ LASSERT(mode == LCK_PR || mode == LCK_PW || mode == LCK_GROUP);
+ if (mode == LCK_PR)
+ return CLM_READ;
+ else if (mode == LCK_PW)
+ return CLM_WRITE;
+ else
+ return CLM_GROUP;
+}
+
+static inline struct osc_page *cl2osc_page(const struct cl_page_slice *slice)
+{
+ LINVRNT(osc_is_object(&slice->cpl_obj->co_lu));
+ return container_of0(slice, struct osc_page, ops_cl);
+}
+
+static inline struct osc_page *oap2osc(struct osc_async_page *oap)
+{
+ return container_of0(oap, struct osc_page, ops_oap);
+}
+
+static inline struct cl_page *oap2cl_page(struct osc_async_page *oap)
+{
+ return oap2osc(oap)->ops_cl.cpl_page;
+}
+
+static inline struct osc_page *oap2osc_page(struct osc_async_page *oap)
+{
+ return (struct osc_page *)container_of(oap, struct osc_page, ops_oap);
+}
+
+static inline struct osc_lock *cl2osc_lock(const struct cl_lock_slice *slice)
+{
+ LINVRNT(osc_is_object(&slice->cls_obj->co_lu));
+ return container_of0(slice, struct osc_lock, ols_cl);
+}
+
+static inline struct osc_lock *osc_lock_at(const struct cl_lock *lock)
+{
+ return cl2osc_lock(cl_lock_at(lock, &osc_device_type));
+}
+
+static inline int osc_io_srvlock(struct osc_io *oio)
+{
+ return (oio->oi_lockless && !oio->oi_cl.cis_io->ci_no_srvlock);
+}
+
+enum osc_extent_state {
+ OES_INV = 0, /** extent is just initialized or destroyed */
+ OES_ACTIVE = 1, /** process is using this extent */
+ OES_CACHE = 2, /** extent is ready for IO */
+ OES_LOCKING = 3, /** locking page to prepare IO */
+ OES_LOCK_DONE = 4, /** locking finished, ready to send */
+ OES_RPC = 5, /** in RPC */
+ OES_TRUNC = 6, /** being truncated */
+ OES_STATE_MAX
+};
+
+/**
+ * osc_extent data to manage dirty pages.
+ * osc_extent has the following attributes:
+ * 1. all pages in the same must be in one RPC in write back;
+ * 2. # of pages must be less than max_pages_per_rpc - implied by 1;
+ * 3. must be covered by only 1 osc_lock;
+ * 4. exclusive. It's impossible to have overlapped osc_extent.
+ *
+ * The lifetime of an extent is from when the 1st page is dirtied to when
+ * all pages inside it are written out.
+ *
+ * LOCKING ORDER
+ * =============
+ * page lock -> client_obd_list_lock -> object lock(osc_object::oo_lock)
+ */
+struct osc_extent {
+ /** red-black tree node */
+ struct rb_node oe_node;
+ /** osc_object of this extent */
+ struct osc_object *oe_obj;
+ /** refcount, removed from red-black tree if reaches zero. */
+ atomic_t oe_refc;
+ /** busy if non-zero */
+ atomic_t oe_users;
+ /** link list of osc_object's oo_{hp|urgent|locking}_exts. */
+ struct list_head oe_link;
+ /** state of this extent */
+ unsigned int oe_state;
+ /** flags for this extent. */
+ unsigned int oe_intree:1,
+ /** 0 is write, 1 is read */
+ oe_rw:1,
+ oe_srvlock:1,
+ oe_memalloc:1,
+ /** an ACTIVE extent is going to be truncated, so when this extent
+ * is released, it will turn into TRUNC state instead of CACHE. */
+ oe_trunc_pending:1,
+ /** this extent should be written asap and someone may wait for the
+ * write to finish. This bit is usually set along with urgent if
+ * the extent was CACHE state.
+ * fsync_wait extent can't be merged because new extent region may
+ * exceed fsync range. */
+ oe_fsync_wait:1,
+ /** covering lock is being canceled */
+ oe_hp:1,
+ /** this extent should be written back asap. set if one of pages is
+ * called by page WB daemon, or sync write or reading requests. */
+ oe_urgent:1;
+ /** how many grants allocated for this extent.
+ * Grant allocated for this extent. There is no grant allocated
+ * for reading extents and sync write extents. */
+ unsigned int oe_grants;
+ /** # of dirty pages in this extent */
+ unsigned int oe_nr_pages;
+ /** list of pending oap pages. Pages in this list are NOT sorted. */
+ struct list_head oe_pages;
+ /** Since an extent has to be written out in atomic, this is used to
+ * remember the next page need to be locked to write this extent out.
+ * Not used right now.
+ */
+ struct osc_page *oe_next_page;
+ /** start and end index of this extent, include start and end
+ * themselves. Page offset here is the page index of osc_pages.
+ * oe_start is used as keyword for red-black tree. */
+ pgoff_t oe_start;
+ pgoff_t oe_end;
+ /** maximum ending index of this extent, this is limited by
+ * max_pages_per_rpc, lock extent and chunk size. */
+ pgoff_t oe_max_end;
+ /** waitqueue - for those who want to be notified if this extent's
+ * state has changed. */
+ wait_queue_head_t oe_waitq;
+ /** lock covering this extent */
+ struct cl_lock *oe_osclock;
+ /** terminator of this extent. Must be true if this extent is in IO. */
+ struct task_struct *oe_owner;
+ /** return value of writeback. If somebody is waiting for this extent,
+ * this value can be known by outside world. */
+ int oe_rc;
+ /** max pages per rpc when this extent was created */
+ unsigned int oe_mppr;
+};
+
+int osc_extent_finish(const struct lu_env *env, struct osc_extent *ext,
+ int sent, int rc);
+void osc_extent_release(const struct lu_env *env, struct osc_extent *ext);
+
+/** @} osc */
+
+#endif /* OSC_CL_INTERNAL_H */
diff --git a/drivers/staging/lustre/lustre/osc/osc_dev.c b/drivers/staging/lustre/lustre/osc/osc_dev.c
new file mode 100644
index 000000000..4935fc7c0
--- /dev/null
+++ b/drivers/staging/lustre/lustre/osc/osc_dev.c
@@ -0,0 +1,262 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2012, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ *
+ * Implementation of cl_device, cl_req for OSC layer.
+ *
+ * Author: Nikita Danilov <nikita.danilov@sun.com>
+ */
+
+#define DEBUG_SUBSYSTEM S_OSC
+
+/* class_name2obd() */
+#include "../include/obd_class.h"
+
+#include "osc_cl_internal.h"
+
+/** \addtogroup osc
+ * @{
+ */
+
+struct kmem_cache *osc_lock_kmem;
+struct kmem_cache *osc_object_kmem;
+struct kmem_cache *osc_thread_kmem;
+struct kmem_cache *osc_session_kmem;
+struct kmem_cache *osc_req_kmem;
+struct kmem_cache *osc_extent_kmem;
+struct kmem_cache *osc_quota_kmem;
+
+struct lu_kmem_descr osc_caches[] = {
+ {
+ .ckd_cache = &osc_lock_kmem,
+ .ckd_name = "osc_lock_kmem",
+ .ckd_size = sizeof(struct osc_lock)
+ },
+ {
+ .ckd_cache = &osc_object_kmem,
+ .ckd_name = "osc_object_kmem",
+ .ckd_size = sizeof(struct osc_object)
+ },
+ {
+ .ckd_cache = &osc_thread_kmem,
+ .ckd_name = "osc_thread_kmem",
+ .ckd_size = sizeof(struct osc_thread_info)
+ },
+ {
+ .ckd_cache = &osc_session_kmem,
+ .ckd_name = "osc_session_kmem",
+ .ckd_size = sizeof(struct osc_session)
+ },
+ {
+ .ckd_cache = &osc_req_kmem,
+ .ckd_name = "osc_req_kmem",
+ .ckd_size = sizeof(struct osc_req)
+ },
+ {
+ .ckd_cache = &osc_extent_kmem,
+ .ckd_name = "osc_extent_kmem",
+ .ckd_size = sizeof(struct osc_extent)
+ },
+ {
+ .ckd_cache = &osc_quota_kmem,
+ .ckd_name = "osc_quota_kmem",
+ .ckd_size = sizeof(struct osc_quota_info)
+ },
+ {
+ .ckd_cache = NULL
+ }
+};
+
+struct lock_class_key osc_ast_guard_class;
+
+/*****************************************************************************
+ *
+ * Type conversions.
+ *
+ */
+
+static struct lu_device *osc2lu_dev(struct osc_device *osc)
+{
+ return &osc->od_cl.cd_lu_dev;
+}
+
+/*****************************************************************************
+ *
+ * Osc device and device type functions.
+ *
+ */
+
+static void *osc_key_init(const struct lu_context *ctx,
+ struct lu_context_key *key)
+{
+ struct osc_thread_info *info;
+
+ OBD_SLAB_ALLOC_PTR_GFP(info, osc_thread_kmem, GFP_NOFS);
+ if (info == NULL)
+ info = ERR_PTR(-ENOMEM);
+ return info;
+}
+
+static void osc_key_fini(const struct lu_context *ctx,
+ struct lu_context_key *key, void *data)
+{
+ struct osc_thread_info *info = data;
+
+ OBD_SLAB_FREE_PTR(info, osc_thread_kmem);
+}
+
+struct lu_context_key osc_key = {
+ .lct_tags = LCT_CL_THREAD,
+ .lct_init = osc_key_init,
+ .lct_fini = osc_key_fini
+};
+
+static void *osc_session_init(const struct lu_context *ctx,
+ struct lu_context_key *key)
+{
+ struct osc_session *info;
+
+ OBD_SLAB_ALLOC_PTR_GFP(info, osc_session_kmem, GFP_NOFS);
+ if (info == NULL)
+ info = ERR_PTR(-ENOMEM);
+ return info;
+}
+
+static void osc_session_fini(const struct lu_context *ctx,
+ struct lu_context_key *key, void *data)
+{
+ struct osc_session *info = data;
+
+ OBD_SLAB_FREE_PTR(info, osc_session_kmem);
+}
+
+struct lu_context_key osc_session_key = {
+ .lct_tags = LCT_SESSION,
+ .lct_init = osc_session_init,
+ .lct_fini = osc_session_fini
+};
+
+/* type constructor/destructor: osc_type_{init,fini,start,stop}(). */
+LU_TYPE_INIT_FINI(osc, &osc_key, &osc_session_key);
+
+static int osc_cl_process_config(const struct lu_env *env,
+ struct lu_device *d, struct lustre_cfg *cfg)
+{
+ return osc_process_config_base(d->ld_obd, cfg);
+}
+
+static const struct lu_device_operations osc_lu_ops = {
+ .ldo_object_alloc = osc_object_alloc,
+ .ldo_process_config = osc_cl_process_config,
+ .ldo_recovery_complete = NULL
+};
+
+static const struct cl_device_operations osc_cl_ops = {
+ .cdo_req_init = osc_req_init
+};
+
+static int osc_device_init(const struct lu_env *env, struct lu_device *d,
+ const char *name, struct lu_device *next)
+{
+ return 0;
+}
+
+static struct lu_device *osc_device_fini(const struct lu_env *env,
+ struct lu_device *d)
+{
+ return NULL;
+}
+
+static struct lu_device *osc_device_free(const struct lu_env *env,
+ struct lu_device *d)
+{
+ struct osc_device *od = lu2osc_dev(d);
+
+ cl_device_fini(lu2cl_dev(d));
+ OBD_FREE_PTR(od);
+ return NULL;
+}
+
+static struct lu_device *osc_device_alloc(const struct lu_env *env,
+ struct lu_device_type *t,
+ struct lustre_cfg *cfg)
+{
+ struct lu_device *d;
+ struct osc_device *od;
+ struct obd_device *obd;
+ int rc;
+
+ OBD_ALLOC_PTR(od);
+ if (od == NULL)
+ return ERR_PTR(-ENOMEM);
+
+ cl_device_init(&od->od_cl, t);
+ d = osc2lu_dev(od);
+ d->ld_ops = &osc_lu_ops;
+ od->od_cl.cd_ops = &osc_cl_ops;
+
+ /* Setup OSC OBD */
+ obd = class_name2obd(lustre_cfg_string(cfg, 0));
+ LASSERT(obd != NULL);
+ rc = osc_setup(obd, cfg);
+ if (rc) {
+ osc_device_free(env, d);
+ return ERR_PTR(rc);
+ }
+ od->od_exp = obd->obd_self_export;
+ return d;
+}
+
+static const struct lu_device_type_operations osc_device_type_ops = {
+ .ldto_init = osc_type_init,
+ .ldto_fini = osc_type_fini,
+
+ .ldto_start = osc_type_start,
+ .ldto_stop = osc_type_stop,
+
+ .ldto_device_alloc = osc_device_alloc,
+ .ldto_device_free = osc_device_free,
+
+ .ldto_device_init = osc_device_init,
+ .ldto_device_fini = osc_device_fini
+};
+
+struct lu_device_type osc_device_type = {
+ .ldt_tags = LU_DEVICE_CL,
+ .ldt_name = LUSTRE_OSC_NAME,
+ .ldt_ops = &osc_device_type_ops,
+ .ldt_ctx_tags = LCT_CL_THREAD
+};
+
+/** @} osc */
diff --git a/drivers/staging/lustre/lustre/osc/osc_internal.h b/drivers/staging/lustre/lustre/osc/osc_internal.h
new file mode 100644
index 000000000..af96c7bc7
--- /dev/null
+++ b/drivers/staging/lustre/lustre/osc/osc_internal.h
@@ -0,0 +1,203 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2011, 2012, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ */
+
+#ifndef OSC_INTERNAL_H
+#define OSC_INTERNAL_H
+
+#define OAP_MAGIC 8675309
+
+struct lu_env;
+
+enum async_flags {
+ ASYNC_READY = 0x1, /* ap_make_ready will not be called before this
+ page is added to an rpc */
+ ASYNC_URGENT = 0x2, /* page must be put into an RPC before return */
+ ASYNC_COUNT_STABLE = 0x4, /* ap_refresh_count will not be called
+ to give the caller a chance to update
+ or cancel the size of the io */
+ ASYNC_HP = 0x10,
+};
+
+struct osc_async_page {
+ int oap_magic;
+ unsigned short oap_cmd;
+ unsigned short oap_interrupted:1;
+
+ struct list_head oap_pending_item;
+ struct list_head oap_rpc_item;
+
+ u64 oap_obj_off;
+ unsigned oap_page_off;
+ enum async_flags oap_async_flags;
+
+ struct brw_page oap_brw_page;
+
+ struct ptlrpc_request *oap_request;
+ struct client_obd *oap_cli;
+ struct osc_object *oap_obj;
+
+ struct ldlm_lock *oap_ldlm_lock;
+ spinlock_t oap_lock;
+};
+
+#define oap_page oap_brw_page.pg
+#define oap_count oap_brw_page.count
+#define oap_brw_flags oap_brw_page.flag
+
+struct osc_cache_waiter {
+ struct list_head ocw_entry;
+ wait_queue_head_t ocw_waitq;
+ struct osc_async_page *ocw_oap;
+ int ocw_grant;
+ int ocw_rc;
+};
+
+int osc_create(const struct lu_env *env, struct obd_export *exp,
+ struct obdo *oa, struct lov_stripe_md **ea,
+ struct obd_trans_info *oti);
+int osc_real_create(struct obd_export *exp, struct obdo *oa,
+ struct lov_stripe_md **ea, struct obd_trans_info *oti);
+void osc_wake_cache_waiters(struct client_obd *cli);
+int osc_shrink_grant_to_target(struct client_obd *cli, __u64 target_bytes);
+void osc_update_next_shrink(struct client_obd *cli);
+
+/*
+ * cl integration.
+ */
+#include "../include/cl_object.h"
+
+extern struct ptlrpc_request_set *PTLRPCD_SET;
+
+int osc_enqueue_base(struct obd_export *exp, struct ldlm_res_id *res_id,
+ __u64 *flags, ldlm_policy_data_t *policy,
+ struct ost_lvb *lvb, int kms_valid,
+ obd_enqueue_update_f upcall,
+ void *cookie, struct ldlm_enqueue_info *einfo,
+ struct lustre_handle *lockh,
+ struct ptlrpc_request_set *rqset, int async, int agl);
+int osc_cancel_base(struct lustre_handle *lockh, __u32 mode);
+
+int osc_match_base(struct obd_export *exp, struct ldlm_res_id *res_id,
+ __u32 type, ldlm_policy_data_t *policy, __u32 mode,
+ __u64 *flags, void *data, struct lustre_handle *lockh,
+ int unref);
+
+int osc_setattr_async_base(struct obd_export *exp, struct obd_info *oinfo,
+ struct obd_trans_info *oti,
+ obd_enqueue_update_f upcall, void *cookie,
+ struct ptlrpc_request_set *rqset);
+int osc_punch_base(struct obd_export *exp, struct obd_info *oinfo,
+ obd_enqueue_update_f upcall, void *cookie,
+ struct ptlrpc_request_set *rqset);
+int osc_sync_base(struct obd_export *exp, struct obd_info *oinfo,
+ obd_enqueue_update_f upcall, void *cookie,
+ struct ptlrpc_request_set *rqset);
+
+int osc_process_config_base(struct obd_device *obd, struct lustre_cfg *cfg);
+int osc_build_rpc(const struct lu_env *env, struct client_obd *cli,
+ struct list_head *ext_list, int cmd, pdl_policy_t p);
+int osc_lru_shrink(struct client_obd *cli, int target);
+
+extern spinlock_t osc_ast_guard;
+
+int osc_cleanup(struct obd_device *obd);
+int osc_setup(struct obd_device *obd, struct lustre_cfg *lcfg);
+
+#if defined (CONFIG_PROC_FS)
+int lproc_osc_attach_seqstat(struct obd_device *dev);
+void lprocfs_osc_init_vars(struct lprocfs_static_vars *lvars);
+#else
+static inline int lproc_osc_attach_seqstat(struct obd_device *dev) {return 0;}
+static inline void lprocfs_osc_init_vars(struct lprocfs_static_vars *lvars)
+{
+ memset(lvars, 0, sizeof(*lvars));
+}
+#endif
+
+extern struct lu_device_type osc_device_type;
+
+static inline int osc_recoverable_error(int rc)
+{
+ return (rc == -EIO || rc == -EROFS || rc == -ENOMEM ||
+ rc == -EAGAIN || rc == -EINPROGRESS);
+}
+
+static inline unsigned long rpcs_in_flight(struct client_obd *cli)
+{
+ return cli->cl_r_in_flight + cli->cl_w_in_flight;
+}
+
+struct osc_device {
+ struct cl_device od_cl;
+ struct obd_export *od_exp;
+
+ /* Write stats is actually protected by client_obd's lock. */
+ struct osc_stats {
+ uint64_t os_lockless_writes; /* by bytes */
+ uint64_t os_lockless_reads; /* by bytes */
+ uint64_t os_lockless_truncates; /* by times */
+ } od_stats;
+
+ /* configuration item(s) */
+ int od_contention_time;
+ int od_lockless_truncate;
+};
+
+static inline struct osc_device *obd2osc_dev(const struct obd_device *d)
+{
+ return container_of0(d->obd_lu_dev, struct osc_device, od_cl.cd_lu_dev);
+}
+
+int osc_dlm_lock_pageref(struct ldlm_lock *dlm);
+
+extern struct kmem_cache *osc_quota_kmem;
+struct osc_quota_info {
+ /** linkage for quota hash table */
+ struct hlist_node oqi_hash;
+ u32 oqi_id;
+};
+int osc_quota_setup(struct obd_device *obd);
+int osc_quota_cleanup(struct obd_device *obd);
+int osc_quota_setdq(struct client_obd *cli, const unsigned int qid[],
+ u32 valid, u32 flags);
+int osc_quota_chkdq(struct client_obd *cli, const unsigned int qid[]);
+int osc_quotactl(struct obd_device *unused, struct obd_export *exp,
+ struct obd_quotactl *oqctl);
+int osc_quotacheck(struct obd_device *unused, struct obd_export *exp,
+ struct obd_quotactl *oqctl);
+int osc_quota_poll_check(struct obd_export *exp, struct if_quotacheck *qchk);
+
+#endif /* OSC_INTERNAL_H */
diff --git a/drivers/staging/lustre/lustre/osc/osc_io.c b/drivers/staging/lustre/lustre/osc/osc_io.c
new file mode 100644
index 000000000..3c7300b06
--- /dev/null
+++ b/drivers/staging/lustre/lustre/osc/osc_io.c
@@ -0,0 +1,819 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2011, 2012, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ *
+ * Implementation of cl_io for OSC layer.
+ *
+ * Author: Nikita Danilov <nikita.danilov@sun.com>
+ * Author: Jinshan Xiong <jinshan.xiong@whamcloud.com>
+ */
+
+#define DEBUG_SUBSYSTEM S_OSC
+
+#include "osc_cl_internal.h"
+
+/** \addtogroup osc
+ * @{
+ */
+
+/*****************************************************************************
+ *
+ * Type conversions.
+ *
+ */
+
+static struct osc_req *cl2osc_req(const struct cl_req_slice *slice)
+{
+ LINVRNT(slice->crs_dev->cd_lu_dev.ld_type == &osc_device_type);
+ return container_of0(slice, struct osc_req, or_cl);
+}
+
+static struct osc_io *cl2osc_io(const struct lu_env *env,
+ const struct cl_io_slice *slice)
+{
+ struct osc_io *oio = container_of0(slice, struct osc_io, oi_cl);
+
+ LINVRNT(oio == osc_env_io(env));
+ return oio;
+}
+
+static struct osc_page *osc_cl_page_osc(struct cl_page *page)
+{
+ const struct cl_page_slice *slice;
+
+ slice = cl_page_at(page, &osc_device_type);
+ LASSERT(slice != NULL);
+
+ return cl2osc_page(slice);
+}
+
+
+/*****************************************************************************
+ *
+ * io operations.
+ *
+ */
+
+static void osc_io_fini(const struct lu_env *env, const struct cl_io_slice *io)
+{
+}
+
+/**
+ * An implementation of cl_io_operations::cio_io_submit() method for osc
+ * layer. Iterates over pages in the in-queue, prepares each for io by calling
+ * cl_page_prep() and then either submits them through osc_io_submit_page()
+ * or, if page is already submitted, changes osc flags through
+ * osc_set_async_flags().
+ */
+static int osc_io_submit(const struct lu_env *env,
+ const struct cl_io_slice *ios,
+ enum cl_req_type crt, struct cl_2queue *queue)
+{
+ struct cl_page *page;
+ struct cl_page *tmp;
+ struct client_obd *cli = NULL;
+ struct osc_object *osc = NULL; /* to keep gcc happy */
+ struct osc_page *opg;
+ struct cl_io *io;
+ LIST_HEAD(list);
+
+ struct cl_page_list *qin = &queue->c2_qin;
+ struct cl_page_list *qout = &queue->c2_qout;
+ int queued = 0;
+ int result = 0;
+ int cmd;
+ int brw_flags;
+ int max_pages;
+
+ LASSERT(qin->pl_nr > 0);
+
+ CDEBUG(D_CACHE, "%d %d\n", qin->pl_nr, crt);
+
+ osc = cl2osc(ios->cis_obj);
+ cli = osc_cli(osc);
+ max_pages = cli->cl_max_pages_per_rpc;
+
+ cmd = crt == CRT_WRITE ? OBD_BRW_WRITE : OBD_BRW_READ;
+ brw_flags = osc_io_srvlock(cl2osc_io(env, ios)) ? OBD_BRW_SRVLOCK : 0;
+
+ /*
+ * NOTE: here @page is a top-level page. This is done to avoid
+ * creation of sub-page-list.
+ */
+ cl_page_list_for_each_safe(page, tmp, qin) {
+ struct osc_async_page *oap;
+
+ /* Top level IO. */
+ io = page->cp_owner;
+ LASSERT(io != NULL);
+
+ opg = osc_cl_page_osc(page);
+ oap = &opg->ops_oap;
+ LASSERT(osc == oap->oap_obj);
+
+ if (!list_empty(&oap->oap_pending_item) ||
+ !list_empty(&oap->oap_rpc_item)) {
+ CDEBUG(D_CACHE, "Busy oap %p page %p for submit.\n",
+ oap, opg);
+ result = -EBUSY;
+ break;
+ }
+
+ result = cl_page_prep(env, io, page, crt);
+ if (result != 0) {
+ LASSERT(result < 0);
+ if (result != -EALREADY)
+ break;
+ /*
+ * Handle -EALREADY error: for read case, the page is
+ * already in UPTODATE state; for write, the page
+ * is not dirty.
+ */
+ result = 0;
+ continue;
+ }
+
+ cl_page_list_move(qout, qin, page);
+ oap->oap_async_flags = ASYNC_URGENT|ASYNC_READY;
+ oap->oap_async_flags |= ASYNC_COUNT_STABLE;
+
+ osc_page_submit(env, opg, crt, brw_flags);
+ list_add_tail(&oap->oap_pending_item, &list);
+ if (++queued == max_pages) {
+ queued = 0;
+ result = osc_queue_sync_pages(env, osc, &list, cmd,
+ brw_flags);
+ if (result < 0)
+ break;
+ }
+ }
+
+ if (queued > 0)
+ result = osc_queue_sync_pages(env, osc, &list, cmd, brw_flags);
+
+ CDEBUG(D_INFO, "%d/%d %d\n", qin->pl_nr, qout->pl_nr, result);
+ return qout->pl_nr > 0 ? 0 : result;
+}
+
+static void osc_page_touch_at(const struct lu_env *env,
+ struct cl_object *obj, pgoff_t idx, unsigned to)
+{
+ struct lov_oinfo *loi = cl2osc(obj)->oo_oinfo;
+ struct cl_attr *attr = &osc_env_info(env)->oti_attr;
+ int valid;
+ __u64 kms;
+
+ /* offset within stripe */
+ kms = cl_offset(obj, idx) + to;
+
+ cl_object_attr_lock(obj);
+ /*
+ * XXX old code used
+ *
+ * ll_inode_size_lock(inode, 0); lov_stripe_lock(lsm);
+ *
+ * here
+ */
+ CDEBUG(D_INODE, "stripe KMS %sincreasing %llu->%llu %llu\n",
+ kms > loi->loi_kms ? "" : "not ", loi->loi_kms, kms,
+ loi->loi_lvb.lvb_size);
+
+ valid = 0;
+ if (kms > loi->loi_kms) {
+ attr->cat_kms = kms;
+ valid |= CAT_KMS;
+ }
+ if (kms > loi->loi_lvb.lvb_size) {
+ attr->cat_size = kms;
+ valid |= CAT_SIZE;
+ }
+ cl_object_attr_set(env, obj, attr, valid);
+ cl_object_attr_unlock(obj);
+}
+
+/**
+ * This is called when a page is accessed within file in a way that creates
+ * new page, if one were missing (i.e., if there were a hole at that place in
+ * the file, or accessed page is beyond the current file size). Examples:
+ * ->commit_write() and ->nopage() methods.
+ *
+ * Expand stripe KMS if necessary.
+ */
+static void osc_page_touch(const struct lu_env *env,
+ struct osc_page *opage, unsigned to)
+{
+ struct cl_page *page = opage->ops_cl.cpl_page;
+ struct cl_object *obj = opage->ops_cl.cpl_obj;
+
+ osc_page_touch_at(env, obj, page->cp_index, to);
+}
+
+/**
+ * Implements cl_io_operations::cio_prepare_write() method for osc layer.
+ *
+ * \retval -EIO transfer initiated against this osc will most likely fail
+ * \retval 0 transfer initiated against this osc will most likely succeed.
+ *
+ * The reason for this check is to immediately return an error to the caller
+ * in the case of a deactivated import. Note, that import can be deactivated
+ * later, while pages, dirtied by this IO, are still in the cache, but this is
+ * irrelevant, because that would still return an error to the application (if
+ * it does fsync), but many applications don't do fsync because of performance
+ * issues, and we wanted to return an -EIO at write time to notify the
+ * application.
+ */
+static int osc_io_prepare_write(const struct lu_env *env,
+ const struct cl_io_slice *ios,
+ const struct cl_page_slice *slice,
+ unsigned from, unsigned to)
+{
+ struct osc_device *dev = lu2osc_dev(slice->cpl_obj->co_lu.lo_dev);
+ struct obd_import *imp = class_exp2cliimp(dev->od_exp);
+ struct osc_io *oio = cl2osc_io(env, ios);
+ int result = 0;
+
+ /*
+ * This implements OBD_BRW_CHECK logic from old client.
+ */
+
+ if (imp == NULL || imp->imp_invalid)
+ result = -EIO;
+ if (result == 0 && oio->oi_lockless)
+ /* this page contains `invalid' data, but who cares?
+ * nobody can access the invalid data.
+ * in osc_io_commit_write(), we're going to write exact
+ * [from, to) bytes of this page to OST. -jay */
+ cl_page_export(env, slice->cpl_page, 1);
+
+ return result;
+}
+
+static int osc_io_commit_write(const struct lu_env *env,
+ const struct cl_io_slice *ios,
+ const struct cl_page_slice *slice,
+ unsigned from, unsigned to)
+{
+ struct osc_io *oio = cl2osc_io(env, ios);
+ struct osc_page *opg = cl2osc_page(slice);
+ struct osc_object *obj = cl2osc(opg->ops_cl.cpl_obj);
+ struct osc_async_page *oap = &opg->ops_oap;
+
+ LASSERT(to > 0);
+ /*
+ * XXX instead of calling osc_page_touch() here and in
+ * osc_io_fault_start() it might be more logical to introduce
+ * cl_page_touch() method, that generic cl_io_commit_write() and page
+ * fault code calls.
+ */
+ osc_page_touch(env, cl2osc_page(slice), to);
+ if (!client_is_remote(osc_export(obj)) &&
+ capable(CFS_CAP_SYS_RESOURCE))
+ oap->oap_brw_flags |= OBD_BRW_NOQUOTA;
+
+ if (oio->oi_lockless)
+ /* see osc_io_prepare_write() for lockless io handling. */
+ cl_page_clip(env, slice->cpl_page, from, to);
+
+ return 0;
+}
+
+static int osc_io_fault_start(const struct lu_env *env,
+ const struct cl_io_slice *ios)
+{
+ struct cl_io *io;
+ struct cl_fault_io *fio;
+
+ io = ios->cis_io;
+ fio = &io->u.ci_fault;
+ CDEBUG(D_INFO, "%lu %d %d\n",
+ fio->ft_index, fio->ft_writable, fio->ft_nob);
+ /*
+ * If mapping is writeable, adjust kms to cover this page,
+ * but do not extend kms beyond actual file size.
+ * See bug 10919.
+ */
+ if (fio->ft_writable)
+ osc_page_touch_at(env, ios->cis_obj,
+ fio->ft_index, fio->ft_nob);
+ return 0;
+}
+
+static int osc_async_upcall(void *a, int rc)
+{
+ struct osc_async_cbargs *args = a;
+
+ args->opc_rc = rc;
+ complete(&args->opc_sync);
+ return 0;
+}
+
+/**
+ * Checks that there are no pages being written in the extent being truncated.
+ */
+static int trunc_check_cb(const struct lu_env *env, struct cl_io *io,
+ struct cl_page *page, void *cbdata)
+{
+ const struct cl_page_slice *slice;
+ struct osc_page *ops;
+ struct osc_async_page *oap;
+ __u64 start = *(__u64 *)cbdata;
+
+ slice = cl_page_at(page, &osc_device_type);
+ LASSERT(slice != NULL);
+ ops = cl2osc_page(slice);
+ oap = &ops->ops_oap;
+
+ if (oap->oap_cmd & OBD_BRW_WRITE &&
+ !list_empty(&oap->oap_pending_item))
+ CL_PAGE_DEBUG(D_ERROR, env, page, "exists %llu/%s.\n",
+ start, current->comm);
+
+ {
+ struct page *vmpage = cl_page_vmpage(env, page);
+
+ if (PageLocked(vmpage))
+ CDEBUG(D_CACHE, "page %p index %lu locked for %d.\n",
+ ops, page->cp_index,
+ (oap->oap_cmd & OBD_BRW_RWMASK));
+ }
+
+ return CLP_GANG_OKAY;
+}
+
+static void osc_trunc_check(const struct lu_env *env, struct cl_io *io,
+ struct osc_io *oio, __u64 size)
+{
+ struct cl_object *clob;
+ int partial;
+ pgoff_t start;
+
+ clob = oio->oi_cl.cis_obj;
+ start = cl_index(clob, size);
+ partial = cl_offset(clob, start) < size;
+
+ /*
+ * Complain if there are pages in the truncated region.
+ */
+ cl_page_gang_lookup(env, clob, io, start + partial, CL_PAGE_EOF,
+ trunc_check_cb, (void *)&size);
+}
+
+static int osc_io_setattr_start(const struct lu_env *env,
+ const struct cl_io_slice *slice)
+{
+ struct cl_io *io = slice->cis_io;
+ struct osc_io *oio = cl2osc_io(env, slice);
+ struct cl_object *obj = slice->cis_obj;
+ struct lov_oinfo *loi = cl2osc(obj)->oo_oinfo;
+ struct cl_attr *attr = &osc_env_info(env)->oti_attr;
+ struct obdo *oa = &oio->oi_oa;
+ struct osc_async_cbargs *cbargs = &oio->oi_cbarg;
+ __u64 size = io->u.ci_setattr.sa_attr.lvb_size;
+ unsigned int ia_valid = io->u.ci_setattr.sa_valid;
+ int result = 0;
+ struct obd_info oinfo = { { { 0 } } };
+
+ /* truncate cache dirty pages first */
+ if (cl_io_is_trunc(io))
+ result = osc_cache_truncate_start(env, oio, cl2osc(obj), size);
+
+ if (result == 0 && oio->oi_lockless == 0) {
+ cl_object_attr_lock(obj);
+ result = cl_object_attr_get(env, obj, attr);
+ if (result == 0) {
+ struct ost_lvb *lvb = &io->u.ci_setattr.sa_attr;
+ unsigned int cl_valid = 0;
+
+ if (ia_valid & ATTR_SIZE) {
+ attr->cat_size = attr->cat_kms = size;
+ cl_valid = CAT_SIZE | CAT_KMS;
+ }
+ if (ia_valid & ATTR_MTIME_SET) {
+ attr->cat_mtime = lvb->lvb_mtime;
+ cl_valid |= CAT_MTIME;
+ }
+ if (ia_valid & ATTR_ATIME_SET) {
+ attr->cat_atime = lvb->lvb_atime;
+ cl_valid |= CAT_ATIME;
+ }
+ if (ia_valid & ATTR_CTIME_SET) {
+ attr->cat_ctime = lvb->lvb_ctime;
+ cl_valid |= CAT_CTIME;
+ }
+ result = cl_object_attr_set(env, obj, attr, cl_valid);
+ }
+ cl_object_attr_unlock(obj);
+ }
+ memset(oa, 0, sizeof(*oa));
+ if (result == 0) {
+ oa->o_oi = loi->loi_oi;
+ oa->o_mtime = attr->cat_mtime;
+ oa->o_atime = attr->cat_atime;
+ oa->o_ctime = attr->cat_ctime;
+ oa->o_valid = OBD_MD_FLID | OBD_MD_FLGROUP | OBD_MD_FLATIME |
+ OBD_MD_FLCTIME | OBD_MD_FLMTIME;
+ if (ia_valid & ATTR_SIZE) {
+ oa->o_size = size;
+ oa->o_blocks = OBD_OBJECT_EOF;
+ oa->o_valid |= OBD_MD_FLSIZE | OBD_MD_FLBLOCKS;
+
+ if (oio->oi_lockless) {
+ oa->o_flags = OBD_FL_SRVLOCK;
+ oa->o_valid |= OBD_MD_FLFLAGS;
+ }
+ } else {
+ LASSERT(oio->oi_lockless == 0);
+ }
+
+ oinfo.oi_oa = oa;
+ oinfo.oi_capa = io->u.ci_setattr.sa_capa;
+ init_completion(&cbargs->opc_sync);
+
+ if (ia_valid & ATTR_SIZE)
+ result = osc_punch_base(osc_export(cl2osc(obj)),
+ &oinfo, osc_async_upcall,
+ cbargs, PTLRPCD_SET);
+ else
+ result = osc_setattr_async_base(osc_export(cl2osc(obj)),
+ &oinfo, NULL,
+ osc_async_upcall,
+ cbargs, PTLRPCD_SET);
+ cbargs->opc_rpc_sent = result == 0;
+ }
+ return result;
+}
+
+static void osc_io_setattr_end(const struct lu_env *env,
+ const struct cl_io_slice *slice)
+{
+ struct cl_io *io = slice->cis_io;
+ struct osc_io *oio = cl2osc_io(env, slice);
+ struct cl_object *obj = slice->cis_obj;
+ struct osc_async_cbargs *cbargs = &oio->oi_cbarg;
+ int result = 0;
+
+ if (cbargs->opc_rpc_sent) {
+ wait_for_completion(&cbargs->opc_sync);
+ result = io->ci_result = cbargs->opc_rc;
+ }
+ if (result == 0) {
+ if (oio->oi_lockless) {
+ /* lockless truncate */
+ struct osc_device *osd = lu2osc_dev(obj->co_lu.lo_dev);
+
+ LASSERT(cl_io_is_trunc(io));
+ /* XXX: Need a lock. */
+ osd->od_stats.os_lockless_truncates++;
+ }
+ }
+
+ if (cl_io_is_trunc(io)) {
+ __u64 size = io->u.ci_setattr.sa_attr.lvb_size;
+
+ osc_trunc_check(env, io, oio, size);
+ if (oio->oi_trunc != NULL) {
+ osc_cache_truncate_end(env, oio, cl2osc(obj));
+ oio->oi_trunc = NULL;
+ }
+ }
+}
+
+static int osc_io_read_start(const struct lu_env *env,
+ const struct cl_io_slice *slice)
+{
+ struct cl_object *obj = slice->cis_obj;
+ struct cl_attr *attr = &osc_env_info(env)->oti_attr;
+ int rc = 0;
+
+ if (!slice->cis_io->ci_noatime) {
+ cl_object_attr_lock(obj);
+ attr->cat_atime = LTIME_S(CURRENT_TIME);
+ rc = cl_object_attr_set(env, obj, attr, CAT_ATIME);
+ cl_object_attr_unlock(obj);
+ }
+ return rc;
+}
+
+static int osc_io_write_start(const struct lu_env *env,
+ const struct cl_io_slice *slice)
+{
+ struct cl_object *obj = slice->cis_obj;
+ struct cl_attr *attr = &osc_env_info(env)->oti_attr;
+ int rc = 0;
+
+ OBD_FAIL_TIMEOUT(OBD_FAIL_OSC_DELAY_SETTIME, 1);
+ cl_object_attr_lock(obj);
+ attr->cat_mtime = attr->cat_ctime = LTIME_S(CURRENT_TIME);
+ rc = cl_object_attr_set(env, obj, attr, CAT_MTIME | CAT_CTIME);
+ cl_object_attr_unlock(obj);
+
+ return rc;
+}
+
+static int osc_fsync_ost(const struct lu_env *env, struct osc_object *obj,
+ struct cl_fsync_io *fio)
+{
+ struct osc_io *oio = osc_env_io(env);
+ struct obdo *oa = &oio->oi_oa;
+ struct obd_info *oinfo = &oio->oi_info;
+ struct lov_oinfo *loi = obj->oo_oinfo;
+ struct osc_async_cbargs *cbargs = &oio->oi_cbarg;
+ int rc = 0;
+
+ memset(oa, 0, sizeof(*oa));
+ oa->o_oi = loi->loi_oi;
+ oa->o_valid = OBD_MD_FLID | OBD_MD_FLGROUP;
+
+ /* reload size abd blocks for start and end of sync range */
+ oa->o_size = fio->fi_start;
+ oa->o_blocks = fio->fi_end;
+ oa->o_valid |= OBD_MD_FLSIZE | OBD_MD_FLBLOCKS;
+
+ obdo_set_parent_fid(oa, fio->fi_fid);
+
+ memset(oinfo, 0, sizeof(*oinfo));
+ oinfo->oi_oa = oa;
+ oinfo->oi_capa = fio->fi_capa;
+ init_completion(&cbargs->opc_sync);
+
+ rc = osc_sync_base(osc_export(obj), oinfo, osc_async_upcall, cbargs,
+ PTLRPCD_SET);
+ return rc;
+}
+
+static int osc_io_fsync_start(const struct lu_env *env,
+ const struct cl_io_slice *slice)
+{
+ struct cl_io *io = slice->cis_io;
+ struct cl_fsync_io *fio = &io->u.ci_fsync;
+ struct cl_object *obj = slice->cis_obj;
+ struct osc_object *osc = cl2osc(obj);
+ pgoff_t start = cl_index(obj, fio->fi_start);
+ pgoff_t end = cl_index(obj, fio->fi_end);
+ int result = 0;
+
+ if (fio->fi_end == OBD_OBJECT_EOF)
+ end = CL_PAGE_EOF;
+
+ result = osc_cache_writeback_range(env, osc, start, end, 0,
+ fio->fi_mode == CL_FSYNC_DISCARD);
+ if (result > 0) {
+ fio->fi_nr_written += result;
+ result = 0;
+ }
+ if (fio->fi_mode == CL_FSYNC_ALL) {
+ int rc;
+
+ /* we have to wait for writeback to finish before we can
+ * send OST_SYNC RPC. This is bad because it causes extents
+ * to be written osc by osc. However, we usually start
+ * writeback before CL_FSYNC_ALL so this won't have any real
+ * problem. */
+ rc = osc_cache_wait_range(env, osc, start, end);
+ if (result == 0)
+ result = rc;
+ rc = osc_fsync_ost(env, osc, fio);
+ if (result == 0)
+ result = rc;
+ }
+
+ return result;
+}
+
+static void osc_io_fsync_end(const struct lu_env *env,
+ const struct cl_io_slice *slice)
+{
+ struct cl_fsync_io *fio = &slice->cis_io->u.ci_fsync;
+ struct cl_object *obj = slice->cis_obj;
+ pgoff_t start = cl_index(obj, fio->fi_start);
+ pgoff_t end = cl_index(obj, fio->fi_end);
+ int result = 0;
+
+ if (fio->fi_mode == CL_FSYNC_LOCAL) {
+ result = osc_cache_wait_range(env, cl2osc(obj), start, end);
+ } else if (fio->fi_mode == CL_FSYNC_ALL) {
+ struct osc_io *oio = cl2osc_io(env, slice);
+ struct osc_async_cbargs *cbargs = &oio->oi_cbarg;
+
+ wait_for_completion(&cbargs->opc_sync);
+ if (result == 0)
+ result = cbargs->opc_rc;
+ }
+ slice->cis_io->ci_result = result;
+}
+
+static void osc_io_end(const struct lu_env *env,
+ const struct cl_io_slice *slice)
+{
+ struct osc_io *oio = cl2osc_io(env, slice);
+
+ if (oio->oi_active) {
+ osc_extent_release(env, oio->oi_active);
+ oio->oi_active = NULL;
+ }
+}
+
+static const struct cl_io_operations osc_io_ops = {
+ .op = {
+ [CIT_READ] = {
+ .cio_start = osc_io_read_start,
+ .cio_fini = osc_io_fini
+ },
+ [CIT_WRITE] = {
+ .cio_start = osc_io_write_start,
+ .cio_end = osc_io_end,
+ .cio_fini = osc_io_fini
+ },
+ [CIT_SETATTR] = {
+ .cio_start = osc_io_setattr_start,
+ .cio_end = osc_io_setattr_end
+ },
+ [CIT_FAULT] = {
+ .cio_start = osc_io_fault_start,
+ .cio_end = osc_io_end,
+ .cio_fini = osc_io_fini
+ },
+ [CIT_FSYNC] = {
+ .cio_start = osc_io_fsync_start,
+ .cio_end = osc_io_fsync_end,
+ .cio_fini = osc_io_fini
+ },
+ [CIT_MISC] = {
+ .cio_fini = osc_io_fini
+ }
+ },
+ .req_op = {
+ [CRT_READ] = {
+ .cio_submit = osc_io_submit
+ },
+ [CRT_WRITE] = {
+ .cio_submit = osc_io_submit
+ }
+ },
+ .cio_prepare_write = osc_io_prepare_write,
+ .cio_commit_write = osc_io_commit_write
+};
+
+/*****************************************************************************
+ *
+ * Transfer operations.
+ *
+ */
+
+static int osc_req_prep(const struct lu_env *env,
+ const struct cl_req_slice *slice)
+{
+ return 0;
+}
+
+static void osc_req_completion(const struct lu_env *env,
+ const struct cl_req_slice *slice, int ioret)
+{
+ struct osc_req *or;
+
+ or = cl2osc_req(slice);
+ OBD_SLAB_FREE_PTR(or, osc_req_kmem);
+}
+
+/**
+ * Implementation of struct cl_req_operations::cro_attr_set() for osc
+ * layer. osc is responsible for struct obdo::o_id and struct obdo::o_seq
+ * fields.
+ */
+static void osc_req_attr_set(const struct lu_env *env,
+ const struct cl_req_slice *slice,
+ const struct cl_object *obj,
+ struct cl_req_attr *attr, u64 flags)
+{
+ struct lov_oinfo *oinfo;
+ struct cl_req *clerq;
+ struct cl_page *apage; /* _some_ page in @clerq */
+ struct cl_lock *lock; /* _some_ lock protecting @apage */
+ struct osc_lock *olck;
+ struct osc_page *opg;
+ struct obdo *oa;
+ struct ost_lvb *lvb;
+
+ oinfo = cl2osc(obj)->oo_oinfo;
+ lvb = &oinfo->loi_lvb;
+ oa = attr->cra_oa;
+
+ if ((flags & OBD_MD_FLMTIME) != 0) {
+ oa->o_mtime = lvb->lvb_mtime;
+ oa->o_valid |= OBD_MD_FLMTIME;
+ }
+ if ((flags & OBD_MD_FLATIME) != 0) {
+ oa->o_atime = lvb->lvb_atime;
+ oa->o_valid |= OBD_MD_FLATIME;
+ }
+ if ((flags & OBD_MD_FLCTIME) != 0) {
+ oa->o_ctime = lvb->lvb_ctime;
+ oa->o_valid |= OBD_MD_FLCTIME;
+ }
+ if (flags & OBD_MD_FLGROUP) {
+ ostid_set_seq(&oa->o_oi, ostid_seq(&oinfo->loi_oi));
+ oa->o_valid |= OBD_MD_FLGROUP;
+ }
+ if (flags & OBD_MD_FLID) {
+ ostid_set_id(&oa->o_oi, ostid_id(&oinfo->loi_oi));
+ oa->o_valid |= OBD_MD_FLID;
+ }
+ if (flags & OBD_MD_FLHANDLE) {
+ clerq = slice->crs_req;
+ LASSERT(!list_empty(&clerq->crq_pages));
+ apage = container_of(clerq->crq_pages.next,
+ struct cl_page, cp_flight);
+ opg = osc_cl_page_osc(apage);
+ apage = opg->ops_cl.cpl_page; /* now apage is a sub-page */
+ lock = cl_lock_at_page(env, apage->cp_obj, apage, NULL, 1, 1);
+ if (lock == NULL) {
+ struct cl_object_header *head;
+ struct cl_lock *scan;
+
+ head = cl_object_header(apage->cp_obj);
+ list_for_each_entry(scan, &head->coh_locks,
+ cll_linkage)
+ CL_LOCK_DEBUG(D_ERROR, env, scan,
+ "no cover page!\n");
+ CL_PAGE_DEBUG(D_ERROR, env, apage,
+ "dump uncover page!\n");
+ dump_stack();
+ LBUG();
+ }
+
+ olck = osc_lock_at(lock);
+ LASSERT(olck != NULL);
+ LASSERT(ergo(opg->ops_srvlock, olck->ols_lock == NULL));
+ /* check for lockless io. */
+ if (olck->ols_lock != NULL) {
+ oa->o_handle = olck->ols_lock->l_remote_handle;
+ oa->o_valid |= OBD_MD_FLHANDLE;
+ }
+ cl_lock_put(env, lock);
+ }
+}
+
+static const struct cl_req_operations osc_req_ops = {
+ .cro_prep = osc_req_prep,
+ .cro_attr_set = osc_req_attr_set,
+ .cro_completion = osc_req_completion
+};
+
+
+int osc_io_init(const struct lu_env *env,
+ struct cl_object *obj, struct cl_io *io)
+{
+ struct osc_io *oio = osc_env_io(env);
+
+ CL_IO_SLICE_CLEAN(oio, oi_cl);
+ cl_io_slice_add(io, &oio->oi_cl, obj, &osc_io_ops);
+ return 0;
+}
+
+int osc_req_init(const struct lu_env *env, struct cl_device *dev,
+ struct cl_req *req)
+{
+ struct osc_req *or;
+ int result;
+
+ OBD_SLAB_ALLOC_PTR_GFP(or, osc_req_kmem, GFP_NOFS);
+ if (or != NULL) {
+ cl_req_slice_add(req, &or->or_cl, dev, &osc_req_ops);
+ result = 0;
+ } else
+ result = -ENOMEM;
+ return result;
+}
+
+/** @} osc */
diff --git a/drivers/staging/lustre/lustre/osc/osc_lock.c b/drivers/staging/lustre/lustre/osc/osc_lock.c
new file mode 100644
index 000000000..350ad4955
--- /dev/null
+++ b/drivers/staging/lustre/lustre/osc/osc_lock.c
@@ -0,0 +1,1613 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2011, 2012, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ *
+ * Implementation of cl_lock for OSC layer.
+ *
+ * Author: Nikita Danilov <nikita.danilov@sun.com>
+ */
+
+#define DEBUG_SUBSYSTEM S_OSC
+
+#include "../../include/linux/libcfs/libcfs.h"
+/* fid_build_reg_res_name() */
+#include "../include/lustre_fid.h"
+
+#include "osc_cl_internal.h"
+
+/** \addtogroup osc
+ * @{
+ */
+
+#define _PAGEREF_MAGIC (-10000000)
+
+/*****************************************************************************
+ *
+ * Type conversions.
+ *
+ */
+
+static const struct cl_lock_operations osc_lock_ops;
+static const struct cl_lock_operations osc_lock_lockless_ops;
+static void osc_lock_to_lockless(const struct lu_env *env,
+ struct osc_lock *ols, int force);
+static int osc_lock_has_pages(struct osc_lock *olck);
+
+int osc_lock_is_lockless(const struct osc_lock *olck)
+{
+ return (olck->ols_cl.cls_ops == &osc_lock_lockless_ops);
+}
+
+/**
+ * Returns a weak pointer to the ldlm lock identified by a handle. Returned
+ * pointer cannot be dereferenced, as lock is not protected from concurrent
+ * reclaim. This function is a helper for osc_lock_invariant().
+ */
+static struct ldlm_lock *osc_handle_ptr(struct lustre_handle *handle)
+{
+ struct ldlm_lock *lock;
+
+ lock = ldlm_handle2lock(handle);
+ if (lock != NULL)
+ LDLM_LOCK_PUT(lock);
+ return lock;
+}
+
+/**
+ * Invariant that has to be true all of the time.
+ */
+static int osc_lock_invariant(struct osc_lock *ols)
+{
+ struct ldlm_lock *lock = osc_handle_ptr(&ols->ols_handle);
+ struct ldlm_lock *olock = ols->ols_lock;
+ int handle_used = lustre_handle_is_used(&ols->ols_handle);
+
+ if (ergo(osc_lock_is_lockless(ols),
+ ols->ols_locklessable && ols->ols_lock == NULL))
+ return 1;
+
+ /*
+ * If all the following "ergo"s are true, return 1, otherwise 0
+ */
+ if (!ergo(olock != NULL, handle_used))
+ return 0;
+
+ if (!ergo(olock != NULL,
+ olock->l_handle.h_cookie == ols->ols_handle.cookie))
+ return 0;
+
+ if (!ergo(handle_used,
+ ergo(lock != NULL && olock != NULL, lock == olock) &&
+ ergo(lock == NULL, olock == NULL)))
+ return 0;
+ /*
+ * Check that ->ols_handle and ->ols_lock are consistent, but
+ * take into account that they are set at the different time.
+ */
+ if (!ergo(ols->ols_state == OLS_CANCELLED,
+ olock == NULL && !handle_used))
+ return 0;
+ /*
+ * DLM lock is destroyed only after we have seen cancellation
+ * ast.
+ */
+ if (!ergo(olock != NULL && ols->ols_state < OLS_CANCELLED,
+ ((olock->l_flags & LDLM_FL_DESTROYED) == 0)))
+ return 0;
+
+ if (!ergo(ols->ols_state == OLS_GRANTED,
+ olock != NULL &&
+ olock->l_req_mode == olock->l_granted_mode &&
+ ols->ols_hold))
+ return 0;
+ return 1;
+}
+
+/*****************************************************************************
+ *
+ * Lock operations.
+ *
+ */
+
+/**
+ * Breaks a link between osc_lock and dlm_lock.
+ */
+static void osc_lock_detach(const struct lu_env *env, struct osc_lock *olck)
+{
+ struct ldlm_lock *dlmlock;
+
+ spin_lock(&osc_ast_guard);
+ dlmlock = olck->ols_lock;
+ if (dlmlock == NULL) {
+ spin_unlock(&osc_ast_guard);
+ return;
+ }
+
+ olck->ols_lock = NULL;
+ /* wb(); --- for all who checks (ols->ols_lock != NULL) before
+ * call to osc_lock_detach() */
+ dlmlock->l_ast_data = NULL;
+ olck->ols_handle.cookie = 0ULL;
+ spin_unlock(&osc_ast_guard);
+
+ lock_res_and_lock(dlmlock);
+ if (dlmlock->l_granted_mode == dlmlock->l_req_mode) {
+ struct cl_object *obj = olck->ols_cl.cls_obj;
+ struct cl_attr *attr = &osc_env_info(env)->oti_attr;
+ __u64 old_kms;
+
+ cl_object_attr_lock(obj);
+ /* Must get the value under the lock to avoid possible races. */
+ old_kms = cl2osc(obj)->oo_oinfo->loi_kms;
+ /* Update the kms. Need to loop all granted locks.
+ * Not a problem for the client */
+ attr->cat_kms = ldlm_extent_shift_kms(dlmlock, old_kms);
+
+ cl_object_attr_set(env, obj, attr, CAT_KMS);
+ cl_object_attr_unlock(obj);
+ }
+ unlock_res_and_lock(dlmlock);
+
+ /* release a reference taken in osc_lock_upcall0(). */
+ LASSERT(olck->ols_has_ref);
+ lu_ref_del(&dlmlock->l_reference, "osc_lock", olck);
+ LDLM_LOCK_RELEASE(dlmlock);
+ olck->ols_has_ref = 0;
+}
+
+static int osc_lock_unhold(struct osc_lock *ols)
+{
+ int result = 0;
+
+ if (ols->ols_hold) {
+ ols->ols_hold = 0;
+ result = osc_cancel_base(&ols->ols_handle,
+ ols->ols_einfo.ei_mode);
+ }
+ return result;
+}
+
+static int osc_lock_unuse(const struct lu_env *env,
+ const struct cl_lock_slice *slice)
+{
+ struct osc_lock *ols = cl2osc_lock(slice);
+
+ LINVRNT(osc_lock_invariant(ols));
+
+ switch (ols->ols_state) {
+ case OLS_NEW:
+ LASSERT(!ols->ols_hold);
+ LASSERT(ols->ols_agl);
+ return 0;
+ case OLS_UPCALL_RECEIVED:
+ osc_lock_unhold(ols);
+ case OLS_ENQUEUED:
+ LASSERT(!ols->ols_hold);
+ osc_lock_detach(env, ols);
+ ols->ols_state = OLS_NEW;
+ return 0;
+ case OLS_GRANTED:
+ LASSERT(!ols->ols_glimpse);
+ LASSERT(ols->ols_hold);
+ /*
+ * Move lock into OLS_RELEASED state before calling
+ * osc_cancel_base() so that possible synchronous cancellation
+ * (that always happens e.g., for liblustre) sees that lock is
+ * released.
+ */
+ ols->ols_state = OLS_RELEASED;
+ return osc_lock_unhold(ols);
+ default:
+ CERROR("Impossible state: %d\n", ols->ols_state);
+ LBUG();
+ }
+}
+
+static void osc_lock_fini(const struct lu_env *env,
+ struct cl_lock_slice *slice)
+{
+ struct osc_lock *ols = cl2osc_lock(slice);
+
+ LINVRNT(osc_lock_invariant(ols));
+ /*
+ * ->ols_hold can still be true at this point if, for example, a
+ * thread that requested a lock was killed (and released a reference
+ * to the lock), before reply from a server was received. In this case
+ * lock is destroyed immediately after upcall.
+ */
+ osc_lock_unhold(ols);
+ LASSERT(ols->ols_lock == NULL);
+ LASSERT(atomic_read(&ols->ols_pageref) == 0 ||
+ atomic_read(&ols->ols_pageref) == _PAGEREF_MAGIC);
+
+ OBD_SLAB_FREE_PTR(ols, osc_lock_kmem);
+}
+
+static void osc_lock_build_policy(const struct lu_env *env,
+ const struct cl_lock *lock,
+ ldlm_policy_data_t *policy)
+{
+ const struct cl_lock_descr *d = &lock->cll_descr;
+
+ osc_index2policy(policy, d->cld_obj, d->cld_start, d->cld_end);
+ policy->l_extent.gid = d->cld_gid;
+}
+
+static __u64 osc_enq2ldlm_flags(__u32 enqflags)
+{
+ __u64 result = 0;
+
+ LASSERT((enqflags & ~CEF_MASK) == 0);
+
+ if (enqflags & CEF_NONBLOCK)
+ result |= LDLM_FL_BLOCK_NOWAIT;
+ if (enqflags & CEF_ASYNC)
+ result |= LDLM_FL_HAS_INTENT;
+ if (enqflags & CEF_DISCARD_DATA)
+ result |= LDLM_FL_AST_DISCARD_DATA;
+ return result;
+}
+
+/**
+ * Global spin-lock protecting consistency of ldlm_lock::l_ast_data
+ * pointers. Initialized in osc_init().
+ */
+spinlock_t osc_ast_guard;
+
+static struct osc_lock *osc_ast_data_get(struct ldlm_lock *dlm_lock)
+{
+ struct osc_lock *olck;
+
+ lock_res_and_lock(dlm_lock);
+ spin_lock(&osc_ast_guard);
+ olck = dlm_lock->l_ast_data;
+ if (olck != NULL) {
+ struct cl_lock *lock = olck->ols_cl.cls_lock;
+ /*
+ * If osc_lock holds a reference on ldlm lock, return it even
+ * when cl_lock is in CLS_FREEING state. This way
+ *
+ * osc_ast_data_get(dlmlock) == NULL
+ *
+ * guarantees that all osc references on dlmlock were
+ * released. osc_dlm_blocking_ast0() relies on that.
+ */
+ if (lock->cll_state < CLS_FREEING || olck->ols_has_ref) {
+ cl_lock_get_trust(lock);
+ lu_ref_add_atomic(&lock->cll_reference,
+ "ast", current);
+ } else
+ olck = NULL;
+ }
+ spin_unlock(&osc_ast_guard);
+ unlock_res_and_lock(dlm_lock);
+ return olck;
+}
+
+static void osc_ast_data_put(const struct lu_env *env, struct osc_lock *olck)
+{
+ struct cl_lock *lock;
+
+ lock = olck->ols_cl.cls_lock;
+ lu_ref_del(&lock->cll_reference, "ast", current);
+ cl_lock_put(env, lock);
+}
+
+/**
+ * Updates object attributes from a lock value block (lvb) received together
+ * with the DLM lock reply from the server. Copy of osc_update_enqueue()
+ * logic.
+ *
+ * This can be optimized to not update attributes when lock is a result of a
+ * local match.
+ *
+ * Called under lock and resource spin-locks.
+ */
+static void osc_lock_lvb_update(const struct lu_env *env, struct osc_lock *olck,
+ int rc)
+{
+ struct ost_lvb *lvb;
+ struct cl_object *obj;
+ struct lov_oinfo *oinfo;
+ struct cl_attr *attr;
+ unsigned valid;
+
+ if (!(olck->ols_flags & LDLM_FL_LVB_READY))
+ return;
+
+ lvb = &olck->ols_lvb;
+ obj = olck->ols_cl.cls_obj;
+ oinfo = cl2osc(obj)->oo_oinfo;
+ attr = &osc_env_info(env)->oti_attr;
+ valid = CAT_BLOCKS | CAT_ATIME | CAT_CTIME | CAT_MTIME | CAT_SIZE;
+ cl_lvb2attr(attr, lvb);
+
+ cl_object_attr_lock(obj);
+ if (rc == 0) {
+ struct ldlm_lock *dlmlock;
+ __u64 size;
+
+ dlmlock = olck->ols_lock;
+ LASSERT(dlmlock != NULL);
+
+ /* re-grab LVB from a dlm lock under DLM spin-locks. */
+ *lvb = *(struct ost_lvb *)dlmlock->l_lvb_data;
+ size = lvb->lvb_size;
+ /* Extend KMS up to the end of this lock and no further
+ * A lock on [x,y] means a KMS of up to y + 1 bytes! */
+ if (size > dlmlock->l_policy_data.l_extent.end)
+ size = dlmlock->l_policy_data.l_extent.end + 1;
+ if (size >= oinfo->loi_kms) {
+ LDLM_DEBUG(dlmlock, "lock acquired, setting rss=%llu, kms=%llu",
+ lvb->lvb_size, size);
+ valid |= CAT_KMS;
+ attr->cat_kms = size;
+ } else {
+ LDLM_DEBUG(dlmlock, "lock acquired, setting rss=%llu; leaving kms=%llu, end=%llu",
+ lvb->lvb_size, oinfo->loi_kms,
+ dlmlock->l_policy_data.l_extent.end);
+ }
+ ldlm_lock_allow_match_locked(dlmlock);
+ } else if (rc == -ENAVAIL && olck->ols_glimpse) {
+ CDEBUG(D_INODE, "glimpsed, setting rss=%llu; leaving kms=%llu\n",
+ lvb->lvb_size, oinfo->loi_kms);
+ } else
+ valid = 0;
+
+ if (valid != 0)
+ cl_object_attr_set(env, obj, attr, valid);
+
+ cl_object_attr_unlock(obj);
+}
+
+/**
+ * Called when a lock is granted, from an upcall (when server returned a
+ * granted lock), or from completion AST, when server returned a blocked lock.
+ *
+ * Called under lock and resource spin-locks, that are released temporarily
+ * here.
+ */
+static void osc_lock_granted(const struct lu_env *env, struct osc_lock *olck,
+ struct ldlm_lock *dlmlock, int rc)
+{
+ struct ldlm_extent *ext;
+ struct cl_lock *lock;
+ struct cl_lock_descr *descr;
+
+ LASSERT(dlmlock->l_granted_mode == dlmlock->l_req_mode);
+
+ if (olck->ols_state < OLS_GRANTED) {
+ lock = olck->ols_cl.cls_lock;
+ ext = &dlmlock->l_policy_data.l_extent;
+ descr = &osc_env_info(env)->oti_descr;
+ descr->cld_obj = lock->cll_descr.cld_obj;
+
+ /* XXX check that ->l_granted_mode is valid. */
+ descr->cld_mode = osc_ldlm2cl_lock(dlmlock->l_granted_mode);
+ descr->cld_start = cl_index(descr->cld_obj, ext->start);
+ descr->cld_end = cl_index(descr->cld_obj, ext->end);
+ descr->cld_gid = ext->gid;
+ /*
+ * tell upper layers the extent of the lock that was actually
+ * granted
+ */
+ olck->ols_state = OLS_GRANTED;
+ osc_lock_lvb_update(env, olck, rc);
+
+ /* release DLM spin-locks to allow cl_lock_{modify,signal}()
+ * to take a semaphore on a parent lock. This is safe, because
+ * spin-locks are needed to protect consistency of
+ * dlmlock->l_*_mode and LVB, and we have finished processing
+ * them. */
+ unlock_res_and_lock(dlmlock);
+ cl_lock_modify(env, lock, descr);
+ cl_lock_signal(env, lock);
+ LINVRNT(osc_lock_invariant(olck));
+ lock_res_and_lock(dlmlock);
+ }
+}
+
+static void osc_lock_upcall0(const struct lu_env *env, struct osc_lock *olck)
+
+{
+ struct ldlm_lock *dlmlock;
+
+ dlmlock = ldlm_handle2lock_long(&olck->ols_handle, 0);
+ LASSERT(dlmlock != NULL);
+
+ lock_res_and_lock(dlmlock);
+ spin_lock(&osc_ast_guard);
+ LASSERT(dlmlock->l_ast_data == olck);
+ LASSERT(olck->ols_lock == NULL);
+ olck->ols_lock = dlmlock;
+ spin_unlock(&osc_ast_guard);
+
+ /*
+ * Lock might be not yet granted. In this case, completion ast
+ * (osc_ldlm_completion_ast()) comes later and finishes lock
+ * granting.
+ */
+ if (dlmlock->l_granted_mode == dlmlock->l_req_mode)
+ osc_lock_granted(env, olck, dlmlock, 0);
+ unlock_res_and_lock(dlmlock);
+
+ /*
+ * osc_enqueue_interpret() decrefs asynchronous locks, counter
+ * this.
+ */
+ ldlm_lock_addref(&olck->ols_handle, olck->ols_einfo.ei_mode);
+ olck->ols_hold = 1;
+
+ /* lock reference taken by ldlm_handle2lock_long() is owned by
+ * osc_lock and released in osc_lock_detach() */
+ lu_ref_add(&dlmlock->l_reference, "osc_lock", olck);
+ olck->ols_has_ref = 1;
+}
+
+/**
+ * Lock upcall function that is executed either when a reply to ENQUEUE rpc is
+ * received from a server, or after osc_enqueue_base() matched a local DLM
+ * lock.
+ */
+static int osc_lock_upcall(void *cookie, int errcode)
+{
+ struct osc_lock *olck = cookie;
+ struct cl_lock_slice *slice = &olck->ols_cl;
+ struct cl_lock *lock = slice->cls_lock;
+ struct lu_env *env;
+ struct cl_env_nest nest;
+
+ env = cl_env_nested_get(&nest);
+ if (!IS_ERR(env)) {
+ int rc;
+
+ cl_lock_mutex_get(env, lock);
+
+ LASSERT(lock->cll_state >= CLS_QUEUING);
+ if (olck->ols_state == OLS_ENQUEUED) {
+ olck->ols_state = OLS_UPCALL_RECEIVED;
+ rc = ldlm_error2errno(errcode);
+ } else if (olck->ols_state == OLS_CANCELLED) {
+ rc = -EIO;
+ } else {
+ CERROR("Impossible state: %d\n", olck->ols_state);
+ LBUG();
+ }
+ if (rc) {
+ struct ldlm_lock *dlmlock;
+
+ dlmlock = ldlm_handle2lock(&olck->ols_handle);
+ if (dlmlock != NULL) {
+ lock_res_and_lock(dlmlock);
+ spin_lock(&osc_ast_guard);
+ LASSERT(olck->ols_lock == NULL);
+ dlmlock->l_ast_data = NULL;
+ olck->ols_handle.cookie = 0ULL;
+ spin_unlock(&osc_ast_guard);
+ ldlm_lock_fail_match_locked(dlmlock);
+ unlock_res_and_lock(dlmlock);
+ LDLM_LOCK_PUT(dlmlock);
+ }
+ } else {
+ if (olck->ols_glimpse)
+ olck->ols_glimpse = 0;
+ osc_lock_upcall0(env, olck);
+ }
+
+ /* Error handling, some errors are tolerable. */
+ if (olck->ols_locklessable && rc == -EUSERS) {
+ /* This is a tolerable error, turn this lock into
+ * lockless lock.
+ */
+ osc_object_set_contended(cl2osc(slice->cls_obj));
+ LASSERT(slice->cls_ops == &osc_lock_ops);
+
+ /* Change this lock to ldlmlock-less lock. */
+ osc_lock_to_lockless(env, olck, 1);
+ olck->ols_state = OLS_GRANTED;
+ rc = 0;
+ } else if (olck->ols_glimpse && rc == -ENAVAIL) {
+ osc_lock_lvb_update(env, olck, rc);
+ cl_lock_delete(env, lock);
+ /* Hide the error. */
+ rc = 0;
+ }
+
+ if (rc == 0) {
+ /* For AGL case, the RPC sponsor may exits the cl_lock
+ * processing without wait() called before related OSC
+ * lock upcall(). So update the lock status according
+ * to the enqueue result inside AGL upcall(). */
+ if (olck->ols_agl) {
+ lock->cll_flags |= CLF_FROM_UPCALL;
+ cl_wait_try(env, lock);
+ lock->cll_flags &= ~CLF_FROM_UPCALL;
+ if (!olck->ols_glimpse)
+ olck->ols_agl = 0;
+ }
+ cl_lock_signal(env, lock);
+ /* del user for lock upcall cookie */
+ cl_unuse_try(env, lock);
+ } else {
+ /* del user for lock upcall cookie */
+ cl_lock_user_del(env, lock);
+ cl_lock_error(env, lock, rc);
+ }
+
+ /* release cookie reference, acquired by osc_lock_enqueue() */
+ cl_lock_hold_release(env, lock, "upcall", lock);
+ cl_lock_mutex_put(env, lock);
+
+ lu_ref_del(&lock->cll_reference, "upcall", lock);
+ /* This maybe the last reference, so must be called after
+ * cl_lock_mutex_put(). */
+ cl_lock_put(env, lock);
+
+ cl_env_nested_put(&nest, env);
+ } else {
+ /* should never happen, similar to osc_ldlm_blocking_ast(). */
+ LBUG();
+ }
+ return errcode;
+}
+
+/**
+ * Core of osc_dlm_blocking_ast() logic.
+ */
+static void osc_lock_blocking(const struct lu_env *env,
+ struct ldlm_lock *dlmlock,
+ struct osc_lock *olck, int blocking)
+{
+ struct cl_lock *lock = olck->ols_cl.cls_lock;
+
+ LASSERT(olck->ols_lock == dlmlock);
+ CLASSERT(OLS_BLOCKED < OLS_CANCELLED);
+ LASSERT(!osc_lock_is_lockless(olck));
+
+ /*
+ * Lock might be still addref-ed here, if e.g., blocking ast
+ * is sent for a failed lock.
+ */
+ osc_lock_unhold(olck);
+
+ if (blocking && olck->ols_state < OLS_BLOCKED)
+ /*
+ * Move osc_lock into OLS_BLOCKED before canceling the lock,
+ * because it recursively re-enters osc_lock_blocking(), with
+ * the state set to OLS_CANCELLED.
+ */
+ olck->ols_state = OLS_BLOCKED;
+ /*
+ * cancel and destroy lock at least once no matter how blocking ast is
+ * entered (see comment above osc_ldlm_blocking_ast() for use
+ * cases). cl_lock_cancel() and cl_lock_delete() are idempotent.
+ */
+ cl_lock_cancel(env, lock);
+ cl_lock_delete(env, lock);
+}
+
+/**
+ * Helper for osc_dlm_blocking_ast() handling discrepancies between cl_lock
+ * and ldlm_lock caches.
+ */
+static int osc_dlm_blocking_ast0(const struct lu_env *env,
+ struct ldlm_lock *dlmlock,
+ void *data, int flag)
+{
+ struct osc_lock *olck;
+ struct cl_lock *lock;
+ int result;
+ int cancel;
+
+ LASSERT(flag == LDLM_CB_BLOCKING || flag == LDLM_CB_CANCELING);
+
+ cancel = 0;
+ olck = osc_ast_data_get(dlmlock);
+ if (olck != NULL) {
+ lock = olck->ols_cl.cls_lock;
+ cl_lock_mutex_get(env, lock);
+ LINVRNT(osc_lock_invariant(olck));
+ if (olck->ols_ast_wait) {
+ /* wake up osc_lock_use() */
+ cl_lock_signal(env, lock);
+ olck->ols_ast_wait = 0;
+ }
+ /*
+ * Lock might have been canceled while this thread was
+ * sleeping for lock mutex, but olck is pinned in memory.
+ */
+ if (olck == dlmlock->l_ast_data) {
+ /*
+ * NOTE: DLM sends blocking AST's for failed locks
+ * (that are still in pre-OLS_GRANTED state)
+ * too, and they have to be canceled otherwise
+ * DLM lock is never destroyed and stuck in
+ * the memory.
+ *
+ * Alternatively, ldlm_cli_cancel() can be
+ * called here directly for osc_locks with
+ * ols_state < OLS_GRANTED to maintain an
+ * invariant that ->clo_cancel() is only called
+ * for locks that were granted.
+ */
+ LASSERT(data == olck);
+ osc_lock_blocking(env, dlmlock,
+ olck, flag == LDLM_CB_BLOCKING);
+ } else
+ cancel = 1;
+ cl_lock_mutex_put(env, lock);
+ osc_ast_data_put(env, olck);
+ } else
+ /*
+ * DLM lock exists, but there is no cl_lock attached to it.
+ * This is a `normal' race. cl_object and its cl_lock's can be
+ * removed by memory pressure, together with all pages.
+ */
+ cancel = (flag == LDLM_CB_BLOCKING);
+
+ if (cancel) {
+ struct lustre_handle *lockh;
+
+ lockh = &osc_env_info(env)->oti_handle;
+ ldlm_lock2handle(dlmlock, lockh);
+ result = ldlm_cli_cancel(lockh, LCF_ASYNC);
+ } else
+ result = 0;
+ return result;
+}
+
+/**
+ * Blocking ast invoked by ldlm when dlm lock is either blocking progress of
+ * some other lock, or is canceled. This function is installed as a
+ * ldlm_lock::l_blocking_ast() for client extent locks.
+ *
+ * Control flow is tricky, because ldlm uses the same call-back
+ * (ldlm_lock::l_blocking_ast()) for both blocking and cancellation ast's.
+ *
+ * \param dlmlock lock for which ast occurred.
+ *
+ * \param new description of a conflicting lock in case of blocking ast.
+ *
+ * \param data value of dlmlock->l_ast_data
+ *
+ * \param flag LDLM_CB_BLOCKING or LDLM_CB_CANCELING. Used to distinguish
+ * cancellation and blocking ast's.
+ *
+ * Possible use cases:
+ *
+ * - ldlm calls dlmlock->l_blocking_ast(..., LDLM_CB_CANCELING) to cancel
+ * lock due to lock lru pressure, or explicit user request to purge
+ * locks.
+ *
+ * - ldlm calls dlmlock->l_blocking_ast(..., LDLM_CB_BLOCKING) to notify
+ * us that dlmlock conflicts with another lock that some client is
+ * enqueing. Lock is canceled.
+ *
+ * - cl_lock_cancel() is called. osc_lock_cancel() calls
+ * ldlm_cli_cancel() that calls
+ *
+ * dlmlock->l_blocking_ast(..., LDLM_CB_CANCELING)
+ *
+ * recursively entering osc_ldlm_blocking_ast().
+ *
+ * - client cancels lock voluntary (e.g., as a part of early cancellation):
+ *
+ * cl_lock_cancel()->
+ * osc_lock_cancel()->
+ * ldlm_cli_cancel()->
+ * dlmlock->l_blocking_ast(..., LDLM_CB_CANCELING)
+ *
+ */
+static int osc_ldlm_blocking_ast(struct ldlm_lock *dlmlock,
+ struct ldlm_lock_desc *new, void *data,
+ int flag)
+{
+ struct lu_env *env;
+ struct cl_env_nest nest;
+ int result;
+
+ /*
+ * This can be called in the context of outer IO, e.g.,
+ *
+ * cl_enqueue()->...
+ * ->osc_enqueue_base()->...
+ * ->ldlm_prep_elc_req()->...
+ * ->ldlm_cancel_callback()->...
+ * ->osc_ldlm_blocking_ast()
+ *
+ * new environment has to be created to not corrupt outer context.
+ */
+ env = cl_env_nested_get(&nest);
+ if (!IS_ERR(env)) {
+ result = osc_dlm_blocking_ast0(env, dlmlock, data, flag);
+ cl_env_nested_put(&nest, env);
+ } else {
+ result = PTR_ERR(env);
+ /*
+ * XXX This should never happen, as cl_lock is
+ * stuck. Pre-allocated environment a la vvp_inode_fini_env
+ * should be used.
+ */
+ LBUG();
+ }
+ if (result != 0) {
+ if (result == -ENODATA)
+ result = 0;
+ else
+ CERROR("BAST failed: %d\n", result);
+ }
+ return result;
+}
+
+static int osc_ldlm_completion_ast(struct ldlm_lock *dlmlock,
+ __u64 flags, void *data)
+{
+ struct cl_env_nest nest;
+ struct lu_env *env;
+ struct osc_lock *olck;
+ struct cl_lock *lock;
+ int result;
+ int dlmrc;
+
+ /* first, do dlm part of the work */
+ dlmrc = ldlm_completion_ast_async(dlmlock, flags, data);
+ /* then, notify cl_lock */
+ env = cl_env_nested_get(&nest);
+ if (!IS_ERR(env)) {
+ olck = osc_ast_data_get(dlmlock);
+ if (olck != NULL) {
+ lock = olck->ols_cl.cls_lock;
+ cl_lock_mutex_get(env, lock);
+ /*
+ * ldlm_handle_cp_callback() copied LVB from request
+ * to lock->l_lvb_data, store it in osc_lock.
+ */
+ LASSERT(dlmlock->l_lvb_data != NULL);
+ lock_res_and_lock(dlmlock);
+ olck->ols_lvb = *(struct ost_lvb *)dlmlock->l_lvb_data;
+ if (olck->ols_lock == NULL) {
+ /*
+ * upcall (osc_lock_upcall()) hasn't yet been
+ * called. Do nothing now, upcall will bind
+ * olck to dlmlock and signal the waiters.
+ *
+ * This maintains an invariant that osc_lock
+ * and ldlm_lock are always bound when
+ * osc_lock is in OLS_GRANTED state.
+ */
+ } else if (dlmlock->l_granted_mode ==
+ dlmlock->l_req_mode) {
+ osc_lock_granted(env, olck, dlmlock, dlmrc);
+ }
+ unlock_res_and_lock(dlmlock);
+
+ if (dlmrc != 0) {
+ CL_LOCK_DEBUG(D_ERROR, env, lock,
+ "dlmlock returned %d\n", dlmrc);
+ cl_lock_error(env, lock, dlmrc);
+ }
+ cl_lock_mutex_put(env, lock);
+ osc_ast_data_put(env, olck);
+ result = 0;
+ } else
+ result = -ELDLM_NO_LOCK_DATA;
+ cl_env_nested_put(&nest, env);
+ } else
+ result = PTR_ERR(env);
+ return dlmrc ?: result;
+}
+
+static int osc_ldlm_glimpse_ast(struct ldlm_lock *dlmlock, void *data)
+{
+ struct ptlrpc_request *req = data;
+ struct osc_lock *olck;
+ struct cl_lock *lock;
+ struct cl_object *obj;
+ struct cl_env_nest nest;
+ struct lu_env *env;
+ struct ost_lvb *lvb;
+ struct req_capsule *cap;
+ int result;
+
+ LASSERT(lustre_msg_get_opc(req->rq_reqmsg) == LDLM_GL_CALLBACK);
+
+ env = cl_env_nested_get(&nest);
+ if (!IS_ERR(env)) {
+ /* osc_ast_data_get() has to go after environment is
+ * allocated, because osc_ast_data() acquires a
+ * reference to a lock, and it can only be released in
+ * environment.
+ */
+ olck = osc_ast_data_get(dlmlock);
+ if (olck != NULL) {
+ lock = olck->ols_cl.cls_lock;
+ /* Do not grab the mutex of cl_lock for glimpse.
+ * See LU-1274 for details.
+ * BTW, it's okay for cl_lock to be cancelled during
+ * this period because server can handle this race.
+ * See ldlm_server_glimpse_ast() for details.
+ * cl_lock_mutex_get(env, lock); */
+ cap = &req->rq_pill;
+ req_capsule_extend(cap, &RQF_LDLM_GL_CALLBACK);
+ req_capsule_set_size(cap, &RMF_DLM_LVB, RCL_SERVER,
+ sizeof(*lvb));
+ result = req_capsule_server_pack(cap);
+ if (result == 0) {
+ lvb = req_capsule_server_get(cap, &RMF_DLM_LVB);
+ obj = lock->cll_descr.cld_obj;
+ result = cl_object_glimpse(env, obj, lvb);
+ }
+ if (!exp_connect_lvb_type(req->rq_export))
+ req_capsule_shrink(&req->rq_pill,
+ &RMF_DLM_LVB,
+ sizeof(struct ost_lvb_v1),
+ RCL_SERVER);
+ osc_ast_data_put(env, olck);
+ } else {
+ /*
+ * These errors are normal races, so we don't want to
+ * fill the console with messages by calling
+ * ptlrpc_error()
+ */
+ lustre_pack_reply(req, 1, NULL, NULL);
+ result = -ELDLM_NO_LOCK_DATA;
+ }
+ cl_env_nested_put(&nest, env);
+ } else
+ result = PTR_ERR(env);
+ req->rq_status = result;
+ return result;
+}
+
+static unsigned long osc_lock_weigh(const struct lu_env *env,
+ const struct cl_lock_slice *slice)
+{
+ /*
+ * don't need to grab coh_page_guard since we don't care the exact #
+ * of pages..
+ */
+ return cl_object_header(slice->cls_obj)->coh_pages;
+}
+
+static void osc_lock_build_einfo(const struct lu_env *env,
+ const struct cl_lock *clock,
+ struct osc_lock *lock,
+ struct ldlm_enqueue_info *einfo)
+{
+ enum cl_lock_mode mode;
+
+ mode = clock->cll_descr.cld_mode;
+ if (mode == CLM_PHANTOM)
+ /*
+ * For now, enqueue all glimpse locks in read mode. In the
+ * future, client might choose to enqueue LCK_PW lock for
+ * glimpse on a file opened for write.
+ */
+ mode = CLM_READ;
+
+ einfo->ei_type = LDLM_EXTENT;
+ einfo->ei_mode = osc_cl_lock2ldlm(mode);
+ einfo->ei_cb_bl = osc_ldlm_blocking_ast;
+ einfo->ei_cb_cp = osc_ldlm_completion_ast;
+ einfo->ei_cb_gl = osc_ldlm_glimpse_ast;
+ einfo->ei_cbdata = lock; /* value to be put into ->l_ast_data */
+}
+
+/**
+ * Determine if the lock should be converted into a lockless lock.
+ *
+ * Steps to check:
+ * - if the lock has an explicit requirement for a non-lockless lock;
+ * - if the io lock request type ci_lockreq;
+ * - send the enqueue rpc to ost to make the further decision;
+ * - special treat to truncate lockless lock
+ *
+ * Additional policy can be implemented here, e.g., never do lockless-io
+ * for large extents.
+ */
+static void osc_lock_to_lockless(const struct lu_env *env,
+ struct osc_lock *ols, int force)
+{
+ struct cl_lock_slice *slice = &ols->ols_cl;
+
+ LASSERT(ols->ols_state == OLS_NEW ||
+ ols->ols_state == OLS_UPCALL_RECEIVED);
+
+ if (force) {
+ ols->ols_locklessable = 1;
+ slice->cls_ops = &osc_lock_lockless_ops;
+ } else {
+ struct osc_io *oio = osc_env_io(env);
+ struct cl_io *io = oio->oi_cl.cis_io;
+ struct cl_object *obj = slice->cls_obj;
+ struct osc_object *oob = cl2osc(obj);
+ const struct osc_device *osd = lu2osc_dev(obj->co_lu.lo_dev);
+ struct obd_connect_data *ocd;
+
+ LASSERT(io->ci_lockreq == CILR_MANDATORY ||
+ io->ci_lockreq == CILR_MAYBE ||
+ io->ci_lockreq == CILR_NEVER);
+
+ ocd = &class_exp2cliimp(osc_export(oob))->imp_connect_data;
+ ols->ols_locklessable = (io->ci_type != CIT_SETATTR) &&
+ (io->ci_lockreq == CILR_MAYBE) &&
+ (ocd->ocd_connect_flags & OBD_CONNECT_SRVLOCK);
+ if (io->ci_lockreq == CILR_NEVER ||
+ /* lockless IO */
+ (ols->ols_locklessable && osc_object_is_contended(oob)) ||
+ /* lockless truncate */
+ (cl_io_is_trunc(io) &&
+ (ocd->ocd_connect_flags & OBD_CONNECT_TRUNCLOCK) &&
+ osd->od_lockless_truncate)) {
+ ols->ols_locklessable = 1;
+ slice->cls_ops = &osc_lock_lockless_ops;
+ }
+ }
+ LASSERT(ergo(ols->ols_glimpse, !osc_lock_is_lockless(ols)));
+}
+
+static int osc_lock_compatible(const struct osc_lock *qing,
+ const struct osc_lock *qed)
+{
+ enum cl_lock_mode qing_mode;
+ enum cl_lock_mode qed_mode;
+
+ qing_mode = qing->ols_cl.cls_lock->cll_descr.cld_mode;
+ if (qed->ols_glimpse &&
+ (qed->ols_state >= OLS_UPCALL_RECEIVED || qing_mode == CLM_READ))
+ return 1;
+
+ qed_mode = qed->ols_cl.cls_lock->cll_descr.cld_mode;
+ return ((qing_mode == CLM_READ) && (qed_mode == CLM_READ));
+}
+
+/**
+ * Cancel all conflicting locks and wait for them to be destroyed.
+ *
+ * This function is used for two purposes:
+ *
+ * - early cancel all conflicting locks before starting IO, and
+ *
+ * - guarantee that pages added to the page cache by lockless IO are never
+ * covered by locks other than lockless IO lock, and, hence, are not
+ * visible to other threads.
+ */
+static int osc_lock_enqueue_wait(const struct lu_env *env,
+ const struct osc_lock *olck)
+{
+ struct cl_lock *lock = olck->ols_cl.cls_lock;
+ struct cl_lock_descr *descr = &lock->cll_descr;
+ struct cl_object_header *hdr = cl_object_header(descr->cld_obj);
+ struct cl_lock *scan;
+ struct cl_lock *conflict = NULL;
+ int lockless = osc_lock_is_lockless(olck);
+ int rc = 0;
+
+ LASSERT(cl_lock_is_mutexed(lock));
+
+ /* make it enqueue anyway for glimpse lock, because we actually
+ * don't need to cancel any conflicting locks. */
+ if (olck->ols_glimpse)
+ return 0;
+
+ spin_lock(&hdr->coh_lock_guard);
+ list_for_each_entry(scan, &hdr->coh_locks, cll_linkage) {
+ struct cl_lock_descr *cld = &scan->cll_descr;
+ const struct osc_lock *scan_ols;
+
+ if (scan == lock)
+ break;
+
+ if (scan->cll_state < CLS_QUEUING ||
+ scan->cll_state == CLS_FREEING ||
+ cld->cld_start > descr->cld_end ||
+ cld->cld_end < descr->cld_start)
+ continue;
+
+ /* overlapped and living locks. */
+
+ /* We're not supposed to give up group lock. */
+ if (scan->cll_descr.cld_mode == CLM_GROUP) {
+ LASSERT(descr->cld_mode != CLM_GROUP ||
+ descr->cld_gid != scan->cll_descr.cld_gid);
+ continue;
+ }
+
+ scan_ols = osc_lock_at(scan);
+
+ /* We need to cancel the compatible locks if we're enqueuing
+ * a lockless lock, for example:
+ * imagine that client has PR lock on [0, 1000], and thread T0
+ * is doing lockless IO in [500, 1500] region. Concurrent
+ * thread T1 can see lockless data in [500, 1000], which is
+ * wrong, because these data are possibly stale. */
+ if (!lockless && osc_lock_compatible(olck, scan_ols))
+ continue;
+
+ cl_lock_get_trust(scan);
+ conflict = scan;
+ break;
+ }
+ spin_unlock(&hdr->coh_lock_guard);
+
+ if (conflict) {
+ if (lock->cll_descr.cld_mode == CLM_GROUP) {
+ /* we want a group lock but a previous lock request
+ * conflicts, we do not wait but return 0 so the
+ * request is send to the server
+ */
+ CDEBUG(D_DLMTRACE, "group lock %p is conflicted with %p, no wait, send to server\n",
+ lock, conflict);
+ cl_lock_put(env, conflict);
+ rc = 0;
+ } else {
+ CDEBUG(D_DLMTRACE, "lock %p is conflicted with %p, will wait\n",
+ lock, conflict);
+ LASSERT(lock->cll_conflict == NULL);
+ lu_ref_add(&conflict->cll_reference, "cancel-wait",
+ lock);
+ lock->cll_conflict = conflict;
+ rc = CLO_WAIT;
+ }
+ }
+ return rc;
+}
+
+/**
+ * Implementation of cl_lock_operations::clo_enqueue() method for osc
+ * layer. This initiates ldlm enqueue:
+ *
+ * - cancels conflicting locks early (osc_lock_enqueue_wait());
+ *
+ * - calls osc_enqueue_base() to do actual enqueue.
+ *
+ * osc_enqueue_base() is supplied with an upcall function that is executed
+ * when lock is received either after a local cached ldlm lock is matched, or
+ * when a reply from the server is received.
+ *
+ * This function does not wait for the network communication to complete.
+ */
+static int osc_lock_enqueue(const struct lu_env *env,
+ const struct cl_lock_slice *slice,
+ struct cl_io *unused, __u32 enqflags)
+{
+ struct osc_lock *ols = cl2osc_lock(slice);
+ struct cl_lock *lock = ols->ols_cl.cls_lock;
+ int result;
+
+ LASSERT(cl_lock_is_mutexed(lock));
+ LASSERTF(ols->ols_state == OLS_NEW,
+ "Impossible state: %d\n", ols->ols_state);
+
+ LASSERTF(ergo(ols->ols_glimpse, lock->cll_descr.cld_mode <= CLM_READ),
+ "lock = %p, ols = %p\n", lock, ols);
+
+ result = osc_lock_enqueue_wait(env, ols);
+ if (result == 0) {
+ if (!osc_lock_is_lockless(ols)) {
+ struct osc_object *obj = cl2osc(slice->cls_obj);
+ struct osc_thread_info *info = osc_env_info(env);
+ struct ldlm_res_id *resname = &info->oti_resname;
+ ldlm_policy_data_t *policy = &info->oti_policy;
+ struct ldlm_enqueue_info *einfo = &ols->ols_einfo;
+
+ /* lock will be passed as upcall cookie,
+ * hold ref to prevent to be released. */
+ cl_lock_hold_add(env, lock, "upcall", lock);
+ /* a user for lock also */
+ cl_lock_user_add(env, lock);
+ ols->ols_state = OLS_ENQUEUED;
+
+ /*
+ * XXX: this is possible blocking point as
+ * ldlm_lock_match(LDLM_FL_LVB_READY) waits for
+ * LDLM_CP_CALLBACK.
+ */
+ ostid_build_res_name(&obj->oo_oinfo->loi_oi, resname);
+ osc_lock_build_policy(env, lock, policy);
+ result = osc_enqueue_base(osc_export(obj), resname,
+ &ols->ols_flags, policy,
+ &ols->ols_lvb,
+ obj->oo_oinfo->loi_kms_valid,
+ osc_lock_upcall,
+ ols, einfo, &ols->ols_handle,
+ PTLRPCD_SET, 1, ols->ols_agl);
+ if (result != 0) {
+ cl_lock_user_del(env, lock);
+ cl_lock_unhold(env, lock, "upcall", lock);
+ if (unlikely(result == -ECANCELED)) {
+ ols->ols_state = OLS_NEW;
+ result = 0;
+ }
+ }
+ } else {
+ ols->ols_state = OLS_GRANTED;
+ ols->ols_owner = osc_env_io(env);
+ }
+ }
+ LASSERT(ergo(ols->ols_glimpse, !osc_lock_is_lockless(ols)));
+ return result;
+}
+
+static int osc_lock_wait(const struct lu_env *env,
+ const struct cl_lock_slice *slice)
+{
+ struct osc_lock *olck = cl2osc_lock(slice);
+ struct cl_lock *lock = olck->ols_cl.cls_lock;
+
+ LINVRNT(osc_lock_invariant(olck));
+
+ if (olck->ols_glimpse && olck->ols_state >= OLS_UPCALL_RECEIVED) {
+ if (olck->ols_flags & LDLM_FL_LVB_READY) {
+ return 0;
+ } else if (olck->ols_agl) {
+ if (lock->cll_flags & CLF_FROM_UPCALL)
+ /* It is from enqueue RPC reply upcall for
+ * updating state. Do not re-enqueue. */
+ return -ENAVAIL;
+ else
+ olck->ols_state = OLS_NEW;
+ } else {
+ LASSERT(lock->cll_error);
+ return lock->cll_error;
+ }
+ }
+
+ if (olck->ols_state == OLS_NEW) {
+ int rc;
+
+ LASSERT(olck->ols_agl);
+ olck->ols_agl = 0;
+ olck->ols_flags &= ~LDLM_FL_BLOCK_NOWAIT;
+ rc = osc_lock_enqueue(env, slice, NULL, CEF_ASYNC | CEF_MUST);
+ if (rc != 0)
+ return rc;
+ else
+ return CLO_REENQUEUED;
+ }
+
+ LASSERT(equi(olck->ols_state >= OLS_UPCALL_RECEIVED &&
+ lock->cll_error == 0, olck->ols_lock != NULL));
+
+ return lock->cll_error ?: olck->ols_state >= OLS_GRANTED ? 0 : CLO_WAIT;
+}
+
+/**
+ * An implementation of cl_lock_operations::clo_use() method that pins cached
+ * lock.
+ */
+static int osc_lock_use(const struct lu_env *env,
+ const struct cl_lock_slice *slice)
+{
+ struct osc_lock *olck = cl2osc_lock(slice);
+ int rc;
+
+ LASSERT(!olck->ols_hold);
+
+ /*
+ * Atomically check for LDLM_FL_CBPENDING and addref a lock if this
+ * flag is not set. This protects us from a concurrent blocking ast.
+ */
+ rc = ldlm_lock_addref_try(&olck->ols_handle, olck->ols_einfo.ei_mode);
+ if (rc == 0) {
+ olck->ols_hold = 1;
+ olck->ols_state = OLS_GRANTED;
+ } else {
+ struct cl_lock *lock;
+
+ /*
+ * Lock is being cancelled somewhere within
+ * ldlm_handle_bl_callback(): LDLM_FL_CBPENDING is already
+ * set, but osc_ldlm_blocking_ast() hasn't yet acquired
+ * cl_lock mutex.
+ */
+ lock = slice->cls_lock;
+ LASSERT(lock->cll_state == CLS_INTRANSIT);
+ LASSERT(lock->cll_users > 0);
+ /* set a flag for osc_dlm_blocking_ast0() to signal the
+ * lock.*/
+ olck->ols_ast_wait = 1;
+ rc = CLO_WAIT;
+ }
+ return rc;
+}
+
+static int osc_lock_flush(struct osc_lock *ols, int discard)
+{
+ struct cl_lock *lock = ols->ols_cl.cls_lock;
+ struct cl_env_nest nest;
+ struct lu_env *env;
+ int result = 0;
+
+ env = cl_env_nested_get(&nest);
+ if (!IS_ERR(env)) {
+ struct osc_object *obj = cl2osc(ols->ols_cl.cls_obj);
+ struct cl_lock_descr *descr = &lock->cll_descr;
+ int rc = 0;
+
+ if (descr->cld_mode >= CLM_WRITE) {
+ result = osc_cache_writeback_range(env, obj,
+ descr->cld_start, descr->cld_end,
+ 1, discard);
+ LDLM_DEBUG(ols->ols_lock,
+ "lock %p: %d pages were %s.\n", lock, result,
+ discard ? "discarded" : "written");
+ if (result > 0)
+ result = 0;
+ }
+
+ rc = cl_lock_discard_pages(env, lock);
+ if (result == 0 && rc < 0)
+ result = rc;
+
+ cl_env_nested_put(&nest, env);
+ } else
+ result = PTR_ERR(env);
+ if (result == 0) {
+ ols->ols_flush = 1;
+ LINVRNT(!osc_lock_has_pages(ols));
+ }
+ return result;
+}
+
+/**
+ * Implements cl_lock_operations::clo_cancel() method for osc layer. This is
+ * called (as part of cl_lock_cancel()) when lock is canceled either voluntary
+ * (LRU pressure, early cancellation, umount, etc.) or due to the conflict
+ * with some other lock some where in the cluster. This function does the
+ * following:
+ *
+ * - invalidates all pages protected by this lock (after sending dirty
+ * ones to the server, as necessary);
+ *
+ * - decref's underlying ldlm lock;
+ *
+ * - cancels ldlm lock (ldlm_cli_cancel()).
+ */
+static void osc_lock_cancel(const struct lu_env *env,
+ const struct cl_lock_slice *slice)
+{
+ struct cl_lock *lock = slice->cls_lock;
+ struct osc_lock *olck = cl2osc_lock(slice);
+ struct ldlm_lock *dlmlock = olck->ols_lock;
+ int result = 0;
+ int discard;
+
+ LASSERT(cl_lock_is_mutexed(lock));
+ LINVRNT(osc_lock_invariant(olck));
+
+ if (dlmlock != NULL) {
+ int do_cancel;
+
+ discard = !!(dlmlock->l_flags & LDLM_FL_DISCARD_DATA);
+ if (olck->ols_state >= OLS_GRANTED)
+ result = osc_lock_flush(olck, discard);
+ osc_lock_unhold(olck);
+
+ lock_res_and_lock(dlmlock);
+ /* Now that we're the only user of dlm read/write reference,
+ * mostly the ->l_readers + ->l_writers should be zero.
+ * However, there is a corner case.
+ * See bug 18829 for details.*/
+ do_cancel = (dlmlock->l_readers == 0 &&
+ dlmlock->l_writers == 0);
+ dlmlock->l_flags |= LDLM_FL_CBPENDING;
+ unlock_res_and_lock(dlmlock);
+ if (do_cancel)
+ result = ldlm_cli_cancel(&olck->ols_handle, LCF_ASYNC);
+ if (result < 0)
+ CL_LOCK_DEBUG(D_ERROR, env, lock,
+ "lock %p cancel failure with error(%d)\n",
+ lock, result);
+ }
+ olck->ols_state = OLS_CANCELLED;
+ olck->ols_flags &= ~LDLM_FL_LVB_READY;
+ osc_lock_detach(env, olck);
+}
+
+static int osc_lock_has_pages(struct osc_lock *olck)
+{
+ return 0;
+}
+
+static void osc_lock_delete(const struct lu_env *env,
+ const struct cl_lock_slice *slice)
+{
+ struct osc_lock *olck;
+
+ olck = cl2osc_lock(slice);
+ if (olck->ols_glimpse) {
+ LASSERT(!olck->ols_hold);
+ LASSERT(!olck->ols_lock);
+ return;
+ }
+
+ LINVRNT(osc_lock_invariant(olck));
+ LINVRNT(!osc_lock_has_pages(olck));
+
+ osc_lock_unhold(olck);
+ osc_lock_detach(env, olck);
+}
+
+/**
+ * Implements cl_lock_operations::clo_state() method for osc layer.
+ *
+ * Maintains osc_lock::ols_owner field.
+ *
+ * This assumes that lock always enters CLS_HELD (from some other state) in
+ * the same IO context as one that requested the lock. This should not be a
+ * problem, because context is by definition shared by all activity pertaining
+ * to the same high-level IO.
+ */
+static void osc_lock_state(const struct lu_env *env,
+ const struct cl_lock_slice *slice,
+ enum cl_lock_state state)
+{
+ struct osc_lock *lock = cl2osc_lock(slice);
+
+ /*
+ * XXX multiple io contexts can use the lock at the same time.
+ */
+ LINVRNT(osc_lock_invariant(lock));
+ if (state == CLS_HELD && slice->cls_lock->cll_state != CLS_HELD) {
+ struct osc_io *oio = osc_env_io(env);
+
+ LASSERT(lock->ols_owner == NULL);
+ lock->ols_owner = oio;
+ } else if (state != CLS_HELD)
+ lock->ols_owner = NULL;
+}
+
+static int osc_lock_print(const struct lu_env *env, void *cookie,
+ lu_printer_t p, const struct cl_lock_slice *slice)
+{
+ struct osc_lock *lock = cl2osc_lock(slice);
+
+ /*
+ * XXX print ldlm lock and einfo properly.
+ */
+ (*p)(env, cookie, "%p %#16llx %#llx %d %p ",
+ lock->ols_lock, lock->ols_flags, lock->ols_handle.cookie,
+ lock->ols_state, lock->ols_owner);
+ osc_lvb_print(env, cookie, p, &lock->ols_lvb);
+ return 0;
+}
+
+static int osc_lock_fits_into(const struct lu_env *env,
+ const struct cl_lock_slice *slice,
+ const struct cl_lock_descr *need,
+ const struct cl_io *io)
+{
+ struct osc_lock *ols = cl2osc_lock(slice);
+
+ if (need->cld_enq_flags & CEF_NEVER)
+ return 0;
+
+ if (ols->ols_state >= OLS_CANCELLED)
+ return 0;
+
+ if (need->cld_mode == CLM_PHANTOM) {
+ if (ols->ols_agl)
+ return !(ols->ols_state > OLS_RELEASED);
+
+ /*
+ * Note: the QUEUED lock can't be matched here, otherwise
+ * it might cause the deadlocks.
+ * In read_process,
+ * P1: enqueued read lock, create sublock1
+ * P2: enqueued write lock, create sublock2(conflicted
+ * with sublock1).
+ * P1: Grant read lock.
+ * P1: enqueued glimpse lock(with holding sublock1_read),
+ * matched with sublock2, waiting sublock2 to be granted.
+ * But sublock2 can not be granted, because P1
+ * will not release sublock1. Bang!
+ */
+ if (ols->ols_state < OLS_GRANTED ||
+ ols->ols_state > OLS_RELEASED)
+ return 0;
+ } else if (need->cld_enq_flags & CEF_MUST) {
+ /*
+ * If the lock hasn't ever enqueued, it can't be matched
+ * because enqueue process brings in many information
+ * which can be used to determine things such as lockless,
+ * CEF_MUST, etc.
+ */
+ if (ols->ols_state < OLS_UPCALL_RECEIVED &&
+ ols->ols_locklessable)
+ return 0;
+ }
+ return 1;
+}
+
+static const struct cl_lock_operations osc_lock_ops = {
+ .clo_fini = osc_lock_fini,
+ .clo_enqueue = osc_lock_enqueue,
+ .clo_wait = osc_lock_wait,
+ .clo_unuse = osc_lock_unuse,
+ .clo_use = osc_lock_use,
+ .clo_delete = osc_lock_delete,
+ .clo_state = osc_lock_state,
+ .clo_cancel = osc_lock_cancel,
+ .clo_weigh = osc_lock_weigh,
+ .clo_print = osc_lock_print,
+ .clo_fits_into = osc_lock_fits_into,
+};
+
+static int osc_lock_lockless_unuse(const struct lu_env *env,
+ const struct cl_lock_slice *slice)
+{
+ struct osc_lock *ols = cl2osc_lock(slice);
+ struct cl_lock *lock = slice->cls_lock;
+
+ LASSERT(ols->ols_state == OLS_GRANTED);
+ LINVRNT(osc_lock_invariant(ols));
+
+ cl_lock_cancel(env, lock);
+ cl_lock_delete(env, lock);
+ return 0;
+}
+
+static void osc_lock_lockless_cancel(const struct lu_env *env,
+ const struct cl_lock_slice *slice)
+{
+ struct osc_lock *ols = cl2osc_lock(slice);
+ int result;
+
+ result = osc_lock_flush(ols, 0);
+ if (result)
+ CERROR("Pages for lockless lock %p were not purged(%d)\n",
+ ols, result);
+ ols->ols_state = OLS_CANCELLED;
+}
+
+static int osc_lock_lockless_wait(const struct lu_env *env,
+ const struct cl_lock_slice *slice)
+{
+ struct osc_lock *olck = cl2osc_lock(slice);
+ struct cl_lock *lock = olck->ols_cl.cls_lock;
+
+ LINVRNT(osc_lock_invariant(olck));
+ LASSERT(olck->ols_state >= OLS_UPCALL_RECEIVED);
+
+ return lock->cll_error;
+}
+
+static void osc_lock_lockless_state(const struct lu_env *env,
+ const struct cl_lock_slice *slice,
+ enum cl_lock_state state)
+{
+ struct osc_lock *lock = cl2osc_lock(slice);
+
+ LINVRNT(osc_lock_invariant(lock));
+ if (state == CLS_HELD) {
+ struct osc_io *oio = osc_env_io(env);
+
+ LASSERT(ergo(lock->ols_owner, lock->ols_owner == oio));
+ lock->ols_owner = oio;
+
+ /* set the io to be lockless if this lock is for io's
+ * host object */
+ if (cl_object_same(oio->oi_cl.cis_obj, slice->cls_obj))
+ oio->oi_lockless = 1;
+ }
+}
+
+static int osc_lock_lockless_fits_into(const struct lu_env *env,
+ const struct cl_lock_slice *slice,
+ const struct cl_lock_descr *need,
+ const struct cl_io *io)
+{
+ struct osc_lock *lock = cl2osc_lock(slice);
+
+ if (!(need->cld_enq_flags & CEF_NEVER))
+ return 0;
+
+ /* lockless lock should only be used by its owning io. b22147 */
+ return (lock->ols_owner == osc_env_io(env));
+}
+
+static const struct cl_lock_operations osc_lock_lockless_ops = {
+ .clo_fini = osc_lock_fini,
+ .clo_enqueue = osc_lock_enqueue,
+ .clo_wait = osc_lock_lockless_wait,
+ .clo_unuse = osc_lock_lockless_unuse,
+ .clo_state = osc_lock_lockless_state,
+ .clo_fits_into = osc_lock_lockless_fits_into,
+ .clo_cancel = osc_lock_lockless_cancel,
+ .clo_print = osc_lock_print
+};
+
+int osc_lock_init(const struct lu_env *env,
+ struct cl_object *obj, struct cl_lock *lock,
+ const struct cl_io *unused)
+{
+ struct osc_lock *clk;
+ int result;
+
+ OBD_SLAB_ALLOC_PTR_GFP(clk, osc_lock_kmem, GFP_NOFS);
+ if (clk != NULL) {
+ __u32 enqflags = lock->cll_descr.cld_enq_flags;
+
+ osc_lock_build_einfo(env, lock, clk, &clk->ols_einfo);
+ atomic_set(&clk->ols_pageref, 0);
+ clk->ols_state = OLS_NEW;
+
+ clk->ols_flags = osc_enq2ldlm_flags(enqflags);
+ clk->ols_agl = !!(enqflags & CEF_AGL);
+ if (clk->ols_agl)
+ clk->ols_flags |= LDLM_FL_BLOCK_NOWAIT;
+ if (clk->ols_flags & LDLM_FL_HAS_INTENT)
+ clk->ols_glimpse = 1;
+
+ cl_lock_slice_add(lock, &clk->ols_cl, obj, &osc_lock_ops);
+
+ if (!(enqflags & CEF_MUST))
+ /* try to convert this lock to a lockless lock */
+ osc_lock_to_lockless(env, clk, (enqflags & CEF_NEVER));
+ if (clk->ols_locklessable && !(enqflags & CEF_DISCARD_DATA))
+ clk->ols_flags |= LDLM_FL_DENY_ON_CONTENTION;
+
+ LDLM_DEBUG_NOLOCK("lock %p, osc lock %p, flags %llx\n",
+ lock, clk, clk->ols_flags);
+
+ result = 0;
+ } else
+ result = -ENOMEM;
+ return result;
+}
+
+int osc_dlm_lock_pageref(struct ldlm_lock *dlm)
+{
+ struct osc_lock *olock;
+ int rc = 0;
+
+ spin_lock(&osc_ast_guard);
+ olock = dlm->l_ast_data;
+ /*
+ * there's a very rare race with osc_page_addref_lock(), but that
+ * doesn't matter because in the worst case we don't cancel a lock
+ * which we actually can, that's no harm.
+ */
+ if (olock != NULL &&
+ atomic_add_return(_PAGEREF_MAGIC,
+ &olock->ols_pageref) != _PAGEREF_MAGIC) {
+ atomic_sub(_PAGEREF_MAGIC, &olock->ols_pageref);
+ rc = 1;
+ }
+ spin_unlock(&osc_ast_guard);
+ return rc;
+}
+
+/** @} osc */
diff --git a/drivers/staging/lustre/lustre/osc/osc_object.c b/drivers/staging/lustre/lustre/osc/osc_object.c
new file mode 100644
index 000000000..92c202f70
--- /dev/null
+++ b/drivers/staging/lustre/lustre/osc/osc_object.c
@@ -0,0 +1,271 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2011, 2012, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ *
+ * Implementation of cl_object for OSC layer.
+ *
+ * Author: Nikita Danilov <nikita.danilov@sun.com>
+ */
+
+#define DEBUG_SUBSYSTEM S_OSC
+
+#include "osc_cl_internal.h"
+
+/** \addtogroup osc
+ * @{
+ */
+
+/*****************************************************************************
+ *
+ * Type conversions.
+ *
+ */
+
+static struct lu_object *osc2lu(struct osc_object *osc)
+{
+ return &osc->oo_cl.co_lu;
+}
+
+static struct osc_object *lu2osc(const struct lu_object *obj)
+{
+ LINVRNT(osc_is_object(obj));
+ return container_of0(obj, struct osc_object, oo_cl.co_lu);
+}
+
+/*****************************************************************************
+ *
+ * Object operations.
+ *
+ */
+
+static int osc_object_init(const struct lu_env *env, struct lu_object *obj,
+ const struct lu_object_conf *conf)
+{
+ struct osc_object *osc = lu2osc(obj);
+ const struct cl_object_conf *cconf = lu2cl_conf(conf);
+ int i;
+
+ osc->oo_oinfo = cconf->u.coc_oinfo;
+ spin_lock_init(&osc->oo_seatbelt);
+ for (i = 0; i < CRT_NR; ++i)
+ INIT_LIST_HEAD(&osc->oo_inflight[i]);
+
+ INIT_LIST_HEAD(&osc->oo_ready_item);
+ INIT_LIST_HEAD(&osc->oo_hp_ready_item);
+ INIT_LIST_HEAD(&osc->oo_write_item);
+ INIT_LIST_HEAD(&osc->oo_read_item);
+
+ osc->oo_root.rb_node = NULL;
+ INIT_LIST_HEAD(&osc->oo_hp_exts);
+ INIT_LIST_HEAD(&osc->oo_urgent_exts);
+ INIT_LIST_HEAD(&osc->oo_rpc_exts);
+ INIT_LIST_HEAD(&osc->oo_reading_exts);
+ atomic_set(&osc->oo_nr_reads, 0);
+ atomic_set(&osc->oo_nr_writes, 0);
+ spin_lock_init(&osc->oo_lock);
+
+ cl_object_page_init(lu2cl(obj), sizeof(struct osc_page));
+
+ return 0;
+}
+
+static void osc_object_free(const struct lu_env *env, struct lu_object *obj)
+{
+ struct osc_object *osc = lu2osc(obj);
+ int i;
+
+ for (i = 0; i < CRT_NR; ++i)
+ LASSERT(list_empty(&osc->oo_inflight[i]));
+
+ LASSERT(list_empty(&osc->oo_ready_item));
+ LASSERT(list_empty(&osc->oo_hp_ready_item));
+ LASSERT(list_empty(&osc->oo_write_item));
+ LASSERT(list_empty(&osc->oo_read_item));
+
+ LASSERT(osc->oo_root.rb_node == NULL);
+ LASSERT(list_empty(&osc->oo_hp_exts));
+ LASSERT(list_empty(&osc->oo_urgent_exts));
+ LASSERT(list_empty(&osc->oo_rpc_exts));
+ LASSERT(list_empty(&osc->oo_reading_exts));
+ LASSERT(atomic_read(&osc->oo_nr_reads) == 0);
+ LASSERT(atomic_read(&osc->oo_nr_writes) == 0);
+
+ lu_object_fini(obj);
+ OBD_SLAB_FREE_PTR(osc, osc_object_kmem);
+}
+
+int osc_lvb_print(const struct lu_env *env, void *cookie,
+ lu_printer_t p, const struct ost_lvb *lvb)
+{
+ return (*p)(env, cookie, "size: %llu mtime: %llu atime: %llu ctime: %llu blocks: %llu",
+ lvb->lvb_size, lvb->lvb_mtime, lvb->lvb_atime,
+ lvb->lvb_ctime, lvb->lvb_blocks);
+}
+
+static int osc_object_print(const struct lu_env *env, void *cookie,
+ lu_printer_t p, const struct lu_object *obj)
+{
+ struct osc_object *osc = lu2osc(obj);
+ struct lov_oinfo *oinfo = osc->oo_oinfo;
+ struct osc_async_rc *ar = &oinfo->loi_ar;
+
+ (*p)(env, cookie, "id: " DOSTID " idx: %d gen: %d kms_valid: %u kms %llu rc: %d force_sync: %d min_xid: %llu ",
+ POSTID(&oinfo->loi_oi), oinfo->loi_ost_idx,
+ oinfo->loi_ost_gen, oinfo->loi_kms_valid, oinfo->loi_kms,
+ ar->ar_rc, ar->ar_force_sync, ar->ar_min_xid);
+ osc_lvb_print(env, cookie, p, &oinfo->loi_lvb);
+ return 0;
+}
+
+
+static int osc_attr_get(const struct lu_env *env, struct cl_object *obj,
+ struct cl_attr *attr)
+{
+ struct lov_oinfo *oinfo = cl2osc(obj)->oo_oinfo;
+
+ cl_lvb2attr(attr, &oinfo->loi_lvb);
+ attr->cat_kms = oinfo->loi_kms_valid ? oinfo->loi_kms : 0;
+ return 0;
+}
+
+int osc_attr_set(const struct lu_env *env, struct cl_object *obj,
+ const struct cl_attr *attr, unsigned valid)
+{
+ struct lov_oinfo *oinfo = cl2osc(obj)->oo_oinfo;
+ struct ost_lvb *lvb = &oinfo->loi_lvb;
+
+ if (valid & CAT_SIZE)
+ lvb->lvb_size = attr->cat_size;
+ if (valid & CAT_MTIME)
+ lvb->lvb_mtime = attr->cat_mtime;
+ if (valid & CAT_ATIME)
+ lvb->lvb_atime = attr->cat_atime;
+ if (valid & CAT_CTIME)
+ lvb->lvb_ctime = attr->cat_ctime;
+ if (valid & CAT_BLOCKS)
+ lvb->lvb_blocks = attr->cat_blocks;
+ if (valid & CAT_KMS) {
+ CDEBUG(D_CACHE, "set kms from %llu to %llu\n",
+ oinfo->loi_kms, (__u64)attr->cat_kms);
+ loi_kms_set(oinfo, attr->cat_kms);
+ }
+ return 0;
+}
+
+static int osc_object_glimpse(const struct lu_env *env,
+ const struct cl_object *obj, struct ost_lvb *lvb)
+{
+ struct lov_oinfo *oinfo = cl2osc(obj)->oo_oinfo;
+
+ lvb->lvb_size = oinfo->loi_kms;
+ lvb->lvb_blocks = oinfo->loi_lvb.lvb_blocks;
+ return 0;
+}
+
+
+void osc_object_set_contended(struct osc_object *obj)
+{
+ obj->oo_contention_time = cfs_time_current();
+ /* mb(); */
+ obj->oo_contended = 1;
+}
+
+void osc_object_clear_contended(struct osc_object *obj)
+{
+ obj->oo_contended = 0;
+}
+
+int osc_object_is_contended(struct osc_object *obj)
+{
+ struct osc_device *dev = lu2osc_dev(obj->oo_cl.co_lu.lo_dev);
+ int osc_contention_time = dev->od_contention_time;
+ unsigned long cur_time = cfs_time_current();
+ unsigned long retry_time;
+
+ if (OBD_FAIL_CHECK(OBD_FAIL_OSC_OBJECT_CONTENTION))
+ return 1;
+
+ if (!obj->oo_contended)
+ return 0;
+
+ /*
+ * I like copy-paste. the code is copied from
+ * ll_file_is_contended.
+ */
+ retry_time = cfs_time_add(obj->oo_contention_time,
+ cfs_time_seconds(osc_contention_time));
+ if (cfs_time_after(cur_time, retry_time)) {
+ osc_object_clear_contended(obj);
+ return 0;
+ }
+ return 1;
+}
+
+static const struct cl_object_operations osc_ops = {
+ .coo_page_init = osc_page_init,
+ .coo_lock_init = osc_lock_init,
+ .coo_io_init = osc_io_init,
+ .coo_attr_get = osc_attr_get,
+ .coo_attr_set = osc_attr_set,
+ .coo_glimpse = osc_object_glimpse
+};
+
+static const struct lu_object_operations osc_lu_obj_ops = {
+ .loo_object_init = osc_object_init,
+ .loo_object_delete = NULL,
+ .loo_object_release = NULL,
+ .loo_object_free = osc_object_free,
+ .loo_object_print = osc_object_print,
+ .loo_object_invariant = NULL
+};
+
+struct lu_object *osc_object_alloc(const struct lu_env *env,
+ const struct lu_object_header *unused,
+ struct lu_device *dev)
+{
+ struct osc_object *osc;
+ struct lu_object *obj;
+
+ OBD_SLAB_ALLOC_PTR_GFP(osc, osc_object_kmem, GFP_NOFS);
+ if (osc != NULL) {
+ obj = osc2lu(osc);
+ lu_object_init(obj, NULL, dev);
+ osc->oo_cl.co_ops = &osc_ops;
+ obj->lo_ops = &osc_lu_obj_ops;
+ } else
+ obj = NULL;
+ return obj;
+}
+
+/** @} osc */
diff --git a/drivers/staging/lustre/lustre/osc/osc_page.c b/drivers/staging/lustre/lustre/osc/osc_page.c
new file mode 100644
index 000000000..76ba58b09
--- /dev/null
+++ b/drivers/staging/lustre/lustre/osc/osc_page.c
@@ -0,0 +1,916 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2011, 2012, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ *
+ * Implementation of cl_page for OSC layer.
+ *
+ * Author: Nikita Danilov <nikita.danilov@sun.com>
+ */
+
+#define DEBUG_SUBSYSTEM S_OSC
+
+#include "osc_cl_internal.h"
+
+static void osc_lru_del(struct client_obd *cli, struct osc_page *opg, bool del);
+static void osc_lru_add(struct client_obd *cli, struct osc_page *opg);
+static int osc_lru_reserve(const struct lu_env *env, struct osc_object *obj,
+ struct osc_page *opg);
+
+/** \addtogroup osc
+ * @{
+ */
+
+/*
+ * Comment out osc_page_protected because it may sleep inside the
+ * the client_obd_list_lock.
+ * client_obd_list_lock -> osc_ap_completion -> osc_completion ->
+ * -> osc_page_protected -> osc_page_is_dlocked -> osc_match_base
+ * -> ldlm_lock_match -> sptlrpc_import_check_ctx -> sleep.
+ */
+#if 0
+static int osc_page_is_dlocked(const struct lu_env *env,
+ const struct osc_page *opg,
+ enum cl_lock_mode mode, int pending, int unref)
+{
+ struct cl_page *page;
+ struct osc_object *obj;
+ struct osc_thread_info *info;
+ struct ldlm_res_id *resname;
+ struct lustre_handle *lockh;
+ ldlm_policy_data_t *policy;
+ ldlm_mode_t dlmmode;
+ __u64 flags;
+
+ might_sleep();
+
+ info = osc_env_info(env);
+ resname = &info->oti_resname;
+ policy = &info->oti_policy;
+ lockh = &info->oti_handle;
+ page = opg->ops_cl.cpl_page;
+ obj = cl2osc(opg->ops_cl.cpl_obj);
+
+ flags = LDLM_FL_TEST_LOCK | LDLM_FL_BLOCK_GRANTED;
+ if (pending)
+ flags |= LDLM_FL_CBPENDING;
+
+ dlmmode = osc_cl_lock2ldlm(mode) | LCK_PW;
+ osc_lock_build_res(env, obj, resname);
+ osc_index2policy(policy, page->cp_obj, page->cp_index, page->cp_index);
+ return osc_match_base(osc_export(obj), resname, LDLM_EXTENT, policy,
+ dlmmode, &flags, NULL, lockh, unref);
+}
+
+/**
+ * Checks an invariant that a page in the cache is covered by a lock, as
+ * needed.
+ */
+static int osc_page_protected(const struct lu_env *env,
+ const struct osc_page *opg,
+ enum cl_lock_mode mode, int unref)
+{
+ struct cl_object_header *hdr;
+ struct cl_lock *scan;
+ struct cl_page *page;
+ struct cl_lock_descr *descr;
+ int result;
+
+ LINVRNT(!opg->ops_temp);
+
+ page = opg->ops_cl.cpl_page;
+ if (page->cp_owner != NULL &&
+ cl_io_top(page->cp_owner)->ci_lockreq == CILR_NEVER)
+ /*
+ * If IO is done without locks (liblustre, or lloop), lock is
+ * not required.
+ */
+ result = 1;
+ else
+ /* otherwise check for a DLM lock */
+ result = osc_page_is_dlocked(env, opg, mode, 1, unref);
+ if (result == 0) {
+ /* maybe this page is a part of a lockless io? */
+ hdr = cl_object_header(opg->ops_cl.cpl_obj);
+ descr = &osc_env_info(env)->oti_descr;
+ descr->cld_mode = mode;
+ descr->cld_start = page->cp_index;
+ descr->cld_end = page->cp_index;
+ spin_lock(&hdr->coh_lock_guard);
+ list_for_each_entry(scan, &hdr->coh_locks, cll_linkage) {
+ /*
+ * Lock-less sub-lock has to be either in HELD state
+ * (when io is actively going on), or in CACHED state,
+ * when top-lock is being unlocked:
+ * cl_io_unlock()->cl_unuse()->...->lov_lock_unuse().
+ */
+ if ((scan->cll_state == CLS_HELD ||
+ scan->cll_state == CLS_CACHED) &&
+ cl_lock_ext_match(&scan->cll_descr, descr)) {
+ struct osc_lock *olck;
+
+ olck = osc_lock_at(scan);
+ result = osc_lock_is_lockless(olck);
+ break;
+ }
+ }
+ spin_unlock(&hdr->coh_lock_guard);
+ }
+ return result;
+}
+#else
+static int osc_page_protected(const struct lu_env *env,
+ const struct osc_page *opg,
+ enum cl_lock_mode mode, int unref)
+{
+ return 1;
+}
+#endif
+
+/*****************************************************************************
+ *
+ * Page operations.
+ *
+ */
+static void osc_page_fini(const struct lu_env *env,
+ struct cl_page_slice *slice)
+{
+ struct osc_page *opg = cl2osc_page(slice);
+ CDEBUG(D_TRACE, "%p\n", opg);
+ LASSERT(opg->ops_lock == NULL);
+}
+
+static void osc_page_transfer_get(struct osc_page *opg, const char *label)
+{
+ struct cl_page *page = cl_page_top(opg->ops_cl.cpl_page);
+
+ LASSERT(!opg->ops_transfer_pinned);
+ cl_page_get(page);
+ lu_ref_add_atomic(&page->cp_reference, label, page);
+ opg->ops_transfer_pinned = 1;
+}
+
+static void osc_page_transfer_put(const struct lu_env *env,
+ struct osc_page *opg)
+{
+ struct cl_page *page = cl_page_top(opg->ops_cl.cpl_page);
+
+ if (opg->ops_transfer_pinned) {
+ lu_ref_del(&page->cp_reference, "transfer", page);
+ opg->ops_transfer_pinned = 0;
+ cl_page_put(env, page);
+ }
+}
+
+/**
+ * This is called once for every page when it is submitted for a transfer
+ * either opportunistic (osc_page_cache_add()), or immediate
+ * (osc_page_submit()).
+ */
+static void osc_page_transfer_add(const struct lu_env *env,
+ struct osc_page *opg, enum cl_req_type crt)
+{
+ struct osc_object *obj = cl2osc(opg->ops_cl.cpl_obj);
+
+ /* ops_lru and ops_inflight share the same field, so take it from LRU
+ * first and then use it as inflight. */
+ osc_lru_del(osc_cli(obj), opg, false);
+
+ spin_lock(&obj->oo_seatbelt);
+ list_add(&opg->ops_inflight, &obj->oo_inflight[crt]);
+ opg->ops_submitter = current;
+ spin_unlock(&obj->oo_seatbelt);
+}
+
+static int osc_page_cache_add(const struct lu_env *env,
+ const struct cl_page_slice *slice,
+ struct cl_io *io)
+{
+ struct osc_io *oio = osc_env_io(env);
+ struct osc_page *opg = cl2osc_page(slice);
+ int result;
+
+ LINVRNT(osc_page_protected(env, opg, CLM_WRITE, 0));
+
+ osc_page_transfer_get(opg, "transfer\0cache");
+ result = osc_queue_async_io(env, io, opg);
+ if (result != 0)
+ osc_page_transfer_put(env, opg);
+ else
+ osc_page_transfer_add(env, opg, CRT_WRITE);
+
+ /* for sync write, kernel will wait for this page to be flushed before
+ * osc_io_end() is called, so release it earlier.
+ * for mkwrite(), it's known there is no further pages. */
+ if (cl_io_is_sync_write(io) || cl_io_is_mkwrite(io)) {
+ if (oio->oi_active != NULL) {
+ osc_extent_release(env, oio->oi_active);
+ oio->oi_active = NULL;
+ }
+ }
+
+ return result;
+}
+
+void osc_index2policy(ldlm_policy_data_t *policy, const struct cl_object *obj,
+ pgoff_t start, pgoff_t end)
+{
+ memset(policy, 0, sizeof(*policy));
+ policy->l_extent.start = cl_offset(obj, start);
+ policy->l_extent.end = cl_offset(obj, end + 1) - 1;
+}
+
+static int osc_page_addref_lock(const struct lu_env *env,
+ struct osc_page *opg,
+ struct cl_lock *lock)
+{
+ struct osc_lock *olock;
+ int rc;
+
+ LASSERT(opg->ops_lock == NULL);
+
+ olock = osc_lock_at(lock);
+ if (atomic_inc_return(&olock->ols_pageref) <= 0) {
+ atomic_dec(&olock->ols_pageref);
+ rc = -ENODATA;
+ } else {
+ cl_lock_get(lock);
+ opg->ops_lock = lock;
+ rc = 0;
+ }
+ return rc;
+}
+
+static void osc_page_putref_lock(const struct lu_env *env,
+ struct osc_page *opg)
+{
+ struct cl_lock *lock = opg->ops_lock;
+ struct osc_lock *olock;
+
+ LASSERT(lock != NULL);
+ olock = osc_lock_at(lock);
+
+ atomic_dec(&olock->ols_pageref);
+ opg->ops_lock = NULL;
+
+ cl_lock_put(env, lock);
+}
+
+static int osc_page_is_under_lock(const struct lu_env *env,
+ const struct cl_page_slice *slice,
+ struct cl_io *unused)
+{
+ struct cl_lock *lock;
+ int result = -ENODATA;
+
+ lock = cl_lock_at_page(env, slice->cpl_obj, slice->cpl_page,
+ NULL, 1, 0);
+ if (lock != NULL) {
+ if (osc_page_addref_lock(env, cl2osc_page(slice), lock) == 0)
+ result = -EBUSY;
+ cl_lock_put(env, lock);
+ }
+ return result;
+}
+
+static void osc_page_disown(const struct lu_env *env,
+ const struct cl_page_slice *slice,
+ struct cl_io *io)
+{
+ struct osc_page *opg = cl2osc_page(slice);
+
+ if (unlikely(opg->ops_lock))
+ osc_page_putref_lock(env, opg);
+}
+
+static void osc_page_completion_read(const struct lu_env *env,
+ const struct cl_page_slice *slice,
+ int ioret)
+{
+ struct osc_page *opg = cl2osc_page(slice);
+ struct osc_object *obj = cl2osc(opg->ops_cl.cpl_obj);
+
+ if (likely(opg->ops_lock))
+ osc_page_putref_lock(env, opg);
+ osc_lru_add(osc_cli(obj), opg);
+}
+
+static void osc_page_completion_write(const struct lu_env *env,
+ const struct cl_page_slice *slice,
+ int ioret)
+{
+ struct osc_page *opg = cl2osc_page(slice);
+ struct osc_object *obj = cl2osc(slice->cpl_obj);
+
+ osc_lru_add(osc_cli(obj), opg);
+}
+
+static int osc_page_fail(const struct lu_env *env,
+ const struct cl_page_slice *slice,
+ struct cl_io *unused)
+{
+ /*
+ * Cached read?
+ */
+ LBUG();
+ return 0;
+}
+
+
+static const char *osc_list(struct list_head *head)
+{
+ return list_empty(head) ? "-" : "+";
+}
+
+static inline unsigned long osc_submit_duration(struct osc_page *opg)
+{
+ if (opg->ops_submit_time == 0)
+ return 0;
+
+ return (cfs_time_current() - opg->ops_submit_time);
+}
+
+static int osc_page_print(const struct lu_env *env,
+ const struct cl_page_slice *slice,
+ void *cookie, lu_printer_t printer)
+{
+ struct osc_page *opg = cl2osc_page(slice);
+ struct osc_async_page *oap = &opg->ops_oap;
+ struct osc_object *obj = cl2osc(slice->cpl_obj);
+ struct client_obd *cli = &osc_export(obj)->exp_obd->u.cli;
+
+ return (*printer)(env, cookie, LUSTRE_OSC_NAME "-page@%p: 1< %#x %d %u %s %s > 2< %llu %u %u %#x %#x | %p %p %p > 3< %s %p %d %lu %d > 4< %d %d %d %lu %s | %s %s %s %s > 5< %s %s %s %s | %d %s | %d %s %s>\n",
+ opg,
+ /* 1 */
+ oap->oap_magic, oap->oap_cmd,
+ oap->oap_interrupted,
+ osc_list(&oap->oap_pending_item),
+ osc_list(&oap->oap_rpc_item),
+ /* 2 */
+ oap->oap_obj_off, oap->oap_page_off, oap->oap_count,
+ oap->oap_async_flags, oap->oap_brw_flags,
+ oap->oap_request, oap->oap_cli, obj,
+ /* 3 */
+ osc_list(&opg->ops_inflight),
+ opg->ops_submitter, opg->ops_transfer_pinned,
+ osc_submit_duration(opg), opg->ops_srvlock,
+ /* 4 */
+ cli->cl_r_in_flight, cli->cl_w_in_flight,
+ cli->cl_max_rpcs_in_flight,
+ cli->cl_avail_grant,
+ osc_list(&cli->cl_cache_waiters),
+ osc_list(&cli->cl_loi_ready_list),
+ osc_list(&cli->cl_loi_hp_ready_list),
+ osc_list(&cli->cl_loi_write_list),
+ osc_list(&cli->cl_loi_read_list),
+ /* 5 */
+ osc_list(&obj->oo_ready_item),
+ osc_list(&obj->oo_hp_ready_item),
+ osc_list(&obj->oo_write_item),
+ osc_list(&obj->oo_read_item),
+ atomic_read(&obj->oo_nr_reads),
+ osc_list(&obj->oo_reading_exts),
+ atomic_read(&obj->oo_nr_writes),
+ osc_list(&obj->oo_hp_exts),
+ osc_list(&obj->oo_urgent_exts));
+}
+
+static void osc_page_delete(const struct lu_env *env,
+ const struct cl_page_slice *slice)
+{
+ struct osc_page *opg = cl2osc_page(slice);
+ struct osc_object *obj = cl2osc(opg->ops_cl.cpl_obj);
+ int rc;
+
+ LINVRNT(opg->ops_temp || osc_page_protected(env, opg, CLM_READ, 1));
+
+ CDEBUG(D_TRACE, "%p\n", opg);
+ osc_page_transfer_put(env, opg);
+ rc = osc_teardown_async_page(env, obj, opg);
+ if (rc) {
+ CL_PAGE_DEBUG(D_ERROR, env, cl_page_top(slice->cpl_page),
+ "Trying to teardown failed: %d\n", rc);
+ LASSERT(0);
+ }
+
+ spin_lock(&obj->oo_seatbelt);
+ if (opg->ops_submitter != NULL) {
+ LASSERT(!list_empty(&opg->ops_inflight));
+ list_del_init(&opg->ops_inflight);
+ opg->ops_submitter = NULL;
+ }
+ spin_unlock(&obj->oo_seatbelt);
+
+ osc_lru_del(osc_cli(obj), opg, true);
+}
+
+void osc_page_clip(const struct lu_env *env, const struct cl_page_slice *slice,
+ int from, int to)
+{
+ struct osc_page *opg = cl2osc_page(slice);
+ struct osc_async_page *oap = &opg->ops_oap;
+
+ LINVRNT(osc_page_protected(env, opg, CLM_READ, 0));
+
+ opg->ops_from = from;
+ opg->ops_to = to;
+ spin_lock(&oap->oap_lock);
+ oap->oap_async_flags |= ASYNC_COUNT_STABLE;
+ spin_unlock(&oap->oap_lock);
+}
+
+static int osc_page_cancel(const struct lu_env *env,
+ const struct cl_page_slice *slice)
+{
+ struct osc_page *opg = cl2osc_page(slice);
+ int rc = 0;
+
+ LINVRNT(osc_page_protected(env, opg, CLM_READ, 0));
+
+ /* Check if the transferring against this page
+ * is completed, or not even queued. */
+ if (opg->ops_transfer_pinned)
+ /* FIXME: may not be interrupted.. */
+ rc = osc_cancel_async_page(env, opg);
+ LASSERT(ergo(rc == 0, opg->ops_transfer_pinned == 0));
+ return rc;
+}
+
+static int osc_page_flush(const struct lu_env *env,
+ const struct cl_page_slice *slice,
+ struct cl_io *io)
+{
+ struct osc_page *opg = cl2osc_page(slice);
+ int rc = 0;
+
+ rc = osc_flush_async_page(env, io, opg);
+ return rc;
+}
+
+static const struct cl_page_operations osc_page_ops = {
+ .cpo_fini = osc_page_fini,
+ .cpo_print = osc_page_print,
+ .cpo_delete = osc_page_delete,
+ .cpo_is_under_lock = osc_page_is_under_lock,
+ .cpo_disown = osc_page_disown,
+ .io = {
+ [CRT_READ] = {
+ .cpo_cache_add = osc_page_fail,
+ .cpo_completion = osc_page_completion_read
+ },
+ [CRT_WRITE] = {
+ .cpo_cache_add = osc_page_cache_add,
+ .cpo_completion = osc_page_completion_write
+ }
+ },
+ .cpo_clip = osc_page_clip,
+ .cpo_cancel = osc_page_cancel,
+ .cpo_flush = osc_page_flush
+};
+
+int osc_page_init(const struct lu_env *env, struct cl_object *obj,
+ struct cl_page *page, struct page *vmpage)
+{
+ struct osc_object *osc = cl2osc(obj);
+ struct osc_page *opg = cl_object_page_slice(obj, page);
+ int result;
+
+ opg->ops_from = 0;
+ opg->ops_to = PAGE_CACHE_SIZE;
+
+ result = osc_prep_async_page(osc, opg, vmpage,
+ cl_offset(obj, page->cp_index));
+ if (result == 0) {
+ struct osc_io *oio = osc_env_io(env);
+ opg->ops_srvlock = osc_io_srvlock(oio);
+ cl_page_slice_add(page, &opg->ops_cl, obj,
+ &osc_page_ops);
+ }
+ /*
+ * Cannot assert osc_page_protected() here as read-ahead
+ * creates temporary pages outside of a lock.
+ */
+ /* ops_inflight and ops_lru are the same field, but it doesn't
+ * hurt to initialize it twice :-) */
+ INIT_LIST_HEAD(&opg->ops_inflight);
+ INIT_LIST_HEAD(&opg->ops_lru);
+
+ /* reserve an LRU space for this page */
+ if (page->cp_type == CPT_CACHEABLE && result == 0)
+ result = osc_lru_reserve(env, osc, opg);
+
+ return result;
+}
+
+/**
+ * Helper function called by osc_io_submit() for every page in an immediate
+ * transfer (i.e., transferred synchronously).
+ */
+void osc_page_submit(const struct lu_env *env, struct osc_page *opg,
+ enum cl_req_type crt, int brw_flags)
+{
+ struct osc_async_page *oap = &opg->ops_oap;
+ struct osc_object *obj = oap->oap_obj;
+
+ LINVRNT(osc_page_protected(env, opg,
+ crt == CRT_WRITE ? CLM_WRITE : CLM_READ, 1));
+
+ LASSERTF(oap->oap_magic == OAP_MAGIC, "Bad oap magic: oap %p, magic 0x%x\n",
+ oap, oap->oap_magic);
+ LASSERT(oap->oap_async_flags & ASYNC_READY);
+ LASSERT(oap->oap_async_flags & ASYNC_COUNT_STABLE);
+
+ oap->oap_cmd = crt == CRT_WRITE ? OBD_BRW_WRITE : OBD_BRW_READ;
+ oap->oap_page_off = opg->ops_from;
+ oap->oap_count = opg->ops_to - opg->ops_from;
+ oap->oap_brw_flags = OBD_BRW_SYNC | brw_flags;
+
+ if (!client_is_remote(osc_export(obj)) &&
+ capable(CFS_CAP_SYS_RESOURCE)) {
+ oap->oap_brw_flags |= OBD_BRW_NOQUOTA;
+ oap->oap_cmd |= OBD_BRW_NOQUOTA;
+ }
+
+ opg->ops_submit_time = cfs_time_current();
+ osc_page_transfer_get(opg, "transfer\0imm");
+ osc_page_transfer_add(env, opg, crt);
+}
+
+/* --------------- LRU page management ------------------ */
+
+/* OSC is a natural place to manage LRU pages as applications are specialized
+ * to write OSC by OSC. Ideally, if one OSC is used more frequently it should
+ * occupy more LRU slots. On the other hand, we should avoid using up all LRU
+ * slots (client_obd::cl_lru_left) otherwise process has to be put into sleep
+ * for free LRU slots - this will be very bad so the algorithm requires each
+ * OSC to free slots voluntarily to maintain a reasonable number of free slots
+ * at any time.
+ */
+
+static DECLARE_WAIT_QUEUE_HEAD(osc_lru_waitq);
+static atomic_t osc_lru_waiters = ATOMIC_INIT(0);
+/* LRU pages are freed in batch mode. OSC should at least free this
+ * number of pages to avoid running out of LRU budget, and.. */
+static const int lru_shrink_min = 2 << (20 - PAGE_CACHE_SHIFT); /* 2M */
+/* free this number at most otherwise it will take too long time to finish. */
+static const int lru_shrink_max = 32 << (20 - PAGE_CACHE_SHIFT); /* 32M */
+
+/* Check if we can free LRU slots from this OSC. If there exists LRU waiters,
+ * we should free slots aggressively. In this way, slots are freed in a steady
+ * step to maintain fairness among OSCs.
+ *
+ * Return how many LRU pages should be freed. */
+static int osc_cache_too_much(struct client_obd *cli)
+{
+ struct cl_client_cache *cache = cli->cl_cache;
+ int pages = atomic_read(&cli->cl_lru_in_list) >> 1;
+
+ if (atomic_read(&osc_lru_waiters) > 0 &&
+ atomic_read(cli->cl_lru_left) < lru_shrink_max)
+ /* drop lru pages aggressively */
+ return min(pages, lru_shrink_max);
+
+ /* if it's going to run out LRU slots, we should free some, but not
+ * too much to maintain fairness among OSCs. */
+ if (atomic_read(cli->cl_lru_left) < cache->ccc_lru_max >> 4) {
+ unsigned long tmp;
+
+ tmp = cache->ccc_lru_max / atomic_read(&cache->ccc_users);
+ if (pages > tmp)
+ return min(pages, lru_shrink_max);
+
+ return pages > lru_shrink_min ? lru_shrink_min : 0;
+ }
+
+ return 0;
+}
+
+/* Return how many pages are not discarded in @pvec. */
+static int discard_pagevec(const struct lu_env *env, struct cl_io *io,
+ struct cl_page **pvec, int max_index)
+{
+ int count;
+ int i;
+
+ for (count = 0, i = 0; i < max_index; i++) {
+ struct cl_page *page = pvec[i];
+ if (cl_page_own_try(env, io, page) == 0) {
+ /* free LRU page only if nobody is using it.
+ * This check is necessary to avoid freeing the pages
+ * having already been removed from LRU and pinned
+ * for IO. */
+ if (!cl_page_in_use(page)) {
+ cl_page_unmap(env, io, page);
+ cl_page_discard(env, io, page);
+ ++count;
+ }
+ cl_page_disown(env, io, page);
+ }
+ cl_page_put(env, page);
+ pvec[i] = NULL;
+ }
+ return max_index - count;
+}
+
+/**
+ * Drop @target of pages from LRU at most.
+ */
+int osc_lru_shrink(struct client_obd *cli, int target)
+{
+ struct cl_env_nest nest;
+ struct lu_env *env;
+ struct cl_io *io;
+ struct cl_object *clobj = NULL;
+ struct cl_page **pvec;
+ struct osc_page *opg;
+ int maxscan = 0;
+ int count = 0;
+ int index = 0;
+ int rc = 0;
+
+ LASSERT(atomic_read(&cli->cl_lru_in_list) >= 0);
+ if (atomic_read(&cli->cl_lru_in_list) == 0 || target <= 0)
+ return 0;
+
+ env = cl_env_nested_get(&nest);
+ if (IS_ERR(env))
+ return PTR_ERR(env);
+
+ pvec = osc_env_info(env)->oti_pvec;
+ io = &osc_env_info(env)->oti_io;
+
+ client_obd_list_lock(&cli->cl_lru_list_lock);
+ atomic_inc(&cli->cl_lru_shrinkers);
+ maxscan = min(target << 1, atomic_read(&cli->cl_lru_in_list));
+ while (!list_empty(&cli->cl_lru_list)) {
+ struct cl_page *page;
+
+ if (--maxscan < 0)
+ break;
+
+ opg = list_entry(cli->cl_lru_list.next, struct osc_page,
+ ops_lru);
+ page = cl_page_top(opg->ops_cl.cpl_page);
+ if (cl_page_in_use_noref(page)) {
+ list_move_tail(&opg->ops_lru, &cli->cl_lru_list);
+ continue;
+ }
+
+ LASSERT(page->cp_obj != NULL);
+ if (clobj != page->cp_obj) {
+ struct cl_object *tmp = page->cp_obj;
+
+ cl_object_get(tmp);
+ client_obd_list_unlock(&cli->cl_lru_list_lock);
+
+ if (clobj != NULL) {
+ count -= discard_pagevec(env, io, pvec, index);
+ index = 0;
+
+ cl_io_fini(env, io);
+ cl_object_put(env, clobj);
+ clobj = NULL;
+ }
+
+ clobj = tmp;
+ io->ci_obj = clobj;
+ io->ci_ignore_layout = 1;
+ rc = cl_io_init(env, io, CIT_MISC, clobj);
+
+ client_obd_list_lock(&cli->cl_lru_list_lock);
+
+ if (rc != 0)
+ break;
+
+ ++maxscan;
+ continue;
+ }
+
+ /* move this page to the end of list as it will be discarded
+ * soon. The page will be finally removed from LRU list in
+ * osc_page_delete(). */
+ list_move_tail(&opg->ops_lru, &cli->cl_lru_list);
+
+ /* it's okay to grab a refcount here w/o holding lock because
+ * it has to grab cl_lru_list_lock to delete the page. */
+ cl_page_get(page);
+ pvec[index++] = page;
+ if (++count >= target)
+ break;
+
+ if (unlikely(index == OTI_PVEC_SIZE)) {
+ client_obd_list_unlock(&cli->cl_lru_list_lock);
+ count -= discard_pagevec(env, io, pvec, index);
+ index = 0;
+
+ client_obd_list_lock(&cli->cl_lru_list_lock);
+ }
+ }
+ client_obd_list_unlock(&cli->cl_lru_list_lock);
+
+ if (clobj != NULL) {
+ count -= discard_pagevec(env, io, pvec, index);
+
+ cl_io_fini(env, io);
+ cl_object_put(env, clobj);
+ }
+ cl_env_nested_put(&nest, env);
+
+ atomic_dec(&cli->cl_lru_shrinkers);
+ return count > 0 ? count : rc;
+}
+
+static void osc_lru_add(struct client_obd *cli, struct osc_page *opg)
+{
+ bool wakeup = false;
+
+ if (!opg->ops_in_lru)
+ return;
+
+ atomic_dec(&cli->cl_lru_busy);
+ client_obd_list_lock(&cli->cl_lru_list_lock);
+ if (list_empty(&opg->ops_lru)) {
+ list_move_tail(&opg->ops_lru, &cli->cl_lru_list);
+ atomic_inc_return(&cli->cl_lru_in_list);
+ wakeup = atomic_read(&osc_lru_waiters) > 0;
+ }
+ client_obd_list_unlock(&cli->cl_lru_list_lock);
+
+ if (wakeup) {
+ osc_lru_shrink(cli, osc_cache_too_much(cli));
+ wake_up_all(&osc_lru_waitq);
+ }
+}
+
+/* delete page from LRUlist. The page can be deleted from LRUlist for two
+ * reasons: redirtied or deleted from page cache. */
+static void osc_lru_del(struct client_obd *cli, struct osc_page *opg, bool del)
+{
+ if (opg->ops_in_lru) {
+ client_obd_list_lock(&cli->cl_lru_list_lock);
+ if (!list_empty(&opg->ops_lru)) {
+ LASSERT(atomic_read(&cli->cl_lru_in_list) > 0);
+ list_del_init(&opg->ops_lru);
+ atomic_dec(&cli->cl_lru_in_list);
+ if (!del)
+ atomic_inc(&cli->cl_lru_busy);
+ } else if (del) {
+ LASSERT(atomic_read(&cli->cl_lru_busy) > 0);
+ atomic_dec(&cli->cl_lru_busy);
+ }
+ client_obd_list_unlock(&cli->cl_lru_list_lock);
+ if (del) {
+ atomic_inc(cli->cl_lru_left);
+ /* this is a great place to release more LRU pages if
+ * this osc occupies too many LRU pages and kernel is
+ * stealing one of them.
+ * cl_lru_shrinkers is to avoid recursive call in case
+ * we're already in the context of osc_lru_shrink(). */
+ if (atomic_read(&cli->cl_lru_shrinkers) == 0 &&
+ !memory_pressure_get())
+ osc_lru_shrink(cli, osc_cache_too_much(cli));
+ wake_up(&osc_lru_waitq);
+ }
+ } else {
+ LASSERT(list_empty(&opg->ops_lru));
+ }
+}
+
+static inline int max_to_shrink(struct client_obd *cli)
+{
+ return min(atomic_read(&cli->cl_lru_in_list) >> 1, lru_shrink_max);
+}
+
+static int osc_lru_reclaim(struct client_obd *cli)
+{
+ struct cl_client_cache *cache = cli->cl_cache;
+ int max_scans;
+ int rc;
+
+ LASSERT(cache != NULL);
+ LASSERT(!list_empty(&cache->ccc_lru));
+
+ rc = osc_lru_shrink(cli, lru_shrink_min);
+ if (rc != 0) {
+ CDEBUG(D_CACHE, "%s: Free %d pages from own LRU: %p.\n",
+ cli->cl_import->imp_obd->obd_name, rc, cli);
+ return rc;
+ }
+
+ CDEBUG(D_CACHE, "%s: cli %p no free slots, pages: %d, busy: %d.\n",
+ cli->cl_import->imp_obd->obd_name, cli,
+ atomic_read(&cli->cl_lru_in_list),
+ atomic_read(&cli->cl_lru_busy));
+
+ /* Reclaim LRU slots from other client_obd as it can't free enough
+ * from its own. This should rarely happen. */
+ spin_lock(&cache->ccc_lru_lock);
+ cache->ccc_lru_shrinkers++;
+ list_move_tail(&cli->cl_lru_osc, &cache->ccc_lru);
+
+ max_scans = atomic_read(&cache->ccc_users);
+ while (--max_scans > 0 && !list_empty(&cache->ccc_lru)) {
+ cli = list_entry(cache->ccc_lru.next, struct client_obd,
+ cl_lru_osc);
+
+ CDEBUG(D_CACHE, "%s: cli %p LRU pages: %d, busy: %d.\n",
+ cli->cl_import->imp_obd->obd_name, cli,
+ atomic_read(&cli->cl_lru_in_list),
+ atomic_read(&cli->cl_lru_busy));
+
+ list_move_tail(&cli->cl_lru_osc, &cache->ccc_lru);
+ if (atomic_read(&cli->cl_lru_in_list) > 0) {
+ spin_unlock(&cache->ccc_lru_lock);
+
+ rc = osc_lru_shrink(cli, max_to_shrink(cli));
+ spin_lock(&cache->ccc_lru_lock);
+ if (rc != 0)
+ break;
+ }
+ }
+ spin_unlock(&cache->ccc_lru_lock);
+
+ CDEBUG(D_CACHE, "%s: cli %p freed %d pages.\n",
+ cli->cl_import->imp_obd->obd_name, cli, rc);
+ return rc;
+}
+
+static int osc_lru_reserve(const struct lu_env *env, struct osc_object *obj,
+ struct osc_page *opg)
+{
+ struct l_wait_info lwi = LWI_INTR(LWI_ON_SIGNAL_NOOP, NULL);
+ struct client_obd *cli = osc_cli(obj);
+ int rc = 0;
+
+ if (cli->cl_cache == NULL) /* shall not be in LRU */
+ return 0;
+
+ LASSERT(atomic_read(cli->cl_lru_left) >= 0);
+ while (!atomic_add_unless(cli->cl_lru_left, -1, 0)) {
+ int gen;
+
+ /* run out of LRU spaces, try to drop some by itself */
+ rc = osc_lru_reclaim(cli);
+ if (rc < 0)
+ break;
+ if (rc > 0)
+ continue;
+
+ cond_resched();
+
+ /* slowest case, all of caching pages are busy, notifying
+ * other OSCs that we're lack of LRU slots. */
+ atomic_inc(&osc_lru_waiters);
+
+ gen = atomic_read(&cli->cl_lru_in_list);
+ rc = l_wait_event(osc_lru_waitq,
+ atomic_read(cli->cl_lru_left) > 0 ||
+ (atomic_read(&cli->cl_lru_in_list) > 0 &&
+ gen != atomic_read(&cli->cl_lru_in_list)),
+ &lwi);
+
+ atomic_dec(&osc_lru_waiters);
+ if (rc < 0)
+ break;
+ }
+
+ if (rc >= 0) {
+ atomic_inc(&cli->cl_lru_busy);
+ opg->ops_in_lru = 1;
+ rc = 0;
+ }
+
+ return rc;
+}
+
+/** @} osc */
diff --git a/drivers/staging/lustre/lustre/osc/osc_quota.c b/drivers/staging/lustre/lustre/osc/osc_quota.c
new file mode 100644
index 000000000..6690f149a
--- /dev/null
+++ b/drivers/staging/lustre/lustre/osc/osc_quota.c
@@ -0,0 +1,327 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 021110-1307, USA
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
+ *
+ * Copyright (c) 2011, 2012, Intel Corporation.
+ *
+ * Code originally extracted from quota directory
+ */
+
+#include "../include/obd_class.h"
+#include "osc_internal.h"
+
+static inline struct osc_quota_info *osc_oqi_alloc(u32 id)
+{
+ struct osc_quota_info *oqi;
+
+ OBD_SLAB_ALLOC_PTR(oqi, osc_quota_kmem);
+ if (oqi != NULL)
+ oqi->oqi_id = id;
+
+ return oqi;
+}
+
+int osc_quota_chkdq(struct client_obd *cli, const unsigned int qid[])
+{
+ int type;
+
+ for (type = 0; type < MAXQUOTAS; type++) {
+ struct osc_quota_info *oqi;
+
+ oqi = cfs_hash_lookup(cli->cl_quota_hash[type], &qid[type]);
+ if (oqi) {
+ /* do not try to access oqi here, it could have been
+ * freed by osc_quota_setdq() */
+
+ /* the slot is busy, the user is about to run out of
+ * quota space on this OST */
+ CDEBUG(D_QUOTA, "chkdq found noquota for %s %d\n",
+ type == USRQUOTA ? "user" : "grout", qid[type]);
+ return NO_QUOTA;
+ }
+ }
+
+ return QUOTA_OK;
+}
+
+#define MD_QUOTA_FLAG(type) ((type == USRQUOTA) ? OBD_MD_FLUSRQUOTA \
+ : OBD_MD_FLGRPQUOTA)
+#define FL_QUOTA_FLAG(type) ((type == USRQUOTA) ? OBD_FL_NO_USRQUOTA \
+ : OBD_FL_NO_GRPQUOTA)
+
+int osc_quota_setdq(struct client_obd *cli, const unsigned int qid[],
+ u32 valid, u32 flags)
+{
+ int type;
+ int rc = 0;
+
+ if ((valid & (OBD_MD_FLUSRQUOTA | OBD_MD_FLGRPQUOTA)) == 0)
+ return 0;
+
+ for (type = 0; type < MAXQUOTAS; type++) {
+ struct osc_quota_info *oqi;
+
+ if ((valid & MD_QUOTA_FLAG(type)) == 0)
+ continue;
+
+ /* lookup the ID in the per-type hash table */
+ oqi = cfs_hash_lookup(cli->cl_quota_hash[type], &qid[type]);
+ if ((flags & FL_QUOTA_FLAG(type)) != 0) {
+ /* This ID is getting close to its quota limit, let's
+ * switch to sync I/O */
+ if (oqi != NULL)
+ continue;
+
+ oqi = osc_oqi_alloc(qid[type]);
+ if (oqi == NULL) {
+ rc = -ENOMEM;
+ break;
+ }
+
+ rc = cfs_hash_add_unique(cli->cl_quota_hash[type],
+ &qid[type], &oqi->oqi_hash);
+ /* race with others? */
+ if (rc == -EALREADY) {
+ rc = 0;
+ OBD_SLAB_FREE_PTR(oqi, osc_quota_kmem);
+ }
+
+ CDEBUG(D_QUOTA, "%s: setdq to insert for %s %d (%d)\n",
+ cli->cl_import->imp_obd->obd_name,
+ type == USRQUOTA ? "user" : "group",
+ qid[type], rc);
+ } else {
+ /* This ID is now off the hook, let's remove it from
+ * the hash table */
+ if (oqi == NULL)
+ continue;
+
+ oqi = cfs_hash_del_key(cli->cl_quota_hash[type],
+ &qid[type]);
+ if (oqi)
+ OBD_SLAB_FREE_PTR(oqi, osc_quota_kmem);
+
+ CDEBUG(D_QUOTA, "%s: setdq to remove for %s %d (%p)\n",
+ cli->cl_import->imp_obd->obd_name,
+ type == USRQUOTA ? "user" : "group",
+ qid[type], oqi);
+ }
+ }
+
+ return rc;
+}
+
+/*
+ * Hash operations for uid/gid <-> osc_quota_info
+ */
+static unsigned
+oqi_hashfn(struct cfs_hash *hs, const void *key, unsigned mask)
+{
+ return cfs_hash_u32_hash(*((__u32 *)key), mask);
+}
+
+static int
+oqi_keycmp(const void *key, struct hlist_node *hnode)
+{
+ struct osc_quota_info *oqi;
+ u32 uid;
+
+ LASSERT(key != NULL);
+ uid = *((u32 *)key);
+ oqi = hlist_entry(hnode, struct osc_quota_info, oqi_hash);
+
+ return uid == oqi->oqi_id;
+}
+
+static void *
+oqi_key(struct hlist_node *hnode)
+{
+ struct osc_quota_info *oqi;
+ oqi = hlist_entry(hnode, struct osc_quota_info, oqi_hash);
+ return &oqi->oqi_id;
+}
+
+static void *
+oqi_object(struct hlist_node *hnode)
+{
+ return hlist_entry(hnode, struct osc_quota_info, oqi_hash);
+}
+
+static void
+oqi_get(struct cfs_hash *hs, struct hlist_node *hnode)
+{
+}
+
+static void
+oqi_put_locked(struct cfs_hash *hs, struct hlist_node *hnode)
+{
+}
+
+static void
+oqi_exit(struct cfs_hash *hs, struct hlist_node *hnode)
+{
+ struct osc_quota_info *oqi;
+
+ oqi = hlist_entry(hnode, struct osc_quota_info, oqi_hash);
+
+ OBD_SLAB_FREE_PTR(oqi, osc_quota_kmem);
+}
+
+#define HASH_QUOTA_BKT_BITS 5
+#define HASH_QUOTA_CUR_BITS 5
+#define HASH_QUOTA_MAX_BITS 15
+
+static cfs_hash_ops_t quota_hash_ops = {
+ .hs_hash = oqi_hashfn,
+ .hs_keycmp = oqi_keycmp,
+ .hs_key = oqi_key,
+ .hs_object = oqi_object,
+ .hs_get = oqi_get,
+ .hs_put_locked = oqi_put_locked,
+ .hs_exit = oqi_exit,
+};
+
+int osc_quota_setup(struct obd_device *obd)
+{
+ struct client_obd *cli = &obd->u.cli;
+ int i, type;
+
+ for (type = 0; type < MAXQUOTAS; type++) {
+ cli->cl_quota_hash[type] = cfs_hash_create("QUOTA_HASH",
+ HASH_QUOTA_CUR_BITS,
+ HASH_QUOTA_MAX_BITS,
+ HASH_QUOTA_BKT_BITS,
+ 0,
+ CFS_HASH_MIN_THETA,
+ CFS_HASH_MAX_THETA,
+ &quota_hash_ops,
+ CFS_HASH_DEFAULT);
+ if (cli->cl_quota_hash[type] == NULL)
+ break;
+ }
+
+ if (type == MAXQUOTAS)
+ return 0;
+
+ for (i = 0; i < type; i++)
+ cfs_hash_putref(cli->cl_quota_hash[i]);
+
+ return -ENOMEM;
+}
+
+int osc_quota_cleanup(struct obd_device *obd)
+{
+ struct client_obd *cli = &obd->u.cli;
+ int type;
+
+ for (type = 0; type < MAXQUOTAS; type++)
+ cfs_hash_putref(cli->cl_quota_hash[type]);
+
+ return 0;
+}
+
+int osc_quotactl(struct obd_device *unused, struct obd_export *exp,
+ struct obd_quotactl *oqctl)
+{
+ struct ptlrpc_request *req;
+ struct obd_quotactl *oqc;
+ int rc;
+
+ req = ptlrpc_request_alloc_pack(class_exp2cliimp(exp),
+ &RQF_OST_QUOTACTL, LUSTRE_OST_VERSION,
+ OST_QUOTACTL);
+ if (req == NULL)
+ return -ENOMEM;
+
+ oqc = req_capsule_client_get(&req->rq_pill, &RMF_OBD_QUOTACTL);
+ *oqc = *oqctl;
+
+ ptlrpc_request_set_replen(req);
+ ptlrpc_at_set_req_timeout(req);
+ req->rq_no_resend = 1;
+
+ rc = ptlrpc_queue_wait(req);
+ if (rc)
+ CERROR("ptlrpc_queue_wait failed, rc: %d\n", rc);
+
+ if (req->rq_repmsg) {
+ oqc = req_capsule_server_get(&req->rq_pill, &RMF_OBD_QUOTACTL);
+ if (oqc) {
+ *oqctl = *oqc;
+ } else if (!rc) {
+ CERROR("Can't unpack obd_quotactl\n");
+ rc = -EPROTO;
+ }
+ } else if (!rc) {
+ CERROR("Can't unpack obd_quotactl\n");
+ rc = -EPROTO;
+ }
+ ptlrpc_req_finished(req);
+
+ return rc;
+}
+
+int osc_quotacheck(struct obd_device *unused, struct obd_export *exp,
+ struct obd_quotactl *oqctl)
+{
+ struct client_obd *cli = &exp->exp_obd->u.cli;
+ struct ptlrpc_request *req;
+ struct obd_quotactl *body;
+ int rc;
+
+ req = ptlrpc_request_alloc_pack(class_exp2cliimp(exp),
+ &RQF_OST_QUOTACHECK, LUSTRE_OST_VERSION,
+ OST_QUOTACHECK);
+ if (req == NULL)
+ return -ENOMEM;
+
+ body = req_capsule_client_get(&req->rq_pill, &RMF_OBD_QUOTACTL);
+ *body = *oqctl;
+
+ ptlrpc_request_set_replen(req);
+
+ /* the next poll will find -ENODATA, that means quotacheck is
+ * going on */
+ cli->cl_qchk_stat = -ENODATA;
+ rc = ptlrpc_queue_wait(req);
+ if (rc)
+ cli->cl_qchk_stat = rc;
+ ptlrpc_req_finished(req);
+ return rc;
+}
+
+int osc_quota_poll_check(struct obd_export *exp, struct if_quotacheck *qchk)
+{
+ struct client_obd *cli = &exp->exp_obd->u.cli;
+ int rc;
+
+ qchk->obd_uuid = cli->cl_target_uuid;
+ memcpy(qchk->obd_type, LUSTRE_OST_NAME, strlen(LUSTRE_OST_NAME));
+
+ rc = cli->cl_qchk_stat;
+ /* the client is not the previous one */
+ if (rc == CL_NOT_QUOTACHECKED)
+ rc = -EINTR;
+ return rc;
+}
diff --git a/drivers/staging/lustre/lustre/osc/osc_request.c b/drivers/staging/lustre/lustre/osc/osc_request.c
new file mode 100644
index 000000000..d7a9b650d
--- /dev/null
+++ b/drivers/staging/lustre/lustre/osc/osc_request.c
@@ -0,0 +1,3379 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2002, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2011, 2012, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ */
+
+#define DEBUG_SUBSYSTEM S_OSC
+
+#include "../../include/linux/libcfs/libcfs.h"
+
+
+#include "../include/lustre_dlm.h"
+#include "../include/lustre_net.h"
+#include "../include/lustre/lustre_user.h"
+#include "../include/obd_cksum.h"
+
+#include "../include/lustre_ha.h"
+#include "../include/lprocfs_status.h"
+#include "../include/lustre_debug.h"
+#include "../include/lustre_param.h"
+#include "../include/lustre_fid.h"
+#include "../include/obd_class.h"
+#include "osc_internal.h"
+#include "osc_cl_internal.h"
+
+struct osc_brw_async_args {
+ struct obdo *aa_oa;
+ int aa_requested_nob;
+ int aa_nio_count;
+ u32 aa_page_count;
+ int aa_resends;
+ struct brw_page **aa_ppga;
+ struct client_obd *aa_cli;
+ struct list_head aa_oaps;
+ struct list_head aa_exts;
+ struct obd_capa *aa_ocapa;
+ struct cl_req *aa_clerq;
+};
+
+struct osc_async_args {
+ struct obd_info *aa_oi;
+};
+
+struct osc_setattr_args {
+ struct obdo *sa_oa;
+ obd_enqueue_update_f sa_upcall;
+ void *sa_cookie;
+};
+
+struct osc_fsync_args {
+ struct obd_info *fa_oi;
+ obd_enqueue_update_f fa_upcall;
+ void *fa_cookie;
+};
+
+struct osc_enqueue_args {
+ struct obd_export *oa_exp;
+ __u64 *oa_flags;
+ obd_enqueue_update_f oa_upcall;
+ void *oa_cookie;
+ struct ost_lvb *oa_lvb;
+ struct lustre_handle *oa_lockh;
+ struct ldlm_enqueue_info *oa_ei;
+ unsigned int oa_agl:1;
+};
+
+static void osc_release_ppga(struct brw_page **ppga, u32 count);
+static int brw_interpret(const struct lu_env *env,
+ struct ptlrpc_request *req, void *data, int rc);
+int osc_cleanup(struct obd_device *obd);
+
+/* Pack OSC object metadata for disk storage (LE byte order). */
+static int osc_packmd(struct obd_export *exp, struct lov_mds_md **lmmp,
+ struct lov_stripe_md *lsm)
+{
+ int lmm_size;
+
+ lmm_size = sizeof(**lmmp);
+ if (lmmp == NULL)
+ return lmm_size;
+
+ if (*lmmp != NULL && lsm == NULL) {
+ OBD_FREE(*lmmp, lmm_size);
+ *lmmp = NULL;
+ return 0;
+ } else if (unlikely(lsm != NULL && ostid_id(&lsm->lsm_oi) == 0)) {
+ return -EBADF;
+ }
+
+ if (*lmmp == NULL) {
+ OBD_ALLOC(*lmmp, lmm_size);
+ if (*lmmp == NULL)
+ return -ENOMEM;
+ }
+
+ if (lsm)
+ ostid_cpu_to_le(&lsm->lsm_oi, &(*lmmp)->lmm_oi);
+
+ return lmm_size;
+}
+
+/* Unpack OSC object metadata from disk storage (LE byte order). */
+static int osc_unpackmd(struct obd_export *exp, struct lov_stripe_md **lsmp,
+ struct lov_mds_md *lmm, int lmm_bytes)
+{
+ int lsm_size;
+ struct obd_import *imp = class_exp2cliimp(exp);
+
+ if (lmm != NULL) {
+ if (lmm_bytes < sizeof(*lmm)) {
+ CERROR("%s: lov_mds_md too small: %d, need %d\n",
+ exp->exp_obd->obd_name, lmm_bytes,
+ (int)sizeof(*lmm));
+ return -EINVAL;
+ }
+ /* XXX LOV_MAGIC etc check? */
+
+ if (unlikely(ostid_id(&lmm->lmm_oi) == 0)) {
+ CERROR("%s: zero lmm_object_id: rc = %d\n",
+ exp->exp_obd->obd_name, -EINVAL);
+ return -EINVAL;
+ }
+ }
+
+ lsm_size = lov_stripe_md_size(1);
+ if (lsmp == NULL)
+ return lsm_size;
+
+ if (*lsmp != NULL && lmm == NULL) {
+ OBD_FREE((*lsmp)->lsm_oinfo[0], sizeof(struct lov_oinfo));
+ OBD_FREE(*lsmp, lsm_size);
+ *lsmp = NULL;
+ return 0;
+ }
+
+ if (*lsmp == NULL) {
+ OBD_ALLOC(*lsmp, lsm_size);
+ if (unlikely(*lsmp == NULL))
+ return -ENOMEM;
+ OBD_ALLOC((*lsmp)->lsm_oinfo[0], sizeof(struct lov_oinfo));
+ if (unlikely((*lsmp)->lsm_oinfo[0] == NULL)) {
+ OBD_FREE(*lsmp, lsm_size);
+ return -ENOMEM;
+ }
+ loi_init((*lsmp)->lsm_oinfo[0]);
+ } else if (unlikely(ostid_id(&(*lsmp)->lsm_oi) == 0)) {
+ return -EBADF;
+ }
+
+ if (lmm != NULL)
+ /* XXX zero *lsmp? */
+ ostid_le_to_cpu(&lmm->lmm_oi, &(*lsmp)->lsm_oi);
+
+ if (imp != NULL &&
+ (imp->imp_connect_data.ocd_connect_flags & OBD_CONNECT_MAXBYTES))
+ (*lsmp)->lsm_maxbytes = imp->imp_connect_data.ocd_maxbytes;
+ else
+ (*lsmp)->lsm_maxbytes = LUSTRE_STRIPE_MAXBYTES;
+
+ return lsm_size;
+}
+
+static inline void osc_pack_capa(struct ptlrpc_request *req,
+ struct ost_body *body, void *capa)
+{
+ struct obd_capa *oc = (struct obd_capa *)capa;
+ struct lustre_capa *c;
+
+ if (!capa)
+ return;
+
+ c = req_capsule_client_get(&req->rq_pill, &RMF_CAPA1);
+ LASSERT(c);
+ capa_cpy(c, oc);
+ body->oa.o_valid |= OBD_MD_FLOSSCAPA;
+ DEBUG_CAPA(D_SEC, c, "pack");
+}
+
+static inline void osc_pack_req_body(struct ptlrpc_request *req,
+ struct obd_info *oinfo)
+{
+ struct ost_body *body;
+
+ body = req_capsule_client_get(&req->rq_pill, &RMF_OST_BODY);
+ LASSERT(body);
+
+ lustre_set_wire_obdo(&req->rq_import->imp_connect_data, &body->oa,
+ oinfo->oi_oa);
+ osc_pack_capa(req, body, oinfo->oi_capa);
+}
+
+static inline void osc_set_capa_size(struct ptlrpc_request *req,
+ const struct req_msg_field *field,
+ struct obd_capa *oc)
+{
+ if (oc == NULL)
+ req_capsule_set_size(&req->rq_pill, field, RCL_CLIENT, 0);
+ else
+ /* it is already calculated as sizeof struct obd_capa */
+ ;
+}
+
+static int osc_getattr_interpret(const struct lu_env *env,
+ struct ptlrpc_request *req,
+ struct osc_async_args *aa, int rc)
+{
+ struct ost_body *body;
+
+ if (rc != 0)
+ goto out;
+
+ body = req_capsule_server_get(&req->rq_pill, &RMF_OST_BODY);
+ if (body) {
+ CDEBUG(D_INODE, "mode: %o\n", body->oa.o_mode);
+ lustre_get_wire_obdo(&req->rq_import->imp_connect_data,
+ aa->aa_oi->oi_oa, &body->oa);
+
+ /* This should really be sent by the OST */
+ aa->aa_oi->oi_oa->o_blksize = DT_MAX_BRW_SIZE;
+ aa->aa_oi->oi_oa->o_valid |= OBD_MD_FLBLKSZ;
+ } else {
+ CDEBUG(D_INFO, "can't unpack ost_body\n");
+ rc = -EPROTO;
+ aa->aa_oi->oi_oa->o_valid = 0;
+ }
+out:
+ rc = aa->aa_oi->oi_cb_up(aa->aa_oi, rc);
+ return rc;
+}
+
+static int osc_getattr_async(struct obd_export *exp, struct obd_info *oinfo,
+ struct ptlrpc_request_set *set)
+{
+ struct ptlrpc_request *req;
+ struct osc_async_args *aa;
+ int rc;
+
+ req = ptlrpc_request_alloc(class_exp2cliimp(exp), &RQF_OST_GETATTR);
+ if (req == NULL)
+ return -ENOMEM;
+
+ osc_set_capa_size(req, &RMF_CAPA1, oinfo->oi_capa);
+ rc = ptlrpc_request_pack(req, LUSTRE_OST_VERSION, OST_GETATTR);
+ if (rc) {
+ ptlrpc_request_free(req);
+ return rc;
+ }
+
+ osc_pack_req_body(req, oinfo);
+
+ ptlrpc_request_set_replen(req);
+ req->rq_interpret_reply = (ptlrpc_interpterer_t)osc_getattr_interpret;
+
+ CLASSERT(sizeof(*aa) <= sizeof(req->rq_async_args));
+ aa = ptlrpc_req_async_args(req);
+ aa->aa_oi = oinfo;
+
+ ptlrpc_set_add_req(set, req);
+ return 0;
+}
+
+static int osc_getattr(const struct lu_env *env, struct obd_export *exp,
+ struct obd_info *oinfo)
+{
+ struct ptlrpc_request *req;
+ struct ost_body *body;
+ int rc;
+
+ req = ptlrpc_request_alloc(class_exp2cliimp(exp), &RQF_OST_GETATTR);
+ if (req == NULL)
+ return -ENOMEM;
+
+ osc_set_capa_size(req, &RMF_CAPA1, oinfo->oi_capa);
+ rc = ptlrpc_request_pack(req, LUSTRE_OST_VERSION, OST_GETATTR);
+ if (rc) {
+ ptlrpc_request_free(req);
+ return rc;
+ }
+
+ osc_pack_req_body(req, oinfo);
+
+ ptlrpc_request_set_replen(req);
+
+ rc = ptlrpc_queue_wait(req);
+ if (rc)
+ goto out;
+
+ body = req_capsule_server_get(&req->rq_pill, &RMF_OST_BODY);
+ if (body == NULL) {
+ rc = -EPROTO;
+ goto out;
+ }
+
+ CDEBUG(D_INODE, "mode: %o\n", body->oa.o_mode);
+ lustre_get_wire_obdo(&req->rq_import->imp_connect_data, oinfo->oi_oa,
+ &body->oa);
+
+ oinfo->oi_oa->o_blksize = cli_brw_size(exp->exp_obd);
+ oinfo->oi_oa->o_valid |= OBD_MD_FLBLKSZ;
+
+ out:
+ ptlrpc_req_finished(req);
+ return rc;
+}
+
+static int osc_setattr(const struct lu_env *env, struct obd_export *exp,
+ struct obd_info *oinfo, struct obd_trans_info *oti)
+{
+ struct ptlrpc_request *req;
+ struct ost_body *body;
+ int rc;
+
+ LASSERT(oinfo->oi_oa->o_valid & OBD_MD_FLGROUP);
+
+ req = ptlrpc_request_alloc(class_exp2cliimp(exp), &RQF_OST_SETATTR);
+ if (req == NULL)
+ return -ENOMEM;
+
+ osc_set_capa_size(req, &RMF_CAPA1, oinfo->oi_capa);
+ rc = ptlrpc_request_pack(req, LUSTRE_OST_VERSION, OST_SETATTR);
+ if (rc) {
+ ptlrpc_request_free(req);
+ return rc;
+ }
+
+ osc_pack_req_body(req, oinfo);
+
+ ptlrpc_request_set_replen(req);
+
+ rc = ptlrpc_queue_wait(req);
+ if (rc)
+ goto out;
+
+ body = req_capsule_server_get(&req->rq_pill, &RMF_OST_BODY);
+ if (body == NULL) {
+ rc = -EPROTO;
+ goto out;
+ }
+
+ lustre_get_wire_obdo(&req->rq_import->imp_connect_data, oinfo->oi_oa,
+ &body->oa);
+
+out:
+ ptlrpc_req_finished(req);
+ return rc;
+}
+
+static int osc_setattr_interpret(const struct lu_env *env,
+ struct ptlrpc_request *req,
+ struct osc_setattr_args *sa, int rc)
+{
+ struct ost_body *body;
+
+ if (rc != 0)
+ goto out;
+
+ body = req_capsule_server_get(&req->rq_pill, &RMF_OST_BODY);
+ if (body == NULL) {
+ rc = -EPROTO;
+ goto out;
+ }
+
+ lustre_get_wire_obdo(&req->rq_import->imp_connect_data, sa->sa_oa,
+ &body->oa);
+out:
+ rc = sa->sa_upcall(sa->sa_cookie, rc);
+ return rc;
+}
+
+int osc_setattr_async_base(struct obd_export *exp, struct obd_info *oinfo,
+ struct obd_trans_info *oti,
+ obd_enqueue_update_f upcall, void *cookie,
+ struct ptlrpc_request_set *rqset)
+{
+ struct ptlrpc_request *req;
+ struct osc_setattr_args *sa;
+ int rc;
+
+ req = ptlrpc_request_alloc(class_exp2cliimp(exp), &RQF_OST_SETATTR);
+ if (req == NULL)
+ return -ENOMEM;
+
+ osc_set_capa_size(req, &RMF_CAPA1, oinfo->oi_capa);
+ rc = ptlrpc_request_pack(req, LUSTRE_OST_VERSION, OST_SETATTR);
+ if (rc) {
+ ptlrpc_request_free(req);
+ return rc;
+ }
+
+ if (oti && oinfo->oi_oa->o_valid & OBD_MD_FLCOOKIE)
+ oinfo->oi_oa->o_lcookie = *oti->oti_logcookies;
+
+ osc_pack_req_body(req, oinfo);
+
+ ptlrpc_request_set_replen(req);
+
+ /* do mds to ost setattr asynchronously */
+ if (!rqset) {
+ /* Do not wait for response. */
+ ptlrpcd_add_req(req, PDL_POLICY_ROUND, -1);
+ } else {
+ req->rq_interpret_reply =
+ (ptlrpc_interpterer_t)osc_setattr_interpret;
+
+ CLASSERT (sizeof(*sa) <= sizeof(req->rq_async_args));
+ sa = ptlrpc_req_async_args(req);
+ sa->sa_oa = oinfo->oi_oa;
+ sa->sa_upcall = upcall;
+ sa->sa_cookie = cookie;
+
+ if (rqset == PTLRPCD_SET)
+ ptlrpcd_add_req(req, PDL_POLICY_ROUND, -1);
+ else
+ ptlrpc_set_add_req(rqset, req);
+ }
+
+ return 0;
+}
+
+static int osc_setattr_async(struct obd_export *exp, struct obd_info *oinfo,
+ struct obd_trans_info *oti,
+ struct ptlrpc_request_set *rqset)
+{
+ return osc_setattr_async_base(exp, oinfo, oti,
+ oinfo->oi_cb_up, oinfo, rqset);
+}
+
+int osc_real_create(struct obd_export *exp, struct obdo *oa,
+ struct lov_stripe_md **ea, struct obd_trans_info *oti)
+{
+ struct ptlrpc_request *req;
+ struct ost_body *body;
+ struct lov_stripe_md *lsm;
+ int rc;
+
+ LASSERT(oa);
+ LASSERT(ea);
+
+ lsm = *ea;
+ if (!lsm) {
+ rc = obd_alloc_memmd(exp, &lsm);
+ if (rc < 0)
+ return rc;
+ }
+
+ req = ptlrpc_request_alloc(class_exp2cliimp(exp), &RQF_OST_CREATE);
+ if (req == NULL) {
+ rc = -ENOMEM;
+ goto out;
+ }
+
+ rc = ptlrpc_request_pack(req, LUSTRE_OST_VERSION, OST_CREATE);
+ if (rc) {
+ ptlrpc_request_free(req);
+ goto out;
+ }
+
+ body = req_capsule_client_get(&req->rq_pill, &RMF_OST_BODY);
+ LASSERT(body);
+
+ lustre_set_wire_obdo(&req->rq_import->imp_connect_data, &body->oa, oa);
+
+ ptlrpc_request_set_replen(req);
+
+ if ((oa->o_valid & OBD_MD_FLFLAGS) &&
+ oa->o_flags == OBD_FL_DELORPHAN) {
+ DEBUG_REQ(D_HA, req,
+ "delorphan from OST integration");
+ /* Don't resend the delorphan req */
+ req->rq_no_resend = req->rq_no_delay = 1;
+ }
+
+ rc = ptlrpc_queue_wait(req);
+ if (rc)
+ goto out_req;
+
+ body = req_capsule_server_get(&req->rq_pill, &RMF_OST_BODY);
+ if (body == NULL) {
+ rc = -EPROTO;
+ goto out_req;
+ }
+
+ CDEBUG(D_INFO, "oa flags %x\n", oa->o_flags);
+ lustre_get_wire_obdo(&req->rq_import->imp_connect_data, oa, &body->oa);
+
+ oa->o_blksize = cli_brw_size(exp->exp_obd);
+ oa->o_valid |= OBD_MD_FLBLKSZ;
+
+ /* XXX LOV STACKING: the lsm that is passed to us from LOV does not
+ * have valid lsm_oinfo data structs, so don't go touching that.
+ * This needs to be fixed in a big way.
+ */
+ lsm->lsm_oi = oa->o_oi;
+ *ea = lsm;
+
+ if (oti != NULL) {
+ oti->oti_transno = lustre_msg_get_transno(req->rq_repmsg);
+
+ if (oa->o_valid & OBD_MD_FLCOOKIE) {
+ if (!oti->oti_logcookies)
+ oti_alloc_cookies(oti, 1);
+ *oti->oti_logcookies = oa->o_lcookie;
+ }
+ }
+
+ CDEBUG(D_HA, "transno: %lld\n",
+ lustre_msg_get_transno(req->rq_repmsg));
+out_req:
+ ptlrpc_req_finished(req);
+out:
+ if (rc && !*ea)
+ obd_free_memmd(exp, &lsm);
+ return rc;
+}
+
+int osc_punch_base(struct obd_export *exp, struct obd_info *oinfo,
+ obd_enqueue_update_f upcall, void *cookie,
+ struct ptlrpc_request_set *rqset)
+{
+ struct ptlrpc_request *req;
+ struct osc_setattr_args *sa;
+ struct ost_body *body;
+ int rc;
+
+ req = ptlrpc_request_alloc(class_exp2cliimp(exp), &RQF_OST_PUNCH);
+ if (req == NULL)
+ return -ENOMEM;
+
+ osc_set_capa_size(req, &RMF_CAPA1, oinfo->oi_capa);
+ rc = ptlrpc_request_pack(req, LUSTRE_OST_VERSION, OST_PUNCH);
+ if (rc) {
+ ptlrpc_request_free(req);
+ return rc;
+ }
+ req->rq_request_portal = OST_IO_PORTAL; /* bug 7198 */
+ ptlrpc_at_set_req_timeout(req);
+
+ body = req_capsule_client_get(&req->rq_pill, &RMF_OST_BODY);
+ LASSERT(body);
+ lustre_set_wire_obdo(&req->rq_import->imp_connect_data, &body->oa,
+ oinfo->oi_oa);
+ osc_pack_capa(req, body, oinfo->oi_capa);
+
+ ptlrpc_request_set_replen(req);
+
+ req->rq_interpret_reply = (ptlrpc_interpterer_t)osc_setattr_interpret;
+ CLASSERT (sizeof(*sa) <= sizeof(req->rq_async_args));
+ sa = ptlrpc_req_async_args(req);
+ sa->sa_oa = oinfo->oi_oa;
+ sa->sa_upcall = upcall;
+ sa->sa_cookie = cookie;
+ if (rqset == PTLRPCD_SET)
+ ptlrpcd_add_req(req, PDL_POLICY_ROUND, -1);
+ else
+ ptlrpc_set_add_req(rqset, req);
+
+ return 0;
+}
+
+static int osc_sync_interpret(const struct lu_env *env,
+ struct ptlrpc_request *req,
+ void *arg, int rc)
+{
+ struct osc_fsync_args *fa = arg;
+ struct ost_body *body;
+
+ if (rc)
+ goto out;
+
+ body = req_capsule_server_get(&req->rq_pill, &RMF_OST_BODY);
+ if (body == NULL) {
+ CERROR ("can't unpack ost_body\n");
+ rc = -EPROTO;
+ goto out;
+ }
+
+ *fa->fa_oi->oi_oa = body->oa;
+out:
+ rc = fa->fa_upcall(fa->fa_cookie, rc);
+ return rc;
+}
+
+int osc_sync_base(struct obd_export *exp, struct obd_info *oinfo,
+ obd_enqueue_update_f upcall, void *cookie,
+ struct ptlrpc_request_set *rqset)
+{
+ struct ptlrpc_request *req;
+ struct ost_body *body;
+ struct osc_fsync_args *fa;
+ int rc;
+
+ req = ptlrpc_request_alloc(class_exp2cliimp(exp), &RQF_OST_SYNC);
+ if (req == NULL)
+ return -ENOMEM;
+
+ osc_set_capa_size(req, &RMF_CAPA1, oinfo->oi_capa);
+ rc = ptlrpc_request_pack(req, LUSTRE_OST_VERSION, OST_SYNC);
+ if (rc) {
+ ptlrpc_request_free(req);
+ return rc;
+ }
+
+ /* overload the size and blocks fields in the oa with start/end */
+ body = req_capsule_client_get(&req->rq_pill, &RMF_OST_BODY);
+ LASSERT(body);
+ lustre_set_wire_obdo(&req->rq_import->imp_connect_data, &body->oa,
+ oinfo->oi_oa);
+ osc_pack_capa(req, body, oinfo->oi_capa);
+
+ ptlrpc_request_set_replen(req);
+ req->rq_interpret_reply = osc_sync_interpret;
+
+ CLASSERT(sizeof(*fa) <= sizeof(req->rq_async_args));
+ fa = ptlrpc_req_async_args(req);
+ fa->fa_oi = oinfo;
+ fa->fa_upcall = upcall;
+ fa->fa_cookie = cookie;
+
+ if (rqset == PTLRPCD_SET)
+ ptlrpcd_add_req(req, PDL_POLICY_ROUND, -1);
+ else
+ ptlrpc_set_add_req(rqset, req);
+
+ return 0;
+}
+
+/* Find and cancel locally locks matched by @mode in the resource found by
+ * @objid. Found locks are added into @cancel list. Returns the amount of
+ * locks added to @cancels list. */
+static int osc_resource_get_unused(struct obd_export *exp, struct obdo *oa,
+ struct list_head *cancels,
+ ldlm_mode_t mode, __u64 lock_flags)
+{
+ struct ldlm_namespace *ns = exp->exp_obd->obd_namespace;
+ struct ldlm_res_id res_id;
+ struct ldlm_resource *res;
+ int count;
+
+ /* Return, i.e. cancel nothing, only if ELC is supported (flag in
+ * export) but disabled through procfs (flag in NS).
+ *
+ * This distinguishes from a case when ELC is not supported originally,
+ * when we still want to cancel locks in advance and just cancel them
+ * locally, without sending any RPC. */
+ if (exp_connect_cancelset(exp) && !ns_connect_cancelset(ns))
+ return 0;
+
+ ostid_build_res_name(&oa->o_oi, &res_id);
+ res = ldlm_resource_get(ns, NULL, &res_id, 0, 0);
+ if (res == NULL)
+ return 0;
+
+ LDLM_RESOURCE_ADDREF(res);
+ count = ldlm_cancel_resource_local(res, cancels, NULL, mode,
+ lock_flags, 0, NULL);
+ LDLM_RESOURCE_DELREF(res);
+ ldlm_resource_putref(res);
+ return count;
+}
+
+static int osc_destroy_interpret(const struct lu_env *env,
+ struct ptlrpc_request *req, void *data,
+ int rc)
+{
+ struct client_obd *cli = &req->rq_import->imp_obd->u.cli;
+
+ atomic_dec(&cli->cl_destroy_in_flight);
+ wake_up(&cli->cl_destroy_waitq);
+ return 0;
+}
+
+static int osc_can_send_destroy(struct client_obd *cli)
+{
+ if (atomic_inc_return(&cli->cl_destroy_in_flight) <=
+ cli->cl_max_rpcs_in_flight) {
+ /* The destroy request can be sent */
+ return 1;
+ }
+ if (atomic_dec_return(&cli->cl_destroy_in_flight) <
+ cli->cl_max_rpcs_in_flight) {
+ /*
+ * The counter has been modified between the two atomic
+ * operations.
+ */
+ wake_up(&cli->cl_destroy_waitq);
+ }
+ return 0;
+}
+
+int osc_create(const struct lu_env *env, struct obd_export *exp,
+ struct obdo *oa, struct lov_stripe_md **ea,
+ struct obd_trans_info *oti)
+{
+ int rc = 0;
+
+ LASSERT(oa);
+ LASSERT(ea);
+ LASSERT(oa->o_valid & OBD_MD_FLGROUP);
+
+ if ((oa->o_valid & OBD_MD_FLFLAGS) &&
+ oa->o_flags == OBD_FL_RECREATE_OBJS) {
+ return osc_real_create(exp, oa, ea, oti);
+ }
+
+ if (!fid_seq_is_mdt(ostid_seq(&oa->o_oi)))
+ return osc_real_create(exp, oa, ea, oti);
+
+ /* we should not get here anymore */
+ LBUG();
+
+ return rc;
+}
+
+/* Destroy requests can be async always on the client, and we don't even really
+ * care about the return code since the client cannot do anything at all about
+ * a destroy failure.
+ * When the MDS is unlinking a filename, it saves the file objects into a
+ * recovery llog, and these object records are cancelled when the OST reports
+ * they were destroyed and sync'd to disk (i.e. transaction committed).
+ * If the client dies, or the OST is down when the object should be destroyed,
+ * the records are not cancelled, and when the OST reconnects to the MDS next,
+ * it will retrieve the llog unlink logs and then sends the log cancellation
+ * cookies to the MDS after committing destroy transactions. */
+static int osc_destroy(const struct lu_env *env, struct obd_export *exp,
+ struct obdo *oa, struct lov_stripe_md *ea,
+ struct obd_trans_info *oti, struct obd_export *md_export,
+ void *capa)
+{
+ struct client_obd *cli = &exp->exp_obd->u.cli;
+ struct ptlrpc_request *req;
+ struct ost_body *body;
+ LIST_HEAD(cancels);
+ int rc, count;
+
+ if (!oa) {
+ CDEBUG(D_INFO, "oa NULL\n");
+ return -EINVAL;
+ }
+
+ count = osc_resource_get_unused(exp, oa, &cancels, LCK_PW,
+ LDLM_FL_DISCARD_DATA);
+
+ req = ptlrpc_request_alloc(class_exp2cliimp(exp), &RQF_OST_DESTROY);
+ if (req == NULL) {
+ ldlm_lock_list_put(&cancels, l_bl_ast, count);
+ return -ENOMEM;
+ }
+
+ osc_set_capa_size(req, &RMF_CAPA1, (struct obd_capa *)capa);
+ rc = ldlm_prep_elc_req(exp, req, LUSTRE_OST_VERSION, OST_DESTROY,
+ 0, &cancels, count);
+ if (rc) {
+ ptlrpc_request_free(req);
+ return rc;
+ }
+
+ req->rq_request_portal = OST_IO_PORTAL; /* bug 7198 */
+ ptlrpc_at_set_req_timeout(req);
+
+ if (oti != NULL && oa->o_valid & OBD_MD_FLCOOKIE)
+ oa->o_lcookie = *oti->oti_logcookies;
+ body = req_capsule_client_get(&req->rq_pill, &RMF_OST_BODY);
+ LASSERT(body);
+ lustre_set_wire_obdo(&req->rq_import->imp_connect_data, &body->oa, oa);
+
+ osc_pack_capa(req, body, (struct obd_capa *)capa);
+ ptlrpc_request_set_replen(req);
+
+ /* If osc_destroy is for destroying the unlink orphan,
+ * sent from MDT to OST, which should not be blocked here,
+ * because the process might be triggered by ptlrpcd, and
+ * it is not good to block ptlrpcd thread (b=16006)*/
+ if (!(oa->o_flags & OBD_FL_DELORPHAN)) {
+ req->rq_interpret_reply = osc_destroy_interpret;
+ if (!osc_can_send_destroy(cli)) {
+ struct l_wait_info lwi = LWI_INTR(LWI_ON_SIGNAL_NOOP,
+ NULL);
+
+ /*
+ * Wait until the number of on-going destroy RPCs drops
+ * under max_rpc_in_flight
+ */
+ l_wait_event_exclusive(cli->cl_destroy_waitq,
+ osc_can_send_destroy(cli), &lwi);
+ }
+ }
+
+ /* Do not wait for response */
+ ptlrpcd_add_req(req, PDL_POLICY_ROUND, -1);
+ return 0;
+}
+
+static void osc_announce_cached(struct client_obd *cli, struct obdo *oa,
+ long writing_bytes)
+{
+ u32 bits = OBD_MD_FLBLOCKS|OBD_MD_FLGRANT;
+
+ LASSERT(!(oa->o_valid & bits));
+
+ oa->o_valid |= bits;
+ client_obd_list_lock(&cli->cl_loi_list_lock);
+ oa->o_dirty = cli->cl_dirty;
+ if (unlikely(cli->cl_dirty - cli->cl_dirty_transit >
+ cli->cl_dirty_max)) {
+ CERROR("dirty %lu - %lu > dirty_max %lu\n",
+ cli->cl_dirty, cli->cl_dirty_transit, cli->cl_dirty_max);
+ oa->o_undirty = 0;
+ } else if (unlikely(atomic_read(&obd_dirty_pages) -
+ atomic_read(&obd_dirty_transit_pages) >
+ (long)(obd_max_dirty_pages + 1))) {
+ /* The atomic_read() allowing the atomic_inc() are
+ * not covered by a lock thus they may safely race and trip
+ * this CERROR() unless we add in a small fudge factor (+1). */
+ CERROR("dirty %d - %d > system dirty_max %d\n",
+ atomic_read(&obd_dirty_pages),
+ atomic_read(&obd_dirty_transit_pages),
+ obd_max_dirty_pages);
+ oa->o_undirty = 0;
+ } else if (unlikely(cli->cl_dirty_max - cli->cl_dirty > 0x7fffffff)) {
+ CERROR("dirty %lu - dirty_max %lu too big???\n",
+ cli->cl_dirty, cli->cl_dirty_max);
+ oa->o_undirty = 0;
+ } else {
+ long max_in_flight = (cli->cl_max_pages_per_rpc <<
+ PAGE_CACHE_SHIFT)*
+ (cli->cl_max_rpcs_in_flight + 1);
+ oa->o_undirty = max(cli->cl_dirty_max, max_in_flight);
+ }
+ oa->o_grant = cli->cl_avail_grant + cli->cl_reserved_grant;
+ oa->o_dropped = cli->cl_lost_grant;
+ cli->cl_lost_grant = 0;
+ client_obd_list_unlock(&cli->cl_loi_list_lock);
+ CDEBUG(D_CACHE, "dirty: %llu undirty: %u dropped %u grant: %llu\n",
+ oa->o_dirty, oa->o_undirty, oa->o_dropped, oa->o_grant);
+
+}
+
+void osc_update_next_shrink(struct client_obd *cli)
+{
+ cli->cl_next_shrink_grant =
+ cfs_time_shift(cli->cl_grant_shrink_interval);
+ CDEBUG(D_CACHE, "next time %ld to shrink grant \n",
+ cli->cl_next_shrink_grant);
+}
+
+static void __osc_update_grant(struct client_obd *cli, u64 grant)
+{
+ client_obd_list_lock(&cli->cl_loi_list_lock);
+ cli->cl_avail_grant += grant;
+ client_obd_list_unlock(&cli->cl_loi_list_lock);
+}
+
+static void osc_update_grant(struct client_obd *cli, struct ost_body *body)
+{
+ if (body->oa.o_valid & OBD_MD_FLGRANT) {
+ CDEBUG(D_CACHE, "got %llu extra grant\n", body->oa.o_grant);
+ __osc_update_grant(cli, body->oa.o_grant);
+ }
+}
+
+static int osc_set_info_async(const struct lu_env *env, struct obd_export *exp,
+ u32 keylen, void *key, u32 vallen,
+ void *val, struct ptlrpc_request_set *set);
+
+static int osc_shrink_grant_interpret(const struct lu_env *env,
+ struct ptlrpc_request *req,
+ void *aa, int rc)
+{
+ struct client_obd *cli = &req->rq_import->imp_obd->u.cli;
+ struct obdo *oa = ((struct osc_brw_async_args *)aa)->aa_oa;
+ struct ost_body *body;
+
+ if (rc != 0) {
+ __osc_update_grant(cli, oa->o_grant);
+ goto out;
+ }
+
+ body = req_capsule_server_get(&req->rq_pill, &RMF_OST_BODY);
+ LASSERT(body);
+ osc_update_grant(cli, body);
+out:
+ OBDO_FREE(oa);
+ return rc;
+}
+
+static void osc_shrink_grant_local(struct client_obd *cli, struct obdo *oa)
+{
+ client_obd_list_lock(&cli->cl_loi_list_lock);
+ oa->o_grant = cli->cl_avail_grant / 4;
+ cli->cl_avail_grant -= oa->o_grant;
+ client_obd_list_unlock(&cli->cl_loi_list_lock);
+ if (!(oa->o_valid & OBD_MD_FLFLAGS)) {
+ oa->o_valid |= OBD_MD_FLFLAGS;
+ oa->o_flags = 0;
+ }
+ oa->o_flags |= OBD_FL_SHRINK_GRANT;
+ osc_update_next_shrink(cli);
+}
+
+/* Shrink the current grant, either from some large amount to enough for a
+ * full set of in-flight RPCs, or if we have already shrunk to that limit
+ * then to enough for a single RPC. This avoids keeping more grant than
+ * needed, and avoids shrinking the grant piecemeal. */
+static int osc_shrink_grant(struct client_obd *cli)
+{
+ __u64 target_bytes = (cli->cl_max_rpcs_in_flight + 1) *
+ (cli->cl_max_pages_per_rpc << PAGE_CACHE_SHIFT);
+
+ client_obd_list_lock(&cli->cl_loi_list_lock);
+ if (cli->cl_avail_grant <= target_bytes)
+ target_bytes = cli->cl_max_pages_per_rpc << PAGE_CACHE_SHIFT;
+ client_obd_list_unlock(&cli->cl_loi_list_lock);
+
+ return osc_shrink_grant_to_target(cli, target_bytes);
+}
+
+int osc_shrink_grant_to_target(struct client_obd *cli, __u64 target_bytes)
+{
+ int rc = 0;
+ struct ost_body *body;
+
+ client_obd_list_lock(&cli->cl_loi_list_lock);
+ /* Don't shrink if we are already above or below the desired limit
+ * We don't want to shrink below a single RPC, as that will negatively
+ * impact block allocation and long-term performance. */
+ if (target_bytes < cli->cl_max_pages_per_rpc << PAGE_CACHE_SHIFT)
+ target_bytes = cli->cl_max_pages_per_rpc << PAGE_CACHE_SHIFT;
+
+ if (target_bytes >= cli->cl_avail_grant) {
+ client_obd_list_unlock(&cli->cl_loi_list_lock);
+ return 0;
+ }
+ client_obd_list_unlock(&cli->cl_loi_list_lock);
+
+ OBD_ALLOC_PTR(body);
+ if (!body)
+ return -ENOMEM;
+
+ osc_announce_cached(cli, &body->oa, 0);
+
+ client_obd_list_lock(&cli->cl_loi_list_lock);
+ body->oa.o_grant = cli->cl_avail_grant - target_bytes;
+ cli->cl_avail_grant = target_bytes;
+ client_obd_list_unlock(&cli->cl_loi_list_lock);
+ if (!(body->oa.o_valid & OBD_MD_FLFLAGS)) {
+ body->oa.o_valid |= OBD_MD_FLFLAGS;
+ body->oa.o_flags = 0;
+ }
+ body->oa.o_flags |= OBD_FL_SHRINK_GRANT;
+ osc_update_next_shrink(cli);
+
+ rc = osc_set_info_async(NULL, cli->cl_import->imp_obd->obd_self_export,
+ sizeof(KEY_GRANT_SHRINK), KEY_GRANT_SHRINK,
+ sizeof(*body), body, NULL);
+ if (rc != 0)
+ __osc_update_grant(cli, body->oa.o_grant);
+ OBD_FREE_PTR(body);
+ return rc;
+}
+
+static int osc_should_shrink_grant(struct client_obd *client)
+{
+ unsigned long time = cfs_time_current();
+ unsigned long next_shrink = client->cl_next_shrink_grant;
+
+ if ((client->cl_import->imp_connect_data.ocd_connect_flags &
+ OBD_CONNECT_GRANT_SHRINK) == 0)
+ return 0;
+
+ if (cfs_time_aftereq(time, next_shrink - 5 * CFS_TICK)) {
+ /* Get the current RPC size directly, instead of going via:
+ * cli_brw_size(obd->u.cli.cl_import->imp_obd->obd_self_export)
+ * Keep comment here so that it can be found by searching. */
+ int brw_size = client->cl_max_pages_per_rpc << PAGE_CACHE_SHIFT;
+
+ if (client->cl_import->imp_state == LUSTRE_IMP_FULL &&
+ client->cl_avail_grant > brw_size)
+ return 1;
+ else
+ osc_update_next_shrink(client);
+ }
+ return 0;
+}
+
+static int osc_grant_shrink_grant_cb(struct timeout_item *item, void *data)
+{
+ struct client_obd *client;
+
+ list_for_each_entry(client, &item->ti_obd_list,
+ cl_grant_shrink_list) {
+ if (osc_should_shrink_grant(client))
+ osc_shrink_grant(client);
+ }
+ return 0;
+}
+
+static int osc_add_shrink_grant(struct client_obd *client)
+{
+ int rc;
+
+ rc = ptlrpc_add_timeout_client(client->cl_grant_shrink_interval,
+ TIMEOUT_GRANT,
+ osc_grant_shrink_grant_cb, NULL,
+ &client->cl_grant_shrink_list);
+ if (rc) {
+ CERROR("add grant client %s error %d\n",
+ client->cl_import->imp_obd->obd_name, rc);
+ return rc;
+ }
+ CDEBUG(D_CACHE, "add grant client %s \n",
+ client->cl_import->imp_obd->obd_name);
+ osc_update_next_shrink(client);
+ return 0;
+}
+
+static int osc_del_shrink_grant(struct client_obd *client)
+{
+ return ptlrpc_del_timeout_client(&client->cl_grant_shrink_list,
+ TIMEOUT_GRANT);
+}
+
+static void osc_init_grant(struct client_obd *cli, struct obd_connect_data *ocd)
+{
+ /*
+ * ocd_grant is the total grant amount we're expect to hold: if we've
+ * been evicted, it's the new avail_grant amount, cl_dirty will drop
+ * to 0 as inflight RPCs fail out; otherwise, it's avail_grant + dirty.
+ *
+ * race is tolerable here: if we're evicted, but imp_state already
+ * left EVICTED state, then cl_dirty must be 0 already.
+ */
+ client_obd_list_lock(&cli->cl_loi_list_lock);
+ if (cli->cl_import->imp_state == LUSTRE_IMP_EVICTED)
+ cli->cl_avail_grant = ocd->ocd_grant;
+ else
+ cli->cl_avail_grant = ocd->ocd_grant - cli->cl_dirty;
+
+ if (cli->cl_avail_grant < 0) {
+ CWARN("%s: available grant < 0: avail/ocd/dirty %ld/%u/%ld\n",
+ cli->cl_import->imp_obd->obd_name, cli->cl_avail_grant,
+ ocd->ocd_grant, cli->cl_dirty);
+ /* workaround for servers which do not have the patch from
+ * LU-2679 */
+ cli->cl_avail_grant = ocd->ocd_grant;
+ }
+
+ /* determine the appropriate chunk size used by osc_extent. */
+ cli->cl_chunkbits = max_t(int, PAGE_CACHE_SHIFT, ocd->ocd_blocksize);
+ client_obd_list_unlock(&cli->cl_loi_list_lock);
+
+ CDEBUG(D_CACHE, "%s, setting cl_avail_grant: %ld cl_lost_grant: %ld chunk bits: %d\n",
+ cli->cl_import->imp_obd->obd_name,
+ cli->cl_avail_grant, cli->cl_lost_grant, cli->cl_chunkbits);
+
+ if (ocd->ocd_connect_flags & OBD_CONNECT_GRANT_SHRINK &&
+ list_empty(&cli->cl_grant_shrink_list))
+ osc_add_shrink_grant(cli);
+}
+
+/* We assume that the reason this OSC got a short read is because it read
+ * beyond the end of a stripe file; i.e. lustre is reading a sparse file
+ * via the LOV, and it _knows_ it's reading inside the file, it's just that
+ * this stripe never got written at or beyond this stripe offset yet. */
+static void handle_short_read(int nob_read, u32 page_count,
+ struct brw_page **pga)
+{
+ char *ptr;
+ int i = 0;
+
+ /* skip bytes read OK */
+ while (nob_read > 0) {
+ LASSERT (page_count > 0);
+
+ if (pga[i]->count > nob_read) {
+ /* EOF inside this page */
+ ptr = kmap(pga[i]->pg) +
+ (pga[i]->off & ~CFS_PAGE_MASK);
+ memset(ptr + nob_read, 0, pga[i]->count - nob_read);
+ kunmap(pga[i]->pg);
+ page_count--;
+ i++;
+ break;
+ }
+
+ nob_read -= pga[i]->count;
+ page_count--;
+ i++;
+ }
+
+ /* zero remaining pages */
+ while (page_count-- > 0) {
+ ptr = kmap(pga[i]->pg) + (pga[i]->off & ~CFS_PAGE_MASK);
+ memset(ptr, 0, pga[i]->count);
+ kunmap(pga[i]->pg);
+ i++;
+ }
+}
+
+static int check_write_rcs(struct ptlrpc_request *req,
+ int requested_nob, int niocount,
+ u32 page_count, struct brw_page **pga)
+{
+ int i;
+ __u32 *remote_rcs;
+
+ remote_rcs = req_capsule_server_sized_get(&req->rq_pill, &RMF_RCS,
+ sizeof(*remote_rcs) *
+ niocount);
+ if (remote_rcs == NULL) {
+ CDEBUG(D_INFO, "Missing/short RC vector on BRW_WRITE reply\n");
+ return -EPROTO;
+ }
+
+ /* return error if any niobuf was in error */
+ for (i = 0; i < niocount; i++) {
+ if ((int)remote_rcs[i] < 0)
+ return remote_rcs[i];
+
+ if (remote_rcs[i] != 0) {
+ CDEBUG(D_INFO, "rc[%d] invalid (%d) req %p\n",
+ i, remote_rcs[i], req);
+ return -EPROTO;
+ }
+ }
+
+ if (req->rq_bulk->bd_nob_transferred != requested_nob) {
+ CERROR("Unexpected # bytes transferred: %d (requested %d)\n",
+ req->rq_bulk->bd_nob_transferred, requested_nob);
+ return -EPROTO;
+ }
+
+ return 0;
+}
+
+static inline int can_merge_pages(struct brw_page *p1, struct brw_page *p2)
+{
+ if (p1->flag != p2->flag) {
+ unsigned mask = ~(OBD_BRW_FROM_GRANT | OBD_BRW_NOCACHE |
+ OBD_BRW_SYNC | OBD_BRW_ASYNC|OBD_BRW_NOQUOTA);
+
+ /* warn if we try to combine flags that we don't know to be
+ * safe to combine */
+ if (unlikely((p1->flag & mask) != (p2->flag & mask))) {
+ CWARN("Saw flags 0x%x and 0x%x in the same brw, please report this at http://bugs.whamcloud.com/\n",
+ p1->flag, p2->flag);
+ }
+ return 0;
+ }
+
+ return (p1->off + p1->count == p2->off);
+}
+
+static u32 osc_checksum_bulk(int nob, u32 pg_count,
+ struct brw_page **pga, int opc,
+ cksum_type_t cksum_type)
+{
+ __u32 cksum;
+ int i = 0;
+ struct cfs_crypto_hash_desc *hdesc;
+ unsigned int bufsize;
+ int err;
+ unsigned char cfs_alg = cksum_obd2cfs(cksum_type);
+
+ LASSERT(pg_count > 0);
+
+ hdesc = cfs_crypto_hash_init(cfs_alg, NULL, 0);
+ if (IS_ERR(hdesc)) {
+ CERROR("Unable to initialize checksum hash %s\n",
+ cfs_crypto_hash_name(cfs_alg));
+ return PTR_ERR(hdesc);
+ }
+
+ while (nob > 0 && pg_count > 0) {
+ int count = pga[i]->count > nob ? nob : pga[i]->count;
+
+ /* corrupt the data before we compute the checksum, to
+ * simulate an OST->client data error */
+ if (i == 0 && opc == OST_READ &&
+ OBD_FAIL_CHECK(OBD_FAIL_OSC_CHECKSUM_RECEIVE)) {
+ unsigned char *ptr = kmap(pga[i]->pg);
+ int off = pga[i]->off & ~CFS_PAGE_MASK;
+ memcpy(ptr + off, "bad1", min(4, nob));
+ kunmap(pga[i]->pg);
+ }
+ cfs_crypto_hash_update_page(hdesc, pga[i]->pg,
+ pga[i]->off & ~CFS_PAGE_MASK,
+ count);
+ CDEBUG(D_PAGE,
+ "page %p map %p index %lu flags %lx count %u priv %0lx: off %d\n",
+ pga[i]->pg, pga[i]->pg->mapping, pga[i]->pg->index,
+ (long)pga[i]->pg->flags, page_count(pga[i]->pg),
+ page_private(pga[i]->pg),
+ (int)(pga[i]->off & ~CFS_PAGE_MASK));
+
+ nob -= pga[i]->count;
+ pg_count--;
+ i++;
+ }
+
+ bufsize = 4;
+ err = cfs_crypto_hash_final(hdesc, (unsigned char *)&cksum, &bufsize);
+
+ if (err)
+ cfs_crypto_hash_final(hdesc, NULL, NULL);
+
+ /* For sending we only compute the wrong checksum instead
+ * of corrupting the data so it is still correct on a redo */
+ if (opc == OST_WRITE && OBD_FAIL_CHECK(OBD_FAIL_OSC_CHECKSUM_SEND))
+ cksum++;
+
+ return cksum;
+}
+
+static int osc_brw_prep_request(int cmd, struct client_obd *cli,
+ struct obdo *oa,
+ struct lov_stripe_md *lsm, u32 page_count,
+ struct brw_page **pga,
+ struct ptlrpc_request **reqp,
+ struct obd_capa *ocapa, int reserve,
+ int resend)
+{
+ struct ptlrpc_request *req;
+ struct ptlrpc_bulk_desc *desc;
+ struct ost_body *body;
+ struct obd_ioobj *ioobj;
+ struct niobuf_remote *niobuf;
+ int niocount, i, requested_nob, opc, rc;
+ struct osc_brw_async_args *aa;
+ struct req_capsule *pill;
+ struct brw_page *pg_prev;
+
+ if (OBD_FAIL_CHECK(OBD_FAIL_OSC_BRW_PREP_REQ))
+ return -ENOMEM; /* Recoverable */
+ if (OBD_FAIL_CHECK(OBD_FAIL_OSC_BRW_PREP_REQ2))
+ return -EINVAL; /* Fatal */
+
+ if ((cmd & OBD_BRW_WRITE) != 0) {
+ opc = OST_WRITE;
+ req = ptlrpc_request_alloc_pool(cli->cl_import,
+ cli->cl_import->imp_rq_pool,
+ &RQF_OST_BRW_WRITE);
+ } else {
+ opc = OST_READ;
+ req = ptlrpc_request_alloc(cli->cl_import, &RQF_OST_BRW_READ);
+ }
+ if (req == NULL)
+ return -ENOMEM;
+
+ for (niocount = i = 1; i < page_count; i++) {
+ if (!can_merge_pages(pga[i - 1], pga[i]))
+ niocount++;
+ }
+
+ pill = &req->rq_pill;
+ req_capsule_set_size(pill, &RMF_OBD_IOOBJ, RCL_CLIENT,
+ sizeof(*ioobj));
+ req_capsule_set_size(pill, &RMF_NIOBUF_REMOTE, RCL_CLIENT,
+ niocount * sizeof(*niobuf));
+ osc_set_capa_size(req, &RMF_CAPA1, ocapa);
+
+ rc = ptlrpc_request_pack(req, LUSTRE_OST_VERSION, opc);
+ if (rc) {
+ ptlrpc_request_free(req);
+ return rc;
+ }
+ req->rq_request_portal = OST_IO_PORTAL; /* bug 7198 */
+ ptlrpc_at_set_req_timeout(req);
+ /* ask ptlrpc not to resend on EINPROGRESS since BRWs have their own
+ * retry logic */
+ req->rq_no_retry_einprogress = 1;
+
+ desc = ptlrpc_prep_bulk_imp(req, page_count,
+ cli->cl_import->imp_connect_data.ocd_brw_size >> LNET_MTU_BITS,
+ opc == OST_WRITE ? BULK_GET_SOURCE : BULK_PUT_SINK,
+ OST_BULK_PORTAL);
+
+ if (desc == NULL) {
+ rc = -ENOMEM;
+ goto out;
+ }
+ /* NB request now owns desc and will free it when it gets freed */
+
+ body = req_capsule_client_get(pill, &RMF_OST_BODY);
+ ioobj = req_capsule_client_get(pill, &RMF_OBD_IOOBJ);
+ niobuf = req_capsule_client_get(pill, &RMF_NIOBUF_REMOTE);
+ LASSERT(body != NULL && ioobj != NULL && niobuf != NULL);
+
+ lustre_set_wire_obdo(&req->rq_import->imp_connect_data, &body->oa, oa);
+
+ obdo_to_ioobj(oa, ioobj);
+ ioobj->ioo_bufcnt = niocount;
+ /* The high bits of ioo_max_brw tells server _maximum_ number of bulks
+ * that might be send for this request. The actual number is decided
+ * when the RPC is finally sent in ptlrpc_register_bulk(). It sends
+ * "max - 1" for old client compatibility sending "0", and also so the
+ * the actual maximum is a power-of-two number, not one less. LU-1431 */
+ ioobj_max_brw_set(ioobj, desc->bd_md_max_brw);
+ osc_pack_capa(req, body, ocapa);
+ LASSERT(page_count > 0);
+ pg_prev = pga[0];
+ for (requested_nob = i = 0; i < page_count; i++, niobuf++) {
+ struct brw_page *pg = pga[i];
+ int poff = pg->off & ~CFS_PAGE_MASK;
+
+ LASSERT(pg->count > 0);
+ /* make sure there is no gap in the middle of page array */
+ LASSERTF(page_count == 1 ||
+ (ergo(i == 0, poff + pg->count == PAGE_CACHE_SIZE) &&
+ ergo(i > 0 && i < page_count - 1,
+ poff == 0 && pg->count == PAGE_CACHE_SIZE) &&
+ ergo(i == page_count - 1, poff == 0)),
+ "i: %d/%d pg: %p off: %llu, count: %u\n",
+ i, page_count, pg, pg->off, pg->count);
+ LASSERTF(i == 0 || pg->off > pg_prev->off,
+ "i %d p_c %u pg %p [pri %lu ind %lu] off %llu prev_pg %p [pri %lu ind %lu] off %llu\n",
+ i, page_count,
+ pg->pg, page_private(pg->pg), pg->pg->index, pg->off,
+ pg_prev->pg, page_private(pg_prev->pg),
+ pg_prev->pg->index, pg_prev->off);
+ LASSERT((pga[0]->flag & OBD_BRW_SRVLOCK) ==
+ (pg->flag & OBD_BRW_SRVLOCK));
+
+ ptlrpc_prep_bulk_page_pin(desc, pg->pg, poff, pg->count);
+ requested_nob += pg->count;
+
+ if (i > 0 && can_merge_pages(pg_prev, pg)) {
+ niobuf--;
+ niobuf->len += pg->count;
+ } else {
+ niobuf->offset = pg->off;
+ niobuf->len = pg->count;
+ niobuf->flags = pg->flag;
+ }
+ pg_prev = pg;
+ }
+
+ LASSERTF((void *)(niobuf - niocount) ==
+ req_capsule_client_get(&req->rq_pill, &RMF_NIOBUF_REMOTE),
+ "want %p - real %p\n", req_capsule_client_get(&req->rq_pill,
+ &RMF_NIOBUF_REMOTE), (void *)(niobuf - niocount));
+
+ osc_announce_cached(cli, &body->oa, opc == OST_WRITE ? requested_nob:0);
+ if (resend) {
+ if ((body->oa.o_valid & OBD_MD_FLFLAGS) == 0) {
+ body->oa.o_valid |= OBD_MD_FLFLAGS;
+ body->oa.o_flags = 0;
+ }
+ body->oa.o_flags |= OBD_FL_RECOV_RESEND;
+ }
+
+ if (osc_should_shrink_grant(cli))
+ osc_shrink_grant_local(cli, &body->oa);
+
+ /* size[REQ_REC_OFF] still sizeof (*body) */
+ if (opc == OST_WRITE) {
+ if (cli->cl_checksum &&
+ !sptlrpc_flavor_has_bulk(&req->rq_flvr)) {
+ /* store cl_cksum_type in a local variable since
+ * it can be changed via lprocfs */
+ cksum_type_t cksum_type = cli->cl_cksum_type;
+
+ if ((body->oa.o_valid & OBD_MD_FLFLAGS) == 0) {
+ oa->o_flags &= OBD_FL_LOCAL_MASK;
+ body->oa.o_flags = 0;
+ }
+ body->oa.o_flags |= cksum_type_pack(cksum_type);
+ body->oa.o_valid |= OBD_MD_FLCKSUM | OBD_MD_FLFLAGS;
+ body->oa.o_cksum = osc_checksum_bulk(requested_nob,
+ page_count, pga,
+ OST_WRITE,
+ cksum_type);
+ CDEBUG(D_PAGE, "checksum at write origin: %x\n",
+ body->oa.o_cksum);
+ /* save this in 'oa', too, for later checking */
+ oa->o_valid |= OBD_MD_FLCKSUM | OBD_MD_FLFLAGS;
+ oa->o_flags |= cksum_type_pack(cksum_type);
+ } else {
+ /* clear out the checksum flag, in case this is a
+ * resend but cl_checksum is no longer set. b=11238 */
+ oa->o_valid &= ~OBD_MD_FLCKSUM;
+ }
+ oa->o_cksum = body->oa.o_cksum;
+ /* 1 RC per niobuf */
+ req_capsule_set_size(pill, &RMF_RCS, RCL_SERVER,
+ sizeof(__u32) * niocount);
+ } else {
+ if (cli->cl_checksum &&
+ !sptlrpc_flavor_has_bulk(&req->rq_flvr)) {
+ if ((body->oa.o_valid & OBD_MD_FLFLAGS) == 0)
+ body->oa.o_flags = 0;
+ body->oa.o_flags |= cksum_type_pack(cli->cl_cksum_type);
+ body->oa.o_valid |= OBD_MD_FLCKSUM | OBD_MD_FLFLAGS;
+ }
+ }
+ ptlrpc_request_set_replen(req);
+
+ CLASSERT(sizeof(*aa) <= sizeof(req->rq_async_args));
+ aa = ptlrpc_req_async_args(req);
+ aa->aa_oa = oa;
+ aa->aa_requested_nob = requested_nob;
+ aa->aa_nio_count = niocount;
+ aa->aa_page_count = page_count;
+ aa->aa_resends = 0;
+ aa->aa_ppga = pga;
+ aa->aa_cli = cli;
+ INIT_LIST_HEAD(&aa->aa_oaps);
+ if (ocapa && reserve)
+ aa->aa_ocapa = capa_get(ocapa);
+
+ *reqp = req;
+ return 0;
+
+ out:
+ ptlrpc_req_finished(req);
+ return rc;
+}
+
+static int check_write_checksum(struct obdo *oa, const lnet_process_id_t *peer,
+ __u32 client_cksum, __u32 server_cksum, int nob,
+ u32 page_count, struct brw_page **pga,
+ cksum_type_t client_cksum_type)
+{
+ __u32 new_cksum;
+ char *msg;
+ cksum_type_t cksum_type;
+
+ if (server_cksum == client_cksum) {
+ CDEBUG(D_PAGE, "checksum %x confirmed\n", client_cksum);
+ return 0;
+ }
+
+ cksum_type = cksum_type_unpack(oa->o_valid & OBD_MD_FLFLAGS ?
+ oa->o_flags : 0);
+ new_cksum = osc_checksum_bulk(nob, page_count, pga, OST_WRITE,
+ cksum_type);
+
+ if (cksum_type != client_cksum_type)
+ msg = "the server did not use the checksum type specified in the original request - likely a protocol problem"
+ ;
+ else if (new_cksum == server_cksum)
+ msg = "changed on the client after we checksummed it - likely false positive due to mmap IO (bug 11742)"
+ ;
+ else if (new_cksum == client_cksum)
+ msg = "changed in transit before arrival at OST";
+ else
+ msg = "changed in transit AND doesn't match the original - likely false positive due to mmap IO (bug 11742)"
+ ;
+
+ LCONSOLE_ERROR_MSG(0x132, "BAD WRITE CHECKSUM: %s: from %s inode "DFID
+ " object "DOSTID" extent [%llu-%llu]\n",
+ msg, libcfs_nid2str(peer->nid),
+ oa->o_valid & OBD_MD_FLFID ? oa->o_parent_seq : (__u64)0,
+ oa->o_valid & OBD_MD_FLFID ? oa->o_parent_oid : 0,
+ oa->o_valid & OBD_MD_FLFID ? oa->o_parent_ver : 0,
+ POSTID(&oa->o_oi), pga[0]->off,
+ pga[page_count-1]->off + pga[page_count-1]->count - 1);
+ CERROR("original client csum %x (type %x), server csum %x (type %x), client csum now %x\n",
+ client_cksum, client_cksum_type,
+ server_cksum, cksum_type, new_cksum);
+ return 1;
+}
+
+/* Note rc enters this function as number of bytes transferred */
+static int osc_brw_fini_request(struct ptlrpc_request *req, int rc)
+{
+ struct osc_brw_async_args *aa = (void *)&req->rq_async_args;
+ const lnet_process_id_t *peer =
+ &req->rq_import->imp_connection->c_peer;
+ struct client_obd *cli = aa->aa_cli;
+ struct ost_body *body;
+ __u32 client_cksum = 0;
+
+ if (rc < 0 && rc != -EDQUOT) {
+ DEBUG_REQ(D_INFO, req, "Failed request with rc = %d\n", rc);
+ return rc;
+ }
+
+ LASSERTF(req->rq_repmsg != NULL, "rc = %d\n", rc);
+ body = req_capsule_server_get(&req->rq_pill, &RMF_OST_BODY);
+ if (body == NULL) {
+ DEBUG_REQ(D_INFO, req, "Can't unpack body\n");
+ return -EPROTO;
+ }
+
+ /* set/clear over quota flag for a uid/gid */
+ if (lustre_msg_get_opc(req->rq_reqmsg) == OST_WRITE &&
+ body->oa.o_valid & (OBD_MD_FLUSRQUOTA | OBD_MD_FLGRPQUOTA)) {
+ unsigned int qid[MAXQUOTAS] = { body->oa.o_uid, body->oa.o_gid };
+
+ CDEBUG(D_QUOTA, "setdq for [%u %u] with valid %#llx, flags %x\n",
+ body->oa.o_uid, body->oa.o_gid, body->oa.o_valid,
+ body->oa.o_flags);
+ osc_quota_setdq(cli, qid, body->oa.o_valid, body->oa.o_flags);
+ }
+
+ osc_update_grant(cli, body);
+
+ if (rc < 0)
+ return rc;
+
+ if (aa->aa_oa->o_valid & OBD_MD_FLCKSUM)
+ client_cksum = aa->aa_oa->o_cksum; /* save for later */
+
+ if (lustre_msg_get_opc(req->rq_reqmsg) == OST_WRITE) {
+ if (rc > 0) {
+ CERROR("Unexpected +ve rc %d\n", rc);
+ return -EPROTO;
+ }
+ LASSERT(req->rq_bulk->bd_nob == aa->aa_requested_nob);
+
+ if (sptlrpc_cli_unwrap_bulk_write(req, req->rq_bulk))
+ return -EAGAIN;
+
+ if ((aa->aa_oa->o_valid & OBD_MD_FLCKSUM) && client_cksum &&
+ check_write_checksum(&body->oa, peer, client_cksum,
+ body->oa.o_cksum, aa->aa_requested_nob,
+ aa->aa_page_count, aa->aa_ppga,
+ cksum_type_unpack(aa->aa_oa->o_flags)))
+ return -EAGAIN;
+
+ rc = check_write_rcs(req, aa->aa_requested_nob,
+ aa->aa_nio_count,
+ aa->aa_page_count, aa->aa_ppga);
+ goto out;
+ }
+
+ /* The rest of this function executes only for OST_READs */
+
+ /* if unwrap_bulk failed, return -EAGAIN to retry */
+ rc = sptlrpc_cli_unwrap_bulk_read(req, req->rq_bulk, rc);
+ if (rc < 0) {
+ rc = -EAGAIN;
+ goto out;
+ }
+
+ if (rc > aa->aa_requested_nob) {
+ CERROR("Unexpected rc %d (%d requested)\n", rc,
+ aa->aa_requested_nob);
+ return -EPROTO;
+ }
+
+ if (rc != req->rq_bulk->bd_nob_transferred) {
+ CERROR ("Unexpected rc %d (%d transferred)\n",
+ rc, req->rq_bulk->bd_nob_transferred);
+ return -EPROTO;
+ }
+
+ if (rc < aa->aa_requested_nob)
+ handle_short_read(rc, aa->aa_page_count, aa->aa_ppga);
+
+ if (body->oa.o_valid & OBD_MD_FLCKSUM) {
+ static int cksum_counter;
+ __u32 server_cksum = body->oa.o_cksum;
+ char *via;
+ char *router;
+ cksum_type_t cksum_type;
+
+ cksum_type = cksum_type_unpack(body->oa.o_valid &OBD_MD_FLFLAGS?
+ body->oa.o_flags : 0);
+ client_cksum = osc_checksum_bulk(rc, aa->aa_page_count,
+ aa->aa_ppga, OST_READ,
+ cksum_type);
+
+ if (peer->nid == req->rq_bulk->bd_sender) {
+ via = router = "";
+ } else {
+ via = " via ";
+ router = libcfs_nid2str(req->rq_bulk->bd_sender);
+ }
+
+ if (server_cksum != client_cksum) {
+ LCONSOLE_ERROR_MSG(0x133, "%s: BAD READ CHECKSUM: from %s%s%s inode " DFID " object " DOSTID " extent [%llu-%llu]\n",
+ req->rq_import->imp_obd->obd_name,
+ libcfs_nid2str(peer->nid),
+ via, router,
+ body->oa.o_valid & OBD_MD_FLFID ?
+ body->oa.o_parent_seq : (__u64)0,
+ body->oa.o_valid & OBD_MD_FLFID ?
+ body->oa.o_parent_oid : 0,
+ body->oa.o_valid & OBD_MD_FLFID ?
+ body->oa.o_parent_ver : 0,
+ POSTID(&body->oa.o_oi),
+ aa->aa_ppga[0]->off,
+ aa->aa_ppga[aa->aa_page_count-1]->off +
+ aa->aa_ppga[aa->aa_page_count-1]->count -
+ 1);
+ CERROR("client %x, server %x, cksum_type %x\n",
+ client_cksum, server_cksum, cksum_type);
+ cksum_counter = 0;
+ aa->aa_oa->o_cksum = client_cksum;
+ rc = -EAGAIN;
+ } else {
+ cksum_counter++;
+ CDEBUG(D_PAGE, "checksum %x confirmed\n", client_cksum);
+ rc = 0;
+ }
+ } else if (unlikely(client_cksum)) {
+ static int cksum_missed;
+
+ cksum_missed++;
+ if ((cksum_missed & (-cksum_missed)) == cksum_missed)
+ CERROR("Checksum %u requested from %s but not sent\n",
+ cksum_missed, libcfs_nid2str(peer->nid));
+ } else {
+ rc = 0;
+ }
+out:
+ if (rc >= 0)
+ lustre_get_wire_obdo(&req->rq_import->imp_connect_data,
+ aa->aa_oa, &body->oa);
+
+ return rc;
+}
+
+static int osc_brw_redo_request(struct ptlrpc_request *request,
+ struct osc_brw_async_args *aa, int rc)
+{
+ struct ptlrpc_request *new_req;
+ struct osc_brw_async_args *new_aa;
+ struct osc_async_page *oap;
+
+ DEBUG_REQ(rc == -EINPROGRESS ? D_RPCTRACE : D_ERROR, request,
+ "redo for recoverable error %d", rc);
+
+ rc = osc_brw_prep_request(lustre_msg_get_opc(request->rq_reqmsg) ==
+ OST_WRITE ? OBD_BRW_WRITE :OBD_BRW_READ,
+ aa->aa_cli, aa->aa_oa,
+ NULL /* lsm unused by osc currently */,
+ aa->aa_page_count, aa->aa_ppga,
+ &new_req, aa->aa_ocapa, 0, 1);
+ if (rc)
+ return rc;
+
+ list_for_each_entry(oap, &aa->aa_oaps, oap_rpc_item) {
+ if (oap->oap_request != NULL) {
+ LASSERTF(request == oap->oap_request,
+ "request %p != oap_request %p\n",
+ request, oap->oap_request);
+ if (oap->oap_interrupted) {
+ ptlrpc_req_finished(new_req);
+ return -EINTR;
+ }
+ }
+ }
+ /* New request takes over pga and oaps from old request.
+ * Note that copying a list_head doesn't work, need to move it... */
+ aa->aa_resends++;
+ new_req->rq_interpret_reply = request->rq_interpret_reply;
+ new_req->rq_async_args = request->rq_async_args;
+ /* cap resend delay to the current request timeout, this is similar to
+ * what ptlrpc does (see after_reply()) */
+ if (aa->aa_resends > new_req->rq_timeout)
+ new_req->rq_sent = get_seconds() + new_req->rq_timeout;
+ else
+ new_req->rq_sent = get_seconds() + aa->aa_resends;
+ new_req->rq_generation_set = 1;
+ new_req->rq_import_generation = request->rq_import_generation;
+
+ new_aa = ptlrpc_req_async_args(new_req);
+
+ INIT_LIST_HEAD(&new_aa->aa_oaps);
+ list_splice_init(&aa->aa_oaps, &new_aa->aa_oaps);
+ INIT_LIST_HEAD(&new_aa->aa_exts);
+ list_splice_init(&aa->aa_exts, &new_aa->aa_exts);
+ new_aa->aa_resends = aa->aa_resends;
+
+ list_for_each_entry(oap, &new_aa->aa_oaps, oap_rpc_item) {
+ if (oap->oap_request) {
+ ptlrpc_req_finished(oap->oap_request);
+ oap->oap_request = ptlrpc_request_addref(new_req);
+ }
+ }
+
+ new_aa->aa_ocapa = aa->aa_ocapa;
+ aa->aa_ocapa = NULL;
+
+ /* XXX: This code will run into problem if we're going to support
+ * to add a series of BRW RPCs into a self-defined ptlrpc_request_set
+ * and wait for all of them to be finished. We should inherit request
+ * set from old request. */
+ ptlrpcd_add_req(new_req, PDL_POLICY_SAME, -1);
+
+ DEBUG_REQ(D_INFO, new_req, "new request");
+ return 0;
+}
+
+/*
+ * ugh, we want disk allocation on the target to happen in offset order. we'll
+ * follow sedgewicks advice and stick to the dead simple shellsort -- it'll do
+ * fine for our small page arrays and doesn't require allocation. its an
+ * insertion sort that swaps elements that are strides apart, shrinking the
+ * stride down until its '1' and the array is sorted.
+ */
+static void sort_brw_pages(struct brw_page **array, int num)
+{
+ int stride, i, j;
+ struct brw_page *tmp;
+
+ if (num == 1)
+ return;
+ for (stride = 1; stride < num ; stride = (stride * 3) + 1)
+ ;
+
+ do {
+ stride /= 3;
+ for (i = stride ; i < num ; i++) {
+ tmp = array[i];
+ j = i;
+ while (j >= stride && array[j - stride]->off > tmp->off) {
+ array[j] = array[j - stride];
+ j -= stride;
+ }
+ array[j] = tmp;
+ }
+ } while (stride > 1);
+}
+
+static void osc_release_ppga(struct brw_page **ppga, u32 count)
+{
+ LASSERT(ppga != NULL);
+ OBD_FREE(ppga, sizeof(*ppga) * count);
+}
+
+static int brw_interpret(const struct lu_env *env,
+ struct ptlrpc_request *req, void *data, int rc)
+{
+ struct osc_brw_async_args *aa = data;
+ struct osc_extent *ext;
+ struct osc_extent *tmp;
+ struct cl_object *obj = NULL;
+ struct client_obd *cli = aa->aa_cli;
+
+ rc = osc_brw_fini_request(req, rc);
+ CDEBUG(D_INODE, "request %p aa %p rc %d\n", req, aa, rc);
+ /* When server return -EINPROGRESS, client should always retry
+ * regardless of the number of times the bulk was resent already. */
+ if (osc_recoverable_error(rc)) {
+ if (req->rq_import_generation !=
+ req->rq_import->imp_generation) {
+ CDEBUG(D_HA, "%s: resend cross eviction for object: " DOSTID ", rc = %d.\n",
+ req->rq_import->imp_obd->obd_name,
+ POSTID(&aa->aa_oa->o_oi), rc);
+ } else if (rc == -EINPROGRESS ||
+ client_should_resend(aa->aa_resends, aa->aa_cli)) {
+ rc = osc_brw_redo_request(req, aa, rc);
+ } else {
+ CERROR("%s: too many resent retries for object: %llu:%llu, rc = %d.\n",
+ req->rq_import->imp_obd->obd_name,
+ POSTID(&aa->aa_oa->o_oi), rc);
+ }
+
+ if (rc == 0)
+ return 0;
+ else if (rc == -EAGAIN || rc == -EINPROGRESS)
+ rc = -EIO;
+ }
+
+ if (aa->aa_ocapa) {
+ capa_put(aa->aa_ocapa);
+ aa->aa_ocapa = NULL;
+ }
+
+ list_for_each_entry_safe(ext, tmp, &aa->aa_exts, oe_link) {
+ if (obj == NULL && rc == 0) {
+ obj = osc2cl(ext->oe_obj);
+ cl_object_get(obj);
+ }
+
+ list_del_init(&ext->oe_link);
+ osc_extent_finish(env, ext, 1, rc);
+ }
+ LASSERT(list_empty(&aa->aa_exts));
+ LASSERT(list_empty(&aa->aa_oaps));
+
+ if (obj != NULL) {
+ struct obdo *oa = aa->aa_oa;
+ struct cl_attr *attr = &osc_env_info(env)->oti_attr;
+ unsigned long valid = 0;
+
+ LASSERT(rc == 0);
+ if (oa->o_valid & OBD_MD_FLBLOCKS) {
+ attr->cat_blocks = oa->o_blocks;
+ valid |= CAT_BLOCKS;
+ }
+ if (oa->o_valid & OBD_MD_FLMTIME) {
+ attr->cat_mtime = oa->o_mtime;
+ valid |= CAT_MTIME;
+ }
+ if (oa->o_valid & OBD_MD_FLATIME) {
+ attr->cat_atime = oa->o_atime;
+ valid |= CAT_ATIME;
+ }
+ if (oa->o_valid & OBD_MD_FLCTIME) {
+ attr->cat_ctime = oa->o_ctime;
+ valid |= CAT_CTIME;
+ }
+ if (valid != 0) {
+ cl_object_attr_lock(obj);
+ cl_object_attr_set(env, obj, attr, valid);
+ cl_object_attr_unlock(obj);
+ }
+ cl_object_put(env, obj);
+ }
+ OBDO_FREE(aa->aa_oa);
+
+ cl_req_completion(env, aa->aa_clerq, rc < 0 ? rc :
+ req->rq_bulk->bd_nob_transferred);
+ osc_release_ppga(aa->aa_ppga, aa->aa_page_count);
+ ptlrpc_lprocfs_brw(req, req->rq_bulk->bd_nob_transferred);
+
+ client_obd_list_lock(&cli->cl_loi_list_lock);
+ /* We need to decrement before osc_ap_completion->osc_wake_cache_waiters
+ * is called so we know whether to go to sync BRWs or wait for more
+ * RPCs to complete */
+ if (lustre_msg_get_opc(req->rq_reqmsg) == OST_WRITE)
+ cli->cl_w_in_flight--;
+ else
+ cli->cl_r_in_flight--;
+ osc_wake_cache_waiters(cli);
+ client_obd_list_unlock(&cli->cl_loi_list_lock);
+
+ osc_io_unplug(env, cli, NULL, PDL_POLICY_SAME);
+ return rc;
+}
+
+/**
+ * Build an RPC by the list of extent @ext_list. The caller must ensure
+ * that the total pages in this list are NOT over max pages per RPC.
+ * Extents in the list must be in OES_RPC state.
+ */
+int osc_build_rpc(const struct lu_env *env, struct client_obd *cli,
+ struct list_head *ext_list, int cmd, pdl_policy_t pol)
+{
+ struct ptlrpc_request *req = NULL;
+ struct osc_extent *ext;
+ struct brw_page **pga = NULL;
+ struct osc_brw_async_args *aa = NULL;
+ struct obdo *oa = NULL;
+ struct osc_async_page *oap;
+ struct osc_async_page *tmp;
+ struct cl_req *clerq = NULL;
+ enum cl_req_type crt = (cmd & OBD_BRW_WRITE) ? CRT_WRITE :
+ CRT_READ;
+ struct ldlm_lock *lock = NULL;
+ struct cl_req_attr *crattr = NULL;
+ u64 starting_offset = OBD_OBJECT_EOF;
+ u64 ending_offset = 0;
+ int mpflag = 0;
+ int mem_tight = 0;
+ int page_count = 0;
+ int i;
+ int rc;
+ struct ost_body *body;
+ LIST_HEAD(rpc_list);
+
+ LASSERT(!list_empty(ext_list));
+
+ /* add pages into rpc_list to build BRW rpc */
+ list_for_each_entry(ext, ext_list, oe_link) {
+ LASSERT(ext->oe_state == OES_RPC);
+ mem_tight |= ext->oe_memalloc;
+ list_for_each_entry(oap, &ext->oe_pages, oap_pending_item) {
+ ++page_count;
+ list_add_tail(&oap->oap_rpc_item, &rpc_list);
+ if (starting_offset > oap->oap_obj_off)
+ starting_offset = oap->oap_obj_off;
+ else
+ LASSERT(oap->oap_page_off == 0);
+ if (ending_offset < oap->oap_obj_off + oap->oap_count)
+ ending_offset = oap->oap_obj_off +
+ oap->oap_count;
+ else
+ LASSERT(oap->oap_page_off + oap->oap_count ==
+ PAGE_CACHE_SIZE);
+ }
+ }
+
+ if (mem_tight)
+ mpflag = cfs_memory_pressure_get_and_set();
+
+ OBD_ALLOC(crattr, sizeof(*crattr));
+ if (crattr == NULL) {
+ rc = -ENOMEM;
+ goto out;
+ }
+
+ OBD_ALLOC(pga, sizeof(*pga) * page_count);
+ if (pga == NULL) {
+ rc = -ENOMEM;
+ goto out;
+ }
+
+ OBDO_ALLOC(oa);
+ if (oa == NULL) {
+ rc = -ENOMEM;
+ goto out;
+ }
+
+ i = 0;
+ list_for_each_entry(oap, &rpc_list, oap_rpc_item) {
+ struct cl_page *page = oap2cl_page(oap);
+ if (clerq == NULL) {
+ clerq = cl_req_alloc(env, page, crt,
+ 1 /* only 1-object rpcs for now */);
+ if (IS_ERR(clerq)) {
+ rc = PTR_ERR(clerq);
+ goto out;
+ }
+ lock = oap->oap_ldlm_lock;
+ }
+ if (mem_tight)
+ oap->oap_brw_flags |= OBD_BRW_MEMALLOC;
+ pga[i] = &oap->oap_brw_page;
+ pga[i]->off = oap->oap_obj_off + oap->oap_page_off;
+ CDEBUG(0, "put page %p index %lu oap %p flg %x to pga\n",
+ pga[i]->pg, page_index(oap->oap_page), oap,
+ pga[i]->flag);
+ i++;
+ cl_req_page_add(env, clerq, page);
+ }
+
+ /* always get the data for the obdo for the rpc */
+ LASSERT(clerq != NULL);
+ crattr->cra_oa = oa;
+ cl_req_attr_set(env, clerq, crattr, ~0ULL);
+ if (lock) {
+ oa->o_handle = lock->l_remote_handle;
+ oa->o_valid |= OBD_MD_FLHANDLE;
+ }
+
+ rc = cl_req_prep(env, clerq);
+ if (rc != 0) {
+ CERROR("cl_req_prep failed: %d\n", rc);
+ goto out;
+ }
+
+ sort_brw_pages(pga, page_count);
+ rc = osc_brw_prep_request(cmd, cli, oa, NULL, page_count,
+ pga, &req, crattr->cra_capa, 1, 0);
+ if (rc != 0) {
+ CERROR("prep_req failed: %d\n", rc);
+ goto out;
+ }
+
+ req->rq_interpret_reply = brw_interpret;
+
+ if (mem_tight != 0)
+ req->rq_memalloc = 1;
+
+ /* Need to update the timestamps after the request is built in case
+ * we race with setattr (locally or in queue at OST). If OST gets
+ * later setattr before earlier BRW (as determined by the request xid),
+ * the OST will not use BRW timestamps. Sadly, there is no obvious
+ * way to do this in a single call. bug 10150 */
+ body = req_capsule_client_get(&req->rq_pill, &RMF_OST_BODY);
+ crattr->cra_oa = &body->oa;
+ cl_req_attr_set(env, clerq, crattr,
+ OBD_MD_FLMTIME|OBD_MD_FLCTIME|OBD_MD_FLATIME);
+
+ lustre_msg_set_jobid(req->rq_reqmsg, crattr->cra_jobid);
+
+ CLASSERT(sizeof(*aa) <= sizeof(req->rq_async_args));
+ aa = ptlrpc_req_async_args(req);
+ INIT_LIST_HEAD(&aa->aa_oaps);
+ list_splice_init(&rpc_list, &aa->aa_oaps);
+ INIT_LIST_HEAD(&aa->aa_exts);
+ list_splice_init(ext_list, &aa->aa_exts);
+ aa->aa_clerq = clerq;
+
+ /* queued sync pages can be torn down while the pages
+ * were between the pending list and the rpc */
+ tmp = NULL;
+ list_for_each_entry(oap, &aa->aa_oaps, oap_rpc_item) {
+ /* only one oap gets a request reference */
+ if (tmp == NULL)
+ tmp = oap;
+ if (oap->oap_interrupted && !req->rq_intr) {
+ CDEBUG(D_INODE, "oap %p in req %p interrupted\n",
+ oap, req);
+ ptlrpc_mark_interrupted(req);
+ }
+ }
+ if (tmp != NULL)
+ tmp->oap_request = ptlrpc_request_addref(req);
+
+ client_obd_list_lock(&cli->cl_loi_list_lock);
+ starting_offset >>= PAGE_CACHE_SHIFT;
+ if (cmd == OBD_BRW_READ) {
+ cli->cl_r_in_flight++;
+ lprocfs_oh_tally_log2(&cli->cl_read_page_hist, page_count);
+ lprocfs_oh_tally(&cli->cl_read_rpc_hist, cli->cl_r_in_flight);
+ lprocfs_oh_tally_log2(&cli->cl_read_offset_hist,
+ starting_offset + 1);
+ } else {
+ cli->cl_w_in_flight++;
+ lprocfs_oh_tally_log2(&cli->cl_write_page_hist, page_count);
+ lprocfs_oh_tally(&cli->cl_write_rpc_hist, cli->cl_w_in_flight);
+ lprocfs_oh_tally_log2(&cli->cl_write_offset_hist,
+ starting_offset + 1);
+ }
+ client_obd_list_unlock(&cli->cl_loi_list_lock);
+
+ DEBUG_REQ(D_INODE, req, "%d pages, aa %p. now %dr/%dw in flight",
+ page_count, aa, cli->cl_r_in_flight,
+ cli->cl_w_in_flight);
+
+ /* XXX: Maybe the caller can check the RPC bulk descriptor to
+ * see which CPU/NUMA node the majority of pages were allocated
+ * on, and try to assign the async RPC to the CPU core
+ * (PDL_POLICY_PREFERRED) to reduce cross-CPU memory traffic.
+ *
+ * But on the other hand, we expect that multiple ptlrpcd
+ * threads and the initial write sponsor can run in parallel,
+ * especially when data checksum is enabled, which is CPU-bound
+ * operation and single ptlrpcd thread cannot process in time.
+ * So more ptlrpcd threads sharing BRW load
+ * (with PDL_POLICY_ROUND) seems better.
+ */
+ ptlrpcd_add_req(req, pol, -1);
+ rc = 0;
+
+out:
+ if (mem_tight != 0)
+ cfs_memory_pressure_restore(mpflag);
+
+ if (crattr != NULL) {
+ capa_put(crattr->cra_capa);
+ OBD_FREE(crattr, sizeof(*crattr));
+ }
+
+ if (rc != 0) {
+ LASSERT(req == NULL);
+
+ if (oa)
+ OBDO_FREE(oa);
+ if (pga)
+ OBD_FREE(pga, sizeof(*pga) * page_count);
+ /* this should happen rarely and is pretty bad, it makes the
+ * pending list not follow the dirty order */
+ while (!list_empty(ext_list)) {
+ ext = list_entry(ext_list->next, struct osc_extent,
+ oe_link);
+ list_del_init(&ext->oe_link);
+ osc_extent_finish(env, ext, 0, rc);
+ }
+ if (clerq && !IS_ERR(clerq))
+ cl_req_completion(env, clerq, rc);
+ }
+ return rc;
+}
+
+static int osc_set_lock_data_with_check(struct ldlm_lock *lock,
+ struct ldlm_enqueue_info *einfo)
+{
+ void *data = einfo->ei_cbdata;
+ int set = 0;
+
+ LASSERT(lock != NULL);
+ LASSERT(lock->l_blocking_ast == einfo->ei_cb_bl);
+ LASSERT(lock->l_resource->lr_type == einfo->ei_type);
+ LASSERT(lock->l_completion_ast == einfo->ei_cb_cp);
+ LASSERT(lock->l_glimpse_ast == einfo->ei_cb_gl);
+
+ lock_res_and_lock(lock);
+ spin_lock(&osc_ast_guard);
+
+ if (lock->l_ast_data == NULL)
+ lock->l_ast_data = data;
+ if (lock->l_ast_data == data)
+ set = 1;
+
+ spin_unlock(&osc_ast_guard);
+ unlock_res_and_lock(lock);
+
+ return set;
+}
+
+static int osc_set_data_with_check(struct lustre_handle *lockh,
+ struct ldlm_enqueue_info *einfo)
+{
+ struct ldlm_lock *lock = ldlm_handle2lock(lockh);
+ int set = 0;
+
+ if (lock != NULL) {
+ set = osc_set_lock_data_with_check(lock, einfo);
+ LDLM_LOCK_PUT(lock);
+ } else
+ CERROR("lockh %p, data %p - client evicted?\n",
+ lockh, einfo->ei_cbdata);
+ return set;
+}
+
+/* find any ldlm lock of the inode in osc
+ * return 0 not find
+ * 1 find one
+ * < 0 error */
+static int osc_find_cbdata(struct obd_export *exp, struct lov_stripe_md *lsm,
+ ldlm_iterator_t replace, void *data)
+{
+ struct ldlm_res_id res_id;
+ struct obd_device *obd = class_exp2obd(exp);
+ int rc = 0;
+
+ ostid_build_res_name(&lsm->lsm_oi, &res_id);
+ rc = ldlm_resource_iterate(obd->obd_namespace, &res_id, replace, data);
+ if (rc == LDLM_ITER_STOP)
+ return 1;
+ if (rc == LDLM_ITER_CONTINUE)
+ return 0;
+ return rc;
+}
+
+static int osc_enqueue_fini(struct ptlrpc_request *req, struct ost_lvb *lvb,
+ obd_enqueue_update_f upcall, void *cookie,
+ __u64 *flags, int agl, int rc)
+{
+ int intent = *flags & LDLM_FL_HAS_INTENT;
+
+ if (intent) {
+ /* The request was created before ldlm_cli_enqueue call. */
+ if (rc == ELDLM_LOCK_ABORTED) {
+ struct ldlm_reply *rep;
+ rep = req_capsule_server_get(&req->rq_pill,
+ &RMF_DLM_REP);
+
+ LASSERT(rep != NULL);
+ rep->lock_policy_res1 =
+ ptlrpc_status_ntoh(rep->lock_policy_res1);
+ if (rep->lock_policy_res1)
+ rc = rep->lock_policy_res1;
+ }
+ }
+
+ if ((intent != 0 && rc == ELDLM_LOCK_ABORTED && agl == 0) ||
+ (rc == 0)) {
+ *flags |= LDLM_FL_LVB_READY;
+ CDEBUG(D_INODE, "got kms %llu blocks %llu mtime %llu\n",
+ lvb->lvb_size, lvb->lvb_blocks, lvb->lvb_mtime);
+ }
+
+ /* Call the update callback. */
+ rc = (*upcall)(cookie, rc);
+ return rc;
+}
+
+static int osc_enqueue_interpret(const struct lu_env *env,
+ struct ptlrpc_request *req,
+ struct osc_enqueue_args *aa, int rc)
+{
+ struct ldlm_lock *lock;
+ struct lustre_handle handle;
+ __u32 mode;
+ struct ost_lvb *lvb;
+ __u32 lvb_len;
+ __u64 *flags = aa->oa_flags;
+
+ /* Make a local copy of a lock handle and a mode, because aa->oa_*
+ * might be freed anytime after lock upcall has been called. */
+ lustre_handle_copy(&handle, aa->oa_lockh);
+ mode = aa->oa_ei->ei_mode;
+
+ /* ldlm_cli_enqueue is holding a reference on the lock, so it must
+ * be valid. */
+ lock = ldlm_handle2lock(&handle);
+
+ /* Take an additional reference so that a blocking AST that
+ * ldlm_cli_enqueue_fini() might post for a failed lock, is guaranteed
+ * to arrive after an upcall has been executed by
+ * osc_enqueue_fini(). */
+ ldlm_lock_addref(&handle, mode);
+
+ /* Let CP AST to grant the lock first. */
+ OBD_FAIL_TIMEOUT(OBD_FAIL_OSC_CP_ENQ_RACE, 1);
+
+ if (aa->oa_agl && rc == ELDLM_LOCK_ABORTED) {
+ lvb = NULL;
+ lvb_len = 0;
+ } else {
+ lvb = aa->oa_lvb;
+ lvb_len = sizeof(*aa->oa_lvb);
+ }
+
+ /* Complete obtaining the lock procedure. */
+ rc = ldlm_cli_enqueue_fini(aa->oa_exp, req, aa->oa_ei->ei_type, 1,
+ mode, flags, lvb, lvb_len, &handle, rc);
+ /* Complete osc stuff. */
+ rc = osc_enqueue_fini(req, aa->oa_lvb, aa->oa_upcall, aa->oa_cookie,
+ flags, aa->oa_agl, rc);
+
+ OBD_FAIL_TIMEOUT(OBD_FAIL_OSC_CP_CANCEL_RACE, 10);
+
+ /* Release the lock for async request. */
+ if (lustre_handle_is_used(&handle) && rc == ELDLM_OK)
+ /*
+ * Releases a reference taken by ldlm_cli_enqueue(), if it is
+ * not already released by
+ * ldlm_cli_enqueue_fini()->failed_lock_cleanup()
+ */
+ ldlm_lock_decref(&handle, mode);
+
+ LASSERTF(lock != NULL, "lockh %p, req %p, aa %p - client evicted?\n",
+ aa->oa_lockh, req, aa);
+ ldlm_lock_decref(&handle, mode);
+ LDLM_LOCK_PUT(lock);
+ return rc;
+}
+
+struct ptlrpc_request_set *PTLRPCD_SET = (void *)1;
+
+/* When enqueuing asynchronously, locks are not ordered, we can obtain a lock
+ * from the 2nd OSC before a lock from the 1st one. This does not deadlock with
+ * other synchronous requests, however keeping some locks and trying to obtain
+ * others may take a considerable amount of time in a case of ost failure; and
+ * when other sync requests do not get released lock from a client, the client
+ * is excluded from the cluster -- such scenarious make the life difficult, so
+ * release locks just after they are obtained. */
+int osc_enqueue_base(struct obd_export *exp, struct ldlm_res_id *res_id,
+ __u64 *flags, ldlm_policy_data_t *policy,
+ struct ost_lvb *lvb, int kms_valid,
+ obd_enqueue_update_f upcall, void *cookie,
+ struct ldlm_enqueue_info *einfo,
+ struct lustre_handle *lockh,
+ struct ptlrpc_request_set *rqset, int async, int agl)
+{
+ struct obd_device *obd = exp->exp_obd;
+ struct ptlrpc_request *req = NULL;
+ int intent = *flags & LDLM_FL_HAS_INTENT;
+ __u64 match_lvb = (agl != 0 ? 0 : LDLM_FL_LVB_READY);
+ ldlm_mode_t mode;
+ int rc;
+
+ /* Filesystem lock extents are extended to page boundaries so that
+ * dealing with the page cache is a little smoother. */
+ policy->l_extent.start -= policy->l_extent.start & ~CFS_PAGE_MASK;
+ policy->l_extent.end |= ~CFS_PAGE_MASK;
+
+ /*
+ * kms is not valid when either object is completely fresh (so that no
+ * locks are cached), or object was evicted. In the latter case cached
+ * lock cannot be used, because it would prime inode state with
+ * potentially stale LVB.
+ */
+ if (!kms_valid)
+ goto no_match;
+
+ /* Next, search for already existing extent locks that will cover us */
+ /* If we're trying to read, we also search for an existing PW lock. The
+ * VFS and page cache already protect us locally, so lots of readers/
+ * writers can share a single PW lock.
+ *
+ * There are problems with conversion deadlocks, so instead of
+ * converting a read lock to a write lock, we'll just enqueue a new
+ * one.
+ *
+ * At some point we should cancel the read lock instead of making them
+ * send us a blocking callback, but there are problems with canceling
+ * locks out from other users right now, too. */
+ mode = einfo->ei_mode;
+ if (einfo->ei_mode == LCK_PR)
+ mode |= LCK_PW;
+ mode = ldlm_lock_match(obd->obd_namespace, *flags | match_lvb, res_id,
+ einfo->ei_type, policy, mode, lockh, 0);
+ if (mode) {
+ struct ldlm_lock *matched = ldlm_handle2lock(lockh);
+
+ if ((agl != 0) && !(matched->l_flags & LDLM_FL_LVB_READY)) {
+ /* For AGL, if enqueue RPC is sent but the lock is not
+ * granted, then skip to process this strpe.
+ * Return -ECANCELED to tell the caller. */
+ ldlm_lock_decref(lockh, mode);
+ LDLM_LOCK_PUT(matched);
+ return -ECANCELED;
+ } else if (osc_set_lock_data_with_check(matched, einfo)) {
+ *flags |= LDLM_FL_LVB_READY;
+ /* addref the lock only if not async requests and PW
+ * lock is matched whereas we asked for PR. */
+ if (!rqset && einfo->ei_mode != mode)
+ ldlm_lock_addref(lockh, LCK_PR);
+ if (intent) {
+ /* I would like to be able to ASSERT here that
+ * rss <= kms, but I can't, for reasons which
+ * are explained in lov_enqueue() */
+ }
+
+ /* We already have a lock, and it's referenced.
+ *
+ * At this point, the cl_lock::cll_state is CLS_QUEUING,
+ * AGL upcall may change it to CLS_HELD directly. */
+ (*upcall)(cookie, ELDLM_OK);
+
+ if (einfo->ei_mode != mode)
+ ldlm_lock_decref(lockh, LCK_PW);
+ else if (rqset)
+ /* For async requests, decref the lock. */
+ ldlm_lock_decref(lockh, einfo->ei_mode);
+ LDLM_LOCK_PUT(matched);
+ return ELDLM_OK;
+ } else {
+ ldlm_lock_decref(lockh, mode);
+ LDLM_LOCK_PUT(matched);
+ }
+ }
+
+ no_match:
+ if (intent) {
+ LIST_HEAD(cancels);
+ req = ptlrpc_request_alloc(class_exp2cliimp(exp),
+ &RQF_LDLM_ENQUEUE_LVB);
+ if (req == NULL)
+ return -ENOMEM;
+
+ rc = ldlm_prep_enqueue_req(exp, req, &cancels, 0);
+ if (rc) {
+ ptlrpc_request_free(req);
+ return rc;
+ }
+
+ req_capsule_set_size(&req->rq_pill, &RMF_DLM_LVB, RCL_SERVER,
+ sizeof(*lvb));
+ ptlrpc_request_set_replen(req);
+ }
+
+ /* users of osc_enqueue() can pass this flag for ldlm_lock_match() */
+ *flags &= ~LDLM_FL_BLOCK_GRANTED;
+
+ rc = ldlm_cli_enqueue(exp, &req, einfo, res_id, policy, flags, lvb,
+ sizeof(*lvb), LVB_T_OST, lockh, async);
+ if (rqset) {
+ if (!rc) {
+ struct osc_enqueue_args *aa;
+ CLASSERT (sizeof(*aa) <= sizeof(req->rq_async_args));
+ aa = ptlrpc_req_async_args(req);
+ aa->oa_ei = einfo;
+ aa->oa_exp = exp;
+ aa->oa_flags = flags;
+ aa->oa_upcall = upcall;
+ aa->oa_cookie = cookie;
+ aa->oa_lvb = lvb;
+ aa->oa_lockh = lockh;
+ aa->oa_agl = !!agl;
+
+ req->rq_interpret_reply =
+ (ptlrpc_interpterer_t)osc_enqueue_interpret;
+ if (rqset == PTLRPCD_SET)
+ ptlrpcd_add_req(req, PDL_POLICY_ROUND, -1);
+ else
+ ptlrpc_set_add_req(rqset, req);
+ } else if (intent) {
+ ptlrpc_req_finished(req);
+ }
+ return rc;
+ }
+
+ rc = osc_enqueue_fini(req, lvb, upcall, cookie, flags, agl, rc);
+ if (intent)
+ ptlrpc_req_finished(req);
+
+ return rc;
+}
+
+int osc_match_base(struct obd_export *exp, struct ldlm_res_id *res_id,
+ __u32 type, ldlm_policy_data_t *policy, __u32 mode,
+ __u64 *flags, void *data, struct lustre_handle *lockh,
+ int unref)
+{
+ struct obd_device *obd = exp->exp_obd;
+ __u64 lflags = *flags;
+ ldlm_mode_t rc;
+
+ if (OBD_FAIL_CHECK(OBD_FAIL_OSC_MATCH))
+ return -EIO;
+
+ /* Filesystem lock extents are extended to page boundaries so that
+ * dealing with the page cache is a little smoother */
+ policy->l_extent.start -= policy->l_extent.start & ~CFS_PAGE_MASK;
+ policy->l_extent.end |= ~CFS_PAGE_MASK;
+
+ /* Next, search for already existing extent locks that will cover us */
+ /* If we're trying to read, we also search for an existing PW lock. The
+ * VFS and page cache already protect us locally, so lots of readers/
+ * writers can share a single PW lock. */
+ rc = mode;
+ if (mode == LCK_PR)
+ rc |= LCK_PW;
+ rc = ldlm_lock_match(obd->obd_namespace, lflags,
+ res_id, type, policy, rc, lockh, unref);
+ if (rc) {
+ if (data != NULL) {
+ if (!osc_set_data_with_check(lockh, data)) {
+ if (!(lflags & LDLM_FL_TEST_LOCK))
+ ldlm_lock_decref(lockh, rc);
+ return 0;
+ }
+ }
+ if (!(lflags & LDLM_FL_TEST_LOCK) && mode != rc) {
+ ldlm_lock_addref(lockh, LCK_PR);
+ ldlm_lock_decref(lockh, LCK_PW);
+ }
+ return rc;
+ }
+ return rc;
+}
+
+int osc_cancel_base(struct lustre_handle *lockh, __u32 mode)
+{
+ if (unlikely(mode == LCK_GROUP))
+ ldlm_lock_decref_and_cancel(lockh, mode);
+ else
+ ldlm_lock_decref(lockh, mode);
+
+ return 0;
+}
+
+static int osc_statfs_interpret(const struct lu_env *env,
+ struct ptlrpc_request *req,
+ struct osc_async_args *aa, int rc)
+{
+ struct obd_statfs *msfs;
+
+ if (rc == -EBADR)
+ /* The request has in fact never been sent
+ * due to issues at a higher level (LOV).
+ * Exit immediately since the caller is
+ * aware of the problem and takes care
+ * of the clean up */
+ return rc;
+
+ if ((rc == -ENOTCONN || rc == -EAGAIN) &&
+ (aa->aa_oi->oi_flags & OBD_STATFS_NODELAY)) {
+ rc = 0;
+ goto out;
+ }
+
+ if (rc != 0)
+ goto out;
+
+ msfs = req_capsule_server_get(&req->rq_pill, &RMF_OBD_STATFS);
+ if (msfs == NULL) {
+ rc = -EPROTO;
+ goto out;
+ }
+
+ *aa->aa_oi->oi_osfs = *msfs;
+out:
+ rc = aa->aa_oi->oi_cb_up(aa->aa_oi, rc);
+ return rc;
+}
+
+static int osc_statfs_async(struct obd_export *exp,
+ struct obd_info *oinfo, __u64 max_age,
+ struct ptlrpc_request_set *rqset)
+{
+ struct obd_device *obd = class_exp2obd(exp);
+ struct ptlrpc_request *req;
+ struct osc_async_args *aa;
+ int rc;
+
+ /* We could possibly pass max_age in the request (as an absolute
+ * timestamp or a "seconds.usec ago") so the target can avoid doing
+ * extra calls into the filesystem if that isn't necessary (e.g.
+ * during mount that would help a bit). Having relative timestamps
+ * is not so great if request processing is slow, while absolute
+ * timestamps are not ideal because they need time synchronization. */
+ req = ptlrpc_request_alloc(obd->u.cli.cl_import, &RQF_OST_STATFS);
+ if (req == NULL)
+ return -ENOMEM;
+
+ rc = ptlrpc_request_pack(req, LUSTRE_OST_VERSION, OST_STATFS);
+ if (rc) {
+ ptlrpc_request_free(req);
+ return rc;
+ }
+ ptlrpc_request_set_replen(req);
+ req->rq_request_portal = OST_CREATE_PORTAL;
+ ptlrpc_at_set_req_timeout(req);
+
+ if (oinfo->oi_flags & OBD_STATFS_NODELAY) {
+ /* procfs requests not want stat in wait for avoid deadlock */
+ req->rq_no_resend = 1;
+ req->rq_no_delay = 1;
+ }
+
+ req->rq_interpret_reply = (ptlrpc_interpterer_t)osc_statfs_interpret;
+ CLASSERT (sizeof(*aa) <= sizeof(req->rq_async_args));
+ aa = ptlrpc_req_async_args(req);
+ aa->aa_oi = oinfo;
+
+ ptlrpc_set_add_req(rqset, req);
+ return 0;
+}
+
+static int osc_statfs(const struct lu_env *env, struct obd_export *exp,
+ struct obd_statfs *osfs, __u64 max_age, __u32 flags)
+{
+ struct obd_device *obd = class_exp2obd(exp);
+ struct obd_statfs *msfs;
+ struct ptlrpc_request *req;
+ struct obd_import *imp = NULL;
+ int rc;
+
+ /*Since the request might also come from lprocfs, so we need
+ *sync this with client_disconnect_export Bug15684*/
+ down_read(&obd->u.cli.cl_sem);
+ if (obd->u.cli.cl_import)
+ imp = class_import_get(obd->u.cli.cl_import);
+ up_read(&obd->u.cli.cl_sem);
+ if (!imp)
+ return -ENODEV;
+
+ /* We could possibly pass max_age in the request (as an absolute
+ * timestamp or a "seconds.usec ago") so the target can avoid doing
+ * extra calls into the filesystem if that isn't necessary (e.g.
+ * during mount that would help a bit). Having relative timestamps
+ * is not so great if request processing is slow, while absolute
+ * timestamps are not ideal because they need time synchronization. */
+ req = ptlrpc_request_alloc(imp, &RQF_OST_STATFS);
+
+ class_import_put(imp);
+
+ if (req == NULL)
+ return -ENOMEM;
+
+ rc = ptlrpc_request_pack(req, LUSTRE_OST_VERSION, OST_STATFS);
+ if (rc) {
+ ptlrpc_request_free(req);
+ return rc;
+ }
+ ptlrpc_request_set_replen(req);
+ req->rq_request_portal = OST_CREATE_PORTAL;
+ ptlrpc_at_set_req_timeout(req);
+
+ if (flags & OBD_STATFS_NODELAY) {
+ /* procfs requests not want stat in wait for avoid deadlock */
+ req->rq_no_resend = 1;
+ req->rq_no_delay = 1;
+ }
+
+ rc = ptlrpc_queue_wait(req);
+ if (rc)
+ goto out;
+
+ msfs = req_capsule_server_get(&req->rq_pill, &RMF_OBD_STATFS);
+ if (msfs == NULL) {
+ rc = -EPROTO;
+ goto out;
+ }
+
+ *osfs = *msfs;
+
+ out:
+ ptlrpc_req_finished(req);
+ return rc;
+}
+
+/* Retrieve object striping information.
+ *
+ * @lmmu is a pointer to an in-core struct with lmm_ost_count indicating
+ * the maximum number of OST indices which will fit in the user buffer.
+ * lmm_magic must be LOV_MAGIC (we only use 1 slot here).
+ */
+static int osc_getstripe(struct lov_stripe_md *lsm, struct lov_user_md *lump)
+{
+ /* we use lov_user_md_v3 because it is larger than lov_user_md_v1 */
+ struct lov_user_md_v3 lum, *lumk;
+ struct lov_user_ost_data_v1 *lmm_objects;
+ int rc = 0, lum_size;
+
+ if (!lsm)
+ return -ENODATA;
+
+ /* we only need the header part from user space to get lmm_magic and
+ * lmm_stripe_count, (the header part is common to v1 and v3) */
+ lum_size = sizeof(struct lov_user_md_v1);
+ if (copy_from_user(&lum, lump, lum_size))
+ return -EFAULT;
+
+ if ((lum.lmm_magic != LOV_USER_MAGIC_V1) &&
+ (lum.lmm_magic != LOV_USER_MAGIC_V3))
+ return -EINVAL;
+
+ /* lov_user_md_vX and lov_mds_md_vX must have the same size */
+ LASSERT(sizeof(struct lov_user_md_v1) == sizeof(struct lov_mds_md_v1));
+ LASSERT(sizeof(struct lov_user_md_v3) == sizeof(struct lov_mds_md_v3));
+ LASSERT(sizeof(lum.lmm_objects[0]) == sizeof(lumk->lmm_objects[0]));
+
+ /* we can use lov_mds_md_size() to compute lum_size
+ * because lov_user_md_vX and lov_mds_md_vX have the same size */
+ if (lum.lmm_stripe_count > 0) {
+ lum_size = lov_mds_md_size(lum.lmm_stripe_count, lum.lmm_magic);
+ OBD_ALLOC(lumk, lum_size);
+ if (!lumk)
+ return -ENOMEM;
+
+ if (lum.lmm_magic == LOV_USER_MAGIC_V1)
+ lmm_objects =
+ &(((struct lov_user_md_v1 *)lumk)->lmm_objects[0]);
+ else
+ lmm_objects = &(lumk->lmm_objects[0]);
+ lmm_objects->l_ost_oi = lsm->lsm_oi;
+ } else {
+ lum_size = lov_mds_md_size(0, lum.lmm_magic);
+ lumk = &lum;
+ }
+
+ lumk->lmm_oi = lsm->lsm_oi;
+ lumk->lmm_stripe_count = 1;
+
+ if (copy_to_user(lump, lumk, lum_size))
+ rc = -EFAULT;
+
+ if (lumk != &lum)
+ OBD_FREE(lumk, lum_size);
+
+ return rc;
+}
+
+
+static int osc_iocontrol(unsigned int cmd, struct obd_export *exp, int len,
+ void *karg, void *uarg)
+{
+ struct obd_device *obd = exp->exp_obd;
+ struct obd_ioctl_data *data = karg;
+ int err = 0;
+
+ if (!try_module_get(THIS_MODULE)) {
+ CERROR("Can't get module. Is it alive?");
+ return -EINVAL;
+ }
+ switch (cmd) {
+ case OBD_IOC_LOV_GET_CONFIG: {
+ char *buf;
+ struct lov_desc *desc;
+ struct obd_uuid uuid;
+
+ buf = NULL;
+ len = 0;
+ if (obd_ioctl_getdata(&buf, &len, (void *)uarg)) {
+ err = -EINVAL;
+ goto out;
+ }
+
+ data = (struct obd_ioctl_data *)buf;
+
+ if (sizeof(*desc) > data->ioc_inllen1) {
+ obd_ioctl_freedata(buf, len);
+ err = -EINVAL;
+ goto out;
+ }
+
+ if (data->ioc_inllen2 < sizeof(uuid)) {
+ obd_ioctl_freedata(buf, len);
+ err = -EINVAL;
+ goto out;
+ }
+
+ desc = (struct lov_desc *)data->ioc_inlbuf1;
+ desc->ld_tgt_count = 1;
+ desc->ld_active_tgt_count = 1;
+ desc->ld_default_stripe_count = 1;
+ desc->ld_default_stripe_size = 0;
+ desc->ld_default_stripe_offset = 0;
+ desc->ld_pattern = 0;
+ memcpy(&desc->ld_uuid, &obd->obd_uuid, sizeof(uuid));
+
+ memcpy(data->ioc_inlbuf2, &obd->obd_uuid, sizeof(uuid));
+
+ err = copy_to_user((void *)uarg, buf, len);
+ if (err)
+ err = -EFAULT;
+ obd_ioctl_freedata(buf, len);
+ goto out;
+ }
+ case LL_IOC_LOV_SETSTRIPE:
+ err = obd_alloc_memmd(exp, karg);
+ if (err > 0)
+ err = 0;
+ goto out;
+ case LL_IOC_LOV_GETSTRIPE:
+ err = osc_getstripe(karg, uarg);
+ goto out;
+ case OBD_IOC_CLIENT_RECOVER:
+ err = ptlrpc_recover_import(obd->u.cli.cl_import,
+ data->ioc_inlbuf1, 0);
+ if (err > 0)
+ err = 0;
+ goto out;
+ case IOC_OSC_SET_ACTIVE:
+ err = ptlrpc_set_import_active(obd->u.cli.cl_import,
+ data->ioc_offset);
+ goto out;
+ case OBD_IOC_POLL_QUOTACHECK:
+ err = osc_quota_poll_check(exp, (struct if_quotacheck *)karg);
+ goto out;
+ case OBD_IOC_PING_TARGET:
+ err = ptlrpc_obd_ping(obd);
+ goto out;
+ default:
+ CDEBUG(D_INODE, "unrecognised ioctl %#x by %s\n",
+ cmd, current_comm());
+ err = -ENOTTY;
+ goto out;
+ }
+out:
+ module_put(THIS_MODULE);
+ return err;
+}
+
+static int osc_get_info(const struct lu_env *env, struct obd_export *exp,
+ u32 keylen, void *key, __u32 *vallen, void *val,
+ struct lov_stripe_md *lsm)
+{
+ if (!vallen || !val)
+ return -EFAULT;
+
+ if (KEY_IS(KEY_LOCK_TO_STRIPE)) {
+ __u32 *stripe = val;
+ *vallen = sizeof(*stripe);
+ *stripe = 0;
+ return 0;
+ } else if (KEY_IS(KEY_LAST_ID)) {
+ struct ptlrpc_request *req;
+ u64 *reply;
+ char *tmp;
+ int rc;
+
+ req = ptlrpc_request_alloc(class_exp2cliimp(exp),
+ &RQF_OST_GET_INFO_LAST_ID);
+ if (req == NULL)
+ return -ENOMEM;
+
+ req_capsule_set_size(&req->rq_pill, &RMF_SETINFO_KEY,
+ RCL_CLIENT, keylen);
+ rc = ptlrpc_request_pack(req, LUSTRE_OST_VERSION, OST_GET_INFO);
+ if (rc) {
+ ptlrpc_request_free(req);
+ return rc;
+ }
+
+ tmp = req_capsule_client_get(&req->rq_pill, &RMF_SETINFO_KEY);
+ memcpy(tmp, key, keylen);
+
+ req->rq_no_delay = req->rq_no_resend = 1;
+ ptlrpc_request_set_replen(req);
+ rc = ptlrpc_queue_wait(req);
+ if (rc)
+ goto out;
+
+ reply = req_capsule_server_get(&req->rq_pill, &RMF_OBD_ID);
+ if (reply == NULL) {
+ rc = -EPROTO;
+ goto out;
+ }
+
+ *((u64 *)val) = *reply;
+ out:
+ ptlrpc_req_finished(req);
+ return rc;
+ } else if (KEY_IS(KEY_FIEMAP)) {
+ struct ll_fiemap_info_key *fm_key =
+ (struct ll_fiemap_info_key *)key;
+ struct ldlm_res_id res_id;
+ ldlm_policy_data_t policy;
+ struct lustre_handle lockh;
+ ldlm_mode_t mode = 0;
+ struct ptlrpc_request *req;
+ struct ll_user_fiemap *reply;
+ char *tmp;
+ int rc;
+
+ if (!(fm_key->fiemap.fm_flags & FIEMAP_FLAG_SYNC))
+ goto skip_locking;
+
+ policy.l_extent.start = fm_key->fiemap.fm_start &
+ CFS_PAGE_MASK;
+
+ if (OBD_OBJECT_EOF - fm_key->fiemap.fm_length <=
+ fm_key->fiemap.fm_start + PAGE_CACHE_SIZE - 1)
+ policy.l_extent.end = OBD_OBJECT_EOF;
+ else
+ policy.l_extent.end = (fm_key->fiemap.fm_start +
+ fm_key->fiemap.fm_length +
+ PAGE_CACHE_SIZE - 1) & CFS_PAGE_MASK;
+
+ ostid_build_res_name(&fm_key->oa.o_oi, &res_id);
+ mode = ldlm_lock_match(exp->exp_obd->obd_namespace,
+ LDLM_FL_BLOCK_GRANTED |
+ LDLM_FL_LVB_READY,
+ &res_id, LDLM_EXTENT, &policy,
+ LCK_PR | LCK_PW, &lockh, 0);
+ if (mode) { /* lock is cached on client */
+ if (mode != LCK_PR) {
+ ldlm_lock_addref(&lockh, LCK_PR);
+ ldlm_lock_decref(&lockh, LCK_PW);
+ }
+ } else { /* no cached lock, needs acquire lock on server side */
+ fm_key->oa.o_valid |= OBD_MD_FLFLAGS;
+ fm_key->oa.o_flags |= OBD_FL_SRVLOCK;
+ }
+
+skip_locking:
+ req = ptlrpc_request_alloc(class_exp2cliimp(exp),
+ &RQF_OST_GET_INFO_FIEMAP);
+ if (req == NULL) {
+ rc = -ENOMEM;
+ goto drop_lock;
+ }
+
+ req_capsule_set_size(&req->rq_pill, &RMF_FIEMAP_KEY,
+ RCL_CLIENT, keylen);
+ req_capsule_set_size(&req->rq_pill, &RMF_FIEMAP_VAL,
+ RCL_CLIENT, *vallen);
+ req_capsule_set_size(&req->rq_pill, &RMF_FIEMAP_VAL,
+ RCL_SERVER, *vallen);
+
+ rc = ptlrpc_request_pack(req, LUSTRE_OST_VERSION, OST_GET_INFO);
+ if (rc) {
+ ptlrpc_request_free(req);
+ goto drop_lock;
+ }
+
+ tmp = req_capsule_client_get(&req->rq_pill, &RMF_FIEMAP_KEY);
+ memcpy(tmp, key, keylen);
+ tmp = req_capsule_client_get(&req->rq_pill, &RMF_FIEMAP_VAL);
+ memcpy(tmp, val, *vallen);
+
+ ptlrpc_request_set_replen(req);
+ rc = ptlrpc_queue_wait(req);
+ if (rc)
+ goto fini_req;
+
+ reply = req_capsule_server_get(&req->rq_pill, &RMF_FIEMAP_VAL);
+ if (reply == NULL) {
+ rc = -EPROTO;
+ goto fini_req;
+ }
+
+ memcpy(val, reply, *vallen);
+fini_req:
+ ptlrpc_req_finished(req);
+drop_lock:
+ if (mode)
+ ldlm_lock_decref(&lockh, LCK_PR);
+ return rc;
+ }
+
+ return -EINVAL;
+}
+
+static int osc_set_info_async(const struct lu_env *env, struct obd_export *exp,
+ u32 keylen, void *key, u32 vallen,
+ void *val, struct ptlrpc_request_set *set)
+{
+ struct ptlrpc_request *req;
+ struct obd_device *obd = exp->exp_obd;
+ struct obd_import *imp = class_exp2cliimp(exp);
+ char *tmp;
+ int rc;
+
+ OBD_FAIL_TIMEOUT(OBD_FAIL_OSC_SHUTDOWN, 10);
+
+ if (KEY_IS(KEY_CHECKSUM)) {
+ if (vallen != sizeof(int))
+ return -EINVAL;
+ exp->exp_obd->u.cli.cl_checksum = (*(int *)val) ? 1 : 0;
+ return 0;
+ }
+
+ if (KEY_IS(KEY_SPTLRPC_CONF)) {
+ sptlrpc_conf_client_adapt(obd);
+ return 0;
+ }
+
+ if (KEY_IS(KEY_FLUSH_CTX)) {
+ sptlrpc_import_flush_my_ctx(imp);
+ return 0;
+ }
+
+ if (KEY_IS(KEY_CACHE_SET)) {
+ struct client_obd *cli = &obd->u.cli;
+
+ LASSERT(cli->cl_cache == NULL); /* only once */
+ cli->cl_cache = (struct cl_client_cache *)val;
+ atomic_inc(&cli->cl_cache->ccc_users);
+ cli->cl_lru_left = &cli->cl_cache->ccc_lru_left;
+
+ /* add this osc into entity list */
+ LASSERT(list_empty(&cli->cl_lru_osc));
+ spin_lock(&cli->cl_cache->ccc_lru_lock);
+ list_add(&cli->cl_lru_osc, &cli->cl_cache->ccc_lru);
+ spin_unlock(&cli->cl_cache->ccc_lru_lock);
+
+ return 0;
+ }
+
+ if (KEY_IS(KEY_CACHE_LRU_SHRINK)) {
+ struct client_obd *cli = &obd->u.cli;
+ int nr = atomic_read(&cli->cl_lru_in_list) >> 1;
+ int target = *(int *)val;
+
+ nr = osc_lru_shrink(cli, min(nr, target));
+ *(int *)val -= nr;
+ return 0;
+ }
+
+ if (!set && !KEY_IS(KEY_GRANT_SHRINK))
+ return -EINVAL;
+
+ /* We pass all other commands directly to OST. Since nobody calls osc
+ methods directly and everybody is supposed to go through LOV, we
+ assume lov checked invalid values for us.
+ The only recognised values so far are evict_by_nid and mds_conn.
+ Even if something bad goes through, we'd get a -EINVAL from OST
+ anyway. */
+
+ req = ptlrpc_request_alloc(imp, KEY_IS(KEY_GRANT_SHRINK) ?
+ &RQF_OST_SET_GRANT_INFO :
+ &RQF_OBD_SET_INFO);
+ if (req == NULL)
+ return -ENOMEM;
+
+ req_capsule_set_size(&req->rq_pill, &RMF_SETINFO_KEY,
+ RCL_CLIENT, keylen);
+ if (!KEY_IS(KEY_GRANT_SHRINK))
+ req_capsule_set_size(&req->rq_pill, &RMF_SETINFO_VAL,
+ RCL_CLIENT, vallen);
+ rc = ptlrpc_request_pack(req, LUSTRE_OST_VERSION, OST_SET_INFO);
+ if (rc) {
+ ptlrpc_request_free(req);
+ return rc;
+ }
+
+ tmp = req_capsule_client_get(&req->rq_pill, &RMF_SETINFO_KEY);
+ memcpy(tmp, key, keylen);
+ tmp = req_capsule_client_get(&req->rq_pill, KEY_IS(KEY_GRANT_SHRINK) ?
+ &RMF_OST_BODY :
+ &RMF_SETINFO_VAL);
+ memcpy(tmp, val, vallen);
+
+ if (KEY_IS(KEY_GRANT_SHRINK)) {
+ struct osc_brw_async_args *aa;
+ struct obdo *oa;
+
+ CLASSERT(sizeof(*aa) <= sizeof(req->rq_async_args));
+ aa = ptlrpc_req_async_args(req);
+ OBDO_ALLOC(oa);
+ if (!oa) {
+ ptlrpc_req_finished(req);
+ return -ENOMEM;
+ }
+ *oa = ((struct ost_body *)val)->oa;
+ aa->aa_oa = oa;
+ req->rq_interpret_reply = osc_shrink_grant_interpret;
+ }
+
+ ptlrpc_request_set_replen(req);
+ if (!KEY_IS(KEY_GRANT_SHRINK)) {
+ LASSERT(set != NULL);
+ ptlrpc_set_add_req(set, req);
+ ptlrpc_check_set(NULL, set);
+ } else
+ ptlrpcd_add_req(req, PDL_POLICY_ROUND, -1);
+
+ return 0;
+}
+
+static int osc_reconnect(const struct lu_env *env,
+ struct obd_export *exp, struct obd_device *obd,
+ struct obd_uuid *cluuid,
+ struct obd_connect_data *data,
+ void *localdata)
+{
+ struct client_obd *cli = &obd->u.cli;
+
+ if (data != NULL && (data->ocd_connect_flags & OBD_CONNECT_GRANT)) {
+ long lost_grant;
+
+ client_obd_list_lock(&cli->cl_loi_list_lock);
+ data->ocd_grant = (cli->cl_avail_grant + cli->cl_dirty) ?:
+ 2 * cli_brw_size(obd);
+ lost_grant = cli->cl_lost_grant;
+ cli->cl_lost_grant = 0;
+ client_obd_list_unlock(&cli->cl_loi_list_lock);
+
+ CDEBUG(D_RPCTRACE, "ocd_connect_flags: %#llx ocd_version: %d ocd_grant: %d, lost: %ld.\n",
+ data->ocd_connect_flags,
+ data->ocd_version, data->ocd_grant, lost_grant);
+ }
+
+ return 0;
+}
+
+static int osc_disconnect(struct obd_export *exp)
+{
+ struct obd_device *obd = class_exp2obd(exp);
+ int rc;
+
+ rc = client_disconnect_export(exp);
+ /**
+ * Initially we put del_shrink_grant before disconnect_export, but it
+ * causes the following problem if setup (connect) and cleanup
+ * (disconnect) are tangled together.
+ * connect p1 disconnect p2
+ * ptlrpc_connect_import
+ * ............... class_manual_cleanup
+ * osc_disconnect
+ * del_shrink_grant
+ * ptlrpc_connect_interrupt
+ * init_grant_shrink
+ * add this client to shrink list
+ * cleanup_osc
+ * Bang! pinger trigger the shrink.
+ * So the osc should be disconnected from the shrink list, after we
+ * are sure the import has been destroyed. BUG18662
+ */
+ if (obd->u.cli.cl_import == NULL)
+ osc_del_shrink_grant(&obd->u.cli);
+ return rc;
+}
+
+static int osc_import_event(struct obd_device *obd,
+ struct obd_import *imp,
+ enum obd_import_event event)
+{
+ struct client_obd *cli;
+ int rc = 0;
+
+ LASSERT(imp->imp_obd == obd);
+
+ switch (event) {
+ case IMP_EVENT_DISCON: {
+ cli = &obd->u.cli;
+ client_obd_list_lock(&cli->cl_loi_list_lock);
+ cli->cl_avail_grant = 0;
+ cli->cl_lost_grant = 0;
+ client_obd_list_unlock(&cli->cl_loi_list_lock);
+ break;
+ }
+ case IMP_EVENT_INACTIVE: {
+ rc = obd_notify_observer(obd, obd, OBD_NOTIFY_INACTIVE, NULL);
+ break;
+ }
+ case IMP_EVENT_INVALIDATE: {
+ struct ldlm_namespace *ns = obd->obd_namespace;
+ struct lu_env *env;
+ int refcheck;
+
+ env = cl_env_get(&refcheck);
+ if (!IS_ERR(env)) {
+ /* Reset grants */
+ cli = &obd->u.cli;
+ /* all pages go to failing rpcs due to the invalid
+ * import */
+ osc_io_unplug(env, cli, NULL, PDL_POLICY_ROUND);
+
+ ldlm_namespace_cleanup(ns, LDLM_FL_LOCAL_ONLY);
+ cl_env_put(env, &refcheck);
+ } else
+ rc = PTR_ERR(env);
+ break;
+ }
+ case IMP_EVENT_ACTIVE: {
+ rc = obd_notify_observer(obd, obd, OBD_NOTIFY_ACTIVE, NULL);
+ break;
+ }
+ case IMP_EVENT_OCD: {
+ struct obd_connect_data *ocd = &imp->imp_connect_data;
+
+ if (ocd->ocd_connect_flags & OBD_CONNECT_GRANT)
+ osc_init_grant(&obd->u.cli, ocd);
+
+ /* See bug 7198 */
+ if (ocd->ocd_connect_flags & OBD_CONNECT_REQPORTAL)
+ imp->imp_client->cli_request_portal =OST_REQUEST_PORTAL;
+
+ rc = obd_notify_observer(obd, obd, OBD_NOTIFY_OCD, NULL);
+ break;
+ }
+ case IMP_EVENT_DEACTIVATE: {
+ rc = obd_notify_observer(obd, obd, OBD_NOTIFY_DEACTIVATE, NULL);
+ break;
+ }
+ case IMP_EVENT_ACTIVATE: {
+ rc = obd_notify_observer(obd, obd, OBD_NOTIFY_ACTIVATE, NULL);
+ break;
+ }
+ default:
+ CERROR("Unknown import event %d\n", event);
+ LBUG();
+ }
+ return rc;
+}
+
+/**
+ * Determine whether the lock can be canceled before replaying the lock
+ * during recovery, see bug16774 for detailed information.
+ *
+ * \retval zero the lock can't be canceled
+ * \retval other ok to cancel
+ */
+static int osc_cancel_for_recovery(struct ldlm_lock *lock)
+{
+ check_res_locked(lock->l_resource);
+
+ /*
+ * Cancel all unused extent lock in granted mode LCK_PR or LCK_CR.
+ *
+ * XXX as a future improvement, we can also cancel unused write lock
+ * if it doesn't have dirty data and active mmaps.
+ */
+ if (lock->l_resource->lr_type == LDLM_EXTENT &&
+ (lock->l_granted_mode == LCK_PR ||
+ lock->l_granted_mode == LCK_CR) &&
+ (osc_dlm_lock_pageref(lock) == 0))
+ return 1;
+
+ return 0;
+}
+
+static int brw_queue_work(const struct lu_env *env, void *data)
+{
+ struct client_obd *cli = data;
+
+ CDEBUG(D_CACHE, "Run writeback work for client obd %p.\n", cli);
+
+ osc_io_unplug(env, cli, NULL, PDL_POLICY_SAME);
+ return 0;
+}
+
+int osc_setup(struct obd_device *obd, struct lustre_cfg *lcfg)
+{
+ struct lprocfs_static_vars lvars = { NULL };
+ struct client_obd *cli = &obd->u.cli;
+ void *handler;
+ int rc;
+
+ rc = ptlrpcd_addref();
+ if (rc)
+ return rc;
+
+ rc = client_obd_setup(obd, lcfg);
+ if (rc)
+ goto out_ptlrpcd;
+
+ handler = ptlrpcd_alloc_work(cli->cl_import, brw_queue_work, cli);
+ if (IS_ERR(handler)) {
+ rc = PTR_ERR(handler);
+ goto out_client_setup;
+ }
+ cli->cl_writeback_work = handler;
+
+ rc = osc_quota_setup(obd);
+ if (rc)
+ goto out_ptlrpcd_work;
+
+ cli->cl_grant_shrink_interval = GRANT_SHRINK_INTERVAL;
+ lprocfs_osc_init_vars(&lvars);
+ if (lprocfs_obd_setup(obd, lvars.obd_vars) == 0) {
+ lproc_osc_attach_seqstat(obd);
+ sptlrpc_lprocfs_cliobd_attach(obd);
+ ptlrpc_lprocfs_register_obd(obd);
+ }
+
+ /* We need to allocate a few requests more, because
+ * brw_interpret tries to create new requests before freeing
+ * previous ones, Ideally we want to have 2x max_rpcs_in_flight
+ * reserved, but I'm afraid that might be too much wasted RAM
+ * in fact, so 2 is just my guess and still should work. */
+ cli->cl_import->imp_rq_pool =
+ ptlrpc_init_rq_pool(cli->cl_max_rpcs_in_flight + 2,
+ OST_MAXREQSIZE,
+ ptlrpc_add_rqs_to_pool);
+
+ INIT_LIST_HEAD(&cli->cl_grant_shrink_list);
+ ns_register_cancel(obd->obd_namespace, osc_cancel_for_recovery);
+ return rc;
+
+out_ptlrpcd_work:
+ ptlrpcd_destroy_work(handler);
+out_client_setup:
+ client_obd_cleanup(obd);
+out_ptlrpcd:
+ ptlrpcd_decref();
+ return rc;
+}
+
+static int osc_precleanup(struct obd_device *obd, enum obd_cleanup_stage stage)
+{
+ switch (stage) {
+ case OBD_CLEANUP_EARLY: {
+ struct obd_import *imp;
+ imp = obd->u.cli.cl_import;
+ CDEBUG(D_HA, "Deactivating import %s\n", obd->obd_name);
+ /* ptlrpc_abort_inflight to stop an mds_lov_synchronize */
+ ptlrpc_deactivate_import(imp);
+ spin_lock(&imp->imp_lock);
+ imp->imp_pingable = 0;
+ spin_unlock(&imp->imp_lock);
+ break;
+ }
+ case OBD_CLEANUP_EXPORTS: {
+ struct client_obd *cli = &obd->u.cli;
+ /* LU-464
+ * for echo client, export may be on zombie list, wait for
+ * zombie thread to cull it, because cli.cl_import will be
+ * cleared in client_disconnect_export():
+ * class_export_destroy() -> obd_cleanup() ->
+ * echo_device_free() -> echo_client_cleanup() ->
+ * obd_disconnect() -> osc_disconnect() ->
+ * client_disconnect_export()
+ */
+ obd_zombie_barrier();
+ if (cli->cl_writeback_work) {
+ ptlrpcd_destroy_work(cli->cl_writeback_work);
+ cli->cl_writeback_work = NULL;
+ }
+ obd_cleanup_client_import(obd);
+ ptlrpc_lprocfs_unregister_obd(obd);
+ lprocfs_obd_cleanup(obd);
+ break;
+ }
+ }
+ return 0;
+}
+
+int osc_cleanup(struct obd_device *obd)
+{
+ struct client_obd *cli = &obd->u.cli;
+ int rc;
+
+ /* lru cleanup */
+ if (cli->cl_cache != NULL) {
+ LASSERT(atomic_read(&cli->cl_cache->ccc_users) > 0);
+ spin_lock(&cli->cl_cache->ccc_lru_lock);
+ list_del_init(&cli->cl_lru_osc);
+ spin_unlock(&cli->cl_cache->ccc_lru_lock);
+ cli->cl_lru_left = NULL;
+ atomic_dec(&cli->cl_cache->ccc_users);
+ cli->cl_cache = NULL;
+ }
+
+ /* free memory of osc quota cache */
+ osc_quota_cleanup(obd);
+
+ rc = client_obd_cleanup(obd);
+
+ ptlrpcd_decref();
+ return rc;
+}
+
+int osc_process_config_base(struct obd_device *obd, struct lustre_cfg *lcfg)
+{
+ struct lprocfs_static_vars lvars = { NULL };
+ int rc = 0;
+
+ lprocfs_osc_init_vars(&lvars);
+
+ switch (lcfg->lcfg_command) {
+ default:
+ rc = class_process_proc_param(PARAM_OSC, lvars.obd_vars,
+ lcfg, obd);
+ if (rc > 0)
+ rc = 0;
+ break;
+ }
+
+ return rc;
+}
+
+static int osc_process_config(struct obd_device *obd, u32 len, void *buf)
+{
+ return osc_process_config_base(obd, buf);
+}
+
+struct obd_ops osc_obd_ops = {
+ .o_owner = THIS_MODULE,
+ .o_setup = osc_setup,
+ .o_precleanup = osc_precleanup,
+ .o_cleanup = osc_cleanup,
+ .o_add_conn = client_import_add_conn,
+ .o_del_conn = client_import_del_conn,
+ .o_connect = client_connect_import,
+ .o_reconnect = osc_reconnect,
+ .o_disconnect = osc_disconnect,
+ .o_statfs = osc_statfs,
+ .o_statfs_async = osc_statfs_async,
+ .o_packmd = osc_packmd,
+ .o_unpackmd = osc_unpackmd,
+ .o_create = osc_create,
+ .o_destroy = osc_destroy,
+ .o_getattr = osc_getattr,
+ .o_getattr_async = osc_getattr_async,
+ .o_setattr = osc_setattr,
+ .o_setattr_async = osc_setattr_async,
+ .o_find_cbdata = osc_find_cbdata,
+ .o_iocontrol = osc_iocontrol,
+ .o_get_info = osc_get_info,
+ .o_set_info_async = osc_set_info_async,
+ .o_import_event = osc_import_event,
+ .o_process_config = osc_process_config,
+ .o_quotactl = osc_quotactl,
+ .o_quotacheck = osc_quotacheck,
+};
+
+extern struct lu_kmem_descr osc_caches[];
+extern spinlock_t osc_ast_guard;
+extern struct lock_class_key osc_ast_guard_class;
+
+static int __init osc_init(void)
+{
+ struct lprocfs_static_vars lvars = { NULL };
+ int rc;
+
+ /* print an address of _any_ initialized kernel symbol from this
+ * module, to allow debugging with gdb that doesn't support data
+ * symbols from modules.*/
+ CDEBUG(D_INFO, "Lustre OSC module (%p).\n", &osc_caches);
+
+ rc = lu_kmem_init(osc_caches);
+ if (rc)
+ return rc;
+
+ lprocfs_osc_init_vars(&lvars);
+
+ rc = class_register_type(&osc_obd_ops, NULL, lvars.module_vars,
+ LUSTRE_OSC_NAME, &osc_device_type);
+ if (rc) {
+ lu_kmem_fini(osc_caches);
+ return rc;
+ }
+
+ spin_lock_init(&osc_ast_guard);
+ lockdep_set_class(&osc_ast_guard, &osc_ast_guard_class);
+
+ return rc;
+}
+
+static void /*__exit*/ osc_exit(void)
+{
+ class_unregister_type(LUSTRE_OSC_NAME);
+ lu_kmem_fini(osc_caches);
+}
+
+MODULE_AUTHOR("Sun Microsystems, Inc. <http://www.lustre.org/>");
+MODULE_DESCRIPTION("Lustre Object Storage Client (OSC)");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(LUSTRE_VERSION_STRING);
+
+module_init(osc_init);
+module_exit(osc_exit);
diff --git a/drivers/staging/lustre/lustre/ptlrpc/Makefile b/drivers/staging/lustre/lustre/ptlrpc/Makefile
new file mode 100644
index 000000000..fb50cd4c6
--- /dev/null
+++ b/drivers/staging/lustre/lustre/ptlrpc/Makefile
@@ -0,0 +1,20 @@
+obj-$(CONFIG_LUSTRE_FS) += ptlrpc.o
+LDLM := ../../lustre/ldlm/
+
+ldlm_objs := $(LDLM)l_lock.o $(LDLM)ldlm_lock.o
+ldlm_objs += $(LDLM)ldlm_resource.o $(LDLM)ldlm_lib.o
+ldlm_objs += $(LDLM)ldlm_plain.o $(LDLM)ldlm_extent.o
+ldlm_objs += $(LDLM)ldlm_request.o $(LDLM)ldlm_lockd.o
+ldlm_objs += $(LDLM)ldlm_flock.o $(LDLM)ldlm_inodebits.o
+ldlm_objs += $(LDLM)ldlm_pool.o
+ldlm_objs += $(LDLM)interval_tree.o
+ptlrpc_objs := client.o recover.o connection.o niobuf.o pack_generic.o
+ptlrpc_objs += events.o ptlrpc_module.o service.o pinger.o
+ptlrpc_objs += llog_net.o llog_client.o import.o ptlrpcd.o
+ptlrpc_objs += pers.o lproc_ptlrpc.o wiretest.o layout.o
+ptlrpc_objs += sec.o sec_bulk.o sec_gc.o sec_config.o
+ptlrpc_objs += sec_null.o sec_plain.o nrs.o nrs_fifo.o
+
+ptlrpc-y := $(ldlm_objs) $(ptlrpc_objs)
+ptlrpc-$(CONFIG_PROC_FS) += sec_lproc.o
+ptlrpc-$(CONFIG_LUSTRE_TRANSLATE_ERRNOS) += errno.o
diff --git a/drivers/staging/lustre/lustre/ptlrpc/client.c b/drivers/staging/lustre/lustre/ptlrpc/client.c
new file mode 100644
index 000000000..0357f1d45
--- /dev/null
+++ b/drivers/staging/lustre/lustre/ptlrpc/client.c
@@ -0,0 +1,3149 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2002, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2011, 2012, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ */
+
+/** Implementation of client-side PortalRPC interfaces */
+
+#define DEBUG_SUBSYSTEM S_RPC
+
+#include "../include/obd_support.h"
+#include "../include/obd_class.h"
+#include "../include/lustre_lib.h"
+#include "../include/lustre_ha.h"
+#include "../include/lustre_import.h"
+#include "../include/lustre_req_layout.h"
+
+#include "ptlrpc_internal.h"
+
+static int ptlrpc_send_new_req(struct ptlrpc_request *req);
+static int ptlrpcd_check_work(struct ptlrpc_request *req);
+
+/**
+ * Initialize passed in client structure \a cl.
+ */
+void ptlrpc_init_client(int req_portal, int rep_portal, char *name,
+ struct ptlrpc_client *cl)
+{
+ cl->cli_request_portal = req_portal;
+ cl->cli_reply_portal = rep_portal;
+ cl->cli_name = name;
+}
+EXPORT_SYMBOL(ptlrpc_init_client);
+
+/**
+ * Return PortalRPC connection for remote uud \a uuid
+ */
+struct ptlrpc_connection *ptlrpc_uuid_to_connection(struct obd_uuid *uuid)
+{
+ struct ptlrpc_connection *c;
+ lnet_nid_t self;
+ lnet_process_id_t peer;
+ int err;
+
+ /* ptlrpc_uuid_to_peer() initializes its 2nd parameter
+ * before accessing its values. */
+ /* coverity[uninit_use_in_call] */
+ err = ptlrpc_uuid_to_peer(uuid, &peer, &self);
+ if (err != 0) {
+ CNETERR("cannot find peer %s!\n", uuid->uuid);
+ return NULL;
+ }
+
+ c = ptlrpc_connection_get(peer, self, uuid);
+ if (c) {
+ memcpy(c->c_remote_uuid.uuid,
+ uuid->uuid, sizeof(c->c_remote_uuid.uuid));
+ }
+
+ CDEBUG(D_INFO, "%s -> %p\n", uuid->uuid, c);
+
+ return c;
+}
+EXPORT_SYMBOL(ptlrpc_uuid_to_connection);
+
+/**
+ * Allocate and initialize new bulk descriptor on the sender.
+ * Returns pointer to the descriptor or NULL on error.
+ */
+struct ptlrpc_bulk_desc *ptlrpc_new_bulk(unsigned npages, unsigned max_brw,
+ unsigned type, unsigned portal)
+{
+ struct ptlrpc_bulk_desc *desc;
+ int i;
+
+ OBD_ALLOC(desc, offsetof(struct ptlrpc_bulk_desc, bd_iov[npages]));
+ if (!desc)
+ return NULL;
+
+ spin_lock_init(&desc->bd_lock);
+ init_waitqueue_head(&desc->bd_waitq);
+ desc->bd_max_iov = npages;
+ desc->bd_iov_count = 0;
+ desc->bd_portal = portal;
+ desc->bd_type = type;
+ desc->bd_md_count = 0;
+ LASSERT(max_brw > 0);
+ desc->bd_md_max_brw = min(max_brw, PTLRPC_BULK_OPS_COUNT);
+ /* PTLRPC_BULK_OPS_COUNT is the compile-time transfer limit for this
+ * node. Negotiated ocd_brw_size will always be <= this number. */
+ for (i = 0; i < PTLRPC_BULK_OPS_COUNT; i++)
+ LNetInvalidateHandle(&desc->bd_mds[i]);
+
+ return desc;
+}
+
+/**
+ * Prepare bulk descriptor for specified outgoing request \a req that
+ * can fit \a npages * pages. \a type is bulk type. \a portal is where
+ * the bulk to be sent. Used on client-side.
+ * Returns pointer to newly allocated initialized bulk descriptor or NULL on
+ * error.
+ */
+struct ptlrpc_bulk_desc *ptlrpc_prep_bulk_imp(struct ptlrpc_request *req,
+ unsigned npages, unsigned max_brw,
+ unsigned type, unsigned portal)
+{
+ struct obd_import *imp = req->rq_import;
+ struct ptlrpc_bulk_desc *desc;
+
+ LASSERT(type == BULK_PUT_SINK || type == BULK_GET_SOURCE);
+ desc = ptlrpc_new_bulk(npages, max_brw, type, portal);
+ if (desc == NULL)
+ return NULL;
+
+ desc->bd_import_generation = req->rq_import_generation;
+ desc->bd_import = class_import_get(imp);
+ desc->bd_req = req;
+
+ desc->bd_cbid.cbid_fn = client_bulk_callback;
+ desc->bd_cbid.cbid_arg = desc;
+
+ /* This makes req own desc, and free it when she frees herself */
+ req->rq_bulk = desc;
+
+ return desc;
+}
+EXPORT_SYMBOL(ptlrpc_prep_bulk_imp);
+
+/**
+ * Add a page \a page to the bulk descriptor \a desc.
+ * Data to transfer in the page starts at offset \a pageoffset and
+ * amount of data to transfer from the page is \a len
+ */
+void __ptlrpc_prep_bulk_page(struct ptlrpc_bulk_desc *desc,
+ struct page *page, int pageoffset, int len, int pin)
+{
+ LASSERT(desc->bd_iov_count < desc->bd_max_iov);
+ LASSERT(page != NULL);
+ LASSERT(pageoffset >= 0);
+ LASSERT(len > 0);
+ LASSERT(pageoffset + len <= PAGE_CACHE_SIZE);
+
+ desc->bd_nob += len;
+
+ if (pin)
+ page_cache_get(page);
+
+ ptlrpc_add_bulk_page(desc, page, pageoffset, len);
+}
+EXPORT_SYMBOL(__ptlrpc_prep_bulk_page);
+
+/**
+ * Uninitialize and free bulk descriptor \a desc.
+ * Works on bulk descriptors both from server and client side.
+ */
+void __ptlrpc_free_bulk(struct ptlrpc_bulk_desc *desc, int unpin)
+{
+ int i;
+
+ LASSERT(desc != NULL);
+ LASSERT(desc->bd_iov_count != LI_POISON); /* not freed already */
+ LASSERT(desc->bd_md_count == 0); /* network hands off */
+ LASSERT((desc->bd_export != NULL) ^ (desc->bd_import != NULL));
+
+ sptlrpc_enc_pool_put_pages(desc);
+
+ if (desc->bd_export)
+ class_export_put(desc->bd_export);
+ else
+ class_import_put(desc->bd_import);
+
+ if (unpin) {
+ for (i = 0; i < desc->bd_iov_count; i++)
+ page_cache_release(desc->bd_iov[i].kiov_page);
+ }
+
+ OBD_FREE(desc, offsetof(struct ptlrpc_bulk_desc,
+ bd_iov[desc->bd_max_iov]));
+}
+EXPORT_SYMBOL(__ptlrpc_free_bulk);
+
+/**
+ * Set server timelimit for this req, i.e. how long are we willing to wait
+ * for reply before timing out this request.
+ */
+void ptlrpc_at_set_req_timeout(struct ptlrpc_request *req)
+{
+ __u32 serv_est;
+ int idx;
+ struct imp_at *at;
+
+ LASSERT(req->rq_import);
+
+ if (AT_OFF) {
+ /* non-AT settings */
+ /**
+ * \a imp_server_timeout means this is reverse import and
+ * we send (currently only) ASTs to the client and cannot afford
+ * to wait too long for the reply, otherwise the other client
+ * (because of which we are sending this request) would
+ * timeout waiting for us
+ */
+ req->rq_timeout = req->rq_import->imp_server_timeout ?
+ obd_timeout / 2 : obd_timeout;
+ } else {
+ at = &req->rq_import->imp_at;
+ idx = import_at_get_index(req->rq_import,
+ req->rq_request_portal);
+ serv_est = at_get(&at->iat_service_estimate[idx]);
+ req->rq_timeout = at_est2timeout(serv_est);
+ }
+ /* We could get even fancier here, using history to predict increased
+ loading... */
+
+ /* Let the server know what this RPC timeout is by putting it in the
+ reqmsg*/
+ lustre_msg_set_timeout(req->rq_reqmsg, req->rq_timeout);
+}
+EXPORT_SYMBOL(ptlrpc_at_set_req_timeout);
+
+/* Adjust max service estimate based on server value */
+static void ptlrpc_at_adj_service(struct ptlrpc_request *req,
+ unsigned int serv_est)
+{
+ int idx;
+ unsigned int oldse;
+ struct imp_at *at;
+
+ LASSERT(req->rq_import);
+ at = &req->rq_import->imp_at;
+
+ idx = import_at_get_index(req->rq_import, req->rq_request_portal);
+ /* max service estimates are tracked on the server side,
+ so just keep minimal history here */
+ oldse = at_measured(&at->iat_service_estimate[idx], serv_est);
+ if (oldse != 0)
+ CDEBUG(D_ADAPTTO, "The RPC service estimate for %s ptl %d has changed from %d to %d\n",
+ req->rq_import->imp_obd->obd_name, req->rq_request_portal,
+ oldse, at_get(&at->iat_service_estimate[idx]));
+}
+
+/* Expected network latency per remote node (secs) */
+int ptlrpc_at_get_net_latency(struct ptlrpc_request *req)
+{
+ return AT_OFF ? 0 : at_get(&req->rq_import->imp_at.iat_net_latency);
+}
+
+/* Adjust expected network latency */
+static void ptlrpc_at_adj_net_latency(struct ptlrpc_request *req,
+ unsigned int service_time)
+{
+ unsigned int nl, oldnl;
+ struct imp_at *at;
+ time_t now = get_seconds();
+
+ LASSERT(req->rq_import);
+
+ if (service_time > now - req->rq_sent + 3) {
+ /* bz16408, however, this can also happen if early reply
+ * is lost and client RPC is expired and resent, early reply
+ * or reply of original RPC can still be fit in reply buffer
+ * of resent RPC, now client is measuring time from the
+ * resent time, but server sent back service time of original
+ * RPC.
+ */
+ CDEBUG((lustre_msg_get_flags(req->rq_reqmsg) & MSG_RESENT) ?
+ D_ADAPTTO : D_WARNING,
+ "Reported service time %u > total measured time "
+ CFS_DURATION_T"\n", service_time,
+ cfs_time_sub(now, req->rq_sent));
+ return;
+ }
+
+ /* Network latency is total time less server processing time */
+ nl = max_t(int, now - req->rq_sent -
+ service_time, 0) + 1; /* st rounding */
+ at = &req->rq_import->imp_at;
+
+ oldnl = at_measured(&at->iat_net_latency, nl);
+ if (oldnl != 0)
+ CDEBUG(D_ADAPTTO, "The network latency for %s (nid %s) has changed from %d to %d\n",
+ req->rq_import->imp_obd->obd_name,
+ obd_uuid2str(
+ &req->rq_import->imp_connection->c_remote_uuid),
+ oldnl, at_get(&at->iat_net_latency));
+}
+
+static int unpack_reply(struct ptlrpc_request *req)
+{
+ int rc;
+
+ if (SPTLRPC_FLVR_POLICY(req->rq_flvr.sf_rpc) != SPTLRPC_POLICY_NULL) {
+ rc = ptlrpc_unpack_rep_msg(req, req->rq_replen);
+ if (rc) {
+ DEBUG_REQ(D_ERROR, req, "unpack_rep failed: %d", rc);
+ return -EPROTO;
+ }
+ }
+
+ rc = lustre_unpack_rep_ptlrpc_body(req, MSG_PTLRPC_BODY_OFF);
+ if (rc) {
+ DEBUG_REQ(D_ERROR, req, "unpack ptlrpc body failed: %d", rc);
+ return -EPROTO;
+ }
+ return 0;
+}
+
+/**
+ * Handle an early reply message, called with the rq_lock held.
+ * If anything goes wrong just ignore it - same as if it never happened
+ */
+static int ptlrpc_at_recv_early_reply(struct ptlrpc_request *req)
+{
+ struct ptlrpc_request *early_req;
+ time_t olddl;
+ int rc;
+
+ req->rq_early = 0;
+ spin_unlock(&req->rq_lock);
+
+ rc = sptlrpc_cli_unwrap_early_reply(req, &early_req);
+ if (rc) {
+ spin_lock(&req->rq_lock);
+ return rc;
+ }
+
+ rc = unpack_reply(early_req);
+ if (rc == 0) {
+ /* Expecting to increase the service time estimate here */
+ ptlrpc_at_adj_service(req,
+ lustre_msg_get_timeout(early_req->rq_repmsg));
+ ptlrpc_at_adj_net_latency(req,
+ lustre_msg_get_service_time(early_req->rq_repmsg));
+ }
+
+ sptlrpc_cli_finish_early_reply(early_req);
+
+ if (rc != 0) {
+ spin_lock(&req->rq_lock);
+ return rc;
+ }
+
+ /* Adjust the local timeout for this req */
+ ptlrpc_at_set_req_timeout(req);
+
+ spin_lock(&req->rq_lock);
+ olddl = req->rq_deadline;
+ /* server assumes it now has rq_timeout from when it sent the
+ * early reply, so client should give it at least that long. */
+ req->rq_deadline = get_seconds() + req->rq_timeout +
+ ptlrpc_at_get_net_latency(req);
+
+ DEBUG_REQ(D_ADAPTTO, req,
+ "Early reply #%d, new deadline in " CFS_DURATION_T "s (" CFS_DURATION_T "s)",
+ req->rq_early_count,
+ cfs_time_sub(req->rq_deadline, get_seconds()),
+ cfs_time_sub(req->rq_deadline, olddl));
+
+ return rc;
+}
+
+struct kmem_cache *request_cache;
+
+int ptlrpc_request_cache_init(void)
+{
+ request_cache = kmem_cache_create("ptlrpc_cache",
+ sizeof(struct ptlrpc_request),
+ 0, SLAB_HWCACHE_ALIGN, NULL);
+ return request_cache == NULL ? -ENOMEM : 0;
+}
+
+void ptlrpc_request_cache_fini(void)
+{
+ kmem_cache_destroy(request_cache);
+}
+
+struct ptlrpc_request *ptlrpc_request_cache_alloc(gfp_t flags)
+{
+ struct ptlrpc_request *req;
+
+ OBD_SLAB_ALLOC_PTR_GFP(req, request_cache, flags);
+ return req;
+}
+
+void ptlrpc_request_cache_free(struct ptlrpc_request *req)
+{
+ OBD_SLAB_FREE_PTR(req, request_cache);
+}
+
+/**
+ * Wind down request pool \a pool.
+ * Frees all requests from the pool too
+ */
+void ptlrpc_free_rq_pool(struct ptlrpc_request_pool *pool)
+{
+ struct list_head *l, *tmp;
+ struct ptlrpc_request *req;
+
+ LASSERT(pool != NULL);
+
+ spin_lock(&pool->prp_lock);
+ list_for_each_safe(l, tmp, &pool->prp_req_list) {
+ req = list_entry(l, struct ptlrpc_request, rq_list);
+ list_del(&req->rq_list);
+ LASSERT(req->rq_reqbuf);
+ LASSERT(req->rq_reqbuf_len == pool->prp_rq_size);
+ OBD_FREE_LARGE(req->rq_reqbuf, pool->prp_rq_size);
+ ptlrpc_request_cache_free(req);
+ }
+ spin_unlock(&pool->prp_lock);
+ OBD_FREE(pool, sizeof(*pool));
+}
+EXPORT_SYMBOL(ptlrpc_free_rq_pool);
+
+/**
+ * Allocates, initializes and adds \a num_rq requests to the pool \a pool
+ */
+void ptlrpc_add_rqs_to_pool(struct ptlrpc_request_pool *pool, int num_rq)
+{
+ int i;
+ int size = 1;
+
+ while (size < pool->prp_rq_size)
+ size <<= 1;
+
+ LASSERTF(list_empty(&pool->prp_req_list) ||
+ size == pool->prp_rq_size,
+ "Trying to change pool size with nonempty pool from %d to %d bytes\n",
+ pool->prp_rq_size, size);
+
+ spin_lock(&pool->prp_lock);
+ pool->prp_rq_size = size;
+ for (i = 0; i < num_rq; i++) {
+ struct ptlrpc_request *req;
+ struct lustre_msg *msg;
+
+ spin_unlock(&pool->prp_lock);
+ req = ptlrpc_request_cache_alloc(GFP_NOFS);
+ if (!req)
+ return;
+ OBD_ALLOC_LARGE(msg, size);
+ if (!msg) {
+ ptlrpc_request_cache_free(req);
+ return;
+ }
+ req->rq_reqbuf = msg;
+ req->rq_reqbuf_len = size;
+ req->rq_pool = pool;
+ spin_lock(&pool->prp_lock);
+ list_add_tail(&req->rq_list, &pool->prp_req_list);
+ }
+ spin_unlock(&pool->prp_lock);
+}
+EXPORT_SYMBOL(ptlrpc_add_rqs_to_pool);
+
+/**
+ * Create and initialize new request pool with given attributes:
+ * \a num_rq - initial number of requests to create for the pool
+ * \a msgsize - maximum message size possible for requests in thid pool
+ * \a populate_pool - function to be called when more requests need to be added
+ * to the pool
+ * Returns pointer to newly created pool or NULL on error.
+ */
+struct ptlrpc_request_pool *
+ptlrpc_init_rq_pool(int num_rq, int msgsize,
+ void (*populate_pool)(struct ptlrpc_request_pool *, int))
+{
+ struct ptlrpc_request_pool *pool;
+
+ OBD_ALLOC(pool, sizeof(struct ptlrpc_request_pool));
+ if (!pool)
+ return NULL;
+
+ /* Request next power of two for the allocation, because internally
+ kernel would do exactly this */
+
+ spin_lock_init(&pool->prp_lock);
+ INIT_LIST_HEAD(&pool->prp_req_list);
+ pool->prp_rq_size = msgsize + SPTLRPC_MAX_PAYLOAD;
+ pool->prp_populate = populate_pool;
+
+ populate_pool(pool, num_rq);
+
+ if (list_empty(&pool->prp_req_list)) {
+ /* have not allocated a single request for the pool */
+ OBD_FREE(pool, sizeof(struct ptlrpc_request_pool));
+ pool = NULL;
+ }
+ return pool;
+}
+EXPORT_SYMBOL(ptlrpc_init_rq_pool);
+
+/**
+ * Fetches one request from pool \a pool
+ */
+static struct ptlrpc_request *
+ptlrpc_prep_req_from_pool(struct ptlrpc_request_pool *pool)
+{
+ struct ptlrpc_request *request;
+ struct lustre_msg *reqbuf;
+
+ if (!pool)
+ return NULL;
+
+ spin_lock(&pool->prp_lock);
+
+ /* See if we have anything in a pool, and bail out if nothing,
+ * in writeout path, where this matters, this is safe to do, because
+ * nothing is lost in this case, and when some in-flight requests
+ * complete, this code will be called again. */
+ if (unlikely(list_empty(&pool->prp_req_list))) {
+ spin_unlock(&pool->prp_lock);
+ return NULL;
+ }
+
+ request = list_entry(pool->prp_req_list.next, struct ptlrpc_request,
+ rq_list);
+ list_del_init(&request->rq_list);
+ spin_unlock(&pool->prp_lock);
+
+ LASSERT(request->rq_reqbuf);
+ LASSERT(request->rq_pool);
+
+ reqbuf = request->rq_reqbuf;
+ memset(request, 0, sizeof(*request));
+ request->rq_reqbuf = reqbuf;
+ request->rq_reqbuf_len = pool->prp_rq_size;
+ request->rq_pool = pool;
+
+ return request;
+}
+
+/**
+ * Returns freed \a request to pool.
+ */
+static void __ptlrpc_free_req_to_pool(struct ptlrpc_request *request)
+{
+ struct ptlrpc_request_pool *pool = request->rq_pool;
+
+ spin_lock(&pool->prp_lock);
+ LASSERT(list_empty(&request->rq_list));
+ LASSERT(!request->rq_receiving_reply);
+ list_add_tail(&request->rq_list, &pool->prp_req_list);
+ spin_unlock(&pool->prp_lock);
+}
+
+static int __ptlrpc_request_bufs_pack(struct ptlrpc_request *request,
+ __u32 version, int opcode,
+ int count, __u32 *lengths, char **bufs,
+ struct ptlrpc_cli_ctx *ctx)
+{
+ struct obd_import *imp = request->rq_import;
+ int rc;
+
+ if (unlikely(ctx))
+ request->rq_cli_ctx = sptlrpc_cli_ctx_get(ctx);
+ else {
+ rc = sptlrpc_req_get_ctx(request);
+ if (rc)
+ goto out_free;
+ }
+
+ sptlrpc_req_set_flavor(request, opcode);
+
+ rc = lustre_pack_request(request, imp->imp_msg_magic, count,
+ lengths, bufs);
+ if (rc) {
+ LASSERT(!request->rq_pool);
+ goto out_ctx;
+ }
+
+ lustre_msg_add_version(request->rq_reqmsg, version);
+ request->rq_send_state = LUSTRE_IMP_FULL;
+ request->rq_type = PTL_RPC_MSG_REQUEST;
+ request->rq_export = NULL;
+
+ request->rq_req_cbid.cbid_fn = request_out_callback;
+ request->rq_req_cbid.cbid_arg = request;
+
+ request->rq_reply_cbid.cbid_fn = reply_in_callback;
+ request->rq_reply_cbid.cbid_arg = request;
+
+ request->rq_reply_deadline = 0;
+ request->rq_phase = RQ_PHASE_NEW;
+ request->rq_next_phase = RQ_PHASE_UNDEFINED;
+
+ request->rq_request_portal = imp->imp_client->cli_request_portal;
+ request->rq_reply_portal = imp->imp_client->cli_reply_portal;
+
+ ptlrpc_at_set_req_timeout(request);
+
+ spin_lock_init(&request->rq_lock);
+ INIT_LIST_HEAD(&request->rq_list);
+ INIT_LIST_HEAD(&request->rq_timed_list);
+ INIT_LIST_HEAD(&request->rq_replay_list);
+ INIT_LIST_HEAD(&request->rq_ctx_chain);
+ INIT_LIST_HEAD(&request->rq_set_chain);
+ INIT_LIST_HEAD(&request->rq_history_list);
+ INIT_LIST_HEAD(&request->rq_exp_list);
+ init_waitqueue_head(&request->rq_reply_waitq);
+ init_waitqueue_head(&request->rq_set_waitq);
+ request->rq_xid = ptlrpc_next_xid();
+ atomic_set(&request->rq_refcount, 1);
+
+ lustre_msg_set_opc(request->rq_reqmsg, opcode);
+
+ return 0;
+out_ctx:
+ sptlrpc_cli_ctx_put(request->rq_cli_ctx, 1);
+out_free:
+ class_import_put(imp);
+ return rc;
+}
+
+int ptlrpc_request_bufs_pack(struct ptlrpc_request *request,
+ __u32 version, int opcode, char **bufs,
+ struct ptlrpc_cli_ctx *ctx)
+{
+ int count;
+
+ count = req_capsule_filled_sizes(&request->rq_pill, RCL_CLIENT);
+ return __ptlrpc_request_bufs_pack(request, version, opcode, count,
+ request->rq_pill.rc_area[RCL_CLIENT],
+ bufs, ctx);
+}
+EXPORT_SYMBOL(ptlrpc_request_bufs_pack);
+
+/**
+ * Pack request buffers for network transfer, performing necessary encryption
+ * steps if necessary.
+ */
+int ptlrpc_request_pack(struct ptlrpc_request *request,
+ __u32 version, int opcode)
+{
+ int rc;
+ rc = ptlrpc_request_bufs_pack(request, version, opcode, NULL, NULL);
+ if (rc)
+ return rc;
+
+ /* For some old 1.8 clients (< 1.8.7), they will LASSERT the size of
+ * ptlrpc_body sent from server equal to local ptlrpc_body size, so we
+ * have to send old ptlrpc_body to keep interoperability with these
+ * clients.
+ *
+ * Only three kinds of server->client RPCs so far:
+ * - LDLM_BL_CALLBACK
+ * - LDLM_CP_CALLBACK
+ * - LDLM_GL_CALLBACK
+ *
+ * XXX This should be removed whenever we drop the interoperability with
+ * the these old clients.
+ */
+ if (opcode == LDLM_BL_CALLBACK || opcode == LDLM_CP_CALLBACK ||
+ opcode == LDLM_GL_CALLBACK)
+ req_capsule_shrink(&request->rq_pill, &RMF_PTLRPC_BODY,
+ sizeof(struct ptlrpc_body_v2), RCL_CLIENT);
+
+ return rc;
+}
+EXPORT_SYMBOL(ptlrpc_request_pack);
+
+/**
+ * Helper function to allocate new request on import \a imp
+ * and possibly using existing request from pool \a pool if provided.
+ * Returns allocated request structure with import field filled or
+ * NULL on error.
+ */
+static inline
+struct ptlrpc_request *__ptlrpc_request_alloc(struct obd_import *imp,
+ struct ptlrpc_request_pool *pool)
+{
+ struct ptlrpc_request *request = NULL;
+
+ if (pool)
+ request = ptlrpc_prep_req_from_pool(pool);
+
+ if (!request)
+ request = ptlrpc_request_cache_alloc(GFP_NOFS);
+
+ if (request) {
+ LASSERTF((unsigned long)imp > 0x1000, "%p", imp);
+ LASSERT(imp != LP_POISON);
+ LASSERTF((unsigned long)imp->imp_client > 0x1000, "%p",
+ imp->imp_client);
+ LASSERT(imp->imp_client != LP_POISON);
+
+ request->rq_import = class_import_get(imp);
+ } else {
+ CERROR("request allocation out of memory\n");
+ }
+
+ return request;
+}
+
+/**
+ * Helper function for creating a request.
+ * Calls __ptlrpc_request_alloc to allocate new request structure and inits
+ * buffer structures according to capsule template \a format.
+ * Returns allocated request structure pointer or NULL on error.
+ */
+static struct ptlrpc_request *
+ptlrpc_request_alloc_internal(struct obd_import *imp,
+ struct ptlrpc_request_pool *pool,
+ const struct req_format *format)
+{
+ struct ptlrpc_request *request;
+
+ request = __ptlrpc_request_alloc(imp, pool);
+ if (request == NULL)
+ return NULL;
+
+ req_capsule_init(&request->rq_pill, request, RCL_CLIENT);
+ req_capsule_set(&request->rq_pill, format);
+ return request;
+}
+
+/**
+ * Allocate new request structure for import \a imp and initialize its
+ * buffer structure according to capsule template \a format.
+ */
+struct ptlrpc_request *ptlrpc_request_alloc(struct obd_import *imp,
+ const struct req_format *format)
+{
+ return ptlrpc_request_alloc_internal(imp, NULL, format);
+}
+EXPORT_SYMBOL(ptlrpc_request_alloc);
+
+/**
+ * Allocate new request structure for import \a imp from pool \a pool and
+ * initialize its buffer structure according to capsule template \a format.
+ */
+struct ptlrpc_request *ptlrpc_request_alloc_pool(struct obd_import *imp,
+ struct ptlrpc_request_pool *pool,
+ const struct req_format *format)
+{
+ return ptlrpc_request_alloc_internal(imp, pool, format);
+}
+EXPORT_SYMBOL(ptlrpc_request_alloc_pool);
+
+/**
+ * For requests not from pool, free memory of the request structure.
+ * For requests obtained from a pool earlier, return request back to pool.
+ */
+void ptlrpc_request_free(struct ptlrpc_request *request)
+{
+ if (request->rq_pool)
+ __ptlrpc_free_req_to_pool(request);
+ else
+ ptlrpc_request_cache_free(request);
+}
+EXPORT_SYMBOL(ptlrpc_request_free);
+
+/**
+ * Allocate new request for operation \a opcode and immediately pack it for
+ * network transfer.
+ * Only used for simple requests like OBD_PING where the only important
+ * part of the request is operation itself.
+ * Returns allocated request or NULL on error.
+ */
+struct ptlrpc_request *ptlrpc_request_alloc_pack(struct obd_import *imp,
+ const struct req_format *format,
+ __u32 version, int opcode)
+{
+ struct ptlrpc_request *req = ptlrpc_request_alloc(imp, format);
+ int rc;
+
+ if (req) {
+ rc = ptlrpc_request_pack(req, version, opcode);
+ if (rc) {
+ ptlrpc_request_free(req);
+ req = NULL;
+ }
+ }
+ return req;
+}
+EXPORT_SYMBOL(ptlrpc_request_alloc_pack);
+
+/**
+ * Prepare request (fetched from pool \a pool if not NULL) on import \a imp
+ * for operation \a opcode. Request would contain \a count buffers.
+ * Sizes of buffers are described in array \a lengths and buffers themselves
+ * are provided by a pointer \a bufs.
+ * Returns prepared request structure pointer or NULL on error.
+ */
+struct ptlrpc_request *
+ptlrpc_prep_req_pool(struct obd_import *imp,
+ __u32 version, int opcode,
+ int count, __u32 *lengths, char **bufs,
+ struct ptlrpc_request_pool *pool)
+{
+ struct ptlrpc_request *request;
+ int rc;
+
+ request = __ptlrpc_request_alloc(imp, pool);
+ if (!request)
+ return NULL;
+
+ rc = __ptlrpc_request_bufs_pack(request, version, opcode, count,
+ lengths, bufs, NULL);
+ if (rc) {
+ ptlrpc_request_free(request);
+ request = NULL;
+ }
+ return request;
+}
+EXPORT_SYMBOL(ptlrpc_prep_req_pool);
+
+/**
+ * Same as ptlrpc_prep_req_pool, but without pool
+ */
+struct ptlrpc_request *
+ptlrpc_prep_req(struct obd_import *imp, __u32 version, int opcode, int count,
+ __u32 *lengths, char **bufs)
+{
+ return ptlrpc_prep_req_pool(imp, version, opcode, count, lengths, bufs,
+ NULL);
+}
+EXPORT_SYMBOL(ptlrpc_prep_req);
+
+/**
+ * Allocate and initialize new request set structure.
+ * Returns a pointer to the newly allocated set structure or NULL on error.
+ */
+struct ptlrpc_request_set *ptlrpc_prep_set(void)
+{
+ struct ptlrpc_request_set *set;
+
+ OBD_ALLOC(set, sizeof(*set));
+ if (!set)
+ return NULL;
+ atomic_set(&set->set_refcount, 1);
+ INIT_LIST_HEAD(&set->set_requests);
+ init_waitqueue_head(&set->set_waitq);
+ atomic_set(&set->set_new_count, 0);
+ atomic_set(&set->set_remaining, 0);
+ spin_lock_init(&set->set_new_req_lock);
+ INIT_LIST_HEAD(&set->set_new_requests);
+ INIT_LIST_HEAD(&set->set_cblist);
+ set->set_max_inflight = UINT_MAX;
+ set->set_producer = NULL;
+ set->set_producer_arg = NULL;
+ set->set_rc = 0;
+
+ return set;
+}
+EXPORT_SYMBOL(ptlrpc_prep_set);
+
+/**
+ * Allocate and initialize new request set structure with flow control
+ * extension. This extension allows to control the number of requests in-flight
+ * for the whole set. A callback function to generate requests must be provided
+ * and the request set will keep the number of requests sent over the wire to
+ * @max_inflight.
+ * Returns a pointer to the newly allocated set structure or NULL on error.
+ */
+struct ptlrpc_request_set *ptlrpc_prep_fcset(int max, set_producer_func func,
+ void *arg)
+
+{
+ struct ptlrpc_request_set *set;
+
+ set = ptlrpc_prep_set();
+ if (!set)
+ return NULL;
+
+ set->set_max_inflight = max;
+ set->set_producer = func;
+ set->set_producer_arg = arg;
+
+ return set;
+}
+EXPORT_SYMBOL(ptlrpc_prep_fcset);
+
+/**
+ * Wind down and free request set structure previously allocated with
+ * ptlrpc_prep_set.
+ * Ensures that all requests on the set have completed and removes
+ * all requests from the request list in a set.
+ * If any unsent request happen to be on the list, pretends that they got
+ * an error in flight and calls their completion handler.
+ */
+void ptlrpc_set_destroy(struct ptlrpc_request_set *set)
+{
+ struct list_head *tmp;
+ struct list_head *next;
+ int expected_phase;
+ int n = 0;
+
+ /* Requests on the set should either all be completed, or all be new */
+ expected_phase = (atomic_read(&set->set_remaining) == 0) ?
+ RQ_PHASE_COMPLETE : RQ_PHASE_NEW;
+ list_for_each(tmp, &set->set_requests) {
+ struct ptlrpc_request *req =
+ list_entry(tmp, struct ptlrpc_request,
+ rq_set_chain);
+
+ LASSERT(req->rq_phase == expected_phase);
+ n++;
+ }
+
+ LASSERTF(atomic_read(&set->set_remaining) == 0 ||
+ atomic_read(&set->set_remaining) == n, "%d / %d\n",
+ atomic_read(&set->set_remaining), n);
+
+ list_for_each_safe(tmp, next, &set->set_requests) {
+ struct ptlrpc_request *req =
+ list_entry(tmp, struct ptlrpc_request,
+ rq_set_chain);
+ list_del_init(&req->rq_set_chain);
+
+ LASSERT(req->rq_phase == expected_phase);
+
+ if (req->rq_phase == RQ_PHASE_NEW) {
+ ptlrpc_req_interpret(NULL, req, -EBADR);
+ atomic_dec(&set->set_remaining);
+ }
+
+ spin_lock(&req->rq_lock);
+ req->rq_set = NULL;
+ req->rq_invalid_rqset = 0;
+ spin_unlock(&req->rq_lock);
+
+ ptlrpc_req_finished(req);
+ }
+
+ LASSERT(atomic_read(&set->set_remaining) == 0);
+
+ ptlrpc_reqset_put(set);
+}
+EXPORT_SYMBOL(ptlrpc_set_destroy);
+
+/**
+ * Add a callback function \a fn to the set.
+ * This function would be called when all requests on this set are completed.
+ * The function will be passed \a data argument.
+ */
+int ptlrpc_set_add_cb(struct ptlrpc_request_set *set,
+ set_interpreter_func fn, void *data)
+{
+ struct ptlrpc_set_cbdata *cbdata;
+
+ OBD_ALLOC_PTR(cbdata);
+ if (cbdata == NULL)
+ return -ENOMEM;
+
+ cbdata->psc_interpret = fn;
+ cbdata->psc_data = data;
+ list_add_tail(&cbdata->psc_item, &set->set_cblist);
+
+ return 0;
+}
+EXPORT_SYMBOL(ptlrpc_set_add_cb);
+
+/**
+ * Add a new request to the general purpose request set.
+ * Assumes request reference from the caller.
+ */
+void ptlrpc_set_add_req(struct ptlrpc_request_set *set,
+ struct ptlrpc_request *req)
+{
+ LASSERT(list_empty(&req->rq_set_chain));
+
+ /* The set takes over the caller's request reference */
+ list_add_tail(&req->rq_set_chain, &set->set_requests);
+ req->rq_set = set;
+ atomic_inc(&set->set_remaining);
+ req->rq_queued_time = cfs_time_current();
+
+ if (req->rq_reqmsg != NULL)
+ lustre_msg_set_jobid(req->rq_reqmsg, NULL);
+
+ if (set->set_producer != NULL)
+ /* If the request set has a producer callback, the RPC must be
+ * sent straight away */
+ ptlrpc_send_new_req(req);
+}
+EXPORT_SYMBOL(ptlrpc_set_add_req);
+
+/**
+ * Add a request to a request with dedicated server thread
+ * and wake the thread to make any necessary processing.
+ * Currently only used for ptlrpcd.
+ */
+void ptlrpc_set_add_new_req(struct ptlrpcd_ctl *pc,
+ struct ptlrpc_request *req)
+{
+ struct ptlrpc_request_set *set = pc->pc_set;
+ int count, i;
+
+ LASSERT(req->rq_set == NULL);
+ LASSERT(test_bit(LIOD_STOP, &pc->pc_flags) == 0);
+
+ spin_lock(&set->set_new_req_lock);
+ /*
+ * The set takes over the caller's request reference.
+ */
+ req->rq_set = set;
+ req->rq_queued_time = cfs_time_current();
+ list_add_tail(&req->rq_set_chain, &set->set_new_requests);
+ count = atomic_inc_return(&set->set_new_count);
+ spin_unlock(&set->set_new_req_lock);
+
+ /* Only need to call wakeup once for the first entry. */
+ if (count == 1) {
+ wake_up(&set->set_waitq);
+
+ /* XXX: It maybe unnecessary to wakeup all the partners. But to
+ * guarantee the async RPC can be processed ASAP, we have
+ * no other better choice. It maybe fixed in future. */
+ for (i = 0; i < pc->pc_npartners; i++)
+ wake_up(&pc->pc_partners[i]->pc_set->set_waitq);
+ }
+}
+EXPORT_SYMBOL(ptlrpc_set_add_new_req);
+
+/**
+ * Based on the current state of the import, determine if the request
+ * can be sent, is an error, or should be delayed.
+ *
+ * Returns true if this request should be delayed. If false, and
+ * *status is set, then the request can not be sent and *status is the
+ * error code. If false and status is 0, then request can be sent.
+ *
+ * The imp->imp_lock must be held.
+ */
+static int ptlrpc_import_delay_req(struct obd_import *imp,
+ struct ptlrpc_request *req, int *status)
+{
+ int delay = 0;
+
+ LASSERT(status != NULL);
+ *status = 0;
+
+ if (req->rq_ctx_init || req->rq_ctx_fini) {
+ /* always allow ctx init/fini rpc go through */
+ } else if (imp->imp_state == LUSTRE_IMP_NEW) {
+ DEBUG_REQ(D_ERROR, req, "Uninitialized import.");
+ *status = -EIO;
+ } else if (imp->imp_state == LUSTRE_IMP_CLOSED) {
+ /* pings may safely race with umount */
+ DEBUG_REQ(lustre_msg_get_opc(req->rq_reqmsg) == OBD_PING ?
+ D_HA : D_ERROR, req, "IMP_CLOSED ");
+ *status = -EIO;
+ } else if (ptlrpc_send_limit_expired(req)) {
+ /* probably doesn't need to be a D_ERROR after initial testing */
+ DEBUG_REQ(D_ERROR, req, "send limit expired ");
+ *status = -EIO;
+ } else if (req->rq_send_state == LUSTRE_IMP_CONNECTING &&
+ imp->imp_state == LUSTRE_IMP_CONNECTING) {
+ /* allow CONNECT even if import is invalid */
+ if (atomic_read(&imp->imp_inval_count) != 0) {
+ DEBUG_REQ(D_ERROR, req, "invalidate in flight");
+ *status = -EIO;
+ }
+ } else if (imp->imp_invalid || imp->imp_obd->obd_no_recov) {
+ if (!imp->imp_deactive)
+ DEBUG_REQ(D_NET, req, "IMP_INVALID");
+ *status = -ESHUTDOWN; /* bz 12940 */
+ } else if (req->rq_import_generation != imp->imp_generation) {
+ DEBUG_REQ(D_ERROR, req, "req wrong generation:");
+ *status = -EIO;
+ } else if (req->rq_send_state != imp->imp_state) {
+ /* invalidate in progress - any requests should be drop */
+ if (atomic_read(&imp->imp_inval_count) != 0) {
+ DEBUG_REQ(D_ERROR, req, "invalidate in flight");
+ *status = -EIO;
+ } else if (imp->imp_dlm_fake || req->rq_no_delay) {
+ *status = -EWOULDBLOCK;
+ } else if (req->rq_allow_replay &&
+ (imp->imp_state == LUSTRE_IMP_REPLAY ||
+ imp->imp_state == LUSTRE_IMP_REPLAY_LOCKS ||
+ imp->imp_state == LUSTRE_IMP_REPLAY_WAIT ||
+ imp->imp_state == LUSTRE_IMP_RECOVER)) {
+ DEBUG_REQ(D_HA, req, "allow during recovery.\n");
+ } else {
+ delay = 1;
+ }
+ }
+
+ return delay;
+}
+
+/**
+ * Decide if the error message regarding provided request \a req
+ * should be printed to the console or not.
+ * Makes it's decision on request status and other properties.
+ * Returns 1 to print error on the system console or 0 if not.
+ */
+static int ptlrpc_console_allow(struct ptlrpc_request *req)
+{
+ __u32 opc;
+ int err;
+
+ LASSERT(req->rq_reqmsg != NULL);
+ opc = lustre_msg_get_opc(req->rq_reqmsg);
+
+ /* Suppress particular reconnect errors which are to be expected. No
+ * errors are suppressed for the initial connection on an import */
+ if ((lustre_handle_is_used(&req->rq_import->imp_remote_handle)) &&
+ (opc == OST_CONNECT || opc == MDS_CONNECT || opc == MGS_CONNECT)) {
+
+ /* Suppress timed out reconnect requests */
+ if (req->rq_timedout)
+ return 0;
+
+ /* Suppress unavailable/again reconnect requests */
+ err = lustre_msg_get_status(req->rq_repmsg);
+ if (err == -ENODEV || err == -EAGAIN)
+ return 0;
+ }
+
+ return 1;
+}
+
+/**
+ * Check request processing status.
+ * Returns the status.
+ */
+static int ptlrpc_check_status(struct ptlrpc_request *req)
+{
+ int err;
+
+ err = lustre_msg_get_status(req->rq_repmsg);
+ if (lustre_msg_get_type(req->rq_repmsg) == PTL_RPC_MSG_ERR) {
+ struct obd_import *imp = req->rq_import;
+ __u32 opc = lustre_msg_get_opc(req->rq_reqmsg);
+ if (ptlrpc_console_allow(req))
+ LCONSOLE_ERROR_MSG(0x011, "%s: Communicating with %s, operation %s failed with %d.\n",
+ imp->imp_obd->obd_name,
+ libcfs_nid2str(
+ imp->imp_connection->c_peer.nid),
+ ll_opcode2str(opc), err);
+ return err < 0 ? err : -EINVAL;
+ }
+
+ if (err < 0) {
+ DEBUG_REQ(D_INFO, req, "status is %d", err);
+ } else if (err > 0) {
+ /* XXX: translate this error from net to host */
+ DEBUG_REQ(D_INFO, req, "status is %d", err);
+ }
+
+ return err;
+}
+
+/**
+ * save pre-versions of objects into request for replay.
+ * Versions are obtained from server reply.
+ * used for VBR.
+ */
+static void ptlrpc_save_versions(struct ptlrpc_request *req)
+{
+ struct lustre_msg *repmsg = req->rq_repmsg;
+ struct lustre_msg *reqmsg = req->rq_reqmsg;
+ __u64 *versions = lustre_msg_get_versions(repmsg);
+
+ if (lustre_msg_get_flags(req->rq_reqmsg) & MSG_REPLAY)
+ return;
+
+ LASSERT(versions);
+ lustre_msg_set_versions(reqmsg, versions);
+ CDEBUG(D_INFO, "Client save versions [%#llx/%#llx]\n",
+ versions[0], versions[1]);
+}
+
+/**
+ * Callback function called when client receives RPC reply for \a req.
+ * Returns 0 on success or error code.
+ * The return value would be assigned to req->rq_status by the caller
+ * as request processing status.
+ * This function also decides if the request needs to be saved for later replay.
+ */
+static int after_reply(struct ptlrpc_request *req)
+{
+ struct obd_import *imp = req->rq_import;
+ struct obd_device *obd = req->rq_import->imp_obd;
+ int rc;
+ struct timeval work_start;
+ long timediff;
+
+ LASSERT(obd != NULL);
+ /* repbuf must be unlinked */
+ LASSERT(!req->rq_receiving_reply && !req->rq_reply_unlink);
+
+ if (req->rq_reply_truncate) {
+ if (ptlrpc_no_resend(req)) {
+ DEBUG_REQ(D_ERROR, req, "reply buffer overflow, expected: %d, actual size: %d",
+ req->rq_nob_received, req->rq_repbuf_len);
+ return -EOVERFLOW;
+ }
+
+ sptlrpc_cli_free_repbuf(req);
+ /* Pass the required reply buffer size (include
+ * space for early reply).
+ * NB: no need to roundup because alloc_repbuf
+ * will roundup it */
+ req->rq_replen = req->rq_nob_received;
+ req->rq_nob_received = 0;
+ spin_lock(&req->rq_lock);
+ req->rq_resend = 1;
+ spin_unlock(&req->rq_lock);
+ return 0;
+ }
+
+ /*
+ * NB Until this point, the whole of the incoming message,
+ * including buflens, status etc is in the sender's byte order.
+ */
+ rc = sptlrpc_cli_unwrap_reply(req);
+ if (rc) {
+ DEBUG_REQ(D_ERROR, req, "unwrap reply failed (%d):", rc);
+ return rc;
+ }
+
+ /*
+ * Security layer unwrap might ask resend this request.
+ */
+ if (req->rq_resend)
+ return 0;
+
+ rc = unpack_reply(req);
+ if (rc)
+ return rc;
+
+ /* retry indefinitely on EINPROGRESS */
+ if (lustre_msg_get_status(req->rq_repmsg) == -EINPROGRESS &&
+ ptlrpc_no_resend(req) == 0 && !req->rq_no_retry_einprogress) {
+ time_t now = get_seconds();
+
+ DEBUG_REQ(D_RPCTRACE, req, "Resending request on EINPROGRESS");
+ spin_lock(&req->rq_lock);
+ req->rq_resend = 1;
+ spin_unlock(&req->rq_lock);
+ req->rq_nr_resend++;
+
+ /* allocate new xid to avoid reply reconstruction */
+ if (!req->rq_bulk) {
+ /* new xid is already allocated for bulk in
+ * ptlrpc_check_set() */
+ req->rq_xid = ptlrpc_next_xid();
+ DEBUG_REQ(D_RPCTRACE, req, "Allocating new xid for resend on EINPROGRESS");
+ }
+
+ /* Readjust the timeout for current conditions */
+ ptlrpc_at_set_req_timeout(req);
+ /* delay resend to give a chance to the server to get ready.
+ * The delay is increased by 1s on every resend and is capped to
+ * the current request timeout (i.e. obd_timeout if AT is off,
+ * or AT service time x 125% + 5s, see at_est2timeout) */
+ if (req->rq_nr_resend > req->rq_timeout)
+ req->rq_sent = now + req->rq_timeout;
+ else
+ req->rq_sent = now + req->rq_nr_resend;
+
+ return 0;
+ }
+
+ do_gettimeofday(&work_start);
+ timediff = cfs_timeval_sub(&work_start, &req->rq_arrival_time, NULL);
+ if (obd->obd_svc_stats != NULL) {
+ lprocfs_counter_add(obd->obd_svc_stats, PTLRPC_REQWAIT_CNTR,
+ timediff);
+ ptlrpc_lprocfs_rpc_sent(req, timediff);
+ }
+
+ if (lustre_msg_get_type(req->rq_repmsg) != PTL_RPC_MSG_REPLY &&
+ lustre_msg_get_type(req->rq_repmsg) != PTL_RPC_MSG_ERR) {
+ DEBUG_REQ(D_ERROR, req, "invalid packet received (type=%u)",
+ lustre_msg_get_type(req->rq_repmsg));
+ return -EPROTO;
+ }
+
+ if (lustre_msg_get_opc(req->rq_reqmsg) != OBD_PING)
+ CFS_FAIL_TIMEOUT(OBD_FAIL_PTLRPC_PAUSE_REP, cfs_fail_val);
+ ptlrpc_at_adj_service(req, lustre_msg_get_timeout(req->rq_repmsg));
+ ptlrpc_at_adj_net_latency(req,
+ lustre_msg_get_service_time(req->rq_repmsg));
+
+ rc = ptlrpc_check_status(req);
+ imp->imp_connect_error = rc;
+
+ if (rc) {
+ /*
+ * Either we've been evicted, or the server has failed for
+ * some reason. Try to reconnect, and if that fails, punt to
+ * the upcall.
+ */
+ if (ll_rpc_recoverable_error(rc)) {
+ if (req->rq_send_state != LUSTRE_IMP_FULL ||
+ imp->imp_obd->obd_no_recov || imp->imp_dlm_fake) {
+ return rc;
+ }
+ ptlrpc_request_handle_notconn(req);
+ return rc;
+ }
+ } else {
+ /*
+ * Let's look if server sent slv. Do it only for RPC with
+ * rc == 0.
+ */
+ ldlm_cli_update_pool(req);
+ }
+
+ /*
+ * Store transno in reqmsg for replay.
+ */
+ if (!(lustre_msg_get_flags(req->rq_reqmsg) & MSG_REPLAY)) {
+ req->rq_transno = lustre_msg_get_transno(req->rq_repmsg);
+ lustre_msg_set_transno(req->rq_reqmsg, req->rq_transno);
+ }
+
+ if (imp->imp_replayable) {
+ spin_lock(&imp->imp_lock);
+ /*
+ * No point in adding already-committed requests to the replay
+ * list, we will just remove them immediately. b=9829
+ */
+ if (req->rq_transno != 0 &&
+ (req->rq_transno >
+ lustre_msg_get_last_committed(req->rq_repmsg) ||
+ req->rq_replay)) {
+ /** version recovery */
+ ptlrpc_save_versions(req);
+ ptlrpc_retain_replayable_request(req, imp);
+ } else if (req->rq_commit_cb != NULL &&
+ list_empty(&req->rq_replay_list)) {
+ /* NB: don't call rq_commit_cb if it's already on
+ * rq_replay_list, ptlrpc_free_committed() will call
+ * it later, see LU-3618 for details */
+ spin_unlock(&imp->imp_lock);
+ req->rq_commit_cb(req);
+ spin_lock(&imp->imp_lock);
+ }
+
+ /*
+ * Replay-enabled imports return commit-status information.
+ */
+ if (lustre_msg_get_last_committed(req->rq_repmsg)) {
+ imp->imp_peer_committed_transno =
+ lustre_msg_get_last_committed(req->rq_repmsg);
+ }
+
+ ptlrpc_free_committed(imp);
+
+ if (!list_empty(&imp->imp_replay_list)) {
+ struct ptlrpc_request *last;
+
+ last = list_entry(imp->imp_replay_list.prev,
+ struct ptlrpc_request,
+ rq_replay_list);
+ /*
+ * Requests with rq_replay stay on the list even if no
+ * commit is expected.
+ */
+ if (last->rq_transno > imp->imp_peer_committed_transno)
+ ptlrpc_pinger_commit_expected(imp);
+ }
+
+ spin_unlock(&imp->imp_lock);
+ }
+
+ return rc;
+}
+
+/**
+ * Helper function to send request \a req over the network for the first time
+ * Also adjusts request phase.
+ * Returns 0 on success or error code.
+ */
+static int ptlrpc_send_new_req(struct ptlrpc_request *req)
+{
+ struct obd_import *imp = req->rq_import;
+ int rc;
+
+ LASSERT(req->rq_phase == RQ_PHASE_NEW);
+ if (req->rq_sent && (req->rq_sent > get_seconds()) &&
+ (!req->rq_generation_set ||
+ req->rq_import_generation == imp->imp_generation))
+ return 0;
+
+ ptlrpc_rqphase_move(req, RQ_PHASE_RPC);
+
+ spin_lock(&imp->imp_lock);
+
+ if (!req->rq_generation_set)
+ req->rq_import_generation = imp->imp_generation;
+
+ if (ptlrpc_import_delay_req(imp, req, &rc)) {
+ spin_lock(&req->rq_lock);
+ req->rq_waiting = 1;
+ spin_unlock(&req->rq_lock);
+
+ DEBUG_REQ(D_HA, req, "req from PID %d waiting for recovery: (%s != %s)",
+ lustre_msg_get_status(req->rq_reqmsg),
+ ptlrpc_import_state_name(req->rq_send_state),
+ ptlrpc_import_state_name(imp->imp_state));
+ LASSERT(list_empty(&req->rq_list));
+ list_add_tail(&req->rq_list, &imp->imp_delayed_list);
+ atomic_inc(&req->rq_import->imp_inflight);
+ spin_unlock(&imp->imp_lock);
+ return 0;
+ }
+
+ if (rc != 0) {
+ spin_unlock(&imp->imp_lock);
+ req->rq_status = rc;
+ ptlrpc_rqphase_move(req, RQ_PHASE_INTERPRET);
+ return rc;
+ }
+
+ LASSERT(list_empty(&req->rq_list));
+ list_add_tail(&req->rq_list, &imp->imp_sending_list);
+ atomic_inc(&req->rq_import->imp_inflight);
+ spin_unlock(&imp->imp_lock);
+
+ lustre_msg_set_status(req->rq_reqmsg, current_pid());
+
+ rc = sptlrpc_req_refresh_ctx(req, -1);
+ if (rc) {
+ if (req->rq_err) {
+ req->rq_status = rc;
+ return 1;
+ }
+ spin_lock(&req->rq_lock);
+ req->rq_wait_ctx = 1;
+ spin_unlock(&req->rq_lock);
+ return 0;
+ }
+
+ CDEBUG(D_RPCTRACE, "Sending RPC pname:cluuid:pid:xid:nid:opc %s:%s:%d:%llu:%s:%d\n",
+ current_comm(),
+ imp->imp_obd->obd_uuid.uuid,
+ lustre_msg_get_status(req->rq_reqmsg), req->rq_xid,
+ libcfs_nid2str(imp->imp_connection->c_peer.nid),
+ lustre_msg_get_opc(req->rq_reqmsg));
+
+ rc = ptl_send_rpc(req, 0);
+ if (rc) {
+ DEBUG_REQ(D_HA, req, "send failed (%d); expect timeout", rc);
+ spin_lock(&req->rq_lock);
+ req->rq_net_err = 1;
+ spin_unlock(&req->rq_lock);
+ return rc;
+ }
+ return 0;
+}
+
+static inline int ptlrpc_set_producer(struct ptlrpc_request_set *set)
+{
+ int remaining, rc;
+
+ LASSERT(set->set_producer != NULL);
+
+ remaining = atomic_read(&set->set_remaining);
+
+ /* populate the ->set_requests list with requests until we
+ * reach the maximum number of RPCs in flight for this set */
+ while (atomic_read(&set->set_remaining) < set->set_max_inflight) {
+ rc = set->set_producer(set, set->set_producer_arg);
+ if (rc == -ENOENT) {
+ /* no more RPC to produce */
+ set->set_producer = NULL;
+ set->set_producer_arg = NULL;
+ return 0;
+ }
+ }
+
+ return (atomic_read(&set->set_remaining) - remaining);
+}
+
+/**
+ * this sends any unsent RPCs in \a set and returns 1 if all are sent
+ * and no more replies are expected.
+ * (it is possible to get less replies than requests sent e.g. due to timed out
+ * requests or requests that we had trouble to send out)
+ *
+ * NOTE: This function contains a potential schedule point (cond_resched()).
+ */
+int ptlrpc_check_set(const struct lu_env *env, struct ptlrpc_request_set *set)
+{
+ struct list_head *tmp, *next;
+ struct list_head comp_reqs;
+ int force_timer_recalc = 0;
+
+ if (atomic_read(&set->set_remaining) == 0)
+ return 1;
+
+ INIT_LIST_HEAD(&comp_reqs);
+ list_for_each_safe(tmp, next, &set->set_requests) {
+ struct ptlrpc_request *req =
+ list_entry(tmp, struct ptlrpc_request,
+ rq_set_chain);
+ struct obd_import *imp = req->rq_import;
+ int unregistered = 0;
+ int rc = 0;
+
+ /* This schedule point is mainly for the ptlrpcd caller of this
+ * function. Most ptlrpc sets are not long-lived and unbounded
+ * in length, but at the least the set used by the ptlrpcd is.
+ * Since the processing time is unbounded, we need to insert an
+ * explicit schedule point to make the thread well-behaved.
+ */
+ cond_resched();
+
+ if (req->rq_phase == RQ_PHASE_NEW &&
+ ptlrpc_send_new_req(req)) {
+ force_timer_recalc = 1;
+ }
+
+ /* delayed send - skip */
+ if (req->rq_phase == RQ_PHASE_NEW && req->rq_sent)
+ continue;
+
+ /* delayed resend - skip */
+ if (req->rq_phase == RQ_PHASE_RPC && req->rq_resend &&
+ req->rq_sent > get_seconds())
+ continue;
+
+ if (!(req->rq_phase == RQ_PHASE_RPC ||
+ req->rq_phase == RQ_PHASE_BULK ||
+ req->rq_phase == RQ_PHASE_INTERPRET ||
+ req->rq_phase == RQ_PHASE_UNREGISTERING ||
+ req->rq_phase == RQ_PHASE_COMPLETE)) {
+ DEBUG_REQ(D_ERROR, req, "bad phase %x", req->rq_phase);
+ LBUG();
+ }
+
+ if (req->rq_phase == RQ_PHASE_UNREGISTERING) {
+ LASSERT(req->rq_next_phase != req->rq_phase);
+ LASSERT(req->rq_next_phase != RQ_PHASE_UNDEFINED);
+
+ /*
+ * Skip processing until reply is unlinked. We
+ * can't return to pool before that and we can't
+ * call interpret before that. We need to make
+ * sure that all rdma transfers finished and will
+ * not corrupt any data.
+ */
+ if (ptlrpc_client_recv_or_unlink(req) ||
+ ptlrpc_client_bulk_active(req))
+ continue;
+
+ /*
+ * Turn fail_loc off to prevent it from looping
+ * forever.
+ */
+ if (OBD_FAIL_CHECK(OBD_FAIL_PTLRPC_LONG_REPL_UNLINK)) {
+ OBD_FAIL_CHECK_ORSET(OBD_FAIL_PTLRPC_LONG_REPL_UNLINK,
+ OBD_FAIL_ONCE);
+ }
+ if (OBD_FAIL_CHECK(OBD_FAIL_PTLRPC_LONG_BULK_UNLINK)) {
+ OBD_FAIL_CHECK_ORSET(OBD_FAIL_PTLRPC_LONG_BULK_UNLINK,
+ OBD_FAIL_ONCE);
+ }
+
+ /*
+ * Move to next phase if reply was successfully
+ * unlinked.
+ */
+ ptlrpc_rqphase_move(req, req->rq_next_phase);
+ }
+
+ if (req->rq_phase == RQ_PHASE_COMPLETE) {
+ list_move_tail(&req->rq_set_chain, &comp_reqs);
+ continue;
+ }
+
+ if (req->rq_phase == RQ_PHASE_INTERPRET)
+ goto interpret;
+
+ /*
+ * Note that this also will start async reply unlink.
+ */
+ if (req->rq_net_err && !req->rq_timedout) {
+ ptlrpc_expire_one_request(req, 1);
+
+ /*
+ * Check if we still need to wait for unlink.
+ */
+ if (ptlrpc_client_recv_or_unlink(req) ||
+ ptlrpc_client_bulk_active(req))
+ continue;
+ /* If there is no need to resend, fail it now. */
+ if (req->rq_no_resend) {
+ if (req->rq_status == 0)
+ req->rq_status = -EIO;
+ ptlrpc_rqphase_move(req, RQ_PHASE_INTERPRET);
+ goto interpret;
+ } else {
+ continue;
+ }
+ }
+
+ if (req->rq_err) {
+ spin_lock(&req->rq_lock);
+ req->rq_replied = 0;
+ spin_unlock(&req->rq_lock);
+ if (req->rq_status == 0)
+ req->rq_status = -EIO;
+ ptlrpc_rqphase_move(req, RQ_PHASE_INTERPRET);
+ goto interpret;
+ }
+
+ /* ptlrpc_set_wait->l_wait_event sets lwi_allow_intr
+ * so it sets rq_intr regardless of individual rpc
+ * timeouts. The synchronous IO waiting path sets
+ * rq_intr irrespective of whether ptlrpcd
+ * has seen a timeout. Our policy is to only interpret
+ * interrupted rpcs after they have timed out, so we
+ * need to enforce that here.
+ */
+
+ if (req->rq_intr && (req->rq_timedout || req->rq_waiting ||
+ req->rq_wait_ctx)) {
+ req->rq_status = -EINTR;
+ ptlrpc_rqphase_move(req, RQ_PHASE_INTERPRET);
+ goto interpret;
+ }
+
+ if (req->rq_phase == RQ_PHASE_RPC) {
+ if (req->rq_timedout || req->rq_resend ||
+ req->rq_waiting || req->rq_wait_ctx) {
+ int status;
+
+ if (!ptlrpc_unregister_reply(req, 1))
+ continue;
+
+ spin_lock(&imp->imp_lock);
+ if (ptlrpc_import_delay_req(imp, req,
+ &status)) {
+ /* put on delay list - only if we wait
+ * recovery finished - before send */
+ list_del_init(&req->rq_list);
+ list_add_tail(&req->rq_list,
+ &imp->
+ imp_delayed_list);
+ spin_unlock(&imp->imp_lock);
+ continue;
+ }
+
+ if (status != 0) {
+ req->rq_status = status;
+ ptlrpc_rqphase_move(req,
+ RQ_PHASE_INTERPRET);
+ spin_unlock(&imp->imp_lock);
+ goto interpret;
+ }
+ if (ptlrpc_no_resend(req) &&
+ !req->rq_wait_ctx) {
+ req->rq_status = -ENOTCONN;
+ ptlrpc_rqphase_move(req,
+ RQ_PHASE_INTERPRET);
+ spin_unlock(&imp->imp_lock);
+ goto interpret;
+ }
+
+ list_del_init(&req->rq_list);
+ list_add_tail(&req->rq_list,
+ &imp->imp_sending_list);
+
+ spin_unlock(&imp->imp_lock);
+
+ spin_lock(&req->rq_lock);
+ req->rq_waiting = 0;
+ spin_unlock(&req->rq_lock);
+
+ if (req->rq_timedout || req->rq_resend) {
+ /* This is re-sending anyways,
+ * let's mark req as resend. */
+ spin_lock(&req->rq_lock);
+ req->rq_resend = 1;
+ spin_unlock(&req->rq_lock);
+ if (req->rq_bulk) {
+ __u64 old_xid;
+
+ if (!ptlrpc_unregister_bulk(req, 1))
+ continue;
+
+ /* ensure previous bulk fails */
+ old_xid = req->rq_xid;
+ req->rq_xid = ptlrpc_next_xid();
+ CDEBUG(D_HA, "resend bulk old x%llu new x%llu\n",
+ old_xid, req->rq_xid);
+ }
+ }
+ /*
+ * rq_wait_ctx is only touched by ptlrpcd,
+ * so no lock is needed here.
+ */
+ status = sptlrpc_req_refresh_ctx(req, -1);
+ if (status) {
+ if (req->rq_err) {
+ req->rq_status = status;
+ spin_lock(&req->rq_lock);
+ req->rq_wait_ctx = 0;
+ spin_unlock(&req->rq_lock);
+ force_timer_recalc = 1;
+ } else {
+ spin_lock(&req->rq_lock);
+ req->rq_wait_ctx = 1;
+ spin_unlock(&req->rq_lock);
+ }
+
+ continue;
+ } else {
+ spin_lock(&req->rq_lock);
+ req->rq_wait_ctx = 0;
+ spin_unlock(&req->rq_lock);
+ }
+
+ rc = ptl_send_rpc(req, 0);
+ if (rc) {
+ DEBUG_REQ(D_HA, req,
+ "send failed: rc = %d", rc);
+ force_timer_recalc = 1;
+ spin_lock(&req->rq_lock);
+ req->rq_net_err = 1;
+ spin_unlock(&req->rq_lock);
+ continue;
+ }
+ /* need to reset the timeout */
+ force_timer_recalc = 1;
+ }
+
+ spin_lock(&req->rq_lock);
+
+ if (ptlrpc_client_early(req)) {
+ ptlrpc_at_recv_early_reply(req);
+ spin_unlock(&req->rq_lock);
+ continue;
+ }
+
+ /* Still waiting for a reply? */
+ if (ptlrpc_client_recv(req)) {
+ spin_unlock(&req->rq_lock);
+ continue;
+ }
+
+ /* Did we actually receive a reply? */
+ if (!ptlrpc_client_replied(req)) {
+ spin_unlock(&req->rq_lock);
+ continue;
+ }
+
+ spin_unlock(&req->rq_lock);
+
+ /* unlink from net because we are going to
+ * swab in-place of reply buffer */
+ unregistered = ptlrpc_unregister_reply(req, 1);
+ if (!unregistered)
+ continue;
+
+ req->rq_status = after_reply(req);
+ if (req->rq_resend)
+ continue;
+
+ /* If there is no bulk associated with this request,
+ * then we're done and should let the interpreter
+ * process the reply. Similarly if the RPC returned
+ * an error, and therefore the bulk will never arrive.
+ */
+ if (req->rq_bulk == NULL || req->rq_status < 0) {
+ ptlrpc_rqphase_move(req, RQ_PHASE_INTERPRET);
+ goto interpret;
+ }
+
+ ptlrpc_rqphase_move(req, RQ_PHASE_BULK);
+ }
+
+ LASSERT(req->rq_phase == RQ_PHASE_BULK);
+ if (ptlrpc_client_bulk_active(req))
+ continue;
+
+ if (req->rq_bulk->bd_failure) {
+ /* The RPC reply arrived OK, but the bulk screwed
+ * up! Dead weird since the server told us the RPC
+ * was good after getting the REPLY for her GET or
+ * the ACK for her PUT. */
+ DEBUG_REQ(D_ERROR, req, "bulk transfer failed");
+ req->rq_status = -EIO;
+ }
+
+ ptlrpc_rqphase_move(req, RQ_PHASE_INTERPRET);
+
+interpret:
+ LASSERT(req->rq_phase == RQ_PHASE_INTERPRET);
+
+ /* This moves to "unregistering" phase we need to wait for
+ * reply unlink. */
+ if (!unregistered && !ptlrpc_unregister_reply(req, 1)) {
+ /* start async bulk unlink too */
+ ptlrpc_unregister_bulk(req, 1);
+ continue;
+ }
+
+ if (!ptlrpc_unregister_bulk(req, 1))
+ continue;
+
+ /* When calling interpret receiving already should be
+ * finished. */
+ LASSERT(!req->rq_receiving_reply);
+
+ ptlrpc_req_interpret(env, req, req->rq_status);
+
+ if (ptlrpcd_check_work(req)) {
+ atomic_dec(&set->set_remaining);
+ continue;
+ }
+ ptlrpc_rqphase_move(req, RQ_PHASE_COMPLETE);
+
+ CDEBUG(req->rq_reqmsg != NULL ? D_RPCTRACE : 0,
+ "Completed RPC pname:cluuid:pid:xid:nid:opc %s:%s:%d:%llu:%s:%d\n",
+ current_comm(), imp->imp_obd->obd_uuid.uuid,
+ lustre_msg_get_status(req->rq_reqmsg), req->rq_xid,
+ libcfs_nid2str(imp->imp_connection->c_peer.nid),
+ lustre_msg_get_opc(req->rq_reqmsg));
+
+ spin_lock(&imp->imp_lock);
+ /* Request already may be not on sending or delaying list. This
+ * may happen in the case of marking it erroneous for the case
+ * ptlrpc_import_delay_req(req, status) find it impossible to
+ * allow sending this rpc and returns *status != 0. */
+ if (!list_empty(&req->rq_list)) {
+ list_del_init(&req->rq_list);
+ atomic_dec(&imp->imp_inflight);
+ }
+ spin_unlock(&imp->imp_lock);
+
+ atomic_dec(&set->set_remaining);
+ wake_up_all(&imp->imp_recovery_waitq);
+
+ if (set->set_producer) {
+ /* produce a new request if possible */
+ if (ptlrpc_set_producer(set) > 0)
+ force_timer_recalc = 1;
+
+ /* free the request that has just been completed
+ * in order not to pollute set->set_requests */
+ list_del_init(&req->rq_set_chain);
+ spin_lock(&req->rq_lock);
+ req->rq_set = NULL;
+ req->rq_invalid_rqset = 0;
+ spin_unlock(&req->rq_lock);
+
+ /* record rq_status to compute the final status later */
+ if (req->rq_status != 0)
+ set->set_rc = req->rq_status;
+ ptlrpc_req_finished(req);
+ } else {
+ list_move_tail(&req->rq_set_chain, &comp_reqs);
+ }
+ }
+
+ /* move completed request at the head of list so it's easier for
+ * caller to find them */
+ list_splice(&comp_reqs, &set->set_requests);
+
+ /* If we hit an error, we want to recover promptly. */
+ return atomic_read(&set->set_remaining) == 0 || force_timer_recalc;
+}
+EXPORT_SYMBOL(ptlrpc_check_set);
+
+/**
+ * Time out request \a req. is \a async_unlink is set, that means do not wait
+ * until LNet actually confirms network buffer unlinking.
+ * Return 1 if we should give up further retrying attempts or 0 otherwise.
+ */
+int ptlrpc_expire_one_request(struct ptlrpc_request *req, int async_unlink)
+{
+ struct obd_import *imp = req->rq_import;
+ int rc = 0;
+
+ spin_lock(&req->rq_lock);
+ req->rq_timedout = 1;
+ spin_unlock(&req->rq_lock);
+
+ DEBUG_REQ(D_WARNING, req, "Request sent has %s: [sent "CFS_DURATION_T
+ "/real "CFS_DURATION_T"]",
+ req->rq_net_err ? "failed due to network error" :
+ ((req->rq_real_sent == 0 ||
+ time_before((unsigned long)req->rq_real_sent, (unsigned long)req->rq_sent) ||
+ cfs_time_aftereq(req->rq_real_sent, req->rq_deadline)) ?
+ "timed out for sent delay" : "timed out for slow reply"),
+ req->rq_sent, req->rq_real_sent);
+
+ if (imp != NULL && obd_debug_peer_on_timeout)
+ LNetCtl(IOC_LIBCFS_DEBUG_PEER, &imp->imp_connection->c_peer);
+
+ ptlrpc_unregister_reply(req, async_unlink);
+ ptlrpc_unregister_bulk(req, async_unlink);
+
+ if (obd_dump_on_timeout)
+ libcfs_debug_dumplog();
+
+ if (imp == NULL) {
+ DEBUG_REQ(D_HA, req, "NULL import: already cleaned up?");
+ return 1;
+ }
+
+ atomic_inc(&imp->imp_timeouts);
+
+ /* The DLM server doesn't want recovery run on its imports. */
+ if (imp->imp_dlm_fake)
+ return 1;
+
+ /* If this request is for recovery or other primordial tasks,
+ * then error it out here. */
+ if (req->rq_ctx_init || req->rq_ctx_fini ||
+ req->rq_send_state != LUSTRE_IMP_FULL ||
+ imp->imp_obd->obd_no_recov) {
+ DEBUG_REQ(D_RPCTRACE, req, "err -110, sent_state=%s (now=%s)",
+ ptlrpc_import_state_name(req->rq_send_state),
+ ptlrpc_import_state_name(imp->imp_state));
+ spin_lock(&req->rq_lock);
+ req->rq_status = -ETIMEDOUT;
+ req->rq_err = 1;
+ spin_unlock(&req->rq_lock);
+ return 1;
+ }
+
+ /* if a request can't be resent we can't wait for an answer after
+ the timeout */
+ if (ptlrpc_no_resend(req)) {
+ DEBUG_REQ(D_RPCTRACE, req, "TIMEOUT-NORESEND:");
+ rc = 1;
+ }
+
+ ptlrpc_fail_import(imp, lustre_msg_get_conn_cnt(req->rq_reqmsg));
+
+ return rc;
+}
+
+/**
+ * Time out all uncompleted requests in request set pointed by \a data
+ * Callback used when waiting on sets with l_wait_event.
+ * Always returns 1.
+ */
+int ptlrpc_expired_set(void *data)
+{
+ struct ptlrpc_request_set *set = data;
+ struct list_head *tmp;
+ time_t now = get_seconds();
+
+ LASSERT(set != NULL);
+
+ /*
+ * A timeout expired. See which reqs it applies to...
+ */
+ list_for_each(tmp, &set->set_requests) {
+ struct ptlrpc_request *req =
+ list_entry(tmp, struct ptlrpc_request,
+ rq_set_chain);
+
+ /* don't expire request waiting for context */
+ if (req->rq_wait_ctx)
+ continue;
+
+ /* Request in-flight? */
+ if (!((req->rq_phase == RQ_PHASE_RPC &&
+ !req->rq_waiting && !req->rq_resend) ||
+ (req->rq_phase == RQ_PHASE_BULK)))
+ continue;
+
+ if (req->rq_timedout || /* already dealt with */
+ req->rq_deadline > now) /* not expired */
+ continue;
+
+ /* Deal with this guy. Do it asynchronously to not block
+ * ptlrpcd thread. */
+ ptlrpc_expire_one_request(req, 1);
+ }
+
+ /*
+ * When waiting for a whole set, we always break out of the
+ * sleep so we can recalculate the timeout, or enable interrupts
+ * if everyone's timed out.
+ */
+ return 1;
+}
+EXPORT_SYMBOL(ptlrpc_expired_set);
+
+/**
+ * Sets rq_intr flag in \a req under spinlock.
+ */
+void ptlrpc_mark_interrupted(struct ptlrpc_request *req)
+{
+ spin_lock(&req->rq_lock);
+ req->rq_intr = 1;
+ spin_unlock(&req->rq_lock);
+}
+EXPORT_SYMBOL(ptlrpc_mark_interrupted);
+
+/**
+ * Interrupts (sets interrupted flag) all uncompleted requests in
+ * a set \a data. Callback for l_wait_event for interruptible waits.
+ */
+void ptlrpc_interrupted_set(void *data)
+{
+ struct ptlrpc_request_set *set = data;
+ struct list_head *tmp;
+
+ LASSERT(set != NULL);
+ CDEBUG(D_RPCTRACE, "INTERRUPTED SET %p\n", set);
+
+ list_for_each(tmp, &set->set_requests) {
+ struct ptlrpc_request *req =
+ list_entry(tmp, struct ptlrpc_request,
+ rq_set_chain);
+
+ if (req->rq_phase != RQ_PHASE_RPC &&
+ req->rq_phase != RQ_PHASE_UNREGISTERING)
+ continue;
+
+ ptlrpc_mark_interrupted(req);
+ }
+}
+EXPORT_SYMBOL(ptlrpc_interrupted_set);
+
+/**
+ * Get the smallest timeout in the set; this does NOT set a timeout.
+ */
+int ptlrpc_set_next_timeout(struct ptlrpc_request_set *set)
+{
+ struct list_head *tmp;
+ time_t now = get_seconds();
+ int timeout = 0;
+ struct ptlrpc_request *req;
+ int deadline;
+
+ list_for_each(tmp, &set->set_requests) {
+ req = list_entry(tmp, struct ptlrpc_request, rq_set_chain);
+
+ /*
+ * Request in-flight?
+ */
+ if (!(((req->rq_phase == RQ_PHASE_RPC) && !req->rq_waiting) ||
+ (req->rq_phase == RQ_PHASE_BULK) ||
+ (req->rq_phase == RQ_PHASE_NEW)))
+ continue;
+
+ /*
+ * Already timed out.
+ */
+ if (req->rq_timedout)
+ continue;
+
+ /*
+ * Waiting for ctx.
+ */
+ if (req->rq_wait_ctx)
+ continue;
+
+ if (req->rq_phase == RQ_PHASE_NEW)
+ deadline = req->rq_sent;
+ else if (req->rq_phase == RQ_PHASE_RPC && req->rq_resend)
+ deadline = req->rq_sent;
+ else
+ deadline = req->rq_sent + req->rq_timeout;
+
+ if (deadline <= now) /* actually expired already */
+ timeout = 1; /* ASAP */
+ else if (timeout == 0 || timeout > deadline - now)
+ timeout = deadline - now;
+ }
+ return timeout;
+}
+EXPORT_SYMBOL(ptlrpc_set_next_timeout);
+
+/**
+ * Send all unset request from the set and then wait until all
+ * requests in the set complete (either get a reply, timeout, get an
+ * error or otherwise be interrupted).
+ * Returns 0 on success or error code otherwise.
+ */
+int ptlrpc_set_wait(struct ptlrpc_request_set *set)
+{
+ struct list_head *tmp;
+ struct ptlrpc_request *req;
+ struct l_wait_info lwi;
+ int rc, timeout;
+
+ if (set->set_producer)
+ (void)ptlrpc_set_producer(set);
+ else
+ list_for_each(tmp, &set->set_requests) {
+ req = list_entry(tmp, struct ptlrpc_request,
+ rq_set_chain);
+ if (req->rq_phase == RQ_PHASE_NEW)
+ (void)ptlrpc_send_new_req(req);
+ }
+
+ if (list_empty(&set->set_requests))
+ return 0;
+
+ do {
+ timeout = ptlrpc_set_next_timeout(set);
+
+ /* wait until all complete, interrupted, or an in-flight
+ * req times out */
+ CDEBUG(D_RPCTRACE, "set %p going to sleep for %d seconds\n",
+ set, timeout);
+
+ if (timeout == 0 && !cfs_signal_pending())
+ /*
+ * No requests are in-flight (ether timed out
+ * or delayed), so we can allow interrupts.
+ * We still want to block for a limited time,
+ * so we allow interrupts during the timeout.
+ */
+ lwi = LWI_TIMEOUT_INTR_ALL(cfs_time_seconds(1),
+ ptlrpc_expired_set,
+ ptlrpc_interrupted_set, set);
+ else
+ /*
+ * At least one request is in flight, so no
+ * interrupts are allowed. Wait until all
+ * complete, or an in-flight req times out.
+ */
+ lwi = LWI_TIMEOUT(cfs_time_seconds(timeout ? timeout : 1),
+ ptlrpc_expired_set, set);
+
+ rc = l_wait_event(set->set_waitq, ptlrpc_check_set(NULL, set), &lwi);
+
+ /* LU-769 - if we ignored the signal because it was already
+ * pending when we started, we need to handle it now or we risk
+ * it being ignored forever */
+ if (rc == -ETIMEDOUT && !lwi.lwi_allow_intr &&
+ cfs_signal_pending()) {
+ sigset_t blocked_sigs =
+ cfs_block_sigsinv(LUSTRE_FATAL_SIGS);
+
+ /* In fact we only interrupt for the "fatal" signals
+ * like SIGINT or SIGKILL. We still ignore less
+ * important signals since ptlrpc set is not easily
+ * reentrant from userspace again */
+ if (cfs_signal_pending())
+ ptlrpc_interrupted_set(set);
+ cfs_restore_sigs(blocked_sigs);
+ }
+
+ LASSERT(rc == 0 || rc == -EINTR || rc == -ETIMEDOUT);
+
+ /* -EINTR => all requests have been flagged rq_intr so next
+ * check completes.
+ * -ETIMEDOUT => someone timed out. When all reqs have
+ * timed out, signals are enabled allowing completion with
+ * EINTR.
+ * I don't really care if we go once more round the loop in
+ * the error cases -eeb. */
+ if (rc == 0 && atomic_read(&set->set_remaining) == 0) {
+ list_for_each(tmp, &set->set_requests) {
+ req = list_entry(tmp, struct ptlrpc_request,
+ rq_set_chain);
+ spin_lock(&req->rq_lock);
+ req->rq_invalid_rqset = 1;
+ spin_unlock(&req->rq_lock);
+ }
+ }
+ } while (rc != 0 || atomic_read(&set->set_remaining) != 0);
+
+ LASSERT(atomic_read(&set->set_remaining) == 0);
+
+ rc = set->set_rc; /* rq_status of already freed requests if any */
+ list_for_each(tmp, &set->set_requests) {
+ req = list_entry(tmp, struct ptlrpc_request, rq_set_chain);
+
+ LASSERT(req->rq_phase == RQ_PHASE_COMPLETE);
+ if (req->rq_status != 0)
+ rc = req->rq_status;
+ }
+
+ if (set->set_interpret != NULL) {
+ int (*interpreter)(struct ptlrpc_request_set *set, void *, int) =
+ set->set_interpret;
+ rc = interpreter(set, set->set_arg, rc);
+ } else {
+ struct ptlrpc_set_cbdata *cbdata, *n;
+ int err;
+
+ list_for_each_entry_safe(cbdata, n,
+ &set->set_cblist, psc_item) {
+ list_del_init(&cbdata->psc_item);
+ err = cbdata->psc_interpret(set, cbdata->psc_data, rc);
+ if (err && !rc)
+ rc = err;
+ OBD_FREE_PTR(cbdata);
+ }
+ }
+
+ return rc;
+}
+EXPORT_SYMBOL(ptlrpc_set_wait);
+
+/**
+ * Helper function for request freeing.
+ * Called when request count reached zero and request needs to be freed.
+ * Removes request from all sorts of sending/replay lists it might be on,
+ * frees network buffers if any are present.
+ * If \a locked is set, that means caller is already holding import imp_lock
+ * and so we no longer need to reobtain it (for certain lists manipulations)
+ */
+static void __ptlrpc_free_req(struct ptlrpc_request *request, int locked)
+{
+ if (request == NULL)
+ return;
+ LASSERTF(!request->rq_receiving_reply, "req %p\n", request);
+ LASSERTF(request->rq_rqbd == NULL, "req %p\n", request);/* client-side */
+ LASSERTF(list_empty(&request->rq_list), "req %p\n", request);
+ LASSERTF(list_empty(&request->rq_set_chain), "req %p\n", request);
+ LASSERTF(list_empty(&request->rq_exp_list), "req %p\n", request);
+ LASSERTF(!request->rq_replay, "req %p\n", request);
+
+ req_capsule_fini(&request->rq_pill);
+
+ /* We must take it off the imp_replay_list first. Otherwise, we'll set
+ * request->rq_reqmsg to NULL while osc_close is dereferencing it. */
+ if (request->rq_import != NULL) {
+ if (!locked)
+ spin_lock(&request->rq_import->imp_lock);
+ list_del_init(&request->rq_replay_list);
+ if (!locked)
+ spin_unlock(&request->rq_import->imp_lock);
+ }
+ LASSERTF(list_empty(&request->rq_replay_list), "req %p\n", request);
+
+ if (atomic_read(&request->rq_refcount) != 0) {
+ DEBUG_REQ(D_ERROR, request,
+ "freeing request with nonzero refcount");
+ LBUG();
+ }
+
+ if (request->rq_repbuf != NULL)
+ sptlrpc_cli_free_repbuf(request);
+ if (request->rq_export != NULL) {
+ class_export_put(request->rq_export);
+ request->rq_export = NULL;
+ }
+ if (request->rq_import != NULL) {
+ class_import_put(request->rq_import);
+ request->rq_import = NULL;
+ }
+ if (request->rq_bulk != NULL)
+ ptlrpc_free_bulk_pin(request->rq_bulk);
+
+ if (request->rq_reqbuf != NULL || request->rq_clrbuf != NULL)
+ sptlrpc_cli_free_reqbuf(request);
+
+ if (request->rq_cli_ctx)
+ sptlrpc_req_put_ctx(request, !locked);
+
+ if (request->rq_pool)
+ __ptlrpc_free_req_to_pool(request);
+ else
+ ptlrpc_request_cache_free(request);
+}
+
+static int __ptlrpc_req_finished(struct ptlrpc_request *request, int locked);
+/**
+ * Drop one request reference. Must be called with import imp_lock held.
+ * When reference count drops to zero, request is freed.
+ */
+void ptlrpc_req_finished_with_imp_lock(struct ptlrpc_request *request)
+{
+ assert_spin_locked(&request->rq_import->imp_lock);
+ (void)__ptlrpc_req_finished(request, 1);
+}
+EXPORT_SYMBOL(ptlrpc_req_finished_with_imp_lock);
+
+/**
+ * Helper function
+ * Drops one reference count for request \a request.
+ * \a locked set indicates that caller holds import imp_lock.
+ * Frees the request when reference count reaches zero.
+ */
+static int __ptlrpc_req_finished(struct ptlrpc_request *request, int locked)
+{
+ if (request == NULL)
+ return 1;
+
+ if (request == LP_POISON ||
+ request->rq_reqmsg == LP_POISON) {
+ CERROR("dereferencing freed request (bug 575)\n");
+ LBUG();
+ return 1;
+ }
+
+ DEBUG_REQ(D_INFO, request, "refcount now %u",
+ atomic_read(&request->rq_refcount) - 1);
+
+ if (atomic_dec_and_test(&request->rq_refcount)) {
+ __ptlrpc_free_req(request, locked);
+ return 1;
+ }
+
+ return 0;
+}
+
+/**
+ * Drops one reference count for a request.
+ */
+void ptlrpc_req_finished(struct ptlrpc_request *request)
+{
+ __ptlrpc_req_finished(request, 0);
+}
+EXPORT_SYMBOL(ptlrpc_req_finished);
+
+/**
+ * Returns xid of a \a request
+ */
+__u64 ptlrpc_req_xid(struct ptlrpc_request *request)
+{
+ return request->rq_xid;
+}
+EXPORT_SYMBOL(ptlrpc_req_xid);
+
+/**
+ * Disengage the client's reply buffer from the network
+ * NB does _NOT_ unregister any client-side bulk.
+ * IDEMPOTENT, but _not_ safe against concurrent callers.
+ * The request owner (i.e. the thread doing the I/O) must call...
+ * Returns 0 on success or 1 if unregistering cannot be made.
+ */
+int ptlrpc_unregister_reply(struct ptlrpc_request *request, int async)
+{
+ int rc;
+ wait_queue_head_t *wq;
+ struct l_wait_info lwi;
+
+ /*
+ * Might sleep.
+ */
+ LASSERT(!in_interrupt());
+
+ /*
+ * Let's setup deadline for reply unlink.
+ */
+ if (OBD_FAIL_CHECK(OBD_FAIL_PTLRPC_LONG_REPL_UNLINK) &&
+ async && request->rq_reply_deadline == 0)
+ request->rq_reply_deadline = get_seconds()+LONG_UNLINK;
+
+ /*
+ * Nothing left to do.
+ */
+ if (!ptlrpc_client_recv_or_unlink(request))
+ return 1;
+
+ LNetMDUnlink(request->rq_reply_md_h);
+
+ /*
+ * Let's check it once again.
+ */
+ if (!ptlrpc_client_recv_or_unlink(request))
+ return 1;
+
+ /*
+ * Move to "Unregistering" phase as reply was not unlinked yet.
+ */
+ ptlrpc_rqphase_move(request, RQ_PHASE_UNREGISTERING);
+
+ /*
+ * Do not wait for unlink to finish.
+ */
+ if (async)
+ return 0;
+
+ /*
+ * We have to l_wait_event() whatever the result, to give liblustre
+ * a chance to run reply_in_callback(), and to make sure we've
+ * unlinked before returning a req to the pool.
+ */
+ if (request->rq_set != NULL)
+ wq = &request->rq_set->set_waitq;
+ else
+ wq = &request->rq_reply_waitq;
+
+ for (;;) {
+ /* Network access will complete in finite time but the HUGE
+ * timeout lets us CWARN for visibility of sluggish NALs */
+ lwi = LWI_TIMEOUT_INTERVAL(cfs_time_seconds(LONG_UNLINK),
+ cfs_time_seconds(1), NULL, NULL);
+ rc = l_wait_event(*wq, !ptlrpc_client_recv_or_unlink(request),
+ &lwi);
+ if (rc == 0) {
+ ptlrpc_rqphase_move(request, request->rq_next_phase);
+ return 1;
+ }
+
+ LASSERT(rc == -ETIMEDOUT);
+ DEBUG_REQ(D_WARNING, request,
+ "Unexpectedly long timeout rvcng=%d unlnk=%d/%d",
+ request->rq_receiving_reply,
+ request->rq_req_unlink, request->rq_reply_unlink);
+ }
+ return 0;
+}
+EXPORT_SYMBOL(ptlrpc_unregister_reply);
+
+static void ptlrpc_free_request(struct ptlrpc_request *req)
+{
+ spin_lock(&req->rq_lock);
+ req->rq_replay = 0;
+ spin_unlock(&req->rq_lock);
+
+ if (req->rq_commit_cb != NULL)
+ req->rq_commit_cb(req);
+ list_del_init(&req->rq_replay_list);
+
+ __ptlrpc_req_finished(req, 1);
+}
+
+/**
+ * the request is committed and dropped from the replay list of its import
+ */
+void ptlrpc_request_committed(struct ptlrpc_request *req, int force)
+{
+ struct obd_import *imp = req->rq_import;
+
+ spin_lock(&imp->imp_lock);
+ if (list_empty(&req->rq_replay_list)) {
+ spin_unlock(&imp->imp_lock);
+ return;
+ }
+
+ if (force || req->rq_transno <= imp->imp_peer_committed_transno)
+ ptlrpc_free_request(req);
+
+ spin_unlock(&imp->imp_lock);
+}
+EXPORT_SYMBOL(ptlrpc_request_committed);
+
+/**
+ * Iterates through replay_list on import and prunes
+ * all requests have transno smaller than last_committed for the
+ * import and don't have rq_replay set.
+ * Since requests are sorted in transno order, stops when meeting first
+ * transno bigger than last_committed.
+ * caller must hold imp->imp_lock
+ */
+void ptlrpc_free_committed(struct obd_import *imp)
+{
+ struct ptlrpc_request *req, *saved;
+ struct ptlrpc_request *last_req = NULL; /* temporary fire escape */
+ bool skip_committed_list = true;
+
+ LASSERT(imp != NULL);
+ assert_spin_locked(&imp->imp_lock);
+
+ if (imp->imp_peer_committed_transno == imp->imp_last_transno_checked &&
+ imp->imp_generation == imp->imp_last_generation_checked) {
+ CDEBUG(D_INFO, "%s: skip recheck: last_committed %llu\n",
+ imp->imp_obd->obd_name, imp->imp_peer_committed_transno);
+ return;
+ }
+ CDEBUG(D_RPCTRACE, "%s: committing for last_committed %llu gen %d\n",
+ imp->imp_obd->obd_name, imp->imp_peer_committed_transno,
+ imp->imp_generation);
+
+ if (imp->imp_generation != imp->imp_last_generation_checked)
+ skip_committed_list = false;
+
+ imp->imp_last_transno_checked = imp->imp_peer_committed_transno;
+ imp->imp_last_generation_checked = imp->imp_generation;
+
+ list_for_each_entry_safe(req, saved, &imp->imp_replay_list,
+ rq_replay_list) {
+ /* XXX ok to remove when 1357 resolved - rread 05/29/03 */
+ LASSERT(req != last_req);
+ last_req = req;
+
+ if (req->rq_transno == 0) {
+ DEBUG_REQ(D_EMERG, req, "zero transno during replay");
+ LBUG();
+ }
+ if (req->rq_import_generation < imp->imp_generation) {
+ DEBUG_REQ(D_RPCTRACE, req, "free request with old gen");
+ goto free_req;
+ }
+
+ /* not yet committed */
+ if (req->rq_transno > imp->imp_peer_committed_transno) {
+ DEBUG_REQ(D_RPCTRACE, req, "stopping search");
+ break;
+ }
+
+ if (req->rq_replay) {
+ DEBUG_REQ(D_RPCTRACE, req, "keeping (FL_REPLAY)");
+ list_move_tail(&req->rq_replay_list,
+ &imp->imp_committed_list);
+ continue;
+ }
+
+ DEBUG_REQ(D_INFO, req, "commit (last_committed %llu)",
+ imp->imp_peer_committed_transno);
+free_req:
+ ptlrpc_free_request(req);
+ }
+ if (skip_committed_list)
+ return;
+
+ list_for_each_entry_safe(req, saved, &imp->imp_committed_list,
+ rq_replay_list) {
+ LASSERT(req->rq_transno != 0);
+ if (req->rq_import_generation < imp->imp_generation) {
+ DEBUG_REQ(D_RPCTRACE, req, "free stale open request");
+ ptlrpc_free_request(req);
+ }
+ }
+}
+
+void ptlrpc_cleanup_client(struct obd_import *imp)
+{
+}
+EXPORT_SYMBOL(ptlrpc_cleanup_client);
+
+/**
+ * Schedule previously sent request for resend.
+ * For bulk requests we assign new xid (to avoid problems with
+ * lost replies and therefore several transfers landing into same buffer
+ * from different sending attempts).
+ */
+void ptlrpc_resend_req(struct ptlrpc_request *req)
+{
+ DEBUG_REQ(D_HA, req, "going to resend");
+ spin_lock(&req->rq_lock);
+
+ /* Request got reply but linked to the import list still.
+ Let ptlrpc_check_set() to process it. */
+ if (ptlrpc_client_replied(req)) {
+ spin_unlock(&req->rq_lock);
+ DEBUG_REQ(D_HA, req, "it has reply, so skip it");
+ return;
+ }
+
+ lustre_msg_set_handle(req->rq_reqmsg, &(struct lustre_handle){ 0 });
+ req->rq_status = -EAGAIN;
+
+ req->rq_resend = 1;
+ req->rq_net_err = 0;
+ req->rq_timedout = 0;
+ if (req->rq_bulk) {
+ __u64 old_xid = req->rq_xid;
+
+ /* ensure previous bulk fails */
+ req->rq_xid = ptlrpc_next_xid();
+ CDEBUG(D_HA, "resend bulk old x%llu new x%llu\n",
+ old_xid, req->rq_xid);
+ }
+ ptlrpc_client_wake_req(req);
+ spin_unlock(&req->rq_lock);
+}
+EXPORT_SYMBOL(ptlrpc_resend_req);
+
+/* XXX: this function and rq_status are currently unused */
+void ptlrpc_restart_req(struct ptlrpc_request *req)
+{
+ DEBUG_REQ(D_HA, req, "restarting (possibly-)completed request");
+ req->rq_status = -ERESTARTSYS;
+
+ spin_lock(&req->rq_lock);
+ req->rq_restart = 1;
+ req->rq_timedout = 0;
+ ptlrpc_client_wake_req(req);
+ spin_unlock(&req->rq_lock);
+}
+EXPORT_SYMBOL(ptlrpc_restart_req);
+
+/**
+ * Grab additional reference on a request \a req
+ */
+struct ptlrpc_request *ptlrpc_request_addref(struct ptlrpc_request *req)
+{
+ atomic_inc(&req->rq_refcount);
+ return req;
+}
+EXPORT_SYMBOL(ptlrpc_request_addref);
+
+/**
+ * Add a request to import replay_list.
+ * Must be called under imp_lock
+ */
+void ptlrpc_retain_replayable_request(struct ptlrpc_request *req,
+ struct obd_import *imp)
+{
+ struct list_head *tmp;
+
+ assert_spin_locked(&imp->imp_lock);
+
+ if (req->rq_transno == 0) {
+ DEBUG_REQ(D_EMERG, req, "saving request with zero transno");
+ LBUG();
+ }
+
+ /* clear this for new requests that were resent as well
+ as resent replayed requests. */
+ lustre_msg_clear_flags(req->rq_reqmsg, MSG_RESENT);
+
+ /* don't re-add requests that have been replayed */
+ if (!list_empty(&req->rq_replay_list))
+ return;
+
+ lustre_msg_add_flags(req->rq_reqmsg, MSG_REPLAY);
+
+ LASSERT(imp->imp_replayable);
+ /* Balanced in ptlrpc_free_committed, usually. */
+ ptlrpc_request_addref(req);
+ list_for_each_prev(tmp, &imp->imp_replay_list) {
+ struct ptlrpc_request *iter =
+ list_entry(tmp, struct ptlrpc_request,
+ rq_replay_list);
+
+ /* We may have duplicate transnos if we create and then
+ * open a file, or for closes retained if to match creating
+ * opens, so use req->rq_xid as a secondary key.
+ * (See bugs 684, 685, and 428.)
+ * XXX no longer needed, but all opens need transnos!
+ */
+ if (iter->rq_transno > req->rq_transno)
+ continue;
+
+ if (iter->rq_transno == req->rq_transno) {
+ LASSERT(iter->rq_xid != req->rq_xid);
+ if (iter->rq_xid > req->rq_xid)
+ continue;
+ }
+
+ list_add(&req->rq_replay_list, &iter->rq_replay_list);
+ return;
+ }
+
+ list_add(&req->rq_replay_list, &imp->imp_replay_list);
+}
+EXPORT_SYMBOL(ptlrpc_retain_replayable_request);
+
+/**
+ * Send request and wait until it completes.
+ * Returns request processing status.
+ */
+int ptlrpc_queue_wait(struct ptlrpc_request *req)
+{
+ struct ptlrpc_request_set *set;
+ int rc;
+
+ LASSERT(req->rq_set == NULL);
+ LASSERT(!req->rq_receiving_reply);
+
+ set = ptlrpc_prep_set();
+ if (set == NULL) {
+ CERROR("Unable to allocate ptlrpc set.");
+ return -ENOMEM;
+ }
+
+ /* for distributed debugging */
+ lustre_msg_set_status(req->rq_reqmsg, current_pid());
+
+ /* add a ref for the set (see comment in ptlrpc_set_add_req) */
+ ptlrpc_request_addref(req);
+ ptlrpc_set_add_req(set, req);
+ rc = ptlrpc_set_wait(set);
+ ptlrpc_set_destroy(set);
+
+ return rc;
+}
+EXPORT_SYMBOL(ptlrpc_queue_wait);
+
+struct ptlrpc_replay_async_args {
+ int praa_old_state;
+ int praa_old_status;
+};
+
+/**
+ * Callback used for replayed requests reply processing.
+ * In case of successful reply calls registered request replay callback.
+ * In case of error restart replay process.
+ */
+static int ptlrpc_replay_interpret(const struct lu_env *env,
+ struct ptlrpc_request *req,
+ void *data, int rc)
+{
+ struct ptlrpc_replay_async_args *aa = data;
+ struct obd_import *imp = req->rq_import;
+
+ atomic_dec(&imp->imp_replay_inflight);
+
+ if (!ptlrpc_client_replied(req)) {
+ CERROR("request replay timed out, restarting recovery\n");
+ rc = -ETIMEDOUT;
+ goto out;
+ }
+
+ if (lustre_msg_get_type(req->rq_repmsg) == PTL_RPC_MSG_ERR &&
+ (lustre_msg_get_status(req->rq_repmsg) == -ENOTCONN ||
+ lustre_msg_get_status(req->rq_repmsg) == -ENODEV)) {
+ rc = lustre_msg_get_status(req->rq_repmsg);
+ goto out;
+ }
+
+ /** VBR: check version failure */
+ if (lustre_msg_get_status(req->rq_repmsg) == -EOVERFLOW) {
+ /** replay was failed due to version mismatch */
+ DEBUG_REQ(D_WARNING, req, "Version mismatch during replay\n");
+ spin_lock(&imp->imp_lock);
+ imp->imp_vbr_failed = 1;
+ imp->imp_no_lock_replay = 1;
+ spin_unlock(&imp->imp_lock);
+ lustre_msg_set_status(req->rq_repmsg, aa->praa_old_status);
+ } else {
+ /** The transno had better not change over replay. */
+ LASSERTF(lustre_msg_get_transno(req->rq_reqmsg) ==
+ lustre_msg_get_transno(req->rq_repmsg) ||
+ lustre_msg_get_transno(req->rq_repmsg) == 0,
+ "%#llx/%#llx\n",
+ lustre_msg_get_transno(req->rq_reqmsg),
+ lustre_msg_get_transno(req->rq_repmsg));
+ }
+
+ spin_lock(&imp->imp_lock);
+ /** if replays by version then gap occur on server, no trust to locks */
+ if (lustre_msg_get_flags(req->rq_repmsg) & MSG_VERSION_REPLAY)
+ imp->imp_no_lock_replay = 1;
+ imp->imp_last_replay_transno = lustre_msg_get_transno(req->rq_reqmsg);
+ spin_unlock(&imp->imp_lock);
+ LASSERT(imp->imp_last_replay_transno);
+
+ /* transaction number shouldn't be bigger than the latest replayed */
+ if (req->rq_transno > lustre_msg_get_transno(req->rq_reqmsg)) {
+ DEBUG_REQ(D_ERROR, req,
+ "Reported transno %llu is bigger than the replayed one: %llu",
+ req->rq_transno,
+ lustre_msg_get_transno(req->rq_reqmsg));
+ rc = -EINVAL;
+ goto out;
+ }
+
+ DEBUG_REQ(D_HA, req, "got rep");
+
+ /* let the callback do fixups, possibly including in the request */
+ if (req->rq_replay_cb)
+ req->rq_replay_cb(req);
+
+ if (ptlrpc_client_replied(req) &&
+ lustre_msg_get_status(req->rq_repmsg) != aa->praa_old_status) {
+ DEBUG_REQ(D_ERROR, req, "status %d, old was %d",
+ lustre_msg_get_status(req->rq_repmsg),
+ aa->praa_old_status);
+ } else {
+ /* Put it back for re-replay. */
+ lustre_msg_set_status(req->rq_repmsg, aa->praa_old_status);
+ }
+
+ /*
+ * Errors while replay can set transno to 0, but
+ * imp_last_replay_transno shouldn't be set to 0 anyway
+ */
+ if (req->rq_transno == 0)
+ CERROR("Transno is 0 during replay!\n");
+
+ /* continue with recovery */
+ rc = ptlrpc_import_recovery_state_machine(imp);
+ out:
+ req->rq_send_state = aa->praa_old_state;
+
+ if (rc != 0)
+ /* this replay failed, so restart recovery */
+ ptlrpc_connect_import(imp);
+
+ return rc;
+}
+
+/**
+ * Prepares and queues request for replay.
+ * Adds it to ptlrpcd queue for actual sending.
+ * Returns 0 on success.
+ */
+int ptlrpc_replay_req(struct ptlrpc_request *req)
+{
+ struct ptlrpc_replay_async_args *aa;
+
+ LASSERT(req->rq_import->imp_state == LUSTRE_IMP_REPLAY);
+
+ LASSERT(sizeof(*aa) <= sizeof(req->rq_async_args));
+ aa = ptlrpc_req_async_args(req);
+ memset(aa, 0, sizeof(*aa));
+
+ /* Prepare request to be resent with ptlrpcd */
+ aa->praa_old_state = req->rq_send_state;
+ req->rq_send_state = LUSTRE_IMP_REPLAY;
+ req->rq_phase = RQ_PHASE_NEW;
+ req->rq_next_phase = RQ_PHASE_UNDEFINED;
+ if (req->rq_repmsg)
+ aa->praa_old_status = lustre_msg_get_status(req->rq_repmsg);
+ req->rq_status = 0;
+ req->rq_interpret_reply = ptlrpc_replay_interpret;
+ /* Readjust the timeout for current conditions */
+ ptlrpc_at_set_req_timeout(req);
+
+ /* Tell server the net_latency, so the server can calculate how long
+ * it should wait for next replay */
+ lustre_msg_set_service_time(req->rq_reqmsg,
+ ptlrpc_at_get_net_latency(req));
+ DEBUG_REQ(D_HA, req, "REPLAY");
+
+ atomic_inc(&req->rq_import->imp_replay_inflight);
+ ptlrpc_request_addref(req); /* ptlrpcd needs a ref */
+
+ ptlrpcd_add_req(req, PDL_POLICY_LOCAL, -1);
+ return 0;
+}
+EXPORT_SYMBOL(ptlrpc_replay_req);
+
+/**
+ * Aborts all in-flight request on import \a imp sending and delayed lists
+ */
+void ptlrpc_abort_inflight(struct obd_import *imp)
+{
+ struct list_head *tmp, *n;
+
+ /* Make sure that no new requests get processed for this import.
+ * ptlrpc_{queue,set}_wait must (and does) hold imp_lock while testing
+ * this flag and then putting requests on sending_list or delayed_list.
+ */
+ spin_lock(&imp->imp_lock);
+
+ /* XXX locking? Maybe we should remove each request with the list
+ * locked? Also, how do we know if the requests on the list are
+ * being freed at this time?
+ */
+ list_for_each_safe(tmp, n, &imp->imp_sending_list) {
+ struct ptlrpc_request *req =
+ list_entry(tmp, struct ptlrpc_request, rq_list);
+
+ DEBUG_REQ(D_RPCTRACE, req, "inflight");
+
+ spin_lock(&req->rq_lock);
+ if (req->rq_import_generation < imp->imp_generation) {
+ req->rq_err = 1;
+ req->rq_status = -EIO;
+ ptlrpc_client_wake_req(req);
+ }
+ spin_unlock(&req->rq_lock);
+ }
+
+ list_for_each_safe(tmp, n, &imp->imp_delayed_list) {
+ struct ptlrpc_request *req =
+ list_entry(tmp, struct ptlrpc_request, rq_list);
+
+ DEBUG_REQ(D_RPCTRACE, req, "aborting waiting req");
+
+ spin_lock(&req->rq_lock);
+ if (req->rq_import_generation < imp->imp_generation) {
+ req->rq_err = 1;
+ req->rq_status = -EIO;
+ ptlrpc_client_wake_req(req);
+ }
+ spin_unlock(&req->rq_lock);
+ }
+
+ /* Last chance to free reqs left on the replay list, but we
+ * will still leak reqs that haven't committed. */
+ if (imp->imp_replayable)
+ ptlrpc_free_committed(imp);
+
+ spin_unlock(&imp->imp_lock);
+}
+EXPORT_SYMBOL(ptlrpc_abort_inflight);
+
+/**
+ * Abort all uncompleted requests in request set \a set
+ */
+void ptlrpc_abort_set(struct ptlrpc_request_set *set)
+{
+ struct list_head *tmp, *pos;
+
+ LASSERT(set != NULL);
+
+ list_for_each_safe(pos, tmp, &set->set_requests) {
+ struct ptlrpc_request *req =
+ list_entry(pos, struct ptlrpc_request,
+ rq_set_chain);
+
+ spin_lock(&req->rq_lock);
+ if (req->rq_phase != RQ_PHASE_RPC) {
+ spin_unlock(&req->rq_lock);
+ continue;
+ }
+
+ req->rq_err = 1;
+ req->rq_status = -EINTR;
+ ptlrpc_client_wake_req(req);
+ spin_unlock(&req->rq_lock);
+ }
+}
+
+static __u64 ptlrpc_last_xid;
+static spinlock_t ptlrpc_last_xid_lock;
+
+/**
+ * Initialize the XID for the node. This is common among all requests on
+ * this node, and only requires the property that it is monotonically
+ * increasing. It does not need to be sequential. Since this is also used
+ * as the RDMA match bits, it is important that a single client NOT have
+ * the same match bits for two different in-flight requests, hence we do
+ * NOT want to have an XID per target or similar.
+ *
+ * To avoid an unlikely collision between match bits after a client reboot
+ * (which would deliver old data into the wrong RDMA buffer) initialize
+ * the XID based on the current time, assuming a maximum RPC rate of 1M RPC/s.
+ * If the time is clearly incorrect, we instead use a 62-bit random number.
+ * In the worst case the random number will overflow 1M RPCs per second in
+ * 9133 years, or permutations thereof.
+ */
+#define YEAR_2004 (1ULL << 30)
+void ptlrpc_init_xid(void)
+{
+ time_t now = get_seconds();
+
+ spin_lock_init(&ptlrpc_last_xid_lock);
+ if (now < YEAR_2004) {
+ cfs_get_random_bytes(&ptlrpc_last_xid, sizeof(ptlrpc_last_xid));
+ ptlrpc_last_xid >>= 2;
+ ptlrpc_last_xid |= (1ULL << 61);
+ } else {
+ ptlrpc_last_xid = (__u64)now << 20;
+ }
+
+ /* Always need to be aligned to a power-of-two for multi-bulk BRW */
+ CLASSERT((PTLRPC_BULK_OPS_COUNT & (PTLRPC_BULK_OPS_COUNT - 1)) == 0);
+ ptlrpc_last_xid &= PTLRPC_BULK_OPS_MASK;
+}
+
+/**
+ * Increase xid and returns resulting new value to the caller.
+ *
+ * Multi-bulk BRW RPCs consume multiple XIDs for each bulk transfer, starting
+ * at the returned xid, up to xid + PTLRPC_BULK_OPS_COUNT - 1. The BRW RPC
+ * itself uses the last bulk xid needed, so the server can determine the
+ * the number of bulk transfers from the RPC XID and a bitmask. The starting
+ * xid must align to a power-of-two value.
+ *
+ * This is assumed to be true due to the initial ptlrpc_last_xid
+ * value also being initialized to a power-of-two value. LU-1431
+ */
+__u64 ptlrpc_next_xid(void)
+{
+ __u64 next;
+
+ spin_lock(&ptlrpc_last_xid_lock);
+ next = ptlrpc_last_xid + PTLRPC_BULK_OPS_COUNT;
+ ptlrpc_last_xid = next;
+ spin_unlock(&ptlrpc_last_xid_lock);
+
+ return next;
+}
+EXPORT_SYMBOL(ptlrpc_next_xid);
+
+/**
+ * Get a glimpse at what next xid value might have been.
+ * Returns possible next xid.
+ */
+__u64 ptlrpc_sample_next_xid(void)
+{
+#if BITS_PER_LONG == 32
+ /* need to avoid possible word tearing on 32-bit systems */
+ __u64 next;
+
+ spin_lock(&ptlrpc_last_xid_lock);
+ next = ptlrpc_last_xid + PTLRPC_BULK_OPS_COUNT;
+ spin_unlock(&ptlrpc_last_xid_lock);
+
+ return next;
+#else
+ /* No need to lock, since returned value is racy anyways */
+ return ptlrpc_last_xid + PTLRPC_BULK_OPS_COUNT;
+#endif
+}
+EXPORT_SYMBOL(ptlrpc_sample_next_xid);
+
+/**
+ * Functions for operating ptlrpc workers.
+ *
+ * A ptlrpc work is a function which will be running inside ptlrpc context.
+ * The callback shouldn't sleep otherwise it will block that ptlrpcd thread.
+ *
+ * 1. after a work is created, it can be used many times, that is:
+ * handler = ptlrpcd_alloc_work();
+ * ptlrpcd_queue_work();
+ *
+ * queue it again when necessary:
+ * ptlrpcd_queue_work();
+ * ptlrpcd_destroy_work();
+ * 2. ptlrpcd_queue_work() can be called by multiple processes meanwhile, but
+ * it will only be queued once in any time. Also as its name implies, it may
+ * have delay before it really runs by ptlrpcd thread.
+ */
+struct ptlrpc_work_async_args {
+ int (*cb)(const struct lu_env *, void *);
+ void *cbdata;
+};
+
+static void ptlrpcd_add_work_req(struct ptlrpc_request *req)
+{
+ /* re-initialize the req */
+ req->rq_timeout = obd_timeout;
+ req->rq_sent = get_seconds();
+ req->rq_deadline = req->rq_sent + req->rq_timeout;
+ req->rq_reply_deadline = req->rq_deadline;
+ req->rq_phase = RQ_PHASE_INTERPRET;
+ req->rq_next_phase = RQ_PHASE_COMPLETE;
+ req->rq_xid = ptlrpc_next_xid();
+ req->rq_import_generation = req->rq_import->imp_generation;
+
+ ptlrpcd_add_req(req, PDL_POLICY_ROUND, -1);
+}
+
+static int work_interpreter(const struct lu_env *env,
+ struct ptlrpc_request *req, void *data, int rc)
+{
+ struct ptlrpc_work_async_args *arg = data;
+
+ LASSERT(ptlrpcd_check_work(req));
+ LASSERT(arg->cb != NULL);
+
+ rc = arg->cb(env, arg->cbdata);
+
+ list_del_init(&req->rq_set_chain);
+ req->rq_set = NULL;
+
+ if (atomic_dec_return(&req->rq_refcount) > 1) {
+ atomic_set(&req->rq_refcount, 2);
+ ptlrpcd_add_work_req(req);
+ }
+ return rc;
+}
+
+static int worker_format;
+
+static int ptlrpcd_check_work(struct ptlrpc_request *req)
+{
+ return req->rq_pill.rc_fmt == (void *)&worker_format;
+}
+
+/**
+ * Create a work for ptlrpc.
+ */
+void *ptlrpcd_alloc_work(struct obd_import *imp,
+ int (*cb)(const struct lu_env *, void *), void *cbdata)
+{
+ struct ptlrpc_request *req = NULL;
+ struct ptlrpc_work_async_args *args;
+
+ might_sleep();
+
+ if (cb == NULL)
+ return ERR_PTR(-EINVAL);
+
+ /* copy some code from deprecated fakereq. */
+ req = ptlrpc_request_cache_alloc(GFP_NOFS);
+ if (req == NULL) {
+ CERROR("ptlrpc: run out of memory!\n");
+ return ERR_PTR(-ENOMEM);
+ }
+
+ req->rq_send_state = LUSTRE_IMP_FULL;
+ req->rq_type = PTL_RPC_MSG_REQUEST;
+ req->rq_import = class_import_get(imp);
+ req->rq_export = NULL;
+ req->rq_interpret_reply = work_interpreter;
+ /* don't want reply */
+ req->rq_receiving_reply = 0;
+ req->rq_req_unlink = req->rq_reply_unlink = 0;
+ req->rq_no_delay = req->rq_no_resend = 1;
+ req->rq_pill.rc_fmt = (void *)&worker_format;
+
+ spin_lock_init(&req->rq_lock);
+ INIT_LIST_HEAD(&req->rq_list);
+ INIT_LIST_HEAD(&req->rq_replay_list);
+ INIT_LIST_HEAD(&req->rq_set_chain);
+ INIT_LIST_HEAD(&req->rq_history_list);
+ INIT_LIST_HEAD(&req->rq_exp_list);
+ init_waitqueue_head(&req->rq_reply_waitq);
+ init_waitqueue_head(&req->rq_set_waitq);
+ atomic_set(&req->rq_refcount, 1);
+
+ CLASSERT(sizeof(*args) <= sizeof(req->rq_async_args));
+ args = ptlrpc_req_async_args(req);
+ args->cb = cb;
+ args->cbdata = cbdata;
+
+ return req;
+}
+EXPORT_SYMBOL(ptlrpcd_alloc_work);
+
+void ptlrpcd_destroy_work(void *handler)
+{
+ struct ptlrpc_request *req = handler;
+
+ if (req)
+ ptlrpc_req_finished(req);
+}
+EXPORT_SYMBOL(ptlrpcd_destroy_work);
+
+int ptlrpcd_queue_work(void *handler)
+{
+ struct ptlrpc_request *req = handler;
+
+ /*
+ * Check if the req is already being queued.
+ *
+ * Here comes a trick: it lacks a way of checking if a req is being
+ * processed reliably in ptlrpc. Here I have to use refcount of req
+ * for this purpose. This is okay because the caller should use this
+ * req as opaque data. - Jinshan
+ */
+ LASSERT(atomic_read(&req->rq_refcount) > 0);
+ if (atomic_inc_return(&req->rq_refcount) == 2)
+ ptlrpcd_add_work_req(req);
+ return 0;
+}
+EXPORT_SYMBOL(ptlrpcd_queue_work);
diff --git a/drivers/staging/lustre/lustre/ptlrpc/connection.c b/drivers/staging/lustre/lustre/ptlrpc/connection.c
new file mode 100644
index 000000000..7e27397ce
--- /dev/null
+++ b/drivers/staging/lustre/lustre/ptlrpc/connection.c
@@ -0,0 +1,241 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2002, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2011, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ */
+
+#define DEBUG_SUBSYSTEM S_RPC
+#include "../include/obd_support.h"
+#include "../include/obd_class.h"
+#include "../include/lustre_net.h"
+
+#include "ptlrpc_internal.h"
+
+static struct cfs_hash *conn_hash;
+static cfs_hash_ops_t conn_hash_ops;
+
+struct ptlrpc_connection *
+ptlrpc_connection_get(lnet_process_id_t peer, lnet_nid_t self,
+ struct obd_uuid *uuid)
+{
+ struct ptlrpc_connection *conn, *conn2;
+
+ conn = cfs_hash_lookup(conn_hash, &peer);
+ if (conn)
+ goto out;
+
+ OBD_ALLOC_PTR(conn);
+ if (!conn)
+ return NULL;
+
+ conn->c_peer = peer;
+ conn->c_self = self;
+ INIT_HLIST_NODE(&conn->c_hash);
+ atomic_set(&conn->c_refcount, 1);
+ if (uuid)
+ obd_str2uuid(&conn->c_remote_uuid, uuid->uuid);
+
+ /*
+ * Add the newly created conn to the hash, on key collision we
+ * lost a racing addition and must destroy our newly allocated
+ * connection. The object which exists in the has will be
+ * returned and may be compared against out object.
+ */
+ /* In the function below, .hs_keycmp resolves to
+ * conn_keycmp() */
+ /* coverity[overrun-buffer-val] */
+ conn2 = cfs_hash_findadd_unique(conn_hash, &peer, &conn->c_hash);
+ if (conn != conn2) {
+ OBD_FREE_PTR(conn);
+ conn = conn2;
+ }
+out:
+ CDEBUG(D_INFO, "conn=%p refcount %d to %s\n",
+ conn, atomic_read(&conn->c_refcount),
+ libcfs_nid2str(conn->c_peer.nid));
+ return conn;
+}
+EXPORT_SYMBOL(ptlrpc_connection_get);
+
+int ptlrpc_connection_put(struct ptlrpc_connection *conn)
+{
+ int rc = 0;
+
+ if (!conn)
+ return rc;
+
+ LASSERT(atomic_read(&conn->c_refcount) > 1);
+
+ /*
+ * We do not remove connection from hashtable and
+ * do not free it even if last caller released ref,
+ * as we want to have it cached for the case it is
+ * needed again.
+ *
+ * Deallocating it and later creating new connection
+ * again would be wastful. This way we also avoid
+ * expensive locking to protect things from get/put
+ * race when found cached connection is freed by
+ * ptlrpc_connection_put().
+ *
+ * It will be freed later in module unload time,
+ * when ptlrpc_connection_fini()->lh_exit->conn_exit()
+ * path is called.
+ */
+ if (atomic_dec_return(&conn->c_refcount) == 1)
+ rc = 1;
+
+ CDEBUG(D_INFO, "PUT conn=%p refcount %d to %s\n",
+ conn, atomic_read(&conn->c_refcount),
+ libcfs_nid2str(conn->c_peer.nid));
+
+ return rc;
+}
+EXPORT_SYMBOL(ptlrpc_connection_put);
+
+struct ptlrpc_connection *
+ptlrpc_connection_addref(struct ptlrpc_connection *conn)
+{
+ atomic_inc(&conn->c_refcount);
+ CDEBUG(D_INFO, "conn=%p refcount %d to %s\n",
+ conn, atomic_read(&conn->c_refcount),
+ libcfs_nid2str(conn->c_peer.nid));
+
+ return conn;
+}
+EXPORT_SYMBOL(ptlrpc_connection_addref);
+
+int ptlrpc_connection_init(void)
+{
+ conn_hash = cfs_hash_create("CONN_HASH",
+ HASH_CONN_CUR_BITS,
+ HASH_CONN_MAX_BITS,
+ HASH_CONN_BKT_BITS, 0,
+ CFS_HASH_MIN_THETA,
+ CFS_HASH_MAX_THETA,
+ &conn_hash_ops, CFS_HASH_DEFAULT);
+ if (!conn_hash)
+ return -ENOMEM;
+
+ return 0;
+}
+EXPORT_SYMBOL(ptlrpc_connection_init);
+
+void ptlrpc_connection_fini(void)
+{
+ cfs_hash_putref(conn_hash);
+}
+EXPORT_SYMBOL(ptlrpc_connection_fini);
+
+/*
+ * Hash operations for net_peer<->connection
+ */
+static unsigned
+conn_hashfn(struct cfs_hash *hs, const void *key, unsigned mask)
+{
+ return cfs_hash_djb2_hash(key, sizeof(lnet_process_id_t), mask);
+}
+
+static int
+conn_keycmp(const void *key, struct hlist_node *hnode)
+{
+ struct ptlrpc_connection *conn;
+ const lnet_process_id_t *conn_key;
+
+ LASSERT(key != NULL);
+ conn_key = (lnet_process_id_t *)key;
+ conn = hlist_entry(hnode, struct ptlrpc_connection, c_hash);
+
+ return conn_key->nid == conn->c_peer.nid &&
+ conn_key->pid == conn->c_peer.pid;
+}
+
+static void *
+conn_key(struct hlist_node *hnode)
+{
+ struct ptlrpc_connection *conn;
+
+ conn = hlist_entry(hnode, struct ptlrpc_connection, c_hash);
+ return &conn->c_peer;
+}
+
+static void *
+conn_object(struct hlist_node *hnode)
+{
+ return hlist_entry(hnode, struct ptlrpc_connection, c_hash);
+}
+
+static void
+conn_get(struct cfs_hash *hs, struct hlist_node *hnode)
+{
+ struct ptlrpc_connection *conn;
+
+ conn = hlist_entry(hnode, struct ptlrpc_connection, c_hash);
+ atomic_inc(&conn->c_refcount);
+}
+
+static void
+conn_put_locked(struct cfs_hash *hs, struct hlist_node *hnode)
+{
+ struct ptlrpc_connection *conn;
+
+ conn = hlist_entry(hnode, struct ptlrpc_connection, c_hash);
+ atomic_dec(&conn->c_refcount);
+}
+
+static void
+conn_exit(struct cfs_hash *hs, struct hlist_node *hnode)
+{
+ struct ptlrpc_connection *conn;
+
+ conn = hlist_entry(hnode, struct ptlrpc_connection, c_hash);
+ /*
+ * Nothing should be left. Connection user put it and
+ * connection also was deleted from table by this time
+ * so we should have 0 refs.
+ */
+ LASSERTF(atomic_read(&conn->c_refcount) == 0,
+ "Busy connection with %d refs\n",
+ atomic_read(&conn->c_refcount));
+ OBD_FREE_PTR(conn);
+}
+
+static cfs_hash_ops_t conn_hash_ops = {
+ .hs_hash = conn_hashfn,
+ .hs_keycmp = conn_keycmp,
+ .hs_key = conn_key,
+ .hs_object = conn_object,
+ .hs_get = conn_get,
+ .hs_put_locked = conn_put_locked,
+ .hs_exit = conn_exit,
+};
diff --git a/drivers/staging/lustre/lustre/ptlrpc/errno.c b/drivers/staging/lustre/lustre/ptlrpc/errno.c
new file mode 100644
index 000000000..73f8374f1
--- /dev/null
+++ b/drivers/staging/lustre/lustre/ptlrpc/errno.c
@@ -0,0 +1,380 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.gnu.org/licenses/gpl-2.0.txt
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (C) 2011 FUJITSU LIMITED. All rights reserved.
+ *
+ * Copyright (c) 2013, Intel Corporation.
+ */
+
+#include "../../include/linux/libcfs/libcfs.h"
+#include "../include/lustre/lustre_errno.h"
+
+/*
+ * The two translation tables below must define a one-to-one mapping between
+ * host and network errnos.
+ *
+ * EWOULDBLOCK is equal to EAGAIN on all architectures except for parisc, which
+ * appears irrelevant. Thus, existing references to EWOULDBLOCK are fine.
+ *
+ * EDEADLOCK is equal to EDEADLK on x86 but not on sparc, at least. A sparc
+ * host has no context-free way to determine if a LUSTRE_EDEADLK represents an
+ * EDEADLK or an EDEADLOCK. Therefore, all existing references to EDEADLOCK
+ * that need to be transferred on wire have been replaced with EDEADLK.
+ */
+static int lustre_errno_hton_mapping[] = {
+ [EPERM] = LUSTRE_EPERM,
+ [ENOENT] = LUSTRE_ENOENT,
+ [ESRCH] = LUSTRE_ESRCH,
+ [EINTR] = LUSTRE_EINTR,
+ [EIO] = LUSTRE_EIO,
+ [ENXIO] = LUSTRE_ENXIO,
+ [E2BIG] = LUSTRE_E2BIG,
+ [ENOEXEC] = LUSTRE_ENOEXEC,
+ [EBADF] = LUSTRE_EBADF,
+ [ECHILD] = LUSTRE_ECHILD,
+ [EAGAIN] = LUSTRE_EAGAIN,
+ [ENOMEM] = LUSTRE_ENOMEM,
+ [EACCES] = LUSTRE_EACCES,
+ [EFAULT] = LUSTRE_EFAULT,
+ [ENOTBLK] = LUSTRE_ENOTBLK,
+ [EBUSY] = LUSTRE_EBUSY,
+ [EEXIST] = LUSTRE_EEXIST,
+ [EXDEV] = LUSTRE_EXDEV,
+ [ENODEV] = LUSTRE_ENODEV,
+ [ENOTDIR] = LUSTRE_ENOTDIR,
+ [EISDIR] = LUSTRE_EISDIR,
+ [EINVAL] = LUSTRE_EINVAL,
+ [ENFILE] = LUSTRE_ENFILE,
+ [EMFILE] = LUSTRE_EMFILE,
+ [ENOTTY] = LUSTRE_ENOTTY,
+ [ETXTBSY] = LUSTRE_ETXTBSY,
+ [EFBIG] = LUSTRE_EFBIG,
+ [ENOSPC] = LUSTRE_ENOSPC,
+ [ESPIPE] = LUSTRE_ESPIPE,
+ [EROFS] = LUSTRE_EROFS,
+ [EMLINK] = LUSTRE_EMLINK,
+ [EPIPE] = LUSTRE_EPIPE,
+ [EDOM] = LUSTRE_EDOM,
+ [ERANGE] = LUSTRE_ERANGE,
+ [EDEADLK] = LUSTRE_EDEADLK,
+ [ENAMETOOLONG] = LUSTRE_ENAMETOOLONG,
+ [ENOLCK] = LUSTRE_ENOLCK,
+ [ENOSYS] = LUSTRE_ENOSYS,
+ [ENOTEMPTY] = LUSTRE_ENOTEMPTY,
+ [ELOOP] = LUSTRE_ELOOP,
+ [ENOMSG] = LUSTRE_ENOMSG,
+ [EIDRM] = LUSTRE_EIDRM,
+ [ECHRNG] = LUSTRE_ECHRNG,
+ [EL2NSYNC] = LUSTRE_EL2NSYNC,
+ [EL3HLT] = LUSTRE_EL3HLT,
+ [EL3RST] = LUSTRE_EL3RST,
+ [ELNRNG] = LUSTRE_ELNRNG,
+ [EUNATCH] = LUSTRE_EUNATCH,
+ [ENOCSI] = LUSTRE_ENOCSI,
+ [EL2HLT] = LUSTRE_EL2HLT,
+ [EBADE] = LUSTRE_EBADE,
+ [EBADR] = LUSTRE_EBADR,
+ [EXFULL] = LUSTRE_EXFULL,
+ [ENOANO] = LUSTRE_ENOANO,
+ [EBADRQC] = LUSTRE_EBADRQC,
+ [EBADSLT] = LUSTRE_EBADSLT,
+ [EBFONT] = LUSTRE_EBFONT,
+ [ENOSTR] = LUSTRE_ENOSTR,
+ [ENODATA] = LUSTRE_ENODATA,
+ [ETIME] = LUSTRE_ETIME,
+ [ENOSR] = LUSTRE_ENOSR,
+ [ENONET] = LUSTRE_ENONET,
+ [ENOPKG] = LUSTRE_ENOPKG,
+ [EREMOTE] = LUSTRE_EREMOTE,
+ [ENOLINK] = LUSTRE_ENOLINK,
+ [EADV] = LUSTRE_EADV,
+ [ESRMNT] = LUSTRE_ESRMNT,
+ [ECOMM] = LUSTRE_ECOMM,
+ [EPROTO] = LUSTRE_EPROTO,
+ [EMULTIHOP] = LUSTRE_EMULTIHOP,
+ [EDOTDOT] = LUSTRE_EDOTDOT,
+ [EBADMSG] = LUSTRE_EBADMSG,
+ [EOVERFLOW] = LUSTRE_EOVERFLOW,
+ [ENOTUNIQ] = LUSTRE_ENOTUNIQ,
+ [EBADFD] = LUSTRE_EBADFD,
+ [EREMCHG] = LUSTRE_EREMCHG,
+ [ELIBACC] = LUSTRE_ELIBACC,
+ [ELIBBAD] = LUSTRE_ELIBBAD,
+ [ELIBSCN] = LUSTRE_ELIBSCN,
+ [ELIBMAX] = LUSTRE_ELIBMAX,
+ [ELIBEXEC] = LUSTRE_ELIBEXEC,
+ [EILSEQ] = LUSTRE_EILSEQ,
+ [ERESTART] = LUSTRE_ERESTART,
+ [ESTRPIPE] = LUSTRE_ESTRPIPE,
+ [EUSERS] = LUSTRE_EUSERS,
+ [ENOTSOCK] = LUSTRE_ENOTSOCK,
+ [EDESTADDRREQ] = LUSTRE_EDESTADDRREQ,
+ [EMSGSIZE] = LUSTRE_EMSGSIZE,
+ [EPROTOTYPE] = LUSTRE_EPROTOTYPE,
+ [ENOPROTOOPT] = LUSTRE_ENOPROTOOPT,
+ [EPROTONOSUPPORT] = LUSTRE_EPROTONOSUPPORT,
+ [ESOCKTNOSUPPORT] = LUSTRE_ESOCKTNOSUPPORT,
+ [EOPNOTSUPP] = LUSTRE_EOPNOTSUPP,
+ [EPFNOSUPPORT] = LUSTRE_EPFNOSUPPORT,
+ [EAFNOSUPPORT] = LUSTRE_EAFNOSUPPORT,
+ [EADDRINUSE] = LUSTRE_EADDRINUSE,
+ [EADDRNOTAVAIL] = LUSTRE_EADDRNOTAVAIL,
+ [ENETDOWN] = LUSTRE_ENETDOWN,
+ [ENETUNREACH] = LUSTRE_ENETUNREACH,
+ [ENETRESET] = LUSTRE_ENETRESET,
+ [ECONNABORTED] = LUSTRE_ECONNABORTED,
+ [ECONNRESET] = LUSTRE_ECONNRESET,
+ [ENOBUFS] = LUSTRE_ENOBUFS,
+ [EISCONN] = LUSTRE_EISCONN,
+ [ENOTCONN] = LUSTRE_ENOTCONN,
+ [ESHUTDOWN] = LUSTRE_ESHUTDOWN,
+ [ETOOMANYREFS] = LUSTRE_ETOOMANYREFS,
+ [ETIMEDOUT] = LUSTRE_ETIMEDOUT,
+ [ECONNREFUSED] = LUSTRE_ECONNREFUSED,
+ [EHOSTDOWN] = LUSTRE_EHOSTDOWN,
+ [EHOSTUNREACH] = LUSTRE_EHOSTUNREACH,
+ [EALREADY] = LUSTRE_EALREADY,
+ [EINPROGRESS] = LUSTRE_EINPROGRESS,
+ [ESTALE] = LUSTRE_ESTALE,
+ [EUCLEAN] = LUSTRE_EUCLEAN,
+ [ENOTNAM] = LUSTRE_ENOTNAM,
+ [ENAVAIL] = LUSTRE_ENAVAIL,
+ [EISNAM] = LUSTRE_EISNAM,
+ [EREMOTEIO] = LUSTRE_EREMOTEIO,
+ [EDQUOT] = LUSTRE_EDQUOT,
+ [ENOMEDIUM] = LUSTRE_ENOMEDIUM,
+ [EMEDIUMTYPE] = LUSTRE_EMEDIUMTYPE,
+ [ECANCELED] = LUSTRE_ECANCELED,
+ [ENOKEY] = LUSTRE_ENOKEY,
+ [EKEYEXPIRED] = LUSTRE_EKEYEXPIRED,
+ [EKEYREVOKED] = LUSTRE_EKEYREVOKED,
+ [EKEYREJECTED] = LUSTRE_EKEYREJECTED,
+ [EOWNERDEAD] = LUSTRE_EOWNERDEAD,
+ [ENOTRECOVERABLE] = LUSTRE_ENOTRECOVERABLE,
+ [ERESTARTSYS] = LUSTRE_ERESTARTSYS,
+ [ERESTARTNOINTR] = LUSTRE_ERESTARTNOINTR,
+ [ERESTARTNOHAND] = LUSTRE_ERESTARTNOHAND,
+ [ENOIOCTLCMD] = LUSTRE_ENOIOCTLCMD,
+ [ERESTART_RESTARTBLOCK] = LUSTRE_ERESTART_RESTARTBLOCK,
+ [EBADHANDLE] = LUSTRE_EBADHANDLE,
+ [ENOTSYNC] = LUSTRE_ENOTSYNC,
+ [EBADCOOKIE] = LUSTRE_EBADCOOKIE,
+ [ENOTSUPP] = LUSTRE_ENOTSUPP,
+ [ETOOSMALL] = LUSTRE_ETOOSMALL,
+ [ESERVERFAULT] = LUSTRE_ESERVERFAULT,
+ [EBADTYPE] = LUSTRE_EBADTYPE,
+ [EJUKEBOX] = LUSTRE_EJUKEBOX,
+ [EIOCBQUEUED] = LUSTRE_EIOCBQUEUED,
+};
+
+static int lustre_errno_ntoh_mapping[] = {
+ [LUSTRE_EPERM] = EPERM,
+ [LUSTRE_ENOENT] = ENOENT,
+ [LUSTRE_ESRCH] = ESRCH,
+ [LUSTRE_EINTR] = EINTR,
+ [LUSTRE_EIO] = EIO,
+ [LUSTRE_ENXIO] = ENXIO,
+ [LUSTRE_E2BIG] = E2BIG,
+ [LUSTRE_ENOEXEC] = ENOEXEC,
+ [LUSTRE_EBADF] = EBADF,
+ [LUSTRE_ECHILD] = ECHILD,
+ [LUSTRE_EAGAIN] = EAGAIN,
+ [LUSTRE_ENOMEM] = ENOMEM,
+ [LUSTRE_EACCES] = EACCES,
+ [LUSTRE_EFAULT] = EFAULT,
+ [LUSTRE_ENOTBLK] = ENOTBLK,
+ [LUSTRE_EBUSY] = EBUSY,
+ [LUSTRE_EEXIST] = EEXIST,
+ [LUSTRE_EXDEV] = EXDEV,
+ [LUSTRE_ENODEV] = ENODEV,
+ [LUSTRE_ENOTDIR] = ENOTDIR,
+ [LUSTRE_EISDIR] = EISDIR,
+ [LUSTRE_EINVAL] = EINVAL,
+ [LUSTRE_ENFILE] = ENFILE,
+ [LUSTRE_EMFILE] = EMFILE,
+ [LUSTRE_ENOTTY] = ENOTTY,
+ [LUSTRE_ETXTBSY] = ETXTBSY,
+ [LUSTRE_EFBIG] = EFBIG,
+ [LUSTRE_ENOSPC] = ENOSPC,
+ [LUSTRE_ESPIPE] = ESPIPE,
+ [LUSTRE_EROFS] = EROFS,
+ [LUSTRE_EMLINK] = EMLINK,
+ [LUSTRE_EPIPE] = EPIPE,
+ [LUSTRE_EDOM] = EDOM,
+ [LUSTRE_ERANGE] = ERANGE,
+ [LUSTRE_EDEADLK] = EDEADLK,
+ [LUSTRE_ENAMETOOLONG] = ENAMETOOLONG,
+ [LUSTRE_ENOLCK] = ENOLCK,
+ [LUSTRE_ENOSYS] = ENOSYS,
+ [LUSTRE_ENOTEMPTY] = ENOTEMPTY,
+ [LUSTRE_ELOOP] = ELOOP,
+ [LUSTRE_ENOMSG] = ENOMSG,
+ [LUSTRE_EIDRM] = EIDRM,
+ [LUSTRE_ECHRNG] = ECHRNG,
+ [LUSTRE_EL2NSYNC] = EL2NSYNC,
+ [LUSTRE_EL3HLT] = EL3HLT,
+ [LUSTRE_EL3RST] = EL3RST,
+ [LUSTRE_ELNRNG] = ELNRNG,
+ [LUSTRE_EUNATCH] = EUNATCH,
+ [LUSTRE_ENOCSI] = ENOCSI,
+ [LUSTRE_EL2HLT] = EL2HLT,
+ [LUSTRE_EBADE] = EBADE,
+ [LUSTRE_EBADR] = EBADR,
+ [LUSTRE_EXFULL] = EXFULL,
+ [LUSTRE_ENOANO] = ENOANO,
+ [LUSTRE_EBADRQC] = EBADRQC,
+ [LUSTRE_EBADSLT] = EBADSLT,
+ [LUSTRE_EBFONT] = EBFONT,
+ [LUSTRE_ENOSTR] = ENOSTR,
+ [LUSTRE_ENODATA] = ENODATA,
+ [LUSTRE_ETIME] = ETIME,
+ [LUSTRE_ENOSR] = ENOSR,
+ [LUSTRE_ENONET] = ENONET,
+ [LUSTRE_ENOPKG] = ENOPKG,
+ [LUSTRE_EREMOTE] = EREMOTE,
+ [LUSTRE_ENOLINK] = ENOLINK,
+ [LUSTRE_EADV] = EADV,
+ [LUSTRE_ESRMNT] = ESRMNT,
+ [LUSTRE_ECOMM] = ECOMM,
+ [LUSTRE_EPROTO] = EPROTO,
+ [LUSTRE_EMULTIHOP] = EMULTIHOP,
+ [LUSTRE_EDOTDOT] = EDOTDOT,
+ [LUSTRE_EBADMSG] = EBADMSG,
+ [LUSTRE_EOVERFLOW] = EOVERFLOW,
+ [LUSTRE_ENOTUNIQ] = ENOTUNIQ,
+ [LUSTRE_EBADFD] = EBADFD,
+ [LUSTRE_EREMCHG] = EREMCHG,
+ [LUSTRE_ELIBACC] = ELIBACC,
+ [LUSTRE_ELIBBAD] = ELIBBAD,
+ [LUSTRE_ELIBSCN] = ELIBSCN,
+ [LUSTRE_ELIBMAX] = ELIBMAX,
+ [LUSTRE_ELIBEXEC] = ELIBEXEC,
+ [LUSTRE_EILSEQ] = EILSEQ,
+ [LUSTRE_ERESTART] = ERESTART,
+ [LUSTRE_ESTRPIPE] = ESTRPIPE,
+ [LUSTRE_EUSERS] = EUSERS,
+ [LUSTRE_ENOTSOCK] = ENOTSOCK,
+ [LUSTRE_EDESTADDRREQ] = EDESTADDRREQ,
+ [LUSTRE_EMSGSIZE] = EMSGSIZE,
+ [LUSTRE_EPROTOTYPE] = EPROTOTYPE,
+ [LUSTRE_ENOPROTOOPT] = ENOPROTOOPT,
+ [LUSTRE_EPROTONOSUPPORT] = EPROTONOSUPPORT,
+ [LUSTRE_ESOCKTNOSUPPORT] = ESOCKTNOSUPPORT,
+ [LUSTRE_EOPNOTSUPP] = EOPNOTSUPP,
+ [LUSTRE_EPFNOSUPPORT] = EPFNOSUPPORT,
+ [LUSTRE_EAFNOSUPPORT] = EAFNOSUPPORT,
+ [LUSTRE_EADDRINUSE] = EADDRINUSE,
+ [LUSTRE_EADDRNOTAVAIL] = EADDRNOTAVAIL,
+ [LUSTRE_ENETDOWN] = ENETDOWN,
+ [LUSTRE_ENETUNREACH] = ENETUNREACH,
+ [LUSTRE_ENETRESET] = ENETRESET,
+ [LUSTRE_ECONNABORTED] = ECONNABORTED,
+ [LUSTRE_ECONNRESET] = ECONNRESET,
+ [LUSTRE_ENOBUFS] = ENOBUFS,
+ [LUSTRE_EISCONN] = EISCONN,
+ [LUSTRE_ENOTCONN] = ENOTCONN,
+ [LUSTRE_ESHUTDOWN] = ESHUTDOWN,
+ [LUSTRE_ETOOMANYREFS] = ETOOMANYREFS,
+ [LUSTRE_ETIMEDOUT] = ETIMEDOUT,
+ [LUSTRE_ECONNREFUSED] = ECONNREFUSED,
+ [LUSTRE_EHOSTDOWN] = EHOSTDOWN,
+ [LUSTRE_EHOSTUNREACH] = EHOSTUNREACH,
+ [LUSTRE_EALREADY] = EALREADY,
+ [LUSTRE_EINPROGRESS] = EINPROGRESS,
+ [LUSTRE_ESTALE] = ESTALE,
+ [LUSTRE_EUCLEAN] = EUCLEAN,
+ [LUSTRE_ENOTNAM] = ENOTNAM,
+ [LUSTRE_ENAVAIL] = ENAVAIL,
+ [LUSTRE_EISNAM] = EISNAM,
+ [LUSTRE_EREMOTEIO] = EREMOTEIO,
+ [LUSTRE_EDQUOT] = EDQUOT,
+ [LUSTRE_ENOMEDIUM] = ENOMEDIUM,
+ [LUSTRE_EMEDIUMTYPE] = EMEDIUMTYPE,
+ [LUSTRE_ECANCELED] = ECANCELED,
+ [LUSTRE_ENOKEY] = ENOKEY,
+ [LUSTRE_EKEYEXPIRED] = EKEYEXPIRED,
+ [LUSTRE_EKEYREVOKED] = EKEYREVOKED,
+ [LUSTRE_EKEYREJECTED] = EKEYREJECTED,
+ [LUSTRE_EOWNERDEAD] = EOWNERDEAD,
+ [LUSTRE_ENOTRECOVERABLE] = ENOTRECOVERABLE,
+ [LUSTRE_ERESTARTSYS] = ERESTARTSYS,
+ [LUSTRE_ERESTARTNOINTR] = ERESTARTNOINTR,
+ [LUSTRE_ERESTARTNOHAND] = ERESTARTNOHAND,
+ [LUSTRE_ENOIOCTLCMD] = ENOIOCTLCMD,
+ [LUSTRE_ERESTART_RESTARTBLOCK] = ERESTART_RESTARTBLOCK,
+ [LUSTRE_EBADHANDLE] = EBADHANDLE,
+ [LUSTRE_ENOTSYNC] = ENOTSYNC,
+ [LUSTRE_EBADCOOKIE] = EBADCOOKIE,
+ [LUSTRE_ENOTSUPP] = ENOTSUPP,
+ [LUSTRE_ETOOSMALL] = ETOOSMALL,
+ [LUSTRE_ESERVERFAULT] = ESERVERFAULT,
+ [LUSTRE_EBADTYPE] = EBADTYPE,
+ [LUSTRE_EJUKEBOX] = EJUKEBOX,
+ [LUSTRE_EIOCBQUEUED] = EIOCBQUEUED,
+};
+
+unsigned int lustre_errno_hton(unsigned int h)
+{
+ unsigned int n;
+
+ if (h == 0) {
+ n = 0;
+ } else if (h < ARRAY_SIZE(lustre_errno_hton_mapping)) {
+ n = lustre_errno_hton_mapping[h];
+ if (n == 0)
+ goto generic;
+ } else {
+generic:
+ /*
+ * A generic errno is better than the unknown one that could
+ * mean anything to a different host.
+ */
+ n = LUSTRE_EIO;
+ }
+
+ return n;
+}
+EXPORT_SYMBOL(lustre_errno_hton);
+
+unsigned int lustre_errno_ntoh(unsigned int n)
+{
+ unsigned int h;
+
+ if (n == 0) {
+ h = 0;
+ } else if (n < ARRAY_SIZE(lustre_errno_ntoh_mapping)) {
+ h = lustre_errno_ntoh_mapping[n];
+ if (h == 0)
+ goto generic;
+ } else {
+generic:
+ /*
+ * Similar to the situation in lustre_errno_hton(), an unknown
+ * network errno could coincide with anything. Hence, it is
+ * better to return a generic errno.
+ */
+ h = EIO;
+ }
+
+ return h;
+}
+EXPORT_SYMBOL(lustre_errno_ntoh);
diff --git a/drivers/staging/lustre/lustre/ptlrpc/events.c b/drivers/staging/lustre/lustre/ptlrpc/events.c
new file mode 100644
index 000000000..7f8644e01
--- /dev/null
+++ b/drivers/staging/lustre/lustre/ptlrpc/events.c
@@ -0,0 +1,585 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2002, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2012, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ */
+
+#define DEBUG_SUBSYSTEM S_RPC
+
+#include "../../include/linux/libcfs/libcfs.h"
+# ifdef __mips64__
+# include <linux/kernel.h>
+# endif
+
+#include "../include/obd_class.h"
+#include "../include/lustre_net.h"
+#include "../include/lustre_sec.h"
+#include "ptlrpc_internal.h"
+
+lnet_handle_eq_t ptlrpc_eq_h;
+
+/*
+ * Client's outgoing request callback
+ */
+void request_out_callback(lnet_event_t *ev)
+{
+ struct ptlrpc_cb_id *cbid = ev->md.user_ptr;
+ struct ptlrpc_request *req = cbid->cbid_arg;
+
+ LASSERT(ev->type == LNET_EVENT_SEND ||
+ ev->type == LNET_EVENT_UNLINK);
+ LASSERT(ev->unlinked);
+
+ DEBUG_REQ(D_NET, req, "type %d, status %d", ev->type, ev->status);
+
+ sptlrpc_request_out_callback(req);
+ spin_lock(&req->rq_lock);
+ req->rq_real_sent = get_seconds();
+ if (ev->unlinked)
+ req->rq_req_unlink = 0;
+
+ if (ev->type == LNET_EVENT_UNLINK || ev->status != 0) {
+
+ /* Failed send: make it seem like the reply timed out, just
+ * like failing sends in client.c does currently... */
+
+ req->rq_net_err = 1;
+ ptlrpc_client_wake_req(req);
+ }
+ spin_unlock(&req->rq_lock);
+
+ ptlrpc_req_finished(req);
+}
+
+/*
+ * Client's incoming reply callback
+ */
+void reply_in_callback(lnet_event_t *ev)
+{
+ struct ptlrpc_cb_id *cbid = ev->md.user_ptr;
+ struct ptlrpc_request *req = cbid->cbid_arg;
+
+ DEBUG_REQ(D_NET, req, "type %d, status %d", ev->type, ev->status);
+
+ LASSERT(ev->type == LNET_EVENT_PUT || ev->type == LNET_EVENT_UNLINK);
+ LASSERT(ev->md.start == req->rq_repbuf);
+ LASSERT(ev->offset + ev->mlength <= req->rq_repbuf_len);
+ /* We've set LNET_MD_MANAGE_REMOTE for all outgoing requests
+ for adaptive timeouts' early reply. */
+ LASSERT((ev->md.options & LNET_MD_MANAGE_REMOTE) != 0);
+
+ spin_lock(&req->rq_lock);
+
+ req->rq_receiving_reply = 0;
+ req->rq_early = 0;
+ if (ev->unlinked)
+ req->rq_reply_unlink = 0;
+
+ if (ev->status)
+ goto out_wake;
+
+ if (ev->type == LNET_EVENT_UNLINK) {
+ LASSERT(ev->unlinked);
+ DEBUG_REQ(D_NET, req, "unlink");
+ goto out_wake;
+ }
+
+ if (ev->mlength < ev->rlength) {
+ CDEBUG(D_RPCTRACE, "truncate req %p rpc %d - %d+%d\n", req,
+ req->rq_replen, ev->rlength, ev->offset);
+ req->rq_reply_truncate = 1;
+ req->rq_replied = 1;
+ req->rq_status = -EOVERFLOW;
+ req->rq_nob_received = ev->rlength + ev->offset;
+ goto out_wake;
+ }
+
+ if ((ev->offset == 0) &&
+ ((lustre_msghdr_get_flags(req->rq_reqmsg) & MSGHDR_AT_SUPPORT))) {
+ /* Early reply */
+ DEBUG_REQ(D_ADAPTTO, req,
+ "Early reply received: mlen=%u offset=%d replen=%d replied=%d unlinked=%d",
+ ev->mlength, ev->offset,
+ req->rq_replen, req->rq_replied, ev->unlinked);
+
+ req->rq_early_count++; /* number received, client side */
+
+ if (req->rq_replied) /* already got the real reply */
+ goto out_wake;
+
+ req->rq_early = 1;
+ req->rq_reply_off = ev->offset;
+ req->rq_nob_received = ev->mlength;
+ /* And we're still receiving */
+ req->rq_receiving_reply = 1;
+ } else {
+ /* Real reply */
+ req->rq_rep_swab_mask = 0;
+ req->rq_replied = 1;
+ /* Got reply, no resend required */
+ req->rq_resend = 0;
+ req->rq_reply_off = ev->offset;
+ req->rq_nob_received = ev->mlength;
+ /* LNetMDUnlink can't be called under the LNET_LOCK,
+ so we must unlink in ptlrpc_unregister_reply */
+ DEBUG_REQ(D_INFO, req,
+ "reply in flags=%x mlen=%u offset=%d replen=%d",
+ lustre_msg_get_flags(req->rq_reqmsg),
+ ev->mlength, ev->offset, req->rq_replen);
+ }
+
+ req->rq_import->imp_last_reply_time = get_seconds();
+
+out_wake:
+ /* NB don't unlock till after wakeup; req can disappear under us
+ * since we don't have our own ref */
+ ptlrpc_client_wake_req(req);
+ spin_unlock(&req->rq_lock);
+}
+
+/*
+ * Client's bulk has been written/read
+ */
+void client_bulk_callback(lnet_event_t *ev)
+{
+ struct ptlrpc_cb_id *cbid = ev->md.user_ptr;
+ struct ptlrpc_bulk_desc *desc = cbid->cbid_arg;
+ struct ptlrpc_request *req;
+
+ LASSERT((desc->bd_type == BULK_PUT_SINK &&
+ ev->type == LNET_EVENT_PUT) ||
+ (desc->bd_type == BULK_GET_SOURCE &&
+ ev->type == LNET_EVENT_GET) ||
+ ev->type == LNET_EVENT_UNLINK);
+ LASSERT(ev->unlinked);
+
+ if (CFS_FAIL_CHECK_ORSET(OBD_FAIL_PTLRPC_CLIENT_BULK_CB, CFS_FAIL_ONCE))
+ ev->status = -EIO;
+
+ if (CFS_FAIL_CHECK_ORSET(OBD_FAIL_PTLRPC_CLIENT_BULK_CB2,
+ CFS_FAIL_ONCE))
+ ev->status = -EIO;
+
+ CDEBUG((ev->status == 0) ? D_NET : D_ERROR,
+ "event type %d, status %d, desc %p\n",
+ ev->type, ev->status, desc);
+
+ spin_lock(&desc->bd_lock);
+ req = desc->bd_req;
+ LASSERT(desc->bd_md_count > 0);
+ desc->bd_md_count--;
+
+ if (ev->type != LNET_EVENT_UNLINK && ev->status == 0) {
+ desc->bd_nob_transferred += ev->mlength;
+ desc->bd_sender = ev->sender;
+ } else {
+ /* start reconnect and resend if network error hit */
+ spin_lock(&req->rq_lock);
+ req->rq_net_err = 1;
+ spin_unlock(&req->rq_lock);
+ }
+
+ if (ev->status != 0)
+ desc->bd_failure = 1;
+
+ /* NB don't unlock till after wakeup; desc can disappear under us
+ * otherwise */
+ if (desc->bd_md_count == 0)
+ ptlrpc_client_wake_req(desc->bd_req);
+
+ spin_unlock(&desc->bd_lock);
+}
+
+/*
+ * We will have percpt request history list for ptlrpc service in upcoming
+ * patches because we don't want to be serialized by current per-service
+ * history operations. So we require history ID can (somehow) show arriving
+ * order w/o grabbing global lock, and user can sort them in userspace.
+ *
+ * This is how we generate history ID for ptlrpc_request:
+ * ----------------------------------------------------
+ * | 32 bits | 16 bits | (16 - X)bits | X bits |
+ * ----------------------------------------------------
+ * | seconds | usec / 16 | sequence | CPT id |
+ * ----------------------------------------------------
+ *
+ * it might not be precise but should be good enough.
+ */
+
+#define REQS_CPT_BITS(svcpt) ((svcpt)->scp_service->srv_cpt_bits)
+
+#define REQS_SEC_SHIFT 32
+#define REQS_USEC_SHIFT 16
+#define REQS_SEQ_SHIFT(svcpt) REQS_CPT_BITS(svcpt)
+
+static void ptlrpc_req_add_history(struct ptlrpc_service_part *svcpt,
+ struct ptlrpc_request *req)
+{
+ __u64 sec = req->rq_arrival_time.tv_sec;
+ __u32 usec = req->rq_arrival_time.tv_usec >> 4; /* usec / 16 */
+ __u64 new_seq;
+
+ /* set sequence ID for request and add it to history list,
+ * it must be called with hold svcpt::scp_lock */
+
+ new_seq = (sec << REQS_SEC_SHIFT) |
+ (usec << REQS_USEC_SHIFT) |
+ (svcpt->scp_cpt < 0 ? 0 : svcpt->scp_cpt);
+
+ if (new_seq > svcpt->scp_hist_seq) {
+ /* This handles the initial case of scp_hist_seq == 0 or
+ * we just jumped into a new time window */
+ svcpt->scp_hist_seq = new_seq;
+ } else {
+ LASSERT(REQS_SEQ_SHIFT(svcpt) < REQS_USEC_SHIFT);
+ /* NB: increase sequence number in current usec bucket,
+ * however, it's possible that we used up all bits for
+ * sequence and jumped into the next usec bucket (future time),
+ * then we hope there will be less RPCs per bucket at some
+ * point, and sequence will catch up again */
+ svcpt->scp_hist_seq += (1U << REQS_SEQ_SHIFT(svcpt));
+ new_seq = svcpt->scp_hist_seq;
+ }
+
+ req->rq_history_seq = new_seq;
+
+ list_add_tail(&req->rq_history_list, &svcpt->scp_hist_reqs);
+}
+
+/*
+ * Server's incoming request callback
+ */
+void request_in_callback(lnet_event_t *ev)
+{
+ struct ptlrpc_cb_id *cbid = ev->md.user_ptr;
+ struct ptlrpc_request_buffer_desc *rqbd = cbid->cbid_arg;
+ struct ptlrpc_service_part *svcpt = rqbd->rqbd_svcpt;
+ struct ptlrpc_service *service = svcpt->scp_service;
+ struct ptlrpc_request *req;
+
+ LASSERT(ev->type == LNET_EVENT_PUT ||
+ ev->type == LNET_EVENT_UNLINK);
+ LASSERT((char *)ev->md.start >= rqbd->rqbd_buffer);
+ LASSERT((char *)ev->md.start + ev->offset + ev->mlength <=
+ rqbd->rqbd_buffer + service->srv_buf_size);
+
+ CDEBUG((ev->status == 0) ? D_NET : D_ERROR,
+ "event type %d, status %d, service %s\n",
+ ev->type, ev->status, service->srv_name);
+
+ if (ev->unlinked) {
+ /* If this is the last request message to fit in the
+ * request buffer we can use the request object embedded in
+ * rqbd. Note that if we failed to allocate a request,
+ * we'd have to re-post the rqbd, which we can't do in this
+ * context. */
+ req = &rqbd->rqbd_req;
+ memset(req, 0, sizeof(*req));
+ } else {
+ LASSERT(ev->type == LNET_EVENT_PUT);
+ if (ev->status != 0) {
+ /* We moaned above already... */
+ return;
+ }
+ req = ptlrpc_request_cache_alloc(GFP_ATOMIC);
+ if (req == NULL) {
+ CERROR("Can't allocate incoming request descriptor: Dropping %s RPC from %s\n",
+ service->srv_name,
+ libcfs_id2str(ev->initiator));
+ return;
+ }
+ }
+
+ /* NB we ABSOLUTELY RELY on req being zeroed, so pointers are NULL,
+ * flags are reset and scalars are zero. We only set the message
+ * size to non-zero if this was a successful receive. */
+ req->rq_xid = ev->match_bits;
+ req->rq_reqbuf = ev->md.start + ev->offset;
+ if (ev->type == LNET_EVENT_PUT && ev->status == 0)
+ req->rq_reqdata_len = ev->mlength;
+ do_gettimeofday(&req->rq_arrival_time);
+ req->rq_peer = ev->initiator;
+ req->rq_self = ev->target.nid;
+ req->rq_rqbd = rqbd;
+ req->rq_phase = RQ_PHASE_NEW;
+ spin_lock_init(&req->rq_lock);
+ INIT_LIST_HEAD(&req->rq_timed_list);
+ INIT_LIST_HEAD(&req->rq_exp_list);
+ atomic_set(&req->rq_refcount, 1);
+ if (ev->type == LNET_EVENT_PUT)
+ CDEBUG(D_INFO, "incoming req@%p x%llu msgsize %u\n",
+ req, req->rq_xid, ev->mlength);
+
+ CDEBUG(D_RPCTRACE, "peer: %s\n", libcfs_id2str(req->rq_peer));
+
+ spin_lock(&svcpt->scp_lock);
+
+ ptlrpc_req_add_history(svcpt, req);
+
+ if (ev->unlinked) {
+ svcpt->scp_nrqbds_posted--;
+ CDEBUG(D_INFO, "Buffer complete: %d buffers still posted\n",
+ svcpt->scp_nrqbds_posted);
+
+ /* Normally, don't complain about 0 buffers posted; LNET won't
+ * drop incoming reqs since we set the portal lazy */
+ if (test_req_buffer_pressure &&
+ ev->type != LNET_EVENT_UNLINK &&
+ svcpt->scp_nrqbds_posted == 0)
+ CWARN("All %s request buffers busy\n",
+ service->srv_name);
+
+ /* req takes over the network's ref on rqbd */
+ } else {
+ /* req takes a ref on rqbd */
+ rqbd->rqbd_refcount++;
+ }
+
+ list_add_tail(&req->rq_list, &svcpt->scp_req_incoming);
+ svcpt->scp_nreqs_incoming++;
+
+ /* NB everything can disappear under us once the request
+ * has been queued and we unlock, so do the wake now... */
+ wake_up(&svcpt->scp_waitq);
+
+ spin_unlock(&svcpt->scp_lock);
+}
+
+/*
+ * Server's outgoing reply callback
+ */
+void reply_out_callback(lnet_event_t *ev)
+{
+ struct ptlrpc_cb_id *cbid = ev->md.user_ptr;
+ struct ptlrpc_reply_state *rs = cbid->cbid_arg;
+ struct ptlrpc_service_part *svcpt = rs->rs_svcpt;
+
+ LASSERT(ev->type == LNET_EVENT_SEND ||
+ ev->type == LNET_EVENT_ACK ||
+ ev->type == LNET_EVENT_UNLINK);
+
+ if (!rs->rs_difficult) {
+ /* 'Easy' replies have no further processing so I drop the
+ * net's ref on 'rs' */
+ LASSERT(ev->unlinked);
+ ptlrpc_rs_decref(rs);
+ return;
+ }
+
+ LASSERT(rs->rs_on_net);
+
+ if (ev->unlinked) {
+ /* Last network callback. The net's ref on 'rs' stays put
+ * until ptlrpc_handle_rs() is done with it */
+ spin_lock(&svcpt->scp_rep_lock);
+ spin_lock(&rs->rs_lock);
+
+ rs->rs_on_net = 0;
+ if (!rs->rs_no_ack ||
+ rs->rs_transno <=
+ rs->rs_export->exp_obd->obd_last_committed)
+ ptlrpc_schedule_difficult_reply(rs);
+
+ spin_unlock(&rs->rs_lock);
+ spin_unlock(&svcpt->scp_rep_lock);
+ }
+}
+
+
+static void ptlrpc_master_callback(lnet_event_t *ev)
+{
+ struct ptlrpc_cb_id *cbid = ev->md.user_ptr;
+ void (*callback)(lnet_event_t *ev) = cbid->cbid_fn;
+
+ /* Honestly, it's best to find out early. */
+ LASSERT(cbid->cbid_arg != LP_POISON);
+ LASSERT(callback == request_out_callback ||
+ callback == reply_in_callback ||
+ callback == client_bulk_callback ||
+ callback == request_in_callback ||
+ callback == reply_out_callback);
+
+ callback(ev);
+}
+
+int ptlrpc_uuid_to_peer(struct obd_uuid *uuid,
+ lnet_process_id_t *peer, lnet_nid_t *self)
+{
+ int best_dist = 0;
+ __u32 best_order = 0;
+ int count = 0;
+ int rc = -ENOENT;
+ int portals_compatibility;
+ int dist;
+ __u32 order;
+ lnet_nid_t dst_nid;
+ lnet_nid_t src_nid;
+
+ portals_compatibility = LNetCtl(IOC_LIBCFS_PORTALS_COMPATIBILITY, NULL);
+
+ peer->pid = LUSTRE_SRV_LNET_PID;
+
+ /* Choose the matching UUID that's closest */
+ while (lustre_uuid_to_peer(uuid->uuid, &dst_nid, count++) == 0) {
+ dist = LNetDist(dst_nid, &src_nid, &order);
+ if (dist < 0)
+ continue;
+
+ if (dist == 0) { /* local! use loopback LND */
+ peer->nid = *self = LNET_MKNID(LNET_MKNET(LOLND, 0), 0);
+ rc = 0;
+ break;
+ }
+
+ if (rc < 0 ||
+ dist < best_dist ||
+ (dist == best_dist && order < best_order)) {
+ best_dist = dist;
+ best_order = order;
+
+ if (portals_compatibility > 1) {
+ /* Strong portals compatibility: Zero the nid's
+ * NET, so if I'm reading new config logs, or
+ * getting configured by (new) lconf I can
+ * still talk to old servers. */
+ dst_nid = LNET_MKNID(0, LNET_NIDADDR(dst_nid));
+ src_nid = LNET_MKNID(0, LNET_NIDADDR(src_nid));
+ }
+ peer->nid = dst_nid;
+ *self = src_nid;
+ rc = 0;
+ }
+ }
+
+ CDEBUG(D_NET, "%s->%s\n", uuid->uuid, libcfs_id2str(*peer));
+ return rc;
+}
+
+void ptlrpc_ni_fini(void)
+{
+ wait_queue_head_t waitq;
+ struct l_wait_info lwi;
+ int rc;
+ int retries;
+
+ /* Wait for the event queue to become idle since there may still be
+ * messages in flight with pending events (i.e. the fire-and-forget
+ * messages == client requests and "non-difficult" server
+ * replies */
+
+ for (retries = 0;; retries++) {
+ rc = LNetEQFree(ptlrpc_eq_h);
+ switch (rc) {
+ default:
+ LBUG();
+
+ case 0:
+ LNetNIFini();
+ return;
+
+ case -EBUSY:
+ if (retries != 0)
+ CWARN("Event queue still busy\n");
+
+ /* Wait for a bit */
+ init_waitqueue_head(&waitq);
+ lwi = LWI_TIMEOUT(cfs_time_seconds(2), NULL, NULL);
+ l_wait_event(waitq, 0, &lwi);
+ break;
+ }
+ }
+ /* notreached */
+}
+
+lnet_pid_t ptl_get_pid(void)
+{
+ lnet_pid_t pid;
+
+ pid = LUSTRE_SRV_LNET_PID;
+ return pid;
+}
+
+int ptlrpc_ni_init(void)
+{
+ int rc;
+ lnet_pid_t pid;
+
+ pid = ptl_get_pid();
+ CDEBUG(D_NET, "My pid is: %x\n", pid);
+
+ /* We're not passing any limits yet... */
+ rc = LNetNIInit(pid);
+ if (rc < 0) {
+ CDEBUG(D_NET, "Can't init network interface: %d\n", rc);
+ return -ENOENT;
+ }
+
+ /* CAVEAT EMPTOR: how we process portals events is _radically_
+ * different depending on... */
+ /* kernel LNet calls our master callback when there are new event,
+ * because we are guaranteed to get every event via callback,
+ * so we just set EQ size to 0 to avoid overhead of serializing
+ * enqueue/dequeue operations in LNet. */
+ rc = LNetEQAlloc(0, ptlrpc_master_callback, &ptlrpc_eq_h);
+ if (rc == 0)
+ return 0;
+
+ CERROR("Failed to allocate event queue: %d\n", rc);
+ LNetNIFini();
+
+ return -ENOMEM;
+}
+
+
+int ptlrpc_init_portals(void)
+{
+ int rc = ptlrpc_ni_init();
+
+ if (rc != 0) {
+ CERROR("network initialisation failed\n");
+ return -EIO;
+ }
+ rc = ptlrpcd_addref();
+ if (rc == 0)
+ return 0;
+
+ CERROR("rpcd initialisation failed\n");
+ ptlrpc_ni_fini();
+ return rc;
+}
+
+void ptlrpc_exit_portals(void)
+{
+ ptlrpcd_decref();
+ ptlrpc_ni_fini();
+}
diff --git a/drivers/staging/lustre/lustre/ptlrpc/import.c b/drivers/staging/lustre/lustre/ptlrpc/import.c
new file mode 100644
index 000000000..d5fc689c0
--- /dev/null
+++ b/drivers/staging/lustre/lustre/ptlrpc/import.c
@@ -0,0 +1,1642 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2011, 2012, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ *
+ * lustre/ptlrpc/import.c
+ *
+ * Author: Mike Shaver <shaver@clusterfs.com>
+ */
+
+#define DEBUG_SUBSYSTEM S_RPC
+
+#include "../include/obd_support.h"
+#include "../include/lustre_ha.h"
+#include "../include/lustre_net.h"
+#include "../include/lustre_import.h"
+#include "../include/lustre_export.h"
+#include "../include/obd.h"
+#include "../include/obd_cksum.h"
+#include "../include/obd_class.h"
+
+#include "ptlrpc_internal.h"
+
+struct ptlrpc_connect_async_args {
+ __u64 pcaa_peer_committed;
+ int pcaa_initial_connect;
+};
+
+/**
+ * Updates import \a imp current state to provided \a state value
+ * Helper function. Must be called under imp_lock.
+ */
+static void __import_set_state(struct obd_import *imp,
+ enum lustre_imp_state state)
+{
+ switch (state) {
+ case LUSTRE_IMP_CLOSED:
+ case LUSTRE_IMP_NEW:
+ case LUSTRE_IMP_DISCON:
+ case LUSTRE_IMP_CONNECTING:
+ break;
+ case LUSTRE_IMP_REPLAY_WAIT:
+ imp->imp_replay_state = LUSTRE_IMP_REPLAY_LOCKS;
+ break;
+ default:
+ imp->imp_replay_state = LUSTRE_IMP_REPLAY;
+ }
+
+ imp->imp_state = state;
+ imp->imp_state_hist[imp->imp_state_hist_idx].ish_state = state;
+ imp->imp_state_hist[imp->imp_state_hist_idx].ish_time =
+ get_seconds();
+ imp->imp_state_hist_idx = (imp->imp_state_hist_idx + 1) %
+ IMP_STATE_HIST_LEN;
+}
+
+/* A CLOSED import should remain so. */
+#define IMPORT_SET_STATE_NOLOCK(imp, state) \
+do { \
+ if (imp->imp_state != LUSTRE_IMP_CLOSED) { \
+ CDEBUG(D_HA, "%p %s: changing import state from %s to %s\n", \
+ imp, obd2cli_tgt(imp->imp_obd), \
+ ptlrpc_import_state_name(imp->imp_state), \
+ ptlrpc_import_state_name(state)); \
+ __import_set_state(imp, state); \
+ } \
+} while (0)
+
+#define IMPORT_SET_STATE(imp, state) \
+do { \
+ spin_lock(&imp->imp_lock); \
+ IMPORT_SET_STATE_NOLOCK(imp, state); \
+ spin_unlock(&imp->imp_lock); \
+} while (0)
+
+
+static int ptlrpc_connect_interpret(const struct lu_env *env,
+ struct ptlrpc_request *request,
+ void *data, int rc);
+int ptlrpc_import_recovery_state_machine(struct obd_import *imp);
+
+/* Only this function is allowed to change the import state when it is
+ * CLOSED. I would rather refcount the import and free it after
+ * disconnection like we do with exports. To do that, the client_obd
+ * will need to save the peer info somewhere other than in the import,
+ * though. */
+int ptlrpc_init_import(struct obd_import *imp)
+{
+ spin_lock(&imp->imp_lock);
+
+ imp->imp_generation++;
+ imp->imp_state = LUSTRE_IMP_NEW;
+
+ spin_unlock(&imp->imp_lock);
+
+ return 0;
+}
+EXPORT_SYMBOL(ptlrpc_init_import);
+
+#define UUID_STR "_UUID"
+void deuuidify(char *uuid, const char *prefix, char **uuid_start, int *uuid_len)
+{
+ *uuid_start = !prefix || strncmp(uuid, prefix, strlen(prefix))
+ ? uuid : uuid + strlen(prefix);
+
+ *uuid_len = strlen(*uuid_start);
+
+ if (*uuid_len < strlen(UUID_STR))
+ return;
+
+ if (!strncmp(*uuid_start + *uuid_len - strlen(UUID_STR),
+ UUID_STR, strlen(UUID_STR)))
+ *uuid_len -= strlen(UUID_STR);
+}
+EXPORT_SYMBOL(deuuidify);
+
+/**
+ * Returns true if import was FULL, false if import was already not
+ * connected.
+ * @imp - import to be disconnected
+ * @conn_cnt - connection count (epoch) of the request that timed out
+ * and caused the disconnection. In some cases, multiple
+ * inflight requests can fail to a single target (e.g. OST
+ * bulk requests) and if one has already caused a reconnection
+ * (increasing the import->conn_cnt) the older failure should
+ * not also cause a reconnection. If zero it forces a reconnect.
+ */
+int ptlrpc_set_import_discon(struct obd_import *imp, __u32 conn_cnt)
+{
+ int rc = 0;
+
+ spin_lock(&imp->imp_lock);
+
+ if (imp->imp_state == LUSTRE_IMP_FULL &&
+ (conn_cnt == 0 || conn_cnt == imp->imp_conn_cnt)) {
+ char *target_start;
+ int target_len;
+
+ deuuidify(obd2cli_tgt(imp->imp_obd), NULL,
+ &target_start, &target_len);
+
+ if (imp->imp_replayable) {
+ LCONSOLE_WARN("%s: Connection to %.*s (at %s) was lost; in progress operations using this service will wait for recovery to complete\n",
+ imp->imp_obd->obd_name, target_len, target_start,
+ libcfs_nid2str(imp->imp_connection->c_peer.nid));
+ } else {
+ LCONSOLE_ERROR_MSG(0x166, "%s: Connection to %.*s (at %s) was lost; in progress operations using this service will fail\n",
+ imp->imp_obd->obd_name,
+ target_len, target_start,
+ libcfs_nid2str(imp->imp_connection->c_peer.nid));
+ }
+ IMPORT_SET_STATE_NOLOCK(imp, LUSTRE_IMP_DISCON);
+ spin_unlock(&imp->imp_lock);
+
+ if (obd_dump_on_timeout)
+ libcfs_debug_dumplog();
+
+ obd_import_event(imp->imp_obd, imp, IMP_EVENT_DISCON);
+ rc = 1;
+ } else {
+ spin_unlock(&imp->imp_lock);
+ CDEBUG(D_HA, "%s: import %p already %s (conn %u, was %u): %s\n",
+ imp->imp_client->cli_name, imp,
+ (imp->imp_state == LUSTRE_IMP_FULL &&
+ imp->imp_conn_cnt > conn_cnt) ?
+ "reconnected" : "not connected", imp->imp_conn_cnt,
+ conn_cnt, ptlrpc_import_state_name(imp->imp_state));
+ }
+
+ return rc;
+}
+
+/* Must be called with imp_lock held! */
+static void ptlrpc_deactivate_and_unlock_import(struct obd_import *imp)
+{
+ assert_spin_locked(&imp->imp_lock);
+
+ CDEBUG(D_HA, "setting import %s INVALID\n", obd2cli_tgt(imp->imp_obd));
+ imp->imp_invalid = 1;
+ imp->imp_generation++;
+ spin_unlock(&imp->imp_lock);
+
+ ptlrpc_abort_inflight(imp);
+ obd_import_event(imp->imp_obd, imp, IMP_EVENT_INACTIVE);
+}
+
+/*
+ * This acts as a barrier; all existing requests are rejected, and
+ * no new requests will be accepted until the import is valid again.
+ */
+void ptlrpc_deactivate_import(struct obd_import *imp)
+{
+ spin_lock(&imp->imp_lock);
+ ptlrpc_deactivate_and_unlock_import(imp);
+}
+EXPORT_SYMBOL(ptlrpc_deactivate_import);
+
+static unsigned int
+ptlrpc_inflight_deadline(struct ptlrpc_request *req, time_t now)
+{
+ long dl;
+
+ if (!(((req->rq_phase == RQ_PHASE_RPC) && !req->rq_waiting) ||
+ (req->rq_phase == RQ_PHASE_BULK) ||
+ (req->rq_phase == RQ_PHASE_NEW)))
+ return 0;
+
+ if (req->rq_timedout)
+ return 0;
+
+ if (req->rq_phase == RQ_PHASE_NEW)
+ dl = req->rq_sent;
+ else
+ dl = req->rq_deadline;
+
+ if (dl <= now)
+ return 0;
+
+ return dl - now;
+}
+
+static unsigned int ptlrpc_inflight_timeout(struct obd_import *imp)
+{
+ time_t now = get_seconds();
+ struct list_head *tmp, *n;
+ struct ptlrpc_request *req;
+ unsigned int timeout = 0;
+
+ spin_lock(&imp->imp_lock);
+ list_for_each_safe(tmp, n, &imp->imp_sending_list) {
+ req = list_entry(tmp, struct ptlrpc_request, rq_list);
+ timeout = max(ptlrpc_inflight_deadline(req, now), timeout);
+ }
+ spin_unlock(&imp->imp_lock);
+ return timeout;
+}
+
+/**
+ * This function will invalidate the import, if necessary, then block
+ * for all the RPC completions, and finally notify the obd to
+ * invalidate its state (ie cancel locks, clear pending requests,
+ * etc).
+ */
+void ptlrpc_invalidate_import(struct obd_import *imp)
+{
+ struct list_head *tmp, *n;
+ struct ptlrpc_request *req;
+ struct l_wait_info lwi;
+ unsigned int timeout;
+ int rc;
+
+ atomic_inc(&imp->imp_inval_count);
+
+ if (!imp->imp_invalid || imp->imp_obd->obd_no_recov)
+ ptlrpc_deactivate_import(imp);
+
+ CFS_FAIL_TIMEOUT(OBD_FAIL_MGS_CONNECT_NET, 3 * cfs_fail_val / 2);
+ LASSERT(imp->imp_invalid);
+
+ /* Wait forever until inflight == 0. We really can't do it another
+ * way because in some cases we need to wait for very long reply
+ * unlink. We can't do anything before that because there is really
+ * no guarantee that some rdma transfer is not in progress right now. */
+ do {
+ /* Calculate max timeout for waiting on rpcs to error
+ * out. Use obd_timeout if calculated value is smaller
+ * than it. */
+ if (!OBD_FAIL_CHECK(OBD_FAIL_PTLRPC_LONG_REPL_UNLINK)) {
+ timeout = ptlrpc_inflight_timeout(imp);
+ timeout += timeout / 3;
+
+ if (timeout == 0)
+ timeout = obd_timeout;
+ } else {
+ /* decrease the interval to increase race condition */
+ timeout = 1;
+ }
+
+ CDEBUG(D_RPCTRACE,
+ "Sleeping %d sec for inflight to error out\n",
+ timeout);
+
+ /* Wait for all requests to error out and call completion
+ * callbacks. Cap it at obd_timeout -- these should all
+ * have been locally cancelled by ptlrpc_abort_inflight. */
+ lwi = LWI_TIMEOUT_INTERVAL(
+ cfs_timeout_cap(cfs_time_seconds(timeout)),
+ (timeout > 1)?cfs_time_seconds(1):cfs_time_seconds(1)/2,
+ NULL, NULL);
+ rc = l_wait_event(imp->imp_recovery_waitq,
+ (atomic_read(&imp->imp_inflight) == 0),
+ &lwi);
+ if (rc) {
+ const char *cli_tgt = obd2cli_tgt(imp->imp_obd);
+
+ CERROR("%s: rc = %d waiting for callback (%d != 0)\n",
+ cli_tgt, rc,
+ atomic_read(&imp->imp_inflight));
+
+ spin_lock(&imp->imp_lock);
+ if (atomic_read(&imp->imp_inflight) == 0) {
+ int count = atomic_read(&imp->imp_unregistering);
+
+ /* We know that "unregistering" rpcs only can
+ * survive in sending or delaying lists (they
+ * maybe waiting for long reply unlink in
+ * sluggish nets). Let's check this. If there
+ * is no inflight and unregistering != 0, this
+ * is bug. */
+ LASSERTF(count == 0, "Some RPCs are still unregistering: %d\n",
+ count);
+
+ /* Let's save one loop as soon as inflight have
+ * dropped to zero. No new inflights possible at
+ * this point. */
+ rc = 0;
+ } else {
+ list_for_each_safe(tmp, n,
+ &imp->imp_sending_list) {
+ req = list_entry(tmp,
+ struct ptlrpc_request,
+ rq_list);
+ DEBUG_REQ(D_ERROR, req,
+ "still on sending list");
+ }
+ list_for_each_safe(tmp, n,
+ &imp->imp_delayed_list) {
+ req = list_entry(tmp,
+ struct ptlrpc_request,
+ rq_list);
+ DEBUG_REQ(D_ERROR, req,
+ "still on delayed list");
+ }
+
+ CERROR("%s: RPCs in \"%s\" phase found (%d). Network is sluggish? Waiting them to error out.\n",
+ cli_tgt,
+ ptlrpc_phase2str(RQ_PHASE_UNREGISTERING),
+ atomic_read(&imp->
+ imp_unregistering));
+ }
+ spin_unlock(&imp->imp_lock);
+ }
+ } while (rc != 0);
+
+ /*
+ * Let's additionally check that no new rpcs added to import in
+ * "invalidate" state.
+ */
+ LASSERT(atomic_read(&imp->imp_inflight) == 0);
+ obd_import_event(imp->imp_obd, imp, IMP_EVENT_INVALIDATE);
+ sptlrpc_import_flush_all_ctx(imp);
+
+ atomic_dec(&imp->imp_inval_count);
+ wake_up_all(&imp->imp_recovery_waitq);
+}
+EXPORT_SYMBOL(ptlrpc_invalidate_import);
+
+/* unset imp_invalid */
+void ptlrpc_activate_import(struct obd_import *imp)
+{
+ struct obd_device *obd = imp->imp_obd;
+
+ spin_lock(&imp->imp_lock);
+ if (imp->imp_deactive != 0) {
+ spin_unlock(&imp->imp_lock);
+ return;
+ }
+
+ imp->imp_invalid = 0;
+ spin_unlock(&imp->imp_lock);
+ obd_import_event(obd, imp, IMP_EVENT_ACTIVE);
+}
+EXPORT_SYMBOL(ptlrpc_activate_import);
+
+static void ptlrpc_pinger_force(struct obd_import *imp)
+{
+ CDEBUG(D_HA, "%s: waking up pinger s:%s\n", obd2cli_tgt(imp->imp_obd),
+ ptlrpc_import_state_name(imp->imp_state));
+
+ spin_lock(&imp->imp_lock);
+ imp->imp_force_verify = 1;
+ spin_unlock(&imp->imp_lock);
+
+ if (imp->imp_state != LUSTRE_IMP_CONNECTING)
+ ptlrpc_pinger_wake_up();
+}
+
+void ptlrpc_fail_import(struct obd_import *imp, __u32 conn_cnt)
+{
+ LASSERT(!imp->imp_dlm_fake);
+
+ if (ptlrpc_set_import_discon(imp, conn_cnt)) {
+ if (!imp->imp_replayable) {
+ CDEBUG(D_HA, "import %s@%s for %s not replayable, auto-deactivating\n",
+ obd2cli_tgt(imp->imp_obd),
+ imp->imp_connection->c_remote_uuid.uuid,
+ imp->imp_obd->obd_name);
+ ptlrpc_deactivate_import(imp);
+ }
+
+ ptlrpc_pinger_force(imp);
+ }
+}
+EXPORT_SYMBOL(ptlrpc_fail_import);
+
+int ptlrpc_reconnect_import(struct obd_import *imp)
+{
+#ifdef ENABLE_PINGER
+ struct l_wait_info lwi;
+ int secs = cfs_time_seconds(obd_timeout);
+ int rc;
+
+ ptlrpc_pinger_force(imp);
+
+ CDEBUG(D_HA, "%s: recovery started, waiting %u seconds\n",
+ obd2cli_tgt(imp->imp_obd), secs);
+
+ lwi = LWI_TIMEOUT(secs, NULL, NULL);
+ rc = l_wait_event(imp->imp_recovery_waitq,
+ !ptlrpc_import_in_recovery(imp), &lwi);
+ CDEBUG(D_HA, "%s: recovery finished s:%s\n", obd2cli_tgt(imp->imp_obd),
+ ptlrpc_import_state_name(imp->imp_state));
+ return rc;
+#else
+ ptlrpc_set_import_discon(imp, 0);
+ /* Force a new connect attempt */
+ ptlrpc_invalidate_import(imp);
+ /* Do a fresh connect next time by zeroing the handle */
+ ptlrpc_disconnect_import(imp, 1);
+ /* Wait for all invalidate calls to finish */
+ if (atomic_read(&imp->imp_inval_count) > 0) {
+ int rc;
+ struct l_wait_info lwi = LWI_INTR(LWI_ON_SIGNAL_NOOP, NULL);
+ rc = l_wait_event(imp->imp_recovery_waitq,
+ (atomic_read(&imp->imp_inval_count) == 0),
+ &lwi);
+ if (rc)
+ CERROR("Interrupted, inval=%d\n",
+ atomic_read(&imp->imp_inval_count));
+ }
+
+ /* Allow reconnect attempts */
+ imp->imp_obd->obd_no_recov = 0;
+ /* Remove 'invalid' flag */
+ ptlrpc_activate_import(imp);
+ /* Attempt a new connect */
+ ptlrpc_recover_import(imp, NULL, 0);
+ return 0;
+#endif
+}
+EXPORT_SYMBOL(ptlrpc_reconnect_import);
+
+/**
+ * Connection on import \a imp is changed to another one (if more than one is
+ * present). We typically chose connection that we have not tried to connect to
+ * the longest
+ */
+static int import_select_connection(struct obd_import *imp)
+{
+ struct obd_import_conn *imp_conn = NULL, *conn;
+ struct obd_export *dlmexp;
+ char *target_start;
+ int target_len, tried_all = 1;
+
+ spin_lock(&imp->imp_lock);
+
+ if (list_empty(&imp->imp_conn_list)) {
+ CERROR("%s: no connections available\n",
+ imp->imp_obd->obd_name);
+ spin_unlock(&imp->imp_lock);
+ return -EINVAL;
+ }
+
+ list_for_each_entry(conn, &imp->imp_conn_list, oic_item) {
+ CDEBUG(D_HA, "%s: connect to NID %s last attempt %llu\n",
+ imp->imp_obd->obd_name,
+ libcfs_nid2str(conn->oic_conn->c_peer.nid),
+ conn->oic_last_attempt);
+
+ /* If we have not tried this connection since
+ the last successful attempt, go with this one */
+ if ((conn->oic_last_attempt == 0) ||
+ cfs_time_beforeq_64(conn->oic_last_attempt,
+ imp->imp_last_success_conn)) {
+ imp_conn = conn;
+ tried_all = 0;
+ break;
+ }
+
+ /* If all of the connections have already been tried
+ since the last successful connection; just choose the
+ least recently used */
+ if (!imp_conn)
+ imp_conn = conn;
+ else if (cfs_time_before_64(conn->oic_last_attempt,
+ imp_conn->oic_last_attempt))
+ imp_conn = conn;
+ }
+
+ /* if not found, simply choose the current one */
+ if (!imp_conn || imp->imp_force_reconnect) {
+ LASSERT(imp->imp_conn_current);
+ imp_conn = imp->imp_conn_current;
+ tried_all = 0;
+ }
+ LASSERT(imp_conn->oic_conn);
+
+ /* If we've tried everything, and we're back to the beginning of the
+ list, increase our timeout and try again. It will be reset when
+ we do finally connect. (FIXME: really we should wait for all network
+ state associated with the last connection attempt to drain before
+ trying to reconnect on it.) */
+ if (tried_all && (imp->imp_conn_list.next == &imp_conn->oic_item)) {
+ struct adaptive_timeout *at = &imp->imp_at.iat_net_latency;
+ if (at_get(at) < CONNECTION_SWITCH_MAX) {
+ at_measured(at, at_get(at) + CONNECTION_SWITCH_INC);
+ if (at_get(at) > CONNECTION_SWITCH_MAX)
+ at_reset(at, CONNECTION_SWITCH_MAX);
+ }
+ LASSERT(imp_conn->oic_last_attempt);
+ CDEBUG(D_HA, "%s: tried all connections, increasing latency to %ds\n",
+ imp->imp_obd->obd_name, at_get(at));
+ }
+
+ imp_conn->oic_last_attempt = cfs_time_current_64();
+
+ /* switch connection, don't mind if it's same as the current one */
+ if (imp->imp_connection)
+ ptlrpc_connection_put(imp->imp_connection);
+ imp->imp_connection = ptlrpc_connection_addref(imp_conn->oic_conn);
+
+ dlmexp = class_conn2export(&imp->imp_dlm_handle);
+ LASSERT(dlmexp != NULL);
+ if (dlmexp->exp_connection)
+ ptlrpc_connection_put(dlmexp->exp_connection);
+ dlmexp->exp_connection = ptlrpc_connection_addref(imp_conn->oic_conn);
+ class_export_put(dlmexp);
+
+ if (imp->imp_conn_current != imp_conn) {
+ if (imp->imp_conn_current) {
+ deuuidify(obd2cli_tgt(imp->imp_obd), NULL,
+ &target_start, &target_len);
+
+ CDEBUG(D_HA, "%s: Connection changing to %.*s (at %s)\n",
+ imp->imp_obd->obd_name,
+ target_len, target_start,
+ libcfs_nid2str(imp_conn->oic_conn->c_peer.nid));
+ }
+
+ imp->imp_conn_current = imp_conn;
+ }
+
+ CDEBUG(D_HA, "%s: import %p using connection %s/%s\n",
+ imp->imp_obd->obd_name, imp, imp_conn->oic_uuid.uuid,
+ libcfs_nid2str(imp_conn->oic_conn->c_peer.nid));
+
+ spin_unlock(&imp->imp_lock);
+
+ return 0;
+}
+
+/*
+ * must be called under imp_lock
+ */
+static int ptlrpc_first_transno(struct obd_import *imp, __u64 *transno)
+{
+ struct ptlrpc_request *req;
+ struct list_head *tmp;
+
+ /* The requests in committed_list always have smaller transnos than
+ * the requests in replay_list */
+ if (!list_empty(&imp->imp_committed_list)) {
+ tmp = imp->imp_committed_list.next;
+ req = list_entry(tmp, struct ptlrpc_request, rq_replay_list);
+ *transno = req->rq_transno;
+ if (req->rq_transno == 0) {
+ DEBUG_REQ(D_ERROR, req,
+ "zero transno in committed_list");
+ LBUG();
+ }
+ return 1;
+ }
+ if (!list_empty(&imp->imp_replay_list)) {
+ tmp = imp->imp_replay_list.next;
+ req = list_entry(tmp, struct ptlrpc_request, rq_replay_list);
+ *transno = req->rq_transno;
+ if (req->rq_transno == 0) {
+ DEBUG_REQ(D_ERROR, req, "zero transno in replay_list");
+ LBUG();
+ }
+ return 1;
+ }
+ return 0;
+}
+
+/**
+ * Attempt to (re)connect import \a imp. This includes all preparations,
+ * initializing CONNECT RPC request and passing it to ptlrpcd for
+ * actual sending.
+ * Returns 0 on success or error code.
+ */
+int ptlrpc_connect_import(struct obd_import *imp)
+{
+ struct obd_device *obd = imp->imp_obd;
+ int initial_connect = 0;
+ int set_transno = 0;
+ __u64 committed_before_reconnect = 0;
+ struct ptlrpc_request *request;
+ char *bufs[] = { NULL,
+ obd2cli_tgt(imp->imp_obd),
+ obd->obd_uuid.uuid,
+ (char *)&imp->imp_dlm_handle,
+ (char *)&imp->imp_connect_data };
+ struct ptlrpc_connect_async_args *aa;
+ int rc;
+
+ spin_lock(&imp->imp_lock);
+ if (imp->imp_state == LUSTRE_IMP_CLOSED) {
+ spin_unlock(&imp->imp_lock);
+ CERROR("can't connect to a closed import\n");
+ return -EINVAL;
+ } else if (imp->imp_state == LUSTRE_IMP_FULL) {
+ spin_unlock(&imp->imp_lock);
+ CERROR("already connected\n");
+ return 0;
+ } else if (imp->imp_state == LUSTRE_IMP_CONNECTING) {
+ spin_unlock(&imp->imp_lock);
+ CERROR("already connecting\n");
+ return -EALREADY;
+ }
+
+ IMPORT_SET_STATE_NOLOCK(imp, LUSTRE_IMP_CONNECTING);
+
+ imp->imp_conn_cnt++;
+ imp->imp_resend_replay = 0;
+
+ if (!lustre_handle_is_used(&imp->imp_remote_handle))
+ initial_connect = 1;
+ else
+ committed_before_reconnect = imp->imp_peer_committed_transno;
+
+ set_transno = ptlrpc_first_transno(imp,
+ &imp->imp_connect_data.ocd_transno);
+ spin_unlock(&imp->imp_lock);
+
+ rc = import_select_connection(imp);
+ if (rc)
+ goto out;
+
+ rc = sptlrpc_import_sec_adapt(imp, NULL, NULL);
+ if (rc)
+ goto out;
+
+ /* Reset connect flags to the originally requested flags, in case
+ * the server is updated on-the-fly we will get the new features. */
+ imp->imp_connect_data.ocd_connect_flags = imp->imp_connect_flags_orig;
+ /* Reset ocd_version each time so the server knows the exact versions */
+ imp->imp_connect_data.ocd_version = LUSTRE_VERSION_CODE;
+ imp->imp_msghdr_flags &= ~MSGHDR_AT_SUPPORT;
+ imp->imp_msghdr_flags &= ~MSGHDR_CKSUM_INCOMPAT18;
+
+ rc = obd_reconnect(NULL, imp->imp_obd->obd_self_export, obd,
+ &obd->obd_uuid, &imp->imp_connect_data, NULL);
+ if (rc)
+ goto out;
+
+ request = ptlrpc_request_alloc(imp, &RQF_MDS_CONNECT);
+ if (request == NULL) {
+ rc = -ENOMEM;
+ goto out;
+ }
+
+ rc = ptlrpc_request_bufs_pack(request, LUSTRE_OBD_VERSION,
+ imp->imp_connect_op, bufs, NULL);
+ if (rc) {
+ ptlrpc_request_free(request);
+ goto out;
+ }
+
+ /* Report the rpc service time to the server so that it knows how long
+ * to wait for clients to join recovery */
+ lustre_msg_set_service_time(request->rq_reqmsg,
+ at_timeout2est(request->rq_timeout));
+
+ /* The amount of time we give the server to process the connect req.
+ * import_select_connection will increase the net latency on
+ * repeated reconnect attempts to cover slow networks.
+ * We override/ignore the server rpc completion estimate here,
+ * which may be large if this is a reconnect attempt */
+ request->rq_timeout = INITIAL_CONNECT_TIMEOUT;
+ lustre_msg_set_timeout(request->rq_reqmsg, request->rq_timeout);
+
+ lustre_msg_add_op_flags(request->rq_reqmsg, MSG_CONNECT_NEXT_VER);
+
+ request->rq_no_resend = request->rq_no_delay = 1;
+ request->rq_send_state = LUSTRE_IMP_CONNECTING;
+ /* Allow a slightly larger reply for future growth compatibility */
+ req_capsule_set_size(&request->rq_pill, &RMF_CONNECT_DATA, RCL_SERVER,
+ sizeof(struct obd_connect_data)+16*sizeof(__u64));
+ ptlrpc_request_set_replen(request);
+ request->rq_interpret_reply = ptlrpc_connect_interpret;
+
+ CLASSERT(sizeof(*aa) <= sizeof(request->rq_async_args));
+ aa = ptlrpc_req_async_args(request);
+ memset(aa, 0, sizeof(*aa));
+
+ aa->pcaa_peer_committed = committed_before_reconnect;
+ aa->pcaa_initial_connect = initial_connect;
+
+ if (aa->pcaa_initial_connect) {
+ spin_lock(&imp->imp_lock);
+ imp->imp_replayable = 1;
+ spin_unlock(&imp->imp_lock);
+ lustre_msg_add_op_flags(request->rq_reqmsg,
+ MSG_CONNECT_INITIAL);
+ }
+
+ if (set_transno)
+ lustre_msg_add_op_flags(request->rq_reqmsg,
+ MSG_CONNECT_TRANSNO);
+
+ DEBUG_REQ(D_RPCTRACE, request, "(re)connect request (timeout %d)",
+ request->rq_timeout);
+ ptlrpcd_add_req(request, PDL_POLICY_ROUND, -1);
+ rc = 0;
+out:
+ if (rc != 0) {
+ IMPORT_SET_STATE(imp, LUSTRE_IMP_DISCON);
+ }
+
+ return rc;
+}
+EXPORT_SYMBOL(ptlrpc_connect_import);
+
+static void ptlrpc_maybe_ping_import_soon(struct obd_import *imp)
+{
+ int force_verify;
+
+ spin_lock(&imp->imp_lock);
+ force_verify = imp->imp_force_verify != 0;
+ spin_unlock(&imp->imp_lock);
+
+ if (force_verify)
+ ptlrpc_pinger_wake_up();
+}
+
+static int ptlrpc_busy_reconnect(int rc)
+{
+ return (rc == -EBUSY) || (rc == -EAGAIN);
+}
+
+/**
+ * interpret_reply callback for connect RPCs.
+ * Looks into returned status of connect operation and decides
+ * what to do with the import - i.e enter recovery, promote it to
+ * full state for normal operations of disconnect it due to an error.
+ */
+static int ptlrpc_connect_interpret(const struct lu_env *env,
+ struct ptlrpc_request *request,
+ void *data, int rc)
+{
+ struct ptlrpc_connect_async_args *aa = data;
+ struct obd_import *imp = request->rq_import;
+ struct client_obd *cli = &imp->imp_obd->u.cli;
+ struct lustre_handle old_hdl;
+ __u64 old_connect_flags;
+ int msg_flags;
+ struct obd_connect_data *ocd;
+ struct obd_export *exp;
+ int ret;
+
+ spin_lock(&imp->imp_lock);
+ if (imp->imp_state == LUSTRE_IMP_CLOSED) {
+ imp->imp_connect_tried = 1;
+ spin_unlock(&imp->imp_lock);
+ return 0;
+ }
+
+ if (rc) {
+ /* if this reconnect to busy export - not need select new target
+ * for connecting*/
+ imp->imp_force_reconnect = ptlrpc_busy_reconnect(rc);
+ spin_unlock(&imp->imp_lock);
+ ptlrpc_maybe_ping_import_soon(imp);
+ goto out;
+ }
+ spin_unlock(&imp->imp_lock);
+
+ LASSERT(imp->imp_conn_current);
+
+ msg_flags = lustre_msg_get_op_flags(request->rq_repmsg);
+
+ ret = req_capsule_get_size(&request->rq_pill, &RMF_CONNECT_DATA,
+ RCL_SERVER);
+ /* server replied obd_connect_data is always bigger */
+ ocd = req_capsule_server_sized_get(&request->rq_pill,
+ &RMF_CONNECT_DATA, ret);
+
+ if (ocd == NULL) {
+ CERROR("%s: no connect data from server\n",
+ imp->imp_obd->obd_name);
+ rc = -EPROTO;
+ goto out;
+ }
+
+ spin_lock(&imp->imp_lock);
+
+ /* All imports are pingable */
+ imp->imp_pingable = 1;
+ imp->imp_force_reconnect = 0;
+ imp->imp_force_verify = 0;
+
+ imp->imp_connect_data = *ocd;
+
+ CDEBUG(D_HA, "%s: connect to target with instance %u\n",
+ imp->imp_obd->obd_name, ocd->ocd_instance);
+ exp = class_conn2export(&imp->imp_dlm_handle);
+
+ spin_unlock(&imp->imp_lock);
+
+ /* check that server granted subset of flags we asked for. */
+ if ((ocd->ocd_connect_flags & imp->imp_connect_flags_orig) !=
+ ocd->ocd_connect_flags) {
+ CERROR("%s: Server didn't granted asked subset of flags: asked=%#llx grranted=%#llx\n",
+ imp->imp_obd->obd_name, imp->imp_connect_flags_orig,
+ ocd->ocd_connect_flags);
+ rc = -EPROTO;
+ goto out;
+ }
+
+ if (!exp) {
+ /* This could happen if export is cleaned during the
+ connect attempt */
+ CERROR("%s: missing export after connect\n",
+ imp->imp_obd->obd_name);
+ rc = -ENODEV;
+ goto out;
+ }
+ old_connect_flags = exp_connect_flags(exp);
+ exp->exp_connect_data = *ocd;
+ imp->imp_obd->obd_self_export->exp_connect_data = *ocd;
+ class_export_put(exp);
+
+ obd_import_event(imp->imp_obd, imp, IMP_EVENT_OCD);
+
+ if (aa->pcaa_initial_connect) {
+ spin_lock(&imp->imp_lock);
+ if (msg_flags & MSG_CONNECT_REPLAYABLE) {
+ imp->imp_replayable = 1;
+ spin_unlock(&imp->imp_lock);
+ CDEBUG(D_HA, "connected to replayable target: %s\n",
+ obd2cli_tgt(imp->imp_obd));
+ } else {
+ imp->imp_replayable = 0;
+ spin_unlock(&imp->imp_lock);
+ }
+
+ /* if applies, adjust the imp->imp_msg_magic here
+ * according to reply flags */
+
+ imp->imp_remote_handle =
+ *lustre_msg_get_handle(request->rq_repmsg);
+
+ /* Initial connects are allowed for clients with non-random
+ * uuids when servers are in recovery. Simply signal the
+ * servers replay is complete and wait in REPLAY_WAIT. */
+ if (msg_flags & MSG_CONNECT_RECOVERING) {
+ CDEBUG(D_HA, "connect to %s during recovery\n",
+ obd2cli_tgt(imp->imp_obd));
+ IMPORT_SET_STATE(imp, LUSTRE_IMP_REPLAY_LOCKS);
+ } else {
+ IMPORT_SET_STATE(imp, LUSTRE_IMP_FULL);
+ ptlrpc_activate_import(imp);
+ }
+
+ rc = 0;
+ goto finish;
+ }
+
+ /* Determine what recovery state to move the import to. */
+ if (MSG_CONNECT_RECONNECT & msg_flags) {
+ memset(&old_hdl, 0, sizeof(old_hdl));
+ if (!memcmp(&old_hdl, lustre_msg_get_handle(request->rq_repmsg),
+ sizeof(old_hdl))) {
+ LCONSOLE_WARN("Reconnect to %s (at @%s) failed due bad handle %#llx\n",
+ obd2cli_tgt(imp->imp_obd),
+ imp->imp_connection->c_remote_uuid.uuid,
+ imp->imp_dlm_handle.cookie);
+ rc = -ENOTCONN;
+ goto out;
+ }
+
+ if (memcmp(&imp->imp_remote_handle,
+ lustre_msg_get_handle(request->rq_repmsg),
+ sizeof(imp->imp_remote_handle))) {
+ int level = msg_flags & MSG_CONNECT_RECOVERING ?
+ D_HA : D_WARNING;
+
+ /* Bug 16611/14775: if server handle have changed,
+ * that means some sort of disconnection happened.
+ * If the server is not in recovery, that also means it
+ * already erased all of our state because of previous
+ * eviction. If it is in recovery - we are safe to
+ * participate since we can reestablish all of our state
+ * with server again */
+ if ((MSG_CONNECT_RECOVERING & msg_flags)) {
+ CDEBUG(level, "%s@%s changed server handle from %#llx to %#llx but is still in recovery\n",
+ obd2cli_tgt(imp->imp_obd),
+ imp->imp_connection->c_remote_uuid.uuid,
+ imp->imp_remote_handle.cookie,
+ lustre_msg_get_handle(
+ request->rq_repmsg)->cookie);
+ } else {
+ LCONSOLE_WARN("Evicted from %s (at %s) after server handle changed from %#llx to %#llx\n",
+ obd2cli_tgt(imp->imp_obd),
+ imp->imp_connection-> \
+ c_remote_uuid.uuid,
+ imp->imp_remote_handle.cookie,
+ lustre_msg_get_handle(
+ request->rq_repmsg)->cookie);
+ }
+
+
+ imp->imp_remote_handle =
+ *lustre_msg_get_handle(request->rq_repmsg);
+
+ if (!(MSG_CONNECT_RECOVERING & msg_flags)) {
+ IMPORT_SET_STATE(imp, LUSTRE_IMP_EVICTED);
+ rc = 0;
+ goto finish;
+ }
+
+ } else {
+ CDEBUG(D_HA, "reconnected to %s@%s after partition\n",
+ obd2cli_tgt(imp->imp_obd),
+ imp->imp_connection->c_remote_uuid.uuid);
+ }
+
+ if (imp->imp_invalid) {
+ CDEBUG(D_HA, "%s: reconnected but import is invalid; marking evicted\n",
+ imp->imp_obd->obd_name);
+ IMPORT_SET_STATE(imp, LUSTRE_IMP_EVICTED);
+ } else if (MSG_CONNECT_RECOVERING & msg_flags) {
+ CDEBUG(D_HA, "%s: reconnected to %s during replay\n",
+ imp->imp_obd->obd_name,
+ obd2cli_tgt(imp->imp_obd));
+
+ spin_lock(&imp->imp_lock);
+ imp->imp_resend_replay = 1;
+ spin_unlock(&imp->imp_lock);
+
+ IMPORT_SET_STATE(imp, imp->imp_replay_state);
+ } else {
+ IMPORT_SET_STATE(imp, LUSTRE_IMP_RECOVER);
+ }
+ } else if ((MSG_CONNECT_RECOVERING & msg_flags) && !imp->imp_invalid) {
+ LASSERT(imp->imp_replayable);
+ imp->imp_remote_handle =
+ *lustre_msg_get_handle(request->rq_repmsg);
+ imp->imp_last_replay_transno = 0;
+ IMPORT_SET_STATE(imp, LUSTRE_IMP_REPLAY);
+ } else {
+ DEBUG_REQ(D_HA, request, "%s: evicting (reconnect/recover flags not set: %x)",
+ imp->imp_obd->obd_name, msg_flags);
+ imp->imp_remote_handle =
+ *lustre_msg_get_handle(request->rq_repmsg);
+ IMPORT_SET_STATE(imp, LUSTRE_IMP_EVICTED);
+ }
+
+ /* Sanity checks for a reconnected import. */
+ if (!(imp->imp_replayable) != !(msg_flags & MSG_CONNECT_REPLAYABLE)) {
+ CERROR("imp_replayable flag does not match server after reconnect. We should LBUG right here.\n");
+ }
+
+ if (lustre_msg_get_last_committed(request->rq_repmsg) > 0 &&
+ lustre_msg_get_last_committed(request->rq_repmsg) <
+ aa->pcaa_peer_committed) {
+ CERROR("%s went back in time (transno %lld was previously committed, server now claims %lld)! See https://bugzilla.lustre.org/show_bug.cgi?id=9646\n",
+ obd2cli_tgt(imp->imp_obd), aa->pcaa_peer_committed,
+ lustre_msg_get_last_committed(request->rq_repmsg));
+ }
+
+finish:
+ rc = ptlrpc_import_recovery_state_machine(imp);
+ if (rc != 0) {
+ if (rc == -ENOTCONN) {
+ CDEBUG(D_HA, "evicted/aborted by %s@%s during recovery; invalidating and reconnecting\n",
+ obd2cli_tgt(imp->imp_obd),
+ imp->imp_connection->c_remote_uuid.uuid);
+ ptlrpc_connect_import(imp);
+ imp->imp_connect_tried = 1;
+ return 0;
+ }
+ } else {
+
+ spin_lock(&imp->imp_lock);
+ list_del(&imp->imp_conn_current->oic_item);
+ list_add(&imp->imp_conn_current->oic_item,
+ &imp->imp_conn_list);
+ imp->imp_last_success_conn =
+ imp->imp_conn_current->oic_last_attempt;
+
+ spin_unlock(&imp->imp_lock);
+
+ if ((imp->imp_connect_flags_orig & OBD_CONNECT_IBITS) &&
+ !(ocd->ocd_connect_flags & OBD_CONNECT_IBITS)) {
+ LCONSOLE_WARN("%s: MDS %s does not support ibits lock, either very old or invalid: requested %llx, replied %llx\n",
+ imp->imp_obd->obd_name,
+ imp->imp_connection->c_remote_uuid.uuid,
+ imp->imp_connect_flags_orig,
+ ocd->ocd_connect_flags);
+ rc = -EPROTO;
+ goto out;
+ }
+
+ if ((ocd->ocd_connect_flags & OBD_CONNECT_VERSION) &&
+ (ocd->ocd_version > LUSTRE_VERSION_CODE +
+ LUSTRE_VERSION_OFFSET_WARN ||
+ ocd->ocd_version < LUSTRE_VERSION_CODE -
+ LUSTRE_VERSION_OFFSET_WARN)) {
+ /* Sigh, some compilers do not like #ifdef in the middle
+ of macro arguments */
+ const char *older = "older. Consider upgrading server or downgrading client"
+ ;
+ const char *newer = "newer than client version. Consider upgrading client"
+ ;
+
+ LCONSOLE_WARN("Server %s version (%d.%d.%d.%d) is much %s (%s)\n",
+ obd2cli_tgt(imp->imp_obd),
+ OBD_OCD_VERSION_MAJOR(ocd->ocd_version),
+ OBD_OCD_VERSION_MINOR(ocd->ocd_version),
+ OBD_OCD_VERSION_PATCH(ocd->ocd_version),
+ OBD_OCD_VERSION_FIX(ocd->ocd_version),
+ ocd->ocd_version > LUSTRE_VERSION_CODE ?
+ newer : older, LUSTRE_VERSION_STRING);
+ }
+
+#if LUSTRE_VERSION_CODE < OBD_OCD_VERSION(3, 2, 50, 0)
+ /* Check if server has LU-1252 fix applied to not always swab
+ * the IR MNE entries. Do this only once per connection. This
+ * fixup is version-limited, because we don't want to carry the
+ * OBD_CONNECT_MNE_SWAB flag around forever, just so long as we
+ * need interop with unpatched 2.2 servers. For newer servers,
+ * the client will do MNE swabbing only as needed. LU-1644 */
+ if (unlikely((ocd->ocd_connect_flags & OBD_CONNECT_VERSION) &&
+ !(ocd->ocd_connect_flags & OBD_CONNECT_MNE_SWAB) &&
+ OBD_OCD_VERSION_MAJOR(ocd->ocd_version) == 2 &&
+ OBD_OCD_VERSION_MINOR(ocd->ocd_version) == 2 &&
+ OBD_OCD_VERSION_PATCH(ocd->ocd_version) < 55 &&
+ strcmp(imp->imp_obd->obd_type->typ_name,
+ LUSTRE_MGC_NAME) == 0))
+ imp->imp_need_mne_swab = 1;
+ else /* clear if server was upgraded since last connect */
+ imp->imp_need_mne_swab = 0;
+#else
+#warning "LU-1644: Remove old OBD_CONNECT_MNE_SWAB fixup and imp_need_mne_swab"
+#endif
+
+ if (ocd->ocd_connect_flags & OBD_CONNECT_CKSUM) {
+ /* We sent to the server ocd_cksum_types with bits set
+ * for algorithms we understand. The server masked off
+ * the checksum types it doesn't support */
+ if ((ocd->ocd_cksum_types &
+ cksum_types_supported_client()) == 0) {
+ LCONSOLE_WARN("The negotiation of the checksum algorithm to use with server %s failed (%x/%x), disabling checksums\n",
+ obd2cli_tgt(imp->imp_obd),
+ ocd->ocd_cksum_types,
+ cksum_types_supported_client());
+ cli->cl_checksum = 0;
+ cli->cl_supp_cksum_types = OBD_CKSUM_ADLER;
+ } else {
+ cli->cl_supp_cksum_types = ocd->ocd_cksum_types;
+ }
+ } else {
+ /* The server does not support OBD_CONNECT_CKSUM.
+ * Enforce ADLER for backward compatibility*/
+ cli->cl_supp_cksum_types = OBD_CKSUM_ADLER;
+ }
+ cli->cl_cksum_type = cksum_type_select(cli->cl_supp_cksum_types);
+
+ if (ocd->ocd_connect_flags & OBD_CONNECT_BRW_SIZE)
+ cli->cl_max_pages_per_rpc =
+ min(ocd->ocd_brw_size >> PAGE_CACHE_SHIFT,
+ cli->cl_max_pages_per_rpc);
+ else if (imp->imp_connect_op == MDS_CONNECT ||
+ imp->imp_connect_op == MGS_CONNECT)
+ cli->cl_max_pages_per_rpc = 1;
+
+ /* Reset ns_connect_flags only for initial connect. It might be
+ * changed in while using FS and if we reset it in reconnect
+ * this leads to losing user settings done before such as
+ * disable lru_resize, etc. */
+ if (old_connect_flags != exp_connect_flags(exp) ||
+ aa->pcaa_initial_connect) {
+ CDEBUG(D_HA, "%s: Resetting ns_connect_flags to server flags: %#llx\n",
+ imp->imp_obd->obd_name, ocd->ocd_connect_flags);
+ imp->imp_obd->obd_namespace->ns_connect_flags =
+ ocd->ocd_connect_flags;
+ imp->imp_obd->obd_namespace->ns_orig_connect_flags =
+ ocd->ocd_connect_flags;
+ }
+
+ if ((ocd->ocd_connect_flags & OBD_CONNECT_AT) &&
+ (imp->imp_msg_magic == LUSTRE_MSG_MAGIC_V2))
+ /* We need a per-message support flag, because
+ a. we don't know if the incoming connect reply
+ supports AT or not (in reply_in_callback)
+ until we unpack it.
+ b. failovered server means export and flags are gone
+ (in ptlrpc_send_reply).
+ Can only be set when we know AT is supported at
+ both ends */
+ imp->imp_msghdr_flags |= MSGHDR_AT_SUPPORT;
+ else
+ imp->imp_msghdr_flags &= ~MSGHDR_AT_SUPPORT;
+
+ if ((ocd->ocd_connect_flags & OBD_CONNECT_FULL20) &&
+ (imp->imp_msg_magic == LUSTRE_MSG_MAGIC_V2))
+ imp->imp_msghdr_flags |= MSGHDR_CKSUM_INCOMPAT18;
+ else
+ imp->imp_msghdr_flags &= ~MSGHDR_CKSUM_INCOMPAT18;
+
+ LASSERT((cli->cl_max_pages_per_rpc <= PTLRPC_MAX_BRW_PAGES) &&
+ (cli->cl_max_pages_per_rpc > 0));
+ }
+
+out:
+ imp->imp_connect_tried = 1;
+
+ if (rc != 0) {
+ IMPORT_SET_STATE(imp, LUSTRE_IMP_DISCON);
+ if (rc == -EACCES) {
+ /*
+ * Give up trying to reconnect
+ * EACCES means client has no permission for connection
+ */
+ imp->imp_obd->obd_no_recov = 1;
+ ptlrpc_deactivate_import(imp);
+ }
+
+ if (rc == -EPROTO) {
+ struct obd_connect_data *ocd;
+
+ /* reply message might not be ready */
+ if (request->rq_repmsg == NULL)
+ return -EPROTO;
+
+ ocd = req_capsule_server_get(&request->rq_pill,
+ &RMF_CONNECT_DATA);
+ if (ocd &&
+ (ocd->ocd_connect_flags & OBD_CONNECT_VERSION) &&
+ (ocd->ocd_version != LUSTRE_VERSION_CODE)) {
+ /*
+ * Actually servers are only supposed to refuse
+ * connection from liblustre clients, so we
+ * should never see this from VFS context
+ */
+ LCONSOLE_ERROR_MSG(0x16a, "Server %s version (%d.%d.%d.%d) refused connection from this client with an incompatible version (%s). Client must be recompiled\n",
+ obd2cli_tgt(imp->imp_obd),
+ OBD_OCD_VERSION_MAJOR(ocd->ocd_version),
+ OBD_OCD_VERSION_MINOR(ocd->ocd_version),
+ OBD_OCD_VERSION_PATCH(ocd->ocd_version),
+ OBD_OCD_VERSION_FIX(ocd->ocd_version),
+ LUSTRE_VERSION_STRING);
+ ptlrpc_deactivate_import(imp);
+ IMPORT_SET_STATE(imp, LUSTRE_IMP_CLOSED);
+ }
+ return -EPROTO;
+ }
+
+ ptlrpc_maybe_ping_import_soon(imp);
+
+ CDEBUG(D_HA, "recovery of %s on %s failed (%d)\n",
+ obd2cli_tgt(imp->imp_obd),
+ (char *)imp->imp_connection->c_remote_uuid.uuid, rc);
+ }
+
+ wake_up_all(&imp->imp_recovery_waitq);
+ return rc;
+}
+
+/**
+ * interpret callback for "completed replay" RPCs.
+ * \see signal_completed_replay
+ */
+static int completed_replay_interpret(const struct lu_env *env,
+ struct ptlrpc_request *req,
+ void *data, int rc)
+{
+ atomic_dec(&req->rq_import->imp_replay_inflight);
+ if (req->rq_status == 0 &&
+ !req->rq_import->imp_vbr_failed) {
+ ptlrpc_import_recovery_state_machine(req->rq_import);
+ } else {
+ if (req->rq_import->imp_vbr_failed) {
+ CDEBUG(D_WARNING,
+ "%s: version recovery fails, reconnecting\n",
+ req->rq_import->imp_obd->obd_name);
+ } else {
+ CDEBUG(D_HA, "%s: LAST_REPLAY message error: %d, reconnecting\n",
+ req->rq_import->imp_obd->obd_name,
+ req->rq_status);
+ }
+ ptlrpc_connect_import(req->rq_import);
+ }
+
+ return 0;
+}
+
+/**
+ * Let server know that we have no requests to replay anymore.
+ * Achieved by just sending a PING request
+ */
+static int signal_completed_replay(struct obd_import *imp)
+{
+ struct ptlrpc_request *req;
+
+ if (unlikely(OBD_FAIL_CHECK(OBD_FAIL_PTLRPC_FINISH_REPLAY)))
+ return 0;
+
+ LASSERT(atomic_read(&imp->imp_replay_inflight) == 0);
+ atomic_inc(&imp->imp_replay_inflight);
+
+ req = ptlrpc_request_alloc_pack(imp, &RQF_OBD_PING, LUSTRE_OBD_VERSION,
+ OBD_PING);
+ if (req == NULL) {
+ atomic_dec(&imp->imp_replay_inflight);
+ return -ENOMEM;
+ }
+
+ ptlrpc_request_set_replen(req);
+ req->rq_send_state = LUSTRE_IMP_REPLAY_WAIT;
+ lustre_msg_add_flags(req->rq_reqmsg,
+ MSG_LOCK_REPLAY_DONE | MSG_REQ_REPLAY_DONE);
+ if (AT_OFF)
+ req->rq_timeout *= 3;
+ req->rq_interpret_reply = completed_replay_interpret;
+
+ ptlrpcd_add_req(req, PDL_POLICY_ROUND, -1);
+ return 0;
+}
+
+/**
+ * In kernel code all import invalidation happens in its own
+ * separate thread, so that whatever application happened to encounter
+ * a problem could still be killed or otherwise continue
+ */
+static int ptlrpc_invalidate_import_thread(void *data)
+{
+ struct obd_import *imp = data;
+
+ unshare_fs_struct();
+
+ CDEBUG(D_HA, "thread invalidate import %s to %s@%s\n",
+ imp->imp_obd->obd_name, obd2cli_tgt(imp->imp_obd),
+ imp->imp_connection->c_remote_uuid.uuid);
+
+ ptlrpc_invalidate_import(imp);
+
+ if (obd_dump_on_eviction) {
+ CERROR("dump the log upon eviction\n");
+ libcfs_debug_dumplog();
+ }
+
+ IMPORT_SET_STATE(imp, LUSTRE_IMP_RECOVER);
+ ptlrpc_import_recovery_state_machine(imp);
+
+ class_import_put(imp);
+ return 0;
+}
+
+/**
+ * This is the state machine for client-side recovery on import.
+ *
+ * Typically we have two possibly paths. If we came to server and it is not
+ * in recovery, we just enter IMP_EVICTED state, invalidate our import
+ * state and reconnect from scratch.
+ * If we came to server that is in recovery, we enter IMP_REPLAY import state.
+ * We go through our list of requests to replay and send them to server one by
+ * one.
+ * After sending all request from the list we change import state to
+ * IMP_REPLAY_LOCKS and re-request all the locks we believe we have from server
+ * and also all the locks we don't yet have and wait for server to grant us.
+ * After that we send a special "replay completed" request and change import
+ * state to IMP_REPLAY_WAIT.
+ * Upon receiving reply to that "replay completed" RPC we enter IMP_RECOVER
+ * state and resend all requests from sending list.
+ * After that we promote import to FULL state and send all delayed requests
+ * and import is fully operational after that.
+ *
+ */
+int ptlrpc_import_recovery_state_machine(struct obd_import *imp)
+{
+ int rc = 0;
+ int inflight;
+ char *target_start;
+ int target_len;
+
+ if (imp->imp_state == LUSTRE_IMP_EVICTED) {
+ deuuidify(obd2cli_tgt(imp->imp_obd), NULL,
+ &target_start, &target_len);
+ /* Don't care about MGC eviction */
+ if (strcmp(imp->imp_obd->obd_type->typ_name,
+ LUSTRE_MGC_NAME) != 0) {
+ LCONSOLE_ERROR_MSG(0x167, "%s: This client was evicted by %.*s; in progress operations using this service will fail.\n",
+ imp->imp_obd->obd_name, target_len,
+ target_start);
+ }
+ CDEBUG(D_HA, "evicted from %s@%s; invalidating\n",
+ obd2cli_tgt(imp->imp_obd),
+ imp->imp_connection->c_remote_uuid.uuid);
+ /* reset vbr_failed flag upon eviction */
+ spin_lock(&imp->imp_lock);
+ imp->imp_vbr_failed = 0;
+ spin_unlock(&imp->imp_lock);
+
+ {
+ struct task_struct *task;
+ /* bug 17802: XXX client_disconnect_export vs connect request
+ * race. if client will evicted at this time, we start
+ * invalidate thread without reference to import and import can
+ * be freed at same time. */
+ class_import_get(imp);
+ task = kthread_run(ptlrpc_invalidate_import_thread, imp,
+ "ll_imp_inval");
+ if (IS_ERR(task)) {
+ class_import_put(imp);
+ CERROR("error starting invalidate thread: %d\n", rc);
+ rc = PTR_ERR(task);
+ } else {
+ rc = 0;
+ }
+ return rc;
+ }
+ }
+
+ if (imp->imp_state == LUSTRE_IMP_REPLAY) {
+ CDEBUG(D_HA, "replay requested by %s\n",
+ obd2cli_tgt(imp->imp_obd));
+ rc = ptlrpc_replay_next(imp, &inflight);
+ if (inflight == 0 &&
+ atomic_read(&imp->imp_replay_inflight) == 0) {
+ IMPORT_SET_STATE(imp, LUSTRE_IMP_REPLAY_LOCKS);
+ rc = ldlm_replay_locks(imp);
+ if (rc)
+ goto out;
+ }
+ rc = 0;
+ }
+
+ if (imp->imp_state == LUSTRE_IMP_REPLAY_LOCKS) {
+ if (atomic_read(&imp->imp_replay_inflight) == 0) {
+ IMPORT_SET_STATE(imp, LUSTRE_IMP_REPLAY_WAIT);
+ rc = signal_completed_replay(imp);
+ if (rc)
+ goto out;
+ }
+
+ }
+
+ if (imp->imp_state == LUSTRE_IMP_REPLAY_WAIT) {
+ if (atomic_read(&imp->imp_replay_inflight) == 0) {
+ IMPORT_SET_STATE(imp, LUSTRE_IMP_RECOVER);
+ }
+ }
+
+ if (imp->imp_state == LUSTRE_IMP_RECOVER) {
+ CDEBUG(D_HA, "reconnected to %s@%s\n",
+ obd2cli_tgt(imp->imp_obd),
+ imp->imp_connection->c_remote_uuid.uuid);
+
+ rc = ptlrpc_resend(imp);
+ if (rc)
+ goto out;
+ IMPORT_SET_STATE(imp, LUSTRE_IMP_FULL);
+ ptlrpc_activate_import(imp);
+
+ deuuidify(obd2cli_tgt(imp->imp_obd), NULL,
+ &target_start, &target_len);
+ LCONSOLE_INFO("%s: Connection restored to %.*s (at %s)\n",
+ imp->imp_obd->obd_name,
+ target_len, target_start,
+ libcfs_nid2str(imp->imp_connection->c_peer.nid));
+ }
+
+ if (imp->imp_state == LUSTRE_IMP_FULL) {
+ wake_up_all(&imp->imp_recovery_waitq);
+ ptlrpc_wake_delayed(imp);
+ }
+
+out:
+ return rc;
+}
+
+int ptlrpc_disconnect_import(struct obd_import *imp, int noclose)
+{
+ struct ptlrpc_request *req;
+ int rq_opc, rc = 0;
+
+ if (imp->imp_obd->obd_force)
+ goto set_state;
+
+ switch (imp->imp_connect_op) {
+ case OST_CONNECT:
+ rq_opc = OST_DISCONNECT;
+ break;
+ case MDS_CONNECT:
+ rq_opc = MDS_DISCONNECT;
+ break;
+ case MGS_CONNECT:
+ rq_opc = MGS_DISCONNECT;
+ break;
+ default:
+ rc = -EINVAL;
+ CERROR("%s: don't know how to disconnect from %s (connect_op %d): rc = %d\n",
+ imp->imp_obd->obd_name, obd2cli_tgt(imp->imp_obd),
+ imp->imp_connect_op, rc);
+ return rc;
+ }
+
+ if (ptlrpc_import_in_recovery(imp)) {
+ struct l_wait_info lwi;
+ long timeout;
+
+ if (AT_OFF) {
+ if (imp->imp_server_timeout)
+ timeout = cfs_time_seconds(obd_timeout / 2);
+ else
+ timeout = cfs_time_seconds(obd_timeout);
+ } else {
+ int idx = import_at_get_index(imp,
+ imp->imp_client->cli_request_portal);
+ timeout = cfs_time_seconds(
+ at_get(&imp->imp_at.iat_service_estimate[idx]));
+ }
+
+ lwi = LWI_TIMEOUT_INTR(cfs_timeout_cap(timeout),
+ back_to_sleep, LWI_ON_SIGNAL_NOOP, NULL);
+ rc = l_wait_event(imp->imp_recovery_waitq,
+ !ptlrpc_import_in_recovery(imp), &lwi);
+
+ }
+
+ spin_lock(&imp->imp_lock);
+ if (imp->imp_state != LUSTRE_IMP_FULL)
+ goto out;
+ spin_unlock(&imp->imp_lock);
+
+ req = ptlrpc_request_alloc_pack(imp, &RQF_MDS_DISCONNECT,
+ LUSTRE_OBD_VERSION, rq_opc);
+ if (req) {
+ /* We are disconnecting, do not retry a failed DISCONNECT rpc if
+ * it fails. We can get through the above with a down server
+ * if the client doesn't know the server is gone yet. */
+ req->rq_no_resend = 1;
+
+ /* We want client umounts to happen quickly, no matter the
+ server state... */
+ req->rq_timeout = min_t(int, req->rq_timeout,
+ INITIAL_CONNECT_TIMEOUT);
+
+ IMPORT_SET_STATE(imp, LUSTRE_IMP_CONNECTING);
+ req->rq_send_state = LUSTRE_IMP_CONNECTING;
+ ptlrpc_request_set_replen(req);
+ rc = ptlrpc_queue_wait(req);
+ ptlrpc_req_finished(req);
+ }
+
+set_state:
+ spin_lock(&imp->imp_lock);
+out:
+ if (noclose)
+ IMPORT_SET_STATE_NOLOCK(imp, LUSTRE_IMP_DISCON);
+ else
+ IMPORT_SET_STATE_NOLOCK(imp, LUSTRE_IMP_CLOSED);
+ memset(&imp->imp_remote_handle, 0, sizeof(imp->imp_remote_handle));
+ spin_unlock(&imp->imp_lock);
+
+ if (rc == -ETIMEDOUT || rc == -ENOTCONN || rc == -ESHUTDOWN)
+ rc = 0;
+
+ return rc;
+}
+EXPORT_SYMBOL(ptlrpc_disconnect_import);
+
+void ptlrpc_cleanup_imp(struct obd_import *imp)
+{
+ spin_lock(&imp->imp_lock);
+ IMPORT_SET_STATE_NOLOCK(imp, LUSTRE_IMP_CLOSED);
+ imp->imp_generation++;
+ spin_unlock(&imp->imp_lock);
+ ptlrpc_abort_inflight(imp);
+}
+EXPORT_SYMBOL(ptlrpc_cleanup_imp);
+
+/* Adaptive Timeout utils */
+extern unsigned int at_min, at_max, at_history;
+
+/* Bin into timeslices using AT_BINS bins.
+ This gives us a max of the last binlimit*AT_BINS secs without the storage,
+ but still smoothing out a return to normalcy from a slow response.
+ (E.g. remember the maximum latency in each minute of the last 4 minutes.) */
+int at_measured(struct adaptive_timeout *at, unsigned int val)
+{
+ unsigned int old = at->at_current;
+ time_t now = get_seconds();
+ time_t binlimit = max_t(time_t, at_history / AT_BINS, 1);
+
+ LASSERT(at);
+ CDEBUG(D_OTHER, "add %u to %p time=%lu v=%u (%u %u %u %u)\n",
+ val, at, now - at->at_binstart, at->at_current,
+ at->at_hist[0], at->at_hist[1], at->at_hist[2], at->at_hist[3]);
+
+ if (val == 0)
+ /* 0's don't count, because we never want our timeout to
+ drop to 0, and because 0 could mean an error */
+ return 0;
+
+ spin_lock(&at->at_lock);
+
+ if (unlikely(at->at_binstart == 0)) {
+ /* Special case to remove default from history */
+ at->at_current = val;
+ at->at_worst_ever = val;
+ at->at_worst_time = now;
+ at->at_hist[0] = val;
+ at->at_binstart = now;
+ } else if (now - at->at_binstart < binlimit) {
+ /* in bin 0 */
+ at->at_hist[0] = max(val, at->at_hist[0]);
+ at->at_current = max(val, at->at_current);
+ } else {
+ int i, shift;
+ unsigned int maxv = val;
+ /* move bins over */
+ shift = (now - at->at_binstart) / binlimit;
+ LASSERT(shift > 0);
+ for (i = AT_BINS - 1; i >= 0; i--) {
+ if (i >= shift) {
+ at->at_hist[i] = at->at_hist[i - shift];
+ maxv = max(maxv, at->at_hist[i]);
+ } else {
+ at->at_hist[i] = 0;
+ }
+ }
+ at->at_hist[0] = val;
+ at->at_current = maxv;
+ at->at_binstart += shift * binlimit;
+ }
+
+ if (at->at_current > at->at_worst_ever) {
+ at->at_worst_ever = at->at_current;
+ at->at_worst_time = now;
+ }
+
+ if (at->at_flags & AT_FLG_NOHIST)
+ /* Only keep last reported val; keeping the rest of the history
+ for proc only */
+ at->at_current = val;
+
+ if (at_max > 0)
+ at->at_current = min(at->at_current, at_max);
+ at->at_current = max(at->at_current, at_min);
+
+ if (at->at_current != old)
+ CDEBUG(D_OTHER, "AT %p change: old=%u new=%u delta=%d (val=%u) hist %u %u %u %u\n",
+ at,
+ old, at->at_current, at->at_current - old, val,
+ at->at_hist[0], at->at_hist[1], at->at_hist[2],
+ at->at_hist[3]);
+
+ /* if we changed, report the old value */
+ old = (at->at_current != old) ? old : 0;
+
+ spin_unlock(&at->at_lock);
+ return old;
+}
+
+/* Find the imp_at index for a given portal; assign if space available */
+int import_at_get_index(struct obd_import *imp, int portal)
+{
+ struct imp_at *at = &imp->imp_at;
+ int i;
+
+ for (i = 0; i < IMP_AT_MAX_PORTALS; i++) {
+ if (at->iat_portal[i] == portal)
+ return i;
+ if (at->iat_portal[i] == 0)
+ /* unused */
+ break;
+ }
+
+ /* Not found in list, add it under a lock */
+ spin_lock(&imp->imp_lock);
+
+ /* Check unused under lock */
+ for (; i < IMP_AT_MAX_PORTALS; i++) {
+ if (at->iat_portal[i] == portal)
+ goto out;
+ if (at->iat_portal[i] == 0)
+ /* unused */
+ break;
+ }
+
+ /* Not enough portals? */
+ LASSERT(i < IMP_AT_MAX_PORTALS);
+
+ at->iat_portal[i] = portal;
+out:
+ spin_unlock(&imp->imp_lock);
+ return i;
+}
diff --git a/drivers/staging/lustre/lustre/ptlrpc/layout.c b/drivers/staging/lustre/lustre/ptlrpc/layout.c
new file mode 100644
index 000000000..a42335e26
--- /dev/null
+++ b/drivers/staging/lustre/lustre/ptlrpc/layout.c
@@ -0,0 +1,2442 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2011, 2012, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ *
+ * lustre/ptlrpc/layout.c
+ *
+ * Lustre Metadata Target (mdt) request handler
+ *
+ * Author: Nikita Danilov <nikita@clusterfs.com>
+ */
+/*
+ * This file contains the "capsule/pill" abstraction layered above PTLRPC.
+ *
+ * Every struct ptlrpc_request contains a "pill", which points to a description
+ * of the format that the request conforms to.
+ */
+
+#if !defined(__REQ_LAYOUT_USER__)
+
+#define DEBUG_SUBSYSTEM S_RPC
+
+#include <linux/module.h>
+
+/* LUSTRE_VERSION_CODE */
+#include "../include/lustre_ver.h"
+
+#include "../include/obd_support.h"
+/* lustre_swab_mdt_body */
+#include "../include/lustre/lustre_idl.h"
+/* obd2cli_tgt() (required by DEBUG_REQ()) */
+#include "../include/obd.h"
+
+/* __REQ_LAYOUT_USER__ */
+#endif
+/* struct ptlrpc_request, lustre_msg* */
+#include "../include/lustre_req_layout.h"
+#include "../include/lustre_acl.h"
+#include "../include/lustre_debug.h"
+
+/*
+ * RQFs (see below) refer to two struct req_msg_field arrays describing the
+ * client request and server reply, respectively.
+ */
+/* empty set of fields... for suitable definition of emptiness. */
+static const struct req_msg_field *empty[] = {
+ &RMF_PTLRPC_BODY
+};
+
+static const struct req_msg_field *mgs_target_info_only[] = {
+ &RMF_PTLRPC_BODY,
+ &RMF_MGS_TARGET_INFO
+};
+
+static const struct req_msg_field *mgs_set_info[] = {
+ &RMF_PTLRPC_BODY,
+ &RMF_MGS_SEND_PARAM
+};
+
+static const struct req_msg_field *mgs_config_read_client[] = {
+ &RMF_PTLRPC_BODY,
+ &RMF_MGS_CONFIG_BODY
+};
+
+static const struct req_msg_field *mgs_config_read_server[] = {
+ &RMF_PTLRPC_BODY,
+ &RMF_MGS_CONFIG_RES
+};
+
+static const struct req_msg_field *log_cancel_client[] = {
+ &RMF_PTLRPC_BODY,
+ &RMF_LOGCOOKIES
+};
+
+static const struct req_msg_field *mdt_body_only[] = {
+ &RMF_PTLRPC_BODY,
+ &RMF_MDT_BODY
+};
+
+static const struct req_msg_field *mdt_body_capa[] = {
+ &RMF_PTLRPC_BODY,
+ &RMF_MDT_BODY,
+ &RMF_CAPA1
+};
+
+static const struct req_msg_field *quotactl_only[] = {
+ &RMF_PTLRPC_BODY,
+ &RMF_OBD_QUOTACTL
+};
+
+static const struct req_msg_field *quota_body_only[] = {
+ &RMF_PTLRPC_BODY,
+ &RMF_QUOTA_BODY
+};
+
+static const struct req_msg_field *ldlm_intent_quota_client[] = {
+ &RMF_PTLRPC_BODY,
+ &RMF_DLM_REQ,
+ &RMF_LDLM_INTENT,
+ &RMF_QUOTA_BODY
+};
+
+static const struct req_msg_field *ldlm_intent_quota_server[] = {
+ &RMF_PTLRPC_BODY,
+ &RMF_DLM_REP,
+ &RMF_DLM_LVB,
+ &RMF_QUOTA_BODY
+};
+
+static const struct req_msg_field *mdt_close_client[] = {
+ &RMF_PTLRPC_BODY,
+ &RMF_MDT_EPOCH,
+ &RMF_REC_REINT,
+ &RMF_CAPA1
+};
+
+static const struct req_msg_field *mdt_release_close_client[] = {
+ &RMF_PTLRPC_BODY,
+ &RMF_MDT_EPOCH,
+ &RMF_REC_REINT,
+ &RMF_CAPA1,
+ &RMF_CLOSE_DATA
+};
+
+static const struct req_msg_field *obd_statfs_server[] = {
+ &RMF_PTLRPC_BODY,
+ &RMF_OBD_STATFS
+};
+
+static const struct req_msg_field *seq_query_client[] = {
+ &RMF_PTLRPC_BODY,
+ &RMF_SEQ_OPC,
+ &RMF_SEQ_RANGE
+};
+
+static const struct req_msg_field *seq_query_server[] = {
+ &RMF_PTLRPC_BODY,
+ &RMF_SEQ_RANGE
+};
+
+static const struct req_msg_field *fld_query_client[] = {
+ &RMF_PTLRPC_BODY,
+ &RMF_FLD_OPC,
+ &RMF_FLD_MDFLD
+};
+
+static const struct req_msg_field *fld_query_server[] = {
+ &RMF_PTLRPC_BODY,
+ &RMF_FLD_MDFLD
+};
+
+static const struct req_msg_field *mds_getattr_name_client[] = {
+ &RMF_PTLRPC_BODY,
+ &RMF_MDT_BODY,
+ &RMF_CAPA1,
+ &RMF_NAME
+};
+
+static const struct req_msg_field *mds_reint_client[] = {
+ &RMF_PTLRPC_BODY,
+ &RMF_REC_REINT
+};
+
+static const struct req_msg_field *mds_reint_create_client[] = {
+ &RMF_PTLRPC_BODY,
+ &RMF_REC_REINT,
+ &RMF_CAPA1,
+ &RMF_NAME
+};
+
+static const struct req_msg_field *mds_reint_create_slave_client[] = {
+ &RMF_PTLRPC_BODY,
+ &RMF_REC_REINT,
+ &RMF_CAPA1,
+ &RMF_NAME,
+ &RMF_EADATA,
+ &RMF_DLM_REQ
+};
+
+static const struct req_msg_field *mds_reint_create_rmt_acl_client[] = {
+ &RMF_PTLRPC_BODY,
+ &RMF_REC_REINT,
+ &RMF_CAPA1,
+ &RMF_NAME,
+ &RMF_EADATA,
+ &RMF_DLM_REQ
+};
+
+static const struct req_msg_field *mds_reint_create_sym_client[] = {
+ &RMF_PTLRPC_BODY,
+ &RMF_REC_REINT,
+ &RMF_CAPA1,
+ &RMF_NAME,
+ &RMF_SYMTGT,
+ &RMF_DLM_REQ
+};
+
+static const struct req_msg_field *mds_reint_open_client[] = {
+ &RMF_PTLRPC_BODY,
+ &RMF_REC_REINT,
+ &RMF_CAPA1,
+ &RMF_CAPA2,
+ &RMF_NAME,
+ &RMF_EADATA
+};
+
+static const struct req_msg_field *mds_reint_open_server[] = {
+ &RMF_PTLRPC_BODY,
+ &RMF_MDT_BODY,
+ &RMF_MDT_MD,
+ &RMF_ACL,
+ &RMF_CAPA1,
+ &RMF_CAPA2
+};
+
+static const struct req_msg_field *mds_reint_unlink_client[] = {
+ &RMF_PTLRPC_BODY,
+ &RMF_REC_REINT,
+ &RMF_CAPA1,
+ &RMF_NAME,
+ &RMF_DLM_REQ
+};
+
+static const struct req_msg_field *mds_reint_link_client[] = {
+ &RMF_PTLRPC_BODY,
+ &RMF_REC_REINT,
+ &RMF_CAPA1,
+ &RMF_CAPA2,
+ &RMF_NAME,
+ &RMF_DLM_REQ
+};
+
+static const struct req_msg_field *mds_reint_rename_client[] = {
+ &RMF_PTLRPC_BODY,
+ &RMF_REC_REINT,
+ &RMF_CAPA1,
+ &RMF_CAPA2,
+ &RMF_NAME,
+ &RMF_SYMTGT,
+ &RMF_DLM_REQ
+};
+
+static const struct req_msg_field *mds_last_unlink_server[] = {
+ &RMF_PTLRPC_BODY,
+ &RMF_MDT_BODY,
+ &RMF_MDT_MD,
+ &RMF_LOGCOOKIES,
+ &RMF_CAPA1,
+ &RMF_CAPA2
+};
+
+static const struct req_msg_field *mds_reint_setattr_client[] = {
+ &RMF_PTLRPC_BODY,
+ &RMF_REC_REINT,
+ &RMF_CAPA1,
+ &RMF_MDT_EPOCH,
+ &RMF_EADATA,
+ &RMF_LOGCOOKIES,
+ &RMF_DLM_REQ
+};
+
+static const struct req_msg_field *mds_reint_setxattr_client[] = {
+ &RMF_PTLRPC_BODY,
+ &RMF_REC_REINT,
+ &RMF_CAPA1,
+ &RMF_NAME,
+ &RMF_EADATA,
+ &RMF_DLM_REQ
+};
+
+static const struct req_msg_field *mdt_swap_layouts[] = {
+ &RMF_PTLRPC_BODY,
+ &RMF_MDT_BODY,
+ &RMF_SWAP_LAYOUTS,
+ &RMF_CAPA1,
+ &RMF_CAPA2,
+ &RMF_DLM_REQ
+};
+
+static const struct req_msg_field *obd_connect_client[] = {
+ &RMF_PTLRPC_BODY,
+ &RMF_TGTUUID,
+ &RMF_CLUUID,
+ &RMF_CONN,
+ &RMF_CONNECT_DATA
+};
+
+static const struct req_msg_field *obd_connect_server[] = {
+ &RMF_PTLRPC_BODY,
+ &RMF_CONNECT_DATA
+};
+
+static const struct req_msg_field *obd_set_info_client[] = {
+ &RMF_PTLRPC_BODY,
+ &RMF_SETINFO_KEY,
+ &RMF_SETINFO_VAL
+};
+
+static const struct req_msg_field *ost_grant_shrink_client[] = {
+ &RMF_PTLRPC_BODY,
+ &RMF_SETINFO_KEY,
+ &RMF_OST_BODY
+};
+
+static const struct req_msg_field *mds_getinfo_client[] = {
+ &RMF_PTLRPC_BODY,
+ &RMF_GETINFO_KEY,
+ &RMF_GETINFO_VALLEN
+};
+
+static const struct req_msg_field *mds_getinfo_server[] = {
+ &RMF_PTLRPC_BODY,
+ &RMF_GETINFO_VAL,
+};
+
+static const struct req_msg_field *ldlm_enqueue_client[] = {
+ &RMF_PTLRPC_BODY,
+ &RMF_DLM_REQ
+};
+
+static const struct req_msg_field *ldlm_enqueue_server[] = {
+ &RMF_PTLRPC_BODY,
+ &RMF_DLM_REP
+};
+
+static const struct req_msg_field *ldlm_enqueue_lvb_server[] = {
+ &RMF_PTLRPC_BODY,
+ &RMF_DLM_REP,
+ &RMF_DLM_LVB
+};
+
+static const struct req_msg_field *ldlm_cp_callback_client[] = {
+ &RMF_PTLRPC_BODY,
+ &RMF_DLM_REQ,
+ &RMF_DLM_LVB
+};
+
+static const struct req_msg_field *ldlm_gl_callback_desc_client[] = {
+ &RMF_PTLRPC_BODY,
+ &RMF_DLM_REQ,
+ &RMF_DLM_GL_DESC
+};
+
+static const struct req_msg_field *ldlm_gl_callback_server[] = {
+ &RMF_PTLRPC_BODY,
+ &RMF_DLM_LVB
+};
+
+static const struct req_msg_field *ldlm_intent_basic_client[] = {
+ &RMF_PTLRPC_BODY,
+ &RMF_DLM_REQ,
+ &RMF_LDLM_INTENT,
+};
+
+static const struct req_msg_field *ldlm_intent_client[] = {
+ &RMF_PTLRPC_BODY,
+ &RMF_DLM_REQ,
+ &RMF_LDLM_INTENT,
+ &RMF_REC_REINT
+};
+
+static const struct req_msg_field *ldlm_intent_server[] = {
+ &RMF_PTLRPC_BODY,
+ &RMF_DLM_REP,
+ &RMF_MDT_BODY,
+ &RMF_MDT_MD,
+ &RMF_ACL
+};
+
+static const struct req_msg_field *ldlm_intent_layout_client[] = {
+ &RMF_PTLRPC_BODY,
+ &RMF_DLM_REQ,
+ &RMF_LDLM_INTENT,
+ &RMF_LAYOUT_INTENT,
+ &RMF_EADATA /* for new layout to be set up */
+};
+static const struct req_msg_field *ldlm_intent_open_server[] = {
+ &RMF_PTLRPC_BODY,
+ &RMF_DLM_REP,
+ &RMF_MDT_BODY,
+ &RMF_MDT_MD,
+ &RMF_ACL,
+ &RMF_CAPA1,
+ &RMF_CAPA2
+};
+
+static const struct req_msg_field *ldlm_intent_getattr_client[] = {
+ &RMF_PTLRPC_BODY,
+ &RMF_DLM_REQ,
+ &RMF_LDLM_INTENT,
+ &RMF_MDT_BODY, /* coincides with mds_getattr_name_client[] */
+ &RMF_CAPA1,
+ &RMF_NAME
+};
+
+static const struct req_msg_field *ldlm_intent_getattr_server[] = {
+ &RMF_PTLRPC_BODY,
+ &RMF_DLM_REP,
+ &RMF_MDT_BODY,
+ &RMF_MDT_MD,
+ &RMF_ACL,
+ &RMF_CAPA1
+};
+
+static const struct req_msg_field *ldlm_intent_create_client[] = {
+ &RMF_PTLRPC_BODY,
+ &RMF_DLM_REQ,
+ &RMF_LDLM_INTENT,
+ &RMF_REC_REINT, /* coincides with mds_reint_create_client[] */
+ &RMF_CAPA1,
+ &RMF_NAME,
+ &RMF_EADATA
+};
+
+static const struct req_msg_field *ldlm_intent_open_client[] = {
+ &RMF_PTLRPC_BODY,
+ &RMF_DLM_REQ,
+ &RMF_LDLM_INTENT,
+ &RMF_REC_REINT, /* coincides with mds_reint_open_client[] */
+ &RMF_CAPA1,
+ &RMF_CAPA2,
+ &RMF_NAME,
+ &RMF_EADATA
+};
+
+static const struct req_msg_field *ldlm_intent_unlink_client[] = {
+ &RMF_PTLRPC_BODY,
+ &RMF_DLM_REQ,
+ &RMF_LDLM_INTENT,
+ &RMF_REC_REINT, /* coincides with mds_reint_unlink_client[] */
+ &RMF_CAPA1,
+ &RMF_NAME
+};
+
+static const struct req_msg_field *ldlm_intent_getxattr_client[] = {
+ &RMF_PTLRPC_BODY,
+ &RMF_DLM_REQ,
+ &RMF_LDLM_INTENT,
+ &RMF_MDT_BODY,
+ &RMF_CAPA1,
+};
+
+static const struct req_msg_field *ldlm_intent_getxattr_server[] = {
+ &RMF_PTLRPC_BODY,
+ &RMF_DLM_REP,
+ &RMF_MDT_BODY,
+ &RMF_MDT_MD,
+ &RMF_ACL, /* for req_capsule_extend/mdt_intent_policy */
+ &RMF_EADATA,
+ &RMF_EAVALS,
+ &RMF_EAVALS_LENS
+};
+
+static const struct req_msg_field *mds_getxattr_client[] = {
+ &RMF_PTLRPC_BODY,
+ &RMF_MDT_BODY,
+ &RMF_CAPA1,
+ &RMF_NAME,
+ &RMF_EADATA
+};
+
+static const struct req_msg_field *mds_getxattr_server[] = {
+ &RMF_PTLRPC_BODY,
+ &RMF_MDT_BODY,
+ &RMF_EADATA
+};
+
+static const struct req_msg_field *mds_getattr_server[] = {
+ &RMF_PTLRPC_BODY,
+ &RMF_MDT_BODY,
+ &RMF_MDT_MD,
+ &RMF_ACL,
+ &RMF_CAPA1,
+ &RMF_CAPA2
+};
+
+static const struct req_msg_field *mds_setattr_server[] = {
+ &RMF_PTLRPC_BODY,
+ &RMF_MDT_BODY,
+ &RMF_MDT_MD,
+ &RMF_ACL,
+ &RMF_CAPA1,
+ &RMF_CAPA2
+};
+
+static const struct req_msg_field *mds_update_client[] = {
+ &RMF_PTLRPC_BODY,
+ &RMF_UPDATE,
+};
+
+static const struct req_msg_field *mds_update_server[] = {
+ &RMF_PTLRPC_BODY,
+ &RMF_UPDATE_REPLY,
+};
+
+static const struct req_msg_field *llog_origin_handle_create_client[] = {
+ &RMF_PTLRPC_BODY,
+ &RMF_LLOGD_BODY,
+ &RMF_NAME
+};
+
+static const struct req_msg_field *llogd_body_only[] = {
+ &RMF_PTLRPC_BODY,
+ &RMF_LLOGD_BODY
+};
+
+static const struct req_msg_field *llog_log_hdr_only[] = {
+ &RMF_PTLRPC_BODY,
+ &RMF_LLOG_LOG_HDR
+};
+
+static const struct req_msg_field *llogd_conn_body_only[] = {
+ &RMF_PTLRPC_BODY,
+ &RMF_LLOGD_CONN_BODY
+};
+
+static const struct req_msg_field *llog_origin_handle_next_block_server[] = {
+ &RMF_PTLRPC_BODY,
+ &RMF_LLOGD_BODY,
+ &RMF_EADATA
+};
+
+static const struct req_msg_field *obd_idx_read_client[] = {
+ &RMF_PTLRPC_BODY,
+ &RMF_IDX_INFO
+};
+
+static const struct req_msg_field *obd_idx_read_server[] = {
+ &RMF_PTLRPC_BODY,
+ &RMF_IDX_INFO
+};
+
+static const struct req_msg_field *ost_body_only[] = {
+ &RMF_PTLRPC_BODY,
+ &RMF_OST_BODY
+};
+
+static const struct req_msg_field *ost_body_capa[] = {
+ &RMF_PTLRPC_BODY,
+ &RMF_OST_BODY,
+ &RMF_CAPA1
+};
+
+static const struct req_msg_field *ost_destroy_client[] = {
+ &RMF_PTLRPC_BODY,
+ &RMF_OST_BODY,
+ &RMF_DLM_REQ,
+ &RMF_CAPA1
+};
+
+
+static const struct req_msg_field *ost_brw_client[] = {
+ &RMF_PTLRPC_BODY,
+ &RMF_OST_BODY,
+ &RMF_OBD_IOOBJ,
+ &RMF_NIOBUF_REMOTE,
+ &RMF_CAPA1
+};
+
+static const struct req_msg_field *ost_brw_read_server[] = {
+ &RMF_PTLRPC_BODY,
+ &RMF_OST_BODY
+};
+
+static const struct req_msg_field *ost_brw_write_server[] = {
+ &RMF_PTLRPC_BODY,
+ &RMF_OST_BODY,
+ &RMF_RCS
+};
+
+static const struct req_msg_field *ost_get_info_generic_server[] = {
+ &RMF_PTLRPC_BODY,
+ &RMF_GENERIC_DATA,
+};
+
+static const struct req_msg_field *ost_get_info_generic_client[] = {
+ &RMF_PTLRPC_BODY,
+ &RMF_SETINFO_KEY
+};
+
+static const struct req_msg_field *ost_get_last_id_server[] = {
+ &RMF_PTLRPC_BODY,
+ &RMF_OBD_ID
+};
+
+static const struct req_msg_field *ost_get_last_fid_server[] = {
+ &RMF_PTLRPC_BODY,
+ &RMF_FID,
+};
+
+static const struct req_msg_field *ost_get_fiemap_client[] = {
+ &RMF_PTLRPC_BODY,
+ &RMF_FIEMAP_KEY,
+ &RMF_FIEMAP_VAL
+};
+
+static const struct req_msg_field *ost_get_fiemap_server[] = {
+ &RMF_PTLRPC_BODY,
+ &RMF_FIEMAP_VAL
+};
+
+static const struct req_msg_field *mdt_hsm_progress[] = {
+ &RMF_PTLRPC_BODY,
+ &RMF_MDT_BODY,
+ &RMF_MDS_HSM_PROGRESS,
+};
+
+static const struct req_msg_field *mdt_hsm_ct_register[] = {
+ &RMF_PTLRPC_BODY,
+ &RMF_MDT_BODY,
+ &RMF_MDS_HSM_ARCHIVE,
+};
+
+static const struct req_msg_field *mdt_hsm_ct_unregister[] = {
+ &RMF_PTLRPC_BODY,
+ &RMF_MDT_BODY,
+};
+
+static const struct req_msg_field *mdt_hsm_action_server[] = {
+ &RMF_PTLRPC_BODY,
+ &RMF_MDT_BODY,
+ &RMF_MDS_HSM_CURRENT_ACTION,
+};
+
+static const struct req_msg_field *mdt_hsm_state_get_server[] = {
+ &RMF_PTLRPC_BODY,
+ &RMF_MDT_BODY,
+ &RMF_HSM_USER_STATE,
+};
+
+static const struct req_msg_field *mdt_hsm_state_set[] = {
+ &RMF_PTLRPC_BODY,
+ &RMF_MDT_BODY,
+ &RMF_CAPA1,
+ &RMF_HSM_STATE_SET,
+};
+
+static const struct req_msg_field *mdt_hsm_request[] = {
+ &RMF_PTLRPC_BODY,
+ &RMF_MDT_BODY,
+ &RMF_MDS_HSM_REQUEST,
+ &RMF_MDS_HSM_USER_ITEM,
+ &RMF_GENERIC_DATA,
+};
+
+static struct req_format *req_formats[] = {
+ &RQF_OBD_PING,
+ &RQF_OBD_SET_INFO,
+ &RQF_OBD_IDX_READ,
+ &RQF_SEC_CTX,
+ &RQF_MGS_TARGET_REG,
+ &RQF_MGS_SET_INFO,
+ &RQF_MGS_CONFIG_READ,
+ &RQF_SEQ_QUERY,
+ &RQF_FLD_QUERY,
+ &RQF_MDS_CONNECT,
+ &RQF_MDS_DISCONNECT,
+ &RQF_MDS_GET_INFO,
+ &RQF_MDS_GETSTATUS,
+ &RQF_MDS_STATFS,
+ &RQF_MDS_GETATTR,
+ &RQF_MDS_GETATTR_NAME,
+ &RQF_MDS_GETXATTR,
+ &RQF_MDS_SYNC,
+ &RQF_MDS_CLOSE,
+ &RQF_MDS_RELEASE_CLOSE,
+ &RQF_MDS_PIN,
+ &RQF_MDS_UNPIN,
+ &RQF_MDS_READPAGE,
+ &RQF_MDS_WRITEPAGE,
+ &RQF_MDS_IS_SUBDIR,
+ &RQF_MDS_DONE_WRITING,
+ &RQF_MDS_REINT,
+ &RQF_MDS_REINT_CREATE,
+ &RQF_MDS_REINT_CREATE_RMT_ACL,
+ &RQF_MDS_REINT_CREATE_SLAVE,
+ &RQF_MDS_REINT_CREATE_SYM,
+ &RQF_MDS_REINT_OPEN,
+ &RQF_MDS_REINT_UNLINK,
+ &RQF_MDS_REINT_LINK,
+ &RQF_MDS_REINT_RENAME,
+ &RQF_MDS_REINT_SETATTR,
+ &RQF_MDS_REINT_SETXATTR,
+ &RQF_MDS_QUOTACHECK,
+ &RQF_MDS_QUOTACTL,
+ &RQF_MDS_HSM_PROGRESS,
+ &RQF_MDS_HSM_CT_REGISTER,
+ &RQF_MDS_HSM_CT_UNREGISTER,
+ &RQF_MDS_HSM_STATE_GET,
+ &RQF_MDS_HSM_STATE_SET,
+ &RQF_MDS_HSM_ACTION,
+ &RQF_MDS_HSM_REQUEST,
+ &RQF_MDS_SWAP_LAYOUTS,
+ &RQF_UPDATE_OBJ,
+ &RQF_QC_CALLBACK,
+ &RQF_OST_CONNECT,
+ &RQF_OST_DISCONNECT,
+ &RQF_OST_QUOTACHECK,
+ &RQF_OST_QUOTACTL,
+ &RQF_OST_GETATTR,
+ &RQF_OST_SETATTR,
+ &RQF_OST_CREATE,
+ &RQF_OST_PUNCH,
+ &RQF_OST_SYNC,
+ &RQF_OST_DESTROY,
+ &RQF_OST_BRW_READ,
+ &RQF_OST_BRW_WRITE,
+ &RQF_OST_STATFS,
+ &RQF_OST_SET_GRANT_INFO,
+ &RQF_OST_GET_INFO_GENERIC,
+ &RQF_OST_GET_INFO_LAST_ID,
+ &RQF_OST_GET_INFO_LAST_FID,
+ &RQF_OST_SET_INFO_LAST_FID,
+ &RQF_OST_GET_INFO_FIEMAP,
+ &RQF_LDLM_ENQUEUE,
+ &RQF_LDLM_ENQUEUE_LVB,
+ &RQF_LDLM_CONVERT,
+ &RQF_LDLM_CANCEL,
+ &RQF_LDLM_CALLBACK,
+ &RQF_LDLM_CP_CALLBACK,
+ &RQF_LDLM_BL_CALLBACK,
+ &RQF_LDLM_GL_CALLBACK,
+ &RQF_LDLM_GL_DESC_CALLBACK,
+ &RQF_LDLM_INTENT,
+ &RQF_LDLM_INTENT_BASIC,
+ &RQF_LDLM_INTENT_LAYOUT,
+ &RQF_LDLM_INTENT_GETATTR,
+ &RQF_LDLM_INTENT_OPEN,
+ &RQF_LDLM_INTENT_CREATE,
+ &RQF_LDLM_INTENT_UNLINK,
+ &RQF_LDLM_INTENT_GETXATTR,
+ &RQF_LDLM_INTENT_QUOTA,
+ &RQF_QUOTA_DQACQ,
+ &RQF_LOG_CANCEL,
+ &RQF_LLOG_ORIGIN_HANDLE_CREATE,
+ &RQF_LLOG_ORIGIN_HANDLE_DESTROY,
+ &RQF_LLOG_ORIGIN_HANDLE_NEXT_BLOCK,
+ &RQF_LLOG_ORIGIN_HANDLE_PREV_BLOCK,
+ &RQF_LLOG_ORIGIN_HANDLE_READ_HEADER,
+ &RQF_LLOG_ORIGIN_CONNECT,
+ &RQF_CONNECT,
+};
+
+struct req_msg_field {
+ const __u32 rmf_flags;
+ const char *rmf_name;
+ /**
+ * Field length. (-1) means "variable length". If the
+ * \a RMF_F_STRUCT_ARRAY flag is set the field is also variable-length,
+ * but the actual size must be a whole multiple of \a rmf_size.
+ */
+ const int rmf_size;
+ void (*rmf_swabber)(void *);
+ void (*rmf_dumper)(void *);
+ int rmf_offset[ARRAY_SIZE(req_formats)][RCL_NR];
+};
+
+enum rmf_flags {
+ /**
+ * The field is a string, must be NUL-terminated.
+ */
+ RMF_F_STRING = 1 << 0,
+ /**
+ * The field's buffer size need not match the declared \a rmf_size.
+ */
+ RMF_F_NO_SIZE_CHECK = 1 << 1,
+ /**
+ * The field's buffer size must be a whole multiple of the declared \a
+ * rmf_size and the \a rmf_swabber function must work on the declared \a
+ * rmf_size worth of bytes.
+ */
+ RMF_F_STRUCT_ARRAY = 1 << 2
+};
+
+struct req_capsule;
+
+/*
+ * Request fields.
+ */
+#define DEFINE_MSGF(name, flags, size, swabber, dumper) { \
+ .rmf_name = (name), \
+ .rmf_flags = (flags), \
+ .rmf_size = (size), \
+ .rmf_swabber = (void (*)(void *))(swabber), \
+ .rmf_dumper = (void (*)(void *))(dumper) \
+}
+
+struct req_msg_field RMF_GENERIC_DATA =
+ DEFINE_MSGF("generic_data", 0,
+ -1, NULL, NULL);
+EXPORT_SYMBOL(RMF_GENERIC_DATA);
+
+struct req_msg_field RMF_MGS_TARGET_INFO =
+ DEFINE_MSGF("mgs_target_info", 0,
+ sizeof(struct mgs_target_info),
+ lustre_swab_mgs_target_info, NULL);
+EXPORT_SYMBOL(RMF_MGS_TARGET_INFO);
+
+struct req_msg_field RMF_MGS_SEND_PARAM =
+ DEFINE_MSGF("mgs_send_param", 0,
+ sizeof(struct mgs_send_param),
+ NULL, NULL);
+EXPORT_SYMBOL(RMF_MGS_SEND_PARAM);
+
+struct req_msg_field RMF_MGS_CONFIG_BODY =
+ DEFINE_MSGF("mgs_config_read request", 0,
+ sizeof(struct mgs_config_body),
+ lustre_swab_mgs_config_body, NULL);
+EXPORT_SYMBOL(RMF_MGS_CONFIG_BODY);
+
+struct req_msg_field RMF_MGS_CONFIG_RES =
+ DEFINE_MSGF("mgs_config_read reply ", 0,
+ sizeof(struct mgs_config_res),
+ lustre_swab_mgs_config_res, NULL);
+EXPORT_SYMBOL(RMF_MGS_CONFIG_RES);
+
+struct req_msg_field RMF_U32 =
+ DEFINE_MSGF("generic u32", 0,
+ sizeof(__u32), lustre_swab_generic_32s, NULL);
+EXPORT_SYMBOL(RMF_U32);
+
+struct req_msg_field RMF_SETINFO_VAL =
+ DEFINE_MSGF("setinfo_val", 0, -1, NULL, NULL);
+EXPORT_SYMBOL(RMF_SETINFO_VAL);
+
+struct req_msg_field RMF_GETINFO_KEY =
+ DEFINE_MSGF("getinfo_key", 0, -1, NULL, NULL);
+EXPORT_SYMBOL(RMF_GETINFO_KEY);
+
+struct req_msg_field RMF_GETINFO_VALLEN =
+ DEFINE_MSGF("getinfo_vallen", 0,
+ sizeof(__u32), lustre_swab_generic_32s, NULL);
+EXPORT_SYMBOL(RMF_GETINFO_VALLEN);
+
+struct req_msg_field RMF_GETINFO_VAL =
+ DEFINE_MSGF("getinfo_val", 0, -1, NULL, NULL);
+EXPORT_SYMBOL(RMF_GETINFO_VAL);
+
+struct req_msg_field RMF_SEQ_OPC =
+ DEFINE_MSGF("seq_query_opc", 0,
+ sizeof(__u32), lustre_swab_generic_32s, NULL);
+EXPORT_SYMBOL(RMF_SEQ_OPC);
+
+struct req_msg_field RMF_SEQ_RANGE =
+ DEFINE_MSGF("seq_query_range", 0,
+ sizeof(struct lu_seq_range),
+ lustre_swab_lu_seq_range, NULL);
+EXPORT_SYMBOL(RMF_SEQ_RANGE);
+
+struct req_msg_field RMF_FLD_OPC =
+ DEFINE_MSGF("fld_query_opc", 0,
+ sizeof(__u32), lustre_swab_generic_32s, NULL);
+EXPORT_SYMBOL(RMF_FLD_OPC);
+
+struct req_msg_field RMF_FLD_MDFLD =
+ DEFINE_MSGF("fld_query_mdfld", 0,
+ sizeof(struct lu_seq_range),
+ lustre_swab_lu_seq_range, NULL);
+EXPORT_SYMBOL(RMF_FLD_MDFLD);
+
+struct req_msg_field RMF_MDT_BODY =
+ DEFINE_MSGF("mdt_body", 0,
+ sizeof(struct mdt_body), lustre_swab_mdt_body, NULL);
+EXPORT_SYMBOL(RMF_MDT_BODY);
+
+struct req_msg_field RMF_OBD_QUOTACTL =
+ DEFINE_MSGF("obd_quotactl", 0,
+ sizeof(struct obd_quotactl),
+ lustre_swab_obd_quotactl, NULL);
+EXPORT_SYMBOL(RMF_OBD_QUOTACTL);
+
+struct req_msg_field RMF_QUOTA_BODY =
+ DEFINE_MSGF("quota_body", 0,
+ sizeof(struct quota_body), lustre_swab_quota_body, NULL);
+EXPORT_SYMBOL(RMF_QUOTA_BODY);
+
+struct req_msg_field RMF_MDT_EPOCH =
+ DEFINE_MSGF("mdt_ioepoch", 0,
+ sizeof(struct mdt_ioepoch), lustre_swab_mdt_ioepoch, NULL);
+EXPORT_SYMBOL(RMF_MDT_EPOCH);
+
+struct req_msg_field RMF_PTLRPC_BODY =
+ DEFINE_MSGF("ptlrpc_body", 0,
+ sizeof(struct ptlrpc_body), lustre_swab_ptlrpc_body, NULL);
+EXPORT_SYMBOL(RMF_PTLRPC_BODY);
+
+struct req_msg_field RMF_CLOSE_DATA =
+ DEFINE_MSGF("data_version", 0,
+ sizeof(struct close_data), lustre_swab_close_data, NULL);
+EXPORT_SYMBOL(RMF_CLOSE_DATA);
+
+struct req_msg_field RMF_OBD_STATFS =
+ DEFINE_MSGF("obd_statfs", 0,
+ sizeof(struct obd_statfs), lustre_swab_obd_statfs, NULL);
+EXPORT_SYMBOL(RMF_OBD_STATFS);
+
+struct req_msg_field RMF_SETINFO_KEY =
+ DEFINE_MSGF("setinfo_key", 0, -1, NULL, NULL);
+EXPORT_SYMBOL(RMF_SETINFO_KEY);
+
+struct req_msg_field RMF_NAME =
+ DEFINE_MSGF("name", RMF_F_STRING, -1, NULL, NULL);
+EXPORT_SYMBOL(RMF_NAME);
+
+struct req_msg_field RMF_SYMTGT =
+ DEFINE_MSGF("symtgt", RMF_F_STRING, -1, NULL, NULL);
+EXPORT_SYMBOL(RMF_SYMTGT);
+
+struct req_msg_field RMF_TGTUUID =
+ DEFINE_MSGF("tgtuuid", RMF_F_STRING, sizeof(struct obd_uuid) - 1, NULL,
+ NULL);
+EXPORT_SYMBOL(RMF_TGTUUID);
+
+struct req_msg_field RMF_CLUUID =
+ DEFINE_MSGF("cluuid", RMF_F_STRING, sizeof(struct obd_uuid) - 1, NULL,
+ NULL);
+EXPORT_SYMBOL(RMF_CLUUID);
+
+struct req_msg_field RMF_STRING =
+ DEFINE_MSGF("string", RMF_F_STRING, -1, NULL, NULL);
+EXPORT_SYMBOL(RMF_STRING);
+
+struct req_msg_field RMF_LLOGD_BODY =
+ DEFINE_MSGF("llogd_body", 0,
+ sizeof(struct llogd_body), lustre_swab_llogd_body, NULL);
+EXPORT_SYMBOL(RMF_LLOGD_BODY);
+
+struct req_msg_field RMF_LLOG_LOG_HDR =
+ DEFINE_MSGF("llog_log_hdr", 0,
+ sizeof(struct llog_log_hdr), lustre_swab_llog_hdr, NULL);
+EXPORT_SYMBOL(RMF_LLOG_LOG_HDR);
+
+struct req_msg_field RMF_LLOGD_CONN_BODY =
+ DEFINE_MSGF("llogd_conn_body", 0,
+ sizeof(struct llogd_conn_body),
+ lustre_swab_llogd_conn_body, NULL);
+EXPORT_SYMBOL(RMF_LLOGD_CONN_BODY);
+
+/*
+ * connection handle received in MDS_CONNECT request.
+ *
+ * No swabbing needed because struct lustre_handle contains only a 64-bit cookie
+ * that the client does not interpret at all.
+ */
+struct req_msg_field RMF_CONN =
+ DEFINE_MSGF("conn", 0, sizeof(struct lustre_handle), NULL, NULL);
+EXPORT_SYMBOL(RMF_CONN);
+
+struct req_msg_field RMF_CONNECT_DATA =
+ DEFINE_MSGF("cdata",
+ RMF_F_NO_SIZE_CHECK /* we allow extra space for interop */,
+ sizeof(struct obd_connect_data),
+ lustre_swab_connect, NULL);
+EXPORT_SYMBOL(RMF_CONNECT_DATA);
+
+struct req_msg_field RMF_DLM_REQ =
+ DEFINE_MSGF("dlm_req", RMF_F_NO_SIZE_CHECK /* ldlm_request_bufsize */,
+ sizeof(struct ldlm_request),
+ lustre_swab_ldlm_request, NULL);
+EXPORT_SYMBOL(RMF_DLM_REQ);
+
+struct req_msg_field RMF_DLM_REP =
+ DEFINE_MSGF("dlm_rep", 0,
+ sizeof(struct ldlm_reply), lustre_swab_ldlm_reply, NULL);
+EXPORT_SYMBOL(RMF_DLM_REP);
+
+struct req_msg_field RMF_LDLM_INTENT =
+ DEFINE_MSGF("ldlm_intent", 0,
+ sizeof(struct ldlm_intent), lustre_swab_ldlm_intent, NULL);
+EXPORT_SYMBOL(RMF_LDLM_INTENT);
+
+struct req_msg_field RMF_DLM_LVB =
+ DEFINE_MSGF("dlm_lvb", 0, -1, NULL, NULL);
+EXPORT_SYMBOL(RMF_DLM_LVB);
+
+struct req_msg_field RMF_DLM_GL_DESC =
+ DEFINE_MSGF("dlm_gl_desc", 0, sizeof(union ldlm_gl_desc),
+ lustre_swab_gl_desc, NULL);
+EXPORT_SYMBOL(RMF_DLM_GL_DESC);
+
+struct req_msg_field RMF_MDT_MD =
+ DEFINE_MSGF("mdt_md", RMF_F_NO_SIZE_CHECK, MIN_MD_SIZE, NULL, NULL);
+EXPORT_SYMBOL(RMF_MDT_MD);
+
+struct req_msg_field RMF_REC_REINT =
+ DEFINE_MSGF("rec_reint", 0, sizeof(struct mdt_rec_reint),
+ lustre_swab_mdt_rec_reint, NULL);
+EXPORT_SYMBOL(RMF_REC_REINT);
+
+/* FIXME: this length should be defined as a macro */
+struct req_msg_field RMF_EADATA = DEFINE_MSGF("eadata", 0, -1,
+ NULL, NULL);
+EXPORT_SYMBOL(RMF_EADATA);
+
+struct req_msg_field RMF_EAVALS = DEFINE_MSGF("eavals", 0, -1, NULL, NULL);
+EXPORT_SYMBOL(RMF_EAVALS);
+
+struct req_msg_field RMF_ACL =
+ DEFINE_MSGF("acl", RMF_F_NO_SIZE_CHECK,
+ LUSTRE_POSIX_ACL_MAX_SIZE, NULL, NULL);
+EXPORT_SYMBOL(RMF_ACL);
+
+/* FIXME: this should be made to use RMF_F_STRUCT_ARRAY */
+struct req_msg_field RMF_LOGCOOKIES =
+ DEFINE_MSGF("logcookies", RMF_F_NO_SIZE_CHECK /* multiple cookies */,
+ sizeof(struct llog_cookie), NULL, NULL);
+EXPORT_SYMBOL(RMF_LOGCOOKIES);
+
+struct req_msg_field RMF_CAPA1 =
+ DEFINE_MSGF("capa", 0, sizeof(struct lustre_capa),
+ lustre_swab_lustre_capa, NULL);
+EXPORT_SYMBOL(RMF_CAPA1);
+
+struct req_msg_field RMF_CAPA2 =
+ DEFINE_MSGF("capa", 0, sizeof(struct lustre_capa),
+ lustre_swab_lustre_capa, NULL);
+EXPORT_SYMBOL(RMF_CAPA2);
+
+struct req_msg_field RMF_LAYOUT_INTENT =
+ DEFINE_MSGF("layout_intent", 0,
+ sizeof(struct layout_intent), lustre_swab_layout_intent,
+ NULL);
+EXPORT_SYMBOL(RMF_LAYOUT_INTENT);
+
+/*
+ * OST request field.
+ */
+struct req_msg_field RMF_OST_BODY =
+ DEFINE_MSGF("ost_body", 0,
+ sizeof(struct ost_body), lustre_swab_ost_body, dump_ost_body);
+EXPORT_SYMBOL(RMF_OST_BODY);
+
+struct req_msg_field RMF_OBD_IOOBJ =
+ DEFINE_MSGF("obd_ioobj", RMF_F_STRUCT_ARRAY,
+ sizeof(struct obd_ioobj), lustre_swab_obd_ioobj, dump_ioo);
+EXPORT_SYMBOL(RMF_OBD_IOOBJ);
+
+struct req_msg_field RMF_NIOBUF_REMOTE =
+ DEFINE_MSGF("niobuf_remote", RMF_F_STRUCT_ARRAY,
+ sizeof(struct niobuf_remote), lustre_swab_niobuf_remote,
+ dump_rniobuf);
+EXPORT_SYMBOL(RMF_NIOBUF_REMOTE);
+
+struct req_msg_field RMF_RCS =
+ DEFINE_MSGF("niobuf_remote", RMF_F_STRUCT_ARRAY, sizeof(__u32),
+ lustre_swab_generic_32s, dump_rcs);
+EXPORT_SYMBOL(RMF_RCS);
+
+struct req_msg_field RMF_EAVALS_LENS =
+ DEFINE_MSGF("eavals_lens", RMF_F_STRUCT_ARRAY, sizeof(__u32),
+ lustre_swab_generic_32s, NULL);
+EXPORT_SYMBOL(RMF_EAVALS_LENS);
+
+struct req_msg_field RMF_OBD_ID =
+ DEFINE_MSGF("u64", 0,
+ sizeof(u64), lustre_swab_ost_last_id, NULL);
+EXPORT_SYMBOL(RMF_OBD_ID);
+
+struct req_msg_field RMF_FID =
+ DEFINE_MSGF("fid", 0,
+ sizeof(struct lu_fid), lustre_swab_lu_fid, NULL);
+EXPORT_SYMBOL(RMF_FID);
+
+struct req_msg_field RMF_OST_ID =
+ DEFINE_MSGF("ost_id", 0,
+ sizeof(struct ost_id), lustre_swab_ost_id, NULL);
+EXPORT_SYMBOL(RMF_OST_ID);
+
+struct req_msg_field RMF_FIEMAP_KEY =
+ DEFINE_MSGF("fiemap", 0, sizeof(struct ll_fiemap_info_key),
+ lustre_swab_fiemap, NULL);
+EXPORT_SYMBOL(RMF_FIEMAP_KEY);
+
+struct req_msg_field RMF_FIEMAP_VAL =
+ DEFINE_MSGF("fiemap", 0, -1, lustre_swab_fiemap, NULL);
+EXPORT_SYMBOL(RMF_FIEMAP_VAL);
+
+struct req_msg_field RMF_IDX_INFO =
+ DEFINE_MSGF("idx_info", 0, sizeof(struct idx_info),
+ lustre_swab_idx_info, NULL);
+EXPORT_SYMBOL(RMF_IDX_INFO);
+struct req_msg_field RMF_HSM_USER_STATE =
+ DEFINE_MSGF("hsm_user_state", 0, sizeof(struct hsm_user_state),
+ lustre_swab_hsm_user_state, NULL);
+EXPORT_SYMBOL(RMF_HSM_USER_STATE);
+
+struct req_msg_field RMF_HSM_STATE_SET =
+ DEFINE_MSGF("hsm_state_set", 0, sizeof(struct hsm_state_set),
+ lustre_swab_hsm_state_set, NULL);
+EXPORT_SYMBOL(RMF_HSM_STATE_SET);
+
+struct req_msg_field RMF_MDS_HSM_PROGRESS =
+ DEFINE_MSGF("hsm_progress", 0, sizeof(struct hsm_progress_kernel),
+ lustre_swab_hsm_progress_kernel, NULL);
+EXPORT_SYMBOL(RMF_MDS_HSM_PROGRESS);
+
+struct req_msg_field RMF_MDS_HSM_CURRENT_ACTION =
+ DEFINE_MSGF("hsm_current_action", 0, sizeof(struct hsm_current_action),
+ lustre_swab_hsm_current_action, NULL);
+EXPORT_SYMBOL(RMF_MDS_HSM_CURRENT_ACTION);
+
+struct req_msg_field RMF_MDS_HSM_USER_ITEM =
+ DEFINE_MSGF("hsm_user_item", RMF_F_STRUCT_ARRAY,
+ sizeof(struct hsm_user_item), lustre_swab_hsm_user_item,
+ NULL);
+EXPORT_SYMBOL(RMF_MDS_HSM_USER_ITEM);
+
+struct req_msg_field RMF_MDS_HSM_ARCHIVE =
+ DEFINE_MSGF("hsm_archive", 0,
+ sizeof(__u32), lustre_swab_generic_32s, NULL);
+EXPORT_SYMBOL(RMF_MDS_HSM_ARCHIVE);
+
+struct req_msg_field RMF_MDS_HSM_REQUEST =
+ DEFINE_MSGF("hsm_request", 0, sizeof(struct hsm_request),
+ lustre_swab_hsm_request, NULL);
+EXPORT_SYMBOL(RMF_MDS_HSM_REQUEST);
+
+struct req_msg_field RMF_UPDATE = DEFINE_MSGF("update", 0, -1,
+ lustre_swab_update_buf, NULL);
+EXPORT_SYMBOL(RMF_UPDATE);
+
+struct req_msg_field RMF_UPDATE_REPLY = DEFINE_MSGF("update_reply", 0, -1,
+ lustre_swab_update_reply_buf,
+ NULL);
+EXPORT_SYMBOL(RMF_UPDATE_REPLY);
+
+struct req_msg_field RMF_SWAP_LAYOUTS =
+ DEFINE_MSGF("swap_layouts", 0, sizeof(struct mdc_swap_layouts),
+ lustre_swab_swap_layouts, NULL);
+EXPORT_SYMBOL(RMF_SWAP_LAYOUTS);
+/*
+ * Request formats.
+ */
+
+struct req_format {
+ const char *rf_name;
+ int rf_idx;
+ struct {
+ int nr;
+ const struct req_msg_field **d;
+ } rf_fields[RCL_NR];
+};
+
+#define DEFINE_REQ_FMT(name, client, client_nr, server, server_nr) { \
+ .rf_name = name, \
+ .rf_fields = { \
+ [RCL_CLIENT] = { \
+ .nr = client_nr, \
+ .d = client \
+ }, \
+ [RCL_SERVER] = { \
+ .nr = server_nr, \
+ .d = server \
+ } \
+ } \
+}
+
+#define DEFINE_REQ_FMT0(name, client, server) \
+DEFINE_REQ_FMT(name, client, ARRAY_SIZE(client), server, ARRAY_SIZE(server))
+
+struct req_format RQF_OBD_PING =
+ DEFINE_REQ_FMT0("OBD_PING", empty, empty);
+EXPORT_SYMBOL(RQF_OBD_PING);
+
+struct req_format RQF_OBD_SET_INFO =
+ DEFINE_REQ_FMT0("OBD_SET_INFO", obd_set_info_client, empty);
+EXPORT_SYMBOL(RQF_OBD_SET_INFO);
+
+/* Read index file through the network */
+struct req_format RQF_OBD_IDX_READ =
+ DEFINE_REQ_FMT0("OBD_IDX_READ",
+ obd_idx_read_client, obd_idx_read_server);
+EXPORT_SYMBOL(RQF_OBD_IDX_READ);
+
+struct req_format RQF_SEC_CTX =
+ DEFINE_REQ_FMT0("SEC_CTX", empty, empty);
+EXPORT_SYMBOL(RQF_SEC_CTX);
+
+struct req_format RQF_MGS_TARGET_REG =
+ DEFINE_REQ_FMT0("MGS_TARGET_REG", mgs_target_info_only,
+ mgs_target_info_only);
+EXPORT_SYMBOL(RQF_MGS_TARGET_REG);
+
+struct req_format RQF_MGS_SET_INFO =
+ DEFINE_REQ_FMT0("MGS_SET_INFO", mgs_set_info,
+ mgs_set_info);
+EXPORT_SYMBOL(RQF_MGS_SET_INFO);
+
+struct req_format RQF_MGS_CONFIG_READ =
+ DEFINE_REQ_FMT0("MGS_CONFIG_READ", mgs_config_read_client,
+ mgs_config_read_server);
+EXPORT_SYMBOL(RQF_MGS_CONFIG_READ);
+
+struct req_format RQF_SEQ_QUERY =
+ DEFINE_REQ_FMT0("SEQ_QUERY", seq_query_client, seq_query_server);
+EXPORT_SYMBOL(RQF_SEQ_QUERY);
+
+struct req_format RQF_FLD_QUERY =
+ DEFINE_REQ_FMT0("FLD_QUERY", fld_query_client, fld_query_server);
+EXPORT_SYMBOL(RQF_FLD_QUERY);
+
+struct req_format RQF_LOG_CANCEL =
+ DEFINE_REQ_FMT0("OBD_LOG_CANCEL", log_cancel_client, empty);
+EXPORT_SYMBOL(RQF_LOG_CANCEL);
+
+struct req_format RQF_MDS_QUOTACHECK =
+ DEFINE_REQ_FMT0("MDS_QUOTACHECK", quotactl_only, empty);
+EXPORT_SYMBOL(RQF_MDS_QUOTACHECK);
+
+struct req_format RQF_OST_QUOTACHECK =
+ DEFINE_REQ_FMT0("OST_QUOTACHECK", quotactl_only, empty);
+EXPORT_SYMBOL(RQF_OST_QUOTACHECK);
+
+struct req_format RQF_MDS_QUOTACTL =
+ DEFINE_REQ_FMT0("MDS_QUOTACTL", quotactl_only, quotactl_only);
+EXPORT_SYMBOL(RQF_MDS_QUOTACTL);
+
+struct req_format RQF_OST_QUOTACTL =
+ DEFINE_REQ_FMT0("OST_QUOTACTL", quotactl_only, quotactl_only);
+EXPORT_SYMBOL(RQF_OST_QUOTACTL);
+
+struct req_format RQF_QC_CALLBACK =
+ DEFINE_REQ_FMT0("QC_CALLBACK", quotactl_only, empty);
+EXPORT_SYMBOL(RQF_QC_CALLBACK);
+
+struct req_format RQF_QUOTA_DQACQ =
+ DEFINE_REQ_FMT0("QUOTA_DQACQ", quota_body_only, quota_body_only);
+EXPORT_SYMBOL(RQF_QUOTA_DQACQ);
+
+struct req_format RQF_LDLM_INTENT_QUOTA =
+ DEFINE_REQ_FMT0("LDLM_INTENT_QUOTA",
+ ldlm_intent_quota_client,
+ ldlm_intent_quota_server);
+EXPORT_SYMBOL(RQF_LDLM_INTENT_QUOTA);
+
+struct req_format RQF_MDS_GETSTATUS =
+ DEFINE_REQ_FMT0("MDS_GETSTATUS", mdt_body_only, mdt_body_capa);
+EXPORT_SYMBOL(RQF_MDS_GETSTATUS);
+
+struct req_format RQF_MDS_STATFS =
+ DEFINE_REQ_FMT0("MDS_STATFS", empty, obd_statfs_server);
+EXPORT_SYMBOL(RQF_MDS_STATFS);
+
+struct req_format RQF_MDS_SYNC =
+ DEFINE_REQ_FMT0("MDS_SYNC", mdt_body_capa, mdt_body_only);
+EXPORT_SYMBOL(RQF_MDS_SYNC);
+
+struct req_format RQF_MDS_GETATTR =
+ DEFINE_REQ_FMT0("MDS_GETATTR", mdt_body_capa, mds_getattr_server);
+EXPORT_SYMBOL(RQF_MDS_GETATTR);
+
+struct req_format RQF_MDS_GETXATTR =
+ DEFINE_REQ_FMT0("MDS_GETXATTR",
+ mds_getxattr_client, mds_getxattr_server);
+EXPORT_SYMBOL(RQF_MDS_GETXATTR);
+
+struct req_format RQF_MDS_GETATTR_NAME =
+ DEFINE_REQ_FMT0("MDS_GETATTR_NAME",
+ mds_getattr_name_client, mds_getattr_server);
+EXPORT_SYMBOL(RQF_MDS_GETATTR_NAME);
+
+struct req_format RQF_MDS_REINT =
+ DEFINE_REQ_FMT0("MDS_REINT", mds_reint_client, mdt_body_only);
+EXPORT_SYMBOL(RQF_MDS_REINT);
+
+struct req_format RQF_MDS_REINT_CREATE =
+ DEFINE_REQ_FMT0("MDS_REINT_CREATE",
+ mds_reint_create_client, mdt_body_capa);
+EXPORT_SYMBOL(RQF_MDS_REINT_CREATE);
+
+struct req_format RQF_MDS_REINT_CREATE_RMT_ACL =
+ DEFINE_REQ_FMT0("MDS_REINT_CREATE_RMT_ACL",
+ mds_reint_create_rmt_acl_client, mdt_body_capa);
+EXPORT_SYMBOL(RQF_MDS_REINT_CREATE_RMT_ACL);
+
+struct req_format RQF_MDS_REINT_CREATE_SLAVE =
+ DEFINE_REQ_FMT0("MDS_REINT_CREATE_EA",
+ mds_reint_create_slave_client, mdt_body_capa);
+EXPORT_SYMBOL(RQF_MDS_REINT_CREATE_SLAVE);
+
+struct req_format RQF_MDS_REINT_CREATE_SYM =
+ DEFINE_REQ_FMT0("MDS_REINT_CREATE_SYM",
+ mds_reint_create_sym_client, mdt_body_capa);
+EXPORT_SYMBOL(RQF_MDS_REINT_CREATE_SYM);
+
+struct req_format RQF_MDS_REINT_OPEN =
+ DEFINE_REQ_FMT0("MDS_REINT_OPEN",
+ mds_reint_open_client, mds_reint_open_server);
+EXPORT_SYMBOL(RQF_MDS_REINT_OPEN);
+
+struct req_format RQF_MDS_REINT_UNLINK =
+ DEFINE_REQ_FMT0("MDS_REINT_UNLINK", mds_reint_unlink_client,
+ mds_last_unlink_server);
+EXPORT_SYMBOL(RQF_MDS_REINT_UNLINK);
+
+struct req_format RQF_MDS_REINT_LINK =
+ DEFINE_REQ_FMT0("MDS_REINT_LINK",
+ mds_reint_link_client, mdt_body_only);
+EXPORT_SYMBOL(RQF_MDS_REINT_LINK);
+
+struct req_format RQF_MDS_REINT_RENAME =
+ DEFINE_REQ_FMT0("MDS_REINT_RENAME", mds_reint_rename_client,
+ mds_last_unlink_server);
+EXPORT_SYMBOL(RQF_MDS_REINT_RENAME);
+
+struct req_format RQF_MDS_REINT_SETATTR =
+ DEFINE_REQ_FMT0("MDS_REINT_SETATTR",
+ mds_reint_setattr_client, mds_setattr_server);
+EXPORT_SYMBOL(RQF_MDS_REINT_SETATTR);
+
+struct req_format RQF_MDS_REINT_SETXATTR =
+ DEFINE_REQ_FMT0("MDS_REINT_SETXATTR",
+ mds_reint_setxattr_client, mdt_body_only);
+EXPORT_SYMBOL(RQF_MDS_REINT_SETXATTR);
+
+struct req_format RQF_MDS_CONNECT =
+ DEFINE_REQ_FMT0("MDS_CONNECT",
+ obd_connect_client, obd_connect_server);
+EXPORT_SYMBOL(RQF_MDS_CONNECT);
+
+struct req_format RQF_MDS_DISCONNECT =
+ DEFINE_REQ_FMT0("MDS_DISCONNECT", empty, empty);
+EXPORT_SYMBOL(RQF_MDS_DISCONNECT);
+
+struct req_format RQF_MDS_GET_INFO =
+ DEFINE_REQ_FMT0("MDS_GET_INFO", mds_getinfo_client,
+ mds_getinfo_server);
+EXPORT_SYMBOL(RQF_MDS_GET_INFO);
+
+struct req_format RQF_UPDATE_OBJ =
+ DEFINE_REQ_FMT0("OBJECT_UPDATE_OBJ", mds_update_client,
+ mds_update_server);
+EXPORT_SYMBOL(RQF_UPDATE_OBJ);
+
+struct req_format RQF_LDLM_ENQUEUE =
+ DEFINE_REQ_FMT0("LDLM_ENQUEUE",
+ ldlm_enqueue_client, ldlm_enqueue_lvb_server);
+EXPORT_SYMBOL(RQF_LDLM_ENQUEUE);
+
+struct req_format RQF_LDLM_ENQUEUE_LVB =
+ DEFINE_REQ_FMT0("LDLM_ENQUEUE_LVB",
+ ldlm_enqueue_client, ldlm_enqueue_lvb_server);
+EXPORT_SYMBOL(RQF_LDLM_ENQUEUE_LVB);
+
+struct req_format RQF_LDLM_CONVERT =
+ DEFINE_REQ_FMT0("LDLM_CONVERT",
+ ldlm_enqueue_client, ldlm_enqueue_server);
+EXPORT_SYMBOL(RQF_LDLM_CONVERT);
+
+struct req_format RQF_LDLM_CANCEL =
+ DEFINE_REQ_FMT0("LDLM_CANCEL", ldlm_enqueue_client, empty);
+EXPORT_SYMBOL(RQF_LDLM_CANCEL);
+
+struct req_format RQF_LDLM_CALLBACK =
+ DEFINE_REQ_FMT0("LDLM_CALLBACK", ldlm_enqueue_client, empty);
+EXPORT_SYMBOL(RQF_LDLM_CALLBACK);
+
+struct req_format RQF_LDLM_CP_CALLBACK =
+ DEFINE_REQ_FMT0("LDLM_CP_CALLBACK", ldlm_cp_callback_client, empty);
+EXPORT_SYMBOL(RQF_LDLM_CP_CALLBACK);
+
+struct req_format RQF_LDLM_BL_CALLBACK =
+ DEFINE_REQ_FMT0("LDLM_BL_CALLBACK", ldlm_enqueue_client, empty);
+EXPORT_SYMBOL(RQF_LDLM_BL_CALLBACK);
+
+struct req_format RQF_LDLM_GL_CALLBACK =
+ DEFINE_REQ_FMT0("LDLM_GL_CALLBACK", ldlm_enqueue_client,
+ ldlm_gl_callback_server);
+EXPORT_SYMBOL(RQF_LDLM_GL_CALLBACK);
+
+struct req_format RQF_LDLM_GL_DESC_CALLBACK =
+ DEFINE_REQ_FMT0("LDLM_GL_CALLBACK", ldlm_gl_callback_desc_client,
+ ldlm_gl_callback_server);
+EXPORT_SYMBOL(RQF_LDLM_GL_DESC_CALLBACK);
+
+struct req_format RQF_LDLM_INTENT_BASIC =
+ DEFINE_REQ_FMT0("LDLM_INTENT_BASIC",
+ ldlm_intent_basic_client, ldlm_enqueue_lvb_server);
+EXPORT_SYMBOL(RQF_LDLM_INTENT_BASIC);
+
+struct req_format RQF_LDLM_INTENT =
+ DEFINE_REQ_FMT0("LDLM_INTENT",
+ ldlm_intent_client, ldlm_intent_server);
+EXPORT_SYMBOL(RQF_LDLM_INTENT);
+
+struct req_format RQF_LDLM_INTENT_LAYOUT =
+ DEFINE_REQ_FMT0("LDLM_INTENT_LAYOUT ",
+ ldlm_intent_layout_client, ldlm_enqueue_lvb_server);
+EXPORT_SYMBOL(RQF_LDLM_INTENT_LAYOUT);
+
+struct req_format RQF_LDLM_INTENT_GETATTR =
+ DEFINE_REQ_FMT0("LDLM_INTENT_GETATTR",
+ ldlm_intent_getattr_client, ldlm_intent_getattr_server);
+EXPORT_SYMBOL(RQF_LDLM_INTENT_GETATTR);
+
+struct req_format RQF_LDLM_INTENT_OPEN =
+ DEFINE_REQ_FMT0("LDLM_INTENT_OPEN",
+ ldlm_intent_open_client, ldlm_intent_open_server);
+EXPORT_SYMBOL(RQF_LDLM_INTENT_OPEN);
+
+struct req_format RQF_LDLM_INTENT_CREATE =
+ DEFINE_REQ_FMT0("LDLM_INTENT_CREATE",
+ ldlm_intent_create_client, ldlm_intent_getattr_server);
+EXPORT_SYMBOL(RQF_LDLM_INTENT_CREATE);
+
+struct req_format RQF_LDLM_INTENT_UNLINK =
+ DEFINE_REQ_FMT0("LDLM_INTENT_UNLINK",
+ ldlm_intent_unlink_client, ldlm_intent_server);
+EXPORT_SYMBOL(RQF_LDLM_INTENT_UNLINK);
+
+struct req_format RQF_LDLM_INTENT_GETXATTR =
+ DEFINE_REQ_FMT0("LDLM_INTENT_GETXATTR",
+ ldlm_intent_getxattr_client,
+ ldlm_intent_getxattr_server);
+EXPORT_SYMBOL(RQF_LDLM_INTENT_GETXATTR);
+
+struct req_format RQF_MDS_CLOSE =
+ DEFINE_REQ_FMT0("MDS_CLOSE",
+ mdt_close_client, mds_last_unlink_server);
+EXPORT_SYMBOL(RQF_MDS_CLOSE);
+
+struct req_format RQF_MDS_RELEASE_CLOSE =
+ DEFINE_REQ_FMT0("MDS_CLOSE",
+ mdt_release_close_client, mds_last_unlink_server);
+EXPORT_SYMBOL(RQF_MDS_RELEASE_CLOSE);
+
+struct req_format RQF_MDS_PIN =
+ DEFINE_REQ_FMT0("MDS_PIN",
+ mdt_body_capa, mdt_body_only);
+EXPORT_SYMBOL(RQF_MDS_PIN);
+
+struct req_format RQF_MDS_UNPIN =
+ DEFINE_REQ_FMT0("MDS_UNPIN", mdt_body_only, empty);
+EXPORT_SYMBOL(RQF_MDS_UNPIN);
+
+struct req_format RQF_MDS_DONE_WRITING =
+ DEFINE_REQ_FMT0("MDS_DONE_WRITING",
+ mdt_close_client, mdt_body_only);
+EXPORT_SYMBOL(RQF_MDS_DONE_WRITING);
+
+struct req_format RQF_MDS_READPAGE =
+ DEFINE_REQ_FMT0("MDS_READPAGE",
+ mdt_body_capa, mdt_body_only);
+EXPORT_SYMBOL(RQF_MDS_READPAGE);
+
+struct req_format RQF_MDS_HSM_ACTION =
+ DEFINE_REQ_FMT0("MDS_HSM_ACTION", mdt_body_capa, mdt_hsm_action_server);
+EXPORT_SYMBOL(RQF_MDS_HSM_ACTION);
+
+struct req_format RQF_MDS_HSM_PROGRESS =
+ DEFINE_REQ_FMT0("MDS_HSM_PROGRESS", mdt_hsm_progress, empty);
+EXPORT_SYMBOL(RQF_MDS_HSM_PROGRESS);
+
+struct req_format RQF_MDS_HSM_CT_REGISTER =
+ DEFINE_REQ_FMT0("MDS_HSM_CT_REGISTER", mdt_hsm_ct_register, empty);
+EXPORT_SYMBOL(RQF_MDS_HSM_CT_REGISTER);
+
+struct req_format RQF_MDS_HSM_CT_UNREGISTER =
+ DEFINE_REQ_FMT0("MDS_HSM_CT_UNREGISTER", mdt_hsm_ct_unregister, empty);
+EXPORT_SYMBOL(RQF_MDS_HSM_CT_UNREGISTER);
+
+struct req_format RQF_MDS_HSM_STATE_GET =
+ DEFINE_REQ_FMT0("MDS_HSM_STATE_GET",
+ mdt_body_capa, mdt_hsm_state_get_server);
+EXPORT_SYMBOL(RQF_MDS_HSM_STATE_GET);
+
+struct req_format RQF_MDS_HSM_STATE_SET =
+ DEFINE_REQ_FMT0("MDS_HSM_STATE_SET", mdt_hsm_state_set, empty);
+EXPORT_SYMBOL(RQF_MDS_HSM_STATE_SET);
+
+struct req_format RQF_MDS_HSM_REQUEST =
+ DEFINE_REQ_FMT0("MDS_HSM_REQUEST", mdt_hsm_request, empty);
+EXPORT_SYMBOL(RQF_MDS_HSM_REQUEST);
+
+struct req_format RQF_MDS_SWAP_LAYOUTS =
+ DEFINE_REQ_FMT0("MDS_SWAP_LAYOUTS",
+ mdt_swap_layouts, empty);
+EXPORT_SYMBOL(RQF_MDS_SWAP_LAYOUTS);
+
+/* This is for split */
+struct req_format RQF_MDS_WRITEPAGE =
+ DEFINE_REQ_FMT0("MDS_WRITEPAGE",
+ mdt_body_capa, mdt_body_only);
+EXPORT_SYMBOL(RQF_MDS_WRITEPAGE);
+
+struct req_format RQF_MDS_IS_SUBDIR =
+ DEFINE_REQ_FMT0("MDS_IS_SUBDIR",
+ mdt_body_only, mdt_body_only);
+EXPORT_SYMBOL(RQF_MDS_IS_SUBDIR);
+
+struct req_format RQF_LLOG_ORIGIN_HANDLE_CREATE =
+ DEFINE_REQ_FMT0("LLOG_ORIGIN_HANDLE_CREATE",
+ llog_origin_handle_create_client, llogd_body_only);
+EXPORT_SYMBOL(RQF_LLOG_ORIGIN_HANDLE_CREATE);
+
+struct req_format RQF_LLOG_ORIGIN_HANDLE_DESTROY =
+ DEFINE_REQ_FMT0("LLOG_ORIGIN_HANDLE_DESTROY",
+ llogd_body_only, llogd_body_only);
+EXPORT_SYMBOL(RQF_LLOG_ORIGIN_HANDLE_DESTROY);
+
+struct req_format RQF_LLOG_ORIGIN_HANDLE_NEXT_BLOCK =
+ DEFINE_REQ_FMT0("LLOG_ORIGIN_HANDLE_NEXT_BLOCK",
+ llogd_body_only, llog_origin_handle_next_block_server);
+EXPORT_SYMBOL(RQF_LLOG_ORIGIN_HANDLE_NEXT_BLOCK);
+
+struct req_format RQF_LLOG_ORIGIN_HANDLE_PREV_BLOCK =
+ DEFINE_REQ_FMT0("LLOG_ORIGIN_HANDLE_PREV_BLOCK",
+ llogd_body_only, llog_origin_handle_next_block_server);
+EXPORT_SYMBOL(RQF_LLOG_ORIGIN_HANDLE_PREV_BLOCK);
+
+struct req_format RQF_LLOG_ORIGIN_HANDLE_READ_HEADER =
+ DEFINE_REQ_FMT0("LLOG_ORIGIN_HANDLE_READ_HEADER",
+ llogd_body_only, llog_log_hdr_only);
+EXPORT_SYMBOL(RQF_LLOG_ORIGIN_HANDLE_READ_HEADER);
+
+struct req_format RQF_LLOG_ORIGIN_CONNECT =
+ DEFINE_REQ_FMT0("LLOG_ORIGIN_CONNECT", llogd_conn_body_only, empty);
+EXPORT_SYMBOL(RQF_LLOG_ORIGIN_CONNECT);
+
+struct req_format RQF_CONNECT =
+ DEFINE_REQ_FMT0("CONNECT", obd_connect_client, obd_connect_server);
+EXPORT_SYMBOL(RQF_CONNECT);
+
+struct req_format RQF_OST_CONNECT =
+ DEFINE_REQ_FMT0("OST_CONNECT",
+ obd_connect_client, obd_connect_server);
+EXPORT_SYMBOL(RQF_OST_CONNECT);
+
+struct req_format RQF_OST_DISCONNECT =
+ DEFINE_REQ_FMT0("OST_DISCONNECT", empty, empty);
+EXPORT_SYMBOL(RQF_OST_DISCONNECT);
+
+struct req_format RQF_OST_GETATTR =
+ DEFINE_REQ_FMT0("OST_GETATTR", ost_body_capa, ost_body_only);
+EXPORT_SYMBOL(RQF_OST_GETATTR);
+
+struct req_format RQF_OST_SETATTR =
+ DEFINE_REQ_FMT0("OST_SETATTR", ost_body_capa, ost_body_only);
+EXPORT_SYMBOL(RQF_OST_SETATTR);
+
+struct req_format RQF_OST_CREATE =
+ DEFINE_REQ_FMT0("OST_CREATE", ost_body_only, ost_body_only);
+EXPORT_SYMBOL(RQF_OST_CREATE);
+
+struct req_format RQF_OST_PUNCH =
+ DEFINE_REQ_FMT0("OST_PUNCH", ost_body_capa, ost_body_only);
+EXPORT_SYMBOL(RQF_OST_PUNCH);
+
+struct req_format RQF_OST_SYNC =
+ DEFINE_REQ_FMT0("OST_SYNC", ost_body_capa, ost_body_only);
+EXPORT_SYMBOL(RQF_OST_SYNC);
+
+struct req_format RQF_OST_DESTROY =
+ DEFINE_REQ_FMT0("OST_DESTROY", ost_destroy_client, ost_body_only);
+EXPORT_SYMBOL(RQF_OST_DESTROY);
+
+struct req_format RQF_OST_BRW_READ =
+ DEFINE_REQ_FMT0("OST_BRW_READ", ost_brw_client, ost_brw_read_server);
+EXPORT_SYMBOL(RQF_OST_BRW_READ);
+
+struct req_format RQF_OST_BRW_WRITE =
+ DEFINE_REQ_FMT0("OST_BRW_WRITE", ost_brw_client, ost_brw_write_server);
+EXPORT_SYMBOL(RQF_OST_BRW_WRITE);
+
+struct req_format RQF_OST_STATFS =
+ DEFINE_REQ_FMT0("OST_STATFS", empty, obd_statfs_server);
+EXPORT_SYMBOL(RQF_OST_STATFS);
+
+struct req_format RQF_OST_SET_GRANT_INFO =
+ DEFINE_REQ_FMT0("OST_SET_GRANT_INFO", ost_grant_shrink_client,
+ ost_body_only);
+EXPORT_SYMBOL(RQF_OST_SET_GRANT_INFO);
+
+struct req_format RQF_OST_GET_INFO_GENERIC =
+ DEFINE_REQ_FMT0("OST_GET_INFO", ost_get_info_generic_client,
+ ost_get_info_generic_server);
+EXPORT_SYMBOL(RQF_OST_GET_INFO_GENERIC);
+
+struct req_format RQF_OST_GET_INFO_LAST_ID =
+ DEFINE_REQ_FMT0("OST_GET_INFO_LAST_ID", ost_get_info_generic_client,
+ ost_get_last_id_server);
+EXPORT_SYMBOL(RQF_OST_GET_INFO_LAST_ID);
+
+struct req_format RQF_OST_GET_INFO_LAST_FID =
+ DEFINE_REQ_FMT0("OST_GET_INFO_LAST_FID", obd_set_info_client,
+ ost_get_last_fid_server);
+EXPORT_SYMBOL(RQF_OST_GET_INFO_LAST_FID);
+
+struct req_format RQF_OST_SET_INFO_LAST_FID =
+ DEFINE_REQ_FMT0("OST_SET_INFO_LAST_FID", obd_set_info_client,
+ empty);
+EXPORT_SYMBOL(RQF_OST_SET_INFO_LAST_FID);
+
+struct req_format RQF_OST_GET_INFO_FIEMAP =
+ DEFINE_REQ_FMT0("OST_GET_INFO_FIEMAP", ost_get_fiemap_client,
+ ost_get_fiemap_server);
+EXPORT_SYMBOL(RQF_OST_GET_INFO_FIEMAP);
+
+#if !defined(__REQ_LAYOUT_USER__)
+
+/* Convenience macro */
+#define FMT_FIELD(fmt, i, j) (fmt)->rf_fields[(i)].d[(j)]
+
+/**
+ * Initializes the capsule abstraction by computing and setting the \a rf_idx
+ * field of RQFs and the \a rmf_offset field of RMFs.
+ */
+int req_layout_init(void)
+{
+ int i;
+ int j;
+ int k;
+ struct req_format *rf = NULL;
+
+ for (i = 0; i < ARRAY_SIZE(req_formats); ++i) {
+ rf = req_formats[i];
+ rf->rf_idx = i;
+ for (j = 0; j < RCL_NR; ++j) {
+ LASSERT(rf->rf_fields[j].nr <= REQ_MAX_FIELD_NR);
+ for (k = 0; k < rf->rf_fields[j].nr; ++k) {
+ struct req_msg_field *field;
+
+ field = (typeof(field))rf->rf_fields[j].d[k];
+ LASSERT(!(field->rmf_flags & RMF_F_STRUCT_ARRAY)
+ || field->rmf_size > 0);
+ LASSERT(field->rmf_offset[i][j] == 0);
+ /*
+ * k + 1 to detect unused format/field
+ * combinations.
+ */
+ field->rmf_offset[i][j] = k + 1;
+ }
+ }
+ }
+ return 0;
+}
+EXPORT_SYMBOL(req_layout_init);
+
+void req_layout_fini(void)
+{
+}
+EXPORT_SYMBOL(req_layout_fini);
+
+/**
+ * Initializes the expected sizes of each RMF in a \a pill (\a rc_area) to -1.
+ *
+ * Actual/expected field sizes are set elsewhere in functions in this file:
+ * req_capsule_init(), req_capsule_server_pack(), req_capsule_set_size() and
+ * req_capsule_msg_size(). The \a rc_area information is used by.
+ * ptlrpc_request_set_replen().
+ */
+void req_capsule_init_area(struct req_capsule *pill)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(pill->rc_area[RCL_CLIENT]); i++) {
+ pill->rc_area[RCL_CLIENT][i] = -1;
+ pill->rc_area[RCL_SERVER][i] = -1;
+ }
+}
+EXPORT_SYMBOL(req_capsule_init_area);
+
+/**
+ * Initialize a pill.
+ *
+ * The \a location indicates whether the caller is executing on the client side
+ * (RCL_CLIENT) or server side (RCL_SERVER)..
+ */
+void req_capsule_init(struct req_capsule *pill,
+ struct ptlrpc_request *req,
+ enum req_location location)
+{
+ LASSERT(location == RCL_SERVER || location == RCL_CLIENT);
+
+ /*
+ * Today all capsules are embedded in ptlrpc_request structs,
+ * but just in case that ever isn't the case, we don't reach
+ * into req unless req != NULL and pill is the one embedded in
+ * the req.
+ *
+ * The req->rq_pill_init flag makes it safe to initialize a pill
+ * twice, which might happen in the OST paths as a result of the
+ * high-priority RPC queue getting peeked at before ost_handle()
+ * handles an OST RPC.
+ */
+ if (req != NULL && pill == &req->rq_pill && req->rq_pill_init)
+ return;
+
+ memset(pill, 0, sizeof(*pill));
+ pill->rc_req = req;
+ pill->rc_loc = location;
+ req_capsule_init_area(pill);
+
+ if (req != NULL && pill == &req->rq_pill)
+ req->rq_pill_init = 1;
+}
+EXPORT_SYMBOL(req_capsule_init);
+
+void req_capsule_fini(struct req_capsule *pill)
+{
+}
+EXPORT_SYMBOL(req_capsule_fini);
+
+static int __req_format_is_sane(const struct req_format *fmt)
+{
+ return
+ 0 <= fmt->rf_idx && fmt->rf_idx < ARRAY_SIZE(req_formats) &&
+ req_formats[fmt->rf_idx] == fmt;
+}
+
+static struct lustre_msg *__req_msg(const struct req_capsule *pill,
+ enum req_location loc)
+{
+ struct ptlrpc_request *req;
+
+ req = pill->rc_req;
+ return loc == RCL_CLIENT ? req->rq_reqmsg : req->rq_repmsg;
+}
+
+/**
+ * Set the format (\a fmt) of a \a pill; format changes are not allowed here
+ * (see req_capsule_extend()).
+ */
+void req_capsule_set(struct req_capsule *pill, const struct req_format *fmt)
+{
+ LASSERT(pill->rc_fmt == NULL || pill->rc_fmt == fmt);
+ LASSERT(__req_format_is_sane(fmt));
+
+ pill->rc_fmt = fmt;
+}
+EXPORT_SYMBOL(req_capsule_set);
+
+/**
+ * Fills in any parts of the \a rc_area of a \a pill that haven't been filled in
+ * yet.
+
+ * \a rc_area is an array of REQ_MAX_FIELD_NR elements, used to store sizes of
+ * variable-sized fields. The field sizes come from the declared \a rmf_size
+ * field of a \a pill's \a rc_fmt's RMF's.
+ */
+int req_capsule_filled_sizes(struct req_capsule *pill,
+ enum req_location loc)
+{
+ const struct req_format *fmt = pill->rc_fmt;
+ int i;
+
+ LASSERT(fmt != NULL);
+
+ for (i = 0; i < fmt->rf_fields[loc].nr; ++i) {
+ if (pill->rc_area[loc][i] == -1) {
+ pill->rc_area[loc][i] =
+ fmt->rf_fields[loc].d[i]->rmf_size;
+ if (pill->rc_area[loc][i] == -1) {
+ /*
+ * Skip the following fields.
+ *
+ * If this LASSERT() trips then you're missing a
+ * call to req_capsule_set_size().
+ */
+ LASSERT(loc != RCL_SERVER);
+ break;
+ }
+ }
+ }
+ return i;
+}
+EXPORT_SYMBOL(req_capsule_filled_sizes);
+
+/**
+ * Capsule equivalent of lustre_pack_request() and lustre_pack_reply().
+ *
+ * This function uses the \a pill's \a rc_area as filled in by
+ * req_capsule_set_size() or req_capsule_filled_sizes() (the latter is called by
+ * this function).
+ */
+int req_capsule_server_pack(struct req_capsule *pill)
+{
+ const struct req_format *fmt;
+ int count;
+ int rc;
+
+ LASSERT(pill->rc_loc == RCL_SERVER);
+ fmt = pill->rc_fmt;
+ LASSERT(fmt != NULL);
+
+ count = req_capsule_filled_sizes(pill, RCL_SERVER);
+ rc = lustre_pack_reply(pill->rc_req, count,
+ pill->rc_area[RCL_SERVER], NULL);
+ if (rc != 0) {
+ DEBUG_REQ(D_ERROR, pill->rc_req,
+ "Cannot pack %d fields in format `%s': ",
+ count, fmt->rf_name);
+ }
+ return rc;
+}
+EXPORT_SYMBOL(req_capsule_server_pack);
+
+/**
+ * Returns the PTLRPC request or reply (\a loc) buffer offset of a \a pill
+ * corresponding to the given RMF (\a field).
+ */
+static int __req_capsule_offset(const struct req_capsule *pill,
+ const struct req_msg_field *field,
+ enum req_location loc)
+{
+ int offset;
+
+ offset = field->rmf_offset[pill->rc_fmt->rf_idx][loc];
+ LASSERTF(offset > 0, "%s:%s, off=%d, loc=%d\n",
+ pill->rc_fmt->rf_name,
+ field->rmf_name, offset, loc);
+ offset--;
+
+ LASSERT(0 <= offset && offset < REQ_MAX_FIELD_NR);
+ return offset;
+}
+
+/**
+ * Helper for __req_capsule_get(); swabs value / array of values and/or dumps
+ * them if desired.
+ */
+static
+void
+swabber_dumper_helper(struct req_capsule *pill,
+ const struct req_msg_field *field,
+ enum req_location loc,
+ int offset,
+ void *value, int len, int dump, void (*swabber)(void *))
+{
+ void *p;
+ int i;
+ int n;
+ int do_swab;
+ int inout = loc == RCL_CLIENT;
+
+ swabber = swabber ?: field->rmf_swabber;
+
+ if (ptlrpc_buf_need_swab(pill->rc_req, inout, offset) &&
+ swabber != NULL && value != NULL)
+ do_swab = 1;
+ else
+ do_swab = 0;
+
+ if (!field->rmf_dumper)
+ dump = 0;
+
+ if (!(field->rmf_flags & RMF_F_STRUCT_ARRAY)) {
+ if (dump) {
+ CDEBUG(D_RPCTRACE, "Dump of %sfield %s follows\n",
+ do_swab ? "unswabbed " : "", field->rmf_name);
+ field->rmf_dumper(value);
+ }
+ if (!do_swab)
+ return;
+ swabber(value);
+ ptlrpc_buf_set_swabbed(pill->rc_req, inout, offset);
+ if (dump) {
+ CDEBUG(D_RPCTRACE, "Dump of swabbed field %s follows\n",
+ field->rmf_name);
+ field->rmf_dumper(value);
+ }
+
+ return;
+ }
+
+ /*
+ * We're swabbing an array; swabber() swabs a single array element, so
+ * swab every element.
+ */
+ LASSERT((len % field->rmf_size) == 0);
+ for (p = value, i = 0, n = len / field->rmf_size;
+ i < n;
+ i++, p += field->rmf_size) {
+ if (dump) {
+ CDEBUG(D_RPCTRACE, "Dump of %sarray field %s, element %d follows\n",
+ do_swab ? "unswabbed " : "", field->rmf_name, i);
+ field->rmf_dumper(p);
+ }
+ if (!do_swab)
+ continue;
+ swabber(p);
+ if (dump) {
+ CDEBUG(D_RPCTRACE, "Dump of swabbed array field %s, element %d follows\n",
+ field->rmf_name, i);
+ field->rmf_dumper(value);
+ }
+ }
+ if (do_swab)
+ ptlrpc_buf_set_swabbed(pill->rc_req, inout, offset);
+}
+
+/**
+ * Returns the pointer to a PTLRPC request or reply (\a loc) buffer of a \a pill
+ * corresponding to the given RMF (\a field).
+ *
+ * The buffer will be swabbed using the given \a swabber. If \a swabber == NULL
+ * then the \a rmf_swabber from the RMF will be used. Soon there will be no
+ * calls to __req_capsule_get() with a non-NULL \a swabber; \a swabber will then
+ * be removed. Fields with the \a RMF_F_STRUCT_ARRAY flag set will have each
+ * element of the array swabbed.
+ */
+static void *__req_capsule_get(struct req_capsule *pill,
+ const struct req_msg_field *field,
+ enum req_location loc,
+ void (*swabber)(void *),
+ int dump)
+{
+ const struct req_format *fmt;
+ struct lustre_msg *msg;
+ void *value;
+ int len;
+ int offset;
+
+ void *(*getter)(struct lustre_msg *m, int n, int minlen);
+
+ static const char *rcl_names[RCL_NR] = {
+ [RCL_CLIENT] = "client",
+ [RCL_SERVER] = "server"
+ };
+
+ LASSERT(pill != NULL);
+ LASSERT(pill != LP_POISON);
+ fmt = pill->rc_fmt;
+ LASSERT(fmt != NULL);
+ LASSERT(fmt != LP_POISON);
+ LASSERT(__req_format_is_sane(fmt));
+
+ offset = __req_capsule_offset(pill, field, loc);
+
+ msg = __req_msg(pill, loc);
+ LASSERT(msg != NULL);
+
+ getter = (field->rmf_flags & RMF_F_STRING) ?
+ (typeof(getter))lustre_msg_string : lustre_msg_buf;
+
+ if (field->rmf_flags & RMF_F_STRUCT_ARRAY) {
+ /*
+ * We've already asserted that field->rmf_size > 0 in
+ * req_layout_init().
+ */
+ len = lustre_msg_buflen(msg, offset);
+ if ((len % field->rmf_size) != 0) {
+ CERROR("%s: array field size mismatch %d modulo %d != 0 (%d)\n",
+ field->rmf_name, len, field->rmf_size, loc);
+ return NULL;
+ }
+ } else if (pill->rc_area[loc][offset] != -1) {
+ len = pill->rc_area[loc][offset];
+ } else {
+ len = max(field->rmf_size, 0);
+ }
+ value = getter(msg, offset, len);
+
+ if (value == NULL) {
+ DEBUG_REQ(D_ERROR, pill->rc_req,
+ "Wrong buffer for field `%s' (%d of %d) in format `%s': %d vs. %d (%s)\n",
+ field->rmf_name, offset, lustre_msg_bufcount(msg),
+ fmt->rf_name, lustre_msg_buflen(msg, offset), len,
+ rcl_names[loc]);
+ } else {
+ swabber_dumper_helper(pill, field, loc, offset, value, len,
+ dump, swabber);
+ }
+
+ return value;
+}
+
+/**
+ * Dump a request and/or reply
+ */
+static void __req_capsule_dump(struct req_capsule *pill, enum req_location loc)
+{
+ const struct req_format *fmt;
+ const struct req_msg_field *field;
+ int len;
+ int i;
+
+ fmt = pill->rc_fmt;
+
+ DEBUG_REQ(D_RPCTRACE, pill->rc_req, "BEGIN REQ CAPSULE DUMP\n");
+ for (i = 0; i < fmt->rf_fields[loc].nr; ++i) {
+ field = FMT_FIELD(fmt, loc, i);
+ if (field->rmf_dumper == NULL) {
+ /*
+ * FIXME Add a default hex dumper for fields that don't
+ * have a specific dumper
+ */
+ len = req_capsule_get_size(pill, field, loc);
+ CDEBUG(D_RPCTRACE, "Field %s has no dumper function; field size is %d\n",
+ field->rmf_name, len);
+ } else {
+ /* It's the dumping side-effect that we're interested in */
+ (void) __req_capsule_get(pill, field, loc, NULL, 1);
+ }
+ }
+ CDEBUG(D_RPCTRACE, "END REQ CAPSULE DUMP\n");
+}
+
+/**
+ * Dump a request.
+ */
+void req_capsule_client_dump(struct req_capsule *pill)
+{
+ __req_capsule_dump(pill, RCL_CLIENT);
+}
+EXPORT_SYMBOL(req_capsule_client_dump);
+
+/**
+ * Dump a reply
+ */
+void req_capsule_server_dump(struct req_capsule *pill)
+{
+ __req_capsule_dump(pill, RCL_SERVER);
+}
+EXPORT_SYMBOL(req_capsule_server_dump);
+
+/**
+ * Trivial wrapper around __req_capsule_get(), that returns the PTLRPC request
+ * buffer corresponding to the given RMF (\a field) of a \a pill.
+ */
+void *req_capsule_client_get(struct req_capsule *pill,
+ const struct req_msg_field *field)
+{
+ return __req_capsule_get(pill, field, RCL_CLIENT, NULL, 0);
+}
+EXPORT_SYMBOL(req_capsule_client_get);
+
+/**
+ * Same as req_capsule_client_get(), but with a \a swabber argument.
+ *
+ * Currently unused; will be removed when req_capsule_server_swab_get() is
+ * unused too.
+ */
+void *req_capsule_client_swab_get(struct req_capsule *pill,
+ const struct req_msg_field *field,
+ void *swabber)
+{
+ return __req_capsule_get(pill, field, RCL_CLIENT, swabber, 0);
+}
+EXPORT_SYMBOL(req_capsule_client_swab_get);
+
+/**
+ * Utility that combines req_capsule_set_size() and req_capsule_client_get().
+ *
+ * First the \a pill's request \a field's size is set (\a rc_area) using
+ * req_capsule_set_size() with the given \a len. Then the actual buffer is
+ * returned.
+ */
+void *req_capsule_client_sized_get(struct req_capsule *pill,
+ const struct req_msg_field *field,
+ int len)
+{
+ req_capsule_set_size(pill, field, RCL_CLIENT, len);
+ return __req_capsule_get(pill, field, RCL_CLIENT, NULL, 0);
+}
+EXPORT_SYMBOL(req_capsule_client_sized_get);
+
+/**
+ * Trivial wrapper around __req_capsule_get(), that returns the PTLRPC reply
+ * buffer corresponding to the given RMF (\a field) of a \a pill.
+ */
+void *req_capsule_server_get(struct req_capsule *pill,
+ const struct req_msg_field *field)
+{
+ return __req_capsule_get(pill, field, RCL_SERVER, NULL, 0);
+}
+EXPORT_SYMBOL(req_capsule_server_get);
+
+/**
+ * Same as req_capsule_server_get(), but with a \a swabber argument.
+ *
+ * Ideally all swabbing should be done pursuant to RMF definitions, with no
+ * swabbing done outside this capsule abstraction.
+ */
+void *req_capsule_server_swab_get(struct req_capsule *pill,
+ const struct req_msg_field *field,
+ void *swabber)
+{
+ return __req_capsule_get(pill, field, RCL_SERVER, swabber, 0);
+}
+EXPORT_SYMBOL(req_capsule_server_swab_get);
+
+/**
+ * Utility that combines req_capsule_set_size() and req_capsule_server_get().
+ *
+ * First the \a pill's request \a field's size is set (\a rc_area) using
+ * req_capsule_set_size() with the given \a len. Then the actual buffer is
+ * returned.
+ */
+void *req_capsule_server_sized_get(struct req_capsule *pill,
+ const struct req_msg_field *field,
+ int len)
+{
+ req_capsule_set_size(pill, field, RCL_SERVER, len);
+ return __req_capsule_get(pill, field, RCL_SERVER, NULL, 0);
+}
+EXPORT_SYMBOL(req_capsule_server_sized_get);
+
+void *req_capsule_server_sized_swab_get(struct req_capsule *pill,
+ const struct req_msg_field *field,
+ int len, void *swabber)
+{
+ req_capsule_set_size(pill, field, RCL_SERVER, len);
+ return __req_capsule_get(pill, field, RCL_SERVER, swabber, 0);
+}
+EXPORT_SYMBOL(req_capsule_server_sized_swab_get);
+
+/**
+ * Returns the buffer of a \a pill corresponding to the given \a field from the
+ * request (if the caller is executing on the server-side) or reply (if the
+ * caller is executing on the client-side).
+ *
+ * This function convenient for use is code that could be executed on the
+ * client and server alike.
+ */
+const void *req_capsule_other_get(struct req_capsule *pill,
+ const struct req_msg_field *field)
+{
+ return __req_capsule_get(pill, field, pill->rc_loc ^ 1, NULL, 0);
+}
+EXPORT_SYMBOL(req_capsule_other_get);
+
+/**
+ * Set the size of the PTLRPC request/reply (\a loc) buffer for the given \a
+ * field of the given \a pill.
+ *
+ * This function must be used when constructing variable sized fields of a
+ * request or reply.
+ */
+void req_capsule_set_size(struct req_capsule *pill,
+ const struct req_msg_field *field,
+ enum req_location loc, int size)
+{
+ LASSERT(loc == RCL_SERVER || loc == RCL_CLIENT);
+
+ if ((size != field->rmf_size) &&
+ (field->rmf_size != -1) &&
+ !(field->rmf_flags & RMF_F_NO_SIZE_CHECK) &&
+ (size > 0)) {
+ if ((field->rmf_flags & RMF_F_STRUCT_ARRAY) &&
+ (size % field->rmf_size != 0)) {
+ CERROR("%s: array field size mismatch %d %% %d != 0 (%d)\n",
+ field->rmf_name, size, field->rmf_size, loc);
+ LBUG();
+ } else if (!(field->rmf_flags & RMF_F_STRUCT_ARRAY) &&
+ size < field->rmf_size) {
+ CERROR("%s: field size mismatch %d != %d (%d)\n",
+ field->rmf_name, size, field->rmf_size, loc);
+ LBUG();
+ }
+ }
+
+ pill->rc_area[loc][__req_capsule_offset(pill, field, loc)] = size;
+}
+EXPORT_SYMBOL(req_capsule_set_size);
+
+/**
+ * Return the actual PTLRPC buffer length of a request or reply (\a loc)
+ * for the given \a pill's given \a field.
+ *
+ * NB: this function doesn't correspond with req_capsule_set_size(), which
+ * actually sets the size in pill.rc_area[loc][offset], but this function
+ * returns the message buflen[offset], maybe we should use another name.
+ */
+int req_capsule_get_size(const struct req_capsule *pill,
+ const struct req_msg_field *field,
+ enum req_location loc)
+{
+ LASSERT(loc == RCL_SERVER || loc == RCL_CLIENT);
+
+ return lustre_msg_buflen(__req_msg(pill, loc),
+ __req_capsule_offset(pill, field, loc));
+}
+EXPORT_SYMBOL(req_capsule_get_size);
+
+/**
+ * Wrapper around lustre_msg_size() that returns the PTLRPC size needed for the
+ * given \a pill's request or reply (\a loc) given the field size recorded in
+ * the \a pill's rc_area.
+ *
+ * See also req_capsule_set_size().
+ */
+int req_capsule_msg_size(struct req_capsule *pill, enum req_location loc)
+{
+ return lustre_msg_size(pill->rc_req->rq_import->imp_msg_magic,
+ pill->rc_fmt->rf_fields[loc].nr,
+ pill->rc_area[loc]);
+}
+
+/**
+ * While req_capsule_msg_size() computes the size of a PTLRPC request or reply
+ * (\a loc) given a \a pill's \a rc_area, this function computes the size of a
+ * PTLRPC request or reply given only an RQF (\a fmt).
+ *
+ * This function should not be used for formats which contain variable size
+ * fields.
+ */
+int req_capsule_fmt_size(__u32 magic, const struct req_format *fmt,
+ enum req_location loc)
+{
+ int size, i = 0;
+
+ /*
+ * This function should probably LASSERT() that fmt has no fields with
+ * RMF_F_STRUCT_ARRAY in rmf_flags, since we can't know here how many
+ * elements in the array there will ultimately be, but then, we could
+ * assume that there will be at least one element, and that's just what
+ * we do.
+ */
+ size = lustre_msg_hdr_size(magic, fmt->rf_fields[loc].nr);
+ if (size < 0)
+ return size;
+
+ for (; i < fmt->rf_fields[loc].nr; ++i)
+ if (fmt->rf_fields[loc].d[i]->rmf_size != -1)
+ size += cfs_size_round(fmt->rf_fields[loc].d[i]->
+ rmf_size);
+ return size;
+}
+
+/**
+ * Changes the format of an RPC.
+ *
+ * The pill must already have been initialized, which means that it already has
+ * a request format. The new format \a fmt must be an extension of the pill's
+ * old format. Specifically: the new format must have as many request and reply
+ * fields as the old one, and all fields shared by the old and new format must
+ * be at least as large in the new format.
+ *
+ * The new format's fields may be of different "type" than the old format, but
+ * only for fields that are "opaque" blobs: fields which have a) have no
+ * \a rmf_swabber, b) \a rmf_flags == 0 or RMF_F_NO_SIZE_CHECK, and c) \a
+ * rmf_size == -1 or \a rmf_flags == RMF_F_NO_SIZE_CHECK. For example,
+ * OBD_SET_INFO has a key field and an opaque value field that gets interpreted
+ * according to the key field. When the value, according to the key, contains a
+ * structure (or array thereof) to be swabbed, the format should be changed to
+ * one where the value field has \a rmf_size/rmf_flags/rmf_swabber set
+ * accordingly.
+ */
+void req_capsule_extend(struct req_capsule *pill, const struct req_format *fmt)
+{
+ int i;
+ int j;
+
+ const struct req_format *old;
+
+ LASSERT(pill->rc_fmt != NULL);
+ LASSERT(__req_format_is_sane(fmt));
+
+ old = pill->rc_fmt;
+ /*
+ * Sanity checking...
+ */
+ for (i = 0; i < RCL_NR; ++i) {
+ LASSERT(fmt->rf_fields[i].nr >= old->rf_fields[i].nr);
+ for (j = 0; j < old->rf_fields[i].nr - 1; ++j) {
+ const struct req_msg_field *ofield = FMT_FIELD(old, i, j);
+
+ /* "opaque" fields can be transmogrified */
+ if (ofield->rmf_swabber == NULL &&
+ (ofield->rmf_flags & ~RMF_F_NO_SIZE_CHECK) == 0 &&
+ (ofield->rmf_size == -1 ||
+ ofield->rmf_flags == RMF_F_NO_SIZE_CHECK))
+ continue;
+ LASSERT(FMT_FIELD(fmt, i, j) == FMT_FIELD(old, i, j));
+ }
+ /*
+ * Last field in old format can be shorter than in new.
+ */
+ LASSERT(FMT_FIELD(fmt, i, j)->rmf_size >=
+ FMT_FIELD(old, i, j)->rmf_size);
+ }
+
+ pill->rc_fmt = fmt;
+}
+EXPORT_SYMBOL(req_capsule_extend);
+
+/**
+ * This function returns a non-zero value if the given \a field is present in
+ * the format (\a rc_fmt) of \a pill's PTLRPC request or reply (\a loc), else it
+ * returns 0.
+ */
+int req_capsule_has_field(const struct req_capsule *pill,
+ const struct req_msg_field *field,
+ enum req_location loc)
+{
+ LASSERT(loc == RCL_SERVER || loc == RCL_CLIENT);
+
+ return field->rmf_offset[pill->rc_fmt->rf_idx][loc];
+}
+EXPORT_SYMBOL(req_capsule_has_field);
+
+/**
+ * Returns a non-zero value if the given \a field is present in the given \a
+ * pill's PTLRPC request or reply (\a loc), else it returns 0.
+ */
+int req_capsule_field_present(const struct req_capsule *pill,
+ const struct req_msg_field *field,
+ enum req_location loc)
+{
+ int offset;
+
+ LASSERT(loc == RCL_SERVER || loc == RCL_CLIENT);
+ LASSERT(req_capsule_has_field(pill, field, loc));
+
+ offset = __req_capsule_offset(pill, field, loc);
+ return lustre_msg_bufcount(__req_msg(pill, loc)) > offset;
+}
+EXPORT_SYMBOL(req_capsule_field_present);
+
+/**
+ * This function shrinks the size of the _buffer_ of the \a pill's PTLRPC
+ * request or reply (\a loc).
+ *
+ * This is not the opposite of req_capsule_extend().
+ */
+void req_capsule_shrink(struct req_capsule *pill,
+ const struct req_msg_field *field,
+ unsigned int newlen,
+ enum req_location loc)
+{
+ const struct req_format *fmt;
+ struct lustre_msg *msg;
+ int len;
+ int offset;
+
+ fmt = pill->rc_fmt;
+ LASSERT(fmt != NULL);
+ LASSERT(__req_format_is_sane(fmt));
+ LASSERT(req_capsule_has_field(pill, field, loc));
+ LASSERT(req_capsule_field_present(pill, field, loc));
+
+ offset = __req_capsule_offset(pill, field, loc);
+
+ msg = __req_msg(pill, loc);
+ len = lustre_msg_buflen(msg, offset);
+ LASSERTF(newlen <= len, "%s:%s, oldlen=%d, newlen=%d\n",
+ fmt->rf_name, field->rmf_name, len, newlen);
+
+ if (loc == RCL_CLIENT)
+ pill->rc_req->rq_reqlen = lustre_shrink_msg(msg, offset, newlen,
+ 1);
+ else
+ pill->rc_req->rq_replen = lustre_shrink_msg(msg, offset, newlen,
+ 1);
+}
+EXPORT_SYMBOL(req_capsule_shrink);
+
+int req_capsule_server_grow(struct req_capsule *pill,
+ const struct req_msg_field *field,
+ unsigned int newlen)
+{
+ struct ptlrpc_reply_state *rs = pill->rc_req->rq_reply_state, *nrs;
+ char *from, *to;
+ int offset, len, rc;
+
+ LASSERT(pill->rc_fmt != NULL);
+ LASSERT(__req_format_is_sane(pill->rc_fmt));
+ LASSERT(req_capsule_has_field(pill, field, RCL_SERVER));
+ LASSERT(req_capsule_field_present(pill, field, RCL_SERVER));
+
+ len = req_capsule_get_size(pill, field, RCL_SERVER);
+ offset = __req_capsule_offset(pill, field, RCL_SERVER);
+ if (pill->rc_req->rq_repbuf_len >=
+ lustre_packed_msg_size(pill->rc_req->rq_repmsg) - len + newlen)
+ CERROR("Inplace repack might be done\n");
+
+ pill->rc_req->rq_reply_state = NULL;
+ req_capsule_set_size(pill, field, RCL_SERVER, newlen);
+ rc = req_capsule_server_pack(pill);
+ if (rc) {
+ /* put old rs back, the caller will decide what to do */
+ pill->rc_req->rq_reply_state = rs;
+ return rc;
+ }
+ nrs = pill->rc_req->rq_reply_state;
+ /* Now we need only buffers, copy first chunk */
+ to = lustre_msg_buf(nrs->rs_msg, 0, 0);
+ from = lustre_msg_buf(rs->rs_msg, 0, 0);
+ len = (char *)lustre_msg_buf(rs->rs_msg, offset, 0) - from;
+ memcpy(to, from, len);
+ /* check if we have tail and copy it too */
+ if (rs->rs_msg->lm_bufcount > offset + 1) {
+ to = lustre_msg_buf(nrs->rs_msg, offset + 1, 0);
+ from = lustre_msg_buf(rs->rs_msg, offset + 1, 0);
+ offset = rs->rs_msg->lm_bufcount - 1;
+ len = (char *)lustre_msg_buf(rs->rs_msg, offset, 0) +
+ cfs_size_round(rs->rs_msg->lm_buflens[offset]) - from;
+ memcpy(to, from, len);
+ }
+ /* drop old reply if everything is fine */
+ if (rs->rs_difficult) {
+ /* copy rs data */
+ int i;
+
+ nrs->rs_difficult = 1;
+ nrs->rs_no_ack = rs->rs_no_ack;
+ for (i = 0; i < rs->rs_nlocks; i++) {
+ nrs->rs_locks[i] = rs->rs_locks[i];
+ nrs->rs_modes[i] = rs->rs_modes[i];
+ nrs->rs_nlocks++;
+ }
+ rs->rs_nlocks = 0;
+ rs->rs_difficult = 0;
+ rs->rs_no_ack = 0;
+ }
+ ptlrpc_rs_decref(rs);
+ return 0;
+}
+EXPORT_SYMBOL(req_capsule_server_grow);
+/* __REQ_LAYOUT_USER__ */
+#endif
diff --git a/drivers/staging/lustre/lustre/ptlrpc/llog_client.c b/drivers/staging/lustre/lustre/ptlrpc/llog_client.c
new file mode 100644
index 000000000..e9baf5bbe
--- /dev/null
+++ b/drivers/staging/lustre/lustre/ptlrpc/llog_client.c
@@ -0,0 +1,366 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2012, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ *
+ * lustre/ptlrpc/llog_client.c
+ *
+ * remote api for llog - client side
+ *
+ * Author: Andreas Dilger <adilger@clusterfs.com>
+ */
+
+#define DEBUG_SUBSYSTEM S_LOG
+
+#include "../../include/linux/libcfs/libcfs.h"
+
+#include "../include/obd_class.h"
+#include "../include/lustre_log.h"
+#include "../include/lustre_net.h"
+#include <linux/list.h>
+
+#define LLOG_CLIENT_ENTRY(ctxt, imp) do { \
+ mutex_lock(&ctxt->loc_mutex); \
+ if (ctxt->loc_imp) { \
+ imp = class_import_get(ctxt->loc_imp); \
+ } else { \
+ CERROR("ctxt->loc_imp == NULL for context idx %d." \
+ "Unable to complete MDS/OSS recovery," \
+ "but I'll try again next time. Not fatal.\n", \
+ ctxt->loc_idx); \
+ imp = NULL; \
+ mutex_unlock(&ctxt->loc_mutex); \
+ return (-EINVAL); \
+ } \
+ mutex_unlock(&ctxt->loc_mutex); \
+} while (0)
+
+#define LLOG_CLIENT_EXIT(ctxt, imp) do { \
+ mutex_lock(&ctxt->loc_mutex); \
+ if (ctxt->loc_imp != imp) \
+ CWARN("loc_imp has changed from %p to %p\n", \
+ ctxt->loc_imp, imp); \
+ class_import_put(imp); \
+ mutex_unlock(&ctxt->loc_mutex); \
+} while (0)
+
+/* This is a callback from the llog_* functions.
+ * Assumes caller has already pushed us into the kernel context. */
+static int llog_client_open(const struct lu_env *env,
+ struct llog_handle *lgh, struct llog_logid *logid,
+ char *name, enum llog_open_param open_param)
+{
+ struct obd_import *imp;
+ struct llogd_body *body;
+ struct llog_ctxt *ctxt = lgh->lgh_ctxt;
+ struct ptlrpc_request *req = NULL;
+ int rc;
+
+ LLOG_CLIENT_ENTRY(ctxt, imp);
+
+ /* client cannot create llog */
+ LASSERTF(open_param != LLOG_OPEN_NEW, "%#x\n", open_param);
+ LASSERT(lgh);
+
+ req = ptlrpc_request_alloc(imp, &RQF_LLOG_ORIGIN_HANDLE_CREATE);
+ if (req == NULL) {
+ rc = -ENOMEM;
+ goto out;
+ }
+
+ if (name)
+ req_capsule_set_size(&req->rq_pill, &RMF_NAME, RCL_CLIENT,
+ strlen(name) + 1);
+
+ rc = ptlrpc_request_pack(req, LUSTRE_LOG_VERSION,
+ LLOG_ORIGIN_HANDLE_CREATE);
+ if (rc) {
+ ptlrpc_request_free(req);
+ req = NULL;
+ goto out;
+ }
+ ptlrpc_request_set_replen(req);
+
+ body = req_capsule_client_get(&req->rq_pill, &RMF_LLOGD_BODY);
+ if (logid)
+ body->lgd_logid = *logid;
+ body->lgd_ctxt_idx = ctxt->loc_idx - 1;
+
+ if (name) {
+ char *tmp;
+ tmp = req_capsule_client_sized_get(&req->rq_pill, &RMF_NAME,
+ strlen(name) + 1);
+ LASSERT(tmp);
+ strcpy(tmp, name);
+ }
+
+ rc = ptlrpc_queue_wait(req);
+ if (rc)
+ goto out;
+
+ body = req_capsule_server_get(&req->rq_pill, &RMF_LLOGD_BODY);
+ if (body == NULL) {
+ rc = -EFAULT;
+ goto out;
+ }
+
+ lgh->lgh_id = body->lgd_logid;
+ lgh->lgh_ctxt = ctxt;
+out:
+ LLOG_CLIENT_EXIT(ctxt, imp);
+ ptlrpc_req_finished(req);
+ return rc;
+}
+
+static int llog_client_destroy(const struct lu_env *env,
+ struct llog_handle *loghandle)
+{
+ struct obd_import *imp;
+ struct ptlrpc_request *req = NULL;
+ struct llogd_body *body;
+ int rc;
+
+ LLOG_CLIENT_ENTRY(loghandle->lgh_ctxt, imp);
+ req = ptlrpc_request_alloc_pack(imp, &RQF_LLOG_ORIGIN_HANDLE_DESTROY,
+ LUSTRE_LOG_VERSION,
+ LLOG_ORIGIN_HANDLE_DESTROY);
+ if (req == NULL) {
+ rc = -ENOMEM;
+ goto err_exit;
+ }
+
+ body = req_capsule_client_get(&req->rq_pill, &RMF_LLOGD_BODY);
+ body->lgd_logid = loghandle->lgh_id;
+ body->lgd_llh_flags = loghandle->lgh_hdr->llh_flags;
+
+ if (!(body->lgd_llh_flags & LLOG_F_IS_PLAIN))
+ CERROR("%s: wrong llog flags %x\n", imp->imp_obd->obd_name,
+ body->lgd_llh_flags);
+
+ ptlrpc_request_set_replen(req);
+ rc = ptlrpc_queue_wait(req);
+
+ ptlrpc_req_finished(req);
+err_exit:
+ LLOG_CLIENT_EXIT(loghandle->lgh_ctxt, imp);
+ return rc;
+}
+
+
+static int llog_client_next_block(const struct lu_env *env,
+ struct llog_handle *loghandle,
+ int *cur_idx, int next_idx,
+ __u64 *cur_offset, void *buf, int len)
+{
+ struct obd_import *imp;
+ struct ptlrpc_request *req = NULL;
+ struct llogd_body *body;
+ void *ptr;
+ int rc;
+
+ LLOG_CLIENT_ENTRY(loghandle->lgh_ctxt, imp);
+ req = ptlrpc_request_alloc_pack(imp, &RQF_LLOG_ORIGIN_HANDLE_NEXT_BLOCK,
+ LUSTRE_LOG_VERSION,
+ LLOG_ORIGIN_HANDLE_NEXT_BLOCK);
+ if (req == NULL) {
+ rc = -ENOMEM;
+ goto err_exit;
+ }
+
+ body = req_capsule_client_get(&req->rq_pill, &RMF_LLOGD_BODY);
+ body->lgd_logid = loghandle->lgh_id;
+ body->lgd_ctxt_idx = loghandle->lgh_ctxt->loc_idx - 1;
+ body->lgd_llh_flags = loghandle->lgh_hdr->llh_flags;
+ body->lgd_index = next_idx;
+ body->lgd_saved_index = *cur_idx;
+ body->lgd_len = len;
+ body->lgd_cur_offset = *cur_offset;
+
+ req_capsule_set_size(&req->rq_pill, &RMF_EADATA, RCL_SERVER, len);
+ ptlrpc_request_set_replen(req);
+ rc = ptlrpc_queue_wait(req);
+ if (rc)
+ goto out;
+
+ body = req_capsule_server_get(&req->rq_pill, &RMF_LLOGD_BODY);
+ if (body == NULL) {
+ rc = -EFAULT;
+ goto out;
+ }
+
+ /* The log records are swabbed as they are processed */
+ ptr = req_capsule_server_get(&req->rq_pill, &RMF_EADATA);
+ if (ptr == NULL) {
+ rc = -EFAULT;
+ goto out;
+ }
+
+ *cur_idx = body->lgd_saved_index;
+ *cur_offset = body->lgd_cur_offset;
+
+ memcpy(buf, ptr, len);
+out:
+ ptlrpc_req_finished(req);
+err_exit:
+ LLOG_CLIENT_EXIT(loghandle->lgh_ctxt, imp);
+ return rc;
+}
+
+static int llog_client_prev_block(const struct lu_env *env,
+ struct llog_handle *loghandle,
+ int prev_idx, void *buf, int len)
+{
+ struct obd_import *imp;
+ struct ptlrpc_request *req = NULL;
+ struct llogd_body *body;
+ void *ptr;
+ int rc;
+
+ LLOG_CLIENT_ENTRY(loghandle->lgh_ctxt, imp);
+ req = ptlrpc_request_alloc_pack(imp, &RQF_LLOG_ORIGIN_HANDLE_PREV_BLOCK,
+ LUSTRE_LOG_VERSION,
+ LLOG_ORIGIN_HANDLE_PREV_BLOCK);
+ if (req == NULL) {
+ rc = -ENOMEM;
+ goto err_exit;
+ }
+
+ body = req_capsule_client_get(&req->rq_pill, &RMF_LLOGD_BODY);
+ body->lgd_logid = loghandle->lgh_id;
+ body->lgd_ctxt_idx = loghandle->lgh_ctxt->loc_idx - 1;
+ body->lgd_llh_flags = loghandle->lgh_hdr->llh_flags;
+ body->lgd_index = prev_idx;
+ body->lgd_len = len;
+
+ req_capsule_set_size(&req->rq_pill, &RMF_EADATA, RCL_SERVER, len);
+ ptlrpc_request_set_replen(req);
+
+ rc = ptlrpc_queue_wait(req);
+ if (rc)
+ goto out;
+
+ body = req_capsule_server_get(&req->rq_pill, &RMF_LLOGD_BODY);
+ if (body == NULL) {
+ rc = -EFAULT;
+ goto out;
+ }
+
+ ptr = req_capsule_server_get(&req->rq_pill, &RMF_EADATA);
+ if (ptr == NULL) {
+ rc = -EFAULT;
+ goto out;
+ }
+
+ memcpy(buf, ptr, len);
+out:
+ ptlrpc_req_finished(req);
+err_exit:
+ LLOG_CLIENT_EXIT(loghandle->lgh_ctxt, imp);
+ return rc;
+}
+
+static int llog_client_read_header(const struct lu_env *env,
+ struct llog_handle *handle)
+{
+ struct obd_import *imp;
+ struct ptlrpc_request *req = NULL;
+ struct llogd_body *body;
+ struct llog_log_hdr *hdr;
+ struct llog_rec_hdr *llh_hdr;
+ int rc;
+
+ LLOG_CLIENT_ENTRY(handle->lgh_ctxt, imp);
+ req = ptlrpc_request_alloc_pack(imp, &RQF_LLOG_ORIGIN_HANDLE_READ_HEADER,
+ LUSTRE_LOG_VERSION,
+ LLOG_ORIGIN_HANDLE_READ_HEADER);
+ if (req == NULL) {
+ rc = -ENOMEM;
+ goto err_exit;
+ }
+
+ body = req_capsule_client_get(&req->rq_pill, &RMF_LLOGD_BODY);
+ body->lgd_logid = handle->lgh_id;
+ body->lgd_ctxt_idx = handle->lgh_ctxt->loc_idx - 1;
+ body->lgd_llh_flags = handle->lgh_hdr->llh_flags;
+
+ ptlrpc_request_set_replen(req);
+ rc = ptlrpc_queue_wait(req);
+ if (rc)
+ goto out;
+
+ hdr = req_capsule_server_get(&req->rq_pill, &RMF_LLOG_LOG_HDR);
+ if (hdr == NULL) {
+ rc = -EFAULT;
+ goto out;
+ }
+
+ memcpy(handle->lgh_hdr, hdr, sizeof(*hdr));
+ handle->lgh_last_idx = handle->lgh_hdr->llh_tail.lrt_index;
+
+ /* sanity checks */
+ llh_hdr = &handle->lgh_hdr->llh_hdr;
+ if (llh_hdr->lrh_type != LLOG_HDR_MAGIC) {
+ CERROR("bad log header magic: %#x (expecting %#x)\n",
+ llh_hdr->lrh_type, LLOG_HDR_MAGIC);
+ rc = -EIO;
+ } else if (llh_hdr->lrh_len != LLOG_CHUNK_SIZE) {
+ CERROR("incorrectly sized log header: %#x (expecting %#x)\n",
+ llh_hdr->lrh_len, LLOG_CHUNK_SIZE);
+ CERROR("you may need to re-run lconf --write_conf.\n");
+ rc = -EIO;
+ }
+out:
+ ptlrpc_req_finished(req);
+err_exit:
+ LLOG_CLIENT_EXIT(handle->lgh_ctxt, imp);
+ return rc;
+}
+
+static int llog_client_close(const struct lu_env *env,
+ struct llog_handle *handle)
+{
+ /* this doesn't call LLOG_ORIGIN_HANDLE_CLOSE because
+ the servers all close the file at the end of every
+ other LLOG_ RPC. */
+ return 0;
+}
+
+struct llog_operations llog_client_ops = {
+ .lop_next_block = llog_client_next_block,
+ .lop_prev_block = llog_client_prev_block,
+ .lop_read_header = llog_client_read_header,
+ .lop_open = llog_client_open,
+ .lop_destroy = llog_client_destroy,
+ .lop_close = llog_client_close,
+};
+EXPORT_SYMBOL(llog_client_ops);
diff --git a/drivers/staging/lustre/lustre/ptlrpc/llog_net.c b/drivers/staging/lustre/lustre/ptlrpc/llog_net.c
new file mode 100644
index 000000000..dac66f5b3
--- /dev/null
+++ b/drivers/staging/lustre/lustre/ptlrpc/llog_net.c
@@ -0,0 +1,72 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2012, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ *
+ * lustre/ptlrpc/llog_net.c
+ *
+ * OST<->MDS recovery logging infrastructure.
+ *
+ * Invariants in implementation:
+ * - we do not share logs among different OST<->MDS connections, so that
+ * if an OST or MDS fails it need only look at log(s) relevant to itself
+ *
+ * Author: Andreas Dilger <adilger@clusterfs.com>
+ */
+
+#define DEBUG_SUBSYSTEM S_LOG
+
+#include "../../include/linux/libcfs/libcfs.h"
+
+#include "../include/obd_class.h"
+#include "../include/lustre_log.h"
+#include <linux/list.h>
+
+int llog_initiator_connect(struct llog_ctxt *ctxt)
+{
+ struct obd_import *new_imp;
+
+ LASSERT(ctxt);
+ new_imp = ctxt->loc_obd->u.cli.cl_import;
+ LASSERTF(ctxt->loc_imp == NULL || ctxt->loc_imp == new_imp,
+ "%p - %p\n", ctxt->loc_imp, new_imp);
+ mutex_lock(&ctxt->loc_mutex);
+ if (ctxt->loc_imp != new_imp) {
+ if (ctxt->loc_imp)
+ class_import_put(ctxt->loc_imp);
+ ctxt->loc_imp = class_import_get(new_imp);
+ }
+ mutex_unlock(&ctxt->loc_mutex);
+ return 0;
+}
+EXPORT_SYMBOL(llog_initiator_connect);
diff --git a/drivers/staging/lustre/lustre/ptlrpc/lproc_ptlrpc.c b/drivers/staging/lustre/lustre/ptlrpc/lproc_ptlrpc.c
new file mode 100644
index 000000000..9533ab976
--- /dev/null
+++ b/drivers/staging/lustre/lustre/ptlrpc/lproc_ptlrpc.c
@@ -0,0 +1,1366 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2002, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2011, 2012, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ */
+#define DEBUG_SUBSYSTEM S_CLASS
+
+
+#include "../include/obd_support.h"
+#include "../include/obd.h"
+#include "../include/lprocfs_status.h"
+#include "../include/lustre/lustre_idl.h"
+#include "../include/lustre_net.h"
+#include "../include/obd_class.h"
+#include "ptlrpc_internal.h"
+
+
+static struct ll_rpc_opcode {
+ __u32 opcode;
+ const char *opname;
+} ll_rpc_opcode_table[LUSTRE_MAX_OPCODES] = {
+ { OST_REPLY, "ost_reply" },
+ { OST_GETATTR, "ost_getattr" },
+ { OST_SETATTR, "ost_setattr" },
+ { OST_READ, "ost_read" },
+ { OST_WRITE, "ost_write" },
+ { OST_CREATE , "ost_create" },
+ { OST_DESTROY, "ost_destroy" },
+ { OST_GET_INFO, "ost_get_info" },
+ { OST_CONNECT, "ost_connect" },
+ { OST_DISCONNECT, "ost_disconnect" },
+ { OST_PUNCH, "ost_punch" },
+ { OST_OPEN, "ost_open" },
+ { OST_CLOSE, "ost_close" },
+ { OST_STATFS, "ost_statfs" },
+ { 14, NULL }, /* formerly OST_SAN_READ */
+ { 15, NULL }, /* formerly OST_SAN_WRITE */
+ { OST_SYNC, "ost_sync" },
+ { OST_SET_INFO, "ost_set_info" },
+ { OST_QUOTACHECK, "ost_quotacheck" },
+ { OST_QUOTACTL, "ost_quotactl" },
+ { OST_QUOTA_ADJUST_QUNIT, "ost_quota_adjust_qunit" },
+ { MDS_GETATTR, "mds_getattr" },
+ { MDS_GETATTR_NAME, "mds_getattr_lock" },
+ { MDS_CLOSE, "mds_close" },
+ { MDS_REINT, "mds_reint" },
+ { MDS_READPAGE, "mds_readpage" },
+ { MDS_CONNECT, "mds_connect" },
+ { MDS_DISCONNECT, "mds_disconnect" },
+ { MDS_GETSTATUS, "mds_getstatus" },
+ { MDS_STATFS, "mds_statfs" },
+ { MDS_PIN, "mds_pin" },
+ { MDS_UNPIN, "mds_unpin" },
+ { MDS_SYNC, "mds_sync" },
+ { MDS_DONE_WRITING, "mds_done_writing" },
+ { MDS_SET_INFO, "mds_set_info" },
+ { MDS_QUOTACHECK, "mds_quotacheck" },
+ { MDS_QUOTACTL, "mds_quotactl" },
+ { MDS_GETXATTR, "mds_getxattr" },
+ { MDS_SETXATTR, "mds_setxattr" },
+ { MDS_WRITEPAGE, "mds_writepage" },
+ { MDS_IS_SUBDIR, "mds_is_subdir" },
+ { MDS_GET_INFO, "mds_get_info" },
+ { MDS_HSM_STATE_GET, "mds_hsm_state_get" },
+ { MDS_HSM_STATE_SET, "mds_hsm_state_set" },
+ { MDS_HSM_ACTION, "mds_hsm_action" },
+ { MDS_HSM_PROGRESS, "mds_hsm_progress" },
+ { MDS_HSM_REQUEST, "mds_hsm_request" },
+ { MDS_HSM_CT_REGISTER, "mds_hsm_ct_register" },
+ { MDS_HSM_CT_UNREGISTER, "mds_hsm_ct_unregister" },
+ { MDS_SWAP_LAYOUTS, "mds_swap_layouts" },
+ { LDLM_ENQUEUE, "ldlm_enqueue" },
+ { LDLM_CONVERT, "ldlm_convert" },
+ { LDLM_CANCEL, "ldlm_cancel" },
+ { LDLM_BL_CALLBACK, "ldlm_bl_callback" },
+ { LDLM_CP_CALLBACK, "ldlm_cp_callback" },
+ { LDLM_GL_CALLBACK, "ldlm_gl_callback" },
+ { LDLM_SET_INFO, "ldlm_set_info" },
+ { MGS_CONNECT, "mgs_connect" },
+ { MGS_DISCONNECT, "mgs_disconnect" },
+ { MGS_EXCEPTION, "mgs_exception" },
+ { MGS_TARGET_REG, "mgs_target_reg" },
+ { MGS_TARGET_DEL, "mgs_target_del" },
+ { MGS_SET_INFO, "mgs_set_info" },
+ { MGS_CONFIG_READ, "mgs_config_read" },
+ { OBD_PING, "obd_ping" },
+ { OBD_LOG_CANCEL, "llog_cancel" },
+ { OBD_QC_CALLBACK, "obd_quota_callback" },
+ { OBD_IDX_READ, "dt_index_read" },
+ { LLOG_ORIGIN_HANDLE_CREATE, "llog_origin_handle_open" },
+ { LLOG_ORIGIN_HANDLE_NEXT_BLOCK, "llog_origin_handle_next_block" },
+ { LLOG_ORIGIN_HANDLE_READ_HEADER, "llog_origin_handle_read_header" },
+ { LLOG_ORIGIN_HANDLE_WRITE_REC, "llog_origin_handle_write_rec" },
+ { LLOG_ORIGIN_HANDLE_CLOSE, "llog_origin_handle_close" },
+ { LLOG_ORIGIN_CONNECT, "llog_origin_connect" },
+ { LLOG_CATINFO, "llog_catinfo" },
+ { LLOG_ORIGIN_HANDLE_PREV_BLOCK, "llog_origin_handle_prev_block" },
+ { LLOG_ORIGIN_HANDLE_DESTROY, "llog_origin_handle_destroy" },
+ { QUOTA_DQACQ, "quota_acquire" },
+ { QUOTA_DQREL, "quota_release" },
+ { SEQ_QUERY, "seq_query" },
+ { SEC_CTX_INIT, "sec_ctx_init" },
+ { SEC_CTX_INIT_CONT, "sec_ctx_init_cont" },
+ { SEC_CTX_FINI, "sec_ctx_fini" },
+ { FLD_QUERY, "fld_query" },
+ { UPDATE_OBJ, "update_obj" },
+};
+
+static struct ll_eopcode {
+ __u32 opcode;
+ const char *opname;
+} ll_eopcode_table[EXTRA_LAST_OPC] = {
+ { LDLM_GLIMPSE_ENQUEUE, "ldlm_glimpse_enqueue" },
+ { LDLM_PLAIN_ENQUEUE, "ldlm_plain_enqueue" },
+ { LDLM_EXTENT_ENQUEUE, "ldlm_extent_enqueue" },
+ { LDLM_FLOCK_ENQUEUE, "ldlm_flock_enqueue" },
+ { LDLM_IBITS_ENQUEUE, "ldlm_ibits_enqueue" },
+ { MDS_REINT_SETATTR, "mds_reint_setattr" },
+ { MDS_REINT_CREATE, "mds_reint_create" },
+ { MDS_REINT_LINK, "mds_reint_link" },
+ { MDS_REINT_UNLINK, "mds_reint_unlink" },
+ { MDS_REINT_RENAME, "mds_reint_rename" },
+ { MDS_REINT_OPEN, "mds_reint_open" },
+ { MDS_REINT_SETXATTR, "mds_reint_setxattr" },
+ { BRW_READ_BYTES, "read_bytes" },
+ { BRW_WRITE_BYTES, "write_bytes" },
+};
+
+const char *ll_opcode2str(__u32 opcode)
+{
+ /* When one of the assertions below fail, chances are that:
+ * 1) A new opcode was added in include/lustre/lustre_idl.h,
+ * but is missing from the table above.
+ * or 2) The opcode space was renumbered or rearranged,
+ * and the opcode_offset() function in
+ * ptlrpc_internal.h needs to be modified.
+ */
+ __u32 offset = opcode_offset(opcode);
+ LASSERTF(offset < LUSTRE_MAX_OPCODES,
+ "offset %u >= LUSTRE_MAX_OPCODES %u\n",
+ offset, LUSTRE_MAX_OPCODES);
+ LASSERTF(ll_rpc_opcode_table[offset].opcode == opcode,
+ "ll_rpc_opcode_table[%u].opcode %u != opcode %u\n",
+ offset, ll_rpc_opcode_table[offset].opcode, opcode);
+ return ll_rpc_opcode_table[offset].opname;
+}
+
+static const char *ll_eopcode2str(__u32 opcode)
+{
+ LASSERT(ll_eopcode_table[opcode].opcode == opcode);
+ return ll_eopcode_table[opcode].opname;
+}
+
+#if defined(CONFIG_PROC_FS)
+static void ptlrpc_lprocfs_register(struct proc_dir_entry *root, char *dir,
+ char *name,
+ struct proc_dir_entry **procroot_ret,
+ struct lprocfs_stats **stats_ret)
+{
+ struct proc_dir_entry *svc_procroot;
+ struct lprocfs_stats *svc_stats;
+ int i, rc;
+ unsigned int svc_counter_config = LPROCFS_CNTR_AVGMINMAX |
+ LPROCFS_CNTR_STDDEV;
+
+ LASSERT(*procroot_ret == NULL);
+ LASSERT(*stats_ret == NULL);
+
+ svc_stats = lprocfs_alloc_stats(EXTRA_MAX_OPCODES+LUSTRE_MAX_OPCODES,
+ 0);
+ if (svc_stats == NULL)
+ return;
+
+ if (dir) {
+ svc_procroot = lprocfs_register(dir, root, NULL, NULL);
+ if (IS_ERR(svc_procroot)) {
+ lprocfs_free_stats(&svc_stats);
+ return;
+ }
+ } else {
+ svc_procroot = root;
+ }
+
+ lprocfs_counter_init(svc_stats, PTLRPC_REQWAIT_CNTR,
+ svc_counter_config, "req_waittime", "usec");
+ lprocfs_counter_init(svc_stats, PTLRPC_REQQDEPTH_CNTR,
+ svc_counter_config, "req_qdepth", "reqs");
+ lprocfs_counter_init(svc_stats, PTLRPC_REQACTIVE_CNTR,
+ svc_counter_config, "req_active", "reqs");
+ lprocfs_counter_init(svc_stats, PTLRPC_TIMEOUT,
+ svc_counter_config, "req_timeout", "sec");
+ lprocfs_counter_init(svc_stats, PTLRPC_REQBUF_AVAIL_CNTR,
+ svc_counter_config, "reqbuf_avail", "bufs");
+ for (i = 0; i < EXTRA_LAST_OPC; i++) {
+ char *units;
+
+ switch (i) {
+ case BRW_WRITE_BYTES:
+ case BRW_READ_BYTES:
+ units = "bytes";
+ break;
+ default:
+ units = "reqs";
+ break;
+ }
+ lprocfs_counter_init(svc_stats, PTLRPC_LAST_CNTR + i,
+ svc_counter_config,
+ ll_eopcode2str(i), units);
+ }
+ for (i = 0; i < LUSTRE_MAX_OPCODES; i++) {
+ __u32 opcode = ll_rpc_opcode_table[i].opcode;
+ lprocfs_counter_init(svc_stats,
+ EXTRA_MAX_OPCODES + i, svc_counter_config,
+ ll_opcode2str(opcode), "usec");
+ }
+
+ rc = lprocfs_register_stats(svc_procroot, name, svc_stats);
+ if (rc < 0) {
+ if (dir)
+ lprocfs_remove(&svc_procroot);
+ lprocfs_free_stats(&svc_stats);
+ } else {
+ if (dir)
+ *procroot_ret = svc_procroot;
+ *stats_ret = svc_stats;
+ }
+}
+
+static int
+ptlrpc_lprocfs_req_history_len_seq_show(struct seq_file *m, void *v)
+{
+ struct ptlrpc_service *svc = m->private;
+ struct ptlrpc_service_part *svcpt;
+ int total = 0;
+ int i;
+
+ ptlrpc_service_for_each_part(svcpt, i, svc)
+ total += svcpt->scp_hist_nrqbds;
+
+ seq_printf(m, "%d\n", total);
+ return 0;
+}
+LPROC_SEQ_FOPS_RO(ptlrpc_lprocfs_req_history_len);
+
+static int
+ptlrpc_lprocfs_req_history_max_seq_show(struct seq_file *m, void *n)
+{
+ struct ptlrpc_service *svc = m->private;
+ struct ptlrpc_service_part *svcpt;
+ int total = 0;
+ int i;
+
+ ptlrpc_service_for_each_part(svcpt, i, svc)
+ total += svc->srv_hist_nrqbds_cpt_max;
+
+ seq_printf(m, "%d\n", total);
+ return 0;
+}
+
+static ssize_t
+ptlrpc_lprocfs_req_history_max_seq_write(struct file *file,
+ const char __user *buffer,
+ size_t count, loff_t *off)
+{
+ struct ptlrpc_service *svc = ((struct seq_file *)file->private_data)->private;
+ int bufpages;
+ int val;
+ int rc;
+
+ rc = lprocfs_write_helper(buffer, count, &val);
+ if (rc < 0)
+ return rc;
+
+ if (val < 0)
+ return -ERANGE;
+
+ /* This sanity check is more of an insanity check; we can still
+ * hose a kernel by allowing the request history to grow too
+ * far. */
+ bufpages = (svc->srv_buf_size + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT;
+ if (val > totalram_pages / (2 * bufpages))
+ return -ERANGE;
+
+ spin_lock(&svc->srv_lock);
+
+ if (val == 0)
+ svc->srv_hist_nrqbds_cpt_max = 0;
+ else
+ svc->srv_hist_nrqbds_cpt_max = max(1, (val / svc->srv_ncpts));
+
+ spin_unlock(&svc->srv_lock);
+
+ return count;
+}
+LPROC_SEQ_FOPS(ptlrpc_lprocfs_req_history_max);
+
+static int
+ptlrpc_lprocfs_threads_min_seq_show(struct seq_file *m, void *n)
+{
+ struct ptlrpc_service *svc = m->private;
+
+ seq_printf(m, "%d\n", svc->srv_nthrs_cpt_init * svc->srv_ncpts);
+ return 0;
+}
+
+static ssize_t
+ptlrpc_lprocfs_threads_min_seq_write(struct file *file,
+ const char __user *buffer,
+ size_t count, loff_t *off)
+{
+ struct ptlrpc_service *svc = ((struct seq_file *)file->private_data)->private;
+ int val;
+ int rc = lprocfs_write_helper(buffer, count, &val);
+
+ if (rc < 0)
+ return rc;
+
+ if (val / svc->srv_ncpts < PTLRPC_NTHRS_INIT)
+ return -ERANGE;
+
+ spin_lock(&svc->srv_lock);
+ if (val > svc->srv_nthrs_cpt_limit * svc->srv_ncpts) {
+ spin_unlock(&svc->srv_lock);
+ return -ERANGE;
+ }
+
+ svc->srv_nthrs_cpt_init = val / svc->srv_ncpts;
+
+ spin_unlock(&svc->srv_lock);
+
+ return count;
+}
+LPROC_SEQ_FOPS(ptlrpc_lprocfs_threads_min);
+
+static int
+ptlrpc_lprocfs_threads_started_seq_show(struct seq_file *m, void *n)
+{
+ struct ptlrpc_service *svc = m->private;
+ struct ptlrpc_service_part *svcpt;
+ int total = 0;
+ int i;
+
+ ptlrpc_service_for_each_part(svcpt, i, svc)
+ total += svcpt->scp_nthrs_running;
+
+ seq_printf(m, "%d\n", total);
+ return 0;
+}
+LPROC_SEQ_FOPS_RO(ptlrpc_lprocfs_threads_started);
+
+static int
+ptlrpc_lprocfs_threads_max_seq_show(struct seq_file *m, void *n)
+{
+ struct ptlrpc_service *svc = m->private;
+
+ seq_printf(m, "%d\n", svc->srv_nthrs_cpt_limit * svc->srv_ncpts);
+ return 0;
+}
+
+static ssize_t
+ptlrpc_lprocfs_threads_max_seq_write(struct file *file,
+ const char __user *buffer,
+ size_t count, loff_t *off)
+{
+ struct ptlrpc_service *svc = ((struct seq_file *)file->private_data)->private;
+ int val;
+ int rc = lprocfs_write_helper(buffer, count, &val);
+
+ if (rc < 0)
+ return rc;
+
+ if (val / svc->srv_ncpts < PTLRPC_NTHRS_INIT)
+ return -ERANGE;
+
+ spin_lock(&svc->srv_lock);
+ if (val < svc->srv_nthrs_cpt_init * svc->srv_ncpts) {
+ spin_unlock(&svc->srv_lock);
+ return -ERANGE;
+ }
+
+ svc->srv_nthrs_cpt_limit = val / svc->srv_ncpts;
+
+ spin_unlock(&svc->srv_lock);
+
+ return count;
+}
+LPROC_SEQ_FOPS(ptlrpc_lprocfs_threads_max);
+
+/**
+ * \addtogoup nrs
+ * @{
+ */
+extern struct nrs_core nrs_core;
+
+/**
+ * Translates \e ptlrpc_nrs_pol_state values to human-readable strings.
+ *
+ * \param[in] state The policy state
+ */
+static const char *nrs_state2str(enum ptlrpc_nrs_pol_state state)
+{
+ switch (state) {
+ default:
+ LBUG();
+ case NRS_POL_STATE_INVALID:
+ return "invalid";
+ case NRS_POL_STATE_STOPPED:
+ return "stopped";
+ case NRS_POL_STATE_STOPPING:
+ return "stopping";
+ case NRS_POL_STATE_STARTING:
+ return "starting";
+ case NRS_POL_STATE_STARTED:
+ return "started";
+ }
+}
+
+/**
+ * Obtains status information for \a policy.
+ *
+ * Information is copied in \a info.
+ *
+ * \param[in] policy The policy
+ * \param[out] info Holds returned status information
+ */
+void nrs_policy_get_info_locked(struct ptlrpc_nrs_policy *policy,
+ struct ptlrpc_nrs_pol_info *info)
+{
+ LASSERT(policy != NULL);
+ LASSERT(info != NULL);
+ assert_spin_locked(&policy->pol_nrs->nrs_lock);
+
+ memcpy(info->pi_name, policy->pol_desc->pd_name, NRS_POL_NAME_MAX);
+
+ info->pi_fallback = !!(policy->pol_flags & PTLRPC_NRS_FL_FALLBACK);
+ info->pi_state = policy->pol_state;
+ /**
+ * XXX: These are accessed without holding
+ * ptlrpc_service_part::scp_req_lock.
+ */
+ info->pi_req_queued = policy->pol_req_queued;
+ info->pi_req_started = policy->pol_req_started;
+}
+
+/**
+ * Reads and prints policy status information for all policies of a PTLRPC
+ * service.
+ */
+static int ptlrpc_lprocfs_nrs_seq_show(struct seq_file *m, void *n)
+{
+ struct ptlrpc_service *svc = m->private;
+ struct ptlrpc_service_part *svcpt;
+ struct ptlrpc_nrs *nrs;
+ struct ptlrpc_nrs_policy *policy;
+ struct ptlrpc_nrs_pol_info *infos;
+ struct ptlrpc_nrs_pol_info tmp;
+ unsigned num_pols;
+ unsigned pol_idx = 0;
+ bool hp = false;
+ int i;
+ int rc = 0;
+
+ /**
+ * Serialize NRS core lprocfs operations with policy registration/
+ * unregistration.
+ */
+ mutex_lock(&nrs_core.nrs_mutex);
+
+ /**
+ * Use the first service partition's regular NRS head in order to obtain
+ * the number of policies registered with NRS heads of this service. All
+ * service partitions will have the same number of policies.
+ */
+ nrs = nrs_svcpt2nrs(svc->srv_parts[0], false);
+
+ spin_lock(&nrs->nrs_lock);
+ num_pols = svc->srv_parts[0]->scp_nrs_reg.nrs_num_pols;
+ spin_unlock(&nrs->nrs_lock);
+
+ OBD_ALLOC(infos, num_pols * sizeof(*infos));
+ if (infos == NULL) {
+ rc = -ENOMEM;
+ goto out;
+ }
+again:
+
+ ptlrpc_service_for_each_part(svcpt, i, svc) {
+ nrs = nrs_svcpt2nrs(svcpt, hp);
+ spin_lock(&nrs->nrs_lock);
+
+ pol_idx = 0;
+
+ list_for_each_entry(policy, &nrs->nrs_policy_list,
+ pol_list) {
+ LASSERT(pol_idx < num_pols);
+
+ nrs_policy_get_info_locked(policy, &tmp);
+ /**
+ * Copy values when handling the first service
+ * partition.
+ */
+ if (i == 0) {
+ memcpy(infos[pol_idx].pi_name, tmp.pi_name,
+ NRS_POL_NAME_MAX);
+ memcpy(&infos[pol_idx].pi_state, &tmp.pi_state,
+ sizeof(tmp.pi_state));
+ infos[pol_idx].pi_fallback = tmp.pi_fallback;
+ /**
+ * For the rest of the service partitions
+ * sanity-check the values we get.
+ */
+ } else {
+ LASSERT(strncmp(infos[pol_idx].pi_name,
+ tmp.pi_name,
+ NRS_POL_NAME_MAX) == 0);
+ /**
+ * Not asserting ptlrpc_nrs_pol_info::pi_state,
+ * because it may be different between
+ * instances of the same policy in different
+ * service partitions.
+ */
+ LASSERT(infos[pol_idx].pi_fallback ==
+ tmp.pi_fallback);
+ }
+
+ infos[pol_idx].pi_req_queued += tmp.pi_req_queued;
+ infos[pol_idx].pi_req_started += tmp.pi_req_started;
+
+ pol_idx++;
+ }
+ spin_unlock(&nrs->nrs_lock);
+ }
+
+ /**
+ * Policy status information output is in YAML format.
+ * For example:
+ *
+ * regular_requests:
+ * - name: fifo
+ * state: started
+ * fallback: yes
+ * queued: 0
+ * active: 0
+ *
+ * - name: crrn
+ * state: started
+ * fallback: no
+ * queued: 2015
+ * active: 384
+ *
+ * high_priority_requests:
+ * - name: fifo
+ * state: started
+ * fallback: yes
+ * queued: 0
+ * active: 2
+ *
+ * - name: crrn
+ * state: stopped
+ * fallback: no
+ * queued: 0
+ * active: 0
+ */
+ seq_printf(m, "%s\n",
+ !hp ? "\nregular_requests:" : "high_priority_requests:");
+
+ for (pol_idx = 0; pol_idx < num_pols; pol_idx++) {
+ seq_printf(m, " - name: %s\n"
+ " state: %s\n"
+ " fallback: %s\n"
+ " queued: %-20d\n"
+ " active: %-20d\n\n",
+ infos[pol_idx].pi_name,
+ nrs_state2str(infos[pol_idx].pi_state),
+ infos[pol_idx].pi_fallback ? "yes" : "no",
+ (int)infos[pol_idx].pi_req_queued,
+ (int)infos[pol_idx].pi_req_started);
+ }
+
+ if (!hp && nrs_svc_has_hp(svc)) {
+ memset(infos, 0, num_pols * sizeof(*infos));
+
+ /**
+ * Redo the processing for the service's HP NRS heads' policies.
+ */
+ hp = true;
+ goto again;
+ }
+
+out:
+ if (infos)
+ OBD_FREE(infos, num_pols * sizeof(*infos));
+
+ mutex_unlock(&nrs_core.nrs_mutex);
+
+ return rc;
+}
+
+/**
+ * The longest valid command string is the maximum policy name size, plus the
+ * length of the " reg" substring
+ */
+#define LPROCFS_NRS_WR_MAX_CMD (NRS_POL_NAME_MAX + sizeof(" reg") - 1)
+
+/**
+ * Starts and stops a given policy on a PTLRPC service.
+ *
+ * Commands consist of the policy name, followed by an optional [reg|hp] token;
+ * if the optional token is omitted, the operation is performed on both the
+ * regular and high-priority (if the service has one) NRS head.
+ */
+static ssize_t ptlrpc_lprocfs_nrs_seq_write(struct file *file,
+ const char __user *buffer,
+ size_t count, loff_t *off)
+{
+ struct ptlrpc_service *svc = ((struct seq_file *)file->private_data)->private;
+ enum ptlrpc_nrs_queue_type queue = PTLRPC_NRS_QUEUE_BOTH;
+ char *cmd;
+ char *cmd_copy = NULL;
+ char *token;
+ int rc = 0;
+
+ if (count >= LPROCFS_NRS_WR_MAX_CMD) {
+ rc = -EINVAL;
+ goto out;
+ }
+
+ OBD_ALLOC(cmd, LPROCFS_NRS_WR_MAX_CMD);
+ if (cmd == NULL) {
+ rc = -ENOMEM;
+ goto out;
+ }
+ /**
+ * strsep() modifies its argument, so keep a copy
+ */
+ cmd_copy = cmd;
+
+ if (copy_from_user(cmd, buffer, count)) {
+ rc = -EFAULT;
+ goto out;
+ }
+
+ cmd[count] = '\0';
+
+ token = strsep(&cmd, " ");
+
+ if (strlen(token) > NRS_POL_NAME_MAX - 1) {
+ rc = -EINVAL;
+ goto out;
+ }
+
+ /**
+ * No [reg|hp] token has been specified
+ */
+ if (cmd == NULL)
+ goto default_queue;
+
+ /**
+ * The second token is either NULL, or an optional [reg|hp] string
+ */
+ if (strcmp(cmd, "reg") == 0)
+ queue = PTLRPC_NRS_QUEUE_REG;
+ else if (strcmp(cmd, "hp") == 0)
+ queue = PTLRPC_NRS_QUEUE_HP;
+ else {
+ rc = -EINVAL;
+ goto out;
+ }
+
+default_queue:
+
+ if (queue == PTLRPC_NRS_QUEUE_HP && !nrs_svc_has_hp(svc)) {
+ rc = -ENODEV;
+ goto out;
+ } else if (queue == PTLRPC_NRS_QUEUE_BOTH && !nrs_svc_has_hp(svc))
+ queue = PTLRPC_NRS_QUEUE_REG;
+
+ /**
+ * Serialize NRS core lprocfs operations with policy registration/
+ * unregistration.
+ */
+ mutex_lock(&nrs_core.nrs_mutex);
+
+ rc = ptlrpc_nrs_policy_control(svc, queue, token, PTLRPC_NRS_CTL_START,
+ false, NULL);
+
+ mutex_unlock(&nrs_core.nrs_mutex);
+out:
+ if (cmd_copy)
+ OBD_FREE(cmd_copy, LPROCFS_NRS_WR_MAX_CMD);
+
+ return rc < 0 ? rc : count;
+}
+LPROC_SEQ_FOPS(ptlrpc_lprocfs_nrs);
+
+/** @} nrs */
+
+struct ptlrpc_srh_iterator {
+ int srhi_idx;
+ __u64 srhi_seq;
+ struct ptlrpc_request *srhi_req;
+};
+
+static int
+ptlrpc_lprocfs_svc_req_history_seek(struct ptlrpc_service_part *svcpt,
+ struct ptlrpc_srh_iterator *srhi,
+ __u64 seq)
+{
+ struct list_head *e;
+ struct ptlrpc_request *req;
+
+ if (srhi->srhi_req != NULL &&
+ srhi->srhi_seq > svcpt->scp_hist_seq_culled &&
+ srhi->srhi_seq <= seq) {
+ /* If srhi_req was set previously, hasn't been culled and
+ * we're searching for a seq on or after it (i.e. more
+ * recent), search from it onwards.
+ * Since the service history is LRU (i.e. culled reqs will
+ * be near the head), we shouldn't have to do long
+ * re-scans */
+ LASSERTF(srhi->srhi_seq == srhi->srhi_req->rq_history_seq,
+ "%s:%d: seek seq %llu, request seq %llu\n",
+ svcpt->scp_service->srv_name, svcpt->scp_cpt,
+ srhi->srhi_seq, srhi->srhi_req->rq_history_seq);
+ LASSERTF(!list_empty(&svcpt->scp_hist_reqs),
+ "%s:%d: seek offset %llu, request seq %llu, last culled %llu\n",
+ svcpt->scp_service->srv_name, svcpt->scp_cpt,
+ seq, srhi->srhi_seq, svcpt->scp_hist_seq_culled);
+ e = &srhi->srhi_req->rq_history_list;
+ } else {
+ /* search from start */
+ e = svcpt->scp_hist_reqs.next;
+ }
+
+ while (e != &svcpt->scp_hist_reqs) {
+ req = list_entry(e, struct ptlrpc_request, rq_history_list);
+
+ if (req->rq_history_seq >= seq) {
+ srhi->srhi_seq = req->rq_history_seq;
+ srhi->srhi_req = req;
+ return 0;
+ }
+ e = e->next;
+ }
+
+ return -ENOENT;
+}
+
+/*
+ * ptlrpc history sequence is used as "position" of seq_file, in some case,
+ * seq_read() will increase "position" to indicate reading the next
+ * element, however, low bits of history sequence are reserved for CPT id
+ * (check the details from comments before ptlrpc_req_add_history), which
+ * means seq_read() might change CPT id of history sequence and never
+ * finish reading of requests on a CPT. To make it work, we have to shift
+ * CPT id to high bits and timestamp to low bits, so seq_read() will only
+ * increase timestamp which can correctly indicate the next position.
+ */
+
+/* convert seq_file pos to cpt */
+#define PTLRPC_REQ_POS2CPT(svc, pos) \
+ ((svc)->srv_cpt_bits == 0 ? 0 : \
+ (__u64)(pos) >> (64 - (svc)->srv_cpt_bits))
+
+/* make up seq_file pos from cpt */
+#define PTLRPC_REQ_CPT2POS(svc, cpt) \
+ ((svc)->srv_cpt_bits == 0 ? 0 : \
+ (cpt) << (64 - (svc)->srv_cpt_bits))
+
+/* convert sequence to position */
+#define PTLRPC_REQ_SEQ2POS(svc, seq) \
+ ((svc)->srv_cpt_bits == 0 ? (seq) : \
+ ((seq) >> (svc)->srv_cpt_bits) | \
+ ((seq) << (64 - (svc)->srv_cpt_bits)))
+
+/* convert position to sequence */
+#define PTLRPC_REQ_POS2SEQ(svc, pos) \
+ ((svc)->srv_cpt_bits == 0 ? (pos) : \
+ ((__u64)(pos) << (svc)->srv_cpt_bits) | \
+ ((__u64)(pos) >> (64 - (svc)->srv_cpt_bits)))
+
+static void *
+ptlrpc_lprocfs_svc_req_history_start(struct seq_file *s, loff_t *pos)
+{
+ struct ptlrpc_service *svc = s->private;
+ struct ptlrpc_service_part *svcpt;
+ struct ptlrpc_srh_iterator *srhi;
+ unsigned int cpt;
+ int rc;
+ int i;
+
+ if (sizeof(loff_t) != sizeof(__u64)) { /* can't support */
+ CWARN("Failed to read request history because size of loff_t %d can't match size of u64\n",
+ (int)sizeof(loff_t));
+ return NULL;
+ }
+
+ OBD_ALLOC(srhi, sizeof(*srhi));
+ if (srhi == NULL)
+ return NULL;
+
+ srhi->srhi_seq = 0;
+ srhi->srhi_req = NULL;
+
+ cpt = PTLRPC_REQ_POS2CPT(svc, *pos);
+
+ ptlrpc_service_for_each_part(svcpt, i, svc) {
+ if (i < cpt) /* skip */
+ continue;
+ if (i > cpt) /* make up the lowest position for this CPT */
+ *pos = PTLRPC_REQ_CPT2POS(svc, i);
+
+ spin_lock(&svcpt->scp_lock);
+ rc = ptlrpc_lprocfs_svc_req_history_seek(svcpt, srhi,
+ PTLRPC_REQ_POS2SEQ(svc, *pos));
+ spin_unlock(&svcpt->scp_lock);
+ if (rc == 0) {
+ *pos = PTLRPC_REQ_SEQ2POS(svc, srhi->srhi_seq);
+ srhi->srhi_idx = i;
+ return srhi;
+ }
+ }
+
+ OBD_FREE(srhi, sizeof(*srhi));
+ return NULL;
+}
+
+static void
+ptlrpc_lprocfs_svc_req_history_stop(struct seq_file *s, void *iter)
+{
+ struct ptlrpc_srh_iterator *srhi = iter;
+
+ if (srhi != NULL)
+ OBD_FREE(srhi, sizeof(*srhi));
+}
+
+static void *
+ptlrpc_lprocfs_svc_req_history_next(struct seq_file *s,
+ void *iter, loff_t *pos)
+{
+ struct ptlrpc_service *svc = s->private;
+ struct ptlrpc_srh_iterator *srhi = iter;
+ struct ptlrpc_service_part *svcpt;
+ __u64 seq;
+ int rc;
+ int i;
+
+ for (i = srhi->srhi_idx; i < svc->srv_ncpts; i++) {
+ svcpt = svc->srv_parts[i];
+
+ if (i > srhi->srhi_idx) { /* reset iterator for a new CPT */
+ srhi->srhi_req = NULL;
+ seq = srhi->srhi_seq = 0;
+ } else { /* the next sequence */
+ seq = srhi->srhi_seq + (1 << svc->srv_cpt_bits);
+ }
+
+ spin_lock(&svcpt->scp_lock);
+ rc = ptlrpc_lprocfs_svc_req_history_seek(svcpt, srhi, seq);
+ spin_unlock(&svcpt->scp_lock);
+ if (rc == 0) {
+ *pos = PTLRPC_REQ_SEQ2POS(svc, srhi->srhi_seq);
+ srhi->srhi_idx = i;
+ return srhi;
+ }
+ }
+
+ OBD_FREE(srhi, sizeof(*srhi));
+ return NULL;
+}
+
+/* common ost/mdt so_req_printer */
+void target_print_req(void *seq_file, struct ptlrpc_request *req)
+{
+ /* Called holding srv_lock with irqs disabled.
+ * Print specific req contents and a newline.
+ * CAVEAT EMPTOR: check request message length before printing!!!
+ * You might have received any old crap so you must be just as
+ * careful here as the service's request parser!!! */
+ struct seq_file *sf = seq_file;
+
+ switch (req->rq_phase) {
+ case RQ_PHASE_NEW:
+ /* still awaiting a service thread's attention, or rejected
+ * because the generic request message didn't unpack */
+ seq_printf(sf, "<not swabbed>\n");
+ break;
+ case RQ_PHASE_INTERPRET:
+ /* being handled, so basic msg swabbed, and opc is valid
+ * but racing with mds_handle() */
+ case RQ_PHASE_COMPLETE:
+ /* been handled by mds_handle() reply state possibly still
+ * volatile */
+ seq_printf(sf, "opc %d\n", lustre_msg_get_opc(req->rq_reqmsg));
+ break;
+ default:
+ DEBUG_REQ(D_ERROR, req, "bad phase %d", req->rq_phase);
+ }
+}
+EXPORT_SYMBOL(target_print_req);
+
+static int ptlrpc_lprocfs_svc_req_history_show(struct seq_file *s, void *iter)
+{
+ struct ptlrpc_service *svc = s->private;
+ struct ptlrpc_srh_iterator *srhi = iter;
+ struct ptlrpc_service_part *svcpt;
+ struct ptlrpc_request *req;
+ int rc;
+
+ LASSERT(srhi->srhi_idx < svc->srv_ncpts);
+
+ svcpt = svc->srv_parts[srhi->srhi_idx];
+
+ spin_lock(&svcpt->scp_lock);
+
+ rc = ptlrpc_lprocfs_svc_req_history_seek(svcpt, srhi, srhi->srhi_seq);
+
+ if (rc == 0) {
+ req = srhi->srhi_req;
+
+ /* Print common req fields.
+ * CAVEAT EMPTOR: we're racing with the service handler
+ * here. The request could contain any old crap, so you
+ * must be just as careful as the service's request
+ * parser. Currently I only print stuff here I know is OK
+ * to look at coz it was set up in request_in_callback()!!! */
+ seq_printf(s, "%lld:%s:%s:x%llu:%d:%s:%ld:%lds(%+lds) ",
+ req->rq_history_seq, libcfs_nid2str(req->rq_self),
+ libcfs_id2str(req->rq_peer), req->rq_xid,
+ req->rq_reqlen, ptlrpc_rqphase2str(req),
+ req->rq_arrival_time.tv_sec,
+ req->rq_sent - req->rq_arrival_time.tv_sec,
+ req->rq_sent - req->rq_deadline);
+ if (svc->srv_ops.so_req_printer == NULL)
+ seq_printf(s, "\n");
+ else
+ svc->srv_ops.so_req_printer(s, srhi->srhi_req);
+ }
+
+ spin_unlock(&svcpt->scp_lock);
+ return rc;
+}
+
+static int
+ptlrpc_lprocfs_svc_req_history_open(struct inode *inode, struct file *file)
+{
+ static struct seq_operations sops = {
+ .start = ptlrpc_lprocfs_svc_req_history_start,
+ .stop = ptlrpc_lprocfs_svc_req_history_stop,
+ .next = ptlrpc_lprocfs_svc_req_history_next,
+ .show = ptlrpc_lprocfs_svc_req_history_show,
+ };
+ struct seq_file *seqf;
+ int rc;
+
+ rc = seq_open(file, &sops);
+ if (rc)
+ return rc;
+
+ seqf = file->private_data;
+ seqf->private = PDE_DATA(inode);
+ return 0;
+}
+
+/* See also lprocfs_rd_timeouts */
+static int ptlrpc_lprocfs_timeouts_seq_show(struct seq_file *m, void *n)
+{
+ struct ptlrpc_service *svc = m->private;
+ struct ptlrpc_service_part *svcpt;
+ struct dhms ts;
+ time_t worstt;
+ unsigned int cur;
+ unsigned int worst;
+ int i;
+
+ if (AT_OFF) {
+ seq_printf(m, "adaptive timeouts off, using obd_timeout %u\n",
+ obd_timeout);
+ return 0;
+ }
+
+ ptlrpc_service_for_each_part(svcpt, i, svc) {
+ cur = at_get(&svcpt->scp_at_estimate);
+ worst = svcpt->scp_at_estimate.at_worst_ever;
+ worstt = svcpt->scp_at_estimate.at_worst_time;
+ s2dhms(&ts, get_seconds() - worstt);
+
+ seq_printf(m, "%10s : cur %3u worst %3u (at %ld, "
+ DHMS_FMT" ago) ", "service",
+ cur, worst, worstt, DHMS_VARS(&ts));
+
+ lprocfs_at_hist_helper(m, &svcpt->scp_at_estimate);
+ }
+
+ return 0;
+}
+LPROC_SEQ_FOPS_RO(ptlrpc_lprocfs_timeouts);
+
+static int ptlrpc_lprocfs_hp_ratio_seq_show(struct seq_file *m, void *v)
+{
+ struct ptlrpc_service *svc = m->private;
+ seq_printf(m, "%d", svc->srv_hpreq_ratio);
+ return 0;
+}
+
+static ssize_t ptlrpc_lprocfs_hp_ratio_seq_write(struct file *file,
+ const char __user *buffer,
+ size_t count,
+ loff_t *off)
+{
+ struct ptlrpc_service *svc = ((struct seq_file *)file->private_data)->private;
+ int rc;
+ int val;
+
+ rc = lprocfs_write_helper(buffer, count, &val);
+ if (rc < 0)
+ return rc;
+
+ if (val < 0)
+ return -ERANGE;
+
+ spin_lock(&svc->srv_lock);
+ svc->srv_hpreq_ratio = val;
+ spin_unlock(&svc->srv_lock);
+
+ return count;
+}
+LPROC_SEQ_FOPS(ptlrpc_lprocfs_hp_ratio);
+
+void ptlrpc_lprocfs_register_service(struct proc_dir_entry *entry,
+ struct ptlrpc_service *svc)
+{
+ struct lprocfs_vars lproc_vars[] = {
+ {.name = "high_priority_ratio",
+ .fops = &ptlrpc_lprocfs_hp_ratio_fops,
+ .data = svc},
+ {.name = "req_buffer_history_len",
+ .fops = &ptlrpc_lprocfs_req_history_len_fops,
+ .data = svc},
+ {.name = "req_buffer_history_max",
+ .fops = &ptlrpc_lprocfs_req_history_max_fops,
+ .data = svc},
+ {.name = "threads_min",
+ .fops = &ptlrpc_lprocfs_threads_min_fops,
+ .data = svc},
+ {.name = "threads_max",
+ .fops = &ptlrpc_lprocfs_threads_max_fops,
+ .data = svc},
+ {.name = "threads_started",
+ .fops = &ptlrpc_lprocfs_threads_started_fops,
+ .data = svc},
+ {.name = "timeouts",
+ .fops = &ptlrpc_lprocfs_timeouts_fops,
+ .data = svc},
+ {.name = "nrs_policies",
+ .fops = &ptlrpc_lprocfs_nrs_fops,
+ .data = svc},
+ {NULL}
+ };
+ static const struct file_operations req_history_fops = {
+ .owner = THIS_MODULE,
+ .open = ptlrpc_lprocfs_svc_req_history_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = lprocfs_seq_release,
+ };
+
+ int rc;
+
+ ptlrpc_lprocfs_register(entry, svc->srv_name,
+ "stats", &svc->srv_procroot,
+ &svc->srv_stats);
+
+ if (svc->srv_procroot == NULL)
+ return;
+
+ lprocfs_add_vars(svc->srv_procroot, lproc_vars, NULL);
+
+ rc = lprocfs_seq_create(svc->srv_procroot, "req_history",
+ 0400, &req_history_fops, svc);
+ if (rc)
+ CWARN("Error adding the req_history file\n");
+}
+
+void ptlrpc_lprocfs_register_obd(struct obd_device *obddev)
+{
+ ptlrpc_lprocfs_register(obddev->obd_proc_entry, NULL, "stats",
+ &obddev->obd_svc_procroot,
+ &obddev->obd_svc_stats);
+}
+EXPORT_SYMBOL(ptlrpc_lprocfs_register_obd);
+
+void ptlrpc_lprocfs_rpc_sent(struct ptlrpc_request *req, long amount)
+{
+ struct lprocfs_stats *svc_stats;
+ __u32 op = lustre_msg_get_opc(req->rq_reqmsg);
+ int opc = opcode_offset(op);
+
+ svc_stats = req->rq_import->imp_obd->obd_svc_stats;
+ if (svc_stats == NULL || opc <= 0)
+ return;
+ LASSERT(opc < LUSTRE_MAX_OPCODES);
+ if (!(op == LDLM_ENQUEUE || op == MDS_REINT))
+ lprocfs_counter_add(svc_stats, opc + EXTRA_MAX_OPCODES, amount);
+}
+
+void ptlrpc_lprocfs_brw(struct ptlrpc_request *req, int bytes)
+{
+ struct lprocfs_stats *svc_stats;
+ int idx;
+
+ if (!req->rq_import)
+ return;
+ svc_stats = req->rq_import->imp_obd->obd_svc_stats;
+ if (!svc_stats)
+ return;
+ idx = lustre_msg_get_opc(req->rq_reqmsg);
+ switch (idx) {
+ case OST_READ:
+ idx = BRW_READ_BYTES + PTLRPC_LAST_CNTR;
+ break;
+ case OST_WRITE:
+ idx = BRW_WRITE_BYTES + PTLRPC_LAST_CNTR;
+ break;
+ default:
+ LASSERTF(0, "unsupported opcode %u\n", idx);
+ break;
+ }
+
+ lprocfs_counter_add(svc_stats, idx, bytes);
+}
+
+EXPORT_SYMBOL(ptlrpc_lprocfs_brw);
+
+void ptlrpc_lprocfs_unregister_service(struct ptlrpc_service *svc)
+{
+ if (svc->srv_procroot != NULL)
+ lprocfs_remove(&svc->srv_procroot);
+
+ if (svc->srv_stats)
+ lprocfs_free_stats(&svc->srv_stats);
+}
+
+void ptlrpc_lprocfs_unregister_obd(struct obd_device *obd)
+{
+ if (obd->obd_svc_procroot)
+ lprocfs_remove(&obd->obd_svc_procroot);
+
+ if (obd->obd_svc_stats)
+ lprocfs_free_stats(&obd->obd_svc_stats);
+}
+EXPORT_SYMBOL(ptlrpc_lprocfs_unregister_obd);
+
+
+#define BUFLEN (UUID_MAX + 5)
+
+int lprocfs_wr_evict_client(struct file *file, const char __user *buffer,
+ size_t count, loff_t *off)
+{
+ struct obd_device *obd = ((struct seq_file *)file->private_data)->private;
+ char *kbuf;
+ char *tmpbuf;
+
+ OBD_ALLOC(kbuf, BUFLEN);
+ if (kbuf == NULL)
+ return -ENOMEM;
+
+ /*
+ * OBD_ALLOC() will zero kbuf, but we only copy BUFLEN - 1
+ * bytes into kbuf, to ensure that the string is NUL-terminated.
+ * UUID_MAX should include a trailing NUL already.
+ */
+ if (copy_from_user(kbuf, buffer,
+ min_t(unsigned long, BUFLEN - 1, count))) {
+ count = -EFAULT;
+ goto out;
+ }
+ tmpbuf = cfs_firststr(kbuf, min_t(unsigned long, BUFLEN - 1, count));
+ /* Kludge code(deadlock situation): the lprocfs lock has been held
+ * since the client is evicted by writing client's
+ * uuid/nid to procfs "evict_client" entry. However,
+ * obd_export_evict_by_uuid() will call lprocfs_remove() to destroy
+ * the proc entries under the being destroyed export{}, so I have
+ * to drop the lock at first here.
+ * - jay, jxiong@clusterfs.com */
+ class_incref(obd, __func__, current);
+
+ if (strncmp(tmpbuf, "nid:", 4) == 0)
+ obd_export_evict_by_nid(obd, tmpbuf + 4);
+ else if (strncmp(tmpbuf, "uuid:", 5) == 0)
+ obd_export_evict_by_uuid(obd, tmpbuf + 5);
+ else
+ obd_export_evict_by_uuid(obd, tmpbuf);
+
+ class_decref(obd, __func__, current);
+
+out:
+ OBD_FREE(kbuf, BUFLEN);
+ return count;
+}
+EXPORT_SYMBOL(lprocfs_wr_evict_client);
+
+#undef BUFLEN
+
+int lprocfs_wr_ping(struct file *file, const char __user *buffer,
+ size_t count, loff_t *off)
+{
+ struct obd_device *obd = ((struct seq_file *)file->private_data)->private;
+ struct ptlrpc_request *req;
+ int rc;
+
+ LPROCFS_CLIMP_CHECK(obd);
+ req = ptlrpc_prep_ping(obd->u.cli.cl_import);
+ LPROCFS_CLIMP_EXIT(obd);
+ if (req == NULL)
+ return -ENOMEM;
+
+ req->rq_send_state = LUSTRE_IMP_FULL;
+
+ rc = ptlrpc_queue_wait(req);
+
+ ptlrpc_req_finished(req);
+ if (rc >= 0)
+ return count;
+ return rc;
+}
+EXPORT_SYMBOL(lprocfs_wr_ping);
+
+/* Write the connection UUID to this file to attempt to connect to that node.
+ * The connection UUID is a node's primary NID. For example,
+ * "echo connection=192.168.0.1@tcp0::instance > .../import".
+ */
+int lprocfs_wr_import(struct file *file, const char __user *buffer,
+ size_t count, loff_t *off)
+{
+ struct obd_device *obd = ((struct seq_file *)file->private_data)->private;
+ struct obd_import *imp = obd->u.cli.cl_import;
+ char *kbuf = NULL;
+ char *uuid;
+ char *ptr;
+ int do_reconn = 1;
+ const char prefix[] = "connection=";
+ const int prefix_len = sizeof(prefix) - 1;
+
+ if (count > PAGE_CACHE_SIZE - 1 || count <= prefix_len)
+ return -EINVAL;
+
+ OBD_ALLOC(kbuf, count + 1);
+ if (kbuf == NULL)
+ return -ENOMEM;
+
+ if (copy_from_user(kbuf, buffer, count)) {
+ count = -EFAULT;
+ goto out;
+ }
+
+ kbuf[count] = 0;
+
+ /* only support connection=uuid::instance now */
+ if (strncmp(prefix, kbuf, prefix_len) != 0) {
+ count = -EINVAL;
+ goto out;
+ }
+
+ uuid = kbuf + prefix_len;
+ ptr = strstr(uuid, "::");
+ if (ptr) {
+ __u32 inst;
+ char *endptr;
+
+ *ptr = 0;
+ do_reconn = 0;
+ ptr += strlen("::");
+ inst = simple_strtol(ptr, &endptr, 10);
+ if (*endptr) {
+ CERROR("config: wrong instance # %s\n", ptr);
+ } else if (inst != imp->imp_connect_data.ocd_instance) {
+ CDEBUG(D_INFO, "IR: %s is connecting to an obsoleted target(%u/%u), reconnecting...\n",
+ imp->imp_obd->obd_name,
+ imp->imp_connect_data.ocd_instance, inst);
+ do_reconn = 1;
+ } else {
+ CDEBUG(D_INFO, "IR: %s has already been connecting to new target(%u)\n",
+ imp->imp_obd->obd_name, inst);
+ }
+ }
+
+ if (do_reconn)
+ ptlrpc_recover_import(imp, uuid, 1);
+
+out:
+ OBD_FREE(kbuf, count + 1);
+ return count;
+}
+EXPORT_SYMBOL(lprocfs_wr_import);
+
+int lprocfs_rd_pinger_recov(struct seq_file *m, void *n)
+{
+ struct obd_device *obd = m->private;
+ struct obd_import *imp = obd->u.cli.cl_import;
+
+ LPROCFS_CLIMP_CHECK(obd);
+ seq_printf(m, "%d\n", !imp->imp_no_pinger_recover);
+ LPROCFS_CLIMP_EXIT(obd);
+
+ return 0;
+}
+EXPORT_SYMBOL(lprocfs_rd_pinger_recov);
+
+int lprocfs_wr_pinger_recov(struct file *file, const char __user *buffer,
+ size_t count, loff_t *off)
+{
+ struct obd_device *obd = ((struct seq_file *)file->private_data)->private;
+ struct client_obd *cli = &obd->u.cli;
+ struct obd_import *imp = cli->cl_import;
+ int rc, val;
+
+ rc = lprocfs_write_helper(buffer, count, &val);
+ if (rc < 0)
+ return rc;
+
+ if (val != 0 && val != 1)
+ return -ERANGE;
+
+ LPROCFS_CLIMP_CHECK(obd);
+ spin_lock(&imp->imp_lock);
+ imp->imp_no_pinger_recover = !val;
+ spin_unlock(&imp->imp_lock);
+ LPROCFS_CLIMP_EXIT(obd);
+
+ return count;
+
+}
+EXPORT_SYMBOL(lprocfs_wr_pinger_recov);
+
+#endif /* CONFIG_PROC_FS */
diff --git a/drivers/staging/lustre/lustre/ptlrpc/niobuf.c b/drivers/staging/lustre/lustre/ptlrpc/niobuf.c
new file mode 100644
index 000000000..2fa258558
--- /dev/null
+++ b/drivers/staging/lustre/lustre/ptlrpc/niobuf.c
@@ -0,0 +1,731 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2002, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2011, 2012, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ */
+
+#define DEBUG_SUBSYSTEM S_RPC
+#include "../include/obd_support.h"
+#include "../include/lustre_net.h"
+#include "../include/lustre_lib.h"
+#include "../include/obd.h"
+#include "../include/obd_class.h"
+#include "ptlrpc_internal.h"
+
+/**
+ * Helper function. Sends \a len bytes from \a base at offset \a offset
+ * over \a conn connection to portal \a portal.
+ * Returns 0 on success or error code.
+ */
+static int ptl_send_buf(lnet_handle_md_t *mdh, void *base, int len,
+ lnet_ack_req_t ack, struct ptlrpc_cb_id *cbid,
+ struct ptlrpc_connection *conn, int portal, __u64 xid,
+ unsigned int offset)
+{
+ int rc;
+ lnet_md_t md;
+
+ LASSERT(portal != 0);
+ LASSERT(conn != NULL);
+ CDEBUG(D_INFO, "conn=%p id %s\n", conn, libcfs_id2str(conn->c_peer));
+ md.start = base;
+ md.length = len;
+ md.threshold = (ack == LNET_ACK_REQ) ? 2 : 1;
+ md.options = PTLRPC_MD_OPTIONS;
+ md.user_ptr = cbid;
+ md.eq_handle = ptlrpc_eq_h;
+
+ if (unlikely(ack == LNET_ACK_REQ &&
+ OBD_FAIL_CHECK_ORSET(OBD_FAIL_PTLRPC_ACK,
+ OBD_FAIL_ONCE))) {
+ /* don't ask for the ack to simulate failing client */
+ ack = LNET_NOACK_REQ;
+ }
+
+ rc = LNetMDBind(md, LNET_UNLINK, mdh);
+ if (unlikely(rc != 0)) {
+ CERROR("LNetMDBind failed: %d\n", rc);
+ LASSERT(rc == -ENOMEM);
+ return -ENOMEM;
+ }
+
+ CDEBUG(D_NET, "Sending %d bytes to portal %d, xid %lld, offset %u\n",
+ len, portal, xid, offset);
+
+ rc = LNetPut(conn->c_self, *mdh, ack,
+ conn->c_peer, portal, xid, offset, 0);
+ if (unlikely(rc != 0)) {
+ int rc2;
+ /* We're going to get an UNLINK event when I unlink below,
+ * which will complete just like any other failed send, so
+ * I fall through and return success here! */
+ CERROR("LNetPut(%s, %d, %lld) failed: %d\n",
+ libcfs_id2str(conn->c_peer), portal, xid, rc);
+ rc2 = LNetMDUnlink(*mdh);
+ LASSERTF(rc2 == 0, "rc2 = %d\n", rc2);
+ }
+
+ return 0;
+}
+
+static void mdunlink_iterate_helper(lnet_handle_md_t *bd_mds, int count)
+{
+ int i;
+
+ for (i = 0; i < count; i++)
+ LNetMDUnlink(bd_mds[i]);
+}
+
+
+/**
+ * Register bulk at the sender for later transfer.
+ * Returns 0 on success or error code.
+ */
+int ptlrpc_register_bulk(struct ptlrpc_request *req)
+{
+ struct ptlrpc_bulk_desc *desc = req->rq_bulk;
+ lnet_process_id_t peer;
+ int rc = 0;
+ int rc2;
+ int posted_md;
+ int total_md;
+ __u64 xid;
+ lnet_handle_me_t me_h;
+ lnet_md_t md;
+
+ if (OBD_FAIL_CHECK(OBD_FAIL_PTLRPC_BULK_GET_NET))
+ return 0;
+
+ /* NB no locking required until desc is on the network */
+ LASSERT(desc->bd_nob > 0);
+ LASSERT(desc->bd_md_count == 0);
+ LASSERT(desc->bd_md_max_brw <= PTLRPC_BULK_OPS_COUNT);
+ LASSERT(desc->bd_iov_count <= PTLRPC_MAX_BRW_PAGES);
+ LASSERT(desc->bd_req != NULL);
+ LASSERT(desc->bd_type == BULK_PUT_SINK ||
+ desc->bd_type == BULK_GET_SOURCE);
+
+ /* cleanup the state of the bulk for it will be reused */
+ if (req->rq_resend || req->rq_send_state == LUSTRE_IMP_REPLAY)
+ desc->bd_nob_transferred = 0;
+ else
+ LASSERT(desc->bd_nob_transferred == 0);
+
+ desc->bd_failure = 0;
+
+ peer = desc->bd_import->imp_connection->c_peer;
+
+ LASSERT(desc->bd_cbid.cbid_fn == client_bulk_callback);
+ LASSERT(desc->bd_cbid.cbid_arg == desc);
+
+ /* An XID is only used for a single request from the client.
+ * For retried bulk transfers, a new XID will be allocated in
+ * in ptlrpc_check_set() if it needs to be resent, so it is not
+ * using the same RDMA match bits after an error.
+ *
+ * For multi-bulk RPCs, rq_xid is the last XID needed for bulks. The
+ * first bulk XID is power-of-two aligned before rq_xid. LU-1431 */
+ xid = req->rq_xid & ~((__u64)desc->bd_md_max_brw - 1);
+ LASSERTF(!(desc->bd_registered &&
+ req->rq_send_state != LUSTRE_IMP_REPLAY) ||
+ xid != desc->bd_last_xid,
+ "registered: %d rq_xid: %llu bd_last_xid: %llu\n",
+ desc->bd_registered, xid, desc->bd_last_xid);
+
+ total_md = (desc->bd_iov_count + LNET_MAX_IOV - 1) / LNET_MAX_IOV;
+ desc->bd_registered = 1;
+ desc->bd_last_xid = xid;
+ desc->bd_md_count = total_md;
+ md.user_ptr = &desc->bd_cbid;
+ md.eq_handle = ptlrpc_eq_h;
+ md.threshold = 1; /* PUT or GET */
+
+ for (posted_md = 0; posted_md < total_md; posted_md++, xid++) {
+ md.options = PTLRPC_MD_OPTIONS |
+ ((desc->bd_type == BULK_GET_SOURCE) ?
+ LNET_MD_OP_GET : LNET_MD_OP_PUT);
+ ptlrpc_fill_bulk_md(&md, desc, posted_md);
+
+ rc = LNetMEAttach(desc->bd_portal, peer, xid, 0,
+ LNET_UNLINK, LNET_INS_AFTER, &me_h);
+ if (rc != 0) {
+ CERROR("%s: LNetMEAttach failed x%llu/%d: rc = %d\n",
+ desc->bd_import->imp_obd->obd_name, xid,
+ posted_md, rc);
+ break;
+ }
+
+ /* About to let the network at it... */
+ rc = LNetMDAttach(me_h, md, LNET_UNLINK,
+ &desc->bd_mds[posted_md]);
+ if (rc != 0) {
+ CERROR("%s: LNetMDAttach failed x%llu/%d: rc = %d\n",
+ desc->bd_import->imp_obd->obd_name, xid,
+ posted_md, rc);
+ rc2 = LNetMEUnlink(me_h);
+ LASSERT(rc2 == 0);
+ break;
+ }
+ }
+
+ if (rc != 0) {
+ LASSERT(rc == -ENOMEM);
+ spin_lock(&desc->bd_lock);
+ desc->bd_md_count -= total_md - posted_md;
+ spin_unlock(&desc->bd_lock);
+ LASSERT(desc->bd_md_count >= 0);
+ mdunlink_iterate_helper(desc->bd_mds, desc->bd_md_max_brw);
+ req->rq_status = -ENOMEM;
+ return -ENOMEM;
+ }
+
+ /* Set rq_xid to matchbits of the final bulk so that server can
+ * infer the number of bulks that were prepared */
+ req->rq_xid = --xid;
+ LASSERTF(desc->bd_last_xid == (req->rq_xid & PTLRPC_BULK_OPS_MASK),
+ "bd_last_xid = x%llu, rq_xid = x%llu\n",
+ desc->bd_last_xid, req->rq_xid);
+
+ spin_lock(&desc->bd_lock);
+ /* Holler if peer manages to touch buffers before he knows the xid */
+ if (desc->bd_md_count != total_md)
+ CWARN("%s: Peer %s touched %d buffers while I registered\n",
+ desc->bd_import->imp_obd->obd_name, libcfs_id2str(peer),
+ total_md - desc->bd_md_count);
+ spin_unlock(&desc->bd_lock);
+
+ CDEBUG(D_NET, "Setup %u bulk %s buffers: %u pages %u bytes, xid x%#llx-%#llx, portal %u\n",
+ desc->bd_md_count,
+ desc->bd_type == BULK_GET_SOURCE ? "get-source" : "put-sink",
+ desc->bd_iov_count, desc->bd_nob,
+ desc->bd_last_xid, req->rq_xid, desc->bd_portal);
+
+ return 0;
+}
+EXPORT_SYMBOL(ptlrpc_register_bulk);
+
+/**
+ * Disconnect a bulk desc from the network. Idempotent. Not
+ * thread-safe (i.e. only interlocks with completion callback).
+ * Returns 1 on success or 0 if network unregistration failed for whatever
+ * reason.
+ */
+int ptlrpc_unregister_bulk(struct ptlrpc_request *req, int async)
+{
+ struct ptlrpc_bulk_desc *desc = req->rq_bulk;
+ wait_queue_head_t *wq;
+ struct l_wait_info lwi;
+ int rc;
+
+ LASSERT(!in_interrupt()); /* might sleep */
+
+ /* Let's setup deadline for reply unlink. */
+ if (OBD_FAIL_CHECK(OBD_FAIL_PTLRPC_LONG_BULK_UNLINK) &&
+ async && req->rq_bulk_deadline == 0)
+ req->rq_bulk_deadline = get_seconds() + LONG_UNLINK;
+
+ if (ptlrpc_client_bulk_active(req) == 0) /* completed or */
+ return 1; /* never registered */
+
+ LASSERT(desc->bd_req == req); /* bd_req NULL until registered */
+
+ /* the unlink ensures the callback happens ASAP and is the last
+ * one. If it fails, it must be because completion just happened,
+ * but we must still l_wait_event() in this case to give liblustre
+ * a chance to run client_bulk_callback() */
+ mdunlink_iterate_helper(desc->bd_mds, desc->bd_md_max_brw);
+
+ if (ptlrpc_client_bulk_active(req) == 0) /* completed or */
+ return 1; /* never registered */
+
+ /* Move to "Unregistering" phase as bulk was not unlinked yet. */
+ ptlrpc_rqphase_move(req, RQ_PHASE_UNREGISTERING);
+
+ /* Do not wait for unlink to finish. */
+ if (async)
+ return 0;
+
+ if (req->rq_set != NULL)
+ wq = &req->rq_set->set_waitq;
+ else
+ wq = &req->rq_reply_waitq;
+
+ for (;;) {
+ /* Network access will complete in finite time but the HUGE
+ * timeout lets us CWARN for visibility of sluggish NALs */
+ lwi = LWI_TIMEOUT_INTERVAL(cfs_time_seconds(LONG_UNLINK),
+ cfs_time_seconds(1), NULL, NULL);
+ rc = l_wait_event(*wq, !ptlrpc_client_bulk_active(req), &lwi);
+ if (rc == 0) {
+ ptlrpc_rqphase_move(req, req->rq_next_phase);
+ return 1;
+ }
+
+ LASSERT(rc == -ETIMEDOUT);
+ DEBUG_REQ(D_WARNING, req, "Unexpectedly long timeout: desc %p",
+ desc);
+ }
+ return 0;
+}
+EXPORT_SYMBOL(ptlrpc_unregister_bulk);
+
+static void ptlrpc_at_set_reply(struct ptlrpc_request *req, int flags)
+{
+ struct ptlrpc_service_part *svcpt = req->rq_rqbd->rqbd_svcpt;
+ struct ptlrpc_service *svc = svcpt->scp_service;
+ int service_time = max_t(int, get_seconds() -
+ req->rq_arrival_time.tv_sec, 1);
+
+ if (!(flags & PTLRPC_REPLY_EARLY) &&
+ (req->rq_type != PTL_RPC_MSG_ERR) &&
+ (req->rq_reqmsg != NULL) &&
+ !(lustre_msg_get_flags(req->rq_reqmsg) &
+ (MSG_RESENT | MSG_REPLAY |
+ MSG_REQ_REPLAY_DONE | MSG_LOCK_REPLAY_DONE))) {
+ /* early replies, errors and recovery requests don't count
+ * toward our service time estimate */
+ int oldse = at_measured(&svcpt->scp_at_estimate, service_time);
+
+ if (oldse != 0) {
+ DEBUG_REQ(D_ADAPTTO, req,
+ "svc %s changed estimate from %d to %d",
+ svc->srv_name, oldse,
+ at_get(&svcpt->scp_at_estimate));
+ }
+ }
+ /* Report actual service time for client latency calc */
+ lustre_msg_set_service_time(req->rq_repmsg, service_time);
+ /* Report service time estimate for future client reqs, but report 0
+ * (to be ignored by client) if it's a error reply during recovery.
+ * (bz15815) */
+ if (req->rq_type == PTL_RPC_MSG_ERR &&
+ (req->rq_export == NULL || req->rq_export->exp_obd->obd_recovering))
+ lustre_msg_set_timeout(req->rq_repmsg, 0);
+ else
+ lustre_msg_set_timeout(req->rq_repmsg,
+ at_get(&svcpt->scp_at_estimate));
+
+ if (req->rq_reqmsg &&
+ !(lustre_msghdr_get_flags(req->rq_reqmsg) & MSGHDR_AT_SUPPORT)) {
+ CDEBUG(D_ADAPTTO, "No early reply support: flags=%#x req_flags=%#x magic=%d:%x/%x len=%d\n",
+ flags, lustre_msg_get_flags(req->rq_reqmsg),
+ lustre_msg_is_v1(req->rq_reqmsg),
+ lustre_msg_get_magic(req->rq_reqmsg),
+ lustre_msg_get_magic(req->rq_repmsg), req->rq_replen);
+ }
+}
+
+/**
+ * Send request reply from request \a req reply buffer.
+ * \a flags defines reply types
+ * Returns 0 on success or error code
+ */
+int ptlrpc_send_reply(struct ptlrpc_request *req, int flags)
+{
+ struct ptlrpc_reply_state *rs = req->rq_reply_state;
+ struct ptlrpc_connection *conn;
+ int rc;
+
+ /* We must already have a reply buffer (only ptlrpc_error() may be
+ * called without one). The reply generated by sptlrpc layer (e.g.
+ * error notify, etc.) might have NULL rq->reqmsg; Otherwise we must
+ * have a request buffer which is either the actual (swabbed) incoming
+ * request, or a saved copy if this is a req saved in
+ * target_queue_final_reply().
+ */
+ LASSERT(req->rq_no_reply == 0);
+ LASSERT(req->rq_reqbuf != NULL);
+ LASSERT(rs != NULL);
+ LASSERT((flags & PTLRPC_REPLY_MAYBE_DIFFICULT) || !rs->rs_difficult);
+ LASSERT(req->rq_repmsg != NULL);
+ LASSERT(req->rq_repmsg == rs->rs_msg);
+ LASSERT(rs->rs_cb_id.cbid_fn == reply_out_callback);
+ LASSERT(rs->rs_cb_id.cbid_arg == rs);
+
+ /* There may be no rq_export during failover */
+
+ if (unlikely(req->rq_export && req->rq_export->exp_obd &&
+ req->rq_export->exp_obd->obd_fail)) {
+ /* Failed obd's only send ENODEV */
+ req->rq_type = PTL_RPC_MSG_ERR;
+ req->rq_status = -ENODEV;
+ CDEBUG(D_HA, "sending ENODEV from failed obd %d\n",
+ req->rq_export->exp_obd->obd_minor);
+ }
+
+ /* In order to keep interoperability with the client (< 2.3) which
+ * doesn't have pb_jobid in ptlrpc_body, We have to shrink the
+ * ptlrpc_body in reply buffer to ptlrpc_body_v2, otherwise, the
+ * reply buffer on client will be overflow.
+ *
+ * XXX Remove this whenever we drop the interoperability with
+ * such client.
+ */
+ req->rq_replen = lustre_shrink_msg(req->rq_repmsg, 0,
+ sizeof(struct ptlrpc_body_v2), 1);
+
+ if (req->rq_type != PTL_RPC_MSG_ERR)
+ req->rq_type = PTL_RPC_MSG_REPLY;
+
+ lustre_msg_set_type(req->rq_repmsg, req->rq_type);
+ lustre_msg_set_status(req->rq_repmsg,
+ ptlrpc_status_hton(req->rq_status));
+ lustre_msg_set_opc(req->rq_repmsg,
+ req->rq_reqmsg ? lustre_msg_get_opc(req->rq_reqmsg) : 0);
+
+ target_pack_pool_reply(req);
+
+ ptlrpc_at_set_reply(req, flags);
+
+ if (req->rq_export == NULL || req->rq_export->exp_connection == NULL)
+ conn = ptlrpc_connection_get(req->rq_peer, req->rq_self, NULL);
+ else
+ conn = ptlrpc_connection_addref(req->rq_export->exp_connection);
+
+ if (unlikely(conn == NULL)) {
+ CERROR("not replying on NULL connection\n"); /* bug 9635 */
+ return -ENOTCONN;
+ }
+ ptlrpc_rs_addref(rs); /* +1 ref for the network */
+
+ rc = sptlrpc_svc_wrap_reply(req);
+ if (unlikely(rc))
+ goto out;
+
+ req->rq_sent = get_seconds();
+
+ rc = ptl_send_buf(&rs->rs_md_h, rs->rs_repbuf, rs->rs_repdata_len,
+ (rs->rs_difficult && !rs->rs_no_ack) ?
+ LNET_ACK_REQ : LNET_NOACK_REQ,
+ &rs->rs_cb_id, conn,
+ ptlrpc_req2svc(req)->srv_rep_portal,
+ req->rq_xid, req->rq_reply_off);
+out:
+ if (unlikely(rc != 0))
+ ptlrpc_req_drop_rs(req);
+ ptlrpc_connection_put(conn);
+ return rc;
+}
+EXPORT_SYMBOL(ptlrpc_send_reply);
+
+int ptlrpc_reply(struct ptlrpc_request *req)
+{
+ if (req->rq_no_reply)
+ return 0;
+ return ptlrpc_send_reply(req, 0);
+}
+EXPORT_SYMBOL(ptlrpc_reply);
+
+/**
+ * For request \a req send an error reply back. Create empty
+ * reply buffers if necessary.
+ */
+int ptlrpc_send_error(struct ptlrpc_request *req, int may_be_difficult)
+{
+ int rc;
+
+ if (req->rq_no_reply)
+ return 0;
+
+ if (!req->rq_repmsg) {
+ rc = lustre_pack_reply(req, 1, NULL, NULL);
+ if (rc)
+ return rc;
+ }
+
+ if (req->rq_status != -ENOSPC && req->rq_status != -EACCES &&
+ req->rq_status != -EPERM && req->rq_status != -ENOENT &&
+ req->rq_status != -EINPROGRESS && req->rq_status != -EDQUOT)
+ req->rq_type = PTL_RPC_MSG_ERR;
+
+ rc = ptlrpc_send_reply(req, may_be_difficult);
+ return rc;
+}
+EXPORT_SYMBOL(ptlrpc_send_error);
+
+int ptlrpc_error(struct ptlrpc_request *req)
+{
+ return ptlrpc_send_error(req, 0);
+}
+EXPORT_SYMBOL(ptlrpc_error);
+
+/**
+ * Send request \a request.
+ * if \a noreply is set, don't expect any reply back and don't set up
+ * reply buffers.
+ * Returns 0 on success or error code.
+ */
+int ptl_send_rpc(struct ptlrpc_request *request, int noreply)
+{
+ int rc;
+ int rc2;
+ int mpflag = 0;
+ struct ptlrpc_connection *connection;
+ lnet_handle_me_t reply_me_h;
+ lnet_md_t reply_md;
+ struct obd_device *obd = request->rq_import->imp_obd;
+
+ if (OBD_FAIL_CHECK(OBD_FAIL_PTLRPC_DROP_RPC))
+ return 0;
+
+ LASSERT(request->rq_type == PTL_RPC_MSG_REQUEST);
+ LASSERT(request->rq_wait_ctx == 0);
+
+ /* If this is a re-transmit, we're required to have disengaged
+ * cleanly from the previous attempt */
+ LASSERT(!request->rq_receiving_reply);
+ LASSERT(!((lustre_msg_get_flags(request->rq_reqmsg) & MSG_REPLAY) &&
+ (request->rq_import->imp_state == LUSTRE_IMP_FULL)));
+
+ if (unlikely(obd != NULL && obd->obd_fail)) {
+ CDEBUG(D_HA, "muting rpc for failed imp obd %s\n",
+ obd->obd_name);
+ /* this prevents us from waiting in ptlrpc_queue_wait */
+ spin_lock(&request->rq_lock);
+ request->rq_err = 1;
+ spin_unlock(&request->rq_lock);
+ request->rq_status = -ENODEV;
+ return -ENODEV;
+ }
+
+ connection = request->rq_import->imp_connection;
+
+ lustre_msg_set_handle(request->rq_reqmsg,
+ &request->rq_import->imp_remote_handle);
+ lustre_msg_set_type(request->rq_reqmsg, PTL_RPC_MSG_REQUEST);
+ lustre_msg_set_conn_cnt(request->rq_reqmsg,
+ request->rq_import->imp_conn_cnt);
+ lustre_msghdr_set_flags(request->rq_reqmsg,
+ request->rq_import->imp_msghdr_flags);
+
+ if (request->rq_resend)
+ lustre_msg_add_flags(request->rq_reqmsg, MSG_RESENT);
+
+ if (request->rq_memalloc)
+ mpflag = cfs_memory_pressure_get_and_set();
+
+ rc = sptlrpc_cli_wrap_request(request);
+ if (rc)
+ goto out;
+
+ /* bulk register should be done after wrap_request() */
+ if (request->rq_bulk != NULL) {
+ rc = ptlrpc_register_bulk(request);
+ if (rc != 0)
+ goto out;
+ }
+
+ if (!noreply) {
+ LASSERT(request->rq_replen != 0);
+ if (request->rq_repbuf == NULL) {
+ LASSERT(request->rq_repdata == NULL);
+ LASSERT(request->rq_repmsg == NULL);
+ rc = sptlrpc_cli_alloc_repbuf(request,
+ request->rq_replen);
+ if (rc) {
+ /* this prevents us from looping in
+ * ptlrpc_queue_wait */
+ spin_lock(&request->rq_lock);
+ request->rq_err = 1;
+ spin_unlock(&request->rq_lock);
+ request->rq_status = rc;
+ goto cleanup_bulk;
+ }
+ } else {
+ request->rq_repdata = NULL;
+ request->rq_repmsg = NULL;
+ }
+
+ rc = LNetMEAttach(request->rq_reply_portal,/*XXX FIXME bug 249*/
+ connection->c_peer, request->rq_xid, 0,
+ LNET_UNLINK, LNET_INS_AFTER, &reply_me_h);
+ if (rc != 0) {
+ CERROR("LNetMEAttach failed: %d\n", rc);
+ LASSERT(rc == -ENOMEM);
+ rc = -ENOMEM;
+ goto cleanup_bulk;
+ }
+ }
+
+ spin_lock(&request->rq_lock);
+ /* If the MD attach succeeds, there _will_ be a reply_in callback */
+ request->rq_receiving_reply = !noreply;
+ request->rq_req_unlink = 1;
+ /* We are responsible for unlinking the reply buffer */
+ request->rq_reply_unlink = !noreply;
+ /* Clear any flags that may be present from previous sends. */
+ request->rq_replied = 0;
+ request->rq_err = 0;
+ request->rq_timedout = 0;
+ request->rq_net_err = 0;
+ request->rq_resend = 0;
+ request->rq_restart = 0;
+ request->rq_reply_truncate = 0;
+ spin_unlock(&request->rq_lock);
+
+ if (!noreply) {
+ reply_md.start = request->rq_repbuf;
+ reply_md.length = request->rq_repbuf_len;
+ /* Allow multiple early replies */
+ reply_md.threshold = LNET_MD_THRESH_INF;
+ /* Manage remote for early replies */
+ reply_md.options = PTLRPC_MD_OPTIONS | LNET_MD_OP_PUT |
+ LNET_MD_MANAGE_REMOTE |
+ LNET_MD_TRUNCATE; /* allow to make EOVERFLOW error */;
+ reply_md.user_ptr = &request->rq_reply_cbid;
+ reply_md.eq_handle = ptlrpc_eq_h;
+
+ /* We must see the unlink callback to unset rq_reply_unlink,
+ so we can't auto-unlink */
+ rc = LNetMDAttach(reply_me_h, reply_md, LNET_RETAIN,
+ &request->rq_reply_md_h);
+ if (rc != 0) {
+ CERROR("LNetMDAttach failed: %d\n", rc);
+ LASSERT(rc == -ENOMEM);
+ spin_lock(&request->rq_lock);
+ /* ...but the MD attach didn't succeed... */
+ request->rq_receiving_reply = 0;
+ spin_unlock(&request->rq_lock);
+ rc = -ENOMEM;
+ goto cleanup_me;
+ }
+
+ CDEBUG(D_NET, "Setup reply buffer: %u bytes, xid %llu, portal %u\n",
+ request->rq_repbuf_len, request->rq_xid,
+ request->rq_reply_portal);
+ }
+
+ /* add references on request for request_out_callback */
+ ptlrpc_request_addref(request);
+ if (obd != NULL && obd->obd_svc_stats != NULL)
+ lprocfs_counter_add(obd->obd_svc_stats, PTLRPC_REQACTIVE_CNTR,
+ atomic_read(&request->rq_import->imp_inflight));
+
+ OBD_FAIL_TIMEOUT(OBD_FAIL_PTLRPC_DELAY_SEND, request->rq_timeout + 5);
+
+ do_gettimeofday(&request->rq_arrival_time);
+ request->rq_sent = get_seconds();
+ /* We give the server rq_timeout secs to process the req, and
+ add the network latency for our local timeout. */
+ request->rq_deadline = request->rq_sent + request->rq_timeout +
+ ptlrpc_at_get_net_latency(request);
+
+ ptlrpc_pinger_sending_on_import(request->rq_import);
+
+ DEBUG_REQ(D_INFO, request, "send flg=%x",
+ lustre_msg_get_flags(request->rq_reqmsg));
+ rc = ptl_send_buf(&request->rq_req_md_h,
+ request->rq_reqbuf, request->rq_reqdata_len,
+ LNET_NOACK_REQ, &request->rq_req_cbid,
+ connection,
+ request->rq_request_portal,
+ request->rq_xid, 0);
+ if (rc == 0)
+ goto out;
+
+ ptlrpc_req_finished(request);
+ if (noreply)
+ goto out;
+
+ cleanup_me:
+ /* MEUnlink is safe; the PUT didn't even get off the ground, and
+ * nobody apart from the PUT's target has the right nid+XID to
+ * access the reply buffer. */
+ rc2 = LNetMEUnlink(reply_me_h);
+ LASSERT(rc2 == 0);
+ /* UNLINKED callback called synchronously */
+ LASSERT(!request->rq_receiving_reply);
+
+ cleanup_bulk:
+ /* We do sync unlink here as there was no real transfer here so
+ * the chance to have long unlink to sluggish net is smaller here. */
+ ptlrpc_unregister_bulk(request, 0);
+ out:
+ if (request->rq_memalloc)
+ cfs_memory_pressure_restore(mpflag);
+ return rc;
+}
+EXPORT_SYMBOL(ptl_send_rpc);
+
+/**
+ * Register request buffer descriptor for request receiving.
+ */
+int ptlrpc_register_rqbd(struct ptlrpc_request_buffer_desc *rqbd)
+{
+ struct ptlrpc_service *service = rqbd->rqbd_svcpt->scp_service;
+ static lnet_process_id_t match_id = {LNET_NID_ANY, LNET_PID_ANY};
+ int rc;
+ lnet_md_t md;
+ lnet_handle_me_t me_h;
+
+ CDEBUG(D_NET, "LNetMEAttach: portal %d\n",
+ service->srv_req_portal);
+
+ if (OBD_FAIL_CHECK(OBD_FAIL_PTLRPC_RQBD))
+ return -ENOMEM;
+
+ /* NB: CPT affinity service should use new LNet flag LNET_INS_LOCAL,
+ * which means buffer can only be attached on local CPT, and LND
+ * threads can find it by grabbing a local lock */
+ rc = LNetMEAttach(service->srv_req_portal,
+ match_id, 0, ~0, LNET_UNLINK,
+ rqbd->rqbd_svcpt->scp_cpt >= 0 ?
+ LNET_INS_LOCAL : LNET_INS_AFTER, &me_h);
+ if (rc != 0) {
+ CERROR("LNetMEAttach failed: %d\n", rc);
+ return -ENOMEM;
+ }
+
+ LASSERT(rqbd->rqbd_refcount == 0);
+ rqbd->rqbd_refcount = 1;
+
+ md.start = rqbd->rqbd_buffer;
+ md.length = service->srv_buf_size;
+ md.max_size = service->srv_max_req_size;
+ md.threshold = LNET_MD_THRESH_INF;
+ md.options = PTLRPC_MD_OPTIONS | LNET_MD_OP_PUT | LNET_MD_MAX_SIZE;
+ md.user_ptr = &rqbd->rqbd_cbid;
+ md.eq_handle = ptlrpc_eq_h;
+
+ rc = LNetMDAttach(me_h, md, LNET_UNLINK, &rqbd->rqbd_md_h);
+ if (rc == 0)
+ return 0;
+
+ CERROR("LNetMDAttach failed: %d;\n", rc);
+ LASSERT(rc == -ENOMEM);
+ rc = LNetMEUnlink(me_h);
+ LASSERT(rc == 0);
+ rqbd->rqbd_refcount = 0;
+
+ return -ENOMEM;
+}
diff --git a/drivers/staging/lustre/lustre/ptlrpc/nrs.c b/drivers/staging/lustre/lustre/ptlrpc/nrs.c
new file mode 100644
index 000000000..81ad74732
--- /dev/null
+++ b/drivers/staging/lustre/lustre/ptlrpc/nrs.c
@@ -0,0 +1,1754 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+
+ * 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 version 2 for more details. A copy is
+ * included in the COPYING file that accompanied this code.
+
+ * 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
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2011 Intel Corporation
+ *
+ * Copyright 2012 Xyratex Technology Limited
+ */
+/*
+ * lustre/ptlrpc/nrs.c
+ *
+ * Network Request Scheduler (NRS)
+ *
+ * Allows to reorder the handling of RPCs at servers.
+ *
+ * Author: Liang Zhen <liang@whamcloud.com>
+ * Author: Nikitas Angelinas <nikitas_angelinas@xyratex.com>
+ */
+/**
+ * \addtogoup nrs
+ * @{
+ */
+
+#define DEBUG_SUBSYSTEM S_RPC
+#include "../include/obd_support.h"
+#include "../include/obd_class.h"
+#include "../include/lustre_net.h"
+#include "../include/lprocfs_status.h"
+#include "../../include/linux/libcfs/libcfs.h"
+#include "ptlrpc_internal.h"
+
+/* XXX: This is just for liblustre. Remove the #if defined directive when the
+ * "cfs_" prefix is dropped from cfs_list_head. */
+extern struct list_head ptlrpc_all_services;
+
+/**
+ * NRS core object.
+ */
+struct nrs_core nrs_core;
+
+static int nrs_policy_init(struct ptlrpc_nrs_policy *policy)
+{
+ return policy->pol_desc->pd_ops->op_policy_init != NULL ?
+ policy->pol_desc->pd_ops->op_policy_init(policy) : 0;
+}
+
+static void nrs_policy_fini(struct ptlrpc_nrs_policy *policy)
+{
+ LASSERT(policy->pol_ref == 0);
+ LASSERT(policy->pol_req_queued == 0);
+
+ if (policy->pol_desc->pd_ops->op_policy_fini != NULL)
+ policy->pol_desc->pd_ops->op_policy_fini(policy);
+}
+
+static int nrs_policy_ctl_locked(struct ptlrpc_nrs_policy *policy,
+ enum ptlrpc_nrs_ctl opc, void *arg)
+{
+ /**
+ * The policy may be stopped, but the lprocfs files and
+ * ptlrpc_nrs_policy instances remain present until unregistration time.
+ * Do not perform the ctl operation if the policy is stopped, as
+ * policy->pol_private will be NULL in such a case.
+ */
+ if (policy->pol_state == NRS_POL_STATE_STOPPED)
+ return -ENODEV;
+
+ return policy->pol_desc->pd_ops->op_policy_ctl != NULL ?
+ policy->pol_desc->pd_ops->op_policy_ctl(policy, opc, arg) :
+ -ENOSYS;
+}
+
+static void nrs_policy_stop0(struct ptlrpc_nrs_policy *policy)
+{
+ struct ptlrpc_nrs *nrs = policy->pol_nrs;
+
+ if (policy->pol_desc->pd_ops->op_policy_stop != NULL) {
+ spin_unlock(&nrs->nrs_lock);
+
+ policy->pol_desc->pd_ops->op_policy_stop(policy);
+
+ spin_lock(&nrs->nrs_lock);
+ }
+
+ LASSERT(list_empty(&policy->pol_list_queued));
+ LASSERT(policy->pol_req_queued == 0 &&
+ policy->pol_req_started == 0);
+
+ policy->pol_private = NULL;
+
+ policy->pol_state = NRS_POL_STATE_STOPPED;
+
+ if (atomic_dec_and_test(&policy->pol_desc->pd_refs))
+ module_put(policy->pol_desc->pd_owner);
+}
+
+static int nrs_policy_stop_locked(struct ptlrpc_nrs_policy *policy)
+{
+ struct ptlrpc_nrs *nrs = policy->pol_nrs;
+
+ if (nrs->nrs_policy_fallback == policy && !nrs->nrs_stopping)
+ return -EPERM;
+
+ if (policy->pol_state == NRS_POL_STATE_STARTING)
+ return -EAGAIN;
+
+ /* In progress or already stopped */
+ if (policy->pol_state != NRS_POL_STATE_STARTED)
+ return 0;
+
+ policy->pol_state = NRS_POL_STATE_STOPPING;
+
+ /* Immediately make it invisible */
+ if (nrs->nrs_policy_primary == policy) {
+ nrs->nrs_policy_primary = NULL;
+
+ } else {
+ LASSERT(nrs->nrs_policy_fallback == policy);
+ nrs->nrs_policy_fallback = NULL;
+ }
+
+ /* I have the only refcount */
+ if (policy->pol_ref == 1)
+ nrs_policy_stop0(policy);
+
+ return 0;
+}
+
+/**
+ * Transitions the \a nrs NRS head's primary policy to
+ * ptlrpc_nrs_pol_state::NRS_POL_STATE_STOPPING and if the policy has no
+ * pending usage references, to ptlrpc_nrs_pol_state::NRS_POL_STATE_STOPPED.
+ *
+ * \param[in] nrs the NRS head to carry out this operation on
+ */
+static void nrs_policy_stop_primary(struct ptlrpc_nrs *nrs)
+{
+ struct ptlrpc_nrs_policy *tmp = nrs->nrs_policy_primary;
+
+ if (tmp == NULL)
+ return;
+
+ nrs->nrs_policy_primary = NULL;
+
+ LASSERT(tmp->pol_state == NRS_POL_STATE_STARTED);
+ tmp->pol_state = NRS_POL_STATE_STOPPING;
+
+ if (tmp->pol_ref == 0)
+ nrs_policy_stop0(tmp);
+}
+
+/**
+ * Transitions a policy across the ptlrpc_nrs_pol_state range of values, in
+ * response to an lprocfs command to start a policy.
+ *
+ * If a primary policy different to the current one is specified, this function
+ * will transition the new policy to the
+ * ptlrpc_nrs_pol_state::NRS_POL_STATE_STARTING and then to
+ * ptlrpc_nrs_pol_state::NRS_POL_STATE_STARTED, and will then transition
+ * the old primary policy (if there is one) to
+ * ptlrpc_nrs_pol_state::NRS_POL_STATE_STOPPING, and if there are no outstanding
+ * references on the policy to ptlrpc_nrs_pol_stae::NRS_POL_STATE_STOPPED.
+ *
+ * If the fallback policy is specified, this is taken to indicate an instruction
+ * to stop the current primary policy, without substituting it with another
+ * primary policy, so the primary policy (if any) is transitioned to
+ * ptlrpc_nrs_pol_state::NRS_POL_STATE_STOPPING, and if there are no outstanding
+ * references on the policy to ptlrpc_nrs_pol_stae::NRS_POL_STATE_STOPPED. In
+ * this case, the fallback policy is only left active in the NRS head.
+ */
+static int nrs_policy_start_locked(struct ptlrpc_nrs_policy *policy)
+{
+ struct ptlrpc_nrs *nrs = policy->pol_nrs;
+ int rc = 0;
+
+ /**
+ * Don't allow multiple starting which is too complex, and has no real
+ * benefit.
+ */
+ if (nrs->nrs_policy_starting)
+ return -EAGAIN;
+
+ LASSERT(policy->pol_state != NRS_POL_STATE_STARTING);
+
+ if (policy->pol_state == NRS_POL_STATE_STOPPING)
+ return -EAGAIN;
+
+ if (policy->pol_flags & PTLRPC_NRS_FL_FALLBACK) {
+ /**
+ * This is for cases in which the user sets the policy to the
+ * fallback policy (currently fifo for all services); i.e. the
+ * user is resetting the policy to the default; so we stop the
+ * primary policy, if any.
+ */
+ if (policy == nrs->nrs_policy_fallback) {
+ nrs_policy_stop_primary(nrs);
+ return 0;
+ }
+
+ /**
+ * If we reach here, we must be setting up the fallback policy
+ * at service startup time, and only a single policy with the
+ * nrs_policy_flags::PTLRPC_NRS_FL_FALLBACK flag set can
+ * register with NRS core.
+ */
+ LASSERT(nrs->nrs_policy_fallback == NULL);
+ } else {
+ /**
+ * Shouldn't start primary policy if w/o fallback policy.
+ */
+ if (nrs->nrs_policy_fallback == NULL)
+ return -EPERM;
+
+ if (policy->pol_state == NRS_POL_STATE_STARTED)
+ return 0;
+ }
+
+ /**
+ * Increase the module usage count for policies registering from other
+ * modules.
+ */
+ if (atomic_inc_return(&policy->pol_desc->pd_refs) == 1 &&
+ !try_module_get(policy->pol_desc->pd_owner)) {
+ atomic_dec(&policy->pol_desc->pd_refs);
+ CERROR("NRS: cannot get module for policy %s; is it alive?\n",
+ policy->pol_desc->pd_name);
+ return -ENODEV;
+ }
+
+ /**
+ * Serialize policy starting across the NRS head
+ */
+ nrs->nrs_policy_starting = 1;
+
+ policy->pol_state = NRS_POL_STATE_STARTING;
+
+ if (policy->pol_desc->pd_ops->op_policy_start) {
+ spin_unlock(&nrs->nrs_lock);
+
+ rc = policy->pol_desc->pd_ops->op_policy_start(policy);
+
+ spin_lock(&nrs->nrs_lock);
+ if (rc != 0) {
+ if (atomic_dec_and_test(&policy->pol_desc->pd_refs))
+ module_put(policy->pol_desc->pd_owner);
+
+ policy->pol_state = NRS_POL_STATE_STOPPED;
+ goto out;
+ }
+ }
+
+ policy->pol_state = NRS_POL_STATE_STARTED;
+
+ if (policy->pol_flags & PTLRPC_NRS_FL_FALLBACK) {
+ /**
+ * This path is only used at PTLRPC service setup time.
+ */
+ nrs->nrs_policy_fallback = policy;
+ } else {
+ /*
+ * Try to stop the current primary policy if there is one.
+ */
+ nrs_policy_stop_primary(nrs);
+
+ /**
+ * And set the newly-started policy as the primary one.
+ */
+ nrs->nrs_policy_primary = policy;
+ }
+
+out:
+ nrs->nrs_policy_starting = 0;
+
+ return rc;
+}
+
+/**
+ * Increases the policy's usage reference count.
+ */
+static inline void nrs_policy_get_locked(struct ptlrpc_nrs_policy *policy)
+{
+ policy->pol_ref++;
+}
+
+/**
+ * Decreases the policy's usage reference count, and stops the policy in case it
+ * was already stopping and have no more outstanding usage references (which
+ * indicates it has no more queued or started requests, and can be safely
+ * stopped).
+ */
+static void nrs_policy_put_locked(struct ptlrpc_nrs_policy *policy)
+{
+ LASSERT(policy->pol_ref > 0);
+
+ policy->pol_ref--;
+ if (unlikely(policy->pol_ref == 0 &&
+ policy->pol_state == NRS_POL_STATE_STOPPING))
+ nrs_policy_stop0(policy);
+}
+
+static void nrs_policy_put(struct ptlrpc_nrs_policy *policy)
+{
+ spin_lock(&policy->pol_nrs->nrs_lock);
+ nrs_policy_put_locked(policy);
+ spin_unlock(&policy->pol_nrs->nrs_lock);
+}
+
+/**
+ * Find and return a policy by name.
+ */
+static struct ptlrpc_nrs_policy *nrs_policy_find_locked(struct ptlrpc_nrs *nrs,
+ char *name)
+{
+ struct ptlrpc_nrs_policy *tmp;
+
+ list_for_each_entry(tmp, &nrs->nrs_policy_list, pol_list) {
+ if (strncmp(tmp->pol_desc->pd_name, name,
+ NRS_POL_NAME_MAX) == 0) {
+ nrs_policy_get_locked(tmp);
+ return tmp;
+ }
+ }
+ return NULL;
+}
+
+/**
+ * Release references for the resource hierarchy moving upwards towards the
+ * policy instance resource.
+ */
+static void nrs_resource_put(struct ptlrpc_nrs_resource *res)
+{
+ struct ptlrpc_nrs_policy *policy = res->res_policy;
+
+ if (policy->pol_desc->pd_ops->op_res_put != NULL) {
+ struct ptlrpc_nrs_resource *parent;
+
+ for (; res != NULL; res = parent) {
+ parent = res->res_parent;
+ policy->pol_desc->pd_ops->op_res_put(policy, res);
+ }
+ }
+}
+
+/**
+ * Obtains references for each resource in the resource hierarchy for request
+ * \a nrq if it is to be handled by \a policy.
+ *
+ * \param[in] policy the policy
+ * \param[in] nrq the request
+ * \param[in] moving_req denotes whether this is a call to the function by
+ * ldlm_lock_reorder_req(), in order to move \a nrq to
+ * the high-priority NRS head; we should not sleep when
+ * set.
+ *
+ * \retval NULL resource hierarchy references not obtained
+ * \retval valid-pointer the bottom level of the resource hierarchy
+ *
+ * \see ptlrpc_nrs_pol_ops::op_res_get()
+ */
+static
+struct ptlrpc_nrs_resource *nrs_resource_get(struct ptlrpc_nrs_policy *policy,
+ struct ptlrpc_nrs_request *nrq,
+ bool moving_req)
+{
+ /**
+ * Set to NULL to traverse the resource hierarchy from the top.
+ */
+ struct ptlrpc_nrs_resource *res = NULL;
+ struct ptlrpc_nrs_resource *tmp = NULL;
+ int rc;
+
+ while (1) {
+ rc = policy->pol_desc->pd_ops->op_res_get(policy, nrq, res,
+ &tmp, moving_req);
+ if (rc < 0) {
+ if (res != NULL)
+ nrs_resource_put(res);
+ return NULL;
+ }
+
+ LASSERT(tmp != NULL);
+ tmp->res_parent = res;
+ tmp->res_policy = policy;
+ res = tmp;
+ tmp = NULL;
+ /**
+ * Return once we have obtained a reference to the bottom level
+ * of the resource hierarchy.
+ */
+ if (rc > 0)
+ return res;
+ }
+}
+
+/**
+ * Obtains resources for the resource hierarchies and policy references for
+ * the fallback and current primary policy (if any), that will later be used
+ * to handle request \a nrq.
+ *
+ * \param[in] nrs the NRS head instance that will be handling request \a nrq.
+ * \param[in] nrq the request that is being handled.
+ * \param[out] resp the array where references to the resource hierarchy are
+ * stored.
+ * \param[in] moving_req is set when obtaining resources while moving a
+ * request from a policy on the regular NRS head to a
+ * policy on the HP NRS head (via
+ * ldlm_lock_reorder_req()). It signifies that
+ * allocations to get resources should be atomic; for
+ * a full explanation, see comment in
+ * ptlrpc_nrs_pol_ops::op_res_get().
+ */
+static void nrs_resource_get_safe(struct ptlrpc_nrs *nrs,
+ struct ptlrpc_nrs_request *nrq,
+ struct ptlrpc_nrs_resource **resp,
+ bool moving_req)
+{
+ struct ptlrpc_nrs_policy *primary = NULL;
+ struct ptlrpc_nrs_policy *fallback = NULL;
+
+ memset(resp, 0, sizeof(resp[0]) * NRS_RES_MAX);
+
+ /**
+ * Obtain policy references.
+ */
+ spin_lock(&nrs->nrs_lock);
+
+ fallback = nrs->nrs_policy_fallback;
+ nrs_policy_get_locked(fallback);
+
+ primary = nrs->nrs_policy_primary;
+ if (primary != NULL)
+ nrs_policy_get_locked(primary);
+
+ spin_unlock(&nrs->nrs_lock);
+
+ /**
+ * Obtain resource hierarchy references.
+ */
+ resp[NRS_RES_FALLBACK] = nrs_resource_get(fallback, nrq, moving_req);
+ LASSERT(resp[NRS_RES_FALLBACK] != NULL);
+
+ if (primary != NULL) {
+ resp[NRS_RES_PRIMARY] = nrs_resource_get(primary, nrq,
+ moving_req);
+ /**
+ * A primary policy may exist which may not wish to serve a
+ * particular request for different reasons; release the
+ * reference on the policy as it will not be used for this
+ * request.
+ */
+ if (resp[NRS_RES_PRIMARY] == NULL)
+ nrs_policy_put(primary);
+ }
+}
+
+/**
+ * Releases references to resource hierarchies and policies, because they are no
+ * longer required; used when request handling has been completed, or the
+ * request is moving to the high priority NRS head.
+ *
+ * \param resp the resource hierarchy that is being released
+ *
+ * \see ptlrpcnrs_req_hp_move()
+ * \see ptlrpc_nrs_req_finalize()
+ */
+static void nrs_resource_put_safe(struct ptlrpc_nrs_resource **resp)
+{
+ struct ptlrpc_nrs_policy *pols[NRS_RES_MAX];
+ struct ptlrpc_nrs *nrs = NULL;
+ int i;
+
+ for (i = 0; i < NRS_RES_MAX; i++) {
+ if (resp[i] != NULL) {
+ pols[i] = resp[i]->res_policy;
+ nrs_resource_put(resp[i]);
+ resp[i] = NULL;
+ } else {
+ pols[i] = NULL;
+ }
+ }
+
+ for (i = 0; i < NRS_RES_MAX; i++) {
+ if (pols[i] == NULL)
+ continue;
+
+ if (nrs == NULL) {
+ nrs = pols[i]->pol_nrs;
+ spin_lock(&nrs->nrs_lock);
+ }
+ nrs_policy_put_locked(pols[i]);
+ }
+
+ if (nrs != NULL)
+ spin_unlock(&nrs->nrs_lock);
+}
+
+/**
+ * Obtains an NRS request from \a policy for handling or examination; the
+ * request should be removed in the 'handling' case.
+ *
+ * Calling into this function implies we already know the policy has a request
+ * waiting to be handled.
+ *
+ * \param[in] policy the policy from which a request
+ * \param[in] peek when set, signifies that we just want to examine the
+ * request, and not handle it, so the request is not removed
+ * from the policy.
+ * \param[in] force when set, it will force a policy to return a request if it
+ * has one pending
+ *
+ * \retval the NRS request to be handled
+ */
+static inline
+struct ptlrpc_nrs_request *nrs_request_get(struct ptlrpc_nrs_policy *policy,
+ bool peek, bool force)
+{
+ struct ptlrpc_nrs_request *nrq;
+
+ LASSERT(policy->pol_req_queued > 0);
+
+ nrq = policy->pol_desc->pd_ops->op_req_get(policy, peek, force);
+
+ LASSERT(ergo(nrq != NULL, nrs_request_policy(nrq) == policy));
+
+ return nrq;
+}
+
+/**
+ * Enqueues request \a nrq for later handling, via one one the policies for
+ * which resources where earlier obtained via nrs_resource_get_safe(). The
+ * function attempts to enqueue the request first on the primary policy
+ * (if any), since this is the preferred choice.
+ *
+ * \param nrq the request being enqueued
+ *
+ * \see nrs_resource_get_safe()
+ */
+static inline void nrs_request_enqueue(struct ptlrpc_nrs_request *nrq)
+{
+ struct ptlrpc_nrs_policy *policy;
+ int rc;
+ int i;
+
+ /**
+ * Try in descending order, because the primary policy (if any) is
+ * the preferred choice.
+ */
+ for (i = NRS_RES_MAX - 1; i >= 0; i--) {
+ if (nrq->nr_res_ptrs[i] == NULL)
+ continue;
+
+ nrq->nr_res_idx = i;
+ policy = nrq->nr_res_ptrs[i]->res_policy;
+
+ rc = policy->pol_desc->pd_ops->op_req_enqueue(policy, nrq);
+ if (rc == 0) {
+ policy->pol_nrs->nrs_req_queued++;
+ policy->pol_req_queued++;
+ return;
+ }
+ }
+ /**
+ * Should never get here, as at least the primary policy's
+ * ptlrpc_nrs_pol_ops::op_req_enqueue() implementation should always
+ * succeed.
+ */
+ LBUG();
+}
+
+/**
+ * Called when a request has been handled
+ *
+ * \param[in] nrs the request that has been handled; can be used for
+ * job/resource control.
+ *
+ * \see ptlrpc_nrs_req_stop_nolock()
+ */
+static inline void nrs_request_stop(struct ptlrpc_nrs_request *nrq)
+{
+ struct ptlrpc_nrs_policy *policy = nrs_request_policy(nrq);
+
+ if (policy->pol_desc->pd_ops->op_req_stop)
+ policy->pol_desc->pd_ops->op_req_stop(policy, nrq);
+
+ LASSERT(policy->pol_nrs->nrs_req_started > 0);
+ LASSERT(policy->pol_req_started > 0);
+
+ policy->pol_nrs->nrs_req_started--;
+ policy->pol_req_started--;
+}
+
+/**
+ * Handler for operations that can be carried out on policies.
+ *
+ * Handles opcodes that are common to all policy types within NRS core, and
+ * passes any unknown opcodes to the policy-specific control function.
+ *
+ * \param[in] nrs the NRS head this policy belongs to.
+ * \param[in] name the human-readable policy name; should be the same as
+ * ptlrpc_nrs_pol_desc::pd_name.
+ * \param[in] opc the opcode of the operation being carried out.
+ * \param[in,out] arg can be used to pass information in and out between when
+ * carrying an operation; usually data that is private to
+ * the policy at some level, or generic policy status
+ * information.
+ *
+ * \retval -ve error condition
+ * \retval 0 operation was carried out successfully
+ */
+static int nrs_policy_ctl(struct ptlrpc_nrs *nrs, char *name,
+ enum ptlrpc_nrs_ctl opc, void *arg)
+{
+ struct ptlrpc_nrs_policy *policy;
+ int rc = 0;
+
+ spin_lock(&nrs->nrs_lock);
+
+ policy = nrs_policy_find_locked(nrs, name);
+ if (policy == NULL) {
+ rc = -ENOENT;
+ goto out;
+ }
+
+ switch (opc) {
+ /**
+ * Unknown opcode, pass it down to the policy-specific control
+ * function for handling.
+ */
+ default:
+ rc = nrs_policy_ctl_locked(policy, opc, arg);
+ break;
+
+ /**
+ * Start \e policy
+ */
+ case PTLRPC_NRS_CTL_START:
+ rc = nrs_policy_start_locked(policy);
+ break;
+ }
+out:
+ if (policy != NULL)
+ nrs_policy_put_locked(policy);
+
+ spin_unlock(&nrs->nrs_lock);
+
+ return rc;
+}
+
+/**
+ * Unregisters a policy by name.
+ *
+ * \param[in] nrs the NRS head this policy belongs to.
+ * \param[in] name the human-readable policy name; should be the same as
+ * ptlrpc_nrs_pol_desc::pd_name
+ *
+ * \retval -ve error
+ * \retval 0 success
+ */
+static int nrs_policy_unregister(struct ptlrpc_nrs *nrs, char *name)
+{
+ struct ptlrpc_nrs_policy *policy = NULL;
+
+ spin_lock(&nrs->nrs_lock);
+
+ policy = nrs_policy_find_locked(nrs, name);
+ if (policy == NULL) {
+ spin_unlock(&nrs->nrs_lock);
+
+ CERROR("Can't find NRS policy %s\n", name);
+ return -ENOENT;
+ }
+
+ if (policy->pol_ref > 1) {
+ CERROR("Policy %s is busy with %d references\n", name,
+ (int)policy->pol_ref);
+ nrs_policy_put_locked(policy);
+
+ spin_unlock(&nrs->nrs_lock);
+ return -EBUSY;
+ }
+
+ LASSERT(policy->pol_req_queued == 0);
+ LASSERT(policy->pol_req_started == 0);
+
+ if (policy->pol_state != NRS_POL_STATE_STOPPED) {
+ nrs_policy_stop_locked(policy);
+ LASSERT(policy->pol_state == NRS_POL_STATE_STOPPED);
+ }
+
+ list_del(&policy->pol_list);
+ nrs->nrs_num_pols--;
+
+ nrs_policy_put_locked(policy);
+
+ spin_unlock(&nrs->nrs_lock);
+
+ nrs_policy_fini(policy);
+
+ LASSERT(policy->pol_private == NULL);
+ OBD_FREE_PTR(policy);
+
+ return 0;
+}
+
+/**
+ * Register a policy from \policy descriptor \a desc with NRS head \a nrs.
+ *
+ * \param[in] nrs the NRS head on which the policy will be registered.
+ * \param[in] desc the policy descriptor from which the information will be
+ * obtained to register the policy.
+ *
+ * \retval -ve error
+ * \retval 0 success
+ */
+static int nrs_policy_register(struct ptlrpc_nrs *nrs,
+ struct ptlrpc_nrs_pol_desc *desc)
+{
+ struct ptlrpc_nrs_policy *policy;
+ struct ptlrpc_nrs_policy *tmp;
+ struct ptlrpc_service_part *svcpt = nrs->nrs_svcpt;
+ int rc;
+
+ LASSERT(svcpt != NULL);
+ LASSERT(desc->pd_ops != NULL);
+ LASSERT(desc->pd_ops->op_res_get != NULL);
+ LASSERT(desc->pd_ops->op_req_get != NULL);
+ LASSERT(desc->pd_ops->op_req_enqueue != NULL);
+ LASSERT(desc->pd_ops->op_req_dequeue != NULL);
+ LASSERT(desc->pd_compat != NULL);
+
+ OBD_CPT_ALLOC_GFP(policy, svcpt->scp_service->srv_cptable,
+ svcpt->scp_cpt, sizeof(*policy), GFP_NOFS);
+ if (policy == NULL)
+ return -ENOMEM;
+
+ policy->pol_nrs = nrs;
+ policy->pol_desc = desc;
+ policy->pol_state = NRS_POL_STATE_STOPPED;
+ policy->pol_flags = desc->pd_flags;
+
+ INIT_LIST_HEAD(&policy->pol_list);
+ INIT_LIST_HEAD(&policy->pol_list_queued);
+
+ rc = nrs_policy_init(policy);
+ if (rc != 0) {
+ OBD_FREE_PTR(policy);
+ return rc;
+ }
+
+ spin_lock(&nrs->nrs_lock);
+
+ tmp = nrs_policy_find_locked(nrs, policy->pol_desc->pd_name);
+ if (tmp != NULL) {
+ CERROR("NRS policy %s has been registered, can't register it for %s\n",
+ policy->pol_desc->pd_name,
+ svcpt->scp_service->srv_name);
+ nrs_policy_put_locked(tmp);
+
+ spin_unlock(&nrs->nrs_lock);
+ nrs_policy_fini(policy);
+ OBD_FREE_PTR(policy);
+
+ return -EEXIST;
+ }
+
+ list_add_tail(&policy->pol_list, &nrs->nrs_policy_list);
+ nrs->nrs_num_pols++;
+
+ if (policy->pol_flags & PTLRPC_NRS_FL_REG_START)
+ rc = nrs_policy_start_locked(policy);
+
+ spin_unlock(&nrs->nrs_lock);
+
+ if (rc != 0)
+ (void) nrs_policy_unregister(nrs, policy->pol_desc->pd_name);
+
+ return rc;
+}
+
+/**
+ * Enqueue request \a req using one of the policies its resources are referring
+ * to.
+ *
+ * \param[in] req the request to enqueue.
+ */
+static void ptlrpc_nrs_req_add_nolock(struct ptlrpc_request *req)
+{
+ struct ptlrpc_nrs_policy *policy;
+
+ LASSERT(req->rq_nrq.nr_initialized);
+ LASSERT(!req->rq_nrq.nr_enqueued);
+
+ nrs_request_enqueue(&req->rq_nrq);
+ req->rq_nrq.nr_enqueued = 1;
+
+ policy = nrs_request_policy(&req->rq_nrq);
+ /**
+ * Add the policy to the NRS head's list of policies with enqueued
+ * requests, if it has not been added there.
+ */
+ if (unlikely(list_empty(&policy->pol_list_queued)))
+ list_add_tail(&policy->pol_list_queued,
+ &policy->pol_nrs->nrs_policy_queued);
+}
+
+/**
+ * Enqueue a request on the high priority NRS head.
+ *
+ * \param req the request to enqueue.
+ */
+static void ptlrpc_nrs_hpreq_add_nolock(struct ptlrpc_request *req)
+{
+ int opc = lustre_msg_get_opc(req->rq_reqmsg);
+
+ spin_lock(&req->rq_lock);
+ req->rq_hp = 1;
+ ptlrpc_nrs_req_add_nolock(req);
+ if (opc != OBD_PING)
+ DEBUG_REQ(D_NET, req, "high priority req");
+ spin_unlock(&req->rq_lock);
+}
+
+/**
+ * Returns a boolean predicate indicating whether the policy described by
+ * \a desc is adequate for use with service \a svc.
+ *
+ * \param[in] svc the service
+ * \param[in] desc the policy descriptor
+ *
+ * \retval false the policy is not compatible with the service
+ * \retval true the policy is compatible with the service
+ */
+static inline bool nrs_policy_compatible(const struct ptlrpc_service *svc,
+ const struct ptlrpc_nrs_pol_desc *desc)
+{
+ return desc->pd_compat(svc, desc);
+}
+
+/**
+ * Registers all compatible policies in nrs_core.nrs_policies, for NRS head
+ * \a nrs.
+ *
+ * \param[in] nrs the NRS head
+ *
+ * \retval -ve error
+ * \retval 0 success
+ *
+ * \pre mutex_is_locked(&nrs_core.nrs_mutex)
+ *
+ * \see ptlrpc_service_nrs_setup()
+ */
+static int nrs_register_policies_locked(struct ptlrpc_nrs *nrs)
+{
+ struct ptlrpc_nrs_pol_desc *desc;
+ /* for convenience */
+ struct ptlrpc_service_part *svcpt = nrs->nrs_svcpt;
+ struct ptlrpc_service *svc = svcpt->scp_service;
+ int rc = -EINVAL;
+
+ LASSERT(mutex_is_locked(&nrs_core.nrs_mutex));
+
+ list_for_each_entry(desc, &nrs_core.nrs_policies, pd_list) {
+ if (nrs_policy_compatible(svc, desc)) {
+ rc = nrs_policy_register(nrs, desc);
+ if (rc != 0) {
+ CERROR("Failed to register NRS policy %s for partition %d of service %s: %d\n",
+ desc->pd_name, svcpt->scp_cpt,
+ svc->srv_name, rc);
+ /**
+ * Fail registration if any of the policies'
+ * registration fails.
+ */
+ break;
+ }
+ }
+ }
+
+ return rc;
+}
+
+/**
+ * Initializes NRS head \a nrs of service partition \a svcpt, and registers all
+ * compatible policies in NRS core, with the NRS head.
+ *
+ * \param[in] nrs the NRS head
+ * \param[in] svcpt the PTLRPC service partition to setup
+ *
+ * \retval -ve error
+ * \retval 0 success
+ *
+ * \pre mutex_is_locked(&nrs_core.nrs_mutex)
+ */
+static int nrs_svcpt_setup_locked0(struct ptlrpc_nrs *nrs,
+ struct ptlrpc_service_part *svcpt)
+{
+ enum ptlrpc_nrs_queue_type queue;
+
+ LASSERT(mutex_is_locked(&nrs_core.nrs_mutex));
+
+ if (nrs == &svcpt->scp_nrs_reg)
+ queue = PTLRPC_NRS_QUEUE_REG;
+ else if (nrs == svcpt->scp_nrs_hp)
+ queue = PTLRPC_NRS_QUEUE_HP;
+ else
+ LBUG();
+
+ nrs->nrs_svcpt = svcpt;
+ nrs->nrs_queue_type = queue;
+ spin_lock_init(&nrs->nrs_lock);
+ INIT_LIST_HEAD(&nrs->nrs_policy_list);
+ INIT_LIST_HEAD(&nrs->nrs_policy_queued);
+
+ return nrs_register_policies_locked(nrs);
+}
+
+/**
+ * Allocates a regular and optionally a high-priority NRS head (if the service
+ * handles high-priority RPCs), and then registers all available compatible
+ * policies on those NRS heads.
+ *
+ * \param[in,out] svcpt the PTLRPC service partition to setup
+ *
+ * \pre mutex_is_locked(&nrs_core.nrs_mutex)
+ */
+static int nrs_svcpt_setup_locked(struct ptlrpc_service_part *svcpt)
+{
+ struct ptlrpc_nrs *nrs;
+ int rc;
+
+ LASSERT(mutex_is_locked(&nrs_core.nrs_mutex));
+
+ /**
+ * Initialize the regular NRS head.
+ */
+ nrs = nrs_svcpt2nrs(svcpt, false);
+ rc = nrs_svcpt_setup_locked0(nrs, svcpt);
+ if (rc < 0)
+ goto out;
+
+ /**
+ * Optionally allocate a high-priority NRS head.
+ */
+ if (svcpt->scp_service->srv_ops.so_hpreq_handler == NULL)
+ goto out;
+
+ OBD_CPT_ALLOC_PTR(svcpt->scp_nrs_hp,
+ svcpt->scp_service->srv_cptable,
+ svcpt->scp_cpt);
+ if (svcpt->scp_nrs_hp == NULL) {
+ rc = -ENOMEM;
+ goto out;
+ }
+
+ nrs = nrs_svcpt2nrs(svcpt, true);
+ rc = nrs_svcpt_setup_locked0(nrs, svcpt);
+
+out:
+ return rc;
+}
+
+/**
+ * Unregisters all policies on all available NRS heads in a service partition;
+ * called at PTLRPC service unregistration time.
+ *
+ * \param[in] svcpt the PTLRPC service partition
+ *
+ * \pre mutex_is_locked(&nrs_core.nrs_mutex)
+ */
+static void nrs_svcpt_cleanup_locked(struct ptlrpc_service_part *svcpt)
+{
+ struct ptlrpc_nrs *nrs;
+ struct ptlrpc_nrs_policy *policy;
+ struct ptlrpc_nrs_policy *tmp;
+ int rc;
+ bool hp = false;
+
+ LASSERT(mutex_is_locked(&nrs_core.nrs_mutex));
+
+again:
+ nrs = nrs_svcpt2nrs(svcpt, hp);
+ nrs->nrs_stopping = 1;
+
+ list_for_each_entry_safe(policy, tmp, &nrs->nrs_policy_list,
+ pol_list) {
+ rc = nrs_policy_unregister(nrs, policy->pol_desc->pd_name);
+ LASSERT(rc == 0);
+ }
+
+ /**
+ * If the service partition has an HP NRS head, clean that up as well.
+ */
+ if (!hp && nrs_svcpt_has_hp(svcpt)) {
+ hp = true;
+ goto again;
+ }
+
+ if (hp)
+ OBD_FREE_PTR(nrs);
+}
+
+/**
+ * Returns the descriptor for a policy as identified by by \a name.
+ *
+ * \param[in] name the policy name
+ *
+ * \retval the policy descriptor
+ * \retval NULL
+ */
+static struct ptlrpc_nrs_pol_desc *nrs_policy_find_desc_locked(const char *name)
+{
+ struct ptlrpc_nrs_pol_desc *tmp;
+
+ list_for_each_entry(tmp, &nrs_core.nrs_policies, pd_list) {
+ if (strncmp(tmp->pd_name, name, NRS_POL_NAME_MAX) == 0)
+ return tmp;
+ }
+ return NULL;
+}
+
+/**
+ * Removes the policy from all supported NRS heads of all partitions of all
+ * PTLRPC services.
+ *
+ * \param[in] desc the policy descriptor to unregister
+ *
+ * \retval -ve error
+ * \retval 0 successfully unregistered policy on all supported NRS heads
+ *
+ * \pre mutex_is_locked(&nrs_core.nrs_mutex)
+ * \pre mutex_is_locked(&ptlrpc_all_services_mutex)
+ */
+static int nrs_policy_unregister_locked(struct ptlrpc_nrs_pol_desc *desc)
+{
+ struct ptlrpc_nrs *nrs;
+ struct ptlrpc_service *svc;
+ struct ptlrpc_service_part *svcpt;
+ int i;
+ int rc = 0;
+
+ LASSERT(mutex_is_locked(&nrs_core.nrs_mutex));
+ LASSERT(mutex_is_locked(&ptlrpc_all_services_mutex));
+
+ list_for_each_entry(svc, &ptlrpc_all_services, srv_list) {
+
+ if (!nrs_policy_compatible(svc, desc) ||
+ unlikely(svc->srv_is_stopping))
+ continue;
+
+ ptlrpc_service_for_each_part(svcpt, i, svc) {
+ bool hp = false;
+
+again:
+ nrs = nrs_svcpt2nrs(svcpt, hp);
+ rc = nrs_policy_unregister(nrs, desc->pd_name);
+ /**
+ * Ignore -ENOENT as the policy may not have registered
+ * successfully on all service partitions.
+ */
+ if (rc == -ENOENT) {
+ rc = 0;
+ } else if (rc != 0) {
+ CERROR("Failed to unregister NRS policy %s for partition %d of service %s: %d\n",
+ desc->pd_name, svcpt->scp_cpt,
+ svcpt->scp_service->srv_name, rc);
+ return rc;
+ }
+
+ if (!hp && nrs_svc_has_hp(svc)) {
+ hp = true;
+ goto again;
+ }
+ }
+
+ if (desc->pd_ops->op_lprocfs_fini != NULL)
+ desc->pd_ops->op_lprocfs_fini(svc);
+ }
+
+ return rc;
+}
+
+/**
+ * Registers a new policy with NRS core.
+ *
+ * The function will only succeed if policy registration with all compatible
+ * service partitions (if any) is successful.
+ *
+ * N.B. This function should be called either at ptlrpc module initialization
+ * time when registering a policy that ships with NRS core, or in a
+ * module's init() function for policies registering from other modules.
+ *
+ * \param[in] conf configuration information for the new policy to register
+ *
+ * \retval -ve error
+ * \retval 0 success
+ */
+int ptlrpc_nrs_policy_register(struct ptlrpc_nrs_pol_conf *conf)
+{
+ struct ptlrpc_service *svc;
+ struct ptlrpc_nrs_pol_desc *desc;
+ int rc = 0;
+
+ LASSERT(conf != NULL);
+ LASSERT(conf->nc_ops != NULL);
+ LASSERT(conf->nc_compat != NULL);
+ LASSERT(ergo(conf->nc_compat == nrs_policy_compat_one,
+ conf->nc_compat_svc_name != NULL));
+ LASSERT(ergo((conf->nc_flags & PTLRPC_NRS_FL_REG_EXTERN) != 0,
+ conf->nc_owner != NULL));
+
+ conf->nc_name[NRS_POL_NAME_MAX - 1] = '\0';
+
+ /**
+ * External policies are not allowed to start immediately upon
+ * registration, as there is a relatively higher chance that their
+ * registration might fail. In such a case, some policy instances may
+ * already have requests queued wen unregistration needs to happen as
+ * part o cleanup; since there is currently no way to drain requests
+ * from a policy unless the service is unregistering, we just disallow
+ * this.
+ */
+ if ((conf->nc_flags & PTLRPC_NRS_FL_REG_EXTERN) &&
+ (conf->nc_flags & (PTLRPC_NRS_FL_FALLBACK |
+ PTLRPC_NRS_FL_REG_START))) {
+ CERROR("NRS: failing to register policy %s. Please check policy flags; external policies cannot act as fallback policies, or be started immediately upon registration without interaction with lprocfs\n",
+ conf->nc_name);
+ return -EINVAL;
+ }
+
+ mutex_lock(&nrs_core.nrs_mutex);
+
+ if (nrs_policy_find_desc_locked(conf->nc_name) != NULL) {
+ CERROR("NRS: failing to register policy %s which has already been registered with NRS core!\n",
+ conf->nc_name);
+ rc = -EEXIST;
+ goto fail;
+ }
+
+ OBD_ALLOC_PTR(desc);
+ if (desc == NULL) {
+ rc = -ENOMEM;
+ goto fail;
+ }
+
+ strncpy(desc->pd_name, conf->nc_name, NRS_POL_NAME_MAX);
+ desc->pd_ops = conf->nc_ops;
+ desc->pd_compat = conf->nc_compat;
+ desc->pd_compat_svc_name = conf->nc_compat_svc_name;
+ if ((conf->nc_flags & PTLRPC_NRS_FL_REG_EXTERN) != 0)
+ desc->pd_owner = conf->nc_owner;
+ desc->pd_flags = conf->nc_flags;
+ atomic_set(&desc->pd_refs, 0);
+
+ /**
+ * For policies that are held in the same module as NRS (currently
+ * ptlrpc), do not register the policy with all compatible services,
+ * as the services will not have started at this point, since we are
+ * calling from ptlrpc module initialization code. In such cases each
+ * service will register all compatible policies later, via
+ * ptlrpc_service_nrs_setup().
+ */
+ if ((conf->nc_flags & PTLRPC_NRS_FL_REG_EXTERN) == 0)
+ goto internal;
+
+ /**
+ * Register the new policy on all compatible services
+ */
+ mutex_lock(&ptlrpc_all_services_mutex);
+
+ list_for_each_entry(svc, &ptlrpc_all_services, srv_list) {
+ struct ptlrpc_service_part *svcpt;
+ int i;
+ int rc2;
+
+ if (!nrs_policy_compatible(svc, desc) ||
+ unlikely(svc->srv_is_stopping))
+ continue;
+
+ ptlrpc_service_for_each_part(svcpt, i, svc) {
+ struct ptlrpc_nrs *nrs;
+ bool hp = false;
+again:
+ nrs = nrs_svcpt2nrs(svcpt, hp);
+ rc = nrs_policy_register(nrs, desc);
+ if (rc != 0) {
+ CERROR("Failed to register NRS policy %s for partition %d of service %s: %d\n",
+ desc->pd_name, svcpt->scp_cpt,
+ svcpt->scp_service->srv_name, rc);
+
+ rc2 = nrs_policy_unregister_locked(desc);
+ /**
+ * Should not fail at this point
+ */
+ LASSERT(rc2 == 0);
+ mutex_unlock(&ptlrpc_all_services_mutex);
+ OBD_FREE_PTR(desc);
+ goto fail;
+ }
+
+ if (!hp && nrs_svc_has_hp(svc)) {
+ hp = true;
+ goto again;
+ }
+ }
+
+ /**
+ * No need to take a reference to other modules here, as we
+ * will be calling from the module's init() function.
+ */
+ if (desc->pd_ops->op_lprocfs_init != NULL) {
+ rc = desc->pd_ops->op_lprocfs_init(svc);
+ if (rc != 0) {
+ rc2 = nrs_policy_unregister_locked(desc);
+ /**
+ * Should not fail at this point
+ */
+ LASSERT(rc2 == 0);
+ mutex_unlock(&ptlrpc_all_services_mutex);
+ OBD_FREE_PTR(desc);
+ goto fail;
+ }
+ }
+ }
+
+ mutex_unlock(&ptlrpc_all_services_mutex);
+internal:
+ list_add_tail(&desc->pd_list, &nrs_core.nrs_policies);
+fail:
+ mutex_unlock(&nrs_core.nrs_mutex);
+
+ return rc;
+}
+EXPORT_SYMBOL(ptlrpc_nrs_policy_register);
+
+/**
+ * Unregisters a previously registered policy with NRS core. All instances of
+ * the policy on all NRS heads of all supported services are removed.
+ *
+ * N.B. This function should only be called from a module's exit() function.
+ * Although it can be used for policies that ship alongside NRS core, the
+ * function is primarily intended for policies that register externally,
+ * from other modules.
+ *
+ * \param[in] conf configuration information for the policy to unregister
+ *
+ * \retval -ve error
+ * \retval 0 success
+ */
+int ptlrpc_nrs_policy_unregister(struct ptlrpc_nrs_pol_conf *conf)
+{
+ struct ptlrpc_nrs_pol_desc *desc;
+ int rc;
+
+ LASSERT(conf != NULL);
+
+ if (conf->nc_flags & PTLRPC_NRS_FL_FALLBACK) {
+ CERROR("Unable to unregister a fallback policy, unless the PTLRPC service is stopping.\n");
+ return -EPERM;
+ }
+
+ conf->nc_name[NRS_POL_NAME_MAX - 1] = '\0';
+
+ mutex_lock(&nrs_core.nrs_mutex);
+
+ desc = nrs_policy_find_desc_locked(conf->nc_name);
+ if (desc == NULL) {
+ CERROR("Failing to unregister NRS policy %s which has not been registered with NRS core!\n",
+ conf->nc_name);
+ rc = -ENOENT;
+ goto not_exist;
+ }
+
+ mutex_lock(&ptlrpc_all_services_mutex);
+
+ rc = nrs_policy_unregister_locked(desc);
+ if (rc < 0) {
+ if (rc == -EBUSY)
+ CERROR("Please first stop policy %s on all service partitions and then retry to unregister the policy.\n",
+ conf->nc_name);
+ goto fail;
+ }
+
+ CDEBUG(D_INFO, "Unregistering policy %s from NRS core.\n",
+ conf->nc_name);
+
+ list_del(&desc->pd_list);
+ OBD_FREE_PTR(desc);
+
+fail:
+ mutex_unlock(&ptlrpc_all_services_mutex);
+
+not_exist:
+ mutex_unlock(&nrs_core.nrs_mutex);
+
+ return rc;
+}
+EXPORT_SYMBOL(ptlrpc_nrs_policy_unregister);
+
+/**
+ * Setup NRS heads on all service partitions of service \a svc, and register
+ * all compatible policies on those NRS heads.
+ *
+ * To be called from within ptl
+ * \param[in] svc the service to setup
+ *
+ * \retval -ve error, the calling logic should eventually call
+ * ptlrpc_service_nrs_cleanup() to undo any work performed
+ * by this function.
+ *
+ * \see ptlrpc_register_service()
+ * \see ptlrpc_service_nrs_cleanup()
+ */
+int ptlrpc_service_nrs_setup(struct ptlrpc_service *svc)
+{
+ struct ptlrpc_service_part *svcpt;
+ const struct ptlrpc_nrs_pol_desc *desc;
+ int i;
+ int rc = 0;
+
+ mutex_lock(&nrs_core.nrs_mutex);
+
+ /**
+ * Initialize NRS heads on all service CPTs.
+ */
+ ptlrpc_service_for_each_part(svcpt, i, svc) {
+ rc = nrs_svcpt_setup_locked(svcpt);
+ if (rc != 0)
+ goto failed;
+ }
+
+ /**
+ * Set up lprocfs interfaces for all supported policies for the
+ * service.
+ */
+ list_for_each_entry(desc, &nrs_core.nrs_policies, pd_list) {
+ if (!nrs_policy_compatible(svc, desc))
+ continue;
+
+ if (desc->pd_ops->op_lprocfs_init != NULL) {
+ rc = desc->pd_ops->op_lprocfs_init(svc);
+ if (rc != 0)
+ goto failed;
+ }
+ }
+
+failed:
+
+ mutex_unlock(&nrs_core.nrs_mutex);
+
+ return rc;
+}
+
+/**
+ * Unregisters all policies on all service partitions of service \a svc.
+ *
+ * \param[in] svc the PTLRPC service to unregister
+ */
+void ptlrpc_service_nrs_cleanup(struct ptlrpc_service *svc)
+{
+ struct ptlrpc_service_part *svcpt;
+ const struct ptlrpc_nrs_pol_desc *desc;
+ int i;
+
+ mutex_lock(&nrs_core.nrs_mutex);
+
+ /**
+ * Clean up NRS heads on all service partitions
+ */
+ ptlrpc_service_for_each_part(svcpt, i, svc)
+ nrs_svcpt_cleanup_locked(svcpt);
+
+ /**
+ * Clean up lprocfs interfaces for all supported policies for the
+ * service.
+ */
+ list_for_each_entry(desc, &nrs_core.nrs_policies, pd_list) {
+ if (!nrs_policy_compatible(svc, desc))
+ continue;
+
+ if (desc->pd_ops->op_lprocfs_fini != NULL)
+ desc->pd_ops->op_lprocfs_fini(svc);
+ }
+
+ mutex_unlock(&nrs_core.nrs_mutex);
+}
+
+/**
+ * Obtains NRS head resources for request \a req.
+ *
+ * These could be either on the regular or HP NRS head of \a svcpt; resources
+ * taken on the regular head can later be swapped for HP head resources by
+ * ldlm_lock_reorder_req().
+ *
+ * \param[in] svcpt the service partition
+ * \param[in] req the request
+ * \param[in] hp which NRS head of \a svcpt to use
+ */
+void ptlrpc_nrs_req_initialize(struct ptlrpc_service_part *svcpt,
+ struct ptlrpc_request *req, bool hp)
+{
+ struct ptlrpc_nrs *nrs = nrs_svcpt2nrs(svcpt, hp);
+
+ memset(&req->rq_nrq, 0, sizeof(req->rq_nrq));
+ nrs_resource_get_safe(nrs, &req->rq_nrq, req->rq_nrq.nr_res_ptrs,
+ false);
+
+ /**
+ * It is fine to access \e nr_initialized without locking as there is
+ * no contention at this early stage.
+ */
+ req->rq_nrq.nr_initialized = 1;
+}
+
+/**
+ * Releases resources for a request; is called after the request has been
+ * handled.
+ *
+ * \param[in] req the request
+ *
+ * \see ptlrpc_server_finish_request()
+ */
+void ptlrpc_nrs_req_finalize(struct ptlrpc_request *req)
+{
+ if (req->rq_nrq.nr_initialized) {
+ nrs_resource_put_safe(req->rq_nrq.nr_res_ptrs);
+ /* no protection on bit nr_initialized because no
+ * contention at this late stage */
+ req->rq_nrq.nr_finalized = 1;
+ }
+}
+
+void ptlrpc_nrs_req_stop_nolock(struct ptlrpc_request *req)
+{
+ if (req->rq_nrq.nr_started)
+ nrs_request_stop(&req->rq_nrq);
+}
+
+/**
+ * Enqueues request \a req on either the regular or high-priority NRS head
+ * of service partition \a svcpt.
+ *
+ * \param[in] svcpt the service partition
+ * \param[in] req the request to be enqueued
+ * \param[in] hp whether to enqueue the request on the regular or
+ * high-priority NRS head.
+ */
+void ptlrpc_nrs_req_add(struct ptlrpc_service_part *svcpt,
+ struct ptlrpc_request *req, bool hp)
+{
+ spin_lock(&svcpt->scp_req_lock);
+
+ if (hp)
+ ptlrpc_nrs_hpreq_add_nolock(req);
+ else
+ ptlrpc_nrs_req_add_nolock(req);
+
+ spin_unlock(&svcpt->scp_req_lock);
+}
+
+static void nrs_request_removed(struct ptlrpc_nrs_policy *policy)
+{
+ LASSERT(policy->pol_nrs->nrs_req_queued > 0);
+ LASSERT(policy->pol_req_queued > 0);
+
+ policy->pol_nrs->nrs_req_queued--;
+ policy->pol_req_queued--;
+
+ /**
+ * If the policy has no more requests queued, remove it from
+ * ptlrpc_nrs::nrs_policy_queued.
+ */
+ if (unlikely(policy->pol_req_queued == 0)) {
+ list_del_init(&policy->pol_list_queued);
+
+ /**
+ * If there are other policies with queued requests, move the
+ * current policy to the end so that we can round robin over
+ * all policies and drain the requests.
+ */
+ } else if (policy->pol_req_queued != policy->pol_nrs->nrs_req_queued) {
+ LASSERT(policy->pol_req_queued <
+ policy->pol_nrs->nrs_req_queued);
+
+ list_move_tail(&policy->pol_list_queued,
+ &policy->pol_nrs->nrs_policy_queued);
+ }
+}
+
+/**
+ * Obtains a request for handling from an NRS head of service partition
+ * \a svcpt.
+ *
+ * \param[in] svcpt the service partition
+ * \param[in] hp whether to obtain a request from the regular or
+ * high-priority NRS head.
+ * \param[in] peek when set, signifies that we just want to examine the
+ * request, and not handle it, so the request is not removed
+ * from the policy.
+ * \param[in] force when set, it will force a policy to return a request if it
+ * has one pending
+ *
+ * \retval the request to be handled
+ * \retval NULL the head has no requests to serve
+ */
+struct ptlrpc_request *
+ptlrpc_nrs_req_get_nolock0(struct ptlrpc_service_part *svcpt, bool hp,
+ bool peek, bool force)
+{
+ struct ptlrpc_nrs *nrs = nrs_svcpt2nrs(svcpt, hp);
+ struct ptlrpc_nrs_policy *policy;
+ struct ptlrpc_nrs_request *nrq;
+
+ /**
+ * Always try to drain requests from all NRS polices even if they are
+ * inactive, because the user can change policy status at runtime.
+ */
+ list_for_each_entry(policy, &nrs->nrs_policy_queued,
+ pol_list_queued) {
+ nrq = nrs_request_get(policy, peek, force);
+ if (nrq != NULL) {
+ if (likely(!peek)) {
+ nrq->nr_started = 1;
+
+ policy->pol_req_started++;
+ policy->pol_nrs->nrs_req_started++;
+
+ nrs_request_removed(policy);
+ }
+
+ return container_of(nrq, struct ptlrpc_request, rq_nrq);
+ }
+ }
+
+ return NULL;
+}
+
+/**
+ * Dequeues request \a req from the policy it has been enqueued on.
+ *
+ * \param[in] req the request
+ */
+void ptlrpc_nrs_req_del_nolock(struct ptlrpc_request *req)
+{
+ struct ptlrpc_nrs_policy *policy = nrs_request_policy(&req->rq_nrq);
+
+ policy->pol_desc->pd_ops->op_req_dequeue(policy, &req->rq_nrq);
+
+ req->rq_nrq.nr_enqueued = 0;
+
+ nrs_request_removed(policy);
+}
+
+/**
+ * Returns whether there are any requests currently enqueued on any of the
+ * policies of service partition's \a svcpt NRS head specified by \a hp. Should
+ * be called while holding ptlrpc_service_part::scp_req_lock to get a reliable
+ * result.
+ *
+ * \param[in] svcpt the service partition to enquire.
+ * \param[in] hp whether the regular or high-priority NRS head is to be
+ * enquired.
+ *
+ * \retval false the indicated NRS head has no enqueued requests.
+ * \retval true the indicated NRS head has some enqueued requests.
+ */
+bool ptlrpc_nrs_req_pending_nolock(struct ptlrpc_service_part *svcpt, bool hp)
+{
+ struct ptlrpc_nrs *nrs = nrs_svcpt2nrs(svcpt, hp);
+
+ return nrs->nrs_req_queued > 0;
+};
+
+/**
+ * Moves request \a req from the regular to the high-priority NRS head.
+ *
+ * \param[in] req the request to move
+ */
+void ptlrpc_nrs_req_hp_move(struct ptlrpc_request *req)
+{
+ struct ptlrpc_service_part *svcpt = req->rq_rqbd->rqbd_svcpt;
+ struct ptlrpc_nrs_request *nrq = &req->rq_nrq;
+ struct ptlrpc_nrs_resource *res1[NRS_RES_MAX];
+ struct ptlrpc_nrs_resource *res2[NRS_RES_MAX];
+
+ /**
+ * Obtain the high-priority NRS head resources.
+ */
+ nrs_resource_get_safe(nrs_svcpt2nrs(svcpt, true), nrq, res1, true);
+
+ spin_lock(&svcpt->scp_req_lock);
+
+ if (!ptlrpc_nrs_req_can_move(req))
+ goto out;
+
+ ptlrpc_nrs_req_del_nolock(req);
+
+ memcpy(res2, nrq->nr_res_ptrs, NRS_RES_MAX * sizeof(res2[0]));
+ memcpy(nrq->nr_res_ptrs, res1, NRS_RES_MAX * sizeof(res1[0]));
+
+ ptlrpc_nrs_hpreq_add_nolock(req);
+
+ memcpy(res1, res2, NRS_RES_MAX * sizeof(res1[0]));
+out:
+ spin_unlock(&svcpt->scp_req_lock);
+
+ /**
+ * Release either the regular NRS head resources if we moved the
+ * request, or the high-priority NRS head resources if we took a
+ * reference earlier in this function and ptlrpc_nrs_req_can_move()
+ * returned false.
+ */
+ nrs_resource_put_safe(res1);
+}
+
+/**
+ * Carries out a control operation \a opc on the policy identified by the
+ * human-readable \a name, on either all partitions, or only on the first
+ * partition of service \a svc.
+ *
+ * \param[in] svc the service the policy belongs to.
+ * \param[in] queue whether to carry out the command on the policy which
+ * belongs to the regular, high-priority, or both NRS
+ * heads of service partitions of \a svc.
+ * \param[in] name the policy to act upon, by human-readable name
+ * \param[in] opc the opcode of the operation to carry out
+ * \param[in] single when set, the operation will only be carried out on the
+ * NRS heads of the first service partition of \a svc.
+ * This is useful for some policies which e.g. share
+ * identical values on the same parameters of different
+ * service partitions; when reading these parameters via
+ * lprocfs, these policies may just want to obtain and
+ * print out the values from the first service partition.
+ * Storing these values centrally elsewhere then could be
+ * another solution for this.
+ * \param[in,out] arg can be used as a generic in/out buffer between control
+ * operations and the user environment.
+ *
+ *\retval -ve error condition
+ *\retval 0 operation was carried out successfully
+ */
+int ptlrpc_nrs_policy_control(const struct ptlrpc_service *svc,
+ enum ptlrpc_nrs_queue_type queue, char *name,
+ enum ptlrpc_nrs_ctl opc, bool single, void *arg)
+{
+ struct ptlrpc_service_part *svcpt;
+ int i;
+ int rc = 0;
+
+ LASSERT(opc != PTLRPC_NRS_CTL_INVALID);
+
+ if ((queue & PTLRPC_NRS_QUEUE_BOTH) == 0)
+ return -EINVAL;
+
+ ptlrpc_service_for_each_part(svcpt, i, svc) {
+ if ((queue & PTLRPC_NRS_QUEUE_REG) != 0) {
+ rc = nrs_policy_ctl(nrs_svcpt2nrs(svcpt, false), name,
+ opc, arg);
+ if (rc != 0 || (queue == PTLRPC_NRS_QUEUE_REG &&
+ single))
+ goto out;
+ }
+
+ if ((queue & PTLRPC_NRS_QUEUE_HP) != 0) {
+ /**
+ * XXX: We could optionally check for
+ * nrs_svc_has_hp(svc) here, and return an error if it
+ * is false. Right now we rely on the policies' lprocfs
+ * handlers that call the present function to make this
+ * check; if they fail to do so, they might hit the
+ * assertion inside nrs_svcpt2nrs() below.
+ */
+ rc = nrs_policy_ctl(nrs_svcpt2nrs(svcpt, true), name,
+ opc, arg);
+ if (rc != 0 || single)
+ goto out;
+ }
+ }
+out:
+ return rc;
+}
+
+
+/* ptlrpc/nrs_fifo.c */
+extern struct ptlrpc_nrs_pol_conf nrs_conf_fifo;
+
+/**
+ * Adds all policies that ship with the ptlrpc module, to NRS core's list of
+ * policies \e nrs_core.nrs_policies.
+ *
+ * \retval 0 all policies have been registered successfully
+ * \retval -ve error
+ */
+int ptlrpc_nrs_init(void)
+{
+ int rc;
+
+ mutex_init(&nrs_core.nrs_mutex);
+ INIT_LIST_HEAD(&nrs_core.nrs_policies);
+
+ rc = ptlrpc_nrs_policy_register(&nrs_conf_fifo);
+ if (rc != 0)
+ goto fail;
+
+
+ return rc;
+fail:
+ /**
+ * Since no PTLRPC services have been started at this point, all we need
+ * to do for cleanup is to free the descriptors.
+ */
+ ptlrpc_nrs_fini();
+
+ return rc;
+}
+
+/**
+ * Removes all policy descriptors from nrs_core::nrs_policies, and frees the
+ * policy descriptors.
+ *
+ * Since all PTLRPC services are stopped at this point, there are no more
+ * instances of any policies, because each service will have stopped its policy
+ * instances in ptlrpc_service_nrs_cleanup(), so we just need to free the
+ * descriptors here.
+ */
+void ptlrpc_nrs_fini(void)
+{
+ struct ptlrpc_nrs_pol_desc *desc;
+ struct ptlrpc_nrs_pol_desc *tmp;
+
+ list_for_each_entry_safe(desc, tmp, &nrs_core.nrs_policies,
+ pd_list) {
+ list_del_init(&desc->pd_list);
+ OBD_FREE_PTR(desc);
+ }
+}
+
+/** @} nrs */
diff --git a/drivers/staging/lustre/lustre/ptlrpc/nrs_fifo.c b/drivers/staging/lustre/lustre/ptlrpc/nrs_fifo.c
new file mode 100644
index 000000000..eb40c01db
--- /dev/null
+++ b/drivers/staging/lustre/lustre/ptlrpc/nrs_fifo.c
@@ -0,0 +1,270 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+
+ * 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 version 2 for more details. A copy is
+ * included in the COPYING file that accompanied this code.
+
+ * 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
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2011 Intel Corporation
+ *
+ * Copyright 2012 Xyratex Technology Limited
+ */
+/*
+ * lustre/ptlrpc/nrs_fifo.c
+ *
+ * Network Request Scheduler (NRS) FIFO policy
+ *
+ * Handles RPCs in a FIFO manner, as received from the network. This policy is
+ * a logical wrapper around previous, non-NRS functionality. It is used as the
+ * default and fallback policy for all types of RPCs on all PTLRPC service
+ * partitions, for both regular and high-priority NRS heads. Default here means
+ * the policy is the one enabled at PTLRPC service partition startup time, and
+ * fallback means the policy is used to handle RPCs that are not handled
+ * successfully or are not handled at all by any primary policy that may be
+ * enabled on a given NRS head.
+ *
+ * Author: Liang Zhen <liang@whamcloud.com>
+ * Author: Nikitas Angelinas <nikitas_angelinas@xyratex.com>
+ */
+/**
+ * \addtogoup nrs
+ * @{
+ */
+
+#define DEBUG_SUBSYSTEM S_RPC
+#include "../include/obd_support.h"
+#include "../include/obd_class.h"
+#include "../../include/linux/libcfs/libcfs.h"
+#include "ptlrpc_internal.h"
+
+/**
+ * \name fifo
+ *
+ * The FIFO policy is a logical wrapper around previous, non-NRS functionality.
+ * It schedules RPCs in the same order as they are queued from LNet.
+ *
+ * @{
+ */
+
+#define NRS_POL_NAME_FIFO "fifo"
+
+/**
+ * Is called before the policy transitions into
+ * ptlrpc_nrs_pol_state::NRS_POL_STATE_STARTED; allocates and initializes a
+ * policy-specific private data structure.
+ *
+ * \param[in] policy The policy to start
+ *
+ * \retval -ENOMEM OOM error
+ * \retval 0 success
+ *
+ * \see nrs_policy_register()
+ * \see nrs_policy_ctl()
+ */
+static int nrs_fifo_start(struct ptlrpc_nrs_policy *policy)
+{
+ struct nrs_fifo_head *head;
+
+ OBD_CPT_ALLOC_PTR(head, nrs_pol2cptab(policy), nrs_pol2cptid(policy));
+ if (head == NULL)
+ return -ENOMEM;
+
+ INIT_LIST_HEAD(&head->fh_list);
+ policy->pol_private = head;
+ return 0;
+}
+
+/**
+ * Is called before the policy transitions into
+ * ptlrpc_nrs_pol_state::NRS_POL_STATE_STOPPED; deallocates the policy-specific
+ * private data structure.
+ *
+ * \param[in] policy The policy to stop
+ *
+ * \see nrs_policy_stop0()
+ */
+static void nrs_fifo_stop(struct ptlrpc_nrs_policy *policy)
+{
+ struct nrs_fifo_head *head = policy->pol_private;
+
+ LASSERT(head != NULL);
+ LASSERT(list_empty(&head->fh_list));
+
+ OBD_FREE_PTR(head);
+}
+
+/**
+ * Is called for obtaining a FIFO policy resource.
+ *
+ * \param[in] policy The policy on which the request is being asked for
+ * \param[in] nrq The request for which resources are being taken
+ * \param[in] parent Parent resource, unused in this policy
+ * \param[out] resp Resources references are placed in this array
+ * \param[in] moving_req Signifies limited caller context; unused in this
+ * policy
+ *
+ * \retval 1 The FIFO policy only has a one-level resource hierarchy, as since
+ * it implements a simple scheduling algorithm in which request
+ * priority is determined on the request arrival order, it does not
+ * need to maintain a set of resources that would otherwise be used
+ * to calculate a request's priority.
+ *
+ * \see nrs_resource_get_safe()
+ */
+static int nrs_fifo_res_get(struct ptlrpc_nrs_policy *policy,
+ struct ptlrpc_nrs_request *nrq,
+ const struct ptlrpc_nrs_resource *parent,
+ struct ptlrpc_nrs_resource **resp, bool moving_req)
+{
+ /**
+ * Just return the resource embedded inside nrs_fifo_head, and end this
+ * resource hierarchy reference request.
+ */
+ *resp = &((struct nrs_fifo_head *)policy->pol_private)->fh_res;
+ return 1;
+}
+
+/**
+ * Called when getting a request from the FIFO policy for handling, or just
+ * peeking; removes the request from the policy when it is to be handled.
+ *
+ * \param[in] policy The policy
+ * \param[in] peek When set, signifies that we just want to examine the
+ * request, and not handle it, so the request is not removed
+ * from the policy.
+ * \param[in] force Force the policy to return a request; unused in this
+ * policy
+ *
+ * \retval The request to be handled; this is the next request in the FIFO
+ * queue
+ *
+ * \see ptlrpc_nrs_req_get_nolock()
+ * \see nrs_request_get()
+ */
+static
+struct ptlrpc_nrs_request *nrs_fifo_req_get(struct ptlrpc_nrs_policy *policy,
+ bool peek, bool force)
+{
+ struct nrs_fifo_head *head = policy->pol_private;
+ struct ptlrpc_nrs_request *nrq;
+
+ nrq = unlikely(list_empty(&head->fh_list)) ? NULL :
+ list_entry(head->fh_list.next, struct ptlrpc_nrs_request,
+ nr_u.fifo.fr_list);
+
+ if (likely(!peek && nrq != NULL)) {
+ struct ptlrpc_request *req = container_of(nrq,
+ struct ptlrpc_request,
+ rq_nrq);
+
+ list_del_init(&nrq->nr_u.fifo.fr_list);
+
+ CDEBUG(D_RPCTRACE, "NRS start %s request from %s, seq: %llu\n",
+ policy->pol_desc->pd_name, libcfs_id2str(req->rq_peer),
+ nrq->nr_u.fifo.fr_sequence);
+ }
+
+ return nrq;
+}
+
+/**
+ * Adds request \a nrq to \a policy's list of queued requests
+ *
+ * \param[in] policy The policy
+ * \param[in] nrq The request to add
+ *
+ * \retval 0 success; nrs_request_enqueue() assumes this function will always
+ * succeed
+ */
+static int nrs_fifo_req_add(struct ptlrpc_nrs_policy *policy,
+ struct ptlrpc_nrs_request *nrq)
+{
+ struct nrs_fifo_head *head;
+
+ head = container_of(nrs_request_resource(nrq), struct nrs_fifo_head,
+ fh_res);
+ /**
+ * Only used for debugging
+ */
+ nrq->nr_u.fifo.fr_sequence = head->fh_sequence++;
+ list_add_tail(&nrq->nr_u.fifo.fr_list, &head->fh_list);
+
+ return 0;
+}
+
+/**
+ * Removes request \a nrq from \a policy's list of queued requests.
+ *
+ * \param[in] policy The policy
+ * \param[in] nrq The request to remove
+ */
+static void nrs_fifo_req_del(struct ptlrpc_nrs_policy *policy,
+ struct ptlrpc_nrs_request *nrq)
+{
+ LASSERT(!list_empty(&nrq->nr_u.fifo.fr_list));
+ list_del_init(&nrq->nr_u.fifo.fr_list);
+}
+
+/**
+ * Prints a debug statement right before the request \a nrq stops being
+ * handled.
+ *
+ * \param[in] policy The policy handling the request
+ * \param[in] nrq The request being handled
+ *
+ * \see ptlrpc_server_finish_request()
+ * \see ptlrpc_nrs_req_stop_nolock()
+ */
+static void nrs_fifo_req_stop(struct ptlrpc_nrs_policy *policy,
+ struct ptlrpc_nrs_request *nrq)
+{
+ struct ptlrpc_request *req = container_of(nrq, struct ptlrpc_request,
+ rq_nrq);
+
+ CDEBUG(D_RPCTRACE, "NRS stop %s request from %s, seq: %llu\n",
+ policy->pol_desc->pd_name, libcfs_id2str(req->rq_peer),
+ nrq->nr_u.fifo.fr_sequence);
+}
+
+/**
+ * FIFO policy operations
+ */
+static const struct ptlrpc_nrs_pol_ops nrs_fifo_ops = {
+ .op_policy_start = nrs_fifo_start,
+ .op_policy_stop = nrs_fifo_stop,
+ .op_res_get = nrs_fifo_res_get,
+ .op_req_get = nrs_fifo_req_get,
+ .op_req_enqueue = nrs_fifo_req_add,
+ .op_req_dequeue = nrs_fifo_req_del,
+ .op_req_stop = nrs_fifo_req_stop,
+};
+
+/**
+ * FIFO policy configuration
+ */
+struct ptlrpc_nrs_pol_conf nrs_conf_fifo = {
+ .nc_name = NRS_POL_NAME_FIFO,
+ .nc_ops = &nrs_fifo_ops,
+ .nc_compat = nrs_policy_compat_all,
+ .nc_flags = PTLRPC_NRS_FL_FALLBACK |
+ PTLRPC_NRS_FL_REG_START
+};
+
+/** @} fifo */
+
+/** @} nrs */
diff --git a/drivers/staging/lustre/lustre/ptlrpc/pack_generic.c b/drivers/staging/lustre/lustre/ptlrpc/pack_generic.c
new file mode 100644
index 000000000..b51af9bf3
--- /dev/null
+++ b/drivers/staging/lustre/lustre/ptlrpc/pack_generic.c
@@ -0,0 +1,2536 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2002, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2011, 2012, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ *
+ * lustre/ptlrpc/pack_generic.c
+ *
+ * (Un)packing of OST requests
+ *
+ * Author: Peter J. Braam <braam@clusterfs.com>
+ * Author: Phil Schwan <phil@clusterfs.com>
+ * Author: Eric Barton <eeb@clusterfs.com>
+ */
+
+#define DEBUG_SUBSYSTEM S_RPC
+
+#include "../../include/linux/libcfs/libcfs.h"
+
+#include "../include/obd_support.h"
+#include "../include/obd_class.h"
+#include "../include/lustre_net.h"
+#include "../include/obd_cksum.h"
+#include "../include/lustre/ll_fiemap.h"
+
+static inline int lustre_msg_hdr_size_v2(int count)
+{
+ return cfs_size_round(offsetof(struct lustre_msg_v2,
+ lm_buflens[count]));
+}
+
+int lustre_msg_hdr_size(__u32 magic, int count)
+{
+ switch (magic) {
+ case LUSTRE_MSG_MAGIC_V2:
+ return lustre_msg_hdr_size_v2(count);
+ default:
+ LASSERTF(0, "incorrect message magic: %08x\n", magic);
+ return -EINVAL;
+ }
+}
+EXPORT_SYMBOL(lustre_msg_hdr_size);
+
+void ptlrpc_buf_set_swabbed(struct ptlrpc_request *req, const int inout,
+ int index)
+{
+ if (inout)
+ lustre_set_req_swabbed(req, index);
+ else
+ lustre_set_rep_swabbed(req, index);
+}
+EXPORT_SYMBOL(ptlrpc_buf_set_swabbed);
+
+int ptlrpc_buf_need_swab(struct ptlrpc_request *req, const int inout,
+ int index)
+{
+ if (inout)
+ return (ptlrpc_req_need_swab(req) &&
+ !lustre_req_swabbed(req, index));
+ else
+ return (ptlrpc_rep_need_swab(req) &&
+ !lustre_rep_swabbed(req, index));
+}
+EXPORT_SYMBOL(ptlrpc_buf_need_swab);
+
+static inline int lustre_msg_check_version_v2(struct lustre_msg_v2 *msg,
+ __u32 version)
+{
+ __u32 ver = lustre_msg_get_version(msg);
+ return (ver & LUSTRE_VERSION_MASK) != version;
+}
+
+int lustre_msg_check_version(struct lustre_msg *msg, __u32 version)
+{
+ switch (msg->lm_magic) {
+ case LUSTRE_MSG_MAGIC_V1:
+ CERROR("msg v1 not supported - please upgrade you system\n");
+ return -EINVAL;
+ case LUSTRE_MSG_MAGIC_V2:
+ return lustre_msg_check_version_v2(msg, version);
+ default:
+ CERROR("incorrect message magic: %08x\n", msg->lm_magic);
+ return 0;
+ }
+}
+EXPORT_SYMBOL(lustre_msg_check_version);
+
+/* early reply size */
+int lustre_msg_early_size(void)
+{
+ static int size;
+ if (!size) {
+ /* Always reply old ptlrpc_body_v2 to keep interoperability
+ * with the old client (< 2.3) which doesn't have pb_jobid
+ * in the ptlrpc_body.
+ *
+ * XXX Remove this whenever we drop interoperability with such
+ * client.
+ */
+ __u32 pblen = sizeof(struct ptlrpc_body_v2);
+ size = lustre_msg_size(LUSTRE_MSG_MAGIC_V2, 1, &pblen);
+ }
+ return size;
+}
+EXPORT_SYMBOL(lustre_msg_early_size);
+
+int lustre_msg_size_v2(int count, __u32 *lengths)
+{
+ int size;
+ int i;
+
+ size = lustre_msg_hdr_size_v2(count);
+ for (i = 0; i < count; i++)
+ size += cfs_size_round(lengths[i]);
+
+ return size;
+}
+EXPORT_SYMBOL(lustre_msg_size_v2);
+
+/* This returns the size of the buffer that is required to hold a lustre_msg
+ * with the given sub-buffer lengths.
+ * NOTE: this should only be used for NEW requests, and should always be
+ * in the form of a v2 request. If this is a connection to a v1
+ * target then the first buffer will be stripped because the ptlrpc
+ * data is part of the lustre_msg_v1 header. b=14043 */
+int lustre_msg_size(__u32 magic, int count, __u32 *lens)
+{
+ __u32 size[] = { sizeof(struct ptlrpc_body) };
+
+ if (!lens) {
+ LASSERT(count == 1);
+ lens = size;
+ }
+
+ LASSERT(count > 0);
+ LASSERT(lens[MSG_PTLRPC_BODY_OFF] >= sizeof(struct ptlrpc_body_v2));
+
+ switch (magic) {
+ case LUSTRE_MSG_MAGIC_V2:
+ return lustre_msg_size_v2(count, lens);
+ default:
+ LASSERTF(0, "incorrect message magic: %08x\n", magic);
+ return -EINVAL;
+ }
+}
+EXPORT_SYMBOL(lustre_msg_size);
+
+/* This is used to determine the size of a buffer that was already packed
+ * and will correctly handle the different message formats. */
+int lustre_packed_msg_size(struct lustre_msg *msg)
+{
+ switch (msg->lm_magic) {
+ case LUSTRE_MSG_MAGIC_V2:
+ return lustre_msg_size_v2(msg->lm_bufcount, msg->lm_buflens);
+ default:
+ CERROR("incorrect message magic: %08x\n", msg->lm_magic);
+ return 0;
+ }
+}
+EXPORT_SYMBOL(lustre_packed_msg_size);
+
+void lustre_init_msg_v2(struct lustre_msg_v2 *msg, int count, __u32 *lens,
+ char **bufs)
+{
+ char *ptr;
+ int i;
+
+ msg->lm_bufcount = count;
+ /* XXX: lm_secflvr uninitialized here */
+ msg->lm_magic = LUSTRE_MSG_MAGIC_V2;
+
+ for (i = 0; i < count; i++)
+ msg->lm_buflens[i] = lens[i];
+
+ if (bufs == NULL)
+ return;
+
+ ptr = (char *)msg + lustre_msg_hdr_size_v2(count);
+ for (i = 0; i < count; i++) {
+ char *tmp = bufs[i];
+ LOGL(tmp, lens[i], ptr);
+ }
+}
+EXPORT_SYMBOL(lustre_init_msg_v2);
+
+static int lustre_pack_request_v2(struct ptlrpc_request *req,
+ int count, __u32 *lens, char **bufs)
+{
+ int reqlen, rc;
+
+ reqlen = lustre_msg_size_v2(count, lens);
+
+ rc = sptlrpc_cli_alloc_reqbuf(req, reqlen);
+ if (rc)
+ return rc;
+
+ req->rq_reqlen = reqlen;
+
+ lustre_init_msg_v2(req->rq_reqmsg, count, lens, bufs);
+ lustre_msg_add_version(req->rq_reqmsg, PTLRPC_MSG_VERSION);
+ return 0;
+}
+
+int lustre_pack_request(struct ptlrpc_request *req, __u32 magic, int count,
+ __u32 *lens, char **bufs)
+{
+ __u32 size[] = { sizeof(struct ptlrpc_body) };
+
+ if (!lens) {
+ LASSERT(count == 1);
+ lens = size;
+ }
+
+ LASSERT(count > 0);
+ LASSERT(lens[MSG_PTLRPC_BODY_OFF] == sizeof(struct ptlrpc_body));
+
+ /* only use new format, we don't need to be compatible with 1.4 */
+ return lustre_pack_request_v2(req, count, lens, bufs);
+}
+EXPORT_SYMBOL(lustre_pack_request);
+
+#if RS_DEBUG
+LIST_HEAD(ptlrpc_rs_debug_lru);
+spinlock_t ptlrpc_rs_debug_lock;
+
+#define PTLRPC_RS_DEBUG_LRU_ADD(rs) \
+do { \
+ spin_lock(&ptlrpc_rs_debug_lock); \
+ list_add_tail(&(rs)->rs_debug_list, &ptlrpc_rs_debug_lru); \
+ spin_unlock(&ptlrpc_rs_debug_lock); \
+} while (0)
+
+#define PTLRPC_RS_DEBUG_LRU_DEL(rs) \
+do { \
+ spin_lock(&ptlrpc_rs_debug_lock); \
+ list_del(&(rs)->rs_debug_list); \
+ spin_unlock(&ptlrpc_rs_debug_lock); \
+} while (0)
+#else
+# define PTLRPC_RS_DEBUG_LRU_ADD(rs) do {} while (0)
+# define PTLRPC_RS_DEBUG_LRU_DEL(rs) do {} while (0)
+#endif
+
+struct ptlrpc_reply_state *
+lustre_get_emerg_rs(struct ptlrpc_service_part *svcpt)
+{
+ struct ptlrpc_reply_state *rs = NULL;
+
+ spin_lock(&svcpt->scp_rep_lock);
+
+ /* See if we have anything in a pool, and wait if nothing */
+ while (list_empty(&svcpt->scp_rep_idle)) {
+ struct l_wait_info lwi;
+ int rc;
+
+ spin_unlock(&svcpt->scp_rep_lock);
+ /* If we cannot get anything for some long time, we better
+ * bail out instead of waiting infinitely */
+ lwi = LWI_TIMEOUT(cfs_time_seconds(10), NULL, NULL);
+ rc = l_wait_event(svcpt->scp_rep_waitq,
+ !list_empty(&svcpt->scp_rep_idle), &lwi);
+ if (rc != 0)
+ goto out;
+ spin_lock(&svcpt->scp_rep_lock);
+ }
+
+ rs = list_entry(svcpt->scp_rep_idle.next,
+ struct ptlrpc_reply_state, rs_list);
+ list_del(&rs->rs_list);
+
+ spin_unlock(&svcpt->scp_rep_lock);
+
+ memset(rs, 0, svcpt->scp_service->srv_max_reply_size);
+ rs->rs_size = svcpt->scp_service->srv_max_reply_size;
+ rs->rs_svcpt = svcpt;
+ rs->rs_prealloc = 1;
+out:
+ return rs;
+}
+
+void lustre_put_emerg_rs(struct ptlrpc_reply_state *rs)
+{
+ struct ptlrpc_service_part *svcpt = rs->rs_svcpt;
+
+ spin_lock(&svcpt->scp_rep_lock);
+ list_add(&rs->rs_list, &svcpt->scp_rep_idle);
+ spin_unlock(&svcpt->scp_rep_lock);
+ wake_up(&svcpt->scp_rep_waitq);
+}
+
+int lustre_pack_reply_v2(struct ptlrpc_request *req, int count,
+ __u32 *lens, char **bufs, int flags)
+{
+ struct ptlrpc_reply_state *rs;
+ int msg_len, rc;
+
+ LASSERT(req->rq_reply_state == NULL);
+
+ if ((flags & LPRFL_EARLY_REPLY) == 0) {
+ spin_lock(&req->rq_lock);
+ req->rq_packed_final = 1;
+ spin_unlock(&req->rq_lock);
+ }
+
+ msg_len = lustre_msg_size_v2(count, lens);
+ rc = sptlrpc_svc_alloc_rs(req, msg_len);
+ if (rc)
+ return rc;
+
+ rs = req->rq_reply_state;
+ atomic_set(&rs->rs_refcount, 1); /* 1 ref for rq_reply_state */
+ rs->rs_cb_id.cbid_fn = reply_out_callback;
+ rs->rs_cb_id.cbid_arg = rs;
+ rs->rs_svcpt = req->rq_rqbd->rqbd_svcpt;
+ INIT_LIST_HEAD(&rs->rs_exp_list);
+ INIT_LIST_HEAD(&rs->rs_obd_list);
+ INIT_LIST_HEAD(&rs->rs_list);
+ spin_lock_init(&rs->rs_lock);
+
+ req->rq_replen = msg_len;
+ req->rq_reply_state = rs;
+ req->rq_repmsg = rs->rs_msg;
+
+ lustre_init_msg_v2(rs->rs_msg, count, lens, bufs);
+ lustre_msg_add_version(rs->rs_msg, PTLRPC_MSG_VERSION);
+
+ PTLRPC_RS_DEBUG_LRU_ADD(rs);
+
+ return 0;
+}
+EXPORT_SYMBOL(lustre_pack_reply_v2);
+
+int lustre_pack_reply_flags(struct ptlrpc_request *req, int count, __u32 *lens,
+ char **bufs, int flags)
+{
+ int rc = 0;
+ __u32 size[] = { sizeof(struct ptlrpc_body) };
+
+ if (!lens) {
+ LASSERT(count == 1);
+ lens = size;
+ }
+
+ LASSERT(count > 0);
+ LASSERT(lens[MSG_PTLRPC_BODY_OFF] == sizeof(struct ptlrpc_body));
+
+ switch (req->rq_reqmsg->lm_magic) {
+ case LUSTRE_MSG_MAGIC_V2:
+ rc = lustre_pack_reply_v2(req, count, lens, bufs, flags);
+ break;
+ default:
+ LASSERTF(0, "incorrect message magic: %08x\n",
+ req->rq_reqmsg->lm_magic);
+ rc = -EINVAL;
+ }
+ if (rc != 0)
+ CERROR("lustre_pack_reply failed: rc=%d size=%d\n", rc,
+ lustre_msg_size(req->rq_reqmsg->lm_magic, count, lens));
+ return rc;
+}
+EXPORT_SYMBOL(lustre_pack_reply_flags);
+
+int lustre_pack_reply(struct ptlrpc_request *req, int count, __u32 *lens,
+ char **bufs)
+{
+ return lustre_pack_reply_flags(req, count, lens, bufs, 0);
+}
+EXPORT_SYMBOL(lustre_pack_reply);
+
+void *lustre_msg_buf_v2(struct lustre_msg_v2 *m, int n, int min_size)
+{
+ int i, offset, buflen, bufcount;
+
+ LASSERT(m != NULL);
+ LASSERT(n >= 0);
+
+ bufcount = m->lm_bufcount;
+ if (unlikely(n >= bufcount)) {
+ CDEBUG(D_INFO, "msg %p buffer[%d] not present (count %d)\n",
+ m, n, bufcount);
+ return NULL;
+ }
+
+ buflen = m->lm_buflens[n];
+ if (unlikely(buflen < min_size)) {
+ CERROR("msg %p buffer[%d] size %d too small (required %d, opc=%d)\n",
+ m, n, buflen, min_size,
+ n == MSG_PTLRPC_BODY_OFF ? -1 : lustre_msg_get_opc(m));
+ return NULL;
+ }
+
+ offset = lustre_msg_hdr_size_v2(bufcount);
+ for (i = 0; i < n; i++)
+ offset += cfs_size_round(m->lm_buflens[i]);
+
+ return (char *)m + offset;
+}
+
+void *lustre_msg_buf(struct lustre_msg *m, int n, int min_size)
+{
+ switch (m->lm_magic) {
+ case LUSTRE_MSG_MAGIC_V2:
+ return lustre_msg_buf_v2(m, n, min_size);
+ default:
+ LASSERTF(0, "incorrect message magic: %08x(msg:%p)\n", m->lm_magic, m);
+ return NULL;
+ }
+}
+EXPORT_SYMBOL(lustre_msg_buf);
+
+int lustre_shrink_msg_v2(struct lustre_msg_v2 *msg, int segment,
+ unsigned int newlen, int move_data)
+{
+ char *tail = NULL, *newpos;
+ int tail_len = 0, n;
+
+ LASSERT(msg);
+ LASSERT(msg->lm_bufcount > segment);
+ LASSERT(msg->lm_buflens[segment] >= newlen);
+
+ if (msg->lm_buflens[segment] == newlen)
+ goto out;
+
+ if (move_data && msg->lm_bufcount > segment + 1) {
+ tail = lustre_msg_buf_v2(msg, segment + 1, 0);
+ for (n = segment + 1; n < msg->lm_bufcount; n++)
+ tail_len += cfs_size_round(msg->lm_buflens[n]);
+ }
+
+ msg->lm_buflens[segment] = newlen;
+
+ if (tail && tail_len) {
+ newpos = lustre_msg_buf_v2(msg, segment + 1, 0);
+ LASSERT(newpos <= tail);
+ if (newpos != tail)
+ memmove(newpos, tail, tail_len);
+ }
+out:
+ return lustre_msg_size_v2(msg->lm_bufcount, msg->lm_buflens);
+}
+
+/*
+ * for @msg, shrink @segment to size @newlen. if @move_data is non-zero,
+ * we also move data forward from @segment + 1.
+ *
+ * if @newlen == 0, we remove the segment completely, but we still keep the
+ * totally bufcount the same to save possible data moving. this will leave a
+ * unused segment with size 0 at the tail, but that's ok.
+ *
+ * return new msg size after shrinking.
+ *
+ * CAUTION:
+ * + if any buffers higher than @segment has been filled in, must call shrink
+ * with non-zero @move_data.
+ * + caller should NOT keep pointers to msg buffers which higher than @segment
+ * after call shrink.
+ */
+int lustre_shrink_msg(struct lustre_msg *msg, int segment,
+ unsigned int newlen, int move_data)
+{
+ switch (msg->lm_magic) {
+ case LUSTRE_MSG_MAGIC_V2:
+ return lustre_shrink_msg_v2(msg, segment, newlen, move_data);
+ default:
+ LASSERTF(0, "incorrect message magic: %08x\n", msg->lm_magic);
+ }
+}
+EXPORT_SYMBOL(lustre_shrink_msg);
+
+void lustre_free_reply_state(struct ptlrpc_reply_state *rs)
+{
+ PTLRPC_RS_DEBUG_LRU_DEL(rs);
+
+ LASSERT(atomic_read(&rs->rs_refcount) == 0);
+ LASSERT(!rs->rs_difficult || rs->rs_handled);
+ LASSERT(!rs->rs_on_net);
+ LASSERT(!rs->rs_scheduled);
+ LASSERT(rs->rs_export == NULL);
+ LASSERT(rs->rs_nlocks == 0);
+ LASSERT(list_empty(&rs->rs_exp_list));
+ LASSERT(list_empty(&rs->rs_obd_list));
+
+ sptlrpc_svc_free_rs(rs);
+}
+EXPORT_SYMBOL(lustre_free_reply_state);
+
+static int lustre_unpack_msg_v2(struct lustre_msg_v2 *m, int len)
+{
+ int swabbed, required_len, i;
+
+ /* Now we know the sender speaks my language. */
+ required_len = lustre_msg_hdr_size_v2(0);
+ if (len < required_len) {
+ /* can't even look inside the message */
+ CERROR("message length %d too small for lustre_msg\n", len);
+ return -EINVAL;
+ }
+
+ swabbed = (m->lm_magic == LUSTRE_MSG_MAGIC_V2_SWABBED);
+
+ if (swabbed) {
+ __swab32s(&m->lm_magic);
+ __swab32s(&m->lm_bufcount);
+ __swab32s(&m->lm_secflvr);
+ __swab32s(&m->lm_repsize);
+ __swab32s(&m->lm_cksum);
+ __swab32s(&m->lm_flags);
+ CLASSERT(offsetof(typeof(*m), lm_padding_2) != 0);
+ CLASSERT(offsetof(typeof(*m), lm_padding_3) != 0);
+ }
+
+ required_len = lustre_msg_hdr_size_v2(m->lm_bufcount);
+ if (len < required_len) {
+ /* didn't receive all the buffer lengths */
+ CERROR("message length %d too small for %d buflens\n",
+ len, m->lm_bufcount);
+ return -EINVAL;
+ }
+
+ for (i = 0; i < m->lm_bufcount; i++) {
+ if (swabbed)
+ __swab32s(&m->lm_buflens[i]);
+ required_len += cfs_size_round(m->lm_buflens[i]);
+ }
+
+ if (len < required_len) {
+ CERROR("len: %d, required_len %d\n", len, required_len);
+ CERROR("bufcount: %d\n", m->lm_bufcount);
+ for (i = 0; i < m->lm_bufcount; i++)
+ CERROR("buffer %d length %d\n", i, m->lm_buflens[i]);
+ return -EINVAL;
+ }
+
+ return swabbed;
+}
+
+int __lustre_unpack_msg(struct lustre_msg *m, int len)
+{
+ int required_len, rc;
+
+ /* We can provide a slightly better error log, if we check the
+ * message magic and version first. In the future, struct
+ * lustre_msg may grow, and we'd like to log a version mismatch,
+ * rather than a short message.
+ *
+ */
+ required_len = offsetof(struct lustre_msg, lm_magic) +
+ sizeof(m->lm_magic);
+ if (len < required_len) {
+ /* can't even look inside the message */
+ CERROR("message length %d too small for magic/version check\n",
+ len);
+ return -EINVAL;
+ }
+
+ rc = lustre_unpack_msg_v2(m, len);
+
+ return rc;
+}
+EXPORT_SYMBOL(__lustre_unpack_msg);
+
+int ptlrpc_unpack_req_msg(struct ptlrpc_request *req, int len)
+{
+ int rc;
+ rc = __lustre_unpack_msg(req->rq_reqmsg, len);
+ if (rc == 1) {
+ lustre_set_req_swabbed(req, MSG_PTLRPC_HEADER_OFF);
+ rc = 0;
+ }
+ return rc;
+}
+EXPORT_SYMBOL(ptlrpc_unpack_req_msg);
+
+int ptlrpc_unpack_rep_msg(struct ptlrpc_request *req, int len)
+{
+ int rc;
+ rc = __lustre_unpack_msg(req->rq_repmsg, len);
+ if (rc == 1) {
+ lustre_set_rep_swabbed(req, MSG_PTLRPC_HEADER_OFF);
+ rc = 0;
+ }
+ return rc;
+}
+EXPORT_SYMBOL(ptlrpc_unpack_rep_msg);
+
+static inline int lustre_unpack_ptlrpc_body_v2(struct ptlrpc_request *req,
+ const int inout, int offset)
+{
+ struct ptlrpc_body *pb;
+ struct lustre_msg_v2 *m = inout ? req->rq_reqmsg : req->rq_repmsg;
+
+ pb = lustre_msg_buf_v2(m, offset, sizeof(struct ptlrpc_body_v2));
+ if (!pb) {
+ CERROR("error unpacking ptlrpc body\n");
+ return -EFAULT;
+ }
+ if (ptlrpc_buf_need_swab(req, inout, offset)) {
+ lustre_swab_ptlrpc_body(pb);
+ ptlrpc_buf_set_swabbed(req, inout, offset);
+ }
+
+ if ((pb->pb_version & ~LUSTRE_VERSION_MASK) != PTLRPC_MSG_VERSION) {
+ CERROR("wrong lustre_msg version %08x\n", pb->pb_version);
+ return -EINVAL;
+ }
+
+ if (!inout)
+ pb->pb_status = ptlrpc_status_ntoh(pb->pb_status);
+
+ return 0;
+}
+
+int lustre_unpack_req_ptlrpc_body(struct ptlrpc_request *req, int offset)
+{
+ switch (req->rq_reqmsg->lm_magic) {
+ case LUSTRE_MSG_MAGIC_V2:
+ return lustre_unpack_ptlrpc_body_v2(req, 1, offset);
+ default:
+ CERROR("bad lustre msg magic: %08x\n",
+ req->rq_reqmsg->lm_magic);
+ return -EINVAL;
+ }
+}
+
+int lustre_unpack_rep_ptlrpc_body(struct ptlrpc_request *req, int offset)
+{
+ switch (req->rq_repmsg->lm_magic) {
+ case LUSTRE_MSG_MAGIC_V2:
+ return lustre_unpack_ptlrpc_body_v2(req, 0, offset);
+ default:
+ CERROR("bad lustre msg magic: %08x\n",
+ req->rq_repmsg->lm_magic);
+ return -EINVAL;
+ }
+}
+
+static inline int lustre_msg_buflen_v2(struct lustre_msg_v2 *m, int n)
+{
+ if (n >= m->lm_bufcount)
+ return 0;
+
+ return m->lm_buflens[n];
+}
+
+/**
+ * lustre_msg_buflen - return the length of buffer \a n in message \a m
+ * \param m lustre_msg (request or reply) to look at
+ * \param n message index (base 0)
+ *
+ * returns zero for non-existent message indices
+ */
+int lustre_msg_buflen(struct lustre_msg *m, int n)
+{
+ switch (m->lm_magic) {
+ case LUSTRE_MSG_MAGIC_V2:
+ return lustre_msg_buflen_v2(m, n);
+ default:
+ CERROR("incorrect message magic: %08x\n", m->lm_magic);
+ return -EINVAL;
+ }
+}
+EXPORT_SYMBOL(lustre_msg_buflen);
+
+static inline void
+lustre_msg_set_buflen_v2(struct lustre_msg_v2 *m, int n, int len)
+{
+ if (n >= m->lm_bufcount)
+ LBUG();
+
+ m->lm_buflens[n] = len;
+}
+
+void lustre_msg_set_buflen(struct lustre_msg *m, int n, int len)
+{
+ switch (m->lm_magic) {
+ case LUSTRE_MSG_MAGIC_V2:
+ lustre_msg_set_buflen_v2(m, n, len);
+ return;
+ default:
+ LASSERTF(0, "incorrect message magic: %08x\n", m->lm_magic);
+ }
+}
+
+EXPORT_SYMBOL(lustre_msg_set_buflen);
+
+/* NB return the bufcount for lustre_msg_v2 format, so if message is packed
+ * in V1 format, the result is one bigger. (add struct ptlrpc_body). */
+int lustre_msg_bufcount(struct lustre_msg *m)
+{
+ switch (m->lm_magic) {
+ case LUSTRE_MSG_MAGIC_V2:
+ return m->lm_bufcount;
+ default:
+ CERROR("incorrect message magic: %08x\n", m->lm_magic);
+ return -EINVAL;
+ }
+}
+EXPORT_SYMBOL(lustre_msg_bufcount);
+
+char *lustre_msg_string(struct lustre_msg *m, int index, int max_len)
+{
+ /* max_len == 0 means the string should fill the buffer */
+ char *str;
+ int slen, blen;
+
+ switch (m->lm_magic) {
+ case LUSTRE_MSG_MAGIC_V2:
+ str = lustre_msg_buf_v2(m, index, 0);
+ blen = lustre_msg_buflen_v2(m, index);
+ break;
+ default:
+ LASSERTF(0, "incorrect message magic: %08x\n", m->lm_magic);
+ }
+
+ if (str == NULL) {
+ CERROR("can't unpack string in msg %p buffer[%d]\n", m, index);
+ return NULL;
+ }
+
+ slen = strnlen(str, blen);
+
+ if (slen == blen) { /* not NULL terminated */
+ CERROR("can't unpack non-NULL terminated string in msg %p buffer[%d] len %d\n",
+ m, index, blen);
+ return NULL;
+ }
+
+ if (max_len == 0) {
+ if (slen != blen - 1) {
+ CERROR("can't unpack short string in msg %p buffer[%d] len %d: strlen %d\n",
+ m, index, blen, slen);
+ return NULL;
+ }
+ } else if (slen > max_len) {
+ CERROR("can't unpack oversized string in msg %p buffer[%d] len %d strlen %d: max %d expected\n",
+ m, index, blen, slen, max_len);
+ return NULL;
+ }
+
+ return str;
+}
+EXPORT_SYMBOL(lustre_msg_string);
+
+/* Wrap up the normal fixed length cases */
+static inline void *__lustre_swab_buf(struct lustre_msg *msg, int index,
+ int min_size, void *swabber)
+{
+ void *ptr = NULL;
+
+ LASSERT(msg != NULL);
+ switch (msg->lm_magic) {
+ case LUSTRE_MSG_MAGIC_V2:
+ ptr = lustre_msg_buf_v2(msg, index, min_size);
+ break;
+ default:
+ CERROR("incorrect message magic: %08x\n", msg->lm_magic);
+ }
+
+ if (ptr && swabber)
+ ((void (*)(void *))swabber)(ptr);
+
+ return ptr;
+}
+
+static inline struct ptlrpc_body *lustre_msg_ptlrpc_body(struct lustre_msg *msg)
+{
+ return lustre_msg_buf_v2(msg, MSG_PTLRPC_BODY_OFF,
+ sizeof(struct ptlrpc_body_v2));
+}
+
+__u32 lustre_msghdr_get_flags(struct lustre_msg *msg)
+{
+ switch (msg->lm_magic) {
+ case LUSTRE_MSG_MAGIC_V1:
+ case LUSTRE_MSG_MAGIC_V1_SWABBED:
+ return 0;
+ case LUSTRE_MSG_MAGIC_V2:
+ /* already in host endian */
+ return msg->lm_flags;
+ default:
+ LASSERTF(0, "incorrect message magic: %08x\n", msg->lm_magic);
+ return 0;
+ }
+}
+EXPORT_SYMBOL(lustre_msghdr_get_flags);
+
+void lustre_msghdr_set_flags(struct lustre_msg *msg, __u32 flags)
+{
+ switch (msg->lm_magic) {
+ case LUSTRE_MSG_MAGIC_V1:
+ return;
+ case LUSTRE_MSG_MAGIC_V2:
+ msg->lm_flags = flags;
+ return;
+ default:
+ LASSERTF(0, "incorrect message magic: %08x\n", msg->lm_magic);
+ }
+}
+
+__u32 lustre_msg_get_flags(struct lustre_msg *msg)
+{
+ switch (msg->lm_magic) {
+ case LUSTRE_MSG_MAGIC_V2: {
+ struct ptlrpc_body *pb = lustre_msg_ptlrpc_body(msg);
+ if (!pb) {
+ CERROR("invalid msg %p: no ptlrpc body!\n", msg);
+ return 0;
+ }
+ return pb->pb_flags;
+ }
+ default:
+ /* flags might be printed in debug code while message
+ * uninitialized */
+ return 0;
+ }
+}
+EXPORT_SYMBOL(lustre_msg_get_flags);
+
+void lustre_msg_add_flags(struct lustre_msg *msg, int flags)
+{
+ switch (msg->lm_magic) {
+ case LUSTRE_MSG_MAGIC_V2: {
+ struct ptlrpc_body *pb = lustre_msg_ptlrpc_body(msg);
+ LASSERTF(pb, "invalid msg %p: no ptlrpc body!\n", msg);
+ pb->pb_flags |= flags;
+ return;
+ }
+ default:
+ LASSERTF(0, "incorrect message magic: %08x\n", msg->lm_magic);
+ }
+}
+EXPORT_SYMBOL(lustre_msg_add_flags);
+
+void lustre_msg_set_flags(struct lustre_msg *msg, int flags)
+{
+ switch (msg->lm_magic) {
+ case LUSTRE_MSG_MAGIC_V2: {
+ struct ptlrpc_body *pb = lustre_msg_ptlrpc_body(msg);
+ LASSERTF(pb, "invalid msg %p: no ptlrpc body!\n", msg);
+ pb->pb_flags = flags;
+ return;
+ }
+ default:
+ LASSERTF(0, "incorrect message magic: %08x\n", msg->lm_magic);
+ }
+}
+EXPORT_SYMBOL(lustre_msg_set_flags);
+
+void lustre_msg_clear_flags(struct lustre_msg *msg, int flags)
+{
+ switch (msg->lm_magic) {
+ case LUSTRE_MSG_MAGIC_V2: {
+ struct ptlrpc_body *pb = lustre_msg_ptlrpc_body(msg);
+ LASSERTF(pb, "invalid msg %p: no ptlrpc body!\n", msg);
+ pb->pb_flags &= ~(MSG_GEN_FLAG_MASK & flags);
+ return;
+ }
+ default:
+ LASSERTF(0, "incorrect message magic: %08x\n", msg->lm_magic);
+ }
+}
+EXPORT_SYMBOL(lustre_msg_clear_flags);
+
+__u32 lustre_msg_get_op_flags(struct lustre_msg *msg)
+{
+ switch (msg->lm_magic) {
+ case LUSTRE_MSG_MAGIC_V2: {
+ struct ptlrpc_body *pb = lustre_msg_ptlrpc_body(msg);
+ if (!pb) {
+ CERROR("invalid msg %p: no ptlrpc body!\n", msg);
+ return 0;
+ }
+ return pb->pb_op_flags;
+ }
+ default:
+ return 0;
+ }
+}
+EXPORT_SYMBOL(lustre_msg_get_op_flags);
+
+void lustre_msg_add_op_flags(struct lustre_msg *msg, int flags)
+{
+ switch (msg->lm_magic) {
+ case LUSTRE_MSG_MAGIC_V2: {
+ struct ptlrpc_body *pb = lustre_msg_ptlrpc_body(msg);
+ LASSERTF(pb, "invalid msg %p: no ptlrpc body!\n", msg);
+ pb->pb_op_flags |= flags;
+ return;
+ }
+ default:
+ LASSERTF(0, "incorrect message magic: %08x\n", msg->lm_magic);
+ }
+}
+EXPORT_SYMBOL(lustre_msg_add_op_flags);
+
+void lustre_msg_set_op_flags(struct lustre_msg *msg, int flags)
+{
+ switch (msg->lm_magic) {
+ case LUSTRE_MSG_MAGIC_V2: {
+ struct ptlrpc_body *pb = lustre_msg_ptlrpc_body(msg);
+ LASSERTF(pb, "invalid msg %p: no ptlrpc body!\n", msg);
+ pb->pb_op_flags |= flags;
+ return;
+ }
+ default:
+ LASSERTF(0, "incorrect message magic: %08x\n", msg->lm_magic);
+ }
+}
+EXPORT_SYMBOL(lustre_msg_set_op_flags);
+
+struct lustre_handle *lustre_msg_get_handle(struct lustre_msg *msg)
+{
+ switch (msg->lm_magic) {
+ case LUSTRE_MSG_MAGIC_V2: {
+ struct ptlrpc_body *pb = lustre_msg_ptlrpc_body(msg);
+ if (!pb) {
+ CERROR("invalid msg %p: no ptlrpc body!\n", msg);
+ return NULL;
+ }
+ return &pb->pb_handle;
+ }
+ default:
+ CERROR("incorrect message magic: %08x\n", msg->lm_magic);
+ return NULL;
+ }
+}
+EXPORT_SYMBOL(lustre_msg_get_handle);
+
+__u32 lustre_msg_get_type(struct lustre_msg *msg)
+{
+ switch (msg->lm_magic) {
+ case LUSTRE_MSG_MAGIC_V2: {
+ struct ptlrpc_body *pb = lustre_msg_ptlrpc_body(msg);
+ if (!pb) {
+ CERROR("invalid msg %p: no ptlrpc body!\n", msg);
+ return PTL_RPC_MSG_ERR;
+ }
+ return pb->pb_type;
+ }
+ default:
+ CERROR("incorrect message magic: %08x\n", msg->lm_magic);
+ return PTL_RPC_MSG_ERR;
+ }
+}
+EXPORT_SYMBOL(lustre_msg_get_type);
+
+__u32 lustre_msg_get_version(struct lustre_msg *msg)
+{
+ switch (msg->lm_magic) {
+ case LUSTRE_MSG_MAGIC_V2: {
+ struct ptlrpc_body *pb = lustre_msg_ptlrpc_body(msg);
+ if (!pb) {
+ CERROR("invalid msg %p: no ptlrpc body!\n", msg);
+ return 0;
+ }
+ return pb->pb_version;
+ }
+ default:
+ CERROR("incorrect message magic: %08x\n", msg->lm_magic);
+ return 0;
+ }
+}
+EXPORT_SYMBOL(lustre_msg_get_version);
+
+void lustre_msg_add_version(struct lustre_msg *msg, int version)
+{
+ switch (msg->lm_magic) {
+ case LUSTRE_MSG_MAGIC_V2: {
+ struct ptlrpc_body *pb = lustre_msg_ptlrpc_body(msg);
+ LASSERTF(pb, "invalid msg %p: no ptlrpc body!\n", msg);
+ pb->pb_version |= version;
+ return;
+ }
+ default:
+ LASSERTF(0, "incorrect message magic: %08x\n", msg->lm_magic);
+ }
+}
+EXPORT_SYMBOL(lustre_msg_add_version);
+
+__u32 lustre_msg_get_opc(struct lustre_msg *msg)
+{
+ switch (msg->lm_magic) {
+ case LUSTRE_MSG_MAGIC_V2: {
+ struct ptlrpc_body *pb = lustre_msg_ptlrpc_body(msg);
+ if (!pb) {
+ CERROR("invalid msg %p: no ptlrpc body!\n", msg);
+ return 0;
+ }
+ return pb->pb_opc;
+ }
+ default:
+ CERROR("incorrect message magic: %08x(msg:%p)\n", msg->lm_magic, msg);
+ LBUG();
+ return 0;
+ }
+}
+EXPORT_SYMBOL(lustre_msg_get_opc);
+
+__u64 lustre_msg_get_last_xid(struct lustre_msg *msg)
+{
+ switch (msg->lm_magic) {
+ case LUSTRE_MSG_MAGIC_V2: {
+ struct ptlrpc_body *pb = lustre_msg_ptlrpc_body(msg);
+ if (!pb) {
+ CERROR("invalid msg %p: no ptlrpc body!\n", msg);
+ return 0;
+ }
+ return pb->pb_last_xid;
+ }
+ default:
+ CERROR("incorrect message magic: %08x\n", msg->lm_magic);
+ return 0;
+ }
+}
+EXPORT_SYMBOL(lustre_msg_get_last_xid);
+
+__u64 lustre_msg_get_last_committed(struct lustre_msg *msg)
+{
+ switch (msg->lm_magic) {
+ case LUSTRE_MSG_MAGIC_V2: {
+ struct ptlrpc_body *pb = lustre_msg_ptlrpc_body(msg);
+ if (!pb) {
+ CERROR("invalid msg %p: no ptlrpc body!\n", msg);
+ return 0;
+ }
+ return pb->pb_last_committed;
+ }
+ default:
+ CERROR("incorrect message magic: %08x\n", msg->lm_magic);
+ return 0;
+ }
+}
+EXPORT_SYMBOL(lustre_msg_get_last_committed);
+
+__u64 *lustre_msg_get_versions(struct lustre_msg *msg)
+{
+ switch (msg->lm_magic) {
+ case LUSTRE_MSG_MAGIC_V1:
+ return NULL;
+ case LUSTRE_MSG_MAGIC_V2: {
+ struct ptlrpc_body *pb = lustre_msg_ptlrpc_body(msg);
+ if (!pb) {
+ CERROR("invalid msg %p: no ptlrpc body!\n", msg);
+ return NULL;
+ }
+ return pb->pb_pre_versions;
+ }
+ default:
+ CERROR("incorrect message magic: %08x\n", msg->lm_magic);
+ return NULL;
+ }
+}
+EXPORT_SYMBOL(lustre_msg_get_versions);
+
+__u64 lustre_msg_get_transno(struct lustre_msg *msg)
+{
+ switch (msg->lm_magic) {
+ case LUSTRE_MSG_MAGIC_V2: {
+ struct ptlrpc_body *pb = lustre_msg_ptlrpc_body(msg);
+ if (!pb) {
+ CERROR("invalid msg %p: no ptlrpc body!\n", msg);
+ return 0;
+ }
+ return pb->pb_transno;
+ }
+ default:
+ CERROR("incorrect message magic: %08x\n", msg->lm_magic);
+ return 0;
+ }
+}
+EXPORT_SYMBOL(lustre_msg_get_transno);
+
+int lustre_msg_get_status(struct lustre_msg *msg)
+{
+ switch (msg->lm_magic) {
+ case LUSTRE_MSG_MAGIC_V2: {
+ struct ptlrpc_body *pb = lustre_msg_ptlrpc_body(msg);
+ if (!pb) {
+ CERROR("invalid msg %p: no ptlrpc body!\n", msg);
+ return -EINVAL;
+ }
+ return pb->pb_status;
+ }
+ default:
+ /* status might be printed in debug code while message
+ * uninitialized */
+ return -EINVAL;
+ }
+}
+EXPORT_SYMBOL(lustre_msg_get_status);
+
+__u64 lustre_msg_get_slv(struct lustre_msg *msg)
+{
+ switch (msg->lm_magic) {
+ case LUSTRE_MSG_MAGIC_V2: {
+ struct ptlrpc_body *pb = lustre_msg_ptlrpc_body(msg);
+ if (!pb) {
+ CERROR("invalid msg %p: no ptlrpc body!\n", msg);
+ return -EINVAL;
+ }
+ return pb->pb_slv;
+ }
+ default:
+ CERROR("invalid msg magic %08x\n", msg->lm_magic);
+ return -EINVAL;
+ }
+}
+EXPORT_SYMBOL(lustre_msg_get_slv);
+
+
+void lustre_msg_set_slv(struct lustre_msg *msg, __u64 slv)
+{
+ switch (msg->lm_magic) {
+ case LUSTRE_MSG_MAGIC_V2: {
+ struct ptlrpc_body *pb = lustre_msg_ptlrpc_body(msg);
+ if (!pb) {
+ CERROR("invalid msg %p: no ptlrpc body!\n", msg);
+ return;
+ }
+ pb->pb_slv = slv;
+ return;
+ }
+ default:
+ CERROR("invalid msg magic %x\n", msg->lm_magic);
+ return;
+ }
+}
+EXPORT_SYMBOL(lustre_msg_set_slv);
+
+__u32 lustre_msg_get_limit(struct lustre_msg *msg)
+{
+ switch (msg->lm_magic) {
+ case LUSTRE_MSG_MAGIC_V2: {
+ struct ptlrpc_body *pb = lustre_msg_ptlrpc_body(msg);
+ if (!pb) {
+ CERROR("invalid msg %p: no ptlrpc body!\n", msg);
+ return -EINVAL;
+ }
+ return pb->pb_limit;
+ }
+ default:
+ CERROR("invalid msg magic %x\n", msg->lm_magic);
+ return -EINVAL;
+ }
+}
+EXPORT_SYMBOL(lustre_msg_get_limit);
+
+
+void lustre_msg_set_limit(struct lustre_msg *msg, __u64 limit)
+{
+ switch (msg->lm_magic) {
+ case LUSTRE_MSG_MAGIC_V2: {
+ struct ptlrpc_body *pb = lustre_msg_ptlrpc_body(msg);
+ if (!pb) {
+ CERROR("invalid msg %p: no ptlrpc body!\n", msg);
+ return;
+ }
+ pb->pb_limit = limit;
+ return;
+ }
+ default:
+ CERROR("invalid msg magic %08x\n", msg->lm_magic);
+ return;
+ }
+}
+EXPORT_SYMBOL(lustre_msg_set_limit);
+
+__u32 lustre_msg_get_conn_cnt(struct lustre_msg *msg)
+{
+ switch (msg->lm_magic) {
+ case LUSTRE_MSG_MAGIC_V2: {
+ struct ptlrpc_body *pb = lustre_msg_ptlrpc_body(msg);
+ if (!pb) {
+ CERROR("invalid msg %p: no ptlrpc body!\n", msg);
+ return 0;
+ }
+ return pb->pb_conn_cnt;
+ }
+ default:
+ CERROR("incorrect message magic: %08x\n", msg->lm_magic);
+ return 0;
+ }
+}
+EXPORT_SYMBOL(lustre_msg_get_conn_cnt);
+
+int lustre_msg_is_v1(struct lustre_msg *msg)
+{
+ switch (msg->lm_magic) {
+ case LUSTRE_MSG_MAGIC_V1:
+ case LUSTRE_MSG_MAGIC_V1_SWABBED:
+ return 1;
+ default:
+ return 0;
+ }
+}
+EXPORT_SYMBOL(lustre_msg_is_v1);
+
+__u32 lustre_msg_get_magic(struct lustre_msg *msg)
+{
+ switch (msg->lm_magic) {
+ case LUSTRE_MSG_MAGIC_V2:
+ return msg->lm_magic;
+ default:
+ CERROR("incorrect message magic: %08x\n", msg->lm_magic);
+ return 0;
+ }
+}
+EXPORT_SYMBOL(lustre_msg_get_magic);
+
+__u32 lustre_msg_get_timeout(struct lustre_msg *msg)
+{
+ switch (msg->lm_magic) {
+ case LUSTRE_MSG_MAGIC_V1:
+ case LUSTRE_MSG_MAGIC_V1_SWABBED:
+ return 0;
+ case LUSTRE_MSG_MAGIC_V2: {
+ struct ptlrpc_body *pb = lustre_msg_ptlrpc_body(msg);
+ if (!pb) {
+ CERROR("invalid msg %p: no ptlrpc body!\n", msg);
+ return 0;
+
+ }
+ return pb->pb_timeout;
+ }
+ default:
+ CERROR("incorrect message magic: %08x\n", msg->lm_magic);
+ return 0;
+ }
+}
+
+__u32 lustre_msg_get_service_time(struct lustre_msg *msg)
+{
+ switch (msg->lm_magic) {
+ case LUSTRE_MSG_MAGIC_V1:
+ case LUSTRE_MSG_MAGIC_V1_SWABBED:
+ return 0;
+ case LUSTRE_MSG_MAGIC_V2: {
+ struct ptlrpc_body *pb = lustre_msg_ptlrpc_body(msg);
+ if (!pb) {
+ CERROR("invalid msg %p: no ptlrpc body!\n", msg);
+ return 0;
+
+ }
+ return pb->pb_service_time;
+ }
+ default:
+ CERROR("incorrect message magic: %08x\n", msg->lm_magic);
+ return 0;
+ }
+}
+
+char *lustre_msg_get_jobid(struct lustre_msg *msg)
+{
+ switch (msg->lm_magic) {
+ case LUSTRE_MSG_MAGIC_V1:
+ case LUSTRE_MSG_MAGIC_V1_SWABBED:
+ return NULL;
+ case LUSTRE_MSG_MAGIC_V2: {
+ struct ptlrpc_body *pb =
+ lustre_msg_buf_v2(msg, MSG_PTLRPC_BODY_OFF,
+ sizeof(struct ptlrpc_body));
+ if (!pb)
+ return NULL;
+
+ return pb->pb_jobid;
+ }
+ default:
+ CERROR("incorrect message magic: %08x\n", msg->lm_magic);
+ return NULL;
+ }
+}
+EXPORT_SYMBOL(lustre_msg_get_jobid);
+
+__u32 lustre_msg_get_cksum(struct lustre_msg *msg)
+{
+ switch (msg->lm_magic) {
+ case LUSTRE_MSG_MAGIC_V2:
+ return msg->lm_cksum;
+ default:
+ CERROR("incorrect message magic: %08x\n", msg->lm_magic);
+ return 0;
+ }
+}
+
+__u32 lustre_msg_calc_cksum(struct lustre_msg *msg)
+{
+ switch (msg->lm_magic) {
+ case LUSTRE_MSG_MAGIC_V2: {
+ struct ptlrpc_body *pb = lustre_msg_ptlrpc_body(msg);
+ __u32 crc;
+ unsigned int hsize = 4;
+ cfs_crypto_hash_digest(CFS_HASH_ALG_CRC32, (unsigned char *)pb,
+ lustre_msg_buflen(msg, MSG_PTLRPC_BODY_OFF),
+ NULL, 0, (unsigned char *)&crc, &hsize);
+ return crc;
+ }
+ default:
+ CERROR("incorrect message magic: %08x\n", msg->lm_magic);
+ return 0;
+ }
+}
+
+void lustre_msg_set_handle(struct lustre_msg *msg, struct lustre_handle *handle)
+{
+ switch (msg->lm_magic) {
+ case LUSTRE_MSG_MAGIC_V2: {
+ struct ptlrpc_body *pb = lustre_msg_ptlrpc_body(msg);
+ LASSERTF(pb, "invalid msg %p: no ptlrpc body!\n", msg);
+ pb->pb_handle = *handle;
+ return;
+ }
+ default:
+ LASSERTF(0, "incorrect message magic: %08x\n", msg->lm_magic);
+ }
+}
+EXPORT_SYMBOL(lustre_msg_set_handle);
+
+void lustre_msg_set_type(struct lustre_msg *msg, __u32 type)
+{
+ switch (msg->lm_magic) {
+ case LUSTRE_MSG_MAGIC_V2: {
+ struct ptlrpc_body *pb = lustre_msg_ptlrpc_body(msg);
+ LASSERTF(pb, "invalid msg %p: no ptlrpc body!\n", msg);
+ pb->pb_type = type;
+ return;
+ }
+ default:
+ LASSERTF(0, "incorrect message magic: %08x\n", msg->lm_magic);
+ }
+}
+EXPORT_SYMBOL(lustre_msg_set_type);
+
+void lustre_msg_set_opc(struct lustre_msg *msg, __u32 opc)
+{
+ switch (msg->lm_magic) {
+ case LUSTRE_MSG_MAGIC_V2: {
+ struct ptlrpc_body *pb = lustre_msg_ptlrpc_body(msg);
+ LASSERTF(pb, "invalid msg %p: no ptlrpc body!\n", msg);
+ pb->pb_opc = opc;
+ return;
+ }
+ default:
+ LASSERTF(0, "incorrect message magic: %08x\n", msg->lm_magic);
+ }
+}
+EXPORT_SYMBOL(lustre_msg_set_opc);
+
+void lustre_msg_set_last_xid(struct lustre_msg *msg, __u64 last_xid)
+{
+ switch (msg->lm_magic) {
+ case LUSTRE_MSG_MAGIC_V2: {
+ struct ptlrpc_body *pb = lustre_msg_ptlrpc_body(msg);
+ LASSERTF(pb, "invalid msg %p: no ptlrpc body!\n", msg);
+ pb->pb_last_xid = last_xid;
+ return;
+ }
+ default:
+ LASSERTF(0, "incorrect message magic: %08x\n", msg->lm_magic);
+ }
+}
+EXPORT_SYMBOL(lustre_msg_set_last_xid);
+
+void lustre_msg_set_last_committed(struct lustre_msg *msg, __u64 last_committed)
+{
+ switch (msg->lm_magic) {
+ case LUSTRE_MSG_MAGIC_V2: {
+ struct ptlrpc_body *pb = lustre_msg_ptlrpc_body(msg);
+ LASSERTF(pb, "invalid msg %p: no ptlrpc body!\n", msg);
+ pb->pb_last_committed = last_committed;
+ return;
+ }
+ default:
+ LASSERTF(0, "incorrect message magic: %08x\n", msg->lm_magic);
+ }
+}
+EXPORT_SYMBOL(lustre_msg_set_last_committed);
+
+void lustre_msg_set_versions(struct lustre_msg *msg, __u64 *versions)
+{
+ switch (msg->lm_magic) {
+ case LUSTRE_MSG_MAGIC_V1:
+ return;
+ case LUSTRE_MSG_MAGIC_V2: {
+ struct ptlrpc_body *pb = lustre_msg_ptlrpc_body(msg);
+ LASSERTF(pb, "invalid msg %p: no ptlrpc body!\n", msg);
+ pb->pb_pre_versions[0] = versions[0];
+ pb->pb_pre_versions[1] = versions[1];
+ pb->pb_pre_versions[2] = versions[2];
+ pb->pb_pre_versions[3] = versions[3];
+ return;
+ }
+ default:
+ LASSERTF(0, "incorrect message magic: %08x\n", msg->lm_magic);
+ }
+}
+EXPORT_SYMBOL(lustre_msg_set_versions);
+
+void lustre_msg_set_transno(struct lustre_msg *msg, __u64 transno)
+{
+ switch (msg->lm_magic) {
+ case LUSTRE_MSG_MAGIC_V2: {
+ struct ptlrpc_body *pb = lustre_msg_ptlrpc_body(msg);
+ LASSERTF(pb, "invalid msg %p: no ptlrpc body!\n", msg);
+ pb->pb_transno = transno;
+ return;
+ }
+ default:
+ LASSERTF(0, "incorrect message magic: %08x\n", msg->lm_magic);
+ }
+}
+EXPORT_SYMBOL(lustre_msg_set_transno);
+
+void lustre_msg_set_status(struct lustre_msg *msg, __u32 status)
+{
+ switch (msg->lm_magic) {
+ case LUSTRE_MSG_MAGIC_V2: {
+ struct ptlrpc_body *pb = lustre_msg_ptlrpc_body(msg);
+ LASSERTF(pb, "invalid msg %p: no ptlrpc body!\n", msg);
+ pb->pb_status = status;
+ return;
+ }
+ default:
+ LASSERTF(0, "incorrect message magic: %08x\n", msg->lm_magic);
+ }
+}
+EXPORT_SYMBOL(lustre_msg_set_status);
+
+void lustre_msg_set_conn_cnt(struct lustre_msg *msg, __u32 conn_cnt)
+{
+ switch (msg->lm_magic) {
+ case LUSTRE_MSG_MAGIC_V2: {
+ struct ptlrpc_body *pb = lustre_msg_ptlrpc_body(msg);
+ LASSERTF(pb, "invalid msg %p: no ptlrpc body!\n", msg);
+ pb->pb_conn_cnt = conn_cnt;
+ return;
+ }
+ default:
+ LASSERTF(0, "incorrect message magic: %08x\n", msg->lm_magic);
+ }
+}
+EXPORT_SYMBOL(lustre_msg_set_conn_cnt);
+
+void lustre_msg_set_timeout(struct lustre_msg *msg, __u32 timeout)
+{
+ switch (msg->lm_magic) {
+ case LUSTRE_MSG_MAGIC_V1:
+ return;
+ case LUSTRE_MSG_MAGIC_V2: {
+ struct ptlrpc_body *pb = lustre_msg_ptlrpc_body(msg);
+ LASSERTF(pb, "invalid msg %p: no ptlrpc body!\n", msg);
+ pb->pb_timeout = timeout;
+ return;
+ }
+ default:
+ LASSERTF(0, "incorrect message magic: %08x\n", msg->lm_magic);
+ }
+}
+
+void lustre_msg_set_service_time(struct lustre_msg *msg, __u32 service_time)
+{
+ switch (msg->lm_magic) {
+ case LUSTRE_MSG_MAGIC_V1:
+ return;
+ case LUSTRE_MSG_MAGIC_V2: {
+ struct ptlrpc_body *pb = lustre_msg_ptlrpc_body(msg);
+ LASSERTF(pb, "invalid msg %p: no ptlrpc body!\n", msg);
+ pb->pb_service_time = service_time;
+ return;
+ }
+ default:
+ LASSERTF(0, "incorrect message magic: %08x\n", msg->lm_magic);
+ }
+}
+
+void lustre_msg_set_jobid(struct lustre_msg *msg, char *jobid)
+{
+ switch (msg->lm_magic) {
+ case LUSTRE_MSG_MAGIC_V1:
+ return;
+ case LUSTRE_MSG_MAGIC_V2: {
+ __u32 opc = lustre_msg_get_opc(msg);
+ struct ptlrpc_body *pb;
+
+ /* Don't set jobid for ldlm ast RPCs, they've been shrunk.
+ * See the comment in ptlrpc_request_pack(). */
+ if (!opc || opc == LDLM_BL_CALLBACK ||
+ opc == LDLM_CP_CALLBACK || opc == LDLM_GL_CALLBACK)
+ return;
+
+ pb = lustre_msg_buf_v2(msg, MSG_PTLRPC_BODY_OFF,
+ sizeof(struct ptlrpc_body));
+ LASSERTF(pb, "invalid msg %p: no ptlrpc body!\n", msg);
+
+ if (jobid != NULL)
+ memcpy(pb->pb_jobid, jobid, JOBSTATS_JOBID_SIZE);
+ else if (pb->pb_jobid[0] == '\0')
+ lustre_get_jobid(pb->pb_jobid);
+ return;
+ }
+ default:
+ LASSERTF(0, "incorrect message magic: %08x\n", msg->lm_magic);
+ }
+}
+EXPORT_SYMBOL(lustre_msg_set_jobid);
+
+void lustre_msg_set_cksum(struct lustre_msg *msg, __u32 cksum)
+{
+ switch (msg->lm_magic) {
+ case LUSTRE_MSG_MAGIC_V1:
+ return;
+ case LUSTRE_MSG_MAGIC_V2:
+ msg->lm_cksum = cksum;
+ return;
+ default:
+ LASSERTF(0, "incorrect message magic: %08x\n", msg->lm_magic);
+ }
+}
+
+
+void ptlrpc_request_set_replen(struct ptlrpc_request *req)
+{
+ int count = req_capsule_filled_sizes(&req->rq_pill, RCL_SERVER);
+
+ req->rq_replen = lustre_msg_size(req->rq_reqmsg->lm_magic, count,
+ req->rq_pill.rc_area[RCL_SERVER]);
+ if (req->rq_reqmsg->lm_magic == LUSTRE_MSG_MAGIC_V2)
+ req->rq_reqmsg->lm_repsize = req->rq_replen;
+}
+EXPORT_SYMBOL(ptlrpc_request_set_replen);
+
+void ptlrpc_req_set_repsize(struct ptlrpc_request *req, int count, __u32 *lens)
+{
+ req->rq_replen = lustre_msg_size(req->rq_reqmsg->lm_magic, count, lens);
+ if (req->rq_reqmsg->lm_magic == LUSTRE_MSG_MAGIC_V2)
+ req->rq_reqmsg->lm_repsize = req->rq_replen;
+}
+EXPORT_SYMBOL(ptlrpc_req_set_repsize);
+
+/**
+ * Send a remote set_info_async.
+ *
+ * This may go from client to server or server to client.
+ */
+int do_set_info_async(struct obd_import *imp,
+ int opcode, int version,
+ u32 keylen, void *key,
+ u32 vallen, void *val,
+ struct ptlrpc_request_set *set)
+{
+ struct ptlrpc_request *req;
+ char *tmp;
+ int rc;
+
+ req = ptlrpc_request_alloc(imp, &RQF_OBD_SET_INFO);
+ if (req == NULL)
+ return -ENOMEM;
+
+ req_capsule_set_size(&req->rq_pill, &RMF_SETINFO_KEY,
+ RCL_CLIENT, keylen);
+ req_capsule_set_size(&req->rq_pill, &RMF_SETINFO_VAL,
+ RCL_CLIENT, vallen);
+ rc = ptlrpc_request_pack(req, version, opcode);
+ if (rc) {
+ ptlrpc_request_free(req);
+ return rc;
+ }
+
+ tmp = req_capsule_client_get(&req->rq_pill, &RMF_SETINFO_KEY);
+ memcpy(tmp, key, keylen);
+ tmp = req_capsule_client_get(&req->rq_pill, &RMF_SETINFO_VAL);
+ memcpy(tmp, val, vallen);
+
+ ptlrpc_request_set_replen(req);
+
+ if (set) {
+ ptlrpc_set_add_req(set, req);
+ ptlrpc_check_set(NULL, set);
+ } else {
+ rc = ptlrpc_queue_wait(req);
+ ptlrpc_req_finished(req);
+ }
+
+ return rc;
+}
+EXPORT_SYMBOL(do_set_info_async);
+
+/* byte flipping routines for all wire types declared in
+ * lustre_idl.h implemented here.
+ */
+void lustre_swab_ptlrpc_body(struct ptlrpc_body *b)
+{
+ __swab32s(&b->pb_type);
+ __swab32s(&b->pb_version);
+ __swab32s(&b->pb_opc);
+ __swab32s(&b->pb_status);
+ __swab64s(&b->pb_last_xid);
+ __swab64s(&b->pb_last_seen);
+ __swab64s(&b->pb_last_committed);
+ __swab64s(&b->pb_transno);
+ __swab32s(&b->pb_flags);
+ __swab32s(&b->pb_op_flags);
+ __swab32s(&b->pb_conn_cnt);
+ __swab32s(&b->pb_timeout);
+ __swab32s(&b->pb_service_time);
+ __swab32s(&b->pb_limit);
+ __swab64s(&b->pb_slv);
+ __swab64s(&b->pb_pre_versions[0]);
+ __swab64s(&b->pb_pre_versions[1]);
+ __swab64s(&b->pb_pre_versions[2]);
+ __swab64s(&b->pb_pre_versions[3]);
+ CLASSERT(offsetof(typeof(*b), pb_padding) != 0);
+ /* While we need to maintain compatibility between
+ * clients and servers without ptlrpc_body_v2 (< 2.3)
+ * do not swab any fields beyond pb_jobid, as we are
+ * using this swab function for both ptlrpc_body
+ * and ptlrpc_body_v2. */
+ CLASSERT(offsetof(typeof(*b), pb_jobid) != 0);
+}
+EXPORT_SYMBOL(lustre_swab_ptlrpc_body);
+
+void lustre_swab_connect(struct obd_connect_data *ocd)
+{
+ __swab64s(&ocd->ocd_connect_flags);
+ __swab32s(&ocd->ocd_version);
+ __swab32s(&ocd->ocd_grant);
+ __swab64s(&ocd->ocd_ibits_known);
+ __swab32s(&ocd->ocd_index);
+ __swab32s(&ocd->ocd_brw_size);
+ /* ocd_blocksize and ocd_inodespace don't need to be swabbed because
+ * they are 8-byte values */
+ __swab16s(&ocd->ocd_grant_extent);
+ __swab32s(&ocd->ocd_unused);
+ __swab64s(&ocd->ocd_transno);
+ __swab32s(&ocd->ocd_group);
+ __swab32s(&ocd->ocd_cksum_types);
+ __swab32s(&ocd->ocd_instance);
+ /* Fields after ocd_cksum_types are only accessible by the receiver
+ * if the corresponding flag in ocd_connect_flags is set. Accessing
+ * any field after ocd_maxbytes on the receiver without a valid flag
+ * may result in out-of-bound memory access and kernel oops. */
+ if (ocd->ocd_connect_flags & OBD_CONNECT_MAX_EASIZE)
+ __swab32s(&ocd->ocd_max_easize);
+ if (ocd->ocd_connect_flags & OBD_CONNECT_MAXBYTES)
+ __swab64s(&ocd->ocd_maxbytes);
+ CLASSERT(offsetof(typeof(*ocd), padding1) != 0);
+ CLASSERT(offsetof(typeof(*ocd), padding2) != 0);
+ CLASSERT(offsetof(typeof(*ocd), padding3) != 0);
+ CLASSERT(offsetof(typeof(*ocd), padding4) != 0);
+ CLASSERT(offsetof(typeof(*ocd), padding5) != 0);
+ CLASSERT(offsetof(typeof(*ocd), padding6) != 0);
+ CLASSERT(offsetof(typeof(*ocd), padding7) != 0);
+ CLASSERT(offsetof(typeof(*ocd), padding8) != 0);
+ CLASSERT(offsetof(typeof(*ocd), padding9) != 0);
+ CLASSERT(offsetof(typeof(*ocd), paddingA) != 0);
+ CLASSERT(offsetof(typeof(*ocd), paddingB) != 0);
+ CLASSERT(offsetof(typeof(*ocd), paddingC) != 0);
+ CLASSERT(offsetof(typeof(*ocd), paddingD) != 0);
+ CLASSERT(offsetof(typeof(*ocd), paddingE) != 0);
+ CLASSERT(offsetof(typeof(*ocd), paddingF) != 0);
+}
+
+void lustre_swab_obdo(struct obdo *o)
+{
+ __swab64s(&o->o_valid);
+ lustre_swab_ost_id(&o->o_oi);
+ __swab64s(&o->o_parent_seq);
+ __swab64s(&o->o_size);
+ __swab64s(&o->o_mtime);
+ __swab64s(&o->o_atime);
+ __swab64s(&o->o_ctime);
+ __swab64s(&o->o_blocks);
+ __swab64s(&o->o_grant);
+ __swab32s(&o->o_blksize);
+ __swab32s(&o->o_mode);
+ __swab32s(&o->o_uid);
+ __swab32s(&o->o_gid);
+ __swab32s(&o->o_flags);
+ __swab32s(&o->o_nlink);
+ __swab32s(&o->o_parent_oid);
+ __swab32s(&o->o_misc);
+ __swab64s(&o->o_ioepoch);
+ __swab32s(&o->o_stripe_idx);
+ __swab32s(&o->o_parent_ver);
+ /* o_handle is opaque */
+ /* o_lcookie is swabbed elsewhere */
+ __swab32s(&o->o_uid_h);
+ __swab32s(&o->o_gid_h);
+ __swab64s(&o->o_data_version);
+ CLASSERT(offsetof(typeof(*o), o_padding_4) != 0);
+ CLASSERT(offsetof(typeof(*o), o_padding_5) != 0);
+ CLASSERT(offsetof(typeof(*o), o_padding_6) != 0);
+
+}
+EXPORT_SYMBOL(lustre_swab_obdo);
+
+void lustre_swab_obd_statfs(struct obd_statfs *os)
+{
+ __swab64s(&os->os_type);
+ __swab64s(&os->os_blocks);
+ __swab64s(&os->os_bfree);
+ __swab64s(&os->os_bavail);
+ __swab64s(&os->os_files);
+ __swab64s(&os->os_ffree);
+ /* no need to swab os_fsid */
+ __swab32s(&os->os_bsize);
+ __swab32s(&os->os_namelen);
+ __swab64s(&os->os_maxbytes);
+ __swab32s(&os->os_state);
+ CLASSERT(offsetof(typeof(*os), os_fprecreated) != 0);
+ CLASSERT(offsetof(typeof(*os), os_spare2) != 0);
+ CLASSERT(offsetof(typeof(*os), os_spare3) != 0);
+ CLASSERT(offsetof(typeof(*os), os_spare4) != 0);
+ CLASSERT(offsetof(typeof(*os), os_spare5) != 0);
+ CLASSERT(offsetof(typeof(*os), os_spare6) != 0);
+ CLASSERT(offsetof(typeof(*os), os_spare7) != 0);
+ CLASSERT(offsetof(typeof(*os), os_spare8) != 0);
+ CLASSERT(offsetof(typeof(*os), os_spare9) != 0);
+}
+EXPORT_SYMBOL(lustre_swab_obd_statfs);
+
+void lustre_swab_obd_ioobj(struct obd_ioobj *ioo)
+{
+ lustre_swab_ost_id(&ioo->ioo_oid);
+ __swab32s(&ioo->ioo_max_brw);
+ __swab32s(&ioo->ioo_bufcnt);
+}
+EXPORT_SYMBOL(lustre_swab_obd_ioobj);
+
+void lustre_swab_niobuf_remote(struct niobuf_remote *nbr)
+{
+ __swab64s(&nbr->offset);
+ __swab32s(&nbr->len);
+ __swab32s(&nbr->flags);
+}
+EXPORT_SYMBOL(lustre_swab_niobuf_remote);
+
+void lustre_swab_ost_body(struct ost_body *b)
+{
+ lustre_swab_obdo(&b->oa);
+}
+EXPORT_SYMBOL(lustre_swab_ost_body);
+
+void lustre_swab_ost_last_id(u64 *id)
+{
+ __swab64s(id);
+}
+EXPORT_SYMBOL(lustre_swab_ost_last_id);
+
+void lustre_swab_generic_32s(__u32 *val)
+{
+ __swab32s(val);
+}
+EXPORT_SYMBOL(lustre_swab_generic_32s);
+
+void lustre_swab_gl_desc(union ldlm_gl_desc *desc)
+{
+ lustre_swab_lu_fid(&desc->lquota_desc.gl_id.qid_fid);
+ __swab64s(&desc->lquota_desc.gl_flags);
+ __swab64s(&desc->lquota_desc.gl_ver);
+ __swab64s(&desc->lquota_desc.gl_hardlimit);
+ __swab64s(&desc->lquota_desc.gl_softlimit);
+ __swab64s(&desc->lquota_desc.gl_time);
+ CLASSERT(offsetof(typeof(desc->lquota_desc), gl_pad2) != 0);
+}
+
+void lustre_swab_ost_lvb_v1(struct ost_lvb_v1 *lvb)
+{
+ __swab64s(&lvb->lvb_size);
+ __swab64s(&lvb->lvb_mtime);
+ __swab64s(&lvb->lvb_atime);
+ __swab64s(&lvb->lvb_ctime);
+ __swab64s(&lvb->lvb_blocks);
+}
+EXPORT_SYMBOL(lustre_swab_ost_lvb_v1);
+
+void lustre_swab_ost_lvb(struct ost_lvb *lvb)
+{
+ __swab64s(&lvb->lvb_size);
+ __swab64s(&lvb->lvb_mtime);
+ __swab64s(&lvb->lvb_atime);
+ __swab64s(&lvb->lvb_ctime);
+ __swab64s(&lvb->lvb_blocks);
+ __swab32s(&lvb->lvb_mtime_ns);
+ __swab32s(&lvb->lvb_atime_ns);
+ __swab32s(&lvb->lvb_ctime_ns);
+ __swab32s(&lvb->lvb_padding);
+}
+EXPORT_SYMBOL(lustre_swab_ost_lvb);
+
+void lustre_swab_lquota_lvb(struct lquota_lvb *lvb)
+{
+ __swab64s(&lvb->lvb_flags);
+ __swab64s(&lvb->lvb_id_may_rel);
+ __swab64s(&lvb->lvb_id_rel);
+ __swab64s(&lvb->lvb_id_qunit);
+ __swab64s(&lvb->lvb_pad1);
+}
+EXPORT_SYMBOL(lustre_swab_lquota_lvb);
+
+void lustre_swab_mdt_body(struct mdt_body *b)
+{
+ lustre_swab_lu_fid(&b->fid1);
+ lustre_swab_lu_fid(&b->fid2);
+ /* handle is opaque */
+ __swab64s(&b->valid);
+ __swab64s(&b->size);
+ __swab64s(&b->mtime);
+ __swab64s(&b->atime);
+ __swab64s(&b->ctime);
+ __swab64s(&b->blocks);
+ __swab64s(&b->ioepoch);
+ __swab64s(&b->t_state);
+ __swab32s(&b->fsuid);
+ __swab32s(&b->fsgid);
+ __swab32s(&b->capability);
+ __swab32s(&b->mode);
+ __swab32s(&b->uid);
+ __swab32s(&b->gid);
+ __swab32s(&b->flags);
+ __swab32s(&b->rdev);
+ __swab32s(&b->nlink);
+ CLASSERT(offsetof(typeof(*b), unused2) != 0);
+ __swab32s(&b->suppgid);
+ __swab32s(&b->eadatasize);
+ __swab32s(&b->aclsize);
+ __swab32s(&b->max_mdsize);
+ __swab32s(&b->max_cookiesize);
+ __swab32s(&b->uid_h);
+ __swab32s(&b->gid_h);
+ CLASSERT(offsetof(typeof(*b), padding_5) != 0);
+}
+EXPORT_SYMBOL(lustre_swab_mdt_body);
+
+void lustre_swab_mdt_ioepoch(struct mdt_ioepoch *b)
+{
+ /* handle is opaque */
+ __swab64s(&b->ioepoch);
+ __swab32s(&b->flags);
+ CLASSERT(offsetof(typeof(*b), padding) != 0);
+}
+EXPORT_SYMBOL(lustre_swab_mdt_ioepoch);
+
+void lustre_swab_mgs_target_info(struct mgs_target_info *mti)
+{
+ int i;
+ __swab32s(&mti->mti_lustre_ver);
+ __swab32s(&mti->mti_stripe_index);
+ __swab32s(&mti->mti_config_ver);
+ __swab32s(&mti->mti_flags);
+ __swab32s(&mti->mti_instance);
+ __swab32s(&mti->mti_nid_count);
+ CLASSERT(sizeof(lnet_nid_t) == sizeof(__u64));
+ for (i = 0; i < MTI_NIDS_MAX; i++)
+ __swab64s(&mti->mti_nids[i]);
+}
+EXPORT_SYMBOL(lustre_swab_mgs_target_info);
+
+void lustre_swab_mgs_nidtbl_entry(struct mgs_nidtbl_entry *entry)
+{
+ int i;
+
+ __swab64s(&entry->mne_version);
+ __swab32s(&entry->mne_instance);
+ __swab32s(&entry->mne_index);
+ __swab32s(&entry->mne_length);
+
+ /* mne_nid_(count|type) must be one byte size because we're gonna
+ * access it w/o swapping. */
+ CLASSERT(sizeof(entry->mne_nid_count) == sizeof(__u8));
+ CLASSERT(sizeof(entry->mne_nid_type) == sizeof(__u8));
+
+ /* remove this assertion if ipv6 is supported. */
+ LASSERT(entry->mne_nid_type == 0);
+ for (i = 0; i < entry->mne_nid_count; i++) {
+ CLASSERT(sizeof(lnet_nid_t) == sizeof(__u64));
+ __swab64s(&entry->u.nids[i]);
+ }
+}
+EXPORT_SYMBOL(lustre_swab_mgs_nidtbl_entry);
+
+void lustre_swab_mgs_config_body(struct mgs_config_body *body)
+{
+ __swab64s(&body->mcb_offset);
+ __swab32s(&body->mcb_units);
+ __swab16s(&body->mcb_type);
+}
+EXPORT_SYMBOL(lustre_swab_mgs_config_body);
+
+void lustre_swab_mgs_config_res(struct mgs_config_res *body)
+{
+ __swab64s(&body->mcr_offset);
+ __swab64s(&body->mcr_size);
+}
+EXPORT_SYMBOL(lustre_swab_mgs_config_res);
+
+static void lustre_swab_obd_dqinfo(struct obd_dqinfo *i)
+{
+ __swab64s(&i->dqi_bgrace);
+ __swab64s(&i->dqi_igrace);
+ __swab32s(&i->dqi_flags);
+ __swab32s(&i->dqi_valid);
+}
+
+static void lustre_swab_obd_dqblk(struct obd_dqblk *b)
+{
+ __swab64s(&b->dqb_ihardlimit);
+ __swab64s(&b->dqb_isoftlimit);
+ __swab64s(&b->dqb_curinodes);
+ __swab64s(&b->dqb_bhardlimit);
+ __swab64s(&b->dqb_bsoftlimit);
+ __swab64s(&b->dqb_curspace);
+ __swab64s(&b->dqb_btime);
+ __swab64s(&b->dqb_itime);
+ __swab32s(&b->dqb_valid);
+ CLASSERT(offsetof(typeof(*b), dqb_padding) != 0);
+}
+
+void lustre_swab_obd_quotactl(struct obd_quotactl *q)
+{
+ __swab32s(&q->qc_cmd);
+ __swab32s(&q->qc_type);
+ __swab32s(&q->qc_id);
+ __swab32s(&q->qc_stat);
+ lustre_swab_obd_dqinfo(&q->qc_dqinfo);
+ lustre_swab_obd_dqblk(&q->qc_dqblk);
+}
+EXPORT_SYMBOL(lustre_swab_obd_quotactl);
+
+void lustre_swab_mdt_remote_perm(struct mdt_remote_perm *p)
+{
+ __swab32s(&p->rp_uid);
+ __swab32s(&p->rp_gid);
+ __swab32s(&p->rp_fsuid);
+ __swab32s(&p->rp_fsuid_h);
+ __swab32s(&p->rp_fsgid);
+ __swab32s(&p->rp_fsgid_h);
+ __swab32s(&p->rp_access_perm);
+ __swab32s(&p->rp_padding);
+};
+EXPORT_SYMBOL(lustre_swab_mdt_remote_perm);
+
+void lustre_swab_fid2path(struct getinfo_fid2path *gf)
+{
+ lustre_swab_lu_fid(&gf->gf_fid);
+ __swab64s(&gf->gf_recno);
+ __swab32s(&gf->gf_linkno);
+ __swab32s(&gf->gf_pathlen);
+}
+EXPORT_SYMBOL(lustre_swab_fid2path);
+
+void lustre_swab_fiemap_extent(struct ll_fiemap_extent *fm_extent)
+{
+ __swab64s(&fm_extent->fe_logical);
+ __swab64s(&fm_extent->fe_physical);
+ __swab64s(&fm_extent->fe_length);
+ __swab32s(&fm_extent->fe_flags);
+ __swab32s(&fm_extent->fe_device);
+}
+
+void lustre_swab_fiemap(struct ll_user_fiemap *fiemap)
+{
+ int i;
+
+ __swab64s(&fiemap->fm_start);
+ __swab64s(&fiemap->fm_length);
+ __swab32s(&fiemap->fm_flags);
+ __swab32s(&fiemap->fm_mapped_extents);
+ __swab32s(&fiemap->fm_extent_count);
+ __swab32s(&fiemap->fm_reserved);
+
+ for (i = 0; i < fiemap->fm_mapped_extents; i++)
+ lustre_swab_fiemap_extent(&fiemap->fm_extents[i]);
+}
+EXPORT_SYMBOL(lustre_swab_fiemap);
+
+void lustre_swab_idx_info(struct idx_info *ii)
+{
+ __swab32s(&ii->ii_magic);
+ __swab32s(&ii->ii_flags);
+ __swab16s(&ii->ii_count);
+ __swab32s(&ii->ii_attrs);
+ lustre_swab_lu_fid(&ii->ii_fid);
+ __swab64s(&ii->ii_version);
+ __swab64s(&ii->ii_hash_start);
+ __swab64s(&ii->ii_hash_end);
+ __swab16s(&ii->ii_keysize);
+ __swab16s(&ii->ii_recsize);
+}
+
+void lustre_swab_lip_header(struct lu_idxpage *lip)
+{
+ /* swab header */
+ __swab32s(&lip->lip_magic);
+ __swab16s(&lip->lip_flags);
+ __swab16s(&lip->lip_nr);
+}
+EXPORT_SYMBOL(lustre_swab_lip_header);
+
+void lustre_swab_mdt_rec_reint (struct mdt_rec_reint *rr)
+{
+ __swab32s(&rr->rr_opcode);
+ __swab32s(&rr->rr_cap);
+ __swab32s(&rr->rr_fsuid);
+ /* rr_fsuid_h is unused */
+ __swab32s(&rr->rr_fsgid);
+ /* rr_fsgid_h is unused */
+ __swab32s(&rr->rr_suppgid1);
+ /* rr_suppgid1_h is unused */
+ __swab32s(&rr->rr_suppgid2);
+ /* rr_suppgid2_h is unused */
+ lustre_swab_lu_fid(&rr->rr_fid1);
+ lustre_swab_lu_fid(&rr->rr_fid2);
+ __swab64s(&rr->rr_mtime);
+ __swab64s(&rr->rr_atime);
+ __swab64s(&rr->rr_ctime);
+ __swab64s(&rr->rr_size);
+ __swab64s(&rr->rr_blocks);
+ __swab32s(&rr->rr_bias);
+ __swab32s(&rr->rr_mode);
+ __swab32s(&rr->rr_flags);
+ __swab32s(&rr->rr_flags_h);
+ __swab32s(&rr->rr_umask);
+
+ CLASSERT(offsetof(typeof(*rr), rr_padding_4) != 0);
+};
+EXPORT_SYMBOL(lustre_swab_mdt_rec_reint);
+
+void lustre_swab_lov_desc(struct lov_desc *ld)
+{
+ __swab32s(&ld->ld_tgt_count);
+ __swab32s(&ld->ld_active_tgt_count);
+ __swab32s(&ld->ld_default_stripe_count);
+ __swab32s(&ld->ld_pattern);
+ __swab64s(&ld->ld_default_stripe_size);
+ __swab64s(&ld->ld_default_stripe_offset);
+ __swab32s(&ld->ld_qos_maxage);
+ /* uuid endian insensitive */
+}
+EXPORT_SYMBOL(lustre_swab_lov_desc);
+
+void lustre_swab_lmv_desc(struct lmv_desc *ld)
+{
+ __swab32s(&ld->ld_tgt_count);
+ __swab32s(&ld->ld_active_tgt_count);
+ __swab32s(&ld->ld_default_stripe_count);
+ __swab32s(&ld->ld_pattern);
+ __swab64s(&ld->ld_default_hash_size);
+ __swab32s(&ld->ld_qos_maxage);
+ /* uuid endian insensitive */
+}
+
+void lustre_swab_lmv_stripe_md(struct lmv_stripe_md *mea)
+{
+ __swab32s(&mea->mea_magic);
+ __swab32s(&mea->mea_count);
+ __swab32s(&mea->mea_master);
+ CLASSERT(offsetof(typeof(*mea), mea_padding) != 0);
+}
+
+void lustre_swab_lmv_user_md(struct lmv_user_md *lum)
+{
+ int i;
+
+ __swab32s(&lum->lum_magic);
+ __swab32s(&lum->lum_stripe_count);
+ __swab32s(&lum->lum_stripe_offset);
+ __swab32s(&lum->lum_hash_type);
+ __swab32s(&lum->lum_type);
+ CLASSERT(offsetof(typeof(*lum), lum_padding1) != 0);
+ CLASSERT(offsetof(typeof(*lum), lum_padding2) != 0);
+ CLASSERT(offsetof(typeof(*lum), lum_padding3) != 0);
+
+ for (i = 0; i < lum->lum_stripe_count; i++) {
+ __swab32s(&lum->lum_objects[i].lum_mds);
+ lustre_swab_lu_fid(&lum->lum_objects[i].lum_fid);
+ }
+
+}
+EXPORT_SYMBOL(lustre_swab_lmv_user_md);
+
+static void print_lum(struct lov_user_md *lum)
+{
+ CDEBUG(D_OTHER, "lov_user_md %p:\n", lum);
+ CDEBUG(D_OTHER, "\tlmm_magic: %#x\n", lum->lmm_magic);
+ CDEBUG(D_OTHER, "\tlmm_pattern: %#x\n", lum->lmm_pattern);
+ CDEBUG(D_OTHER, "\tlmm_object_id: %llu\n", lmm_oi_id(&lum->lmm_oi));
+ CDEBUG(D_OTHER, "\tlmm_object_gr: %llu\n", lmm_oi_seq(&lum->lmm_oi));
+ CDEBUG(D_OTHER, "\tlmm_stripe_size: %#x\n", lum->lmm_stripe_size);
+ CDEBUG(D_OTHER, "\tlmm_stripe_count: %#x\n", lum->lmm_stripe_count);
+ CDEBUG(D_OTHER, "\tlmm_stripe_offset/lmm_layout_gen: %#x\n",
+ lum->lmm_stripe_offset);
+}
+
+static void lustre_swab_lmm_oi(struct ost_id *oi)
+{
+ __swab64s(&oi->oi.oi_id);
+ __swab64s(&oi->oi.oi_seq);
+}
+
+static void lustre_swab_lov_user_md_common(struct lov_user_md_v1 *lum)
+{
+ __swab32s(&lum->lmm_magic);
+ __swab32s(&lum->lmm_pattern);
+ lustre_swab_lmm_oi(&lum->lmm_oi);
+ __swab32s(&lum->lmm_stripe_size);
+ __swab16s(&lum->lmm_stripe_count);
+ __swab16s(&lum->lmm_stripe_offset);
+ print_lum(lum);
+}
+
+void lustre_swab_lov_user_md_v1(struct lov_user_md_v1 *lum)
+{
+ CDEBUG(D_IOCTL, "swabbing lov_user_md v1\n");
+ lustre_swab_lov_user_md_common(lum);
+}
+EXPORT_SYMBOL(lustre_swab_lov_user_md_v1);
+
+void lustre_swab_lov_user_md_v3(struct lov_user_md_v3 *lum)
+{
+ CDEBUG(D_IOCTL, "swabbing lov_user_md v3\n");
+ lustre_swab_lov_user_md_common((struct lov_user_md_v1 *)lum);
+ /* lmm_pool_name nothing to do with char */
+}
+EXPORT_SYMBOL(lustre_swab_lov_user_md_v3);
+
+void lustre_swab_lov_mds_md(struct lov_mds_md *lmm)
+{
+ CDEBUG(D_IOCTL, "swabbing lov_mds_md\n");
+ __swab32s(&lmm->lmm_magic);
+ __swab32s(&lmm->lmm_pattern);
+ lustre_swab_lmm_oi(&lmm->lmm_oi);
+ __swab32s(&lmm->lmm_stripe_size);
+ __swab16s(&lmm->lmm_stripe_count);
+ __swab16s(&lmm->lmm_layout_gen);
+}
+EXPORT_SYMBOL(lustre_swab_lov_mds_md);
+
+void lustre_swab_lov_user_md_objects(struct lov_user_ost_data *lod,
+ int stripe_count)
+{
+ int i;
+
+ for (i = 0; i < stripe_count; i++) {
+ lustre_swab_ost_id(&(lod[i].l_ost_oi));
+ __swab32s(&(lod[i].l_ost_gen));
+ __swab32s(&(lod[i].l_ost_idx));
+ }
+}
+EXPORT_SYMBOL(lustre_swab_lov_user_md_objects);
+
+void lustre_swab_ldlm_res_id(struct ldlm_res_id *id)
+{
+ int i;
+
+ for (i = 0; i < RES_NAME_SIZE; i++)
+ __swab64s(&id->name[i]);
+}
+EXPORT_SYMBOL(lustre_swab_ldlm_res_id);
+
+void lustre_swab_ldlm_policy_data(ldlm_wire_policy_data_t *d)
+{
+ /* the lock data is a union and the first two fields are always an
+ * extent so it's ok to process an LDLM_EXTENT and LDLM_FLOCK lock
+ * data the same way. */
+ __swab64s(&d->l_extent.start);
+ __swab64s(&d->l_extent.end);
+ __swab64s(&d->l_extent.gid);
+ __swab64s(&d->l_flock.lfw_owner);
+ __swab32s(&d->l_flock.lfw_pid);
+}
+EXPORT_SYMBOL(lustre_swab_ldlm_policy_data);
+
+void lustre_swab_ldlm_intent(struct ldlm_intent *i)
+{
+ __swab64s(&i->opc);
+}
+EXPORT_SYMBOL(lustre_swab_ldlm_intent);
+
+void lustre_swab_ldlm_resource_desc(struct ldlm_resource_desc *r)
+{
+ __swab32s(&r->lr_type);
+ CLASSERT(offsetof(typeof(*r), lr_padding) != 0);
+ lustre_swab_ldlm_res_id(&r->lr_name);
+}
+EXPORT_SYMBOL(lustre_swab_ldlm_resource_desc);
+
+void lustre_swab_ldlm_lock_desc(struct ldlm_lock_desc *l)
+{
+ lustre_swab_ldlm_resource_desc(&l->l_resource);
+ __swab32s(&l->l_req_mode);
+ __swab32s(&l->l_granted_mode);
+ lustre_swab_ldlm_policy_data(&l->l_policy_data);
+}
+EXPORT_SYMBOL(lustre_swab_ldlm_lock_desc);
+
+void lustre_swab_ldlm_request(struct ldlm_request *rq)
+{
+ __swab32s(&rq->lock_flags);
+ lustre_swab_ldlm_lock_desc(&rq->lock_desc);
+ __swab32s(&rq->lock_count);
+ /* lock_handle[] opaque */
+}
+EXPORT_SYMBOL(lustre_swab_ldlm_request);
+
+void lustre_swab_ldlm_reply(struct ldlm_reply *r)
+{
+ __swab32s(&r->lock_flags);
+ CLASSERT(offsetof(typeof(*r), lock_padding) != 0);
+ lustre_swab_ldlm_lock_desc(&r->lock_desc);
+ /* lock_handle opaque */
+ __swab64s(&r->lock_policy_res1);
+ __swab64s(&r->lock_policy_res2);
+}
+EXPORT_SYMBOL(lustre_swab_ldlm_reply);
+
+void lustre_swab_quota_body(struct quota_body *b)
+{
+ lustre_swab_lu_fid(&b->qb_fid);
+ lustre_swab_lu_fid((struct lu_fid *)&b->qb_id);
+ __swab32s(&b->qb_flags);
+ __swab64s(&b->qb_count);
+ __swab64s(&b->qb_usage);
+ __swab64s(&b->qb_slv_ver);
+}
+
+/* Dump functions */
+void dump_ioo(struct obd_ioobj *ioo)
+{
+ CDEBUG(D_RPCTRACE,
+ "obd_ioobj: ioo_oid=" DOSTID ", ioo_max_brw=%#x, ioo_bufct=%d\n",
+ POSTID(&ioo->ioo_oid), ioo->ioo_max_brw,
+ ioo->ioo_bufcnt);
+}
+EXPORT_SYMBOL(dump_ioo);
+
+void dump_rniobuf(struct niobuf_remote *nb)
+{
+ CDEBUG(D_RPCTRACE, "niobuf_remote: offset=%llu, len=%d, flags=%x\n",
+ nb->offset, nb->len, nb->flags);
+}
+EXPORT_SYMBOL(dump_rniobuf);
+
+void dump_obdo(struct obdo *oa)
+{
+ __u32 valid = oa->o_valid;
+
+ CDEBUG(D_RPCTRACE, "obdo: o_valid = %08x\n", valid);
+ if (valid & OBD_MD_FLID)
+ CDEBUG(D_RPCTRACE, "obdo: id = "DOSTID"\n", POSTID(&oa->o_oi));
+ if (valid & OBD_MD_FLFID)
+ CDEBUG(D_RPCTRACE, "obdo: o_parent_seq = %#llx\n",
+ oa->o_parent_seq);
+ if (valid & OBD_MD_FLSIZE)
+ CDEBUG(D_RPCTRACE, "obdo: o_size = %lld\n", oa->o_size);
+ if (valid & OBD_MD_FLMTIME)
+ CDEBUG(D_RPCTRACE, "obdo: o_mtime = %lld\n", oa->o_mtime);
+ if (valid & OBD_MD_FLATIME)
+ CDEBUG(D_RPCTRACE, "obdo: o_atime = %lld\n", oa->o_atime);
+ if (valid & OBD_MD_FLCTIME)
+ CDEBUG(D_RPCTRACE, "obdo: o_ctime = %lld\n", oa->o_ctime);
+ if (valid & OBD_MD_FLBLOCKS) /* allocation of space */
+ CDEBUG(D_RPCTRACE, "obdo: o_blocks = %lld\n", oa->o_blocks);
+ if (valid & OBD_MD_FLGRANT)
+ CDEBUG(D_RPCTRACE, "obdo: o_grant = %lld\n", oa->o_grant);
+ if (valid & OBD_MD_FLBLKSZ)
+ CDEBUG(D_RPCTRACE, "obdo: o_blksize = %d\n", oa->o_blksize);
+ if (valid & (OBD_MD_FLTYPE | OBD_MD_FLMODE))
+ CDEBUG(D_RPCTRACE, "obdo: o_mode = %o\n",
+ oa->o_mode & ((valid & OBD_MD_FLTYPE ? S_IFMT : 0) |
+ (valid & OBD_MD_FLMODE ? ~S_IFMT : 0)));
+ if (valid & OBD_MD_FLUID)
+ CDEBUG(D_RPCTRACE, "obdo: o_uid = %u\n", oa->o_uid);
+ if (valid & OBD_MD_FLUID)
+ CDEBUG(D_RPCTRACE, "obdo: o_uid_h = %u\n", oa->o_uid_h);
+ if (valid & OBD_MD_FLGID)
+ CDEBUG(D_RPCTRACE, "obdo: o_gid = %u\n", oa->o_gid);
+ if (valid & OBD_MD_FLGID)
+ CDEBUG(D_RPCTRACE, "obdo: o_gid_h = %u\n", oa->o_gid_h);
+ if (valid & OBD_MD_FLFLAGS)
+ CDEBUG(D_RPCTRACE, "obdo: o_flags = %x\n", oa->o_flags);
+ if (valid & OBD_MD_FLNLINK)
+ CDEBUG(D_RPCTRACE, "obdo: o_nlink = %u\n", oa->o_nlink);
+ else if (valid & OBD_MD_FLCKSUM)
+ CDEBUG(D_RPCTRACE, "obdo: o_checksum (o_nlink) = %u\n",
+ oa->o_nlink);
+ if (valid & OBD_MD_FLGENER)
+ CDEBUG(D_RPCTRACE, "obdo: o_parent_oid = %x\n",
+ oa->o_parent_oid);
+ if (valid & OBD_MD_FLEPOCH)
+ CDEBUG(D_RPCTRACE, "obdo: o_ioepoch = %lld\n",
+ oa->o_ioepoch);
+ if (valid & OBD_MD_FLFID) {
+ CDEBUG(D_RPCTRACE, "obdo: o_stripe_idx = %u\n",
+ oa->o_stripe_idx);
+ CDEBUG(D_RPCTRACE, "obdo: o_parent_ver = %x\n",
+ oa->o_parent_ver);
+ }
+ if (valid & OBD_MD_FLHANDLE)
+ CDEBUG(D_RPCTRACE, "obdo: o_handle = %lld\n",
+ oa->o_handle.cookie);
+ if (valid & OBD_MD_FLCOOKIE)
+ CDEBUG(D_RPCTRACE, "obdo: o_lcookie = (llog_cookie dumping not yet implemented)\n");
+}
+EXPORT_SYMBOL(dump_obdo);
+
+void dump_ost_body(struct ost_body *ob)
+{
+ dump_obdo(&ob->oa);
+}
+EXPORT_SYMBOL(dump_ost_body);
+
+void dump_rcs(__u32 *rc)
+{
+ CDEBUG(D_RPCTRACE, "rmf_rcs: %d\n", *rc);
+}
+EXPORT_SYMBOL(dump_rcs);
+
+static inline int req_ptlrpc_body_swabbed(struct ptlrpc_request *req)
+{
+ LASSERT(req->rq_reqmsg);
+
+ switch (req->rq_reqmsg->lm_magic) {
+ case LUSTRE_MSG_MAGIC_V2:
+ return lustre_req_swabbed(req, MSG_PTLRPC_BODY_OFF);
+ default:
+ CERROR("bad lustre msg magic: %#08X\n",
+ req->rq_reqmsg->lm_magic);
+ }
+ return 0;
+}
+
+static inline int rep_ptlrpc_body_swabbed(struct ptlrpc_request *req)
+{
+ LASSERT(req->rq_repmsg);
+
+ switch (req->rq_repmsg->lm_magic) {
+ case LUSTRE_MSG_MAGIC_V2:
+ return lustre_rep_swabbed(req, MSG_PTLRPC_BODY_OFF);
+ default:
+ /* uninitialized yet */
+ return 0;
+ }
+}
+
+void _debug_req(struct ptlrpc_request *req,
+ struct libcfs_debug_msg_data *msgdata,
+ const char *fmt, ...)
+{
+ int req_ok = req->rq_reqmsg != NULL;
+ int rep_ok = req->rq_repmsg != NULL;
+ lnet_nid_t nid = LNET_NID_ANY;
+ va_list args;
+
+ if (ptlrpc_req_need_swab(req)) {
+ req_ok = req_ok && req_ptlrpc_body_swabbed(req);
+ rep_ok = rep_ok && rep_ptlrpc_body_swabbed(req);
+ }
+
+ if (req->rq_import && req->rq_import->imp_connection)
+ nid = req->rq_import->imp_connection->c_peer.nid;
+ else if (req->rq_export && req->rq_export->exp_connection)
+ nid = req->rq_export->exp_connection->c_peer.nid;
+
+ va_start(args, fmt);
+ libcfs_debug_vmsg2(msgdata, fmt, args,
+ " req@%p x%llu/t%lld(%lld) o%d->%s@%s:%d/%d lens %d/%d e %d to %d dl " CFS_TIME_T " ref %d fl " REQ_FLAGS_FMT "/%x/%x rc %d/%d\n",
+ req, req->rq_xid, req->rq_transno,
+ req_ok ? lustre_msg_get_transno(req->rq_reqmsg) : 0,
+ req_ok ? lustre_msg_get_opc(req->rq_reqmsg) : -1,
+ req->rq_import ?
+ req->rq_import->imp_obd->obd_name :
+ req->rq_export ?
+ req->rq_export->exp_client_uuid.uuid :
+ "<?>",
+ libcfs_nid2str(nid),
+ req->rq_request_portal, req->rq_reply_portal,
+ req->rq_reqlen, req->rq_replen,
+ req->rq_early_count, req->rq_timedout,
+ req->rq_deadline,
+ atomic_read(&req->rq_refcount),
+ DEBUG_REQ_FLAGS(req),
+ req_ok ? lustre_msg_get_flags(req->rq_reqmsg) : -1,
+ rep_ok ? lustre_msg_get_flags(req->rq_repmsg) : -1,
+ req->rq_status,
+ rep_ok ? lustre_msg_get_status(req->rq_repmsg) : -1);
+ va_end(args);
+}
+EXPORT_SYMBOL(_debug_req);
+
+void lustre_swab_lustre_capa(struct lustre_capa *c)
+{
+ lustre_swab_lu_fid(&c->lc_fid);
+ __swab64s(&c->lc_opc);
+ __swab64s(&c->lc_uid);
+ __swab64s(&c->lc_gid);
+ __swab32s(&c->lc_flags);
+ __swab32s(&c->lc_keyid);
+ __swab32s(&c->lc_timeout);
+ __swab32s(&c->lc_expiry);
+}
+EXPORT_SYMBOL(lustre_swab_lustre_capa);
+
+void lustre_swab_lustre_capa_key(struct lustre_capa_key *k)
+{
+ __swab64s(&k->lk_seq);
+ __swab32s(&k->lk_keyid);
+ CLASSERT(offsetof(typeof(*k), lk_padding) != 0);
+}
+EXPORT_SYMBOL(lustre_swab_lustre_capa_key);
+
+void lustre_swab_hsm_user_state(struct hsm_user_state *state)
+{
+ __swab32s(&state->hus_states);
+ __swab32s(&state->hus_archive_id);
+}
+EXPORT_SYMBOL(lustre_swab_hsm_user_state);
+
+void lustre_swab_hsm_state_set(struct hsm_state_set *hss)
+{
+ __swab32s(&hss->hss_valid);
+ __swab64s(&hss->hss_setmask);
+ __swab64s(&hss->hss_clearmask);
+ __swab32s(&hss->hss_archive_id);
+}
+EXPORT_SYMBOL(lustre_swab_hsm_state_set);
+
+void lustre_swab_hsm_extent(struct hsm_extent *extent)
+{
+ __swab64s(&extent->offset);
+ __swab64s(&extent->length);
+}
+
+void lustre_swab_hsm_current_action(struct hsm_current_action *action)
+{
+ __swab32s(&action->hca_state);
+ __swab32s(&action->hca_action);
+ lustre_swab_hsm_extent(&action->hca_location);
+}
+EXPORT_SYMBOL(lustre_swab_hsm_current_action);
+
+void lustre_swab_hsm_user_item(struct hsm_user_item *hui)
+{
+ lustre_swab_lu_fid(&hui->hui_fid);
+ lustre_swab_hsm_extent(&hui->hui_extent);
+}
+EXPORT_SYMBOL(lustre_swab_hsm_user_item);
+
+void lustre_swab_layout_intent(struct layout_intent *li)
+{
+ __swab32s(&li->li_opc);
+ __swab32s(&li->li_flags);
+ __swab64s(&li->li_start);
+ __swab64s(&li->li_end);
+}
+EXPORT_SYMBOL(lustre_swab_layout_intent);
+
+void lustre_swab_hsm_progress_kernel(struct hsm_progress_kernel *hpk)
+{
+ lustre_swab_lu_fid(&hpk->hpk_fid);
+ __swab64s(&hpk->hpk_cookie);
+ __swab64s(&hpk->hpk_extent.offset);
+ __swab64s(&hpk->hpk_extent.length);
+ __swab16s(&hpk->hpk_flags);
+ __swab16s(&hpk->hpk_errval);
+}
+EXPORT_SYMBOL(lustre_swab_hsm_progress_kernel);
+
+void lustre_swab_hsm_request(struct hsm_request *hr)
+{
+ __swab32s(&hr->hr_action);
+ __swab32s(&hr->hr_archive_id);
+ __swab64s(&hr->hr_flags);
+ __swab32s(&hr->hr_itemcount);
+ __swab32s(&hr->hr_data_len);
+}
+EXPORT_SYMBOL(lustre_swab_hsm_request);
+
+void lustre_swab_update_buf(struct update_buf *ub)
+{
+ __swab32s(&ub->ub_magic);
+ __swab32s(&ub->ub_count);
+}
+EXPORT_SYMBOL(lustre_swab_update_buf);
+
+void lustre_swab_update_reply_buf(struct update_reply *ur)
+{
+ int i;
+
+ __swab32s(&ur->ur_version);
+ __swab32s(&ur->ur_count);
+ for (i = 0; i < ur->ur_count; i++)
+ __swab32s(&ur->ur_lens[i]);
+}
+EXPORT_SYMBOL(lustre_swab_update_reply_buf);
+
+void lustre_swab_swap_layouts(struct mdc_swap_layouts *msl)
+{
+ __swab64s(&msl->msl_flags);
+}
+EXPORT_SYMBOL(lustre_swab_swap_layouts);
+
+void lustre_swab_close_data(struct close_data *cd)
+{
+ lustre_swab_lu_fid(&cd->cd_fid);
+ __swab64s(&cd->cd_data_version);
+}
+EXPORT_SYMBOL(lustre_swab_close_data);
diff --git a/drivers/staging/lustre/lustre/ptlrpc/pers.c b/drivers/staging/lustre/lustre/ptlrpc/pers.c
new file mode 100644
index 000000000..e1334c24e
--- /dev/null
+++ b/drivers/staging/lustre/lustre/ptlrpc/pers.c
@@ -0,0 +1,75 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ */
+
+#define DEBUG_SUBSYSTEM S_RPC
+
+#include "../include/obd_support.h"
+#include "../include/obd_class.h"
+#include "../include/lustre_lib.h"
+#include "../include/lustre_ha.h"
+#include "../include/lustre_import.h"
+
+#include "ptlrpc_internal.h"
+
+
+void ptlrpc_fill_bulk_md(lnet_md_t *md, struct ptlrpc_bulk_desc *desc,
+ int mdidx)
+{
+ CLASSERT(PTLRPC_MAX_BRW_PAGES < LI_POISON);
+
+ LASSERT(mdidx < desc->bd_md_max_brw);
+ LASSERT(desc->bd_iov_count <= PTLRPC_MAX_BRW_PAGES);
+ LASSERT(!(md->options & (LNET_MD_IOVEC | LNET_MD_KIOV |
+ LNET_MD_PHYS)));
+
+ md->options |= LNET_MD_KIOV;
+ md->length = max(0, desc->bd_iov_count - mdidx * LNET_MAX_IOV);
+ md->length = min_t(unsigned int, LNET_MAX_IOV, md->length);
+ if (desc->bd_enc_iov)
+ md->start = &desc->bd_enc_iov[mdidx * LNET_MAX_IOV];
+ else
+ md->start = &desc->bd_iov[mdidx * LNET_MAX_IOV];
+}
+
+void ptlrpc_add_bulk_page(struct ptlrpc_bulk_desc *desc, struct page *page,
+ int pageoffset, int len)
+{
+ lnet_kiov_t *kiov = &desc->bd_iov[desc->bd_iov_count];
+
+ kiov->kiov_page = page;
+ kiov->kiov_offset = pageoffset;
+ kiov->kiov_len = len;
+
+ desc->bd_iov_count++;
+}
diff --git a/drivers/staging/lustre/lustre/ptlrpc/pinger.c b/drivers/staging/lustre/lustre/ptlrpc/pinger.c
new file mode 100644
index 000000000..9dbda9332
--- /dev/null
+++ b/drivers/staging/lustre/lustre/ptlrpc/pinger.c
@@ -0,0 +1,678 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2011, 2012, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ *
+ * lustre/ptlrpc/pinger.c
+ *
+ * Portal-RPC reconnection and replay operations, for use in recovery.
+ */
+
+#define DEBUG_SUBSYSTEM S_RPC
+
+#include "../include/obd_support.h"
+#include "../include/obd_class.h"
+#include "ptlrpc_internal.h"
+
+static int suppress_pings;
+module_param(suppress_pings, int, 0644);
+MODULE_PARM_DESC(suppress_pings, "Suppress pings");
+
+struct mutex pinger_mutex;
+static LIST_HEAD(pinger_imports);
+static struct list_head timeout_list = LIST_HEAD_INIT(timeout_list);
+
+int ptlrpc_pinger_suppress_pings(void)
+{
+ return suppress_pings;
+}
+EXPORT_SYMBOL(ptlrpc_pinger_suppress_pings);
+
+struct ptlrpc_request *
+ptlrpc_prep_ping(struct obd_import *imp)
+{
+ struct ptlrpc_request *req;
+
+ req = ptlrpc_request_alloc_pack(imp, &RQF_OBD_PING,
+ LUSTRE_OBD_VERSION, OBD_PING);
+ if (req) {
+ ptlrpc_request_set_replen(req);
+ req->rq_no_resend = req->rq_no_delay = 1;
+ }
+ return req;
+}
+
+int ptlrpc_obd_ping(struct obd_device *obd)
+{
+ int rc;
+ struct ptlrpc_request *req;
+
+ req = ptlrpc_prep_ping(obd->u.cli.cl_import);
+ if (req == NULL)
+ return -ENOMEM;
+
+ req->rq_send_state = LUSTRE_IMP_FULL;
+
+ rc = ptlrpc_queue_wait(req);
+
+ ptlrpc_req_finished(req);
+
+ return rc;
+}
+EXPORT_SYMBOL(ptlrpc_obd_ping);
+
+int ptlrpc_ping(struct obd_import *imp)
+{
+ struct ptlrpc_request *req;
+
+ req = ptlrpc_prep_ping(imp);
+ if (req == NULL) {
+ CERROR("OOM trying to ping %s->%s\n",
+ imp->imp_obd->obd_uuid.uuid,
+ obd2cli_tgt(imp->imp_obd));
+ return -ENOMEM;
+ }
+
+ DEBUG_REQ(D_INFO, req, "pinging %s->%s",
+ imp->imp_obd->obd_uuid.uuid, obd2cli_tgt(imp->imp_obd));
+ ptlrpcd_add_req(req, PDL_POLICY_ROUND, -1);
+
+ return 0;
+}
+
+void ptlrpc_update_next_ping(struct obd_import *imp, int soon)
+{
+ int time = soon ? PING_INTERVAL_SHORT : PING_INTERVAL;
+ if (imp->imp_state == LUSTRE_IMP_DISCON) {
+ int dtime = max_t(int, CONNECTION_SWITCH_MIN,
+ AT_OFF ? 0 :
+ at_get(&imp->imp_at.iat_net_latency));
+ time = min(time, dtime);
+ }
+ imp->imp_next_ping = cfs_time_shift(time);
+}
+
+void ptlrpc_ping_import_soon(struct obd_import *imp)
+{
+ imp->imp_next_ping = cfs_time_current();
+}
+
+static inline int imp_is_deactive(struct obd_import *imp)
+{
+ return (imp->imp_deactive ||
+ OBD_FAIL_CHECK(OBD_FAIL_PTLRPC_IMP_DEACTIVE));
+}
+
+static inline int ptlrpc_next_reconnect(struct obd_import *imp)
+{
+ if (imp->imp_server_timeout)
+ return cfs_time_shift(obd_timeout / 2);
+ else
+ return cfs_time_shift(obd_timeout);
+}
+
+long pinger_check_timeout(unsigned long time)
+{
+ struct timeout_item *item;
+ unsigned long timeout = PING_INTERVAL;
+
+ /* The timeout list is a increase order sorted list */
+ mutex_lock(&pinger_mutex);
+ list_for_each_entry(item, &timeout_list, ti_chain) {
+ int ti_timeout = item->ti_timeout;
+ if (timeout > ti_timeout)
+ timeout = ti_timeout;
+ break;
+ }
+ mutex_unlock(&pinger_mutex);
+
+ return cfs_time_sub(cfs_time_add(time, cfs_time_seconds(timeout)),
+ cfs_time_current());
+}
+
+static bool ir_up;
+
+void ptlrpc_pinger_ir_up(void)
+{
+ CDEBUG(D_HA, "IR up\n");
+ ir_up = true;
+}
+EXPORT_SYMBOL(ptlrpc_pinger_ir_up);
+
+void ptlrpc_pinger_ir_down(void)
+{
+ CDEBUG(D_HA, "IR down\n");
+ ir_up = false;
+}
+EXPORT_SYMBOL(ptlrpc_pinger_ir_down);
+
+static void ptlrpc_pinger_process_import(struct obd_import *imp,
+ unsigned long this_ping)
+{
+ int level;
+ int force;
+ int force_next;
+ int suppress;
+
+ spin_lock(&imp->imp_lock);
+
+ level = imp->imp_state;
+ force = imp->imp_force_verify;
+ force_next = imp->imp_force_next_verify;
+ /*
+ * This will be used below only if the import is "FULL".
+ */
+ suppress = ir_up && OCD_HAS_FLAG(&imp->imp_connect_data, PINGLESS);
+
+ imp->imp_force_verify = 0;
+
+ if (cfs_time_aftereq(imp->imp_next_ping - 5 * CFS_TICK, this_ping) &&
+ !force) {
+ spin_unlock(&imp->imp_lock);
+ return;
+ }
+
+ imp->imp_force_next_verify = 0;
+
+ spin_unlock(&imp->imp_lock);
+
+ CDEBUG(level == LUSTRE_IMP_FULL ? D_INFO : D_HA, "%s->%s: level %s/%u force %u force_next %u deactive %u pingable %u suppress %u\n",
+ imp->imp_obd->obd_uuid.uuid, obd2cli_tgt(imp->imp_obd),
+ ptlrpc_import_state_name(level), level, force, force_next,
+ imp->imp_deactive, imp->imp_pingable, suppress);
+
+ if (level == LUSTRE_IMP_DISCON && !imp_is_deactive(imp)) {
+ /* wait for a while before trying recovery again */
+ imp->imp_next_ping = ptlrpc_next_reconnect(imp);
+ if (!imp->imp_no_pinger_recover)
+ ptlrpc_initiate_recovery(imp);
+ } else if (level != LUSTRE_IMP_FULL ||
+ imp->imp_obd->obd_no_recov ||
+ imp_is_deactive(imp)) {
+ CDEBUG(D_HA, "%s->%s: not pinging (in recovery or recovery disabled: %s)\n",
+ imp->imp_obd->obd_uuid.uuid, obd2cli_tgt(imp->imp_obd),
+ ptlrpc_import_state_name(level));
+ if (force) {
+ spin_lock(&imp->imp_lock);
+ imp->imp_force_verify = 1;
+ spin_unlock(&imp->imp_lock);
+ }
+ } else if ((imp->imp_pingable && !suppress) || force_next || force) {
+ ptlrpc_ping(imp);
+ }
+}
+
+static int ptlrpc_pinger_main(void *arg)
+{
+ struct ptlrpc_thread *thread = (struct ptlrpc_thread *)arg;
+
+ /* Record that the thread is running */
+ thread_set_flags(thread, SVC_RUNNING);
+ wake_up(&thread->t_ctl_waitq);
+
+ /* And now, loop forever, pinging as needed. */
+ while (1) {
+ unsigned long this_ping = cfs_time_current();
+ struct l_wait_info lwi;
+ long time_to_next_wake;
+ struct timeout_item *item;
+ struct list_head *iter;
+
+ mutex_lock(&pinger_mutex);
+ list_for_each_entry(item, &timeout_list, ti_chain) {
+ item->ti_cb(item, item->ti_cb_data);
+ }
+ list_for_each(iter, &pinger_imports) {
+ struct obd_import *imp =
+ list_entry(iter, struct obd_import,
+ imp_pinger_chain);
+
+ ptlrpc_pinger_process_import(imp, this_ping);
+ /* obd_timeout might have changed */
+ if (imp->imp_pingable && imp->imp_next_ping &&
+ cfs_time_after(imp->imp_next_ping,
+ cfs_time_add(this_ping,
+ cfs_time_seconds(PING_INTERVAL))))
+ ptlrpc_update_next_ping(imp, 0);
+ }
+ mutex_unlock(&pinger_mutex);
+ /* update memory usage info */
+ obd_update_maxusage();
+
+ /* Wait until the next ping time, or until we're stopped. */
+ time_to_next_wake = pinger_check_timeout(this_ping);
+ /* The ping sent by ptlrpc_send_rpc may get sent out
+ say .01 second after this.
+ ptlrpc_pinger_sending_on_import will then set the
+ next ping time to next_ping + .01 sec, which means
+ we will SKIP the next ping at next_ping, and the
+ ping will get sent 2 timeouts from now! Beware. */
+ CDEBUG(D_INFO, "next wakeup in "CFS_DURATION_T" ("
+ CFS_TIME_T")\n", time_to_next_wake,
+ cfs_time_add(this_ping,
+ cfs_time_seconds(PING_INTERVAL)));
+ if (time_to_next_wake > 0) {
+ lwi = LWI_TIMEOUT(max_t(long, time_to_next_wake,
+ cfs_time_seconds(1)),
+ NULL, NULL);
+ l_wait_event(thread->t_ctl_waitq,
+ thread_is_stopping(thread) ||
+ thread_is_event(thread),
+ &lwi);
+ if (thread_test_and_clear_flags(thread, SVC_STOPPING)) {
+ break;
+ } else {
+ /* woken after adding import to reset timer */
+ thread_test_and_clear_flags(thread, SVC_EVENT);
+ }
+ }
+ }
+
+ thread_set_flags(thread, SVC_STOPPED);
+ wake_up(&thread->t_ctl_waitq);
+
+ CDEBUG(D_NET, "pinger thread exiting, process %d\n", current_pid());
+ return 0;
+}
+
+static struct ptlrpc_thread pinger_thread;
+
+int ptlrpc_start_pinger(void)
+{
+ struct l_wait_info lwi = { 0 };
+ int rc;
+
+ if (!thread_is_init(&pinger_thread) &&
+ !thread_is_stopped(&pinger_thread))
+ return -EALREADY;
+
+ init_waitqueue_head(&pinger_thread.t_ctl_waitq);
+
+ strcpy(pinger_thread.t_name, "ll_ping");
+
+ /* CLONE_VM and CLONE_FILES just avoid a needless copy, because we
+ * just drop the VM and FILES in cfs_daemonize_ctxt() right away. */
+ rc = PTR_ERR(kthread_run(ptlrpc_pinger_main, &pinger_thread,
+ "%s", pinger_thread.t_name));
+ if (IS_ERR_VALUE(rc)) {
+ CERROR("cannot start thread: %d\n", rc);
+ return rc;
+ }
+ l_wait_event(pinger_thread.t_ctl_waitq,
+ thread_is_running(&pinger_thread), &lwi);
+
+ if (suppress_pings)
+ CWARN("Pings will be suppressed at the request of the administrator. The configuration shall meet the additional requirements described in the manual. (Search for the \"suppress_pings\" kernel module parameter.)\n");
+
+ return 0;
+}
+
+int ptlrpc_pinger_remove_timeouts(void);
+
+int ptlrpc_stop_pinger(void)
+{
+ struct l_wait_info lwi = { 0 };
+ int rc = 0;
+
+ if (thread_is_init(&pinger_thread) ||
+ thread_is_stopped(&pinger_thread))
+ return -EALREADY;
+
+ ptlrpc_pinger_remove_timeouts();
+ thread_set_flags(&pinger_thread, SVC_STOPPING);
+ wake_up(&pinger_thread.t_ctl_waitq);
+
+ l_wait_event(pinger_thread.t_ctl_waitq,
+ thread_is_stopped(&pinger_thread), &lwi);
+
+ return rc;
+}
+
+void ptlrpc_pinger_sending_on_import(struct obd_import *imp)
+{
+ ptlrpc_update_next_ping(imp, 0);
+}
+EXPORT_SYMBOL(ptlrpc_pinger_sending_on_import);
+
+void ptlrpc_pinger_commit_expected(struct obd_import *imp)
+{
+ ptlrpc_update_next_ping(imp, 1);
+ assert_spin_locked(&imp->imp_lock);
+ /*
+ * Avoid reading stale imp_connect_data. When not sure if pings are
+ * expected or not on next connection, we assume they are not and force
+ * one anyway to guarantee the chance of updating
+ * imp_peer_committed_transno.
+ */
+ if (imp->imp_state != LUSTRE_IMP_FULL ||
+ OCD_HAS_FLAG(&imp->imp_connect_data, PINGLESS))
+ imp->imp_force_next_verify = 1;
+}
+
+int ptlrpc_pinger_add_import(struct obd_import *imp)
+{
+ if (!list_empty(&imp->imp_pinger_chain))
+ return -EALREADY;
+
+ mutex_lock(&pinger_mutex);
+ CDEBUG(D_HA, "adding pingable import %s->%s\n",
+ imp->imp_obd->obd_uuid.uuid, obd2cli_tgt(imp->imp_obd));
+ /* if we add to pinger we want recovery on this import */
+ imp->imp_obd->obd_no_recov = 0;
+ ptlrpc_update_next_ping(imp, 0);
+ /* XXX sort, blah blah */
+ list_add_tail(&imp->imp_pinger_chain, &pinger_imports);
+ class_import_get(imp);
+
+ ptlrpc_pinger_wake_up();
+ mutex_unlock(&pinger_mutex);
+
+ return 0;
+}
+EXPORT_SYMBOL(ptlrpc_pinger_add_import);
+
+int ptlrpc_pinger_del_import(struct obd_import *imp)
+{
+ if (list_empty(&imp->imp_pinger_chain))
+ return -ENOENT;
+
+ mutex_lock(&pinger_mutex);
+ list_del_init(&imp->imp_pinger_chain);
+ CDEBUG(D_HA, "removing pingable import %s->%s\n",
+ imp->imp_obd->obd_uuid.uuid, obd2cli_tgt(imp->imp_obd));
+ /* if we remove from pinger we don't want recovery on this import */
+ imp->imp_obd->obd_no_recov = 1;
+ class_import_put(imp);
+ mutex_unlock(&pinger_mutex);
+ return 0;
+}
+EXPORT_SYMBOL(ptlrpc_pinger_del_import);
+
+/**
+ * Register a timeout callback to the pinger list, and the callback will
+ * be called when timeout happens.
+ */
+struct timeout_item *ptlrpc_new_timeout(int time, enum timeout_event event,
+ timeout_cb_t cb, void *data)
+{
+ struct timeout_item *ti;
+
+ OBD_ALLOC_PTR(ti);
+ if (!ti)
+ return NULL;
+
+ INIT_LIST_HEAD(&ti->ti_obd_list);
+ INIT_LIST_HEAD(&ti->ti_chain);
+ ti->ti_timeout = time;
+ ti->ti_event = event;
+ ti->ti_cb = cb;
+ ti->ti_cb_data = data;
+
+ return ti;
+}
+
+/**
+ * Register timeout event on the pinger thread.
+ * Note: the timeout list is an sorted list with increased timeout value.
+ */
+static struct timeout_item*
+ptlrpc_pinger_register_timeout(int time, enum timeout_event event,
+ timeout_cb_t cb, void *data)
+{
+ struct timeout_item *item, *tmp;
+
+ LASSERT(mutex_is_locked(&pinger_mutex));
+
+ list_for_each_entry(item, &timeout_list, ti_chain)
+ if (item->ti_event == event)
+ goto out;
+
+ item = ptlrpc_new_timeout(time, event, cb, data);
+ if (item) {
+ list_for_each_entry_reverse(tmp, &timeout_list, ti_chain) {
+ if (tmp->ti_timeout < time) {
+ list_add(&item->ti_chain, &tmp->ti_chain);
+ goto out;
+ }
+ }
+ list_add(&item->ti_chain, &timeout_list);
+ }
+out:
+ return item;
+}
+
+/* Add a client_obd to the timeout event list, when timeout(@time)
+ * happens, the callback(@cb) will be called.
+ */
+int ptlrpc_add_timeout_client(int time, enum timeout_event event,
+ timeout_cb_t cb, void *data,
+ struct list_head *obd_list)
+{
+ struct timeout_item *ti;
+
+ mutex_lock(&pinger_mutex);
+ ti = ptlrpc_pinger_register_timeout(time, event, cb, data);
+ if (!ti) {
+ mutex_unlock(&pinger_mutex);
+ return -EINVAL;
+ }
+ list_add(obd_list, &ti->ti_obd_list);
+ mutex_unlock(&pinger_mutex);
+ return 0;
+}
+EXPORT_SYMBOL(ptlrpc_add_timeout_client);
+
+int ptlrpc_del_timeout_client(struct list_head *obd_list,
+ enum timeout_event event)
+{
+ struct timeout_item *ti = NULL, *item;
+
+ if (list_empty(obd_list))
+ return 0;
+ mutex_lock(&pinger_mutex);
+ list_del_init(obd_list);
+ /**
+ * If there are no obd attached to the timeout event
+ * list, remove this timeout event from the pinger
+ */
+ list_for_each_entry(item, &timeout_list, ti_chain) {
+ if (item->ti_event == event) {
+ ti = item;
+ break;
+ }
+ }
+ LASSERTF(ti != NULL, "ti is NULL !\n");
+ if (list_empty(&ti->ti_obd_list)) {
+ list_del(&ti->ti_chain);
+ OBD_FREE_PTR(ti);
+ }
+ mutex_unlock(&pinger_mutex);
+ return 0;
+}
+EXPORT_SYMBOL(ptlrpc_del_timeout_client);
+
+int ptlrpc_pinger_remove_timeouts(void)
+{
+ struct timeout_item *item, *tmp;
+
+ mutex_lock(&pinger_mutex);
+ list_for_each_entry_safe(item, tmp, &timeout_list, ti_chain) {
+ LASSERT(list_empty(&item->ti_obd_list));
+ list_del(&item->ti_chain);
+ OBD_FREE_PTR(item);
+ }
+ mutex_unlock(&pinger_mutex);
+ return 0;
+}
+
+void ptlrpc_pinger_wake_up(void)
+{
+ thread_add_flags(&pinger_thread, SVC_EVENT);
+ wake_up(&pinger_thread.t_ctl_waitq);
+}
+
+/* Ping evictor thread */
+#define PET_READY 1
+#define PET_TERMINATE 2
+
+static int pet_refcount;
+static int pet_state;
+static wait_queue_head_t pet_waitq;
+LIST_HEAD(pet_list);
+static DEFINE_SPINLOCK(pet_lock);
+
+int ping_evictor_wake(struct obd_export *exp)
+{
+ struct obd_device *obd;
+
+ spin_lock(&pet_lock);
+ if (pet_state != PET_READY) {
+ /* eventually the new obd will call here again. */
+ spin_unlock(&pet_lock);
+ return 1;
+ }
+
+ obd = class_exp2obd(exp);
+ if (list_empty(&obd->obd_evict_list)) {
+ class_incref(obd, "evictor", obd);
+ list_add(&obd->obd_evict_list, &pet_list);
+ }
+ spin_unlock(&pet_lock);
+
+ wake_up(&pet_waitq);
+ return 0;
+}
+
+static int ping_evictor_main(void *arg)
+{
+ struct obd_device *obd;
+ struct obd_export *exp;
+ struct l_wait_info lwi = { 0 };
+ time_t expire_time;
+
+ unshare_fs_struct();
+
+ CDEBUG(D_HA, "Starting Ping Evictor\n");
+ pet_state = PET_READY;
+ while (1) {
+ l_wait_event(pet_waitq, (!list_empty(&pet_list)) ||
+ (pet_state == PET_TERMINATE), &lwi);
+
+ /* loop until all obd's will be removed */
+ if ((pet_state == PET_TERMINATE) && list_empty(&pet_list))
+ break;
+
+ /* we only get here if pet_exp != NULL, and the end of this
+ * loop is the only place which sets it NULL again, so lock
+ * is not strictly necessary. */
+ spin_lock(&pet_lock);
+ obd = list_entry(pet_list.next, struct obd_device,
+ obd_evict_list);
+ spin_unlock(&pet_lock);
+
+ expire_time = get_seconds() - PING_EVICT_TIMEOUT;
+
+ CDEBUG(D_HA, "evicting all exports of obd %s older than %ld\n",
+ obd->obd_name, expire_time);
+
+ /* Exports can't be deleted out of the list while we hold
+ * the obd lock (class_unlink_export), which means we can't
+ * lose the last ref on the export. If they've already been
+ * removed from the list, we won't find them here. */
+ spin_lock(&obd->obd_dev_lock);
+ while (!list_empty(&obd->obd_exports_timed)) {
+ exp = list_entry(obd->obd_exports_timed.next,
+ struct obd_export,
+ exp_obd_chain_timed);
+ if (expire_time > exp->exp_last_request_time) {
+ class_export_get(exp);
+ spin_unlock(&obd->obd_dev_lock);
+ LCONSOLE_WARN("%s: haven't heard from client %s (at %s) in %ld seconds. I think it's dead, and I am evicting it. exp %p, cur %ld expire %ld last %ld\n",
+ obd->obd_name,
+ obd_uuid2str(&exp->exp_client_uuid),
+ obd_export_nid2str(exp),
+ (long)(get_seconds() -
+ exp->exp_last_request_time),
+ exp, (long)get_seconds(),
+ (long)expire_time,
+ (long)exp->exp_last_request_time);
+ CDEBUG(D_HA, "Last request was at %ld\n",
+ exp->exp_last_request_time);
+ class_fail_export(exp);
+ class_export_put(exp);
+ spin_lock(&obd->obd_dev_lock);
+ } else {
+ /* List is sorted, so everyone below is ok */
+ break;
+ }
+ }
+ spin_unlock(&obd->obd_dev_lock);
+
+ spin_lock(&pet_lock);
+ list_del_init(&obd->obd_evict_list);
+ spin_unlock(&pet_lock);
+
+ class_decref(obd, "evictor", obd);
+ }
+ CDEBUG(D_HA, "Exiting Ping Evictor\n");
+
+ return 0;
+}
+
+void ping_evictor_start(void)
+{
+ struct task_struct *task;
+
+ if (++pet_refcount > 1)
+ return;
+
+ init_waitqueue_head(&pet_waitq);
+
+ task = kthread_run(ping_evictor_main, NULL, "ll_evictor");
+ if (IS_ERR(task)) {
+ pet_refcount--;
+ CERROR("Cannot start ping evictor thread: %ld\n",
+ PTR_ERR(task));
+ }
+}
+EXPORT_SYMBOL(ping_evictor_start);
+
+void ping_evictor_stop(void)
+{
+ if (--pet_refcount > 0)
+ return;
+
+ pet_state = PET_TERMINATE;
+ wake_up(&pet_waitq);
+}
+EXPORT_SYMBOL(ping_evictor_stop);
diff --git a/drivers/staging/lustre/lustre/ptlrpc/ptlrpc_internal.h b/drivers/staging/lustre/lustre/ptlrpc/ptlrpc_internal.h
new file mode 100644
index 000000000..a66dc3c6d
--- /dev/null
+++ b/drivers/staging/lustre/lustre/ptlrpc/ptlrpc_internal.h
@@ -0,0 +1,312 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2011, 2012, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ */
+
+/* Intramodule declarations for ptlrpc. */
+
+#ifndef PTLRPC_INTERNAL_H
+#define PTLRPC_INTERNAL_H
+
+#include "../ldlm/ldlm_internal.h"
+
+struct ldlm_namespace;
+struct obd_import;
+struct ldlm_res_id;
+struct ptlrpc_request_set;
+extern int test_req_buffer_pressure;
+extern struct mutex ptlrpc_all_services_mutex;
+
+int ptlrpc_start_thread(struct ptlrpc_service_part *svcpt, int wait);
+/* ptlrpcd.c */
+int ptlrpcd_start(int index, int max, const char *name, struct ptlrpcd_ctl *pc);
+
+/* client.c */
+struct ptlrpc_bulk_desc *ptlrpc_new_bulk(unsigned npages, unsigned max_brw,
+ unsigned type, unsigned portal);
+int ptlrpc_request_cache_init(void);
+void ptlrpc_request_cache_fini(void);
+struct ptlrpc_request *ptlrpc_request_cache_alloc(gfp_t flags);
+void ptlrpc_request_cache_free(struct ptlrpc_request *req);
+void ptlrpc_init_xid(void);
+
+/* events.c */
+int ptlrpc_init_portals(void);
+void ptlrpc_exit_portals(void);
+
+void ptlrpc_request_handle_notconn(struct ptlrpc_request *);
+void lustre_assert_wire_constants(void);
+int ptlrpc_import_in_recovery(struct obd_import *imp);
+int ptlrpc_set_import_discon(struct obd_import *imp, __u32 conn_cnt);
+void ptlrpc_handle_failed_import(struct obd_import *imp);
+int ptlrpc_replay_next(struct obd_import *imp, int *inflight);
+void ptlrpc_initiate_recovery(struct obd_import *imp);
+
+int lustre_unpack_req_ptlrpc_body(struct ptlrpc_request *req, int offset);
+int lustre_unpack_rep_ptlrpc_body(struct ptlrpc_request *req, int offset);
+
+#if defined(CONFIG_PROC_FS)
+void ptlrpc_lprocfs_register_service(struct proc_dir_entry *proc_entry,
+ struct ptlrpc_service *svc);
+void ptlrpc_lprocfs_unregister_service(struct ptlrpc_service *svc);
+void ptlrpc_lprocfs_rpc_sent(struct ptlrpc_request *req, long amount);
+void ptlrpc_lprocfs_do_request_stat(struct ptlrpc_request *req,
+ long q_usec, long work_usec);
+#else
+#define ptlrpc_lprocfs_register_service(params...) do {} while (0)
+#define ptlrpc_lprocfs_unregister_service(params...) do {} while (0)
+#define ptlrpc_lprocfs_rpc_sent(params...) do {} while (0)
+#define ptlrpc_lprocfs_do_request_stat(params...) do {} while (0)
+#endif /* CONFIG_PROC_FS */
+
+/* NRS */
+
+/**
+ * NRS core object.
+ *
+ * Holds NRS core fields.
+ */
+struct nrs_core {
+ /**
+ * Protects nrs_core::nrs_policies, serializes external policy
+ * registration/unregistration, and NRS core lprocfs operations.
+ */
+ struct mutex nrs_mutex;
+ /* XXX: This is just for liblustre. Remove the #if defined directive
+ * when the * "cfs_" prefix is dropped from cfs_list_head. */
+ /**
+ * List of all policy descriptors registered with NRS core; protected
+ * by nrs_core::nrs_mutex.
+ */
+ struct list_head nrs_policies;
+
+};
+
+int ptlrpc_service_nrs_setup(struct ptlrpc_service *svc);
+void ptlrpc_service_nrs_cleanup(struct ptlrpc_service *svc);
+
+void ptlrpc_nrs_req_initialize(struct ptlrpc_service_part *svcpt,
+ struct ptlrpc_request *req, bool hp);
+void ptlrpc_nrs_req_finalize(struct ptlrpc_request *req);
+void ptlrpc_nrs_req_stop_nolock(struct ptlrpc_request *req);
+void ptlrpc_nrs_req_add(struct ptlrpc_service_part *svcpt,
+ struct ptlrpc_request *req, bool hp);
+
+struct ptlrpc_request *
+ptlrpc_nrs_req_get_nolock0(struct ptlrpc_service_part *svcpt, bool hp,
+ bool peek, bool force);
+
+static inline struct ptlrpc_request *
+ptlrpc_nrs_req_get_nolock(struct ptlrpc_service_part *svcpt, bool hp,
+ bool force)
+{
+ return ptlrpc_nrs_req_get_nolock0(svcpt, hp, false, force);
+}
+
+static inline struct ptlrpc_request *
+ptlrpc_nrs_req_peek_nolock(struct ptlrpc_service_part *svcpt, bool hp)
+{
+ return ptlrpc_nrs_req_get_nolock0(svcpt, hp, true, false);
+}
+
+void ptlrpc_nrs_req_del_nolock(struct ptlrpc_request *req);
+bool ptlrpc_nrs_req_pending_nolock(struct ptlrpc_service_part *svcpt, bool hp);
+
+int ptlrpc_nrs_policy_control(const struct ptlrpc_service *svc,
+ enum ptlrpc_nrs_queue_type queue, char *name,
+ enum ptlrpc_nrs_ctl opc, bool single, void *arg);
+
+int ptlrpc_nrs_init(void);
+void ptlrpc_nrs_fini(void);
+
+static inline bool nrs_svcpt_has_hp(const struct ptlrpc_service_part *svcpt)
+{
+ return svcpt->scp_nrs_hp != NULL;
+}
+
+static inline bool nrs_svc_has_hp(const struct ptlrpc_service *svc)
+{
+ /**
+ * If the first service partition has an HP NRS head, all service
+ * partitions will.
+ */
+ return nrs_svcpt_has_hp(svc->srv_parts[0]);
+}
+
+static inline
+struct ptlrpc_nrs *nrs_svcpt2nrs(struct ptlrpc_service_part *svcpt, bool hp)
+{
+ LASSERT(ergo(hp, nrs_svcpt_has_hp(svcpt)));
+ return hp ? svcpt->scp_nrs_hp : &svcpt->scp_nrs_reg;
+}
+
+static inline int nrs_pol2cptid(const struct ptlrpc_nrs_policy *policy)
+{
+ return policy->pol_nrs->nrs_svcpt->scp_cpt;
+}
+
+static inline
+struct ptlrpc_service *nrs_pol2svc(struct ptlrpc_nrs_policy *policy)
+{
+ return policy->pol_nrs->nrs_svcpt->scp_service;
+}
+
+static inline
+struct ptlrpc_service_part *nrs_pol2svcpt(struct ptlrpc_nrs_policy *policy)
+{
+ return policy->pol_nrs->nrs_svcpt;
+}
+
+static inline
+struct cfs_cpt_table *nrs_pol2cptab(struct ptlrpc_nrs_policy *policy)
+{
+ return nrs_pol2svc(policy)->srv_cptable;
+}
+
+static inline struct ptlrpc_nrs_resource *
+nrs_request_resource(struct ptlrpc_nrs_request *nrq)
+{
+ LASSERT(nrq->nr_initialized);
+ LASSERT(!nrq->nr_finalized);
+
+ return nrq->nr_res_ptrs[nrq->nr_res_idx];
+}
+
+static inline
+struct ptlrpc_nrs_policy *nrs_request_policy(struct ptlrpc_nrs_request *nrq)
+{
+ return nrs_request_resource(nrq)->res_policy;
+}
+
+#define NRS_LPROCFS_QUANTUM_NAME_REG "reg_quantum:"
+#define NRS_LPROCFS_QUANTUM_NAME_HP "hp_quantum:"
+
+/**
+ * the maximum size of nrs_crrn_client::cc_quantum and nrs_orr_data::od_quantum.
+ */
+#define LPROCFS_NRS_QUANTUM_MAX 65535
+
+/**
+ * Max valid command string is the size of the labels, plus "65535" twice, plus
+ * a separating space character.
+ */
+#define LPROCFS_NRS_WR_QUANTUM_MAX_CMD \
+ sizeof(NRS_LPROCFS_QUANTUM_NAME_REG __stringify(LPROCFS_NRS_QUANTUM_MAX) " " \
+ NRS_LPROCFS_QUANTUM_NAME_HP __stringify(LPROCFS_NRS_QUANTUM_MAX))
+
+/* recovd_thread.c */
+
+int ptlrpc_expire_one_request(struct ptlrpc_request *req, int async_unlink);
+
+/* pers.c */
+void ptlrpc_fill_bulk_md(lnet_md_t *md, struct ptlrpc_bulk_desc *desc,
+ int mdcnt);
+void ptlrpc_add_bulk_page(struct ptlrpc_bulk_desc *desc, struct page *page,
+ int pageoffset, int len);
+
+/* pack_generic.c */
+struct ptlrpc_reply_state *
+lustre_get_emerg_rs(struct ptlrpc_service_part *svcpt);
+void lustre_put_emerg_rs(struct ptlrpc_reply_state *rs);
+
+/* pinger.c */
+int ptlrpc_start_pinger(void);
+int ptlrpc_stop_pinger(void);
+void ptlrpc_pinger_sending_on_import(struct obd_import *imp);
+void ptlrpc_pinger_commit_expected(struct obd_import *imp);
+void ptlrpc_pinger_wake_up(void);
+void ptlrpc_ping_import_soon(struct obd_import *imp);
+int ping_evictor_wake(struct obd_export *exp);
+
+/* sec_null.c */
+int sptlrpc_null_init(void);
+void sptlrpc_null_fini(void);
+
+/* sec_plain.c */
+int sptlrpc_plain_init(void);
+void sptlrpc_plain_fini(void);
+
+/* sec_bulk.c */
+int sptlrpc_enc_pool_init(void);
+void sptlrpc_enc_pool_fini(void);
+int sptlrpc_proc_enc_pool_seq_show(struct seq_file *m, void *v);
+
+/* sec_lproc.c */
+#if defined(CONFIG_PROC_FS)
+int sptlrpc_lproc_init(void);
+void sptlrpc_lproc_fini(void);
+#else
+static inline int sptlrpc_lproc_init(void)
+{ return 0; }
+static inline void sptlrpc_lproc_fini(void) {}
+#endif
+
+/* sec_gc.c */
+int sptlrpc_gc_init(void);
+void sptlrpc_gc_fini(void);
+
+/* sec_config.c */
+void sptlrpc_conf_choose_flavor(enum lustre_sec_part from,
+ enum lustre_sec_part to,
+ struct obd_uuid *target,
+ lnet_nid_t nid,
+ struct sptlrpc_flavor *sf);
+int sptlrpc_conf_init(void);
+void sptlrpc_conf_fini(void);
+
+/* sec.c */
+int sptlrpc_init(void);
+void sptlrpc_fini(void);
+
+static inline int ll_rpc_recoverable_error(int rc)
+{
+ return (rc == -ENOTCONN || rc == -ENODEV);
+}
+
+static inline int tgt_mod_init(void)
+{
+ return 0;
+}
+
+static inline void tgt_mod_exit(void)
+{
+ return;
+}
+
+static inline void ptlrpc_reqset_put(struct ptlrpc_request_set *set)
+{
+ if (atomic_dec_and_test(&set->set_refcount))
+ OBD_FREE_PTR(set);
+}
+#endif /* PTLRPC_INTERNAL_H */
diff --git a/drivers/staging/lustre/lustre/ptlrpc/ptlrpc_module.c b/drivers/staging/lustre/lustre/ptlrpc/ptlrpc_module.c
new file mode 100644
index 000000000..5268887ca
--- /dev/null
+++ b/drivers/staging/lustre/lustre/ptlrpc/ptlrpc_module.c
@@ -0,0 +1,171 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2011, 2012, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ */
+
+#define DEBUG_SUBSYSTEM S_RPC
+
+
+#include "../include/obd_support.h"
+#include "../include/obd_class.h"
+#include "../include/lustre_net.h"
+#include "../include/lustre_req_layout.h"
+
+#include "ptlrpc_internal.h"
+
+extern spinlock_t ptlrpc_last_xid_lock;
+#if RS_DEBUG
+extern spinlock_t ptlrpc_rs_debug_lock;
+#endif
+extern struct mutex pinger_mutex;
+extern struct mutex ptlrpcd_mutex;
+
+__init int ptlrpc_init(void)
+{
+ int rc, cleanup_phase = 0;
+
+ lustre_assert_wire_constants();
+#if RS_DEBUG
+ spin_lock_init(&ptlrpc_rs_debug_lock);
+#endif
+ mutex_init(&ptlrpc_all_services_mutex);
+ mutex_init(&pinger_mutex);
+ mutex_init(&ptlrpcd_mutex);
+ ptlrpc_init_xid();
+
+ rc = req_layout_init();
+ if (rc)
+ return rc;
+
+ rc = ptlrpc_hr_init();
+ if (rc)
+ return rc;
+
+ cleanup_phase = 1;
+ rc = ptlrpc_request_cache_init();
+ if (rc)
+ goto cleanup;
+
+ cleanup_phase = 2;
+ rc = ptlrpc_init_portals();
+ if (rc)
+ goto cleanup;
+
+ cleanup_phase = 3;
+
+ rc = ptlrpc_connection_init();
+ if (rc)
+ goto cleanup;
+
+ cleanup_phase = 4;
+ ptlrpc_put_connection_superhack = ptlrpc_connection_put;
+
+ rc = ptlrpc_start_pinger();
+ if (rc)
+ goto cleanup;
+
+ cleanup_phase = 5;
+ rc = ldlm_init();
+ if (rc)
+ goto cleanup;
+
+ cleanup_phase = 6;
+ rc = sptlrpc_init();
+ if (rc)
+ goto cleanup;
+
+ cleanup_phase = 7;
+ rc = ptlrpc_nrs_init();
+ if (rc)
+ goto cleanup;
+
+ cleanup_phase = 8;
+ rc = tgt_mod_init();
+ if (rc)
+ goto cleanup;
+ return 0;
+
+cleanup:
+ switch (cleanup_phase) {
+ case 8:
+ ptlrpc_nrs_fini();
+ /* Fall through */
+ case 7:
+ sptlrpc_fini();
+ /* Fall through */
+ case 6:
+ ldlm_exit();
+ /* Fall through */
+ case 5:
+ ptlrpc_stop_pinger();
+ /* Fall through */
+ case 4:
+ ptlrpc_connection_fini();
+ /* Fall through */
+ case 3:
+ ptlrpc_exit_portals();
+ /* Fall through */
+ case 2:
+ ptlrpc_request_cache_fini();
+ /* Fall through */
+ case 1:
+ ptlrpc_hr_fini();
+ req_layout_fini();
+ /* Fall through */
+ default: ;
+ }
+
+ return rc;
+}
+
+static void __exit ptlrpc_exit(void)
+{
+ tgt_mod_exit();
+ ptlrpc_nrs_fini();
+ sptlrpc_fini();
+ ldlm_exit();
+ ptlrpc_stop_pinger();
+ ptlrpc_exit_portals();
+ ptlrpc_request_cache_fini();
+ ptlrpc_hr_fini();
+ ptlrpc_connection_fini();
+}
+
+MODULE_AUTHOR("Sun Microsystems, Inc. <http://www.lustre.org/>");
+MODULE_DESCRIPTION("Lustre Request Processor and Lock Management");
+MODULE_LICENSE("GPL");
+MODULE_VERSION("1.0.0");
+
+module_init(ptlrpc_init);
+module_exit(ptlrpc_exit);
diff --git a/drivers/staging/lustre/lustre/ptlrpc/ptlrpcd.c b/drivers/staging/lustre/lustre/ptlrpc/ptlrpcd.c
new file mode 100644
index 000000000..0c178ec0e
--- /dev/null
+++ b/drivers/staging/lustre/lustre/ptlrpc/ptlrpcd.c
@@ -0,0 +1,811 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2011, 2012, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ *
+ * lustre/ptlrpc/ptlrpcd.c
+ */
+
+/** \defgroup ptlrpcd PortalRPC daemon
+ *
+ * ptlrpcd is a special thread with its own set where other user might add
+ * requests when they don't want to wait for their completion.
+ * PtlRPCD will take care of sending such requests and then processing their
+ * replies and calling completion callbacks as necessary.
+ * The callbacks are called directly from ptlrpcd context.
+ * It is important to never significantly block (esp. on RPCs!) within such
+ * completion handler or a deadlock might occur where ptlrpcd enters some
+ * callback that attempts to send another RPC and wait for it to return,
+ * during which time ptlrpcd is completely blocked, so e.g. if import
+ * fails, recovery cannot progress because connection requests are also
+ * sent by ptlrpcd.
+ *
+ * @{
+ */
+
+#define DEBUG_SUBSYSTEM S_RPC
+
+#include "../../include/linux/libcfs/libcfs.h"
+
+#include "../include/lustre_net.h"
+#include "../include/lustre_lib.h"
+#include "../include/lustre_ha.h"
+#include "../include/obd_class.h" /* for obd_zombie */
+#include "../include/obd_support.h" /* for OBD_FAIL_CHECK */
+#include "../include/cl_object.h" /* cl_env_{get,put}() */
+#include "../include/lprocfs_status.h"
+
+#include "ptlrpc_internal.h"
+
+struct ptlrpcd {
+ int pd_size;
+ int pd_index;
+ int pd_nthreads;
+ struct ptlrpcd_ctl pd_thread_rcv;
+ struct ptlrpcd_ctl pd_threads[0];
+};
+
+static int max_ptlrpcds;
+module_param(max_ptlrpcds, int, 0644);
+MODULE_PARM_DESC(max_ptlrpcds, "Max ptlrpcd thread count to be started.");
+
+static int ptlrpcd_bind_policy = PDB_POLICY_PAIR;
+module_param(ptlrpcd_bind_policy, int, 0644);
+MODULE_PARM_DESC(ptlrpcd_bind_policy, "Ptlrpcd threads binding mode.");
+static struct ptlrpcd *ptlrpcds;
+
+struct mutex ptlrpcd_mutex;
+static int ptlrpcd_users;
+
+void ptlrpcd_wake(struct ptlrpc_request *req)
+{
+ struct ptlrpc_request_set *rq_set = req->rq_set;
+
+ LASSERT(rq_set != NULL);
+
+ wake_up(&rq_set->set_waitq);
+}
+EXPORT_SYMBOL(ptlrpcd_wake);
+
+static struct ptlrpcd_ctl *
+ptlrpcd_select_pc(struct ptlrpc_request *req, pdl_policy_t policy, int index)
+{
+ int idx = 0;
+
+ if (req != NULL && req->rq_send_state != LUSTRE_IMP_FULL)
+ return &ptlrpcds->pd_thread_rcv;
+
+ switch (policy) {
+ case PDL_POLICY_SAME:
+ idx = smp_processor_id() % ptlrpcds->pd_nthreads;
+ break;
+ case PDL_POLICY_LOCAL:
+ /* Before CPU partition patches available, process it the same
+ * as "PDL_POLICY_ROUND". */
+# ifdef CFS_CPU_MODE_NUMA
+# warning "fix this code to use new CPU partition APIs"
+# endif
+ /* Fall through to PDL_POLICY_ROUND until the CPU
+ * CPU partition patches are available. */
+ index = -1;
+ case PDL_POLICY_PREFERRED:
+ if (index >= 0 && index < num_online_cpus()) {
+ idx = index % ptlrpcds->pd_nthreads;
+ break;
+ }
+ /* Fall through to PDL_POLICY_ROUND for bad index. */
+ default:
+ /* Fall through to PDL_POLICY_ROUND for unknown policy. */
+ case PDL_POLICY_ROUND:
+ /* We do not care whether it is strict load balance. */
+ idx = ptlrpcds->pd_index + 1;
+ if (idx == smp_processor_id())
+ idx++;
+ idx %= ptlrpcds->pd_nthreads;
+ ptlrpcds->pd_index = idx;
+ break;
+ }
+
+ return &ptlrpcds->pd_threads[idx];
+}
+
+/**
+ * Move all request from an existing request set to the ptlrpcd queue.
+ * All requests from the set must be in phase RQ_PHASE_NEW.
+ */
+void ptlrpcd_add_rqset(struct ptlrpc_request_set *set)
+{
+ struct list_head *tmp, *pos;
+ struct ptlrpcd_ctl *pc;
+ struct ptlrpc_request_set *new;
+ int count, i;
+
+ pc = ptlrpcd_select_pc(NULL, PDL_POLICY_LOCAL, -1);
+ new = pc->pc_set;
+
+ list_for_each_safe(pos, tmp, &set->set_requests) {
+ struct ptlrpc_request *req =
+ list_entry(pos, struct ptlrpc_request,
+ rq_set_chain);
+
+ LASSERT(req->rq_phase == RQ_PHASE_NEW);
+ req->rq_set = new;
+ req->rq_queued_time = cfs_time_current();
+ }
+
+ spin_lock(&new->set_new_req_lock);
+ list_splice_init(&set->set_requests, &new->set_new_requests);
+ i = atomic_read(&set->set_remaining);
+ count = atomic_add_return(i, &new->set_new_count);
+ atomic_set(&set->set_remaining, 0);
+ spin_unlock(&new->set_new_req_lock);
+ if (count == i) {
+ wake_up(&new->set_waitq);
+
+ /* XXX: It maybe unnecessary to wakeup all the partners. But to
+ * guarantee the async RPC can be processed ASAP, we have
+ * no other better choice. It maybe fixed in future. */
+ for (i = 0; i < pc->pc_npartners; i++)
+ wake_up(&pc->pc_partners[i]->pc_set->set_waitq);
+ }
+}
+EXPORT_SYMBOL(ptlrpcd_add_rqset);
+
+/**
+ * Return transferred RPCs count.
+ */
+static int ptlrpcd_steal_rqset(struct ptlrpc_request_set *des,
+ struct ptlrpc_request_set *src)
+{
+ struct list_head *tmp, *pos;
+ struct ptlrpc_request *req;
+ int rc = 0;
+
+ spin_lock(&src->set_new_req_lock);
+ if (likely(!list_empty(&src->set_new_requests))) {
+ list_for_each_safe(pos, tmp, &src->set_new_requests) {
+ req = list_entry(pos, struct ptlrpc_request,
+ rq_set_chain);
+ req->rq_set = des;
+ }
+ list_splice_init(&src->set_new_requests,
+ &des->set_requests);
+ rc = atomic_read(&src->set_new_count);
+ atomic_add(rc, &des->set_remaining);
+ atomic_set(&src->set_new_count, 0);
+ }
+ spin_unlock(&src->set_new_req_lock);
+ return rc;
+}
+
+/**
+ * Requests that are added to the ptlrpcd queue are sent via
+ * ptlrpcd_check->ptlrpc_check_set().
+ */
+void ptlrpcd_add_req(struct ptlrpc_request *req, pdl_policy_t policy, int idx)
+{
+ struct ptlrpcd_ctl *pc;
+
+ if (req->rq_reqmsg)
+ lustre_msg_set_jobid(req->rq_reqmsg, NULL);
+
+ spin_lock(&req->rq_lock);
+ if (req->rq_invalid_rqset) {
+ struct l_wait_info lwi = LWI_TIMEOUT(cfs_time_seconds(5),
+ back_to_sleep, NULL);
+
+ req->rq_invalid_rqset = 0;
+ spin_unlock(&req->rq_lock);
+ l_wait_event(req->rq_set_waitq, (req->rq_set == NULL), &lwi);
+ } else if (req->rq_set) {
+ /* If we have a valid "rq_set", just reuse it to avoid double
+ * linked. */
+ LASSERT(req->rq_phase == RQ_PHASE_NEW);
+ LASSERT(req->rq_send_state == LUSTRE_IMP_REPLAY);
+
+ /* ptlrpc_check_set will decrease the count */
+ atomic_inc(&req->rq_set->set_remaining);
+ spin_unlock(&req->rq_lock);
+ wake_up(&req->rq_set->set_waitq);
+ return;
+ } else {
+ spin_unlock(&req->rq_lock);
+ }
+
+ pc = ptlrpcd_select_pc(req, policy, idx);
+
+ DEBUG_REQ(D_INFO, req, "add req [%p] to pc [%s:%d]",
+ req, pc->pc_name, pc->pc_index);
+
+ ptlrpc_set_add_new_req(pc, req);
+}
+EXPORT_SYMBOL(ptlrpcd_add_req);
+
+static inline void ptlrpc_reqset_get(struct ptlrpc_request_set *set)
+{
+ atomic_inc(&set->set_refcount);
+}
+
+/**
+ * Check if there is more work to do on ptlrpcd set.
+ * Returns 1 if yes.
+ */
+static int ptlrpcd_check(struct lu_env *env, struct ptlrpcd_ctl *pc)
+{
+ struct list_head *tmp, *pos;
+ struct ptlrpc_request *req;
+ struct ptlrpc_request_set *set = pc->pc_set;
+ int rc = 0;
+ int rc2;
+
+ if (atomic_read(&set->set_new_count)) {
+ spin_lock(&set->set_new_req_lock);
+ if (likely(!list_empty(&set->set_new_requests))) {
+ list_splice_init(&set->set_new_requests,
+ &set->set_requests);
+ atomic_add(atomic_read(&set->set_new_count),
+ &set->set_remaining);
+ atomic_set(&set->set_new_count, 0);
+ /*
+ * Need to calculate its timeout.
+ */
+ rc = 1;
+ }
+ spin_unlock(&set->set_new_req_lock);
+ }
+
+ /* We should call lu_env_refill() before handling new requests to make
+ * sure that env key the requests depending on really exists.
+ */
+ rc2 = lu_env_refill(env);
+ if (rc2 != 0) {
+ /*
+ * XXX This is very awkward situation, because
+ * execution can neither continue (request
+ * interpreters assume that env is set up), nor repeat
+ * the loop (as this potentially results in a tight
+ * loop of -ENOMEM's).
+ *
+ * Fortunately, refill only ever does something when
+ * new modules are loaded, i.e., early during boot up.
+ */
+ CERROR("Failure to refill session: %d\n", rc2);
+ return rc;
+ }
+
+ if (atomic_read(&set->set_remaining))
+ rc |= ptlrpc_check_set(env, set);
+
+ /* NB: ptlrpc_check_set has already moved completed request at the
+ * head of seq::set_requests */
+ list_for_each_safe(pos, tmp, &set->set_requests) {
+ req = list_entry(pos, struct ptlrpc_request, rq_set_chain);
+ if (req->rq_phase != RQ_PHASE_COMPLETE)
+ break;
+
+ list_del_init(&req->rq_set_chain);
+ req->rq_set = NULL;
+ ptlrpc_req_finished(req);
+ }
+
+ if (rc == 0) {
+ /*
+ * If new requests have been added, make sure to wake up.
+ */
+ rc = atomic_read(&set->set_new_count);
+
+ /* If we have nothing to do, check whether we can take some
+ * work from our partner threads. */
+ if (rc == 0 && pc->pc_npartners > 0) {
+ struct ptlrpcd_ctl *partner;
+ struct ptlrpc_request_set *ps;
+ int first = pc->pc_cursor;
+
+ do {
+ partner = pc->pc_partners[pc->pc_cursor++];
+ if (pc->pc_cursor >= pc->pc_npartners)
+ pc->pc_cursor = 0;
+ if (partner == NULL)
+ continue;
+
+ spin_lock(&partner->pc_lock);
+ ps = partner->pc_set;
+ if (ps == NULL) {
+ spin_unlock(&partner->pc_lock);
+ continue;
+ }
+
+ ptlrpc_reqset_get(ps);
+ spin_unlock(&partner->pc_lock);
+
+ if (atomic_read(&ps->set_new_count)) {
+ rc = ptlrpcd_steal_rqset(set, ps);
+ if (rc > 0)
+ CDEBUG(D_RPCTRACE, "transfer %d async RPCs [%d->%d]\n",
+ rc, partner->pc_index,
+ pc->pc_index);
+ }
+ ptlrpc_reqset_put(ps);
+ } while (rc == 0 && pc->pc_cursor != first);
+ }
+ }
+
+ return rc;
+}
+
+/**
+ * Main ptlrpcd thread.
+ * ptlrpc's code paths like to execute in process context, so we have this
+ * thread which spins on a set which contains the rpcs and sends them.
+ *
+ */
+static int ptlrpcd(void *arg)
+{
+ struct ptlrpcd_ctl *pc = arg;
+ struct ptlrpc_request_set *set = pc->pc_set;
+ struct lu_env env = { .le_ses = NULL };
+ int rc, exit = 0;
+
+ unshare_fs_struct();
+#if defined(CONFIG_SMP)
+ if (test_bit(LIOD_BIND, &pc->pc_flags)) {
+ int index = pc->pc_index;
+
+ if (index >= 0 && index < num_possible_cpus()) {
+ while (!cpu_online(index)) {
+ if (++index >= num_possible_cpus())
+ index = 0;
+ }
+ set_cpus_allowed_ptr(current,
+ cpumask_of_node(cpu_to_node(index)));
+ }
+ }
+#endif
+ /*
+ * XXX So far only "client" ptlrpcd uses an environment. In
+ * the future, ptlrpcd thread (or a thread-set) has to given
+ * an argument, describing its "scope".
+ */
+ rc = lu_context_init(&env.le_ctx,
+ LCT_CL_THREAD|LCT_REMEMBER|LCT_NOREF);
+ complete(&pc->pc_starting);
+
+ if (rc != 0)
+ return rc;
+
+ /*
+ * This mainloop strongly resembles ptlrpc_set_wait() except that our
+ * set never completes. ptlrpcd_check() calls ptlrpc_check_set() when
+ * there are requests in the set. New requests come in on the set's
+ * new_req_list and ptlrpcd_check() moves them into the set.
+ */
+ do {
+ struct l_wait_info lwi;
+ int timeout;
+
+ timeout = ptlrpc_set_next_timeout(set);
+ lwi = LWI_TIMEOUT(cfs_time_seconds(timeout ? timeout : 1),
+ ptlrpc_expired_set, set);
+
+ lu_context_enter(&env.le_ctx);
+ l_wait_event(set->set_waitq,
+ ptlrpcd_check(&env, pc), &lwi);
+ lu_context_exit(&env.le_ctx);
+
+ /*
+ * Abort inflight rpcs for forced stop case.
+ */
+ if (test_bit(LIOD_STOP, &pc->pc_flags)) {
+ if (test_bit(LIOD_FORCE, &pc->pc_flags))
+ ptlrpc_abort_set(set);
+ exit++;
+ }
+
+ /*
+ * Let's make one more loop to make sure that ptlrpcd_check()
+ * copied all raced new rpcs into the set so we can kill them.
+ */
+ } while (exit < 2);
+
+ /*
+ * Wait for inflight requests to drain.
+ */
+ if (!list_empty(&set->set_requests))
+ ptlrpc_set_wait(set);
+ lu_context_fini(&env.le_ctx);
+
+ complete(&pc->pc_finishing);
+
+ return 0;
+}
+
+/* XXX: We want multiple CPU cores to share the async RPC load. So we start many
+ * ptlrpcd threads. We also want to reduce the ptlrpcd overhead caused by
+ * data transfer cross-CPU cores. So we bind ptlrpcd thread to specified
+ * CPU core. But binding all ptlrpcd threads maybe cause response delay
+ * because of some CPU core(s) busy with other loads.
+ *
+ * For example: "ls -l", some async RPCs for statahead are assigned to
+ * ptlrpcd_0, and ptlrpcd_0 is bound to CPU_0, but CPU_0 may be quite busy
+ * with other non-ptlrpcd, like "ls -l" itself (we want to the "ls -l"
+ * thread, statahead thread, and ptlrpcd thread can run in parallel), under
+ * such case, the statahead async RPCs can not be processed in time, it is
+ * unexpected. If ptlrpcd_0 can be re-scheduled on other CPU core, it may
+ * be better. But it breaks former data transfer policy.
+ *
+ * So we shouldn't be blind for avoiding the data transfer. We make some
+ * compromise: divide the ptlrpcd threads pool into two parts. One part is
+ * for bound mode, each ptlrpcd thread in this part is bound to some CPU
+ * core. The other part is for free mode, all the ptlrpcd threads in the
+ * part can be scheduled on any CPU core. We specify some partnership
+ * between bound mode ptlrpcd thread(s) and free mode ptlrpcd thread(s),
+ * and the async RPC load within the partners are shared.
+ *
+ * It can partly avoid data transfer cross-CPU (if the bound mode ptlrpcd
+ * thread can be scheduled in time), and try to guarantee the async RPC
+ * processed ASAP (as long as the free mode ptlrpcd thread can be scheduled
+ * on any CPU core).
+ *
+ * As for how to specify the partnership between bound mode ptlrpcd
+ * thread(s) and free mode ptlrpcd thread(s), the simplest way is to use
+ * <free bound> pair. In future, we can specify some more complex
+ * partnership based on the patches for CPU partition. But before such
+ * patches are available, we prefer to use the simplest one.
+ */
+# ifdef CFS_CPU_MODE_NUMA
+# warning "fix ptlrpcd_bind() to use new CPU partition APIs"
+# endif
+static int ptlrpcd_bind(int index, int max)
+{
+ struct ptlrpcd_ctl *pc;
+ int rc = 0;
+#if defined(CONFIG_NUMA)
+ cpumask_t mask;
+#endif
+
+ LASSERT(index <= max - 1);
+ pc = &ptlrpcds->pd_threads[index];
+ switch (ptlrpcd_bind_policy) {
+ case PDB_POLICY_NONE:
+ pc->pc_npartners = -1;
+ break;
+ case PDB_POLICY_FULL:
+ pc->pc_npartners = 0;
+ set_bit(LIOD_BIND, &pc->pc_flags);
+ break;
+ case PDB_POLICY_PAIR:
+ LASSERT(max % 2 == 0);
+ pc->pc_npartners = 1;
+ break;
+ case PDB_POLICY_NEIGHBOR:
+#if defined(CONFIG_NUMA)
+ {
+ int i;
+ cpumask_copy(&mask, cpumask_of_node(cpu_to_node(index)));
+ for (i = max; i < num_online_cpus(); i++)
+ cpumask_clear_cpu(i, &mask);
+ pc->pc_npartners = cpumask_weight(&mask) - 1;
+ set_bit(LIOD_BIND, &pc->pc_flags);
+ }
+#else
+ LASSERT(max >= 3);
+ pc->pc_npartners = 2;
+#endif
+ break;
+ default:
+ CERROR("unknown ptlrpcd bind policy %d\n", ptlrpcd_bind_policy);
+ rc = -EINVAL;
+ }
+
+ if (rc == 0 && pc->pc_npartners > 0) {
+ OBD_ALLOC(pc->pc_partners,
+ sizeof(struct ptlrpcd_ctl *) * pc->pc_npartners);
+ if (pc->pc_partners == NULL) {
+ pc->pc_npartners = 0;
+ rc = -ENOMEM;
+ } else {
+ switch (ptlrpcd_bind_policy) {
+ case PDB_POLICY_PAIR:
+ if (index & 0x1) {
+ set_bit(LIOD_BIND, &pc->pc_flags);
+ pc->pc_partners[0] = &ptlrpcds->
+ pd_threads[index - 1];
+ ptlrpcds->pd_threads[index - 1].
+ pc_partners[0] = pc;
+ }
+ break;
+ case PDB_POLICY_NEIGHBOR:
+#if defined(CONFIG_NUMA)
+ {
+ struct ptlrpcd_ctl *ppc;
+ int i, pidx;
+ /* partners are cores in the same NUMA node.
+ * setup partnership only with ptlrpcd threads
+ * that are already initialized
+ */
+ for (pidx = 0, i = 0; i < index; i++) {
+ if (cpumask_test_cpu(i, &mask)) {
+ ppc = &ptlrpcds->pd_threads[i];
+ pc->pc_partners[pidx++] = ppc;
+ ppc->pc_partners[ppc->
+ pc_npartners++] = pc;
+ }
+ }
+ /* adjust number of partners to the number
+ * of partnership really setup */
+ pc->pc_npartners = pidx;
+ }
+#else
+ if (index & 0x1)
+ set_bit(LIOD_BIND, &pc->pc_flags);
+ if (index > 0) {
+ pc->pc_partners[0] = &ptlrpcds->
+ pd_threads[index - 1];
+ ptlrpcds->pd_threads[index - 1].
+ pc_partners[1] = pc;
+ if (index == max - 1) {
+ pc->pc_partners[1] =
+ &ptlrpcds->pd_threads[0];
+ ptlrpcds->pd_threads[0].
+ pc_partners[0] = pc;
+ }
+ }
+#endif
+ break;
+ }
+ }
+ }
+
+ return rc;
+}
+
+
+int ptlrpcd_start(int index, int max, const char *name, struct ptlrpcd_ctl *pc)
+{
+ int rc;
+
+ /*
+ * Do not allow start second thread for one pc.
+ */
+ if (test_and_set_bit(LIOD_START, &pc->pc_flags)) {
+ CWARN("Starting second thread (%s) for same pc %p\n",
+ name, pc);
+ return 0;
+ }
+
+ pc->pc_index = index;
+ init_completion(&pc->pc_starting);
+ init_completion(&pc->pc_finishing);
+ spin_lock_init(&pc->pc_lock);
+ strlcpy(pc->pc_name, name, sizeof(pc->pc_name));
+ pc->pc_set = ptlrpc_prep_set();
+ if (pc->pc_set == NULL) {
+ rc = -ENOMEM;
+ goto out;
+ }
+
+ /*
+ * So far only "client" ptlrpcd uses an environment. In the future,
+ * ptlrpcd thread (or a thread-set) has to be given an argument,
+ * describing its "scope".
+ */
+ rc = lu_context_init(&pc->pc_env.le_ctx, LCT_CL_THREAD|LCT_REMEMBER);
+ if (rc != 0)
+ goto out_set;
+
+ {
+ struct task_struct *task;
+ if (index >= 0) {
+ rc = ptlrpcd_bind(index, max);
+ if (rc < 0)
+ goto out_env;
+ }
+
+ task = kthread_run(ptlrpcd, pc, "%s", pc->pc_name);
+ if (IS_ERR(task)) {
+ rc = PTR_ERR(task);
+ goto out_env;
+ }
+
+ wait_for_completion(&pc->pc_starting);
+ }
+ return 0;
+
+out_env:
+ lu_context_fini(&pc->pc_env.le_ctx);
+
+out_set:
+ if (pc->pc_set != NULL) {
+ struct ptlrpc_request_set *set = pc->pc_set;
+
+ spin_lock(&pc->pc_lock);
+ pc->pc_set = NULL;
+ spin_unlock(&pc->pc_lock);
+ ptlrpc_set_destroy(set);
+ }
+ clear_bit(LIOD_BIND, &pc->pc_flags);
+
+out:
+ clear_bit(LIOD_START, &pc->pc_flags);
+ return rc;
+}
+
+void ptlrpcd_stop(struct ptlrpcd_ctl *pc, int force)
+{
+ if (!test_bit(LIOD_START, &pc->pc_flags)) {
+ CWARN("Thread for pc %p was not started\n", pc);
+ return;
+ }
+
+ set_bit(LIOD_STOP, &pc->pc_flags);
+ if (force)
+ set_bit(LIOD_FORCE, &pc->pc_flags);
+ wake_up(&pc->pc_set->set_waitq);
+}
+
+void ptlrpcd_free(struct ptlrpcd_ctl *pc)
+{
+ struct ptlrpc_request_set *set = pc->pc_set;
+
+ if (!test_bit(LIOD_START, &pc->pc_flags)) {
+ CWARN("Thread for pc %p was not started\n", pc);
+ goto out;
+ }
+
+ wait_for_completion(&pc->pc_finishing);
+ lu_context_fini(&pc->pc_env.le_ctx);
+
+ spin_lock(&pc->pc_lock);
+ pc->pc_set = NULL;
+ spin_unlock(&pc->pc_lock);
+ ptlrpc_set_destroy(set);
+
+ clear_bit(LIOD_START, &pc->pc_flags);
+ clear_bit(LIOD_STOP, &pc->pc_flags);
+ clear_bit(LIOD_FORCE, &pc->pc_flags);
+ clear_bit(LIOD_BIND, &pc->pc_flags);
+
+out:
+ if (pc->pc_npartners > 0) {
+ LASSERT(pc->pc_partners != NULL);
+
+ OBD_FREE(pc->pc_partners,
+ sizeof(struct ptlrpcd_ctl *) * pc->pc_npartners);
+ pc->pc_partners = NULL;
+ }
+ pc->pc_npartners = 0;
+}
+
+static void ptlrpcd_fini(void)
+{
+ int i;
+
+ if (ptlrpcds != NULL) {
+ for (i = 0; i < ptlrpcds->pd_nthreads; i++)
+ ptlrpcd_stop(&ptlrpcds->pd_threads[i], 0);
+ for (i = 0; i < ptlrpcds->pd_nthreads; i++)
+ ptlrpcd_free(&ptlrpcds->pd_threads[i]);
+ ptlrpcd_stop(&ptlrpcds->pd_thread_rcv, 0);
+ ptlrpcd_free(&ptlrpcds->pd_thread_rcv);
+ OBD_FREE(ptlrpcds, ptlrpcds->pd_size);
+ ptlrpcds = NULL;
+ }
+}
+
+static int ptlrpcd_init(void)
+{
+ int nthreads = num_online_cpus();
+ char name[16];
+ int size, i = -1, j, rc = 0;
+
+ if (max_ptlrpcds > 0 && max_ptlrpcds < nthreads)
+ nthreads = max_ptlrpcds;
+ if (nthreads < 2)
+ nthreads = 2;
+ if (nthreads < 3 && ptlrpcd_bind_policy == PDB_POLICY_NEIGHBOR)
+ ptlrpcd_bind_policy = PDB_POLICY_PAIR;
+ else if (nthreads % 2 != 0 && ptlrpcd_bind_policy == PDB_POLICY_PAIR)
+ nthreads &= ~1; /* make sure it is even */
+
+ size = offsetof(struct ptlrpcd, pd_threads[nthreads]);
+ OBD_ALLOC(ptlrpcds, size);
+ if (ptlrpcds == NULL) {
+ rc = -ENOMEM;
+ goto out;
+ }
+
+ snprintf(name, sizeof(name), "ptlrpcd_rcv");
+ set_bit(LIOD_RECOVERY, &ptlrpcds->pd_thread_rcv.pc_flags);
+ rc = ptlrpcd_start(-1, nthreads, name, &ptlrpcds->pd_thread_rcv);
+ if (rc < 0)
+ goto out;
+
+ /* XXX: We start nthreads ptlrpc daemons. Each of them can process any
+ * non-recovery async RPC to improve overall async RPC efficiency.
+ *
+ * But there are some issues with async I/O RPCs and async non-I/O
+ * RPCs processed in the same set under some cases. The ptlrpcd may
+ * be blocked by some async I/O RPC(s), then will cause other async
+ * non-I/O RPC(s) can not be processed in time.
+ *
+ * Maybe we should distinguish blocked async RPCs from non-blocked
+ * async RPCs, and process them in different ptlrpcd sets to avoid
+ * unnecessary dependency. But how to distribute async RPCs load
+ * among all the ptlrpc daemons becomes another trouble. */
+ for (i = 0; i < nthreads; i++) {
+ snprintf(name, sizeof(name), "ptlrpcd_%d", i);
+ rc = ptlrpcd_start(i, nthreads, name, &ptlrpcds->pd_threads[i]);
+ if (rc < 0)
+ goto out;
+ }
+
+ ptlrpcds->pd_size = size;
+ ptlrpcds->pd_index = 0;
+ ptlrpcds->pd_nthreads = nthreads;
+
+out:
+ if (rc != 0 && ptlrpcds != NULL) {
+ for (j = 0; j <= i; j++)
+ ptlrpcd_stop(&ptlrpcds->pd_threads[j], 0);
+ for (j = 0; j <= i; j++)
+ ptlrpcd_free(&ptlrpcds->pd_threads[j]);
+ ptlrpcd_stop(&ptlrpcds->pd_thread_rcv, 0);
+ ptlrpcd_free(&ptlrpcds->pd_thread_rcv);
+ OBD_FREE(ptlrpcds, size);
+ ptlrpcds = NULL;
+ }
+
+ return 0;
+}
+
+int ptlrpcd_addref(void)
+{
+ int rc = 0;
+
+ mutex_lock(&ptlrpcd_mutex);
+ if (++ptlrpcd_users == 1)
+ rc = ptlrpcd_init();
+ mutex_unlock(&ptlrpcd_mutex);
+ return rc;
+}
+EXPORT_SYMBOL(ptlrpcd_addref);
+
+void ptlrpcd_decref(void)
+{
+ mutex_lock(&ptlrpcd_mutex);
+ if (--ptlrpcd_users == 0)
+ ptlrpcd_fini();
+ mutex_unlock(&ptlrpcd_mutex);
+}
+EXPORT_SYMBOL(ptlrpcd_decref);
+/** @} ptlrpcd */
diff --git a/drivers/staging/lustre/lustre/ptlrpc/recover.c b/drivers/staging/lustre/lustre/ptlrpc/recover.c
new file mode 100644
index 000000000..7b1d72947
--- /dev/null
+++ b/drivers/staging/lustre/lustre/ptlrpc/recover.c
@@ -0,0 +1,379 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2002, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2011, 2012, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ *
+ * lustre/ptlrpc/recover.c
+ *
+ * Author: Mike Shaver <shaver@clusterfs.com>
+ */
+
+#define DEBUG_SUBSYSTEM S_RPC
+#include "../../include/linux/libcfs/libcfs.h"
+
+#include "../include/obd_support.h"
+#include "../include/lustre_ha.h"
+#include "../include/lustre_net.h"
+#include "../include/lustre_import.h"
+#include "../include/lustre_export.h"
+#include "../include/obd.h"
+#include "../include/obd_class.h"
+#include <linux/list.h>
+
+#include "ptlrpc_internal.h"
+
+/**
+ * Start recovery on disconnected import.
+ * This is done by just attempting a connect
+ */
+void ptlrpc_initiate_recovery(struct obd_import *imp)
+{
+ CDEBUG(D_HA, "%s: starting recovery\n", obd2cli_tgt(imp->imp_obd));
+ ptlrpc_connect_import(imp);
+}
+
+/**
+ * Identify what request from replay list needs to be replayed next
+ * (based on what we have already replayed) and send it to server.
+ */
+int ptlrpc_replay_next(struct obd_import *imp, int *inflight)
+{
+ int rc = 0;
+ struct list_head *tmp, *pos;
+ struct ptlrpc_request *req = NULL;
+ __u64 last_transno;
+
+ *inflight = 0;
+
+ /* It might have committed some after we last spoke, so make sure we
+ * get rid of them now.
+ */
+ spin_lock(&imp->imp_lock);
+ imp->imp_last_transno_checked = 0;
+ ptlrpc_free_committed(imp);
+ last_transno = imp->imp_last_replay_transno;
+ spin_unlock(&imp->imp_lock);
+
+ CDEBUG(D_HA, "import %p from %s committed %llu last %llu\n",
+ imp, obd2cli_tgt(imp->imp_obd),
+ imp->imp_peer_committed_transno, last_transno);
+
+ /* Do I need to hold a lock across this iteration? We shouldn't be
+ * racing with any additions to the list, because we're in recovery
+ * and are therefore not processing additional requests to add. Calls
+ * to ptlrpc_free_committed might commit requests, but nothing "newer"
+ * than the one we're replaying (it can't be committed until it's
+ * replayed, and we're doing that here). l_f_e_safe protects against
+ * problems with the current request being committed, in the unlikely
+ * event of that race. So, in conclusion, I think that it's safe to
+ * perform this list-walk without the imp_lock held.
+ *
+ * But, the {mdc,osc}_replay_open callbacks both iterate
+ * request lists, and have comments saying they assume the
+ * imp_lock is being held by ptlrpc_replay, but it's not. it's
+ * just a little race...
+ */
+
+ /* Replay all the committed open requests on committed_list first */
+ if (!list_empty(&imp->imp_committed_list)) {
+ tmp = imp->imp_committed_list.prev;
+ req = list_entry(tmp, struct ptlrpc_request,
+ rq_replay_list);
+
+ /* The last request on committed_list hasn't been replayed */
+ if (req->rq_transno > last_transno) {
+ /* Since the imp_committed_list is immutable before
+ * all of it's requests being replayed, it's safe to
+ * use a cursor to accelerate the search */
+ imp->imp_replay_cursor = imp->imp_replay_cursor->next;
+
+ while (imp->imp_replay_cursor !=
+ &imp->imp_committed_list) {
+ req = list_entry(imp->imp_replay_cursor,
+ struct ptlrpc_request,
+ rq_replay_list);
+ if (req->rq_transno > last_transno)
+ break;
+
+ req = NULL;
+ imp->imp_replay_cursor =
+ imp->imp_replay_cursor->next;
+ }
+ } else {
+ /* All requests on committed_list have been replayed */
+ imp->imp_replay_cursor = &imp->imp_committed_list;
+ req = NULL;
+ }
+ }
+
+ /* All the requests in committed list have been replayed, let's replay
+ * the imp_replay_list */
+ if (req == NULL) {
+ list_for_each_safe(tmp, pos, &imp->imp_replay_list) {
+ req = list_entry(tmp, struct ptlrpc_request,
+ rq_replay_list);
+
+ if (req->rq_transno > last_transno)
+ break;
+ req = NULL;
+ }
+ }
+
+ /* If need to resend the last sent transno (because a reconnect
+ * has occurred), then stop on the matching req and send it again.
+ * If, however, the last sent transno has been committed then we
+ * continue replay from the next request. */
+ if (req != NULL && imp->imp_resend_replay)
+ lustre_msg_add_flags(req->rq_reqmsg, MSG_RESENT);
+
+ spin_lock(&imp->imp_lock);
+ imp->imp_resend_replay = 0;
+ spin_unlock(&imp->imp_lock);
+
+ if (req != NULL) {
+ rc = ptlrpc_replay_req(req);
+ if (rc) {
+ CERROR("recovery replay error %d for req %llu\n",
+ rc, req->rq_xid);
+ return rc;
+ }
+ *inflight = 1;
+ }
+ return rc;
+}
+
+/**
+ * Schedule resending of request on sending_list. This is done after
+ * we completed replaying of requests and locks.
+ */
+int ptlrpc_resend(struct obd_import *imp)
+{
+ struct ptlrpc_request *req, *next;
+
+ /* As long as we're in recovery, nothing should be added to the sending
+ * list, so we don't need to hold the lock during this iteration and
+ * resend process.
+ */
+ /* Well... what if lctl recover is called twice at the same time?
+ */
+ spin_lock(&imp->imp_lock);
+ if (imp->imp_state != LUSTRE_IMP_RECOVER) {
+ spin_unlock(&imp->imp_lock);
+ return -1;
+ }
+
+ list_for_each_entry_safe(req, next, &imp->imp_sending_list,
+ rq_list) {
+ LASSERTF((long)req > PAGE_CACHE_SIZE && req != LP_POISON,
+ "req %p bad\n", req);
+ LASSERTF(req->rq_type != LI_POISON, "req %p freed\n", req);
+ if (!ptlrpc_no_resend(req))
+ ptlrpc_resend_req(req);
+ }
+ spin_unlock(&imp->imp_lock);
+
+ return 0;
+}
+EXPORT_SYMBOL(ptlrpc_resend);
+
+/**
+ * Go through all requests in delayed list and wake their threads
+ * for resending
+ */
+void ptlrpc_wake_delayed(struct obd_import *imp)
+{
+ struct list_head *tmp, *pos;
+ struct ptlrpc_request *req;
+
+ spin_lock(&imp->imp_lock);
+ list_for_each_safe(tmp, pos, &imp->imp_delayed_list) {
+ req = list_entry(tmp, struct ptlrpc_request, rq_list);
+
+ DEBUG_REQ(D_HA, req, "waking (set %p):", req->rq_set);
+ ptlrpc_client_wake_req(req);
+ }
+ spin_unlock(&imp->imp_lock);
+}
+EXPORT_SYMBOL(ptlrpc_wake_delayed);
+
+void ptlrpc_request_handle_notconn(struct ptlrpc_request *failed_req)
+{
+ struct obd_import *imp = failed_req->rq_import;
+
+ CDEBUG(D_HA, "import %s of %s@%s abruptly disconnected: reconnecting\n",
+ imp->imp_obd->obd_name, obd2cli_tgt(imp->imp_obd),
+ imp->imp_connection->c_remote_uuid.uuid);
+
+ if (ptlrpc_set_import_discon(imp,
+ lustre_msg_get_conn_cnt(failed_req->rq_reqmsg))) {
+ if (!imp->imp_replayable) {
+ CDEBUG(D_HA, "import %s@%s for %s not replayable, auto-deactivating\n",
+ obd2cli_tgt(imp->imp_obd),
+ imp->imp_connection->c_remote_uuid.uuid,
+ imp->imp_obd->obd_name);
+ ptlrpc_deactivate_import(imp);
+ }
+ /* to control recovery via lctl {disable|enable}_recovery */
+ if (imp->imp_deactive == 0)
+ ptlrpc_connect_import(imp);
+ }
+
+ /* Wait for recovery to complete and resend. If evicted, then
+ this request will be errored out later.*/
+ spin_lock(&failed_req->rq_lock);
+ if (!failed_req->rq_no_resend)
+ failed_req->rq_resend = 1;
+ spin_unlock(&failed_req->rq_lock);
+}
+
+/**
+ * Administratively active/deactive a client.
+ * This should only be called by the ioctl interface, currently
+ * - the lctl deactivate and activate commands
+ * - echo 0/1 >> /proc/osc/XXX/active
+ * - client umount -f (ll_umount_begin)
+ */
+int ptlrpc_set_import_active(struct obd_import *imp, int active)
+{
+ struct obd_device *obd = imp->imp_obd;
+ int rc = 0;
+
+ LASSERT(obd);
+
+ /* When deactivating, mark import invalid, and abort in-flight
+ * requests. */
+ if (!active) {
+ LCONSOLE_WARN("setting import %s INACTIVE by administrator request\n",
+ obd2cli_tgt(imp->imp_obd));
+
+ /* set before invalidate to avoid messages about imp_inval
+ * set without imp_deactive in ptlrpc_import_delay_req */
+ spin_lock(&imp->imp_lock);
+ imp->imp_deactive = 1;
+ spin_unlock(&imp->imp_lock);
+
+ obd_import_event(imp->imp_obd, imp, IMP_EVENT_DEACTIVATE);
+
+ ptlrpc_invalidate_import(imp);
+ }
+
+ /* When activating, mark import valid, and attempt recovery */
+ if (active) {
+ CDEBUG(D_HA, "setting import %s VALID\n",
+ obd2cli_tgt(imp->imp_obd));
+
+ spin_lock(&imp->imp_lock);
+ imp->imp_deactive = 0;
+ spin_unlock(&imp->imp_lock);
+ obd_import_event(imp->imp_obd, imp, IMP_EVENT_ACTIVATE);
+
+ rc = ptlrpc_recover_import(imp, NULL, 0);
+ }
+
+ return rc;
+}
+EXPORT_SYMBOL(ptlrpc_set_import_active);
+
+/* Attempt to reconnect an import */
+int ptlrpc_recover_import(struct obd_import *imp, char *new_uuid, int async)
+{
+ int rc = 0;
+
+ spin_lock(&imp->imp_lock);
+ if (imp->imp_state == LUSTRE_IMP_NEW || imp->imp_deactive ||
+ atomic_read(&imp->imp_inval_count))
+ rc = -EINVAL;
+ spin_unlock(&imp->imp_lock);
+ if (rc)
+ goto out;
+
+ /* force import to be disconnected. */
+ ptlrpc_set_import_discon(imp, 0);
+
+ if (new_uuid) {
+ struct obd_uuid uuid;
+
+ /* intruct import to use new uuid */
+ obd_str2uuid(&uuid, new_uuid);
+ rc = import_set_conn_priority(imp, &uuid);
+ if (rc)
+ goto out;
+ }
+
+ /* Check if reconnect is already in progress */
+ spin_lock(&imp->imp_lock);
+ if (imp->imp_state != LUSTRE_IMP_DISCON) {
+ imp->imp_force_verify = 1;
+ rc = -EALREADY;
+ }
+ spin_unlock(&imp->imp_lock);
+ if (rc)
+ goto out;
+
+ rc = ptlrpc_connect_import(imp);
+ if (rc)
+ goto out;
+
+ if (!async) {
+ struct l_wait_info lwi;
+ int secs = cfs_time_seconds(obd_timeout);
+
+ CDEBUG(D_HA, "%s: recovery started, waiting %u seconds\n",
+ obd2cli_tgt(imp->imp_obd), secs);
+
+ lwi = LWI_TIMEOUT(secs, NULL, NULL);
+ rc = l_wait_event(imp->imp_recovery_waitq,
+ !ptlrpc_import_in_recovery(imp), &lwi);
+ CDEBUG(D_HA, "%s: recovery finished\n",
+ obd2cli_tgt(imp->imp_obd));
+ }
+
+out:
+ return rc;
+}
+EXPORT_SYMBOL(ptlrpc_recover_import);
+
+int ptlrpc_import_in_recovery(struct obd_import *imp)
+{
+ int in_recovery = 1;
+
+ spin_lock(&imp->imp_lock);
+ if (imp->imp_state == LUSTRE_IMP_FULL ||
+ imp->imp_state == LUSTRE_IMP_CLOSED ||
+ imp->imp_state == LUSTRE_IMP_DISCON ||
+ imp->imp_obd->obd_no_recov)
+ in_recovery = 0;
+ spin_unlock(&imp->imp_lock);
+
+ return in_recovery;
+}
diff --git a/drivers/staging/lustre/lustre/ptlrpc/sec.c b/drivers/staging/lustre/lustre/ptlrpc/sec.c
new file mode 100644
index 000000000..21e9dc9d5
--- /dev/null
+++ b/drivers/staging/lustre/lustre/ptlrpc/sec.c
@@ -0,0 +1,2459 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2011, 2012, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ *
+ * lustre/ptlrpc/sec.c
+ *
+ * Author: Eric Mei <ericm@clusterfs.com>
+ */
+
+#define DEBUG_SUBSYSTEM S_SEC
+
+#include "../../include/linux/libcfs/libcfs.h"
+#include <linux/crypto.h>
+#include <linux/key.h>
+
+#include "../include/obd.h"
+#include "../include/obd_class.h"
+#include "../include/obd_support.h"
+#include "../include/lustre_net.h"
+#include "../include/lustre_import.h"
+#include "../include/lustre_dlm.h"
+#include "../include/lustre_sec.h"
+
+#include "ptlrpc_internal.h"
+
+/***********************************************
+ * policy registers *
+ ***********************************************/
+
+static rwlock_t policy_lock;
+static struct ptlrpc_sec_policy *policies[SPTLRPC_POLICY_MAX] = {
+ NULL,
+};
+
+int sptlrpc_register_policy(struct ptlrpc_sec_policy *policy)
+{
+ __u16 number = policy->sp_policy;
+
+ LASSERT(policy->sp_name);
+ LASSERT(policy->sp_cops);
+ LASSERT(policy->sp_sops);
+
+ if (number >= SPTLRPC_POLICY_MAX)
+ return -EINVAL;
+
+ write_lock(&policy_lock);
+ if (unlikely(policies[number])) {
+ write_unlock(&policy_lock);
+ return -EALREADY;
+ }
+ policies[number] = policy;
+ write_unlock(&policy_lock);
+
+ CDEBUG(D_SEC, "%s: registered\n", policy->sp_name);
+ return 0;
+}
+EXPORT_SYMBOL(sptlrpc_register_policy);
+
+int sptlrpc_unregister_policy(struct ptlrpc_sec_policy *policy)
+{
+ __u16 number = policy->sp_policy;
+
+ LASSERT(number < SPTLRPC_POLICY_MAX);
+
+ write_lock(&policy_lock);
+ if (unlikely(policies[number] == NULL)) {
+ write_unlock(&policy_lock);
+ CERROR("%s: already unregistered\n", policy->sp_name);
+ return -EINVAL;
+ }
+
+ LASSERT(policies[number] == policy);
+ policies[number] = NULL;
+ write_unlock(&policy_lock);
+
+ CDEBUG(D_SEC, "%s: unregistered\n", policy->sp_name);
+ return 0;
+}
+EXPORT_SYMBOL(sptlrpc_unregister_policy);
+
+static
+struct ptlrpc_sec_policy *sptlrpc_wireflavor2policy(__u32 flavor)
+{
+ static DEFINE_MUTEX(load_mutex);
+ static atomic_t loaded = ATOMIC_INIT(0);
+ struct ptlrpc_sec_policy *policy;
+ __u16 number = SPTLRPC_FLVR_POLICY(flavor);
+ __u16 flag = 0;
+
+ if (number >= SPTLRPC_POLICY_MAX)
+ return NULL;
+
+ while (1) {
+ read_lock(&policy_lock);
+ policy = policies[number];
+ if (policy && !try_module_get(policy->sp_owner))
+ policy = NULL;
+ if (policy == NULL)
+ flag = atomic_read(&loaded);
+ read_unlock(&policy_lock);
+
+ if (policy != NULL || flag != 0 ||
+ number != SPTLRPC_POLICY_GSS)
+ break;
+
+ /* try to load gss module, once */
+ mutex_lock(&load_mutex);
+ if (atomic_read(&loaded) == 0) {
+ if (request_module("ptlrpc_gss") == 0)
+ CDEBUG(D_SEC,
+ "module ptlrpc_gss loaded on demand\n");
+ else
+ CERROR("Unable to load module ptlrpc_gss\n");
+
+ atomic_set(&loaded, 1);
+ }
+ mutex_unlock(&load_mutex);
+ }
+
+ return policy;
+}
+
+__u32 sptlrpc_name2flavor_base(const char *name)
+{
+ if (!strcmp(name, "null"))
+ return SPTLRPC_FLVR_NULL;
+ if (!strcmp(name, "plain"))
+ return SPTLRPC_FLVR_PLAIN;
+ if (!strcmp(name, "krb5n"))
+ return SPTLRPC_FLVR_KRB5N;
+ if (!strcmp(name, "krb5a"))
+ return SPTLRPC_FLVR_KRB5A;
+ if (!strcmp(name, "krb5i"))
+ return SPTLRPC_FLVR_KRB5I;
+ if (!strcmp(name, "krb5p"))
+ return SPTLRPC_FLVR_KRB5P;
+
+ return SPTLRPC_FLVR_INVALID;
+}
+EXPORT_SYMBOL(sptlrpc_name2flavor_base);
+
+const char *sptlrpc_flavor2name_base(__u32 flvr)
+{
+ __u32 base = SPTLRPC_FLVR_BASE(flvr);
+
+ if (base == SPTLRPC_FLVR_BASE(SPTLRPC_FLVR_NULL))
+ return "null";
+ else if (base == SPTLRPC_FLVR_BASE(SPTLRPC_FLVR_PLAIN))
+ return "plain";
+ else if (base == SPTLRPC_FLVR_BASE(SPTLRPC_FLVR_KRB5N))
+ return "krb5n";
+ else if (base == SPTLRPC_FLVR_BASE(SPTLRPC_FLVR_KRB5A))
+ return "krb5a";
+ else if (base == SPTLRPC_FLVR_BASE(SPTLRPC_FLVR_KRB5I))
+ return "krb5i";
+ else if (base == SPTLRPC_FLVR_BASE(SPTLRPC_FLVR_KRB5P))
+ return "krb5p";
+
+ CERROR("invalid wire flavor 0x%x\n", flvr);
+ return "invalid";
+}
+EXPORT_SYMBOL(sptlrpc_flavor2name_base);
+
+char *sptlrpc_flavor2name_bulk(struct sptlrpc_flavor *sf,
+ char *buf, int bufsize)
+{
+ if (SPTLRPC_FLVR_POLICY(sf->sf_rpc) == SPTLRPC_POLICY_PLAIN)
+ snprintf(buf, bufsize, "hash:%s",
+ sptlrpc_get_hash_name(sf->u_bulk.hash.hash_alg));
+ else
+ snprintf(buf, bufsize, "%s",
+ sptlrpc_flavor2name_base(sf->sf_rpc));
+
+ buf[bufsize - 1] = '\0';
+ return buf;
+}
+EXPORT_SYMBOL(sptlrpc_flavor2name_bulk);
+
+char *sptlrpc_flavor2name(struct sptlrpc_flavor *sf, char *buf, int bufsize)
+{
+ strlcpy(buf, sptlrpc_flavor2name_base(sf->sf_rpc), bufsize);
+
+ /*
+ * currently we don't support customized bulk specification for
+ * flavors other than plain
+ */
+ if (SPTLRPC_FLVR_POLICY(sf->sf_rpc) == SPTLRPC_POLICY_PLAIN) {
+ char bspec[16];
+
+ bspec[0] = '-';
+ sptlrpc_flavor2name_bulk(sf, &bspec[1], sizeof(bspec) - 1);
+ strlcat(buf, bspec, bufsize);
+ }
+
+ return buf;
+}
+EXPORT_SYMBOL(sptlrpc_flavor2name);
+
+char *sptlrpc_secflags2str(__u32 flags, char *buf, int bufsize)
+{
+ buf[0] = '\0';
+
+ if (flags & PTLRPC_SEC_FL_REVERSE)
+ strlcat(buf, "reverse,", bufsize);
+ if (flags & PTLRPC_SEC_FL_ROOTONLY)
+ strlcat(buf, "rootonly,", bufsize);
+ if (flags & PTLRPC_SEC_FL_UDESC)
+ strlcat(buf, "udesc,", bufsize);
+ if (flags & PTLRPC_SEC_FL_BULK)
+ strlcat(buf, "bulk,", bufsize);
+ if (buf[0] == '\0')
+ strlcat(buf, "-,", bufsize);
+
+ return buf;
+}
+EXPORT_SYMBOL(sptlrpc_secflags2str);
+
+/**************************************************
+ * client context APIs *
+ **************************************************/
+
+static
+struct ptlrpc_cli_ctx *get_my_ctx(struct ptlrpc_sec *sec)
+{
+ struct vfs_cred vcred;
+ int create = 1, remove_dead = 1;
+
+ LASSERT(sec);
+ LASSERT(sec->ps_policy->sp_cops->lookup_ctx);
+
+ if (sec->ps_flvr.sf_flags & (PTLRPC_SEC_FL_REVERSE |
+ PTLRPC_SEC_FL_ROOTONLY)) {
+ vcred.vc_uid = 0;
+ vcred.vc_gid = 0;
+ if (sec->ps_flvr.sf_flags & PTLRPC_SEC_FL_REVERSE) {
+ create = 0;
+ remove_dead = 0;
+ }
+ } else {
+ vcred.vc_uid = from_kuid(&init_user_ns, current_uid());
+ vcred.vc_gid = from_kgid(&init_user_ns, current_gid());
+ }
+
+ return sec->ps_policy->sp_cops->lookup_ctx(sec, &vcred,
+ create, remove_dead);
+}
+
+struct ptlrpc_cli_ctx *sptlrpc_cli_ctx_get(struct ptlrpc_cli_ctx *ctx)
+{
+ atomic_inc(&ctx->cc_refcount);
+ return ctx;
+}
+EXPORT_SYMBOL(sptlrpc_cli_ctx_get);
+
+void sptlrpc_cli_ctx_put(struct ptlrpc_cli_ctx *ctx, int sync)
+{
+ struct ptlrpc_sec *sec = ctx->cc_sec;
+
+ LASSERT(sec);
+ LASSERT_ATOMIC_POS(&ctx->cc_refcount);
+
+ if (!atomic_dec_and_test(&ctx->cc_refcount))
+ return;
+
+ sec->ps_policy->sp_cops->release_ctx(sec, ctx, sync);
+}
+EXPORT_SYMBOL(sptlrpc_cli_ctx_put);
+
+/**
+ * Expire the client context immediately.
+ *
+ * \pre Caller must hold at least 1 reference on the \a ctx.
+ */
+void sptlrpc_cli_ctx_expire(struct ptlrpc_cli_ctx *ctx)
+{
+ LASSERT(ctx->cc_ops->force_die);
+ ctx->cc_ops->force_die(ctx, 0);
+}
+EXPORT_SYMBOL(sptlrpc_cli_ctx_expire);
+
+/**
+ * To wake up the threads who are waiting for this client context. Called
+ * after some status change happened on \a ctx.
+ */
+void sptlrpc_cli_ctx_wakeup(struct ptlrpc_cli_ctx *ctx)
+{
+ struct ptlrpc_request *req, *next;
+
+ spin_lock(&ctx->cc_lock);
+ list_for_each_entry_safe(req, next, &ctx->cc_req_list,
+ rq_ctx_chain) {
+ list_del_init(&req->rq_ctx_chain);
+ ptlrpc_client_wake_req(req);
+ }
+ spin_unlock(&ctx->cc_lock);
+}
+EXPORT_SYMBOL(sptlrpc_cli_ctx_wakeup);
+
+int sptlrpc_cli_ctx_display(struct ptlrpc_cli_ctx *ctx, char *buf, int bufsize)
+{
+ LASSERT(ctx->cc_ops);
+
+ if (ctx->cc_ops->display == NULL)
+ return 0;
+
+ return ctx->cc_ops->display(ctx, buf, bufsize);
+}
+
+static int import_sec_check_expire(struct obd_import *imp)
+{
+ int adapt = 0;
+
+ spin_lock(&imp->imp_lock);
+ if (imp->imp_sec_expire &&
+ imp->imp_sec_expire < get_seconds()) {
+ adapt = 1;
+ imp->imp_sec_expire = 0;
+ }
+ spin_unlock(&imp->imp_lock);
+
+ if (!adapt)
+ return 0;
+
+ CDEBUG(D_SEC, "found delayed sec adapt expired, do it now\n");
+ return sptlrpc_import_sec_adapt(imp, NULL, NULL);
+}
+
+static int import_sec_validate_get(struct obd_import *imp,
+ struct ptlrpc_sec **sec)
+{
+ int rc;
+
+ if (unlikely(imp->imp_sec_expire)) {
+ rc = import_sec_check_expire(imp);
+ if (rc)
+ return rc;
+ }
+
+ *sec = sptlrpc_import_sec_ref(imp);
+ if (*sec == NULL) {
+ CERROR("import %p (%s) with no sec\n",
+ imp, ptlrpc_import_state_name(imp->imp_state));
+ return -EACCES;
+ }
+
+ if (unlikely((*sec)->ps_dying)) {
+ CERROR("attempt to use dying sec %p\n", sec);
+ sptlrpc_sec_put(*sec);
+ return -EACCES;
+ }
+
+ return 0;
+}
+
+/**
+ * Given a \a req, find or allocate a appropriate context for it.
+ * \pre req->rq_cli_ctx == NULL.
+ *
+ * \retval 0 succeed, and req->rq_cli_ctx is set.
+ * \retval -ev error number, and req->rq_cli_ctx == NULL.
+ */
+int sptlrpc_req_get_ctx(struct ptlrpc_request *req)
+{
+ struct obd_import *imp = req->rq_import;
+ struct ptlrpc_sec *sec;
+ int rc;
+
+ LASSERT(!req->rq_cli_ctx);
+ LASSERT(imp);
+
+ rc = import_sec_validate_get(imp, &sec);
+ if (rc)
+ return rc;
+
+ req->rq_cli_ctx = get_my_ctx(sec);
+
+ sptlrpc_sec_put(sec);
+
+ if (!req->rq_cli_ctx) {
+ CERROR("req %p: fail to get context\n", req);
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+/**
+ * Drop the context for \a req.
+ * \pre req->rq_cli_ctx != NULL.
+ * \post req->rq_cli_ctx == NULL.
+ *
+ * If \a sync == 0, this function should return quickly without sleep;
+ * otherwise it might trigger and wait for the whole process of sending
+ * an context-destroying rpc to server.
+ */
+void sptlrpc_req_put_ctx(struct ptlrpc_request *req, int sync)
+{
+ LASSERT(req);
+ LASSERT(req->rq_cli_ctx);
+
+ /* request might be asked to release earlier while still
+ * in the context waiting list.
+ */
+ if (!list_empty(&req->rq_ctx_chain)) {
+ spin_lock(&req->rq_cli_ctx->cc_lock);
+ list_del_init(&req->rq_ctx_chain);
+ spin_unlock(&req->rq_cli_ctx->cc_lock);
+ }
+
+ sptlrpc_cli_ctx_put(req->rq_cli_ctx, sync);
+ req->rq_cli_ctx = NULL;
+}
+
+static
+int sptlrpc_req_ctx_switch(struct ptlrpc_request *req,
+ struct ptlrpc_cli_ctx *oldctx,
+ struct ptlrpc_cli_ctx *newctx)
+{
+ struct sptlrpc_flavor old_flvr;
+ char *reqmsg = NULL; /* to workaround old gcc */
+ int reqmsg_size;
+ int rc = 0;
+
+ LASSERT(req->rq_reqmsg);
+ LASSERT(req->rq_reqlen);
+ LASSERT(req->rq_replen);
+
+ CDEBUG(D_SEC, "req %p: switch ctx %p(%u->%s) -> %p(%u->%s), switch sec %p(%s) -> %p(%s)\n",
+ req,
+ oldctx, oldctx->cc_vcred.vc_uid, sec2target_str(oldctx->cc_sec),
+ newctx, newctx->cc_vcred.vc_uid, sec2target_str(newctx->cc_sec),
+ oldctx->cc_sec, oldctx->cc_sec->ps_policy->sp_name,
+ newctx->cc_sec, newctx->cc_sec->ps_policy->sp_name);
+
+ /* save flavor */
+ old_flvr = req->rq_flvr;
+
+ /* save request message */
+ reqmsg_size = req->rq_reqlen;
+ if (reqmsg_size != 0) {
+ OBD_ALLOC_LARGE(reqmsg, reqmsg_size);
+ if (reqmsg == NULL)
+ return -ENOMEM;
+ memcpy(reqmsg, req->rq_reqmsg, reqmsg_size);
+ }
+
+ /* release old req/rep buf */
+ req->rq_cli_ctx = oldctx;
+ sptlrpc_cli_free_reqbuf(req);
+ sptlrpc_cli_free_repbuf(req);
+ req->rq_cli_ctx = newctx;
+
+ /* recalculate the flavor */
+ sptlrpc_req_set_flavor(req, 0);
+
+ /* alloc new request buffer
+ * we don't need to alloc reply buffer here, leave it to the
+ * rest procedure of ptlrpc */
+ if (reqmsg_size != 0) {
+ rc = sptlrpc_cli_alloc_reqbuf(req, reqmsg_size);
+ if (!rc) {
+ LASSERT(req->rq_reqmsg);
+ memcpy(req->rq_reqmsg, reqmsg, reqmsg_size);
+ } else {
+ CWARN("failed to alloc reqbuf: %d\n", rc);
+ req->rq_flvr = old_flvr;
+ }
+
+ OBD_FREE_LARGE(reqmsg, reqmsg_size);
+ }
+ return rc;
+}
+
+/**
+ * If current context of \a req is dead somehow, e.g. we just switched flavor
+ * thus marked original contexts dead, we'll find a new context for it. if
+ * no switch is needed, \a req will end up with the same context.
+ *
+ * \note a request must have a context, to keep other parts of code happy.
+ * In any case of failure during the switching, we must restore the old one.
+ */
+int sptlrpc_req_replace_dead_ctx(struct ptlrpc_request *req)
+{
+ struct ptlrpc_cli_ctx *oldctx = req->rq_cli_ctx;
+ struct ptlrpc_cli_ctx *newctx;
+ int rc;
+
+ LASSERT(oldctx);
+
+ sptlrpc_cli_ctx_get(oldctx);
+ sptlrpc_req_put_ctx(req, 0);
+
+ rc = sptlrpc_req_get_ctx(req);
+ if (unlikely(rc)) {
+ LASSERT(!req->rq_cli_ctx);
+
+ /* restore old ctx */
+ req->rq_cli_ctx = oldctx;
+ return rc;
+ }
+
+ newctx = req->rq_cli_ctx;
+ LASSERT(newctx);
+
+ if (unlikely(newctx == oldctx &&
+ test_bit(PTLRPC_CTX_DEAD_BIT, &oldctx->cc_flags))) {
+ /*
+ * still get the old dead ctx, usually means system too busy
+ */
+ CDEBUG(D_SEC,
+ "ctx (%p, fl %lx) doesn't switch, relax a little bit\n",
+ newctx, newctx->cc_flags);
+
+ set_current_state(TASK_INTERRUPTIBLE);
+ schedule_timeout(HZ);
+ } else {
+ /*
+ * it's possible newctx == oldctx if we're switching
+ * subflavor with the same sec.
+ */
+ rc = sptlrpc_req_ctx_switch(req, oldctx, newctx);
+ if (rc) {
+ /* restore old ctx */
+ sptlrpc_req_put_ctx(req, 0);
+ req->rq_cli_ctx = oldctx;
+ return rc;
+ }
+
+ LASSERT(req->rq_cli_ctx == newctx);
+ }
+
+ sptlrpc_cli_ctx_put(oldctx, 1);
+ return 0;
+}
+EXPORT_SYMBOL(sptlrpc_req_replace_dead_ctx);
+
+static
+int ctx_check_refresh(struct ptlrpc_cli_ctx *ctx)
+{
+ if (cli_ctx_is_refreshed(ctx))
+ return 1;
+ return 0;
+}
+
+static
+int ctx_refresh_timeout(void *data)
+{
+ struct ptlrpc_request *req = data;
+ int rc;
+
+ /* conn_cnt is needed in expire_one_request */
+ lustre_msg_set_conn_cnt(req->rq_reqmsg, req->rq_import->imp_conn_cnt);
+
+ rc = ptlrpc_expire_one_request(req, 1);
+ /* if we started recovery, we should mark this ctx dead; otherwise
+ * in case of lgssd died nobody would retire this ctx, following
+ * connecting will still find the same ctx thus cause deadlock.
+ * there's an assumption that expire time of the request should be
+ * later than the context refresh expire time.
+ */
+ if (rc == 0)
+ req->rq_cli_ctx->cc_ops->force_die(req->rq_cli_ctx, 0);
+ return rc;
+}
+
+static
+void ctx_refresh_interrupt(void *data)
+{
+ struct ptlrpc_request *req = data;
+
+ spin_lock(&req->rq_lock);
+ req->rq_intr = 1;
+ spin_unlock(&req->rq_lock);
+}
+
+static
+void req_off_ctx_list(struct ptlrpc_request *req, struct ptlrpc_cli_ctx *ctx)
+{
+ spin_lock(&ctx->cc_lock);
+ if (!list_empty(&req->rq_ctx_chain))
+ list_del_init(&req->rq_ctx_chain);
+ spin_unlock(&ctx->cc_lock);
+}
+
+/**
+ * To refresh the context of \req, if it's not up-to-date.
+ * \param timeout
+ * - < 0: don't wait
+ * - = 0: wait until success or fatal error occur
+ * - > 0: timeout value (in seconds)
+ *
+ * The status of the context could be subject to be changed by other threads
+ * at any time. We allow this race, but once we return with 0, the caller will
+ * suppose it's uptodated and keep using it until the owning rpc is done.
+ *
+ * \retval 0 only if the context is uptodated.
+ * \retval -ev error number.
+ */
+int sptlrpc_req_refresh_ctx(struct ptlrpc_request *req, long timeout)
+{
+ struct ptlrpc_cli_ctx *ctx = req->rq_cli_ctx;
+ struct ptlrpc_sec *sec;
+ struct l_wait_info lwi;
+ int rc;
+
+ LASSERT(ctx);
+
+ if (req->rq_ctx_init || req->rq_ctx_fini)
+ return 0;
+
+ /*
+ * during the process a request's context might change type even
+ * (e.g. from gss ctx to null ctx), so each loop we need to re-check
+ * everything
+ */
+again:
+ rc = import_sec_validate_get(req->rq_import, &sec);
+ if (rc)
+ return rc;
+
+ if (sec->ps_flvr.sf_rpc != req->rq_flvr.sf_rpc) {
+ CDEBUG(D_SEC, "req %p: flavor has changed %x -> %x\n",
+ req, req->rq_flvr.sf_rpc, sec->ps_flvr.sf_rpc);
+ req_off_ctx_list(req, ctx);
+ sptlrpc_req_replace_dead_ctx(req);
+ ctx = req->rq_cli_ctx;
+ }
+ sptlrpc_sec_put(sec);
+
+ if (cli_ctx_is_eternal(ctx))
+ return 0;
+
+ if (unlikely(test_bit(PTLRPC_CTX_NEW_BIT, &ctx->cc_flags))) {
+ LASSERT(ctx->cc_ops->refresh);
+ ctx->cc_ops->refresh(ctx);
+ }
+ LASSERT(test_bit(PTLRPC_CTX_NEW_BIT, &ctx->cc_flags) == 0);
+
+ LASSERT(ctx->cc_ops->validate);
+ if (ctx->cc_ops->validate(ctx) == 0) {
+ req_off_ctx_list(req, ctx);
+ return 0;
+ }
+
+ if (unlikely(test_bit(PTLRPC_CTX_ERROR_BIT, &ctx->cc_flags))) {
+ spin_lock(&req->rq_lock);
+ req->rq_err = 1;
+ spin_unlock(&req->rq_lock);
+ req_off_ctx_list(req, ctx);
+ return -EPERM;
+ }
+
+ /*
+ * There's a subtle issue for resending RPCs, suppose following
+ * situation:
+ * 1. the request was sent to server.
+ * 2. recovery was kicked start, after finished the request was
+ * marked as resent.
+ * 3. resend the request.
+ * 4. old reply from server received, we accept and verify the reply.
+ * this has to be success, otherwise the error will be aware
+ * by application.
+ * 5. new reply from server received, dropped by LNet.
+ *
+ * Note the xid of old & new request is the same. We can't simply
+ * change xid for the resent request because the server replies on
+ * it for reply reconstruction.
+ *
+ * Commonly the original context should be uptodate because we
+ * have a expiry nice time; server will keep its context because
+ * we at least hold a ref of old context which prevent context
+ * destroying RPC being sent. So server still can accept the request
+ * and finish the RPC. But if that's not the case:
+ * 1. If server side context has been trimmed, a NO_CONTEXT will
+ * be returned, gss_cli_ctx_verify/unseal will switch to new
+ * context by force.
+ * 2. Current context never be refreshed, then we are fine: we
+ * never really send request with old context before.
+ */
+ if (test_bit(PTLRPC_CTX_UPTODATE_BIT, &ctx->cc_flags) &&
+ unlikely(req->rq_reqmsg) &&
+ lustre_msg_get_flags(req->rq_reqmsg) & MSG_RESENT) {
+ req_off_ctx_list(req, ctx);
+ return 0;
+ }
+
+ if (unlikely(test_bit(PTLRPC_CTX_DEAD_BIT, &ctx->cc_flags))) {
+ req_off_ctx_list(req, ctx);
+ /*
+ * don't switch ctx if import was deactivated
+ */
+ if (req->rq_import->imp_deactive) {
+ spin_lock(&req->rq_lock);
+ req->rq_err = 1;
+ spin_unlock(&req->rq_lock);
+ return -EINTR;
+ }
+
+ rc = sptlrpc_req_replace_dead_ctx(req);
+ if (rc) {
+ LASSERT(ctx == req->rq_cli_ctx);
+ CERROR("req %p: failed to replace dead ctx %p: %d\n",
+ req, ctx, rc);
+ spin_lock(&req->rq_lock);
+ req->rq_err = 1;
+ spin_unlock(&req->rq_lock);
+ return rc;
+ }
+
+ ctx = req->rq_cli_ctx;
+ goto again;
+ }
+
+ /*
+ * Now we're sure this context is during upcall, add myself into
+ * waiting list
+ */
+ spin_lock(&ctx->cc_lock);
+ if (list_empty(&req->rq_ctx_chain))
+ list_add(&req->rq_ctx_chain, &ctx->cc_req_list);
+ spin_unlock(&ctx->cc_lock);
+
+ if (timeout < 0)
+ return -EWOULDBLOCK;
+
+ /* Clear any flags that may be present from previous sends */
+ LASSERT(req->rq_receiving_reply == 0);
+ spin_lock(&req->rq_lock);
+ req->rq_err = 0;
+ req->rq_timedout = 0;
+ req->rq_resend = 0;
+ req->rq_restart = 0;
+ spin_unlock(&req->rq_lock);
+
+ lwi = LWI_TIMEOUT_INTR(timeout * HZ, ctx_refresh_timeout,
+ ctx_refresh_interrupt, req);
+ rc = l_wait_event(req->rq_reply_waitq, ctx_check_refresh(ctx), &lwi);
+
+ /*
+ * following cases could lead us here:
+ * - successfully refreshed;
+ * - interrupted;
+ * - timedout, and we don't want recover from the failure;
+ * - timedout, and waked up upon recovery finished;
+ * - someone else mark this ctx dead by force;
+ * - someone invalidate the req and call ptlrpc_client_wake_req(),
+ * e.g. ptlrpc_abort_inflight();
+ */
+ if (!cli_ctx_is_refreshed(ctx)) {
+ /* timed out or interrupted */
+ req_off_ctx_list(req, ctx);
+
+ LASSERT(rc != 0);
+ return rc;
+ }
+
+ goto again;
+}
+
+/**
+ * Initialize flavor settings for \a req, according to \a opcode.
+ *
+ * \note this could be called in two situations:
+ * - new request from ptlrpc_pre_req(), with proper @opcode
+ * - old request which changed ctx in the middle, with @opcode == 0
+ */
+void sptlrpc_req_set_flavor(struct ptlrpc_request *req, int opcode)
+{
+ struct ptlrpc_sec *sec;
+
+ LASSERT(req->rq_import);
+ LASSERT(req->rq_cli_ctx);
+ LASSERT(req->rq_cli_ctx->cc_sec);
+ LASSERT(req->rq_bulk_read == 0 || req->rq_bulk_write == 0);
+
+ /* special security flags according to opcode */
+ switch (opcode) {
+ case OST_READ:
+ case MDS_READPAGE:
+ case MGS_CONFIG_READ:
+ case OBD_IDX_READ:
+ req->rq_bulk_read = 1;
+ break;
+ case OST_WRITE:
+ case MDS_WRITEPAGE:
+ req->rq_bulk_write = 1;
+ break;
+ case SEC_CTX_INIT:
+ req->rq_ctx_init = 1;
+ break;
+ case SEC_CTX_FINI:
+ req->rq_ctx_fini = 1;
+ break;
+ case 0:
+ /* init/fini rpc won't be resend, so can't be here */
+ LASSERT(req->rq_ctx_init == 0);
+ LASSERT(req->rq_ctx_fini == 0);
+
+ /* cleanup flags, which should be recalculated */
+ req->rq_pack_udesc = 0;
+ req->rq_pack_bulk = 0;
+ break;
+ }
+
+ sec = req->rq_cli_ctx->cc_sec;
+
+ spin_lock(&sec->ps_lock);
+ req->rq_flvr = sec->ps_flvr;
+ spin_unlock(&sec->ps_lock);
+
+ /* force SVC_NULL for context initiation rpc, SVC_INTG for context
+ * destruction rpc */
+ if (unlikely(req->rq_ctx_init))
+ flvr_set_svc(&req->rq_flvr.sf_rpc, SPTLRPC_SVC_NULL);
+ else if (unlikely(req->rq_ctx_fini))
+ flvr_set_svc(&req->rq_flvr.sf_rpc, SPTLRPC_SVC_INTG);
+
+ /* user descriptor flag, null security can't do it anyway */
+ if ((sec->ps_flvr.sf_flags & PTLRPC_SEC_FL_UDESC) &&
+ (req->rq_flvr.sf_rpc != SPTLRPC_FLVR_NULL))
+ req->rq_pack_udesc = 1;
+
+ /* bulk security flag */
+ if ((req->rq_bulk_read || req->rq_bulk_write) &&
+ sptlrpc_flavor_has_bulk(&req->rq_flvr))
+ req->rq_pack_bulk = 1;
+}
+
+void sptlrpc_request_out_callback(struct ptlrpc_request *req)
+{
+ if (SPTLRPC_FLVR_SVC(req->rq_flvr.sf_rpc) != SPTLRPC_SVC_PRIV)
+ return;
+
+ LASSERT(req->rq_clrbuf);
+ if (req->rq_pool || !req->rq_reqbuf)
+ return;
+
+ OBD_FREE(req->rq_reqbuf, req->rq_reqbuf_len);
+ req->rq_reqbuf = NULL;
+ req->rq_reqbuf_len = 0;
+}
+
+/**
+ * Given an import \a imp, check whether current user has a valid context
+ * or not. We may create a new context and try to refresh it, and try
+ * repeatedly try in case of non-fatal errors. Return 0 means success.
+ */
+int sptlrpc_import_check_ctx(struct obd_import *imp)
+{
+ struct ptlrpc_sec *sec;
+ struct ptlrpc_cli_ctx *ctx;
+ struct ptlrpc_request *req = NULL;
+ int rc;
+
+ might_sleep();
+
+ sec = sptlrpc_import_sec_ref(imp);
+ ctx = get_my_ctx(sec);
+ sptlrpc_sec_put(sec);
+
+ if (!ctx)
+ return -ENOMEM;
+
+ if (cli_ctx_is_eternal(ctx) ||
+ ctx->cc_ops->validate(ctx) == 0) {
+ sptlrpc_cli_ctx_put(ctx, 1);
+ return 0;
+ }
+
+ if (cli_ctx_is_error(ctx)) {
+ sptlrpc_cli_ctx_put(ctx, 1);
+ return -EACCES;
+ }
+
+ req = ptlrpc_request_cache_alloc(GFP_NOFS);
+ if (!req)
+ return -ENOMEM;
+
+ spin_lock_init(&req->rq_lock);
+ atomic_set(&req->rq_refcount, 10000);
+ INIT_LIST_HEAD(&req->rq_ctx_chain);
+ init_waitqueue_head(&req->rq_reply_waitq);
+ init_waitqueue_head(&req->rq_set_waitq);
+ req->rq_import = imp;
+ req->rq_flvr = sec->ps_flvr;
+ req->rq_cli_ctx = ctx;
+
+ rc = sptlrpc_req_refresh_ctx(req, 0);
+ LASSERT(list_empty(&req->rq_ctx_chain));
+ sptlrpc_cli_ctx_put(req->rq_cli_ctx, 1);
+ ptlrpc_request_cache_free(req);
+
+ return rc;
+}
+
+/**
+ * Used by ptlrpc client, to perform the pre-defined security transformation
+ * upon the request message of \a req. After this function called,
+ * req->rq_reqmsg is still accessible as clear text.
+ */
+int sptlrpc_cli_wrap_request(struct ptlrpc_request *req)
+{
+ struct ptlrpc_cli_ctx *ctx = req->rq_cli_ctx;
+ int rc = 0;
+
+ LASSERT(ctx);
+ LASSERT(ctx->cc_sec);
+ LASSERT(req->rq_reqbuf || req->rq_clrbuf);
+
+ /* we wrap bulk request here because now we can be sure
+ * the context is uptodate.
+ */
+ if (req->rq_bulk) {
+ rc = sptlrpc_cli_wrap_bulk(req, req->rq_bulk);
+ if (rc)
+ return rc;
+ }
+
+ switch (SPTLRPC_FLVR_SVC(req->rq_flvr.sf_rpc)) {
+ case SPTLRPC_SVC_NULL:
+ case SPTLRPC_SVC_AUTH:
+ case SPTLRPC_SVC_INTG:
+ LASSERT(ctx->cc_ops->sign);
+ rc = ctx->cc_ops->sign(ctx, req);
+ break;
+ case SPTLRPC_SVC_PRIV:
+ LASSERT(ctx->cc_ops->seal);
+ rc = ctx->cc_ops->seal(ctx, req);
+ break;
+ default:
+ LBUG();
+ }
+
+ if (rc == 0) {
+ LASSERT(req->rq_reqdata_len);
+ LASSERT(req->rq_reqdata_len % 8 == 0);
+ LASSERT(req->rq_reqdata_len <= req->rq_reqbuf_len);
+ }
+
+ return rc;
+}
+
+static int do_cli_unwrap_reply(struct ptlrpc_request *req)
+{
+ struct ptlrpc_cli_ctx *ctx = req->rq_cli_ctx;
+ int rc;
+
+ LASSERT(ctx);
+ LASSERT(ctx->cc_sec);
+ LASSERT(req->rq_repbuf);
+ LASSERT(req->rq_repdata);
+ LASSERT(req->rq_repmsg == NULL);
+
+ req->rq_rep_swab_mask = 0;
+
+ rc = __lustre_unpack_msg(req->rq_repdata, req->rq_repdata_len);
+ switch (rc) {
+ case 1:
+ lustre_set_rep_swabbed(req, MSG_PTLRPC_HEADER_OFF);
+ case 0:
+ break;
+ default:
+ CERROR("failed unpack reply: x%llu\n", req->rq_xid);
+ return -EPROTO;
+ }
+
+ if (req->rq_repdata_len < sizeof(struct lustre_msg)) {
+ CERROR("replied data length %d too small\n",
+ req->rq_repdata_len);
+ return -EPROTO;
+ }
+
+ if (SPTLRPC_FLVR_POLICY(req->rq_repdata->lm_secflvr) !=
+ SPTLRPC_FLVR_POLICY(req->rq_flvr.sf_rpc)) {
+ CERROR("reply policy %u doesn't match request policy %u\n",
+ SPTLRPC_FLVR_POLICY(req->rq_repdata->lm_secflvr),
+ SPTLRPC_FLVR_POLICY(req->rq_flvr.sf_rpc));
+ return -EPROTO;
+ }
+
+ switch (SPTLRPC_FLVR_SVC(req->rq_flvr.sf_rpc)) {
+ case SPTLRPC_SVC_NULL:
+ case SPTLRPC_SVC_AUTH:
+ case SPTLRPC_SVC_INTG:
+ LASSERT(ctx->cc_ops->verify);
+ rc = ctx->cc_ops->verify(ctx, req);
+ break;
+ case SPTLRPC_SVC_PRIV:
+ LASSERT(ctx->cc_ops->unseal);
+ rc = ctx->cc_ops->unseal(ctx, req);
+ break;
+ default:
+ LBUG();
+ }
+ LASSERT(rc || req->rq_repmsg || req->rq_resend);
+
+ if (SPTLRPC_FLVR_POLICY(req->rq_flvr.sf_rpc) != SPTLRPC_POLICY_NULL &&
+ !req->rq_ctx_init)
+ req->rq_rep_swab_mask = 0;
+ return rc;
+}
+
+/**
+ * Used by ptlrpc client, to perform security transformation upon the reply
+ * message of \a req. After return successfully, req->rq_repmsg points to
+ * the reply message in clear text.
+ *
+ * \pre the reply buffer should have been un-posted from LNet, so nothing is
+ * going to change.
+ */
+int sptlrpc_cli_unwrap_reply(struct ptlrpc_request *req)
+{
+ LASSERT(req->rq_repbuf);
+ LASSERT(req->rq_repdata == NULL);
+ LASSERT(req->rq_repmsg == NULL);
+ LASSERT(req->rq_reply_off + req->rq_nob_received <= req->rq_repbuf_len);
+
+ if (req->rq_reply_off == 0 &&
+ (lustre_msghdr_get_flags(req->rq_reqmsg) & MSGHDR_AT_SUPPORT)) {
+ CERROR("real reply with offset 0\n");
+ return -EPROTO;
+ }
+
+ if (req->rq_reply_off % 8 != 0) {
+ CERROR("reply at odd offset %u\n", req->rq_reply_off);
+ return -EPROTO;
+ }
+
+ req->rq_repdata = (struct lustre_msg *)
+ (req->rq_repbuf + req->rq_reply_off);
+ req->rq_repdata_len = req->rq_nob_received;
+
+ return do_cli_unwrap_reply(req);
+}
+
+/**
+ * Used by ptlrpc client, to perform security transformation upon the early
+ * reply message of \a req. We expect the rq_reply_off is 0, and
+ * rq_nob_received is the early reply size.
+ *
+ * Because the receive buffer might be still posted, the reply data might be
+ * changed at any time, no matter we're holding rq_lock or not. For this reason
+ * we allocate a separate ptlrpc_request and reply buffer for early reply
+ * processing.
+ *
+ * \retval 0 success, \a req_ret is filled with a duplicated ptlrpc_request.
+ * Later the caller must call sptlrpc_cli_finish_early_reply() on the returned
+ * \a *req_ret to release it.
+ * \retval -ev error number, and \a req_ret will not be set.
+ */
+int sptlrpc_cli_unwrap_early_reply(struct ptlrpc_request *req,
+ struct ptlrpc_request **req_ret)
+{
+ struct ptlrpc_request *early_req;
+ char *early_buf;
+ int early_bufsz, early_size;
+ int rc;
+
+ early_req = ptlrpc_request_cache_alloc(GFP_NOFS);
+ if (early_req == NULL)
+ return -ENOMEM;
+
+ early_size = req->rq_nob_received;
+ early_bufsz = size_roundup_power2(early_size);
+ OBD_ALLOC_LARGE(early_buf, early_bufsz);
+ if (early_buf == NULL) {
+ rc = -ENOMEM;
+ goto err_req;
+ }
+
+ /* sanity checkings and copy data out, do it inside spinlock */
+ spin_lock(&req->rq_lock);
+
+ if (req->rq_replied) {
+ spin_unlock(&req->rq_lock);
+ rc = -EALREADY;
+ goto err_buf;
+ }
+
+ LASSERT(req->rq_repbuf);
+ LASSERT(req->rq_repdata == NULL);
+ LASSERT(req->rq_repmsg == NULL);
+
+ if (req->rq_reply_off != 0) {
+ CERROR("early reply with offset %u\n", req->rq_reply_off);
+ spin_unlock(&req->rq_lock);
+ rc = -EPROTO;
+ goto err_buf;
+ }
+
+ if (req->rq_nob_received != early_size) {
+ /* even another early arrived the size should be the same */
+ CERROR("data size has changed from %u to %u\n",
+ early_size, req->rq_nob_received);
+ spin_unlock(&req->rq_lock);
+ rc = -EINVAL;
+ goto err_buf;
+ }
+
+ if (req->rq_nob_received < sizeof(struct lustre_msg)) {
+ CERROR("early reply length %d too small\n",
+ req->rq_nob_received);
+ spin_unlock(&req->rq_lock);
+ rc = -EALREADY;
+ goto err_buf;
+ }
+
+ memcpy(early_buf, req->rq_repbuf, early_size);
+ spin_unlock(&req->rq_lock);
+
+ spin_lock_init(&early_req->rq_lock);
+ early_req->rq_cli_ctx = sptlrpc_cli_ctx_get(req->rq_cli_ctx);
+ early_req->rq_flvr = req->rq_flvr;
+ early_req->rq_repbuf = early_buf;
+ early_req->rq_repbuf_len = early_bufsz;
+ early_req->rq_repdata = (struct lustre_msg *) early_buf;
+ early_req->rq_repdata_len = early_size;
+ early_req->rq_early = 1;
+ early_req->rq_reqmsg = req->rq_reqmsg;
+
+ rc = do_cli_unwrap_reply(early_req);
+ if (rc) {
+ DEBUG_REQ(D_ADAPTTO, early_req,
+ "error %d unwrap early reply", rc);
+ goto err_ctx;
+ }
+
+ LASSERT(early_req->rq_repmsg);
+ *req_ret = early_req;
+ return 0;
+
+err_ctx:
+ sptlrpc_cli_ctx_put(early_req->rq_cli_ctx, 1);
+err_buf:
+ OBD_FREE_LARGE(early_buf, early_bufsz);
+err_req:
+ ptlrpc_request_cache_free(early_req);
+ return rc;
+}
+
+/**
+ * Used by ptlrpc client, to release a processed early reply \a early_req.
+ *
+ * \pre \a early_req was obtained from calling sptlrpc_cli_unwrap_early_reply().
+ */
+void sptlrpc_cli_finish_early_reply(struct ptlrpc_request *early_req)
+{
+ LASSERT(early_req->rq_repbuf);
+ LASSERT(early_req->rq_repdata);
+ LASSERT(early_req->rq_repmsg);
+
+ sptlrpc_cli_ctx_put(early_req->rq_cli_ctx, 1);
+ OBD_FREE_LARGE(early_req->rq_repbuf, early_req->rq_repbuf_len);
+ ptlrpc_request_cache_free(early_req);
+}
+
+/**************************************************
+ * sec ID *
+ **************************************************/
+
+/*
+ * "fixed" sec (e.g. null) use sec_id < 0
+ */
+static atomic_t sptlrpc_sec_id = ATOMIC_INIT(1);
+
+int sptlrpc_get_next_secid(void)
+{
+ return atomic_inc_return(&sptlrpc_sec_id);
+}
+EXPORT_SYMBOL(sptlrpc_get_next_secid);
+
+/**************************************************
+ * client side high-level security APIs *
+ **************************************************/
+
+static int sec_cop_flush_ctx_cache(struct ptlrpc_sec *sec, uid_t uid,
+ int grace, int force)
+{
+ struct ptlrpc_sec_policy *policy = sec->ps_policy;
+
+ LASSERT(policy->sp_cops);
+ LASSERT(policy->sp_cops->flush_ctx_cache);
+
+ return policy->sp_cops->flush_ctx_cache(sec, uid, grace, force);
+}
+
+static void sec_cop_destroy_sec(struct ptlrpc_sec *sec)
+{
+ struct ptlrpc_sec_policy *policy = sec->ps_policy;
+
+ LASSERT_ATOMIC_ZERO(&sec->ps_refcount);
+ LASSERT_ATOMIC_ZERO(&sec->ps_nctx);
+ LASSERT(policy->sp_cops->destroy_sec);
+
+ CDEBUG(D_SEC, "%s@%p: being destroyed\n", sec->ps_policy->sp_name, sec);
+
+ policy->sp_cops->destroy_sec(sec);
+ sptlrpc_policy_put(policy);
+}
+
+void sptlrpc_sec_destroy(struct ptlrpc_sec *sec)
+{
+ sec_cop_destroy_sec(sec);
+}
+EXPORT_SYMBOL(sptlrpc_sec_destroy);
+
+static void sptlrpc_sec_kill(struct ptlrpc_sec *sec)
+{
+ LASSERT_ATOMIC_POS(&sec->ps_refcount);
+
+ if (sec->ps_policy->sp_cops->kill_sec) {
+ sec->ps_policy->sp_cops->kill_sec(sec);
+
+ sec_cop_flush_ctx_cache(sec, -1, 1, 1);
+ }
+}
+
+struct ptlrpc_sec *sptlrpc_sec_get(struct ptlrpc_sec *sec)
+{
+ if (sec)
+ atomic_inc(&sec->ps_refcount);
+
+ return sec;
+}
+EXPORT_SYMBOL(sptlrpc_sec_get);
+
+void sptlrpc_sec_put(struct ptlrpc_sec *sec)
+{
+ if (sec) {
+ LASSERT_ATOMIC_POS(&sec->ps_refcount);
+
+ if (atomic_dec_and_test(&sec->ps_refcount)) {
+ sptlrpc_gc_del_sec(sec);
+ sec_cop_destroy_sec(sec);
+ }
+ }
+}
+EXPORT_SYMBOL(sptlrpc_sec_put);
+
+/*
+ * policy module is responsible for taking reference of import
+ */
+static
+struct ptlrpc_sec *sptlrpc_sec_create(struct obd_import *imp,
+ struct ptlrpc_svc_ctx *svc_ctx,
+ struct sptlrpc_flavor *sf,
+ enum lustre_sec_part sp)
+{
+ struct ptlrpc_sec_policy *policy;
+ struct ptlrpc_sec *sec;
+ char str[32];
+
+ if (svc_ctx) {
+ LASSERT(imp->imp_dlm_fake == 1);
+
+ CDEBUG(D_SEC, "%s %s: reverse sec using flavor %s\n",
+ imp->imp_obd->obd_type->typ_name,
+ imp->imp_obd->obd_name,
+ sptlrpc_flavor2name(sf, str, sizeof(str)));
+
+ policy = sptlrpc_policy_get(svc_ctx->sc_policy);
+ sf->sf_flags |= PTLRPC_SEC_FL_REVERSE | PTLRPC_SEC_FL_ROOTONLY;
+ } else {
+ LASSERT(imp->imp_dlm_fake == 0);
+
+ CDEBUG(D_SEC, "%s %s: select security flavor %s\n",
+ imp->imp_obd->obd_type->typ_name,
+ imp->imp_obd->obd_name,
+ sptlrpc_flavor2name(sf, str, sizeof(str)));
+
+ policy = sptlrpc_wireflavor2policy(sf->sf_rpc);
+ if (!policy) {
+ CERROR("invalid flavor 0x%x\n", sf->sf_rpc);
+ return NULL;
+ }
+ }
+
+ sec = policy->sp_cops->create_sec(imp, svc_ctx, sf);
+ if (sec) {
+ atomic_inc(&sec->ps_refcount);
+
+ sec->ps_part = sp;
+
+ if (sec->ps_gc_interval && policy->sp_cops->gc_ctx)
+ sptlrpc_gc_add_sec(sec);
+ } else {
+ sptlrpc_policy_put(policy);
+ }
+
+ return sec;
+}
+
+struct ptlrpc_sec *sptlrpc_import_sec_ref(struct obd_import *imp)
+{
+ struct ptlrpc_sec *sec;
+
+ spin_lock(&imp->imp_lock);
+ sec = sptlrpc_sec_get(imp->imp_sec);
+ spin_unlock(&imp->imp_lock);
+
+ return sec;
+}
+EXPORT_SYMBOL(sptlrpc_import_sec_ref);
+
+static void sptlrpc_import_sec_install(struct obd_import *imp,
+ struct ptlrpc_sec *sec)
+{
+ struct ptlrpc_sec *old_sec;
+
+ LASSERT_ATOMIC_POS(&sec->ps_refcount);
+
+ spin_lock(&imp->imp_lock);
+ old_sec = imp->imp_sec;
+ imp->imp_sec = sec;
+ spin_unlock(&imp->imp_lock);
+
+ if (old_sec) {
+ sptlrpc_sec_kill(old_sec);
+
+ /* balance the ref taken by this import */
+ sptlrpc_sec_put(old_sec);
+ }
+}
+
+static inline
+int flavor_equal(struct sptlrpc_flavor *sf1, struct sptlrpc_flavor *sf2)
+{
+ return (memcmp(sf1, sf2, sizeof(*sf1)) == 0);
+}
+
+static inline
+void flavor_copy(struct sptlrpc_flavor *dst, struct sptlrpc_flavor *src)
+{
+ *dst = *src;
+}
+
+static void sptlrpc_import_sec_adapt_inplace(struct obd_import *imp,
+ struct ptlrpc_sec *sec,
+ struct sptlrpc_flavor *sf)
+{
+ char str1[32], str2[32];
+
+ if (sec->ps_flvr.sf_flags != sf->sf_flags)
+ CDEBUG(D_SEC, "changing sec flags: %s -> %s\n",
+ sptlrpc_secflags2str(sec->ps_flvr.sf_flags,
+ str1, sizeof(str1)),
+ sptlrpc_secflags2str(sf->sf_flags,
+ str2, sizeof(str2)));
+
+ spin_lock(&sec->ps_lock);
+ flavor_copy(&sec->ps_flvr, sf);
+ spin_unlock(&sec->ps_lock);
+}
+
+/**
+ * To get an appropriate ptlrpc_sec for the \a imp, according to the current
+ * configuration. Upon called, imp->imp_sec may or may not be NULL.
+ *
+ * - regular import: \a svc_ctx should be NULL and \a flvr is ignored;
+ * - reverse import: \a svc_ctx and \a flvr are obtained from incoming request.
+ */
+int sptlrpc_import_sec_adapt(struct obd_import *imp,
+ struct ptlrpc_svc_ctx *svc_ctx,
+ struct sptlrpc_flavor *flvr)
+{
+ struct ptlrpc_connection *conn;
+ struct sptlrpc_flavor sf;
+ struct ptlrpc_sec *sec, *newsec;
+ enum lustre_sec_part sp;
+ char str[24];
+ int rc = 0;
+
+ might_sleep();
+
+ if (imp == NULL)
+ return 0;
+
+ conn = imp->imp_connection;
+
+ if (svc_ctx == NULL) {
+ struct client_obd *cliobd = &imp->imp_obd->u.cli;
+ /*
+ * normal import, determine flavor from rule set, except
+ * for mgc the flavor is predetermined.
+ */
+ if (cliobd->cl_sp_me == LUSTRE_SP_MGC)
+ sf = cliobd->cl_flvr_mgc;
+ else
+ sptlrpc_conf_choose_flavor(cliobd->cl_sp_me,
+ cliobd->cl_sp_to,
+ &cliobd->cl_target_uuid,
+ conn->c_self, &sf);
+
+ sp = imp->imp_obd->u.cli.cl_sp_me;
+ } else {
+ /* reverse import, determine flavor from incoming request */
+ sf = *flvr;
+
+ if (sf.sf_rpc != SPTLRPC_FLVR_NULL)
+ sf.sf_flags = PTLRPC_SEC_FL_REVERSE |
+ PTLRPC_SEC_FL_ROOTONLY;
+
+ sp = sptlrpc_target_sec_part(imp->imp_obd);
+ }
+
+ sec = sptlrpc_import_sec_ref(imp);
+ if (sec) {
+ char str2[24];
+
+ if (flavor_equal(&sf, &sec->ps_flvr))
+ goto out;
+
+ CDEBUG(D_SEC, "import %s->%s: changing flavor %s -> %s\n",
+ imp->imp_obd->obd_name,
+ obd_uuid2str(&conn->c_remote_uuid),
+ sptlrpc_flavor2name(&sec->ps_flvr, str, sizeof(str)),
+ sptlrpc_flavor2name(&sf, str2, sizeof(str2)));
+
+ if (SPTLRPC_FLVR_POLICY(sf.sf_rpc) ==
+ SPTLRPC_FLVR_POLICY(sec->ps_flvr.sf_rpc) &&
+ SPTLRPC_FLVR_MECH(sf.sf_rpc) ==
+ SPTLRPC_FLVR_MECH(sec->ps_flvr.sf_rpc)) {
+ sptlrpc_import_sec_adapt_inplace(imp, sec, &sf);
+ goto out;
+ }
+ } else if (SPTLRPC_FLVR_BASE(sf.sf_rpc) !=
+ SPTLRPC_FLVR_BASE(SPTLRPC_FLVR_NULL)) {
+ CDEBUG(D_SEC, "import %s->%s netid %x: select flavor %s\n",
+ imp->imp_obd->obd_name,
+ obd_uuid2str(&conn->c_remote_uuid),
+ LNET_NIDNET(conn->c_self),
+ sptlrpc_flavor2name(&sf, str, sizeof(str)));
+ }
+
+ mutex_lock(&imp->imp_sec_mutex);
+
+ newsec = sptlrpc_sec_create(imp, svc_ctx, &sf, sp);
+ if (newsec) {
+ sptlrpc_import_sec_install(imp, newsec);
+ } else {
+ CERROR("import %s->%s: failed to create new sec\n",
+ imp->imp_obd->obd_name,
+ obd_uuid2str(&conn->c_remote_uuid));
+ rc = -EPERM;
+ }
+
+ mutex_unlock(&imp->imp_sec_mutex);
+out:
+ sptlrpc_sec_put(sec);
+ return rc;
+}
+
+void sptlrpc_import_sec_put(struct obd_import *imp)
+{
+ if (imp->imp_sec) {
+ sptlrpc_sec_kill(imp->imp_sec);
+
+ sptlrpc_sec_put(imp->imp_sec);
+ imp->imp_sec = NULL;
+ }
+}
+
+static void import_flush_ctx_common(struct obd_import *imp,
+ uid_t uid, int grace, int force)
+{
+ struct ptlrpc_sec *sec;
+
+ if (imp == NULL)
+ return;
+
+ sec = sptlrpc_import_sec_ref(imp);
+ if (sec == NULL)
+ return;
+
+ sec_cop_flush_ctx_cache(sec, uid, grace, force);
+ sptlrpc_sec_put(sec);
+}
+
+void sptlrpc_import_flush_root_ctx(struct obd_import *imp)
+{
+ /* it's important to use grace mode, see explain in
+ * sptlrpc_req_refresh_ctx() */
+ import_flush_ctx_common(imp, 0, 1, 1);
+}
+
+void sptlrpc_import_flush_my_ctx(struct obd_import *imp)
+{
+ import_flush_ctx_common(imp, from_kuid(&init_user_ns, current_uid()),
+ 1, 1);
+}
+EXPORT_SYMBOL(sptlrpc_import_flush_my_ctx);
+
+void sptlrpc_import_flush_all_ctx(struct obd_import *imp)
+{
+ import_flush_ctx_common(imp, -1, 1, 1);
+}
+EXPORT_SYMBOL(sptlrpc_import_flush_all_ctx);
+
+/**
+ * Used by ptlrpc client to allocate request buffer of \a req. Upon return
+ * successfully, req->rq_reqmsg points to a buffer with size \a msgsize.
+ */
+int sptlrpc_cli_alloc_reqbuf(struct ptlrpc_request *req, int msgsize)
+{
+ struct ptlrpc_cli_ctx *ctx = req->rq_cli_ctx;
+ struct ptlrpc_sec_policy *policy;
+ int rc;
+
+ LASSERT(ctx);
+ LASSERT(ctx->cc_sec);
+ LASSERT(ctx->cc_sec->ps_policy);
+ LASSERT(req->rq_reqmsg == NULL);
+ LASSERT_ATOMIC_POS(&ctx->cc_refcount);
+
+ policy = ctx->cc_sec->ps_policy;
+ rc = policy->sp_cops->alloc_reqbuf(ctx->cc_sec, req, msgsize);
+ if (!rc) {
+ LASSERT(req->rq_reqmsg);
+ LASSERT(req->rq_reqbuf || req->rq_clrbuf);
+
+ /* zeroing preallocated buffer */
+ if (req->rq_pool)
+ memset(req->rq_reqmsg, 0, msgsize);
+ }
+
+ return rc;
+}
+
+/**
+ * Used by ptlrpc client to free request buffer of \a req. After this
+ * req->rq_reqmsg is set to NULL and should not be accessed anymore.
+ */
+void sptlrpc_cli_free_reqbuf(struct ptlrpc_request *req)
+{
+ struct ptlrpc_cli_ctx *ctx = req->rq_cli_ctx;
+ struct ptlrpc_sec_policy *policy;
+
+ LASSERT(ctx);
+ LASSERT(ctx->cc_sec);
+ LASSERT(ctx->cc_sec->ps_policy);
+ LASSERT_ATOMIC_POS(&ctx->cc_refcount);
+
+ if (req->rq_reqbuf == NULL && req->rq_clrbuf == NULL)
+ return;
+
+ policy = ctx->cc_sec->ps_policy;
+ policy->sp_cops->free_reqbuf(ctx->cc_sec, req);
+ req->rq_reqmsg = NULL;
+}
+
+/*
+ * NOTE caller must guarantee the buffer size is enough for the enlargement
+ */
+void _sptlrpc_enlarge_msg_inplace(struct lustre_msg *msg,
+ int segment, int newsize)
+{
+ void *src, *dst;
+ int oldsize, oldmsg_size, movesize;
+
+ LASSERT(segment < msg->lm_bufcount);
+ LASSERT(msg->lm_buflens[segment] <= newsize);
+
+ if (msg->lm_buflens[segment] == newsize)
+ return;
+
+ /* nothing to do if we are enlarging the last segment */
+ if (segment == msg->lm_bufcount - 1) {
+ msg->lm_buflens[segment] = newsize;
+ return;
+ }
+
+ oldsize = msg->lm_buflens[segment];
+
+ src = lustre_msg_buf(msg, segment + 1, 0);
+ msg->lm_buflens[segment] = newsize;
+ dst = lustre_msg_buf(msg, segment + 1, 0);
+ msg->lm_buflens[segment] = oldsize;
+
+ /* move from segment + 1 to end segment */
+ LASSERT(msg->lm_magic == LUSTRE_MSG_MAGIC_V2);
+ oldmsg_size = lustre_msg_size_v2(msg->lm_bufcount, msg->lm_buflens);
+ movesize = oldmsg_size - ((unsigned long) src - (unsigned long) msg);
+ LASSERT(movesize >= 0);
+
+ if (movesize)
+ memmove(dst, src, movesize);
+
+ /* note we don't clear the ares where old data live, not secret */
+
+ /* finally set new segment size */
+ msg->lm_buflens[segment] = newsize;
+}
+EXPORT_SYMBOL(_sptlrpc_enlarge_msg_inplace);
+
+/**
+ * Used by ptlrpc client to enlarge the \a segment of request message pointed
+ * by req->rq_reqmsg to size \a newsize, all previously filled-in data will be
+ * preserved after the enlargement. this must be called after original request
+ * buffer being allocated.
+ *
+ * \note after this be called, rq_reqmsg and rq_reqlen might have been changed,
+ * so caller should refresh its local pointers if needed.
+ */
+int sptlrpc_cli_enlarge_reqbuf(struct ptlrpc_request *req,
+ int segment, int newsize)
+{
+ struct ptlrpc_cli_ctx *ctx = req->rq_cli_ctx;
+ struct ptlrpc_sec_cops *cops;
+ struct lustre_msg *msg = req->rq_reqmsg;
+
+ LASSERT(ctx);
+ LASSERT(msg);
+ LASSERT(msg->lm_bufcount > segment);
+ LASSERT(msg->lm_buflens[segment] <= newsize);
+
+ if (msg->lm_buflens[segment] == newsize)
+ return 0;
+
+ cops = ctx->cc_sec->ps_policy->sp_cops;
+ LASSERT(cops->enlarge_reqbuf);
+ return cops->enlarge_reqbuf(ctx->cc_sec, req, segment, newsize);
+}
+EXPORT_SYMBOL(sptlrpc_cli_enlarge_reqbuf);
+
+/**
+ * Used by ptlrpc client to allocate reply buffer of \a req.
+ *
+ * \note After this, req->rq_repmsg is still not accessible.
+ */
+int sptlrpc_cli_alloc_repbuf(struct ptlrpc_request *req, int msgsize)
+{
+ struct ptlrpc_cli_ctx *ctx = req->rq_cli_ctx;
+ struct ptlrpc_sec_policy *policy;
+
+ LASSERT(ctx);
+ LASSERT(ctx->cc_sec);
+ LASSERT(ctx->cc_sec->ps_policy);
+
+ if (req->rq_repbuf)
+ return 0;
+
+ policy = ctx->cc_sec->ps_policy;
+ return policy->sp_cops->alloc_repbuf(ctx->cc_sec, req, msgsize);
+}
+
+/**
+ * Used by ptlrpc client to free reply buffer of \a req. After this
+ * req->rq_repmsg is set to NULL and should not be accessed anymore.
+ */
+void sptlrpc_cli_free_repbuf(struct ptlrpc_request *req)
+{
+ struct ptlrpc_cli_ctx *ctx = req->rq_cli_ctx;
+ struct ptlrpc_sec_policy *policy;
+
+ LASSERT(ctx);
+ LASSERT(ctx->cc_sec);
+ LASSERT(ctx->cc_sec->ps_policy);
+ LASSERT_ATOMIC_POS(&ctx->cc_refcount);
+
+ if (req->rq_repbuf == NULL)
+ return;
+ LASSERT(req->rq_repbuf_len);
+
+ policy = ctx->cc_sec->ps_policy;
+ policy->sp_cops->free_repbuf(ctx->cc_sec, req);
+ req->rq_repmsg = NULL;
+}
+
+int sptlrpc_cli_install_rvs_ctx(struct obd_import *imp,
+ struct ptlrpc_cli_ctx *ctx)
+{
+ struct ptlrpc_sec_policy *policy = ctx->cc_sec->ps_policy;
+
+ if (!policy->sp_cops->install_rctx)
+ return 0;
+ return policy->sp_cops->install_rctx(imp, ctx->cc_sec, ctx);
+}
+
+int sptlrpc_svc_install_rvs_ctx(struct obd_import *imp,
+ struct ptlrpc_svc_ctx *ctx)
+{
+ struct ptlrpc_sec_policy *policy = ctx->sc_policy;
+
+ if (!policy->sp_sops->install_rctx)
+ return 0;
+ return policy->sp_sops->install_rctx(imp, ctx);
+}
+
+/****************************************
+ * server side security *
+ ****************************************/
+
+static int flavor_allowed(struct sptlrpc_flavor *exp,
+ struct ptlrpc_request *req)
+{
+ struct sptlrpc_flavor *flvr = &req->rq_flvr;
+
+ if (exp->sf_rpc == SPTLRPC_FLVR_ANY || exp->sf_rpc == flvr->sf_rpc)
+ return 1;
+
+ if ((req->rq_ctx_init || req->rq_ctx_fini) &&
+ SPTLRPC_FLVR_POLICY(exp->sf_rpc) ==
+ SPTLRPC_FLVR_POLICY(flvr->sf_rpc) &&
+ SPTLRPC_FLVR_MECH(exp->sf_rpc) == SPTLRPC_FLVR_MECH(flvr->sf_rpc))
+ return 1;
+
+ return 0;
+}
+
+#define EXP_FLVR_UPDATE_EXPIRE (OBD_TIMEOUT_DEFAULT + 10)
+
+/**
+ * Given an export \a exp, check whether the flavor of incoming \a req
+ * is allowed by the export \a exp. Main logic is about taking care of
+ * changing configurations. Return 0 means success.
+ */
+int sptlrpc_target_export_check(struct obd_export *exp,
+ struct ptlrpc_request *req)
+{
+ struct sptlrpc_flavor flavor;
+
+ if (exp == NULL)
+ return 0;
+
+ /* client side export has no imp_reverse, skip
+ * FIXME maybe we should check flavor this as well??? */
+ if (exp->exp_imp_reverse == NULL)
+ return 0;
+
+ /* don't care about ctx fini rpc */
+ if (req->rq_ctx_fini)
+ return 0;
+
+ spin_lock(&exp->exp_lock);
+
+ /* if flavor just changed (exp->exp_flvr_changed != 0), we wait for
+ * the first req with the new flavor, then treat it as current flavor,
+ * adapt reverse sec according to it.
+ * note the first rpc with new flavor might not be with root ctx, in
+ * which case delay the sec_adapt by leaving exp_flvr_adapt == 1. */
+ if (unlikely(exp->exp_flvr_changed) &&
+ flavor_allowed(&exp->exp_flvr_old[1], req)) {
+ /* make the new flavor as "current", and old ones as
+ * about-to-expire */
+ CDEBUG(D_SEC, "exp %p: just changed: %x->%x\n", exp,
+ exp->exp_flvr.sf_rpc, exp->exp_flvr_old[1].sf_rpc);
+ flavor = exp->exp_flvr_old[1];
+ exp->exp_flvr_old[1] = exp->exp_flvr_old[0];
+ exp->exp_flvr_expire[1] = exp->exp_flvr_expire[0];
+ exp->exp_flvr_old[0] = exp->exp_flvr;
+ exp->exp_flvr_expire[0] = get_seconds() +
+ EXP_FLVR_UPDATE_EXPIRE;
+ exp->exp_flvr = flavor;
+
+ /* flavor change finished */
+ exp->exp_flvr_changed = 0;
+ LASSERT(exp->exp_flvr_adapt == 1);
+
+ /* if it's gss, we only interested in root ctx init */
+ if (req->rq_auth_gss &&
+ !(req->rq_ctx_init &&
+ (req->rq_auth_usr_root || req->rq_auth_usr_mdt ||
+ req->rq_auth_usr_ost))) {
+ spin_unlock(&exp->exp_lock);
+ CDEBUG(D_SEC, "is good but not root(%d:%d:%d:%d:%d)\n",
+ req->rq_auth_gss, req->rq_ctx_init,
+ req->rq_auth_usr_root, req->rq_auth_usr_mdt,
+ req->rq_auth_usr_ost);
+ return 0;
+ }
+
+ exp->exp_flvr_adapt = 0;
+ spin_unlock(&exp->exp_lock);
+
+ return sptlrpc_import_sec_adapt(exp->exp_imp_reverse,
+ req->rq_svc_ctx, &flavor);
+ }
+
+ /* if it equals to the current flavor, we accept it, but need to
+ * dealing with reverse sec/ctx */
+ if (likely(flavor_allowed(&exp->exp_flvr, req))) {
+ /* most cases should return here, we only interested in
+ * gss root ctx init */
+ if (!req->rq_auth_gss || !req->rq_ctx_init ||
+ (!req->rq_auth_usr_root && !req->rq_auth_usr_mdt &&
+ !req->rq_auth_usr_ost)) {
+ spin_unlock(&exp->exp_lock);
+ return 0;
+ }
+
+ /* if flavor just changed, we should not proceed, just leave
+ * it and current flavor will be discovered and replaced
+ * shortly, and let _this_ rpc pass through */
+ if (exp->exp_flvr_changed) {
+ LASSERT(exp->exp_flvr_adapt);
+ spin_unlock(&exp->exp_lock);
+ return 0;
+ }
+
+ if (exp->exp_flvr_adapt) {
+ exp->exp_flvr_adapt = 0;
+ CDEBUG(D_SEC, "exp %p (%x|%x|%x): do delayed adapt\n",
+ exp, exp->exp_flvr.sf_rpc,
+ exp->exp_flvr_old[0].sf_rpc,
+ exp->exp_flvr_old[1].sf_rpc);
+ flavor = exp->exp_flvr;
+ spin_unlock(&exp->exp_lock);
+
+ return sptlrpc_import_sec_adapt(exp->exp_imp_reverse,
+ req->rq_svc_ctx,
+ &flavor);
+ } else {
+ CDEBUG(D_SEC, "exp %p (%x|%x|%x): is current flavor, install rvs ctx\n",
+ exp, exp->exp_flvr.sf_rpc,
+ exp->exp_flvr_old[0].sf_rpc,
+ exp->exp_flvr_old[1].sf_rpc);
+ spin_unlock(&exp->exp_lock);
+
+ return sptlrpc_svc_install_rvs_ctx(exp->exp_imp_reverse,
+ req->rq_svc_ctx);
+ }
+ }
+
+ if (exp->exp_flvr_expire[0]) {
+ if (exp->exp_flvr_expire[0] >= get_seconds()) {
+ if (flavor_allowed(&exp->exp_flvr_old[0], req)) {
+ CDEBUG(D_SEC, "exp %p (%x|%x|%x): match the middle one (" CFS_DURATION_T ")\n", exp,
+ exp->exp_flvr.sf_rpc,
+ exp->exp_flvr_old[0].sf_rpc,
+ exp->exp_flvr_old[1].sf_rpc,
+ exp->exp_flvr_expire[0] -
+ get_seconds());
+ spin_unlock(&exp->exp_lock);
+ return 0;
+ }
+ } else {
+ CDEBUG(D_SEC, "mark middle expired\n");
+ exp->exp_flvr_expire[0] = 0;
+ }
+ CDEBUG(D_SEC, "exp %p (%x|%x|%x): %x not match middle\n", exp,
+ exp->exp_flvr.sf_rpc,
+ exp->exp_flvr_old[0].sf_rpc, exp->exp_flvr_old[1].sf_rpc,
+ req->rq_flvr.sf_rpc);
+ }
+
+ /* now it doesn't match the current flavor, the only chance we can
+ * accept it is match the old flavors which is not expired. */
+ if (exp->exp_flvr_changed == 0 && exp->exp_flvr_expire[1]) {
+ if (exp->exp_flvr_expire[1] >= get_seconds()) {
+ if (flavor_allowed(&exp->exp_flvr_old[1], req)) {
+ CDEBUG(D_SEC, "exp %p (%x|%x|%x): match the oldest one (" CFS_DURATION_T ")\n",
+ exp,
+ exp->exp_flvr.sf_rpc,
+ exp->exp_flvr_old[0].sf_rpc,
+ exp->exp_flvr_old[1].sf_rpc,
+ exp->exp_flvr_expire[1] -
+ get_seconds());
+ spin_unlock(&exp->exp_lock);
+ return 0;
+ }
+ } else {
+ CDEBUG(D_SEC, "mark oldest expired\n");
+ exp->exp_flvr_expire[1] = 0;
+ }
+ CDEBUG(D_SEC, "exp %p (%x|%x|%x): %x not match found\n",
+ exp, exp->exp_flvr.sf_rpc,
+ exp->exp_flvr_old[0].sf_rpc, exp->exp_flvr_old[1].sf_rpc,
+ req->rq_flvr.sf_rpc);
+ } else {
+ CDEBUG(D_SEC, "exp %p (%x|%x|%x): skip the last one\n",
+ exp, exp->exp_flvr.sf_rpc, exp->exp_flvr_old[0].sf_rpc,
+ exp->exp_flvr_old[1].sf_rpc);
+ }
+
+ spin_unlock(&exp->exp_lock);
+
+ CWARN("exp %p(%s): req %p (%u|%u|%u|%u|%u|%u) with unauthorized flavor %x, expect %x|%x(%+ld)|%x(%+ld)\n",
+ exp, exp->exp_obd->obd_name,
+ req, req->rq_auth_gss, req->rq_ctx_init, req->rq_ctx_fini,
+ req->rq_auth_usr_root, req->rq_auth_usr_mdt, req->rq_auth_usr_ost,
+ req->rq_flvr.sf_rpc,
+ exp->exp_flvr.sf_rpc,
+ exp->exp_flvr_old[0].sf_rpc,
+ exp->exp_flvr_expire[0] ?
+ (unsigned long) (exp->exp_flvr_expire[0] -
+ get_seconds()) : 0,
+ exp->exp_flvr_old[1].sf_rpc,
+ exp->exp_flvr_expire[1] ?
+ (unsigned long) (exp->exp_flvr_expire[1] -
+ get_seconds()) : 0);
+ return -EACCES;
+}
+EXPORT_SYMBOL(sptlrpc_target_export_check);
+
+void sptlrpc_target_update_exp_flavor(struct obd_device *obd,
+ struct sptlrpc_rule_set *rset)
+{
+ struct obd_export *exp;
+ struct sptlrpc_flavor new_flvr;
+
+ LASSERT(obd);
+
+ spin_lock(&obd->obd_dev_lock);
+
+ list_for_each_entry(exp, &obd->obd_exports, exp_obd_chain) {
+ if (exp->exp_connection == NULL)
+ continue;
+
+ /* note if this export had just been updated flavor
+ * (exp_flvr_changed == 1), this will override the
+ * previous one. */
+ spin_lock(&exp->exp_lock);
+ sptlrpc_target_choose_flavor(rset, exp->exp_sp_peer,
+ exp->exp_connection->c_peer.nid,
+ &new_flvr);
+ if (exp->exp_flvr_changed ||
+ !flavor_equal(&new_flvr, &exp->exp_flvr)) {
+ exp->exp_flvr_old[1] = new_flvr;
+ exp->exp_flvr_expire[1] = 0;
+ exp->exp_flvr_changed = 1;
+ exp->exp_flvr_adapt = 1;
+
+ CDEBUG(D_SEC, "exp %p (%s): updated flavor %x->%x\n",
+ exp, sptlrpc_part2name(exp->exp_sp_peer),
+ exp->exp_flvr.sf_rpc,
+ exp->exp_flvr_old[1].sf_rpc);
+ }
+ spin_unlock(&exp->exp_lock);
+ }
+
+ spin_unlock(&obd->obd_dev_lock);
+}
+EXPORT_SYMBOL(sptlrpc_target_update_exp_flavor);
+
+static int sptlrpc_svc_check_from(struct ptlrpc_request *req, int svc_rc)
+{
+ /* peer's claim is unreliable unless gss is being used */
+ if (!req->rq_auth_gss || svc_rc == SECSVC_DROP)
+ return svc_rc;
+
+ switch (req->rq_sp_from) {
+ case LUSTRE_SP_CLI:
+ if (req->rq_auth_usr_mdt || req->rq_auth_usr_ost) {
+ DEBUG_REQ(D_ERROR, req, "faked source CLI");
+ svc_rc = SECSVC_DROP;
+ }
+ break;
+ case LUSTRE_SP_MDT:
+ if (!req->rq_auth_usr_mdt) {
+ DEBUG_REQ(D_ERROR, req, "faked source MDT");
+ svc_rc = SECSVC_DROP;
+ }
+ break;
+ case LUSTRE_SP_OST:
+ if (!req->rq_auth_usr_ost) {
+ DEBUG_REQ(D_ERROR, req, "faked source OST");
+ svc_rc = SECSVC_DROP;
+ }
+ break;
+ case LUSTRE_SP_MGS:
+ case LUSTRE_SP_MGC:
+ if (!req->rq_auth_usr_root && !req->rq_auth_usr_mdt &&
+ !req->rq_auth_usr_ost) {
+ DEBUG_REQ(D_ERROR, req, "faked source MGC/MGS");
+ svc_rc = SECSVC_DROP;
+ }
+ break;
+ case LUSTRE_SP_ANY:
+ default:
+ DEBUG_REQ(D_ERROR, req, "invalid source %u", req->rq_sp_from);
+ svc_rc = SECSVC_DROP;
+ }
+
+ return svc_rc;
+}
+
+/**
+ * Used by ptlrpc server, to perform transformation upon request message of
+ * incoming \a req. This must be the first thing to do with a incoming
+ * request in ptlrpc layer.
+ *
+ * \retval SECSVC_OK success, and req->rq_reqmsg point to request message in
+ * clear text, size is req->rq_reqlen; also req->rq_svc_ctx is set.
+ * \retval SECSVC_COMPLETE success, the request has been fully processed, and
+ * reply message has been prepared.
+ * \retval SECSVC_DROP failed, this request should be dropped.
+ */
+int sptlrpc_svc_unwrap_request(struct ptlrpc_request *req)
+{
+ struct ptlrpc_sec_policy *policy;
+ struct lustre_msg *msg = req->rq_reqbuf;
+ int rc;
+
+ LASSERT(msg);
+ LASSERT(req->rq_reqmsg == NULL);
+ LASSERT(req->rq_repmsg == NULL);
+ LASSERT(req->rq_svc_ctx == NULL);
+
+ req->rq_req_swab_mask = 0;
+
+ rc = __lustre_unpack_msg(msg, req->rq_reqdata_len);
+ switch (rc) {
+ case 1:
+ lustre_set_req_swabbed(req, MSG_PTLRPC_HEADER_OFF);
+ case 0:
+ break;
+ default:
+ CERROR("error unpacking request from %s x%llu\n",
+ libcfs_id2str(req->rq_peer), req->rq_xid);
+ return SECSVC_DROP;
+ }
+
+ req->rq_flvr.sf_rpc = WIRE_FLVR(msg->lm_secflvr);
+ req->rq_sp_from = LUSTRE_SP_ANY;
+ req->rq_auth_uid = -1;
+ req->rq_auth_mapped_uid = -1;
+
+ policy = sptlrpc_wireflavor2policy(req->rq_flvr.sf_rpc);
+ if (!policy) {
+ CERROR("unsupported rpc flavor %x\n", req->rq_flvr.sf_rpc);
+ return SECSVC_DROP;
+ }
+
+ LASSERT(policy->sp_sops->accept);
+ rc = policy->sp_sops->accept(req);
+ sptlrpc_policy_put(policy);
+ LASSERT(req->rq_reqmsg || rc != SECSVC_OK);
+ LASSERT(req->rq_svc_ctx || rc == SECSVC_DROP);
+
+ /*
+ * if it's not null flavor (which means embedded packing msg),
+ * reset the swab mask for the coming inner msg unpacking.
+ */
+ if (SPTLRPC_FLVR_POLICY(req->rq_flvr.sf_rpc) != SPTLRPC_POLICY_NULL)
+ req->rq_req_swab_mask = 0;
+
+ /* sanity check for the request source */
+ rc = sptlrpc_svc_check_from(req, rc);
+ return rc;
+}
+
+/**
+ * Used by ptlrpc server, to allocate reply buffer for \a req. If succeed,
+ * req->rq_reply_state is set, and req->rq_reply_state->rs_msg point to
+ * a buffer of \a msglen size.
+ */
+int sptlrpc_svc_alloc_rs(struct ptlrpc_request *req, int msglen)
+{
+ struct ptlrpc_sec_policy *policy;
+ struct ptlrpc_reply_state *rs;
+ int rc;
+
+ LASSERT(req->rq_svc_ctx);
+ LASSERT(req->rq_svc_ctx->sc_policy);
+
+ policy = req->rq_svc_ctx->sc_policy;
+ LASSERT(policy->sp_sops->alloc_rs);
+
+ rc = policy->sp_sops->alloc_rs(req, msglen);
+ if (unlikely(rc == -ENOMEM)) {
+ struct ptlrpc_service_part *svcpt = req->rq_rqbd->rqbd_svcpt;
+ if (svcpt->scp_service->srv_max_reply_size <
+ msglen + sizeof(struct ptlrpc_reply_state)) {
+ /* Just return failure if the size is too big */
+ CERROR("size of message is too big (%zd), %d allowed",
+ msglen + sizeof(struct ptlrpc_reply_state),
+ svcpt->scp_service->srv_max_reply_size);
+ return -ENOMEM;
+ }
+
+ /* failed alloc, try emergency pool */
+ rs = lustre_get_emerg_rs(svcpt);
+ if (rs == NULL)
+ return -ENOMEM;
+
+ req->rq_reply_state = rs;
+ rc = policy->sp_sops->alloc_rs(req, msglen);
+ if (rc) {
+ lustre_put_emerg_rs(rs);
+ req->rq_reply_state = NULL;
+ }
+ }
+
+ LASSERT(rc != 0 ||
+ (req->rq_reply_state && req->rq_reply_state->rs_msg));
+
+ return rc;
+}
+
+/**
+ * Used by ptlrpc server, to perform transformation upon reply message.
+ *
+ * \post req->rq_reply_off is set to appropriate server-controlled reply offset.
+ * \post req->rq_repmsg and req->rq_reply_state->rs_msg becomes inaccessible.
+ */
+int sptlrpc_svc_wrap_reply(struct ptlrpc_request *req)
+{
+ struct ptlrpc_sec_policy *policy;
+ int rc;
+
+ LASSERT(req->rq_svc_ctx);
+ LASSERT(req->rq_svc_ctx->sc_policy);
+
+ policy = req->rq_svc_ctx->sc_policy;
+ LASSERT(policy->sp_sops->authorize);
+
+ rc = policy->sp_sops->authorize(req);
+ LASSERT(rc || req->rq_reply_state->rs_repdata_len);
+
+ return rc;
+}
+
+/**
+ * Used by ptlrpc server, to free reply_state.
+ */
+void sptlrpc_svc_free_rs(struct ptlrpc_reply_state *rs)
+{
+ struct ptlrpc_sec_policy *policy;
+ unsigned int prealloc;
+
+ LASSERT(rs->rs_svc_ctx);
+ LASSERT(rs->rs_svc_ctx->sc_policy);
+
+ policy = rs->rs_svc_ctx->sc_policy;
+ LASSERT(policy->sp_sops->free_rs);
+
+ prealloc = rs->rs_prealloc;
+ policy->sp_sops->free_rs(rs);
+
+ if (prealloc)
+ lustre_put_emerg_rs(rs);
+}
+
+void sptlrpc_svc_ctx_addref(struct ptlrpc_request *req)
+{
+ struct ptlrpc_svc_ctx *ctx = req->rq_svc_ctx;
+
+ if (ctx != NULL)
+ atomic_inc(&ctx->sc_refcount);
+}
+
+void sptlrpc_svc_ctx_decref(struct ptlrpc_request *req)
+{
+ struct ptlrpc_svc_ctx *ctx = req->rq_svc_ctx;
+
+ if (ctx == NULL)
+ return;
+
+ LASSERT_ATOMIC_POS(&ctx->sc_refcount);
+ if (atomic_dec_and_test(&ctx->sc_refcount)) {
+ if (ctx->sc_policy->sp_sops->free_ctx)
+ ctx->sc_policy->sp_sops->free_ctx(ctx);
+ }
+ req->rq_svc_ctx = NULL;
+}
+
+void sptlrpc_svc_ctx_invalidate(struct ptlrpc_request *req)
+{
+ struct ptlrpc_svc_ctx *ctx = req->rq_svc_ctx;
+
+ if (ctx == NULL)
+ return;
+
+ LASSERT_ATOMIC_POS(&ctx->sc_refcount);
+ if (ctx->sc_policy->sp_sops->invalidate_ctx)
+ ctx->sc_policy->sp_sops->invalidate_ctx(ctx);
+}
+EXPORT_SYMBOL(sptlrpc_svc_ctx_invalidate);
+
+/****************************************
+ * bulk security *
+ ****************************************/
+
+/**
+ * Perform transformation upon bulk data pointed by \a desc. This is called
+ * before transforming the request message.
+ */
+int sptlrpc_cli_wrap_bulk(struct ptlrpc_request *req,
+ struct ptlrpc_bulk_desc *desc)
+{
+ struct ptlrpc_cli_ctx *ctx;
+
+ LASSERT(req->rq_bulk_read || req->rq_bulk_write);
+
+ if (!req->rq_pack_bulk)
+ return 0;
+
+ ctx = req->rq_cli_ctx;
+ if (ctx->cc_ops->wrap_bulk)
+ return ctx->cc_ops->wrap_bulk(ctx, req, desc);
+ return 0;
+}
+EXPORT_SYMBOL(sptlrpc_cli_wrap_bulk);
+
+/**
+ * This is called after unwrap the reply message.
+ * return nob of actual plain text size received, or error code.
+ */
+int sptlrpc_cli_unwrap_bulk_read(struct ptlrpc_request *req,
+ struct ptlrpc_bulk_desc *desc,
+ int nob)
+{
+ struct ptlrpc_cli_ctx *ctx;
+ int rc;
+
+ LASSERT(req->rq_bulk_read && !req->rq_bulk_write);
+
+ if (!req->rq_pack_bulk)
+ return desc->bd_nob_transferred;
+
+ ctx = req->rq_cli_ctx;
+ if (ctx->cc_ops->unwrap_bulk) {
+ rc = ctx->cc_ops->unwrap_bulk(ctx, req, desc);
+ if (rc < 0)
+ return rc;
+ }
+ return desc->bd_nob_transferred;
+}
+EXPORT_SYMBOL(sptlrpc_cli_unwrap_bulk_read);
+
+/**
+ * This is called after unwrap the reply message.
+ * return 0 for success or error code.
+ */
+int sptlrpc_cli_unwrap_bulk_write(struct ptlrpc_request *req,
+ struct ptlrpc_bulk_desc *desc)
+{
+ struct ptlrpc_cli_ctx *ctx;
+ int rc;
+
+ LASSERT(!req->rq_bulk_read && req->rq_bulk_write);
+
+ if (!req->rq_pack_bulk)
+ return 0;
+
+ ctx = req->rq_cli_ctx;
+ if (ctx->cc_ops->unwrap_bulk) {
+ rc = ctx->cc_ops->unwrap_bulk(ctx, req, desc);
+ if (rc < 0)
+ return rc;
+ }
+
+ /*
+ * if everything is going right, nob should equals to nob_transferred.
+ * in case of privacy mode, nob_transferred needs to be adjusted.
+ */
+ if (desc->bd_nob != desc->bd_nob_transferred) {
+ CERROR("nob %d doesn't match transferred nob %d",
+ desc->bd_nob, desc->bd_nob_transferred);
+ return -EPROTO;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL(sptlrpc_cli_unwrap_bulk_write);
+
+
+/****************************************
+ * user descriptor helpers *
+ ****************************************/
+
+int sptlrpc_current_user_desc_size(void)
+{
+ int ngroups;
+
+ ngroups = current_ngroups;
+
+ if (ngroups > LUSTRE_MAX_GROUPS)
+ ngroups = LUSTRE_MAX_GROUPS;
+ return sptlrpc_user_desc_size(ngroups);
+}
+EXPORT_SYMBOL(sptlrpc_current_user_desc_size);
+
+int sptlrpc_pack_user_desc(struct lustre_msg *msg, int offset)
+{
+ struct ptlrpc_user_desc *pud;
+
+ pud = lustre_msg_buf(msg, offset, 0);
+
+ pud->pud_uid = from_kuid(&init_user_ns, current_uid());
+ pud->pud_gid = from_kgid(&init_user_ns, current_gid());
+ pud->pud_fsuid = from_kuid(&init_user_ns, current_fsuid());
+ pud->pud_fsgid = from_kgid(&init_user_ns, current_fsgid());
+ pud->pud_cap = cfs_curproc_cap_pack();
+ pud->pud_ngroups = (msg->lm_buflens[offset] - sizeof(*pud)) / 4;
+
+ task_lock(current);
+ if (pud->pud_ngroups > current_ngroups)
+ pud->pud_ngroups = current_ngroups;
+ memcpy(pud->pud_groups, current_cred()->group_info->blocks[0],
+ pud->pud_ngroups * sizeof(__u32));
+ task_unlock(current);
+
+ return 0;
+}
+EXPORT_SYMBOL(sptlrpc_pack_user_desc);
+
+int sptlrpc_unpack_user_desc(struct lustre_msg *msg, int offset, int swabbed)
+{
+ struct ptlrpc_user_desc *pud;
+ int i;
+
+ pud = lustre_msg_buf(msg, offset, sizeof(*pud));
+ if (!pud)
+ return -EINVAL;
+
+ if (swabbed) {
+ __swab32s(&pud->pud_uid);
+ __swab32s(&pud->pud_gid);
+ __swab32s(&pud->pud_fsuid);
+ __swab32s(&pud->pud_fsgid);
+ __swab32s(&pud->pud_cap);
+ __swab32s(&pud->pud_ngroups);
+ }
+
+ if (pud->pud_ngroups > LUSTRE_MAX_GROUPS) {
+ CERROR("%u groups is too large\n", pud->pud_ngroups);
+ return -EINVAL;
+ }
+
+ if (sizeof(*pud) + pud->pud_ngroups * sizeof(__u32) >
+ msg->lm_buflens[offset]) {
+ CERROR("%u groups are claimed but bufsize only %u\n",
+ pud->pud_ngroups, msg->lm_buflens[offset]);
+ return -EINVAL;
+ }
+
+ if (swabbed) {
+ for (i = 0; i < pud->pud_ngroups; i++)
+ __swab32s(&pud->pud_groups[i]);
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL(sptlrpc_unpack_user_desc);
+
+/****************************************
+ * misc helpers *
+ ****************************************/
+
+const char *sec2target_str(struct ptlrpc_sec *sec)
+{
+ if (!sec || !sec->ps_import || !sec->ps_import->imp_obd)
+ return "*";
+ if (sec_is_reverse(sec))
+ return "c";
+ return obd_uuid2str(&sec->ps_import->imp_obd->u.cli.cl_target_uuid);
+}
+EXPORT_SYMBOL(sec2target_str);
+
+/*
+ * return true if the bulk data is protected
+ */
+int sptlrpc_flavor_has_bulk(struct sptlrpc_flavor *flvr)
+{
+ switch (SPTLRPC_FLVR_BULK_SVC(flvr->sf_rpc)) {
+ case SPTLRPC_BULK_SVC_INTG:
+ case SPTLRPC_BULK_SVC_PRIV:
+ return 1;
+ default:
+ return 0;
+ }
+}
+EXPORT_SYMBOL(sptlrpc_flavor_has_bulk);
+
+/****************************************
+ * crypto API helper/alloc blkciper *
+ ****************************************/
+
+/****************************************
+ * initialize/finalize *
+ ****************************************/
+
+int sptlrpc_init(void)
+{
+ int rc;
+
+ rwlock_init(&policy_lock);
+
+ rc = sptlrpc_gc_init();
+ if (rc)
+ goto out;
+
+ rc = sptlrpc_conf_init();
+ if (rc)
+ goto out_gc;
+
+ rc = sptlrpc_enc_pool_init();
+ if (rc)
+ goto out_conf;
+
+ rc = sptlrpc_null_init();
+ if (rc)
+ goto out_pool;
+
+ rc = sptlrpc_plain_init();
+ if (rc)
+ goto out_null;
+
+ rc = sptlrpc_lproc_init();
+ if (rc)
+ goto out_plain;
+
+ return 0;
+
+out_plain:
+ sptlrpc_plain_fini();
+out_null:
+ sptlrpc_null_fini();
+out_pool:
+ sptlrpc_enc_pool_fini();
+out_conf:
+ sptlrpc_conf_fini();
+out_gc:
+ sptlrpc_gc_fini();
+out:
+ return rc;
+}
+
+void sptlrpc_fini(void)
+{
+ sptlrpc_lproc_fini();
+ sptlrpc_plain_fini();
+ sptlrpc_null_fini();
+ sptlrpc_enc_pool_fini();
+ sptlrpc_conf_fini();
+ sptlrpc_gc_fini();
+}
diff --git a/drivers/staging/lustre/lustre/ptlrpc/sec_bulk.c b/drivers/staging/lustre/lustre/ptlrpc/sec_bulk.c
new file mode 100644
index 000000000..c05a8554d
--- /dev/null
+++ b/drivers/staging/lustre/lustre/ptlrpc/sec_bulk.c
@@ -0,0 +1,884 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2011, 2012, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ *
+ * lustre/ptlrpc/sec_bulk.c
+ *
+ * Author: Eric Mei <ericm@clusterfs.com>
+ */
+
+#define DEBUG_SUBSYSTEM S_SEC
+
+#include "../../include/linux/libcfs/libcfs.h"
+#include <linux/crypto.h>
+
+#include "../include/obd.h"
+#include "../include/obd_cksum.h"
+#include "../include/obd_class.h"
+#include "../include/obd_support.h"
+#include "../include/lustre_net.h"
+#include "../include/lustre_import.h"
+#include "../include/lustre_dlm.h"
+#include "../include/lustre_sec.h"
+
+#include "ptlrpc_internal.h"
+
+/****************************************
+ * bulk encryption page pools *
+ ****************************************/
+
+
+#define POINTERS_PER_PAGE (PAGE_CACHE_SIZE / sizeof(void *))
+#define PAGES_PER_POOL (POINTERS_PER_PAGE)
+
+#define IDLE_IDX_MAX (100)
+#define IDLE_IDX_WEIGHT (3)
+
+#define CACHE_QUIESCENT_PERIOD (20)
+
+static struct ptlrpc_enc_page_pool {
+ /*
+ * constants
+ */
+ unsigned long epp_max_pages; /* maximum pages can hold, const */
+ unsigned int epp_max_pools; /* number of pools, const */
+
+ /*
+ * wait queue in case of not enough free pages.
+ */
+ wait_queue_head_t epp_waitq; /* waiting threads */
+ unsigned int epp_waitqlen; /* wait queue length */
+ unsigned long epp_pages_short; /* # of pages wanted of in-q users */
+ unsigned int epp_growing:1; /* during adding pages */
+
+ /*
+ * indicating how idle the pools are, from 0 to MAX_IDLE_IDX
+ * this is counted based on each time when getting pages from
+ * the pools, not based on time. which means in case that system
+ * is idled for a while but the idle_idx might still be low if no
+ * activities happened in the pools.
+ */
+ unsigned long epp_idle_idx;
+
+ /* last shrink time due to mem tight */
+ long epp_last_shrink;
+ long epp_last_access;
+
+ /*
+ * in-pool pages bookkeeping
+ */
+ spinlock_t epp_lock; /* protect following fields */
+ unsigned long epp_total_pages; /* total pages in pools */
+ unsigned long epp_free_pages; /* current pages available */
+
+ /*
+ * statistics
+ */
+ unsigned long epp_st_max_pages; /* # of pages ever reached */
+ unsigned int epp_st_grows; /* # of grows */
+ unsigned int epp_st_grow_fails; /* # of add pages failures */
+ unsigned int epp_st_shrinks; /* # of shrinks */
+ unsigned long epp_st_access; /* # of access */
+ unsigned long epp_st_missings; /* # of cache missing */
+ unsigned long epp_st_lowfree; /* lowest free pages reached */
+ unsigned int epp_st_max_wqlen; /* highest waitqueue length */
+ unsigned long epp_st_max_wait; /* in jiffies */
+ /*
+ * pointers to pools
+ */
+ struct page ***epp_pools;
+} page_pools;
+
+/*
+ * /proc/fs/lustre/sptlrpc/encrypt_page_pools
+ */
+int sptlrpc_proc_enc_pool_seq_show(struct seq_file *m, void *v)
+{
+ spin_lock(&page_pools.epp_lock);
+
+ seq_printf(m,
+ "physical pages: %lu\n"
+ "pages per pool: %lu\n"
+ "max pages: %lu\n"
+ "max pools: %u\n"
+ "total pages: %lu\n"
+ "total free: %lu\n"
+ "idle index: %lu/100\n"
+ "last shrink: %lds\n"
+ "last access: %lds\n"
+ "max pages reached: %lu\n"
+ "grows: %u\n"
+ "grows failure: %u\n"
+ "shrinks: %u\n"
+ "cache access: %lu\n"
+ "cache missing: %lu\n"
+ "low free mark: %lu\n"
+ "max waitqueue depth: %u\n"
+ "max wait time: " CFS_TIME_T "/%u\n",
+ totalram_pages,
+ PAGES_PER_POOL,
+ page_pools.epp_max_pages,
+ page_pools.epp_max_pools,
+ page_pools.epp_total_pages,
+ page_pools.epp_free_pages,
+ page_pools.epp_idle_idx,
+ get_seconds() - page_pools.epp_last_shrink,
+ get_seconds() - page_pools.epp_last_access,
+ page_pools.epp_st_max_pages,
+ page_pools.epp_st_grows,
+ page_pools.epp_st_grow_fails,
+ page_pools.epp_st_shrinks,
+ page_pools.epp_st_access,
+ page_pools.epp_st_missings,
+ page_pools.epp_st_lowfree,
+ page_pools.epp_st_max_wqlen,
+ page_pools.epp_st_max_wait,
+ HZ);
+
+ spin_unlock(&page_pools.epp_lock);
+
+ return 0;
+}
+
+static void enc_pools_release_free_pages(long npages)
+{
+ int p_idx, g_idx;
+ int p_idx_max1, p_idx_max2;
+
+ LASSERT(npages > 0);
+ LASSERT(npages <= page_pools.epp_free_pages);
+ LASSERT(page_pools.epp_free_pages <= page_pools.epp_total_pages);
+
+ /* max pool index before the release */
+ p_idx_max2 = (page_pools.epp_total_pages - 1) / PAGES_PER_POOL;
+
+ page_pools.epp_free_pages -= npages;
+ page_pools.epp_total_pages -= npages;
+
+ /* max pool index after the release */
+ p_idx_max1 = page_pools.epp_total_pages == 0 ? -1 :
+ ((page_pools.epp_total_pages - 1) / PAGES_PER_POOL);
+
+ p_idx = page_pools.epp_free_pages / PAGES_PER_POOL;
+ g_idx = page_pools.epp_free_pages % PAGES_PER_POOL;
+ LASSERT(page_pools.epp_pools[p_idx]);
+
+ while (npages--) {
+ LASSERT(page_pools.epp_pools[p_idx]);
+ LASSERT(page_pools.epp_pools[p_idx][g_idx] != NULL);
+
+ __free_page(page_pools.epp_pools[p_idx][g_idx]);
+ page_pools.epp_pools[p_idx][g_idx] = NULL;
+
+ if (++g_idx == PAGES_PER_POOL) {
+ p_idx++;
+ g_idx = 0;
+ }
+ }
+
+ /* free unused pools */
+ while (p_idx_max1 < p_idx_max2) {
+ LASSERT(page_pools.epp_pools[p_idx_max2]);
+ OBD_FREE(page_pools.epp_pools[p_idx_max2], PAGE_CACHE_SIZE);
+ page_pools.epp_pools[p_idx_max2] = NULL;
+ p_idx_max2--;
+ }
+}
+
+/*
+ * we try to keep at least PTLRPC_MAX_BRW_PAGES pages in the pool.
+ */
+static unsigned long enc_pools_shrink_count(struct shrinker *s,
+ struct shrink_control *sc)
+{
+ /*
+ * if no pool access for a long time, we consider it's fully idle.
+ * a little race here is fine.
+ */
+ if (unlikely(get_seconds() - page_pools.epp_last_access >
+ CACHE_QUIESCENT_PERIOD)) {
+ spin_lock(&page_pools.epp_lock);
+ page_pools.epp_idle_idx = IDLE_IDX_MAX;
+ spin_unlock(&page_pools.epp_lock);
+ }
+
+ LASSERT(page_pools.epp_idle_idx <= IDLE_IDX_MAX);
+ return max((int)page_pools.epp_free_pages - PTLRPC_MAX_BRW_PAGES, 0) *
+ (IDLE_IDX_MAX - page_pools.epp_idle_idx) / IDLE_IDX_MAX;
+}
+
+/*
+ * we try to keep at least PTLRPC_MAX_BRW_PAGES pages in the pool.
+ */
+static unsigned long enc_pools_shrink_scan(struct shrinker *s,
+ struct shrink_control *sc)
+{
+ spin_lock(&page_pools.epp_lock);
+ sc->nr_to_scan = min_t(unsigned long, sc->nr_to_scan,
+ page_pools.epp_free_pages - PTLRPC_MAX_BRW_PAGES);
+ if (sc->nr_to_scan > 0) {
+ enc_pools_release_free_pages(sc->nr_to_scan);
+ CDEBUG(D_SEC, "released %ld pages, %ld left\n",
+ (long)sc->nr_to_scan, page_pools.epp_free_pages);
+
+ page_pools.epp_st_shrinks++;
+ page_pools.epp_last_shrink = get_seconds();
+ }
+ spin_unlock(&page_pools.epp_lock);
+
+ /*
+ * if no pool access for a long time, we consider it's fully idle.
+ * a little race here is fine.
+ */
+ if (unlikely(get_seconds() - page_pools.epp_last_access >
+ CACHE_QUIESCENT_PERIOD)) {
+ spin_lock(&page_pools.epp_lock);
+ page_pools.epp_idle_idx = IDLE_IDX_MAX;
+ spin_unlock(&page_pools.epp_lock);
+ }
+
+ LASSERT(page_pools.epp_idle_idx <= IDLE_IDX_MAX);
+ return sc->nr_to_scan;
+}
+
+static inline
+int npages_to_npools(unsigned long npages)
+{
+ return (int) ((npages + PAGES_PER_POOL - 1) / PAGES_PER_POOL);
+}
+
+/*
+ * return how many pages cleaned up.
+ */
+static unsigned long enc_pools_cleanup(struct page ***pools, int npools)
+{
+ unsigned long cleaned = 0;
+ int i, j;
+
+ for (i = 0; i < npools; i++) {
+ if (pools[i]) {
+ for (j = 0; j < PAGES_PER_POOL; j++) {
+ if (pools[i][j]) {
+ __free_page(pools[i][j]);
+ cleaned++;
+ }
+ }
+ OBD_FREE(pools[i], PAGE_CACHE_SIZE);
+ pools[i] = NULL;
+ }
+ }
+
+ return cleaned;
+}
+
+/*
+ * merge @npools pointed by @pools which contains @npages new pages
+ * into current pools.
+ *
+ * we have options to avoid most memory copy with some tricks. but we choose
+ * the simplest way to avoid complexity. It's not frequently called.
+ */
+static void enc_pools_insert(struct page ***pools, int npools, int npages)
+{
+ int freeslot;
+ int op_idx, np_idx, og_idx, ng_idx;
+ int cur_npools, end_npools;
+
+ LASSERT(npages > 0);
+ LASSERT(page_pools.epp_total_pages+npages <= page_pools.epp_max_pages);
+ LASSERT(npages_to_npools(npages) == npools);
+ LASSERT(page_pools.epp_growing);
+
+ spin_lock(&page_pools.epp_lock);
+
+ /*
+ * (1) fill all the free slots of current pools.
+ */
+ /* free slots are those left by rent pages, and the extra ones with
+ * index >= total_pages, locate at the tail of last pool. */
+ freeslot = page_pools.epp_total_pages % PAGES_PER_POOL;
+ if (freeslot != 0)
+ freeslot = PAGES_PER_POOL - freeslot;
+ freeslot += page_pools.epp_total_pages - page_pools.epp_free_pages;
+
+ op_idx = page_pools.epp_free_pages / PAGES_PER_POOL;
+ og_idx = page_pools.epp_free_pages % PAGES_PER_POOL;
+ np_idx = npools - 1;
+ ng_idx = (npages - 1) % PAGES_PER_POOL;
+
+ while (freeslot) {
+ LASSERT(page_pools.epp_pools[op_idx][og_idx] == NULL);
+ LASSERT(pools[np_idx][ng_idx] != NULL);
+
+ page_pools.epp_pools[op_idx][og_idx] = pools[np_idx][ng_idx];
+ pools[np_idx][ng_idx] = NULL;
+
+ freeslot--;
+
+ if (++og_idx == PAGES_PER_POOL) {
+ op_idx++;
+ og_idx = 0;
+ }
+ if (--ng_idx < 0) {
+ if (np_idx == 0)
+ break;
+ np_idx--;
+ ng_idx = PAGES_PER_POOL - 1;
+ }
+ }
+
+ /*
+ * (2) add pools if needed.
+ */
+ cur_npools = (page_pools.epp_total_pages + PAGES_PER_POOL - 1) /
+ PAGES_PER_POOL;
+ end_npools = (page_pools.epp_total_pages + npages + PAGES_PER_POOL - 1)
+ / PAGES_PER_POOL;
+ LASSERT(end_npools <= page_pools.epp_max_pools);
+
+ np_idx = 0;
+ while (cur_npools < end_npools) {
+ LASSERT(page_pools.epp_pools[cur_npools] == NULL);
+ LASSERT(np_idx < npools);
+ LASSERT(pools[np_idx] != NULL);
+
+ page_pools.epp_pools[cur_npools++] = pools[np_idx];
+ pools[np_idx++] = NULL;
+ }
+
+ page_pools.epp_total_pages += npages;
+ page_pools.epp_free_pages += npages;
+ page_pools.epp_st_lowfree = page_pools.epp_free_pages;
+
+ if (page_pools.epp_total_pages > page_pools.epp_st_max_pages)
+ page_pools.epp_st_max_pages = page_pools.epp_total_pages;
+
+ CDEBUG(D_SEC, "add %d pages to total %lu\n", npages,
+ page_pools.epp_total_pages);
+
+ spin_unlock(&page_pools.epp_lock);
+}
+
+static int enc_pools_add_pages(int npages)
+{
+ static DEFINE_MUTEX(add_pages_mutex);
+ struct page ***pools;
+ int npools, alloced = 0;
+ int i, j, rc = -ENOMEM;
+
+ if (npages < PTLRPC_MAX_BRW_PAGES)
+ npages = PTLRPC_MAX_BRW_PAGES;
+
+ mutex_lock(&add_pages_mutex);
+
+ if (npages + page_pools.epp_total_pages > page_pools.epp_max_pages)
+ npages = page_pools.epp_max_pages - page_pools.epp_total_pages;
+ LASSERT(npages > 0);
+
+ page_pools.epp_st_grows++;
+
+ npools = npages_to_npools(npages);
+ OBD_ALLOC(pools, npools * sizeof(*pools));
+ if (pools == NULL)
+ goto out;
+
+ for (i = 0; i < npools; i++) {
+ OBD_ALLOC(pools[i], PAGE_CACHE_SIZE);
+ if (pools[i] == NULL)
+ goto out_pools;
+
+ for (j = 0; j < PAGES_PER_POOL && alloced < npages; j++) {
+ pools[i][j] = alloc_page(GFP_NOFS |
+ __GFP_HIGHMEM);
+ if (pools[i][j] == NULL)
+ goto out_pools;
+
+ alloced++;
+ }
+ }
+ LASSERT(alloced == npages);
+
+ enc_pools_insert(pools, npools, npages);
+ CDEBUG(D_SEC, "added %d pages into pools\n", npages);
+ rc = 0;
+
+out_pools:
+ enc_pools_cleanup(pools, npools);
+ OBD_FREE(pools, npools * sizeof(*pools));
+out:
+ if (rc) {
+ page_pools.epp_st_grow_fails++;
+ CERROR("Failed to allocate %d enc pages\n", npages);
+ }
+
+ mutex_unlock(&add_pages_mutex);
+ return rc;
+}
+
+static inline void enc_pools_wakeup(void)
+{
+ assert_spin_locked(&page_pools.epp_lock);
+ LASSERT(page_pools.epp_waitqlen >= 0);
+
+ if (unlikely(page_pools.epp_waitqlen)) {
+ LASSERT(waitqueue_active(&page_pools.epp_waitq));
+ wake_up_all(&page_pools.epp_waitq);
+ }
+}
+
+static int enc_pools_should_grow(int page_needed, long now)
+{
+ /* don't grow if someone else is growing the pools right now,
+ * or the pools has reached its full capacity
+ */
+ if (page_pools.epp_growing ||
+ page_pools.epp_total_pages == page_pools.epp_max_pages)
+ return 0;
+
+ /* if total pages is not enough, we need to grow */
+ if (page_pools.epp_total_pages < page_needed)
+ return 1;
+
+ /*
+ * we wanted to return 0 here if there was a shrink just happened
+ * moment ago, but this may cause deadlock if both client and ost
+ * live on single node.
+ */
+#if 0
+ if (now - page_pools.epp_last_shrink < 2)
+ return 0;
+#endif
+
+ /*
+ * here we perhaps need consider other factors like wait queue
+ * length, idle index, etc. ?
+ */
+
+ /* grow the pools in any other cases */
+ return 1;
+}
+
+/*
+ * we allocate the requested pages atomically.
+ */
+int sptlrpc_enc_pool_get_pages(struct ptlrpc_bulk_desc *desc)
+{
+ wait_queue_t waitlink;
+ unsigned long this_idle = -1;
+ unsigned long tick = 0;
+ long now;
+ int p_idx, g_idx;
+ int i;
+
+ LASSERT(desc->bd_iov_count > 0);
+ LASSERT(desc->bd_iov_count <= page_pools.epp_max_pages);
+
+ /* resent bulk, enc iov might have been allocated previously */
+ if (desc->bd_enc_iov != NULL)
+ return 0;
+
+ OBD_ALLOC(desc->bd_enc_iov,
+ desc->bd_iov_count * sizeof(*desc->bd_enc_iov));
+ if (desc->bd_enc_iov == NULL)
+ return -ENOMEM;
+
+ spin_lock(&page_pools.epp_lock);
+
+ page_pools.epp_st_access++;
+again:
+ if (unlikely(page_pools.epp_free_pages < desc->bd_iov_count)) {
+ if (tick == 0)
+ tick = cfs_time_current();
+
+ now = get_seconds();
+
+ page_pools.epp_st_missings++;
+ page_pools.epp_pages_short += desc->bd_iov_count;
+
+ if (enc_pools_should_grow(desc->bd_iov_count, now)) {
+ page_pools.epp_growing = 1;
+
+ spin_unlock(&page_pools.epp_lock);
+ enc_pools_add_pages(page_pools.epp_pages_short / 2);
+ spin_lock(&page_pools.epp_lock);
+
+ page_pools.epp_growing = 0;
+
+ enc_pools_wakeup();
+ } else {
+ if (++page_pools.epp_waitqlen >
+ page_pools.epp_st_max_wqlen)
+ page_pools.epp_st_max_wqlen =
+ page_pools.epp_waitqlen;
+
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ init_waitqueue_entry(&waitlink, current);
+ add_wait_queue(&page_pools.epp_waitq, &waitlink);
+
+ spin_unlock(&page_pools.epp_lock);
+ schedule();
+ remove_wait_queue(&page_pools.epp_waitq, &waitlink);
+ LASSERT(page_pools.epp_waitqlen > 0);
+ spin_lock(&page_pools.epp_lock);
+ page_pools.epp_waitqlen--;
+ }
+
+ LASSERT(page_pools.epp_pages_short >= desc->bd_iov_count);
+ page_pools.epp_pages_short -= desc->bd_iov_count;
+
+ this_idle = 0;
+ goto again;
+ }
+
+ /* record max wait time */
+ if (unlikely(tick != 0)) {
+ tick = cfs_time_current() - tick;
+ if (tick > page_pools.epp_st_max_wait)
+ page_pools.epp_st_max_wait = tick;
+ }
+
+ /* proceed with rest of allocation */
+ page_pools.epp_free_pages -= desc->bd_iov_count;
+
+ p_idx = page_pools.epp_free_pages / PAGES_PER_POOL;
+ g_idx = page_pools.epp_free_pages % PAGES_PER_POOL;
+
+ for (i = 0; i < desc->bd_iov_count; i++) {
+ LASSERT(page_pools.epp_pools[p_idx][g_idx] != NULL);
+ desc->bd_enc_iov[i].kiov_page =
+ page_pools.epp_pools[p_idx][g_idx];
+ page_pools.epp_pools[p_idx][g_idx] = NULL;
+
+ if (++g_idx == PAGES_PER_POOL) {
+ p_idx++;
+ g_idx = 0;
+ }
+ }
+
+ if (page_pools.epp_free_pages < page_pools.epp_st_lowfree)
+ page_pools.epp_st_lowfree = page_pools.epp_free_pages;
+
+ /*
+ * new idle index = (old * weight + new) / (weight + 1)
+ */
+ if (this_idle == -1) {
+ this_idle = page_pools.epp_free_pages * IDLE_IDX_MAX /
+ page_pools.epp_total_pages;
+ }
+ page_pools.epp_idle_idx = (page_pools.epp_idle_idx * IDLE_IDX_WEIGHT +
+ this_idle) /
+ (IDLE_IDX_WEIGHT + 1);
+
+ page_pools.epp_last_access = get_seconds();
+
+ spin_unlock(&page_pools.epp_lock);
+ return 0;
+}
+EXPORT_SYMBOL(sptlrpc_enc_pool_get_pages);
+
+void sptlrpc_enc_pool_put_pages(struct ptlrpc_bulk_desc *desc)
+{
+ int p_idx, g_idx;
+ int i;
+
+ if (desc->bd_enc_iov == NULL)
+ return;
+
+ LASSERT(desc->bd_iov_count > 0);
+
+ spin_lock(&page_pools.epp_lock);
+
+ p_idx = page_pools.epp_free_pages / PAGES_PER_POOL;
+ g_idx = page_pools.epp_free_pages % PAGES_PER_POOL;
+
+ LASSERT(page_pools.epp_free_pages + desc->bd_iov_count <=
+ page_pools.epp_total_pages);
+ LASSERT(page_pools.epp_pools[p_idx]);
+
+ for (i = 0; i < desc->bd_iov_count; i++) {
+ LASSERT(desc->bd_enc_iov[i].kiov_page != NULL);
+ LASSERT(g_idx != 0 || page_pools.epp_pools[p_idx]);
+ LASSERT(page_pools.epp_pools[p_idx][g_idx] == NULL);
+
+ page_pools.epp_pools[p_idx][g_idx] =
+ desc->bd_enc_iov[i].kiov_page;
+
+ if (++g_idx == PAGES_PER_POOL) {
+ p_idx++;
+ g_idx = 0;
+ }
+ }
+
+ page_pools.epp_free_pages += desc->bd_iov_count;
+
+ enc_pools_wakeup();
+
+ spin_unlock(&page_pools.epp_lock);
+
+ OBD_FREE(desc->bd_enc_iov,
+ desc->bd_iov_count * sizeof(*desc->bd_enc_iov));
+ desc->bd_enc_iov = NULL;
+}
+EXPORT_SYMBOL(sptlrpc_enc_pool_put_pages);
+
+/*
+ * we don't do much stuff for add_user/del_user anymore, except adding some
+ * initial pages in add_user() if current pools are empty, rest would be
+ * handled by the pools's self-adaption.
+ */
+int sptlrpc_enc_pool_add_user(void)
+{
+ int need_grow = 0;
+
+ spin_lock(&page_pools.epp_lock);
+ if (page_pools.epp_growing == 0 && page_pools.epp_total_pages == 0) {
+ page_pools.epp_growing = 1;
+ need_grow = 1;
+ }
+ spin_unlock(&page_pools.epp_lock);
+
+ if (need_grow) {
+ enc_pools_add_pages(PTLRPC_MAX_BRW_PAGES +
+ PTLRPC_MAX_BRW_PAGES);
+
+ spin_lock(&page_pools.epp_lock);
+ page_pools.epp_growing = 0;
+ enc_pools_wakeup();
+ spin_unlock(&page_pools.epp_lock);
+ }
+ return 0;
+}
+EXPORT_SYMBOL(sptlrpc_enc_pool_add_user);
+
+int sptlrpc_enc_pool_del_user(void)
+{
+ return 0;
+}
+EXPORT_SYMBOL(sptlrpc_enc_pool_del_user);
+
+static inline void enc_pools_alloc(void)
+{
+ LASSERT(page_pools.epp_max_pools);
+ OBD_ALLOC_LARGE(page_pools.epp_pools,
+ page_pools.epp_max_pools *
+ sizeof(*page_pools.epp_pools));
+}
+
+static inline void enc_pools_free(void)
+{
+ LASSERT(page_pools.epp_max_pools);
+ LASSERT(page_pools.epp_pools);
+
+ OBD_FREE_LARGE(page_pools.epp_pools,
+ page_pools.epp_max_pools *
+ sizeof(*page_pools.epp_pools));
+}
+
+static struct shrinker pools_shrinker = {
+ .count_objects = enc_pools_shrink_count,
+ .scan_objects = enc_pools_shrink_scan,
+ .seeks = DEFAULT_SEEKS,
+};
+
+int sptlrpc_enc_pool_init(void)
+{
+ /*
+ * maximum capacity is 1/8 of total physical memory.
+ * is the 1/8 a good number?
+ */
+ page_pools.epp_max_pages = totalram_pages / 8;
+ page_pools.epp_max_pools = npages_to_npools(page_pools.epp_max_pages);
+
+ init_waitqueue_head(&page_pools.epp_waitq);
+ page_pools.epp_waitqlen = 0;
+ page_pools.epp_pages_short = 0;
+
+ page_pools.epp_growing = 0;
+
+ page_pools.epp_idle_idx = 0;
+ page_pools.epp_last_shrink = get_seconds();
+ page_pools.epp_last_access = get_seconds();
+
+ spin_lock_init(&page_pools.epp_lock);
+ page_pools.epp_total_pages = 0;
+ page_pools.epp_free_pages = 0;
+
+ page_pools.epp_st_max_pages = 0;
+ page_pools.epp_st_grows = 0;
+ page_pools.epp_st_grow_fails = 0;
+ page_pools.epp_st_shrinks = 0;
+ page_pools.epp_st_access = 0;
+ page_pools.epp_st_missings = 0;
+ page_pools.epp_st_lowfree = 0;
+ page_pools.epp_st_max_wqlen = 0;
+ page_pools.epp_st_max_wait = 0;
+
+ enc_pools_alloc();
+ if (page_pools.epp_pools == NULL)
+ return -ENOMEM;
+
+ register_shrinker(&pools_shrinker);
+
+ return 0;
+}
+
+void sptlrpc_enc_pool_fini(void)
+{
+ unsigned long cleaned, npools;
+
+ LASSERT(page_pools.epp_pools);
+ LASSERT(page_pools.epp_total_pages == page_pools.epp_free_pages);
+
+ unregister_shrinker(&pools_shrinker);
+
+ npools = npages_to_npools(page_pools.epp_total_pages);
+ cleaned = enc_pools_cleanup(page_pools.epp_pools, npools);
+ LASSERT(cleaned == page_pools.epp_total_pages);
+
+ enc_pools_free();
+
+ if (page_pools.epp_st_access > 0) {
+ CDEBUG(D_SEC,
+ "max pages %lu, grows %u, grow fails %u, shrinks %u, access %lu, missing %lu, max qlen %u, max wait "
+ CFS_TIME_T"/%d\n",
+ page_pools.epp_st_max_pages, page_pools.epp_st_grows,
+ page_pools.epp_st_grow_fails,
+ page_pools.epp_st_shrinks, page_pools.epp_st_access,
+ page_pools.epp_st_missings, page_pools.epp_st_max_wqlen,
+ page_pools.epp_st_max_wait, HZ);
+ }
+}
+
+
+static int cfs_hash_alg_id[] = {
+ [BULK_HASH_ALG_NULL] = CFS_HASH_ALG_NULL,
+ [BULK_HASH_ALG_ADLER32] = CFS_HASH_ALG_ADLER32,
+ [BULK_HASH_ALG_CRC32] = CFS_HASH_ALG_CRC32,
+ [BULK_HASH_ALG_MD5] = CFS_HASH_ALG_MD5,
+ [BULK_HASH_ALG_SHA1] = CFS_HASH_ALG_SHA1,
+ [BULK_HASH_ALG_SHA256] = CFS_HASH_ALG_SHA256,
+ [BULK_HASH_ALG_SHA384] = CFS_HASH_ALG_SHA384,
+ [BULK_HASH_ALG_SHA512] = CFS_HASH_ALG_SHA512,
+};
+const char *sptlrpc_get_hash_name(__u8 hash_alg)
+{
+ return cfs_crypto_hash_name(cfs_hash_alg_id[hash_alg]);
+}
+EXPORT_SYMBOL(sptlrpc_get_hash_name);
+
+__u8 sptlrpc_get_hash_alg(const char *algname)
+{
+ return cfs_crypto_hash_alg(algname);
+}
+EXPORT_SYMBOL(sptlrpc_get_hash_alg);
+
+int bulk_sec_desc_unpack(struct lustre_msg *msg, int offset, int swabbed)
+{
+ struct ptlrpc_bulk_sec_desc *bsd;
+ int size = msg->lm_buflens[offset];
+
+ bsd = lustre_msg_buf(msg, offset, sizeof(*bsd));
+ if (bsd == NULL) {
+ CERROR("Invalid bulk sec desc: size %d\n", size);
+ return -EINVAL;
+ }
+
+ if (swabbed)
+ __swab32s(&bsd->bsd_nob);
+
+ if (unlikely(bsd->bsd_version != 0)) {
+ CERROR("Unexpected version %u\n", bsd->bsd_version);
+ return -EPROTO;
+ }
+
+ if (unlikely(bsd->bsd_type >= SPTLRPC_BULK_MAX)) {
+ CERROR("Invalid type %u\n", bsd->bsd_type);
+ return -EPROTO;
+ }
+
+ /* FIXME more sanity check here */
+
+ if (unlikely(bsd->bsd_svc != SPTLRPC_BULK_SVC_NULL &&
+ bsd->bsd_svc != SPTLRPC_BULK_SVC_INTG &&
+ bsd->bsd_svc != SPTLRPC_BULK_SVC_PRIV)) {
+ CERROR("Invalid svc %u\n", bsd->bsd_svc);
+ return -EPROTO;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL(bulk_sec_desc_unpack);
+
+int sptlrpc_get_bulk_checksum(struct ptlrpc_bulk_desc *desc, __u8 alg,
+ void *buf, int buflen)
+{
+ struct cfs_crypto_hash_desc *hdesc;
+ int hashsize;
+ char hashbuf[64];
+ unsigned int bufsize;
+ int i, err;
+
+ LASSERT(alg > BULK_HASH_ALG_NULL && alg < BULK_HASH_ALG_MAX);
+ LASSERT(buflen >= 4);
+
+ hdesc = cfs_crypto_hash_init(cfs_hash_alg_id[alg], NULL, 0);
+ if (IS_ERR(hdesc)) {
+ CERROR("Unable to initialize checksum hash %s\n",
+ cfs_crypto_hash_name(cfs_hash_alg_id[alg]));
+ return PTR_ERR(hdesc);
+ }
+
+ hashsize = cfs_crypto_hash_digestsize(cfs_hash_alg_id[alg]);
+
+ for (i = 0; i < desc->bd_iov_count; i++) {
+ cfs_crypto_hash_update_page(hdesc, desc->bd_iov[i].kiov_page,
+ desc->bd_iov[i].kiov_offset & ~CFS_PAGE_MASK,
+ desc->bd_iov[i].kiov_len);
+ }
+ if (hashsize > buflen) {
+ bufsize = sizeof(hashbuf);
+ err = cfs_crypto_hash_final(hdesc, (unsigned char *)hashbuf,
+ &bufsize);
+ memcpy(buf, hashbuf, buflen);
+ } else {
+ bufsize = buflen;
+ err = cfs_crypto_hash_final(hdesc, (unsigned char *)buf,
+ &bufsize);
+ }
+
+ if (err)
+ cfs_crypto_hash_final(hdesc, NULL, NULL);
+ return err;
+}
+EXPORT_SYMBOL(sptlrpc_get_bulk_checksum);
diff --git a/drivers/staging/lustre/lustre/ptlrpc/sec_config.c b/drivers/staging/lustre/lustre/ptlrpc/sec_config.c
new file mode 100644
index 000000000..56ba9e4e5
--- /dev/null
+++ b/drivers/staging/lustre/lustre/ptlrpc/sec_config.c
@@ -0,0 +1,901 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2011, 2012, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ */
+
+#define DEBUG_SUBSYSTEM S_SEC
+
+#include "../../include/linux/libcfs/libcfs.h"
+#include <linux/crypto.h>
+#include <linux/key.h>
+
+#include "../include/obd.h"
+#include "../include/obd_support.h"
+#include "../include/lustre_import.h"
+#include "../include/lustre_param.h"
+#include "../include/lustre_sec.h"
+
+#include "ptlrpc_internal.h"
+
+const char *sptlrpc_part2name(enum lustre_sec_part part)
+{
+ switch (part) {
+ case LUSTRE_SP_CLI:
+ return "cli";
+ case LUSTRE_SP_MDT:
+ return "mdt";
+ case LUSTRE_SP_OST:
+ return "ost";
+ case LUSTRE_SP_MGC:
+ return "mgc";
+ case LUSTRE_SP_MGS:
+ return "mgs";
+ case LUSTRE_SP_ANY:
+ return "any";
+ default:
+ return "err";
+ }
+}
+EXPORT_SYMBOL(sptlrpc_part2name);
+
+enum lustre_sec_part sptlrpc_target_sec_part(struct obd_device *obd)
+{
+ const char *type = obd->obd_type->typ_name;
+
+ if (!strcmp(type, LUSTRE_MDT_NAME))
+ return LUSTRE_SP_MDT;
+ if (!strcmp(type, LUSTRE_OST_NAME))
+ return LUSTRE_SP_OST;
+ if (!strcmp(type, LUSTRE_MGS_NAME))
+ return LUSTRE_SP_MGS;
+
+ CERROR("unknown target %p(%s)\n", obd, type);
+ return LUSTRE_SP_ANY;
+}
+EXPORT_SYMBOL(sptlrpc_target_sec_part);
+
+/****************************************
+ * user supplied flavor string parsing *
+ ****************************************/
+
+/*
+ * format: <base_flavor>[-<bulk_type:alg_spec>]
+ */
+int sptlrpc_parse_flavor(const char *str, struct sptlrpc_flavor *flvr)
+{
+ char buf[32];
+ char *bulk, *alg;
+
+ memset(flvr, 0, sizeof(*flvr));
+
+ if (str == NULL || str[0] == '\0') {
+ flvr->sf_rpc = SPTLRPC_FLVR_INVALID;
+ return 0;
+ }
+
+ strncpy(buf, str, sizeof(buf));
+ buf[sizeof(buf) - 1] = '\0';
+
+ bulk = strchr(buf, '-');
+ if (bulk)
+ *bulk++ = '\0';
+
+ flvr->sf_rpc = sptlrpc_name2flavor_base(buf);
+ if (flvr->sf_rpc == SPTLRPC_FLVR_INVALID)
+ goto err_out;
+
+ /*
+ * currently only base flavor "plain" can have bulk specification.
+ */
+ if (flvr->sf_rpc == SPTLRPC_FLVR_PLAIN) {
+ flvr->u_bulk.hash.hash_alg = BULK_HASH_ALG_ADLER32;
+ if (bulk) {
+ /*
+ * format: plain-hash:<hash_alg>
+ */
+ alg = strchr(bulk, ':');
+ if (alg == NULL)
+ goto err_out;
+ *alg++ = '\0';
+
+ if (strcmp(bulk, "hash"))
+ goto err_out;
+
+ flvr->u_bulk.hash.hash_alg = sptlrpc_get_hash_alg(alg);
+ if (flvr->u_bulk.hash.hash_alg >= BULK_HASH_ALG_MAX)
+ goto err_out;
+ }
+
+ if (flvr->u_bulk.hash.hash_alg == BULK_HASH_ALG_NULL)
+ flvr_set_bulk_svc(&flvr->sf_rpc, SPTLRPC_BULK_SVC_NULL);
+ else
+ flvr_set_bulk_svc(&flvr->sf_rpc, SPTLRPC_BULK_SVC_INTG);
+ } else {
+ if (bulk)
+ goto err_out;
+ }
+
+ flvr->sf_flags = 0;
+ return 0;
+
+err_out:
+ CERROR("invalid flavor string: %s\n", str);
+ return -EINVAL;
+}
+EXPORT_SYMBOL(sptlrpc_parse_flavor);
+
+/****************************************
+ * configure rules *
+ ****************************************/
+
+static void get_default_flavor(struct sptlrpc_flavor *sf)
+{
+ memset(sf, 0, sizeof(*sf));
+
+ sf->sf_rpc = SPTLRPC_FLVR_NULL;
+ sf->sf_flags = 0;
+}
+
+static void sptlrpc_rule_init(struct sptlrpc_rule *rule)
+{
+ rule->sr_netid = LNET_NIDNET(LNET_NID_ANY);
+ rule->sr_from = LUSTRE_SP_ANY;
+ rule->sr_to = LUSTRE_SP_ANY;
+ rule->sr_padding = 0;
+
+ get_default_flavor(&rule->sr_flvr);
+}
+
+/*
+ * format: network[.direction]=flavor
+ */
+int sptlrpc_parse_rule(char *param, struct sptlrpc_rule *rule)
+{
+ char *flavor, *dir;
+ int rc;
+
+ sptlrpc_rule_init(rule);
+
+ flavor = strchr(param, '=');
+ if (flavor == NULL) {
+ CERROR("invalid param, no '='\n");
+ return -EINVAL;
+ }
+ *flavor++ = '\0';
+
+ dir = strchr(param, '.');
+ if (dir)
+ *dir++ = '\0';
+
+ /* 1.1 network */
+ if (strcmp(param, "default")) {
+ rule->sr_netid = libcfs_str2net(param);
+ if (rule->sr_netid == LNET_NIDNET(LNET_NID_ANY)) {
+ CERROR("invalid network name: %s\n", param);
+ return -EINVAL;
+ }
+ }
+
+ /* 1.2 direction */
+ if (dir) {
+ if (!strcmp(dir, "mdt2ost")) {
+ rule->sr_from = LUSTRE_SP_MDT;
+ rule->sr_to = LUSTRE_SP_OST;
+ } else if (!strcmp(dir, "mdt2mdt")) {
+ rule->sr_from = LUSTRE_SP_MDT;
+ rule->sr_to = LUSTRE_SP_MDT;
+ } else if (!strcmp(dir, "cli2ost")) {
+ rule->sr_from = LUSTRE_SP_CLI;
+ rule->sr_to = LUSTRE_SP_OST;
+ } else if (!strcmp(dir, "cli2mdt")) {
+ rule->sr_from = LUSTRE_SP_CLI;
+ rule->sr_to = LUSTRE_SP_MDT;
+ } else {
+ CERROR("invalid rule dir segment: %s\n", dir);
+ return -EINVAL;
+ }
+ }
+
+ /* 2.1 flavor */
+ rc = sptlrpc_parse_flavor(flavor, &rule->sr_flvr);
+ if (rc)
+ return -EINVAL;
+
+ return 0;
+}
+EXPORT_SYMBOL(sptlrpc_parse_rule);
+
+void sptlrpc_rule_set_free(struct sptlrpc_rule_set *rset)
+{
+ LASSERT(rset->srs_nslot ||
+ (rset->srs_nrule == 0 && rset->srs_rules == NULL));
+
+ if (rset->srs_nslot) {
+ OBD_FREE(rset->srs_rules,
+ rset->srs_nslot * sizeof(*rset->srs_rules));
+ sptlrpc_rule_set_init(rset);
+ }
+}
+EXPORT_SYMBOL(sptlrpc_rule_set_free);
+
+/*
+ * return 0 if the rule set could accommodate one more rule.
+ */
+int sptlrpc_rule_set_expand(struct sptlrpc_rule_set *rset)
+{
+ struct sptlrpc_rule *rules;
+ int nslot;
+
+ might_sleep();
+
+ if (rset->srs_nrule < rset->srs_nslot)
+ return 0;
+
+ nslot = rset->srs_nslot + 8;
+
+ /* better use realloc() if available */
+ OBD_ALLOC(rules, nslot * sizeof(*rset->srs_rules));
+ if (rules == NULL)
+ return -ENOMEM;
+
+ if (rset->srs_nrule) {
+ LASSERT(rset->srs_nslot && rset->srs_rules);
+ memcpy(rules, rset->srs_rules,
+ rset->srs_nrule * sizeof(*rset->srs_rules));
+
+ OBD_FREE(rset->srs_rules,
+ rset->srs_nslot * sizeof(*rset->srs_rules));
+ }
+
+ rset->srs_rules = rules;
+ rset->srs_nslot = nslot;
+ return 0;
+}
+EXPORT_SYMBOL(sptlrpc_rule_set_expand);
+
+static inline int rule_spec_dir(struct sptlrpc_rule *rule)
+{
+ return (rule->sr_from != LUSTRE_SP_ANY ||
+ rule->sr_to != LUSTRE_SP_ANY);
+}
+static inline int rule_spec_net(struct sptlrpc_rule *rule)
+{
+ return (rule->sr_netid != LNET_NIDNET(LNET_NID_ANY));
+}
+static inline int rule_match_dir(struct sptlrpc_rule *r1,
+ struct sptlrpc_rule *r2)
+{
+ return (r1->sr_from == r2->sr_from && r1->sr_to == r2->sr_to);
+}
+static inline int rule_match_net(struct sptlrpc_rule *r1,
+ struct sptlrpc_rule *r2)
+{
+ return (r1->sr_netid == r2->sr_netid);
+}
+
+/*
+ * merge @rule into @rset.
+ * the @rset slots might be expanded.
+ */
+int sptlrpc_rule_set_merge(struct sptlrpc_rule_set *rset,
+ struct sptlrpc_rule *rule)
+{
+ struct sptlrpc_rule *p = rset->srs_rules;
+ int spec_dir, spec_net;
+ int rc, n, match = 0;
+
+ might_sleep();
+
+ spec_net = rule_spec_net(rule);
+ spec_dir = rule_spec_dir(rule);
+
+ for (n = 0; n < rset->srs_nrule; n++) {
+ p = &rset->srs_rules[n];
+
+ /* test network match, if failed:
+ * - spec rule: skip rules which is also spec rule match, until
+ * we hit a wild rule, which means no more chance
+ * - wild rule: skip until reach the one which is also wild
+ * and matches
+ */
+ if (!rule_match_net(p, rule)) {
+ if (spec_net) {
+ if (rule_spec_net(p))
+ continue;
+ else
+ break;
+ } else {
+ continue;
+ }
+ }
+
+ /* test dir match, same logic as net matching */
+ if (!rule_match_dir(p, rule)) {
+ if (spec_dir) {
+ if (rule_spec_dir(p))
+ continue;
+ else
+ break;
+ } else {
+ continue;
+ }
+ }
+
+ /* find a match */
+ match = 1;
+ break;
+ }
+
+ if (match) {
+ LASSERT(n >= 0 && n < rset->srs_nrule);
+
+ if (rule->sr_flvr.sf_rpc == SPTLRPC_FLVR_INVALID) {
+ /* remove this rule */
+ if (n < rset->srs_nrule - 1)
+ memmove(&rset->srs_rules[n],
+ &rset->srs_rules[n + 1],
+ (rset->srs_nrule - n - 1) *
+ sizeof(*rule));
+ rset->srs_nrule--;
+ } else {
+ /* override the rule */
+ memcpy(&rset->srs_rules[n], rule, sizeof(*rule));
+ }
+ } else {
+ LASSERT(n >= 0 && n <= rset->srs_nrule);
+
+ if (rule->sr_flvr.sf_rpc != SPTLRPC_FLVR_INVALID) {
+ rc = sptlrpc_rule_set_expand(rset);
+ if (rc)
+ return rc;
+
+ if (n < rset->srs_nrule)
+ memmove(&rset->srs_rules[n + 1],
+ &rset->srs_rules[n],
+ (rset->srs_nrule - n) * sizeof(*rule));
+ memcpy(&rset->srs_rules[n], rule, sizeof(*rule));
+ rset->srs_nrule++;
+ } else {
+ CDEBUG(D_CONFIG, "ignore the unmatched deletion\n");
+ }
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL(sptlrpc_rule_set_merge);
+
+/**
+ * given from/to/nid, determine a matching flavor in ruleset.
+ * return 1 if a match found, otherwise return 0.
+ */
+int sptlrpc_rule_set_choose(struct sptlrpc_rule_set *rset,
+ enum lustre_sec_part from,
+ enum lustre_sec_part to,
+ lnet_nid_t nid,
+ struct sptlrpc_flavor *sf)
+{
+ struct sptlrpc_rule *r;
+ int n;
+
+ for (n = 0; n < rset->srs_nrule; n++) {
+ r = &rset->srs_rules[n];
+
+ if (LNET_NIDNET(nid) != LNET_NIDNET(LNET_NID_ANY) &&
+ r->sr_netid != LNET_NIDNET(LNET_NID_ANY) &&
+ LNET_NIDNET(nid) != r->sr_netid)
+ continue;
+
+ if (from != LUSTRE_SP_ANY && r->sr_from != LUSTRE_SP_ANY &&
+ from != r->sr_from)
+ continue;
+
+ if (to != LUSTRE_SP_ANY && r->sr_to != LUSTRE_SP_ANY &&
+ to != r->sr_to)
+ continue;
+
+ *sf = r->sr_flvr;
+ return 1;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL(sptlrpc_rule_set_choose);
+
+void sptlrpc_rule_set_dump(struct sptlrpc_rule_set *rset)
+{
+ struct sptlrpc_rule *r;
+ int n;
+
+ for (n = 0; n < rset->srs_nrule; n++) {
+ r = &rset->srs_rules[n];
+ CDEBUG(D_SEC, "<%02d> from %x to %x, net %x, rpc %x\n", n,
+ r->sr_from, r->sr_to, r->sr_netid, r->sr_flvr.sf_rpc);
+ }
+}
+EXPORT_SYMBOL(sptlrpc_rule_set_dump);
+
+/**********************************
+ * sptlrpc configuration support *
+ **********************************/
+
+struct sptlrpc_conf_tgt {
+ struct list_head sct_list;
+ char sct_name[MAX_OBD_NAME];
+ struct sptlrpc_rule_set sct_rset;
+};
+
+struct sptlrpc_conf {
+ struct list_head sc_list;
+ char sc_fsname[MTI_NAME_MAXLEN];
+ unsigned int sc_modified; /* modified during updating */
+ unsigned int sc_updated:1, /* updated copy from MGS */
+ sc_local:1; /* local copy from target */
+ struct sptlrpc_rule_set sc_rset; /* fs general rules */
+ struct list_head sc_tgts; /* target-specific rules */
+};
+
+static struct mutex sptlrpc_conf_lock;
+static LIST_HEAD(sptlrpc_confs);
+
+static inline int is_hex(char c)
+{
+ return ((c >= '0' && c <= '9') ||
+ (c >= 'a' && c <= 'f'));
+}
+
+static void target2fsname(const char *tgt, char *fsname, int buflen)
+{
+ const char *ptr;
+ int len;
+
+ ptr = strrchr(tgt, '-');
+ if (ptr) {
+ if ((strncmp(ptr, "-MDT", 4) != 0 &&
+ strncmp(ptr, "-OST", 4) != 0) ||
+ !is_hex(ptr[4]) || !is_hex(ptr[5]) ||
+ !is_hex(ptr[6]) || !is_hex(ptr[7]))
+ ptr = NULL;
+ }
+
+ /* if we didn't find the pattern, treat the whole string as fsname */
+ if (ptr == NULL)
+ len = strlen(tgt);
+ else
+ len = ptr - tgt;
+
+ len = min(len, buflen - 1);
+ memcpy(fsname, tgt, len);
+ fsname[len] = '\0';
+}
+
+static void sptlrpc_conf_free_rsets(struct sptlrpc_conf *conf)
+{
+ struct sptlrpc_conf_tgt *conf_tgt, *conf_tgt_next;
+
+ sptlrpc_rule_set_free(&conf->sc_rset);
+
+ list_for_each_entry_safe(conf_tgt, conf_tgt_next,
+ &conf->sc_tgts, sct_list) {
+ sptlrpc_rule_set_free(&conf_tgt->sct_rset);
+ list_del(&conf_tgt->sct_list);
+ OBD_FREE_PTR(conf_tgt);
+ }
+ LASSERT(list_empty(&conf->sc_tgts));
+
+ conf->sc_updated = 0;
+ conf->sc_local = 0;
+}
+
+static void sptlrpc_conf_free(struct sptlrpc_conf *conf)
+{
+ CDEBUG(D_SEC, "free sptlrpc conf %s\n", conf->sc_fsname);
+
+ sptlrpc_conf_free_rsets(conf);
+ list_del(&conf->sc_list);
+ OBD_FREE_PTR(conf);
+}
+
+static
+struct sptlrpc_conf_tgt *sptlrpc_conf_get_tgt(struct sptlrpc_conf *conf,
+ const char *name,
+ int create)
+{
+ struct sptlrpc_conf_tgt *conf_tgt;
+
+ list_for_each_entry(conf_tgt, &conf->sc_tgts, sct_list) {
+ if (strcmp(conf_tgt->sct_name, name) == 0)
+ return conf_tgt;
+ }
+
+ if (!create)
+ return NULL;
+
+ OBD_ALLOC_PTR(conf_tgt);
+ if (conf_tgt) {
+ strlcpy(conf_tgt->sct_name, name, sizeof(conf_tgt->sct_name));
+ sptlrpc_rule_set_init(&conf_tgt->sct_rset);
+ list_add(&conf_tgt->sct_list, &conf->sc_tgts);
+ }
+
+ return conf_tgt;
+}
+
+static
+struct sptlrpc_conf *sptlrpc_conf_get(const char *fsname,
+ int create)
+{
+ struct sptlrpc_conf *conf;
+
+ list_for_each_entry(conf, &sptlrpc_confs, sc_list) {
+ if (strcmp(conf->sc_fsname, fsname) == 0)
+ return conf;
+ }
+
+ if (!create)
+ return NULL;
+
+ OBD_ALLOC_PTR(conf);
+ if (conf == NULL)
+ return NULL;
+
+ strcpy(conf->sc_fsname, fsname);
+ sptlrpc_rule_set_init(&conf->sc_rset);
+ INIT_LIST_HEAD(&conf->sc_tgts);
+ list_add(&conf->sc_list, &sptlrpc_confs);
+
+ CDEBUG(D_SEC, "create sptlrpc conf %s\n", conf->sc_fsname);
+ return conf;
+}
+
+/**
+ * caller must hold conf_lock already.
+ */
+static int sptlrpc_conf_merge_rule(struct sptlrpc_conf *conf,
+ const char *target,
+ struct sptlrpc_rule *rule)
+{
+ struct sptlrpc_conf_tgt *conf_tgt;
+ struct sptlrpc_rule_set *rule_set;
+
+ /* fsname == target means general rules for the whole fs */
+ if (strcmp(conf->sc_fsname, target) == 0) {
+ rule_set = &conf->sc_rset;
+ } else {
+ conf_tgt = sptlrpc_conf_get_tgt(conf, target, 1);
+ if (conf_tgt) {
+ rule_set = &conf_tgt->sct_rset;
+ } else {
+ CERROR("out of memory, can't merge rule!\n");
+ return -ENOMEM;
+ }
+ }
+
+ return sptlrpc_rule_set_merge(rule_set, rule);
+}
+
+/**
+ * process one LCFG_SPTLRPC_CONF record. if \a conf is NULL, we
+ * find one through the target name in the record inside conf_lock;
+ * otherwise means caller already hold conf_lock.
+ */
+static int __sptlrpc_process_config(struct lustre_cfg *lcfg,
+ struct sptlrpc_conf *conf)
+{
+ char *target, *param;
+ char fsname[MTI_NAME_MAXLEN];
+ struct sptlrpc_rule rule;
+ int rc;
+
+ target = lustre_cfg_string(lcfg, 1);
+ if (target == NULL) {
+ CERROR("missing target name\n");
+ return -EINVAL;
+ }
+
+ param = lustre_cfg_string(lcfg, 2);
+ if (param == NULL) {
+ CERROR("missing parameter\n");
+ return -EINVAL;
+ }
+
+ CDEBUG(D_SEC, "processing rule: %s.%s\n", target, param);
+
+ /* parse rule to make sure the format is correct */
+ if (strncmp(param, PARAM_SRPC_FLVR, sizeof(PARAM_SRPC_FLVR) - 1) != 0) {
+ CERROR("Invalid sptlrpc parameter: %s\n", param);
+ return -EINVAL;
+ }
+ param += sizeof(PARAM_SRPC_FLVR) - 1;
+
+ rc = sptlrpc_parse_rule(param, &rule);
+ if (rc)
+ return -EINVAL;
+
+ if (conf == NULL) {
+ target2fsname(target, fsname, sizeof(fsname));
+
+ mutex_lock(&sptlrpc_conf_lock);
+ conf = sptlrpc_conf_get(fsname, 0);
+ if (conf == NULL) {
+ CERROR("can't find conf\n");
+ rc = -ENOMEM;
+ } else {
+ rc = sptlrpc_conf_merge_rule(conf, target, &rule);
+ }
+ mutex_unlock(&sptlrpc_conf_lock);
+ } else {
+ LASSERT(mutex_is_locked(&sptlrpc_conf_lock));
+ rc = sptlrpc_conf_merge_rule(conf, target, &rule);
+ }
+
+ if (rc == 0)
+ conf->sc_modified++;
+
+ return rc;
+}
+
+int sptlrpc_process_config(struct lustre_cfg *lcfg)
+{
+ return __sptlrpc_process_config(lcfg, NULL);
+}
+EXPORT_SYMBOL(sptlrpc_process_config);
+
+static int logname2fsname(const char *logname, char *buf, int buflen)
+{
+ char *ptr;
+ int len;
+
+ ptr = strrchr(logname, '-');
+ if (ptr == NULL || strcmp(ptr, "-sptlrpc")) {
+ CERROR("%s is not a sptlrpc config log\n", logname);
+ return -EINVAL;
+ }
+
+ len = min((int) (ptr - logname), buflen - 1);
+
+ memcpy(buf, logname, len);
+ buf[len] = '\0';
+ return 0;
+}
+
+void sptlrpc_conf_log_update_begin(const char *logname)
+{
+ struct sptlrpc_conf *conf;
+ char fsname[16];
+
+ if (logname2fsname(logname, fsname, sizeof(fsname)))
+ return;
+
+ mutex_lock(&sptlrpc_conf_lock);
+
+ conf = sptlrpc_conf_get(fsname, 0);
+ if (conf) {
+ if (conf->sc_local) {
+ LASSERT(conf->sc_updated == 0);
+ sptlrpc_conf_free_rsets(conf);
+ }
+ conf->sc_modified = 0;
+ }
+
+ mutex_unlock(&sptlrpc_conf_lock);
+}
+EXPORT_SYMBOL(sptlrpc_conf_log_update_begin);
+
+/**
+ * mark a config log has been updated
+ */
+void sptlrpc_conf_log_update_end(const char *logname)
+{
+ struct sptlrpc_conf *conf;
+ char fsname[16];
+
+ if (logname2fsname(logname, fsname, sizeof(fsname)))
+ return;
+
+ mutex_lock(&sptlrpc_conf_lock);
+
+ conf = sptlrpc_conf_get(fsname, 0);
+ if (conf) {
+ /*
+ * if original state is not updated, make sure the
+ * modified counter > 0 to enforce updating local copy.
+ */
+ if (conf->sc_updated == 0)
+ conf->sc_modified++;
+
+ conf->sc_updated = 1;
+ }
+
+ mutex_unlock(&sptlrpc_conf_lock);
+}
+EXPORT_SYMBOL(sptlrpc_conf_log_update_end);
+
+void sptlrpc_conf_log_start(const char *logname)
+{
+ char fsname[16];
+
+ if (logname2fsname(logname, fsname, sizeof(fsname)))
+ return;
+
+ mutex_lock(&sptlrpc_conf_lock);
+ sptlrpc_conf_get(fsname, 1);
+ mutex_unlock(&sptlrpc_conf_lock);
+}
+EXPORT_SYMBOL(sptlrpc_conf_log_start);
+
+void sptlrpc_conf_log_stop(const char *logname)
+{
+ struct sptlrpc_conf *conf;
+ char fsname[16];
+
+ if (logname2fsname(logname, fsname, sizeof(fsname)))
+ return;
+
+ mutex_lock(&sptlrpc_conf_lock);
+ conf = sptlrpc_conf_get(fsname, 0);
+ if (conf)
+ sptlrpc_conf_free(conf);
+ mutex_unlock(&sptlrpc_conf_lock);
+}
+EXPORT_SYMBOL(sptlrpc_conf_log_stop);
+
+static inline void flavor_set_flags(struct sptlrpc_flavor *sf,
+ enum lustre_sec_part from,
+ enum lustre_sec_part to,
+ unsigned int fl_udesc)
+{
+ /*
+ * null flavor doesn't need to set any flavor, and in fact
+ * we'd better not do that because everybody share a single sec.
+ */
+ if (sf->sf_rpc == SPTLRPC_FLVR_NULL)
+ return;
+
+ if (from == LUSTRE_SP_MDT) {
+ /* MDT->MDT; MDT->OST */
+ sf->sf_flags |= PTLRPC_SEC_FL_ROOTONLY;
+ } else if (from == LUSTRE_SP_CLI && to == LUSTRE_SP_OST) {
+ /* CLI->OST */
+ sf->sf_flags |= PTLRPC_SEC_FL_ROOTONLY | PTLRPC_SEC_FL_BULK;
+ } else if (from == LUSTRE_SP_CLI && to == LUSTRE_SP_MDT) {
+ /* CLI->MDT */
+ if (fl_udesc && sf->sf_rpc != SPTLRPC_FLVR_NULL)
+ sf->sf_flags |= PTLRPC_SEC_FL_UDESC;
+ }
+}
+
+void sptlrpc_conf_choose_flavor(enum lustre_sec_part from,
+ enum lustre_sec_part to,
+ struct obd_uuid *target,
+ lnet_nid_t nid,
+ struct sptlrpc_flavor *sf)
+{
+ struct sptlrpc_conf *conf;
+ struct sptlrpc_conf_tgt *conf_tgt;
+ char name[MTI_NAME_MAXLEN];
+ int len, rc = 0;
+
+ target2fsname(target->uuid, name, sizeof(name));
+
+ mutex_lock(&sptlrpc_conf_lock);
+
+ conf = sptlrpc_conf_get(name, 0);
+ if (conf == NULL)
+ goto out;
+
+ /* convert uuid name (supposed end with _UUID) to target name */
+ len = strlen(target->uuid);
+ LASSERT(len > 5);
+ memcpy(name, target->uuid, len - 5);
+ name[len - 5] = '\0';
+
+ conf_tgt = sptlrpc_conf_get_tgt(conf, name, 0);
+ if (conf_tgt) {
+ rc = sptlrpc_rule_set_choose(&conf_tgt->sct_rset,
+ from, to, nid, sf);
+ if (rc)
+ goto out;
+ }
+
+ rc = sptlrpc_rule_set_choose(&conf->sc_rset, from, to, nid, sf);
+out:
+ mutex_unlock(&sptlrpc_conf_lock);
+
+ if (rc == 0)
+ get_default_flavor(sf);
+
+ flavor_set_flags(sf, from, to, 1);
+}
+
+/**
+ * called by target devices, determine the expected flavor from
+ * certain peer (from, nid).
+ */
+void sptlrpc_target_choose_flavor(struct sptlrpc_rule_set *rset,
+ enum lustre_sec_part from,
+ lnet_nid_t nid,
+ struct sptlrpc_flavor *sf)
+{
+ if (sptlrpc_rule_set_choose(rset, from, LUSTRE_SP_ANY, nid, sf) == 0)
+ get_default_flavor(sf);
+}
+EXPORT_SYMBOL(sptlrpc_target_choose_flavor);
+
+#define SEC_ADAPT_DELAY (10)
+
+/**
+ * called by client devices, notify the sptlrpc config has changed and
+ * do import_sec_adapt later.
+ */
+void sptlrpc_conf_client_adapt(struct obd_device *obd)
+{
+ struct obd_import *imp;
+
+ LASSERT(strcmp(obd->obd_type->typ_name, LUSTRE_MDC_NAME) == 0 ||
+ strcmp(obd->obd_type->typ_name, LUSTRE_OSC_NAME) == 0);
+ CDEBUG(D_SEC, "obd %s\n", obd->u.cli.cl_target_uuid.uuid);
+
+ /* serialize with connect/disconnect import */
+ down_read(&obd->u.cli.cl_sem);
+
+ imp = obd->u.cli.cl_import;
+ if (imp) {
+ spin_lock(&imp->imp_lock);
+ if (imp->imp_sec)
+ imp->imp_sec_expire = get_seconds() +
+ SEC_ADAPT_DELAY;
+ spin_unlock(&imp->imp_lock);
+ }
+
+ up_read(&obd->u.cli.cl_sem);
+}
+EXPORT_SYMBOL(sptlrpc_conf_client_adapt);
+
+int sptlrpc_conf_init(void)
+{
+ mutex_init(&sptlrpc_conf_lock);
+ return 0;
+}
+
+void sptlrpc_conf_fini(void)
+{
+ struct sptlrpc_conf *conf, *conf_next;
+
+ mutex_lock(&sptlrpc_conf_lock);
+ list_for_each_entry_safe(conf, conf_next, &sptlrpc_confs, sc_list) {
+ sptlrpc_conf_free(conf);
+ }
+ LASSERT(list_empty(&sptlrpc_confs));
+ mutex_unlock(&sptlrpc_conf_lock);
+}
diff --git a/drivers/staging/lustre/lustre/ptlrpc/sec_gc.c b/drivers/staging/lustre/lustre/ptlrpc/sec_gc.c
new file mode 100644
index 000000000..81de68edb
--- /dev/null
+++ b/drivers/staging/lustre/lustre/ptlrpc/sec_gc.c
@@ -0,0 +1,252 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2011, 2012, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ *
+ * lustre/ptlrpc/sec_gc.c
+ *
+ * Author: Eric Mei <ericm@clusterfs.com>
+ */
+
+#define DEBUG_SUBSYSTEM S_SEC
+
+#include "../../include/linux/libcfs/libcfs.h"
+
+#include "../include/obd_support.h"
+#include "../include/obd_class.h"
+#include "../include/lustre_net.h"
+#include "../include/lustre_sec.h"
+
+#include "ptlrpc_internal.h"
+
+#define SEC_GC_INTERVAL (30 * 60)
+
+
+static struct mutex sec_gc_mutex;
+static LIST_HEAD(sec_gc_list);
+static spinlock_t sec_gc_list_lock;
+
+static LIST_HEAD(sec_gc_ctx_list);
+static spinlock_t sec_gc_ctx_list_lock;
+
+static struct ptlrpc_thread sec_gc_thread;
+static atomic_t sec_gc_wait_del = ATOMIC_INIT(0);
+
+
+void sptlrpc_gc_add_sec(struct ptlrpc_sec *sec)
+{
+ LASSERT(sec->ps_policy->sp_cops->gc_ctx);
+ LASSERT(sec->ps_gc_interval > 0);
+ LASSERT(list_empty(&sec->ps_gc_list));
+
+ sec->ps_gc_next = get_seconds() + sec->ps_gc_interval;
+
+ spin_lock(&sec_gc_list_lock);
+ list_add_tail(&sec_gc_list, &sec->ps_gc_list);
+ spin_unlock(&sec_gc_list_lock);
+
+ CDEBUG(D_SEC, "added sec %p(%s)\n", sec, sec->ps_policy->sp_name);
+}
+EXPORT_SYMBOL(sptlrpc_gc_add_sec);
+
+void sptlrpc_gc_del_sec(struct ptlrpc_sec *sec)
+{
+ if (list_empty(&sec->ps_gc_list))
+ return;
+
+ might_sleep();
+
+ /* signal before list_del to make iteration in gc thread safe */
+ atomic_inc(&sec_gc_wait_del);
+
+ spin_lock(&sec_gc_list_lock);
+ list_del_init(&sec->ps_gc_list);
+ spin_unlock(&sec_gc_list_lock);
+
+ /* barrier */
+ mutex_lock(&sec_gc_mutex);
+ mutex_unlock(&sec_gc_mutex);
+
+ atomic_dec(&sec_gc_wait_del);
+
+ CDEBUG(D_SEC, "del sec %p(%s)\n", sec, sec->ps_policy->sp_name);
+}
+EXPORT_SYMBOL(sptlrpc_gc_del_sec);
+
+void sptlrpc_gc_add_ctx(struct ptlrpc_cli_ctx *ctx)
+{
+ LASSERT(list_empty(&ctx->cc_gc_chain));
+
+ CDEBUG(D_SEC, "hand over ctx %p(%u->%s)\n",
+ ctx, ctx->cc_vcred.vc_uid, sec2target_str(ctx->cc_sec));
+ spin_lock(&sec_gc_ctx_list_lock);
+ list_add(&ctx->cc_gc_chain, &sec_gc_ctx_list);
+ spin_unlock(&sec_gc_ctx_list_lock);
+
+ thread_add_flags(&sec_gc_thread, SVC_SIGNAL);
+ wake_up(&sec_gc_thread.t_ctl_waitq);
+}
+EXPORT_SYMBOL(sptlrpc_gc_add_ctx);
+
+static void sec_process_ctx_list(void)
+{
+ struct ptlrpc_cli_ctx *ctx;
+
+ spin_lock(&sec_gc_ctx_list_lock);
+
+ while (!list_empty(&sec_gc_ctx_list)) {
+ ctx = list_entry(sec_gc_ctx_list.next,
+ struct ptlrpc_cli_ctx, cc_gc_chain);
+ list_del_init(&ctx->cc_gc_chain);
+ spin_unlock(&sec_gc_ctx_list_lock);
+
+ LASSERT(ctx->cc_sec);
+ LASSERT(atomic_read(&ctx->cc_refcount) == 1);
+ CDEBUG(D_SEC, "gc pick up ctx %p(%u->%s)\n",
+ ctx, ctx->cc_vcred.vc_uid, sec2target_str(ctx->cc_sec));
+ sptlrpc_cli_ctx_put(ctx, 1);
+
+ spin_lock(&sec_gc_ctx_list_lock);
+ }
+
+ spin_unlock(&sec_gc_ctx_list_lock);
+}
+
+static void sec_do_gc(struct ptlrpc_sec *sec)
+{
+ LASSERT(sec->ps_policy->sp_cops->gc_ctx);
+
+ if (unlikely(sec->ps_gc_next == 0)) {
+ CDEBUG(D_SEC, "sec %p(%s) has 0 gc time\n",
+ sec, sec->ps_policy->sp_name);
+ return;
+ }
+
+ CDEBUG(D_SEC, "check on sec %p(%s)\n", sec, sec->ps_policy->sp_name);
+
+ if (cfs_time_after(sec->ps_gc_next, get_seconds()))
+ return;
+
+ sec->ps_policy->sp_cops->gc_ctx(sec);
+ sec->ps_gc_next = get_seconds() + sec->ps_gc_interval;
+}
+
+static int sec_gc_main(void *arg)
+{
+ struct ptlrpc_thread *thread = (struct ptlrpc_thread *) arg;
+ struct l_wait_info lwi;
+
+ unshare_fs_struct();
+
+ /* Record that the thread is running */
+ thread_set_flags(thread, SVC_RUNNING);
+ wake_up(&thread->t_ctl_waitq);
+
+ while (1) {
+ struct ptlrpc_sec *sec;
+
+ thread_clear_flags(thread, SVC_SIGNAL);
+ sec_process_ctx_list();
+again:
+ /* go through sec list do gc.
+ * FIXME here we iterate through the whole list each time which
+ * is not optimal. we perhaps want to use balanced binary tree
+ * to trace each sec as order of expiry time.
+ * another issue here is we wakeup as fixed interval instead of
+ * according to each sec's expiry time */
+ mutex_lock(&sec_gc_mutex);
+ list_for_each_entry(sec, &sec_gc_list, ps_gc_list) {
+ /* if someone is waiting to be deleted, let it
+ * proceed as soon as possible. */
+ if (atomic_read(&sec_gc_wait_del)) {
+ CDEBUG(D_SEC, "deletion pending, start over\n");
+ mutex_unlock(&sec_gc_mutex);
+ goto again;
+ }
+
+ sec_do_gc(sec);
+ }
+ mutex_unlock(&sec_gc_mutex);
+
+ /* check ctx list again before sleep */
+ sec_process_ctx_list();
+
+ lwi = LWI_TIMEOUT(SEC_GC_INTERVAL * HZ, NULL, NULL);
+ l_wait_event(thread->t_ctl_waitq,
+ thread_is_stopping(thread) ||
+ thread_is_signal(thread),
+ &lwi);
+
+ if (thread_test_and_clear_flags(thread, SVC_STOPPING))
+ break;
+ }
+
+ thread_set_flags(thread, SVC_STOPPED);
+ wake_up(&thread->t_ctl_waitq);
+ return 0;
+}
+
+int sptlrpc_gc_init(void)
+{
+ struct l_wait_info lwi = { 0 };
+ struct task_struct *task;
+
+ mutex_init(&sec_gc_mutex);
+ spin_lock_init(&sec_gc_list_lock);
+ spin_lock_init(&sec_gc_ctx_list_lock);
+
+ /* initialize thread control */
+ memset(&sec_gc_thread, 0, sizeof(sec_gc_thread));
+ init_waitqueue_head(&sec_gc_thread.t_ctl_waitq);
+
+ task = kthread_run(sec_gc_main, &sec_gc_thread, "sptlrpc_gc");
+ if (IS_ERR(task)) {
+ CERROR("can't start gc thread: %ld\n", PTR_ERR(task));
+ return PTR_ERR(task);
+ }
+
+ l_wait_event(sec_gc_thread.t_ctl_waitq,
+ thread_is_running(&sec_gc_thread), &lwi);
+ return 0;
+}
+
+void sptlrpc_gc_fini(void)
+{
+ struct l_wait_info lwi = { 0 };
+
+ thread_set_flags(&sec_gc_thread, SVC_STOPPING);
+ wake_up(&sec_gc_thread.t_ctl_waitq);
+
+ l_wait_event(sec_gc_thread.t_ctl_waitq,
+ thread_is_stopped(&sec_gc_thread), &lwi);
+}
diff --git a/drivers/staging/lustre/lustre/ptlrpc/sec_lproc.c b/drivers/staging/lustre/lustre/ptlrpc/sec_lproc.c
new file mode 100644
index 000000000..0d08145a6
--- /dev/null
+++ b/drivers/staging/lustre/lustre/ptlrpc/sec_lproc.c
@@ -0,0 +1,199 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ *
+ * lustre/ptlrpc/sec_lproc.c
+ *
+ * Author: Eric Mei <ericm@clusterfs.com>
+ */
+
+#define DEBUG_SUBSYSTEM S_SEC
+
+#include "../../include/linux/libcfs/libcfs.h"
+#include <linux/crypto.h>
+
+#include "../include/obd.h"
+#include "../include/obd_class.h"
+#include "../include/obd_support.h"
+#include "../include/lustre_net.h"
+#include "../include/lustre_import.h"
+#include "../include/lustre_dlm.h"
+#include "../include/lustre_sec.h"
+
+#include "ptlrpc_internal.h"
+
+
+struct proc_dir_entry *sptlrpc_proc_root = NULL;
+EXPORT_SYMBOL(sptlrpc_proc_root);
+
+static char *sec_flags2str(unsigned long flags, char *buf, int bufsize)
+{
+ buf[0] = '\0';
+
+ if (flags & PTLRPC_SEC_FL_REVERSE)
+ strlcat(buf, "reverse,", bufsize);
+ if (flags & PTLRPC_SEC_FL_ROOTONLY)
+ strlcat(buf, "rootonly,", bufsize);
+ if (flags & PTLRPC_SEC_FL_UDESC)
+ strlcat(buf, "udesc,", bufsize);
+ if (flags & PTLRPC_SEC_FL_BULK)
+ strlcat(buf, "bulk,", bufsize);
+ if (buf[0] == '\0')
+ strlcat(buf, "-,", bufsize);
+
+ return buf;
+}
+
+static int sptlrpc_info_lprocfs_seq_show(struct seq_file *seq, void *v)
+{
+ struct obd_device *dev = seq->private;
+ struct client_obd *cli = &dev->u.cli;
+ struct ptlrpc_sec *sec = NULL;
+ char str[32];
+
+ LASSERT(strcmp(dev->obd_type->typ_name, LUSTRE_OSC_NAME) == 0 ||
+ strcmp(dev->obd_type->typ_name, LUSTRE_MDC_NAME) == 0 ||
+ strcmp(dev->obd_type->typ_name, LUSTRE_MGC_NAME) == 0);
+
+ if (cli->cl_import)
+ sec = sptlrpc_import_sec_ref(cli->cl_import);
+ if (sec == NULL)
+ goto out;
+
+ sec_flags2str(sec->ps_flvr.sf_flags, str, sizeof(str));
+
+ seq_printf(seq, "rpc flavor: %s\n",
+ sptlrpc_flavor2name_base(sec->ps_flvr.sf_rpc));
+ seq_printf(seq, "bulk flavor: %s\n",
+ sptlrpc_flavor2name_bulk(&sec->ps_flvr, str, sizeof(str)));
+ seq_printf(seq, "flags: %s\n",
+ sec_flags2str(sec->ps_flvr.sf_flags, str, sizeof(str)));
+ seq_printf(seq, "id: %d\n", sec->ps_id);
+ seq_printf(seq, "refcount: %d\n",
+ atomic_read(&sec->ps_refcount));
+ seq_printf(seq, "nctx: %d\n", atomic_read(&sec->ps_nctx));
+ seq_printf(seq, "gc internal %ld\n", sec->ps_gc_interval);
+ seq_printf(seq, "gc next %ld\n",
+ sec->ps_gc_interval ?
+ sec->ps_gc_next - get_seconds() : 0);
+
+ sptlrpc_sec_put(sec);
+out:
+ return 0;
+}
+LPROC_SEQ_FOPS_RO(sptlrpc_info_lprocfs);
+
+static int sptlrpc_ctxs_lprocfs_seq_show(struct seq_file *seq, void *v)
+{
+ struct obd_device *dev = seq->private;
+ struct client_obd *cli = &dev->u.cli;
+ struct ptlrpc_sec *sec = NULL;
+
+ LASSERT(strcmp(dev->obd_type->typ_name, LUSTRE_OSC_NAME) == 0 ||
+ strcmp(dev->obd_type->typ_name, LUSTRE_MDC_NAME) == 0 ||
+ strcmp(dev->obd_type->typ_name, LUSTRE_MGC_NAME) == 0);
+
+ if (cli->cl_import)
+ sec = sptlrpc_import_sec_ref(cli->cl_import);
+ if (sec == NULL)
+ goto out;
+
+ if (sec->ps_policy->sp_cops->display)
+ sec->ps_policy->sp_cops->display(sec, seq);
+
+ sptlrpc_sec_put(sec);
+out:
+ return 0;
+}
+LPROC_SEQ_FOPS_RO(sptlrpc_ctxs_lprocfs);
+
+int sptlrpc_lprocfs_cliobd_attach(struct obd_device *dev)
+{
+ int rc;
+
+ if (strcmp(dev->obd_type->typ_name, LUSTRE_OSC_NAME) != 0 &&
+ strcmp(dev->obd_type->typ_name, LUSTRE_MDC_NAME) != 0 &&
+ strcmp(dev->obd_type->typ_name, LUSTRE_MGC_NAME) != 0) {
+ CERROR("can't register lproc for obd type %s\n",
+ dev->obd_type->typ_name);
+ return -EINVAL;
+ }
+
+ rc = lprocfs_obd_seq_create(dev, "srpc_info", 0444,
+ &sptlrpc_info_lprocfs_fops, dev);
+ if (rc) {
+ CERROR("create proc entry srpc_info for %s: %d\n",
+ dev->obd_name, rc);
+ return rc;
+ }
+
+ rc = lprocfs_obd_seq_create(dev, "srpc_contexts", 0444,
+ &sptlrpc_ctxs_lprocfs_fops, dev);
+ if (rc) {
+ CERROR("create proc entry srpc_contexts for %s: %d\n",
+ dev->obd_name, rc);
+ return rc;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL(sptlrpc_lprocfs_cliobd_attach);
+
+LPROC_SEQ_FOPS_RO(sptlrpc_proc_enc_pool);
+static struct lprocfs_vars sptlrpc_lprocfs_vars[] = {
+ { "encrypt_page_pools", &sptlrpc_proc_enc_pool_fops },
+ { NULL }
+};
+
+int sptlrpc_lproc_init(void)
+{
+ int rc;
+
+ LASSERT(sptlrpc_proc_root == NULL);
+
+ sptlrpc_proc_root = lprocfs_register("sptlrpc", proc_lustre_root,
+ sptlrpc_lprocfs_vars, NULL);
+ if (IS_ERR(sptlrpc_proc_root)) {
+ rc = PTR_ERR(sptlrpc_proc_root);
+ sptlrpc_proc_root = NULL;
+ return rc;
+ }
+ return 0;
+}
+
+void sptlrpc_lproc_fini(void)
+{
+ if (sptlrpc_proc_root) {
+ lprocfs_remove(&sptlrpc_proc_root);
+ sptlrpc_proc_root = NULL;
+ }
+}
diff --git a/drivers/staging/lustre/lustre/ptlrpc/sec_null.c b/drivers/staging/lustre/lustre/ptlrpc/sec_null.c
new file mode 100644
index 000000000..4e132435b
--- /dev/null
+++ b/drivers/staging/lustre/lustre/ptlrpc/sec_null.c
@@ -0,0 +1,458 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2011, 2012, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ *
+ * lustre/ptlrpc/sec_null.c
+ *
+ * Author: Eric Mei <ericm@clusterfs.com>
+ */
+
+#define DEBUG_SUBSYSTEM S_SEC
+
+
+#include "../include/obd_support.h"
+#include "../include/obd_cksum.h"
+#include "../include/obd_class.h"
+#include "../include/lustre_net.h"
+#include "../include/lustre_sec.h"
+
+static struct ptlrpc_sec_policy null_policy;
+static struct ptlrpc_sec null_sec;
+static struct ptlrpc_cli_ctx null_cli_ctx;
+static struct ptlrpc_svc_ctx null_svc_ctx;
+
+/*
+ * we can temporarily use the topmost 8-bits of lm_secflvr to identify
+ * the source sec part.
+ */
+static inline
+void null_encode_sec_part(struct lustre_msg *msg, enum lustre_sec_part sp)
+{
+ msg->lm_secflvr |= (((__u32) sp) & 0xFF) << 24;
+}
+
+static inline
+enum lustre_sec_part null_decode_sec_part(struct lustre_msg *msg)
+{
+ return (msg->lm_secflvr >> 24) & 0xFF;
+}
+
+static int null_ctx_refresh(struct ptlrpc_cli_ctx *ctx)
+{
+ /* should never reach here */
+ LBUG();
+ return 0;
+}
+
+static
+int null_ctx_sign(struct ptlrpc_cli_ctx *ctx, struct ptlrpc_request *req)
+{
+ req->rq_reqbuf->lm_secflvr = SPTLRPC_FLVR_NULL;
+
+ if (!req->rq_import->imp_dlm_fake) {
+ struct obd_device *obd = req->rq_import->imp_obd;
+ null_encode_sec_part(req->rq_reqbuf,
+ obd->u.cli.cl_sp_me);
+ }
+ req->rq_reqdata_len = req->rq_reqlen;
+ return 0;
+}
+
+static
+int null_ctx_verify(struct ptlrpc_cli_ctx *ctx, struct ptlrpc_request *req)
+{
+ __u32 cksums, cksumc;
+
+ LASSERT(req->rq_repdata);
+
+ req->rq_repmsg = req->rq_repdata;
+ req->rq_replen = req->rq_repdata_len;
+
+ if (req->rq_early) {
+ cksums = lustre_msg_get_cksum(req->rq_repdata);
+ cksumc = lustre_msg_calc_cksum(req->rq_repmsg);
+ if (cksumc != cksums) {
+ CDEBUG(D_SEC,
+ "early reply checksum mismatch: %08x != %08x\n",
+ cksumc, cksums);
+ return -EINVAL;
+ }
+ }
+
+ return 0;
+}
+
+static
+struct ptlrpc_sec *null_create_sec(struct obd_import *imp,
+ struct ptlrpc_svc_ctx *svc_ctx,
+ struct sptlrpc_flavor *sf)
+{
+ LASSERT(SPTLRPC_FLVR_POLICY(sf->sf_rpc) == SPTLRPC_POLICY_NULL);
+
+ /* general layer has take a module reference for us, because we never
+ * really destroy the sec, simply release the reference here.
+ */
+ sptlrpc_policy_put(&null_policy);
+ return &null_sec;
+}
+
+static
+void null_destroy_sec(struct ptlrpc_sec *sec)
+{
+ LASSERT(sec == &null_sec);
+}
+
+static
+struct ptlrpc_cli_ctx *null_lookup_ctx(struct ptlrpc_sec *sec,
+ struct vfs_cred *vcred,
+ int create, int remove_dead)
+{
+ atomic_inc(&null_cli_ctx.cc_refcount);
+ return &null_cli_ctx;
+}
+
+static
+int null_flush_ctx_cache(struct ptlrpc_sec *sec,
+ uid_t uid,
+ int grace, int force)
+{
+ return 0;
+}
+
+static
+int null_alloc_reqbuf(struct ptlrpc_sec *sec,
+ struct ptlrpc_request *req,
+ int msgsize)
+{
+ if (!req->rq_reqbuf) {
+ int alloc_size = size_roundup_power2(msgsize);
+
+ LASSERT(!req->rq_pool);
+ OBD_ALLOC_LARGE(req->rq_reqbuf, alloc_size);
+ if (!req->rq_reqbuf)
+ return -ENOMEM;
+
+ req->rq_reqbuf_len = alloc_size;
+ } else {
+ LASSERT(req->rq_pool);
+ LASSERT(req->rq_reqbuf_len >= msgsize);
+ memset(req->rq_reqbuf, 0, msgsize);
+ }
+
+ req->rq_reqmsg = req->rq_reqbuf;
+ return 0;
+}
+
+static
+void null_free_reqbuf(struct ptlrpc_sec *sec,
+ struct ptlrpc_request *req)
+{
+ if (!req->rq_pool) {
+ LASSERTF(req->rq_reqmsg == req->rq_reqbuf,
+ "req %p: reqmsg %p is not reqbuf %p in null sec\n",
+ req, req->rq_reqmsg, req->rq_reqbuf);
+ LASSERTF(req->rq_reqbuf_len >= req->rq_reqlen,
+ "req %p: reqlen %d should smaller than buflen %d\n",
+ req, req->rq_reqlen, req->rq_reqbuf_len);
+
+ OBD_FREE_LARGE(req->rq_reqbuf, req->rq_reqbuf_len);
+ req->rq_reqbuf = NULL;
+ req->rq_reqbuf_len = 0;
+ }
+}
+
+static
+int null_alloc_repbuf(struct ptlrpc_sec *sec,
+ struct ptlrpc_request *req,
+ int msgsize)
+{
+ /* add space for early replied */
+ msgsize += lustre_msg_early_size();
+
+ msgsize = size_roundup_power2(msgsize);
+
+ OBD_ALLOC_LARGE(req->rq_repbuf, msgsize);
+ if (!req->rq_repbuf)
+ return -ENOMEM;
+
+ req->rq_repbuf_len = msgsize;
+ return 0;
+}
+
+static
+void null_free_repbuf(struct ptlrpc_sec *sec,
+ struct ptlrpc_request *req)
+{
+ LASSERT(req->rq_repbuf);
+
+ OBD_FREE_LARGE(req->rq_repbuf, req->rq_repbuf_len);
+ req->rq_repbuf = NULL;
+ req->rq_repbuf_len = 0;
+}
+
+static
+int null_enlarge_reqbuf(struct ptlrpc_sec *sec,
+ struct ptlrpc_request *req,
+ int segment, int newsize)
+{
+ struct lustre_msg *newbuf;
+ struct lustre_msg *oldbuf = req->rq_reqmsg;
+ int oldsize, newmsg_size, alloc_size;
+
+ LASSERT(req->rq_reqbuf);
+ LASSERT(req->rq_reqbuf == req->rq_reqmsg);
+ LASSERT(req->rq_reqbuf_len >= req->rq_reqlen);
+ LASSERT(req->rq_reqlen == lustre_packed_msg_size(oldbuf));
+
+ /* compute new message size */
+ oldsize = req->rq_reqbuf->lm_buflens[segment];
+ req->rq_reqbuf->lm_buflens[segment] = newsize;
+ newmsg_size = lustre_packed_msg_size(oldbuf);
+ req->rq_reqbuf->lm_buflens[segment] = oldsize;
+
+ /* request from pool should always have enough buffer */
+ LASSERT(!req->rq_pool || req->rq_reqbuf_len >= newmsg_size);
+
+ if (req->rq_reqbuf_len < newmsg_size) {
+ alloc_size = size_roundup_power2(newmsg_size);
+
+ OBD_ALLOC_LARGE(newbuf, alloc_size);
+ if (newbuf == NULL)
+ return -ENOMEM;
+
+ /* Must lock this, so that otherwise unprotected change of
+ * rq_reqmsg is not racing with parallel processing of
+ * imp_replay_list traversing threads. See LU-3333
+ * This is a bandaid at best, we really need to deal with this
+ * in request enlarging code before unpacking that's already
+ * there */
+ if (req->rq_import)
+ spin_lock(&req->rq_import->imp_lock);
+ memcpy(newbuf, req->rq_reqbuf, req->rq_reqlen);
+
+ OBD_FREE_LARGE(req->rq_reqbuf, req->rq_reqbuf_len);
+ req->rq_reqbuf = req->rq_reqmsg = newbuf;
+ req->rq_reqbuf_len = alloc_size;
+
+ if (req->rq_import)
+ spin_unlock(&req->rq_import->imp_lock);
+ }
+
+ _sptlrpc_enlarge_msg_inplace(req->rq_reqmsg, segment, newsize);
+ req->rq_reqlen = newmsg_size;
+
+ return 0;
+}
+
+static struct ptlrpc_svc_ctx null_svc_ctx = {
+ .sc_refcount = ATOMIC_INIT(1),
+ .sc_policy = &null_policy,
+};
+
+static
+int null_accept(struct ptlrpc_request *req)
+{
+ LASSERT(SPTLRPC_FLVR_POLICY(req->rq_flvr.sf_rpc) ==
+ SPTLRPC_POLICY_NULL);
+
+ if (req->rq_flvr.sf_rpc != SPTLRPC_FLVR_NULL) {
+ CERROR("Invalid rpc flavor 0x%x\n", req->rq_flvr.sf_rpc);
+ return SECSVC_DROP;
+ }
+
+ req->rq_sp_from = null_decode_sec_part(req->rq_reqbuf);
+
+ req->rq_reqmsg = req->rq_reqbuf;
+ req->rq_reqlen = req->rq_reqdata_len;
+
+ req->rq_svc_ctx = &null_svc_ctx;
+ atomic_inc(&req->rq_svc_ctx->sc_refcount);
+
+ return SECSVC_OK;
+}
+
+static
+int null_alloc_rs(struct ptlrpc_request *req, int msgsize)
+{
+ struct ptlrpc_reply_state *rs;
+ int rs_size = sizeof(*rs) + msgsize;
+
+ LASSERT(msgsize % 8 == 0);
+
+ rs = req->rq_reply_state;
+
+ if (rs) {
+ /* pre-allocated */
+ LASSERT(rs->rs_size >= rs_size);
+ } else {
+ OBD_ALLOC_LARGE(rs, rs_size);
+ if (rs == NULL)
+ return -ENOMEM;
+
+ rs->rs_size = rs_size;
+ }
+
+ rs->rs_svc_ctx = req->rq_svc_ctx;
+ atomic_inc(&req->rq_svc_ctx->sc_refcount);
+
+ rs->rs_repbuf = (struct lustre_msg *) (rs + 1);
+ rs->rs_repbuf_len = rs_size - sizeof(*rs);
+ rs->rs_msg = rs->rs_repbuf;
+
+ req->rq_reply_state = rs;
+ return 0;
+}
+
+static
+void null_free_rs(struct ptlrpc_reply_state *rs)
+{
+ LASSERT_ATOMIC_GT(&rs->rs_svc_ctx->sc_refcount, 1);
+ atomic_dec(&rs->rs_svc_ctx->sc_refcount);
+
+ if (!rs->rs_prealloc)
+ OBD_FREE_LARGE(rs, rs->rs_size);
+}
+
+static
+int null_authorize(struct ptlrpc_request *req)
+{
+ struct ptlrpc_reply_state *rs = req->rq_reply_state;
+
+ LASSERT(rs);
+
+ rs->rs_repbuf->lm_secflvr = SPTLRPC_FLVR_NULL;
+ rs->rs_repdata_len = req->rq_replen;
+
+ if (likely(req->rq_packed_final)) {
+ if (lustre_msghdr_get_flags(req->rq_reqmsg) & MSGHDR_AT_SUPPORT)
+ req->rq_reply_off = lustre_msg_early_size();
+ else
+ req->rq_reply_off = 0;
+ } else {
+ __u32 cksum;
+
+ cksum = lustre_msg_calc_cksum(rs->rs_repbuf);
+ lustre_msg_set_cksum(rs->rs_repbuf, cksum);
+ req->rq_reply_off = 0;
+ }
+
+ return 0;
+}
+
+static struct ptlrpc_ctx_ops null_ctx_ops = {
+ .refresh = null_ctx_refresh,
+ .sign = null_ctx_sign,
+ .verify = null_ctx_verify,
+};
+
+static struct ptlrpc_sec_cops null_sec_cops = {
+ .create_sec = null_create_sec,
+ .destroy_sec = null_destroy_sec,
+ .lookup_ctx = null_lookup_ctx,
+ .flush_ctx_cache = null_flush_ctx_cache,
+ .alloc_reqbuf = null_alloc_reqbuf,
+ .alloc_repbuf = null_alloc_repbuf,
+ .free_reqbuf = null_free_reqbuf,
+ .free_repbuf = null_free_repbuf,
+ .enlarge_reqbuf = null_enlarge_reqbuf,
+};
+
+static struct ptlrpc_sec_sops null_sec_sops = {
+ .accept = null_accept,
+ .alloc_rs = null_alloc_rs,
+ .authorize = null_authorize,
+ .free_rs = null_free_rs,
+};
+
+static struct ptlrpc_sec_policy null_policy = {
+ .sp_owner = THIS_MODULE,
+ .sp_name = "sec.null",
+ .sp_policy = SPTLRPC_POLICY_NULL,
+ .sp_cops = &null_sec_cops,
+ .sp_sops = &null_sec_sops,
+};
+
+static void null_init_internal(void)
+{
+ static HLIST_HEAD(__list);
+
+ null_sec.ps_policy = &null_policy;
+ atomic_set(&null_sec.ps_refcount, 1); /* always busy */
+ null_sec.ps_id = -1;
+ null_sec.ps_import = NULL;
+ null_sec.ps_flvr.sf_rpc = SPTLRPC_FLVR_NULL;
+ null_sec.ps_flvr.sf_flags = 0;
+ null_sec.ps_part = LUSTRE_SP_ANY;
+ null_sec.ps_dying = 0;
+ spin_lock_init(&null_sec.ps_lock);
+ atomic_set(&null_sec.ps_nctx, 1); /* for "null_cli_ctx" */
+ INIT_LIST_HEAD(&null_sec.ps_gc_list);
+ null_sec.ps_gc_interval = 0;
+ null_sec.ps_gc_next = 0;
+
+ hlist_add_head(&null_cli_ctx.cc_cache, &__list);
+ atomic_set(&null_cli_ctx.cc_refcount, 1); /* for hash */
+ null_cli_ctx.cc_sec = &null_sec;
+ null_cli_ctx.cc_ops = &null_ctx_ops;
+ null_cli_ctx.cc_expire = 0;
+ null_cli_ctx.cc_flags = PTLRPC_CTX_CACHED | PTLRPC_CTX_ETERNAL |
+ PTLRPC_CTX_UPTODATE;
+ null_cli_ctx.cc_vcred.vc_uid = 0;
+ spin_lock_init(&null_cli_ctx.cc_lock);
+ INIT_LIST_HEAD(&null_cli_ctx.cc_req_list);
+ INIT_LIST_HEAD(&null_cli_ctx.cc_gc_chain);
+}
+
+int sptlrpc_null_init(void)
+{
+ int rc;
+
+ null_init_internal();
+
+ rc = sptlrpc_register_policy(&null_policy);
+ if (rc)
+ CERROR("failed to register %s: %d\n", null_policy.sp_name, rc);
+
+ return rc;
+}
+
+void sptlrpc_null_fini(void)
+{
+ int rc;
+
+ rc = sptlrpc_unregister_policy(&null_policy);
+ if (rc)
+ CERROR("failed to unregister %s: %d\n",
+ null_policy.sp_name, rc);
+}
diff --git a/drivers/staging/lustre/lustre/ptlrpc/sec_plain.c b/drivers/staging/lustre/lustre/ptlrpc/sec_plain.c
new file mode 100644
index 000000000..a79cd5301
--- /dev/null
+++ b/drivers/staging/lustre/lustre/ptlrpc/sec_plain.c
@@ -0,0 +1,1013 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2011, 2012, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ *
+ * lustre/ptlrpc/sec_plain.c
+ *
+ * Author: Eric Mei <ericm@clusterfs.com>
+ */
+
+#define DEBUG_SUBSYSTEM S_SEC
+
+
+#include "../include/obd_support.h"
+#include "../include/obd_cksum.h"
+#include "../include/obd_class.h"
+#include "../include/lustre_net.h"
+#include "../include/lustre_sec.h"
+
+struct plain_sec {
+ struct ptlrpc_sec pls_base;
+ rwlock_t pls_lock;
+ struct ptlrpc_cli_ctx *pls_ctx;
+};
+
+static inline struct plain_sec *sec2plsec(struct ptlrpc_sec *sec)
+{
+ return container_of(sec, struct plain_sec, pls_base);
+}
+
+static struct ptlrpc_sec_policy plain_policy;
+static struct ptlrpc_ctx_ops plain_ctx_ops;
+static struct ptlrpc_svc_ctx plain_svc_ctx;
+
+static unsigned int plain_at_offset;
+
+/*
+ * for simplicity, plain policy rpc use fixed layout.
+ */
+#define PLAIN_PACK_SEGMENTS (4)
+
+#define PLAIN_PACK_HDR_OFF (0)
+#define PLAIN_PACK_MSG_OFF (1)
+#define PLAIN_PACK_USER_OFF (2)
+#define PLAIN_PACK_BULK_OFF (3)
+
+#define PLAIN_FL_USER (0x01)
+#define PLAIN_FL_BULK (0x02)
+
+struct plain_header {
+ __u8 ph_ver; /* 0 */
+ __u8 ph_flags;
+ __u8 ph_sp; /* source */
+ __u8 ph_bulk_hash_alg; /* complete flavor desc */
+ __u8 ph_pad[4];
+};
+
+struct plain_bulk_token {
+ __u8 pbt_hash[8];
+};
+
+#define PLAIN_BSD_SIZE \
+ (sizeof(struct ptlrpc_bulk_sec_desc) + sizeof(struct plain_bulk_token))
+
+/****************************************
+ * bulk checksum helpers *
+ ****************************************/
+
+static int plain_unpack_bsd(struct lustre_msg *msg, int swabbed)
+{
+ struct ptlrpc_bulk_sec_desc *bsd;
+
+ if (bulk_sec_desc_unpack(msg, PLAIN_PACK_BULK_OFF, swabbed))
+ return -EPROTO;
+
+ bsd = lustre_msg_buf(msg, PLAIN_PACK_BULK_OFF, PLAIN_BSD_SIZE);
+ if (bsd == NULL) {
+ CERROR("bulk sec desc has short size %d\n",
+ lustre_msg_buflen(msg, PLAIN_PACK_BULK_OFF));
+ return -EPROTO;
+ }
+
+ if (bsd->bsd_svc != SPTLRPC_BULK_SVC_NULL &&
+ bsd->bsd_svc != SPTLRPC_BULK_SVC_INTG) {
+ CERROR("invalid bulk svc %u\n", bsd->bsd_svc);
+ return -EPROTO;
+ }
+
+ return 0;
+}
+
+static int plain_generate_bulk_csum(struct ptlrpc_bulk_desc *desc,
+ __u8 hash_alg,
+ struct plain_bulk_token *token)
+{
+ if (hash_alg == BULK_HASH_ALG_NULL)
+ return 0;
+
+ memset(token->pbt_hash, 0, sizeof(token->pbt_hash));
+ return sptlrpc_get_bulk_checksum(desc, hash_alg, token->pbt_hash,
+ sizeof(token->pbt_hash));
+}
+
+static int plain_verify_bulk_csum(struct ptlrpc_bulk_desc *desc,
+ __u8 hash_alg,
+ struct plain_bulk_token *tokenr)
+{
+ struct plain_bulk_token tokenv;
+ int rc;
+
+ if (hash_alg == BULK_HASH_ALG_NULL)
+ return 0;
+
+ memset(&tokenv.pbt_hash, 0, sizeof(tokenv.pbt_hash));
+ rc = sptlrpc_get_bulk_checksum(desc, hash_alg, tokenv.pbt_hash,
+ sizeof(tokenv.pbt_hash));
+ if (rc)
+ return rc;
+
+ if (memcmp(tokenr->pbt_hash, tokenv.pbt_hash, sizeof(tokenr->pbt_hash)))
+ return -EACCES;
+ return 0;
+}
+
+static void corrupt_bulk_data(struct ptlrpc_bulk_desc *desc)
+{
+ char *ptr;
+ unsigned int off, i;
+
+ for (i = 0; i < desc->bd_iov_count; i++) {
+ if (desc->bd_iov[i].kiov_len == 0)
+ continue;
+
+ ptr = kmap(desc->bd_iov[i].kiov_page);
+ off = desc->bd_iov[i].kiov_offset & ~CFS_PAGE_MASK;
+ ptr[off] ^= 0x1;
+ kunmap(desc->bd_iov[i].kiov_page);
+ return;
+ }
+}
+
+/****************************************
+ * cli_ctx apis *
+ ****************************************/
+
+static
+int plain_ctx_refresh(struct ptlrpc_cli_ctx *ctx)
+{
+ /* should never reach here */
+ LBUG();
+ return 0;
+}
+
+static
+int plain_ctx_validate(struct ptlrpc_cli_ctx *ctx)
+{
+ return 0;
+}
+
+static
+int plain_ctx_sign(struct ptlrpc_cli_ctx *ctx, struct ptlrpc_request *req)
+{
+ struct lustre_msg *msg = req->rq_reqbuf;
+ struct plain_header *phdr;
+
+ msg->lm_secflvr = req->rq_flvr.sf_rpc;
+
+ phdr = lustre_msg_buf(msg, PLAIN_PACK_HDR_OFF, 0);
+ phdr->ph_ver = 0;
+ phdr->ph_flags = 0;
+ phdr->ph_sp = ctx->cc_sec->ps_part;
+ phdr->ph_bulk_hash_alg = req->rq_flvr.u_bulk.hash.hash_alg;
+
+ if (req->rq_pack_udesc)
+ phdr->ph_flags |= PLAIN_FL_USER;
+ if (req->rq_pack_bulk)
+ phdr->ph_flags |= PLAIN_FL_BULK;
+
+ req->rq_reqdata_len = lustre_msg_size_v2(msg->lm_bufcount,
+ msg->lm_buflens);
+ return 0;
+}
+
+static
+int plain_ctx_verify(struct ptlrpc_cli_ctx *ctx, struct ptlrpc_request *req)
+{
+ struct lustre_msg *msg = req->rq_repdata;
+ struct plain_header *phdr;
+ __u32 cksum;
+ int swabbed;
+
+ if (msg->lm_bufcount != PLAIN_PACK_SEGMENTS) {
+ CERROR("unexpected reply buf count %u\n", msg->lm_bufcount);
+ return -EPROTO;
+ }
+
+ swabbed = ptlrpc_rep_need_swab(req);
+
+ phdr = lustre_msg_buf(msg, PLAIN_PACK_HDR_OFF, sizeof(*phdr));
+ if (phdr == NULL) {
+ CERROR("missing plain header\n");
+ return -EPROTO;
+ }
+
+ if (phdr->ph_ver != 0) {
+ CERROR("Invalid header version\n");
+ return -EPROTO;
+ }
+
+ /* expect no user desc in reply */
+ if (phdr->ph_flags & PLAIN_FL_USER) {
+ CERROR("Unexpected udesc flag in reply\n");
+ return -EPROTO;
+ }
+
+ if (phdr->ph_bulk_hash_alg != req->rq_flvr.u_bulk.hash.hash_alg) {
+ CERROR("reply bulk flavor %u != %u\n", phdr->ph_bulk_hash_alg,
+ req->rq_flvr.u_bulk.hash.hash_alg);
+ return -EPROTO;
+ }
+
+ if (unlikely(req->rq_early)) {
+ unsigned int hsize = 4;
+
+ cfs_crypto_hash_digest(CFS_HASH_ALG_CRC32,
+ lustre_msg_buf(msg, PLAIN_PACK_MSG_OFF, 0),
+ lustre_msg_buflen(msg, PLAIN_PACK_MSG_OFF),
+ NULL, 0, (unsigned char *)&cksum, &hsize);
+ if (cksum != msg->lm_cksum) {
+ CDEBUG(D_SEC,
+ "early reply checksum mismatch: %08x != %08x\n",
+ cpu_to_le32(cksum), msg->lm_cksum);
+ return -EINVAL;
+ }
+ } else {
+ /* whether we sent with bulk or not, we expect the same
+ * in reply, except for early reply */
+ if (!req->rq_early &&
+ !equi(req->rq_pack_bulk == 1,
+ phdr->ph_flags & PLAIN_FL_BULK)) {
+ CERROR("%s bulk checksum in reply\n",
+ req->rq_pack_bulk ? "Missing" : "Unexpected");
+ return -EPROTO;
+ }
+
+ if (phdr->ph_flags & PLAIN_FL_BULK) {
+ if (plain_unpack_bsd(msg, swabbed))
+ return -EPROTO;
+ }
+ }
+
+ req->rq_repmsg = lustre_msg_buf(msg, PLAIN_PACK_MSG_OFF, 0);
+ req->rq_replen = lustre_msg_buflen(msg, PLAIN_PACK_MSG_OFF);
+ return 0;
+}
+
+static
+int plain_cli_wrap_bulk(struct ptlrpc_cli_ctx *ctx,
+ struct ptlrpc_request *req,
+ struct ptlrpc_bulk_desc *desc)
+{
+ struct ptlrpc_bulk_sec_desc *bsd;
+ struct plain_bulk_token *token;
+ int rc;
+
+ LASSERT(req->rq_pack_bulk);
+ LASSERT(req->rq_reqbuf->lm_bufcount == PLAIN_PACK_SEGMENTS);
+
+ bsd = lustre_msg_buf(req->rq_reqbuf, PLAIN_PACK_BULK_OFF, 0);
+ token = (struct plain_bulk_token *) bsd->bsd_data;
+
+ bsd->bsd_version = 0;
+ bsd->bsd_flags = 0;
+ bsd->bsd_type = SPTLRPC_BULK_DEFAULT;
+ bsd->bsd_svc = SPTLRPC_FLVR_BULK_SVC(req->rq_flvr.sf_rpc);
+
+ if (bsd->bsd_svc == SPTLRPC_BULK_SVC_NULL)
+ return 0;
+
+ if (req->rq_bulk_read)
+ return 0;
+
+ rc = plain_generate_bulk_csum(desc, req->rq_flvr.u_bulk.hash.hash_alg,
+ token);
+ if (rc) {
+ CERROR("bulk write: failed to compute checksum: %d\n", rc);
+ } else {
+ /*
+ * for sending we only compute the wrong checksum instead
+ * of corrupting the data so it is still correct on a redo
+ */
+ if (OBD_FAIL_CHECK(OBD_FAIL_OSC_CHECKSUM_SEND) &&
+ req->rq_flvr.u_bulk.hash.hash_alg != BULK_HASH_ALG_NULL)
+ token->pbt_hash[0] ^= 0x1;
+ }
+
+ return rc;
+}
+
+static
+int plain_cli_unwrap_bulk(struct ptlrpc_cli_ctx *ctx,
+ struct ptlrpc_request *req,
+ struct ptlrpc_bulk_desc *desc)
+{
+ struct ptlrpc_bulk_sec_desc *bsdv;
+ struct plain_bulk_token *tokenv;
+ int rc;
+ int i, nob;
+
+ LASSERT(req->rq_pack_bulk);
+ LASSERT(req->rq_reqbuf->lm_bufcount == PLAIN_PACK_SEGMENTS);
+ LASSERT(req->rq_repdata->lm_bufcount == PLAIN_PACK_SEGMENTS);
+
+ bsdv = lustre_msg_buf(req->rq_repdata, PLAIN_PACK_BULK_OFF, 0);
+ tokenv = (struct plain_bulk_token *) bsdv->bsd_data;
+
+ if (req->rq_bulk_write) {
+ if (bsdv->bsd_flags & BSD_FL_ERR)
+ return -EIO;
+ return 0;
+ }
+
+ /* fix the actual data size */
+ for (i = 0, nob = 0; i < desc->bd_iov_count; i++) {
+ if (desc->bd_iov[i].kiov_len + nob > desc->bd_nob_transferred) {
+ desc->bd_iov[i].kiov_len =
+ desc->bd_nob_transferred - nob;
+ }
+ nob += desc->bd_iov[i].kiov_len;
+ }
+
+ rc = plain_verify_bulk_csum(desc, req->rq_flvr.u_bulk.hash.hash_alg,
+ tokenv);
+ if (rc)
+ CERROR("bulk read: client verify failed: %d\n", rc);
+
+ return rc;
+}
+
+/****************************************
+ * sec apis *
+ ****************************************/
+
+static
+struct ptlrpc_cli_ctx *plain_sec_install_ctx(struct plain_sec *plsec)
+{
+ struct ptlrpc_cli_ctx *ctx, *ctx_new;
+
+ OBD_ALLOC_PTR(ctx_new);
+
+ write_lock(&plsec->pls_lock);
+
+ ctx = plsec->pls_ctx;
+ if (ctx) {
+ atomic_inc(&ctx->cc_refcount);
+
+ if (ctx_new)
+ OBD_FREE_PTR(ctx_new);
+ } else if (ctx_new) {
+ ctx = ctx_new;
+
+ atomic_set(&ctx->cc_refcount, 1); /* for cache */
+ ctx->cc_sec = &plsec->pls_base;
+ ctx->cc_ops = &plain_ctx_ops;
+ ctx->cc_expire = 0;
+ ctx->cc_flags = PTLRPC_CTX_CACHED | PTLRPC_CTX_UPTODATE;
+ ctx->cc_vcred.vc_uid = 0;
+ spin_lock_init(&ctx->cc_lock);
+ INIT_LIST_HEAD(&ctx->cc_req_list);
+ INIT_LIST_HEAD(&ctx->cc_gc_chain);
+
+ plsec->pls_ctx = ctx;
+ atomic_inc(&plsec->pls_base.ps_nctx);
+ atomic_inc(&plsec->pls_base.ps_refcount);
+
+ atomic_inc(&ctx->cc_refcount); /* for caller */
+ }
+
+ write_unlock(&plsec->pls_lock);
+
+ return ctx;
+}
+
+static
+void plain_destroy_sec(struct ptlrpc_sec *sec)
+{
+ struct plain_sec *plsec = sec2plsec(sec);
+
+ LASSERT(sec->ps_policy == &plain_policy);
+ LASSERT(sec->ps_import);
+ LASSERT(atomic_read(&sec->ps_refcount) == 0);
+ LASSERT(atomic_read(&sec->ps_nctx) == 0);
+ LASSERT(plsec->pls_ctx == NULL);
+
+ class_import_put(sec->ps_import);
+
+ OBD_FREE_PTR(plsec);
+}
+
+static
+void plain_kill_sec(struct ptlrpc_sec *sec)
+{
+ sec->ps_dying = 1;
+}
+
+static
+struct ptlrpc_sec *plain_create_sec(struct obd_import *imp,
+ struct ptlrpc_svc_ctx *svc_ctx,
+ struct sptlrpc_flavor *sf)
+{
+ struct plain_sec *plsec;
+ struct ptlrpc_sec *sec;
+ struct ptlrpc_cli_ctx *ctx;
+
+ LASSERT(SPTLRPC_FLVR_POLICY(sf->sf_rpc) == SPTLRPC_POLICY_PLAIN);
+
+ OBD_ALLOC_PTR(plsec);
+ if (plsec == NULL)
+ return NULL;
+
+ /*
+ * initialize plain_sec
+ */
+ rwlock_init(&plsec->pls_lock);
+ plsec->pls_ctx = NULL;
+
+ sec = &plsec->pls_base;
+ sec->ps_policy = &plain_policy;
+ atomic_set(&sec->ps_refcount, 0);
+ atomic_set(&sec->ps_nctx, 0);
+ sec->ps_id = sptlrpc_get_next_secid();
+ sec->ps_import = class_import_get(imp);
+ sec->ps_flvr = *sf;
+ spin_lock_init(&sec->ps_lock);
+ INIT_LIST_HEAD(&sec->ps_gc_list);
+ sec->ps_gc_interval = 0;
+ sec->ps_gc_next = 0;
+
+ /* install ctx immediately if this is a reverse sec */
+ if (svc_ctx) {
+ ctx = plain_sec_install_ctx(plsec);
+ if (ctx == NULL) {
+ plain_destroy_sec(sec);
+ return NULL;
+ }
+ sptlrpc_cli_ctx_put(ctx, 1);
+ }
+
+ return sec;
+}
+
+static
+struct ptlrpc_cli_ctx *plain_lookup_ctx(struct ptlrpc_sec *sec,
+ struct vfs_cred *vcred,
+ int create, int remove_dead)
+{
+ struct plain_sec *plsec = sec2plsec(sec);
+ struct ptlrpc_cli_ctx *ctx;
+
+ read_lock(&plsec->pls_lock);
+ ctx = plsec->pls_ctx;
+ if (ctx)
+ atomic_inc(&ctx->cc_refcount);
+ read_unlock(&plsec->pls_lock);
+
+ if (unlikely(ctx == NULL))
+ ctx = plain_sec_install_ctx(plsec);
+
+ return ctx;
+}
+
+static
+void plain_release_ctx(struct ptlrpc_sec *sec,
+ struct ptlrpc_cli_ctx *ctx, int sync)
+{
+ LASSERT(atomic_read(&sec->ps_refcount) > 0);
+ LASSERT(atomic_read(&sec->ps_nctx) > 0);
+ LASSERT(atomic_read(&ctx->cc_refcount) == 0);
+ LASSERT(ctx->cc_sec == sec);
+
+ OBD_FREE_PTR(ctx);
+
+ atomic_dec(&sec->ps_nctx);
+ sptlrpc_sec_put(sec);
+}
+
+static
+int plain_flush_ctx_cache(struct ptlrpc_sec *sec,
+ uid_t uid, int grace, int force)
+{
+ struct plain_sec *plsec = sec2plsec(sec);
+ struct ptlrpc_cli_ctx *ctx;
+
+ /* do nothing unless caller want to flush for 'all' */
+ if (uid != -1)
+ return 0;
+
+ write_lock(&plsec->pls_lock);
+ ctx = plsec->pls_ctx;
+ plsec->pls_ctx = NULL;
+ write_unlock(&plsec->pls_lock);
+
+ if (ctx)
+ sptlrpc_cli_ctx_put(ctx, 1);
+ return 0;
+}
+
+static
+int plain_alloc_reqbuf(struct ptlrpc_sec *sec,
+ struct ptlrpc_request *req,
+ int msgsize)
+{
+ __u32 buflens[PLAIN_PACK_SEGMENTS] = { 0, };
+ int alloc_len;
+
+ buflens[PLAIN_PACK_HDR_OFF] = sizeof(struct plain_header);
+ buflens[PLAIN_PACK_MSG_OFF] = msgsize;
+
+ if (req->rq_pack_udesc)
+ buflens[PLAIN_PACK_USER_OFF] = sptlrpc_current_user_desc_size();
+
+ if (req->rq_pack_bulk) {
+ LASSERT(req->rq_bulk_read || req->rq_bulk_write);
+ buflens[PLAIN_PACK_BULK_OFF] = PLAIN_BSD_SIZE;
+ }
+
+ alloc_len = lustre_msg_size_v2(PLAIN_PACK_SEGMENTS, buflens);
+
+ if (!req->rq_reqbuf) {
+ LASSERT(!req->rq_pool);
+
+ alloc_len = size_roundup_power2(alloc_len);
+ OBD_ALLOC_LARGE(req->rq_reqbuf, alloc_len);
+ if (!req->rq_reqbuf)
+ return -ENOMEM;
+
+ req->rq_reqbuf_len = alloc_len;
+ } else {
+ LASSERT(req->rq_pool);
+ LASSERT(req->rq_reqbuf_len >= alloc_len);
+ memset(req->rq_reqbuf, 0, alloc_len);
+ }
+
+ lustre_init_msg_v2(req->rq_reqbuf, PLAIN_PACK_SEGMENTS, buflens, NULL);
+ req->rq_reqmsg = lustre_msg_buf(req->rq_reqbuf, PLAIN_PACK_MSG_OFF, 0);
+
+ if (req->rq_pack_udesc)
+ sptlrpc_pack_user_desc(req->rq_reqbuf, PLAIN_PACK_USER_OFF);
+
+ return 0;
+}
+
+static
+void plain_free_reqbuf(struct ptlrpc_sec *sec,
+ struct ptlrpc_request *req)
+{
+ if (!req->rq_pool) {
+ OBD_FREE_LARGE(req->rq_reqbuf, req->rq_reqbuf_len);
+ req->rq_reqbuf = NULL;
+ req->rq_reqbuf_len = 0;
+ }
+}
+
+static
+int plain_alloc_repbuf(struct ptlrpc_sec *sec,
+ struct ptlrpc_request *req,
+ int msgsize)
+{
+ __u32 buflens[PLAIN_PACK_SEGMENTS] = { 0, };
+ int alloc_len;
+
+ buflens[PLAIN_PACK_HDR_OFF] = sizeof(struct plain_header);
+ buflens[PLAIN_PACK_MSG_OFF] = msgsize;
+
+ if (req->rq_pack_bulk) {
+ LASSERT(req->rq_bulk_read || req->rq_bulk_write);
+ buflens[PLAIN_PACK_BULK_OFF] = PLAIN_BSD_SIZE;
+ }
+
+ alloc_len = lustre_msg_size_v2(PLAIN_PACK_SEGMENTS, buflens);
+
+ /* add space for early reply */
+ alloc_len += plain_at_offset;
+
+ alloc_len = size_roundup_power2(alloc_len);
+
+ OBD_ALLOC_LARGE(req->rq_repbuf, alloc_len);
+ if (!req->rq_repbuf)
+ return -ENOMEM;
+
+ req->rq_repbuf_len = alloc_len;
+ return 0;
+}
+
+static
+void plain_free_repbuf(struct ptlrpc_sec *sec,
+ struct ptlrpc_request *req)
+{
+ OBD_FREE_LARGE(req->rq_repbuf, req->rq_repbuf_len);
+ req->rq_repbuf = NULL;
+ req->rq_repbuf_len = 0;
+}
+
+static
+int plain_enlarge_reqbuf(struct ptlrpc_sec *sec,
+ struct ptlrpc_request *req,
+ int segment, int newsize)
+{
+ struct lustre_msg *newbuf;
+ int oldsize;
+ int newmsg_size, newbuf_size;
+
+ LASSERT(req->rq_reqbuf);
+ LASSERT(req->rq_reqbuf_len >= req->rq_reqlen);
+ LASSERT(lustre_msg_buf(req->rq_reqbuf, PLAIN_PACK_MSG_OFF, 0) ==
+ req->rq_reqmsg);
+
+ /* compute new embedded msg size. */
+ oldsize = req->rq_reqmsg->lm_buflens[segment];
+ req->rq_reqmsg->lm_buflens[segment] = newsize;
+ newmsg_size = lustre_msg_size_v2(req->rq_reqmsg->lm_bufcount,
+ req->rq_reqmsg->lm_buflens);
+ req->rq_reqmsg->lm_buflens[segment] = oldsize;
+
+ /* compute new wrapper msg size. */
+ oldsize = req->rq_reqbuf->lm_buflens[PLAIN_PACK_MSG_OFF];
+ req->rq_reqbuf->lm_buflens[PLAIN_PACK_MSG_OFF] = newmsg_size;
+ newbuf_size = lustre_msg_size_v2(req->rq_reqbuf->lm_bufcount,
+ req->rq_reqbuf->lm_buflens);
+ req->rq_reqbuf->lm_buflens[PLAIN_PACK_MSG_OFF] = oldsize;
+
+ /* request from pool should always have enough buffer */
+ LASSERT(!req->rq_pool || req->rq_reqbuf_len >= newbuf_size);
+
+ if (req->rq_reqbuf_len < newbuf_size) {
+ newbuf_size = size_roundup_power2(newbuf_size);
+
+ OBD_ALLOC_LARGE(newbuf, newbuf_size);
+ if (newbuf == NULL)
+ return -ENOMEM;
+
+ /* Must lock this, so that otherwise unprotected change of
+ * rq_reqmsg is not racing with parallel processing of
+ * imp_replay_list traversing threads. See LU-3333
+ * This is a bandaid at best, we really need to deal with this
+ * in request enlarging code before unpacking that's already
+ * there */
+ if (req->rq_import)
+ spin_lock(&req->rq_import->imp_lock);
+
+ memcpy(newbuf, req->rq_reqbuf, req->rq_reqbuf_len);
+
+ OBD_FREE_LARGE(req->rq_reqbuf, req->rq_reqbuf_len);
+ req->rq_reqbuf = newbuf;
+ req->rq_reqbuf_len = newbuf_size;
+ req->rq_reqmsg = lustre_msg_buf(req->rq_reqbuf,
+ PLAIN_PACK_MSG_OFF, 0);
+
+ if (req->rq_import)
+ spin_unlock(&req->rq_import->imp_lock);
+ }
+
+ _sptlrpc_enlarge_msg_inplace(req->rq_reqbuf, PLAIN_PACK_MSG_OFF,
+ newmsg_size);
+ _sptlrpc_enlarge_msg_inplace(req->rq_reqmsg, segment, newsize);
+
+ req->rq_reqlen = newmsg_size;
+ return 0;
+}
+
+/****************************************
+ * service apis *
+ ****************************************/
+
+static struct ptlrpc_svc_ctx plain_svc_ctx = {
+ .sc_refcount = ATOMIC_INIT(1),
+ .sc_policy = &plain_policy,
+};
+
+static
+int plain_accept(struct ptlrpc_request *req)
+{
+ struct lustre_msg *msg = req->rq_reqbuf;
+ struct plain_header *phdr;
+ int swabbed;
+
+ LASSERT(SPTLRPC_FLVR_POLICY(req->rq_flvr.sf_rpc) ==
+ SPTLRPC_POLICY_PLAIN);
+
+ if (SPTLRPC_FLVR_BASE(req->rq_flvr.sf_rpc) !=
+ SPTLRPC_FLVR_BASE(SPTLRPC_FLVR_PLAIN) ||
+ SPTLRPC_FLVR_BULK_TYPE(req->rq_flvr.sf_rpc) !=
+ SPTLRPC_FLVR_BULK_TYPE(SPTLRPC_FLVR_PLAIN)) {
+ CERROR("Invalid rpc flavor %x\n", req->rq_flvr.sf_rpc);
+ return SECSVC_DROP;
+ }
+
+ if (msg->lm_bufcount < PLAIN_PACK_SEGMENTS) {
+ CERROR("unexpected request buf count %u\n", msg->lm_bufcount);
+ return SECSVC_DROP;
+ }
+
+ swabbed = ptlrpc_req_need_swab(req);
+
+ phdr = lustre_msg_buf(msg, PLAIN_PACK_HDR_OFF, sizeof(*phdr));
+ if (phdr == NULL) {
+ CERROR("missing plain header\n");
+ return -EPROTO;
+ }
+
+ if (phdr->ph_ver != 0) {
+ CERROR("Invalid header version\n");
+ return -EPROTO;
+ }
+
+ if (phdr->ph_bulk_hash_alg >= BULK_HASH_ALG_MAX) {
+ CERROR("invalid hash algorithm: %u\n", phdr->ph_bulk_hash_alg);
+ return -EPROTO;
+ }
+
+ req->rq_sp_from = phdr->ph_sp;
+ req->rq_flvr.u_bulk.hash.hash_alg = phdr->ph_bulk_hash_alg;
+
+ if (phdr->ph_flags & PLAIN_FL_USER) {
+ if (sptlrpc_unpack_user_desc(msg, PLAIN_PACK_USER_OFF,
+ swabbed)) {
+ CERROR("Mal-formed user descriptor\n");
+ return SECSVC_DROP;
+ }
+
+ req->rq_pack_udesc = 1;
+ req->rq_user_desc = lustre_msg_buf(msg, PLAIN_PACK_USER_OFF, 0);
+ }
+
+ if (phdr->ph_flags & PLAIN_FL_BULK) {
+ if (plain_unpack_bsd(msg, swabbed))
+ return SECSVC_DROP;
+
+ req->rq_pack_bulk = 1;
+ }
+
+ req->rq_reqmsg = lustre_msg_buf(msg, PLAIN_PACK_MSG_OFF, 0);
+ req->rq_reqlen = msg->lm_buflens[PLAIN_PACK_MSG_OFF];
+
+ req->rq_svc_ctx = &plain_svc_ctx;
+ atomic_inc(&req->rq_svc_ctx->sc_refcount);
+
+ return SECSVC_OK;
+}
+
+static
+int plain_alloc_rs(struct ptlrpc_request *req, int msgsize)
+{
+ struct ptlrpc_reply_state *rs;
+ __u32 buflens[PLAIN_PACK_SEGMENTS] = { 0, };
+ int rs_size = sizeof(*rs);
+
+ LASSERT(msgsize % 8 == 0);
+
+ buflens[PLAIN_PACK_HDR_OFF] = sizeof(struct plain_header);
+ buflens[PLAIN_PACK_MSG_OFF] = msgsize;
+
+ if (req->rq_pack_bulk && (req->rq_bulk_read || req->rq_bulk_write))
+ buflens[PLAIN_PACK_BULK_OFF] = PLAIN_BSD_SIZE;
+
+ rs_size += lustre_msg_size_v2(PLAIN_PACK_SEGMENTS, buflens);
+
+ rs = req->rq_reply_state;
+
+ if (rs) {
+ /* pre-allocated */
+ LASSERT(rs->rs_size >= rs_size);
+ } else {
+ OBD_ALLOC_LARGE(rs, rs_size);
+ if (rs == NULL)
+ return -ENOMEM;
+
+ rs->rs_size = rs_size;
+ }
+
+ rs->rs_svc_ctx = req->rq_svc_ctx;
+ atomic_inc(&req->rq_svc_ctx->sc_refcount);
+ rs->rs_repbuf = (struct lustre_msg *) (rs + 1);
+ rs->rs_repbuf_len = rs_size - sizeof(*rs);
+
+ lustre_init_msg_v2(rs->rs_repbuf, PLAIN_PACK_SEGMENTS, buflens, NULL);
+ rs->rs_msg = lustre_msg_buf_v2(rs->rs_repbuf, PLAIN_PACK_MSG_OFF, 0);
+
+ req->rq_reply_state = rs;
+ return 0;
+}
+
+static
+void plain_free_rs(struct ptlrpc_reply_state *rs)
+{
+ LASSERT(atomic_read(&rs->rs_svc_ctx->sc_refcount) > 1);
+ atomic_dec(&rs->rs_svc_ctx->sc_refcount);
+
+ if (!rs->rs_prealloc)
+ OBD_FREE_LARGE(rs, rs->rs_size);
+}
+
+static
+int plain_authorize(struct ptlrpc_request *req)
+{
+ struct ptlrpc_reply_state *rs = req->rq_reply_state;
+ struct lustre_msg_v2 *msg = rs->rs_repbuf;
+ struct plain_header *phdr;
+ int len;
+
+ LASSERT(rs);
+ LASSERT(msg);
+
+ if (req->rq_replen != msg->lm_buflens[PLAIN_PACK_MSG_OFF])
+ len = lustre_shrink_msg(msg, PLAIN_PACK_MSG_OFF,
+ req->rq_replen, 1);
+ else
+ len = lustre_msg_size_v2(msg->lm_bufcount, msg->lm_buflens);
+
+ msg->lm_secflvr = req->rq_flvr.sf_rpc;
+
+ phdr = lustre_msg_buf(msg, PLAIN_PACK_HDR_OFF, 0);
+ phdr->ph_ver = 0;
+ phdr->ph_flags = 0;
+ phdr->ph_bulk_hash_alg = req->rq_flvr.u_bulk.hash.hash_alg;
+
+ if (req->rq_pack_bulk)
+ phdr->ph_flags |= PLAIN_FL_BULK;
+
+ rs->rs_repdata_len = len;
+
+ if (likely(req->rq_packed_final)) {
+ if (lustre_msghdr_get_flags(req->rq_reqmsg) & MSGHDR_AT_SUPPORT)
+ req->rq_reply_off = plain_at_offset;
+ else
+ req->rq_reply_off = 0;
+ } else {
+ unsigned int hsize = 4;
+
+ cfs_crypto_hash_digest(CFS_HASH_ALG_CRC32,
+ lustre_msg_buf(msg, PLAIN_PACK_MSG_OFF, 0),
+ lustre_msg_buflen(msg, PLAIN_PACK_MSG_OFF),
+ NULL, 0, (unsigned char *)&msg->lm_cksum, &hsize);
+ req->rq_reply_off = 0;
+ }
+
+ return 0;
+}
+
+static
+int plain_svc_unwrap_bulk(struct ptlrpc_request *req,
+ struct ptlrpc_bulk_desc *desc)
+{
+ struct ptlrpc_reply_state *rs = req->rq_reply_state;
+ struct ptlrpc_bulk_sec_desc *bsdr, *bsdv;
+ struct plain_bulk_token *tokenr;
+ int rc;
+
+ LASSERT(req->rq_bulk_write);
+ LASSERT(req->rq_pack_bulk);
+
+ bsdr = lustre_msg_buf(req->rq_reqbuf, PLAIN_PACK_BULK_OFF, 0);
+ tokenr = (struct plain_bulk_token *) bsdr->bsd_data;
+ bsdv = lustre_msg_buf(rs->rs_repbuf, PLAIN_PACK_BULK_OFF, 0);
+
+ bsdv->bsd_version = 0;
+ bsdv->bsd_type = SPTLRPC_BULK_DEFAULT;
+ bsdv->bsd_svc = bsdr->bsd_svc;
+ bsdv->bsd_flags = 0;
+
+ if (bsdr->bsd_svc == SPTLRPC_BULK_SVC_NULL)
+ return 0;
+
+ rc = plain_verify_bulk_csum(desc, req->rq_flvr.u_bulk.hash.hash_alg,
+ tokenr);
+ if (rc) {
+ bsdv->bsd_flags |= BSD_FL_ERR;
+ CERROR("bulk write: server verify failed: %d\n", rc);
+ }
+
+ return rc;
+}
+
+static
+int plain_svc_wrap_bulk(struct ptlrpc_request *req,
+ struct ptlrpc_bulk_desc *desc)
+{
+ struct ptlrpc_reply_state *rs = req->rq_reply_state;
+ struct ptlrpc_bulk_sec_desc *bsdr, *bsdv;
+ struct plain_bulk_token *tokenv;
+ int rc;
+
+ LASSERT(req->rq_bulk_read);
+ LASSERT(req->rq_pack_bulk);
+
+ bsdr = lustre_msg_buf(req->rq_reqbuf, PLAIN_PACK_BULK_OFF, 0);
+ bsdv = lustre_msg_buf(rs->rs_repbuf, PLAIN_PACK_BULK_OFF, 0);
+ tokenv = (struct plain_bulk_token *) bsdv->bsd_data;
+
+ bsdv->bsd_version = 0;
+ bsdv->bsd_type = SPTLRPC_BULK_DEFAULT;
+ bsdv->bsd_svc = bsdr->bsd_svc;
+ bsdv->bsd_flags = 0;
+
+ if (bsdr->bsd_svc == SPTLRPC_BULK_SVC_NULL)
+ return 0;
+
+ rc = plain_generate_bulk_csum(desc, req->rq_flvr.u_bulk.hash.hash_alg,
+ tokenv);
+ if (rc) {
+ CERROR("bulk read: server failed to compute checksum: %d\n",
+ rc);
+ } else {
+ if (OBD_FAIL_CHECK(OBD_FAIL_OSC_CHECKSUM_RECEIVE))
+ corrupt_bulk_data(desc);
+ }
+
+ return rc;
+}
+
+static struct ptlrpc_ctx_ops plain_ctx_ops = {
+ .refresh = plain_ctx_refresh,
+ .validate = plain_ctx_validate,
+ .sign = plain_ctx_sign,
+ .verify = plain_ctx_verify,
+ .wrap_bulk = plain_cli_wrap_bulk,
+ .unwrap_bulk = plain_cli_unwrap_bulk,
+};
+
+static struct ptlrpc_sec_cops plain_sec_cops = {
+ .create_sec = plain_create_sec,
+ .destroy_sec = plain_destroy_sec,
+ .kill_sec = plain_kill_sec,
+ .lookup_ctx = plain_lookup_ctx,
+ .release_ctx = plain_release_ctx,
+ .flush_ctx_cache = plain_flush_ctx_cache,
+ .alloc_reqbuf = plain_alloc_reqbuf,
+ .free_reqbuf = plain_free_reqbuf,
+ .alloc_repbuf = plain_alloc_repbuf,
+ .free_repbuf = plain_free_repbuf,
+ .enlarge_reqbuf = plain_enlarge_reqbuf,
+};
+
+static struct ptlrpc_sec_sops plain_sec_sops = {
+ .accept = plain_accept,
+ .alloc_rs = plain_alloc_rs,
+ .authorize = plain_authorize,
+ .free_rs = plain_free_rs,
+ .unwrap_bulk = plain_svc_unwrap_bulk,
+ .wrap_bulk = plain_svc_wrap_bulk,
+};
+
+static struct ptlrpc_sec_policy plain_policy = {
+ .sp_owner = THIS_MODULE,
+ .sp_name = "plain",
+ .sp_policy = SPTLRPC_POLICY_PLAIN,
+ .sp_cops = &plain_sec_cops,
+ .sp_sops = &plain_sec_sops,
+};
+
+int sptlrpc_plain_init(void)
+{
+ __u32 buflens[PLAIN_PACK_SEGMENTS] = { 0, };
+ int rc;
+
+ buflens[PLAIN_PACK_MSG_OFF] = lustre_msg_early_size();
+ plain_at_offset = lustre_msg_size_v2(PLAIN_PACK_SEGMENTS, buflens);
+
+ rc = sptlrpc_register_policy(&plain_policy);
+ if (rc)
+ CERROR("failed to register: %d\n", rc);
+
+ return rc;
+}
+
+void sptlrpc_plain_fini(void)
+{
+ int rc;
+
+ rc = sptlrpc_unregister_policy(&plain_policy);
+ if (rc)
+ CERROR("cannot unregister: %d\n", rc);
+}
diff --git a/drivers/staging/lustre/lustre/ptlrpc/service.c b/drivers/staging/lustre/lustre/ptlrpc/service.c
new file mode 100644
index 000000000..8e6142151
--- /dev/null
+++ b/drivers/staging/lustre/lustre/ptlrpc/service.c
@@ -0,0 +1,3105 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2002, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2010, 2012, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ */
+
+#define DEBUG_SUBSYSTEM S_RPC
+#include "../include/obd_support.h"
+#include "../include/obd_class.h"
+#include "../include/lustre_net.h"
+#include "../include/lu_object.h"
+#include "../../include/linux/lnet/types.h"
+#include "ptlrpc_internal.h"
+
+/* The following are visible and mutable through /sys/module/ptlrpc */
+int test_req_buffer_pressure = 0;
+module_param(test_req_buffer_pressure, int, 0444);
+MODULE_PARM_DESC(test_req_buffer_pressure, "set non-zero to put pressure on request buffer pools");
+module_param(at_min, int, 0644);
+MODULE_PARM_DESC(at_min, "Adaptive timeout minimum (sec)");
+module_param(at_max, int, 0644);
+MODULE_PARM_DESC(at_max, "Adaptive timeout maximum (sec)");
+module_param(at_history, int, 0644);
+MODULE_PARM_DESC(at_history,
+ "Adaptive timeouts remember the slowest event that took place within this period (sec)");
+module_param(at_early_margin, int, 0644);
+MODULE_PARM_DESC(at_early_margin, "How soon before an RPC deadline to send an early reply");
+module_param(at_extra, int, 0644);
+MODULE_PARM_DESC(at_extra, "How much extra time to give with each early reply");
+
+
+/* forward ref */
+static int ptlrpc_server_post_idle_rqbds(struct ptlrpc_service_part *svcpt);
+static void ptlrpc_server_hpreq_fini(struct ptlrpc_request *req);
+static void ptlrpc_at_remove_timed(struct ptlrpc_request *req);
+
+/** Holds a list of all PTLRPC services */
+LIST_HEAD(ptlrpc_all_services);
+/** Used to protect the \e ptlrpc_all_services list */
+struct mutex ptlrpc_all_services_mutex;
+
+struct ptlrpc_request_buffer_desc *
+ptlrpc_alloc_rqbd(struct ptlrpc_service_part *svcpt)
+{
+ struct ptlrpc_service *svc = svcpt->scp_service;
+ struct ptlrpc_request_buffer_desc *rqbd;
+
+ OBD_CPT_ALLOC_PTR(rqbd, svc->srv_cptable, svcpt->scp_cpt);
+ if (rqbd == NULL)
+ return NULL;
+
+ rqbd->rqbd_svcpt = svcpt;
+ rqbd->rqbd_refcount = 0;
+ rqbd->rqbd_cbid.cbid_fn = request_in_callback;
+ rqbd->rqbd_cbid.cbid_arg = rqbd;
+ INIT_LIST_HEAD(&rqbd->rqbd_reqs);
+ OBD_CPT_ALLOC_LARGE(rqbd->rqbd_buffer, svc->srv_cptable,
+ svcpt->scp_cpt, svc->srv_buf_size);
+ if (rqbd->rqbd_buffer == NULL) {
+ OBD_FREE_PTR(rqbd);
+ return NULL;
+ }
+
+ spin_lock(&svcpt->scp_lock);
+ list_add(&rqbd->rqbd_list, &svcpt->scp_rqbd_idle);
+ svcpt->scp_nrqbds_total++;
+ spin_unlock(&svcpt->scp_lock);
+
+ return rqbd;
+}
+
+void
+ptlrpc_free_rqbd(struct ptlrpc_request_buffer_desc *rqbd)
+{
+ struct ptlrpc_service_part *svcpt = rqbd->rqbd_svcpt;
+
+ LASSERT(rqbd->rqbd_refcount == 0);
+ LASSERT(list_empty(&rqbd->rqbd_reqs));
+
+ spin_lock(&svcpt->scp_lock);
+ list_del(&rqbd->rqbd_list);
+ svcpt->scp_nrqbds_total--;
+ spin_unlock(&svcpt->scp_lock);
+
+ OBD_FREE_LARGE(rqbd->rqbd_buffer, svcpt->scp_service->srv_buf_size);
+ OBD_FREE_PTR(rqbd);
+}
+
+int
+ptlrpc_grow_req_bufs(struct ptlrpc_service_part *svcpt, int post)
+{
+ struct ptlrpc_service *svc = svcpt->scp_service;
+ struct ptlrpc_request_buffer_desc *rqbd;
+ int rc = 0;
+ int i;
+
+ if (svcpt->scp_rqbd_allocating)
+ goto try_post;
+
+ spin_lock(&svcpt->scp_lock);
+ /* check again with lock */
+ if (svcpt->scp_rqbd_allocating) {
+ /* NB: we might allow more than one thread in the future */
+ LASSERT(svcpt->scp_rqbd_allocating == 1);
+ spin_unlock(&svcpt->scp_lock);
+ goto try_post;
+ }
+
+ svcpt->scp_rqbd_allocating++;
+ spin_unlock(&svcpt->scp_lock);
+
+
+ for (i = 0; i < svc->srv_nbuf_per_group; i++) {
+ /* NB: another thread might have recycled enough rqbds, we
+ * need to make sure it wouldn't over-allocate, see LU-1212. */
+ if (svcpt->scp_nrqbds_posted >= svc->srv_nbuf_per_group)
+ break;
+
+ rqbd = ptlrpc_alloc_rqbd(svcpt);
+
+ if (rqbd == NULL) {
+ CERROR("%s: Can't allocate request buffer\n",
+ svc->srv_name);
+ rc = -ENOMEM;
+ break;
+ }
+ }
+
+ spin_lock(&svcpt->scp_lock);
+
+ LASSERT(svcpt->scp_rqbd_allocating == 1);
+ svcpt->scp_rqbd_allocating--;
+
+ spin_unlock(&svcpt->scp_lock);
+
+ CDEBUG(D_RPCTRACE,
+ "%s: allocate %d new %d-byte reqbufs (%d/%d left), rc = %d\n",
+ svc->srv_name, i, svc->srv_buf_size, svcpt->scp_nrqbds_posted,
+ svcpt->scp_nrqbds_total, rc);
+
+ try_post:
+ if (post && rc == 0)
+ rc = ptlrpc_server_post_idle_rqbds(svcpt);
+
+ return rc;
+}
+
+/**
+ * Part of Rep-Ack logic.
+ * Puts a lock and its mode into reply state associated to request reply.
+ */
+void
+ptlrpc_save_lock(struct ptlrpc_request *req,
+ struct lustre_handle *lock, int mode, int no_ack)
+{
+ struct ptlrpc_reply_state *rs = req->rq_reply_state;
+ int idx;
+
+ LASSERT(rs != NULL);
+ LASSERT(rs->rs_nlocks < RS_MAX_LOCKS);
+
+ if (req->rq_export->exp_disconnected) {
+ ldlm_lock_decref(lock, mode);
+ } else {
+ idx = rs->rs_nlocks++;
+ rs->rs_locks[idx] = *lock;
+ rs->rs_modes[idx] = mode;
+ rs->rs_difficult = 1;
+ rs->rs_no_ack = !!no_ack;
+ }
+}
+EXPORT_SYMBOL(ptlrpc_save_lock);
+
+
+struct ptlrpc_hr_partition;
+
+struct ptlrpc_hr_thread {
+ int hrt_id; /* thread ID */
+ spinlock_t hrt_lock;
+ wait_queue_head_t hrt_waitq;
+ struct list_head hrt_queue; /* RS queue */
+ struct ptlrpc_hr_partition *hrt_partition;
+};
+
+struct ptlrpc_hr_partition {
+ /* # of started threads */
+ atomic_t hrp_nstarted;
+ /* # of stopped threads */
+ atomic_t hrp_nstopped;
+ /* cpu partition id */
+ int hrp_cpt;
+ /* round-robin rotor for choosing thread */
+ int hrp_rotor;
+ /* total number of threads on this partition */
+ int hrp_nthrs;
+ /* threads table */
+ struct ptlrpc_hr_thread *hrp_thrs;
+};
+
+#define HRT_RUNNING 0
+#define HRT_STOPPING 1
+
+struct ptlrpc_hr_service {
+ /* CPU partition table, it's just cfs_cpt_table for now */
+ struct cfs_cpt_table *hr_cpt_table;
+ /** controller sleep waitq */
+ wait_queue_head_t hr_waitq;
+ unsigned int hr_stopping;
+ /** roundrobin rotor for non-affinity service */
+ unsigned int hr_rotor;
+ /* partition data */
+ struct ptlrpc_hr_partition **hr_partitions;
+};
+
+struct rs_batch {
+ struct list_head rsb_replies;
+ unsigned int rsb_n_replies;
+ struct ptlrpc_service_part *rsb_svcpt;
+};
+
+/** reply handling service. */
+static struct ptlrpc_hr_service ptlrpc_hr;
+
+/**
+ * maximum number of replies scheduled in one batch
+ */
+#define MAX_SCHEDULED 256
+
+/**
+ * Initialize a reply batch.
+ *
+ * \param b batch
+ */
+static void rs_batch_init(struct rs_batch *b)
+{
+ memset(b, 0, sizeof(*b));
+ INIT_LIST_HEAD(&b->rsb_replies);
+}
+
+/**
+ * Choose an hr thread to dispatch requests to.
+ */
+static struct ptlrpc_hr_thread *
+ptlrpc_hr_select(struct ptlrpc_service_part *svcpt)
+{
+ struct ptlrpc_hr_partition *hrp;
+ unsigned int rotor;
+
+ if (svcpt->scp_cpt >= 0 &&
+ svcpt->scp_service->srv_cptable == ptlrpc_hr.hr_cpt_table) {
+ /* directly match partition */
+ hrp = ptlrpc_hr.hr_partitions[svcpt->scp_cpt];
+
+ } else {
+ rotor = ptlrpc_hr.hr_rotor++;
+ rotor %= cfs_cpt_number(ptlrpc_hr.hr_cpt_table);
+
+ hrp = ptlrpc_hr.hr_partitions[rotor];
+ }
+
+ rotor = hrp->hrp_rotor++;
+ return &hrp->hrp_thrs[rotor % hrp->hrp_nthrs];
+}
+
+/**
+ * Dispatch all replies accumulated in the batch to one from
+ * dedicated reply handling threads.
+ *
+ * \param b batch
+ */
+static void rs_batch_dispatch(struct rs_batch *b)
+{
+ if (b->rsb_n_replies != 0) {
+ struct ptlrpc_hr_thread *hrt;
+
+ hrt = ptlrpc_hr_select(b->rsb_svcpt);
+
+ spin_lock(&hrt->hrt_lock);
+ list_splice_init(&b->rsb_replies, &hrt->hrt_queue);
+ spin_unlock(&hrt->hrt_lock);
+
+ wake_up(&hrt->hrt_waitq);
+ b->rsb_n_replies = 0;
+ }
+}
+
+/**
+ * Add a reply to a batch.
+ * Add one reply object to a batch, schedule batched replies if overload.
+ *
+ * \param b batch
+ * \param rs reply
+ */
+static void rs_batch_add(struct rs_batch *b, struct ptlrpc_reply_state *rs)
+{
+ struct ptlrpc_service_part *svcpt = rs->rs_svcpt;
+
+ if (svcpt != b->rsb_svcpt || b->rsb_n_replies >= MAX_SCHEDULED) {
+ if (b->rsb_svcpt != NULL) {
+ rs_batch_dispatch(b);
+ spin_unlock(&b->rsb_svcpt->scp_rep_lock);
+ }
+ spin_lock(&svcpt->scp_rep_lock);
+ b->rsb_svcpt = svcpt;
+ }
+ spin_lock(&rs->rs_lock);
+ rs->rs_scheduled_ever = 1;
+ if (rs->rs_scheduled == 0) {
+ list_move(&rs->rs_list, &b->rsb_replies);
+ rs->rs_scheduled = 1;
+ b->rsb_n_replies++;
+ }
+ rs->rs_committed = 1;
+ spin_unlock(&rs->rs_lock);
+}
+
+/**
+ * Reply batch finalization.
+ * Dispatch remaining replies from the batch
+ * and release remaining spinlock.
+ *
+ * \param b batch
+ */
+static void rs_batch_fini(struct rs_batch *b)
+{
+ if (b->rsb_svcpt != NULL) {
+ rs_batch_dispatch(b);
+ spin_unlock(&b->rsb_svcpt->scp_rep_lock);
+ }
+}
+
+#define DECLARE_RS_BATCH(b) struct rs_batch b
+
+
+/**
+ * Put reply state into a queue for processing because we received
+ * ACK from the client
+ */
+void ptlrpc_dispatch_difficult_reply(struct ptlrpc_reply_state *rs)
+{
+ struct ptlrpc_hr_thread *hrt;
+
+ LASSERT(list_empty(&rs->rs_list));
+
+ hrt = ptlrpc_hr_select(rs->rs_svcpt);
+
+ spin_lock(&hrt->hrt_lock);
+ list_add_tail(&rs->rs_list, &hrt->hrt_queue);
+ spin_unlock(&hrt->hrt_lock);
+
+ wake_up(&hrt->hrt_waitq);
+}
+
+void
+ptlrpc_schedule_difficult_reply(struct ptlrpc_reply_state *rs)
+{
+ assert_spin_locked(&rs->rs_svcpt->scp_rep_lock);
+ assert_spin_locked(&rs->rs_lock);
+ LASSERT(rs->rs_difficult);
+ rs->rs_scheduled_ever = 1; /* flag any notification attempt */
+
+ if (rs->rs_scheduled) { /* being set up or already notified */
+ return;
+ }
+
+ rs->rs_scheduled = 1;
+ list_del_init(&rs->rs_list);
+ ptlrpc_dispatch_difficult_reply(rs);
+}
+EXPORT_SYMBOL(ptlrpc_schedule_difficult_reply);
+
+void ptlrpc_commit_replies(struct obd_export *exp)
+{
+ struct ptlrpc_reply_state *rs, *nxt;
+ DECLARE_RS_BATCH(batch);
+
+ rs_batch_init(&batch);
+ /* Find any replies that have been committed and get their service
+ * to attend to complete them. */
+
+ /* CAVEAT EMPTOR: spinlock ordering!!! */
+ spin_lock(&exp->exp_uncommitted_replies_lock);
+ list_for_each_entry_safe(rs, nxt, &exp->exp_uncommitted_replies,
+ rs_obd_list) {
+ LASSERT(rs->rs_difficult);
+ /* VBR: per-export last_committed */
+ LASSERT(rs->rs_export);
+ if (rs->rs_transno <= exp->exp_last_committed) {
+ list_del_init(&rs->rs_obd_list);
+ rs_batch_add(&batch, rs);
+ }
+ }
+ spin_unlock(&exp->exp_uncommitted_replies_lock);
+ rs_batch_fini(&batch);
+}
+EXPORT_SYMBOL(ptlrpc_commit_replies);
+
+static int
+ptlrpc_server_post_idle_rqbds(struct ptlrpc_service_part *svcpt)
+{
+ struct ptlrpc_request_buffer_desc *rqbd;
+ int rc;
+ int posted = 0;
+
+ for (;;) {
+ spin_lock(&svcpt->scp_lock);
+
+ if (list_empty(&svcpt->scp_rqbd_idle)) {
+ spin_unlock(&svcpt->scp_lock);
+ return posted;
+ }
+
+ rqbd = list_entry(svcpt->scp_rqbd_idle.next,
+ struct ptlrpc_request_buffer_desc,
+ rqbd_list);
+ list_del(&rqbd->rqbd_list);
+
+ /* assume we will post successfully */
+ svcpt->scp_nrqbds_posted++;
+ list_add(&rqbd->rqbd_list, &svcpt->scp_rqbd_posted);
+
+ spin_unlock(&svcpt->scp_lock);
+
+ rc = ptlrpc_register_rqbd(rqbd);
+ if (rc != 0)
+ break;
+
+ posted = 1;
+ }
+
+ spin_lock(&svcpt->scp_lock);
+
+ svcpt->scp_nrqbds_posted--;
+ list_del(&rqbd->rqbd_list);
+ list_add_tail(&rqbd->rqbd_list, &svcpt->scp_rqbd_idle);
+
+ /* Don't complain if no request buffers are posted right now; LNET
+ * won't drop requests because we set the portal lazy! */
+
+ spin_unlock(&svcpt->scp_lock);
+
+ return -1;
+}
+
+static void ptlrpc_at_timer(unsigned long castmeharder)
+{
+ struct ptlrpc_service_part *svcpt;
+
+ svcpt = (struct ptlrpc_service_part *)castmeharder;
+
+ svcpt->scp_at_check = 1;
+ svcpt->scp_at_checktime = cfs_time_current();
+ wake_up(&svcpt->scp_waitq);
+}
+
+static void
+ptlrpc_server_nthreads_check(struct ptlrpc_service *svc,
+ struct ptlrpc_service_conf *conf)
+{
+ struct ptlrpc_service_thr_conf *tc = &conf->psc_thr;
+ unsigned init;
+ unsigned total;
+ unsigned nthrs;
+ int weight;
+
+ /*
+ * Common code for estimating & validating threads number.
+ * CPT affinity service could have percpt thread-pool instead
+ * of a global thread-pool, which means user might not always
+ * get the threads number they give it in conf::tc_nthrs_user
+ * even they did set. It's because we need to validate threads
+ * number for each CPT to guarantee each pool will have enough
+ * threads to keep the service healthy.
+ */
+ init = PTLRPC_NTHRS_INIT + (svc->srv_ops.so_hpreq_handler != NULL);
+ init = max_t(int, init, tc->tc_nthrs_init);
+
+ /* NB: please see comments in lustre_lnet.h for definition
+ * details of these members */
+ LASSERT(tc->tc_nthrs_max != 0);
+
+ if (tc->tc_nthrs_user != 0) {
+ /* In case there is a reason to test a service with many
+ * threads, we give a less strict check here, it can
+ * be up to 8 * nthrs_max */
+ total = min(tc->tc_nthrs_max * 8, tc->tc_nthrs_user);
+ nthrs = total / svc->srv_ncpts;
+ init = max(init, nthrs);
+ goto out;
+ }
+
+ total = tc->tc_nthrs_max;
+ if (tc->tc_nthrs_base == 0) {
+ /* don't care about base threads number per partition,
+ * this is most for non-affinity service */
+ nthrs = total / svc->srv_ncpts;
+ goto out;
+ }
+
+ nthrs = tc->tc_nthrs_base;
+ if (svc->srv_ncpts == 1) {
+ int i;
+
+ /* NB: Increase the base number if it's single partition
+ * and total number of cores/HTs is larger or equal to 4.
+ * result will always < 2 * nthrs_base */
+ weight = cfs_cpt_weight(svc->srv_cptable, CFS_CPT_ANY);
+ for (i = 1; (weight >> (i + 1)) != 0 && /* >= 4 cores/HTs */
+ (tc->tc_nthrs_base >> i) != 0; i++)
+ nthrs += tc->tc_nthrs_base >> i;
+ }
+
+ if (tc->tc_thr_factor != 0) {
+ int factor = tc->tc_thr_factor;
+ const int fade = 4;
+
+ /*
+ * User wants to increase number of threads with for
+ * each CPU core/HT, most likely the factor is larger then
+ * one thread/core because service threads are supposed to
+ * be blocked by lock or wait for IO.
+ */
+ /*
+ * Amdahl's law says that adding processors wouldn't give
+ * a linear increasing of parallelism, so it's nonsense to
+ * have too many threads no matter how many cores/HTs
+ * there are.
+ */
+ /* weight is # of HTs */
+ if (cpumask_weight(topology_thread_cpumask(0)) > 1) {
+ /* depress thread factor for hyper-thread */
+ factor = factor - (factor >> 1) + (factor >> 3);
+ }
+
+ weight = cfs_cpt_weight(svc->srv_cptable, 0);
+ LASSERT(weight > 0);
+
+ for (; factor > 0 && weight > 0; factor--, weight -= fade)
+ nthrs += min(weight, fade) * factor;
+ }
+
+ if (nthrs * svc->srv_ncpts > tc->tc_nthrs_max) {
+ nthrs = max(tc->tc_nthrs_base,
+ tc->tc_nthrs_max / svc->srv_ncpts);
+ }
+ out:
+ nthrs = max(nthrs, tc->tc_nthrs_init);
+ svc->srv_nthrs_cpt_limit = nthrs;
+ svc->srv_nthrs_cpt_init = init;
+
+ if (nthrs * svc->srv_ncpts > tc->tc_nthrs_max) {
+ CDEBUG(D_OTHER, "%s: This service may have more threads (%d) than the given soft limit (%d)\n",
+ svc->srv_name, nthrs * svc->srv_ncpts,
+ tc->tc_nthrs_max);
+ }
+}
+
+/**
+ * Initialize percpt data for a service
+ */
+static int
+ptlrpc_service_part_init(struct ptlrpc_service *svc,
+ struct ptlrpc_service_part *svcpt, int cpt)
+{
+ struct ptlrpc_at_array *array;
+ int size;
+ int index;
+ int rc;
+
+ svcpt->scp_cpt = cpt;
+ INIT_LIST_HEAD(&svcpt->scp_threads);
+
+ /* rqbd and incoming request queue */
+ spin_lock_init(&svcpt->scp_lock);
+ INIT_LIST_HEAD(&svcpt->scp_rqbd_idle);
+ INIT_LIST_HEAD(&svcpt->scp_rqbd_posted);
+ INIT_LIST_HEAD(&svcpt->scp_req_incoming);
+ init_waitqueue_head(&svcpt->scp_waitq);
+ /* history request & rqbd list */
+ INIT_LIST_HEAD(&svcpt->scp_hist_reqs);
+ INIT_LIST_HEAD(&svcpt->scp_hist_rqbds);
+
+ /* active requests and hp requests */
+ spin_lock_init(&svcpt->scp_req_lock);
+
+ /* reply states */
+ spin_lock_init(&svcpt->scp_rep_lock);
+ INIT_LIST_HEAD(&svcpt->scp_rep_active);
+ INIT_LIST_HEAD(&svcpt->scp_rep_idle);
+ init_waitqueue_head(&svcpt->scp_rep_waitq);
+ atomic_set(&svcpt->scp_nreps_difficult, 0);
+
+ /* adaptive timeout */
+ spin_lock_init(&svcpt->scp_at_lock);
+ array = &svcpt->scp_at_array;
+
+ size = at_est2timeout(at_max);
+ array->paa_size = size;
+ array->paa_count = 0;
+ array->paa_deadline = -1;
+
+ /* allocate memory for scp_at_array (ptlrpc_at_array) */
+ OBD_CPT_ALLOC(array->paa_reqs_array,
+ svc->srv_cptable, cpt, sizeof(struct list_head) * size);
+ if (array->paa_reqs_array == NULL)
+ return -ENOMEM;
+
+ for (index = 0; index < size; index++)
+ INIT_LIST_HEAD(&array->paa_reqs_array[index]);
+
+ OBD_CPT_ALLOC(array->paa_reqs_count,
+ svc->srv_cptable, cpt, sizeof(__u32) * size);
+ if (array->paa_reqs_count == NULL)
+ goto failed;
+
+ cfs_timer_init(&svcpt->scp_at_timer, ptlrpc_at_timer, svcpt);
+ /* At SOW, service time should be quick; 10s seems generous. If client
+ * timeout is less than this, we'll be sending an early reply. */
+ at_init(&svcpt->scp_at_estimate, 10, 0);
+
+ /* assign this before call ptlrpc_grow_req_bufs */
+ svcpt->scp_service = svc;
+ /* Now allocate the request buffers, but don't post them now */
+ rc = ptlrpc_grow_req_bufs(svcpt, 0);
+ /* We shouldn't be under memory pressure at startup, so
+ * fail if we can't allocate all our buffers at this time. */
+ if (rc != 0)
+ goto failed;
+
+ return 0;
+
+ failed:
+ if (array->paa_reqs_count != NULL) {
+ OBD_FREE(array->paa_reqs_count, sizeof(__u32) * size);
+ array->paa_reqs_count = NULL;
+ }
+
+ if (array->paa_reqs_array != NULL) {
+ OBD_FREE(array->paa_reqs_array,
+ sizeof(struct list_head) * array->paa_size);
+ array->paa_reqs_array = NULL;
+ }
+
+ return -ENOMEM;
+}
+
+/**
+ * Initialize service on a given portal.
+ * This includes starting serving threads , allocating and posting rqbds and
+ * so on.
+ */
+struct ptlrpc_service *
+ptlrpc_register_service(struct ptlrpc_service_conf *conf,
+ struct proc_dir_entry *proc_entry)
+{
+ struct ptlrpc_service_cpt_conf *cconf = &conf->psc_cpt;
+ struct ptlrpc_service *service;
+ struct ptlrpc_service_part *svcpt;
+ struct cfs_cpt_table *cptable;
+ __u32 *cpts = NULL;
+ int ncpts;
+ int cpt;
+ int rc;
+ int i;
+
+ LASSERT(conf->psc_buf.bc_nbufs > 0);
+ LASSERT(conf->psc_buf.bc_buf_size >=
+ conf->psc_buf.bc_req_max_size + SPTLRPC_MAX_PAYLOAD);
+ LASSERT(conf->psc_thr.tc_ctx_tags != 0);
+
+ cptable = cconf->cc_cptable;
+ if (cptable == NULL)
+ cptable = cfs_cpt_table;
+
+ if (!conf->psc_thr.tc_cpu_affinity) {
+ ncpts = 1;
+ } else {
+ ncpts = cfs_cpt_number(cptable);
+ if (cconf->cc_pattern != NULL) {
+ struct cfs_expr_list *el;
+
+ rc = cfs_expr_list_parse(cconf->cc_pattern,
+ strlen(cconf->cc_pattern),
+ 0, ncpts - 1, &el);
+ if (rc != 0) {
+ CERROR("%s: invalid CPT pattern string: %s",
+ conf->psc_name, cconf->cc_pattern);
+ return ERR_PTR(-EINVAL);
+ }
+
+ rc = cfs_expr_list_values(el, ncpts, &cpts);
+ cfs_expr_list_free(el);
+ if (rc <= 0) {
+ CERROR("%s: failed to parse CPT array %s: %d\n",
+ conf->psc_name, cconf->cc_pattern, rc);
+ if (cpts != NULL)
+ OBD_FREE(cpts, sizeof(*cpts) * ncpts);
+ return ERR_PTR(rc < 0 ? rc : -EINVAL);
+ }
+ ncpts = rc;
+ }
+ }
+
+ OBD_ALLOC(service, offsetof(struct ptlrpc_service, srv_parts[ncpts]));
+ if (service == NULL) {
+ if (cpts != NULL)
+ OBD_FREE(cpts, sizeof(*cpts) * ncpts);
+ return ERR_PTR(-ENOMEM);
+ }
+
+ service->srv_cptable = cptable;
+ service->srv_cpts = cpts;
+ service->srv_ncpts = ncpts;
+
+ service->srv_cpt_bits = 0; /* it's zero already, easy to read... */
+ while ((1 << service->srv_cpt_bits) < cfs_cpt_number(cptable))
+ service->srv_cpt_bits++;
+
+ /* public members */
+ spin_lock_init(&service->srv_lock);
+ service->srv_name = conf->psc_name;
+ service->srv_watchdog_factor = conf->psc_watchdog_factor;
+ INIT_LIST_HEAD(&service->srv_list); /* for safety of cleanup */
+
+ /* buffer configuration */
+ service->srv_nbuf_per_group = test_req_buffer_pressure ?
+ 1 : conf->psc_buf.bc_nbufs;
+ service->srv_max_req_size = conf->psc_buf.bc_req_max_size +
+ SPTLRPC_MAX_PAYLOAD;
+ service->srv_buf_size = conf->psc_buf.bc_buf_size;
+ service->srv_rep_portal = conf->psc_buf.bc_rep_portal;
+ service->srv_req_portal = conf->psc_buf.bc_req_portal;
+
+ /* Increase max reply size to next power of two */
+ service->srv_max_reply_size = 1;
+ while (service->srv_max_reply_size <
+ conf->psc_buf.bc_rep_max_size + SPTLRPC_MAX_PAYLOAD)
+ service->srv_max_reply_size <<= 1;
+
+ service->srv_thread_name = conf->psc_thr.tc_thr_name;
+ service->srv_ctx_tags = conf->psc_thr.tc_ctx_tags;
+ service->srv_hpreq_ratio = PTLRPC_SVC_HP_RATIO;
+ service->srv_ops = conf->psc_ops;
+
+ for (i = 0; i < ncpts; i++) {
+ if (!conf->psc_thr.tc_cpu_affinity)
+ cpt = CFS_CPT_ANY;
+ else
+ cpt = cpts != NULL ? cpts[i] : i;
+
+ OBD_CPT_ALLOC(svcpt, cptable, cpt, sizeof(*svcpt));
+ if (svcpt == NULL) {
+ rc = -ENOMEM;
+ goto failed;
+ }
+
+ service->srv_parts[i] = svcpt;
+ rc = ptlrpc_service_part_init(service, svcpt, cpt);
+ if (rc != 0)
+ goto failed;
+ }
+
+ ptlrpc_server_nthreads_check(service, conf);
+
+ rc = LNetSetLazyPortal(service->srv_req_portal);
+ LASSERT(rc == 0);
+
+ mutex_lock(&ptlrpc_all_services_mutex);
+ list_add(&service->srv_list, &ptlrpc_all_services);
+ mutex_unlock(&ptlrpc_all_services_mutex);
+
+ if (proc_entry != NULL)
+ ptlrpc_lprocfs_register_service(proc_entry, service);
+
+ rc = ptlrpc_service_nrs_setup(service);
+ if (rc != 0)
+ goto failed;
+
+ CDEBUG(D_NET, "%s: Started, listening on portal %d\n",
+ service->srv_name, service->srv_req_portal);
+
+ rc = ptlrpc_start_threads(service);
+ if (rc != 0) {
+ CERROR("Failed to start threads for service %s: %d\n",
+ service->srv_name, rc);
+ goto failed;
+ }
+
+ return service;
+failed:
+ ptlrpc_unregister_service(service);
+ return ERR_PTR(rc);
+}
+EXPORT_SYMBOL(ptlrpc_register_service);
+
+/**
+ * to actually free the request, must be called without holding svc_lock.
+ * note it's caller's responsibility to unlink req->rq_list.
+ */
+static void ptlrpc_server_free_request(struct ptlrpc_request *req)
+{
+ LASSERT(atomic_read(&req->rq_refcount) == 0);
+ LASSERT(list_empty(&req->rq_timed_list));
+
+ /* DEBUG_REQ() assumes the reply state of a request with a valid
+ * ref will not be destroyed until that reference is dropped. */
+ ptlrpc_req_drop_rs(req);
+
+ sptlrpc_svc_ctx_decref(req);
+
+ if (req != &req->rq_rqbd->rqbd_req) {
+ /* NB request buffers use an embedded
+ * req if the incoming req unlinked the
+ * MD; this isn't one of them! */
+ ptlrpc_request_cache_free(req);
+ }
+}
+
+/**
+ * drop a reference count of the request. if it reaches 0, we either
+ * put it into history list, or free it immediately.
+ */
+void ptlrpc_server_drop_request(struct ptlrpc_request *req)
+{
+ struct ptlrpc_request_buffer_desc *rqbd = req->rq_rqbd;
+ struct ptlrpc_service_part *svcpt = rqbd->rqbd_svcpt;
+ struct ptlrpc_service *svc = svcpt->scp_service;
+ int refcount;
+ struct list_head *tmp;
+ struct list_head *nxt;
+
+ if (!atomic_dec_and_test(&req->rq_refcount))
+ return;
+
+ if (req->rq_at_linked) {
+ spin_lock(&svcpt->scp_at_lock);
+ /* recheck with lock, in case it's unlinked by
+ * ptlrpc_at_check_timed() */
+ if (likely(req->rq_at_linked))
+ ptlrpc_at_remove_timed(req);
+ spin_unlock(&svcpt->scp_at_lock);
+ }
+
+ LASSERT(list_empty(&req->rq_timed_list));
+
+ /* finalize request */
+ if (req->rq_export) {
+ class_export_put(req->rq_export);
+ req->rq_export = NULL;
+ }
+
+ spin_lock(&svcpt->scp_lock);
+
+ list_add(&req->rq_list, &rqbd->rqbd_reqs);
+
+ refcount = --(rqbd->rqbd_refcount);
+ if (refcount == 0) {
+ /* request buffer is now idle: add to history */
+ list_del(&rqbd->rqbd_list);
+
+ list_add_tail(&rqbd->rqbd_list, &svcpt->scp_hist_rqbds);
+ svcpt->scp_hist_nrqbds++;
+
+ /* cull some history?
+ * I expect only about 1 or 2 rqbds need to be recycled here */
+ while (svcpt->scp_hist_nrqbds > svc->srv_hist_nrqbds_cpt_max) {
+ rqbd = list_entry(svcpt->scp_hist_rqbds.next,
+ struct ptlrpc_request_buffer_desc,
+ rqbd_list);
+
+ list_del(&rqbd->rqbd_list);
+ svcpt->scp_hist_nrqbds--;
+
+ /* remove rqbd's reqs from svc's req history while
+ * I've got the service lock */
+ list_for_each(tmp, &rqbd->rqbd_reqs) {
+ req = list_entry(tmp, struct ptlrpc_request,
+ rq_list);
+ /* Track the highest culled req seq */
+ if (req->rq_history_seq >
+ svcpt->scp_hist_seq_culled) {
+ svcpt->scp_hist_seq_culled =
+ req->rq_history_seq;
+ }
+ list_del(&req->rq_history_list);
+ }
+
+ spin_unlock(&svcpt->scp_lock);
+
+ list_for_each_safe(tmp, nxt, &rqbd->rqbd_reqs) {
+ req = list_entry(rqbd->rqbd_reqs.next,
+ struct ptlrpc_request,
+ rq_list);
+ list_del(&req->rq_list);
+ ptlrpc_server_free_request(req);
+ }
+
+ spin_lock(&svcpt->scp_lock);
+ /*
+ * now all reqs including the embedded req has been
+ * disposed, schedule request buffer for re-use.
+ */
+ LASSERT(atomic_read(&rqbd->rqbd_req.rq_refcount) ==
+ 0);
+ list_add_tail(&rqbd->rqbd_list,
+ &svcpt->scp_rqbd_idle);
+ }
+
+ spin_unlock(&svcpt->scp_lock);
+ } else if (req->rq_reply_state && req->rq_reply_state->rs_prealloc) {
+ /* If we are low on memory, we are not interested in history */
+ list_del(&req->rq_list);
+ list_del_init(&req->rq_history_list);
+
+ /* Track the highest culled req seq */
+ if (req->rq_history_seq > svcpt->scp_hist_seq_culled)
+ svcpt->scp_hist_seq_culled = req->rq_history_seq;
+
+ spin_unlock(&svcpt->scp_lock);
+
+ ptlrpc_server_free_request(req);
+ } else {
+ spin_unlock(&svcpt->scp_lock);
+ }
+}
+
+/** Change request export and move hp request from old export to new */
+void ptlrpc_request_change_export(struct ptlrpc_request *req,
+ struct obd_export *export)
+{
+ if (req->rq_export != NULL) {
+ if (!list_empty(&req->rq_exp_list)) {
+ /* remove rq_exp_list from last export */
+ spin_lock_bh(&req->rq_export->exp_rpc_lock);
+ list_del_init(&req->rq_exp_list);
+ spin_unlock_bh(&req->rq_export->exp_rpc_lock);
+
+ /* export has one reference already, so it`s safe to
+ * add req to export queue here and get another
+ * reference for request later */
+ spin_lock_bh(&export->exp_rpc_lock);
+ list_add(&req->rq_exp_list, &export->exp_hp_rpcs);
+ spin_unlock_bh(&export->exp_rpc_lock);
+ }
+ class_export_rpc_dec(req->rq_export);
+ class_export_put(req->rq_export);
+ }
+
+ /* request takes one export refcount */
+ req->rq_export = class_export_get(export);
+ class_export_rpc_inc(export);
+
+ return;
+}
+
+/**
+ * to finish a request: stop sending more early replies, and release
+ * the request.
+ */
+static void ptlrpc_server_finish_request(struct ptlrpc_service_part *svcpt,
+ struct ptlrpc_request *req)
+{
+ ptlrpc_server_hpreq_fini(req);
+
+ ptlrpc_server_drop_request(req);
+}
+
+/**
+ * to finish a active request: stop sending more early replies, and release
+ * the request. should be called after we finished handling the request.
+ */
+static void ptlrpc_server_finish_active_request(
+ struct ptlrpc_service_part *svcpt,
+ struct ptlrpc_request *req)
+{
+ spin_lock(&svcpt->scp_req_lock);
+ ptlrpc_nrs_req_stop_nolock(req);
+ svcpt->scp_nreqs_active--;
+ if (req->rq_hp)
+ svcpt->scp_nhreqs_active--;
+ spin_unlock(&svcpt->scp_req_lock);
+
+ ptlrpc_nrs_req_finalize(req);
+
+ if (req->rq_export != NULL)
+ class_export_rpc_dec(req->rq_export);
+
+ ptlrpc_server_finish_request(svcpt, req);
+}
+
+/**
+ * This function makes sure dead exports are evicted in a timely manner.
+ * This function is only called when some export receives a message (i.e.,
+ * the network is up.)
+ */
+static void ptlrpc_update_export_timer(struct obd_export *exp, long extra_delay)
+{
+ struct obd_export *oldest_exp;
+ time_t oldest_time, new_time;
+
+ LASSERT(exp);
+
+ /* Compensate for slow machines, etc, by faking our request time
+ into the future. Although this can break the strict time-ordering
+ of the list, we can be really lazy here - we don't have to evict
+ at the exact right moment. Eventually, all silent exports
+ will make it to the top of the list. */
+
+ /* Do not pay attention on 1sec or smaller renewals. */
+ new_time = get_seconds() + extra_delay;
+ if (exp->exp_last_request_time + 1 /*second */ >= new_time)
+ return;
+
+ exp->exp_last_request_time = new_time;
+
+ /* exports may get disconnected from the chain even though the
+ export has references, so we must keep the spin lock while
+ manipulating the lists */
+ spin_lock(&exp->exp_obd->obd_dev_lock);
+
+ if (list_empty(&exp->exp_obd_chain_timed)) {
+ /* this one is not timed */
+ spin_unlock(&exp->exp_obd->obd_dev_lock);
+ return;
+ }
+
+ list_move_tail(&exp->exp_obd_chain_timed,
+ &exp->exp_obd->obd_exports_timed);
+
+ oldest_exp = list_entry(exp->exp_obd->obd_exports_timed.next,
+ struct obd_export, exp_obd_chain_timed);
+ oldest_time = oldest_exp->exp_last_request_time;
+ spin_unlock(&exp->exp_obd->obd_dev_lock);
+
+ if (exp->exp_obd->obd_recovering) {
+ /* be nice to everyone during recovery */
+ return;
+ }
+
+ /* Note - racing to start/reset the obd_eviction timer is safe */
+ if (exp->exp_obd->obd_eviction_timer == 0) {
+ /* Check if the oldest entry is expired. */
+ if (get_seconds() > (oldest_time + PING_EVICT_TIMEOUT +
+ extra_delay)) {
+ /* We need a second timer, in case the net was down and
+ * it just came back. Since the pinger may skip every
+ * other PING_INTERVAL (see note in ptlrpc_pinger_main),
+ * we better wait for 3. */
+ exp->exp_obd->obd_eviction_timer =
+ get_seconds() + 3 * PING_INTERVAL;
+ CDEBUG(D_HA, "%s: Think about evicting %s from "CFS_TIME_T"\n",
+ exp->exp_obd->obd_name,
+ obd_export_nid2str(oldest_exp), oldest_time);
+ }
+ } else {
+ if (get_seconds() >
+ (exp->exp_obd->obd_eviction_timer + extra_delay)) {
+ /* The evictor won't evict anyone who we've heard from
+ * recently, so we don't have to check before we start
+ * it. */
+ if (!ping_evictor_wake(exp))
+ exp->exp_obd->obd_eviction_timer = 0;
+ }
+ }
+}
+
+/**
+ * Sanity check request \a req.
+ * Return 0 if all is ok, error code otherwise.
+ */
+static int ptlrpc_check_req(struct ptlrpc_request *req)
+{
+ struct obd_device *obd = req->rq_export->exp_obd;
+ int rc = 0;
+
+ if (unlikely(lustre_msg_get_conn_cnt(req->rq_reqmsg) <
+ req->rq_export->exp_conn_cnt)) {
+ DEBUG_REQ(D_RPCTRACE, req,
+ "DROPPING req from old connection %d < %d",
+ lustre_msg_get_conn_cnt(req->rq_reqmsg),
+ req->rq_export->exp_conn_cnt);
+ return -EEXIST;
+ }
+ if (unlikely(obd == NULL || obd->obd_fail)) {
+ /*
+ * Failing over, don't handle any more reqs, send
+ * error response instead.
+ */
+ CDEBUG(D_RPCTRACE, "Dropping req %p for failed obd %s\n",
+ req, (obd != NULL) ? obd->obd_name : "unknown");
+ rc = -ENODEV;
+ } else if (lustre_msg_get_flags(req->rq_reqmsg) &
+ (MSG_REPLAY | MSG_REQ_REPLAY_DONE) &&
+ !obd->obd_recovering) {
+ DEBUG_REQ(D_ERROR, req,
+ "Invalid replay without recovery");
+ class_fail_export(req->rq_export);
+ rc = -ENODEV;
+ } else if (lustre_msg_get_transno(req->rq_reqmsg) != 0 &&
+ !obd->obd_recovering) {
+ DEBUG_REQ(D_ERROR, req, "Invalid req with transno %llu without recovery",
+ lustre_msg_get_transno(req->rq_reqmsg));
+ class_fail_export(req->rq_export);
+ rc = -ENODEV;
+ }
+
+ if (unlikely(rc < 0)) {
+ req->rq_status = rc;
+ ptlrpc_error(req);
+ }
+ return rc;
+}
+
+static void ptlrpc_at_set_timer(struct ptlrpc_service_part *svcpt)
+{
+ struct ptlrpc_at_array *array = &svcpt->scp_at_array;
+ __s32 next;
+
+ if (array->paa_count == 0) {
+ cfs_timer_disarm(&svcpt->scp_at_timer);
+ return;
+ }
+
+ /* Set timer for closest deadline */
+ next = (__s32)(array->paa_deadline - get_seconds() -
+ at_early_margin);
+ if (next <= 0) {
+ ptlrpc_at_timer((unsigned long)svcpt);
+ } else {
+ cfs_timer_arm(&svcpt->scp_at_timer, cfs_time_shift(next));
+ CDEBUG(D_INFO, "armed %s at %+ds\n",
+ svcpt->scp_service->srv_name, next);
+ }
+}
+
+/* Add rpc to early reply check list */
+static int ptlrpc_at_add_timed(struct ptlrpc_request *req)
+{
+ struct ptlrpc_service_part *svcpt = req->rq_rqbd->rqbd_svcpt;
+ struct ptlrpc_at_array *array = &svcpt->scp_at_array;
+ struct ptlrpc_request *rq = NULL;
+ __u32 index;
+
+ if (AT_OFF)
+ return 0;
+
+ if (req->rq_no_reply)
+ return 0;
+
+ if ((lustre_msghdr_get_flags(req->rq_reqmsg) & MSGHDR_AT_SUPPORT) == 0)
+ return -ENOSYS;
+
+ spin_lock(&svcpt->scp_at_lock);
+ LASSERT(list_empty(&req->rq_timed_list));
+
+ index = (unsigned long)req->rq_deadline % array->paa_size;
+ if (array->paa_reqs_count[index] > 0) {
+ /* latest rpcs will have the latest deadlines in the list,
+ * so search backward. */
+ list_for_each_entry_reverse(rq,
+ &array->paa_reqs_array[index],
+ rq_timed_list) {
+ if (req->rq_deadline >= rq->rq_deadline) {
+ list_add(&req->rq_timed_list,
+ &rq->rq_timed_list);
+ break;
+ }
+ }
+ }
+
+ /* Add the request at the head of the list */
+ if (list_empty(&req->rq_timed_list))
+ list_add(&req->rq_timed_list,
+ &array->paa_reqs_array[index]);
+
+ spin_lock(&req->rq_lock);
+ req->rq_at_linked = 1;
+ spin_unlock(&req->rq_lock);
+ req->rq_at_index = index;
+ array->paa_reqs_count[index]++;
+ array->paa_count++;
+ if (array->paa_count == 1 || array->paa_deadline > req->rq_deadline) {
+ array->paa_deadline = req->rq_deadline;
+ ptlrpc_at_set_timer(svcpt);
+ }
+ spin_unlock(&svcpt->scp_at_lock);
+
+ return 0;
+}
+
+static void
+ptlrpc_at_remove_timed(struct ptlrpc_request *req)
+{
+ struct ptlrpc_at_array *array;
+
+ array = &req->rq_rqbd->rqbd_svcpt->scp_at_array;
+
+ /* NB: must call with hold svcpt::scp_at_lock */
+ LASSERT(!list_empty(&req->rq_timed_list));
+ list_del_init(&req->rq_timed_list);
+
+ spin_lock(&req->rq_lock);
+ req->rq_at_linked = 0;
+ spin_unlock(&req->rq_lock);
+
+ array->paa_reqs_count[req->rq_at_index]--;
+ array->paa_count--;
+}
+
+static int ptlrpc_at_send_early_reply(struct ptlrpc_request *req)
+{
+ struct ptlrpc_service_part *svcpt = req->rq_rqbd->rqbd_svcpt;
+ struct ptlrpc_request *reqcopy;
+ struct lustre_msg *reqmsg;
+ long olddl = req->rq_deadline - get_seconds();
+ time_t newdl;
+ int rc;
+
+ /* deadline is when the client expects us to reply, margin is the
+ difference between clients' and servers' expectations */
+ DEBUG_REQ(D_ADAPTTO, req,
+ "%ssending early reply (deadline %+lds, margin %+lds) for %d+%d",
+ AT_OFF ? "AT off - not " : "",
+ olddl, olddl - at_get(&svcpt->scp_at_estimate),
+ at_get(&svcpt->scp_at_estimate), at_extra);
+
+ if (AT_OFF)
+ return 0;
+
+ if (olddl < 0) {
+ DEBUG_REQ(D_WARNING, req, "Already past deadline (%+lds), not sending early reply. Consider increasing at_early_margin (%d)?",
+ olddl, at_early_margin);
+
+ /* Return an error so we're not re-added to the timed list. */
+ return -ETIMEDOUT;
+ }
+
+ if (!(lustre_msghdr_get_flags(req->rq_reqmsg) & MSGHDR_AT_SUPPORT)) {
+ DEBUG_REQ(D_INFO, req, "Wanted to ask client for more time, but no AT support");
+ return -ENOSYS;
+ }
+
+ if (req->rq_export &&
+ lustre_msg_get_flags(req->rq_reqmsg) &
+ (MSG_REPLAY | MSG_REQ_REPLAY_DONE | MSG_LOCK_REPLAY_DONE)) {
+ /* During recovery, we don't want to send too many early
+ * replies, but on the other hand we want to make sure the
+ * client has enough time to resend if the rpc is lost. So
+ * during the recovery period send at least 4 early replies,
+ * spacing them every at_extra if we can. at_estimate should
+ * always equal this fixed value during recovery. */
+ at_measured(&svcpt->scp_at_estimate, min(at_extra,
+ req->rq_export->exp_obd->obd_recovery_timeout / 4));
+ } else {
+ /* Fake our processing time into the future to ask the clients
+ * for some extra amount of time */
+ at_measured(&svcpt->scp_at_estimate, at_extra +
+ get_seconds() -
+ req->rq_arrival_time.tv_sec);
+
+ /* Check to see if we've actually increased the deadline -
+ * we may be past adaptive_max */
+ if (req->rq_deadline >= req->rq_arrival_time.tv_sec +
+ at_get(&svcpt->scp_at_estimate)) {
+ DEBUG_REQ(D_WARNING, req, "Couldn't add any time (%ld/%ld), not sending early reply\n",
+ olddl, req->rq_arrival_time.tv_sec +
+ at_get(&svcpt->scp_at_estimate) -
+ get_seconds());
+ return -ETIMEDOUT;
+ }
+ }
+ newdl = get_seconds() + at_get(&svcpt->scp_at_estimate);
+
+ reqcopy = ptlrpc_request_cache_alloc(GFP_NOFS);
+ if (reqcopy == NULL)
+ return -ENOMEM;
+ OBD_ALLOC_LARGE(reqmsg, req->rq_reqlen);
+ if (!reqmsg) {
+ rc = -ENOMEM;
+ goto out_free;
+ }
+
+ *reqcopy = *req;
+ reqcopy->rq_reply_state = NULL;
+ reqcopy->rq_rep_swab_mask = 0;
+ reqcopy->rq_pack_bulk = 0;
+ reqcopy->rq_pack_udesc = 0;
+ reqcopy->rq_packed_final = 0;
+ sptlrpc_svc_ctx_addref(reqcopy);
+ /* We only need the reqmsg for the magic */
+ reqcopy->rq_reqmsg = reqmsg;
+ memcpy(reqmsg, req->rq_reqmsg, req->rq_reqlen);
+
+ LASSERT(atomic_read(&req->rq_refcount));
+ /** if it is last refcount then early reply isn't needed */
+ if (atomic_read(&req->rq_refcount) == 1) {
+ DEBUG_REQ(D_ADAPTTO, reqcopy, "Normal reply already sent out, abort sending early reply\n");
+ rc = -EINVAL;
+ goto out;
+ }
+
+ /* Connection ref */
+ reqcopy->rq_export = class_conn2export(
+ lustre_msg_get_handle(reqcopy->rq_reqmsg));
+ if (reqcopy->rq_export == NULL) {
+ rc = -ENODEV;
+ goto out;
+ }
+
+ /* RPC ref */
+ class_export_rpc_inc(reqcopy->rq_export);
+ if (reqcopy->rq_export->exp_obd &&
+ reqcopy->rq_export->exp_obd->obd_fail) {
+ rc = -ENODEV;
+ goto out_put;
+ }
+
+ rc = lustre_pack_reply_flags(reqcopy, 1, NULL, NULL, LPRFL_EARLY_REPLY);
+ if (rc)
+ goto out_put;
+
+ rc = ptlrpc_send_reply(reqcopy, PTLRPC_REPLY_EARLY);
+
+ if (!rc) {
+ /* Adjust our own deadline to what we told the client */
+ req->rq_deadline = newdl;
+ req->rq_early_count++; /* number sent, server side */
+ } else {
+ DEBUG_REQ(D_ERROR, req, "Early reply send failed %d", rc);
+ }
+
+ /* Free the (early) reply state from lustre_pack_reply.
+ (ptlrpc_send_reply takes it's own rs ref, so this is safe here) */
+ ptlrpc_req_drop_rs(reqcopy);
+
+out_put:
+ class_export_rpc_dec(reqcopy->rq_export);
+ class_export_put(reqcopy->rq_export);
+out:
+ sptlrpc_svc_ctx_decref(reqcopy);
+ OBD_FREE_LARGE(reqmsg, req->rq_reqlen);
+out_free:
+ ptlrpc_request_cache_free(reqcopy);
+ return rc;
+}
+
+/* Send early replies to everybody expiring within at_early_margin
+ asking for at_extra time */
+static int ptlrpc_at_check_timed(struct ptlrpc_service_part *svcpt)
+{
+ struct ptlrpc_at_array *array = &svcpt->scp_at_array;
+ struct ptlrpc_request *rq, *n;
+ struct list_head work_list;
+ __u32 index, count;
+ time_t deadline;
+ time_t now = get_seconds();
+ long delay;
+ int first, counter = 0;
+
+ spin_lock(&svcpt->scp_at_lock);
+ if (svcpt->scp_at_check == 0) {
+ spin_unlock(&svcpt->scp_at_lock);
+ return 0;
+ }
+ delay = cfs_time_sub(cfs_time_current(), svcpt->scp_at_checktime);
+ svcpt->scp_at_check = 0;
+
+ if (array->paa_count == 0) {
+ spin_unlock(&svcpt->scp_at_lock);
+ return 0;
+ }
+
+ /* The timer went off, but maybe the nearest rpc already completed. */
+ first = array->paa_deadline - now;
+ if (first > at_early_margin) {
+ /* We've still got plenty of time. Reset the timer. */
+ ptlrpc_at_set_timer(svcpt);
+ spin_unlock(&svcpt->scp_at_lock);
+ return 0;
+ }
+
+ /* We're close to a timeout, and we don't know how much longer the
+ server will take. Send early replies to everyone expiring soon. */
+ INIT_LIST_HEAD(&work_list);
+ deadline = -1;
+ index = (unsigned long)array->paa_deadline % array->paa_size;
+ count = array->paa_count;
+ while (count > 0) {
+ count -= array->paa_reqs_count[index];
+ list_for_each_entry_safe(rq, n,
+ &array->paa_reqs_array[index],
+ rq_timed_list) {
+ if (rq->rq_deadline > now + at_early_margin) {
+ /* update the earliest deadline */
+ if (deadline == -1 ||
+ rq->rq_deadline < deadline)
+ deadline = rq->rq_deadline;
+ break;
+ }
+
+ ptlrpc_at_remove_timed(rq);
+ /**
+ * ptlrpc_server_drop_request() may drop
+ * refcount to 0 already. Let's check this and
+ * don't add entry to work_list
+ */
+ if (likely(atomic_inc_not_zero(&rq->rq_refcount)))
+ list_add(&rq->rq_timed_list, &work_list);
+ counter++;
+ }
+
+ if (++index >= array->paa_size)
+ index = 0;
+ }
+ array->paa_deadline = deadline;
+ /* we have a new earliest deadline, restart the timer */
+ ptlrpc_at_set_timer(svcpt);
+
+ spin_unlock(&svcpt->scp_at_lock);
+
+ CDEBUG(D_ADAPTTO, "timeout in %+ds, asking for %d secs on %d early replies\n",
+ first, at_extra, counter);
+ if (first < 0) {
+ /* We're already past request deadlines before we even get a
+ chance to send early replies */
+ LCONSOLE_WARN("%s: This server is not able to keep up with request traffic (cpu-bound).\n",
+ svcpt->scp_service->srv_name);
+ CWARN("earlyQ=%d reqQ=%d recA=%d, svcEst=%d, delay=" CFS_DURATION_T "(jiff)\n",
+ counter, svcpt->scp_nreqs_incoming,
+ svcpt->scp_nreqs_active,
+ at_get(&svcpt->scp_at_estimate), delay);
+ }
+
+ /* we took additional refcount so entries can't be deleted from list, no
+ * locking is needed */
+ while (!list_empty(&work_list)) {
+ rq = list_entry(work_list.next, struct ptlrpc_request,
+ rq_timed_list);
+ list_del_init(&rq->rq_timed_list);
+
+ if (ptlrpc_at_send_early_reply(rq) == 0)
+ ptlrpc_at_add_timed(rq);
+
+ ptlrpc_server_drop_request(rq);
+ }
+
+ return 1; /* return "did_something" for liblustre */
+}
+
+/**
+ * Put the request to the export list if the request may become
+ * a high priority one.
+ */
+static int ptlrpc_server_hpreq_init(struct ptlrpc_service_part *svcpt,
+ struct ptlrpc_request *req)
+{
+ int rc = 0;
+
+ if (svcpt->scp_service->srv_ops.so_hpreq_handler) {
+ rc = svcpt->scp_service->srv_ops.so_hpreq_handler(req);
+ if (rc < 0)
+ return rc;
+ LASSERT(rc == 0);
+ }
+ if (req->rq_export && req->rq_ops) {
+ /* Perform request specific check. We should do this check
+ * before the request is added into exp_hp_rpcs list otherwise
+ * it may hit swab race at LU-1044. */
+ if (req->rq_ops->hpreq_check) {
+ rc = req->rq_ops->hpreq_check(req);
+ /**
+ * XXX: Out of all current
+ * ptlrpc_hpreq_ops::hpreq_check(), only
+ * ldlm_cancel_hpreq_check() can return an error code;
+ * other functions assert in similar places, which seems
+ * odd. What also does not seem right is that handlers
+ * for those RPCs do not assert on the same checks, but
+ * rather handle the error cases. e.g. see
+ * ost_rw_hpreq_check(), and ost_brw_read(),
+ * ost_brw_write().
+ */
+ if (rc < 0)
+ return rc;
+ LASSERT(rc == 0 || rc == 1);
+ }
+
+ spin_lock_bh(&req->rq_export->exp_rpc_lock);
+ list_add(&req->rq_exp_list,
+ &req->rq_export->exp_hp_rpcs);
+ spin_unlock_bh(&req->rq_export->exp_rpc_lock);
+ }
+
+ ptlrpc_nrs_req_initialize(svcpt, req, rc);
+
+ return rc;
+}
+
+/** Remove the request from the export list. */
+static void ptlrpc_server_hpreq_fini(struct ptlrpc_request *req)
+{
+ if (req->rq_export && req->rq_ops) {
+ /* refresh lock timeout again so that client has more
+ * room to send lock cancel RPC. */
+ if (req->rq_ops->hpreq_fini)
+ req->rq_ops->hpreq_fini(req);
+
+ spin_lock_bh(&req->rq_export->exp_rpc_lock);
+ list_del_init(&req->rq_exp_list);
+ spin_unlock_bh(&req->rq_export->exp_rpc_lock);
+ }
+}
+
+static int ptlrpc_hpreq_check(struct ptlrpc_request *req)
+{
+ return 1;
+}
+
+static struct ptlrpc_hpreq_ops ptlrpc_hpreq_common = {
+ .hpreq_check = ptlrpc_hpreq_check,
+};
+
+/* Hi-Priority RPC check by RPC operation code. */
+int ptlrpc_hpreq_handler(struct ptlrpc_request *req)
+{
+ int opc = lustre_msg_get_opc(req->rq_reqmsg);
+
+ /* Check for export to let only reconnects for not yet evicted
+ * export to become a HP rpc. */
+ if ((req->rq_export != NULL) &&
+ (opc == OBD_PING || opc == MDS_CONNECT || opc == OST_CONNECT))
+ req->rq_ops = &ptlrpc_hpreq_common;
+
+ return 0;
+}
+EXPORT_SYMBOL(ptlrpc_hpreq_handler);
+
+static int ptlrpc_server_request_add(struct ptlrpc_service_part *svcpt,
+ struct ptlrpc_request *req)
+{
+ int rc;
+
+ rc = ptlrpc_server_hpreq_init(svcpt, req);
+ if (rc < 0)
+ return rc;
+
+ ptlrpc_nrs_req_add(svcpt, req, !!rc);
+
+ return 0;
+}
+
+/**
+ * Allow to handle high priority request
+ * User can call it w/o any lock but need to hold
+ * ptlrpc_service_part::scp_req_lock to get reliable result
+ */
+static bool ptlrpc_server_allow_high(struct ptlrpc_service_part *svcpt,
+ bool force)
+{
+ int running = svcpt->scp_nthrs_running;
+
+ if (!nrs_svcpt_has_hp(svcpt))
+ return false;
+
+ if (force)
+ return true;
+
+ if (unlikely(svcpt->scp_service->srv_req_portal == MDS_REQUEST_PORTAL &&
+ CFS_FAIL_PRECHECK(OBD_FAIL_PTLRPC_CANCEL_RESEND))) {
+ /* leave just 1 thread for normal RPCs */
+ running = PTLRPC_NTHRS_INIT;
+ if (svcpt->scp_service->srv_ops.so_hpreq_handler != NULL)
+ running += 1;
+ }
+
+ if (svcpt->scp_nreqs_active >= running - 1)
+ return false;
+
+ if (svcpt->scp_nhreqs_active == 0)
+ return true;
+
+ return !ptlrpc_nrs_req_pending_nolock(svcpt, false) ||
+ svcpt->scp_hreq_count < svcpt->scp_service->srv_hpreq_ratio;
+}
+
+static bool ptlrpc_server_high_pending(struct ptlrpc_service_part *svcpt,
+ bool force)
+{
+ return ptlrpc_server_allow_high(svcpt, force) &&
+ ptlrpc_nrs_req_pending_nolock(svcpt, true);
+}
+
+/**
+ * Only allow normal priority requests on a service that has a high-priority
+ * queue if forced (i.e. cleanup), if there are other high priority requests
+ * already being processed (i.e. those threads can service more high-priority
+ * requests), or if there are enough idle threads that a later thread can do
+ * a high priority request.
+ * User can call it w/o any lock but need to hold
+ * ptlrpc_service_part::scp_req_lock to get reliable result
+ */
+static bool ptlrpc_server_allow_normal(struct ptlrpc_service_part *svcpt,
+ bool force)
+{
+ int running = svcpt->scp_nthrs_running;
+ if (unlikely(svcpt->scp_service->srv_req_portal == MDS_REQUEST_PORTAL &&
+ CFS_FAIL_PRECHECK(OBD_FAIL_PTLRPC_CANCEL_RESEND))) {
+ /* leave just 1 thread for normal RPCs */
+ running = PTLRPC_NTHRS_INIT;
+ if (svcpt->scp_service->srv_ops.so_hpreq_handler != NULL)
+ running += 1;
+ }
+
+ if (force ||
+ svcpt->scp_nreqs_active < running - 2)
+ return true;
+
+ if (svcpt->scp_nreqs_active >= running - 1)
+ return false;
+
+ return svcpt->scp_nhreqs_active > 0 || !nrs_svcpt_has_hp(svcpt);
+}
+
+static bool ptlrpc_server_normal_pending(struct ptlrpc_service_part *svcpt,
+ bool force)
+{
+ return ptlrpc_server_allow_normal(svcpt, force) &&
+ ptlrpc_nrs_req_pending_nolock(svcpt, false);
+}
+
+/**
+ * Returns true if there are requests available in incoming
+ * request queue for processing and it is allowed to fetch them.
+ * User can call it w/o any lock but need to hold ptlrpc_service::scp_req_lock
+ * to get reliable result
+ * \see ptlrpc_server_allow_normal
+ * \see ptlrpc_server_allow high
+ */
+static inline bool
+ptlrpc_server_request_pending(struct ptlrpc_service_part *svcpt, bool force)
+{
+ return ptlrpc_server_high_pending(svcpt, force) ||
+ ptlrpc_server_normal_pending(svcpt, force);
+}
+
+/**
+ * Fetch a request for processing from queue of unprocessed requests.
+ * Favors high-priority requests.
+ * Returns a pointer to fetched request.
+ */
+static struct ptlrpc_request *
+ptlrpc_server_request_get(struct ptlrpc_service_part *svcpt, bool force)
+{
+ struct ptlrpc_request *req = NULL;
+
+ spin_lock(&svcpt->scp_req_lock);
+
+ if (ptlrpc_server_high_pending(svcpt, force)) {
+ req = ptlrpc_nrs_req_get_nolock(svcpt, true, force);
+ if (req != NULL) {
+ svcpt->scp_hreq_count++;
+ goto got_request;
+ }
+ }
+
+ if (ptlrpc_server_normal_pending(svcpt, force)) {
+ req = ptlrpc_nrs_req_get_nolock(svcpt, false, force);
+ if (req != NULL) {
+ svcpt->scp_hreq_count = 0;
+ goto got_request;
+ }
+ }
+
+ spin_unlock(&svcpt->scp_req_lock);
+ return NULL;
+
+got_request:
+ svcpt->scp_nreqs_active++;
+ if (req->rq_hp)
+ svcpt->scp_nhreqs_active++;
+
+ spin_unlock(&svcpt->scp_req_lock);
+
+ if (likely(req->rq_export))
+ class_export_rpc_inc(req->rq_export);
+
+ return req;
+}
+
+/**
+ * Handle freshly incoming reqs, add to timed early reply list,
+ * pass on to regular request queue.
+ * All incoming requests pass through here before getting into
+ * ptlrpc_server_handle_req later on.
+ */
+static int
+ptlrpc_server_handle_req_in(struct ptlrpc_service_part *svcpt,
+ struct ptlrpc_thread *thread)
+{
+ struct ptlrpc_service *svc = svcpt->scp_service;
+ struct ptlrpc_request *req;
+ __u32 deadline;
+ int rc;
+
+ spin_lock(&svcpt->scp_lock);
+ if (list_empty(&svcpt->scp_req_incoming)) {
+ spin_unlock(&svcpt->scp_lock);
+ return 0;
+ }
+
+ req = list_entry(svcpt->scp_req_incoming.next,
+ struct ptlrpc_request, rq_list);
+ list_del_init(&req->rq_list);
+ svcpt->scp_nreqs_incoming--;
+ /* Consider this still a "queued" request as far as stats are
+ * concerned */
+ spin_unlock(&svcpt->scp_lock);
+
+ /* go through security check/transform */
+ rc = sptlrpc_svc_unwrap_request(req);
+ switch (rc) {
+ case SECSVC_OK:
+ break;
+ case SECSVC_COMPLETE:
+ target_send_reply(req, 0, OBD_FAIL_MDS_ALL_REPLY_NET);
+ goto err_req;
+ case SECSVC_DROP:
+ goto err_req;
+ default:
+ LBUG();
+ }
+
+ /*
+ * for null-flavored rpc, msg has been unpacked by sptlrpc, although
+ * redo it wouldn't be harmful.
+ */
+ if (SPTLRPC_FLVR_POLICY(req->rq_flvr.sf_rpc) != SPTLRPC_POLICY_NULL) {
+ rc = ptlrpc_unpack_req_msg(req, req->rq_reqlen);
+ if (rc != 0) {
+ CERROR("error unpacking request: ptl %d from %s x%llu\n",
+ svc->srv_req_portal, libcfs_id2str(req->rq_peer),
+ req->rq_xid);
+ goto err_req;
+ }
+ }
+
+ rc = lustre_unpack_req_ptlrpc_body(req, MSG_PTLRPC_BODY_OFF);
+ if (rc) {
+ CERROR("error unpacking ptlrpc body: ptl %d from %s x%llu\n",
+ svc->srv_req_portal, libcfs_id2str(req->rq_peer),
+ req->rq_xid);
+ goto err_req;
+ }
+
+ if (OBD_FAIL_CHECK(OBD_FAIL_PTLRPC_DROP_REQ_OPC) &&
+ lustre_msg_get_opc(req->rq_reqmsg) == cfs_fail_val) {
+ CERROR("drop incoming rpc opc %u, x%llu\n",
+ cfs_fail_val, req->rq_xid);
+ goto err_req;
+ }
+
+ rc = -EINVAL;
+ if (lustre_msg_get_type(req->rq_reqmsg) != PTL_RPC_MSG_REQUEST) {
+ CERROR("wrong packet type received (type=%u) from %s\n",
+ lustre_msg_get_type(req->rq_reqmsg),
+ libcfs_id2str(req->rq_peer));
+ goto err_req;
+ }
+
+ switch (lustre_msg_get_opc(req->rq_reqmsg)) {
+ case MDS_WRITEPAGE:
+ case OST_WRITE:
+ req->rq_bulk_write = 1;
+ break;
+ case MDS_READPAGE:
+ case OST_READ:
+ case MGS_CONFIG_READ:
+ req->rq_bulk_read = 1;
+ break;
+ }
+
+ CDEBUG(D_RPCTRACE, "got req x%llu\n", req->rq_xid);
+
+ req->rq_export = class_conn2export(
+ lustre_msg_get_handle(req->rq_reqmsg));
+ if (req->rq_export) {
+ rc = ptlrpc_check_req(req);
+ if (rc == 0) {
+ rc = sptlrpc_target_export_check(req->rq_export, req);
+ if (rc)
+ DEBUG_REQ(D_ERROR, req, "DROPPING req with illegal security flavor,");
+ }
+
+ if (rc)
+ goto err_req;
+ ptlrpc_update_export_timer(req->rq_export, 0);
+ }
+
+ /* req_in handling should/must be fast */
+ if (get_seconds() - req->rq_arrival_time.tv_sec > 5)
+ DEBUG_REQ(D_WARNING, req, "Slow req_in handling "CFS_DURATION_T"s",
+ cfs_time_sub(get_seconds(),
+ req->rq_arrival_time.tv_sec));
+
+ /* Set rpc server deadline and add it to the timed list */
+ deadline = (lustre_msghdr_get_flags(req->rq_reqmsg) &
+ MSGHDR_AT_SUPPORT) ?
+ /* The max time the client expects us to take */
+ lustre_msg_get_timeout(req->rq_reqmsg) : obd_timeout;
+ req->rq_deadline = req->rq_arrival_time.tv_sec + deadline;
+ if (unlikely(deadline == 0)) {
+ DEBUG_REQ(D_ERROR, req, "Dropping request with 0 timeout");
+ goto err_req;
+ }
+
+ req->rq_svc_thread = thread;
+
+ ptlrpc_at_add_timed(req);
+
+ /* Move it over to the request processing queue */
+ rc = ptlrpc_server_request_add(svcpt, req);
+ if (rc)
+ goto err_req;
+
+ wake_up(&svcpt->scp_waitq);
+ return 1;
+
+err_req:
+ ptlrpc_server_finish_request(svcpt, req);
+
+ return 1;
+}
+
+/**
+ * Main incoming request handling logic.
+ * Calls handler function from service to do actual processing.
+ */
+static int
+ptlrpc_server_handle_request(struct ptlrpc_service_part *svcpt,
+ struct ptlrpc_thread *thread)
+{
+ struct ptlrpc_service *svc = svcpt->scp_service;
+ struct ptlrpc_request *request;
+ struct timeval work_start;
+ struct timeval work_end;
+ long timediff;
+ int rc;
+ int fail_opc = 0;
+
+ request = ptlrpc_server_request_get(svcpt, false);
+ if (request == NULL)
+ return 0;
+
+ if (OBD_FAIL_CHECK(OBD_FAIL_PTLRPC_HPREQ_NOTIMEOUT))
+ fail_opc = OBD_FAIL_PTLRPC_HPREQ_NOTIMEOUT;
+ else if (OBD_FAIL_CHECK(OBD_FAIL_PTLRPC_HPREQ_TIMEOUT))
+ fail_opc = OBD_FAIL_PTLRPC_HPREQ_TIMEOUT;
+
+ if (unlikely(fail_opc)) {
+ if (request->rq_export && request->rq_ops)
+ OBD_FAIL_TIMEOUT(fail_opc, 4);
+ }
+
+ ptlrpc_rqphase_move(request, RQ_PHASE_INTERPRET);
+
+ if (OBD_FAIL_CHECK(OBD_FAIL_PTLRPC_DUMP_LOG))
+ libcfs_debug_dumplog();
+
+ do_gettimeofday(&work_start);
+ timediff = cfs_timeval_sub(&work_start, &request->rq_arrival_time,
+ NULL);
+ if (likely(svc->srv_stats != NULL)) {
+ lprocfs_counter_add(svc->srv_stats, PTLRPC_REQWAIT_CNTR,
+ timediff);
+ lprocfs_counter_add(svc->srv_stats, PTLRPC_REQQDEPTH_CNTR,
+ svcpt->scp_nreqs_incoming);
+ lprocfs_counter_add(svc->srv_stats, PTLRPC_REQACTIVE_CNTR,
+ svcpt->scp_nreqs_active);
+ lprocfs_counter_add(svc->srv_stats, PTLRPC_TIMEOUT,
+ at_get(&svcpt->scp_at_estimate));
+ }
+
+ rc = lu_context_init(&request->rq_session, LCT_SESSION | LCT_NOREF);
+ if (rc) {
+ CERROR("Failure to initialize session: %d\n", rc);
+ goto out_req;
+ }
+ request->rq_session.lc_thread = thread;
+ request->rq_session.lc_cookie = 0x5;
+ lu_context_enter(&request->rq_session);
+
+ CDEBUG(D_NET, "got req %llu\n", request->rq_xid);
+
+ request->rq_svc_thread = thread;
+ if (thread)
+ request->rq_svc_thread->t_env->le_ses = &request->rq_session;
+
+ if (likely(request->rq_export)) {
+ if (unlikely(ptlrpc_check_req(request)))
+ goto put_conn;
+ ptlrpc_update_export_timer(request->rq_export, timediff >> 19);
+ }
+
+ /* Discard requests queued for longer than the deadline.
+ The deadline is increased if we send an early reply. */
+ if (get_seconds() > request->rq_deadline) {
+ DEBUG_REQ(D_ERROR, request, "Dropping timed-out request from %s: deadline " CFS_DURATION_T ":" CFS_DURATION_T "s ago\n",
+ libcfs_id2str(request->rq_peer),
+ cfs_time_sub(request->rq_deadline,
+ request->rq_arrival_time.tv_sec),
+ cfs_time_sub(get_seconds(),
+ request->rq_deadline));
+ goto put_conn;
+ }
+
+ CDEBUG(D_RPCTRACE, "Handling RPC pname:cluuid+ref:pid:xid:nid:opc %s:%s+%d:%d:x%llu:%s:%d\n",
+ current_comm(),
+ (request->rq_export ?
+ (char *)request->rq_export->exp_client_uuid.uuid : "0"),
+ (request->rq_export ?
+ atomic_read(&request->rq_export->exp_refcount) : -99),
+ lustre_msg_get_status(request->rq_reqmsg), request->rq_xid,
+ libcfs_id2str(request->rq_peer),
+ lustre_msg_get_opc(request->rq_reqmsg));
+
+ if (lustre_msg_get_opc(request->rq_reqmsg) != OBD_PING)
+ CFS_FAIL_TIMEOUT_MS(OBD_FAIL_PTLRPC_PAUSE_REQ, cfs_fail_val);
+
+ rc = svc->srv_ops.so_req_handler(request);
+
+ ptlrpc_rqphase_move(request, RQ_PHASE_COMPLETE);
+
+put_conn:
+ lu_context_exit(&request->rq_session);
+ lu_context_fini(&request->rq_session);
+
+ if (unlikely(get_seconds() > request->rq_deadline)) {
+ DEBUG_REQ(D_WARNING, request,
+ "Request took longer than estimated ("
+ CFS_DURATION_T":"CFS_DURATION_T
+ "s); client may timeout.",
+ cfs_time_sub(request->rq_deadline,
+ request->rq_arrival_time.tv_sec),
+ cfs_time_sub(get_seconds(),
+ request->rq_deadline));
+ }
+
+ do_gettimeofday(&work_end);
+ timediff = cfs_timeval_sub(&work_end, &work_start, NULL);
+ CDEBUG(D_RPCTRACE, "Handled RPC pname:cluuid+ref:pid:xid:nid:opc %s:%s+%d:%d:x%llu:%s:%d Request processed in %ldus (%ldus total) trans %llu rc %d/%d\n",
+ current_comm(),
+ (request->rq_export ?
+ (char *)request->rq_export->exp_client_uuid.uuid : "0"),
+ (request->rq_export ?
+ atomic_read(&request->rq_export->exp_refcount) : -99),
+ lustre_msg_get_status(request->rq_reqmsg),
+ request->rq_xid,
+ libcfs_id2str(request->rq_peer),
+ lustre_msg_get_opc(request->rq_reqmsg),
+ timediff,
+ cfs_timeval_sub(&work_end, &request->rq_arrival_time, NULL),
+ (request->rq_repmsg ?
+ lustre_msg_get_transno(request->rq_repmsg) :
+ request->rq_transno),
+ request->rq_status,
+ (request->rq_repmsg ?
+ lustre_msg_get_status(request->rq_repmsg) : -999));
+ if (likely(svc->srv_stats != NULL && request->rq_reqmsg != NULL)) {
+ __u32 op = lustre_msg_get_opc(request->rq_reqmsg);
+ int opc = opcode_offset(op);
+ if (opc > 0 && !(op == LDLM_ENQUEUE || op == MDS_REINT)) {
+ LASSERT(opc < LUSTRE_MAX_OPCODES);
+ lprocfs_counter_add(svc->srv_stats,
+ opc + EXTRA_MAX_OPCODES,
+ timediff);
+ }
+ }
+ if (unlikely(request->rq_early_count)) {
+ DEBUG_REQ(D_ADAPTTO, request,
+ "sent %d early replies before finishing in "
+ CFS_DURATION_T"s",
+ request->rq_early_count,
+ cfs_time_sub(work_end.tv_sec,
+ request->rq_arrival_time.tv_sec));
+ }
+
+out_req:
+ ptlrpc_server_finish_active_request(svcpt, request);
+
+ return 1;
+}
+
+/**
+ * An internal function to process a single reply state object.
+ */
+static int
+ptlrpc_handle_rs(struct ptlrpc_reply_state *rs)
+{
+ struct ptlrpc_service_part *svcpt = rs->rs_svcpt;
+ struct ptlrpc_service *svc = svcpt->scp_service;
+ struct obd_export *exp;
+ int nlocks;
+ int been_handled;
+
+ exp = rs->rs_export;
+
+ LASSERT(rs->rs_difficult);
+ LASSERT(rs->rs_scheduled);
+ LASSERT(list_empty(&rs->rs_list));
+
+ spin_lock(&exp->exp_lock);
+ /* Noop if removed already */
+ list_del_init(&rs->rs_exp_list);
+ spin_unlock(&exp->exp_lock);
+
+ /* The disk commit callback holds exp_uncommitted_replies_lock while it
+ * iterates over newly committed replies, removing them from
+ * exp_uncommitted_replies. It then drops this lock and schedules the
+ * replies it found for handling here.
+ *
+ * We can avoid contention for exp_uncommitted_replies_lock between the
+ * HRT threads and further commit callbacks by checking rs_committed
+ * which is set in the commit callback while it holds both
+ * rs_lock and exp_uncommitted_reples.
+ *
+ * If we see rs_committed clear, the commit callback _may_ not have
+ * handled this reply yet and we race with it to grab
+ * exp_uncommitted_replies_lock before removing the reply from
+ * exp_uncommitted_replies. Note that if we lose the race and the
+ * reply has already been removed, list_del_init() is a noop.
+ *
+ * If we see rs_committed set, we know the commit callback is handling,
+ * or has handled this reply since store reordering might allow us to
+ * see rs_committed set out of sequence. But since this is done
+ * holding rs_lock, we can be sure it has all completed once we hold
+ * rs_lock, which we do right next.
+ */
+ if (!rs->rs_committed) {
+ spin_lock(&exp->exp_uncommitted_replies_lock);
+ list_del_init(&rs->rs_obd_list);
+ spin_unlock(&exp->exp_uncommitted_replies_lock);
+ }
+
+ spin_lock(&rs->rs_lock);
+
+ been_handled = rs->rs_handled;
+ rs->rs_handled = 1;
+
+ nlocks = rs->rs_nlocks; /* atomic "steal", but */
+ rs->rs_nlocks = 0; /* locks still on rs_locks! */
+
+ if (nlocks == 0 && !been_handled) {
+ /* If we see this, we should already have seen the warning
+ * in mds_steal_ack_locks() */
+ CDEBUG(D_HA, "All locks stolen from rs %p x%lld.t%lld o%d NID %s\n",
+ rs,
+ rs->rs_xid, rs->rs_transno, rs->rs_opc,
+ libcfs_nid2str(exp->exp_connection->c_peer.nid));
+ }
+
+ if ((!been_handled && rs->rs_on_net) || nlocks > 0) {
+ spin_unlock(&rs->rs_lock);
+
+ if (!been_handled && rs->rs_on_net) {
+ LNetMDUnlink(rs->rs_md_h);
+ /* Ignore return code; we're racing with completion */
+ }
+
+ while (nlocks-- > 0)
+ ldlm_lock_decref(&rs->rs_locks[nlocks],
+ rs->rs_modes[nlocks]);
+
+ spin_lock(&rs->rs_lock);
+ }
+
+ rs->rs_scheduled = 0;
+
+ if (!rs->rs_on_net) {
+ /* Off the net */
+ spin_unlock(&rs->rs_lock);
+
+ class_export_put(exp);
+ rs->rs_export = NULL;
+ ptlrpc_rs_decref(rs);
+ if (atomic_dec_and_test(&svcpt->scp_nreps_difficult) &&
+ svc->srv_is_stopping)
+ wake_up_all(&svcpt->scp_waitq);
+ return 1;
+ }
+
+ /* still on the net; callback will schedule */
+ spin_unlock(&rs->rs_lock);
+ return 1;
+}
+
+
+static void
+ptlrpc_check_rqbd_pool(struct ptlrpc_service_part *svcpt)
+{
+ int avail = svcpt->scp_nrqbds_posted;
+ int low_water = test_req_buffer_pressure ? 0 :
+ svcpt->scp_service->srv_nbuf_per_group / 2;
+
+ /* NB I'm not locking; just looking. */
+
+ /* CAVEAT EMPTOR: We might be allocating buffers here because we've
+ * allowed the request history to grow out of control. We could put a
+ * sanity check on that here and cull some history if we need the
+ * space. */
+
+ if (avail <= low_water)
+ ptlrpc_grow_req_bufs(svcpt, 1);
+
+ if (svcpt->scp_service->srv_stats) {
+ lprocfs_counter_add(svcpt->scp_service->srv_stats,
+ PTLRPC_REQBUF_AVAIL_CNTR, avail);
+ }
+}
+
+static int
+ptlrpc_retry_rqbds(void *arg)
+{
+ struct ptlrpc_service_part *svcpt = (struct ptlrpc_service_part *)arg;
+
+ svcpt->scp_rqbd_timeout = 0;
+ return -ETIMEDOUT;
+}
+
+static inline int
+ptlrpc_threads_enough(struct ptlrpc_service_part *svcpt)
+{
+ return svcpt->scp_nreqs_active <
+ svcpt->scp_nthrs_running - 1 -
+ (svcpt->scp_service->srv_ops.so_hpreq_handler != NULL);
+}
+
+/**
+ * allowed to create more threads
+ * user can call it w/o any lock but need to hold
+ * ptlrpc_service_part::scp_lock to get reliable result
+ */
+static inline int
+ptlrpc_threads_increasable(struct ptlrpc_service_part *svcpt)
+{
+ return svcpt->scp_nthrs_running +
+ svcpt->scp_nthrs_starting <
+ svcpt->scp_service->srv_nthrs_cpt_limit;
+}
+
+/**
+ * too many requests and allowed to create more threads
+ */
+static inline int
+ptlrpc_threads_need_create(struct ptlrpc_service_part *svcpt)
+{
+ return !ptlrpc_threads_enough(svcpt) &&
+ ptlrpc_threads_increasable(svcpt);
+}
+
+static inline int
+ptlrpc_thread_stopping(struct ptlrpc_thread *thread)
+{
+ return thread_is_stopping(thread) ||
+ thread->t_svcpt->scp_service->srv_is_stopping;
+}
+
+static inline int
+ptlrpc_rqbd_pending(struct ptlrpc_service_part *svcpt)
+{
+ return !list_empty(&svcpt->scp_rqbd_idle) &&
+ svcpt->scp_rqbd_timeout == 0;
+}
+
+static inline int
+ptlrpc_at_check(struct ptlrpc_service_part *svcpt)
+{
+ return svcpt->scp_at_check;
+}
+
+/**
+ * requests wait on preprocessing
+ * user can call it w/o any lock but need to hold
+ * ptlrpc_service_part::scp_lock to get reliable result
+ */
+static inline int
+ptlrpc_server_request_incoming(struct ptlrpc_service_part *svcpt)
+{
+ return !list_empty(&svcpt->scp_req_incoming);
+}
+
+static __attribute__((__noinline__)) int
+ptlrpc_wait_event(struct ptlrpc_service_part *svcpt,
+ struct ptlrpc_thread *thread)
+{
+ /* Don't exit while there are replies to be handled */
+ struct l_wait_info lwi = LWI_TIMEOUT(svcpt->scp_rqbd_timeout,
+ ptlrpc_retry_rqbds, svcpt);
+
+ /* XXX: Add this back when libcfs watchdog is merged upstream
+ lc_watchdog_disable(thread->t_watchdog);
+ */
+
+ cond_resched();
+
+ l_wait_event_exclusive_head(svcpt->scp_waitq,
+ ptlrpc_thread_stopping(thread) ||
+ ptlrpc_server_request_incoming(svcpt) ||
+ ptlrpc_server_request_pending(svcpt, false) ||
+ ptlrpc_rqbd_pending(svcpt) ||
+ ptlrpc_at_check(svcpt), &lwi);
+
+ if (ptlrpc_thread_stopping(thread))
+ return -EINTR;
+
+ /*
+ lc_watchdog_touch(thread->t_watchdog,
+ ptlrpc_server_get_timeout(svcpt));
+ */
+ return 0;
+}
+
+/**
+ * Main thread body for service threads.
+ * Waits in a loop waiting for new requests to process to appear.
+ * Every time an incoming requests is added to its queue, a waitq
+ * is woken up and one of the threads will handle it.
+ */
+static int ptlrpc_main(void *arg)
+{
+ struct ptlrpc_thread *thread = (struct ptlrpc_thread *)arg;
+ struct ptlrpc_service_part *svcpt = thread->t_svcpt;
+ struct ptlrpc_service *svc = svcpt->scp_service;
+ struct ptlrpc_reply_state *rs;
+ struct group_info *ginfo = NULL;
+ struct lu_env *env;
+ int counter = 0, rc = 0;
+
+ thread->t_pid = current_pid();
+ unshare_fs_struct();
+
+ /* NB: we will call cfs_cpt_bind() for all threads, because we
+ * might want to run lustre server only on a subset of system CPUs,
+ * in that case ->scp_cpt is CFS_CPT_ANY */
+ rc = cfs_cpt_bind(svc->srv_cptable, svcpt->scp_cpt);
+ if (rc != 0) {
+ CWARN("%s: failed to bind %s on CPT %d\n",
+ svc->srv_name, thread->t_name, svcpt->scp_cpt);
+ }
+
+ ginfo = groups_alloc(0);
+ if (!ginfo) {
+ rc = -ENOMEM;
+ goto out;
+ }
+
+ set_current_groups(ginfo);
+ put_group_info(ginfo);
+
+ if (svc->srv_ops.so_thr_init != NULL) {
+ rc = svc->srv_ops.so_thr_init(thread);
+ if (rc)
+ goto out;
+ }
+
+ OBD_ALLOC_PTR(env);
+ if (env == NULL) {
+ rc = -ENOMEM;
+ goto out_srv_fini;
+ }
+
+ rc = lu_context_init(&env->le_ctx,
+ svc->srv_ctx_tags|LCT_REMEMBER|LCT_NOREF);
+ if (rc)
+ goto out_srv_fini;
+
+ thread->t_env = env;
+ env->le_ctx.lc_thread = thread;
+ env->le_ctx.lc_cookie = 0x6;
+
+ while (!list_empty(&svcpt->scp_rqbd_idle)) {
+ rc = ptlrpc_server_post_idle_rqbds(svcpt);
+ if (rc >= 0)
+ continue;
+
+ CERROR("Failed to post rqbd for %s on CPT %d: %d\n",
+ svc->srv_name, svcpt->scp_cpt, rc);
+ goto out_srv_fini;
+ }
+
+ /* Alloc reply state structure for this one */
+ OBD_ALLOC_LARGE(rs, svc->srv_max_reply_size);
+ if (!rs) {
+ rc = -ENOMEM;
+ goto out_srv_fini;
+ }
+
+ spin_lock(&svcpt->scp_lock);
+
+ LASSERT(thread_is_starting(thread));
+ thread_clear_flags(thread, SVC_STARTING);
+
+ LASSERT(svcpt->scp_nthrs_starting == 1);
+ svcpt->scp_nthrs_starting--;
+
+ /* SVC_STOPPING may already be set here if someone else is trying
+ * to stop the service while this new thread has been dynamically
+ * forked. We still set SVC_RUNNING to let our creator know that
+ * we are now running, however we will exit as soon as possible */
+ thread_add_flags(thread, SVC_RUNNING);
+ svcpt->scp_nthrs_running++;
+ spin_unlock(&svcpt->scp_lock);
+
+ /* wake up our creator in case he's still waiting. */
+ wake_up(&thread->t_ctl_waitq);
+
+ /*
+ thread->t_watchdog = lc_watchdog_add(ptlrpc_server_get_timeout(svcpt),
+ NULL, NULL);
+ */
+
+ spin_lock(&svcpt->scp_rep_lock);
+ list_add(&rs->rs_list, &svcpt->scp_rep_idle);
+ wake_up(&svcpt->scp_rep_waitq);
+ spin_unlock(&svcpt->scp_rep_lock);
+
+ CDEBUG(D_NET, "service thread %d (#%d) started\n", thread->t_id,
+ svcpt->scp_nthrs_running);
+
+ /* XXX maintain a list of all managed devices: insert here */
+ while (!ptlrpc_thread_stopping(thread)) {
+ if (ptlrpc_wait_event(svcpt, thread))
+ break;
+
+ ptlrpc_check_rqbd_pool(svcpt);
+
+ if (ptlrpc_threads_need_create(svcpt)) {
+ /* Ignore return code - we tried... */
+ ptlrpc_start_thread(svcpt, 0);
+ }
+
+ /* Process all incoming reqs before handling any */
+ if (ptlrpc_server_request_incoming(svcpt)) {
+ lu_context_enter(&env->le_ctx);
+ env->le_ses = NULL;
+ ptlrpc_server_handle_req_in(svcpt, thread);
+ lu_context_exit(&env->le_ctx);
+
+ /* but limit ourselves in case of flood */
+ if (counter++ < 100)
+ continue;
+ counter = 0;
+ }
+
+ if (ptlrpc_at_check(svcpt))
+ ptlrpc_at_check_timed(svcpt);
+
+ if (ptlrpc_server_request_pending(svcpt, false)) {
+ lu_context_enter(&env->le_ctx);
+ ptlrpc_server_handle_request(svcpt, thread);
+ lu_context_exit(&env->le_ctx);
+ }
+
+ if (ptlrpc_rqbd_pending(svcpt) &&
+ ptlrpc_server_post_idle_rqbds(svcpt) < 0) {
+ /* I just failed to repost request buffers.
+ * Wait for a timeout (unless something else
+ * happens) before I try again */
+ svcpt->scp_rqbd_timeout = cfs_time_seconds(1) / 10;
+ CDEBUG(D_RPCTRACE, "Posted buffers: %d\n",
+ svcpt->scp_nrqbds_posted);
+ }
+ }
+
+ /*
+ lc_watchdog_delete(thread->t_watchdog);
+ thread->t_watchdog = NULL;
+ */
+
+out_srv_fini:
+ /*
+ * deconstruct service specific state created by ptlrpc_start_thread()
+ */
+ if (svc->srv_ops.so_thr_done != NULL)
+ svc->srv_ops.so_thr_done(thread);
+
+ if (env != NULL) {
+ lu_context_fini(&env->le_ctx);
+ OBD_FREE_PTR(env);
+ }
+out:
+ CDEBUG(D_RPCTRACE, "service thread [ %p : %u ] %d exiting: rc %d\n",
+ thread, thread->t_pid, thread->t_id, rc);
+
+ spin_lock(&svcpt->scp_lock);
+ if (thread_test_and_clear_flags(thread, SVC_STARTING))
+ svcpt->scp_nthrs_starting--;
+
+ if (thread_test_and_clear_flags(thread, SVC_RUNNING)) {
+ /* must know immediately */
+ svcpt->scp_nthrs_running--;
+ }
+
+ thread->t_id = rc;
+ thread_add_flags(thread, SVC_STOPPED);
+
+ wake_up(&thread->t_ctl_waitq);
+ spin_unlock(&svcpt->scp_lock);
+
+ return rc;
+}
+
+static int hrt_dont_sleep(struct ptlrpc_hr_thread *hrt,
+ struct list_head *replies)
+{
+ int result;
+
+ spin_lock(&hrt->hrt_lock);
+
+ list_splice_init(&hrt->hrt_queue, replies);
+ result = ptlrpc_hr.hr_stopping || !list_empty(replies);
+
+ spin_unlock(&hrt->hrt_lock);
+ return result;
+}
+
+/**
+ * Main body of "handle reply" function.
+ * It processes acked reply states
+ */
+static int ptlrpc_hr_main(void *arg)
+{
+ struct ptlrpc_hr_thread *hrt = (struct ptlrpc_hr_thread *)arg;
+ struct ptlrpc_hr_partition *hrp = hrt->hrt_partition;
+ LIST_HEAD (replies);
+ char threadname[20];
+ int rc;
+
+ snprintf(threadname, sizeof(threadname), "ptlrpc_hr%02d_%03d",
+ hrp->hrp_cpt, hrt->hrt_id);
+ unshare_fs_struct();
+
+ rc = cfs_cpt_bind(ptlrpc_hr.hr_cpt_table, hrp->hrp_cpt);
+ if (rc != 0) {
+ CWARN("Failed to bind %s on CPT %d of CPT table %p: rc = %d\n",
+ threadname, hrp->hrp_cpt, ptlrpc_hr.hr_cpt_table, rc);
+ }
+
+ atomic_inc(&hrp->hrp_nstarted);
+ wake_up(&ptlrpc_hr.hr_waitq);
+
+ while (!ptlrpc_hr.hr_stopping) {
+ l_wait_condition(hrt->hrt_waitq, hrt_dont_sleep(hrt, &replies));
+
+ while (!list_empty(&replies)) {
+ struct ptlrpc_reply_state *rs;
+
+ rs = list_entry(replies.prev,
+ struct ptlrpc_reply_state,
+ rs_list);
+ list_del_init(&rs->rs_list);
+ ptlrpc_handle_rs(rs);
+ }
+ }
+
+ atomic_inc(&hrp->hrp_nstopped);
+ wake_up(&ptlrpc_hr.hr_waitq);
+
+ return 0;
+}
+
+static void ptlrpc_stop_hr_threads(void)
+{
+ struct ptlrpc_hr_partition *hrp;
+ int i;
+ int j;
+
+ ptlrpc_hr.hr_stopping = 1;
+
+ cfs_percpt_for_each(hrp, i, ptlrpc_hr.hr_partitions) {
+ if (hrp->hrp_thrs == NULL)
+ continue; /* uninitialized */
+ for (j = 0; j < hrp->hrp_nthrs; j++)
+ wake_up_all(&hrp->hrp_thrs[j].hrt_waitq);
+ }
+
+ cfs_percpt_for_each(hrp, i, ptlrpc_hr.hr_partitions) {
+ if (hrp->hrp_thrs == NULL)
+ continue; /* uninitialized */
+ wait_event(ptlrpc_hr.hr_waitq,
+ atomic_read(&hrp->hrp_nstopped) ==
+ atomic_read(&hrp->hrp_nstarted));
+ }
+}
+
+static int ptlrpc_start_hr_threads(void)
+{
+ struct ptlrpc_hr_partition *hrp;
+ int i;
+ int j;
+
+ cfs_percpt_for_each(hrp, i, ptlrpc_hr.hr_partitions) {
+ int rc = 0;
+
+ for (j = 0; j < hrp->hrp_nthrs; j++) {
+ struct ptlrpc_hr_thread *hrt = &hrp->hrp_thrs[j];
+ rc = PTR_ERR(kthread_run(ptlrpc_hr_main,
+ &hrp->hrp_thrs[j],
+ "ptlrpc_hr%02d_%03d",
+ hrp->hrp_cpt,
+ hrt->hrt_id));
+ if (IS_ERR_VALUE(rc))
+ break;
+ }
+ wait_event(ptlrpc_hr.hr_waitq,
+ atomic_read(&hrp->hrp_nstarted) == j);
+ if (!IS_ERR_VALUE(rc))
+ continue;
+
+ CERROR("Reply handling thread %d:%d Failed on starting: rc = %d\n",
+ i, j, rc);
+ ptlrpc_stop_hr_threads();
+ return rc;
+ }
+ return 0;
+}
+
+static void ptlrpc_svcpt_stop_threads(struct ptlrpc_service_part *svcpt)
+{
+ struct l_wait_info lwi = { 0 };
+ struct ptlrpc_thread *thread;
+ LIST_HEAD (zombie);
+
+ CDEBUG(D_INFO, "Stopping threads for service %s\n",
+ svcpt->scp_service->srv_name);
+
+ spin_lock(&svcpt->scp_lock);
+ /* let the thread know that we would like it to stop asap */
+ list_for_each_entry(thread, &svcpt->scp_threads, t_link) {
+ CDEBUG(D_INFO, "Stopping thread %s #%u\n",
+ svcpt->scp_service->srv_thread_name, thread->t_id);
+ thread_add_flags(thread, SVC_STOPPING);
+ }
+
+ wake_up_all(&svcpt->scp_waitq);
+
+ while (!list_empty(&svcpt->scp_threads)) {
+ thread = list_entry(svcpt->scp_threads.next,
+ struct ptlrpc_thread, t_link);
+ if (thread_is_stopped(thread)) {
+ list_del(&thread->t_link);
+ list_add(&thread->t_link, &zombie);
+ continue;
+ }
+ spin_unlock(&svcpt->scp_lock);
+
+ CDEBUG(D_INFO, "waiting for stopping-thread %s #%u\n",
+ svcpt->scp_service->srv_thread_name, thread->t_id);
+ l_wait_event(thread->t_ctl_waitq,
+ thread_is_stopped(thread), &lwi);
+
+ spin_lock(&svcpt->scp_lock);
+ }
+
+ spin_unlock(&svcpt->scp_lock);
+
+ while (!list_empty(&zombie)) {
+ thread = list_entry(zombie.next,
+ struct ptlrpc_thread, t_link);
+ list_del(&thread->t_link);
+ OBD_FREE_PTR(thread);
+ }
+}
+
+/**
+ * Stops all threads of a particular service \a svc
+ */
+void ptlrpc_stop_all_threads(struct ptlrpc_service *svc)
+{
+ struct ptlrpc_service_part *svcpt;
+ int i;
+
+ ptlrpc_service_for_each_part(svcpt, i, svc) {
+ if (svcpt->scp_service != NULL)
+ ptlrpc_svcpt_stop_threads(svcpt);
+ }
+}
+EXPORT_SYMBOL(ptlrpc_stop_all_threads);
+
+int ptlrpc_start_threads(struct ptlrpc_service *svc)
+{
+ int rc = 0;
+ int i;
+ int j;
+
+ /* We require 2 threads min, see note in ptlrpc_server_handle_request */
+ LASSERT(svc->srv_nthrs_cpt_init >= PTLRPC_NTHRS_INIT);
+
+ for (i = 0; i < svc->srv_ncpts; i++) {
+ for (j = 0; j < svc->srv_nthrs_cpt_init; j++) {
+ rc = ptlrpc_start_thread(svc->srv_parts[i], 1);
+ if (rc == 0)
+ continue;
+
+ if (rc != -EMFILE)
+ goto failed;
+ /* We have enough threads, don't start more. b=15759 */
+ break;
+ }
+ }
+
+ return 0;
+ failed:
+ CERROR("cannot start %s thread #%d_%d: rc %d\n",
+ svc->srv_thread_name, i, j, rc);
+ ptlrpc_stop_all_threads(svc);
+ return rc;
+}
+EXPORT_SYMBOL(ptlrpc_start_threads);
+
+int ptlrpc_start_thread(struct ptlrpc_service_part *svcpt, int wait)
+{
+ struct l_wait_info lwi = { 0 };
+ struct ptlrpc_thread *thread;
+ struct ptlrpc_service *svc;
+ int rc;
+
+ LASSERT(svcpt != NULL);
+
+ svc = svcpt->scp_service;
+
+ CDEBUG(D_RPCTRACE, "%s[%d] started %d min %d max %d\n",
+ svc->srv_name, svcpt->scp_cpt, svcpt->scp_nthrs_running,
+ svc->srv_nthrs_cpt_init, svc->srv_nthrs_cpt_limit);
+
+ again:
+ if (unlikely(svc->srv_is_stopping))
+ return -ESRCH;
+
+ if (!ptlrpc_threads_increasable(svcpt) ||
+ (OBD_FAIL_CHECK(OBD_FAIL_TGT_TOOMANY_THREADS) &&
+ svcpt->scp_nthrs_running == svc->srv_nthrs_cpt_init - 1))
+ return -EMFILE;
+
+ OBD_CPT_ALLOC_PTR(thread, svc->srv_cptable, svcpt->scp_cpt);
+ if (thread == NULL)
+ return -ENOMEM;
+ init_waitqueue_head(&thread->t_ctl_waitq);
+
+ spin_lock(&svcpt->scp_lock);
+ if (!ptlrpc_threads_increasable(svcpt)) {
+ spin_unlock(&svcpt->scp_lock);
+ OBD_FREE_PTR(thread);
+ return -EMFILE;
+ }
+
+ if (svcpt->scp_nthrs_starting != 0) {
+ /* serialize starting because some modules (obdfilter)
+ * might require unique and contiguous t_id */
+ LASSERT(svcpt->scp_nthrs_starting == 1);
+ spin_unlock(&svcpt->scp_lock);
+ OBD_FREE_PTR(thread);
+ if (wait) {
+ CDEBUG(D_INFO, "Waiting for creating thread %s #%d\n",
+ svc->srv_thread_name, svcpt->scp_thr_nextid);
+ schedule();
+ goto again;
+ }
+
+ CDEBUG(D_INFO, "Creating thread %s #%d race, retry later\n",
+ svc->srv_thread_name, svcpt->scp_thr_nextid);
+ return -EAGAIN;
+ }
+
+ svcpt->scp_nthrs_starting++;
+ thread->t_id = svcpt->scp_thr_nextid++;
+ thread_add_flags(thread, SVC_STARTING);
+ thread->t_svcpt = svcpt;
+
+ list_add(&thread->t_link, &svcpt->scp_threads);
+ spin_unlock(&svcpt->scp_lock);
+
+ if (svcpt->scp_cpt >= 0) {
+ snprintf(thread->t_name, sizeof(thread->t_name), "%s%02d_%03d",
+ svc->srv_thread_name, svcpt->scp_cpt, thread->t_id);
+ } else {
+ snprintf(thread->t_name, sizeof(thread->t_name), "%s_%04d",
+ svc->srv_thread_name, thread->t_id);
+ }
+
+ CDEBUG(D_RPCTRACE, "starting thread '%s'\n", thread->t_name);
+ rc = PTR_ERR(kthread_run(ptlrpc_main, thread, "%s", thread->t_name));
+ if (IS_ERR_VALUE(rc)) {
+ CERROR("cannot start thread '%s': rc %d\n",
+ thread->t_name, rc);
+ spin_lock(&svcpt->scp_lock);
+ --svcpt->scp_nthrs_starting;
+ if (thread_is_stopping(thread)) {
+ /* this ptlrpc_thread is being handled
+ * by ptlrpc_svcpt_stop_threads now
+ */
+ thread_add_flags(thread, SVC_STOPPED);
+ wake_up(&thread->t_ctl_waitq);
+ spin_unlock(&svcpt->scp_lock);
+ } else {
+ list_del(&thread->t_link);
+ spin_unlock(&svcpt->scp_lock);
+ OBD_FREE_PTR(thread);
+ }
+ return rc;
+ }
+
+ if (!wait)
+ return 0;
+
+ l_wait_event(thread->t_ctl_waitq,
+ thread_is_running(thread) || thread_is_stopped(thread),
+ &lwi);
+
+ rc = thread_is_stopped(thread) ? thread->t_id : 0;
+ return rc;
+}
+
+int ptlrpc_hr_init(void)
+{
+ struct ptlrpc_hr_partition *hrp;
+ struct ptlrpc_hr_thread *hrt;
+ int rc;
+ int i;
+ int j;
+ int weight;
+
+ memset(&ptlrpc_hr, 0, sizeof(ptlrpc_hr));
+ ptlrpc_hr.hr_cpt_table = cfs_cpt_table;
+
+ ptlrpc_hr.hr_partitions = cfs_percpt_alloc(ptlrpc_hr.hr_cpt_table,
+ sizeof(*hrp));
+ if (ptlrpc_hr.hr_partitions == NULL)
+ return -ENOMEM;
+
+ init_waitqueue_head(&ptlrpc_hr.hr_waitq);
+
+ weight = cpumask_weight(topology_thread_cpumask(0));
+
+ cfs_percpt_for_each(hrp, i, ptlrpc_hr.hr_partitions) {
+ hrp->hrp_cpt = i;
+
+ atomic_set(&hrp->hrp_nstarted, 0);
+ atomic_set(&hrp->hrp_nstopped, 0);
+
+ hrp->hrp_nthrs = cfs_cpt_weight(ptlrpc_hr.hr_cpt_table, i);
+ hrp->hrp_nthrs /= weight;
+
+ LASSERT(hrp->hrp_nthrs > 0);
+ OBD_CPT_ALLOC(hrp->hrp_thrs, ptlrpc_hr.hr_cpt_table, i,
+ hrp->hrp_nthrs * sizeof(*hrt));
+ if (hrp->hrp_thrs == NULL) {
+ rc = -ENOMEM;
+ goto out;
+ }
+
+ for (j = 0; j < hrp->hrp_nthrs; j++) {
+ hrt = &hrp->hrp_thrs[j];
+
+ hrt->hrt_id = j;
+ hrt->hrt_partition = hrp;
+ init_waitqueue_head(&hrt->hrt_waitq);
+ spin_lock_init(&hrt->hrt_lock);
+ INIT_LIST_HEAD(&hrt->hrt_queue);
+ }
+ }
+
+ rc = ptlrpc_start_hr_threads();
+out:
+ if (rc != 0)
+ ptlrpc_hr_fini();
+ return rc;
+}
+
+void ptlrpc_hr_fini(void)
+{
+ struct ptlrpc_hr_partition *hrp;
+ int i;
+
+ if (ptlrpc_hr.hr_partitions == NULL)
+ return;
+
+ ptlrpc_stop_hr_threads();
+
+ cfs_percpt_for_each(hrp, i, ptlrpc_hr.hr_partitions) {
+ if (hrp->hrp_thrs != NULL) {
+ OBD_FREE(hrp->hrp_thrs,
+ hrp->hrp_nthrs * sizeof(hrp->hrp_thrs[0]));
+ }
+ }
+
+ cfs_percpt_free(ptlrpc_hr.hr_partitions);
+ ptlrpc_hr.hr_partitions = NULL;
+}
+
+
+/**
+ * Wait until all already scheduled replies are processed.
+ */
+static void ptlrpc_wait_replies(struct ptlrpc_service_part *svcpt)
+{
+ while (1) {
+ int rc;
+ struct l_wait_info lwi = LWI_TIMEOUT(cfs_time_seconds(10),
+ NULL, NULL);
+
+ rc = l_wait_event(svcpt->scp_waitq,
+ atomic_read(&svcpt->scp_nreps_difficult) == 0, &lwi);
+ if (rc == 0)
+ break;
+ CWARN("Unexpectedly long timeout %s %p\n",
+ svcpt->scp_service->srv_name, svcpt->scp_service);
+ }
+}
+
+static void
+ptlrpc_service_del_atimer(struct ptlrpc_service *svc)
+{
+ struct ptlrpc_service_part *svcpt;
+ int i;
+
+ /* early disarm AT timer... */
+ ptlrpc_service_for_each_part(svcpt, i, svc) {
+ if (svcpt->scp_service != NULL)
+ cfs_timer_disarm(&svcpt->scp_at_timer);
+ }
+}
+
+static void
+ptlrpc_service_unlink_rqbd(struct ptlrpc_service *svc)
+{
+ struct ptlrpc_service_part *svcpt;
+ struct ptlrpc_request_buffer_desc *rqbd;
+ struct l_wait_info lwi;
+ int rc;
+ int i;
+
+ /* All history will be culled when the next request buffer is
+ * freed in ptlrpc_service_purge_all() */
+ svc->srv_hist_nrqbds_cpt_max = 0;
+
+ rc = LNetClearLazyPortal(svc->srv_req_portal);
+ LASSERT(rc == 0);
+
+ ptlrpc_service_for_each_part(svcpt, i, svc) {
+ if (svcpt->scp_service == NULL)
+ break;
+
+ /* Unlink all the request buffers. This forces a 'final'
+ * event with its 'unlink' flag set for each posted rqbd */
+ list_for_each_entry(rqbd, &svcpt->scp_rqbd_posted,
+ rqbd_list) {
+ rc = LNetMDUnlink(rqbd->rqbd_md_h);
+ LASSERT(rc == 0 || rc == -ENOENT);
+ }
+ }
+
+ ptlrpc_service_for_each_part(svcpt, i, svc) {
+ if (svcpt->scp_service == NULL)
+ break;
+
+ /* Wait for the network to release any buffers
+ * it's currently filling */
+ spin_lock(&svcpt->scp_lock);
+ while (svcpt->scp_nrqbds_posted != 0) {
+ spin_unlock(&svcpt->scp_lock);
+ /* Network access will complete in finite time but
+ * the HUGE timeout lets us CWARN for visibility
+ * of sluggish NALs */
+ lwi = LWI_TIMEOUT_INTERVAL(
+ cfs_time_seconds(LONG_UNLINK),
+ cfs_time_seconds(1), NULL, NULL);
+ rc = l_wait_event(svcpt->scp_waitq,
+ svcpt->scp_nrqbds_posted == 0, &lwi);
+ if (rc == -ETIMEDOUT) {
+ CWARN("Service %s waiting for request buffers\n",
+ svcpt->scp_service->srv_name);
+ }
+ spin_lock(&svcpt->scp_lock);
+ }
+ spin_unlock(&svcpt->scp_lock);
+ }
+}
+
+static void
+ptlrpc_service_purge_all(struct ptlrpc_service *svc)
+{
+ struct ptlrpc_service_part *svcpt;
+ struct ptlrpc_request_buffer_desc *rqbd;
+ struct ptlrpc_request *req;
+ struct ptlrpc_reply_state *rs;
+ int i;
+
+ ptlrpc_service_for_each_part(svcpt, i, svc) {
+ if (svcpt->scp_service == NULL)
+ break;
+
+ spin_lock(&svcpt->scp_rep_lock);
+ while (!list_empty(&svcpt->scp_rep_active)) {
+ rs = list_entry(svcpt->scp_rep_active.next,
+ struct ptlrpc_reply_state, rs_list);
+ spin_lock(&rs->rs_lock);
+ ptlrpc_schedule_difficult_reply(rs);
+ spin_unlock(&rs->rs_lock);
+ }
+ spin_unlock(&svcpt->scp_rep_lock);
+
+ /* purge the request queue. NB No new replies (rqbds
+ * all unlinked) and no service threads, so I'm the only
+ * thread noodling the request queue now */
+ while (!list_empty(&svcpt->scp_req_incoming)) {
+ req = list_entry(svcpt->scp_req_incoming.next,
+ struct ptlrpc_request, rq_list);
+
+ list_del(&req->rq_list);
+ svcpt->scp_nreqs_incoming--;
+ ptlrpc_server_finish_request(svcpt, req);
+ }
+
+ while (ptlrpc_server_request_pending(svcpt, true)) {
+ req = ptlrpc_server_request_get(svcpt, true);
+ ptlrpc_server_finish_active_request(svcpt, req);
+ }
+
+ LASSERT(list_empty(&svcpt->scp_rqbd_posted));
+ LASSERT(svcpt->scp_nreqs_incoming == 0);
+ LASSERT(svcpt->scp_nreqs_active == 0);
+ /* history should have been culled by
+ * ptlrpc_server_finish_request */
+ LASSERT(svcpt->scp_hist_nrqbds == 0);
+
+ /* Now free all the request buffers since nothing
+ * references them any more... */
+
+ while (!list_empty(&svcpt->scp_rqbd_idle)) {
+ rqbd = list_entry(svcpt->scp_rqbd_idle.next,
+ struct ptlrpc_request_buffer_desc,
+ rqbd_list);
+ ptlrpc_free_rqbd(rqbd);
+ }
+ ptlrpc_wait_replies(svcpt);
+
+ while (!list_empty(&svcpt->scp_rep_idle)) {
+ rs = list_entry(svcpt->scp_rep_idle.next,
+ struct ptlrpc_reply_state,
+ rs_list);
+ list_del(&rs->rs_list);
+ OBD_FREE_LARGE(rs, svc->srv_max_reply_size);
+ }
+ }
+}
+
+static void
+ptlrpc_service_free(struct ptlrpc_service *svc)
+{
+ struct ptlrpc_service_part *svcpt;
+ struct ptlrpc_at_array *array;
+ int i;
+
+ ptlrpc_service_for_each_part(svcpt, i, svc) {
+ if (svcpt->scp_service == NULL)
+ break;
+
+ /* In case somebody rearmed this in the meantime */
+ cfs_timer_disarm(&svcpt->scp_at_timer);
+ array = &svcpt->scp_at_array;
+
+ if (array->paa_reqs_array != NULL) {
+ OBD_FREE(array->paa_reqs_array,
+ sizeof(struct list_head) * array->paa_size);
+ array->paa_reqs_array = NULL;
+ }
+
+ if (array->paa_reqs_count != NULL) {
+ OBD_FREE(array->paa_reqs_count,
+ sizeof(__u32) * array->paa_size);
+ array->paa_reqs_count = NULL;
+ }
+ }
+
+ ptlrpc_service_for_each_part(svcpt, i, svc)
+ OBD_FREE_PTR(svcpt);
+
+ if (svc->srv_cpts != NULL)
+ cfs_expr_list_values_free(svc->srv_cpts, svc->srv_ncpts);
+
+ OBD_FREE(svc, offsetof(struct ptlrpc_service,
+ srv_parts[svc->srv_ncpts]));
+}
+
+int ptlrpc_unregister_service(struct ptlrpc_service *service)
+{
+ CDEBUG(D_NET, "%s: tearing down\n", service->srv_name);
+
+ service->srv_is_stopping = 1;
+
+ mutex_lock(&ptlrpc_all_services_mutex);
+ list_del_init(&service->srv_list);
+ mutex_unlock(&ptlrpc_all_services_mutex);
+
+ ptlrpc_service_del_atimer(service);
+ ptlrpc_stop_all_threads(service);
+
+ ptlrpc_service_unlink_rqbd(service);
+ ptlrpc_service_purge_all(service);
+ ptlrpc_service_nrs_cleanup(service);
+
+ ptlrpc_lprocfs_unregister_service(service);
+
+ ptlrpc_service_free(service);
+
+ return 0;
+}
+EXPORT_SYMBOL(ptlrpc_unregister_service);
+
+/**
+ * Returns 0 if the service is healthy.
+ *
+ * Right now, it just checks to make sure that requests aren't languishing
+ * in the queue. We'll use this health check to govern whether a node needs
+ * to be shot, so it's intentionally non-aggressive. */
+int ptlrpc_svcpt_health_check(struct ptlrpc_service_part *svcpt)
+{
+ struct ptlrpc_request *request = NULL;
+ struct timeval right_now;
+ long timediff;
+
+ do_gettimeofday(&right_now);
+
+ spin_lock(&svcpt->scp_req_lock);
+ /* How long has the next entry been waiting? */
+ if (ptlrpc_server_high_pending(svcpt, true))
+ request = ptlrpc_nrs_req_peek_nolock(svcpt, true);
+ else if (ptlrpc_server_normal_pending(svcpt, true))
+ request = ptlrpc_nrs_req_peek_nolock(svcpt, false);
+
+ if (request == NULL) {
+ spin_unlock(&svcpt->scp_req_lock);
+ return 0;
+ }
+
+ timediff = cfs_timeval_sub(&right_now, &request->rq_arrival_time, NULL);
+ spin_unlock(&svcpt->scp_req_lock);
+
+ if ((timediff / ONE_MILLION) >
+ (AT_OFF ? obd_timeout * 3 / 2 : at_max)) {
+ CERROR("%s: unhealthy - request has been waiting %lds\n",
+ svcpt->scp_service->srv_name, timediff / ONE_MILLION);
+ return -1;
+ }
+
+ return 0;
+}
+
+int
+ptlrpc_service_health_check(struct ptlrpc_service *svc)
+{
+ struct ptlrpc_service_part *svcpt;
+ int i;
+
+ if (svc == NULL)
+ return 0;
+
+ ptlrpc_service_for_each_part(svcpt, i, svc) {
+ int rc = ptlrpc_svcpt_health_check(svcpt);
+
+ if (rc != 0)
+ return rc;
+ }
+ return 0;
+}
+EXPORT_SYMBOL(ptlrpc_service_health_check);
diff --git a/drivers/staging/lustre/lustre/ptlrpc/wiretest.c b/drivers/staging/lustre/lustre/ptlrpc/wiretest.c
new file mode 100644
index 000000000..d6d92046c
--- /dev/null
+++ b/drivers/staging/lustre/lustre/ptlrpc/wiretest.c
@@ -0,0 +1,4492 @@
+/*
+ * GPL HEADER START
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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 version 2 for more details (a copy is included
+ * in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; If not, see
+ * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ * GPL HEADER END
+ */
+/*
+ * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 2011, Intel Corporation.
+ */
+/*
+ * This file is part of Lustre, http://www.lustre.org/
+ * Lustre is a trademark of Sun Microsystems, Inc.
+ */
+
+#define DEBUG_SUBSYSTEM S_RPC
+
+#include <linux/fs.h>
+#include <linux/posix_acl_xattr.h>
+
+#include "../include/obd_support.h"
+#include "../include/obd_class.h"
+#include "../include/lustre_net.h"
+#include "../include/lustre_disk.h"
+void lustre_assert_wire_constants(void)
+{
+ /* Wire protocol assertions generated by 'wirecheck'
+ * (make -C lustre/utils newwiretest)
+ * running on Linux centos6-bis 2.6.32-358.0.1.el6-head
+ * #3 SMP Wed Apr 17 17:37:43 CEST 2013
+ * with gcc version 4.4.6 20110731 (Red Hat 4.4.6-3) (GCC)
+ */
+
+ /* Constants... */
+ LASSERTF(PTL_RPC_MSG_REQUEST == 4711, "found %lld\n",
+ (long long)PTL_RPC_MSG_REQUEST);
+ LASSERTF(PTL_RPC_MSG_ERR == 4712, "found %lld\n",
+ (long long)PTL_RPC_MSG_ERR);
+ LASSERTF(PTL_RPC_MSG_REPLY == 4713, "found %lld\n",
+ (long long)PTL_RPC_MSG_REPLY);
+ LASSERTF(MDS_DIR_END_OFF == 0xfffffffffffffffeULL, "found 0x%.16llxULL\n",
+ MDS_DIR_END_OFF);
+ LASSERTF(DEAD_HANDLE_MAGIC == 0xdeadbeefcafebabeULL, "found 0x%.16llxULL\n",
+ DEAD_HANDLE_MAGIC);
+ CLASSERT(MTI_NAME_MAXLEN == 64);
+ LASSERTF(OST_REPLY == 0, "found %lld\n",
+ (long long)OST_REPLY);
+ LASSERTF(OST_GETATTR == 1, "found %lld\n",
+ (long long)OST_GETATTR);
+ LASSERTF(OST_SETATTR == 2, "found %lld\n",
+ (long long)OST_SETATTR);
+ LASSERTF(OST_READ == 3, "found %lld\n",
+ (long long)OST_READ);
+ LASSERTF(OST_WRITE == 4, "found %lld\n",
+ (long long)OST_WRITE);
+ LASSERTF(OST_CREATE == 5, "found %lld\n",
+ (long long)OST_CREATE);
+ LASSERTF(OST_DESTROY == 6, "found %lld\n",
+ (long long)OST_DESTROY);
+ LASSERTF(OST_GET_INFO == 7, "found %lld\n",
+ (long long)OST_GET_INFO);
+ LASSERTF(OST_CONNECT == 8, "found %lld\n",
+ (long long)OST_CONNECT);
+ LASSERTF(OST_DISCONNECT == 9, "found %lld\n",
+ (long long)OST_DISCONNECT);
+ LASSERTF(OST_PUNCH == 10, "found %lld\n",
+ (long long)OST_PUNCH);
+ LASSERTF(OST_OPEN == 11, "found %lld\n",
+ (long long)OST_OPEN);
+ LASSERTF(OST_CLOSE == 12, "found %lld\n",
+ (long long)OST_CLOSE);
+ LASSERTF(OST_STATFS == 13, "found %lld\n",
+ (long long)OST_STATFS);
+ LASSERTF(OST_SYNC == 16, "found %lld\n",
+ (long long)OST_SYNC);
+ LASSERTF(OST_SET_INFO == 17, "found %lld\n",
+ (long long)OST_SET_INFO);
+ LASSERTF(OST_QUOTACHECK == 18, "found %lld\n",
+ (long long)OST_QUOTACHECK);
+ LASSERTF(OST_QUOTACTL == 19, "found %lld\n",
+ (long long)OST_QUOTACTL);
+ LASSERTF(OST_QUOTA_ADJUST_QUNIT == 20, "found %lld\n",
+ (long long)OST_QUOTA_ADJUST_QUNIT);
+ LASSERTF(OST_LAST_OPC == 21, "found %lld\n",
+ (long long)OST_LAST_OPC);
+ LASSERTF(OBD_OBJECT_EOF == 0xffffffffffffffffULL, "found 0x%.16llxULL\n",
+ OBD_OBJECT_EOF);
+ LASSERTF(OST_MIN_PRECREATE == 32, "found %lld\n",
+ (long long)OST_MIN_PRECREATE);
+ LASSERTF(OST_MAX_PRECREATE == 20000, "found %lld\n",
+ (long long)OST_MAX_PRECREATE);
+ LASSERTF(OST_LVB_ERR_INIT == 0xffbadbad80000000ULL, "found 0x%.16llxULL\n",
+ OST_LVB_ERR_INIT);
+ LASSERTF(OST_LVB_ERR_MASK == 0xffbadbad00000000ULL, "found 0x%.16llxULL\n",
+ OST_LVB_ERR_MASK);
+ LASSERTF(MDS_FIRST_OPC == 33, "found %lld\n",
+ (long long)MDS_FIRST_OPC);
+ LASSERTF(MDS_GETATTR == 33, "found %lld\n",
+ (long long)MDS_GETATTR);
+ LASSERTF(MDS_GETATTR_NAME == 34, "found %lld\n",
+ (long long)MDS_GETATTR_NAME);
+ LASSERTF(MDS_CLOSE == 35, "found %lld\n",
+ (long long)MDS_CLOSE);
+ LASSERTF(MDS_REINT == 36, "found %lld\n",
+ (long long)MDS_REINT);
+ LASSERTF(MDS_READPAGE == 37, "found %lld\n",
+ (long long)MDS_READPAGE);
+ LASSERTF(MDS_CONNECT == 38, "found %lld\n",
+ (long long)MDS_CONNECT);
+ LASSERTF(MDS_DISCONNECT == 39, "found %lld\n",
+ (long long)MDS_DISCONNECT);
+ LASSERTF(MDS_GETSTATUS == 40, "found %lld\n",
+ (long long)MDS_GETSTATUS);
+ LASSERTF(MDS_STATFS == 41, "found %lld\n",
+ (long long)MDS_STATFS);
+ LASSERTF(MDS_PIN == 42, "found %lld\n",
+ (long long)MDS_PIN);
+ LASSERTF(MDS_UNPIN == 43, "found %lld\n",
+ (long long)MDS_UNPIN);
+ LASSERTF(MDS_SYNC == 44, "found %lld\n",
+ (long long)MDS_SYNC);
+ LASSERTF(MDS_DONE_WRITING == 45, "found %lld\n",
+ (long long)MDS_DONE_WRITING);
+ LASSERTF(MDS_SET_INFO == 46, "found %lld\n",
+ (long long)MDS_SET_INFO);
+ LASSERTF(MDS_QUOTACHECK == 47, "found %lld\n",
+ (long long)MDS_QUOTACHECK);
+ LASSERTF(MDS_QUOTACTL == 48, "found %lld\n",
+ (long long)MDS_QUOTACTL);
+ LASSERTF(MDS_GETXATTR == 49, "found %lld\n",
+ (long long)MDS_GETXATTR);
+ LASSERTF(MDS_SETXATTR == 50, "found %lld\n",
+ (long long)MDS_SETXATTR);
+ LASSERTF(MDS_WRITEPAGE == 51, "found %lld\n",
+ (long long)MDS_WRITEPAGE);
+ LASSERTF(MDS_IS_SUBDIR == 52, "found %lld\n",
+ (long long)MDS_IS_SUBDIR);
+ LASSERTF(MDS_GET_INFO == 53, "found %lld\n",
+ (long long)MDS_GET_INFO);
+ LASSERTF(MDS_HSM_STATE_GET == 54, "found %lld\n",
+ (long long)MDS_HSM_STATE_GET);
+ LASSERTF(MDS_HSM_STATE_SET == 55, "found %lld\n",
+ (long long)MDS_HSM_STATE_SET);
+ LASSERTF(MDS_HSM_ACTION == 56, "found %lld\n",
+ (long long)MDS_HSM_ACTION);
+ LASSERTF(MDS_HSM_PROGRESS == 57, "found %lld\n",
+ (long long)MDS_HSM_PROGRESS);
+ LASSERTF(MDS_HSM_REQUEST == 58, "found %lld\n",
+ (long long)MDS_HSM_REQUEST);
+ LASSERTF(MDS_HSM_CT_REGISTER == 59, "found %lld\n",
+ (long long)MDS_HSM_CT_REGISTER);
+ LASSERTF(MDS_HSM_CT_UNREGISTER == 60, "found %lld\n",
+ (long long)MDS_HSM_CT_UNREGISTER);
+ LASSERTF(MDS_SWAP_LAYOUTS == 61, "found %lld\n",
+ (long long)MDS_SWAP_LAYOUTS);
+ LASSERTF(MDS_LAST_OPC == 62, "found %lld\n",
+ (long long)MDS_LAST_OPC);
+ LASSERTF(REINT_SETATTR == 1, "found %lld\n",
+ (long long)REINT_SETATTR);
+ LASSERTF(REINT_CREATE == 2, "found %lld\n",
+ (long long)REINT_CREATE);
+ LASSERTF(REINT_LINK == 3, "found %lld\n",
+ (long long)REINT_LINK);
+ LASSERTF(REINT_UNLINK == 4, "found %lld\n",
+ (long long)REINT_UNLINK);
+ LASSERTF(REINT_RENAME == 5, "found %lld\n",
+ (long long)REINT_RENAME);
+ LASSERTF(REINT_OPEN == 6, "found %lld\n",
+ (long long)REINT_OPEN);
+ LASSERTF(REINT_SETXATTR == 7, "found %lld\n",
+ (long long)REINT_SETXATTR);
+ LASSERTF(REINT_RMENTRY == 8, "found %lld\n",
+ (long long)REINT_RMENTRY);
+ LASSERTF(REINT_MAX == 9, "found %lld\n",
+ (long long)REINT_MAX);
+ LASSERTF(DISP_IT_EXECD == 0x00000001UL, "found 0x%.8xUL\n",
+ (unsigned)DISP_IT_EXECD);
+ LASSERTF(DISP_LOOKUP_EXECD == 0x00000002UL, "found 0x%.8xUL\n",
+ (unsigned)DISP_LOOKUP_EXECD);
+ LASSERTF(DISP_LOOKUP_NEG == 0x00000004UL, "found 0x%.8xUL\n",
+ (unsigned)DISP_LOOKUP_NEG);
+ LASSERTF(DISP_LOOKUP_POS == 0x00000008UL, "found 0x%.8xUL\n",
+ (unsigned)DISP_LOOKUP_POS);
+ LASSERTF(DISP_OPEN_CREATE == 0x00000010UL, "found 0x%.8xUL\n",
+ (unsigned)DISP_OPEN_CREATE);
+ LASSERTF(DISP_OPEN_OPEN == 0x00000020UL, "found 0x%.8xUL\n",
+ (unsigned)DISP_OPEN_OPEN);
+ LASSERTF(DISP_ENQ_COMPLETE == 0x00400000UL, "found 0x%.8xUL\n",
+ (unsigned)DISP_ENQ_COMPLETE);
+ LASSERTF(DISP_ENQ_OPEN_REF == 0x00800000UL, "found 0x%.8xUL\n",
+ (unsigned)DISP_ENQ_OPEN_REF);
+ LASSERTF(DISP_ENQ_CREATE_REF == 0x01000000UL, "found 0x%.8xUL\n",
+ (unsigned)DISP_ENQ_CREATE_REF);
+ LASSERTF(DISP_OPEN_LOCK == 0x02000000UL, "found 0x%.8xUL\n",
+ (unsigned)DISP_OPEN_LOCK);
+ LASSERTF(MDS_STATUS_CONN == 1, "found %lld\n",
+ (long long)MDS_STATUS_CONN);
+ LASSERTF(MDS_STATUS_LOV == 2, "found %lld\n",
+ (long long)MDS_STATUS_LOV);
+ LASSERTF(LUSTRE_BFLAG_UNCOMMITTED_WRITES == 1, "found %lld\n",
+ (long long)LUSTRE_BFLAG_UNCOMMITTED_WRITES);
+ LASSERTF(MF_SOM_CHANGE == 0x00000001UL, "found 0x%.8xUL\n",
+ (unsigned)MF_SOM_CHANGE);
+ LASSERTF(MF_EPOCH_OPEN == 0x00000002UL, "found 0x%.8xUL\n",
+ (unsigned)MF_EPOCH_OPEN);
+ LASSERTF(MF_EPOCH_CLOSE == 0x00000004UL, "found 0x%.8xUL\n",
+ (unsigned)MF_EPOCH_CLOSE);
+ LASSERTF(MF_MDC_CANCEL_FID1 == 0x00000008UL, "found 0x%.8xUL\n",
+ (unsigned)MF_MDC_CANCEL_FID1);
+ LASSERTF(MF_MDC_CANCEL_FID2 == 0x00000010UL, "found 0x%.8xUL\n",
+ (unsigned)MF_MDC_CANCEL_FID2);
+ LASSERTF(MF_MDC_CANCEL_FID3 == 0x00000020UL, "found 0x%.8xUL\n",
+ (unsigned)MF_MDC_CANCEL_FID3);
+ LASSERTF(MF_MDC_CANCEL_FID4 == 0x00000040UL, "found 0x%.8xUL\n",
+ (unsigned)MF_MDC_CANCEL_FID4);
+ LASSERTF(MF_SOM_AU == 0x00000080UL, "found 0x%.8xUL\n",
+ (unsigned)MF_SOM_AU);
+ LASSERTF(MF_GETATTR_LOCK == 0x00000100UL, "found 0x%.8xUL\n",
+ (unsigned)MF_GETATTR_LOCK);
+ LASSERTF(MDS_ATTR_MODE == 0x0000000000000001ULL, "found 0x%.16llxULL\n",
+ (long long)MDS_ATTR_MODE);
+ LASSERTF(MDS_ATTR_UID == 0x0000000000000002ULL, "found 0x%.16llxULL\n",
+ (long long)MDS_ATTR_UID);
+ LASSERTF(MDS_ATTR_GID == 0x0000000000000004ULL, "found 0x%.16llxULL\n",
+ (long long)MDS_ATTR_GID);
+ LASSERTF(MDS_ATTR_SIZE == 0x0000000000000008ULL, "found 0x%.16llxULL\n",
+ (long long)MDS_ATTR_SIZE);
+ LASSERTF(MDS_ATTR_ATIME == 0x0000000000000010ULL, "found 0x%.16llxULL\n",
+ (long long)MDS_ATTR_ATIME);
+ LASSERTF(MDS_ATTR_MTIME == 0x0000000000000020ULL, "found 0x%.16llxULL\n",
+ (long long)MDS_ATTR_MTIME);
+ LASSERTF(MDS_ATTR_CTIME == 0x0000000000000040ULL, "found 0x%.16llxULL\n",
+ (long long)MDS_ATTR_CTIME);
+ LASSERTF(MDS_ATTR_ATIME_SET == 0x0000000000000080ULL, "found 0x%.16llxULL\n",
+ (long long)MDS_ATTR_ATIME_SET);
+ LASSERTF(MDS_ATTR_MTIME_SET == 0x0000000000000100ULL, "found 0x%.16llxULL\n",
+ (long long)MDS_ATTR_MTIME_SET);
+ LASSERTF(MDS_ATTR_FORCE == 0x0000000000000200ULL, "found 0x%.16llxULL\n",
+ (long long)MDS_ATTR_FORCE);
+ LASSERTF(MDS_ATTR_ATTR_FLAG == 0x0000000000000400ULL, "found 0x%.16llxULL\n",
+ (long long)MDS_ATTR_ATTR_FLAG);
+ LASSERTF(MDS_ATTR_KILL_SUID == 0x0000000000000800ULL, "found 0x%.16llxULL\n",
+ (long long)MDS_ATTR_KILL_SUID);
+ LASSERTF(MDS_ATTR_KILL_SGID == 0x0000000000001000ULL, "found 0x%.16llxULL\n",
+ (long long)MDS_ATTR_KILL_SGID);
+ LASSERTF(MDS_ATTR_CTIME_SET == 0x0000000000002000ULL, "found 0x%.16llxULL\n",
+ (long long)MDS_ATTR_CTIME_SET);
+ LASSERTF(MDS_ATTR_FROM_OPEN == 0x0000000000004000ULL, "found 0x%.16llxULL\n",
+ (long long)MDS_ATTR_FROM_OPEN);
+ LASSERTF(MDS_ATTR_BLOCKS == 0x0000000000008000ULL, "found 0x%.16llxULL\n",
+ (long long)MDS_ATTR_BLOCKS);
+ LASSERTF(FLD_QUERY == 900, "found %lld\n",
+ (long long)FLD_QUERY);
+ LASSERTF(FLD_FIRST_OPC == 900, "found %lld\n",
+ (long long)FLD_FIRST_OPC);
+ LASSERTF(FLD_LAST_OPC == 901, "found %lld\n",
+ (long long)FLD_LAST_OPC);
+ LASSERTF(SEQ_QUERY == 700, "found %lld\n",
+ (long long)SEQ_QUERY);
+ LASSERTF(SEQ_FIRST_OPC == 700, "found %lld\n",
+ (long long)SEQ_FIRST_OPC);
+ LASSERTF(SEQ_LAST_OPC == 701, "found %lld\n",
+ (long long)SEQ_LAST_OPC);
+ LASSERTF(SEQ_ALLOC_SUPER == 0, "found %lld\n",
+ (long long)SEQ_ALLOC_SUPER);
+ LASSERTF(SEQ_ALLOC_META == 1, "found %lld\n",
+ (long long)SEQ_ALLOC_META);
+ LASSERTF(LDLM_ENQUEUE == 101, "found %lld\n",
+ (long long)LDLM_ENQUEUE);
+ LASSERTF(LDLM_CONVERT == 102, "found %lld\n",
+ (long long)LDLM_CONVERT);
+ LASSERTF(LDLM_CANCEL == 103, "found %lld\n",
+ (long long)LDLM_CANCEL);
+ LASSERTF(LDLM_BL_CALLBACK == 104, "found %lld\n",
+ (long long)LDLM_BL_CALLBACK);
+ LASSERTF(LDLM_CP_CALLBACK == 105, "found %lld\n",
+ (long long)LDLM_CP_CALLBACK);
+ LASSERTF(LDLM_GL_CALLBACK == 106, "found %lld\n",
+ (long long)LDLM_GL_CALLBACK);
+ LASSERTF(LDLM_SET_INFO == 107, "found %lld\n",
+ (long long)LDLM_SET_INFO);
+ LASSERTF(LDLM_LAST_OPC == 108, "found %lld\n",
+ (long long)LDLM_LAST_OPC);
+ LASSERTF(LCK_MINMODE == 0, "found %lld\n",
+ (long long)LCK_MINMODE);
+ LASSERTF(LCK_EX == 1, "found %lld\n",
+ (long long)LCK_EX);
+ LASSERTF(LCK_PW == 2, "found %lld\n",
+ (long long)LCK_PW);
+ LASSERTF(LCK_PR == 4, "found %lld\n",
+ (long long)LCK_PR);
+ LASSERTF(LCK_CW == 8, "found %lld\n",
+ (long long)LCK_CW);
+ LASSERTF(LCK_CR == 16, "found %lld\n",
+ (long long)LCK_CR);
+ LASSERTF(LCK_NL == 32, "found %lld\n",
+ (long long)LCK_NL);
+ LASSERTF(LCK_GROUP == 64, "found %lld\n",
+ (long long)LCK_GROUP);
+ LASSERTF(LCK_COS == 128, "found %lld\n",
+ (long long)LCK_COS);
+ LASSERTF(LCK_MAXMODE == 129, "found %lld\n",
+ (long long)LCK_MAXMODE);
+ LASSERTF(LCK_MODE_NUM == 8, "found %lld\n",
+ (long long)LCK_MODE_NUM);
+ CLASSERT(LDLM_PLAIN == 10);
+ CLASSERT(LDLM_EXTENT == 11);
+ CLASSERT(LDLM_FLOCK == 12);
+ CLASSERT(LDLM_IBITS == 13);
+ CLASSERT(LDLM_MAX_TYPE == 14);
+ CLASSERT(LUSTRE_RES_ID_SEQ_OFF == 0);
+ CLASSERT(LUSTRE_RES_ID_VER_OID_OFF == 1);
+ LASSERTF(UPDATE_OBJ == 1000, "found %lld\n",
+ (long long)UPDATE_OBJ);
+ LASSERTF(UPDATE_LAST_OPC == 1001, "found %lld\n",
+ (long long)UPDATE_LAST_OPC);
+ CLASSERT(LUSTRE_RES_ID_QUOTA_SEQ_OFF == 2);
+ CLASSERT(LUSTRE_RES_ID_QUOTA_VER_OID_OFF == 3);
+ CLASSERT(LUSTRE_RES_ID_HSH_OFF == 3);
+ CLASSERT(LQUOTA_TYPE_USR == 0);
+ CLASSERT(LQUOTA_TYPE_GRP == 1);
+ CLASSERT(LQUOTA_RES_MD == 1);
+ CLASSERT(LQUOTA_RES_DT == 2);
+ LASSERTF(OBD_PING == 400, "found %lld\n",
+ (long long)OBD_PING);
+ LASSERTF(OBD_LOG_CANCEL == 401, "found %lld\n",
+ (long long)OBD_LOG_CANCEL);
+ LASSERTF(OBD_QC_CALLBACK == 402, "found %lld\n",
+ (long long)OBD_QC_CALLBACK);
+ LASSERTF(OBD_IDX_READ == 403, "found %lld\n",
+ (long long)OBD_IDX_READ);
+ LASSERTF(OBD_LAST_OPC == 404, "found %lld\n",
+ (long long)OBD_LAST_OPC);
+ LASSERTF(QUOTA_DQACQ == 601, "found %lld\n",
+ (long long)QUOTA_DQACQ);
+ LASSERTF(QUOTA_DQREL == 602, "found %lld\n",
+ (long long)QUOTA_DQREL);
+ LASSERTF(QUOTA_LAST_OPC == 603, "found %lld\n",
+ (long long)QUOTA_LAST_OPC);
+ LASSERTF(MGS_CONNECT == 250, "found %lld\n",
+ (long long)MGS_CONNECT);
+ LASSERTF(MGS_DISCONNECT == 251, "found %lld\n",
+ (long long)MGS_DISCONNECT);
+ LASSERTF(MGS_EXCEPTION == 252, "found %lld\n",
+ (long long)MGS_EXCEPTION);
+ LASSERTF(MGS_TARGET_REG == 253, "found %lld\n",
+ (long long)MGS_TARGET_REG);
+ LASSERTF(MGS_TARGET_DEL == 254, "found %lld\n",
+ (long long)MGS_TARGET_DEL);
+ LASSERTF(MGS_SET_INFO == 255, "found %lld\n",
+ (long long)MGS_SET_INFO);
+ LASSERTF(MGS_LAST_OPC == 257, "found %lld\n",
+ (long long)MGS_LAST_OPC);
+ LASSERTF(SEC_CTX_INIT == 801, "found %lld\n",
+ (long long)SEC_CTX_INIT);
+ LASSERTF(SEC_CTX_INIT_CONT == 802, "found %lld\n",
+ (long long)SEC_CTX_INIT_CONT);
+ LASSERTF(SEC_CTX_FINI == 803, "found %lld\n",
+ (long long)SEC_CTX_FINI);
+ LASSERTF(SEC_LAST_OPC == 804, "found %lld\n",
+ (long long)SEC_LAST_OPC);
+ /* Sizes and Offsets */
+
+ /* Checks for struct obd_uuid */
+ LASSERTF((int)sizeof(struct obd_uuid) == 40, "found %lld\n",
+ (long long)(int)sizeof(struct obd_uuid));
+
+ /* Checks for struct lu_seq_range */
+ LASSERTF((int)sizeof(struct lu_seq_range) == 24, "found %lld\n",
+ (long long)(int)sizeof(struct lu_seq_range));
+ LASSERTF((int)offsetof(struct lu_seq_range, lsr_start) == 0, "found %lld\n",
+ (long long)(int)offsetof(struct lu_seq_range, lsr_start));
+ LASSERTF((int)sizeof(((struct lu_seq_range *)0)->lsr_start) == 8, "found %lld\n",
+ (long long)(int)sizeof(((struct lu_seq_range *)0)->lsr_start));
+ LASSERTF((int)offsetof(struct lu_seq_range, lsr_end) == 8, "found %lld\n",
+ (long long)(int)offsetof(struct lu_seq_range, lsr_end));
+ LASSERTF((int)sizeof(((struct lu_seq_range *)0)->lsr_end) == 8, "found %lld\n",
+ (long long)(int)sizeof(((struct lu_seq_range *)0)->lsr_end));
+ LASSERTF((int)offsetof(struct lu_seq_range, lsr_index) == 16, "found %lld\n",
+ (long long)(int)offsetof(struct lu_seq_range, lsr_index));
+ LASSERTF((int)sizeof(((struct lu_seq_range *)0)->lsr_index) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct lu_seq_range *)0)->lsr_index));
+ LASSERTF((int)offsetof(struct lu_seq_range, lsr_flags) == 20, "found %lld\n",
+ (long long)(int)offsetof(struct lu_seq_range, lsr_flags));
+ LASSERTF((int)sizeof(((struct lu_seq_range *)0)->lsr_flags) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct lu_seq_range *)0)->lsr_flags));
+ LASSERTF(LU_SEQ_RANGE_MDT == 0, "found %lld\n",
+ (long long)LU_SEQ_RANGE_MDT);
+ LASSERTF(LU_SEQ_RANGE_OST == 1, "found %lld\n",
+ (long long)LU_SEQ_RANGE_OST);
+
+ /* Checks for struct lustre_mdt_attrs */
+ LASSERTF((int)sizeof(struct lustre_mdt_attrs) == 24, "found %lld\n",
+ (long long)(int)sizeof(struct lustre_mdt_attrs));
+ LASSERTF((int)offsetof(struct lustre_mdt_attrs, lma_compat) == 0, "found %lld\n",
+ (long long)(int)offsetof(struct lustre_mdt_attrs, lma_compat));
+ LASSERTF((int)sizeof(((struct lustre_mdt_attrs *)0)->lma_compat) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct lustre_mdt_attrs *)0)->lma_compat));
+ LASSERTF((int)offsetof(struct lustre_mdt_attrs, lma_incompat) == 4, "found %lld\n",
+ (long long)(int)offsetof(struct lustre_mdt_attrs, lma_incompat));
+ LASSERTF((int)sizeof(((struct lustre_mdt_attrs *)0)->lma_incompat) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct lustre_mdt_attrs *)0)->lma_incompat));
+ LASSERTF((int)offsetof(struct lustre_mdt_attrs, lma_self_fid) == 8, "found %lld\n",
+ (long long)(int)offsetof(struct lustre_mdt_attrs, lma_self_fid));
+ LASSERTF((int)sizeof(((struct lustre_mdt_attrs *)0)->lma_self_fid) == 16, "found %lld\n",
+ (long long)(int)sizeof(((struct lustre_mdt_attrs *)0)->lma_self_fid));
+ LASSERTF(LMAI_RELEASED == 0x00000001UL, "found 0x%.8xUL\n",
+ (unsigned)LMAI_RELEASED);
+ LASSERTF(LMAC_HSM == 0x00000001UL, "found 0x%.8xUL\n",
+ (unsigned)LMAC_HSM);
+ LASSERTF(LMAC_SOM == 0x00000002UL, "found 0x%.8xUL\n",
+ (unsigned)LMAC_SOM);
+ LASSERTF(LMAC_NOT_IN_OI == 0x00000004UL, "found 0x%.8xUL\n",
+ (unsigned)LMAC_NOT_IN_OI);
+ LASSERTF(LMAC_FID_ON_OST == 0x00000008UL, "found 0x%.8xUL\n",
+ (unsigned)LMAC_FID_ON_OST);
+ LASSERTF(OBJ_CREATE == 1, "found %lld\n",
+ (long long)OBJ_CREATE);
+ LASSERTF(OBJ_DESTROY == 2, "found %lld\n",
+ (long long)OBJ_DESTROY);
+ LASSERTF(OBJ_REF_ADD == 3, "found %lld\n",
+ (long long)OBJ_REF_ADD);
+ LASSERTF(OBJ_REF_DEL == 4, "found %lld\n",
+ (long long)OBJ_REF_DEL);
+ LASSERTF(OBJ_ATTR_SET == 5, "found %lld\n",
+ (long long)OBJ_ATTR_SET);
+ LASSERTF(OBJ_ATTR_GET == 6, "found %lld\n",
+ (long long)OBJ_ATTR_GET);
+ LASSERTF(OBJ_XATTR_SET == 7, "found %lld\n",
+ (long long)OBJ_XATTR_SET);
+ LASSERTF(OBJ_XATTR_GET == 8, "found %lld\n",
+ (long long)OBJ_XATTR_GET);
+ LASSERTF(OBJ_INDEX_LOOKUP == 9, "found %lld\n",
+ (long long)OBJ_INDEX_LOOKUP);
+ LASSERTF(OBJ_INDEX_LOOKUP == 9, "found %lld\n",
+ (long long)OBJ_INDEX_LOOKUP);
+ LASSERTF(OBJ_INDEX_INSERT == 10, "found %lld\n",
+ (long long)OBJ_INDEX_INSERT);
+ LASSERTF(OBJ_INDEX_DELETE == 11, "found %lld\n",
+ (long long)OBJ_INDEX_DELETE);
+
+ /* Checks for struct ost_id */
+ LASSERTF((int)sizeof(struct ost_id) == 16, "found %lld\n",
+ (long long)(int)sizeof(struct ost_id));
+ LASSERTF((int)offsetof(struct ost_id, oi) == 0, "found %lld\n",
+ (long long)(int)offsetof(struct ost_id, oi));
+ LASSERTF((int)sizeof(((struct ost_id *)0)->oi) == 16, "found %lld\n",
+ (long long)(int)sizeof(((struct ost_id *)0)->oi));
+ LASSERTF(LUSTRE_FID_INIT_OID == 1, "found %lld\n",
+ (long long)LUSTRE_FID_INIT_OID);
+ LASSERTF(FID_SEQ_OST_MDT0 == 0, "found %lld\n",
+ (long long)FID_SEQ_OST_MDT0);
+ LASSERTF(FID_SEQ_LLOG == 1, "found %lld\n",
+ (long long)FID_SEQ_LLOG);
+ LASSERTF(FID_SEQ_ECHO == 2, "found %lld\n",
+ (long long)FID_SEQ_ECHO);
+ LASSERTF(FID_SEQ_OST_MDT1 == 3, "found %lld\n",
+ (long long)FID_SEQ_OST_MDT1);
+ LASSERTF(FID_SEQ_OST_MAX == 9, "found %lld\n",
+ (long long)FID_SEQ_OST_MAX);
+ LASSERTF(FID_SEQ_RSVD == 11, "found %lld\n",
+ (long long)FID_SEQ_RSVD);
+ LASSERTF(FID_SEQ_IGIF == 12, "found %lld\n",
+ (long long)FID_SEQ_IGIF);
+ LASSERTF(FID_SEQ_IGIF_MAX == 0x00000000ffffffffULL, "found 0x%.16llxULL\n",
+ (long long)FID_SEQ_IGIF_MAX);
+ LASSERTF(FID_SEQ_IDIF == 0x0000000100000000ULL, "found 0x%.16llxULL\n",
+ (long long)FID_SEQ_IDIF);
+ LASSERTF(FID_SEQ_IDIF_MAX == 0x00000001ffffffffULL, "found 0x%.16llxULL\n",
+ (long long)FID_SEQ_IDIF_MAX);
+ LASSERTF(FID_SEQ_START == 0x0000000200000000ULL, "found 0x%.16llxULL\n",
+ (long long)FID_SEQ_START);
+ LASSERTF(FID_SEQ_LOCAL_FILE == 0x0000000200000001ULL, "found 0x%.16llxULL\n",
+ (long long)FID_SEQ_LOCAL_FILE);
+ LASSERTF(FID_SEQ_DOT_LUSTRE == 0x0000000200000002ULL, "found 0x%.16llxULL\n",
+ (long long)FID_SEQ_DOT_LUSTRE);
+ LASSERTF(FID_SEQ_SPECIAL == 0x0000000200000004ULL, "found 0x%.16llxULL\n",
+ (long long)FID_SEQ_SPECIAL);
+ LASSERTF(FID_SEQ_QUOTA == 0x0000000200000005ULL, "found 0x%.16llxULL\n",
+ (long long)FID_SEQ_QUOTA);
+ LASSERTF(FID_SEQ_QUOTA_GLB == 0x0000000200000006ULL, "found 0x%.16llxULL\n",
+ (long long)FID_SEQ_QUOTA_GLB);
+ LASSERTF(FID_SEQ_ROOT == 0x0000000200000007ULL, "found 0x%.16llxULL\n",
+ (long long)FID_SEQ_ROOT);
+ LASSERTF(FID_SEQ_NORMAL == 0x0000000200000400ULL, "found 0x%.16llxULL\n",
+ (long long)FID_SEQ_NORMAL);
+ LASSERTF(FID_SEQ_LOV_DEFAULT == 0xffffffffffffffffULL, "found 0x%.16llxULL\n",
+ (long long)FID_SEQ_LOV_DEFAULT);
+ LASSERTF(FID_OID_SPECIAL_BFL == 0x00000001UL, "found 0x%.8xUL\n",
+ (unsigned)FID_OID_SPECIAL_BFL);
+ LASSERTF(FID_OID_DOT_LUSTRE == 0x00000001UL, "found 0x%.8xUL\n",
+ (unsigned)FID_OID_DOT_LUSTRE);
+ LASSERTF(FID_OID_DOT_LUSTRE_OBF == 0x00000002UL, "found 0x%.8xUL\n",
+ (unsigned)FID_OID_DOT_LUSTRE_OBF);
+
+ /* Checks for struct lu_dirent */
+ LASSERTF((int)sizeof(struct lu_dirent) == 32, "found %lld\n",
+ (long long)(int)sizeof(struct lu_dirent));
+ LASSERTF((int)offsetof(struct lu_dirent, lde_fid) == 0, "found %lld\n",
+ (long long)(int)offsetof(struct lu_dirent, lde_fid));
+ LASSERTF((int)sizeof(((struct lu_dirent *)0)->lde_fid) == 16, "found %lld\n",
+ (long long)(int)sizeof(((struct lu_dirent *)0)->lde_fid));
+ LASSERTF((int)offsetof(struct lu_dirent, lde_hash) == 16, "found %lld\n",
+ (long long)(int)offsetof(struct lu_dirent, lde_hash));
+ LASSERTF((int)sizeof(((struct lu_dirent *)0)->lde_hash) == 8, "found %lld\n",
+ (long long)(int)sizeof(((struct lu_dirent *)0)->lde_hash));
+ LASSERTF((int)offsetof(struct lu_dirent, lde_reclen) == 24, "found %lld\n",
+ (long long)(int)offsetof(struct lu_dirent, lde_reclen));
+ LASSERTF((int)sizeof(((struct lu_dirent *)0)->lde_reclen) == 2, "found %lld\n",
+ (long long)(int)sizeof(((struct lu_dirent *)0)->lde_reclen));
+ LASSERTF((int)offsetof(struct lu_dirent, lde_namelen) == 26, "found %lld\n",
+ (long long)(int)offsetof(struct lu_dirent, lde_namelen));
+ LASSERTF((int)sizeof(((struct lu_dirent *)0)->lde_namelen) == 2, "found %lld\n",
+ (long long)(int)sizeof(((struct lu_dirent *)0)->lde_namelen));
+ LASSERTF((int)offsetof(struct lu_dirent, lde_attrs) == 28, "found %lld\n",
+ (long long)(int)offsetof(struct lu_dirent, lde_attrs));
+ LASSERTF((int)sizeof(((struct lu_dirent *)0)->lde_attrs) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct lu_dirent *)0)->lde_attrs));
+ LASSERTF((int)offsetof(struct lu_dirent, lde_name[0]) == 32, "found %lld\n",
+ (long long)(int)offsetof(struct lu_dirent, lde_name[0]));
+ LASSERTF((int)sizeof(((struct lu_dirent *)0)->lde_name[0]) == 1, "found %lld\n",
+ (long long)(int)sizeof(((struct lu_dirent *)0)->lde_name[0]));
+ LASSERTF(LUDA_FID == 0x00000001UL, "found 0x%.8xUL\n",
+ (unsigned)LUDA_FID);
+ LASSERTF(LUDA_TYPE == 0x00000002UL, "found 0x%.8xUL\n",
+ (unsigned)LUDA_TYPE);
+ LASSERTF(LUDA_64BITHASH == 0x00000004UL, "found 0x%.8xUL\n",
+ (unsigned)LUDA_64BITHASH);
+
+ /* Checks for struct luda_type */
+ LASSERTF((int)sizeof(struct luda_type) == 2, "found %lld\n",
+ (long long)(int)sizeof(struct luda_type));
+ LASSERTF((int)offsetof(struct luda_type, lt_type) == 0, "found %lld\n",
+ (long long)(int)offsetof(struct luda_type, lt_type));
+ LASSERTF((int)sizeof(((struct luda_type *)0)->lt_type) == 2, "found %lld\n",
+ (long long)(int)sizeof(((struct luda_type *)0)->lt_type));
+
+ /* Checks for struct lu_dirpage */
+ LASSERTF((int)sizeof(struct lu_dirpage) == 24, "found %lld\n",
+ (long long)(int)sizeof(struct lu_dirpage));
+ LASSERTF((int)offsetof(struct lu_dirpage, ldp_hash_start) == 0, "found %lld\n",
+ (long long)(int)offsetof(struct lu_dirpage, ldp_hash_start));
+ LASSERTF((int)sizeof(((struct lu_dirpage *)0)->ldp_hash_start) == 8, "found %lld\n",
+ (long long)(int)sizeof(((struct lu_dirpage *)0)->ldp_hash_start));
+ LASSERTF((int)offsetof(struct lu_dirpage, ldp_hash_end) == 8, "found %lld\n",
+ (long long)(int)offsetof(struct lu_dirpage, ldp_hash_end));
+ LASSERTF((int)sizeof(((struct lu_dirpage *)0)->ldp_hash_end) == 8, "found %lld\n",
+ (long long)(int)sizeof(((struct lu_dirpage *)0)->ldp_hash_end));
+ LASSERTF((int)offsetof(struct lu_dirpage, ldp_flags) == 16, "found %lld\n",
+ (long long)(int)offsetof(struct lu_dirpage, ldp_flags));
+ LASSERTF((int)sizeof(((struct lu_dirpage *)0)->ldp_flags) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct lu_dirpage *)0)->ldp_flags));
+ LASSERTF((int)offsetof(struct lu_dirpage, ldp_pad0) == 20, "found %lld\n",
+ (long long)(int)offsetof(struct lu_dirpage, ldp_pad0));
+ LASSERTF((int)sizeof(((struct lu_dirpage *)0)->ldp_pad0) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct lu_dirpage *)0)->ldp_pad0));
+ LASSERTF((int)offsetof(struct lu_dirpage, ldp_entries[0]) == 24, "found %lld\n",
+ (long long)(int)offsetof(struct lu_dirpage, ldp_entries[0]));
+ LASSERTF((int)sizeof(((struct lu_dirpage *)0)->ldp_entries[0]) == 32, "found %lld\n",
+ (long long)(int)sizeof(((struct lu_dirpage *)0)->ldp_entries[0]));
+ LASSERTF(LDF_EMPTY == 1, "found %lld\n",
+ (long long)LDF_EMPTY);
+ LASSERTF(LDF_COLLIDE == 2, "found %lld\n",
+ (long long)LDF_COLLIDE);
+ LASSERTF(LU_PAGE_SIZE == 4096, "found %lld\n",
+ (long long)LU_PAGE_SIZE);
+ /* Checks for union lu_page */
+ LASSERTF((int)sizeof(union lu_page) == 4096, "found %lld\n",
+ (long long)(int)sizeof(union lu_page));
+
+ /* Checks for struct lustre_handle */
+ LASSERTF((int)sizeof(struct lustre_handle) == 8, "found %lld\n",
+ (long long)(int)sizeof(struct lustre_handle));
+ LASSERTF((int)offsetof(struct lustre_handle, cookie) == 0, "found %lld\n",
+ (long long)(int)offsetof(struct lustre_handle, cookie));
+ LASSERTF((int)sizeof(((struct lustre_handle *)0)->cookie) == 8, "found %lld\n",
+ (long long)(int)sizeof(((struct lustre_handle *)0)->cookie));
+
+ /* Checks for struct lustre_msg_v2 */
+ LASSERTF((int)sizeof(struct lustre_msg_v2) == 32, "found %lld\n",
+ (long long)(int)sizeof(struct lustre_msg_v2));
+ LASSERTF((int)offsetof(struct lustre_msg_v2, lm_bufcount) == 0, "found %lld\n",
+ (long long)(int)offsetof(struct lustre_msg_v2, lm_bufcount));
+ LASSERTF((int)sizeof(((struct lustre_msg_v2 *)0)->lm_bufcount) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct lustre_msg_v2 *)0)->lm_bufcount));
+ LASSERTF((int)offsetof(struct lustre_msg_v2, lm_secflvr) == 4, "found %lld\n",
+ (long long)(int)offsetof(struct lustre_msg_v2, lm_secflvr));
+ LASSERTF((int)sizeof(((struct lustre_msg_v2 *)0)->lm_secflvr) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct lustre_msg_v2 *)0)->lm_secflvr));
+ LASSERTF((int)offsetof(struct lustre_msg_v2, lm_magic) == 8, "found %lld\n",
+ (long long)(int)offsetof(struct lustre_msg_v2, lm_magic));
+ LASSERTF((int)sizeof(((struct lustre_msg_v2 *)0)->lm_magic) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct lustre_msg_v2 *)0)->lm_magic));
+ LASSERTF((int)offsetof(struct lustre_msg_v2, lm_repsize) == 12, "found %lld\n",
+ (long long)(int)offsetof(struct lustre_msg_v2, lm_repsize));
+ LASSERTF((int)sizeof(((struct lustre_msg_v2 *)0)->lm_repsize) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct lustre_msg_v2 *)0)->lm_repsize));
+ LASSERTF((int)offsetof(struct lustre_msg_v2, lm_cksum) == 16, "found %lld\n",
+ (long long)(int)offsetof(struct lustre_msg_v2, lm_cksum));
+ LASSERTF((int)sizeof(((struct lustre_msg_v2 *)0)->lm_cksum) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct lustre_msg_v2 *)0)->lm_cksum));
+ LASSERTF((int)offsetof(struct lustre_msg_v2, lm_flags) == 20, "found %lld\n",
+ (long long)(int)offsetof(struct lustre_msg_v2, lm_flags));
+ LASSERTF((int)sizeof(((struct lustre_msg_v2 *)0)->lm_flags) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct lustre_msg_v2 *)0)->lm_flags));
+ LASSERTF((int)offsetof(struct lustre_msg_v2, lm_padding_2) == 24, "found %lld\n",
+ (long long)(int)offsetof(struct lustre_msg_v2, lm_padding_2));
+ LASSERTF((int)sizeof(((struct lustre_msg_v2 *)0)->lm_padding_2) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct lustre_msg_v2 *)0)->lm_padding_2));
+ LASSERTF((int)offsetof(struct lustre_msg_v2, lm_padding_3) == 28, "found %lld\n",
+ (long long)(int)offsetof(struct lustre_msg_v2, lm_padding_3));
+ LASSERTF((int)sizeof(((struct lustre_msg_v2 *)0)->lm_padding_3) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct lustre_msg_v2 *)0)->lm_padding_3));
+ LASSERTF((int)offsetof(struct lustre_msg_v2, lm_buflens[0]) == 32, "found %lld\n",
+ (long long)(int)offsetof(struct lustre_msg_v2, lm_buflens[0]));
+ LASSERTF((int)sizeof(((struct lustre_msg_v2 *)0)->lm_buflens[0]) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct lustre_msg_v2 *)0)->lm_buflens[0]));
+ LASSERTF(LUSTRE_MSG_MAGIC_V1 == 0x0BD00BD0, "found 0x%.8x\n",
+ LUSTRE_MSG_MAGIC_V1);
+ LASSERTF(LUSTRE_MSG_MAGIC_V2 == 0x0BD00BD3, "found 0x%.8x\n",
+ LUSTRE_MSG_MAGIC_V2);
+ LASSERTF(LUSTRE_MSG_MAGIC_V1_SWABBED == 0xD00BD00B, "found 0x%.8x\n",
+ LUSTRE_MSG_MAGIC_V1_SWABBED);
+ LASSERTF(LUSTRE_MSG_MAGIC_V2_SWABBED == 0xD30BD00B, "found 0x%.8x\n",
+ LUSTRE_MSG_MAGIC_V2_SWABBED);
+
+ /* Checks for struct ptlrpc_body */
+ LASSERTF((int)sizeof(struct ptlrpc_body_v3) == 184, "found %lld\n",
+ (long long)(int)sizeof(struct ptlrpc_body_v3));
+ LASSERTF((int)offsetof(struct ptlrpc_body_v3, pb_handle) == 0, "found %lld\n",
+ (long long)(int)offsetof(struct ptlrpc_body_v3, pb_handle));
+ LASSERTF((int)sizeof(((struct ptlrpc_body_v3 *)0)->pb_handle) == 8, "found %lld\n",
+ (long long)(int)sizeof(((struct ptlrpc_body_v3 *)0)->pb_handle));
+ LASSERTF((int)offsetof(struct ptlrpc_body_v3, pb_type) == 8, "found %lld\n",
+ (long long)(int)offsetof(struct ptlrpc_body_v3, pb_type));
+ LASSERTF((int)sizeof(((struct ptlrpc_body_v3 *)0)->pb_type) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct ptlrpc_body_v3 *)0)->pb_type));
+ LASSERTF((int)offsetof(struct ptlrpc_body_v3, pb_version) == 12, "found %lld\n",
+ (long long)(int)offsetof(struct ptlrpc_body_v3, pb_version));
+ LASSERTF((int)sizeof(((struct ptlrpc_body_v3 *)0)->pb_version) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct ptlrpc_body_v3 *)0)->pb_version));
+ LASSERTF((int)offsetof(struct ptlrpc_body_v3, pb_opc) == 16, "found %lld\n",
+ (long long)(int)offsetof(struct ptlrpc_body_v3, pb_opc));
+ LASSERTF((int)sizeof(((struct ptlrpc_body_v3 *)0)->pb_opc) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct ptlrpc_body_v3 *)0)->pb_opc));
+ LASSERTF((int)offsetof(struct ptlrpc_body_v3, pb_status) == 20, "found %lld\n",
+ (long long)(int)offsetof(struct ptlrpc_body_v3, pb_status));
+ LASSERTF((int)sizeof(((struct ptlrpc_body_v3 *)0)->pb_status) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct ptlrpc_body_v3 *)0)->pb_status));
+ LASSERTF((int)offsetof(struct ptlrpc_body_v3, pb_last_xid) == 24, "found %lld\n",
+ (long long)(int)offsetof(struct ptlrpc_body_v3, pb_last_xid));
+ LASSERTF((int)sizeof(((struct ptlrpc_body_v3 *)0)->pb_last_xid) == 8, "found %lld\n",
+ (long long)(int)sizeof(((struct ptlrpc_body_v3 *)0)->pb_last_xid));
+ LASSERTF((int)offsetof(struct ptlrpc_body_v3, pb_last_seen) == 32, "found %lld\n",
+ (long long)(int)offsetof(struct ptlrpc_body_v3, pb_last_seen));
+ LASSERTF((int)sizeof(((struct ptlrpc_body_v3 *)0)->pb_last_seen) == 8, "found %lld\n",
+ (long long)(int)sizeof(((struct ptlrpc_body_v3 *)0)->pb_last_seen));
+ LASSERTF((int)offsetof(struct ptlrpc_body_v3, pb_last_committed) == 40, "found %lld\n",
+ (long long)(int)offsetof(struct ptlrpc_body_v3, pb_last_committed));
+ LASSERTF((int)sizeof(((struct ptlrpc_body_v3 *)0)->pb_last_committed) == 8, "found %lld\n",
+ (long long)(int)sizeof(((struct ptlrpc_body_v3 *)0)->pb_last_committed));
+ LASSERTF((int)offsetof(struct ptlrpc_body_v3, pb_transno) == 48, "found %lld\n",
+ (long long)(int)offsetof(struct ptlrpc_body_v3, pb_transno));
+ LASSERTF((int)sizeof(((struct ptlrpc_body_v3 *)0)->pb_transno) == 8, "found %lld\n",
+ (long long)(int)sizeof(((struct ptlrpc_body_v3 *)0)->pb_transno));
+ LASSERTF((int)offsetof(struct ptlrpc_body_v3, pb_flags) == 56, "found %lld\n",
+ (long long)(int)offsetof(struct ptlrpc_body_v3, pb_flags));
+ LASSERTF((int)sizeof(((struct ptlrpc_body_v3 *)0)->pb_flags) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct ptlrpc_body_v3 *)0)->pb_flags));
+ LASSERTF((int)offsetof(struct ptlrpc_body_v3, pb_op_flags) == 60, "found %lld\n",
+ (long long)(int)offsetof(struct ptlrpc_body_v3, pb_op_flags));
+ LASSERTF((int)sizeof(((struct ptlrpc_body_v3 *)0)->pb_op_flags) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct ptlrpc_body_v3 *)0)->pb_op_flags));
+ LASSERTF((int)offsetof(struct ptlrpc_body_v3, pb_conn_cnt) == 64, "found %lld\n",
+ (long long)(int)offsetof(struct ptlrpc_body_v3, pb_conn_cnt));
+ LASSERTF((int)sizeof(((struct ptlrpc_body_v3 *)0)->pb_conn_cnt) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct ptlrpc_body_v3 *)0)->pb_conn_cnt));
+ LASSERTF((int)offsetof(struct ptlrpc_body_v3, pb_timeout) == 68, "found %lld\n",
+ (long long)(int)offsetof(struct ptlrpc_body_v3, pb_timeout));
+ LASSERTF((int)sizeof(((struct ptlrpc_body_v3 *)0)->pb_timeout) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct ptlrpc_body_v3 *)0)->pb_timeout));
+ LASSERTF((int)offsetof(struct ptlrpc_body_v3, pb_service_time) == 72, "found %lld\n",
+ (long long)(int)offsetof(struct ptlrpc_body_v3, pb_service_time));
+ LASSERTF((int)sizeof(((struct ptlrpc_body_v3 *)0)->pb_service_time) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct ptlrpc_body_v3 *)0)->pb_service_time));
+ LASSERTF((int)offsetof(struct ptlrpc_body_v3, pb_limit) == 76, "found %lld\n",
+ (long long)(int)offsetof(struct ptlrpc_body_v3, pb_limit));
+ LASSERTF((int)sizeof(((struct ptlrpc_body_v3 *)0)->pb_limit) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct ptlrpc_body_v3 *)0)->pb_limit));
+ LASSERTF((int)offsetof(struct ptlrpc_body_v3, pb_slv) == 80, "found %lld\n",
+ (long long)(int)offsetof(struct ptlrpc_body_v3, pb_slv));
+ LASSERTF((int)sizeof(((struct ptlrpc_body_v3 *)0)->pb_slv) == 8, "found %lld\n",
+ (long long)(int)sizeof(((struct ptlrpc_body_v3 *)0)->pb_slv));
+ CLASSERT(PTLRPC_NUM_VERSIONS == 4);
+ LASSERTF((int)offsetof(struct ptlrpc_body_v3, pb_pre_versions) == 88, "found %lld\n",
+ (long long)(int)offsetof(struct ptlrpc_body_v3, pb_pre_versions));
+ LASSERTF((int)sizeof(((struct ptlrpc_body_v3 *)0)->pb_pre_versions) == 32, "found %lld\n",
+ (long long)(int)sizeof(((struct ptlrpc_body_v3 *)0)->pb_pre_versions));
+ LASSERTF((int)offsetof(struct ptlrpc_body_v3, pb_padding) == 120, "found %lld\n",
+ (long long)(int)offsetof(struct ptlrpc_body_v3, pb_padding));
+ LASSERTF((int)sizeof(((struct ptlrpc_body_v3 *)0)->pb_padding) == 32, "found %lld\n",
+ (long long)(int)sizeof(((struct ptlrpc_body_v3 *)0)->pb_padding));
+ CLASSERT(JOBSTATS_JOBID_SIZE == 32);
+ LASSERTF((int)offsetof(struct ptlrpc_body_v3, pb_jobid) == 152, "found %lld\n",
+ (long long)(int)offsetof(struct ptlrpc_body_v3, pb_jobid));
+ LASSERTF((int)sizeof(((struct ptlrpc_body_v3 *)0)->pb_jobid) == 32, "found %lld\n",
+ (long long)(int)sizeof(((struct ptlrpc_body_v3 *)0)->pb_jobid));
+ LASSERTF((int)offsetof(struct ptlrpc_body_v3, pb_handle) == (int)offsetof(struct ptlrpc_body_v2, pb_handle), "%d != %d\n",
+ (int)offsetof(struct ptlrpc_body_v3, pb_handle), (int)offsetof(struct ptlrpc_body_v2, pb_handle));
+ LASSERTF((int)sizeof(((struct ptlrpc_body_v3 *)0)->pb_handle) == (int)sizeof(((struct ptlrpc_body_v2 *)0)->pb_handle), "%d != %d\n",
+ (int)sizeof(((struct ptlrpc_body_v3 *)0)->pb_handle), (int)sizeof(((struct ptlrpc_body_v2 *)0)->pb_handle));
+ LASSERTF((int)offsetof(struct ptlrpc_body_v3, pb_type) == (int)offsetof(struct ptlrpc_body_v2, pb_type), "%d != %d\n",
+ (int)offsetof(struct ptlrpc_body_v3, pb_type), (int)offsetof(struct ptlrpc_body_v2, pb_type));
+ LASSERTF((int)sizeof(((struct ptlrpc_body_v3 *)0)->pb_type) == (int)sizeof(((struct ptlrpc_body_v2 *)0)->pb_type), "%d != %d\n",
+ (int)sizeof(((struct ptlrpc_body_v3 *)0)->pb_type), (int)sizeof(((struct ptlrpc_body_v2 *)0)->pb_type));
+ LASSERTF((int)offsetof(struct ptlrpc_body_v3, pb_version) == (int)offsetof(struct ptlrpc_body_v2, pb_version), "%d != %d\n",
+ (int)offsetof(struct ptlrpc_body_v3, pb_version), (int)offsetof(struct ptlrpc_body_v2, pb_version));
+ LASSERTF((int)sizeof(((struct ptlrpc_body_v3 *)0)->pb_version) == (int)sizeof(((struct ptlrpc_body_v2 *)0)->pb_version), "%d != %d\n",
+ (int)sizeof(((struct ptlrpc_body_v3 *)0)->pb_version), (int)sizeof(((struct ptlrpc_body_v2 *)0)->pb_version));
+ LASSERTF((int)offsetof(struct ptlrpc_body_v3, pb_opc) == (int)offsetof(struct ptlrpc_body_v2, pb_opc), "%d != %d\n",
+ (int)offsetof(struct ptlrpc_body_v3, pb_opc), (int)offsetof(struct ptlrpc_body_v2, pb_opc));
+ LASSERTF((int)sizeof(((struct ptlrpc_body_v3 *)0)->pb_opc) == (int)sizeof(((struct ptlrpc_body_v2 *)0)->pb_opc), "%d != %d\n",
+ (int)sizeof(((struct ptlrpc_body_v3 *)0)->pb_opc), (int)sizeof(((struct ptlrpc_body_v2 *)0)->pb_opc));
+ LASSERTF((int)offsetof(struct ptlrpc_body_v3, pb_status) == (int)offsetof(struct ptlrpc_body_v2, pb_status), "%d != %d\n",
+ (int)offsetof(struct ptlrpc_body_v3, pb_status), (int)offsetof(struct ptlrpc_body_v2, pb_status));
+ LASSERTF((int)sizeof(((struct ptlrpc_body_v3 *)0)->pb_status) == (int)sizeof(((struct ptlrpc_body_v2 *)0)->pb_status), "%d != %d\n",
+ (int)sizeof(((struct ptlrpc_body_v3 *)0)->pb_status), (int)sizeof(((struct ptlrpc_body_v2 *)0)->pb_status));
+ LASSERTF((int)offsetof(struct ptlrpc_body_v3, pb_last_xid) == (int)offsetof(struct ptlrpc_body_v2, pb_last_xid), "%d != %d\n",
+ (int)offsetof(struct ptlrpc_body_v3, pb_last_xid), (int)offsetof(struct ptlrpc_body_v2, pb_last_xid));
+ LASSERTF((int)sizeof(((struct ptlrpc_body_v3 *)0)->pb_last_xid) == (int)sizeof(((struct ptlrpc_body_v2 *)0)->pb_last_xid), "%d != %d\n",
+ (int)sizeof(((struct ptlrpc_body_v3 *)0)->pb_last_xid), (int)sizeof(((struct ptlrpc_body_v2 *)0)->pb_last_xid));
+ LASSERTF((int)offsetof(struct ptlrpc_body_v3, pb_last_seen) == (int)offsetof(struct ptlrpc_body_v2, pb_last_seen), "%d != %d\n",
+ (int)offsetof(struct ptlrpc_body_v3, pb_last_seen), (int)offsetof(struct ptlrpc_body_v2, pb_last_seen));
+ LASSERTF((int)sizeof(((struct ptlrpc_body_v3 *)0)->pb_last_seen) == (int)sizeof(((struct ptlrpc_body_v2 *)0)->pb_last_seen), "%d != %d\n",
+ (int)sizeof(((struct ptlrpc_body_v3 *)0)->pb_last_seen), (int)sizeof(((struct ptlrpc_body_v2 *)0)->pb_last_seen));
+ LASSERTF((int)offsetof(struct ptlrpc_body_v3, pb_last_committed) == (int)offsetof(struct ptlrpc_body_v2, pb_last_committed), "%d != %d\n",
+ (int)offsetof(struct ptlrpc_body_v3, pb_last_committed), (int)offsetof(struct ptlrpc_body_v2, pb_last_committed));
+ LASSERTF((int)sizeof(((struct ptlrpc_body_v3 *)0)->pb_last_committed) == (int)sizeof(((struct ptlrpc_body_v2 *)0)->pb_last_committed), "%d != %d\n",
+ (int)sizeof(((struct ptlrpc_body_v3 *)0)->pb_last_committed), (int)sizeof(((struct ptlrpc_body_v2 *)0)->pb_last_committed));
+ LASSERTF((int)offsetof(struct ptlrpc_body_v3, pb_transno) == (int)offsetof(struct ptlrpc_body_v2, pb_transno), "%d != %d\n",
+ (int)offsetof(struct ptlrpc_body_v3, pb_transno), (int)offsetof(struct ptlrpc_body_v2, pb_transno));
+ LASSERTF((int)sizeof(((struct ptlrpc_body_v3 *)0)->pb_transno) == (int)sizeof(((struct ptlrpc_body_v2 *)0)->pb_transno), "%d != %d\n",
+ (int)sizeof(((struct ptlrpc_body_v3 *)0)->pb_transno), (int)sizeof(((struct ptlrpc_body_v2 *)0)->pb_transno));
+ LASSERTF((int)offsetof(struct ptlrpc_body_v3, pb_flags) == (int)offsetof(struct ptlrpc_body_v2, pb_flags), "%d != %d\n",
+ (int)offsetof(struct ptlrpc_body_v3, pb_flags), (int)offsetof(struct ptlrpc_body_v2, pb_flags));
+ LASSERTF((int)sizeof(((struct ptlrpc_body_v3 *)0)->pb_flags) == (int)sizeof(((struct ptlrpc_body_v2 *)0)->pb_flags), "%d != %d\n",
+ (int)sizeof(((struct ptlrpc_body_v3 *)0)->pb_flags), (int)sizeof(((struct ptlrpc_body_v2 *)0)->pb_flags));
+ LASSERTF((int)offsetof(struct ptlrpc_body_v3, pb_op_flags) == (int)offsetof(struct ptlrpc_body_v2, pb_op_flags), "%d != %d\n",
+ (int)offsetof(struct ptlrpc_body_v3, pb_op_flags), (int)offsetof(struct ptlrpc_body_v2, pb_op_flags));
+ LASSERTF((int)sizeof(((struct ptlrpc_body_v3 *)0)->pb_op_flags) == (int)sizeof(((struct ptlrpc_body_v2 *)0)->pb_op_flags), "%d != %d\n",
+ (int)sizeof(((struct ptlrpc_body_v3 *)0)->pb_op_flags), (int)sizeof(((struct ptlrpc_body_v2 *)0)->pb_op_flags));
+ LASSERTF((int)offsetof(struct ptlrpc_body_v3, pb_conn_cnt) == (int)offsetof(struct ptlrpc_body_v2, pb_conn_cnt), "%d != %d\n",
+ (int)offsetof(struct ptlrpc_body_v3, pb_conn_cnt), (int)offsetof(struct ptlrpc_body_v2, pb_conn_cnt));
+ LASSERTF((int)sizeof(((struct ptlrpc_body_v3 *)0)->pb_conn_cnt) == (int)sizeof(((struct ptlrpc_body_v2 *)0)->pb_conn_cnt), "%d != %d\n",
+ (int)sizeof(((struct ptlrpc_body_v3 *)0)->pb_conn_cnt), (int)sizeof(((struct ptlrpc_body_v2 *)0)->pb_conn_cnt));
+ LASSERTF((int)offsetof(struct ptlrpc_body_v3, pb_timeout) == (int)offsetof(struct ptlrpc_body_v2, pb_timeout), "%d != %d\n",
+ (int)offsetof(struct ptlrpc_body_v3, pb_timeout), (int)offsetof(struct ptlrpc_body_v2, pb_timeout));
+ LASSERTF((int)sizeof(((struct ptlrpc_body_v3 *)0)->pb_timeout) == (int)sizeof(((struct ptlrpc_body_v2 *)0)->pb_timeout), "%d != %d\n",
+ (int)sizeof(((struct ptlrpc_body_v3 *)0)->pb_timeout), (int)sizeof(((struct ptlrpc_body_v2 *)0)->pb_timeout));
+ LASSERTF((int)offsetof(struct ptlrpc_body_v3, pb_service_time) == (int)offsetof(struct ptlrpc_body_v2, pb_service_time), "%d != %d\n",
+ (int)offsetof(struct ptlrpc_body_v3, pb_service_time), (int)offsetof(struct ptlrpc_body_v2, pb_service_time));
+ LASSERTF((int)sizeof(((struct ptlrpc_body_v3 *)0)->pb_service_time) == (int)sizeof(((struct ptlrpc_body_v2 *)0)->pb_service_time), "%d != %d\n",
+ (int)sizeof(((struct ptlrpc_body_v3 *)0)->pb_service_time), (int)sizeof(((struct ptlrpc_body_v2 *)0)->pb_service_time));
+ LASSERTF((int)offsetof(struct ptlrpc_body_v3, pb_limit) == (int)offsetof(struct ptlrpc_body_v2, pb_limit), "%d != %d\n",
+ (int)offsetof(struct ptlrpc_body_v3, pb_limit), (int)offsetof(struct ptlrpc_body_v2, pb_limit));
+ LASSERTF((int)sizeof(((struct ptlrpc_body_v3 *)0)->pb_limit) == (int)sizeof(((struct ptlrpc_body_v2 *)0)->pb_limit), "%d != %d\n",
+ (int)sizeof(((struct ptlrpc_body_v3 *)0)->pb_limit), (int)sizeof(((struct ptlrpc_body_v2 *)0)->pb_limit));
+ LASSERTF((int)offsetof(struct ptlrpc_body_v3, pb_slv) == (int)offsetof(struct ptlrpc_body_v2, pb_slv), "%d != %d\n",
+ (int)offsetof(struct ptlrpc_body_v3, pb_slv), (int)offsetof(struct ptlrpc_body_v2, pb_slv));
+ LASSERTF((int)sizeof(((struct ptlrpc_body_v3 *)0)->pb_slv) == (int)sizeof(((struct ptlrpc_body_v2 *)0)->pb_slv), "%d != %d\n",
+ (int)sizeof(((struct ptlrpc_body_v3 *)0)->pb_slv), (int)sizeof(((struct ptlrpc_body_v2 *)0)->pb_slv));
+ LASSERTF((int)offsetof(struct ptlrpc_body_v3, pb_pre_versions) == (int)offsetof(struct ptlrpc_body_v2, pb_pre_versions), "%d != %d\n",
+ (int)offsetof(struct ptlrpc_body_v3, pb_pre_versions), (int)offsetof(struct ptlrpc_body_v2, pb_pre_versions));
+ LASSERTF((int)sizeof(((struct ptlrpc_body_v3 *)0)->pb_pre_versions) == (int)sizeof(((struct ptlrpc_body_v2 *)0)->pb_pre_versions), "%d != %d\n",
+ (int)sizeof(((struct ptlrpc_body_v3 *)0)->pb_pre_versions), (int)sizeof(((struct ptlrpc_body_v2 *)0)->pb_pre_versions));
+ LASSERTF((int)offsetof(struct ptlrpc_body_v3, pb_padding) == (int)offsetof(struct ptlrpc_body_v2, pb_padding), "%d != %d\n",
+ (int)offsetof(struct ptlrpc_body_v3, pb_padding), (int)offsetof(struct ptlrpc_body_v2, pb_padding));
+ LASSERTF((int)sizeof(((struct ptlrpc_body_v3 *)0)->pb_padding) == (int)sizeof(((struct ptlrpc_body_v2 *)0)->pb_padding), "%d != %d\n",
+ (int)sizeof(((struct ptlrpc_body_v3 *)0)->pb_padding), (int)sizeof(((struct ptlrpc_body_v2 *)0)->pb_padding));
+ LASSERTF(MSG_PTLRPC_BODY_OFF == 0, "found %lld\n",
+ (long long)MSG_PTLRPC_BODY_OFF);
+ LASSERTF(REQ_REC_OFF == 1, "found %lld\n",
+ (long long)REQ_REC_OFF);
+ LASSERTF(REPLY_REC_OFF == 1, "found %lld\n",
+ (long long)REPLY_REC_OFF);
+ LASSERTF(DLM_LOCKREQ_OFF == 1, "found %lld\n",
+ (long long)DLM_LOCKREQ_OFF);
+ LASSERTF(DLM_REQ_REC_OFF == 2, "found %lld\n",
+ (long long)DLM_REQ_REC_OFF);
+ LASSERTF(DLM_INTENT_IT_OFF == 2, "found %lld\n",
+ (long long)DLM_INTENT_IT_OFF);
+ LASSERTF(DLM_INTENT_REC_OFF == 3, "found %lld\n",
+ (long long)DLM_INTENT_REC_OFF);
+ LASSERTF(DLM_LOCKREPLY_OFF == 1, "found %lld\n",
+ (long long)DLM_LOCKREPLY_OFF);
+ LASSERTF(DLM_REPLY_REC_OFF == 2, "found %lld\n",
+ (long long)DLM_REPLY_REC_OFF);
+ LASSERTF(MSG_PTLRPC_HEADER_OFF == 31, "found %lld\n",
+ (long long)MSG_PTLRPC_HEADER_OFF);
+ LASSERTF(PTLRPC_MSG_VERSION == 0x00000003, "found 0x%.8x\n",
+ PTLRPC_MSG_VERSION);
+ LASSERTF(LUSTRE_VERSION_MASK == 0xffff0000, "found 0x%.8x\n",
+ LUSTRE_VERSION_MASK);
+ LASSERTF(LUSTRE_OBD_VERSION == 0x00010000, "found 0x%.8x\n",
+ LUSTRE_OBD_VERSION);
+ LASSERTF(LUSTRE_MDS_VERSION == 0x00020000, "found 0x%.8x\n",
+ LUSTRE_MDS_VERSION);
+ LASSERTF(LUSTRE_OST_VERSION == 0x00030000, "found 0x%.8x\n",
+ LUSTRE_OST_VERSION);
+ LASSERTF(LUSTRE_DLM_VERSION == 0x00040000, "found 0x%.8x\n",
+ LUSTRE_DLM_VERSION);
+ LASSERTF(LUSTRE_LOG_VERSION == 0x00050000, "found 0x%.8x\n",
+ LUSTRE_LOG_VERSION);
+ LASSERTF(LUSTRE_MGS_VERSION == 0x00060000, "found 0x%.8x\n",
+ LUSTRE_MGS_VERSION);
+ LASSERTF(MSGHDR_AT_SUPPORT == 1, "found %lld\n",
+ (long long)MSGHDR_AT_SUPPORT);
+ LASSERTF(MSGHDR_CKSUM_INCOMPAT18 == 2, "found %lld\n",
+ (long long)MSGHDR_CKSUM_INCOMPAT18);
+ LASSERTF(MSG_OP_FLAG_MASK == 0xffff0000UL, "found 0x%.8xUL\n",
+ (unsigned)MSG_OP_FLAG_MASK);
+ LASSERTF(MSG_OP_FLAG_SHIFT == 16, "found %lld\n",
+ (long long)MSG_OP_FLAG_SHIFT);
+ LASSERTF(MSG_GEN_FLAG_MASK == 0x0000ffffUL, "found 0x%.8xUL\n",
+ (unsigned)MSG_GEN_FLAG_MASK);
+ LASSERTF(MSG_LAST_REPLAY == 0x00000001UL, "found 0x%.8xUL\n",
+ (unsigned)MSG_LAST_REPLAY);
+ LASSERTF(MSG_RESENT == 0x00000002UL, "found 0x%.8xUL\n",
+ (unsigned)MSG_RESENT);
+ LASSERTF(MSG_REPLAY == 0x00000004UL, "found 0x%.8xUL\n",
+ (unsigned)MSG_REPLAY);
+ LASSERTF(MSG_DELAY_REPLAY == 0x00000010UL, "found 0x%.8xUL\n",
+ (unsigned)MSG_DELAY_REPLAY);
+ LASSERTF(MSG_VERSION_REPLAY == 0x00000020UL, "found 0x%.8xUL\n",
+ (unsigned)MSG_VERSION_REPLAY);
+ LASSERTF(MSG_REQ_REPLAY_DONE == 0x00000040UL, "found 0x%.8xUL\n",
+ (unsigned)MSG_REQ_REPLAY_DONE);
+ LASSERTF(MSG_LOCK_REPLAY_DONE == 0x00000080UL, "found 0x%.8xUL\n",
+ (unsigned)MSG_LOCK_REPLAY_DONE);
+ LASSERTF(MSG_CONNECT_RECOVERING == 0x00000001UL, "found 0x%.8xUL\n",
+ (unsigned)MSG_CONNECT_RECOVERING);
+ LASSERTF(MSG_CONNECT_RECONNECT == 0x00000002UL, "found 0x%.8xUL\n",
+ (unsigned)MSG_CONNECT_RECONNECT);
+ LASSERTF(MSG_CONNECT_REPLAYABLE == 0x00000004UL, "found 0x%.8xUL\n",
+ (unsigned)MSG_CONNECT_REPLAYABLE);
+ LASSERTF(MSG_CONNECT_LIBCLIENT == 0x00000010UL, "found 0x%.8xUL\n",
+ (unsigned)MSG_CONNECT_LIBCLIENT);
+ LASSERTF(MSG_CONNECT_INITIAL == 0x00000020UL, "found 0x%.8xUL\n",
+ (unsigned)MSG_CONNECT_INITIAL);
+ LASSERTF(MSG_CONNECT_ASYNC == 0x00000040UL, "found 0x%.8xUL\n",
+ (unsigned)MSG_CONNECT_ASYNC);
+ LASSERTF(MSG_CONNECT_NEXT_VER == 0x00000080UL, "found 0x%.8xUL\n",
+ (unsigned)MSG_CONNECT_NEXT_VER);
+ LASSERTF(MSG_CONNECT_TRANSNO == 0x00000100UL, "found 0x%.8xUL\n",
+ (unsigned)MSG_CONNECT_TRANSNO);
+
+ /* Checks for struct obd_connect_data */
+ LASSERTF((int)sizeof(struct obd_connect_data) == 192, "found %lld\n",
+ (long long)(int)sizeof(struct obd_connect_data));
+ LASSERTF((int)offsetof(struct obd_connect_data, ocd_connect_flags) == 0, "found %lld\n",
+ (long long)(int)offsetof(struct obd_connect_data, ocd_connect_flags));
+ LASSERTF((int)sizeof(((struct obd_connect_data *)0)->ocd_connect_flags) == 8, "found %lld\n",
+ (long long)(int)sizeof(((struct obd_connect_data *)0)->ocd_connect_flags));
+ LASSERTF((int)offsetof(struct obd_connect_data, ocd_version) == 8, "found %lld\n",
+ (long long)(int)offsetof(struct obd_connect_data, ocd_version));
+ LASSERTF((int)sizeof(((struct obd_connect_data *)0)->ocd_version) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct obd_connect_data *)0)->ocd_version));
+ LASSERTF((int)offsetof(struct obd_connect_data, ocd_grant) == 12, "found %lld\n",
+ (long long)(int)offsetof(struct obd_connect_data, ocd_grant));
+ LASSERTF((int)sizeof(((struct obd_connect_data *)0)->ocd_grant) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct obd_connect_data *)0)->ocd_grant));
+ LASSERTF((int)offsetof(struct obd_connect_data, ocd_index) == 16, "found %lld\n",
+ (long long)(int)offsetof(struct obd_connect_data, ocd_index));
+ LASSERTF((int)sizeof(((struct obd_connect_data *)0)->ocd_index) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct obd_connect_data *)0)->ocd_index));
+ LASSERTF((int)offsetof(struct obd_connect_data, ocd_brw_size) == 20, "found %lld\n",
+ (long long)(int)offsetof(struct obd_connect_data, ocd_brw_size));
+ LASSERTF((int)sizeof(((struct obd_connect_data *)0)->ocd_brw_size) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct obd_connect_data *)0)->ocd_brw_size));
+ LASSERTF((int)offsetof(struct obd_connect_data, ocd_ibits_known) == 24, "found %lld\n",
+ (long long)(int)offsetof(struct obd_connect_data, ocd_ibits_known));
+ LASSERTF((int)sizeof(((struct obd_connect_data *)0)->ocd_ibits_known) == 8, "found %lld\n",
+ (long long)(int)sizeof(((struct obd_connect_data *)0)->ocd_ibits_known));
+ LASSERTF((int)offsetof(struct obd_connect_data, ocd_blocksize) == 32, "found %lld\n",
+ (long long)(int)offsetof(struct obd_connect_data, ocd_blocksize));
+ LASSERTF((int)sizeof(((struct obd_connect_data *)0)->ocd_blocksize) == 1, "found %lld\n",
+ (long long)(int)sizeof(((struct obd_connect_data *)0)->ocd_blocksize));
+ LASSERTF((int)offsetof(struct obd_connect_data, ocd_inodespace) == 33, "found %lld\n",
+ (long long)(int)offsetof(struct obd_connect_data, ocd_inodespace));
+ LASSERTF((int)sizeof(((struct obd_connect_data *)0)->ocd_inodespace) == 1, "found %lld\n",
+ (long long)(int)sizeof(((struct obd_connect_data *)0)->ocd_inodespace));
+ LASSERTF((int)offsetof(struct obd_connect_data, ocd_grant_extent) == 34, "found %lld\n",
+ (long long)(int)offsetof(struct obd_connect_data, ocd_grant_extent));
+ LASSERTF((int)sizeof(((struct obd_connect_data *)0)->ocd_grant_extent) == 2, "found %lld\n",
+ (long long)(int)sizeof(((struct obd_connect_data *)0)->ocd_grant_extent));
+ LASSERTF((int)offsetof(struct obd_connect_data, ocd_unused) == 36, "found %lld\n",
+ (long long)(int)offsetof(struct obd_connect_data, ocd_unused));
+ LASSERTF((int)sizeof(((struct obd_connect_data *)0)->ocd_unused) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct obd_connect_data *)0)->ocd_unused));
+ LASSERTF((int)offsetof(struct obd_connect_data, ocd_transno) == 40, "found %lld\n",
+ (long long)(int)offsetof(struct obd_connect_data, ocd_transno));
+ LASSERTF((int)sizeof(((struct obd_connect_data *)0)->ocd_transno) == 8, "found %lld\n",
+ (long long)(int)sizeof(((struct obd_connect_data *)0)->ocd_transno));
+ LASSERTF((int)offsetof(struct obd_connect_data, ocd_group) == 48, "found %lld\n",
+ (long long)(int)offsetof(struct obd_connect_data, ocd_group));
+ LASSERTF((int)sizeof(((struct obd_connect_data *)0)->ocd_group) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct obd_connect_data *)0)->ocd_group));
+ LASSERTF((int)offsetof(struct obd_connect_data, ocd_cksum_types) == 52, "found %lld\n",
+ (long long)(int)offsetof(struct obd_connect_data, ocd_cksum_types));
+ LASSERTF((int)sizeof(((struct obd_connect_data *)0)->ocd_cksum_types) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct obd_connect_data *)0)->ocd_cksum_types));
+ LASSERTF((int)offsetof(struct obd_connect_data, ocd_max_easize) == 56, "found %lld\n",
+ (long long)(int)offsetof(struct obd_connect_data, ocd_max_easize));
+ LASSERTF((int)sizeof(((struct obd_connect_data *)0)->ocd_max_easize) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct obd_connect_data *)0)->ocd_max_easize));
+ LASSERTF((int)offsetof(struct obd_connect_data, ocd_instance) == 60, "found %lld\n",
+ (long long)(int)offsetof(struct obd_connect_data, ocd_instance));
+ LASSERTF((int)sizeof(((struct obd_connect_data *)0)->ocd_instance) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct obd_connect_data *)0)->ocd_instance));
+ LASSERTF((int)offsetof(struct obd_connect_data, ocd_maxbytes) == 64, "found %lld\n",
+ (long long)(int)offsetof(struct obd_connect_data, ocd_maxbytes));
+ LASSERTF((int)sizeof(((struct obd_connect_data *)0)->ocd_maxbytes) == 8, "found %lld\n",
+ (long long)(int)sizeof(((struct obd_connect_data *)0)->ocd_maxbytes));
+ LASSERTF((int)offsetof(struct obd_connect_data, padding1) == 72, "found %lld\n",
+ (long long)(int)offsetof(struct obd_connect_data, padding1));
+ LASSERTF((int)sizeof(((struct obd_connect_data *)0)->padding1) == 8, "found %lld\n",
+ (long long)(int)sizeof(((struct obd_connect_data *)0)->padding1));
+ LASSERTF((int)offsetof(struct obd_connect_data, padding2) == 80, "found %lld\n",
+ (long long)(int)offsetof(struct obd_connect_data, padding2));
+ LASSERTF((int)sizeof(((struct obd_connect_data *)0)->padding2) == 8, "found %lld\n",
+ (long long)(int)sizeof(((struct obd_connect_data *)0)->padding2));
+ LASSERTF((int)offsetof(struct obd_connect_data, padding3) == 88, "found %lld\n",
+ (long long)(int)offsetof(struct obd_connect_data, padding3));
+ LASSERTF((int)sizeof(((struct obd_connect_data *)0)->padding3) == 8, "found %lld\n",
+ (long long)(int)sizeof(((struct obd_connect_data *)0)->padding3));
+ LASSERTF((int)offsetof(struct obd_connect_data, padding4) == 96, "found %lld\n",
+ (long long)(int)offsetof(struct obd_connect_data, padding4));
+ LASSERTF((int)sizeof(((struct obd_connect_data *)0)->padding4) == 8, "found %lld\n",
+ (long long)(int)sizeof(((struct obd_connect_data *)0)->padding4));
+ LASSERTF((int)offsetof(struct obd_connect_data, padding5) == 104, "found %lld\n",
+ (long long)(int)offsetof(struct obd_connect_data, padding5));
+ LASSERTF((int)sizeof(((struct obd_connect_data *)0)->padding5) == 8, "found %lld\n",
+ (long long)(int)sizeof(((struct obd_connect_data *)0)->padding5));
+ LASSERTF((int)offsetof(struct obd_connect_data, padding6) == 112, "found %lld\n",
+ (long long)(int)offsetof(struct obd_connect_data, padding6));
+ LASSERTF((int)sizeof(((struct obd_connect_data *)0)->padding6) == 8, "found %lld\n",
+ (long long)(int)sizeof(((struct obd_connect_data *)0)->padding6));
+ LASSERTF((int)offsetof(struct obd_connect_data, padding7) == 120, "found %lld\n",
+ (long long)(int)offsetof(struct obd_connect_data, padding7));
+ LASSERTF((int)sizeof(((struct obd_connect_data *)0)->padding7) == 8, "found %lld\n",
+ (long long)(int)sizeof(((struct obd_connect_data *)0)->padding7));
+ LASSERTF((int)offsetof(struct obd_connect_data, padding8) == 128, "found %lld\n",
+ (long long)(int)offsetof(struct obd_connect_data, padding8));
+ LASSERTF((int)sizeof(((struct obd_connect_data *)0)->padding8) == 8, "found %lld\n",
+ (long long)(int)sizeof(((struct obd_connect_data *)0)->padding8));
+ LASSERTF((int)offsetof(struct obd_connect_data, padding9) == 136, "found %lld\n",
+ (long long)(int)offsetof(struct obd_connect_data, padding9));
+ LASSERTF((int)sizeof(((struct obd_connect_data *)0)->padding9) == 8, "found %lld\n",
+ (long long)(int)sizeof(((struct obd_connect_data *)0)->padding9));
+ LASSERTF((int)offsetof(struct obd_connect_data, paddingA) == 144, "found %lld\n",
+ (long long)(int)offsetof(struct obd_connect_data, paddingA));
+ LASSERTF((int)sizeof(((struct obd_connect_data *)0)->paddingA) == 8, "found %lld\n",
+ (long long)(int)sizeof(((struct obd_connect_data *)0)->paddingA));
+ LASSERTF((int)offsetof(struct obd_connect_data, paddingB) == 152, "found %lld\n",
+ (long long)(int)offsetof(struct obd_connect_data, paddingB));
+ LASSERTF((int)sizeof(((struct obd_connect_data *)0)->paddingB) == 8, "found %lld\n",
+ (long long)(int)sizeof(((struct obd_connect_data *)0)->paddingB));
+ LASSERTF((int)offsetof(struct obd_connect_data, paddingC) == 160, "found %lld\n",
+ (long long)(int)offsetof(struct obd_connect_data, paddingC));
+ LASSERTF((int)sizeof(((struct obd_connect_data *)0)->paddingC) == 8, "found %lld\n",
+ (long long)(int)sizeof(((struct obd_connect_data *)0)->paddingC));
+ LASSERTF((int)offsetof(struct obd_connect_data, paddingD) == 168, "found %lld\n",
+ (long long)(int)offsetof(struct obd_connect_data, paddingD));
+ LASSERTF((int)sizeof(((struct obd_connect_data *)0)->paddingD) == 8, "found %lld\n",
+ (long long)(int)sizeof(((struct obd_connect_data *)0)->paddingD));
+ LASSERTF((int)offsetof(struct obd_connect_data, paddingE) == 176, "found %lld\n",
+ (long long)(int)offsetof(struct obd_connect_data, paddingE));
+ LASSERTF((int)sizeof(((struct obd_connect_data *)0)->paddingE) == 8, "found %lld\n",
+ (long long)(int)sizeof(((struct obd_connect_data *)0)->paddingE));
+ LASSERTF((int)offsetof(struct obd_connect_data, paddingF) == 184, "found %lld\n",
+ (long long)(int)offsetof(struct obd_connect_data, paddingF));
+ LASSERTF((int)sizeof(((struct obd_connect_data *)0)->paddingF) == 8, "found %lld\n",
+ (long long)(int)sizeof(((struct obd_connect_data *)0)->paddingF));
+ LASSERTF(OBD_CONNECT_RDONLY == 0x1ULL, "found 0x%.16llxULL\n",
+ OBD_CONNECT_RDONLY);
+ LASSERTF(OBD_CONNECT_INDEX == 0x2ULL, "found 0x%.16llxULL\n",
+ OBD_CONNECT_INDEX);
+ LASSERTF(OBD_CONNECT_MDS == 0x4ULL, "found 0x%.16llxULL\n",
+ OBD_CONNECT_MDS);
+ LASSERTF(OBD_CONNECT_GRANT == 0x8ULL, "found 0x%.16llxULL\n",
+ OBD_CONNECT_GRANT);
+ LASSERTF(OBD_CONNECT_SRVLOCK == 0x10ULL, "found 0x%.16llxULL\n",
+ OBD_CONNECT_SRVLOCK);
+ LASSERTF(OBD_CONNECT_VERSION == 0x20ULL, "found 0x%.16llxULL\n",
+ OBD_CONNECT_VERSION);
+ LASSERTF(OBD_CONNECT_REQPORTAL == 0x40ULL, "found 0x%.16llxULL\n",
+ OBD_CONNECT_REQPORTAL);
+ LASSERTF(OBD_CONNECT_ACL == 0x80ULL, "found 0x%.16llxULL\n",
+ OBD_CONNECT_ACL);
+ LASSERTF(OBD_CONNECT_XATTR == 0x100ULL, "found 0x%.16llxULL\n",
+ OBD_CONNECT_XATTR);
+ LASSERTF(OBD_CONNECT_CROW == 0x200ULL, "found 0x%.16llxULL\n",
+ OBD_CONNECT_CROW);
+ LASSERTF(OBD_CONNECT_TRUNCLOCK == 0x400ULL, "found 0x%.16llxULL\n",
+ OBD_CONNECT_TRUNCLOCK);
+ LASSERTF(OBD_CONNECT_TRANSNO == 0x800ULL, "found 0x%.16llxULL\n",
+ OBD_CONNECT_TRANSNO);
+ LASSERTF(OBD_CONNECT_IBITS == 0x1000ULL, "found 0x%.16llxULL\n",
+ OBD_CONNECT_IBITS);
+ LASSERTF(OBD_CONNECT_JOIN == 0x2000ULL, "found 0x%.16llxULL\n",
+ OBD_CONNECT_JOIN);
+ LASSERTF(OBD_CONNECT_ATTRFID == 0x4000ULL, "found 0x%.16llxULL\n",
+ OBD_CONNECT_ATTRFID);
+ LASSERTF(OBD_CONNECT_NODEVOH == 0x8000ULL, "found 0x%.16llxULL\n",
+ OBD_CONNECT_NODEVOH);
+ LASSERTF(OBD_CONNECT_RMT_CLIENT == 0x10000ULL, "found 0x%.16llxULL\n",
+ OBD_CONNECT_RMT_CLIENT);
+ LASSERTF(OBD_CONNECT_RMT_CLIENT_FORCE == 0x20000ULL, "found 0x%.16llxULL\n",
+ OBD_CONNECT_RMT_CLIENT_FORCE);
+ LASSERTF(OBD_CONNECT_BRW_SIZE == 0x40000ULL, "found 0x%.16llxULL\n",
+ OBD_CONNECT_BRW_SIZE);
+ LASSERTF(OBD_CONNECT_QUOTA64 == 0x80000ULL, "found 0x%.16llxULL\n",
+ OBD_CONNECT_QUOTA64);
+ LASSERTF(OBD_CONNECT_MDS_CAPA == 0x100000ULL, "found 0x%.16llxULL\n",
+ OBD_CONNECT_MDS_CAPA);
+ LASSERTF(OBD_CONNECT_OSS_CAPA == 0x200000ULL, "found 0x%.16llxULL\n",
+ OBD_CONNECT_OSS_CAPA);
+ LASSERTF(OBD_CONNECT_CANCELSET == 0x400000ULL, "found 0x%.16llxULL\n",
+ OBD_CONNECT_CANCELSET);
+ LASSERTF(OBD_CONNECT_SOM == 0x800000ULL, "found 0x%.16llxULL\n",
+ OBD_CONNECT_SOM);
+ LASSERTF(OBD_CONNECT_AT == 0x1000000ULL, "found 0x%.16llxULL\n",
+ OBD_CONNECT_AT);
+ LASSERTF(OBD_CONNECT_LRU_RESIZE == 0x2000000ULL, "found 0x%.16llxULL\n",
+ OBD_CONNECT_LRU_RESIZE);
+ LASSERTF(OBD_CONNECT_MDS_MDS == 0x4000000ULL, "found 0x%.16llxULL\n",
+ OBD_CONNECT_MDS_MDS);
+ LASSERTF(OBD_CONNECT_REAL == 0x8000000ULL, "found 0x%.16llxULL\n",
+ OBD_CONNECT_REAL);
+ LASSERTF(OBD_CONNECT_CHANGE_QS == 0x10000000ULL, "found 0x%.16llxULL\n",
+ OBD_CONNECT_CHANGE_QS);
+ LASSERTF(OBD_CONNECT_CKSUM == 0x20000000ULL, "found 0x%.16llxULL\n",
+ OBD_CONNECT_CKSUM);
+ LASSERTF(OBD_CONNECT_FID == 0x40000000ULL, "found 0x%.16llxULL\n",
+ OBD_CONNECT_FID);
+ LASSERTF(OBD_CONNECT_VBR == 0x80000000ULL, "found 0x%.16llxULL\n",
+ OBD_CONNECT_VBR);
+ LASSERTF(OBD_CONNECT_LOV_V3 == 0x100000000ULL, "found 0x%.16llxULL\n",
+ OBD_CONNECT_LOV_V3);
+ LASSERTF(OBD_CONNECT_GRANT_SHRINK == 0x200000000ULL, "found 0x%.16llxULL\n",
+ OBD_CONNECT_GRANT_SHRINK);
+ LASSERTF(OBD_CONNECT_SKIP_ORPHAN == 0x400000000ULL, "found 0x%.16llxULL\n",
+ OBD_CONNECT_SKIP_ORPHAN);
+ LASSERTF(OBD_CONNECT_MAX_EASIZE == 0x800000000ULL, "found 0x%.16llxULL\n",
+ OBD_CONNECT_MAX_EASIZE);
+ LASSERTF(OBD_CONNECT_FULL20 == 0x1000000000ULL, "found 0x%.16llxULL\n",
+ OBD_CONNECT_FULL20);
+ LASSERTF(OBD_CONNECT_LAYOUTLOCK == 0x2000000000ULL, "found 0x%.16llxULL\n",
+ OBD_CONNECT_LAYOUTLOCK);
+ LASSERTF(OBD_CONNECT_64BITHASH == 0x4000000000ULL, "found 0x%.16llxULL\n",
+ OBD_CONNECT_64BITHASH);
+ LASSERTF(OBD_CONNECT_MAXBYTES == 0x8000000000ULL, "found 0x%.16llxULL\n",
+ OBD_CONNECT_MAXBYTES);
+ LASSERTF(OBD_CONNECT_IMP_RECOV == 0x10000000000ULL, "found 0x%.16llxULL\n",
+ OBD_CONNECT_IMP_RECOV);
+ LASSERTF(OBD_CONNECT_JOBSTATS == 0x20000000000ULL, "found 0x%.16llxULL\n",
+ OBD_CONNECT_JOBSTATS);
+ LASSERTF(OBD_CONNECT_UMASK == 0x40000000000ULL, "found 0x%.16llxULL\n",
+ OBD_CONNECT_UMASK);
+ LASSERTF(OBD_CONNECT_EINPROGRESS == 0x80000000000ULL, "found 0x%.16llxULL\n",
+ OBD_CONNECT_EINPROGRESS);
+ LASSERTF(OBD_CONNECT_GRANT_PARAM == 0x100000000000ULL, "found 0x%.16llxULL\n",
+ OBD_CONNECT_GRANT_PARAM);
+ LASSERTF(OBD_CONNECT_FLOCK_OWNER == 0x200000000000ULL, "found 0x%.16llxULL\n",
+ OBD_CONNECT_FLOCK_OWNER);
+ LASSERTF(OBD_CONNECT_LVB_TYPE == 0x400000000000ULL, "found 0x%.16llxULL\n",
+ OBD_CONNECT_LVB_TYPE);
+ LASSERTF(OBD_CONNECT_NANOSEC_TIME == 0x800000000000ULL, "found 0x%.16llxULL\n",
+ OBD_CONNECT_NANOSEC_TIME);
+ LASSERTF(OBD_CONNECT_LIGHTWEIGHT == 0x1000000000000ULL, "found 0x%.16llxULL\n",
+ OBD_CONNECT_LIGHTWEIGHT);
+ LASSERTF(OBD_CONNECT_SHORTIO == 0x2000000000000ULL, "found 0x%.16llxULL\n",
+ OBD_CONNECT_SHORTIO);
+ LASSERTF(OBD_CONNECT_PINGLESS == 0x4000000000000ULL, "found 0x%.16llxULL\n",
+ OBD_CONNECT_PINGLESS);
+ LASSERTF(OBD_CONNECT_FLOCK_DEAD == 0x8000000000000ULL,
+ "found 0x%.16llxULL\n", OBD_CONNECT_FLOCK_DEAD);
+ LASSERTF(OBD_CKSUM_CRC32 == 0x00000001UL, "found 0x%.8xUL\n",
+ (unsigned)OBD_CKSUM_CRC32);
+ LASSERTF(OBD_CKSUM_ADLER == 0x00000002UL, "found 0x%.8xUL\n",
+ (unsigned)OBD_CKSUM_ADLER);
+ LASSERTF(OBD_CKSUM_CRC32C == 0x00000004UL, "found 0x%.8xUL\n",
+ (unsigned)OBD_CKSUM_CRC32C);
+
+ /* Checks for struct obdo */
+ LASSERTF((int)sizeof(struct obdo) == 208, "found %lld\n",
+ (long long)(int)sizeof(struct obdo));
+ LASSERTF((int)offsetof(struct obdo, o_valid) == 0, "found %lld\n",
+ (long long)(int)offsetof(struct obdo, o_valid));
+ LASSERTF((int)sizeof(((struct obdo *)0)->o_valid) == 8, "found %lld\n",
+ (long long)(int)sizeof(((struct obdo *)0)->o_valid));
+ LASSERTF((int)offsetof(struct obdo, o_oi) == 8, "found %lld\n",
+ (long long)(int)offsetof(struct obdo, o_oi));
+ LASSERTF((int)sizeof(((struct obdo *)0)->o_oi) == 16, "found %lld\n",
+ (long long)(int)sizeof(((struct obdo *)0)->o_oi));
+ LASSERTF((int)offsetof(struct obdo, o_parent_seq) == 24, "found %lld\n",
+ (long long)(int)offsetof(struct obdo, o_parent_seq));
+ LASSERTF((int)sizeof(((struct obdo *)0)->o_parent_seq) == 8, "found %lld\n",
+ (long long)(int)sizeof(((struct obdo *)0)->o_parent_seq));
+ LASSERTF((int)offsetof(struct obdo, o_size) == 32, "found %lld\n",
+ (long long)(int)offsetof(struct obdo, o_size));
+ LASSERTF((int)sizeof(((struct obdo *)0)->o_size) == 8, "found %lld\n",
+ (long long)(int)sizeof(((struct obdo *)0)->o_size));
+ LASSERTF((int)offsetof(struct obdo, o_mtime) == 40, "found %lld\n",
+ (long long)(int)offsetof(struct obdo, o_mtime));
+ LASSERTF((int)sizeof(((struct obdo *)0)->o_mtime) == 8, "found %lld\n",
+ (long long)(int)sizeof(((struct obdo *)0)->o_mtime));
+ LASSERTF((int)offsetof(struct obdo, o_atime) == 48, "found %lld\n",
+ (long long)(int)offsetof(struct obdo, o_atime));
+ LASSERTF((int)sizeof(((struct obdo *)0)->o_atime) == 8, "found %lld\n",
+ (long long)(int)sizeof(((struct obdo *)0)->o_atime));
+ LASSERTF((int)offsetof(struct obdo, o_ctime) == 56, "found %lld\n",
+ (long long)(int)offsetof(struct obdo, o_ctime));
+ LASSERTF((int)sizeof(((struct obdo *)0)->o_ctime) == 8, "found %lld\n",
+ (long long)(int)sizeof(((struct obdo *)0)->o_ctime));
+ LASSERTF((int)offsetof(struct obdo, o_blocks) == 64, "found %lld\n",
+ (long long)(int)offsetof(struct obdo, o_blocks));
+ LASSERTF((int)sizeof(((struct obdo *)0)->o_blocks) == 8, "found %lld\n",
+ (long long)(int)sizeof(((struct obdo *)0)->o_blocks));
+ LASSERTF((int)offsetof(struct obdo, o_grant) == 72, "found %lld\n",
+ (long long)(int)offsetof(struct obdo, o_grant));
+ LASSERTF((int)sizeof(((struct obdo *)0)->o_grant) == 8, "found %lld\n",
+ (long long)(int)sizeof(((struct obdo *)0)->o_grant));
+ LASSERTF((int)offsetof(struct obdo, o_blksize) == 80, "found %lld\n",
+ (long long)(int)offsetof(struct obdo, o_blksize));
+ LASSERTF((int)sizeof(((struct obdo *)0)->o_blksize) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct obdo *)0)->o_blksize));
+ LASSERTF((int)offsetof(struct obdo, o_mode) == 84, "found %lld\n",
+ (long long)(int)offsetof(struct obdo, o_mode));
+ LASSERTF((int)sizeof(((struct obdo *)0)->o_mode) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct obdo *)0)->o_mode));
+ LASSERTF((int)offsetof(struct obdo, o_uid) == 88, "found %lld\n",
+ (long long)(int)offsetof(struct obdo, o_uid));
+ LASSERTF((int)sizeof(((struct obdo *)0)->o_uid) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct obdo *)0)->o_uid));
+ LASSERTF((int)offsetof(struct obdo, o_gid) == 92, "found %lld\n",
+ (long long)(int)offsetof(struct obdo, o_gid));
+ LASSERTF((int)sizeof(((struct obdo *)0)->o_gid) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct obdo *)0)->o_gid));
+ LASSERTF((int)offsetof(struct obdo, o_flags) == 96, "found %lld\n",
+ (long long)(int)offsetof(struct obdo, o_flags));
+ LASSERTF((int)sizeof(((struct obdo *)0)->o_flags) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct obdo *)0)->o_flags));
+ LASSERTF((int)offsetof(struct obdo, o_nlink) == 100, "found %lld\n",
+ (long long)(int)offsetof(struct obdo, o_nlink));
+ LASSERTF((int)sizeof(((struct obdo *)0)->o_nlink) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct obdo *)0)->o_nlink));
+ LASSERTF((int)offsetof(struct obdo, o_parent_oid) == 104, "found %lld\n",
+ (long long)(int)offsetof(struct obdo, o_parent_oid));
+ LASSERTF((int)sizeof(((struct obdo *)0)->o_parent_oid) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct obdo *)0)->o_parent_oid));
+ LASSERTF((int)offsetof(struct obdo, o_misc) == 108, "found %lld\n",
+ (long long)(int)offsetof(struct obdo, o_misc));
+ LASSERTF((int)sizeof(((struct obdo *)0)->o_misc) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct obdo *)0)->o_misc));
+ LASSERTF((int)offsetof(struct obdo, o_ioepoch) == 112, "found %lld\n",
+ (long long)(int)offsetof(struct obdo, o_ioepoch));
+ LASSERTF((int)sizeof(((struct obdo *)0)->o_ioepoch) == 8, "found %lld\n",
+ (long long)(int)sizeof(((struct obdo *)0)->o_ioepoch));
+ LASSERTF((int)offsetof(struct obdo, o_stripe_idx) == 120, "found %lld\n",
+ (long long)(int)offsetof(struct obdo, o_stripe_idx));
+ LASSERTF((int)sizeof(((struct obdo *)0)->o_stripe_idx) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct obdo *)0)->o_stripe_idx));
+ LASSERTF((int)offsetof(struct obdo, o_parent_ver) == 124, "found %lld\n",
+ (long long)(int)offsetof(struct obdo, o_parent_ver));
+ LASSERTF((int)sizeof(((struct obdo *)0)->o_parent_ver) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct obdo *)0)->o_parent_ver));
+ LASSERTF((int)offsetof(struct obdo, o_handle) == 128, "found %lld\n",
+ (long long)(int)offsetof(struct obdo, o_handle));
+ LASSERTF((int)sizeof(((struct obdo *)0)->o_handle) == 8, "found %lld\n",
+ (long long)(int)sizeof(((struct obdo *)0)->o_handle));
+ LASSERTF((int)offsetof(struct obdo, o_lcookie) == 136, "found %lld\n",
+ (long long)(int)offsetof(struct obdo, o_lcookie));
+ LASSERTF((int)sizeof(((struct obdo *)0)->o_lcookie) == 32, "found %lld\n",
+ (long long)(int)sizeof(((struct obdo *)0)->o_lcookie));
+ LASSERTF((int)offsetof(struct obdo, o_uid_h) == 168, "found %lld\n",
+ (long long)(int)offsetof(struct obdo, o_uid_h));
+ LASSERTF((int)sizeof(((struct obdo *)0)->o_uid_h) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct obdo *)0)->o_uid_h));
+ LASSERTF((int)offsetof(struct obdo, o_gid_h) == 172, "found %lld\n",
+ (long long)(int)offsetof(struct obdo, o_gid_h));
+ LASSERTF((int)sizeof(((struct obdo *)0)->o_gid_h) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct obdo *)0)->o_gid_h));
+ LASSERTF((int)offsetof(struct obdo, o_data_version) == 176, "found %lld\n",
+ (long long)(int)offsetof(struct obdo, o_data_version));
+ LASSERTF((int)sizeof(((struct obdo *)0)->o_data_version) == 8, "found %lld\n",
+ (long long)(int)sizeof(((struct obdo *)0)->o_data_version));
+ LASSERTF((int)offsetof(struct obdo, o_padding_4) == 184, "found %lld\n",
+ (long long)(int)offsetof(struct obdo, o_padding_4));
+ LASSERTF((int)sizeof(((struct obdo *)0)->o_padding_4) == 8, "found %lld\n",
+ (long long)(int)sizeof(((struct obdo *)0)->o_padding_4));
+ LASSERTF((int)offsetof(struct obdo, o_padding_5) == 192, "found %lld\n",
+ (long long)(int)offsetof(struct obdo, o_padding_5));
+ LASSERTF((int)sizeof(((struct obdo *)0)->o_padding_5) == 8, "found %lld\n",
+ (long long)(int)sizeof(((struct obdo *)0)->o_padding_5));
+ LASSERTF((int)offsetof(struct obdo, o_padding_6) == 200, "found %lld\n",
+ (long long)(int)offsetof(struct obdo, o_padding_6));
+ LASSERTF((int)sizeof(((struct obdo *)0)->o_padding_6) == 8, "found %lld\n",
+ (long long)(int)sizeof(((struct obdo *)0)->o_padding_6));
+ LASSERTF(OBD_MD_FLID == (0x00000001ULL), "found 0x%.16llxULL\n",
+ OBD_MD_FLID);
+ LASSERTF(OBD_MD_FLATIME == (0x00000002ULL), "found 0x%.16llxULL\n",
+ OBD_MD_FLATIME);
+ LASSERTF(OBD_MD_FLMTIME == (0x00000004ULL), "found 0x%.16llxULL\n",
+ OBD_MD_FLMTIME);
+ LASSERTF(OBD_MD_FLCTIME == (0x00000008ULL), "found 0x%.16llxULL\n",
+ OBD_MD_FLCTIME);
+ LASSERTF(OBD_MD_FLSIZE == (0x00000010ULL), "found 0x%.16llxULL\n",
+ OBD_MD_FLSIZE);
+ LASSERTF(OBD_MD_FLBLOCKS == (0x00000020ULL), "found 0x%.16llxULL\n",
+ OBD_MD_FLBLOCKS);
+ LASSERTF(OBD_MD_FLBLKSZ == (0x00000040ULL), "found 0x%.16llxULL\n",
+ OBD_MD_FLBLKSZ);
+ LASSERTF(OBD_MD_FLMODE == (0x00000080ULL), "found 0x%.16llxULL\n",
+ OBD_MD_FLMODE);
+ LASSERTF(OBD_MD_FLTYPE == (0x00000100ULL), "found 0x%.16llxULL\n",
+ OBD_MD_FLTYPE);
+ LASSERTF(OBD_MD_FLUID == (0x00000200ULL), "found 0x%.16llxULL\n",
+ OBD_MD_FLUID);
+ LASSERTF(OBD_MD_FLGID == (0x00000400ULL), "found 0x%.16llxULL\n",
+ OBD_MD_FLGID);
+ LASSERTF(OBD_MD_FLFLAGS == (0x00000800ULL), "found 0x%.16llxULL\n",
+ OBD_MD_FLFLAGS);
+ LASSERTF(OBD_MD_FLNLINK == (0x00002000ULL), "found 0x%.16llxULL\n",
+ OBD_MD_FLNLINK);
+ LASSERTF(OBD_MD_FLGENER == (0x00004000ULL), "found 0x%.16llxULL\n",
+ OBD_MD_FLGENER);
+ LASSERTF(OBD_MD_FLRDEV == (0x00010000ULL), "found 0x%.16llxULL\n",
+ OBD_MD_FLRDEV);
+ LASSERTF(OBD_MD_FLEASIZE == (0x00020000ULL), "found 0x%.16llxULL\n",
+ OBD_MD_FLEASIZE);
+ LASSERTF(OBD_MD_LINKNAME == (0x00040000ULL), "found 0x%.16llxULL\n",
+ OBD_MD_LINKNAME);
+ LASSERTF(OBD_MD_FLHANDLE == (0x00080000ULL), "found 0x%.16llxULL\n",
+ OBD_MD_FLHANDLE);
+ LASSERTF(OBD_MD_FLCKSUM == (0x00100000ULL), "found 0x%.16llxULL\n",
+ OBD_MD_FLCKSUM);
+ LASSERTF(OBD_MD_FLQOS == (0x00200000ULL), "found 0x%.16llxULL\n",
+ OBD_MD_FLQOS);
+ LASSERTF(OBD_MD_FLCOOKIE == (0x00800000ULL), "found 0x%.16llxULL\n",
+ OBD_MD_FLCOOKIE);
+ LASSERTF(OBD_MD_FLGROUP == (0x01000000ULL), "found 0x%.16llxULL\n",
+ OBD_MD_FLGROUP);
+ LASSERTF(OBD_MD_FLFID == (0x02000000ULL), "found 0x%.16llxULL\n",
+ OBD_MD_FLFID);
+ LASSERTF(OBD_MD_FLEPOCH == (0x04000000ULL), "found 0x%.16llxULL\n",
+ OBD_MD_FLEPOCH);
+ LASSERTF(OBD_MD_FLGRANT == (0x08000000ULL), "found 0x%.16llxULL\n",
+ OBD_MD_FLGRANT);
+ LASSERTF(OBD_MD_FLDIREA == (0x10000000ULL), "found 0x%.16llxULL\n",
+ OBD_MD_FLDIREA);
+ LASSERTF(OBD_MD_FLUSRQUOTA == (0x20000000ULL), "found 0x%.16llxULL\n",
+ OBD_MD_FLUSRQUOTA);
+ LASSERTF(OBD_MD_FLGRPQUOTA == (0x40000000ULL), "found 0x%.16llxULL\n",
+ OBD_MD_FLGRPQUOTA);
+ LASSERTF(OBD_MD_FLMODEASIZE == (0x80000000ULL), "found 0x%.16llxULL\n",
+ OBD_MD_FLMODEASIZE);
+ LASSERTF(OBD_MD_MDS == (0x0000000100000000ULL), "found 0x%.16llxULL\n",
+ OBD_MD_MDS);
+ LASSERTF(OBD_MD_REINT == (0x0000000200000000ULL), "found 0x%.16llxULL\n",
+ OBD_MD_REINT);
+ LASSERTF(OBD_MD_MEA == (0x0000000400000000ULL), "found 0x%.16llxULL\n",
+ OBD_MD_MEA);
+ LASSERTF(OBD_MD_TSTATE == (0x0000000800000000ULL),
+ "found 0x%.16llxULL\n", OBD_MD_TSTATE);
+ LASSERTF(OBD_MD_FLXATTR == (0x0000001000000000ULL), "found 0x%.16llxULL\n",
+ OBD_MD_FLXATTR);
+ LASSERTF(OBD_MD_FLXATTRLS == (0x0000002000000000ULL), "found 0x%.16llxULL\n",
+ OBD_MD_FLXATTRLS);
+ LASSERTF(OBD_MD_FLXATTRRM == (0x0000004000000000ULL), "found 0x%.16llxULL\n",
+ OBD_MD_FLXATTRRM);
+ LASSERTF(OBD_MD_FLACL == (0x0000008000000000ULL), "found 0x%.16llxULL\n",
+ OBD_MD_FLACL);
+ LASSERTF(OBD_MD_FLRMTPERM == (0x0000010000000000ULL), "found 0x%.16llxULL\n",
+ OBD_MD_FLRMTPERM);
+ LASSERTF(OBD_MD_FLMDSCAPA == (0x0000020000000000ULL), "found 0x%.16llxULL\n",
+ OBD_MD_FLMDSCAPA);
+ LASSERTF(OBD_MD_FLOSSCAPA == (0x0000040000000000ULL), "found 0x%.16llxULL\n",
+ OBD_MD_FLOSSCAPA);
+ LASSERTF(OBD_MD_FLCKSPLIT == (0x0000080000000000ULL), "found 0x%.16llxULL\n",
+ OBD_MD_FLCKSPLIT);
+ LASSERTF(OBD_MD_FLCROSSREF == (0x0000100000000000ULL), "found 0x%.16llxULL\n",
+ OBD_MD_FLCROSSREF);
+ LASSERTF(OBD_MD_FLGETATTRLOCK == (0x0000200000000000ULL), "found 0x%.16llxULL\n",
+ OBD_MD_FLGETATTRLOCK);
+ LASSERTF(OBD_MD_FLRMTLSETFACL == (0x0001000000000000ULL), "found 0x%.16llxULL\n",
+ OBD_MD_FLRMTLSETFACL);
+ LASSERTF(OBD_MD_FLRMTLGETFACL == (0x0002000000000000ULL), "found 0x%.16llxULL\n",
+ OBD_MD_FLRMTLGETFACL);
+ LASSERTF(OBD_MD_FLRMTRSETFACL == (0x0004000000000000ULL), "found 0x%.16llxULL\n",
+ OBD_MD_FLRMTRSETFACL);
+ LASSERTF(OBD_MD_FLRMTRGETFACL == (0x0008000000000000ULL), "found 0x%.16llxULL\n",
+ OBD_MD_FLRMTRGETFACL);
+ LASSERTF(OBD_MD_FLDATAVERSION == (0x0010000000000000ULL), "found 0x%.16llxULL\n",
+ OBD_MD_FLDATAVERSION);
+ CLASSERT(OBD_FL_INLINEDATA == 0x00000001);
+ CLASSERT(OBD_FL_OBDMDEXISTS == 0x00000002);
+ CLASSERT(OBD_FL_DELORPHAN == 0x00000004);
+ CLASSERT(OBD_FL_NORPC == 0x00000008);
+ CLASSERT(OBD_FL_IDONLY == 0x00000010);
+ CLASSERT(OBD_FL_RECREATE_OBJS == 0x00000020);
+ CLASSERT(OBD_FL_DEBUG_CHECK == 0x00000040);
+ CLASSERT(OBD_FL_NO_USRQUOTA == 0x00000100);
+ CLASSERT(OBD_FL_NO_GRPQUOTA == 0x00000200);
+ CLASSERT(OBD_FL_CREATE_CROW == 0x00000400);
+ CLASSERT(OBD_FL_SRVLOCK == 0x00000800);
+ CLASSERT(OBD_FL_CKSUM_CRC32 == 0x00001000);
+ CLASSERT(OBD_FL_CKSUM_ADLER == 0x00002000);
+ CLASSERT(OBD_FL_CKSUM_CRC32C == 0x00004000);
+ CLASSERT(OBD_FL_CKSUM_RSVD2 == 0x00008000);
+ CLASSERT(OBD_FL_CKSUM_RSVD3 == 0x00010000);
+ CLASSERT(OBD_FL_SHRINK_GRANT == 0x00020000);
+ CLASSERT(OBD_FL_MMAP == 0x00040000);
+ CLASSERT(OBD_FL_RECOV_RESEND == 0x00080000);
+ CLASSERT(OBD_FL_NOSPC_BLK == 0x00100000);
+ CLASSERT(OBD_FL_LOCAL_MASK == 0xf0000000);
+
+ /* Checks for struct lov_ost_data_v1 */
+ LASSERTF((int)sizeof(struct lov_ost_data_v1) == 24, "found %lld\n",
+ (long long)(int)sizeof(struct lov_ost_data_v1));
+ LASSERTF((int)offsetof(struct lov_ost_data_v1, l_ost_oi) == 0, "found %lld\n",
+ (long long)(int)offsetof(struct lov_ost_data_v1, l_ost_oi));
+ LASSERTF((int)sizeof(((struct lov_ost_data_v1 *)0)->l_ost_oi) == 16, "found %lld\n",
+ (long long)(int)sizeof(((struct lov_ost_data_v1 *)0)->l_ost_oi));
+ LASSERTF((int)offsetof(struct lov_ost_data_v1, l_ost_gen) == 16, "found %lld\n",
+ (long long)(int)offsetof(struct lov_ost_data_v1, l_ost_gen));
+ LASSERTF((int)sizeof(((struct lov_ost_data_v1 *)0)->l_ost_gen) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct lov_ost_data_v1 *)0)->l_ost_gen));
+ LASSERTF((int)offsetof(struct lov_ost_data_v1, l_ost_idx) == 20, "found %lld\n",
+ (long long)(int)offsetof(struct lov_ost_data_v1, l_ost_idx));
+ LASSERTF((int)sizeof(((struct lov_ost_data_v1 *)0)->l_ost_idx) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct lov_ost_data_v1 *)0)->l_ost_idx));
+
+ /* Checks for struct lov_mds_md_v1 */
+ LASSERTF((int)sizeof(struct lov_mds_md_v1) == 32, "found %lld\n",
+ (long long)(int)sizeof(struct lov_mds_md_v1));
+ LASSERTF((int)offsetof(struct lov_mds_md_v1, lmm_magic) == 0, "found %lld\n",
+ (long long)(int)offsetof(struct lov_mds_md_v1, lmm_magic));
+ LASSERTF((int)sizeof(((struct lov_mds_md_v1 *)0)->lmm_magic) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct lov_mds_md_v1 *)0)->lmm_magic));
+ LASSERTF((int)offsetof(struct lov_mds_md_v1, lmm_pattern) == 4, "found %lld\n",
+ (long long)(int)offsetof(struct lov_mds_md_v1, lmm_pattern));
+ LASSERTF((int)sizeof(((struct lov_mds_md_v1 *)0)->lmm_pattern) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct lov_mds_md_v1 *)0)->lmm_pattern));
+ LASSERTF((int)offsetof(struct lov_mds_md_v1, lmm_oi) == 8, "found %lld\n",
+ (long long)(int)offsetof(struct lov_mds_md_v1, lmm_oi));
+ LASSERTF((int)sizeof(((struct lov_mds_md_v1 *)0)->lmm_oi) == 16, "found %lld\n",
+ (long long)(int)sizeof(((struct lov_mds_md_v1 *)0)->lmm_oi));
+ LASSERTF((int)offsetof(struct lov_mds_md_v1, lmm_stripe_size) == 24, "found %lld\n",
+ (long long)(int)offsetof(struct lov_mds_md_v1, lmm_stripe_size));
+ LASSERTF((int)sizeof(((struct lov_mds_md_v1 *)0)->lmm_stripe_size) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct lov_mds_md_v1 *)0)->lmm_stripe_size));
+ LASSERTF((int)offsetof(struct lov_mds_md_v1, lmm_stripe_count) == 28, "found %lld\n",
+ (long long)(int)offsetof(struct lov_mds_md_v1, lmm_stripe_count));
+ LASSERTF((int)sizeof(((struct lov_mds_md_v1 *)0)->lmm_stripe_count) == 2, "found %lld\n",
+ (long long)(int)sizeof(((struct lov_mds_md_v1 *)0)->lmm_stripe_count));
+ LASSERTF((int)offsetof(struct lov_mds_md_v1, lmm_layout_gen) == 30, "found %lld\n",
+ (long long)(int)offsetof(struct lov_mds_md_v1, lmm_layout_gen));
+ LASSERTF((int)sizeof(((struct lov_mds_md_v1 *)0)->lmm_layout_gen) == 2, "found %lld\n",
+ (long long)(int)sizeof(((struct lov_mds_md_v1 *)0)->lmm_layout_gen));
+ LASSERTF((int)offsetof(struct lov_mds_md_v1, lmm_objects[0]) == 32, "found %lld\n",
+ (long long)(int)offsetof(struct lov_mds_md_v1, lmm_objects[0]));
+ LASSERTF((int)sizeof(((struct lov_mds_md_v1 *)0)->lmm_objects[0]) == 24, "found %lld\n",
+ (long long)(int)sizeof(((struct lov_mds_md_v1 *)0)->lmm_objects[0]));
+ CLASSERT(LOV_MAGIC_V1 == 0x0BD10BD0);
+
+ /* Checks for struct lov_mds_md_v3 */
+ LASSERTF((int)sizeof(struct lov_mds_md_v3) == 48, "found %lld\n",
+ (long long)(int)sizeof(struct lov_mds_md_v3));
+ LASSERTF((int)offsetof(struct lov_mds_md_v3, lmm_magic) == 0, "found %lld\n",
+ (long long)(int)offsetof(struct lov_mds_md_v3, lmm_magic));
+ LASSERTF((int)sizeof(((struct lov_mds_md_v3 *)0)->lmm_magic) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct lov_mds_md_v3 *)0)->lmm_magic));
+ LASSERTF((int)offsetof(struct lov_mds_md_v3, lmm_pattern) == 4, "found %lld\n",
+ (long long)(int)offsetof(struct lov_mds_md_v3, lmm_pattern));
+ LASSERTF((int)sizeof(((struct lov_mds_md_v3 *)0)->lmm_pattern) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct lov_mds_md_v3 *)0)->lmm_pattern));
+ LASSERTF((int)offsetof(struct lov_mds_md_v3, lmm_oi) == 8, "found %lld\n",
+ (long long)(int)offsetof(struct lov_mds_md_v3, lmm_oi));
+ LASSERTF((int)sizeof(((struct lov_mds_md_v3 *)0)->lmm_oi) == 16, "found %lld\n",
+ (long long)(int)sizeof(((struct lov_mds_md_v3 *)0)->lmm_oi));
+ LASSERTF((int)offsetof(struct lov_mds_md_v3, lmm_stripe_size) == 24, "found %lld\n",
+ (long long)(int)offsetof(struct lov_mds_md_v3, lmm_stripe_size));
+ LASSERTF((int)sizeof(((struct lov_mds_md_v3 *)0)->lmm_stripe_size) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct lov_mds_md_v3 *)0)->lmm_stripe_size));
+ LASSERTF((int)offsetof(struct lov_mds_md_v3, lmm_stripe_count) == 28, "found %lld\n",
+ (long long)(int)offsetof(struct lov_mds_md_v3, lmm_stripe_count));
+ LASSERTF((int)sizeof(((struct lov_mds_md_v3 *)0)->lmm_stripe_count) == 2, "found %lld\n",
+ (long long)(int)sizeof(((struct lov_mds_md_v3 *)0)->lmm_stripe_count));
+ LASSERTF((int)offsetof(struct lov_mds_md_v3, lmm_layout_gen) == 30, "found %lld\n",
+ (long long)(int)offsetof(struct lov_mds_md_v3, lmm_layout_gen));
+ LASSERTF((int)sizeof(((struct lov_mds_md_v3 *)0)->lmm_layout_gen) == 2, "found %lld\n",
+ (long long)(int)sizeof(((struct lov_mds_md_v3 *)0)->lmm_layout_gen));
+ CLASSERT(LOV_MAXPOOLNAME == 16);
+ LASSERTF((int)offsetof(struct lov_mds_md_v3, lmm_pool_name[16]) == 48, "found %lld\n",
+ (long long)(int)offsetof(struct lov_mds_md_v3, lmm_pool_name[16]));
+ LASSERTF((int)sizeof(((struct lov_mds_md_v3 *)0)->lmm_pool_name[16]) == 1, "found %lld\n",
+ (long long)(int)sizeof(((struct lov_mds_md_v3 *)0)->lmm_pool_name[16]));
+ LASSERTF((int)offsetof(struct lov_mds_md_v3, lmm_objects[0]) == 48, "found %lld\n",
+ (long long)(int)offsetof(struct lov_mds_md_v3, lmm_objects[0]));
+ LASSERTF((int)sizeof(((struct lov_mds_md_v3 *)0)->lmm_objects[0]) == 24, "found %lld\n",
+ (long long)(int)sizeof(((struct lov_mds_md_v3 *)0)->lmm_objects[0]));
+ CLASSERT(LOV_MAGIC_V3 == 0x0BD30BD0);
+ LASSERTF(LOV_PATTERN_RAID0 == 0x00000001UL, "found 0x%.8xUL\n",
+ (unsigned)LOV_PATTERN_RAID0);
+ LASSERTF(LOV_PATTERN_RAID1 == 0x00000002UL, "found 0x%.8xUL\n",
+ (unsigned)LOV_PATTERN_RAID1);
+ LASSERTF(LOV_PATTERN_FIRST == 0x00000100UL, "found 0x%.8xUL\n",
+ (unsigned)LOV_PATTERN_FIRST);
+ LASSERTF(LOV_PATTERN_CMOBD == 0x00000200UL, "found 0x%.8xUL\n",
+ (unsigned)LOV_PATTERN_CMOBD);
+
+ /* Checks for struct obd_statfs */
+ LASSERTF((int)sizeof(struct obd_statfs) == 144, "found %lld\n",
+ (long long)(int)sizeof(struct obd_statfs));
+ LASSERTF((int)offsetof(struct obd_statfs, os_type) == 0, "found %lld\n",
+ (long long)(int)offsetof(struct obd_statfs, os_type));
+ LASSERTF((int)sizeof(((struct obd_statfs *)0)->os_type) == 8, "found %lld\n",
+ (long long)(int)sizeof(((struct obd_statfs *)0)->os_type));
+ LASSERTF((int)offsetof(struct obd_statfs, os_blocks) == 8, "found %lld\n",
+ (long long)(int)offsetof(struct obd_statfs, os_blocks));
+ LASSERTF((int)sizeof(((struct obd_statfs *)0)->os_blocks) == 8, "found %lld\n",
+ (long long)(int)sizeof(((struct obd_statfs *)0)->os_blocks));
+ LASSERTF((int)offsetof(struct obd_statfs, os_bfree) == 16, "found %lld\n",
+ (long long)(int)offsetof(struct obd_statfs, os_bfree));
+ LASSERTF((int)sizeof(((struct obd_statfs *)0)->os_bfree) == 8, "found %lld\n",
+ (long long)(int)sizeof(((struct obd_statfs *)0)->os_bfree));
+ LASSERTF((int)offsetof(struct obd_statfs, os_bavail) == 24, "found %lld\n",
+ (long long)(int)offsetof(struct obd_statfs, os_bavail));
+ LASSERTF((int)sizeof(((struct obd_statfs *)0)->os_bavail) == 8, "found %lld\n",
+ (long long)(int)sizeof(((struct obd_statfs *)0)->os_bavail));
+ LASSERTF((int)offsetof(struct obd_statfs, os_ffree) == 40, "found %lld\n",
+ (long long)(int)offsetof(struct obd_statfs, os_ffree));
+ LASSERTF((int)sizeof(((struct obd_statfs *)0)->os_ffree) == 8, "found %lld\n",
+ (long long)(int)sizeof(((struct obd_statfs *)0)->os_ffree));
+ LASSERTF((int)offsetof(struct obd_statfs, os_fsid) == 48, "found %lld\n",
+ (long long)(int)offsetof(struct obd_statfs, os_fsid));
+ LASSERTF((int)sizeof(((struct obd_statfs *)0)->os_fsid) == 40, "found %lld\n",
+ (long long)(int)sizeof(((struct obd_statfs *)0)->os_fsid));
+ LASSERTF((int)offsetof(struct obd_statfs, os_bsize) == 88, "found %lld\n",
+ (long long)(int)offsetof(struct obd_statfs, os_bsize));
+ LASSERTF((int)sizeof(((struct obd_statfs *)0)->os_bsize) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct obd_statfs *)0)->os_bsize));
+ LASSERTF((int)offsetof(struct obd_statfs, os_namelen) == 92, "found %lld\n",
+ (long long)(int)offsetof(struct obd_statfs, os_namelen));
+ LASSERTF((int)sizeof(((struct obd_statfs *)0)->os_namelen) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct obd_statfs *)0)->os_namelen));
+ LASSERTF((int)offsetof(struct obd_statfs, os_state) == 104, "found %lld\n",
+ (long long)(int)offsetof(struct obd_statfs, os_state));
+ LASSERTF((int)sizeof(((struct obd_statfs *)0)->os_state) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct obd_statfs *)0)->os_state));
+ LASSERTF((int)offsetof(struct obd_statfs, os_fprecreated) == 108, "found %lld\n",
+ (long long)(int)offsetof(struct obd_statfs, os_fprecreated));
+ LASSERTF((int)sizeof(((struct obd_statfs *)0)->os_fprecreated) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct obd_statfs *)0)->os_fprecreated));
+ LASSERTF((int)offsetof(struct obd_statfs, os_spare2) == 112, "found %lld\n",
+ (long long)(int)offsetof(struct obd_statfs, os_spare2));
+ LASSERTF((int)sizeof(((struct obd_statfs *)0)->os_spare2) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct obd_statfs *)0)->os_spare2));
+ LASSERTF((int)offsetof(struct obd_statfs, os_spare3) == 116, "found %lld\n",
+ (long long)(int)offsetof(struct obd_statfs, os_spare3));
+ LASSERTF((int)sizeof(((struct obd_statfs *)0)->os_spare3) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct obd_statfs *)0)->os_spare3));
+ LASSERTF((int)offsetof(struct obd_statfs, os_spare4) == 120, "found %lld\n",
+ (long long)(int)offsetof(struct obd_statfs, os_spare4));
+ LASSERTF((int)sizeof(((struct obd_statfs *)0)->os_spare4) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct obd_statfs *)0)->os_spare4));
+ LASSERTF((int)offsetof(struct obd_statfs, os_spare5) == 124, "found %lld\n",
+ (long long)(int)offsetof(struct obd_statfs, os_spare5));
+ LASSERTF((int)sizeof(((struct obd_statfs *)0)->os_spare5) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct obd_statfs *)0)->os_spare5));
+ LASSERTF((int)offsetof(struct obd_statfs, os_spare6) == 128, "found %lld\n",
+ (long long)(int)offsetof(struct obd_statfs, os_spare6));
+ LASSERTF((int)sizeof(((struct obd_statfs *)0)->os_spare6) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct obd_statfs *)0)->os_spare6));
+ LASSERTF((int)offsetof(struct obd_statfs, os_spare7) == 132, "found %lld\n",
+ (long long)(int)offsetof(struct obd_statfs, os_spare7));
+ LASSERTF((int)sizeof(((struct obd_statfs *)0)->os_spare7) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct obd_statfs *)0)->os_spare7));
+ LASSERTF((int)offsetof(struct obd_statfs, os_spare8) == 136, "found %lld\n",
+ (long long)(int)offsetof(struct obd_statfs, os_spare8));
+ LASSERTF((int)sizeof(((struct obd_statfs *)0)->os_spare8) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct obd_statfs *)0)->os_spare8));
+ LASSERTF((int)offsetof(struct obd_statfs, os_spare9) == 140, "found %lld\n",
+ (long long)(int)offsetof(struct obd_statfs, os_spare9));
+ LASSERTF((int)sizeof(((struct obd_statfs *)0)->os_spare9) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct obd_statfs *)0)->os_spare9));
+
+ /* Checks for struct obd_ioobj */
+ LASSERTF((int)sizeof(struct obd_ioobj) == 24, "found %lld\n",
+ (long long)(int)sizeof(struct obd_ioobj));
+ LASSERTF((int)offsetof(struct obd_ioobj, ioo_oid) == 0, "found %lld\n",
+ (long long)(int)offsetof(struct obd_ioobj, ioo_oid));
+ LASSERTF((int)sizeof(((struct obd_ioobj *)0)->ioo_oid) == 16, "found %lld\n",
+ (long long)(int)sizeof(((struct obd_ioobj *)0)->ioo_oid));
+ LASSERTF((int)offsetof(struct obd_ioobj, ioo_max_brw) == 16, "found %lld\n",
+ (long long)(int)offsetof(struct obd_ioobj, ioo_max_brw));
+ LASSERTF((int)sizeof(((struct obd_ioobj *)0)->ioo_max_brw) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct obd_ioobj *)0)->ioo_max_brw));
+ LASSERTF((int)offsetof(struct obd_ioobj, ioo_bufcnt) == 20, "found %lld\n",
+ (long long)(int)offsetof(struct obd_ioobj, ioo_bufcnt));
+ LASSERTF((int)sizeof(((struct obd_ioobj *)0)->ioo_bufcnt) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct obd_ioobj *)0)->ioo_bufcnt));
+
+ /* Checks for union lquota_id */
+ LASSERTF((int)sizeof(union lquota_id) == 16, "found %lld\n",
+ (long long)(int)sizeof(union lquota_id));
+
+ LASSERTF(QUOTABLOCK_BITS == 10, "found %lld\n",
+ (long long)QUOTABLOCK_BITS);
+ LASSERTF(QUOTABLOCK_SIZE == 1024, "found %lld\n",
+ (long long)QUOTABLOCK_SIZE);
+
+ /* Checks for struct obd_quotactl */
+ LASSERTF((int)sizeof(struct obd_quotactl) == 112, "found %lld\n",
+ (long long)(int)sizeof(struct obd_quotactl));
+ LASSERTF((int)offsetof(struct obd_quotactl, qc_cmd) == 0, "found %lld\n",
+ (long long)(int)offsetof(struct obd_quotactl, qc_cmd));
+ LASSERTF((int)sizeof(((struct obd_quotactl *)0)->qc_cmd) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct obd_quotactl *)0)->qc_cmd));
+ LASSERTF((int)offsetof(struct obd_quotactl, qc_type) == 4, "found %lld\n",
+ (long long)(int)offsetof(struct obd_quotactl, qc_type));
+ LASSERTF((int)sizeof(((struct obd_quotactl *)0)->qc_type) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct obd_quotactl *)0)->qc_type));
+ LASSERTF((int)offsetof(struct obd_quotactl, qc_id) == 8, "found %lld\n",
+ (long long)(int)offsetof(struct obd_quotactl, qc_id));
+ LASSERTF((int)sizeof(((struct obd_quotactl *)0)->qc_id) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct obd_quotactl *)0)->qc_id));
+ LASSERTF((int)offsetof(struct obd_quotactl, qc_stat) == 12, "found %lld\n",
+ (long long)(int)offsetof(struct obd_quotactl, qc_stat));
+ LASSERTF((int)sizeof(((struct obd_quotactl *)0)->qc_stat) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct obd_quotactl *)0)->qc_stat));
+ LASSERTF((int)offsetof(struct obd_quotactl, qc_dqinfo) == 16, "found %lld\n",
+ (long long)(int)offsetof(struct obd_quotactl, qc_dqinfo));
+ LASSERTF((int)sizeof(((struct obd_quotactl *)0)->qc_dqinfo) == 24, "found %lld\n",
+ (long long)(int)sizeof(((struct obd_quotactl *)0)->qc_dqinfo));
+ LASSERTF((int)offsetof(struct obd_quotactl, qc_dqblk) == 40, "found %lld\n",
+ (long long)(int)offsetof(struct obd_quotactl, qc_dqblk));
+ LASSERTF((int)sizeof(((struct obd_quotactl *)0)->qc_dqblk) == 72, "found %lld\n",
+ (long long)(int)sizeof(((struct obd_quotactl *)0)->qc_dqblk));
+
+ /* Checks for struct obd_dqinfo */
+ LASSERTF((int)sizeof(struct obd_dqinfo) == 24, "found %lld\n",
+ (long long)(int)sizeof(struct obd_dqinfo));
+ LASSERTF((int)offsetof(struct obd_dqinfo, dqi_bgrace) == 0, "found %lld\n",
+ (long long)(int)offsetof(struct obd_dqinfo, dqi_bgrace));
+ LASSERTF((int)sizeof(((struct obd_dqinfo *)0)->dqi_bgrace) == 8, "found %lld\n",
+ (long long)(int)sizeof(((struct obd_dqinfo *)0)->dqi_bgrace));
+ LASSERTF((int)offsetof(struct obd_dqinfo, dqi_igrace) == 8, "found %lld\n",
+ (long long)(int)offsetof(struct obd_dqinfo, dqi_igrace));
+ LASSERTF((int)sizeof(((struct obd_dqinfo *)0)->dqi_igrace) == 8, "found %lld\n",
+ (long long)(int)sizeof(((struct obd_dqinfo *)0)->dqi_igrace));
+ LASSERTF((int)offsetof(struct obd_dqinfo, dqi_flags) == 16, "found %lld\n",
+ (long long)(int)offsetof(struct obd_dqinfo, dqi_flags));
+ LASSERTF((int)sizeof(((struct obd_dqinfo *)0)->dqi_flags) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct obd_dqinfo *)0)->dqi_flags));
+ LASSERTF((int)offsetof(struct obd_dqinfo, dqi_valid) == 20, "found %lld\n",
+ (long long)(int)offsetof(struct obd_dqinfo, dqi_valid));
+ LASSERTF((int)sizeof(((struct obd_dqinfo *)0)->dqi_valid) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct obd_dqinfo *)0)->dqi_valid));
+
+ /* Checks for struct obd_dqblk */
+ LASSERTF((int)sizeof(struct obd_dqblk) == 72, "found %lld\n",
+ (long long)(int)sizeof(struct obd_dqblk));
+ LASSERTF((int)offsetof(struct obd_dqblk, dqb_bhardlimit) == 0, "found %lld\n",
+ (long long)(int)offsetof(struct obd_dqblk, dqb_bhardlimit));
+ LASSERTF((int)sizeof(((struct obd_dqblk *)0)->dqb_bhardlimit) == 8, "found %lld\n",
+ (long long)(int)sizeof(((struct obd_dqblk *)0)->dqb_bhardlimit));
+ LASSERTF((int)offsetof(struct obd_dqblk, dqb_bsoftlimit) == 8, "found %lld\n",
+ (long long)(int)offsetof(struct obd_dqblk, dqb_bsoftlimit));
+ LASSERTF((int)sizeof(((struct obd_dqblk *)0)->dqb_bsoftlimit) == 8, "found %lld\n",
+ (long long)(int)sizeof(((struct obd_dqblk *)0)->dqb_bsoftlimit));
+ LASSERTF((int)offsetof(struct obd_dqblk, dqb_curspace) == 16, "found %lld\n",
+ (long long)(int)offsetof(struct obd_dqblk, dqb_curspace));
+ LASSERTF((int)sizeof(((struct obd_dqblk *)0)->dqb_curspace) == 8, "found %lld\n",
+ (long long)(int)sizeof(((struct obd_dqblk *)0)->dqb_curspace));
+ LASSERTF((int)offsetof(struct obd_dqblk, dqb_ihardlimit) == 24, "found %lld\n",
+ (long long)(int)offsetof(struct obd_dqblk, dqb_ihardlimit));
+ LASSERTF((int)sizeof(((struct obd_dqblk *)0)->dqb_ihardlimit) == 8, "found %lld\n",
+ (long long)(int)sizeof(((struct obd_dqblk *)0)->dqb_ihardlimit));
+ LASSERTF((int)offsetof(struct obd_dqblk, dqb_isoftlimit) == 32, "found %lld\n",
+ (long long)(int)offsetof(struct obd_dqblk, dqb_isoftlimit));
+ LASSERTF((int)sizeof(((struct obd_dqblk *)0)->dqb_isoftlimit) == 8, "found %lld\n",
+ (long long)(int)sizeof(((struct obd_dqblk *)0)->dqb_isoftlimit));
+ LASSERTF((int)offsetof(struct obd_dqblk, dqb_curinodes) == 40, "found %lld\n",
+ (long long)(int)offsetof(struct obd_dqblk, dqb_curinodes));
+ LASSERTF((int)sizeof(((struct obd_dqblk *)0)->dqb_curinodes) == 8, "found %lld\n",
+ (long long)(int)sizeof(((struct obd_dqblk *)0)->dqb_curinodes));
+ LASSERTF((int)offsetof(struct obd_dqblk, dqb_btime) == 48, "found %lld\n",
+ (long long)(int)offsetof(struct obd_dqblk, dqb_btime));
+ LASSERTF((int)sizeof(((struct obd_dqblk *)0)->dqb_btime) == 8, "found %lld\n",
+ (long long)(int)sizeof(((struct obd_dqblk *)0)->dqb_btime));
+ LASSERTF((int)offsetof(struct obd_dqblk, dqb_itime) == 56, "found %lld\n",
+ (long long)(int)offsetof(struct obd_dqblk, dqb_itime));
+ LASSERTF((int)sizeof(((struct obd_dqblk *)0)->dqb_itime) == 8, "found %lld\n",
+ (long long)(int)sizeof(((struct obd_dqblk *)0)->dqb_itime));
+ LASSERTF((int)offsetof(struct obd_dqblk, dqb_valid) == 64, "found %lld\n",
+ (long long)(int)offsetof(struct obd_dqblk, dqb_valid));
+ LASSERTF((int)sizeof(((struct obd_dqblk *)0)->dqb_valid) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct obd_dqblk *)0)->dqb_valid));
+ LASSERTF((int)offsetof(struct obd_dqblk, dqb_padding) == 68, "found %lld\n",
+ (long long)(int)offsetof(struct obd_dqblk, dqb_padding));
+ LASSERTF((int)sizeof(((struct obd_dqblk *)0)->dqb_padding) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct obd_dqblk *)0)->dqb_padding));
+ LASSERTF(Q_QUOTACHECK == 0x800100, "found 0x%.8x\n",
+ Q_QUOTACHECK);
+ LASSERTF(Q_INITQUOTA == 0x800101, "found 0x%.8x\n",
+ Q_INITQUOTA);
+ LASSERTF(Q_GETOINFO == 0x800102, "found 0x%.8x\n",
+ Q_GETOINFO);
+ LASSERTF(Q_GETOQUOTA == 0x800103, "found 0x%.8x\n",
+ Q_GETOQUOTA);
+ LASSERTF(Q_FINVALIDATE == 0x800104, "found 0x%.8x\n",
+ Q_FINVALIDATE);
+
+ /* Checks for struct lquota_acct_rec */
+ LASSERTF((int)sizeof(struct lquota_acct_rec) == 16, "found %lld\n",
+ (long long)(int)sizeof(struct lquota_acct_rec));
+ LASSERTF((int)offsetof(struct lquota_acct_rec, bspace) == 0, "found %lld\n",
+ (long long)(int)offsetof(struct lquota_acct_rec, bspace));
+ LASSERTF((int)sizeof(((struct lquota_acct_rec *)0)->bspace) == 8, "found %lld\n",
+ (long long)(int)sizeof(((struct lquota_acct_rec *)0)->bspace));
+ LASSERTF((int)offsetof(struct lquota_acct_rec, ispace) == 8, "found %lld\n",
+ (long long)(int)offsetof(struct lquota_acct_rec, ispace));
+ LASSERTF((int)sizeof(((struct lquota_acct_rec *)0)->ispace) == 8, "found %lld\n",
+ (long long)(int)sizeof(((struct lquota_acct_rec *)0)->ispace));
+
+ /* Checks for struct lquota_glb_rec */
+ LASSERTF((int)sizeof(struct lquota_glb_rec) == 32, "found %lld\n",
+ (long long)(int)sizeof(struct lquota_glb_rec));
+ LASSERTF((int)offsetof(struct lquota_glb_rec, qbr_hardlimit) == 0, "found %lld\n",
+ (long long)(int)offsetof(struct lquota_glb_rec, qbr_hardlimit));
+ LASSERTF((int)sizeof(((struct lquota_glb_rec *)0)->qbr_hardlimit) == 8, "found %lld\n",
+ (long long)(int)sizeof(((struct lquota_glb_rec *)0)->qbr_hardlimit));
+ LASSERTF((int)offsetof(struct lquota_glb_rec, qbr_softlimit) == 8, "found %lld\n",
+ (long long)(int)offsetof(struct lquota_glb_rec, qbr_softlimit));
+ LASSERTF((int)sizeof(((struct lquota_glb_rec *)0)->qbr_softlimit) == 8, "found %lld\n",
+ (long long)(int)sizeof(((struct lquota_glb_rec *)0)->qbr_softlimit));
+ LASSERTF((int)offsetof(struct lquota_glb_rec, qbr_time) == 16, "found %lld\n",
+ (long long)(int)offsetof(struct lquota_glb_rec, qbr_time));
+ LASSERTF((int)sizeof(((struct lquota_glb_rec *)0)->qbr_time) == 8, "found %lld\n",
+ (long long)(int)sizeof(((struct lquota_glb_rec *)0)->qbr_time));
+ LASSERTF((int)offsetof(struct lquota_glb_rec, qbr_granted) == 24, "found %lld\n",
+ (long long)(int)offsetof(struct lquota_glb_rec, qbr_granted));
+ LASSERTF((int)sizeof(((struct lquota_glb_rec *)0)->qbr_granted) == 8, "found %lld\n",
+ (long long)(int)sizeof(((struct lquota_glb_rec *)0)->qbr_granted));
+
+ /* Checks for struct lquota_slv_rec */
+ LASSERTF((int)sizeof(struct lquota_slv_rec) == 8, "found %lld\n",
+ (long long)(int)sizeof(struct lquota_slv_rec));
+ LASSERTF((int)offsetof(struct lquota_slv_rec, qsr_granted) == 0, "found %lld\n",
+ (long long)(int)offsetof(struct lquota_slv_rec, qsr_granted));
+ LASSERTF((int)sizeof(((struct lquota_slv_rec *)0)->qsr_granted) == 8, "found %lld\n",
+ (long long)(int)sizeof(((struct lquota_slv_rec *)0)->qsr_granted));
+
+ /* Checks for struct idx_info */
+ LASSERTF((int)sizeof(struct idx_info) == 80, "found %lld\n",
+ (long long)(int)sizeof(struct idx_info));
+ LASSERTF((int)offsetof(struct idx_info, ii_magic) == 0, "found %lld\n",
+ (long long)(int)offsetof(struct idx_info, ii_magic));
+ LASSERTF((int)sizeof(((struct idx_info *)0)->ii_magic) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct idx_info *)0)->ii_magic));
+ LASSERTF((int)offsetof(struct idx_info, ii_flags) == 4, "found %lld\n",
+ (long long)(int)offsetof(struct idx_info, ii_flags));
+ LASSERTF((int)sizeof(((struct idx_info *)0)->ii_flags) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct idx_info *)0)->ii_flags));
+ LASSERTF((int)offsetof(struct idx_info, ii_count) == 8, "found %lld\n",
+ (long long)(int)offsetof(struct idx_info, ii_count));
+ LASSERTF((int)sizeof(((struct idx_info *)0)->ii_count) == 2, "found %lld\n",
+ (long long)(int)sizeof(((struct idx_info *)0)->ii_count));
+ LASSERTF((int)offsetof(struct idx_info, ii_pad0) == 10, "found %lld\n",
+ (long long)(int)offsetof(struct idx_info, ii_pad0));
+ LASSERTF((int)sizeof(((struct idx_info *)0)->ii_pad0) == 2, "found %lld\n",
+ (long long)(int)sizeof(((struct idx_info *)0)->ii_pad0));
+ LASSERTF((int)offsetof(struct idx_info, ii_attrs) == 12, "found %lld\n",
+ (long long)(int)offsetof(struct idx_info, ii_attrs));
+ LASSERTF((int)sizeof(((struct idx_info *)0)->ii_attrs) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct idx_info *)0)->ii_attrs));
+ LASSERTF((int)offsetof(struct idx_info, ii_fid) == 16, "found %lld\n",
+ (long long)(int)offsetof(struct idx_info, ii_fid));
+ LASSERTF((int)sizeof(((struct idx_info *)0)->ii_fid) == 16, "found %lld\n",
+ (long long)(int)sizeof(((struct idx_info *)0)->ii_fid));
+ LASSERTF((int)offsetof(struct idx_info, ii_version) == 32, "found %lld\n",
+ (long long)(int)offsetof(struct idx_info, ii_version));
+ LASSERTF((int)sizeof(((struct idx_info *)0)->ii_version) == 8, "found %lld\n",
+ (long long)(int)sizeof(((struct idx_info *)0)->ii_version));
+ LASSERTF((int)offsetof(struct idx_info, ii_hash_start) == 40, "found %lld\n",
+ (long long)(int)offsetof(struct idx_info, ii_hash_start));
+ LASSERTF((int)sizeof(((struct idx_info *)0)->ii_hash_start) == 8, "found %lld\n",
+ (long long)(int)sizeof(((struct idx_info *)0)->ii_hash_start));
+ LASSERTF((int)offsetof(struct idx_info, ii_hash_end) == 48, "found %lld\n",
+ (long long)(int)offsetof(struct idx_info, ii_hash_end));
+ LASSERTF((int)sizeof(((struct idx_info *)0)->ii_hash_end) == 8, "found %lld\n",
+ (long long)(int)sizeof(((struct idx_info *)0)->ii_hash_end));
+ LASSERTF((int)offsetof(struct idx_info, ii_keysize) == 56, "found %lld\n",
+ (long long)(int)offsetof(struct idx_info, ii_keysize));
+ LASSERTF((int)sizeof(((struct idx_info *)0)->ii_keysize) == 2, "found %lld\n",
+ (long long)(int)sizeof(((struct idx_info *)0)->ii_keysize));
+ LASSERTF((int)offsetof(struct idx_info, ii_recsize) == 58, "found %lld\n",
+ (long long)(int)offsetof(struct idx_info, ii_recsize));
+ LASSERTF((int)sizeof(((struct idx_info *)0)->ii_recsize) == 2, "found %lld\n",
+ (long long)(int)sizeof(((struct idx_info *)0)->ii_recsize));
+ LASSERTF((int)offsetof(struct idx_info, ii_pad1) == 60, "found %lld\n",
+ (long long)(int)offsetof(struct idx_info, ii_pad1));
+ LASSERTF((int)sizeof(((struct idx_info *)0)->ii_pad1) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct idx_info *)0)->ii_pad1));
+ LASSERTF((int)offsetof(struct idx_info, ii_pad2) == 64, "found %lld\n",
+ (long long)(int)offsetof(struct idx_info, ii_pad2));
+ LASSERTF((int)sizeof(((struct idx_info *)0)->ii_pad2) == 8, "found %lld\n",
+ (long long)(int)sizeof(((struct idx_info *)0)->ii_pad2));
+ LASSERTF((int)offsetof(struct idx_info, ii_pad3) == 72, "found %lld\n",
+ (long long)(int)offsetof(struct idx_info, ii_pad3));
+ LASSERTF((int)sizeof(((struct idx_info *)0)->ii_pad3) == 8, "found %lld\n",
+ (long long)(int)sizeof(((struct idx_info *)0)->ii_pad3));
+ CLASSERT(IDX_INFO_MAGIC == 0x3D37CC37);
+
+ /* Checks for struct lu_idxpage */
+ LASSERTF((int)sizeof(struct lu_idxpage) == 16, "found %lld\n",
+ (long long)(int)sizeof(struct lu_idxpage));
+ LASSERTF((int)offsetof(struct lu_idxpage, lip_magic) == 0, "found %lld\n",
+ (long long)(int)offsetof(struct lu_idxpage, lip_magic));
+ LASSERTF((int)sizeof(((struct lu_idxpage *)0)->lip_magic) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct lu_idxpage *)0)->lip_magic));
+ LASSERTF((int)offsetof(struct lu_idxpage, lip_flags) == 4, "found %lld\n",
+ (long long)(int)offsetof(struct lu_idxpage, lip_flags));
+ LASSERTF((int)sizeof(((struct lu_idxpage *)0)->lip_flags) == 2, "found %lld\n",
+ (long long)(int)sizeof(((struct lu_idxpage *)0)->lip_flags));
+ LASSERTF((int)offsetof(struct lu_idxpage, lip_nr) == 6, "found %lld\n",
+ (long long)(int)offsetof(struct lu_idxpage, lip_nr));
+ LASSERTF((int)sizeof(((struct lu_idxpage *)0)->lip_nr) == 2, "found %lld\n",
+ (long long)(int)sizeof(((struct lu_idxpage *)0)->lip_nr));
+ LASSERTF((int)offsetof(struct lu_idxpage, lip_pad0) == 8, "found %lld\n",
+ (long long)(int)offsetof(struct lu_idxpage, lip_pad0));
+ LASSERTF((int)sizeof(((struct lu_idxpage *)0)->lip_pad0) == 8, "found %lld\n",
+ (long long)(int)sizeof(((struct lu_idxpage *)0)->lip_pad0));
+ CLASSERT(LIP_MAGIC == 0x8A6D6B6C);
+ LASSERTF(LIP_HDR_SIZE == 16, "found %lld\n",
+ (long long)LIP_HDR_SIZE);
+ LASSERTF(II_FL_NOHASH == 1, "found %lld\n",
+ (long long)II_FL_NOHASH);
+ LASSERTF(II_FL_VARKEY == 2, "found %lld\n",
+ (long long)II_FL_VARKEY);
+ LASSERTF(II_FL_VARREC == 4, "found %lld\n",
+ (long long)II_FL_VARREC);
+ LASSERTF(II_FL_NONUNQ == 8, "found %lld\n",
+ (long long)II_FL_NONUNQ);
+
+ /* Checks for struct niobuf_remote */
+ LASSERTF((int)sizeof(struct niobuf_remote) == 16, "found %lld\n",
+ (long long)(int)sizeof(struct niobuf_remote));
+ LASSERTF((int)offsetof(struct niobuf_remote, offset) == 0, "found %lld\n",
+ (long long)(int)offsetof(struct niobuf_remote, offset));
+ LASSERTF((int)sizeof(((struct niobuf_remote *)0)->offset) == 8, "found %lld\n",
+ (long long)(int)sizeof(((struct niobuf_remote *)0)->offset));
+ LASSERTF((int)offsetof(struct niobuf_remote, len) == 8, "found %lld\n",
+ (long long)(int)offsetof(struct niobuf_remote, len));
+ LASSERTF((int)sizeof(((struct niobuf_remote *)0)->len) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct niobuf_remote *)0)->len));
+ LASSERTF((int)offsetof(struct niobuf_remote, flags) == 12, "found %lld\n",
+ (long long)(int)offsetof(struct niobuf_remote, flags));
+ LASSERTF((int)sizeof(((struct niobuf_remote *)0)->flags) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct niobuf_remote *)0)->flags));
+ LASSERTF(OBD_BRW_READ == 0x01, "found 0x%.8x\n",
+ OBD_BRW_READ);
+ LASSERTF(OBD_BRW_WRITE == 0x02, "found 0x%.8x\n",
+ OBD_BRW_WRITE);
+ LASSERTF(OBD_BRW_SYNC == 0x08, "found 0x%.8x\n",
+ OBD_BRW_SYNC);
+ LASSERTF(OBD_BRW_CHECK == 0x10, "found 0x%.8x\n",
+ OBD_BRW_CHECK);
+ LASSERTF(OBD_BRW_FROM_GRANT == 0x20, "found 0x%.8x\n",
+ OBD_BRW_FROM_GRANT);
+ LASSERTF(OBD_BRW_GRANTED == 0x40, "found 0x%.8x\n",
+ OBD_BRW_GRANTED);
+ LASSERTF(OBD_BRW_NOCACHE == 0x80, "found 0x%.8x\n",
+ OBD_BRW_NOCACHE);
+ LASSERTF(OBD_BRW_NOQUOTA == 0x100, "found 0x%.8x\n",
+ OBD_BRW_NOQUOTA);
+ LASSERTF(OBD_BRW_SRVLOCK == 0x200, "found 0x%.8x\n",
+ OBD_BRW_SRVLOCK);
+ LASSERTF(OBD_BRW_ASYNC == 0x400, "found 0x%.8x\n",
+ OBD_BRW_ASYNC);
+ LASSERTF(OBD_BRW_MEMALLOC == 0x800, "found 0x%.8x\n",
+ OBD_BRW_MEMALLOC);
+
+ /* Checks for struct ost_body */
+ LASSERTF((int)sizeof(struct ost_body) == 208, "found %lld\n",
+ (long long)(int)sizeof(struct ost_body));
+ LASSERTF((int)offsetof(struct ost_body, oa) == 0, "found %lld\n",
+ (long long)(int)offsetof(struct ost_body, oa));
+ LASSERTF((int)sizeof(((struct ost_body *)0)->oa) == 208, "found %lld\n",
+ (long long)(int)sizeof(((struct ost_body *)0)->oa));
+
+ /* Checks for struct ll_fid */
+ LASSERTF((int)sizeof(struct ll_fid) == 16, "found %lld\n",
+ (long long)(int)sizeof(struct ll_fid));
+ LASSERTF((int)offsetof(struct ll_fid, id) == 0, "found %lld\n",
+ (long long)(int)offsetof(struct ll_fid, id));
+ LASSERTF((int)sizeof(((struct ll_fid *)0)->id) == 8, "found %lld\n",
+ (long long)(int)sizeof(((struct ll_fid *)0)->id));
+ LASSERTF((int)offsetof(struct ll_fid, generation) == 8, "found %lld\n",
+ (long long)(int)offsetof(struct ll_fid, generation));
+ LASSERTF((int)sizeof(((struct ll_fid *)0)->generation) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct ll_fid *)0)->generation));
+ LASSERTF((int)offsetof(struct ll_fid, f_type) == 12, "found %lld\n",
+ (long long)(int)offsetof(struct ll_fid, f_type));
+ LASSERTF((int)sizeof(((struct ll_fid *)0)->f_type) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct ll_fid *)0)->f_type));
+
+ /* Checks for struct mdt_body */
+ LASSERTF((int)sizeof(struct mdt_body) == 216, "found %lld\n",
+ (long long)(int)sizeof(struct mdt_body));
+ LASSERTF((int)offsetof(struct mdt_body, fid1) == 0, "found %lld\n",
+ (long long)(int)offsetof(struct mdt_body, fid1));
+ LASSERTF((int)sizeof(((struct mdt_body *)0)->fid1) == 16, "found %lld\n",
+ (long long)(int)sizeof(((struct mdt_body *)0)->fid1));
+ LASSERTF((int)offsetof(struct mdt_body, fid2) == 16, "found %lld\n",
+ (long long)(int)offsetof(struct mdt_body, fid2));
+ LASSERTF((int)sizeof(((struct mdt_body *)0)->fid2) == 16, "found %lld\n",
+ (long long)(int)sizeof(((struct mdt_body *)0)->fid2));
+ LASSERTF((int)offsetof(struct mdt_body, handle) == 32, "found %lld\n",
+ (long long)(int)offsetof(struct mdt_body, handle));
+ LASSERTF((int)sizeof(((struct mdt_body *)0)->handle) == 8, "found %lld\n",
+ (long long)(int)sizeof(((struct mdt_body *)0)->handle));
+ LASSERTF((int)offsetof(struct mdt_body, valid) == 40, "found %lld\n",
+ (long long)(int)offsetof(struct mdt_body, valid));
+ LASSERTF((int)sizeof(((struct mdt_body *)0)->valid) == 8, "found %lld\n",
+ (long long)(int)sizeof(((struct mdt_body *)0)->valid));
+ LASSERTF((int)offsetof(struct mdt_body, size) == 48, "found %lld\n",
+ (long long)(int)offsetof(struct mdt_body, size));
+ LASSERTF((int)sizeof(((struct mdt_body *)0)->size) == 8, "found %lld\n",
+ (long long)(int)sizeof(((struct mdt_body *)0)->size));
+ LASSERTF((int)offsetof(struct mdt_body, mtime) == 56, "found %lld\n",
+ (long long)(int)offsetof(struct mdt_body, mtime));
+ LASSERTF((int)sizeof(((struct mdt_body *)0)->mtime) == 8, "found %lld\n",
+ (long long)(int)sizeof(((struct mdt_body *)0)->mtime));
+ LASSERTF((int)offsetof(struct mdt_body, atime) == 64, "found %lld\n",
+ (long long)(int)offsetof(struct mdt_body, atime));
+ LASSERTF((int)sizeof(((struct mdt_body *)0)->atime) == 8, "found %lld\n",
+ (long long)(int)sizeof(((struct mdt_body *)0)->atime));
+ LASSERTF((int)offsetof(struct mdt_body, ctime) == 72, "found %lld\n",
+ (long long)(int)offsetof(struct mdt_body, ctime));
+ LASSERTF((int)sizeof(((struct mdt_body *)0)->ctime) == 8, "found %lld\n",
+ (long long)(int)sizeof(((struct mdt_body *)0)->ctime));
+ LASSERTF((int)offsetof(struct mdt_body, blocks) == 80, "found %lld\n",
+ (long long)(int)offsetof(struct mdt_body, blocks));
+ LASSERTF((int)sizeof(((struct mdt_body *)0)->blocks) == 8, "found %lld\n",
+ (long long)(int)sizeof(((struct mdt_body *)0)->blocks));
+ LASSERTF((int)offsetof(struct mdt_body, t_state) == 96, "found %lld\n",
+ (long long)(int)offsetof(struct mdt_body, t_state));
+ LASSERTF((int)sizeof(((struct mdt_body *)0)->t_state) == 8,
+ "found %lld\n",
+ (long long)(int)sizeof(((struct mdt_body *)0)->t_state));
+ LASSERTF((int)offsetof(struct mdt_body, fsuid) == 104, "found %lld\n",
+ (long long)(int)offsetof(struct mdt_body, fsuid));
+ LASSERTF((int)sizeof(((struct mdt_body *)0)->fsuid) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct mdt_body *)0)->fsuid));
+ LASSERTF((int)offsetof(struct mdt_body, fsgid) == 108, "found %lld\n",
+ (long long)(int)offsetof(struct mdt_body, fsgid));
+ LASSERTF((int)sizeof(((struct mdt_body *)0)->fsgid) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct mdt_body *)0)->fsgid));
+ LASSERTF((int)offsetof(struct mdt_body, capability) == 112, "found %lld\n",
+ (long long)(int)offsetof(struct mdt_body, capability));
+ LASSERTF((int)sizeof(((struct mdt_body *)0)->capability) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct mdt_body *)0)->capability));
+ LASSERTF((int)offsetof(struct mdt_body, mode) == 116, "found %lld\n",
+ (long long)(int)offsetof(struct mdt_body, mode));
+ LASSERTF((int)sizeof(((struct mdt_body *)0)->mode) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct mdt_body *)0)->mode));
+ LASSERTF((int)offsetof(struct mdt_body, uid) == 120, "found %lld\n",
+ (long long)(int)offsetof(struct mdt_body, uid));
+ LASSERTF((int)sizeof(((struct mdt_body *)0)->uid) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct mdt_body *)0)->uid));
+ LASSERTF((int)offsetof(struct mdt_body, gid) == 124, "found %lld\n",
+ (long long)(int)offsetof(struct mdt_body, gid));
+ LASSERTF((int)sizeof(((struct mdt_body *)0)->gid) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct mdt_body *)0)->gid));
+ LASSERTF((int)offsetof(struct mdt_body, flags) == 128, "found %lld\n",
+ (long long)(int)offsetof(struct mdt_body, flags));
+ LASSERTF((int)sizeof(((struct mdt_body *)0)->flags) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct mdt_body *)0)->flags));
+ LASSERTF((int)offsetof(struct mdt_body, rdev) == 132, "found %lld\n",
+ (long long)(int)offsetof(struct mdt_body, rdev));
+ LASSERTF((int)sizeof(((struct mdt_body *)0)->rdev) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct mdt_body *)0)->rdev));
+ LASSERTF((int)offsetof(struct mdt_body, nlink) == 136, "found %lld\n",
+ (long long)(int)offsetof(struct mdt_body, nlink));
+ LASSERTF((int)sizeof(((struct mdt_body *)0)->nlink) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct mdt_body *)0)->nlink));
+ LASSERTF((int)offsetof(struct mdt_body, unused2) == 140, "found %lld\n",
+ (long long)(int)offsetof(struct mdt_body, unused2));
+ LASSERTF((int)sizeof(((struct mdt_body *)0)->unused2) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct mdt_body *)0)->unused2));
+ LASSERTF((int)offsetof(struct mdt_body, suppgid) == 144, "found %lld\n",
+ (long long)(int)offsetof(struct mdt_body, suppgid));
+ LASSERTF((int)sizeof(((struct mdt_body *)0)->suppgid) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct mdt_body *)0)->suppgid));
+ LASSERTF((int)offsetof(struct mdt_body, eadatasize) == 148, "found %lld\n",
+ (long long)(int)offsetof(struct mdt_body, eadatasize));
+ LASSERTF((int)sizeof(((struct mdt_body *)0)->eadatasize) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct mdt_body *)0)->eadatasize));
+ LASSERTF((int)offsetof(struct mdt_body, aclsize) == 152, "found %lld\n",
+ (long long)(int)offsetof(struct mdt_body, aclsize));
+ LASSERTF((int)sizeof(((struct mdt_body *)0)->aclsize) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct mdt_body *)0)->aclsize));
+ LASSERTF((int)offsetof(struct mdt_body, max_mdsize) == 156, "found %lld\n",
+ (long long)(int)offsetof(struct mdt_body, max_mdsize));
+ LASSERTF((int)sizeof(((struct mdt_body *)0)->max_mdsize) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct mdt_body *)0)->max_mdsize));
+ LASSERTF((int)offsetof(struct mdt_body, max_cookiesize) == 160, "found %lld\n",
+ (long long)(int)offsetof(struct mdt_body, max_cookiesize));
+ LASSERTF((int)sizeof(((struct mdt_body *)0)->max_cookiesize) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct mdt_body *)0)->max_cookiesize));
+ LASSERTF((int)offsetof(struct mdt_body, uid_h) == 164, "found %lld\n",
+ (long long)(int)offsetof(struct mdt_body, uid_h));
+ LASSERTF((int)sizeof(((struct mdt_body *)0)->uid_h) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct mdt_body *)0)->uid_h));
+ LASSERTF((int)offsetof(struct mdt_body, gid_h) == 168, "found %lld\n",
+ (long long)(int)offsetof(struct mdt_body, gid_h));
+ LASSERTF((int)sizeof(((struct mdt_body *)0)->gid_h) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct mdt_body *)0)->gid_h));
+ LASSERTF((int)offsetof(struct mdt_body, padding_5) == 172, "found %lld\n",
+ (long long)(int)offsetof(struct mdt_body, padding_5));
+ LASSERTF((int)sizeof(((struct mdt_body *)0)->padding_5) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct mdt_body *)0)->padding_5));
+ LASSERTF((int)offsetof(struct mdt_body, padding_6) == 176, "found %lld\n",
+ (long long)(int)offsetof(struct mdt_body, padding_6));
+ LASSERTF((int)sizeof(((struct mdt_body *)0)->padding_6) == 8, "found %lld\n",
+ (long long)(int)sizeof(((struct mdt_body *)0)->padding_6));
+ LASSERTF((int)offsetof(struct mdt_body, padding_7) == 184, "found %lld\n",
+ (long long)(int)offsetof(struct mdt_body, padding_7));
+ LASSERTF((int)sizeof(((struct mdt_body *)0)->padding_7) == 8, "found %lld\n",
+ (long long)(int)sizeof(((struct mdt_body *)0)->padding_7));
+ LASSERTF((int)offsetof(struct mdt_body, padding_8) == 192, "found %lld\n",
+ (long long)(int)offsetof(struct mdt_body, padding_8));
+ LASSERTF((int)sizeof(((struct mdt_body *)0)->padding_8) == 8, "found %lld\n",
+ (long long)(int)sizeof(((struct mdt_body *)0)->padding_8));
+ LASSERTF((int)offsetof(struct mdt_body, padding_9) == 200, "found %lld\n",
+ (long long)(int)offsetof(struct mdt_body, padding_9));
+ LASSERTF((int)sizeof(((struct mdt_body *)0)->padding_9) == 8, "found %lld\n",
+ (long long)(int)sizeof(((struct mdt_body *)0)->padding_9));
+ LASSERTF((int)offsetof(struct mdt_body, padding_10) == 208, "found %lld\n",
+ (long long)(int)offsetof(struct mdt_body, padding_10));
+ LASSERTF((int)sizeof(((struct mdt_body *)0)->padding_10) == 8, "found %lld\n",
+ (long long)(int)sizeof(((struct mdt_body *)0)->padding_10));
+ LASSERTF(MDS_FMODE_CLOSED == 000000000000UL, "found 0%.11oUL\n",
+ MDS_FMODE_CLOSED);
+ LASSERTF(MDS_FMODE_EXEC == 000000000004UL, "found 0%.11oUL\n",
+ MDS_FMODE_EXEC);
+ LASSERTF(MDS_FMODE_EPOCH == 000001000000UL, "found 0%.11oUL\n",
+ MDS_FMODE_EPOCH);
+ LASSERTF(MDS_FMODE_TRUNC == 000002000000UL, "found 0%.11oUL\n",
+ MDS_FMODE_TRUNC);
+ LASSERTF(MDS_FMODE_SOM == 000004000000UL, "found 0%.11oUL\n",
+ MDS_FMODE_SOM);
+ LASSERTF(MDS_OPEN_CREATED == 000000000010UL, "found 0%.11oUL\n",
+ MDS_OPEN_CREATED);
+ LASSERTF(MDS_OPEN_CROSS == 000000000020UL, "found 0%.11oUL\n",
+ MDS_OPEN_CROSS);
+ LASSERTF(MDS_OPEN_CREAT == 000000000100UL, "found 0%.11oUL\n",
+ MDS_OPEN_CREAT);
+ LASSERTF(MDS_OPEN_EXCL == 000000000200UL, "found 0%.11oUL\n",
+ MDS_OPEN_EXCL);
+ LASSERTF(MDS_OPEN_TRUNC == 000000001000UL, "found 0%.11oUL\n",
+ MDS_OPEN_TRUNC);
+ LASSERTF(MDS_OPEN_APPEND == 000000002000UL, "found 0%.11oUL\n",
+ MDS_OPEN_APPEND);
+ LASSERTF(MDS_OPEN_SYNC == 000000010000UL, "found 0%.11oUL\n",
+ MDS_OPEN_SYNC);
+ LASSERTF(MDS_OPEN_DIRECTORY == 000000200000UL, "found 0%.11oUL\n",
+ MDS_OPEN_DIRECTORY);
+ LASSERTF(MDS_OPEN_BY_FID == 000040000000UL, "found 0%.11oUL\n",
+ MDS_OPEN_BY_FID);
+ LASSERTF(MDS_OPEN_DELAY_CREATE == 000100000000UL, "found 0%.11oUL\n",
+ MDS_OPEN_DELAY_CREATE);
+ LASSERTF(MDS_OPEN_OWNEROVERRIDE == 000200000000UL, "found 0%.11oUL\n",
+ MDS_OPEN_OWNEROVERRIDE);
+ LASSERTF(MDS_OPEN_JOIN_FILE == 000400000000UL, "found 0%.11oUL\n",
+ MDS_OPEN_JOIN_FILE);
+ LASSERTF(MDS_OPEN_LOCK == 004000000000UL, "found 0%.11oUL\n",
+ MDS_OPEN_LOCK);
+ LASSERTF(MDS_OPEN_HAS_EA == 010000000000UL, "found 0%.11oUL\n",
+ MDS_OPEN_HAS_EA);
+ LASSERTF(MDS_OPEN_HAS_OBJS == 020000000000UL, "found 0%.11oUL\n",
+ MDS_OPEN_HAS_OBJS);
+ LASSERTF(MDS_OPEN_NORESTORE == 00000000000100000000000ULL, "found 0%.22lloULL\n",
+ (long long)MDS_OPEN_NORESTORE);
+ LASSERTF(MDS_OPEN_NEWSTRIPE == 00000000000200000000000ULL, "found 0%.22lloULL\n",
+ (long long)MDS_OPEN_NEWSTRIPE);
+ LASSERTF(MDS_OPEN_VOLATILE == 00000000000400000000000ULL, "found 0%.22lloULL\n",
+ (long long)MDS_OPEN_VOLATILE);
+ LASSERTF(LUSTRE_SYNC_FL == 0x00000008, "found 0x%.8x\n",
+ LUSTRE_SYNC_FL);
+ LASSERTF(LUSTRE_IMMUTABLE_FL == 0x00000010, "found 0x%.8x\n",
+ LUSTRE_IMMUTABLE_FL);
+ LASSERTF(LUSTRE_APPEND_FL == 0x00000020, "found 0x%.8x\n",
+ LUSTRE_APPEND_FL);
+ LASSERTF(LUSTRE_NOATIME_FL == 0x00000080, "found 0x%.8x\n",
+ LUSTRE_NOATIME_FL);
+ LASSERTF(LUSTRE_DIRSYNC_FL == 0x00010000, "found 0x%.8x\n",
+ LUSTRE_DIRSYNC_FL);
+ LASSERTF(MDS_INODELOCK_LOOKUP == 0x000001, "found 0x%.8x\n",
+ MDS_INODELOCK_LOOKUP);
+ LASSERTF(MDS_INODELOCK_UPDATE == 0x000002, "found 0x%.8x\n",
+ MDS_INODELOCK_UPDATE);
+ LASSERTF(MDS_INODELOCK_OPEN == 0x000004, "found 0x%.8x\n",
+ MDS_INODELOCK_OPEN);
+ LASSERTF(MDS_INODELOCK_LAYOUT == 0x000008, "found 0x%.8x\n",
+ MDS_INODELOCK_LAYOUT);
+
+ /* Checks for struct mdt_ioepoch */
+ LASSERTF((int)sizeof(struct mdt_ioepoch) == 24, "found %lld\n",
+ (long long)(int)sizeof(struct mdt_ioepoch));
+ LASSERTF((int)offsetof(struct mdt_ioepoch, handle) == 0, "found %lld\n",
+ (long long)(int)offsetof(struct mdt_ioepoch, handle));
+ LASSERTF((int)sizeof(((struct mdt_ioepoch *)0)->handle) == 8, "found %lld\n",
+ (long long)(int)sizeof(((struct mdt_ioepoch *)0)->handle));
+ LASSERTF((int)offsetof(struct mdt_ioepoch, ioepoch) == 8, "found %lld\n",
+ (long long)(int)offsetof(struct mdt_ioepoch, ioepoch));
+ LASSERTF((int)sizeof(((struct mdt_ioepoch *)0)->ioepoch) == 8, "found %lld\n",
+ (long long)(int)sizeof(((struct mdt_ioepoch *)0)->ioepoch));
+ LASSERTF((int)offsetof(struct mdt_ioepoch, flags) == 16, "found %lld\n",
+ (long long)(int)offsetof(struct mdt_ioepoch, flags));
+ LASSERTF((int)sizeof(((struct mdt_ioepoch *)0)->flags) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct mdt_ioepoch *)0)->flags));
+ LASSERTF((int)offsetof(struct mdt_ioepoch, padding) == 20, "found %lld\n",
+ (long long)(int)offsetof(struct mdt_ioepoch, padding));
+ LASSERTF((int)sizeof(((struct mdt_ioepoch *)0)->padding) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct mdt_ioepoch *)0)->padding));
+
+ /* Checks for struct mdt_remote_perm */
+ LASSERTF((int)sizeof(struct mdt_remote_perm) == 32, "found %lld\n",
+ (long long)(int)sizeof(struct mdt_remote_perm));
+ LASSERTF((int)offsetof(struct mdt_remote_perm, rp_uid) == 0, "found %lld\n",
+ (long long)(int)offsetof(struct mdt_remote_perm, rp_uid));
+ LASSERTF((int)sizeof(((struct mdt_remote_perm *)0)->rp_uid) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct mdt_remote_perm *)0)->rp_uid));
+ LASSERTF((int)offsetof(struct mdt_remote_perm, rp_gid) == 4, "found %lld\n",
+ (long long)(int)offsetof(struct mdt_remote_perm, rp_gid));
+ LASSERTF((int)sizeof(((struct mdt_remote_perm *)0)->rp_gid) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct mdt_remote_perm *)0)->rp_gid));
+ LASSERTF((int)offsetof(struct mdt_remote_perm, rp_fsuid) == 8, "found %lld\n",
+ (long long)(int)offsetof(struct mdt_remote_perm, rp_fsuid));
+ LASSERTF((int)sizeof(((struct mdt_remote_perm *)0)->rp_fsuid) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct mdt_remote_perm *)0)->rp_fsuid));
+ LASSERTF((int)offsetof(struct mdt_remote_perm, rp_fsgid) == 16, "found %lld\n",
+ (long long)(int)offsetof(struct mdt_remote_perm, rp_fsgid));
+ LASSERTF((int)sizeof(((struct mdt_remote_perm *)0)->rp_fsgid) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct mdt_remote_perm *)0)->rp_fsgid));
+ LASSERTF((int)offsetof(struct mdt_remote_perm, rp_access_perm) == 24, "found %lld\n",
+ (long long)(int)offsetof(struct mdt_remote_perm, rp_access_perm));
+ LASSERTF((int)sizeof(((struct mdt_remote_perm *)0)->rp_access_perm) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct mdt_remote_perm *)0)->rp_access_perm));
+ LASSERTF((int)offsetof(struct mdt_remote_perm, rp_padding) == 28, "found %lld\n",
+ (long long)(int)offsetof(struct mdt_remote_perm, rp_padding));
+ LASSERTF((int)sizeof(((struct mdt_remote_perm *)0)->rp_padding) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct mdt_remote_perm *)0)->rp_padding));
+ LASSERTF(CFS_SETUID_PERM == 0x00000001UL, "found 0x%.8xUL\n",
+ (unsigned)CFS_SETUID_PERM);
+ LASSERTF(CFS_SETGID_PERM == 0x00000002UL, "found 0x%.8xUL\n",
+ (unsigned)CFS_SETGID_PERM);
+ LASSERTF(CFS_SETGRP_PERM == 0x00000004UL, "found 0x%.8xUL\n",
+ (unsigned)CFS_SETGRP_PERM);
+ LASSERTF(CFS_RMTACL_PERM == 0x00000008UL, "found 0x%.8xUL\n",
+ (unsigned)CFS_RMTACL_PERM);
+ LASSERTF(CFS_RMTOWN_PERM == 0x00000010UL, "found 0x%.8xUL\n",
+ (unsigned)CFS_RMTOWN_PERM);
+
+ /* Checks for struct mdt_rec_setattr */
+ LASSERTF((int)sizeof(struct mdt_rec_setattr) == 136, "found %lld\n",
+ (long long)(int)sizeof(struct mdt_rec_setattr));
+ LASSERTF((int)offsetof(struct mdt_rec_setattr, sa_opcode) == 0, "found %lld\n",
+ (long long)(int)offsetof(struct mdt_rec_setattr, sa_opcode));
+ LASSERTF((int)sizeof(((struct mdt_rec_setattr *)0)->sa_opcode) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct mdt_rec_setattr *)0)->sa_opcode));
+ LASSERTF((int)offsetof(struct mdt_rec_setattr, sa_cap) == 4, "found %lld\n",
+ (long long)(int)offsetof(struct mdt_rec_setattr, sa_cap));
+ LASSERTF((int)sizeof(((struct mdt_rec_setattr *)0)->sa_cap) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct mdt_rec_setattr *)0)->sa_cap));
+ LASSERTF((int)offsetof(struct mdt_rec_setattr, sa_fsuid) == 8, "found %lld\n",
+ (long long)(int)offsetof(struct mdt_rec_setattr, sa_fsuid));
+ LASSERTF((int)sizeof(((struct mdt_rec_setattr *)0)->sa_fsuid) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct mdt_rec_setattr *)0)->sa_fsuid));
+ LASSERTF((int)offsetof(struct mdt_rec_setattr, sa_fsuid_h) == 12, "found %lld\n",
+ (long long)(int)offsetof(struct mdt_rec_setattr, sa_fsuid_h));
+ LASSERTF((int)sizeof(((struct mdt_rec_setattr *)0)->sa_fsuid_h) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct mdt_rec_setattr *)0)->sa_fsuid_h));
+ LASSERTF((int)offsetof(struct mdt_rec_setattr, sa_fsgid) == 16, "found %lld\n",
+ (long long)(int)offsetof(struct mdt_rec_setattr, sa_fsgid));
+ LASSERTF((int)sizeof(((struct mdt_rec_setattr *)0)->sa_fsgid) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct mdt_rec_setattr *)0)->sa_fsgid));
+ LASSERTF((int)offsetof(struct mdt_rec_setattr, sa_fsgid_h) == 20, "found %lld\n",
+ (long long)(int)offsetof(struct mdt_rec_setattr, sa_fsgid_h));
+ LASSERTF((int)sizeof(((struct mdt_rec_setattr *)0)->sa_fsgid_h) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct mdt_rec_setattr *)0)->sa_fsgid_h));
+ LASSERTF((int)offsetof(struct mdt_rec_setattr, sa_suppgid) == 24, "found %lld\n",
+ (long long)(int)offsetof(struct mdt_rec_setattr, sa_suppgid));
+ LASSERTF((int)sizeof(((struct mdt_rec_setattr *)0)->sa_suppgid) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct mdt_rec_setattr *)0)->sa_suppgid));
+ LASSERTF((int)offsetof(struct mdt_rec_setattr, sa_suppgid_h) == 28, "found %lld\n",
+ (long long)(int)offsetof(struct mdt_rec_setattr, sa_suppgid_h));
+ LASSERTF((int)sizeof(((struct mdt_rec_setattr *)0)->sa_suppgid_h) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct mdt_rec_setattr *)0)->sa_suppgid_h));
+ LASSERTF((int)offsetof(struct mdt_rec_setattr, sa_padding_1) == 32, "found %lld\n",
+ (long long)(int)offsetof(struct mdt_rec_setattr, sa_padding_1));
+ LASSERTF((int)sizeof(((struct mdt_rec_setattr *)0)->sa_padding_1) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct mdt_rec_setattr *)0)->sa_padding_1));
+ LASSERTF((int)offsetof(struct mdt_rec_setattr, sa_padding_1_h) == 36, "found %lld\n",
+ (long long)(int)offsetof(struct mdt_rec_setattr, sa_padding_1_h));
+ LASSERTF((int)sizeof(((struct mdt_rec_setattr *)0)->sa_padding_1_h) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct mdt_rec_setattr *)0)->sa_padding_1_h));
+ LASSERTF((int)offsetof(struct mdt_rec_setattr, sa_fid) == 40, "found %lld\n",
+ (long long)(int)offsetof(struct mdt_rec_setattr, sa_fid));
+ LASSERTF((int)sizeof(((struct mdt_rec_setattr *)0)->sa_fid) == 16, "found %lld\n",
+ (long long)(int)sizeof(((struct mdt_rec_setattr *)0)->sa_fid));
+ LASSERTF((int)offsetof(struct mdt_rec_setattr, sa_valid) == 56, "found %lld\n",
+ (long long)(int)offsetof(struct mdt_rec_setattr, sa_valid));
+ LASSERTF((int)sizeof(((struct mdt_rec_setattr *)0)->sa_valid) == 8, "found %lld\n",
+ (long long)(int)sizeof(((struct mdt_rec_setattr *)0)->sa_valid));
+ LASSERTF((int)offsetof(struct mdt_rec_setattr, sa_uid) == 64, "found %lld\n",
+ (long long)(int)offsetof(struct mdt_rec_setattr, sa_uid));
+ LASSERTF((int)sizeof(((struct mdt_rec_setattr *)0)->sa_uid) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct mdt_rec_setattr *)0)->sa_uid));
+ LASSERTF((int)offsetof(struct mdt_rec_setattr, sa_gid) == 68, "found %lld\n",
+ (long long)(int)offsetof(struct mdt_rec_setattr, sa_gid));
+ LASSERTF((int)sizeof(((struct mdt_rec_setattr *)0)->sa_gid) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct mdt_rec_setattr *)0)->sa_gid));
+ LASSERTF((int)offsetof(struct mdt_rec_setattr, sa_size) == 72, "found %lld\n",
+ (long long)(int)offsetof(struct mdt_rec_setattr, sa_size));
+ LASSERTF((int)sizeof(((struct mdt_rec_setattr *)0)->sa_size) == 8, "found %lld\n",
+ (long long)(int)sizeof(((struct mdt_rec_setattr *)0)->sa_size));
+ LASSERTF((int)offsetof(struct mdt_rec_setattr, sa_blocks) == 80, "found %lld\n",
+ (long long)(int)offsetof(struct mdt_rec_setattr, sa_blocks));
+ LASSERTF((int)sizeof(((struct mdt_rec_setattr *)0)->sa_blocks) == 8, "found %lld\n",
+ (long long)(int)sizeof(((struct mdt_rec_setattr *)0)->sa_blocks));
+ LASSERTF((int)offsetof(struct mdt_rec_setattr, sa_mtime) == 88, "found %lld\n",
+ (long long)(int)offsetof(struct mdt_rec_setattr, sa_mtime));
+ LASSERTF((int)sizeof(((struct mdt_rec_setattr *)0)->sa_mtime) == 8, "found %lld\n",
+ (long long)(int)sizeof(((struct mdt_rec_setattr *)0)->sa_mtime));
+ LASSERTF((int)offsetof(struct mdt_rec_setattr, sa_atime) == 96, "found %lld\n",
+ (long long)(int)offsetof(struct mdt_rec_setattr, sa_atime));
+ LASSERTF((int)sizeof(((struct mdt_rec_setattr *)0)->sa_atime) == 8, "found %lld\n",
+ (long long)(int)sizeof(((struct mdt_rec_setattr *)0)->sa_atime));
+ LASSERTF((int)offsetof(struct mdt_rec_setattr, sa_ctime) == 104, "found %lld\n",
+ (long long)(int)offsetof(struct mdt_rec_setattr, sa_ctime));
+ LASSERTF((int)sizeof(((struct mdt_rec_setattr *)0)->sa_ctime) == 8, "found %lld\n",
+ (long long)(int)sizeof(((struct mdt_rec_setattr *)0)->sa_ctime));
+ LASSERTF((int)offsetof(struct mdt_rec_setattr, sa_attr_flags) == 112, "found %lld\n",
+ (long long)(int)offsetof(struct mdt_rec_setattr, sa_attr_flags));
+ LASSERTF((int)sizeof(((struct mdt_rec_setattr *)0)->sa_attr_flags) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct mdt_rec_setattr *)0)->sa_attr_flags));
+ LASSERTF((int)offsetof(struct mdt_rec_setattr, sa_mode) == 116, "found %lld\n",
+ (long long)(int)offsetof(struct mdt_rec_setattr, sa_mode));
+ LASSERTF((int)sizeof(((struct mdt_rec_setattr *)0)->sa_mode) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct mdt_rec_setattr *)0)->sa_mode));
+ LASSERTF((int)offsetof(struct mdt_rec_setattr, sa_bias) == 120, "found %lld\n",
+ (long long)(int)offsetof(struct mdt_rec_setattr, sa_bias));
+ LASSERTF((int)sizeof(((struct mdt_rec_setattr *)0)->sa_bias) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct mdt_rec_setattr *)0)->sa_bias));
+ LASSERTF((int)offsetof(struct mdt_rec_setattr, sa_padding_3) == 124, "found %lld\n",
+ (long long)(int)offsetof(struct mdt_rec_setattr, sa_padding_3));
+ LASSERTF((int)sizeof(((struct mdt_rec_setattr *)0)->sa_padding_3) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct mdt_rec_setattr *)0)->sa_padding_3));
+ LASSERTF((int)offsetof(struct mdt_rec_setattr, sa_padding_4) == 128, "found %lld\n",
+ (long long)(int)offsetof(struct mdt_rec_setattr, sa_padding_4));
+ LASSERTF((int)sizeof(((struct mdt_rec_setattr *)0)->sa_padding_4) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct mdt_rec_setattr *)0)->sa_padding_4));
+ LASSERTF((int)offsetof(struct mdt_rec_setattr, sa_padding_5) == 132, "found %lld\n",
+ (long long)(int)offsetof(struct mdt_rec_setattr, sa_padding_5));
+ LASSERTF((int)sizeof(((struct mdt_rec_setattr *)0)->sa_padding_5) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct mdt_rec_setattr *)0)->sa_padding_5));
+
+ /* Checks for struct mdt_rec_create */
+ LASSERTF((int)sizeof(struct mdt_rec_create) == 136, "found %lld\n",
+ (long long)(int)sizeof(struct mdt_rec_create));
+ LASSERTF((int)offsetof(struct mdt_rec_create, cr_opcode) == 0, "found %lld\n",
+ (long long)(int)offsetof(struct mdt_rec_create, cr_opcode));
+ LASSERTF((int)sizeof(((struct mdt_rec_create *)0)->cr_opcode) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct mdt_rec_create *)0)->cr_opcode));
+ LASSERTF((int)offsetof(struct mdt_rec_create, cr_cap) == 4, "found %lld\n",
+ (long long)(int)offsetof(struct mdt_rec_create, cr_cap));
+ LASSERTF((int)sizeof(((struct mdt_rec_create *)0)->cr_cap) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct mdt_rec_create *)0)->cr_cap));
+ LASSERTF((int)offsetof(struct mdt_rec_create, cr_fsuid) == 8, "found %lld\n",
+ (long long)(int)offsetof(struct mdt_rec_create, cr_fsuid));
+ LASSERTF((int)sizeof(((struct mdt_rec_create *)0)->cr_fsuid) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct mdt_rec_create *)0)->cr_fsuid));
+ LASSERTF((int)offsetof(struct mdt_rec_create, cr_fsuid_h) == 12, "found %lld\n",
+ (long long)(int)offsetof(struct mdt_rec_create, cr_fsuid_h));
+ LASSERTF((int)sizeof(((struct mdt_rec_create *)0)->cr_fsuid_h) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct mdt_rec_create *)0)->cr_fsuid_h));
+ LASSERTF((int)offsetof(struct mdt_rec_create, cr_fsgid) == 16, "found %lld\n",
+ (long long)(int)offsetof(struct mdt_rec_create, cr_fsgid));
+ LASSERTF((int)sizeof(((struct mdt_rec_create *)0)->cr_fsgid) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct mdt_rec_create *)0)->cr_fsgid));
+ LASSERTF((int)offsetof(struct mdt_rec_create, cr_fsgid_h) == 20, "found %lld\n",
+ (long long)(int)offsetof(struct mdt_rec_create, cr_fsgid_h));
+ LASSERTF((int)sizeof(((struct mdt_rec_create *)0)->cr_fsgid_h) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct mdt_rec_create *)0)->cr_fsgid_h));
+ LASSERTF((int)offsetof(struct mdt_rec_create, cr_suppgid1) == 24, "found %lld\n",
+ (long long)(int)offsetof(struct mdt_rec_create, cr_suppgid1));
+ LASSERTF((int)sizeof(((struct mdt_rec_create *)0)->cr_suppgid1) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct mdt_rec_create *)0)->cr_suppgid1));
+ LASSERTF((int)offsetof(struct mdt_rec_create, cr_suppgid1_h) == 28, "found %lld\n",
+ (long long)(int)offsetof(struct mdt_rec_create, cr_suppgid1_h));
+ LASSERTF((int)sizeof(((struct mdt_rec_create *)0)->cr_suppgid1_h) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct mdt_rec_create *)0)->cr_suppgid1_h));
+ LASSERTF((int)offsetof(struct mdt_rec_create, cr_suppgid2) == 32, "found %lld\n",
+ (long long)(int)offsetof(struct mdt_rec_create, cr_suppgid2));
+ LASSERTF((int)sizeof(((struct mdt_rec_create *)0)->cr_suppgid2) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct mdt_rec_create *)0)->cr_suppgid2));
+ LASSERTF((int)offsetof(struct mdt_rec_create, cr_suppgid2_h) == 36, "found %lld\n",
+ (long long)(int)offsetof(struct mdt_rec_create, cr_suppgid2_h));
+ LASSERTF((int)sizeof(((struct mdt_rec_create *)0)->cr_suppgid2_h) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct mdt_rec_create *)0)->cr_suppgid2_h));
+ LASSERTF((int)offsetof(struct mdt_rec_create, cr_fid1) == 40, "found %lld\n",
+ (long long)(int)offsetof(struct mdt_rec_create, cr_fid1));
+ LASSERTF((int)sizeof(((struct mdt_rec_create *)0)->cr_fid1) == 16, "found %lld\n",
+ (long long)(int)sizeof(((struct mdt_rec_create *)0)->cr_fid1));
+ LASSERTF((int)offsetof(struct mdt_rec_create, cr_fid2) == 56, "found %lld\n",
+ (long long)(int)offsetof(struct mdt_rec_create, cr_fid2));
+ LASSERTF((int)sizeof(((struct mdt_rec_create *)0)->cr_fid2) == 16, "found %lld\n",
+ (long long)(int)sizeof(((struct mdt_rec_create *)0)->cr_fid2));
+ LASSERTF((int)offsetof(struct mdt_rec_create, cr_old_handle) == 72, "found %lld\n",
+ (long long)(int)offsetof(struct mdt_rec_create, cr_old_handle));
+ LASSERTF((int)sizeof(((struct mdt_rec_create *)0)->cr_old_handle) == 8, "found %lld\n",
+ (long long)(int)sizeof(((struct mdt_rec_create *)0)->cr_old_handle));
+ LASSERTF((int)offsetof(struct mdt_rec_create, cr_time) == 80, "found %lld\n",
+ (long long)(int)offsetof(struct mdt_rec_create, cr_time));
+ LASSERTF((int)sizeof(((struct mdt_rec_create *)0)->cr_time) == 8, "found %lld\n",
+ (long long)(int)sizeof(((struct mdt_rec_create *)0)->cr_time));
+ LASSERTF((int)offsetof(struct mdt_rec_create, cr_rdev) == 88, "found %lld\n",
+ (long long)(int)offsetof(struct mdt_rec_create, cr_rdev));
+ LASSERTF((int)sizeof(((struct mdt_rec_create *)0)->cr_rdev) == 8, "found %lld\n",
+ (long long)(int)sizeof(((struct mdt_rec_create *)0)->cr_rdev));
+ LASSERTF((int)offsetof(struct mdt_rec_create, cr_ioepoch) == 96, "found %lld\n",
+ (long long)(int)offsetof(struct mdt_rec_create, cr_ioepoch));
+ LASSERTF((int)sizeof(((struct mdt_rec_create *)0)->cr_ioepoch) == 8, "found %lld\n",
+ (long long)(int)sizeof(((struct mdt_rec_create *)0)->cr_ioepoch));
+ LASSERTF((int)offsetof(struct mdt_rec_create, cr_padding_1) == 104, "found %lld\n",
+ (long long)(int)offsetof(struct mdt_rec_create, cr_padding_1));
+ LASSERTF((int)sizeof(((struct mdt_rec_create *)0)->cr_padding_1) == 8, "found %lld\n",
+ (long long)(int)sizeof(((struct mdt_rec_create *)0)->cr_padding_1));
+ LASSERTF((int)offsetof(struct mdt_rec_create, cr_mode) == 112, "found %lld\n",
+ (long long)(int)offsetof(struct mdt_rec_create, cr_mode));
+ LASSERTF((int)sizeof(((struct mdt_rec_create *)0)->cr_mode) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct mdt_rec_create *)0)->cr_mode));
+ LASSERTF((int)offsetof(struct mdt_rec_create, cr_bias) == 116, "found %lld\n",
+ (long long)(int)offsetof(struct mdt_rec_create, cr_bias));
+ LASSERTF((int)sizeof(((struct mdt_rec_create *)0)->cr_bias) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct mdt_rec_create *)0)->cr_bias));
+ LASSERTF((int)offsetof(struct mdt_rec_create, cr_flags_l) == 120, "found %lld\n",
+ (long long)(int)offsetof(struct mdt_rec_create, cr_flags_l));
+ LASSERTF((int)sizeof(((struct mdt_rec_create *)0)->cr_flags_l) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct mdt_rec_create *)0)->cr_flags_l));
+ LASSERTF((int)offsetof(struct mdt_rec_create, cr_flags_h) == 124, "found %lld\n",
+ (long long)(int)offsetof(struct mdt_rec_create, cr_flags_h));
+ LASSERTF((int)sizeof(((struct mdt_rec_create *)0)->cr_flags_h) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct mdt_rec_create *)0)->cr_flags_h));
+ LASSERTF((int)offsetof(struct mdt_rec_create, cr_umask) == 128, "found %lld\n",
+ (long long)(int)offsetof(struct mdt_rec_create, cr_umask));
+ LASSERTF((int)sizeof(((struct mdt_rec_create *)0)->cr_umask) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct mdt_rec_create *)0)->cr_umask));
+ LASSERTF((int)offsetof(struct mdt_rec_create, cr_padding_4) == 132, "found %lld\n",
+ (long long)(int)offsetof(struct mdt_rec_create, cr_padding_4));
+ LASSERTF((int)sizeof(((struct mdt_rec_create *)0)->cr_padding_4) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct mdt_rec_create *)0)->cr_padding_4));
+
+ /* Checks for struct mdt_rec_link */
+ LASSERTF((int)sizeof(struct mdt_rec_link) == 136, "found %lld\n",
+ (long long)(int)sizeof(struct mdt_rec_link));
+ LASSERTF((int)offsetof(struct mdt_rec_link, lk_opcode) == 0, "found %lld\n",
+ (long long)(int)offsetof(struct mdt_rec_link, lk_opcode));
+ LASSERTF((int)sizeof(((struct mdt_rec_link *)0)->lk_opcode) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct mdt_rec_link *)0)->lk_opcode));
+ LASSERTF((int)offsetof(struct mdt_rec_link, lk_cap) == 4, "found %lld\n",
+ (long long)(int)offsetof(struct mdt_rec_link, lk_cap));
+ LASSERTF((int)sizeof(((struct mdt_rec_link *)0)->lk_cap) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct mdt_rec_link *)0)->lk_cap));
+ LASSERTF((int)offsetof(struct mdt_rec_link, lk_fsuid) == 8, "found %lld\n",
+ (long long)(int)offsetof(struct mdt_rec_link, lk_fsuid));
+ LASSERTF((int)sizeof(((struct mdt_rec_link *)0)->lk_fsuid) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct mdt_rec_link *)0)->lk_fsuid));
+ LASSERTF((int)offsetof(struct mdt_rec_link, lk_fsuid_h) == 12, "found %lld\n",
+ (long long)(int)offsetof(struct mdt_rec_link, lk_fsuid_h));
+ LASSERTF((int)sizeof(((struct mdt_rec_link *)0)->lk_fsuid_h) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct mdt_rec_link *)0)->lk_fsuid_h));
+ LASSERTF((int)offsetof(struct mdt_rec_link, lk_fsgid) == 16, "found %lld\n",
+ (long long)(int)offsetof(struct mdt_rec_link, lk_fsgid));
+ LASSERTF((int)sizeof(((struct mdt_rec_link *)0)->lk_fsgid) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct mdt_rec_link *)0)->lk_fsgid));
+ LASSERTF((int)offsetof(struct mdt_rec_link, lk_fsgid_h) == 20, "found %lld\n",
+ (long long)(int)offsetof(struct mdt_rec_link, lk_fsgid_h));
+ LASSERTF((int)sizeof(((struct mdt_rec_link *)0)->lk_fsgid_h) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct mdt_rec_link *)0)->lk_fsgid_h));
+ LASSERTF((int)offsetof(struct mdt_rec_link, lk_suppgid1) == 24, "found %lld\n",
+ (long long)(int)offsetof(struct mdt_rec_link, lk_suppgid1));
+ LASSERTF((int)sizeof(((struct mdt_rec_link *)0)->lk_suppgid1) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct mdt_rec_link *)0)->lk_suppgid1));
+ LASSERTF((int)offsetof(struct mdt_rec_link, lk_suppgid1_h) == 28, "found %lld\n",
+ (long long)(int)offsetof(struct mdt_rec_link, lk_suppgid1_h));
+ LASSERTF((int)sizeof(((struct mdt_rec_link *)0)->lk_suppgid1_h) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct mdt_rec_link *)0)->lk_suppgid1_h));
+ LASSERTF((int)offsetof(struct mdt_rec_link, lk_suppgid2) == 32, "found %lld\n",
+ (long long)(int)offsetof(struct mdt_rec_link, lk_suppgid2));
+ LASSERTF((int)sizeof(((struct mdt_rec_link *)0)->lk_suppgid2) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct mdt_rec_link *)0)->lk_suppgid2));
+ LASSERTF((int)offsetof(struct mdt_rec_link, lk_suppgid2_h) == 36, "found %lld\n",
+ (long long)(int)offsetof(struct mdt_rec_link, lk_suppgid2_h));
+ LASSERTF((int)sizeof(((struct mdt_rec_link *)0)->lk_suppgid2_h) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct mdt_rec_link *)0)->lk_suppgid2_h));
+ LASSERTF((int)offsetof(struct mdt_rec_link, lk_fid1) == 40, "found %lld\n",
+ (long long)(int)offsetof(struct mdt_rec_link, lk_fid1));
+ LASSERTF((int)sizeof(((struct mdt_rec_link *)0)->lk_fid1) == 16, "found %lld\n",
+ (long long)(int)sizeof(((struct mdt_rec_link *)0)->lk_fid1));
+ LASSERTF((int)offsetof(struct mdt_rec_link, lk_fid2) == 56, "found %lld\n",
+ (long long)(int)offsetof(struct mdt_rec_link, lk_fid2));
+ LASSERTF((int)sizeof(((struct mdt_rec_link *)0)->lk_fid2) == 16, "found %lld\n",
+ (long long)(int)sizeof(((struct mdt_rec_link *)0)->lk_fid2));
+ LASSERTF((int)offsetof(struct mdt_rec_link, lk_time) == 72, "found %lld\n",
+ (long long)(int)offsetof(struct mdt_rec_link, lk_time));
+ LASSERTF((int)sizeof(((struct mdt_rec_link *)0)->lk_time) == 8, "found %lld\n",
+ (long long)(int)sizeof(((struct mdt_rec_link *)0)->lk_time));
+ LASSERTF((int)offsetof(struct mdt_rec_link, lk_padding_1) == 80, "found %lld\n",
+ (long long)(int)offsetof(struct mdt_rec_link, lk_padding_1));
+ LASSERTF((int)sizeof(((struct mdt_rec_link *)0)->lk_padding_1) == 8, "found %lld\n",
+ (long long)(int)sizeof(((struct mdt_rec_link *)0)->lk_padding_1));
+ LASSERTF((int)offsetof(struct mdt_rec_link, lk_padding_2) == 88, "found %lld\n",
+ (long long)(int)offsetof(struct mdt_rec_link, lk_padding_2));
+ LASSERTF((int)sizeof(((struct mdt_rec_link *)0)->lk_padding_2) == 8, "found %lld\n",
+ (long long)(int)sizeof(((struct mdt_rec_link *)0)->lk_padding_2));
+ LASSERTF((int)offsetof(struct mdt_rec_link, lk_padding_3) == 96, "found %lld\n",
+ (long long)(int)offsetof(struct mdt_rec_link, lk_padding_3));
+ LASSERTF((int)sizeof(((struct mdt_rec_link *)0)->lk_padding_3) == 8, "found %lld\n",
+ (long long)(int)sizeof(((struct mdt_rec_link *)0)->lk_padding_3));
+ LASSERTF((int)offsetof(struct mdt_rec_link, lk_padding_4) == 104, "found %lld\n",
+ (long long)(int)offsetof(struct mdt_rec_link, lk_padding_4));
+ LASSERTF((int)sizeof(((struct mdt_rec_link *)0)->lk_padding_4) == 8, "found %lld\n",
+ (long long)(int)sizeof(((struct mdt_rec_link *)0)->lk_padding_4));
+ LASSERTF((int)offsetof(struct mdt_rec_link, lk_bias) == 112, "found %lld\n",
+ (long long)(int)offsetof(struct mdt_rec_link, lk_bias));
+ LASSERTF((int)sizeof(((struct mdt_rec_link *)0)->lk_bias) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct mdt_rec_link *)0)->lk_bias));
+ LASSERTF((int)offsetof(struct mdt_rec_link, lk_padding_5) == 116, "found %lld\n",
+ (long long)(int)offsetof(struct mdt_rec_link, lk_padding_5));
+ LASSERTF((int)sizeof(((struct mdt_rec_link *)0)->lk_padding_5) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct mdt_rec_link *)0)->lk_padding_5));
+ LASSERTF((int)offsetof(struct mdt_rec_link, lk_padding_6) == 120, "found %lld\n",
+ (long long)(int)offsetof(struct mdt_rec_link, lk_padding_6));
+ LASSERTF((int)sizeof(((struct mdt_rec_link *)0)->lk_padding_6) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct mdt_rec_link *)0)->lk_padding_6));
+ LASSERTF((int)offsetof(struct mdt_rec_link, lk_padding_7) == 124, "found %lld\n",
+ (long long)(int)offsetof(struct mdt_rec_link, lk_padding_7));
+ LASSERTF((int)sizeof(((struct mdt_rec_link *)0)->lk_padding_7) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct mdt_rec_link *)0)->lk_padding_7));
+ LASSERTF((int)offsetof(struct mdt_rec_link, lk_padding_8) == 128, "found %lld\n",
+ (long long)(int)offsetof(struct mdt_rec_link, lk_padding_8));
+ LASSERTF((int)sizeof(((struct mdt_rec_link *)0)->lk_padding_8) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct mdt_rec_link *)0)->lk_padding_8));
+ LASSERTF((int)offsetof(struct mdt_rec_link, lk_padding_9) == 132, "found %lld\n",
+ (long long)(int)offsetof(struct mdt_rec_link, lk_padding_9));
+ LASSERTF((int)sizeof(((struct mdt_rec_link *)0)->lk_padding_9) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct mdt_rec_link *)0)->lk_padding_9));
+
+ /* Checks for struct mdt_rec_unlink */
+ LASSERTF((int)sizeof(struct mdt_rec_unlink) == 136, "found %lld\n",
+ (long long)(int)sizeof(struct mdt_rec_unlink));
+ LASSERTF((int)offsetof(struct mdt_rec_unlink, ul_opcode) == 0, "found %lld\n",
+ (long long)(int)offsetof(struct mdt_rec_unlink, ul_opcode));
+ LASSERTF((int)sizeof(((struct mdt_rec_unlink *)0)->ul_opcode) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct mdt_rec_unlink *)0)->ul_opcode));
+ LASSERTF((int)offsetof(struct mdt_rec_unlink, ul_cap) == 4, "found %lld\n",
+ (long long)(int)offsetof(struct mdt_rec_unlink, ul_cap));
+ LASSERTF((int)sizeof(((struct mdt_rec_unlink *)0)->ul_cap) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct mdt_rec_unlink *)0)->ul_cap));
+ LASSERTF((int)offsetof(struct mdt_rec_unlink, ul_fsuid) == 8, "found %lld\n",
+ (long long)(int)offsetof(struct mdt_rec_unlink, ul_fsuid));
+ LASSERTF((int)sizeof(((struct mdt_rec_unlink *)0)->ul_fsuid) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct mdt_rec_unlink *)0)->ul_fsuid));
+ LASSERTF((int)offsetof(struct mdt_rec_unlink, ul_fsuid_h) == 12, "found %lld\n",
+ (long long)(int)offsetof(struct mdt_rec_unlink, ul_fsuid_h));
+ LASSERTF((int)sizeof(((struct mdt_rec_unlink *)0)->ul_fsuid_h) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct mdt_rec_unlink *)0)->ul_fsuid_h));
+ LASSERTF((int)offsetof(struct mdt_rec_unlink, ul_fsgid) == 16, "found %lld\n",
+ (long long)(int)offsetof(struct mdt_rec_unlink, ul_fsgid));
+ LASSERTF((int)sizeof(((struct mdt_rec_unlink *)0)->ul_fsgid) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct mdt_rec_unlink *)0)->ul_fsgid));
+ LASSERTF((int)offsetof(struct mdt_rec_unlink, ul_fsgid_h) == 20, "found %lld\n",
+ (long long)(int)offsetof(struct mdt_rec_unlink, ul_fsgid_h));
+ LASSERTF((int)sizeof(((struct mdt_rec_unlink *)0)->ul_fsgid_h) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct mdt_rec_unlink *)0)->ul_fsgid_h));
+ LASSERTF((int)offsetof(struct mdt_rec_unlink, ul_suppgid1) == 24, "found %lld\n",
+ (long long)(int)offsetof(struct mdt_rec_unlink, ul_suppgid1));
+ LASSERTF((int)sizeof(((struct mdt_rec_unlink *)0)->ul_suppgid1) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct mdt_rec_unlink *)0)->ul_suppgid1));
+ LASSERTF((int)offsetof(struct mdt_rec_unlink, ul_suppgid1_h) == 28, "found %lld\n",
+ (long long)(int)offsetof(struct mdt_rec_unlink, ul_suppgid1_h));
+ LASSERTF((int)sizeof(((struct mdt_rec_unlink *)0)->ul_suppgid1_h) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct mdt_rec_unlink *)0)->ul_suppgid1_h));
+ LASSERTF((int)offsetof(struct mdt_rec_unlink, ul_suppgid2) == 32, "found %lld\n",
+ (long long)(int)offsetof(struct mdt_rec_unlink, ul_suppgid2));
+ LASSERTF((int)sizeof(((struct mdt_rec_unlink *)0)->ul_suppgid2) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct mdt_rec_unlink *)0)->ul_suppgid2));
+ LASSERTF((int)offsetof(struct mdt_rec_unlink, ul_suppgid2_h) == 36, "found %lld\n",
+ (long long)(int)offsetof(struct mdt_rec_unlink, ul_suppgid2_h));
+ LASSERTF((int)sizeof(((struct mdt_rec_unlink *)0)->ul_suppgid2_h) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct mdt_rec_unlink *)0)->ul_suppgid2_h));
+ LASSERTF((int)offsetof(struct mdt_rec_unlink, ul_fid1) == 40, "found %lld\n",
+ (long long)(int)offsetof(struct mdt_rec_unlink, ul_fid1));
+ LASSERTF((int)sizeof(((struct mdt_rec_unlink *)0)->ul_fid1) == 16, "found %lld\n",
+ (long long)(int)sizeof(((struct mdt_rec_unlink *)0)->ul_fid1));
+ LASSERTF((int)offsetof(struct mdt_rec_unlink, ul_fid2) == 56, "found %lld\n",
+ (long long)(int)offsetof(struct mdt_rec_unlink, ul_fid2));
+ LASSERTF((int)sizeof(((struct mdt_rec_unlink *)0)->ul_fid2) == 16, "found %lld\n",
+ (long long)(int)sizeof(((struct mdt_rec_unlink *)0)->ul_fid2));
+ LASSERTF((int)offsetof(struct mdt_rec_unlink, ul_time) == 72, "found %lld\n",
+ (long long)(int)offsetof(struct mdt_rec_unlink, ul_time));
+ LASSERTF((int)sizeof(((struct mdt_rec_unlink *)0)->ul_time) == 8, "found %lld\n",
+ (long long)(int)sizeof(((struct mdt_rec_unlink *)0)->ul_time));
+ LASSERTF((int)offsetof(struct mdt_rec_unlink, ul_padding_2) == 80, "found %lld\n",
+ (long long)(int)offsetof(struct mdt_rec_unlink, ul_padding_2));
+ LASSERTF((int)sizeof(((struct mdt_rec_unlink *)0)->ul_padding_2) == 8, "found %lld\n",
+ (long long)(int)sizeof(((struct mdt_rec_unlink *)0)->ul_padding_2));
+ LASSERTF((int)offsetof(struct mdt_rec_unlink, ul_padding_3) == 88, "found %lld\n",
+ (long long)(int)offsetof(struct mdt_rec_unlink, ul_padding_3));
+ LASSERTF((int)sizeof(((struct mdt_rec_unlink *)0)->ul_padding_3) == 8, "found %lld\n",
+ (long long)(int)sizeof(((struct mdt_rec_unlink *)0)->ul_padding_3));
+ LASSERTF((int)offsetof(struct mdt_rec_unlink, ul_padding_4) == 96, "found %lld\n",
+ (long long)(int)offsetof(struct mdt_rec_unlink, ul_padding_4));
+ LASSERTF((int)sizeof(((struct mdt_rec_unlink *)0)->ul_padding_4) == 8, "found %lld\n",
+ (long long)(int)sizeof(((struct mdt_rec_unlink *)0)->ul_padding_4));
+ LASSERTF((int)offsetof(struct mdt_rec_unlink, ul_padding_5) == 104, "found %lld\n",
+ (long long)(int)offsetof(struct mdt_rec_unlink, ul_padding_5));
+ LASSERTF((int)sizeof(((struct mdt_rec_unlink *)0)->ul_padding_5) == 8, "found %lld\n",
+ (long long)(int)sizeof(((struct mdt_rec_unlink *)0)->ul_padding_5));
+ LASSERTF((int)offsetof(struct mdt_rec_unlink, ul_bias) == 112, "found %lld\n",
+ (long long)(int)offsetof(struct mdt_rec_unlink, ul_bias));
+ LASSERTF((int)sizeof(((struct mdt_rec_unlink *)0)->ul_bias) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct mdt_rec_unlink *)0)->ul_bias));
+ LASSERTF((int)offsetof(struct mdt_rec_unlink, ul_mode) == 116, "found %lld\n",
+ (long long)(int)offsetof(struct mdt_rec_unlink, ul_mode));
+ LASSERTF((int)sizeof(((struct mdt_rec_unlink *)0)->ul_mode) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct mdt_rec_unlink *)0)->ul_mode));
+ LASSERTF((int)offsetof(struct mdt_rec_unlink, ul_padding_6) == 120, "found %lld\n",
+ (long long)(int)offsetof(struct mdt_rec_unlink, ul_padding_6));
+ LASSERTF((int)sizeof(((struct mdt_rec_unlink *)0)->ul_padding_6) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct mdt_rec_unlink *)0)->ul_padding_6));
+ LASSERTF((int)offsetof(struct mdt_rec_unlink, ul_padding_7) == 124, "found %lld\n",
+ (long long)(int)offsetof(struct mdt_rec_unlink, ul_padding_7));
+ LASSERTF((int)sizeof(((struct mdt_rec_unlink *)0)->ul_padding_7) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct mdt_rec_unlink *)0)->ul_padding_7));
+ LASSERTF((int)offsetof(struct mdt_rec_unlink, ul_padding_8) == 128, "found %lld\n",
+ (long long)(int)offsetof(struct mdt_rec_unlink, ul_padding_8));
+ LASSERTF((int)sizeof(((struct mdt_rec_unlink *)0)->ul_padding_8) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct mdt_rec_unlink *)0)->ul_padding_8));
+ LASSERTF((int)offsetof(struct mdt_rec_unlink, ul_padding_9) == 132, "found %lld\n",
+ (long long)(int)offsetof(struct mdt_rec_unlink, ul_padding_9));
+ LASSERTF((int)sizeof(((struct mdt_rec_unlink *)0)->ul_padding_9) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct mdt_rec_unlink *)0)->ul_padding_9));
+
+ /* Checks for struct mdt_rec_rename */
+ LASSERTF((int)sizeof(struct mdt_rec_rename) == 136, "found %lld\n",
+ (long long)(int)sizeof(struct mdt_rec_rename));
+ LASSERTF((int)offsetof(struct mdt_rec_rename, rn_opcode) == 0, "found %lld\n",
+ (long long)(int)offsetof(struct mdt_rec_rename, rn_opcode));
+ LASSERTF((int)sizeof(((struct mdt_rec_rename *)0)->rn_opcode) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct mdt_rec_rename *)0)->rn_opcode));
+ LASSERTF((int)offsetof(struct mdt_rec_rename, rn_cap) == 4, "found %lld\n",
+ (long long)(int)offsetof(struct mdt_rec_rename, rn_cap));
+ LASSERTF((int)sizeof(((struct mdt_rec_rename *)0)->rn_cap) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct mdt_rec_rename *)0)->rn_cap));
+ LASSERTF((int)offsetof(struct mdt_rec_rename, rn_fsuid) == 8, "found %lld\n",
+ (long long)(int)offsetof(struct mdt_rec_rename, rn_fsuid));
+ LASSERTF((int)sizeof(((struct mdt_rec_rename *)0)->rn_fsuid) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct mdt_rec_rename *)0)->rn_fsuid));
+ LASSERTF((int)offsetof(struct mdt_rec_rename, rn_fsuid_h) == 12, "found %lld\n",
+ (long long)(int)offsetof(struct mdt_rec_rename, rn_fsuid_h));
+ LASSERTF((int)sizeof(((struct mdt_rec_rename *)0)->rn_fsuid_h) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct mdt_rec_rename *)0)->rn_fsuid_h));
+ LASSERTF((int)offsetof(struct mdt_rec_rename, rn_fsgid) == 16, "found %lld\n",
+ (long long)(int)offsetof(struct mdt_rec_rename, rn_fsgid));
+ LASSERTF((int)sizeof(((struct mdt_rec_rename *)0)->rn_fsgid) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct mdt_rec_rename *)0)->rn_fsgid));
+ LASSERTF((int)offsetof(struct mdt_rec_rename, rn_fsgid_h) == 20, "found %lld\n",
+ (long long)(int)offsetof(struct mdt_rec_rename, rn_fsgid_h));
+ LASSERTF((int)sizeof(((struct mdt_rec_rename *)0)->rn_fsgid_h) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct mdt_rec_rename *)0)->rn_fsgid_h));
+ LASSERTF((int)offsetof(struct mdt_rec_rename, rn_suppgid1) == 24, "found %lld\n",
+ (long long)(int)offsetof(struct mdt_rec_rename, rn_suppgid1));
+ LASSERTF((int)sizeof(((struct mdt_rec_rename *)0)->rn_suppgid1) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct mdt_rec_rename *)0)->rn_suppgid1));
+ LASSERTF((int)offsetof(struct mdt_rec_rename, rn_suppgid1_h) == 28, "found %lld\n",
+ (long long)(int)offsetof(struct mdt_rec_rename, rn_suppgid1_h));
+ LASSERTF((int)sizeof(((struct mdt_rec_rename *)0)->rn_suppgid1_h) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct mdt_rec_rename *)0)->rn_suppgid1_h));
+ LASSERTF((int)offsetof(struct mdt_rec_rename, rn_suppgid2) == 32, "found %lld\n",
+ (long long)(int)offsetof(struct mdt_rec_rename, rn_suppgid2));
+ LASSERTF((int)sizeof(((struct mdt_rec_rename *)0)->rn_suppgid2) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct mdt_rec_rename *)0)->rn_suppgid2));
+ LASSERTF((int)offsetof(struct mdt_rec_rename, rn_suppgid2_h) == 36, "found %lld\n",
+ (long long)(int)offsetof(struct mdt_rec_rename, rn_suppgid2_h));
+ LASSERTF((int)sizeof(((struct mdt_rec_rename *)0)->rn_suppgid2_h) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct mdt_rec_rename *)0)->rn_suppgid2_h));
+ LASSERTF((int)offsetof(struct mdt_rec_rename, rn_fid1) == 40, "found %lld\n",
+ (long long)(int)offsetof(struct mdt_rec_rename, rn_fid1));
+ LASSERTF((int)sizeof(((struct mdt_rec_rename *)0)->rn_fid1) == 16, "found %lld\n",
+ (long long)(int)sizeof(((struct mdt_rec_rename *)0)->rn_fid1));
+ LASSERTF((int)offsetof(struct mdt_rec_rename, rn_fid2) == 56, "found %lld\n",
+ (long long)(int)offsetof(struct mdt_rec_rename, rn_fid2));
+ LASSERTF((int)sizeof(((struct mdt_rec_rename *)0)->rn_fid2) == 16, "found %lld\n",
+ (long long)(int)sizeof(((struct mdt_rec_rename *)0)->rn_fid2));
+ LASSERTF((int)offsetof(struct mdt_rec_rename, rn_time) == 72, "found %lld\n",
+ (long long)(int)offsetof(struct mdt_rec_rename, rn_time));
+ LASSERTF((int)sizeof(((struct mdt_rec_rename *)0)->rn_time) == 8, "found %lld\n",
+ (long long)(int)sizeof(((struct mdt_rec_rename *)0)->rn_time));
+ LASSERTF((int)offsetof(struct mdt_rec_rename, rn_padding_1) == 80, "found %lld\n",
+ (long long)(int)offsetof(struct mdt_rec_rename, rn_padding_1));
+ LASSERTF((int)sizeof(((struct mdt_rec_rename *)0)->rn_padding_1) == 8, "found %lld\n",
+ (long long)(int)sizeof(((struct mdt_rec_rename *)0)->rn_padding_1));
+ LASSERTF((int)offsetof(struct mdt_rec_rename, rn_padding_2) == 88, "found %lld\n",
+ (long long)(int)offsetof(struct mdt_rec_rename, rn_padding_2));
+ LASSERTF((int)sizeof(((struct mdt_rec_rename *)0)->rn_padding_2) == 8, "found %lld\n",
+ (long long)(int)sizeof(((struct mdt_rec_rename *)0)->rn_padding_2));
+ LASSERTF((int)offsetof(struct mdt_rec_rename, rn_padding_3) == 96, "found %lld\n",
+ (long long)(int)offsetof(struct mdt_rec_rename, rn_padding_3));
+ LASSERTF((int)sizeof(((struct mdt_rec_rename *)0)->rn_padding_3) == 8, "found %lld\n",
+ (long long)(int)sizeof(((struct mdt_rec_rename *)0)->rn_padding_3));
+ LASSERTF((int)offsetof(struct mdt_rec_rename, rn_padding_4) == 104, "found %lld\n",
+ (long long)(int)offsetof(struct mdt_rec_rename, rn_padding_4));
+ LASSERTF((int)sizeof(((struct mdt_rec_rename *)0)->rn_padding_4) == 8, "found %lld\n",
+ (long long)(int)sizeof(((struct mdt_rec_rename *)0)->rn_padding_4));
+ LASSERTF((int)offsetof(struct mdt_rec_rename, rn_bias) == 112, "found %lld\n",
+ (long long)(int)offsetof(struct mdt_rec_rename, rn_bias));
+ LASSERTF((int)sizeof(((struct mdt_rec_rename *)0)->rn_bias) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct mdt_rec_rename *)0)->rn_bias));
+ LASSERTF((int)offsetof(struct mdt_rec_rename, rn_mode) == 116, "found %lld\n",
+ (long long)(int)offsetof(struct mdt_rec_rename, rn_mode));
+ LASSERTF((int)sizeof(((struct mdt_rec_rename *)0)->rn_mode) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct mdt_rec_rename *)0)->rn_mode));
+ LASSERTF((int)offsetof(struct mdt_rec_rename, rn_padding_5) == 120, "found %lld\n",
+ (long long)(int)offsetof(struct mdt_rec_rename, rn_padding_5));
+ LASSERTF((int)sizeof(((struct mdt_rec_rename *)0)->rn_padding_5) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct mdt_rec_rename *)0)->rn_padding_5));
+ LASSERTF((int)offsetof(struct mdt_rec_rename, rn_padding_6) == 124, "found %lld\n",
+ (long long)(int)offsetof(struct mdt_rec_rename, rn_padding_6));
+ LASSERTF((int)sizeof(((struct mdt_rec_rename *)0)->rn_padding_6) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct mdt_rec_rename *)0)->rn_padding_6));
+ LASSERTF((int)offsetof(struct mdt_rec_rename, rn_padding_7) == 128, "found %lld\n",
+ (long long)(int)offsetof(struct mdt_rec_rename, rn_padding_7));
+ LASSERTF((int)sizeof(((struct mdt_rec_rename *)0)->rn_padding_7) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct mdt_rec_rename *)0)->rn_padding_7));
+ LASSERTF((int)offsetof(struct mdt_rec_rename, rn_padding_8) == 132, "found %lld\n",
+ (long long)(int)offsetof(struct mdt_rec_rename, rn_padding_8));
+ LASSERTF((int)sizeof(((struct mdt_rec_rename *)0)->rn_padding_8) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct mdt_rec_rename *)0)->rn_padding_8));
+
+ /* Checks for struct mdt_rec_setxattr */
+ LASSERTF((int)sizeof(struct mdt_rec_setxattr) == 136, "found %lld\n",
+ (long long)(int)sizeof(struct mdt_rec_setxattr));
+ LASSERTF((int)offsetof(struct mdt_rec_setxattr, sx_opcode) == 0, "found %lld\n",
+ (long long)(int)offsetof(struct mdt_rec_setxattr, sx_opcode));
+ LASSERTF((int)sizeof(((struct mdt_rec_setxattr *)0)->sx_opcode) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct mdt_rec_setxattr *)0)->sx_opcode));
+ LASSERTF((int)offsetof(struct mdt_rec_setxattr, sx_cap) == 4, "found %lld\n",
+ (long long)(int)offsetof(struct mdt_rec_setxattr, sx_cap));
+ LASSERTF((int)sizeof(((struct mdt_rec_setxattr *)0)->sx_cap) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct mdt_rec_setxattr *)0)->sx_cap));
+ LASSERTF((int)offsetof(struct mdt_rec_setxattr, sx_fsuid) == 8, "found %lld\n",
+ (long long)(int)offsetof(struct mdt_rec_setxattr, sx_fsuid));
+ LASSERTF((int)sizeof(((struct mdt_rec_setxattr *)0)->sx_fsuid) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct mdt_rec_setxattr *)0)->sx_fsuid));
+ LASSERTF((int)offsetof(struct mdt_rec_setxattr, sx_fsuid_h) == 12, "found %lld\n",
+ (long long)(int)offsetof(struct mdt_rec_setxattr, sx_fsuid_h));
+ LASSERTF((int)sizeof(((struct mdt_rec_setxattr *)0)->sx_fsuid_h) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct mdt_rec_setxattr *)0)->sx_fsuid_h));
+ LASSERTF((int)offsetof(struct mdt_rec_setxattr, sx_fsgid) == 16, "found %lld\n",
+ (long long)(int)offsetof(struct mdt_rec_setxattr, sx_fsgid));
+ LASSERTF((int)sizeof(((struct mdt_rec_setxattr *)0)->sx_fsgid) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct mdt_rec_setxattr *)0)->sx_fsgid));
+ LASSERTF((int)offsetof(struct mdt_rec_setxattr, sx_fsgid_h) == 20, "found %lld\n",
+ (long long)(int)offsetof(struct mdt_rec_setxattr, sx_fsgid_h));
+ LASSERTF((int)sizeof(((struct mdt_rec_setxattr *)0)->sx_fsgid_h) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct mdt_rec_setxattr *)0)->sx_fsgid_h));
+ LASSERTF((int)offsetof(struct mdt_rec_setxattr, sx_suppgid1) == 24, "found %lld\n",
+ (long long)(int)offsetof(struct mdt_rec_setxattr, sx_suppgid1));
+ LASSERTF((int)sizeof(((struct mdt_rec_setxattr *)0)->sx_suppgid1) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct mdt_rec_setxattr *)0)->sx_suppgid1));
+ LASSERTF((int)offsetof(struct mdt_rec_setxattr, sx_suppgid1_h) == 28, "found %lld\n",
+ (long long)(int)offsetof(struct mdt_rec_setxattr, sx_suppgid1_h));
+ LASSERTF((int)sizeof(((struct mdt_rec_setxattr *)0)->sx_suppgid1_h) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct mdt_rec_setxattr *)0)->sx_suppgid1_h));
+ LASSERTF((int)offsetof(struct mdt_rec_setxattr, sx_suppgid2) == 32, "found %lld\n",
+ (long long)(int)offsetof(struct mdt_rec_setxattr, sx_suppgid2));
+ LASSERTF((int)sizeof(((struct mdt_rec_setxattr *)0)->sx_suppgid2) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct mdt_rec_setxattr *)0)->sx_suppgid2));
+ LASSERTF((int)offsetof(struct mdt_rec_setxattr, sx_suppgid2_h) == 36, "found %lld\n",
+ (long long)(int)offsetof(struct mdt_rec_setxattr, sx_suppgid2_h));
+ LASSERTF((int)sizeof(((struct mdt_rec_setxattr *)0)->sx_suppgid2_h) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct mdt_rec_setxattr *)0)->sx_suppgid2_h));
+ LASSERTF((int)offsetof(struct mdt_rec_setxattr, sx_fid) == 40, "found %lld\n",
+ (long long)(int)offsetof(struct mdt_rec_setxattr, sx_fid));
+ LASSERTF((int)sizeof(((struct mdt_rec_setxattr *)0)->sx_fid) == 16, "found %lld\n",
+ (long long)(int)sizeof(((struct mdt_rec_setxattr *)0)->sx_fid));
+ LASSERTF((int)offsetof(struct mdt_rec_setxattr, sx_padding_1) == 56, "found %lld\n",
+ (long long)(int)offsetof(struct mdt_rec_setxattr, sx_padding_1));
+ LASSERTF((int)sizeof(((struct mdt_rec_setxattr *)0)->sx_padding_1) == 8, "found %lld\n",
+ (long long)(int)sizeof(((struct mdt_rec_setxattr *)0)->sx_padding_1));
+ LASSERTF((int)offsetof(struct mdt_rec_setxattr, sx_padding_2) == 64, "found %lld\n",
+ (long long)(int)offsetof(struct mdt_rec_setxattr, sx_padding_2));
+ LASSERTF((int)sizeof(((struct mdt_rec_setxattr *)0)->sx_padding_2) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct mdt_rec_setxattr *)0)->sx_padding_2));
+ LASSERTF((int)offsetof(struct mdt_rec_setxattr, sx_padding_3) == 68, "found %lld\n",
+ (long long)(int)offsetof(struct mdt_rec_setxattr, sx_padding_3));
+ LASSERTF((int)sizeof(((struct mdt_rec_setxattr *)0)->sx_padding_3) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct mdt_rec_setxattr *)0)->sx_padding_3));
+ LASSERTF((int)offsetof(struct mdt_rec_setxattr, sx_valid) == 72, "found %lld\n",
+ (long long)(int)offsetof(struct mdt_rec_setxattr, sx_valid));
+ LASSERTF((int)sizeof(((struct mdt_rec_setxattr *)0)->sx_valid) == 8, "found %lld\n",
+ (long long)(int)sizeof(((struct mdt_rec_setxattr *)0)->sx_valid));
+ LASSERTF((int)offsetof(struct mdt_rec_setxattr, sx_time) == 80, "found %lld\n",
+ (long long)(int)offsetof(struct mdt_rec_setxattr, sx_time));
+ LASSERTF((int)sizeof(((struct mdt_rec_setxattr *)0)->sx_time) == 8, "found %lld\n",
+ (long long)(int)sizeof(((struct mdt_rec_setxattr *)0)->sx_time));
+ LASSERTF((int)offsetof(struct mdt_rec_setxattr, sx_padding_5) == 88, "found %lld\n",
+ (long long)(int)offsetof(struct mdt_rec_setxattr, sx_padding_5));
+ LASSERTF((int)sizeof(((struct mdt_rec_setxattr *)0)->sx_padding_5) == 8, "found %lld\n",
+ (long long)(int)sizeof(((struct mdt_rec_setxattr *)0)->sx_padding_5));
+ LASSERTF((int)offsetof(struct mdt_rec_setxattr, sx_padding_6) == 96, "found %lld\n",
+ (long long)(int)offsetof(struct mdt_rec_setxattr, sx_padding_6));
+ LASSERTF((int)sizeof(((struct mdt_rec_setxattr *)0)->sx_padding_6) == 8, "found %lld\n",
+ (long long)(int)sizeof(((struct mdt_rec_setxattr *)0)->sx_padding_6));
+ LASSERTF((int)offsetof(struct mdt_rec_setxattr, sx_padding_7) == 104, "found %lld\n",
+ (long long)(int)offsetof(struct mdt_rec_setxattr, sx_padding_7));
+ LASSERTF((int)sizeof(((struct mdt_rec_setxattr *)0)->sx_padding_7) == 8, "found %lld\n",
+ (long long)(int)sizeof(((struct mdt_rec_setxattr *)0)->sx_padding_7));
+ LASSERTF((int)offsetof(struct mdt_rec_setxattr, sx_size) == 112, "found %lld\n",
+ (long long)(int)offsetof(struct mdt_rec_setxattr, sx_size));
+ LASSERTF((int)sizeof(((struct mdt_rec_setxattr *)0)->sx_size) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct mdt_rec_setxattr *)0)->sx_size));
+ LASSERTF((int)offsetof(struct mdt_rec_setxattr, sx_flags) == 116, "found %lld\n",
+ (long long)(int)offsetof(struct mdt_rec_setxattr, sx_flags));
+ LASSERTF((int)sizeof(((struct mdt_rec_setxattr *)0)->sx_flags) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct mdt_rec_setxattr *)0)->sx_flags));
+ LASSERTF((int)offsetof(struct mdt_rec_setxattr, sx_padding_8) == 120, "found %lld\n",
+ (long long)(int)offsetof(struct mdt_rec_setxattr, sx_padding_8));
+ LASSERTF((int)sizeof(((struct mdt_rec_setxattr *)0)->sx_padding_8) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct mdt_rec_setxattr *)0)->sx_padding_8));
+ LASSERTF((int)offsetof(struct mdt_rec_setxattr, sx_padding_9) == 124, "found %lld\n",
+ (long long)(int)offsetof(struct mdt_rec_setxattr, sx_padding_9));
+ LASSERTF((int)sizeof(((struct mdt_rec_setxattr *)0)->sx_padding_9) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct mdt_rec_setxattr *)0)->sx_padding_9));
+ LASSERTF((int)offsetof(struct mdt_rec_setxattr, sx_padding_10) == 128, "found %lld\n",
+ (long long)(int)offsetof(struct mdt_rec_setxattr, sx_padding_10));
+ LASSERTF((int)sizeof(((struct mdt_rec_setxattr *)0)->sx_padding_10) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct mdt_rec_setxattr *)0)->sx_padding_10));
+ LASSERTF((int)offsetof(struct mdt_rec_setxattr, sx_padding_11) == 132, "found %lld\n",
+ (long long)(int)offsetof(struct mdt_rec_setxattr, sx_padding_11));
+ LASSERTF((int)sizeof(((struct mdt_rec_setxattr *)0)->sx_padding_11) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct mdt_rec_setxattr *)0)->sx_padding_11));
+
+ /* Checks for struct mdt_rec_reint */
+ LASSERTF((int)sizeof(struct mdt_rec_reint) == 136, "found %lld\n",
+ (long long)(int)sizeof(struct mdt_rec_reint));
+ LASSERTF((int)offsetof(struct mdt_rec_reint, rr_opcode) == 0, "found %lld\n",
+ (long long)(int)offsetof(struct mdt_rec_reint, rr_opcode));
+ LASSERTF((int)sizeof(((struct mdt_rec_reint *)0)->rr_opcode) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct mdt_rec_reint *)0)->rr_opcode));
+ LASSERTF((int)offsetof(struct mdt_rec_reint, rr_cap) == 4, "found %lld\n",
+ (long long)(int)offsetof(struct mdt_rec_reint, rr_cap));
+ LASSERTF((int)sizeof(((struct mdt_rec_reint *)0)->rr_cap) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct mdt_rec_reint *)0)->rr_cap));
+ LASSERTF((int)offsetof(struct mdt_rec_reint, rr_fsuid) == 8, "found %lld\n",
+ (long long)(int)offsetof(struct mdt_rec_reint, rr_fsuid));
+ LASSERTF((int)sizeof(((struct mdt_rec_reint *)0)->rr_fsuid) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct mdt_rec_reint *)0)->rr_fsuid));
+ LASSERTF((int)offsetof(struct mdt_rec_reint, rr_fsuid_h) == 12, "found %lld\n",
+ (long long)(int)offsetof(struct mdt_rec_reint, rr_fsuid_h));
+ LASSERTF((int)sizeof(((struct mdt_rec_reint *)0)->rr_fsuid_h) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct mdt_rec_reint *)0)->rr_fsuid_h));
+ LASSERTF((int)offsetof(struct mdt_rec_reint, rr_fsgid) == 16, "found %lld\n",
+ (long long)(int)offsetof(struct mdt_rec_reint, rr_fsgid));
+ LASSERTF((int)sizeof(((struct mdt_rec_reint *)0)->rr_fsgid) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct mdt_rec_reint *)0)->rr_fsgid));
+ LASSERTF((int)offsetof(struct mdt_rec_reint, rr_fsgid_h) == 20, "found %lld\n",
+ (long long)(int)offsetof(struct mdt_rec_reint, rr_fsgid_h));
+ LASSERTF((int)sizeof(((struct mdt_rec_reint *)0)->rr_fsgid_h) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct mdt_rec_reint *)0)->rr_fsgid_h));
+ LASSERTF((int)offsetof(struct mdt_rec_reint, rr_suppgid1) == 24, "found %lld\n",
+ (long long)(int)offsetof(struct mdt_rec_reint, rr_suppgid1));
+ LASSERTF((int)sizeof(((struct mdt_rec_reint *)0)->rr_suppgid1) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct mdt_rec_reint *)0)->rr_suppgid1));
+ LASSERTF((int)offsetof(struct mdt_rec_reint, rr_suppgid1_h) == 28, "found %lld\n",
+ (long long)(int)offsetof(struct mdt_rec_reint, rr_suppgid1_h));
+ LASSERTF((int)sizeof(((struct mdt_rec_reint *)0)->rr_suppgid1_h) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct mdt_rec_reint *)0)->rr_suppgid1_h));
+ LASSERTF((int)offsetof(struct mdt_rec_reint, rr_suppgid2) == 32, "found %lld\n",
+ (long long)(int)offsetof(struct mdt_rec_reint, rr_suppgid2));
+ LASSERTF((int)sizeof(((struct mdt_rec_reint *)0)->rr_suppgid2) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct mdt_rec_reint *)0)->rr_suppgid2));
+ LASSERTF((int)offsetof(struct mdt_rec_reint, rr_suppgid2_h) == 36, "found %lld\n",
+ (long long)(int)offsetof(struct mdt_rec_reint, rr_suppgid2_h));
+ LASSERTF((int)sizeof(((struct mdt_rec_reint *)0)->rr_suppgid2_h) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct mdt_rec_reint *)0)->rr_suppgid2_h));
+ LASSERTF((int)offsetof(struct mdt_rec_reint, rr_fid1) == 40, "found %lld\n",
+ (long long)(int)offsetof(struct mdt_rec_reint, rr_fid1));
+ LASSERTF((int)sizeof(((struct mdt_rec_reint *)0)->rr_fid1) == 16, "found %lld\n",
+ (long long)(int)sizeof(((struct mdt_rec_reint *)0)->rr_fid1));
+ LASSERTF((int)offsetof(struct mdt_rec_reint, rr_fid2) == 56, "found %lld\n",
+ (long long)(int)offsetof(struct mdt_rec_reint, rr_fid2));
+ LASSERTF((int)sizeof(((struct mdt_rec_reint *)0)->rr_fid2) == 16, "found %lld\n",
+ (long long)(int)sizeof(((struct mdt_rec_reint *)0)->rr_fid2));
+ LASSERTF((int)offsetof(struct mdt_rec_reint, rr_mtime) == 72, "found %lld\n",
+ (long long)(int)offsetof(struct mdt_rec_reint, rr_mtime));
+ LASSERTF((int)sizeof(((struct mdt_rec_reint *)0)->rr_mtime) == 8, "found %lld\n",
+ (long long)(int)sizeof(((struct mdt_rec_reint *)0)->rr_mtime));
+ LASSERTF((int)offsetof(struct mdt_rec_reint, rr_atime) == 80, "found %lld\n",
+ (long long)(int)offsetof(struct mdt_rec_reint, rr_atime));
+ LASSERTF((int)sizeof(((struct mdt_rec_reint *)0)->rr_atime) == 8, "found %lld\n",
+ (long long)(int)sizeof(((struct mdt_rec_reint *)0)->rr_atime));
+ LASSERTF((int)offsetof(struct mdt_rec_reint, rr_ctime) == 88, "found %lld\n",
+ (long long)(int)offsetof(struct mdt_rec_reint, rr_ctime));
+ LASSERTF((int)sizeof(((struct mdt_rec_reint *)0)->rr_ctime) == 8, "found %lld\n",
+ (long long)(int)sizeof(((struct mdt_rec_reint *)0)->rr_ctime));
+ LASSERTF((int)offsetof(struct mdt_rec_reint, rr_size) == 96, "found %lld\n",
+ (long long)(int)offsetof(struct mdt_rec_reint, rr_size));
+ LASSERTF((int)sizeof(((struct mdt_rec_reint *)0)->rr_size) == 8, "found %lld\n",
+ (long long)(int)sizeof(((struct mdt_rec_reint *)0)->rr_size));
+ LASSERTF((int)offsetof(struct mdt_rec_reint, rr_blocks) == 104, "found %lld\n",
+ (long long)(int)offsetof(struct mdt_rec_reint, rr_blocks));
+ LASSERTF((int)sizeof(((struct mdt_rec_reint *)0)->rr_blocks) == 8, "found %lld\n",
+ (long long)(int)sizeof(((struct mdt_rec_reint *)0)->rr_blocks));
+ LASSERTF((int)offsetof(struct mdt_rec_reint, rr_bias) == 112, "found %lld\n",
+ (long long)(int)offsetof(struct mdt_rec_reint, rr_bias));
+ LASSERTF((int)sizeof(((struct mdt_rec_reint *)0)->rr_bias) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct mdt_rec_reint *)0)->rr_bias));
+ LASSERTF((int)offsetof(struct mdt_rec_reint, rr_mode) == 116, "found %lld\n",
+ (long long)(int)offsetof(struct mdt_rec_reint, rr_mode));
+ LASSERTF((int)sizeof(((struct mdt_rec_reint *)0)->rr_mode) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct mdt_rec_reint *)0)->rr_mode));
+ LASSERTF((int)offsetof(struct mdt_rec_reint, rr_flags) == 120, "found %lld\n",
+ (long long)(int)offsetof(struct mdt_rec_reint, rr_flags));
+ LASSERTF((int)sizeof(((struct mdt_rec_reint *)0)->rr_flags) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct mdt_rec_reint *)0)->rr_flags));
+ LASSERTF((int)offsetof(struct mdt_rec_reint, rr_flags_h) == 124, "found %lld\n",
+ (long long)(int)offsetof(struct mdt_rec_reint, rr_flags_h));
+ LASSERTF((int)sizeof(((struct mdt_rec_reint *)0)->rr_flags_h) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct mdt_rec_reint *)0)->rr_flags_h));
+ LASSERTF((int)offsetof(struct mdt_rec_reint, rr_umask) == 128, "found %lld\n",
+ (long long)(int)offsetof(struct mdt_rec_reint, rr_umask));
+ LASSERTF((int)sizeof(((struct mdt_rec_reint *)0)->rr_umask) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct mdt_rec_reint *)0)->rr_umask));
+ LASSERTF((int)offsetof(struct mdt_rec_reint, rr_padding_4) == 132, "found %lld\n",
+ (long long)(int)offsetof(struct mdt_rec_reint, rr_padding_4));
+ LASSERTF((int)sizeof(((struct mdt_rec_reint *)0)->rr_padding_4) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct mdt_rec_reint *)0)->rr_padding_4));
+
+ /* Checks for struct lmv_desc */
+ LASSERTF((int)sizeof(struct lmv_desc) == 88, "found %lld\n",
+ (long long)(int)sizeof(struct lmv_desc));
+ LASSERTF((int)offsetof(struct lmv_desc, ld_tgt_count) == 0, "found %lld\n",
+ (long long)(int)offsetof(struct lmv_desc, ld_tgt_count));
+ LASSERTF((int)sizeof(((struct lmv_desc *)0)->ld_tgt_count) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct lmv_desc *)0)->ld_tgt_count));
+ LASSERTF((int)offsetof(struct lmv_desc, ld_active_tgt_count) == 4, "found %lld\n",
+ (long long)(int)offsetof(struct lmv_desc, ld_active_tgt_count));
+ LASSERTF((int)sizeof(((struct lmv_desc *)0)->ld_active_tgt_count) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct lmv_desc *)0)->ld_active_tgt_count));
+ LASSERTF((int)offsetof(struct lmv_desc, ld_default_stripe_count) == 8, "found %lld\n",
+ (long long)(int)offsetof(struct lmv_desc, ld_default_stripe_count));
+ LASSERTF((int)sizeof(((struct lmv_desc *)0)->ld_default_stripe_count) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct lmv_desc *)0)->ld_default_stripe_count));
+ LASSERTF((int)offsetof(struct lmv_desc, ld_pattern) == 12, "found %lld\n",
+ (long long)(int)offsetof(struct lmv_desc, ld_pattern));
+ LASSERTF((int)sizeof(((struct lmv_desc *)0)->ld_pattern) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct lmv_desc *)0)->ld_pattern));
+ LASSERTF((int)offsetof(struct lmv_desc, ld_default_hash_size) == 16, "found %lld\n",
+ (long long)(int)offsetof(struct lmv_desc, ld_default_hash_size));
+ LASSERTF((int)sizeof(((struct lmv_desc *)0)->ld_default_hash_size) == 8, "found %lld\n",
+ (long long)(int)sizeof(((struct lmv_desc *)0)->ld_default_hash_size));
+ LASSERTF((int)offsetof(struct lmv_desc, ld_padding_1) == 24, "found %lld\n",
+ (long long)(int)offsetof(struct lmv_desc, ld_padding_1));
+ LASSERTF((int)sizeof(((struct lmv_desc *)0)->ld_padding_1) == 8, "found %lld\n",
+ (long long)(int)sizeof(((struct lmv_desc *)0)->ld_padding_1));
+ LASSERTF((int)offsetof(struct lmv_desc, ld_padding_2) == 32, "found %lld\n",
+ (long long)(int)offsetof(struct lmv_desc, ld_padding_2));
+ LASSERTF((int)sizeof(((struct lmv_desc *)0)->ld_padding_2) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct lmv_desc *)0)->ld_padding_2));
+ LASSERTF((int)offsetof(struct lmv_desc, ld_qos_maxage) == 36, "found %lld\n",
+ (long long)(int)offsetof(struct lmv_desc, ld_qos_maxage));
+ LASSERTF((int)sizeof(((struct lmv_desc *)0)->ld_qos_maxage) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct lmv_desc *)0)->ld_qos_maxage));
+ LASSERTF((int)offsetof(struct lmv_desc, ld_padding_3) == 40, "found %lld\n",
+ (long long)(int)offsetof(struct lmv_desc, ld_padding_3));
+ LASSERTF((int)sizeof(((struct lmv_desc *)0)->ld_padding_3) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct lmv_desc *)0)->ld_padding_3));
+ LASSERTF((int)offsetof(struct lmv_desc, ld_padding_4) == 44, "found %lld\n",
+ (long long)(int)offsetof(struct lmv_desc, ld_padding_4));
+ LASSERTF((int)sizeof(((struct lmv_desc *)0)->ld_padding_4) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct lmv_desc *)0)->ld_padding_4));
+ LASSERTF((int)offsetof(struct lmv_desc, ld_uuid) == 48, "found %lld\n",
+ (long long)(int)offsetof(struct lmv_desc, ld_uuid));
+ LASSERTF((int)sizeof(((struct lmv_desc *)0)->ld_uuid) == 40, "found %lld\n",
+ (long long)(int)sizeof(((struct lmv_desc *)0)->ld_uuid));
+
+ /* Checks for struct lmv_stripe_md */
+ LASSERTF((int)sizeof(struct lmv_stripe_md) == 32, "found %lld\n",
+ (long long)(int)sizeof(struct lmv_stripe_md));
+ LASSERTF((int)offsetof(struct lmv_stripe_md, mea_magic) == 0, "found %lld\n",
+ (long long)(int)offsetof(struct lmv_stripe_md, mea_magic));
+ LASSERTF((int)sizeof(((struct lmv_stripe_md *)0)->mea_magic) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct lmv_stripe_md *)0)->mea_magic));
+ LASSERTF((int)offsetof(struct lmv_stripe_md, mea_count) == 4, "found %lld\n",
+ (long long)(int)offsetof(struct lmv_stripe_md, mea_count));
+ LASSERTF((int)sizeof(((struct lmv_stripe_md *)0)->mea_count) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct lmv_stripe_md *)0)->mea_count));
+ LASSERTF((int)offsetof(struct lmv_stripe_md, mea_master) == 8, "found %lld\n",
+ (long long)(int)offsetof(struct lmv_stripe_md, mea_master));
+ LASSERTF((int)sizeof(((struct lmv_stripe_md *)0)->mea_master) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct lmv_stripe_md *)0)->mea_master));
+ LASSERTF((int)offsetof(struct lmv_stripe_md, mea_padding) == 12, "found %lld\n",
+ (long long)(int)offsetof(struct lmv_stripe_md, mea_padding));
+ LASSERTF((int)sizeof(((struct lmv_stripe_md *)0)->mea_padding) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct lmv_stripe_md *)0)->mea_padding));
+ CLASSERT(LOV_MAXPOOLNAME == 16);
+ LASSERTF((int)offsetof(struct lmv_stripe_md, mea_pool_name[16]) == 32, "found %lld\n",
+ (long long)(int)offsetof(struct lmv_stripe_md, mea_pool_name[16]));
+ LASSERTF((int)sizeof(((struct lmv_stripe_md *)0)->mea_pool_name[16]) == 1, "found %lld\n",
+ (long long)(int)sizeof(((struct lmv_stripe_md *)0)->mea_pool_name[16]));
+ LASSERTF((int)offsetof(struct lmv_stripe_md, mea_ids[0]) == 32, "found %lld\n",
+ (long long)(int)offsetof(struct lmv_stripe_md, mea_ids[0]));
+ LASSERTF((int)sizeof(((struct lmv_stripe_md *)0)->mea_ids[0]) == 16, "found %lld\n",
+ (long long)(int)sizeof(((struct lmv_stripe_md *)0)->mea_ids[0]));
+
+ /* Checks for struct lov_desc */
+ LASSERTF((int)sizeof(struct lov_desc) == 88, "found %lld\n",
+ (long long)(int)sizeof(struct lov_desc));
+ LASSERTF((int)offsetof(struct lov_desc, ld_tgt_count) == 0, "found %lld\n",
+ (long long)(int)offsetof(struct lov_desc, ld_tgt_count));
+ LASSERTF((int)sizeof(((struct lov_desc *)0)->ld_tgt_count) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct lov_desc *)0)->ld_tgt_count));
+ LASSERTF((int)offsetof(struct lov_desc, ld_active_tgt_count) == 4, "found %lld\n",
+ (long long)(int)offsetof(struct lov_desc, ld_active_tgt_count));
+ LASSERTF((int)sizeof(((struct lov_desc *)0)->ld_active_tgt_count) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct lov_desc *)0)->ld_active_tgt_count));
+ LASSERTF((int)offsetof(struct lov_desc, ld_default_stripe_count) == 8, "found %lld\n",
+ (long long)(int)offsetof(struct lov_desc, ld_default_stripe_count));
+ LASSERTF((int)sizeof(((struct lov_desc *)0)->ld_default_stripe_count) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct lov_desc *)0)->ld_default_stripe_count));
+ LASSERTF((int)offsetof(struct lov_desc, ld_pattern) == 12, "found %lld\n",
+ (long long)(int)offsetof(struct lov_desc, ld_pattern));
+ LASSERTF((int)sizeof(((struct lov_desc *)0)->ld_pattern) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct lov_desc *)0)->ld_pattern));
+ LASSERTF((int)offsetof(struct lov_desc, ld_default_stripe_size) == 16, "found %lld\n",
+ (long long)(int)offsetof(struct lov_desc, ld_default_stripe_size));
+ LASSERTF((int)sizeof(((struct lov_desc *)0)->ld_default_stripe_size) == 8, "found %lld\n",
+ (long long)(int)sizeof(((struct lov_desc *)0)->ld_default_stripe_size));
+ LASSERTF((int)offsetof(struct lov_desc, ld_default_stripe_offset) == 24, "found %lld\n",
+ (long long)(int)offsetof(struct lov_desc, ld_default_stripe_offset));
+ LASSERTF((int)sizeof(((struct lov_desc *)0)->ld_default_stripe_offset) == 8, "found %lld\n",
+ (long long)(int)sizeof(((struct lov_desc *)0)->ld_default_stripe_offset));
+ LASSERTF((int)offsetof(struct lov_desc, ld_padding_0) == 32, "found %lld\n",
+ (long long)(int)offsetof(struct lov_desc, ld_padding_0));
+ LASSERTF((int)sizeof(((struct lov_desc *)0)->ld_padding_0) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct lov_desc *)0)->ld_padding_0));
+ LASSERTF((int)offsetof(struct lov_desc, ld_qos_maxage) == 36, "found %lld\n",
+ (long long)(int)offsetof(struct lov_desc, ld_qos_maxage));
+ LASSERTF((int)sizeof(((struct lov_desc *)0)->ld_qos_maxage) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct lov_desc *)0)->ld_qos_maxage));
+ LASSERTF((int)offsetof(struct lov_desc, ld_padding_1) == 40, "found %lld\n",
+ (long long)(int)offsetof(struct lov_desc, ld_padding_1));
+ LASSERTF((int)sizeof(((struct lov_desc *)0)->ld_padding_1) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct lov_desc *)0)->ld_padding_1));
+ LASSERTF((int)offsetof(struct lov_desc, ld_padding_2) == 44, "found %lld\n",
+ (long long)(int)offsetof(struct lov_desc, ld_padding_2));
+ LASSERTF((int)sizeof(((struct lov_desc *)0)->ld_padding_2) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct lov_desc *)0)->ld_padding_2));
+ LASSERTF((int)offsetof(struct lov_desc, ld_uuid) == 48, "found %lld\n",
+ (long long)(int)offsetof(struct lov_desc, ld_uuid));
+ LASSERTF((int)sizeof(((struct lov_desc *)0)->ld_uuid) == 40, "found %lld\n",
+ (long long)(int)sizeof(((struct lov_desc *)0)->ld_uuid));
+ CLASSERT(LOV_DESC_MAGIC == 0xB0CCDE5C);
+
+ /* Checks for struct ldlm_res_id */
+ LASSERTF((int)sizeof(struct ldlm_res_id) == 32, "found %lld\n",
+ (long long)(int)sizeof(struct ldlm_res_id));
+ CLASSERT(RES_NAME_SIZE == 4);
+ LASSERTF((int)offsetof(struct ldlm_res_id, name[4]) == 32, "found %lld\n",
+ (long long)(int)offsetof(struct ldlm_res_id, name[4]));
+ LASSERTF((int)sizeof(((struct ldlm_res_id *)0)->name[4]) == 8, "found %lld\n",
+ (long long)(int)sizeof(((struct ldlm_res_id *)0)->name[4]));
+
+ /* Checks for struct ldlm_extent */
+ LASSERTF((int)sizeof(struct ldlm_extent) == 24, "found %lld\n",
+ (long long)(int)sizeof(struct ldlm_extent));
+ LASSERTF((int)offsetof(struct ldlm_extent, start) == 0, "found %lld\n",
+ (long long)(int)offsetof(struct ldlm_extent, start));
+ LASSERTF((int)sizeof(((struct ldlm_extent *)0)->start) == 8, "found %lld\n",
+ (long long)(int)sizeof(((struct ldlm_extent *)0)->start));
+ LASSERTF((int)offsetof(struct ldlm_extent, end) == 8, "found %lld\n",
+ (long long)(int)offsetof(struct ldlm_extent, end));
+ LASSERTF((int)sizeof(((struct ldlm_extent *)0)->end) == 8, "found %lld\n",
+ (long long)(int)sizeof(((struct ldlm_extent *)0)->end));
+ LASSERTF((int)offsetof(struct ldlm_extent, gid) == 16, "found %lld\n",
+ (long long)(int)offsetof(struct ldlm_extent, gid));
+ LASSERTF((int)sizeof(((struct ldlm_extent *)0)->gid) == 8, "found %lld\n",
+ (long long)(int)sizeof(((struct ldlm_extent *)0)->gid));
+
+ /* Checks for struct ldlm_inodebits */
+ LASSERTF((int)sizeof(struct ldlm_inodebits) == 8, "found %lld\n",
+ (long long)(int)sizeof(struct ldlm_inodebits));
+ LASSERTF((int)offsetof(struct ldlm_inodebits, bits) == 0, "found %lld\n",
+ (long long)(int)offsetof(struct ldlm_inodebits, bits));
+ LASSERTF((int)sizeof(((struct ldlm_inodebits *)0)->bits) == 8, "found %lld\n",
+ (long long)(int)sizeof(((struct ldlm_inodebits *)0)->bits));
+
+ /* Checks for struct ldlm_flock_wire */
+ LASSERTF((int)sizeof(struct ldlm_flock_wire) == 32, "found %lld\n",
+ (long long)(int)sizeof(struct ldlm_flock_wire));
+ LASSERTF((int)offsetof(struct ldlm_flock_wire, lfw_start) == 0, "found %lld\n",
+ (long long)(int)offsetof(struct ldlm_flock_wire, lfw_start));
+ LASSERTF((int)sizeof(((struct ldlm_flock_wire *)0)->lfw_start) == 8, "found %lld\n",
+ (long long)(int)sizeof(((struct ldlm_flock_wire *)0)->lfw_start));
+ LASSERTF((int)offsetof(struct ldlm_flock_wire, lfw_end) == 8, "found %lld\n",
+ (long long)(int)offsetof(struct ldlm_flock_wire, lfw_end));
+ LASSERTF((int)sizeof(((struct ldlm_flock_wire *)0)->lfw_end) == 8, "found %lld\n",
+ (long long)(int)sizeof(((struct ldlm_flock_wire *)0)->lfw_end));
+ LASSERTF((int)offsetof(struct ldlm_flock_wire, lfw_owner) == 16, "found %lld\n",
+ (long long)(int)offsetof(struct ldlm_flock_wire, lfw_owner));
+ LASSERTF((int)sizeof(((struct ldlm_flock_wire *)0)->lfw_owner) == 8, "found %lld\n",
+ (long long)(int)sizeof(((struct ldlm_flock_wire *)0)->lfw_owner));
+ LASSERTF((int)offsetof(struct ldlm_flock_wire, lfw_padding) == 24, "found %lld\n",
+ (long long)(int)offsetof(struct ldlm_flock_wire, lfw_padding));
+ LASSERTF((int)sizeof(((struct ldlm_flock_wire *)0)->lfw_padding) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct ldlm_flock_wire *)0)->lfw_padding));
+ LASSERTF((int)offsetof(struct ldlm_flock_wire, lfw_pid) == 28, "found %lld\n",
+ (long long)(int)offsetof(struct ldlm_flock_wire, lfw_pid));
+ LASSERTF((int)sizeof(((struct ldlm_flock_wire *)0)->lfw_pid) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct ldlm_flock_wire *)0)->lfw_pid));
+
+ /* Checks for struct ldlm_intent */
+ LASSERTF((int)sizeof(struct ldlm_intent) == 8, "found %lld\n",
+ (long long)(int)sizeof(struct ldlm_intent));
+ LASSERTF((int)offsetof(struct ldlm_intent, opc) == 0, "found %lld\n",
+ (long long)(int)offsetof(struct ldlm_intent, opc));
+ LASSERTF((int)sizeof(((struct ldlm_intent *)0)->opc) == 8, "found %lld\n",
+ (long long)(int)sizeof(((struct ldlm_intent *)0)->opc));
+
+ /* Checks for struct ldlm_resource_desc */
+ LASSERTF((int)sizeof(struct ldlm_resource_desc) == 40, "found %lld\n",
+ (long long)(int)sizeof(struct ldlm_resource_desc));
+ LASSERTF((int)offsetof(struct ldlm_resource_desc, lr_type) == 0, "found %lld\n",
+ (long long)(int)offsetof(struct ldlm_resource_desc, lr_type));
+ LASSERTF((int)sizeof(((struct ldlm_resource_desc *)0)->lr_type) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct ldlm_resource_desc *)0)->lr_type));
+ LASSERTF((int)offsetof(struct ldlm_resource_desc, lr_padding) == 4, "found %lld\n",
+ (long long)(int)offsetof(struct ldlm_resource_desc, lr_padding));
+ LASSERTF((int)sizeof(((struct ldlm_resource_desc *)0)->lr_padding) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct ldlm_resource_desc *)0)->lr_padding));
+ LASSERTF((int)offsetof(struct ldlm_resource_desc, lr_name) == 8, "found %lld\n",
+ (long long)(int)offsetof(struct ldlm_resource_desc, lr_name));
+ LASSERTF((int)sizeof(((struct ldlm_resource_desc *)0)->lr_name) == 32, "found %lld\n",
+ (long long)(int)sizeof(((struct ldlm_resource_desc *)0)->lr_name));
+
+ /* Checks for struct ldlm_lock_desc */
+ LASSERTF((int)sizeof(struct ldlm_lock_desc) == 80, "found %lld\n",
+ (long long)(int)sizeof(struct ldlm_lock_desc));
+ LASSERTF((int)offsetof(struct ldlm_lock_desc, l_resource) == 0, "found %lld\n",
+ (long long)(int)offsetof(struct ldlm_lock_desc, l_resource));
+ LASSERTF((int)sizeof(((struct ldlm_lock_desc *)0)->l_resource) == 40, "found %lld\n",
+ (long long)(int)sizeof(((struct ldlm_lock_desc *)0)->l_resource));
+ LASSERTF((int)offsetof(struct ldlm_lock_desc, l_req_mode) == 40, "found %lld\n",
+ (long long)(int)offsetof(struct ldlm_lock_desc, l_req_mode));
+ LASSERTF((int)sizeof(((struct ldlm_lock_desc *)0)->l_req_mode) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct ldlm_lock_desc *)0)->l_req_mode));
+ LASSERTF((int)offsetof(struct ldlm_lock_desc, l_granted_mode) == 44, "found %lld\n",
+ (long long)(int)offsetof(struct ldlm_lock_desc, l_granted_mode));
+ LASSERTF((int)sizeof(((struct ldlm_lock_desc *)0)->l_granted_mode) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct ldlm_lock_desc *)0)->l_granted_mode));
+ LASSERTF((int)offsetof(struct ldlm_lock_desc, l_policy_data) == 48, "found %lld\n",
+ (long long)(int)offsetof(struct ldlm_lock_desc, l_policy_data));
+ LASSERTF((int)sizeof(((struct ldlm_lock_desc *)0)->l_policy_data) == 32, "found %lld\n",
+ (long long)(int)sizeof(((struct ldlm_lock_desc *)0)->l_policy_data));
+
+ /* Checks for struct ldlm_request */
+ LASSERTF((int)sizeof(struct ldlm_request) == 104, "found %lld\n",
+ (long long)(int)sizeof(struct ldlm_request));
+ LASSERTF((int)offsetof(struct ldlm_request, lock_flags) == 0, "found %lld\n",
+ (long long)(int)offsetof(struct ldlm_request, lock_flags));
+ LASSERTF((int)sizeof(((struct ldlm_request *)0)->lock_flags) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct ldlm_request *)0)->lock_flags));
+ LASSERTF((int)offsetof(struct ldlm_request, lock_count) == 4, "found %lld\n",
+ (long long)(int)offsetof(struct ldlm_request, lock_count));
+ LASSERTF((int)sizeof(((struct ldlm_request *)0)->lock_count) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct ldlm_request *)0)->lock_count));
+ LASSERTF((int)offsetof(struct ldlm_request, lock_desc) == 8, "found %lld\n",
+ (long long)(int)offsetof(struct ldlm_request, lock_desc));
+ LASSERTF((int)sizeof(((struct ldlm_request *)0)->lock_desc) == 80, "found %lld\n",
+ (long long)(int)sizeof(((struct ldlm_request *)0)->lock_desc));
+ LASSERTF((int)offsetof(struct ldlm_request, lock_handle) == 88, "found %lld\n",
+ (long long)(int)offsetof(struct ldlm_request, lock_handle));
+ LASSERTF((int)sizeof(((struct ldlm_request *)0)->lock_handle) == 16, "found %lld\n",
+ (long long)(int)sizeof(((struct ldlm_request *)0)->lock_handle));
+
+ /* Checks for struct ldlm_reply */
+ LASSERTF((int)sizeof(struct ldlm_reply) == 112, "found %lld\n",
+ (long long)(int)sizeof(struct ldlm_reply));
+ LASSERTF((int)offsetof(struct ldlm_reply, lock_flags) == 0, "found %lld\n",
+ (long long)(int)offsetof(struct ldlm_reply, lock_flags));
+ LASSERTF((int)sizeof(((struct ldlm_reply *)0)->lock_flags) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct ldlm_reply *)0)->lock_flags));
+ LASSERTF((int)offsetof(struct ldlm_reply, lock_padding) == 4, "found %lld\n",
+ (long long)(int)offsetof(struct ldlm_reply, lock_padding));
+ LASSERTF((int)sizeof(((struct ldlm_reply *)0)->lock_padding) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct ldlm_reply *)0)->lock_padding));
+ LASSERTF((int)offsetof(struct ldlm_reply, lock_desc) == 8, "found %lld\n",
+ (long long)(int)offsetof(struct ldlm_reply, lock_desc));
+ LASSERTF((int)sizeof(((struct ldlm_reply *)0)->lock_desc) == 80, "found %lld\n",
+ (long long)(int)sizeof(((struct ldlm_reply *)0)->lock_desc));
+ LASSERTF((int)offsetof(struct ldlm_reply, lock_handle) == 88, "found %lld\n",
+ (long long)(int)offsetof(struct ldlm_reply, lock_handle));
+ LASSERTF((int)sizeof(((struct ldlm_reply *)0)->lock_handle) == 8, "found %lld\n",
+ (long long)(int)sizeof(((struct ldlm_reply *)0)->lock_handle));
+ LASSERTF((int)offsetof(struct ldlm_reply, lock_policy_res1) == 96, "found %lld\n",
+ (long long)(int)offsetof(struct ldlm_reply, lock_policy_res1));
+ LASSERTF((int)sizeof(((struct ldlm_reply *)0)->lock_policy_res1) == 8, "found %lld\n",
+ (long long)(int)sizeof(((struct ldlm_reply *)0)->lock_policy_res1));
+ LASSERTF((int)offsetof(struct ldlm_reply, lock_policy_res2) == 104, "found %lld\n",
+ (long long)(int)offsetof(struct ldlm_reply, lock_policy_res2));
+ LASSERTF((int)sizeof(((struct ldlm_reply *)0)->lock_policy_res2) == 8, "found %lld\n",
+ (long long)(int)sizeof(((struct ldlm_reply *)0)->lock_policy_res2));
+
+ /* Checks for struct ost_lvb_v1 */
+ LASSERTF((int)sizeof(struct ost_lvb_v1) == 40, "found %lld\n",
+ (long long)(int)sizeof(struct ost_lvb_v1));
+ LASSERTF((int)offsetof(struct ost_lvb_v1, lvb_size) == 0, "found %lld\n",
+ (long long)(int)offsetof(struct ost_lvb_v1, lvb_size));
+ LASSERTF((int)sizeof(((struct ost_lvb_v1 *)0)->lvb_size) == 8, "found %lld\n",
+ (long long)(int)sizeof(((struct ost_lvb_v1 *)0)->lvb_size));
+ LASSERTF((int)offsetof(struct ost_lvb_v1, lvb_mtime) == 8, "found %lld\n",
+ (long long)(int)offsetof(struct ost_lvb_v1, lvb_mtime));
+ LASSERTF((int)sizeof(((struct ost_lvb_v1 *)0)->lvb_mtime) == 8, "found %lld\n",
+ (long long)(int)sizeof(((struct ost_lvb_v1 *)0)->lvb_mtime));
+ LASSERTF((int)offsetof(struct ost_lvb_v1, lvb_atime) == 16, "found %lld\n",
+ (long long)(int)offsetof(struct ost_lvb_v1, lvb_atime));
+ LASSERTF((int)sizeof(((struct ost_lvb_v1 *)0)->lvb_atime) == 8, "found %lld\n",
+ (long long)(int)sizeof(((struct ost_lvb_v1 *)0)->lvb_atime));
+ LASSERTF((int)offsetof(struct ost_lvb_v1, lvb_ctime) == 24, "found %lld\n",
+ (long long)(int)offsetof(struct ost_lvb_v1, lvb_ctime));
+ LASSERTF((int)sizeof(((struct ost_lvb_v1 *)0)->lvb_ctime) == 8, "found %lld\n",
+ (long long)(int)sizeof(((struct ost_lvb_v1 *)0)->lvb_ctime));
+ LASSERTF((int)offsetof(struct ost_lvb_v1, lvb_blocks) == 32, "found %lld\n",
+ (long long)(int)offsetof(struct ost_lvb_v1, lvb_blocks));
+ LASSERTF((int)sizeof(((struct ost_lvb_v1 *)0)->lvb_blocks) == 8, "found %lld\n",
+ (long long)(int)sizeof(((struct ost_lvb_v1 *)0)->lvb_blocks));
+
+ /* Checks for struct ost_lvb */
+ LASSERTF((int)sizeof(struct ost_lvb) == 56, "found %lld\n",
+ (long long)(int)sizeof(struct ost_lvb));
+ LASSERTF((int)offsetof(struct ost_lvb, lvb_size) == 0, "found %lld\n",
+ (long long)(int)offsetof(struct ost_lvb, lvb_size));
+ LASSERTF((int)sizeof(((struct ost_lvb *)0)->lvb_size) == 8, "found %lld\n",
+ (long long)(int)sizeof(((struct ost_lvb *)0)->lvb_size));
+ LASSERTF((int)offsetof(struct ost_lvb, lvb_mtime) == 8, "found %lld\n",
+ (long long)(int)offsetof(struct ost_lvb, lvb_mtime));
+ LASSERTF((int)sizeof(((struct ost_lvb *)0)->lvb_mtime) == 8, "found %lld\n",
+ (long long)(int)sizeof(((struct ost_lvb *)0)->lvb_mtime));
+ LASSERTF((int)offsetof(struct ost_lvb, lvb_atime) == 16, "found %lld\n",
+ (long long)(int)offsetof(struct ost_lvb, lvb_atime));
+ LASSERTF((int)sizeof(((struct ost_lvb *)0)->lvb_atime) == 8, "found %lld\n",
+ (long long)(int)sizeof(((struct ost_lvb *)0)->lvb_atime));
+ LASSERTF((int)offsetof(struct ost_lvb, lvb_ctime) == 24, "found %lld\n",
+ (long long)(int)offsetof(struct ost_lvb, lvb_ctime));
+ LASSERTF((int)sizeof(((struct ost_lvb *)0)->lvb_ctime) == 8, "found %lld\n",
+ (long long)(int)sizeof(((struct ost_lvb *)0)->lvb_ctime));
+ LASSERTF((int)offsetof(struct ost_lvb, lvb_blocks) == 32, "found %lld\n",
+ (long long)(int)offsetof(struct ost_lvb, lvb_blocks));
+ LASSERTF((int)sizeof(((struct ost_lvb *)0)->lvb_blocks) == 8, "found %lld\n",
+ (long long)(int)sizeof(((struct ost_lvb *)0)->lvb_blocks));
+ LASSERTF((int)offsetof(struct ost_lvb, lvb_mtime_ns) == 40, "found %lld\n",
+ (long long)(int)offsetof(struct ost_lvb, lvb_mtime_ns));
+ LASSERTF((int)sizeof(((struct ost_lvb *)0)->lvb_mtime_ns) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct ost_lvb *)0)->lvb_mtime_ns));
+ LASSERTF((int)offsetof(struct ost_lvb, lvb_atime_ns) == 44, "found %lld\n",
+ (long long)(int)offsetof(struct ost_lvb, lvb_atime_ns));
+ LASSERTF((int)sizeof(((struct ost_lvb *)0)->lvb_atime_ns) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct ost_lvb *)0)->lvb_atime_ns));
+ LASSERTF((int)offsetof(struct ost_lvb, lvb_ctime_ns) == 48, "found %lld\n",
+ (long long)(int)offsetof(struct ost_lvb, lvb_ctime_ns));
+ LASSERTF((int)sizeof(((struct ost_lvb *)0)->lvb_ctime_ns) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct ost_lvb *)0)->lvb_ctime_ns));
+ LASSERTF((int)offsetof(struct ost_lvb, lvb_padding) == 52, "found %lld\n",
+ (long long)(int)offsetof(struct ost_lvb, lvb_padding));
+ LASSERTF((int)sizeof(((struct ost_lvb *)0)->lvb_padding) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct ost_lvb *)0)->lvb_padding));
+
+ /* Checks for struct lquota_lvb */
+ LASSERTF((int)sizeof(struct lquota_lvb) == 40, "found %lld\n",
+ (long long)(int)sizeof(struct lquota_lvb));
+ LASSERTF((int)offsetof(struct lquota_lvb, lvb_flags) == 0, "found %lld\n",
+ (long long)(int)offsetof(struct lquota_lvb, lvb_flags));
+ LASSERTF((int)sizeof(((struct lquota_lvb *)0)->lvb_flags) == 8, "found %lld\n",
+ (long long)(int)sizeof(((struct lquota_lvb *)0)->lvb_flags));
+ LASSERTF((int)offsetof(struct lquota_lvb, lvb_id_may_rel) == 8, "found %lld\n",
+ (long long)(int)offsetof(struct lquota_lvb, lvb_id_may_rel));
+ LASSERTF((int)sizeof(((struct lquota_lvb *)0)->lvb_id_may_rel) == 8, "found %lld\n",
+ (long long)(int)sizeof(((struct lquota_lvb *)0)->lvb_id_may_rel));
+ LASSERTF((int)offsetof(struct lquota_lvb, lvb_id_rel) == 16, "found %lld\n",
+ (long long)(int)offsetof(struct lquota_lvb, lvb_id_rel));
+ LASSERTF((int)sizeof(((struct lquota_lvb *)0)->lvb_id_rel) == 8, "found %lld\n",
+ (long long)(int)sizeof(((struct lquota_lvb *)0)->lvb_id_rel));
+ LASSERTF((int)offsetof(struct lquota_lvb, lvb_id_qunit) == 24, "found %lld\n",
+ (long long)(int)offsetof(struct lquota_lvb, lvb_id_qunit));
+ LASSERTF((int)sizeof(((struct lquota_lvb *)0)->lvb_id_qunit) == 8, "found %lld\n",
+ (long long)(int)sizeof(((struct lquota_lvb *)0)->lvb_id_qunit));
+ LASSERTF((int)offsetof(struct lquota_lvb, lvb_pad1) == 32, "found %lld\n",
+ (long long)(int)offsetof(struct lquota_lvb, lvb_pad1));
+ LASSERTF((int)sizeof(((struct lquota_lvb *)0)->lvb_pad1) == 8, "found %lld\n",
+ (long long)(int)sizeof(((struct lquota_lvb *)0)->lvb_pad1));
+ LASSERTF(LQUOTA_FL_EDQUOT == 1, "found %lld\n",
+ (long long)LQUOTA_FL_EDQUOT);
+
+ /* Checks for struct ldlm_gl_lquota_desc */
+ LASSERTF((int)sizeof(struct ldlm_gl_lquota_desc) == 64, "found %lld\n",
+ (long long)(int)sizeof(struct ldlm_gl_lquota_desc));
+ LASSERTF((int)offsetof(struct ldlm_gl_lquota_desc, gl_id) == 0, "found %lld\n",
+ (long long)(int)offsetof(struct ldlm_gl_lquota_desc, gl_id));
+ LASSERTF((int)sizeof(((struct ldlm_gl_lquota_desc *)0)->gl_id) == 16, "found %lld\n",
+ (long long)(int)sizeof(((struct ldlm_gl_lquota_desc *)0)->gl_id));
+ LASSERTF((int)offsetof(struct ldlm_gl_lquota_desc, gl_flags) == 16, "found %lld\n",
+ (long long)(int)offsetof(struct ldlm_gl_lquota_desc, gl_flags));
+ LASSERTF((int)sizeof(((struct ldlm_gl_lquota_desc *)0)->gl_flags) == 8, "found %lld\n",
+ (long long)(int)sizeof(((struct ldlm_gl_lquota_desc *)0)->gl_flags));
+ LASSERTF((int)offsetof(struct ldlm_gl_lquota_desc, gl_ver) == 24, "found %lld\n",
+ (long long)(int)offsetof(struct ldlm_gl_lquota_desc, gl_ver));
+ LASSERTF((int)sizeof(((struct ldlm_gl_lquota_desc *)0)->gl_ver) == 8, "found %lld\n",
+ (long long)(int)sizeof(((struct ldlm_gl_lquota_desc *)0)->gl_ver));
+ LASSERTF((int)offsetof(struct ldlm_gl_lquota_desc, gl_hardlimit) == 32, "found %lld\n",
+ (long long)(int)offsetof(struct ldlm_gl_lquota_desc, gl_hardlimit));
+ LASSERTF((int)sizeof(((struct ldlm_gl_lquota_desc *)0)->gl_hardlimit) == 8, "found %lld\n",
+ (long long)(int)sizeof(((struct ldlm_gl_lquota_desc *)0)->gl_hardlimit));
+ LASSERTF((int)offsetof(struct ldlm_gl_lquota_desc, gl_softlimit) == 40, "found %lld\n",
+ (long long)(int)offsetof(struct ldlm_gl_lquota_desc, gl_softlimit));
+ LASSERTF((int)sizeof(((struct ldlm_gl_lquota_desc *)0)->gl_softlimit) == 8, "found %lld\n",
+ (long long)(int)sizeof(((struct ldlm_gl_lquota_desc *)0)->gl_softlimit));
+ LASSERTF((int)offsetof(struct ldlm_gl_lquota_desc, gl_time) == 48, "found %lld\n",
+ (long long)(int)offsetof(struct ldlm_gl_lquota_desc, gl_time));
+ LASSERTF((int)sizeof(((struct ldlm_gl_lquota_desc *)0)->gl_time) == 8, "found %lld\n",
+ (long long)(int)sizeof(((struct ldlm_gl_lquota_desc *)0)->gl_time));
+ LASSERTF((int)offsetof(struct ldlm_gl_lquota_desc, gl_pad2) == 56, "found %lld\n",
+ (long long)(int)offsetof(struct ldlm_gl_lquota_desc, gl_pad2));
+ LASSERTF((int)sizeof(((struct ldlm_gl_lquota_desc *)0)->gl_pad2) == 8, "found %lld\n",
+ (long long)(int)sizeof(((struct ldlm_gl_lquota_desc *)0)->gl_pad2));
+
+ /* Checks for struct mgs_send_param */
+ LASSERTF((int)sizeof(struct mgs_send_param) == 1024, "found %lld\n",
+ (long long)(int)sizeof(struct mgs_send_param));
+ CLASSERT(MGS_PARAM_MAXLEN == 1024);
+ LASSERTF((int)offsetof(struct mgs_send_param, mgs_param[1024]) == 1024, "found %lld\n",
+ (long long)(int)offsetof(struct mgs_send_param, mgs_param[1024]));
+ LASSERTF((int)sizeof(((struct mgs_send_param *)0)->mgs_param[1024]) == 1, "found %lld\n",
+ (long long)(int)sizeof(((struct mgs_send_param *)0)->mgs_param[1024]));
+
+ /* Checks for struct cfg_marker */
+ LASSERTF((int)sizeof(struct cfg_marker) == 160, "found %lld\n",
+ (long long)(int)sizeof(struct cfg_marker));
+ LASSERTF((int)offsetof(struct cfg_marker, cm_step) == 0, "found %lld\n",
+ (long long)(int)offsetof(struct cfg_marker, cm_step));
+ LASSERTF((int)sizeof(((struct cfg_marker *)0)->cm_step) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct cfg_marker *)0)->cm_step));
+ LASSERTF((int)offsetof(struct cfg_marker, cm_flags) == 4, "found %lld\n",
+ (long long)(int)offsetof(struct cfg_marker, cm_flags));
+ LASSERTF((int)sizeof(((struct cfg_marker *)0)->cm_flags) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct cfg_marker *)0)->cm_flags));
+ LASSERTF((int)offsetof(struct cfg_marker, cm_vers) == 8, "found %lld\n",
+ (long long)(int)offsetof(struct cfg_marker, cm_vers));
+ LASSERTF((int)sizeof(((struct cfg_marker *)0)->cm_vers) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct cfg_marker *)0)->cm_vers));
+ LASSERTF((int)offsetof(struct cfg_marker, cm_padding) == 12, "found %lld\n",
+ (long long)(int)offsetof(struct cfg_marker, cm_padding));
+ LASSERTF((int)sizeof(((struct cfg_marker *)0)->cm_padding) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct cfg_marker *)0)->cm_padding));
+ LASSERTF((int)offsetof(struct cfg_marker, cm_createtime) == 16, "found %lld\n",
+ (long long)(int)offsetof(struct cfg_marker, cm_createtime));
+ LASSERTF((int)sizeof(((struct cfg_marker *)0)->cm_createtime) == 8, "found %lld\n",
+ (long long)(int)sizeof(((struct cfg_marker *)0)->cm_createtime));
+ LASSERTF((int)offsetof(struct cfg_marker, cm_canceltime) == 24, "found %lld\n",
+ (long long)(int)offsetof(struct cfg_marker, cm_canceltime));
+ LASSERTF((int)sizeof(((struct cfg_marker *)0)->cm_canceltime) == 8, "found %lld\n",
+ (long long)(int)sizeof(((struct cfg_marker *)0)->cm_canceltime));
+ LASSERTF((int)offsetof(struct cfg_marker, cm_tgtname) == 32, "found %lld\n",
+ (long long)(int)offsetof(struct cfg_marker, cm_tgtname));
+ LASSERTF((int)sizeof(((struct cfg_marker *)0)->cm_tgtname) == 64, "found %lld\n",
+ (long long)(int)sizeof(((struct cfg_marker *)0)->cm_tgtname));
+ LASSERTF((int)offsetof(struct cfg_marker, cm_comment) == 96, "found %lld\n",
+ (long long)(int)offsetof(struct cfg_marker, cm_comment));
+ LASSERTF((int)sizeof(((struct cfg_marker *)0)->cm_comment) == 64, "found %lld\n",
+ (long long)(int)sizeof(((struct cfg_marker *)0)->cm_comment));
+
+ /* Checks for struct llog_logid */
+ LASSERTF((int)sizeof(struct llog_logid) == 20, "found %lld\n",
+ (long long)(int)sizeof(struct llog_logid));
+ LASSERTF((int)offsetof(struct llog_logid, lgl_oi) == 0, "found %lld\n",
+ (long long)(int)offsetof(struct llog_logid, lgl_oi));
+ LASSERTF((int)sizeof(((struct llog_logid *)0)->lgl_oi) == 16, "found %lld\n",
+ (long long)(int)sizeof(((struct llog_logid *)0)->lgl_oi));
+ LASSERTF((int)offsetof(struct llog_logid, lgl_ogen) == 16, "found %lld\n",
+ (long long)(int)offsetof(struct llog_logid, lgl_ogen));
+ LASSERTF((int)sizeof(((struct llog_logid *)0)->lgl_ogen) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct llog_logid *)0)->lgl_ogen));
+ CLASSERT(OST_SZ_REC == 274730752);
+ CLASSERT(MDS_UNLINK_REC == 274801668);
+ CLASSERT(MDS_UNLINK64_REC == 275325956);
+ CLASSERT(MDS_SETATTR64_REC == 275325953);
+ CLASSERT(OBD_CFG_REC == 274857984);
+ CLASSERT(LLOG_GEN_REC == 274989056);
+ CLASSERT(CHANGELOG_REC == 275120128);
+ CLASSERT(CHANGELOG_USER_REC == 275185664);
+ CLASSERT(LLOG_HDR_MAGIC == 275010873);
+ CLASSERT(LLOG_LOGID_MAGIC == 275010875);
+
+ /* Checks for struct llog_catid */
+ LASSERTF((int)sizeof(struct llog_catid) == 32, "found %lld\n",
+ (long long)(int)sizeof(struct llog_catid));
+ LASSERTF((int)offsetof(struct llog_catid, lci_logid) == 0, "found %lld\n",
+ (long long)(int)offsetof(struct llog_catid, lci_logid));
+ LASSERTF((int)sizeof(((struct llog_catid *)0)->lci_logid) == 20, "found %lld\n",
+ (long long)(int)sizeof(((struct llog_catid *)0)->lci_logid));
+ LASSERTF((int)offsetof(struct llog_catid, lci_padding1) == 20, "found %lld\n",
+ (long long)(int)offsetof(struct llog_catid, lci_padding1));
+ LASSERTF((int)sizeof(((struct llog_catid *)0)->lci_padding1) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct llog_catid *)0)->lci_padding1));
+ LASSERTF((int)offsetof(struct llog_catid, lci_padding2) == 24, "found %lld\n",
+ (long long)(int)offsetof(struct llog_catid, lci_padding2));
+ LASSERTF((int)sizeof(((struct llog_catid *)0)->lci_padding2) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct llog_catid *)0)->lci_padding2));
+ LASSERTF((int)offsetof(struct llog_catid, lci_padding3) == 28, "found %lld\n",
+ (long long)(int)offsetof(struct llog_catid, lci_padding3));
+ LASSERTF((int)sizeof(((struct llog_catid *)0)->lci_padding3) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct llog_catid *)0)->lci_padding3));
+
+ /* Checks for struct llog_rec_hdr */
+ LASSERTF((int)sizeof(struct llog_rec_hdr) == 16, "found %lld\n",
+ (long long)(int)sizeof(struct llog_rec_hdr));
+ LASSERTF((int)offsetof(struct llog_rec_hdr, lrh_len) == 0, "found %lld\n",
+ (long long)(int)offsetof(struct llog_rec_hdr, lrh_len));
+ LASSERTF((int)sizeof(((struct llog_rec_hdr *)0)->lrh_len) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct llog_rec_hdr *)0)->lrh_len));
+ LASSERTF((int)offsetof(struct llog_rec_hdr, lrh_index) == 4, "found %lld\n",
+ (long long)(int)offsetof(struct llog_rec_hdr, lrh_index));
+ LASSERTF((int)sizeof(((struct llog_rec_hdr *)0)->lrh_index) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct llog_rec_hdr *)0)->lrh_index));
+ LASSERTF((int)offsetof(struct llog_rec_hdr, lrh_type) == 8, "found %lld\n",
+ (long long)(int)offsetof(struct llog_rec_hdr, lrh_type));
+ LASSERTF((int)sizeof(((struct llog_rec_hdr *)0)->lrh_type) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct llog_rec_hdr *)0)->lrh_type));
+ LASSERTF((int)offsetof(struct llog_rec_hdr, lrh_id) == 12, "found %lld\n",
+ (long long)(int)offsetof(struct llog_rec_hdr, lrh_id));
+ LASSERTF((int)sizeof(((struct llog_rec_hdr *)0)->lrh_id) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct llog_rec_hdr *)0)->lrh_id));
+
+ /* Checks for struct llog_rec_tail */
+ LASSERTF((int)sizeof(struct llog_rec_tail) == 8, "found %lld\n",
+ (long long)(int)sizeof(struct llog_rec_tail));
+ LASSERTF((int)offsetof(struct llog_rec_tail, lrt_len) == 0, "found %lld\n",
+ (long long)(int)offsetof(struct llog_rec_tail, lrt_len));
+ LASSERTF((int)sizeof(((struct llog_rec_tail *)0)->lrt_len) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct llog_rec_tail *)0)->lrt_len));
+ LASSERTF((int)offsetof(struct llog_rec_tail, lrt_index) == 4, "found %lld\n",
+ (long long)(int)offsetof(struct llog_rec_tail, lrt_index));
+ LASSERTF((int)sizeof(((struct llog_rec_tail *)0)->lrt_index) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct llog_rec_tail *)0)->lrt_index));
+
+ /* Checks for struct llog_logid_rec */
+ LASSERTF((int)sizeof(struct llog_logid_rec) == 64, "found %lld\n",
+ (long long)(int)sizeof(struct llog_logid_rec));
+ LASSERTF((int)offsetof(struct llog_logid_rec, lid_hdr) == 0, "found %lld\n",
+ (long long)(int)offsetof(struct llog_logid_rec, lid_hdr));
+ LASSERTF((int)sizeof(((struct llog_logid_rec *)0)->lid_hdr) == 16, "found %lld\n",
+ (long long)(int)sizeof(((struct llog_logid_rec *)0)->lid_hdr));
+ LASSERTF((int)offsetof(struct llog_logid_rec, lid_id) == 16, "found %lld\n",
+ (long long)(int)offsetof(struct llog_logid_rec, lid_id));
+ LASSERTF((int)sizeof(((struct llog_logid_rec *)0)->lid_id) == 20, "found %lld\n",
+ (long long)(int)sizeof(((struct llog_logid_rec *)0)->lid_id));
+ LASSERTF((int)offsetof(struct llog_logid_rec, lid_padding1) == 36, "found %lld\n",
+ (long long)(int)offsetof(struct llog_logid_rec, lid_padding1));
+ LASSERTF((int)sizeof(((struct llog_logid_rec *)0)->lid_padding1) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct llog_logid_rec *)0)->lid_padding1));
+ LASSERTF((int)offsetof(struct llog_logid_rec, lid_padding2) == 40, "found %lld\n",
+ (long long)(int)offsetof(struct llog_logid_rec, lid_padding2));
+ LASSERTF((int)sizeof(((struct llog_logid_rec *)0)->lid_padding2) == 8, "found %lld\n",
+ (long long)(int)sizeof(((struct llog_logid_rec *)0)->lid_padding2));
+ LASSERTF((int)offsetof(struct llog_logid_rec, lid_padding3) == 48, "found %lld\n",
+ (long long)(int)offsetof(struct llog_logid_rec, lid_padding3));
+ LASSERTF((int)sizeof(((struct llog_logid_rec *)0)->lid_padding3) == 8, "found %lld\n",
+ (long long)(int)sizeof(((struct llog_logid_rec *)0)->lid_padding3));
+ LASSERTF((int)offsetof(struct llog_logid_rec, lid_tail) == 56, "found %lld\n",
+ (long long)(int)offsetof(struct llog_logid_rec, lid_tail));
+ LASSERTF((int)sizeof(((struct llog_logid_rec *)0)->lid_tail) == 8, "found %lld\n",
+ (long long)(int)sizeof(((struct llog_logid_rec *)0)->lid_tail));
+
+ /* Checks for struct llog_unlink_rec */
+ LASSERTF((int)sizeof(struct llog_unlink_rec) == 40, "found %lld\n",
+ (long long)(int)sizeof(struct llog_unlink_rec));
+ LASSERTF((int)offsetof(struct llog_unlink_rec, lur_hdr) == 0, "found %lld\n",
+ (long long)(int)offsetof(struct llog_unlink_rec, lur_hdr));
+ LASSERTF((int)sizeof(((struct llog_unlink_rec *)0)->lur_hdr) == 16, "found %lld\n",
+ (long long)(int)sizeof(((struct llog_unlink_rec *)0)->lur_hdr));
+ LASSERTF((int)offsetof(struct llog_unlink_rec, lur_oid) == 16, "found %lld\n",
+ (long long)(int)offsetof(struct llog_unlink_rec, lur_oid));
+ LASSERTF((int)sizeof(((struct llog_unlink_rec *)0)->lur_oid) == 8, "found %lld\n",
+ (long long)(int)sizeof(((struct llog_unlink_rec *)0)->lur_oid));
+ LASSERTF((int)offsetof(struct llog_unlink_rec, lur_oseq) == 24, "found %lld\n",
+ (long long)(int)offsetof(struct llog_unlink_rec, lur_oseq));
+ LASSERTF((int)sizeof(((struct llog_unlink_rec *)0)->lur_oseq) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct llog_unlink_rec *)0)->lur_oseq));
+ LASSERTF((int)offsetof(struct llog_unlink_rec, lur_count) == 28, "found %lld\n",
+ (long long)(int)offsetof(struct llog_unlink_rec, lur_count));
+ LASSERTF((int)sizeof(((struct llog_unlink_rec *)0)->lur_count) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct llog_unlink_rec *)0)->lur_count));
+ LASSERTF((int)offsetof(struct llog_unlink_rec, lur_tail) == 32, "found %lld\n",
+ (long long)(int)offsetof(struct llog_unlink_rec, lur_tail));
+ LASSERTF((int)sizeof(((struct llog_unlink_rec *)0)->lur_tail) == 8, "found %lld\n",
+ (long long)(int)sizeof(((struct llog_unlink_rec *)0)->lur_tail));
+ /* Checks for struct llog_unlink64_rec */
+ LASSERTF((int)sizeof(struct llog_unlink64_rec) == 64, "found %lld\n",
+ (long long)(int)sizeof(struct llog_unlink64_rec));
+ LASSERTF((int)offsetof(struct llog_unlink64_rec, lur_hdr) == 0, "found %lld\n",
+ (long long)(int)offsetof(struct llog_unlink64_rec, lur_hdr));
+ LASSERTF((int)sizeof(((struct llog_unlink64_rec *)0)->lur_hdr) == 16, "found %lld\n",
+ (long long)(int)sizeof(((struct llog_unlink64_rec *)0)->lur_hdr));
+ LASSERTF((int)offsetof(struct llog_unlink64_rec, lur_fid) == 16, "found %lld\n",
+ (long long)(int)offsetof(struct llog_unlink64_rec, lur_fid));
+ LASSERTF((int)sizeof(((struct llog_unlink64_rec *)0)->lur_fid) == 16, "found %lld\n",
+ (long long)(int)sizeof(((struct llog_unlink64_rec *)0)->lur_fid));
+ LASSERTF((int)offsetof(struct llog_unlink64_rec, lur_count) == 32, "found %lld\n",
+ (long long)(int)offsetof(struct llog_unlink64_rec, lur_count));
+ LASSERTF((int)sizeof(((struct llog_unlink64_rec *)0)->lur_count) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct llog_unlink64_rec *)0)->lur_count));
+ LASSERTF((int)offsetof(struct llog_unlink64_rec, lur_tail) == 56, "found %lld\n",
+ (long long)(int)offsetof(struct llog_unlink64_rec, lur_tail));
+ LASSERTF((int)sizeof(((struct llog_unlink64_rec *)0)->lur_tail) == 8, "found %lld\n",
+ (long long)(int)sizeof(((struct llog_unlink64_rec *)0)->lur_tail));
+ LASSERTF((int)offsetof(struct llog_unlink64_rec, lur_padding1) == 36, "found %lld\n",
+ (long long)(int)offsetof(struct llog_unlink64_rec, lur_padding1));
+ LASSERTF((int)sizeof(((struct llog_unlink64_rec *)0)->lur_padding1) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct llog_unlink64_rec *)0)->lur_padding1));
+ LASSERTF((int)offsetof(struct llog_unlink64_rec, lur_padding2) == 40, "found %lld\n",
+ (long long)(int)offsetof(struct llog_unlink64_rec, lur_padding2));
+ LASSERTF((int)sizeof(((struct llog_unlink64_rec *)0)->lur_padding2) == 8, "found %lld\n",
+ (long long)(int)sizeof(((struct llog_unlink64_rec *)0)->lur_padding2));
+ LASSERTF((int)offsetof(struct llog_unlink64_rec, lur_padding3) == 48, "found %lld\n",
+ (long long)(int)offsetof(struct llog_unlink64_rec, lur_padding3));
+ LASSERTF((int)sizeof(((struct llog_unlink64_rec *)0)->lur_padding3) == 8, "found %lld\n",
+ (long long)(int)sizeof(((struct llog_unlink64_rec *)0)->lur_padding3));
+
+ /* Checks for struct llog_setattr64_rec */
+ LASSERTF((int)sizeof(struct llog_setattr64_rec) == 64, "found %lld\n",
+ (long long)(int)sizeof(struct llog_setattr64_rec));
+ LASSERTF((int)offsetof(struct llog_setattr64_rec, lsr_hdr) == 0, "found %lld\n",
+ (long long)(int)offsetof(struct llog_setattr64_rec, lsr_hdr));
+ LASSERTF((int)sizeof(((struct llog_setattr64_rec *)0)->lsr_hdr) == 16, "found %lld\n",
+ (long long)(int)sizeof(((struct llog_setattr64_rec *)0)->lsr_hdr));
+ LASSERTF((int)offsetof(struct llog_setattr64_rec, lsr_oi) == 16, "found %lld\n",
+ (long long)(int)offsetof(struct llog_setattr64_rec, lsr_oi));
+ LASSERTF((int)sizeof(((struct llog_setattr64_rec *)0)->lsr_oi) == 16, "found %lld\n",
+ (long long)(int)sizeof(((struct llog_setattr64_rec *)0)->lsr_oi));
+ LASSERTF((int)offsetof(struct llog_setattr64_rec, lsr_uid) == 32, "found %lld\n",
+ (long long)(int)offsetof(struct llog_setattr64_rec, lsr_uid));
+ LASSERTF((int)sizeof(((struct llog_setattr64_rec *)0)->lsr_uid) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct llog_setattr64_rec *)0)->lsr_uid));
+ LASSERTF((int)offsetof(struct llog_setattr64_rec, lsr_uid_h) == 36, "found %lld\n",
+ (long long)(int)offsetof(struct llog_setattr64_rec, lsr_uid_h));
+ LASSERTF((int)sizeof(((struct llog_setattr64_rec *)0)->lsr_uid_h) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct llog_setattr64_rec *)0)->lsr_uid_h));
+ LASSERTF((int)offsetof(struct llog_setattr64_rec, lsr_gid) == 40, "found %lld\n",
+ (long long)(int)offsetof(struct llog_setattr64_rec, lsr_gid));
+ LASSERTF((int)sizeof(((struct llog_setattr64_rec *)0)->lsr_gid) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct llog_setattr64_rec *)0)->lsr_gid));
+ LASSERTF((int)offsetof(struct llog_setattr64_rec, lsr_gid_h) == 44, "found %lld\n",
+ (long long)(int)offsetof(struct llog_setattr64_rec, lsr_gid_h));
+ LASSERTF((int)sizeof(((struct llog_setattr64_rec *)0)->lsr_gid_h) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct llog_setattr64_rec *)0)->lsr_gid_h));
+ LASSERTF((int)offsetof(struct llog_setattr64_rec, lsr_padding) == 48, "found %lld\n",
+ (long long)(int)offsetof(struct llog_setattr64_rec, lsr_padding));
+ LASSERTF((int)sizeof(((struct llog_setattr64_rec *)0)->lsr_padding) == 8, "found %lld\n",
+ (long long)(int)sizeof(((struct llog_setattr64_rec *)0)->lsr_padding));
+ LASSERTF((int)offsetof(struct llog_setattr64_rec, lsr_tail) == 56, "found %lld\n",
+ (long long)(int)offsetof(struct llog_setattr64_rec, lsr_tail));
+ LASSERTF((int)sizeof(((struct llog_setattr64_rec *)0)->lsr_tail) == 8, "found %lld\n",
+ (long long)(int)sizeof(((struct llog_setattr64_rec *)0)->lsr_tail));
+
+ /* Checks for struct llog_size_change_rec */
+ LASSERTF((int)sizeof(struct llog_size_change_rec) == 64, "found %lld\n",
+ (long long)(int)sizeof(struct llog_size_change_rec));
+ LASSERTF((int)offsetof(struct llog_size_change_rec, lsc_hdr) == 0, "found %lld\n",
+ (long long)(int)offsetof(struct llog_size_change_rec, lsc_hdr));
+ LASSERTF((int)sizeof(((struct llog_size_change_rec *)0)->lsc_hdr) == 16, "found %lld\n",
+ (long long)(int)sizeof(((struct llog_size_change_rec *)0)->lsc_hdr));
+ LASSERTF((int)offsetof(struct llog_size_change_rec, lsc_fid) == 16, "found %lld\n",
+ (long long)(int)offsetof(struct llog_size_change_rec, lsc_fid));
+ LASSERTF((int)sizeof(((struct llog_size_change_rec *)0)->lsc_fid) == 16, "found %lld\n",
+ (long long)(int)sizeof(((struct llog_size_change_rec *)0)->lsc_fid));
+ LASSERTF((int)offsetof(struct llog_size_change_rec, lsc_ioepoch) == 32, "found %lld\n",
+ (long long)(int)offsetof(struct llog_size_change_rec, lsc_ioepoch));
+ LASSERTF((int)sizeof(((struct llog_size_change_rec *)0)->lsc_ioepoch) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct llog_size_change_rec *)0)->lsc_ioepoch));
+ LASSERTF((int)offsetof(struct llog_size_change_rec, lsc_padding1) == 36, "found %lld\n",
+ (long long)(int)offsetof(struct llog_size_change_rec, lsc_padding1));
+ LASSERTF((int)sizeof(((struct llog_size_change_rec *)0)->lsc_padding1) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct llog_size_change_rec *)0)->lsc_padding1));
+ LASSERTF((int)offsetof(struct llog_size_change_rec, lsc_padding2) == 40, "found %lld\n",
+ (long long)(int)offsetof(struct llog_size_change_rec, lsc_padding2));
+ LASSERTF((int)sizeof(((struct llog_size_change_rec *)0)->lsc_padding2) == 8, "found %lld\n",
+ (long long)(int)sizeof(((struct llog_size_change_rec *)0)->lsc_padding2));
+ LASSERTF((int)offsetof(struct llog_size_change_rec, lsc_padding3) == 48, "found %lld\n",
+ (long long)(int)offsetof(struct llog_size_change_rec, lsc_padding3));
+ LASSERTF((int)sizeof(((struct llog_size_change_rec *)0)->lsc_padding3) == 8, "found %lld\n",
+ (long long)(int)sizeof(((struct llog_size_change_rec *)0)->lsc_padding3));
+ LASSERTF((int)offsetof(struct llog_size_change_rec, lsc_tail) == 56, "found %lld\n",
+ (long long)(int)offsetof(struct llog_size_change_rec, lsc_tail));
+ LASSERTF((int)sizeof(((struct llog_size_change_rec *)0)->lsc_tail) == 8, "found %lld\n",
+ (long long)(int)sizeof(((struct llog_size_change_rec *)0)->lsc_tail));
+
+ /* Checks for struct changelog_rec */
+ LASSERTF((int)sizeof(struct changelog_rec) == 64, "found %lld\n",
+ (long long)(int)sizeof(struct changelog_rec));
+ LASSERTF((int)offsetof(struct changelog_rec, cr_namelen) == 0, "found %lld\n",
+ (long long)(int)offsetof(struct changelog_rec, cr_namelen));
+ LASSERTF((int)sizeof(((struct changelog_rec *)0)->cr_namelen) == 2, "found %lld\n",
+ (long long)(int)sizeof(((struct changelog_rec *)0)->cr_namelen));
+ LASSERTF((int)offsetof(struct changelog_rec, cr_flags) == 2, "found %lld\n",
+ (long long)(int)offsetof(struct changelog_rec, cr_flags));
+ LASSERTF((int)sizeof(((struct changelog_rec *)0)->cr_flags) == 2, "found %lld\n",
+ (long long)(int)sizeof(((struct changelog_rec *)0)->cr_flags));
+ LASSERTF((int)offsetof(struct changelog_rec, cr_type) == 4, "found %lld\n",
+ (long long)(int)offsetof(struct changelog_rec, cr_type));
+ LASSERTF((int)sizeof(((struct changelog_rec *)0)->cr_type) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct changelog_rec *)0)->cr_type));
+ LASSERTF((int)offsetof(struct changelog_rec, cr_index) == 8, "found %lld\n",
+ (long long)(int)offsetof(struct changelog_rec, cr_index));
+ LASSERTF((int)sizeof(((struct changelog_rec *)0)->cr_index) == 8, "found %lld\n",
+ (long long)(int)sizeof(((struct changelog_rec *)0)->cr_index));
+ LASSERTF((int)offsetof(struct changelog_rec, cr_prev) == 16, "found %lld\n",
+ (long long)(int)offsetof(struct changelog_rec, cr_prev));
+ LASSERTF((int)sizeof(((struct changelog_rec *)0)->cr_prev) == 8, "found %lld\n",
+ (long long)(int)sizeof(((struct changelog_rec *)0)->cr_prev));
+ LASSERTF((int)offsetof(struct changelog_rec, cr_time) == 24, "found %lld\n",
+ (long long)(int)offsetof(struct changelog_rec, cr_time));
+ LASSERTF((int)sizeof(((struct changelog_rec *)0)->cr_time) == 8, "found %lld\n",
+ (long long)(int)sizeof(((struct changelog_rec *)0)->cr_time));
+ LASSERTF((int)offsetof(struct changelog_rec, cr_tfid) == 32, "found %lld\n",
+ (long long)(int)offsetof(struct changelog_rec, cr_tfid));
+ LASSERTF((int)sizeof(((struct changelog_rec *)0)->cr_tfid) == 16, "found %lld\n",
+ (long long)(int)sizeof(((struct changelog_rec *)0)->cr_tfid));
+ LASSERTF((int)offsetof(struct changelog_rec, cr_pfid) == 48, "found %lld\n",
+ (long long)(int)offsetof(struct changelog_rec, cr_pfid));
+ LASSERTF((int)sizeof(((struct changelog_rec *)0)->cr_pfid) == 16, "found %lld\n",
+ (long long)(int)sizeof(((struct changelog_rec *)0)->cr_pfid));
+
+ /* Checks for struct changelog_ext_rec */
+ LASSERTF((int)sizeof(struct changelog_ext_rec) == 96, "found %lld\n",
+ (long long)(int)sizeof(struct changelog_ext_rec));
+ LASSERTF((int)offsetof(struct changelog_ext_rec, cr_namelen) == 0, "found %lld\n",
+ (long long)(int)offsetof(struct changelog_ext_rec, cr_namelen));
+ LASSERTF((int)sizeof(((struct changelog_ext_rec *)0)->cr_namelen) == 2, "found %lld\n",
+ (long long)(int)sizeof(((struct changelog_ext_rec *)0)->cr_namelen));
+ LASSERTF((int)offsetof(struct changelog_ext_rec, cr_flags) == 2, "found %lld\n",
+ (long long)(int)offsetof(struct changelog_ext_rec, cr_flags));
+ LASSERTF((int)sizeof(((struct changelog_ext_rec *)0)->cr_flags) == 2, "found %lld\n",
+ (long long)(int)sizeof(((struct changelog_ext_rec *)0)->cr_flags));
+ LASSERTF((int)offsetof(struct changelog_ext_rec, cr_type) == 4, "found %lld\n",
+ (long long)(int)offsetof(struct changelog_ext_rec, cr_type));
+ LASSERTF((int)sizeof(((struct changelog_ext_rec *)0)->cr_type) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct changelog_ext_rec *)0)->cr_type));
+ LASSERTF((int)offsetof(struct changelog_ext_rec, cr_index) == 8, "found %lld\n",
+ (long long)(int)offsetof(struct changelog_ext_rec, cr_index));
+ LASSERTF((int)sizeof(((struct changelog_ext_rec *)0)->cr_index) == 8, "found %lld\n",
+ (long long)(int)sizeof(((struct changelog_ext_rec *)0)->cr_index));
+ LASSERTF((int)offsetof(struct changelog_ext_rec, cr_prev) == 16, "found %lld\n",
+ (long long)(int)offsetof(struct changelog_ext_rec, cr_prev));
+ LASSERTF((int)sizeof(((struct changelog_ext_rec *)0)->cr_prev) == 8, "found %lld\n",
+ (long long)(int)sizeof(((struct changelog_ext_rec *)0)->cr_prev));
+ LASSERTF((int)offsetof(struct changelog_ext_rec, cr_time) == 24, "found %lld\n",
+ (long long)(int)offsetof(struct changelog_ext_rec, cr_time));
+ LASSERTF((int)sizeof(((struct changelog_ext_rec *)0)->cr_time) == 8, "found %lld\n",
+ (long long)(int)sizeof(((struct changelog_ext_rec *)0)->cr_time));
+ LASSERTF((int)offsetof(struct changelog_ext_rec, cr_tfid) == 32, "found %lld\n",
+ (long long)(int)offsetof(struct changelog_ext_rec, cr_tfid));
+ LASSERTF((int)sizeof(((struct changelog_ext_rec *)0)->cr_tfid) == 16, "found %lld\n",
+ (long long)(int)sizeof(((struct changelog_ext_rec *)0)->cr_tfid));
+ LASSERTF((int)offsetof(struct changelog_ext_rec, cr_pfid) == 48, "found %lld\n",
+ (long long)(int)offsetof(struct changelog_ext_rec, cr_pfid));
+ LASSERTF((int)sizeof(((struct changelog_ext_rec *)0)->cr_pfid) == 16, "found %lld\n",
+ (long long)(int)sizeof(((struct changelog_ext_rec *)0)->cr_pfid));
+ LASSERTF((int)offsetof(struct changelog_ext_rec, cr_sfid) == 64, "found %lld\n",
+ (long long)(int)offsetof(struct changelog_ext_rec, cr_sfid));
+ LASSERTF((int)sizeof(((struct changelog_ext_rec *)0)->cr_sfid) == 16, "found %lld\n",
+ (long long)(int)sizeof(((struct changelog_ext_rec *)0)->cr_sfid));
+ LASSERTF((int)offsetof(struct changelog_ext_rec, cr_spfid) == 80, "found %lld\n",
+ (long long)(int)offsetof(struct changelog_ext_rec, cr_spfid));
+ LASSERTF((int)sizeof(((struct changelog_ext_rec *)0)->cr_spfid) == 16, "found %lld\n",
+ (long long)(int)sizeof(((struct changelog_ext_rec *)0)->cr_spfid));
+
+ /* Checks for struct changelog_setinfo */
+ LASSERTF((int)sizeof(struct changelog_setinfo) == 12, "found %lld\n",
+ (long long)(int)sizeof(struct changelog_setinfo));
+ LASSERTF((int)offsetof(struct changelog_setinfo, cs_recno) == 0, "found %lld\n",
+ (long long)(int)offsetof(struct changelog_setinfo, cs_recno));
+ LASSERTF((int)sizeof(((struct changelog_setinfo *)0)->cs_recno) == 8, "found %lld\n",
+ (long long)(int)sizeof(((struct changelog_setinfo *)0)->cs_recno));
+ LASSERTF((int)offsetof(struct changelog_setinfo, cs_id) == 8, "found %lld\n",
+ (long long)(int)offsetof(struct changelog_setinfo, cs_id));
+ LASSERTF((int)sizeof(((struct changelog_setinfo *)0)->cs_id) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct changelog_setinfo *)0)->cs_id));
+
+ /* Checks for struct llog_changelog_rec */
+ LASSERTF((int)sizeof(struct llog_changelog_rec) == 88, "found %lld\n",
+ (long long)(int)sizeof(struct llog_changelog_rec));
+ LASSERTF((int)offsetof(struct llog_changelog_rec, cr_hdr) == 0, "found %lld\n",
+ (long long)(int)offsetof(struct llog_changelog_rec, cr_hdr));
+ LASSERTF((int)sizeof(((struct llog_changelog_rec *)0)->cr_hdr) == 16, "found %lld\n",
+ (long long)(int)sizeof(((struct llog_changelog_rec *)0)->cr_hdr));
+ LASSERTF((int)offsetof(struct llog_changelog_rec, cr) == 16, "found %lld\n",
+ (long long)(int)offsetof(struct llog_changelog_rec, cr));
+ LASSERTF((int)sizeof(((struct llog_changelog_rec *)0)->cr) == 64, "found %lld\n",
+ (long long)(int)sizeof(((struct llog_changelog_rec *)0)->cr));
+ LASSERTF((int)offsetof(struct llog_changelog_rec, cr_tail) == 80, "found %lld\n",
+ (long long)(int)offsetof(struct llog_changelog_rec, cr_tail));
+ LASSERTF((int)sizeof(((struct llog_changelog_rec *)0)->cr_tail) == 8, "found %lld\n",
+ (long long)(int)sizeof(((struct llog_changelog_rec *)0)->cr_tail));
+
+ /* Checks for struct llog_changelog_user_rec */
+ LASSERTF((int)sizeof(struct llog_changelog_user_rec) == 40, "found %lld\n",
+ (long long)(int)sizeof(struct llog_changelog_user_rec));
+ LASSERTF((int)offsetof(struct llog_changelog_user_rec, cur_hdr) == 0, "found %lld\n",
+ (long long)(int)offsetof(struct llog_changelog_user_rec, cur_hdr));
+ LASSERTF((int)sizeof(((struct llog_changelog_user_rec *)0)->cur_hdr) == 16, "found %lld\n",
+ (long long)(int)sizeof(((struct llog_changelog_user_rec *)0)->cur_hdr));
+ LASSERTF((int)offsetof(struct llog_changelog_user_rec, cur_id) == 16, "found %lld\n",
+ (long long)(int)offsetof(struct llog_changelog_user_rec, cur_id));
+ LASSERTF((int)sizeof(((struct llog_changelog_user_rec *)0)->cur_id) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct llog_changelog_user_rec *)0)->cur_id));
+ LASSERTF((int)offsetof(struct llog_changelog_user_rec, cur_padding) == 20, "found %lld\n",
+ (long long)(int)offsetof(struct llog_changelog_user_rec, cur_padding));
+ LASSERTF((int)sizeof(((struct llog_changelog_user_rec *)0)->cur_padding) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct llog_changelog_user_rec *)0)->cur_padding));
+ LASSERTF((int)offsetof(struct llog_changelog_user_rec, cur_endrec) == 24, "found %lld\n",
+ (long long)(int)offsetof(struct llog_changelog_user_rec, cur_endrec));
+ LASSERTF((int)sizeof(((struct llog_changelog_user_rec *)0)->cur_endrec) == 8, "found %lld\n",
+ (long long)(int)sizeof(((struct llog_changelog_user_rec *)0)->cur_endrec));
+ LASSERTF((int)offsetof(struct llog_changelog_user_rec, cur_tail) == 32, "found %lld\n",
+ (long long)(int)offsetof(struct llog_changelog_user_rec, cur_tail));
+ LASSERTF((int)sizeof(((struct llog_changelog_user_rec *)0)->cur_tail) == 8, "found %lld\n",
+ (long long)(int)sizeof(((struct llog_changelog_user_rec *)0)->cur_tail));
+
+ /* Checks for struct llog_gen */
+ LASSERTF((int)sizeof(struct llog_gen) == 16, "found %lld\n",
+ (long long)(int)sizeof(struct llog_gen));
+ LASSERTF((int)offsetof(struct llog_gen, mnt_cnt) == 0, "found %lld\n",
+ (long long)(int)offsetof(struct llog_gen, mnt_cnt));
+ LASSERTF((int)sizeof(((struct llog_gen *)0)->mnt_cnt) == 8, "found %lld\n",
+ (long long)(int)sizeof(((struct llog_gen *)0)->mnt_cnt));
+ LASSERTF((int)offsetof(struct llog_gen, conn_cnt) == 8, "found %lld\n",
+ (long long)(int)offsetof(struct llog_gen, conn_cnt));
+ LASSERTF((int)sizeof(((struct llog_gen *)0)->conn_cnt) == 8, "found %lld\n",
+ (long long)(int)sizeof(((struct llog_gen *)0)->conn_cnt));
+
+ /* Checks for struct llog_gen_rec */
+ LASSERTF((int)sizeof(struct llog_gen_rec) == 64, "found %lld\n",
+ (long long)(int)sizeof(struct llog_gen_rec));
+ LASSERTF((int)offsetof(struct llog_gen_rec, lgr_hdr) == 0, "found %lld\n",
+ (long long)(int)offsetof(struct llog_gen_rec, lgr_hdr));
+ LASSERTF((int)sizeof(((struct llog_gen_rec *)0)->lgr_hdr) == 16, "found %lld\n",
+ (long long)(int)sizeof(((struct llog_gen_rec *)0)->lgr_hdr));
+ LASSERTF((int)offsetof(struct llog_gen_rec, lgr_gen) == 16, "found %lld\n",
+ (long long)(int)offsetof(struct llog_gen_rec, lgr_gen));
+ LASSERTF((int)sizeof(((struct llog_gen_rec *)0)->lgr_gen) == 16, "found %lld\n",
+ (long long)(int)sizeof(((struct llog_gen_rec *)0)->lgr_gen));
+ LASSERTF((int)offsetof(struct llog_gen_rec, lgr_tail) == 56, "found %lld\n",
+ (long long)(int)offsetof(struct llog_gen_rec, lgr_tail));
+ LASSERTF((int)sizeof(((struct llog_gen_rec *)0)->lgr_tail) == 8, "found %lld\n",
+ (long long)(int)sizeof(((struct llog_gen_rec *)0)->lgr_tail));
+
+ /* Checks for struct llog_log_hdr */
+ LASSERTF((int)sizeof(struct llog_log_hdr) == 8192, "found %lld\n",
+ (long long)(int)sizeof(struct llog_log_hdr));
+ LASSERTF((int)offsetof(struct llog_log_hdr, llh_hdr) == 0, "found %lld\n",
+ (long long)(int)offsetof(struct llog_log_hdr, llh_hdr));
+ LASSERTF((int)sizeof(((struct llog_log_hdr *)0)->llh_hdr) == 16, "found %lld\n",
+ (long long)(int)sizeof(((struct llog_log_hdr *)0)->llh_hdr));
+ LASSERTF((int)offsetof(struct llog_log_hdr, llh_timestamp) == 16, "found %lld\n",
+ (long long)(int)offsetof(struct llog_log_hdr, llh_timestamp));
+ LASSERTF((int)sizeof(((struct llog_log_hdr *)0)->llh_timestamp) == 8, "found %lld\n",
+ (long long)(int)sizeof(((struct llog_log_hdr *)0)->llh_timestamp));
+ LASSERTF((int)offsetof(struct llog_log_hdr, llh_count) == 24, "found %lld\n",
+ (long long)(int)offsetof(struct llog_log_hdr, llh_count));
+ LASSERTF((int)sizeof(((struct llog_log_hdr *)0)->llh_count) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct llog_log_hdr *)0)->llh_count));
+ LASSERTF((int)offsetof(struct llog_log_hdr, llh_bitmap_offset) == 28, "found %lld\n",
+ (long long)(int)offsetof(struct llog_log_hdr, llh_bitmap_offset));
+ LASSERTF((int)sizeof(((struct llog_log_hdr *)0)->llh_bitmap_offset) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct llog_log_hdr *)0)->llh_bitmap_offset));
+ LASSERTF((int)offsetof(struct llog_log_hdr, llh_size) == 32, "found %lld\n",
+ (long long)(int)offsetof(struct llog_log_hdr, llh_size));
+ LASSERTF((int)sizeof(((struct llog_log_hdr *)0)->llh_size) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct llog_log_hdr *)0)->llh_size));
+ LASSERTF((int)offsetof(struct llog_log_hdr, llh_flags) == 36, "found %lld\n",
+ (long long)(int)offsetof(struct llog_log_hdr, llh_flags));
+ LASSERTF((int)sizeof(((struct llog_log_hdr *)0)->llh_flags) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct llog_log_hdr *)0)->llh_flags));
+ LASSERTF((int)offsetof(struct llog_log_hdr, llh_cat_idx) == 40, "found %lld\n",
+ (long long)(int)offsetof(struct llog_log_hdr, llh_cat_idx));
+ LASSERTF((int)sizeof(((struct llog_log_hdr *)0)->llh_cat_idx) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct llog_log_hdr *)0)->llh_cat_idx));
+ LASSERTF((int)offsetof(struct llog_log_hdr, llh_tgtuuid) == 44, "found %lld\n",
+ (long long)(int)offsetof(struct llog_log_hdr, llh_tgtuuid));
+ LASSERTF((int)sizeof(((struct llog_log_hdr *)0)->llh_tgtuuid) == 40, "found %lld\n",
+ (long long)(int)sizeof(((struct llog_log_hdr *)0)->llh_tgtuuid));
+ LASSERTF((int)offsetof(struct llog_log_hdr, llh_reserved) == 84, "found %lld\n",
+ (long long)(int)offsetof(struct llog_log_hdr, llh_reserved));
+ LASSERTF((int)sizeof(((struct llog_log_hdr *)0)->llh_reserved) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct llog_log_hdr *)0)->llh_reserved));
+ LASSERTF((int)offsetof(struct llog_log_hdr, llh_bitmap) == 88, "found %lld\n",
+ (long long)(int)offsetof(struct llog_log_hdr, llh_bitmap));
+ LASSERTF((int)sizeof(((struct llog_log_hdr *)0)->llh_bitmap) == 8096, "found %lld\n",
+ (long long)(int)sizeof(((struct llog_log_hdr *)0)->llh_bitmap));
+ LASSERTF((int)offsetof(struct llog_log_hdr, llh_tail) == 8184, "found %lld\n",
+ (long long)(int)offsetof(struct llog_log_hdr, llh_tail));
+ LASSERTF((int)sizeof(((struct llog_log_hdr *)0)->llh_tail) == 8, "found %lld\n",
+ (long long)(int)sizeof(((struct llog_log_hdr *)0)->llh_tail));
+
+ /* Checks for struct llog_cookie */
+ LASSERTF((int)sizeof(struct llog_cookie) == 32, "found %lld\n",
+ (long long)(int)sizeof(struct llog_cookie));
+ LASSERTF((int)offsetof(struct llog_cookie, lgc_lgl) == 0, "found %lld\n",
+ (long long)(int)offsetof(struct llog_cookie, lgc_lgl));
+ LASSERTF((int)sizeof(((struct llog_cookie *)0)->lgc_lgl) == 20, "found %lld\n",
+ (long long)(int)sizeof(((struct llog_cookie *)0)->lgc_lgl));
+ LASSERTF((int)offsetof(struct llog_cookie, lgc_subsys) == 20, "found %lld\n",
+ (long long)(int)offsetof(struct llog_cookie, lgc_subsys));
+ LASSERTF((int)sizeof(((struct llog_cookie *)0)->lgc_subsys) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct llog_cookie *)0)->lgc_subsys));
+ LASSERTF((int)offsetof(struct llog_cookie, lgc_index) == 24, "found %lld\n",
+ (long long)(int)offsetof(struct llog_cookie, lgc_index));
+ LASSERTF((int)sizeof(((struct llog_cookie *)0)->lgc_index) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct llog_cookie *)0)->lgc_index));
+ LASSERTF((int)offsetof(struct llog_cookie, lgc_padding) == 28, "found %lld\n",
+ (long long)(int)offsetof(struct llog_cookie, lgc_padding));
+ LASSERTF((int)sizeof(((struct llog_cookie *)0)->lgc_padding) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct llog_cookie *)0)->lgc_padding));
+
+ /* Checks for struct llogd_body */
+ LASSERTF((int)sizeof(struct llogd_body) == 48, "found %lld\n",
+ (long long)(int)sizeof(struct llogd_body));
+ LASSERTF((int)offsetof(struct llogd_body, lgd_logid) == 0, "found %lld\n",
+ (long long)(int)offsetof(struct llogd_body, lgd_logid));
+ LASSERTF((int)sizeof(((struct llogd_body *)0)->lgd_logid) == 20, "found %lld\n",
+ (long long)(int)sizeof(((struct llogd_body *)0)->lgd_logid));
+ LASSERTF((int)offsetof(struct llogd_body, lgd_ctxt_idx) == 20, "found %lld\n",
+ (long long)(int)offsetof(struct llogd_body, lgd_ctxt_idx));
+ LASSERTF((int)sizeof(((struct llogd_body *)0)->lgd_ctxt_idx) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct llogd_body *)0)->lgd_ctxt_idx));
+ LASSERTF((int)offsetof(struct llogd_body, lgd_llh_flags) == 24, "found %lld\n",
+ (long long)(int)offsetof(struct llogd_body, lgd_llh_flags));
+ LASSERTF((int)sizeof(((struct llogd_body *)0)->lgd_llh_flags) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct llogd_body *)0)->lgd_llh_flags));
+ LASSERTF((int)offsetof(struct llogd_body, lgd_index) == 28, "found %lld\n",
+ (long long)(int)offsetof(struct llogd_body, lgd_index));
+ LASSERTF((int)sizeof(((struct llogd_body *)0)->lgd_index) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct llogd_body *)0)->lgd_index));
+ LASSERTF((int)offsetof(struct llogd_body, lgd_saved_index) == 32, "found %lld\n",
+ (long long)(int)offsetof(struct llogd_body, lgd_saved_index));
+ LASSERTF((int)sizeof(((struct llogd_body *)0)->lgd_saved_index) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct llogd_body *)0)->lgd_saved_index));
+ LASSERTF((int)offsetof(struct llogd_body, lgd_len) == 36, "found %lld\n",
+ (long long)(int)offsetof(struct llogd_body, lgd_len));
+ LASSERTF((int)sizeof(((struct llogd_body *)0)->lgd_len) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct llogd_body *)0)->lgd_len));
+ LASSERTF((int)offsetof(struct llogd_body, lgd_cur_offset) == 40, "found %lld\n",
+ (long long)(int)offsetof(struct llogd_body, lgd_cur_offset));
+ LASSERTF((int)sizeof(((struct llogd_body *)0)->lgd_cur_offset) == 8, "found %lld\n",
+ (long long)(int)sizeof(((struct llogd_body *)0)->lgd_cur_offset));
+ CLASSERT(LLOG_ORIGIN_HANDLE_CREATE == 501);
+ CLASSERT(LLOG_ORIGIN_HANDLE_NEXT_BLOCK == 502);
+ CLASSERT(LLOG_ORIGIN_HANDLE_READ_HEADER == 503);
+ CLASSERT(LLOG_ORIGIN_HANDLE_WRITE_REC == 504);
+ CLASSERT(LLOG_ORIGIN_HANDLE_CLOSE == 505);
+ CLASSERT(LLOG_ORIGIN_CONNECT == 506);
+ CLASSERT(LLOG_CATINFO == 507);
+ CLASSERT(LLOG_ORIGIN_HANDLE_PREV_BLOCK == 508);
+ CLASSERT(LLOG_ORIGIN_HANDLE_DESTROY == 509);
+ CLASSERT(LLOG_FIRST_OPC == 501);
+ CLASSERT(LLOG_LAST_OPC == 510);
+
+ /* Checks for struct llogd_conn_body */
+ LASSERTF((int)sizeof(struct llogd_conn_body) == 40, "found %lld\n",
+ (long long)(int)sizeof(struct llogd_conn_body));
+ LASSERTF((int)offsetof(struct llogd_conn_body, lgdc_gen) == 0, "found %lld\n",
+ (long long)(int)offsetof(struct llogd_conn_body, lgdc_gen));
+ LASSERTF((int)sizeof(((struct llogd_conn_body *)0)->lgdc_gen) == 16, "found %lld\n",
+ (long long)(int)sizeof(((struct llogd_conn_body *)0)->lgdc_gen));
+ LASSERTF((int)offsetof(struct llogd_conn_body, lgdc_logid) == 16, "found %lld\n",
+ (long long)(int)offsetof(struct llogd_conn_body, lgdc_logid));
+ LASSERTF((int)sizeof(((struct llogd_conn_body *)0)->lgdc_logid) == 20, "found %lld\n",
+ (long long)(int)sizeof(((struct llogd_conn_body *)0)->lgdc_logid));
+ LASSERTF((int)offsetof(struct llogd_conn_body, lgdc_ctxt_idx) == 36, "found %lld\n",
+ (long long)(int)offsetof(struct llogd_conn_body, lgdc_ctxt_idx));
+ LASSERTF((int)sizeof(((struct llogd_conn_body *)0)->lgdc_ctxt_idx) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct llogd_conn_body *)0)->lgdc_ctxt_idx));
+
+ /* Checks for struct ll_fiemap_info_key */
+ LASSERTF((int)sizeof(struct ll_fiemap_info_key) == 248, "found %lld\n",
+ (long long)(int)sizeof(struct ll_fiemap_info_key));
+ LASSERTF((int)offsetof(struct ll_fiemap_info_key, name[8]) == 8, "found %lld\n",
+ (long long)(int)offsetof(struct ll_fiemap_info_key, name[8]));
+ LASSERTF((int)sizeof(((struct ll_fiemap_info_key *)0)->name[8]) == 1, "found %lld\n",
+ (long long)(int)sizeof(((struct ll_fiemap_info_key *)0)->name[8]));
+ LASSERTF((int)offsetof(struct ll_fiemap_info_key, oa) == 8, "found %lld\n",
+ (long long)(int)offsetof(struct ll_fiemap_info_key, oa));
+ LASSERTF((int)sizeof(((struct ll_fiemap_info_key *)0)->oa) == 208, "found %lld\n",
+ (long long)(int)sizeof(((struct ll_fiemap_info_key *)0)->oa));
+ LASSERTF((int)offsetof(struct ll_fiemap_info_key, fiemap) == 216, "found %lld\n",
+ (long long)(int)offsetof(struct ll_fiemap_info_key, fiemap));
+ LASSERTF((int)sizeof(((struct ll_fiemap_info_key *)0)->fiemap) == 32, "found %lld\n",
+ (long long)(int)sizeof(((struct ll_fiemap_info_key *)0)->fiemap));
+
+ /* Checks for struct quota_body */
+ LASSERTF((int)sizeof(struct quota_body) == 112, "found %lld\n",
+ (long long)(int)sizeof(struct quota_body));
+ LASSERTF((int)offsetof(struct quota_body, qb_fid) == 0, "found %lld\n",
+ (long long)(int)offsetof(struct quota_body, qb_fid));
+ LASSERTF((int)sizeof(((struct quota_body *)0)->qb_fid) == 16, "found %lld\n",
+ (long long)(int)sizeof(((struct quota_body *)0)->qb_fid));
+ LASSERTF((int)offsetof(struct quota_body, qb_id) == 16, "found %lld\n",
+ (long long)(int)offsetof(struct quota_body, qb_id));
+ LASSERTF((int)sizeof(((struct quota_body *)0)->qb_id) == 16, "found %lld\n",
+ (long long)(int)sizeof(((struct quota_body *)0)->qb_id));
+ LASSERTF((int)offsetof(struct quota_body, qb_flags) == 32, "found %lld\n",
+ (long long)(int)offsetof(struct quota_body, qb_flags));
+ LASSERTF((int)sizeof(((struct quota_body *)0)->qb_flags) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct quota_body *)0)->qb_flags));
+ LASSERTF((int)offsetof(struct quota_body, qb_padding) == 36, "found %lld\n",
+ (long long)(int)offsetof(struct quota_body, qb_padding));
+ LASSERTF((int)sizeof(((struct quota_body *)0)->qb_padding) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct quota_body *)0)->qb_padding));
+ LASSERTF((int)offsetof(struct quota_body, qb_count) == 40, "found %lld\n",
+ (long long)(int)offsetof(struct quota_body, qb_count));
+ LASSERTF((int)sizeof(((struct quota_body *)0)->qb_count) == 8, "found %lld\n",
+ (long long)(int)sizeof(((struct quota_body *)0)->qb_count));
+ LASSERTF((int)offsetof(struct quota_body, qb_usage) == 48, "found %lld\n",
+ (long long)(int)offsetof(struct quota_body, qb_usage));
+ LASSERTF((int)sizeof(((struct quota_body *)0)->qb_usage) == 8, "found %lld\n",
+ (long long)(int)sizeof(((struct quota_body *)0)->qb_usage));
+ LASSERTF((int)offsetof(struct quota_body, qb_slv_ver) == 56, "found %lld\n",
+ (long long)(int)offsetof(struct quota_body, qb_slv_ver));
+ LASSERTF((int)sizeof(((struct quota_body *)0)->qb_slv_ver) == 8, "found %lld\n",
+ (long long)(int)sizeof(((struct quota_body *)0)->qb_slv_ver));
+ LASSERTF((int)offsetof(struct quota_body, qb_lockh) == 64, "found %lld\n",
+ (long long)(int)offsetof(struct quota_body, qb_lockh));
+ LASSERTF((int)sizeof(((struct quota_body *)0)->qb_lockh) == 8, "found %lld\n",
+ (long long)(int)sizeof(((struct quota_body *)0)->qb_lockh));
+ LASSERTF((int)offsetof(struct quota_body, qb_glb_lockh) == 72, "found %lld\n",
+ (long long)(int)offsetof(struct quota_body, qb_glb_lockh));
+ LASSERTF((int)sizeof(((struct quota_body *)0)->qb_glb_lockh) == 8, "found %lld\n",
+ (long long)(int)sizeof(((struct quota_body *)0)->qb_glb_lockh));
+ LASSERTF((int)offsetof(struct quota_body, qb_padding1[4]) == 112, "found %lld\n",
+ (long long)(int)offsetof(struct quota_body, qb_padding1[4]));
+ LASSERTF((int)sizeof(((struct quota_body *)0)->qb_padding1[4]) == 8, "found %lld\n",
+ (long long)(int)sizeof(((struct quota_body *)0)->qb_padding1[4]));
+
+ /* Checks for struct mgs_target_info */
+ LASSERTF((int)sizeof(struct mgs_target_info) == 4544, "found %lld\n",
+ (long long)(int)sizeof(struct mgs_target_info));
+ LASSERTF((int)offsetof(struct mgs_target_info, mti_lustre_ver) == 0, "found %lld\n",
+ (long long)(int)offsetof(struct mgs_target_info, mti_lustre_ver));
+ LASSERTF((int)sizeof(((struct mgs_target_info *)0)->mti_lustre_ver) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct mgs_target_info *)0)->mti_lustre_ver));
+ LASSERTF((int)offsetof(struct mgs_target_info, mti_stripe_index) == 4, "found %lld\n",
+ (long long)(int)offsetof(struct mgs_target_info, mti_stripe_index));
+ LASSERTF((int)sizeof(((struct mgs_target_info *)0)->mti_stripe_index) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct mgs_target_info *)0)->mti_stripe_index));
+ LASSERTF((int)offsetof(struct mgs_target_info, mti_config_ver) == 8, "found %lld\n",
+ (long long)(int)offsetof(struct mgs_target_info, mti_config_ver));
+ LASSERTF((int)sizeof(((struct mgs_target_info *)0)->mti_config_ver) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct mgs_target_info *)0)->mti_config_ver));
+ LASSERTF((int)offsetof(struct mgs_target_info, mti_flags) == 12, "found %lld\n",
+ (long long)(int)offsetof(struct mgs_target_info, mti_flags));
+ LASSERTF((int)sizeof(((struct mgs_target_info *)0)->mti_flags) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct mgs_target_info *)0)->mti_flags));
+ LASSERTF((int)offsetof(struct mgs_target_info, mti_nid_count) == 16, "found %lld\n",
+ (long long)(int)offsetof(struct mgs_target_info, mti_nid_count));
+ LASSERTF((int)sizeof(((struct mgs_target_info *)0)->mti_nid_count) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct mgs_target_info *)0)->mti_nid_count));
+ LASSERTF((int)offsetof(struct mgs_target_info, mti_instance) == 20, "found %lld\n",
+ (long long)(int)offsetof(struct mgs_target_info, mti_instance));
+ LASSERTF((int)sizeof(((struct mgs_target_info *)0)->mti_instance) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct mgs_target_info *)0)->mti_instance));
+ LASSERTF((int)offsetof(struct mgs_target_info, mti_fsname) == 24, "found %lld\n",
+ (long long)(int)offsetof(struct mgs_target_info, mti_fsname));
+ LASSERTF((int)sizeof(((struct mgs_target_info *)0)->mti_fsname) == 64, "found %lld\n",
+ (long long)(int)sizeof(((struct mgs_target_info *)0)->mti_fsname));
+ LASSERTF((int)offsetof(struct mgs_target_info, mti_svname) == 88, "found %lld\n",
+ (long long)(int)offsetof(struct mgs_target_info, mti_svname));
+ LASSERTF((int)sizeof(((struct mgs_target_info *)0)->mti_svname) == 64, "found %lld\n",
+ (long long)(int)sizeof(((struct mgs_target_info *)0)->mti_svname));
+ LASSERTF((int)offsetof(struct mgs_target_info, mti_uuid) == 152, "found %lld\n",
+ (long long)(int)offsetof(struct mgs_target_info, mti_uuid));
+ LASSERTF((int)sizeof(((struct mgs_target_info *)0)->mti_uuid) == 40, "found %lld\n",
+ (long long)(int)sizeof(((struct mgs_target_info *)0)->mti_uuid));
+ LASSERTF((int)offsetof(struct mgs_target_info, mti_nids) == 192, "found %lld\n",
+ (long long)(int)offsetof(struct mgs_target_info, mti_nids));
+ LASSERTF((int)sizeof(((struct mgs_target_info *)0)->mti_nids) == 256, "found %lld\n",
+ (long long)(int)sizeof(((struct mgs_target_info *)0)->mti_nids));
+ LASSERTF((int)offsetof(struct mgs_target_info, mti_params) == 448, "found %lld\n",
+ (long long)(int)offsetof(struct mgs_target_info, mti_params));
+ LASSERTF((int)sizeof(((struct mgs_target_info *)0)->mti_params) == 4096, "found %lld\n",
+ (long long)(int)sizeof(((struct mgs_target_info *)0)->mti_params));
+
+ /* Checks for struct lustre_capa */
+ LASSERTF((int)sizeof(struct lustre_capa) == 120, "found %lld\n",
+ (long long)(int)sizeof(struct lustre_capa));
+ LASSERTF((int)offsetof(struct lustre_capa, lc_fid) == 0, "found %lld\n",
+ (long long)(int)offsetof(struct lustre_capa, lc_fid));
+ LASSERTF((int)sizeof(((struct lustre_capa *)0)->lc_fid) == 16, "found %lld\n",
+ (long long)(int)sizeof(((struct lustre_capa *)0)->lc_fid));
+ LASSERTF((int)offsetof(struct lustre_capa, lc_opc) == 16, "found %lld\n",
+ (long long)(int)offsetof(struct lustre_capa, lc_opc));
+ LASSERTF((int)sizeof(((struct lustre_capa *)0)->lc_opc) == 8, "found %lld\n",
+ (long long)(int)sizeof(((struct lustre_capa *)0)->lc_opc));
+ LASSERTF((int)offsetof(struct lustre_capa, lc_uid) == 24, "found %lld\n",
+ (long long)(int)offsetof(struct lustre_capa, lc_uid));
+ LASSERTF((int)sizeof(((struct lustre_capa *)0)->lc_uid) == 8, "found %lld\n",
+ (long long)(int)sizeof(((struct lustre_capa *)0)->lc_uid));
+ LASSERTF((int)offsetof(struct lustre_capa, lc_gid) == 32, "found %lld\n",
+ (long long)(int)offsetof(struct lustre_capa, lc_gid));
+ LASSERTF((int)sizeof(((struct lustre_capa *)0)->lc_gid) == 8, "found %lld\n",
+ (long long)(int)sizeof(((struct lustre_capa *)0)->lc_gid));
+ LASSERTF((int)offsetof(struct lustre_capa, lc_flags) == 40, "found %lld\n",
+ (long long)(int)offsetof(struct lustre_capa, lc_flags));
+ LASSERTF((int)sizeof(((struct lustre_capa *)0)->lc_flags) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct lustre_capa *)0)->lc_flags));
+ LASSERTF((int)offsetof(struct lustre_capa, lc_keyid) == 44, "found %lld\n",
+ (long long)(int)offsetof(struct lustre_capa, lc_keyid));
+ LASSERTF((int)sizeof(((struct lustre_capa *)0)->lc_keyid) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct lustre_capa *)0)->lc_keyid));
+ LASSERTF((int)offsetof(struct lustre_capa, lc_timeout) == 48, "found %lld\n",
+ (long long)(int)offsetof(struct lustre_capa, lc_timeout));
+ LASSERTF((int)sizeof(((struct lustre_capa *)0)->lc_timeout) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct lustre_capa *)0)->lc_timeout));
+ LASSERTF((int)offsetof(struct lustre_capa, lc_expiry) == 52, "found %lld\n",
+ (long long)(int)offsetof(struct lustre_capa, lc_expiry));
+ LASSERTF((int)sizeof(((struct lustre_capa *)0)->lc_expiry) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct lustre_capa *)0)->lc_expiry));
+ CLASSERT(CAPA_HMAC_MAX_LEN == 64);
+ LASSERTF((int)offsetof(struct lustre_capa, lc_hmac[64]) == 120, "found %lld\n",
+ (long long)(int)offsetof(struct lustre_capa, lc_hmac[64]));
+ LASSERTF((int)sizeof(((struct lustre_capa *)0)->lc_hmac[64]) == 1, "found %lld\n",
+ (long long)(int)sizeof(((struct lustre_capa *)0)->lc_hmac[64]));
+
+ /* Checks for struct lustre_capa_key */
+ LASSERTF((int)sizeof(struct lustre_capa_key) == 72, "found %lld\n",
+ (long long)(int)sizeof(struct lustre_capa_key));
+ LASSERTF((int)offsetof(struct lustre_capa_key, lk_seq) == 0, "found %lld\n",
+ (long long)(int)offsetof(struct lustre_capa_key, lk_seq));
+ LASSERTF((int)sizeof(((struct lustre_capa_key *)0)->lk_seq) == 8, "found %lld\n",
+ (long long)(int)sizeof(((struct lustre_capa_key *)0)->lk_seq));
+ LASSERTF((int)offsetof(struct lustre_capa_key, lk_keyid) == 8, "found %lld\n",
+ (long long)(int)offsetof(struct lustre_capa_key, lk_keyid));
+ LASSERTF((int)sizeof(((struct lustre_capa_key *)0)->lk_keyid) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct lustre_capa_key *)0)->lk_keyid));
+ LASSERTF((int)offsetof(struct lustre_capa_key, lk_padding) == 12, "found %lld\n",
+ (long long)(int)offsetof(struct lustre_capa_key, lk_padding));
+ LASSERTF((int)sizeof(((struct lustre_capa_key *)0)->lk_padding) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct lustre_capa_key *)0)->lk_padding));
+ CLASSERT(CAPA_HMAC_KEY_MAX_LEN == 56);
+ LASSERTF((int)offsetof(struct lustre_capa_key, lk_key[56]) == 72, "found %lld\n",
+ (long long)(int)offsetof(struct lustre_capa_key, lk_key[56]));
+ LASSERTF((int)sizeof(((struct lustre_capa_key *)0)->lk_key[56]) == 1, "found %lld\n",
+ (long long)(int)sizeof(((struct lustre_capa_key *)0)->lk_key[56]));
+
+ /* Checks for struct getinfo_fid2path */
+ LASSERTF((int)sizeof(struct getinfo_fid2path) == 32, "found %lld\n",
+ (long long)(int)sizeof(struct getinfo_fid2path));
+ LASSERTF((int)offsetof(struct getinfo_fid2path, gf_fid) == 0, "found %lld\n",
+ (long long)(int)offsetof(struct getinfo_fid2path, gf_fid));
+ LASSERTF((int)sizeof(((struct getinfo_fid2path *)0)->gf_fid) == 16, "found %lld\n",
+ (long long)(int)sizeof(((struct getinfo_fid2path *)0)->gf_fid));
+ LASSERTF((int)offsetof(struct getinfo_fid2path, gf_recno) == 16, "found %lld\n",
+ (long long)(int)offsetof(struct getinfo_fid2path, gf_recno));
+ LASSERTF((int)sizeof(((struct getinfo_fid2path *)0)->gf_recno) == 8, "found %lld\n",
+ (long long)(int)sizeof(((struct getinfo_fid2path *)0)->gf_recno));
+ LASSERTF((int)offsetof(struct getinfo_fid2path, gf_linkno) == 24, "found %lld\n",
+ (long long)(int)offsetof(struct getinfo_fid2path, gf_linkno));
+ LASSERTF((int)sizeof(((struct getinfo_fid2path *)0)->gf_linkno) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct getinfo_fid2path *)0)->gf_linkno));
+ LASSERTF((int)offsetof(struct getinfo_fid2path, gf_pathlen) == 28, "found %lld\n",
+ (long long)(int)offsetof(struct getinfo_fid2path, gf_pathlen));
+ LASSERTF((int)sizeof(((struct getinfo_fid2path *)0)->gf_pathlen) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct getinfo_fid2path *)0)->gf_pathlen));
+ LASSERTF((int)offsetof(struct getinfo_fid2path, gf_path[0]) == 32, "found %lld\n",
+ (long long)(int)offsetof(struct getinfo_fid2path, gf_path[0]));
+ LASSERTF((int)sizeof(((struct getinfo_fid2path *)0)->gf_path[0]) == 1, "found %lld\n",
+ (long long)(int)sizeof(((struct getinfo_fid2path *)0)->gf_path[0]));
+
+ /* Checks for struct ll_user_fiemap */
+ LASSERTF((int)sizeof(struct ll_user_fiemap) == 32, "found %lld\n",
+ (long long)(int)sizeof(struct ll_user_fiemap));
+ LASSERTF((int)offsetof(struct ll_user_fiemap, fm_start) == 0, "found %lld\n",
+ (long long)(int)offsetof(struct ll_user_fiemap, fm_start));
+ LASSERTF((int)sizeof(((struct ll_user_fiemap *)0)->fm_start) == 8, "found %lld\n",
+ (long long)(int)sizeof(((struct ll_user_fiemap *)0)->fm_start));
+ LASSERTF((int)offsetof(struct ll_user_fiemap, fm_length) == 8, "found %lld\n",
+ (long long)(int)offsetof(struct ll_user_fiemap, fm_length));
+ LASSERTF((int)sizeof(((struct ll_user_fiemap *)0)->fm_length) == 8, "found %lld\n",
+ (long long)(int)sizeof(((struct ll_user_fiemap *)0)->fm_length));
+ LASSERTF((int)offsetof(struct ll_user_fiemap, fm_flags) == 16, "found %lld\n",
+ (long long)(int)offsetof(struct ll_user_fiemap, fm_flags));
+ LASSERTF((int)sizeof(((struct ll_user_fiemap *)0)->fm_flags) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct ll_user_fiemap *)0)->fm_flags));
+ LASSERTF((int)offsetof(struct ll_user_fiemap, fm_mapped_extents) == 20, "found %lld\n",
+ (long long)(int)offsetof(struct ll_user_fiemap, fm_mapped_extents));
+ LASSERTF((int)sizeof(((struct ll_user_fiemap *)0)->fm_mapped_extents) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct ll_user_fiemap *)0)->fm_mapped_extents));
+ LASSERTF((int)offsetof(struct ll_user_fiemap, fm_extent_count) == 24, "found %lld\n",
+ (long long)(int)offsetof(struct ll_user_fiemap, fm_extent_count));
+ LASSERTF((int)sizeof(((struct ll_user_fiemap *)0)->fm_extent_count) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct ll_user_fiemap *)0)->fm_extent_count));
+ LASSERTF((int)offsetof(struct ll_user_fiemap, fm_reserved) == 28, "found %lld\n",
+ (long long)(int)offsetof(struct ll_user_fiemap, fm_reserved));
+ LASSERTF((int)sizeof(((struct ll_user_fiemap *)0)->fm_reserved) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct ll_user_fiemap *)0)->fm_reserved));
+ LASSERTF((int)offsetof(struct ll_user_fiemap, fm_extents) == 32, "found %lld\n",
+ (long long)(int)offsetof(struct ll_user_fiemap, fm_extents));
+ LASSERTF((int)sizeof(((struct ll_user_fiemap *)0)->fm_extents) == 0, "found %lld\n",
+ (long long)(int)sizeof(((struct ll_user_fiemap *)0)->fm_extents));
+ CLASSERT(FIEMAP_FLAG_SYNC == 0x00000001);
+ CLASSERT(FIEMAP_FLAG_XATTR == 0x00000002);
+ CLASSERT(FIEMAP_FLAG_DEVICE_ORDER == 0x40000000);
+
+ /* Checks for struct ll_fiemap_extent */
+ LASSERTF((int)sizeof(struct ll_fiemap_extent) == 56, "found %lld\n",
+ (long long)(int)sizeof(struct ll_fiemap_extent));
+ LASSERTF((int)offsetof(struct ll_fiemap_extent, fe_logical) == 0, "found %lld\n",
+ (long long)(int)offsetof(struct ll_fiemap_extent, fe_logical));
+ LASSERTF((int)sizeof(((struct ll_fiemap_extent *)0)->fe_logical) == 8, "found %lld\n",
+ (long long)(int)sizeof(((struct ll_fiemap_extent *)0)->fe_logical));
+ LASSERTF((int)offsetof(struct ll_fiemap_extent, fe_physical) == 8, "found %lld\n",
+ (long long)(int)offsetof(struct ll_fiemap_extent, fe_physical));
+ LASSERTF((int)sizeof(((struct ll_fiemap_extent *)0)->fe_physical) == 8, "found %lld\n",
+ (long long)(int)sizeof(((struct ll_fiemap_extent *)0)->fe_physical));
+ LASSERTF((int)offsetof(struct ll_fiemap_extent, fe_length) == 16, "found %lld\n",
+ (long long)(int)offsetof(struct ll_fiemap_extent, fe_length));
+ LASSERTF((int)sizeof(((struct ll_fiemap_extent *)0)->fe_length) == 8, "found %lld\n",
+ (long long)(int)sizeof(((struct ll_fiemap_extent *)0)->fe_length));
+ LASSERTF((int)offsetof(struct ll_fiemap_extent, fe_flags) == 40, "found %lld\n",
+ (long long)(int)offsetof(struct ll_fiemap_extent, fe_flags));
+ LASSERTF((int)sizeof(((struct ll_fiemap_extent *)0)->fe_flags) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct ll_fiemap_extent *)0)->fe_flags));
+ LASSERTF((int)offsetof(struct ll_fiemap_extent, fe_device) == 44, "found %lld\n",
+ (long long)(int)offsetof(struct ll_fiemap_extent, fe_device));
+ LASSERTF((int)sizeof(((struct ll_fiemap_extent *)0)->fe_device) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct ll_fiemap_extent *)0)->fe_device));
+ CLASSERT(FIEMAP_EXTENT_LAST == 0x00000001);
+ CLASSERT(FIEMAP_EXTENT_UNKNOWN == 0x00000002);
+ CLASSERT(FIEMAP_EXTENT_DELALLOC == 0x00000004);
+ CLASSERT(FIEMAP_EXTENT_ENCODED == 0x00000008);
+ CLASSERT(FIEMAP_EXTENT_DATA_ENCRYPTED == 0x00000080);
+ CLASSERT(FIEMAP_EXTENT_NOT_ALIGNED == 0x00000100);
+ CLASSERT(FIEMAP_EXTENT_DATA_INLINE == 0x00000200);
+ CLASSERT(FIEMAP_EXTENT_DATA_TAIL == 0x00000400);
+ CLASSERT(FIEMAP_EXTENT_UNWRITTEN == 0x00000800);
+ CLASSERT(FIEMAP_EXTENT_MERGED == 0x00001000);
+ CLASSERT(FIEMAP_EXTENT_NO_DIRECT == 0x40000000);
+ CLASSERT(FIEMAP_EXTENT_NET == 0x80000000);
+
+ /* Checks for type posix_acl_xattr_entry */
+ LASSERTF((int)sizeof(posix_acl_xattr_entry) == 8, "found %lld\n",
+ (long long)(int)sizeof(posix_acl_xattr_entry));
+ LASSERTF((int)offsetof(posix_acl_xattr_entry, e_tag) == 0, "found %lld\n",
+ (long long)(int)offsetof(posix_acl_xattr_entry, e_tag));
+ LASSERTF((int)sizeof(((posix_acl_xattr_entry *)0)->e_tag) == 2, "found %lld\n",
+ (long long)(int)sizeof(((posix_acl_xattr_entry *)0)->e_tag));
+ LASSERTF((int)offsetof(posix_acl_xattr_entry, e_perm) == 2, "found %lld\n",
+ (long long)(int)offsetof(posix_acl_xattr_entry, e_perm));
+ LASSERTF((int)sizeof(((posix_acl_xattr_entry *)0)->e_perm) == 2, "found %lld\n",
+ (long long)(int)sizeof(((posix_acl_xattr_entry *)0)->e_perm));
+ LASSERTF((int)offsetof(posix_acl_xattr_entry, e_id) == 4, "found %lld\n",
+ (long long)(int)offsetof(posix_acl_xattr_entry, e_id));
+ LASSERTF((int)sizeof(((posix_acl_xattr_entry *)0)->e_id) == 4, "found %lld\n",
+ (long long)(int)sizeof(((posix_acl_xattr_entry *)0)->e_id));
+
+ /* Checks for type posix_acl_xattr_header */
+ LASSERTF((int)sizeof(posix_acl_xattr_header) == 4, "found %lld\n",
+ (long long)(int)sizeof(posix_acl_xattr_header));
+ LASSERTF((int)offsetof(posix_acl_xattr_header, a_version) == 0, "found %lld\n",
+ (long long)(int)offsetof(posix_acl_xattr_header, a_version));
+ LASSERTF((int)sizeof(((posix_acl_xattr_header *)0)->a_version) == 4, "found %lld\n",
+ (long long)(int)sizeof(((posix_acl_xattr_header *)0)->a_version));
+ LASSERTF((int)offsetof(posix_acl_xattr_header, a_entries) == 4, "found %lld\n",
+ (long long)(int)offsetof(posix_acl_xattr_header, a_entries));
+ LASSERTF((int)sizeof(((posix_acl_xattr_header *)0)->a_entries) == 0, "found %lld\n",
+ (long long)(int)sizeof(((posix_acl_xattr_header *)0)->a_entries));
+
+ /* Checks for struct link_ea_header */
+ LASSERTF((int)sizeof(struct link_ea_header) == 24, "found %lld\n",
+ (long long)(int)sizeof(struct link_ea_header));
+ LASSERTF((int)offsetof(struct link_ea_header, leh_magic) == 0, "found %lld\n",
+ (long long)(int)offsetof(struct link_ea_header, leh_magic));
+ LASSERTF((int)sizeof(((struct link_ea_header *)0)->leh_magic) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct link_ea_header *)0)->leh_magic));
+ LASSERTF((int)offsetof(struct link_ea_header, leh_reccount) == 4, "found %lld\n",
+ (long long)(int)offsetof(struct link_ea_header, leh_reccount));
+ LASSERTF((int)sizeof(((struct link_ea_header *)0)->leh_reccount) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct link_ea_header *)0)->leh_reccount));
+ LASSERTF((int)offsetof(struct link_ea_header, leh_len) == 8, "found %lld\n",
+ (long long)(int)offsetof(struct link_ea_header, leh_len));
+ LASSERTF((int)sizeof(((struct link_ea_header *)0)->leh_len) == 8, "found %lld\n",
+ (long long)(int)sizeof(((struct link_ea_header *)0)->leh_len));
+ LASSERTF((int)offsetof(struct link_ea_header, padding1) == 16, "found %lld\n",
+ (long long)(int)offsetof(struct link_ea_header, padding1));
+ LASSERTF((int)sizeof(((struct link_ea_header *)0)->padding1) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct link_ea_header *)0)->padding1));
+ LASSERTF((int)offsetof(struct link_ea_header, padding2) == 20, "found %lld\n",
+ (long long)(int)offsetof(struct link_ea_header, padding2));
+ LASSERTF((int)sizeof(((struct link_ea_header *)0)->padding2) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct link_ea_header *)0)->padding2));
+ CLASSERT(LINK_EA_MAGIC == 0x11EAF1DFUL);
+
+ /* Checks for struct link_ea_entry */
+ LASSERTF((int)sizeof(struct link_ea_entry) == 18, "found %lld\n",
+ (long long)(int)sizeof(struct link_ea_entry));
+ LASSERTF((int)offsetof(struct link_ea_entry, lee_reclen) == 0, "found %lld\n",
+ (long long)(int)offsetof(struct link_ea_entry, lee_reclen));
+ LASSERTF((int)sizeof(((struct link_ea_entry *)0)->lee_reclen) == 2, "found %lld\n",
+ (long long)(int)sizeof(((struct link_ea_entry *)0)->lee_reclen));
+ LASSERTF((int)offsetof(struct link_ea_entry, lee_parent_fid) == 2, "found %lld\n",
+ (long long)(int)offsetof(struct link_ea_entry, lee_parent_fid));
+ LASSERTF((int)sizeof(((struct link_ea_entry *)0)->lee_parent_fid) == 16, "found %lld\n",
+ (long long)(int)sizeof(((struct link_ea_entry *)0)->lee_parent_fid));
+ LASSERTF((int)offsetof(struct link_ea_entry, lee_name) == 18, "found %lld\n",
+ (long long)(int)offsetof(struct link_ea_entry, lee_name));
+ LASSERTF((int)sizeof(((struct link_ea_entry *)0)->lee_name) == 0, "found %lld\n",
+ (long long)(int)sizeof(((struct link_ea_entry *)0)->lee_name));
+
+ /* Checks for struct layout_intent */
+ LASSERTF((int)sizeof(struct layout_intent) == 24, "found %lld\n",
+ (long long)(int)sizeof(struct layout_intent));
+ LASSERTF((int)offsetof(struct layout_intent, li_opc) == 0, "found %lld\n",
+ (long long)(int)offsetof(struct layout_intent, li_opc));
+ LASSERTF((int)sizeof(((struct layout_intent *)0)->li_opc) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct layout_intent *)0)->li_opc));
+ LASSERTF((int)offsetof(struct layout_intent, li_flags) == 4, "found %lld\n",
+ (long long)(int)offsetof(struct layout_intent, li_flags));
+ LASSERTF((int)sizeof(((struct layout_intent *)0)->li_flags) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct layout_intent *)0)->li_flags));
+ LASSERTF((int)offsetof(struct layout_intent, li_start) == 8, "found %lld\n",
+ (long long)(int)offsetof(struct layout_intent, li_start));
+ LASSERTF((int)sizeof(((struct layout_intent *)0)->li_start) == 8, "found %lld\n",
+ (long long)(int)sizeof(((struct layout_intent *)0)->li_start));
+ LASSERTF((int)offsetof(struct layout_intent, li_end) == 16, "found %lld\n",
+ (long long)(int)offsetof(struct layout_intent, li_end));
+ LASSERTF((int)sizeof(((struct layout_intent *)0)->li_end) == 8, "found %lld\n",
+ (long long)(int)sizeof(((struct layout_intent *)0)->li_end));
+ LASSERTF(LAYOUT_INTENT_ACCESS == 0, "found %lld\n",
+ (long long)LAYOUT_INTENT_ACCESS);
+ LASSERTF(LAYOUT_INTENT_READ == 1, "found %lld\n",
+ (long long)LAYOUT_INTENT_READ);
+ LASSERTF(LAYOUT_INTENT_WRITE == 2, "found %lld\n",
+ (long long)LAYOUT_INTENT_WRITE);
+ LASSERTF(LAYOUT_INTENT_GLIMPSE == 3, "found %lld\n",
+ (long long)LAYOUT_INTENT_GLIMPSE);
+ LASSERTF(LAYOUT_INTENT_TRUNC == 4, "found %lld\n",
+ (long long)LAYOUT_INTENT_TRUNC);
+ LASSERTF(LAYOUT_INTENT_RELEASE == 5, "found %lld\n",
+ (long long)LAYOUT_INTENT_RELEASE);
+ LASSERTF(LAYOUT_INTENT_RESTORE == 6, "found %lld\n",
+ (long long)LAYOUT_INTENT_RESTORE);
+
+ /* Checks for struct hsm_action_item */
+ LASSERTF((int)sizeof(struct hsm_action_item) == 72, "found %lld\n",
+ (long long)(int)sizeof(struct hsm_action_item));
+ LASSERTF((int)offsetof(struct hsm_action_item, hai_len) == 0, "found %lld\n",
+ (long long)(int)offsetof(struct hsm_action_item, hai_len));
+ LASSERTF((int)sizeof(((struct hsm_action_item *)0)->hai_len) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct hsm_action_item *)0)->hai_len));
+ LASSERTF((int)offsetof(struct hsm_action_item, hai_action) == 4, "found %lld\n",
+ (long long)(int)offsetof(struct hsm_action_item, hai_action));
+ LASSERTF((int)sizeof(((struct hsm_action_item *)0)->hai_action) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct hsm_action_item *)0)->hai_action));
+ LASSERTF((int)offsetof(struct hsm_action_item, hai_fid) == 8, "found %lld\n",
+ (long long)(int)offsetof(struct hsm_action_item, hai_fid));
+ LASSERTF((int)sizeof(((struct hsm_action_item *)0)->hai_fid) == 16, "found %lld\n",
+ (long long)(int)sizeof(((struct hsm_action_item *)0)->hai_fid));
+ LASSERTF((int)offsetof(struct hsm_action_item, hai_dfid) == 24, "found %lld\n",
+ (long long)(int)offsetof(struct hsm_action_item, hai_dfid));
+ LASSERTF((int)sizeof(((struct hsm_action_item *)0)->hai_dfid) == 16, "found %lld\n",
+ (long long)(int)sizeof(((struct hsm_action_item *)0)->hai_dfid));
+ LASSERTF((int)offsetof(struct hsm_action_item, hai_extent) == 40, "found %lld\n",
+ (long long)(int)offsetof(struct hsm_action_item, hai_extent));
+ LASSERTF((int)sizeof(((struct hsm_action_item *)0)->hai_extent) == 16, "found %lld\n",
+ (long long)(int)sizeof(((struct hsm_action_item *)0)->hai_extent));
+ LASSERTF((int)offsetof(struct hsm_action_item, hai_cookie) == 56, "found %lld\n",
+ (long long)(int)offsetof(struct hsm_action_item, hai_cookie));
+ LASSERTF((int)sizeof(((struct hsm_action_item *)0)->hai_cookie) == 8, "found %lld\n",
+ (long long)(int)sizeof(((struct hsm_action_item *)0)->hai_cookie));
+ LASSERTF((int)offsetof(struct hsm_action_item, hai_gid) == 64, "found %lld\n",
+ (long long)(int)offsetof(struct hsm_action_item, hai_gid));
+ LASSERTF((int)sizeof(((struct hsm_action_item *)0)->hai_gid) == 8, "found %lld\n",
+ (long long)(int)sizeof(((struct hsm_action_item *)0)->hai_gid));
+ LASSERTF((int)offsetof(struct hsm_action_item, hai_data) == 72, "found %lld\n",
+ (long long)(int)offsetof(struct hsm_action_item, hai_data));
+ LASSERTF((int)sizeof(((struct hsm_action_item *)0)->hai_data) == 0, "found %lld\n",
+ (long long)(int)sizeof(((struct hsm_action_item *)0)->hai_data));
+
+ /* Checks for struct hsm_action_list */
+ LASSERTF((int)sizeof(struct hsm_action_list) == 32, "found %lld\n",
+ (long long)(int)sizeof(struct hsm_action_list));
+ LASSERTF((int)offsetof(struct hsm_action_list, hal_version) == 0, "found %lld\n",
+ (long long)(int)offsetof(struct hsm_action_list, hal_version));
+ LASSERTF((int)sizeof(((struct hsm_action_list *)0)->hal_version) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct hsm_action_list *)0)->hal_version));
+ LASSERTF((int)offsetof(struct hsm_action_list, hal_count) == 4, "found %lld\n",
+ (long long)(int)offsetof(struct hsm_action_list, hal_count));
+ LASSERTF((int)sizeof(((struct hsm_action_list *)0)->hal_count) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct hsm_action_list *)0)->hal_count));
+ LASSERTF((int)offsetof(struct hsm_action_list, hal_compound_id) == 8, "found %lld\n",
+ (long long)(int)offsetof(struct hsm_action_list, hal_compound_id));
+ LASSERTF((int)sizeof(((struct hsm_action_list *)0)->hal_compound_id) == 8, "found %lld\n",
+ (long long)(int)sizeof(((struct hsm_action_list *)0)->hal_compound_id));
+ LASSERTF((int)offsetof(struct hsm_action_list, hal_flags) == 16, "found %lld\n",
+ (long long)(int)offsetof(struct hsm_action_list, hal_flags));
+ LASSERTF((int)sizeof(((struct hsm_action_list *)0)->hal_flags) == 8, "found %lld\n",
+ (long long)(int)sizeof(((struct hsm_action_list *)0)->hal_flags));
+ LASSERTF((int)offsetof(struct hsm_action_list, hal_archive_id) == 24, "found %lld\n",
+ (long long)(int)offsetof(struct hsm_action_list, hal_archive_id));
+ LASSERTF((int)sizeof(((struct hsm_action_list *)0)->hal_archive_id) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct hsm_action_list *)0)->hal_archive_id));
+ LASSERTF((int)offsetof(struct hsm_action_list, padding1) == 28, "found %lld\n",
+ (long long)(int)offsetof(struct hsm_action_list, padding1));
+ LASSERTF((int)sizeof(((struct hsm_action_list *)0)->padding1) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct hsm_action_list *)0)->padding1));
+ LASSERTF((int)offsetof(struct hsm_action_list, hal_fsname) == 32, "found %lld\n",
+ (long long)(int)offsetof(struct hsm_action_list, hal_fsname));
+ LASSERTF((int)sizeof(((struct hsm_action_list *)0)->hal_fsname) == 0, "found %lld\n",
+ (long long)(int)sizeof(((struct hsm_action_list *)0)->hal_fsname));
+
+ /* Checks for struct hsm_progress */
+ LASSERTF((int)sizeof(struct hsm_progress) == 48, "found %lld\n",
+ (long long)(int)sizeof(struct hsm_progress));
+ LASSERTF((int)offsetof(struct hsm_progress, hp_fid) == 0, "found %lld\n",
+ (long long)(int)offsetof(struct hsm_progress, hp_fid));
+ LASSERTF((int)sizeof(((struct hsm_progress *)0)->hp_fid) == 16, "found %lld\n",
+ (long long)(int)sizeof(((struct hsm_progress *)0)->hp_fid));
+ LASSERTF((int)offsetof(struct hsm_progress, hp_cookie) == 16, "found %lld\n",
+ (long long)(int)offsetof(struct hsm_progress, hp_cookie));
+ LASSERTF((int)sizeof(((struct hsm_progress *)0)->hp_cookie) == 8, "found %lld\n",
+ (long long)(int)sizeof(((struct hsm_progress *)0)->hp_cookie));
+ LASSERTF((int)offsetof(struct hsm_progress, hp_extent) == 24, "found %lld\n",
+ (long long)(int)offsetof(struct hsm_progress, hp_extent));
+ LASSERTF((int)sizeof(((struct hsm_progress *)0)->hp_extent) == 16, "found %lld\n",
+ (long long)(int)sizeof(((struct hsm_progress *)0)->hp_extent));
+ LASSERTF((int)offsetof(struct hsm_progress, hp_flags) == 40, "found %lld\n",
+ (long long)(int)offsetof(struct hsm_progress, hp_flags));
+ LASSERTF((int)sizeof(((struct hsm_progress *)0)->hp_flags) == 2, "found %lld\n",
+ (long long)(int)sizeof(((struct hsm_progress *)0)->hp_flags));
+ LASSERTF((int)offsetof(struct hsm_progress, hp_errval) == 42, "found %lld\n",
+ (long long)(int)offsetof(struct hsm_progress, hp_errval));
+ LASSERTF((int)sizeof(((struct hsm_progress *)0)->hp_errval) == 2, "found %lld\n",
+ (long long)(int)sizeof(((struct hsm_progress *)0)->hp_errval));
+ LASSERTF((int)offsetof(struct hsm_progress, padding) == 44, "found %lld\n",
+ (long long)(int)offsetof(struct hsm_progress, padding));
+ LASSERTF((int)sizeof(((struct hsm_progress *)0)->padding) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct hsm_progress *)0)->padding));
+ LASSERTF(HP_FLAG_COMPLETED == 0x01, "found 0x%.8x\n",
+ HP_FLAG_COMPLETED);
+ LASSERTF(HP_FLAG_RETRY == 0x02, "found 0x%.8x\n",
+ HP_FLAG_RETRY);
+
+ LASSERTF((int)offsetof(struct hsm_copy, hc_data_version) == 0, "found %lld\n",
+ (long long)(int)offsetof(struct hsm_copy, hc_data_version));
+ LASSERTF((int)sizeof(((struct hsm_copy *)0)->hc_data_version) == 8, "found %lld\n",
+ (long long)(int)sizeof(((struct hsm_copy *)0)->hc_data_version));
+ LASSERTF((int)offsetof(struct hsm_copy, hc_flags) == 8, "found %lld\n",
+ (long long)(int)offsetof(struct hsm_copy, hc_flags));
+ LASSERTF((int)sizeof(((struct hsm_copy *)0)->hc_flags) == 2, "found %lld\n",
+ (long long)(int)sizeof(((struct hsm_copy *)0)->hc_flags));
+ LASSERTF((int)offsetof(struct hsm_copy, hc_errval) == 10, "found %lld\n",
+ (long long)(int)offsetof(struct hsm_copy, hc_errval));
+ LASSERTF((int)sizeof(((struct hsm_copy *)0)->hc_errval) == 2, "found %lld\n",
+ (long long)(int)sizeof(((struct hsm_copy *)0)->hc_errval));
+ LASSERTF((int)offsetof(struct hsm_copy, padding) == 12, "found %lld\n",
+ (long long)(int)offsetof(struct hsm_copy, padding));
+ LASSERTF((int)sizeof(((struct hsm_copy *)0)->padding) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct hsm_copy *)0)->padding));
+ LASSERTF((int)offsetof(struct hsm_copy, hc_hai) == 16, "found %lld\n",
+ (long long)(int)offsetof(struct hsm_copy, hc_hai));
+ LASSERTF((int)sizeof(((struct hsm_copy *)0)->hc_hai) == 72, "found %lld\n",
+ (long long)(int)sizeof(((struct hsm_copy *)0)->hc_hai));
+
+ /* Checks for struct hsm_progress_kernel */
+ LASSERTF((int)sizeof(struct hsm_progress_kernel) == 64, "found %lld\n",
+ (long long)(int)sizeof(struct hsm_progress_kernel));
+ LASSERTF((int)offsetof(struct hsm_progress_kernel, hpk_fid) == 0, "found %lld\n",
+ (long long)(int)offsetof(struct hsm_progress_kernel, hpk_fid));
+ LASSERTF((int)sizeof(((struct hsm_progress_kernel *)0)->hpk_fid) == 16, "found %lld\n",
+ (long long)(int)sizeof(((struct hsm_progress_kernel *)0)->hpk_fid));
+ LASSERTF((int)offsetof(struct hsm_progress_kernel, hpk_cookie) == 16, "found %lld\n",
+ (long long)(int)offsetof(struct hsm_progress_kernel, hpk_cookie));
+ LASSERTF((int)sizeof(((struct hsm_progress_kernel *)0)->hpk_cookie) == 8, "found %lld\n",
+ (long long)(int)sizeof(((struct hsm_progress_kernel *)0)->hpk_cookie));
+ LASSERTF((int)offsetof(struct hsm_progress_kernel, hpk_extent) == 24, "found %lld\n",
+ (long long)(int)offsetof(struct hsm_progress_kernel, hpk_extent));
+ LASSERTF((int)sizeof(((struct hsm_progress_kernel *)0)->hpk_extent) == 16, "found %lld\n",
+ (long long)(int)sizeof(((struct hsm_progress_kernel *)0)->hpk_extent));
+ LASSERTF((int)offsetof(struct hsm_progress_kernel, hpk_flags) == 40, "found %lld\n",
+ (long long)(int)offsetof(struct hsm_progress_kernel, hpk_flags));
+ LASSERTF((int)sizeof(((struct hsm_progress_kernel *)0)->hpk_flags) == 2, "found %lld\n",
+ (long long)(int)sizeof(((struct hsm_progress_kernel *)0)->hpk_flags));
+ LASSERTF((int)offsetof(struct hsm_progress_kernel, hpk_errval) == 42, "found %lld\n",
+ (long long)(int)offsetof(struct hsm_progress_kernel, hpk_errval));
+ LASSERTF((int)sizeof(((struct hsm_progress_kernel *)0)->hpk_errval) == 2, "found %lld\n",
+ (long long)(int)sizeof(((struct hsm_progress_kernel *)0)->hpk_errval));
+ LASSERTF((int)offsetof(struct hsm_progress_kernel, hpk_padding1) == 44, "found %lld\n",
+ (long long)(int)offsetof(struct hsm_progress_kernel, hpk_padding1));
+ LASSERTF((int)sizeof(((struct hsm_progress_kernel *)0)->hpk_padding1) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct hsm_progress_kernel *)0)->hpk_padding1));
+ LASSERTF((int)offsetof(struct hsm_progress_kernel, hpk_data_version) == 48, "found %lld\n",
+ (long long)(int)offsetof(struct hsm_progress_kernel, hpk_data_version));
+ LASSERTF((int)sizeof(((struct hsm_progress_kernel *)0)->hpk_data_version) == 8, "found %lld\n",
+ (long long)(int)sizeof(((struct hsm_progress_kernel *)0)->hpk_data_version));
+ LASSERTF((int)offsetof(struct hsm_progress_kernel, hpk_padding2) == 56, "found %lld\n",
+ (long long)(int)offsetof(struct hsm_progress_kernel, hpk_padding2));
+ LASSERTF((int)sizeof(((struct hsm_progress_kernel *)0)->hpk_padding2) == 8, "found %lld\n",
+ (long long)(int)sizeof(((struct hsm_progress_kernel *)0)->hpk_padding2));
+
+ /* Checks for struct hsm_user_item */
+ LASSERTF((int)sizeof(struct hsm_user_item) == 32, "found %lld\n",
+ (long long)(int)sizeof(struct hsm_user_item));
+ LASSERTF((int)offsetof(struct hsm_user_item, hui_fid) == 0, "found %lld\n",
+ (long long)(int)offsetof(struct hsm_user_item, hui_fid));
+ LASSERTF((int)sizeof(((struct hsm_user_item *)0)->hui_fid) == 16, "found %lld\n",
+ (long long)(int)sizeof(((struct hsm_user_item *)0)->hui_fid));
+ LASSERTF((int)offsetof(struct hsm_user_item, hui_extent) == 16, "found %lld\n",
+ (long long)(int)offsetof(struct hsm_user_item, hui_extent));
+ LASSERTF((int)sizeof(((struct hsm_user_item *)0)->hui_extent) == 16, "found %lld\n",
+ (long long)(int)sizeof(((struct hsm_user_item *)0)->hui_extent));
+
+ /* Checks for struct hsm_user_state */
+ LASSERTF((int)sizeof(struct hsm_user_state) == 32, "found %lld\n",
+ (long long)(int)sizeof(struct hsm_user_state));
+ LASSERTF((int)offsetof(struct hsm_user_state, hus_states) == 0, "found %lld\n",
+ (long long)(int)offsetof(struct hsm_user_state, hus_states));
+ LASSERTF((int)sizeof(((struct hsm_user_state *)0)->hus_states) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct hsm_user_state *)0)->hus_states));
+ LASSERTF((int)offsetof(struct hsm_user_state, hus_archive_id) == 4, "found %lld\n",
+ (long long)(int)offsetof(struct hsm_user_state, hus_archive_id));
+ LASSERTF((int)sizeof(((struct hsm_user_state *)0)->hus_archive_id) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct hsm_user_state *)0)->hus_archive_id));
+ LASSERTF((int)offsetof(struct hsm_user_state, hus_in_progress_state) == 8, "found %lld\n",
+ (long long)(int)offsetof(struct hsm_user_state, hus_in_progress_state));
+ LASSERTF((int)sizeof(((struct hsm_user_state *)0)->hus_in_progress_state) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct hsm_user_state *)0)->hus_in_progress_state));
+ LASSERTF((int)offsetof(struct hsm_user_state, hus_in_progress_action) == 12, "found %lld\n",
+ (long long)(int)offsetof(struct hsm_user_state, hus_in_progress_action));
+ LASSERTF((int)sizeof(((struct hsm_user_state *)0)->hus_in_progress_action) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct hsm_user_state *)0)->hus_in_progress_action));
+ LASSERTF((int)offsetof(struct hsm_user_state, hus_in_progress_location) == 16, "found %lld\n",
+ (long long)(int)offsetof(struct hsm_user_state, hus_in_progress_location));
+ LASSERTF((int)sizeof(((struct hsm_user_state *)0)->hus_in_progress_location) == 16, "found %lld\n",
+ (long long)(int)sizeof(((struct hsm_user_state *)0)->hus_in_progress_location));
+
+ /* Checks for struct hsm_state_set */
+ LASSERTF((int)sizeof(struct hsm_state_set) == 24, "found %lld\n",
+ (long long)(int)sizeof(struct hsm_state_set));
+ LASSERTF((int)offsetof(struct hsm_state_set, hss_valid) == 0, "found %lld\n",
+ (long long)(int)offsetof(struct hsm_state_set, hss_valid));
+ LASSERTF((int)sizeof(((struct hsm_state_set *)0)->hss_valid) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct hsm_state_set *)0)->hss_valid));
+ LASSERTF((int)offsetof(struct hsm_state_set, hss_archive_id) == 4, "found %lld\n",
+ (long long)(int)offsetof(struct hsm_state_set, hss_archive_id));
+ LASSERTF((int)sizeof(((struct hsm_state_set *)0)->hss_archive_id) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct hsm_state_set *)0)->hss_archive_id));
+ LASSERTF((int)offsetof(struct hsm_state_set, hss_setmask) == 8, "found %lld\n",
+ (long long)(int)offsetof(struct hsm_state_set, hss_setmask));
+ LASSERTF((int)sizeof(((struct hsm_state_set *)0)->hss_setmask) == 8, "found %lld\n",
+ (long long)(int)sizeof(((struct hsm_state_set *)0)->hss_setmask));
+ LASSERTF((int)offsetof(struct hsm_state_set, hss_clearmask) == 16, "found %lld\n",
+ (long long)(int)offsetof(struct hsm_state_set, hss_clearmask));
+ LASSERTF((int)sizeof(((struct hsm_state_set *)0)->hss_clearmask) == 8, "found %lld\n",
+ (long long)(int)sizeof(((struct hsm_state_set *)0)->hss_clearmask));
+
+ /* Checks for struct hsm_current_action */
+ LASSERTF((int)sizeof(struct hsm_current_action) == 24, "found %lld\n",
+ (long long)(int)sizeof(struct hsm_current_action));
+ LASSERTF((int)offsetof(struct hsm_current_action, hca_state) == 0, "found %lld\n",
+ (long long)(int)offsetof(struct hsm_current_action, hca_state));
+ LASSERTF((int)sizeof(((struct hsm_current_action *)0)->hca_state) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct hsm_current_action *)0)->hca_state));
+ LASSERTF((int)offsetof(struct hsm_current_action, hca_action) == 4, "found %lld\n",
+ (long long)(int)offsetof(struct hsm_current_action, hca_action));
+ LASSERTF((int)sizeof(((struct hsm_current_action *)0)->hca_action) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct hsm_current_action *)0)->hca_action));
+ LASSERTF((int)offsetof(struct hsm_current_action, hca_location) == 8, "found %lld\n",
+ (long long)(int)offsetof(struct hsm_current_action, hca_location));
+ LASSERTF((int)sizeof(((struct hsm_current_action *)0)->hca_location) == 16, "found %lld\n",
+ (long long)(int)sizeof(((struct hsm_current_action *)0)->hca_location));
+
+ /* Checks for struct hsm_request */
+ LASSERTF((int)sizeof(struct hsm_request) == 24, "found %lld\n",
+ (long long)(int)sizeof(struct hsm_request));
+ LASSERTF((int)offsetof(struct hsm_request, hr_action) == 0, "found %lld\n",
+ (long long)(int)offsetof(struct hsm_request, hr_action));
+ LASSERTF((int)sizeof(((struct hsm_request *)0)->hr_action) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct hsm_request *)0)->hr_action));
+ LASSERTF((int)offsetof(struct hsm_request, hr_archive_id) == 4, "found %lld\n",
+ (long long)(int)offsetof(struct hsm_request, hr_archive_id));
+ LASSERTF((int)sizeof(((struct hsm_request *)0)->hr_archive_id) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct hsm_request *)0)->hr_archive_id));
+ LASSERTF((int)offsetof(struct hsm_request, hr_flags) == 8, "found %lld\n",
+ (long long)(int)offsetof(struct hsm_request, hr_flags));
+ LASSERTF((int)sizeof(((struct hsm_request *)0)->hr_flags) == 8, "found %lld\n",
+ (long long)(int)sizeof(((struct hsm_request *)0)->hr_flags));
+ LASSERTF((int)offsetof(struct hsm_request, hr_itemcount) == 16, "found %lld\n",
+ (long long)(int)offsetof(struct hsm_request, hr_itemcount));
+ LASSERTF((int)sizeof(((struct hsm_request *)0)->hr_itemcount) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct hsm_request *)0)->hr_itemcount));
+ LASSERTF((int)offsetof(struct hsm_request, hr_data_len) == 20, "found %lld\n",
+ (long long)(int)offsetof(struct hsm_request, hr_data_len));
+ LASSERTF((int)sizeof(((struct hsm_request *)0)->hr_data_len) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct hsm_request *)0)->hr_data_len));
+ LASSERTF(HSM_FORCE_ACTION == 0x00000001UL, "found 0x%.8xUL\n",
+ (unsigned)HSM_FORCE_ACTION);
+ LASSERTF(HSM_GHOST_COPY == 0x00000002UL, "found 0x%.8xUL\n",
+ (unsigned)HSM_GHOST_COPY);
+
+ /* Checks for struct hsm_user_request */
+ LASSERTF((int)sizeof(struct hsm_user_request) == 24, "found %lld\n",
+ (long long)(int)sizeof(struct hsm_user_request));
+ LASSERTF((int)offsetof(struct hsm_user_request, hur_request) == 0, "found %lld\n",
+ (long long)(int)offsetof(struct hsm_user_request, hur_request));
+ LASSERTF((int)sizeof(((struct hsm_user_request *)0)->hur_request) == 24, "found %lld\n",
+ (long long)(int)sizeof(((struct hsm_user_request *)0)->hur_request));
+ LASSERTF((int)offsetof(struct hsm_user_request, hur_user_item) == 24, "found %lld\n",
+ (long long)(int)offsetof(struct hsm_user_request, hur_user_item));
+ LASSERTF((int)sizeof(((struct hsm_user_request *)0)->hur_user_item) == 0, "found %lld\n",
+ (long long)(int)sizeof(((struct hsm_user_request *)0)->hur_user_item));
+
+ /* Checks for struct hsm_user_import */
+ LASSERTF(sizeof(struct hsm_user_import) == 48, "found %lld\n",
+ (long long)sizeof(struct hsm_user_import));
+ LASSERTF(offsetof(struct hsm_user_import, hui_size) == 0,
+ "found %lld\n",
+ (long long)offsetof(struct hsm_user_import, hui_size));
+ LASSERTF(sizeof(((struct hsm_user_import *)0)->hui_size) == 8,
+ "found %lld\n",
+ (long long)sizeof(((struct hsm_user_import *)0)->hui_size));
+ LASSERTF(offsetof(struct hsm_user_import, hui_uid) == 32,
+ "found %lld\n",
+ (long long)offsetof(struct hsm_user_import, hui_uid));
+ LASSERTF(sizeof(((struct hsm_user_import *)0)->hui_uid) == 4,
+ "found %lld\n",
+ (long long)sizeof(((struct hsm_user_import *)0)->hui_uid));
+ LASSERTF(offsetof(struct hsm_user_import, hui_gid) == 36,
+ "found %lld\n",
+ (long long)offsetof(struct hsm_user_import, hui_gid));
+ LASSERTF(sizeof(((struct hsm_user_import *)0)->hui_gid) == 4,
+ "found %lld\n",
+ (long long)sizeof(((struct hsm_user_import *)0)->hui_gid));
+ LASSERTF(offsetof(struct hsm_user_import, hui_mode) == 40,
+ "found %lld\n",
+ (long long)offsetof(struct hsm_user_import, hui_mode));
+ LASSERTF(sizeof(((struct hsm_user_import *)0)->hui_mode) == 4,
+ "found %lld\n",
+ (long long)sizeof(((struct hsm_user_import *)0)->hui_mode));
+ LASSERTF(offsetof(struct hsm_user_import, hui_atime) == 8,
+ "found %lld\n",
+ (long long)offsetof(struct hsm_user_import, hui_atime));
+ LASSERTF(sizeof(((struct hsm_user_import *)0)->hui_atime) == 8,
+ "found %lld\n",
+ (long long)sizeof(((struct hsm_user_import *)0)->hui_atime));
+ LASSERTF(offsetof(struct hsm_user_import, hui_atime_ns) == 24,
+ "found %lld\n",
+ (long long)(int)offsetof(struct hsm_user_import, hui_atime_ns));
+ LASSERTF(sizeof(((struct hsm_user_import *)0)->hui_atime_ns) == 4,
+ "found %lld\n",
+ (long long)sizeof(((struct hsm_user_import *)0)->hui_atime_ns));
+ LASSERTF(offsetof(struct hsm_user_import, hui_mtime) == 16,
+ "found %lld\n",
+ (long long)offsetof(struct hsm_user_import, hui_mtime));
+ LASSERTF(sizeof(((struct hsm_user_import *)0)->hui_mtime) == 8,
+ "found %lld\n",
+ (long long)sizeof(((struct hsm_user_import *)0)->hui_mtime));
+ LASSERTF(offsetof(struct hsm_user_import, hui_mtime_ns) == 28,
+ "found %lld\n",
+ (long long)offsetof(struct hsm_user_import, hui_mtime_ns));
+ LASSERTF(sizeof(((struct hsm_user_import *)0)->hui_mtime_ns) == 4,
+ "found %lld\n",
+ (long long)sizeof(((struct hsm_user_import *)0)->hui_mtime_ns));
+ LASSERTF(offsetof(struct hsm_user_import, hui_archive_id) == 44,
+ "found %lld\n",
+ (long long)offsetof(struct hsm_user_import, hui_archive_id));
+ LASSERTF(sizeof(((struct hsm_user_import *)0)->hui_archive_id) == 4,
+ "found %lld\n",
+ (long long)sizeof(((struct hsm_user_import *)0)->hui_archive_id));
+
+ /* Checks for struct update_buf */
+ LASSERTF((int)sizeof(struct update_buf) == 8, "found %lld\n",
+ (long long)(int)sizeof(struct update_buf));
+ LASSERTF((int)offsetof(struct update_buf, ub_magic) == 0, "found %lld\n",
+ (long long)(int)offsetof(struct update_buf, ub_magic));
+ LASSERTF((int)sizeof(((struct update_buf *)0)->ub_magic) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct update_buf *)0)->ub_magic));
+ LASSERTF((int)offsetof(struct update_buf, ub_count) == 4, "found %lld\n",
+ (long long)(int)offsetof(struct update_buf, ub_count));
+ LASSERTF((int)sizeof(((struct update_buf *)0)->ub_count) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct update_buf *)0)->ub_count));
+ LASSERTF((int)offsetof(struct update_buf, ub_bufs) == 8, "found %lld\n",
+ (long long)(int)offsetof(struct update_buf, ub_bufs));
+ LASSERTF((int)sizeof(((struct update_buf *)0)->ub_bufs) == 0, "found %lld\n",
+ (long long)(int)sizeof(((struct update_buf *)0)->ub_bufs));
+
+ /* Checks for struct update_reply */
+ LASSERTF((int)sizeof(struct update_reply) == 8, "found %lld\n",
+ (long long)(int)sizeof(struct update_reply));
+ LASSERTF((int)offsetof(struct update_reply, ur_version) == 0, "found %lld\n",
+ (long long)(int)offsetof(struct update_reply, ur_version));
+ LASSERTF((int)sizeof(((struct update_reply *)0)->ur_version) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct update_reply *)0)->ur_version));
+ LASSERTF((int)offsetof(struct update_reply, ur_count) == 4, "found %lld\n",
+ (long long)(int)offsetof(struct update_reply, ur_count));
+ LASSERTF((int)sizeof(((struct update_reply *)0)->ur_count) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct update_reply *)0)->ur_count));
+ LASSERTF((int)offsetof(struct update_reply, ur_lens) == 8, "found %lld\n",
+ (long long)(int)offsetof(struct update_reply, ur_lens));
+ LASSERTF((int)sizeof(((struct update_reply *)0)->ur_lens) == 0, "found %lld\n",
+ (long long)(int)sizeof(((struct update_reply *)0)->ur_lens));
+
+ /* Checks for struct update */
+ LASSERTF((int)sizeof(struct update) == 56, "found %lld\n",
+ (long long)(int)sizeof(struct update));
+ LASSERTF((int)offsetof(struct update, u_type) == 0, "found %lld\n",
+ (long long)(int)offsetof(struct update, u_type));
+ LASSERTF((int)sizeof(((struct update *)0)->u_type) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct update *)0)->u_type));
+ LASSERTF((int)offsetof(struct update, u_batchid) == 4, "found %lld\n",
+ (long long)(int)offsetof(struct update, u_batchid));
+ LASSERTF((int)sizeof(((struct update *)0)->u_batchid) == 4, "found %lld\n",
+ (long long)(int)sizeof(((struct update *)0)->u_batchid));
+ LASSERTF((int)offsetof(struct update, u_fid) == 8, "found %lld\n",
+ (long long)(int)offsetof(struct update, u_fid));
+ LASSERTF((int)sizeof(((struct update *)0)->u_fid) == 16, "found %lld\n",
+ (long long)(int)sizeof(((struct update *)0)->u_fid));
+ LASSERTF((int)offsetof(struct update, u_lens) == 24, "found %lld\n",
+ (long long)(int)offsetof(struct update, u_lens));
+ LASSERTF((int)sizeof(((struct update *)0)->u_lens) == 32, "found %lld\n",
+ (long long)(int)sizeof(((struct update *)0)->u_lens));
+ LASSERTF((int)offsetof(struct update, u_bufs) == 56, "found %lld\n",
+ (long long)(int)offsetof(struct update, u_bufs));
+ LASSERTF((int)sizeof(((struct update *)0)->u_bufs) == 0, "found %lld\n",
+ (long long)(int)sizeof(((struct update *)0)->u_bufs));
+}
diff --git a/drivers/staging/media/Kconfig b/drivers/staging/media/Kconfig
new file mode 100644
index 000000000..96498b7fc
--- /dev/null
+++ b/drivers/staging/media/Kconfig
@@ -0,0 +1,39 @@
+menuconfig STAGING_MEDIA
+ bool "Media staging drivers"
+ default n
+ ---help---
+ This option allows you to select a number of media drivers that
+ don't have the "normal" Linux kernel quality level.
+ Most of them don't follow properly the V4L, DVB and/or RC API's,
+ so, they won't likely work fine with the existing applications.
+ That also means that, once fixed, their API's will change to match
+ the existing ones.
+
+ If you wish to work on these drivers, to help improve them, or
+ to report problems you have with them, please use the
+ linux-media@vger.kernel.org mailing list.
+
+ If in doubt, say N here.
+
+
+if STAGING_MEDIA
+
+# Please keep them in alphabetic order
+source "drivers/staging/media/bcm2048/Kconfig"
+
+source "drivers/staging/media/cxd2099/Kconfig"
+
+source "drivers/staging/media/davinci_vpfe/Kconfig"
+
+source "drivers/staging/media/dt3155v4l/Kconfig"
+
+source "drivers/staging/media/mn88472/Kconfig"
+
+source "drivers/staging/media/mn88473/Kconfig"
+
+source "drivers/staging/media/omap4iss/Kconfig"
+
+# Keep LIRC at the end, as it has sub-menus
+source "drivers/staging/media/lirc/Kconfig"
+
+endif
diff --git a/drivers/staging/media/Makefile b/drivers/staging/media/Makefile
new file mode 100644
index 000000000..a9006bcb4
--- /dev/null
+++ b/drivers/staging/media/Makefile
@@ -0,0 +1,8 @@
+obj-$(CONFIG_I2C_BCM2048) += bcm2048/
+obj-$(CONFIG_DVB_CXD2099) += cxd2099/
+obj-$(CONFIG_LIRC_STAGING) += lirc/
+obj-$(CONFIG_VIDEO_DT3155) += dt3155v4l/
+obj-$(CONFIG_VIDEO_DM365_VPFE) += davinci_vpfe/
+obj-$(CONFIG_VIDEO_OMAP4) += omap4iss/
+obj-$(CONFIG_DVB_MN88472) += mn88472/
+obj-$(CONFIG_DVB_MN88473) += mn88473/
diff --git a/drivers/staging/media/bcm2048/Kconfig b/drivers/staging/media/bcm2048/Kconfig
new file mode 100644
index 000000000..a9fc6e186
--- /dev/null
+++ b/drivers/staging/media/bcm2048/Kconfig
@@ -0,0 +1,13 @@
+#
+# Multimedia Video device configuration
+#
+
+config I2C_BCM2048
+ tristate "Broadcom BCM2048 FM Radio Receiver support"
+ depends on I2C && VIDEO_V4L2 && RADIO_ADAPTERS
+ ---help---
+ Say Y here if you want support to BCM2048 FM Radio Receiver.
+ This device driver supports only i2c bus.
+
+ To compile this driver as a module, choose M here: the
+ module will be called radio-bcm2048.
diff --git a/drivers/staging/media/bcm2048/Makefile b/drivers/staging/media/bcm2048/Makefile
new file mode 100644
index 000000000..b4f5663d1
--- /dev/null
+++ b/drivers/staging/media/bcm2048/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_I2C_BCM2048) += radio-bcm2048.o
diff --git a/drivers/staging/media/bcm2048/TODO b/drivers/staging/media/bcm2048/TODO
new file mode 100644
index 000000000..051f85dbe
--- /dev/null
+++ b/drivers/staging/media/bcm2048/TODO
@@ -0,0 +1,24 @@
+TODO:
+
+From the initial code review:
+
+The main thing you need to do is to implement all the controls using the
+control framework (see Documentation/video4linux/v4l2-controls.txt).
+Most drivers are by now converted to the control framework, so you will
+find many examples of how to do this in drivers/media/radio.
+
+The sysfs stuff should be replaced by controls as well. A lot of the RDS
+support is now available as controls (although there may well be some
+missing features, but that is easy enough to add). Since the RDS data is
+actually read() from the device I am not sure whether the RDS
+properties/controls should be there at all.
+
+Correct Coding Style, as this driver also violates several Style
+rules, and do evil tricks, like returning from a function inside a
+macro.
+
+Finally this driver should probably be split up into two parts: one
+v4l2_subdev-based core driver and one platform driver. See e.g.
+radio-si4713/si4713-i2c.c as a good example. But I would wait with that
+until the rest of the driver is cleaned up. Then I have a better idea of
+whether this is necessary or not.
diff --git a/drivers/staging/media/bcm2048/radio-bcm2048.c b/drivers/staging/media/bcm2048/radio-bcm2048.c
new file mode 100644
index 000000000..e9d0691b2
--- /dev/null
+++ b/drivers/staging/media/bcm2048/radio-bcm2048.c
@@ -0,0 +1,2708 @@
+/*
+ * drivers/staging/media/radio-bcm2048.c
+ *
+ * Driver for I2C Broadcom BCM2048 FM Radio Receiver:
+ *
+ * Copyright (C) Nokia Corporation
+ * Contact: Eero Nurkkala <ext-eero.nurkkala@nokia.com>
+ *
+ * Copyright (C) Nils Faerber <nils.faerber@kernelconcepts.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.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+/*
+ * History:
+ * Eero Nurkkala <ext-eero.nurkkala@nokia.com>
+ * Version 0.0.1
+ * - Initial implementation
+ * 2010-02-21 Nils Faerber <nils.faerber@kernelconcepts.de>
+ * Version 0.0.2
+ * - Add support for interrupt driven rds data reading
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/version.h>
+#include <linux/interrupt.h>
+#include <linux/sysfs.h>
+#include <linux/completion.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/videodev2.h>
+#include <linux/mutex.h>
+#include <linux/slab.h>
+#include <media/v4l2-common.h>
+#include <media/v4l2-ioctl.h>
+#include "radio-bcm2048.h"
+
+/* driver definitions */
+#define BCM2048_DRIVER_AUTHOR "Eero Nurkkala <ext-eero.nurkkala@nokia.com>"
+#define BCM2048_DRIVER_NAME BCM2048_NAME
+#define BCM2048_DRIVER_VERSION KERNEL_VERSION(0, 0, 1)
+#define BCM2048_DRIVER_CARD "Broadcom bcm2048 FM Radio Receiver"
+#define BCM2048_DRIVER_DESC "I2C driver for BCM2048 FM Radio Receiver"
+
+/* I2C Control Registers */
+#define BCM2048_I2C_FM_RDS_SYSTEM 0x00
+#define BCM2048_I2C_FM_CTRL 0x01
+#define BCM2048_I2C_RDS_CTRL0 0x02
+#define BCM2048_I2C_RDS_CTRL1 0x03
+#define BCM2048_I2C_FM_AUDIO_PAUSE 0x04
+#define BCM2048_I2C_FM_AUDIO_CTRL0 0x05
+#define BCM2048_I2C_FM_AUDIO_CTRL1 0x06
+#define BCM2048_I2C_FM_SEARCH_CTRL0 0x07
+#define BCM2048_I2C_FM_SEARCH_CTRL1 0x08
+#define BCM2048_I2C_FM_SEARCH_TUNE_MODE 0x09
+#define BCM2048_I2C_FM_FREQ0 0x0a
+#define BCM2048_I2C_FM_FREQ1 0x0b
+#define BCM2048_I2C_FM_AF_FREQ0 0x0c
+#define BCM2048_I2C_FM_AF_FREQ1 0x0d
+#define BCM2048_I2C_FM_CARRIER 0x0e
+#define BCM2048_I2C_FM_RSSI 0x0f
+#define BCM2048_I2C_FM_RDS_MASK0 0x10
+#define BCM2048_I2C_FM_RDS_MASK1 0x11
+#define BCM2048_I2C_FM_RDS_FLAG0 0x12
+#define BCM2048_I2C_FM_RDS_FLAG1 0x13
+#define BCM2048_I2C_RDS_WLINE 0x14
+#define BCM2048_I2C_RDS_BLKB_MATCH0 0x16
+#define BCM2048_I2C_RDS_BLKB_MATCH1 0x17
+#define BCM2048_I2C_RDS_BLKB_MASK0 0x18
+#define BCM2048_I2C_RDS_BLKB_MASK1 0x19
+#define BCM2048_I2C_RDS_PI_MATCH0 0x1a
+#define BCM2048_I2C_RDS_PI_MATCH1 0x1b
+#define BCM2048_I2C_RDS_PI_MASK0 0x1c
+#define BCM2048_I2C_RDS_PI_MASK1 0x1d
+#define BCM2048_I2C_SPARE1 0x20
+#define BCM2048_I2C_SPARE2 0x21
+#define BCM2048_I2C_FM_RDS_REV 0x28
+#define BCM2048_I2C_SLAVE_CONFIGURATION 0x29
+#define BCM2048_I2C_RDS_DATA 0x80
+#define BCM2048_I2C_FM_BEST_TUNE_MODE 0x90
+
+/* BCM2048_I2C_FM_RDS_SYSTEM */
+#define BCM2048_FM_ON 0x01
+#define BCM2048_RDS_ON 0x02
+
+/* BCM2048_I2C_FM_CTRL */
+#define BCM2048_BAND_SELECT 0x01
+#define BCM2048_STEREO_MONO_AUTO_SELECT 0x02
+#define BCM2048_STEREO_MONO_MANUAL_SELECT 0x04
+#define BCM2048_STEREO_MONO_BLEND_SWITCH 0x08
+#define BCM2048_HI_LO_INJECTION 0x10
+
+/* BCM2048_I2C_RDS_CTRL0 */
+#define BCM2048_RBDS_RDS_SELECT 0x01
+#define BCM2048_FLUSH_FIFO 0x02
+
+/* BCM2048_I2C_FM_AUDIO_PAUSE */
+#define BCM2048_AUDIO_PAUSE_RSSI_TRESH 0x0f
+#define BCM2048_AUDIO_PAUSE_DURATION 0xf0
+
+/* BCM2048_I2C_FM_AUDIO_CTRL0 */
+#define BCM2048_RF_MUTE 0x01
+#define BCM2048_MANUAL_MUTE 0x02
+#define BCM2048_DAC_OUTPUT_LEFT 0x04
+#define BCM2048_DAC_OUTPUT_RIGHT 0x08
+#define BCM2048_AUDIO_ROUTE_DAC 0x10
+#define BCM2048_AUDIO_ROUTE_I2S 0x20
+#define BCM2048_DE_EMPHASIS_SELECT 0x40
+#define BCM2048_AUDIO_BANDWIDTH_SELECT 0x80
+
+/* BCM2048_I2C_FM_SEARCH_CTRL0 */
+#define BCM2048_SEARCH_RSSI_THRESHOLD 0x7f
+#define BCM2048_SEARCH_DIRECTION 0x80
+
+/* BCM2048_I2C_FM_SEARCH_TUNE_MODE */
+#define BCM2048_FM_AUTO_SEARCH 0x03
+
+/* BCM2048_I2C_FM_RSSI */
+#define BCM2048_RSSI_VALUE 0xff
+
+/* BCM2048_I2C_FM_RDS_MASK0 */
+/* BCM2048_I2C_FM_RDS_MASK1 */
+#define BCM2048_FM_FLAG_SEARCH_TUNE_FINISHED 0x01
+#define BCM2048_FM_FLAG_SEARCH_TUNE_FAIL 0x02
+#define BCM2048_FM_FLAG_RSSI_LOW 0x04
+#define BCM2048_FM_FLAG_CARRIER_ERROR_HIGH 0x08
+#define BCM2048_FM_FLAG_AUDIO_PAUSE_INDICATION 0x10
+#define BCM2048_FLAG_STEREO_DETECTED 0x20
+#define BCM2048_FLAG_STEREO_ACTIVE 0x40
+
+/* BCM2048_I2C_RDS_DATA */
+#define BCM2048_SLAVE_ADDRESS 0x3f
+#define BCM2048_SLAVE_ENABLE 0x80
+
+/* BCM2048_I2C_FM_BEST_TUNE_MODE */
+#define BCM2048_BEST_TUNE_MODE 0x80
+
+#define BCM2048_FM_FLAG_SEARCH_TUNE_FINISHED 0x01
+#define BCM2048_FM_FLAG_SEARCH_TUNE_FAIL 0x02
+#define BCM2048_FM_FLAG_RSSI_LOW 0x04
+#define BCM2048_FM_FLAG_CARRIER_ERROR_HIGH 0x08
+#define BCM2048_FM_FLAG_AUDIO_PAUSE_INDICATION 0x10
+#define BCM2048_FLAG_STEREO_DETECTED 0x20
+#define BCM2048_FLAG_STEREO_ACTIVE 0x40
+
+#define BCM2048_RDS_FLAG_FIFO_WLINE 0x02
+#define BCM2048_RDS_FLAG_B_BLOCK_MATCH 0x08
+#define BCM2048_RDS_FLAG_SYNC_LOST 0x10
+#define BCM2048_RDS_FLAG_PI_MATCH 0x20
+
+#define BCM2048_RDS_MARK_END_BYTE0 0x7C
+#define BCM2048_RDS_MARK_END_BYTEN 0xFF
+
+#define BCM2048_FM_FLAGS_ALL (FM_FLAG_SEARCH_TUNE_FINISHED | \
+ FM_FLAG_SEARCH_TUNE_FAIL | \
+ FM_FLAG_RSSI_LOW | \
+ FM_FLAG_CARRIER_ERROR_HIGH | \
+ FM_FLAG_AUDIO_PAUSE_INDICATION | \
+ FLAG_STEREO_DETECTED | FLAG_STEREO_ACTIVE)
+
+#define BCM2048_RDS_FLAGS_ALL (RDS_FLAG_FIFO_WLINE | \
+ RDS_FLAG_B_BLOCK_MATCH | \
+ RDS_FLAG_SYNC_LOST | RDS_FLAG_PI_MATCH)
+
+#define BCM2048_DEFAULT_TIMEOUT 1500
+#define BCM2048_AUTO_SEARCH_TIMEOUT 3000
+
+
+#define BCM2048_FREQDEV_UNIT 10000
+#define BCM2048_FREQV4L2_MULTI 625
+#define dev_to_v4l2(f) ((f * BCM2048_FREQDEV_UNIT) / BCM2048_FREQV4L2_MULTI)
+#define v4l2_to_dev(f) ((f * BCM2048_FREQV4L2_MULTI) / BCM2048_FREQDEV_UNIT)
+
+#define msb(x) ((u8)((u16) x >> 8))
+#define lsb(x) ((u8)((u16) x & 0x00FF))
+#define compose_u16(msb, lsb) (((u16)msb << 8) | lsb)
+
+#define BCM2048_DEFAULT_POWERING_DELAY 20
+#define BCM2048_DEFAULT_REGION 0x02
+#define BCM2048_DEFAULT_MUTE 0x01
+#define BCM2048_DEFAULT_RSSI_THRESHOLD 0x64
+#define BCM2048_DEFAULT_RDS_WLINE 0x7E
+
+#define BCM2048_FM_SEARCH_INACTIVE 0x00
+#define BCM2048_FM_PRE_SET_MODE 0x01
+#define BCM2048_FM_AUTO_SEARCH_MODE 0x02
+#define BCM2048_FM_AF_JUMP_MODE 0x03
+
+#define BCM2048_FREQUENCY_BASE 64000
+
+#define BCM2048_POWER_ON 0x01
+#define BCM2048_POWER_OFF 0x00
+
+#define BCM2048_ITEM_ENABLED 0x01
+#define BCM2048_SEARCH_DIRECTION_UP 0x01
+
+#define BCM2048_DE_EMPHASIS_75us 75
+#define BCM2048_DE_EMPHASIS_50us 50
+
+#define BCM2048_SCAN_FAIL 0x00
+#define BCM2048_SCAN_OK 0x01
+
+#define BCM2048_FREQ_ERROR_FLOOR -20
+#define BCM2048_FREQ_ERROR_ROOF 20
+
+/* -60 dB is reported as full signal strenght */
+#define BCM2048_RSSI_LEVEL_BASE -60
+#define BCM2048_RSSI_LEVEL_ROOF -100
+#define BCM2048_RSSI_LEVEL_ROOF_NEG 100
+#define BCM2048_SIGNAL_MULTIPLIER (0xFFFF / \
+ (BCM2048_RSSI_LEVEL_ROOF_NEG + \
+ BCM2048_RSSI_LEVEL_BASE))
+
+#define BCM2048_RDS_FIFO_DUPLE_SIZE 0x03
+#define BCM2048_RDS_CRC_MASK 0x0F
+#define BCM2048_RDS_CRC_NONE 0x00
+#define BCM2048_RDS_CRC_MAX_2BITS 0x04
+#define BCM2048_RDS_CRC_LEAST_2BITS 0x08
+#define BCM2048_RDS_CRC_UNRECOVARABLE 0x0C
+
+#define BCM2048_RDS_BLOCK_MASK 0xF0
+#define BCM2048_RDS_BLOCK_A 0x00
+#define BCM2048_RDS_BLOCK_B 0x10
+#define BCM2048_RDS_BLOCK_C 0x20
+#define BCM2048_RDS_BLOCK_D 0x30
+#define BCM2048_RDS_BLOCK_C_SCORED 0x40
+#define BCM2048_RDS_BLOCK_E 0x60
+
+#define BCM2048_RDS_RT 0x20
+#define BCM2048_RDS_PS 0x00
+
+#define BCM2048_RDS_GROUP_AB_MASK 0x08
+#define BCM2048_RDS_GROUP_A 0x00
+#define BCM2048_RDS_GROUP_B 0x08
+
+#define BCM2048_RDS_RT_AB_MASK 0x10
+#define BCM2048_RDS_RT_A 0x00
+#define BCM2048_RDS_RT_B 0x10
+#define BCM2048_RDS_RT_INDEX 0x0F
+
+#define BCM2048_RDS_PS_INDEX 0x03
+
+struct rds_info {
+ u16 rds_pi;
+#define BCM2048_MAX_RDS_RT (64 + 1)
+ u8 rds_rt[BCM2048_MAX_RDS_RT];
+ u8 rds_rt_group_b;
+ u8 rds_rt_ab;
+#define BCM2048_MAX_RDS_PS (8 + 1)
+ u8 rds_ps[BCM2048_MAX_RDS_PS];
+ u8 rds_ps_group;
+ u8 rds_ps_group_cnt;
+#define BCM2048_MAX_RDS_RADIO_TEXT 255
+ u8 radio_text[BCM2048_MAX_RDS_RADIO_TEXT + 3];
+ u8 text_len;
+};
+
+struct region_info {
+ u32 bottom_frequency;
+ u32 top_frequency;
+ u8 deemphasis;
+ u8 channel_spacing;
+ u8 region;
+};
+
+struct bcm2048_device {
+ struct i2c_client *client;
+ struct video_device videodev;
+ struct work_struct work;
+ struct completion compl;
+ struct mutex mutex;
+ struct bcm2048_platform_data *platform_data;
+ struct rds_info rds_info;
+ struct region_info region_info;
+ u16 frequency;
+ u8 cache_fm_rds_system;
+ u8 cache_fm_ctrl;
+ u8 cache_fm_audio_ctrl0;
+ u8 cache_fm_search_ctrl0;
+ u8 power_state;
+ u8 rds_state;
+ u8 fifo_size;
+ u8 scan_state;
+ u8 mute_state;
+
+ /* for rds data device read */
+ wait_queue_head_t read_queue;
+ unsigned int users;
+ unsigned char rds_data_available;
+ unsigned int rd_index;
+};
+
+static int radio_nr = -1; /* radio device minor (-1 ==> auto assign) */
+module_param(radio_nr, int, 0);
+MODULE_PARM_DESC(radio_nr,
+ "Minor number for radio device (-1 ==> auto assign)");
+
+static struct region_info region_configs[] = {
+ /* USA */
+ {
+ .channel_spacing = 20,
+ .bottom_frequency = 87500,
+ .top_frequency = 108000,
+ .deemphasis = 75,
+ .region = 0,
+ },
+ /* Australia */
+ {
+ .channel_spacing = 20,
+ .bottom_frequency = 87500,
+ .top_frequency = 108000,
+ .deemphasis = 50,
+ .region = 1,
+ },
+ /* Europe */
+ {
+ .channel_spacing = 10,
+ .bottom_frequency = 87500,
+ .top_frequency = 108000,
+ .deemphasis = 50,
+ .region = 2,
+ },
+ /* Japan */
+ {
+ .channel_spacing = 10,
+ .bottom_frequency = 76000,
+ .top_frequency = 90000,
+ .deemphasis = 50,
+ .region = 3,
+ },
+ /* Japan wide band */
+ {
+ .channel_spacing = 10,
+ .bottom_frequency = 76000,
+ .top_frequency = 108000,
+ .deemphasis = 50,
+ .region = 4,
+ },
+};
+
+/*
+ * I2C Interface read / write
+ */
+static int bcm2048_send_command(struct bcm2048_device *bdev, unsigned int reg,
+ unsigned int value)
+{
+ struct i2c_client *client = bdev->client;
+ u8 data[2];
+
+ if (!bdev->power_state) {
+ dev_err(&bdev->client->dev, "bcm2048: chip not powered!\n");
+ return -EIO;
+ }
+
+ data[0] = reg & 0xff;
+ data[1] = value & 0xff;
+
+ if (i2c_master_send(client, data, 2) == 2)
+ return 0;
+
+ dev_err(&bdev->client->dev, "BCM I2C error!\n");
+ dev_err(&bdev->client->dev, "Is Bluetooth up and running?\n");
+ return -EIO;
+}
+
+static int bcm2048_recv_command(struct bcm2048_device *bdev, unsigned int reg,
+ u8 *value)
+{
+ struct i2c_client *client = bdev->client;
+
+ if (!bdev->power_state) {
+ dev_err(&bdev->client->dev, "bcm2048: chip not powered!\n");
+ return -EIO;
+ }
+
+ value[0] = i2c_smbus_read_byte_data(client, reg & 0xff);
+
+ return 0;
+}
+
+static int bcm2048_recv_duples(struct bcm2048_device *bdev, unsigned int reg,
+ u8 *value, u8 duples)
+{
+ struct i2c_client *client = bdev->client;
+ struct i2c_adapter *adap = client->adapter;
+ struct i2c_msg msg[2];
+ u8 buf;
+
+ if (!bdev->power_state) {
+ dev_err(&bdev->client->dev, "bcm2048: chip not powered!\n");
+ return -EIO;
+ }
+
+ buf = reg & 0xff;
+
+ msg[0].addr = client->addr;
+ msg[0].flags = client->flags & I2C_M_TEN;
+ msg[0].len = 1;
+ msg[0].buf = &buf;
+
+ msg[1].addr = client->addr;
+ msg[1].flags = client->flags & I2C_M_TEN;
+ msg[1].flags |= I2C_M_RD;
+ msg[1].len = duples;
+ msg[1].buf = value;
+
+ return i2c_transfer(adap, msg, 2);
+}
+
+/*
+ * BCM2048 - I2C register programming helpers
+ */
+static int bcm2048_set_power_state(struct bcm2048_device *bdev, u8 power)
+{
+ int err = 0;
+
+ mutex_lock(&bdev->mutex);
+
+ if (power) {
+ bdev->power_state = BCM2048_POWER_ON;
+ bdev->cache_fm_rds_system |= BCM2048_FM_ON;
+ } else {
+ bdev->cache_fm_rds_system &= ~BCM2048_FM_ON;
+ }
+
+ /*
+ * Warning! FM cannot be turned off because then
+ * the I2C communications get ruined!
+ * Comment off the "if (power)" when the chip works!
+ */
+ if (power)
+ err = bcm2048_send_command(bdev, BCM2048_I2C_FM_RDS_SYSTEM,
+ bdev->cache_fm_rds_system);
+ msleep(BCM2048_DEFAULT_POWERING_DELAY);
+
+ if (!power)
+ bdev->power_state = BCM2048_POWER_OFF;
+
+ mutex_unlock(&bdev->mutex);
+ return err;
+}
+
+static int bcm2048_get_power_state(struct bcm2048_device *bdev)
+{
+ int err;
+ u8 value;
+
+ mutex_lock(&bdev->mutex);
+
+ err = bcm2048_recv_command(bdev, BCM2048_I2C_FM_RDS_SYSTEM, &value);
+
+ mutex_unlock(&bdev->mutex);
+
+ if (!err && (value & BCM2048_FM_ON))
+ return BCM2048_POWER_ON;
+
+ return err;
+}
+
+static int bcm2048_set_rds_no_lock(struct bcm2048_device *bdev, u8 rds_on)
+{
+ int err;
+ u8 flags;
+
+ bdev->cache_fm_rds_system &= ~BCM2048_RDS_ON;
+
+ if (rds_on) {
+ bdev->cache_fm_rds_system |= BCM2048_RDS_ON;
+ bdev->rds_state = BCM2048_RDS_ON;
+ flags = BCM2048_RDS_FLAG_FIFO_WLINE;
+ err = bcm2048_send_command(bdev, BCM2048_I2C_FM_RDS_MASK1,
+ flags);
+ } else {
+ flags = 0;
+ bdev->rds_state = 0;
+ err = bcm2048_send_command(bdev, BCM2048_I2C_FM_RDS_MASK1,
+ flags);
+ memset(&bdev->rds_info, 0, sizeof(bdev->rds_info));
+ }
+
+ err = bcm2048_send_command(bdev, BCM2048_I2C_FM_RDS_SYSTEM,
+ bdev->cache_fm_rds_system);
+
+ return err;
+}
+
+static int bcm2048_get_rds_no_lock(struct bcm2048_device *bdev)
+{
+ int err;
+ u8 value;
+
+ err = bcm2048_recv_command(bdev, BCM2048_I2C_FM_RDS_SYSTEM, &value);
+
+ if (!err && (value & BCM2048_RDS_ON))
+ return BCM2048_ITEM_ENABLED;
+
+ return err;
+}
+
+static int bcm2048_set_rds(struct bcm2048_device *bdev, u8 rds_on)
+{
+ int err;
+
+ mutex_lock(&bdev->mutex);
+
+ err = bcm2048_set_rds_no_lock(bdev, rds_on);
+
+ mutex_unlock(&bdev->mutex);
+ return err;
+}
+
+static int bcm2048_get_rds(struct bcm2048_device *bdev)
+{
+ int err;
+
+ mutex_lock(&bdev->mutex);
+
+ err = bcm2048_get_rds_no_lock(bdev);
+
+ mutex_unlock(&bdev->mutex);
+ return err;
+}
+
+static int bcm2048_get_rds_pi(struct bcm2048_device *bdev)
+{
+ return bdev->rds_info.rds_pi;
+}
+
+static int bcm2048_set_fm_automatic_stereo_mono(struct bcm2048_device *bdev,
+ u8 enabled)
+{
+ int err;
+
+ mutex_lock(&bdev->mutex);
+
+ bdev->cache_fm_ctrl &= ~BCM2048_STEREO_MONO_AUTO_SELECT;
+
+ if (enabled)
+ bdev->cache_fm_ctrl |= BCM2048_STEREO_MONO_AUTO_SELECT;
+
+ err = bcm2048_send_command(bdev, BCM2048_I2C_FM_CTRL,
+ bdev->cache_fm_ctrl);
+
+ mutex_unlock(&bdev->mutex);
+ return err;
+}
+
+static int bcm2048_set_fm_hi_lo_injection(struct bcm2048_device *bdev,
+ u8 hi_lo)
+{
+ int err;
+
+ mutex_lock(&bdev->mutex);
+
+ bdev->cache_fm_ctrl &= ~BCM2048_HI_LO_INJECTION;
+
+ if (hi_lo)
+ bdev->cache_fm_ctrl |= BCM2048_HI_LO_INJECTION;
+
+ err = bcm2048_send_command(bdev, BCM2048_I2C_FM_CTRL,
+ bdev->cache_fm_ctrl);
+
+ mutex_unlock(&bdev->mutex);
+ return err;
+}
+
+static int bcm2048_get_fm_hi_lo_injection(struct bcm2048_device *bdev)
+{
+ int err;
+ u8 value;
+
+ mutex_lock(&bdev->mutex);
+
+ err = bcm2048_recv_command(bdev, BCM2048_I2C_FM_CTRL, &value);
+
+ mutex_unlock(&bdev->mutex);
+
+ if (!err && (value & BCM2048_HI_LO_INJECTION))
+ return BCM2048_ITEM_ENABLED;
+
+ return err;
+}
+
+static int bcm2048_set_fm_frequency(struct bcm2048_device *bdev, u32 frequency)
+{
+ int err;
+
+ if (frequency < bdev->region_info.bottom_frequency ||
+ frequency > bdev->region_info.top_frequency)
+ return -EDOM;
+
+ frequency -= BCM2048_FREQUENCY_BASE;
+
+ mutex_lock(&bdev->mutex);
+
+ err = bcm2048_send_command(bdev, BCM2048_I2C_FM_FREQ0, lsb(frequency));
+ err |= bcm2048_send_command(bdev, BCM2048_I2C_FM_FREQ1,
+ msb(frequency));
+
+ if (!err)
+ bdev->frequency = frequency;
+
+ mutex_unlock(&bdev->mutex);
+ return err;
+}
+
+static int bcm2048_get_fm_frequency(struct bcm2048_device *bdev)
+{
+ int err;
+ u8 lsb, msb;
+
+ mutex_lock(&bdev->mutex);
+
+ err = bcm2048_recv_command(bdev, BCM2048_I2C_FM_FREQ0, &lsb);
+ err |= bcm2048_recv_command(bdev, BCM2048_I2C_FM_FREQ1, &msb);
+
+ mutex_unlock(&bdev->mutex);
+
+ if (err)
+ return err;
+
+ err = compose_u16(msb, lsb);
+ err += BCM2048_FREQUENCY_BASE;
+
+ return err;
+}
+
+static int bcm2048_set_fm_af_frequency(struct bcm2048_device *bdev,
+ u32 frequency)
+{
+ int err;
+
+ if (frequency < bdev->region_info.bottom_frequency ||
+ frequency > bdev->region_info.top_frequency)
+ return -EDOM;
+
+ frequency -= BCM2048_FREQUENCY_BASE;
+
+ mutex_lock(&bdev->mutex);
+
+ err = bcm2048_send_command(bdev, BCM2048_I2C_FM_AF_FREQ0,
+ lsb(frequency));
+ err |= bcm2048_send_command(bdev, BCM2048_I2C_FM_AF_FREQ1,
+ msb(frequency));
+ if (!err)
+ bdev->frequency = frequency;
+
+ mutex_unlock(&bdev->mutex);
+ return err;
+}
+
+static int bcm2048_get_fm_af_frequency(struct bcm2048_device *bdev)
+{
+ int err;
+ u8 lsb, msb;
+
+ mutex_lock(&bdev->mutex);
+
+ err = bcm2048_recv_command(bdev, BCM2048_I2C_FM_AF_FREQ0, &lsb);
+ err |= bcm2048_recv_command(bdev, BCM2048_I2C_FM_AF_FREQ1, &msb);
+
+ mutex_unlock(&bdev->mutex);
+
+ if (err)
+ return err;
+
+ err = compose_u16(msb, lsb);
+ err += BCM2048_FREQUENCY_BASE;
+
+ return err;
+}
+
+static int bcm2048_set_fm_deemphasis(struct bcm2048_device *bdev, int d)
+{
+ int err;
+ u8 deemphasis;
+
+ if (d == BCM2048_DE_EMPHASIS_75us)
+ deemphasis = BCM2048_DE_EMPHASIS_SELECT;
+ else
+ deemphasis = 0;
+
+ mutex_lock(&bdev->mutex);
+
+ bdev->cache_fm_audio_ctrl0 &= ~BCM2048_DE_EMPHASIS_SELECT;
+ bdev->cache_fm_audio_ctrl0 |= deemphasis;
+
+ err = bcm2048_send_command(bdev, BCM2048_I2C_FM_AUDIO_CTRL0,
+ bdev->cache_fm_audio_ctrl0);
+
+ if (!err)
+ bdev->region_info.deemphasis = d;
+
+ mutex_unlock(&bdev->mutex);
+
+ return err;
+}
+
+static int bcm2048_get_fm_deemphasis(struct bcm2048_device *bdev)
+{
+ int err;
+ u8 value;
+
+ mutex_lock(&bdev->mutex);
+
+ err = bcm2048_recv_command(bdev, BCM2048_I2C_FM_AUDIO_CTRL0, &value);
+
+ mutex_unlock(&bdev->mutex);
+
+ if (!err) {
+ if (value & BCM2048_DE_EMPHASIS_SELECT)
+ return BCM2048_DE_EMPHASIS_75us;
+
+ return BCM2048_DE_EMPHASIS_50us;
+ }
+
+ return err;
+}
+
+static int bcm2048_set_region(struct bcm2048_device *bdev, u8 region)
+{
+ int err;
+ u32 new_frequency = 0;
+
+ if (region >= ARRAY_SIZE(region_configs))
+ return -EINVAL;
+
+ mutex_lock(&bdev->mutex);
+ bdev->region_info = region_configs[region];
+ mutex_unlock(&bdev->mutex);
+
+ if (bdev->frequency < region_configs[region].bottom_frequency ||
+ bdev->frequency > region_configs[region].top_frequency)
+ new_frequency = region_configs[region].bottom_frequency;
+
+ if (new_frequency > 0) {
+ err = bcm2048_set_fm_frequency(bdev, new_frequency);
+
+ if (err)
+ goto done;
+ }
+
+ err = bcm2048_set_fm_deemphasis(bdev,
+ region_configs[region].deemphasis);
+
+done:
+ return err;
+}
+
+static int bcm2048_get_region(struct bcm2048_device *bdev)
+{
+ int err;
+
+ mutex_lock(&bdev->mutex);
+ err = bdev->region_info.region;
+ mutex_unlock(&bdev->mutex);
+
+ return err;
+}
+
+static int bcm2048_set_mute(struct bcm2048_device *bdev, u16 mute)
+{
+ int err;
+
+ mutex_lock(&bdev->mutex);
+
+ bdev->cache_fm_audio_ctrl0 &= ~(BCM2048_RF_MUTE | BCM2048_MANUAL_MUTE);
+
+ if (mute)
+ bdev->cache_fm_audio_ctrl0 |= (BCM2048_RF_MUTE |
+ BCM2048_MANUAL_MUTE);
+
+ err = bcm2048_send_command(bdev, BCM2048_I2C_FM_AUDIO_CTRL0,
+ bdev->cache_fm_audio_ctrl0);
+
+ if (!err)
+ bdev->mute_state = mute;
+
+ mutex_unlock(&bdev->mutex);
+ return err;
+}
+
+static int bcm2048_get_mute(struct bcm2048_device *bdev)
+{
+ int err;
+ u8 value;
+
+ mutex_lock(&bdev->mutex);
+
+ if (bdev->power_state) {
+ err = bcm2048_recv_command(bdev, BCM2048_I2C_FM_AUDIO_CTRL0,
+ &value);
+ if (!err)
+ err = value & (BCM2048_RF_MUTE | BCM2048_MANUAL_MUTE);
+ } else {
+ err = bdev->mute_state;
+ }
+
+ mutex_unlock(&bdev->mutex);
+ return err;
+}
+
+static int bcm2048_set_audio_route(struct bcm2048_device *bdev, u8 route)
+{
+ int err;
+
+ mutex_lock(&bdev->mutex);
+
+ route &= (BCM2048_AUDIO_ROUTE_DAC | BCM2048_AUDIO_ROUTE_I2S);
+ bdev->cache_fm_audio_ctrl0 &= ~(BCM2048_AUDIO_ROUTE_DAC |
+ BCM2048_AUDIO_ROUTE_I2S);
+ bdev->cache_fm_audio_ctrl0 |= route;
+
+ err = bcm2048_send_command(bdev, BCM2048_I2C_FM_AUDIO_CTRL0,
+ bdev->cache_fm_audio_ctrl0);
+
+ mutex_unlock(&bdev->mutex);
+ return err;
+}
+
+static int bcm2048_get_audio_route(struct bcm2048_device *bdev)
+{
+ int err;
+ u8 value;
+
+ mutex_lock(&bdev->mutex);
+
+ err = bcm2048_recv_command(bdev, BCM2048_I2C_FM_AUDIO_CTRL0, &value);
+
+ mutex_unlock(&bdev->mutex);
+
+ if (!err)
+ return value & (BCM2048_AUDIO_ROUTE_DAC |
+ BCM2048_AUDIO_ROUTE_I2S);
+
+ return err;
+}
+
+static int bcm2048_set_dac_output(struct bcm2048_device *bdev, u8 channels)
+{
+ int err;
+
+ mutex_lock(&bdev->mutex);
+
+ bdev->cache_fm_audio_ctrl0 &= ~(BCM2048_DAC_OUTPUT_LEFT |
+ BCM2048_DAC_OUTPUT_RIGHT);
+ bdev->cache_fm_audio_ctrl0 |= channels;
+
+ err = bcm2048_send_command(bdev, BCM2048_I2C_FM_AUDIO_CTRL0,
+ bdev->cache_fm_audio_ctrl0);
+
+ mutex_unlock(&bdev->mutex);
+ return err;
+}
+
+static int bcm2048_get_dac_output(struct bcm2048_device *bdev)
+{
+ int err;
+ u8 value;
+
+ mutex_lock(&bdev->mutex);
+
+ err = bcm2048_recv_command(bdev, BCM2048_I2C_FM_AUDIO_CTRL0, &value);
+
+ mutex_unlock(&bdev->mutex);
+
+ if (!err)
+ return value & (BCM2048_DAC_OUTPUT_LEFT |
+ BCM2048_DAC_OUTPUT_RIGHT);
+
+ return err;
+}
+
+static int bcm2048_set_fm_search_rssi_threshold(struct bcm2048_device *bdev,
+ u8 threshold)
+{
+ int err;
+
+ mutex_lock(&bdev->mutex);
+
+ threshold &= BCM2048_SEARCH_RSSI_THRESHOLD;
+ bdev->cache_fm_search_ctrl0 &= ~BCM2048_SEARCH_RSSI_THRESHOLD;
+ bdev->cache_fm_search_ctrl0 |= threshold;
+
+ err = bcm2048_send_command(bdev, BCM2048_I2C_FM_SEARCH_CTRL0,
+ bdev->cache_fm_search_ctrl0);
+
+ mutex_unlock(&bdev->mutex);
+ return err;
+}
+
+static int bcm2048_get_fm_search_rssi_threshold(struct bcm2048_device *bdev)
+{
+ int err;
+ u8 value;
+
+ mutex_lock(&bdev->mutex);
+
+ err = bcm2048_recv_command(bdev, BCM2048_I2C_FM_SEARCH_CTRL0, &value);
+
+ mutex_unlock(&bdev->mutex);
+
+ if (!err)
+ return value & BCM2048_SEARCH_RSSI_THRESHOLD;
+
+ return err;
+}
+
+static int bcm2048_set_fm_search_mode_direction(struct bcm2048_device *bdev,
+ u8 direction)
+{
+ int err;
+
+ mutex_lock(&bdev->mutex);
+
+ bdev->cache_fm_search_ctrl0 &= ~BCM2048_SEARCH_DIRECTION;
+
+ if (direction)
+ bdev->cache_fm_search_ctrl0 |= BCM2048_SEARCH_DIRECTION;
+
+ err = bcm2048_send_command(bdev, BCM2048_I2C_FM_SEARCH_CTRL0,
+ bdev->cache_fm_search_ctrl0);
+
+ mutex_unlock(&bdev->mutex);
+ return err;
+}
+
+static int bcm2048_get_fm_search_mode_direction(struct bcm2048_device *bdev)
+{
+ int err;
+ u8 value;
+
+ mutex_lock(&bdev->mutex);
+
+ err = bcm2048_recv_command(bdev, BCM2048_I2C_FM_SEARCH_CTRL0, &value);
+
+ mutex_unlock(&bdev->mutex);
+
+ if (!err && (value & BCM2048_SEARCH_DIRECTION))
+ return BCM2048_SEARCH_DIRECTION_UP;
+
+ return err;
+}
+
+static int bcm2048_set_fm_search_tune_mode(struct bcm2048_device *bdev,
+ u8 mode)
+{
+ int err, timeout, restart_rds = 0;
+ u8 value, flags;
+
+ value = mode & BCM2048_FM_AUTO_SEARCH;
+
+ flags = BCM2048_FM_FLAG_SEARCH_TUNE_FINISHED |
+ BCM2048_FM_FLAG_SEARCH_TUNE_FAIL;
+
+ mutex_lock(&bdev->mutex);
+
+ /*
+ * If RDS is enabled, and frequency is changed, RDS quits working.
+ * Thus, always restart RDS if it's enabled. Moreover, RDS must
+ * not be enabled while changing the frequency because it can
+ * provide a race to the mutex from the workqueue handler if RDS
+ * IRQ occurs while waiting for frequency changed IRQ.
+ */
+ if (bcm2048_get_rds_no_lock(bdev)) {
+ err = bcm2048_set_rds_no_lock(bdev, 0);
+ if (err)
+ goto unlock;
+ restart_rds = 1;
+ }
+
+ err = bcm2048_send_command(bdev, BCM2048_I2C_FM_RDS_MASK0, flags);
+
+ if (err)
+ goto unlock;
+
+ bcm2048_send_command(bdev, BCM2048_I2C_FM_SEARCH_TUNE_MODE, value);
+
+ if (mode != BCM2048_FM_AUTO_SEARCH_MODE)
+ timeout = BCM2048_DEFAULT_TIMEOUT;
+ else
+ timeout = BCM2048_AUTO_SEARCH_TIMEOUT;
+
+ if (!wait_for_completion_timeout(&bdev->compl,
+ msecs_to_jiffies(timeout)))
+ dev_err(&bdev->client->dev, "IRQ timeout.\n");
+
+ if (value)
+ if (!bdev->scan_state)
+ err = -EIO;
+
+unlock:
+ if (restart_rds)
+ err |= bcm2048_set_rds_no_lock(bdev, 1);
+
+ mutex_unlock(&bdev->mutex);
+
+ return err;
+}
+
+static int bcm2048_get_fm_search_tune_mode(struct bcm2048_device *bdev)
+{
+ int err;
+ u8 value;
+
+ mutex_lock(&bdev->mutex);
+
+ err = bcm2048_recv_command(bdev, BCM2048_I2C_FM_SEARCH_TUNE_MODE,
+ &value);
+
+ mutex_unlock(&bdev->mutex);
+
+ if (!err)
+ return value & BCM2048_FM_AUTO_SEARCH;
+
+ return err;
+}
+
+static int bcm2048_set_rds_b_block_mask(struct bcm2048_device *bdev, u16 mask)
+{
+ int err;
+
+ mutex_lock(&bdev->mutex);
+
+ err = bcm2048_send_command(bdev,
+ BCM2048_I2C_RDS_BLKB_MASK0, lsb(mask));
+ err |= bcm2048_send_command(bdev,
+ BCM2048_I2C_RDS_BLKB_MASK1, msb(mask));
+
+ mutex_unlock(&bdev->mutex);
+ return err;
+}
+
+static int bcm2048_get_rds_b_block_mask(struct bcm2048_device *bdev)
+{
+ int err;
+ u8 lsb, msb;
+
+ mutex_lock(&bdev->mutex);
+
+ err = bcm2048_recv_command(bdev,
+ BCM2048_I2C_RDS_BLKB_MASK0, &lsb);
+ err |= bcm2048_recv_command(bdev,
+ BCM2048_I2C_RDS_BLKB_MASK1, &msb);
+
+ mutex_unlock(&bdev->mutex);
+
+ if (!err)
+ return compose_u16(msb, lsb);
+
+ return err;
+}
+
+static int bcm2048_set_rds_b_block_match(struct bcm2048_device *bdev,
+ u16 match)
+{
+ int err;
+
+ mutex_lock(&bdev->mutex);
+
+ err = bcm2048_send_command(bdev,
+ BCM2048_I2C_RDS_BLKB_MATCH0, lsb(match));
+ err |= bcm2048_send_command(bdev,
+ BCM2048_I2C_RDS_BLKB_MATCH1, msb(match));
+
+ mutex_unlock(&bdev->mutex);
+ return err;
+}
+
+static int bcm2048_get_rds_b_block_match(struct bcm2048_device *bdev)
+{
+ int err;
+ u8 lsb, msb;
+
+ mutex_lock(&bdev->mutex);
+
+ err = bcm2048_recv_command(bdev,
+ BCM2048_I2C_RDS_BLKB_MATCH0, &lsb);
+ err |= bcm2048_recv_command(bdev,
+ BCM2048_I2C_RDS_BLKB_MATCH1, &msb);
+
+ mutex_unlock(&bdev->mutex);
+
+ if (!err)
+ return compose_u16(msb, lsb);
+
+ return err;
+}
+
+static int bcm2048_set_rds_pi_mask(struct bcm2048_device *bdev, u16 mask)
+{
+ int err;
+
+ mutex_lock(&bdev->mutex);
+
+ err = bcm2048_send_command(bdev,
+ BCM2048_I2C_RDS_PI_MASK0, lsb(mask));
+ err |= bcm2048_send_command(bdev,
+ BCM2048_I2C_RDS_PI_MASK1, msb(mask));
+
+ mutex_unlock(&bdev->mutex);
+ return err;
+}
+
+static int bcm2048_get_rds_pi_mask(struct bcm2048_device *bdev)
+{
+ int err;
+ u8 lsb, msb;
+
+ mutex_lock(&bdev->mutex);
+
+ err = bcm2048_recv_command(bdev,
+ BCM2048_I2C_RDS_PI_MASK0, &lsb);
+ err |= bcm2048_recv_command(bdev,
+ BCM2048_I2C_RDS_PI_MASK1, &msb);
+
+ mutex_unlock(&bdev->mutex);
+
+ if (!err)
+ return compose_u16(msb, lsb);
+
+ return err;
+}
+
+static int bcm2048_set_rds_pi_match(struct bcm2048_device *bdev, u16 match)
+{
+ int err;
+
+ mutex_lock(&bdev->mutex);
+
+ err = bcm2048_send_command(bdev,
+ BCM2048_I2C_RDS_PI_MATCH0, lsb(match));
+ err |= bcm2048_send_command(bdev,
+ BCM2048_I2C_RDS_PI_MATCH1, msb(match));
+
+ mutex_unlock(&bdev->mutex);
+ return err;
+}
+
+static int bcm2048_get_rds_pi_match(struct bcm2048_device *bdev)
+{
+ int err;
+ u8 lsb, msb;
+
+ mutex_lock(&bdev->mutex);
+
+ err = bcm2048_recv_command(bdev,
+ BCM2048_I2C_RDS_PI_MATCH0, &lsb);
+ err |= bcm2048_recv_command(bdev,
+ BCM2048_I2C_RDS_PI_MATCH1, &msb);
+
+ mutex_unlock(&bdev->mutex);
+
+ if (!err)
+ return compose_u16(msb, lsb);
+
+ return err;
+}
+
+static int bcm2048_set_fm_rds_mask(struct bcm2048_device *bdev, u16 mask)
+{
+ int err;
+
+ mutex_lock(&bdev->mutex);
+
+ err = bcm2048_send_command(bdev,
+ BCM2048_I2C_FM_RDS_MASK0, lsb(mask));
+ err |= bcm2048_send_command(bdev,
+ BCM2048_I2C_FM_RDS_MASK1, msb(mask));
+
+ mutex_unlock(&bdev->mutex);
+ return err;
+}
+
+static int bcm2048_get_fm_rds_mask(struct bcm2048_device *bdev)
+{
+ int err;
+ u8 value0, value1;
+
+ mutex_lock(&bdev->mutex);
+
+ err = bcm2048_recv_command(bdev, BCM2048_I2C_FM_RDS_MASK0, &value0);
+ err |= bcm2048_recv_command(bdev, BCM2048_I2C_FM_RDS_MASK1, &value1);
+
+ mutex_unlock(&bdev->mutex);
+
+ if (!err)
+ return compose_u16(value1, value0);
+
+ return err;
+}
+
+static int bcm2048_get_fm_rds_flags(struct bcm2048_device *bdev)
+{
+ int err;
+ u8 value0, value1;
+
+ mutex_lock(&bdev->mutex);
+
+ err = bcm2048_recv_command(bdev, BCM2048_I2C_FM_RDS_FLAG0, &value0);
+ err |= bcm2048_recv_command(bdev, BCM2048_I2C_FM_RDS_FLAG1, &value1);
+
+ mutex_unlock(&bdev->mutex);
+
+ if (!err)
+ return compose_u16(value1, value0);
+
+ return err;
+}
+
+static int bcm2048_get_region_bottom_frequency(struct bcm2048_device *bdev)
+{
+ return bdev->region_info.bottom_frequency;
+}
+
+static int bcm2048_get_region_top_frequency(struct bcm2048_device *bdev)
+{
+ return bdev->region_info.top_frequency;
+}
+
+static int bcm2048_set_fm_best_tune_mode(struct bcm2048_device *bdev, u8 mode)
+{
+ int err;
+ u8 value;
+
+ mutex_lock(&bdev->mutex);
+
+ /* Perform read as the manual indicates */
+ err = bcm2048_recv_command(bdev, BCM2048_I2C_FM_BEST_TUNE_MODE,
+ &value);
+ value &= ~BCM2048_BEST_TUNE_MODE;
+
+ if (mode)
+ value |= BCM2048_BEST_TUNE_MODE;
+ err |= bcm2048_send_command(bdev, BCM2048_I2C_FM_BEST_TUNE_MODE,
+ value);
+
+ mutex_unlock(&bdev->mutex);
+ return err;
+}
+
+static int bcm2048_get_fm_best_tune_mode(struct bcm2048_device *bdev)
+{
+ int err;
+ u8 value;
+
+ mutex_lock(&bdev->mutex);
+
+ err = bcm2048_recv_command(bdev, BCM2048_I2C_FM_BEST_TUNE_MODE,
+ &value);
+
+ mutex_unlock(&bdev->mutex);
+
+ if (!err && (value & BCM2048_BEST_TUNE_MODE))
+ return BCM2048_ITEM_ENABLED;
+
+ return err;
+}
+
+static int bcm2048_get_fm_carrier_error(struct bcm2048_device *bdev)
+{
+ int err = 0;
+ s8 value;
+
+ mutex_lock(&bdev->mutex);
+ err = bcm2048_recv_command(bdev, BCM2048_I2C_FM_CARRIER, &value);
+ mutex_unlock(&bdev->mutex);
+
+ if (!err)
+ return value;
+
+ return err;
+}
+
+static int bcm2048_get_fm_rssi(struct bcm2048_device *bdev)
+{
+ int err;
+ s8 value;
+
+ mutex_lock(&bdev->mutex);
+ err = bcm2048_recv_command(bdev, BCM2048_I2C_FM_RSSI, &value);
+ mutex_unlock(&bdev->mutex);
+
+ if (!err)
+ return value;
+
+ return err;
+}
+
+static int bcm2048_set_rds_wline(struct bcm2048_device *bdev, u8 wline)
+{
+ int err;
+
+ mutex_lock(&bdev->mutex);
+
+ err = bcm2048_send_command(bdev, BCM2048_I2C_RDS_WLINE, wline);
+
+ if (!err)
+ bdev->fifo_size = wline;
+
+ mutex_unlock(&bdev->mutex);
+ return err;
+}
+
+static int bcm2048_get_rds_wline(struct bcm2048_device *bdev)
+{
+ int err;
+ u8 value;
+
+ mutex_lock(&bdev->mutex);
+
+ err = bcm2048_recv_command(bdev, BCM2048_I2C_RDS_WLINE, &value);
+
+ mutex_unlock(&bdev->mutex);
+
+ if (!err) {
+ bdev->fifo_size = value;
+ return value;
+ }
+
+ return err;
+}
+
+static int bcm2048_checkrev(struct bcm2048_device *bdev)
+{
+ int err;
+ u8 version;
+
+ mutex_lock(&bdev->mutex);
+
+ err = bcm2048_recv_command(bdev, BCM2048_I2C_FM_RDS_REV, &version);
+
+ mutex_unlock(&bdev->mutex);
+
+ if (!err) {
+ dev_info(&bdev->client->dev, "BCM2048 Version 0x%x\n",
+ version);
+ return version;
+ }
+
+ return err;
+}
+
+static int bcm2048_get_rds_rt(struct bcm2048_device *bdev, char *data)
+{
+ int err = 0, i, j = 0, ce = 0, cr = 0;
+ char data_buffer[BCM2048_MAX_RDS_RT+1];
+
+ mutex_lock(&bdev->mutex);
+
+ if (!bdev->rds_info.text_len) {
+ err = -EINVAL;
+ goto unlock;
+ }
+
+ for (i = 0; i < BCM2048_MAX_RDS_RT; i++) {
+ if (bdev->rds_info.rds_rt[i]) {
+ ce = i;
+ /* Skip the carriage return */
+ if (bdev->rds_info.rds_rt[i] != 0x0d) {
+ data_buffer[j++] = bdev->rds_info.rds_rt[i];
+ } else {
+ cr = i;
+ break;
+ }
+ }
+ }
+
+ if (j <= BCM2048_MAX_RDS_RT)
+ data_buffer[j] = 0;
+
+ for (i = 0; i < BCM2048_MAX_RDS_RT; i++) {
+ if (!bdev->rds_info.rds_rt[i]) {
+ if (cr && (i < cr)) {
+ err = -EBUSY;
+ goto unlock;
+ }
+ if (i < ce) {
+ if (cr && (i >= cr))
+ break;
+ err = -EBUSY;
+ goto unlock;
+ }
+ }
+ }
+
+ memcpy(data, data_buffer, sizeof(data_buffer));
+
+unlock:
+ mutex_unlock(&bdev->mutex);
+ return err;
+}
+
+static int bcm2048_get_rds_ps(struct bcm2048_device *bdev, char *data)
+{
+ int err = 0, i, j = 0;
+ char data_buffer[BCM2048_MAX_RDS_PS+1];
+
+ mutex_lock(&bdev->mutex);
+
+ if (!bdev->rds_info.text_len) {
+ err = -EINVAL;
+ goto unlock;
+ }
+
+ for (i = 0; i < BCM2048_MAX_RDS_PS; i++) {
+ if (bdev->rds_info.rds_ps[i]) {
+ data_buffer[j++] = bdev->rds_info.rds_ps[i];
+ } else {
+ if (i < (BCM2048_MAX_RDS_PS - 1)) {
+ err = -EBUSY;
+ goto unlock;
+ }
+ }
+ }
+
+ if (j <= BCM2048_MAX_RDS_PS)
+ data_buffer[j] = 0;
+
+ memcpy(data, data_buffer, sizeof(data_buffer));
+
+unlock:
+ mutex_unlock(&bdev->mutex);
+ return err;
+}
+
+static void bcm2048_parse_rds_pi(struct bcm2048_device *bdev)
+{
+ int i, cnt = 0;
+ u16 pi;
+
+ for (i = 0; i < bdev->fifo_size; i += BCM2048_RDS_FIFO_DUPLE_SIZE) {
+
+ /* Block A match, only data without crc errors taken */
+ if (bdev->rds_info.radio_text[i] == BCM2048_RDS_BLOCK_A) {
+
+ pi = (bdev->rds_info.radio_text[i+1] << 8) +
+ bdev->rds_info.radio_text[i+2];
+
+ if (!bdev->rds_info.rds_pi) {
+ bdev->rds_info.rds_pi = pi;
+ return;
+ }
+ if (pi != bdev->rds_info.rds_pi) {
+ cnt++;
+ if (cnt > 3) {
+ bdev->rds_info.rds_pi = pi;
+ cnt = 0;
+ }
+ } else {
+ cnt = 0;
+ }
+ }
+ }
+}
+
+static int bcm2048_rds_block_crc(struct bcm2048_device *bdev, int i)
+{
+ return bdev->rds_info.radio_text[i] & BCM2048_RDS_CRC_MASK;
+}
+
+static void bcm2048_parse_rds_rt_block(struct bcm2048_device *bdev, int i,
+ int index, int crc)
+{
+ /* Good data will overwrite poor data */
+ if (crc) {
+ if (!bdev->rds_info.rds_rt[index])
+ bdev->rds_info.rds_rt[index] =
+ bdev->rds_info.radio_text[i+1];
+ if (!bdev->rds_info.rds_rt[index+1])
+ bdev->rds_info.rds_rt[index+1] =
+ bdev->rds_info.radio_text[i+2];
+ } else {
+ bdev->rds_info.rds_rt[index] = bdev->rds_info.radio_text[i+1];
+ bdev->rds_info.rds_rt[index+1] =
+ bdev->rds_info.radio_text[i+2];
+ }
+}
+
+static int bcm2048_parse_rt_match_b(struct bcm2048_device *bdev, int i)
+{
+ int crc, rt_id, rt_group_b, rt_ab, index = 0;
+
+ crc = bcm2048_rds_block_crc(bdev, i);
+
+ if (crc == BCM2048_RDS_CRC_UNRECOVARABLE)
+ return -EIO;
+
+ if ((bdev->rds_info.radio_text[i] & BCM2048_RDS_BLOCK_MASK) ==
+ BCM2048_RDS_BLOCK_B) {
+
+ rt_id = bdev->rds_info.radio_text[i+1] &
+ BCM2048_RDS_BLOCK_MASK;
+ rt_group_b = bdev->rds_info.radio_text[i+1] &
+ BCM2048_RDS_GROUP_AB_MASK;
+ rt_ab = bdev->rds_info.radio_text[i+2] &
+ BCM2048_RDS_RT_AB_MASK;
+
+ if (rt_group_b != bdev->rds_info.rds_rt_group_b) {
+ memset(bdev->rds_info.rds_rt, 0,
+ sizeof(bdev->rds_info.rds_rt));
+ bdev->rds_info.rds_rt_group_b = rt_group_b;
+ }
+
+ if (rt_id == BCM2048_RDS_RT) {
+ /* A to B or (vice versa), means: clear screen */
+ if (rt_ab != bdev->rds_info.rds_rt_ab) {
+ memset(bdev->rds_info.rds_rt, 0,
+ sizeof(bdev->rds_info.rds_rt));
+ bdev->rds_info.rds_rt_ab = rt_ab;
+ }
+
+ index = bdev->rds_info.radio_text[i+2] &
+ BCM2048_RDS_RT_INDEX;
+
+ if (bdev->rds_info.rds_rt_group_b)
+ index <<= 1;
+ else
+ index <<= 2;
+
+ return index;
+ }
+ }
+
+ return -EIO;
+}
+
+static int bcm2048_parse_rt_match_c(struct bcm2048_device *bdev, int i,
+ int index)
+{
+ int crc;
+
+ crc = bcm2048_rds_block_crc(bdev, i);
+
+ if (crc == BCM2048_RDS_CRC_UNRECOVARABLE)
+ return 0;
+
+ BUG_ON((index+2) >= BCM2048_MAX_RDS_RT);
+
+ if ((bdev->rds_info.radio_text[i] & BCM2048_RDS_BLOCK_MASK) ==
+ BCM2048_RDS_BLOCK_C) {
+ if (bdev->rds_info.rds_rt_group_b)
+ return 1;
+ bcm2048_parse_rds_rt_block(bdev, i, index, crc);
+ return 1;
+ }
+
+ return 0;
+}
+
+static void bcm2048_parse_rt_match_d(struct bcm2048_device *bdev, int i,
+ int index)
+{
+ int crc;
+
+ crc = bcm2048_rds_block_crc(bdev, i);
+
+ if (crc == BCM2048_RDS_CRC_UNRECOVARABLE)
+ return;
+
+ BUG_ON((index+4) >= BCM2048_MAX_RDS_RT);
+
+ if ((bdev->rds_info.radio_text[i] & BCM2048_RDS_BLOCK_MASK) ==
+ BCM2048_RDS_BLOCK_D)
+ bcm2048_parse_rds_rt_block(bdev, i, index+2, crc);
+}
+
+static void bcm2048_parse_rds_rt(struct bcm2048_device *bdev)
+{
+ int i, index = 0, crc, match_b = 0, match_c = 0, match_d = 0;
+
+ for (i = 0; i < bdev->fifo_size; i += BCM2048_RDS_FIFO_DUPLE_SIZE) {
+
+ if (match_b) {
+ match_b = 0;
+ index = bcm2048_parse_rt_match_b(bdev, i);
+ if (index >= 0 && index <= (BCM2048_MAX_RDS_RT - 5))
+ match_c = 1;
+ continue;
+ } else if (match_c) {
+ match_c = 0;
+ if (bcm2048_parse_rt_match_c(bdev, i, index))
+ match_d = 1;
+ continue;
+ } else if (match_d) {
+ match_d = 0;
+ bcm2048_parse_rt_match_d(bdev, i, index);
+ continue;
+ }
+
+ /* Skip erroneous blocks due to messed up A block altogether */
+ if ((bdev->rds_info.radio_text[i] & BCM2048_RDS_BLOCK_MASK)
+ == BCM2048_RDS_BLOCK_A) {
+ crc = bcm2048_rds_block_crc(bdev, i);
+ if (crc == BCM2048_RDS_CRC_UNRECOVARABLE)
+ continue;
+ /* Syncronize to a good RDS PI */
+ if (((bdev->rds_info.radio_text[i+1] << 8) +
+ bdev->rds_info.radio_text[i+2]) ==
+ bdev->rds_info.rds_pi)
+ match_b = 1;
+ }
+ }
+}
+
+static void bcm2048_parse_rds_ps_block(struct bcm2048_device *bdev, int i,
+ int index, int crc)
+{
+ /* Good data will overwrite poor data */
+ if (crc) {
+ if (!bdev->rds_info.rds_ps[index])
+ bdev->rds_info.rds_ps[index] =
+ bdev->rds_info.radio_text[i+1];
+ if (!bdev->rds_info.rds_ps[index+1])
+ bdev->rds_info.rds_ps[index+1] =
+ bdev->rds_info.radio_text[i+2];
+ } else {
+ bdev->rds_info.rds_ps[index] = bdev->rds_info.radio_text[i+1];
+ bdev->rds_info.rds_ps[index+1] =
+ bdev->rds_info.radio_text[i+2];
+ }
+}
+
+static int bcm2048_parse_ps_match_c(struct bcm2048_device *bdev, int i,
+ int index)
+{
+ int crc;
+
+ crc = bcm2048_rds_block_crc(bdev, i);
+
+ if (crc == BCM2048_RDS_CRC_UNRECOVARABLE)
+ return 0;
+
+ if ((bdev->rds_info.radio_text[i] & BCM2048_RDS_BLOCK_MASK) ==
+ BCM2048_RDS_BLOCK_C)
+ return 1;
+
+ return 0;
+}
+
+static void bcm2048_parse_ps_match_d(struct bcm2048_device *bdev, int i,
+ int index)
+{
+ int crc;
+
+ crc = bcm2048_rds_block_crc(bdev, i);
+
+ if (crc == BCM2048_RDS_CRC_UNRECOVARABLE)
+ return;
+
+ if ((bdev->rds_info.radio_text[i] & BCM2048_RDS_BLOCK_MASK) ==
+ BCM2048_RDS_BLOCK_D)
+ bcm2048_parse_rds_ps_block(bdev, i, index, crc);
+}
+
+static int bcm2048_parse_ps_match_b(struct bcm2048_device *bdev, int i)
+{
+ int crc, index, ps_id, ps_group;
+
+ crc = bcm2048_rds_block_crc(bdev, i);
+
+ if (crc == BCM2048_RDS_CRC_UNRECOVARABLE)
+ return -EIO;
+
+ /* Block B Radio PS match */
+ if ((bdev->rds_info.radio_text[i] & BCM2048_RDS_BLOCK_MASK) ==
+ BCM2048_RDS_BLOCK_B) {
+ ps_id = bdev->rds_info.radio_text[i+1] &
+ BCM2048_RDS_BLOCK_MASK;
+ ps_group = bdev->rds_info.radio_text[i+1] &
+ BCM2048_RDS_GROUP_AB_MASK;
+
+ /*
+ * Poor RSSI will lead to RDS data corruption
+ * So using 3 (same) sequential values to justify major changes
+ */
+ if (ps_group != bdev->rds_info.rds_ps_group) {
+ if (crc == BCM2048_RDS_CRC_NONE) {
+ bdev->rds_info.rds_ps_group_cnt++;
+ if (bdev->rds_info.rds_ps_group_cnt > 2) {
+ bdev->rds_info.rds_ps_group = ps_group;
+ bdev->rds_info.rds_ps_group_cnt = 0;
+ dev_err(&bdev->client->dev,
+ "RDS PS Group change!\n");
+ } else {
+ return -EIO;
+ }
+ } else {
+ bdev->rds_info.rds_ps_group_cnt = 0;
+ }
+ }
+
+ if (ps_id == BCM2048_RDS_PS) {
+ index = bdev->rds_info.radio_text[i+2] &
+ BCM2048_RDS_PS_INDEX;
+ index <<= 1;
+ return index;
+ }
+ }
+
+ return -EIO;
+}
+
+static void bcm2048_parse_rds_ps(struct bcm2048_device *bdev)
+{
+ int i, index = 0, crc, match_b = 0, match_c = 0, match_d = 0;
+
+ for (i = 0; i < bdev->fifo_size; i += BCM2048_RDS_FIFO_DUPLE_SIZE) {
+
+ if (match_b) {
+ match_b = 0;
+ index = bcm2048_parse_ps_match_b(bdev, i);
+ if (index >= 0 && index < (BCM2048_MAX_RDS_PS - 1))
+ match_c = 1;
+ continue;
+ } else if (match_c) {
+ match_c = 0;
+ if (bcm2048_parse_ps_match_c(bdev, i, index))
+ match_d = 1;
+ continue;
+ } else if (match_d) {
+ match_d = 0;
+ bcm2048_parse_ps_match_d(bdev, i, index);
+ continue;
+ }
+
+ /* Skip erroneous blocks due to messed up A block altogether */
+ if ((bdev->rds_info.radio_text[i] & BCM2048_RDS_BLOCK_MASK)
+ == BCM2048_RDS_BLOCK_A) {
+ crc = bcm2048_rds_block_crc(bdev, i);
+ if (crc == BCM2048_RDS_CRC_UNRECOVARABLE)
+ continue;
+ /* Syncronize to a good RDS PI */
+ if (((bdev->rds_info.radio_text[i+1] << 8) +
+ bdev->rds_info.radio_text[i+2]) ==
+ bdev->rds_info.rds_pi)
+ match_b = 1;
+ }
+ }
+}
+
+static void bcm2048_rds_fifo_receive(struct bcm2048_device *bdev)
+{
+ int err;
+
+ mutex_lock(&bdev->mutex);
+
+ err = bcm2048_recv_duples(bdev, BCM2048_I2C_RDS_DATA,
+ bdev->rds_info.radio_text, bdev->fifo_size);
+ if (err != 2) {
+ dev_err(&bdev->client->dev, "RDS Read problem\n");
+ mutex_unlock(&bdev->mutex);
+ return;
+ }
+
+ bdev->rds_info.text_len = bdev->fifo_size;
+
+ bcm2048_parse_rds_pi(bdev);
+ bcm2048_parse_rds_rt(bdev);
+ bcm2048_parse_rds_ps(bdev);
+
+ mutex_unlock(&bdev->mutex);
+
+ wake_up_interruptible(&bdev->read_queue);
+}
+
+static int bcm2048_get_rds_data(struct bcm2048_device *bdev, char *data)
+{
+ int err = 0, i, p = 0;
+ char *data_buffer;
+
+ mutex_lock(&bdev->mutex);
+
+ if (!bdev->rds_info.text_len) {
+ err = -EINVAL;
+ goto unlock;
+ }
+
+ data_buffer = kcalloc(BCM2048_MAX_RDS_RADIO_TEXT, 5, GFP_KERNEL);
+ if (!data_buffer) {
+ err = -ENOMEM;
+ goto unlock;
+ }
+
+ for (i = 0; i < bdev->rds_info.text_len; i++) {
+ p += sprintf(data_buffer+p, "%x ",
+ bdev->rds_info.radio_text[i]);
+ }
+
+ memcpy(data, data_buffer, p);
+ kfree(data_buffer);
+
+unlock:
+ mutex_unlock(&bdev->mutex);
+ return err;
+}
+
+/*
+ * BCM2048 default initialization sequence
+ */
+static int bcm2048_init(struct bcm2048_device *bdev)
+{
+ int err;
+
+ err = bcm2048_set_power_state(bdev, BCM2048_POWER_ON);
+ if (err < 0)
+ goto exit;
+
+ err = bcm2048_set_audio_route(bdev, BCM2048_AUDIO_ROUTE_DAC);
+ if (err < 0)
+ goto exit;
+
+ err = bcm2048_set_dac_output(bdev, BCM2048_DAC_OUTPUT_LEFT |
+ BCM2048_DAC_OUTPUT_RIGHT);
+
+exit:
+ return err;
+}
+
+/*
+ * BCM2048 default deinitialization sequence
+ */
+static int bcm2048_deinit(struct bcm2048_device *bdev)
+{
+ int err;
+
+ err = bcm2048_set_audio_route(bdev, 0);
+ if (err < 0)
+ goto exit;
+
+ err = bcm2048_set_dac_output(bdev, 0);
+ if (err < 0)
+ goto exit;
+
+ err = bcm2048_set_power_state(bdev, BCM2048_POWER_OFF);
+ if (err < 0)
+ goto exit;
+
+exit:
+ return err;
+}
+
+/*
+ * BCM2048 probe sequence
+ */
+static int bcm2048_probe(struct bcm2048_device *bdev)
+{
+ int err;
+
+ err = bcm2048_set_power_state(bdev, BCM2048_POWER_ON);
+ if (err < 0)
+ goto unlock;
+
+ err = bcm2048_checkrev(bdev);
+ if (err < 0)
+ goto unlock;
+
+ err = bcm2048_set_mute(bdev, BCM2048_DEFAULT_MUTE);
+ if (err < 0)
+ goto unlock;
+
+ err = bcm2048_set_region(bdev, BCM2048_DEFAULT_REGION);
+ if (err < 0)
+ goto unlock;
+
+ err = bcm2048_set_fm_search_rssi_threshold(bdev,
+ BCM2048_DEFAULT_RSSI_THRESHOLD);
+ if (err < 0)
+ goto unlock;
+
+ err = bcm2048_set_fm_automatic_stereo_mono(bdev, BCM2048_ITEM_ENABLED);
+ if (err < 0)
+ goto unlock;
+
+ err = bcm2048_get_rds_wline(bdev);
+ if (err < BCM2048_DEFAULT_RDS_WLINE)
+ err = bcm2048_set_rds_wline(bdev, BCM2048_DEFAULT_RDS_WLINE);
+ if (err < 0)
+ goto unlock;
+
+ err = bcm2048_set_power_state(bdev, BCM2048_POWER_OFF);
+
+ init_waitqueue_head(&bdev->read_queue);
+ bdev->rds_data_available = 0;
+ bdev->rd_index = 0;
+ bdev->users = 0;
+
+unlock:
+ return err;
+}
+
+/*
+ * BCM2048 workqueue handler
+ */
+static void bcm2048_work(struct work_struct *work)
+{
+ struct bcm2048_device *bdev;
+ u8 flag_lsb, flag_msb, flags;
+
+ bdev = container_of(work, struct bcm2048_device, work);
+ bcm2048_recv_command(bdev, BCM2048_I2C_FM_RDS_FLAG0, &flag_lsb);
+ bcm2048_recv_command(bdev, BCM2048_I2C_FM_RDS_FLAG1, &flag_msb);
+
+ if (flag_lsb & (BCM2048_FM_FLAG_SEARCH_TUNE_FINISHED |
+ BCM2048_FM_FLAG_SEARCH_TUNE_FAIL)) {
+
+ if (flag_lsb & BCM2048_FM_FLAG_SEARCH_TUNE_FAIL)
+ bdev->scan_state = BCM2048_SCAN_FAIL;
+ else
+ bdev->scan_state = BCM2048_SCAN_OK;
+
+ complete(&bdev->compl);
+ }
+
+ if (flag_msb & BCM2048_RDS_FLAG_FIFO_WLINE) {
+ bcm2048_rds_fifo_receive(bdev);
+ if (bdev->rds_state) {
+ flags = BCM2048_RDS_FLAG_FIFO_WLINE;
+ bcm2048_send_command(bdev, BCM2048_I2C_FM_RDS_MASK1,
+ flags);
+ }
+ bdev->rds_data_available = 1;
+ bdev->rd_index = 0; /* new data, new start */
+ }
+}
+
+/*
+ * BCM2048 interrupt handler
+ */
+static irqreturn_t bcm2048_handler(int irq, void *dev)
+{
+ struct bcm2048_device *bdev = dev;
+
+ dev_dbg(&bdev->client->dev, "IRQ called, queuing work\n");
+ if (bdev->power_state)
+ schedule_work(&bdev->work);
+
+ return IRQ_HANDLED;
+}
+
+/*
+ * BCM2048 sysfs interface definitions
+ */
+#define property_write(prop, type, mask, check) \
+static ssize_t bcm2048_##prop##_write(struct device *dev, \
+ struct device_attribute *attr, \
+ const char *buf, \
+ size_t count) \
+{ \
+ struct bcm2048_device *bdev = dev_get_drvdata(dev); \
+ type value; \
+ int err; \
+ \
+ if (!bdev) \
+ return -ENODEV; \
+ \
+ if (sscanf(buf, mask, &value) != 1) \
+ return -EINVAL; \
+ \
+ if (check) \
+ return -EDOM; \
+ \
+ err = bcm2048_set_##prop(bdev, value); \
+ \
+ return err < 0 ? err : count; \
+}
+
+#define property_read(prop, size, mask) \
+static ssize_t bcm2048_##prop##_read(struct device *dev, \
+ struct device_attribute *attr, \
+ char *buf) \
+{ \
+ struct bcm2048_device *bdev = dev_get_drvdata(dev); \
+ int value; \
+ \
+ if (!bdev) \
+ return -ENODEV; \
+ \
+ value = bcm2048_get_##prop(bdev); \
+ \
+ if (value >= 0) \
+ value = sprintf(buf, mask "\n", value); \
+ \
+ return value; \
+}
+
+#define property_signed_read(prop, size, mask) \
+static ssize_t bcm2048_##prop##_read(struct device *dev, \
+ struct device_attribute *attr, \
+ char *buf) \
+{ \
+ struct bcm2048_device *bdev = dev_get_drvdata(dev); \
+ size value; \
+ \
+ if (!bdev) \
+ return -ENODEV; \
+ \
+ value = bcm2048_get_##prop(bdev); \
+ \
+ value = sprintf(buf, mask "\n", value); \
+ \
+ return value; \
+}
+
+#define DEFINE_SYSFS_PROPERTY(prop, signal, size, mask, check) \
+property_write(prop, signal size, mask, check) \
+property_read(prop, size, mask)
+
+#define property_str_read(prop, size) \
+static ssize_t bcm2048_##prop##_read(struct device *dev, \
+ struct device_attribute *attr, \
+ char *buf) \
+{ \
+ struct bcm2048_device *bdev = dev_get_drvdata(dev); \
+ int count; \
+ u8 *out; \
+ \
+ if (!bdev) \
+ return -ENODEV; \
+ \
+ out = kzalloc(size + 1, GFP_KERNEL); \
+ if (!out) \
+ return -ENOMEM; \
+ \
+ bcm2048_get_##prop(bdev, out); \
+ count = sprintf(buf, "%s\n", out); \
+ \
+ kfree(out); \
+ \
+ return count; \
+}
+
+DEFINE_SYSFS_PROPERTY(power_state, unsigned, int, "%u", 0)
+DEFINE_SYSFS_PROPERTY(mute, unsigned, int, "%u", 0)
+DEFINE_SYSFS_PROPERTY(audio_route, unsigned, int, "%u", 0)
+DEFINE_SYSFS_PROPERTY(dac_output, unsigned, int, "%u", 0)
+
+DEFINE_SYSFS_PROPERTY(fm_hi_lo_injection, unsigned, int, "%u", 0)
+DEFINE_SYSFS_PROPERTY(fm_frequency, unsigned, int, "%u", 0)
+DEFINE_SYSFS_PROPERTY(fm_af_frequency, unsigned, int, "%u", 0)
+DEFINE_SYSFS_PROPERTY(fm_deemphasis, unsigned, int, "%u", 0)
+DEFINE_SYSFS_PROPERTY(fm_rds_mask, unsigned, int, "%u", 0)
+DEFINE_SYSFS_PROPERTY(fm_best_tune_mode, unsigned, int, "%u", 0)
+DEFINE_SYSFS_PROPERTY(fm_search_rssi_threshold, unsigned, int, "%u", 0)
+DEFINE_SYSFS_PROPERTY(fm_search_mode_direction, unsigned, int, "%u", 0)
+DEFINE_SYSFS_PROPERTY(fm_search_tune_mode, unsigned, int, "%u", value > 3)
+
+DEFINE_SYSFS_PROPERTY(rds, unsigned, int, "%u", 0)
+DEFINE_SYSFS_PROPERTY(rds_b_block_mask, unsigned, int, "%u", 0)
+DEFINE_SYSFS_PROPERTY(rds_b_block_match, unsigned, int, "%u", 0)
+DEFINE_SYSFS_PROPERTY(rds_pi_mask, unsigned, int, "%u", 0)
+DEFINE_SYSFS_PROPERTY(rds_pi_match, unsigned, int, "%u", 0)
+DEFINE_SYSFS_PROPERTY(rds_wline, unsigned, int, "%u", 0)
+property_read(rds_pi, unsigned int, "%x")
+property_str_read(rds_rt, (BCM2048_MAX_RDS_RT + 1))
+property_str_read(rds_ps, (BCM2048_MAX_RDS_PS + 1))
+
+property_read(fm_rds_flags, unsigned int, "%u")
+property_str_read(rds_data, BCM2048_MAX_RDS_RADIO_TEXT*5)
+
+property_read(region_bottom_frequency, unsigned int, "%u")
+property_read(region_top_frequency, unsigned int, "%u")
+property_signed_read(fm_carrier_error, int, "%d")
+property_signed_read(fm_rssi, int, "%d")
+DEFINE_SYSFS_PROPERTY(region, unsigned, int, "%u", 0)
+
+static struct device_attribute attrs[] = {
+ __ATTR(power_state, S_IRUGO | S_IWUSR, bcm2048_power_state_read,
+ bcm2048_power_state_write),
+ __ATTR(mute, S_IRUGO | S_IWUSR, bcm2048_mute_read,
+ bcm2048_mute_write),
+ __ATTR(audio_route, S_IRUGO | S_IWUSR, bcm2048_audio_route_read,
+ bcm2048_audio_route_write),
+ __ATTR(dac_output, S_IRUGO | S_IWUSR, bcm2048_dac_output_read,
+ bcm2048_dac_output_write),
+ __ATTR(fm_hi_lo_injection, S_IRUGO | S_IWUSR,
+ bcm2048_fm_hi_lo_injection_read,
+ bcm2048_fm_hi_lo_injection_write),
+ __ATTR(fm_frequency, S_IRUGO | S_IWUSR, bcm2048_fm_frequency_read,
+ bcm2048_fm_frequency_write),
+ __ATTR(fm_af_frequency, S_IRUGO | S_IWUSR,
+ bcm2048_fm_af_frequency_read,
+ bcm2048_fm_af_frequency_write),
+ __ATTR(fm_deemphasis, S_IRUGO | S_IWUSR, bcm2048_fm_deemphasis_read,
+ bcm2048_fm_deemphasis_write),
+ __ATTR(fm_rds_mask, S_IRUGO | S_IWUSR, bcm2048_fm_rds_mask_read,
+ bcm2048_fm_rds_mask_write),
+ __ATTR(fm_best_tune_mode, S_IRUGO | S_IWUSR,
+ bcm2048_fm_best_tune_mode_read,
+ bcm2048_fm_best_tune_mode_write),
+ __ATTR(fm_search_rssi_threshold, S_IRUGO | S_IWUSR,
+ bcm2048_fm_search_rssi_threshold_read,
+ bcm2048_fm_search_rssi_threshold_write),
+ __ATTR(fm_search_mode_direction, S_IRUGO | S_IWUSR,
+ bcm2048_fm_search_mode_direction_read,
+ bcm2048_fm_search_mode_direction_write),
+ __ATTR(fm_search_tune_mode, S_IRUGO | S_IWUSR,
+ bcm2048_fm_search_tune_mode_read,
+ bcm2048_fm_search_tune_mode_write),
+ __ATTR(rds, S_IRUGO | S_IWUSR, bcm2048_rds_read,
+ bcm2048_rds_write),
+ __ATTR(rds_b_block_mask, S_IRUGO | S_IWUSR,
+ bcm2048_rds_b_block_mask_read,
+ bcm2048_rds_b_block_mask_write),
+ __ATTR(rds_b_block_match, S_IRUGO | S_IWUSR,
+ bcm2048_rds_b_block_match_read,
+ bcm2048_rds_b_block_match_write),
+ __ATTR(rds_pi_mask, S_IRUGO | S_IWUSR, bcm2048_rds_pi_mask_read,
+ bcm2048_rds_pi_mask_write),
+ __ATTR(rds_pi_match, S_IRUGO | S_IWUSR, bcm2048_rds_pi_match_read,
+ bcm2048_rds_pi_match_write),
+ __ATTR(rds_wline, S_IRUGO | S_IWUSR, bcm2048_rds_wline_read,
+ bcm2048_rds_wline_write),
+ __ATTR(rds_pi, S_IRUGO, bcm2048_rds_pi_read, NULL),
+ __ATTR(rds_rt, S_IRUGO, bcm2048_rds_rt_read, NULL),
+ __ATTR(rds_ps, S_IRUGO, bcm2048_rds_ps_read, NULL),
+ __ATTR(fm_rds_flags, S_IRUGO, bcm2048_fm_rds_flags_read, NULL),
+ __ATTR(region_bottom_frequency, S_IRUGO,
+ bcm2048_region_bottom_frequency_read, NULL),
+ __ATTR(region_top_frequency, S_IRUGO,
+ bcm2048_region_top_frequency_read, NULL),
+ __ATTR(fm_carrier_error, S_IRUGO,
+ bcm2048_fm_carrier_error_read, NULL),
+ __ATTR(fm_rssi, S_IRUGO,
+ bcm2048_fm_rssi_read, NULL),
+ __ATTR(region, S_IRUGO | S_IWUSR, bcm2048_region_read,
+ bcm2048_region_write),
+ __ATTR(rds_data, S_IRUGO, bcm2048_rds_data_read, NULL),
+};
+
+static int bcm2048_sysfs_unregister_properties(struct bcm2048_device *bdev,
+ int size)
+{
+ int i;
+
+ for (i = 0; i < size; i++)
+ device_remove_file(&bdev->client->dev, &attrs[i]);
+
+ return 0;
+}
+
+static int bcm2048_sysfs_register_properties(struct bcm2048_device *bdev)
+{
+ int err = 0;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(attrs); i++) {
+ if (device_create_file(&bdev->client->dev, &attrs[i]) != 0) {
+ dev_err(&bdev->client->dev,
+ "could not register sysfs entry\n");
+ err = -EBUSY;
+ bcm2048_sysfs_unregister_properties(bdev, i);
+ break;
+ }
+ }
+
+ return err;
+}
+
+
+static int bcm2048_fops_open(struct file *file)
+{
+ struct bcm2048_device *bdev = video_drvdata(file);
+
+ bdev->users++;
+ bdev->rd_index = 0;
+ bdev->rds_data_available = 0;
+
+ return 0;
+}
+
+static int bcm2048_fops_release(struct file *file)
+{
+ struct bcm2048_device *bdev = video_drvdata(file);
+
+ bdev->users--;
+
+ return 0;
+}
+
+static unsigned int bcm2048_fops_poll(struct file *file,
+ struct poll_table_struct *pts)
+{
+ struct bcm2048_device *bdev = video_drvdata(file);
+ int retval = 0;
+
+ poll_wait(file, &bdev->read_queue, pts);
+
+ if (bdev->rds_data_available)
+ retval = POLLIN | POLLRDNORM;
+
+ return retval;
+}
+
+static ssize_t bcm2048_fops_read(struct file *file, char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ struct bcm2048_device *bdev = video_drvdata(file);
+ int i;
+ int retval = 0;
+
+ /* we return at least 3 bytes, one block */
+ count = (count / 3) * 3; /* only multiples of 3 */
+ if (count < 3)
+ return -ENOBUFS;
+
+ while (!bdev->rds_data_available) {
+ if (file->f_flags & O_NONBLOCK) {
+ retval = -EWOULDBLOCK;
+ goto done;
+ }
+ /* interruptible_sleep_on(&bdev->read_queue); */
+ if (wait_event_interruptible(bdev->read_queue,
+ bdev->rds_data_available) < 0) {
+ retval = -EINTR;
+ goto done;
+ }
+ }
+
+ mutex_lock(&bdev->mutex);
+ /* copy data to userspace */
+ i = bdev->fifo_size - bdev->rd_index;
+ if (count > i)
+ count = (i / 3) * 3;
+
+ i = 0;
+ while (i < count) {
+ unsigned char tmpbuf[3];
+
+ tmpbuf[i] = bdev->rds_info.radio_text[bdev->rd_index+i+2];
+ tmpbuf[i+1] = bdev->rds_info.radio_text[bdev->rd_index+i+1];
+ tmpbuf[i+2] = (bdev->rds_info.radio_text[bdev->rd_index + i] & 0xf0) >> 4;
+ if ((bdev->rds_info.radio_text[bdev->rd_index+i] &
+ BCM2048_RDS_CRC_MASK) == BCM2048_RDS_CRC_UNRECOVARABLE)
+ tmpbuf[i+2] |= 0x80;
+ if (copy_to_user(buf+i, tmpbuf, 3)) {
+ retval = -EFAULT;
+ break;
+ }
+ i += 3;
+ }
+
+ bdev->rd_index += i;
+ if (bdev->rd_index >= bdev->fifo_size)
+ bdev->rds_data_available = 0;
+
+ mutex_unlock(&bdev->mutex);
+ if (retval == 0)
+ retval = i;
+
+done:
+ return retval;
+}
+
+/*
+ * bcm2048_fops - file operations interface
+ */
+static const struct v4l2_file_operations bcm2048_fops = {
+ .owner = THIS_MODULE,
+ .unlocked_ioctl = video_ioctl2,
+ /* for RDS read support */
+ .open = bcm2048_fops_open,
+ .release = bcm2048_fops_release,
+ .read = bcm2048_fops_read,
+ .poll = bcm2048_fops_poll
+};
+
+/*
+ * Video4Linux Interface
+ */
+static struct v4l2_queryctrl bcm2048_v4l2_queryctrl[] = {
+ {
+ .id = V4L2_CID_AUDIO_VOLUME,
+ .flags = V4L2_CTRL_FLAG_DISABLED,
+ },
+ {
+ .id = V4L2_CID_AUDIO_BALANCE,
+ .flags = V4L2_CTRL_FLAG_DISABLED,
+ },
+ {
+ .id = V4L2_CID_AUDIO_BASS,
+ .flags = V4L2_CTRL_FLAG_DISABLED,
+ },
+ {
+ .id = V4L2_CID_AUDIO_TREBLE,
+ .flags = V4L2_CTRL_FLAG_DISABLED,
+ },
+ {
+ .id = V4L2_CID_AUDIO_MUTE,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "Mute",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 1,
+ },
+ {
+ .id = V4L2_CID_AUDIO_LOUDNESS,
+ .flags = V4L2_CTRL_FLAG_DISABLED,
+ },
+};
+
+static int bcm2048_vidioc_querycap(struct file *file, void *priv,
+ struct v4l2_capability *capability)
+{
+ struct bcm2048_device *bdev = video_get_drvdata(video_devdata(file));
+
+ strlcpy(capability->driver, BCM2048_DRIVER_NAME,
+ sizeof(capability->driver));
+ strlcpy(capability->card, BCM2048_DRIVER_CARD,
+ sizeof(capability->card));
+ snprintf(capability->bus_info, 32, "I2C: 0x%X", bdev->client->addr);
+ capability->device_caps = V4L2_CAP_TUNER | V4L2_CAP_RADIO |
+ V4L2_CAP_HW_FREQ_SEEK;
+ capability->capabilities = capability->device_caps |
+ V4L2_CAP_DEVICE_CAPS;
+
+ return 0;
+}
+
+static int bcm2048_vidioc_g_input(struct file *filp, void *priv,
+ unsigned int *i)
+{
+ *i = 0;
+
+ return 0;
+}
+
+static int bcm2048_vidioc_s_input(struct file *filp, void *priv,
+ unsigned int i)
+{
+ if (i)
+ return -EINVAL;
+
+ return 0;
+}
+
+static int bcm2048_vidioc_queryctrl(struct file *file, void *priv,
+ struct v4l2_queryctrl *qc)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(bcm2048_v4l2_queryctrl); i++) {
+ if (qc->id && qc->id == bcm2048_v4l2_queryctrl[i].id) {
+ *qc = bcm2048_v4l2_queryctrl[i];
+ return 0;
+ }
+ }
+
+ return -EINVAL;
+}
+
+static int bcm2048_vidioc_g_ctrl(struct file *file, void *priv,
+ struct v4l2_control *ctrl)
+{
+ struct bcm2048_device *bdev = video_get_drvdata(video_devdata(file));
+ int err = 0;
+
+ if (!bdev)
+ return -ENODEV;
+
+ switch (ctrl->id) {
+ case V4L2_CID_AUDIO_MUTE:
+ err = bcm2048_get_mute(bdev);
+ if (err >= 0)
+ ctrl->value = err;
+ break;
+ }
+
+ return err;
+}
+
+static int bcm2048_vidioc_s_ctrl(struct file *file, void *priv,
+ struct v4l2_control *ctrl)
+{
+ struct bcm2048_device *bdev = video_get_drvdata(video_devdata(file));
+ int err = 0;
+
+ if (!bdev)
+ return -ENODEV;
+
+ switch (ctrl->id) {
+ case V4L2_CID_AUDIO_MUTE:
+ if (ctrl->value) {
+ if (bdev->power_state) {
+ err = bcm2048_set_mute(bdev, ctrl->value);
+ err |= bcm2048_deinit(bdev);
+ }
+ } else {
+ if (!bdev->power_state) {
+ err = bcm2048_init(bdev);
+ err |= bcm2048_set_mute(bdev, ctrl->value);
+ }
+ }
+ break;
+ }
+
+ return err;
+}
+
+static int bcm2048_vidioc_g_audio(struct file *file, void *priv,
+ struct v4l2_audio *audio)
+{
+ if (audio->index > 1)
+ return -EINVAL;
+
+ strncpy(audio->name, "Radio", 32);
+ audio->capability = V4L2_AUDCAP_STEREO;
+
+ return 0;
+}
+
+static int bcm2048_vidioc_s_audio(struct file *file, void *priv,
+ const struct v4l2_audio *audio)
+{
+ if (audio->index != 0)
+ return -EINVAL;
+
+ return 0;
+}
+
+static int bcm2048_vidioc_g_tuner(struct file *file, void *priv,
+ struct v4l2_tuner *tuner)
+{
+ struct bcm2048_device *bdev = video_get_drvdata(video_devdata(file));
+ s8 f_error;
+ s8 rssi;
+
+ if (!bdev)
+ return -ENODEV;
+
+ if (tuner->index > 0)
+ return -EINVAL;
+
+ strncpy(tuner->name, "FM Receiver", 32);
+ tuner->type = V4L2_TUNER_RADIO;
+ tuner->rangelow =
+ dev_to_v4l2(bcm2048_get_region_bottom_frequency(bdev));
+ tuner->rangehigh =
+ dev_to_v4l2(bcm2048_get_region_top_frequency(bdev));
+ tuner->rxsubchans = V4L2_TUNER_SUB_STEREO;
+ tuner->capability = V4L2_TUNER_CAP_STEREO | V4L2_TUNER_CAP_LOW;
+ tuner->audmode = V4L2_TUNER_MODE_STEREO;
+ tuner->afc = 0;
+ if (bdev->power_state) {
+ /*
+ * Report frequencies with high carrier errors to have zero
+ * signal level
+ */
+ f_error = bcm2048_get_fm_carrier_error(bdev);
+ if (f_error < BCM2048_FREQ_ERROR_FLOOR ||
+ f_error > BCM2048_FREQ_ERROR_ROOF) {
+ tuner->signal = 0;
+ } else {
+ /*
+ * RSSI level -60 dB is defined to report full
+ * signal strenght
+ */
+ rssi = bcm2048_get_fm_rssi(bdev);
+ if (rssi >= BCM2048_RSSI_LEVEL_BASE) {
+ tuner->signal = 0xFFFF;
+ } else if (rssi > BCM2048_RSSI_LEVEL_ROOF) {
+ tuner->signal = (rssi +
+ BCM2048_RSSI_LEVEL_ROOF_NEG)
+ * BCM2048_SIGNAL_MULTIPLIER;
+ } else {
+ tuner->signal = 0;
+ }
+ }
+ } else {
+ tuner->signal = 0;
+ }
+
+ return 0;
+}
+
+static int bcm2048_vidioc_s_tuner(struct file *file, void *priv,
+ const struct v4l2_tuner *tuner)
+{
+ struct bcm2048_device *bdev = video_get_drvdata(video_devdata(file));
+
+ if (!bdev)
+ return -ENODEV;
+
+ if (tuner->index > 0)
+ return -EINVAL;
+
+ return 0;
+}
+
+static int bcm2048_vidioc_g_frequency(struct file *file, void *priv,
+ struct v4l2_frequency *freq)
+{
+ struct bcm2048_device *bdev = video_get_drvdata(video_devdata(file));
+ int err = 0;
+ int f;
+
+ if (!bdev->power_state)
+ return -ENODEV;
+
+ freq->type = V4L2_TUNER_RADIO;
+ f = bcm2048_get_fm_frequency(bdev);
+
+ if (f < 0)
+ err = f;
+ else
+ freq->frequency = dev_to_v4l2(f);
+
+ return err;
+}
+
+static int bcm2048_vidioc_s_frequency(struct file *file, void *priv,
+ const struct v4l2_frequency *freq)
+{
+ struct bcm2048_device *bdev = video_get_drvdata(video_devdata(file));
+ int err;
+
+ if (freq->type != V4L2_TUNER_RADIO)
+ return -EINVAL;
+
+ if (!bdev->power_state)
+ return -ENODEV;
+
+ err = bcm2048_set_fm_frequency(bdev, v4l2_to_dev(freq->frequency));
+ err |= bcm2048_set_fm_search_tune_mode(bdev, BCM2048_FM_PRE_SET_MODE);
+
+ return err;
+}
+
+static int bcm2048_vidioc_s_hw_freq_seek(struct file *file, void *priv,
+ const struct v4l2_hw_freq_seek *seek)
+{
+ struct bcm2048_device *bdev = video_get_drvdata(video_devdata(file));
+ int err;
+
+ if (!bdev->power_state)
+ return -ENODEV;
+
+ if ((seek->tuner != 0) || (seek->type != V4L2_TUNER_RADIO))
+ return -EINVAL;
+
+ err = bcm2048_set_fm_search_mode_direction(bdev, seek->seek_upward);
+ err |= bcm2048_set_fm_search_tune_mode(bdev,
+ BCM2048_FM_AUTO_SEARCH_MODE);
+
+ return err;
+}
+
+static struct v4l2_ioctl_ops bcm2048_ioctl_ops = {
+ .vidioc_querycap = bcm2048_vidioc_querycap,
+ .vidioc_g_input = bcm2048_vidioc_g_input,
+ .vidioc_s_input = bcm2048_vidioc_s_input,
+ .vidioc_queryctrl = bcm2048_vidioc_queryctrl,
+ .vidioc_g_ctrl = bcm2048_vidioc_g_ctrl,
+ .vidioc_s_ctrl = bcm2048_vidioc_s_ctrl,
+ .vidioc_g_audio = bcm2048_vidioc_g_audio,
+ .vidioc_s_audio = bcm2048_vidioc_s_audio,
+ .vidioc_g_tuner = bcm2048_vidioc_g_tuner,
+ .vidioc_s_tuner = bcm2048_vidioc_s_tuner,
+ .vidioc_g_frequency = bcm2048_vidioc_g_frequency,
+ .vidioc_s_frequency = bcm2048_vidioc_s_frequency,
+ .vidioc_s_hw_freq_seek = bcm2048_vidioc_s_hw_freq_seek,
+};
+
+/*
+ * bcm2048_viddev_template - video device interface
+ */
+static struct video_device bcm2048_viddev_template = {
+ .fops = &bcm2048_fops,
+ .name = BCM2048_DRIVER_NAME,
+ .release = video_device_release_empty,
+ .ioctl_ops = &bcm2048_ioctl_ops,
+};
+
+/*
+ * I2C driver interface
+ */
+static int bcm2048_i2c_driver_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct bcm2048_device *bdev;
+ int err, skip_release = 0;
+
+ bdev = kzalloc(sizeof(*bdev), GFP_KERNEL);
+ if (!bdev) {
+ err = -ENOMEM;
+ goto exit;
+ }
+
+ bdev->client = client;
+ i2c_set_clientdata(client, bdev);
+ mutex_init(&bdev->mutex);
+ init_completion(&bdev->compl);
+ INIT_WORK(&bdev->work, bcm2048_work);
+
+ if (client->irq) {
+ err = request_irq(client->irq,
+ bcm2048_handler, IRQF_TRIGGER_FALLING,
+ client->name, bdev);
+ if (err < 0) {
+ dev_err(&client->dev, "Could not request IRQ\n");
+ goto free_bdev;
+ }
+ dev_dbg(&client->dev, "IRQ requested.\n");
+ } else {
+ dev_dbg(&client->dev, "IRQ not configured. Using timeouts.\n");
+ }
+
+ bdev->videodev = bcm2048_viddev_template;
+ video_set_drvdata(&bdev->videodev, bdev);
+ if (video_register_device(&bdev->videodev, VFL_TYPE_RADIO, radio_nr)) {
+ dev_dbg(&client->dev, "Could not register video device.\n");
+ err = -EIO;
+ goto free_irq;
+ }
+
+ err = bcm2048_sysfs_register_properties(bdev);
+ if (err < 0) {
+ dev_dbg(&client->dev, "Could not register sysfs interface.\n");
+ goto free_registration;
+ }
+
+ err = bcm2048_probe(bdev);
+ if (err < 0) {
+ dev_dbg(&client->dev, "Failed to probe device information.\n");
+ goto free_sysfs;
+ }
+
+ return 0;
+
+free_sysfs:
+ bcm2048_sysfs_unregister_properties(bdev, ARRAY_SIZE(attrs));
+free_registration:
+ video_unregister_device(&bdev->videodev);
+ skip_release = 1;
+free_irq:
+ if (client->irq)
+ free_irq(client->irq, bdev);
+free_bdev:
+ i2c_set_clientdata(client, NULL);
+ kfree(bdev);
+exit:
+ return err;
+}
+
+static int __exit bcm2048_i2c_driver_remove(struct i2c_client *client)
+{
+ struct bcm2048_device *bdev = i2c_get_clientdata(client);
+
+ if (!client->adapter)
+ return -ENODEV;
+
+ if (bdev) {
+ bcm2048_sysfs_unregister_properties(bdev, ARRAY_SIZE(attrs));
+ video_unregister_device(&bdev->videodev);
+
+ if (bdev->power_state)
+ bcm2048_set_power_state(bdev, BCM2048_POWER_OFF);
+
+ if (client->irq > 0)
+ free_irq(client->irq, bdev);
+
+ cancel_work_sync(&bdev->work);
+
+ kfree(bdev);
+ }
+
+ return 0;
+}
+
+/*
+ * bcm2048_i2c_driver - i2c driver interface
+ */
+static const struct i2c_device_id bcm2048_id[] = {
+ { "bcm2048", 0 },
+ { },
+};
+MODULE_DEVICE_TABLE(i2c, bcm2048_id);
+
+static struct i2c_driver bcm2048_i2c_driver = {
+ .driver = {
+ .name = BCM2048_DRIVER_NAME,
+ },
+ .probe = bcm2048_i2c_driver_probe,
+ .remove = __exit_p(bcm2048_i2c_driver_remove),
+ .id_table = bcm2048_id,
+};
+
+module_i2c_driver(bcm2048_i2c_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR(BCM2048_DRIVER_AUTHOR);
+MODULE_DESCRIPTION(BCM2048_DRIVER_DESC);
+MODULE_VERSION("0.0.2");
diff --git a/drivers/staging/media/bcm2048/radio-bcm2048.h b/drivers/staging/media/bcm2048/radio-bcm2048.h
new file mode 100644
index 000000000..4c90a32db
--- /dev/null
+++ b/drivers/staging/media/bcm2048/radio-bcm2048.h
@@ -0,0 +1,30 @@
+/*
+ * drivers/staging/media/radio-bcm2048.h
+ *
+ * Property and command definitions for bcm2048 radio receiver chip.
+ *
+ * Copyright (C) Nokia Corporation
+ * Contact: Eero Nurkkala <ext-eero.nurkkala@nokia.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+#ifndef BCM2048_H
+#define BCM2048_H
+
+#define BCM2048_NAME "bcm2048"
+#define BCM2048_I2C_ADDR 0x22
+
+#endif /* ifndef BCM2048_H */
diff --git a/drivers/staging/media/cxd2099/Kconfig b/drivers/staging/media/cxd2099/Kconfig
new file mode 100644
index 000000000..b48aefddc
--- /dev/null
+++ b/drivers/staging/media/cxd2099/Kconfig
@@ -0,0 +1,12 @@
+config DVB_CXD2099
+ tristate "CXD2099AR Common Interface driver"
+ depends on DVB_CORE && PCI && I2C
+ ---help---
+ Support for the CI module found on cards based on
+ - Micronas ngene PCIe bridge: cineS2 etc.
+ - Digital Devices PCIe bridge: Octopus series
+
+ For now, data is passed through '/dev/dvb/adapterX/sec0':
+ - Encrypted data must be written to 'sec0'.
+ - Decrypted data can be read from 'sec0'.
+ - Setup the CAM using device 'ca0'.
diff --git a/drivers/staging/media/cxd2099/Makefile b/drivers/staging/media/cxd2099/Makefile
new file mode 100644
index 000000000..b2905e650
--- /dev/null
+++ b/drivers/staging/media/cxd2099/Makefile
@@ -0,0 +1,5 @@
+obj-$(CONFIG_DVB_CXD2099) += cxd2099.o
+
+ccflags-y += -Idrivers/media/dvb-core/
+ccflags-y += -Idrivers/media/dvb-frontends/
+ccflags-y += -Idrivers/media/tuners/
diff --git a/drivers/staging/media/cxd2099/TODO b/drivers/staging/media/cxd2099/TODO
new file mode 100644
index 000000000..375bb6f8e
--- /dev/null
+++ b/drivers/staging/media/cxd2099/TODO
@@ -0,0 +1,12 @@
+For now, data is passed through '/dev/dvb/adapterX/sec0':
+ - Encrypted data must be written to 'sec0'.
+ - Decrypted data can be read from 'sec0'.
+ - Setup the CAM using device 'ca0'.
+
+But this is wrong. There are some discussions about the proper way for
+doing it, as seen at:
+ http://www.mail-archive.com/linux-media@vger.kernel.org/msg22196.html
+
+While there's no proper fix for it, the driver should be kept in staging.
+
+Patches should be submitted to: linux-media@vger.kernel.org.
diff --git a/drivers/staging/media/cxd2099/cxd2099.c b/drivers/staging/media/cxd2099/cxd2099.c
new file mode 100644
index 000000000..692ba3e63
--- /dev/null
+++ b/drivers/staging/media/cxd2099/cxd2099.c
@@ -0,0 +1,721 @@
+/*
+ * cxd2099.c: Driver for the CXD2099AR Common Interface Controller
+ *
+ * Copyright (C) 2010-2011 Digital Devices GmbH
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 only, as published by the Free Software Foundation.
+ *
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA
+ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
+ */
+
+#include <linux/slab.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/wait.h>
+#include <linux/delay.h>
+#include <linux/mutex.h>
+#include <linux/io.h>
+
+#include "cxd2099.h"
+
+#define MAX_BUFFER_SIZE 248
+
+struct cxd {
+ struct dvb_ca_en50221 en;
+
+ struct i2c_adapter *i2c;
+ struct cxd2099_cfg cfg;
+
+ u8 regs[0x23];
+ u8 lastaddress;
+ u8 clk_reg_f;
+ u8 clk_reg_b;
+ int mode;
+ int ready;
+ int dr;
+ int slot_stat;
+
+ u8 amem[1024];
+ int amem_read;
+
+ int cammode;
+ struct mutex lock;
+};
+
+static int i2c_write_reg(struct i2c_adapter *adapter, u8 adr,
+ u8 reg, u8 data)
+{
+ u8 m[2] = {reg, data};
+ struct i2c_msg msg = {.addr = adr, .flags = 0, .buf = m, .len = 2};
+
+ if (i2c_transfer(adapter, &msg, 1) != 1) {
+ dev_err(&adapter->dev,
+ "Failed to write to I2C register %02x@%02x!\n",
+ reg, adr);
+ return -1;
+ }
+ return 0;
+}
+
+static int i2c_write(struct i2c_adapter *adapter, u8 adr,
+ u8 *data, u8 len)
+{
+ struct i2c_msg msg = {.addr = adr, .flags = 0, .buf = data, .len = len};
+
+ if (i2c_transfer(adapter, &msg, 1) != 1) {
+ dev_err(&adapter->dev, "Failed to write to I2C!\n");
+ return -1;
+ }
+ return 0;
+}
+
+static int i2c_read_reg(struct i2c_adapter *adapter, u8 adr,
+ u8 reg, u8 *val)
+{
+ struct i2c_msg msgs[2] = {{.addr = adr, .flags = 0,
+ .buf = &reg, .len = 1},
+ {.addr = adr, .flags = I2C_M_RD,
+ .buf = val, .len = 1} };
+
+ if (i2c_transfer(adapter, msgs, 2) != 2) {
+ dev_err(&adapter->dev, "error in i2c_read_reg\n");
+ return -1;
+ }
+ return 0;
+}
+
+static int i2c_read(struct i2c_adapter *adapter, u8 adr,
+ u8 reg, u8 *data, u8 n)
+{
+ struct i2c_msg msgs[2] = {{.addr = adr, .flags = 0,
+ .buf = &reg, .len = 1},
+ {.addr = adr, .flags = I2C_M_RD,
+ .buf = data, .len = n} };
+
+ if (i2c_transfer(adapter, msgs, 2) != 2) {
+ dev_err(&adapter->dev, "error in i2c_read\n");
+ return -1;
+ }
+ return 0;
+}
+
+static int read_block(struct cxd *ci, u8 adr, u8 *data, u8 n)
+{
+ int status;
+
+ status = i2c_write_reg(ci->i2c, ci->cfg.adr, 0, adr);
+ if (!status) {
+ ci->lastaddress = adr;
+ status = i2c_read(ci->i2c, ci->cfg.adr, 1, data, n);
+ }
+ return status;
+}
+
+static int read_reg(struct cxd *ci, u8 reg, u8 *val)
+{
+ return read_block(ci, reg, val, 1);
+}
+
+
+static int read_pccard(struct cxd *ci, u16 address, u8 *data, u8 n)
+{
+ int status;
+ u8 addr[3] = {2, address & 0xff, address >> 8};
+
+ status = i2c_write(ci->i2c, ci->cfg.adr, addr, 3);
+ if (!status)
+ status = i2c_read(ci->i2c, ci->cfg.adr, 3, data, n);
+ return status;
+}
+
+static int write_pccard(struct cxd *ci, u16 address, u8 *data, u8 n)
+{
+ int status;
+ u8 addr[3] = {2, address & 0xff, address >> 8};
+
+ status = i2c_write(ci->i2c, ci->cfg.adr, addr, 3);
+ if (!status) {
+ u8 buf[256] = {3};
+
+ memcpy(buf+1, data, n);
+ status = i2c_write(ci->i2c, ci->cfg.adr, buf, n+1);
+ }
+ return status;
+}
+
+static int read_io(struct cxd *ci, u16 address, u8 *val)
+{
+ int status;
+ u8 addr[3] = {2, address & 0xff, address >> 8};
+
+ status = i2c_write(ci->i2c, ci->cfg.adr, addr, 3);
+ if (!status)
+ status = i2c_read(ci->i2c, ci->cfg.adr, 3, val, 1);
+ return status;
+}
+
+static int write_io(struct cxd *ci, u16 address, u8 val)
+{
+ int status;
+ u8 addr[3] = {2, address & 0xff, address >> 8};
+ u8 buf[2] = {3, val};
+
+ status = i2c_write(ci->i2c, ci->cfg.adr, addr, 3);
+ if (!status)
+ status = i2c_write(ci->i2c, ci->cfg.adr, buf, 2);
+ return status;
+}
+
+#if 0
+static int read_io_data(struct cxd *ci, u8 *data, u8 n)
+{
+ int status;
+ u8 addr[3] = { 2, 0, 0 };
+
+ status = i2c_write(ci->i2c, ci->cfg.adr, addr, 3);
+ if (!status)
+ status = i2c_read(ci->i2c, ci->cfg.adr, 3, data, n);
+ return 0;
+}
+
+static int write_io_data(struct cxd *ci, u8 *data, u8 n)
+{
+ int status;
+ u8 addr[3] = {2, 0, 0};
+
+ status = i2c_write(ci->i2c, ci->cfg.adr, addr, 3);
+ if (!status) {
+ u8 buf[256] = {3};
+
+ memcpy(buf+1, data, n);
+ status = i2c_write(ci->i2c, ci->cfg.adr, buf, n + 1);
+ }
+ return 0;
+}
+#endif
+
+static int write_regm(struct cxd *ci, u8 reg, u8 val, u8 mask)
+{
+ int status;
+
+ status = i2c_write_reg(ci->i2c, ci->cfg.adr, 0, reg);
+ if (!status && reg >= 6 && reg <= 8 && mask != 0xff)
+ status = i2c_read_reg(ci->i2c, ci->cfg.adr, 1, &ci->regs[reg]);
+ ci->regs[reg] = (ci->regs[reg] & (~mask)) | val;
+ if (!status) {
+ ci->lastaddress = reg;
+ status = i2c_write_reg(ci->i2c, ci->cfg.adr, 1, ci->regs[reg]);
+ }
+ if (reg == 0x20)
+ ci->regs[reg] &= 0x7f;
+ return status;
+}
+
+static int write_reg(struct cxd *ci, u8 reg, u8 val)
+{
+ return write_regm(ci, reg, val, 0xff);
+}
+
+#ifdef BUFFER_MODE
+static int write_block(struct cxd *ci, u8 adr, u8 *data, int n)
+{
+ int status;
+ u8 buf[256] = {1};
+
+ status = i2c_write_reg(ci->i2c, ci->cfg.adr, 0, adr);
+ if (!status) {
+ ci->lastaddress = adr;
+ memcpy(buf + 1, data, n);
+ status = i2c_write(ci->i2c, ci->cfg.adr, buf, n + 1);
+ }
+ return status;
+}
+#endif
+
+static void set_mode(struct cxd *ci, int mode)
+{
+ if (mode == ci->mode)
+ return;
+
+ switch (mode) {
+ case 0x00: /* IO mem */
+ write_regm(ci, 0x06, 0x00, 0x07);
+ break;
+ case 0x01: /* ATT mem */
+ write_regm(ci, 0x06, 0x02, 0x07);
+ break;
+ default:
+ break;
+ }
+ ci->mode = mode;
+}
+
+static void cam_mode(struct cxd *ci, int mode)
+{
+ if (mode == ci->cammode)
+ return;
+
+ switch (mode) {
+ case 0x00:
+ write_regm(ci, 0x20, 0x80, 0x80);
+ break;
+ case 0x01:
+#ifdef BUFFER_MODE
+ if (!ci->en.read_data)
+ return;
+ dev_info(&ci->i2c->dev, "enable cam buffer mode\n");
+ /* write_reg(ci, 0x0d, 0x00); */
+ /* write_reg(ci, 0x0e, 0x01); */
+ write_regm(ci, 0x08, 0x40, 0x40);
+ /* read_reg(ci, 0x12, &dummy); */
+ write_regm(ci, 0x08, 0x80, 0x80);
+#endif
+ break;
+ default:
+ break;
+ }
+ ci->cammode = mode;
+}
+
+
+
+static int init(struct cxd *ci)
+{
+ int status;
+
+ mutex_lock(&ci->lock);
+ ci->mode = -1;
+ do {
+ status = write_reg(ci, 0x00, 0x00);
+ if (status < 0)
+ break;
+ status = write_reg(ci, 0x01, 0x00);
+ if (status < 0)
+ break;
+ status = write_reg(ci, 0x02, 0x10);
+ if (status < 0)
+ break;
+ status = write_reg(ci, 0x03, 0x00);
+ if (status < 0)
+ break;
+ status = write_reg(ci, 0x05, 0xFF);
+ if (status < 0)
+ break;
+ status = write_reg(ci, 0x06, 0x1F);
+ if (status < 0)
+ break;
+ status = write_reg(ci, 0x07, 0x1F);
+ if (status < 0)
+ break;
+ status = write_reg(ci, 0x08, 0x28);
+ if (status < 0)
+ break;
+ status = write_reg(ci, 0x14, 0x20);
+ if (status < 0)
+ break;
+
+#if 0
+ /* Input Mode C, BYPass Serial, TIVAL = low, MSB */
+ status = write_reg(ci, 0x09, 0x4D);
+ if (status < 0)
+ break;
+#endif
+ /* TOSTRT = 8, Mode B (gated clock), falling Edge,
+ * Serial, POL=HIGH, MSB */
+ status = write_reg(ci, 0x0A, 0xA7);
+ if (status < 0)
+ break;
+
+ status = write_reg(ci, 0x0B, 0x33);
+ if (status < 0)
+ break;
+ status = write_reg(ci, 0x0C, 0x33);
+ if (status < 0)
+ break;
+
+ status = write_regm(ci, 0x14, 0x00, 0x0F);
+ if (status < 0)
+ break;
+ status = write_reg(ci, 0x15, ci->clk_reg_b);
+ if (status < 0)
+ break;
+ status = write_regm(ci, 0x16, 0x00, 0x0F);
+ if (status < 0)
+ break;
+ status = write_reg(ci, 0x17, ci->clk_reg_f);
+ if (status < 0)
+ break;
+
+ if (ci->cfg.clock_mode) {
+ if (ci->cfg.polarity) {
+ status = write_reg(ci, 0x09, 0x6f);
+ if (status < 0)
+ break;
+ } else {
+ status = write_reg(ci, 0x09, 0x6d);
+ if (status < 0)
+ break;
+ }
+ status = write_reg(ci, 0x20, 0x68);
+ if (status < 0)
+ break;
+ status = write_reg(ci, 0x21, 0x00);
+ if (status < 0)
+ break;
+ status = write_reg(ci, 0x22, 0x02);
+ if (status < 0)
+ break;
+ } else {
+ if (ci->cfg.polarity) {
+ status = write_reg(ci, 0x09, 0x4f);
+ if (status < 0)
+ break;
+ } else {
+ status = write_reg(ci, 0x09, 0x4d);
+ if (status < 0)
+ break;
+ }
+
+ status = write_reg(ci, 0x20, 0x28);
+ if (status < 0)
+ break;
+ status = write_reg(ci, 0x21, 0x00);
+ if (status < 0)
+ break;
+ status = write_reg(ci, 0x22, 0x07);
+ if (status < 0)
+ break;
+ }
+
+ status = write_regm(ci, 0x20, 0x80, 0x80);
+ if (status < 0)
+ break;
+ status = write_regm(ci, 0x03, 0x02, 0x02);
+ if (status < 0)
+ break;
+ status = write_reg(ci, 0x01, 0x04);
+ if (status < 0)
+ break;
+ status = write_reg(ci, 0x00, 0x31);
+ if (status < 0)
+ break;
+
+ /* Put TS in bypass */
+ status = write_regm(ci, 0x09, 0x08, 0x08);
+ if (status < 0)
+ break;
+ ci->cammode = -1;
+ cam_mode(ci, 0);
+ } while (0);
+ mutex_unlock(&ci->lock);
+
+ return 0;
+}
+
+static int read_attribute_mem(struct dvb_ca_en50221 *ca,
+ int slot, int address)
+{
+ struct cxd *ci = ca->data;
+#if 0
+ if (ci->amem_read) {
+ if (address <= 0 || address > 1024)
+ return -EIO;
+ return ci->amem[address];
+ }
+
+ mutex_lock(&ci->lock);
+ write_regm(ci, 0x06, 0x00, 0x05);
+ read_pccard(ci, 0, &ci->amem[0], 128);
+ read_pccard(ci, 128, &ci->amem[0], 128);
+ read_pccard(ci, 256, &ci->amem[0], 128);
+ read_pccard(ci, 384, &ci->amem[0], 128);
+ write_regm(ci, 0x06, 0x05, 0x05);
+ mutex_unlock(&ci->lock);
+ return ci->amem[address];
+#else
+ u8 val;
+
+ mutex_lock(&ci->lock);
+ set_mode(ci, 1);
+ read_pccard(ci, address, &val, 1);
+ mutex_unlock(&ci->lock);
+ /* printk(KERN_INFO "%02x:%02x\n", address,val); */
+ return val;
+#endif
+}
+
+static int write_attribute_mem(struct dvb_ca_en50221 *ca, int slot,
+ int address, u8 value)
+{
+ struct cxd *ci = ca->data;
+
+ mutex_lock(&ci->lock);
+ set_mode(ci, 1);
+ write_pccard(ci, address, &value, 1);
+ mutex_unlock(&ci->lock);
+ return 0;
+}
+
+static int read_cam_control(struct dvb_ca_en50221 *ca,
+ int slot, u8 address)
+{
+ struct cxd *ci = ca->data;
+ u8 val;
+
+ mutex_lock(&ci->lock);
+ set_mode(ci, 0);
+ read_io(ci, address, &val);
+ mutex_unlock(&ci->lock);
+ return val;
+}
+
+static int write_cam_control(struct dvb_ca_en50221 *ca, int slot,
+ u8 address, u8 value)
+{
+ struct cxd *ci = ca->data;
+
+ mutex_lock(&ci->lock);
+ set_mode(ci, 0);
+ write_io(ci, address, value);
+ mutex_unlock(&ci->lock);
+ return 0;
+}
+
+static int slot_reset(struct dvb_ca_en50221 *ca, int slot)
+{
+ struct cxd *ci = ca->data;
+
+ mutex_lock(&ci->lock);
+#if 0
+ write_reg(ci, 0x00, 0x21);
+ write_reg(ci, 0x06, 0x1F);
+ write_reg(ci, 0x00, 0x31);
+#else
+#if 0
+ write_reg(ci, 0x06, 0x1F);
+ write_reg(ci, 0x06, 0x2F);
+#else
+ cam_mode(ci, 0);
+ write_reg(ci, 0x00, 0x21);
+ write_reg(ci, 0x06, 0x1F);
+ write_reg(ci, 0x00, 0x31);
+ write_regm(ci, 0x20, 0x80, 0x80);
+ write_reg(ci, 0x03, 0x02);
+ ci->ready = 0;
+#endif
+#endif
+ ci->mode = -1;
+ {
+ int i;
+#if 0
+ u8 val;
+#endif
+ for (i = 0; i < 100; i++) {
+ usleep_range(10000, 11000);
+#if 0
+ read_reg(ci, 0x06, &val);
+ dev_info(&ci->i2c->dev, "%d:%02x\n", i, val);
+ if (!(val&0x10))
+ break;
+#else
+ if (ci->ready)
+ break;
+#endif
+ }
+ }
+ mutex_unlock(&ci->lock);
+ /* msleep(500); */
+ return 0;
+}
+
+static int slot_shutdown(struct dvb_ca_en50221 *ca, int slot)
+{
+ struct cxd *ci = ca->data;
+
+ dev_info(&ci->i2c->dev, "slot_shutdown\n");
+ mutex_lock(&ci->lock);
+ write_regm(ci, 0x09, 0x08, 0x08);
+ write_regm(ci, 0x20, 0x80, 0x80); /* Reset CAM Mode */
+ write_regm(ci, 0x06, 0x07, 0x07); /* Clear IO Mode */
+ ci->mode = -1;
+ mutex_unlock(&ci->lock);
+ return 0;
+}
+
+static int slot_ts_enable(struct dvb_ca_en50221 *ca, int slot)
+{
+ struct cxd *ci = ca->data;
+
+ mutex_lock(&ci->lock);
+ write_regm(ci, 0x09, 0x00, 0x08);
+ set_mode(ci, 0);
+#ifdef BUFFER_MODE
+ cam_mode(ci, 1);
+#endif
+ mutex_unlock(&ci->lock);
+ return 0;
+}
+
+
+static int campoll(struct cxd *ci)
+{
+ u8 istat;
+
+ read_reg(ci, 0x04, &istat);
+ if (!istat)
+ return 0;
+ write_reg(ci, 0x05, istat);
+
+ if (istat&0x40) {
+ ci->dr = 1;
+ dev_info(&ci->i2c->dev, "DR\n");
+ }
+ if (istat&0x20)
+ dev_info(&ci->i2c->dev, "WC\n");
+
+ if (istat&2) {
+ u8 slotstat;
+
+ read_reg(ci, 0x01, &slotstat);
+ if (!(2&slotstat)) {
+ if (!ci->slot_stat) {
+ ci->slot_stat = DVB_CA_EN50221_POLL_CAM_PRESENT;
+ write_regm(ci, 0x03, 0x08, 0x08);
+ }
+
+ } else {
+ if (ci->slot_stat) {
+ ci->slot_stat = 0;
+ write_regm(ci, 0x03, 0x00, 0x08);
+ dev_info(&ci->i2c->dev, "NO CAM\n");
+ ci->ready = 0;
+ }
+ }
+ if (istat&8 &&
+ ci->slot_stat == DVB_CA_EN50221_POLL_CAM_PRESENT) {
+ ci->ready = 1;
+ ci->slot_stat |= DVB_CA_EN50221_POLL_CAM_READY;
+ }
+ }
+ return 0;
+}
+
+
+static int poll_slot_status(struct dvb_ca_en50221 *ca, int slot, int open)
+{
+ struct cxd *ci = ca->data;
+ u8 slotstat;
+
+ mutex_lock(&ci->lock);
+ campoll(ci);
+ read_reg(ci, 0x01, &slotstat);
+ mutex_unlock(&ci->lock);
+
+ return ci->slot_stat;
+}
+
+#ifdef BUFFER_MODE
+static int read_data(struct dvb_ca_en50221 *ca, int slot, u8 *ebuf, int ecount)
+{
+ struct cxd *ci = ca->data;
+ u8 msb, lsb;
+ u16 len;
+
+ mutex_lock(&ci->lock);
+ campoll(ci);
+ mutex_unlock(&ci->lock);
+
+ dev_info(&ci->i2c->dev, "read_data\n");
+ if (!ci->dr)
+ return 0;
+
+ mutex_lock(&ci->lock);
+ read_reg(ci, 0x0f, &msb);
+ read_reg(ci, 0x10, &lsb);
+ len = (msb<<8)|lsb;
+ read_block(ci, 0x12, ebuf, len);
+ ci->dr = 0;
+ mutex_unlock(&ci->lock);
+
+ return len;
+}
+
+static int write_data(struct dvb_ca_en50221 *ca, int slot, u8 *ebuf, int ecount)
+{
+ struct cxd *ci = ca->data;
+
+ mutex_lock(&ci->lock);
+ printk(kern_INFO "write_data %d\n", ecount);
+ write_reg(ci, 0x0d, ecount>>8);
+ write_reg(ci, 0x0e, ecount&0xff);
+ write_block(ci, 0x11, ebuf, ecount);
+ mutex_unlock(&ci->lock);
+ return ecount;
+}
+#endif
+
+static struct dvb_ca_en50221 en_templ = {
+ .read_attribute_mem = read_attribute_mem,
+ .write_attribute_mem = write_attribute_mem,
+ .read_cam_control = read_cam_control,
+ .write_cam_control = write_cam_control,
+ .slot_reset = slot_reset,
+ .slot_shutdown = slot_shutdown,
+ .slot_ts_enable = slot_ts_enable,
+ .poll_slot_status = poll_slot_status,
+#ifdef BUFFER_MODE
+ .read_data = read_data,
+ .write_data = write_data,
+#endif
+
+};
+
+struct dvb_ca_en50221 *cxd2099_attach(struct cxd2099_cfg *cfg,
+ void *priv,
+ struct i2c_adapter *i2c)
+{
+ struct cxd *ci;
+ u8 val;
+
+ if (i2c_read_reg(i2c, cfg->adr, 0, &val) < 0) {
+ dev_info(&i2c->dev, "No CXD2099 detected at %02x\n", cfg->adr);
+ return NULL;
+ }
+
+ ci = kzalloc(sizeof(struct cxd), GFP_KERNEL);
+ if (!ci)
+ return NULL;
+
+ mutex_init(&ci->lock);
+ ci->cfg = *cfg;
+ ci->i2c = i2c;
+ ci->lastaddress = 0xff;
+ ci->clk_reg_b = 0x4a;
+ ci->clk_reg_f = 0x1b;
+
+ ci->en = en_templ;
+ ci->en.data = ci;
+ init(ci);
+ dev_info(&i2c->dev, "Attached CXD2099AR at %02x\n", ci->cfg.adr);
+ return &ci->en;
+}
+EXPORT_SYMBOL(cxd2099_attach);
+
+MODULE_DESCRIPTION("cxd2099");
+MODULE_AUTHOR("Ralph Metzler");
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/media/cxd2099/cxd2099.h b/drivers/staging/media/cxd2099/cxd2099.h
new file mode 100644
index 000000000..0eb607c5b
--- /dev/null
+++ b/drivers/staging/media/cxd2099/cxd2099.h
@@ -0,0 +1,51 @@
+/*
+ * cxd2099.h: Driver for the CXD2099AR Common Interface Controller
+ *
+ * Copyright (C) 2010-2011 Digital Devices GmbH
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 only, as published by the Free Software Foundation.
+ *
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA
+ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
+ */
+
+#ifndef _CXD2099_H_
+#define _CXD2099_H_
+
+#include <dvb_ca_en50221.h>
+
+struct cxd2099_cfg {
+ u32 bitrate;
+ u8 adr;
+ u8 polarity:1;
+ u8 clock_mode:1;
+};
+
+#if defined(CONFIG_DVB_CXD2099) || \
+ (defined(CONFIG_DVB_CXD2099_MODULE) && defined(MODULE))
+struct dvb_ca_en50221 *cxd2099_attach(struct cxd2099_cfg *cfg,
+ void *priv, struct i2c_adapter *i2c);
+#else
+
+static inline struct dvb_ca_en50221 *cxd2099_attach(struct cxd2099_cfg *cfg,
+ void *priv, struct i2c_adapter *i2c)
+{
+ dev_warn(&i2c->dev, "%s: driver disabled by Kconfig\n", __func__);
+ return NULL;
+}
+#endif
+
+#endif
diff --git a/drivers/staging/media/davinci_vpfe/Kconfig b/drivers/staging/media/davinci_vpfe/Kconfig
new file mode 100644
index 000000000..4de2f0824
--- /dev/null
+++ b/drivers/staging/media/davinci_vpfe/Kconfig
@@ -0,0 +1,10 @@
+config VIDEO_DM365_VPFE
+ tristate "DM365 VPFE Media Controller Capture Driver"
+ depends on VIDEO_V4L2 && ARCH_DAVINCI_DM365 && !VIDEO_DM365_ISIF
+ depends on HAS_DMA
+ select VIDEOBUF2_DMA_CONTIG
+ help
+ Support for DM365 VPFE based Media Controller Capture driver.
+
+ To compile this driver as a module, choose M here: the
+ module will be called vpfe-mc-capture.
diff --git a/drivers/staging/media/davinci_vpfe/Makefile b/drivers/staging/media/davinci_vpfe/Makefile
new file mode 100644
index 000000000..c64515c64
--- /dev/null
+++ b/drivers/staging/media/davinci_vpfe/Makefile
@@ -0,0 +1,3 @@
+obj-$(CONFIG_VIDEO_DM365_VPFE) += \
+ dm365_isif.o dm365_ipipe_hw.o dm365_ipipe.o \
+ dm365_resizer.o dm365_ipipeif.o vpfe_mc_capture.o vpfe_video.o
diff --git a/drivers/staging/media/davinci_vpfe/TODO b/drivers/staging/media/davinci_vpfe/TODO
new file mode 100644
index 000000000..7015ab35d
--- /dev/null
+++ b/drivers/staging/media/davinci_vpfe/TODO
@@ -0,0 +1,37 @@
+TODO (general):
+==================================
+
+- User space interface refinement
+ - Controls should be used when possible rather than private ioctl
+ - No enums should be used
+ - Use of MC and V4L2 subdev APIs when applicable
+ - Single interface header might suffice
+ - Current interface forces to configure everything at once
+- Get rid of the dm365_ipipe_hw.[ch] layer
+- Active external sub-devices defined by link configuration; no strcmp
+ needed
+- More generic platform data (i2c adapters)
+- The driver should have no knowledge of possible external subdevs; see
+ struct vpfe_subdev_id
+- Some of the hardware control should be refactorede
+- Check proper serialisation (through mutexes and spinlocks)
+- Names that are visible in kernel global namespace should have a common
+ prefix (or a few)
+- While replacing the older driver in media folder, provide a compatibility
+ layer and compatibility tests that warrants (using the libv4l's LD_PRELOAD
+ approach) there is no regression for the users using the older driver.
+
+Building of uImage and Applications:
+==================================
+
+As of now since the interface will undergo few changes all the include
+files are present in staging itself, to build for dm365 follow below steps,
+
+- copy vpfe.h from drivers/staging/media/davinci_vpfe/ to
+ include/media/davinci/ folder for building the uImage.
+- copy davinci_vpfe_user.h from drivers/staging/media/davinci_vpfe/ to
+ include/uapi/linux/davinci_vpfe.h, and add a entry in Kbuild (required
+ for building application).
+- copy dm365_ipipeif_user.h from drivers/staging/media/davinci_vpfe/ to
+ include/uapi/linux/dm365_ipipeif.h and a entry in Kbuild (required
+ for building application).
diff --git a/drivers/staging/media/davinci_vpfe/davinci-vpfe-mc.txt b/drivers/staging/media/davinci_vpfe/davinci-vpfe-mc.txt
new file mode 100644
index 000000000..a1e91778a
--- /dev/null
+++ b/drivers/staging/media/davinci_vpfe/davinci-vpfe-mc.txt
@@ -0,0 +1,154 @@
+Davinci Video processing Front End (VPFE) driver
+
+Copyright (C) 2012 Texas Instruments Inc
+
+Contacts: Manjunath Hadli <manjunath.hadli@ti.com>
+ Prabhakar Lad <prabhakar.lad@ti.com>
+
+
+Introduction
+============
+
+This file documents the Texas Instruments Davinci Video processing Front End
+(VPFE) driver located under drivers/media/platform/davinci. The original driver
+exists for Davinci VPFE, which is now being changed to Media Controller
+Framework.
+
+Currently the driver has been successfully used on the following
+version of Davinci:
+
+ DM365/DM368
+
+The driver implements V4L2, Media controller and v4l2_subdev interfaces. Sensor,
+lens and flash drivers using the v4l2_subdev interface in the kernel are
+supported.
+
+
+Split to subdevs
+================
+
+The Davinci VPFE is split into V4L2 subdevs, each of the blocks inside the VPFE
+having one subdev to represent it. Each of the subdevs provide a V4L2 subdev
+interface to userspace.
+
+ DAVINCI ISIF
+ DAVINCI IPIPEIF
+ DAVINCI IPIPE
+ DAVINCI CROP RESIZER
+ DAVINCI RESIZER A
+ DAVINCI RESIZER B
+
+Each possible link in the VPFE is modelled by a link in the Media controller
+interface. For an example program see [1].
+
+
+ISIF, IPIPE, and RESIZER block IOCTLs
+======================================
+
+The Davinci Video processing Front End (VPFE) driver supports standard V4L2
+IOCTLs and controls where possible and practical. Much of the functions provided
+by the VPFE, however, does not fall under the standard IOCTL's.
+
+In general, there is a private ioctl for configuring each of the blocks
+containing hardware-dependent functions.
+
+The following private IOCTLs are supported:
+
+ VIDIOC_VPFE_ISIF_[S/G]_RAW_PARAMS
+ VIDIOC_VPFE_IPIPE_[S/G]_CONFIG
+ VIDIOC_VPFE_RSZ_[S/G]_CONFIG
+
+The parameter structures used by these ioctl's are described in
+include/uapi/linux/davinci_vpfe.h.
+
+The VIDIOC_VPFE_ISIF_S_RAW_PARAMS, VIDIOC_VPFE_IPIPE_S_CONFIG and
+VIDIOC_VPFE_RSZ_S_CONFIG are used to configure, enable and disable functions in
+the isif, ipipe and resizer blocks respectively. These IOCTL's control several
+functions in the blocks they control. VIDIOC_VPFE_ISIF_S_RAW_PARAMS IOCTL
+accepts a pointer to struct vpfe_isif_raw_config as its argument. Similarly
+VIDIOC_VPFE_IPIPE_S_CONFIG accepts a pointer to struct vpfe_ipipe_config. And
+VIDIOC_VPFE_RSZ_S_CONFIG accepts a pointer to struct vpfe_rsz_config as its
+argument. Similarly VIDIOC_VPFE_ISIF_G_RAW_PARAMS, VIDIOC_VPFE_IPIPE_G_CONFIG
+and VIDIOC_VPFE_RSZ_G_CONFIG are used to get the current configuration set in
+the isif, ipipe and resizer blocks respectively.
+
+The detailed functions of the VPFE itself related to a given VPFE block is
+described in the Technical Reference Manuals (TRMs) --- see the end of the
+document for those.
+
+
+IPIPEIF block IOCTLs
+======================================
+
+The following private IOCTLs are supported:
+
+ VIDIOC_VPFE_IPIPEIF_[S/G]_CONFIG
+
+The parameter structures used by these ioctl's are described in
+include/uapi/linux/dm365_ipipeif.h
+
+The VIDIOC_VPFE_IPIPEIF_S_CONFIG is used to configure the ipipeif
+hardware block. The VIDIOC_VPFE_IPIPEIF_S_CONFIG and
+VIDIOC_VPFE_IPIPEIF_G_CONFIG accepts a pointer to struct ipipeif_params
+as its argument.
+
+
+VPFE Operating Modes
+==========================================
+
+a: Continuous Modes
+------------------------
+
+1: tvp514x/tvp7002/mt9p031---> DAVINCI ISIF---> SDRAM
+
+2: tvp514x/tvp7002/mt9p031---> DAVINCI ISIF---> DAVINCI IPIPEIF--->|
+ |
+ <--------------------<----------------<---------------------<---|
+ |
+ V
+ DAVINCI CROP RESIZER--->DAVINCI RESIZER [A/B]---> SDRAM
+
+3: tvp514x/tvp7002/mt9p031---> DAVINCI ISIF---> DAVINCI IPIPEIF--->|
+ |
+ <--------------------<----------------<---------------------<---|
+ |
+ V
+ DAVINCI IPIPE---> DAVINCI CROP RESIZER--->DAVINCI RESIZER [A/B]---> SDRAM
+
+a: Single Shot Modes
+------------------------
+
+1: SDRAM---> DAVINCI IPIPEIF---> DAVINCI IPIPE---> DAVINCI CROP RESIZER--->|
+ |
+ <----------------<----------------<------------------<---------------<--|
+ |
+ V
+DAVINCI RESIZER [A/B]---> SDRAM
+
+2: SDRAM---> DAVINCI IPIPEIF---> DAVINCI CROP RESIZER--->|
+ |
+ <----------------<----------------<---------------<---|
+ |
+ V
+DAVINCI RESIZER [A/B]---> SDRAM
+
+
+Technical reference manuals (TRMs) and other documentation
+==========================================================
+
+Davinci DM365 TRM:
+<URL:http://www.ti.com/lit/ds/sprs457e/sprs457e.pdf>
+Referenced MARCH 2009-REVISED JUNE 2011
+
+Davinci DM368 TRM:
+<URL:http://www.ti.com/lit/ds/sprs668c/sprs668c.pdf>
+Referenced APRIL 2010-REVISED JUNE 2011
+
+Davinci Video Processing Front End (VPFE) DM36x
+<URL:http://www.ti.com/lit/ug/sprufg8c/sprufg8c.pdf>
+
+
+References
+==========
+
+[1] http://git.ideasonboard.org/?p=media-ctl.git;a=summary
diff --git a/drivers/staging/media/davinci_vpfe/davinci_vpfe_user.h b/drivers/staging/media/davinci_vpfe/davinci_vpfe_user.h
new file mode 100644
index 000000000..7b7e7b26c
--- /dev/null
+++ b/drivers/staging/media/davinci_vpfe/davinci_vpfe_user.h
@@ -0,0 +1,1290 @@
+/*
+ * Copyright (C) 2012 Texas Instruments 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 version 2.
+ *
+ * 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
+ *
+ * Contributors:
+ * Manjunath Hadli <manjunath.hadli@ti.com>
+ * Prabhakar Lad <prabhakar.lad@ti.com>
+ */
+
+#ifndef _DAVINCI_VPFE_USER_H
+#define _DAVINCI_VPFE_USER_H
+
+#include <linux/types.h>
+#include <linux/videodev2.h>
+
+/*
+ * Private IOCTL
+ *
+ * VIDIOC_VPFE_ISIF_S_RAW_PARAMS: Set raw params in isif
+ * VIDIOC_VPFE_ISIF_G_RAW_PARAMS: Get raw params from isif
+ * VIDIOC_VPFE_PRV_S_CONFIG: Set ipipe engine configuration
+ * VIDIOC_VPFE_PRV_G_CONFIG: Get ipipe engine configuration
+ * VIDIOC_VPFE_RSZ_S_CONFIG: Set resizer engine configuration
+ * VIDIOC_VPFE_RSZ_G_CONFIG: Get resizer engine configuration
+ */
+
+#define VIDIOC_VPFE_ISIF_S_RAW_PARAMS \
+ _IOW('V', BASE_VIDIOC_PRIVATE + 1, struct vpfe_isif_raw_config)
+#define VIDIOC_VPFE_ISIF_G_RAW_PARAMS \
+ _IOR('V', BASE_VIDIOC_PRIVATE + 2, struct vpfe_isif_raw_config)
+#define VIDIOC_VPFE_IPIPE_S_CONFIG \
+ _IOWR('P', BASE_VIDIOC_PRIVATE + 3, struct vpfe_ipipe_config)
+#define VIDIOC_VPFE_IPIPE_G_CONFIG \
+ _IOWR('P', BASE_VIDIOC_PRIVATE + 4, struct vpfe_ipipe_config)
+#define VIDIOC_VPFE_RSZ_S_CONFIG \
+ _IOWR('R', BASE_VIDIOC_PRIVATE + 5, struct vpfe_rsz_config)
+#define VIDIOC_VPFE_RSZ_G_CONFIG \
+ _IOWR('R', BASE_VIDIOC_PRIVATE + 6, struct vpfe_rsz_config)
+
+/*
+ * Private Control's for ISIF
+ */
+#define VPFE_ISIF_CID_CRGAIN (V4L2_CID_USER_BASE | 0xa001)
+#define VPFE_ISIF_CID_CGRGAIN (V4L2_CID_USER_BASE | 0xa002)
+#define VPFE_ISIF_CID_CGBGAIN (V4L2_CID_USER_BASE | 0xa003)
+#define VPFE_ISIF_CID_CBGAIN (V4L2_CID_USER_BASE | 0xa004)
+#define VPFE_ISIF_CID_GAIN_OFFSET (V4L2_CID_USER_BASE | 0xa005)
+
+/*
+ * Private Control's for ISIF and IPIPEIF
+ */
+#define VPFE_CID_DPCM_PREDICTOR (V4L2_CID_USER_BASE | 0xa006)
+
+/************************************************************************
+ * Vertical Defect Correction parameters
+ ***********************************************************************/
+
+/**
+ * vertical defect correction methods
+ */
+enum vpfe_isif_vdfc_corr_mode {
+ /* Defect level subtraction. Just fed through if saturating */
+ VPFE_ISIF_VDFC_NORMAL,
+ /**
+ * Defect level subtraction. Horizontal interpolation ((i-2)+(i+2))/2
+ * if data saturating
+ */
+ VPFE_ISIF_VDFC_HORZ_INTERPOL_IF_SAT,
+ /* Horizontal interpolation (((i-2)+(i+2))/2) */
+ VPFE_ISIF_VDFC_HORZ_INTERPOL
+};
+
+/**
+ * Max Size of the Vertical Defect Correction table
+ */
+#define VPFE_ISIF_VDFC_TABLE_SIZE 8
+
+/**
+ * Values used for shifting up the vdfc defect level
+ */
+enum vpfe_isif_vdfc_shift {
+ /* No Shift */
+ VPFE_ISIF_VDFC_NO_SHIFT,
+ /* Shift by 1 bit */
+ VPFE_ISIF_VDFC_SHIFT_1,
+ /* Shift by 2 bit */
+ VPFE_ISIF_VDFC_SHIFT_2,
+ /* Shift by 3 bit */
+ VPFE_ISIF_VDFC_SHIFT_3,
+ /* Shift by 4 bit */
+ VPFE_ISIF_VDFC_SHIFT_4
+};
+
+/**
+ * Defect Correction (DFC) table entry
+ */
+struct vpfe_isif_vdfc_entry {
+ /* vertical position of defect */
+ unsigned short pos_vert;
+ /* horizontal position of defect */
+ unsigned short pos_horz;
+ /**
+ * Defect level of Vertical line defect position. This is subtracted
+ * from the data at the defect position
+ */
+ unsigned char level_at_pos;
+ /**
+ * Defect level of the pixels upper than the vertical line defect.
+ * This is subtracted from the data
+ */
+ unsigned char level_up_pixels;
+ /**
+ * Defect level of the pixels lower than the vertical line defect.
+ * This is subtracted from the data
+ */
+ unsigned char level_low_pixels;
+};
+
+/**
+ * Structure for Defect Correction (DFC) parameter
+ */
+struct vpfe_isif_dfc {
+ /* enable vertical defect correction */
+ unsigned char en;
+ /* Correction methods */
+ enum vpfe_isif_vdfc_corr_mode corr_mode;
+ /**
+ * 0 - whole line corrected, 1 - not
+ * pixels upper than the defect
+ */
+ unsigned char corr_whole_line;
+ /**
+ * defect level shift value. level_at_pos, level_upper_pos,
+ * and level_lower_pos can be shifted up by this value
+ */
+ enum vpfe_isif_vdfc_shift def_level_shift;
+ /* defect saturation level */
+ unsigned short def_sat_level;
+ /* number of vertical defects. Max is VPFE_ISIF_VDFC_TABLE_SIZE */
+ short num_vdefects;
+ /* VDFC table ptr */
+ struct vpfe_isif_vdfc_entry table[VPFE_ISIF_VDFC_TABLE_SIZE];
+};
+
+/************************************************************************
+* Digital/Black clamp or DC Subtract parameters
+************************************************************************/
+/**
+ * Horizontal Black Clamp modes
+ */
+enum vpfe_isif_horz_bc_mode {
+ /**
+ * Horizontal clamp disabled. Only vertical clamp
+ * value is subtracted
+ */
+ VPFE_ISIF_HORZ_BC_DISABLE,
+ /**
+ * Horizontal clamp value is calculated and subtracted
+ * from image data along with vertical clamp value
+ */
+ VPFE_ISIF_HORZ_BC_CLAMP_CALC_ENABLED,
+ /**
+ * Horizontal clamp value calculated from previous image
+ * is subtracted from image data along with vertical clamp
+ * value. How the horizontal clamp value for the first image
+ * is calculated in this case ???
+ */
+ VPFE_ISIF_HORZ_BC_CLAMP_NOT_UPDATED
+};
+
+/**
+ * Base window selection for Horizontal Black Clamp calculations
+ */
+enum vpfe_isif_horz_bc_base_win_sel {
+ /* Select Most left window for bc calculation */
+ VPFE_ISIF_SEL_MOST_LEFT_WIN,
+
+ /* Select Most right window for bc calculation */
+ VPFE_ISIF_SEL_MOST_RIGHT_WIN,
+};
+
+/* Size of window in horizontal direction for horizontal bc */
+enum vpfe_isif_horz_bc_sz_h {
+ VPFE_ISIF_HORZ_BC_SZ_H_2PIXELS,
+ VPFE_ISIF_HORZ_BC_SZ_H_4PIXELS,
+ VPFE_ISIF_HORZ_BC_SZ_H_8PIXELS,
+ VPFE_ISIF_HORZ_BC_SZ_H_16PIXELS
+};
+
+/* Size of window in vertcal direction for vertical bc */
+enum vpfe_isif_horz_bc_sz_v {
+ VPFE_ISIF_HORZ_BC_SZ_H_32PIXELS,
+ VPFE_ISIF_HORZ_BC_SZ_H_64PIXELS,
+ VPFE_ISIF_HORZ_BC_SZ_H_128PIXELS,
+ VPFE_ISIF_HORZ_BC_SZ_H_256PIXELS
+};
+
+/**
+ * Structure for Horizontal Black Clamp config params
+ */
+struct vpfe_isif_horz_bclamp {
+ /* horizontal clamp mode */
+ enum vpfe_isif_horz_bc_mode mode;
+ /**
+ * pixel value limit enable.
+ * 0 - limit disabled
+ * 1 - pixel value limited to 1023
+ */
+ unsigned char clamp_pix_limit;
+ /**
+ * Select most left or right window for clamp val
+ * calculation
+ */
+ enum vpfe_isif_horz_bc_base_win_sel base_win_sel_calc;
+ /* Window count per color for calculation. range 1-32 */
+ unsigned char win_count_calc;
+ /* Window start position - horizontal for calculation. 0 - 8191 */
+ unsigned short win_start_h_calc;
+ /* Window start position - vertical for calculation 0 - 8191 */
+ unsigned short win_start_v_calc;
+ /* Width of the sample window in pixels for calculation */
+ enum vpfe_isif_horz_bc_sz_h win_h_sz_calc;
+ /* Height of the sample window in pixels for calculation */
+ enum vpfe_isif_horz_bc_sz_v win_v_sz_calc;
+};
+
+/**
+ * Black Clamp vertical reset values
+ */
+enum vpfe_isif_vert_bc_reset_val_sel {
+ /* Reset value used is the clamp value calculated */
+ VPFE_ISIF_VERT_BC_USE_HORZ_CLAMP_VAL,
+ /* Reset value used is reset_clamp_val configured */
+ VPFE_ISIF_VERT_BC_USE_CONFIG_CLAMP_VAL,
+ /* No update, previous image value is used */
+ VPFE_ISIF_VERT_BC_NO_UPDATE
+};
+
+enum vpfe_isif_vert_bc_sz_h {
+ VPFE_ISIF_VERT_BC_SZ_H_2PIXELS,
+ VPFE_ISIF_VERT_BC_SZ_H_4PIXELS,
+ VPFE_ISIF_VERT_BC_SZ_H_8PIXELS,
+ VPFE_ISIF_VERT_BC_SZ_H_16PIXELS,
+ VPFE_ISIF_VERT_BC_SZ_H_32PIXELS,
+ VPFE_ISIF_VERT_BC_SZ_H_64PIXELS
+};
+
+/**
+ * Structure for Vertical Black Clamp configuration params
+ */
+struct vpfe_isif_vert_bclamp {
+ /* Reset value selection for vertical clamp calculation */
+ enum vpfe_isif_vert_bc_reset_val_sel reset_val_sel;
+ /* U12 value if reset_sel = ISIF_BC_VERT_USE_CONFIG_CLAMP_VAL */
+ unsigned short reset_clamp_val;
+ /**
+ * U8Q8. Line average coefficient used in vertical clamp
+ * calculation
+ */
+ unsigned char line_ave_coef;
+ /* Width in pixels of the optical black region used for calculation. */
+ enum vpfe_isif_vert_bc_sz_h ob_h_sz_calc;
+ /* Height of the optical black region for calculation */
+ unsigned short ob_v_sz_calc;
+ /* Optical black region start position - horizontal. 0 - 8191 */
+ unsigned short ob_start_h;
+ /* Optical black region start position - vertical 0 - 8191 */
+ unsigned short ob_start_v;
+};
+
+/**
+ * Structure for Black Clamp configuration params
+ */
+struct vpfe_isif_black_clamp {
+ /**
+ * this offset value is added irrespective of the clamp
+ * enable status. S13
+ */
+ unsigned short dc_offset;
+ /**
+ * Enable black/digital clamp value to be subtracted
+ * from the image data
+ */
+ unsigned char en;
+ /**
+ * black clamp mode. same/separate clamp for 4 colors
+ * 0 - disable - same clamp value for all colors
+ * 1 - clamp value calculated separately for all colors
+ */
+ unsigned char bc_mode_color;
+ /* Vertical start position for bc subtraction */
+ unsigned short vert_start_sub;
+ /* Black clamp for horizontal direction */
+ struct vpfe_isif_horz_bclamp horz;
+ /* Black clamp for vertical direction */
+ struct vpfe_isif_vert_bclamp vert;
+};
+
+/*************************************************************************
+** Color Space Conversion (CSC)
+*************************************************************************/
+/**
+ * Number of Coefficient values used for CSC
+ */
+#define VPFE_ISIF_CSC_NUM_COEFF 16
+
+struct float_8_bit {
+ /* 8 bit integer part */
+ __u8 integer;
+ /* 8 bit decimal part */
+ __u8 decimal;
+};
+
+struct float_16_bit {
+ /* 16 bit integer part */
+ __u16 integer;
+ /* 16 bit decimal part */
+ __u16 decimal;
+};
+
+/*************************************************************************
+** Color Space Conversion parameters
+*************************************************************************/
+/**
+ * Structure used for CSC config params
+ */
+struct vpfe_isif_color_space_conv {
+ /* Enable color space conversion */
+ unsigned char en;
+ /**
+ * csc coefficient table. S8Q5, M00 at index 0, M01 at index 1, and
+ * so forth
+ */
+ struct float_8_bit coeff[VPFE_ISIF_CSC_NUM_COEFF];
+};
+
+enum vpfe_isif_datasft {
+ /* No Shift */
+ VPFE_ISIF_NO_SHIFT,
+ /* 1 bit Shift */
+ VPFE_ISIF_1BIT_SHIFT,
+ /* 2 bit Shift */
+ VPFE_ISIF_2BIT_SHIFT,
+ /* 3 bit Shift */
+ VPFE_ISIF_3BIT_SHIFT,
+ /* 4 bit Shift */
+ VPFE_ISIF_4BIT_SHIFT,
+ /* 5 bit Shift */
+ VPFE_ISIF_5BIT_SHIFT,
+ /* 6 bit Shift */
+ VPFE_ISIF_6BIT_SHIFT
+};
+
+#define VPFE_ISIF_LINEAR_TAB_SIZE 192
+/*************************************************************************
+** Linearization parameters
+*************************************************************************/
+/**
+ * Structure for Sensor data linearization
+ */
+struct vpfe_isif_linearize {
+ /* Enable or Disable linearization of data */
+ unsigned char en;
+ /* Shift value applied */
+ enum vpfe_isif_datasft corr_shft;
+ /* scale factor applied U11Q10 */
+ struct float_16_bit scale_fact;
+ /* Size of the linear table */
+ unsigned short table[VPFE_ISIF_LINEAR_TAB_SIZE];
+};
+
+/*************************************************************************
+** ISIF Raw configuration parameters
+*************************************************************************/
+enum vpfe_isif_fmt_mode {
+ VPFE_ISIF_SPLIT,
+ VPFE_ISIF_COMBINE
+};
+
+enum vpfe_isif_lnum {
+ VPFE_ISIF_1LINE,
+ VPFE_ISIF_2LINES,
+ VPFE_ISIF_3LINES,
+ VPFE_ISIF_4LINES
+};
+
+enum vpfe_isif_line {
+ VPFE_ISIF_1STLINE,
+ VPFE_ISIF_2NDLINE,
+ VPFE_ISIF_3RDLINE,
+ VPFE_ISIF_4THLINE
+};
+
+struct vpfe_isif_fmtplen {
+ /**
+ * number of program entries for SET0, range 1 - 16
+ * when fmtmode is ISIF_SPLIT, 1 - 8 when fmtmode is
+ * ISIF_COMBINE
+ */
+ unsigned short plen0;
+ /**
+ * number of program entries for SET1, range 1 - 16
+ * when fmtmode is ISIF_SPLIT, 1 - 8 when fmtmode is
+ * ISIF_COMBINE
+ */
+ unsigned short plen1;
+ /**
+ * number of program entries for SET2, range 1 - 16
+ * when fmtmode is ISIF_SPLIT, 1 - 8 when fmtmode is
+ * ISIF_COMBINE
+ */
+ unsigned short plen2;
+ /**
+ * number of program entries for SET3, range 1 - 16
+ * when fmtmode is ISIF_SPLIT, 1 - 8 when fmtmode is
+ * ISIF_COMBINE
+ */
+ unsigned short plen3;
+};
+
+struct vpfe_isif_fmt_cfg {
+ /* Split or combine or line alternate */
+ enum vpfe_isif_fmt_mode fmtmode;
+ /* enable or disable line alternating mode */
+ unsigned char ln_alter_en;
+ /* Split/combine line number */
+ enum vpfe_isif_lnum lnum;
+ /* Address increment Range 1 - 16 */
+ unsigned int addrinc;
+};
+
+struct vpfe_isif_fmt_addr_ptr {
+ /* Initial address */
+ unsigned int init_addr;
+ /* output line number */
+ enum vpfe_isif_line out_line;
+};
+
+struct vpfe_isif_fmtpgm_ap {
+ /* program address pointer */
+ unsigned char pgm_aptr;
+ /* program address increment or decrement */
+ unsigned char pgmupdt;
+};
+
+struct vpfe_isif_data_formatter {
+ /* Enable/Disable data formatter */
+ unsigned char en;
+ /* data formatter configuration */
+ struct vpfe_isif_fmt_cfg cfg;
+ /* Formatter program entries length */
+ struct vpfe_isif_fmtplen plen;
+ /* first pixel in a line fed to formatter */
+ unsigned short fmtrlen;
+ /* HD interval for output line. Only valid when split line */
+ unsigned short fmthcnt;
+ /* formatter address pointers */
+ struct vpfe_isif_fmt_addr_ptr fmtaddr_ptr[16];
+ /* program enable/disable */
+ unsigned char pgm_en[32];
+ /* program address pointers */
+ struct vpfe_isif_fmtpgm_ap fmtpgm_ap[32];
+};
+
+struct vpfe_isif_df_csc {
+ /* Color Space Conversion configuration, 0 - csc, 1 - df */
+ unsigned int df_or_csc;
+ /* csc configuration valid if df_or_csc is 0 */
+ struct vpfe_isif_color_space_conv csc;
+ /* data formatter configuration valid if df_or_csc is 1 */
+ struct vpfe_isif_data_formatter df;
+ /* start pixel in a line at the input */
+ unsigned int start_pix;
+ /* number of pixels in input line */
+ unsigned int num_pixels;
+ /* start line at the input */
+ unsigned int start_line;
+ /* number of lines at the input */
+ unsigned int num_lines;
+};
+
+struct vpfe_isif_gain_offsets_adj {
+ /* Enable or Disable Gain adjustment for SDRAM data */
+ unsigned char gain_sdram_en;
+ /* Enable or Disable Gain adjustment for IPIPE data */
+ unsigned char gain_ipipe_en;
+ /* Enable or Disable Gain adjustment for H3A data */
+ unsigned char gain_h3a_en;
+ /* Enable or Disable Gain adjustment for SDRAM data */
+ unsigned char offset_sdram_en;
+ /* Enable or Disable Gain adjustment for IPIPE data */
+ unsigned char offset_ipipe_en;
+ /* Enable or Disable Gain adjustment for H3A data */
+ unsigned char offset_h3a_en;
+};
+
+struct vpfe_isif_cul {
+ /* Horizontal Cull pattern for odd lines */
+ unsigned char hcpat_odd;
+ /* Horizontal Cull pattern for even lines */
+ unsigned char hcpat_even;
+ /* Vertical Cull pattern */
+ unsigned char vcpat;
+ /* Enable or disable lpf. Apply when cull is enabled */
+ unsigned char en_lpf;
+};
+
+/* all the stuff in this struct will be provided by userland */
+struct vpfe_isif_raw_config {
+ /* Linearization parameters for image sensor data input */
+ struct vpfe_isif_linearize linearize;
+ /* Data formatter or CSC */
+ struct vpfe_isif_df_csc df_csc;
+ /* Defect Pixel Correction (DFC) confguration */
+ struct vpfe_isif_dfc dfc;
+ /* Black/Digital Clamp configuration */
+ struct vpfe_isif_black_clamp bclamp;
+ /* Gain, offset adjustments */
+ struct vpfe_isif_gain_offsets_adj gain_offset;
+ /* Culling */
+ struct vpfe_isif_cul culling;
+ /* horizontal offset for Gain/LSC/DFC */
+ unsigned short horz_offset;
+ /* vertical offset for Gain/LSC/DFC */
+ unsigned short vert_offset;
+};
+
+/**********************************************************************
+ IPIPE API Structures
+**********************************************************************/
+
+/* IPIPE module configurations */
+
+/* IPIPE input configuration */
+#define VPFE_IPIPE_INPUT_CONFIG (1 << 0)
+/* LUT based Defect Pixel Correction */
+#define VPFE_IPIPE_LUTDPC (1 << 1)
+/* On the fly (OTF) Defect Pixel Correction */
+#define VPFE_IPIPE_OTFDPC (1 << 2)
+/* Noise Filter - 1 */
+#define VPFE_IPIPE_NF1 (1 << 3)
+/* Noise Filter - 2 */
+#define VPFE_IPIPE_NF2 (1 << 4)
+/* White Balance. Also a control ID */
+#define VPFE_IPIPE_WB (1 << 5)
+/* 1st RGB to RBG Blend module */
+#define VPFE_IPIPE_RGB2RGB_1 (1 << 6)
+/* 2nd RGB to RBG Blend module */
+#define VPFE_IPIPE_RGB2RGB_2 (1 << 7)
+/* Gamma Correction */
+#define VPFE_IPIPE_GAMMA (1 << 8)
+/* 3D LUT color conversion */
+#define VPFE_IPIPE_3D_LUT (1 << 9)
+/* RGB to YCbCr module */
+#define VPFE_IPIPE_RGB2YUV (1 << 10)
+/* YUV 422 conversion module */
+#define VPFE_IPIPE_YUV422_CONV (1 << 11)
+/* Edge Enhancement */
+#define VPFE_IPIPE_YEE (1 << 12)
+/* Green Imbalance Correction */
+#define VPFE_IPIPE_GIC (1 << 13)
+/* CFA Interpolation */
+#define VPFE_IPIPE_CFA (1 << 14)
+/* Chroma Artifact Reduction */
+#define VPFE_IPIPE_CAR (1 << 15)
+/* Chroma Gain Suppression */
+#define VPFE_IPIPE_CGS (1 << 16)
+/* Global brightness and contrast control */
+#define VPFE_IPIPE_GBCE (1 << 17)
+
+#define VPFE_IPIPE_MAX_MODULES 18
+
+struct ipipe_float_u16 {
+ unsigned short integer;
+ unsigned short decimal;
+};
+
+struct ipipe_float_s16 {
+ short integer;
+ unsigned short decimal;
+};
+
+struct ipipe_float_u8 {
+ unsigned char integer;
+ unsigned char decimal;
+};
+
+/* Copy method selection for vertical correction
+ * Used when ipipe_dfc_corr_meth is IPIPE_DPC_CTORB_AFTER_HINT
+ */
+enum vpfe_ipipe_dpc_corr_meth {
+ /* replace by black or white dot specified by repl_white */
+ VPFE_IPIPE_DPC_REPL_BY_DOT = 0,
+ /* Copy from left */
+ VPFE_IPIPE_DPC_CL = 1,
+ /* Copy from right */
+ VPFE_IPIPE_DPC_CR = 2,
+ /* Horizontal interpolation */
+ VPFE_IPIPE_DPC_H_INTP = 3,
+ /* Vertical interpolation */
+ VPFE_IPIPE_DPC_V_INTP = 4,
+ /* Copy from top */
+ VPFE_IPIPE_DPC_CT = 5,
+ /* Copy from bottom */
+ VPFE_IPIPE_DPC_CB = 6,
+ /* 2D interpolation */
+ VPFE_IPIPE_DPC_2D_INTP = 7,
+};
+
+struct vpfe_ipipe_lutdpc_entry {
+ /* Horizontal position */
+ unsigned short horz_pos;
+ /* vertical position */
+ unsigned short vert_pos;
+ enum vpfe_ipipe_dpc_corr_meth method;
+};
+
+#define VPFE_IPIPE_MAX_SIZE_DPC 256
+
+/* Structure for configuring DPC module */
+struct vpfe_ipipe_lutdpc {
+ /* 0 - disable, 1 - enable */
+ unsigned char en;
+ /* 0 - replace with black dot, 1 - white dot when correction
+ * method is IPIPE_DFC_REPL_BY_DOT=0,
+ */
+ unsigned char repl_white;
+ /* number of entries in the correction table. Currently only
+ * support up-to 256 entries. infinite mode is not supported
+ */
+ unsigned short dpc_size;
+ struct vpfe_ipipe_lutdpc_entry table[VPFE_IPIPE_MAX_SIZE_DPC];
+};
+
+enum vpfe_ipipe_otfdpc_det_meth {
+ VPFE_IPIPE_DPC_OTF_MIN_MAX,
+ VPFE_IPIPE_DPC_OTF_MIN_MAX2
+};
+
+struct vpfe_ipipe_otfdpc_thr {
+ unsigned short r;
+ unsigned short gr;
+ unsigned short gb;
+ unsigned short b;
+};
+
+enum vpfe_ipipe_otfdpc_alg {
+ VPFE_IPIPE_OTFDPC_2_0,
+ VPFE_IPIPE_OTFDPC_3_0
+};
+
+struct vpfe_ipipe_otfdpc_2_0_cfg {
+ /* defect detection threshold for MIN_MAX2 method (DPC 2.0 alg) */
+ struct vpfe_ipipe_otfdpc_thr det_thr;
+ /* defect correction threshold for MIN_MAX2 method (DPC 2.0 alg) or
+ * maximum value for MIN_MAX method
+ */
+ struct vpfe_ipipe_otfdpc_thr corr_thr;
+};
+
+struct vpfe_ipipe_otfdpc_3_0_cfg {
+ /* DPC3.0 activity adj shf. activity = (max2-min2) >> (6 -shf)
+ */
+ unsigned char act_adj_shf;
+ /* DPC3.0 detection threshold, THR */
+ unsigned short det_thr;
+ /* DPC3.0 detection threshold slope, SLP */
+ unsigned short det_slp;
+ /* DPC3.0 detection threshold min, MIN */
+ unsigned short det_thr_min;
+ /* DPC3.0 detection threshold max, MAX */
+ unsigned short det_thr_max;
+ /* DPC3.0 correction threshold, THR */
+ unsigned short corr_thr;
+ /* DPC3.0 correction threshold slope, SLP */
+ unsigned short corr_slp;
+ /* DPC3.0 correction threshold min, MIN */
+ unsigned short corr_thr_min;
+ /* DPC3.0 correction threshold max, MAX */
+ unsigned short corr_thr_max;
+};
+
+struct vpfe_ipipe_otfdpc {
+ /* 0 - disable, 1 - enable */
+ unsigned char en;
+ /* defect detection method */
+ enum vpfe_ipipe_otfdpc_det_meth det_method;
+ /* Algorithm used. Applicable only when IPIPE_DPC_OTF_MIN_MAX2 is
+ * used
+ */
+ enum vpfe_ipipe_otfdpc_alg alg;
+ union {
+ /* if alg is IPIPE_OTFDPC_2_0 */
+ struct vpfe_ipipe_otfdpc_2_0_cfg dpc_2_0;
+ /* if alg is IPIPE_OTFDPC_3_0 */
+ struct vpfe_ipipe_otfdpc_3_0_cfg dpc_3_0;
+ } alg_cfg;
+};
+
+/* Threshold values table size */
+#define VPFE_IPIPE_NF_THR_TABLE_SIZE 8
+/* Intensity values table size */
+#define VPFE_IPIPE_NF_STR_TABLE_SIZE 8
+
+/* NF, sampling method for green pixels */
+enum vpfe_ipipe_nf_sampl_meth {
+ /* Same as R or B */
+ VPFE_IPIPE_NF_BOX,
+ /* Diamond mode */
+ VPFE_IPIPE_NF_DIAMOND
+};
+
+/* Structure for configuring NF module */
+struct vpfe_ipipe_nf {
+ /* 0 - disable, 1 - enable */
+ unsigned char en;
+ /* Sampling method for green pixels */
+ enum vpfe_ipipe_nf_sampl_meth gr_sample_meth;
+ /* Down shift value in LUT reference address
+ */
+ unsigned char shft_val;
+ /* Spread value in NF algorithm
+ */
+ unsigned char spread_val;
+ /* Apply LSC gain to threshold. Enable this only if
+ * LSC is enabled in ISIF
+ */
+ unsigned char apply_lsc_gain;
+ /* Threshold values table */
+ unsigned short thr[VPFE_IPIPE_NF_THR_TABLE_SIZE];
+ /* intensity values table */
+ unsigned char str[VPFE_IPIPE_NF_STR_TABLE_SIZE];
+ /* Edge detection minimum threshold */
+ unsigned short edge_det_min_thr;
+ /* Edge detection maximum threshold */
+ unsigned short edge_det_max_thr;
+};
+
+enum vpfe_ipipe_gic_alg {
+ VPFE_IPIPE_GIC_ALG_CONST_GAIN,
+ VPFE_IPIPE_GIC_ALG_ADAPT_GAIN
+};
+
+enum vpfe_ipipe_gic_thr_sel {
+ VPFE_IPIPE_GIC_THR_REG,
+ VPFE_IPIPE_GIC_THR_NF
+};
+
+enum vpfe_ipipe_gic_wt_fn_type {
+ /* Use difference as index */
+ VPFE_IPIPE_GIC_WT_FN_TYP_DIF,
+ /* Use weight function as index */
+ VPFE_IPIPE_GIC_WT_FN_TYP_HP_VAL
+};
+
+/* structure for Green Imbalance Correction */
+struct vpfe_ipipe_gic {
+ /* 0 - disable, 1 - enable */
+ unsigned char en;
+ /* 0 - Constant gain , 1 - Adaptive gain algorithm */
+ enum vpfe_ipipe_gic_alg gic_alg;
+ /* GIC gain or weight. Used for Constant gain and Adaptive algorithms
+ */
+ unsigned short gain;
+ /* Threshold selection. GIC register values or NF2 thr table */
+ enum vpfe_ipipe_gic_thr_sel thr_sel;
+ /* thr1. Used when thr_sel is IPIPE_GIC_THR_REG */
+ unsigned short thr;
+ /* this value is used for thr2-thr1, thr3-thr2 or
+ * thr4-thr3 when wt_fn_type is index. Otherwise it
+ * is the
+ */
+ unsigned short slope;
+ /* Apply LSC gain to threshold. Enable this only if
+ * LSC is enabled in ISIF & thr_sel is IPIPE_GIC_THR_REG
+ */
+ unsigned char apply_lsc_gain;
+ /* Multiply Nf2 threshold by this gain. Use this when thr_sel
+ * is IPIPE_GIC_THR_NF
+ */
+ struct ipipe_float_u8 nf2_thr_gain;
+ /* Weight function uses difference as index or high pass value.
+ * Used for adaptive gain algorithm
+ */
+ enum vpfe_ipipe_gic_wt_fn_type wt_fn_type;
+};
+
+/* Structure for configuring WB module */
+struct vpfe_ipipe_wb {
+ /* Offset (S12) for R */
+ short ofst_r;
+ /* Offset (S12) for Gr */
+ short ofst_gr;
+ /* Offset (S12) for Gb */
+ short ofst_gb;
+ /* Offset (S12) for B */
+ short ofst_b;
+ /* Gain (U13Q9) for Red */
+ struct ipipe_float_u16 gain_r;
+ /* Gain (U13Q9) for Gr */
+ struct ipipe_float_u16 gain_gr;
+ /* Gain (U13Q9) for Gb */
+ struct ipipe_float_u16 gain_gb;
+ /* Gain (U13Q9) for Blue */
+ struct ipipe_float_u16 gain_b;
+};
+
+enum vpfe_ipipe_cfa_alg {
+ /* Algorithm is 2DirAC */
+ VPFE_IPIPE_CFA_ALG_2DIRAC,
+ /* Algorithm is 2DirAC + Digital Antialiasing (DAA) */
+ VPFE_IPIPE_CFA_ALG_2DIRAC_DAA,
+ /* Algorithm is DAA */
+ VPFE_IPIPE_CFA_ALG_DAA
+};
+
+/* Structure for CFA Interpolation */
+struct vpfe_ipipe_cfa {
+ /* 2DirAC or 2DirAC + DAA */
+ enum vpfe_ipipe_cfa_alg alg;
+ /* 2Dir CFA HP value Low Threshold */
+ unsigned short hpf_thr_2dir;
+ /* 2Dir CFA HP value slope */
+ unsigned short hpf_slp_2dir;
+ /* 2Dir CFA HP mix threshold */
+ unsigned short hp_mix_thr_2dir;
+ /* 2Dir CFA HP mix slope */
+ unsigned short hp_mix_slope_2dir;
+ /* 2Dir Direction threshold */
+ unsigned short dir_thr_2dir;
+ /* 2Dir Direction slope */
+ unsigned short dir_slope_2dir;
+ /* 2Dir Non Directional Weight */
+ unsigned short nd_wt_2dir;
+ /* DAA Mono Hue Fraction */
+ unsigned short hue_fract_daa;
+ /* DAA Mono Edge threshold */
+ unsigned short edge_thr_daa;
+ /* DAA Mono threshold minimum */
+ unsigned short thr_min_daa;
+ /* DAA Mono threshold slope */
+ unsigned short thr_slope_daa;
+ /* DAA Mono slope minimum */
+ unsigned short slope_min_daa;
+ /* DAA Mono slope slope */
+ unsigned short slope_slope_daa;
+ /* DAA Mono LP wight */
+ unsigned short lp_wt_daa;
+};
+
+/* Struct for configuring RGB2RGB blending module */
+struct vpfe_ipipe_rgb2rgb {
+ /* Matrix coefficient for RR S12Q8 for ID = 1 and S11Q8 for ID = 2 */
+ struct ipipe_float_s16 coef_rr;
+ /* Matrix coefficient for GR S12Q8/S11Q8 */
+ struct ipipe_float_s16 coef_gr;
+ /* Matrix coefficient for BR S12Q8/S11Q8 */
+ struct ipipe_float_s16 coef_br;
+ /* Matrix coefficient for RG S12Q8/S11Q8 */
+ struct ipipe_float_s16 coef_rg;
+ /* Matrix coefficient for GG S12Q8/S11Q8 */
+ struct ipipe_float_s16 coef_gg;
+ /* Matrix coefficient for BG S12Q8/S11Q8 */
+ struct ipipe_float_s16 coef_bg;
+ /* Matrix coefficient for RB S12Q8/S11Q8 */
+ struct ipipe_float_s16 coef_rb;
+ /* Matrix coefficient for GB S12Q8/S11Q8 */
+ struct ipipe_float_s16 coef_gb;
+ /* Matrix coefficient for BB S12Q8/S11Q8 */
+ struct ipipe_float_s16 coef_bb;
+ /* Output offset for R S13/S11 */
+ int out_ofst_r;
+ /* Output offset for G S13/S11 */
+ int out_ofst_g;
+ /* Output offset for B S13/S11 */
+ int out_ofst_b;
+};
+
+#define VPFE_IPIPE_MAX_SIZE_GAMMA 512
+
+enum vpfe_ipipe_gamma_tbl_size {
+ VPFE_IPIPE_GAMMA_TBL_SZ_64 = 64,
+ VPFE_IPIPE_GAMMA_TBL_SZ_128 = 128,
+ VPFE_IPIPE_GAMMA_TBL_SZ_256 = 256,
+ VPFE_IPIPE_GAMMA_TBL_SZ_512 = 512,
+};
+
+enum vpfe_ipipe_gamma_tbl_sel {
+ VPFE_IPIPE_GAMMA_TBL_RAM = 0,
+ VPFE_IPIPE_GAMMA_TBL_ROM = 1,
+};
+
+struct vpfe_ipipe_gamma_entry {
+ /* 10 bit slope */
+ short slope;
+ /* 10 bit offset */
+ unsigned short offset;
+};
+
+/* Structure for configuring Gamma correction module */
+struct vpfe_ipipe_gamma {
+ /* 0 - Enable Gamma correction for Red
+ * 1 - bypass Gamma correction. Data is divided by 16
+ */
+ unsigned char bypass_r;
+ /* 0 - Enable Gamma correction for Blue
+ * 1 - bypass Gamma correction. Data is divided by 16
+ */
+ unsigned char bypass_b;
+ /* 0 - Enable Gamma correction for Green
+ * 1 - bypass Gamma correction. Data is divided by 16
+ */
+ unsigned char bypass_g;
+ /* IPIPE_GAMMA_TBL_RAM or IPIPE_GAMMA_TBL_ROM */
+ enum vpfe_ipipe_gamma_tbl_sel tbl_sel;
+ /* Table size for RAM gamma table.
+ */
+ enum vpfe_ipipe_gamma_tbl_size tbl_size;
+ /* R table */
+ struct vpfe_ipipe_gamma_entry table_r[VPFE_IPIPE_MAX_SIZE_GAMMA];
+ /* Blue table */
+ struct vpfe_ipipe_gamma_entry table_b[VPFE_IPIPE_MAX_SIZE_GAMMA];
+ /* Green table */
+ struct vpfe_ipipe_gamma_entry table_g[VPFE_IPIPE_MAX_SIZE_GAMMA];
+};
+
+#define VPFE_IPIPE_MAX_SIZE_3D_LUT 729
+
+struct vpfe_ipipe_3d_lut_entry {
+ /* 10 bit entry for red */
+ unsigned short r;
+ /* 10 bit entry for green */
+ unsigned short g;
+ /* 10 bit entry for blue */
+ unsigned short b;
+};
+
+/* structure for 3D-LUT */
+struct vpfe_ipipe_3d_lut {
+ /* enable/disable 3D lut */
+ unsigned char en;
+ /* 3D - LUT table entry */
+ struct vpfe_ipipe_3d_lut_entry table[VPFE_IPIPE_MAX_SIZE_3D_LUT];
+};
+
+/* Struct for configuring rgb2ycbcr module */
+struct vpfe_ipipe_rgb2yuv {
+ /* Matrix coefficient for RY S12Q8 */
+ struct ipipe_float_s16 coef_ry;
+ /* Matrix coefficient for GY S12Q8 */
+ struct ipipe_float_s16 coef_gy;
+ /* Matrix coefficient for BY S12Q8 */
+ struct ipipe_float_s16 coef_by;
+ /* Matrix coefficient for RCb S12Q8 */
+ struct ipipe_float_s16 coef_rcb;
+ /* Matrix coefficient for GCb S12Q8 */
+ struct ipipe_float_s16 coef_gcb;
+ /* Matrix coefficient for BCb S12Q8 */
+ struct ipipe_float_s16 coef_bcb;
+ /* Matrix coefficient for RCr S12Q8 */
+ struct ipipe_float_s16 coef_rcr;
+ /* Matrix coefficient for GCr S12Q8 */
+ struct ipipe_float_s16 coef_gcr;
+ /* Matrix coefficient for BCr S12Q8 */
+ struct ipipe_float_s16 coef_bcr;
+ /* Output offset for R S11 */
+ int out_ofst_y;
+ /* Output offset for Cb S11 */
+ int out_ofst_cb;
+ /* Output offset for Cr S11 */
+ int out_ofst_cr;
+};
+
+enum vpfe_ipipe_gbce_type {
+ VPFE_IPIPE_GBCE_Y_VAL_TBL = 0,
+ VPFE_IPIPE_GBCE_GAIN_TBL = 1,
+};
+
+#define VPFE_IPIPE_MAX_SIZE_GBCE_LUT 1024
+
+/* structure for Global brightness and Contrast */
+struct vpfe_ipipe_gbce {
+ /* enable/disable GBCE */
+ unsigned char en;
+ /* Y - value table or Gain table */
+ enum vpfe_ipipe_gbce_type type;
+ /* ptr to LUT for GBCE with 1024 entries */
+ unsigned short table[VPFE_IPIPE_MAX_SIZE_GBCE_LUT];
+};
+
+/* Chrominance position. Applicable only for YCbCr input
+ * Applied after edge enhancement
+ */
+enum vpfe_chr_pos {
+ /* Co-siting, same position with luminance */
+ VPFE_IPIPE_YUV422_CHR_POS_COSITE = 0,
+ /* Centering, In the middle of luminance */
+ VPFE_IPIPE_YUV422_CHR_POS_CENTRE = 1,
+};
+
+/* Structure for configuring yuv422 conversion module */
+struct vpfe_ipipe_yuv422_conv {
+ /* Max Chrominance value */
+ unsigned char en_chrom_lpf;
+ /* 1 - enable LPF for chrminance, 0 - disable */
+ enum vpfe_chr_pos chrom_pos;
+};
+
+#define VPFE_IPIPE_MAX_SIZE_YEE_LUT 1024
+
+enum vpfe_ipipe_yee_merge_meth {
+ VPFE_IPIPE_YEE_ABS_MAX = 0,
+ VPFE_IPIPE_YEE_EE_ES = 1,
+};
+
+/* Structure for configuring YUV Edge Enhancement module */
+struct vpfe_ipipe_yee {
+ /* 1 - enable enhancement, 0 - disable */
+ unsigned char en;
+ /* enable/disable halo reduction in edge sharpner */
+ unsigned char en_halo_red;
+ /* Merge method between Edge Enhancer and Edge sharpner */
+ enum vpfe_ipipe_yee_merge_meth merge_meth;
+ /* HPF Shift length */
+ unsigned char hpf_shft;
+ /* HPF Coefficient 00, S10 */
+ short hpf_coef_00;
+ /* HPF Coefficient 01, S10 */
+ short hpf_coef_01;
+ /* HPF Coefficient 02, S10 */
+ short hpf_coef_02;
+ /* HPF Coefficient 10, S10 */
+ short hpf_coef_10;
+ /* HPF Coefficient 11, S10 */
+ short hpf_coef_11;
+ /* HPF Coefficient 12, S10 */
+ short hpf_coef_12;
+ /* HPF Coefficient 20, S10 */
+ short hpf_coef_20;
+ /* HPF Coefficient 21, S10 */
+ short hpf_coef_21;
+ /* HPF Coefficient 22, S10 */
+ short hpf_coef_22;
+ /* Lower threshold before referring to LUT */
+ unsigned short yee_thr;
+ /* Edge sharpener Gain */
+ unsigned short es_gain;
+ /* Edge sharpener lower threshold */
+ unsigned short es_thr1;
+ /* Edge sharpener upper threshold */
+ unsigned short es_thr2;
+ /* Edge sharpener gain on gradient */
+ unsigned short es_gain_grad;
+ /* Edge sharpener offset on gradient */
+ unsigned short es_ofst_grad;
+ /* Ptr to EE table. Must have 1024 entries */
+ short table[VPFE_IPIPE_MAX_SIZE_YEE_LUT];
+};
+
+enum vpfe_ipipe_car_meth {
+ /* Chromatic Gain Control */
+ VPFE_IPIPE_CAR_CHR_GAIN_CTRL = 0,
+ /* Dynamic switching between CHR_GAIN_CTRL
+ * and MED_FLTR
+ */
+ VPFE_IPIPE_CAR_DYN_SWITCH = 1,
+ /* Median Filter */
+ VPFE_IPIPE_CAR_MED_FLTR = 2,
+};
+
+enum vpfe_ipipe_car_hpf_type {
+ VPFE_IPIPE_CAR_HPF_Y = 0,
+ VPFE_IPIPE_CAR_HPF_H = 1,
+ VPFE_IPIPE_CAR_HPF_V = 2,
+ VPFE_IPIPE_CAR_HPF_2D = 3,
+ /* 2D HPF from YUV Edge Enhancement */
+ VPFE_IPIPE_CAR_HPF_2D_YEE = 4,
+};
+
+struct vpfe_ipipe_car_gain {
+ /* csup_gain */
+ unsigned char gain;
+ /* csup_shf. */
+ unsigned char shft;
+ /* gain minimum */
+ unsigned short gain_min;
+};
+
+/* Structure for Chromatic Artifact Reduction */
+struct vpfe_ipipe_car {
+ /* enable/disable */
+ unsigned char en;
+ /* Gain control or Dynamic switching */
+ enum vpfe_ipipe_car_meth meth;
+ /* Gain1 function configuration for Gain control */
+ struct vpfe_ipipe_car_gain gain1;
+ /* Gain2 function configuration for Gain control */
+ struct vpfe_ipipe_car_gain gain2;
+ /* HPF type used for CAR */
+ enum vpfe_ipipe_car_hpf_type hpf;
+ /* csup_thr: HPF threshold for Gain control */
+ unsigned char hpf_thr;
+ /* Down shift value for hpf. 2 bits */
+ unsigned char hpf_shft;
+ /* switch limit for median filter */
+ unsigned char sw0;
+ /* switch coefficient for Gain control */
+ unsigned char sw1;
+};
+
+/* structure for Chromatic Gain Suppression */
+struct vpfe_ipipe_cgs {
+ /* enable/disable */
+ unsigned char en;
+ /* gain1 bright side threshold */
+ unsigned char h_thr;
+ /* gain1 bright side slope */
+ unsigned char h_slope;
+ /* gain1 down shift value for bright side */
+ unsigned char h_shft;
+ /* gain1 bright side minimum gain */
+ unsigned char h_min;
+};
+
+/* Max pixels allowed in the input. If above this either decimation
+ * or frame division mode to be enabled
+ */
+#define VPFE_IPIPE_MAX_INPUT_WIDTH 2600
+
+struct vpfe_ipipe_input_config {
+ unsigned int vst;
+ unsigned int hst;
+};
+
+/**
+ * struct vpfe_ipipe_config - IPIPE engine configuration (user)
+ * @input_config: Pointer to structure for ipipe configuration.
+ * @flag: Specifies which ISP IPIPE functions should be enabled.
+ * @lutdpc: Pointer to luma enhancement structure.
+ * @otfdpc: Pointer to structure for defect correction.
+ * @nf1: Pointer to structure for Noise Filter.
+ * @nf2: Pointer to structure for Noise Filter.
+ * @gic: Pointer to structure for Green Imbalance.
+ * @wbal: Pointer to structure for White Balance.
+ * @cfa: Pointer to structure containing the CFA interpolation.
+ * @rgb2rgb1: Pointer to structure for RGB to RGB Blending.
+ * @rgb2rgb2: Pointer to structure for RGB to RGB Blending.
+ * @gamma: Pointer to gamma structure.
+ * @lut: Pointer to structure for 3D LUT.
+ * @rgb2yuv: Pointer to structure for RGB-YCbCr conversion.
+ * @gbce: Pointer to structure for Global Brightness,Contrast Control.
+ * @yuv422_conv: Pointer to structure for YUV 422 conversion.
+ * @yee: Pointer to structure for Edge Enhancer.
+ * @car: Pointer to structure for Chromatic Artifact Reduction.
+ * @cgs: Pointer to structure for Chromatic Gain Suppression.
+ */
+struct vpfe_ipipe_config {
+ __u32 flag;
+ struct vpfe_ipipe_input_config __user *input_config;
+ struct vpfe_ipipe_lutdpc __user *lutdpc;
+ struct vpfe_ipipe_otfdpc __user *otfdpc;
+ struct vpfe_ipipe_nf __user *nf1;
+ struct vpfe_ipipe_nf __user *nf2;
+ struct vpfe_ipipe_gic __user *gic;
+ struct vpfe_ipipe_wb __user *wbal;
+ struct vpfe_ipipe_cfa __user *cfa;
+ struct vpfe_ipipe_rgb2rgb __user *rgb2rgb1;
+ struct vpfe_ipipe_rgb2rgb __user *rgb2rgb2;
+ struct vpfe_ipipe_gamma __user *gamma;
+ struct vpfe_ipipe_3d_lut __user *lut;
+ struct vpfe_ipipe_rgb2yuv __user *rgb2yuv;
+ struct vpfe_ipipe_gbce __user *gbce;
+ struct vpfe_ipipe_yuv422_conv __user *yuv422_conv;
+ struct vpfe_ipipe_yee __user *yee;
+ struct vpfe_ipipe_car __user *car;
+ struct vpfe_ipipe_cgs __user *cgs;
+};
+
+/*******************************************************************
+** Resizer API structures
+*******************************************************************/
+/* Interpolation types used for horizontal rescale */
+enum vpfe_rsz_intp_t {
+ VPFE_RSZ_INTP_CUBIC,
+ VPFE_RSZ_INTP_LINEAR
+};
+
+/* Horizontal LPF intensity selection */
+enum vpfe_rsz_h_lpf_lse_t {
+ VPFE_RSZ_H_LPF_LSE_INTERN,
+ VPFE_RSZ_H_LPF_LSE_USER_VAL
+};
+
+enum vpfe_rsz_down_scale_ave_sz {
+ VPFE_IPIPE_DWN_SCALE_1_OVER_2,
+ VPFE_IPIPE_DWN_SCALE_1_OVER_4,
+ VPFE_IPIPE_DWN_SCALE_1_OVER_8,
+ VPFE_IPIPE_DWN_SCALE_1_OVER_16,
+ VPFE_IPIPE_DWN_SCALE_1_OVER_32,
+ VPFE_IPIPE_DWN_SCALE_1_OVER_64,
+ VPFE_IPIPE_DWN_SCALE_1_OVER_128,
+ VPFE_IPIPE_DWN_SCALE_1_OVER_256
+};
+
+struct vpfe_rsz_output_spec {
+ /* enable horizontal flip */
+ unsigned char h_flip;
+ /* enable vertical flip */
+ unsigned char v_flip;
+ /* line start offset for y. */
+ unsigned int vst_y;
+ /* line start offset for c. Only for 420 */
+ unsigned int vst_c;
+ /* vertical rescale interpolation type, YCbCr or Luminance */
+ enum vpfe_rsz_intp_t v_typ_y;
+ /* vertical rescale interpolation type for Chrominance */
+ enum vpfe_rsz_intp_t v_typ_c;
+ /* vertical lpf intensity - Luminance */
+ unsigned char v_lpf_int_y;
+ /* vertical lpf intensity - Chrominance */
+ unsigned char v_lpf_int_c;
+ /* horizontal rescale interpolation types, YCbCr or Luminance */
+ enum vpfe_rsz_intp_t h_typ_y;
+ /* horizontal rescale interpolation types, Chrominance */
+ enum vpfe_rsz_intp_t h_typ_c;
+ /* horizontal lpf intensity - Luminance */
+ unsigned char h_lpf_int_y;
+ /* horizontal lpf intensity - Chrominance */
+ unsigned char h_lpf_int_c;
+ /* Use down scale mode for scale down */
+ unsigned char en_down_scale;
+ /* if downscale, set the downscale more average size for horizontal
+ * direction. Used only if output width and height is less than
+ * input sizes
+ */
+ enum vpfe_rsz_down_scale_ave_sz h_dscale_ave_sz;
+ /* if downscale, set the downscale more average size for vertical
+ * direction. Used only if output width and height is less than
+ * input sizes
+ */
+ enum vpfe_rsz_down_scale_ave_sz v_dscale_ave_sz;
+ /* Y offset. If set, the offset would be added to the base address
+ */
+ unsigned int user_y_ofst;
+ /* C offset. If set, the offset would be added to the base address
+ */
+ unsigned int user_c_ofst;
+};
+
+struct vpfe_rsz_config_params {
+ unsigned int vst;
+ /* horizontal start position of the image
+ * data to IPIPE
+ */
+ unsigned int hst;
+ /* output spec of the image data coming out of resizer - 0(UYVY).
+ */
+ struct vpfe_rsz_output_spec output1;
+ /* output spec of the image data coming out of resizer - 1(UYVY).
+ */
+ struct vpfe_rsz_output_spec output2;
+ /* 0 , chroma sample at odd pixel, 1 - even pixel */
+ unsigned char chroma_sample_even;
+ unsigned char frame_div_mode_en;
+ unsigned char yuv_y_min;
+ unsigned char yuv_y_max;
+ unsigned char yuv_c_min;
+ unsigned char yuv_c_max;
+ enum vpfe_chr_pos out_chr_pos;
+ unsigned char bypass;
+};
+
+/* Structure for VIDIOC_VPFE_RSZ_[S/G]_CONFIG IOCTLs */
+struct vpfe_rsz_config {
+ struct vpfe_rsz_config_params *config;
+};
+
+#endif /* _DAVINCI_VPFE_USER_H */
diff --git a/drivers/staging/media/davinci_vpfe/dm365_ipipe.c b/drivers/staging/media/davinci_vpfe/dm365_ipipe.c
new file mode 100644
index 000000000..1bbb90ce0
--- /dev/null
+++ b/drivers/staging/media/davinci_vpfe/dm365_ipipe.c
@@ -0,0 +1,1862 @@
+/*
+ * Copyright (C) 2012 Texas Instruments 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 version 2.
+ *
+ * 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
+ *
+ * Contributors:
+ * Manjunath Hadli <manjunath.hadli@ti.com>
+ * Prabhakar Lad <prabhakar.lad@ti.com>
+ *
+ *
+ * IPIPE allows fine tuning of the input image using different
+ * tuning modules in IPIPE. Some examples :- Noise filter, Defect
+ * pixel correction etc. It essentially operate on Bayer Raw data
+ * or YUV raw data. To do image tuning, application call,
+ *
+ */
+
+#include <linux/slab.h>
+
+#include "dm365_ipipe.h"
+#include "dm365_ipipe_hw.h"
+#include "vpfe_mc_capture.h"
+
+#define MIN_OUT_WIDTH 32
+#define MIN_OUT_HEIGHT 32
+
+/* ipipe input format's */
+static const unsigned int ipipe_input_fmts[] = {
+ MEDIA_BUS_FMT_UYVY8_2X8,
+ MEDIA_BUS_FMT_SGRBG12_1X12,
+ MEDIA_BUS_FMT_SGRBG10_DPCM8_1X8,
+ MEDIA_BUS_FMT_SGRBG10_ALAW8_1X8,
+};
+
+/* ipipe output format's */
+static const unsigned int ipipe_output_fmts[] = {
+ MEDIA_BUS_FMT_UYVY8_2X8,
+};
+
+static int ipipe_validate_lutdpc_params(struct vpfe_ipipe_lutdpc *lutdpc)
+{
+ int i;
+
+ if (lutdpc->en > 1 || lutdpc->repl_white > 1 ||
+ lutdpc->dpc_size > LUT_DPC_MAX_SIZE)
+ return -EINVAL;
+
+ if (lutdpc->en && !lutdpc->table)
+ return -EINVAL;
+
+ for (i = 0; i < lutdpc->dpc_size; i++)
+ if (lutdpc->table[i].horz_pos > LUT_DPC_H_POS_MASK ||
+ lutdpc->table[i].vert_pos > LUT_DPC_V_POS_MASK)
+ return -EINVAL;
+
+ return 0;
+}
+
+static int ipipe_set_lutdpc_params(struct vpfe_ipipe_device *ipipe, void *param)
+{
+ struct vpfe_ipipe_lutdpc *lutdpc = &ipipe->config.lutdpc;
+ struct vpfe_ipipe_lutdpc *dpc_param;
+ struct device *dev;
+
+ if (!param) {
+ memset((void *)lutdpc, 0, sizeof(struct vpfe_ipipe_lutdpc));
+ goto success;
+ }
+
+ dev = ipipe->subdev.v4l2_dev->dev;
+ dpc_param = (struct vpfe_ipipe_lutdpc *)param;
+ lutdpc->en = dpc_param->en;
+ lutdpc->repl_white = dpc_param->repl_white;
+ lutdpc->dpc_size = dpc_param->dpc_size;
+ memcpy(&lutdpc->table, &dpc_param->table,
+ (dpc_param->dpc_size * sizeof(struct vpfe_ipipe_lutdpc_entry)));
+ if (ipipe_validate_lutdpc_params(lutdpc) < 0)
+ return -EINVAL;
+
+success:
+ ipipe_set_lutdpc_regs(ipipe->base_addr, ipipe->isp5_base_addr, lutdpc);
+
+ return 0;
+}
+
+static int ipipe_get_lutdpc_params(struct vpfe_ipipe_device *ipipe, void *param)
+{
+ struct vpfe_ipipe_lutdpc *lut_param = (struct vpfe_ipipe_lutdpc *)param;
+ struct vpfe_ipipe_lutdpc *lutdpc = &ipipe->config.lutdpc;
+
+ lut_param->en = lutdpc->en;
+ lut_param->repl_white = lutdpc->repl_white;
+ lut_param->dpc_size = lutdpc->dpc_size;
+ memcpy(&lut_param->table, &lutdpc->table,
+ (lutdpc->dpc_size * sizeof(struct vpfe_ipipe_lutdpc_entry)));
+
+ return 0;
+}
+
+static int ipipe_set_input_config(struct vpfe_ipipe_device *ipipe, void *param)
+{
+ struct vpfe_ipipe_input_config *config = &ipipe->config.input_config;
+
+ if (!param)
+ memset(config, 0, sizeof(struct vpfe_ipipe_input_config));
+ else
+ memcpy(config, param, sizeof(struct vpfe_ipipe_input_config));
+ return 0;
+}
+
+static int ipipe_get_input_config(struct vpfe_ipipe_device *ipipe, void *param)
+{
+ struct vpfe_ipipe_input_config *config = &ipipe->config.input_config;
+
+ if (!param)
+ return -EINVAL;
+
+ memcpy(param, config, sizeof(struct vpfe_ipipe_input_config));
+
+ return 0;
+}
+
+static int ipipe_validate_otfdpc_params(struct vpfe_ipipe_otfdpc *dpc_param)
+{
+ struct vpfe_ipipe_otfdpc_2_0_cfg *dpc_2_0;
+ struct vpfe_ipipe_otfdpc_3_0_cfg *dpc_3_0;
+
+ if (dpc_param->en > 1)
+ return -EINVAL;
+
+ if (dpc_param->alg == VPFE_IPIPE_OTFDPC_2_0) {
+ dpc_2_0 = &dpc_param->alg_cfg.dpc_2_0;
+ if (dpc_2_0->det_thr.r > OTFDPC_DPC2_THR_MASK ||
+ dpc_2_0->det_thr.gr > OTFDPC_DPC2_THR_MASK ||
+ dpc_2_0->det_thr.gb > OTFDPC_DPC2_THR_MASK ||
+ dpc_2_0->det_thr.b > OTFDPC_DPC2_THR_MASK ||
+ dpc_2_0->corr_thr.r > OTFDPC_DPC2_THR_MASK ||
+ dpc_2_0->corr_thr.gr > OTFDPC_DPC2_THR_MASK ||
+ dpc_2_0->corr_thr.gb > OTFDPC_DPC2_THR_MASK ||
+ dpc_2_0->corr_thr.b > OTFDPC_DPC2_THR_MASK)
+ return -EINVAL;
+ return 0;
+ }
+
+ dpc_3_0 = &dpc_param->alg_cfg.dpc_3_0;
+
+ if (dpc_3_0->act_adj_shf > OTF_DPC3_0_SHF_MASK ||
+ dpc_3_0->det_thr > OTF_DPC3_0_DET_MASK ||
+ dpc_3_0->det_slp > OTF_DPC3_0_SLP_MASK ||
+ dpc_3_0->det_thr_min > OTF_DPC3_0_DET_MASK ||
+ dpc_3_0->det_thr_max > OTF_DPC3_0_DET_MASK ||
+ dpc_3_0->corr_thr > OTF_DPC3_0_CORR_MASK ||
+ dpc_3_0->corr_slp > OTF_DPC3_0_SLP_MASK ||
+ dpc_3_0->corr_thr_min > OTF_DPC3_0_CORR_MASK ||
+ dpc_3_0->corr_thr_max > OTF_DPC3_0_CORR_MASK)
+ return -EINVAL;
+
+ return 0;
+}
+
+static int ipipe_set_otfdpc_params(struct vpfe_ipipe_device *ipipe, void *param)
+{
+ struct vpfe_ipipe_otfdpc *dpc_param = (struct vpfe_ipipe_otfdpc *)param;
+ struct vpfe_ipipe_otfdpc *otfdpc = &ipipe->config.otfdpc;
+ struct device *dev;
+
+ if (!param) {
+ memset((void *)otfdpc, 0, sizeof(struct ipipe_otfdpc_2_0));
+ goto success;
+ }
+ dev = ipipe->subdev.v4l2_dev->dev;
+ memcpy(otfdpc, dpc_param, sizeof(struct vpfe_ipipe_otfdpc));
+ if (ipipe_validate_otfdpc_params(otfdpc) < 0) {
+ dev_err(dev, "Invalid otfdpc params\n");
+ return -EINVAL;
+ }
+
+success:
+ ipipe_set_otfdpc_regs(ipipe->base_addr, otfdpc);
+
+ return 0;
+}
+
+static int ipipe_get_otfdpc_params(struct vpfe_ipipe_device *ipipe, void *param)
+{
+ struct vpfe_ipipe_otfdpc *dpc_param = (struct vpfe_ipipe_otfdpc *)param;
+ struct vpfe_ipipe_otfdpc *otfdpc = &ipipe->config.otfdpc;
+
+ memcpy(dpc_param, otfdpc, sizeof(struct vpfe_ipipe_otfdpc));
+ return 0;
+}
+
+static int ipipe_validate_nf_params(struct vpfe_ipipe_nf *nf_param)
+{
+ int i;
+
+ if (nf_param->en > 1 || nf_param->shft_val > D2F_SHFT_VAL_MASK ||
+ nf_param->spread_val > D2F_SPR_VAL_MASK ||
+ nf_param->apply_lsc_gain > 1 ||
+ nf_param->edge_det_min_thr > D2F_EDGE_DET_THR_MASK ||
+ nf_param->edge_det_max_thr > D2F_EDGE_DET_THR_MASK)
+ return -EINVAL;
+
+ for (i = 0; i < VPFE_IPIPE_NF_THR_TABLE_SIZE; i++)
+ if (nf_param->thr[i] > D2F_THR_VAL_MASK)
+ return -EINVAL;
+
+ for (i = 0; i < VPFE_IPIPE_NF_STR_TABLE_SIZE; i++)
+ if (nf_param->str[i] > D2F_STR_VAL_MASK)
+ return -EINVAL;
+
+ return 0;
+}
+
+static int ipipe_set_nf_params(struct vpfe_ipipe_device *ipipe,
+ unsigned int id, void *param)
+{
+ struct vpfe_ipipe_nf *nf_param = (struct vpfe_ipipe_nf *)param;
+ struct vpfe_ipipe_nf *nf = &ipipe->config.nf1;
+ struct device *dev;
+
+ if (id == IPIPE_D2F_2ND)
+ nf = &ipipe->config.nf2;
+
+ if (!nf_param) {
+ memset((void *)nf, 0, sizeof(struct vpfe_ipipe_nf));
+ goto success;
+ }
+
+ dev = ipipe->subdev.v4l2_dev->dev;
+ memcpy(nf, nf_param, sizeof(struct vpfe_ipipe_nf));
+ if (ipipe_validate_nf_params(nf) < 0) {
+ dev_err(dev, "Invalid nf params\n");
+ return -EINVAL;
+ }
+
+success:
+ ipipe_set_d2f_regs(ipipe->base_addr, id, nf);
+
+ return 0;
+}
+
+static int ipipe_set_nf1_params(struct vpfe_ipipe_device *ipipe, void *param)
+{
+ return ipipe_set_nf_params(ipipe, IPIPE_D2F_1ST, param);
+}
+
+static int ipipe_set_nf2_params(struct vpfe_ipipe_device *ipipe, void *param)
+{
+ return ipipe_set_nf_params(ipipe, IPIPE_D2F_2ND, param);
+}
+
+static int ipipe_get_nf_params(struct vpfe_ipipe_device *ipipe,
+ unsigned int id, void *param)
+{
+ struct vpfe_ipipe_nf *nf_param = (struct vpfe_ipipe_nf *)param;
+ struct vpfe_ipipe_nf *nf = &ipipe->config.nf1;
+
+ if (id == IPIPE_D2F_2ND)
+ nf = &ipipe->config.nf2;
+
+ memcpy(nf_param, nf, sizeof(struct vpfe_ipipe_nf));
+
+ return 0;
+}
+
+static int ipipe_get_nf1_params(struct vpfe_ipipe_device *ipipe, void *param)
+{
+ return ipipe_get_nf_params(ipipe, IPIPE_D2F_1ST, param);
+}
+
+static int ipipe_get_nf2_params(struct vpfe_ipipe_device *ipipe, void *param)
+{
+ return ipipe_get_nf_params(ipipe, IPIPE_D2F_2ND, param);
+}
+
+static int ipipe_validate_gic_params(struct vpfe_ipipe_gic *gic)
+{
+ if (gic->en > 1 || gic->gain > GIC_GAIN_MASK ||
+ gic->thr > GIC_THR_MASK || gic->slope > GIC_SLOPE_MASK ||
+ gic->apply_lsc_gain > 1 ||
+ gic->nf2_thr_gain.integer > GIC_NFGAN_INT_MASK ||
+ gic->nf2_thr_gain.decimal > GIC_NFGAN_DECI_MASK)
+ return -EINVAL;
+
+ return 0;
+}
+
+static int ipipe_set_gic_params(struct vpfe_ipipe_device *ipipe, void *param)
+{
+ struct vpfe_ipipe_gic *gic_param = (struct vpfe_ipipe_gic *)param;
+ struct device *dev = ipipe->subdev.v4l2_dev->dev;
+ struct vpfe_ipipe_gic *gic = &ipipe->config.gic;
+
+ if (!gic_param) {
+ memset((void *)gic, 0, sizeof(struct vpfe_ipipe_gic));
+ goto success;
+ }
+
+ memcpy(gic, gic_param, sizeof(struct vpfe_ipipe_gic));
+ if (ipipe_validate_gic_params(gic) < 0) {
+ dev_err(dev, "Invalid gic params\n");
+ return -EINVAL;
+ }
+
+success:
+ ipipe_set_gic_regs(ipipe->base_addr, gic);
+
+ return 0;
+}
+
+static int ipipe_get_gic_params(struct vpfe_ipipe_device *ipipe, void *param)
+{
+ struct vpfe_ipipe_gic *gic_param = (struct vpfe_ipipe_gic *)param;
+ struct vpfe_ipipe_gic *gic = &ipipe->config.gic;
+
+ memcpy(gic_param, gic, sizeof(struct vpfe_ipipe_gic));
+
+ return 0;
+}
+
+static int ipipe_validate_wb_params(struct vpfe_ipipe_wb *wbal)
+{
+ if (wbal->ofst_r > WB_OFFSET_MASK ||
+ wbal->ofst_gr > WB_OFFSET_MASK ||
+ wbal->ofst_gb > WB_OFFSET_MASK ||
+ wbal->ofst_b > WB_OFFSET_MASK ||
+ wbal->gain_r.integer > WB_GAIN_INT_MASK ||
+ wbal->gain_r.decimal > WB_GAIN_DECI_MASK ||
+ wbal->gain_gr.integer > WB_GAIN_INT_MASK ||
+ wbal->gain_gr.decimal > WB_GAIN_DECI_MASK ||
+ wbal->gain_gb.integer > WB_GAIN_INT_MASK ||
+ wbal->gain_gb.decimal > WB_GAIN_DECI_MASK ||
+ wbal->gain_b.integer > WB_GAIN_INT_MASK ||
+ wbal->gain_b.decimal > WB_GAIN_DECI_MASK)
+ return -EINVAL;
+
+ return 0;
+}
+
+static int ipipe_set_wb_params(struct vpfe_ipipe_device *ipipe, void *param)
+{
+ struct vpfe_ipipe_wb *wb_param = (struct vpfe_ipipe_wb *)param;
+ struct vpfe_ipipe_wb *wbal = &ipipe->config.wbal;
+
+ if (!wb_param) {
+ const struct vpfe_ipipe_wb wb_defaults = {
+ .gain_r = {2, 0x0},
+ .gain_gr = {2, 0x0},
+ .gain_gb = {2, 0x0},
+ .gain_b = {2, 0x0}
+ };
+ memcpy(wbal, &wb_defaults, sizeof(struct vpfe_ipipe_wb));
+ goto success;
+ }
+
+ memcpy(wbal, wb_param, sizeof(struct vpfe_ipipe_wb));
+ if (ipipe_validate_wb_params(wbal) < 0)
+ return -EINVAL;
+
+success:
+ ipipe_set_wb_regs(ipipe->base_addr, wbal);
+
+ return 0;
+}
+
+static int ipipe_get_wb_params(struct vpfe_ipipe_device *ipipe, void *param)
+{
+ struct vpfe_ipipe_wb *wb_param = (struct vpfe_ipipe_wb *)param;
+ struct vpfe_ipipe_wb *wbal = &ipipe->config.wbal;
+
+ memcpy(wb_param, wbal, sizeof(struct vpfe_ipipe_wb));
+ return 0;
+}
+
+static int ipipe_validate_cfa_params(struct vpfe_ipipe_cfa *cfa)
+{
+ if (cfa->hpf_thr_2dir > CFA_HPF_THR_2DIR_MASK ||
+ cfa->hpf_slp_2dir > CFA_HPF_SLOPE_2DIR_MASK ||
+ cfa->hp_mix_thr_2dir > CFA_HPF_MIX_THR_2DIR_MASK ||
+ cfa->hp_mix_slope_2dir > CFA_HPF_MIX_SLP_2DIR_MASK ||
+ cfa->dir_thr_2dir > CFA_DIR_THR_2DIR_MASK ||
+ cfa->dir_slope_2dir > CFA_DIR_SLP_2DIR_MASK ||
+ cfa->nd_wt_2dir > CFA_ND_WT_2DIR_MASK ||
+ cfa->hue_fract_daa > CFA_DAA_HUE_FRA_MASK ||
+ cfa->edge_thr_daa > CFA_DAA_EDG_THR_MASK ||
+ cfa->thr_min_daa > CFA_DAA_THR_MIN_MASK ||
+ cfa->thr_slope_daa > CFA_DAA_THR_SLP_MASK ||
+ cfa->slope_min_daa > CFA_DAA_SLP_MIN_MASK ||
+ cfa->slope_slope_daa > CFA_DAA_SLP_SLP_MASK ||
+ cfa->lp_wt_daa > CFA_DAA_LP_WT_MASK)
+ return -EINVAL;
+
+ return 0;
+}
+
+static int ipipe_set_cfa_params(struct vpfe_ipipe_device *ipipe, void *param)
+{
+ struct vpfe_ipipe_cfa *cfa_param = (struct vpfe_ipipe_cfa *)param;
+ struct vpfe_ipipe_cfa *cfa = &ipipe->config.cfa;
+
+ if (!cfa_param) {
+ memset(cfa, 0, sizeof(struct vpfe_ipipe_cfa));
+ cfa->alg = VPFE_IPIPE_CFA_ALG_2DIRAC;
+ goto success;
+ }
+
+ memcpy(cfa, cfa_param, sizeof(struct vpfe_ipipe_cfa));
+ if (ipipe_validate_cfa_params(cfa) < 0)
+ return -EINVAL;
+
+success:
+ ipipe_set_cfa_regs(ipipe->base_addr, cfa);
+
+ return 0;
+}
+
+static int ipipe_get_cfa_params(struct vpfe_ipipe_device *ipipe, void *param)
+{
+ struct vpfe_ipipe_cfa *cfa_param = (struct vpfe_ipipe_cfa *)param;
+ struct vpfe_ipipe_cfa *cfa = &ipipe->config.cfa;
+
+ memcpy(cfa_param, cfa, sizeof(struct vpfe_ipipe_cfa));
+ return 0;
+}
+
+static int
+ipipe_validate_rgb2rgb_params(struct vpfe_ipipe_rgb2rgb *rgb2rgb,
+ unsigned int id)
+{
+ u32 gain_int_upper = RGB2RGB_1_GAIN_INT_MASK;
+ u32 offset_upper = RGB2RGB_1_OFST_MASK;
+
+ if (id == IPIPE_RGB2RGB_2) {
+ offset_upper = RGB2RGB_2_OFST_MASK;
+ gain_int_upper = RGB2RGB_2_GAIN_INT_MASK;
+ }
+
+ if (rgb2rgb->coef_rr.decimal > RGB2RGB_GAIN_DECI_MASK ||
+ rgb2rgb->coef_rr.integer > gain_int_upper)
+ return -EINVAL;
+
+ if (rgb2rgb->coef_gr.decimal > RGB2RGB_GAIN_DECI_MASK ||
+ rgb2rgb->coef_gr.integer > gain_int_upper)
+ return -EINVAL;
+
+ if (rgb2rgb->coef_br.decimal > RGB2RGB_GAIN_DECI_MASK ||
+ rgb2rgb->coef_br.integer > gain_int_upper)
+ return -EINVAL;
+
+ if (rgb2rgb->coef_rg.decimal > RGB2RGB_GAIN_DECI_MASK ||
+ rgb2rgb->coef_rg.integer > gain_int_upper)
+ return -EINVAL;
+
+ if (rgb2rgb->coef_gg.decimal > RGB2RGB_GAIN_DECI_MASK ||
+ rgb2rgb->coef_gg.integer > gain_int_upper)
+ return -EINVAL;
+
+ if (rgb2rgb->coef_bg.decimal > RGB2RGB_GAIN_DECI_MASK ||
+ rgb2rgb->coef_bg.integer > gain_int_upper)
+ return -EINVAL;
+
+ if (rgb2rgb->coef_rb.decimal > RGB2RGB_GAIN_DECI_MASK ||
+ rgb2rgb->coef_rb.integer > gain_int_upper)
+ return -EINVAL;
+
+ if (rgb2rgb->coef_gb.decimal > RGB2RGB_GAIN_DECI_MASK ||
+ rgb2rgb->coef_gb.integer > gain_int_upper)
+ return -EINVAL;
+
+ if (rgb2rgb->coef_bb.decimal > RGB2RGB_GAIN_DECI_MASK ||
+ rgb2rgb->coef_bb.integer > gain_int_upper)
+ return -EINVAL;
+
+ if (rgb2rgb->out_ofst_r > offset_upper ||
+ rgb2rgb->out_ofst_g > offset_upper ||
+ rgb2rgb->out_ofst_b > offset_upper)
+ return -EINVAL;
+
+ return 0;
+}
+
+static int ipipe_set_rgb2rgb_params(struct vpfe_ipipe_device *ipipe,
+ unsigned int id, void *param)
+{
+ struct vpfe_ipipe_rgb2rgb *rgb2rgb = &ipipe->config.rgb2rgb1;
+ struct device *dev = ipipe->subdev.v4l2_dev->dev;
+ struct vpfe_ipipe_rgb2rgb *rgb2rgb_param;
+
+ rgb2rgb_param = (struct vpfe_ipipe_rgb2rgb *)param;
+
+ if (id == IPIPE_RGB2RGB_2)
+ rgb2rgb = &ipipe->config.rgb2rgb2;
+
+ if (!rgb2rgb_param) {
+ const struct vpfe_ipipe_rgb2rgb rgb2rgb_defaults = {
+ .coef_rr = {1, 0}, /* 256 */
+ .coef_gr = {0, 0},
+ .coef_br = {0, 0},
+ .coef_rg = {0, 0},
+ .coef_gg = {1, 0}, /* 256 */
+ .coef_bg = {0, 0},
+ .coef_rb = {0, 0},
+ .coef_gb = {0, 0},
+ .coef_bb = {1, 0}, /* 256 */
+ };
+ /* Copy defaults for rgb2rgb conversion */
+ memcpy(rgb2rgb, &rgb2rgb_defaults,
+ sizeof(struct vpfe_ipipe_rgb2rgb));
+ goto success;
+ }
+
+ memcpy(rgb2rgb, rgb2rgb_param, sizeof(struct vpfe_ipipe_rgb2rgb));
+ if (ipipe_validate_rgb2rgb_params(rgb2rgb, id) < 0) {
+ dev_err(dev, "Invalid rgb2rgb params\n");
+ return -EINVAL;
+ }
+
+success:
+ ipipe_set_rgb2rgb_regs(ipipe->base_addr, id, rgb2rgb);
+
+ return 0;
+}
+
+static int
+ipipe_set_rgb2rgb_1_params(struct vpfe_ipipe_device *ipipe, void *param)
+{
+ return ipipe_set_rgb2rgb_params(ipipe, IPIPE_RGB2RGB_1, param);
+}
+
+static int
+ipipe_set_rgb2rgb_2_params(struct vpfe_ipipe_device *ipipe, void *param)
+{
+ return ipipe_set_rgb2rgb_params(ipipe, IPIPE_RGB2RGB_2, param);
+}
+
+static int ipipe_get_rgb2rgb_params(struct vpfe_ipipe_device *ipipe,
+ unsigned int id, void *param)
+{
+ struct vpfe_ipipe_rgb2rgb *rgb2rgb = &ipipe->config.rgb2rgb1;
+ struct vpfe_ipipe_rgb2rgb *rgb2rgb_param;
+
+ rgb2rgb_param = (struct vpfe_ipipe_rgb2rgb *)param;
+
+ if (id == IPIPE_RGB2RGB_2)
+ rgb2rgb = &ipipe->config.rgb2rgb2;
+
+ memcpy(rgb2rgb_param, rgb2rgb, sizeof(struct vpfe_ipipe_rgb2rgb));
+
+ return 0;
+}
+
+static int
+ipipe_get_rgb2rgb_1_params(struct vpfe_ipipe_device *ipipe, void *param)
+{
+ return ipipe_get_rgb2rgb_params(ipipe, IPIPE_RGB2RGB_1, param);
+}
+
+static int
+ipipe_get_rgb2rgb_2_params(struct vpfe_ipipe_device *ipipe, void *param)
+{
+ return ipipe_get_rgb2rgb_params(ipipe, IPIPE_RGB2RGB_2, param);
+}
+
+static int
+ipipe_validate_gamma_entry(struct vpfe_ipipe_gamma_entry *table, int size)
+{
+ int i;
+
+ if (!table)
+ return -EINVAL;
+
+ for (i = 0; i < size; i++)
+ if (table[i].slope > GAMMA_MASK ||
+ table[i].offset > GAMMA_MASK)
+ return -EINVAL;
+
+ return 0;
+}
+
+static int
+ipipe_validate_gamma_params(struct vpfe_ipipe_gamma *gamma, struct device *dev)
+{
+ int table_size;
+ int err;
+
+ if (gamma->bypass_r > 1 ||
+ gamma->bypass_b > 1 ||
+ gamma->bypass_g > 1)
+ return -EINVAL;
+
+ if (gamma->tbl_sel != VPFE_IPIPE_GAMMA_TBL_RAM)
+ return 0;
+
+ table_size = gamma->tbl_size;
+ if (!gamma->bypass_r) {
+ err = ipipe_validate_gamma_entry(gamma->table_r, table_size);
+ if (err) {
+ dev_err(dev, "GAMMA R - table entry invalid\n");
+ return err;
+ }
+ }
+
+ if (!gamma->bypass_b) {
+ err = ipipe_validate_gamma_entry(gamma->table_b, table_size);
+ if (err) {
+ dev_err(dev, "GAMMA B - table entry invalid\n");
+ return err;
+ }
+ }
+
+ if (!gamma->bypass_g) {
+ err = ipipe_validate_gamma_entry(gamma->table_g, table_size);
+ if (err) {
+ dev_err(dev, "GAMMA G - table entry invalid\n");
+ return err;
+ }
+ }
+
+ return 0;
+}
+
+static int
+ipipe_set_gamma_params(struct vpfe_ipipe_device *ipipe, void *param)
+{
+ struct vpfe_ipipe_gamma *gamma_param = (struct vpfe_ipipe_gamma *)param;
+ struct vpfe_ipipe_gamma *gamma = &ipipe->config.gamma;
+ struct device *dev = ipipe->subdev.v4l2_dev->dev;
+ int table_size;
+
+ if (!gamma_param) {
+ memset(gamma, 0, sizeof(struct vpfe_ipipe_gamma));
+ gamma->tbl_sel = VPFE_IPIPE_GAMMA_TBL_ROM;
+ goto success;
+ }
+
+ gamma->bypass_r = gamma_param->bypass_r;
+ gamma->bypass_b = gamma_param->bypass_b;
+ gamma->bypass_g = gamma_param->bypass_g;
+ gamma->tbl_sel = gamma_param->tbl_sel;
+ gamma->tbl_size = gamma_param->tbl_size;
+
+ if (ipipe_validate_gamma_params(gamma, dev) < 0)
+ return -EINVAL;
+
+ if (gamma_param->tbl_sel != VPFE_IPIPE_GAMMA_TBL_RAM)
+ goto success;
+
+ table_size = gamma->tbl_size;
+ if (!gamma_param->bypass_r)
+ memcpy(&gamma->table_r, &gamma_param->table_r,
+ (table_size * sizeof(struct vpfe_ipipe_gamma_entry)));
+
+ if (!gamma_param->bypass_b)
+ memcpy(&gamma->table_b, &gamma_param->table_b,
+ (table_size * sizeof(struct vpfe_ipipe_gamma_entry)));
+
+ if (!gamma_param->bypass_g)
+ memcpy(&gamma->table_g, &gamma_param->table_g,
+ (table_size * sizeof(struct vpfe_ipipe_gamma_entry)));
+
+success:
+ ipipe_set_gamma_regs(ipipe->base_addr, ipipe->isp5_base_addr, gamma);
+
+ return 0;
+}
+
+static int ipipe_get_gamma_params(struct vpfe_ipipe_device *ipipe, void *param)
+{
+ struct vpfe_ipipe_gamma *gamma_param = (struct vpfe_ipipe_gamma *)param;
+ struct vpfe_ipipe_gamma *gamma = &ipipe->config.gamma;
+ struct device *dev = ipipe->subdev.v4l2_dev->dev;
+ int table_size;
+
+ gamma_param->bypass_r = gamma->bypass_r;
+ gamma_param->bypass_g = gamma->bypass_g;
+ gamma_param->bypass_b = gamma->bypass_b;
+ gamma_param->tbl_sel = gamma->tbl_sel;
+ gamma_param->tbl_size = gamma->tbl_size;
+
+ if (gamma->tbl_sel != VPFE_IPIPE_GAMMA_TBL_RAM)
+ return 0;
+
+ table_size = gamma->tbl_size;
+
+ if (!gamma->bypass_r && !gamma_param->table_r) {
+ dev_err(dev,
+ "ipipe_get_gamma_params: table ptr empty for R\n");
+ return -EINVAL;
+ }
+ memcpy(gamma_param->table_r, gamma->table_r,
+ (table_size * sizeof(struct vpfe_ipipe_gamma_entry)));
+
+ if (!gamma->bypass_g && !gamma_param->table_g) {
+ dev_err(dev, "ipipe_get_gamma_params: table ptr empty for G\n");
+ return -EINVAL;
+ }
+ memcpy(gamma_param->table_g, gamma->table_g,
+ (table_size * sizeof(struct vpfe_ipipe_gamma_entry)));
+
+ if (!gamma->bypass_b && !gamma_param->table_b) {
+ dev_err(dev, "ipipe_get_gamma_params: table ptr empty for B\n");
+ return -EINVAL;
+ }
+ memcpy(gamma_param->table_b, gamma->table_b,
+ (table_size * sizeof(struct vpfe_ipipe_gamma_entry)));
+
+ return 0;
+}
+
+static int ipipe_validate_3d_lut_params(struct vpfe_ipipe_3d_lut *lut)
+{
+ int i;
+
+ if (!lut->en)
+ return 0;
+
+ for (i = 0; i < VPFE_IPIPE_MAX_SIZE_3D_LUT; i++)
+ if (lut->table[i].r > D3_LUT_ENTRY_MASK ||
+ lut->table[i].g > D3_LUT_ENTRY_MASK ||
+ lut->table[i].b > D3_LUT_ENTRY_MASK)
+ return -EINVAL;
+
+ return 0;
+}
+
+static int ipipe_get_3d_lut_params(struct vpfe_ipipe_device *ipipe, void *param)
+{
+ struct vpfe_ipipe_3d_lut *lut_param = (struct vpfe_ipipe_3d_lut *)param;
+ struct vpfe_ipipe_3d_lut *lut = &ipipe->config.lut;
+ struct device *dev = ipipe->subdev.v4l2_dev->dev;
+
+ lut_param->en = lut->en;
+ if (!lut_param->table) {
+ dev_err(dev, "ipipe_get_3d_lut_params: Invalid table ptr\n");
+ return -EINVAL;
+ }
+
+ memcpy(lut_param->table, &lut->table,
+ (VPFE_IPIPE_MAX_SIZE_3D_LUT *
+ sizeof(struct vpfe_ipipe_3d_lut_entry)));
+
+ return 0;
+}
+
+static int
+ipipe_set_3d_lut_params(struct vpfe_ipipe_device *ipipe, void *param)
+{
+ struct vpfe_ipipe_3d_lut *lut_param = (struct vpfe_ipipe_3d_lut *)param;
+ struct vpfe_ipipe_3d_lut *lut = &ipipe->config.lut;
+ struct device *dev = ipipe->subdev.v4l2_dev->dev;
+
+ if (!lut_param) {
+ memset(lut, 0, sizeof(struct vpfe_ipipe_3d_lut));
+ goto success;
+ }
+
+ memcpy(lut, lut_param, sizeof(struct vpfe_ipipe_3d_lut));
+ if (ipipe_validate_3d_lut_params(lut) < 0) {
+ dev_err(dev, "Invalid 3D-LUT Params\n");
+ return -EINVAL;
+ }
+
+success:
+ ipipe_set_3d_lut_regs(ipipe->base_addr, ipipe->isp5_base_addr, lut);
+
+ return 0;
+}
+
+static int ipipe_validate_rgb2yuv_params(struct vpfe_ipipe_rgb2yuv *rgb2yuv)
+{
+ if (rgb2yuv->coef_ry.decimal > RGB2YCBCR_COEF_DECI_MASK ||
+ rgb2yuv->coef_ry.integer > RGB2YCBCR_COEF_INT_MASK)
+ return -EINVAL;
+
+ if (rgb2yuv->coef_gy.decimal > RGB2YCBCR_COEF_DECI_MASK ||
+ rgb2yuv->coef_gy.integer > RGB2YCBCR_COEF_INT_MASK)
+ return -EINVAL;
+
+ if (rgb2yuv->coef_by.decimal > RGB2YCBCR_COEF_DECI_MASK ||
+ rgb2yuv->coef_by.integer > RGB2YCBCR_COEF_INT_MASK)
+ return -EINVAL;
+
+ if (rgb2yuv->coef_rcb.decimal > RGB2YCBCR_COEF_DECI_MASK ||
+ rgb2yuv->coef_rcb.integer > RGB2YCBCR_COEF_INT_MASK)
+ return -EINVAL;
+
+ if (rgb2yuv->coef_gcb.decimal > RGB2YCBCR_COEF_DECI_MASK ||
+ rgb2yuv->coef_gcb.integer > RGB2YCBCR_COEF_INT_MASK)
+ return -EINVAL;
+
+ if (rgb2yuv->coef_bcb.decimal > RGB2YCBCR_COEF_DECI_MASK ||
+ rgb2yuv->coef_bcb.integer > RGB2YCBCR_COEF_INT_MASK)
+ return -EINVAL;
+
+ if (rgb2yuv->coef_rcr.decimal > RGB2YCBCR_COEF_DECI_MASK ||
+ rgb2yuv->coef_rcr.integer > RGB2YCBCR_COEF_INT_MASK)
+ return -EINVAL;
+
+ if (rgb2yuv->coef_gcr.decimal > RGB2YCBCR_COEF_DECI_MASK ||
+ rgb2yuv->coef_gcr.integer > RGB2YCBCR_COEF_INT_MASK)
+ return -EINVAL;
+
+ if (rgb2yuv->coef_bcr.decimal > RGB2YCBCR_COEF_DECI_MASK ||
+ rgb2yuv->coef_bcr.integer > RGB2YCBCR_COEF_INT_MASK)
+ return -EINVAL;
+
+ if (rgb2yuv->out_ofst_y > RGB2YCBCR_OFST_MASK ||
+ rgb2yuv->out_ofst_cb > RGB2YCBCR_OFST_MASK ||
+ rgb2yuv->out_ofst_cr > RGB2YCBCR_OFST_MASK)
+ return -EINVAL;
+
+ return 0;
+}
+
+static int
+ipipe_set_rgb2yuv_params(struct vpfe_ipipe_device *ipipe, void *param)
+{
+ struct vpfe_ipipe_rgb2yuv *rgb2yuv = &ipipe->config.rgb2yuv;
+ struct device *dev = ipipe->subdev.v4l2_dev->dev;
+ struct vpfe_ipipe_rgb2yuv *rgb2yuv_param;
+
+ rgb2yuv_param = (struct vpfe_ipipe_rgb2yuv *)param;
+ if (!rgb2yuv_param) {
+ /* Defaults for rgb2yuv conversion */
+ const struct vpfe_ipipe_rgb2yuv rgb2yuv_defaults = {
+ .coef_ry = {0, 0x4d},
+ .coef_gy = {0, 0x96},
+ .coef_by = {0, 0x1d},
+ .coef_rcb = {0xf, 0xd5},
+ .coef_gcb = {0xf, 0xab},
+ .coef_bcb = {0, 0x80},
+ .coef_rcr = {0, 0x80},
+ .coef_gcr = {0xf, 0x95},
+ .coef_bcr = {0xf, 0xeb},
+ .out_ofst_cb = 0x80,
+ .out_ofst_cr = 0x80,
+ };
+ /* Copy defaults for rgb2yuv conversion */
+ memcpy(rgb2yuv, &rgb2yuv_defaults,
+ sizeof(struct vpfe_ipipe_rgb2yuv));
+ goto success;
+ }
+
+ memcpy(rgb2yuv, rgb2yuv_param, sizeof(struct vpfe_ipipe_rgb2yuv));
+ if (ipipe_validate_rgb2yuv_params(rgb2yuv) < 0) {
+ dev_err(dev, "Invalid rgb2yuv params\n");
+ return -EINVAL;
+ }
+
+success:
+ ipipe_set_rgb2ycbcr_regs(ipipe->base_addr, rgb2yuv);
+
+ return 0;
+}
+
+static int
+ipipe_get_rgb2yuv_params(struct vpfe_ipipe_device *ipipe, void *param)
+{
+ struct vpfe_ipipe_rgb2yuv *rgb2yuv = &ipipe->config.rgb2yuv;
+ struct vpfe_ipipe_rgb2yuv *rgb2yuv_param;
+
+ rgb2yuv_param = (struct vpfe_ipipe_rgb2yuv *)param;
+ memcpy(rgb2yuv_param, rgb2yuv, sizeof(struct vpfe_ipipe_rgb2yuv));
+ return 0;
+}
+
+static int ipipe_validate_gbce_params(struct vpfe_ipipe_gbce *gbce)
+{
+ u32 max = GBCE_Y_VAL_MASK;
+ int i;
+
+ if (!gbce->en)
+ return 0;
+
+ if (gbce->type == VPFE_IPIPE_GBCE_GAIN_TBL)
+ max = GBCE_GAIN_VAL_MASK;
+
+ for (i = 0; i < VPFE_IPIPE_MAX_SIZE_GBCE_LUT; i++)
+ if (gbce->table[i] > max)
+ return -EINVAL;
+
+ return 0;
+}
+
+static int ipipe_set_gbce_params(struct vpfe_ipipe_device *ipipe, void *param)
+{
+ struct vpfe_ipipe_gbce *gbce_param = (struct vpfe_ipipe_gbce *)param;
+ struct vpfe_ipipe_gbce *gbce = &ipipe->config.gbce;
+ struct device *dev = ipipe->subdev.v4l2_dev->dev;
+
+ if (!gbce_param) {
+ memset(gbce, 0, sizeof(struct vpfe_ipipe_gbce));
+ } else {
+ memcpy(gbce, gbce_param, sizeof(struct vpfe_ipipe_gbce));
+ if (ipipe_validate_gbce_params(gbce) < 0) {
+ dev_err(dev, "Invalid gbce params\n");
+ return -EINVAL;
+ }
+ }
+
+ ipipe_set_gbce_regs(ipipe->base_addr, ipipe->isp5_base_addr, gbce);
+
+ return 0;
+}
+
+static int ipipe_get_gbce_params(struct vpfe_ipipe_device *ipipe, void *param)
+{
+ struct vpfe_ipipe_gbce *gbce_param = (struct vpfe_ipipe_gbce *)param;
+ struct vpfe_ipipe_gbce *gbce = &ipipe->config.gbce;
+ struct device *dev = ipipe->subdev.v4l2_dev->dev;
+
+ gbce_param->en = gbce->en;
+ gbce_param->type = gbce->type;
+ if (!gbce_param->table) {
+ dev_err(dev, "ipipe_get_gbce_params: Invalid table ptr\n");
+ return -EINVAL;
+ }
+
+ memcpy(gbce_param->table, gbce->table,
+ (VPFE_IPIPE_MAX_SIZE_GBCE_LUT * sizeof(unsigned short)));
+
+ return 0;
+}
+
+static int
+ipipe_validate_yuv422_conv_params(struct vpfe_ipipe_yuv422_conv *yuv422_conv)
+{
+ if (yuv422_conv->en_chrom_lpf > 1)
+ return -EINVAL;
+
+ return 0;
+}
+
+static int
+ipipe_set_yuv422_conv_params(struct vpfe_ipipe_device *ipipe, void *param)
+{
+ struct vpfe_ipipe_yuv422_conv *yuv422_conv = &ipipe->config.yuv422_conv;
+ struct vpfe_ipipe_yuv422_conv *yuv422_conv_param;
+ struct device *dev = ipipe->subdev.v4l2_dev->dev;
+
+ yuv422_conv_param = (struct vpfe_ipipe_yuv422_conv *)param;
+ if (!yuv422_conv_param) {
+ memset(yuv422_conv, 0, sizeof(struct vpfe_ipipe_yuv422_conv));
+ yuv422_conv->chrom_pos = VPFE_IPIPE_YUV422_CHR_POS_COSITE;
+ } else {
+ memcpy(yuv422_conv, yuv422_conv_param,
+ sizeof(struct vpfe_ipipe_yuv422_conv));
+ if (ipipe_validate_yuv422_conv_params(yuv422_conv) < 0) {
+ dev_err(dev, "Invalid yuv422 params\n");
+ return -EINVAL;
+ }
+ }
+
+ ipipe_set_yuv422_conv_regs(ipipe->base_addr, yuv422_conv);
+
+ return 0;
+}
+
+static int
+ipipe_get_yuv422_conv_params(struct vpfe_ipipe_device *ipipe, void *param)
+{
+ struct vpfe_ipipe_yuv422_conv *yuv422_conv = &ipipe->config.yuv422_conv;
+ struct vpfe_ipipe_yuv422_conv *yuv422_conv_param;
+
+ yuv422_conv_param = (struct vpfe_ipipe_yuv422_conv *)param;
+ memcpy(yuv422_conv_param, yuv422_conv,
+ sizeof(struct vpfe_ipipe_yuv422_conv));
+
+ return 0;
+}
+
+static int ipipe_validate_yee_params(struct vpfe_ipipe_yee *yee)
+{
+ int i;
+
+ if (yee->en > 1 ||
+ yee->en_halo_red > 1 ||
+ yee->hpf_shft > YEE_HPF_SHIFT_MASK)
+ return -EINVAL;
+
+ if (yee->hpf_coef_00 > YEE_COEF_MASK ||
+ yee->hpf_coef_01 > YEE_COEF_MASK ||
+ yee->hpf_coef_02 > YEE_COEF_MASK ||
+ yee->hpf_coef_10 > YEE_COEF_MASK ||
+ yee->hpf_coef_11 > YEE_COEF_MASK ||
+ yee->hpf_coef_12 > YEE_COEF_MASK ||
+ yee->hpf_coef_20 > YEE_COEF_MASK ||
+ yee->hpf_coef_21 > YEE_COEF_MASK ||
+ yee->hpf_coef_22 > YEE_COEF_MASK)
+ return -EINVAL;
+
+ if (yee->yee_thr > YEE_THR_MASK ||
+ yee->es_gain > YEE_ES_GAIN_MASK ||
+ yee->es_thr1 > YEE_ES_THR1_MASK ||
+ yee->es_thr2 > YEE_THR_MASK ||
+ yee->es_gain_grad > YEE_THR_MASK ||
+ yee->es_ofst_grad > YEE_THR_MASK)
+ return -EINVAL;
+
+ for (i = 0; i < VPFE_IPIPE_MAX_SIZE_YEE_LUT; i++)
+ if (yee->table[i] > YEE_ENTRY_MASK)
+ return -EINVAL;
+
+ return 0;
+}
+
+static int ipipe_set_yee_params(struct vpfe_ipipe_device *ipipe, void *param)
+{
+ struct vpfe_ipipe_yee *yee_param = (struct vpfe_ipipe_yee *)param;
+ struct device *dev = ipipe->subdev.v4l2_dev->dev;
+ struct vpfe_ipipe_yee *yee = &ipipe->config.yee;
+
+ if (!yee_param) {
+ memset(yee, 0, sizeof(struct vpfe_ipipe_yee));
+ } else {
+ memcpy(yee, yee_param, sizeof(struct vpfe_ipipe_yee));
+ if (ipipe_validate_yee_params(yee) < 0) {
+ dev_err(dev, "Invalid yee params\n");
+ return -EINVAL;
+ }
+ }
+
+ ipipe_set_ee_regs(ipipe->base_addr, ipipe->isp5_base_addr, yee);
+
+ return 0;
+}
+
+static int ipipe_get_yee_params(struct vpfe_ipipe_device *ipipe, void *param)
+{
+ struct vpfe_ipipe_yee *yee_param = (struct vpfe_ipipe_yee *)param;
+ struct vpfe_ipipe_yee *yee = &ipipe->config.yee;
+
+ yee_param->en = yee->en;
+ yee_param->en_halo_red = yee->en_halo_red;
+ yee_param->merge_meth = yee->merge_meth;
+ yee_param->hpf_shft = yee->hpf_shft;
+ yee_param->hpf_coef_00 = yee->hpf_coef_00;
+ yee_param->hpf_coef_01 = yee->hpf_coef_01;
+ yee_param->hpf_coef_02 = yee->hpf_coef_02;
+ yee_param->hpf_coef_10 = yee->hpf_coef_10;
+ yee_param->hpf_coef_11 = yee->hpf_coef_11;
+ yee_param->hpf_coef_12 = yee->hpf_coef_12;
+ yee_param->hpf_coef_20 = yee->hpf_coef_20;
+ yee_param->hpf_coef_21 = yee->hpf_coef_21;
+ yee_param->hpf_coef_22 = yee->hpf_coef_22;
+ yee_param->yee_thr = yee->yee_thr;
+ yee_param->es_gain = yee->es_gain;
+ yee_param->es_thr1 = yee->es_thr1;
+ yee_param->es_thr2 = yee->es_thr2;
+ yee_param->es_gain_grad = yee->es_gain_grad;
+ yee_param->es_ofst_grad = yee->es_ofst_grad;
+ memcpy(yee_param->table, &yee->table,
+ (VPFE_IPIPE_MAX_SIZE_YEE_LUT * sizeof(short)));
+
+ return 0;
+}
+
+static int ipipe_validate_car_params(struct vpfe_ipipe_car *car)
+{
+ if (car->en > 1 || car->hpf_shft > CAR_HPF_SHIFT_MASK ||
+ car->gain1.shft > CAR_GAIN1_SHFT_MASK ||
+ car->gain1.gain_min > CAR_GAIN_MIN_MASK ||
+ car->gain2.shft > CAR_GAIN2_SHFT_MASK ||
+ car->gain2.gain_min > CAR_GAIN_MIN_MASK)
+ return -EINVAL;
+
+ return 0;
+}
+
+static int ipipe_set_car_params(struct vpfe_ipipe_device *ipipe, void *param)
+{
+ struct vpfe_ipipe_car *car_param = (struct vpfe_ipipe_car *)param;
+ struct device *dev = ipipe->subdev.v4l2_dev->dev;
+ struct vpfe_ipipe_car *car = &ipipe->config.car;
+
+ if (!car_param) {
+ memset(car, 0, sizeof(struct vpfe_ipipe_car));
+ } else {
+ memcpy(car, car_param, sizeof(struct vpfe_ipipe_car));
+ if (ipipe_validate_car_params(car) < 0) {
+ dev_err(dev, "Invalid car params\n");
+ return -EINVAL;
+ }
+ }
+
+ ipipe_set_car_regs(ipipe->base_addr, car);
+
+ return 0;
+}
+
+static int ipipe_get_car_params(struct vpfe_ipipe_device *ipipe, void *param)
+{
+ struct vpfe_ipipe_car *car_param = (struct vpfe_ipipe_car *)param;
+ struct vpfe_ipipe_car *car = &ipipe->config.car;
+
+ memcpy(car_param, car, sizeof(struct vpfe_ipipe_car));
+ return 0;
+}
+
+static int ipipe_validate_cgs_params(struct vpfe_ipipe_cgs *cgs)
+{
+ if (cgs->en > 1 || cgs->h_shft > CAR_SHIFT_MASK)
+ return -EINVAL;
+
+ return 0;
+}
+
+static int ipipe_set_cgs_params(struct vpfe_ipipe_device *ipipe, void *param)
+{
+ struct vpfe_ipipe_cgs *cgs_param = (struct vpfe_ipipe_cgs *)param;
+ struct device *dev = ipipe->subdev.v4l2_dev->dev;
+ struct vpfe_ipipe_cgs *cgs = &ipipe->config.cgs;
+
+ if (!cgs_param) {
+ memset(cgs, 0, sizeof(struct vpfe_ipipe_cgs));
+ } else {
+ memcpy(cgs, cgs_param, sizeof(struct vpfe_ipipe_cgs));
+ if (ipipe_validate_cgs_params(cgs) < 0) {
+ dev_err(dev, "Invalid cgs params\n");
+ return -EINVAL;
+ }
+ }
+
+ ipipe_set_cgs_regs(ipipe->base_addr, cgs);
+
+ return 0;
+}
+
+static int ipipe_get_cgs_params(struct vpfe_ipipe_device *ipipe, void *param)
+{
+ struct vpfe_ipipe_cgs *cgs_param = (struct vpfe_ipipe_cgs *)param;
+ struct vpfe_ipipe_cgs *cgs = &ipipe->config.cgs;
+
+ memcpy(cgs_param, cgs, sizeof(struct vpfe_ipipe_cgs));
+
+ return 0;
+}
+
+static const struct ipipe_module_if ipipe_modules[VPFE_IPIPE_MAX_MODULES] = {
+ /* VPFE_IPIPE_INPUT_CONFIG */ {
+ offsetof(struct ipipe_module_params, input_config),
+ FIELD_SIZEOF(struct ipipe_module_params, input_config),
+ offsetof(struct vpfe_ipipe_config, input_config),
+ ipipe_set_input_config,
+ ipipe_get_input_config,
+ }, /* VPFE_IPIPE_LUTDPC */ {
+ offsetof(struct ipipe_module_params, lutdpc),
+ FIELD_SIZEOF(struct ipipe_module_params, lutdpc),
+ offsetof(struct vpfe_ipipe_config, lutdpc),
+ ipipe_set_lutdpc_params,
+ ipipe_get_lutdpc_params,
+ }, /* VPFE_IPIPE_OTFDPC */ {
+ offsetof(struct ipipe_module_params, otfdpc),
+ FIELD_SIZEOF(struct ipipe_module_params, otfdpc),
+ offsetof(struct vpfe_ipipe_config, otfdpc),
+ ipipe_set_otfdpc_params,
+ ipipe_get_otfdpc_params,
+ }, /* VPFE_IPIPE_NF1 */ {
+ offsetof(struct ipipe_module_params, nf1),
+ FIELD_SIZEOF(struct ipipe_module_params, nf1),
+ offsetof(struct vpfe_ipipe_config, nf1),
+ ipipe_set_nf1_params,
+ ipipe_get_nf1_params,
+ }, /* VPFE_IPIPE_NF2 */ {
+ offsetof(struct ipipe_module_params, nf2),
+ FIELD_SIZEOF(struct ipipe_module_params, nf2),
+ offsetof(struct vpfe_ipipe_config, nf2),
+ ipipe_set_nf2_params,
+ ipipe_get_nf2_params,
+ }, /* VPFE_IPIPE_WB */ {
+ offsetof(struct ipipe_module_params, wbal),
+ FIELD_SIZEOF(struct ipipe_module_params, wbal),
+ offsetof(struct vpfe_ipipe_config, wbal),
+ ipipe_set_wb_params,
+ ipipe_get_wb_params,
+ }, /* VPFE_IPIPE_RGB2RGB_1 */ {
+ offsetof(struct ipipe_module_params, rgb2rgb1),
+ FIELD_SIZEOF(struct ipipe_module_params, rgb2rgb1),
+ offsetof(struct vpfe_ipipe_config, rgb2rgb1),
+ ipipe_set_rgb2rgb_1_params,
+ ipipe_get_rgb2rgb_1_params,
+ }, /* VPFE_IPIPE_RGB2RGB_2 */ {
+ offsetof(struct ipipe_module_params, rgb2rgb2),
+ FIELD_SIZEOF(struct ipipe_module_params, rgb2rgb2),
+ offsetof(struct vpfe_ipipe_config, rgb2rgb2),
+ ipipe_set_rgb2rgb_2_params,
+ ipipe_get_rgb2rgb_2_params,
+ }, /* VPFE_IPIPE_GAMMA */ {
+ offsetof(struct ipipe_module_params, gamma),
+ FIELD_SIZEOF(struct ipipe_module_params, gamma),
+ offsetof(struct vpfe_ipipe_config, gamma),
+ ipipe_set_gamma_params,
+ ipipe_get_gamma_params,
+ }, /* VPFE_IPIPE_3D_LUT */ {
+ offsetof(struct ipipe_module_params, lut),
+ FIELD_SIZEOF(struct ipipe_module_params, lut),
+ offsetof(struct vpfe_ipipe_config, lut),
+ ipipe_set_3d_lut_params,
+ ipipe_get_3d_lut_params,
+ }, /* VPFE_IPIPE_RGB2YUV */ {
+ offsetof(struct ipipe_module_params, rgb2yuv),
+ FIELD_SIZEOF(struct ipipe_module_params, rgb2yuv),
+ offsetof(struct vpfe_ipipe_config, rgb2yuv),
+ ipipe_set_rgb2yuv_params,
+ ipipe_get_rgb2yuv_params,
+ }, /* VPFE_IPIPE_YUV422_CONV */ {
+ offsetof(struct ipipe_module_params, yuv422_conv),
+ FIELD_SIZEOF(struct ipipe_module_params, yuv422_conv),
+ offsetof(struct vpfe_ipipe_config, yuv422_conv),
+ ipipe_set_yuv422_conv_params,
+ ipipe_get_yuv422_conv_params,
+ }, /* VPFE_IPIPE_YEE */ {
+ offsetof(struct ipipe_module_params, yee),
+ FIELD_SIZEOF(struct ipipe_module_params, yee),
+ offsetof(struct vpfe_ipipe_config, yee),
+ ipipe_set_yee_params,
+ ipipe_get_yee_params,
+ }, /* VPFE_IPIPE_GIC */ {
+ offsetof(struct ipipe_module_params, gic),
+ FIELD_SIZEOF(struct ipipe_module_params, gic),
+ offsetof(struct vpfe_ipipe_config, gic),
+ ipipe_set_gic_params,
+ ipipe_get_gic_params,
+ }, /* VPFE_IPIPE_CFA */ {
+ offsetof(struct ipipe_module_params, cfa),
+ FIELD_SIZEOF(struct ipipe_module_params, cfa),
+ offsetof(struct vpfe_ipipe_config, cfa),
+ ipipe_set_cfa_params,
+ ipipe_get_cfa_params,
+ }, /* VPFE_IPIPE_CAR */ {
+ offsetof(struct ipipe_module_params, car),
+ FIELD_SIZEOF(struct ipipe_module_params, car),
+ offsetof(struct vpfe_ipipe_config, car),
+ ipipe_set_car_params,
+ ipipe_get_car_params,
+ }, /* VPFE_IPIPE_CGS */ {
+ offsetof(struct ipipe_module_params, cgs),
+ FIELD_SIZEOF(struct ipipe_module_params, cgs),
+ offsetof(struct vpfe_ipipe_config, cgs),
+ ipipe_set_cgs_params,
+ ipipe_get_cgs_params,
+ }, /* VPFE_IPIPE_GBCE */ {
+ offsetof(struct ipipe_module_params, gbce),
+ FIELD_SIZEOF(struct ipipe_module_params, gbce),
+ offsetof(struct vpfe_ipipe_config, gbce),
+ ipipe_set_gbce_params,
+ ipipe_get_gbce_params,
+ },
+};
+
+static int ipipe_s_config(struct v4l2_subdev *sd, struct vpfe_ipipe_config *cfg)
+{
+ struct vpfe_ipipe_device *ipipe = v4l2_get_subdevdata(sd);
+ unsigned int i;
+ int rval = 0;
+
+ for (i = 0; i < ARRAY_SIZE(ipipe_modules); i++) {
+ unsigned int bit = 1 << i;
+
+ if (cfg->flag & bit) {
+ const struct ipipe_module_if *module_if =
+ &ipipe_modules[i];
+ struct ipipe_module_params *params;
+ void __user *from = *(void * __user *)
+ ((void *)cfg + module_if->config_offset);
+ size_t size;
+ void *to;
+
+ params = kmalloc(sizeof(struct ipipe_module_params),
+ GFP_KERNEL);
+ to = (void *)params + module_if->param_offset;
+ size = module_if->param_size;
+
+ if (to && from && size) {
+ if (copy_from_user(to, from, size)) {
+ rval = -EFAULT;
+ break;
+ }
+ rval = module_if->set(ipipe, to);
+ if (rval)
+ goto error;
+ } else if (to && !from && size) {
+ rval = module_if->set(ipipe, NULL);
+ if (rval)
+ goto error;
+ }
+ kfree(params);
+ }
+ }
+error:
+ return rval;
+}
+
+static int ipipe_g_config(struct v4l2_subdev *sd, struct vpfe_ipipe_config *cfg)
+{
+ struct vpfe_ipipe_device *ipipe = v4l2_get_subdevdata(sd);
+ unsigned int i;
+ int rval = 0;
+
+ for (i = 1; i < ARRAY_SIZE(ipipe_modules); i++) {
+ unsigned int bit = 1 << i;
+
+ if (cfg->flag & bit) {
+ const struct ipipe_module_if *module_if =
+ &ipipe_modules[i];
+ struct ipipe_module_params *params;
+ void __user *to = *(void * __user *)
+ ((void *)cfg + module_if->config_offset);
+ size_t size;
+ void *from;
+
+ params = kmalloc(sizeof(struct ipipe_module_params),
+ GFP_KERNEL);
+ from = (void *)params + module_if->param_offset;
+ size = module_if->param_size;
+
+ if (to && from && size) {
+ rval = module_if->get(ipipe, from);
+ if (rval)
+ goto error;
+ if (copy_to_user(to, from, size)) {
+ rval = -EFAULT;
+ break;
+ }
+ }
+ kfree(params);
+ }
+ }
+error:
+ return rval;
+}
+
+/*
+ * ipipe_ioctl() - Handle ipipe module private ioctl's
+ * @sd: pointer to v4l2 subdev structure
+ * @cmd: configuration command
+ * @arg: configuration argument
+ */
+static long ipipe_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg)
+{
+ int ret = 0;
+
+ switch (cmd) {
+ case VIDIOC_VPFE_IPIPE_S_CONFIG:
+ ret = ipipe_s_config(sd, arg);
+ break;
+
+ case VIDIOC_VPFE_IPIPE_G_CONFIG:
+ ret = ipipe_g_config(sd, arg);
+ break;
+
+ default:
+ ret = -ENOIOCTLCMD;
+ }
+ return ret;
+}
+
+void vpfe_ipipe_enable(struct vpfe_device *vpfe_dev, int en)
+{
+ struct vpfe_ipipeif_device *ipipeif = &vpfe_dev->vpfe_ipipeif;
+ struct vpfe_ipipe_device *ipipe = &vpfe_dev->vpfe_ipipe;
+ unsigned char val;
+
+ if (ipipe->input == IPIPE_INPUT_NONE)
+ return;
+
+ /* ipipe is set to single shot */
+ if (ipipeif->input == IPIPEIF_INPUT_MEMORY && en) {
+ /* for single-shot mode, need to wait for h/w to
+ * reset many register bits
+ */
+ do {
+ val = regr_ip(vpfe_dev->vpfe_ipipe.base_addr,
+ IPIPE_SRC_EN);
+ } while (val);
+ }
+ regw_ip(vpfe_dev->vpfe_ipipe.base_addr, en, IPIPE_SRC_EN);
+}
+
+/*
+ * ipipe_set_stream() - Enable/Disable streaming on the ipipe subdevice
+ * @sd: pointer to v4l2 subdev structure
+ * @enable: 1 == Enable, 0 == Disable
+ */
+static int ipipe_set_stream(struct v4l2_subdev *sd, int enable)
+{
+ struct vpfe_ipipe_device *ipipe = v4l2_get_subdevdata(sd);
+ struct vpfe_device *vpfe_dev = to_vpfe_device(ipipe);
+
+ if (enable && ipipe->input != IPIPE_INPUT_NONE &&
+ ipipe->output != IPIPE_OUTPUT_NONE) {
+ if (config_ipipe_hw(ipipe) < 0)
+ return -EINVAL;
+ }
+
+ vpfe_ipipe_enable(vpfe_dev, enable);
+
+ return 0;
+}
+
+/*
+ * __ipipe_get_format() - helper function for getting ipipe format
+ * @ipipe: pointer to ipipe private structure.
+ * @pad: pad number.
+ * @cfg: V4L2 subdev pad config
+ * @which: wanted subdev format.
+ *
+ */
+static struct v4l2_mbus_framefmt *
+__ipipe_get_format(struct vpfe_ipipe_device *ipipe,
+ struct v4l2_subdev_pad_config *cfg, unsigned int pad,
+ enum v4l2_subdev_format_whence which)
+{
+ if (which == V4L2_SUBDEV_FORMAT_TRY)
+ return v4l2_subdev_get_try_format(&ipipe->subdev, cfg, pad);
+
+ return &ipipe->formats[pad];
+}
+
+/*
+ * ipipe_try_format() - Handle try format by pad subdev method
+ * @ipipe: VPFE ipipe device.
+ * @cfg: V4L2 subdev pad config
+ * @pad: pad num.
+ * @fmt: pointer to v4l2 format structure.
+ * @which : wanted subdev format
+ */
+static void
+ipipe_try_format(struct vpfe_ipipe_device *ipipe,
+ struct v4l2_subdev_pad_config *cfg, unsigned int pad,
+ struct v4l2_mbus_framefmt *fmt,
+ enum v4l2_subdev_format_whence which)
+{
+ unsigned int max_out_height;
+ unsigned int max_out_width;
+ unsigned int i;
+
+ max_out_width = IPIPE_MAX_OUTPUT_WIDTH_A;
+ max_out_height = IPIPE_MAX_OUTPUT_HEIGHT_A;
+
+ if (pad == IPIPE_PAD_SINK) {
+ for (i = 0; i < ARRAY_SIZE(ipipe_input_fmts); i++)
+ if (fmt->code == ipipe_input_fmts[i])
+ break;
+
+ /* If not found, use SBGGR10 as default */
+ if (i >= ARRAY_SIZE(ipipe_input_fmts))
+ fmt->code = MEDIA_BUS_FMT_SGRBG12_1X12;
+ } else if (pad == IPIPE_PAD_SOURCE) {
+ for (i = 0; i < ARRAY_SIZE(ipipe_output_fmts); i++)
+ if (fmt->code == ipipe_output_fmts[i])
+ break;
+
+ /* If not found, use UYVY as default */
+ if (i >= ARRAY_SIZE(ipipe_output_fmts))
+ fmt->code = MEDIA_BUS_FMT_UYVY8_2X8;
+ }
+
+ fmt->width = clamp_t(u32, fmt->width, MIN_OUT_HEIGHT, max_out_width);
+ fmt->height = clamp_t(u32, fmt->height, MIN_OUT_WIDTH, max_out_height);
+}
+
+/*
+ * ipipe_set_format() - Handle set format by pads subdev method
+ * @sd: pointer to v4l2 subdev structure
+ * @cfg: V4L2 subdev pad config
+ * @fmt: pointer to v4l2 subdev format structure
+ * return -EINVAL or zero on success
+ */
+static int
+ipipe_set_format(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_format *fmt)
+{
+ struct vpfe_ipipe_device *ipipe = v4l2_get_subdevdata(sd);
+ struct v4l2_mbus_framefmt *format;
+
+ format = __ipipe_get_format(ipipe, cfg, fmt->pad, fmt->which);
+ if (format == NULL)
+ return -EINVAL;
+
+ ipipe_try_format(ipipe, cfg, fmt->pad, &fmt->format, fmt->which);
+ *format = fmt->format;
+
+ if (fmt->which == V4L2_SUBDEV_FORMAT_TRY)
+ return 0;
+
+ if (fmt->pad == IPIPE_PAD_SINK &&
+ (ipipe->input == IPIPE_INPUT_CCDC ||
+ ipipe->input == IPIPE_INPUT_MEMORY))
+ ipipe->formats[fmt->pad] = fmt->format;
+ else if (fmt->pad == IPIPE_PAD_SOURCE &&
+ ipipe->output == IPIPE_OUTPUT_RESIZER)
+ ipipe->formats[fmt->pad] = fmt->format;
+ else
+ return -EINVAL;
+
+ return 0;
+}
+
+/*
+ * ipipe_get_format() - Handle get format by pads subdev method.
+ * @sd: pointer to v4l2 subdev structure.
+ * @cfg: V4L2 subdev pad config
+ * @fmt: pointer to v4l2 subdev format structure.
+ */
+static int
+ipipe_get_format(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_format *fmt)
+{
+ struct vpfe_ipipe_device *ipipe = v4l2_get_subdevdata(sd);
+
+ if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE)
+ fmt->format = ipipe->formats[fmt->pad];
+ else
+ fmt->format = *(v4l2_subdev_get_try_format(sd, cfg, fmt->pad));
+
+ return 0;
+}
+
+/*
+ * ipipe_enum_frame_size() - enum frame sizes on pads
+ * @sd: pointer to v4l2 subdev structure.
+ * @cfg: V4L2 subdev pad config
+ * @fse: pointer to v4l2_subdev_frame_size_enum structure.
+ */
+static int
+ipipe_enum_frame_size(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_frame_size_enum *fse)
+{
+ struct vpfe_ipipe_device *ipipe = v4l2_get_subdevdata(sd);
+ struct v4l2_mbus_framefmt format;
+
+ if (fse->index != 0)
+ return -EINVAL;
+
+ format.code = fse->code;
+ format.width = 1;
+ format.height = 1;
+ ipipe_try_format(ipipe, cfg, fse->pad, &format, fse->which);
+ fse->min_width = format.width;
+ fse->min_height = format.height;
+
+ if (format.code != fse->code)
+ return -EINVAL;
+
+ format.code = fse->code;
+ format.width = -1;
+ format.height = -1;
+ ipipe_try_format(ipipe, cfg, fse->pad, &format, fse->which);
+ fse->max_width = format.width;
+ fse->max_height = format.height;
+
+ return 0;
+}
+
+/*
+ * ipipe_enum_mbus_code() - enum mbus codes for pads
+ * @sd: pointer to v4l2 subdev structure.
+ * @cfg: V4L2 subdev pad config
+ * @code: pointer to v4l2_subdev_mbus_code_enum structure
+ */
+static int
+ipipe_enum_mbus_code(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_mbus_code_enum *code)
+{
+ switch (code->pad) {
+ case IPIPE_PAD_SINK:
+ if (code->index >= ARRAY_SIZE(ipipe_input_fmts))
+ return -EINVAL;
+ code->code = ipipe_input_fmts[code->index];
+ break;
+
+ case IPIPE_PAD_SOURCE:
+ if (code->index >= ARRAY_SIZE(ipipe_output_fmts))
+ return -EINVAL;
+ code->code = ipipe_output_fmts[code->index];
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/*
+ * ipipe_s_ctrl() - Handle set control subdev method
+ * @ctrl: pointer to v4l2 control structure
+ */
+static int ipipe_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct vpfe_ipipe_device *ipipe =
+ container_of(ctrl->handler, struct vpfe_ipipe_device, ctrls);
+ struct ipipe_lum_adj *lum_adj = &ipipe->config.lum_adj;
+
+ switch (ctrl->id) {
+ case V4L2_CID_BRIGHTNESS:
+ lum_adj->brightness = ctrl->val;
+ ipipe_set_lum_adj_regs(ipipe->base_addr, lum_adj);
+ break;
+
+ case V4L2_CID_CONTRAST:
+ lum_adj->contrast = ctrl->val;
+ ipipe_set_lum_adj_regs(ipipe->base_addr, lum_adj);
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/*
+ * ipipe_init_formats() - Initialize formats on all pads
+ * @sd: pointer to v4l2 subdev structure.
+ * @fh: V4L2 subdev file handle
+ *
+ * Initialize all pad formats with default values. Try formats are initialized
+ * on the file handle.
+ */
+static int
+ipipe_init_formats(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
+{
+ struct v4l2_subdev_format format;
+
+ memset(&format, 0, sizeof(format));
+ format.pad = IPIPE_PAD_SINK;
+ format.which = V4L2_SUBDEV_FORMAT_TRY;
+ format.format.code = MEDIA_BUS_FMT_SGRBG12_1X12;
+ format.format.width = IPIPE_MAX_OUTPUT_WIDTH_A;
+ format.format.height = IPIPE_MAX_OUTPUT_HEIGHT_A;
+ ipipe_set_format(sd, fh->pad, &format);
+
+ memset(&format, 0, sizeof(format));
+ format.pad = IPIPE_PAD_SOURCE;
+ format.which = V4L2_SUBDEV_FORMAT_TRY;
+ format.format.code = MEDIA_BUS_FMT_UYVY8_2X8;
+ format.format.width = IPIPE_MAX_OUTPUT_WIDTH_A;
+ format.format.height = IPIPE_MAX_OUTPUT_HEIGHT_A;
+ ipipe_set_format(sd, fh->pad, &format);
+
+ return 0;
+}
+
+/* subdev core operations */
+static const struct v4l2_subdev_core_ops ipipe_v4l2_core_ops = {
+ .ioctl = ipipe_ioctl,
+};
+
+static const struct v4l2_ctrl_ops ipipe_ctrl_ops = {
+ .s_ctrl = ipipe_s_ctrl,
+};
+
+/* subdev file operations */
+static const struct v4l2_subdev_internal_ops ipipe_v4l2_internal_ops = {
+ .open = ipipe_init_formats,
+};
+
+/* subdev video operations */
+static const struct v4l2_subdev_video_ops ipipe_v4l2_video_ops = {
+ .s_stream = ipipe_set_stream,
+};
+
+/* subdev pad operations */
+static const struct v4l2_subdev_pad_ops ipipe_v4l2_pad_ops = {
+ .enum_mbus_code = ipipe_enum_mbus_code,
+ .enum_frame_size = ipipe_enum_frame_size,
+ .get_fmt = ipipe_get_format,
+ .set_fmt = ipipe_set_format,
+};
+
+/* v4l2 subdev operation */
+static const struct v4l2_subdev_ops ipipe_v4l2_ops = {
+ .core = &ipipe_v4l2_core_ops,
+ .video = &ipipe_v4l2_video_ops,
+ .pad = &ipipe_v4l2_pad_ops,
+};
+
+/*
+ * Media entity operations
+ */
+
+/*
+ * ipipe_link_setup() - Setup ipipe connections
+ * @entity: ipipe media entity
+ * @local: Pad at the local end of the link
+ * @remote: Pad at the remote end of the link
+ * @flags: Link flags
+ *
+ * return -EINVAL or zero on success
+ */
+static int
+ipipe_link_setup(struct media_entity *entity, const struct media_pad *local,
+ const struct media_pad *remote, u32 flags)
+{
+ struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity);
+ struct vpfe_ipipe_device *ipipe = v4l2_get_subdevdata(sd);
+ struct vpfe_device *vpfe_dev = to_vpfe_device(ipipe);
+ u16 ipipeif_sink = vpfe_dev->vpfe_ipipeif.input;
+
+ switch (local->index | media_entity_type(remote->entity)) {
+ case IPIPE_PAD_SINK | MEDIA_ENT_T_V4L2_SUBDEV:
+ if (!(flags & MEDIA_LNK_FL_ENABLED)) {
+ ipipe->input = IPIPE_INPUT_NONE;
+ break;
+ }
+ if (ipipe->input != IPIPE_INPUT_NONE)
+ return -EBUSY;
+ if (ipipeif_sink == IPIPEIF_INPUT_MEMORY)
+ ipipe->input = IPIPE_INPUT_MEMORY;
+ else
+ ipipe->input = IPIPE_INPUT_CCDC;
+ break;
+
+ case IPIPE_PAD_SOURCE | MEDIA_ENT_T_V4L2_SUBDEV:
+ /* out to RESIZER */
+ if (flags & MEDIA_LNK_FL_ENABLED)
+ ipipe->output = IPIPE_OUTPUT_RESIZER;
+ else
+ ipipe->output = IPIPE_OUTPUT_NONE;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static const struct media_entity_operations ipipe_media_ops = {
+ .link_setup = ipipe_link_setup,
+};
+
+/*
+ * vpfe_ipipe_unregister_entities() - ipipe unregister entity
+ * @vpfe_ipipe: pointer to ipipe subdevice structure.
+ */
+void vpfe_ipipe_unregister_entities(struct vpfe_ipipe_device *vpfe_ipipe)
+{
+ /* unregister subdev */
+ v4l2_device_unregister_subdev(&vpfe_ipipe->subdev);
+ /* cleanup entity */
+ media_entity_cleanup(&vpfe_ipipe->subdev.entity);
+}
+
+/*
+ * vpfe_ipipe_register_entities() - ipipe register entity
+ * @ipipe: pointer to ipipe subdevice structure.
+ * @vdev: pointer to v4l2 device structure.
+ */
+int
+vpfe_ipipe_register_entities(struct vpfe_ipipe_device *ipipe,
+ struct v4l2_device *vdev)
+{
+ int ret;
+
+ /* Register the subdev */
+ ret = v4l2_device_register_subdev(vdev, &ipipe->subdev);
+ if (ret) {
+ pr_err("Failed to register ipipe as v4l2 subdevice\n");
+ return ret;
+ }
+
+ return ret;
+}
+
+#define IPIPE_CONTRAST_HIGH 0xff
+#define IPIPE_BRIGHT_HIGH 0xff
+
+/*
+ * vpfe_ipipe_init() - ipipe module initialization.
+ * @ipipe: pointer to ipipe subdevice structure.
+ * @pdev: platform device pointer.
+ */
+int
+vpfe_ipipe_init(struct vpfe_ipipe_device *ipipe, struct platform_device *pdev)
+{
+ struct media_pad *pads = &ipipe->pads[0];
+ struct v4l2_subdev *sd = &ipipe->subdev;
+ struct media_entity *me = &sd->entity;
+ static resource_size_t res_len;
+ struct resource *res;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 4);
+ if (!res)
+ return -ENOENT;
+
+ res_len = resource_size(res);
+ res = request_mem_region(res->start, res_len, res->name);
+ if (!res)
+ return -EBUSY;
+ ipipe->base_addr = ioremap_nocache(res->start, res_len);
+ if (!ipipe->base_addr)
+ return -EBUSY;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 6);
+ if (!res)
+ return -ENOENT;
+ ipipe->isp5_base_addr = ioremap_nocache(res->start, res_len);
+ if (!ipipe->isp5_base_addr)
+ return -EBUSY;
+
+ v4l2_subdev_init(sd, &ipipe_v4l2_ops);
+ sd->internal_ops = &ipipe_v4l2_internal_ops;
+ strlcpy(sd->name, "DAVINCI IPIPE", sizeof(sd->name));
+ sd->grp_id = 1 << 16; /* group ID for davinci subdevs */
+ v4l2_set_subdevdata(sd, ipipe);
+ sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+
+ pads[IPIPE_PAD_SINK].flags = MEDIA_PAD_FL_SINK;
+ pads[IPIPE_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE;
+
+ ipipe->input = IPIPE_INPUT_NONE;
+ ipipe->output = IPIPE_OUTPUT_NONE;
+
+ me->ops = &ipipe_media_ops;
+ v4l2_ctrl_handler_init(&ipipe->ctrls, 2);
+ v4l2_ctrl_new_std(&ipipe->ctrls, &ipipe_ctrl_ops,
+ V4L2_CID_BRIGHTNESS, 0,
+ IPIPE_BRIGHT_HIGH, 1, 16);
+ v4l2_ctrl_new_std(&ipipe->ctrls, &ipipe_ctrl_ops,
+ V4L2_CID_CONTRAST, 0,
+ IPIPE_CONTRAST_HIGH, 1, 16);
+
+
+ v4l2_ctrl_handler_setup(&ipipe->ctrls);
+ sd->ctrl_handler = &ipipe->ctrls;
+
+ return media_entity_init(me, IPIPE_PADS_NUM, pads, 0);
+}
+
+/*
+ * vpfe_ipipe_cleanup() - ipipe subdevice cleanup.
+ * @ipipe: pointer to ipipe subdevice
+ * @dev: pointer to platform device
+ */
+void vpfe_ipipe_cleanup(struct vpfe_ipipe_device *ipipe,
+ struct platform_device *pdev)
+{
+ struct resource *res;
+
+ v4l2_ctrl_handler_free(&ipipe->ctrls);
+
+ iounmap(ipipe->base_addr);
+ iounmap(ipipe->isp5_base_addr);
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 4);
+ if (res)
+ release_mem_region(res->start, resource_size(res));
+}
diff --git a/drivers/staging/media/davinci_vpfe/dm365_ipipe.h b/drivers/staging/media/davinci_vpfe/dm365_ipipe.h
new file mode 100644
index 000000000..d81b29e19
--- /dev/null
+++ b/drivers/staging/media/davinci_vpfe/dm365_ipipe.h
@@ -0,0 +1,179 @@
+/*
+ * Copyright (C) 2012 Texas Instruments 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 version 2.
+ *
+ * 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
+ *
+ * Contributors:
+ * Manjunath Hadli <manjunath.hadli@ti.com>
+ * Prabhakar Lad <prabhakar.lad@ti.com>
+ */
+
+#ifndef _DAVINCI_VPFE_DM365_IPIPE_H
+#define _DAVINCI_VPFE_DM365_IPIPE_H
+
+#include <linux/platform_device.h>
+
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-subdev.h>
+
+#include "davinci_vpfe_user.h"
+#include "vpfe_video.h"
+
+#define CEIL(a, b) (((a) + (b-1)) / (b))
+
+enum ipipe_noise_filter {
+ IPIPE_D2F_1ST = 0,
+ IPIPE_D2F_2ND = 1,
+};
+
+/* Used for driver storage */
+struct ipipe_otfdpc_2_0 {
+ /* 0 - disable, 1 - enable */
+ unsigned char en;
+ /* defect detection method */
+ enum vpfe_ipipe_otfdpc_det_meth det_method;
+ /* Algorithm used. Applicable only when IPIPE_DPC_OTF_MIN_MAX2 is
+ * used
+ */
+ enum vpfe_ipipe_otfdpc_alg alg;
+ struct vpfe_ipipe_otfdpc_2_0_cfg otfdpc_2_0;
+};
+
+struct ipipe_otfdpc_3_0 {
+ /* 0 - disable, 1 - enable */
+ unsigned char en;
+ /* defect detection method */
+ enum vpfe_ipipe_otfdpc_det_meth det_method;
+ /* Algorithm used. Applicable only when IPIPE_DPC_OTF_MIN_MAX2 is
+ * used
+ */
+ enum vpfe_ipipe_otfdpc_alg alg;
+ struct vpfe_ipipe_otfdpc_3_0_cfg otfdpc_3_0;
+};
+
+/* Structure for configuring Luminance Adjustment module */
+struct ipipe_lum_adj {
+ /* Brightness adjustments */
+ unsigned char brightness;
+ /* contrast adjustments */
+ unsigned char contrast;
+};
+
+enum ipipe_rgb2rgb {
+ IPIPE_RGB2RGB_1 = 0,
+ IPIPE_RGB2RGB_2 = 1,
+};
+
+struct ipipe_module_params {
+ __u32 flag;
+ struct vpfe_ipipe_input_config input_config;
+ struct vpfe_ipipe_lutdpc lutdpc;
+ struct vpfe_ipipe_otfdpc otfdpc;
+ struct vpfe_ipipe_nf nf1;
+ struct vpfe_ipipe_nf nf2;
+ struct vpfe_ipipe_gic gic;
+ struct vpfe_ipipe_wb wbal;
+ struct vpfe_ipipe_cfa cfa;
+ struct vpfe_ipipe_rgb2rgb rgb2rgb1;
+ struct vpfe_ipipe_rgb2rgb rgb2rgb2;
+ struct vpfe_ipipe_gamma gamma;
+ struct vpfe_ipipe_3d_lut lut;
+ struct vpfe_ipipe_rgb2yuv rgb2yuv;
+ struct vpfe_ipipe_gbce gbce;
+ struct vpfe_ipipe_yuv422_conv yuv422_conv;
+ struct vpfe_ipipe_yee yee;
+ struct vpfe_ipipe_car car;
+ struct vpfe_ipipe_cgs cgs;
+ struct ipipe_lum_adj lum_adj;
+};
+
+#define IPIPE_PAD_SINK 0
+#define IPIPE_PAD_SOURCE 1
+
+#define IPIPE_PADS_NUM 2
+
+#define IPIPE_OUTPUT_NONE 0
+#define IPIPE_OUTPUT_RESIZER (1 << 0)
+
+enum ipipe_input_entity {
+ IPIPE_INPUT_NONE = 0,
+ IPIPE_INPUT_MEMORY = 1,
+ IPIPE_INPUT_CCDC = 2,
+};
+
+
+struct vpfe_ipipe_device {
+ struct v4l2_subdev subdev;
+ struct media_pad pads[IPIPE_PADS_NUM];
+ struct v4l2_mbus_framefmt formats[IPIPE_PADS_NUM];
+ enum ipipe_input_entity input;
+ unsigned int output;
+ struct v4l2_ctrl_handler ctrls;
+ void __iomem *base_addr;
+ void __iomem *isp5_base_addr;
+ struct ipipe_module_params config;
+};
+
+struct ipipe_module_if {
+ unsigned int param_offset;
+ unsigned int param_size;
+ unsigned int config_offset;
+ int (*set)(struct vpfe_ipipe_device *ipipe, void *param);
+ int (*get)(struct vpfe_ipipe_device *ipipe, void *param);
+};
+
+/* data paths */
+enum ipipe_data_paths {
+ IPIPE_RAW2YUV,
+ /* Bayer RAW input to YCbCr output */
+ IPIPE_RAW2RAW,
+ /* Bayer Raw to Bayer output */
+ IPIPE_RAW2BOX,
+ /* Bayer Raw to Boxcar output */
+ IPIPE_YUV2YUV
+ /* YUV Raw to YUV Raw output */
+};
+
+#define IPIPE_COLPTN_R_Ye 0x0
+#define IPIPE_COLPTN_Gr_Cy 0x1
+#define IPIPE_COLPTN_Gb_G 0x2
+#define IPIPE_COLPTN_B_Mg 0x3
+
+#define COLPAT_EE_SHIFT 0
+#define COLPAT_EO_SHIFT 2
+#define COLPAT_OE_SHIFT 4
+#define COLPAT_OO_SHIFT 6
+
+#define ipipe_sgrbg_pattern \
+ (IPIPE_COLPTN_Gr_Cy << COLPAT_EE_SHIFT | \
+ IPIPE_COLPTN_R_Ye << COLPAT_EO_SHIFT | \
+ IPIPE_COLPTN_B_Mg << COLPAT_OE_SHIFT | \
+ IPIPE_COLPTN_Gb_G << COLPAT_OO_SHIFT)
+
+#define ipipe_srggb_pattern \
+ (IPIPE_COLPTN_R_Ye << COLPAT_EE_SHIFT | \
+ IPIPE_COLPTN_Gr_Cy << COLPAT_EO_SHIFT | \
+ IPIPE_COLPTN_Gb_G << COLPAT_OE_SHIFT | \
+ IPIPE_COLPTN_B_Mg << COLPAT_OO_SHIFT)
+
+int vpfe_ipipe_register_entities(struct vpfe_ipipe_device *ipipe,
+ struct v4l2_device *v4l2_dev);
+int vpfe_ipipe_init(struct vpfe_ipipe_device *ipipe,
+ struct platform_device *pdev);
+void vpfe_ipipe_unregister_entities(struct vpfe_ipipe_device *ipipe);
+void vpfe_ipipe_cleanup(struct vpfe_ipipe_device *ipipe,
+ struct platform_device *pdev);
+void vpfe_ipipe_enable(struct vpfe_device *vpfe_dev, int en);
+
+#endif /* _DAVINCI_VPFE_DM365_IPIPE_H */
diff --git a/drivers/staging/media/davinci_vpfe/dm365_ipipe_hw.c b/drivers/staging/media/davinci_vpfe/dm365_ipipe_hw.c
new file mode 100644
index 000000000..2a3a56b88
--- /dev/null
+++ b/drivers/staging/media/davinci_vpfe/dm365_ipipe_hw.c
@@ -0,0 +1,1048 @@
+/*
+ * Copyright (C) 2012 Texas Instruments 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 version 2.
+ *
+ * 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
+ *
+ * Contributors:
+ * Manjunath Hadli <manjunath.hadli@ti.com>
+ * Prabhakar Lad <prabhakar.lad@ti.com>
+ */
+
+#include "dm365_ipipe_hw.h"
+
+#define IPIPE_MODE_CONTINUOUS 0
+#define IPIPE_MODE_SINGLE_SHOT 1
+
+static void ipipe_clock_enable(void __iomem *base_addr)
+{
+ /* enable IPIPE MMR for register write access */
+ regw_ip(base_addr, IPIPE_GCK_MMR_DEFAULT, IPIPE_GCK_MMR);
+
+ /* enable the clock wb,cfa,dfc,d2f,pre modules */
+ regw_ip(base_addr, IPIPE_GCK_PIX_DEFAULT, IPIPE_GCK_PIX);
+}
+
+static void
+rsz_set_common_params(void __iomem *rsz_base, struct resizer_params *params)
+{
+ struct rsz_common_params *rsz_common = &params->rsz_common;
+ u32 val;
+
+ /* Set mode */
+ regw_rsz(rsz_base, params->oper_mode, RSZ_SRC_MODE);
+
+ /* data source selection and bypass */
+ val = (rsz_common->passthrough << RSZ_BYPASS_SHIFT) |
+ rsz_common->source;
+ regw_rsz(rsz_base, val, RSZ_SRC_FMT0);
+
+ /* src image selection */
+ val = (rsz_common->raw_flip & 1) |
+ (rsz_common->src_img_fmt << RSZ_SRC_IMG_FMT_SHIFT) |
+ ((rsz_common->y_c & 1) << RSZ_SRC_Y_C_SEL_SHIFT);
+ regw_rsz(rsz_base, val, RSZ_SRC_FMT1);
+
+ regw_rsz(rsz_base, rsz_common->vps & IPIPE_RSZ_VPS_MASK, RSZ_SRC_VPS);
+ regw_rsz(rsz_base, rsz_common->hps & IPIPE_RSZ_HPS_MASK, RSZ_SRC_HPS);
+ regw_rsz(rsz_base, rsz_common->vsz & IPIPE_RSZ_VSZ_MASK, RSZ_SRC_VSZ);
+ regw_rsz(rsz_base, rsz_common->hsz & IPIPE_RSZ_HSZ_MASK, RSZ_SRC_HSZ);
+ regw_rsz(rsz_base, rsz_common->yuv_y_min, RSZ_YUV_Y_MIN);
+ regw_rsz(rsz_base, rsz_common->yuv_y_max, RSZ_YUV_Y_MAX);
+ regw_rsz(rsz_base, rsz_common->yuv_c_min, RSZ_YUV_C_MIN);
+ regw_rsz(rsz_base, rsz_common->yuv_c_max, RSZ_YUV_C_MAX);
+ /* chromatic position */
+ regw_rsz(rsz_base, rsz_common->out_chr_pos, RSZ_YUV_PHS);
+}
+
+static void
+rsz_set_rsz_regs(void __iomem *rsz_base, unsigned int rsz_id,
+ struct resizer_params *params)
+{
+ struct resizer_scale_param *rsc_params;
+ struct rsz_ext_mem_param *ext_mem;
+ struct resizer_rgb *rgb;
+ u32 reg_base;
+ u32 val;
+
+ rsc_params = &params->rsz_rsc_param[rsz_id];
+ rgb = &params->rsz2rgb[rsz_id];
+ ext_mem = &params->ext_mem_param[rsz_id];
+
+ if (rsz_id == RSZ_A) {
+ val = rsc_params->h_flip << RSZA_H_FLIP_SHIFT;
+ val |= rsc_params->v_flip << RSZA_V_FLIP_SHIFT;
+ reg_base = RSZ_EN_A;
+ } else {
+ val = rsc_params->h_flip << RSZB_H_FLIP_SHIFT;
+ val |= rsc_params->v_flip << RSZB_V_FLIP_SHIFT;
+ reg_base = RSZ_EN_B;
+ }
+ /* update flip settings */
+ regw_rsz(rsz_base, val, RSZ_SEQ);
+
+ regw_rsz(rsz_base, params->oper_mode, reg_base + RSZ_MODE);
+
+ val = (rsc_params->cen << RSZ_CEN_SHIFT) | rsc_params->yen;
+ regw_rsz(rsz_base, val, reg_base + RSZ_420);
+
+ regw_rsz(rsz_base, rsc_params->i_vps & RSZ_VPS_MASK,
+ reg_base + RSZ_I_VPS);
+ regw_rsz(rsz_base, rsc_params->i_hps & RSZ_HPS_MASK,
+ reg_base + RSZ_I_HPS);
+ regw_rsz(rsz_base, rsc_params->o_vsz & RSZ_O_VSZ_MASK,
+ reg_base + RSZ_O_VSZ);
+ regw_rsz(rsz_base, rsc_params->o_hsz & RSZ_O_HSZ_MASK,
+ reg_base + RSZ_O_HSZ);
+ regw_rsz(rsz_base, rsc_params->v_phs_y & RSZ_V_PHS_MASK,
+ reg_base + RSZ_V_PHS_Y);
+ regw_rsz(rsz_base, rsc_params->v_phs_c & RSZ_V_PHS_MASK,
+ reg_base + RSZ_V_PHS_C);
+
+ /* keep this additional adjustment to zero for now */
+ regw_rsz(rsz_base, rsc_params->v_dif & RSZ_V_DIF_MASK,
+ reg_base + RSZ_V_DIF);
+
+ val = (rsc_params->v_typ_y & 1) |
+ ((rsc_params->v_typ_c & 1) << RSZ_TYP_C_SHIFT);
+ regw_rsz(rsz_base, val, reg_base + RSZ_V_TYP);
+
+ val = (rsc_params->v_lpf_int_y & RSZ_LPF_INT_MASK) |
+ ((rsc_params->v_lpf_int_c & RSZ_LPF_INT_MASK) <<
+ RSZ_LPF_INT_C_SHIFT);
+ regw_rsz(rsz_base, val, reg_base + RSZ_V_LPF);
+
+ regw_rsz(rsz_base, rsc_params->h_phs &
+ RSZ_H_PHS_MASK, reg_base + RSZ_H_PHS);
+
+ regw_rsz(rsz_base, 0, reg_base + RSZ_H_PHS_ADJ);
+ regw_rsz(rsz_base, rsc_params->h_dif &
+ RSZ_H_DIF_MASK, reg_base + RSZ_H_DIF);
+
+ val = (rsc_params->h_typ_y & 1) |
+ ((rsc_params->h_typ_c & 1) << RSZ_TYP_C_SHIFT);
+ regw_rsz(rsz_base, val, reg_base + RSZ_H_TYP);
+
+ val = (rsc_params->h_lpf_int_y & RSZ_LPF_INT_MASK) |
+ ((rsc_params->h_lpf_int_c & RSZ_LPF_INT_MASK) <<
+ RSZ_LPF_INT_C_SHIFT);
+ regw_rsz(rsz_base, val, reg_base + RSZ_H_LPF);
+
+ regw_rsz(rsz_base, rsc_params->dscale_en & 1, reg_base + RSZ_DWN_EN);
+
+ val = (rsc_params->h_dscale_ave_sz & RSZ_DWN_SCALE_AV_SZ_MASK) |
+ ((rsc_params->v_dscale_ave_sz & RSZ_DWN_SCALE_AV_SZ_MASK) <<
+ RSZ_DWN_SCALE_AV_SZ_V_SHIFT);
+ regw_rsz(rsz_base, val, reg_base + RSZ_DWN_AV);
+
+ /* setting rgb conversion parameters */
+ regw_rsz(rsz_base, rgb->rgb_en, reg_base + RSZ_RGB_EN);
+
+ val = (rgb->rgb_typ << RSZ_RGB_TYP_SHIFT) |
+ (rgb->rgb_msk0 << RSZ_RGB_MSK0_SHIFT) |
+ (rgb->rgb_msk1 << RSZ_RGB_MSK1_SHIFT);
+ regw_rsz(rsz_base, val, reg_base + RSZ_RGB_TYP);
+
+ regw_rsz(rsz_base, rgb->rgb_alpha_val & RSZ_RGB_ALPHA_MASK,
+ reg_base + RSZ_RGB_BLD);
+
+ /* setting external memory parameters */
+ regw_rsz(rsz_base, ext_mem->rsz_sdr_oft_y, reg_base + RSZ_SDR_Y_OFT);
+ regw_rsz(rsz_base, ext_mem->rsz_sdr_ptr_s_y,
+ reg_base + RSZ_SDR_Y_PTR_S);
+ regw_rsz(rsz_base, ext_mem->rsz_sdr_ptr_e_y,
+ reg_base + RSZ_SDR_Y_PTR_E);
+ regw_rsz(rsz_base, ext_mem->rsz_sdr_oft_c, reg_base + RSZ_SDR_C_OFT);
+ regw_rsz(rsz_base, ext_mem->rsz_sdr_ptr_s_c,
+ reg_base + RSZ_SDR_C_PTR_S);
+ regw_rsz(rsz_base, (ext_mem->rsz_sdr_ptr_e_c >> 1),
+ reg_base + RSZ_SDR_C_PTR_E);
+}
+
+/*set the registers of either RSZ0 or RSZ1 */
+static void
+ipipe_setup_resizer(void __iomem *rsz_base, struct resizer_params *params)
+{
+ /* enable MMR gate to write to Resizer */
+ regw_rsz(rsz_base, 1, RSZ_GCK_MMR);
+
+ /* Enable resizer if it is not in bypass mode */
+ if (params->rsz_common.passthrough)
+ regw_rsz(rsz_base, 0, RSZ_GCK_SDR);
+ else
+ regw_rsz(rsz_base, 1, RSZ_GCK_SDR);
+
+ rsz_set_common_params(rsz_base, params);
+
+ regw_rsz(rsz_base, params->rsz_en[RSZ_A], RSZ_EN_A);
+
+ if (params->rsz_en[RSZ_A])
+ /*setting rescale parameters */
+ rsz_set_rsz_regs(rsz_base, RSZ_A, params);
+
+ regw_rsz(rsz_base, params->rsz_en[RSZ_B], RSZ_EN_B);
+
+ if (params->rsz_en[RSZ_B])
+ rsz_set_rsz_regs(rsz_base, RSZ_B, params);
+}
+
+static u32 ipipe_get_color_pat(u32 pix)
+{
+ switch (pix) {
+ case MEDIA_BUS_FMT_SGRBG10_ALAW8_1X8:
+ case MEDIA_BUS_FMT_SGRBG10_DPCM8_1X8:
+ case MEDIA_BUS_FMT_SGRBG12_1X12:
+ return ipipe_sgrbg_pattern;
+
+ default:
+ return ipipe_srggb_pattern;
+ }
+}
+
+static int ipipe_get_data_path(struct vpfe_ipipe_device *ipipe)
+{
+ u32 temp_pix_fmt;
+
+ switch (ipipe->formats[IPIPE_PAD_SINK].code) {
+ case MEDIA_BUS_FMT_SBGGR8_1X8:
+ case MEDIA_BUS_FMT_SGRBG10_ALAW8_1X8:
+ case MEDIA_BUS_FMT_SGRBG10_DPCM8_1X8:
+ case MEDIA_BUS_FMT_SGRBG12_1X12:
+ temp_pix_fmt = MEDIA_BUS_FMT_SGRBG12_1X12;
+ break;
+
+ default:
+ temp_pix_fmt = MEDIA_BUS_FMT_UYVY8_2X8;
+ }
+
+ if (temp_pix_fmt == MEDIA_BUS_FMT_SGRBG12_1X12) {
+ if (ipipe->formats[IPIPE_PAD_SOURCE].code ==
+ MEDIA_BUS_FMT_SGRBG12_1X12)
+ return IPIPE_RAW2RAW;
+ return IPIPE_RAW2YUV;
+ }
+
+ return IPIPE_YUV2YUV;
+}
+
+static int get_ipipe_mode(struct vpfe_ipipe_device *ipipe)
+{
+ struct vpfe_device *vpfe_dev = to_vpfe_device(ipipe);
+ u16 ipipeif_sink = vpfe_dev->vpfe_ipipeif.input;
+
+ if (ipipeif_sink == IPIPEIF_INPUT_MEMORY)
+ return IPIPE_MODE_SINGLE_SHOT;
+ else if (ipipeif_sink == IPIPEIF_INPUT_ISIF)
+ return IPIPE_MODE_CONTINUOUS;
+
+ return -EINVAL;
+}
+
+int config_ipipe_hw(struct vpfe_ipipe_device *ipipe)
+{
+ struct vpfe_ipipe_input_config *config = &ipipe->config.input_config;
+ void __iomem *ipipe_base = ipipe->base_addr;
+ struct v4l2_mbus_framefmt *outformat;
+ u32 color_pat;
+ u32 ipipe_mode;
+ u32 data_path;
+
+ /* enable clock to IPIPE */
+ vpss_enable_clock(VPSS_IPIPE_CLOCK, 1);
+ ipipe_clock_enable(ipipe_base);
+
+ if (ipipe->input == IPIPE_INPUT_NONE) {
+ regw_ip(ipipe_base, 0, IPIPE_SRC_EN);
+ return 0;
+ }
+
+ ipipe_mode = get_ipipe_mode(ipipe);
+ if (ipipe_mode < 0) {
+ pr_err("Failed to get ipipe mode");
+ return -EINVAL;
+ }
+ regw_ip(ipipe_base, ipipe_mode, IPIPE_SRC_MODE);
+
+ data_path = ipipe_get_data_path(ipipe);
+ regw_ip(ipipe_base, data_path, IPIPE_SRC_FMT);
+
+ regw_ip(ipipe_base, config->vst & IPIPE_RSZ_VPS_MASK, IPIPE_SRC_VPS);
+ regw_ip(ipipe_base, config->hst & IPIPE_RSZ_HPS_MASK, IPIPE_SRC_HPS);
+
+ outformat = &ipipe->formats[IPIPE_PAD_SOURCE];
+ regw_ip(ipipe_base, (outformat->height + 1) & IPIPE_RSZ_VSZ_MASK,
+ IPIPE_SRC_VSZ);
+ regw_ip(ipipe_base, (outformat->width + 1) & IPIPE_RSZ_HSZ_MASK,
+ IPIPE_SRC_HSZ);
+
+ if (data_path == IPIPE_RAW2YUV ||
+ data_path == IPIPE_RAW2RAW) {
+ color_pat =
+ ipipe_get_color_pat(ipipe->formats[IPIPE_PAD_SINK].code);
+ regw_ip(ipipe_base, color_pat, IPIPE_SRC_COL);
+ }
+
+ return 0;
+}
+
+/*
+ * config_rsz_hw() - Performs hardware setup of resizer.
+ */
+int config_rsz_hw(struct vpfe_resizer_device *resizer,
+ struct resizer_params *config)
+{
+ struct vpfe_device *vpfe_dev = to_vpfe_device(resizer);
+ void __iomem *ipipe_base = vpfe_dev->vpfe_ipipe.base_addr;
+ void __iomem *rsz_base = vpfe_dev->vpfe_resizer.base_addr;
+
+ /* enable VPSS clock */
+ vpss_enable_clock(VPSS_IPIPE_CLOCK, 1);
+ ipipe_clock_enable(ipipe_base);
+
+ ipipe_setup_resizer(rsz_base, config);
+
+ return 0;
+}
+
+static void
+rsz_set_y_address(void __iomem *rsz_base, unsigned int address,
+ unsigned int offset)
+{
+ u32 val;
+
+ val = address & SET_LOW_ADDR;
+ regw_rsz(rsz_base, val, offset + RSZ_SDR_Y_BAD_L);
+ regw_rsz(rsz_base, val, offset + RSZ_SDR_Y_SAD_L);
+
+ val = (address & SET_HIGH_ADDR) >> 16;
+ regw_rsz(rsz_base, val, offset + RSZ_SDR_Y_BAD_H);
+ regw_rsz(rsz_base, val, offset + RSZ_SDR_Y_SAD_H);
+}
+
+static void
+rsz_set_c_address(void __iomem *rsz_base, unsigned int address,
+ unsigned int offset)
+{
+ u32 val;
+
+ val = address & SET_LOW_ADDR;
+ regw_rsz(rsz_base, val, offset + RSZ_SDR_C_BAD_L);
+ regw_rsz(rsz_base, val, offset + RSZ_SDR_C_SAD_L);
+
+ val = (address & SET_HIGH_ADDR) >> 16;
+ regw_rsz(rsz_base, val, offset + RSZ_SDR_C_BAD_H);
+ regw_rsz(rsz_base, val, offset + RSZ_SDR_C_SAD_H);
+}
+
+/*
+ * resizer_set_outaddr() - set the address for given resize_no
+ * @rsz_base: resizer base address
+ * @params: pointer to ipipe_params structure
+ * @resize_no: 0 - Resizer-A, 1 - Resizer B
+ * @address: the address to set
+ */
+int
+resizer_set_outaddr(void __iomem *rsz_base, struct resizer_params *params,
+ int resize_no, unsigned int address)
+{
+ struct resizer_scale_param *rsc_param;
+ struct rsz_ext_mem_param *mem_param;
+ struct rsz_common_params *rsz_common;
+ unsigned int rsz_start_add;
+ unsigned int val;
+
+ if (resize_no != RSZ_A && resize_no != RSZ_B)
+ return -EINVAL;
+
+ mem_param = &params->ext_mem_param[resize_no];
+ rsc_param = &params->rsz_rsc_param[resize_no];
+ rsz_common = &params->rsz_common;
+
+ if (resize_no == RSZ_A)
+ rsz_start_add = RSZ_EN_A;
+ else
+ rsz_start_add = RSZ_EN_B;
+
+ /* y_c = 0 for y, = 1 for c */
+ if (rsz_common->src_img_fmt == RSZ_IMG_420) {
+ if (rsz_common->y_c) {
+ /* C channel */
+ val = address + mem_param->flip_ofst_c;
+ rsz_set_c_address(rsz_base, val, rsz_start_add);
+ } else {
+ val = address + mem_param->flip_ofst_y;
+ rsz_set_y_address(rsz_base, val, rsz_start_add);
+ }
+ } else {
+ if (rsc_param->cen && rsc_param->yen) {
+ /* 420 */
+ val = address + mem_param->c_offset +
+ mem_param->flip_ofst_c +
+ mem_param->user_y_ofst +
+ mem_param->user_c_ofst;
+ if (resize_no == RSZ_B)
+ val +=
+ params->ext_mem_param[RSZ_A].user_y_ofst +
+ params->ext_mem_param[RSZ_A].user_c_ofst;
+ /* set C address */
+ rsz_set_c_address(rsz_base, val, rsz_start_add);
+ }
+ val = address + mem_param->flip_ofst_y + mem_param->user_y_ofst;
+ if (resize_no == RSZ_B)
+ val += params->ext_mem_param[RSZ_A].user_y_ofst +
+ params->ext_mem_param[RSZ_A].user_c_ofst;
+ /* set Y address */
+ rsz_set_y_address(rsz_base, val, rsz_start_add);
+ }
+ /* resizer must be enabled */
+ regw_rsz(rsz_base, params->rsz_en[resize_no], rsz_start_add);
+
+ return 0;
+}
+
+void
+ipipe_set_lutdpc_regs(void __iomem *base_addr, void __iomem *isp5_base_addr,
+ struct vpfe_ipipe_lutdpc *dpc)
+{
+ u32 max_tbl_size = LUT_DPC_MAX_SIZE >> 1;
+ u32 lut_start_addr = DPC_TB0_START_ADDR;
+ u32 val;
+ u32 count;
+
+ ipipe_clock_enable(base_addr);
+ regw_ip(base_addr, dpc->en, DPC_LUT_EN);
+
+ if (dpc->en != 1)
+ return;
+
+ val = LUTDPC_TBL_256_EN | (dpc->repl_white & 1);
+ regw_ip(base_addr, val, DPC_LUT_SEL);
+ regw_ip(base_addr, LUT_DPC_START_ADDR, DPC_LUT_ADR);
+ regw_ip(base_addr, dpc->dpc_size, DPC_LUT_SIZ & LUT_DPC_SIZE_MASK);
+
+ if (dpc->table == NULL)
+ return;
+
+ for (count = 0; count < dpc->dpc_size; count++) {
+ if (count >= max_tbl_size)
+ lut_start_addr = DPC_TB1_START_ADDR;
+ val = (dpc->table[count].horz_pos & LUT_DPC_H_POS_MASK) |
+ ((dpc->table[count].vert_pos & LUT_DPC_V_POS_MASK) <<
+ LUT_DPC_V_POS_SHIFT) | (dpc->table[count].method <<
+ LUT_DPC_CORR_METH_SHIFT);
+ w_ip_table(isp5_base_addr, val, (lut_start_addr +
+ ((count % max_tbl_size) << 2)));
+ }
+}
+
+static void
+set_dpc_thresholds(void __iomem *base_addr,
+ struct vpfe_ipipe_otfdpc_2_0_cfg *dpc_thr)
+{
+ regw_ip(base_addr, dpc_thr->corr_thr.r & OTFDPC_DPC2_THR_MASK,
+ DPC_OTF_2C_THR_R);
+ regw_ip(base_addr, dpc_thr->corr_thr.gr & OTFDPC_DPC2_THR_MASK,
+ DPC_OTF_2C_THR_GR);
+ regw_ip(base_addr, dpc_thr->corr_thr.gb & OTFDPC_DPC2_THR_MASK,
+ DPC_OTF_2C_THR_GB);
+ regw_ip(base_addr, dpc_thr->corr_thr.b & OTFDPC_DPC2_THR_MASK,
+ DPC_OTF_2C_THR_B);
+ regw_ip(base_addr, dpc_thr->det_thr.r & OTFDPC_DPC2_THR_MASK,
+ DPC_OTF_2D_THR_R);
+ regw_ip(base_addr, dpc_thr->det_thr.gr & OTFDPC_DPC2_THR_MASK,
+ DPC_OTF_2D_THR_GR);
+ regw_ip(base_addr, dpc_thr->det_thr.gb & OTFDPC_DPC2_THR_MASK,
+ DPC_OTF_2D_THR_GB);
+ regw_ip(base_addr, dpc_thr->det_thr.b & OTFDPC_DPC2_THR_MASK,
+ DPC_OTF_2D_THR_B);
+}
+
+void ipipe_set_otfdpc_regs(void __iomem *base_addr,
+ struct vpfe_ipipe_otfdpc *otfdpc)
+{
+ struct vpfe_ipipe_otfdpc_2_0_cfg *dpc_2_0 = &otfdpc->alg_cfg.dpc_2_0;
+ struct vpfe_ipipe_otfdpc_3_0_cfg *dpc_3_0 = &otfdpc->alg_cfg.dpc_3_0;
+ u32 val;
+
+ ipipe_clock_enable(base_addr);
+
+ regw_ip(base_addr, (otfdpc->en & 1), DPC_OTF_EN);
+ if (!otfdpc->en)
+ return;
+
+ /* dpc enabled */
+ val = (otfdpc->det_method << OTF_DET_METHOD_SHIFT) | otfdpc->alg;
+ regw_ip(base_addr, val, DPC_OTF_TYP);
+
+ if (otfdpc->det_method == VPFE_IPIPE_DPC_OTF_MIN_MAX) {
+ /* ALG= 0, TYP = 0, DPC_OTF_2D_THR_[x]=0
+ * DPC_OTF_2C_THR_[x] = Maximum thresohld
+ * MinMax method
+ */
+ dpc_2_0->det_thr.r = dpc_2_0->det_thr.gb =
+ dpc_2_0->det_thr.gr = dpc_2_0->det_thr.b = 0;
+ set_dpc_thresholds(base_addr, dpc_2_0);
+ return;
+ }
+ /* MinMax2 */
+ if (otfdpc->alg == VPFE_IPIPE_OTFDPC_2_0) {
+ set_dpc_thresholds(base_addr, dpc_2_0);
+ return;
+ }
+ regw_ip(base_addr, dpc_3_0->act_adj_shf &
+ OTF_DPC3_0_SHF_MASK, DPC_OTF_3_SHF);
+ /* Detection thresholds */
+ regw_ip(base_addr, ((dpc_3_0->det_thr & OTF_DPC3_0_THR_MASK) <<
+ OTF_DPC3_0_THR_SHIFT), DPC_OTF_3D_THR);
+ regw_ip(base_addr, dpc_3_0->det_slp &
+ OTF_DPC3_0_SLP_MASK, DPC_OTF_3D_SLP);
+ regw_ip(base_addr, dpc_3_0->det_thr_min &
+ OTF_DPC3_0_DET_MASK, DPC_OTF_3D_MIN);
+ regw_ip(base_addr, dpc_3_0->det_thr_max &
+ OTF_DPC3_0_DET_MASK, DPC_OTF_3D_MAX);
+ /* Correction thresholds */
+ regw_ip(base_addr, ((dpc_3_0->corr_thr & OTF_DPC3_0_THR_MASK) <<
+ OTF_DPC3_0_THR_SHIFT), DPC_OTF_3C_THR);
+ regw_ip(base_addr, dpc_3_0->corr_slp &
+ OTF_DPC3_0_SLP_MASK, DPC_OTF_3C_SLP);
+ regw_ip(base_addr, dpc_3_0->corr_thr_min &
+ OTF_DPC3_0_CORR_MASK, DPC_OTF_3C_MIN);
+ regw_ip(base_addr, dpc_3_0->corr_thr_max &
+ OTF_DPC3_0_CORR_MASK, DPC_OTF_3C_MAX);
+}
+
+/* 2D Noise filter */
+void
+ipipe_set_d2f_regs(void __iomem *base_addr, unsigned int id,
+ struct vpfe_ipipe_nf *noise_filter)
+{
+
+ u32 offset = D2F_1ST;
+ int count;
+ u32 val;
+
+ if (id == IPIPE_D2F_2ND)
+ offset = D2F_2ND;
+
+ ipipe_clock_enable(base_addr);
+ regw_ip(base_addr, noise_filter->en & 1, offset + D2F_EN);
+ if (!noise_filter->en)
+ return;
+
+ /*noise filter enabled */
+ /* Combine all the fields to make D2F_CFG register of IPIPE */
+ val = ((noise_filter->spread_val & D2F_SPR_VAL_MASK) <<
+ D2F_SPR_VAL_SHIFT) | ((noise_filter->shft_val &
+ D2F_SHFT_VAL_MASK) << D2F_SHFT_VAL_SHIFT) |
+ (noise_filter->gr_sample_meth << D2F_SAMPLE_METH_SHIFT) |
+ ((noise_filter->apply_lsc_gain & 1) <<
+ D2F_APPLY_LSC_GAIN_SHIFT) | D2F_USE_SPR_REG_VAL;
+ regw_ip(base_addr, val, offset + D2F_TYP);
+
+ /* edge detection minimum */
+ regw_ip(base_addr, noise_filter->edge_det_min_thr &
+ D2F_EDGE_DET_THR_MASK, offset + D2F_EDG_MIN);
+
+ /* edge detection maximum */
+ regw_ip(base_addr, noise_filter->edge_det_max_thr &
+ D2F_EDGE_DET_THR_MASK, offset + D2F_EDG_MAX);
+
+ for (count = 0; count < VPFE_IPIPE_NF_STR_TABLE_SIZE; count++)
+ regw_ip(base_addr,
+ (noise_filter->str[count] & D2F_STR_VAL_MASK),
+ offset + D2F_STR + count * 4);
+
+ for (count = 0; count < VPFE_IPIPE_NF_THR_TABLE_SIZE; count++)
+ regw_ip(base_addr, noise_filter->thr[count] & D2F_THR_VAL_MASK,
+ offset + D2F_THR + count * 4);
+}
+
+#define IPIPE_U8Q5(decimal, integer) \
+ (((decimal & 0x1f) | ((integer & 0x7) << 5)))
+
+/* Green Imbalance Correction */
+void ipipe_set_gic_regs(void __iomem *base_addr, struct vpfe_ipipe_gic *gic)
+{
+ u32 val;
+
+ ipipe_clock_enable(base_addr);
+ regw_ip(base_addr, gic->en & 1, GIC_EN);
+
+ if (!gic->en)
+ return;
+
+ /*gic enabled */
+ val = (gic->wt_fn_type << GIC_TYP_SHIFT) |
+ (gic->thr_sel << GIC_THR_SEL_SHIFT) |
+ ((gic->apply_lsc_gain & 1) << GIC_APPLY_LSC_GAIN_SHIFT);
+ regw_ip(base_addr, val, GIC_TYP);
+
+ regw_ip(base_addr, gic->gain & GIC_GAIN_MASK, GIC_GAN);
+
+ if (gic->gic_alg != VPFE_IPIPE_GIC_ALG_ADAPT_GAIN) {
+ /* Constant Gain. Set threshold to maximum */
+ regw_ip(base_addr, GIC_THR_MASK, GIC_THR);
+ return;
+ }
+
+ if (gic->thr_sel == VPFE_IPIPE_GIC_THR_REG) {
+ regw_ip(base_addr, gic->thr & GIC_THR_MASK, GIC_THR);
+ regw_ip(base_addr, gic->slope & GIC_SLOPE_MASK, GIC_SLP);
+ } else {
+ /* Use NF thresholds */
+ val = IPIPE_U8Q5(gic->nf2_thr_gain.decimal,
+ gic->nf2_thr_gain.integer);
+ regw_ip(base_addr, val, GIC_NFGAN);
+ }
+}
+
+#define IPIPE_U13Q9(decimal, integer) \
+ (((decimal & 0x1ff) | ((integer & 0xf) << 9)))
+/* White balance */
+void ipipe_set_wb_regs(void __iomem *base_addr, struct vpfe_ipipe_wb *wb)
+{
+ u32 val;
+
+ ipipe_clock_enable(base_addr);
+ /* Ofsets. S12 */
+ regw_ip(base_addr, wb->ofst_r & WB_OFFSET_MASK, WB2_OFT_R);
+ regw_ip(base_addr, wb->ofst_gr & WB_OFFSET_MASK, WB2_OFT_GR);
+ regw_ip(base_addr, wb->ofst_gb & WB_OFFSET_MASK, WB2_OFT_GB);
+ regw_ip(base_addr, wb->ofst_b & WB_OFFSET_MASK, WB2_OFT_B);
+
+ /* Gains. U13Q9 */
+ val = IPIPE_U13Q9(wb->gain_r.decimal, wb->gain_r.integer);
+ regw_ip(base_addr, val, WB2_WGN_R);
+
+ val = IPIPE_U13Q9(wb->gain_gr.decimal, wb->gain_gr.integer);
+ regw_ip(base_addr, val, WB2_WGN_GR);
+
+ val = IPIPE_U13Q9(wb->gain_gb.decimal, wb->gain_gb.integer);
+ regw_ip(base_addr, val, WB2_WGN_GB);
+
+ val = IPIPE_U13Q9(wb->gain_b.decimal, wb->gain_b.integer);
+ regw_ip(base_addr, val, WB2_WGN_B);
+}
+
+/* CFA */
+void ipipe_set_cfa_regs(void __iomem *base_addr, struct vpfe_ipipe_cfa *cfa)
+{
+ ipipe_clock_enable(base_addr);
+
+ regw_ip(base_addr, cfa->alg, CFA_MODE);
+ regw_ip(base_addr, cfa->hpf_thr_2dir & CFA_HPF_THR_2DIR_MASK,
+ CFA_2DIR_HPF_THR);
+ regw_ip(base_addr, cfa->hpf_slp_2dir & CFA_HPF_SLOPE_2DIR_MASK,
+ CFA_2DIR_HPF_SLP);
+ regw_ip(base_addr, cfa->hp_mix_thr_2dir & CFA_HPF_MIX_THR_2DIR_MASK,
+ CFA_2DIR_MIX_THR);
+ regw_ip(base_addr, cfa->hp_mix_slope_2dir & CFA_HPF_MIX_SLP_2DIR_MASK,
+ CFA_2DIR_MIX_SLP);
+ regw_ip(base_addr, cfa->dir_thr_2dir & CFA_DIR_THR_2DIR_MASK,
+ CFA_2DIR_DIR_THR);
+ regw_ip(base_addr, cfa->dir_slope_2dir & CFA_DIR_SLP_2DIR_MASK,
+ CFA_2DIR_DIR_SLP);
+ regw_ip(base_addr, cfa->nd_wt_2dir & CFA_ND_WT_2DIR_MASK,
+ CFA_2DIR_NDWT);
+ regw_ip(base_addr, cfa->hue_fract_daa & CFA_DAA_HUE_FRA_MASK,
+ CFA_MONO_HUE_FRA);
+ regw_ip(base_addr, cfa->edge_thr_daa & CFA_DAA_EDG_THR_MASK,
+ CFA_MONO_EDG_THR);
+ regw_ip(base_addr, cfa->thr_min_daa & CFA_DAA_THR_MIN_MASK,
+ CFA_MONO_THR_MIN);
+ regw_ip(base_addr, cfa->thr_slope_daa & CFA_DAA_THR_SLP_MASK,
+ CFA_MONO_THR_SLP);
+ regw_ip(base_addr, cfa->slope_min_daa & CFA_DAA_SLP_MIN_MASK,
+ CFA_MONO_SLP_MIN);
+ regw_ip(base_addr, cfa->slope_slope_daa & CFA_DAA_SLP_SLP_MASK,
+ CFA_MONO_SLP_SLP);
+ regw_ip(base_addr, cfa->lp_wt_daa & CFA_DAA_LP_WT_MASK,
+ CFA_MONO_LPWT);
+}
+
+void
+ipipe_set_rgb2rgb_regs(void __iomem *base_addr, unsigned int id,
+ struct vpfe_ipipe_rgb2rgb *rgb)
+{
+ u32 offset_mask = RGB2RGB_1_OFST_MASK;
+ u32 offset = RGB1_MUL_BASE;
+ u32 integ_mask = 0xf;
+ u32 val;
+
+ ipipe_clock_enable(base_addr);
+
+ if (id == IPIPE_RGB2RGB_2) {
+ /* For second RGB module, gain integer is 3 bits instead
+ of 4, offset has 11 bits insread of 13 */
+ offset = RGB2_MUL_BASE;
+ integ_mask = 0x7;
+ offset_mask = RGB2RGB_2_OFST_MASK;
+ }
+ /* Gains */
+ val = (rgb->coef_rr.decimal & 0xff) |
+ ((rgb->coef_rr.integer & integ_mask) << 8);
+ regw_ip(base_addr, val, offset + RGB_MUL_RR);
+ val = (rgb->coef_gr.decimal & 0xff) |
+ ((rgb->coef_gr.integer & integ_mask) << 8);
+ regw_ip(base_addr, val, offset + RGB_MUL_GR);
+ val = (rgb->coef_br.decimal & 0xff) |
+ ((rgb->coef_br.integer & integ_mask) << 8);
+ regw_ip(base_addr, val, offset + RGB_MUL_BR);
+ val = (rgb->coef_rg.decimal & 0xff) |
+ ((rgb->coef_rg.integer & integ_mask) << 8);
+ regw_ip(base_addr, val, offset + RGB_MUL_RG);
+ val = (rgb->coef_gg.decimal & 0xff) |
+ ((rgb->coef_gg.integer & integ_mask) << 8);
+ regw_ip(base_addr, val, offset + RGB_MUL_GG);
+ val = (rgb->coef_bg.decimal & 0xff) |
+ ((rgb->coef_bg.integer & integ_mask) << 8);
+ regw_ip(base_addr, val, offset + RGB_MUL_BG);
+ val = (rgb->coef_rb.decimal & 0xff) |
+ ((rgb->coef_rb.integer & integ_mask) << 8);
+ regw_ip(base_addr, val, offset + RGB_MUL_RB);
+ val = (rgb->coef_gb.decimal & 0xff) |
+ ((rgb->coef_gb.integer & integ_mask) << 8);
+ regw_ip(base_addr, val, offset + RGB_MUL_GB);
+ val = (rgb->coef_bb.decimal & 0xff) |
+ ((rgb->coef_bb.integer & integ_mask) << 8);
+ regw_ip(base_addr, val, offset + RGB_MUL_BB);
+
+ /* Offsets */
+ regw_ip(base_addr, rgb->out_ofst_r & offset_mask, offset + RGB_OFT_OR);
+ regw_ip(base_addr, rgb->out_ofst_g & offset_mask, offset + RGB_OFT_OG);
+ regw_ip(base_addr, rgb->out_ofst_b & offset_mask, offset + RGB_OFT_OB);
+}
+
+static void
+ipipe_update_gamma_tbl(void __iomem *isp5_base_addr,
+ struct vpfe_ipipe_gamma_entry *table, int size, u32 addr)
+{
+ int count;
+ u32 val;
+
+ for (count = 0; count < size; count++) {
+ val = table[count].slope & GAMMA_MASK;
+ val |= (table[count].offset & GAMMA_MASK) << GAMMA_SHIFT;
+ w_ip_table(isp5_base_addr, val, (addr + (count * 4)));
+ }
+}
+
+void
+ipipe_set_gamma_regs(void __iomem *base_addr, void __iomem *isp5_base_addr,
+ struct vpfe_ipipe_gamma *gamma)
+{
+ int table_size;
+ u32 val;
+
+ ipipe_clock_enable(base_addr);
+ val = (gamma->bypass_r << GAMMA_BYPR_SHIFT) |
+ (gamma->bypass_b << GAMMA_BYPG_SHIFT) |
+ (gamma->bypass_g << GAMMA_BYPB_SHIFT) |
+ (gamma->tbl_sel << GAMMA_TBL_SEL_SHIFT) |
+ (gamma->tbl_size << GAMMA_TBL_SIZE_SHIFT);
+
+ regw_ip(base_addr, val, GMM_CFG);
+
+ if (gamma->tbl_sel != VPFE_IPIPE_GAMMA_TBL_RAM)
+ return;
+
+ table_size = gamma->tbl_size;
+
+ if (!gamma->bypass_r && gamma->table_r != NULL)
+ ipipe_update_gamma_tbl(isp5_base_addr, gamma->table_r,
+ table_size, GAMMA_R_START_ADDR);
+ if (!gamma->bypass_b && gamma->table_b != NULL)
+ ipipe_update_gamma_tbl(isp5_base_addr, gamma->table_b,
+ table_size, GAMMA_B_START_ADDR);
+ if (!gamma->bypass_g && gamma->table_g != NULL)
+ ipipe_update_gamma_tbl(isp5_base_addr, gamma->table_g,
+ table_size, GAMMA_G_START_ADDR);
+}
+
+void
+ipipe_set_3d_lut_regs(void __iomem *base_addr, void __iomem *isp5_base_addr,
+ struct vpfe_ipipe_3d_lut *lut_3d)
+{
+ struct vpfe_ipipe_3d_lut_entry *tbl;
+ u32 bnk_index;
+ u32 tbl_index;
+ u32 val;
+ u32 i;
+
+ ipipe_clock_enable(base_addr);
+ regw_ip(base_addr, lut_3d->en, D3LUT_EN);
+
+ if (!lut_3d->en)
+ return;
+
+ /* lut_3d enabled */
+ if (!lut_3d->table)
+ return;
+
+ /* valied table */
+ tbl = lut_3d->table;
+ for (i = 0; i < VPFE_IPIPE_MAX_SIZE_3D_LUT; i++) {
+ /* Each entry has 0-9 (B), 10-19 (G) and
+ 20-29 R values */
+ val = tbl[i].b & D3_LUT_ENTRY_MASK;
+ val |= (tbl[i].g & D3_LUT_ENTRY_MASK) <<
+ D3_LUT_ENTRY_G_SHIFT;
+ val |= (tbl[i].r & D3_LUT_ENTRY_MASK) <<
+ D3_LUT_ENTRY_R_SHIFT;
+ bnk_index = i % 4;
+ tbl_index = i >> 2;
+ tbl_index <<= 2;
+ if (bnk_index == 0)
+ w_ip_table(isp5_base_addr, val,
+ tbl_index + D3L_TB0_START_ADDR);
+ else if (bnk_index == 1)
+ w_ip_table(isp5_base_addr, val,
+ tbl_index + D3L_TB1_START_ADDR);
+ else if (bnk_index == 2)
+ w_ip_table(isp5_base_addr, val,
+ tbl_index + D3L_TB2_START_ADDR);
+ else
+ w_ip_table(isp5_base_addr, val,
+ tbl_index + D3L_TB3_START_ADDR);
+ }
+}
+
+/* Lumina adjustments */
+void
+ipipe_set_lum_adj_regs(void __iomem *base_addr, struct ipipe_lum_adj *lum_adj)
+{
+ u32 val;
+
+ ipipe_clock_enable(base_addr);
+
+ /* combine fields of YUV_ADJ to set brightness and contrast */
+ val = lum_adj->contrast << LUM_ADJ_CONTR_SHIFT |
+ lum_adj->brightness << LUM_ADJ_BRIGHT_SHIFT;
+ regw_ip(base_addr, val, YUV_ADJ);
+}
+
+#define IPIPE_S12Q8(decimal, integer) \
+ (((decimal & 0xff) | ((integer & 0xf) << 8)))
+
+void ipipe_set_rgb2ycbcr_regs(void __iomem *base_addr,
+ struct vpfe_ipipe_rgb2yuv *yuv)
+{
+ u32 val;
+
+ /* S10Q8 */
+ ipipe_clock_enable(base_addr);
+ val = IPIPE_S12Q8(yuv->coef_ry.decimal, yuv->coef_ry.integer);
+ regw_ip(base_addr, val, YUV_MUL_RY);
+ val = IPIPE_S12Q8(yuv->coef_gy.decimal, yuv->coef_gy.integer);
+ regw_ip(base_addr, val, YUV_MUL_GY);
+ val = IPIPE_S12Q8(yuv->coef_by.decimal, yuv->coef_by.integer);
+ regw_ip(base_addr, val, YUV_MUL_BY);
+ val = IPIPE_S12Q8(yuv->coef_rcb.decimal, yuv->coef_rcb.integer);
+ regw_ip(base_addr, val, YUV_MUL_RCB);
+ val = IPIPE_S12Q8(yuv->coef_gcb.decimal, yuv->coef_gcb.integer);
+ regw_ip(base_addr, val, YUV_MUL_GCB);
+ val = IPIPE_S12Q8(yuv->coef_bcb.decimal, yuv->coef_bcb.integer);
+ regw_ip(base_addr, val, YUV_MUL_BCB);
+ val = IPIPE_S12Q8(yuv->coef_rcr.decimal, yuv->coef_rcr.integer);
+ regw_ip(base_addr, val, YUV_MUL_RCR);
+ val = IPIPE_S12Q8(yuv->coef_gcr.decimal, yuv->coef_gcr.integer);
+ regw_ip(base_addr, val, YUV_MUL_GCR);
+ val = IPIPE_S12Q8(yuv->coef_bcr.decimal, yuv->coef_bcr.integer);
+ regw_ip(base_addr, val, YUV_MUL_BCR);
+ regw_ip(base_addr, yuv->out_ofst_y & RGB2YCBCR_OFST_MASK, YUV_OFT_Y);
+ regw_ip(base_addr, yuv->out_ofst_cb & RGB2YCBCR_OFST_MASK, YUV_OFT_CB);
+ regw_ip(base_addr, yuv->out_ofst_cr & RGB2YCBCR_OFST_MASK, YUV_OFT_CR);
+}
+
+/* YUV 422 conversion */
+void
+ipipe_set_yuv422_conv_regs(void __iomem *base_addr,
+ struct vpfe_ipipe_yuv422_conv *conv)
+{
+ u32 val;
+
+ ipipe_clock_enable(base_addr);
+
+ /* Combine all the fields to make YUV_PHS register of IPIPE */
+ val = (conv->chrom_pos << 0) | (conv->en_chrom_lpf << 1);
+ regw_ip(base_addr, val, YUV_PHS);
+}
+
+void
+ipipe_set_gbce_regs(void __iomem *base_addr, void __iomem *isp5_base_addr,
+ struct vpfe_ipipe_gbce *gbce)
+{
+ unsigned int count;
+ u32 mask = GBCE_Y_VAL_MASK;
+
+ if (gbce->type == VPFE_IPIPE_GBCE_GAIN_TBL)
+ mask = GBCE_GAIN_VAL_MASK;
+
+ ipipe_clock_enable(base_addr);
+ regw_ip(base_addr, gbce->en & 1, GBCE_EN);
+
+ if (!gbce->en)
+ return;
+
+ regw_ip(base_addr, gbce->type, GBCE_TYP);
+
+ if (!gbce->table)
+ return;
+
+ for (count = 0; count < VPFE_IPIPE_MAX_SIZE_GBCE_LUT; count += 2)
+ w_ip_table(isp5_base_addr, ((gbce->table[count + 1] & mask) <<
+ GBCE_ENTRY_SHIFT) | (gbce->table[count] & mask),
+ ((count/2) << 2) + GBCE_TB_START_ADDR);
+}
+
+void
+ipipe_set_ee_regs(void __iomem *base_addr, void __iomem *isp5_base_addr,
+ struct vpfe_ipipe_yee *ee)
+{
+ unsigned int count;
+ u32 val;
+
+ ipipe_clock_enable(base_addr);
+ regw_ip(base_addr, ee->en, YEE_EN);
+
+ if (!ee->en)
+ return;
+
+ val = ee->en_halo_red & 1;
+ val |= ee->merge_meth << YEE_HALO_RED_EN_SHIFT;
+ regw_ip(base_addr, val, YEE_TYP);
+
+ regw_ip(base_addr, ee->hpf_shft, YEE_SHF);
+ regw_ip(base_addr, ee->hpf_coef_00 & YEE_COEF_MASK, YEE_MUL_00);
+ regw_ip(base_addr, ee->hpf_coef_01 & YEE_COEF_MASK, YEE_MUL_01);
+ regw_ip(base_addr, ee->hpf_coef_02 & YEE_COEF_MASK, YEE_MUL_02);
+ regw_ip(base_addr, ee->hpf_coef_10 & YEE_COEF_MASK, YEE_MUL_10);
+ regw_ip(base_addr, ee->hpf_coef_11 & YEE_COEF_MASK, YEE_MUL_11);
+ regw_ip(base_addr, ee->hpf_coef_12 & YEE_COEF_MASK, YEE_MUL_12);
+ regw_ip(base_addr, ee->hpf_coef_20 & YEE_COEF_MASK, YEE_MUL_20);
+ regw_ip(base_addr, ee->hpf_coef_21 & YEE_COEF_MASK, YEE_MUL_21);
+ regw_ip(base_addr, ee->hpf_coef_22 & YEE_COEF_MASK, YEE_MUL_22);
+ regw_ip(base_addr, ee->yee_thr & YEE_THR_MASK, YEE_THR);
+ regw_ip(base_addr, ee->es_gain & YEE_ES_GAIN_MASK, YEE_E_GAN);
+ regw_ip(base_addr, ee->es_thr1 & YEE_ES_THR1_MASK, YEE_E_THR1);
+ regw_ip(base_addr, ee->es_thr2 & YEE_THR_MASK, YEE_E_THR2);
+ regw_ip(base_addr, ee->es_gain_grad & YEE_THR_MASK, YEE_G_GAN);
+ regw_ip(base_addr, ee->es_ofst_grad & YEE_THR_MASK, YEE_G_OFT);
+
+ if (ee->table == NULL)
+ return;
+
+ for (count = 0; count < VPFE_IPIPE_MAX_SIZE_YEE_LUT; count += 2)
+ w_ip_table(isp5_base_addr, ((ee->table[count + 1] &
+ YEE_ENTRY_MASK) << YEE_ENTRY_SHIFT) |
+ (ee->table[count] & YEE_ENTRY_MASK),
+ ((count/2) << 2) + YEE_TB_START_ADDR);
+}
+
+/* Chromatic Artifact Correction. CAR */
+static void ipipe_set_mf(void __iomem *base_addr)
+{
+ /* typ to dynamic switch */
+ regw_ip(base_addr, VPFE_IPIPE_CAR_DYN_SWITCH, CAR_TYP);
+ /* Set SW0 to maximum */
+ regw_ip(base_addr, CAR_MF_THR, CAR_SW);
+}
+
+static void
+ipipe_set_gain_ctrl(void __iomem *base_addr, struct vpfe_ipipe_car *car)
+{
+ regw_ip(base_addr, VPFE_IPIPE_CAR_CHR_GAIN_CTRL, CAR_TYP);
+ regw_ip(base_addr, car->hpf, CAR_HPF_TYP);
+ regw_ip(base_addr, car->hpf_shft & CAR_HPF_SHIFT_MASK, CAR_HPF_SHF);
+ regw_ip(base_addr, car->hpf_thr, CAR_HPF_THR);
+ regw_ip(base_addr, car->gain1.gain, CAR_GN1_GAN);
+ regw_ip(base_addr, car->gain1.shft & CAR_GAIN1_SHFT_MASK, CAR_GN1_SHF);
+ regw_ip(base_addr, car->gain1.gain_min & CAR_GAIN_MIN_MASK,
+ CAR_GN1_MIN);
+ regw_ip(base_addr, car->gain2.gain, CAR_GN2_GAN);
+ regw_ip(base_addr, car->gain2.shft & CAR_GAIN2_SHFT_MASK, CAR_GN2_SHF);
+ regw_ip(base_addr, car->gain2.gain_min & CAR_GAIN_MIN_MASK,
+ CAR_GN2_MIN);
+}
+
+void ipipe_set_car_regs(void __iomem *base_addr, struct vpfe_ipipe_car *car)
+{
+ u32 val;
+
+ ipipe_clock_enable(base_addr);
+ regw_ip(base_addr, car->en, CAR_EN);
+
+ if (!car->en)
+ return;
+
+ switch (car->meth) {
+ case VPFE_IPIPE_CAR_MED_FLTR:
+ ipipe_set_mf(base_addr);
+ break;
+
+ case VPFE_IPIPE_CAR_CHR_GAIN_CTRL:
+ ipipe_set_gain_ctrl(base_addr, car);
+ break;
+
+ default:
+ /* Dynamic switch between MF and Gain Ctrl. */
+ ipipe_set_mf(base_addr);
+ ipipe_set_gain_ctrl(base_addr, car);
+ /* Set the threshold for switching between
+ * the two Here we overwrite the MF SW0 value
+ */
+ regw_ip(base_addr, VPFE_IPIPE_CAR_DYN_SWITCH, CAR_TYP);
+ val = car->sw1;
+ val <<= CAR_SW1_SHIFT;
+ val |= car->sw0;
+ regw_ip(base_addr, val, CAR_SW);
+ }
+}
+
+/* Chromatic Gain Suppression */
+void ipipe_set_cgs_regs(void __iomem *base_addr, struct vpfe_ipipe_cgs *cgs)
+{
+ ipipe_clock_enable(base_addr);
+ regw_ip(base_addr, cgs->en, CGS_EN);
+
+ if (!cgs->en)
+ return;
+
+ /* Set the bright side parameters */
+ regw_ip(base_addr, cgs->h_thr, CGS_GN1_H_THR);
+ regw_ip(base_addr, cgs->h_slope, CGS_GN1_H_GAN);
+ regw_ip(base_addr, cgs->h_shft & CAR_SHIFT_MASK, CGS_GN1_H_SHF);
+ regw_ip(base_addr, cgs->h_min, CGS_GN1_H_MIN);
+}
+
+void rsz_src_enable(void __iomem *rsz_base, int enable)
+{
+ regw_rsz(rsz_base, enable, RSZ_SRC_EN);
+}
+
+int rsz_enable(void __iomem *rsz_base, int rsz_id, int enable)
+{
+ if (rsz_id == RSZ_A) {
+ regw_rsz(rsz_base, enable, RSZ_EN_A);
+ /* We always enable RSZ_A. RSZ_B is enable upon request from
+ * application. So enable RSZ_SRC_EN along with RSZ_A
+ */
+ regw_rsz(rsz_base, enable, RSZ_SRC_EN);
+ } else if (rsz_id == RSZ_B) {
+ regw_rsz(rsz_base, enable, RSZ_EN_B);
+ } else {
+ BUG();
+ }
+
+ return 0;
+}
diff --git a/drivers/staging/media/davinci_vpfe/dm365_ipipe_hw.h b/drivers/staging/media/davinci_vpfe/dm365_ipipe_hw.h
new file mode 100644
index 000000000..2bf2f7a69
--- /dev/null
+++ b/drivers/staging/media/davinci_vpfe/dm365_ipipe_hw.h
@@ -0,0 +1,558 @@
+/*
+ * Copyright (C) 2012 Texas Instruments 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 version 2.
+ *
+ * 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
+ *
+ * Contributors:
+ * Manjunath Hadli <manjunath.hadli@ti.com>
+ * Prabhakar Lad <prabhakar.lad@ti.com>
+ */
+
+#ifndef _DAVINCI_VPFE_DM365_IPIPE_HW_H
+#define _DAVINCI_VPFE_DM365_IPIPE_HW_H
+
+#include "vpfe_mc_capture.h"
+
+#define SET_LOW_ADDR 0x0000ffff
+#define SET_HIGH_ADDR 0xffff0000
+
+/* Below are the internal tables */
+#define DPC_TB0_START_ADDR 0x8000
+#define DPC_TB1_START_ADDR 0x8400
+
+#define GAMMA_R_START_ADDR 0xa800
+#define GAMMA_G_START_ADDR 0xb000
+#define GAMMA_B_START_ADDR 0xb800
+
+/* RAM table addresses for edge enhancement correction*/
+#define YEE_TB_START_ADDR 0x8800
+
+/* RAM table address for GBC LUT */
+#define GBCE_TB_START_ADDR 0x9000
+
+/* RAM table for 3D NF LUT */
+#define D3L_TB0_START_ADDR 0x9800
+#define D3L_TB1_START_ADDR 0x9c00
+#define D3L_TB2_START_ADDR 0xa000
+#define D3L_TB3_START_ADDR 0xa400
+
+/* IPIPE Register Offsets from the base address */
+#define IPIPE_SRC_EN 0x0000
+#define IPIPE_SRC_MODE 0x0004
+#define IPIPE_SRC_FMT 0x0008
+#define IPIPE_SRC_COL 0x000c
+#define IPIPE_SRC_VPS 0x0010
+#define IPIPE_SRC_VSZ 0x0014
+#define IPIPE_SRC_HPS 0x0018
+#define IPIPE_SRC_HSZ 0x001c
+
+#define IPIPE_SEL_SBU 0x0020
+
+#define IPIPE_DMA_STA 0x0024
+#define IPIPE_GCK_MMR 0x0028
+#define IPIPE_GCK_PIX 0x002c
+#define IPIPE_RESERVED0 0x0030
+
+/* Defect Correction */
+#define DPC_LUT_EN 0x0034
+#define DPC_LUT_SEL 0x0038
+#define DPC_LUT_ADR 0x003c
+#define DPC_LUT_SIZ 0x0040
+#define DPC_OTF_EN 0x0044
+#define DPC_OTF_TYP 0x0048
+#define DPC_OTF_2D_THR_R 0x004c
+#define DPC_OTF_2D_THR_GR 0x0050
+#define DPC_OTF_2D_THR_GB 0x0054
+#define DPC_OTF_2D_THR_B 0x0058
+#define DPC_OTF_2C_THR_R 0x005c
+#define DPC_OTF_2C_THR_GR 0x0060
+#define DPC_OTF_2C_THR_GB 0x0064
+#define DPC_OTF_2C_THR_B 0x0068
+#define DPC_OTF_3_SHF 0x006c
+#define DPC_OTF_3D_THR 0x0070
+#define DPC_OTF_3D_SLP 0x0074
+#define DPC_OTF_3D_MIN 0x0078
+#define DPC_OTF_3D_MAX 0x007c
+#define DPC_OTF_3C_THR 0x0080
+#define DPC_OTF_3C_SLP 0x0084
+#define DPC_OTF_3C_MIN 0x0088
+#define DPC_OTF_3C_MAX 0x008c
+
+/* Lense Shading Correction */
+#define LSC_VOFT 0x90
+#define LSC_VA2 0x94
+#define LSC_VA1 0x98
+#define LSC_VS 0x9c
+#define LSC_HOFT 0xa0
+#define LSC_HA2 0xa4
+#define LSC_HA1 0xa8
+#define LSC_HS 0xac
+#define LSC_GAIN_R 0xb0
+#define LSC_GAIN_GR 0xb4
+#define LSC_GAIN_GB 0xb8
+#define LSC_GAIN_B 0xbc
+#define LSC_OFT_R 0xc0
+#define LSC_OFT_GR 0xc4
+#define LSC_OFT_GB 0xc8
+#define LSC_OFT_B 0xcc
+#define LSC_SHF 0xd0
+#define LSC_MAX 0xd4
+
+/* Noise Filter 1. Ofsets from start address given */
+#define D2F_1ST 0xd8
+#define D2F_EN 0x0
+#define D2F_TYP 0x4
+#define D2F_THR 0x8
+#define D2F_STR 0x28
+#define D2F_SPR 0x48
+#define D2F_EDG_MIN 0x68
+#define D2F_EDG_MAX 0x6c
+
+/* Noise Filter 2 */
+#define D2F_2ND 0x148
+
+/* GIC */
+#define GIC_EN 0x1b8
+#define GIC_TYP 0x1bc
+#define GIC_GAN 0x1c0
+#define GIC_NFGAN 0x1c4
+#define GIC_THR 0x1c8
+#define GIC_SLP 0x1cc
+
+/* White Balance */
+#define WB2_OFT_R 0x1d0
+#define WB2_OFT_GR 0x1d4
+#define WB2_OFT_GB 0x1d8
+#define WB2_OFT_B 0x1dc
+#define WB2_WGN_R 0x1e0
+#define WB2_WGN_GR 0x1e4
+#define WB2_WGN_GB 0x1e8
+#define WB2_WGN_B 0x1ec
+
+/* CFA interpolation */
+#define CFA_MODE 0x1f0
+#define CFA_2DIR_HPF_THR 0x1f4
+#define CFA_2DIR_HPF_SLP 0x1f8
+#define CFA_2DIR_MIX_THR 0x1fc
+#define CFA_2DIR_MIX_SLP 0x200
+#define CFA_2DIR_DIR_THR 0x204
+#define CFA_2DIR_DIR_SLP 0x208
+#define CFA_2DIR_NDWT 0x20c
+#define CFA_MONO_HUE_FRA 0x210
+#define CFA_MONO_EDG_THR 0x214
+#define CFA_MONO_THR_MIN 0x218
+#define CFA_MONO_THR_SLP 0x21c
+#define CFA_MONO_SLP_MIN 0x220
+#define CFA_MONO_SLP_SLP 0x224
+#define CFA_MONO_LPWT 0x228
+
+/* RGB to RGB conversiona - 1st */
+#define RGB1_MUL_BASE 0x22c
+/* Offsets from base */
+#define RGB_MUL_RR 0x0
+#define RGB_MUL_GR 0x4
+#define RGB_MUL_BR 0x8
+#define RGB_MUL_RG 0xc
+#define RGB_MUL_GG 0x10
+#define RGB_MUL_BG 0x14
+#define RGB_MUL_RB 0x18
+#define RGB_MUL_GB 0x1c
+#define RGB_MUL_BB 0x20
+#define RGB_OFT_OR 0x24
+#define RGB_OFT_OG 0x28
+#define RGB_OFT_OB 0x2c
+
+/* Gamma */
+#define GMM_CFG 0x25c
+
+/* RGB to RGB conversiona - 2nd */
+#define RGB2_MUL_BASE 0x260
+
+/* 3D LUT */
+#define D3LUT_EN 0x290
+
+/* RGB to YUV(YCbCr) conversion */
+#define YUV_ADJ 0x294
+#define YUV_MUL_RY 0x298
+#define YUV_MUL_GY 0x29c
+#define YUV_MUL_BY 0x2a0
+#define YUV_MUL_RCB 0x2a4
+#define YUV_MUL_GCB 0x2a8
+#define YUV_MUL_BCB 0x2ac
+#define YUV_MUL_RCR 0x2b0
+#define YUV_MUL_GCR 0x2b4
+#define YUV_MUL_BCR 0x2b8
+#define YUV_OFT_Y 0x2bc
+#define YUV_OFT_CB 0x2c0
+#define YUV_OFT_CR 0x2c4
+#define YUV_PHS 0x2c8
+
+/* Global Brightness and Contrast */
+#define GBCE_EN 0x2cc
+#define GBCE_TYP 0x2d0
+
+/* Edge Enhancer */
+#define YEE_EN 0x2d4
+#define YEE_TYP 0x2d8
+#define YEE_SHF 0x2dc
+#define YEE_MUL_00 0x2e0
+#define YEE_MUL_01 0x2e4
+#define YEE_MUL_02 0x2e8
+#define YEE_MUL_10 0x2ec
+#define YEE_MUL_11 0x2f0
+#define YEE_MUL_12 0x2f4
+#define YEE_MUL_20 0x2f8
+#define YEE_MUL_21 0x2fc
+#define YEE_MUL_22 0x300
+#define YEE_THR 0x304
+#define YEE_E_GAN 0x308
+#define YEE_E_THR1 0x30c
+#define YEE_E_THR2 0x310
+#define YEE_G_GAN 0x314
+#define YEE_G_OFT 0x318
+
+/* Chroma Artifact Reduction */
+#define CAR_EN 0x31c
+#define CAR_TYP 0x320
+#define CAR_SW 0x324
+#define CAR_HPF_TYP 0x328
+#define CAR_HPF_SHF 0x32c
+#define CAR_HPF_THR 0x330
+#define CAR_GN1_GAN 0x334
+#define CAR_GN1_SHF 0x338
+#define CAR_GN1_MIN 0x33c
+#define CAR_GN2_GAN 0x340
+#define CAR_GN2_SHF 0x344
+#define CAR_GN2_MIN 0x348
+
+/* Chroma Gain Suppression */
+#define CGS_EN 0x34c
+#define CGS_GN1_L_THR 0x350
+#define CGS_GN1_L_GAN 0x354
+#define CGS_GN1_L_SHF 0x358
+#define CGS_GN1_L_MIN 0x35c
+#define CGS_GN1_H_THR 0x360
+#define CGS_GN1_H_GAN 0x364
+#define CGS_GN1_H_SHF 0x368
+#define CGS_GN1_H_MIN 0x36c
+#define CGS_GN2_L_THR 0x370
+#define CGS_GN2_L_GAN 0x374
+#define CGS_GN2_L_SHF 0x378
+#define CGS_GN2_L_MIN 0x37c
+
+/* Resizer */
+#define RSZ_SRC_EN 0x0
+#define RSZ_SRC_MODE 0x4
+#define RSZ_SRC_FMT0 0x8
+#define RSZ_SRC_FMT1 0xc
+#define RSZ_SRC_VPS 0x10
+#define RSZ_SRC_VSZ 0x14
+#define RSZ_SRC_HPS 0x18
+#define RSZ_SRC_HSZ 0x1c
+#define RSZ_DMA_RZA 0x20
+#define RSZ_DMA_RZB 0x24
+#define RSZ_DMA_STA 0x28
+#define RSZ_GCK_MMR 0x2c
+#define RSZ_RESERVED0 0x30
+#define RSZ_GCK_SDR 0x34
+#define RSZ_IRQ_RZA 0x38
+#define RSZ_IRQ_RZB 0x3c
+#define RSZ_YUV_Y_MIN 0x40
+#define RSZ_YUV_Y_MAX 0x44
+#define RSZ_YUV_C_MIN 0x48
+#define RSZ_YUV_C_MAX 0x4c
+#define RSZ_YUV_PHS 0x50
+#define RSZ_SEQ 0x54
+
+/* Resizer Rescale Parameters */
+#define RSZ_EN_A 0x58
+#define RSZ_EN_B 0xe8
+/* offset of the registers to be added with base register of
+ either RSZ0 or RSZ1
+*/
+#define RSZ_MODE 0x4
+#define RSZ_420 0x8
+#define RSZ_I_VPS 0xc
+#define RSZ_I_HPS 0x10
+#define RSZ_O_VSZ 0x14
+#define RSZ_O_HSZ 0x18
+#define RSZ_V_PHS_Y 0x1c
+#define RSZ_V_PHS_C 0x20
+#define RSZ_V_DIF 0x24
+#define RSZ_V_TYP 0x28
+#define RSZ_V_LPF 0x2c
+#define RSZ_H_PHS 0x30
+#define RSZ_H_PHS_ADJ 0x34
+#define RSZ_H_DIF 0x38
+#define RSZ_H_TYP 0x3c
+#define RSZ_H_LPF 0x40
+#define RSZ_DWN_EN 0x44
+#define RSZ_DWN_AV 0x48
+
+/* Resizer RGB Conversion Parameters */
+#define RSZ_RGB_EN 0x4c
+#define RSZ_RGB_TYP 0x50
+#define RSZ_RGB_BLD 0x54
+
+/* Resizer External Memory Parameters */
+#define RSZ_SDR_Y_BAD_H 0x58
+#define RSZ_SDR_Y_BAD_L 0x5c
+#define RSZ_SDR_Y_SAD_H 0x60
+#define RSZ_SDR_Y_SAD_L 0x64
+#define RSZ_SDR_Y_OFT 0x68
+#define RSZ_SDR_Y_PTR_S 0x6c
+#define RSZ_SDR_Y_PTR_E 0x70
+#define RSZ_SDR_C_BAD_H 0x74
+#define RSZ_SDR_C_BAD_L 0x78
+#define RSZ_SDR_C_SAD_H 0x7c
+#define RSZ_SDR_C_SAD_L 0x80
+#define RSZ_SDR_C_OFT 0x84
+#define RSZ_SDR_C_PTR_S 0x88
+#define RSZ_SDR_C_PTR_E 0x8c
+
+/* Macro for resizer */
+#define RSZ_YUV_Y_MIN 0x40
+#define RSZ_YUV_Y_MAX 0x44
+#define RSZ_YUV_C_MIN 0x48
+#define RSZ_YUV_C_MAX 0x4c
+
+#define IPIPE_GCK_MMR_DEFAULT 1
+#define IPIPE_GCK_PIX_DEFAULT 0xe
+#define RSZ_GCK_MMR_DEFAULT 1
+#define RSZ_GCK_SDR_DEFAULT 1
+
+/* LUTDPC */
+#define LUTDPC_TBL_256_EN 0
+#define LUTDPC_INF_TBL_EN 1
+#define LUT_DPC_START_ADDR 0
+#define LUT_DPC_H_POS_MASK 0x1fff
+#define LUT_DPC_V_POS_MASK 0x1fff
+#define LUT_DPC_V_POS_SHIFT 13
+#define LUT_DPC_CORR_METH_SHIFT 26
+#define LUT_DPC_MAX_SIZE 256
+#define LUT_DPC_SIZE_MASK 0x3ff
+
+/* OTFDPC */
+#define OTFDPC_DPC2_THR_MASK 0xfff
+#define OTF_DET_METHOD_SHIFT 1
+#define OTF_DPC3_0_SHF_MASK 3
+#define OTF_DPC3_0_THR_SHIFT 6
+#define OTF_DPC3_0_THR_MASK 0x3f
+#define OTF_DPC3_0_SLP_MASK 0x3f
+#define OTF_DPC3_0_DET_MASK 0xfff
+#define OTF_DPC3_0_CORR_MASK 0xfff
+
+/* NF (D2F) */
+#define D2F_SPR_VAL_MASK 0x1f
+#define D2F_SPR_VAL_SHIFT 0
+#define D2F_SHFT_VAL_MASK 3
+#define D2F_SHFT_VAL_SHIFT 5
+#define D2F_SAMPLE_METH_SHIFT 7
+#define D2F_APPLY_LSC_GAIN_SHIFT 8
+#define D2F_USE_SPR_REG_VAL 0
+#define D2F_STR_VAL_MASK 0x1f
+#define D2F_THR_VAL_MASK 0x3ff
+#define D2F_EDGE_DET_THR_MASK 0x7ff
+
+/* Green Imbalance Correction */
+#define GIC_TYP_SHIFT 0
+#define GIC_THR_SEL_SHIFT 1
+#define GIC_APPLY_LSC_GAIN_SHIFT 2
+#define GIC_GAIN_MASK 0xff
+#define GIC_THR_MASK 0xfff
+#define GIC_SLOPE_MASK 0xfff
+#define GIC_NFGAN_INT_MASK 7
+#define GIC_NFGAN_DECI_MASK 0x1f
+
+/* WB */
+#define WB_OFFSET_MASK 0xfff
+#define WB_GAIN_INT_MASK 0xf
+#define WB_GAIN_DECI_MASK 0x1ff
+
+/* CFA */
+#define CFA_HPF_THR_2DIR_MASK 0x1fff
+#define CFA_HPF_SLOPE_2DIR_MASK 0x3ff
+#define CFA_HPF_MIX_THR_2DIR_MASK 0x1fff
+#define CFA_HPF_MIX_SLP_2DIR_MASK 0x3ff
+#define CFA_DIR_THR_2DIR_MASK 0x3ff
+#define CFA_DIR_SLP_2DIR_MASK 0x7f
+#define CFA_ND_WT_2DIR_MASK 0x3f
+#define CFA_DAA_HUE_FRA_MASK 0x3f
+#define CFA_DAA_EDG_THR_MASK 0xff
+#define CFA_DAA_THR_MIN_MASK 0x3ff
+#define CFA_DAA_THR_SLP_MASK 0x3ff
+#define CFA_DAA_SLP_MIN_MASK 0x3ff
+#define CFA_DAA_SLP_SLP_MASK 0x3ff
+#define CFA_DAA_LP_WT_MASK 0x3f
+
+/* RGB2RGB */
+#define RGB2RGB_1_OFST_MASK 0x1fff
+#define RGB2RGB_1_GAIN_INT_MASK 0xf
+#define RGB2RGB_GAIN_DECI_MASK 0xff
+#define RGB2RGB_2_OFST_MASK 0x7ff
+#define RGB2RGB_2_GAIN_INT_MASK 0x7
+
+/* Gamma */
+#define GAMMA_BYPR_SHIFT 0
+#define GAMMA_BYPG_SHIFT 1
+#define GAMMA_BYPB_SHIFT 2
+#define GAMMA_TBL_SEL_SHIFT 4
+#define GAMMA_TBL_SIZE_SHIFT 5
+#define GAMMA_MASK 0x3ff
+#define GAMMA_SHIFT 10
+
+/* 3D LUT */
+#define D3_LUT_ENTRY_MASK 0x3ff
+#define D3_LUT_ENTRY_R_SHIFT 20
+#define D3_LUT_ENTRY_G_SHIFT 10
+#define D3_LUT_ENTRY_B_SHIFT 0
+
+/* Lumina adj */
+#define LUM_ADJ_CONTR_SHIFT 0
+#define LUM_ADJ_BRIGHT_SHIFT 8
+
+/* RGB2YCbCr */
+#define RGB2YCBCR_OFST_MASK 0x7ff
+#define RGB2YCBCR_COEF_INT_MASK 0xf
+#define RGB2YCBCR_COEF_DECI_MASK 0xff
+
+/* GBCE */
+#define GBCE_Y_VAL_MASK 0xff
+#define GBCE_GAIN_VAL_MASK 0x3ff
+#define GBCE_ENTRY_SHIFT 10
+
+/* Edge Enhancements */
+#define YEE_HALO_RED_EN_SHIFT 1
+#define YEE_HPF_SHIFT_MASK 0xf
+#define YEE_COEF_MASK 0x3ff
+#define YEE_THR_MASK 0x3f
+#define YEE_ES_GAIN_MASK 0xfff
+#define YEE_ES_THR1_MASK 0xfff
+#define YEE_ENTRY_SHIFT 9
+#define YEE_ENTRY_MASK 0x1ff
+
+/* CAR */
+#define CAR_MF_THR 0xff
+#define CAR_SW1_SHIFT 8
+#define CAR_GAIN1_SHFT_MASK 7
+#define CAR_GAIN_MIN_MASK 0x1ff
+#define CAR_GAIN2_SHFT_MASK 0xf
+#define CAR_HPF_SHIFT_MASK 3
+
+/* CGS */
+#define CAR_SHIFT_MASK 3
+
+/* Resizer */
+#define RSZ_BYPASS_SHIFT 1
+#define RSZ_SRC_IMG_FMT_SHIFT 1
+#define RSZ_SRC_Y_C_SEL_SHIFT 2
+#define IPIPE_RSZ_VPS_MASK 0xffff
+#define IPIPE_RSZ_HPS_MASK 0xffff
+#define IPIPE_RSZ_VSZ_MASK 0x1fff
+#define IPIPE_RSZ_HSZ_MASK 0x1fff
+#define RSZ_HPS_MASK 0x1fff
+#define RSZ_VPS_MASK 0x1fff
+#define RSZ_O_HSZ_MASK 0x1fff
+#define RSZ_O_VSZ_MASK 0x1fff
+#define RSZ_V_PHS_MASK 0x3fff
+#define RSZ_V_DIF_MASK 0x3fff
+
+#define RSZA_H_FLIP_SHIFT 0
+#define RSZA_V_FLIP_SHIFT 1
+#define RSZB_H_FLIP_SHIFT 2
+#define RSZB_V_FLIP_SHIFT 3
+#define RSZ_A 0
+#define RSZ_B 1
+#define RSZ_CEN_SHIFT 1
+#define RSZ_YEN_SHIFT 0
+#define RSZ_TYP_Y_SHIFT 0
+#define RSZ_TYP_C_SHIFT 1
+#define RSZ_LPF_INT_MASK 0x3f
+#define RSZ_LPF_INT_C_SHIFT 6
+#define RSZ_H_PHS_MASK 0x3fff
+#define RSZ_H_DIF_MASK 0x3fff
+#define RSZ_DIFF_DOWN_THR 256
+#define RSZ_DWN_SCALE_AV_SZ_V_SHIFT 3
+#define RSZ_DWN_SCALE_AV_SZ_MASK 7
+#define RSZ_RGB_MSK1_SHIFT 2
+#define RSZ_RGB_MSK0_SHIFT 1
+#define RSZ_RGB_TYP_SHIFT 0
+#define RSZ_RGB_ALPHA_MASK 0xff
+
+static inline u32 regr_ip(void __iomem *addr, u32 offset)
+{
+ return readl(addr + offset);
+}
+
+static inline void regw_ip(void __iomem *addr, u32 val, u32 offset)
+{
+ writel(val, addr + offset);
+}
+
+static inline u32 w_ip_table(void __iomem *addr, u32 val, u32 offset)
+{
+ writel(val, addr + offset);
+
+ return val;
+}
+
+static inline u32 regr_rsz(void __iomem *addr, u32 offset)
+{
+ return readl(addr + offset);
+}
+
+static inline u32 regw_rsz(void __iomem *addr, u32 val, u32 offset)
+{
+ writel(val, addr + offset);
+
+ return val;
+}
+
+int config_ipipe_hw(struct vpfe_ipipe_device *ipipe);
+int resizer_set_outaddr(void __iomem *rsz_base, struct resizer_params *params,
+ int resize_no, unsigned int address);
+int rsz_enable(void __iomem *rsz_base, int rsz_id, int enable);
+void rsz_src_enable(void __iomem *rsz_base, int enable);
+void rsz_set_in_pix_format(unsigned char y_c);
+int config_rsz_hw(struct vpfe_resizer_device *resizer,
+ struct resizer_params *config);
+void ipipe_set_d2f_regs(void __iomem *base_addr, unsigned int id,
+ struct vpfe_ipipe_nf *noise_filter);
+void ipipe_set_rgb2rgb_regs(void __iomem *base_addr, unsigned int id,
+ struct vpfe_ipipe_rgb2rgb *rgb);
+void ipipe_set_yuv422_conv_regs(void __iomem *base_addr,
+ struct vpfe_ipipe_yuv422_conv *conv);
+void ipipe_set_lum_adj_regs(void __iomem *base_addr,
+ struct ipipe_lum_adj *lum_adj);
+void ipipe_set_rgb2ycbcr_regs(void __iomem *base_addr,
+ struct vpfe_ipipe_rgb2yuv *yuv);
+void ipipe_set_lutdpc_regs(void __iomem *base_addr,
+ void __iomem *isp5_base_addr, struct vpfe_ipipe_lutdpc *lutdpc);
+void ipipe_set_otfdpc_regs(void __iomem *base_addr,
+ struct vpfe_ipipe_otfdpc *otfdpc);
+void ipipe_set_3d_lut_regs(void __iomem *base_addr,
+ void __iomem *isp5_base_addr, struct vpfe_ipipe_3d_lut *lut_3d);
+void ipipe_set_gamma_regs(void __iomem *base_addr,
+ void __iomem *isp5_base_addr, struct vpfe_ipipe_gamma *gamma);
+void ipipe_set_ee_regs(void __iomem *base_addr,
+ void __iomem *isp5_base_addr, struct vpfe_ipipe_yee *ee);
+void ipipe_set_gbce_regs(void __iomem *base_addr,
+ void __iomem *isp5_base_addr, struct vpfe_ipipe_gbce *gbce);
+void ipipe_set_gic_regs(void __iomem *base_addr, struct vpfe_ipipe_gic *gic);
+void ipipe_set_cfa_regs(void __iomem *base_addr, struct vpfe_ipipe_cfa *cfa);
+void ipipe_set_car_regs(void __iomem *base_addr, struct vpfe_ipipe_car *car);
+void ipipe_set_cgs_regs(void __iomem *base_addr, struct vpfe_ipipe_cgs *cgs);
+void ipipe_set_wb_regs(void __iomem *base_addr, struct vpfe_ipipe_wb *wb);
+
+#endif /* _DAVINCI_VPFE_DM365_IPIPE_HW_H */
diff --git a/drivers/staging/media/davinci_vpfe/dm365_ipipeif.c b/drivers/staging/media/davinci_vpfe/dm365_ipipeif.c
new file mode 100644
index 000000000..8b230541b
--- /dev/null
+++ b/drivers/staging/media/davinci_vpfe/dm365_ipipeif.c
@@ -0,0 +1,1067 @@
+/*
+ * Copyright (C) 2012 Texas Instruments 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 version 2.
+ *
+ * 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
+ *
+ * Contributors:
+ * Manjunath Hadli <manjunath.hadli@ti.com>
+ * Prabhakar Lad <prabhakar.lad@ti.com>
+ */
+
+#include "dm365_ipipeif.h"
+#include "vpfe_mc_capture.h"
+
+static const unsigned int ipipeif_input_fmts[] = {
+ MEDIA_BUS_FMT_UYVY8_2X8,
+ MEDIA_BUS_FMT_SGRBG12_1X12,
+ MEDIA_BUS_FMT_Y8_1X8,
+ MEDIA_BUS_FMT_UV8_1X8,
+ MEDIA_BUS_FMT_YDYUYDYV8_1X16,
+ MEDIA_BUS_FMT_SBGGR8_1X8,
+};
+
+static const unsigned int ipipeif_output_fmts[] = {
+ MEDIA_BUS_FMT_UYVY8_2X8,
+ MEDIA_BUS_FMT_SGRBG12_1X12,
+ MEDIA_BUS_FMT_Y8_1X8,
+ MEDIA_BUS_FMT_UV8_1X8,
+ MEDIA_BUS_FMT_YDYUYDYV8_1X16,
+ MEDIA_BUS_FMT_SBGGR8_1X8,
+ MEDIA_BUS_FMT_SGRBG10_DPCM8_1X8,
+ MEDIA_BUS_FMT_SGRBG10_ALAW8_1X8,
+};
+
+static int
+ipipeif_get_pack_mode(u32 in_pix_fmt)
+{
+ switch (in_pix_fmt) {
+ case MEDIA_BUS_FMT_SBGGR8_1X8:
+ case MEDIA_BUS_FMT_Y8_1X8:
+ case MEDIA_BUS_FMT_SGRBG10_DPCM8_1X8:
+ case MEDIA_BUS_FMT_UV8_1X8:
+ return IPIPEIF_5_1_PACK_8_BIT;
+
+ case MEDIA_BUS_FMT_SGRBG10_ALAW8_1X8:
+ return IPIPEIF_5_1_PACK_8_BIT_A_LAW;
+
+ case MEDIA_BUS_FMT_SGRBG12_1X12:
+ return IPIPEIF_5_1_PACK_16_BIT;
+
+ case MEDIA_BUS_FMT_SBGGR12_1X12:
+ return IPIPEIF_5_1_PACK_12_BIT;
+
+ default:
+ return IPIPEIF_5_1_PACK_16_BIT;
+ }
+}
+
+static inline u32 ipipeif_read(void *addr, u32 offset)
+{
+ return readl(addr + offset);
+}
+
+static inline void ipipeif_write(u32 val, void *addr, u32 offset)
+{
+ writel(val, addr + offset);
+}
+
+static void ipipeif_config_dpc(void *addr, struct ipipeif_dpc *dpc)
+{
+ u32 val = 0;
+
+ if (dpc->en) {
+ val = (dpc->en & 1) << IPIPEIF_DPC2_EN_SHIFT;
+ val |= dpc->thr & IPIPEIF_DPC2_THR_MASK;
+ }
+ ipipeif_write(val, addr, IPIPEIF_DPC2);
+}
+
+#define IPIPEIF_MODE_CONTINUOUS 0
+#define IPIPEIF_MODE_ONE_SHOT 1
+
+static int get_oneshot_mode(enum ipipeif_input_entity input)
+{
+ if (input == IPIPEIF_INPUT_MEMORY)
+ return IPIPEIF_MODE_ONE_SHOT;
+ else if (input == IPIPEIF_INPUT_ISIF)
+ return IPIPEIF_MODE_CONTINUOUS;
+
+ return -EINVAL;
+}
+
+static int
+ipipeif_get_cfg_src1(struct vpfe_ipipeif_device *ipipeif)
+{
+ struct v4l2_mbus_framefmt *informat;
+
+ informat = &ipipeif->formats[IPIPEIF_PAD_SINK];
+ if (ipipeif->input == IPIPEIF_INPUT_MEMORY &&
+ (informat->code == MEDIA_BUS_FMT_Y8_1X8 ||
+ informat->code == MEDIA_BUS_FMT_UV8_1X8))
+ return IPIPEIF_CCDC;
+
+ return IPIPEIF_SRC1_PARALLEL_PORT;
+}
+
+static int
+ipipeif_get_data_shift(struct vpfe_ipipeif_device *ipipeif)
+{
+ struct v4l2_mbus_framefmt *informat;
+
+ informat = &ipipeif->formats[IPIPEIF_PAD_SINK];
+
+ switch (informat->code) {
+ case MEDIA_BUS_FMT_SGRBG12_1X12:
+ return IPIPEIF_5_1_BITS11_0;
+
+ case MEDIA_BUS_FMT_Y8_1X8:
+ case MEDIA_BUS_FMT_UV8_1X8:
+ return IPIPEIF_5_1_BITS11_0;
+
+ default:
+ return IPIPEIF_5_1_BITS7_0;
+ }
+}
+
+static enum ipipeif_input_source
+ipipeif_get_source(struct vpfe_ipipeif_device *ipipeif)
+{
+ struct v4l2_mbus_framefmt *informat;
+
+ informat = &ipipeif->formats[IPIPEIF_PAD_SINK];
+ if (ipipeif->input == IPIPEIF_INPUT_ISIF)
+ return IPIPEIF_CCDC;
+
+ if (informat->code == MEDIA_BUS_FMT_UYVY8_2X8)
+ return IPIPEIF_SDRAM_YUV;
+
+ return IPIPEIF_SDRAM_RAW;
+}
+
+void vpfe_ipipeif_ss_buffer_isr(struct vpfe_ipipeif_device *ipipeif)
+{
+ struct vpfe_video_device *video_in = &ipipeif->video_in;
+
+ if (ipipeif->input != IPIPEIF_INPUT_MEMORY)
+ return;
+
+ spin_lock(&video_in->dma_queue_lock);
+ vpfe_video_process_buffer_complete(video_in);
+ video_in->state = VPFE_VIDEO_BUFFER_NOT_QUEUED;
+ vpfe_video_schedule_next_buffer(video_in);
+ spin_unlock(&video_in->dma_queue_lock);
+}
+
+int vpfe_ipipeif_decimation_enabled(struct vpfe_device *vpfe_dev)
+{
+ struct vpfe_ipipeif_device *ipipeif = &vpfe_dev->vpfe_ipipeif;
+
+ return ipipeif->config.decimation;
+}
+
+int vpfe_ipipeif_get_rsz(struct vpfe_device *vpfe_dev)
+{
+ struct vpfe_ipipeif_device *ipipeif = &vpfe_dev->vpfe_ipipeif;
+
+ return ipipeif->config.rsz;
+}
+
+#define RD_DATA_15_2 0x7
+
+/*
+ * ipipeif_hw_setup() - This function sets up IPIPEIF
+ * @sd: pointer to v4l2 subdev structure
+ * return -EINVAL or zero on success
+ */
+static int ipipeif_hw_setup(struct v4l2_subdev *sd)
+{
+ struct vpfe_ipipeif_device *ipipeif = v4l2_get_subdevdata(sd);
+ struct v4l2_mbus_framefmt *informat, *outformat;
+ struct ipipeif_params params = ipipeif->config;
+ enum ipipeif_input_source ipipeif_source;
+ u32 isif_port_if;
+ void *ipipeif_base_addr;
+ unsigned int val;
+ int data_shift;
+ int pack_mode;
+ int source1;
+ int tmp;
+
+ ipipeif_base_addr = ipipeif->ipipeif_base_addr;
+
+ /* Enable clock to IPIPEIF and IPIPE */
+ vpss_enable_clock(VPSS_IPIPEIF_CLOCK, 1);
+
+ informat = &ipipeif->formats[IPIPEIF_PAD_SINK];
+ outformat = &ipipeif->formats[IPIPEIF_PAD_SOURCE];
+
+ /* Combine all the fields to make CFG1 register of IPIPEIF */
+ tmp = val = get_oneshot_mode(ipipeif->input);
+ if (tmp < 0) {
+ dev_err(&sd->devnode->dev, "ipipeif: links setup required");
+ return -EINVAL;
+ }
+ val <<= ONESHOT_SHIFT;
+
+ ipipeif_source = ipipeif_get_source(ipipeif);
+ val |= ipipeif_source << INPSRC_SHIFT;
+
+ val |= params.clock_select << CLKSEL_SHIFT;
+ val |= params.avg_filter << AVGFILT_SHIFT;
+ val |= params.decimation << DECIM_SHIFT;
+
+ pack_mode = ipipeif_get_pack_mode(informat->code);
+ val |= pack_mode << PACK8IN_SHIFT;
+
+ source1 = ipipeif_get_cfg_src1(ipipeif);
+ val |= source1 << INPSRC1_SHIFT;
+
+ data_shift = ipipeif_get_data_shift(ipipeif);
+ if (ipipeif_source != IPIPEIF_SDRAM_YUV)
+ val |= data_shift << DATASFT_SHIFT;
+ else
+ val &= ~(RD_DATA_15_2 << DATASFT_SHIFT);
+
+ ipipeif_write(val, ipipeif_base_addr, IPIPEIF_CFG1);
+
+ switch (ipipeif_source) {
+ case IPIPEIF_CCDC:
+ ipipeif_write(ipipeif->gain, ipipeif_base_addr, IPIPEIF_GAIN);
+ break;
+
+ case IPIPEIF_SDRAM_RAW:
+ case IPIPEIF_CCDC_DARKFM:
+ ipipeif_write(ipipeif->gain, ipipeif_base_addr, IPIPEIF_GAIN);
+ /* fall through */
+ case IPIPEIF_SDRAM_YUV:
+ val |= data_shift << DATASFT_SHIFT;
+ ipipeif_write(params.ppln, ipipeif_base_addr, IPIPEIF_PPLN);
+ ipipeif_write(params.lpfr, ipipeif_base_addr, IPIPEIF_LPFR);
+ ipipeif_write(informat->width, ipipeif_base_addr, IPIPEIF_HNUM);
+ ipipeif_write(informat->height,
+ ipipeif_base_addr, IPIPEIF_VNUM);
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ /*check if decimation is enable or not */
+ if (params.decimation)
+ ipipeif_write(params.rsz, ipipeif_base_addr, IPIPEIF_RSZ);
+
+ /* Setup sync alignment and initial rsz position */
+ val = params.if_5_1.align_sync & 1;
+ val <<= IPIPEIF_INIRSZ_ALNSYNC_SHIFT;
+ val |= params.if_5_1.rsz_start & IPIPEIF_INIRSZ_MASK;
+ ipipeif_write(val, ipipeif_base_addr, IPIPEIF_INIRSZ);
+ isif_port_if = informat->code;
+
+ if (isif_port_if == MEDIA_BUS_FMT_Y8_1X8)
+ isif_port_if = MEDIA_BUS_FMT_YUYV8_1X16;
+ else if (isif_port_if == MEDIA_BUS_FMT_UV8_1X8)
+ isif_port_if = MEDIA_BUS_FMT_SGRBG12_1X12;
+
+ /* Enable DPCM decompression */
+ switch (ipipeif_source) {
+ case IPIPEIF_SDRAM_RAW:
+ val = 0;
+ if (outformat->code == MEDIA_BUS_FMT_SGRBG10_DPCM8_1X8) {
+ val = 1;
+ val |= (IPIPEIF_DPCM_8BIT_10BIT & 1) <<
+ IPIPEIF_DPCM_BITS_SHIFT;
+ val |= (ipipeif->dpcm_predictor & 1) <<
+ IPIPEIF_DPCM_PRED_SHIFT;
+ }
+ ipipeif_write(val, ipipeif_base_addr, IPIPEIF_DPCM);
+
+ /* set DPC */
+ ipipeif_config_dpc(ipipeif_base_addr, &params.if_5_1.dpc);
+
+ ipipeif_write(params.if_5_1.clip,
+ ipipeif_base_addr, IPIPEIF_OCLIP);
+
+ /* fall through for SDRAM YUV mode */
+ /* configure CFG2 */
+ val = ipipeif_read(ipipeif_base_addr, IPIPEIF_CFG2);
+ switch (isif_port_if) {
+ case MEDIA_BUS_FMT_YUYV8_1X16:
+ case MEDIA_BUS_FMT_UYVY8_2X8:
+ case MEDIA_BUS_FMT_Y8_1X8:
+ RESETBIT(val, IPIPEIF_CFG2_YUV8_SHIFT);
+ SETBIT(val, IPIPEIF_CFG2_YUV16_SHIFT);
+ ipipeif_write(val, ipipeif_base_addr, IPIPEIF_CFG2);
+ break;
+
+ default:
+ RESETBIT(val, IPIPEIF_CFG2_YUV8_SHIFT);
+ RESETBIT(val, IPIPEIF_CFG2_YUV16_SHIFT);
+ ipipeif_write(val, ipipeif_base_addr, IPIPEIF_CFG2);
+ break;
+ }
+
+ case IPIPEIF_SDRAM_YUV:
+ /* Set clock divider */
+ if (params.clock_select == IPIPEIF_SDRAM_CLK) {
+ val = ipipeif_read(ipipeif_base_addr, IPIPEIF_CLKDIV);
+ val |= (params.if_5_1.clk_div.m - 1) <<
+ IPIPEIF_CLKDIV_M_SHIFT;
+ val |= (params.if_5_1.clk_div.n - 1);
+ ipipeif_write(val, ipipeif_base_addr, IPIPEIF_CLKDIV);
+ }
+ break;
+
+ case IPIPEIF_CCDC:
+ case IPIPEIF_CCDC_DARKFM:
+ /* set DPC */
+ ipipeif_config_dpc(ipipeif_base_addr, &params.if_5_1.dpc);
+
+ /* Set DF gain & threshold control */
+ val = 0;
+ if (params.if_5_1.df_gain_en) {
+ val = params.if_5_1.df_gain_thr &
+ IPIPEIF_DF_GAIN_THR_MASK;
+ ipipeif_write(val, ipipeif_base_addr, IPIPEIF_DFSGTH);
+ val = (params.if_5_1.df_gain_en & 1) <<
+ IPIPEIF_DF_GAIN_EN_SHIFT;
+ val |= params.if_5_1.df_gain &
+ IPIPEIF_DF_GAIN_MASK;
+ }
+ ipipeif_write(val, ipipeif_base_addr, IPIPEIF_DFSGVL);
+ /* configure CFG2 */
+ val = VPFE_PINPOL_POSITIVE << IPIPEIF_CFG2_HDPOL_SHIFT;
+ val |= VPFE_PINPOL_POSITIVE << IPIPEIF_CFG2_VDPOL_SHIFT;
+
+ switch (isif_port_if) {
+ case MEDIA_BUS_FMT_YUYV8_1X16:
+ case MEDIA_BUS_FMT_YUYV10_1X20:
+ RESETBIT(val, IPIPEIF_CFG2_YUV8_SHIFT);
+ SETBIT(val, IPIPEIF_CFG2_YUV16_SHIFT);
+ break;
+
+ case MEDIA_BUS_FMT_YUYV8_2X8:
+ case MEDIA_BUS_FMT_UYVY8_2X8:
+ case MEDIA_BUS_FMT_Y8_1X8:
+ case MEDIA_BUS_FMT_YUYV10_2X10:
+ SETBIT(val, IPIPEIF_CFG2_YUV8_SHIFT);
+ SETBIT(val, IPIPEIF_CFG2_YUV16_SHIFT);
+ val |= IPIPEIF_CBCR_Y << IPIPEIF_CFG2_YUV8P_SHIFT;
+ break;
+
+ default:
+ /* Bayer */
+ ipipeif_write(params.if_5_1.clip, ipipeif_base_addr,
+ IPIPEIF_OCLIP);
+ }
+ ipipeif_write(val, ipipeif_base_addr, IPIPEIF_CFG2);
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int
+ipipeif_set_config(struct v4l2_subdev *sd, struct ipipeif_params *config)
+{
+ struct vpfe_ipipeif_device *ipipeif = v4l2_get_subdevdata(sd);
+ struct device *dev = ipipeif->subdev.v4l2_dev->dev;
+
+ if (!config) {
+ dev_err(dev, "Invalid configuration pointer\n");
+ return -EINVAL;
+ }
+
+ ipipeif->config.clock_select = config->clock_select;
+ ipipeif->config.ppln = config->ppln;
+ ipipeif->config.lpfr = config->lpfr;
+ ipipeif->config.rsz = config->rsz;
+ ipipeif->config.decimation = config->decimation;
+ if (ipipeif->config.decimation &&
+ (ipipeif->config.rsz < IPIPEIF_RSZ_MIN ||
+ ipipeif->config.rsz > IPIPEIF_RSZ_MAX)) {
+ dev_err(dev, "rsz range is %d to %d\n",
+ IPIPEIF_RSZ_MIN, IPIPEIF_RSZ_MAX);
+ return -EINVAL;
+ }
+
+ ipipeif->config.avg_filter = config->avg_filter;
+
+ ipipeif->config.if_5_1.df_gain_thr = config->if_5_1.df_gain_thr;
+ ipipeif->config.if_5_1.df_gain = config->if_5_1.df_gain;
+ ipipeif->config.if_5_1.df_gain_en = config->if_5_1.df_gain_en;
+
+ ipipeif->config.if_5_1.rsz_start = config->if_5_1.rsz_start;
+ ipipeif->config.if_5_1.align_sync = config->if_5_1.align_sync;
+ ipipeif->config.if_5_1.clip = config->if_5_1.clip;
+
+ ipipeif->config.if_5_1.dpc.en = config->if_5_1.dpc.en;
+ ipipeif->config.if_5_1.dpc.thr = config->if_5_1.dpc.thr;
+
+ ipipeif->config.if_5_1.clk_div.m = config->if_5_1.clk_div.m;
+ ipipeif->config.if_5_1.clk_div.n = config->if_5_1.clk_div.n;
+
+ return 0;
+}
+
+static int
+ipipeif_get_config(struct v4l2_subdev *sd, void __user *arg)
+{
+ struct vpfe_ipipeif_device *ipipeif = v4l2_get_subdevdata(sd);
+ struct ipipeif_params *config = arg;
+ struct device *dev = ipipeif->subdev.v4l2_dev->dev;
+
+ if (!arg) {
+ dev_err(dev, "Invalid configuration pointer\n");
+ return -EINVAL;
+ }
+
+ config->clock_select = ipipeif->config.clock_select;
+ config->ppln = ipipeif->config.ppln;
+ config->lpfr = ipipeif->config.lpfr;
+ config->rsz = ipipeif->config.rsz;
+ config->decimation = ipipeif->config.decimation;
+ config->avg_filter = ipipeif->config.avg_filter;
+
+ config->if_5_1.df_gain_thr = ipipeif->config.if_5_1.df_gain_thr;
+ config->if_5_1.df_gain = ipipeif->config.if_5_1.df_gain;
+ config->if_5_1.df_gain_en = ipipeif->config.if_5_1.df_gain_en;
+
+ config->if_5_1.rsz_start = ipipeif->config.if_5_1.rsz_start;
+ config->if_5_1.align_sync = ipipeif->config.if_5_1.align_sync;
+ config->if_5_1.clip = ipipeif->config.if_5_1.clip;
+
+ config->if_5_1.dpc.en = ipipeif->config.if_5_1.dpc.en;
+ config->if_5_1.dpc.thr = ipipeif->config.if_5_1.dpc.thr;
+
+ config->if_5_1.clk_div.m = ipipeif->config.if_5_1.clk_div.m;
+ config->if_5_1.clk_div.n = ipipeif->config.if_5_1.clk_div.n;
+
+ return 0;
+}
+
+/*
+ * ipipeif_ioctl() - Handle ipipeif module private ioctl's
+ * @sd: pointer to v4l2 subdev structure
+ * @cmd: configuration command
+ * @arg: configuration argument
+ */
+static long ipipeif_ioctl(struct v4l2_subdev *sd,
+ unsigned int cmd, void *arg)
+{
+ struct ipipeif_params *config = arg;
+ int ret = -ENOIOCTLCMD;
+
+ switch (cmd) {
+ case VIDIOC_VPFE_IPIPEIF_S_CONFIG:
+ ret = ipipeif_set_config(sd, config);
+ break;
+
+ case VIDIOC_VPFE_IPIPEIF_G_CONFIG:
+ ret = ipipeif_get_config(sd, arg);
+ break;
+ }
+ return ret;
+}
+
+/*
+ * ipipeif_s_ctrl() - Handle set control subdev method
+ * @ctrl: pointer to v4l2 control structure
+ */
+static int ipipeif_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct vpfe_ipipeif_device *ipipeif =
+ container_of(ctrl->handler, struct vpfe_ipipeif_device, ctrls);
+
+ switch (ctrl->id) {
+ case VPFE_CID_DPCM_PREDICTOR:
+ ipipeif->dpcm_predictor = ctrl->val;
+ break;
+
+ case V4L2_CID_GAIN:
+ ipipeif->gain = ctrl->val;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+#define ENABLE_IPIPEIF 0x1
+
+void vpfe_ipipeif_enable(struct vpfe_device *vpfe_dev)
+{
+ struct vpfe_ipipeif_device *ipipeif = &vpfe_dev->vpfe_ipipeif;
+ void *ipipeif_base_addr = ipipeif->ipipeif_base_addr;
+ unsigned char val;
+
+ if (ipipeif->input != IPIPEIF_INPUT_MEMORY)
+ return;
+
+ do {
+ val = ipipeif_read(ipipeif_base_addr, IPIPEIF_ENABLE);
+ } while (val & 0x1);
+
+ ipipeif_write(ENABLE_IPIPEIF, ipipeif_base_addr, IPIPEIF_ENABLE);
+}
+
+/*
+ * ipipeif_set_stream() - Enable/Disable streaming on ipipeif subdev
+ * @sd: pointer to v4l2 subdev structure
+ * @enable: 1 == Enable, 0 == Disable
+ */
+static int ipipeif_set_stream(struct v4l2_subdev *sd, int enable)
+{
+ struct vpfe_ipipeif_device *ipipeif = v4l2_get_subdevdata(sd);
+ struct vpfe_device *vpfe_dev = to_vpfe_device(ipipeif);
+ int ret = 0;
+
+ if (!enable)
+ return ret;
+
+ ret = ipipeif_hw_setup(sd);
+ if (!ret)
+ vpfe_ipipeif_enable(vpfe_dev);
+
+ return ret;
+}
+
+/*
+ * ipipeif_enum_mbus_code() - Handle pixel format enumeration
+ * @sd: pointer to v4l2 subdev structure
+ * @cfg: V4L2 subdev pad config
+ * @code: pointer to v4l2_subdev_mbus_code_enum structure
+ * return -EINVAL or zero on success
+ */
+static int ipipeif_enum_mbus_code(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_mbus_code_enum *code)
+{
+ switch (code->pad) {
+ case IPIPEIF_PAD_SINK:
+ if (code->index >= ARRAY_SIZE(ipipeif_input_fmts))
+ return -EINVAL;
+
+ code->code = ipipeif_input_fmts[code->index];
+ break;
+
+ case IPIPEIF_PAD_SOURCE:
+ if (code->index >= ARRAY_SIZE(ipipeif_output_fmts))
+ return -EINVAL;
+
+ code->code = ipipeif_output_fmts[code->index];
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/*
+ * ipipeif_get_format() - Handle get format by pads subdev method
+ * @sd: pointer to v4l2 subdev structure
+ * @cfg: V4L2 subdev pad config
+ * @fmt: pointer to v4l2 subdev format structure
+ */
+static int
+ipipeif_get_format(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_format *fmt)
+{
+ struct vpfe_ipipeif_device *ipipeif = v4l2_get_subdevdata(sd);
+
+ if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE)
+ fmt->format = ipipeif->formats[fmt->pad];
+ else
+ fmt->format = *(v4l2_subdev_get_try_format(sd, cfg, fmt->pad));
+
+ return 0;
+}
+
+#define MIN_OUT_WIDTH 32
+#define MIN_OUT_HEIGHT 32
+
+/*
+ * ipipeif_try_format() - Handle try format by pad subdev method
+ * @ipipeif: VPFE ipipeif device.
+ * @cfg: V4L2 subdev pad config
+ * @pad: pad num.
+ * @fmt: pointer to v4l2 format structure.
+ * @which : wanted subdev format
+ */
+static void
+ipipeif_try_format(struct vpfe_ipipeif_device *ipipeif,
+ struct v4l2_subdev_pad_config *cfg, unsigned int pad,
+ struct v4l2_mbus_framefmt *fmt,
+ enum v4l2_subdev_format_whence which)
+{
+ unsigned int max_out_height;
+ unsigned int max_out_width;
+ unsigned int i;
+
+ max_out_width = IPIPE_MAX_OUTPUT_WIDTH_A;
+ max_out_height = IPIPE_MAX_OUTPUT_HEIGHT_A;
+
+ if (pad == IPIPEIF_PAD_SINK) {
+ for (i = 0; i < ARRAY_SIZE(ipipeif_input_fmts); i++)
+ if (fmt->code == ipipeif_input_fmts[i])
+ break;
+
+ /* If not found, use SBGGR10 as default */
+ if (i >= ARRAY_SIZE(ipipeif_input_fmts))
+ fmt->code = MEDIA_BUS_FMT_SGRBG12_1X12;
+ } else if (pad == IPIPEIF_PAD_SOURCE) {
+ for (i = 0; i < ARRAY_SIZE(ipipeif_output_fmts); i++)
+ if (fmt->code == ipipeif_output_fmts[i])
+ break;
+
+ /* If not found, use UYVY as default */
+ if (i >= ARRAY_SIZE(ipipeif_output_fmts))
+ fmt->code = MEDIA_BUS_FMT_UYVY8_2X8;
+ }
+
+ fmt->width = clamp_t(u32, fmt->width, MIN_OUT_HEIGHT, max_out_width);
+ fmt->height = clamp_t(u32, fmt->height, MIN_OUT_WIDTH, max_out_height);
+}
+
+static int
+ipipeif_enum_frame_size(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_frame_size_enum *fse)
+{
+ struct vpfe_ipipeif_device *ipipeif = v4l2_get_subdevdata(sd);
+ struct v4l2_mbus_framefmt format;
+
+ if (fse->index != 0)
+ return -EINVAL;
+
+ format.code = fse->code;
+ format.width = 1;
+ format.height = 1;
+ ipipeif_try_format(ipipeif, cfg, fse->pad, &format, fse->which);
+ fse->min_width = format.width;
+ fse->min_height = format.height;
+
+ if (format.code != fse->code)
+ return -EINVAL;
+
+ format.code = fse->code;
+ format.width = -1;
+ format.height = -1;
+ ipipeif_try_format(ipipeif, cfg, fse->pad, &format, fse->which);
+ fse->max_width = format.width;
+ fse->max_height = format.height;
+
+ return 0;
+}
+
+/*
+ * __ipipeif_get_format() - helper function for getting ipipeif format
+ * @ipipeif: pointer to ipipeif private structure.
+ * @cfg: V4L2 subdev pad config
+ * @pad: pad number.
+ * @which: wanted subdev format.
+ *
+ */
+static struct v4l2_mbus_framefmt *
+__ipipeif_get_format(struct vpfe_ipipeif_device *ipipeif,
+ struct v4l2_subdev_pad_config *cfg, unsigned int pad,
+ enum v4l2_subdev_format_whence which)
+{
+ if (which == V4L2_SUBDEV_FORMAT_TRY)
+ return v4l2_subdev_get_try_format(&ipipeif->subdev, cfg, pad);
+
+ return &ipipeif->formats[pad];
+}
+
+/*
+ * ipipeif_set_format() - Handle set format by pads subdev method
+ * @sd: pointer to v4l2 subdev structure
+ * @cfg: V4L2 subdev pad config
+ * @fmt: pointer to v4l2 subdev format structure
+ * return -EINVAL or zero on success
+ */
+static int
+ipipeif_set_format(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_format *fmt)
+{
+ struct vpfe_ipipeif_device *ipipeif = v4l2_get_subdevdata(sd);
+ struct v4l2_mbus_framefmt *format;
+
+ format = __ipipeif_get_format(ipipeif, cfg, fmt->pad, fmt->which);
+ if (format == NULL)
+ return -EINVAL;
+
+ ipipeif_try_format(ipipeif, cfg, fmt->pad, &fmt->format, fmt->which);
+ *format = fmt->format;
+
+ if (fmt->which == V4L2_SUBDEV_FORMAT_TRY)
+ return 0;
+
+ if (fmt->pad == IPIPEIF_PAD_SINK &&
+ ipipeif->input != IPIPEIF_INPUT_NONE)
+ ipipeif->formats[fmt->pad] = fmt->format;
+ else if (fmt->pad == IPIPEIF_PAD_SOURCE &&
+ ipipeif->output != IPIPEIF_OUTPUT_NONE)
+ ipipeif->formats[fmt->pad] = fmt->format;
+ else
+ return -EINVAL;
+
+ return 0;
+}
+
+static void ipipeif_set_default_config(struct vpfe_ipipeif_device *ipipeif)
+{
+#define WIDTH_I 640
+#define HEIGHT_I 480
+
+ const struct ipipeif_params ipipeif_defaults = {
+ .clock_select = IPIPEIF_SDRAM_CLK,
+ .ppln = WIDTH_I + 8,
+ .lpfr = HEIGHT_I + 10,
+ .rsz = 16, /* resize ratio 16/rsz */
+ .decimation = IPIPEIF_DECIMATION_OFF,
+ .avg_filter = IPIPEIF_AVG_OFF,
+ .if_5_1 = {
+ .clk_div = {
+ .m = 1, /* clock = sdram clock * (m/n) */
+ .n = 6
+ },
+ .clip = 4095,
+ },
+ };
+ memcpy(&ipipeif->config, &ipipeif_defaults,
+ sizeof(struct ipipeif_params));
+}
+
+/*
+ * ipipeif_init_formats() - Initialize formats on all pads
+ * @sd: VPFE ipipeif V4L2 subdevice
+ * @fh: V4L2 subdev file handle
+ *
+ * Initialize all pad formats with default values. Try formats are initialized
+ * on the file handle.
+ */
+static int
+ipipeif_init_formats(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
+{
+ struct vpfe_ipipeif_device *ipipeif = v4l2_get_subdevdata(sd);
+ struct v4l2_subdev_format format;
+
+ memset(&format, 0, sizeof(format));
+ format.pad = IPIPEIF_PAD_SINK;
+ format.which = V4L2_SUBDEV_FORMAT_TRY;
+ format.format.code = MEDIA_BUS_FMT_SGRBG12_1X12;
+ format.format.width = IPIPE_MAX_OUTPUT_WIDTH_A;
+ format.format.height = IPIPE_MAX_OUTPUT_HEIGHT_A;
+ ipipeif_set_format(sd, fh->pad, &format);
+
+ memset(&format, 0, sizeof(format));
+ format.pad = IPIPEIF_PAD_SOURCE;
+ format.which = V4L2_SUBDEV_FORMAT_TRY;
+ format.format.code = MEDIA_BUS_FMT_UYVY8_2X8;
+ format.format.width = IPIPE_MAX_OUTPUT_WIDTH_A;
+ format.format.height = IPIPE_MAX_OUTPUT_HEIGHT_A;
+ ipipeif_set_format(sd, fh->pad, &format);
+
+ ipipeif_set_default_config(ipipeif);
+
+ return 0;
+}
+
+/*
+ * ipipeif_video_in_queue() - ipipeif video in queue
+ * @vpfe_dev: vpfe device pointer
+ * @addr: buffer address
+ */
+static int
+ipipeif_video_in_queue(struct vpfe_device *vpfe_dev, unsigned long addr)
+{
+ struct vpfe_ipipeif_device *ipipeif = &vpfe_dev->vpfe_ipipeif;
+ void *ipipeif_base_addr = ipipeif->ipipeif_base_addr;
+ unsigned int adofs;
+ u32 val;
+
+ if (ipipeif->input != IPIPEIF_INPUT_MEMORY)
+ return -EINVAL;
+
+ switch (ipipeif->formats[IPIPEIF_PAD_SINK].code) {
+ case MEDIA_BUS_FMT_Y8_1X8:
+ case MEDIA_BUS_FMT_UV8_1X8:
+ case MEDIA_BUS_FMT_YDYUYDYV8_1X16:
+ adofs = ipipeif->formats[IPIPEIF_PAD_SINK].width;
+ break;
+
+ default:
+ adofs = ipipeif->formats[IPIPEIF_PAD_SINK].width << 1;
+ break;
+ }
+
+ /* adjust the line len to be a multiple of 32 */
+ adofs += 31;
+ adofs &= ~0x1f;
+ val = (adofs >> 5) & IPIPEIF_ADOFS_LSB_MASK;
+ ipipeif_write(val, ipipeif_base_addr, IPIPEIF_ADOFS);
+
+ /* lower sixteen bit */
+ val = (addr >> IPIPEIF_ADDRL_SHIFT) & IPIPEIF_ADDRL_MASK;
+ ipipeif_write(val, ipipeif_base_addr, IPIPEIF_ADDRL);
+
+ /* upper next seven bit */
+ val = (addr >> IPIPEIF_ADDRU_SHIFT) & IPIPEIF_ADDRU_MASK;
+ ipipeif_write(val, ipipeif_base_addr, IPIPEIF_ADDRU);
+
+ return 0;
+}
+
+/* subdev core operations */
+static const struct v4l2_subdev_core_ops ipipeif_v4l2_core_ops = {
+ .ioctl = ipipeif_ioctl,
+};
+
+static const struct v4l2_ctrl_ops ipipeif_ctrl_ops = {
+ .s_ctrl = ipipeif_s_ctrl,
+};
+
+static const struct v4l2_ctrl_config vpfe_ipipeif_dpcm_pred = {
+ .ops = &ipipeif_ctrl_ops,
+ .id = VPFE_CID_DPCM_PREDICTOR,
+ .name = "DPCM Predictor",
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .min = 0,
+ .max = 1,
+ .step = 1,
+ .def = 0,
+};
+
+/* subdev file operations */
+static const struct v4l2_subdev_internal_ops ipipeif_v4l2_internal_ops = {
+ .open = ipipeif_init_formats,
+};
+
+/* subdev video operations */
+static const struct v4l2_subdev_video_ops ipipeif_v4l2_video_ops = {
+ .s_stream = ipipeif_set_stream,
+};
+
+/* subdev pad operations */
+static const struct v4l2_subdev_pad_ops ipipeif_v4l2_pad_ops = {
+ .enum_mbus_code = ipipeif_enum_mbus_code,
+ .enum_frame_size = ipipeif_enum_frame_size,
+ .get_fmt = ipipeif_get_format,
+ .set_fmt = ipipeif_set_format,
+};
+
+/* subdev operations */
+static const struct v4l2_subdev_ops ipipeif_v4l2_ops = {
+ .core = &ipipeif_v4l2_core_ops,
+ .video = &ipipeif_v4l2_video_ops,
+ .pad = &ipipeif_v4l2_pad_ops,
+};
+
+static const struct vpfe_video_operations video_in_ops = {
+ .queue = ipipeif_video_in_queue,
+};
+
+static int
+ipipeif_link_setup(struct media_entity *entity, const struct media_pad *local,
+ const struct media_pad *remote, u32 flags)
+{
+ struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity);
+ struct vpfe_ipipeif_device *ipipeif = v4l2_get_subdevdata(sd);
+ struct vpfe_device *vpfe = to_vpfe_device(ipipeif);
+
+ switch (local->index | media_entity_type(remote->entity)) {
+ case IPIPEIF_PAD_SINK | MEDIA_ENT_T_DEVNODE:
+ /* Single shot mode */
+ if (!(flags & MEDIA_LNK_FL_ENABLED)) {
+ ipipeif->input = IPIPEIF_INPUT_NONE;
+ break;
+ }
+ ipipeif->input = IPIPEIF_INPUT_MEMORY;
+ break;
+
+ case IPIPEIF_PAD_SINK | MEDIA_ENT_T_V4L2_SUBDEV:
+ /* read from isif */
+ if (!(flags & MEDIA_LNK_FL_ENABLED)) {
+ ipipeif->input = IPIPEIF_INPUT_NONE;
+ break;
+ }
+ if (ipipeif->input != IPIPEIF_INPUT_NONE)
+ return -EBUSY;
+
+ ipipeif->input = IPIPEIF_INPUT_ISIF;
+ break;
+
+ case IPIPEIF_PAD_SOURCE | MEDIA_ENT_T_V4L2_SUBDEV:
+ if (!(flags & MEDIA_LNK_FL_ENABLED)) {
+ ipipeif->output = IPIPEIF_OUTPUT_NONE;
+ break;
+ }
+ if (remote->entity == &vpfe->vpfe_ipipe.subdev.entity)
+ /* connencted to ipipe */
+ ipipeif->output = IPIPEIF_OUTPUT_IPIPE;
+ else if (remote->entity == &vpfe->vpfe_resizer.
+ crop_resizer.subdev.entity)
+ /* connected to resizer */
+ ipipeif->output = IPIPEIF_OUTPUT_RESIZER;
+ else
+ return -EINVAL;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static const struct media_entity_operations ipipeif_media_ops = {
+ .link_setup = ipipeif_link_setup,
+};
+
+/*
+ * vpfe_ipipeif_unregister_entities() - Unregister entity
+ * @ipipeif - pointer to ipipeif subdevice structure.
+ */
+void vpfe_ipipeif_unregister_entities(struct vpfe_ipipeif_device *ipipeif)
+{
+ /* unregister video device */
+ vpfe_video_unregister(&ipipeif->video_in);
+
+ /* unregister subdev */
+ v4l2_device_unregister_subdev(&ipipeif->subdev);
+ /* cleanup entity */
+ media_entity_cleanup(&ipipeif->subdev.entity);
+}
+
+int
+vpfe_ipipeif_register_entities(struct vpfe_ipipeif_device *ipipeif,
+ struct v4l2_device *vdev)
+{
+ struct vpfe_device *vpfe_dev = to_vpfe_device(ipipeif);
+ unsigned int flags;
+ int ret;
+
+ /* Register the subdev */
+ ret = v4l2_device_register_subdev(vdev, &ipipeif->subdev);
+ if (ret < 0)
+ return ret;
+
+ ret = vpfe_video_register(&ipipeif->video_in, vdev);
+ if (ret) {
+ pr_err("Failed to register ipipeif video-in device\n");
+ goto fail;
+ }
+ ipipeif->video_in.vpfe_dev = vpfe_dev;
+
+ flags = 0;
+ ret = media_entity_create_link(&ipipeif->video_in.video_dev.entity, 0,
+ &ipipeif->subdev.entity, 0, flags);
+ if (ret < 0)
+ goto fail;
+
+ return 0;
+fail:
+ v4l2_device_unregister_subdev(&ipipeif->subdev);
+
+ return ret;
+}
+
+#define IPIPEIF_GAIN_HIGH 0x3ff
+#define IPIPEIF_DEFAULT_GAIN 0x200
+
+int vpfe_ipipeif_init(struct vpfe_ipipeif_device *ipipeif,
+ struct platform_device *pdev)
+{
+ struct v4l2_subdev *sd = &ipipeif->subdev;
+ struct media_pad *pads = &ipipeif->pads[0];
+ struct media_entity *me = &sd->entity;
+ static resource_size_t res_len;
+ struct resource *res;
+ int ret;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 3);
+ if (!res)
+ return -ENOENT;
+
+ res_len = resource_size(res);
+ res = request_mem_region(res->start, res_len, res->name);
+ if (!res)
+ return -EBUSY;
+
+ ipipeif->ipipeif_base_addr = ioremap_nocache(res->start, res_len);
+ if (!ipipeif->ipipeif_base_addr) {
+ ret = -EBUSY;
+ goto fail;
+ }
+
+ v4l2_subdev_init(sd, &ipipeif_v4l2_ops);
+
+ sd->internal_ops = &ipipeif_v4l2_internal_ops;
+ strlcpy(sd->name, "DAVINCI IPIPEIF", sizeof(sd->name));
+ sd->grp_id = 1 << 16; /* group ID for davinci subdevs */
+
+ v4l2_set_subdevdata(sd, ipipeif);
+
+ sd->flags |= V4L2_SUBDEV_FL_HAS_EVENTS | V4L2_SUBDEV_FL_HAS_DEVNODE;
+ pads[IPIPEIF_PAD_SINK].flags = MEDIA_PAD_FL_SINK;
+ pads[IPIPEIF_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE;
+ ipipeif->input = IPIPEIF_INPUT_NONE;
+ ipipeif->output = IPIPEIF_OUTPUT_NONE;
+ me->ops = &ipipeif_media_ops;
+
+ ret = media_entity_init(me, IPIPEIF_NUM_PADS, pads, 0);
+ if (ret)
+ goto fail;
+
+ v4l2_ctrl_handler_init(&ipipeif->ctrls, 2);
+ v4l2_ctrl_new_std(&ipipeif->ctrls, &ipipeif_ctrl_ops,
+ V4L2_CID_GAIN, 0,
+ IPIPEIF_GAIN_HIGH, 1, IPIPEIF_DEFAULT_GAIN);
+ v4l2_ctrl_new_custom(&ipipeif->ctrls, &vpfe_ipipeif_dpcm_pred, NULL);
+ v4l2_ctrl_handler_setup(&ipipeif->ctrls);
+ sd->ctrl_handler = &ipipeif->ctrls;
+
+ ipipeif->video_in.ops = &video_in_ops;
+ ipipeif->video_in.type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
+ ret = vpfe_video_init(&ipipeif->video_in, "IPIPEIF");
+ if (ret) {
+ pr_err("Failed to init IPIPEIF video-in device\n");
+ goto fail;
+ }
+ ipipeif_set_default_config(ipipeif);
+ return 0;
+fail:
+ release_mem_region(res->start, res_len);
+ return ret;
+}
+
+void
+vpfe_ipipeif_cleanup(struct vpfe_ipipeif_device *ipipeif,
+ struct platform_device *pdev)
+{
+ struct resource *res;
+
+ v4l2_ctrl_handler_free(&ipipeif->ctrls);
+ iounmap(ipipeif->ipipeif_base_addr);
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 3);
+ if (res)
+ release_mem_region(res->start, resource_size(res));
+
+}
diff --git a/drivers/staging/media/davinci_vpfe/dm365_ipipeif.h b/drivers/staging/media/davinci_vpfe/dm365_ipipeif.h
new file mode 100644
index 000000000..cea3d6133
--- /dev/null
+++ b/drivers/staging/media/davinci_vpfe/dm365_ipipeif.h
@@ -0,0 +1,233 @@
+/*
+ * Copyright (C) 2012 Texas Instruments 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 version 2.
+ *
+ * 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
+ *
+ * Contributors:
+ * Manjunath Hadli <manjunath.hadli@ti.com>
+ * Prabhakar Lad <prabhakar.lad@ti.com>
+ */
+
+#ifndef _DAVINCI_VPFE_DM365_IPIPEIF_H
+#define _DAVINCI_VPFE_DM365_IPIPEIF_H
+
+#include <linux/platform_device.h>
+
+#include <media/davinci/vpss.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-subdev.h>
+
+#include "dm365_ipipeif_user.h"
+#include "vpfe_video.h"
+
+/* IPIPE base specific types */
+enum ipipeif_data_shift {
+ IPIPEIF_BITS15_2 = 0,
+ IPIPEIF_BITS14_1 = 1,
+ IPIPEIF_BITS13_0 = 2,
+ IPIPEIF_BITS12_0 = 3,
+ IPIPEIF_BITS11_0 = 4,
+ IPIPEIF_BITS10_0 = 5,
+ IPIPEIF_BITS9_0 = 6,
+};
+
+enum ipipeif_clkdiv {
+ IPIPEIF_DIVIDE_HALF = 0,
+ IPIPEIF_DIVIDE_THIRD = 1,
+ IPIPEIF_DIVIDE_FOURTH = 2,
+ IPIPEIF_DIVIDE_FIFTH = 3,
+ IPIPEIF_DIVIDE_SIXTH = 4,
+ IPIPEIF_DIVIDE_EIGHTH = 5,
+ IPIPEIF_DIVIDE_SIXTEENTH = 6,
+ IPIPEIF_DIVIDE_THIRTY = 7,
+};
+
+enum ipipeif_pack_mode {
+ IPIPEIF_PACK_16_BIT = 0,
+ IPIPEIF_PACK_8_BIT = 1,
+};
+
+enum ipipeif_5_1_pack_mode {
+ IPIPEIF_5_1_PACK_16_BIT = 0,
+ IPIPEIF_5_1_PACK_8_BIT = 1,
+ IPIPEIF_5_1_PACK_8_BIT_A_LAW = 2,
+ IPIPEIF_5_1_PACK_12_BIT = 3
+};
+
+enum ipipeif_input_source {
+ IPIPEIF_CCDC = 0,
+ IPIPEIF_SDRAM_RAW = 1,
+ IPIPEIF_CCDC_DARKFM = 2,
+ IPIPEIF_SDRAM_YUV = 3,
+};
+
+enum ipipeif_ialaw {
+ IPIPEIF_ALAW_OFF = 0,
+ IPIPEIF_ALAW_ON = 1,
+};
+
+enum ipipeif_input_src1 {
+ IPIPEIF_SRC1_PARALLEL_PORT = 0,
+ IPIPEIF_SRC1_SDRAM_RAW = 1,
+ IPIPEIF_SRC1_ISIF_DARKFM = 2,
+ IPIPEIF_SRC1_SDRAM_YUV = 3,
+};
+
+enum ipipeif_dfs_dir {
+ IPIPEIF_PORT_MINUS_SDRAM = 0,
+ IPIPEIF_SDRAM_MINUS_PORT = 1,
+};
+
+enum ipipeif_chroma_phase {
+ IPIPEIF_CBCR_Y = 0,
+ IPIPEIF_Y_CBCR = 1,
+};
+
+enum ipipeif_dpcm_type {
+ IPIPEIF_DPCM_8BIT_10BIT = 0,
+ IPIPEIF_DPCM_8BIT_12BIT = 1,
+};
+
+/* data shift for IPIPE 5.1 */
+enum ipipeif_5_1_data_shift {
+ IPIPEIF_5_1_BITS11_0 = 0,
+ IPIPEIF_5_1_BITS10_0 = 1,
+ IPIPEIF_5_1_BITS9_0 = 2,
+ IPIPEIF_5_1_BITS8_0 = 3,
+ IPIPEIF_5_1_BITS7_0 = 4,
+ IPIPEIF_5_1_BITS15_4 = 5,
+};
+
+#define IPIPEIF_PAD_SINK 0
+#define IPIPEIF_PAD_SOURCE 1
+
+#define IPIPEIF_NUM_PADS 2
+
+enum ipipeif_input_entity {
+ IPIPEIF_INPUT_NONE = 0,
+ IPIPEIF_INPUT_ISIF = 1,
+ IPIPEIF_INPUT_MEMORY = 2,
+};
+
+enum ipipeif_output_entity {
+ IPIPEIF_OUTPUT_NONE = 0,
+ IPIPEIF_OUTPUT_IPIPE = 1,
+ IPIPEIF_OUTPUT_RESIZER = 2,
+};
+
+struct vpfe_ipipeif_device {
+ struct v4l2_subdev subdev;
+ struct media_pad pads[IPIPEIF_NUM_PADS];
+ struct v4l2_mbus_framefmt formats[IPIPEIF_NUM_PADS];
+ enum ipipeif_input_entity input;
+ unsigned int output;
+ struct vpfe_video_device video_in;
+ struct v4l2_ctrl_handler ctrls;
+ void __iomem *ipipeif_base_addr;
+ struct ipipeif_params config;
+ int dpcm_predictor;
+ int gain;
+};
+
+/* IPIPEIF Register Offsets from the base address */
+#define IPIPEIF_ENABLE 0x00
+#define IPIPEIF_CFG1 0x04
+#define IPIPEIF_PPLN 0x08
+#define IPIPEIF_LPFR 0x0c
+#define IPIPEIF_HNUM 0x10
+#define IPIPEIF_VNUM 0x14
+#define IPIPEIF_ADDRU 0x18
+#define IPIPEIF_ADDRL 0x1c
+#define IPIPEIF_ADOFS 0x20
+#define IPIPEIF_RSZ 0x24
+#define IPIPEIF_GAIN 0x28
+
+/* Below registers are available only on IPIPE 5.1 */
+#define IPIPEIF_DPCM 0x2c
+#define IPIPEIF_CFG2 0x30
+#define IPIPEIF_INIRSZ 0x34
+#define IPIPEIF_OCLIP 0x38
+#define IPIPEIF_DTUDF 0x3c
+#define IPIPEIF_CLKDIV 0x40
+#define IPIPEIF_DPC1 0x44
+#define IPIPEIF_DPC2 0x48
+#define IPIPEIF_DFSGVL 0x4c
+#define IPIPEIF_DFSGTH 0x50
+#define IPIPEIF_RSZ3A 0x54
+#define IPIPEIF_INIRSZ3A 0x58
+#define IPIPEIF_RSZ_MIN 16
+#define IPIPEIF_RSZ_MAX 112
+#define IPIPEIF_RSZ_CONST 16
+#define SETBIT(reg, bit) (reg = ((reg) | ((0x00000001)<<(bit))))
+#define RESETBIT(reg, bit) (reg = ((reg) & (~(0x00000001<<(bit)))))
+
+#define IPIPEIF_ADOFS_LSB_MASK 0x1ff
+#define IPIPEIF_ADOFS_LSB_SHIFT 5
+#define IPIPEIF_ADOFS_MSB_MASK 0x200
+#define IPIPEIF_ADDRU_MASK 0x7ff
+#define IPIPEIF_ADDRL_SHIFT 5
+#define IPIPEIF_ADDRL_MASK 0xffff
+#define IPIPEIF_ADDRU_SHIFT 21
+#define IPIPEIF_ADDRMSB_SHIFT 31
+#define IPIPEIF_ADDRMSB_LEFT_SHIFT 10
+
+/* CFG1 Masks and shifts */
+#define ONESHOT_SHIFT 0
+#define DECIM_SHIFT 1
+#define INPSRC_SHIFT 2
+#define CLKDIV_SHIFT 4
+#define AVGFILT_SHIFT 7
+#define PACK8IN_SHIFT 8
+#define IALAW_SHIFT 9
+#define CLKSEL_SHIFT 10
+#define DATASFT_SHIFT 11
+#define INPSRC1_SHIFT 14
+
+/* DPC2 */
+#define IPIPEIF_DPC2_EN_SHIFT 12
+#define IPIPEIF_DPC2_THR_MASK 0xfff
+/* Applicable for IPIPE 5.1 */
+#define IPIPEIF_DF_GAIN_EN_SHIFT 10
+#define IPIPEIF_DF_GAIN_MASK 0x3ff
+#define IPIPEIF_DF_GAIN_THR_MASK 0xfff
+/* DPCM */
+#define IPIPEIF_DPCM_BITS_SHIFT 2
+#define IPIPEIF_DPCM_PRED_SHIFT 1
+/* CFG2 */
+#define IPIPEIF_CFG2_HDPOL_SHIFT 1
+#define IPIPEIF_CFG2_VDPOL_SHIFT 2
+#define IPIPEIF_CFG2_YUV8_SHIFT 6
+#define IPIPEIF_CFG2_YUV16_SHIFT 3
+#define IPIPEIF_CFG2_YUV8P_SHIFT 7
+
+/* INIRSZ */
+#define IPIPEIF_INIRSZ_ALNSYNC_SHIFT 13
+#define IPIPEIF_INIRSZ_MASK 0x1fff
+
+/* CLKDIV */
+#define IPIPEIF_CLKDIV_M_SHIFT 8
+
+void vpfe_ipipeif_enable(struct vpfe_device *vpfe_dev);
+void vpfe_ipipeif_ss_buffer_isr(struct vpfe_ipipeif_device *ipipeif);
+int vpfe_ipipeif_decimation_enabled(struct vpfe_device *vpfe_dev);
+int vpfe_ipipeif_get_rsz(struct vpfe_device *vpfe_dev);
+void vpfe_ipipeif_cleanup(struct vpfe_ipipeif_device *ipipeif,
+ struct platform_device *pdev);
+int vpfe_ipipeif_init(struct vpfe_ipipeif_device *ipipeif,
+ struct platform_device *pdev);
+int vpfe_ipipeif_register_entities(struct vpfe_ipipeif_device *ipipeif,
+ struct v4l2_device *vdev);
+void vpfe_ipipeif_unregister_entities(struct vpfe_ipipeif_device *ipipeif);
+
+#endif /* _DAVINCI_VPFE_DM365_IPIPEIF_H */
diff --git a/drivers/staging/media/davinci_vpfe/dm365_ipipeif_user.h b/drivers/staging/media/davinci_vpfe/dm365_ipipeif_user.h
new file mode 100644
index 000000000..e2a69b595
--- /dev/null
+++ b/drivers/staging/media/davinci_vpfe/dm365_ipipeif_user.h
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2012 Texas Instruments 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 version 2.
+ *
+ * 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
+ *
+ * Contributors:
+ * Manjunath Hadli <manjunath.hadli@ti.com>
+ * Prabhakar Lad <prabhakar.lad@ti.com>
+ */
+
+#ifndef _DAVINCI_VPFE_DM365_IPIPEIF_USER_H
+#define _DAVINCI_VPFE_DM365_IPIPEIF_USER_H
+
+/* clockdiv for IPIPE 5.1 */
+struct ipipeif_5_1_clkdiv {
+ unsigned char m;
+ unsigned char n;
+};
+
+enum ipipeif_decimation {
+ IPIPEIF_DECIMATION_OFF,
+ IPIPEIF_DECIMATION_ON
+};
+
+/* DPC at the if for IPIPE 5.1 */
+struct ipipeif_dpc {
+ /* 0 - disable, 1 - enable */
+ unsigned char en;
+ /* threshold */
+ unsigned short thr;
+};
+
+enum ipipeif_clock {
+ IPIPEIF_PIXCEL_CLK,
+ IPIPEIF_SDRAM_CLK
+};
+
+enum ipipeif_avg_filter {
+ IPIPEIF_AVG_OFF,
+ IPIPEIF_AVG_ON
+};
+
+struct ipipeif_5_1 {
+ struct ipipeif_5_1_clkdiv clk_div;
+ /* Defect pixel correction */
+ struct ipipeif_dpc dpc;
+ /* clipped to this value */
+ unsigned short clip;
+ /* Align HSync and VSync to rsz_start */
+ unsigned char align_sync;
+ /* resizer start position */
+ unsigned int rsz_start;
+ /* DF gain enable */
+ unsigned char df_gain_en;
+ /* DF gain value */
+ unsigned short df_gain;
+ /* DF gain threshold value */
+ unsigned short df_gain_thr;
+};
+
+struct ipipeif_params {
+ enum ipipeif_clock clock_select;
+ unsigned int ppln;
+ unsigned int lpfr;
+ unsigned char rsz;
+ enum ipipeif_decimation decimation;
+ enum ipipeif_avg_filter avg_filter;
+ /* IPIPE 5.1 */
+ struct ipipeif_5_1 if_5_1;
+};
+
+/*
+ * Private IOCTL
+ * VIDIOC_VPFE_IPIPEIF_S_CONFIG: Set IPIEIF configuration
+ * VIDIOC_VPFE_IPIPEIF_G_CONFIG: Get IPIEIF configuration
+ */
+#define VIDIOC_VPFE_IPIPEIF_S_CONFIG \
+ _IOWR('I', BASE_VIDIOC_PRIVATE + 1, struct ipipeif_params)
+#define VIDIOC_VPFE_IPIPEIF_G_CONFIG \
+ _IOWR('I', BASE_VIDIOC_PRIVATE + 2, struct ipipeif_params)
+
+#endif /* _DAVINCI_VPFE_DM365_IPIPEIF_USER_H */
diff --git a/drivers/staging/media/davinci_vpfe/dm365_isif.c b/drivers/staging/media/davinci_vpfe/dm365_isif.c
new file mode 100644
index 000000000..80907b464
--- /dev/null
+++ b/drivers/staging/media/davinci_vpfe/dm365_isif.c
@@ -0,0 +1,2106 @@
+/*
+ * Copyright (C) 2012 Texas Instruments 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 version 2.
+ *
+ * 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
+ *
+ * Contributors:
+ * Manjunath Hadli <manjunath.hadli@ti.com>
+ * Prabhakar Lad <prabhakar.lad@ti.com>
+ */
+
+#include <linux/delay.h>
+#include "dm365_isif.h"
+#include "vpfe_mc_capture.h"
+
+#define MAX_WIDTH 4096
+#define MAX_HEIGHT 4096
+
+static const unsigned int isif_fmts[] = {
+ MEDIA_BUS_FMT_YUYV8_2X8,
+ MEDIA_BUS_FMT_UYVY8_2X8,
+ MEDIA_BUS_FMT_YUYV8_1X16,
+ MEDIA_BUS_FMT_YUYV10_1X20,
+ MEDIA_BUS_FMT_SGRBG12_1X12,
+ MEDIA_BUS_FMT_SGRBG10_ALAW8_1X8,
+ MEDIA_BUS_FMT_SGRBG10_DPCM8_1X8,
+};
+
+#define ISIF_COLPTN_R_Ye 0x0
+#define ISIF_COLPTN_Gr_Cy 0x1
+#define ISIF_COLPTN_Gb_G 0x2
+#define ISIF_COLPTN_B_Mg 0x3
+
+#define ISIF_CCOLP_CP01_0 0
+#define ISIF_CCOLP_CP03_2 2
+#define ISIF_CCOLP_CP05_4 4
+#define ISIF_CCOLP_CP07_6 6
+#define ISIF_CCOLP_CP11_0 8
+#define ISIF_CCOLP_CP13_2 10
+#define ISIF_CCOLP_CP15_4 12
+#define ISIF_CCOLP_CP17_6 14
+
+static const u32 isif_sgrbg_pattern =
+ ISIF_COLPTN_Gr_Cy << ISIF_CCOLP_CP01_0 |
+ ISIF_COLPTN_R_Ye << ISIF_CCOLP_CP03_2 |
+ ISIF_COLPTN_B_Mg << ISIF_CCOLP_CP05_4 |
+ ISIF_COLPTN_Gb_G << ISIF_CCOLP_CP07_6 |
+ ISIF_COLPTN_Gr_Cy << ISIF_CCOLP_CP11_0 |
+ ISIF_COLPTN_R_Ye << ISIF_CCOLP_CP13_2 |
+ ISIF_COLPTN_B_Mg << ISIF_CCOLP_CP15_4 |
+ ISIF_COLPTN_Gb_G << ISIF_CCOLP_CP17_6;
+
+static const u32 isif_srggb_pattern =
+ ISIF_COLPTN_R_Ye << ISIF_CCOLP_CP01_0 |
+ ISIF_COLPTN_Gr_Cy << ISIF_CCOLP_CP03_2 |
+ ISIF_COLPTN_Gb_G << ISIF_CCOLP_CP05_4 |
+ ISIF_COLPTN_B_Mg << ISIF_CCOLP_CP07_6 |
+ ISIF_COLPTN_R_Ye << ISIF_CCOLP_CP11_0 |
+ ISIF_COLPTN_Gr_Cy << ISIF_CCOLP_CP13_2 |
+ ISIF_COLPTN_Gb_G << ISIF_CCOLP_CP15_4 |
+ ISIF_COLPTN_B_Mg << ISIF_CCOLP_CP17_6;
+
+static inline u32 isif_read(void __iomem *base_addr, u32 offset)
+{
+ return readl(base_addr + offset);
+}
+
+static inline void isif_write(void __iomem *base_addr, u32 val, u32 offset)
+{
+ writel(val, base_addr + offset);
+}
+
+static inline u32 isif_merge(void __iomem *base_addr, u32 mask, u32 val,
+ u32 offset)
+{
+ u32 new_val = (isif_read(base_addr, offset) & ~mask) | (val & mask);
+
+ isif_write(base_addr, new_val, offset);
+
+ return new_val;
+}
+
+static void isif_enable_output_to_sdram(struct vpfe_isif_device *isif, int en)
+{
+ isif_merge(isif->isif_cfg.base_addr, ISIF_SYNCEN_WEN_MASK,
+ en << ISIF_SYNCEN_WEN_SHIFT, SYNCEN);
+}
+
+static inline void
+isif_regw_lin_tbl(struct vpfe_isif_device *isif, u32 val, u32 offset, int i)
+{
+ if (!i)
+ writel(val, isif->isif_cfg.linear_tbl0_addr + offset);
+ else
+ writel(val, isif->isif_cfg.linear_tbl1_addr + offset);
+}
+
+static void isif_disable_all_modules(struct vpfe_isif_device *isif)
+{
+ /* disable BC */
+ isif_write(isif->isif_cfg.base_addr, 0, CLAMPCFG);
+ /* disable vdfc */
+ isif_write(isif->isif_cfg.base_addr, 0, DFCCTL);
+ /* disable CSC */
+ isif_write(isif->isif_cfg.base_addr, 0, CSCCTL);
+ /* disable linearization */
+ isif_write(isif->isif_cfg.base_addr, 0, LINCFG0);
+}
+
+static void isif_enable(struct vpfe_isif_device *isif, int en)
+{
+ if (!en)
+ /* Before disable isif, disable all ISIF modules */
+ isif_disable_all_modules(isif);
+
+ /*
+ * wait for next VD. Assume lowest scan rate is 12 Hz. So
+ * 100 msec delay is good enough
+ */
+ msleep(100);
+ isif_merge(isif->isif_cfg.base_addr, ISIF_SYNCEN_VDHDEN_MASK,
+ en, SYNCEN);
+}
+
+/*
+ * ISIF helper functions
+ */
+
+#define DM365_ISIF_MDFS_OFFSET 15
+#define DM365_ISIF_MDFS_MASK 0x1
+
+/* get field id in isif hardware */
+enum v4l2_field vpfe_isif_get_fid(struct vpfe_device *vpfe_dev)
+{
+ struct vpfe_isif_device *isif = &vpfe_dev->vpfe_isif;
+ u32 field_status;
+
+ field_status = isif_read(isif->isif_cfg.base_addr, MODESET);
+ field_status = (field_status >> DM365_ISIF_MDFS_OFFSET) &
+ DM365_ISIF_MDFS_MASK;
+ return field_status;
+}
+
+static int
+isif_set_pixel_format(struct vpfe_isif_device *isif, unsigned int pixfmt)
+{
+ if (isif->formats[ISIF_PAD_SINK].code == MEDIA_BUS_FMT_SGRBG12_1X12) {
+ if (pixfmt == V4L2_PIX_FMT_SBGGR16)
+ isif->isif_cfg.data_pack = ISIF_PACK_16BIT;
+ else if ((pixfmt == V4L2_PIX_FMT_SGRBG10DPCM8) ||
+ (pixfmt == V4L2_PIX_FMT_SGRBG10ALAW8))
+ isif->isif_cfg.data_pack = ISIF_PACK_8BIT;
+ else
+ return -EINVAL;
+
+ isif->isif_cfg.bayer.pix_fmt = ISIF_PIXFMT_RAW;
+ isif->isif_cfg.bayer.v4l2_pix_fmt = pixfmt;
+ } else {
+ if (pixfmt == V4L2_PIX_FMT_YUYV)
+ isif->isif_cfg.ycbcr.pix_order = ISIF_PIXORDER_YCBYCR;
+ else if (pixfmt == V4L2_PIX_FMT_UYVY)
+ isif->isif_cfg.ycbcr.pix_order = ISIF_PIXORDER_CBYCRY;
+ else
+ return -EINVAL;
+
+ isif->isif_cfg.data_pack = ISIF_PACK_8BIT;
+ isif->isif_cfg.ycbcr.v4l2_pix_fmt = pixfmt;
+ }
+
+ return 0;
+}
+
+static int
+isif_set_frame_format(struct vpfe_isif_device *isif,
+ enum isif_frmfmt frm_fmt)
+{
+ if (isif->formats[ISIF_PAD_SINK].code == MEDIA_BUS_FMT_SGRBG12_1X12)
+ isif->isif_cfg.bayer.frm_fmt = frm_fmt;
+ else
+ isif->isif_cfg.ycbcr.frm_fmt = frm_fmt;
+
+ return 0;
+}
+
+static int isif_set_image_window(struct vpfe_isif_device *isif)
+{
+ struct v4l2_rect *win = &isif->crop;
+
+ if (isif->formats[ISIF_PAD_SINK].code == MEDIA_BUS_FMT_SGRBG12_1X12) {
+ isif->isif_cfg.bayer.win.top = win->top;
+ isif->isif_cfg.bayer.win.left = win->left;
+ isif->isif_cfg.bayer.win.width = win->width;
+ isif->isif_cfg.bayer.win.height = win->height;
+ return 0;
+ }
+ isif->isif_cfg.ycbcr.win.top = win->top;
+ isif->isif_cfg.ycbcr.win.left = win->left;
+ isif->isif_cfg.ycbcr.win.width = win->width;
+ isif->isif_cfg.ycbcr.win.height = win->height;
+
+ return 0;
+}
+
+static int
+isif_set_buftype(struct vpfe_isif_device *isif, enum isif_buftype buf_type)
+{
+ if (isif->formats[ISIF_PAD_SINK].code == MEDIA_BUS_FMT_SGRBG12_1X12)
+ isif->isif_cfg.bayer.buf_type = buf_type;
+ else
+ isif->isif_cfg.ycbcr.buf_type = buf_type;
+
+ return 0;
+}
+
+/* configure format in isif hardware */
+static int
+isif_config_format(struct vpfe_device *vpfe_dev, unsigned int pad)
+{
+ struct vpfe_isif_device *vpfe_isif = &vpfe_dev->vpfe_isif;
+ enum isif_frmfmt frm_fmt = ISIF_FRMFMT_INTERLACED;
+ struct v4l2_pix_format format;
+ int ret = 0;
+
+ v4l2_fill_pix_format(&format, &vpfe_dev->vpfe_isif.formats[pad]);
+ mbus_to_pix(&vpfe_dev->vpfe_isif.formats[pad], &format);
+
+ if (isif_set_pixel_format(vpfe_isif, format.pixelformat) < 0) {
+ v4l2_err(&vpfe_dev->v4l2_dev,
+ "Failed to set pixel format in isif\n");
+ return -EINVAL;
+ }
+
+ /* call for s_crop will override these values */
+ vpfe_isif->crop.left = 0;
+ vpfe_isif->crop.top = 0;
+ vpfe_isif->crop.width = format.width;
+ vpfe_isif->crop.height = format.height;
+
+ /* configure the image window */
+ isif_set_image_window(vpfe_isif);
+
+ switch (vpfe_dev->vpfe_isif.formats[pad].field) {
+ case V4L2_FIELD_INTERLACED:
+ /* do nothing, since it is default */
+ ret = isif_set_buftype(vpfe_isif, ISIF_BUFTYPE_FLD_INTERLEAVED);
+ break;
+
+ case V4L2_FIELD_NONE:
+ frm_fmt = ISIF_FRMFMT_PROGRESSIVE;
+ /* buffer type only applicable for interlaced scan */
+ break;
+
+ case V4L2_FIELD_SEQ_TB:
+ ret = isif_set_buftype(vpfe_isif, ISIF_BUFTYPE_FLD_SEPARATED);
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ /* set the frame format */
+ if (!ret)
+ ret = isif_set_frame_format(vpfe_isif, frm_fmt);
+
+ return ret;
+}
+
+/*
+ * isif_try_format() - Try video format on a pad
+ * @isif: VPFE isif device
+ * @cfg: V4L2 subdev pad config
+ * @fmt: pointer to v4l2 subdev format structure
+ */
+static void
+isif_try_format(struct vpfe_isif_device *isif, struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_format *fmt)
+{
+ unsigned int width = fmt->format.width;
+ unsigned int height = fmt->format.height;
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(isif_fmts); i++) {
+ if (fmt->format.code == isif_fmts[i])
+ break;
+ }
+
+ /* If not found, use YUYV8_2x8 as default */
+ if (i >= ARRAY_SIZE(isif_fmts))
+ fmt->format.code = MEDIA_BUS_FMT_YUYV8_2X8;
+
+ /* Clamp the size. */
+ fmt->format.width = clamp_t(u32, width, 32, MAX_WIDTH);
+ fmt->format.height = clamp_t(u32, height, 32, MAX_HEIGHT);
+
+ /* The data formatter truncates the number of horizontal output
+ * pixels to a multiple of 16. To avoid clipping data, allow
+ * callers to request an output size bigger than the input size
+ * up to the nearest multiple of 16.
+ */
+ if (fmt->pad == ISIF_PAD_SOURCE)
+ fmt->format.width &= ~15;
+}
+
+/*
+ * vpfe_isif_buffer_isr() - isif module non-progressive buffer scheduling isr
+ * @isif: Pointer to isif subdevice.
+ */
+void vpfe_isif_buffer_isr(struct vpfe_isif_device *isif)
+{
+ struct vpfe_device *vpfe_dev = to_vpfe_device(isif);
+ struct vpfe_video_device *video = &isif->video_out;
+ enum v4l2_field field;
+ int fid;
+
+ if (!video->started)
+ return;
+
+ field = video->fmt.fmt.pix.field;
+
+ if (field == V4L2_FIELD_NONE) {
+ /* handle progressive frame capture */
+ if (video->cur_frm != video->next_frm)
+ vpfe_video_process_buffer_complete(video);
+ return;
+ }
+
+ /* interlaced or TB capture check which field we
+ * are in hardware
+ */
+ fid = vpfe_isif_get_fid(vpfe_dev);
+
+ /* switch the software maintained field id */
+ video->field_id ^= 1;
+ if (fid == video->field_id) {
+ /* we are in-sync here,continue */
+ if (fid == 0) {
+ /*
+ * One frame is just being captured. If the
+ * next frame is available, release the current
+ * frame and move on
+ */
+ if (video->cur_frm != video->next_frm)
+ vpfe_video_process_buffer_complete(video);
+ /*
+ * based on whether the two fields are stored
+ * interleavely or separately in memory,
+ * reconfigure the ISIF memory address
+ */
+ if (field == V4L2_FIELD_SEQ_TB)
+ vpfe_video_schedule_bottom_field(video);
+ return;
+ }
+ /*
+ * if one field is just being captured configure
+ * the next frame get the next frame from the
+ * empty queue if no frame is available hold on
+ * to the current buffer
+ */
+ spin_lock(&video->dma_queue_lock);
+ if (!list_empty(&video->dma_queue) &&
+ video->cur_frm == video->next_frm)
+ vpfe_video_schedule_next_buffer(video);
+ spin_unlock(&video->dma_queue_lock);
+ } else if (fid == 0) {
+ /*
+ * out of sync. Recover from any hardware out-of-sync.
+ * May loose one frame
+ */
+ video->field_id = fid;
+ }
+}
+
+/*
+ * vpfe_isif_vidint1_isr() - ISIF module progressive buffer scheduling isr
+ * @isif: Pointer to isif subdevice.
+ */
+void vpfe_isif_vidint1_isr(struct vpfe_isif_device *isif)
+{
+ struct vpfe_video_device *video = &isif->video_out;
+
+ if (!video->started)
+ return;
+
+ spin_lock(&video->dma_queue_lock);
+ if (video->fmt.fmt.pix.field == V4L2_FIELD_NONE &&
+ !list_empty(&video->dma_queue) && video->cur_frm == video->next_frm)
+ vpfe_video_schedule_next_buffer(video);
+
+ spin_unlock(&video->dma_queue_lock);
+}
+
+/*
+ * VPFE video operations
+ */
+
+static int isif_video_queue(struct vpfe_device *vpfe_dev, unsigned long addr)
+{
+ struct vpfe_isif_device *isif = &vpfe_dev->vpfe_isif;
+
+ isif_write(isif->isif_cfg.base_addr, (addr >> 21) &
+ ISIF_CADU_BITS, CADU);
+ isif_write(isif->isif_cfg.base_addr, (addr >> 5) &
+ ISIF_CADL_BITS, CADL);
+
+ return 0;
+}
+
+static const struct vpfe_video_operations isif_video_ops = {
+ .queue = isif_video_queue,
+};
+
+/*
+ * V4L2 subdev operations
+ */
+
+/* Parameter operations */
+static int isif_get_params(struct v4l2_subdev *sd, void *params)
+{
+ struct vpfe_isif_device *isif = v4l2_get_subdevdata(sd);
+
+ /* only raw module parameters can be set through the IOCTL */
+ if (isif->formats[ISIF_PAD_SINK].code != MEDIA_BUS_FMT_SGRBG12_1X12)
+ return -EINVAL;
+ memcpy(params, &isif->isif_cfg.bayer.config_params,
+ sizeof(isif->isif_cfg.bayer.config_params));
+ return 0;
+}
+
+static int isif_validate_df_csc_params(struct vpfe_isif_df_csc *df_csc)
+{
+ struct vpfe_isif_color_space_conv *csc;
+ int err = -EINVAL;
+ int i;
+
+ if (!df_csc->df_or_csc) {
+ /* csc configuration */
+ csc = &df_csc->csc;
+ if (csc->en) {
+ for (i = 0; i < VPFE_ISIF_CSC_NUM_COEFF; i++)
+ if (csc->coeff[i].integer >
+ ISIF_CSC_COEF_INTEG_MASK ||
+ csc->coeff[i].decimal >
+ ISIF_CSC_COEF_DECIMAL_MASK) {
+ pr_err("Invalid CSC coefficients\n");
+ return err;
+ }
+ }
+ }
+ if (df_csc->start_pix > ISIF_DF_CSC_SPH_MASK) {
+ pr_err("Invalid df_csc start pix value\n");
+ return err;
+ }
+
+ if (df_csc->num_pixels > ISIF_DF_NUMPIX) {
+ pr_err("Invalid df_csc num pixels value\n");
+ return err;
+ }
+
+ if (df_csc->start_line > ISIF_DF_CSC_LNH_MASK) {
+ pr_err("Invalid df_csc start_line value\n");
+ return err;
+ }
+
+ if (df_csc->num_lines > ISIF_DF_NUMLINES) {
+ pr_err("Invalid df_csc num_lines value\n");
+ return err;
+ }
+
+ return 0;
+}
+
+#define DM365_ISIF_MAX_VDFLSFT 4
+#define DM365_ISIF_MAX_VDFSLV 4095
+#define DM365_ISIF_MAX_DFCMEM0 0x1fff
+#define DM365_ISIF_MAX_DFCMEM1 0x1fff
+
+static int isif_validate_dfc_params(struct vpfe_isif_dfc *dfc)
+{
+ int err = -EINVAL;
+ int i;
+
+ if (!dfc->en)
+ return 0;
+
+ if (dfc->corr_whole_line > 1) {
+ pr_err("Invalid corr_whole_line value\n");
+ return err;
+ }
+
+ if (dfc->def_level_shift > DM365_ISIF_MAX_VDFLSFT) {
+ pr_err("Invalid def_level_shift value\n");
+ return err;
+ }
+
+ if (dfc->def_sat_level > DM365_ISIF_MAX_VDFSLV) {
+ pr_err("Invalid def_sat_level value\n");
+ return err;
+ }
+
+ if (!dfc->num_vdefects ||
+ dfc->num_vdefects > VPFE_ISIF_VDFC_TABLE_SIZE) {
+ pr_err("Invalid num_vdefects value\n");
+ return err;
+ }
+
+ for (i = 0; i < VPFE_ISIF_VDFC_TABLE_SIZE; i++) {
+ if (dfc->table[i].pos_vert > DM365_ISIF_MAX_DFCMEM0) {
+ pr_err("Invalid pos_vert value\n");
+ return err;
+ }
+ if (dfc->table[i].pos_horz > DM365_ISIF_MAX_DFCMEM1) {
+ pr_err("Invalid pos_horz value\n");
+ return err;
+ }
+ }
+
+ return 0;
+}
+
+#define DM365_ISIF_MAX_CLVRV 0xfff
+#define DM365_ISIF_MAX_CLDC 0x1fff
+#define DM365_ISIF_MAX_CLHSH 0x1fff
+#define DM365_ISIF_MAX_CLHSV 0x1fff
+#define DM365_ISIF_MAX_CLVSH 0x1fff
+#define DM365_ISIF_MAX_CLVSV 0x1fff
+#define DM365_ISIF_MAX_HEIGHT_BLACK_REGION 0x1fff
+
+static int isif_validate_bclamp_params(struct vpfe_isif_black_clamp *bclamp)
+{
+ int err = -EINVAL;
+
+ if (bclamp->dc_offset > DM365_ISIF_MAX_CLDC) {
+ pr_err("Invalid bclamp dc_offset value\n");
+ return err;
+ }
+ if (!bclamp->en)
+ return 0;
+ if (bclamp->horz.clamp_pix_limit > 1) {
+ pr_err("Invalid bclamp horz clamp_pix_limit value\n");
+ return err;
+ }
+ if (bclamp->horz.win_count_calc < 1 ||
+ bclamp->horz.win_count_calc > 32) {
+ pr_err("Invalid bclamp horz win_count_calc value\n");
+ return err;
+ }
+ if (bclamp->horz.win_start_h_calc > DM365_ISIF_MAX_CLHSH) {
+ pr_err("Invalid bclamp win_start_v_calc value\n");
+ return err;
+ }
+
+ if (bclamp->horz.win_start_v_calc > DM365_ISIF_MAX_CLHSV) {
+ pr_err("Invalid bclamp win_start_v_calc value\n");
+ return err;
+ }
+ if (bclamp->vert.reset_clamp_val > DM365_ISIF_MAX_CLVRV) {
+ pr_err("Invalid bclamp reset_clamp_val value\n");
+ return err;
+ }
+ if (bclamp->vert.ob_v_sz_calc > DM365_ISIF_MAX_HEIGHT_BLACK_REGION) {
+ pr_err("Invalid bclamp ob_v_sz_calc value\n");
+ return err;
+ }
+ if (bclamp->vert.ob_start_h > DM365_ISIF_MAX_CLVSH) {
+ pr_err("Invalid bclamp ob_start_h value\n");
+ return err;
+ }
+ if (bclamp->vert.ob_start_v > DM365_ISIF_MAX_CLVSV) {
+ pr_err("Invalid bclamp ob_start_h value\n");
+ return err;
+ }
+ return 0;
+}
+
+static int
+isif_validate_raw_params(struct vpfe_isif_raw_config *params)
+{
+ int ret;
+
+ ret = isif_validate_df_csc_params(&params->df_csc);
+ if (ret)
+ return ret;
+ ret = isif_validate_dfc_params(&params->dfc);
+ if (ret)
+ return ret;
+ ret = isif_validate_bclamp_params(&params->bclamp);
+ return ret;
+}
+
+static int isif_set_params(struct v4l2_subdev *sd, void *params)
+{
+ struct vpfe_isif_device *isif = v4l2_get_subdevdata(sd);
+ struct vpfe_isif_raw_config isif_raw_params;
+ int ret = -EINVAL;
+
+ /* only raw module parameters can be set through the IOCTL */
+ if (isif->formats[ISIF_PAD_SINK].code != MEDIA_BUS_FMT_SGRBG12_1X12)
+ return ret;
+
+ memcpy(&isif_raw_params, params, sizeof(isif_raw_params));
+ if (!isif_validate_raw_params(&isif_raw_params)) {
+ memcpy(&isif->isif_cfg.bayer.config_params, &isif_raw_params,
+ sizeof(isif_raw_params));
+ ret = 0;
+ }
+ return ret;
+}
+/*
+ * isif_ioctl() - isif module private ioctl's
+ * @sd: VPFE isif V4L2 subdevice
+ * @cmd: ioctl command
+ * @arg: ioctl argument
+ *
+ * Return 0 on success or a negative error code otherwise.
+ */
+static long isif_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg)
+{
+ int ret;
+
+ switch (cmd) {
+ case VIDIOC_VPFE_ISIF_S_RAW_PARAMS:
+ ret = isif_set_params(sd, arg);
+ break;
+
+ case VIDIOC_VPFE_ISIF_G_RAW_PARAMS:
+ ret = isif_get_params(sd, arg);
+ break;
+
+ default:
+ ret = -ENOIOCTLCMD;
+ }
+ return ret;
+}
+
+static void isif_config_gain_offset(struct vpfe_isif_device *isif)
+{
+ struct vpfe_isif_gain_offsets_adj *gain_off_ptr =
+ &isif->isif_cfg.bayer.config_params.gain_offset;
+ void __iomem *base = isif->isif_cfg.base_addr;
+ u32 val;
+
+ val = ((gain_off_ptr->gain_sdram_en & 1) << GAIN_SDRAM_EN_SHIFT) |
+ ((gain_off_ptr->gain_ipipe_en & 1) << GAIN_IPIPE_EN_SHIFT) |
+ ((gain_off_ptr->gain_h3a_en & 1) << GAIN_H3A_EN_SHIFT) |
+ ((gain_off_ptr->offset_sdram_en & 1) << OFST_SDRAM_EN_SHIFT) |
+ ((gain_off_ptr->offset_ipipe_en & 1) << OFST_IPIPE_EN_SHIFT) |
+ ((gain_off_ptr->offset_h3a_en & 1) << OFST_H3A_EN_SHIFT);
+ isif_merge(base, GAIN_OFFSET_EN_MASK, val, CGAMMAWD);
+
+ isif_write(base, isif->isif_cfg.isif_gain_params.cr_gain, CRGAIN);
+ isif_write(base, isif->isif_cfg.isif_gain_params.cgr_gain, CGRGAIN);
+ isif_write(base, isif->isif_cfg.isif_gain_params.cgb_gain, CGBGAIN);
+ isif_write(base, isif->isif_cfg.isif_gain_params.cb_gain, CBGAIN);
+ isif_write(base, isif->isif_cfg.isif_gain_params.offset & OFFSET_MASK,
+ COFSTA);
+
+}
+
+static void isif_config_bclamp(struct vpfe_isif_device *isif,
+ struct vpfe_isif_black_clamp *bc)
+{
+ u32 val;
+
+ /**
+ * DC Offset is always added to image data irrespective of bc enable
+ * status
+ */
+ val = bc->dc_offset & ISIF_BC_DCOFFSET_MASK;
+ isif_write(isif->isif_cfg.base_addr, val, CLDCOFST);
+
+ if (!bc->en)
+ return;
+
+ val = (bc->bc_mode_color & ISIF_BC_MODE_COLOR_MASK) <<
+ ISIF_BC_MODE_COLOR_SHIFT;
+
+ /* Enable BC and horizontal clamp calculation paramaters */
+ val = val | 1 | ((bc->horz.mode & ISIF_HORZ_BC_MODE_MASK) <<
+ ISIF_HORZ_BC_MODE_SHIFT);
+
+ isif_write(isif->isif_cfg.base_addr, val, CLAMPCFG);
+
+ if (bc->horz.mode != VPFE_ISIF_HORZ_BC_DISABLE) {
+ /*
+ * Window count for calculation
+ * Base window selection
+ * pixel limit
+ * Horizontal size of window
+ * vertical size of the window
+ * Horizontal start position of the window
+ * Vertical start position of the window
+ */
+ val = (bc->horz.win_count_calc & ISIF_HORZ_BC_WIN_COUNT_MASK) |
+ ((bc->horz.base_win_sel_calc & 1) <<
+ ISIF_HORZ_BC_WIN_SEL_SHIFT) |
+ ((bc->horz.clamp_pix_limit & 1) <<
+ ISIF_HORZ_BC_PIX_LIMIT_SHIFT) |
+ ((bc->horz.win_h_sz_calc &
+ ISIF_HORZ_BC_WIN_H_SIZE_MASK) <<
+ ISIF_HORZ_BC_WIN_H_SIZE_SHIFT) |
+ ((bc->horz.win_v_sz_calc &
+ ISIF_HORZ_BC_WIN_V_SIZE_MASK) <<
+ ISIF_HORZ_BC_WIN_V_SIZE_SHIFT);
+
+ isif_write(isif->isif_cfg.base_addr, val, CLHWIN0);
+
+ val = bc->horz.win_start_h_calc & ISIF_HORZ_BC_WIN_START_H_MASK;
+ isif_write(isif->isif_cfg.base_addr, val, CLHWIN1);
+
+ val = bc->horz.win_start_v_calc & ISIF_HORZ_BC_WIN_START_V_MASK;
+ isif_write(isif->isif_cfg.base_addr, val, CLHWIN2);
+ }
+
+ /* vertical clamp calculation paramaters */
+ /* OB H Valid */
+ val = bc->vert.ob_h_sz_calc & ISIF_VERT_BC_OB_H_SZ_MASK;
+
+ /* Reset clamp value sel for previous line */
+ val |= (bc->vert.reset_val_sel & ISIF_VERT_BC_RST_VAL_SEL_MASK) <<
+ ISIF_VERT_BC_RST_VAL_SEL_SHIFT;
+
+ /* Line average coefficient */
+ val |= bc->vert.line_ave_coef << ISIF_VERT_BC_LINE_AVE_COEF_SHIFT;
+ isif_write(isif->isif_cfg.base_addr, val, CLVWIN0);
+
+ /* Configured reset value */
+ if (bc->vert.reset_val_sel == VPFE_ISIF_VERT_BC_USE_CONFIG_CLAMP_VAL) {
+ val = bc->vert.reset_clamp_val & ISIF_VERT_BC_RST_VAL_MASK;
+ isif_write(isif->isif_cfg.base_addr, val, CLVRV);
+ }
+
+ /* Optical Black horizontal start position */
+ val = bc->vert.ob_start_h & ISIF_VERT_BC_OB_START_HORZ_MASK;
+ isif_write(isif->isif_cfg.base_addr, val, CLVWIN1);
+
+ /* Optical Black vertical start position */
+ val = bc->vert.ob_start_v & ISIF_VERT_BC_OB_START_VERT_MASK;
+ isif_write(isif->isif_cfg.base_addr, val, CLVWIN2);
+
+ val = bc->vert.ob_v_sz_calc & ISIF_VERT_BC_OB_VERT_SZ_MASK;
+ isif_write(isif->isif_cfg.base_addr, val, CLVWIN3);
+
+ /* Vertical start position for BC subtraction */
+ val = bc->vert_start_sub & ISIF_BC_VERT_START_SUB_V_MASK;
+ isif_write(isif->isif_cfg.base_addr, val, CLSV);
+}
+
+/* This function will configure the window size to be capture in ISIF reg */
+static void
+isif_setwin(struct vpfe_isif_device *isif, struct v4l2_rect *image_win,
+ enum isif_frmfmt frm_fmt, int ppc, int mode)
+{
+ int horz_nr_pixels;
+ int vert_nr_lines;
+ int horz_start;
+ int vert_start;
+ int mid_img;
+
+ /*
+ * ppc - per pixel count. indicates how many pixels per cell
+ * output to SDRAM. example, for ycbcr, it is one y and one c, so 2.
+ * raw capture this is 1
+ */
+ horz_start = image_win->left << (ppc - 1);
+ horz_nr_pixels = (image_win->width << (ppc - 1)) - 1;
+
+ /* Writing the horizontal info into the registers */
+ isif_write(isif->isif_cfg.base_addr,
+ horz_start & START_PX_HOR_MASK, SPH);
+ isif_write(isif->isif_cfg.base_addr,
+ horz_nr_pixels & NUM_PX_HOR_MASK, LNH);
+ vert_start = image_win->top;
+
+ if (frm_fmt == ISIF_FRMFMT_INTERLACED) {
+ vert_nr_lines = (image_win->height >> 1) - 1;
+ vert_start >>= 1;
+ /* To account for VD since line 0 doesn't have any data */
+ vert_start += 1;
+ } else {
+ /* To account for VD since line 0 doesn't have any data */
+ vert_start += 1;
+ vert_nr_lines = image_win->height - 1;
+ /* configure VDINT0 and VDINT1 */
+ mid_img = vert_start + (image_win->height / 2);
+ isif_write(isif->isif_cfg.base_addr, mid_img, VDINT1);
+ }
+
+ if (!mode)
+ isif_write(isif->isif_cfg.base_addr, 0, VDINT0);
+ else
+ isif_write(isif->isif_cfg.base_addr, vert_nr_lines, VDINT0);
+ isif_write(isif->isif_cfg.base_addr,
+ vert_start & START_VER_ONE_MASK, SLV0);
+ isif_write(isif->isif_cfg.base_addr,
+ vert_start & START_VER_TWO_MASK, SLV1);
+ isif_write(isif->isif_cfg.base_addr,
+ vert_nr_lines & NUM_LINES_VER, LNV);
+}
+
+#define DM365_ISIF_DFCMWR_MEMORY_WRITE 1
+#define DM365_ISIF_DFCMRD_MEMORY_READ 0x2
+
+static void
+isif_config_dfc(struct vpfe_isif_device *isif, struct vpfe_isif_dfc *vdfc)
+{
+#define DFC_WRITE_WAIT_COUNT 1000
+ u32 count = DFC_WRITE_WAIT_COUNT;
+ u32 val;
+ int i;
+
+ if (!vdfc->en)
+ return;
+
+ /* Correction mode */
+ val = (vdfc->corr_mode & ISIF_VDFC_CORR_MOD_MASK) <<
+ ISIF_VDFC_CORR_MOD_SHIFT;
+
+ /* Correct whole line or partial */
+ if (vdfc->corr_whole_line)
+ val |= 1 << ISIF_VDFC_CORR_WHOLE_LN_SHIFT;
+
+ /* level shift value */
+ val |= (vdfc->def_level_shift & ISIF_VDFC_LEVEL_SHFT_MASK) <<
+ ISIF_VDFC_LEVEL_SHFT_SHIFT;
+
+ isif_write(isif->isif_cfg.base_addr, val, DFCCTL);
+
+ /* Defect saturation level */
+ val = vdfc->def_sat_level & ISIF_VDFC_SAT_LEVEL_MASK;
+ isif_write(isif->isif_cfg.base_addr, val, VDFSATLV);
+
+ isif_write(isif->isif_cfg.base_addr, vdfc->table[0].pos_vert &
+ ISIF_VDFC_POS_MASK, DFCMEM0);
+ isif_write(isif->isif_cfg.base_addr, vdfc->table[0].pos_horz &
+ ISIF_VDFC_POS_MASK, DFCMEM1);
+ if (vdfc->corr_mode == VPFE_ISIF_VDFC_NORMAL ||
+ vdfc->corr_mode == VPFE_ISIF_VDFC_HORZ_INTERPOL_IF_SAT) {
+ isif_write(isif->isif_cfg.base_addr,
+ vdfc->table[0].level_at_pos, DFCMEM2);
+ isif_write(isif->isif_cfg.base_addr,
+ vdfc->table[0].level_up_pixels, DFCMEM3);
+ isif_write(isif->isif_cfg.base_addr,
+ vdfc->table[0].level_low_pixels, DFCMEM4);
+ }
+
+ val = isif_read(isif->isif_cfg.base_addr, DFCMEMCTL);
+ /* set DFCMARST and set DFCMWR */
+ val |= 1 << ISIF_DFCMEMCTL_DFCMARST_SHIFT;
+ val |= 1;
+ isif_write(isif->isif_cfg.base_addr, val, DFCMEMCTL);
+
+ while (count && (isif_read(isif->isif_cfg.base_addr, DFCMEMCTL) & 0x01))
+ count--;
+
+ val = isif_read(isif->isif_cfg.base_addr, DFCMEMCTL);
+ if (!count) {
+ pr_debug("defect table write timeout !!\n");
+ return;
+ }
+
+ for (i = 1; i < vdfc->num_vdefects; i++) {
+ isif_write(isif->isif_cfg.base_addr, vdfc->table[i].pos_vert &
+ ISIF_VDFC_POS_MASK, DFCMEM0);
+
+ isif_write(isif->isif_cfg.base_addr, vdfc->table[i].pos_horz &
+ ISIF_VDFC_POS_MASK, DFCMEM1);
+
+ if (vdfc->corr_mode == VPFE_ISIF_VDFC_NORMAL ||
+ vdfc->corr_mode == VPFE_ISIF_VDFC_HORZ_INTERPOL_IF_SAT) {
+ isif_write(isif->isif_cfg.base_addr,
+ vdfc->table[i].level_at_pos, DFCMEM2);
+ isif_write(isif->isif_cfg.base_addr,
+ vdfc->table[i].level_up_pixels, DFCMEM3);
+ isif_write(isif->isif_cfg.base_addr,
+ vdfc->table[i].level_low_pixels, DFCMEM4);
+ }
+ val = isif_read(isif->isif_cfg.base_addr, DFCMEMCTL);
+ /* clear DFCMARST and set DFCMWR */
+ val &= ~(1 << ISIF_DFCMEMCTL_DFCMARST_SHIFT);
+ val |= 1;
+ isif_write(isif->isif_cfg.base_addr, val, DFCMEMCTL);
+
+ count = DFC_WRITE_WAIT_COUNT;
+ while (count && (isif_read(isif->isif_cfg.base_addr,
+ DFCMEMCTL) & 0x01))
+ count--;
+
+ val = isif_read(isif->isif_cfg.base_addr, DFCMEMCTL);
+ if (!count) {
+ pr_debug("defect table write timeout !!\n");
+ return;
+ }
+ }
+ if (vdfc->num_vdefects < VPFE_ISIF_VDFC_TABLE_SIZE) {
+ /* Extra cycle needed */
+ isif_write(isif->isif_cfg.base_addr, 0, DFCMEM0);
+ isif_write(isif->isif_cfg.base_addr,
+ DM365_ISIF_MAX_DFCMEM1, DFCMEM1);
+ isif_write(isif->isif_cfg.base_addr,
+ DM365_ISIF_DFCMWR_MEMORY_WRITE, DFCMEMCTL);
+ }
+ /* enable VDFC */
+ isif_merge(isif->isif_cfg.base_addr, (1 << ISIF_VDFC_EN_SHIFT),
+ (1 << ISIF_VDFC_EN_SHIFT), DFCCTL);
+
+ isif_merge(isif->isif_cfg.base_addr, (1 << ISIF_VDFC_EN_SHIFT),
+ (0 << ISIF_VDFC_EN_SHIFT), DFCCTL);
+
+ isif_write(isif->isif_cfg.base_addr, 0x6, DFCMEMCTL);
+ for (i = 0; i < vdfc->num_vdefects; i++) {
+ count = DFC_WRITE_WAIT_COUNT;
+ while (count &&
+ (isif_read(isif->isif_cfg.base_addr, DFCMEMCTL) & 0x2))
+ count--;
+ val = isif_read(isif->isif_cfg.base_addr, DFCMEMCTL);
+ if (!count) {
+ pr_debug("defect table write timeout !!\n");
+ return;
+ }
+ isif_write(isif->isif_cfg.base_addr,
+ DM365_ISIF_DFCMRD_MEMORY_READ, DFCMEMCTL);
+ }
+}
+
+static void
+isif_config_csc(struct vpfe_isif_device *isif, struct vpfe_isif_df_csc *df_csc)
+{
+ u32 val1;
+ u32 val2;
+ u32 i;
+
+ if (!df_csc->csc.en) {
+ isif_write(isif->isif_cfg.base_addr, 0, CSCCTL);
+ return;
+ }
+ /* initialize all bits to 0 */
+ val1 = 0;
+ for (i = 0; i < VPFE_ISIF_CSC_NUM_COEFF; i++) {
+ if ((i % 2) == 0) {
+ /* CSCM - LSB */
+ val1 = ((df_csc->csc.coeff[i].integer &
+ ISIF_CSC_COEF_INTEG_MASK) <<
+ ISIF_CSC_COEF_INTEG_SHIFT) |
+ ((df_csc->csc.coeff[i].decimal &
+ ISIF_CSC_COEF_DECIMAL_MASK));
+ } else {
+
+ /* CSCM - MSB */
+ val2 = ((df_csc->csc.coeff[i].integer &
+ ISIF_CSC_COEF_INTEG_MASK) <<
+ ISIF_CSC_COEF_INTEG_SHIFT) |
+ ((df_csc->csc.coeff[i].decimal &
+ ISIF_CSC_COEF_DECIMAL_MASK));
+ val2 <<= ISIF_CSCM_MSB_SHIFT;
+ val2 |= val1;
+ isif_write(isif->isif_cfg.base_addr, val2,
+ (CSCM0 + ((i-1) << 1)));
+ }
+ }
+ /* program the active area */
+ isif_write(isif->isif_cfg.base_addr, df_csc->start_pix &
+ ISIF_DF_CSC_SPH_MASK, FMTSPH);
+ /*
+ * one extra pixel as required for CSC. Actually number of
+ * pixel - 1 should be configured in this register. So we
+ * need to subtract 1 before writing to FMTSPH, but we will
+ * not do this since csc requires one extra pixel
+ */
+ isif_write(isif->isif_cfg.base_addr, df_csc->num_pixels &
+ ISIF_DF_CSC_SPH_MASK, FMTLNH);
+ isif_write(isif->isif_cfg.base_addr, df_csc->start_line &
+ ISIF_DF_CSC_SPH_MASK, FMTSLV);
+ /*
+ * one extra line as required for CSC. See reason documented for
+ * num_pixels
+ */
+ isif_write(isif->isif_cfg.base_addr, df_csc->num_lines &
+ ISIF_DF_CSC_SPH_MASK, FMTLNV);
+ /* Enable CSC */
+ isif_write(isif->isif_cfg.base_addr, 1, CSCCTL);
+}
+
+static void
+isif_config_linearization(struct vpfe_isif_device *isif,
+ struct vpfe_isif_linearize *linearize)
+{
+ u32 val;
+ u32 i;
+
+ if (!linearize->en) {
+ isif_write(isif->isif_cfg.base_addr, 0, LINCFG0);
+ return;
+ }
+ /* shift value for correction */
+ val = (linearize->corr_shft & ISIF_LIN_CORRSFT_MASK) <<
+ ISIF_LIN_CORRSFT_SHIFT;
+ /* enable */
+ val |= 1;
+ isif_write(isif->isif_cfg.base_addr, val, LINCFG0);
+ /* Scale factor */
+ val = (linearize->scale_fact.integer & 1) <<
+ ISIF_LIN_SCALE_FACT_INTEG_SHIFT;
+ val |= linearize->scale_fact.decimal & ISIF_LIN_SCALE_FACT_DECIMAL_MASK;
+ isif_write(isif->isif_cfg.base_addr, val, LINCFG1);
+
+ for (i = 0; i < VPFE_ISIF_LINEAR_TAB_SIZE; i++) {
+ val = linearize->table[i] & ISIF_LIN_ENTRY_MASK;
+ if (i%2)
+ isif_regw_lin_tbl(isif, val, ((i >> 1) << 2), 1);
+ else
+ isif_regw_lin_tbl(isif, val, ((i >> 1) << 2), 0);
+ }
+}
+
+static void
+isif_config_culling(struct vpfe_isif_device *isif, struct vpfe_isif_cul *cul)
+{
+ u32 val;
+
+ /* Horizontal pattern */
+ val = cul->hcpat_even << CULL_PAT_EVEN_LINE_SHIFT;
+ val |= cul->hcpat_odd;
+ isif_write(isif->isif_cfg.base_addr, val, CULH);
+ /* vertical pattern */
+ isif_write(isif->isif_cfg.base_addr, cul->vcpat, CULV);
+ /* LPF */
+ isif_merge(isif->isif_cfg.base_addr, ISIF_LPF_MASK << ISIF_LPF_SHIFT,
+ cul->en_lpf << ISIF_LPF_SHIFT, MODESET);
+}
+
+static int isif_get_pix_fmt(u32 mbus_code)
+{
+ switch (mbus_code) {
+ case MEDIA_BUS_FMT_SGRBG10_ALAW8_1X8:
+ case MEDIA_BUS_FMT_SGRBG10_DPCM8_1X8:
+ case MEDIA_BUS_FMT_SGRBG12_1X12:
+ return ISIF_PIXFMT_RAW;
+
+ case MEDIA_BUS_FMT_YUYV8_2X8:
+ case MEDIA_BUS_FMT_UYVY8_2X8:
+ case MEDIA_BUS_FMT_YUYV10_2X10:
+ case MEDIA_BUS_FMT_Y8_1X8:
+ return ISIF_PIXFMT_YCBCR_8BIT;
+
+ case MEDIA_BUS_FMT_YUYV8_1X16:
+ case MEDIA_BUS_FMT_YUYV10_1X20:
+ return ISIF_PIXFMT_YCBCR_16BIT;
+
+ default:
+ break;
+ }
+ return -EINVAL;
+}
+
+#define ISIF_INTERLACE_INVERSE_MODE 0x4b6d
+#define ISIF_INTERLACE_NON_INVERSE_MODE 0x0b6d
+#define ISIF_PROGRESSIVE_INVERSE_MODE 0x4000
+#define ISIF_PROGRESSIVE_NON_INVERSE_MODE 0x0000
+
+static int isif_config_raw(struct v4l2_subdev *sd, int mode)
+{
+ struct vpfe_isif_device *isif = v4l2_get_subdevdata(sd);
+ struct isif_params_raw *params = &isif->isif_cfg.bayer;
+ struct vpfe_isif_raw_config *module_params =
+ &isif->isif_cfg.bayer.config_params;
+ struct v4l2_mbus_framefmt *format;
+ int pix_fmt;
+ u32 val;
+
+ format = &isif->formats[ISIF_PAD_SINK];
+
+ /* In case of user has set BT656IF earlier, it should be reset
+ * when configuring for raw input.
+ */
+ isif_write(isif->isif_cfg.base_addr, 0, REC656IF);
+ /* Configure CCDCFG register
+ * Set CCD Not to swap input since input is RAW data
+ * Set FID detection function to Latch at V-Sync
+ * Set WENLOG - isif valid area
+ * Set TRGSEL
+ * Set EXTRG
+ * Packed to 8 or 16 bits
+ */
+ val = ISIF_YCINSWP_RAW | ISIF_CCDCFG_FIDMD_LATCH_VSYNC |
+ ISIF_CCDCFG_WENLOG_AND | ISIF_CCDCFG_TRGSEL_WEN |
+ ISIF_CCDCFG_EXTRG_DISABLE | (isif->isif_cfg.data_pack &
+ ISIF_DATA_PACK_MASK);
+ isif_write(isif->isif_cfg.base_addr, val, CCDCFG);
+
+ pix_fmt = isif_get_pix_fmt(format->code);
+ if (pix_fmt < 0) {
+ pr_debug("Invalid pix_fmt(input mode)\n");
+ return -EINVAL;
+ }
+ /*
+ * Configure the vertical sync polarity(MODESET.VDPOL)
+ * Configure the horizontal sync polarity (MODESET.HDPOL)
+ * Configure frame id polarity (MODESET.FLDPOL)
+ * Configure data polarity
+ * Configure External WEN Selection
+ * Configure frame format(progressive or interlace)
+ * Configure pixel format (Input mode)
+ * Configure the data shift
+ */
+ val = ISIF_VDHDOUT_INPUT | ((params->vd_pol & ISIF_VD_POL_MASK) <<
+ ISIF_VD_POL_SHIFT) | ((params->hd_pol & ISIF_HD_POL_MASK) <<
+ ISIF_HD_POL_SHIFT) | ((params->fid_pol & ISIF_FID_POL_MASK) <<
+ ISIF_FID_POL_SHIFT) | ((ISIF_DATAPOL_NORMAL &
+ ISIF_DATAPOL_MASK) << ISIF_DATAPOL_SHIFT) | ((ISIF_EXWEN_DISABLE &
+ ISIF_EXWEN_MASK) << ISIF_EXWEN_SHIFT) | ((params->frm_fmt &
+ ISIF_FRM_FMT_MASK) << ISIF_FRM_FMT_SHIFT) | ((pix_fmt &
+ ISIF_INPUT_MASK) << ISIF_INPUT_SHIFT);
+
+ /* currently only MEDIA_BUS_FMT_SGRBG12_1X12 is
+ * supported. shift appropriately depending on
+ * different MBUS fmt's added
+ */
+ if (format->code == MEDIA_BUS_FMT_SGRBG12_1X12)
+ val |= ((VPFE_ISIF_NO_SHIFT &
+ ISIF_DATASFT_MASK) << ISIF_DATASFT_SHIFT);
+
+ isif_write(isif->isif_cfg.base_addr, val, MODESET);
+ /*
+ * Configure GAMMAWD register
+ * CFA pattern setting
+ */
+ val = (params->cfa_pat & ISIF_GAMMAWD_CFA_MASK) <<
+ ISIF_GAMMAWD_CFA_SHIFT;
+ /* Gamma msb */
+ if (params->v4l2_pix_fmt == V4L2_PIX_FMT_SGRBG10ALAW8)
+ val = val | ISIF_ALAW_ENABLE;
+
+ val = val | ((params->data_msb & ISIF_ALAW_GAMA_WD_MASK) <<
+ ISIF_ALAW_GAMA_WD_SHIFT);
+
+ isif_write(isif->isif_cfg.base_addr, val, CGAMMAWD);
+ /* Configure DPCM compression settings */
+ if (params->v4l2_pix_fmt == V4L2_PIX_FMT_SGRBG10DPCM8) {
+ val = 1 << ISIF_DPCM_EN_SHIFT;
+ val |= (params->dpcm_predictor &
+ ISIF_DPCM_PREDICTOR_MASK) << ISIF_DPCM_PREDICTOR_SHIFT;
+ }
+ isif_write(isif->isif_cfg.base_addr, val, MISC);
+ /* Configure Gain & Offset */
+ isif_config_gain_offset(isif);
+ /* Configure Color pattern */
+ if (format->code == MEDIA_BUS_FMT_SGRBG12_1X12)
+ val = isif_sgrbg_pattern;
+ else
+ /* default set to rggb */
+ val = isif_srggb_pattern;
+
+ isif_write(isif->isif_cfg.base_addr, val, CCOLP);
+
+ /* Configure HSIZE register */
+ val = (params->horz_flip_en & ISIF_HSIZE_FLIP_MASK) <<
+ ISIF_HSIZE_FLIP_SHIFT;
+
+ /* calculate line offset in 32 bytes based on pack value */
+ if (isif->isif_cfg.data_pack == ISIF_PACK_8BIT)
+ val |= ((params->win.width + 31) >> 5) & ISIF_LINEOFST_MASK;
+ else if (isif->isif_cfg.data_pack == ISIF_PACK_12BIT)
+ val |= ((((params->win.width + (params->win.width >> 2)) +
+ 31) >> 5) & ISIF_LINEOFST_MASK);
+ else
+ val |= (((params->win.width * 2) + 31) >> 5) &
+ ISIF_LINEOFST_MASK;
+ isif_write(isif->isif_cfg.base_addr, val, HSIZE);
+ /* Configure SDOFST register */
+ if (params->frm_fmt == ISIF_FRMFMT_INTERLACED) {
+ if (params->image_invert_en)
+ /* For interlace inverse mode */
+ isif_write(isif->isif_cfg.base_addr,
+ ISIF_INTERLACE_INVERSE_MODE, SDOFST);
+ else
+ /* For interlace non inverse mode */
+ isif_write(isif->isif_cfg.base_addr,
+ ISIF_INTERLACE_NON_INVERSE_MODE, SDOFST);
+ } else if (params->frm_fmt == ISIF_FRMFMT_PROGRESSIVE) {
+ if (params->image_invert_en)
+ isif_write(isif->isif_cfg.base_addr,
+ ISIF_PROGRESSIVE_INVERSE_MODE, SDOFST);
+ else
+ /* For progessive non inverse mode */
+ isif_write(isif->isif_cfg.base_addr,
+ ISIF_PROGRESSIVE_NON_INVERSE_MODE, SDOFST);
+ }
+ /* Configure video window */
+ isif_setwin(isif, &params->win, params->frm_fmt, 1, mode);
+ /* Configure Black Clamp */
+ isif_config_bclamp(isif, &module_params->bclamp);
+ /* Configure Vertical Defection Pixel Correction */
+ isif_config_dfc(isif, &module_params->dfc);
+ if (!module_params->df_csc.df_or_csc)
+ /* Configure Color Space Conversion */
+ isif_config_csc(isif, &module_params->df_csc);
+
+ isif_config_linearization(isif, &module_params->linearize);
+ /* Configure Culling */
+ isif_config_culling(isif, &module_params->culling);
+ /* Configure Horizontal and vertical offsets(DFC,LSC,Gain) */
+ val = module_params->horz_offset & ISIF_DATA_H_OFFSET_MASK;
+ isif_write(isif->isif_cfg.base_addr, val, DATAHOFST);
+
+ val = module_params->vert_offset & ISIF_DATA_V_OFFSET_MASK;
+ isif_write(isif->isif_cfg.base_addr, val, DATAVOFST);
+
+ return 0;
+}
+
+#define DM365_ISIF_HSIZE_MASK 0xffffffe0
+#define DM365_ISIF_SDOFST_2_LINES 0x00000249
+
+/* This function will configure ISIF for YCbCr parameters. */
+static int isif_config_ycbcr(struct v4l2_subdev *sd, int mode)
+{
+ struct vpfe_isif_device *isif = v4l2_get_subdevdata(sd);
+ struct isif_ycbcr_config *params = &isif->isif_cfg.ycbcr;
+ struct v4l2_mbus_framefmt *format;
+ int pix_fmt;
+ u32 modeset;
+ u32 ccdcfg;
+
+ format = &isif->formats[ISIF_PAD_SINK];
+ /*
+ * first reset the ISIF
+ * all registers have default values after reset
+ * This is important since we assume default values to be set in
+ * a lot of registers that we didn't touch
+ */
+ /* start with all bits zero */
+ ccdcfg = modeset = 0;
+ pix_fmt = isif_get_pix_fmt(format->code);
+ if (pix_fmt < 0) {
+ pr_debug("Invalid pix_fmt(input mode)\n");
+ return -EINVAL;
+ }
+ /* configure pixel format or input mode */
+ modeset = modeset | ((pix_fmt & ISIF_INPUT_MASK) <<
+ ISIF_INPUT_SHIFT) | ((params->frm_fmt & ISIF_FRM_FMT_MASK) <<
+ ISIF_FRM_FMT_SHIFT) | (((params->fid_pol &
+ ISIF_FID_POL_MASK) << ISIF_FID_POL_SHIFT)) |
+ (((params->hd_pol & ISIF_HD_POL_MASK) << ISIF_HD_POL_SHIFT)) |
+ (((params->vd_pol & ISIF_VD_POL_MASK) << ISIF_VD_POL_SHIFT));
+ /* pack the data to 8-bit CCDCCFG */
+ switch (format->code) {
+ case MEDIA_BUS_FMT_YUYV8_2X8:
+ case MEDIA_BUS_FMT_UYVY8_2X8:
+ if (pix_fmt != ISIF_PIXFMT_YCBCR_8BIT) {
+ pr_debug("Invalid pix_fmt(input mode)\n");
+ return -EINVAL;
+ }
+ modeset |= ((VPFE_PINPOL_NEGATIVE & ISIF_VD_POL_MASK) <<
+ ISIF_VD_POL_SHIFT);
+ isif_write(isif->isif_cfg.base_addr, 3, REC656IF);
+ ccdcfg = ccdcfg | ISIF_PACK_8BIT | ISIF_YCINSWP_YCBCR;
+ break;
+
+ case MEDIA_BUS_FMT_YUYV10_2X10:
+ if (pix_fmt != ISIF_PIXFMT_YCBCR_8BIT) {
+ pr_debug("Invalid pix_fmt(input mode)\n");
+ return -EINVAL;
+ }
+ /* setup BT.656, embedded sync */
+ isif_write(isif->isif_cfg.base_addr, 3, REC656IF);
+ /* enable 10 bit mode in ccdcfg */
+ ccdcfg = ccdcfg | ISIF_PACK_8BIT | ISIF_YCINSWP_YCBCR |
+ ISIF_BW656_ENABLE;
+ break;
+
+ case MEDIA_BUS_FMT_YUYV10_1X20:
+ if (pix_fmt != ISIF_PIXFMT_YCBCR_16BIT) {
+ pr_debug("Invalid pix_fmt(input mode)\n");
+ return -EINVAL;
+ }
+ isif_write(isif->isif_cfg.base_addr, 3, REC656IF);
+ break;
+
+ case MEDIA_BUS_FMT_Y8_1X8:
+ ccdcfg |= ISIF_PACK_8BIT;
+ ccdcfg |= ISIF_YCINSWP_YCBCR;
+ if (pix_fmt != ISIF_PIXFMT_YCBCR_8BIT) {
+ pr_debug("Invalid pix_fmt(input mode)\n");
+ return -EINVAL;
+ }
+ break;
+
+ case MEDIA_BUS_FMT_YUYV8_1X16:
+ if (pix_fmt != ISIF_PIXFMT_YCBCR_16BIT) {
+ pr_debug("Invalid pix_fmt(input mode)\n");
+ return -EINVAL;
+ }
+ break;
+
+ default:
+ /* should never come here */
+ pr_debug("Invalid interface type\n");
+ return -EINVAL;
+ }
+ isif_write(isif->isif_cfg.base_addr, modeset, MODESET);
+ /* Set up pix order */
+ ccdcfg |= (params->pix_order & ISIF_PIX_ORDER_MASK) <<
+ ISIF_PIX_ORDER_SHIFT;
+ isif_write(isif->isif_cfg.base_addr, ccdcfg, CCDCFG);
+ /* configure video window */
+ if (format->code == MEDIA_BUS_FMT_YUYV10_1X20 ||
+ format->code == MEDIA_BUS_FMT_YUYV8_1X16)
+ isif_setwin(isif, &params->win, params->frm_fmt, 1, mode);
+ else
+ isif_setwin(isif, &params->win, params->frm_fmt, 2, mode);
+
+ /*
+ * configure the horizontal line offset
+ * this is done by rounding up width to a multiple of 16 pixels
+ * and multiply by two to account for y:cb:cr 4:2:2 data
+ */
+ isif_write(isif->isif_cfg.base_addr,
+ ((((params->win.width * 2) + 31) &
+ DM365_ISIF_HSIZE_MASK) >> 5), HSIZE);
+
+ /* configure the memory line offset */
+ if (params->frm_fmt == ISIF_FRMFMT_INTERLACED &&
+ params->buf_type == ISIF_BUFTYPE_FLD_INTERLEAVED)
+ /* two fields are interleaved in memory */
+ isif_write(isif->isif_cfg.base_addr,
+ DM365_ISIF_SDOFST_2_LINES, SDOFST);
+ return 0;
+}
+
+static int isif_configure(struct v4l2_subdev *sd, int mode)
+{
+ struct vpfe_isif_device *isif = v4l2_get_subdevdata(sd);
+ struct v4l2_mbus_framefmt *format;
+
+ format = &isif->formats[ISIF_PAD_SINK];
+
+ switch (format->code) {
+ case MEDIA_BUS_FMT_SGRBG10_ALAW8_1X8:
+ case MEDIA_BUS_FMT_SGRBG10_DPCM8_1X8:
+ case MEDIA_BUS_FMT_SGRBG12_1X12:
+ return isif_config_raw(sd, mode);
+
+ case MEDIA_BUS_FMT_YUYV8_2X8:
+ case MEDIA_BUS_FMT_UYVY8_2X8:
+ case MEDIA_BUS_FMT_YUYV10_2X10:
+ case MEDIA_BUS_FMT_Y8_1X8:
+ case MEDIA_BUS_FMT_YUYV8_1X16:
+ case MEDIA_BUS_FMT_YUYV10_1X20:
+ return isif_config_ycbcr(sd, mode);
+
+ default:
+ break;
+ }
+ return -EINVAL;
+}
+
+/*
+ * isif_set_stream() - Enable/Disable streaming on the ISIF module
+ * @sd: VPFE ISIF V4L2 subdevice
+ * @enable: Enable/disable stream
+ */
+static int isif_set_stream(struct v4l2_subdev *sd, int enable)
+{
+ struct vpfe_isif_device *isif = v4l2_get_subdevdata(sd);
+ int ret;
+
+ if (enable) {
+ ret = isif_configure(sd,
+ (isif->output == ISIF_OUTPUT_MEMORY) ? 0 : 1);
+ if (ret)
+ return ret;
+ if (isif->output == ISIF_OUTPUT_MEMORY)
+ isif_enable_output_to_sdram(isif, 1);
+ isif_enable(isif, 1);
+ } else {
+ isif_enable(isif, 0);
+ isif_enable_output_to_sdram(isif, 0);
+ }
+
+ return 0;
+}
+
+/*
+ * __isif_get_format() - helper function for getting isif format
+ * @isif: pointer to isif private structure.
+ * @pad: pad number.
+ * @cfg: V4L2 subdev pad config
+ * @which: wanted subdev format.
+ */
+static struct v4l2_mbus_framefmt *
+__isif_get_format(struct vpfe_isif_device *isif, struct v4l2_subdev_pad_config *cfg,
+ unsigned int pad, enum v4l2_subdev_format_whence which)
+{
+ if (which == V4L2_SUBDEV_FORMAT_TRY) {
+ struct v4l2_subdev_format fmt;
+
+ fmt.pad = pad;
+ fmt.which = which;
+
+ return v4l2_subdev_get_try_format(&isif->subdev, cfg, pad);
+ }
+ return &isif->formats[pad];
+}
+
+/*
+ * isif_set_format() - set format on pad
+ * @sd : VPFE ISIF device
+ * @cfg : V4L2 subdev pad config
+ * @fmt : pointer to v4l2 subdev format structure
+ *
+ * Return 0 on success or -EINVAL if format or pad is invalid
+ */
+static int
+isif_set_format(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_format *fmt)
+{
+ struct vpfe_isif_device *isif = v4l2_get_subdevdata(sd);
+ struct vpfe_device *vpfe_dev = to_vpfe_device(isif);
+ struct v4l2_mbus_framefmt *format;
+
+ format = __isif_get_format(isif, cfg, fmt->pad, fmt->which);
+ if (format == NULL)
+ return -EINVAL;
+
+ isif_try_format(isif, cfg, fmt);
+ memcpy(format, &fmt->format, sizeof(*format));
+
+ if (fmt->which == V4L2_SUBDEV_FORMAT_TRY)
+ return 0;
+
+ if (fmt->pad == ISIF_PAD_SOURCE)
+ return isif_config_format(vpfe_dev, fmt->pad);
+
+ return 0;
+}
+
+/*
+ * isif_get_format() - Retrieve the video format on a pad
+ * @sd: VPFE ISIF V4L2 subdevice
+ * @cfg: V4L2 subdev pad config
+ * @fmt: pointer to v4l2 subdev format structure
+ *
+ * Return 0 on success or -EINVAL if the pad is invalid or doesn't correspond
+ * to the format type.
+ */
+static int
+isif_get_format(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_format *fmt)
+{
+ struct vpfe_isif_device *vpfe_isif = v4l2_get_subdevdata(sd);
+ struct v4l2_mbus_framefmt *format;
+
+ format = __isif_get_format(vpfe_isif, cfg, fmt->pad, fmt->which);
+ if (format == NULL)
+ return -EINVAL;
+
+ memcpy(&fmt->format, format, sizeof(fmt->format));
+
+ return 0;
+}
+
+/*
+ * isif_enum_frame_size() - enum frame sizes on pads
+ * @sd: VPFE isif V4L2 subdevice
+ * @cfg: V4L2 subdev pad config
+ * @code: pointer to v4l2_subdev_frame_size_enum structure
+ */
+static int
+isif_enum_frame_size(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_frame_size_enum *fse)
+{
+ struct vpfe_isif_device *isif = v4l2_get_subdevdata(sd);
+ struct v4l2_subdev_format format;
+
+ if (fse->index != 0)
+ return -EINVAL;
+
+ format.pad = fse->pad;
+ format.format.code = fse->code;
+ format.format.width = 1;
+ format.format.height = 1;
+ format.which = fse->which;
+ isif_try_format(isif, cfg, &format);
+ fse->min_width = format.format.width;
+ fse->min_height = format.format.height;
+
+ if (format.format.code != fse->code)
+ return -EINVAL;
+
+ format.pad = fse->pad;
+ format.format.code = fse->code;
+ format.format.width = -1;
+ format.format.height = -1;
+ format.which = fse->which;
+ isif_try_format(isif, cfg, &format);
+ fse->max_width = format.format.width;
+ fse->max_height = format.format.height;
+
+ return 0;
+}
+
+/*
+ * isif_enum_mbus_code() - enum mbus codes for pads
+ * @sd: VPFE isif V4L2 subdevice
+ * @cfg: V4L2 subdev pad config
+ * @code: pointer to v4l2_subdev_mbus_code_enum structure
+ */
+static int
+isif_enum_mbus_code(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_mbus_code_enum *code)
+{
+ switch (code->pad) {
+ case ISIF_PAD_SINK:
+ case ISIF_PAD_SOURCE:
+ if (code->index >= ARRAY_SIZE(isif_fmts))
+ return -EINVAL;
+ code->code = isif_fmts[code->index];
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/*
+ * isif_pad_set_selection() - set crop rectangle on pad
+ * @sd: VPFE isif V4L2 subdevice
+ * @cfg: V4L2 subdev pad config
+ * @code: pointer to v4l2_subdev_mbus_code_enum structure
+ *
+ * Return 0 on success, -EINVAL if pad is invalid
+ */
+static int
+isif_pad_set_selection(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_selection *sel)
+{
+ struct vpfe_isif_device *vpfe_isif = v4l2_get_subdevdata(sd);
+ struct v4l2_mbus_framefmt *format;
+
+ /* check whether it's a valid pad and target */
+ if (sel->pad != ISIF_PAD_SINK || sel->target != V4L2_SEL_TGT_CROP)
+ return -EINVAL;
+
+ format = __isif_get_format(vpfe_isif, cfg, sel->pad, sel->which);
+ if (format == NULL)
+ return -EINVAL;
+
+ /* check wether crop rect is within limits */
+ if (sel->r.top < 0 || sel->r.left < 0 ||
+ (sel->r.left + sel->r.width >
+ vpfe_isif->formats[ISIF_PAD_SINK].width) ||
+ (sel->r.top + sel->r.height >
+ vpfe_isif->formats[ISIF_PAD_SINK].height)) {
+ sel->r.left = 0;
+ sel->r.top = 0;
+ sel->r.width = format->width;
+ sel->r.height = format->height;
+ }
+ /* adjust the width to 16 pixel boundary */
+ sel->r.width = ((sel->r.width + 15) & ~0xf);
+ vpfe_isif->crop = sel->r;
+ if (sel->which == V4L2_SUBDEV_FORMAT_ACTIVE) {
+ isif_set_image_window(vpfe_isif);
+ } else {
+ struct v4l2_rect *rect;
+
+ rect = v4l2_subdev_get_try_crop(sd, cfg, ISIF_PAD_SINK);
+ memcpy(rect, &vpfe_isif->crop, sizeof(*rect));
+ }
+ return 0;
+}
+
+/*
+ * isif_pad_get_selection() - get crop rectangle on pad
+ * @sd: VPFE isif V4L2 subdevice
+ * @cfg: V4L2 subdev pad config
+ * @code: pointer to v4l2_subdev_mbus_code_enum structure
+ *
+ * Return 0 on success, -EINVAL if pad is invalid
+ */
+static int
+isif_pad_get_selection(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_selection *sel)
+{
+ struct vpfe_isif_device *vpfe_isif = v4l2_get_subdevdata(sd);
+
+ /* check whether it's a valid pad and target */
+ if (sel->pad != ISIF_PAD_SINK || sel->target != V4L2_SEL_TGT_CROP)
+ return -EINVAL;
+
+ if (sel->which == V4L2_SUBDEV_FORMAT_TRY) {
+ struct v4l2_rect *rect;
+
+ rect = v4l2_subdev_get_try_crop(sd, cfg, ISIF_PAD_SINK);
+ memcpy(&sel->r, rect, sizeof(*rect));
+ } else {
+ sel->r = vpfe_isif->crop;
+ }
+
+ return 0;
+}
+
+/*
+ * isif_init_formats() - Initialize formats on all pads
+ * @sd: VPFE isif V4L2 subdevice
+ * @fh: V4L2 subdev file handle
+ *
+ * Initialize all pad formats with default values. Try formats are initialized
+ * on the file handle.
+ */
+static int
+isif_init_formats(struct v4l2_subdev *sd,
+ struct v4l2_subdev_fh *fh)
+{
+ struct v4l2_subdev_format format;
+ struct v4l2_subdev_selection sel;
+
+ memset(&format, 0, sizeof(format));
+ format.pad = ISIF_PAD_SINK;
+ format.which = V4L2_SUBDEV_FORMAT_TRY;
+ format.format.code = MEDIA_BUS_FMT_SGRBG12_1X12;
+ format.format.width = MAX_WIDTH;
+ format.format.height = MAX_HEIGHT;
+ isif_set_format(sd, fh->pad, &format);
+
+ memset(&format, 0, sizeof(format));
+ format.pad = ISIF_PAD_SOURCE;
+ format.which = V4L2_SUBDEV_FORMAT_TRY;
+ format.format.code = MEDIA_BUS_FMT_SGRBG12_1X12;
+ format.format.width = MAX_WIDTH;
+ format.format.height = MAX_HEIGHT;
+ isif_set_format(sd, fh->pad, &format);
+
+ memset(&sel, 0, sizeof(sel));
+ sel.pad = ISIF_PAD_SINK;
+ sel.which = V4L2_SUBDEV_FORMAT_TRY;
+ sel.target = V4L2_SEL_TGT_CROP;
+ sel.r.width = MAX_WIDTH;
+ sel.r.height = MAX_HEIGHT;
+ isif_pad_set_selection(sd, fh->pad, &sel);
+
+ return 0;
+}
+
+/* subdev core operations */
+static const struct v4l2_subdev_core_ops isif_v4l2_core_ops = {
+ .ioctl = isif_ioctl,
+};
+
+/* subdev file operations */
+static const struct v4l2_subdev_internal_ops isif_v4l2_internal_ops = {
+ .open = isif_init_formats,
+};
+
+/* subdev video operations */
+static const struct v4l2_subdev_video_ops isif_v4l2_video_ops = {
+ .s_stream = isif_set_stream,
+};
+
+/* subdev pad operations */
+static const struct v4l2_subdev_pad_ops isif_v4l2_pad_ops = {
+ .enum_mbus_code = isif_enum_mbus_code,
+ .enum_frame_size = isif_enum_frame_size,
+ .get_fmt = isif_get_format,
+ .set_fmt = isif_set_format,
+ .set_selection = isif_pad_set_selection,
+ .get_selection = isif_pad_get_selection,
+};
+
+/* subdev operations */
+static const struct v4l2_subdev_ops isif_v4l2_ops = {
+ .core = &isif_v4l2_core_ops,
+ .video = &isif_v4l2_video_ops,
+ .pad = &isif_v4l2_pad_ops,
+};
+
+/*
+ * Media entity operations
+ */
+
+/*
+ * isif_link_setup() - Setup isif connections
+ * @entity: isif media entity
+ * @local: Pad at the local end of the link
+ * @remote: Pad at the remote end of the link
+ * @flags: Link flags
+ *
+ * return -EINVAL or zero on success
+ */
+static int
+isif_link_setup(struct media_entity *entity, const struct media_pad *local,
+ const struct media_pad *remote, u32 flags)
+{
+ struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity);
+ struct vpfe_isif_device *isif = v4l2_get_subdevdata(sd);
+
+ switch (local->index | media_entity_type(remote->entity)) {
+ case ISIF_PAD_SINK | MEDIA_ENT_T_V4L2_SUBDEV:
+ /* read from decoder/sensor */
+ if (!(flags & MEDIA_LNK_FL_ENABLED)) {
+ isif->input = ISIF_INPUT_NONE;
+ break;
+ }
+ if (isif->input != ISIF_INPUT_NONE)
+ return -EBUSY;
+ isif->input = ISIF_INPUT_PARALLEL;
+ break;
+
+ case ISIF_PAD_SOURCE | MEDIA_ENT_T_DEVNODE:
+ /* write to memory */
+ if (flags & MEDIA_LNK_FL_ENABLED)
+ isif->output = ISIF_OUTPUT_MEMORY;
+ else
+ isif->output = ISIF_OUTPUT_NONE;
+ break;
+
+ case ISIF_PAD_SOURCE | MEDIA_ENT_T_V4L2_SUBDEV:
+ if (flags & MEDIA_LNK_FL_ENABLED)
+ isif->output = ISIF_OUTPUT_IPIPEIF;
+ else
+ isif->output = ISIF_OUTPUT_NONE;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+static const struct media_entity_operations isif_media_ops = {
+ .link_setup = isif_link_setup,
+};
+
+/*
+ * vpfe_isif_unregister_entities() - isif unregister entity
+ * @isif - pointer to isif subdevice structure.
+ */
+void vpfe_isif_unregister_entities(struct vpfe_isif_device *isif)
+{
+ vpfe_video_unregister(&isif->video_out);
+ /* unregister subdev */
+ v4l2_device_unregister_subdev(&isif->subdev);
+ /* cleanup entity */
+ media_entity_cleanup(&isif->subdev.entity);
+}
+
+static void isif_restore_defaults(struct vpfe_isif_device *isif)
+{
+ enum vpss_ccdc_source_sel source = VPSS_CCDCIN;
+ int i;
+
+ memset(&isif->isif_cfg.bayer.config_params, 0,
+ sizeof(struct vpfe_isif_raw_config));
+
+ isif->isif_cfg.bayer.config_params.linearize.corr_shft =
+ VPFE_ISIF_NO_SHIFT;
+ isif->isif_cfg.bayer.config_params.linearize.scale_fact.integer = 1;
+ isif->isif_cfg.bayer.config_params.culling.hcpat_odd =
+ ISIF_CULLING_HCAPT_ODD;
+ isif->isif_cfg.bayer.config_params.culling.hcpat_even =
+ ISIF_CULLING_HCAPT_EVEN;
+ isif->isif_cfg.bayer.config_params.culling.vcpat = ISIF_CULLING_VCAPT;
+ /* Enable clock to ISIF, IPIPEIF and BL */
+ vpss_enable_clock(VPSS_CCDC_CLOCK, 1);
+ vpss_enable_clock(VPSS_IPIPEIF_CLOCK, 1);
+ vpss_enable_clock(VPSS_BL_CLOCK, 1);
+
+ /* set all registers to default value */
+ for (i = 0; i <= 0x1f8; i += 4)
+ isif_write(isif->isif_cfg.base_addr, 0, i);
+ /* no culling support */
+ isif_write(isif->isif_cfg.base_addr, 0xffff, CULH);
+ isif_write(isif->isif_cfg.base_addr, 0xff, CULV);
+
+ /* Set default offset and gain */
+ isif_config_gain_offset(isif);
+ vpss_select_ccdc_source(source);
+}
+
+/*
+ * vpfe_isif_register_entities() - isif register entity
+ * @isif - pointer to isif subdevice structure.
+ * @vdev: pointer to v4l2 device structure.
+ */
+int vpfe_isif_register_entities(struct vpfe_isif_device *isif,
+ struct v4l2_device *vdev)
+{
+ struct vpfe_device *vpfe_dev = to_vpfe_device(isif);
+ unsigned int flags;
+ int ret;
+
+ /* Register the subdev */
+ ret = v4l2_device_register_subdev(vdev, &isif->subdev);
+ if (ret < 0)
+ return ret;
+
+ isif_restore_defaults(isif);
+ ret = vpfe_video_register(&isif->video_out, vdev);
+ if (ret) {
+ pr_err("Failed to register isif video out device\n");
+ goto out_video_register;
+ }
+ isif->video_out.vpfe_dev = vpfe_dev;
+ flags = 0;
+ /* connect isif to video node */
+ ret = media_entity_create_link(&isif->subdev.entity, 1,
+ &isif->video_out.video_dev.entity,
+ 0, flags);
+ if (ret < 0)
+ goto out_create_link;
+ return 0;
+out_create_link:
+ vpfe_video_unregister(&isif->video_out);
+out_video_register:
+ v4l2_device_unregister_subdev(&isif->subdev);
+ return ret;
+}
+
+/* -------------------------------------------------------------------
+ * V4L2 subdev control operations
+ */
+
+static int vpfe_isif_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct vpfe_isif_device *isif =
+ container_of(ctrl->handler, struct vpfe_isif_device, ctrls);
+ struct isif_oper_config *config = &isif->isif_cfg;
+
+ switch (ctrl->id) {
+ case VPFE_CID_DPCM_PREDICTOR:
+ config->bayer.dpcm_predictor = ctrl->val;
+ break;
+
+ case VPFE_ISIF_CID_CRGAIN:
+ config->isif_gain_params.cr_gain = ctrl->val;
+ break;
+
+ case VPFE_ISIF_CID_CGRGAIN:
+ config->isif_gain_params.cgr_gain = ctrl->val;
+ break;
+
+ case VPFE_ISIF_CID_CGBGAIN:
+ config->isif_gain_params.cgb_gain = ctrl->val;
+ break;
+
+ case VPFE_ISIF_CID_CBGAIN:
+ config->isif_gain_params.cb_gain = ctrl->val;
+ break;
+
+ case VPFE_ISIF_CID_GAIN_OFFSET:
+ config->isif_gain_params.offset = ctrl->val;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static const struct v4l2_ctrl_ops vpfe_isif_ctrl_ops = {
+ .s_ctrl = vpfe_isif_s_ctrl,
+};
+
+static const struct v4l2_ctrl_config vpfe_isif_dpcm_pred = {
+ .ops = &vpfe_isif_ctrl_ops,
+ .id = VPFE_CID_DPCM_PREDICTOR,
+ .name = "DPCM Predictor",
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .min = 0,
+ .max = 1,
+ .step = 1,
+ .def = 0,
+};
+
+static const struct v4l2_ctrl_config vpfe_isif_crgain = {
+ .ops = &vpfe_isif_ctrl_ops,
+ .id = VPFE_ISIF_CID_CRGAIN,
+ .name = "CRGAIN",
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .min = 0,
+ .max = (1 << 12) - 1,
+ .step = 1,
+ .def = 0,
+};
+
+static const struct v4l2_ctrl_config vpfe_isif_cgrgain = {
+ .ops = &vpfe_isif_ctrl_ops,
+ .id = VPFE_ISIF_CID_CGRGAIN,
+ .name = "CGRGAIN",
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .min = 0,
+ .max = (1 << 12) - 1,
+ .step = 1,
+ .def = 0,
+};
+
+static const struct v4l2_ctrl_config vpfe_isif_cgbgain = {
+ .ops = &vpfe_isif_ctrl_ops,
+ .id = VPFE_ISIF_CID_CGBGAIN,
+ .name = "CGBGAIN",
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .min = 0,
+ .max = (1 << 12) - 1,
+ .step = 1,
+ .def = 0,
+};
+
+static const struct v4l2_ctrl_config vpfe_isif_cbgain = {
+ .ops = &vpfe_isif_ctrl_ops,
+ .id = VPFE_ISIF_CID_CBGAIN,
+ .name = "CBGAIN",
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .min = 0,
+ .max = (1 << 12) - 1,
+ .step = 1,
+ .def = 0,
+};
+
+static const struct v4l2_ctrl_config vpfe_isif_gain_offset = {
+ .ops = &vpfe_isif_ctrl_ops,
+ .id = VPFE_ISIF_CID_GAIN_OFFSET,
+ .name = "Gain Offset",
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .min = 0,
+ .max = (1 << 12) - 1,
+ .step = 1,
+ .def = 0,
+};
+
+static void isif_remove(struct vpfe_isif_device *isif,
+ struct platform_device *pdev)
+{
+ struct resource *res;
+ int i = 0;
+
+ iounmap(isif->isif_cfg.base_addr);
+ iounmap(isif->isif_cfg.linear_tbl0_addr);
+ iounmap(isif->isif_cfg.linear_tbl1_addr);
+
+ while (i < 3) {
+ res = platform_get_resource(pdev, IORESOURCE_MEM, i);
+ if (res)
+ release_mem_region(res->start,
+ resource_size(res));
+ i++;
+ }
+}
+
+static void isif_config_defaults(struct vpfe_isif_device *isif)
+{
+ isif->isif_cfg.ycbcr.v4l2_pix_fmt = V4L2_PIX_FMT_UYVY;
+ isif->isif_cfg.ycbcr.pix_fmt = ISIF_PIXFMT_YCBCR_8BIT;
+ isif->isif_cfg.ycbcr.frm_fmt = ISIF_FRMFMT_INTERLACED;
+ isif->isif_cfg.ycbcr.fid_pol = VPFE_PINPOL_POSITIVE;
+ isif->isif_cfg.ycbcr.vd_pol = VPFE_PINPOL_POSITIVE;
+ isif->isif_cfg.ycbcr.hd_pol = VPFE_PINPOL_POSITIVE;
+ isif->isif_cfg.ycbcr.pix_order = ISIF_PIXORDER_CBYCRY;
+ isif->isif_cfg.ycbcr.buf_type = ISIF_BUFTYPE_FLD_INTERLEAVED;
+
+ isif->isif_cfg.bayer.v4l2_pix_fmt = V4L2_PIX_FMT_SGRBG10ALAW8;
+ isif->isif_cfg.bayer.pix_fmt = ISIF_PIXFMT_RAW;
+ isif->isif_cfg.bayer.frm_fmt = ISIF_FRMFMT_PROGRESSIVE;
+ isif->isif_cfg.bayer.fid_pol = VPFE_PINPOL_POSITIVE;
+ isif->isif_cfg.bayer.vd_pol = VPFE_PINPOL_POSITIVE;
+ isif->isif_cfg.bayer.hd_pol = VPFE_PINPOL_POSITIVE;
+ isif->isif_cfg.bayer.cfa_pat = ISIF_CFA_PAT_MOSAIC;
+ isif->isif_cfg.bayer.data_msb = ISIF_BIT_MSB_11;
+ isif->isif_cfg.data_pack = ISIF_PACK_8BIT;
+}
+/*
+ * vpfe_isif_init() - Initialize V4L2 subdev and media entity
+ * @isif: VPFE isif module
+ * @pdev: Pointer to platform device structure.
+ * Return 0 on success and a negative error code on failure.
+ */
+int vpfe_isif_init(struct vpfe_isif_device *isif, struct platform_device *pdev)
+{
+ struct v4l2_subdev *sd = &isif->subdev;
+ struct media_pad *pads = &isif->pads[0];
+ struct media_entity *me = &sd->entity;
+ static resource_size_t res_len;
+ struct resource *res;
+ void __iomem *addr;
+ int status;
+ int i = 0;
+
+ /* Get the ISIF base address, linearization table0 and table1 addr. */
+ while (i < 3) {
+ res = platform_get_resource(pdev, IORESOURCE_MEM, i);
+ if (!res) {
+ status = -ENOENT;
+ goto fail_nobase_res;
+ }
+ res_len = resource_size(res);
+ res = request_mem_region(res->start, res_len, res->name);
+ if (!res) {
+ status = -EBUSY;
+ goto fail_nobase_res;
+ }
+ addr = ioremap_nocache(res->start, res_len);
+ if (!addr) {
+ status = -EBUSY;
+ goto fail_base_iomap;
+ }
+ switch (i) {
+ case 0:
+ /* ISIF base address */
+ isif->isif_cfg.base_addr = addr;
+ break;
+ case 1:
+ /* ISIF linear tbl0 address */
+ isif->isif_cfg.linear_tbl0_addr = addr;
+ break;
+ default:
+ /* ISIF linear tbl0 address */
+ isif->isif_cfg.linear_tbl1_addr = addr;
+ break;
+ }
+ i++;
+ }
+ davinci_cfg_reg(DM365_VIN_CAM_WEN);
+ davinci_cfg_reg(DM365_VIN_CAM_VD);
+ davinci_cfg_reg(DM365_VIN_CAM_HD);
+ davinci_cfg_reg(DM365_VIN_YIN4_7_EN);
+ davinci_cfg_reg(DM365_VIN_YIN0_3_EN);
+
+ /* queue ops */
+ isif->video_out.ops = &isif_video_ops;
+ v4l2_subdev_init(sd, &isif_v4l2_ops);
+ sd->internal_ops = &isif_v4l2_internal_ops;
+ strlcpy(sd->name, "DAVINCI ISIF", sizeof(sd->name));
+ sd->grp_id = 1 << 16; /* group ID for davinci subdevs */
+ v4l2_set_subdevdata(sd, isif);
+ sd->flags |= V4L2_SUBDEV_FL_HAS_EVENTS | V4L2_SUBDEV_FL_HAS_DEVNODE;
+ pads[ISIF_PAD_SINK].flags = MEDIA_PAD_FL_SINK;
+ pads[ISIF_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE;
+
+ isif->input = ISIF_INPUT_NONE;
+ isif->output = ISIF_OUTPUT_NONE;
+ me->ops = &isif_media_ops;
+ status = media_entity_init(me, ISIF_PADS_NUM, pads, 0);
+ if (status)
+ goto isif_fail;
+ isif->video_out.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ status = vpfe_video_init(&isif->video_out, "ISIF");
+ if (status) {
+ pr_err("Failed to init isif-out video device\n");
+ goto isif_fail;
+ }
+ v4l2_ctrl_handler_init(&isif->ctrls, 6);
+ v4l2_ctrl_new_custom(&isif->ctrls, &vpfe_isif_crgain, NULL);
+ v4l2_ctrl_new_custom(&isif->ctrls, &vpfe_isif_cgrgain, NULL);
+ v4l2_ctrl_new_custom(&isif->ctrls, &vpfe_isif_cgbgain, NULL);
+ v4l2_ctrl_new_custom(&isif->ctrls, &vpfe_isif_cbgain, NULL);
+ v4l2_ctrl_new_custom(&isif->ctrls, &vpfe_isif_gain_offset, NULL);
+ v4l2_ctrl_new_custom(&isif->ctrls, &vpfe_isif_dpcm_pred, NULL);
+
+ v4l2_ctrl_handler_setup(&isif->ctrls);
+ sd->ctrl_handler = &isif->ctrls;
+ isif_config_defaults(isif);
+ return 0;
+fail_base_iomap:
+ release_mem_region(res->start, res_len);
+ i--;
+fail_nobase_res:
+ if (isif->isif_cfg.base_addr)
+ iounmap(isif->isif_cfg.base_addr);
+ if (isif->isif_cfg.linear_tbl0_addr)
+ iounmap(isif->isif_cfg.linear_tbl0_addr);
+
+ while (i >= 0) {
+ res = platform_get_resource(pdev, IORESOURCE_MEM, i);
+ release_mem_region(res->start, res_len);
+ i--;
+ }
+ return status;
+isif_fail:
+ v4l2_ctrl_handler_free(&isif->ctrls);
+ isif_remove(isif, pdev);
+ return status;
+}
+
+/*
+ * vpfe_isif_cleanup - isif module cleanup
+ * @isif: pointer to isif subdevice
+ * @dev: pointer to platform device structure
+ */
+void
+vpfe_isif_cleanup(struct vpfe_isif_device *isif, struct platform_device *pdev)
+{
+ isif_remove(isif, pdev);
+}
diff --git a/drivers/staging/media/davinci_vpfe/dm365_isif.h b/drivers/staging/media/davinci_vpfe/dm365_isif.h
new file mode 100644
index 000000000..89e814e9c
--- /dev/null
+++ b/drivers/staging/media/davinci_vpfe/dm365_isif.h
@@ -0,0 +1,203 @@
+/*
+ * Copyright (C) 2012 Texas Instruments 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 version 2.
+ *
+ * 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
+ *
+ * Contributors:
+ * Manjunath Hadli <manjunath.hadli@ti.com>
+ * Prabhakar Lad <prabhakar.lad@ti.com>
+ */
+
+#ifndef _DAVINCI_VPFE_DM365_ISIF_H
+#define _DAVINCI_VPFE_DM365_ISIF_H
+
+#include <linux/platform_device.h>
+
+#include <mach/mux.h>
+
+#include <media/davinci/vpfe_types.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-device.h>
+
+#include "davinci_vpfe_user.h"
+#include "dm365_isif_regs.h"
+#include "vpfe_video.h"
+
+#define ISIF_CULLING_HCAPT_ODD 0xff
+#define ISIF_CULLING_HCAPT_EVEN 0xff
+#define ISIF_CULLING_VCAPT 0xff
+
+#define ISIF_CADU_BITS 0x07ff
+#define ISIF_CADL_BITS 0x0ffff
+
+enum isif_pixfmt {
+ ISIF_PIXFMT_RAW = 0,
+ ISIF_PIXFMT_YCBCR_16BIT = 1,
+ ISIF_PIXFMT_YCBCR_8BIT = 2,
+};
+
+enum isif_frmfmt {
+ ISIF_FRMFMT_PROGRESSIVE = 0,
+ ISIF_FRMFMT_INTERLACED = 1,
+};
+
+/* PIXEL ORDER IN MEMORY from LSB to MSB */
+/* only applicable for 8-bit input mode */
+enum isif_pixorder {
+ ISIF_PIXORDER_YCBYCR = 0,
+ ISIF_PIXORDER_CBYCRY = 1,
+};
+
+enum isif_buftype {
+ ISIF_BUFTYPE_FLD_INTERLEAVED = 0,
+ ISIF_BUFTYPE_FLD_SEPARATED = 1,
+};
+
+struct isif_ycbcr_config {
+ /* v4l2 pixel format */
+ unsigned long v4l2_pix_fmt;
+ /* isif pixel format */
+ enum isif_pixfmt pix_fmt;
+ /* isif frame format */
+ enum isif_frmfmt frm_fmt;
+ /* isif crop window */
+ struct v4l2_rect win;
+ /* field polarity */
+ enum vpfe_pin_pol fid_pol;
+ /* interface VD polarity */
+ enum vpfe_pin_pol vd_pol;
+ /* interface HD polarity */
+ enum vpfe_pin_pol hd_pol;
+ /* isif pix order. Only used for ycbcr capture */
+ enum isif_pixorder pix_order;
+ /* isif buffer type. Only used for ycbcr capture */
+ enum isif_buftype buf_type;
+};
+
+enum isif_cfa_pattern {
+ ISIF_CFA_PAT_MOSAIC = 0,
+ ISIF_CFA_PAT_STRIPE = 1,
+};
+
+enum isif_data_msb {
+ /* MSB b15 */
+ ISIF_BIT_MSB_15 = 0,
+ /* MSB b14 */
+ ISIF_BIT_MSB_14 = 1,
+ /* MSB b13 */
+ ISIF_BIT_MSB_13 = 2,
+ /* MSB b12 */
+ ISIF_BIT_MSB_12 = 3,
+ /* MSB b11 */
+ ISIF_BIT_MSB_11 = 4,
+ /* MSB b10 */
+ ISIF_BIT_MSB_10 = 5,
+ /* MSB b9 */
+ ISIF_BIT_MSB_9 = 6,
+ /* MSB b8 */
+ ISIF_BIT_MSB_8 = 7,
+ /* MSB b7 */
+ ISIF_BIT_MSB_7 = 8,
+};
+
+struct isif_params_raw {
+ /* v4l2 pixel format */
+ unsigned long v4l2_pix_fmt;
+ /* isif pixel format */
+ enum isif_pixfmt pix_fmt;
+ /* isif frame format */
+ enum isif_frmfmt frm_fmt;
+ /* video window */
+ struct v4l2_rect win;
+ /* field polarity */
+ enum vpfe_pin_pol fid_pol;
+ /* interface VD polarity */
+ enum vpfe_pin_pol vd_pol;
+ /* interface HD polarity */
+ enum vpfe_pin_pol hd_pol;
+ /* buffer type. Applicable for interlaced mode */
+ enum isif_buftype buf_type;
+ /* cfa pattern */
+ enum isif_cfa_pattern cfa_pat;
+ /* Data MSB position */
+ enum isif_data_msb data_msb;
+ /* Enable horizontal flip */
+ unsigned char horz_flip_en;
+ /* Enable image invert vertically */
+ unsigned char image_invert_en;
+ unsigned char dpcm_predictor;
+ struct vpfe_isif_raw_config config_params;
+};
+
+enum isif_data_pack {
+ ISIF_PACK_16BIT = 0,
+ ISIF_PACK_12BIT = 1,
+ ISIF_PACK_8BIT = 2,
+};
+
+struct isif_gain_values {
+ unsigned int cr_gain;
+ unsigned int cgr_gain;
+ unsigned int cgb_gain;
+ unsigned int cb_gain;
+ unsigned int offset;
+};
+
+struct isif_oper_config {
+ struct isif_ycbcr_config ycbcr;
+ struct isif_params_raw bayer;
+ enum isif_data_pack data_pack;
+ struct isif_gain_values isif_gain_params;
+ void __iomem *base_addr;
+ void __iomem *linear_tbl0_addr;
+ void __iomem *linear_tbl1_addr;
+};
+
+#define ISIF_PAD_SINK 0
+#define ISIF_PAD_SOURCE 1
+
+#define ISIF_PADS_NUM 2
+
+enum isif_input_entity {
+ ISIF_INPUT_NONE = 0,
+ ISIF_INPUT_PARALLEL = 1,
+};
+
+#define ISIF_OUTPUT_NONE (0)
+#define ISIF_OUTPUT_MEMORY (1 << 0)
+#define ISIF_OUTPUT_IPIPEIF (1 << 1)
+
+struct vpfe_isif_device {
+ struct v4l2_subdev subdev;
+ struct media_pad pads[ISIF_PADS_NUM];
+ struct v4l2_mbus_framefmt formats[ISIF_PADS_NUM];
+ enum isif_input_entity input;
+ unsigned int output;
+ struct v4l2_ctrl_handler ctrls;
+ struct v4l2_rect crop;
+ struct isif_oper_config isif_cfg;
+ struct vpfe_video_device video_out;
+};
+
+enum v4l2_field vpfe_isif_get_fid(struct vpfe_device *vpfe_dev);
+void vpfe_isif_unregister_entities(struct vpfe_isif_device *isif);
+int vpfe_isif_register_entities(struct vpfe_isif_device *isif,
+ struct v4l2_device *dev);
+int vpfe_isif_init(struct vpfe_isif_device *isif, struct platform_device *pdev);
+void vpfe_isif_cleanup(struct vpfe_isif_device *vpfe_isif,
+ struct platform_device *pdev);
+void vpfe_isif_vidint1_isr(struct vpfe_isif_device *isif);
+void vpfe_isif_buffer_isr(struct vpfe_isif_device *isif);
+
+#endif /* _DAVINCI_VPFE_DM365_ISIF_H */
diff --git a/drivers/staging/media/davinci_vpfe/dm365_isif_regs.h b/drivers/staging/media/davinci_vpfe/dm365_isif_regs.h
new file mode 100644
index 000000000..8aceabb43
--- /dev/null
+++ b/drivers/staging/media/davinci_vpfe/dm365_isif_regs.h
@@ -0,0 +1,294 @@
+/*
+ * Copyright (C) 2012 Texas Instruments 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 version 2.
+ *
+ * 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
+ *
+ * Contributors:
+ * Manjunath Hadli <manjunath.hadli@ti.com>
+ * Prabhakar Lad <prabhakar.lad@ti.com>
+ */
+
+#ifndef _DAVINCI_VPFE_DM365_ISIF_REGS_H
+#define _DAVINCI_VPFE_DM365_ISIF_REGS_H
+
+/* ISIF registers relative offsets */
+#define SYNCEN 0x00
+#define MODESET 0x04
+#define HDW 0x08
+#define VDW 0x0c
+#define PPLN 0x10
+#define LPFR 0x14
+#define SPH 0x18
+#define LNH 0x1c
+#define SLV0 0x20
+#define SLV1 0x24
+#define LNV 0x28
+#define CULH 0x2c
+#define CULV 0x30
+#define HSIZE 0x34
+#define SDOFST 0x38
+#define CADU 0x3c
+#define CADL 0x40
+#define LINCFG0 0x44
+#define LINCFG1 0x48
+#define CCOLP 0x4c
+#define CRGAIN 0x50
+#define CGRGAIN 0x54
+#define CGBGAIN 0x58
+#define CBGAIN 0x5c
+#define COFSTA 0x60
+#define FLSHCFG0 0x64
+#define FLSHCFG1 0x68
+#define FLSHCFG2 0x6c
+#define VDINT0 0x70
+#define VDINT1 0x74
+#define VDINT2 0x78
+#define MISC 0x7c
+#define CGAMMAWD 0x80
+#define REC656IF 0x84
+#define CCDCFG 0x88
+/*****************************************************
+* Defect Correction registers
+*****************************************************/
+#define DFCCTL 0x8c
+#define VDFSATLV 0x90
+#define DFCMEMCTL 0x94
+#define DFCMEM0 0x98
+#define DFCMEM1 0x9c
+#define DFCMEM2 0xa0
+#define DFCMEM3 0xa4
+#define DFCMEM4 0xa8
+/****************************************************
+* Black Clamp registers
+****************************************************/
+#define CLAMPCFG 0xac
+#define CLDCOFST 0xb0
+#define CLSV 0xb4
+#define CLHWIN0 0xb8
+#define CLHWIN1 0xbc
+#define CLHWIN2 0xc0
+#define CLVRV 0xc4
+#define CLVWIN0 0xc8
+#define CLVWIN1 0xcc
+#define CLVWIN2 0xd0
+#define CLVWIN3 0xd4
+/****************************************************
+* Lense Shading Correction
+****************************************************/
+#define DATAHOFST 0xd8
+#define DATAVOFST 0xdc
+#define LSCHVAL 0xe0
+#define LSCVVAL 0xe4
+#define TWODLSCCFG 0xe8
+#define TWODLSCOFST 0xec
+#define TWODLSCINI 0xf0
+#define TWODLSCGRBU 0xf4
+#define TWODLSCGRBL 0xf8
+#define TWODLSCGROF 0xfc
+#define TWODLSCORBU 0x100
+#define TWODLSCORBL 0x104
+#define TWODLSCOROF 0x108
+#define TWODLSCIRQEN 0x10c
+#define TWODLSCIRQST 0x110
+/****************************************************
+* Data formatter
+****************************************************/
+#define FMTCFG 0x114
+#define FMTPLEN 0x118
+#define FMTSPH 0x11c
+#define FMTLNH 0x120
+#define FMTSLV 0x124
+#define FMTLNV 0x128
+#define FMTRLEN 0x12c
+#define FMTHCNT 0x130
+#define FMTAPTR_BASE 0x134
+/* Below macro for addresses FMTAPTR0 - FMTAPTR15 */
+#define FMTAPTR(i) (FMTAPTR_BASE + (i * 4))
+#define FMTPGMVF0 0x174
+#define FMTPGMVF1 0x178
+#define FMTPGMAPU0 0x17c
+#define FMTPGMAPU1 0x180
+#define FMTPGMAPS0 0x184
+#define FMTPGMAPS1 0x188
+#define FMTPGMAPS2 0x18c
+#define FMTPGMAPS3 0x190
+#define FMTPGMAPS4 0x194
+#define FMTPGMAPS5 0x198
+#define FMTPGMAPS6 0x19c
+#define FMTPGMAPS7 0x1a0
+/************************************************
+* Color Space Converter
+************************************************/
+#define CSCCTL 0x1a4
+#define CSCM0 0x1a8
+#define CSCM1 0x1ac
+#define CSCM2 0x1b0
+#define CSCM3 0x1b4
+#define CSCM4 0x1b8
+#define CSCM5 0x1bc
+#define CSCM6 0x1c0
+#define CSCM7 0x1c4
+#define OBWIN0 0x1c8
+#define OBWIN1 0x1cc
+#define OBWIN2 0x1d0
+#define OBWIN3 0x1d4
+#define OBVAL0 0x1d8
+#define OBVAL1 0x1dc
+#define OBVAL2 0x1e0
+#define OBVAL3 0x1e4
+#define OBVAL4 0x1e8
+#define OBVAL5 0x1ec
+#define OBVAL6 0x1f0
+#define OBVAL7 0x1f4
+#define CLKCTL 0x1f8
+
+/* Masks & Shifts below */
+#define START_PX_HOR_MASK 0x7fff
+#define NUM_PX_HOR_MASK 0x7fff
+#define START_VER_ONE_MASK 0x7fff
+#define START_VER_TWO_MASK 0x7fff
+#define NUM_LINES_VER 0x7fff
+
+/* gain - offset masks */
+#define OFFSET_MASK 0xfff
+#define GAIN_SDRAM_EN_SHIFT 12
+#define GAIN_IPIPE_EN_SHIFT 13
+#define GAIN_H3A_EN_SHIFT 14
+#define OFST_SDRAM_EN_SHIFT 8
+#define OFST_IPIPE_EN_SHIFT 9
+#define OFST_H3A_EN_SHIFT 10
+#define GAIN_OFFSET_EN_MASK 0x7700
+
+/* Culling */
+#define CULL_PAT_EVEN_LINE_SHIFT 8
+
+/* CCDCFG register */
+#define ISIF_YCINSWP_RAW (0x00 << 4)
+#define ISIF_YCINSWP_YCBCR (0x01 << 4)
+#define ISIF_CCDCFG_FIDMD_LATCH_VSYNC (0x00 << 6)
+#define ISIF_CCDCFG_WENLOG_AND (0x00 << 8)
+#define ISIF_CCDCFG_TRGSEL_WEN (0x00 << 9)
+#define ISIF_CCDCFG_EXTRG_DISABLE (0x00 << 10)
+#define ISIF_LATCH_ON_VSYNC_DISABLE (0x01 << 15)
+#define ISIF_LATCH_ON_VSYNC_ENABLE (0x00 << 15)
+#define ISIF_DATA_PACK_MASK 0x03
+#define ISIF_PIX_ORDER_SHIFT 11
+#define ISIF_PIX_ORDER_MASK 0x01
+#define ISIF_BW656_ENABLE (0x01 << 5)
+
+/* MODESET registers */
+#define ISIF_VDHDOUT_INPUT (0x00 << 0)
+#define ISIF_INPUT_MASK 0x03
+#define ISIF_INPUT_SHIFT 12
+#define ISIF_FID_POL_MASK 0x01
+#define ISIF_FID_POL_SHIFT 4
+#define ISIF_HD_POL_MASK 0x01
+#define ISIF_HD_POL_SHIFT 3
+#define ISIF_VD_POL_MASK 0x01
+#define ISIF_VD_POL_SHIFT 2
+#define ISIF_DATAPOL_NORMAL 0x00
+#define ISIF_DATAPOL_MASK 0x01
+#define ISIF_DATAPOL_SHIFT 6
+#define ISIF_EXWEN_DISABLE 0x00
+#define ISIF_EXWEN_MASK 0x01
+#define ISIF_EXWEN_SHIFT 5
+#define ISIF_FRM_FMT_MASK 0x01
+#define ISIF_FRM_FMT_SHIFT 7
+#define ISIF_DATASFT_MASK 0x07
+#define ISIF_DATASFT_SHIFT 8
+#define ISIF_LPF_SHIFT 14
+#define ISIF_LPF_MASK 0x1
+
+/* GAMMAWD registers */
+#define ISIF_ALAW_GAMA_WD_MASK 0xf
+#define ISIF_ALAW_GAMA_WD_SHIFT 1
+#define ISIF_ALAW_ENABLE 0x01
+#define ISIF_GAMMAWD_CFA_MASK 0x01
+#define ISIF_GAMMAWD_CFA_SHIFT 5
+
+/* HSIZE registers */
+#define ISIF_HSIZE_FLIP_MASK 0x01
+#define ISIF_HSIZE_FLIP_SHIFT 12
+#define ISIF_LINEOFST_MASK 0xfff
+
+/* MISC registers */
+#define ISIF_DPCM_EN_SHIFT 12
+#define ISIF_DPCM_PREDICTOR_SHIFT 13
+#define ISIF_DPCM_PREDICTOR_MASK 1
+
+/* Black clamp related */
+#define ISIF_BC_DCOFFSET_MASK 0x1fff
+#define ISIF_BC_MODE_COLOR_MASK 1
+#define ISIF_BC_MODE_COLOR_SHIFT 4
+#define ISIF_HORZ_BC_MODE_MASK 3
+#define ISIF_HORZ_BC_MODE_SHIFT 1
+#define ISIF_HORZ_BC_WIN_COUNT_MASK 0x1f
+#define ISIF_HORZ_BC_WIN_SEL_SHIFT 5
+#define ISIF_HORZ_BC_PIX_LIMIT_SHIFT 6
+#define ISIF_HORZ_BC_WIN_H_SIZE_MASK 3
+#define ISIF_HORZ_BC_WIN_H_SIZE_SHIFT 8
+#define ISIF_HORZ_BC_WIN_V_SIZE_MASK 3
+#define ISIF_HORZ_BC_WIN_V_SIZE_SHIFT 12
+#define ISIF_HORZ_BC_WIN_START_H_MASK 0x1fff
+#define ISIF_HORZ_BC_WIN_START_V_MASK 0x1fff
+#define ISIF_VERT_BC_OB_H_SZ_MASK 7
+#define ISIF_VERT_BC_RST_VAL_SEL_MASK 3
+#define ISIF_VERT_BC_RST_VAL_SEL_SHIFT 4
+#define ISIF_VERT_BC_LINE_AVE_COEF_SHIFT 8
+#define ISIF_VERT_BC_OB_START_HORZ_MASK 0x1fff
+#define ISIF_VERT_BC_OB_START_VERT_MASK 0x1fff
+#define ISIF_VERT_BC_OB_VERT_SZ_MASK 0x1fff
+#define ISIF_VERT_BC_RST_VAL_MASK 0xfff
+#define ISIF_BC_VERT_START_SUB_V_MASK 0x1fff
+
+/* VDFC registers */
+#define ISIF_VDFC_EN_SHIFT 4
+#define ISIF_VDFC_CORR_MOD_MASK 3
+#define ISIF_VDFC_CORR_MOD_SHIFT 5
+#define ISIF_VDFC_CORR_WHOLE_LN_SHIFT 7
+#define ISIF_VDFC_LEVEL_SHFT_MASK 7
+#define ISIF_VDFC_LEVEL_SHFT_SHIFT 8
+#define ISIF_VDFC_SAT_LEVEL_MASK 0xfff
+#define ISIF_VDFC_POS_MASK 0x1fff
+#define ISIF_DFCMEMCTL_DFCMARST_SHIFT 2
+
+/* CSC registers */
+#define ISIF_CSC_COEF_INTEG_MASK 7
+#define ISIF_CSC_COEF_DECIMAL_MASK 0x1f
+#define ISIF_CSC_COEF_INTEG_SHIFT 5
+#define ISIF_CSCM_MSB_SHIFT 8
+#define ISIF_DF_CSC_SPH_MASK 0x1fff
+#define ISIF_DF_CSC_LNH_MASK 0x1fff
+#define ISIF_DF_CSC_SLV_MASK 0x1fff
+#define ISIF_DF_CSC_LNV_MASK 0x1fff
+#define ISIF_DF_NUMLINES 0x7fff
+#define ISIF_DF_NUMPIX 0x1fff
+
+/* Offsets for LSC/DFC/Gain */
+#define ISIF_DATA_H_OFFSET_MASK 0x1fff
+#define ISIF_DATA_V_OFFSET_MASK 0x1fff
+
+/* Linearization */
+#define ISIF_LIN_CORRSFT_MASK 7
+#define ISIF_LIN_CORRSFT_SHIFT 4
+#define ISIF_LIN_SCALE_FACT_INTEG_SHIFT 10
+#define ISIF_LIN_SCALE_FACT_DECIMAL_MASK 0x3ff
+#define ISIF_LIN_ENTRY_MASK 0x3ff
+
+/* masks and shifts*/
+#define ISIF_SYNCEN_VDHDEN_MASK (1 << 0)
+#define ISIF_SYNCEN_WEN_MASK (1 << 1)
+#define ISIF_SYNCEN_WEN_SHIFT 1
+
+#endif /* _DAVINCI_VPFE_DM365_ISIF_REGS_H */
diff --git a/drivers/staging/media/davinci_vpfe/dm365_resizer.c b/drivers/staging/media/davinci_vpfe/dm365_resizer.c
new file mode 100644
index 000000000..b6498137d
--- /dev/null
+++ b/drivers/staging/media/davinci_vpfe/dm365_resizer.c
@@ -0,0 +1,1995 @@
+/*
+ * Copyright (C) 2012 Texas Instruments 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 version 2.
+ *
+ * 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
+ *
+ * Contributors:
+ * Manjunath Hadli <manjunath.hadli@ti.com>
+ * Prabhakar Lad <prabhakar.lad@ti.com>
+ *
+ *
+ * Resizer allows upscaling or downscaling a image to a desired
+ * resolution. There are 2 resizer modules. both operating on the
+ * same input image, but can have different output resolution.
+ */
+
+#include "dm365_ipipe_hw.h"
+#include "dm365_resizer.h"
+
+#define MIN_IN_WIDTH 32
+#define MIN_IN_HEIGHT 32
+#define MAX_IN_WIDTH 4095
+#define MAX_IN_HEIGHT 4095
+#define MIN_OUT_WIDTH 16
+#define MIN_OUT_HEIGHT 2
+
+static const unsigned int resizer_input_formats[] = {
+ MEDIA_BUS_FMT_UYVY8_2X8,
+ MEDIA_BUS_FMT_Y8_1X8,
+ MEDIA_BUS_FMT_UV8_1X8,
+ MEDIA_BUS_FMT_SGRBG12_1X12,
+};
+
+static const unsigned int resizer_output_formats[] = {
+ MEDIA_BUS_FMT_UYVY8_2X8,
+ MEDIA_BUS_FMT_Y8_1X8,
+ MEDIA_BUS_FMT_UV8_1X8,
+ MEDIA_BUS_FMT_YDYUYDYV8_1X16,
+ MEDIA_BUS_FMT_SGRBG12_1X12,
+};
+
+/* resizer_calculate_line_length() - This function calculates the line length of
+ * various image planes at the input and
+ * output.
+ */
+static void
+resizer_calculate_line_length(u32 pix, int width, int height,
+ int *line_len, int *line_len_c)
+{
+ *line_len = 0;
+ *line_len_c = 0;
+
+ if (pix == MEDIA_BUS_FMT_UYVY8_2X8 ||
+ pix == MEDIA_BUS_FMT_SGRBG12_1X12) {
+ *line_len = width << 1;
+ } else if (pix == MEDIA_BUS_FMT_Y8_1X8 ||
+ pix == MEDIA_BUS_FMT_UV8_1X8) {
+ *line_len = width;
+ *line_len_c = width;
+ } else {
+ /* YUV 420 */
+ /* round width to upper 32 byte boundary */
+ *line_len = width;
+ *line_len_c = width;
+ }
+ /* adjust the line len to be a multiple of 32 */
+ *line_len += 31;
+ *line_len &= ~0x1f;
+ *line_len_c += 31;
+ *line_len_c &= ~0x1f;
+}
+
+static inline int
+resizer_validate_output_image_format(struct device *dev,
+ struct v4l2_mbus_framefmt *format,
+ int *in_line_len, int *in_line_len_c)
+{
+ if (format->code != MEDIA_BUS_FMT_UYVY8_2X8 &&
+ format->code != MEDIA_BUS_FMT_Y8_1X8 &&
+ format->code != MEDIA_BUS_FMT_UV8_1X8 &&
+ format->code != MEDIA_BUS_FMT_YDYUYDYV8_1X16 &&
+ format->code != MEDIA_BUS_FMT_SGRBG12_1X12) {
+ dev_err(dev, "Invalid Mbus format, %d\n", format->code);
+ return -EINVAL;
+ }
+ if (!format->width || !format->height) {
+ dev_err(dev, "invalid width or height\n");
+ return -EINVAL;
+ }
+ resizer_calculate_line_length(format->code, format->width,
+ format->height, in_line_len, in_line_len_c);
+ return 0;
+}
+
+static void
+resizer_configure_passthru(struct vpfe_resizer_device *resizer, int bypass)
+{
+ struct resizer_params *param = &resizer->config;
+
+ param->rsz_rsc_param[RSZ_A].cen = DISABLE;
+ param->rsz_rsc_param[RSZ_A].yen = DISABLE;
+ param->rsz_rsc_param[RSZ_A].v_phs_y = 0;
+ param->rsz_rsc_param[RSZ_A].v_phs_c = 0;
+ param->rsz_rsc_param[RSZ_A].v_dif = 256;
+ param->rsz_rsc_param[RSZ_A].v_lpf_int_y = 0;
+ param->rsz_rsc_param[RSZ_A].v_lpf_int_c = 0;
+ param->rsz_rsc_param[RSZ_A].h_phs = 0;
+ param->rsz_rsc_param[RSZ_A].h_dif = 256;
+ param->rsz_rsc_param[RSZ_A].h_lpf_int_y = 0;
+ param->rsz_rsc_param[RSZ_A].h_lpf_int_c = 0;
+ param->rsz_rsc_param[RSZ_A].dscale_en = DISABLE;
+ param->rsz2rgb[RSZ_A].rgb_en = DISABLE;
+ param->rsz_en[RSZ_A] = ENABLE;
+ param->rsz_en[RSZ_B] = DISABLE;
+ if (bypass) {
+ param->rsz_rsc_param[RSZ_A].i_vps = 0;
+ param->rsz_rsc_param[RSZ_A].i_hps = 0;
+ /* Raw Bypass */
+ param->rsz_common.passthrough = BYPASS_ON;
+ }
+}
+
+static void
+configure_resizer_out_params(struct vpfe_resizer_device *resizer, int index,
+ void *output_spec, unsigned char partial,
+ unsigned flag)
+{
+ struct resizer_params *param = &resizer->config;
+ struct v4l2_mbus_framefmt *outformat;
+ struct vpfe_rsz_output_spec *output;
+
+ if (index == RSZ_A &&
+ resizer->resizer_a.output == RESIZER_OUTPUT_NONE) {
+ param->rsz_en[index] = DISABLE;
+ return;
+ }
+ if (index == RSZ_B &&
+ resizer->resizer_b.output == RESIZER_OUTPUT_NONE) {
+ param->rsz_en[index] = DISABLE;
+ return;
+ }
+ output = output_spec;
+ param->rsz_en[index] = ENABLE;
+ if (partial) {
+ param->rsz_rsc_param[index].h_flip = output->h_flip;
+ param->rsz_rsc_param[index].v_flip = output->v_flip;
+ param->rsz_rsc_param[index].v_typ_y = output->v_typ_y;
+ param->rsz_rsc_param[index].v_typ_c = output->v_typ_c;
+ param->rsz_rsc_param[index].v_lpf_int_y =
+ output->v_lpf_int_y;
+ param->rsz_rsc_param[index].v_lpf_int_c =
+ output->v_lpf_int_c;
+ param->rsz_rsc_param[index].h_typ_y = output->h_typ_y;
+ param->rsz_rsc_param[index].h_typ_c = output->h_typ_c;
+ param->rsz_rsc_param[index].h_lpf_int_y =
+ output->h_lpf_int_y;
+ param->rsz_rsc_param[index].h_lpf_int_c =
+ output->h_lpf_int_c;
+ param->rsz_rsc_param[index].dscale_en =
+ output->en_down_scale;
+ param->rsz_rsc_param[index].h_dscale_ave_sz =
+ output->h_dscale_ave_sz;
+ param->rsz_rsc_param[index].v_dscale_ave_sz =
+ output->v_dscale_ave_sz;
+ param->ext_mem_param[index].user_y_ofst =
+ (output->user_y_ofst + 31) & ~0x1f;
+ param->ext_mem_param[index].user_c_ofst =
+ (output->user_c_ofst + 31) & ~0x1f;
+ return;
+ }
+
+ if (index == RSZ_A)
+ outformat = &resizer->resizer_a.formats[RESIZER_PAD_SOURCE];
+ else
+ outformat = &resizer->resizer_b.formats[RESIZER_PAD_SOURCE];
+ param->rsz_rsc_param[index].o_vsz = outformat->height - 1;
+ param->rsz_rsc_param[index].o_hsz = outformat->width - 1;
+ param->ext_mem_param[index].rsz_sdr_ptr_s_y = output->vst_y;
+ param->ext_mem_param[index].rsz_sdr_ptr_e_y = outformat->height;
+ param->ext_mem_param[index].rsz_sdr_ptr_s_c = output->vst_c;
+ param->ext_mem_param[index].rsz_sdr_ptr_e_c = outformat->height;
+
+ if (!flag)
+ return;
+ /* update common parameters */
+ param->rsz_rsc_param[index].h_flip = output->h_flip;
+ param->rsz_rsc_param[index].v_flip = output->v_flip;
+ param->rsz_rsc_param[index].v_typ_y = output->v_typ_y;
+ param->rsz_rsc_param[index].v_typ_c = output->v_typ_c;
+ param->rsz_rsc_param[index].v_lpf_int_y = output->v_lpf_int_y;
+ param->rsz_rsc_param[index].v_lpf_int_c = output->v_lpf_int_c;
+ param->rsz_rsc_param[index].h_typ_y = output->h_typ_y;
+ param->rsz_rsc_param[index].h_typ_c = output->h_typ_c;
+ param->rsz_rsc_param[index].h_lpf_int_y = output->h_lpf_int_y;
+ param->rsz_rsc_param[index].h_lpf_int_c = output->h_lpf_int_c;
+ param->rsz_rsc_param[index].dscale_en = output->en_down_scale;
+ param->rsz_rsc_param[index].h_dscale_ave_sz = output->h_dscale_ave_sz;
+ param->rsz_rsc_param[index].v_dscale_ave_sz = output->h_dscale_ave_sz;
+ param->ext_mem_param[index].user_y_ofst =
+ (output->user_y_ofst + 31) & ~0x1f;
+ param->ext_mem_param[index].user_c_ofst =
+ (output->user_c_ofst + 31) & ~0x1f;
+}
+
+/*
+ * resizer_calculate_resize_ratios() - Calculates resize ratio for resizer
+ * A or B. This is called after setting
+ * the input size or output size.
+ * @resizer: Pointer to VPFE resizer subdevice.
+ * @index: index RSZ_A-resizer-A RSZ_B-resizer-B.
+ */
+static void
+resizer_calculate_resize_ratios(struct vpfe_resizer_device *resizer, int index)
+{
+ struct resizer_params *param = &resizer->config;
+ struct v4l2_mbus_framefmt *informat, *outformat;
+
+ informat = &resizer->crop_resizer.formats[RESIZER_CROP_PAD_SINK];
+
+ if (index == RSZ_A)
+ outformat = &resizer->resizer_a.formats[RESIZER_PAD_SOURCE];
+ else
+ outformat = &resizer->resizer_b.formats[RESIZER_PAD_SOURCE];
+
+ if (outformat->field != V4L2_FIELD_INTERLACED)
+ param->rsz_rsc_param[index].v_dif =
+ ((informat->height) * 256) / (outformat->height);
+ else
+ param->rsz_rsc_param[index].v_dif =
+ ((informat->height >> 1) * 256) / (outformat->height);
+ param->rsz_rsc_param[index].h_dif =
+ ((informat->width) * 256) / (outformat->width);
+}
+
+void
+static resizer_enable_422_420_conversion(struct resizer_params *param,
+ int index, bool en)
+{
+ param->rsz_rsc_param[index].cen = en;
+ param->rsz_rsc_param[index].yen = en;
+}
+
+/* resizer_calculate_sdram_offsets() - This function calculates the offsets from
+ * start of buffer for the C plane when
+ * output format is YUV420SP. It also
+ * calculates the offsets from the start of
+ * the buffer when the image is flipped
+ * vertically or horizontally for ycbcr/y/c
+ * planes.
+ * @resizer: Pointer to resizer subdevice.
+ * @index: index RSZ_A-resizer-A RSZ_B-resizer-B.
+ */
+static int
+resizer_calculate_sdram_offsets(struct vpfe_resizer_device *resizer, int index)
+{
+ struct resizer_params *param = &resizer->config;
+ struct v4l2_mbus_framefmt *outformat;
+ int bytesperpixel = 2;
+ int image_height;
+ int image_width;
+ int yuv_420 = 0;
+ int offset = 0;
+
+ if (index == RSZ_A)
+ outformat = &resizer->resizer_a.formats[RESIZER_PAD_SOURCE];
+ else
+ outformat = &resizer->resizer_b.formats[RESIZER_PAD_SOURCE];
+
+ image_height = outformat->height + 1;
+ image_width = outformat->width + 1;
+ param->ext_mem_param[index].c_offset = 0;
+ param->ext_mem_param[index].flip_ofst_y = 0;
+ param->ext_mem_param[index].flip_ofst_c = 0;
+ if (outformat->code == MEDIA_BUS_FMT_YDYUYDYV8_1X16) {
+ /* YUV 420 */
+ yuv_420 = 1;
+ bytesperpixel = 1;
+ }
+
+ if (param->rsz_rsc_param[index].h_flip)
+ /* width * bytesperpixel - 1 */
+ offset = (image_width * bytesperpixel) - 1;
+ if (param->rsz_rsc_param[index].v_flip)
+ offset += (image_height - 1) *
+ param->ext_mem_param[index].rsz_sdr_oft_y;
+ param->ext_mem_param[index].flip_ofst_y = offset;
+ if (!yuv_420)
+ return 0;
+ offset = 0;
+ /* half height for c-plane */
+ if (param->rsz_rsc_param[index].h_flip)
+ /* width * bytesperpixel - 1 */
+ offset = image_width - 1;
+ if (param->rsz_rsc_param[index].v_flip)
+ offset += (((image_height >> 1) - 1) *
+ param->ext_mem_param[index].rsz_sdr_oft_c);
+ param->ext_mem_param[index].flip_ofst_c = offset;
+ param->ext_mem_param[index].c_offset =
+ param->ext_mem_param[index].rsz_sdr_oft_y * image_height;
+ return 0;
+}
+
+static int resizer_configure_output_win(struct vpfe_resizer_device *resizer)
+{
+ struct resizer_params *param = &resizer->config;
+ struct vpfe_rsz_output_spec output_specs;
+ struct v4l2_mbus_framefmt *outformat;
+ int line_len_c;
+ int line_len;
+ int ret;
+
+ outformat = &resizer->resizer_a.formats[RESIZER_PAD_SOURCE];
+
+ output_specs.vst_y = param->user_config.vst;
+ if (outformat->code == MEDIA_BUS_FMT_YDYUYDYV8_1X16)
+ output_specs.vst_c = param->user_config.vst;
+
+ configure_resizer_out_params(resizer, RSZ_A, &output_specs, 0, 0);
+ resizer_calculate_line_length(outformat->code,
+ param->rsz_rsc_param[0].o_hsz + 1,
+ param->rsz_rsc_param[0].o_vsz + 1,
+ &line_len, &line_len_c);
+ param->ext_mem_param[0].rsz_sdr_oft_y = line_len;
+ param->ext_mem_param[0].rsz_sdr_oft_c = line_len_c;
+ resizer_calculate_resize_ratios(resizer, RSZ_A);
+ if (param->rsz_en[RSZ_B])
+ resizer_calculate_resize_ratios(resizer, RSZ_B);
+
+ if (outformat->code == MEDIA_BUS_FMT_YDYUYDYV8_1X16)
+ resizer_enable_422_420_conversion(param, RSZ_A, ENABLE);
+ else
+ resizer_enable_422_420_conversion(param, RSZ_A, DISABLE);
+
+ ret = resizer_calculate_sdram_offsets(resizer, RSZ_A);
+ if (!ret && param->rsz_en[RSZ_B])
+ ret = resizer_calculate_sdram_offsets(resizer, RSZ_B);
+
+ if (ret)
+ pr_err("Error in calculating sdram offsets\n");
+ return ret;
+}
+
+static int
+resizer_calculate_down_scale_f_div_param(struct device *dev,
+ int input_width, int output_width,
+ struct resizer_scale_param *param)
+{
+ /* rsz = R, input_width = H, output width = h in the equation */
+ unsigned int two_power;
+ unsigned int upper_h1;
+ unsigned int upper_h2;
+ unsigned int val1;
+ unsigned int val;
+ unsigned int rsz;
+ unsigned int h1;
+ unsigned int h2;
+ unsigned int o;
+ unsigned int n;
+
+ upper_h1 = input_width >> 1;
+ n = param->h_dscale_ave_sz;
+ /* 2 ^ (scale+1) */
+ two_power = 1 << (n + 1);
+ upper_h1 = (upper_h1 >> (n + 1)) << (n + 1);
+ upper_h2 = input_width - upper_h1;
+ if (upper_h2 % two_power) {
+ dev_err(dev, "frame halves to be a multiple of 2 power n+1\n");
+ return -EINVAL;
+ }
+ two_power = 1 << n;
+ rsz = (input_width << 8) / output_width;
+ val = rsz * two_power;
+ val = ((upper_h1 << 8) / val) + 1;
+ if (!(val % 2)) {
+ h1 = val;
+ } else {
+ val = upper_h1 << 8;
+ val >>= n + 1;
+ val -= rsz >> 1;
+ val /= rsz << 1;
+ val <<= 1;
+ val += 2;
+ h1 = val;
+ }
+ o = 10 + (two_power << 2);
+ if (((input_width << 7) / rsz) % 2)
+ o += (((CEIL(rsz, 1024)) << 1) << n);
+ h2 = output_width - h1;
+ /* phi */
+ val = (h1 * rsz) - (((upper_h1 - (o - 10)) / two_power) << 8);
+ /* skip */
+ val1 = ((val - 1024) >> 9) << 1;
+ param->f_div.num_passes = MAX_PASSES;
+ param->f_div.pass[0].o_hsz = h1 - 1;
+ param->f_div.pass[0].i_hps = 0;
+ param->f_div.pass[0].h_phs = 0;
+ param->f_div.pass[0].src_hps = 0;
+ param->f_div.pass[0].src_hsz = upper_h1 + o;
+ param->f_div.pass[1].o_hsz = h2 - 1;
+ param->f_div.pass[1].i_hps = 10 + (val1 * two_power);
+ param->f_div.pass[1].h_phs = (val - (val1 << 8));
+ param->f_div.pass[1].src_hps = upper_h1 - o;
+ param->f_div.pass[1].src_hsz = upper_h2 + o;
+
+ return 0;
+}
+
+static int
+resizer_configure_common_in_params(struct vpfe_resizer_device *resizer)
+{
+ struct vpfe_device *vpfe_dev = to_vpfe_device(resizer);
+ struct resizer_params *param = &resizer->config;
+ struct vpfe_rsz_config_params *user_config;
+ struct v4l2_mbus_framefmt *informat;
+
+ informat = &resizer->crop_resizer.formats[RESIZER_CROP_PAD_SINK];
+ user_config = &resizer->config.user_config;
+ param->rsz_common.vps = param->user_config.vst;
+ param->rsz_common.hps = param->user_config.hst;
+
+ if (vpfe_ipipeif_decimation_enabled(vpfe_dev))
+ param->rsz_common.hsz = (((informat->width - 1) *
+ IPIPEIF_RSZ_CONST) / vpfe_ipipeif_get_rsz(vpfe_dev));
+ else
+ param->rsz_common.hsz = informat->width - 1;
+
+ if (informat->field == V4L2_FIELD_INTERLACED)
+ param->rsz_common.vsz = (informat->height - 1) >> 1;
+ else
+ param->rsz_common.vsz = informat->height - 1;
+
+ param->rsz_common.raw_flip = 0;
+
+ if (resizer->crop_resizer.input == RESIZER_CROP_INPUT_IPIPEIF)
+ param->rsz_common.source = IPIPEIF_DATA;
+ else
+ param->rsz_common.source = IPIPE_DATA;
+
+ switch (informat->code) {
+ case MEDIA_BUS_FMT_UYVY8_2X8:
+ param->rsz_common.src_img_fmt = RSZ_IMG_422;
+ param->rsz_common.raw_flip = 0;
+ break;
+
+ case MEDIA_BUS_FMT_Y8_1X8:
+ param->rsz_common.src_img_fmt = RSZ_IMG_420;
+ /* Select y */
+ param->rsz_common.y_c = 0;
+ param->rsz_common.raw_flip = 0;
+ break;
+
+ case MEDIA_BUS_FMT_UV8_1X8:
+ param->rsz_common.src_img_fmt = RSZ_IMG_420;
+ /* Select y */
+ param->rsz_common.y_c = 1;
+ param->rsz_common.raw_flip = 0;
+ break;
+
+ case MEDIA_BUS_FMT_SGRBG12_1X12:
+ param->rsz_common.raw_flip = 1;
+ break;
+
+ default:
+ param->rsz_common.src_img_fmt = RSZ_IMG_422;
+ param->rsz_common.source = IPIPE_DATA;
+ }
+
+ param->rsz_common.yuv_y_min = user_config->yuv_y_min;
+ param->rsz_common.yuv_y_max = user_config->yuv_y_max;
+ param->rsz_common.yuv_c_min = user_config->yuv_c_min;
+ param->rsz_common.yuv_c_max = user_config->yuv_c_max;
+ param->rsz_common.out_chr_pos = user_config->out_chr_pos;
+ param->rsz_common.rsz_seq_crv = user_config->chroma_sample_even;
+
+ return 0;
+}
+static int
+resizer_configure_in_continious_mode(struct vpfe_resizer_device *resizer)
+{
+ struct device *dev = resizer->crop_resizer.subdev.v4l2_dev->dev;
+ struct resizer_params *param = &resizer->config;
+ struct vpfe_rsz_config_params *cont_config;
+ int line_len_c;
+ int line_len;
+ int ret;
+
+ if (resizer->resizer_a.output != RESIZER_OUPUT_MEMORY) {
+ dev_err(dev, "enable resizer - Resizer-A\n");
+ return -EINVAL;
+ }
+
+ cont_config = &resizer->config.user_config;
+ param->rsz_en[RSZ_A] = ENABLE;
+ configure_resizer_out_params(resizer, RSZ_A,
+ &cont_config->output1, 1, 0);
+ param->rsz_en[RSZ_B] = DISABLE;
+ param->oper_mode = RESIZER_MODE_CONTINIOUS;
+
+ if (resizer->resizer_b.output == RESIZER_OUPUT_MEMORY) {
+ struct v4l2_mbus_framefmt *outformat2;
+
+ param->rsz_en[RSZ_B] = ENABLE;
+ outformat2 = &resizer->resizer_b.formats[RESIZER_PAD_SOURCE];
+ ret = resizer_validate_output_image_format(dev, outformat2,
+ &line_len, &line_len_c);
+ if (ret)
+ return ret;
+ param->ext_mem_param[RSZ_B].rsz_sdr_oft_y = line_len;
+ param->ext_mem_param[RSZ_B].rsz_sdr_oft_c = line_len_c;
+ configure_resizer_out_params(resizer, RSZ_B,
+ &cont_config->output2, 0, 1);
+ if (outformat2->code == MEDIA_BUS_FMT_YDYUYDYV8_1X16)
+ resizer_enable_422_420_conversion(param,
+ RSZ_B, ENABLE);
+ else
+ resizer_enable_422_420_conversion(param,
+ RSZ_B, DISABLE);
+ }
+ resizer_configure_common_in_params(resizer);
+ ret = resizer_configure_output_win(resizer);
+ if (ret)
+ return ret;
+
+ param->rsz_common.passthrough = cont_config->bypass;
+ if (cont_config->bypass)
+ resizer_configure_passthru(resizer, 1);
+
+ return 0;
+}
+
+static inline int
+resizer_validate_input_image_format(struct device *dev,
+ u32 pix,
+ int width, int height, int *line_len)
+{
+ int val;
+
+ if (pix != MEDIA_BUS_FMT_UYVY8_2X8 &&
+ pix != MEDIA_BUS_FMT_Y8_1X8 &&
+ pix != MEDIA_BUS_FMT_UV8_1X8 &&
+ pix != MEDIA_BUS_FMT_SGRBG12_1X12) {
+ dev_err(dev,
+ "resizer validate output: pix format not supported, %d\n", pix);
+ return -EINVAL;
+ }
+
+ if (!width || !height) {
+ dev_err(dev,
+ "resizer validate input: invalid width or height\n");
+ return -EINVAL;
+ }
+
+ if (pix == MEDIA_BUS_FMT_UV8_1X8)
+ resizer_calculate_line_length(pix, width,
+ height, &val, line_len);
+ else
+ resizer_calculate_line_length(pix, width,
+ height, line_len, &val);
+
+ return 0;
+}
+
+static int
+resizer_validate_decimation(struct device *dev, enum ipipeif_decimation dec_en,
+ unsigned char rsz, unsigned char frame_div_mode_en,
+ int width)
+{
+ if (dec_en && frame_div_mode_en) {
+ dev_err(dev,
+ "dec_en & frame_div_mode_en can not enabled simultaneously\n");
+ return -EINVAL;
+ }
+
+ if (frame_div_mode_en) {
+ dev_err(dev, "frame_div_mode mode not supported\n");
+ return -EINVAL;
+ }
+
+ if (!dec_en)
+ return 0;
+
+ if (width <= VPFE_IPIPE_MAX_INPUT_WIDTH) {
+ dev_err(dev,
+ "image width to be more than %d for decimation\n",
+ VPFE_IPIPE_MAX_INPUT_WIDTH);
+ return -EINVAL;
+ }
+
+ if (rsz < IPIPEIF_RSZ_MIN || rsz > IPIPEIF_RSZ_MAX) {
+ dev_err(dev, "rsz range is %d to %d\n",
+ IPIPEIF_RSZ_MIN, IPIPEIF_RSZ_MAX);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/* resizer_calculate_normal_f_div_param() - Algorithm to calculate the frame
+ * division parameters for resizer.
+ * in normal mode.
+ */
+static int
+resizer_calculate_normal_f_div_param(struct device *dev, int input_width,
+ int output_width, struct resizer_scale_param *param)
+{
+ /* rsz = R, input_width = H, output width = h in the equation */
+ unsigned int val1;
+ unsigned int rsz;
+ unsigned int val;
+ unsigned int h1;
+ unsigned int h2;
+ unsigned int o;
+
+ if (output_width > input_width) {
+ dev_err(dev, "frame div mode is used for scale down only\n");
+ return -EINVAL;
+ }
+
+ rsz = (input_width << 8) / output_width;
+ val = rsz << 1;
+ val = ((input_width << 8) / val) + 1;
+ o = 14;
+ if (!(val % 2)) {
+ h1 = val;
+ } else {
+ val = input_width << 7;
+ val -= rsz >> 1;
+ val /= rsz << 1;
+ val <<= 1;
+ val += 2;
+ o += ((CEIL(rsz, 1024)) << 1);
+ h1 = val;
+ }
+ h2 = output_width - h1;
+ /* phi */
+ val = (h1 * rsz) - (((input_width >> 1) - o) << 8);
+ /* skip */
+ val1 = ((val - 1024) >> 9) << 1;
+ param->f_div.num_passes = MAX_PASSES;
+ param->f_div.pass[0].o_hsz = h1 - 1;
+ param->f_div.pass[0].i_hps = 0;
+ param->f_div.pass[0].h_phs = 0;
+ param->f_div.pass[0].src_hps = 0;
+ param->f_div.pass[0].src_hsz = (input_width >> 2) + o;
+ param->f_div.pass[1].o_hsz = h2 - 1;
+ param->f_div.pass[1].i_hps = val1;
+ param->f_div.pass[1].h_phs = (val - (val1 << 8));
+ param->f_div.pass[1].src_hps = (input_width >> 2) - o;
+ param->f_div.pass[1].src_hsz = (input_width >> 2) + o;
+
+ return 0;
+}
+
+static int
+resizer_configure_in_single_shot_mode(struct vpfe_resizer_device *resizer)
+{
+ struct vpfe_rsz_config_params *config = &resizer->config.user_config;
+ struct device *dev = resizer->crop_resizer.subdev.v4l2_dev->dev;
+ struct vpfe_device *vpfe_dev = to_vpfe_device(resizer);
+ struct v4l2_mbus_framefmt *outformat1, *outformat2;
+ struct resizer_params *param = &resizer->config;
+ struct v4l2_mbus_framefmt *informat;
+ int decimation;
+ int line_len_c;
+ int line_len;
+ int rsz;
+ int ret;
+
+ informat = &resizer->crop_resizer.formats[RESIZER_CROP_PAD_SINK];
+ outformat1 = &resizer->resizer_a.formats[RESIZER_PAD_SOURCE];
+ outformat2 = &resizer->resizer_b.formats[RESIZER_PAD_SOURCE];
+
+ decimation = vpfe_ipipeif_decimation_enabled(vpfe_dev);
+ rsz = vpfe_ipipeif_get_rsz(vpfe_dev);
+ if (decimation && param->user_config.frame_div_mode_en) {
+ dev_err(dev,
+ "dec_en & frame_div_mode_en cannot enabled simultaneously\n");
+ return -EINVAL;
+ }
+
+ ret = resizer_validate_decimation(dev, decimation, rsz,
+ param->user_config.frame_div_mode_en, informat->width);
+ if (ret)
+ return -EINVAL;
+
+ ret = resizer_validate_input_image_format(dev, informat->code,
+ informat->width, informat->height, &line_len);
+ if (ret)
+ return -EINVAL;
+
+ if (resizer->resizer_a.output != RESIZER_OUTPUT_NONE) {
+ param->rsz_en[RSZ_A] = ENABLE;
+ ret = resizer_validate_output_image_format(dev, outformat1,
+ &line_len, &line_len_c);
+ if (ret)
+ return ret;
+ param->ext_mem_param[RSZ_A].rsz_sdr_oft_y = line_len;
+ param->ext_mem_param[RSZ_A].rsz_sdr_oft_c = line_len_c;
+ configure_resizer_out_params(resizer, RSZ_A,
+ &param->user_config.output1, 0, 1);
+
+ if (outformat1->code == MEDIA_BUS_FMT_SGRBG12_1X12)
+ param->rsz_common.raw_flip = 1;
+ else
+ param->rsz_common.raw_flip = 0;
+
+ if (outformat1->code == MEDIA_BUS_FMT_YDYUYDYV8_1X16)
+ resizer_enable_422_420_conversion(param,
+ RSZ_A, ENABLE);
+ else
+ resizer_enable_422_420_conversion(param,
+ RSZ_A, DISABLE);
+ }
+
+ if (resizer->resizer_b.output != RESIZER_OUTPUT_NONE) {
+ param->rsz_en[RSZ_B] = ENABLE;
+ ret = resizer_validate_output_image_format(dev, outformat2,
+ &line_len, &line_len_c);
+ if (ret)
+ return ret;
+ param->ext_mem_param[RSZ_B].rsz_sdr_oft_y = line_len;
+ param->ext_mem_param[RSZ_B].rsz_sdr_oft_c = line_len_c;
+ configure_resizer_out_params(resizer, RSZ_B,
+ &param->user_config.output2, 0, 1);
+ if (outformat2->code == MEDIA_BUS_FMT_YDYUYDYV8_1X16)
+ resizer_enable_422_420_conversion(param,
+ RSZ_B, ENABLE);
+ else
+ resizer_enable_422_420_conversion(param,
+ RSZ_B, DISABLE);
+ }
+
+ resizer_configure_common_in_params(resizer);
+ if (resizer->resizer_a.output != RESIZER_OUTPUT_NONE) {
+ resizer_calculate_resize_ratios(resizer, RSZ_A);
+ resizer_calculate_sdram_offsets(resizer, RSZ_A);
+ /* Overriding resize ratio calculation */
+ if (informat->code == MEDIA_BUS_FMT_UV8_1X8) {
+ param->rsz_rsc_param[RSZ_A].v_dif =
+ (((informat->height + 1) * 2) * 256) /
+ (param->rsz_rsc_param[RSZ_A].o_vsz + 1);
+ }
+ }
+
+ if (resizer->resizer_b.output != RESIZER_OUTPUT_NONE) {
+ resizer_calculate_resize_ratios(resizer, RSZ_B);
+ resizer_calculate_sdram_offsets(resizer, RSZ_B);
+ /* Overriding resize ratio calculation */
+ if (informat->code == MEDIA_BUS_FMT_UV8_1X8) {
+ param->rsz_rsc_param[RSZ_B].v_dif =
+ (((informat->height + 1) * 2) * 256) /
+ (param->rsz_rsc_param[RSZ_B].o_vsz + 1);
+ }
+ }
+ if (param->user_config.frame_div_mode_en &&
+ param->rsz_en[RSZ_A]) {
+ if (!param->rsz_rsc_param[RSZ_A].dscale_en)
+ ret = resizer_calculate_normal_f_div_param(dev,
+ informat->width,
+ param->rsz_rsc_param[RSZ_A].o_vsz + 1,
+ &param->rsz_rsc_param[RSZ_A]);
+ else
+ ret = resizer_calculate_down_scale_f_div_param(dev,
+ informat->width,
+ param->rsz_rsc_param[RSZ_A].o_vsz + 1,
+ &param->rsz_rsc_param[RSZ_A]);
+ if (ret)
+ return -EINVAL;
+ }
+ if (param->user_config.frame_div_mode_en &&
+ param->rsz_en[RSZ_B]) {
+ if (!param->rsz_rsc_param[RSZ_B].dscale_en)
+ ret = resizer_calculate_normal_f_div_param(dev,
+ informat->width,
+ param->rsz_rsc_param[RSZ_B].o_vsz + 1,
+ &param->rsz_rsc_param[RSZ_B]);
+ else
+ ret = resizer_calculate_down_scale_f_div_param(dev,
+ informat->width,
+ param->rsz_rsc_param[RSZ_B].o_vsz + 1,
+ &param->rsz_rsc_param[RSZ_B]);
+ if (ret)
+ return -EINVAL;
+ }
+ param->rsz_common.passthrough = config->bypass;
+ if (config->bypass)
+ resizer_configure_passthru(resizer, 1);
+ return 0;
+}
+
+static void
+resizer_set_defualt_configuration(struct vpfe_resizer_device *resizer)
+{
+#define WIDTH_I 640
+#define HEIGHT_I 480
+#define WIDTH_O 640
+#define HEIGHT_O 480
+ const struct resizer_params rsz_default_config = {
+ .oper_mode = RESIZER_MODE_ONE_SHOT,
+ .rsz_common = {
+ .vsz = HEIGHT_I - 1,
+ .hsz = WIDTH_I - 1,
+ .src_img_fmt = RSZ_IMG_422,
+ .raw_flip = 1, /* flip preserve Raw format */
+ .source = IPIPE_DATA,
+ .passthrough = BYPASS_OFF,
+ .yuv_y_max = 255,
+ .yuv_c_max = 255,
+ .rsz_seq_crv = DISABLE,
+ .out_chr_pos = VPFE_IPIPE_YUV422_CHR_POS_COSITE,
+ },
+ .rsz_rsc_param = {
+ {
+ .h_flip = DISABLE,
+ .v_flip = DISABLE,
+ .cen = DISABLE,
+ .yen = DISABLE,
+ .o_vsz = HEIGHT_O - 1,
+ .o_hsz = WIDTH_O - 1,
+ .v_dif = 256,
+ .v_typ_y = VPFE_RSZ_INTP_CUBIC,
+ .h_typ_c = VPFE_RSZ_INTP_CUBIC,
+ .h_dif = 256,
+ .h_typ_y = VPFE_RSZ_INTP_CUBIC,
+ .h_typ_c = VPFE_RSZ_INTP_CUBIC,
+ .h_dscale_ave_sz =
+ VPFE_IPIPE_DWN_SCALE_1_OVER_2,
+ .v_dscale_ave_sz =
+ VPFE_IPIPE_DWN_SCALE_1_OVER_2,
+ },
+ {
+ .h_flip = DISABLE,
+ .v_flip = DISABLE,
+ .cen = DISABLE,
+ .yen = DISABLE,
+ .o_vsz = HEIGHT_O - 1,
+ .o_hsz = WIDTH_O - 1,
+ .v_dif = 256,
+ .v_typ_y = VPFE_RSZ_INTP_CUBIC,
+ .h_typ_c = VPFE_RSZ_INTP_CUBIC,
+ .h_dif = 256,
+ .h_typ_y = VPFE_RSZ_INTP_CUBIC,
+ .h_typ_c = VPFE_RSZ_INTP_CUBIC,
+ .h_dscale_ave_sz =
+ VPFE_IPIPE_DWN_SCALE_1_OVER_2,
+ .v_dscale_ave_sz =
+ VPFE_IPIPE_DWN_SCALE_1_OVER_2,
+ },
+ },
+ .rsz2rgb = {
+ {
+ .rgb_en = DISABLE
+ },
+ {
+ .rgb_en = DISABLE
+ }
+ },
+ .ext_mem_param = {
+ {
+ .rsz_sdr_oft_y = WIDTH_O << 1,
+ .rsz_sdr_ptr_e_y = HEIGHT_O,
+ .rsz_sdr_oft_c = WIDTH_O,
+ .rsz_sdr_ptr_e_c = HEIGHT_O >> 1,
+ },
+ {
+ .rsz_sdr_oft_y = WIDTH_O << 1,
+ .rsz_sdr_ptr_e_y = HEIGHT_O,
+ .rsz_sdr_oft_c = WIDTH_O,
+ .rsz_sdr_ptr_e_c = HEIGHT_O,
+ },
+ },
+ .rsz_en[0] = ENABLE,
+ .rsz_en[1] = DISABLE,
+ .user_config = {
+ .output1 = {
+ .v_typ_y = VPFE_RSZ_INTP_CUBIC,
+ .v_typ_c = VPFE_RSZ_INTP_CUBIC,
+ .h_typ_y = VPFE_RSZ_INTP_CUBIC,
+ .h_typ_c = VPFE_RSZ_INTP_CUBIC,
+ .h_dscale_ave_sz =
+ VPFE_IPIPE_DWN_SCALE_1_OVER_2,
+ .v_dscale_ave_sz =
+ VPFE_IPIPE_DWN_SCALE_1_OVER_2,
+ },
+ .output2 = {
+ .v_typ_y = VPFE_RSZ_INTP_CUBIC,
+ .v_typ_c = VPFE_RSZ_INTP_CUBIC,
+ .h_typ_y = VPFE_RSZ_INTP_CUBIC,
+ .h_typ_c = VPFE_RSZ_INTP_CUBIC,
+ .h_dscale_ave_sz =
+ VPFE_IPIPE_DWN_SCALE_1_OVER_2,
+ .v_dscale_ave_sz =
+ VPFE_IPIPE_DWN_SCALE_1_OVER_2,
+ },
+ .yuv_y_max = 255,
+ .yuv_c_max = 255,
+ .out_chr_pos = VPFE_IPIPE_YUV422_CHR_POS_COSITE,
+ },
+ };
+ memcpy(&resizer->config, &rsz_default_config,
+ sizeof(struct resizer_params));
+}
+
+/*
+ * resizer_set_configuration() - set resizer config
+ * @resizer: vpfe resizer device pointer.
+ * @chan_config: resizer channel configuration.
+ */
+static int
+resizer_set_configuration(struct vpfe_resizer_device *resizer,
+ struct vpfe_rsz_config *chan_config)
+{
+ if (!chan_config->config)
+ resizer_set_defualt_configuration(resizer);
+ else
+ if (copy_from_user(&resizer->config.user_config,
+ chan_config->config, sizeof(struct vpfe_rsz_config_params)))
+ return -EFAULT;
+
+ return 0;
+}
+
+/*
+ * resizer_get_configuration() - get resizer config
+ * @resizer: vpfe resizer device pointer.
+ * @channel: image processor logical channel.
+ * @chan_config: resizer channel configuration.
+ */
+static int
+resizer_get_configuration(struct vpfe_resizer_device *resizer,
+ struct vpfe_rsz_config *chan_config)
+{
+ struct device *dev = resizer->crop_resizer.subdev.v4l2_dev->dev;
+
+ if (!chan_config->config) {
+ dev_err(dev, "Resizer channel invalid pointer\n");
+ return -EINVAL;
+ }
+
+ if (copy_to_user((void *)chan_config->config,
+ (void *)&resizer->config.user_config,
+ sizeof(struct vpfe_rsz_config_params))) {
+ dev_err(dev, "resizer_get_configuration: Error in copy to user\n");
+ return -EFAULT;
+ }
+
+ return 0;
+}
+
+/*
+ * VPFE video operations
+ */
+
+/*
+ * resizer_a_video_out_queue() - RESIZER-A video out queue
+ * @vpfe_dev: vpfe device pointer.
+ * @addr: buffer address.
+ */
+static int resizer_a_video_out_queue(struct vpfe_device *vpfe_dev,
+ unsigned long addr)
+{
+ struct vpfe_resizer_device *resizer = &vpfe_dev->vpfe_resizer;
+
+ return resizer_set_outaddr(resizer->base_addr,
+ &resizer->config, RSZ_A, addr);
+}
+
+/*
+ * resizer_b_video_out_queue() - RESIZER-B video out queue
+ * @vpfe_dev: vpfe device pointer.
+ * @addr: buffer address.
+ */
+static int resizer_b_video_out_queue(struct vpfe_device *vpfe_dev,
+ unsigned long addr)
+{
+ struct vpfe_resizer_device *resizer = &vpfe_dev->vpfe_resizer;
+
+ return resizer_set_outaddr(resizer->base_addr,
+ &resizer->config, RSZ_B, addr);
+}
+
+static const struct vpfe_video_operations resizer_a_video_ops = {
+ .queue = resizer_a_video_out_queue,
+};
+
+static const struct vpfe_video_operations resizer_b_video_ops = {
+ .queue = resizer_b_video_out_queue,
+};
+
+static void resizer_enable(struct vpfe_resizer_device *resizer, int en)
+{
+ struct vpfe_device *vpfe_dev = to_vpfe_device(resizer);
+ u16 ipipeif_sink = vpfe_dev->vpfe_ipipeif.input;
+ unsigned char val;
+
+ if (resizer->crop_resizer.input == RESIZER_CROP_INPUT_NONE)
+ return;
+
+ if (resizer->crop_resizer.input == RESIZER_CROP_INPUT_IPIPEIF &&
+ ipipeif_sink == IPIPEIF_INPUT_MEMORY) {
+ do {
+ val = regr_rsz(resizer->base_addr, RSZ_SRC_EN);
+ } while (val);
+
+ if (resizer->resizer_a.output != RESIZER_OUTPUT_NONE) {
+ do {
+ val = regr_rsz(resizer->base_addr, RSZ_A);
+ } while (val);
+ }
+ if (resizer->resizer_b.output != RESIZER_OUTPUT_NONE) {
+ do {
+ val = regr_rsz(resizer->base_addr, RSZ_B);
+ } while (val);
+ }
+ }
+ if (resizer->resizer_a.output != RESIZER_OUTPUT_NONE)
+ rsz_enable(resizer->base_addr, RSZ_A, en);
+
+ if (resizer->resizer_b.output != RESIZER_OUTPUT_NONE)
+ rsz_enable(resizer->base_addr, RSZ_B, en);
+}
+
+
+/*
+ * resizer_ss_isr() - resizer module single-shot buffer scheduling isr
+ * @resizer: vpfe resizer device pointer.
+ */
+static void resizer_ss_isr(struct vpfe_resizer_device *resizer)
+{
+ struct vpfe_video_device *video_out = &resizer->resizer_a.video_out;
+ struct vpfe_video_device *video_out2 = &resizer->resizer_b.video_out;
+ struct vpfe_device *vpfe_dev = to_vpfe_device(resizer);
+ struct vpfe_pipeline *pipe = &video_out->pipe;
+ u16 ipipeif_sink = vpfe_dev->vpfe_ipipeif.input;
+ u32 val;
+
+ if (ipipeif_sink != IPIPEIF_INPUT_MEMORY)
+ return;
+
+ if (resizer->resizer_a.output == RESIZER_OUPUT_MEMORY) {
+ val = vpss_dma_complete_interrupt();
+ if (val != 0 && val != 2)
+ return;
+ }
+
+ if (resizer->resizer_a.output == RESIZER_OUPUT_MEMORY) {
+ spin_lock(&video_out->dma_queue_lock);
+ vpfe_video_process_buffer_complete(video_out);
+ video_out->state = VPFE_VIDEO_BUFFER_NOT_QUEUED;
+ vpfe_video_schedule_next_buffer(video_out);
+ spin_unlock(&video_out->dma_queue_lock);
+ }
+
+ /* If resizer B is enabled */
+ if (pipe->output_num > 1 && resizer->resizer_b.output ==
+ RESIZER_OUPUT_MEMORY) {
+ spin_lock(&video_out->dma_queue_lock);
+ vpfe_video_process_buffer_complete(video_out2);
+ video_out2->state = VPFE_VIDEO_BUFFER_NOT_QUEUED;
+ vpfe_video_schedule_next_buffer(video_out2);
+ spin_unlock(&video_out2->dma_queue_lock);
+ }
+
+ /* start HW if buffers are queued */
+ if (vpfe_video_is_pipe_ready(pipe) &&
+ resizer->resizer_a.output == RESIZER_OUPUT_MEMORY) {
+ resizer_enable(resizer, 1);
+ vpfe_ipipe_enable(vpfe_dev, 1);
+ vpfe_ipipeif_enable(vpfe_dev);
+ }
+}
+
+/*
+ * vpfe_resizer_buffer_isr() - resizer module buffer scheduling isr
+ * @resizer: vpfe resizer device pointer.
+ */
+void vpfe_resizer_buffer_isr(struct vpfe_resizer_device *resizer)
+{
+ struct vpfe_device *vpfe_dev = to_vpfe_device(resizer);
+ struct vpfe_video_device *video_out = &resizer->resizer_a.video_out;
+ struct vpfe_video_device *video_out2 = &resizer->resizer_b.video_out;
+ struct vpfe_pipeline *pipe = &resizer->resizer_a.video_out.pipe;
+ enum v4l2_field field;
+ int fid;
+
+ if (!video_out->started)
+ return;
+
+ if (resizer->crop_resizer.input == RESIZER_CROP_INPUT_NONE)
+ return;
+
+ field = video_out->fmt.fmt.pix.field;
+ if (field == V4L2_FIELD_NONE) {
+ /* handle progressive frame capture */
+ if (video_out->cur_frm != video_out->next_frm) {
+ vpfe_video_process_buffer_complete(video_out);
+ if (pipe->output_num > 1)
+ vpfe_video_process_buffer_complete(video_out2);
+ }
+
+ video_out->skip_frame_count--;
+ if (!video_out->skip_frame_count) {
+ video_out->skip_frame_count =
+ video_out->skip_frame_count_init;
+ rsz_src_enable(resizer->base_addr, 1);
+ } else {
+ rsz_src_enable(resizer->base_addr, 0);
+ }
+ return;
+ }
+
+ /* handle interlaced frame capture */
+ fid = vpfe_isif_get_fid(vpfe_dev);
+
+ /* switch the software maintained field id */
+ video_out->field_id ^= 1;
+ if (fid == video_out->field_id) {
+ /*
+ * we are in-sync here,continue.
+ * One frame is just being captured. If the
+ * next frame is available, release the current
+ * frame and move on
+ */
+ if (fid == 0 && video_out->cur_frm != video_out->next_frm) {
+ vpfe_video_process_buffer_complete(video_out);
+ if (pipe->output_num > 1)
+ vpfe_video_process_buffer_complete(video_out2);
+ }
+ } else if (fid == 0) {
+ /*
+ * out of sync. Recover from any hardware out-of-sync.
+ * May loose one frame
+ */
+ video_out->field_id = fid;
+ }
+}
+
+/*
+ * vpfe_resizer_dma_isr() - resizer module dma isr
+ * @resizer: vpfe resizer device pointer.
+ */
+void vpfe_resizer_dma_isr(struct vpfe_resizer_device *resizer)
+{
+ struct vpfe_video_device *video_out2 = &resizer->resizer_b.video_out;
+ struct vpfe_video_device *video_out = &resizer->resizer_a.video_out;
+ struct vpfe_device *vpfe_dev = to_vpfe_device(resizer);
+ struct vpfe_pipeline *pipe = &video_out->pipe;
+ int schedule_capture = 0;
+ enum v4l2_field field;
+ int fid;
+
+ if (!video_out->started)
+ return;
+
+ if (pipe->state == VPFE_PIPELINE_STREAM_SINGLESHOT) {
+ resizer_ss_isr(resizer);
+ return;
+ }
+
+ field = video_out->fmt.fmt.pix.field;
+ if (field == V4L2_FIELD_NONE) {
+ if (!list_empty(&video_out->dma_queue) &&
+ video_out->cur_frm == video_out->next_frm)
+ schedule_capture = 1;
+ } else {
+ fid = vpfe_isif_get_fid(vpfe_dev);
+ if (fid == video_out->field_id) {
+ /* we are in-sync here,continue */
+ if (fid == 1 && !list_empty(&video_out->dma_queue) &&
+ video_out->cur_frm == video_out->next_frm)
+ schedule_capture = 1;
+ }
+ }
+
+ if (!schedule_capture)
+ return;
+
+ spin_lock(&video_out->dma_queue_lock);
+ vpfe_video_schedule_next_buffer(video_out);
+ spin_unlock(&video_out->dma_queue_lock);
+ if (pipe->output_num > 1) {
+ spin_lock(&video_out2->dma_queue_lock);
+ vpfe_video_schedule_next_buffer(video_out2);
+ spin_unlock(&video_out2->dma_queue_lock);
+ }
+}
+
+/*
+ * V4L2 subdev operations
+ */
+
+/*
+ * resizer_ioctl() - Handle resizer module private ioctl's
+ * @sd: pointer to v4l2 subdev structure
+ * @cmd: configuration command
+ * @arg: configuration argument
+ */
+static long resizer_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg)
+{
+ struct vpfe_resizer_device *resizer = v4l2_get_subdevdata(sd);
+ struct device *dev = resizer->crop_resizer.subdev.v4l2_dev->dev;
+ struct vpfe_rsz_config *user_config;
+ int ret = -ENOIOCTLCMD;
+
+ if (&resizer->crop_resizer.subdev != sd)
+ return ret;
+
+ switch (cmd) {
+ case VIDIOC_VPFE_RSZ_S_CONFIG:
+ user_config = arg;
+ ret = resizer_set_configuration(resizer, user_config);
+ break;
+
+ case VIDIOC_VPFE_RSZ_G_CONFIG:
+ user_config = arg;
+ if (!user_config->config) {
+ dev_err(dev, "error in VIDIOC_VPFE_RSZ_G_CONFIG\n");
+ return -EINVAL;
+ }
+ ret = resizer_get_configuration(resizer, user_config);
+ break;
+ }
+ return ret;
+}
+
+static int resizer_do_hw_setup(struct vpfe_resizer_device *resizer)
+{
+ struct vpfe_device *vpfe_dev = to_vpfe_device(resizer);
+ u16 ipipeif_sink = vpfe_dev->vpfe_ipipeif.input;
+ u16 ipipeif_source = vpfe_dev->vpfe_ipipeif.output;
+ struct resizer_params *param = &resizer->config;
+ int ret = 0;
+
+ if (resizer->resizer_a.output == RESIZER_OUPUT_MEMORY ||
+ resizer->resizer_b.output == RESIZER_OUPUT_MEMORY) {
+ if (ipipeif_sink == IPIPEIF_INPUT_MEMORY &&
+ ipipeif_source == IPIPEIF_OUTPUT_RESIZER)
+ ret = resizer_configure_in_single_shot_mode(resizer);
+ else
+ ret = resizer_configure_in_continious_mode(resizer);
+ if (ret)
+ return ret;
+ ret = config_rsz_hw(resizer, param);
+ }
+ return ret;
+}
+
+/*
+ * resizer_set_stream() - Enable/Disable streaming on resizer subdev
+ * @sd: pointer to v4l2 subdev structure
+ * @enable: 1 == Enable, 0 == Disable
+ */
+static int resizer_set_stream(struct v4l2_subdev *sd, int enable)
+{
+ struct vpfe_resizer_device *resizer = v4l2_get_subdevdata(sd);
+
+ if (&resizer->crop_resizer.subdev != sd)
+ return 0;
+
+ if (resizer->resizer_a.output != RESIZER_OUPUT_MEMORY)
+ return 0;
+
+ switch (enable) {
+ case 1:
+ if (resizer_do_hw_setup(resizer) < 0)
+ return -EINVAL;
+ resizer_enable(resizer, enable);
+ break;
+
+ case 0:
+ resizer_enable(resizer, enable);
+ break;
+ }
+
+ return 0;
+}
+
+/*
+ * __resizer_get_format() - helper function for getting resizer format
+ * @sd: pointer to subdev.
+ * @cfg: V4L2 subdev pad config
+ * @pad: pad number.
+ * @which: wanted subdev format.
+ * Retun wanted mbus frame format.
+ */
+static struct v4l2_mbus_framefmt *
+__resizer_get_format(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg,
+ unsigned int pad, enum v4l2_subdev_format_whence which)
+{
+ struct vpfe_resizer_device *resizer = v4l2_get_subdevdata(sd);
+
+ if (which == V4L2_SUBDEV_FORMAT_TRY)
+ return v4l2_subdev_get_try_format(sd, cfg, pad);
+ if (&resizer->crop_resizer.subdev == sd)
+ return &resizer->crop_resizer.formats[pad];
+ if (&resizer->resizer_a.subdev == sd)
+ return &resizer->resizer_a.formats[pad];
+ if (&resizer->resizer_b.subdev == sd)
+ return &resizer->resizer_b.formats[pad];
+ return NULL;
+}
+
+/*
+ * resizer_try_format() - Handle try format by pad subdev method
+ * @sd: pointer to subdev.
+ * @cfg: V4L2 subdev pad config
+ * @pad: pad num.
+ * @fmt: pointer to v4l2 format structure.
+ * @which: wanted subdev format.
+ */
+static void
+resizer_try_format(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg,
+ unsigned int pad, struct v4l2_mbus_framefmt *fmt,
+ enum v4l2_subdev_format_whence which)
+{
+ struct vpfe_resizer_device *resizer = v4l2_get_subdevdata(sd);
+ unsigned int max_out_height;
+ unsigned int max_out_width;
+ unsigned int i;
+
+ if ((&resizer->resizer_a.subdev == sd && pad == RESIZER_PAD_SINK) ||
+ (&resizer->resizer_b.subdev == sd && pad == RESIZER_PAD_SINK) ||
+ (&resizer->crop_resizer.subdev == sd &&
+ (pad == RESIZER_CROP_PAD_SOURCE ||
+ pad == RESIZER_CROP_PAD_SOURCE2 || pad == RESIZER_CROP_PAD_SINK))) {
+ for (i = 0; i < ARRAY_SIZE(resizer_input_formats); i++) {
+ if (fmt->code == resizer_input_formats[i])
+ break;
+ }
+ /* If not found, use UYVY as default */
+ if (i >= ARRAY_SIZE(resizer_input_formats))
+ fmt->code = MEDIA_BUS_FMT_UYVY8_2X8;
+
+ fmt->width = clamp_t(u32, fmt->width, MIN_IN_WIDTH,
+ MAX_IN_WIDTH);
+ fmt->height = clamp_t(u32, fmt->height, MIN_IN_HEIGHT,
+ MAX_IN_HEIGHT);
+ } else if (&resizer->resizer_a.subdev == sd &&
+ pad == RESIZER_PAD_SOURCE) {
+ max_out_width = IPIPE_MAX_OUTPUT_WIDTH_A;
+ max_out_height = IPIPE_MAX_OUTPUT_HEIGHT_A;
+
+ for (i = 0; i < ARRAY_SIZE(resizer_output_formats); i++) {
+ if (fmt->code == resizer_output_formats[i])
+ break;
+ }
+ /* If not found, use UYVY as default */
+ if (i >= ARRAY_SIZE(resizer_output_formats))
+ fmt->code = MEDIA_BUS_FMT_UYVY8_2X8;
+
+ fmt->width = clamp_t(u32, fmt->width, MIN_OUT_WIDTH,
+ max_out_width);
+ fmt->width &= ~15;
+ fmt->height = clamp_t(u32, fmt->height, MIN_OUT_HEIGHT,
+ max_out_height);
+ } else if (&resizer->resizer_b.subdev == sd &&
+ pad == RESIZER_PAD_SOURCE) {
+ max_out_width = IPIPE_MAX_OUTPUT_WIDTH_B;
+ max_out_height = IPIPE_MAX_OUTPUT_HEIGHT_B;
+
+ for (i = 0; i < ARRAY_SIZE(resizer_output_formats); i++) {
+ if (fmt->code == resizer_output_formats[i])
+ break;
+ }
+ /* If not found, use UYVY as default */
+ if (i >= ARRAY_SIZE(resizer_output_formats))
+ fmt->code = MEDIA_BUS_FMT_UYVY8_2X8;
+
+ fmt->width = clamp_t(u32, fmt->width, MIN_OUT_WIDTH,
+ max_out_width);
+ fmt->width &= ~15;
+ fmt->height = clamp_t(u32, fmt->height, MIN_OUT_HEIGHT,
+ max_out_height);
+ }
+}
+
+/*
+ * resizer_set_format() - Handle set format by pads subdev method
+ * @sd: pointer to v4l2 subdev structure
+ * @cfg: V4L2 subdev pad config
+ * @fmt: pointer to v4l2 subdev format structure
+ * return -EINVAL or zero on success
+ */
+static int resizer_set_format(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_format *fmt)
+{
+ struct vpfe_resizer_device *resizer = v4l2_get_subdevdata(sd);
+ struct v4l2_mbus_framefmt *format;
+
+ format = __resizer_get_format(sd, cfg, fmt->pad, fmt->which);
+ if (format == NULL)
+ return -EINVAL;
+
+ resizer_try_format(sd, cfg, fmt->pad, &fmt->format, fmt->which);
+ *format = fmt->format;
+
+ if (fmt->which == V4L2_SUBDEV_FORMAT_TRY)
+ return 0;
+
+ if (&resizer->crop_resizer.subdev == sd) {
+ if (fmt->pad == RESIZER_CROP_PAD_SINK) {
+ resizer->crop_resizer.formats[fmt->pad] = fmt->format;
+ } else if (fmt->pad == RESIZER_CROP_PAD_SOURCE &&
+ resizer->crop_resizer.output == RESIZER_A) {
+ resizer->crop_resizer.formats[fmt->pad] = fmt->format;
+ resizer->crop_resizer.
+ formats[RESIZER_CROP_PAD_SOURCE2] = fmt->format;
+ } else if (fmt->pad == RESIZER_CROP_PAD_SOURCE2 &&
+ resizer->crop_resizer.output2 == RESIZER_B) {
+ resizer->crop_resizer.formats[fmt->pad] = fmt->format;
+ resizer->crop_resizer.
+ formats[RESIZER_CROP_PAD_SOURCE] = fmt->format;
+ } else {
+ return -EINVAL;
+ }
+ } else if (&resizer->resizer_a.subdev == sd) {
+ if (fmt->pad == RESIZER_PAD_SINK)
+ resizer->resizer_a.formats[fmt->pad] = fmt->format;
+ else if (fmt->pad == RESIZER_PAD_SOURCE)
+ resizer->resizer_a.formats[fmt->pad] = fmt->format;
+ else
+ return -EINVAL;
+ } else if (&resizer->resizer_b.subdev == sd) {
+ if (fmt->pad == RESIZER_PAD_SINK)
+ resizer->resizer_b.formats[fmt->pad] = fmt->format;
+ else if (fmt->pad == RESIZER_PAD_SOURCE)
+ resizer->resizer_b.formats[fmt->pad] = fmt->format;
+ else
+ return -EINVAL;
+ } else {
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/*
+ * resizer_get_format() - Retrieve the video format on a pad
+ * @sd: pointer to v4l2 subdev structure.
+ * @cfg: V4L2 subdev pad config
+ * @fmt: pointer to v4l2 subdev format structure
+ * return -EINVAL or zero on success
+ */
+static int resizer_get_format(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_format *fmt)
+{
+ struct v4l2_mbus_framefmt *format;
+
+ format = __resizer_get_format(sd, cfg, fmt->pad, fmt->which);
+ if (format == NULL)
+ return -EINVAL;
+
+ fmt->format = *format;
+
+ return 0;
+}
+
+/*
+ * resizer_enum_frame_size() - enum frame sizes on pads
+ * @sd: Pointer to subdevice.
+ * @cfg: V4L2 subdev pad config
+ * @code: pointer to v4l2_subdev_frame_size_enum structure.
+ */
+static int resizer_enum_frame_size(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_frame_size_enum *fse)
+{
+ struct v4l2_mbus_framefmt format;
+
+ if (fse->index != 0)
+ return -EINVAL;
+
+ format.code = fse->code;
+ format.width = 1;
+ format.height = 1;
+ resizer_try_format(sd, cfg, fse->pad, &format, fse->which);
+ fse->min_width = format.width;
+ fse->min_height = format.height;
+
+ if (format.code != fse->code)
+ return -EINVAL;
+
+ format.code = fse->code;
+ format.width = -1;
+ format.height = -1;
+ resizer_try_format(sd, cfg, fse->pad, &format, fse->which);
+ fse->max_width = format.width;
+ fse->max_height = format.height;
+
+ return 0;
+}
+
+/*
+ * resizer_enum_mbus_code() - enum mbus codes for pads
+ * @sd: Pointer to subdevice.
+ * @cfg: V4L2 subdev pad config
+ * @code: pointer to v4l2_subdev_mbus_code_enum structure
+ */
+static int resizer_enum_mbus_code(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_mbus_code_enum *code)
+{
+ if (code->pad == RESIZER_PAD_SINK) {
+ if (code->index >= ARRAY_SIZE(resizer_input_formats))
+ return -EINVAL;
+
+ code->code = resizer_input_formats[code->index];
+ } else if (code->pad == RESIZER_PAD_SOURCE) {
+ if (code->index >= ARRAY_SIZE(resizer_output_formats))
+ return -EINVAL;
+
+ code->code = resizer_output_formats[code->index];
+ }
+
+ return 0;
+}
+
+/*
+ * resizer_init_formats() - Initialize formats on all pads
+ * @sd: Pointer to subdevice.
+ * @fh: V4L2 subdev file handle.
+ *
+ * Initialize all pad formats with default values. Try formats are
+ * initialized on the file handle.
+ */
+static int resizer_init_formats(struct v4l2_subdev *sd,
+ struct v4l2_subdev_fh *fh)
+{
+ __u32 which = V4L2_SUBDEV_FORMAT_TRY;
+ struct vpfe_resizer_device *resizer = v4l2_get_subdevdata(sd);
+ struct v4l2_subdev_format format;
+
+ if (&resizer->crop_resizer.subdev == sd) {
+ memset(&format, 0, sizeof(format));
+ format.pad = RESIZER_CROP_PAD_SINK;
+ format.which = which;
+ format.format.code = MEDIA_BUS_FMT_YUYV8_2X8;
+ format.format.width = MAX_IN_WIDTH;
+ format.format.height = MAX_IN_HEIGHT;
+ resizer_set_format(sd, fh->pad, &format);
+
+ memset(&format, 0, sizeof(format));
+ format.pad = RESIZER_CROP_PAD_SOURCE;
+ format.which = which;
+ format.format.code = MEDIA_BUS_FMT_UYVY8_2X8;
+ format.format.width = MAX_IN_WIDTH;
+ format.format.height = MAX_IN_WIDTH;
+ resizer_set_format(sd, fh->pad, &format);
+
+ memset(&format, 0, sizeof(format));
+ format.pad = RESIZER_CROP_PAD_SOURCE2;
+ format.which = which;
+ format.format.code = MEDIA_BUS_FMT_UYVY8_2X8;
+ format.format.width = MAX_IN_WIDTH;
+ format.format.height = MAX_IN_WIDTH;
+ resizer_set_format(sd, fh->pad, &format);
+ } else if (&resizer->resizer_a.subdev == sd) {
+ memset(&format, 0, sizeof(format));
+ format.pad = RESIZER_PAD_SINK;
+ format.which = which;
+ format.format.code = MEDIA_BUS_FMT_YUYV8_2X8;
+ format.format.width = MAX_IN_WIDTH;
+ format.format.height = MAX_IN_HEIGHT;
+ resizer_set_format(sd, fh->pad, &format);
+
+ memset(&format, 0, sizeof(format));
+ format.pad = RESIZER_PAD_SOURCE;
+ format.which = which;
+ format.format.code = MEDIA_BUS_FMT_UYVY8_2X8;
+ format.format.width = IPIPE_MAX_OUTPUT_WIDTH_A;
+ format.format.height = IPIPE_MAX_OUTPUT_HEIGHT_A;
+ resizer_set_format(sd, fh->pad, &format);
+ } else if (&resizer->resizer_b.subdev == sd) {
+ memset(&format, 0, sizeof(format));
+ format.pad = RESIZER_PAD_SINK;
+ format.which = which;
+ format.format.code = MEDIA_BUS_FMT_YUYV8_2X8;
+ format.format.width = MAX_IN_WIDTH;
+ format.format.height = MAX_IN_HEIGHT;
+ resizer_set_format(sd, fh->pad, &format);
+
+ memset(&format, 0, sizeof(format));
+ format.pad = RESIZER_PAD_SOURCE;
+ format.which = which;
+ format.format.code = MEDIA_BUS_FMT_UYVY8_2X8;
+ format.format.width = IPIPE_MAX_OUTPUT_WIDTH_B;
+ format.format.height = IPIPE_MAX_OUTPUT_HEIGHT_B;
+ resizer_set_format(sd, fh->pad, &format);
+ }
+
+ return 0;
+}
+
+/* subdev core operations */
+static const struct v4l2_subdev_core_ops resizer_v4l2_core_ops = {
+ .ioctl = resizer_ioctl,
+};
+
+/* subdev internal operations */
+static const struct v4l2_subdev_internal_ops resizer_v4l2_internal_ops = {
+ .open = resizer_init_formats,
+};
+
+/* subdev video operations */
+static const struct v4l2_subdev_video_ops resizer_v4l2_video_ops = {
+ .s_stream = resizer_set_stream,
+};
+
+/* subdev pad operations */
+static const struct v4l2_subdev_pad_ops resizer_v4l2_pad_ops = {
+ .enum_mbus_code = resizer_enum_mbus_code,
+ .enum_frame_size = resizer_enum_frame_size,
+ .get_fmt = resizer_get_format,
+ .set_fmt = resizer_set_format,
+};
+
+/* subdev operations */
+static const struct v4l2_subdev_ops resizer_v4l2_ops = {
+ .core = &resizer_v4l2_core_ops,
+ .video = &resizer_v4l2_video_ops,
+ .pad = &resizer_v4l2_pad_ops,
+};
+
+/*
+ * Media entity operations
+ */
+
+/*
+ * resizer_link_setup() - Setup resizer connections
+ * @entity: Pointer to media entity structure
+ * @local: Pointer to local pad array
+ * @remote: Pointer to remote pad array
+ * @flags: Link flags
+ * return -EINVAL or zero on success
+ */
+static int resizer_link_setup(struct media_entity *entity,
+ const struct media_pad *local,
+ const struct media_pad *remote, u32 flags)
+{
+ struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity);
+ struct vpfe_resizer_device *resizer = v4l2_get_subdevdata(sd);
+ struct vpfe_device *vpfe_dev = to_vpfe_device(resizer);
+ u16 ipipeif_source = vpfe_dev->vpfe_ipipeif.output;
+ u16 ipipe_source = vpfe_dev->vpfe_ipipe.output;
+
+ if (&resizer->crop_resizer.subdev == sd) {
+ switch (local->index | media_entity_type(remote->entity)) {
+ case RESIZER_CROP_PAD_SINK | MEDIA_ENT_T_V4L2_SUBDEV:
+ if (!(flags & MEDIA_LNK_FL_ENABLED)) {
+ resizer->crop_resizer.input =
+ RESIZER_CROP_INPUT_NONE;
+ break;
+ }
+
+ if (resizer->crop_resizer.input !=
+ RESIZER_CROP_INPUT_NONE)
+ return -EBUSY;
+ if (ipipeif_source == IPIPEIF_OUTPUT_RESIZER)
+ resizer->crop_resizer.input =
+ RESIZER_CROP_INPUT_IPIPEIF;
+ else if (ipipe_source == IPIPE_OUTPUT_RESIZER)
+ resizer->crop_resizer.input =
+ RESIZER_CROP_INPUT_IPIPE;
+ else
+ return -EINVAL;
+ break;
+
+ case RESIZER_CROP_PAD_SOURCE | MEDIA_ENT_T_V4L2_SUBDEV:
+ if (!(flags & MEDIA_LNK_FL_ENABLED)) {
+ resizer->crop_resizer.output =
+ RESIZER_CROP_OUTPUT_NONE;
+ break;
+ }
+ if (resizer->crop_resizer.output !=
+ RESIZER_CROP_OUTPUT_NONE)
+ return -EBUSY;
+ resizer->crop_resizer.output = RESIZER_A;
+ break;
+
+ case RESIZER_CROP_PAD_SOURCE2 | MEDIA_ENT_T_V4L2_SUBDEV:
+ if (!(flags & MEDIA_LNK_FL_ENABLED)) {
+ resizer->crop_resizer.output2 =
+ RESIZER_CROP_OUTPUT_NONE;
+ break;
+ }
+ if (resizer->crop_resizer.output2 !=
+ RESIZER_CROP_OUTPUT_NONE)
+ return -EBUSY;
+ resizer->crop_resizer.output2 = RESIZER_B;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+ } else if (&resizer->resizer_a.subdev == sd) {
+ switch (local->index | media_entity_type(remote->entity)) {
+ case RESIZER_PAD_SINK | MEDIA_ENT_T_V4L2_SUBDEV:
+ if (!(flags & MEDIA_LNK_FL_ENABLED)) {
+ resizer->resizer_a.input = RESIZER_INPUT_NONE;
+ break;
+ }
+ if (resizer->resizer_a.input != RESIZER_INPUT_NONE)
+ return -EBUSY;
+ resizer->resizer_a.input = RESIZER_INPUT_CROP_RESIZER;
+ break;
+
+ case RESIZER_PAD_SOURCE | MEDIA_ENT_T_DEVNODE:
+ if (!(flags & MEDIA_LNK_FL_ENABLED)) {
+ resizer->resizer_a.output = RESIZER_OUTPUT_NONE;
+ break;
+ }
+ if (resizer->resizer_a.output != RESIZER_OUTPUT_NONE)
+ return -EBUSY;
+ resizer->resizer_a.output = RESIZER_OUPUT_MEMORY;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+ } else if (&resizer->resizer_b.subdev == sd) {
+ switch (local->index | media_entity_type(remote->entity)) {
+ case RESIZER_PAD_SINK | MEDIA_ENT_T_V4L2_SUBDEV:
+ if (!(flags & MEDIA_LNK_FL_ENABLED)) {
+ resizer->resizer_b.input = RESIZER_INPUT_NONE;
+ break;
+ }
+ if (resizer->resizer_b.input != RESIZER_INPUT_NONE)
+ return -EBUSY;
+ resizer->resizer_b.input = RESIZER_INPUT_CROP_RESIZER;
+ break;
+
+ case RESIZER_PAD_SOURCE | MEDIA_ENT_T_DEVNODE:
+ if (!(flags & MEDIA_LNK_FL_ENABLED)) {
+ resizer->resizer_b.output = RESIZER_OUTPUT_NONE;
+ break;
+ }
+ if (resizer->resizer_b.output != RESIZER_OUTPUT_NONE)
+ return -EBUSY;
+ resizer->resizer_b.output = RESIZER_OUPUT_MEMORY;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+ } else {
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static const struct media_entity_operations resizer_media_ops = {
+ .link_setup = resizer_link_setup,
+};
+
+/*
+ * vpfe_resizer_unregister_entities() - Unregister entity
+ * @vpfe_rsz - pointer to resizer subdevice structure.
+ */
+void vpfe_resizer_unregister_entities(struct vpfe_resizer_device *vpfe_rsz)
+{
+ /* unregister video devices */
+ vpfe_video_unregister(&vpfe_rsz->resizer_a.video_out);
+ vpfe_video_unregister(&vpfe_rsz->resizer_b.video_out);
+
+ /* unregister subdev */
+ v4l2_device_unregister_subdev(&vpfe_rsz->crop_resizer.subdev);
+ v4l2_device_unregister_subdev(&vpfe_rsz->resizer_a.subdev);
+ v4l2_device_unregister_subdev(&vpfe_rsz->resizer_b.subdev);
+ /* cleanup entity */
+ media_entity_cleanup(&vpfe_rsz->crop_resizer.subdev.entity);
+ media_entity_cleanup(&vpfe_rsz->resizer_a.subdev.entity);
+ media_entity_cleanup(&vpfe_rsz->resizer_b.subdev.entity);
+}
+
+/*
+ * vpfe_resizer_register_entities() - Register entity
+ * @resizer - pointer to resizer devive.
+ * @vdev: pointer to v4l2 device structure.
+ */
+int vpfe_resizer_register_entities(struct vpfe_resizer_device *resizer,
+ struct v4l2_device *vdev)
+{
+ struct vpfe_device *vpfe_dev = to_vpfe_device(resizer);
+ unsigned int flags = 0;
+ int ret;
+
+ /* Register the crop resizer subdev */
+ ret = v4l2_device_register_subdev(vdev, &resizer->crop_resizer.subdev);
+ if (ret < 0) {
+ pr_err("Failed to register crop resizer as v4l2-subdev\n");
+ return ret;
+ }
+ /* Register Resizer-A subdev */
+ ret = v4l2_device_register_subdev(vdev, &resizer->resizer_a.subdev);
+ if (ret < 0) {
+ pr_err("Failed to register resizer-a as v4l2-subdev\n");
+ return ret;
+ }
+ /* Register Resizer-B subdev */
+ ret = v4l2_device_register_subdev(vdev, &resizer->resizer_b.subdev);
+ if (ret < 0) {
+ pr_err("Failed to register resizer-b as v4l2-subdev\n");
+ return ret;
+ }
+ /* Register video-out device for resizer-a */
+ ret = vpfe_video_register(&resizer->resizer_a.video_out, vdev);
+ if (ret) {
+ pr_err("Failed to register RSZ-A video-out device\n");
+ goto out_video_out2_register;
+ }
+ resizer->resizer_a.video_out.vpfe_dev = vpfe_dev;
+
+ /* Register video-out device for resizer-b */
+ ret = vpfe_video_register(&resizer->resizer_b.video_out, vdev);
+ if (ret) {
+ pr_err("Failed to register RSZ-B video-out device\n");
+ goto out_video_out2_register;
+ }
+ resizer->resizer_b.video_out.vpfe_dev = vpfe_dev;
+
+ /* create link between Resizer Crop----> Resizer A*/
+ ret = media_entity_create_link(&resizer->crop_resizer.subdev.entity, 1,
+ &resizer->resizer_a.subdev.entity,
+ 0, flags);
+ if (ret < 0)
+ goto out_create_link;
+
+ /* create link between Resizer Crop----> Resizer B*/
+ ret = media_entity_create_link(&resizer->crop_resizer.subdev.entity, 2,
+ &resizer->resizer_b.subdev.entity,
+ 0, flags);
+ if (ret < 0)
+ goto out_create_link;
+
+ /* create link between Resizer A ----> video out */
+ ret = media_entity_create_link(&resizer->resizer_a.subdev.entity, 1,
+ &resizer->resizer_a.video_out.video_dev.entity, 0, flags);
+ if (ret < 0)
+ goto out_create_link;
+
+ /* create link between Resizer B ----> video out */
+ ret = media_entity_create_link(&resizer->resizer_b.subdev.entity, 1,
+ &resizer->resizer_b.video_out.video_dev.entity, 0, flags);
+ if (ret < 0)
+ goto out_create_link;
+
+ return 0;
+
+out_create_link:
+ vpfe_video_unregister(&resizer->resizer_b.video_out);
+out_video_out2_register:
+ vpfe_video_unregister(&resizer->resizer_a.video_out);
+ v4l2_device_unregister_subdev(&resizer->crop_resizer.subdev);
+ v4l2_device_unregister_subdev(&resizer->resizer_a.subdev);
+ v4l2_device_unregister_subdev(&resizer->resizer_b.subdev);
+ media_entity_cleanup(&resizer->crop_resizer.subdev.entity);
+ media_entity_cleanup(&resizer->resizer_a.subdev.entity);
+ media_entity_cleanup(&resizer->resizer_b.subdev.entity);
+ return ret;
+}
+
+/*
+ * vpfe_resizer_init() - resizer device initialization.
+ * @vpfe_rsz - pointer to resizer device
+ * @pdev: platform device pointer.
+ */
+int vpfe_resizer_init(struct vpfe_resizer_device *vpfe_rsz,
+ struct platform_device *pdev)
+{
+ struct v4l2_subdev *sd = &vpfe_rsz->crop_resizer.subdev;
+ struct media_pad *pads = &vpfe_rsz->crop_resizer.pads[0];
+ struct media_entity *me = &sd->entity;
+ static resource_size_t res_len;
+ struct resource *res;
+ int ret;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 5);
+ if (!res)
+ return -ENOENT;
+
+ res_len = resource_size(res);
+ res = request_mem_region(res->start, res_len, res->name);
+ if (!res)
+ return -EBUSY;
+
+ vpfe_rsz->base_addr = ioremap_nocache(res->start, res_len);
+ if (!vpfe_rsz->base_addr)
+ return -EBUSY;
+
+ v4l2_subdev_init(sd, &resizer_v4l2_ops);
+ sd->internal_ops = &resizer_v4l2_internal_ops;
+ strlcpy(sd->name, "DAVINCI RESIZER CROP", sizeof(sd->name));
+ sd->grp_id = 1 << 16; /* group ID for davinci subdevs */
+ v4l2_set_subdevdata(sd, vpfe_rsz);
+ sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+
+ pads[RESIZER_CROP_PAD_SINK].flags = MEDIA_PAD_FL_SINK;
+ pads[RESIZER_CROP_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE;
+ pads[RESIZER_CROP_PAD_SOURCE2].flags = MEDIA_PAD_FL_SOURCE;
+
+ vpfe_rsz->crop_resizer.input = RESIZER_CROP_INPUT_NONE;
+ vpfe_rsz->crop_resizer.output = RESIZER_CROP_OUTPUT_NONE;
+ vpfe_rsz->crop_resizer.output2 = RESIZER_CROP_OUTPUT_NONE;
+ vpfe_rsz->crop_resizer.rsz_device = vpfe_rsz;
+ me->ops = &resizer_media_ops;
+ ret = media_entity_init(me, RESIZER_CROP_PADS_NUM, pads, 0);
+ if (ret)
+ return ret;
+
+ sd = &vpfe_rsz->resizer_a.subdev;
+ pads = &vpfe_rsz->resizer_a.pads[0];
+ me = &sd->entity;
+
+ v4l2_subdev_init(sd, &resizer_v4l2_ops);
+ sd->internal_ops = &resizer_v4l2_internal_ops;
+ strlcpy(sd->name, "DAVINCI RESIZER A", sizeof(sd->name));
+ sd->grp_id = 1 << 16; /* group ID for davinci subdevs */
+ v4l2_set_subdevdata(sd, vpfe_rsz);
+ sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+
+ pads[RESIZER_PAD_SINK].flags = MEDIA_PAD_FL_SINK;
+ pads[RESIZER_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE;
+
+ vpfe_rsz->resizer_a.input = RESIZER_INPUT_NONE;
+ vpfe_rsz->resizer_a.output = RESIZER_OUTPUT_NONE;
+ vpfe_rsz->resizer_a.rsz_device = vpfe_rsz;
+ me->ops = &resizer_media_ops;
+ ret = media_entity_init(me, RESIZER_PADS_NUM, pads, 0);
+ if (ret)
+ return ret;
+
+ sd = &vpfe_rsz->resizer_b.subdev;
+ pads = &vpfe_rsz->resizer_b.pads[0];
+ me = &sd->entity;
+
+ v4l2_subdev_init(sd, &resizer_v4l2_ops);
+ sd->internal_ops = &resizer_v4l2_internal_ops;
+ strlcpy(sd->name, "DAVINCI RESIZER B", sizeof(sd->name));
+ sd->grp_id = 1 << 16; /* group ID for davinci subdevs */
+ v4l2_set_subdevdata(sd, vpfe_rsz);
+ sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+
+ pads[RESIZER_PAD_SINK].flags = MEDIA_PAD_FL_SINK;
+ pads[RESIZER_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE;
+
+ vpfe_rsz->resizer_b.input = RESIZER_INPUT_NONE;
+ vpfe_rsz->resizer_b.output = RESIZER_OUTPUT_NONE;
+ vpfe_rsz->resizer_b.rsz_device = vpfe_rsz;
+ me->ops = &resizer_media_ops;
+ ret = media_entity_init(me, RESIZER_PADS_NUM, pads, 0);
+ if (ret)
+ return ret;
+
+ vpfe_rsz->resizer_a.video_out.ops = &resizer_a_video_ops;
+ vpfe_rsz->resizer_a.video_out.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ ret = vpfe_video_init(&vpfe_rsz->resizer_a.video_out, "RSZ-A");
+ if (ret) {
+ pr_err("Failed to init RSZ video-out device\n");
+ return ret;
+ }
+ vpfe_rsz->resizer_b.video_out.ops = &resizer_b_video_ops;
+ vpfe_rsz->resizer_b.video_out.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ ret = vpfe_video_init(&vpfe_rsz->resizer_b.video_out, "RSZ-B");
+ if (ret) {
+ pr_err("Failed to init RSZ video-out2 device\n");
+ return ret;
+ }
+ memset(&vpfe_rsz->config, 0, sizeof(struct resizer_params));
+
+ return 0;
+}
+
+void
+vpfe_resizer_cleanup(struct vpfe_resizer_device *vpfe_rsz,
+ struct platform_device *pdev)
+{
+ struct resource *res;
+
+ iounmap(vpfe_rsz->base_addr);
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 5);
+ if (res)
+ release_mem_region(res->start,
+ resource_size(res));
+}
diff --git a/drivers/staging/media/davinci_vpfe/dm365_resizer.h b/drivers/staging/media/davinci_vpfe/dm365_resizer.h
new file mode 100644
index 000000000..93b0f4403
--- /dev/null
+++ b/drivers/staging/media/davinci_vpfe/dm365_resizer.h
@@ -0,0 +1,244 @@
+/*
+ * Copyright (C) 2012 Texas Instruments 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 version 2.
+ *
+ * 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
+ *
+ * Contributors:
+ * Manjunath Hadli <manjunath.hadli@ti.com>
+ * Prabhakar Lad <prabhakar.lad@ti.com>
+ */
+
+#ifndef _DAVINCI_VPFE_DM365_RESIZER_H
+#define _DAVINCI_VPFE_DM365_RESIZER_H
+
+enum resizer_oper_mode {
+ RESIZER_MODE_CONTINIOUS = 0,
+ RESIZER_MODE_ONE_SHOT = 1,
+};
+
+struct f_div_pass {
+ unsigned int o_hsz;
+ unsigned int i_hps;
+ unsigned int h_phs;
+ unsigned int src_hps;
+ unsigned int src_hsz;
+};
+
+#define MAX_PASSES 2
+
+struct f_div_param {
+ unsigned char en;
+ unsigned int num_passes;
+ struct f_div_pass pass[MAX_PASSES];
+};
+
+/* Resizer Rescale Parameters*/
+struct resizer_scale_param {
+ bool h_flip;
+ bool v_flip;
+ bool cen;
+ bool yen;
+ unsigned short i_vps;
+ unsigned short i_hps;
+ unsigned short o_vsz;
+ unsigned short o_hsz;
+ unsigned short v_phs_y;
+ unsigned short v_phs_c;
+ unsigned short v_dif;
+ /* resize method - Luminance */
+ enum vpfe_rsz_intp_t v_typ_y;
+ /* resize method - Chrominance */
+ enum vpfe_rsz_intp_t v_typ_c;
+ /* vertical lpf intensity - Luminance */
+ unsigned char v_lpf_int_y;
+ /* vertical lpf intensity - Chrominance */
+ unsigned char v_lpf_int_c;
+ unsigned short h_phs;
+ unsigned short h_dif;
+ /* resize method - Luminance */
+ enum vpfe_rsz_intp_t h_typ_y;
+ /* resize method - Chrominance */
+ enum vpfe_rsz_intp_t h_typ_c;
+ /* horizontal lpf intensity - Luminance */
+ unsigned char h_lpf_int_y;
+ /* horizontal lpf intensity - Chrominance */
+ unsigned char h_lpf_int_c;
+ bool dscale_en;
+ enum vpfe_rsz_down_scale_ave_sz h_dscale_ave_sz;
+ enum vpfe_rsz_down_scale_ave_sz v_dscale_ave_sz;
+ /* store the calculated frame division parameter */
+ struct f_div_param f_div;
+};
+
+enum resizer_rgb_t {
+ OUTPUT_32BIT,
+ OUTPUT_16BIT
+};
+
+enum resizer_rgb_msk_t {
+ NOMASK = 0,
+ MASKLAST2 = 1,
+};
+
+/* Resizer RGB Conversion Parameters */
+struct resizer_rgb {
+ bool rgb_en;
+ enum resizer_rgb_t rgb_typ;
+ enum resizer_rgb_msk_t rgb_msk0;
+ enum resizer_rgb_msk_t rgb_msk1;
+ unsigned int rgb_alpha_val;
+};
+
+/* Resizer External Memory Parameters */
+struct rsz_ext_mem_param {
+ unsigned int rsz_sdr_oft_y;
+ unsigned int rsz_sdr_ptr_s_y;
+ unsigned int rsz_sdr_ptr_e_y;
+ unsigned int rsz_sdr_oft_c;
+ unsigned int rsz_sdr_ptr_s_c;
+ unsigned int rsz_sdr_ptr_e_c;
+ /* offset to be added to buffer start when flipping for y/ycbcr */
+ unsigned int flip_ofst_y;
+ /* offset to be added to buffer start when flipping for c */
+ unsigned int flip_ofst_c;
+ /* c offset for YUV 420SP */
+ unsigned int c_offset;
+ /* User Defined Y offset for YUV 420SP or YUV420ILE data */
+ unsigned int user_y_ofst;
+ /* User Defined C offset for YUV 420SP data */
+ unsigned int user_c_ofst;
+};
+
+enum rsz_data_source {
+ IPIPE_DATA,
+ IPIPEIF_DATA
+};
+
+enum rsz_src_img_fmt {
+ RSZ_IMG_422,
+ RSZ_IMG_420
+};
+
+enum rsz_dpaths_bypass_t {
+ BYPASS_OFF = 0,
+ BYPASS_ON = 1,
+};
+
+struct rsz_common_params {
+ unsigned int vps;
+ unsigned int vsz;
+ unsigned int hps;
+ unsigned int hsz;
+ /* 420 or 422 */
+ enum rsz_src_img_fmt src_img_fmt;
+ /* Y or C when src_fmt is 420, 0 - y, 1 - c */
+ unsigned char y_c;
+ /* flip raw or ycbcr */
+ unsigned char raw_flip;
+ /* IPIPE or IPIPEIF data */
+ enum rsz_data_source source;
+ enum rsz_dpaths_bypass_t passthrough;
+ unsigned char yuv_y_min;
+ unsigned char yuv_y_max;
+ unsigned char yuv_c_min;
+ unsigned char yuv_c_max;
+ bool rsz_seq_crv;
+ enum vpfe_chr_pos out_chr_pos;
+};
+
+struct resizer_params {
+ enum resizer_oper_mode oper_mode;
+ struct rsz_common_params rsz_common;
+ struct resizer_scale_param rsz_rsc_param[2];
+ struct resizer_rgb rsz2rgb[2];
+ struct rsz_ext_mem_param ext_mem_param[2];
+ bool rsz_en[2];
+ struct vpfe_rsz_config_params user_config;
+};
+
+#define ENABLE 1
+#define DISABLE (!ENABLE)
+
+#define RESIZER_CROP_PAD_SINK 0
+#define RESIZER_CROP_PAD_SOURCE 1
+#define RESIZER_CROP_PAD_SOURCE2 2
+
+#define RESIZER_CROP_PADS_NUM 3
+
+enum resizer_crop_input_entity {
+ RESIZER_CROP_INPUT_NONE = 0,
+ RESIZER_CROP_INPUT_IPIPEIF = 1,
+ RESIZER_CROP_INPUT_IPIPE = 2,
+};
+
+enum resizer_crop_output_entity {
+ RESIZER_CROP_OUTPUT_NONE,
+ RESIZER_A,
+ RESIZER_B,
+};
+
+struct dm365_crop_resizer_device {
+ struct v4l2_subdev subdev;
+ struct media_pad pads[RESIZER_CROP_PADS_NUM];
+ struct v4l2_mbus_framefmt formats[RESIZER_CROP_PADS_NUM];
+ enum resizer_crop_input_entity input;
+ enum resizer_crop_output_entity output;
+ enum resizer_crop_output_entity output2;
+ struct vpfe_resizer_device *rsz_device;
+};
+
+#define RESIZER_PAD_SINK 0
+#define RESIZER_PAD_SOURCE 1
+
+#define RESIZER_PADS_NUM 2
+
+enum resizer_input_entity {
+ RESIZER_INPUT_NONE = 0,
+ RESIZER_INPUT_CROP_RESIZER = 1,
+};
+
+enum resizer_output_entity {
+ RESIZER_OUTPUT_NONE = 0,
+ RESIZER_OUPUT_MEMORY = 1,
+};
+
+struct dm365_resizer_device {
+ struct v4l2_subdev subdev;
+ struct media_pad pads[RESIZER_PADS_NUM];
+ struct v4l2_mbus_framefmt formats[RESIZER_PADS_NUM];
+ enum resizer_input_entity input;
+ enum resizer_output_entity output;
+ struct vpfe_video_device video_out;
+ struct vpfe_resizer_device *rsz_device;
+};
+
+struct vpfe_resizer_device {
+ struct dm365_crop_resizer_device crop_resizer;
+ struct dm365_resizer_device resizer_a;
+ struct dm365_resizer_device resizer_b;
+ struct resizer_params config;
+ void __iomem *base_addr;
+};
+
+int vpfe_resizer_init(struct vpfe_resizer_device *vpfe_rsz,
+ struct platform_device *pdev);
+int vpfe_resizer_register_entities(struct vpfe_resizer_device *vpfe_rsz,
+ struct v4l2_device *v4l2_dev);
+void vpfe_resizer_unregister_entities(struct vpfe_resizer_device *vpfe_rsz);
+void vpfe_resizer_cleanup(struct vpfe_resizer_device *vpfe_rsz,
+ struct platform_device *pdev);
+void vpfe_resizer_buffer_isr(struct vpfe_resizer_device *resizer);
+void vpfe_resizer_dma_isr(struct vpfe_resizer_device *resizer);
+
+#endif /* _DAVINCI_VPFE_DM365_RESIZER_H */
diff --git a/drivers/staging/media/davinci_vpfe/vpfe.h b/drivers/staging/media/davinci_vpfe/vpfe.h
new file mode 100644
index 000000000..0587bc52a
--- /dev/null
+++ b/drivers/staging/media/davinci_vpfe/vpfe.h
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2012 Texas Instruments 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 version 2.
+ *
+ * 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
+ *
+ * Contributors:
+ * Manjunath Hadli <manjunath.hadli@ti.com>
+ * Prabhakar Lad <prabhakar.lad@ti.com>
+ */
+
+#ifndef _VPFE_H
+#define _VPFE_H
+
+#ifdef __KERNEL__
+#include <linux/v4l2-subdev.h>
+#include <linux/clk.h>
+#include <linux/i2c.h>
+
+#include <media/davinci/vpfe_types.h>
+
+#define CAPTURE_DRV_NAME "vpfe-capture"
+
+struct vpfe_route {
+ __u32 input;
+ __u32 output;
+};
+
+enum vpfe_subdev_id {
+ VPFE_SUBDEV_TVP5146 = 1,
+ VPFE_SUBDEV_MT9T031 = 2,
+ VPFE_SUBDEV_TVP7002 = 3,
+ VPFE_SUBDEV_MT9P031 = 4,
+};
+
+struct vpfe_ext_subdev_info {
+ /* v4l2 subdev */
+ struct v4l2_subdev *subdev;
+ /* Sub device module name */
+ char module_name[32];
+ /* Sub device group id */
+ int grp_id;
+ /* Number of inputs supported */
+ int num_inputs;
+ /* inputs available at the sub device */
+ struct v4l2_input *inputs;
+ /* Sub dev routing information for each input */
+ struct vpfe_route *routes;
+ /* ccdc bus/interface configuration */
+ struct vpfe_hw_if_param ccdc_if_params;
+ /* i2c subdevice board info */
+ struct i2c_board_info board_info;
+ /* Is this a camera sub device ? */
+ unsigned is_camera:1;
+ /* check if sub dev supports routing */
+ unsigned can_route:1;
+ /* registered ? */
+ unsigned registered:1;
+};
+
+struct vpfe_config {
+ /* Number of sub devices connected to vpfe */
+ int num_subdevs;
+ /* information about each subdev */
+ struct vpfe_ext_subdev_info *sub_devs;
+ /* evm card info */
+ char *card_name;
+ /* setup function for the input path */
+ int (*setup_input)(enum vpfe_subdev_id id);
+ /* number of clocks */
+ int num_clocks;
+ /* clocks used for vpfe capture */
+ char *clocks[];
+};
+#endif
+#endif
diff --git a/drivers/staging/media/davinci_vpfe/vpfe_mc_capture.c b/drivers/staging/media/davinci_vpfe/vpfe_mc_capture.c
new file mode 100644
index 000000000..57426199a
--- /dev/null
+++ b/drivers/staging/media/davinci_vpfe/vpfe_mc_capture.c
@@ -0,0 +1,716 @@
+/*
+ * Copyright (C) 2012 Texas Instruments 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 version 2.
+ *
+ * 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
+ *
+ * Contributors:
+ * Manjunath Hadli <manjunath.hadli@ti.com>
+ * Prabhakar Lad <prabhakar.lad@ti.com>
+ *
+ *
+ * Driver name : VPFE Capture driver
+ * VPFE Capture driver allows applications to capture and stream video
+ * frames on DaVinci SoCs (DM6446, DM355 etc) from a YUV source such as
+ * TVP5146 or Raw Bayer RGB image data from an image sensor
+ * such as Microns' MT9T001, MT9T031 etc.
+ *
+ * These SoCs have, in common, a Video Processing Subsystem (VPSS) that
+ * consists of a Video Processing Front End (VPFE) for capturing
+ * video/raw image data and Video Processing Back End (VPBE) for displaying
+ * YUV data through an in-built analog encoder or Digital LCD port. This
+ * driver is for capture through VPFE. A typical EVM using these SoCs have
+ * following high level configuration.
+ *
+ * decoder(TVP5146/ YUV/
+ * MT9T001) --> Raw Bayer RGB ---> MUX -> VPFE (CCDC/ISIF)
+ * data input | |
+ * V |
+ * SDRAM |
+ * V
+ * Image Processor
+ * |
+ * V
+ * SDRAM
+ * The data flow happens from a decoder connected to the VPFE over a
+ * YUV embedded (BT.656/BT.1120) or separate sync or raw bayer rgb interface
+ * and to the input of VPFE through an optional MUX (if more inputs are
+ * to be interfaced on the EVM). The input data is first passed through
+ * CCDC (CCD Controller, a.k.a Image Sensor Interface, ISIF). The CCDC
+ * does very little or no processing on YUV data and does pre-process Raw
+ * Bayer RGB data through modules such as Defect Pixel Correction (DFC)
+ * Color Space Conversion (CSC), data gain/offset etc. After this, data
+ * can be written to SDRAM or can be connected to the image processing
+ * block such as IPIPE (on DM355/DM365 only).
+ *
+ * Features supported
+ * - MMAP IO
+ * - USERPTR IO
+ * - Capture using TVP5146 over BT.656
+ * - Support for interfacing decoders using sub device model
+ * - Work with DM365 or DM355 or DM6446 CCDC to do Raw Bayer
+ * RGB/YUV data capture to SDRAM.
+ * - Chaining of Image Processor
+ * - SINGLE-SHOT mode
+ */
+
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+
+#include "vpfe.h"
+#include "vpfe_mc_capture.h"
+
+static bool debug;
+static bool interface;
+
+module_param(interface, bool, S_IRUGO);
+module_param(debug, bool, 0644);
+
+/**
+ * VPFE capture can be used for capturing video such as from TVP5146 or TVP7002
+ * and for capture raw bayer data from camera sensors such as mt9p031. At this
+ * point there is problem in co-existence of mt9p031 and tvp5146 due to i2c
+ * address collision. So set the variable below from bootargs to do either video
+ * capture or camera capture.
+ * interface = 0 - video capture (from TVP514x or such),
+ * interface = 1 - Camera capture (from mt9p031 or such)
+ * Re-visit this when we fix the co-existence issue
+ */
+MODULE_PARM_DESC(interface, "interface 0-1 (default:0)");
+MODULE_PARM_DESC(debug, "Debug level 0-1");
+
+MODULE_DESCRIPTION("VPFE Video for Linux Capture Driver");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Texas Instruments");
+
+/* map mbus_fmt to pixelformat */
+void mbus_to_pix(const struct v4l2_mbus_framefmt *mbus,
+ struct v4l2_pix_format *pix)
+{
+ switch (mbus->code) {
+ case MEDIA_BUS_FMT_UYVY8_2X8:
+ pix->pixelformat = V4L2_PIX_FMT_UYVY;
+ pix->bytesperline = pix->width * 2;
+ break;
+
+ case MEDIA_BUS_FMT_YUYV8_2X8:
+ pix->pixelformat = V4L2_PIX_FMT_YUYV;
+ pix->bytesperline = pix->width * 2;
+ break;
+
+ case MEDIA_BUS_FMT_YUYV10_1X20:
+ pix->pixelformat = V4L2_PIX_FMT_UYVY;
+ pix->bytesperline = pix->width * 2;
+ break;
+
+ case MEDIA_BUS_FMT_SGRBG12_1X12:
+ pix->pixelformat = V4L2_PIX_FMT_SBGGR16;
+ pix->bytesperline = pix->width * 2;
+ break;
+
+ case MEDIA_BUS_FMT_SGRBG10_DPCM8_1X8:
+ pix->pixelformat = V4L2_PIX_FMT_SGRBG10DPCM8;
+ pix->bytesperline = pix->width;
+ break;
+
+ case MEDIA_BUS_FMT_SGRBG10_ALAW8_1X8:
+ pix->pixelformat = V4L2_PIX_FMT_SGRBG10ALAW8;
+ pix->bytesperline = pix->width;
+ break;
+
+ case MEDIA_BUS_FMT_YDYUYDYV8_1X16:
+ pix->pixelformat = V4L2_PIX_FMT_NV12;
+ pix->bytesperline = pix->width;
+ break;
+
+ case MEDIA_BUS_FMT_Y8_1X8:
+ pix->pixelformat = V4L2_PIX_FMT_GREY;
+ pix->bytesperline = pix->width;
+ break;
+
+ case MEDIA_BUS_FMT_UV8_1X8:
+ pix->pixelformat = V4L2_PIX_FMT_UV8;
+ pix->bytesperline = pix->width;
+ break;
+
+ default:
+ pr_err("Invalid mbus code set\n");
+ }
+ /* pitch should be 32 bytes aligned */
+ pix->bytesperline = ALIGN(pix->bytesperline, 32);
+ if (pix->pixelformat == V4L2_PIX_FMT_NV12)
+ pix->sizeimage = pix->bytesperline * pix->height +
+ ((pix->bytesperline * pix->height) >> 1);
+ else
+ pix->sizeimage = pix->bytesperline * pix->height;
+}
+
+/* ISR for VINT0*/
+static irqreturn_t vpfe_isr(int irq, void *dev_id)
+{
+ struct vpfe_device *vpfe_dev = dev_id;
+
+ v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_isr\n");
+ vpfe_isif_buffer_isr(&vpfe_dev->vpfe_isif);
+ vpfe_resizer_buffer_isr(&vpfe_dev->vpfe_resizer);
+ return IRQ_HANDLED;
+}
+
+/* vpfe_vdint1_isr() - isr handler for VINT1 interrupt */
+static irqreturn_t vpfe_vdint1_isr(int irq, void *dev_id)
+{
+ struct vpfe_device *vpfe_dev = dev_id;
+
+ v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_vdint1_isr\n");
+ vpfe_isif_vidint1_isr(&vpfe_dev->vpfe_isif);
+ return IRQ_HANDLED;
+}
+
+/* vpfe_imp_dma_isr() - ISR for ipipe dma completion */
+static irqreturn_t vpfe_imp_dma_isr(int irq, void *dev_id)
+{
+ struct vpfe_device *vpfe_dev = dev_id;
+
+ v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_imp_dma_isr\n");
+ vpfe_ipipeif_ss_buffer_isr(&vpfe_dev->vpfe_ipipeif);
+ vpfe_resizer_dma_isr(&vpfe_dev->vpfe_resizer);
+ return IRQ_HANDLED;
+}
+
+/*
+ * vpfe_disable_clock() - Disable clocks for vpfe capture driver
+ * @vpfe_dev - ptr to vpfe capture device
+ *
+ * Disables clocks defined in vpfe configuration. The function
+ * assumes that at least one clock is to be defined which is
+ * true as of now.
+ */
+static void vpfe_disable_clock(struct vpfe_device *vpfe_dev)
+{
+ struct vpfe_config *vpfe_cfg = vpfe_dev->cfg;
+ int i;
+
+ for (i = 0; i < vpfe_cfg->num_clocks; i++) {
+ clk_disable_unprepare(vpfe_dev->clks[i]);
+ clk_put(vpfe_dev->clks[i]);
+ }
+ kzfree(vpfe_dev->clks);
+ v4l2_info(vpfe_dev->pdev->driver, "vpfe capture clocks disabled\n");
+}
+
+/*
+ * vpfe_enable_clock() - Enable clocks for vpfe capture driver
+ * @vpfe_dev - ptr to vpfe capture device
+ *
+ * Enables clocks defined in vpfe configuration. The function
+ * assumes that at least one clock is to be defined which is
+ * true as of now.
+ */
+static int vpfe_enable_clock(struct vpfe_device *vpfe_dev)
+{
+ struct vpfe_config *vpfe_cfg = vpfe_dev->cfg;
+ int ret = -EFAULT;
+ int i;
+
+ if (!vpfe_cfg->num_clocks)
+ return 0;
+
+ vpfe_dev->clks = kcalloc(vpfe_cfg->num_clocks,
+ sizeof(struct clock *), GFP_KERNEL);
+ if (vpfe_dev->clks == NULL)
+ return -ENOMEM;
+
+ for (i = 0; i < vpfe_cfg->num_clocks; i++) {
+ if (vpfe_cfg->clocks[i] == NULL) {
+ v4l2_err(vpfe_dev->pdev->driver,
+ "clock %s is not defined in vpfe config\n",
+ vpfe_cfg->clocks[i]);
+ goto out;
+ }
+
+ vpfe_dev->clks[i] =
+ clk_get(vpfe_dev->pdev, vpfe_cfg->clocks[i]);
+ if (IS_ERR(vpfe_dev->clks[i])) {
+ v4l2_err(vpfe_dev->pdev->driver,
+ "Failed to get clock %s\n",
+ vpfe_cfg->clocks[i]);
+ goto out;
+ }
+
+ if (clk_prepare_enable(vpfe_dev->clks[i])) {
+ v4l2_err(vpfe_dev->pdev->driver,
+ "vpfe clock %s not enabled\n",
+ vpfe_cfg->clocks[i]);
+ goto out;
+ }
+
+ v4l2_info(vpfe_dev->pdev->driver, "vpss clock %s enabled",
+ vpfe_cfg->clocks[i]);
+ }
+
+ return 0;
+out:
+ for (i = 0; i < vpfe_cfg->num_clocks; i++)
+ if (!IS_ERR(vpfe_dev->clks[i])) {
+ clk_disable_unprepare(vpfe_dev->clks[i]);
+ clk_put(vpfe_dev->clks[i]);
+ }
+
+ v4l2_err(vpfe_dev->pdev->driver, "Failed to enable clocks\n");
+ kzfree(vpfe_dev->clks);
+
+ return ret;
+}
+
+/*
+ * vpfe_detach_irq() - Detach IRQs for vpfe capture driver
+ * @vpfe_dev - ptr to vpfe capture device
+ *
+ * Detach all IRQs defined in vpfe configuration.
+ */
+static void vpfe_detach_irq(struct vpfe_device *vpfe_dev)
+{
+ free_irq(vpfe_dev->ccdc_irq0, vpfe_dev);
+ free_irq(vpfe_dev->ccdc_irq1, vpfe_dev);
+ free_irq(vpfe_dev->imp_dma_irq, vpfe_dev);
+}
+
+/*
+ * vpfe_attach_irq() - Attach IRQs for vpfe capture driver
+ * @vpfe_dev - ptr to vpfe capture device
+ *
+ * Attach all IRQs defined in vpfe configuration.
+ */
+static int vpfe_attach_irq(struct vpfe_device *vpfe_dev)
+{
+ int ret = 0;
+
+ ret = request_irq(vpfe_dev->ccdc_irq0, vpfe_isr, 0,
+ "vpfe_capture0", vpfe_dev);
+ if (ret < 0) {
+ v4l2_err(&vpfe_dev->v4l2_dev,
+ "Error: requesting VINT0 interrupt\n");
+ return ret;
+ }
+
+ ret = request_irq(vpfe_dev->ccdc_irq1, vpfe_vdint1_isr, 0,
+ "vpfe_capture1", vpfe_dev);
+ if (ret < 0) {
+ v4l2_err(&vpfe_dev->v4l2_dev,
+ "Error: requesting VINT1 interrupt\n");
+ free_irq(vpfe_dev->ccdc_irq0, vpfe_dev);
+ return ret;
+ }
+
+ ret = request_irq(vpfe_dev->imp_dma_irq, vpfe_imp_dma_isr,
+ 0, "Imp_Sdram_Irq", vpfe_dev);
+ if (ret < 0) {
+ v4l2_err(&vpfe_dev->v4l2_dev,
+ "Error: requesting IMP IRQ interrupt\n");
+ free_irq(vpfe_dev->ccdc_irq1, vpfe_dev);
+ free_irq(vpfe_dev->ccdc_irq0, vpfe_dev);
+ return ret;
+ }
+
+ return 0;
+}
+
+/*
+ * register_i2c_devices() - register all i2c v4l2 subdevs
+ * @vpfe_dev - ptr to vpfe capture device
+ *
+ * register all i2c v4l2 subdevs
+ */
+static int register_i2c_devices(struct vpfe_device *vpfe_dev)
+{
+ struct vpfe_ext_subdev_info *sdinfo;
+ struct vpfe_config *vpfe_cfg;
+ struct i2c_adapter *i2c_adap;
+ unsigned int num_subdevs;
+ int ret;
+ int i;
+ int k;
+
+ vpfe_cfg = vpfe_dev->cfg;
+ i2c_adap = i2c_get_adapter(1);
+ num_subdevs = vpfe_cfg->num_subdevs;
+ vpfe_dev->sd =
+ kcalloc(num_subdevs, sizeof(struct v4l2_subdev *),
+ GFP_KERNEL);
+ if (vpfe_dev->sd == NULL)
+ return -ENOMEM;
+
+ for (i = 0, k = 0; i < num_subdevs; i++) {
+ sdinfo = &vpfe_cfg->sub_devs[i];
+ /*
+ * register subdevices based on interface setting. Currently
+ * tvp5146 and mt9p031 cannot co-exists due to i2c address
+ * conflicts. So only one of them is registered. Re-visit this
+ * once we have support for i2c switch handling in i2c driver
+ * framework
+ */
+ if (interface == sdinfo->is_camera) {
+ /* setup input path */
+ if (vpfe_cfg->setup_input &&
+ vpfe_cfg->setup_input(sdinfo->grp_id) < 0) {
+ ret = -EFAULT;
+ v4l2_info(&vpfe_dev->v4l2_dev,
+ "could not setup input for %s\n",
+ sdinfo->module_name);
+ goto probe_sd_out;
+ }
+ /* Load up the subdevice */
+ vpfe_dev->sd[k] =
+ v4l2_i2c_new_subdev_board(&vpfe_dev->v4l2_dev,
+ i2c_adap, &sdinfo->board_info,
+ NULL);
+ if (vpfe_dev->sd[k]) {
+ v4l2_info(&vpfe_dev->v4l2_dev,
+ "v4l2 sub device %s registered\n",
+ sdinfo->module_name);
+
+ vpfe_dev->sd[k]->grp_id = sdinfo->grp_id;
+ k++;
+
+ sdinfo->registered = 1;
+ }
+ } else {
+ v4l2_info(&vpfe_dev->v4l2_dev,
+ "v4l2 sub device %s is not registered\n",
+ sdinfo->module_name);
+ }
+ }
+ vpfe_dev->num_ext_subdevs = k;
+
+ return 0;
+
+probe_sd_out:
+ kzfree(vpfe_dev->sd);
+
+ return ret;
+}
+
+/*
+ * vpfe_register_entities() - register all v4l2 subdevs and media entities
+ * @vpfe_dev - ptr to vpfe capture device
+ *
+ * register all v4l2 subdevs, media entities, and creates links
+ * between entities
+ */
+static int vpfe_register_entities(struct vpfe_device *vpfe_dev)
+{
+ unsigned int flags = 0;
+ int ret;
+ int i;
+
+ /* register i2c devices first */
+ ret = register_i2c_devices(vpfe_dev);
+ if (ret)
+ return ret;
+
+ /* register rest of the sub-devs */
+ ret = vpfe_isif_register_entities(&vpfe_dev->vpfe_isif,
+ &vpfe_dev->v4l2_dev);
+ if (ret)
+ return ret;
+
+ ret = vpfe_ipipeif_register_entities(&vpfe_dev->vpfe_ipipeif,
+ &vpfe_dev->v4l2_dev);
+ if (ret)
+ goto out_isif_register;
+
+ ret = vpfe_ipipe_register_entities(&vpfe_dev->vpfe_ipipe,
+ &vpfe_dev->v4l2_dev);
+ if (ret)
+ goto out_ipipeif_register;
+
+ ret = vpfe_resizer_register_entities(&vpfe_dev->vpfe_resizer,
+ &vpfe_dev->v4l2_dev);
+ if (ret)
+ goto out_ipipe_register;
+
+ /* create links now, starting with external(i2c) entities */
+ for (i = 0; i < vpfe_dev->num_ext_subdevs; i++)
+ /* if entity has no pads (ex: amplifier),
+ cant establish link */
+ if (vpfe_dev->sd[i]->entity.num_pads) {
+ ret = media_entity_create_link(&vpfe_dev->sd[i]->entity,
+ 0, &vpfe_dev->vpfe_isif.subdev.entity,
+ 0, flags);
+ if (ret < 0)
+ goto out_resizer_register;
+ }
+
+ ret = media_entity_create_link(&vpfe_dev->vpfe_isif.subdev.entity, 1,
+ &vpfe_dev->vpfe_ipipeif.subdev.entity,
+ 0, flags);
+ if (ret < 0)
+ goto out_resizer_register;
+
+ ret = media_entity_create_link(&vpfe_dev->vpfe_ipipeif.subdev.entity, 1,
+ &vpfe_dev->vpfe_ipipe.subdev.entity,
+ 0, flags);
+ if (ret < 0)
+ goto out_resizer_register;
+
+ ret = media_entity_create_link(&vpfe_dev->vpfe_ipipe.subdev.entity,
+ 1, &vpfe_dev->vpfe_resizer.crop_resizer.subdev.entity,
+ 0, flags);
+ if (ret < 0)
+ goto out_resizer_register;
+
+ ret = media_entity_create_link(&vpfe_dev->vpfe_ipipeif.subdev.entity, 1,
+ &vpfe_dev->vpfe_resizer.crop_resizer.subdev.entity,
+ 0, flags);
+ if (ret < 0)
+ goto out_resizer_register;
+
+ ret = v4l2_device_register_subdev_nodes(&vpfe_dev->v4l2_dev);
+ if (ret < 0)
+ goto out_resizer_register;
+
+ return 0;
+
+out_resizer_register:
+ vpfe_resizer_unregister_entities(&vpfe_dev->vpfe_resizer);
+out_ipipe_register:
+ vpfe_ipipe_unregister_entities(&vpfe_dev->vpfe_ipipe);
+out_ipipeif_register:
+ vpfe_ipipeif_unregister_entities(&vpfe_dev->vpfe_ipipeif);
+out_isif_register:
+ vpfe_isif_unregister_entities(&vpfe_dev->vpfe_isif);
+
+ return ret;
+}
+
+/*
+ * vpfe_unregister_entities() - unregister all v4l2 subdevs and media entities
+ * @vpfe_dev - ptr to vpfe capture device
+ *
+ * unregister all v4l2 subdevs and media entities
+ */
+static void vpfe_unregister_entities(struct vpfe_device *vpfe_dev)
+{
+ vpfe_isif_unregister_entities(&vpfe_dev->vpfe_isif);
+ vpfe_ipipeif_unregister_entities(&vpfe_dev->vpfe_ipipeif);
+ vpfe_ipipe_unregister_entities(&vpfe_dev->vpfe_ipipe);
+ vpfe_resizer_unregister_entities(&vpfe_dev->vpfe_resizer);
+}
+
+/*
+ * vpfe_cleanup_modules() - cleanup all non-i2c v4l2 subdevs
+ * @vpfe_dev - ptr to vpfe capture device
+ * @pdev - pointer to platform device
+ *
+ * cleanup all v4l2 subdevs
+ */
+static void vpfe_cleanup_modules(struct vpfe_device *vpfe_dev,
+ struct platform_device *pdev)
+{
+ vpfe_isif_cleanup(&vpfe_dev->vpfe_isif, pdev);
+ vpfe_ipipeif_cleanup(&vpfe_dev->vpfe_ipipeif, pdev);
+ vpfe_ipipe_cleanup(&vpfe_dev->vpfe_ipipe, pdev);
+ vpfe_resizer_cleanup(&vpfe_dev->vpfe_resizer, pdev);
+}
+
+/*
+ * vpfe_initialize_modules() - initialize all non-i2c v4l2 subdevs
+ * @vpfe_dev - ptr to vpfe capture device
+ * @pdev - pointer to platform device
+ *
+ * intialize all v4l2 subdevs and media entities
+ */
+static int vpfe_initialize_modules(struct vpfe_device *vpfe_dev,
+ struct platform_device *pdev)
+{
+ int ret;
+
+ ret = vpfe_isif_init(&vpfe_dev->vpfe_isif, pdev);
+ if (ret)
+ return ret;
+
+ ret = vpfe_ipipeif_init(&vpfe_dev->vpfe_ipipeif, pdev);
+ if (ret)
+ goto out_isif_init;
+
+ ret = vpfe_ipipe_init(&vpfe_dev->vpfe_ipipe, pdev);
+ if (ret)
+ goto out_ipipeif_init;
+
+ ret = vpfe_resizer_init(&vpfe_dev->vpfe_resizer, pdev);
+ if (ret)
+ goto out_ipipe_init;
+
+ return 0;
+
+out_ipipe_init:
+ vpfe_ipipe_cleanup(&vpfe_dev->vpfe_ipipe, pdev);
+out_ipipeif_init:
+ vpfe_ipipeif_cleanup(&vpfe_dev->vpfe_ipipeif, pdev);
+out_isif_init:
+ vpfe_isif_cleanup(&vpfe_dev->vpfe_isif, pdev);
+
+ return ret;
+}
+
+/*
+ * vpfe_probe() : vpfe probe function
+ * @pdev: platform device pointer
+ *
+ * This function creates device entries by register itself to the V4L2 driver
+ * and initializes fields of each device objects
+ */
+static int vpfe_probe(struct platform_device *pdev)
+{
+ struct vpfe_device *vpfe_dev;
+ struct resource *res1;
+ int ret = -ENOMEM;
+
+ vpfe_dev = kzalloc(sizeof(*vpfe_dev), GFP_KERNEL);
+ if (!vpfe_dev)
+ return ret;
+
+ if (pdev->dev.platform_data == NULL) {
+ v4l2_err(pdev->dev.driver, "Unable to get vpfe config\n");
+ ret = -ENOENT;
+ goto probe_free_dev_mem;
+ }
+
+ vpfe_dev->cfg = pdev->dev.platform_data;
+ if (vpfe_dev->cfg->card_name == NULL ||
+ vpfe_dev->cfg->sub_devs == NULL) {
+ v4l2_err(pdev->dev.driver, "null ptr in vpfe_cfg\n");
+ ret = -ENOENT;
+ goto probe_free_dev_mem;
+ }
+
+ /* Get VINT0 irq resource */
+ res1 = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+ if (!res1) {
+ v4l2_err(pdev->dev.driver,
+ "Unable to get interrupt for VINT0\n");
+ ret = -ENOENT;
+ goto probe_free_dev_mem;
+ }
+ vpfe_dev->ccdc_irq0 = res1->start;
+
+ /* Get VINT1 irq resource */
+ res1 = platform_get_resource(pdev, IORESOURCE_IRQ, 1);
+ if (!res1) {
+ v4l2_err(pdev->dev.driver,
+ "Unable to get interrupt for VINT1\n");
+ ret = -ENOENT;
+ goto probe_free_dev_mem;
+ }
+ vpfe_dev->ccdc_irq1 = res1->start;
+
+ /* Get DMA irq resource */
+ res1 = platform_get_resource(pdev, IORESOURCE_IRQ, 2);
+ if (!res1) {
+ v4l2_err(pdev->dev.driver,
+ "Unable to get interrupt for DMA\n");
+ ret = -ENOENT;
+ goto probe_free_dev_mem;
+ }
+ vpfe_dev->imp_dma_irq = res1->start;
+
+ vpfe_dev->pdev = &pdev->dev;
+
+ /* enable vpss clocks */
+ ret = vpfe_enable_clock(vpfe_dev);
+ if (ret)
+ goto probe_free_dev_mem;
+
+ ret = vpfe_initialize_modules(vpfe_dev, pdev);
+ if (ret)
+ goto probe_disable_clock;
+
+ vpfe_dev->media_dev.dev = vpfe_dev->pdev;
+ strcpy((char *)&vpfe_dev->media_dev.model, "davinci-media");
+
+ ret = media_device_register(&vpfe_dev->media_dev);
+ if (ret) {
+ v4l2_err(pdev->dev.driver,
+ "Unable to register media device.\n");
+ goto probe_out_entities_cleanup;
+ }
+
+ vpfe_dev->v4l2_dev.mdev = &vpfe_dev->media_dev;
+ ret = v4l2_device_register(&pdev->dev, &vpfe_dev->v4l2_dev);
+ if (ret) {
+ v4l2_err(pdev->dev.driver, "Unable to register v4l2 device.\n");
+ goto probe_out_media_unregister;
+ }
+
+ v4l2_info(&vpfe_dev->v4l2_dev, "v4l2 device registered\n");
+ /* set the driver data in platform device */
+ platform_set_drvdata(pdev, vpfe_dev);
+ /* register subdevs/entities */
+ ret = vpfe_register_entities(vpfe_dev);
+ if (ret)
+ goto probe_out_v4l2_unregister;
+
+ ret = vpfe_attach_irq(vpfe_dev);
+ if (ret)
+ goto probe_out_entities_unregister;
+
+ return 0;
+
+probe_out_entities_unregister:
+ vpfe_unregister_entities(vpfe_dev);
+ kzfree(vpfe_dev->sd);
+probe_out_v4l2_unregister:
+ v4l2_device_unregister(&vpfe_dev->v4l2_dev);
+probe_out_media_unregister:
+ media_device_unregister(&vpfe_dev->media_dev);
+probe_out_entities_cleanup:
+ vpfe_cleanup_modules(vpfe_dev, pdev);
+probe_disable_clock:
+ vpfe_disable_clock(vpfe_dev);
+probe_free_dev_mem:
+ kzfree(vpfe_dev);
+
+ return ret;
+}
+
+/*
+ * vpfe_remove : This function un-registers device from V4L2 driver
+ */
+static int vpfe_remove(struct platform_device *pdev)
+{
+ struct vpfe_device *vpfe_dev = platform_get_drvdata(pdev);
+
+ v4l2_info(pdev->dev.driver, "vpfe_remove\n");
+
+ kzfree(vpfe_dev->sd);
+ vpfe_detach_irq(vpfe_dev);
+ vpfe_unregister_entities(vpfe_dev);
+ vpfe_cleanup_modules(vpfe_dev, pdev);
+ v4l2_device_unregister(&vpfe_dev->v4l2_dev);
+ media_device_unregister(&vpfe_dev->media_dev);
+ vpfe_disable_clock(vpfe_dev);
+ kzfree(vpfe_dev);
+
+ return 0;
+}
+
+static struct platform_driver vpfe_driver = {
+ .driver = {
+ .name = CAPTURE_DRV_NAME,
+ },
+ .probe = vpfe_probe,
+ .remove = vpfe_remove,
+};
+
+module_platform_driver(vpfe_driver);
diff --git a/drivers/staging/media/davinci_vpfe/vpfe_mc_capture.h b/drivers/staging/media/davinci_vpfe/vpfe_mc_capture.h
new file mode 100644
index 000000000..2632a806c
--- /dev/null
+++ b/drivers/staging/media/davinci_vpfe/vpfe_mc_capture.h
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2012 Texas Instruments 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 version 2.
+ *
+ * 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
+ *
+ * Contributors:
+ * Manjunath Hadli <manjunath.hadli@ti.com>
+ * Prabhakar Lad <prabhakar.lad@ti.com>
+ */
+
+#ifndef _DAVINCI_VPFE_MC_CAPTURE_H
+#define _DAVINCI_VPFE_MC_CAPTURE_H
+
+#include "dm365_ipipe.h"
+#include "dm365_ipipeif.h"
+#include "dm365_isif.h"
+#include "dm365_resizer.h"
+#include "vpfe_video.h"
+
+#define VPFE_MAJOR_RELEASE 0
+#define VPFE_MINOR_RELEASE 0
+#define VPFE_BUILD 1
+#define VPFE_CAPTURE_VERSION_CODE ((VPFE_MAJOR_RELEASE << 16) | \
+ (VPFE_MINOR_RELEASE << 8) | \
+ VPFE_BUILD)
+
+/* IPIPE hardware limits */
+#define IPIPE_MAX_OUTPUT_WIDTH_A 2176
+#define IPIPE_MAX_OUTPUT_WIDTH_B 640
+
+/* Based on max resolution supported. QXGA */
+#define IPIPE_MAX_OUTPUT_HEIGHT_A 1536
+/* Based on max resolution supported. VGA */
+#define IPIPE_MAX_OUTPUT_HEIGHT_B 480
+
+#define to_vpfe_device(ptr_module) \
+ container_of(ptr_module, struct vpfe_device, vpfe_##ptr_module)
+#define to_device(ptr_module) \
+ (to_vpfe_device(ptr_module)->dev)
+
+struct vpfe_device {
+ /* external registered sub devices */
+ struct v4l2_subdev **sd;
+ /* number of registered external subdevs */
+ unsigned int num_ext_subdevs;
+ /* vpfe cfg */
+ struct vpfe_config *cfg;
+ /* clock ptrs for vpfe capture */
+ struct clk **clks;
+ /* V4l2 device */
+ struct v4l2_device v4l2_dev;
+ /* parent device */
+ struct device *pdev;
+ /* IRQ number for DMA transfer completion at the image processor */
+ unsigned int imp_dma_irq;
+ /* CCDC IRQs used when CCDC/ISIF output to SDRAM */
+ unsigned int ccdc_irq0;
+ unsigned int ccdc_irq1;
+ /* maximum video memory that is available*/
+ unsigned int video_limit;
+ /* media device */
+ struct media_device media_dev;
+ /* ccdc subdevice */
+ struct vpfe_isif_device vpfe_isif;
+ /* ipipeif subdevice */
+ struct vpfe_ipipeif_device vpfe_ipipeif;
+ /* ipipe subdevice */
+ struct vpfe_ipipe_device vpfe_ipipe;
+ /* resizer subdevice */
+ struct vpfe_resizer_device vpfe_resizer;
+};
+
+/* File handle structure */
+struct vpfe_fh {
+ struct v4l2_fh vfh;
+ struct vpfe_video_device *video;
+ /* Indicates whether this file handle is doing IO */
+ u8 io_allowed;
+};
+
+void mbus_to_pix(const struct v4l2_mbus_framefmt *mbus,
+ struct v4l2_pix_format *pix);
+
+#endif /* _DAVINCI_VPFE_MC_CAPTURE_H */
diff --git a/drivers/staging/media/davinci_vpfe/vpfe_video.c b/drivers/staging/media/davinci_vpfe/vpfe_video.c
new file mode 100644
index 000000000..06d48d5eb
--- /dev/null
+++ b/drivers/staging/media/davinci_vpfe/vpfe_video.c
@@ -0,0 +1,1636 @@
+/*
+ * Copyright (C) 2012 Texas Instruments 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 version 2.
+ *
+ * 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
+ *
+ * Contributors:
+ * Manjunath Hadli <manjunath.hadli@ti.com>
+ * Prabhakar Lad <prabhakar.lad@ti.com>
+ */
+
+#include <linux/module.h>
+#include <linux/slab.h>
+
+#include <media/v4l2-ioctl.h>
+
+#include "vpfe.h"
+#include "vpfe_mc_capture.h"
+
+/* minimum number of buffers needed in cont-mode */
+#define MIN_NUM_BUFFERS 3
+
+static int debug;
+
+/* get v4l2 subdev pointer to external subdev which is active */
+static struct media_entity *vpfe_get_input_entity
+ (struct vpfe_video_device *video)
+{
+ struct vpfe_device *vpfe_dev = video->vpfe_dev;
+ struct media_pad *remote;
+
+ remote = media_entity_remote_pad(&vpfe_dev->vpfe_isif.pads[0]);
+ if (remote == NULL) {
+ pr_err("Invalid media connection to isif/ccdc\n");
+ return NULL;
+ }
+ return remote->entity;
+}
+
+/* updates external subdev(sensor/decoder) which is active */
+static int vpfe_update_current_ext_subdev(struct vpfe_video_device *video)
+{
+ struct vpfe_device *vpfe_dev = video->vpfe_dev;
+ struct vpfe_config *vpfe_cfg;
+ struct v4l2_subdev *subdev;
+ struct media_pad *remote;
+ int i;
+
+ remote = media_entity_remote_pad(&vpfe_dev->vpfe_isif.pads[0]);
+ if (remote == NULL) {
+ pr_err("Invalid media connection to isif/ccdc\n");
+ return -EINVAL;
+ }
+
+ subdev = media_entity_to_v4l2_subdev(remote->entity);
+ vpfe_cfg = vpfe_dev->pdev->platform_data;
+ for (i = 0; i < vpfe_cfg->num_subdevs; i++) {
+ if (!strcmp(vpfe_cfg->sub_devs[i].module_name, subdev->name)) {
+ video->current_ext_subdev = &vpfe_cfg->sub_devs[i];
+ break;
+ }
+ }
+
+ /* if user not linked decoder/sensor to isif/ccdc */
+ if (i == vpfe_cfg->num_subdevs) {
+ pr_err("Invalid media chain connection to isif/ccdc\n");
+ return -EINVAL;
+ }
+ /* find the v4l2 subdev pointer */
+ for (i = 0; i < vpfe_dev->num_ext_subdevs; i++) {
+ if (!strcmp(video->current_ext_subdev->module_name,
+ vpfe_dev->sd[i]->name))
+ video->current_ext_subdev->subdev = vpfe_dev->sd[i];
+ }
+ return 0;
+}
+
+/* get the subdev which is connected to the output video node */
+static struct v4l2_subdev *
+vpfe_video_remote_subdev(struct vpfe_video_device *video, u32 *pad)
+{
+ struct media_pad *remote = media_entity_remote_pad(&video->pad);
+
+ if (remote == NULL || remote->entity->type != MEDIA_ENT_T_V4L2_SUBDEV)
+ return NULL;
+ if (pad)
+ *pad = remote->index;
+ return media_entity_to_v4l2_subdev(remote->entity);
+}
+
+/* get the format set at output pad of the adjacent subdev */
+static int
+__vpfe_video_get_format(struct vpfe_video_device *video,
+ struct v4l2_format *format)
+{
+ struct v4l2_subdev_format fmt;
+ struct v4l2_subdev *subdev;
+ struct media_pad *remote;
+ u32 pad;
+ int ret;
+
+ subdev = vpfe_video_remote_subdev(video, &pad);
+ if (subdev == NULL)
+ return -EINVAL;
+
+ fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE;
+ remote = media_entity_remote_pad(&video->pad);
+ fmt.pad = remote->index;
+
+ ret = v4l2_subdev_call(subdev, pad, get_fmt, NULL, &fmt);
+ if (ret == -ENOIOCTLCMD)
+ return -EINVAL;
+
+ format->type = video->type;
+ /* convert mbus_format to v4l2_format */
+ v4l2_fill_pix_format(&format->fmt.pix, &fmt.format);
+ mbus_to_pix(&fmt.format, &format->fmt.pix);
+
+ return 0;
+}
+
+/* make a note of pipeline details */
+static void vpfe_prepare_pipeline(struct vpfe_video_device *video)
+{
+ struct media_entity *entity = &video->video_dev.entity;
+ struct media_device *mdev = entity->parent;
+ struct vpfe_pipeline *pipe = &video->pipe;
+ struct vpfe_video_device *far_end = NULL;
+ struct media_entity_graph graph;
+
+ pipe->input_num = 0;
+ pipe->output_num = 0;
+
+ if (video->type == V4L2_BUF_TYPE_VIDEO_OUTPUT)
+ pipe->inputs[pipe->input_num++] = video;
+ else
+ pipe->outputs[pipe->output_num++] = video;
+
+ mutex_lock(&mdev->graph_mutex);
+ media_entity_graph_walk_start(&graph, entity);
+ while ((entity = media_entity_graph_walk_next(&graph))) {
+ if (entity == &video->video_dev.entity)
+ continue;
+ if (media_entity_type(entity) != MEDIA_ENT_T_DEVNODE)
+ continue;
+ far_end = to_vpfe_video(media_entity_to_video_device(entity));
+ if (far_end->type == V4L2_BUF_TYPE_VIDEO_OUTPUT)
+ pipe->inputs[pipe->input_num++] = far_end;
+ else
+ pipe->outputs[pipe->output_num++] = far_end;
+ }
+ mutex_unlock(&mdev->graph_mutex);
+}
+
+/* update pipe state selected by user */
+static int vpfe_update_pipe_state(struct vpfe_video_device *video)
+{
+ struct vpfe_pipeline *pipe = &video->pipe;
+ int ret;
+
+ vpfe_prepare_pipeline(video);
+
+ /* Find out if there is any input video
+ if yes, it is single shot.
+ */
+ if (pipe->input_num == 0) {
+ pipe->state = VPFE_PIPELINE_STREAM_CONTINUOUS;
+ ret = vpfe_update_current_ext_subdev(video);
+ if (ret) {
+ pr_err("Invalid external subdev\n");
+ return ret;
+ }
+ } else {
+ pipe->state = VPFE_PIPELINE_STREAM_SINGLESHOT;
+ }
+ video->initialized = 1;
+ video->skip_frame_count = 1;
+ video->skip_frame_count_init = 1;
+ return 0;
+}
+
+/* checks wether pipeline is ready for enabling */
+int vpfe_video_is_pipe_ready(struct vpfe_pipeline *pipe)
+{
+ int i;
+
+ for (i = 0; i < pipe->input_num; i++)
+ if (!pipe->inputs[i]->started ||
+ pipe->inputs[i]->state != VPFE_VIDEO_BUFFER_QUEUED)
+ return 0;
+ for (i = 0; i < pipe->output_num; i++)
+ if (!pipe->outputs[i]->started ||
+ pipe->outputs[i]->state != VPFE_VIDEO_BUFFER_QUEUED)
+ return 0;
+ return 1;
+}
+
+/**
+ * Validate a pipeline by checking both ends of all links for format
+ * discrepancies.
+ *
+ * Return 0 if all formats match, or -EPIPE if at least one link is found with
+ * different formats on its two ends.
+ */
+static int vpfe_video_validate_pipeline(struct vpfe_pipeline *pipe)
+{
+ struct v4l2_subdev_format fmt_source;
+ struct v4l2_subdev_format fmt_sink;
+ struct v4l2_subdev *subdev;
+ struct media_pad *pad;
+ int ret;
+
+ /*
+ * Should not matter if it is output[0] or 1 as
+ * the general ideas is to traverse backwards and
+ * the fact that the out video node always has the
+ * format of the connected pad.
+ */
+ subdev = vpfe_video_remote_subdev(pipe->outputs[0], NULL);
+ if (subdev == NULL)
+ return -EPIPE;
+
+ while (1) {
+ /* Retrieve the sink format */
+ pad = &subdev->entity.pads[0];
+ if (!(pad->flags & MEDIA_PAD_FL_SINK))
+ break;
+
+ fmt_sink.which = V4L2_SUBDEV_FORMAT_ACTIVE;
+ fmt_sink.pad = pad->index;
+ ret = v4l2_subdev_call(subdev, pad, get_fmt, NULL,
+ &fmt_sink);
+
+ if (ret < 0 && ret != -ENOIOCTLCMD)
+ return -EPIPE;
+
+ /* Retrieve the source format */
+ pad = media_entity_remote_pad(pad);
+ if (pad == NULL ||
+ pad->entity->type != MEDIA_ENT_T_V4L2_SUBDEV)
+ break;
+
+ subdev = media_entity_to_v4l2_subdev(pad->entity);
+
+ fmt_source.which = V4L2_SUBDEV_FORMAT_ACTIVE;
+ fmt_source.pad = pad->index;
+ ret = v4l2_subdev_call(subdev, pad, get_fmt, NULL, &fmt_source);
+ if (ret < 0 && ret != -ENOIOCTLCMD)
+ return -EPIPE;
+
+ /* Check if the two ends match */
+ if (fmt_source.format.code != fmt_sink.format.code ||
+ fmt_source.format.width != fmt_sink.format.width ||
+ fmt_source.format.height != fmt_sink.format.height)
+ return -EPIPE;
+ }
+ return 0;
+}
+
+/*
+ * vpfe_pipeline_enable() - Enable streaming on a pipeline
+ * @vpfe_dev: vpfe device
+ * @pipe: vpfe pipeline
+ *
+ * Walk the entities chain starting at the pipeline output video node and start
+ * all modules in the chain in the given mode.
+ *
+ * Return 0 if successful, or the return value of the failed video::s_stream
+ * operation otherwise.
+ */
+static int vpfe_pipeline_enable(struct vpfe_pipeline *pipe)
+{
+ struct media_entity_graph graph;
+ struct media_entity *entity;
+ struct v4l2_subdev *subdev;
+ struct media_device *mdev;
+ int ret = 0;
+
+ if (pipe->state == VPFE_PIPELINE_STREAM_CONTINUOUS)
+ entity = vpfe_get_input_entity(pipe->outputs[0]);
+ else
+ entity = &pipe->inputs[0]->video_dev.entity;
+
+ mdev = entity->parent;
+ mutex_lock(&mdev->graph_mutex);
+ media_entity_graph_walk_start(&graph, entity);
+ while ((entity = media_entity_graph_walk_next(&graph))) {
+
+ if (media_entity_type(entity) == MEDIA_ENT_T_DEVNODE)
+ continue;
+ subdev = media_entity_to_v4l2_subdev(entity);
+ ret = v4l2_subdev_call(subdev, video, s_stream, 1);
+ if (ret < 0 && ret != -ENOIOCTLCMD)
+ break;
+ }
+ mutex_unlock(&mdev->graph_mutex);
+ return ret;
+}
+
+/*
+ * vpfe_pipeline_disable() - Disable streaming on a pipeline
+ * @vpfe_dev: vpfe device
+ * @pipe: VPFE pipeline
+ *
+ * Walk the entities chain starting at the pipeline output video node and stop
+ * all modules in the chain.
+ *
+ * Return 0 if all modules have been properly stopped, or -ETIMEDOUT if a module
+ * can't be stopped.
+ */
+static int vpfe_pipeline_disable(struct vpfe_pipeline *pipe)
+{
+ struct media_entity_graph graph;
+ struct media_entity *entity;
+ struct v4l2_subdev *subdev;
+ struct media_device *mdev;
+ int ret = 0;
+
+ if (pipe->state == VPFE_PIPELINE_STREAM_CONTINUOUS)
+ entity = vpfe_get_input_entity(pipe->outputs[0]);
+ else
+ entity = &pipe->inputs[0]->video_dev.entity;
+
+ mdev = entity->parent;
+ mutex_lock(&mdev->graph_mutex);
+ media_entity_graph_walk_start(&graph, entity);
+
+ while ((entity = media_entity_graph_walk_next(&graph))) {
+
+ if (media_entity_type(entity) == MEDIA_ENT_T_DEVNODE)
+ continue;
+ subdev = media_entity_to_v4l2_subdev(entity);
+ ret = v4l2_subdev_call(subdev, video, s_stream, 0);
+ if (ret < 0 && ret != -ENOIOCTLCMD)
+ break;
+ }
+ mutex_unlock(&mdev->graph_mutex);
+
+ return ret ? -ETIMEDOUT : 0;
+}
+
+/*
+ * vpfe_pipeline_set_stream() - Enable/disable streaming on a pipeline
+ * @vpfe_dev: VPFE device
+ * @pipe: VPFE pipeline
+ * @state: Stream state (stopped or active)
+ *
+ * Set the pipeline to the given stream state.
+ *
+ * Return 0 if successful, or the return value of the failed video::s_stream
+ * operation otherwise.
+ */
+static int vpfe_pipeline_set_stream(struct vpfe_pipeline *pipe,
+ enum vpfe_pipeline_stream_state state)
+{
+ if (state == VPFE_PIPELINE_STREAM_STOPPED)
+ return vpfe_pipeline_disable(pipe);
+
+ return vpfe_pipeline_enable(pipe);
+}
+
+static int all_videos_stopped(struct vpfe_video_device *video)
+{
+ struct vpfe_pipeline *pipe = &video->pipe;
+ int i;
+
+ for (i = 0; i < pipe->input_num; i++)
+ if (pipe->inputs[i]->started)
+ return 0;
+ for (i = 0; i < pipe->output_num; i++)
+ if (pipe->outputs[i]->started)
+ return 0;
+ return 1;
+}
+
+/*
+ * vpfe_open() - open video device
+ * @file: file pointer
+ *
+ * initialize media pipeline state, allocate memory for file handle
+ *
+ * Return 0 if successful, or the return -ENODEV otherwise.
+ */
+static int vpfe_open(struct file *file)
+{
+ struct vpfe_video_device *video = video_drvdata(file);
+ struct vpfe_fh *handle;
+
+ /* Allocate memory for the file handle object */
+ handle = kzalloc(sizeof(struct vpfe_fh), GFP_KERNEL);
+
+ if (handle == NULL)
+ return -ENOMEM;
+
+ v4l2_fh_init(&handle->vfh, &video->video_dev);
+ v4l2_fh_add(&handle->vfh);
+
+ mutex_lock(&video->lock);
+ /* If decoder is not initialized. initialize it */
+ if (!video->initialized && vpfe_update_pipe_state(video)) {
+ mutex_unlock(&video->lock);
+ return -ENODEV;
+ }
+ /* Increment device users counter */
+ video->usrs++;
+ /* Set io_allowed member to false */
+ handle->io_allowed = 0;
+ handle->video = video;
+ file->private_data = &handle->vfh;
+ mutex_unlock(&video->lock);
+
+ return 0;
+}
+
+/* get the next buffer available from dma queue */
+static unsigned long
+vpfe_video_get_next_buffer(struct vpfe_video_device *video)
+{
+ video->cur_frm = video->next_frm =
+ list_entry(video->dma_queue.next,
+ struct vpfe_cap_buffer, list);
+
+ list_del(&video->next_frm->list);
+ video->next_frm->vb.state = VB2_BUF_STATE_ACTIVE;
+ return vb2_dma_contig_plane_dma_addr(&video->next_frm->vb, 0);
+}
+
+/* schedule the next buffer which is available on dma queue */
+void vpfe_video_schedule_next_buffer(struct vpfe_video_device *video)
+{
+ struct vpfe_device *vpfe_dev = video->vpfe_dev;
+ unsigned long addr;
+
+ if (list_empty(&video->dma_queue))
+ return;
+
+ video->next_frm = list_entry(video->dma_queue.next,
+ struct vpfe_cap_buffer, list);
+
+ if (VPFE_PIPELINE_STREAM_SINGLESHOT == video->pipe.state)
+ video->cur_frm = video->next_frm;
+
+ list_del(&video->next_frm->list);
+ video->next_frm->vb.state = VB2_BUF_STATE_ACTIVE;
+ addr = vb2_dma_contig_plane_dma_addr(&video->next_frm->vb, 0);
+ video->ops->queue(vpfe_dev, addr);
+ video->state = VPFE_VIDEO_BUFFER_QUEUED;
+}
+
+/* schedule the buffer for capturing bottom field */
+void vpfe_video_schedule_bottom_field(struct vpfe_video_device *video)
+{
+ struct vpfe_device *vpfe_dev = video->vpfe_dev;
+ unsigned long addr;
+
+ addr = vb2_dma_contig_plane_dma_addr(&video->cur_frm->vb, 0);
+ addr += video->field_off;
+ video->ops->queue(vpfe_dev, addr);
+}
+
+/* make buffer available for dequeue */
+void vpfe_video_process_buffer_complete(struct vpfe_video_device *video)
+{
+ struct vpfe_pipeline *pipe = &video->pipe;
+
+ do_gettimeofday(&video->cur_frm->vb.v4l2_buf.timestamp);
+ vb2_buffer_done(&video->cur_frm->vb, VB2_BUF_STATE_DONE);
+ if (pipe->state == VPFE_PIPELINE_STREAM_CONTINUOUS)
+ video->cur_frm = video->next_frm;
+}
+
+/* vpfe_stop_capture() - stop streaming */
+static void vpfe_stop_capture(struct vpfe_video_device *video)
+{
+ struct vpfe_pipeline *pipe = &video->pipe;
+
+ video->started = 0;
+
+ if (video->type == V4L2_BUF_TYPE_VIDEO_OUTPUT)
+ return;
+ if (all_videos_stopped(video))
+ vpfe_pipeline_set_stream(pipe,
+ VPFE_PIPELINE_STREAM_STOPPED);
+}
+
+/*
+ * vpfe_release() - release video device
+ * @file: file pointer
+ *
+ * deletes buffer queue, frees the buffers and the vpfe file handle
+ *
+ * Return 0
+ */
+static int vpfe_release(struct file *file)
+{
+ struct vpfe_video_device *video = video_drvdata(file);
+ struct v4l2_fh *vfh = file->private_data;
+ struct vpfe_device *vpfe_dev = video->vpfe_dev;
+ struct vpfe_fh *fh = container_of(vfh, struct vpfe_fh, vfh);
+
+ v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_release\n");
+
+ /* Get the device lock */
+ mutex_lock(&video->lock);
+ /* if this instance is doing IO */
+ if (fh->io_allowed) {
+ if (video->started) {
+ vpfe_stop_capture(video);
+ /* mark pipe state as stopped in vpfe_release(),
+ as app might call streamon() after streamoff()
+ in which case driver has to start streaming.
+ */
+ video->pipe.state = VPFE_PIPELINE_STREAM_STOPPED;
+ vb2_streamoff(&video->buffer_queue,
+ video->buffer_queue.type);
+ }
+ video->io_usrs = 0;
+ /* Free buffers allocated */
+ vb2_queue_release(&video->buffer_queue);
+ vb2_dma_contig_cleanup_ctx(video->alloc_ctx);
+ }
+ /* Decrement device users counter */
+ video->usrs--;
+ v4l2_fh_del(&fh->vfh);
+ v4l2_fh_exit(&fh->vfh);
+ /* If this is the last file handle */
+ if (!video->usrs)
+ video->initialized = 0;
+ mutex_unlock(&video->lock);
+ file->private_data = NULL;
+ /* Free memory allocated to file handle object */
+ v4l2_fh_del(vfh);
+ kzfree(fh);
+ return 0;
+}
+
+/*
+ * vpfe_mmap() - It is used to map kernel space buffers
+ * into user spaces
+ */
+static int vpfe_mmap(struct file *file, struct vm_area_struct *vma)
+{
+ struct vpfe_video_device *video = video_drvdata(file);
+ struct vpfe_device *vpfe_dev = video->vpfe_dev;
+
+ v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_mmap\n");
+ return vb2_mmap(&video->buffer_queue, vma);
+}
+
+/*
+ * vpfe_poll() - It is used for select/poll system call
+ */
+static unsigned int vpfe_poll(struct file *file, poll_table *wait)
+{
+ struct vpfe_video_device *video = video_drvdata(file);
+ struct vpfe_device *vpfe_dev = video->vpfe_dev;
+
+ v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_poll\n");
+ if (video->started)
+ return vb2_poll(&video->buffer_queue, file, wait);
+ return 0;
+}
+
+/* vpfe capture driver file operations */
+static const struct v4l2_file_operations vpfe_fops = {
+ .owner = THIS_MODULE,
+ .open = vpfe_open,
+ .release = vpfe_release,
+ .unlocked_ioctl = video_ioctl2,
+ .mmap = vpfe_mmap,
+ .poll = vpfe_poll
+};
+
+/*
+ * vpfe_querycap() - query capabilities of video device
+ * @file: file pointer
+ * @priv: void pointer
+ * @cap: pointer to v4l2_capability structure
+ *
+ * fills v4l2 capabilities structure
+ *
+ * Return 0
+ */
+static int vpfe_querycap(struct file *file, void *priv,
+ struct v4l2_capability *cap)
+{
+ struct vpfe_video_device *video = video_drvdata(file);
+ struct vpfe_device *vpfe_dev = video->vpfe_dev;
+
+ v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_querycap\n");
+
+ if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;
+ else
+ cap->device_caps = V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_STREAMING;
+ cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_VIDEO_OUTPUT |
+ V4L2_CAP_STREAMING | V4L2_CAP_DEVICE_CAPS;
+ strlcpy(cap->driver, CAPTURE_DRV_NAME, sizeof(cap->driver));
+ strlcpy(cap->bus_info, "VPFE", sizeof(cap->bus_info));
+ strlcpy(cap->card, vpfe_dev->cfg->card_name, sizeof(cap->card));
+
+ return 0;
+}
+
+/*
+ * vpfe_g_fmt() - get the format which is active on video device
+ * @file: file pointer
+ * @priv: void pointer
+ * @fmt: pointer to v4l2_format structure
+ *
+ * fills v4l2 format structure with active format
+ *
+ * Return 0
+ */
+static int vpfe_g_fmt(struct file *file, void *priv,
+ struct v4l2_format *fmt)
+{
+ struct vpfe_video_device *video = video_drvdata(file);
+ struct vpfe_device *vpfe_dev = video->vpfe_dev;
+
+ v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_g_fmt\n");
+ /* Fill in the information about format */
+ *fmt = video->fmt;
+ return 0;
+}
+
+/*
+ * vpfe_enum_fmt() - enum formats supported on media chain
+ * @file: file pointer
+ * @priv: void pointer
+ * @fmt: pointer to v4l2_fmtdesc structure
+ *
+ * fills v4l2_fmtdesc structure with output format set on adjacent subdev,
+ * only one format is enumearted as subdevs are already configured
+ *
+ * Return 0 if successful, error code otherwise
+ */
+static int vpfe_enum_fmt(struct file *file, void *priv,
+ struct v4l2_fmtdesc *fmt)
+{
+ struct vpfe_video_device *video = video_drvdata(file);
+ struct vpfe_device *vpfe_dev = video->vpfe_dev;
+ struct v4l2_subdev_format sd_fmt;
+ struct v4l2_mbus_framefmt mbus;
+ struct v4l2_subdev *subdev;
+ struct v4l2_format format;
+ struct media_pad *remote;
+ int ret;
+
+ v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_enum_fmt\n");
+
+ /* since already subdev pad format is set,
+ only one pixel format is available */
+ if (fmt->index > 0) {
+ v4l2_err(&vpfe_dev->v4l2_dev, "Invalid index\n");
+ return -EINVAL;
+ }
+ /* get the remote pad */
+ remote = media_entity_remote_pad(&video->pad);
+ if (remote == NULL) {
+ v4l2_err(&vpfe_dev->v4l2_dev,
+ "invalid remote pad for video node\n");
+ return -EINVAL;
+ }
+ /* get the remote subdev */
+ subdev = vpfe_video_remote_subdev(video, NULL);
+ if (subdev == NULL) {
+ v4l2_err(&vpfe_dev->v4l2_dev,
+ "invalid remote subdev for video node\n");
+ return -EINVAL;
+ }
+ sd_fmt.pad = remote->index;
+ sd_fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE;
+ /* get output format of remote subdev */
+ ret = v4l2_subdev_call(subdev, pad, get_fmt, NULL, &sd_fmt);
+ if (ret) {
+ v4l2_err(&vpfe_dev->v4l2_dev,
+ "invalid remote subdev for video node\n");
+ return ret;
+ }
+ /* convert to pix format */
+ mbus.code = sd_fmt.format.code;
+ mbus_to_pix(&mbus, &format.fmt.pix);
+ /* copy the result */
+ fmt->pixelformat = format.fmt.pix.pixelformat;
+
+ return 0;
+}
+
+/*
+ * vpfe_s_fmt() - set the format on video device
+ * @file: file pointer
+ * @priv: void pointer
+ * @fmt: pointer to v4l2_format structure
+ *
+ * validate and set the format on video device
+ *
+ * Return 0 on success, error code otherwise
+ */
+static int vpfe_s_fmt(struct file *file, void *priv,
+ struct v4l2_format *fmt)
+{
+ struct vpfe_video_device *video = video_drvdata(file);
+ struct vpfe_device *vpfe_dev = video->vpfe_dev;
+ struct v4l2_format format;
+ int ret;
+
+ v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_s_fmt\n");
+ /* If streaming is started, return error */
+ if (video->started) {
+ v4l2_err(&vpfe_dev->v4l2_dev, "Streaming is started\n");
+ return -EBUSY;
+ }
+ /* get adjacent subdev's output pad format */
+ ret = __vpfe_video_get_format(video, &format);
+ if (ret)
+ return ret;
+ *fmt = format;
+ video->fmt = *fmt;
+ return 0;
+}
+
+/*
+ * vpfe_try_fmt() - try the format on video device
+ * @file: file pointer
+ * @priv: void pointer
+ * @fmt: pointer to v4l2_format structure
+ *
+ * validate the format, update with correct format
+ * based on output format set on adjacent subdev
+ *
+ * Return 0 on success, error code otherwise
+ */
+static int vpfe_try_fmt(struct file *file, void *priv,
+ struct v4l2_format *fmt)
+{
+ struct vpfe_video_device *video = video_drvdata(file);
+ struct vpfe_device *vpfe_dev = video->vpfe_dev;
+ struct v4l2_format format;
+ int ret;
+
+ v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_try_fmt\n");
+ /* get adjacent subdev's output pad format */
+ ret = __vpfe_video_get_format(video, &format);
+ if (ret)
+ return ret;
+
+ *fmt = format;
+ return 0;
+}
+
+/*
+ * vpfe_enum_input() - enum inputs supported on media chain
+ * @file: file pointer
+ * @priv: void pointer
+ * @fmt: pointer to v4l2_fmtdesc structure
+ *
+ * fills v4l2_input structure with input available on media chain,
+ * only one input is enumearted as media chain is setup by this time
+ *
+ * Return 0 if successful, -EINVAL is media chain is invalid
+ */
+static int vpfe_enum_input(struct file *file, void *priv,
+ struct v4l2_input *inp)
+{
+ struct vpfe_video_device *video = video_drvdata(file);
+ struct vpfe_ext_subdev_info *sdinfo = video->current_ext_subdev;
+ struct vpfe_device *vpfe_dev = video->vpfe_dev;
+
+ v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_enum_input\n");
+ /* enumerate from the subdev user has chosen through mc */
+ if (inp->index < sdinfo->num_inputs) {
+ memcpy(inp, &sdinfo->inputs[inp->index],
+ sizeof(struct v4l2_input));
+ return 0;
+ }
+ return -EINVAL;
+}
+
+/*
+ * vpfe_g_input() - get index of the input which is active
+ * @file: file pointer
+ * @priv: void pointer
+ * @index: pointer to unsigned int
+ *
+ * set index with input index which is active
+ */
+static int vpfe_g_input(struct file *file, void *priv, unsigned int *index)
+{
+ struct vpfe_video_device *video = video_drvdata(file);
+ struct vpfe_device *vpfe_dev = video->vpfe_dev;
+
+ v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_g_input\n");
+
+ *index = video->current_input;
+ return 0;
+}
+
+/*
+ * vpfe_s_input() - set input which is pointed by input index
+ * @file: file pointer
+ * @priv: void pointer
+ * @index: pointer to unsigned int
+ *
+ * set input on external subdev
+ *
+ * Return 0 on success, error code otherwise
+ */
+static int vpfe_s_input(struct file *file, void *priv, unsigned int index)
+{
+ struct vpfe_video_device *video = video_drvdata(file);
+ struct vpfe_device *vpfe_dev = video->vpfe_dev;
+ struct vpfe_ext_subdev_info *sdinfo;
+ struct vpfe_route *route;
+ struct v4l2_input *inps;
+ u32 output;
+ u32 input;
+ int ret;
+ int i;
+
+ v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_s_input\n");
+
+ ret = mutex_lock_interruptible(&video->lock);
+ if (ret)
+ return ret;
+ /*
+ * If streaming is started return device busy
+ * error
+ */
+ if (video->started) {
+ v4l2_err(&vpfe_dev->v4l2_dev, "Streaming is on\n");
+ ret = -EBUSY;
+ goto unlock_out;
+ }
+
+ sdinfo = video->current_ext_subdev;
+ if (!sdinfo->registered) {
+ ret = -EINVAL;
+ goto unlock_out;
+ }
+ if (vpfe_dev->cfg->setup_input &&
+ vpfe_dev->cfg->setup_input(sdinfo->grp_id) < 0) {
+ ret = -EFAULT;
+ v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev,
+ "couldn't setup input for %s\n",
+ sdinfo->module_name);
+ goto unlock_out;
+ }
+ route = &sdinfo->routes[index];
+ if (route && sdinfo->can_route) {
+ input = route->input;
+ output = route->output;
+ ret = v4l2_device_call_until_err(&vpfe_dev->v4l2_dev,
+ sdinfo->grp_id, video,
+ s_routing, input, output, 0);
+ if (ret) {
+ v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev,
+ "s_input:error in setting input in decoder\n");
+ ret = -EINVAL;
+ goto unlock_out;
+ }
+ }
+ /* set standards set by subdev in video device */
+ for (i = 0; i < sdinfo->num_inputs; i++) {
+ inps = &sdinfo->inputs[i];
+ video->video_dev.tvnorms |= inps->std;
+ }
+ video->current_input = index;
+unlock_out:
+ mutex_unlock(&video->lock);
+ return ret;
+}
+
+/*
+ * vpfe_querystd() - query std which is being input on external subdev
+ * @file: file pointer
+ * @priv: void pointer
+ * @std_id: pointer to v4l2_std_id structure
+ *
+ * call external subdev through v4l2_device_call_until_err to
+ * get the std that is being active.
+ *
+ * Return 0 on success, error code otherwise
+ */
+static int vpfe_querystd(struct file *file, void *priv, v4l2_std_id *std_id)
+{
+ struct vpfe_video_device *video = video_drvdata(file);
+ struct vpfe_device *vpfe_dev = video->vpfe_dev;
+ struct vpfe_ext_subdev_info *sdinfo;
+ int ret;
+
+ v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_querystd\n");
+
+ ret = mutex_lock_interruptible(&video->lock);
+ sdinfo = video->current_ext_subdev;
+ if (ret)
+ return ret;
+ /* Call querystd function of decoder device */
+ ret = v4l2_device_call_until_err(&vpfe_dev->v4l2_dev, sdinfo->grp_id,
+ video, querystd, std_id);
+ mutex_unlock(&video->lock);
+ return ret;
+}
+
+/*
+ * vpfe_s_std() - set std on external subdev
+ * @file: file pointer
+ * @priv: void pointer
+ * @std_id: pointer to v4l2_std_id structure
+ *
+ * set std pointed by std_id on external subdev by calling it using
+ * v4l2_device_call_until_err
+ *
+ * Return 0 on success, error code otherwise
+ */
+static int vpfe_s_std(struct file *file, void *priv, v4l2_std_id std_id)
+{
+ struct vpfe_video_device *video = video_drvdata(file);
+ struct vpfe_device *vpfe_dev = video->vpfe_dev;
+ struct vpfe_ext_subdev_info *sdinfo;
+ int ret;
+
+ v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_s_std\n");
+
+ /* Call decoder driver function to set the standard */
+ ret = mutex_lock_interruptible(&video->lock);
+ if (ret)
+ return ret;
+ sdinfo = video->current_ext_subdev;
+ /* If streaming is started, return device busy error */
+ if (video->started) {
+ v4l2_err(&vpfe_dev->v4l2_dev, "streaming is started\n");
+ ret = -EBUSY;
+ goto unlock_out;
+ }
+ ret = v4l2_device_call_until_err(&vpfe_dev->v4l2_dev, sdinfo->grp_id,
+ video, s_std, std_id);
+ if (ret < 0) {
+ v4l2_err(&vpfe_dev->v4l2_dev, "Failed to set standard\n");
+ video->stdid = V4L2_STD_UNKNOWN;
+ goto unlock_out;
+ }
+ video->stdid = std_id;
+unlock_out:
+ mutex_unlock(&video->lock);
+ return ret;
+}
+
+static int vpfe_g_std(struct file *file, void *priv, v4l2_std_id *tvnorm)
+{
+ struct vpfe_video_device *video = video_drvdata(file);
+ struct vpfe_device *vpfe_dev = video->vpfe_dev;
+
+ v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_g_std\n");
+ *tvnorm = video->stdid;
+ return 0;
+}
+
+/*
+ * vpfe_enum_dv_timings() - enumerate dv_timings which are supported by
+ * to external subdev
+ * @file: file pointer
+ * @priv: void pointer
+ * @timings: pointer to v4l2_enum_dv_timings structure
+ *
+ * enum dv_timings's which are supported by external subdev through
+ * v4l2_subdev_call
+ *
+ * Return 0 on success, error code otherwise
+ */
+static int
+vpfe_enum_dv_timings(struct file *file, void *fh,
+ struct v4l2_enum_dv_timings *timings)
+{
+ struct vpfe_video_device *video = video_drvdata(file);
+ struct vpfe_device *vpfe_dev = video->vpfe_dev;
+ struct v4l2_subdev *subdev = video->current_ext_subdev->subdev;
+
+ timings->pad = 0;
+
+ v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_enum_dv_timings\n");
+ return v4l2_subdev_call(subdev, pad, enum_dv_timings, timings);
+}
+
+/*
+ * vpfe_query_dv_timings() - query the dv_timings which is being input
+ * to external subdev
+ * @file: file pointer
+ * @priv: void pointer
+ * @timings: pointer to v4l2_dv_timings structure
+ *
+ * get dv_timings which is being input on external subdev through
+ * v4l2_subdev_call
+ *
+ * Return 0 on success, error code otherwise
+ */
+static int
+vpfe_query_dv_timings(struct file *file, void *fh,
+ struct v4l2_dv_timings *timings)
+{
+ struct vpfe_video_device *video = video_drvdata(file);
+ struct vpfe_device *vpfe_dev = video->vpfe_dev;
+ struct v4l2_subdev *subdev = video->current_ext_subdev->subdev;
+
+ v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_query_dv_timings\n");
+ return v4l2_subdev_call(subdev, video, query_dv_timings, timings);
+}
+
+/*
+ * vpfe_s_dv_timings() - set dv_timings on external subdev
+ * @file: file pointer
+ * @priv: void pointer
+ * @timings: pointer to v4l2_dv_timings structure
+ *
+ * set dv_timings pointed by timings on external subdev through
+ * v4l2_device_call_until_err, this configures amplifier also
+ *
+ * Return 0 on success, error code otherwise
+ */
+static int
+vpfe_s_dv_timings(struct file *file, void *fh,
+ struct v4l2_dv_timings *timings)
+{
+ struct vpfe_video_device *video = video_drvdata(file);
+ struct vpfe_device *vpfe_dev = video->vpfe_dev;
+
+ v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_s_dv_timings\n");
+
+ video->stdid = V4L2_STD_UNKNOWN;
+ return v4l2_device_call_until_err(&vpfe_dev->v4l2_dev,
+ video->current_ext_subdev->grp_id,
+ video, s_dv_timings, timings);
+}
+
+/*
+ * vpfe_g_dv_timings() - get dv_timings which is set on external subdev
+ * @file: file pointer
+ * @priv: void pointer
+ * @timings: pointer to v4l2_dv_timings structure
+ *
+ * get dv_timings which is set on external subdev through
+ * v4l2_subdev_call
+ *
+ * Return 0 on success, error code otherwise
+ */
+static int
+vpfe_g_dv_timings(struct file *file, void *fh,
+ struct v4l2_dv_timings *timings)
+{
+ struct vpfe_video_device *video = video_drvdata(file);
+ struct vpfe_device *vpfe_dev = video->vpfe_dev;
+ struct v4l2_subdev *subdev = video->current_ext_subdev->subdev;
+
+ v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_g_dv_timings\n");
+ return v4l2_subdev_call(subdev, video, g_dv_timings, timings);
+}
+
+/*
+ * Videobuf operations
+ */
+/*
+ * vpfe_buffer_queue_setup : Callback function for buffer setup.
+ * @vq: vb2_queue ptr
+ * @fmt: v4l2 format
+ * @nbuffers: ptr to number of buffers requested by application
+ * @nplanes:: contains number of distinct video planes needed to hold a frame
+ * @sizes[]: contains the size (in bytes) of each plane.
+ * @alloc_ctxs: ptr to allocation context
+ *
+ * This callback function is called when reqbuf() is called to adjust
+ * the buffer nbuffers and buffer size
+ */
+static int
+vpfe_buffer_queue_setup(struct vb2_queue *vq, const struct v4l2_format *fmt,
+ unsigned int *nbuffers, unsigned int *nplanes,
+ unsigned int sizes[], void *alloc_ctxs[])
+{
+ struct vpfe_fh *fh = vb2_get_drv_priv(vq);
+ struct vpfe_video_device *video = fh->video;
+ struct vpfe_device *vpfe_dev = video->vpfe_dev;
+ struct vpfe_pipeline *pipe = &video->pipe;
+ unsigned long size;
+
+ v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_buffer_queue_setup\n");
+ size = video->fmt.fmt.pix.sizeimage;
+
+ if (vpfe_dev->video_limit) {
+ while (size * *nbuffers > vpfe_dev->video_limit)
+ (*nbuffers)--;
+ }
+ if (pipe->state == VPFE_PIPELINE_STREAM_CONTINUOUS) {
+ if (*nbuffers < MIN_NUM_BUFFERS)
+ *nbuffers = MIN_NUM_BUFFERS;
+ }
+ *nplanes = 1;
+ sizes[0] = size;
+ alloc_ctxs[0] = video->alloc_ctx;
+ v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev,
+ "nbuffers=%d, size=%lu\n", *nbuffers, size);
+ return 0;
+}
+
+/*
+ * vpfe_buffer_prepare : callback function for buffer prepare
+ * @vb: ptr to vb2_buffer
+ *
+ * This is the callback function for buffer prepare when vb2_qbuf()
+ * function is called. The buffer is prepared and user space virtual address
+ * or user address is converted into physical address
+ */
+static int vpfe_buffer_prepare(struct vb2_buffer *vb)
+{
+ struct vpfe_fh *fh = vb2_get_drv_priv(vb->vb2_queue);
+ struct vpfe_video_device *video = fh->video;
+ struct vpfe_device *vpfe_dev = video->vpfe_dev;
+ unsigned long addr;
+
+ v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_buffer_prepare\n");
+
+ if (vb->state != VB2_BUF_STATE_ACTIVE &&
+ vb->state != VB2_BUF_STATE_PREPARED)
+ return 0;
+
+ /* Initialize buffer */
+ vb2_set_plane_payload(vb, 0, video->fmt.fmt.pix.sizeimage);
+ if (vb2_plane_vaddr(vb, 0) &&
+ vb2_get_plane_payload(vb, 0) > vb2_plane_size(vb, 0))
+ return -EINVAL;
+
+ addr = vb2_dma_contig_plane_dma_addr(vb, 0);
+ /* Make sure user addresses are aligned to 32 bytes */
+ if (!ALIGN(addr, 32))
+ return -EINVAL;
+
+ return 0;
+}
+
+static void vpfe_buffer_queue(struct vb2_buffer *vb)
+{
+ /* Get the file handle object and device object */
+ struct vpfe_fh *fh = vb2_get_drv_priv(vb->vb2_queue);
+ struct vpfe_video_device *video = fh->video;
+ struct vpfe_device *vpfe_dev = video->vpfe_dev;
+ struct vpfe_pipeline *pipe = &video->pipe;
+ struct vpfe_cap_buffer *buf = container_of(vb,
+ struct vpfe_cap_buffer, vb);
+ unsigned long flags;
+ unsigned long empty;
+ unsigned long addr;
+
+ spin_lock_irqsave(&video->dma_queue_lock, flags);
+ empty = list_empty(&video->dma_queue);
+ /* add the buffer to the DMA queue */
+ list_add_tail(&buf->list, &video->dma_queue);
+ spin_unlock_irqrestore(&video->dma_queue_lock, flags);
+ /* this case happens in case of single shot */
+ if (empty && video->started && pipe->state ==
+ VPFE_PIPELINE_STREAM_SINGLESHOT &&
+ video->state == VPFE_VIDEO_BUFFER_NOT_QUEUED) {
+ spin_lock(&video->dma_queue_lock);
+ addr = vpfe_video_get_next_buffer(video);
+ video->ops->queue(vpfe_dev, addr);
+
+ video->state = VPFE_VIDEO_BUFFER_QUEUED;
+ spin_unlock(&video->dma_queue_lock);
+
+ /* enable h/w each time in single shot */
+ if (vpfe_video_is_pipe_ready(pipe))
+ vpfe_pipeline_set_stream(pipe,
+ VPFE_PIPELINE_STREAM_SINGLESHOT);
+ }
+}
+
+/* vpfe_start_capture() - start streaming on all the subdevs */
+static int vpfe_start_capture(struct vpfe_video_device *video)
+{
+ struct vpfe_pipeline *pipe = &video->pipe;
+ int ret = 0;
+
+ video->started = 1;
+ if (vpfe_video_is_pipe_ready(pipe))
+ ret = vpfe_pipeline_set_stream(pipe, pipe->state);
+
+ return ret;
+}
+
+static int vpfe_start_streaming(struct vb2_queue *vq, unsigned int count)
+{
+ struct vpfe_fh *fh = vb2_get_drv_priv(vq);
+ struct vpfe_video_device *video = fh->video;
+ struct vpfe_device *vpfe_dev = video->vpfe_dev;
+ unsigned long addr;
+ int ret;
+
+ ret = mutex_lock_interruptible(&video->lock);
+ if (ret)
+ goto streamoff;
+
+ /* Get the next frame from the buffer queue */
+ video->cur_frm = video->next_frm =
+ list_entry(video->dma_queue.next, struct vpfe_cap_buffer, list);
+ /* Remove buffer from the buffer queue */
+ list_del(&video->cur_frm->list);
+ /* Mark state of the current frame to active */
+ video->cur_frm->vb.state = VB2_BUF_STATE_ACTIVE;
+ /* Initialize field_id and started member */
+ video->field_id = 0;
+ addr = vb2_dma_contig_plane_dma_addr(&video->cur_frm->vb, 0);
+ video->ops->queue(vpfe_dev, addr);
+ video->state = VPFE_VIDEO_BUFFER_QUEUED;
+
+ ret = vpfe_start_capture(video);
+ if (ret) {
+ struct vpfe_cap_buffer *buf, *tmp;
+
+ vb2_buffer_done(&video->cur_frm->vb, VB2_BUF_STATE_QUEUED);
+ list_for_each_entry_safe(buf, tmp, &video->dma_queue, list) {
+ list_del(&buf->list);
+ vb2_buffer_done(&buf->vb, VB2_BUF_STATE_QUEUED);
+ }
+ goto unlock_out;
+ }
+
+ mutex_unlock(&video->lock);
+
+ return ret;
+unlock_out:
+ mutex_unlock(&video->lock);
+streamoff:
+ ret = vb2_streamoff(&video->buffer_queue, video->buffer_queue.type);
+ return 0;
+}
+
+static int vpfe_buffer_init(struct vb2_buffer *vb)
+{
+ struct vpfe_cap_buffer *buf = container_of(vb,
+ struct vpfe_cap_buffer, vb);
+
+ INIT_LIST_HEAD(&buf->list);
+ return 0;
+}
+
+/* abort streaming and wait for last buffer */
+static void vpfe_stop_streaming(struct vb2_queue *vq)
+{
+ struct vpfe_fh *fh = vb2_get_drv_priv(vq);
+ struct vpfe_video_device *video = fh->video;
+
+ /* release all active buffers */
+ if (video->cur_frm == video->next_frm) {
+ vb2_buffer_done(&video->cur_frm->vb, VB2_BUF_STATE_ERROR);
+ } else {
+ if (video->cur_frm != NULL)
+ vb2_buffer_done(&video->cur_frm->vb,
+ VB2_BUF_STATE_ERROR);
+ if (video->next_frm != NULL)
+ vb2_buffer_done(&video->next_frm->vb,
+ VB2_BUF_STATE_ERROR);
+ }
+
+ while (!list_empty(&video->dma_queue)) {
+ video->next_frm = list_entry(video->dma_queue.next,
+ struct vpfe_cap_buffer, list);
+ list_del(&video->next_frm->list);
+ vb2_buffer_done(&video->next_frm->vb, VB2_BUF_STATE_ERROR);
+ }
+}
+
+static void vpfe_buf_cleanup(struct vb2_buffer *vb)
+{
+ struct vpfe_fh *fh = vb2_get_drv_priv(vb->vb2_queue);
+ struct vpfe_video_device *video = fh->video;
+ struct vpfe_device *vpfe_dev = video->vpfe_dev;
+ struct vpfe_cap_buffer *buf = container_of(vb,
+ struct vpfe_cap_buffer, vb);
+
+ v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_buf_cleanup\n");
+ if (vb->state == VB2_BUF_STATE_ACTIVE)
+ list_del_init(&buf->list);
+}
+
+static struct vb2_ops video_qops = {
+ .queue_setup = vpfe_buffer_queue_setup,
+ .buf_init = vpfe_buffer_init,
+ .buf_prepare = vpfe_buffer_prepare,
+ .start_streaming = vpfe_start_streaming,
+ .stop_streaming = vpfe_stop_streaming,
+ .buf_cleanup = vpfe_buf_cleanup,
+ .buf_queue = vpfe_buffer_queue,
+};
+
+/*
+ * vpfe_reqbufs() - supported REQBUF only once opening
+ * the device.
+ */
+static int vpfe_reqbufs(struct file *file, void *priv,
+ struct v4l2_requestbuffers *req_buf)
+{
+ struct vpfe_video_device *video = video_drvdata(file);
+ struct vpfe_device *vpfe_dev = video->vpfe_dev;
+ struct vpfe_fh *fh = file->private_data;
+ struct vb2_queue *q;
+ int ret;
+
+ v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_reqbufs\n");
+
+ if (V4L2_BUF_TYPE_VIDEO_CAPTURE != req_buf->type &&
+ V4L2_BUF_TYPE_VIDEO_OUTPUT != req_buf->type) {
+ v4l2_err(&vpfe_dev->v4l2_dev, "Invalid buffer type\n");
+ return -EINVAL;
+ }
+
+ ret = mutex_lock_interruptible(&video->lock);
+ if (ret)
+ return ret;
+
+ if (video->io_usrs != 0) {
+ v4l2_err(&vpfe_dev->v4l2_dev, "Only one IO user allowed\n");
+ ret = -EBUSY;
+ goto unlock_out;
+ }
+ video->memory = req_buf->memory;
+
+ /* Initialize videobuf2 queue as per the buffer type */
+ video->alloc_ctx = vb2_dma_contig_init_ctx(vpfe_dev->pdev);
+ if (IS_ERR(video->alloc_ctx)) {
+ v4l2_err(&vpfe_dev->v4l2_dev, "Failed to get the context\n");
+ return PTR_ERR(video->alloc_ctx);
+ }
+
+ q = &video->buffer_queue;
+ q->type = req_buf->type;
+ q->io_modes = VB2_MMAP | VB2_USERPTR;
+ q->drv_priv = fh;
+ q->min_buffers_needed = 1;
+ q->ops = &video_qops;
+ q->mem_ops = &vb2_dma_contig_memops;
+ q->buf_struct_size = sizeof(struct vpfe_cap_buffer);
+
+ ret = vb2_queue_init(q);
+ if (ret) {
+ v4l2_err(&vpfe_dev->v4l2_dev, "vb2_queue_init() failed\n");
+ vb2_dma_contig_cleanup_ctx(vpfe_dev->pdev);
+ return ret;
+ }
+
+ fh->io_allowed = 1;
+ video->io_usrs = 1;
+ INIT_LIST_HEAD(&video->dma_queue);
+ ret = vb2_reqbufs(&video->buffer_queue, req_buf);
+
+unlock_out:
+ mutex_unlock(&video->lock);
+ return ret;
+}
+
+/*
+ * vpfe_querybuf() - query buffers for exchange
+ */
+static int vpfe_querybuf(struct file *file, void *priv,
+ struct v4l2_buffer *buf)
+{
+ struct vpfe_video_device *video = video_drvdata(file);
+ struct vpfe_device *vpfe_dev = video->vpfe_dev;
+
+ v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_querybuf\n");
+
+ if (V4L2_BUF_TYPE_VIDEO_CAPTURE != buf->type &&
+ V4L2_BUF_TYPE_VIDEO_OUTPUT != buf->type) {
+ v4l2_err(&vpfe_dev->v4l2_dev, "Invalid buf type\n");
+ return -EINVAL;
+ }
+
+ if (video->memory != V4L2_MEMORY_MMAP) {
+ v4l2_err(&vpfe_dev->v4l2_dev, "Invalid memory\n");
+ return -EINVAL;
+ }
+
+ /* Call vb2_querybuf to get information */
+ return vb2_querybuf(&video->buffer_queue, buf);
+}
+
+/*
+ * vpfe_qbuf() - queue buffers for capture or processing
+ */
+static int vpfe_qbuf(struct file *file, void *priv,
+ struct v4l2_buffer *p)
+{
+ struct vpfe_video_device *video = video_drvdata(file);
+ struct vpfe_device *vpfe_dev = video->vpfe_dev;
+ struct vpfe_fh *fh = file->private_data;
+
+ v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_qbuf\n");
+
+ if (V4L2_BUF_TYPE_VIDEO_CAPTURE != p->type &&
+ V4L2_BUF_TYPE_VIDEO_OUTPUT != p->type) {
+ v4l2_err(&vpfe_dev->v4l2_dev, "Invalid buf type\n");
+ return -EINVAL;
+ }
+ /*
+ * If this file handle is not allowed to do IO,
+ * return error
+ */
+ if (!fh->io_allowed) {
+ v4l2_err(&vpfe_dev->v4l2_dev, "fh->io_allowed\n");
+ return -EACCES;
+ }
+
+ return vb2_qbuf(&video->buffer_queue, p);
+}
+
+/*
+ * vpfe_dqbuf() - deque buffer which is done with processing
+ */
+static int vpfe_dqbuf(struct file *file, void *priv,
+ struct v4l2_buffer *buf)
+{
+ struct vpfe_video_device *video = video_drvdata(file);
+ struct vpfe_device *vpfe_dev = video->vpfe_dev;
+
+ v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_dqbuf\n");
+
+ if (V4L2_BUF_TYPE_VIDEO_CAPTURE != buf->type &&
+ V4L2_BUF_TYPE_VIDEO_OUTPUT != buf->type) {
+ v4l2_err(&vpfe_dev->v4l2_dev, "Invalid buf type\n");
+ return -EINVAL;
+ }
+
+ return vb2_dqbuf(&video->buffer_queue,
+ buf, (file->f_flags & O_NONBLOCK));
+}
+
+/*
+ * vpfe_streamon() - start streaming
+ * @file: file pointer
+ * @priv: void pointer
+ * @buf_type: enum v4l2_buf_type
+ *
+ * queue buffer onto hardware for capture/processing and
+ * start all the subdevs which are in media chain
+ *
+ * Return 0 on success, error code otherwise
+ */
+static int vpfe_streamon(struct file *file, void *priv,
+ enum v4l2_buf_type buf_type)
+{
+ struct vpfe_video_device *video = video_drvdata(file);
+ struct vpfe_device *vpfe_dev = video->vpfe_dev;
+ struct vpfe_pipeline *pipe = &video->pipe;
+ struct vpfe_fh *fh = file->private_data;
+ struct vpfe_ext_subdev_info *sdinfo;
+ int ret = -EINVAL;
+
+ v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_streamon\n");
+
+ if (V4L2_BUF_TYPE_VIDEO_CAPTURE != buf_type &&
+ V4L2_BUF_TYPE_VIDEO_OUTPUT != buf_type) {
+ v4l2_err(&vpfe_dev->v4l2_dev, "Invalid buf type\n");
+ return ret;
+ }
+ /* If file handle is not allowed IO, return error */
+ if (!fh->io_allowed) {
+ v4l2_err(&vpfe_dev->v4l2_dev, "fh->io_allowed\n");
+ return -EACCES;
+ }
+ sdinfo = video->current_ext_subdev;
+ /* If buffer queue is empty, return error */
+ if (list_empty(&video->buffer_queue.queued_list)) {
+ v4l2_err(&vpfe_dev->v4l2_dev, "buffer queue is empty\n");
+ return -EIO;
+ }
+ /* Validate the pipeline */
+ if (V4L2_BUF_TYPE_VIDEO_CAPTURE == buf_type) {
+ ret = vpfe_video_validate_pipeline(pipe);
+ if (ret < 0)
+ return ret;
+ }
+ /* Call vb2_streamon to start streaming */
+ return vb2_streamon(&video->buffer_queue, buf_type);
+}
+
+/*
+ * vpfe_streamoff() - stop streaming
+ * @file: file pointer
+ * @priv: void pointer
+ * @buf_type: enum v4l2_buf_type
+ *
+ * stop all the subdevs which are in media chain
+ *
+ * Return 0 on success, error code otherwise
+ */
+static int vpfe_streamoff(struct file *file, void *priv,
+ enum v4l2_buf_type buf_type)
+{
+ struct vpfe_video_device *video = video_drvdata(file);
+ struct vpfe_device *vpfe_dev = video->vpfe_dev;
+ struct vpfe_fh *fh = file->private_data;
+ int ret = 0;
+
+ v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_streamoff\n");
+
+ if (buf_type != V4L2_BUF_TYPE_VIDEO_CAPTURE &&
+ buf_type != V4L2_BUF_TYPE_VIDEO_OUTPUT) {
+ v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "Invalid buf type\n");
+ return -EINVAL;
+ }
+
+ /* If io is allowed for this file handle, return error */
+ if (!fh->io_allowed) {
+ v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "fh->io_allowed\n");
+ return -EACCES;
+ }
+
+ /* If streaming is not started, return error */
+ if (!video->started) {
+ v4l2_err(&vpfe_dev->v4l2_dev, "device is not started\n");
+ return -EINVAL;
+ }
+
+ ret = mutex_lock_interruptible(&video->lock);
+ if (ret)
+ return ret;
+
+ vpfe_stop_capture(video);
+ ret = vb2_streamoff(&video->buffer_queue, buf_type);
+ mutex_unlock(&video->lock);
+
+ return ret;
+}
+
+/* vpfe capture ioctl operations */
+static const struct v4l2_ioctl_ops vpfe_ioctl_ops = {
+ .vidioc_querycap = vpfe_querycap,
+ .vidioc_g_fmt_vid_cap = vpfe_g_fmt,
+ .vidioc_s_fmt_vid_cap = vpfe_s_fmt,
+ .vidioc_try_fmt_vid_cap = vpfe_try_fmt,
+ .vidioc_enum_fmt_vid_cap = vpfe_enum_fmt,
+ .vidioc_g_fmt_vid_out = vpfe_g_fmt,
+ .vidioc_s_fmt_vid_out = vpfe_s_fmt,
+ .vidioc_try_fmt_vid_out = vpfe_try_fmt,
+ .vidioc_enum_fmt_vid_out = vpfe_enum_fmt,
+ .vidioc_enum_input = vpfe_enum_input,
+ .vidioc_g_input = vpfe_g_input,
+ .vidioc_s_input = vpfe_s_input,
+ .vidioc_querystd = vpfe_querystd,
+ .vidioc_s_std = vpfe_s_std,
+ .vidioc_g_std = vpfe_g_std,
+ .vidioc_enum_dv_timings = vpfe_enum_dv_timings,
+ .vidioc_query_dv_timings = vpfe_query_dv_timings,
+ .vidioc_s_dv_timings = vpfe_s_dv_timings,
+ .vidioc_g_dv_timings = vpfe_g_dv_timings,
+ .vidioc_reqbufs = vpfe_reqbufs,
+ .vidioc_querybuf = vpfe_querybuf,
+ .vidioc_qbuf = vpfe_qbuf,
+ .vidioc_dqbuf = vpfe_dqbuf,
+ .vidioc_streamon = vpfe_streamon,
+ .vidioc_streamoff = vpfe_streamoff,
+};
+
+/* VPFE video init function */
+int vpfe_video_init(struct vpfe_video_device *video, const char *name)
+{
+ const char *direction;
+ int ret;
+
+ switch (video->type) {
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+ direction = "output";
+ video->pad.flags = MEDIA_PAD_FL_SINK;
+ video->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ break;
+
+ case V4L2_BUF_TYPE_VIDEO_OUTPUT:
+ direction = "input";
+ video->pad.flags = MEDIA_PAD_FL_SOURCE;
+ video->type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+ /* Initialize field of video device */
+ video->video_dev.release = video_device_release;
+ video->video_dev.fops = &vpfe_fops;
+ video->video_dev.ioctl_ops = &vpfe_ioctl_ops;
+ video->video_dev.minor = -1;
+ video->video_dev.tvnorms = 0;
+ snprintf(video->video_dev.name, sizeof(video->video_dev.name),
+ "DAVINCI VIDEO %s %s", name, direction);
+
+ spin_lock_init(&video->irqlock);
+ spin_lock_init(&video->dma_queue_lock);
+ mutex_init(&video->lock);
+ ret = media_entity_init(&video->video_dev.entity,
+ 1, &video->pad, 0);
+ if (ret < 0)
+ return ret;
+
+ video_set_drvdata(&video->video_dev, video);
+
+ return 0;
+}
+
+/* vpfe video device register function */
+int vpfe_video_register(struct vpfe_video_device *video,
+ struct v4l2_device *vdev)
+{
+ int ret;
+
+ video->video_dev.v4l2_dev = vdev;
+
+ ret = video_register_device(&video->video_dev, VFL_TYPE_GRABBER, -1);
+ if (ret < 0)
+ pr_err("%s: could not register video device (%d)\n",
+ __func__, ret);
+ return ret;
+}
+
+/* vpfe video device unregister function */
+void vpfe_video_unregister(struct vpfe_video_device *video)
+{
+ if (video_is_registered(&video->video_dev)) {
+ video_unregister_device(&video->video_dev);
+ media_entity_cleanup(&video->video_dev.entity);
+ }
+}
diff --git a/drivers/staging/media/davinci_vpfe/vpfe_video.h b/drivers/staging/media/davinci_vpfe/vpfe_video.h
new file mode 100644
index 000000000..1b1b6c4a5
--- /dev/null
+++ b/drivers/staging/media/davinci_vpfe/vpfe_video.h
@@ -0,0 +1,153 @@
+/*
+ * Copyright (C) 2012 Texas Instruments 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 version 2.
+ *
+ * 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
+ *
+ * Contributors:
+ * Manjunath Hadli <manjunath.hadli@ti.com>
+ * Prabhakar Lad <prabhakar.lad@ti.com>
+ */
+
+#ifndef _DAVINCI_VPFE_VIDEO_H
+#define _DAVINCI_VPFE_VIDEO_H
+
+#include <media/videobuf2-dma-contig.h>
+
+struct vpfe_device;
+
+/*
+ * struct vpfe_video_operations - VPFE video operations
+ * @queue: Resume streaming when a buffer is queued. Called on VIDIOC_QBUF
+ * if there was no buffer previously queued.
+ */
+struct vpfe_video_operations {
+ int (*queue)(struct vpfe_device *vpfe_dev, unsigned long addr);
+};
+
+enum vpfe_pipeline_stream_state {
+ VPFE_PIPELINE_STREAM_STOPPED = 0,
+ VPFE_PIPELINE_STREAM_CONTINUOUS = 1,
+ VPFE_PIPELINE_STREAM_SINGLESHOT = 2,
+};
+
+enum vpfe_video_state {
+ /* indicates that buffer is not queued */
+ VPFE_VIDEO_BUFFER_NOT_QUEUED = 0,
+ /* indicates that buffer is queued */
+ VPFE_VIDEO_BUFFER_QUEUED = 1,
+};
+
+struct vpfe_pipeline {
+ /* media pipeline */
+ struct media_pipeline *pipe;
+ /* state of the pipeline, continuous,
+ * single-shot or stopped
+ */
+ enum vpfe_pipeline_stream_state state;
+ /* number of active input video entities */
+ unsigned int input_num;
+ /* number of active output video entities */
+ unsigned int output_num;
+ /* input video nodes in case of single-shot mode */
+ struct vpfe_video_device *inputs[10];
+ /* capturing video nodes */
+ struct vpfe_video_device *outputs[10];
+};
+
+#define to_vpfe_pipeline(__e) \
+ container_of((__e)->pipe, struct vpfe_pipeline, pipe)
+
+#define to_vpfe_video(vdev) \
+ container_of(vdev, struct vpfe_video_device, video_dev)
+
+struct vpfe_cap_buffer {
+ struct vb2_buffer vb;
+ struct list_head list;
+};
+
+struct vpfe_video_device {
+ /* vpfe device */
+ struct vpfe_device *vpfe_dev;
+ /* video dev */
+ struct video_device video_dev;
+ /* media pad of video entity */
+ struct media_pad pad;
+ /* video operations supported by video device */
+ const struct vpfe_video_operations *ops;
+ /* type of the video buffers used by user */
+ enum v4l2_buf_type type;
+ /* Indicates id of the field which is being captured */
+ u32 field_id;
+ /* pipeline for which video device is part of */
+ struct vpfe_pipeline pipe;
+ /* Indicates whether streaming started */
+ u8 started;
+ /* Indicates state of the stream */
+ unsigned int state;
+ /* current input at the sub device */
+ int current_input;
+ /*
+ * This field keeps track of type of buffer exchange mechanism
+ * user has selected
+ */
+ enum v4l2_memory memory;
+ /* number of open instances of the channel */
+ u32 usrs;
+ /* flag to indicate whether decoder is initialized */
+ u8 initialized;
+ /* skip frame count */
+ u8 skip_frame_count;
+ /* skip frame count init value */
+ u8 skip_frame_count_init;
+ /* time per frame for skipping */
+ struct v4l2_fract timeperframe;
+ /* ptr to currently selected sub device */
+ struct vpfe_ext_subdev_info *current_ext_subdev;
+ /* Pointer pointing to current vpfe_cap_buffer */
+ struct vpfe_cap_buffer *cur_frm;
+ /* Pointer pointing to next vpfe_cap_buffer */
+ struct vpfe_cap_buffer *next_frm;
+ /* Used to store pixel format */
+ struct v4l2_format fmt;
+ struct vb2_queue buffer_queue;
+ /* allocator-specific contexts for each plane */
+ struct vb2_alloc_ctx *alloc_ctx;
+ /* Queue of filled frames */
+ struct list_head dma_queue;
+ spinlock_t irqlock;
+ /* IRQ lock for DMA queue */
+ spinlock_t dma_queue_lock;
+ /* lock used to access this structure */
+ struct mutex lock;
+ /* number of users performing IO */
+ u32 io_usrs;
+ /* Currently selected or default standard */
+ v4l2_std_id stdid;
+ /*
+ * offset where second field starts from the starting of the
+ * buffer for field separated YCbCr formats
+ */
+ u32 field_off;
+};
+
+int vpfe_video_is_pipe_ready(struct vpfe_pipeline *pipe);
+void vpfe_video_unregister(struct vpfe_video_device *video);
+int vpfe_video_register(struct vpfe_video_device *video,
+ struct v4l2_device *vdev);
+int vpfe_video_init(struct vpfe_video_device *video, const char *name);
+void vpfe_video_process_buffer_complete(struct vpfe_video_device *video);
+void vpfe_video_schedule_bottom_field(struct vpfe_video_device *video);
+void vpfe_video_schedule_next_buffer(struct vpfe_video_device *video);
+
+#endif /* _DAVINCI_VPFE_VIDEO_H */
diff --git a/drivers/staging/media/dt3155v4l/Kconfig b/drivers/staging/media/dt3155v4l/Kconfig
new file mode 100644
index 000000000..2d496001b
--- /dev/null
+++ b/drivers/staging/media/dt3155v4l/Kconfig
@@ -0,0 +1,29 @@
+config VIDEO_DT3155
+ tristate "DT3155 frame grabber, Video4Linux interface"
+ depends on PCI && VIDEO_DEV && VIDEO_V4L2
+ depends on HAS_DMA
+ select VIDEOBUF2_DMA_CONTIG
+ default n
+ ---help---
+ Enables dt3155 device driver for the DataTranslation DT3155 frame grabber.
+ Say Y here if you have this hardware.
+ In doubt, say N.
+
+ To compile this driver as a module, choose M here: the
+ module will be called dt3155v4l.
+
+config DT3155_CCIR
+ bool "Selects CCIR/50Hz vertical refresh"
+ depends on VIDEO_DT3155
+ default y
+ ---help---
+ Select it for CCIR/50Hz (European region),
+ or leave it unselected for RS-170/60Hz (North America).
+
+config DT3155_STREAMING
+ bool "Selects streaming capture method"
+ depends on VIDEO_DT3155
+ default y
+ ---help---
+ Select it if you want to use streaming of memory mapped buffers
+ or leave it unselected if you want to use read method (one copy more).
diff --git a/drivers/staging/media/dt3155v4l/Makefile b/drivers/staging/media/dt3155v4l/Makefile
new file mode 100644
index 000000000..ce7a3ec2f
--- /dev/null
+++ b/drivers/staging/media/dt3155v4l/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_VIDEO_DT3155) += dt3155v4l.o
diff --git a/drivers/staging/media/dt3155v4l/dt3155v4l.c b/drivers/staging/media/dt3155v4l/dt3155v4l.c
new file mode 100644
index 000000000..52a8ffe56
--- /dev/null
+++ b/drivers/staging/media/dt3155v4l/dt3155v4l.c
@@ -0,0 +1,981 @@
+/***************************************************************************
+ * Copyright (C) 2006-2010 by Marin Mitov *
+ * mitov@issp.bas.bg *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ ***************************************************************************/
+
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/stringify.h>
+#include <linux/delay.h>
+#include <linux/kthread.h>
+#include <linux/slab.h>
+#include <media/v4l2-dev.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-common.h>
+#include <media/videobuf2-dma-contig.h>
+
+#include "dt3155v4l.h"
+
+#define DT3155_DEVICE_ID 0x1223
+
+/* DT3155_CHUNK_SIZE is 4M (2^22) 8 full size buffers */
+#define DT3155_CHUNK_SIZE (1U << 22)
+
+#define DT3155_COH_FLAGS (GFP_KERNEL | GFP_DMA32 | __GFP_COLD | __GFP_NOWARN)
+
+#define DT3155_BUF_SIZE (768 * 576)
+
+#ifdef CONFIG_DT3155_STREAMING
+#define DT3155_CAPTURE_METHOD V4L2_CAP_STREAMING
+#else
+#define DT3155_CAPTURE_METHOD V4L2_CAP_READWRITE
+#endif
+
+/* global initializers (for all boards) */
+#ifdef CONFIG_DT3155_CCIR
+static const u8 csr2_init = VT_50HZ;
+#define DT3155_CURRENT_NORM V4L2_STD_625_50
+static const unsigned int img_width = 768;
+static const unsigned int img_height = 576;
+static const unsigned int frames_per_sec = 25;
+static const struct v4l2_fmtdesc frame_std[] = {
+ {
+ .index = 0,
+ .type = V4L2_BUF_TYPE_VIDEO_CAPTURE,
+ .flags = 0,
+ .description = "CCIR/50Hz 8 bits gray",
+ .pixelformat = V4L2_PIX_FMT_GREY,
+ },
+};
+#else
+static const u8 csr2_init = VT_60HZ;
+#define DT3155_CURRENT_NORM V4L2_STD_525_60
+static const unsigned int img_width = 640;
+static const unsigned int img_height = 480;
+static const unsigned int frames_per_sec = 30;
+static const struct v4l2_fmtdesc frame_std[] = {
+ {
+ .index = 0,
+ .type = V4L2_BUF_TYPE_VIDEO_CAPTURE,
+ .flags = 0,
+ .description = "RS-170/60Hz 8 bits gray",
+ .pixelformat = V4L2_PIX_FMT_GREY,
+ },
+};
+#endif
+
+#define NUM_OF_FORMATS ARRAY_SIZE(frame_std)
+
+static u8 config_init = ACQ_MODE_EVEN;
+
+/**
+ * read_i2c_reg - reads an internal i2c register
+ *
+ * @addr: dt3155 mmio base address
+ * @index: index (internal address) of register to read
+ * @data: pointer to byte the read data will be placed in
+ *
+ * returns: zero on success or error code
+ *
+ * This function starts reading the specified (by index) register
+ * and busy waits for the process to finish. The result is placed
+ * in a byte pointed by data.
+ */
+static int
+read_i2c_reg(void __iomem *addr, u8 index, u8 *data)
+{
+ u32 tmp = index;
+
+ iowrite32((tmp<<17) | IIC_READ, addr + IIC_CSR2);
+ mmiowb();
+ udelay(45); /* wait at least 43 usec for NEW_CYCLE to clear */
+ if (ioread32(addr + IIC_CSR2) & NEW_CYCLE)
+ return -EIO; /* error: NEW_CYCLE not cleared */
+ tmp = ioread32(addr + IIC_CSR1);
+ if (tmp & DIRECT_ABORT) {
+ /* reset DIRECT_ABORT bit */
+ iowrite32(DIRECT_ABORT, addr + IIC_CSR1);
+ return -EIO; /* error: DIRECT_ABORT set */
+ }
+ *data = tmp>>24;
+ return 0;
+}
+
+/**
+ * write_i2c_reg - writes to an internal i2c register
+ *
+ * @addr: dt3155 mmio base address
+ * @index: index (internal address) of register to read
+ * @data: data to be written
+ *
+ * returns: zero on success or error code
+ *
+ * This function starts writting the specified (by index) register
+ * and busy waits for the process to finish.
+ */
+static int
+write_i2c_reg(void __iomem *addr, u8 index, u8 data)
+{
+ u32 tmp = index;
+
+ iowrite32((tmp<<17) | IIC_WRITE | data, addr + IIC_CSR2);
+ mmiowb();
+ udelay(65); /* wait at least 63 usec for NEW_CYCLE to clear */
+ if (ioread32(addr + IIC_CSR2) & NEW_CYCLE)
+ return -EIO; /* error: NEW_CYCLE not cleared */
+ if (ioread32(addr + IIC_CSR1) & DIRECT_ABORT) {
+ /* reset DIRECT_ABORT bit */
+ iowrite32(DIRECT_ABORT, addr + IIC_CSR1);
+ return -EIO; /* error: DIRECT_ABORT set */
+ }
+ return 0;
+}
+
+/**
+ * write_i2c_reg_nowait - writes to an internal i2c register
+ *
+ * @addr: dt3155 mmio base address
+ * @index: index (internal address) of register to read
+ * @data: data to be written
+ *
+ * This function starts writting the specified (by index) register
+ * and then returns.
+ */
+static void write_i2c_reg_nowait(void __iomem *addr, u8 index, u8 data)
+{
+ u32 tmp = index;
+
+ iowrite32((tmp<<17) | IIC_WRITE | data, addr + IIC_CSR2);
+ mmiowb();
+}
+
+/**
+ * wait_i2c_reg - waits the read/write to finish
+ *
+ * @addr: dt3155 mmio base address
+ *
+ * returns: zero on success or error code
+ *
+ * This function waits reading/writting to finish.
+ */
+static int wait_i2c_reg(void __iomem *addr)
+{
+ if (ioread32(addr + IIC_CSR2) & NEW_CYCLE)
+ udelay(65); /* wait at least 63 usec for NEW_CYCLE to clear */
+ if (ioread32(addr + IIC_CSR2) & NEW_CYCLE)
+ return -EIO; /* error: NEW_CYCLE not cleared */
+ if (ioread32(addr + IIC_CSR1) & DIRECT_ABORT) {
+ /* reset DIRECT_ABORT bit */
+ iowrite32(DIRECT_ABORT, addr + IIC_CSR1);
+ return -EIO; /* error: DIRECT_ABORT set */
+ }
+ return 0;
+}
+
+static int
+dt3155_start_acq(struct dt3155_priv *pd)
+{
+ struct vb2_buffer *vb = pd->curr_buf;
+ dma_addr_t dma_addr;
+
+ dma_addr = vb2_dma_contig_plane_dma_addr(vb, 0);
+ iowrite32(dma_addr, pd->regs + EVEN_DMA_START);
+ iowrite32(dma_addr + img_width, pd->regs + ODD_DMA_START);
+ iowrite32(img_width, pd->regs + EVEN_DMA_STRIDE);
+ iowrite32(img_width, pd->regs + ODD_DMA_STRIDE);
+ /* enable interrupts, clear all irq flags */
+ iowrite32(FLD_START_EN | FLD_END_ODD_EN | FLD_START |
+ FLD_END_EVEN | FLD_END_ODD, pd->regs + INT_CSR);
+ iowrite32(FIFO_EN | SRST | FLD_CRPT_ODD | FLD_CRPT_EVEN |
+ FLD_DN_ODD | FLD_DN_EVEN | CAP_CONT_EVEN | CAP_CONT_ODD,
+ pd->regs + CSR1);
+ wait_i2c_reg(pd->regs);
+ write_i2c_reg(pd->regs, CONFIG, pd->config);
+ write_i2c_reg(pd->regs, EVEN_CSR, CSR_ERROR | CSR_DONE);
+ write_i2c_reg(pd->regs, ODD_CSR, CSR_ERROR | CSR_DONE);
+
+ /* start the board */
+ write_i2c_reg(pd->regs, CSR2, pd->csr2 | BUSY_EVEN | BUSY_ODD);
+ return 0; /* success */
+}
+
+/*
+ * driver-specific callbacks (vb2_ops)
+ */
+static int
+dt3155_queue_setup(struct vb2_queue *q, const struct v4l2_format *fmt,
+ unsigned int *num_buffers, unsigned int *num_planes,
+ unsigned int sizes[], void *alloc_ctxs[])
+
+{
+ struct dt3155_priv *pd = vb2_get_drv_priv(q);
+ void *ret;
+
+ if (*num_buffers == 0)
+ *num_buffers = 1;
+ *num_planes = 1;
+ sizes[0] = img_width * img_height;
+ if (pd->q->alloc_ctx[0])
+ return 0;
+ ret = vb2_dma_contig_init_ctx(&pd->pdev->dev);
+ if (IS_ERR(ret))
+ return PTR_ERR(ret);
+ pd->q->alloc_ctx[0] = ret;
+ return 0;
+}
+
+static void
+dt3155_wait_prepare(struct vb2_queue *q)
+{
+ struct dt3155_priv *pd = vb2_get_drv_priv(q);
+
+ mutex_unlock(pd->vdev.lock);
+}
+
+static void
+dt3155_wait_finish(struct vb2_queue *q)
+{
+ struct dt3155_priv *pd = vb2_get_drv_priv(q);
+
+ mutex_lock(pd->vdev.lock);
+}
+
+static int
+dt3155_buf_prepare(struct vb2_buffer *vb)
+{
+ vb2_set_plane_payload(vb, 0, img_width * img_height);
+ return 0;
+}
+
+static void
+dt3155_stop_streaming(struct vb2_queue *q)
+{
+ struct dt3155_priv *pd = vb2_get_drv_priv(q);
+ struct vb2_buffer *vb;
+
+ spin_lock_irq(&pd->lock);
+ while (!list_empty(&pd->dmaq)) {
+ vb = list_first_entry(&pd->dmaq, typeof(*vb), done_entry);
+ list_del(&vb->done_entry);
+ vb2_buffer_done(vb, VB2_BUF_STATE_ERROR);
+ }
+ spin_unlock_irq(&pd->lock);
+ msleep(45); /* irq hendler will stop the hardware */
+}
+
+static void
+dt3155_buf_queue(struct vb2_buffer *vb)
+{
+ struct dt3155_priv *pd = vb2_get_drv_priv(vb->vb2_queue);
+
+ /* pd->q->streaming = 1 when dt3155_buf_queue() is invoked */
+ spin_lock_irq(&pd->lock);
+ if (pd->curr_buf)
+ list_add_tail(&vb->done_entry, &pd->dmaq);
+ else {
+ pd->curr_buf = vb;
+ dt3155_start_acq(pd);
+ }
+ spin_unlock_irq(&pd->lock);
+}
+/*
+ * end driver-specific callbacks
+ */
+
+static const struct vb2_ops q_ops = {
+ .queue_setup = dt3155_queue_setup,
+ .wait_prepare = dt3155_wait_prepare,
+ .wait_finish = dt3155_wait_finish,
+ .buf_prepare = dt3155_buf_prepare,
+ .stop_streaming = dt3155_stop_streaming,
+ .buf_queue = dt3155_buf_queue,
+};
+
+static irqreturn_t
+dt3155_irq_handler_even(int irq, void *dev_id)
+{
+ struct dt3155_priv *ipd = dev_id;
+ struct vb2_buffer *ivb;
+ dma_addr_t dma_addr;
+ u32 tmp;
+
+ tmp = ioread32(ipd->regs + INT_CSR) & (FLD_START | FLD_END_ODD);
+ if (!tmp)
+ return IRQ_NONE; /* not our irq */
+ if ((tmp & FLD_START) && !(tmp & FLD_END_ODD)) {
+ iowrite32(FLD_START_EN | FLD_END_ODD_EN | FLD_START,
+ ipd->regs + INT_CSR);
+ ipd->field_count++;
+ return IRQ_HANDLED; /* start of field irq */
+ }
+ if ((tmp & FLD_START) && (tmp & FLD_END_ODD))
+ ipd->stats.start_before_end++;
+ /* check for corrupted fields */
+/* write_i2c_reg(ipd->regs, EVEN_CSR, CSR_ERROR | CSR_DONE); */
+/* write_i2c_reg(ipd->regs, ODD_CSR, CSR_ERROR | CSR_DONE); */
+ tmp = ioread32(ipd->regs + CSR1) & (FLD_CRPT_EVEN | FLD_CRPT_ODD);
+ if (tmp) {
+ ipd->stats.corrupted_fields++;
+ iowrite32(FIFO_EN | SRST | FLD_CRPT_ODD | FLD_CRPT_EVEN |
+ FLD_DN_ODD | FLD_DN_EVEN |
+ CAP_CONT_EVEN | CAP_CONT_ODD,
+ ipd->regs + CSR1);
+ mmiowb();
+ }
+
+ spin_lock(&ipd->lock);
+ if (ipd->curr_buf) {
+ v4l2_get_timestamp(&ipd->curr_buf->v4l2_buf.timestamp);
+ ipd->curr_buf->v4l2_buf.sequence = (ipd->field_count) >> 1;
+ vb2_buffer_done(ipd->curr_buf, VB2_BUF_STATE_DONE);
+ }
+
+ if (!ipd->q->streaming || list_empty(&ipd->dmaq))
+ goto stop_dma;
+ ivb = list_first_entry(&ipd->dmaq, typeof(*ivb), done_entry);
+ list_del(&ivb->done_entry);
+ ipd->curr_buf = ivb;
+ dma_addr = vb2_dma_contig_plane_dma_addr(ivb, 0);
+ iowrite32(dma_addr, ipd->regs + EVEN_DMA_START);
+ iowrite32(dma_addr + img_width, ipd->regs + ODD_DMA_START);
+ iowrite32(img_width, ipd->regs + EVEN_DMA_STRIDE);
+ iowrite32(img_width, ipd->regs + ODD_DMA_STRIDE);
+ mmiowb();
+ /* enable interrupts, clear all irq flags */
+ iowrite32(FLD_START_EN | FLD_END_ODD_EN | FLD_START |
+ FLD_END_EVEN | FLD_END_ODD, ipd->regs + INT_CSR);
+ spin_unlock(&ipd->lock);
+ return IRQ_HANDLED;
+
+stop_dma:
+ ipd->curr_buf = NULL;
+ /* stop the board */
+ write_i2c_reg_nowait(ipd->regs, CSR2, ipd->csr2);
+ iowrite32(FIFO_EN | SRST | FLD_CRPT_ODD | FLD_CRPT_EVEN |
+ FLD_DN_ODD | FLD_DN_EVEN, ipd->regs + CSR1);
+ /* disable interrupts, clear all irq flags */
+ iowrite32(FLD_START | FLD_END_EVEN | FLD_END_ODD, ipd->regs + INT_CSR);
+ spin_unlock(&ipd->lock);
+ return IRQ_HANDLED;
+}
+
+static int
+dt3155_open(struct file *filp)
+{
+ int ret = 0;
+ struct dt3155_priv *pd = video_drvdata(filp);
+
+ if (mutex_lock_interruptible(&pd->mux))
+ return -ERESTARTSYS;
+ if (!pd->users) {
+ pd->q = kzalloc(sizeof(*pd->q), GFP_KERNEL);
+ if (!pd->q) {
+ ret = -ENOMEM;
+ goto err_alloc_queue;
+ }
+ pd->q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ pd->q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
+ pd->q->io_modes = VB2_READ | VB2_MMAP;
+ pd->q->ops = &q_ops;
+ pd->q->mem_ops = &vb2_dma_contig_memops;
+ pd->q->drv_priv = pd;
+ pd->curr_buf = NULL;
+ pd->field_count = 0;
+ ret = vb2_queue_init(pd->q);
+ if (ret < 0)
+ goto err_request_irq;
+ INIT_LIST_HEAD(&pd->dmaq);
+ spin_lock_init(&pd->lock);
+ /* disable all irqs, clear all irq flags */
+ iowrite32(FLD_START | FLD_END_EVEN | FLD_END_ODD,
+ pd->regs + INT_CSR);
+ ret = request_irq(pd->pdev->irq, dt3155_irq_handler_even,
+ IRQF_SHARED, DT3155_NAME, pd);
+ if (ret)
+ goto err_request_irq;
+ }
+ pd->users++;
+ mutex_unlock(&pd->mux);
+ return 0; /* success */
+err_request_irq:
+ kfree(pd->q);
+ pd->q = NULL;
+err_alloc_queue:
+ mutex_unlock(&pd->mux);
+ return ret;
+}
+
+static int
+dt3155_release(struct file *filp)
+{
+ struct dt3155_priv *pd = video_drvdata(filp);
+
+ mutex_lock(&pd->mux);
+ pd->users--;
+ BUG_ON(pd->users < 0);
+ if (!pd->users) {
+ vb2_queue_release(pd->q);
+ free_irq(pd->pdev->irq, pd);
+ if (pd->q->alloc_ctx[0])
+ vb2_dma_contig_cleanup_ctx(pd->q->alloc_ctx[0]);
+ kfree(pd->q);
+ pd->q = NULL;
+ }
+ mutex_unlock(&pd->mux);
+ return 0;
+}
+
+static ssize_t
+dt3155_read(struct file *filp, char __user *user, size_t size, loff_t *loff)
+{
+ struct dt3155_priv *pd = video_drvdata(filp);
+ ssize_t res;
+
+ if (mutex_lock_interruptible(&pd->mux))
+ return -ERESTARTSYS;
+ res = vb2_read(pd->q, user, size, loff, filp->f_flags & O_NONBLOCK);
+ mutex_unlock(&pd->mux);
+ return res;
+}
+
+static unsigned int
+dt3155_poll(struct file *filp, struct poll_table_struct *polltbl)
+{
+ struct dt3155_priv *pd = video_drvdata(filp);
+ unsigned int res;
+
+ mutex_lock(&pd->mux);
+ res = vb2_poll(pd->q, filp, polltbl);
+ mutex_unlock(&pd->mux);
+ return res;
+}
+
+static int
+dt3155_mmap(struct file *filp, struct vm_area_struct *vma)
+{
+ struct dt3155_priv *pd = video_drvdata(filp);
+ int res;
+
+ if (mutex_lock_interruptible(&pd->mux))
+ return -ERESTARTSYS;
+ res = vb2_mmap(pd->q, vma);
+ mutex_unlock(&pd->mux);
+ return res;
+}
+
+static const struct v4l2_file_operations dt3155_fops = {
+ .owner = THIS_MODULE,
+ .open = dt3155_open,
+ .release = dt3155_release,
+ .read = dt3155_read,
+ .poll = dt3155_poll,
+ .unlocked_ioctl = video_ioctl2, /* V4L2 ioctl handler */
+ .mmap = dt3155_mmap,
+};
+
+static int
+dt3155_ioc_streamon(struct file *filp, void *p, enum v4l2_buf_type type)
+{
+ struct dt3155_priv *pd = video_drvdata(filp);
+
+ return vb2_streamon(pd->q, type);
+}
+
+static int
+dt3155_ioc_streamoff(struct file *filp, void *p, enum v4l2_buf_type type)
+{
+ struct dt3155_priv *pd = video_drvdata(filp);
+
+ return vb2_streamoff(pd->q, type);
+}
+
+static int
+dt3155_ioc_querycap(struct file *filp, void *p, struct v4l2_capability *cap)
+{
+ struct dt3155_priv *pd = video_drvdata(filp);
+
+ strcpy(cap->driver, DT3155_NAME);
+ strcpy(cap->card, DT3155_NAME " frame grabber");
+ sprintf(cap->bus_info, "PCI:%s", pci_name(pd->pdev));
+ cap->device_caps = V4L2_CAP_VIDEO_CAPTURE |
+ DT3155_CAPTURE_METHOD;
+ cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS;
+ return 0;
+}
+
+static int
+dt3155_ioc_enum_fmt_vid_cap(struct file *filp, void *p, struct v4l2_fmtdesc *f)
+{
+ if (f->index >= NUM_OF_FORMATS)
+ return -EINVAL;
+ *f = frame_std[f->index];
+ return 0;
+}
+
+static int
+dt3155_ioc_g_fmt_vid_cap(struct file *filp, void *p, struct v4l2_format *f)
+{
+ if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+ f->fmt.pix.width = img_width;
+ f->fmt.pix.height = img_height;
+ f->fmt.pix.pixelformat = V4L2_PIX_FMT_GREY;
+ f->fmt.pix.field = V4L2_FIELD_NONE;
+ f->fmt.pix.bytesperline = f->fmt.pix.width;
+ f->fmt.pix.sizeimage = f->fmt.pix.width * f->fmt.pix.height;
+ f->fmt.pix.colorspace = 0;
+ f->fmt.pix.priv = 0;
+ return 0;
+}
+
+static int
+dt3155_ioc_try_fmt_vid_cap(struct file *filp, void *p, struct v4l2_format *f)
+{
+ if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+ if (f->fmt.pix.width == img_width &&
+ f->fmt.pix.height == img_height &&
+ f->fmt.pix.pixelformat == V4L2_PIX_FMT_GREY &&
+ f->fmt.pix.field == V4L2_FIELD_NONE &&
+ f->fmt.pix.bytesperline == f->fmt.pix.width &&
+ f->fmt.pix.sizeimage == f->fmt.pix.width * f->fmt.pix.height)
+ return 0;
+ else
+ return -EINVAL;
+}
+
+static int
+dt3155_ioc_s_fmt_vid_cap(struct file *filp, void *p, struct v4l2_format *f)
+{
+ return dt3155_ioc_g_fmt_vid_cap(filp, p, f);
+}
+
+static int
+dt3155_ioc_reqbufs(struct file *filp, void *p, struct v4l2_requestbuffers *b)
+{
+ struct dt3155_priv *pd = video_drvdata(filp);
+
+ return vb2_reqbufs(pd->q, b);
+}
+
+static int
+dt3155_ioc_querybuf(struct file *filp, void *p, struct v4l2_buffer *b)
+{
+ struct dt3155_priv *pd = video_drvdata(filp);
+
+ return vb2_querybuf(pd->q, b);
+}
+
+static int
+dt3155_ioc_qbuf(struct file *filp, void *p, struct v4l2_buffer *b)
+{
+ struct dt3155_priv *pd = video_drvdata(filp);
+
+ return vb2_qbuf(pd->q, b);
+}
+
+static int
+dt3155_ioc_dqbuf(struct file *filp, void *p, struct v4l2_buffer *b)
+{
+ struct dt3155_priv *pd = video_drvdata(filp);
+
+ return vb2_dqbuf(pd->q, b, filp->f_flags & O_NONBLOCK);
+}
+
+static int
+dt3155_ioc_querystd(struct file *filp, void *p, v4l2_std_id *norm)
+{
+ *norm = DT3155_CURRENT_NORM;
+ return 0;
+}
+
+static int
+dt3155_ioc_g_std(struct file *filp, void *p, v4l2_std_id *norm)
+{
+ *norm = DT3155_CURRENT_NORM;
+ return 0;
+}
+
+static int
+dt3155_ioc_s_std(struct file *filp, void *p, v4l2_std_id norm)
+{
+ if (norm & DT3155_CURRENT_NORM)
+ return 0;
+ return -EINVAL;
+}
+
+static int
+dt3155_ioc_enum_input(struct file *filp, void *p, struct v4l2_input *input)
+{
+ if (input->index)
+ return -EINVAL;
+ strcpy(input->name, "Coax in");
+ input->type = V4L2_INPUT_TYPE_CAMERA;
+ /*
+ * FIXME: input->std = 0 according to v4l2 API
+ * VIDIOC_G_STD, VIDIOC_S_STD, VIDIOC_QUERYSTD and VIDIOC_ENUMSTD
+ * should return -EINVAL
+ */
+ input->std = DT3155_CURRENT_NORM;
+ input->status = 0;/* FIXME: add sync detection & V4L2_IN_ST_NO_H_LOCK */
+ return 0;
+}
+
+static int
+dt3155_ioc_g_input(struct file *filp, void *p, unsigned int *i)
+{
+ *i = 0;
+ return 0;
+}
+
+static int
+dt3155_ioc_s_input(struct file *filp, void *p, unsigned int i)
+{
+ if (i)
+ return -EINVAL;
+ return 0;
+}
+
+static int
+dt3155_ioc_g_parm(struct file *filp, void *p, struct v4l2_streamparm *parms)
+{
+ if (parms->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+ parms->parm.capture.capability = V4L2_CAP_TIMEPERFRAME;
+ parms->parm.capture.capturemode = 0;
+ parms->parm.capture.timeperframe.numerator = 1001;
+ parms->parm.capture.timeperframe.denominator = frames_per_sec * 1000;
+ parms->parm.capture.extendedmode = 0;
+ parms->parm.capture.readbuffers = 1; /* FIXME: 2 buffers? */
+ return 0;
+}
+
+static int
+dt3155_ioc_s_parm(struct file *filp, void *p, struct v4l2_streamparm *parms)
+{
+ if (parms->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+ parms->parm.capture.capability = V4L2_CAP_TIMEPERFRAME;
+ parms->parm.capture.capturemode = 0;
+ parms->parm.capture.timeperframe.numerator = 1001;
+ parms->parm.capture.timeperframe.denominator = frames_per_sec * 1000;
+ parms->parm.capture.extendedmode = 0;
+ parms->parm.capture.readbuffers = 1; /* FIXME: 2 buffers? */
+ return 0;
+}
+
+static const struct v4l2_ioctl_ops dt3155_ioctl_ops = {
+ .vidioc_streamon = dt3155_ioc_streamon,
+ .vidioc_streamoff = dt3155_ioc_streamoff,
+ .vidioc_querycap = dt3155_ioc_querycap,
+/*
+ .vidioc_g_priority = dt3155_ioc_g_priority,
+ .vidioc_s_priority = dt3155_ioc_s_priority,
+*/
+ .vidioc_enum_fmt_vid_cap = dt3155_ioc_enum_fmt_vid_cap,
+ .vidioc_try_fmt_vid_cap = dt3155_ioc_try_fmt_vid_cap,
+ .vidioc_g_fmt_vid_cap = dt3155_ioc_g_fmt_vid_cap,
+ .vidioc_s_fmt_vid_cap = dt3155_ioc_s_fmt_vid_cap,
+ .vidioc_reqbufs = dt3155_ioc_reqbufs,
+ .vidioc_querybuf = dt3155_ioc_querybuf,
+ .vidioc_qbuf = dt3155_ioc_qbuf,
+ .vidioc_dqbuf = dt3155_ioc_dqbuf,
+ .vidioc_querystd = dt3155_ioc_querystd,
+ .vidioc_g_std = dt3155_ioc_g_std,
+ .vidioc_s_std = dt3155_ioc_s_std,
+ .vidioc_enum_input = dt3155_ioc_enum_input,
+ .vidioc_g_input = dt3155_ioc_g_input,
+ .vidioc_s_input = dt3155_ioc_s_input,
+/*
+ .vidioc_queryctrl = dt3155_ioc_queryctrl,
+ .vidioc_g_ctrl = dt3155_ioc_g_ctrl,
+ .vidioc_s_ctrl = dt3155_ioc_s_ctrl,
+ .vidioc_querymenu = dt3155_ioc_querymenu,
+ .vidioc_g_ext_ctrls = dt3155_ioc_g_ext_ctrls,
+ .vidioc_s_ext_ctrls = dt3155_ioc_s_ext_ctrls,
+*/
+ .vidioc_g_parm = dt3155_ioc_g_parm,
+ .vidioc_s_parm = dt3155_ioc_s_parm,
+/*
+ .vidioc_cropcap = dt3155_ioc_cropcap,
+ .vidioc_g_crop = dt3155_ioc_g_crop,
+ .vidioc_s_crop = dt3155_ioc_s_crop,
+ .vidioc_enum_framesizes = dt3155_ioc_enum_framesizes,
+ .vidioc_enum_frameintervals = dt3155_ioc_enum_frameintervals,
+*/
+};
+
+static int
+dt3155_init_board(struct pci_dev *pdev)
+{
+ struct dt3155_priv *pd = pci_get_drvdata(pdev);
+ void *buf_cpu;
+ dma_addr_t buf_dma;
+ int i;
+ u8 tmp;
+
+ pci_set_master(pdev); /* dt3155 needs it */
+
+ /* resetting the adapter */
+ iowrite32(FLD_CRPT_ODD | FLD_CRPT_EVEN | FLD_DN_ODD | FLD_DN_EVEN,
+ pd->regs + CSR1);
+ mmiowb();
+ msleep(20);
+
+ /* initializing adaper registers */
+ iowrite32(FIFO_EN | SRST, pd->regs + CSR1);
+ mmiowb();
+ iowrite32(0xEEEEEE01, pd->regs + EVEN_PIXEL_FMT);
+ iowrite32(0xEEEEEE01, pd->regs + ODD_PIXEL_FMT);
+ iowrite32(0x00000020, pd->regs + FIFO_TRIGER);
+ iowrite32(0x00000103, pd->regs + XFER_MODE);
+ iowrite32(0, pd->regs + RETRY_WAIT_CNT);
+ iowrite32(0, pd->regs + INT_CSR);
+ iowrite32(1, pd->regs + EVEN_FLD_MASK);
+ iowrite32(1, pd->regs + ODD_FLD_MASK);
+ iowrite32(0, pd->regs + MASK_LENGTH);
+ iowrite32(0x0005007C, pd->regs + FIFO_FLAG_CNT);
+ iowrite32(0x01010101, pd->regs + IIC_CLK_DUR);
+ mmiowb();
+
+ /* verifying that we have a DT3155 board (not just a SAA7116 chip) */
+ read_i2c_reg(pd->regs, DT_ID, &tmp);
+ if (tmp != DT3155_ID)
+ return -ENODEV;
+
+ /* initialize AD LUT */
+ write_i2c_reg(pd->regs, AD_ADDR, 0);
+ for (i = 0; i < 256; i++)
+ write_i2c_reg(pd->regs, AD_LUT, i);
+
+ /* initialize ADC references */
+ /* FIXME: pos_ref & neg_ref depend on VT_50HZ */
+ write_i2c_reg(pd->regs, AD_ADDR, AD_CMD_REG);
+ write_i2c_reg(pd->regs, AD_CMD, VIDEO_CNL_1 | SYNC_CNL_1 | SYNC_LVL_3);
+ write_i2c_reg(pd->regs, AD_ADDR, AD_POS_REF);
+ write_i2c_reg(pd->regs, AD_CMD, 34);
+ write_i2c_reg(pd->regs, AD_ADDR, AD_NEG_REF);
+ write_i2c_reg(pd->regs, AD_CMD, 0);
+
+ /* initialize PM LUT */
+ write_i2c_reg(pd->regs, CONFIG, pd->config | PM_LUT_PGM);
+ for (i = 0; i < 256; i++) {
+ write_i2c_reg(pd->regs, PM_LUT_ADDR, i);
+ write_i2c_reg(pd->regs, PM_LUT_DATA, i);
+ }
+ write_i2c_reg(pd->regs, CONFIG, pd->config | PM_LUT_PGM | PM_LUT_SEL);
+ for (i = 0; i < 256; i++) {
+ write_i2c_reg(pd->regs, PM_LUT_ADDR, i);
+ write_i2c_reg(pd->regs, PM_LUT_DATA, i);
+ }
+ write_i2c_reg(pd->regs, CONFIG, pd->config); /* ACQ_MODE_EVEN */
+
+ /* select channel 1 for input and set sync level */
+ write_i2c_reg(pd->regs, AD_ADDR, AD_CMD_REG);
+ write_i2c_reg(pd->regs, AD_CMD, VIDEO_CNL_1 | SYNC_CNL_1 | SYNC_LVL_3);
+
+ /* allocate memory, and initialize the DMA machine */
+ buf_cpu = dma_alloc_coherent(&pdev->dev, DT3155_BUF_SIZE, &buf_dma,
+ GFP_KERNEL);
+ if (!buf_cpu)
+ return -ENOMEM;
+ iowrite32(buf_dma, pd->regs + EVEN_DMA_START);
+ iowrite32(buf_dma, pd->regs + ODD_DMA_START);
+ iowrite32(0, pd->regs + EVEN_DMA_STRIDE);
+ iowrite32(0, pd->regs + ODD_DMA_STRIDE);
+
+ /* Perform a pseudo even field acquire */
+ iowrite32(FIFO_EN | SRST | CAP_CONT_ODD, pd->regs + CSR1);
+ write_i2c_reg(pd->regs, CSR2, pd->csr2 | SYNC_SNTL);
+ write_i2c_reg(pd->regs, CONFIG, pd->config);
+ write_i2c_reg(pd->regs, EVEN_CSR, CSR_SNGL);
+ write_i2c_reg(pd->regs, CSR2, pd->csr2 | BUSY_EVEN | SYNC_SNTL);
+ msleep(100);
+ read_i2c_reg(pd->regs, CSR2, &tmp);
+ write_i2c_reg(pd->regs, EVEN_CSR, CSR_ERROR | CSR_SNGL | CSR_DONE);
+ write_i2c_reg(pd->regs, ODD_CSR, CSR_ERROR | CSR_SNGL | CSR_DONE);
+ write_i2c_reg(pd->regs, CSR2, pd->csr2);
+ iowrite32(FIFO_EN | SRST | FLD_DN_EVEN | FLD_DN_ODD, pd->regs + CSR1);
+
+ /* deallocate memory */
+ dma_free_coherent(&pdev->dev, DT3155_BUF_SIZE, buf_cpu, buf_dma);
+ if (tmp & BUSY_EVEN)
+ return -EIO;
+ return 0;
+}
+
+static struct video_device dt3155_vdev = {
+ .name = DT3155_NAME,
+ .fops = &dt3155_fops,
+ .ioctl_ops = &dt3155_ioctl_ops,
+ .minor = -1,
+ .release = video_device_release_empty,
+ .tvnorms = DT3155_CURRENT_NORM,
+};
+
+/* same as in drivers/base/dma-coherent.c */
+struct dma_coherent_mem {
+ void *virt_base;
+ dma_addr_t device_base;
+ int size;
+ int flags;
+ unsigned long *bitmap;
+};
+
+static int
+dt3155_alloc_coherent(struct device *dev, size_t size, int flags)
+{
+ struct dma_coherent_mem *mem;
+ dma_addr_t dev_base;
+ int pages = size >> PAGE_SHIFT;
+ int bitmap_size = BITS_TO_LONGS(pages) * sizeof(long);
+
+ if ((flags & DMA_MEMORY_MAP) == 0)
+ goto out;
+ if (!size)
+ goto out;
+ if (dev->dma_mem)
+ goto out;
+
+ mem = kzalloc(sizeof(*mem), GFP_KERNEL);
+ if (!mem)
+ goto out;
+ mem->virt_base = dma_alloc_coherent(dev, size, &dev_base,
+ DT3155_COH_FLAGS);
+ if (!mem->virt_base)
+ goto err_alloc_coherent;
+ mem->bitmap = kzalloc(bitmap_size, GFP_KERNEL);
+ if (!mem->bitmap)
+ goto err_bitmap;
+
+ /* coherent_dma_mask is already set to 32 bits */
+ mem->device_base = dev_base;
+ mem->size = pages;
+ mem->flags = flags;
+ dev->dma_mem = mem;
+ return DMA_MEMORY_MAP;
+
+err_bitmap:
+ dma_free_coherent(dev, size, mem->virt_base, dev_base);
+err_alloc_coherent:
+ kfree(mem);
+out:
+ return 0;
+}
+
+static void
+dt3155_free_coherent(struct device *dev)
+{
+ struct dma_coherent_mem *mem = dev->dma_mem;
+
+ if (!mem)
+ return;
+ dev->dma_mem = NULL;
+ dma_free_coherent(dev, mem->size << PAGE_SHIFT,
+ mem->virt_base, mem->device_base);
+ kfree(mem->bitmap);
+ kfree(mem);
+}
+
+static int
+dt3155_probe(struct pci_dev *pdev, const struct pci_device_id *id)
+{
+ int err;
+ struct dt3155_priv *pd;
+
+ err = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32));
+ if (err)
+ return -ENODEV;
+ pd = devm_kzalloc(&pdev->dev, sizeof(*pd), GFP_KERNEL);
+ if (!pd)
+ return -ENOMEM;
+
+ pd->vdev = dt3155_vdev;
+ pci_set_drvdata(pdev, pd); /* for use in dt3155_remove() */
+ video_set_drvdata(&pd->vdev, pd); /* for use in video_fops */
+ pd->users = 0;
+ pd->pdev = pdev;
+ INIT_LIST_HEAD(&pd->dmaq);
+ mutex_init(&pd->mux);
+ pd->vdev.lock = &pd->mux; /* for locking v4l2_file_operations */
+ spin_lock_init(&pd->lock);
+ pd->csr2 = csr2_init;
+ pd->config = config_init;
+ err = pci_enable_device(pdev);
+ if (err)
+ return err;
+ err = pci_request_region(pdev, 0, pci_name(pdev));
+ if (err)
+ goto err_req_region;
+ pd->regs = pci_iomap(pdev, 0, pci_resource_len(pd->pdev, 0));
+ if (!pd->regs) {
+ err = -ENOMEM;
+ goto err_pci_iomap;
+ }
+ err = dt3155_init_board(pdev);
+ if (err)
+ goto err_init_board;
+ err = video_register_device(&pd->vdev, VFL_TYPE_GRABBER, -1);
+ if (err)
+ goto err_init_board;
+ if (dt3155_alloc_coherent(&pdev->dev, DT3155_CHUNK_SIZE,
+ DMA_MEMORY_MAP))
+ dev_info(&pdev->dev, "preallocated 8 buffers\n");
+ dev_info(&pdev->dev, "/dev/video%i is ready\n", pd->vdev.minor);
+ return 0; /* success */
+
+err_init_board:
+ pci_iounmap(pdev, pd->regs);
+err_pci_iomap:
+ pci_release_region(pdev, 0);
+err_req_region:
+ pci_disable_device(pdev);
+ return err;
+}
+
+static void
+dt3155_remove(struct pci_dev *pdev)
+{
+ struct dt3155_priv *pd = pci_get_drvdata(pdev);
+
+ dt3155_free_coherent(&pdev->dev);
+ video_unregister_device(&pd->vdev);
+ pci_iounmap(pdev, pd->regs);
+ pci_release_region(pdev, 0);
+ pci_disable_device(pdev);
+}
+
+static const struct pci_device_id pci_ids[] = {
+ { PCI_DEVICE(PCI_VENDOR_ID_INTEL, DT3155_DEVICE_ID) },
+ { 0, /* zero marks the end */ },
+};
+MODULE_DEVICE_TABLE(pci, pci_ids);
+
+static struct pci_driver pci_driver = {
+ .name = DT3155_NAME,
+ .id_table = pci_ids,
+ .probe = dt3155_probe,
+ .remove = dt3155_remove,
+};
+
+module_pci_driver(pci_driver);
+
+MODULE_DESCRIPTION("video4linux pci-driver for dt3155 frame grabber");
+MODULE_AUTHOR("Marin Mitov <mitov@issp.bas.bg>");
+MODULE_VERSION(DT3155_VERSION);
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/media/dt3155v4l/dt3155v4l.h b/drivers/staging/media/dt3155v4l/dt3155v4l.h
new file mode 100644
index 000000000..96f01a0c7
--- /dev/null
+++ b/drivers/staging/media/dt3155v4l/dt3155v4l.h
@@ -0,0 +1,212 @@
+/***************************************************************************
+ * Copyright (C) 2006-2010 by Marin Mitov *
+ * mitov@issp.bas.bg *
+ * *
+ * This program is free software; you can redistribute it and/or 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. *
+ ***************************************************************************/
+
+/* DT3155 header file */
+#ifndef _DT3155_H_
+#define _DT3155_H_
+
+#ifdef __KERNEL__
+
+#include <linux/pci.h>
+#include <linux/interrupt.h>
+
+#define DT3155_NAME "dt3155"
+#define DT3155_VER_MAJ 1
+#define DT3155_VER_MIN 1
+#define DT3155_VER_EXT 0
+#define DT3155_VERSION __stringify(DT3155_VER_MAJ) "." \
+ __stringify(DT3155_VER_MIN) "." \
+ __stringify(DT3155_VER_EXT)
+
+/* DT3155 Base Register offsets (memory mapped) */
+#define EVEN_DMA_START 0x00
+#define ODD_DMA_START 0x0C
+#define EVEN_DMA_STRIDE 0x18
+#define ODD_DMA_STRIDE 0x24
+#define EVEN_PIXEL_FMT 0x30
+#define ODD_PIXEL_FMT 0x34
+#define FIFO_TRIGER 0x38
+#define XFER_MODE 0x3C
+#define CSR1 0x40
+#define RETRY_WAIT_CNT 0x44
+#define INT_CSR 0x48
+#define EVEN_FLD_MASK 0x4C
+#define ODD_FLD_MASK 0x50
+#define MASK_LENGTH 0x54
+#define FIFO_FLAG_CNT 0x58
+#define IIC_CLK_DUR 0x5C
+#define IIC_CSR1 0x60
+#define IIC_CSR2 0x64
+
+/* DT3155 Internal Registers indexes (i2c/IIC mapped) */
+#define CSR2 0x10
+#define EVEN_CSR 0x11
+#define ODD_CSR 0x12
+#define CONFIG 0x13
+#define DT_ID 0x1F
+#define X_CLIP_START 0x20
+#define Y_CLIP_START 0x22
+#define X_CLIP_END 0x24
+#define Y_CLIP_END 0x26
+#define AD_ADDR 0x30
+#define AD_LUT 0x31
+#define AD_CMD 0x32
+#define DIG_OUT 0x40
+#define PM_LUT_ADDR 0x50
+#define PM_LUT_DATA 0x51
+
+/* AD command register values */
+#define AD_CMD_REG 0x00
+#define AD_POS_REF 0x01
+#define AD_NEG_REF 0x02
+
+/* CSR1 bit masks */
+#define CRPT_DIS 0x00004000
+#define FLD_CRPT_ODD 0x00000200
+#define FLD_CRPT_EVEN 0x00000100
+#define FIFO_EN 0x00000080
+#define SRST 0x00000040
+#define FLD_DN_ODD 0x00000020
+#define FLD_DN_EVEN 0x00000010
+/* These should not be used.
+ * Use CAP_CONT_ODD/EVEN instead
+#define CAP_SNGL_ODD 0x00000008
+#define CAP_SNGL_EVEN 0x00000004
+*/
+#define CAP_CONT_ODD 0x00000002
+#define CAP_CONT_EVEN 0x00000001
+
+/* INT_CSR bit masks */
+#define FLD_START_EN 0x00000400
+#define FLD_END_ODD_EN 0x00000200
+#define FLD_END_EVEN_EN 0x00000100
+#define FLD_START 0x00000004
+#define FLD_END_ODD 0x00000002
+#define FLD_END_EVEN 0x00000001
+
+/* IIC_CSR1 bit masks */
+#define DIRECT_ABORT 0x00000200
+
+/* IIC_CSR2 bit masks */
+#define NEW_CYCLE 0x01000000
+#define DIR_RD 0x00010000
+#define IIC_READ 0x01010000
+#define IIC_WRITE 0x01000000
+
+/* CSR2 bit masks */
+#define DISP_PASS 0x40
+#define BUSY_ODD 0x20
+#define BUSY_EVEN 0x10
+#define SYNC_PRESENT 0x08
+#define VT_50HZ 0x04
+#define SYNC_SNTL 0x02
+#define CHROM_FILT 0x01
+#define VT_60HZ 0x00
+
+/* CSR_EVEN/ODD bit masks */
+#define CSR_ERROR 0x04
+#define CSR_SNGL 0x02
+#define CSR_DONE 0x01
+
+/* CONFIG bit masks */
+#define PM_LUT_PGM 0x80
+#define PM_LUT_SEL 0x40
+#define CLIP_EN 0x20
+#define HSCALE_EN 0x10
+#define EXT_TRIG_UP 0x0C
+#define EXT_TRIG_DOWN 0x04
+#define ACQ_MODE_NEXT 0x02
+#define ACQ_MODE_ODD 0x01
+#define ACQ_MODE_EVEN 0x00
+
+/* AD_CMD bit masks */
+#define VIDEO_CNL_1 0x00
+#define VIDEO_CNL_2 0x40
+#define VIDEO_CNL_3 0x80
+#define VIDEO_CNL_4 0xC0
+#define SYNC_CNL_1 0x00
+#define SYNC_CNL_2 0x10
+#define SYNC_CNL_3 0x20
+#define SYNC_CNL_4 0x30
+#define SYNC_LVL_1 0x00
+#define SYNC_LVL_2 0x04
+#define SYNC_LVL_3 0x08
+#define SYNC_LVL_4 0x0C
+
+/* DT3155 identificator */
+#define DT3155_ID 0x20
+
+#ifdef CONFIG_DT3155_CCIR
+#define DMA_STRIDE 768
+#else
+#define DMA_STRIDE 640
+#endif
+
+/**
+ * struct dt3155_stats - statistics structure
+ *
+ * @free_bufs_empty: no free image buffers
+ * @corrupted_fields: corrupted fields
+ * @dma_map_failed: dma mapping failed
+ * @start_before_end: new started before old ended
+ */
+struct dt3155_stats {
+ int free_bufs_empty;
+ int corrupted_fields;
+ int dma_map_failed;
+ int start_before_end;
+};
+
+/* per board private data structure */
+/**
+ * struct dt3155_priv - private data structure
+ *
+ * @vdev: video_device structure
+ * @pdev: pointer to pci_dev structure
+ * @q pointer to vb2_queue structure
+ * @curr_buf: pointer to curren buffer
+ * @mux: mutex to protect the instance
+ * @dmaq queue for dma buffers
+ * @lock spinlock for dma queue
+ * @field_count fields counter
+ * @stats: statistics structure
+ * @users open count
+ * @regs: local copy of mmio base register
+ * @csr2: local copy of csr2 register
+ * @config: local copy of config register
+ */
+struct dt3155_priv {
+ struct video_device vdev;
+ struct pci_dev *pdev;
+ struct vb2_queue *q;
+ struct vb2_buffer *curr_buf;
+ struct mutex mux;
+ struct list_head dmaq;
+ spinlock_t lock;
+ unsigned int field_count;
+ struct dt3155_stats stats;
+ void __iomem *regs;
+ int users;
+ u8 csr2, config;
+};
+
+#endif /* __KERNEL__ */
+
+#endif /* _DT3155_H_ */
diff --git a/drivers/staging/media/lirc/Kconfig b/drivers/staging/media/lirc/Kconfig
new file mode 100644
index 000000000..6879c4651
--- /dev/null
+++ b/drivers/staging/media/lirc/Kconfig
@@ -0,0 +1,66 @@
+#
+# LIRC driver(s) configuration
+#
+menuconfig LIRC_STAGING
+ bool "Linux Infrared Remote Control IR receiver/transmitter drivers"
+ depends on LIRC
+ help
+ Say Y here, and all supported Linux Infrared Remote Control IR and
+ RF receiver and transmitter drivers will be displayed. When paired
+ with a remote control and the lirc daemon, the receiver drivers
+ allow control of your Linux system via remote control.
+
+if LIRC_STAGING
+
+config LIRC_BT829
+ tristate "BT829 based hardware"
+ depends on LIRC && PCI
+ help
+ Driver for the IR interface on BT829-based hardware
+
+config LIRC_IMON
+ tristate "Legacy SoundGraph iMON Receiver and Display"
+ depends on LIRC && USB
+ help
+ Driver for the original SoundGraph iMON IR Receiver and Display
+
+ Current generation iMON devices use the input layer imon driver.
+
+config LIRC_PARALLEL
+ tristate "Homebrew Parallel Port Receiver"
+ depends on LIRC && PARPORT
+ help
+ Driver for Homebrew Parallel Port Receivers
+
+config LIRC_SASEM
+ tristate "Sasem USB IR Remote"
+ depends on LIRC && USB
+ help
+ Driver for the Sasem OnAir Remocon-V or Dign HV5 HTPC IR/VFD Module
+
+config LIRC_SERIAL
+ tristate "Homebrew Serial Port Receiver"
+ depends on LIRC
+ help
+ Driver for Homebrew Serial Port Receivers
+
+config LIRC_SERIAL_TRANSMITTER
+ bool "Serial Port Transmitter"
+ default y
+ depends on LIRC_SERIAL
+ help
+ Serial Port Transmitter support
+
+config LIRC_SIR
+ tristate "Built-in SIR IrDA port"
+ depends on LIRC
+ help
+ Driver for the SIR IrDA port
+
+config LIRC_ZILOG
+ tristate "Zilog/Hauppauge IR Transmitter"
+ depends on LIRC && I2C
+ help
+ Driver for the Zilog/Hauppauge IR Transmitter, found on
+ PVR-150/500, HVR-1200/1250/1700/1800, HD-PVR and other cards
+endif
diff --git a/drivers/staging/media/lirc/Makefile b/drivers/staging/media/lirc/Makefile
new file mode 100644
index 000000000..5430adf04
--- /dev/null
+++ b/drivers/staging/media/lirc/Makefile
@@ -0,0 +1,12 @@
+# Makefile for the lirc drivers.
+#
+
+# Each configuration option enables a list of files.
+
+obj-$(CONFIG_LIRC_BT829) += lirc_bt829.o
+obj-$(CONFIG_LIRC_IMON) += lirc_imon.o
+obj-$(CONFIG_LIRC_PARALLEL) += lirc_parallel.o
+obj-$(CONFIG_LIRC_SASEM) += lirc_sasem.o
+obj-$(CONFIG_LIRC_SERIAL) += lirc_serial.o
+obj-$(CONFIG_LIRC_SIR) += lirc_sir.o
+obj-$(CONFIG_LIRC_ZILOG) += lirc_zilog.o
diff --git a/drivers/staging/media/lirc/TODO b/drivers/staging/media/lirc/TODO
new file mode 100644
index 000000000..cbea5d84f
--- /dev/null
+++ b/drivers/staging/media/lirc/TODO
@@ -0,0 +1,13 @@
+- All drivers should either be ported to ir-core, or dropped entirely
+ (see drivers/media/IR/mceusb.c vs. lirc_mceusb.c in lirc cvs for an
+ example of a previously completed port).
+
+- lirc_bt829 uses registers on a Mach64 VT, which has a separate kernel
+ framebuffer driver (atyfb) and userland X driver (mach64). It can't
+ simply be converted to a normal PCI driver, but ideally it should be
+ coordinated with the other drivers.
+
+Please send patches to:
+Jarod Wilson <jarod@wilsonet.com>
+Greg Kroah-Hartman <greg@kroah.com>
+
diff --git a/drivers/staging/media/lirc/TODO.lirc_zilog b/drivers/staging/media/lirc/TODO.lirc_zilog
new file mode 100644
index 000000000..a97800a8e
--- /dev/null
+++ b/drivers/staging/media/lirc/TODO.lirc_zilog
@@ -0,0 +1,36 @@
+1. Both ir-kbd-i2c and lirc_zilog provide support for RX events for
+the chips supported by lirc_zilog. Before moving lirc_zilog out of staging:
+
+a. ir-kbd-i2c needs a module parameter added to allow the user to tell
+ ir-kbd-i2c to ignore Z8 IR units.
+
+b. lirc_zilog should provide Rx key presses to the rc core like ir-kbd-i2c
+ does.
+
+
+2. lirc_zilog module ref-counting need examination. It has not been
+verified that cdev and lirc_dev will take the proper module references on
+lirc_zilog to prevent removal of lirc_zilog when the /dev/lircN device node
+is open.
+
+(The good news is ref-counting of lirc_zilog internal structures appears to be
+complete. Testing has shown the cx18 module can be unloaded out from under
+irw + lircd + lirc_dev, with the /dev/lirc0 device node open, with no adverse
+effects. The cx18 module could then be reloaded and irw properly began
+receiving button presses again and ir_send worked without error.)
+
+
+3. Bridge drivers, if able, should provide a chip reset() callback
+to lirc_zilog via struct IR_i2c_init_data. cx18 and ivtv already have routines
+to perform Z8 chip resets via GPIO manipulations. This would allow lirc_zilog
+to bring the chip back to normal when it hangs, in the same places the
+original lirc_pvr150 driver code does. This is not strictly needed, so it
+is not required to move lirc_zilog out of staging.
+
+Note: Both lirc_zilog and ir-kbd-i2c support the Zilog Z8 for IR, as programmed
+and installed on Hauppauge products. When working on either module, developers
+must consider at least the following bridge drivers which mention an IR Rx unit
+at address 0x71 (indicative of a Z8):
+
+ ivtv cx18 hdpvr pvrusb2 bt8xx cx88 saa7134
+
diff --git a/drivers/staging/media/lirc/lirc_bt829.c b/drivers/staging/media/lirc/lirc_bt829.c
new file mode 100644
index 000000000..44f565547
--- /dev/null
+++ b/drivers/staging/media/lirc/lirc_bt829.c
@@ -0,0 +1,404 @@
+/*
+ * Remote control driver for the TV-card based on bt829
+ *
+ * by Leonid Froenchenko <lfroen@galileo.co.il>
+ *
+ * This program is free software; you can redistribute it and/or 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 pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/threads.h>
+#include <linux/sched.h>
+#include <linux/ioport.h>
+#include <linux/pci.h>
+#include <linux/delay.h>
+
+#include <media/lirc_dev.h>
+
+static int poll_main(void);
+static int atir_init_start(void);
+
+static void write_index(unsigned char index, unsigned int value);
+static unsigned int read_index(unsigned char index);
+
+static void do_i2c_start(void);
+static void do_i2c_stop(void);
+
+static void seems_wr_byte(unsigned char al);
+static unsigned char seems_rd_byte(void);
+
+static unsigned int read_index(unsigned char al);
+static void write_index(unsigned char ah, unsigned int edx);
+
+static void cycle_delay(int cycle);
+
+static void do_set_bits(unsigned char bl);
+static unsigned char do_get_bits(void);
+
+#define DATA_PCI_OFF 0x7FFC00
+#define WAIT_CYCLE 20
+
+#define DRIVER_NAME "lirc_bt829"
+
+static bool debug;
+
+static int atir_minor;
+static phys_addr_t pci_addr_phys;
+static unsigned char __iomem *pci_addr_lin;
+
+static struct lirc_driver atir_driver;
+
+static struct pci_dev *do_pci_probe(void)
+{
+ struct pci_dev *my_dev;
+
+ my_dev = pci_get_device(PCI_VENDOR_ID_ATI,
+ PCI_DEVICE_ID_ATI_264VT, NULL);
+ if (my_dev) {
+ pr_err("Using device: %s\n", pci_name(my_dev));
+ pci_addr_phys = 0;
+ if (my_dev->resource[0].flags & IORESOURCE_MEM) {
+ pci_addr_phys = my_dev->resource[0].start;
+ pr_info("memory at %pa\n", &pci_addr_phys);
+ }
+ if (pci_addr_phys == 0) {
+ pr_err("no memory resource ?\n");
+ pci_dev_put(my_dev);
+ return NULL;
+ }
+ } else {
+ pr_err("pci_probe failed\n");
+ return NULL;
+ }
+ return my_dev;
+}
+
+static int atir_add_to_buf(void *data, struct lirc_buffer *buf)
+{
+ unsigned char key;
+ int status;
+
+ status = poll_main();
+ key = (status >> 8) & 0xFF;
+ if (status & 0xFF) {
+ dev_dbg(atir_driver.dev, "reading key %02X\n", key);
+ lirc_buffer_write(buf, &key);
+ return 0;
+ }
+ return -ENODATA;
+}
+
+static int atir_set_use_inc(void *data)
+{
+ dev_dbg(atir_driver.dev, "driver is opened\n");
+ return 0;
+}
+
+static void atir_set_use_dec(void *data)
+{
+ dev_dbg(atir_driver.dev, "driver is closed\n");
+}
+
+int init_module(void)
+{
+ struct pci_dev *pdev;
+ int rc;
+
+ pdev = do_pci_probe();
+ if (pdev == NULL)
+ return -ENODEV;
+
+ rc = pci_enable_device(pdev);
+ if (rc)
+ goto err_put_dev;
+
+ if (!atir_init_start()) {
+ rc = -ENODEV;
+ goto err_disable;
+ }
+
+ strcpy(atir_driver.name, "ATIR");
+ atir_driver.minor = -1;
+ atir_driver.code_length = 8;
+ atir_driver.sample_rate = 10;
+ atir_driver.data = NULL;
+ atir_driver.add_to_buf = atir_add_to_buf;
+ atir_driver.set_use_inc = atir_set_use_inc;
+ atir_driver.set_use_dec = atir_set_use_dec;
+ atir_driver.dev = &pdev->dev;
+ atir_driver.owner = THIS_MODULE;
+
+ atir_minor = lirc_register_driver(&atir_driver);
+ if (atir_minor < 0) {
+ pr_err("failed to register driver!\n");
+ rc = atir_minor;
+ goto err_unmap;
+ }
+ dev_dbg(atir_driver.dev, "driver is registered on minor %d\n",
+ atir_minor);
+
+ return 0;
+
+err_unmap:
+ iounmap(pci_addr_lin);
+err_disable:
+ pci_disable_device(pdev);
+err_put_dev:
+ pci_dev_put(pdev);
+ return rc;
+}
+
+
+void cleanup_module(void)
+{
+ struct pci_dev *pdev = to_pci_dev(atir_driver.dev);
+
+ lirc_unregister_driver(atir_minor);
+ iounmap(pci_addr_lin);
+ pci_disable_device(pdev);
+ pci_dev_put(pdev);
+}
+
+
+static int atir_init_start(void)
+{
+ pci_addr_lin = ioremap(pci_addr_phys + DATA_PCI_OFF, 0x400);
+ if (!pci_addr_lin) {
+ pr_info("pci mem must be mapped\n");
+ return 0;
+ }
+ return 1;
+}
+
+static void cycle_delay(int cycle)
+{
+ udelay(WAIT_CYCLE*cycle);
+}
+
+
+static int poll_main(void)
+{
+ unsigned char status_high, status_low;
+
+ do_i2c_start();
+
+ seems_wr_byte(0xAA);
+ seems_wr_byte(0x01);
+
+ do_i2c_start();
+
+ seems_wr_byte(0xAB);
+
+ status_low = seems_rd_byte();
+ status_high = seems_rd_byte();
+
+ do_i2c_stop();
+
+ return (status_high << 8) | status_low;
+}
+
+static void do_i2c_start(void)
+{
+ do_set_bits(3);
+ cycle_delay(4);
+
+ do_set_bits(1);
+ cycle_delay(7);
+
+ do_set_bits(0);
+ cycle_delay(2);
+}
+
+static void do_i2c_stop(void)
+{
+ unsigned char bits;
+
+ bits = do_get_bits() & 0xFD;
+ do_set_bits(bits);
+ cycle_delay(1);
+
+ bits |= 1;
+ do_set_bits(bits);
+ cycle_delay(2);
+
+ bits |= 2;
+ do_set_bits(bits);
+ bits = 3;
+ do_set_bits(bits);
+ cycle_delay(2);
+}
+
+static void seems_wr_byte(unsigned char value)
+{
+ int i;
+ unsigned char reg;
+
+ reg = do_get_bits();
+ for (i = 0; i < 8; i++) {
+ if (value & 0x80)
+ reg |= 0x02;
+ else
+ reg &= 0xFD;
+
+ do_set_bits(reg);
+ cycle_delay(1);
+
+ reg |= 1;
+ do_set_bits(reg);
+ cycle_delay(1);
+
+ reg &= 0xFE;
+ do_set_bits(reg);
+ cycle_delay(1);
+ value <<= 1;
+ }
+ cycle_delay(2);
+
+ reg |= 2;
+ do_set_bits(reg);
+
+ reg |= 1;
+ do_set_bits(reg);
+
+ cycle_delay(1);
+ do_get_bits();
+
+ reg &= 0xFE;
+ do_set_bits(reg);
+ cycle_delay(3);
+}
+
+static unsigned char seems_rd_byte(void)
+{
+ int i;
+ int rd_byte;
+ unsigned char bits_2, bits_1;
+
+ bits_1 = do_get_bits() | 2;
+ do_set_bits(bits_1);
+
+ rd_byte = 0;
+ for (i = 0; i < 8; i++) {
+ bits_1 &= 0xFE;
+ do_set_bits(bits_1);
+ cycle_delay(2);
+
+ bits_1 |= 1;
+ do_set_bits(bits_1);
+ cycle_delay(1);
+
+ bits_2 = do_get_bits();
+ if (bits_2 & 2)
+ rd_byte |= 1;
+
+ rd_byte <<= 1;
+ }
+
+ bits_1 = 0;
+ if (bits_2 == 0)
+ bits_1 |= 2;
+
+ do_set_bits(bits_1);
+ cycle_delay(2);
+
+ bits_1 |= 1;
+ do_set_bits(bits_1);
+ cycle_delay(3);
+
+ bits_1 &= 0xFE;
+ do_set_bits(bits_1);
+ cycle_delay(2);
+
+ rd_byte >>= 1;
+ rd_byte &= 0xFF;
+ return rd_byte;
+}
+
+static void do_set_bits(unsigned char new_bits)
+{
+ int reg_val;
+
+ reg_val = read_index(0x34);
+ if (new_bits & 2) {
+ reg_val &= 0xFFFFFFDF;
+ reg_val |= 1;
+ } else {
+ reg_val &= 0xFFFFFFFE;
+ reg_val |= 0x20;
+ }
+ reg_val |= 0x10;
+ write_index(0x34, reg_val);
+
+ reg_val = read_index(0x31);
+ if (new_bits & 1)
+ reg_val |= 0x1000000;
+ else
+ reg_val &= 0xFEFFFFFF;
+
+ reg_val |= 0x8000000;
+ write_index(0x31, reg_val);
+}
+
+static unsigned char do_get_bits(void)
+{
+ unsigned char bits;
+ int reg_val;
+
+ reg_val = read_index(0x34);
+ reg_val |= 0x10;
+ reg_val &= 0xFFFFFFDF;
+ write_index(0x34, reg_val);
+
+ reg_val = read_index(0x34);
+ bits = 0;
+ if (reg_val & 8)
+ bits |= 2;
+ else
+ bits &= 0xFD;
+
+ reg_val = read_index(0x31);
+ if (reg_val & 0x1000000)
+ bits |= 1;
+ else
+ bits &= 0xFE;
+
+ return bits;
+}
+
+static unsigned int read_index(unsigned char index)
+{
+ unsigned char __iomem *addr;
+ /* addr = pci_addr_lin + DATA_PCI_OFF + ((index & 0xFF) << 2); */
+ addr = pci_addr_lin + ((index & 0xFF) << 2);
+ return readl(addr);
+}
+
+static void write_index(unsigned char index, unsigned int reg_val)
+{
+ unsigned char __iomem *addr;
+
+ addr = pci_addr_lin + ((index & 0xFF) << 2);
+ writel(reg_val, addr);
+}
+
+MODULE_AUTHOR("Froenchenko Leonid");
+MODULE_DESCRIPTION("IR remote driver for bt829 based TV cards");
+MODULE_LICENSE("GPL");
+
+module_param(debug, bool, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(debug, "Debug enabled or not");
diff --git a/drivers/staging/media/lirc/lirc_imon.c b/drivers/staging/media/lirc/lirc_imon.c
new file mode 100644
index 000000000..335b98a54
--- /dev/null
+++ b/drivers/staging/media/lirc/lirc_imon.c
@@ -0,0 +1,1002 @@
+/*
+ * lirc_imon.c: LIRC/VFD/LCD driver for SoundGraph iMON IR/VFD/LCD
+ * including the iMON PAD model
+ *
+ * Copyright(C) 2004 Venky Raju(dev@venky.ws)
+ * Copyright(C) 2009 Jarod Wilson <jarod@wilsonet.com>
+ *
+ * lirc_imon is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+#include <linux/usb.h>
+
+#include <media/lirc.h>
+#include <media/lirc_dev.h>
+
+
+#define MOD_AUTHOR "Venky Raju <dev@venky.ws>"
+#define MOD_DESC "Driver for SoundGraph iMON MultiMedia IR/Display"
+#define MOD_NAME "lirc_imon"
+#define MOD_VERSION "0.8"
+
+#define DISPLAY_MINOR_BASE 144
+#define DEVICE_NAME "lcd%d"
+
+#define BUF_CHUNK_SIZE 4
+#define BUF_SIZE 128
+
+#define BIT_DURATION 250 /* each bit received is 250us */
+
+/*** P R O T O T Y P E S ***/
+
+/* USB Callback prototypes */
+static int imon_probe(struct usb_interface *interface,
+ const struct usb_device_id *id);
+static void imon_disconnect(struct usb_interface *interface);
+static void usb_rx_callback(struct urb *urb);
+static void usb_tx_callback(struct urb *urb);
+
+/* suspend/resume support */
+static int imon_resume(struct usb_interface *intf);
+static int imon_suspend(struct usb_interface *intf, pm_message_t message);
+
+/* Display file_operations function prototypes */
+static int display_open(struct inode *inode, struct file *file);
+static int display_close(struct inode *inode, struct file *file);
+
+/* VFD write operation */
+static ssize_t vfd_write(struct file *file, const char __user *buf,
+ size_t n_bytes, loff_t *pos);
+
+/* LIRC driver function prototypes */
+static int ir_open(void *data);
+static void ir_close(void *data);
+
+/*** G L O B A L S ***/
+#define IMON_DATA_BUF_SZ 35
+
+struct imon_context {
+ struct usb_device *usbdev;
+ /* Newer devices have two interfaces */
+ int display; /* not all controllers do */
+ int display_isopen; /* display port has been opened */
+ int ir_isopen; /* IR port open */
+ int dev_present; /* USB device presence */
+ struct mutex ctx_lock; /* to lock this object */
+ wait_queue_head_t remove_ok; /* For unexpected USB disconnects */
+
+ int vfd_proto_6p; /* some VFD require a 6th packet */
+
+ struct lirc_driver *driver;
+ struct usb_endpoint_descriptor *rx_endpoint;
+ struct usb_endpoint_descriptor *tx_endpoint;
+ struct urb *rx_urb;
+ struct urb *tx_urb;
+ unsigned char usb_rx_buf[8];
+ unsigned char usb_tx_buf[8];
+
+ struct rx_data {
+ int count; /* length of 0 or 1 sequence */
+ int prev_bit; /* logic level of sequence */
+ int initial_space; /* initial space flag */
+ } rx;
+
+ struct tx_t {
+ unsigned char data_buf[IMON_DATA_BUF_SZ]; /* user data buffer */
+ struct completion finished; /* wait for write to finish */
+ atomic_t busy; /* write in progress */
+ int status; /* status of tx completion */
+ } tx;
+};
+
+static const struct file_operations display_fops = {
+ .owner = THIS_MODULE,
+ .open = &display_open,
+ .write = &vfd_write,
+ .release = &display_close,
+ .llseek = noop_llseek,
+};
+
+/*
+ * USB Device ID for iMON USB Control Boards
+ *
+ * The Windows drivers contain 6 different inf files, more or less one for
+ * each new device until the 0x0034-0x0046 devices, which all use the same
+ * driver. Some of the devices in the 34-46 range haven't been definitively
+ * identified yet. Early devices have either a TriGem Computer, Inc. or a
+ * Samsung vendor ID (0x0aa8 and 0x04e8 respectively), while all later
+ * devices use the SoundGraph vendor ID (0x15c2).
+ */
+static struct usb_device_id imon_usb_id_table[] = {
+ /* TriGem iMON (IR only) -- TG_iMON.inf */
+ { USB_DEVICE(0x0aa8, 0x8001) },
+
+ /* SoundGraph iMON (IR only) -- sg_imon.inf */
+ { USB_DEVICE(0x04e8, 0xff30) },
+
+ /* SoundGraph iMON VFD (IR & VFD) -- iMON_VFD.inf */
+ { USB_DEVICE(0x0aa8, 0xffda) },
+
+ /* SoundGraph iMON SS (IR & VFD) -- iMON_SS.inf */
+ { USB_DEVICE(0x15c2, 0xffda) },
+
+ {}
+};
+
+/* Some iMON VFD models requires a 6th packet for VFD writes */
+static struct usb_device_id vfd_proto_6p_list[] = {
+ { USB_DEVICE(0x15c2, 0xffda) },
+ {}
+};
+
+/* Some iMON devices have no lcd/vfd, don't set one up */
+static struct usb_device_id ir_only_list[] = {
+ { USB_DEVICE(0x0aa8, 0x8001) },
+ { USB_DEVICE(0x04e8, 0xff30) },
+ {}
+};
+
+/* USB Device data */
+static struct usb_driver imon_driver = {
+ .name = MOD_NAME,
+ .probe = imon_probe,
+ .disconnect = imon_disconnect,
+ .suspend = imon_suspend,
+ .resume = imon_resume,
+ .id_table = imon_usb_id_table,
+};
+
+static struct usb_class_driver imon_class = {
+ .name = DEVICE_NAME,
+ .fops = &display_fops,
+ .minor_base = DISPLAY_MINOR_BASE,
+};
+
+/* to prevent races between open() and disconnect(), probing, etc */
+static DEFINE_MUTEX(driver_lock);
+
+static int debug;
+
+/*** M O D U L E C O D E ***/
+
+MODULE_AUTHOR(MOD_AUTHOR);
+MODULE_DESCRIPTION(MOD_DESC);
+MODULE_VERSION(MOD_VERSION);
+MODULE_LICENSE("GPL");
+MODULE_DEVICE_TABLE(usb, imon_usb_id_table);
+module_param(debug, int, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(debug, "Debug messages: 0=no, 1=yes(default: no)");
+
+static void free_imon_context(struct imon_context *context)
+{
+ struct device *dev = context->driver->dev;
+
+ usb_free_urb(context->tx_urb);
+ usb_free_urb(context->rx_urb);
+ lirc_buffer_free(context->driver->rbuf);
+ kfree(context->driver->rbuf);
+ kfree(context->driver);
+ kfree(context);
+
+ dev_dbg(dev, "%s: iMON context freed\n", __func__);
+}
+
+static void deregister_from_lirc(struct imon_context *context)
+{
+ int retval;
+ int minor = context->driver->minor;
+
+ retval = lirc_unregister_driver(minor);
+ if (retval)
+ dev_err(&context->usbdev->dev,
+ "unable to deregister from lirc(%d)", retval);
+ else
+ dev_info(&context->usbdev->dev,
+ "Deregistered iMON driver (minor:%d)\n", minor);
+
+}
+
+/**
+ * Called when the Display device (e.g. /dev/lcd0)
+ * is opened by the application.
+ */
+static int display_open(struct inode *inode, struct file *file)
+{
+ struct usb_interface *interface;
+ struct imon_context *context = NULL;
+ int subminor;
+ int retval = 0;
+
+ /* prevent races with disconnect */
+ mutex_lock(&driver_lock);
+
+ subminor = iminor(inode);
+ interface = usb_find_interface(&imon_driver, subminor);
+ if (!interface) {
+ pr_err("%s: could not find interface for minor %d\n",
+ __func__, subminor);
+ retval = -ENODEV;
+ goto exit;
+ }
+ context = usb_get_intfdata(interface);
+
+ if (!context) {
+ dev_err(&interface->dev, "no context found for minor %d\n",
+ subminor);
+ retval = -ENODEV;
+ goto exit;
+ }
+
+ mutex_lock(&context->ctx_lock);
+
+ if (!context->display) {
+ dev_err(&interface->dev,
+ "%s: display not supported by device\n", __func__);
+ retval = -ENODEV;
+ } else if (context->display_isopen) {
+ dev_err(&interface->dev,
+ "%s: display port is already open\n", __func__);
+ retval = -EBUSY;
+ } else {
+ context->display_isopen = 1;
+ file->private_data = context;
+ dev_info(context->driver->dev, "display port opened\n");
+ }
+
+ mutex_unlock(&context->ctx_lock);
+
+exit:
+ mutex_unlock(&driver_lock);
+ return retval;
+}
+
+/**
+ * Called when the display device (e.g. /dev/lcd0)
+ * is closed by the application.
+ */
+static int display_close(struct inode *inode, struct file *file)
+{
+ struct imon_context *context = NULL;
+ int retval = 0;
+
+ context = file->private_data;
+
+ if (!context) {
+ pr_err("%s: no context for device\n", __func__);
+ return -ENODEV;
+ }
+
+ mutex_lock(&context->ctx_lock);
+
+ if (!context->display) {
+ dev_err(&context->usbdev->dev,
+ "%s: display not supported by device\n", __func__);
+ retval = -ENODEV;
+ } else if (!context->display_isopen) {
+ dev_err(&context->usbdev->dev,
+ "%s: display is not open\n", __func__);
+ retval = -EIO;
+ } else {
+ context->display_isopen = 0;
+ dev_info(context->driver->dev, "display port closed\n");
+ if (!context->dev_present && !context->ir_isopen) {
+ /*
+ * Device disconnected before close and IR port is not
+ * open. If IR port is open, context will be deleted by
+ * ir_close.
+ */
+ mutex_unlock(&context->ctx_lock);
+ free_imon_context(context);
+ return retval;
+ }
+ }
+
+ mutex_unlock(&context->ctx_lock);
+ return retval;
+}
+
+/**
+ * Sends a packet to the device -- this function must be called
+ * with context->ctx_lock held.
+ */
+static int send_packet(struct imon_context *context)
+{
+ unsigned int pipe;
+ int interval = 0;
+ int retval = 0;
+
+ /* Check if we need to use control or interrupt urb */
+ pipe = usb_sndintpipe(context->usbdev,
+ context->tx_endpoint->bEndpointAddress);
+ interval = context->tx_endpoint->bInterval;
+
+ usb_fill_int_urb(context->tx_urb, context->usbdev, pipe,
+ context->usb_tx_buf,
+ sizeof(context->usb_tx_buf),
+ usb_tx_callback, context, interval);
+
+ context->tx_urb->actual_length = 0;
+
+ init_completion(&context->tx.finished);
+ atomic_set(&context->tx.busy, 1);
+
+ retval = usb_submit_urb(context->tx_urb, GFP_KERNEL);
+ if (retval) {
+ atomic_set(&context->tx.busy, 0);
+ dev_err(&context->usbdev->dev, "error submitting urb(%d)\n",
+ retval);
+ } else {
+ /* Wait for transmission to complete (or abort) */
+ mutex_unlock(&context->ctx_lock);
+ retval = wait_for_completion_interruptible(
+ &context->tx.finished);
+ if (retval)
+ dev_err(&context->usbdev->dev,
+ "%s: task interrupted\n", __func__);
+ mutex_lock(&context->ctx_lock);
+
+ retval = context->tx.status;
+ if (retval)
+ dev_err(&context->usbdev->dev,
+ "packet tx failed (%d)\n", retval);
+ }
+
+ return retval;
+}
+
+/**
+ * Writes data to the VFD. The iMON VFD is 2x16 characters
+ * and requires data in 5 consecutive USB interrupt packets,
+ * each packet but the last carrying 7 bytes.
+ *
+ * I don't know if the VFD board supports features such as
+ * scrolling, clearing rows, blanking, etc. so at
+ * the caller must provide a full screen of data. If fewer
+ * than 32 bytes are provided spaces will be appended to
+ * generate a full screen.
+ */
+static ssize_t vfd_write(struct file *file, const char __user *buf,
+ size_t n_bytes, loff_t *pos)
+{
+ int i;
+ int offset;
+ int seq;
+ int retval = 0;
+ struct imon_context *context;
+ const unsigned char vfd_packet6[] = {
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF };
+ int *data_buf = NULL;
+
+ context = file->private_data;
+ if (!context) {
+ pr_err("%s: no context for device\n", __func__);
+ return -ENODEV;
+ }
+
+ mutex_lock(&context->ctx_lock);
+
+ if (!context->dev_present) {
+ dev_err(&context->usbdev->dev,
+ "%s: no iMON device present\n", __func__);
+ retval = -ENODEV;
+ goto exit;
+ }
+
+ if (n_bytes <= 0 || n_bytes > IMON_DATA_BUF_SZ - 3) {
+ dev_err(&context->usbdev->dev,
+ "%s: invalid payload size\n", __func__);
+ retval = -EINVAL;
+ goto exit;
+ }
+
+ data_buf = memdup_user(buf, n_bytes);
+ if (IS_ERR(data_buf)) {
+ retval = PTR_ERR(data_buf);
+ data_buf = NULL;
+ goto exit;
+ }
+
+ memcpy(context->tx.data_buf, data_buf, n_bytes);
+
+ /* Pad with spaces */
+ for (i = n_bytes; i < IMON_DATA_BUF_SZ - 3; ++i)
+ context->tx.data_buf[i] = ' ';
+
+ for (i = IMON_DATA_BUF_SZ - 3; i < IMON_DATA_BUF_SZ; ++i)
+ context->tx.data_buf[i] = 0xFF;
+
+ offset = 0;
+ seq = 0;
+
+ do {
+ memcpy(context->usb_tx_buf, context->tx.data_buf + offset, 7);
+ context->usb_tx_buf[7] = (unsigned char) seq;
+
+ retval = send_packet(context);
+ if (retval) {
+ dev_err(&context->usbdev->dev,
+ "send packet failed for packet #%d\n",
+ seq / 2);
+ goto exit;
+ } else {
+ seq += 2;
+ offset += 7;
+ }
+
+ } while (offset < IMON_DATA_BUF_SZ);
+
+ if (context->vfd_proto_6p) {
+ /* Send packet #6 */
+ memcpy(context->usb_tx_buf, &vfd_packet6, sizeof(vfd_packet6));
+ context->usb_tx_buf[7] = (unsigned char) seq;
+ retval = send_packet(context);
+ if (retval)
+ dev_err(&context->usbdev->dev,
+ "send packet failed for packet #%d\n",
+ seq / 2);
+ }
+
+exit:
+ mutex_unlock(&context->ctx_lock);
+ kfree(data_buf);
+
+ return (!retval) ? n_bytes : retval;
+}
+
+/**
+ * Callback function for USB core API: transmit data
+ */
+static void usb_tx_callback(struct urb *urb)
+{
+ struct imon_context *context;
+
+ if (!urb)
+ return;
+ context = (struct imon_context *)urb->context;
+ if (!context)
+ return;
+
+ context->tx.status = urb->status;
+
+ /* notify waiters that write has finished */
+ atomic_set(&context->tx.busy, 0);
+ complete(&context->tx.finished);
+}
+
+/**
+ * Called by lirc_dev when the application opens /dev/lirc
+ */
+static int ir_open(void *data)
+{
+ struct imon_context *context;
+
+ /* prevent races with disconnect */
+ mutex_lock(&driver_lock);
+
+ context = data;
+
+ /* initial IR protocol decode variables */
+ context->rx.count = 0;
+ context->rx.initial_space = 1;
+ context->rx.prev_bit = 0;
+
+ context->ir_isopen = 1;
+ dev_info(context->driver->dev, "IR port opened\n");
+
+ mutex_unlock(&driver_lock);
+ return 0;
+}
+
+/**
+ * Called by lirc_dev when the application closes /dev/lirc
+ */
+static void ir_close(void *data)
+{
+ struct imon_context *context;
+
+ context = data;
+ if (!context) {
+ pr_err("%s: no context for device\n", __func__);
+ return;
+ }
+
+ mutex_lock(&context->ctx_lock);
+
+ context->ir_isopen = 0;
+ dev_info(context->driver->dev, "IR port closed\n");
+
+ if (!context->dev_present) {
+ /*
+ * Device disconnected while IR port was still open. Driver
+ * was not deregistered at disconnect time, so do it now.
+ */
+ deregister_from_lirc(context);
+
+ if (!context->display_isopen) {
+ mutex_unlock(&context->ctx_lock);
+ free_imon_context(context);
+ return;
+ }
+ /*
+ * If display port is open, context will be deleted by
+ * display_close
+ */
+ }
+
+ mutex_unlock(&context->ctx_lock);
+}
+
+/**
+ * Convert bit count to time duration (in us) and submit
+ * the value to lirc_dev.
+ */
+static void submit_data(struct imon_context *context)
+{
+ unsigned char buf[4];
+ int value = context->rx.count;
+ int i;
+
+ dev_dbg(context->driver->dev, "submitting data to LIRC\n");
+
+ value *= BIT_DURATION;
+ value &= PULSE_MASK;
+ if (context->rx.prev_bit)
+ value |= PULSE_BIT;
+
+ for (i = 0; i < 4; ++i)
+ buf[i] = value>>(i*8);
+
+ lirc_buffer_write(context->driver->rbuf, buf);
+ wake_up(&context->driver->rbuf->wait_poll);
+}
+
+/**
+ * Process the incoming packet
+ */
+static void imon_incoming_packet(struct imon_context *context,
+ struct urb *urb, int intf)
+{
+ int len = urb->actual_length;
+ unsigned char *buf = urb->transfer_buffer;
+ struct device *dev = context->driver->dev;
+ int octet, bit;
+ unsigned char mask;
+
+ /*
+ * just bail out if no listening IR client
+ */
+ if (!context->ir_isopen)
+ return;
+
+ if (len != 8) {
+ dev_warn(dev, "imon %s: invalid incoming packet size (len = %d, intf%d)\n",
+ __func__, len, intf);
+ return;
+ }
+
+ if (debug)
+ dev_info(dev, "raw packet: %*ph\n", len, buf);
+ /*
+ * Translate received data to pulse and space lengths.
+ * Received data is active low, i.e. pulses are 0 and
+ * spaces are 1.
+ *
+ * My original algorithm was essentially similar to
+ * Changwoo Ryu's with the exception that he switched
+ * the incoming bits to active high and also fed an
+ * initial space to LIRC at the start of a new sequence
+ * if the previous bit was a pulse.
+ *
+ * I've decided to adopt his algorithm.
+ */
+
+ if (buf[7] == 1 && context->rx.initial_space) {
+ /* LIRC requires a leading space */
+ context->rx.prev_bit = 0;
+ context->rx.count = 4;
+ submit_data(context);
+ context->rx.count = 0;
+ }
+
+ for (octet = 0; octet < 5; ++octet) {
+ mask = 0x80;
+ for (bit = 0; bit < 8; ++bit) {
+ int curr_bit = !(buf[octet] & mask);
+
+ if (curr_bit != context->rx.prev_bit) {
+ if (context->rx.count) {
+ submit_data(context);
+ context->rx.count = 0;
+ }
+ context->rx.prev_bit = curr_bit;
+ }
+ ++context->rx.count;
+ mask >>= 1;
+ }
+ }
+
+ if (buf[7] == 10) {
+ if (context->rx.count) {
+ submit_data(context);
+ context->rx.count = 0;
+ }
+ context->rx.initial_space = context->rx.prev_bit;
+ }
+}
+
+/**
+ * Callback function for USB core API: receive data
+ */
+static void usb_rx_callback(struct urb *urb)
+{
+ struct imon_context *context;
+ int intfnum = 0;
+
+ if (!urb)
+ return;
+
+ context = (struct imon_context *)urb->context;
+ if (!context)
+ return;
+
+ switch (urb->status) {
+ case -ENOENT: /* usbcore unlink successful! */
+ return;
+
+ case 0:
+ imon_incoming_packet(context, urb, intfnum);
+ break;
+
+ default:
+ dev_warn(context->driver->dev, "imon %s: status(%d): ignored\n",
+ __func__, urb->status);
+ break;
+ }
+
+ usb_submit_urb(context->rx_urb, GFP_ATOMIC);
+}
+
+/**
+ * Callback function for USB core API: Probe
+ */
+static int imon_probe(struct usb_interface *interface,
+ const struct usb_device_id *id)
+{
+ struct usb_device *usbdev = NULL;
+ struct usb_host_interface *iface_desc = NULL;
+ struct usb_endpoint_descriptor *rx_endpoint = NULL;
+ struct usb_endpoint_descriptor *tx_endpoint = NULL;
+ struct urb *rx_urb = NULL;
+ struct urb *tx_urb = NULL;
+ struct lirc_driver *driver = NULL;
+ struct lirc_buffer *rbuf = NULL;
+ struct device *dev = &interface->dev;
+ int ifnum;
+ int lirc_minor = 0;
+ int num_endpts;
+ int retval = 0;
+ int display_ep_found = 0;
+ int ir_ep_found = 0;
+ int alloc_status = 0;
+ int vfd_proto_6p = 0;
+ struct imon_context *context = NULL;
+ int i;
+ u16 vendor, product;
+
+ /* prevent races probing devices w/multiple interfaces */
+ mutex_lock(&driver_lock);
+
+ context = kzalloc(sizeof(struct imon_context), GFP_KERNEL);
+ if (!context) {
+ alloc_status = 1;
+ goto alloc_status_switch;
+ }
+
+ /*
+ * Try to auto-detect the type of display if the user hasn't set
+ * it by hand via the display_type modparam. Default is VFD.
+ */
+ if (usb_match_id(interface, ir_only_list))
+ context->display = 0;
+ else
+ context->display = 1;
+
+ usbdev = usb_get_dev(interface_to_usbdev(interface));
+ iface_desc = interface->cur_altsetting;
+ num_endpts = iface_desc->desc.bNumEndpoints;
+ ifnum = iface_desc->desc.bInterfaceNumber;
+ vendor = le16_to_cpu(usbdev->descriptor.idVendor);
+ product = le16_to_cpu(usbdev->descriptor.idProduct);
+
+ dev_dbg(dev, "%s: found iMON device (%04x:%04x, intf%d)\n",
+ __func__, vendor, product, ifnum);
+
+ /*
+ * Scan the endpoint list and set:
+ * first input endpoint = IR endpoint
+ * first output endpoint = display endpoint
+ */
+ for (i = 0; i < num_endpts && !(ir_ep_found && display_ep_found); ++i) {
+ struct usb_endpoint_descriptor *ep;
+ int ep_dir;
+ int ep_type;
+
+ ep = &iface_desc->endpoint[i].desc;
+ ep_dir = ep->bEndpointAddress & USB_ENDPOINT_DIR_MASK;
+ ep_type = ep->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK;
+
+ if (!ir_ep_found &&
+ ep_dir == USB_DIR_IN &&
+ ep_type == USB_ENDPOINT_XFER_INT) {
+
+ rx_endpoint = ep;
+ ir_ep_found = 1;
+ dev_dbg(dev, "%s: found IR endpoint\n", __func__);
+
+ } else if (!display_ep_found && ep_dir == USB_DIR_OUT &&
+ ep_type == USB_ENDPOINT_XFER_INT) {
+ tx_endpoint = ep;
+ display_ep_found = 1;
+ dev_dbg(dev, "%s: found display endpoint\n", __func__);
+ }
+ }
+
+ /*
+ * Some iMON receivers have no display. Unfortunately, it seems
+ * that SoundGraph recycles device IDs between devices both with
+ * and without... :\
+ */
+ if (context->display == 0) {
+ display_ep_found = 0;
+ dev_dbg(dev, "%s: device has no display\n", __func__);
+ }
+
+ /* Input endpoint is mandatory */
+ if (!ir_ep_found) {
+ dev_err(dev, "%s: no valid input (IR) endpoint found.\n",
+ __func__);
+ retval = -ENODEV;
+ alloc_status = 2;
+ goto alloc_status_switch;
+ }
+
+ /* Determine if display requires 6 packets */
+ if (display_ep_found) {
+ if (usb_match_id(interface, vfd_proto_6p_list))
+ vfd_proto_6p = 1;
+
+ dev_dbg(dev, "%s: vfd_proto_6p: %d\n",
+ __func__, vfd_proto_6p);
+ }
+
+ driver = kzalloc(sizeof(struct lirc_driver), GFP_KERNEL);
+ if (!driver) {
+ alloc_status = 2;
+ goto alloc_status_switch;
+ }
+ rbuf = kmalloc(sizeof(struct lirc_buffer), GFP_KERNEL);
+ if (!rbuf) {
+ alloc_status = 3;
+ goto alloc_status_switch;
+ }
+ if (lirc_buffer_init(rbuf, BUF_CHUNK_SIZE, BUF_SIZE)) {
+ dev_err(dev, "%s: lirc_buffer_init failed\n", __func__);
+ alloc_status = 4;
+ goto alloc_status_switch;
+ }
+ rx_urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!rx_urb) {
+ dev_err(dev, "%s: usb_alloc_urb failed for IR urb\n", __func__);
+ alloc_status = 5;
+ goto alloc_status_switch;
+ }
+ tx_urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!tx_urb) {
+ dev_err(dev, "%s: usb_alloc_urb failed for display urb\n",
+ __func__);
+ alloc_status = 6;
+ goto alloc_status_switch;
+ }
+
+ mutex_init(&context->ctx_lock);
+ context->vfd_proto_6p = vfd_proto_6p;
+
+ strcpy(driver->name, MOD_NAME);
+ driver->minor = -1;
+ driver->code_length = BUF_CHUNK_SIZE * 8;
+ driver->sample_rate = 0;
+ driver->features = LIRC_CAN_REC_MODE2;
+ driver->data = context;
+ driver->rbuf = rbuf;
+ driver->set_use_inc = ir_open;
+ driver->set_use_dec = ir_close;
+ driver->dev = &interface->dev;
+ driver->owner = THIS_MODULE;
+
+ mutex_lock(&context->ctx_lock);
+
+ context->driver = driver;
+ /* start out in keyboard mode */
+
+ lirc_minor = lirc_register_driver(driver);
+ if (lirc_minor < 0) {
+ dev_err(dev, "%s: lirc_register_driver failed\n", __func__);
+ alloc_status = 7;
+ goto unlock;
+ } else
+ dev_info(dev, "Registered iMON driver (lirc minor: %d)\n",
+ lirc_minor);
+
+ /* Needed while unregistering! */
+ driver->minor = lirc_minor;
+
+ context->usbdev = usbdev;
+ context->dev_present = 1;
+ context->rx_endpoint = rx_endpoint;
+ context->rx_urb = rx_urb;
+
+ /*
+ * tx is used to send characters to lcd/vfd, associate RF
+ * remotes, set IR protocol, and maybe more...
+ */
+ context->tx_endpoint = tx_endpoint;
+ context->tx_urb = tx_urb;
+
+ if (display_ep_found)
+ context->display = 1;
+
+ usb_fill_int_urb(context->rx_urb, context->usbdev,
+ usb_rcvintpipe(context->usbdev,
+ context->rx_endpoint->bEndpointAddress),
+ context->usb_rx_buf, sizeof(context->usb_rx_buf),
+ usb_rx_callback, context,
+ context->rx_endpoint->bInterval);
+
+ retval = usb_submit_urb(context->rx_urb, GFP_KERNEL);
+
+ if (retval) {
+ dev_err(dev, "usb_submit_urb failed for intf0 (%d)\n", retval);
+ alloc_status = 8;
+ goto unlock;
+ }
+
+ usb_set_intfdata(interface, context);
+
+ if (context->display && ifnum == 0) {
+ dev_dbg(dev, "%s: Registering iMON display with sysfs\n",
+ __func__);
+
+ if (usb_register_dev(interface, &imon_class)) {
+ /* Not a fatal error, so ignore */
+ dev_info(dev, "%s: could not get a minor number for display\n",
+ __func__);
+ }
+ }
+
+ dev_info(dev, "iMON device (%04x:%04x, intf%d) on usb<%d:%d> initialized\n",
+ vendor, product, ifnum, usbdev->bus->busnum, usbdev->devnum);
+
+unlock:
+ mutex_unlock(&context->ctx_lock);
+alloc_status_switch:
+
+ switch (alloc_status) {
+ case 8:
+ lirc_unregister_driver(driver->minor);
+ case 7:
+ usb_free_urb(tx_urb);
+ case 6:
+ usb_free_urb(rx_urb);
+ /* fall-through */
+ case 5:
+ if (rbuf)
+ lirc_buffer_free(rbuf);
+ /* fall-through */
+ case 4:
+ kfree(rbuf);
+ /* fall-through */
+ case 3:
+ kfree(driver);
+ /* fall-through */
+ case 2:
+ kfree(context);
+ context = NULL;
+ case 1:
+ if (retval != -ENODEV)
+ retval = -ENOMEM;
+ break;
+ case 0:
+ retval = 0;
+ }
+
+ mutex_unlock(&driver_lock);
+
+ return retval;
+}
+
+/**
+ * Callback function for USB core API: disconnect
+ */
+static void imon_disconnect(struct usb_interface *interface)
+{
+ struct imon_context *context;
+ int ifnum;
+
+ /* prevent races with ir_open()/display_open() */
+ mutex_lock(&driver_lock);
+
+ context = usb_get_intfdata(interface);
+ ifnum = interface->cur_altsetting->desc.bInterfaceNumber;
+
+ mutex_lock(&context->ctx_lock);
+
+ usb_set_intfdata(interface, NULL);
+
+ /* Abort ongoing write */
+ if (atomic_read(&context->tx.busy)) {
+ usb_kill_urb(context->tx_urb);
+ complete_all(&context->tx.finished);
+ }
+
+ context->dev_present = 0;
+ usb_kill_urb(context->rx_urb);
+ if (context->display)
+ usb_deregister_dev(interface, &imon_class);
+
+ if (!context->ir_isopen && !context->dev_present) {
+ deregister_from_lirc(context);
+ mutex_unlock(&context->ctx_lock);
+ if (!context->display_isopen)
+ free_imon_context(context);
+ } else
+ mutex_unlock(&context->ctx_lock);
+
+ mutex_unlock(&driver_lock);
+
+ dev_info(&interface->dev, "%s: iMON device (intf%d) disconnected\n",
+ __func__, ifnum);
+}
+
+static int imon_suspend(struct usb_interface *intf, pm_message_t message)
+{
+ struct imon_context *context = usb_get_intfdata(intf);
+
+ usb_kill_urb(context->rx_urb);
+
+ return 0;
+}
+
+static int imon_resume(struct usb_interface *intf)
+{
+ struct imon_context *context = usb_get_intfdata(intf);
+
+ usb_fill_int_urb(context->rx_urb, context->usbdev,
+ usb_rcvintpipe(context->usbdev,
+ context->rx_endpoint->bEndpointAddress),
+ context->usb_rx_buf, sizeof(context->usb_rx_buf),
+ usb_rx_callback, context,
+ context->rx_endpoint->bInterval);
+
+ return usb_submit_urb(context->rx_urb, GFP_ATOMIC);
+}
+
+module_usb_driver(imon_driver);
diff --git a/drivers/staging/media/lirc/lirc_parallel.c b/drivers/staging/media/lirc/lirc_parallel.c
new file mode 100644
index 000000000..c1408342b
--- /dev/null
+++ b/drivers/staging/media/lirc/lirc_parallel.c
@@ -0,0 +1,744 @@
+/*
+ * lirc_parallel.c
+ *
+ * lirc_parallel - device driver for infra-red signal receiving and
+ * transmitting unit built by the author
+ *
+ * Copyright (C) 1998 Christoph Bartelmus <lirc@bartelmus.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
+ *
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+/*** Includes ***/
+
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/signal.h>
+#include <linux/fs.h>
+#include <linux/kernel.h>
+#include <linux/ioport.h>
+#include <linux/time.h>
+#include <linux/mm.h>
+#include <linux/delay.h>
+
+#include <linux/io.h>
+#include <linux/irq.h>
+#include <linux/uaccess.h>
+#include <asm/div64.h>
+
+#include <linux/poll.h>
+#include <linux/parport.h>
+#include <linux/platform_device.h>
+
+#include <media/lirc.h>
+#include <media/lirc_dev.h>
+
+#include "lirc_parallel.h"
+
+#define LIRC_DRIVER_NAME "lirc_parallel"
+
+#ifndef LIRC_IRQ
+#define LIRC_IRQ 7
+#endif
+#ifndef LIRC_PORT
+#define LIRC_PORT 0x378
+#endif
+#ifndef LIRC_TIMER
+#define LIRC_TIMER 65536
+#endif
+
+/*** Global Variables ***/
+
+static bool debug;
+static bool check_pselecd;
+
+static unsigned int irq = LIRC_IRQ;
+static unsigned int io = LIRC_PORT;
+#ifdef LIRC_TIMER
+static unsigned int timer;
+static unsigned int default_timer = LIRC_TIMER;
+#endif
+
+#define RBUF_SIZE (256) /* this must be a power of 2 larger than 1 */
+
+static int rbuf[RBUF_SIZE];
+
+static DECLARE_WAIT_QUEUE_HEAD(lirc_wait);
+
+static unsigned int rptr;
+static unsigned int wptr;
+static unsigned int lost_irqs;
+static int is_open;
+
+static struct parport *pport;
+static struct pardevice *ppdevice;
+static int is_claimed;
+
+static unsigned int tx_mask = 1;
+
+/*** Internal Functions ***/
+
+static unsigned int in(int offset)
+{
+ switch (offset) {
+ case LIRC_LP_BASE:
+ return parport_read_data(pport);
+ case LIRC_LP_STATUS:
+ return parport_read_status(pport);
+ case LIRC_LP_CONTROL:
+ return parport_read_control(pport);
+ }
+ return 0; /* make compiler happy */
+}
+
+static void out(int offset, int value)
+{
+ switch (offset) {
+ case LIRC_LP_BASE:
+ parport_write_data(pport, value);
+ break;
+ case LIRC_LP_CONTROL:
+ parport_write_control(pport, value);
+ break;
+ case LIRC_LP_STATUS:
+ pr_info("attempt to write to status register\n");
+ break;
+ }
+}
+
+static unsigned int lirc_get_timer(void)
+{
+ return in(LIRC_PORT_TIMER) & LIRC_PORT_TIMER_BIT;
+}
+
+static unsigned int lirc_get_signal(void)
+{
+ return in(LIRC_PORT_SIGNAL) & LIRC_PORT_SIGNAL_BIT;
+}
+
+static void lirc_on(void)
+{
+ out(LIRC_PORT_DATA, tx_mask);
+}
+
+static void lirc_off(void)
+{
+ out(LIRC_PORT_DATA, 0);
+}
+
+static unsigned int init_lirc_timer(void)
+{
+ struct timeval tv, now;
+ unsigned int level, newlevel, timeelapsed, newtimer;
+ int count = 0;
+
+ do_gettimeofday(&tv);
+ tv.tv_sec++; /* wait max. 1 sec. */
+ level = lirc_get_timer();
+ do {
+ newlevel = lirc_get_timer();
+ if (level == 0 && newlevel != 0)
+ count++;
+ level = newlevel;
+ do_gettimeofday(&now);
+ } while (count < 1000 && (now.tv_sec < tv.tv_sec
+ || (now.tv_sec == tv.tv_sec
+ && now.tv_usec < tv.tv_usec)));
+
+ timeelapsed = (now.tv_sec + 1 - tv.tv_sec)*1000000
+ + (now.tv_usec - tv.tv_usec);
+ if (count >= 1000 && timeelapsed > 0) {
+ if (default_timer == 0) {
+ /* autodetect timer */
+ newtimer = (1000000*count)/timeelapsed;
+ pr_info("%u Hz timer detected\n", newtimer);
+ return newtimer;
+ }
+ newtimer = (1000000*count)/timeelapsed;
+ if (abs(newtimer - default_timer) > default_timer/10) {
+ /* bad timer */
+ pr_notice("bad timer: %u Hz\n", newtimer);
+ pr_notice("using default timer: %u Hz\n",
+ default_timer);
+ return default_timer;
+ }
+ pr_info("%u Hz timer detected\n", newtimer);
+ return newtimer; /* use detected value */
+ }
+
+ pr_notice("no timer detected\n");
+ return 0;
+}
+
+static int lirc_claim(void)
+{
+ if (parport_claim(ppdevice) != 0) {
+ pr_warn("could not claim port\n");
+ pr_warn("waiting for port becoming available\n");
+ if (parport_claim_or_block(ppdevice) < 0) {
+ pr_notice("could not claim port, giving up\n");
+ return 0;
+ }
+ }
+ out(LIRC_LP_CONTROL, LP_PSELECP|LP_PINITP);
+ is_claimed = 1;
+ return 1;
+}
+
+/*** interrupt handler ***/
+
+static void rbuf_write(int signal)
+{
+ unsigned int nwptr;
+
+ nwptr = (wptr + 1) & (RBUF_SIZE - 1);
+ if (nwptr == rptr) {
+ /* no new signals will be accepted */
+ lost_irqs++;
+ pr_notice("buffer overrun\n");
+ return;
+ }
+ rbuf[wptr] = signal;
+ wptr = nwptr;
+}
+
+static void lirc_lirc_irq_handler(void *blah)
+{
+ struct timeval tv;
+ static struct timeval lasttv;
+ static int init;
+ long signal;
+ int data;
+ unsigned int level, newlevel;
+ unsigned int timeout;
+
+ if (!is_open)
+ return;
+
+ if (!is_claimed)
+ return;
+
+#if 0
+ /* disable interrupt */
+ disable_irq(irq);
+ out(LIRC_PORT_IRQ, in(LIRC_PORT_IRQ) & (~LP_PINTEN));
+#endif
+ if (check_pselecd && (in(1) & LP_PSELECD))
+ return;
+
+#ifdef LIRC_TIMER
+ if (init) {
+ do_gettimeofday(&tv);
+
+ signal = tv.tv_sec - lasttv.tv_sec;
+ if (signal > 15)
+ /* really long time */
+ data = PULSE_MASK;
+ else
+ data = (int) (signal*1000000 +
+ tv.tv_usec - lasttv.tv_usec +
+ LIRC_SFH506_DELAY);
+
+ rbuf_write(data); /* space */
+ } else {
+ if (timer == 0) {
+ /*
+ * wake up; we'll lose this signal, but it will be
+ * garbage if the device is turned on anyway
+ */
+ timer = init_lirc_timer();
+ /* enable_irq(irq); */
+ return;
+ }
+ init = 1;
+ }
+
+ timeout = timer/10; /* timeout after 1/10 sec. */
+ signal = 1;
+ level = lirc_get_timer();
+ do {
+ newlevel = lirc_get_timer();
+ if (level == 0 && newlevel != 0)
+ signal++;
+ level = newlevel;
+
+ /* giving up */
+ if (signal > timeout
+ || (check_pselecd && (in(1) & LP_PSELECD))) {
+ signal = 0;
+ pr_notice("timeout\n");
+ break;
+ }
+ } while (lirc_get_signal());
+
+ if (signal != 0) {
+ /* adjust value to usecs */
+ __u64 helper;
+
+ helper = ((__u64) signal)*1000000;
+ do_div(helper, timer);
+ signal = (long) helper;
+
+ if (signal > LIRC_SFH506_DELAY)
+ data = signal - LIRC_SFH506_DELAY;
+ else
+ data = 1;
+ rbuf_write(PULSE_BIT|data); /* pulse */
+ }
+ do_gettimeofday(&lasttv);
+#else
+ /* add your code here */
+#endif
+
+ wake_up_interruptible(&lirc_wait);
+
+ /* enable interrupt */
+ /*
+ enable_irq(irq);
+ out(LIRC_PORT_IRQ, in(LIRC_PORT_IRQ)|LP_PINTEN);
+ */
+}
+
+/*** file operations ***/
+
+static loff_t lirc_lseek(struct file *filep, loff_t offset, int orig)
+{
+ return -ESPIPE;
+}
+
+static ssize_t lirc_read(struct file *filep, char __user *buf, size_t n,
+ loff_t *ppos)
+{
+ int result = 0;
+ int count = 0;
+ DECLARE_WAITQUEUE(wait, current);
+
+ if (n % sizeof(int))
+ return -EINVAL;
+
+ add_wait_queue(&lirc_wait, &wait);
+ set_current_state(TASK_INTERRUPTIBLE);
+ while (count < n) {
+ if (rptr != wptr) {
+ if (copy_to_user(buf+count, &rbuf[rptr],
+ sizeof(int))) {
+ result = -EFAULT;
+ break;
+ }
+ rptr = (rptr + 1) & (RBUF_SIZE - 1);
+ count += sizeof(int);
+ } else {
+ if (filep->f_flags & O_NONBLOCK) {
+ result = -EAGAIN;
+ break;
+ }
+ if (signal_pending(current)) {
+ result = -ERESTARTSYS;
+ break;
+ }
+ schedule();
+ set_current_state(TASK_INTERRUPTIBLE);
+ }
+ }
+ remove_wait_queue(&lirc_wait, &wait);
+ set_current_state(TASK_RUNNING);
+ return count ? count : result;
+}
+
+static ssize_t lirc_write(struct file *filep, const char __user *buf, size_t n,
+ loff_t *ppos)
+{
+ int count;
+ unsigned int i;
+ unsigned int level, newlevel;
+ unsigned long flags;
+ int counttimer;
+ int *wbuf;
+ ssize_t ret;
+
+ if (!is_claimed)
+ return -EBUSY;
+
+ count = n / sizeof(int);
+
+ if (n % sizeof(int) || count % 2 == 0)
+ return -EINVAL;
+
+ wbuf = memdup_user(buf, n);
+ if (IS_ERR(wbuf))
+ return PTR_ERR(wbuf);
+
+#ifdef LIRC_TIMER
+ if (timer == 0) {
+ /* try again if device is ready */
+ timer = init_lirc_timer();
+ if (timer == 0) {
+ ret = -EIO;
+ goto out;
+ }
+ }
+
+ /* adjust values from usecs */
+ for (i = 0; i < count; i++) {
+ __u64 helper;
+
+ helper = ((__u64) wbuf[i])*timer;
+ do_div(helper, 1000000);
+ wbuf[i] = (int) helper;
+ }
+
+ local_irq_save(flags);
+ i = 0;
+ while (i < count) {
+ level = lirc_get_timer();
+ counttimer = 0;
+ lirc_on();
+ do {
+ newlevel = lirc_get_timer();
+ if (level == 0 && newlevel != 0)
+ counttimer++;
+ level = newlevel;
+ if (check_pselecd && (in(1) & LP_PSELECD)) {
+ lirc_off();
+ local_irq_restore(flags);
+ ret = -EIO;
+ goto out;
+ }
+ } while (counttimer < wbuf[i]);
+ i++;
+
+ lirc_off();
+ if (i == count)
+ break;
+ counttimer = 0;
+ do {
+ newlevel = lirc_get_timer();
+ if (level == 0 && newlevel != 0)
+ counttimer++;
+ level = newlevel;
+ if (check_pselecd && (in(1) & LP_PSELECD)) {
+ local_irq_restore(flags);
+ ret = -EIO;
+ goto out;
+ }
+ } while (counttimer < wbuf[i]);
+ i++;
+ }
+ local_irq_restore(flags);
+#else
+ /* place code that handles write without external timer here */
+#endif
+ ret = n;
+out:
+ kfree(wbuf);
+
+ return ret;
+}
+
+static unsigned int lirc_poll(struct file *file, poll_table *wait)
+{
+ poll_wait(file, &lirc_wait, wait);
+ if (rptr != wptr)
+ return POLLIN | POLLRDNORM;
+ return 0;
+}
+
+static long lirc_ioctl(struct file *filep, unsigned int cmd, unsigned long arg)
+{
+ int result;
+ u32 __user *uptr = (u32 __user *)arg;
+ u32 features = LIRC_CAN_SET_TRANSMITTER_MASK |
+ LIRC_CAN_SEND_PULSE | LIRC_CAN_REC_MODE2;
+ u32 mode;
+ u32 value;
+
+ switch (cmd) {
+ case LIRC_GET_FEATURES:
+ result = put_user(features, uptr);
+ if (result)
+ return result;
+ break;
+ case LIRC_GET_SEND_MODE:
+ result = put_user(LIRC_MODE_PULSE, uptr);
+ if (result)
+ return result;
+ break;
+ case LIRC_GET_REC_MODE:
+ result = put_user(LIRC_MODE_MODE2, uptr);
+ if (result)
+ return result;
+ break;
+ case LIRC_SET_SEND_MODE:
+ result = get_user(mode, uptr);
+ if (result)
+ return result;
+ if (mode != LIRC_MODE_PULSE)
+ return -EINVAL;
+ break;
+ case LIRC_SET_REC_MODE:
+ result = get_user(mode, uptr);
+ if (result)
+ return result;
+ if (mode != LIRC_MODE_MODE2)
+ return -ENOSYS;
+ break;
+ case LIRC_SET_TRANSMITTER_MASK:
+ result = get_user(value, uptr);
+ if (result)
+ return result;
+ if ((value & LIRC_PARALLEL_TRANSMITTER_MASK) != value)
+ return LIRC_PARALLEL_MAX_TRANSMITTERS;
+ tx_mask = value;
+ break;
+ default:
+ return -ENOIOCTLCMD;
+ }
+ return 0;
+}
+
+static int lirc_open(struct inode *node, struct file *filep)
+{
+ if (is_open || !lirc_claim())
+ return -EBUSY;
+
+ parport_enable_irq(pport);
+
+ /* init read ptr */
+ rptr = 0;
+ wptr = 0;
+ lost_irqs = 0;
+
+ is_open = 1;
+ return 0;
+}
+
+static int lirc_close(struct inode *node, struct file *filep)
+{
+ if (is_claimed) {
+ is_claimed = 0;
+ parport_release(ppdevice);
+ }
+ is_open = 0;
+ return 0;
+}
+
+static const struct file_operations lirc_fops = {
+ .owner = THIS_MODULE,
+ .llseek = lirc_lseek,
+ .read = lirc_read,
+ .write = lirc_write,
+ .poll = lirc_poll,
+ .unlocked_ioctl = lirc_ioctl,
+#ifdef CONFIG_COMPAT
+ .compat_ioctl = lirc_ioctl,
+#endif
+ .open = lirc_open,
+ .release = lirc_close
+};
+
+static int set_use_inc(void *data)
+{
+ return 0;
+}
+
+static void set_use_dec(void *data)
+{
+}
+
+static struct lirc_driver driver = {
+ .name = LIRC_DRIVER_NAME,
+ .minor = -1,
+ .code_length = 1,
+ .sample_rate = 0,
+ .data = NULL,
+ .add_to_buf = NULL,
+ .set_use_inc = set_use_inc,
+ .set_use_dec = set_use_dec,
+ .fops = &lirc_fops,
+ .dev = NULL,
+ .owner = THIS_MODULE,
+};
+
+static struct platform_device *lirc_parallel_dev;
+
+static int lirc_parallel_probe(struct platform_device *dev)
+{
+ return 0;
+}
+
+static int lirc_parallel_remove(struct platform_device *dev)
+{
+ return 0;
+}
+
+static int lirc_parallel_suspend(struct platform_device *dev,
+ pm_message_t state)
+{
+ return 0;
+}
+
+static int lirc_parallel_resume(struct platform_device *dev)
+{
+ return 0;
+}
+
+static struct platform_driver lirc_parallel_driver = {
+ .probe = lirc_parallel_probe,
+ .remove = lirc_parallel_remove,
+ .suspend = lirc_parallel_suspend,
+ .resume = lirc_parallel_resume,
+ .driver = {
+ .name = LIRC_DRIVER_NAME,
+ },
+};
+
+static int pf(void *handle)
+{
+ parport_disable_irq(pport);
+ is_claimed = 0;
+ return 0;
+}
+
+static void kf(void *handle)
+{
+ if (!is_open)
+ return;
+ if (!lirc_claim())
+ return;
+ parport_enable_irq(pport);
+ lirc_off();
+ /* this is a bit annoying when you actually print...*/
+ /*
+ printk(KERN_INFO "%s: reclaimed port\n", LIRC_DRIVER_NAME);
+ */
+}
+
+/*** module initialization and cleanup ***/
+
+static int __init lirc_parallel_init(void)
+{
+ int result;
+
+ result = platform_driver_register(&lirc_parallel_driver);
+ if (result) {
+ pr_notice("platform_driver_register returned %d\n", result);
+ return result;
+ }
+
+ lirc_parallel_dev = platform_device_alloc(LIRC_DRIVER_NAME, 0);
+ if (!lirc_parallel_dev) {
+ result = -ENOMEM;
+ goto exit_driver_unregister;
+ }
+
+ result = platform_device_add(lirc_parallel_dev);
+ if (result)
+ goto exit_device_put;
+
+ pport = parport_find_base(io);
+ if (pport == NULL) {
+ pr_notice("no port at %x found\n", io);
+ result = -ENXIO;
+ goto exit_device_put;
+ }
+ ppdevice = parport_register_device(pport, LIRC_DRIVER_NAME,
+ pf, kf, lirc_lirc_irq_handler, 0,
+ NULL);
+ parport_put_port(pport);
+ if (ppdevice == NULL) {
+ pr_notice("parport_register_device() failed\n");
+ result = -ENXIO;
+ goto exit_device_put;
+ }
+ if (parport_claim(ppdevice) != 0)
+ goto skip_init;
+ is_claimed = 1;
+ out(LIRC_LP_CONTROL, LP_PSELECP|LP_PINITP);
+
+#ifdef LIRC_TIMER
+ if (debug)
+ out(LIRC_PORT_DATA, tx_mask);
+
+ timer = init_lirc_timer();
+
+#if 0 /* continue even if device is offline */
+ if (timer == 0) {
+ is_claimed = 0;
+ parport_release(pport);
+ parport_unregister_device(ppdevice);
+ result = -EIO;
+ goto exit_device_put;
+ }
+
+#endif
+ if (debug)
+ out(LIRC_PORT_DATA, 0);
+#endif
+
+ is_claimed = 0;
+ parport_release(ppdevice);
+ skip_init:
+ driver.dev = &lirc_parallel_dev->dev;
+ driver.minor = lirc_register_driver(&driver);
+ if (driver.minor < 0) {
+ pr_notice("register_chrdev() failed\n");
+ parport_unregister_device(ppdevice);
+ result = -EIO;
+ goto exit_device_put;
+ }
+ pr_info("installed using port 0x%04x irq %d\n", io, irq);
+ return 0;
+
+exit_device_put:
+ platform_device_put(lirc_parallel_dev);
+exit_driver_unregister:
+ platform_driver_unregister(&lirc_parallel_driver);
+ return result;
+}
+
+static void __exit lirc_parallel_exit(void)
+{
+ parport_unregister_device(ppdevice);
+ lirc_unregister_driver(driver.minor);
+
+ platform_device_unregister(lirc_parallel_dev);
+ platform_driver_unregister(&lirc_parallel_driver);
+}
+
+module_init(lirc_parallel_init);
+module_exit(lirc_parallel_exit);
+
+MODULE_DESCRIPTION("Infrared receiver driver for parallel ports.");
+MODULE_AUTHOR("Christoph Bartelmus");
+MODULE_LICENSE("GPL");
+
+module_param(io, int, S_IRUGO);
+MODULE_PARM_DESC(io, "I/O address base (0x3bc, 0x378 or 0x278)");
+
+module_param(irq, int, S_IRUGO);
+MODULE_PARM_DESC(irq, "Interrupt (7 or 5)");
+
+module_param(tx_mask, int, S_IRUGO);
+MODULE_PARM_DESC(tx_maxk, "Transmitter mask (default: 0x01)");
+
+module_param(debug, bool, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(debug, "Enable debugging messages");
+
+module_param(check_pselecd, bool, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(check_pselecd, "Check for printer (default: 0)");
diff --git a/drivers/staging/media/lirc/lirc_parallel.h b/drivers/staging/media/lirc/lirc_parallel.h
new file mode 100644
index 000000000..4bed6afe0
--- /dev/null
+++ b/drivers/staging/media/lirc/lirc_parallel.h
@@ -0,0 +1,26 @@
+/* lirc_parallel.h */
+
+#ifndef _LIRC_PARALLEL_H
+#define _LIRC_PARALLEL_H
+
+#include <linux/lp.h>
+
+#define LIRC_PORT_LEN 3
+
+#define LIRC_LP_BASE 0
+#define LIRC_LP_STATUS 1
+#define LIRC_LP_CONTROL 2
+
+#define LIRC_PORT_DATA LIRC_LP_BASE /* base */
+#define LIRC_PORT_TIMER LIRC_LP_STATUS /* status port */
+#define LIRC_PORT_TIMER_BIT LP_PBUSY /* busy signal */
+#define LIRC_PORT_SIGNAL LIRC_LP_STATUS /* status port */
+#define LIRC_PORT_SIGNAL_BIT LP_PACK /* ack signal */
+#define LIRC_PORT_IRQ LIRC_LP_CONTROL /* control port */
+
+#define LIRC_SFH506_DELAY 0 /* delay t_phl in usecs */
+
+#define LIRC_PARALLEL_MAX_TRANSMITTERS 8
+#define LIRC_PARALLEL_TRANSMITTER_MASK ((1<<LIRC_PARALLEL_MAX_TRANSMITTERS) - 1)
+
+#endif
diff --git a/drivers/staging/media/lirc/lirc_sasem.c b/drivers/staging/media/lirc/lirc_sasem.c
new file mode 100644
index 000000000..9e5674341
--- /dev/null
+++ b/drivers/staging/media/lirc/lirc_sasem.c
@@ -0,0 +1,921 @@
+/*
+ * lirc_sasem.c - USB remote support for LIRC
+ * Version 0.5
+ *
+ * Copyright (C) 2004-2005 Oliver Stabel <oliver.stabel@gmx.de>
+ * Tim Davies <tim@opensystems.net.au>
+ *
+ * This driver was derived from:
+ * Venky Raju <dev@venky.ws>
+ * "lirc_imon - "LIRC/VFD driver for Ahanix/Soundgraph IMON IR/VFD"
+ * Paul Miller <pmiller9@users.sourceforge.net>'s 2003-2004
+ * "lirc_atiusb - USB remote support for LIRC"
+ * Culver Consulting Services <henry@culcon.com>'s 2003
+ * "Sasem OnAir VFD/IR USB driver"
+ *
+ *
+ * NOTE - The LCDproc iMon driver should work with this module. More info at
+ * http://www.frogstorm.info/sasem
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or 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 pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+#include <linux/usb.h>
+
+#include <media/lirc.h>
+#include <media/lirc_dev.h>
+
+
+#define MOD_AUTHOR "Oliver Stabel <oliver.stabel@gmx.de>, " \
+ "Tim Davies <tim@opensystems.net.au>"
+#define MOD_DESC "USB Driver for Sasem Remote Controller V1.1"
+#define MOD_NAME "lirc_sasem"
+#define MOD_VERSION "0.5"
+
+#define VFD_MINOR_BASE 144 /* Same as LCD */
+#define DEVICE_NAME "lcd%d"
+
+#define BUF_CHUNK_SIZE 8
+#define BUF_SIZE 128
+
+#define IOCTL_LCD_CONTRAST 1
+
+/*** P R O T O T Y P E S ***/
+
+/* USB Callback prototypes */
+static int sasem_probe(struct usb_interface *interface,
+ const struct usb_device_id *id);
+static void sasem_disconnect(struct usb_interface *interface);
+static void usb_rx_callback(struct urb *urb);
+static void usb_tx_callback(struct urb *urb);
+
+/* VFD file_operations function prototypes */
+static int vfd_open(struct inode *inode, struct file *file);
+static long vfd_ioctl(struct file *file, unsigned cmd, unsigned long arg);
+static int vfd_close(struct inode *inode, struct file *file);
+static ssize_t vfd_write(struct file *file, const char __user *buf,
+ size_t n_bytes, loff_t *pos);
+
+/* LIRC driver function prototypes */
+static int ir_open(void *data);
+static void ir_close(void *data);
+
+/*** G L O B A L S ***/
+#define SASEM_DATA_BUF_SZ 32
+
+struct sasem_context {
+
+ struct usb_device *dev;
+ int vfd_isopen; /* VFD port has been opened */
+ unsigned int vfd_contrast; /* VFD contrast */
+ int ir_isopen; /* IR port has been opened */
+ int dev_present; /* USB device presence */
+ struct mutex ctx_lock; /* to lock this object */
+ wait_queue_head_t remove_ok; /* For unexpected USB disconnects */
+
+ struct lirc_driver *driver;
+ struct usb_endpoint_descriptor *rx_endpoint;
+ struct usb_endpoint_descriptor *tx_endpoint;
+ struct urb *rx_urb;
+ struct urb *tx_urb;
+ unsigned char usb_rx_buf[8];
+ unsigned char usb_tx_buf[8];
+
+ struct tx_t {
+ unsigned char data_buf[SASEM_DATA_BUF_SZ]; /* user data
+ * buffer */
+ struct completion finished; /* wait for write to finish */
+ atomic_t busy; /* write in progress */
+ int status; /* status of tx completion */
+ } tx;
+
+ /* for dealing with repeat codes (wish there was a toggle bit!) */
+ struct timeval presstime;
+ char lastcode[8];
+ int codesaved;
+};
+
+/* VFD file operations */
+static const struct file_operations vfd_fops = {
+ .owner = THIS_MODULE,
+ .open = &vfd_open,
+ .write = vfd_write,
+ .unlocked_ioctl = &vfd_ioctl,
+ .release = &vfd_close,
+ .llseek = noop_llseek,
+};
+
+/* USB Device ID for Sasem USB Control Board */
+static struct usb_device_id sasem_usb_id_table[] = {
+ /* Sasem USB Control Board */
+ { USB_DEVICE(0x11ba, 0x0101) },
+ /* Terminating entry */
+ {}
+};
+
+/* USB Device data */
+static struct usb_driver sasem_driver = {
+ .name = MOD_NAME,
+ .probe = sasem_probe,
+ .disconnect = sasem_disconnect,
+ .id_table = sasem_usb_id_table,
+};
+
+static struct usb_class_driver sasem_class = {
+ .name = DEVICE_NAME,
+ .fops = &vfd_fops,
+ .minor_base = VFD_MINOR_BASE,
+};
+
+/* to prevent races between open() and disconnect() */
+static DEFINE_MUTEX(disconnect_lock);
+
+static int debug;
+
+
+/*** M O D U L E C O D E ***/
+
+MODULE_AUTHOR(MOD_AUTHOR);
+MODULE_DESCRIPTION(MOD_DESC);
+MODULE_LICENSE("GPL");
+module_param(debug, int, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(debug, "Debug messages: 0=no, 1=yes (default: no)");
+
+static void delete_context(struct sasem_context *context)
+{
+ usb_free_urb(context->tx_urb); /* VFD */
+ usb_free_urb(context->rx_urb); /* IR */
+ lirc_buffer_free(context->driver->rbuf);
+ kfree(context->driver->rbuf);
+ kfree(context->driver);
+ kfree(context);
+}
+
+static void deregister_from_lirc(struct sasem_context *context)
+{
+ int retval;
+ int minor = context->driver->minor;
+
+ retval = lirc_unregister_driver(minor);
+ if (retval)
+ dev_err(&context->dev->dev,
+ "%s: unable to deregister from lirc (%d)\n",
+ __func__, retval);
+ else
+ dev_info(&context->dev->dev,
+ "Deregistered Sasem driver (minor:%d)\n", minor);
+
+}
+
+/**
+ * Called when the VFD device (e.g. /dev/usb/lcd)
+ * is opened by the application.
+ */
+static int vfd_open(struct inode *inode, struct file *file)
+{
+ struct usb_interface *interface;
+ struct sasem_context *context = NULL;
+ int subminor;
+ int retval = 0;
+
+ /* prevent races with disconnect */
+ mutex_lock(&disconnect_lock);
+
+ subminor = iminor(inode);
+ interface = usb_find_interface(&sasem_driver, subminor);
+ if (!interface) {
+ pr_err("%s: could not find interface for minor %d\n",
+ __func__, subminor);
+ retval = -ENODEV;
+ goto exit;
+ }
+ context = usb_get_intfdata(interface);
+
+ if (!context) {
+ dev_err(&interface->dev, "no context found for minor %d\n",
+ subminor);
+ retval = -ENODEV;
+ goto exit;
+ }
+
+ mutex_lock(&context->ctx_lock);
+
+ if (context->vfd_isopen) {
+ dev_err(&interface->dev,
+ "%s: VFD port is already open", __func__);
+ retval = -EBUSY;
+ } else {
+ context->vfd_isopen = 1;
+ file->private_data = context;
+ dev_info(&interface->dev, "VFD port opened\n");
+ }
+
+ mutex_unlock(&context->ctx_lock);
+
+exit:
+ mutex_unlock(&disconnect_lock);
+ return retval;
+}
+
+/**
+ * Called when the VFD device (e.g. /dev/usb/lcd)
+ * is closed by the application.
+ */
+static long vfd_ioctl(struct file *file, unsigned cmd, unsigned long arg)
+{
+ struct sasem_context *context = NULL;
+
+ context = (struct sasem_context *) file->private_data;
+
+ if (!context) {
+ pr_err("%s: no context for device\n", __func__);
+ return -ENODEV;
+ }
+
+ mutex_lock(&context->ctx_lock);
+
+ switch (cmd) {
+ case IOCTL_LCD_CONTRAST:
+ if (arg > 1000)
+ arg = 1000;
+ context->vfd_contrast = (unsigned int)arg;
+ break;
+ default:
+ pr_info("Unknown IOCTL command\n");
+ mutex_unlock(&context->ctx_lock);
+ return -ENOIOCTLCMD; /* not supported */
+ }
+
+ mutex_unlock(&context->ctx_lock);
+ return 0;
+}
+
+/**
+ * Called when the VFD device (e.g. /dev/usb/lcd)
+ * is closed by the application.
+ */
+static int vfd_close(struct inode *inode, struct file *file)
+{
+ struct sasem_context *context = NULL;
+ int retval = 0;
+
+ context = (struct sasem_context *) file->private_data;
+
+ if (!context) {
+ pr_err("%s: no context for device\n", __func__);
+ return -ENODEV;
+ }
+
+ mutex_lock(&context->ctx_lock);
+
+ if (!context->vfd_isopen) {
+ dev_err(&context->dev->dev, "%s: VFD is not open\n", __func__);
+ retval = -EIO;
+ } else {
+ context->vfd_isopen = 0;
+ dev_info(&context->dev->dev, "VFD port closed\n");
+ if (!context->dev_present && !context->ir_isopen) {
+
+ /* Device disconnected before close and IR port is
+ * not open. If IR port is open, context will be
+ * deleted by ir_close. */
+ mutex_unlock(&context->ctx_lock);
+ delete_context(context);
+ return retval;
+ }
+ }
+
+ mutex_unlock(&context->ctx_lock);
+ return retval;
+}
+
+/**
+ * Sends a packet to the VFD.
+ */
+static int send_packet(struct sasem_context *context)
+{
+ unsigned int pipe;
+ int interval = 0;
+ int retval = 0;
+
+ pipe = usb_sndintpipe(context->dev,
+ context->tx_endpoint->bEndpointAddress);
+ interval = context->tx_endpoint->bInterval;
+
+ usb_fill_int_urb(context->tx_urb, context->dev, pipe,
+ context->usb_tx_buf, sizeof(context->usb_tx_buf),
+ usb_tx_callback, context, interval);
+
+ context->tx_urb->actual_length = 0;
+
+ init_completion(&context->tx.finished);
+ atomic_set(&context->tx.busy, 1);
+
+ retval = usb_submit_urb(context->tx_urb, GFP_KERNEL);
+ if (retval) {
+ atomic_set(&context->tx.busy, 0);
+ dev_err(&context->dev->dev, "error submitting urb (%d)\n",
+ retval);
+ } else {
+ /* Wait for transmission to complete (or abort) */
+ mutex_unlock(&context->ctx_lock);
+ wait_for_completion(&context->tx.finished);
+ mutex_lock(&context->ctx_lock);
+
+ retval = context->tx.status;
+ if (retval)
+ dev_err(&context->dev->dev,
+ "packet tx failed (%d)\n", retval);
+ }
+
+ return retval;
+}
+
+/**
+ * Writes data to the VFD. The Sasem VFD is 2x16 characters
+ * and requires data in 9 consecutive USB interrupt packets,
+ * each packet carrying 8 bytes.
+ */
+static ssize_t vfd_write(struct file *file, const char __user *buf,
+ size_t n_bytes, loff_t *pos)
+{
+ int i;
+ int retval = 0;
+ struct sasem_context *context;
+ int *data_buf = NULL;
+
+ context = (struct sasem_context *) file->private_data;
+ if (!context) {
+ pr_err("%s: no context for device\n", __func__);
+ return -ENODEV;
+ }
+
+ mutex_lock(&context->ctx_lock);
+
+ if (!context->dev_present) {
+ pr_err("%s: no Sasem device present\n", __func__);
+ retval = -ENODEV;
+ goto exit;
+ }
+
+ if (n_bytes <= 0 || n_bytes > SASEM_DATA_BUF_SZ) {
+ dev_err(&context->dev->dev, "%s: invalid payload size\n",
+ __func__);
+ retval = -EINVAL;
+ goto exit;
+ }
+
+ data_buf = memdup_user(buf, n_bytes);
+ if (IS_ERR(data_buf)) {
+ retval = PTR_ERR(data_buf);
+ data_buf = NULL;
+ goto exit;
+ }
+
+ memcpy(context->tx.data_buf, data_buf, n_bytes);
+
+ /* Pad with spaces */
+ for (i = n_bytes; i < SASEM_DATA_BUF_SZ; ++i)
+ context->tx.data_buf[i] = ' ';
+
+ /* Nine 8 byte packets to be sent */
+ /* NOTE: "\x07\x01\0\0\0\0\0\0" or "\x0c\0\0\0\0\0\0\0"
+ * will clear the VFD */
+ for (i = 0; i < 9; i++) {
+ switch (i) {
+ case 0:
+ memcpy(context->usb_tx_buf, "\x07\0\0\0\0\0\0\0", 8);
+ context->usb_tx_buf[1] = (context->vfd_contrast) ?
+ (0x2B - (context->vfd_contrast - 1) / 250)
+ : 0x2B;
+ break;
+ case 1:
+ memcpy(context->usb_tx_buf, "\x09\x01\0\0\0\0\0\0", 8);
+ break;
+ case 2:
+ memcpy(context->usb_tx_buf, "\x0b\x01\0\0\0\0\0\0", 8);
+ break;
+ case 3:
+ memcpy(context->usb_tx_buf, context->tx.data_buf, 8);
+ break;
+ case 4:
+ memcpy(context->usb_tx_buf,
+ context->tx.data_buf + 8, 8);
+ break;
+ case 5:
+ memcpy(context->usb_tx_buf, "\x09\x01\0\0\0\0\0\0", 8);
+ break;
+ case 6:
+ memcpy(context->usb_tx_buf, "\x0b\x02\0\0\0\0\0\0", 8);
+ break;
+ case 7:
+ memcpy(context->usb_tx_buf,
+ context->tx.data_buf + 16, 8);
+ break;
+ case 8:
+ memcpy(context->usb_tx_buf,
+ context->tx.data_buf + 24, 8);
+ break;
+ }
+ retval = send_packet(context);
+ if (retval) {
+ dev_err(&context->dev->dev,
+ "send packet failed for packet #%d\n", i);
+ goto exit;
+ }
+ }
+exit:
+
+ mutex_unlock(&context->ctx_lock);
+ kfree(data_buf);
+
+ return (!retval) ? n_bytes : retval;
+}
+
+/**
+ * Callback function for USB core API: transmit data
+ */
+static void usb_tx_callback(struct urb *urb)
+{
+ struct sasem_context *context;
+
+ if (!urb)
+ return;
+ context = (struct sasem_context *) urb->context;
+ if (!context)
+ return;
+
+ context->tx.status = urb->status;
+
+ /* notify waiters that write has finished */
+ atomic_set(&context->tx.busy, 0);
+ complete(&context->tx.finished);
+}
+
+/**
+ * Called by lirc_dev when the application opens /dev/lirc
+ */
+static int ir_open(void *data)
+{
+ int retval = 0;
+ struct sasem_context *context;
+
+ /* prevent races with disconnect */
+ mutex_lock(&disconnect_lock);
+
+ context = data;
+
+ mutex_lock(&context->ctx_lock);
+
+ if (context->ir_isopen) {
+ dev_err(&context->dev->dev, "%s: IR port is already open\n",
+ __func__);
+ retval = -EBUSY;
+ goto exit;
+ }
+
+ usb_fill_int_urb(context->rx_urb, context->dev,
+ usb_rcvintpipe(context->dev,
+ context->rx_endpoint->bEndpointAddress),
+ context->usb_rx_buf, sizeof(context->usb_rx_buf),
+ usb_rx_callback, context, context->rx_endpoint->bInterval);
+
+ retval = usb_submit_urb(context->rx_urb, GFP_KERNEL);
+
+ if (retval)
+ dev_err(&context->dev->dev,
+ "usb_submit_urb failed for ir_open (%d)\n", retval);
+ else {
+ context->ir_isopen = 1;
+ dev_info(&context->dev->dev, "IR port opened\n");
+ }
+
+exit:
+ mutex_unlock(&context->ctx_lock);
+
+ mutex_unlock(&disconnect_lock);
+ return retval;
+}
+
+/**
+ * Called by lirc_dev when the application closes /dev/lirc
+ */
+static void ir_close(void *data)
+{
+ struct sasem_context *context;
+
+ context = data;
+ if (!context) {
+ pr_err("%s: no context for device\n", __func__);
+ return;
+ }
+
+ mutex_lock(&context->ctx_lock);
+
+ usb_kill_urb(context->rx_urb);
+ context->ir_isopen = 0;
+ pr_info("IR port closed\n");
+
+ if (!context->dev_present) {
+
+ /*
+ * Device disconnected while IR port was
+ * still open. Driver was not deregistered
+ * at disconnect time, so do it now.
+ */
+ deregister_from_lirc(context);
+
+ if (!context->vfd_isopen) {
+
+ mutex_unlock(&context->ctx_lock);
+ delete_context(context);
+ return;
+ }
+ /* If VFD port is open, context will be deleted by vfd_close */
+ }
+
+ mutex_unlock(&context->ctx_lock);
+}
+
+/**
+ * Process the incoming packet
+ */
+static void incoming_packet(struct sasem_context *context,
+ struct urb *urb)
+{
+ int len = urb->actual_length;
+ unsigned char *buf = urb->transfer_buffer;
+ long ms;
+ struct timeval tv;
+
+ if (len != 8) {
+ dev_warn(&context->dev->dev,
+ "%s: invalid incoming packet size (%d)\n",
+ __func__, len);
+ return;
+ }
+
+ if (debug)
+ dev_info(&context->dev->dev, "Incoming data: %*ph\n", len, buf);
+ /*
+ * Lirc could deal with the repeat code, but we really need to block it
+ * if it arrives too late. Otherwise we could repeat the wrong code.
+ */
+
+ /* get the time since the last button press */
+ do_gettimeofday(&tv);
+ ms = (tv.tv_sec - context->presstime.tv_sec) * 1000 +
+ (tv.tv_usec - context->presstime.tv_usec) / 1000;
+
+ if (memcmp(buf, "\x08\0\0\0\0\0\0\0", 8) == 0) {
+ /*
+ * the repeat code is being sent, so we copy
+ * the old code to LIRC
+ */
+
+ /*
+ * NOTE: Only if the last code was less than 250ms ago
+ * - no one should be able to push another (undetected) button
+ * in that time and then get a false repeat of the previous
+ * press but it is long enough for a genuine repeat
+ */
+ if ((ms < 250) && (context->codesaved != 0)) {
+ memcpy(buf, &context->lastcode, 8);
+ context->presstime.tv_sec = tv.tv_sec;
+ context->presstime.tv_usec = tv.tv_usec;
+ }
+ } else {
+ /* save the current valid code for repeats */
+ memcpy(&context->lastcode, buf, 8);
+ /*
+ * set flag to signal a valid code was save;
+ * just for safety reasons
+ */
+ context->codesaved = 1;
+ context->presstime.tv_sec = tv.tv_sec;
+ context->presstime.tv_usec = tv.tv_usec;
+ }
+
+ lirc_buffer_write(context->driver->rbuf, buf);
+ wake_up(&context->driver->rbuf->wait_poll);
+}
+
+/**
+ * Callback function for USB core API: receive data
+ */
+static void usb_rx_callback(struct urb *urb)
+{
+ struct sasem_context *context;
+
+ if (!urb)
+ return;
+ context = (struct sasem_context *) urb->context;
+ if (!context)
+ return;
+
+ switch (urb->status) {
+
+ case -ENOENT: /* usbcore unlink successful! */
+ return;
+
+ case 0:
+ if (context->ir_isopen)
+ incoming_packet(context, urb);
+ break;
+
+ default:
+ dev_warn(&urb->dev->dev, "%s: status (%d): ignored",
+ __func__, urb->status);
+ break;
+ }
+
+ usb_submit_urb(context->rx_urb, GFP_ATOMIC);
+}
+
+
+
+/**
+ * Callback function for USB core API: Probe
+ */
+static int sasem_probe(struct usb_interface *interface,
+ const struct usb_device_id *id)
+{
+ struct usb_device *dev = NULL;
+ struct usb_host_interface *iface_desc = NULL;
+ struct usb_endpoint_descriptor *rx_endpoint = NULL;
+ struct usb_endpoint_descriptor *tx_endpoint = NULL;
+ struct urb *rx_urb = NULL;
+ struct urb *tx_urb = NULL;
+ struct lirc_driver *driver = NULL;
+ struct lirc_buffer *rbuf = NULL;
+ int lirc_minor = 0;
+ int num_endpoints;
+ int retval = 0;
+ int vfd_ep_found;
+ int ir_ep_found;
+ int alloc_status;
+ struct sasem_context *context = NULL;
+ int i;
+
+ dev_info(&interface->dev, "%s: found Sasem device\n", __func__);
+
+
+ dev = usb_get_dev(interface_to_usbdev(interface));
+ iface_desc = interface->cur_altsetting;
+ num_endpoints = iface_desc->desc.bNumEndpoints;
+
+ /*
+ * Scan the endpoint list and set:
+ * first input endpoint = IR endpoint
+ * first output endpoint = VFD endpoint
+ */
+
+ ir_ep_found = 0;
+ vfd_ep_found = 0;
+
+ for (i = 0; i < num_endpoints && !(ir_ep_found && vfd_ep_found); ++i) {
+
+ struct usb_endpoint_descriptor *ep;
+ int ep_dir;
+ int ep_type;
+
+ ep = &iface_desc->endpoint [i].desc;
+ ep_dir = ep->bEndpointAddress & USB_ENDPOINT_DIR_MASK;
+ ep_type = ep->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK;
+
+ if (!ir_ep_found &&
+ ep_dir == USB_DIR_IN &&
+ ep_type == USB_ENDPOINT_XFER_INT) {
+
+ rx_endpoint = ep;
+ ir_ep_found = 1;
+ if (debug)
+ dev_info(&interface->dev,
+ "%s: found IR endpoint\n", __func__);
+
+ } else if (!vfd_ep_found &&
+ ep_dir == USB_DIR_OUT &&
+ ep_type == USB_ENDPOINT_XFER_INT) {
+
+ tx_endpoint = ep;
+ vfd_ep_found = 1;
+ if (debug)
+ dev_info(&interface->dev,
+ "%s: found VFD endpoint\n", __func__);
+ }
+ }
+
+ /* Input endpoint is mandatory */
+ if (!ir_ep_found) {
+ dev_err(&interface->dev,
+ "%s: no valid input (IR) endpoint found.\n", __func__);
+ retval = -ENODEV;
+ goto exit;
+ }
+
+ if (!vfd_ep_found)
+ dev_info(&interface->dev,
+ "%s: no valid output (VFD) endpoint found.\n",
+ __func__);
+
+
+ /* Allocate memory */
+ alloc_status = 0;
+
+ context = kzalloc(sizeof(struct sasem_context), GFP_KERNEL);
+ if (!context) {
+ alloc_status = 1;
+ goto alloc_status_switch;
+ }
+ driver = kzalloc(sizeof(struct lirc_driver), GFP_KERNEL);
+ if (!driver) {
+ alloc_status = 2;
+ goto alloc_status_switch;
+ }
+ rbuf = kmalloc(sizeof(struct lirc_buffer), GFP_KERNEL);
+ if (!rbuf) {
+ alloc_status = 3;
+ goto alloc_status_switch;
+ }
+ if (lirc_buffer_init(rbuf, BUF_CHUNK_SIZE, BUF_SIZE)) {
+ dev_err(&interface->dev,
+ "%s: lirc_buffer_init failed\n", __func__);
+ alloc_status = 4;
+ goto alloc_status_switch;
+ }
+ rx_urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!rx_urb) {
+ dev_err(&interface->dev,
+ "%s: usb_alloc_urb failed for IR urb\n", __func__);
+ alloc_status = 5;
+ goto alloc_status_switch;
+ }
+ if (vfd_ep_found) {
+ tx_urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!tx_urb) {
+ dev_err(&interface->dev,
+ "%s: usb_alloc_urb failed for VFD urb",
+ __func__);
+ alloc_status = 6;
+ goto alloc_status_switch;
+ }
+ }
+
+ mutex_init(&context->ctx_lock);
+
+ strcpy(driver->name, MOD_NAME);
+ driver->minor = -1;
+ driver->code_length = 64;
+ driver->sample_rate = 0;
+ driver->features = LIRC_CAN_REC_LIRCCODE;
+ driver->data = context;
+ driver->rbuf = rbuf;
+ driver->set_use_inc = ir_open;
+ driver->set_use_dec = ir_close;
+ driver->dev = &interface->dev;
+ driver->owner = THIS_MODULE;
+
+ mutex_lock(&context->ctx_lock);
+
+ lirc_minor = lirc_register_driver(driver);
+ if (lirc_minor < 0) {
+ dev_err(&interface->dev,
+ "%s: lirc_register_driver failed\n", __func__);
+ alloc_status = 7;
+ retval = lirc_minor;
+ goto unlock;
+ } else
+ dev_info(&interface->dev,
+ "%s: Registered Sasem driver (minor:%d)\n",
+ __func__, lirc_minor);
+
+ /* Needed while unregistering! */
+ driver->minor = lirc_minor;
+
+ context->dev = dev;
+ context->dev_present = 1;
+ context->rx_endpoint = rx_endpoint;
+ context->rx_urb = rx_urb;
+ if (vfd_ep_found) {
+ context->tx_endpoint = tx_endpoint;
+ context->tx_urb = tx_urb;
+ context->vfd_contrast = 1000; /* range 0 - 1000 */
+ }
+ context->driver = driver;
+
+ usb_set_intfdata(interface, context);
+
+ if (vfd_ep_found) {
+
+ if (debug)
+ dev_info(&interface->dev,
+ "Registering VFD with sysfs\n");
+ if (usb_register_dev(interface, &sasem_class))
+ /* Not a fatal error, so ignore */
+ dev_info(&interface->dev,
+ "%s: could not get a minor number for VFD\n",
+ __func__);
+ }
+
+ dev_info(&interface->dev,
+ "%s: Sasem device on usb<%d:%d> initialized\n",
+ __func__, dev->bus->busnum, dev->devnum);
+unlock:
+ mutex_unlock(&context->ctx_lock);
+
+alloc_status_switch:
+ switch (alloc_status) {
+
+ case 7:
+ if (vfd_ep_found)
+ usb_free_urb(tx_urb);
+ case 6:
+ usb_free_urb(rx_urb);
+ /* fall-through */
+ case 5:
+ lirc_buffer_free(rbuf);
+ /* fall-through */
+ case 4:
+ kfree(rbuf);
+ /* fall-through */
+ case 3:
+ kfree(driver);
+ /* fall-through */
+ case 2:
+ kfree(context);
+ context = NULL;
+ /* fall-through */
+ case 1:
+ if (retval == 0)
+ retval = -ENOMEM;
+ }
+
+exit:
+ return retval;
+}
+
+/**
+ * Callback function for USB core API: disconnect
+ */
+static void sasem_disconnect(struct usb_interface *interface)
+{
+ struct sasem_context *context;
+
+ /* prevent races with ir_open()/vfd_open() */
+ mutex_lock(&disconnect_lock);
+
+ context = usb_get_intfdata(interface);
+ mutex_lock(&context->ctx_lock);
+
+ dev_info(&interface->dev, "%s: Sasem device disconnected\n",
+ __func__);
+
+ usb_set_intfdata(interface, NULL);
+ context->dev_present = 0;
+
+ /* Stop reception */
+ usb_kill_urb(context->rx_urb);
+
+ /* Abort ongoing write */
+ if (atomic_read(&context->tx.busy)) {
+
+ usb_kill_urb(context->tx_urb);
+ wait_for_completion(&context->tx.finished);
+ }
+
+ /* De-register from lirc_dev if IR port is not open */
+ if (!context->ir_isopen)
+ deregister_from_lirc(context);
+
+ usb_deregister_dev(interface, &sasem_class);
+
+ mutex_unlock(&context->ctx_lock);
+
+ if (!context->ir_isopen && !context->vfd_isopen)
+ delete_context(context);
+
+ mutex_unlock(&disconnect_lock);
+}
+
+module_usb_driver(sasem_driver);
diff --git a/drivers/staging/media/lirc/lirc_serial.c b/drivers/staging/media/lirc/lirc_serial.c
new file mode 100644
index 000000000..dc7984455
--- /dev/null
+++ b/drivers/staging/media/lirc/lirc_serial.c
@@ -0,0 +1,1213 @@
+/*
+ * lirc_serial.c
+ *
+ * lirc_serial - Device driver that records pulse- and pause-lengths
+ * (space-lengths) between DDCD event on a serial port.
+ *
+ * Copyright (C) 1996,97 Ralph Metzler <rjkm@thp.uni-koeln.de>
+ * Copyright (C) 1998 Trent Piepho <xyzzy@u.washington.edu>
+ * Copyright (C) 1998 Ben Pfaff <blp@gnu.org>
+ * Copyright (C) 1999 Christoph Bartelmus <lirc@bartelmus.de>
+ * Copyright (C) 2007 Andrei Tanas <andrei@tanas.ca> (suspend/resume support)
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+/*
+ * Steve's changes to improve transmission fidelity:
+ * - for systems with the rdtsc instruction and the clock counter, a
+ * send_pule that times the pulses directly using the counter.
+ * This means that the LIRC_SERIAL_TRANSMITTER_LATENCY fudge is
+ * not needed. Measurement shows very stable waveform, even where
+ * PCI activity slows the access to the UART, which trips up other
+ * versions.
+ * - For other system, non-integer-microsecond pulse/space lengths,
+ * done using fixed point binary. So, much more accurate carrier
+ * frequency.
+ * - fine tuned transmitter latency, taking advantage of fractional
+ * microseconds in previous change
+ * - Fixed bug in the way transmitter latency was accounted for by
+ * tuning the pulse lengths down - the send_pulse routine ignored
+ * this overhead as it timed the overall pulse length - so the
+ * pulse frequency was right but overall pulse length was too
+ * long. Fixed by accounting for latency on each pulse/space
+ * iteration.
+ *
+ * Steve Davies <steve@daviesfam.org> July 2001
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/fs.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/kernel.h>
+#include <linux/serial_reg.h>
+#include <linux/time.h>
+#include <linux/string.h>
+#include <linux/types.h>
+#include <linux/wait.h>
+#include <linux/mm.h>
+#include <linux/delay.h>
+#include <linux/poll.h>
+#include <linux/platform_device.h>
+#include <linux/gpio.h>
+#include <linux/io.h>
+#include <linux/irq.h>
+#include <linux/fcntl.h>
+#include <linux/spinlock.h>
+
+/* From Intel IXP42X Developer's Manual (#252480-005): */
+/* ftp://download.intel.com/design/network/manuals/25248005.pdf */
+#define UART_IE_IXP42X_UUE 0x40 /* IXP42X UART Unit enable */
+#define UART_IE_IXP42X_RTOIE 0x10 /* IXP42X Receiver Data Timeout int.enable */
+
+#include <media/lirc.h>
+#include <media/lirc_dev.h>
+
+#define LIRC_DRIVER_NAME "lirc_serial"
+
+struct lirc_serial {
+ int signal_pin;
+ int signal_pin_change;
+ u8 on;
+ u8 off;
+ long (*send_pulse)(unsigned long length);
+ void (*send_space)(long length);
+ int features;
+ spinlock_t lock;
+};
+
+#define LIRC_HOMEBREW 0
+#define LIRC_IRDEO 1
+#define LIRC_IRDEO_REMOTE 2
+#define LIRC_ANIMAX 3
+#define LIRC_IGOR 4
+#define LIRC_NSLU2 5
+
+/*** module parameters ***/
+static int type;
+static int io;
+static int irq;
+static bool iommap;
+static int ioshift;
+static bool softcarrier = true;
+static bool share_irq;
+static bool debug;
+static int sense = -1; /* -1 = auto, 0 = active high, 1 = active low */
+static bool txsense; /* 0 = active high, 1 = active low */
+
+#define dprintk(fmt, args...) \
+ do { \
+ if (debug) \
+ printk(KERN_DEBUG LIRC_DRIVER_NAME ": " \
+ fmt, ## args); \
+ } while (0)
+
+/* forward declarations */
+static long send_pulse_irdeo(unsigned long length);
+static long send_pulse_homebrew(unsigned long length);
+static void send_space_irdeo(long length);
+static void send_space_homebrew(long length);
+
+static struct lirc_serial hardware[] = {
+ [LIRC_HOMEBREW] = {
+ .lock = __SPIN_LOCK_UNLOCKED(hardware[LIRC_HOMEBREW].lock),
+ .signal_pin = UART_MSR_DCD,
+ .signal_pin_change = UART_MSR_DDCD,
+ .on = (UART_MCR_RTS | UART_MCR_OUT2 | UART_MCR_DTR),
+ .off = (UART_MCR_RTS | UART_MCR_OUT2),
+ .send_pulse = send_pulse_homebrew,
+ .send_space = send_space_homebrew,
+#ifdef CONFIG_LIRC_SERIAL_TRANSMITTER
+ .features = (LIRC_CAN_SET_SEND_DUTY_CYCLE |
+ LIRC_CAN_SET_SEND_CARRIER |
+ LIRC_CAN_SEND_PULSE | LIRC_CAN_REC_MODE2)
+#else
+ .features = LIRC_CAN_REC_MODE2
+#endif
+ },
+
+ [LIRC_IRDEO] = {
+ .lock = __SPIN_LOCK_UNLOCKED(hardware[LIRC_IRDEO].lock),
+ .signal_pin = UART_MSR_DSR,
+ .signal_pin_change = UART_MSR_DDSR,
+ .on = UART_MCR_OUT2,
+ .off = (UART_MCR_RTS | UART_MCR_DTR | UART_MCR_OUT2),
+ .send_pulse = send_pulse_irdeo,
+ .send_space = send_space_irdeo,
+ .features = (LIRC_CAN_SET_SEND_DUTY_CYCLE |
+ LIRC_CAN_SEND_PULSE | LIRC_CAN_REC_MODE2)
+ },
+
+ [LIRC_IRDEO_REMOTE] = {
+ .lock = __SPIN_LOCK_UNLOCKED(hardware[LIRC_IRDEO_REMOTE].lock),
+ .signal_pin = UART_MSR_DSR,
+ .signal_pin_change = UART_MSR_DDSR,
+ .on = (UART_MCR_RTS | UART_MCR_DTR | UART_MCR_OUT2),
+ .off = (UART_MCR_RTS | UART_MCR_DTR | UART_MCR_OUT2),
+ .send_pulse = send_pulse_irdeo,
+ .send_space = send_space_irdeo,
+ .features = (LIRC_CAN_SET_SEND_DUTY_CYCLE |
+ LIRC_CAN_SEND_PULSE | LIRC_CAN_REC_MODE2)
+ },
+
+ [LIRC_ANIMAX] = {
+ .lock = __SPIN_LOCK_UNLOCKED(hardware[LIRC_ANIMAX].lock),
+ .signal_pin = UART_MSR_DCD,
+ .signal_pin_change = UART_MSR_DDCD,
+ .on = 0,
+ .off = (UART_MCR_RTS | UART_MCR_DTR | UART_MCR_OUT2),
+ .send_pulse = NULL,
+ .send_space = NULL,
+ .features = LIRC_CAN_REC_MODE2
+ },
+
+ [LIRC_IGOR] = {
+ .lock = __SPIN_LOCK_UNLOCKED(hardware[LIRC_IGOR].lock),
+ .signal_pin = UART_MSR_DSR,
+ .signal_pin_change = UART_MSR_DDSR,
+ .on = (UART_MCR_RTS | UART_MCR_OUT2 | UART_MCR_DTR),
+ .off = (UART_MCR_RTS | UART_MCR_OUT2),
+ .send_pulse = send_pulse_homebrew,
+ .send_space = send_space_homebrew,
+#ifdef CONFIG_LIRC_SERIAL_TRANSMITTER
+ .features = (LIRC_CAN_SET_SEND_DUTY_CYCLE |
+ LIRC_CAN_SET_SEND_CARRIER |
+ LIRC_CAN_SEND_PULSE | LIRC_CAN_REC_MODE2)
+#else
+ .features = LIRC_CAN_REC_MODE2
+#endif
+ },
+};
+
+#define RS_ISR_PASS_LIMIT 256
+
+/*
+ * A long pulse code from a remote might take up to 300 bytes. The
+ * daemon should read the bytes as soon as they are generated, so take
+ * the number of keys you think you can push before the daemon runs
+ * and multiply by 300. The driver will warn you if you overrun this
+ * buffer. If you have a slow computer or non-busmastering IDE disks,
+ * maybe you will need to increase this.
+ */
+
+/* This MUST be a power of two! It has to be larger than 1 as well. */
+
+#define RBUF_LEN 256
+
+static struct timeval lasttv = {0, 0};
+
+static struct lirc_buffer rbuf;
+
+static unsigned int freq = 38000;
+static unsigned int duty_cycle = 50;
+
+/* Initialized in init_timing_params() */
+static unsigned long period;
+static unsigned long pulse_width;
+static unsigned long space_width;
+
+#if defined(__i386__)
+/*
+ * From:
+ * Linux I/O port programming mini-HOWTO
+ * Author: Riku Saikkonen <Riku.Saikkonen@hut.fi>
+ * v, 28 December 1997
+ *
+ * [...]
+ * Actually, a port I/O instruction on most ports in the 0-0x3ff range
+ * takes almost exactly 1 microsecond, so if you're, for example, using
+ * the parallel port directly, just do additional inb()s from that port
+ * to delay.
+ * [...]
+ */
+/* transmitter latency 1.5625us 0x1.90 - this figure arrived at from
+ * comment above plus trimming to match actual measured frequency.
+ * This will be sensitive to cpu speed, though hopefully most of the 1.5us
+ * is spent in the uart access. Still - for reference test machine was a
+ * 1.13GHz Athlon system - Steve
+ */
+
+/*
+ * changed from 400 to 450 as this works better on slower machines;
+ * faster machines will use the rdtsc code anyway
+ */
+#define LIRC_SERIAL_TRANSMITTER_LATENCY 450
+
+#else
+
+/* does anybody have information on other platforms ? */
+/* 256 = 1<<8 */
+#define LIRC_SERIAL_TRANSMITTER_LATENCY 256
+
+#endif /* __i386__ */
+/*
+ * FIXME: should we be using hrtimers instead of this
+ * LIRC_SERIAL_TRANSMITTER_LATENCY nonsense?
+ */
+
+/* fetch serial input packet (1 byte) from register offset */
+static u8 sinp(int offset)
+{
+ if (iommap)
+ /* the register is memory-mapped */
+ offset <<= ioshift;
+
+ return inb(io + offset);
+}
+
+/* write serial output packet (1 byte) of value to register offset */
+static void soutp(int offset, u8 value)
+{
+ if (iommap)
+ /* the register is memory-mapped */
+ offset <<= ioshift;
+
+ outb(value, io + offset);
+}
+
+static void on(void)
+{
+ if (txsense)
+ soutp(UART_MCR, hardware[type].off);
+ else
+ soutp(UART_MCR, hardware[type].on);
+}
+
+static void off(void)
+{
+ if (txsense)
+ soutp(UART_MCR, hardware[type].on);
+ else
+ soutp(UART_MCR, hardware[type].off);
+}
+
+#ifndef MAX_UDELAY_MS
+#define MAX_UDELAY_US 5000
+#else
+#define MAX_UDELAY_US (MAX_UDELAY_MS*1000)
+#endif
+
+static void safe_udelay(unsigned long usecs)
+{
+ while (usecs > MAX_UDELAY_US) {
+ udelay(MAX_UDELAY_US);
+ usecs -= MAX_UDELAY_US;
+ }
+ udelay(usecs);
+}
+
+#ifdef USE_RDTSC
+/*
+ * This is an overflow/precision juggle, complicated in that we can't
+ * do long long divide in the kernel
+ */
+
+/*
+ * When we use the rdtsc instruction to measure clocks, we keep the
+ * pulse and space widths as clock cycles. As this is CPU speed
+ * dependent, the widths must be calculated in init_port and ioctl
+ * time
+ */
+
+/* So send_pulse can quickly convert microseconds to clocks */
+static unsigned long conv_us_to_clocks;
+
+static int init_timing_params(unsigned int new_duty_cycle,
+ unsigned int new_freq)
+{
+ __u64 loops_per_sec, work;
+
+ duty_cycle = new_duty_cycle;
+ freq = new_freq;
+
+ loops_per_sec = __this_cpu_read(cpu.info.loops_per_jiffy);
+ loops_per_sec *= HZ;
+
+ /* How many clocks in a microsecond?, avoiding long long divide */
+ work = loops_per_sec;
+ work *= 4295; /* 4295 = 2^32 / 1e6 */
+ conv_us_to_clocks = work >> 32;
+
+ /*
+ * Carrier period in clocks, approach good up to 32GHz clock,
+ * gets carrier frequency within 8Hz
+ */
+ period = loops_per_sec >> 3;
+ period /= (freq >> 3);
+
+ /* Derive pulse and space from the period */
+ pulse_width = period * duty_cycle / 100;
+ space_width = period - pulse_width;
+ dprintk("in init_timing_params, freq=%d, duty_cycle=%d, "
+ "clk/jiffy=%ld, pulse=%ld, space=%ld, "
+ "conv_us_to_clocks=%ld\n",
+ freq, duty_cycle, __this_cpu_read(cpu_info.loops_per_jiffy),
+ pulse_width, space_width, conv_us_to_clocks);
+ return 0;
+}
+#else /* ! USE_RDTSC */
+static int init_timing_params(unsigned int new_duty_cycle,
+ unsigned int new_freq)
+{
+/*
+ * period, pulse/space width are kept with 8 binary places -
+ * IE multiplied by 256.
+ */
+ if (256 * 1000000L / new_freq * new_duty_cycle / 100 <=
+ LIRC_SERIAL_TRANSMITTER_LATENCY)
+ return -EINVAL;
+ if (256 * 1000000L / new_freq * (100 - new_duty_cycle) / 100 <=
+ LIRC_SERIAL_TRANSMITTER_LATENCY)
+ return -EINVAL;
+ duty_cycle = new_duty_cycle;
+ freq = new_freq;
+ period = 256 * 1000000L / freq;
+ pulse_width = period * duty_cycle / 100;
+ space_width = period - pulse_width;
+ dprintk("in init_timing_params, freq=%d pulse=%ld, space=%ld\n",
+ freq, pulse_width, space_width);
+ return 0;
+}
+#endif /* USE_RDTSC */
+
+
+/* return value: space length delta */
+
+static long send_pulse_irdeo(unsigned long length)
+{
+ long rawbits, ret;
+ int i;
+ unsigned char output;
+ unsigned char chunk, shifted;
+
+ /* how many bits have to be sent ? */
+ rawbits = length * 1152 / 10000;
+ if (duty_cycle > 50)
+ chunk = 3;
+ else
+ chunk = 1;
+ for (i = 0, output = 0x7f; rawbits > 0; rawbits -= 3) {
+ shifted = chunk << (i * 3);
+ shifted >>= 1;
+ output &= (~shifted);
+ i++;
+ if (i == 3) {
+ soutp(UART_TX, output);
+ while (!(sinp(UART_LSR) & UART_LSR_THRE))
+ ;
+ output = 0x7f;
+ i = 0;
+ }
+ }
+ if (i != 0) {
+ soutp(UART_TX, output);
+ while (!(sinp(UART_LSR) & UART_LSR_TEMT))
+ ;
+ }
+
+ if (i == 0)
+ ret = (-rawbits) * 10000 / 1152;
+ else
+ ret = (3 - i) * 3 * 10000 / 1152 + (-rawbits) * 10000 / 1152;
+
+ return ret;
+}
+
+#ifdef USE_RDTSC
+/* Version that uses Pentium rdtsc instruction to measure clocks */
+
+/*
+ * This version does sub-microsecond timing using rdtsc instruction,
+ * and does away with the fudged LIRC_SERIAL_TRANSMITTER_LATENCY
+ * Implicitly i586 architecture... - Steve
+ */
+
+static long send_pulse_homebrew_softcarrier(unsigned long length)
+{
+ int flag;
+ unsigned long target, start, now;
+
+ /* Get going quick as we can */
+ rdtscl(start);
+ on();
+ /* Convert length from microseconds to clocks */
+ length *= conv_us_to_clocks;
+ /* And loop till time is up - flipping at right intervals */
+ now = start;
+ target = pulse_width;
+ flag = 1;
+ /*
+ * FIXME: This looks like a hard busy wait, without even an occasional,
+ * polite, cpu_relax() call. There's got to be a better way?
+ *
+ * The i2c code has the result of a lot of bit-banging work, I wonder if
+ * there's something there which could be helpful here.
+ */
+ while ((now - start) < length) {
+ /* Delay till flip time */
+ do {
+ rdtscl(now);
+ } while ((now - start) < target);
+
+ /* flip */
+ if (flag) {
+ rdtscl(now);
+ off();
+ target += space_width;
+ } else {
+ rdtscl(now); on();
+ target += pulse_width;
+ }
+ flag = !flag;
+ }
+ rdtscl(now);
+ return ((now - start) - length) / conv_us_to_clocks;
+}
+#else /* ! USE_RDTSC */
+/* Version using udelay() */
+
+/*
+ * here we use fixed point arithmetic, with 8
+ * fractional bits. that gets us within 0.1% or so of the right average
+ * frequency, albeit with some jitter in pulse length - Steve
+ */
+
+/* To match 8 fractional bits used for pulse/space length */
+
+static long send_pulse_homebrew_softcarrier(unsigned long length)
+{
+ int flag;
+ unsigned long actual, target, d;
+
+ length <<= 8;
+
+ actual = 0; target = 0; flag = 0;
+ while (actual < length) {
+ if (flag) {
+ off();
+ target += space_width;
+ } else {
+ on();
+ target += pulse_width;
+ }
+ d = (target - actual -
+ LIRC_SERIAL_TRANSMITTER_LATENCY + 128) >> 8;
+ /*
+ * Note - we've checked in ioctl that the pulse/space
+ * widths are big enough so that d is > 0
+ */
+ udelay(d);
+ actual += (d << 8) + LIRC_SERIAL_TRANSMITTER_LATENCY;
+ flag = !flag;
+ }
+ return (actual-length) >> 8;
+}
+#endif /* USE_RDTSC */
+
+static long send_pulse_homebrew(unsigned long length)
+{
+ if (length <= 0)
+ return 0;
+
+ if (softcarrier)
+ return send_pulse_homebrew_softcarrier(length);
+
+ on();
+ safe_udelay(length);
+ return 0;
+}
+
+static void send_space_irdeo(long length)
+{
+ if (length <= 0)
+ return;
+
+ safe_udelay(length);
+}
+
+static void send_space_homebrew(long length)
+{
+ off();
+ if (length <= 0)
+ return;
+ safe_udelay(length);
+}
+
+static void rbwrite(int l)
+{
+ if (lirc_buffer_full(&rbuf)) {
+ /* no new signals will be accepted */
+ dprintk("Buffer overrun\n");
+ return;
+ }
+ lirc_buffer_write(&rbuf, (void *)&l);
+}
+
+static void frbwrite(int l)
+{
+ /* simple noise filter */
+ static int pulse, space;
+ static unsigned int ptr;
+
+ if (ptr > 0 && (l & PULSE_BIT)) {
+ pulse += l & PULSE_MASK;
+ if (pulse > 250) {
+ rbwrite(space);
+ rbwrite(pulse | PULSE_BIT);
+ ptr = 0;
+ pulse = 0;
+ }
+ return;
+ }
+ if (!(l & PULSE_BIT)) {
+ if (ptr == 0) {
+ if (l > 20000) {
+ space = l;
+ ptr++;
+ return;
+ }
+ } else {
+ if (l > 20000) {
+ space += pulse;
+ if (space > PULSE_MASK)
+ space = PULSE_MASK;
+ space += l;
+ if (space > PULSE_MASK)
+ space = PULSE_MASK;
+ pulse = 0;
+ return;
+ }
+ rbwrite(space);
+ rbwrite(pulse | PULSE_BIT);
+ ptr = 0;
+ pulse = 0;
+ }
+ }
+ rbwrite(l);
+}
+
+static irqreturn_t lirc_irq_handler(int i, void *blah)
+{
+ struct timeval tv;
+ int counter, dcd;
+ u8 status;
+ long deltv;
+ int data;
+ static int last_dcd = -1;
+
+ if ((sinp(UART_IIR) & UART_IIR_NO_INT)) {
+ /* not our interrupt */
+ return IRQ_NONE;
+ }
+
+ counter = 0;
+ do {
+ counter++;
+ status = sinp(UART_MSR);
+ if (counter > RS_ISR_PASS_LIMIT) {
+ pr_warn("AIEEEE: We're caught!\n");
+ break;
+ }
+ if ((status & hardware[type].signal_pin_change)
+ && sense != -1) {
+ /* get current time */
+ do_gettimeofday(&tv);
+
+ /* New mode, written by Trent Piepho
+ <xyzzy@u.washington.edu>. */
+
+ /*
+ * The old format was not very portable.
+ * We now use an int to pass pulses
+ * and spaces to user space.
+ *
+ * If PULSE_BIT is set a pulse has been
+ * received, otherwise a space has been
+ * received. The driver needs to know if your
+ * receiver is active high or active low, or
+ * the space/pulse sense could be
+ * inverted. The bits denoted by PULSE_MASK are
+ * the length in microseconds. Lengths greater
+ * than or equal to 16 seconds are clamped to
+ * PULSE_MASK. All other bits are unused.
+ * This is a much simpler interface for user
+ * programs, as well as eliminating "out of
+ * phase" errors with space/pulse
+ * autodetection.
+ */
+
+ /* calc time since last interrupt in microseconds */
+ dcd = (status & hardware[type].signal_pin) ? 1 : 0;
+
+ if (dcd == last_dcd) {
+ pr_warn("ignoring spike: %d %d %lx %lx %lx %lx\n",
+ dcd, sense,
+ tv.tv_sec, lasttv.tv_sec,
+ (unsigned long)tv.tv_usec,
+ (unsigned long)lasttv.tv_usec);
+ continue;
+ }
+
+ deltv = tv.tv_sec-lasttv.tv_sec;
+ if (tv.tv_sec < lasttv.tv_sec ||
+ (tv.tv_sec == lasttv.tv_sec &&
+ tv.tv_usec < lasttv.tv_usec)) {
+ pr_warn("AIEEEE: your clock just jumped backwards\n");
+ pr_warn("%d %d %lx %lx %lx %lx\n",
+ dcd, sense,
+ tv.tv_sec, lasttv.tv_sec,
+ (unsigned long)tv.tv_usec,
+ (unsigned long)lasttv.tv_usec);
+ data = PULSE_MASK;
+ } else if (deltv > 15) {
+ data = PULSE_MASK; /* really long time */
+ if (!(dcd^sense)) {
+ /* sanity check */
+ pr_warn("AIEEEE: %d %d %lx %lx %lx %lx\n",
+ dcd, sense,
+ tv.tv_sec, lasttv.tv_sec,
+ (unsigned long)tv.tv_usec,
+ (unsigned long)lasttv.tv_usec);
+ /*
+ * detecting pulse while this
+ * MUST be a space!
+ */
+ sense = sense ? 0 : 1;
+ }
+ } else
+ data = (int) (deltv*1000000 +
+ tv.tv_usec -
+ lasttv.tv_usec);
+ frbwrite(dcd^sense ? data : (data|PULSE_BIT));
+ lasttv = tv;
+ last_dcd = dcd;
+ wake_up_interruptible(&rbuf.wait_poll);
+ }
+ } while (!(sinp(UART_IIR) & UART_IIR_NO_INT)); /* still pending ? */
+ return IRQ_HANDLED;
+}
+
+
+static int hardware_init_port(void)
+{
+ u8 scratch, scratch2, scratch3;
+
+ /*
+ * This is a simple port existence test, borrowed from the autoconfig
+ * function in drivers/serial/8250.c
+ */
+ scratch = sinp(UART_IER);
+ soutp(UART_IER, 0);
+#ifdef __i386__
+ outb(0xff, 0x080);
+#endif
+ scratch2 = sinp(UART_IER) & 0x0f;
+ soutp(UART_IER, 0x0f);
+#ifdef __i386__
+ outb(0x00, 0x080);
+#endif
+ scratch3 = sinp(UART_IER) & 0x0f;
+ soutp(UART_IER, scratch);
+ if (scratch2 != 0 || scratch3 != 0x0f) {
+ /* we fail, there's nothing here */
+ pr_err("port existence test failed, cannot continue\n");
+ return -ENODEV;
+ }
+
+
+
+ /* Set DLAB 0. */
+ soutp(UART_LCR, sinp(UART_LCR) & (~UART_LCR_DLAB));
+
+ /* First of all, disable all interrupts */
+ soutp(UART_IER, sinp(UART_IER) &
+ (~(UART_IER_MSI|UART_IER_RLSI|UART_IER_THRI|UART_IER_RDI)));
+
+ /* Clear registers. */
+ sinp(UART_LSR);
+ sinp(UART_RX);
+ sinp(UART_IIR);
+ sinp(UART_MSR);
+
+ /* Set line for power source */
+ off();
+
+ /* Clear registers again to be sure. */
+ sinp(UART_LSR);
+ sinp(UART_RX);
+ sinp(UART_IIR);
+ sinp(UART_MSR);
+
+ switch (type) {
+ case LIRC_IRDEO:
+ case LIRC_IRDEO_REMOTE:
+ /* setup port to 7N1 @ 115200 Baud */
+ /* 7N1+start = 9 bits at 115200 ~ 3 bits at 38kHz */
+
+ /* Set DLAB 1. */
+ soutp(UART_LCR, sinp(UART_LCR) | UART_LCR_DLAB);
+ /* Set divisor to 1 => 115200 Baud */
+ soutp(UART_DLM, 0);
+ soutp(UART_DLL, 1);
+ /* Set DLAB 0 + 7N1 */
+ soutp(UART_LCR, UART_LCR_WLEN7);
+ /* THR interrupt already disabled at this point */
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int lirc_serial_probe(struct platform_device *dev)
+{
+ int i, nlow, nhigh, result;
+
+ result = devm_request_irq(&dev->dev, irq, lirc_irq_handler,
+ (share_irq ? IRQF_SHARED : 0),
+ LIRC_DRIVER_NAME, &hardware);
+ if (result < 0) {
+ if (result == -EBUSY)
+ dev_err(&dev->dev, "IRQ %d busy\n", irq);
+ else if (result == -EINVAL)
+ dev_err(&dev->dev, "Bad irq number or handler\n");
+ return result;
+ }
+
+ /* Reserve io region. */
+ /*
+ * Future MMAP-Developers: Attention!
+ * For memory mapped I/O you *might* need to use ioremap() first,
+ * for the NSLU2 it's done in boot code.
+ */
+ if (((iommap)
+ && (devm_request_mem_region(&dev->dev, iommap, 8 << ioshift,
+ LIRC_DRIVER_NAME) == NULL))
+ || ((!iommap)
+ && (devm_request_region(&dev->dev, io, 8,
+ LIRC_DRIVER_NAME) == NULL))) {
+ dev_err(&dev->dev, "port %04x already in use\n", io);
+ dev_warn(&dev->dev, "use 'setserial /dev/ttySX uart none'\n");
+ dev_warn(&dev->dev,
+ "or compile the serial port driver as module and\n");
+ dev_warn(&dev->dev, "make sure this module is loaded first\n");
+ return -EBUSY;
+ }
+
+ result = hardware_init_port();
+ if (result < 0)
+ return result;
+
+ /* Initialize pulse/space widths */
+ init_timing_params(duty_cycle, freq);
+
+ /* If pin is high, then this must be an active low receiver. */
+ if (sense == -1) {
+ /* wait 1/2 sec for the power supply */
+ msleep(500);
+
+ /*
+ * probe 9 times every 0.04s, collect "votes" for
+ * active high/low
+ */
+ nlow = 0;
+ nhigh = 0;
+ for (i = 0; i < 9; i++) {
+ if (sinp(UART_MSR) & hardware[type].signal_pin)
+ nlow++;
+ else
+ nhigh++;
+ msleep(40);
+ }
+ sense = nlow >= nhigh ? 1 : 0;
+ dev_info(&dev->dev, "auto-detected active %s receiver\n",
+ sense ? "low" : "high");
+ } else
+ dev_info(&dev->dev, "Manually using active %s receiver\n",
+ sense ? "low" : "high");
+
+ dprintk("Interrupt %d, port %04x obtained\n", irq, io);
+ return 0;
+}
+
+static int set_use_inc(void *data)
+{
+ unsigned long flags;
+
+ /* initialize timestamp */
+ do_gettimeofday(&lasttv);
+
+ spin_lock_irqsave(&hardware[type].lock, flags);
+
+ /* Set DLAB 0. */
+ soutp(UART_LCR, sinp(UART_LCR) & (~UART_LCR_DLAB));
+
+ soutp(UART_IER, sinp(UART_IER)|UART_IER_MSI);
+
+ spin_unlock_irqrestore(&hardware[type].lock, flags);
+
+ return 0;
+}
+
+static void set_use_dec(void *data)
+{ unsigned long flags;
+
+ spin_lock_irqsave(&hardware[type].lock, flags);
+
+ /* Set DLAB 0. */
+ soutp(UART_LCR, sinp(UART_LCR) & (~UART_LCR_DLAB));
+
+ /* First of all, disable all interrupts */
+ soutp(UART_IER, sinp(UART_IER) &
+ (~(UART_IER_MSI|UART_IER_RLSI|UART_IER_THRI|UART_IER_RDI)));
+ spin_unlock_irqrestore(&hardware[type].lock, flags);
+}
+
+static ssize_t lirc_write(struct file *file, const char __user *buf,
+ size_t n, loff_t *ppos)
+{
+ int i, count;
+ unsigned long flags;
+ long delta = 0;
+ int *wbuf;
+
+ if (!(hardware[type].features & LIRC_CAN_SEND_PULSE))
+ return -EPERM;
+
+ count = n / sizeof(int);
+ if (n % sizeof(int) || count % 2 == 0)
+ return -EINVAL;
+ wbuf = memdup_user(buf, n);
+ if (IS_ERR(wbuf))
+ return PTR_ERR(wbuf);
+ spin_lock_irqsave(&hardware[type].lock, flags);
+ if (type == LIRC_IRDEO) {
+ /* DTR, RTS down */
+ on();
+ }
+ for (i = 0; i < count; i++) {
+ if (i%2)
+ hardware[type].send_space(wbuf[i] - delta);
+ else
+ delta = hardware[type].send_pulse(wbuf[i]);
+ }
+ off();
+ spin_unlock_irqrestore(&hardware[type].lock, flags);
+ kfree(wbuf);
+ return n;
+}
+
+static long lirc_ioctl(struct file *filep, unsigned int cmd, unsigned long arg)
+{
+ int result;
+ u32 __user *uptr = (u32 __user *)arg;
+ u32 value;
+
+ switch (cmd) {
+ case LIRC_GET_SEND_MODE:
+ if (!(hardware[type].features&LIRC_CAN_SEND_MASK))
+ return -ENOIOCTLCMD;
+
+ result = put_user(LIRC_SEND2MODE
+ (hardware[type].features&LIRC_CAN_SEND_MASK),
+ uptr);
+ if (result)
+ return result;
+ break;
+
+ case LIRC_SET_SEND_MODE:
+ if (!(hardware[type].features&LIRC_CAN_SEND_MASK))
+ return -ENOIOCTLCMD;
+
+ result = get_user(value, uptr);
+ if (result)
+ return result;
+ /* only LIRC_MODE_PULSE supported */
+ if (value != LIRC_MODE_PULSE)
+ return -EINVAL;
+ break;
+
+ case LIRC_GET_LENGTH:
+ return -ENOIOCTLCMD;
+
+ case LIRC_SET_SEND_DUTY_CYCLE:
+ dprintk("SET_SEND_DUTY_CYCLE\n");
+ if (!(hardware[type].features&LIRC_CAN_SET_SEND_DUTY_CYCLE))
+ return -ENOIOCTLCMD;
+
+ result = get_user(value, uptr);
+ if (result)
+ return result;
+ if (value <= 0 || value > 100)
+ return -EINVAL;
+ return init_timing_params(value, freq);
+
+ case LIRC_SET_SEND_CARRIER:
+ dprintk("SET_SEND_CARRIER\n");
+ if (!(hardware[type].features&LIRC_CAN_SET_SEND_CARRIER))
+ return -ENOIOCTLCMD;
+
+ result = get_user(value, uptr);
+ if (result)
+ return result;
+ if (value > 500000 || value < 20000)
+ return -EINVAL;
+ return init_timing_params(duty_cycle, value);
+
+ default:
+ return lirc_dev_fop_ioctl(filep, cmd, arg);
+ }
+ return 0;
+}
+
+static const struct file_operations lirc_fops = {
+ .owner = THIS_MODULE,
+ .write = lirc_write,
+ .unlocked_ioctl = lirc_ioctl,
+#ifdef CONFIG_COMPAT
+ .compat_ioctl = lirc_ioctl,
+#endif
+ .read = lirc_dev_fop_read,
+ .poll = lirc_dev_fop_poll,
+ .open = lirc_dev_fop_open,
+ .release = lirc_dev_fop_close,
+ .llseek = no_llseek,
+};
+
+static struct lirc_driver driver = {
+ .name = LIRC_DRIVER_NAME,
+ .minor = -1,
+ .code_length = 1,
+ .sample_rate = 0,
+ .data = NULL,
+ .add_to_buf = NULL,
+ .rbuf = &rbuf,
+ .set_use_inc = set_use_inc,
+ .set_use_dec = set_use_dec,
+ .fops = &lirc_fops,
+ .dev = NULL,
+ .owner = THIS_MODULE,
+};
+
+static struct platform_device *lirc_serial_dev;
+
+static int lirc_serial_suspend(struct platform_device *dev,
+ pm_message_t state)
+{
+ /* Set DLAB 0. */
+ soutp(UART_LCR, sinp(UART_LCR) & (~UART_LCR_DLAB));
+
+ /* Disable all interrupts */
+ soutp(UART_IER, sinp(UART_IER) &
+ (~(UART_IER_MSI|UART_IER_RLSI|UART_IER_THRI|UART_IER_RDI)));
+
+ /* Clear registers. */
+ sinp(UART_LSR);
+ sinp(UART_RX);
+ sinp(UART_IIR);
+ sinp(UART_MSR);
+
+ return 0;
+}
+
+/* twisty maze... need a forward-declaration here... */
+static void lirc_serial_exit(void);
+
+static int lirc_serial_resume(struct platform_device *dev)
+{
+ unsigned long flags;
+ int result;
+
+ result = hardware_init_port();
+ if (result < 0)
+ return result;
+
+ spin_lock_irqsave(&hardware[type].lock, flags);
+ /* Enable Interrupt */
+ do_gettimeofday(&lasttv);
+ soutp(UART_IER, sinp(UART_IER)|UART_IER_MSI);
+ off();
+
+ lirc_buffer_clear(&rbuf);
+
+ spin_unlock_irqrestore(&hardware[type].lock, flags);
+
+ return 0;
+}
+
+static struct platform_driver lirc_serial_driver = {
+ .probe = lirc_serial_probe,
+ .suspend = lirc_serial_suspend,
+ .resume = lirc_serial_resume,
+ .driver = {
+ .name = "lirc_serial",
+ },
+};
+
+static int __init lirc_serial_init(void)
+{
+ int result;
+
+ /* Init read buffer. */
+ result = lirc_buffer_init(&rbuf, sizeof(int), RBUF_LEN);
+ if (result < 0)
+ return result;
+
+ result = platform_driver_register(&lirc_serial_driver);
+ if (result) {
+ printk("lirc register returned %d\n", result);
+ goto exit_buffer_free;
+ }
+
+ lirc_serial_dev = platform_device_alloc("lirc_serial", 0);
+ if (!lirc_serial_dev) {
+ result = -ENOMEM;
+ goto exit_driver_unregister;
+ }
+
+ result = platform_device_add(lirc_serial_dev);
+ if (result)
+ goto exit_device_put;
+
+ return 0;
+
+exit_device_put:
+ platform_device_put(lirc_serial_dev);
+exit_driver_unregister:
+ platform_driver_unregister(&lirc_serial_driver);
+exit_buffer_free:
+ lirc_buffer_free(&rbuf);
+ return result;
+}
+
+static void lirc_serial_exit(void)
+{
+ platform_device_unregister(lirc_serial_dev);
+ platform_driver_unregister(&lirc_serial_driver);
+ lirc_buffer_free(&rbuf);
+}
+
+static int __init lirc_serial_init_module(void)
+{
+ int result;
+
+ switch (type) {
+ case LIRC_HOMEBREW:
+ case LIRC_IRDEO:
+ case LIRC_IRDEO_REMOTE:
+ case LIRC_ANIMAX:
+ case LIRC_IGOR:
+ /* if nothing specified, use ttyS0/com1 and irq 4 */
+ io = io ? io : 0x3f8;
+ irq = irq ? irq : 4;
+ break;
+ default:
+ return -EINVAL;
+ }
+ if (!softcarrier) {
+ switch (type) {
+ case LIRC_HOMEBREW:
+ case LIRC_IGOR:
+ hardware[type].features &=
+ ~(LIRC_CAN_SET_SEND_DUTY_CYCLE|
+ LIRC_CAN_SET_SEND_CARRIER);
+ break;
+ }
+ }
+
+ /* make sure sense is either -1, 0, or 1 */
+ if (sense != -1)
+ sense = !!sense;
+
+ result = lirc_serial_init();
+ if (result)
+ return result;
+
+ driver.features = hardware[type].features;
+ driver.dev = &lirc_serial_dev->dev;
+ driver.minor = lirc_register_driver(&driver);
+ if (driver.minor < 0) {
+ pr_err("register_chrdev failed!\n");
+ lirc_serial_exit();
+ return driver.minor;
+ }
+ return 0;
+}
+
+static void __exit lirc_serial_exit_module(void)
+{
+ lirc_unregister_driver(driver.minor);
+ lirc_serial_exit();
+ dprintk("cleaned up module\n");
+}
+
+
+module_init(lirc_serial_init_module);
+module_exit(lirc_serial_exit_module);
+
+MODULE_DESCRIPTION("Infra-red receiver driver for serial ports.");
+MODULE_AUTHOR("Ralph Metzler, Trent Piepho, Ben Pfaff, "
+ "Christoph Bartelmus, Andrei Tanas");
+MODULE_LICENSE("GPL");
+
+module_param(type, int, S_IRUGO);
+MODULE_PARM_DESC(type, "Hardware type (0 = home-brew, 1 = IRdeo,"
+ " 2 = IRdeo Remote, 3 = AnimaX, 4 = IgorPlug,"
+ " 5 = NSLU2 RX:CTS2/TX:GreenLED)");
+
+module_param(io, int, S_IRUGO);
+MODULE_PARM_DESC(io, "I/O address base (0x3f8 or 0x2f8)");
+
+/* some architectures (e.g. intel xscale) have memory mapped registers */
+module_param(iommap, bool, S_IRUGO);
+MODULE_PARM_DESC(iommap, "physical base for memory mapped I/O"
+ " (0 = no memory mapped io)");
+
+/*
+ * some architectures (e.g. intel xscale) align the 8bit serial registers
+ * on 32bit word boundaries.
+ * See linux-kernel/drivers/tty/serial/8250/8250.c serial_in()/out()
+ */
+module_param(ioshift, int, S_IRUGO);
+MODULE_PARM_DESC(ioshift, "shift I/O register offset (0 = no shift)");
+
+module_param(irq, int, S_IRUGO);
+MODULE_PARM_DESC(irq, "Interrupt (4 or 3)");
+
+module_param(share_irq, bool, S_IRUGO);
+MODULE_PARM_DESC(share_irq, "Share interrupts (0 = off, 1 = on)");
+
+module_param(sense, int, S_IRUGO);
+MODULE_PARM_DESC(sense, "Override autodetection of IR receiver circuit"
+ " (0 = active high, 1 = active low )");
+
+#ifdef CONFIG_LIRC_SERIAL_TRANSMITTER
+module_param(txsense, bool, S_IRUGO);
+MODULE_PARM_DESC(txsense, "Sense of transmitter circuit"
+ " (0 = active high, 1 = active low )");
+#endif
+
+module_param(softcarrier, bool, S_IRUGO);
+MODULE_PARM_DESC(softcarrier, "Software carrier (0 = off, 1 = on, default on)");
+
+module_param(debug, bool, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(debug, "Enable debugging messages");
diff --git a/drivers/staging/media/lirc/lirc_sir.c b/drivers/staging/media/lirc/lirc_sir.c
new file mode 100644
index 000000000..29087f66e
--- /dev/null
+++ b/drivers/staging/media/lirc/lirc_sir.c
@@ -0,0 +1,1014 @@
+/*
+ * LIRC SIR driver, (C) 2000 Milan Pikula <www@fornax.sk>
+ *
+ * lirc_sir - Device driver for use with SIR (serial infra red)
+ * mode of IrDA on many notebooks.
+ *
+ * This program is free software; you can redistribute it and/or 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
+ *
+ *
+ * 2000/09/16 Frank Przybylski <mail@frankprzybylski.de> :
+ * added timeout and relaxed pulse detection, removed gap bug
+ *
+ * 2000/12/15 Christoph Bartelmus <lirc@bartelmus.de> :
+ * added support for Tekram Irmate 210 (sending does not work yet,
+ * kind of disappointing that nobody was able to implement that
+ * before),
+ * major clean-up
+ *
+ * 2001/02/27 Christoph Bartelmus <lirc@bartelmus.de> :
+ * added support for StrongARM SA1100 embedded microprocessor
+ * parts cut'n'pasted from sa1100_ir.c (C) 2000 Russell King
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/signal.h>
+#include <linux/fs.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/kernel.h>
+#include <linux/serial_reg.h>
+#include <linux/time.h>
+#include <linux/string.h>
+#include <linux/types.h>
+#include <linux/wait.h>
+#include <linux/mm.h>
+#include <linux/delay.h>
+#include <linux/poll.h>
+#include <linux/io.h>
+#include <asm/irq.h>
+#include <linux/fcntl.h>
+#include <linux/platform_device.h>
+
+#include <linux/timer.h>
+
+#include <media/lirc.h>
+#include <media/lirc_dev.h>
+
+/* SECTION: Definitions */
+
+/*** Tekram dongle ***/
+#ifdef LIRC_SIR_TEKRAM
+/* stolen from kernel source */
+/* definitions for Tekram dongle */
+#define TEKRAM_115200 0x00
+#define TEKRAM_57600 0x01
+#define TEKRAM_38400 0x02
+#define TEKRAM_19200 0x03
+#define TEKRAM_9600 0x04
+#define TEKRAM_2400 0x08
+
+#define TEKRAM_PW 0x10 /* Pulse select bit */
+
+/* 10bit * 1s/115200bit in milliseconds = 87ms*/
+#define TIME_CONST (10000000ul/115200ul)
+
+#endif
+
+#ifdef LIRC_SIR_ACTISYS_ACT200L
+static void init_act200(void);
+#elif defined(LIRC_SIR_ACTISYS_ACT220L)
+static void init_act220(void);
+#endif
+
+#define RBUF_LEN 1024
+#define WBUF_LEN 1024
+
+#define LIRC_DRIVER_NAME "lirc_sir"
+
+#define PULSE '['
+
+#ifndef LIRC_SIR_TEKRAM
+/* 9bit * 1s/115200bit in milli seconds = 78.125ms*/
+#define TIME_CONST (9000000ul/115200ul)
+#endif
+
+
+/* timeout for sequences in jiffies (=5/100s), must be longer than TIME_CONST */
+#define SIR_TIMEOUT (HZ*5/100)
+
+#ifndef LIRC_ON_SA1100
+#ifndef LIRC_IRQ
+#define LIRC_IRQ 4
+#endif
+#ifndef LIRC_PORT
+/* for external dongles, default to com1 */
+#if defined(LIRC_SIR_ACTISYS_ACT200L) || \
+ defined(LIRC_SIR_ACTISYS_ACT220L) || \
+ defined(LIRC_SIR_TEKRAM)
+#define LIRC_PORT 0x3f8
+#else
+/* onboard sir ports are typically com3 */
+#define LIRC_PORT 0x3e8
+#endif
+#endif
+
+static int io = LIRC_PORT;
+static int irq = LIRC_IRQ;
+static int threshold = 3;
+#endif
+
+static DEFINE_SPINLOCK(timer_lock);
+static struct timer_list timerlist;
+/* time of last signal change detected */
+static struct timeval last_tv = {0, 0};
+/* time of last UART data ready interrupt */
+static struct timeval last_intr_tv = {0, 0};
+static int last_value;
+
+static DECLARE_WAIT_QUEUE_HEAD(lirc_read_queue);
+
+static DEFINE_SPINLOCK(hardware_lock);
+
+static int rx_buf[RBUF_LEN];
+static unsigned int rx_tail, rx_head;
+
+static bool debug;
+
+/* SECTION: Prototypes */
+
+/* Communication with user-space */
+static unsigned int lirc_poll(struct file *file, poll_table *wait);
+static ssize_t lirc_read(struct file *file, char __user *buf, size_t count,
+ loff_t *ppos);
+static ssize_t lirc_write(struct file *file, const char __user *buf, size_t n,
+ loff_t *pos);
+static long lirc_ioctl(struct file *filep, unsigned int cmd, unsigned long arg);
+static void add_read_queue(int flag, unsigned long val);
+static int init_chrdev(void);
+static void drop_chrdev(void);
+/* Hardware */
+static irqreturn_t sir_interrupt(int irq, void *dev_id);
+static void send_space(unsigned long len);
+static void send_pulse(unsigned long len);
+static int init_hardware(void);
+static void drop_hardware(void);
+/* Initialisation */
+static int init_port(void);
+static void drop_port(void);
+
+static inline unsigned int sinp(int offset)
+{
+ return inb(io + offset);
+}
+
+static inline void soutp(int offset, int value)
+{
+ outb(value, io + offset);
+}
+
+#ifndef MAX_UDELAY_MS
+#define MAX_UDELAY_US 5000
+#else
+#define MAX_UDELAY_US (MAX_UDELAY_MS*1000)
+#endif
+
+static void safe_udelay(unsigned long usecs)
+{
+ while (usecs > MAX_UDELAY_US) {
+ udelay(MAX_UDELAY_US);
+ usecs -= MAX_UDELAY_US;
+ }
+ udelay(usecs);
+}
+
+/* SECTION: Communication with user-space */
+
+static unsigned int lirc_poll(struct file *file, poll_table *wait)
+{
+ poll_wait(file, &lirc_read_queue, wait);
+ if (rx_head != rx_tail)
+ return POLLIN | POLLRDNORM;
+ return 0;
+}
+
+static ssize_t lirc_read(struct file *file, char __user *buf, size_t count,
+ loff_t *ppos)
+{
+ int n = 0;
+ int retval = 0;
+ DECLARE_WAITQUEUE(wait, current);
+
+ if (count % sizeof(int))
+ return -EINVAL;
+
+ add_wait_queue(&lirc_read_queue, &wait);
+ set_current_state(TASK_INTERRUPTIBLE);
+ while (n < count) {
+ if (rx_head != rx_tail) {
+ if (copy_to_user(buf + n,
+ rx_buf + rx_head,
+ sizeof(int))) {
+ retval = -EFAULT;
+ break;
+ }
+ rx_head = (rx_head + 1) & (RBUF_LEN - 1);
+ n += sizeof(int);
+ } else {
+ if (file->f_flags & O_NONBLOCK) {
+ retval = -EAGAIN;
+ break;
+ }
+ if (signal_pending(current)) {
+ retval = -ERESTARTSYS;
+ break;
+ }
+ schedule();
+ set_current_state(TASK_INTERRUPTIBLE);
+ }
+ }
+ remove_wait_queue(&lirc_read_queue, &wait);
+ set_current_state(TASK_RUNNING);
+ return n ? n : retval;
+}
+static ssize_t lirc_write(struct file *file, const char __user *buf, size_t n,
+ loff_t *pos)
+{
+ unsigned long flags;
+ int i, count;
+ int *tx_buf;
+
+ count = n / sizeof(int);
+ if (n % sizeof(int) || count % 2 == 0)
+ return -EINVAL;
+ tx_buf = memdup_user(buf, n);
+ if (IS_ERR(tx_buf))
+ return PTR_ERR(tx_buf);
+ i = 0;
+ local_irq_save(flags);
+ while (1) {
+ if (i >= count)
+ break;
+ if (tx_buf[i])
+ send_pulse(tx_buf[i]);
+ i++;
+ if (i >= count)
+ break;
+ if (tx_buf[i])
+ send_space(tx_buf[i]);
+ i++;
+ }
+ local_irq_restore(flags);
+ kfree(tx_buf);
+ return count;
+}
+
+static long lirc_ioctl(struct file *filep, unsigned int cmd, unsigned long arg)
+{
+ u32 __user *uptr = (u32 __user *)arg;
+ int retval = 0;
+ u32 value = 0;
+
+ if (cmd == LIRC_GET_FEATURES)
+ value = LIRC_CAN_SEND_PULSE | LIRC_CAN_REC_MODE2;
+ else if (cmd == LIRC_GET_SEND_MODE)
+ value = LIRC_MODE_PULSE;
+ else if (cmd == LIRC_GET_REC_MODE)
+ value = LIRC_MODE_MODE2;
+
+ switch (cmd) {
+ case LIRC_GET_FEATURES:
+ case LIRC_GET_SEND_MODE:
+ case LIRC_GET_REC_MODE:
+ retval = put_user(value, uptr);
+ break;
+
+ case LIRC_SET_SEND_MODE:
+ case LIRC_SET_REC_MODE:
+ retval = get_user(value, uptr);
+ break;
+ default:
+ retval = -ENOIOCTLCMD;
+
+ }
+
+ if (retval)
+ return retval;
+ if (cmd == LIRC_SET_REC_MODE) {
+ if (value != LIRC_MODE_MODE2)
+ retval = -ENOSYS;
+ } else if (cmd == LIRC_SET_SEND_MODE) {
+ if (value != LIRC_MODE_PULSE)
+ retval = -ENOSYS;
+ }
+
+ return retval;
+}
+
+static void add_read_queue(int flag, unsigned long val)
+{
+ unsigned int new_rx_tail;
+ int newval;
+
+ pr_debug("add flag %d with val %lu\n", flag, val);
+
+ newval = val & PULSE_MASK;
+
+ /*
+ * statistically, pulses are ~TIME_CONST/2 too long. we could
+ * maybe make this more exact, but this is good enough
+ */
+ if (flag) {
+ /* pulse */
+ if (newval > TIME_CONST/2)
+ newval -= TIME_CONST/2;
+ else /* should not ever happen */
+ newval = 1;
+ newval |= PULSE_BIT;
+ } else {
+ newval += TIME_CONST/2;
+ }
+ new_rx_tail = (rx_tail + 1) & (RBUF_LEN - 1);
+ if (new_rx_tail == rx_head) {
+ pr_debug("Buffer overrun.\n");
+ return;
+ }
+ rx_buf[rx_tail] = newval;
+ rx_tail = new_rx_tail;
+ wake_up_interruptible(&lirc_read_queue);
+}
+
+static const struct file_operations lirc_fops = {
+ .owner = THIS_MODULE,
+ .read = lirc_read,
+ .write = lirc_write,
+ .poll = lirc_poll,
+ .unlocked_ioctl = lirc_ioctl,
+#ifdef CONFIG_COMPAT
+ .compat_ioctl = lirc_ioctl,
+#endif
+ .open = lirc_dev_fop_open,
+ .release = lirc_dev_fop_close,
+ .llseek = no_llseek,
+};
+
+static int set_use_inc(void *data)
+{
+ return 0;
+}
+
+static void set_use_dec(void *data)
+{
+}
+
+static struct lirc_driver driver = {
+ .name = LIRC_DRIVER_NAME,
+ .minor = -1,
+ .code_length = 1,
+ .sample_rate = 0,
+ .data = NULL,
+ .add_to_buf = NULL,
+ .set_use_inc = set_use_inc,
+ .set_use_dec = set_use_dec,
+ .fops = &lirc_fops,
+ .dev = NULL,
+ .owner = THIS_MODULE,
+};
+
+static struct platform_device *lirc_sir_dev;
+
+static int init_chrdev(void)
+{
+ driver.dev = &lirc_sir_dev->dev;
+ driver.minor = lirc_register_driver(&driver);
+ if (driver.minor < 0) {
+ pr_err("init_chrdev() failed.\n");
+ return -EIO;
+ }
+ return 0;
+}
+
+static void drop_chrdev(void)
+{
+ lirc_unregister_driver(driver.minor);
+}
+
+/* SECTION: Hardware */
+static long delta(struct timeval *tv1, struct timeval *tv2)
+{
+ unsigned long deltv;
+
+ deltv = tv2->tv_sec - tv1->tv_sec;
+ if (deltv > 15)
+ deltv = 0xFFFFFF;
+ else
+ deltv = deltv*1000000 +
+ tv2->tv_usec -
+ tv1->tv_usec;
+ return deltv;
+}
+
+static void sir_timeout(unsigned long data)
+{
+ /*
+ * if last received signal was a pulse, but receiving stopped
+ * within the 9 bit frame, we need to finish this pulse and
+ * simulate a signal change to from pulse to space. Otherwise
+ * upper layers will receive two sequences next time.
+ */
+
+ unsigned long flags;
+ unsigned long pulse_end;
+
+ /* avoid interference with interrupt */
+ spin_lock_irqsave(&timer_lock, flags);
+ if (last_value) {
+ /* clear unread bits in UART and restart */
+ outb(UART_FCR_CLEAR_RCVR, io + UART_FCR);
+ /* determine 'virtual' pulse end: */
+ pulse_end = delta(&last_tv, &last_intr_tv);
+ dev_dbg(driver.dev, "timeout add %d for %lu usec\n",
+ last_value, pulse_end);
+ add_read_queue(last_value, pulse_end);
+ last_value = 0;
+ last_tv = last_intr_tv;
+ }
+ spin_unlock_irqrestore(&timer_lock, flags);
+}
+
+static irqreturn_t sir_interrupt(int irq, void *dev_id)
+{
+ unsigned char data;
+ struct timeval curr_tv;
+ static unsigned long deltv;
+ unsigned long deltintrtv;
+ unsigned long flags;
+ int iir, lsr;
+
+ while ((iir = inb(io + UART_IIR) & UART_IIR_ID)) {
+ switch (iir&UART_IIR_ID) { /* FIXME toto treba preriedit */
+ case UART_IIR_MSI:
+ (void) inb(io + UART_MSR);
+ break;
+ case UART_IIR_RLSI:
+ (void) inb(io + UART_LSR);
+ break;
+ case UART_IIR_THRI:
+#if 0
+ if (lsr & UART_LSR_THRE) /* FIFO is empty */
+ outb(data, io + UART_TX)
+#endif
+ break;
+ case UART_IIR_RDI:
+ /* avoid interference with timer */
+ spin_lock_irqsave(&timer_lock, flags);
+ do {
+ del_timer(&timerlist);
+ data = inb(io + UART_RX);
+ do_gettimeofday(&curr_tv);
+ deltv = delta(&last_tv, &curr_tv);
+ deltintrtv = delta(&last_intr_tv, &curr_tv);
+ dev_dbg(driver.dev, "t %lu, d %d\n",
+ deltintrtv, (int)data);
+ /*
+ * if nothing came in last X cycles,
+ * it was gap
+ */
+ if (deltintrtv > TIME_CONST * threshold) {
+ if (last_value) {
+ dev_dbg(driver.dev, "GAP\n");
+ /* simulate signal change */
+ add_read_queue(last_value,
+ deltv -
+ deltintrtv);
+ last_value = 0;
+ last_tv.tv_sec =
+ last_intr_tv.tv_sec;
+ last_tv.tv_usec =
+ last_intr_tv.tv_usec;
+ deltv = deltintrtv;
+ }
+ }
+ data = 1;
+ if (data ^ last_value) {
+ /*
+ * deltintrtv > 2*TIME_CONST, remember?
+ * the other case is timeout
+ */
+ add_read_queue(last_value,
+ deltv-TIME_CONST);
+ last_value = data;
+ last_tv = curr_tv;
+ if (last_tv.tv_usec >= TIME_CONST) {
+ last_tv.tv_usec -= TIME_CONST;
+ } else {
+ last_tv.tv_sec--;
+ last_tv.tv_usec += 1000000 -
+ TIME_CONST;
+ }
+ }
+ last_intr_tv = curr_tv;
+ if (data) {
+ /*
+ * start timer for end of
+ * sequence detection
+ */
+ timerlist.expires = jiffies +
+ SIR_TIMEOUT;
+ add_timer(&timerlist);
+ }
+
+ lsr = inb(io + UART_LSR);
+ } while (lsr & UART_LSR_DR); /* data ready */
+ spin_unlock_irqrestore(&timer_lock, flags);
+ break;
+ default:
+ break;
+ }
+ }
+ return IRQ_RETVAL(IRQ_HANDLED);
+}
+
+static void send_space(unsigned long len)
+{
+ safe_udelay(len);
+}
+
+static void send_pulse(unsigned long len)
+{
+ long bytes_out = len / TIME_CONST;
+
+ if (bytes_out == 0)
+ bytes_out++;
+
+ while (bytes_out--) {
+ outb(PULSE, io + UART_TX);
+ /* FIXME treba seriozne cakanie z char/serial.c */
+ while (!(inb(io + UART_LSR) & UART_LSR_THRE))
+ ;
+ }
+}
+
+static int init_hardware(void)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&hardware_lock, flags);
+ /* reset UART */
+#if defined(LIRC_SIR_TEKRAM)
+ /* disable FIFO */
+ soutp(UART_FCR,
+ UART_FCR_CLEAR_RCVR|
+ UART_FCR_CLEAR_XMIT|
+ UART_FCR_TRIGGER_1);
+
+ /* Set DLAB 0. */
+ soutp(UART_LCR, sinp(UART_LCR) & (~UART_LCR_DLAB));
+
+ /* First of all, disable all interrupts */
+ soutp(UART_IER, sinp(UART_IER) &
+ (~(UART_IER_MSI|UART_IER_RLSI|UART_IER_THRI|UART_IER_RDI)));
+
+ /* Set DLAB 1. */
+ soutp(UART_LCR, sinp(UART_LCR) | UART_LCR_DLAB);
+
+ /* Set divisor to 12 => 9600 Baud */
+ soutp(UART_DLM, 0);
+ soutp(UART_DLL, 12);
+
+ /* Set DLAB 0. */
+ soutp(UART_LCR, sinp(UART_LCR) & (~UART_LCR_DLAB));
+
+ /* power supply */
+ soutp(UART_MCR, UART_MCR_RTS|UART_MCR_DTR|UART_MCR_OUT2);
+ safe_udelay(50*1000);
+
+ /* -DTR low -> reset PIC */
+ soutp(UART_MCR, UART_MCR_RTS|UART_MCR_OUT2);
+ udelay(1*1000);
+
+ soutp(UART_MCR, UART_MCR_RTS|UART_MCR_DTR|UART_MCR_OUT2);
+ udelay(100);
+
+
+ /* -RTS low -> send control byte */
+ soutp(UART_MCR, UART_MCR_DTR|UART_MCR_OUT2);
+ udelay(7);
+ soutp(UART_TX, TEKRAM_115200|TEKRAM_PW);
+
+ /* one byte takes ~1042 usec to transmit at 9600,8N1 */
+ udelay(1500);
+
+ /* back to normal operation */
+ soutp(UART_MCR, UART_MCR_RTS|UART_MCR_DTR|UART_MCR_OUT2);
+ udelay(50);
+
+ udelay(1500);
+
+ /* read previous control byte */
+ pr_info("0x%02x\n", sinp(UART_RX));
+
+ /* Set DLAB 1. */
+ soutp(UART_LCR, sinp(UART_LCR) | UART_LCR_DLAB);
+
+ /* Set divisor to 1 => 115200 Baud */
+ soutp(UART_DLM, 0);
+ soutp(UART_DLL, 1);
+
+ /* Set DLAB 0, 8 Bit */
+ soutp(UART_LCR, UART_LCR_WLEN8);
+ /* enable interrupts */
+ soutp(UART_IER, sinp(UART_IER)|UART_IER_RDI);
+#else
+ outb(0, io + UART_MCR);
+ outb(0, io + UART_IER);
+ /* init UART */
+ /* set DLAB, speed = 115200 */
+ outb(UART_LCR_DLAB | UART_LCR_WLEN7, io + UART_LCR);
+ outb(1, io + UART_DLL); outb(0, io + UART_DLM);
+ /* 7N1+start = 9 bits at 115200 ~ 3 bits at 44000 */
+ outb(UART_LCR_WLEN7, io + UART_LCR);
+ /* FIFO operation */
+ outb(UART_FCR_ENABLE_FIFO, io + UART_FCR);
+ /* interrupts */
+ /* outb(UART_IER_RLSI|UART_IER_RDI|UART_IER_THRI, io + UART_IER); */
+ outb(UART_IER_RDI, io + UART_IER);
+ /* turn on UART */
+ outb(UART_MCR_DTR|UART_MCR_RTS|UART_MCR_OUT2, io + UART_MCR);
+#ifdef LIRC_SIR_ACTISYS_ACT200L
+ init_act200();
+#elif defined(LIRC_SIR_ACTISYS_ACT220L)
+ init_act220();
+#endif
+#endif
+ spin_unlock_irqrestore(&hardware_lock, flags);
+ return 0;
+}
+
+static void drop_hardware(void)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&hardware_lock, flags);
+
+ /* turn off interrupts */
+ outb(0, io + UART_IER);
+
+ spin_unlock_irqrestore(&hardware_lock, flags);
+}
+
+/* SECTION: Initialisation */
+
+static int init_port(void)
+{
+ int retval;
+
+ /* get I/O port access and IRQ line */
+ if (request_region(io, 8, LIRC_DRIVER_NAME) == NULL) {
+ pr_err("i/o port 0x%.4x already in use.\n", io);
+ return -EBUSY;
+ }
+ retval = request_irq(irq, sir_interrupt, 0,
+ LIRC_DRIVER_NAME, NULL);
+ if (retval < 0) {
+ release_region(io, 8);
+ pr_err("IRQ %d already in use.\n", irq);
+ return retval;
+ }
+ pr_info("I/O port 0x%.4x, IRQ %d.\n", io, irq);
+
+ setup_timer(&timerlist, sir_timeout, 0);
+
+ return 0;
+}
+
+static void drop_port(void)
+{
+ free_irq(irq, NULL);
+ del_timer_sync(&timerlist);
+ release_region(io, 8);
+}
+
+#ifdef LIRC_SIR_ACTISYS_ACT200L
+/* Crystal/Cirrus CS8130 IR transceiver, used in Actisys Act200L dongle */
+/* some code borrowed from Linux IRDA driver */
+
+/* Register 0: Control register #1 */
+#define ACT200L_REG0 0x00
+#define ACT200L_TXEN 0x01 /* Enable transmitter */
+#define ACT200L_RXEN 0x02 /* Enable receiver */
+#define ACT200L_ECHO 0x08 /* Echo control chars */
+
+/* Register 1: Control register #2 */
+#define ACT200L_REG1 0x10
+#define ACT200L_LODB 0x01 /* Load new baud rate count value */
+#define ACT200L_WIDE 0x04 /* Expand the maximum allowable pulse */
+
+/* Register 3: Transmit mode register #2 */
+#define ACT200L_REG3 0x30
+#define ACT200L_B0 0x01 /* DataBits, 0=6, 1=7, 2=8, 3=9(8P) */
+#define ACT200L_B1 0x02 /* DataBits, 0=6, 1=7, 2=8, 3=9(8P) */
+#define ACT200L_CHSY 0x04 /* StartBit Synced 0=bittime, 1=startbit */
+
+/* Register 4: Output Power register */
+#define ACT200L_REG4 0x40
+#define ACT200L_OP0 0x01 /* Enable LED1C output */
+#define ACT200L_OP1 0x02 /* Enable LED2C output */
+#define ACT200L_BLKR 0x04
+
+/* Register 5: Receive Mode register */
+#define ACT200L_REG5 0x50
+#define ACT200L_RWIDL 0x01 /* fixed 1.6us pulse mode */
+ /*.. other various IRDA bit modes, and TV remote modes..*/
+
+/* Register 6: Receive Sensitivity register #1 */
+#define ACT200L_REG6 0x60
+#define ACT200L_RS0 0x01 /* receive threshold bit 0 */
+#define ACT200L_RS1 0x02 /* receive threshold bit 1 */
+
+/* Register 7: Receive Sensitivity register #2 */
+#define ACT200L_REG7 0x70
+#define ACT200L_ENPOS 0x04 /* Ignore the falling edge */
+
+/* Register 8,9: Baud Rate Divider register #1,#2 */
+#define ACT200L_REG8 0x80
+#define ACT200L_REG9 0x90
+
+#define ACT200L_2400 0x5f
+#define ACT200L_9600 0x17
+#define ACT200L_19200 0x0b
+#define ACT200L_38400 0x05
+#define ACT200L_57600 0x03
+#define ACT200L_115200 0x01
+
+/* Register 13: Control register #3 */
+#define ACT200L_REG13 0xd0
+#define ACT200L_SHDW 0x01 /* Enable access to shadow registers */
+
+/* Register 15: Status register */
+#define ACT200L_REG15 0xf0
+
+/* Register 21: Control register #4 */
+#define ACT200L_REG21 0x50
+#define ACT200L_EXCK 0x02 /* Disable clock output driver */
+#define ACT200L_OSCL 0x04 /* oscillator in low power, medium accuracy mode */
+
+static void init_act200(void)
+{
+ int i;
+ __u8 control[] = {
+ ACT200L_REG15,
+ ACT200L_REG13 | ACT200L_SHDW,
+ ACT200L_REG21 | ACT200L_EXCK | ACT200L_OSCL,
+ ACT200L_REG13,
+ ACT200L_REG7 | ACT200L_ENPOS,
+ ACT200L_REG6 | ACT200L_RS0 | ACT200L_RS1,
+ ACT200L_REG5 | ACT200L_RWIDL,
+ ACT200L_REG4 | ACT200L_OP0 | ACT200L_OP1 | ACT200L_BLKR,
+ ACT200L_REG3 | ACT200L_B0,
+ ACT200L_REG0 | ACT200L_TXEN | ACT200L_RXEN,
+ ACT200L_REG8 | (ACT200L_115200 & 0x0f),
+ ACT200L_REG9 | ((ACT200L_115200 >> 4) & 0x0f),
+ ACT200L_REG1 | ACT200L_LODB | ACT200L_WIDE
+ };
+
+ /* Set DLAB 1. */
+ soutp(UART_LCR, UART_LCR_DLAB | UART_LCR_WLEN8);
+
+ /* Set divisor to 12 => 9600 Baud */
+ soutp(UART_DLM, 0);
+ soutp(UART_DLL, 12);
+
+ /* Set DLAB 0. */
+ soutp(UART_LCR, UART_LCR_WLEN8);
+ /* Set divisor to 12 => 9600 Baud */
+
+ /* power supply */
+ soutp(UART_MCR, UART_MCR_RTS|UART_MCR_DTR|UART_MCR_OUT2);
+ for (i = 0; i < 50; i++)
+ safe_udelay(1000);
+
+ /* Reset the dongle : set RTS low for 25 ms */
+ soutp(UART_MCR, UART_MCR_DTR|UART_MCR_OUT2);
+ for (i = 0; i < 25; i++)
+ udelay(1000);
+
+ soutp(UART_MCR, UART_MCR_RTS|UART_MCR_DTR|UART_MCR_OUT2);
+ udelay(100);
+
+ /* Clear DTR and set RTS to enter command mode */
+ soutp(UART_MCR, UART_MCR_RTS|UART_MCR_OUT2);
+ udelay(7);
+
+ /* send out the control register settings for 115K 7N1 SIR operation */
+ for (i = 0; i < sizeof(control); i++) {
+ soutp(UART_TX, control[i]);
+ /* one byte takes ~1042 usec to transmit at 9600,8N1 */
+ udelay(1500);
+ }
+
+ /* back to normal operation */
+ soutp(UART_MCR, UART_MCR_RTS|UART_MCR_DTR|UART_MCR_OUT2);
+ udelay(50);
+
+ udelay(1500);
+ soutp(UART_LCR, sinp(UART_LCR) | UART_LCR_DLAB);
+
+ /* Set DLAB 1. */
+ soutp(UART_LCR, UART_LCR_DLAB | UART_LCR_WLEN7);
+
+ /* Set divisor to 1 => 115200 Baud */
+ soutp(UART_DLM, 0);
+ soutp(UART_DLL, 1);
+
+ /* Set DLAB 0. */
+ soutp(UART_LCR, sinp(UART_LCR) & (~UART_LCR_DLAB));
+
+ /* Set DLAB 0, 7 Bit */
+ soutp(UART_LCR, UART_LCR_WLEN7);
+
+ /* enable interrupts */
+ soutp(UART_IER, sinp(UART_IER)|UART_IER_RDI);
+}
+#endif
+
+#ifdef LIRC_SIR_ACTISYS_ACT220L
+/*
+ * Derived from linux IrDA driver (net/irda/actisys.c)
+ * Drop me a mail for any kind of comment: maxx@spaceboyz.net
+ */
+
+void init_act220(void)
+{
+ int i;
+
+ /* DLAB 1 */
+ soutp(UART_LCR, UART_LCR_DLAB|UART_LCR_WLEN7);
+
+ /* 9600 baud */
+ soutp(UART_DLM, 0);
+ soutp(UART_DLL, 12);
+
+ /* DLAB 0 */
+ soutp(UART_LCR, UART_LCR_WLEN7);
+
+ /* reset the dongle, set DTR low for 10us */
+ soutp(UART_MCR, UART_MCR_RTS|UART_MCR_OUT2);
+ udelay(10);
+
+ /* back to normal (still 9600) */
+ soutp(UART_MCR, UART_MCR_DTR|UART_MCR_RTS|UART_MCR_OUT2);
+
+ /*
+ * send RTS pulses until we reach 115200
+ * i hope this is really the same for act220l/act220l+
+ */
+ for (i = 0; i < 3; i++) {
+ udelay(10);
+ /* set RTS low for 10 us */
+ soutp(UART_MCR, UART_MCR_DTR|UART_MCR_OUT2);
+ udelay(10);
+ /* set RTS high for 10 us */
+ soutp(UART_MCR, UART_MCR_RTS|UART_MCR_DTR|UART_MCR_OUT2);
+ }
+
+ /* back to normal operation */
+ udelay(1500); /* better safe than sorry ;) */
+
+ /* Set DLAB 1. */
+ soutp(UART_LCR, UART_LCR_DLAB | UART_LCR_WLEN7);
+
+ /* Set divisor to 1 => 115200 Baud */
+ soutp(UART_DLM, 0);
+ soutp(UART_DLL, 1);
+
+ /* Set DLAB 0, 7 Bit */
+ /* The dongle doesn't seem to have any problems with operation at 7N1 */
+ soutp(UART_LCR, UART_LCR_WLEN7);
+
+ /* enable interrupts */
+ soutp(UART_IER, UART_IER_RDI);
+}
+#endif
+
+static int init_lirc_sir(void)
+{
+ int retval;
+
+ init_waitqueue_head(&lirc_read_queue);
+ retval = init_port();
+ if (retval < 0)
+ return retval;
+ init_hardware();
+ pr_info("Installed.\n");
+ return 0;
+}
+
+static int lirc_sir_probe(struct platform_device *dev)
+{
+ return 0;
+}
+
+static int lirc_sir_remove(struct platform_device *dev)
+{
+ return 0;
+}
+
+static struct platform_driver lirc_sir_driver = {
+ .probe = lirc_sir_probe,
+ .remove = lirc_sir_remove,
+ .driver = {
+ .name = "lirc_sir",
+ },
+};
+
+static int __init lirc_sir_init(void)
+{
+ int retval;
+
+ retval = platform_driver_register(&lirc_sir_driver);
+ if (retval) {
+ pr_err("Platform driver register failed!\n");
+ return -ENODEV;
+ }
+
+ lirc_sir_dev = platform_device_alloc("lirc_dev", 0);
+ if (!lirc_sir_dev) {
+ pr_err("Platform device alloc failed!\n");
+ retval = -ENOMEM;
+ goto pdev_alloc_fail;
+ }
+
+ retval = platform_device_add(lirc_sir_dev);
+ if (retval) {
+ pr_err("Platform device add failed!\n");
+ retval = -ENODEV;
+ goto pdev_add_fail;
+ }
+
+ retval = init_chrdev();
+ if (retval < 0)
+ goto fail;
+
+ retval = init_lirc_sir();
+ if (retval) {
+ drop_chrdev();
+ goto fail;
+ }
+
+ return 0;
+
+fail:
+ platform_device_del(lirc_sir_dev);
+pdev_add_fail:
+ platform_device_put(lirc_sir_dev);
+pdev_alloc_fail:
+ platform_driver_unregister(&lirc_sir_driver);
+ return retval;
+}
+
+static void __exit lirc_sir_exit(void)
+{
+ drop_hardware();
+ drop_chrdev();
+ drop_port();
+ platform_device_unregister(lirc_sir_dev);
+ platform_driver_unregister(&lirc_sir_driver);
+ pr_info("Uninstalled.\n");
+}
+
+module_init(lirc_sir_init);
+module_exit(lirc_sir_exit);
+
+#ifdef LIRC_SIR_TEKRAM
+MODULE_DESCRIPTION("Infrared receiver driver for Tekram Irmate 210");
+MODULE_AUTHOR("Christoph Bartelmus");
+#elif defined(LIRC_SIR_ACTISYS_ACT200L)
+MODULE_DESCRIPTION("LIRC driver for Actisys Act200L");
+MODULE_AUTHOR("Karl Bongers");
+#elif defined(LIRC_SIR_ACTISYS_ACT220L)
+MODULE_DESCRIPTION("LIRC driver for Actisys Act220L(+)");
+MODULE_AUTHOR("Jan Roemisch");
+#else
+MODULE_DESCRIPTION("Infrared receiver driver for SIR type serial ports");
+MODULE_AUTHOR("Milan Pikula");
+#endif
+MODULE_LICENSE("GPL");
+
+module_param(io, int, S_IRUGO);
+MODULE_PARM_DESC(io, "I/O address base (0x3f8 or 0x2f8)");
+
+module_param(irq, int, S_IRUGO);
+MODULE_PARM_DESC(irq, "Interrupt (4 or 3)");
+
+module_param(threshold, int, S_IRUGO);
+MODULE_PARM_DESC(threshold, "space detection threshold (3)");
+
+module_param(debug, bool, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(debug, "Enable debugging messages");
diff --git a/drivers/staging/media/lirc/lirc_zilog.c b/drivers/staging/media/lirc/lirc_zilog.c
new file mode 100644
index 000000000..cc1b7f165
--- /dev/null
+++ b/drivers/staging/media/lirc/lirc_zilog.c
@@ -0,0 +1,1697 @@
+/*
+ * i2c IR lirc driver for devices with zilog IR processors
+ *
+ * Copyright (c) 2000 Gerd Knorr <kraxel@goldbach.in-berlin.de>
+ * modified for PixelView (BT878P+W/FM) by
+ * Michal Kochanowicz <mkochano@pld.org.pl>
+ * Christoph Bartelmus <lirc@bartelmus.de>
+ * modified for KNC ONE TV Station/Anubis Typhoon TView Tuner by
+ * Ulrich Mueller <ulrich.mueller42@web.de>
+ * modified for Asus TV-Box and Creative/VisionTek BreakOut-Box by
+ * Stefan Jahn <stefan@lkcc.org>
+ * modified for inclusion into kernel sources by
+ * Jerome Brock <jbrock@users.sourceforge.net>
+ * modified for Leadtek Winfast PVR2000 by
+ * Thomas Reitmayr (treitmayr@yahoo.com)
+ * modified for Hauppauge PVR-150 IR TX device by
+ * Mark Weaver <mark@npsl.co.uk>
+ * changed name from lirc_pvr150 to lirc_zilog, works on more than pvr-150
+ * Jarod Wilson <jarod@redhat.com>
+ *
+ * parts are cut&pasted from the lirc_i2c.c driver
+ *
+ * Numerous changes updating lirc_zilog.c in kernel 2.6.38 and later are
+ * Copyright (C) 2011 Andy Walls <awalls@md.metrocast.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/kmod.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/fs.h>
+#include <linux/poll.h>
+#include <linux/string.h>
+#include <linux/timer.h>
+#include <linux/delay.h>
+#include <linux/completion.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/firmware.h>
+#include <linux/vmalloc.h>
+
+#include <linux/mutex.h>
+#include <linux/kthread.h>
+
+#include <media/lirc_dev.h>
+#include <media/lirc.h>
+
+/* Max transfer size done by I2C transfer functions */
+#define MAX_XFER_SIZE 64
+
+struct IR;
+
+struct IR_rx {
+ struct kref ref;
+ struct IR *ir;
+
+ /* RX device */
+ struct mutex client_lock;
+ struct i2c_client *c;
+
+ /* RX polling thread data */
+ struct task_struct *task;
+
+ /* RX read data */
+ unsigned char b[3];
+ bool hdpvr_data_fmt;
+};
+
+struct IR_tx {
+ struct kref ref;
+ struct IR *ir;
+
+ /* TX device */
+ struct mutex client_lock;
+ struct i2c_client *c;
+
+ /* TX additional actions needed */
+ int need_boot;
+ bool post_tx_ready_poll;
+};
+
+struct IR {
+ struct kref ref;
+ struct list_head list;
+
+ /* FIXME spinlock access to l.features */
+ struct lirc_driver l;
+ struct lirc_buffer rbuf;
+
+ struct mutex ir_lock;
+ atomic_t open_count;
+
+ struct i2c_adapter *adapter;
+
+ spinlock_t rx_ref_lock; /* struct IR_rx kref get()/put() */
+ struct IR_rx *rx;
+
+ spinlock_t tx_ref_lock; /* struct IR_tx kref get()/put() */
+ struct IR_tx *tx;
+};
+
+/* IR transceiver instance object list */
+/*
+ * This lock is used for the following:
+ * a. ir_devices_list access, insertions, deletions
+ * b. struct IR kref get()s and put()s
+ * c. serialization of ir_probe() for the two i2c_clients for a Z8
+ */
+static DEFINE_MUTEX(ir_devices_lock);
+static LIST_HEAD(ir_devices_list);
+
+/* Block size for IR transmitter */
+#define TX_BLOCK_SIZE 99
+
+/* Hauppauge IR transmitter data */
+struct tx_data_struct {
+ /* Boot block */
+ unsigned char *boot_data;
+
+ /* Start of binary data block */
+ unsigned char *datap;
+
+ /* End of binary data block */
+ unsigned char *endp;
+
+ /* Number of installed codesets */
+ unsigned int num_code_sets;
+
+ /* Pointers to codesets */
+ unsigned char **code_sets;
+
+ /* Global fixed data template */
+ int fixed[TX_BLOCK_SIZE];
+};
+
+static struct tx_data_struct *tx_data;
+static struct mutex tx_data_lock;
+
+
+/* module parameters */
+static bool debug; /* debug output */
+static bool tx_only; /* only handle the IR Tx function */
+static int minor = -1; /* minor number */
+
+
+/* struct IR reference counting */
+static struct IR *get_ir_device(struct IR *ir, bool ir_devices_lock_held)
+{
+ if (ir_devices_lock_held) {
+ kref_get(&ir->ref);
+ } else {
+ mutex_lock(&ir_devices_lock);
+ kref_get(&ir->ref);
+ mutex_unlock(&ir_devices_lock);
+ }
+ return ir;
+}
+
+static void release_ir_device(struct kref *ref)
+{
+ struct IR *ir = container_of(ref, struct IR, ref);
+
+ /*
+ * Things should be in this state by now:
+ * ir->rx set to NULL and deallocated - happens before ir->rx->ir put()
+ * ir->rx->task kthread stopped - happens before ir->rx->ir put()
+ * ir->tx set to NULL and deallocated - happens before ir->tx->ir put()
+ * ir->open_count == 0 - happens on final close()
+ * ir_lock, tx_ref_lock, rx_ref_lock, all released
+ */
+ if (ir->l.minor >= 0 && ir->l.minor < MAX_IRCTL_DEVICES) {
+ lirc_unregister_driver(ir->l.minor);
+ ir->l.minor = MAX_IRCTL_DEVICES;
+ }
+ if (kfifo_initialized(&ir->rbuf.fifo))
+ lirc_buffer_free(&ir->rbuf);
+ list_del(&ir->list);
+ kfree(ir);
+}
+
+static int put_ir_device(struct IR *ir, bool ir_devices_lock_held)
+{
+ int released;
+
+ if (ir_devices_lock_held)
+ return kref_put(&ir->ref, release_ir_device);
+
+ mutex_lock(&ir_devices_lock);
+ released = kref_put(&ir->ref, release_ir_device);
+ mutex_unlock(&ir_devices_lock);
+
+ return released;
+}
+
+/* struct IR_rx reference counting */
+static struct IR_rx *get_ir_rx(struct IR *ir)
+{
+ struct IR_rx *rx;
+
+ spin_lock(&ir->rx_ref_lock);
+ rx = ir->rx;
+ if (rx != NULL)
+ kref_get(&rx->ref);
+ spin_unlock(&ir->rx_ref_lock);
+ return rx;
+}
+
+static void destroy_rx_kthread(struct IR_rx *rx, bool ir_devices_lock_held)
+{
+ /* end up polling thread */
+ if (!IS_ERR_OR_NULL(rx->task)) {
+ kthread_stop(rx->task);
+ rx->task = NULL;
+ /* Put the ir ptr that ir_probe() gave to the rx poll thread */
+ put_ir_device(rx->ir, ir_devices_lock_held);
+ }
+}
+
+static void release_ir_rx(struct kref *ref)
+{
+ struct IR_rx *rx = container_of(ref, struct IR_rx, ref);
+ struct IR *ir = rx->ir;
+
+ /*
+ * This release function can't do all the work, as we want
+ * to keep the rx_ref_lock a spinlock, and killing the poll thread
+ * and releasing the ir reference can cause a sleep. That work is
+ * performed by put_ir_rx()
+ */
+ ir->l.features &= ~LIRC_CAN_REC_LIRCCODE;
+ /* Don't put_ir_device(rx->ir) here; lock can't be freed yet */
+ ir->rx = NULL;
+ /* Don't do the kfree(rx) here; we still need to kill the poll thread */
+}
+
+static int put_ir_rx(struct IR_rx *rx, bool ir_devices_lock_held)
+{
+ int released;
+ struct IR *ir = rx->ir;
+
+ spin_lock(&ir->rx_ref_lock);
+ released = kref_put(&rx->ref, release_ir_rx);
+ spin_unlock(&ir->rx_ref_lock);
+ /* Destroy the rx kthread while not holding the spinlock */
+ if (released) {
+ destroy_rx_kthread(rx, ir_devices_lock_held);
+ kfree(rx);
+ /* Make sure we're not still in a poll_table somewhere */
+ wake_up_interruptible(&ir->rbuf.wait_poll);
+ }
+ /* Do a reference put() for the rx->ir reference, if we released rx */
+ if (released)
+ put_ir_device(ir, ir_devices_lock_held);
+ return released;
+}
+
+/* struct IR_tx reference counting */
+static struct IR_tx *get_ir_tx(struct IR *ir)
+{
+ struct IR_tx *tx;
+
+ spin_lock(&ir->tx_ref_lock);
+ tx = ir->tx;
+ if (tx != NULL)
+ kref_get(&tx->ref);
+ spin_unlock(&ir->tx_ref_lock);
+ return tx;
+}
+
+static void release_ir_tx(struct kref *ref)
+{
+ struct IR_tx *tx = container_of(ref, struct IR_tx, ref);
+ struct IR *ir = tx->ir;
+
+ ir->l.features &= ~LIRC_CAN_SEND_PULSE;
+ /* Don't put_ir_device(tx->ir) here, so our lock doesn't get freed */
+ ir->tx = NULL;
+ kfree(tx);
+}
+
+static int put_ir_tx(struct IR_tx *tx, bool ir_devices_lock_held)
+{
+ int released;
+ struct IR *ir = tx->ir;
+
+ spin_lock(&ir->tx_ref_lock);
+ released = kref_put(&tx->ref, release_ir_tx);
+ spin_unlock(&ir->tx_ref_lock);
+ /* Do a reference put() for the tx->ir reference, if we released tx */
+ if (released)
+ put_ir_device(ir, ir_devices_lock_held);
+ return released;
+}
+
+static int add_to_buf(struct IR *ir)
+{
+ __u16 code;
+ unsigned char codes[2];
+ unsigned char keybuf[6];
+ int got_data = 0;
+ int ret;
+ int failures = 0;
+ unsigned char sendbuf[1] = { 0 };
+ struct lirc_buffer *rbuf = ir->l.rbuf;
+ struct IR_rx *rx;
+ struct IR_tx *tx;
+
+ if (lirc_buffer_full(rbuf)) {
+ dev_dbg(ir->l.dev, "buffer overflow\n");
+ return -EOVERFLOW;
+ }
+
+ rx = get_ir_rx(ir);
+ if (rx == NULL)
+ return -ENXIO;
+
+ /* Ensure our rx->c i2c_client remains valid for the duration */
+ mutex_lock(&rx->client_lock);
+ if (rx->c == NULL) {
+ mutex_unlock(&rx->client_lock);
+ put_ir_rx(rx, false);
+ return -ENXIO;
+ }
+
+ tx = get_ir_tx(ir);
+
+ /*
+ * service the device as long as it is returning
+ * data and we have space
+ */
+ do {
+ if (kthread_should_stop()) {
+ ret = -ENODATA;
+ break;
+ }
+
+ /*
+ * Lock i2c bus for the duration. RX/TX chips interfere so
+ * this is worth it
+ */
+ mutex_lock(&ir->ir_lock);
+
+ if (kthread_should_stop()) {
+ mutex_unlock(&ir->ir_lock);
+ ret = -ENODATA;
+ break;
+ }
+
+ /*
+ * Send random "poll command" (?) Windows driver does this
+ * and it is a good point to detect chip failure.
+ */
+ ret = i2c_master_send(rx->c, sendbuf, 1);
+ if (ret != 1) {
+ dev_err(ir->l.dev, "i2c_master_send failed with %d\n",
+ ret);
+ if (failures >= 3) {
+ mutex_unlock(&ir->ir_lock);
+ dev_err(ir->l.dev,
+ "unable to read from the IR chip after 3 resets, giving up\n");
+ break;
+ }
+
+ /* Looks like the chip crashed, reset it */
+ dev_err(ir->l.dev,
+ "polling the IR receiver chip failed, trying reset\n");
+
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ if (kthread_should_stop()) {
+ mutex_unlock(&ir->ir_lock);
+ ret = -ENODATA;
+ break;
+ }
+ schedule_timeout((100 * HZ + 999) / 1000);
+ if (tx != NULL)
+ tx->need_boot = 1;
+
+ ++failures;
+ mutex_unlock(&ir->ir_lock);
+ ret = 0;
+ continue;
+ }
+
+ if (kthread_should_stop()) {
+ mutex_unlock(&ir->ir_lock);
+ ret = -ENODATA;
+ break;
+ }
+ ret = i2c_master_recv(rx->c, keybuf, sizeof(keybuf));
+ mutex_unlock(&ir->ir_lock);
+ if (ret != sizeof(keybuf)) {
+ dev_err(ir->l.dev,
+ "i2c_master_recv failed with %d -- keeping last read buffer\n",
+ ret);
+ } else {
+ rx->b[0] = keybuf[3];
+ rx->b[1] = keybuf[4];
+ rx->b[2] = keybuf[5];
+ dev_dbg(ir->l.dev,
+ "key (0x%02x/0x%02x)\n",
+ rx->b[0], rx->b[1]);
+ }
+
+ /* key pressed ? */
+ if (rx->hdpvr_data_fmt) {
+ if (got_data && (keybuf[0] == 0x80)) {
+ ret = 0;
+ break;
+ } else if (got_data && (keybuf[0] == 0x00)) {
+ ret = -ENODATA;
+ break;
+ }
+ } else if ((rx->b[0] & 0x80) == 0) {
+ ret = got_data ? 0 : -ENODATA;
+ break;
+ }
+
+ /* look what we have */
+ code = (((__u16)rx->b[0] & 0x7f) << 6) | (rx->b[1] >> 2);
+
+ codes[0] = (code >> 8) & 0xff;
+ codes[1] = code & 0xff;
+
+ /* return it */
+ lirc_buffer_write(rbuf, codes);
+ ++got_data;
+ ret = 0;
+ } while (!lirc_buffer_full(rbuf));
+
+ mutex_unlock(&rx->client_lock);
+ if (tx != NULL)
+ put_ir_tx(tx, false);
+ put_ir_rx(rx, false);
+ return ret;
+}
+
+/*
+ * Main function of the polling thread -- from lirc_dev.
+ * We don't fit the LIRC model at all anymore. This is horrible, but
+ * basically we have a single RX/TX device with a nasty failure mode
+ * that needs to be accounted for across the pair. lirc lets us provide
+ * fops, but prevents us from using the internal polling, etc. if we do
+ * so. Hence the replication. Might be neater to extend the LIRC model
+ * to account for this but I'd think it's a very special case of seriously
+ * messed up hardware.
+ */
+static int lirc_thread(void *arg)
+{
+ struct IR *ir = arg;
+ struct lirc_buffer *rbuf = ir->l.rbuf;
+
+ dev_dbg(ir->l.dev, "poll thread started\n");
+
+ while (!kthread_should_stop()) {
+ set_current_state(TASK_INTERRUPTIBLE);
+
+ /* if device not opened, we can sleep half a second */
+ if (atomic_read(&ir->open_count) == 0) {
+ schedule_timeout(HZ/2);
+ continue;
+ }
+
+ /*
+ * This is ~113*2 + 24 + jitter (2*repeat gap + code length).
+ * We use this interval as the chip resets every time you poll
+ * it (bad!). This is therefore just sufficient to catch all
+ * of the button presses. It makes the remote much more
+ * responsive. You can see the difference by running irw and
+ * holding down a button. With 100ms, the old polling
+ * interval, you'll notice breaks in the repeat sequence
+ * corresponding to lost keypresses.
+ */
+ schedule_timeout((260 * HZ) / 1000);
+ if (kthread_should_stop())
+ break;
+ if (!add_to_buf(ir))
+ wake_up_interruptible(&rbuf->wait_poll);
+ }
+
+ dev_dbg(ir->l.dev, "poll thread ended\n");
+ return 0;
+}
+
+static int set_use_inc(void *data)
+{
+ return 0;
+}
+
+static void set_use_dec(void *data)
+{
+}
+
+/* safe read of a uint32 (always network byte order) */
+static int read_uint32(unsigned char **data,
+ unsigned char *endp, unsigned int *val)
+{
+ if (*data + 4 > endp)
+ return 0;
+ *val = ((*data)[0] << 24) | ((*data)[1] << 16) |
+ ((*data)[2] << 8) | (*data)[3];
+ *data += 4;
+ return 1;
+}
+
+/* safe read of a uint8 */
+static int read_uint8(unsigned char **data,
+ unsigned char *endp, unsigned char *val)
+{
+ if (*data + 1 > endp)
+ return 0;
+ *val = *((*data)++);
+ return 1;
+}
+
+/* safe skipping of N bytes */
+static int skip(unsigned char **data,
+ unsigned char *endp, unsigned int distance)
+{
+ if (*data + distance > endp)
+ return 0;
+ *data += distance;
+ return 1;
+}
+
+/* decompress key data into the given buffer */
+static int get_key_data(unsigned char *buf,
+ unsigned int codeset, unsigned int key)
+{
+ unsigned char *data, *endp, *diffs, *key_block;
+ unsigned char keys, ndiffs, id;
+ unsigned int base, lim, pos, i;
+
+ /* Binary search for the codeset */
+ for (base = 0, lim = tx_data->num_code_sets; lim; lim >>= 1) {
+ pos = base + (lim >> 1);
+ data = tx_data->code_sets[pos];
+
+ if (!read_uint32(&data, tx_data->endp, &i))
+ goto corrupt;
+
+ if (i == codeset)
+ break;
+ else if (codeset > i) {
+ base = pos + 1;
+ --lim;
+ }
+ }
+ /* Not found? */
+ if (!lim)
+ return -EPROTO;
+
+ /* Set end of data block */
+ endp = pos < tx_data->num_code_sets - 1 ?
+ tx_data->code_sets[pos + 1] : tx_data->endp;
+
+ /* Read the block header */
+ if (!read_uint8(&data, endp, &keys) ||
+ !read_uint8(&data, endp, &ndiffs) ||
+ ndiffs > TX_BLOCK_SIZE || keys == 0)
+ goto corrupt;
+
+ /* Save diffs & skip */
+ diffs = data;
+ if (!skip(&data, endp, ndiffs))
+ goto corrupt;
+
+ /* Read the id of the first key */
+ if (!read_uint8(&data, endp, &id))
+ goto corrupt;
+
+ /* Unpack the first key's data */
+ for (i = 0; i < TX_BLOCK_SIZE; ++i) {
+ if (tx_data->fixed[i] == -1) {
+ if (!read_uint8(&data, endp, &buf[i]))
+ goto corrupt;
+ } else {
+ buf[i] = (unsigned char)tx_data->fixed[i];
+ }
+ }
+
+ /* Early out key found/not found */
+ if (key == id)
+ return 0;
+ if (keys == 1)
+ return -EPROTO;
+
+ /* Sanity check */
+ key_block = data;
+ if (!skip(&data, endp, (keys - 1) * (ndiffs + 1)))
+ goto corrupt;
+
+ /* Binary search for the key */
+ for (base = 0, lim = keys - 1; lim; lim >>= 1) {
+ /* Seek to block */
+ unsigned char *key_data;
+
+ pos = base + (lim >> 1);
+ key_data = key_block + (ndiffs + 1) * pos;
+
+ if (*key_data == key) {
+ /* skip key id */
+ ++key_data;
+
+ /* found, so unpack the diffs */
+ for (i = 0; i < ndiffs; ++i) {
+ unsigned char val;
+
+ if (!read_uint8(&key_data, endp, &val) ||
+ diffs[i] >= TX_BLOCK_SIZE)
+ goto corrupt;
+ buf[diffs[i]] = val;
+ }
+
+ return 0;
+ } else if (key > *key_data) {
+ base = pos + 1;
+ --lim;
+ }
+ }
+ /* Key not found */
+ return -EPROTO;
+
+corrupt:
+ pr_err("firmware is corrupt\n");
+ return -EFAULT;
+}
+
+/* send a block of data to the IR TX device */
+static int send_data_block(struct IR_tx *tx, unsigned char *data_block)
+{
+ int i, j, ret;
+ unsigned char buf[5];
+
+ for (i = 0; i < TX_BLOCK_SIZE;) {
+ int tosend = TX_BLOCK_SIZE - i;
+
+ if (tosend > 4)
+ tosend = 4;
+ buf[0] = (unsigned char)(i + 1);
+ for (j = 0; j < tosend; ++j)
+ buf[1 + j] = data_block[i + j];
+ dev_dbg(tx->ir->l.dev, "%*ph", 5, buf);
+ ret = i2c_master_send(tx->c, buf, tosend + 1);
+ if (ret != tosend + 1) {
+ dev_err(tx->ir->l.dev,
+ "i2c_master_send failed with %d\n", ret);
+ return ret < 0 ? ret : -EFAULT;
+ }
+ i += tosend;
+ }
+ return 0;
+}
+
+/* send boot data to the IR TX device */
+static int send_boot_data(struct IR_tx *tx)
+{
+ int ret, i;
+ unsigned char buf[4];
+
+ /* send the boot block */
+ ret = send_data_block(tx, tx_data->boot_data);
+ if (ret != 0)
+ return ret;
+
+ /* Hit the go button to activate the new boot data */
+ buf[0] = 0x00;
+ buf[1] = 0x20;
+ ret = i2c_master_send(tx->c, buf, 2);
+ if (ret != 2) {
+ dev_err(tx->ir->l.dev, "i2c_master_send failed with %d\n", ret);
+ return ret < 0 ? ret : -EFAULT;
+ }
+
+ /*
+ * Wait for zilog to settle after hitting go post boot block upload.
+ * Without this delay, the HD-PVR and HVR-1950 both return an -EIO
+ * upon attempting to get firmware revision, and tx probe thus fails.
+ */
+ for (i = 0; i < 10; i++) {
+ ret = i2c_master_send(tx->c, buf, 1);
+ if (ret == 1)
+ break;
+ udelay(100);
+ }
+
+ if (ret != 1) {
+ dev_err(tx->ir->l.dev, "i2c_master_send failed with %d\n", ret);
+ return ret < 0 ? ret : -EFAULT;
+ }
+
+ /* Here comes the firmware version... (hopefully) */
+ ret = i2c_master_recv(tx->c, buf, 4);
+ if (ret != 4) {
+ dev_err(tx->ir->l.dev, "i2c_master_recv failed with %d\n", ret);
+ return 0;
+ }
+ if ((buf[0] != 0x80) && (buf[0] != 0xa0)) {
+ dev_err(tx->ir->l.dev, "unexpected IR TX init response: %02x\n",
+ buf[0]);
+ return 0;
+ }
+ dev_notice(tx->ir->l.dev,
+ "Zilog/Hauppauge IR blaster firmware version %d.%d.%d loaded\n",
+ buf[1], buf[2], buf[3]);
+
+ return 0;
+}
+
+/* unload "firmware", lock held */
+static void fw_unload_locked(void)
+{
+ if (tx_data) {
+ vfree(tx_data->code_sets);
+
+ vfree(tx_data->datap);
+
+ vfree(tx_data);
+ tx_data = NULL;
+ pr_debug("successfully unloaded IR blaster firmware\n");
+ }
+}
+
+/* unload "firmware" for the IR TX device */
+static void fw_unload(void)
+{
+ mutex_lock(&tx_data_lock);
+ fw_unload_locked();
+ mutex_unlock(&tx_data_lock);
+}
+
+/* load "firmware" for the IR TX device */
+static int fw_load(struct IR_tx *tx)
+{
+ int ret;
+ unsigned int i;
+ unsigned char *data, version, num_global_fixed;
+ const struct firmware *fw_entry;
+
+ /* Already loaded? */
+ mutex_lock(&tx_data_lock);
+ if (tx_data) {
+ ret = 0;
+ goto out;
+ }
+
+ /* Request codeset data file */
+ ret = reject_firmware(&fw_entry, "/*(DEBLOBBED)*/", tx->ir->l.dev);
+ if (ret != 0) {
+ dev_err(tx->ir->l.dev,
+ "firmware /*(DEBLOBBED)*/ not available (%d)\n",
+ ret);
+ ret = ret < 0 ? ret : -EFAULT;
+ goto out;
+ }
+ dev_dbg(tx->ir->l.dev, "firmware of size %zu loaded\n", fw_entry->size);
+
+ /* Parse the file */
+ tx_data = vmalloc(sizeof(*tx_data));
+ if (tx_data == NULL) {
+ release_firmware(fw_entry);
+ ret = -ENOMEM;
+ goto out;
+ }
+ tx_data->code_sets = NULL;
+
+ /* Copy the data so hotplug doesn't get confused and timeout */
+ tx_data->datap = vmalloc(fw_entry->size);
+ if (tx_data->datap == NULL) {
+ release_firmware(fw_entry);
+ vfree(tx_data);
+ ret = -ENOMEM;
+ goto out;
+ }
+ memcpy(tx_data->datap, fw_entry->data, fw_entry->size);
+ tx_data->endp = tx_data->datap + fw_entry->size;
+ release_firmware(fw_entry); fw_entry = NULL;
+
+ /* Check version */
+ data = tx_data->datap;
+ if (!read_uint8(&data, tx_data->endp, &version))
+ goto corrupt;
+ if (version != 1) {
+ dev_err(tx->ir->l.dev,
+ "unsupported code set file version (%u, expected 1) -- please upgrade to a newer driver\n",
+ version);
+ fw_unload_locked();
+ ret = -EFAULT;
+ goto out;
+ }
+
+ /* Save boot block for later */
+ tx_data->boot_data = data;
+ if (!skip(&data, tx_data->endp, TX_BLOCK_SIZE))
+ goto corrupt;
+
+ if (!read_uint32(&data, tx_data->endp,
+ &tx_data->num_code_sets))
+ goto corrupt;
+
+ dev_dbg(tx->ir->l.dev, "%u IR blaster codesets loaded\n",
+ tx_data->num_code_sets);
+
+ tx_data->code_sets = vmalloc(
+ tx_data->num_code_sets * sizeof(char *));
+ if (tx_data->code_sets == NULL) {
+ fw_unload_locked();
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ for (i = 0; i < TX_BLOCK_SIZE; ++i)
+ tx_data->fixed[i] = -1;
+
+ /* Read global fixed data template */
+ if (!read_uint8(&data, tx_data->endp, &num_global_fixed) ||
+ num_global_fixed > TX_BLOCK_SIZE)
+ goto corrupt;
+ for (i = 0; i < num_global_fixed; ++i) {
+ unsigned char pos, val;
+
+ if (!read_uint8(&data, tx_data->endp, &pos) ||
+ !read_uint8(&data, tx_data->endp, &val) ||
+ pos >= TX_BLOCK_SIZE)
+ goto corrupt;
+ tx_data->fixed[pos] = (int)val;
+ }
+
+ /* Filch out the position of each code set */
+ for (i = 0; i < tx_data->num_code_sets; ++i) {
+ unsigned int id;
+ unsigned char keys;
+ unsigned char ndiffs;
+
+ /* Save the codeset position */
+ tx_data->code_sets[i] = data;
+
+ /* Read header */
+ if (!read_uint32(&data, tx_data->endp, &id) ||
+ !read_uint8(&data, tx_data->endp, &keys) ||
+ !read_uint8(&data, tx_data->endp, &ndiffs) ||
+ ndiffs > TX_BLOCK_SIZE || keys == 0)
+ goto corrupt;
+
+ /* skip diff positions */
+ if (!skip(&data, tx_data->endp, ndiffs))
+ goto corrupt;
+
+ /*
+ * After the diffs we have the first key id + data -
+ * global fixed
+ */
+ if (!skip(&data, tx_data->endp,
+ 1 + TX_BLOCK_SIZE - num_global_fixed))
+ goto corrupt;
+
+ /* Then we have keys-1 blocks of key id+diffs */
+ if (!skip(&data, tx_data->endp,
+ (ndiffs + 1) * (keys - 1)))
+ goto corrupt;
+ }
+ ret = 0;
+ goto out;
+
+corrupt:
+ dev_err(tx->ir->l.dev, "firmware is corrupt\n");
+ fw_unload_locked();
+ ret = -EFAULT;
+
+out:
+ mutex_unlock(&tx_data_lock);
+ return ret;
+}
+
+/* copied from lirc_dev */
+static ssize_t read(struct file *filep, char __user *outbuf, size_t n,
+ loff_t *ppos)
+{
+ struct IR *ir = filep->private_data;
+ struct IR_rx *rx;
+ struct lirc_buffer *rbuf = ir->l.rbuf;
+ int ret = 0, written = 0, retries = 0;
+ unsigned int m;
+ DECLARE_WAITQUEUE(wait, current);
+
+ dev_dbg(ir->l.dev, "read called\n");
+ if (n % rbuf->chunk_size) {
+ dev_dbg(ir->l.dev, "read result = -EINVAL\n");
+ return -EINVAL;
+ }
+
+ rx = get_ir_rx(ir);
+ if (rx == NULL)
+ return -ENXIO;
+
+ /*
+ * we add ourselves to the task queue before buffer check
+ * to avoid losing scan code (in case when queue is awaken somewhere
+ * between while condition checking and scheduling)
+ */
+ add_wait_queue(&rbuf->wait_poll, &wait);
+ set_current_state(TASK_INTERRUPTIBLE);
+
+ /*
+ * while we didn't provide 'length' bytes, device is opened in blocking
+ * mode and 'copy_to_user' is happy, wait for data.
+ */
+ while (written < n && ret == 0) {
+ if (lirc_buffer_empty(rbuf)) {
+ /*
+ * According to the read(2) man page, 'written' can be
+ * returned as less than 'n', instead of blocking
+ * again, returning -EWOULDBLOCK, or returning
+ * -ERESTARTSYS
+ */
+ if (written)
+ break;
+ if (filep->f_flags & O_NONBLOCK) {
+ ret = -EWOULDBLOCK;
+ break;
+ }
+ if (signal_pending(current)) {
+ ret = -ERESTARTSYS;
+ break;
+ }
+ schedule();
+ set_current_state(TASK_INTERRUPTIBLE);
+ } else {
+ unsigned char buf[MAX_XFER_SIZE];
+
+ if (rbuf->chunk_size > sizeof(buf)) {
+ dev_err(ir->l.dev,
+ "chunk_size is too big (%d)!\n",
+ rbuf->chunk_size);
+ ret = -EINVAL;
+ break;
+ }
+ m = lirc_buffer_read(rbuf, buf);
+ if (m == rbuf->chunk_size) {
+ ret = copy_to_user(outbuf + written, buf,
+ rbuf->chunk_size);
+ written += rbuf->chunk_size;
+ } else {
+ retries++;
+ }
+ if (retries >= 5) {
+ dev_err(ir->l.dev, "Buffer read failed!\n");
+ ret = -EIO;
+ }
+ }
+ }
+
+ remove_wait_queue(&rbuf->wait_poll, &wait);
+ put_ir_rx(rx, false);
+ set_current_state(TASK_RUNNING);
+
+ dev_dbg(ir->l.dev, "read result = %d (%s)\n", ret,
+ ret ? "Error" : "OK");
+
+ return ret ? ret : written;
+}
+
+/* send a keypress to the IR TX device */
+static int send_code(struct IR_tx *tx, unsigned int code, unsigned int key)
+{
+ unsigned char data_block[TX_BLOCK_SIZE];
+ unsigned char buf[2];
+ int i, ret;
+
+ /* Get data for the codeset/key */
+ ret = get_key_data(data_block, code, key);
+
+ if (ret == -EPROTO) {
+ dev_err(tx->ir->l.dev,
+ "failed to get data for code %u, key %u -- check lircd.conf entries\n",
+ code, key);
+ return ret;
+ } else if (ret != 0)
+ return ret;
+
+ /* Send the data block */
+ ret = send_data_block(tx, data_block);
+ if (ret != 0)
+ return ret;
+
+ /* Send data block length? */
+ buf[0] = 0x00;
+ buf[1] = 0x40;
+ ret = i2c_master_send(tx->c, buf, 2);
+ if (ret != 2) {
+ dev_err(tx->ir->l.dev, "i2c_master_send failed with %d\n", ret);
+ return ret < 0 ? ret : -EFAULT;
+ }
+
+ /* Give the z8 a moment to process data block */
+ for (i = 0; i < 10; i++) {
+ ret = i2c_master_send(tx->c, buf, 1);
+ if (ret == 1)
+ break;
+ udelay(100);
+ }
+
+ if (ret != 1) {
+ dev_err(tx->ir->l.dev, "i2c_master_send failed with %d\n", ret);
+ return ret < 0 ? ret : -EFAULT;
+ }
+
+ /* Send finished download? */
+ ret = i2c_master_recv(tx->c, buf, 1);
+ if (ret != 1) {
+ dev_err(tx->ir->l.dev, "i2c_master_recv failed with %d\n", ret);
+ return ret < 0 ? ret : -EFAULT;
+ }
+ if (buf[0] != 0xA0) {
+ dev_err(tx->ir->l.dev, "unexpected IR TX response #1: %02x\n",
+ buf[0]);
+ return -EFAULT;
+ }
+
+ /* Send prepare command? */
+ buf[0] = 0x00;
+ buf[1] = 0x80;
+ ret = i2c_master_send(tx->c, buf, 2);
+ if (ret != 2) {
+ dev_err(tx->ir->l.dev, "i2c_master_send failed with %d\n", ret);
+ return ret < 0 ? ret : -EFAULT;
+ }
+
+ /*
+ * The sleep bits aren't necessary on the HD PVR, and in fact, the
+ * last i2c_master_recv always fails with a -5, so for now, we're
+ * going to skip this whole mess and say we're done on the HD PVR
+ */
+ if (!tx->post_tx_ready_poll) {
+ dev_dbg(tx->ir->l.dev, "sent code %u, key %u\n", code, key);
+ return 0;
+ }
+
+ /*
+ * This bit NAKs until the device is ready, so we retry it
+ * sleeping a bit each time. This seems to be what the windows
+ * driver does, approximately.
+ * Try for up to 1s.
+ */
+ for (i = 0; i < 20; ++i) {
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ schedule_timeout((50 * HZ + 999) / 1000);
+ ret = i2c_master_send(tx->c, buf, 1);
+ if (ret == 1)
+ break;
+ dev_dbg(tx->ir->l.dev,
+ "NAK expected: i2c_master_send failed with %d (try %d)\n",
+ ret, i+1);
+ }
+ if (ret != 1) {
+ dev_err(tx->ir->l.dev,
+ "IR TX chip never got ready: last i2c_master_send failed with %d\n",
+ ret);
+ return ret < 0 ? ret : -EFAULT;
+ }
+
+ /* Seems to be an 'ok' response */
+ i = i2c_master_recv(tx->c, buf, 1);
+ if (i != 1) {
+ dev_err(tx->ir->l.dev, "i2c_master_recv failed with %d\n", ret);
+ return -EFAULT;
+ }
+ if (buf[0] != 0x80) {
+ dev_err(tx->ir->l.dev, "unexpected IR TX response #2: %02x\n",
+ buf[0]);
+ return -EFAULT;
+ }
+
+ /* Oh good, it worked */
+ dev_dbg(tx->ir->l.dev, "sent code %u, key %u\n", code, key);
+ return 0;
+}
+
+/*
+ * Write a code to the device. We take in a 32-bit number (an int) and then
+ * decode this to a codeset/key index. The key data is then decompressed and
+ * sent to the device. We have a spin lock as per i2c documentation to prevent
+ * multiple concurrent sends which would probably cause the device to explode.
+ */
+static ssize_t write(struct file *filep, const char __user *buf, size_t n,
+ loff_t *ppos)
+{
+ struct IR *ir = filep->private_data;
+ struct IR_tx *tx;
+ size_t i;
+ int failures = 0;
+
+ /* Validate user parameters */
+ if (n % sizeof(int))
+ return -EINVAL;
+
+ /* Get a struct IR_tx reference */
+ tx = get_ir_tx(ir);
+ if (tx == NULL)
+ return -ENXIO;
+
+ /* Ensure our tx->c i2c_client remains valid for the duration */
+ mutex_lock(&tx->client_lock);
+ if (tx->c == NULL) {
+ mutex_unlock(&tx->client_lock);
+ put_ir_tx(tx, false);
+ return -ENXIO;
+ }
+
+ /* Lock i2c bus for the duration */
+ mutex_lock(&ir->ir_lock);
+
+ /* Send each keypress */
+ for (i = 0; i < n;) {
+ int ret = 0;
+ int command;
+
+ if (copy_from_user(&command, buf + i, sizeof(command))) {
+ mutex_unlock(&ir->ir_lock);
+ mutex_unlock(&tx->client_lock);
+ put_ir_tx(tx, false);
+ return -EFAULT;
+ }
+
+ /* Send boot data first if required */
+ if (tx->need_boot == 1) {
+ /* Make sure we have the 'firmware' loaded, first */
+ ret = fw_load(tx);
+ if (ret != 0) {
+ mutex_unlock(&ir->ir_lock);
+ mutex_unlock(&tx->client_lock);
+ put_ir_tx(tx, false);
+ if (ret != -ENOMEM)
+ ret = -EIO;
+ return ret;
+ }
+ /* Prep the chip for transmitting codes */
+ ret = send_boot_data(tx);
+ if (ret == 0)
+ tx->need_boot = 0;
+ }
+
+ /* Send the code */
+ if (ret == 0) {
+ ret = send_code(tx, (unsigned)command >> 16,
+ (unsigned)command & 0xFFFF);
+ if (ret == -EPROTO) {
+ mutex_unlock(&ir->ir_lock);
+ mutex_unlock(&tx->client_lock);
+ put_ir_tx(tx, false);
+ return ret;
+ }
+ }
+
+ /*
+ * Hmm, a failure. If we've had a few then give up, otherwise
+ * try a reset
+ */
+ if (ret != 0) {
+ /* Looks like the chip crashed, reset it */
+ dev_err(tx->ir->l.dev,
+ "sending to the IR transmitter chip failed, trying reset\n");
+
+ if (failures >= 3) {
+ dev_err(tx->ir->l.dev,
+ "unable to send to the IR chip after 3 resets, giving up\n");
+ mutex_unlock(&ir->ir_lock);
+ mutex_unlock(&tx->client_lock);
+ put_ir_tx(tx, false);
+ return ret;
+ }
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ schedule_timeout((100 * HZ + 999) / 1000);
+ tx->need_boot = 1;
+ ++failures;
+ } else
+ i += sizeof(int);
+ }
+
+ /* Release i2c bus */
+ mutex_unlock(&ir->ir_lock);
+
+ mutex_unlock(&tx->client_lock);
+
+ /* Give back our struct IR_tx reference */
+ put_ir_tx(tx, false);
+
+ /* All looks good */
+ return n;
+}
+
+/* copied from lirc_dev */
+static unsigned int poll(struct file *filep, poll_table *wait)
+{
+ struct IR *ir = filep->private_data;
+ struct IR_rx *rx;
+ struct lirc_buffer *rbuf = ir->l.rbuf;
+ unsigned int ret;
+
+ dev_dbg(ir->l.dev, "poll called\n");
+
+ rx = get_ir_rx(ir);
+ if (rx == NULL) {
+ /*
+ * Revisit this, if our poll function ever reports writeable
+ * status for Tx
+ */
+ dev_dbg(ir->l.dev, "poll result = POLLERR\n");
+ return POLLERR;
+ }
+
+ /*
+ * Add our lirc_buffer's wait_queue to the poll_table. A wake up on
+ * that buffer's wait queue indicates we may have a new poll status.
+ */
+ poll_wait(filep, &rbuf->wait_poll, wait);
+
+ /* Indicate what ops could happen immediately without blocking */
+ ret = lirc_buffer_empty(rbuf) ? 0 : (POLLIN|POLLRDNORM);
+
+ dev_dbg(ir->l.dev, "poll result = %s\n",
+ ret ? "POLLIN|POLLRDNORM" : "none");
+ return ret;
+}
+
+static long ioctl(struct file *filep, unsigned int cmd, unsigned long arg)
+{
+ struct IR *ir = filep->private_data;
+ unsigned long __user *uptr = (unsigned long __user *)arg;
+ int result;
+ unsigned long mode, features;
+
+ features = ir->l.features;
+
+ switch (cmd) {
+ case LIRC_GET_LENGTH:
+ result = put_user(13UL, uptr);
+ break;
+ case LIRC_GET_FEATURES:
+ result = put_user(features, uptr);
+ break;
+ case LIRC_GET_REC_MODE:
+ if (!(features&LIRC_CAN_REC_MASK))
+ return -ENOSYS;
+
+ result = put_user(LIRC_REC2MODE
+ (features&LIRC_CAN_REC_MASK),
+ uptr);
+ break;
+ case LIRC_SET_REC_MODE:
+ if (!(features&LIRC_CAN_REC_MASK))
+ return -ENOSYS;
+
+ result = get_user(mode, uptr);
+ if (!result && !(LIRC_MODE2REC(mode) & features))
+ result = -EINVAL;
+ break;
+ case LIRC_GET_SEND_MODE:
+ if (!(features&LIRC_CAN_SEND_MASK))
+ return -ENOSYS;
+
+ result = put_user(LIRC_MODE_PULSE, uptr);
+ break;
+ case LIRC_SET_SEND_MODE:
+ if (!(features&LIRC_CAN_SEND_MASK))
+ return -ENOSYS;
+
+ result = get_user(mode, uptr);
+ if (!result && mode != LIRC_MODE_PULSE)
+ return -EINVAL;
+ break;
+ default:
+ return -EINVAL;
+ }
+ return result;
+}
+
+static struct IR *get_ir_device_by_minor(unsigned int minor)
+{
+ struct IR *ir;
+ struct IR *ret = NULL;
+
+ mutex_lock(&ir_devices_lock);
+
+ if (!list_empty(&ir_devices_list)) {
+ list_for_each_entry(ir, &ir_devices_list, list) {
+ if (ir->l.minor == minor) {
+ ret = get_ir_device(ir, true);
+ break;
+ }
+ }
+ }
+
+ mutex_unlock(&ir_devices_lock);
+ return ret;
+}
+
+/*
+ * Open the IR device. Get hold of our IR structure and
+ * stash it in private_data for the file
+ */
+static int open(struct inode *node, struct file *filep)
+{
+ struct IR *ir;
+ unsigned int minor = MINOR(node->i_rdev);
+
+ /* find our IR struct */
+ ir = get_ir_device_by_minor(minor);
+
+ if (ir == NULL)
+ return -ENODEV;
+
+ atomic_inc(&ir->open_count);
+
+ /* stash our IR struct */
+ filep->private_data = ir;
+
+ nonseekable_open(node, filep);
+ return 0;
+}
+
+/* Close the IR device */
+static int close(struct inode *node, struct file *filep)
+{
+ /* find our IR struct */
+ struct IR *ir = filep->private_data;
+
+ if (ir == NULL) {
+ pr_err("ir: close: no private_data attached to the file!\n");
+ return -ENODEV;
+ }
+
+ atomic_dec(&ir->open_count);
+
+ put_ir_device(ir, false);
+ return 0;
+}
+
+static int ir_remove(struct i2c_client *client);
+static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id);
+
+#define ID_FLAG_TX 0x01
+#define ID_FLAG_HDPVR 0x02
+
+static const struct i2c_device_id ir_transceiver_id[] = {
+ { "ir_tx_z8f0811_haup", ID_FLAG_TX },
+ { "ir_rx_z8f0811_haup", 0 },
+ { "ir_tx_z8f0811_hdpvr", ID_FLAG_HDPVR | ID_FLAG_TX },
+ { "ir_rx_z8f0811_hdpvr", ID_FLAG_HDPVR },
+ { }
+};
+
+static struct i2c_driver driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "Zilog/Hauppauge i2c IR",
+ },
+ .probe = ir_probe,
+ .remove = ir_remove,
+ .id_table = ir_transceiver_id,
+};
+
+static const struct file_operations lirc_fops = {
+ .owner = THIS_MODULE,
+ .llseek = no_llseek,
+ .read = read,
+ .write = write,
+ .poll = poll,
+ .unlocked_ioctl = ioctl,
+#ifdef CONFIG_COMPAT
+ .compat_ioctl = ioctl,
+#endif
+ .open = open,
+ .release = close
+};
+
+static struct lirc_driver lirc_template = {
+ .name = "lirc_zilog",
+ .minor = -1,
+ .code_length = 13,
+ .buffer_size = BUFLEN / 2,
+ .sample_rate = 0, /* tell lirc_dev to not start its own kthread */
+ .chunk_size = 2,
+ .set_use_inc = set_use_inc,
+ .set_use_dec = set_use_dec,
+ .fops = &lirc_fops,
+ .owner = THIS_MODULE,
+};
+
+static int ir_remove(struct i2c_client *client)
+{
+ if (strncmp("ir_tx_z8", client->name, 8) == 0) {
+ struct IR_tx *tx = i2c_get_clientdata(client);
+
+ if (tx != NULL) {
+ mutex_lock(&tx->client_lock);
+ tx->c = NULL;
+ mutex_unlock(&tx->client_lock);
+ put_ir_tx(tx, false);
+ }
+ } else if (strncmp("ir_rx_z8", client->name, 8) == 0) {
+ struct IR_rx *rx = i2c_get_clientdata(client);
+
+ if (rx != NULL) {
+ mutex_lock(&rx->client_lock);
+ rx->c = NULL;
+ mutex_unlock(&rx->client_lock);
+ put_ir_rx(rx, false);
+ }
+ }
+ return 0;
+}
+
+
+/* ir_devices_lock must be held */
+static struct IR *get_ir_device_by_adapter(struct i2c_adapter *adapter)
+{
+ struct IR *ir;
+
+ if (list_empty(&ir_devices_list))
+ return NULL;
+
+ list_for_each_entry(ir, &ir_devices_list, list)
+ if (ir->adapter == adapter) {
+ get_ir_device(ir, true);
+ return ir;
+ }
+
+ return NULL;
+}
+
+static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id)
+{
+ struct IR *ir;
+ struct IR_tx *tx;
+ struct IR_rx *rx;
+ struct i2c_adapter *adap = client->adapter;
+ int ret;
+ bool tx_probe = false;
+
+ dev_dbg(&client->dev, "%s: %s on i2c-%d (%s), client addr=0x%02x\n",
+ __func__, id->name, adap->nr, adap->name, client->addr);
+
+ /*
+ * The IR receiver is at i2c address 0x71.
+ * The IR transmitter is at i2c address 0x70.
+ */
+
+ if (id->driver_data & ID_FLAG_TX)
+ tx_probe = true;
+ else if (tx_only) /* module option */
+ return -ENXIO;
+
+ pr_info("probing IR %s on %s (i2c-%d)\n",
+ tx_probe ? "Tx" : "Rx", adap->name, adap->nr);
+
+ mutex_lock(&ir_devices_lock);
+
+ /* Use a single struct IR instance for both the Rx and Tx functions */
+ ir = get_ir_device_by_adapter(adap);
+ if (ir == NULL) {
+ ir = kzalloc(sizeof(struct IR), GFP_KERNEL);
+ if (ir == NULL) {
+ ret = -ENOMEM;
+ goto out_no_ir;
+ }
+ kref_init(&ir->ref);
+
+ /* store for use in ir_probe() again, and open() later on */
+ INIT_LIST_HEAD(&ir->list);
+ list_add_tail(&ir->list, &ir_devices_list);
+
+ ir->adapter = adap;
+ mutex_init(&ir->ir_lock);
+ atomic_set(&ir->open_count, 0);
+ spin_lock_init(&ir->tx_ref_lock);
+ spin_lock_init(&ir->rx_ref_lock);
+
+ /* set lirc_dev stuff */
+ memcpy(&ir->l, &lirc_template, sizeof(struct lirc_driver));
+ /*
+ * FIXME this is a pointer reference to us, but no refcount.
+ *
+ * This OK for now, since lirc_dev currently won't touch this
+ * buffer as we provide our own lirc_fops.
+ *
+ * Currently our own lirc_fops rely on this ir->l.rbuf pointer
+ */
+ ir->l.rbuf = &ir->rbuf;
+ ir->l.dev = &adap->dev;
+ ret = lirc_buffer_init(ir->l.rbuf,
+ ir->l.chunk_size, ir->l.buffer_size);
+ if (ret)
+ goto out_put_ir;
+ }
+
+ if (tx_probe) {
+ /* Get the IR_rx instance for later, if already allocated */
+ rx = get_ir_rx(ir);
+
+ /* Set up a struct IR_tx instance */
+ tx = kzalloc(sizeof(struct IR_tx), GFP_KERNEL);
+ if (tx == NULL) {
+ ret = -ENOMEM;
+ goto out_put_xx;
+ }
+ kref_init(&tx->ref);
+ ir->tx = tx;
+
+ ir->l.features |= LIRC_CAN_SEND_PULSE;
+ mutex_init(&tx->client_lock);
+ tx->c = client;
+ tx->need_boot = 1;
+ tx->post_tx_ready_poll =
+ (id->driver_data & ID_FLAG_HDPVR) ? false : true;
+
+ /* An ir ref goes to the struct IR_tx instance */
+ tx->ir = get_ir_device(ir, true);
+
+ /* A tx ref goes to the i2c_client */
+ i2c_set_clientdata(client, get_ir_tx(ir));
+
+ /*
+ * Load the 'firmware'. We do this before registering with
+ * lirc_dev, so the first firmware load attempt does not happen
+ * after a open() or write() call on the device.
+ *
+ * Failure here is not deemed catastrophic, so the receiver will
+ * still be usable. Firmware load will be retried in write(),
+ * if it is needed.
+ */
+ fw_load(tx);
+
+ /* Proceed only if the Rx client is also ready or not needed */
+ if (rx == NULL && !tx_only) {
+ dev_info(tx->ir->l.dev,
+ "probe of IR Tx on %s (i2c-%d) done. Waiting on IR Rx.\n",
+ adap->name, adap->nr);
+ goto out_ok;
+ }
+ } else {
+ /* Get the IR_tx instance for later, if already allocated */
+ tx = get_ir_tx(ir);
+
+ /* Set up a struct IR_rx instance */
+ rx = kzalloc(sizeof(struct IR_rx), GFP_KERNEL);
+ if (rx == NULL) {
+ ret = -ENOMEM;
+ goto out_put_xx;
+ }
+ kref_init(&rx->ref);
+ ir->rx = rx;
+
+ ir->l.features |= LIRC_CAN_REC_LIRCCODE;
+ mutex_init(&rx->client_lock);
+ rx->c = client;
+ rx->hdpvr_data_fmt =
+ (id->driver_data & ID_FLAG_HDPVR) ? true : false;
+
+ /* An ir ref goes to the struct IR_rx instance */
+ rx->ir = get_ir_device(ir, true);
+
+ /* An rx ref goes to the i2c_client */
+ i2c_set_clientdata(client, get_ir_rx(ir));
+
+ /*
+ * Start the polling thread.
+ * It will only perform an empty loop around schedule_timeout()
+ * until we register with lirc_dev and the first user open()
+ */
+ /* An ir ref goes to the new rx polling kthread */
+ rx->task = kthread_run(lirc_thread, get_ir_device(ir, true),
+ "zilog-rx-i2c-%d", adap->nr);
+ if (IS_ERR(rx->task)) {
+ ret = PTR_ERR(rx->task);
+ dev_err(tx->ir->l.dev,
+ "%s: could not start IR Rx polling thread\n",
+ __func__);
+ /* Failed kthread, so put back the ir ref */
+ put_ir_device(ir, true);
+ /* Failure exit, so put back rx ref from i2c_client */
+ i2c_set_clientdata(client, NULL);
+ put_ir_rx(rx, true);
+ ir->l.features &= ~LIRC_CAN_REC_LIRCCODE;
+ goto out_put_xx;
+ }
+
+ /* Proceed only if the Tx client is also ready */
+ if (tx == NULL) {
+ pr_info("probe of IR Rx on %s (i2c-%d) done. Waiting on IR Tx.\n",
+ adap->name, adap->nr);
+ goto out_ok;
+ }
+ }
+
+ /* register with lirc */
+ ir->l.minor = minor; /* module option: user requested minor number */
+ ir->l.minor = lirc_register_driver(&ir->l);
+ if (ir->l.minor < 0 || ir->l.minor >= MAX_IRCTL_DEVICES) {
+ dev_err(tx->ir->l.dev,
+ "%s: \"minor\" must be between 0 and %d (%d)!\n",
+ __func__, MAX_IRCTL_DEVICES-1, ir->l.minor);
+ ret = -EBADRQC;
+ goto out_put_xx;
+ }
+ dev_info(ir->l.dev,
+ "IR unit on %s (i2c-%d) registered as lirc%d and ready\n",
+ adap->name, adap->nr, ir->l.minor);
+
+out_ok:
+ if (rx != NULL)
+ put_ir_rx(rx, true);
+ if (tx != NULL)
+ put_ir_tx(tx, true);
+ put_ir_device(ir, true);
+ dev_info(ir->l.dev,
+ "probe of IR %s on %s (i2c-%d) done\n",
+ tx_probe ? "Tx" : "Rx", adap->name, adap->nr);
+ mutex_unlock(&ir_devices_lock);
+ return 0;
+
+out_put_xx:
+ if (rx != NULL)
+ put_ir_rx(rx, true);
+ if (tx != NULL)
+ put_ir_tx(tx, true);
+out_put_ir:
+ put_ir_device(ir, true);
+out_no_ir:
+ dev_err(&client->dev,
+ "%s: probing IR %s on %s (i2c-%d) failed with %d\n",
+ __func__, tx_probe ? "Tx" : "Rx", adap->name, adap->nr, ret);
+ mutex_unlock(&ir_devices_lock);
+ return ret;
+}
+
+static int __init zilog_init(void)
+{
+ int ret;
+
+ pr_notice("Zilog/Hauppauge IR driver initializing\n");
+
+ mutex_init(&tx_data_lock);
+
+ request_module("firmware_class");
+
+ ret = i2c_add_driver(&driver);
+ if (ret)
+ pr_err("initialization failed\n");
+ else
+ pr_notice("initialization complete\n");
+
+ return ret;
+}
+
+static void __exit zilog_exit(void)
+{
+ i2c_del_driver(&driver);
+ /* if loaded */
+ fw_unload();
+ pr_notice("Zilog/Hauppauge IR driver unloaded\n");
+}
+
+module_init(zilog_init);
+module_exit(zilog_exit);
+
+MODULE_DESCRIPTION("Zilog/Hauppauge infrared transmitter driver (i2c stack)");
+MODULE_AUTHOR("Gerd Knorr, Michal Kochanowicz, Christoph Bartelmus, "
+ "Ulrich Mueller, Stefan Jahn, Jerome Brock, Mark Weaver, "
+ "Andy Walls");
+MODULE_LICENSE("GPL");
+/* for compat with old name, which isn't all that accurate anymore */
+MODULE_ALIAS("lirc_pvr150");
+
+module_param(minor, int, 0444);
+MODULE_PARM_DESC(minor, "Preferred minor device number");
+
+module_param(debug, bool, 0644);
+MODULE_PARM_DESC(debug, "Enable debugging messages");
+
+module_param(tx_only, bool, 0644);
+MODULE_PARM_DESC(tx_only, "Only handle the IR transmit function");
diff --git a/drivers/staging/media/mn88472/Kconfig b/drivers/staging/media/mn88472/Kconfig
new file mode 100644
index 000000000..a85c90a60
--- /dev/null
+++ b/drivers/staging/media/mn88472/Kconfig
@@ -0,0 +1,7 @@
+config DVB_MN88472
+ tristate "Panasonic MN88472"
+ depends on DVB_CORE && I2C
+ select REGMAP_I2C
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ Say Y when you want to support this frontend.
diff --git a/drivers/staging/media/mn88472/Makefile b/drivers/staging/media/mn88472/Makefile
new file mode 100644
index 000000000..5987b7e6d
--- /dev/null
+++ b/drivers/staging/media/mn88472/Makefile
@@ -0,0 +1,5 @@
+obj-$(CONFIG_DVB_MN88472) += mn88472.o
+
+ccflags-y += -Idrivers/media/dvb-core/
+ccflags-y += -Idrivers/media/dvb-frontends/
+ccflags-y += -Idrivers/media/tuners/
diff --git a/drivers/staging/media/mn88472/TODO b/drivers/staging/media/mn88472/TODO
new file mode 100644
index 000000000..b90a14be3
--- /dev/null
+++ b/drivers/staging/media/mn88472/TODO
@@ -0,0 +1,21 @@
+Driver general quality is not good enough for mainline. Also, other
+device drivers (USB-bridge, tuner) needed for Astrometa receiver in
+question could need some changes. However, if that driver is mainlined
+due to some other device than Astrometa, unrelated TODOs could be
+skipped. In that case rtl28xxu driver needs module parameter to prevent
+driver loading.
+
+Required TODOs:
+* missing lock flags
+* I2C errors
+* tuner sensitivity
+
+*Do not* send any patch fixing checkpatch.pl issues. Currently it passes
+checkpatch.pl tests. I don't want waste my time to review this kind of
+trivial stuff. *Do not* add missing register I/O error checks. Those are
+missing for the reason it is much easier to compare I2C data sniffs when
+there is less lines. Those error checks are about the last thing to be added.
+
+Patches should be submitted to:
+linux-media@vger.kernel.org and Antti Palosaari <crope@iki.fi>
+
diff --git a/drivers/staging/media/mn88472/mn88472.c b/drivers/staging/media/mn88472/mn88472.c
new file mode 100644
index 000000000..b8275c9db
--- /dev/null
+++ b/drivers/staging/media/mn88472/mn88472.c
@@ -0,0 +1,577 @@
+/*
+ * Panasonic MN88472 DVB-T/T2/C demodulator driver
+ *
+ * Copyright (C) 2013 Antti Palosaari <crope@iki.fi>
+ *
+ * This program is free software; you can redistribute it and/or 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.
+ */
+
+#include "mn88472_priv.h"
+
+static int mn88472_get_tune_settings(struct dvb_frontend *fe,
+ struct dvb_frontend_tune_settings *s)
+{
+ s->min_delay_ms = 800;
+ return 0;
+}
+
+static int mn88472_set_frontend(struct dvb_frontend *fe)
+{
+ struct i2c_client *client = fe->demodulator_priv;
+ struct mn88472_dev *dev = i2c_get_clientdata(client);
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ int ret, i;
+ u32 if_frequency = 0;
+ u64 tmp;
+ u8 delivery_system_val, if_val[3], bw_val[7], bw_val2;
+
+ dev_dbg(&client->dev,
+ "delivery_system=%d modulation=%d frequency=%d symbol_rate=%d inversion=%d\n",
+ c->delivery_system, c->modulation,
+ c->frequency, c->symbol_rate, c->inversion);
+
+ if (!dev->warm) {
+ ret = -EAGAIN;
+ goto err;
+ }
+
+ switch (c->delivery_system) {
+ case SYS_DVBT:
+ delivery_system_val = 0x02;
+ break;
+ case SYS_DVBT2:
+ delivery_system_val = 0x03;
+ break;
+ case SYS_DVBC_ANNEX_A:
+ delivery_system_val = 0x04;
+ break;
+ default:
+ ret = -EINVAL;
+ goto err;
+ }
+
+ if (c->bandwidth_hz <= 5000000) {
+ memcpy(bw_val, "\xe5\x99\x9a\x1b\xa9\x1b\xa9", 7);
+ bw_val2 = 0x03;
+ } else if (c->bandwidth_hz <= 6000000) {
+ /* IF 3570000 Hz, BW 6000000 Hz */
+ memcpy(bw_val, "\xbf\x55\x55\x15\x6b\x15\x6b", 7);
+ bw_val2 = 0x02;
+ } else if (c->bandwidth_hz <= 7000000) {
+ /* IF 4570000 Hz, BW 7000000 Hz */
+ memcpy(bw_val, "\xa4\x00\x00\x0f\x2c\x0f\x2c", 7);
+ bw_val2 = 0x01;
+ } else if (c->bandwidth_hz <= 8000000) {
+ /* IF 4570000 Hz, BW 8000000 Hz */
+ memcpy(bw_val, "\x8f\x80\x00\x08\xee\x08\xee", 7);
+ bw_val2 = 0x00;
+ } else {
+ ret = -EINVAL;
+ goto err;
+ }
+
+ /* program tuner */
+ if (fe->ops.tuner_ops.set_params) {
+ ret = fe->ops.tuner_ops.set_params(fe);
+ if (ret)
+ goto err;
+ }
+
+ if (fe->ops.tuner_ops.get_if_frequency) {
+ ret = fe->ops.tuner_ops.get_if_frequency(fe, &if_frequency);
+ if (ret)
+ goto err;
+
+ dev_dbg(&client->dev, "get_if_frequency=%d\n", if_frequency);
+ }
+
+ /* Calculate IF registers ( (1<<24)*IF / Xtal ) */
+ tmp = div_u64(if_frequency * (u64)(1<<24) + (dev->xtal / 2),
+ dev->xtal);
+ if_val[0] = ((tmp >> 16) & 0xff);
+ if_val[1] = ((tmp >> 8) & 0xff);
+ if_val[2] = ((tmp >> 0) & 0xff);
+
+ ret = regmap_write(dev->regmap[2], 0xfb, 0x13);
+ ret = regmap_write(dev->regmap[2], 0xef, 0x13);
+ ret = regmap_write(dev->regmap[2], 0xf9, 0x13);
+ if (ret)
+ goto err;
+
+ ret = regmap_write(dev->regmap[2], 0x00, 0x66);
+ if (ret)
+ goto err;
+ ret = regmap_write(dev->regmap[2], 0x01, 0x00);
+ if (ret)
+ goto err;
+ ret = regmap_write(dev->regmap[2], 0x02, 0x01);
+ if (ret)
+ goto err;
+ ret = regmap_write(dev->regmap[2], 0x03, delivery_system_val);
+ if (ret)
+ goto err;
+ ret = regmap_write(dev->regmap[2], 0x04, bw_val2);
+ if (ret)
+ goto err;
+
+ for (i = 0; i < sizeof(if_val); i++) {
+ ret = regmap_write(dev->regmap[2], 0x10 + i, if_val[i]);
+ if (ret)
+ goto err;
+ }
+
+ for (i = 0; i < sizeof(bw_val); i++) {
+ ret = regmap_write(dev->regmap[2], 0x13 + i, bw_val[i]);
+ if (ret)
+ goto err;
+ }
+
+ switch (c->delivery_system) {
+ case SYS_DVBT:
+ ret = regmap_write(dev->regmap[0], 0x07, 0x26);
+ ret = regmap_write(dev->regmap[0], 0xb0, 0x0a);
+ ret = regmap_write(dev->regmap[0], 0xb4, 0x00);
+ ret = regmap_write(dev->regmap[0], 0xcd, 0x1f);
+ ret = regmap_write(dev->regmap[0], 0xd4, 0x0a);
+ ret = regmap_write(dev->regmap[0], 0xd6, 0x48);
+ ret = regmap_write(dev->regmap[0], 0x00, 0xba);
+ ret = regmap_write(dev->regmap[0], 0x01, 0x13);
+ if (ret)
+ goto err;
+ break;
+ case SYS_DVBT2:
+ ret = regmap_write(dev->regmap[2], 0x2b, 0x13);
+ ret = regmap_write(dev->regmap[2], 0x4f, 0x05);
+ ret = regmap_write(dev->regmap[1], 0xf6, 0x05);
+ ret = regmap_write(dev->regmap[0], 0xb0, 0x0a);
+ ret = regmap_write(dev->regmap[0], 0xb4, 0xf6);
+ ret = regmap_write(dev->regmap[0], 0xcd, 0x01);
+ ret = regmap_write(dev->regmap[0], 0xd4, 0x09);
+ ret = regmap_write(dev->regmap[0], 0xd6, 0x46);
+ ret = regmap_write(dev->regmap[2], 0x30, 0x80);
+ ret = regmap_write(dev->regmap[2], 0x32, 0x00);
+ if (ret)
+ goto err;
+ break;
+ case SYS_DVBC_ANNEX_A:
+ ret = regmap_write(dev->regmap[0], 0xb0, 0x0b);
+ ret = regmap_write(dev->regmap[0], 0xb4, 0x00);
+ ret = regmap_write(dev->regmap[0], 0xcd, 0x17);
+ ret = regmap_write(dev->regmap[0], 0xd4, 0x09);
+ ret = regmap_write(dev->regmap[0], 0xd6, 0x48);
+ ret = regmap_write(dev->regmap[1], 0x00, 0xb0);
+ if (ret)
+ goto err;
+ break;
+ default:
+ ret = -EINVAL;
+ goto err;
+ }
+
+ ret = regmap_write(dev->regmap[0], 0x46, 0x00);
+ ret = regmap_write(dev->regmap[0], 0xae, 0x00);
+
+ switch (dev->ts_mode) {
+ case SERIAL_TS_MODE:
+ ret = regmap_write(dev->regmap[2], 0x08, 0x1d);
+ break;
+ case PARALLEL_TS_MODE:
+ ret = regmap_write(dev->regmap[2], 0x08, 0x00);
+ break;
+ default:
+ dev_dbg(&client->dev, "ts_mode error: %d\n", dev->ts_mode);
+ ret = -EINVAL;
+ goto err;
+ }
+
+ switch (dev->ts_clock) {
+ case VARIABLE_TS_CLOCK:
+ ret = regmap_write(dev->regmap[0], 0xd9, 0xe3);
+ break;
+ case FIXED_TS_CLOCK:
+ ret = regmap_write(dev->regmap[0], 0xd9, 0xe1);
+ break;
+ default:
+ dev_dbg(&client->dev, "ts_clock error: %d\n", dev->ts_clock);
+ ret = -EINVAL;
+ goto err;
+ }
+
+ /* Reset demod */
+ ret = regmap_write(dev->regmap[2], 0xf8, 0x9f);
+ if (ret)
+ goto err;
+
+ dev->delivery_system = c->delivery_system;
+
+ return 0;
+err:
+ dev_dbg(&client->dev, "failed=%d\n", ret);
+ return ret;
+}
+
+static int mn88472_read_status(struct dvb_frontend *fe, fe_status_t *status)
+{
+ struct i2c_client *client = fe->demodulator_priv;
+ struct mn88472_dev *dev = i2c_get_clientdata(client);
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ int ret;
+ unsigned int utmp;
+ int lock = 0;
+
+ *status = 0;
+
+ if (!dev->warm) {
+ ret = -EAGAIN;
+ goto err;
+ }
+
+ switch (c->delivery_system) {
+ case SYS_DVBT:
+ ret = regmap_read(dev->regmap[0], 0x7F, &utmp);
+ if (ret)
+ goto err;
+ if ((utmp & 0xF) >= 0x09)
+ lock = 1;
+ break;
+ case SYS_DVBT2:
+ ret = regmap_read(dev->regmap[2], 0x92, &utmp);
+ if (ret)
+ goto err;
+ if ((utmp & 0xF) >= 0x07)
+ *status |= FE_HAS_SIGNAL;
+ if ((utmp & 0xF) >= 0x0a)
+ *status |= FE_HAS_CARRIER;
+ if ((utmp & 0xF) >= 0x0d)
+ *status |= FE_HAS_VITERBI | FE_HAS_SYNC | FE_HAS_LOCK;
+ break;
+ case SYS_DVBC_ANNEX_A:
+ ret = regmap_read(dev->regmap[1], 0x84, &utmp);
+ if (ret)
+ goto err;
+ if ((utmp & 0xF) >= 0x08)
+ lock = 1;
+ break;
+ default:
+ ret = -EINVAL;
+ goto err;
+ }
+
+ if (lock)
+ *status = FE_HAS_SIGNAL | FE_HAS_CARRIER | FE_HAS_VITERBI |
+ FE_HAS_SYNC | FE_HAS_LOCK;
+
+ return 0;
+err:
+ dev_dbg(&client->dev, "failed=%d\n", ret);
+ return ret;
+}
+
+static int mn88472_init(struct dvb_frontend *fe)
+{
+ struct i2c_client *client = fe->demodulator_priv;
+ struct mn88472_dev *dev = i2c_get_clientdata(client);
+ int ret, len, remaining;
+ const struct firmware *fw = NULL;
+ u8 *fw_file = MN88472_FIRMWARE;
+ unsigned int tmp;
+
+ dev_dbg(&client->dev, "\n");
+
+ /* set cold state by default */
+ dev->warm = false;
+
+ /* power on */
+ ret = regmap_write(dev->regmap[2], 0x05, 0x00);
+ if (ret)
+ goto err;
+
+ ret = regmap_bulk_write(dev->regmap[2], 0x0b, "\x00\x00", 2);
+ if (ret)
+ goto err;
+
+ /* check if firmware is already running */
+ ret = regmap_read(dev->regmap[0], 0xf5, &tmp);
+ if (ret)
+ goto err;
+
+ if (!(tmp & 0x1)) {
+ dev_info(&client->dev, "firmware already running\n");
+ dev->warm = true;
+ return 0;
+ }
+
+ /* request the firmware, this will block and timeout */
+ ret = reject_firmware(&fw, fw_file, &client->dev);
+ if (ret) {
+ dev_err(&client->dev, "firmare file '%s' not found\n",
+ fw_file);
+ goto err;
+ }
+
+ dev_info(&client->dev, "downloading firmware from file '%s'\n",
+ fw_file);
+
+ ret = regmap_write(dev->regmap[0], 0xf5, 0x03);
+ if (ret)
+ goto firmware_release;
+
+ for (remaining = fw->size; remaining > 0;
+ remaining -= (dev->i2c_wr_max - 1)) {
+ len = remaining;
+ if (len > (dev->i2c_wr_max - 1))
+ len = dev->i2c_wr_max - 1;
+
+ ret = regmap_bulk_write(dev->regmap[0], 0xf6,
+ &fw->data[fw->size - remaining], len);
+ if (ret) {
+ dev_err(&client->dev,
+ "firmware download failed=%d\n", ret);
+ goto firmware_release;
+ }
+ }
+
+ /* parity check of firmware */
+ ret = regmap_read(dev->regmap[0], 0xf8, &tmp);
+ if (ret) {
+ dev_err(&client->dev,
+ "parity reg read failed=%d\n", ret);
+ goto err;
+ }
+ if (tmp & 0x10) {
+ dev_err(&client->dev,
+ "firmware parity check failed=0x%x\n", tmp);
+ goto err;
+ }
+ dev_err(&client->dev, "firmware parity check succeeded=0x%x\n", tmp);
+
+ ret = regmap_write(dev->regmap[0], 0xf5, 0x00);
+ if (ret)
+ goto firmware_release;
+
+ release_firmware(fw);
+ fw = NULL;
+
+ /* warm state */
+ dev->warm = true;
+
+ return 0;
+firmware_release:
+ release_firmware(fw);
+err:
+ dev_dbg(&client->dev, "failed=%d\n", ret);
+ return ret;
+}
+
+static int mn88472_sleep(struct dvb_frontend *fe)
+{
+ struct i2c_client *client = fe->demodulator_priv;
+ struct mn88472_dev *dev = i2c_get_clientdata(client);
+ int ret;
+
+ dev_dbg(&client->dev, "\n");
+
+ /* power off */
+ ret = regmap_write(dev->regmap[2], 0x0b, 0x30);
+
+ if (ret)
+ goto err;
+
+ ret = regmap_write(dev->regmap[2], 0x05, 0x3e);
+ if (ret)
+ goto err;
+
+ dev->delivery_system = SYS_UNDEFINED;
+
+ return 0;
+err:
+ dev_dbg(&client->dev, "failed=%d\n", ret);
+ return ret;
+}
+
+static struct dvb_frontend_ops mn88472_ops = {
+ .delsys = {SYS_DVBT, SYS_DVBT2, SYS_DVBC_ANNEX_A},
+ .info = {
+ .name = "Panasonic MN88472",
+ .symbol_rate_min = 1000000,
+ .symbol_rate_max = 7200000,
+ .caps = FE_CAN_FEC_1_2 |
+ FE_CAN_FEC_2_3 |
+ FE_CAN_FEC_3_4 |
+ FE_CAN_FEC_5_6 |
+ FE_CAN_FEC_7_8 |
+ FE_CAN_FEC_AUTO |
+ FE_CAN_QPSK |
+ FE_CAN_QAM_16 |
+ FE_CAN_QAM_32 |
+ FE_CAN_QAM_64 |
+ FE_CAN_QAM_128 |
+ FE_CAN_QAM_256 |
+ FE_CAN_QAM_AUTO |
+ FE_CAN_TRANSMISSION_MODE_AUTO |
+ FE_CAN_GUARD_INTERVAL_AUTO |
+ FE_CAN_HIERARCHY_AUTO |
+ FE_CAN_MUTE_TS |
+ FE_CAN_2G_MODULATION |
+ FE_CAN_MULTISTREAM
+ },
+
+ .get_tune_settings = mn88472_get_tune_settings,
+
+ .init = mn88472_init,
+ .sleep = mn88472_sleep,
+
+ .set_frontend = mn88472_set_frontend,
+
+ .read_status = mn88472_read_status,
+};
+
+static int mn88472_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct mn88472_config *config = client->dev.platform_data;
+ struct mn88472_dev *dev;
+ int ret;
+ unsigned int utmp;
+ static const struct regmap_config regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ };
+
+ dev_dbg(&client->dev, "\n");
+
+ /* Caller really need to provide pointer for frontend we create. */
+ if (config->fe == NULL) {
+ dev_err(&client->dev, "frontend pointer not defined\n");
+ ret = -EINVAL;
+ goto err;
+ }
+
+ dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+ if (dev == NULL) {
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ dev->i2c_wr_max = config->i2c_wr_max;
+ dev->xtal = config->xtal;
+ dev->ts_mode = config->ts_mode;
+ dev->ts_clock = config->ts_clock;
+ dev->client[0] = client;
+ dev->regmap[0] = regmap_init_i2c(dev->client[0], &regmap_config);
+ if (IS_ERR(dev->regmap[0])) {
+ ret = PTR_ERR(dev->regmap[0]);
+ goto err_kfree;
+ }
+
+ /* check demod answers to I2C */
+ ret = regmap_read(dev->regmap[0], 0x00, &utmp);
+ if (ret)
+ goto err_regmap_0_regmap_exit;
+
+ /*
+ * Chip has three I2C addresses for different register pages. Used
+ * addresses are 0x18, 0x1a and 0x1c. We register two dummy clients,
+ * 0x1a and 0x1c, in order to get own I2C client for each register page.
+ */
+ dev->client[1] = i2c_new_dummy(client->adapter, 0x1a);
+ if (dev->client[1] == NULL) {
+ ret = -ENODEV;
+ dev_err(&client->dev, "I2C registration failed\n");
+ if (ret)
+ goto err_regmap_0_regmap_exit;
+ }
+ dev->regmap[1] = regmap_init_i2c(dev->client[1], &regmap_config);
+ if (IS_ERR(dev->regmap[1])) {
+ ret = PTR_ERR(dev->regmap[1]);
+ goto err_client_1_i2c_unregister_device;
+ }
+ i2c_set_clientdata(dev->client[1], dev);
+
+ dev->client[2] = i2c_new_dummy(client->adapter, 0x1c);
+ if (dev->client[2] == NULL) {
+ ret = -ENODEV;
+ dev_err(&client->dev, "2nd I2C registration failed\n");
+ if (ret)
+ goto err_regmap_1_regmap_exit;
+ }
+ dev->regmap[2] = regmap_init_i2c(dev->client[2], &regmap_config);
+ if (IS_ERR(dev->regmap[2])) {
+ ret = PTR_ERR(dev->regmap[2]);
+ goto err_client_2_i2c_unregister_device;
+ }
+ i2c_set_clientdata(dev->client[2], dev);
+
+ /* create dvb_frontend */
+ memcpy(&dev->fe.ops, &mn88472_ops, sizeof(struct dvb_frontend_ops));
+ dev->fe.demodulator_priv = client;
+ *config->fe = &dev->fe;
+ i2c_set_clientdata(client, dev);
+
+ dev_info(&client->dev, "Panasonic MN88472 successfully attached\n");
+ return 0;
+
+err_client_2_i2c_unregister_device:
+ i2c_unregister_device(dev->client[2]);
+err_regmap_1_regmap_exit:
+ regmap_exit(dev->regmap[1]);
+err_client_1_i2c_unregister_device:
+ i2c_unregister_device(dev->client[1]);
+err_regmap_0_regmap_exit:
+ regmap_exit(dev->regmap[0]);
+err_kfree:
+ kfree(dev);
+err:
+ dev_dbg(&client->dev, "failed=%d\n", ret);
+ return ret;
+}
+
+static int mn88472_remove(struct i2c_client *client)
+{
+ struct mn88472_dev *dev = i2c_get_clientdata(client);
+
+ dev_dbg(&client->dev, "\n");
+
+ regmap_exit(dev->regmap[2]);
+ i2c_unregister_device(dev->client[2]);
+
+ regmap_exit(dev->regmap[1]);
+ i2c_unregister_device(dev->client[1]);
+
+ regmap_exit(dev->regmap[0]);
+
+ kfree(dev);
+
+ return 0;
+}
+
+static const struct i2c_device_id mn88472_id_table[] = {
+ {"mn88472", 0},
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, mn88472_id_table);
+
+static struct i2c_driver mn88472_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "mn88472",
+ },
+ .probe = mn88472_probe,
+ .remove = mn88472_remove,
+ .id_table = mn88472_id_table,
+};
+
+module_i2c_driver(mn88472_driver);
+
+MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>");
+MODULE_DESCRIPTION("Panasonic MN88472 DVB-T/T2/C demodulator driver");
+MODULE_LICENSE("GPL");
+/*(DEBLOBBED)*/
diff --git a/drivers/staging/media/mn88472/mn88472_priv.h b/drivers/staging/media/mn88472/mn88472_priv.h
new file mode 100644
index 000000000..b2f14d890
--- /dev/null
+++ b/drivers/staging/media/mn88472/mn88472_priv.h
@@ -0,0 +1,39 @@
+/*
+ * Panasonic MN88472 DVB-T/T2/C demodulator driver
+ *
+ * Copyright (C) 2013 Antti Palosaari <crope@iki.fi>
+ *
+ * This program is free software; you can redistribute it and/or 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.
+ */
+
+#ifndef MN88472_PRIV_H
+#define MN88472_PRIV_H
+
+#include "dvb_frontend.h"
+#include "mn88472.h"
+#include <linux/firmware.h>
+#include <linux/regmap.h>
+
+#define MN88472_FIRMWARE "/*(DEBLOBBED)*/"
+
+struct mn88472_dev {
+ struct i2c_client *client[3];
+ struct regmap *regmap[3];
+ struct dvb_frontend fe;
+ u16 i2c_wr_max;
+ fe_delivery_system_t delivery_system;
+ bool warm; /* FW running */
+ u32 xtal;
+ int ts_mode;
+ int ts_clock;
+};
+
+#endif
diff --git a/drivers/staging/media/mn88473/Kconfig b/drivers/staging/media/mn88473/Kconfig
new file mode 100644
index 000000000..6c9ebf51c
--- /dev/null
+++ b/drivers/staging/media/mn88473/Kconfig
@@ -0,0 +1,7 @@
+config DVB_MN88473
+ tristate "Panasonic MN88473"
+ depends on DVB_CORE && I2C
+ select REGMAP_I2C
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ Say Y when you want to support this frontend.
diff --git a/drivers/staging/media/mn88473/Makefile b/drivers/staging/media/mn88473/Makefile
new file mode 100644
index 000000000..fac55410c
--- /dev/null
+++ b/drivers/staging/media/mn88473/Makefile
@@ -0,0 +1,5 @@
+obj-$(CONFIG_DVB_MN88473) += mn88473.o
+
+ccflags-y += -Idrivers/media/dvb-core/
+ccflags-y += -Idrivers/media/dvb-frontends/
+ccflags-y += -Idrivers/media/tuners/
diff --git a/drivers/staging/media/mn88473/TODO b/drivers/staging/media/mn88473/TODO
new file mode 100644
index 000000000..b90a14be3
--- /dev/null
+++ b/drivers/staging/media/mn88473/TODO
@@ -0,0 +1,21 @@
+Driver general quality is not good enough for mainline. Also, other
+device drivers (USB-bridge, tuner) needed for Astrometa receiver in
+question could need some changes. However, if that driver is mainlined
+due to some other device than Astrometa, unrelated TODOs could be
+skipped. In that case rtl28xxu driver needs module parameter to prevent
+driver loading.
+
+Required TODOs:
+* missing lock flags
+* I2C errors
+* tuner sensitivity
+
+*Do not* send any patch fixing checkpatch.pl issues. Currently it passes
+checkpatch.pl tests. I don't want waste my time to review this kind of
+trivial stuff. *Do not* add missing register I/O error checks. Those are
+missing for the reason it is much easier to compare I2C data sniffs when
+there is less lines. Those error checks are about the last thing to be added.
+
+Patches should be submitted to:
+linux-media@vger.kernel.org and Antti Palosaari <crope@iki.fi>
+
diff --git a/drivers/staging/media/mn88473/mn88473.c b/drivers/staging/media/mn88473/mn88473.c
new file mode 100644
index 000000000..4154ae9fc
--- /dev/null
+++ b/drivers/staging/media/mn88473/mn88473.c
@@ -0,0 +1,523 @@
+/*
+ * Panasonic MN88473 DVB-T/T2/C demodulator driver
+ *
+ * Copyright (C) 2014 Antti Palosaari <crope@iki.fi>
+ *
+ * This program is free software; you can redistribute it and/or 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.
+ */
+
+#include "mn88473_priv.h"
+
+static int mn88473_get_tune_settings(struct dvb_frontend *fe,
+ struct dvb_frontend_tune_settings *s)
+{
+ s->min_delay_ms = 1000;
+ return 0;
+}
+
+static int mn88473_set_frontend(struct dvb_frontend *fe)
+{
+ struct i2c_client *client = fe->demodulator_priv;
+ struct mn88473_dev *dev = i2c_get_clientdata(client);
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ int ret, i;
+ u32 if_frequency;
+ u64 tmp;
+ u8 delivery_system_val, if_val[3], bw_val[7];
+
+ dev_dbg(&client->dev,
+ "delivery_system=%u modulation=%u frequency=%u bandwidth_hz=%u symbol_rate=%u inversion=%d stream_id=%d\n",
+ c->delivery_system,
+ c->modulation,
+ c->frequency,
+ c->bandwidth_hz,
+ c->symbol_rate,
+ c->inversion,
+ c->stream_id);
+
+ if (!dev->warm) {
+ ret = -EAGAIN;
+ goto err;
+ }
+
+ switch (c->delivery_system) {
+ case SYS_DVBT:
+ delivery_system_val = 0x02;
+ break;
+ case SYS_DVBT2:
+ delivery_system_val = 0x03;
+ break;
+ case SYS_DVBC_ANNEX_A:
+ delivery_system_val = 0x04;
+ break;
+ default:
+ ret = -EINVAL;
+ goto err;
+ }
+
+ if (c->bandwidth_hz <= 6000000) {
+ memcpy(bw_val, "\xe9\x55\x55\x1c\x29\x1c\x29", 7);
+ } else if (c->bandwidth_hz <= 7000000) {
+ memcpy(bw_val, "\xc8\x00\x00\x17\x0a\x17\x0a", 7);
+ } else if (c->bandwidth_hz <= 8000000) {
+ memcpy(bw_val, "\xaf\x00\x00\x11\xec\x11\xec", 7);
+ } else {
+ ret = -EINVAL;
+ goto err;
+ }
+
+ /* program tuner */
+ if (fe->ops.tuner_ops.set_params) {
+ ret = fe->ops.tuner_ops.set_params(fe);
+ if (ret)
+ goto err;
+ }
+
+ if (fe->ops.tuner_ops.get_if_frequency) {
+ ret = fe->ops.tuner_ops.get_if_frequency(fe, &if_frequency);
+ if (ret)
+ goto err;
+
+ dev_dbg(&client->dev, "get_if_frequency=%d\n", if_frequency);
+ } else {
+ if_frequency = 0;
+ }
+
+ /* Calculate IF registers ( (1<<24)*IF / Xtal ) */
+ tmp = div_u64(if_frequency * (u64)(1<<24) + (dev->xtal / 2),
+ dev->xtal);
+ if_val[0] = ((tmp >> 16) & 0xff);
+ if_val[1] = ((tmp >> 8) & 0xff);
+ if_val[2] = ((tmp >> 0) & 0xff);
+
+ ret = regmap_write(dev->regmap[2], 0x05, 0x00);
+ ret = regmap_write(dev->regmap[2], 0xfb, 0x13);
+ ret = regmap_write(dev->regmap[2], 0xef, 0x13);
+ ret = regmap_write(dev->regmap[2], 0xf9, 0x13);
+ ret = regmap_write(dev->regmap[2], 0x00, 0x18);
+ ret = regmap_write(dev->regmap[2], 0x01, 0x01);
+ ret = regmap_write(dev->regmap[2], 0x02, 0x21);
+ ret = regmap_write(dev->regmap[2], 0x03, delivery_system_val);
+ ret = regmap_write(dev->regmap[2], 0x0b, 0x00);
+
+ for (i = 0; i < sizeof(if_val); i++) {
+ ret = regmap_write(dev->regmap[2], 0x10 + i, if_val[i]);
+ if (ret)
+ goto err;
+ }
+
+ for (i = 0; i < sizeof(bw_val); i++) {
+ ret = regmap_write(dev->regmap[2], 0x13 + i, bw_val[i]);
+ if (ret)
+ goto err;
+ }
+
+ ret = regmap_write(dev->regmap[2], 0x2d, 0x3b);
+ ret = regmap_write(dev->regmap[2], 0x2e, 0x00);
+ ret = regmap_write(dev->regmap[2], 0x56, 0x0d);
+ ret = regmap_write(dev->regmap[0], 0x01, 0xba);
+ ret = regmap_write(dev->regmap[0], 0x02, 0x13);
+ ret = regmap_write(dev->regmap[0], 0x03, 0x80);
+ ret = regmap_write(dev->regmap[0], 0x04, 0xba);
+ ret = regmap_write(dev->regmap[0], 0x05, 0x91);
+ ret = regmap_write(dev->regmap[0], 0x07, 0xe7);
+ ret = regmap_write(dev->regmap[0], 0x08, 0x28);
+ ret = regmap_write(dev->regmap[0], 0x0a, 0x1a);
+ ret = regmap_write(dev->regmap[0], 0x13, 0x1f);
+ ret = regmap_write(dev->regmap[0], 0x19, 0x03);
+ ret = regmap_write(dev->regmap[0], 0x1d, 0xb0);
+ ret = regmap_write(dev->regmap[0], 0x2a, 0x72);
+ ret = regmap_write(dev->regmap[0], 0x2d, 0x00);
+ ret = regmap_write(dev->regmap[0], 0x3c, 0x00);
+ ret = regmap_write(dev->regmap[0], 0x3f, 0xf8);
+ ret = regmap_write(dev->regmap[0], 0x40, 0xf4);
+ ret = regmap_write(dev->regmap[0], 0x41, 0x08);
+ ret = regmap_write(dev->regmap[0], 0xd2, 0x29);
+ ret = regmap_write(dev->regmap[0], 0xd4, 0x55);
+ ret = regmap_write(dev->regmap[1], 0x10, 0x10);
+ ret = regmap_write(dev->regmap[1], 0x11, 0xab);
+ ret = regmap_write(dev->regmap[1], 0x12, 0x0d);
+ ret = regmap_write(dev->regmap[1], 0x13, 0xae);
+ ret = regmap_write(dev->regmap[1], 0x14, 0x1d);
+ ret = regmap_write(dev->regmap[1], 0x15, 0x9d);
+ ret = regmap_write(dev->regmap[1], 0xbe, 0x08);
+ ret = regmap_write(dev->regmap[2], 0x09, 0x08);
+ ret = regmap_write(dev->regmap[2], 0x08, 0x1d);
+ ret = regmap_write(dev->regmap[0], 0xb2, 0x37);
+ ret = regmap_write(dev->regmap[0], 0xd7, 0x04);
+ ret = regmap_write(dev->regmap[2], 0x32, 0x80);
+ ret = regmap_write(dev->regmap[2], 0x36, 0x00);
+ ret = regmap_write(dev->regmap[2], 0xf8, 0x9f);
+ if (ret)
+ goto err;
+
+ dev->delivery_system = c->delivery_system;
+
+ return 0;
+err:
+ dev_dbg(&client->dev, "failed=%d\n", ret);
+ return ret;
+}
+
+static int mn88473_read_status(struct dvb_frontend *fe, fe_status_t *status)
+{
+ struct i2c_client *client = fe->demodulator_priv;
+ struct mn88473_dev *dev = i2c_get_clientdata(client);
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ int ret;
+ unsigned int utmp;
+ int lock = 0;
+
+ *status = 0;
+
+ if (!dev->warm) {
+ ret = -EAGAIN;
+ goto err;
+ }
+
+ switch (c->delivery_system) {
+ case SYS_DVBT:
+ ret = regmap_read(dev->regmap[0], 0x62, &utmp);
+ if (ret)
+ goto err;
+ if (!(utmp & 0xA0)) {
+ if ((utmp & 0xF) >= 0x03)
+ *status |= FE_HAS_SIGNAL;
+ if ((utmp & 0xF) >= 0x09)
+ lock = 1;
+ }
+ break;
+ case SYS_DVBT2:
+ ret = regmap_read(dev->regmap[2], 0x8B, &utmp);
+ if (ret)
+ goto err;
+ if (!(utmp & 0x40)) {
+ if ((utmp & 0xF) >= 0x07)
+ *status |= FE_HAS_SIGNAL;
+ if ((utmp & 0xF) >= 0x0a)
+ *status |= FE_HAS_CARRIER;
+ if ((utmp & 0xF) >= 0x0d)
+ *status |= FE_HAS_VITERBI | FE_HAS_SYNC | FE_HAS_LOCK;
+ }
+ break;
+ case SYS_DVBC_ANNEX_A:
+ ret = regmap_read(dev->regmap[1], 0x85, &utmp);
+ if (ret)
+ goto err;
+ if (!(utmp & 0x40)) {
+ ret = regmap_read(dev->regmap[1], 0x89, &utmp);
+ if (ret)
+ goto err;
+ if (utmp & 0x01)
+ lock = 1;
+ }
+ break;
+ default:
+ ret = -EINVAL;
+ goto err;
+ }
+
+ if (lock)
+ *status = FE_HAS_SIGNAL | FE_HAS_CARRIER | FE_HAS_VITERBI |
+ FE_HAS_SYNC | FE_HAS_LOCK;
+
+ return 0;
+err:
+ dev_dbg(&client->dev, "failed=%d\n", ret);
+ return ret;
+}
+
+static int mn88473_init(struct dvb_frontend *fe)
+{
+ struct i2c_client *client = fe->demodulator_priv;
+ struct mn88473_dev *dev = i2c_get_clientdata(client);
+ int ret, len, remaining;
+ const struct firmware *fw = NULL;
+ u8 *fw_file = MN88473_FIRMWARE;
+ unsigned int tmp;
+
+ dev_dbg(&client->dev, "\n");
+
+ /* set cold state by default */
+ dev->warm = false;
+
+ /* check if firmware is already running */
+ ret = regmap_read(dev->regmap[0], 0xf5, &tmp);
+ if (ret)
+ goto err;
+
+ if (!(tmp & 0x1)) {
+ dev_info(&client->dev, "firmware already running\n");
+ dev->warm = true;
+ return 0;
+ }
+
+ /* request the firmware, this will block and timeout */
+ ret = reject_firmware(&fw, fw_file, &client->dev);
+ if (ret) {
+ dev_err(&client->dev, "firmare file '%s' not found\n", fw_file);
+ goto err_request_firmware;
+ }
+
+ dev_info(&client->dev, "downloading firmware from file '%s'\n",
+ fw_file);
+
+ ret = regmap_write(dev->regmap[0], 0xf5, 0x03);
+ if (ret)
+ goto err;
+
+ for (remaining = fw->size; remaining > 0;
+ remaining -= (dev->i2c_wr_max - 1)) {
+ len = remaining;
+ if (len > (dev->i2c_wr_max - 1))
+ len = dev->i2c_wr_max - 1;
+
+ ret = regmap_bulk_write(dev->regmap[0], 0xf6,
+ &fw->data[fw->size - remaining], len);
+ if (ret) {
+ dev_err(&client->dev, "firmware download failed=%d\n",
+ ret);
+ goto err;
+ }
+ }
+
+ /* parity check of firmware */
+ ret = regmap_read(dev->regmap[0], 0xf8, &tmp);
+ if (ret) {
+ dev_err(&client->dev,
+ "parity reg read failed=%d\n", ret);
+ goto err;
+ }
+ if (tmp & 0x10) {
+ dev_err(&client->dev,
+ "firmware parity check failed=0x%x\n", tmp);
+ goto err;
+ }
+ dev_err(&client->dev, "firmware parity check succeeded=0x%x\n", tmp);
+
+ ret = regmap_write(dev->regmap[0], 0xf5, 0x00);
+ if (ret)
+ goto err;
+
+ release_firmware(fw);
+ fw = NULL;
+
+ /* warm state */
+ dev->warm = true;
+
+ return 0;
+
+err:
+ release_firmware(fw);
+err_request_firmware:
+ dev_dbg(&client->dev, "failed=%d\n", ret);
+ return ret;
+}
+
+static int mn88473_sleep(struct dvb_frontend *fe)
+{
+ struct i2c_client *client = fe->demodulator_priv;
+ struct mn88473_dev *dev = i2c_get_clientdata(client);
+ int ret;
+
+ dev_dbg(&client->dev, "\n");
+
+ ret = regmap_write(dev->regmap[2], 0x05, 0x3e);
+ if (ret)
+ goto err;
+
+ dev->delivery_system = SYS_UNDEFINED;
+
+ return 0;
+err:
+ dev_dbg(&client->dev, "failed=%d\n", ret);
+ return ret;
+}
+
+static struct dvb_frontend_ops mn88473_ops = {
+ .delsys = {SYS_DVBT, SYS_DVBT2, SYS_DVBC_ANNEX_AC},
+ .info = {
+ .name = "Panasonic MN88473",
+ .symbol_rate_min = 1000000,
+ .symbol_rate_max = 7200000,
+ .caps = FE_CAN_FEC_1_2 |
+ FE_CAN_FEC_2_3 |
+ FE_CAN_FEC_3_4 |
+ FE_CAN_FEC_5_6 |
+ FE_CAN_FEC_7_8 |
+ FE_CAN_FEC_AUTO |
+ FE_CAN_QPSK |
+ FE_CAN_QAM_16 |
+ FE_CAN_QAM_32 |
+ FE_CAN_QAM_64 |
+ FE_CAN_QAM_128 |
+ FE_CAN_QAM_256 |
+ FE_CAN_QAM_AUTO |
+ FE_CAN_TRANSMISSION_MODE_AUTO |
+ FE_CAN_GUARD_INTERVAL_AUTO |
+ FE_CAN_HIERARCHY_AUTO |
+ FE_CAN_MUTE_TS |
+ FE_CAN_2G_MODULATION |
+ FE_CAN_MULTISTREAM
+ },
+
+ .get_tune_settings = mn88473_get_tune_settings,
+
+ .init = mn88473_init,
+ .sleep = mn88473_sleep,
+
+ .set_frontend = mn88473_set_frontend,
+
+ .read_status = mn88473_read_status,
+};
+
+static int mn88473_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct mn88473_config *config = client->dev.platform_data;
+ struct mn88473_dev *dev;
+ int ret;
+ unsigned int utmp;
+ static const struct regmap_config regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ };
+
+ dev_dbg(&client->dev, "\n");
+
+ /* Caller really need to provide pointer for frontend we create. */
+ if (config->fe == NULL) {
+ dev_err(&client->dev, "frontend pointer not defined\n");
+ ret = -EINVAL;
+ goto err;
+ }
+
+ dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+ if (dev == NULL) {
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ dev->i2c_wr_max = config->i2c_wr_max;
+ if (!config->xtal)
+ dev->xtal = 25000000;
+ else
+ dev->xtal = config->xtal;
+ dev->client[0] = client;
+ dev->regmap[0] = regmap_init_i2c(dev->client[0], &regmap_config);
+ if (IS_ERR(dev->regmap[0])) {
+ ret = PTR_ERR(dev->regmap[0]);
+ goto err_kfree;
+ }
+
+ /* check demod answers to I2C */
+ ret = regmap_read(dev->regmap[0], 0x00, &utmp);
+ if (ret)
+ goto err_regmap_0_regmap_exit;
+
+ /*
+ * Chip has three I2C addresses for different register pages. Used
+ * addresses are 0x18, 0x1a and 0x1c. We register two dummy clients,
+ * 0x1a and 0x1c, in order to get own I2C client for each register page.
+ */
+ dev->client[1] = i2c_new_dummy(client->adapter, 0x1a);
+ if (dev->client[1] == NULL) {
+ ret = -ENODEV;
+ dev_err(&client->dev, "I2C registration failed\n");
+ if (ret)
+ goto err_regmap_0_regmap_exit;
+ }
+ dev->regmap[1] = regmap_init_i2c(dev->client[1], &regmap_config);
+ if (IS_ERR(dev->regmap[1])) {
+ ret = PTR_ERR(dev->regmap[1]);
+ goto err_client_1_i2c_unregister_device;
+ }
+ i2c_set_clientdata(dev->client[1], dev);
+
+ dev->client[2] = i2c_new_dummy(client->adapter, 0x1c);
+ if (dev->client[2] == NULL) {
+ ret = -ENODEV;
+ dev_err(&client->dev, "2nd I2C registration failed\n");
+ if (ret)
+ goto err_regmap_1_regmap_exit;
+ }
+ dev->regmap[2] = regmap_init_i2c(dev->client[2], &regmap_config);
+ if (IS_ERR(dev->regmap[2])) {
+ ret = PTR_ERR(dev->regmap[2]);
+ goto err_client_2_i2c_unregister_device;
+ }
+ i2c_set_clientdata(dev->client[2], dev);
+
+ /* create dvb_frontend */
+ memcpy(&dev->fe.ops, &mn88473_ops, sizeof(struct dvb_frontend_ops));
+ dev->fe.demodulator_priv = client;
+ *config->fe = &dev->fe;
+ i2c_set_clientdata(client, dev);
+
+ dev_info(&dev->client[0]->dev, "Panasonic MN88473 successfully attached\n");
+ return 0;
+
+err_client_2_i2c_unregister_device:
+ i2c_unregister_device(dev->client[2]);
+err_regmap_1_regmap_exit:
+ regmap_exit(dev->regmap[1]);
+err_client_1_i2c_unregister_device:
+ i2c_unregister_device(dev->client[1]);
+err_regmap_0_regmap_exit:
+ regmap_exit(dev->regmap[0]);
+err_kfree:
+ kfree(dev);
+err:
+ dev_dbg(&client->dev, "failed=%d\n", ret);
+ return ret;
+}
+
+static int mn88473_remove(struct i2c_client *client)
+{
+ struct mn88473_dev *dev = i2c_get_clientdata(client);
+
+ dev_dbg(&client->dev, "\n");
+
+ regmap_exit(dev->regmap[2]);
+ i2c_unregister_device(dev->client[2]);
+
+ regmap_exit(dev->regmap[1]);
+ i2c_unregister_device(dev->client[1]);
+
+ regmap_exit(dev->regmap[0]);
+
+ kfree(dev);
+
+ return 0;
+}
+
+static const struct i2c_device_id mn88473_id_table[] = {
+ {"mn88473", 0},
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, mn88473_id_table);
+
+static struct i2c_driver mn88473_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "mn88473",
+ },
+ .probe = mn88473_probe,
+ .remove = mn88473_remove,
+ .id_table = mn88473_id_table,
+};
+
+module_i2c_driver(mn88473_driver);
+
+MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>");
+MODULE_DESCRIPTION("Panasonic MN88473 DVB-T/T2/C demodulator driver");
+MODULE_LICENSE("GPL");
+/*(DEBLOBBED)*/
diff --git a/drivers/staging/media/mn88473/mn88473_priv.h b/drivers/staging/media/mn88473/mn88473_priv.h
new file mode 100644
index 000000000..b403bddf3
--- /dev/null
+++ b/drivers/staging/media/mn88473/mn88473_priv.h
@@ -0,0 +1,37 @@
+/*
+ * Panasonic MN88473 DVB-T/T2/C demodulator driver
+ *
+ * Copyright (C) 2014 Antti Palosaari <crope@iki.fi>
+ *
+ * This program is free software; you can redistribute it and/or 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.
+ */
+
+#ifndef MN88473_PRIV_H
+#define MN88473_PRIV_H
+
+#include "dvb_frontend.h"
+#include "mn88473.h"
+#include <linux/firmware.h>
+#include <linux/regmap.h>
+
+#define MN88473_FIRMWARE "/*(DEBLOBBED)*/"
+
+struct mn88473_dev {
+ struct i2c_client *client[3];
+ struct regmap *regmap[3];
+ struct dvb_frontend fe;
+ u16 i2c_wr_max;
+ fe_delivery_system_t delivery_system;
+ bool warm; /* FW running */
+ u32 xtal;
+};
+
+#endif
diff --git a/drivers/staging/media/omap4iss/Kconfig b/drivers/staging/media/omap4iss/Kconfig
new file mode 100644
index 000000000..072dac04a
--- /dev/null
+++ b/drivers/staging/media/omap4iss/Kconfig
@@ -0,0 +1,8 @@
+config VIDEO_OMAP4
+ bool "OMAP 4 Camera support"
+ depends on VIDEO_V4L2=y && VIDEO_V4L2_SUBDEV_API && I2C=y && ARCH_OMAP4
+ depends on HAS_DMA
+ select MFD_SYSCON
+ select VIDEOBUF2_DMA_CONTIG
+ ---help---
+ Driver for an OMAP 4 ISS controller.
diff --git a/drivers/staging/media/omap4iss/Makefile b/drivers/staging/media/omap4iss/Makefile
new file mode 100644
index 000000000..a716ce936
--- /dev/null
+++ b/drivers/staging/media/omap4iss/Makefile
@@ -0,0 +1,6 @@
+# Makefile for OMAP4 ISS driver
+
+omap4-iss-objs += \
+ iss.o iss_csi2.o iss_csiphy.o iss_ipipeif.o iss_ipipe.o iss_resizer.o iss_video.o
+
+obj-$(CONFIG_VIDEO_OMAP4) += omap4-iss.o
diff --git a/drivers/staging/media/omap4iss/TODO b/drivers/staging/media/omap4iss/TODO
new file mode 100644
index 000000000..fcde88860
--- /dev/null
+++ b/drivers/staging/media/omap4iss/TODO
@@ -0,0 +1,4 @@
+* Make the driver compile as a module
+* Fix FIFO/buffer overflows and underflows
+* Replace dummy resizer code with a real implementation
+* Fix checkpatch errors and warnings
diff --git a/drivers/staging/media/omap4iss/iss.c b/drivers/staging/media/omap4iss/iss.c
new file mode 100644
index 000000000..7ced940bd
--- /dev/null
+++ b/drivers/staging/media/omap4iss/iss.c
@@ -0,0 +1,1512 @@
+/*
+ * TI OMAP4 ISS V4L2 Driver
+ *
+ * Copyright (C) 2012, Texas Instruments
+ *
+ * Author: Sergio Aguirre <sergio.a.aguirre@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/dma-mapping.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/sched.h>
+#include <linux/vmalloc.h>
+
+#include <media/v4l2-common.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-ctrls.h>
+
+#include "iss.h"
+#include "iss_regs.h"
+
+#define ISS_PRINT_REGISTER(iss, name)\
+ dev_dbg(iss->dev, "###ISS " #name "=0x%08x\n", \
+ iss_reg_read(iss, OMAP4_ISS_MEM_TOP, ISS_##name))
+
+static void iss_print_status(struct iss_device *iss)
+{
+ dev_dbg(iss->dev, "-------------ISS HL Register dump-------------\n");
+
+ ISS_PRINT_REGISTER(iss, HL_REVISION);
+ ISS_PRINT_REGISTER(iss, HL_SYSCONFIG);
+ ISS_PRINT_REGISTER(iss, HL_IRQSTATUS(5));
+ ISS_PRINT_REGISTER(iss, HL_IRQENABLE_SET(5));
+ ISS_PRINT_REGISTER(iss, HL_IRQENABLE_CLR(5));
+ ISS_PRINT_REGISTER(iss, CTRL);
+ ISS_PRINT_REGISTER(iss, CLKCTRL);
+ ISS_PRINT_REGISTER(iss, CLKSTAT);
+
+ dev_dbg(iss->dev, "-----------------------------------------------\n");
+}
+
+/*
+ * omap4iss_flush - Post pending L3 bus writes by doing a register readback
+ * @iss: OMAP4 ISS device
+ *
+ * In order to force posting of pending writes, we need to write and
+ * readback the same register, in this case the revision register.
+ *
+ * See this link for reference:
+ * http://www.mail-archive.com/linux-omap@vger.kernel.org/msg08149.html
+ */
+void omap4iss_flush(struct iss_device *iss)
+{
+ iss_reg_write(iss, OMAP4_ISS_MEM_TOP, ISS_HL_REVISION, 0);
+ iss_reg_read(iss, OMAP4_ISS_MEM_TOP, ISS_HL_REVISION);
+}
+
+/*
+ * iss_isp_enable_interrupts - Enable ISS ISP interrupts.
+ * @iss: OMAP4 ISS device
+ */
+static void omap4iss_isp_enable_interrupts(struct iss_device *iss)
+{
+ static const u32 isp_irq = ISP5_IRQ_OCP_ERR |
+ ISP5_IRQ_RSZ_FIFO_IN_BLK_ERR |
+ ISP5_IRQ_RSZ_FIFO_OVF |
+ ISP5_IRQ_RSZ_INT_DMA |
+ ISP5_IRQ_ISIF_INT(0);
+
+ /* Enable ISP interrupts */
+ iss_reg_write(iss, OMAP4_ISS_MEM_ISP_SYS1, ISP5_IRQSTATUS(0), isp_irq);
+ iss_reg_write(iss, OMAP4_ISS_MEM_ISP_SYS1, ISP5_IRQENABLE_SET(0),
+ isp_irq);
+}
+
+/*
+ * iss_isp_disable_interrupts - Disable ISS interrupts.
+ * @iss: OMAP4 ISS device
+ */
+static void omap4iss_isp_disable_interrupts(struct iss_device *iss)
+{
+ iss_reg_write(iss, OMAP4_ISS_MEM_ISP_SYS1, ISP5_IRQENABLE_CLR(0), ~0);
+}
+
+/*
+ * iss_enable_interrupts - Enable ISS interrupts.
+ * @iss: OMAP4 ISS device
+ */
+static void iss_enable_interrupts(struct iss_device *iss)
+{
+ static const u32 hl_irq = ISS_HL_IRQ_CSIA | ISS_HL_IRQ_CSIB
+ | ISS_HL_IRQ_ISP(0);
+
+ /* Enable HL interrupts */
+ iss_reg_write(iss, OMAP4_ISS_MEM_TOP, ISS_HL_IRQSTATUS(5), hl_irq);
+ iss_reg_write(iss, OMAP4_ISS_MEM_TOP, ISS_HL_IRQENABLE_SET(5), hl_irq);
+
+ if (iss->regs[OMAP4_ISS_MEM_ISP_SYS1])
+ omap4iss_isp_enable_interrupts(iss);
+}
+
+/*
+ * iss_disable_interrupts - Disable ISS interrupts.
+ * @iss: OMAP4 ISS device
+ */
+static void iss_disable_interrupts(struct iss_device *iss)
+{
+ if (iss->regs[OMAP4_ISS_MEM_ISP_SYS1])
+ omap4iss_isp_disable_interrupts(iss);
+
+ iss_reg_write(iss, OMAP4_ISS_MEM_TOP, ISS_HL_IRQENABLE_CLR(5), ~0);
+}
+
+int omap4iss_get_external_info(struct iss_pipeline *pipe,
+ struct media_link *link)
+{
+ struct iss_device *iss =
+ container_of(pipe, struct iss_video, pipe)->iss;
+ struct v4l2_subdev_format fmt;
+ struct v4l2_ctrl *ctrl;
+ int ret;
+
+ if (!pipe->external)
+ return 0;
+
+ if (pipe->external_rate)
+ return 0;
+
+ memset(&fmt, 0, sizeof(fmt));
+
+ fmt.pad = link->source->index;
+ fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE;
+ ret = v4l2_subdev_call(media_entity_to_v4l2_subdev(link->sink->entity),
+ pad, get_fmt, NULL, &fmt);
+ if (ret < 0)
+ return -EPIPE;
+
+ pipe->external_bpp = omap4iss_video_format_info(fmt.format.code)->bpp;
+
+ ctrl = v4l2_ctrl_find(pipe->external->ctrl_handler,
+ V4L2_CID_PIXEL_RATE);
+ if (ctrl == NULL) {
+ dev_warn(iss->dev, "no pixel rate control in subdev %s\n",
+ pipe->external->name);
+ return -EPIPE;
+ }
+
+ pipe->external_rate = v4l2_ctrl_g_ctrl_int64(ctrl);
+
+ return 0;
+}
+
+/*
+ * Configure the bridge. Valid inputs are
+ *
+ * IPIPEIF_INPUT_CSI2A: CSI2a receiver
+ * IPIPEIF_INPUT_CSI2B: CSI2b receiver
+ *
+ * The bridge and lane shifter are configured according to the selected input
+ * and the ISP platform data.
+ */
+void omap4iss_configure_bridge(struct iss_device *iss,
+ enum ipipeif_input_entity input)
+{
+ u32 issctrl_val;
+ u32 isp5ctrl_val;
+
+ issctrl_val = iss_reg_read(iss, OMAP4_ISS_MEM_TOP, ISS_CTRL);
+ issctrl_val &= ~ISS_CTRL_INPUT_SEL_MASK;
+ issctrl_val &= ~ISS_CTRL_CLK_DIV_MASK;
+
+ isp5ctrl_val = iss_reg_read(iss, OMAP4_ISS_MEM_ISP_SYS1, ISP5_CTRL);
+
+ switch (input) {
+ case IPIPEIF_INPUT_CSI2A:
+ issctrl_val |= ISS_CTRL_INPUT_SEL_CSI2A;
+ break;
+
+ case IPIPEIF_INPUT_CSI2B:
+ issctrl_val |= ISS_CTRL_INPUT_SEL_CSI2B;
+ break;
+
+ default:
+ return;
+ }
+
+ issctrl_val |= ISS_CTRL_SYNC_DETECT_VS_RAISING;
+
+ isp5ctrl_val |= ISP5_CTRL_VD_PULSE_EXT | ISP5_CTRL_PSYNC_CLK_SEL |
+ ISP5_CTRL_SYNC_ENABLE;
+
+ iss_reg_write(iss, OMAP4_ISS_MEM_TOP, ISS_CTRL, issctrl_val);
+ iss_reg_write(iss, OMAP4_ISS_MEM_ISP_SYS1, ISP5_CTRL, isp5ctrl_val);
+}
+
+#ifdef ISS_ISR_DEBUG
+static void iss_isr_dbg(struct iss_device *iss, u32 irqstatus)
+{
+ static const char * const name[] = {
+ "ISP_0",
+ "ISP_1",
+ "ISP_2",
+ "ISP_3",
+ "CSIA",
+ "CSIB",
+ "CCP2_0",
+ "CCP2_1",
+ "CCP2_2",
+ "CCP2_3",
+ "CBUFF",
+ "BTE",
+ "SIMCOP_0",
+ "SIMCOP_1",
+ "SIMCOP_2",
+ "SIMCOP_3",
+ "CCP2_8",
+ "HS_VS",
+ "18",
+ "19",
+ "20",
+ "21",
+ "22",
+ "23",
+ "24",
+ "25",
+ "26",
+ "27",
+ "28",
+ "29",
+ "30",
+ "31",
+ };
+ unsigned int i;
+
+ dev_dbg(iss->dev, "ISS IRQ: ");
+
+ for (i = 0; i < ARRAY_SIZE(name); i++) {
+ if ((1 << i) & irqstatus)
+ pr_cont("%s ", name[i]);
+ }
+ pr_cont("\n");
+}
+
+static void iss_isp_isr_dbg(struct iss_device *iss, u32 irqstatus)
+{
+ static const char * const name[] = {
+ "ISIF_0",
+ "ISIF_1",
+ "ISIF_2",
+ "ISIF_3",
+ "IPIPEREQ",
+ "IPIPELAST_PIX",
+ "IPIPEDMA",
+ "IPIPEBSC",
+ "IPIPEHST",
+ "IPIPEIF",
+ "AEW",
+ "AF",
+ "H3A",
+ "RSZ_REG",
+ "RSZ_LAST_PIX",
+ "RSZ_DMA",
+ "RSZ_CYC_RZA",
+ "RSZ_CYC_RZB",
+ "RSZ_FIFO_OVF",
+ "RSZ_FIFO_IN_BLK_ERR",
+ "20",
+ "21",
+ "RSZ_EOF0",
+ "RSZ_EOF1",
+ "H3A_EOF",
+ "IPIPE_EOF",
+ "26",
+ "IPIPE_DPC_INI",
+ "IPIPE_DPC_RNEW0",
+ "IPIPE_DPC_RNEW1",
+ "30",
+ "OCP_ERR",
+ };
+ unsigned int i;
+
+ dev_dbg(iss->dev, "ISP IRQ: ");
+
+ for (i = 0; i < ARRAY_SIZE(name); i++) {
+ if ((1 << i) & irqstatus)
+ pr_cont("%s ", name[i]);
+ }
+ pr_cont("\n");
+}
+#endif
+
+/*
+ * iss_isr - Interrupt Service Routine for ISS module.
+ * @irq: Not used currently.
+ * @_iss: Pointer to the OMAP4 ISS device
+ *
+ * Handles the corresponding callback if plugged in.
+ *
+ * Returns IRQ_HANDLED when IRQ was correctly handled, or IRQ_NONE when the
+ * IRQ wasn't handled.
+ */
+static irqreturn_t iss_isr(int irq, void *_iss)
+{
+ static const u32 ipipeif_events = ISP5_IRQ_IPIPEIF_IRQ |
+ ISP5_IRQ_ISIF_INT(0);
+ static const u32 resizer_events = ISP5_IRQ_RSZ_FIFO_IN_BLK_ERR |
+ ISP5_IRQ_RSZ_FIFO_OVF |
+ ISP5_IRQ_RSZ_INT_DMA;
+ struct iss_device *iss = _iss;
+ u32 irqstatus;
+
+ irqstatus = iss_reg_read(iss, OMAP4_ISS_MEM_TOP, ISS_HL_IRQSTATUS(5));
+ iss_reg_write(iss, OMAP4_ISS_MEM_TOP, ISS_HL_IRQSTATUS(5), irqstatus);
+
+ if (irqstatus & ISS_HL_IRQ_CSIA)
+ omap4iss_csi2_isr(&iss->csi2a);
+
+ if (irqstatus & ISS_HL_IRQ_CSIB)
+ omap4iss_csi2_isr(&iss->csi2b);
+
+ if (irqstatus & ISS_HL_IRQ_ISP(0)) {
+ u32 isp_irqstatus = iss_reg_read(iss, OMAP4_ISS_MEM_ISP_SYS1,
+ ISP5_IRQSTATUS(0));
+ iss_reg_write(iss, OMAP4_ISS_MEM_ISP_SYS1, ISP5_IRQSTATUS(0),
+ isp_irqstatus);
+
+ if (isp_irqstatus & ISP5_IRQ_OCP_ERR)
+ dev_dbg(iss->dev, "ISP5 OCP Error!\n");
+
+ if (isp_irqstatus & ipipeif_events) {
+ omap4iss_ipipeif_isr(&iss->ipipeif,
+ isp_irqstatus & ipipeif_events);
+ }
+
+ if (isp_irqstatus & resizer_events)
+ omap4iss_resizer_isr(&iss->resizer,
+ isp_irqstatus & resizer_events);
+
+#ifdef ISS_ISR_DEBUG
+ iss_isp_isr_dbg(iss, isp_irqstatus);
+#endif
+ }
+
+ omap4iss_flush(iss);
+
+#ifdef ISS_ISR_DEBUG
+ iss_isr_dbg(iss, irqstatus);
+#endif
+
+ return IRQ_HANDLED;
+}
+
+/* -----------------------------------------------------------------------------
+ * Pipeline power management
+ *
+ * Entities must be powered up when part of a pipeline that contains at least
+ * one open video device node.
+ *
+ * To achieve this use the entity use_count field to track the number of users.
+ * For entities corresponding to video device nodes the use_count field stores
+ * the users count of the node. For entities corresponding to subdevs the
+ * use_count field stores the total number of users of all video device nodes
+ * in the pipeline.
+ *
+ * The omap4iss_pipeline_pm_use() function must be called in the open() and
+ * close() handlers of video device nodes. It increments or decrements the use
+ * count of all subdev entities in the pipeline.
+ *
+ * To react to link management on powered pipelines, the link setup notification
+ * callback updates the use count of all entities in the source and sink sides
+ * of the link.
+ */
+
+/*
+ * iss_pipeline_pm_use_count - Count the number of users of a pipeline
+ * @entity: The entity
+ *
+ * Return the total number of users of all video device nodes in the pipeline.
+ */
+static int iss_pipeline_pm_use_count(struct media_entity *entity)
+{
+ struct media_entity_graph graph;
+ int use = 0;
+
+ media_entity_graph_walk_start(&graph, entity);
+
+ while ((entity = media_entity_graph_walk_next(&graph))) {
+ if (media_entity_type(entity) == MEDIA_ENT_T_DEVNODE)
+ use += entity->use_count;
+ }
+
+ return use;
+}
+
+/*
+ * iss_pipeline_pm_power_one - Apply power change to an entity
+ * @entity: The entity
+ * @change: Use count change
+ *
+ * Change the entity use count by @change. If the entity is a subdev update its
+ * power state by calling the core::s_power operation when the use count goes
+ * from 0 to != 0 or from != 0 to 0.
+ *
+ * Return 0 on success or a negative error code on failure.
+ */
+static int iss_pipeline_pm_power_one(struct media_entity *entity, int change)
+{
+ struct v4l2_subdev *subdev;
+
+ subdev = media_entity_type(entity) == MEDIA_ENT_T_V4L2_SUBDEV
+ ? media_entity_to_v4l2_subdev(entity) : NULL;
+
+ if (entity->use_count == 0 && change > 0 && subdev != NULL) {
+ int ret;
+
+ ret = v4l2_subdev_call(subdev, core, s_power, 1);
+ if (ret < 0 && ret != -ENOIOCTLCMD)
+ return ret;
+ }
+
+ entity->use_count += change;
+ WARN_ON(entity->use_count < 0);
+
+ if (entity->use_count == 0 && change < 0 && subdev != NULL)
+ v4l2_subdev_call(subdev, core, s_power, 0);
+
+ return 0;
+}
+
+/*
+ * iss_pipeline_pm_power - Apply power change to all entities in a pipeline
+ * @entity: The entity
+ * @change: Use count change
+ *
+ * Walk the pipeline to update the use count and the power state of all non-node
+ * entities.
+ *
+ * Return 0 on success or a negative error code on failure.
+ */
+static int iss_pipeline_pm_power(struct media_entity *entity, int change)
+{
+ struct media_entity_graph graph;
+ struct media_entity *first = entity;
+ int ret = 0;
+
+ if (!change)
+ return 0;
+
+ media_entity_graph_walk_start(&graph, entity);
+
+ while (!ret && (entity = media_entity_graph_walk_next(&graph)))
+ if (media_entity_type(entity) != MEDIA_ENT_T_DEVNODE)
+ ret = iss_pipeline_pm_power_one(entity, change);
+
+ if (!ret)
+ return 0;
+
+ media_entity_graph_walk_start(&graph, first);
+
+ while ((first = media_entity_graph_walk_next(&graph))
+ && first != entity)
+ if (media_entity_type(first) != MEDIA_ENT_T_DEVNODE)
+ iss_pipeline_pm_power_one(first, -change);
+
+ return ret;
+}
+
+/*
+ * omap4iss_pipeline_pm_use - Update the use count of an entity
+ * @entity: The entity
+ * @use: Use (1) or stop using (0) the entity
+ *
+ * Update the use count of all entities in the pipeline and power entities on or
+ * off accordingly.
+ *
+ * Return 0 on success or a negative error code on failure. Powering entities
+ * off is assumed to never fail. No failure can occur when the use parameter is
+ * set to 0.
+ */
+int omap4iss_pipeline_pm_use(struct media_entity *entity, int use)
+{
+ int change = use ? 1 : -1;
+ int ret;
+
+ mutex_lock(&entity->parent->graph_mutex);
+
+ /* Apply use count to node. */
+ entity->use_count += change;
+ WARN_ON(entity->use_count < 0);
+
+ /* Apply power change to connected non-nodes. */
+ ret = iss_pipeline_pm_power(entity, change);
+ if (ret < 0)
+ entity->use_count -= change;
+
+ mutex_unlock(&entity->parent->graph_mutex);
+
+ return ret;
+}
+
+/*
+ * iss_pipeline_link_notify - Link management notification callback
+ * @link: The link
+ * @flags: New link flags that will be applied
+ *
+ * React to link management on powered pipelines by updating the use count of
+ * all entities in the source and sink sides of the link. Entities are powered
+ * on or off accordingly.
+ *
+ * Return 0 on success or a negative error code on failure. Powering entities
+ * off is assumed to never fail. This function will not fail for disconnection
+ * events.
+ */
+static int iss_pipeline_link_notify(struct media_link *link, u32 flags,
+ unsigned int notification)
+{
+ struct media_entity *source = link->source->entity;
+ struct media_entity *sink = link->sink->entity;
+ int source_use = iss_pipeline_pm_use_count(source);
+ int sink_use = iss_pipeline_pm_use_count(sink);
+ int ret;
+
+ if (notification == MEDIA_DEV_NOTIFY_POST_LINK_CH &&
+ !(link->flags & MEDIA_LNK_FL_ENABLED)) {
+ /* Powering off entities is assumed to never fail. */
+ iss_pipeline_pm_power(source, -sink_use);
+ iss_pipeline_pm_power(sink, -source_use);
+ return 0;
+ }
+
+ if (notification == MEDIA_DEV_NOTIFY_POST_LINK_CH &&
+ (flags & MEDIA_LNK_FL_ENABLED)) {
+ ret = iss_pipeline_pm_power(source, sink_use);
+ if (ret < 0)
+ return ret;
+
+ ret = iss_pipeline_pm_power(sink, source_use);
+ if (ret < 0)
+ iss_pipeline_pm_power(source, -sink_use);
+
+ return ret;
+ }
+
+ return 0;
+}
+
+/* -----------------------------------------------------------------------------
+ * Pipeline stream management
+ */
+
+/*
+ * iss_pipeline_disable - Disable streaming on a pipeline
+ * @pipe: ISS pipeline
+ * @until: entity at which to stop pipeline walk
+ *
+ * Walk the entities chain starting at the pipeline output video node and stop
+ * all modules in the chain. Wait synchronously for the modules to be stopped if
+ * necessary.
+ *
+ * If the until argument isn't NULL, stop the pipeline walk when reaching the
+ * until entity. This is used to disable a partially started pipeline due to a
+ * subdev start error.
+ */
+static int iss_pipeline_disable(struct iss_pipeline *pipe,
+ struct media_entity *until)
+{
+ struct iss_device *iss = pipe->output->iss;
+ struct media_entity *entity;
+ struct media_pad *pad;
+ struct v4l2_subdev *subdev;
+ int failure = 0;
+ int ret;
+
+ entity = &pipe->output->video.entity;
+ while (1) {
+ pad = &entity->pads[0];
+ if (!(pad->flags & MEDIA_PAD_FL_SINK))
+ break;
+
+ pad = media_entity_remote_pad(pad);
+ if (pad == NULL ||
+ media_entity_type(pad->entity) != MEDIA_ENT_T_V4L2_SUBDEV)
+ break;
+
+ entity = pad->entity;
+ if (entity == until)
+ break;
+
+ subdev = media_entity_to_v4l2_subdev(entity);
+ ret = v4l2_subdev_call(subdev, video, s_stream, 0);
+ if (ret < 0) {
+ dev_dbg(iss->dev, "%s: module stop timeout.\n",
+ subdev->name);
+ /* If the entity failed to stopped, assume it has
+ * crashed. Mark it as such, the ISS will be reset when
+ * applications will release it.
+ */
+ iss->crashed |= 1U << subdev->entity.id;
+ failure = -ETIMEDOUT;
+ }
+ }
+
+ return failure;
+}
+
+/*
+ * iss_pipeline_enable - Enable streaming on a pipeline
+ * @pipe: ISS pipeline
+ * @mode: Stream mode (single shot or continuous)
+ *
+ * Walk the entities chain starting at the pipeline output video node and start
+ * all modules in the chain in the given mode.
+ *
+ * Return 0 if successful, or the return value of the failed video::s_stream
+ * operation otherwise.
+ */
+static int iss_pipeline_enable(struct iss_pipeline *pipe,
+ enum iss_pipeline_stream_state mode)
+{
+ struct iss_device *iss = pipe->output->iss;
+ struct media_entity *entity;
+ struct media_pad *pad;
+ struct v4l2_subdev *subdev;
+ unsigned long flags;
+ int ret;
+
+ /* If one of the entities in the pipeline has crashed it will not work
+ * properly. Refuse to start streaming in that case. This check must be
+ * performed before the loop below to avoid starting entities if the
+ * pipeline won't start anyway (those entities would then likely fail to
+ * stop, making the problem worse).
+ */
+ if (pipe->entities & iss->crashed)
+ return -EIO;
+
+ spin_lock_irqsave(&pipe->lock, flags);
+ pipe->state &= ~(ISS_PIPELINE_IDLE_INPUT | ISS_PIPELINE_IDLE_OUTPUT);
+ spin_unlock_irqrestore(&pipe->lock, flags);
+
+ pipe->do_propagation = false;
+
+ entity = &pipe->output->video.entity;
+ while (1) {
+ pad = &entity->pads[0];
+ if (!(pad->flags & MEDIA_PAD_FL_SINK))
+ break;
+
+ pad = media_entity_remote_pad(pad);
+ if (pad == NULL ||
+ media_entity_type(pad->entity) != MEDIA_ENT_T_V4L2_SUBDEV)
+ break;
+
+ entity = pad->entity;
+ subdev = media_entity_to_v4l2_subdev(entity);
+
+ ret = v4l2_subdev_call(subdev, video, s_stream, mode);
+ if (ret < 0 && ret != -ENOIOCTLCMD) {
+ iss_pipeline_disable(pipe, entity);
+ return ret;
+ }
+
+ if (subdev == &iss->csi2a.subdev ||
+ subdev == &iss->csi2b.subdev)
+ pipe->do_propagation = true;
+ }
+
+ iss_print_status(pipe->output->iss);
+ return 0;
+}
+
+/*
+ * omap4iss_pipeline_set_stream - Enable/disable streaming on a pipeline
+ * @pipe: ISS pipeline
+ * @state: Stream state (stopped, single shot or continuous)
+ *
+ * Set the pipeline to the given stream state. Pipelines can be started in
+ * single-shot or continuous mode.
+ *
+ * Return 0 if successful, or the return value of the failed video::s_stream
+ * operation otherwise. The pipeline state is not updated when the operation
+ * fails, except when stopping the pipeline.
+ */
+int omap4iss_pipeline_set_stream(struct iss_pipeline *pipe,
+ enum iss_pipeline_stream_state state)
+{
+ int ret;
+
+ if (state == ISS_PIPELINE_STREAM_STOPPED)
+ ret = iss_pipeline_disable(pipe, NULL);
+ else
+ ret = iss_pipeline_enable(pipe, state);
+
+ if (ret == 0 || state == ISS_PIPELINE_STREAM_STOPPED)
+ pipe->stream_state = state;
+
+ return ret;
+}
+
+/*
+ * omap4iss_pipeline_cancel_stream - Cancel stream on a pipeline
+ * @pipe: ISS pipeline
+ *
+ * Cancelling a stream mark all buffers on all video nodes in the pipeline as
+ * erroneous and makes sure no new buffer can be queued. This function is called
+ * when a fatal error that prevents any further operation on the pipeline
+ * occurs.
+ */
+void omap4iss_pipeline_cancel_stream(struct iss_pipeline *pipe)
+{
+ if (pipe->input)
+ omap4iss_video_cancel_stream(pipe->input);
+ if (pipe->output)
+ omap4iss_video_cancel_stream(pipe->output);
+}
+
+/*
+ * iss_pipeline_is_last - Verify if entity has an enabled link to the output
+ * video node
+ * @me: ISS module's media entity
+ *
+ * Returns 1 if the entity has an enabled link to the output video node or 0
+ * otherwise. It's true only while pipeline can have no more than one output
+ * node.
+ */
+static int iss_pipeline_is_last(struct media_entity *me)
+{
+ struct iss_pipeline *pipe;
+ struct media_pad *pad;
+
+ if (!me->pipe)
+ return 0;
+ pipe = to_iss_pipeline(me);
+ if (pipe->stream_state == ISS_PIPELINE_STREAM_STOPPED)
+ return 0;
+ pad = media_entity_remote_pad(&pipe->output->pad);
+ return pad->entity == me;
+}
+
+static int iss_reset(struct iss_device *iss)
+{
+ unsigned int timeout;
+
+ iss_reg_set(iss, OMAP4_ISS_MEM_TOP, ISS_HL_SYSCONFIG,
+ ISS_HL_SYSCONFIG_SOFTRESET);
+
+ timeout = iss_poll_condition_timeout(
+ !(iss_reg_read(iss, OMAP4_ISS_MEM_TOP, ISS_HL_SYSCONFIG) &
+ ISS_HL_SYSCONFIG_SOFTRESET), 1000, 10, 100);
+ if (timeout) {
+ dev_err(iss->dev, "ISS reset timeout\n");
+ return -ETIMEDOUT;
+ }
+
+ iss->crashed = 0;
+ return 0;
+}
+
+static int iss_isp_reset(struct iss_device *iss)
+{
+ unsigned int timeout;
+
+ /* Fist, ensure that the ISP is IDLE (no transactions happening) */
+ iss_reg_update(iss, OMAP4_ISS_MEM_ISP_SYS1, ISP5_SYSCONFIG,
+ ISP5_SYSCONFIG_STANDBYMODE_MASK,
+ ISP5_SYSCONFIG_STANDBYMODE_SMART);
+
+ iss_reg_set(iss, OMAP4_ISS_MEM_ISP_SYS1, ISP5_CTRL, ISP5_CTRL_MSTANDBY);
+
+ timeout = iss_poll_condition_timeout(
+ iss_reg_read(iss, OMAP4_ISS_MEM_ISP_SYS1, ISP5_CTRL) &
+ ISP5_CTRL_MSTANDBY_WAIT, 1000000, 1000, 1500);
+ if (timeout) {
+ dev_err(iss->dev, "ISP5 standby timeout\n");
+ return -ETIMEDOUT;
+ }
+
+ /* Now finally, do the reset */
+ iss_reg_set(iss, OMAP4_ISS_MEM_ISP_SYS1, ISP5_SYSCONFIG,
+ ISP5_SYSCONFIG_SOFTRESET);
+
+ timeout = iss_poll_condition_timeout(
+ !(iss_reg_read(iss, OMAP4_ISS_MEM_ISP_SYS1, ISP5_SYSCONFIG) &
+ ISP5_SYSCONFIG_SOFTRESET), 1000000, 1000, 1500);
+ if (timeout) {
+ dev_err(iss->dev, "ISP5 reset timeout\n");
+ return -ETIMEDOUT;
+ }
+
+ return 0;
+}
+
+/*
+ * iss_module_sync_idle - Helper to sync module with its idle state
+ * @me: ISS submodule's media entity
+ * @wait: ISS submodule's wait queue for streamoff/interrupt synchronization
+ * @stopping: flag which tells module wants to stop
+ *
+ * This function checks if ISS submodule needs to wait for next interrupt. If
+ * yes, makes the caller to sleep while waiting for such event.
+ */
+int omap4iss_module_sync_idle(struct media_entity *me, wait_queue_head_t *wait,
+ atomic_t *stopping)
+{
+ struct iss_pipeline *pipe = to_iss_pipeline(me);
+ struct iss_video *video = pipe->output;
+ unsigned long flags;
+
+ if (pipe->stream_state == ISS_PIPELINE_STREAM_STOPPED ||
+ (pipe->stream_state == ISS_PIPELINE_STREAM_SINGLESHOT &&
+ !iss_pipeline_ready(pipe)))
+ return 0;
+
+ /*
+ * atomic_set() doesn't include memory barrier on ARM platform for SMP
+ * scenario. We'll call it here to avoid race conditions.
+ */
+ atomic_set(stopping, 1);
+ smp_wmb();
+
+ /*
+ * If module is the last one, it's writing to memory. In this case,
+ * it's necessary to check if the module is already paused due to
+ * DMA queue underrun or if it has to wait for next interrupt to be
+ * idle.
+ * If it isn't the last one, the function won't sleep but *stopping
+ * will still be set to warn next submodule caller's interrupt the
+ * module wants to be idle.
+ */
+ if (!iss_pipeline_is_last(me))
+ return 0;
+
+ spin_lock_irqsave(&video->qlock, flags);
+ if (video->dmaqueue_flags & ISS_VIDEO_DMAQUEUE_UNDERRUN) {
+ spin_unlock_irqrestore(&video->qlock, flags);
+ atomic_set(stopping, 0);
+ smp_wmb();
+ return 0;
+ }
+ spin_unlock_irqrestore(&video->qlock, flags);
+ if (!wait_event_timeout(*wait, !atomic_read(stopping),
+ msecs_to_jiffies(1000))) {
+ atomic_set(stopping, 0);
+ smp_wmb();
+ return -ETIMEDOUT;
+ }
+
+ return 0;
+}
+
+/*
+ * omap4iss_module_sync_is_stopped - Helper to verify if module was stopping
+ * @wait: ISS submodule's wait queue for streamoff/interrupt synchronization
+ * @stopping: flag which tells module wants to stop
+ *
+ * This function checks if ISS submodule was stopping. In case of yes, it
+ * notices the caller by setting stopping to 0 and waking up the wait queue.
+ * Returns 1 if it was stopping or 0 otherwise.
+ */
+int omap4iss_module_sync_is_stopping(wait_queue_head_t *wait,
+ atomic_t *stopping)
+{
+ if (atomic_cmpxchg(stopping, 1, 0)) {
+ wake_up(wait);
+ return 1;
+ }
+
+ return 0;
+}
+
+/* --------------------------------------------------------------------------
+ * Clock management
+ */
+
+#define ISS_CLKCTRL_MASK (ISS_CLKCTRL_CSI2_A |\
+ ISS_CLKCTRL_CSI2_B |\
+ ISS_CLKCTRL_ISP)
+
+static int __iss_subclk_update(struct iss_device *iss)
+{
+ u32 clk = 0;
+ int ret = 0, timeout = 1000;
+
+ if (iss->subclk_resources & OMAP4_ISS_SUBCLK_CSI2_A)
+ clk |= ISS_CLKCTRL_CSI2_A;
+
+ if (iss->subclk_resources & OMAP4_ISS_SUBCLK_CSI2_B)
+ clk |= ISS_CLKCTRL_CSI2_B;
+
+ if (iss->subclk_resources & OMAP4_ISS_SUBCLK_ISP)
+ clk |= ISS_CLKCTRL_ISP;
+
+ iss_reg_update(iss, OMAP4_ISS_MEM_TOP, ISS_CLKCTRL,
+ ISS_CLKCTRL_MASK, clk);
+
+ /* Wait for HW assertion */
+ while (--timeout > 0) {
+ udelay(1);
+ if ((iss_reg_read(iss, OMAP4_ISS_MEM_TOP, ISS_CLKSTAT) &
+ ISS_CLKCTRL_MASK) == clk)
+ break;
+ }
+
+ if (!timeout)
+ ret = -EBUSY;
+
+ return ret;
+}
+
+int omap4iss_subclk_enable(struct iss_device *iss,
+ enum iss_subclk_resource res)
+{
+ iss->subclk_resources |= res;
+
+ return __iss_subclk_update(iss);
+}
+
+int omap4iss_subclk_disable(struct iss_device *iss,
+ enum iss_subclk_resource res)
+{
+ iss->subclk_resources &= ~res;
+
+ return __iss_subclk_update(iss);
+}
+
+#define ISS_ISP5_CLKCTRL_MASK (ISP5_CTRL_BL_CLK_ENABLE |\
+ ISP5_CTRL_ISIF_CLK_ENABLE |\
+ ISP5_CTRL_H3A_CLK_ENABLE |\
+ ISP5_CTRL_RSZ_CLK_ENABLE |\
+ ISP5_CTRL_IPIPE_CLK_ENABLE |\
+ ISP5_CTRL_IPIPEIF_CLK_ENABLE)
+
+static void __iss_isp_subclk_update(struct iss_device *iss)
+{
+ u32 clk = 0;
+
+ if (iss->isp_subclk_resources & OMAP4_ISS_ISP_SUBCLK_ISIF)
+ clk |= ISP5_CTRL_ISIF_CLK_ENABLE;
+
+ if (iss->isp_subclk_resources & OMAP4_ISS_ISP_SUBCLK_H3A)
+ clk |= ISP5_CTRL_H3A_CLK_ENABLE;
+
+ if (iss->isp_subclk_resources & OMAP4_ISS_ISP_SUBCLK_RSZ)
+ clk |= ISP5_CTRL_RSZ_CLK_ENABLE;
+
+ if (iss->isp_subclk_resources & OMAP4_ISS_ISP_SUBCLK_IPIPE)
+ clk |= ISP5_CTRL_IPIPE_CLK_ENABLE;
+
+ if (iss->isp_subclk_resources & OMAP4_ISS_ISP_SUBCLK_IPIPEIF)
+ clk |= ISP5_CTRL_IPIPEIF_CLK_ENABLE;
+
+ if (clk)
+ clk |= ISP5_CTRL_BL_CLK_ENABLE;
+
+ iss_reg_update(iss, OMAP4_ISS_MEM_ISP_SYS1, ISP5_CTRL,
+ ISS_ISP5_CLKCTRL_MASK, clk);
+}
+
+void omap4iss_isp_subclk_enable(struct iss_device *iss,
+ enum iss_isp_subclk_resource res)
+{
+ iss->isp_subclk_resources |= res;
+
+ __iss_isp_subclk_update(iss);
+}
+
+void omap4iss_isp_subclk_disable(struct iss_device *iss,
+ enum iss_isp_subclk_resource res)
+{
+ iss->isp_subclk_resources &= ~res;
+
+ __iss_isp_subclk_update(iss);
+}
+
+/*
+ * iss_enable_clocks - Enable ISS clocks
+ * @iss: OMAP4 ISS device
+ *
+ * Return 0 if successful, or clk_enable return value if any of tthem fails.
+ */
+static int iss_enable_clocks(struct iss_device *iss)
+{
+ int ret;
+
+ ret = clk_enable(iss->iss_fck);
+ if (ret) {
+ dev_err(iss->dev, "clk_enable iss_fck failed\n");
+ return ret;
+ }
+
+ ret = clk_enable(iss->iss_ctrlclk);
+ if (ret) {
+ dev_err(iss->dev, "clk_enable iss_ctrlclk failed\n");
+ clk_disable(iss->iss_fck);
+ return ret;
+ }
+
+ return 0;
+}
+
+/*
+ * iss_disable_clocks - Disable ISS clocks
+ * @iss: OMAP4 ISS device
+ */
+static void iss_disable_clocks(struct iss_device *iss)
+{
+ clk_disable(iss->iss_ctrlclk);
+ clk_disable(iss->iss_fck);
+}
+
+static int iss_get_clocks(struct iss_device *iss)
+{
+ iss->iss_fck = devm_clk_get(iss->dev, "iss_fck");
+ if (IS_ERR(iss->iss_fck)) {
+ dev_err(iss->dev, "Unable to get iss_fck clock info\n");
+ return PTR_ERR(iss->iss_fck);
+ }
+
+ iss->iss_ctrlclk = devm_clk_get(iss->dev, "iss_ctrlclk");
+ if (IS_ERR(iss->iss_ctrlclk)) {
+ dev_err(iss->dev, "Unable to get iss_ctrlclk clock info\n");
+ return PTR_ERR(iss->iss_ctrlclk);
+ }
+
+ return 0;
+}
+
+/*
+ * omap4iss_get - Acquire the ISS resource.
+ *
+ * Initializes the clocks for the first acquire.
+ *
+ * Increment the reference count on the ISS. If the first reference is taken,
+ * enable clocks and power-up all submodules.
+ *
+ * Return a pointer to the ISS device structure, or NULL if an error occurred.
+ */
+struct iss_device *omap4iss_get(struct iss_device *iss)
+{
+ struct iss_device *__iss = iss;
+
+ if (iss == NULL)
+ return NULL;
+
+ mutex_lock(&iss->iss_mutex);
+ if (iss->ref_count > 0)
+ goto out;
+
+ if (iss_enable_clocks(iss) < 0) {
+ __iss = NULL;
+ goto out;
+ }
+
+ iss_enable_interrupts(iss);
+
+out:
+ if (__iss != NULL)
+ iss->ref_count++;
+ mutex_unlock(&iss->iss_mutex);
+
+ return __iss;
+}
+
+/*
+ * omap4iss_put - Release the ISS
+ *
+ * Decrement the reference count on the ISS. If the last reference is released,
+ * power-down all submodules, disable clocks and free temporary buffers.
+ */
+void omap4iss_put(struct iss_device *iss)
+{
+ if (iss == NULL)
+ return;
+
+ mutex_lock(&iss->iss_mutex);
+ BUG_ON(iss->ref_count == 0);
+ if (--iss->ref_count == 0) {
+ iss_disable_interrupts(iss);
+ /* Reset the ISS if an entity has failed to stop. This is the
+ * only way to recover from such conditions, although it would
+ * be worth investigating whether resetting the ISP only can't
+ * fix the problem in some cases.
+ */
+ if (iss->crashed)
+ iss_reset(iss);
+ iss_disable_clocks(iss);
+ }
+ mutex_unlock(&iss->iss_mutex);
+}
+
+static int iss_map_mem_resource(struct platform_device *pdev,
+ struct iss_device *iss,
+ enum iss_mem_resources res)
+{
+ struct resource *mem;
+
+ mem = platform_get_resource(pdev, IORESOURCE_MEM, res);
+
+ iss->regs[res] = devm_ioremap_resource(iss->dev, mem);
+
+ return PTR_ERR_OR_ZERO(iss->regs[res]);
+}
+
+static void iss_unregister_entities(struct iss_device *iss)
+{
+ omap4iss_resizer_unregister_entities(&iss->resizer);
+ omap4iss_ipipe_unregister_entities(&iss->ipipe);
+ omap4iss_ipipeif_unregister_entities(&iss->ipipeif);
+ omap4iss_csi2_unregister_entities(&iss->csi2a);
+ omap4iss_csi2_unregister_entities(&iss->csi2b);
+
+ v4l2_device_unregister(&iss->v4l2_dev);
+ media_device_unregister(&iss->media_dev);
+}
+
+/*
+ * iss_register_subdev_group - Register a group of subdevices
+ * @iss: OMAP4 ISS device
+ * @board_info: I2C subdevs board information array
+ *
+ * Register all I2C subdevices in the board_info array. The array must be
+ * terminated by a NULL entry, and the first entry must be the sensor.
+ *
+ * Return a pointer to the sensor media entity if it has been successfully
+ * registered, or NULL otherwise.
+ */
+static struct v4l2_subdev *
+iss_register_subdev_group(struct iss_device *iss,
+ struct iss_subdev_i2c_board_info *board_info)
+{
+ struct v4l2_subdev *sensor = NULL;
+ unsigned int first;
+
+ if (board_info->board_info == NULL)
+ return NULL;
+
+ for (first = 1; board_info->board_info; ++board_info, first = 0) {
+ struct v4l2_subdev *subdev;
+ struct i2c_adapter *adapter;
+
+ adapter = i2c_get_adapter(board_info->i2c_adapter_id);
+ if (adapter == NULL) {
+ dev_err(iss->dev,
+ "%s: Unable to get I2C adapter %d for device %s\n",
+ __func__, board_info->i2c_adapter_id,
+ board_info->board_info->type);
+ continue;
+ }
+
+ subdev = v4l2_i2c_new_subdev_board(&iss->v4l2_dev, adapter,
+ board_info->board_info, NULL);
+ if (subdev == NULL) {
+ dev_err(iss->dev, "Unable to register subdev %s\n",
+ board_info->board_info->type);
+ continue;
+ }
+
+ if (first)
+ sensor = subdev;
+ }
+
+ return sensor;
+}
+
+static int iss_register_entities(struct iss_device *iss)
+{
+ struct iss_platform_data *pdata = iss->pdata;
+ struct iss_v4l2_subdevs_group *subdevs;
+ int ret;
+
+ iss->media_dev.dev = iss->dev;
+ strlcpy(iss->media_dev.model, "TI OMAP4 ISS",
+ sizeof(iss->media_dev.model));
+ iss->media_dev.hw_revision = iss->revision;
+ iss->media_dev.link_notify = iss_pipeline_link_notify;
+ ret = media_device_register(&iss->media_dev);
+ if (ret < 0) {
+ dev_err(iss->dev, "Media device registration failed (%d)\n",
+ ret);
+ return ret;
+ }
+
+ iss->v4l2_dev.mdev = &iss->media_dev;
+ ret = v4l2_device_register(iss->dev, &iss->v4l2_dev);
+ if (ret < 0) {
+ dev_err(iss->dev, "V4L2 device registration failed (%d)\n",
+ ret);
+ goto done;
+ }
+
+ /* Register internal entities */
+ ret = omap4iss_csi2_register_entities(&iss->csi2a, &iss->v4l2_dev);
+ if (ret < 0)
+ goto done;
+
+ ret = omap4iss_csi2_register_entities(&iss->csi2b, &iss->v4l2_dev);
+ if (ret < 0)
+ goto done;
+
+ ret = omap4iss_ipipeif_register_entities(&iss->ipipeif, &iss->v4l2_dev);
+ if (ret < 0)
+ goto done;
+
+ ret = omap4iss_ipipe_register_entities(&iss->ipipe, &iss->v4l2_dev);
+ if (ret < 0)
+ goto done;
+
+ ret = omap4iss_resizer_register_entities(&iss->resizer, &iss->v4l2_dev);
+ if (ret < 0)
+ goto done;
+
+ /* Register external entities */
+ for (subdevs = pdata->subdevs; subdevs && subdevs->subdevs; ++subdevs) {
+ struct v4l2_subdev *sensor;
+ struct media_entity *input;
+ unsigned int flags;
+ unsigned int pad;
+
+ sensor = iss_register_subdev_group(iss, subdevs->subdevs);
+ if (sensor == NULL)
+ continue;
+
+ sensor->host_priv = subdevs;
+
+ /* Connect the sensor to the correct interface module.
+ * CSI2a receiver through CSIPHY1, or
+ * CSI2b receiver through CSIPHY2
+ */
+ switch (subdevs->interface) {
+ case ISS_INTERFACE_CSI2A_PHY1:
+ input = &iss->csi2a.subdev.entity;
+ pad = CSI2_PAD_SINK;
+ flags = MEDIA_LNK_FL_IMMUTABLE
+ | MEDIA_LNK_FL_ENABLED;
+ break;
+
+ case ISS_INTERFACE_CSI2B_PHY2:
+ input = &iss->csi2b.subdev.entity;
+ pad = CSI2_PAD_SINK;
+ flags = MEDIA_LNK_FL_IMMUTABLE
+ | MEDIA_LNK_FL_ENABLED;
+ break;
+
+ default:
+ dev_err(iss->dev, "invalid interface type %u\n",
+ subdevs->interface);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ ret = media_entity_create_link(&sensor->entity, 0, input, pad,
+ flags);
+ if (ret < 0)
+ goto done;
+ }
+
+ ret = v4l2_device_register_subdev_nodes(&iss->v4l2_dev);
+
+done:
+ if (ret < 0)
+ iss_unregister_entities(iss);
+
+ return ret;
+}
+
+static void iss_cleanup_modules(struct iss_device *iss)
+{
+ omap4iss_csi2_cleanup(iss);
+ omap4iss_ipipeif_cleanup(iss);
+ omap4iss_ipipe_cleanup(iss);
+ omap4iss_resizer_cleanup(iss);
+}
+
+static int iss_initialize_modules(struct iss_device *iss)
+{
+ int ret;
+
+ ret = omap4iss_csiphy_init(iss);
+ if (ret < 0) {
+ dev_err(iss->dev, "CSI PHY initialization failed\n");
+ goto error_csiphy;
+ }
+
+ ret = omap4iss_csi2_init(iss);
+ if (ret < 0) {
+ dev_err(iss->dev, "CSI2 initialization failed\n");
+ goto error_csi2;
+ }
+
+ ret = omap4iss_ipipeif_init(iss);
+ if (ret < 0) {
+ dev_err(iss->dev, "ISP IPIPEIF initialization failed\n");
+ goto error_ipipeif;
+ }
+
+ ret = omap4iss_ipipe_init(iss);
+ if (ret < 0) {
+ dev_err(iss->dev, "ISP IPIPE initialization failed\n");
+ goto error_ipipe;
+ }
+
+ ret = omap4iss_resizer_init(iss);
+ if (ret < 0) {
+ dev_err(iss->dev, "ISP RESIZER initialization failed\n");
+ goto error_resizer;
+ }
+
+ /* Connect the submodules. */
+ ret = media_entity_create_link(
+ &iss->csi2a.subdev.entity, CSI2_PAD_SOURCE,
+ &iss->ipipeif.subdev.entity, IPIPEIF_PAD_SINK, 0);
+ if (ret < 0)
+ goto error_link;
+
+ ret = media_entity_create_link(
+ &iss->csi2b.subdev.entity, CSI2_PAD_SOURCE,
+ &iss->ipipeif.subdev.entity, IPIPEIF_PAD_SINK, 0);
+ if (ret < 0)
+ goto error_link;
+
+ ret = media_entity_create_link(
+ &iss->ipipeif.subdev.entity, IPIPEIF_PAD_SOURCE_VP,
+ &iss->resizer.subdev.entity, RESIZER_PAD_SINK, 0);
+ if (ret < 0)
+ goto error_link;
+
+ ret = media_entity_create_link(
+ &iss->ipipeif.subdev.entity, IPIPEIF_PAD_SOURCE_VP,
+ &iss->ipipe.subdev.entity, IPIPE_PAD_SINK, 0);
+ if (ret < 0)
+ goto error_link;
+
+ ret = media_entity_create_link(
+ &iss->ipipe.subdev.entity, IPIPE_PAD_SOURCE_VP,
+ &iss->resizer.subdev.entity, RESIZER_PAD_SINK, 0);
+ if (ret < 0)
+ goto error_link;
+
+ return 0;
+
+error_link:
+ omap4iss_resizer_cleanup(iss);
+error_resizer:
+ omap4iss_ipipe_cleanup(iss);
+error_ipipe:
+ omap4iss_ipipeif_cleanup(iss);
+error_ipipeif:
+ omap4iss_csi2_cleanup(iss);
+error_csi2:
+error_csiphy:
+ return ret;
+}
+
+static int iss_probe(struct platform_device *pdev)
+{
+ struct iss_platform_data *pdata = pdev->dev.platform_data;
+ struct iss_device *iss;
+ unsigned int i;
+ int ret;
+
+ if (pdata == NULL)
+ return -EINVAL;
+
+ iss = devm_kzalloc(&pdev->dev, sizeof(*iss), GFP_KERNEL);
+ if (!iss)
+ return -ENOMEM;
+
+ mutex_init(&iss->iss_mutex);
+
+ iss->dev = &pdev->dev;
+ iss->pdata = pdata;
+
+ iss->raw_dmamask = DMA_BIT_MASK(32);
+ iss->dev->dma_mask = &iss->raw_dmamask;
+ iss->dev->coherent_dma_mask = DMA_BIT_MASK(32);
+
+ platform_set_drvdata(pdev, iss);
+
+ /*
+ * TODO: When implementing DT support switch to syscon regmap lookup by
+ * phandle.
+ */
+ iss->syscon = syscon_regmap_lookup_by_compatible("syscon");
+ if (IS_ERR(iss->syscon)) {
+ ret = PTR_ERR(iss->syscon);
+ goto error;
+ }
+
+ /* Clocks */
+ ret = iss_map_mem_resource(pdev, iss, OMAP4_ISS_MEM_TOP);
+ if (ret < 0)
+ goto error;
+
+ ret = iss_get_clocks(iss);
+ if (ret < 0)
+ goto error;
+
+ if (omap4iss_get(iss) == NULL)
+ goto error;
+
+ ret = iss_reset(iss);
+ if (ret < 0)
+ goto error_iss;
+
+ iss->revision = iss_reg_read(iss, OMAP4_ISS_MEM_TOP, ISS_HL_REVISION);
+ dev_info(iss->dev, "Revision %08x found\n", iss->revision);
+
+ for (i = 1; i < OMAP4_ISS_MEM_LAST; i++) {
+ ret = iss_map_mem_resource(pdev, iss, i);
+ if (ret)
+ goto error_iss;
+ }
+
+ /* Configure BTE BW_LIMITER field to max recommended value (1 GB) */
+ iss_reg_update(iss, OMAP4_ISS_MEM_BTE, BTE_CTRL,
+ BTE_CTRL_BW_LIMITER_MASK,
+ 18 << BTE_CTRL_BW_LIMITER_SHIFT);
+
+ /* Perform ISP reset */
+ ret = omap4iss_subclk_enable(iss, OMAP4_ISS_SUBCLK_ISP);
+ if (ret < 0)
+ goto error_iss;
+
+ ret = iss_isp_reset(iss);
+ if (ret < 0)
+ goto error_iss;
+
+ dev_info(iss->dev, "ISP Revision %08x found\n",
+ iss_reg_read(iss, OMAP4_ISS_MEM_ISP_SYS1, ISP5_REVISION));
+
+ /* Interrupt */
+ iss->irq_num = platform_get_irq(pdev, 0);
+ if (iss->irq_num <= 0) {
+ dev_err(iss->dev, "No IRQ resource\n");
+ ret = -ENODEV;
+ goto error_iss;
+ }
+
+ if (devm_request_irq(iss->dev, iss->irq_num, iss_isr, IRQF_SHARED,
+ "OMAP4 ISS", iss)) {
+ dev_err(iss->dev, "Unable to request IRQ\n");
+ ret = -EINVAL;
+ goto error_iss;
+ }
+
+ /* Entities */
+ ret = iss_initialize_modules(iss);
+ if (ret < 0)
+ goto error_iss;
+
+ ret = iss_register_entities(iss);
+ if (ret < 0)
+ goto error_modules;
+
+ omap4iss_put(iss);
+
+ return 0;
+
+error_modules:
+ iss_cleanup_modules(iss);
+error_iss:
+ omap4iss_put(iss);
+error:
+ platform_set_drvdata(pdev, NULL);
+
+ mutex_destroy(&iss->iss_mutex);
+
+ return ret;
+}
+
+static int iss_remove(struct platform_device *pdev)
+{
+ struct iss_device *iss = platform_get_drvdata(pdev);
+
+ iss_unregister_entities(iss);
+ iss_cleanup_modules(iss);
+
+ return 0;
+}
+
+static struct platform_device_id omap4iss_id_table[] = {
+ { "omap4iss", 0 },
+ { },
+};
+MODULE_DEVICE_TABLE(platform, omap4iss_id_table);
+
+static struct platform_driver iss_driver = {
+ .probe = iss_probe,
+ .remove = iss_remove,
+ .id_table = omap4iss_id_table,
+ .driver = {
+ .name = "omap4iss",
+ },
+};
+
+module_platform_driver(iss_driver);
+
+MODULE_DESCRIPTION("TI OMAP4 ISS driver");
+MODULE_AUTHOR("Sergio Aguirre <sergio.a.aguirre@gmail.com>");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(ISS_VIDEO_DRIVER_VERSION);
diff --git a/drivers/staging/media/omap4iss/iss.h b/drivers/staging/media/omap4iss/iss.h
new file mode 100644
index 000000000..35df8b470
--- /dev/null
+++ b/drivers/staging/media/omap4iss/iss.h
@@ -0,0 +1,254 @@
+/*
+ * TI OMAP4 ISS V4L2 Driver
+ *
+ * Copyright (C) 2012 Texas Instruments.
+ *
+ * Author: Sergio Aguirre <sergio.a.aguirre@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef _OMAP4_ISS_H_
+#define _OMAP4_ISS_H_
+
+#include <media/v4l2-device.h>
+#include <linux/device.h>
+#include <linux/io.h>
+#include <linux/platform_device.h>
+#include <linux/wait.h>
+
+#include <media/omap4iss.h>
+
+#include "iss_regs.h"
+#include "iss_csiphy.h"
+#include "iss_csi2.h"
+#include "iss_ipipeif.h"
+#include "iss_ipipe.h"
+#include "iss_resizer.h"
+
+struct regmap;
+
+#define to_iss_device(ptr_module) \
+ container_of(ptr_module, struct iss_device, ptr_module)
+#define to_device(ptr_module) \
+ (to_iss_device(ptr_module)->dev)
+
+enum iss_mem_resources {
+ OMAP4_ISS_MEM_TOP,
+ OMAP4_ISS_MEM_CSI2_A_REGS1,
+ OMAP4_ISS_MEM_CAMERARX_CORE1,
+ OMAP4_ISS_MEM_CSI2_B_REGS1,
+ OMAP4_ISS_MEM_CAMERARX_CORE2,
+ OMAP4_ISS_MEM_BTE,
+ OMAP4_ISS_MEM_ISP_SYS1,
+ OMAP4_ISS_MEM_ISP_RESIZER,
+ OMAP4_ISS_MEM_ISP_IPIPE,
+ OMAP4_ISS_MEM_ISP_ISIF,
+ OMAP4_ISS_MEM_ISP_IPIPEIF,
+ OMAP4_ISS_MEM_LAST,
+};
+
+enum iss_subclk_resource {
+ OMAP4_ISS_SUBCLK_SIMCOP = (1 << 0),
+ OMAP4_ISS_SUBCLK_ISP = (1 << 1),
+ OMAP4_ISS_SUBCLK_CSI2_A = (1 << 2),
+ OMAP4_ISS_SUBCLK_CSI2_B = (1 << 3),
+ OMAP4_ISS_SUBCLK_CCP2 = (1 << 4),
+};
+
+enum iss_isp_subclk_resource {
+ OMAP4_ISS_ISP_SUBCLK_BL = (1 << 0),
+ OMAP4_ISS_ISP_SUBCLK_ISIF = (1 << 1),
+ OMAP4_ISS_ISP_SUBCLK_H3A = (1 << 2),
+ OMAP4_ISS_ISP_SUBCLK_RSZ = (1 << 3),
+ OMAP4_ISS_ISP_SUBCLK_IPIPE = (1 << 4),
+ OMAP4_ISS_ISP_SUBCLK_IPIPEIF = (1 << 5),
+};
+
+/*
+ * struct iss_reg - Structure for ISS register values.
+ * @reg: 32-bit Register address.
+ * @val: 32-bit Register value.
+ */
+struct iss_reg {
+ enum iss_mem_resources mmio_range;
+ u32 reg;
+ u32 val;
+};
+
+/*
+ * struct iss_device - ISS device structure.
+ * @syscon: Regmap for the syscon register space
+ * @crashed: Bitmask of crashed entities (indexed by entity ID)
+ */
+struct iss_device {
+ struct v4l2_device v4l2_dev;
+ struct media_device media_dev;
+ struct device *dev;
+ u32 revision;
+
+ /* platform HW resources */
+ struct iss_platform_data *pdata;
+ unsigned int irq_num;
+
+ struct resource *res[OMAP4_ISS_MEM_LAST];
+ void __iomem *regs[OMAP4_ISS_MEM_LAST];
+ struct regmap *syscon;
+
+ u64 raw_dmamask;
+
+ struct mutex iss_mutex; /* For handling ref_count field */
+ unsigned int crashed;
+ int has_context;
+ int ref_count;
+
+ struct clk *iss_fck;
+ struct clk *iss_ctrlclk;
+
+ /* ISS modules */
+ struct iss_csi2_device csi2a;
+ struct iss_csi2_device csi2b;
+ struct iss_csiphy csiphy1;
+ struct iss_csiphy csiphy2;
+ struct iss_ipipeif_device ipipeif;
+ struct iss_ipipe_device ipipe;
+ struct iss_resizer_device resizer;
+
+ unsigned int subclk_resources;
+ unsigned int isp_subclk_resources;
+};
+
+#define v4l2_dev_to_iss_device(dev) \
+ container_of(dev, struct iss_device, v4l2_dev)
+
+int omap4iss_get_external_info(struct iss_pipeline *pipe,
+ struct media_link *link);
+
+int omap4iss_module_sync_idle(struct media_entity *me, wait_queue_head_t *wait,
+ atomic_t *stopping);
+
+int omap4iss_module_sync_is_stopping(wait_queue_head_t *wait,
+ atomic_t *stopping);
+
+int omap4iss_pipeline_set_stream(struct iss_pipeline *pipe,
+ enum iss_pipeline_stream_state state);
+void omap4iss_pipeline_cancel_stream(struct iss_pipeline *pipe);
+
+void omap4iss_configure_bridge(struct iss_device *iss,
+ enum ipipeif_input_entity input);
+
+struct iss_device *omap4iss_get(struct iss_device *iss);
+void omap4iss_put(struct iss_device *iss);
+int omap4iss_subclk_enable(struct iss_device *iss,
+ enum iss_subclk_resource res);
+int omap4iss_subclk_disable(struct iss_device *iss,
+ enum iss_subclk_resource res);
+void omap4iss_isp_subclk_enable(struct iss_device *iss,
+ enum iss_isp_subclk_resource res);
+void omap4iss_isp_subclk_disable(struct iss_device *iss,
+ enum iss_isp_subclk_resource res);
+
+int omap4iss_pipeline_pm_use(struct media_entity *entity, int use);
+
+int omap4iss_register_entities(struct platform_device *pdev,
+ struct v4l2_device *v4l2_dev);
+void omap4iss_unregister_entities(struct platform_device *pdev);
+
+/*
+ * iss_reg_read - Read the value of an OMAP4 ISS register
+ * @iss: the ISS device
+ * @res: memory resource in which the register is located
+ * @offset: register offset in the memory resource
+ *
+ * Return the register value.
+ */
+static inline
+u32 iss_reg_read(struct iss_device *iss, enum iss_mem_resources res,
+ u32 offset)
+{
+ return readl(iss->regs[res] + offset);
+}
+
+/*
+ * iss_reg_write - Write a value to an OMAP4 ISS register
+ * @iss: the ISS device
+ * @res: memory resource in which the register is located
+ * @offset: register offset in the memory resource
+ * @value: value to be written
+ */
+static inline
+void iss_reg_write(struct iss_device *iss, enum iss_mem_resources res,
+ u32 offset, u32 value)
+{
+ writel(value, iss->regs[res] + offset);
+}
+
+/*
+ * iss_reg_clr - Clear bits in an OMAP4 ISS register
+ * @iss: the ISS device
+ * @res: memory resource in which the register is located
+ * @offset: register offset in the memory resource
+ * @clr: bit mask to be cleared
+ */
+static inline
+void iss_reg_clr(struct iss_device *iss, enum iss_mem_resources res,
+ u32 offset, u32 clr)
+{
+ u32 v = iss_reg_read(iss, res, offset);
+
+ iss_reg_write(iss, res, offset, v & ~clr);
+}
+
+/*
+ * iss_reg_set - Set bits in an OMAP4 ISS register
+ * @iss: the ISS device
+ * @res: memory resource in which the register is located
+ * @offset: register offset in the memory resource
+ * @set: bit mask to be set
+ */
+static inline
+void iss_reg_set(struct iss_device *iss, enum iss_mem_resources res,
+ u32 offset, u32 set)
+{
+ u32 v = iss_reg_read(iss, res, offset);
+
+ iss_reg_write(iss, res, offset, v | set);
+}
+
+/*
+ * iss_reg_update - Clear and set bits in an OMAP4 ISS register
+ * @iss: the ISS device
+ * @res: memory resource in which the register is located
+ * @offset: register offset in the memory resource
+ * @clr: bit mask to be cleared
+ * @set: bit mask to be set
+ *
+ * Clear the clr mask first and then set the set mask.
+ */
+static inline
+void iss_reg_update(struct iss_device *iss, enum iss_mem_resources res,
+ u32 offset, u32 clr, u32 set)
+{
+ u32 v = iss_reg_read(iss, res, offset);
+
+ iss_reg_write(iss, res, offset, (v & ~clr) | set);
+}
+
+#define iss_poll_condition_timeout(cond, timeout, min_ival, max_ival) \
+({ \
+ unsigned long __timeout = jiffies + usecs_to_jiffies(timeout); \
+ unsigned int __min_ival = (min_ival); \
+ unsigned int __max_ival = (max_ival); \
+ bool __cond; \
+ while (!(__cond = (cond))) { \
+ if (time_after(jiffies, __timeout)) \
+ break; \
+ usleep_range(__min_ival, __max_ival); \
+ } \
+ !__cond; \
+})
+
+#endif /* _OMAP4_ISS_H_ */
diff --git a/drivers/staging/media/omap4iss/iss_csi2.c b/drivers/staging/media/omap4iss/iss_csi2.c
new file mode 100644
index 000000000..d7ff7698a
--- /dev/null
+++ b/drivers/staging/media/omap4iss/iss_csi2.c
@@ -0,0 +1,1351 @@
+/*
+ * TI OMAP4 ISS V4L2 Driver - CSI PHY module
+ *
+ * Copyright (C) 2012 Texas Instruments, Inc.
+ *
+ * Author: Sergio Aguirre <sergio.a.aguirre@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/delay.h>
+#include <media/v4l2-common.h>
+#include <linux/v4l2-mediabus.h>
+#include <linux/mm.h>
+
+#include "iss.h"
+#include "iss_regs.h"
+#include "iss_csi2.h"
+
+/*
+ * csi2_if_enable - Enable CSI2 Receiver interface.
+ * @enable: enable flag
+ *
+ */
+static void csi2_if_enable(struct iss_csi2_device *csi2, u8 enable)
+{
+ struct iss_csi2_ctrl_cfg *currctrl = &csi2->ctrl;
+
+ iss_reg_update(csi2->iss, csi2->regs1, CSI2_CTRL, CSI2_CTRL_IF_EN,
+ enable ? CSI2_CTRL_IF_EN : 0);
+
+ currctrl->if_enable = enable;
+}
+
+/*
+ * csi2_recv_config - CSI2 receiver module configuration.
+ * @currctrl: iss_csi2_ctrl_cfg structure
+ *
+ */
+static void csi2_recv_config(struct iss_csi2_device *csi2,
+ struct iss_csi2_ctrl_cfg *currctrl)
+{
+ u32 reg = 0;
+
+ if (currctrl->frame_mode)
+ reg |= CSI2_CTRL_FRAME;
+ else
+ reg &= ~CSI2_CTRL_FRAME;
+
+ if (currctrl->vp_clk_enable)
+ reg |= CSI2_CTRL_VP_CLK_EN;
+ else
+ reg &= ~CSI2_CTRL_VP_CLK_EN;
+
+ if (currctrl->vp_only_enable)
+ reg |= CSI2_CTRL_VP_ONLY_EN;
+ else
+ reg &= ~CSI2_CTRL_VP_ONLY_EN;
+
+ reg &= ~CSI2_CTRL_VP_OUT_CTRL_MASK;
+ reg |= currctrl->vp_out_ctrl << CSI2_CTRL_VP_OUT_CTRL_SHIFT;
+
+ if (currctrl->ecc_enable)
+ reg |= CSI2_CTRL_ECC_EN;
+ else
+ reg &= ~CSI2_CTRL_ECC_EN;
+
+ /*
+ * Set MFlag assertion boundaries to:
+ * Low: 4/8 of FIFO size
+ * High: 6/8 of FIFO size
+ */
+ reg &= ~(CSI2_CTRL_MFLAG_LEVH_MASK | CSI2_CTRL_MFLAG_LEVL_MASK);
+ reg |= (2 << CSI2_CTRL_MFLAG_LEVH_SHIFT) |
+ (4 << CSI2_CTRL_MFLAG_LEVL_SHIFT);
+
+ /* Generation of 16x64-bit bursts (Recommended) */
+ reg |= CSI2_CTRL_BURST_SIZE_EXPAND;
+
+ /* Do Non-Posted writes (Recommended) */
+ reg |= CSI2_CTRL_NON_POSTED_WRITE;
+
+ /*
+ * Enforce Little endian for all formats, including:
+ * YUV4:2:2 8-bit and YUV4:2:0 Legacy
+ */
+ reg |= CSI2_CTRL_ENDIANNESS;
+
+ iss_reg_write(csi2->iss, csi2->regs1, CSI2_CTRL, reg);
+}
+
+static const unsigned int csi2_input_fmts[] = {
+ MEDIA_BUS_FMT_SGRBG10_1X10,
+ MEDIA_BUS_FMT_SGRBG10_DPCM8_1X8,
+ MEDIA_BUS_FMT_SRGGB10_1X10,
+ MEDIA_BUS_FMT_SRGGB10_DPCM8_1X8,
+ MEDIA_BUS_FMT_SBGGR10_1X10,
+ MEDIA_BUS_FMT_SBGGR10_DPCM8_1X8,
+ MEDIA_BUS_FMT_SGBRG10_1X10,
+ MEDIA_BUS_FMT_SGBRG10_DPCM8_1X8,
+ MEDIA_BUS_FMT_SBGGR8_1X8,
+ MEDIA_BUS_FMT_SGBRG8_1X8,
+ MEDIA_BUS_FMT_SGRBG8_1X8,
+ MEDIA_BUS_FMT_SRGGB8_1X8,
+ MEDIA_BUS_FMT_UYVY8_1X16,
+ MEDIA_BUS_FMT_YUYV8_1X16,
+};
+
+/* To set the format on the CSI2 requires a mapping function that takes
+ * the following inputs:
+ * - 3 different formats (at this time)
+ * - 2 destinations (mem, vp+mem) (vp only handled separately)
+ * - 2 decompression options (on, off)
+ * Output should be CSI2 frame format code
+ * Array indices as follows: [format][dest][decompr]
+ * Not all combinations are valid. 0 means invalid.
+ */
+static const u16 __csi2_fmt_map[][2][2] = {
+ /* RAW10 formats */
+ {
+ /* Output to memory */
+ {
+ /* No DPCM decompression */
+ CSI2_PIX_FMT_RAW10_EXP16,
+ /* DPCM decompression */
+ 0,
+ },
+ /* Output to both */
+ {
+ /* No DPCM decompression */
+ CSI2_PIX_FMT_RAW10_EXP16_VP,
+ /* DPCM decompression */
+ 0,
+ },
+ },
+ /* RAW10 DPCM8 formats */
+ {
+ /* Output to memory */
+ {
+ /* No DPCM decompression */
+ CSI2_USERDEF_8BIT_DATA1,
+ /* DPCM decompression */
+ CSI2_USERDEF_8BIT_DATA1_DPCM10,
+ },
+ /* Output to both */
+ {
+ /* No DPCM decompression */
+ CSI2_PIX_FMT_RAW8_VP,
+ /* DPCM decompression */
+ CSI2_USERDEF_8BIT_DATA1_DPCM10_VP,
+ },
+ },
+ /* RAW8 formats */
+ {
+ /* Output to memory */
+ {
+ /* No DPCM decompression */
+ CSI2_PIX_FMT_RAW8,
+ /* DPCM decompression */
+ 0,
+ },
+ /* Output to both */
+ {
+ /* No DPCM decompression */
+ CSI2_PIX_FMT_RAW8_VP,
+ /* DPCM decompression */
+ 0,
+ },
+ },
+ /* YUV422 formats */
+ {
+ /* Output to memory */
+ {
+ /* No DPCM decompression */
+ CSI2_PIX_FMT_YUV422_8BIT,
+ /* DPCM decompression */
+ 0,
+ },
+ /* Output to both */
+ {
+ /* No DPCM decompression */
+ CSI2_PIX_FMT_YUV422_8BIT_VP16,
+ /* DPCM decompression */
+ 0,
+ },
+ },
+};
+
+/*
+ * csi2_ctx_map_format - Map CSI2 sink media bus format to CSI2 format ID
+ * @csi2: ISS CSI2 device
+ *
+ * Returns CSI2 physical format id
+ */
+static u16 csi2_ctx_map_format(struct iss_csi2_device *csi2)
+{
+ const struct v4l2_mbus_framefmt *fmt = &csi2->formats[CSI2_PAD_SINK];
+ int fmtidx, destidx;
+
+ switch (fmt->code) {
+ case MEDIA_BUS_FMT_SGRBG10_1X10:
+ case MEDIA_BUS_FMT_SRGGB10_1X10:
+ case MEDIA_BUS_FMT_SBGGR10_1X10:
+ case MEDIA_BUS_FMT_SGBRG10_1X10:
+ fmtidx = 0;
+ break;
+ case MEDIA_BUS_FMT_SGRBG10_DPCM8_1X8:
+ case MEDIA_BUS_FMT_SRGGB10_DPCM8_1X8:
+ case MEDIA_BUS_FMT_SBGGR10_DPCM8_1X8:
+ case MEDIA_BUS_FMT_SGBRG10_DPCM8_1X8:
+ fmtidx = 1;
+ break;
+ case MEDIA_BUS_FMT_SBGGR8_1X8:
+ case MEDIA_BUS_FMT_SGBRG8_1X8:
+ case MEDIA_BUS_FMT_SGRBG8_1X8:
+ case MEDIA_BUS_FMT_SRGGB8_1X8:
+ fmtidx = 2;
+ break;
+ case MEDIA_BUS_FMT_UYVY8_1X16:
+ case MEDIA_BUS_FMT_YUYV8_1X16:
+ fmtidx = 3;
+ break;
+ default:
+ WARN(1, KERN_ERR "CSI2: pixel format %08x unsupported!\n",
+ fmt->code);
+ return 0;
+ }
+
+ if (!(csi2->output & CSI2_OUTPUT_IPIPEIF) &&
+ !(csi2->output & CSI2_OUTPUT_MEMORY)) {
+ /* Neither output enabled is a valid combination */
+ return CSI2_PIX_FMT_OTHERS;
+ }
+
+ /* If we need to skip frames at the beginning of the stream disable the
+ * video port to avoid sending the skipped frames to the IPIPEIF.
+ */
+ destidx = csi2->frame_skip ? 0 : !!(csi2->output & CSI2_OUTPUT_IPIPEIF);
+
+ return __csi2_fmt_map[fmtidx][destidx][csi2->dpcm_decompress];
+}
+
+/*
+ * csi2_set_outaddr - Set memory address to save output image
+ * @csi2: Pointer to ISS CSI2a device.
+ * @addr: 32-bit memory address aligned on 32 byte boundary.
+ *
+ * Sets the memory address where the output will be saved.
+ *
+ * Returns 0 if successful, or -EINVAL if the address is not in the 32 byte
+ * boundary.
+ */
+static void csi2_set_outaddr(struct iss_csi2_device *csi2, u32 addr)
+{
+ struct iss_csi2_ctx_cfg *ctx = &csi2->contexts[0];
+
+ ctx->ping_addr = addr;
+ ctx->pong_addr = addr;
+ iss_reg_write(csi2->iss, csi2->regs1, CSI2_CTX_PING_ADDR(ctx->ctxnum),
+ ctx->ping_addr);
+ iss_reg_write(csi2->iss, csi2->regs1, CSI2_CTX_PONG_ADDR(ctx->ctxnum),
+ ctx->pong_addr);
+}
+
+/*
+ * is_usr_def_mapping - Checks whether USER_DEF_MAPPING should
+ * be enabled by CSI2.
+ * @format_id: mapped format id
+ *
+ */
+static inline int is_usr_def_mapping(u32 format_id)
+{
+ return (format_id & 0xf0) == 0x40 ? 1 : 0;
+}
+
+/*
+ * csi2_ctx_enable - Enable specified CSI2 context
+ * @ctxnum: Context number, valid between 0 and 7 values.
+ * @enable: enable
+ *
+ */
+static void csi2_ctx_enable(struct iss_csi2_device *csi2, u8 ctxnum, u8 enable)
+{
+ struct iss_csi2_ctx_cfg *ctx = &csi2->contexts[ctxnum];
+ u32 reg;
+
+ reg = iss_reg_read(csi2->iss, csi2->regs1, CSI2_CTX_CTRL1(ctxnum));
+
+ if (enable) {
+ unsigned int skip = 0;
+
+ if (csi2->frame_skip)
+ skip = csi2->frame_skip;
+ else if (csi2->output & CSI2_OUTPUT_MEMORY)
+ skip = 1;
+
+ reg &= ~CSI2_CTX_CTRL1_COUNT_MASK;
+ reg |= CSI2_CTX_CTRL1_COUNT_UNLOCK
+ | (skip << CSI2_CTX_CTRL1_COUNT_SHIFT)
+ | CSI2_CTX_CTRL1_CTX_EN;
+ } else {
+ reg &= ~CSI2_CTX_CTRL1_CTX_EN;
+ }
+
+ iss_reg_write(csi2->iss, csi2->regs1, CSI2_CTX_CTRL1(ctxnum), reg);
+ ctx->enabled = enable;
+}
+
+/*
+ * csi2_ctx_config - CSI2 context configuration.
+ * @ctx: context configuration
+ *
+ */
+static void csi2_ctx_config(struct iss_csi2_device *csi2,
+ struct iss_csi2_ctx_cfg *ctx)
+{
+ u32 reg = 0;
+
+ ctx->frame = 0;
+
+ /* Set up CSI2_CTx_CTRL1 */
+ if (ctx->eof_enabled)
+ reg = CSI2_CTX_CTRL1_EOF_EN;
+
+ if (ctx->eol_enabled)
+ reg |= CSI2_CTX_CTRL1_EOL_EN;
+
+ if (ctx->checksum_enabled)
+ reg |= CSI2_CTX_CTRL1_CS_EN;
+
+ iss_reg_write(csi2->iss, csi2->regs1, CSI2_CTX_CTRL1(ctx->ctxnum), reg);
+
+ /* Set up CSI2_CTx_CTRL2 */
+ reg = ctx->virtual_id << CSI2_CTX_CTRL2_VIRTUAL_ID_SHIFT;
+ reg |= ctx->format_id << CSI2_CTX_CTRL2_FORMAT_SHIFT;
+
+ if (ctx->dpcm_decompress && ctx->dpcm_predictor)
+ reg |= CSI2_CTX_CTRL2_DPCM_PRED;
+
+ if (is_usr_def_mapping(ctx->format_id))
+ reg |= 2 << CSI2_CTX_CTRL2_USER_DEF_MAP_SHIFT;
+
+ iss_reg_write(csi2->iss, csi2->regs1, CSI2_CTX_CTRL2(ctx->ctxnum), reg);
+
+ /* Set up CSI2_CTx_CTRL3 */
+ iss_reg_write(csi2->iss, csi2->regs1, CSI2_CTX_CTRL3(ctx->ctxnum),
+ ctx->alpha << CSI2_CTX_CTRL3_ALPHA_SHIFT);
+
+ /* Set up CSI2_CTx_DAT_OFST */
+ iss_reg_update(csi2->iss, csi2->regs1, CSI2_CTX_DAT_OFST(ctx->ctxnum),
+ CSI2_CTX_DAT_OFST_MASK, ctx->data_offset);
+
+ iss_reg_write(csi2->iss, csi2->regs1, CSI2_CTX_PING_ADDR(ctx->ctxnum),
+ ctx->ping_addr);
+ iss_reg_write(csi2->iss, csi2->regs1, CSI2_CTX_PONG_ADDR(ctx->ctxnum),
+ ctx->pong_addr);
+}
+
+/*
+ * csi2_timing_config - CSI2 timing configuration.
+ * @timing: csi2_timing_cfg structure
+ */
+static void csi2_timing_config(struct iss_csi2_device *csi2,
+ struct iss_csi2_timing_cfg *timing)
+{
+ u32 reg;
+
+ reg = iss_reg_read(csi2->iss, csi2->regs1, CSI2_TIMING);
+
+ if (timing->force_rx_mode)
+ reg |= CSI2_TIMING_FORCE_RX_MODE_IO1;
+ else
+ reg &= ~CSI2_TIMING_FORCE_RX_MODE_IO1;
+
+ if (timing->stop_state_16x)
+ reg |= CSI2_TIMING_STOP_STATE_X16_IO1;
+ else
+ reg &= ~CSI2_TIMING_STOP_STATE_X16_IO1;
+
+ if (timing->stop_state_4x)
+ reg |= CSI2_TIMING_STOP_STATE_X4_IO1;
+ else
+ reg &= ~CSI2_TIMING_STOP_STATE_X4_IO1;
+
+ reg &= ~CSI2_TIMING_STOP_STATE_COUNTER_IO1_MASK;
+ reg |= timing->stop_state_counter <<
+ CSI2_TIMING_STOP_STATE_COUNTER_IO1_SHIFT;
+
+ iss_reg_write(csi2->iss, csi2->regs1, CSI2_TIMING, reg);
+}
+
+/*
+ * csi2_irq_ctx_set - Enables CSI2 Context IRQs.
+ * @enable: Enable/disable CSI2 Context interrupts
+ */
+static void csi2_irq_ctx_set(struct iss_csi2_device *csi2, int enable)
+{
+ const u32 mask = CSI2_CTX_IRQ_FE | CSI2_CTX_IRQ_FS;
+ int i;
+
+ for (i = 0; i < 8; i++) {
+ iss_reg_write(csi2->iss, csi2->regs1, CSI2_CTX_IRQSTATUS(i),
+ mask);
+ if (enable)
+ iss_reg_set(csi2->iss, csi2->regs1,
+ CSI2_CTX_IRQENABLE(i), mask);
+ else
+ iss_reg_clr(csi2->iss, csi2->regs1,
+ CSI2_CTX_IRQENABLE(i), mask);
+ }
+}
+
+/*
+ * csi2_irq_complexio1_set - Enables CSI2 ComplexIO IRQs.
+ * @enable: Enable/disable CSI2 ComplexIO #1 interrupts
+ */
+static void csi2_irq_complexio1_set(struct iss_csi2_device *csi2, int enable)
+{
+ u32 reg;
+
+ reg = CSI2_COMPLEXIO_IRQ_STATEALLULPMEXIT |
+ CSI2_COMPLEXIO_IRQ_STATEALLULPMENTER |
+ CSI2_COMPLEXIO_IRQ_STATEULPM5 |
+ CSI2_COMPLEXIO_IRQ_ERRCONTROL5 |
+ CSI2_COMPLEXIO_IRQ_ERRESC5 |
+ CSI2_COMPLEXIO_IRQ_ERRSOTSYNCHS5 |
+ CSI2_COMPLEXIO_IRQ_ERRSOTHS5 |
+ CSI2_COMPLEXIO_IRQ_STATEULPM4 |
+ CSI2_COMPLEXIO_IRQ_ERRCONTROL4 |
+ CSI2_COMPLEXIO_IRQ_ERRESC4 |
+ CSI2_COMPLEXIO_IRQ_ERRSOTSYNCHS4 |
+ CSI2_COMPLEXIO_IRQ_ERRSOTHS4 |
+ CSI2_COMPLEXIO_IRQ_STATEULPM3 |
+ CSI2_COMPLEXIO_IRQ_ERRCONTROL3 |
+ CSI2_COMPLEXIO_IRQ_ERRESC3 |
+ CSI2_COMPLEXIO_IRQ_ERRSOTSYNCHS3 |
+ CSI2_COMPLEXIO_IRQ_ERRSOTHS3 |
+ CSI2_COMPLEXIO_IRQ_STATEULPM2 |
+ CSI2_COMPLEXIO_IRQ_ERRCONTROL2 |
+ CSI2_COMPLEXIO_IRQ_ERRESC2 |
+ CSI2_COMPLEXIO_IRQ_ERRSOTSYNCHS2 |
+ CSI2_COMPLEXIO_IRQ_ERRSOTHS2 |
+ CSI2_COMPLEXIO_IRQ_STATEULPM1 |
+ CSI2_COMPLEXIO_IRQ_ERRCONTROL1 |
+ CSI2_COMPLEXIO_IRQ_ERRESC1 |
+ CSI2_COMPLEXIO_IRQ_ERRSOTSYNCHS1 |
+ CSI2_COMPLEXIO_IRQ_ERRSOTHS1;
+ iss_reg_write(csi2->iss, csi2->regs1, CSI2_COMPLEXIO_IRQSTATUS, reg);
+ if (enable)
+ iss_reg_set(csi2->iss, csi2->regs1, CSI2_COMPLEXIO_IRQENABLE,
+ reg);
+ else
+ iss_reg_write(csi2->iss, csi2->regs1, CSI2_COMPLEXIO_IRQENABLE,
+ 0);
+}
+
+/*
+ * csi2_irq_status_set - Enables CSI2 Status IRQs.
+ * @enable: Enable/disable CSI2 Status interrupts
+ */
+static void csi2_irq_status_set(struct iss_csi2_device *csi2, int enable)
+{
+ u32 reg;
+
+ reg = CSI2_IRQ_OCP_ERR |
+ CSI2_IRQ_SHORT_PACKET |
+ CSI2_IRQ_ECC_CORRECTION |
+ CSI2_IRQ_ECC_NO_CORRECTION |
+ CSI2_IRQ_COMPLEXIO_ERR |
+ CSI2_IRQ_FIFO_OVF |
+ CSI2_IRQ_CONTEXT0;
+ iss_reg_write(csi2->iss, csi2->regs1, CSI2_IRQSTATUS, reg);
+ if (enable)
+ iss_reg_set(csi2->iss, csi2->regs1, CSI2_IRQENABLE, reg);
+ else
+ iss_reg_write(csi2->iss, csi2->regs1, CSI2_IRQENABLE, 0);
+}
+
+/*
+ * omap4iss_csi2_reset - Resets the CSI2 module.
+ *
+ * Must be called with the phy lock held.
+ *
+ * Returns 0 if successful, or -EBUSY if power command didn't respond.
+ */
+int omap4iss_csi2_reset(struct iss_csi2_device *csi2)
+{
+ unsigned int timeout;
+
+ if (!csi2->available)
+ return -ENODEV;
+
+ if (csi2->phy->phy_in_use)
+ return -EBUSY;
+
+ iss_reg_set(csi2->iss, csi2->regs1, CSI2_SYSCONFIG,
+ CSI2_SYSCONFIG_SOFT_RESET);
+
+ timeout = iss_poll_condition_timeout(
+ iss_reg_read(csi2->iss, csi2->regs1, CSI2_SYSSTATUS) &
+ CSI2_SYSSTATUS_RESET_DONE, 500, 100, 200);
+ if (timeout) {
+ dev_err(csi2->iss->dev, "CSI2: Soft reset timeout!\n");
+ return -EBUSY;
+ }
+
+ iss_reg_set(csi2->iss, csi2->regs1, CSI2_COMPLEXIO_CFG,
+ CSI2_COMPLEXIO_CFG_RESET_CTRL);
+
+ timeout = iss_poll_condition_timeout(
+ iss_reg_read(csi2->iss, csi2->phy->phy_regs, REGISTER1) &
+ REGISTER1_RESET_DONE_CTRLCLK, 10000, 100, 500);
+ if (timeout) {
+ dev_err(csi2->iss->dev, "CSI2: CSI2_96M_FCLK reset timeout!\n");
+ return -EBUSY;
+ }
+
+ iss_reg_update(csi2->iss, csi2->regs1, CSI2_SYSCONFIG,
+ CSI2_SYSCONFIG_MSTANDBY_MODE_MASK |
+ CSI2_SYSCONFIG_AUTO_IDLE,
+ CSI2_SYSCONFIG_MSTANDBY_MODE_NO);
+
+ return 0;
+}
+
+static int csi2_configure(struct iss_csi2_device *csi2)
+{
+ const struct iss_v4l2_subdevs_group *pdata;
+ struct iss_csi2_timing_cfg *timing = &csi2->timing[0];
+ struct v4l2_subdev *sensor;
+ struct media_pad *pad;
+
+ /*
+ * CSI2 fields that can be updated while the context has
+ * been enabled or the interface has been enabled are not
+ * updated dynamically currently. So we do not allow to
+ * reconfigure if either has been enabled
+ */
+ if (csi2->contexts[0].enabled || csi2->ctrl.if_enable)
+ return -EBUSY;
+
+ pad = media_entity_remote_pad(&csi2->pads[CSI2_PAD_SINK]);
+ sensor = media_entity_to_v4l2_subdev(pad->entity);
+ pdata = sensor->host_priv;
+
+ csi2->frame_skip = 0;
+ v4l2_subdev_call(sensor, sensor, g_skip_frames, &csi2->frame_skip);
+
+ csi2->ctrl.vp_out_ctrl = pdata->bus.csi2.vpclk_div;
+ csi2->ctrl.frame_mode = ISS_CSI2_FRAME_IMMEDIATE;
+ csi2->ctrl.ecc_enable = pdata->bus.csi2.crc;
+
+ timing->force_rx_mode = 1;
+ timing->stop_state_16x = 1;
+ timing->stop_state_4x = 1;
+ timing->stop_state_counter = 0x1ff;
+
+ /*
+ * The CSI2 receiver can't do any format conversion except DPCM
+ * decompression, so every set_format call configures both pads
+ * and enables DPCM decompression as a special case:
+ */
+ if (csi2->formats[CSI2_PAD_SINK].code !=
+ csi2->formats[CSI2_PAD_SOURCE].code)
+ csi2->dpcm_decompress = true;
+ else
+ csi2->dpcm_decompress = false;
+
+ csi2->contexts[0].format_id = csi2_ctx_map_format(csi2);
+
+ if (csi2->video_out.bpl_padding == 0)
+ csi2->contexts[0].data_offset = 0;
+ else
+ csi2->contexts[0].data_offset = csi2->video_out.bpl_value;
+
+ /*
+ * Enable end of frame and end of line signals generation for
+ * context 0. These signals are generated from CSI2 receiver to
+ * qualify the last pixel of a frame and the last pixel of a line.
+ * Without enabling the signals CSI2 receiver writes data to memory
+ * beyond buffer size and/or data line offset is not handled correctly.
+ */
+ csi2->contexts[0].eof_enabled = 1;
+ csi2->contexts[0].eol_enabled = 1;
+
+ csi2_irq_complexio1_set(csi2, 1);
+ csi2_irq_ctx_set(csi2, 1);
+ csi2_irq_status_set(csi2, 1);
+
+ /* Set configuration (timings, format and links) */
+ csi2_timing_config(csi2, timing);
+ csi2_recv_config(csi2, &csi2->ctrl);
+ csi2_ctx_config(csi2, &csi2->contexts[0]);
+
+ return 0;
+}
+
+/*
+ * csi2_print_status - Prints CSI2 debug information.
+ */
+#define CSI2_PRINT_REGISTER(iss, regs, name)\
+ dev_dbg(iss->dev, "###CSI2 " #name "=0x%08x\n", \
+ iss_reg_read(iss, regs, CSI2_##name))
+
+static void csi2_print_status(struct iss_csi2_device *csi2)
+{
+ struct iss_device *iss = csi2->iss;
+
+ if (!csi2->available)
+ return;
+
+ dev_dbg(iss->dev, "-------------CSI2 Register dump-------------\n");
+
+ CSI2_PRINT_REGISTER(iss, csi2->regs1, SYSCONFIG);
+ CSI2_PRINT_REGISTER(iss, csi2->regs1, SYSSTATUS);
+ CSI2_PRINT_REGISTER(iss, csi2->regs1, IRQENABLE);
+ CSI2_PRINT_REGISTER(iss, csi2->regs1, IRQSTATUS);
+ CSI2_PRINT_REGISTER(iss, csi2->regs1, CTRL);
+ CSI2_PRINT_REGISTER(iss, csi2->regs1, DBG_H);
+ CSI2_PRINT_REGISTER(iss, csi2->regs1, COMPLEXIO_CFG);
+ CSI2_PRINT_REGISTER(iss, csi2->regs1, COMPLEXIO_IRQSTATUS);
+ CSI2_PRINT_REGISTER(iss, csi2->regs1, SHORT_PACKET);
+ CSI2_PRINT_REGISTER(iss, csi2->regs1, COMPLEXIO_IRQENABLE);
+ CSI2_PRINT_REGISTER(iss, csi2->regs1, DBG_P);
+ CSI2_PRINT_REGISTER(iss, csi2->regs1, TIMING);
+ CSI2_PRINT_REGISTER(iss, csi2->regs1, CTX_CTRL1(0));
+ CSI2_PRINT_REGISTER(iss, csi2->regs1, CTX_CTRL2(0));
+ CSI2_PRINT_REGISTER(iss, csi2->regs1, CTX_DAT_OFST(0));
+ CSI2_PRINT_REGISTER(iss, csi2->regs1, CTX_PING_ADDR(0));
+ CSI2_PRINT_REGISTER(iss, csi2->regs1, CTX_PONG_ADDR(0));
+ CSI2_PRINT_REGISTER(iss, csi2->regs1, CTX_IRQENABLE(0));
+ CSI2_PRINT_REGISTER(iss, csi2->regs1, CTX_IRQSTATUS(0));
+ CSI2_PRINT_REGISTER(iss, csi2->regs1, CTX_CTRL3(0));
+
+ dev_dbg(iss->dev, "--------------------------------------------\n");
+}
+
+/* -----------------------------------------------------------------------------
+ * Interrupt handling
+ */
+
+/*
+ * csi2_isr_buffer - Does buffer handling at end-of-frame
+ * when writing to memory.
+ */
+static void csi2_isr_buffer(struct iss_csi2_device *csi2)
+{
+ struct iss_buffer *buffer;
+
+ csi2_ctx_enable(csi2, 0, 0);
+
+ buffer = omap4iss_video_buffer_next(&csi2->video_out);
+
+ /*
+ * Let video queue operation restart engine if there is an underrun
+ * condition.
+ */
+ if (buffer == NULL)
+ return;
+
+ csi2_set_outaddr(csi2, buffer->iss_addr);
+ csi2_ctx_enable(csi2, 0, 1);
+}
+
+static void csi2_isr_ctx(struct iss_csi2_device *csi2,
+ struct iss_csi2_ctx_cfg *ctx)
+{
+ unsigned int n = ctx->ctxnum;
+ u32 status;
+
+ status = iss_reg_read(csi2->iss, csi2->regs1, CSI2_CTX_IRQSTATUS(n));
+ iss_reg_write(csi2->iss, csi2->regs1, CSI2_CTX_IRQSTATUS(n), status);
+
+ /* Propagate frame number */
+ if (status & CSI2_CTX_IRQ_FS) {
+ struct iss_pipeline *pipe =
+ to_iss_pipeline(&csi2->subdev.entity);
+ u16 frame;
+ u16 delta;
+
+ frame = iss_reg_read(csi2->iss, csi2->regs1,
+ CSI2_CTX_CTRL2(ctx->ctxnum))
+ >> CSI2_CTX_CTRL2_FRAME_SHIFT;
+
+ if (frame == 0) {
+ /* A zero value means that the counter isn't implemented
+ * by the source. Increment the frame number in software
+ * in that case.
+ */
+ atomic_inc(&pipe->frame_number);
+ } else {
+ /* Extend the 16 bit frame number to 32 bits by
+ * computing the delta between two consecutive CSI2
+ * frame numbers and adding it to the software frame
+ * number. The hardware counter starts at 1 and wraps
+ * from 0xffff to 1 without going through 0, so subtract
+ * 1 when the counter wraps.
+ */
+ delta = frame - ctx->frame;
+ if (frame < ctx->frame)
+ delta--;
+ ctx->frame = frame;
+
+ atomic_add(delta, &pipe->frame_number);
+ }
+ }
+
+ if (!(status & CSI2_CTX_IRQ_FE))
+ return;
+
+ /* Skip interrupts until we reach the frame skip count. The CSI2 will be
+ * automatically disabled, as the frame skip count has been programmed
+ * in the CSI2_CTx_CTRL1::COUNT field, so reenable it.
+ *
+ * It would have been nice to rely on the FRAME_NUMBER interrupt instead
+ * but it turned out that the interrupt is only generated when the CSI2
+ * writes to memory (the CSI2_CTx_CTRL1::COUNT field is decreased
+ * correctly and reaches 0 when data is forwarded to the video port only
+ * but no interrupt arrives). Maybe a CSI2 hardware bug.
+ */
+ if (csi2->frame_skip) {
+ csi2->frame_skip--;
+ if (csi2->frame_skip == 0) {
+ ctx->format_id = csi2_ctx_map_format(csi2);
+ csi2_ctx_config(csi2, ctx);
+ csi2_ctx_enable(csi2, n, 1);
+ }
+ return;
+ }
+
+ if (csi2->output & CSI2_OUTPUT_MEMORY)
+ csi2_isr_buffer(csi2);
+}
+
+/*
+ * omap4iss_csi2_isr - CSI2 interrupt handling.
+ */
+void omap4iss_csi2_isr(struct iss_csi2_device *csi2)
+{
+ struct iss_pipeline *pipe = to_iss_pipeline(&csi2->subdev.entity);
+ u32 csi2_irqstatus, cpxio1_irqstatus;
+ struct iss_device *iss = csi2->iss;
+
+ if (!csi2->available)
+ return;
+
+ csi2_irqstatus = iss_reg_read(csi2->iss, csi2->regs1, CSI2_IRQSTATUS);
+ iss_reg_write(csi2->iss, csi2->regs1, CSI2_IRQSTATUS, csi2_irqstatus);
+
+ /* Failure Cases */
+ if (csi2_irqstatus & CSI2_IRQ_COMPLEXIO_ERR) {
+ cpxio1_irqstatus = iss_reg_read(csi2->iss, csi2->regs1,
+ CSI2_COMPLEXIO_IRQSTATUS);
+ iss_reg_write(csi2->iss, csi2->regs1, CSI2_COMPLEXIO_IRQSTATUS,
+ cpxio1_irqstatus);
+ dev_dbg(iss->dev, "CSI2: ComplexIO Error IRQ %x\n",
+ cpxio1_irqstatus);
+ pipe->error = true;
+ }
+
+ if (csi2_irqstatus & (CSI2_IRQ_OCP_ERR |
+ CSI2_IRQ_SHORT_PACKET |
+ CSI2_IRQ_ECC_NO_CORRECTION |
+ CSI2_IRQ_COMPLEXIO_ERR |
+ CSI2_IRQ_FIFO_OVF)) {
+ dev_dbg(iss->dev,
+ "CSI2 Err: OCP:%d SHORT:%d ECC:%d CPXIO:%d OVF:%d\n",
+ csi2_irqstatus & CSI2_IRQ_OCP_ERR ? 1 : 0,
+ csi2_irqstatus & CSI2_IRQ_SHORT_PACKET ? 1 : 0,
+ csi2_irqstatus & CSI2_IRQ_ECC_NO_CORRECTION ? 1 : 0,
+ csi2_irqstatus & CSI2_IRQ_COMPLEXIO_ERR ? 1 : 0,
+ csi2_irqstatus & CSI2_IRQ_FIFO_OVF ? 1 : 0);
+ pipe->error = true;
+ }
+
+ if (omap4iss_module_sync_is_stopping(&csi2->wait, &csi2->stopping))
+ return;
+
+ /* Successful cases */
+ if (csi2_irqstatus & CSI2_IRQ_CONTEXT0)
+ csi2_isr_ctx(csi2, &csi2->contexts[0]);
+
+ if (csi2_irqstatus & CSI2_IRQ_ECC_CORRECTION)
+ dev_dbg(iss->dev, "CSI2: ECC correction done\n");
+}
+
+/* -----------------------------------------------------------------------------
+ * ISS video operations
+ */
+
+/*
+ * csi2_queue - Queues the first buffer when using memory output
+ * @video: The video node
+ * @buffer: buffer to queue
+ */
+static int csi2_queue(struct iss_video *video, struct iss_buffer *buffer)
+{
+ struct iss_csi2_device *csi2 = container_of(video,
+ struct iss_csi2_device, video_out);
+
+ csi2_set_outaddr(csi2, buffer->iss_addr);
+
+ /*
+ * If streaming was enabled before there was a buffer queued
+ * or underrun happened in the ISR, the hardware was not enabled
+ * and DMA queue flag ISS_VIDEO_DMAQUEUE_UNDERRUN is still set.
+ * Enable it now.
+ */
+ if (csi2->video_out.dmaqueue_flags & ISS_VIDEO_DMAQUEUE_UNDERRUN) {
+ /* Enable / disable context 0 and IRQs */
+ csi2_if_enable(csi2, 1);
+ csi2_ctx_enable(csi2, 0, 1);
+ iss_video_dmaqueue_flags_clr(&csi2->video_out);
+ }
+
+ return 0;
+}
+
+static const struct iss_video_operations csi2_issvideo_ops = {
+ .queue = csi2_queue,
+};
+
+/* -----------------------------------------------------------------------------
+ * V4L2 subdev operations
+ */
+
+static struct v4l2_mbus_framefmt *
+__csi2_get_format(struct iss_csi2_device *csi2, struct v4l2_subdev_pad_config *cfg,
+ unsigned int pad, enum v4l2_subdev_format_whence which)
+{
+ if (which == V4L2_SUBDEV_FORMAT_TRY)
+ return v4l2_subdev_get_try_format(&csi2->subdev, cfg, pad);
+
+ return &csi2->formats[pad];
+}
+
+static void
+csi2_try_format(struct iss_csi2_device *csi2, struct v4l2_subdev_pad_config *cfg,
+ unsigned int pad, struct v4l2_mbus_framefmt *fmt,
+ enum v4l2_subdev_format_whence which)
+{
+ u32 pixelcode;
+ struct v4l2_mbus_framefmt *format;
+ const struct iss_format_info *info;
+ unsigned int i;
+
+ switch (pad) {
+ case CSI2_PAD_SINK:
+ /* Clamp the width and height to valid range (1-8191). */
+ for (i = 0; i < ARRAY_SIZE(csi2_input_fmts); i++) {
+ if (fmt->code == csi2_input_fmts[i])
+ break;
+ }
+
+ /* If not found, use SGRBG10 as default */
+ if (i >= ARRAY_SIZE(csi2_input_fmts))
+ fmt->code = MEDIA_BUS_FMT_SGRBG10_1X10;
+
+ fmt->width = clamp_t(u32, fmt->width, 1, 8191);
+ fmt->height = clamp_t(u32, fmt->height, 1, 8191);
+ break;
+
+ case CSI2_PAD_SOURCE:
+ /* Source format same as sink format, except for DPCM
+ * compression.
+ */
+ pixelcode = fmt->code;
+ format = __csi2_get_format(csi2, cfg, CSI2_PAD_SINK, which);
+ memcpy(fmt, format, sizeof(*fmt));
+
+ /*
+ * Only Allow DPCM decompression, and check that the
+ * pattern is preserved
+ */
+ info = omap4iss_video_format_info(fmt->code);
+ if (info->uncompressed == pixelcode)
+ fmt->code = pixelcode;
+ break;
+ }
+
+ /* RGB, non-interlaced */
+ fmt->colorspace = V4L2_COLORSPACE_SRGB;
+ fmt->field = V4L2_FIELD_NONE;
+}
+
+/*
+ * csi2_enum_mbus_code - Handle pixel format enumeration
+ * @sd : pointer to v4l2 subdev structure
+ * @cfg : V4L2 subdev pad config
+ * @code : pointer to v4l2_subdev_mbus_code_enum structure
+ * return -EINVAL or zero on success
+ */
+static int csi2_enum_mbus_code(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_mbus_code_enum *code)
+{
+ struct iss_csi2_device *csi2 = v4l2_get_subdevdata(sd);
+ struct v4l2_mbus_framefmt *format;
+ const struct iss_format_info *info;
+
+ if (code->pad == CSI2_PAD_SINK) {
+ if (code->index >= ARRAY_SIZE(csi2_input_fmts))
+ return -EINVAL;
+
+ code->code = csi2_input_fmts[code->index];
+ } else {
+ format = __csi2_get_format(csi2, cfg, CSI2_PAD_SINK,
+ code->which);
+ switch (code->index) {
+ case 0:
+ /* Passthrough sink pad code */
+ code->code = format->code;
+ break;
+ case 1:
+ /* Uncompressed code */
+ info = omap4iss_video_format_info(format->code);
+ if (info->uncompressed == format->code)
+ return -EINVAL;
+
+ code->code = info->uncompressed;
+ break;
+ default:
+ return -EINVAL;
+ }
+ }
+
+ return 0;
+}
+
+static int csi2_enum_frame_size(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_frame_size_enum *fse)
+{
+ struct iss_csi2_device *csi2 = v4l2_get_subdevdata(sd);
+ struct v4l2_mbus_framefmt format;
+
+ if (fse->index != 0)
+ return -EINVAL;
+
+ format.code = fse->code;
+ format.width = 1;
+ format.height = 1;
+ csi2_try_format(csi2, cfg, fse->pad, &format, fse->which);
+ fse->min_width = format.width;
+ fse->min_height = format.height;
+
+ if (format.code != fse->code)
+ return -EINVAL;
+
+ format.code = fse->code;
+ format.width = -1;
+ format.height = -1;
+ csi2_try_format(csi2, cfg, fse->pad, &format, fse->which);
+ fse->max_width = format.width;
+ fse->max_height = format.height;
+
+ return 0;
+}
+
+/*
+ * csi2_get_format - Handle get format by pads subdev method
+ * @sd : pointer to v4l2 subdev structure
+ * @cfg: V4L2 subdev pad config
+ * @fmt: pointer to v4l2 subdev format structure
+ * return -EINVAL or zero on success
+ */
+static int csi2_get_format(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_format *fmt)
+{
+ struct iss_csi2_device *csi2 = v4l2_get_subdevdata(sd);
+ struct v4l2_mbus_framefmt *format;
+
+ format = __csi2_get_format(csi2, cfg, fmt->pad, fmt->which);
+ if (format == NULL)
+ return -EINVAL;
+
+ fmt->format = *format;
+ return 0;
+}
+
+/*
+ * csi2_set_format - Handle set format by pads subdev method
+ * @sd : pointer to v4l2 subdev structure
+ * @cfg: V4L2 subdev pad config
+ * @fmt: pointer to v4l2 subdev format structure
+ * return -EINVAL or zero on success
+ */
+static int csi2_set_format(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_format *fmt)
+{
+ struct iss_csi2_device *csi2 = v4l2_get_subdevdata(sd);
+ struct v4l2_mbus_framefmt *format;
+
+ format = __csi2_get_format(csi2, cfg, fmt->pad, fmt->which);
+ if (format == NULL)
+ return -EINVAL;
+
+ csi2_try_format(csi2, cfg, fmt->pad, &fmt->format, fmt->which);
+ *format = fmt->format;
+
+ /* Propagate the format from sink to source */
+ if (fmt->pad == CSI2_PAD_SINK) {
+ format = __csi2_get_format(csi2, cfg, CSI2_PAD_SOURCE,
+ fmt->which);
+ *format = fmt->format;
+ csi2_try_format(csi2, cfg, CSI2_PAD_SOURCE, format, fmt->which);
+ }
+
+ return 0;
+}
+
+static int csi2_link_validate(struct v4l2_subdev *sd, struct media_link *link,
+ struct v4l2_subdev_format *source_fmt,
+ struct v4l2_subdev_format *sink_fmt)
+{
+ struct iss_csi2_device *csi2 = v4l2_get_subdevdata(sd);
+ struct iss_pipeline *pipe = to_iss_pipeline(&csi2->subdev.entity);
+ int rval;
+
+ pipe->external = media_entity_to_v4l2_subdev(link->source->entity);
+ rval = omap4iss_get_external_info(pipe, link);
+ if (rval < 0)
+ return rval;
+
+ return v4l2_subdev_link_validate_default(sd, link, source_fmt,
+ sink_fmt);
+}
+
+/*
+ * csi2_init_formats - Initialize formats on all pads
+ * @sd: ISS CSI2 V4L2 subdevice
+ * @fh: V4L2 subdev file handle
+ *
+ * Initialize all pad formats with default values. If fh is not NULL, try
+ * formats are initialized on the file handle. Otherwise active formats are
+ * initialized on the device.
+ */
+static int csi2_init_formats(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
+{
+ struct v4l2_subdev_format format;
+
+ memset(&format, 0, sizeof(format));
+ format.pad = CSI2_PAD_SINK;
+ format.which = fh ? V4L2_SUBDEV_FORMAT_TRY : V4L2_SUBDEV_FORMAT_ACTIVE;
+ format.format.code = MEDIA_BUS_FMT_SGRBG10_1X10;
+ format.format.width = 4096;
+ format.format.height = 4096;
+ csi2_set_format(sd, fh ? fh->pad : NULL, &format);
+
+ return 0;
+}
+
+/*
+ * csi2_set_stream - Enable/Disable streaming on the CSI2 module
+ * @sd: ISS CSI2 V4L2 subdevice
+ * @enable: ISS pipeline stream state
+ *
+ * Return 0 on success or a negative error code otherwise.
+ */
+static int csi2_set_stream(struct v4l2_subdev *sd, int enable)
+{
+ struct iss_csi2_device *csi2 = v4l2_get_subdevdata(sd);
+ struct iss_device *iss = csi2->iss;
+ struct iss_video *video_out = &csi2->video_out;
+ int ret = 0;
+
+ if (csi2->state == ISS_PIPELINE_STREAM_STOPPED) {
+ if (enable == ISS_PIPELINE_STREAM_STOPPED)
+ return 0;
+
+ omap4iss_subclk_enable(iss, csi2->subclk);
+ }
+
+ switch (enable) {
+ case ISS_PIPELINE_STREAM_CONTINUOUS: {
+ ret = omap4iss_csiphy_config(iss, sd);
+ if (ret < 0)
+ return ret;
+
+ if (omap4iss_csiphy_acquire(csi2->phy) < 0)
+ return -ENODEV;
+ csi2_configure(csi2);
+ csi2_print_status(csi2);
+
+ /*
+ * When outputting to memory with no buffer available, let the
+ * buffer queue handler start the hardware. A DMA queue flag
+ * ISS_VIDEO_DMAQUEUE_QUEUED will be set as soon as there is
+ * a buffer available.
+ */
+ if (csi2->output & CSI2_OUTPUT_MEMORY &&
+ !(video_out->dmaqueue_flags & ISS_VIDEO_DMAQUEUE_QUEUED))
+ break;
+ /* Enable context 0 and IRQs */
+ atomic_set(&csi2->stopping, 0);
+ csi2_ctx_enable(csi2, 0, 1);
+ csi2_if_enable(csi2, 1);
+ iss_video_dmaqueue_flags_clr(video_out);
+ break;
+ }
+ case ISS_PIPELINE_STREAM_STOPPED:
+ if (csi2->state == ISS_PIPELINE_STREAM_STOPPED)
+ return 0;
+ if (omap4iss_module_sync_idle(&sd->entity, &csi2->wait,
+ &csi2->stopping))
+ ret = -ETIMEDOUT;
+ csi2_ctx_enable(csi2, 0, 0);
+ csi2_if_enable(csi2, 0);
+ csi2_irq_ctx_set(csi2, 0);
+ omap4iss_csiphy_release(csi2->phy);
+ omap4iss_subclk_disable(iss, csi2->subclk);
+ iss_video_dmaqueue_flags_clr(video_out);
+ break;
+ }
+
+ csi2->state = enable;
+ return ret;
+}
+
+/* subdev video operations */
+static const struct v4l2_subdev_video_ops csi2_video_ops = {
+ .s_stream = csi2_set_stream,
+};
+
+/* subdev pad operations */
+static const struct v4l2_subdev_pad_ops csi2_pad_ops = {
+ .enum_mbus_code = csi2_enum_mbus_code,
+ .enum_frame_size = csi2_enum_frame_size,
+ .get_fmt = csi2_get_format,
+ .set_fmt = csi2_set_format,
+ .link_validate = csi2_link_validate,
+};
+
+/* subdev operations */
+static const struct v4l2_subdev_ops csi2_ops = {
+ .video = &csi2_video_ops,
+ .pad = &csi2_pad_ops,
+};
+
+/* subdev internal operations */
+static const struct v4l2_subdev_internal_ops csi2_internal_ops = {
+ .open = csi2_init_formats,
+};
+
+/* -----------------------------------------------------------------------------
+ * Media entity operations
+ */
+
+/*
+ * csi2_link_setup - Setup CSI2 connections.
+ * @entity : Pointer to media entity structure
+ * @local : Pointer to local pad array
+ * @remote : Pointer to remote pad array
+ * @flags : Link flags
+ * return -EINVAL or zero on success
+ */
+static int csi2_link_setup(struct media_entity *entity,
+ const struct media_pad *local,
+ const struct media_pad *remote, u32 flags)
+{
+ struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity);
+ struct iss_csi2_device *csi2 = v4l2_get_subdevdata(sd);
+ struct iss_csi2_ctrl_cfg *ctrl = &csi2->ctrl;
+
+ /*
+ * The ISS core doesn't support pipelines with multiple video outputs.
+ * Revisit this when it will be implemented, and return -EBUSY for now.
+ */
+
+ switch (local->index | media_entity_type(remote->entity)) {
+ case CSI2_PAD_SOURCE | MEDIA_ENT_T_DEVNODE:
+ if (flags & MEDIA_LNK_FL_ENABLED) {
+ if (csi2->output & ~CSI2_OUTPUT_MEMORY)
+ return -EBUSY;
+ csi2->output |= CSI2_OUTPUT_MEMORY;
+ } else {
+ csi2->output &= ~CSI2_OUTPUT_MEMORY;
+ }
+ break;
+
+ case CSI2_PAD_SOURCE | MEDIA_ENT_T_V4L2_SUBDEV:
+ if (flags & MEDIA_LNK_FL_ENABLED) {
+ if (csi2->output & ~CSI2_OUTPUT_IPIPEIF)
+ return -EBUSY;
+ csi2->output |= CSI2_OUTPUT_IPIPEIF;
+ } else {
+ csi2->output &= ~CSI2_OUTPUT_IPIPEIF;
+ }
+ break;
+
+ default:
+ /* Link from camera to CSI2 is fixed... */
+ return -EINVAL;
+ }
+
+ ctrl->vp_only_enable = csi2->output & CSI2_OUTPUT_MEMORY ? false : true;
+ ctrl->vp_clk_enable = !!(csi2->output & CSI2_OUTPUT_IPIPEIF);
+
+ return 0;
+}
+
+/* media operations */
+static const struct media_entity_operations csi2_media_ops = {
+ .link_setup = csi2_link_setup,
+ .link_validate = v4l2_subdev_link_validate,
+};
+
+void omap4iss_csi2_unregister_entities(struct iss_csi2_device *csi2)
+{
+ v4l2_device_unregister_subdev(&csi2->subdev);
+ omap4iss_video_unregister(&csi2->video_out);
+}
+
+int omap4iss_csi2_register_entities(struct iss_csi2_device *csi2,
+ struct v4l2_device *vdev)
+{
+ int ret;
+
+ /* Register the subdev and video nodes. */
+ ret = v4l2_device_register_subdev(vdev, &csi2->subdev);
+ if (ret < 0)
+ goto error;
+
+ ret = omap4iss_video_register(&csi2->video_out, vdev);
+ if (ret < 0)
+ goto error;
+
+ return 0;
+
+error:
+ omap4iss_csi2_unregister_entities(csi2);
+ return ret;
+}
+
+/* -----------------------------------------------------------------------------
+ * ISS CSI2 initialisation and cleanup
+ */
+
+/*
+ * csi2_init_entities - Initialize subdev and media entity.
+ * @csi2: Pointer to csi2 structure.
+ * return -ENOMEM or zero on success
+ */
+static int csi2_init_entities(struct iss_csi2_device *csi2, const char *subname)
+{
+ struct v4l2_subdev *sd = &csi2->subdev;
+ struct media_pad *pads = csi2->pads;
+ struct media_entity *me = &sd->entity;
+ int ret;
+ char name[V4L2_SUBDEV_NAME_SIZE];
+
+ v4l2_subdev_init(sd, &csi2_ops);
+ sd->internal_ops = &csi2_internal_ops;
+ snprintf(name, sizeof(name), "CSI2%s", subname);
+ snprintf(sd->name, sizeof(sd->name), "OMAP4 ISS %s", name);
+
+ sd->grp_id = 1 << 16; /* group ID for iss subdevs */
+ v4l2_set_subdevdata(sd, csi2);
+ sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+
+ pads[CSI2_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE;
+ pads[CSI2_PAD_SINK].flags = MEDIA_PAD_FL_SINK;
+
+ me->ops = &csi2_media_ops;
+ ret = media_entity_init(me, CSI2_PADS_NUM, pads, 0);
+ if (ret < 0)
+ return ret;
+
+ csi2_init_formats(sd, NULL);
+
+ /* Video device node */
+ csi2->video_out.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ csi2->video_out.ops = &csi2_issvideo_ops;
+ csi2->video_out.bpl_alignment = 32;
+ csi2->video_out.bpl_zero_padding = 1;
+ csi2->video_out.bpl_max = 0x1ffe0;
+ csi2->video_out.iss = csi2->iss;
+ csi2->video_out.capture_mem = PAGE_ALIGN(4096 * 4096) * 3;
+
+ ret = omap4iss_video_init(&csi2->video_out, name);
+ if (ret < 0)
+ goto error_video;
+
+ /* Connect the CSI2 subdev to the video node. */
+ ret = media_entity_create_link(&csi2->subdev.entity, CSI2_PAD_SOURCE,
+ &csi2->video_out.video.entity, 0, 0);
+ if (ret < 0)
+ goto error_link;
+
+ return 0;
+
+error_link:
+ omap4iss_video_cleanup(&csi2->video_out);
+error_video:
+ media_entity_cleanup(&csi2->subdev.entity);
+ return ret;
+}
+
+/*
+ * omap4iss_csi2_init - Routine for module driver init
+ */
+int omap4iss_csi2_init(struct iss_device *iss)
+{
+ struct iss_csi2_device *csi2a = &iss->csi2a;
+ struct iss_csi2_device *csi2b = &iss->csi2b;
+ int ret;
+
+ csi2a->iss = iss;
+ csi2a->available = 1;
+ csi2a->regs1 = OMAP4_ISS_MEM_CSI2_A_REGS1;
+ csi2a->phy = &iss->csiphy1;
+ csi2a->subclk = OMAP4_ISS_SUBCLK_CSI2_A;
+ csi2a->state = ISS_PIPELINE_STREAM_STOPPED;
+ init_waitqueue_head(&csi2a->wait);
+
+ ret = csi2_init_entities(csi2a, "a");
+ if (ret < 0)
+ return ret;
+
+ csi2b->iss = iss;
+ csi2b->available = 1;
+ csi2b->regs1 = OMAP4_ISS_MEM_CSI2_B_REGS1;
+ csi2b->phy = &iss->csiphy2;
+ csi2b->subclk = OMAP4_ISS_SUBCLK_CSI2_B;
+ csi2b->state = ISS_PIPELINE_STREAM_STOPPED;
+ init_waitqueue_head(&csi2b->wait);
+
+ ret = csi2_init_entities(csi2b, "b");
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+/*
+ * omap4iss_csi2_cleanup - Routine for module driver cleanup
+ */
+void omap4iss_csi2_cleanup(struct iss_device *iss)
+{
+ struct iss_csi2_device *csi2a = &iss->csi2a;
+ struct iss_csi2_device *csi2b = &iss->csi2b;
+
+ omap4iss_video_cleanup(&csi2a->video_out);
+ media_entity_cleanup(&csi2a->subdev.entity);
+
+ omap4iss_video_cleanup(&csi2b->video_out);
+ media_entity_cleanup(&csi2b->subdev.entity);
+}
diff --git a/drivers/staging/media/omap4iss/iss_csi2.h b/drivers/staging/media/omap4iss/iss_csi2.h
new file mode 100644
index 000000000..3b37978a3
--- /dev/null
+++ b/drivers/staging/media/omap4iss/iss_csi2.h
@@ -0,0 +1,158 @@
+/*
+ * TI OMAP4 ISS V4L2 Driver - CSI2 module
+ *
+ * Copyright (C) 2012 Texas Instruments, Inc.
+ *
+ * Author: Sergio Aguirre <sergio.a.aguirre@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef OMAP4_ISS_CSI2_H
+#define OMAP4_ISS_CSI2_H
+
+#include <linux/types.h>
+#include <linux/videodev2.h>
+
+#include "iss_video.h"
+
+struct iss_csiphy;
+
+/* This is not an exhaustive list */
+enum iss_csi2_pix_formats {
+ CSI2_PIX_FMT_OTHERS = 0,
+ CSI2_PIX_FMT_YUV422_8BIT = 0x1e,
+ CSI2_PIX_FMT_YUV422_8BIT_VP = 0x9e,
+ CSI2_PIX_FMT_YUV422_8BIT_VP16 = 0xde,
+ CSI2_PIX_FMT_RAW10_EXP16 = 0xab,
+ CSI2_PIX_FMT_RAW10_EXP16_VP = 0x12f,
+ CSI2_PIX_FMT_RAW8 = 0x2a,
+ CSI2_PIX_FMT_RAW8_DPCM10_EXP16 = 0x2aa,
+ CSI2_PIX_FMT_RAW8_DPCM10_VP = 0x32a,
+ CSI2_PIX_FMT_RAW8_VP = 0x12a,
+ CSI2_USERDEF_8BIT_DATA1_DPCM10_VP = 0x340,
+ CSI2_USERDEF_8BIT_DATA1_DPCM10 = 0x2c0,
+ CSI2_USERDEF_8BIT_DATA1 = 0x40,
+};
+
+enum iss_csi2_irqevents {
+ OCP_ERR_IRQ = 0x4000,
+ SHORT_PACKET_IRQ = 0x2000,
+ ECC_CORRECTION_IRQ = 0x1000,
+ ECC_NO_CORRECTION_IRQ = 0x800,
+ COMPLEXIO2_ERR_IRQ = 0x400,
+ COMPLEXIO1_ERR_IRQ = 0x200,
+ FIFO_OVF_IRQ = 0x100,
+ CONTEXT7 = 0x80,
+ CONTEXT6 = 0x40,
+ CONTEXT5 = 0x20,
+ CONTEXT4 = 0x10,
+ CONTEXT3 = 0x8,
+ CONTEXT2 = 0x4,
+ CONTEXT1 = 0x2,
+ CONTEXT0 = 0x1,
+};
+
+enum iss_csi2_ctx_irqevents {
+ CTX_ECC_CORRECTION = 0x100,
+ CTX_LINE_NUMBER = 0x80,
+ CTX_FRAME_NUMBER = 0x40,
+ CTX_CS = 0x20,
+ CTX_LE = 0x8,
+ CTX_LS = 0x4,
+ CTX_FE = 0x2,
+ CTX_FS = 0x1,
+};
+
+enum iss_csi2_frame_mode {
+ ISS_CSI2_FRAME_IMMEDIATE,
+ ISS_CSI2_FRAME_AFTERFEC,
+};
+
+#define ISS_CSI2_MAX_CTX_NUM 7
+
+struct iss_csi2_ctx_cfg {
+ u8 ctxnum; /* context number 0 - 7 */
+ u8 dpcm_decompress;
+
+ /* Fields in CSI2_CTx_CTRL2 - locked by CSI2_CTx_CTRL1.CTX_EN */
+ u8 virtual_id;
+ u16 format_id; /* as in CSI2_CTx_CTRL2[9:0] */
+ u8 dpcm_predictor; /* 1: simple, 0: advanced */
+ u16 frame;
+
+ /* Fields in CSI2_CTx_CTRL1/3 - Shadowed */
+ u16 alpha;
+ u16 data_offset;
+ u32 ping_addr;
+ u32 pong_addr;
+ u8 eof_enabled;
+ u8 eol_enabled;
+ u8 checksum_enabled;
+ u8 enabled;
+};
+
+struct iss_csi2_timing_cfg {
+ u8 ionum; /* IO1 or IO2 as in CSI2_TIMING */
+ unsigned force_rx_mode:1;
+ unsigned stop_state_16x:1;
+ unsigned stop_state_4x:1;
+ u16 stop_state_counter;
+};
+
+struct iss_csi2_ctrl_cfg {
+ bool vp_clk_enable;
+ bool vp_only_enable;
+ u8 vp_out_ctrl;
+ enum iss_csi2_frame_mode frame_mode;
+ bool ecc_enable;
+ bool if_enable;
+};
+
+#define CSI2_PAD_SINK 0
+#define CSI2_PAD_SOURCE 1
+#define CSI2_PADS_NUM 2
+
+#define CSI2_OUTPUT_IPIPEIF (1 << 0)
+#define CSI2_OUTPUT_MEMORY (1 << 1)
+
+struct iss_csi2_device {
+ struct v4l2_subdev subdev;
+ struct media_pad pads[CSI2_PADS_NUM];
+ struct v4l2_mbus_framefmt formats[CSI2_PADS_NUM];
+
+ struct iss_video video_out;
+ struct iss_device *iss;
+
+ u8 available; /* Is the IP present on the silicon? */
+
+ /* memory resources, as defined in enum iss_mem_resources */
+ unsigned int regs1;
+ unsigned int regs2;
+ /* ISP subclock, as defined in enum iss_isp_subclk_resource */
+ unsigned int subclk;
+
+ u32 output; /* output to IPIPEIF, memory or both? */
+ bool dpcm_decompress;
+ unsigned int frame_skip;
+
+ struct iss_csiphy *phy;
+ struct iss_csi2_ctx_cfg contexts[ISS_CSI2_MAX_CTX_NUM + 1];
+ struct iss_csi2_timing_cfg timing[2];
+ struct iss_csi2_ctrl_cfg ctrl;
+ enum iss_pipeline_stream_state state;
+ wait_queue_head_t wait;
+ atomic_t stopping;
+};
+
+void omap4iss_csi2_isr(struct iss_csi2_device *csi2);
+int omap4iss_csi2_reset(struct iss_csi2_device *csi2);
+int omap4iss_csi2_init(struct iss_device *iss);
+void omap4iss_csi2_cleanup(struct iss_device *iss);
+void omap4iss_csi2_unregister_entities(struct iss_csi2_device *csi2);
+int omap4iss_csi2_register_entities(struct iss_csi2_device *csi2,
+ struct v4l2_device *vdev);
+#endif /* OMAP4_ISS_CSI2_H */
diff --git a/drivers/staging/media/omap4iss/iss_csiphy.c b/drivers/staging/media/omap4iss/iss_csiphy.c
new file mode 100644
index 000000000..748607f89
--- /dev/null
+++ b/drivers/staging/media/omap4iss/iss_csiphy.c
@@ -0,0 +1,281 @@
+/*
+ * TI OMAP4 ISS V4L2 Driver - CSI PHY module
+ *
+ * Copyright (C) 2012 Texas Instruments, Inc.
+ *
+ * Author: Sergio Aguirre <sergio.a.aguirre@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/regmap.h>
+
+#include "../../../../arch/arm/mach-omap2/control.h"
+
+#include "iss.h"
+#include "iss_regs.h"
+#include "iss_csiphy.h"
+
+/*
+ * csiphy_lanes_config - Configuration of CSIPHY lanes.
+ *
+ * Updates HW configuration.
+ * Called with phy->mutex taken.
+ */
+static void csiphy_lanes_config(struct iss_csiphy *phy)
+{
+ unsigned int i;
+ u32 reg;
+
+ reg = iss_reg_read(phy->iss, phy->cfg_regs, CSI2_COMPLEXIO_CFG);
+
+ for (i = 0; i < phy->max_data_lanes; i++) {
+ reg &= ~(CSI2_COMPLEXIO_CFG_DATA_POL(i + 1) |
+ CSI2_COMPLEXIO_CFG_DATA_POSITION_MASK(i + 1));
+ reg |= (phy->lanes.data[i].pol ?
+ CSI2_COMPLEXIO_CFG_DATA_POL(i + 1) : 0);
+ reg |= (phy->lanes.data[i].pos <<
+ CSI2_COMPLEXIO_CFG_DATA_POSITION_SHIFT(i + 1));
+ }
+
+ reg &= ~(CSI2_COMPLEXIO_CFG_CLOCK_POL |
+ CSI2_COMPLEXIO_CFG_CLOCK_POSITION_MASK);
+ reg |= phy->lanes.clk.pol ? CSI2_COMPLEXIO_CFG_CLOCK_POL : 0;
+ reg |= phy->lanes.clk.pos << CSI2_COMPLEXIO_CFG_CLOCK_POSITION_SHIFT;
+
+ iss_reg_write(phy->iss, phy->cfg_regs, CSI2_COMPLEXIO_CFG, reg);
+}
+
+/*
+ * csiphy_set_power
+ * @power: Power state to be set.
+ *
+ * Returns 0 if successful, or -EBUSY if the retry count is exceeded.
+ */
+static int csiphy_set_power(struct iss_csiphy *phy, u32 power)
+{
+ u32 reg;
+ u8 retry_count;
+
+ iss_reg_update(phy->iss, phy->cfg_regs, CSI2_COMPLEXIO_CFG,
+ CSI2_COMPLEXIO_CFG_PWD_CMD_MASK,
+ power | CSI2_COMPLEXIO_CFG_PWR_AUTO);
+
+ retry_count = 0;
+ do {
+ udelay(1);
+ reg = iss_reg_read(phy->iss, phy->cfg_regs, CSI2_COMPLEXIO_CFG)
+ & CSI2_COMPLEXIO_CFG_PWD_STATUS_MASK;
+
+ if (reg != power >> 2)
+ retry_count++;
+
+ } while ((reg != power >> 2) && (retry_count < 250));
+
+ if (retry_count == 250) {
+ dev_err(phy->iss->dev, "CSI2 CIO set power failed!\n");
+ return -EBUSY;
+ }
+
+ return 0;
+}
+
+/*
+ * csiphy_dphy_config - Configure CSI2 D-PHY parameters.
+ *
+ * Called with phy->mutex taken.
+ */
+static void csiphy_dphy_config(struct iss_csiphy *phy)
+{
+ u32 reg;
+
+ /* Set up REGISTER0 */
+ reg = phy->dphy.ths_term << REGISTER0_THS_TERM_SHIFT;
+ reg |= phy->dphy.ths_settle << REGISTER0_THS_SETTLE_SHIFT;
+
+ iss_reg_write(phy->iss, phy->phy_regs, REGISTER0, reg);
+
+ /* Set up REGISTER1 */
+ reg = phy->dphy.tclk_term << REGISTER1_TCLK_TERM_SHIFT;
+ reg |= phy->dphy.tclk_miss << REGISTER1_CTRLCLK_DIV_FACTOR_SHIFT;
+ reg |= phy->dphy.tclk_settle << REGISTER1_TCLK_SETTLE_SHIFT;
+ reg |= 0xb8 << REGISTER1_DPHY_HS_SYNC_PATTERN_SHIFT;
+
+ iss_reg_write(phy->iss, phy->phy_regs, REGISTER1, reg);
+}
+
+/*
+ * TCLK values are OK at their reset values
+ */
+#define TCLK_TERM 0
+#define TCLK_MISS 1
+#define TCLK_SETTLE 14
+
+int omap4iss_csiphy_config(struct iss_device *iss,
+ struct v4l2_subdev *csi2_subdev)
+{
+ struct iss_csi2_device *csi2 = v4l2_get_subdevdata(csi2_subdev);
+ struct iss_pipeline *pipe = to_iss_pipeline(&csi2_subdev->entity);
+ struct iss_v4l2_subdevs_group *subdevs = pipe->external->host_priv;
+ struct iss_csiphy_dphy_cfg csi2phy;
+ int csi2_ddrclk_khz;
+ struct iss_csiphy_lanes_cfg *lanes;
+ unsigned int used_lanes = 0;
+ u32 cam_rx_ctrl;
+ unsigned int i;
+
+ lanes = &subdevs->bus.csi2.lanecfg;
+
+ /*
+ * SCM.CONTROL_CAMERA_RX
+ * - bit [31] : CSIPHY2 lane 2 enable (4460+ only)
+ * - bit [30:29] : CSIPHY2 per-lane enable (1 to 0)
+ * - bit [28:24] : CSIPHY1 per-lane enable (4 to 0)
+ * - bit [21] : CSIPHY2 CTRLCLK enable
+ * - bit [20:19] : CSIPHY2 config: 00 d-phy, 01/10 ccp2
+ * - bit [18] : CSIPHY1 CTRLCLK enable
+ * - bit [17:16] : CSIPHY1 config: 00 d-phy, 01/10 ccp2
+ */
+ /*
+ * TODO: When implementing DT support specify the CONTROL_CAMERA_RX
+ * register offset in the syscon property instead of hardcoding it.
+ */
+ regmap_read(iss->syscon, 0x68, &cam_rx_ctrl);
+
+ if (subdevs->interface == ISS_INTERFACE_CSI2A_PHY1) {
+ cam_rx_ctrl &= ~(OMAP4_CAMERARX_CSI21_LANEENABLE_MASK |
+ OMAP4_CAMERARX_CSI21_CAMMODE_MASK);
+ /* NOTE: Leave CSIPHY1 config to 0x0: D-PHY mode */
+ /* Enable all lanes for now */
+ cam_rx_ctrl |=
+ 0x1f << OMAP4_CAMERARX_CSI21_LANEENABLE_SHIFT;
+ /* Enable CTRLCLK */
+ cam_rx_ctrl |= OMAP4_CAMERARX_CSI21_CTRLCLKEN_MASK;
+ }
+
+ if (subdevs->interface == ISS_INTERFACE_CSI2B_PHY2) {
+ cam_rx_ctrl &= ~(OMAP4_CAMERARX_CSI22_LANEENABLE_MASK |
+ OMAP4_CAMERARX_CSI22_CAMMODE_MASK);
+ /* NOTE: Leave CSIPHY2 config to 0x0: D-PHY mode */
+ /* Enable all lanes for now */
+ cam_rx_ctrl |=
+ 0x3 << OMAP4_CAMERARX_CSI22_LANEENABLE_SHIFT;
+ /* Enable CTRLCLK */
+ cam_rx_ctrl |= OMAP4_CAMERARX_CSI22_CTRLCLKEN_MASK;
+ }
+
+ regmap_write(iss->syscon, 0x68, cam_rx_ctrl);
+
+ /* Reset used lane count */
+ csi2->phy->used_data_lanes = 0;
+
+ /* Clock and data lanes verification */
+ for (i = 0; i < csi2->phy->max_data_lanes; i++) {
+ if (lanes->data[i].pos == 0)
+ continue;
+
+ if (lanes->data[i].pol > 1 ||
+ lanes->data[i].pos > (csi2->phy->max_data_lanes + 1))
+ return -EINVAL;
+
+ if (used_lanes & (1 << lanes->data[i].pos))
+ return -EINVAL;
+
+ used_lanes |= 1 << lanes->data[i].pos;
+ csi2->phy->used_data_lanes++;
+ }
+
+ if (lanes->clk.pol > 1 ||
+ lanes->clk.pos > (csi2->phy->max_data_lanes + 1))
+ return -EINVAL;
+
+ if (lanes->clk.pos == 0 || used_lanes & (1 << lanes->clk.pos))
+ return -EINVAL;
+
+ csi2_ddrclk_khz = pipe->external_rate / 1000
+ / (2 * csi2->phy->used_data_lanes)
+ * pipe->external_bpp;
+
+ /*
+ * THS_TERM: Programmed value = ceil(12.5 ns/DDRClk period) - 1.
+ * THS_SETTLE: Programmed value = ceil(90 ns/DDRClk period) + 3.
+ */
+ csi2phy.ths_term = DIV_ROUND_UP(25 * csi2_ddrclk_khz, 2000000) - 1;
+ csi2phy.ths_settle = DIV_ROUND_UP(90 * csi2_ddrclk_khz, 1000000) + 3;
+ csi2phy.tclk_term = TCLK_TERM;
+ csi2phy.tclk_miss = TCLK_MISS;
+ csi2phy.tclk_settle = TCLK_SETTLE;
+
+ mutex_lock(&csi2->phy->mutex);
+ csi2->phy->dphy = csi2phy;
+ csi2->phy->lanes = *lanes;
+ mutex_unlock(&csi2->phy->mutex);
+
+ return 0;
+}
+
+int omap4iss_csiphy_acquire(struct iss_csiphy *phy)
+{
+ int rval;
+
+ mutex_lock(&phy->mutex);
+
+ rval = omap4iss_csi2_reset(phy->csi2);
+ if (rval)
+ goto done;
+
+ csiphy_dphy_config(phy);
+ csiphy_lanes_config(phy);
+
+ rval = csiphy_set_power(phy, CSI2_COMPLEXIO_CFG_PWD_CMD_ON);
+ if (rval)
+ goto done;
+
+ phy->phy_in_use = 1;
+
+done:
+ mutex_unlock(&phy->mutex);
+ return rval;
+}
+
+void omap4iss_csiphy_release(struct iss_csiphy *phy)
+{
+ mutex_lock(&phy->mutex);
+ if (phy->phy_in_use) {
+ csiphy_set_power(phy, CSI2_COMPLEXIO_CFG_PWD_CMD_OFF);
+ phy->phy_in_use = 0;
+ }
+ mutex_unlock(&phy->mutex);
+}
+
+/*
+ * omap4iss_csiphy_init - Initialize the CSI PHY frontends
+ */
+int omap4iss_csiphy_init(struct iss_device *iss)
+{
+ struct iss_csiphy *phy1 = &iss->csiphy1;
+ struct iss_csiphy *phy2 = &iss->csiphy2;
+
+ phy1->iss = iss;
+ phy1->csi2 = &iss->csi2a;
+ phy1->max_data_lanes = ISS_CSIPHY1_NUM_DATA_LANES;
+ phy1->used_data_lanes = 0;
+ phy1->cfg_regs = OMAP4_ISS_MEM_CSI2_A_REGS1;
+ phy1->phy_regs = OMAP4_ISS_MEM_CAMERARX_CORE1;
+ mutex_init(&phy1->mutex);
+
+ phy2->iss = iss;
+ phy2->csi2 = &iss->csi2b;
+ phy2->max_data_lanes = ISS_CSIPHY2_NUM_DATA_LANES;
+ phy2->used_data_lanes = 0;
+ phy2->cfg_regs = OMAP4_ISS_MEM_CSI2_B_REGS1;
+ phy2->phy_regs = OMAP4_ISS_MEM_CAMERARX_CORE2;
+ mutex_init(&phy2->mutex);
+
+ return 0;
+}
diff --git a/drivers/staging/media/omap4iss/iss_csiphy.h b/drivers/staging/media/omap4iss/iss_csiphy.h
new file mode 100644
index 000000000..e9ca43955
--- /dev/null
+++ b/drivers/staging/media/omap4iss/iss_csiphy.h
@@ -0,0 +1,51 @@
+/*
+ * TI OMAP4 ISS V4L2 Driver - CSI PHY module
+ *
+ * Copyright (C) 2012 Texas Instruments, Inc.
+ *
+ * Author: Sergio Aguirre <sergio.a.aguirre@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef OMAP4_ISS_CSI_PHY_H
+#define OMAP4_ISS_CSI_PHY_H
+
+#include <media/omap4iss.h>
+
+struct iss_csi2_device;
+
+struct iss_csiphy_dphy_cfg {
+ u8 ths_term;
+ u8 ths_settle;
+ u8 tclk_term;
+ unsigned tclk_miss:1;
+ u8 tclk_settle;
+};
+
+struct iss_csiphy {
+ struct iss_device *iss;
+ struct mutex mutex; /* serialize csiphy configuration */
+ u8 phy_in_use;
+ struct iss_csi2_device *csi2;
+
+ /* memory resources, as defined in enum iss_mem_resources */
+ unsigned int cfg_regs;
+ unsigned int phy_regs;
+
+ u8 max_data_lanes; /* number of CSI2 Data Lanes supported */
+ u8 used_data_lanes; /* number of CSI2 Data Lanes used */
+ struct iss_csiphy_lanes_cfg lanes;
+ struct iss_csiphy_dphy_cfg dphy;
+};
+
+int omap4iss_csiphy_config(struct iss_device *iss,
+ struct v4l2_subdev *csi2_subdev);
+int omap4iss_csiphy_acquire(struct iss_csiphy *phy);
+void omap4iss_csiphy_release(struct iss_csiphy *phy);
+int omap4iss_csiphy_init(struct iss_device *iss);
+
+#endif /* OMAP4_ISS_CSI_PHY_H */
diff --git a/drivers/staging/media/omap4iss/iss_ipipe.c b/drivers/staging/media/omap4iss/iss_ipipe.c
new file mode 100644
index 000000000..eaa82da30
--- /dev/null
+++ b/drivers/staging/media/omap4iss/iss_ipipe.c
@@ -0,0 +1,570 @@
+/*
+ * TI OMAP4 ISS V4L2 Driver - ISP IPIPE module
+ *
+ * Copyright (C) 2012 Texas Instruments, Inc.
+ *
+ * Author: Sergio Aguirre <sergio.a.aguirre@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/module.h>
+#include <linux/uaccess.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/dma-mapping.h>
+#include <linux/mm.h>
+#include <linux/sched.h>
+
+#include "iss.h"
+#include "iss_regs.h"
+#include "iss_ipipe.h"
+
+static struct v4l2_mbus_framefmt *
+__ipipe_get_format(struct iss_ipipe_device *ipipe, struct v4l2_subdev_pad_config *cfg,
+ unsigned int pad, enum v4l2_subdev_format_whence which);
+
+static const unsigned int ipipe_fmts[] = {
+ MEDIA_BUS_FMT_SGRBG10_1X10,
+ MEDIA_BUS_FMT_SRGGB10_1X10,
+ MEDIA_BUS_FMT_SBGGR10_1X10,
+ MEDIA_BUS_FMT_SGBRG10_1X10,
+};
+
+/*
+ * ipipe_print_status - Print current IPIPE Module register values.
+ * @ipipe: Pointer to ISS ISP IPIPE device.
+ *
+ * Also prints other debug information stored in the IPIPE module.
+ */
+#define IPIPE_PRINT_REGISTER(iss, name)\
+ dev_dbg(iss->dev, "###IPIPE " #name "=0x%08x\n", \
+ iss_reg_read(iss, OMAP4_ISS_MEM_ISP_IPIPE, IPIPE_##name))
+
+static void ipipe_print_status(struct iss_ipipe_device *ipipe)
+{
+ struct iss_device *iss = to_iss_device(ipipe);
+
+ dev_dbg(iss->dev, "-------------IPIPE Register dump-------------\n");
+
+ IPIPE_PRINT_REGISTER(iss, SRC_EN);
+ IPIPE_PRINT_REGISTER(iss, SRC_MODE);
+ IPIPE_PRINT_REGISTER(iss, SRC_FMT);
+ IPIPE_PRINT_REGISTER(iss, SRC_COL);
+ IPIPE_PRINT_REGISTER(iss, SRC_VPS);
+ IPIPE_PRINT_REGISTER(iss, SRC_VSZ);
+ IPIPE_PRINT_REGISTER(iss, SRC_HPS);
+ IPIPE_PRINT_REGISTER(iss, SRC_HSZ);
+ IPIPE_PRINT_REGISTER(iss, GCK_MMR);
+ IPIPE_PRINT_REGISTER(iss, YUV_PHS);
+
+ dev_dbg(iss->dev, "-----------------------------------------------\n");
+}
+
+/*
+ * ipipe_enable - Enable/Disable IPIPE.
+ * @enable: enable flag
+ *
+ */
+static void ipipe_enable(struct iss_ipipe_device *ipipe, u8 enable)
+{
+ struct iss_device *iss = to_iss_device(ipipe);
+
+ iss_reg_update(iss, OMAP4_ISS_MEM_ISP_IPIPE, IPIPE_SRC_EN,
+ IPIPE_SRC_EN_EN, enable ? IPIPE_SRC_EN_EN : 0);
+}
+
+/* -----------------------------------------------------------------------------
+ * Format- and pipeline-related configuration helpers
+ */
+
+static void ipipe_configure(struct iss_ipipe_device *ipipe)
+{
+ struct iss_device *iss = to_iss_device(ipipe);
+ struct v4l2_mbus_framefmt *format;
+
+ /* IPIPE_PAD_SINK */
+ format = &ipipe->formats[IPIPE_PAD_SINK];
+
+ /* NOTE: Currently just supporting pipeline IN: RGB, OUT: YUV422 */
+ iss_reg_write(iss, OMAP4_ISS_MEM_ISP_IPIPE, IPIPE_SRC_FMT,
+ IPIPE_SRC_FMT_RAW2YUV);
+
+ /* Enable YUV444 -> YUV422 conversion */
+ iss_reg_write(iss, OMAP4_ISS_MEM_ISP_IPIPE, IPIPE_YUV_PHS,
+ IPIPE_YUV_PHS_LPF);
+
+ iss_reg_write(iss, OMAP4_ISS_MEM_ISP_IPIPE, IPIPE_SRC_VPS, 0);
+ iss_reg_write(iss, OMAP4_ISS_MEM_ISP_IPIPE, IPIPE_SRC_HPS, 0);
+ iss_reg_write(iss, OMAP4_ISS_MEM_ISP_IPIPE, IPIPE_SRC_VSZ,
+ (format->height - 2) & IPIPE_SRC_VSZ_MASK);
+ iss_reg_write(iss, OMAP4_ISS_MEM_ISP_IPIPE, IPIPE_SRC_HSZ,
+ (format->width - 1) & IPIPE_SRC_HSZ_MASK);
+
+ /* Ignore ipipeif_wrt signal, and operate on-the-fly. */
+ iss_reg_clr(iss, OMAP4_ISS_MEM_ISP_IPIPE, IPIPE_SRC_MODE,
+ IPIPE_SRC_MODE_WRT | IPIPE_SRC_MODE_OST);
+
+ /* HACK: Values tuned for Ducati SW (OV) */
+ iss_reg_write(iss, OMAP4_ISS_MEM_ISP_IPIPE, IPIPE_SRC_COL,
+ IPIPE_SRC_COL_EE_B | IPIPE_SRC_COL_EO_GB |
+ IPIPE_SRC_COL_OE_GR | IPIPE_SRC_COL_OO_R);
+
+ /* IPIPE_PAD_SOURCE_VP */
+ format = &ipipe->formats[IPIPE_PAD_SOURCE_VP];
+ /* Do nothing? */
+}
+
+/* -----------------------------------------------------------------------------
+ * V4L2 subdev operations
+ */
+
+/*
+ * ipipe_set_stream - Enable/Disable streaming on the IPIPE module
+ * @sd: ISP IPIPE V4L2 subdevice
+ * @enable: Enable/disable stream
+ */
+static int ipipe_set_stream(struct v4l2_subdev *sd, int enable)
+{
+ struct iss_ipipe_device *ipipe = v4l2_get_subdevdata(sd);
+ struct iss_device *iss = to_iss_device(ipipe);
+ int ret = 0;
+
+ if (ipipe->state == ISS_PIPELINE_STREAM_STOPPED) {
+ if (enable == ISS_PIPELINE_STREAM_STOPPED)
+ return 0;
+
+ omap4iss_isp_subclk_enable(iss, OMAP4_ISS_ISP_SUBCLK_IPIPE);
+
+ /* Enable clk_arm_g0 */
+ iss_reg_write(iss, OMAP4_ISS_MEM_ISP_IPIPE, IPIPE_GCK_MMR,
+ IPIPE_GCK_MMR_REG);
+
+ /* Enable clk_pix_g[3:0] */
+ iss_reg_write(iss, OMAP4_ISS_MEM_ISP_IPIPE, IPIPE_GCK_PIX,
+ IPIPE_GCK_PIX_G3 | IPIPE_GCK_PIX_G2 |
+ IPIPE_GCK_PIX_G1 | IPIPE_GCK_PIX_G0);
+ }
+
+ switch (enable) {
+ case ISS_PIPELINE_STREAM_CONTINUOUS:
+
+ ipipe_configure(ipipe);
+ ipipe_print_status(ipipe);
+
+ atomic_set(&ipipe->stopping, 0);
+ ipipe_enable(ipipe, 1);
+ break;
+
+ case ISS_PIPELINE_STREAM_STOPPED:
+ if (ipipe->state == ISS_PIPELINE_STREAM_STOPPED)
+ return 0;
+ if (omap4iss_module_sync_idle(&sd->entity, &ipipe->wait,
+ &ipipe->stopping))
+ ret = -ETIMEDOUT;
+
+ ipipe_enable(ipipe, 0);
+ omap4iss_isp_subclk_disable(iss, OMAP4_ISS_ISP_SUBCLK_IPIPE);
+ break;
+ }
+
+ ipipe->state = enable;
+ return ret;
+}
+
+static struct v4l2_mbus_framefmt *
+__ipipe_get_format(struct iss_ipipe_device *ipipe, struct v4l2_subdev_pad_config *cfg,
+ unsigned int pad, enum v4l2_subdev_format_whence which)
+{
+ if (which == V4L2_SUBDEV_FORMAT_TRY)
+ return v4l2_subdev_get_try_format(&ipipe->subdev, cfg, pad);
+
+ return &ipipe->formats[pad];
+}
+
+/*
+ * ipipe_try_format - Try video format on a pad
+ * @ipipe: ISS IPIPE device
+ * @cfg: V4L2 subdev pad config
+ * @pad: Pad number
+ * @fmt: Format
+ */
+static void
+ipipe_try_format(struct iss_ipipe_device *ipipe, struct v4l2_subdev_pad_config *cfg,
+ unsigned int pad, struct v4l2_mbus_framefmt *fmt,
+ enum v4l2_subdev_format_whence which)
+{
+ struct v4l2_mbus_framefmt *format;
+ unsigned int width = fmt->width;
+ unsigned int height = fmt->height;
+ unsigned int i;
+
+ switch (pad) {
+ case IPIPE_PAD_SINK:
+ for (i = 0; i < ARRAY_SIZE(ipipe_fmts); i++) {
+ if (fmt->code == ipipe_fmts[i])
+ break;
+ }
+
+ /* If not found, use SGRBG10 as default */
+ if (i >= ARRAY_SIZE(ipipe_fmts))
+ fmt->code = MEDIA_BUS_FMT_SGRBG10_1X10;
+
+ /* Clamp the input size. */
+ fmt->width = clamp_t(u32, width, 1, 8192);
+ fmt->height = clamp_t(u32, height, 1, 8192);
+ fmt->colorspace = V4L2_COLORSPACE_SRGB;
+ break;
+
+ case IPIPE_PAD_SOURCE_VP:
+ format = __ipipe_get_format(ipipe, cfg, IPIPE_PAD_SINK, which);
+ memcpy(fmt, format, sizeof(*fmt));
+
+ fmt->code = MEDIA_BUS_FMT_UYVY8_1X16;
+ fmt->width = clamp_t(u32, width, 32, fmt->width);
+ fmt->height = clamp_t(u32, height, 32, fmt->height);
+ fmt->colorspace = V4L2_COLORSPACE_JPEG;
+ break;
+ }
+
+ fmt->field = V4L2_FIELD_NONE;
+}
+
+/*
+ * ipipe_enum_mbus_code - Handle pixel format enumeration
+ * @sd : pointer to v4l2 subdev structure
+ * @cfg : V4L2 subdev pad config
+ * @code : pointer to v4l2_subdev_mbus_code_enum structure
+ * return -EINVAL or zero on success
+ */
+static int ipipe_enum_mbus_code(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_mbus_code_enum *code)
+{
+ switch (code->pad) {
+ case IPIPE_PAD_SINK:
+ if (code->index >= ARRAY_SIZE(ipipe_fmts))
+ return -EINVAL;
+
+ code->code = ipipe_fmts[code->index];
+ break;
+
+ case IPIPE_PAD_SOURCE_VP:
+ /* FIXME: Forced format conversion inside IPIPE ? */
+ if (code->index != 0)
+ return -EINVAL;
+
+ code->code = MEDIA_BUS_FMT_UYVY8_1X16;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int ipipe_enum_frame_size(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_frame_size_enum *fse)
+{
+ struct iss_ipipe_device *ipipe = v4l2_get_subdevdata(sd);
+ struct v4l2_mbus_framefmt format;
+
+ if (fse->index != 0)
+ return -EINVAL;
+
+ format.code = fse->code;
+ format.width = 1;
+ format.height = 1;
+ ipipe_try_format(ipipe, cfg, fse->pad, &format, fse->which);
+ fse->min_width = format.width;
+ fse->min_height = format.height;
+
+ if (format.code != fse->code)
+ return -EINVAL;
+
+ format.code = fse->code;
+ format.width = -1;
+ format.height = -1;
+ ipipe_try_format(ipipe, cfg, fse->pad, &format, fse->which);
+ fse->max_width = format.width;
+ fse->max_height = format.height;
+
+ return 0;
+}
+
+/*
+ * ipipe_get_format - Retrieve the video format on a pad
+ * @sd : ISP IPIPE V4L2 subdevice
+ * @cfg: V4L2 subdev pad config
+ * @fmt: Format
+ *
+ * Return 0 on success or -EINVAL if the pad is invalid or doesn't correspond
+ * to the format type.
+ */
+static int ipipe_get_format(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_format *fmt)
+{
+ struct iss_ipipe_device *ipipe = v4l2_get_subdevdata(sd);
+ struct v4l2_mbus_framefmt *format;
+
+ format = __ipipe_get_format(ipipe, cfg, fmt->pad, fmt->which);
+ if (format == NULL)
+ return -EINVAL;
+
+ fmt->format = *format;
+ return 0;
+}
+
+/*
+ * ipipe_set_format - Set the video format on a pad
+ * @sd : ISP IPIPE V4L2 subdevice
+ * @cfg: V4L2 subdev pad config
+ * @fmt: Format
+ *
+ * Return 0 on success or -EINVAL if the pad is invalid or doesn't correspond
+ * to the format type.
+ */
+static int ipipe_set_format(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_format *fmt)
+{
+ struct iss_ipipe_device *ipipe = v4l2_get_subdevdata(sd);
+ struct v4l2_mbus_framefmt *format;
+
+ format = __ipipe_get_format(ipipe, cfg, fmt->pad, fmt->which);
+ if (format == NULL)
+ return -EINVAL;
+
+ ipipe_try_format(ipipe, cfg, fmt->pad, &fmt->format, fmt->which);
+ *format = fmt->format;
+
+ /* Propagate the format from sink to source */
+ if (fmt->pad == IPIPE_PAD_SINK) {
+ format = __ipipe_get_format(ipipe, cfg, IPIPE_PAD_SOURCE_VP,
+ fmt->which);
+ *format = fmt->format;
+ ipipe_try_format(ipipe, cfg, IPIPE_PAD_SOURCE_VP, format,
+ fmt->which);
+ }
+
+ return 0;
+}
+
+static int ipipe_link_validate(struct v4l2_subdev *sd, struct media_link *link,
+ struct v4l2_subdev_format *source_fmt,
+ struct v4l2_subdev_format *sink_fmt)
+{
+ /* Check if the two ends match */
+ if (source_fmt->format.width != sink_fmt->format.width ||
+ source_fmt->format.height != sink_fmt->format.height)
+ return -EPIPE;
+
+ if (source_fmt->format.code != sink_fmt->format.code)
+ return -EPIPE;
+
+ return 0;
+}
+
+/*
+ * ipipe_init_formats - Initialize formats on all pads
+ * @sd: ISP IPIPE V4L2 subdevice
+ * @fh: V4L2 subdev file handle
+ *
+ * Initialize all pad formats with default values. If fh is not NULL, try
+ * formats are initialized on the file handle. Otherwise active formats are
+ * initialized on the device.
+ */
+static int ipipe_init_formats(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
+{
+ struct v4l2_subdev_format format;
+
+ memset(&format, 0, sizeof(format));
+ format.pad = IPIPE_PAD_SINK;
+ format.which = fh ? V4L2_SUBDEV_FORMAT_TRY : V4L2_SUBDEV_FORMAT_ACTIVE;
+ format.format.code = MEDIA_BUS_FMT_SGRBG10_1X10;
+ format.format.width = 4096;
+ format.format.height = 4096;
+ ipipe_set_format(sd, fh ? fh->pad : NULL, &format);
+
+ return 0;
+}
+
+/* V4L2 subdev video operations */
+static const struct v4l2_subdev_video_ops ipipe_v4l2_video_ops = {
+ .s_stream = ipipe_set_stream,
+};
+
+/* V4L2 subdev pad operations */
+static const struct v4l2_subdev_pad_ops ipipe_v4l2_pad_ops = {
+ .enum_mbus_code = ipipe_enum_mbus_code,
+ .enum_frame_size = ipipe_enum_frame_size,
+ .get_fmt = ipipe_get_format,
+ .set_fmt = ipipe_set_format,
+ .link_validate = ipipe_link_validate,
+};
+
+/* V4L2 subdev operations */
+static const struct v4l2_subdev_ops ipipe_v4l2_ops = {
+ .video = &ipipe_v4l2_video_ops,
+ .pad = &ipipe_v4l2_pad_ops,
+};
+
+/* V4L2 subdev internal operations */
+static const struct v4l2_subdev_internal_ops ipipe_v4l2_internal_ops = {
+ .open = ipipe_init_formats,
+};
+
+/* -----------------------------------------------------------------------------
+ * Media entity operations
+ */
+
+/*
+ * ipipe_link_setup - Setup IPIPE connections
+ * @entity: IPIPE media entity
+ * @local: Pad at the local end of the link
+ * @remote: Pad at the remote end of the link
+ * @flags: Link flags
+ *
+ * return -EINVAL or zero on success
+ */
+static int ipipe_link_setup(struct media_entity *entity,
+ const struct media_pad *local,
+ const struct media_pad *remote, u32 flags)
+{
+ struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity);
+ struct iss_ipipe_device *ipipe = v4l2_get_subdevdata(sd);
+ struct iss_device *iss = to_iss_device(ipipe);
+
+ switch (local->index | media_entity_type(remote->entity)) {
+ case IPIPE_PAD_SINK | MEDIA_ENT_T_V4L2_SUBDEV:
+ /* Read from IPIPEIF. */
+ if (!(flags & MEDIA_LNK_FL_ENABLED)) {
+ ipipe->input = IPIPE_INPUT_NONE;
+ break;
+ }
+
+ if (ipipe->input != IPIPE_INPUT_NONE)
+ return -EBUSY;
+
+ if (remote->entity == &iss->ipipeif.subdev.entity)
+ ipipe->input = IPIPE_INPUT_IPIPEIF;
+
+ break;
+
+ case IPIPE_PAD_SOURCE_VP | MEDIA_ENT_T_V4L2_SUBDEV:
+ /* Send to RESIZER */
+ if (flags & MEDIA_LNK_FL_ENABLED) {
+ if (ipipe->output & ~IPIPE_OUTPUT_VP)
+ return -EBUSY;
+ ipipe->output |= IPIPE_OUTPUT_VP;
+ } else {
+ ipipe->output &= ~IPIPE_OUTPUT_VP;
+ }
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/* media operations */
+static const struct media_entity_operations ipipe_media_ops = {
+ .link_setup = ipipe_link_setup,
+ .link_validate = v4l2_subdev_link_validate,
+};
+
+/*
+ * ipipe_init_entities - Initialize V4L2 subdev and media entity
+ * @ipipe: ISS ISP IPIPE module
+ *
+ * Return 0 on success and a negative error code on failure.
+ */
+static int ipipe_init_entities(struct iss_ipipe_device *ipipe)
+{
+ struct v4l2_subdev *sd = &ipipe->subdev;
+ struct media_pad *pads = ipipe->pads;
+ struct media_entity *me = &sd->entity;
+ int ret;
+
+ ipipe->input = IPIPE_INPUT_NONE;
+
+ v4l2_subdev_init(sd, &ipipe_v4l2_ops);
+ sd->internal_ops = &ipipe_v4l2_internal_ops;
+ strlcpy(sd->name, "OMAP4 ISS ISP IPIPE", sizeof(sd->name));
+ sd->grp_id = 1 << 16; /* group ID for iss subdevs */
+ v4l2_set_subdevdata(sd, ipipe);
+ sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+
+ pads[IPIPE_PAD_SINK].flags = MEDIA_PAD_FL_SINK;
+ pads[IPIPE_PAD_SOURCE_VP].flags = MEDIA_PAD_FL_SOURCE;
+
+ me->ops = &ipipe_media_ops;
+ ret = media_entity_init(me, IPIPE_PADS_NUM, pads, 0);
+ if (ret < 0)
+ return ret;
+
+ ipipe_init_formats(sd, NULL);
+
+ return 0;
+}
+
+void omap4iss_ipipe_unregister_entities(struct iss_ipipe_device *ipipe)
+{
+ v4l2_device_unregister_subdev(&ipipe->subdev);
+}
+
+int omap4iss_ipipe_register_entities(struct iss_ipipe_device *ipipe,
+ struct v4l2_device *vdev)
+{
+ int ret;
+
+ /* Register the subdev and video node. */
+ ret = v4l2_device_register_subdev(vdev, &ipipe->subdev);
+ if (ret < 0)
+ goto error;
+
+ return 0;
+
+error:
+ omap4iss_ipipe_unregister_entities(ipipe);
+ return ret;
+}
+
+/* -----------------------------------------------------------------------------
+ * ISP IPIPE initialisation and cleanup
+ */
+
+/*
+ * omap4iss_ipipe_init - IPIPE module initialization.
+ * @iss: Device pointer specific to the OMAP4 ISS.
+ *
+ * TODO: Get the initialisation values from platform data.
+ *
+ * Return 0 on success or a negative error code otherwise.
+ */
+int omap4iss_ipipe_init(struct iss_device *iss)
+{
+ struct iss_ipipe_device *ipipe = &iss->ipipe;
+
+ ipipe->state = ISS_PIPELINE_STREAM_STOPPED;
+ init_waitqueue_head(&ipipe->wait);
+
+ return ipipe_init_entities(ipipe);
+}
+
+/*
+ * omap4iss_ipipe_cleanup - IPIPE module cleanup.
+ * @iss: Device pointer specific to the OMAP4 ISS.
+ */
+void omap4iss_ipipe_cleanup(struct iss_device *iss)
+{
+ struct iss_ipipe_device *ipipe = &iss->ipipe;
+
+ media_entity_cleanup(&ipipe->subdev.entity);
+}
diff --git a/drivers/staging/media/omap4iss/iss_ipipe.h b/drivers/staging/media/omap4iss/iss_ipipe.h
new file mode 100644
index 000000000..c22d9041f
--- /dev/null
+++ b/drivers/staging/media/omap4iss/iss_ipipe.h
@@ -0,0 +1,67 @@
+/*
+ * TI OMAP4 ISS V4L2 Driver - ISP IPIPE module
+ *
+ * Copyright (C) 2012 Texas Instruments, Inc.
+ *
+ * Author: Sergio Aguirre <sergio.a.aguirre@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef OMAP4_ISS_IPIPE_H
+#define OMAP4_ISS_IPIPE_H
+
+#include "iss_video.h"
+
+enum ipipe_input_entity {
+ IPIPE_INPUT_NONE,
+ IPIPE_INPUT_IPIPEIF,
+};
+
+#define IPIPE_OUTPUT_VP (1 << 0)
+
+/* Sink and source IPIPE pads */
+#define IPIPE_PAD_SINK 0
+#define IPIPE_PAD_SOURCE_VP 1
+#define IPIPE_PADS_NUM 2
+
+/*
+ * struct iss_ipipe_device - Structure for the IPIPE module to store its own
+ * information
+ * @subdev: V4L2 subdevice
+ * @pads: Sink and source media entity pads
+ * @formats: Active video formats
+ * @input: Active input
+ * @output: Active outputs
+ * @error: A hardware error occurred during capture
+ * @state: Streaming state
+ * @wait: Wait queue used to stop the module
+ * @stopping: Stopping state
+ */
+struct iss_ipipe_device {
+ struct v4l2_subdev subdev;
+ struct media_pad pads[IPIPE_PADS_NUM];
+ struct v4l2_mbus_framefmt formats[IPIPE_PADS_NUM];
+
+ enum ipipe_input_entity input;
+ unsigned int output;
+ unsigned int error;
+
+ enum iss_pipeline_stream_state state;
+ wait_queue_head_t wait;
+ atomic_t stopping;
+};
+
+struct iss_device;
+
+int omap4iss_ipipe_register_entities(struct iss_ipipe_device *ipipe,
+ struct v4l2_device *vdev);
+void omap4iss_ipipe_unregister_entities(struct iss_ipipe_device *ipipe);
+
+int omap4iss_ipipe_init(struct iss_device *iss);
+void omap4iss_ipipe_cleanup(struct iss_device *iss);
+
+#endif /* OMAP4_ISS_IPIPE_H */
diff --git a/drivers/staging/media/omap4iss/iss_ipipeif.c b/drivers/staging/media/omap4iss/iss_ipipeif.c
new file mode 100644
index 000000000..530ac8426
--- /dev/null
+++ b/drivers/staging/media/omap4iss/iss_ipipeif.c
@@ -0,0 +1,830 @@
+/*
+ * TI OMAP4 ISS V4L2 Driver - ISP IPIPEIF module
+ *
+ * Copyright (C) 2012 Texas Instruments, Inc.
+ *
+ * Author: Sergio Aguirre <sergio.a.aguirre@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/module.h>
+#include <linux/uaccess.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/dma-mapping.h>
+#include <linux/mm.h>
+#include <linux/sched.h>
+
+#include "iss.h"
+#include "iss_regs.h"
+#include "iss_ipipeif.h"
+
+static const unsigned int ipipeif_fmts[] = {
+ MEDIA_BUS_FMT_SGRBG10_1X10,
+ MEDIA_BUS_FMT_SRGGB10_1X10,
+ MEDIA_BUS_FMT_SBGGR10_1X10,
+ MEDIA_BUS_FMT_SGBRG10_1X10,
+ MEDIA_BUS_FMT_UYVY8_1X16,
+ MEDIA_BUS_FMT_YUYV8_1X16,
+};
+
+/*
+ * ipipeif_print_status - Print current IPIPEIF Module register values.
+ * @ipipeif: Pointer to ISS ISP IPIPEIF device.
+ *
+ * Also prints other debug information stored in the IPIPEIF module.
+ */
+#define IPIPEIF_PRINT_REGISTER(iss, name)\
+ dev_dbg(iss->dev, "###IPIPEIF " #name "=0x%08x\n", \
+ iss_reg_read(iss, OMAP4_ISS_MEM_ISP_IPIPEIF, IPIPEIF_##name))
+
+#define ISIF_PRINT_REGISTER(iss, name)\
+ dev_dbg(iss->dev, "###ISIF " #name "=0x%08x\n", \
+ iss_reg_read(iss, OMAP4_ISS_MEM_ISP_ISIF, ISIF_##name))
+
+#define ISP5_PRINT_REGISTER(iss, name)\
+ dev_dbg(iss->dev, "###ISP5 " #name "=0x%08x\n", \
+ iss_reg_read(iss, OMAP4_ISS_MEM_ISP_SYS1, ISP5_##name))
+
+static void ipipeif_print_status(struct iss_ipipeif_device *ipipeif)
+{
+ struct iss_device *iss = to_iss_device(ipipeif);
+
+ dev_dbg(iss->dev, "-------------IPIPEIF Register dump-------------\n");
+
+ IPIPEIF_PRINT_REGISTER(iss, CFG1);
+ IPIPEIF_PRINT_REGISTER(iss, CFG2);
+
+ ISIF_PRINT_REGISTER(iss, SYNCEN);
+ ISIF_PRINT_REGISTER(iss, CADU);
+ ISIF_PRINT_REGISTER(iss, CADL);
+ ISIF_PRINT_REGISTER(iss, MODESET);
+ ISIF_PRINT_REGISTER(iss, CCOLP);
+ ISIF_PRINT_REGISTER(iss, SPH);
+ ISIF_PRINT_REGISTER(iss, LNH);
+ ISIF_PRINT_REGISTER(iss, LNV);
+ ISIF_PRINT_REGISTER(iss, VDINT(0));
+ ISIF_PRINT_REGISTER(iss, HSIZE);
+
+ ISP5_PRINT_REGISTER(iss, SYSCONFIG);
+ ISP5_PRINT_REGISTER(iss, CTRL);
+ ISP5_PRINT_REGISTER(iss, IRQSTATUS(0));
+ ISP5_PRINT_REGISTER(iss, IRQENABLE_SET(0));
+ ISP5_PRINT_REGISTER(iss, IRQENABLE_CLR(0));
+
+ dev_dbg(iss->dev, "-----------------------------------------------\n");
+}
+
+static void ipipeif_write_enable(struct iss_ipipeif_device *ipipeif, u8 enable)
+{
+ struct iss_device *iss = to_iss_device(ipipeif);
+
+ iss_reg_update(iss, OMAP4_ISS_MEM_ISP_ISIF, ISIF_SYNCEN,
+ ISIF_SYNCEN_DWEN, enable ? ISIF_SYNCEN_DWEN : 0);
+}
+
+/*
+ * ipipeif_enable - Enable/Disable IPIPEIF.
+ * @enable: enable flag
+ *
+ */
+static void ipipeif_enable(struct iss_ipipeif_device *ipipeif, u8 enable)
+{
+ struct iss_device *iss = to_iss_device(ipipeif);
+
+ iss_reg_update(iss, OMAP4_ISS_MEM_ISP_ISIF, ISIF_SYNCEN,
+ ISIF_SYNCEN_SYEN, enable ? ISIF_SYNCEN_SYEN : 0);
+}
+
+/* -----------------------------------------------------------------------------
+ * Format- and pipeline-related configuration helpers
+ */
+
+/*
+ * ipipeif_set_outaddr - Set memory address to save output image
+ * @ipipeif: Pointer to ISP IPIPEIF device.
+ * @addr: 32-bit memory address aligned on 32 byte boundary.
+ *
+ * Sets the memory address where the output will be saved.
+ */
+static void ipipeif_set_outaddr(struct iss_ipipeif_device *ipipeif, u32 addr)
+{
+ struct iss_device *iss = to_iss_device(ipipeif);
+
+ /* Save address splitted in Base Address H & L */
+ iss_reg_write(iss, OMAP4_ISS_MEM_ISP_ISIF, ISIF_CADU,
+ (addr >> (16 + 5)) & ISIF_CADU_MASK);
+ iss_reg_write(iss, OMAP4_ISS_MEM_ISP_ISIF, ISIF_CADL,
+ (addr >> 5) & ISIF_CADL_MASK);
+}
+
+static void ipipeif_configure(struct iss_ipipeif_device *ipipeif)
+{
+ struct iss_device *iss = to_iss_device(ipipeif);
+ const struct iss_format_info *info;
+ struct v4l2_mbus_framefmt *format;
+ u32 isif_ccolp = 0;
+
+ omap4iss_configure_bridge(iss, ipipeif->input);
+
+ /* IPIPEIF_PAD_SINK */
+ format = &ipipeif->formats[IPIPEIF_PAD_SINK];
+
+ /* IPIPEIF with YUV422 input from ISIF */
+ iss_reg_clr(iss, OMAP4_ISS_MEM_ISP_IPIPEIF, IPIPEIF_CFG1,
+ IPIPEIF_CFG1_INPSRC1_MASK | IPIPEIF_CFG1_INPSRC2_MASK);
+
+ /* Select ISIF/IPIPEIF input format */
+ switch (format->code) {
+ case MEDIA_BUS_FMT_UYVY8_1X16:
+ case MEDIA_BUS_FMT_YUYV8_1X16:
+ iss_reg_update(iss, OMAP4_ISS_MEM_ISP_ISIF, ISIF_MODESET,
+ ISIF_MODESET_CCDMD | ISIF_MODESET_INPMOD_MASK |
+ ISIF_MODESET_CCDW_MASK,
+ ISIF_MODESET_INPMOD_YCBCR16);
+
+ iss_reg_update(iss, OMAP4_ISS_MEM_ISP_IPIPEIF, IPIPEIF_CFG2,
+ IPIPEIF_CFG2_YUV8, IPIPEIF_CFG2_YUV16);
+
+ break;
+ case MEDIA_BUS_FMT_SGRBG10_1X10:
+ isif_ccolp = ISIF_CCOLP_CP0_F0_GR |
+ ISIF_CCOLP_CP1_F0_R |
+ ISIF_CCOLP_CP2_F0_B |
+ ISIF_CCOLP_CP3_F0_GB;
+ goto cont_raw;
+ case MEDIA_BUS_FMT_SRGGB10_1X10:
+ isif_ccolp = ISIF_CCOLP_CP0_F0_R |
+ ISIF_CCOLP_CP1_F0_GR |
+ ISIF_CCOLP_CP2_F0_GB |
+ ISIF_CCOLP_CP3_F0_B;
+ goto cont_raw;
+ case MEDIA_BUS_FMT_SBGGR10_1X10:
+ isif_ccolp = ISIF_CCOLP_CP0_F0_B |
+ ISIF_CCOLP_CP1_F0_GB |
+ ISIF_CCOLP_CP2_F0_GR |
+ ISIF_CCOLP_CP3_F0_R;
+ goto cont_raw;
+ case MEDIA_BUS_FMT_SGBRG10_1X10:
+ isif_ccolp = ISIF_CCOLP_CP0_F0_GB |
+ ISIF_CCOLP_CP1_F0_B |
+ ISIF_CCOLP_CP2_F0_R |
+ ISIF_CCOLP_CP3_F0_GR;
+cont_raw:
+ iss_reg_clr(iss, OMAP4_ISS_MEM_ISP_IPIPEIF, IPIPEIF_CFG2,
+ IPIPEIF_CFG2_YUV16);
+
+ iss_reg_update(iss, OMAP4_ISS_MEM_ISP_ISIF, ISIF_MODESET,
+ ISIF_MODESET_CCDMD | ISIF_MODESET_INPMOD_MASK |
+ ISIF_MODESET_CCDW_MASK, ISIF_MODESET_INPMOD_RAW |
+ ISIF_MODESET_CCDW_2BIT);
+
+ info = omap4iss_video_format_info(format->code);
+ iss_reg_update(iss, OMAP4_ISS_MEM_ISP_ISIF, ISIF_CGAMMAWD,
+ ISIF_CGAMMAWD_GWDI_MASK,
+ ISIF_CGAMMAWD_GWDI(info->bpp));
+
+ /* Set RAW Bayer pattern */
+ iss_reg_write(iss, OMAP4_ISS_MEM_ISP_ISIF, ISIF_CCOLP,
+ isif_ccolp);
+ break;
+ }
+
+ iss_reg_write(iss, OMAP4_ISS_MEM_ISP_ISIF, ISIF_SPH, 0 & ISIF_SPH_MASK);
+ iss_reg_write(iss, OMAP4_ISS_MEM_ISP_ISIF, ISIF_LNH,
+ (format->width - 1) & ISIF_LNH_MASK);
+ iss_reg_write(iss, OMAP4_ISS_MEM_ISP_ISIF, ISIF_LNV,
+ (format->height - 1) & ISIF_LNV_MASK);
+
+ /* Generate ISIF0 on the last line of the image */
+ iss_reg_write(iss, OMAP4_ISS_MEM_ISP_ISIF, ISIF_VDINT(0),
+ format->height - 1);
+
+ /* IPIPEIF_PAD_SOURCE_ISIF_SF */
+ format = &ipipeif->formats[IPIPEIF_PAD_SOURCE_ISIF_SF];
+
+ iss_reg_write(iss, OMAP4_ISS_MEM_ISP_ISIF, ISIF_HSIZE,
+ (ipipeif->video_out.bpl_value >> 5) &
+ ISIF_HSIZE_HSIZE_MASK);
+
+ /* IPIPEIF_PAD_SOURCE_VP */
+ /* Do nothing? */
+}
+
+/* -----------------------------------------------------------------------------
+ * Interrupt handling
+ */
+
+static void ipipeif_isr_buffer(struct iss_ipipeif_device *ipipeif)
+{
+ struct iss_buffer *buffer;
+
+ /* The ISIF generates VD0 interrupts even when writes are disabled.
+ * deal with it anyway). Disabling the ISIF when no buffer is available
+ * is thus not be enough, we need to handle the situation explicitly.
+ */
+ if (list_empty(&ipipeif->video_out.dmaqueue))
+ return;
+
+ ipipeif_write_enable(ipipeif, 0);
+
+ buffer = omap4iss_video_buffer_next(&ipipeif->video_out);
+ if (buffer == NULL)
+ return;
+
+ ipipeif_set_outaddr(ipipeif, buffer->iss_addr);
+
+ ipipeif_write_enable(ipipeif, 1);
+}
+
+/*
+ * omap4iss_ipipeif_isr - Configure ipipeif during interframe time.
+ * @ipipeif: Pointer to ISP IPIPEIF device.
+ * @events: IPIPEIF events
+ */
+void omap4iss_ipipeif_isr(struct iss_ipipeif_device *ipipeif, u32 events)
+{
+ if (omap4iss_module_sync_is_stopping(&ipipeif->wait,
+ &ipipeif->stopping))
+ return;
+
+ if ((events & ISP5_IRQ_ISIF_INT(0)) &&
+ (ipipeif->output & IPIPEIF_OUTPUT_MEMORY))
+ ipipeif_isr_buffer(ipipeif);
+}
+
+/* -----------------------------------------------------------------------------
+ * ISP video operations
+ */
+
+static int ipipeif_video_queue(struct iss_video *video,
+ struct iss_buffer *buffer)
+{
+ struct iss_ipipeif_device *ipipeif = container_of(video,
+ struct iss_ipipeif_device, video_out);
+
+ if (!(ipipeif->output & IPIPEIF_OUTPUT_MEMORY))
+ return -ENODEV;
+
+ ipipeif_set_outaddr(ipipeif, buffer->iss_addr);
+
+ /*
+ * If streaming was enabled before there was a buffer queued
+ * or underrun happened in the ISR, the hardware was not enabled
+ * and DMA queue flag ISS_VIDEO_DMAQUEUE_UNDERRUN is still set.
+ * Enable it now.
+ */
+ if (video->dmaqueue_flags & ISS_VIDEO_DMAQUEUE_UNDERRUN) {
+ if (ipipeif->output & IPIPEIF_OUTPUT_MEMORY)
+ ipipeif_write_enable(ipipeif, 1);
+ ipipeif_enable(ipipeif, 1);
+ iss_video_dmaqueue_flags_clr(video);
+ }
+
+ return 0;
+}
+
+static const struct iss_video_operations ipipeif_video_ops = {
+ .queue = ipipeif_video_queue,
+};
+
+/* -----------------------------------------------------------------------------
+ * V4L2 subdev operations
+ */
+
+#define IPIPEIF_DRV_SUBCLK_MASK (OMAP4_ISS_ISP_SUBCLK_IPIPEIF |\
+ OMAP4_ISS_ISP_SUBCLK_ISIF)
+/*
+ * ipipeif_set_stream - Enable/Disable streaming on the IPIPEIF module
+ * @sd: ISP IPIPEIF V4L2 subdevice
+ * @enable: Enable/disable stream
+ */
+static int ipipeif_set_stream(struct v4l2_subdev *sd, int enable)
+{
+ struct iss_ipipeif_device *ipipeif = v4l2_get_subdevdata(sd);
+ struct iss_device *iss = to_iss_device(ipipeif);
+ struct iss_video *video_out = &ipipeif->video_out;
+ int ret = 0;
+
+ if (ipipeif->state == ISS_PIPELINE_STREAM_STOPPED) {
+ if (enable == ISS_PIPELINE_STREAM_STOPPED)
+ return 0;
+
+ omap4iss_isp_subclk_enable(iss, IPIPEIF_DRV_SUBCLK_MASK);
+ }
+
+ switch (enable) {
+ case ISS_PIPELINE_STREAM_CONTINUOUS:
+
+ ipipeif_configure(ipipeif);
+ ipipeif_print_status(ipipeif);
+
+ /*
+ * When outputting to memory with no buffer available, let the
+ * buffer queue handler start the hardware. A DMA queue flag
+ * ISS_VIDEO_DMAQUEUE_QUEUED will be set as soon as there is
+ * a buffer available.
+ */
+ if (ipipeif->output & IPIPEIF_OUTPUT_MEMORY &&
+ !(video_out->dmaqueue_flags & ISS_VIDEO_DMAQUEUE_QUEUED))
+ break;
+
+ atomic_set(&ipipeif->stopping, 0);
+ if (ipipeif->output & IPIPEIF_OUTPUT_MEMORY)
+ ipipeif_write_enable(ipipeif, 1);
+ ipipeif_enable(ipipeif, 1);
+ iss_video_dmaqueue_flags_clr(video_out);
+ break;
+
+ case ISS_PIPELINE_STREAM_STOPPED:
+ if (ipipeif->state == ISS_PIPELINE_STREAM_STOPPED)
+ return 0;
+ if (omap4iss_module_sync_idle(&sd->entity, &ipipeif->wait,
+ &ipipeif->stopping))
+ ret = -ETIMEDOUT;
+
+ if (ipipeif->output & IPIPEIF_OUTPUT_MEMORY)
+ ipipeif_write_enable(ipipeif, 0);
+ ipipeif_enable(ipipeif, 0);
+ omap4iss_isp_subclk_disable(iss, IPIPEIF_DRV_SUBCLK_MASK);
+ iss_video_dmaqueue_flags_clr(video_out);
+ break;
+ }
+
+ ipipeif->state = enable;
+ return ret;
+}
+
+static struct v4l2_mbus_framefmt *
+__ipipeif_get_format(struct iss_ipipeif_device *ipipeif,
+ struct v4l2_subdev_pad_config *cfg, unsigned int pad,
+ enum v4l2_subdev_format_whence which)
+{
+ if (which == V4L2_SUBDEV_FORMAT_TRY)
+ return v4l2_subdev_get_try_format(&ipipeif->subdev, cfg, pad);
+ return &ipipeif->formats[pad];
+}
+
+/*
+ * ipipeif_try_format - Try video format on a pad
+ * @ipipeif: ISS IPIPEIF device
+ * @cfg: V4L2 subdev pad config
+ * @pad: Pad number
+ * @fmt: Format
+ */
+static void
+ipipeif_try_format(struct iss_ipipeif_device *ipipeif,
+ struct v4l2_subdev_pad_config *cfg, unsigned int pad,
+ struct v4l2_mbus_framefmt *fmt,
+ enum v4l2_subdev_format_whence which)
+{
+ struct v4l2_mbus_framefmt *format;
+ unsigned int width = fmt->width;
+ unsigned int height = fmt->height;
+ unsigned int i;
+
+ switch (pad) {
+ case IPIPEIF_PAD_SINK:
+ /* TODO: If the IPIPEIF output formatter pad is connected
+ * directly to the resizer, only YUV formats can be used.
+ */
+ for (i = 0; i < ARRAY_SIZE(ipipeif_fmts); i++) {
+ if (fmt->code == ipipeif_fmts[i])
+ break;
+ }
+
+ /* If not found, use SGRBG10 as default */
+ if (i >= ARRAY_SIZE(ipipeif_fmts))
+ fmt->code = MEDIA_BUS_FMT_SGRBG10_1X10;
+
+ /* Clamp the input size. */
+ fmt->width = clamp_t(u32, width, 1, 8192);
+ fmt->height = clamp_t(u32, height, 1, 8192);
+ break;
+
+ case IPIPEIF_PAD_SOURCE_ISIF_SF:
+ format = __ipipeif_get_format(ipipeif, cfg, IPIPEIF_PAD_SINK,
+ which);
+ memcpy(fmt, format, sizeof(*fmt));
+
+ /* The data formatter truncates the number of horizontal output
+ * pixels to a multiple of 16. To avoid clipping data, allow
+ * callers to request an output size bigger than the input size
+ * up to the nearest multiple of 16.
+ */
+ fmt->width = clamp_t(u32, width, 32, (fmt->width + 15) & ~15);
+ fmt->width &= ~15;
+ fmt->height = clamp_t(u32, height, 32, fmt->height);
+ break;
+
+ case IPIPEIF_PAD_SOURCE_VP:
+ format = __ipipeif_get_format(ipipeif, cfg, IPIPEIF_PAD_SINK,
+ which);
+ memcpy(fmt, format, sizeof(*fmt));
+
+ fmt->width = clamp_t(u32, width, 32, fmt->width);
+ fmt->height = clamp_t(u32, height, 32, fmt->height);
+ break;
+ }
+
+ /* Data is written to memory unpacked, each 10-bit or 12-bit pixel is
+ * stored on 2 bytes.
+ */
+ fmt->colorspace = V4L2_COLORSPACE_SRGB;
+ fmt->field = V4L2_FIELD_NONE;
+}
+
+/*
+ * ipipeif_enum_mbus_code - Handle pixel format enumeration
+ * @sd : pointer to v4l2 subdev structure
+ * @cfg : V4L2 subdev pad config
+ * @code : pointer to v4l2_subdev_mbus_code_enum structure
+ * return -EINVAL or zero on success
+ */
+static int ipipeif_enum_mbus_code(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_mbus_code_enum *code)
+{
+ struct iss_ipipeif_device *ipipeif = v4l2_get_subdevdata(sd);
+ struct v4l2_mbus_framefmt *format;
+
+ switch (code->pad) {
+ case IPIPEIF_PAD_SINK:
+ if (code->index >= ARRAY_SIZE(ipipeif_fmts))
+ return -EINVAL;
+
+ code->code = ipipeif_fmts[code->index];
+ break;
+
+ case IPIPEIF_PAD_SOURCE_ISIF_SF:
+ case IPIPEIF_PAD_SOURCE_VP:
+ /* No format conversion inside IPIPEIF */
+ if (code->index != 0)
+ return -EINVAL;
+
+ format = __ipipeif_get_format(ipipeif, cfg, IPIPEIF_PAD_SINK,
+ code->which);
+
+ code->code = format->code;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int ipipeif_enum_frame_size(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_frame_size_enum *fse)
+{
+ struct iss_ipipeif_device *ipipeif = v4l2_get_subdevdata(sd);
+ struct v4l2_mbus_framefmt format;
+
+ if (fse->index != 0)
+ return -EINVAL;
+
+ format.code = fse->code;
+ format.width = 1;
+ format.height = 1;
+ ipipeif_try_format(ipipeif, cfg, fse->pad, &format, fse->which);
+ fse->min_width = format.width;
+ fse->min_height = format.height;
+
+ if (format.code != fse->code)
+ return -EINVAL;
+
+ format.code = fse->code;
+ format.width = -1;
+ format.height = -1;
+ ipipeif_try_format(ipipeif, cfg, fse->pad, &format, fse->which);
+ fse->max_width = format.width;
+ fse->max_height = format.height;
+
+ return 0;
+}
+
+/*
+ * ipipeif_get_format - Retrieve the video format on a pad
+ * @sd : ISP IPIPEIF V4L2 subdevice
+ * @cfg: V4L2 subdev pad config
+ * @fmt: Format
+ *
+ * Return 0 on success or -EINVAL if the pad is invalid or doesn't correspond
+ * to the format type.
+ */
+static int ipipeif_get_format(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_format *fmt)
+{
+ struct iss_ipipeif_device *ipipeif = v4l2_get_subdevdata(sd);
+ struct v4l2_mbus_framefmt *format;
+
+ format = __ipipeif_get_format(ipipeif, cfg, fmt->pad, fmt->which);
+ if (format == NULL)
+ return -EINVAL;
+
+ fmt->format = *format;
+ return 0;
+}
+
+/*
+ * ipipeif_set_format - Set the video format on a pad
+ * @sd : ISP IPIPEIF V4L2 subdevice
+ * @cfg: V4L2 subdev pad config
+ * @fmt: Format
+ *
+ * Return 0 on success or -EINVAL if the pad is invalid or doesn't correspond
+ * to the format type.
+ */
+static int ipipeif_set_format(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_format *fmt)
+{
+ struct iss_ipipeif_device *ipipeif = v4l2_get_subdevdata(sd);
+ struct v4l2_mbus_framefmt *format;
+
+ format = __ipipeif_get_format(ipipeif, cfg, fmt->pad, fmt->which);
+ if (format == NULL)
+ return -EINVAL;
+
+ ipipeif_try_format(ipipeif, cfg, fmt->pad, &fmt->format, fmt->which);
+ *format = fmt->format;
+
+ /* Propagate the format from sink to source */
+ if (fmt->pad == IPIPEIF_PAD_SINK) {
+ format = __ipipeif_get_format(ipipeif, cfg,
+ IPIPEIF_PAD_SOURCE_ISIF_SF,
+ fmt->which);
+ *format = fmt->format;
+ ipipeif_try_format(ipipeif, cfg, IPIPEIF_PAD_SOURCE_ISIF_SF,
+ format, fmt->which);
+
+ format = __ipipeif_get_format(ipipeif, cfg,
+ IPIPEIF_PAD_SOURCE_VP,
+ fmt->which);
+ *format = fmt->format;
+ ipipeif_try_format(ipipeif, cfg, IPIPEIF_PAD_SOURCE_VP, format,
+ fmt->which);
+ }
+
+ return 0;
+}
+
+static int ipipeif_link_validate(struct v4l2_subdev *sd,
+ struct media_link *link,
+ struct v4l2_subdev_format *source_fmt,
+ struct v4l2_subdev_format *sink_fmt)
+{
+ /* Check if the two ends match */
+ if (source_fmt->format.width != sink_fmt->format.width ||
+ source_fmt->format.height != sink_fmt->format.height)
+ return -EPIPE;
+
+ if (source_fmt->format.code != sink_fmt->format.code)
+ return -EPIPE;
+
+ return 0;
+}
+
+/*
+ * ipipeif_init_formats - Initialize formats on all pads
+ * @sd: ISP IPIPEIF V4L2 subdevice
+ * @fh: V4L2 subdev file handle
+ *
+ * Initialize all pad formats with default values. If fh is not NULL, try
+ * formats are initialized on the file handle. Otherwise active formats are
+ * initialized on the device.
+ */
+static int ipipeif_init_formats(struct v4l2_subdev *sd,
+ struct v4l2_subdev_fh *fh)
+{
+ struct v4l2_subdev_format format;
+
+ memset(&format, 0, sizeof(format));
+ format.pad = IPIPEIF_PAD_SINK;
+ format.which = fh ? V4L2_SUBDEV_FORMAT_TRY : V4L2_SUBDEV_FORMAT_ACTIVE;
+ format.format.code = MEDIA_BUS_FMT_SGRBG10_1X10;
+ format.format.width = 4096;
+ format.format.height = 4096;
+ ipipeif_set_format(sd, fh ? fh->pad : NULL, &format);
+
+ return 0;
+}
+
+/* V4L2 subdev video operations */
+static const struct v4l2_subdev_video_ops ipipeif_v4l2_video_ops = {
+ .s_stream = ipipeif_set_stream,
+};
+
+/* V4L2 subdev pad operations */
+static const struct v4l2_subdev_pad_ops ipipeif_v4l2_pad_ops = {
+ .enum_mbus_code = ipipeif_enum_mbus_code,
+ .enum_frame_size = ipipeif_enum_frame_size,
+ .get_fmt = ipipeif_get_format,
+ .set_fmt = ipipeif_set_format,
+ .link_validate = ipipeif_link_validate,
+};
+
+/* V4L2 subdev operations */
+static const struct v4l2_subdev_ops ipipeif_v4l2_ops = {
+ .video = &ipipeif_v4l2_video_ops,
+ .pad = &ipipeif_v4l2_pad_ops,
+};
+
+/* V4L2 subdev internal operations */
+static const struct v4l2_subdev_internal_ops ipipeif_v4l2_internal_ops = {
+ .open = ipipeif_init_formats,
+};
+
+/* -----------------------------------------------------------------------------
+ * Media entity operations
+ */
+
+/*
+ * ipipeif_link_setup - Setup IPIPEIF connections
+ * @entity: IPIPEIF media entity
+ * @local: Pad at the local end of the link
+ * @remote: Pad at the remote end of the link
+ * @flags: Link flags
+ *
+ * return -EINVAL or zero on success
+ */
+static int ipipeif_link_setup(struct media_entity *entity,
+ const struct media_pad *local,
+ const struct media_pad *remote, u32 flags)
+{
+ struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity);
+ struct iss_ipipeif_device *ipipeif = v4l2_get_subdevdata(sd);
+ struct iss_device *iss = to_iss_device(ipipeif);
+
+ switch (local->index | media_entity_type(remote->entity)) {
+ case IPIPEIF_PAD_SINK | MEDIA_ENT_T_V4L2_SUBDEV:
+ /* Read from the sensor CSI2a or CSI2b. */
+ if (!(flags & MEDIA_LNK_FL_ENABLED)) {
+ ipipeif->input = IPIPEIF_INPUT_NONE;
+ break;
+ }
+
+ if (ipipeif->input != IPIPEIF_INPUT_NONE)
+ return -EBUSY;
+
+ if (remote->entity == &iss->csi2a.subdev.entity)
+ ipipeif->input = IPIPEIF_INPUT_CSI2A;
+ else if (remote->entity == &iss->csi2b.subdev.entity)
+ ipipeif->input = IPIPEIF_INPUT_CSI2B;
+
+ break;
+
+ case IPIPEIF_PAD_SOURCE_ISIF_SF | MEDIA_ENT_T_DEVNODE:
+ /* Write to memory */
+ if (flags & MEDIA_LNK_FL_ENABLED) {
+ if (ipipeif->output & ~IPIPEIF_OUTPUT_MEMORY)
+ return -EBUSY;
+ ipipeif->output |= IPIPEIF_OUTPUT_MEMORY;
+ } else {
+ ipipeif->output &= ~IPIPEIF_OUTPUT_MEMORY;
+ }
+ break;
+
+ case IPIPEIF_PAD_SOURCE_VP | MEDIA_ENT_T_V4L2_SUBDEV:
+ /* Send to IPIPE/RESIZER */
+ if (flags & MEDIA_LNK_FL_ENABLED) {
+ if (ipipeif->output & ~IPIPEIF_OUTPUT_VP)
+ return -EBUSY;
+ ipipeif->output |= IPIPEIF_OUTPUT_VP;
+ } else {
+ ipipeif->output &= ~IPIPEIF_OUTPUT_VP;
+ }
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/* media operations */
+static const struct media_entity_operations ipipeif_media_ops = {
+ .link_setup = ipipeif_link_setup,
+ .link_validate = v4l2_subdev_link_validate,
+};
+
+/*
+ * ipipeif_init_entities - Initialize V4L2 subdev and media entity
+ * @ipipeif: ISS ISP IPIPEIF module
+ *
+ * Return 0 on success and a negative error code on failure.
+ */
+static int ipipeif_init_entities(struct iss_ipipeif_device *ipipeif)
+{
+ struct v4l2_subdev *sd = &ipipeif->subdev;
+ struct media_pad *pads = ipipeif->pads;
+ struct media_entity *me = &sd->entity;
+ int ret;
+
+ ipipeif->input = IPIPEIF_INPUT_NONE;
+
+ v4l2_subdev_init(sd, &ipipeif_v4l2_ops);
+ sd->internal_ops = &ipipeif_v4l2_internal_ops;
+ strlcpy(sd->name, "OMAP4 ISS ISP IPIPEIF", sizeof(sd->name));
+ sd->grp_id = 1 << 16; /* group ID for iss subdevs */
+ v4l2_set_subdevdata(sd, ipipeif);
+ sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+
+ pads[IPIPEIF_PAD_SINK].flags = MEDIA_PAD_FL_SINK;
+ pads[IPIPEIF_PAD_SOURCE_ISIF_SF].flags = MEDIA_PAD_FL_SOURCE;
+ pads[IPIPEIF_PAD_SOURCE_VP].flags = MEDIA_PAD_FL_SOURCE;
+
+ me->ops = &ipipeif_media_ops;
+ ret = media_entity_init(me, IPIPEIF_PADS_NUM, pads, 0);
+ if (ret < 0)
+ return ret;
+
+ ipipeif_init_formats(sd, NULL);
+
+ ipipeif->video_out.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ ipipeif->video_out.ops = &ipipeif_video_ops;
+ ipipeif->video_out.iss = to_iss_device(ipipeif);
+ ipipeif->video_out.capture_mem = PAGE_ALIGN(4096 * 4096) * 3;
+ ipipeif->video_out.bpl_alignment = 32;
+ ipipeif->video_out.bpl_zero_padding = 1;
+ ipipeif->video_out.bpl_max = 0x1ffe0;
+
+ ret = omap4iss_video_init(&ipipeif->video_out, "ISP IPIPEIF");
+ if (ret < 0)
+ return ret;
+
+ /* Connect the IPIPEIF subdev to the video node. */
+ ret = media_entity_create_link(&ipipeif->subdev.entity,
+ IPIPEIF_PAD_SOURCE_ISIF_SF,
+ &ipipeif->video_out.video.entity, 0, 0);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+void omap4iss_ipipeif_unregister_entities(struct iss_ipipeif_device *ipipeif)
+{
+ v4l2_device_unregister_subdev(&ipipeif->subdev);
+ omap4iss_video_unregister(&ipipeif->video_out);
+}
+
+int omap4iss_ipipeif_register_entities(struct iss_ipipeif_device *ipipeif,
+ struct v4l2_device *vdev)
+{
+ int ret;
+
+ /* Register the subdev and video node. */
+ ret = v4l2_device_register_subdev(vdev, &ipipeif->subdev);
+ if (ret < 0)
+ goto error;
+
+ ret = omap4iss_video_register(&ipipeif->video_out, vdev);
+ if (ret < 0)
+ goto error;
+
+ return 0;
+
+error:
+ omap4iss_ipipeif_unregister_entities(ipipeif);
+ return ret;
+}
+
+/* -----------------------------------------------------------------------------
+ * ISP IPIPEIF initialisation and cleanup
+ */
+
+/*
+ * omap4iss_ipipeif_init - IPIPEIF module initialization.
+ * @iss: Device pointer specific to the OMAP4 ISS.
+ *
+ * TODO: Get the initialisation values from platform data.
+ *
+ * Return 0 on success or a negative error code otherwise.
+ */
+int omap4iss_ipipeif_init(struct iss_device *iss)
+{
+ struct iss_ipipeif_device *ipipeif = &iss->ipipeif;
+
+ ipipeif->state = ISS_PIPELINE_STREAM_STOPPED;
+ init_waitqueue_head(&ipipeif->wait);
+
+ return ipipeif_init_entities(ipipeif);
+}
+
+/*
+ * omap4iss_ipipeif_cleanup - IPIPEIF module cleanup.
+ * @iss: Device pointer specific to the OMAP4 ISS.
+ */
+void omap4iss_ipipeif_cleanup(struct iss_device *iss)
+{
+ struct iss_ipipeif_device *ipipeif = &iss->ipipeif;
+
+ media_entity_cleanup(&ipipeif->subdev.entity);
+}
diff --git a/drivers/staging/media/omap4iss/iss_ipipeif.h b/drivers/staging/media/omap4iss/iss_ipipeif.h
new file mode 100644
index 000000000..cbdccb982
--- /dev/null
+++ b/drivers/staging/media/omap4iss/iss_ipipeif.h
@@ -0,0 +1,92 @@
+/*
+ * TI OMAP4 ISS V4L2 Driver - ISP IPIPEIF module
+ *
+ * Copyright (C) 2012 Texas Instruments, Inc.
+ *
+ * Author: Sergio Aguirre <sergio.a.aguirre@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef OMAP4_ISS_IPIPEIF_H
+#define OMAP4_ISS_IPIPEIF_H
+
+#include "iss_video.h"
+
+enum ipipeif_input_entity {
+ IPIPEIF_INPUT_NONE,
+ IPIPEIF_INPUT_CSI2A,
+ IPIPEIF_INPUT_CSI2B
+};
+
+#define IPIPEIF_OUTPUT_MEMORY (1 << 0)
+#define IPIPEIF_OUTPUT_VP (1 << 1)
+
+/* Sink and source IPIPEIF pads */
+#define IPIPEIF_PAD_SINK 0
+#define IPIPEIF_PAD_SOURCE_ISIF_SF 1
+#define IPIPEIF_PAD_SOURCE_VP 2
+#define IPIPEIF_PADS_NUM 3
+
+/*
+ * struct iss_ipipeif_device - Structure for the IPIPEIF module to store its own
+ * information
+ * @subdev: V4L2 subdevice
+ * @pads: Sink and source media entity pads
+ * @formats: Active video formats
+ * @input: Active input
+ * @output: Active outputs
+ * @video_out: Output video node
+ * @error: A hardware error occurred during capture
+ * @alaw: A-law compression enabled (1) or disabled (0)
+ * @lpf: Low pass filter enabled (1) or disabled (0)
+ * @obclamp: Optical-black clamp enabled (1) or disabled (0)
+ * @fpc_en: Faulty pixels correction enabled (1) or disabled (0)
+ * @blcomp: Black level compensation configuration
+ * @clamp: Optical-black or digital clamp configuration
+ * @fpc: Faulty pixels correction configuration
+ * @lsc: Lens shading compensation configuration
+ * @update: Bitmask of controls to update during the next interrupt
+ * @shadow_update: Controls update in progress by userspace
+ * @syncif: Interface synchronization configuration
+ * @vpcfg: Video port configuration
+ * @underrun: A buffer underrun occurred and a new buffer has been queued
+ * @state: Streaming state
+ * @lock: Serializes shadow_update with interrupt handler
+ * @wait: Wait queue used to stop the module
+ * @stopping: Stopping state
+ * @ioctl_lock: Serializes ioctl calls and LSC requests freeing
+ */
+struct iss_ipipeif_device {
+ struct v4l2_subdev subdev;
+ struct media_pad pads[IPIPEIF_PADS_NUM];
+ struct v4l2_mbus_framefmt formats[IPIPEIF_PADS_NUM];
+
+ enum ipipeif_input_entity input;
+ unsigned int output;
+ struct iss_video video_out;
+ unsigned int error;
+
+ enum iss_pipeline_stream_state state;
+ wait_queue_head_t wait;
+ atomic_t stopping;
+};
+
+struct iss_device;
+
+int omap4iss_ipipeif_init(struct iss_device *iss);
+void omap4iss_ipipeif_cleanup(struct iss_device *iss);
+int omap4iss_ipipeif_register_entities(struct iss_ipipeif_device *ipipeif,
+ struct v4l2_device *vdev);
+void omap4iss_ipipeif_unregister_entities(struct iss_ipipeif_device *ipipeif);
+
+int omap4iss_ipipeif_busy(struct iss_ipipeif_device *ipipeif);
+void omap4iss_ipipeif_isr(struct iss_ipipeif_device *ipipeif, u32 events);
+void omap4iss_ipipeif_restore_context(struct iss_device *iss);
+void omap4iss_ipipeif_max_rate(struct iss_ipipeif_device *ipipeif,
+ unsigned int *max_rate);
+
+#endif /* OMAP4_ISS_IPIPEIF_H */
diff --git a/drivers/staging/media/omap4iss/iss_regs.h b/drivers/staging/media/omap4iss/iss_regs.h
new file mode 100644
index 000000000..d2b6b6ae9
--- /dev/null
+++ b/drivers/staging/media/omap4iss/iss_regs.h
@@ -0,0 +1,903 @@
+/*
+ * TI OMAP4 ISS V4L2 Driver - Register defines
+ *
+ * Copyright (C) 2012 Texas Instruments.
+ *
+ * Author: Sergio Aguirre <sergio.a.aguirre@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef _OMAP4_ISS_REGS_H_
+#define _OMAP4_ISS_REGS_H_
+
+/* ISS */
+#define ISS_HL_REVISION 0x0
+
+#define ISS_HL_SYSCONFIG 0x10
+#define ISS_HL_SYSCONFIG_IDLEMODE_SHIFT 2
+#define ISS_HL_SYSCONFIG_IDLEMODE_FORCEIDLE 0x0
+#define ISS_HL_SYSCONFIG_IDLEMODE_NOIDLE 0x1
+#define ISS_HL_SYSCONFIG_IDLEMODE_SMARTIDLE 0x2
+#define ISS_HL_SYSCONFIG_SOFTRESET (1 << 0)
+
+#define ISS_HL_IRQSTATUS_RAW(i) (0x20 + (0x10 * (i)))
+#define ISS_HL_IRQSTATUS(i) (0x24 + (0x10 * (i)))
+#define ISS_HL_IRQENABLE_SET(i) (0x28 + (0x10 * (i)))
+#define ISS_HL_IRQENABLE_CLR(i) (0x2c + (0x10 * (i)))
+
+#define ISS_HL_IRQ_HS_VS (1 << 17)
+#define ISS_HL_IRQ_SIMCOP(i) (1 << (12 + (i)))
+#define ISS_HL_IRQ_BTE (1 << 11)
+#define ISS_HL_IRQ_CBUFF (1 << 10)
+#define ISS_HL_IRQ_CCP2(i) (1 << ((i) > 3 ? 16 : 14 + (i)))
+#define ISS_HL_IRQ_CSIB (1 << 5)
+#define ISS_HL_IRQ_CSIA (1 << 4)
+#define ISS_HL_IRQ_ISP(i) (1 << (i))
+
+#define ISS_CTRL 0x80
+#define ISS_CTRL_CLK_DIV_MASK (3 << 4)
+#define ISS_CTRL_INPUT_SEL_MASK (3 << 2)
+#define ISS_CTRL_INPUT_SEL_CSI2A (0 << 2)
+#define ISS_CTRL_INPUT_SEL_CSI2B (1 << 2)
+#define ISS_CTRL_SYNC_DETECT_VS_RAISING (3 << 0)
+
+#define ISS_CLKCTRL 0x84
+#define ISS_CLKCTRL_VPORT2_CLK (1 << 30)
+#define ISS_CLKCTRL_VPORT1_CLK (1 << 29)
+#define ISS_CLKCTRL_VPORT0_CLK (1 << 28)
+#define ISS_CLKCTRL_CCP2 (1 << 4)
+#define ISS_CLKCTRL_CSI2_B (1 << 3)
+#define ISS_CLKCTRL_CSI2_A (1 << 2)
+#define ISS_CLKCTRL_ISP (1 << 1)
+#define ISS_CLKCTRL_SIMCOP (1 << 0)
+
+#define ISS_CLKSTAT 0x88
+#define ISS_CLKSTAT_VPORT2_CLK (1 << 30)
+#define ISS_CLKSTAT_VPORT1_CLK (1 << 29)
+#define ISS_CLKSTAT_VPORT0_CLK (1 << 28)
+#define ISS_CLKSTAT_CCP2 (1 << 4)
+#define ISS_CLKSTAT_CSI2_B (1 << 3)
+#define ISS_CLKSTAT_CSI2_A (1 << 2)
+#define ISS_CLKSTAT_ISP (1 << 1)
+#define ISS_CLKSTAT_SIMCOP (1 << 0)
+
+#define ISS_PM_STATUS 0x8c
+#define ISS_PM_STATUS_CBUFF_PM_MASK (3 << 12)
+#define ISS_PM_STATUS_BTE_PM_MASK (3 << 10)
+#define ISS_PM_STATUS_SIMCOP_PM_MASK (3 << 8)
+#define ISS_PM_STATUS_ISP_PM_MASK (3 << 6)
+#define ISS_PM_STATUS_CCP2_PM_MASK (3 << 4)
+#define ISS_PM_STATUS_CSI2_B_PM_MASK (3 << 2)
+#define ISS_PM_STATUS_CSI2_A_PM_MASK (3 << 0)
+
+#define REGISTER0 0x0
+#define REGISTER0_HSCLOCKCONFIG (1 << 24)
+#define REGISTER0_THS_TERM_MASK (0xff << 8)
+#define REGISTER0_THS_TERM_SHIFT 8
+#define REGISTER0_THS_SETTLE_MASK (0xff << 0)
+#define REGISTER0_THS_SETTLE_SHIFT 0
+
+#define REGISTER1 0x4
+#define REGISTER1_RESET_DONE_CTRLCLK (1 << 29)
+#define REGISTER1_CLOCK_MISS_DETECTOR_STATUS (1 << 25)
+#define REGISTER1_TCLK_TERM_MASK (0x3f << 18)
+#define REGISTER1_TCLK_TERM_SHIFT 18
+#define REGISTER1_DPHY_HS_SYNC_PATTERN_SHIFT 10
+#define REGISTER1_CTRLCLK_DIV_FACTOR_MASK (0x3 << 8)
+#define REGISTER1_CTRLCLK_DIV_FACTOR_SHIFT 8
+#define REGISTER1_TCLK_SETTLE_MASK (0xff << 0)
+#define REGISTER1_TCLK_SETTLE_SHIFT 0
+
+#define REGISTER2 0x8
+
+#define CSI2_SYSCONFIG 0x10
+#define CSI2_SYSCONFIG_MSTANDBY_MODE_MASK (3 << 12)
+#define CSI2_SYSCONFIG_MSTANDBY_MODE_FORCE (0 << 12)
+#define CSI2_SYSCONFIG_MSTANDBY_MODE_NO (1 << 12)
+#define CSI2_SYSCONFIG_MSTANDBY_MODE_SMART (2 << 12)
+#define CSI2_SYSCONFIG_SOFT_RESET (1 << 1)
+#define CSI2_SYSCONFIG_AUTO_IDLE (1 << 0)
+
+#define CSI2_SYSSTATUS 0x14
+#define CSI2_SYSSTATUS_RESET_DONE (1 << 0)
+
+#define CSI2_IRQSTATUS 0x18
+#define CSI2_IRQENABLE 0x1c
+
+/* Shared bits across CSI2_IRQENABLE and IRQSTATUS */
+
+#define CSI2_IRQ_OCP_ERR (1 << 14)
+#define CSI2_IRQ_SHORT_PACKET (1 << 13)
+#define CSI2_IRQ_ECC_CORRECTION (1 << 12)
+#define CSI2_IRQ_ECC_NO_CORRECTION (1 << 11)
+#define CSI2_IRQ_COMPLEXIO_ERR (1 << 9)
+#define CSI2_IRQ_FIFO_OVF (1 << 8)
+#define CSI2_IRQ_CONTEXT0 (1 << 0)
+
+#define CSI2_CTRL 0x40
+#define CSI2_CTRL_MFLAG_LEVH_MASK (7 << 20)
+#define CSI2_CTRL_MFLAG_LEVH_SHIFT 20
+#define CSI2_CTRL_MFLAG_LEVL_MASK (7 << 17)
+#define CSI2_CTRL_MFLAG_LEVL_SHIFT 17
+#define CSI2_CTRL_BURST_SIZE_EXPAND (1 << 16)
+#define CSI2_CTRL_VP_CLK_EN (1 << 15)
+#define CSI2_CTRL_NON_POSTED_WRITE (1 << 13)
+#define CSI2_CTRL_VP_ONLY_EN (1 << 11)
+#define CSI2_CTRL_VP_OUT_CTRL_MASK (3 << 8)
+#define CSI2_CTRL_VP_OUT_CTRL_SHIFT 8
+#define CSI2_CTRL_DBG_EN (1 << 7)
+#define CSI2_CTRL_BURST_SIZE_MASK (3 << 5)
+#define CSI2_CTRL_ENDIANNESS (1 << 4)
+#define CSI2_CTRL_FRAME (1 << 3)
+#define CSI2_CTRL_ECC_EN (1 << 2)
+#define CSI2_CTRL_IF_EN (1 << 0)
+
+#define CSI2_DBG_H 0x44
+
+#define CSI2_COMPLEXIO_CFG 0x50
+#define CSI2_COMPLEXIO_CFG_RESET_CTRL (1 << 30)
+#define CSI2_COMPLEXIO_CFG_RESET_DONE (1 << 29)
+#define CSI2_COMPLEXIO_CFG_PWD_CMD_MASK (3 << 27)
+#define CSI2_COMPLEXIO_CFG_PWD_CMD_OFF (0 << 27)
+#define CSI2_COMPLEXIO_CFG_PWD_CMD_ON (1 << 27)
+#define CSI2_COMPLEXIO_CFG_PWD_CMD_ULP (2 << 27)
+#define CSI2_COMPLEXIO_CFG_PWD_STATUS_MASK (3 << 25)
+#define CSI2_COMPLEXIO_CFG_PWD_STATUS_OFF (0 << 25)
+#define CSI2_COMPLEXIO_CFG_PWD_STATUS_ON (1 << 25)
+#define CSI2_COMPLEXIO_CFG_PWD_STATUS_ULP (2 << 25)
+#define CSI2_COMPLEXIO_CFG_PWR_AUTO (1 << 24)
+#define CSI2_COMPLEXIO_CFG_DATA_POL(i) (1 << (((i) * 4) + 3))
+#define CSI2_COMPLEXIO_CFG_DATA_POSITION_MASK(i) (7 << ((i) * 4))
+#define CSI2_COMPLEXIO_CFG_DATA_POSITION_SHIFT(i) ((i) * 4)
+#define CSI2_COMPLEXIO_CFG_CLOCK_POL (1 << 3)
+#define CSI2_COMPLEXIO_CFG_CLOCK_POSITION_MASK (7 << 0)
+#define CSI2_COMPLEXIO_CFG_CLOCK_POSITION_SHIFT 0
+
+#define CSI2_COMPLEXIO_IRQSTATUS 0x54
+
+#define CSI2_SHORT_PACKET 0x5c
+
+#define CSI2_COMPLEXIO_IRQENABLE 0x60
+
+/* Shared bits across CSI2_COMPLEXIO_IRQENABLE and IRQSTATUS */
+#define CSI2_COMPLEXIO_IRQ_STATEALLULPMEXIT (1 << 26)
+#define CSI2_COMPLEXIO_IRQ_STATEALLULPMENTER (1 << 25)
+#define CSI2_COMPLEXIO_IRQ_STATEULPM5 (1 << 24)
+#define CSI2_COMPLEXIO_IRQ_STATEULPM4 (1 << 23)
+#define CSI2_COMPLEXIO_IRQ_STATEULPM3 (1 << 22)
+#define CSI2_COMPLEXIO_IRQ_STATEULPM2 (1 << 21)
+#define CSI2_COMPLEXIO_IRQ_STATEULPM1 (1 << 20)
+#define CSI2_COMPLEXIO_IRQ_ERRCONTROL5 (1 << 19)
+#define CSI2_COMPLEXIO_IRQ_ERRCONTROL4 (1 << 18)
+#define CSI2_COMPLEXIO_IRQ_ERRCONTROL3 (1 << 17)
+#define CSI2_COMPLEXIO_IRQ_ERRCONTROL2 (1 << 16)
+#define CSI2_COMPLEXIO_IRQ_ERRCONTROL1 (1 << 15)
+#define CSI2_COMPLEXIO_IRQ_ERRESC5 (1 << 14)
+#define CSI2_COMPLEXIO_IRQ_ERRESC4 (1 << 13)
+#define CSI2_COMPLEXIO_IRQ_ERRESC3 (1 << 12)
+#define CSI2_COMPLEXIO_IRQ_ERRESC2 (1 << 11)
+#define CSI2_COMPLEXIO_IRQ_ERRESC1 (1 << 10)
+#define CSI2_COMPLEXIO_IRQ_ERRSOTSYNCHS5 (1 << 9)
+#define CSI2_COMPLEXIO_IRQ_ERRSOTSYNCHS4 (1 << 8)
+#define CSI2_COMPLEXIO_IRQ_ERRSOTSYNCHS3 (1 << 7)
+#define CSI2_COMPLEXIO_IRQ_ERRSOTSYNCHS2 (1 << 6)
+#define CSI2_COMPLEXIO_IRQ_ERRSOTSYNCHS1 (1 << 5)
+#define CSI2_COMPLEXIO_IRQ_ERRSOTHS5 (1 << 4)
+#define CSI2_COMPLEXIO_IRQ_ERRSOTHS4 (1 << 3)
+#define CSI2_COMPLEXIO_IRQ_ERRSOTHS3 (1 << 2)
+#define CSI2_COMPLEXIO_IRQ_ERRSOTHS2 (1 << 1)
+#define CSI2_COMPLEXIO_IRQ_ERRSOTHS1 (1 << 0)
+
+#define CSI2_DBG_P 0x68
+
+#define CSI2_TIMING 0x6c
+#define CSI2_TIMING_FORCE_RX_MODE_IO1 (1 << 15)
+#define CSI2_TIMING_STOP_STATE_X16_IO1 (1 << 14)
+#define CSI2_TIMING_STOP_STATE_X4_IO1 (1 << 13)
+#define CSI2_TIMING_STOP_STATE_COUNTER_IO1_MASK (0x1fff << 0)
+#define CSI2_TIMING_STOP_STATE_COUNTER_IO1_SHIFT 0
+
+#define CSI2_CTX_CTRL1(i) (0x70 + (0x20 * i))
+#define CSI2_CTX_CTRL1_GENERIC (1 << 30)
+#define CSI2_CTX_CTRL1_TRANSCODE (0xf << 24)
+#define CSI2_CTX_CTRL1_FEC_NUMBER_MASK (0xff << 16)
+#define CSI2_CTX_CTRL1_COUNT_MASK (0xff << 8)
+#define CSI2_CTX_CTRL1_COUNT_SHIFT 8
+#define CSI2_CTX_CTRL1_EOF_EN (1 << 7)
+#define CSI2_CTX_CTRL1_EOL_EN (1 << 6)
+#define CSI2_CTX_CTRL1_CS_EN (1 << 5)
+#define CSI2_CTX_CTRL1_COUNT_UNLOCK (1 << 4)
+#define CSI2_CTX_CTRL1_PING_PONG (1 << 3)
+#define CSI2_CTX_CTRL1_CTX_EN (1 << 0)
+
+#define CSI2_CTX_CTRL2(i) (0x74 + (0x20 * i))
+#define CSI2_CTX_CTRL2_FRAME_MASK (0xffff << 16)
+#define CSI2_CTX_CTRL2_FRAME_SHIFT 16
+#define CSI2_CTX_CTRL2_USER_DEF_MAP_SHIFT 13
+#define CSI2_CTX_CTRL2_USER_DEF_MAP_MASK \
+ (0x3 << CSI2_CTX_CTRL2_USER_DEF_MAP_SHIFT)
+#define CSI2_CTX_CTRL2_VIRTUAL_ID_MASK (3 << 11)
+#define CSI2_CTX_CTRL2_VIRTUAL_ID_SHIFT 11
+#define CSI2_CTX_CTRL2_DPCM_PRED (1 << 10)
+#define CSI2_CTX_CTRL2_FORMAT_MASK (0x3ff << 0)
+#define CSI2_CTX_CTRL2_FORMAT_SHIFT 0
+
+#define CSI2_CTX_DAT_OFST(i) (0x78 + (0x20 * i))
+#define CSI2_CTX_DAT_OFST_MASK (0xfff << 5)
+
+#define CSI2_CTX_PING_ADDR(i) (0x7c + (0x20 * i))
+#define CSI2_CTX_PING_ADDR_MASK 0xffffffe0
+
+#define CSI2_CTX_PONG_ADDR(i) (0x80 + (0x20 * i))
+#define CSI2_CTX_PONG_ADDR_MASK CSI2_CTX_PING_ADDR_MASK
+
+#define CSI2_CTX_IRQENABLE(i) (0x84 + (0x20 * i))
+#define CSI2_CTX_IRQSTATUS(i) (0x88 + (0x20 * i))
+
+#define CSI2_CTX_CTRL3(i) (0x8c + (0x20 * i))
+#define CSI2_CTX_CTRL3_ALPHA_SHIFT 5
+#define CSI2_CTX_CTRL3_ALPHA_MASK \
+ (0x3fff << CSI2_CTX_CTRL3_ALPHA_SHIFT)
+
+/* Shared bits across CSI2_CTX_IRQENABLE and IRQSTATUS */
+#define CSI2_CTX_IRQ_ECC_CORRECTION (1 << 8)
+#define CSI2_CTX_IRQ_LINE_NUMBER (1 << 7)
+#define CSI2_CTX_IRQ_FRAME_NUMBER (1 << 6)
+#define CSI2_CTX_IRQ_CS (1 << 5)
+#define CSI2_CTX_IRQ_LE (1 << 3)
+#define CSI2_CTX_IRQ_LS (1 << 2)
+#define CSI2_CTX_IRQ_FE (1 << 1)
+#define CSI2_CTX_IRQ_FS (1 << 0)
+
+/* ISS BTE */
+#define BTE_CTRL (0x0030)
+#define BTE_CTRL_BW_LIMITER_MASK (0x3ff << 22)
+#define BTE_CTRL_BW_LIMITER_SHIFT 22
+
+/* ISS ISP_SYS1 */
+#define ISP5_REVISION (0x0000)
+#define ISP5_SYSCONFIG (0x0010)
+#define ISP5_SYSCONFIG_STANDBYMODE_MASK (3 << 4)
+#define ISP5_SYSCONFIG_STANDBYMODE_FORCE (0 << 4)
+#define ISP5_SYSCONFIG_STANDBYMODE_NO (1 << 4)
+#define ISP5_SYSCONFIG_STANDBYMODE_SMART (2 << 4)
+#define ISP5_SYSCONFIG_SOFTRESET (1 << 1)
+
+#define ISP5_IRQSTATUS(i) (0x0028 + (0x10 * (i)))
+#define ISP5_IRQENABLE_SET(i) (0x002c + (0x10 * (i)))
+#define ISP5_IRQENABLE_CLR(i) (0x0030 + (0x10 * (i)))
+
+/* Bits shared for ISP5_IRQ* registers */
+#define ISP5_IRQ_OCP_ERR (1 << 31)
+#define ISP5_IRQ_IPIPE_INT_DPC_RNEW1 (1 << 29)
+#define ISP5_IRQ_IPIPE_INT_DPC_RNEW0 (1 << 28)
+#define ISP5_IRQ_IPIPE_INT_DPC_INIT (1 << 27)
+#define ISP5_IRQ_IPIPE_INT_EOF (1 << 25)
+#define ISP5_IRQ_H3A_INT_EOF (1 << 24)
+#define ISP5_IRQ_RSZ_INT_EOF1 (1 << 23)
+#define ISP5_IRQ_RSZ_INT_EOF0 (1 << 22)
+#define ISP5_IRQ_RSZ_FIFO_IN_BLK_ERR (1 << 19)
+#define ISP5_IRQ_RSZ_FIFO_OVF (1 << 18)
+#define ISP5_IRQ_RSZ_INT_CYC_RSZB (1 << 17)
+#define ISP5_IRQ_RSZ_INT_CYC_RSZA (1 << 16)
+#define ISP5_IRQ_RSZ_INT_DMA (1 << 15)
+#define ISP5_IRQ_RSZ_INT_LAST_PIX (1 << 14)
+#define ISP5_IRQ_RSZ_INT_REG (1 << 13)
+#define ISP5_IRQ_H3A_INT (1 << 12)
+#define ISP5_IRQ_AF_INT (1 << 11)
+#define ISP5_IRQ_AEW_INT (1 << 10)
+#define ISP5_IRQ_IPIPEIF_IRQ (1 << 9)
+#define ISP5_IRQ_IPIPE_INT_HST (1 << 8)
+#define ISP5_IRQ_IPIPE_INT_BSC (1 << 7)
+#define ISP5_IRQ_IPIPE_INT_DMA (1 << 6)
+#define ISP5_IRQ_IPIPE_INT_LAST_PIX (1 << 5)
+#define ISP5_IRQ_IPIPE_INT_REG (1 << 4)
+#define ISP5_IRQ_ISIF_INT(i) (1 << (i))
+
+#define ISP5_CTRL (0x006c)
+#define ISP5_CTRL_MSTANDBY (1 << 24)
+#define ISP5_CTRL_VD_PULSE_EXT (1 << 23)
+#define ISP5_CTRL_MSTANDBY_WAIT (1 << 20)
+#define ISP5_CTRL_BL_CLK_ENABLE (1 << 15)
+#define ISP5_CTRL_ISIF_CLK_ENABLE (1 << 14)
+#define ISP5_CTRL_H3A_CLK_ENABLE (1 << 13)
+#define ISP5_CTRL_RSZ_CLK_ENABLE (1 << 12)
+#define ISP5_CTRL_IPIPE_CLK_ENABLE (1 << 11)
+#define ISP5_CTRL_IPIPEIF_CLK_ENABLE (1 << 10)
+#define ISP5_CTRL_SYNC_ENABLE (1 << 9)
+#define ISP5_CTRL_PSYNC_CLK_SEL (1 << 8)
+
+/* ISS ISP ISIF register offsets */
+#define ISIF_SYNCEN (0x0000)
+#define ISIF_SYNCEN_DWEN (1 << 1)
+#define ISIF_SYNCEN_SYEN (1 << 0)
+
+#define ISIF_MODESET (0x0004)
+#define ISIF_MODESET_INPMOD_MASK (3 << 12)
+#define ISIF_MODESET_INPMOD_RAW (0 << 12)
+#define ISIF_MODESET_INPMOD_YCBCR16 (1 << 12)
+#define ISIF_MODESET_INPMOD_YCBCR8 (2 << 12)
+#define ISIF_MODESET_CCDW_MASK (7 << 8)
+#define ISIF_MODESET_CCDW_2BIT (2 << 8)
+#define ISIF_MODESET_CCDMD (1 << 7)
+#define ISIF_MODESET_SWEN (1 << 5)
+#define ISIF_MODESET_HDPOL (1 << 3)
+#define ISIF_MODESET_VDPOL (1 << 2)
+
+#define ISIF_SPH (0x0018)
+#define ISIF_SPH_MASK (0x7fff)
+
+#define ISIF_LNH (0x001c)
+#define ISIF_LNH_MASK (0x7fff)
+
+#define ISIF_LNV (0x0028)
+#define ISIF_LNV_MASK (0x7fff)
+
+#define ISIF_HSIZE (0x0034)
+#define ISIF_HSIZE_ADCR (1 << 12)
+#define ISIF_HSIZE_HSIZE_MASK (0xfff)
+
+#define ISIF_CADU (0x003c)
+#define ISIF_CADU_MASK (0x7ff)
+
+#define ISIF_CADL (0x0040)
+#define ISIF_CADL_MASK (0xffff)
+
+#define ISIF_CCOLP (0x004c)
+#define ISIF_CCOLP_CP0_F0_R (0 << 6)
+#define ISIF_CCOLP_CP0_F0_GR (1 << 6)
+#define ISIF_CCOLP_CP0_F0_B (3 << 6)
+#define ISIF_CCOLP_CP0_F0_GB (2 << 6)
+#define ISIF_CCOLP_CP1_F0_R (0 << 4)
+#define ISIF_CCOLP_CP1_F0_GR (1 << 4)
+#define ISIF_CCOLP_CP1_F0_B (3 << 4)
+#define ISIF_CCOLP_CP1_F0_GB (2 << 4)
+#define ISIF_CCOLP_CP2_F0_R (0 << 2)
+#define ISIF_CCOLP_CP2_F0_GR (1 << 2)
+#define ISIF_CCOLP_CP2_F0_B (3 << 2)
+#define ISIF_CCOLP_CP2_F0_GB (2 << 2)
+#define ISIF_CCOLP_CP3_F0_R (0 << 0)
+#define ISIF_CCOLP_CP3_F0_GR (1 << 0)
+#define ISIF_CCOLP_CP3_F0_B (3 << 0)
+#define ISIF_CCOLP_CP3_F0_GB (2 << 0)
+
+#define ISIF_VDINT(i) (0x0070 + (i) * 4)
+#define ISIF_VDINT_MASK (0x7fff)
+
+#define ISIF_CGAMMAWD (0x0080)
+#define ISIF_CGAMMAWD_GWDI_MASK (0xf << 1)
+#define ISIF_CGAMMAWD_GWDI(bpp) ((16 - (bpp)) << 1)
+
+#define ISIF_CCDCFG (0x0088)
+#define ISIF_CCDCFG_Y8POS (1 << 11)
+
+/* ISS ISP IPIPEIF register offsets */
+#define IPIPEIF_ENABLE (0x0000)
+
+#define IPIPEIF_CFG1 (0x0004)
+#define IPIPEIF_CFG1_INPSRC1_MASK (3 << 14)
+#define IPIPEIF_CFG1_INPSRC1_VPORT_RAW (0 << 14)
+#define IPIPEIF_CFG1_INPSRC1_SDRAM_RAW (1 << 14)
+#define IPIPEIF_CFG1_INPSRC1_ISIF_DARKFM (2 << 14)
+#define IPIPEIF_CFG1_INPSRC1_SDRAM_YUV (3 << 14)
+#define IPIPEIF_CFG1_INPSRC2_MASK (3 << 2)
+#define IPIPEIF_CFG1_INPSRC2_ISIF (0 << 2)
+#define IPIPEIF_CFG1_INPSRC2_SDRAM_RAW (1 << 2)
+#define IPIPEIF_CFG1_INPSRC2_ISIF_DARKFM (2 << 2)
+#define IPIPEIF_CFG1_INPSRC2_SDRAM_YUV (3 << 2)
+
+#define IPIPEIF_CFG2 (0x0030)
+#define IPIPEIF_CFG2_YUV8P (1 << 7)
+#define IPIPEIF_CFG2_YUV8 (1 << 6)
+#define IPIPEIF_CFG2_YUV16 (1 << 3)
+#define IPIPEIF_CFG2_VDPOL (1 << 2)
+#define IPIPEIF_CFG2_HDPOL (1 << 1)
+#define IPIPEIF_CFG2_INTSW (1 << 0)
+
+#define IPIPEIF_CLKDIV (0x0040)
+
+/* ISS ISP IPIPE register offsets */
+#define IPIPE_SRC_EN (0x0000)
+#define IPIPE_SRC_EN_EN (1 << 0)
+
+#define IPIPE_SRC_MODE (0x0004)
+#define IPIPE_SRC_MODE_WRT (1 << 1)
+#define IPIPE_SRC_MODE_OST (1 << 0)
+
+#define IPIPE_SRC_FMT (0x0008)
+#define IPIPE_SRC_FMT_RAW2YUV (0 << 0)
+#define IPIPE_SRC_FMT_RAW2RAW (1 << 0)
+#define IPIPE_SRC_FMT_RAW2STATS (2 << 0)
+#define IPIPE_SRC_FMT_YUV2YUV (3 << 0)
+
+#define IPIPE_SRC_COL (0x000c)
+#define IPIPE_SRC_COL_OO_R (0 << 6)
+#define IPIPE_SRC_COL_OO_GR (1 << 6)
+#define IPIPE_SRC_COL_OO_B (3 << 6)
+#define IPIPE_SRC_COL_OO_GB (2 << 6)
+#define IPIPE_SRC_COL_OE_R (0 << 4)
+#define IPIPE_SRC_COL_OE_GR (1 << 4)
+#define IPIPE_SRC_COL_OE_B (3 << 4)
+#define IPIPE_SRC_COL_OE_GB (2 << 4)
+#define IPIPE_SRC_COL_EO_R (0 << 2)
+#define IPIPE_SRC_COL_EO_GR (1 << 2)
+#define IPIPE_SRC_COL_EO_B (3 << 2)
+#define IPIPE_SRC_COL_EO_GB (2 << 2)
+#define IPIPE_SRC_COL_EE_R (0 << 0)
+#define IPIPE_SRC_COL_EE_GR (1 << 0)
+#define IPIPE_SRC_COL_EE_B (3 << 0)
+#define IPIPE_SRC_COL_EE_GB (2 << 0)
+
+#define IPIPE_SRC_VPS (0x0010)
+#define IPIPE_SRC_VPS_MASK (0xffff)
+
+#define IPIPE_SRC_VSZ (0x0014)
+#define IPIPE_SRC_VSZ_MASK (0x1fff)
+
+#define IPIPE_SRC_HPS (0x0018)
+#define IPIPE_SRC_HPS_MASK (0xffff)
+
+#define IPIPE_SRC_HSZ (0x001c)
+#define IPIPE_SRC_HSZ_MASK (0x1ffe)
+
+#define IPIPE_SEL_SBU (0x0020)
+
+#define IPIPE_SRC_STA (0x0024)
+
+#define IPIPE_GCK_MMR (0x0028)
+#define IPIPE_GCK_MMR_REG (1 << 0)
+
+#define IPIPE_GCK_PIX (0x002c)
+#define IPIPE_GCK_PIX_G3 (1 << 3)
+#define IPIPE_GCK_PIX_G2 (1 << 2)
+#define IPIPE_GCK_PIX_G1 (1 << 1)
+#define IPIPE_GCK_PIX_G0 (1 << 0)
+
+#define IPIPE_DPC_LUT_EN (0x0034)
+#define IPIPE_DPC_LUT_SEL (0x0038)
+#define IPIPE_DPC_LUT_ADR (0x003c)
+#define IPIPE_DPC_LUT_SIZ (0x0040)
+
+#define IPIPE_DPC_OTF_EN (0x0044)
+#define IPIPE_DPC_OTF_TYP (0x0048)
+#define IPIPE_DPC_OTF_2_D_THR_R (0x004c)
+#define IPIPE_DPC_OTF_2_D_THR_GR (0x0050)
+#define IPIPE_DPC_OTF_2_D_THR_GB (0x0054)
+#define IPIPE_DPC_OTF_2_D_THR_B (0x0058)
+#define IPIPE_DPC_OTF_2_C_THR_R (0x005c)
+#define IPIPE_DPC_OTF_2_C_THR_GR (0x0060)
+#define IPIPE_DPC_OTF_2_C_THR_GB (0x0064)
+#define IPIPE_DPC_OTF_2_C_THR_B (0x0068)
+#define IPIPE_DPC_OTF_3_SHF (0x006c)
+#define IPIPE_DPC_OTF_3_D_THR (0x0070)
+#define IPIPE_DPC_OTF_3_D_SPL (0x0074)
+#define IPIPE_DPC_OTF_3_D_MIN (0x0078)
+#define IPIPE_DPC_OTF_3_D_MAX (0x007c)
+#define IPIPE_DPC_OTF_3_C_THR (0x0080)
+#define IPIPE_DPC_OTF_3_C_SLP (0x0084)
+#define IPIPE_DPC_OTF_3_C_MIN (0x0088)
+#define IPIPE_DPC_OTF_3_C_MAX (0x008c)
+
+#define IPIPE_LSC_VOFT (0x0090)
+#define IPIPE_LSC_VA2 (0x0094)
+#define IPIPE_LSC_VA1 (0x0098)
+#define IPIPE_LSC_VS (0x009c)
+#define IPIPE_LSC_HOFT (0x00a0)
+#define IPIPE_LSC_HA2 (0x00a4)
+#define IPIPE_LSC_HA1 (0x00a8)
+#define IPIPE_LSC_HS (0x00ac)
+#define IPIPE_LSC_GAN_R (0x00b0)
+#define IPIPE_LSC_GAN_GR (0x00b4)
+#define IPIPE_LSC_GAN_GB (0x00b8)
+#define IPIPE_LSC_GAN_B (0x00bc)
+#define IPIPE_LSC_OFT_R (0x00c0)
+#define IPIPE_LSC_OFT_GR (0x00c4)
+#define IPIPE_LSC_OFT_GB (0x00c8)
+#define IPIPE_LSC_OFT_B (0x00cc)
+#define IPIPE_LSC_SHF (0x00d0)
+#define IPIPE_LSC_MAX (0x00d4)
+
+#define IPIPE_D2F_1ST_EN (0x00d8)
+#define IPIPE_D2F_1ST_TYP (0x00dc)
+#define IPIPE_D2F_1ST_THR_00 (0x00e0)
+#define IPIPE_D2F_1ST_THR_01 (0x00e4)
+#define IPIPE_D2F_1ST_THR_02 (0x00e8)
+#define IPIPE_D2F_1ST_THR_03 (0x00ec)
+#define IPIPE_D2F_1ST_THR_04 (0x00f0)
+#define IPIPE_D2F_1ST_THR_05 (0x00f4)
+#define IPIPE_D2F_1ST_THR_06 (0x00f8)
+#define IPIPE_D2F_1ST_THR_07 (0x00fc)
+#define IPIPE_D2F_1ST_STR_00 (0x0100)
+#define IPIPE_D2F_1ST_STR_01 (0x0104)
+#define IPIPE_D2F_1ST_STR_02 (0x0108)
+#define IPIPE_D2F_1ST_STR_03 (0x010c)
+#define IPIPE_D2F_1ST_STR_04 (0x0110)
+#define IPIPE_D2F_1ST_STR_05 (0x0114)
+#define IPIPE_D2F_1ST_STR_06 (0x0118)
+#define IPIPE_D2F_1ST_STR_07 (0x011c)
+#define IPIPE_D2F_1ST_SPR_00 (0x0120)
+#define IPIPE_D2F_1ST_SPR_01 (0x0124)
+#define IPIPE_D2F_1ST_SPR_02 (0x0128)
+#define IPIPE_D2F_1ST_SPR_03 (0x012c)
+#define IPIPE_D2F_1ST_SPR_04 (0x0130)
+#define IPIPE_D2F_1ST_SPR_05 (0x0134)
+#define IPIPE_D2F_1ST_SPR_06 (0x0138)
+#define IPIPE_D2F_1ST_SPR_07 (0x013c)
+#define IPIPE_D2F_1ST_EDG_MIN (0x0140)
+#define IPIPE_D2F_1ST_EDG_MAX (0x0144)
+#define IPIPE_D2F_2ND_EN (0x0148)
+#define IPIPE_D2F_2ND_TYP (0x014c)
+#define IPIPE_D2F_2ND_THR00 (0x0150)
+#define IPIPE_D2F_2ND_THR01 (0x0154)
+#define IPIPE_D2F_2ND_THR02 (0x0158)
+#define IPIPE_D2F_2ND_THR03 (0x015c)
+#define IPIPE_D2F_2ND_THR04 (0x0160)
+#define IPIPE_D2F_2ND_THR05 (0x0164)
+#define IPIPE_D2F_2ND_THR06 (0x0168)
+#define IPIPE_D2F_2ND_THR07 (0x016c)
+#define IPIPE_D2F_2ND_STR_00 (0x0170)
+#define IPIPE_D2F_2ND_STR_01 (0x0174)
+#define IPIPE_D2F_2ND_STR_02 (0x0178)
+#define IPIPE_D2F_2ND_STR_03 (0x017c)
+#define IPIPE_D2F_2ND_STR_04 (0x0180)
+#define IPIPE_D2F_2ND_STR_05 (0x0184)
+#define IPIPE_D2F_2ND_STR_06 (0x0188)
+#define IPIPE_D2F_2ND_STR_07 (0x018c)
+#define IPIPE_D2F_2ND_SPR_00 (0x0190)
+#define IPIPE_D2F_2ND_SPR_01 (0x0194)
+#define IPIPE_D2F_2ND_SPR_02 (0x0198)
+#define IPIPE_D2F_2ND_SPR_03 (0x019c)
+#define IPIPE_D2F_2ND_SPR_04 (0x01a0)
+#define IPIPE_D2F_2ND_SPR_05 (0x01a4)
+#define IPIPE_D2F_2ND_SPR_06 (0x01a8)
+#define IPIPE_D2F_2ND_SPR_07 (0x01ac)
+#define IPIPE_D2F_2ND_EDG_MIN (0x01b0)
+#define IPIPE_D2F_2ND_EDG_MAX (0x01b4)
+
+#define IPIPE_GIC_EN (0x01b8)
+#define IPIPE_GIC_TYP (0x01bc)
+#define IPIPE_GIC_GAN (0x01c0)
+#define IPIPE_GIC_NFGAIN (0x01c4)
+#define IPIPE_GIC_THR (0x01c8)
+#define IPIPE_GIC_SLP (0x01cc)
+
+#define IPIPE_WB2_OFT_R (0x01d0)
+#define IPIPE_WB2_OFT_GR (0x01d4)
+#define IPIPE_WB2_OFT_GB (0x01d8)
+#define IPIPE_WB2_OFT_B (0x01dc)
+
+#define IPIPE_WB2_WGN_R (0x01e0)
+#define IPIPE_WB2_WGN_GR (0x01e4)
+#define IPIPE_WB2_WGN_GB (0x01e8)
+#define IPIPE_WB2_WGN_B (0x01ec)
+
+#define IPIPE_CFA_MODE (0x01f0)
+#define IPIPE_CFA_2DIR_HPF_THR (0x01f4)
+#define IPIPE_CFA_2DIR_HPF_SLP (0x01f8)
+#define IPIPE_CFA_2DIR_MIX_THR (0x01fc)
+#define IPIPE_CFA_2DIR_MIX_SLP (0x0200)
+#define IPIPE_CFA_2DIR_DIR_TRH (0x0204)
+#define IPIPE_CFA_2DIR_DIR_SLP (0x0208)
+#define IPIPE_CFA_2DIR_NDWT (0x020c)
+#define IPIPE_CFA_MONO_HUE_FRA (0x0210)
+#define IPIPE_CFA_MONO_EDG_THR (0x0214)
+#define IPIPE_CFA_MONO_THR_MIN (0x0218)
+
+#define IPIPE_CFA_MONO_THR_SLP (0x021c)
+#define IPIPE_CFA_MONO_SLP_MIN (0x0220)
+#define IPIPE_CFA_MONO_SLP_SLP (0x0224)
+#define IPIPE_CFA_MONO_LPWT (0x0228)
+
+#define IPIPE_RGB1_MUL_RR (0x022c)
+#define IPIPE_RGB1_MUL_GR (0x0230)
+#define IPIPE_RGB1_MUL_BR (0x0234)
+#define IPIPE_RGB1_MUL_RG (0x0238)
+#define IPIPE_RGB1_MUL_GG (0x023c)
+#define IPIPE_RGB1_MUL_BG (0x0240)
+#define IPIPE_RGB1_MUL_RB (0x0244)
+#define IPIPE_RGB1_MUL_GB (0x0248)
+#define IPIPE_RGB1_MUL_BB (0x024c)
+#define IPIPE_RGB1_OFT_OR (0x0250)
+#define IPIPE_RGB1_OFT_OG (0x0254)
+#define IPIPE_RGB1_OFT_OB (0x0258)
+#define IPIPE_GMM_CFG (0x025c)
+#define IPIPE_RGB2_MUL_RR (0x0260)
+#define IPIPE_RGB2_MUL_GR (0x0264)
+#define IPIPE_RGB2_MUL_BR (0x0268)
+#define IPIPE_RGB2_MUL_RG (0x026c)
+#define IPIPE_RGB2_MUL_GG (0x0270)
+#define IPIPE_RGB2_MUL_BG (0x0274)
+#define IPIPE_RGB2_MUL_RB (0x0278)
+#define IPIPE_RGB2_MUL_GB (0x027c)
+#define IPIPE_RGB2_MUL_BB (0x0280)
+#define IPIPE_RGB2_OFT_OR (0x0284)
+#define IPIPE_RGB2_OFT_OG (0x0288)
+#define IPIPE_RGB2_OFT_OB (0x028c)
+
+#define IPIPE_YUV_ADJ (0x0294)
+#define IPIPE_YUV_MUL_RY (0x0298)
+#define IPIPE_YUV_MUL_GY (0x029c)
+#define IPIPE_YUV_MUL_BY (0x02a0)
+#define IPIPE_YUV_MUL_RCB (0x02a4)
+#define IPIPE_YUV_MUL_GCB (0x02a8)
+#define IPIPE_YUV_MUL_BCB (0x02ac)
+#define IPIPE_YUV_MUL_RCR (0x02b0)
+#define IPIPE_YUV_MUL_GCR (0x02b4)
+#define IPIPE_YUV_MUL_BCR (0x02b8)
+#define IPIPE_YUV_OFT_Y (0x02bc)
+#define IPIPE_YUV_OFT_CB (0x02c0)
+#define IPIPE_YUV_OFT_CR (0x02c4)
+
+#define IPIPE_YUV_PHS (0x02c8)
+#define IPIPE_YUV_PHS_LPF (1 << 1)
+#define IPIPE_YUV_PHS_POS (1 << 0)
+
+#define IPIPE_YEE_EN (0x02d4)
+#define IPIPE_YEE_TYP (0x02d8)
+#define IPIPE_YEE_SHF (0x02dc)
+#define IPIPE_YEE_MUL_00 (0x02e0)
+#define IPIPE_YEE_MUL_01 (0x02e4)
+#define IPIPE_YEE_MUL_02 (0x02e8)
+#define IPIPE_YEE_MUL_10 (0x02ec)
+#define IPIPE_YEE_MUL_11 (0x02f0)
+#define IPIPE_YEE_MUL_12 (0x02f4)
+#define IPIPE_YEE_MUL_20 (0x02f8)
+#define IPIPE_YEE_MUL_21 (0x02fc)
+#define IPIPE_YEE_MUL_22 (0x0300)
+#define IPIPE_YEE_THR (0x0304)
+#define IPIPE_YEE_E_GAN (0x0308)
+#define IPIPE_YEE_E_THR_1 (0x030c)
+#define IPIPE_YEE_E_THR_2 (0x0310)
+#define IPIPE_YEE_G_GAN (0x0314)
+#define IPIPE_YEE_G_OFT (0x0318)
+
+#define IPIPE_CAR_EN (0x031c)
+#define IPIPE_CAR_TYP (0x0320)
+#define IPIPE_CAR_SW (0x0324)
+#define IPIPE_CAR_HPF_TYP (0x0328)
+#define IPIPE_CAR_HPF_SHF (0x032c)
+#define IPIPE_CAR_HPF_THR (0x0330)
+#define IPIPE_CAR_GN1_GAN (0x0334)
+#define IPIPE_CAR_GN1_SHF (0x0338)
+#define IPIPE_CAR_GN1_MIN (0x033c)
+#define IPIPE_CAR_GN2_GAN (0x0340)
+#define IPIPE_CAR_GN2_SHF (0x0344)
+#define IPIPE_CAR_GN2_MIN (0x0348)
+#define IPIPE_CGS_EN (0x034c)
+#define IPIPE_CGS_GN1_L_THR (0x0350)
+#define IPIPE_CGS_GN1_L_GAIN (0x0354)
+#define IPIPE_CGS_GN1_L_SHF (0x0358)
+#define IPIPE_CGS_GN1_L_MIN (0x035c)
+#define IPIPE_CGS_GN1_H_THR (0x0360)
+#define IPIPE_CGS_GN1_H_GAIN (0x0364)
+#define IPIPE_CGS_GN1_H_SHF (0x0368)
+#define IPIPE_CGS_GN1_H_MIN (0x036c)
+#define IPIPE_CGS_GN2_L_THR (0x0370)
+#define IPIPE_CGS_GN2_L_GAIN (0x0374)
+#define IPIPE_CGS_GN2_L_SHF (0x0378)
+#define IPIPE_CGS_GN2_L_MIN (0x037c)
+
+#define IPIPE_BOX_EN (0x0380)
+#define IPIPE_BOX_MODE (0x0384)
+#define IPIPE_BOX_TYP (0x0388)
+#define IPIPE_BOX_SHF (0x038c)
+#define IPIPE_BOX_SDR_SAD_H (0x0390)
+#define IPIPE_BOX_SDR_SAD_L (0x0394)
+
+#define IPIPE_HST_EN (0x039c)
+#define IPIPE_HST_MODE (0x03a0)
+#define IPIPE_HST_SEL (0x03a4)
+#define IPIPE_HST_PARA (0x03a8)
+#define IPIPE_HST_0_VPS (0x03ac)
+#define IPIPE_HST_0_VSZ (0x03b0)
+#define IPIPE_HST_0_HPS (0x03b4)
+#define IPIPE_HST_0_HSZ (0x03b8)
+#define IPIPE_HST_1_VPS (0x03bc)
+#define IPIPE_HST_1_VSZ (0x03c0)
+#define IPIPE_HST_1_HPS (0x03c4)
+#define IPIPE_HST_1_HSZ (0x03c8)
+#define IPIPE_HST_2_VPS (0x03cc)
+#define IPIPE_HST_2_VSZ (0x03d0)
+#define IPIPE_HST_2_HPS (0x03d4)
+#define IPIPE_HST_2_HSZ (0x03d8)
+#define IPIPE_HST_3_VPS (0x03dc)
+#define IPIPE_HST_3_VSZ (0x03e0)
+#define IPIPE_HST_3_HPS (0x03e4)
+#define IPIPE_HST_3_HSZ (0x03e8)
+#define IPIPE_HST_TBL (0x03ec)
+#define IPIPE_HST_MUL_R (0x03f0)
+#define IPIPE_HST_MUL_GR (0x03f4)
+#define IPIPE_HST_MUL_GB (0x03f8)
+#define IPIPE_HST_MUL_B (0x03fc)
+
+#define IPIPE_BSC_EN (0x0400)
+#define IPIPE_BSC_MODE (0x0404)
+#define IPIPE_BSC_TYP (0x0408)
+#define IPIPE_BSC_ROW_VCT (0x040c)
+#define IPIPE_BSC_ROW_SHF (0x0410)
+#define IPIPE_BSC_ROW_VPO (0x0414)
+#define IPIPE_BSC_ROW_VNU (0x0418)
+#define IPIPE_BSC_ROW_VSKIP (0x041c)
+#define IPIPE_BSC_ROW_HPO (0x0420)
+#define IPIPE_BSC_ROW_HNU (0x0424)
+#define IPIPE_BSC_ROW_HSKIP (0x0428)
+#define IPIPE_BSC_COL_VCT (0x042c)
+#define IPIPE_BSC_COL_SHF (0x0430)
+#define IPIPE_BSC_COL_VPO (0x0434)
+#define IPIPE_BSC_COL_VNU (0x0438)
+#define IPIPE_BSC_COL_VSKIP (0x043c)
+#define IPIPE_BSC_COL_HPO (0x0440)
+#define IPIPE_BSC_COL_HNU (0x0444)
+#define IPIPE_BSC_COL_HSKIP (0x0448)
+
+#define IPIPE_BSC_EN (0x0400)
+
+/* ISS ISP Resizer register offsets */
+#define RSZ_REVISION (0x0000)
+#define RSZ_SYSCONFIG (0x0004)
+#define RSZ_SYSCONFIG_RSZB_CLK_EN (1 << 9)
+#define RSZ_SYSCONFIG_RSZA_CLK_EN (1 << 8)
+
+#define RSZ_IN_FIFO_CTRL (0x000c)
+#define RSZ_IN_FIFO_CTRL_THRLD_LOW_MASK (0x1ff << 16)
+#define RSZ_IN_FIFO_CTRL_THRLD_LOW_SHIFT 16
+#define RSZ_IN_FIFO_CTRL_THRLD_HIGH_MASK (0x1ff << 0)
+#define RSZ_IN_FIFO_CTRL_THRLD_HIGH_SHIFT 0
+
+#define RSZ_FRACDIV (0x0008)
+#define RSZ_FRACDIV_MASK (0xffff)
+
+#define RSZ_SRC_EN (0x0020)
+#define RSZ_SRC_EN_SRC_EN (1 << 0)
+
+#define RSZ_SRC_MODE (0x0024)
+#define RSZ_SRC_MODE_OST (1 << 0)
+#define RSZ_SRC_MODE_WRT (1 << 1)
+
+#define RSZ_SRC_FMT0 (0x0028)
+#define RSZ_SRC_FMT0_BYPASS (1 << 1)
+#define RSZ_SRC_FMT0_SEL (1 << 0)
+
+#define RSZ_SRC_FMT1 (0x002c)
+#define RSZ_SRC_FMT1_IN420 (1 << 1)
+
+#define RSZ_SRC_VPS (0x0030)
+#define RSZ_SRC_VSZ (0x0034)
+#define RSZ_SRC_HPS (0x0038)
+#define RSZ_SRC_HSZ (0x003c)
+#define RSZ_DMA_RZA (0x0040)
+#define RSZ_DMA_RZB (0x0044)
+#define RSZ_DMA_STA (0x0048)
+#define RSZ_GCK_MMR (0x004c)
+#define RSZ_GCK_MMR_MMR (1 << 0)
+
+#define RSZ_GCK_SDR (0x0054)
+#define RSZ_GCK_SDR_CORE (1 << 0)
+
+#define RSZ_IRQ_RZA (0x0058)
+#define RSZ_IRQ_RZA_MASK (0x1fff)
+
+#define RSZ_IRQ_RZB (0x005c)
+#define RSZ_IRQ_RZB_MASK (0x1fff)
+
+#define RSZ_YUV_Y_MIN (0x0060)
+#define RSZ_YUV_Y_MAX (0x0064)
+#define RSZ_YUV_C_MIN (0x0068)
+#define RSZ_YUV_C_MAX (0x006c)
+
+#define RSZ_SEQ (0x0074)
+#define RSZ_SEQ_HRVB (1 << 2)
+#define RSZ_SEQ_HRVA (1 << 0)
+
+#define RZA_EN (0x0078)
+#define RZA_MODE (0x007c)
+#define RZA_MODE_ONE_SHOT (1 << 0)
+
+#define RZA_420 (0x0080)
+#define RZA_I_VPS (0x0084)
+#define RZA_I_HPS (0x0088)
+#define RZA_O_VSZ (0x008c)
+#define RZA_O_HSZ (0x0090)
+#define RZA_V_PHS_Y (0x0094)
+#define RZA_V_PHS_C (0x0098)
+#define RZA_V_DIF (0x009c)
+#define RZA_V_TYP (0x00a0)
+#define RZA_V_LPF (0x00a4)
+#define RZA_H_PHS (0x00a8)
+#define RZA_H_DIF (0x00b0)
+#define RZA_H_TYP (0x00b4)
+#define RZA_H_LPF (0x00b8)
+#define RZA_DWN_EN (0x00bc)
+#define RZA_SDR_Y_BAD_H (0x00d0)
+#define RZA_SDR_Y_BAD_L (0x00d4)
+#define RZA_SDR_Y_SAD_H (0x00d8)
+#define RZA_SDR_Y_SAD_L (0x00dc)
+#define RZA_SDR_Y_OFT (0x00e0)
+#define RZA_SDR_Y_PTR_S (0x00e4)
+#define RZA_SDR_Y_PTR_E (0x00e8)
+#define RZA_SDR_C_BAD_H (0x00ec)
+#define RZA_SDR_C_BAD_L (0x00f0)
+#define RZA_SDR_C_SAD_H (0x00f4)
+#define RZA_SDR_C_SAD_L (0x00f8)
+#define RZA_SDR_C_OFT (0x00fc)
+#define RZA_SDR_C_PTR_S (0x0100)
+#define RZA_SDR_C_PTR_E (0x0104)
+
+#define RZB_EN (0x0108)
+#define RZB_MODE (0x010c)
+#define RZB_420 (0x0110)
+#define RZB_I_VPS (0x0114)
+#define RZB_I_HPS (0x0118)
+#define RZB_O_VSZ (0x011c)
+#define RZB_O_HSZ (0x0120)
+
+#define RZB_V_DIF (0x012c)
+#define RZB_V_TYP (0x0130)
+#define RZB_V_LPF (0x0134)
+
+#define RZB_H_DIF (0x0140)
+#define RZB_H_TYP (0x0144)
+#define RZB_H_LPF (0x0148)
+
+#define RZB_SDR_Y_BAD_H (0x0160)
+#define RZB_SDR_Y_BAD_L (0x0164)
+#define RZB_SDR_Y_SAD_H (0x0168)
+#define RZB_SDR_Y_SAD_L (0x016c)
+#define RZB_SDR_Y_OFT (0x0170)
+#define RZB_SDR_Y_PTR_S (0x0174)
+#define RZB_SDR_Y_PTR_E (0x0178)
+#define RZB_SDR_C_BAD_H (0x017c)
+#define RZB_SDR_C_BAD_L (0x0180)
+#define RZB_SDR_C_SAD_H (0x0184)
+#define RZB_SDR_C_SAD_L (0x0188)
+
+#define RZB_SDR_C_PTR_S (0x0190)
+#define RZB_SDR_C_PTR_E (0x0194)
+
+/* Shared Bitmasks between RZA & RZB */
+#define RSZ_EN_EN (1 << 0)
+
+#define RSZ_420_CEN (1 << 1)
+#define RSZ_420_YEN (1 << 0)
+
+#define RSZ_I_VPS_MASK (0x1fff)
+
+#define RSZ_I_HPS_MASK (0x1fff)
+
+#define RSZ_O_VSZ_MASK (0x1fff)
+
+#define RSZ_O_HSZ_MASK (0x1ffe)
+
+#define RSZ_V_PHS_Y_MASK (0x3fff)
+
+#define RSZ_V_PHS_C_MASK (0x3fff)
+
+#define RSZ_V_DIF_MASK (0x3fff)
+
+#define RSZ_V_TYP_C (1 << 1)
+#define RSZ_V_TYP_Y (1 << 0)
+
+#define RSZ_V_LPF_C_MASK (0x3f << 6)
+#define RSZ_V_LPF_C_SHIFT 6
+#define RSZ_V_LPF_Y_MASK (0x3f << 0)
+#define RSZ_V_LPF_Y_SHIFT 0
+
+#define RSZ_H_PHS_MASK (0x3fff)
+
+#define RSZ_H_DIF_MASK (0x3fff)
+
+#define RSZ_H_TYP_C (1 << 1)
+#define RSZ_H_TYP_Y (1 << 0)
+
+#define RSZ_H_LPF_C_MASK (0x3f << 6)
+#define RSZ_H_LPF_C_SHIFT 6
+#define RSZ_H_LPF_Y_MASK (0x3f << 0)
+#define RSZ_H_LPF_Y_SHIFT 0
+
+#define RSZ_DWN_EN_DWN_EN (1 << 0)
+
+#endif /* _OMAP4_ISS_REGS_H_ */
diff --git a/drivers/staging/media/omap4iss/iss_resizer.c b/drivers/staging/media/omap4iss/iss_resizer.c
new file mode 100644
index 000000000..5f69012c4
--- /dev/null
+++ b/drivers/staging/media/omap4iss/iss_resizer.c
@@ -0,0 +1,874 @@
+/*
+ * TI OMAP4 ISS V4L2 Driver - ISP RESIZER module
+ *
+ * Copyright (C) 2012 Texas Instruments, Inc.
+ *
+ * Author: Sergio Aguirre <sergio.a.aguirre@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/module.h>
+#include <linux/uaccess.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/dma-mapping.h>
+#include <linux/mm.h>
+#include <linux/sched.h>
+
+#include "iss.h"
+#include "iss_regs.h"
+#include "iss_resizer.h"
+
+static const unsigned int resizer_fmts[] = {
+ MEDIA_BUS_FMT_UYVY8_1X16,
+ MEDIA_BUS_FMT_YUYV8_1X16,
+};
+
+/*
+ * resizer_print_status - Print current RESIZER Module register values.
+ * @resizer: Pointer to ISS ISP RESIZER device.
+ *
+ * Also prints other debug information stored in the RESIZER module.
+ */
+#define RSZ_PRINT_REGISTER(iss, name)\
+ dev_dbg(iss->dev, "###RSZ " #name "=0x%08x\n", \
+ iss_reg_read(iss, OMAP4_ISS_MEM_ISP_RESIZER, RSZ_##name))
+
+#define RZA_PRINT_REGISTER(iss, name)\
+ dev_dbg(iss->dev, "###RZA " #name "=0x%08x\n", \
+ iss_reg_read(iss, OMAP4_ISS_MEM_ISP_RESIZER, RZA_##name))
+
+static void resizer_print_status(struct iss_resizer_device *resizer)
+{
+ struct iss_device *iss = to_iss_device(resizer);
+
+ dev_dbg(iss->dev, "-------------RESIZER Register dump-------------\n");
+
+ RSZ_PRINT_REGISTER(iss, SYSCONFIG);
+ RSZ_PRINT_REGISTER(iss, IN_FIFO_CTRL);
+ RSZ_PRINT_REGISTER(iss, FRACDIV);
+ RSZ_PRINT_REGISTER(iss, SRC_EN);
+ RSZ_PRINT_REGISTER(iss, SRC_MODE);
+ RSZ_PRINT_REGISTER(iss, SRC_FMT0);
+ RSZ_PRINT_REGISTER(iss, SRC_FMT1);
+ RSZ_PRINT_REGISTER(iss, SRC_VPS);
+ RSZ_PRINT_REGISTER(iss, SRC_VSZ);
+ RSZ_PRINT_REGISTER(iss, SRC_HPS);
+ RSZ_PRINT_REGISTER(iss, SRC_HSZ);
+ RSZ_PRINT_REGISTER(iss, DMA_RZA);
+ RSZ_PRINT_REGISTER(iss, DMA_RZB);
+ RSZ_PRINT_REGISTER(iss, DMA_STA);
+ RSZ_PRINT_REGISTER(iss, GCK_MMR);
+ RSZ_PRINT_REGISTER(iss, GCK_SDR);
+ RSZ_PRINT_REGISTER(iss, IRQ_RZA);
+ RSZ_PRINT_REGISTER(iss, IRQ_RZB);
+ RSZ_PRINT_REGISTER(iss, YUV_Y_MIN);
+ RSZ_PRINT_REGISTER(iss, YUV_Y_MAX);
+ RSZ_PRINT_REGISTER(iss, YUV_C_MIN);
+ RSZ_PRINT_REGISTER(iss, YUV_C_MAX);
+ RSZ_PRINT_REGISTER(iss, SEQ);
+
+ RZA_PRINT_REGISTER(iss, EN);
+ RZA_PRINT_REGISTER(iss, MODE);
+ RZA_PRINT_REGISTER(iss, 420);
+ RZA_PRINT_REGISTER(iss, I_VPS);
+ RZA_PRINT_REGISTER(iss, I_HPS);
+ RZA_PRINT_REGISTER(iss, O_VSZ);
+ RZA_PRINT_REGISTER(iss, O_HSZ);
+ RZA_PRINT_REGISTER(iss, V_PHS_Y);
+ RZA_PRINT_REGISTER(iss, V_PHS_C);
+ RZA_PRINT_REGISTER(iss, V_DIF);
+ RZA_PRINT_REGISTER(iss, V_TYP);
+ RZA_PRINT_REGISTER(iss, V_LPF);
+ RZA_PRINT_REGISTER(iss, H_PHS);
+ RZA_PRINT_REGISTER(iss, H_DIF);
+ RZA_PRINT_REGISTER(iss, H_TYP);
+ RZA_PRINT_REGISTER(iss, H_LPF);
+ RZA_PRINT_REGISTER(iss, DWN_EN);
+ RZA_PRINT_REGISTER(iss, SDR_Y_BAD_H);
+ RZA_PRINT_REGISTER(iss, SDR_Y_BAD_L);
+ RZA_PRINT_REGISTER(iss, SDR_Y_SAD_H);
+ RZA_PRINT_REGISTER(iss, SDR_Y_SAD_L);
+ RZA_PRINT_REGISTER(iss, SDR_Y_OFT);
+ RZA_PRINT_REGISTER(iss, SDR_Y_PTR_S);
+ RZA_PRINT_REGISTER(iss, SDR_Y_PTR_E);
+ RZA_PRINT_REGISTER(iss, SDR_C_BAD_H);
+ RZA_PRINT_REGISTER(iss, SDR_C_BAD_L);
+ RZA_PRINT_REGISTER(iss, SDR_C_SAD_H);
+ RZA_PRINT_REGISTER(iss, SDR_C_SAD_L);
+ RZA_PRINT_REGISTER(iss, SDR_C_OFT);
+ RZA_PRINT_REGISTER(iss, SDR_C_PTR_S);
+ RZA_PRINT_REGISTER(iss, SDR_C_PTR_E);
+
+ dev_dbg(iss->dev, "-----------------------------------------------\n");
+}
+
+/*
+ * resizer_enable - Enable/Disable RESIZER.
+ * @enable: enable flag
+ *
+ */
+static void resizer_enable(struct iss_resizer_device *resizer, u8 enable)
+{
+ struct iss_device *iss = to_iss_device(resizer);
+
+ iss_reg_update(iss, OMAP4_ISS_MEM_ISP_RESIZER, RSZ_SRC_EN,
+ RSZ_SRC_EN_SRC_EN, enable ? RSZ_SRC_EN_SRC_EN : 0);
+
+ /* TODO: Enable RSZB */
+ iss_reg_update(iss, OMAP4_ISS_MEM_ISP_RESIZER, RZA_EN, RSZ_EN_EN,
+ enable ? RSZ_EN_EN : 0);
+}
+
+/* -----------------------------------------------------------------------------
+ * Format- and pipeline-related configuration helpers
+ */
+
+/*
+ * resizer_set_outaddr - Set memory address to save output image
+ * @resizer: Pointer to ISP RESIZER device.
+ * @addr: 32-bit memory address aligned on 32 byte boundary.
+ *
+ * Sets the memory address where the output will be saved.
+ */
+static void resizer_set_outaddr(struct iss_resizer_device *resizer, u32 addr)
+{
+ struct iss_device *iss = to_iss_device(resizer);
+ struct v4l2_mbus_framefmt *informat, *outformat;
+
+ informat = &resizer->formats[RESIZER_PAD_SINK];
+ outformat = &resizer->formats[RESIZER_PAD_SOURCE_MEM];
+
+ /* Save address splitted in Base Address H & L */
+ iss_reg_write(iss, OMAP4_ISS_MEM_ISP_RESIZER, RZA_SDR_Y_BAD_H,
+ (addr >> 16) & 0xffff);
+ iss_reg_write(iss, OMAP4_ISS_MEM_ISP_RESIZER, RZA_SDR_Y_BAD_L,
+ addr & 0xffff);
+
+ /* SAD = BAD */
+ iss_reg_write(iss, OMAP4_ISS_MEM_ISP_RESIZER, RZA_SDR_Y_SAD_H,
+ (addr >> 16) & 0xffff);
+ iss_reg_write(iss, OMAP4_ISS_MEM_ISP_RESIZER, RZA_SDR_Y_SAD_L,
+ addr & 0xffff);
+
+ /* Program UV buffer address... Hardcoded to be contiguous! */
+ if ((informat->code == MEDIA_BUS_FMT_UYVY8_1X16) &&
+ (outformat->code == MEDIA_BUS_FMT_YUYV8_1_5X8)) {
+ u32 c_addr = addr + (resizer->video_out.bpl_value *
+ (outformat->height - 1));
+
+ /* Ensure Y_BAD_L[6:0] = C_BAD_L[6:0]*/
+ if ((c_addr ^ addr) & 0x7f) {
+ c_addr &= ~0x7f;
+ c_addr += 0x80;
+ c_addr |= addr & 0x7f;
+ }
+
+ /* Save address splitted in Base Address H & L */
+ iss_reg_write(iss, OMAP4_ISS_MEM_ISP_RESIZER, RZA_SDR_C_BAD_H,
+ (c_addr >> 16) & 0xffff);
+ iss_reg_write(iss, OMAP4_ISS_MEM_ISP_RESIZER, RZA_SDR_C_BAD_L,
+ c_addr & 0xffff);
+
+ /* SAD = BAD */
+ iss_reg_write(iss, OMAP4_ISS_MEM_ISP_RESIZER, RZA_SDR_C_SAD_H,
+ (c_addr >> 16) & 0xffff);
+ iss_reg_write(iss, OMAP4_ISS_MEM_ISP_RESIZER, RZA_SDR_C_SAD_L,
+ c_addr & 0xffff);
+ }
+}
+
+static void resizer_configure(struct iss_resizer_device *resizer)
+{
+ struct iss_device *iss = to_iss_device(resizer);
+ struct v4l2_mbus_framefmt *informat, *outformat;
+
+ informat = &resizer->formats[RESIZER_PAD_SINK];
+ outformat = &resizer->formats[RESIZER_PAD_SOURCE_MEM];
+
+ /* Disable pass-through more. Despite its name, the BYPASS bit controls
+ * pass-through mode, not bypass mode.
+ */
+ iss_reg_clr(iss, OMAP4_ISS_MEM_ISP_RESIZER, RSZ_SRC_FMT0,
+ RSZ_SRC_FMT0_BYPASS);
+
+ /* Select RSZ input */
+ iss_reg_update(iss, OMAP4_ISS_MEM_ISP_RESIZER, RSZ_SRC_FMT0,
+ RSZ_SRC_FMT0_SEL,
+ resizer->input == RESIZER_INPUT_IPIPEIF ?
+ RSZ_SRC_FMT0_SEL : 0);
+
+ /* RSZ ignores WEN signal from IPIPE/IPIPEIF */
+ iss_reg_clr(iss, OMAP4_ISS_MEM_ISP_RESIZER, RSZ_SRC_MODE,
+ RSZ_SRC_MODE_WRT);
+
+ /* Set Resizer in free-running mode */
+ iss_reg_clr(iss, OMAP4_ISS_MEM_ISP_RESIZER, RSZ_SRC_MODE,
+ RSZ_SRC_MODE_OST);
+
+ /* Init Resizer A */
+ iss_reg_clr(iss, OMAP4_ISS_MEM_ISP_RESIZER, RZA_MODE,
+ RZA_MODE_ONE_SHOT);
+
+ /* Set size related things now */
+ iss_reg_write(iss, OMAP4_ISS_MEM_ISP_RESIZER, RSZ_SRC_VPS, 0);
+ iss_reg_write(iss, OMAP4_ISS_MEM_ISP_RESIZER, RSZ_SRC_HPS, 0);
+ iss_reg_write(iss, OMAP4_ISS_MEM_ISP_RESIZER, RSZ_SRC_VSZ,
+ informat->height - 2);
+ iss_reg_write(iss, OMAP4_ISS_MEM_ISP_RESIZER, RSZ_SRC_HSZ,
+ informat->width - 1);
+
+ iss_reg_write(iss, OMAP4_ISS_MEM_ISP_RESIZER, RZA_I_VPS, 0);
+ iss_reg_write(iss, OMAP4_ISS_MEM_ISP_RESIZER, RZA_I_HPS, 0);
+
+ iss_reg_write(iss, OMAP4_ISS_MEM_ISP_RESIZER, RZA_O_VSZ,
+ outformat->height - 2);
+ iss_reg_write(iss, OMAP4_ISS_MEM_ISP_RESIZER, RZA_O_HSZ,
+ outformat->width - 1);
+
+ iss_reg_write(iss, OMAP4_ISS_MEM_ISP_RESIZER, RZA_V_DIF, 0x100);
+ iss_reg_write(iss, OMAP4_ISS_MEM_ISP_RESIZER, RZA_H_DIF, 0x100);
+
+ /* Buffer output settings */
+ iss_reg_write(iss, OMAP4_ISS_MEM_ISP_RESIZER, RZA_SDR_Y_PTR_S, 0);
+ iss_reg_write(iss, OMAP4_ISS_MEM_ISP_RESIZER, RZA_SDR_Y_PTR_E,
+ outformat->height - 1);
+
+ iss_reg_write(iss, OMAP4_ISS_MEM_ISP_RESIZER, RZA_SDR_Y_OFT,
+ resizer->video_out.bpl_value);
+
+ /* UYVY -> NV12 conversion */
+ if ((informat->code == MEDIA_BUS_FMT_UYVY8_1X16) &&
+ (outformat->code == MEDIA_BUS_FMT_YUYV8_1_5X8)) {
+ iss_reg_write(iss, OMAP4_ISS_MEM_ISP_RESIZER, RZA_420,
+ RSZ_420_CEN | RSZ_420_YEN);
+
+ /* UV Buffer output settings */
+ iss_reg_write(iss, OMAP4_ISS_MEM_ISP_RESIZER, RZA_SDR_C_PTR_S,
+ 0);
+ iss_reg_write(iss, OMAP4_ISS_MEM_ISP_RESIZER, RZA_SDR_C_PTR_E,
+ outformat->height - 1);
+
+ iss_reg_write(iss, OMAP4_ISS_MEM_ISP_RESIZER, RZA_SDR_C_OFT,
+ resizer->video_out.bpl_value);
+ } else {
+ iss_reg_write(iss, OMAP4_ISS_MEM_ISP_RESIZER, RZA_420, 0);
+ }
+}
+
+/* -----------------------------------------------------------------------------
+ * Interrupt handling
+ */
+
+static void resizer_isr_buffer(struct iss_resizer_device *resizer)
+{
+ struct iss_buffer *buffer;
+
+ /* The whole resizer needs to be stopped. Disabling RZA only produces
+ * input FIFO overflows, most probably when the next frame is received.
+ */
+ resizer_enable(resizer, 0);
+
+ buffer = omap4iss_video_buffer_next(&resizer->video_out);
+ if (buffer == NULL)
+ return;
+
+ resizer_set_outaddr(resizer, buffer->iss_addr);
+
+ resizer_enable(resizer, 1);
+}
+
+/*
+ * omap4iss_resizer_isr - Configure resizer during interframe time.
+ * @resizer: Pointer to ISP RESIZER device.
+ * @events: RESIZER events
+ */
+void omap4iss_resizer_isr(struct iss_resizer_device *resizer, u32 events)
+{
+ struct iss_device *iss = to_iss_device(resizer);
+ struct iss_pipeline *pipe =
+ to_iss_pipeline(&resizer->subdev.entity);
+
+ if (events & (ISP5_IRQ_RSZ_FIFO_IN_BLK_ERR |
+ ISP5_IRQ_RSZ_FIFO_OVF)) {
+ dev_dbg(iss->dev, "RSZ Err: FIFO_IN_BLK:%d, FIFO_OVF:%d\n",
+ events & ISP5_IRQ_RSZ_FIFO_IN_BLK_ERR ? 1 : 0,
+ events & ISP5_IRQ_RSZ_FIFO_OVF ? 1 : 0);
+ omap4iss_pipeline_cancel_stream(pipe);
+ }
+
+ if (omap4iss_module_sync_is_stopping(&resizer->wait,
+ &resizer->stopping))
+ return;
+
+ if (events & ISP5_IRQ_RSZ_INT_DMA)
+ resizer_isr_buffer(resizer);
+}
+
+/* -----------------------------------------------------------------------------
+ * ISS video operations
+ */
+
+static int resizer_video_queue(struct iss_video *video,
+ struct iss_buffer *buffer)
+{
+ struct iss_resizer_device *resizer = container_of(video,
+ struct iss_resizer_device, video_out);
+
+ if (!(resizer->output & RESIZER_OUTPUT_MEMORY))
+ return -ENODEV;
+
+ resizer_set_outaddr(resizer, buffer->iss_addr);
+
+ /*
+ * If streaming was enabled before there was a buffer queued
+ * or underrun happened in the ISR, the hardware was not enabled
+ * and DMA queue flag ISS_VIDEO_DMAQUEUE_UNDERRUN is still set.
+ * Enable it now.
+ */
+ if (video->dmaqueue_flags & ISS_VIDEO_DMAQUEUE_UNDERRUN) {
+ resizer_enable(resizer, 1);
+ iss_video_dmaqueue_flags_clr(video);
+ }
+
+ return 0;
+}
+
+static const struct iss_video_operations resizer_video_ops = {
+ .queue = resizer_video_queue,
+};
+
+/* -----------------------------------------------------------------------------
+ * V4L2 subdev operations
+ */
+
+/*
+ * resizer_set_stream - Enable/Disable streaming on the RESIZER module
+ * @sd: ISP RESIZER V4L2 subdevice
+ * @enable: Enable/disable stream
+ */
+static int resizer_set_stream(struct v4l2_subdev *sd, int enable)
+{
+ struct iss_resizer_device *resizer = v4l2_get_subdevdata(sd);
+ struct iss_device *iss = to_iss_device(resizer);
+ struct iss_video *video_out = &resizer->video_out;
+ int ret = 0;
+
+ if (resizer->state == ISS_PIPELINE_STREAM_STOPPED) {
+ if (enable == ISS_PIPELINE_STREAM_STOPPED)
+ return 0;
+
+ omap4iss_isp_subclk_enable(iss, OMAP4_ISS_ISP_SUBCLK_RSZ);
+
+ iss_reg_set(iss, OMAP4_ISS_MEM_ISP_RESIZER, RSZ_GCK_MMR,
+ RSZ_GCK_MMR_MMR);
+ iss_reg_set(iss, OMAP4_ISS_MEM_ISP_RESIZER, RSZ_GCK_SDR,
+ RSZ_GCK_SDR_CORE);
+
+ /* FIXME: Enable RSZB also */
+ iss_reg_set(iss, OMAP4_ISS_MEM_ISP_RESIZER, RSZ_SYSCONFIG,
+ RSZ_SYSCONFIG_RSZA_CLK_EN);
+ }
+
+ switch (enable) {
+ case ISS_PIPELINE_STREAM_CONTINUOUS:
+
+ resizer_configure(resizer);
+ resizer_print_status(resizer);
+
+ /*
+ * When outputting to memory with no buffer available, let the
+ * buffer queue handler start the hardware. A DMA queue flag
+ * ISS_VIDEO_DMAQUEUE_QUEUED will be set as soon as there is
+ * a buffer available.
+ */
+ if (resizer->output & RESIZER_OUTPUT_MEMORY &&
+ !(video_out->dmaqueue_flags & ISS_VIDEO_DMAQUEUE_QUEUED))
+ break;
+
+ atomic_set(&resizer->stopping, 0);
+ resizer_enable(resizer, 1);
+ iss_video_dmaqueue_flags_clr(video_out);
+ break;
+
+ case ISS_PIPELINE_STREAM_STOPPED:
+ if (resizer->state == ISS_PIPELINE_STREAM_STOPPED)
+ return 0;
+ if (omap4iss_module_sync_idle(&sd->entity, &resizer->wait,
+ &resizer->stopping))
+ ret = -ETIMEDOUT;
+
+ resizer_enable(resizer, 0);
+ iss_reg_clr(iss, OMAP4_ISS_MEM_ISP_RESIZER, RSZ_SYSCONFIG,
+ RSZ_SYSCONFIG_RSZA_CLK_EN);
+ iss_reg_clr(iss, OMAP4_ISS_MEM_ISP_RESIZER, RSZ_GCK_SDR,
+ RSZ_GCK_SDR_CORE);
+ iss_reg_clr(iss, OMAP4_ISS_MEM_ISP_RESIZER, RSZ_GCK_MMR,
+ RSZ_GCK_MMR_MMR);
+ omap4iss_isp_subclk_disable(iss, OMAP4_ISS_ISP_SUBCLK_RSZ);
+ iss_video_dmaqueue_flags_clr(video_out);
+ break;
+ }
+
+ resizer->state = enable;
+ return ret;
+}
+
+static struct v4l2_mbus_framefmt *
+__resizer_get_format(struct iss_resizer_device *resizer,
+ struct v4l2_subdev_pad_config *cfg, unsigned int pad,
+ enum v4l2_subdev_format_whence which)
+{
+ if (which == V4L2_SUBDEV_FORMAT_TRY)
+ return v4l2_subdev_get_try_format(&resizer->subdev, cfg, pad);
+ return &resizer->formats[pad];
+}
+
+/*
+ * resizer_try_format - Try video format on a pad
+ * @resizer: ISS RESIZER device
+ * @cfg: V4L2 subdev pad config
+ * @pad: Pad number
+ * @fmt: Format
+ */
+static void
+resizer_try_format(struct iss_resizer_device *resizer,
+ struct v4l2_subdev_pad_config *cfg, unsigned int pad,
+ struct v4l2_mbus_framefmt *fmt,
+ enum v4l2_subdev_format_whence which)
+{
+ u32 pixelcode;
+ struct v4l2_mbus_framefmt *format;
+ unsigned int width = fmt->width;
+ unsigned int height = fmt->height;
+ unsigned int i;
+
+ switch (pad) {
+ case RESIZER_PAD_SINK:
+ for (i = 0; i < ARRAY_SIZE(resizer_fmts); i++) {
+ if (fmt->code == resizer_fmts[i])
+ break;
+ }
+
+ /* If not found, use UYVY as default */
+ if (i >= ARRAY_SIZE(resizer_fmts))
+ fmt->code = MEDIA_BUS_FMT_UYVY8_1X16;
+
+ /* Clamp the input size. */
+ fmt->width = clamp_t(u32, width, 1, 8192);
+ fmt->height = clamp_t(u32, height, 1, 8192);
+ break;
+
+ case RESIZER_PAD_SOURCE_MEM:
+ pixelcode = fmt->code;
+ format = __resizer_get_format(resizer, cfg, RESIZER_PAD_SINK,
+ which);
+ memcpy(fmt, format, sizeof(*fmt));
+
+ if ((pixelcode == MEDIA_BUS_FMT_YUYV8_1_5X8) &&
+ (fmt->code == MEDIA_BUS_FMT_UYVY8_1X16))
+ fmt->code = pixelcode;
+
+ /* The data formatter truncates the number of horizontal output
+ * pixels to a multiple of 16. To avoid clipping data, allow
+ * callers to request an output size bigger than the input size
+ * up to the nearest multiple of 16.
+ */
+ fmt->width = clamp_t(u32, width, 32, (fmt->width + 15) & ~15);
+ fmt->width &= ~15;
+ fmt->height = clamp_t(u32, height, 32, fmt->height);
+ break;
+
+ }
+
+ fmt->colorspace = V4L2_COLORSPACE_JPEG;
+ fmt->field = V4L2_FIELD_NONE;
+}
+
+/*
+ * resizer_enum_mbus_code - Handle pixel format enumeration
+ * @sd : pointer to v4l2 subdev structure
+ * @cfg: V4L2 subdev pad config
+ * @code : pointer to v4l2_subdev_mbus_code_enum structure
+ * return -EINVAL or zero on success
+ */
+static int resizer_enum_mbus_code(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_mbus_code_enum *code)
+{
+ struct iss_resizer_device *resizer = v4l2_get_subdevdata(sd);
+ struct v4l2_mbus_framefmt *format;
+
+ switch (code->pad) {
+ case RESIZER_PAD_SINK:
+ if (code->index >= ARRAY_SIZE(resizer_fmts))
+ return -EINVAL;
+
+ code->code = resizer_fmts[code->index];
+ break;
+
+ case RESIZER_PAD_SOURCE_MEM:
+ format = __resizer_get_format(resizer, cfg, RESIZER_PAD_SINK,
+ code->which);
+
+ if (code->index == 0) {
+ code->code = format->code;
+ break;
+ }
+
+ switch (format->code) {
+ case MEDIA_BUS_FMT_UYVY8_1X16:
+ if (code->index == 1)
+ code->code = MEDIA_BUS_FMT_YUYV8_1_5X8;
+ else
+ return -EINVAL;
+ break;
+ default:
+ if (code->index != 0)
+ return -EINVAL;
+ }
+
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int resizer_enum_frame_size(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_frame_size_enum *fse)
+{
+ struct iss_resizer_device *resizer = v4l2_get_subdevdata(sd);
+ struct v4l2_mbus_framefmt format;
+
+ if (fse->index != 0)
+ return -EINVAL;
+
+ format.code = fse->code;
+ format.width = 1;
+ format.height = 1;
+ resizer_try_format(resizer, cfg, fse->pad, &format, fse->which);
+ fse->min_width = format.width;
+ fse->min_height = format.height;
+
+ if (format.code != fse->code)
+ return -EINVAL;
+
+ format.code = fse->code;
+ format.width = -1;
+ format.height = -1;
+ resizer_try_format(resizer, cfg, fse->pad, &format, fse->which);
+ fse->max_width = format.width;
+ fse->max_height = format.height;
+
+ return 0;
+}
+
+/*
+ * resizer_get_format - Retrieve the video format on a pad
+ * @sd : ISP RESIZER V4L2 subdevice
+ * @cfg: V4L2 subdev pad config
+ * @fmt: Format
+ *
+ * Return 0 on success or -EINVAL if the pad is invalid or doesn't correspond
+ * to the format type.
+ */
+static int resizer_get_format(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_format *fmt)
+{
+ struct iss_resizer_device *resizer = v4l2_get_subdevdata(sd);
+ struct v4l2_mbus_framefmt *format;
+
+ format = __resizer_get_format(resizer, cfg, fmt->pad, fmt->which);
+ if (format == NULL)
+ return -EINVAL;
+
+ fmt->format = *format;
+ return 0;
+}
+
+/*
+ * resizer_set_format - Set the video format on a pad
+ * @sd : ISP RESIZER V4L2 subdevice
+ * @cfg: V4L2 subdev pad config
+ * @fmt: Format
+ *
+ * Return 0 on success or -EINVAL if the pad is invalid or doesn't correspond
+ * to the format type.
+ */
+static int resizer_set_format(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_format *fmt)
+{
+ struct iss_resizer_device *resizer = v4l2_get_subdevdata(sd);
+ struct v4l2_mbus_framefmt *format;
+
+ format = __resizer_get_format(resizer, cfg, fmt->pad, fmt->which);
+ if (format == NULL)
+ return -EINVAL;
+
+ resizer_try_format(resizer, cfg, fmt->pad, &fmt->format, fmt->which);
+ *format = fmt->format;
+
+ /* Propagate the format from sink to source */
+ if (fmt->pad == RESIZER_PAD_SINK) {
+ format = __resizer_get_format(resizer, cfg,
+ RESIZER_PAD_SOURCE_MEM,
+ fmt->which);
+ *format = fmt->format;
+ resizer_try_format(resizer, cfg, RESIZER_PAD_SOURCE_MEM, format,
+ fmt->which);
+ }
+
+ return 0;
+}
+
+static int resizer_link_validate(struct v4l2_subdev *sd,
+ struct media_link *link,
+ struct v4l2_subdev_format *source_fmt,
+ struct v4l2_subdev_format *sink_fmt)
+{
+ /* Check if the two ends match */
+ if (source_fmt->format.width != sink_fmt->format.width ||
+ source_fmt->format.height != sink_fmt->format.height)
+ return -EPIPE;
+
+ if (source_fmt->format.code != sink_fmt->format.code)
+ return -EPIPE;
+
+ return 0;
+}
+
+/*
+ * resizer_init_formats - Initialize formats on all pads
+ * @sd: ISP RESIZER V4L2 subdevice
+ * @fh: V4L2 subdev file handle
+ *
+ * Initialize all pad formats with default values. If fh is not NULL, try
+ * formats are initialized on the file handle. Otherwise active formats are
+ * initialized on the device.
+ */
+static int resizer_init_formats(struct v4l2_subdev *sd,
+ struct v4l2_subdev_fh *fh)
+{
+ struct v4l2_subdev_format format;
+
+ memset(&format, 0, sizeof(format));
+ format.pad = RESIZER_PAD_SINK;
+ format.which = fh ? V4L2_SUBDEV_FORMAT_TRY : V4L2_SUBDEV_FORMAT_ACTIVE;
+ format.format.code = MEDIA_BUS_FMT_UYVY8_1X16;
+ format.format.width = 4096;
+ format.format.height = 4096;
+ resizer_set_format(sd, fh ? fh->pad : NULL, &format);
+
+ return 0;
+}
+
+/* V4L2 subdev video operations */
+static const struct v4l2_subdev_video_ops resizer_v4l2_video_ops = {
+ .s_stream = resizer_set_stream,
+};
+
+/* V4L2 subdev pad operations */
+static const struct v4l2_subdev_pad_ops resizer_v4l2_pad_ops = {
+ .enum_mbus_code = resizer_enum_mbus_code,
+ .enum_frame_size = resizer_enum_frame_size,
+ .get_fmt = resizer_get_format,
+ .set_fmt = resizer_set_format,
+ .link_validate = resizer_link_validate,
+};
+
+/* V4L2 subdev operations */
+static const struct v4l2_subdev_ops resizer_v4l2_ops = {
+ .video = &resizer_v4l2_video_ops,
+ .pad = &resizer_v4l2_pad_ops,
+};
+
+/* V4L2 subdev internal operations */
+static const struct v4l2_subdev_internal_ops resizer_v4l2_internal_ops = {
+ .open = resizer_init_formats,
+};
+
+/* -----------------------------------------------------------------------------
+ * Media entity operations
+ */
+
+/*
+ * resizer_link_setup - Setup RESIZER connections
+ * @entity: RESIZER media entity
+ * @local: Pad at the local end of the link
+ * @remote: Pad at the remote end of the link
+ * @flags: Link flags
+ *
+ * return -EINVAL or zero on success
+ */
+static int resizer_link_setup(struct media_entity *entity,
+ const struct media_pad *local,
+ const struct media_pad *remote, u32 flags)
+{
+ struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity);
+ struct iss_resizer_device *resizer = v4l2_get_subdevdata(sd);
+ struct iss_device *iss = to_iss_device(resizer);
+
+ switch (local->index | media_entity_type(remote->entity)) {
+ case RESIZER_PAD_SINK | MEDIA_ENT_T_V4L2_SUBDEV:
+ /* Read from IPIPE or IPIPEIF. */
+ if (!(flags & MEDIA_LNK_FL_ENABLED)) {
+ resizer->input = RESIZER_INPUT_NONE;
+ break;
+ }
+
+ if (resizer->input != RESIZER_INPUT_NONE)
+ return -EBUSY;
+
+ if (remote->entity == &iss->ipipeif.subdev.entity)
+ resizer->input = RESIZER_INPUT_IPIPEIF;
+ else if (remote->entity == &iss->ipipe.subdev.entity)
+ resizer->input = RESIZER_INPUT_IPIPE;
+
+
+ break;
+
+ case RESIZER_PAD_SOURCE_MEM | MEDIA_ENT_T_DEVNODE:
+ /* Write to memory */
+ if (flags & MEDIA_LNK_FL_ENABLED) {
+ if (resizer->output & ~RESIZER_OUTPUT_MEMORY)
+ return -EBUSY;
+ resizer->output |= RESIZER_OUTPUT_MEMORY;
+ } else {
+ resizer->output &= ~RESIZER_OUTPUT_MEMORY;
+ }
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/* media operations */
+static const struct media_entity_operations resizer_media_ops = {
+ .link_setup = resizer_link_setup,
+ .link_validate = v4l2_subdev_link_validate,
+};
+
+/*
+ * resizer_init_entities - Initialize V4L2 subdev and media entity
+ * @resizer: ISS ISP RESIZER module
+ *
+ * Return 0 on success and a negative error code on failure.
+ */
+static int resizer_init_entities(struct iss_resizer_device *resizer)
+{
+ struct v4l2_subdev *sd = &resizer->subdev;
+ struct media_pad *pads = resizer->pads;
+ struct media_entity *me = &sd->entity;
+ int ret;
+
+ resizer->input = RESIZER_INPUT_NONE;
+
+ v4l2_subdev_init(sd, &resizer_v4l2_ops);
+ sd->internal_ops = &resizer_v4l2_internal_ops;
+ strlcpy(sd->name, "OMAP4 ISS ISP resizer", sizeof(sd->name));
+ sd->grp_id = 1 << 16; /* group ID for iss subdevs */
+ v4l2_set_subdevdata(sd, resizer);
+ sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+
+ pads[RESIZER_PAD_SINK].flags = MEDIA_PAD_FL_SINK;
+ pads[RESIZER_PAD_SOURCE_MEM].flags = MEDIA_PAD_FL_SOURCE;
+
+ me->ops = &resizer_media_ops;
+ ret = media_entity_init(me, RESIZER_PADS_NUM, pads, 0);
+ if (ret < 0)
+ return ret;
+
+ resizer_init_formats(sd, NULL);
+
+ resizer->video_out.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ resizer->video_out.ops = &resizer_video_ops;
+ resizer->video_out.iss = to_iss_device(resizer);
+ resizer->video_out.capture_mem = PAGE_ALIGN(4096 * 4096) * 3;
+ resizer->video_out.bpl_alignment = 32;
+ resizer->video_out.bpl_zero_padding = 1;
+ resizer->video_out.bpl_max = 0x1ffe0;
+
+ ret = omap4iss_video_init(&resizer->video_out, "ISP resizer a");
+ if (ret < 0)
+ return ret;
+
+ /* Connect the RESIZER subdev to the video node. */
+ ret = media_entity_create_link(&resizer->subdev.entity,
+ RESIZER_PAD_SOURCE_MEM,
+ &resizer->video_out.video.entity, 0, 0);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+void omap4iss_resizer_unregister_entities(struct iss_resizer_device *resizer)
+{
+ v4l2_device_unregister_subdev(&resizer->subdev);
+ omap4iss_video_unregister(&resizer->video_out);
+}
+
+int omap4iss_resizer_register_entities(struct iss_resizer_device *resizer,
+ struct v4l2_device *vdev)
+{
+ int ret;
+
+ /* Register the subdev and video node. */
+ ret = v4l2_device_register_subdev(vdev, &resizer->subdev);
+ if (ret < 0)
+ goto error;
+
+ ret = omap4iss_video_register(&resizer->video_out, vdev);
+ if (ret < 0)
+ goto error;
+
+ return 0;
+
+error:
+ omap4iss_resizer_unregister_entities(resizer);
+ return ret;
+}
+
+/* -----------------------------------------------------------------------------
+ * ISP RESIZER initialisation and cleanup
+ */
+
+/*
+ * omap4iss_resizer_init - RESIZER module initialization.
+ * @iss: Device pointer specific to the OMAP4 ISS.
+ *
+ * TODO: Get the initialisation values from platform data.
+ *
+ * Return 0 on success or a negative error code otherwise.
+ */
+int omap4iss_resizer_init(struct iss_device *iss)
+{
+ struct iss_resizer_device *resizer = &iss->resizer;
+
+ resizer->state = ISS_PIPELINE_STREAM_STOPPED;
+ init_waitqueue_head(&resizer->wait);
+
+ return resizer_init_entities(resizer);
+}
+
+/*
+ * omap4iss_resizer_cleanup - RESIZER module cleanup.
+ * @iss: Device pointer specific to the OMAP4 ISS.
+ */
+void omap4iss_resizer_cleanup(struct iss_device *iss)
+{
+ struct iss_resizer_device *resizer = &iss->resizer;
+
+ media_entity_cleanup(&resizer->subdev.entity);
+}
diff --git a/drivers/staging/media/omap4iss/iss_resizer.h b/drivers/staging/media/omap4iss/iss_resizer.h
new file mode 100644
index 000000000..3727498b0
--- /dev/null
+++ b/drivers/staging/media/omap4iss/iss_resizer.h
@@ -0,0 +1,75 @@
+/*
+ * TI OMAP4 ISS V4L2 Driver - ISP RESIZER module
+ *
+ * Copyright (C) 2012 Texas Instruments, Inc.
+ *
+ * Author: Sergio Aguirre <sergio.a.aguirre@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef OMAP4_ISS_RESIZER_H
+#define OMAP4_ISS_RESIZER_H
+
+#include "iss_video.h"
+
+enum resizer_input_entity {
+ RESIZER_INPUT_NONE,
+ RESIZER_INPUT_IPIPE,
+ RESIZER_INPUT_IPIPEIF
+};
+
+#define RESIZER_OUTPUT_MEMORY (1 << 0)
+
+/* Sink and source RESIZER pads */
+#define RESIZER_PAD_SINK 0
+#define RESIZER_PAD_SOURCE_MEM 1
+#define RESIZER_PADS_NUM 2
+
+/*
+ * struct iss_resizer_device - Structure for the RESIZER module to store its own
+ * information
+ * @subdev: V4L2 subdevice
+ * @pads: Sink and source media entity pads
+ * @formats: Active video formats
+ * @input: Active input
+ * @output: Active outputs
+ * @video_out: Output video node
+ * @error: A hardware error occurred during capture
+ * @state: Streaming state
+ * @wait: Wait queue used to stop the module
+ * @stopping: Stopping state
+ */
+struct iss_resizer_device {
+ struct v4l2_subdev subdev;
+ struct media_pad pads[RESIZER_PADS_NUM];
+ struct v4l2_mbus_framefmt formats[RESIZER_PADS_NUM];
+
+ enum resizer_input_entity input;
+ unsigned int output;
+ struct iss_video video_out;
+ unsigned int error;
+
+ enum iss_pipeline_stream_state state;
+ wait_queue_head_t wait;
+ atomic_t stopping;
+};
+
+struct iss_device;
+
+int omap4iss_resizer_init(struct iss_device *iss);
+void omap4iss_resizer_cleanup(struct iss_device *iss);
+int omap4iss_resizer_register_entities(struct iss_resizer_device *resizer,
+ struct v4l2_device *vdev);
+void omap4iss_resizer_unregister_entities(struct iss_resizer_device *resizer);
+
+int omap4iss_resizer_busy(struct iss_resizer_device *resizer);
+void omap4iss_resizer_isr(struct iss_resizer_device *resizer, u32 events);
+void omap4iss_resizer_restore_context(struct iss_device *iss);
+void omap4iss_resizer_max_rate(struct iss_resizer_device *resizer,
+ unsigned int *max_rate);
+
+#endif /* OMAP4_ISS_RESIZER_H */
diff --git a/drivers/staging/media/omap4iss/iss_video.c b/drivers/staging/media/omap4iss/iss_video.c
new file mode 100644
index 000000000..85c54fedd
--- /dev/null
+++ b/drivers/staging/media/omap4iss/iss_video.c
@@ -0,0 +1,1232 @@
+/*
+ * TI OMAP4 ISS V4L2 Driver - Generic video node
+ *
+ * Copyright (C) 2012 Texas Instruments, Inc.
+ *
+ * Author: Sergio Aguirre <sergio.a.aguirre@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <asm/cacheflush.h>
+#include <linux/clk.h>
+#include <linux/mm.h>
+#include <linux/pagemap.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+#include <linux/module.h>
+#include <media/v4l2-dev.h>
+#include <media/v4l2-ioctl.h>
+
+#include "iss_video.h"
+#include "iss.h"
+
+
+/* -----------------------------------------------------------------------------
+ * Helper functions
+ */
+
+static struct iss_format_info formats[] = {
+ { MEDIA_BUS_FMT_Y8_1X8, MEDIA_BUS_FMT_Y8_1X8,
+ MEDIA_BUS_FMT_Y8_1X8, MEDIA_BUS_FMT_Y8_1X8,
+ V4L2_PIX_FMT_GREY, 8, "Greyscale 8 bpp", },
+ { MEDIA_BUS_FMT_Y10_1X10, MEDIA_BUS_FMT_Y10_1X10,
+ MEDIA_BUS_FMT_Y10_1X10, MEDIA_BUS_FMT_Y8_1X8,
+ V4L2_PIX_FMT_Y10, 10, "Greyscale 10 bpp", },
+ { MEDIA_BUS_FMT_Y12_1X12, MEDIA_BUS_FMT_Y10_1X10,
+ MEDIA_BUS_FMT_Y12_1X12, MEDIA_BUS_FMT_Y8_1X8,
+ V4L2_PIX_FMT_Y12, 12, "Greyscale 12 bpp", },
+ { MEDIA_BUS_FMT_SBGGR8_1X8, MEDIA_BUS_FMT_SBGGR8_1X8,
+ MEDIA_BUS_FMT_SBGGR8_1X8, MEDIA_BUS_FMT_SBGGR8_1X8,
+ V4L2_PIX_FMT_SBGGR8, 8, "BGGR Bayer 8 bpp", },
+ { MEDIA_BUS_FMT_SGBRG8_1X8, MEDIA_BUS_FMT_SGBRG8_1X8,
+ MEDIA_BUS_FMT_SGBRG8_1X8, MEDIA_BUS_FMT_SGBRG8_1X8,
+ V4L2_PIX_FMT_SGBRG8, 8, "GBRG Bayer 8 bpp", },
+ { MEDIA_BUS_FMT_SGRBG8_1X8, MEDIA_BUS_FMT_SGRBG8_1X8,
+ MEDIA_BUS_FMT_SGRBG8_1X8, MEDIA_BUS_FMT_SGRBG8_1X8,
+ V4L2_PIX_FMT_SGRBG8, 8, "GRBG Bayer 8 bpp", },
+ { MEDIA_BUS_FMT_SRGGB8_1X8, MEDIA_BUS_FMT_SRGGB8_1X8,
+ MEDIA_BUS_FMT_SRGGB8_1X8, MEDIA_BUS_FMT_SRGGB8_1X8,
+ V4L2_PIX_FMT_SRGGB8, 8, "RGGB Bayer 8 bpp", },
+ { MEDIA_BUS_FMT_SGRBG10_DPCM8_1X8, MEDIA_BUS_FMT_SGRBG10_DPCM8_1X8,
+ MEDIA_BUS_FMT_SGRBG10_1X10, 0,
+ V4L2_PIX_FMT_SGRBG10DPCM8, 8, "GRBG Bayer 10 bpp DPCM8", },
+ { MEDIA_BUS_FMT_SBGGR10_1X10, MEDIA_BUS_FMT_SBGGR10_1X10,
+ MEDIA_BUS_FMT_SBGGR10_1X10, MEDIA_BUS_FMT_SBGGR8_1X8,
+ V4L2_PIX_FMT_SBGGR10, 10, "BGGR Bayer 10 bpp", },
+ { MEDIA_BUS_FMT_SGBRG10_1X10, MEDIA_BUS_FMT_SGBRG10_1X10,
+ MEDIA_BUS_FMT_SGBRG10_1X10, MEDIA_BUS_FMT_SGBRG8_1X8,
+ V4L2_PIX_FMT_SGBRG10, 10, "GBRG Bayer 10 bpp", },
+ { MEDIA_BUS_FMT_SGRBG10_1X10, MEDIA_BUS_FMT_SGRBG10_1X10,
+ MEDIA_BUS_FMT_SGRBG10_1X10, MEDIA_BUS_FMT_SGRBG8_1X8,
+ V4L2_PIX_FMT_SGRBG10, 10, "GRBG Bayer 10 bpp", },
+ { MEDIA_BUS_FMT_SRGGB10_1X10, MEDIA_BUS_FMT_SRGGB10_1X10,
+ MEDIA_BUS_FMT_SRGGB10_1X10, MEDIA_BUS_FMT_SRGGB8_1X8,
+ V4L2_PIX_FMT_SRGGB10, 10, "RGGB Bayer 10 bpp", },
+ { MEDIA_BUS_FMT_SBGGR12_1X12, MEDIA_BUS_FMT_SBGGR10_1X10,
+ MEDIA_BUS_FMT_SBGGR12_1X12, MEDIA_BUS_FMT_SBGGR8_1X8,
+ V4L2_PIX_FMT_SBGGR12, 12, "BGGR Bayer 12 bpp", },
+ { MEDIA_BUS_FMT_SGBRG12_1X12, MEDIA_BUS_FMT_SGBRG10_1X10,
+ MEDIA_BUS_FMT_SGBRG12_1X12, MEDIA_BUS_FMT_SGBRG8_1X8,
+ V4L2_PIX_FMT_SGBRG12, 12, "GBRG Bayer 12 bpp", },
+ { MEDIA_BUS_FMT_SGRBG12_1X12, MEDIA_BUS_FMT_SGRBG10_1X10,
+ MEDIA_BUS_FMT_SGRBG12_1X12, MEDIA_BUS_FMT_SGRBG8_1X8,
+ V4L2_PIX_FMT_SGRBG12, 12, "GRBG Bayer 12 bpp", },
+ { MEDIA_BUS_FMT_SRGGB12_1X12, MEDIA_BUS_FMT_SRGGB10_1X10,
+ MEDIA_BUS_FMT_SRGGB12_1X12, MEDIA_BUS_FMT_SRGGB8_1X8,
+ V4L2_PIX_FMT_SRGGB12, 12, "RGGB Bayer 12 bpp", },
+ { MEDIA_BUS_FMT_UYVY8_1X16, MEDIA_BUS_FMT_UYVY8_1X16,
+ MEDIA_BUS_FMT_UYVY8_1X16, 0,
+ V4L2_PIX_FMT_UYVY, 16, "YUV 4:2:2 (UYVY)", },
+ { MEDIA_BUS_FMT_YUYV8_1X16, MEDIA_BUS_FMT_YUYV8_1X16,
+ MEDIA_BUS_FMT_YUYV8_1X16, 0,
+ V4L2_PIX_FMT_YUYV, 16, "YUV 4:2:2 (YUYV)", },
+ { MEDIA_BUS_FMT_YUYV8_1_5X8, MEDIA_BUS_FMT_YUYV8_1_5X8,
+ MEDIA_BUS_FMT_YUYV8_1_5X8, 0,
+ V4L2_PIX_FMT_NV12, 8, "YUV 4:2:0 (NV12)", },
+};
+
+const struct iss_format_info *
+omap4iss_video_format_info(u32 code)
+{
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(formats); ++i) {
+ if (formats[i].code == code)
+ return &formats[i];
+ }
+
+ return NULL;
+}
+
+/*
+ * iss_video_mbus_to_pix - Convert v4l2_mbus_framefmt to v4l2_pix_format
+ * @video: ISS video instance
+ * @mbus: v4l2_mbus_framefmt format (input)
+ * @pix: v4l2_pix_format format (output)
+ *
+ * Fill the output pix structure with information from the input mbus format.
+ * The bytesperline and sizeimage fields are computed from the requested bytes
+ * per line value in the pix format and information from the video instance.
+ *
+ * Return the number of padding bytes at end of line.
+ */
+static unsigned int iss_video_mbus_to_pix(const struct iss_video *video,
+ const struct v4l2_mbus_framefmt *mbus,
+ struct v4l2_pix_format *pix)
+{
+ unsigned int bpl = pix->bytesperline;
+ unsigned int min_bpl;
+ unsigned int i;
+
+ memset(pix, 0, sizeof(*pix));
+ pix->width = mbus->width;
+ pix->height = mbus->height;
+
+ /* Skip the last format in the loop so that it will be selected if no
+ * match is found.
+ */
+ for (i = 0; i < ARRAY_SIZE(formats) - 1; ++i) {
+ if (formats[i].code == mbus->code)
+ break;
+ }
+
+ min_bpl = pix->width * ALIGN(formats[i].bpp, 8) / 8;
+
+ /* Clamp the requested bytes per line value. If the maximum bytes per
+ * line value is zero, the module doesn't support user configurable line
+ * sizes. Override the requested value with the minimum in that case.
+ */
+ if (video->bpl_max)
+ bpl = clamp(bpl, min_bpl, video->bpl_max);
+ else
+ bpl = min_bpl;
+
+ if (!video->bpl_zero_padding || bpl != min_bpl)
+ bpl = ALIGN(bpl, video->bpl_alignment);
+
+ pix->pixelformat = formats[i].pixelformat;
+ pix->bytesperline = bpl;
+ pix->sizeimage = pix->bytesperline * pix->height;
+ pix->colorspace = mbus->colorspace;
+ pix->field = mbus->field;
+
+ /* FIXME: Special case for NV12! We should make this nicer... */
+ if (pix->pixelformat == V4L2_PIX_FMT_NV12)
+ pix->sizeimage += (pix->bytesperline * pix->height) / 2;
+
+ return bpl - min_bpl;
+}
+
+static void iss_video_pix_to_mbus(const struct v4l2_pix_format *pix,
+ struct v4l2_mbus_framefmt *mbus)
+{
+ unsigned int i;
+
+ memset(mbus, 0, sizeof(*mbus));
+ mbus->width = pix->width;
+ mbus->height = pix->height;
+
+ /* Skip the last format in the loop so that it will be selected if no
+ * match is found.
+ */
+ for (i = 0; i < ARRAY_SIZE(formats) - 1; ++i) {
+ if (formats[i].pixelformat == pix->pixelformat)
+ break;
+ }
+
+ mbus->code = formats[i].code;
+ mbus->colorspace = pix->colorspace;
+ mbus->field = pix->field;
+}
+
+static struct v4l2_subdev *
+iss_video_remote_subdev(struct iss_video *video, u32 *pad)
+{
+ struct media_pad *remote;
+
+ remote = media_entity_remote_pad(&video->pad);
+
+ if (remote == NULL ||
+ media_entity_type(remote->entity) != MEDIA_ENT_T_V4L2_SUBDEV)
+ return NULL;
+
+ if (pad)
+ *pad = remote->index;
+
+ return media_entity_to_v4l2_subdev(remote->entity);
+}
+
+/* Return a pointer to the ISS video instance at the far end of the pipeline. */
+static struct iss_video *
+iss_video_far_end(struct iss_video *video)
+{
+ struct media_entity_graph graph;
+ struct media_entity *entity = &video->video.entity;
+ struct media_device *mdev = entity->parent;
+ struct iss_video *far_end = NULL;
+
+ mutex_lock(&mdev->graph_mutex);
+ media_entity_graph_walk_start(&graph, entity);
+
+ while ((entity = media_entity_graph_walk_next(&graph))) {
+ if (entity == &video->video.entity)
+ continue;
+
+ if (media_entity_type(entity) != MEDIA_ENT_T_DEVNODE)
+ continue;
+
+ far_end = to_iss_video(media_entity_to_video_device(entity));
+ if (far_end->type != video->type)
+ break;
+
+ far_end = NULL;
+ }
+
+ mutex_unlock(&mdev->graph_mutex);
+ return far_end;
+}
+
+static int
+__iss_video_get_format(struct iss_video *video,
+ struct v4l2_mbus_framefmt *format)
+{
+ struct v4l2_subdev_format fmt;
+ struct v4l2_subdev *subdev;
+ u32 pad;
+ int ret;
+
+ subdev = iss_video_remote_subdev(video, &pad);
+ if (subdev == NULL)
+ return -EINVAL;
+
+ memset(&fmt, 0, sizeof(fmt));
+ fmt.pad = pad;
+ fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE;
+
+ mutex_lock(&video->mutex);
+ ret = v4l2_subdev_call(subdev, pad, get_fmt, NULL, &fmt);
+ mutex_unlock(&video->mutex);
+
+ if (ret)
+ return ret;
+
+ *format = fmt.format;
+ return 0;
+}
+
+static int
+iss_video_check_format(struct iss_video *video, struct iss_video_fh *vfh)
+{
+ struct v4l2_mbus_framefmt format;
+ struct v4l2_pix_format pixfmt;
+ int ret;
+
+ ret = __iss_video_get_format(video, &format);
+ if (ret < 0)
+ return ret;
+
+ pixfmt.bytesperline = 0;
+ ret = iss_video_mbus_to_pix(video, &format, &pixfmt);
+
+ if (vfh->format.fmt.pix.pixelformat != pixfmt.pixelformat ||
+ vfh->format.fmt.pix.height != pixfmt.height ||
+ vfh->format.fmt.pix.width != pixfmt.width ||
+ vfh->format.fmt.pix.bytesperline != pixfmt.bytesperline ||
+ vfh->format.fmt.pix.sizeimage != pixfmt.sizeimage)
+ return -EINVAL;
+
+ return ret;
+}
+
+/* -----------------------------------------------------------------------------
+ * Video queue operations
+ */
+
+static int iss_video_queue_setup(struct vb2_queue *vq,
+ const struct v4l2_format *fmt,
+ unsigned int *count, unsigned int *num_planes,
+ unsigned int sizes[], void *alloc_ctxs[])
+{
+ struct iss_video_fh *vfh = vb2_get_drv_priv(vq);
+ struct iss_video *video = vfh->video;
+
+ /* Revisit multi-planar support for NV12 */
+ *num_planes = 1;
+
+ sizes[0] = vfh->format.fmt.pix.sizeimage;
+ if (sizes[0] == 0)
+ return -EINVAL;
+
+ alloc_ctxs[0] = video->alloc_ctx;
+
+ *count = min(*count, video->capture_mem / PAGE_ALIGN(sizes[0]));
+
+ return 0;
+}
+
+static void iss_video_buf_cleanup(struct vb2_buffer *vb)
+{
+ struct iss_buffer *buffer = container_of(vb, struct iss_buffer, vb);
+
+ if (buffer->iss_addr)
+ buffer->iss_addr = 0;
+}
+
+static int iss_video_buf_prepare(struct vb2_buffer *vb)
+{
+ struct iss_video_fh *vfh = vb2_get_drv_priv(vb->vb2_queue);
+ struct iss_buffer *buffer = container_of(vb, struct iss_buffer, vb);
+ struct iss_video *video = vfh->video;
+ unsigned long size = vfh->format.fmt.pix.sizeimage;
+ dma_addr_t addr;
+
+ if (vb2_plane_size(vb, 0) < size)
+ return -ENOBUFS;
+
+ addr = vb2_dma_contig_plane_dma_addr(vb, 0);
+ if (!IS_ALIGNED(addr, 32)) {
+ dev_dbg(video->iss->dev,
+ "Buffer address must be aligned to 32 bytes boundary.\n");
+ return -EINVAL;
+ }
+
+ vb2_set_plane_payload(vb, 0, size);
+ buffer->iss_addr = addr;
+ return 0;
+}
+
+static void iss_video_buf_queue(struct vb2_buffer *vb)
+{
+ struct iss_video_fh *vfh = vb2_get_drv_priv(vb->vb2_queue);
+ struct iss_video *video = vfh->video;
+ struct iss_buffer *buffer = container_of(vb, struct iss_buffer, vb);
+ struct iss_pipeline *pipe = to_iss_pipeline(&video->video.entity);
+ unsigned long flags;
+ bool empty;
+
+ spin_lock_irqsave(&video->qlock, flags);
+
+ /* Mark the buffer is faulty and give it back to the queue immediately
+ * if the video node has registered an error. vb2 will perform the same
+ * check when preparing the buffer, but that is inherently racy, so we
+ * need to handle the race condition with an authoritative check here.
+ */
+ if (unlikely(video->error)) {
+ vb2_buffer_done(vb, VB2_BUF_STATE_ERROR);
+ spin_unlock_irqrestore(&video->qlock, flags);
+ return;
+ }
+
+ empty = list_empty(&video->dmaqueue);
+ list_add_tail(&buffer->list, &video->dmaqueue);
+
+ spin_unlock_irqrestore(&video->qlock, flags);
+
+ if (empty) {
+ enum iss_pipeline_state state;
+ unsigned int start;
+
+ if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ state = ISS_PIPELINE_QUEUE_OUTPUT;
+ else
+ state = ISS_PIPELINE_QUEUE_INPUT;
+
+ spin_lock_irqsave(&pipe->lock, flags);
+ pipe->state |= state;
+ video->ops->queue(video, buffer);
+ video->dmaqueue_flags |= ISS_VIDEO_DMAQUEUE_QUEUED;
+
+ start = iss_pipeline_ready(pipe);
+ if (start)
+ pipe->state |= ISS_PIPELINE_STREAM;
+ spin_unlock_irqrestore(&pipe->lock, flags);
+
+ if (start)
+ omap4iss_pipeline_set_stream(pipe,
+ ISS_PIPELINE_STREAM_SINGLESHOT);
+ }
+}
+
+static const struct vb2_ops iss_video_vb2ops = {
+ .queue_setup = iss_video_queue_setup,
+ .buf_prepare = iss_video_buf_prepare,
+ .buf_queue = iss_video_buf_queue,
+ .buf_cleanup = iss_video_buf_cleanup,
+};
+
+/*
+ * omap4iss_video_buffer_next - Complete the current buffer and return the next
+ * @video: ISS video object
+ *
+ * Remove the current video buffer from the DMA queue and fill its timestamp,
+ * field count and state fields before waking up its completion handler.
+ *
+ * For capture video nodes, the buffer state is set to VB2_BUF_STATE_DONE if no
+ * error has been flagged in the pipeline, or to VB2_BUF_STATE_ERROR otherwise.
+ *
+ * The DMA queue is expected to contain at least one buffer.
+ *
+ * Return a pointer to the next buffer in the DMA queue, or NULL if the queue is
+ * empty.
+ */
+struct iss_buffer *omap4iss_video_buffer_next(struct iss_video *video)
+{
+ struct iss_pipeline *pipe = to_iss_pipeline(&video->video.entity);
+ enum iss_pipeline_state state;
+ struct iss_buffer *buf;
+ unsigned long flags;
+ struct timespec ts;
+
+ spin_lock_irqsave(&video->qlock, flags);
+ if (WARN_ON(list_empty(&video->dmaqueue))) {
+ spin_unlock_irqrestore(&video->qlock, flags);
+ return NULL;
+ }
+
+ buf = list_first_entry(&video->dmaqueue, struct iss_buffer,
+ list);
+ list_del(&buf->list);
+ spin_unlock_irqrestore(&video->qlock, flags);
+
+ ktime_get_ts(&ts);
+ buf->vb.v4l2_buf.timestamp.tv_sec = ts.tv_sec;
+ buf->vb.v4l2_buf.timestamp.tv_usec = ts.tv_nsec / NSEC_PER_USEC;
+
+ /* Do frame number propagation only if this is the output video node.
+ * Frame number either comes from the CSI receivers or it gets
+ * incremented here if H3A is not active.
+ * Note: There is no guarantee that the output buffer will finish
+ * first, so the input number might lag behind by 1 in some cases.
+ */
+ if (video == pipe->output && !pipe->do_propagation)
+ buf->vb.v4l2_buf.sequence =
+ atomic_inc_return(&pipe->frame_number);
+ else
+ buf->vb.v4l2_buf.sequence = atomic_read(&pipe->frame_number);
+
+ vb2_buffer_done(&buf->vb, pipe->error ?
+ VB2_BUF_STATE_ERROR : VB2_BUF_STATE_DONE);
+ pipe->error = false;
+
+ spin_lock_irqsave(&video->qlock, flags);
+ if (list_empty(&video->dmaqueue)) {
+ spin_unlock_irqrestore(&video->qlock, flags);
+ if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ state = ISS_PIPELINE_QUEUE_OUTPUT
+ | ISS_PIPELINE_STREAM;
+ else
+ state = ISS_PIPELINE_QUEUE_INPUT
+ | ISS_PIPELINE_STREAM;
+
+ spin_lock_irqsave(&pipe->lock, flags);
+ pipe->state &= ~state;
+ if (video->pipe.stream_state == ISS_PIPELINE_STREAM_CONTINUOUS)
+ video->dmaqueue_flags |= ISS_VIDEO_DMAQUEUE_UNDERRUN;
+ spin_unlock_irqrestore(&pipe->lock, flags);
+ return NULL;
+ }
+
+ if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE && pipe->input != NULL) {
+ spin_lock(&pipe->lock);
+ pipe->state &= ~ISS_PIPELINE_STREAM;
+ spin_unlock(&pipe->lock);
+ }
+
+ buf = list_first_entry(&video->dmaqueue, struct iss_buffer,
+ list);
+ spin_unlock_irqrestore(&video->qlock, flags);
+ buf->vb.state = VB2_BUF_STATE_ACTIVE;
+ return buf;
+}
+
+/*
+ * omap4iss_video_cancel_stream - Cancel stream on a video node
+ * @video: ISS video object
+ *
+ * Cancelling a stream mark all buffers on the video node as erroneous and makes
+ * sure no new buffer can be queued.
+ */
+void omap4iss_video_cancel_stream(struct iss_video *video)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&video->qlock, flags);
+
+ while (!list_empty(&video->dmaqueue)) {
+ struct iss_buffer *buf;
+
+ buf = list_first_entry(&video->dmaqueue, struct iss_buffer,
+ list);
+ list_del(&buf->list);
+ vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR);
+ }
+
+ vb2_queue_error(video->queue);
+ video->error = true;
+
+ spin_unlock_irqrestore(&video->qlock, flags);
+}
+
+/* -----------------------------------------------------------------------------
+ * V4L2 ioctls
+ */
+
+static int
+iss_video_querycap(struct file *file, void *fh, struct v4l2_capability *cap)
+{
+ struct iss_video *video = video_drvdata(file);
+
+ strlcpy(cap->driver, ISS_VIDEO_DRIVER_NAME, sizeof(cap->driver));
+ strlcpy(cap->card, video->video.name, sizeof(cap->card));
+ strlcpy(cap->bus_info, "media", sizeof(cap->bus_info));
+
+ if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;
+ else
+ cap->device_caps = V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_STREAMING;
+
+ cap->capabilities = V4L2_CAP_DEVICE_CAPS | V4L2_CAP_STREAMING
+ | V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_VIDEO_OUTPUT;
+
+ return 0;
+}
+
+static int
+iss_video_enum_format(struct file *file, void *fh, struct v4l2_fmtdesc *f)
+{
+ struct iss_video *video = video_drvdata(file);
+ struct v4l2_mbus_framefmt format;
+ unsigned int index = f->index;
+ unsigned int i;
+ int ret;
+
+ if (f->type != video->type)
+ return -EINVAL;
+
+ ret = __iss_video_get_format(video, &format);
+ if (ret < 0)
+ return ret;
+
+ for (i = 0; i < ARRAY_SIZE(formats); ++i) {
+ const struct iss_format_info *info = &formats[i];
+
+ if (format.code != info->code)
+ continue;
+
+ if (index == 0) {
+ f->pixelformat = info->pixelformat;
+ strlcpy(f->description, info->description,
+ sizeof(f->description));
+ return 0;
+ }
+
+ index--;
+ }
+
+ return -EINVAL;
+}
+
+static int
+iss_video_get_format(struct file *file, void *fh, struct v4l2_format *format)
+{
+ struct iss_video_fh *vfh = to_iss_video_fh(fh);
+ struct iss_video *video = video_drvdata(file);
+
+ if (format->type != video->type)
+ return -EINVAL;
+
+ mutex_lock(&video->mutex);
+ *format = vfh->format;
+ mutex_unlock(&video->mutex);
+
+ return 0;
+}
+
+static int
+iss_video_set_format(struct file *file, void *fh, struct v4l2_format *format)
+{
+ struct iss_video_fh *vfh = to_iss_video_fh(fh);
+ struct iss_video *video = video_drvdata(file);
+ struct v4l2_mbus_framefmt fmt;
+
+ if (format->type != video->type)
+ return -EINVAL;
+
+ mutex_lock(&video->mutex);
+
+ /* Fill the bytesperline and sizeimage fields by converting to media bus
+ * format and back to pixel format.
+ */
+ iss_video_pix_to_mbus(&format->fmt.pix, &fmt);
+ iss_video_mbus_to_pix(video, &fmt, &format->fmt.pix);
+
+ vfh->format = *format;
+
+ mutex_unlock(&video->mutex);
+ return 0;
+}
+
+static int
+iss_video_try_format(struct file *file, void *fh, struct v4l2_format *format)
+{
+ struct iss_video *video = video_drvdata(file);
+ struct v4l2_subdev_format fmt;
+ struct v4l2_subdev *subdev;
+ u32 pad;
+ int ret;
+
+ if (format->type != video->type)
+ return -EINVAL;
+
+ subdev = iss_video_remote_subdev(video, &pad);
+ if (subdev == NULL)
+ return -EINVAL;
+
+ iss_video_pix_to_mbus(&format->fmt.pix, &fmt.format);
+
+ fmt.pad = pad;
+ fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE;
+ ret = v4l2_subdev_call(subdev, pad, get_fmt, NULL, &fmt);
+ if (ret)
+ return ret;
+
+ iss_video_mbus_to_pix(video, &fmt.format, &format->fmt.pix);
+ return 0;
+}
+
+static int
+iss_video_cropcap(struct file *file, void *fh, struct v4l2_cropcap *cropcap)
+{
+ struct iss_video *video = video_drvdata(file);
+ struct v4l2_subdev *subdev;
+ int ret;
+
+ subdev = iss_video_remote_subdev(video, NULL);
+ if (subdev == NULL)
+ return -EINVAL;
+
+ mutex_lock(&video->mutex);
+ ret = v4l2_subdev_call(subdev, video, cropcap, cropcap);
+ mutex_unlock(&video->mutex);
+
+ return ret == -ENOIOCTLCMD ? -ENOTTY : ret;
+}
+
+static int
+iss_video_get_crop(struct file *file, void *fh, struct v4l2_crop *crop)
+{
+ struct iss_video *video = video_drvdata(file);
+ struct v4l2_subdev_format format;
+ struct v4l2_subdev *subdev;
+ u32 pad;
+ int ret;
+
+ subdev = iss_video_remote_subdev(video, &pad);
+ if (subdev == NULL)
+ return -EINVAL;
+
+ /* Try the get crop operation first and fallback to get format if not
+ * implemented.
+ */
+ ret = v4l2_subdev_call(subdev, video, g_crop, crop);
+ if (ret != -ENOIOCTLCMD)
+ return ret;
+
+ format.pad = pad;
+ format.which = V4L2_SUBDEV_FORMAT_ACTIVE;
+ ret = v4l2_subdev_call(subdev, pad, get_fmt, NULL, &format);
+ if (ret < 0)
+ return ret == -ENOIOCTLCMD ? -ENOTTY : ret;
+
+ crop->c.left = 0;
+ crop->c.top = 0;
+ crop->c.width = format.format.width;
+ crop->c.height = format.format.height;
+
+ return 0;
+}
+
+static int
+iss_video_set_crop(struct file *file, void *fh, const struct v4l2_crop *crop)
+{
+ struct iss_video *video = video_drvdata(file);
+ struct v4l2_subdev *subdev;
+ int ret;
+
+ subdev = iss_video_remote_subdev(video, NULL);
+ if (subdev == NULL)
+ return -EINVAL;
+
+ mutex_lock(&video->mutex);
+ ret = v4l2_subdev_call(subdev, video, s_crop, crop);
+ mutex_unlock(&video->mutex);
+
+ return ret == -ENOIOCTLCMD ? -ENOTTY : ret;
+}
+
+static int
+iss_video_get_param(struct file *file, void *fh, struct v4l2_streamparm *a)
+{
+ struct iss_video_fh *vfh = to_iss_video_fh(fh);
+ struct iss_video *video = video_drvdata(file);
+
+ if (video->type != V4L2_BUF_TYPE_VIDEO_OUTPUT ||
+ video->type != a->type)
+ return -EINVAL;
+
+ memset(a, 0, sizeof(*a));
+ a->type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
+ a->parm.output.capability = V4L2_CAP_TIMEPERFRAME;
+ a->parm.output.timeperframe = vfh->timeperframe;
+
+ return 0;
+}
+
+static int
+iss_video_set_param(struct file *file, void *fh, struct v4l2_streamparm *a)
+{
+ struct iss_video_fh *vfh = to_iss_video_fh(fh);
+ struct iss_video *video = video_drvdata(file);
+
+ if (video->type != V4L2_BUF_TYPE_VIDEO_OUTPUT ||
+ video->type != a->type)
+ return -EINVAL;
+
+ if (a->parm.output.timeperframe.denominator == 0)
+ a->parm.output.timeperframe.denominator = 1;
+
+ vfh->timeperframe = a->parm.output.timeperframe;
+
+ return 0;
+}
+
+static int
+iss_video_reqbufs(struct file *file, void *fh, struct v4l2_requestbuffers *rb)
+{
+ struct iss_video_fh *vfh = to_iss_video_fh(fh);
+
+ return vb2_reqbufs(&vfh->queue, rb);
+}
+
+static int
+iss_video_querybuf(struct file *file, void *fh, struct v4l2_buffer *b)
+{
+ struct iss_video_fh *vfh = to_iss_video_fh(fh);
+
+ return vb2_querybuf(&vfh->queue, b);
+}
+
+static int
+iss_video_qbuf(struct file *file, void *fh, struct v4l2_buffer *b)
+{
+ struct iss_video_fh *vfh = to_iss_video_fh(fh);
+
+ return vb2_qbuf(&vfh->queue, b);
+}
+
+static int
+iss_video_expbuf(struct file *file, void *fh, struct v4l2_exportbuffer *e)
+{
+ struct iss_video_fh *vfh = to_iss_video_fh(fh);
+
+ return vb2_expbuf(&vfh->queue, e);
+}
+
+static int
+iss_video_dqbuf(struct file *file, void *fh, struct v4l2_buffer *b)
+{
+ struct iss_video_fh *vfh = to_iss_video_fh(fh);
+
+ return vb2_dqbuf(&vfh->queue, b, file->f_flags & O_NONBLOCK);
+}
+
+/*
+ * Stream management
+ *
+ * Every ISS pipeline has a single input and a single output. The input can be
+ * either a sensor or a video node. The output is always a video node.
+ *
+ * As every pipeline has an output video node, the ISS video objects at the
+ * pipeline output stores the pipeline state. It tracks the streaming state of
+ * both the input and output, as well as the availability of buffers.
+ *
+ * In sensor-to-memory mode, frames are always available at the pipeline input.
+ * Starting the sensor usually requires I2C transfers and must be done in
+ * interruptible context. The pipeline is started and stopped synchronously
+ * to the stream on/off commands. All modules in the pipeline will get their
+ * subdev set stream handler called. The module at the end of the pipeline must
+ * delay starting the hardware until buffers are available at its output.
+ *
+ * In memory-to-memory mode, starting/stopping the stream requires
+ * synchronization between the input and output. ISS modules can't be stopped
+ * in the middle of a frame, and at least some of the modules seem to become
+ * busy as soon as they're started, even if they don't receive a frame start
+ * event. For that reason frames need to be processed in single-shot mode. The
+ * driver needs to wait until a frame is completely processed and written to
+ * memory before restarting the pipeline for the next frame. Pipelined
+ * processing might be possible but requires more testing.
+ *
+ * Stream start must be delayed until buffers are available at both the input
+ * and output. The pipeline must be started in the videobuf queue callback with
+ * the buffers queue spinlock held. The modules subdev set stream operation must
+ * not sleep.
+ */
+static int
+iss_video_streamon(struct file *file, void *fh, enum v4l2_buf_type type)
+{
+ struct iss_video_fh *vfh = to_iss_video_fh(fh);
+ struct iss_video *video = video_drvdata(file);
+ struct media_entity_graph graph;
+ struct media_entity *entity;
+ enum iss_pipeline_state state;
+ struct iss_pipeline *pipe;
+ struct iss_video *far_end;
+ unsigned long flags;
+ int ret;
+
+ if (type != video->type)
+ return -EINVAL;
+
+ mutex_lock(&video->stream_lock);
+
+ /* Start streaming on the pipeline. No link touching an entity in the
+ * pipeline can be activated or deactivated once streaming is started.
+ */
+ pipe = video->video.entity.pipe
+ ? to_iss_pipeline(&video->video.entity) : &video->pipe;
+ pipe->external = NULL;
+ pipe->external_rate = 0;
+ pipe->external_bpp = 0;
+ pipe->entities = 0;
+
+ if (video->iss->pdata->set_constraints)
+ video->iss->pdata->set_constraints(video->iss, true);
+
+ ret = media_entity_pipeline_start(&video->video.entity, &pipe->pipe);
+ if (ret < 0)
+ goto err_media_entity_pipeline_start;
+
+ entity = &video->video.entity;
+ media_entity_graph_walk_start(&graph, entity);
+ while ((entity = media_entity_graph_walk_next(&graph)))
+ pipe->entities |= 1 << entity->id;
+
+ /* Verify that the currently configured format matches the output of
+ * the connected subdev.
+ */
+ ret = iss_video_check_format(video, vfh);
+ if (ret < 0)
+ goto err_iss_video_check_format;
+
+ video->bpl_padding = ret;
+ video->bpl_value = vfh->format.fmt.pix.bytesperline;
+
+ /* Find the ISS video node connected at the far end of the pipeline and
+ * update the pipeline.
+ */
+ far_end = iss_video_far_end(video);
+
+ if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
+ state = ISS_PIPELINE_STREAM_OUTPUT | ISS_PIPELINE_IDLE_OUTPUT;
+ pipe->input = far_end;
+ pipe->output = video;
+ } else {
+ if (far_end == NULL) {
+ ret = -EPIPE;
+ goto err_iss_video_check_format;
+ }
+
+ state = ISS_PIPELINE_STREAM_INPUT | ISS_PIPELINE_IDLE_INPUT;
+ pipe->input = video;
+ pipe->output = far_end;
+ }
+
+ spin_lock_irqsave(&pipe->lock, flags);
+ pipe->state &= ~ISS_PIPELINE_STREAM;
+ pipe->state |= state;
+ spin_unlock_irqrestore(&pipe->lock, flags);
+
+ /* Set the maximum time per frame as the value requested by userspace.
+ * This is a soft limit that can be overridden if the hardware doesn't
+ * support the request limit.
+ */
+ if (video->type == V4L2_BUF_TYPE_VIDEO_OUTPUT)
+ pipe->max_timeperframe = vfh->timeperframe;
+
+ video->queue = &vfh->queue;
+ INIT_LIST_HEAD(&video->dmaqueue);
+ video->error = false;
+ atomic_set(&pipe->frame_number, -1);
+
+ ret = vb2_streamon(&vfh->queue, type);
+ if (ret < 0)
+ goto err_iss_video_check_format;
+
+ /* In sensor-to-memory mode, the stream can be started synchronously
+ * to the stream on command. In memory-to-memory mode, it will be
+ * started when buffers are queued on both the input and output.
+ */
+ if (pipe->input == NULL) {
+ unsigned long flags;
+
+ ret = omap4iss_pipeline_set_stream(pipe,
+ ISS_PIPELINE_STREAM_CONTINUOUS);
+ if (ret < 0)
+ goto err_omap4iss_set_stream;
+ spin_lock_irqsave(&video->qlock, flags);
+ if (list_empty(&video->dmaqueue))
+ video->dmaqueue_flags |= ISS_VIDEO_DMAQUEUE_UNDERRUN;
+ spin_unlock_irqrestore(&video->qlock, flags);
+ }
+
+ mutex_unlock(&video->stream_lock);
+ return 0;
+
+err_omap4iss_set_stream:
+ vb2_streamoff(&vfh->queue, type);
+err_iss_video_check_format:
+ media_entity_pipeline_stop(&video->video.entity);
+err_media_entity_pipeline_start:
+ if (video->iss->pdata->set_constraints)
+ video->iss->pdata->set_constraints(video->iss, false);
+ video->queue = NULL;
+
+ mutex_unlock(&video->stream_lock);
+ return ret;
+}
+
+static int
+iss_video_streamoff(struct file *file, void *fh, enum v4l2_buf_type type)
+{
+ struct iss_video_fh *vfh = to_iss_video_fh(fh);
+ struct iss_video *video = video_drvdata(file);
+ struct iss_pipeline *pipe = to_iss_pipeline(&video->video.entity);
+ enum iss_pipeline_state state;
+ unsigned long flags;
+
+ if (type != video->type)
+ return -EINVAL;
+
+ mutex_lock(&video->stream_lock);
+
+ if (!vb2_is_streaming(&vfh->queue))
+ goto done;
+
+ /* Update the pipeline state. */
+ if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ state = ISS_PIPELINE_STREAM_OUTPUT
+ | ISS_PIPELINE_QUEUE_OUTPUT;
+ else
+ state = ISS_PIPELINE_STREAM_INPUT
+ | ISS_PIPELINE_QUEUE_INPUT;
+
+ spin_lock_irqsave(&pipe->lock, flags);
+ pipe->state &= ~state;
+ spin_unlock_irqrestore(&pipe->lock, flags);
+
+ /* Stop the stream. */
+ omap4iss_pipeline_set_stream(pipe, ISS_PIPELINE_STREAM_STOPPED);
+ vb2_streamoff(&vfh->queue, type);
+ video->queue = NULL;
+
+ if (video->iss->pdata->set_constraints)
+ video->iss->pdata->set_constraints(video->iss, false);
+ media_entity_pipeline_stop(&video->video.entity);
+
+done:
+ mutex_unlock(&video->stream_lock);
+ return 0;
+}
+
+static int
+iss_video_enum_input(struct file *file, void *fh, struct v4l2_input *input)
+{
+ if (input->index > 0)
+ return -EINVAL;
+
+ strlcpy(input->name, "camera", sizeof(input->name));
+ input->type = V4L2_INPUT_TYPE_CAMERA;
+
+ return 0;
+}
+
+static int
+iss_video_g_input(struct file *file, void *fh, unsigned int *input)
+{
+ *input = 0;
+
+ return 0;
+}
+
+static int
+iss_video_s_input(struct file *file, void *fh, unsigned int input)
+{
+ return input == 0 ? 0 : -EINVAL;
+}
+
+static const struct v4l2_ioctl_ops iss_video_ioctl_ops = {
+ .vidioc_querycap = iss_video_querycap,
+ .vidioc_enum_fmt_vid_cap = iss_video_enum_format,
+ .vidioc_g_fmt_vid_cap = iss_video_get_format,
+ .vidioc_s_fmt_vid_cap = iss_video_set_format,
+ .vidioc_try_fmt_vid_cap = iss_video_try_format,
+ .vidioc_g_fmt_vid_out = iss_video_get_format,
+ .vidioc_s_fmt_vid_out = iss_video_set_format,
+ .vidioc_try_fmt_vid_out = iss_video_try_format,
+ .vidioc_cropcap = iss_video_cropcap,
+ .vidioc_g_crop = iss_video_get_crop,
+ .vidioc_s_crop = iss_video_set_crop,
+ .vidioc_g_parm = iss_video_get_param,
+ .vidioc_s_parm = iss_video_set_param,
+ .vidioc_reqbufs = iss_video_reqbufs,
+ .vidioc_querybuf = iss_video_querybuf,
+ .vidioc_qbuf = iss_video_qbuf,
+ .vidioc_expbuf = iss_video_expbuf,
+ .vidioc_dqbuf = iss_video_dqbuf,
+ .vidioc_streamon = iss_video_streamon,
+ .vidioc_streamoff = iss_video_streamoff,
+ .vidioc_enum_input = iss_video_enum_input,
+ .vidioc_g_input = iss_video_g_input,
+ .vidioc_s_input = iss_video_s_input,
+};
+
+/* -----------------------------------------------------------------------------
+ * V4L2 file operations
+ */
+
+static int iss_video_open(struct file *file)
+{
+ struct iss_video *video = video_drvdata(file);
+ struct iss_video_fh *handle;
+ struct vb2_queue *q;
+ int ret = 0;
+
+ handle = kzalloc(sizeof(*handle), GFP_KERNEL);
+ if (handle == NULL)
+ return -ENOMEM;
+
+ v4l2_fh_init(&handle->vfh, &video->video);
+ v4l2_fh_add(&handle->vfh);
+
+ /* If this is the first user, initialise the pipeline. */
+ if (omap4iss_get(video->iss) == NULL) {
+ ret = -EBUSY;
+ goto done;
+ }
+
+ ret = omap4iss_pipeline_pm_use(&video->video.entity, 1);
+ if (ret < 0) {
+ omap4iss_put(video->iss);
+ goto done;
+ }
+
+ video->alloc_ctx = vb2_dma_contig_init_ctx(video->iss->dev);
+ if (IS_ERR(video->alloc_ctx)) {
+ ret = PTR_ERR(video->alloc_ctx);
+ omap4iss_put(video->iss);
+ goto done;
+ }
+
+ q = &handle->queue;
+
+ q->type = video->type;
+ q->io_modes = VB2_MMAP | VB2_DMABUF;
+ q->drv_priv = handle;
+ q->ops = &iss_video_vb2ops;
+ q->mem_ops = &vb2_dma_contig_memops;
+ q->buf_struct_size = sizeof(struct iss_buffer);
+ q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
+
+ ret = vb2_queue_init(q);
+ if (ret) {
+ omap4iss_put(video->iss);
+ goto done;
+ }
+
+ memset(&handle->format, 0, sizeof(handle->format));
+ handle->format.type = video->type;
+ handle->timeperframe.denominator = 1;
+
+ handle->video = video;
+ file->private_data = &handle->vfh;
+
+done:
+ if (ret < 0) {
+ v4l2_fh_del(&handle->vfh);
+ kfree(handle);
+ }
+
+ return ret;
+}
+
+static int iss_video_release(struct file *file)
+{
+ struct iss_video *video = video_drvdata(file);
+ struct v4l2_fh *vfh = file->private_data;
+ struct iss_video_fh *handle = to_iss_video_fh(vfh);
+
+ /* Disable streaming and free the buffers queue resources. */
+ iss_video_streamoff(file, vfh, video->type);
+
+ omap4iss_pipeline_pm_use(&video->video.entity, 0);
+
+ /* Release the videobuf2 queue */
+ vb2_queue_release(&handle->queue);
+
+ /* Release the file handle. */
+ v4l2_fh_del(vfh);
+ kfree(handle);
+ file->private_data = NULL;
+
+ omap4iss_put(video->iss);
+
+ return 0;
+}
+
+static unsigned int iss_video_poll(struct file *file, poll_table *wait)
+{
+ struct iss_video_fh *vfh = to_iss_video_fh(file->private_data);
+
+ return vb2_poll(&vfh->queue, file, wait);
+}
+
+static int iss_video_mmap(struct file *file, struct vm_area_struct *vma)
+{
+ struct iss_video_fh *vfh = to_iss_video_fh(file->private_data);
+
+ return vb2_mmap(&vfh->queue, vma);
+}
+
+static struct v4l2_file_operations iss_video_fops = {
+ .owner = THIS_MODULE,
+ .unlocked_ioctl = video_ioctl2,
+ .open = iss_video_open,
+ .release = iss_video_release,
+ .poll = iss_video_poll,
+ .mmap = iss_video_mmap,
+};
+
+/* -----------------------------------------------------------------------------
+ * ISS video core
+ */
+
+static const struct iss_video_operations iss_video_dummy_ops = {
+};
+
+int omap4iss_video_init(struct iss_video *video, const char *name)
+{
+ const char *direction;
+ int ret;
+
+ switch (video->type) {
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+ direction = "output";
+ video->pad.flags = MEDIA_PAD_FL_SINK;
+ break;
+ case V4L2_BUF_TYPE_VIDEO_OUTPUT:
+ direction = "input";
+ video->pad.flags = MEDIA_PAD_FL_SOURCE;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ ret = media_entity_init(&video->video.entity, 1, &video->pad, 0);
+ if (ret < 0)
+ return ret;
+
+ spin_lock_init(&video->qlock);
+ mutex_init(&video->mutex);
+ atomic_set(&video->active, 0);
+
+ spin_lock_init(&video->pipe.lock);
+ mutex_init(&video->stream_lock);
+
+ /* Initialize the video device. */
+ if (video->ops == NULL)
+ video->ops = &iss_video_dummy_ops;
+
+ video->video.fops = &iss_video_fops;
+ snprintf(video->video.name, sizeof(video->video.name),
+ "OMAP4 ISS %s %s", name, direction);
+ video->video.vfl_type = VFL_TYPE_GRABBER;
+ video->video.release = video_device_release_empty;
+ video->video.ioctl_ops = &iss_video_ioctl_ops;
+ video->pipe.stream_state = ISS_PIPELINE_STREAM_STOPPED;
+
+ video_set_drvdata(&video->video, video);
+
+ return 0;
+}
+
+void omap4iss_video_cleanup(struct iss_video *video)
+{
+ media_entity_cleanup(&video->video.entity);
+ mutex_destroy(&video->stream_lock);
+ mutex_destroy(&video->mutex);
+}
+
+int omap4iss_video_register(struct iss_video *video, struct v4l2_device *vdev)
+{
+ int ret;
+
+ video->video.v4l2_dev = vdev;
+
+ ret = video_register_device(&video->video, VFL_TYPE_GRABBER, -1);
+ if (ret < 0)
+ dev_err(video->iss->dev,
+ "could not register video device (%d)\n", ret);
+
+ return ret;
+}
+
+void omap4iss_video_unregister(struct iss_video *video)
+{
+ video_unregister_device(&video->video);
+}
diff --git a/drivers/staging/media/omap4iss/iss_video.h b/drivers/staging/media/omap4iss/iss_video.h
new file mode 100644
index 000000000..f11fce2cb
--- /dev/null
+++ b/drivers/staging/media/omap4iss/iss_video.h
@@ -0,0 +1,204 @@
+/*
+ * TI OMAP4 ISS V4L2 Driver - Generic video node
+ *
+ * Copyright (C) 2012 Texas Instruments, Inc.
+ *
+ * Author: Sergio Aguirre <sergio.a.aguirre@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef OMAP4_ISS_VIDEO_H
+#define OMAP4_ISS_VIDEO_H
+
+#include <linux/v4l2-mediabus.h>
+#include <media/media-entity.h>
+#include <media/v4l2-dev.h>
+#include <media/v4l2-fh.h>
+#include <media/videobuf2-core.h>
+#include <media/videobuf2-dma-contig.h>
+
+#define ISS_VIDEO_DRIVER_NAME "issvideo"
+#define ISS_VIDEO_DRIVER_VERSION "0.0.2"
+
+struct iss_device;
+struct iss_video;
+struct v4l2_mbus_framefmt;
+struct v4l2_pix_format;
+
+/*
+ * struct iss_format_info - ISS media bus format information
+ * @code: V4L2 media bus format code
+ * @truncated: V4L2 media bus format code for the same format truncated to 10
+ * bits. Identical to @code if the format is 10 bits wide or less.
+ * @uncompressed: V4L2 media bus format code for the corresponding uncompressed
+ * format. Identical to @code if the format is not DPCM compressed.
+ * @flavor: V4L2 media bus format code for the same pixel layout but
+ * shifted to be 8 bits per pixel. =0 if format is not shiftable.
+ * @pixelformat: V4L2 pixel format FCC identifier
+ * @bpp: Bits per pixel
+ * @description: Human-readable format description
+ */
+struct iss_format_info {
+ u32 code;
+ u32 truncated;
+ u32 uncompressed;
+ u32 flavor;
+ u32 pixelformat;
+ unsigned int bpp;
+ const char *description;
+};
+
+enum iss_pipeline_stream_state {
+ ISS_PIPELINE_STREAM_STOPPED = 0,
+ ISS_PIPELINE_STREAM_CONTINUOUS = 1,
+ ISS_PIPELINE_STREAM_SINGLESHOT = 2,
+};
+
+enum iss_pipeline_state {
+ /* The stream has been started on the input video node. */
+ ISS_PIPELINE_STREAM_INPUT = 1,
+ /* The stream has been started on the output video node. */
+ ISS_PIPELINE_STREAM_OUTPUT = (1 << 1),
+ /* At least one buffer is queued on the input video node. */
+ ISS_PIPELINE_QUEUE_INPUT = (1 << 2),
+ /* At least one buffer is queued on the output video node. */
+ ISS_PIPELINE_QUEUE_OUTPUT = (1 << 3),
+ /* The input entity is idle, ready to be started. */
+ ISS_PIPELINE_IDLE_INPUT = (1 << 4),
+ /* The output entity is idle, ready to be started. */
+ ISS_PIPELINE_IDLE_OUTPUT = (1 << 5),
+ /* The pipeline is currently streaming. */
+ ISS_PIPELINE_STREAM = (1 << 6),
+};
+
+/*
+ * struct iss_pipeline - An OMAP4 ISS hardware pipeline
+ * @entities: Bitmask of entities in the pipeline (indexed by entity ID)
+ * @error: A hardware error occurred during capture
+ */
+struct iss_pipeline {
+ struct media_pipeline pipe;
+ spinlock_t lock; /* Pipeline state and queue flags */
+ unsigned int state;
+ enum iss_pipeline_stream_state stream_state;
+ struct iss_video *input;
+ struct iss_video *output;
+ unsigned int entities;
+ atomic_t frame_number;
+ bool do_propagation; /* of frame number */
+ bool error;
+ struct v4l2_fract max_timeperframe;
+ struct v4l2_subdev *external;
+ unsigned int external_rate;
+ int external_bpp;
+};
+
+#define to_iss_pipeline(__e) \
+ container_of((__e)->pipe, struct iss_pipeline, pipe)
+
+static inline int iss_pipeline_ready(struct iss_pipeline *pipe)
+{
+ return pipe->state == (ISS_PIPELINE_STREAM_INPUT |
+ ISS_PIPELINE_STREAM_OUTPUT |
+ ISS_PIPELINE_QUEUE_INPUT |
+ ISS_PIPELINE_QUEUE_OUTPUT |
+ ISS_PIPELINE_IDLE_INPUT |
+ ISS_PIPELINE_IDLE_OUTPUT);
+}
+
+/*
+ * struct iss_buffer - ISS buffer
+ * @buffer: ISS video buffer
+ * @iss_addr: Physical address of the buffer.
+ */
+struct iss_buffer {
+ /* common v4l buffer stuff -- must be first */
+ struct vb2_buffer vb;
+ struct list_head list;
+ dma_addr_t iss_addr;
+};
+
+#define to_iss_buffer(buf) container_of(buf, struct iss_buffer, buffer)
+
+enum iss_video_dmaqueue_flags {
+ /* Set if DMA queue becomes empty when ISS_PIPELINE_STREAM_CONTINUOUS */
+ ISS_VIDEO_DMAQUEUE_UNDERRUN = (1 << 0),
+ /* Set when queuing buffer to an empty DMA queue */
+ ISS_VIDEO_DMAQUEUE_QUEUED = (1 << 1),
+};
+
+#define iss_video_dmaqueue_flags_clr(video) \
+ ({ (video)->dmaqueue_flags = 0; })
+
+/*
+ * struct iss_video_operations - ISS video operations
+ * @queue: Resume streaming when a buffer is queued. Called on VIDIOC_QBUF
+ * if there was no buffer previously queued.
+ */
+struct iss_video_operations {
+ int (*queue)(struct iss_video *video, struct iss_buffer *buffer);
+};
+
+struct iss_video {
+ struct video_device video;
+ enum v4l2_buf_type type;
+ struct media_pad pad;
+
+ struct mutex mutex; /* format and crop settings */
+ atomic_t active;
+
+ struct iss_device *iss;
+
+ unsigned int capture_mem;
+ unsigned int bpl_alignment; /* alignment value */
+ unsigned int bpl_zero_padding; /* whether the alignment is optional */
+ unsigned int bpl_max; /* maximum bytes per line value */
+ unsigned int bpl_value; /* bytes per line value */
+ unsigned int bpl_padding; /* padding at end of line */
+
+ /* Pipeline state */
+ struct iss_pipeline pipe;
+ struct mutex stream_lock; /* pipeline and stream states */
+ bool error;
+
+ /* Video buffers queue */
+ struct vb2_queue *queue;
+ spinlock_t qlock; /* protects dmaqueue and error */
+ struct list_head dmaqueue;
+ enum iss_video_dmaqueue_flags dmaqueue_flags;
+ struct vb2_alloc_ctx *alloc_ctx;
+
+ const struct iss_video_operations *ops;
+};
+
+#define to_iss_video(vdev) container_of(vdev, struct iss_video, video)
+
+struct iss_video_fh {
+ struct v4l2_fh vfh;
+ struct iss_video *video;
+ struct vb2_queue queue;
+ struct v4l2_format format;
+ struct v4l2_fract timeperframe;
+};
+
+#define to_iss_video_fh(fh) container_of(fh, struct iss_video_fh, vfh)
+#define iss_video_queue_to_iss_video_fh(q) \
+ container_of(q, struct iss_video_fh, queue)
+
+int omap4iss_video_init(struct iss_video *video, const char *name);
+void omap4iss_video_cleanup(struct iss_video *video);
+int omap4iss_video_register(struct iss_video *video,
+ struct v4l2_device *vdev);
+void omap4iss_video_unregister(struct iss_video *video);
+struct iss_buffer *omap4iss_video_buffer_next(struct iss_video *video);
+void omap4iss_video_cancel_stream(struct iss_video *video);
+struct media_pad *omap4iss_video_remote_pad(struct iss_video *video);
+
+const struct iss_format_info *
+omap4iss_video_format_info(u32 code);
+
+#endif /* OMAP4_ISS_VIDEO_H */
diff --git a/drivers/staging/mt29f_spinand/Kconfig b/drivers/staging/mt29f_spinand/Kconfig
new file mode 100644
index 000000000..f3f9cb3b5
--- /dev/null
+++ b/drivers/staging/mt29f_spinand/Kconfig
@@ -0,0 +1,16 @@
+config MTD_SPINAND_MT29F
+ tristate "SPINAND Device Support for Micron"
+ depends on MTD_NAND && SPI
+ help
+ This enables support for accessing Micron SPI NAND flash
+ devices.
+ If you have Micron SPI NAND chip say yes.
+
+ If unsure, say no here.
+
+config MTD_SPINAND_ONDIEECC
+ bool "Use SPINAND internal ECC"
+ depends on MTD_SPINAND_MT29F
+ help
+ Internal ECC.
+ Enables Hardware ECC support for Micron SPI NAND.
diff --git a/drivers/staging/mt29f_spinand/Makefile b/drivers/staging/mt29f_spinand/Makefile
new file mode 100644
index 000000000..e47af0f7f
--- /dev/null
+++ b/drivers/staging/mt29f_spinand/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_MTD_SPINAND_MT29F) += mt29f_spinand.o
diff --git a/drivers/staging/mt29f_spinand/TODO b/drivers/staging/mt29f_spinand/TODO
new file mode 100644
index 000000000..a2209b72d
--- /dev/null
+++ b/drivers/staging/mt29f_spinand/TODO
@@ -0,0 +1,13 @@
+TODO:
+ - Tested on XLP platform, needs to be tested on other platforms.
+ - Checkpatch.pl cleanups
+ - Sparce fixes.
+ - Clean up coding style to meet kernel standard.
+
+Please send patches
+To:
+Kamlakant Patel <kamlakant.patel@broadcom.com>
+Cc:
+Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+Mona Anonuevo <manonuevo@micron.com>
+linux-mtd@lists.infradead.org
diff --git a/drivers/staging/mt29f_spinand/mt29f_spinand.c b/drivers/staging/mt29f_spinand/mt29f_spinand.c
new file mode 100644
index 000000000..7285c64ba
--- /dev/null
+++ b/drivers/staging/mt29f_spinand/mt29f_spinand.c
@@ -0,0 +1,963 @@
+/*
+ * Copyright (c) 2003-2013 Broadcom Corporation
+ *
+ * Copyright (c) 2009-2010 Micron Technology, 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 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.
+ */
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/partitions.h>
+#include <linux/mtd/nand.h>
+#include <linux/spi/spi.h>
+
+#include "mt29f_spinand.h"
+
+#define BUFSIZE (10 * 64 * 2048)
+#define CACHE_BUF 2112
+/*
+ * OOB area specification layout: Total 32 available free bytes.
+ */
+
+static inline struct spinand_state *mtd_to_state(struct mtd_info *mtd)
+{
+ struct nand_chip *chip = (struct nand_chip *)mtd->priv;
+ struct spinand_info *info = (struct spinand_info *)chip->priv;
+ struct spinand_state *state = (struct spinand_state *)info->priv;
+
+ return state;
+}
+
+#ifdef CONFIG_MTD_SPINAND_ONDIEECC
+static int enable_hw_ecc;
+static int enable_read_hw_ecc;
+
+static struct nand_ecclayout spinand_oob_64 = {
+ .eccbytes = 24,
+ .eccpos = {
+ 1, 2, 3, 4, 5, 6,
+ 17, 18, 19, 20, 21, 22,
+ 33, 34, 35, 36, 37, 38,
+ 49, 50, 51, 52, 53, 54, },
+ .oobavail = 32,
+ .oobfree = {
+ {.offset = 8,
+ .length = 8},
+ {.offset = 24,
+ .length = 8},
+ {.offset = 40,
+ .length = 8},
+ {.offset = 56,
+ .length = 8},
+ }
+};
+#endif
+
+/*
+ * spinand_cmd - to process a command to send to the SPI Nand
+ * Description:
+ * Set up the command buffer to send to the SPI controller.
+ * The command buffer has to initialized to 0.
+ */
+
+static int spinand_cmd(struct spi_device *spi, struct spinand_cmd *cmd)
+{
+ struct spi_message message;
+ struct spi_transfer x[4];
+ u8 dummy = 0xff;
+
+ spi_message_init(&message);
+ memset(x, 0, sizeof(x));
+
+ x[0].len = 1;
+ x[0].tx_buf = &cmd->cmd;
+ spi_message_add_tail(&x[0], &message);
+
+ if (cmd->n_addr) {
+ x[1].len = cmd->n_addr;
+ x[1].tx_buf = cmd->addr;
+ spi_message_add_tail(&x[1], &message);
+ }
+
+ if (cmd->n_dummy) {
+ x[2].len = cmd->n_dummy;
+ x[2].tx_buf = &dummy;
+ spi_message_add_tail(&x[2], &message);
+ }
+
+ if (cmd->n_tx) {
+ x[3].len = cmd->n_tx;
+ x[3].tx_buf = cmd->tx_buf;
+ spi_message_add_tail(&x[3], &message);
+ }
+
+ if (cmd->n_rx) {
+ x[3].len = cmd->n_rx;
+ x[3].rx_buf = cmd->rx_buf;
+ spi_message_add_tail(&x[3], &message);
+ }
+
+ return spi_sync(spi, &message);
+}
+
+/*
+ * spinand_read_id- Read SPI Nand ID
+ * Description:
+ * Read ID: read two ID bytes from the SPI Nand device
+ */
+static int spinand_read_id(struct spi_device *spi_nand, u8 *id)
+{
+ int retval;
+ u8 nand_id[3];
+ struct spinand_cmd cmd = {0};
+
+ cmd.cmd = CMD_READ_ID;
+ cmd.n_rx = 3;
+ cmd.rx_buf = &nand_id[0];
+
+ retval = spinand_cmd(spi_nand, &cmd);
+ if (retval < 0) {
+ dev_err(&spi_nand->dev, "error %d reading id\n", retval);
+ return retval;
+ }
+ id[0] = nand_id[1];
+ id[1] = nand_id[2];
+ return retval;
+}
+
+/*
+ * spinand_read_status- send command 0xf to the SPI Nand status register
+ * Description:
+ * After read, write, or erase, the Nand device is expected to set the
+ * busy status.
+ * This function is to allow reading the status of the command: read,
+ * write, and erase.
+ * Once the status turns to be ready, the other status bits also are
+ * valid status bits.
+ */
+static int spinand_read_status(struct spi_device *spi_nand, uint8_t *status)
+{
+ struct spinand_cmd cmd = {0};
+ int ret;
+
+ cmd.cmd = CMD_READ_REG;
+ cmd.n_addr = 1;
+ cmd.addr[0] = REG_STATUS;
+ cmd.n_rx = 1;
+ cmd.rx_buf = status;
+
+ ret = spinand_cmd(spi_nand, &cmd);
+ if (ret < 0)
+ dev_err(&spi_nand->dev, "err: %d read status register\n", ret);
+
+ return ret;
+}
+
+#define MAX_WAIT_JIFFIES (40 * HZ)
+static int wait_till_ready(struct spi_device *spi_nand)
+{
+ unsigned long deadline;
+ int retval;
+ u8 stat = 0;
+
+ deadline = jiffies + MAX_WAIT_JIFFIES;
+ do {
+ retval = spinand_read_status(spi_nand, &stat);
+ if (retval < 0)
+ return -1;
+ else if (!(stat & 0x1))
+ break;
+
+ cond_resched();
+ } while (!time_after_eq(jiffies, deadline));
+
+ if ((stat & 0x1) == 0)
+ return 0;
+
+ return -1;
+}
+/**
+ * spinand_get_otp- send command 0xf to read the SPI Nand OTP register
+ * Description:
+ * There is one bit( bit 0x10 ) to set or to clear the internal ECC.
+ * Enable chip internal ECC, set the bit to 1
+ * Disable chip internal ECC, clear the bit to 0
+ */
+static int spinand_get_otp(struct spi_device *spi_nand, u8 *otp)
+{
+ struct spinand_cmd cmd = {0};
+ int retval;
+
+ cmd.cmd = CMD_READ_REG;
+ cmd.n_addr = 1;
+ cmd.addr[0] = REG_OTP;
+ cmd.n_rx = 1;
+ cmd.rx_buf = otp;
+
+ retval = spinand_cmd(spi_nand, &cmd);
+ if (retval < 0)
+ dev_err(&spi_nand->dev, "error %d get otp\n", retval);
+ return retval;
+}
+
+/**
+ * spinand_set_otp- send command 0x1f to write the SPI Nand OTP register
+ * Description:
+ * There is one bit( bit 0x10 ) to set or to clear the internal ECC.
+ * Enable chip internal ECC, set the bit to 1
+ * Disable chip internal ECC, clear the bit to 0
+ */
+static int spinand_set_otp(struct spi_device *spi_nand, u8 *otp)
+{
+ int retval;
+ struct spinand_cmd cmd = {0};
+
+ cmd.cmd = CMD_WRITE_REG,
+ cmd.n_addr = 1,
+ cmd.addr[0] = REG_OTP,
+ cmd.n_tx = 1,
+ cmd.tx_buf = otp,
+
+ retval = spinand_cmd(spi_nand, &cmd);
+ if (retval < 0)
+ dev_err(&spi_nand->dev, "error %d set otp\n", retval);
+
+ return retval;
+}
+
+#ifdef CONFIG_MTD_SPINAND_ONDIEECC
+/**
+ * spinand_enable_ecc- send command 0x1f to write the SPI Nand OTP register
+ * Description:
+ * There is one bit( bit 0x10 ) to set or to clear the internal ECC.
+ * Enable chip internal ECC, set the bit to 1
+ * Disable chip internal ECC, clear the bit to 0
+ */
+static int spinand_enable_ecc(struct spi_device *spi_nand)
+{
+ int retval;
+ u8 otp = 0;
+
+ retval = spinand_get_otp(spi_nand, &otp);
+ if (retval < 0)
+ return retval;
+
+ if ((otp & OTP_ECC_MASK) == OTP_ECC_MASK)
+ return 0;
+ otp |= OTP_ECC_MASK;
+ retval = spinand_set_otp(spi_nand, &otp);
+ if (retval < 0)
+ return retval;
+ return spinand_get_otp(spi_nand, &otp);
+}
+#endif
+
+static int spinand_disable_ecc(struct spi_device *spi_nand)
+{
+ int retval;
+ u8 otp = 0;
+
+ retval = spinand_get_otp(spi_nand, &otp);
+ if (retval < 0)
+ return retval;
+
+ if ((otp & OTP_ECC_MASK) == OTP_ECC_MASK) {
+ otp &= ~OTP_ECC_MASK;
+ retval = spinand_set_otp(spi_nand, &otp);
+ if (retval < 0)
+ return retval;
+ return spinand_get_otp(spi_nand, &otp);
+ }
+ return 0;
+}
+
+/**
+ * spinand_write_enable- send command 0x06 to enable write or erase the
+ * Nand cells
+ * Description:
+ * Before write and erase the Nand cells, the write enable has to be set.
+ * After the write or erase, the write enable bit is automatically
+ * cleared (status register bit 2)
+ * Set the bit 2 of the status register has the same effect
+ */
+static int spinand_write_enable(struct spi_device *spi_nand)
+{
+ struct spinand_cmd cmd = {0};
+
+ cmd.cmd = CMD_WR_ENABLE;
+ return spinand_cmd(spi_nand, &cmd);
+}
+
+static int spinand_read_page_to_cache(struct spi_device *spi_nand, u16 page_id)
+{
+ struct spinand_cmd cmd = {0};
+ u16 row;
+
+ row = page_id;
+ cmd.cmd = CMD_READ;
+ cmd.n_addr = 3;
+ cmd.addr[1] = (u8)((row & 0xff00) >> 8);
+ cmd.addr[2] = (u8)(row & 0x00ff);
+
+ return spinand_cmd(spi_nand, &cmd);
+}
+
+/*
+ * spinand_read_from_cache- send command 0x03 to read out the data from the
+ * cache register(2112 bytes max)
+ * Description:
+ * The read can specify 1 to 2112 bytes of data read at the corresponding
+ * locations.
+ * No tRd delay.
+ */
+static int spinand_read_from_cache(struct spi_device *spi_nand, u16 page_id,
+ u16 byte_id, u16 len, u8 *rbuf)
+{
+ struct spinand_cmd cmd = {0};
+ u16 column;
+
+ column = byte_id;
+ cmd.cmd = CMD_READ_RDM;
+ cmd.n_addr = 3;
+ cmd.addr[0] = (u8)((column & 0xff00) >> 8);
+ cmd.addr[0] |= (u8)(((page_id >> 6) & 0x1) << 4);
+ cmd.addr[1] = (u8)(column & 0x00ff);
+ cmd.addr[2] = (u8)(0xff);
+ cmd.n_dummy = 0;
+ cmd.n_rx = len;
+ cmd.rx_buf = rbuf;
+
+ return spinand_cmd(spi_nand, &cmd);
+}
+
+/*
+ * spinand_read_page-to read a page with:
+ * @page_id: the physical page number
+ * @offset: the location from 0 to 2111
+ * @len: number of bytes to read
+ * @rbuf: read buffer to hold @len bytes
+ *
+ * Description:
+ * The read includes two commands to the Nand: 0x13 and 0x03 commands
+ * Poll to read status to wait for tRD time.
+ */
+static int spinand_read_page(struct spi_device *spi_nand, u16 page_id,
+ u16 offset, u16 len, u8 *rbuf)
+{
+ int ret;
+ u8 status = 0;
+
+#ifdef CONFIG_MTD_SPINAND_ONDIEECC
+ if (enable_read_hw_ecc) {
+ if (spinand_enable_ecc(spi_nand) < 0)
+ dev_err(&spi_nand->dev, "enable HW ECC failed!");
+ }
+#endif
+ ret = spinand_read_page_to_cache(spi_nand, page_id);
+ if (ret < 0)
+ return ret;
+
+ if (wait_till_ready(spi_nand))
+ dev_err(&spi_nand->dev, "WAIT timedout!!!\n");
+
+ while (1) {
+ ret = spinand_read_status(spi_nand, &status);
+ if (ret < 0) {
+ dev_err(&spi_nand->dev,
+ "err %d read status register\n", ret);
+ return ret;
+ }
+
+ if ((status & STATUS_OIP_MASK) == STATUS_READY) {
+ if ((status & STATUS_ECC_MASK) == STATUS_ECC_ERROR) {
+ dev_err(&spi_nand->dev, "ecc error, page=%d\n",
+ page_id);
+ return 0;
+ }
+ break;
+ }
+ }
+
+ ret = spinand_read_from_cache(spi_nand, page_id, offset, len, rbuf);
+ if (ret < 0) {
+ dev_err(&spi_nand->dev, "read from cache failed!!\n");
+ return ret;
+ }
+
+#ifdef CONFIG_MTD_SPINAND_ONDIEECC
+ if (enable_read_hw_ecc) {
+ ret = spinand_disable_ecc(spi_nand);
+ if (ret < 0) {
+ dev_err(&spi_nand->dev, "disable ecc failed!!\n");
+ return ret;
+ }
+ enable_read_hw_ecc = 0;
+ }
+#endif
+ return ret;
+}
+
+/*
+ * spinand_program_data_to_cache--to write a page to cache with:
+ * @byte_id: the location to write to the cache
+ * @len: number of bytes to write
+ * @rbuf: read buffer to hold @len bytes
+ *
+ * Description:
+ * The write command used here is 0x84--indicating that the cache is
+ * not cleared first.
+ * Since it is writing the data to cache, there is no tPROG time.
+ */
+static int spinand_program_data_to_cache(struct spi_device *spi_nand,
+ u16 page_id, u16 byte_id, u16 len, u8 *wbuf)
+{
+ struct spinand_cmd cmd = {0};
+ u16 column;
+
+ column = byte_id;
+ cmd.cmd = CMD_PROG_PAGE_CLRCACHE;
+ cmd.n_addr = 2;
+ cmd.addr[0] = (u8)((column & 0xff00) >> 8);
+ cmd.addr[0] |= (u8)(((page_id >> 6) & 0x1) << 4);
+ cmd.addr[1] = (u8)(column & 0x00ff);
+ cmd.n_tx = len;
+ cmd.tx_buf = wbuf;
+
+ return spinand_cmd(spi_nand, &cmd);
+}
+
+/**
+ * spinand_program_execute--to write a page from cache to the Nand array with
+ * @page_id: the physical page location to write the page.
+ *
+ * Description:
+ * The write command used here is 0x10--indicating the cache is writing to
+ * the Nand array.
+ * Need to wait for tPROG time to finish the transaction.
+ */
+static int spinand_program_execute(struct spi_device *spi_nand, u16 page_id)
+{
+ struct spinand_cmd cmd = {0};
+ u16 row;
+
+ row = page_id;
+ cmd.cmd = CMD_PROG_PAGE_EXC;
+ cmd.n_addr = 3;
+ cmd.addr[1] = (u8)((row & 0xff00) >> 8);
+ cmd.addr[2] = (u8)(row & 0x00ff);
+
+ return spinand_cmd(spi_nand, &cmd);
+}
+
+/**
+ * spinand_program_page--to write a page with:
+ * @page_id: the physical page location to write the page.
+ * @offset: the location from the cache starting from 0 to 2111
+ * @len: the number of bytes to write
+ * @wbuf: the buffer to hold the number of bytes
+ *
+ * Description:
+ * The commands used here are 0x06, 0x84, and 0x10--indicating that
+ * the write enable is first sent, the write cache command, and the
+ * write execute command.
+ * Poll to wait for the tPROG time to finish the transaction.
+ */
+static int spinand_program_page(struct spi_device *spi_nand,
+ u16 page_id, u16 offset, u16 len, u8 *buf)
+{
+ int retval;
+ u8 status = 0;
+ uint8_t *wbuf;
+#ifdef CONFIG_MTD_SPINAND_ONDIEECC
+ unsigned int i, j;
+
+ enable_read_hw_ecc = 0;
+ wbuf = devm_kzalloc(&spi_nand->dev, CACHE_BUF, GFP_KERNEL);
+ spinand_read_page(spi_nand, page_id, 0, CACHE_BUF, wbuf);
+
+ for (i = offset, j = 0; i < len; i++, j++)
+ wbuf[i] &= buf[j];
+
+ if (enable_hw_ecc) {
+ retval = spinand_enable_ecc(spi_nand);
+ if (retval < 0) {
+ dev_err(&spi_nand->dev, "enable ecc failed!!\n");
+ return retval;
+ }
+ }
+#else
+ wbuf = buf;
+#endif
+ retval = spinand_write_enable(spi_nand);
+ if (retval < 0) {
+ dev_err(&spi_nand->dev, "write enable failed!!\n");
+ return retval;
+ }
+ if (wait_till_ready(spi_nand))
+ dev_err(&spi_nand->dev, "wait timedout!!!\n");
+
+ retval = spinand_program_data_to_cache(spi_nand, page_id,
+ offset, len, wbuf);
+ if (retval < 0)
+ return retval;
+ retval = spinand_program_execute(spi_nand, page_id);
+ if (retval < 0)
+ return retval;
+ while (1) {
+ retval = spinand_read_status(spi_nand, &status);
+ if (retval < 0) {
+ dev_err(&spi_nand->dev,
+ "error %d reading status register\n",
+ retval);
+ return retval;
+ }
+
+ if ((status & STATUS_OIP_MASK) == STATUS_READY) {
+ if ((status & STATUS_P_FAIL_MASK) == STATUS_P_FAIL) {
+ dev_err(&spi_nand->dev,
+ "program error, page %d\n", page_id);
+ return -1;
+ }
+ break;
+ }
+ }
+#ifdef CONFIG_MTD_SPINAND_ONDIEECC
+ if (enable_hw_ecc) {
+ retval = spinand_disable_ecc(spi_nand);
+ if (retval < 0) {
+ dev_err(&spi_nand->dev, "disable ecc failed!!\n");
+ return retval;
+ }
+ enable_hw_ecc = 0;
+ }
+#endif
+
+ return 0;
+}
+
+/**
+ * spinand_erase_block_erase--to erase a page with:
+ * @block_id: the physical block location to erase.
+ *
+ * Description:
+ * The command used here is 0xd8--indicating an erase command to erase
+ * one block--64 pages
+ * Need to wait for tERS.
+ */
+static int spinand_erase_block_erase(struct spi_device *spi_nand, u16 block_id)
+{
+ struct spinand_cmd cmd = {0};
+ u16 row;
+
+ row = block_id;
+ cmd.cmd = CMD_ERASE_BLK;
+ cmd.n_addr = 3;
+ cmd.addr[1] = (u8)((row & 0xff00) >> 8);
+ cmd.addr[2] = (u8)(row & 0x00ff);
+
+ return spinand_cmd(spi_nand, &cmd);
+}
+
+/**
+ * spinand_erase_block--to erase a page with:
+ * @block_id: the physical block location to erase.
+ *
+ * Description:
+ * The commands used here are 0x06 and 0xd8--indicating an erase
+ * command to erase one block--64 pages
+ * It will first to enable the write enable bit (0x06 command),
+ * and then send the 0xd8 erase command
+ * Poll to wait for the tERS time to complete the tranaction.
+ */
+static int spinand_erase_block(struct spi_device *spi_nand, u16 block_id)
+{
+ int retval;
+ u8 status = 0;
+
+ retval = spinand_write_enable(spi_nand);
+ if (wait_till_ready(spi_nand))
+ dev_err(&spi_nand->dev, "wait timedout!!!\n");
+
+ retval = spinand_erase_block_erase(spi_nand, block_id);
+ while (1) {
+ retval = spinand_read_status(spi_nand, &status);
+ if (retval < 0) {
+ dev_err(&spi_nand->dev,
+ "error %d reading status register\n",
+ (int) retval);
+ return retval;
+ }
+
+ if ((status & STATUS_OIP_MASK) == STATUS_READY) {
+ if ((status & STATUS_E_FAIL_MASK) == STATUS_E_FAIL) {
+ dev_err(&spi_nand->dev,
+ "erase error, block %d\n", block_id);
+ return -1;
+ }
+ break;
+ }
+ }
+ return 0;
+}
+
+#ifdef CONFIG_MTD_SPINAND_ONDIEECC
+static int spinand_write_page_hwecc(struct mtd_info *mtd,
+ struct nand_chip *chip, const uint8_t *buf, int oob_required)
+{
+ const uint8_t *p = buf;
+ int eccsize = chip->ecc.size;
+ int eccsteps = chip->ecc.steps;
+
+ enable_hw_ecc = 1;
+ chip->write_buf(mtd, p, eccsize * eccsteps);
+ return 0;
+}
+
+static int spinand_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip,
+ uint8_t *buf, int oob_required, int page)
+{
+ int retval;
+ u8 status;
+ uint8_t *p = buf;
+ int eccsize = chip->ecc.size;
+ int eccsteps = chip->ecc.steps;
+ struct spinand_info *info = (struct spinand_info *)chip->priv;
+
+ enable_read_hw_ecc = 1;
+
+ chip->read_buf(mtd, p, eccsize * eccsteps);
+ if (oob_required)
+ chip->read_buf(mtd, chip->oob_poi, mtd->oobsize);
+
+ while (1) {
+ retval = spinand_read_status(info->spi, &status);
+ if (retval < 0) {
+ dev_err(&mtd->dev,
+ "error %d reading status register\n",
+ retval);
+ return retval;
+ }
+
+ if ((status & STATUS_OIP_MASK) == STATUS_READY) {
+ if ((status & STATUS_ECC_MASK) == STATUS_ECC_ERROR) {
+ pr_info("spinand: ECC error\n");
+ mtd->ecc_stats.failed++;
+ } else if ((status & STATUS_ECC_MASK) ==
+ STATUS_ECC_1BIT_CORRECTED)
+ mtd->ecc_stats.corrected++;
+ break;
+ }
+ }
+ return 0;
+
+}
+#endif
+
+static void spinand_select_chip(struct mtd_info *mtd, int dev)
+{
+}
+
+static uint8_t spinand_read_byte(struct mtd_info *mtd)
+{
+ struct spinand_state *state = mtd_to_state(mtd);
+ u8 data;
+
+ data = state->buf[state->buf_ptr];
+ state->buf_ptr++;
+ return data;
+}
+
+
+static int spinand_wait(struct mtd_info *mtd, struct nand_chip *chip)
+{
+ struct spinand_info *info = (struct spinand_info *)chip->priv;
+
+ unsigned long timeo = jiffies;
+ int retval, state = chip->state;
+ u8 status;
+
+ if (state == FL_ERASING)
+ timeo += (HZ * 400) / 1000;
+ else
+ timeo += (HZ * 20) / 1000;
+
+ while (time_before(jiffies, timeo)) {
+ retval = spinand_read_status(info->spi, &status);
+ if (retval < 0) {
+ dev_err(&mtd->dev,
+ "error %d reading status register\n",
+ retval);
+ return retval;
+ }
+
+ if ((status & STATUS_OIP_MASK) == STATUS_READY)
+ return 0;
+
+ cond_resched();
+ }
+ return 0;
+}
+
+static void spinand_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len)
+{
+
+ struct spinand_state *state = mtd_to_state(mtd);
+
+ memcpy(state->buf + state->buf_ptr, buf, len);
+ state->buf_ptr += len;
+}
+
+static void spinand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
+{
+ struct spinand_state *state = mtd_to_state(mtd);
+
+ memcpy(buf, state->buf + state->buf_ptr, len);
+ state->buf_ptr += len;
+}
+
+/*
+ * spinand_reset- send RESET command "0xff" to the Nand device.
+ */
+static void spinand_reset(struct spi_device *spi_nand)
+{
+ struct spinand_cmd cmd = {0};
+
+ cmd.cmd = CMD_RESET;
+
+ if (spinand_cmd(spi_nand, &cmd) < 0)
+ pr_info("spinand reset failed!\n");
+
+ /* elapse 1ms before issuing any other command */
+ udelay(1000);
+
+ if (wait_till_ready(spi_nand))
+ dev_err(&spi_nand->dev, "wait timedout!\n");
+}
+
+static void spinand_cmdfunc(struct mtd_info *mtd, unsigned int command,
+ int column, int page)
+{
+ struct nand_chip *chip = (struct nand_chip *)mtd->priv;
+ struct spinand_info *info = (struct spinand_info *)chip->priv;
+ struct spinand_state *state = (struct spinand_state *)info->priv;
+
+ switch (command) {
+ /*
+ * READ0 - read in first 0x800 bytes
+ */
+ case NAND_CMD_READ1:
+ case NAND_CMD_READ0:
+ state->buf_ptr = 0;
+ spinand_read_page(info->spi, page, 0x0, 0x840, state->buf);
+ break;
+ /* READOOB reads only the OOB because no ECC is performed. */
+ case NAND_CMD_READOOB:
+ state->buf_ptr = 0;
+ spinand_read_page(info->spi, page, 0x800, 0x40, state->buf);
+ break;
+ case NAND_CMD_RNDOUT:
+ state->buf_ptr = column;
+ break;
+ case NAND_CMD_READID:
+ state->buf_ptr = 0;
+ spinand_read_id(info->spi, state->buf);
+ break;
+ case NAND_CMD_PARAM:
+ state->buf_ptr = 0;
+ break;
+ /* ERASE1 stores the block and page address */
+ case NAND_CMD_ERASE1:
+ spinand_erase_block(info->spi, page);
+ break;
+ /* ERASE2 uses the block and page address from ERASE1 */
+ case NAND_CMD_ERASE2:
+ break;
+ /* SEQIN sets up the addr buffer and all registers except the length */
+ case NAND_CMD_SEQIN:
+ state->col = column;
+ state->row = page;
+ state->buf_ptr = 0;
+ break;
+ /* PAGEPROG reuses all of the setup from SEQIN and adds the length */
+ case NAND_CMD_PAGEPROG:
+ spinand_program_page(info->spi, state->row, state->col,
+ state->buf_ptr, state->buf);
+ break;
+ case NAND_CMD_STATUS:
+ spinand_get_otp(info->spi, state->buf);
+ if (!(state->buf[0] & 0x80))
+ state->buf[0] = 0x80;
+ state->buf_ptr = 0;
+ break;
+ /* RESET command */
+ case NAND_CMD_RESET:
+ if (wait_till_ready(info->spi))
+ dev_err(&info->spi->dev, "WAIT timedout!!!\n");
+ /* a minimum of 250us must elapse before issuing RESET cmd*/
+ udelay(250);
+ spinand_reset(info->spi);
+ break;
+ default:
+ dev_err(&mtd->dev, "Unknown CMD: 0x%x\n", command);
+ }
+}
+
+/**
+ * spinand_lock_block- send write register 0x1f command to the Nand device
+ *
+ * Description:
+ * After power up, all the Nand blocks are locked. This function allows
+ * one to unlock the blocks, and so it can be written or erased.
+ */
+static int spinand_lock_block(struct spi_device *spi_nand, u8 lock)
+{
+ struct spinand_cmd cmd = {0};
+ int ret;
+ u8 otp = 0;
+
+ ret = spinand_get_otp(spi_nand, &otp);
+
+ cmd.cmd = CMD_WRITE_REG;
+ cmd.n_addr = 1;
+ cmd.addr[0] = REG_BLOCK_LOCK;
+ cmd.n_tx = 1;
+ cmd.tx_buf = &lock;
+
+ ret = spinand_cmd(spi_nand, &cmd);
+ if (ret < 0)
+ dev_err(&spi_nand->dev, "error %d lock block\n", ret);
+
+ return ret;
+}
+/*
+ * spinand_probe - [spinand Interface]
+ * @spi_nand: registered device driver.
+ *
+ * Description:
+ * To set up the device driver parameters to make the device available.
+ */
+static int spinand_probe(struct spi_device *spi_nand)
+{
+ struct mtd_info *mtd;
+ struct nand_chip *chip;
+ struct spinand_info *info;
+ struct spinand_state *state;
+ struct mtd_part_parser_data ppdata;
+
+ info = devm_kzalloc(&spi_nand->dev, sizeof(struct spinand_info),
+ GFP_KERNEL);
+ if (!info)
+ return -ENOMEM;
+
+ info->spi = spi_nand;
+
+ spinand_lock_block(spi_nand, BL_ALL_UNLOCKED);
+
+ state = devm_kzalloc(&spi_nand->dev, sizeof(struct spinand_state),
+ GFP_KERNEL);
+ if (!state)
+ return -ENOMEM;
+
+ info->priv = state;
+ state->buf_ptr = 0;
+ state->buf = devm_kzalloc(&spi_nand->dev, BUFSIZE, GFP_KERNEL);
+ if (!state->buf)
+ return -ENOMEM;
+
+ chip = devm_kzalloc(&spi_nand->dev, sizeof(struct nand_chip),
+ GFP_KERNEL);
+ if (!chip)
+ return -ENOMEM;
+
+#ifdef CONFIG_MTD_SPINAND_ONDIEECC
+ chip->ecc.mode = NAND_ECC_HW;
+ chip->ecc.size = 0x200;
+ chip->ecc.bytes = 0x6;
+ chip->ecc.steps = 0x4;
+
+ chip->ecc.strength = 1;
+ chip->ecc.total = chip->ecc.steps * chip->ecc.bytes;
+ chip->ecc.layout = &spinand_oob_64;
+ chip->ecc.read_page = spinand_read_page_hwecc;
+ chip->ecc.write_page = spinand_write_page_hwecc;
+#else
+ chip->ecc.mode = NAND_ECC_SOFT;
+ if (spinand_disable_ecc(spi_nand) < 0)
+ pr_info("%s: disable ecc failed!\n", __func__);
+#endif
+
+ chip->priv = info;
+ chip->read_buf = spinand_read_buf;
+ chip->write_buf = spinand_write_buf;
+ chip->read_byte = spinand_read_byte;
+ chip->cmdfunc = spinand_cmdfunc;
+ chip->waitfunc = spinand_wait;
+ chip->options |= NAND_CACHEPRG;
+ chip->select_chip = spinand_select_chip;
+
+ mtd = devm_kzalloc(&spi_nand->dev, sizeof(struct mtd_info), GFP_KERNEL);
+ if (!mtd)
+ return -ENOMEM;
+
+ dev_set_drvdata(&spi_nand->dev, mtd);
+
+ mtd->priv = chip;
+ mtd->name = dev_name(&spi_nand->dev);
+ mtd->owner = THIS_MODULE;
+ mtd->oobsize = 64;
+
+ if (nand_scan(mtd, 1))
+ return -ENXIO;
+
+ ppdata.of_node = spi_nand->dev.of_node;
+ return mtd_device_parse_register(mtd, NULL, &ppdata, NULL, 0);
+}
+
+/*
+ * spinand_remove: Remove the device driver
+ * @spi: the spi device.
+ *
+ * Description:
+ * To remove the device driver parameters and free up allocated memories.
+ */
+static int spinand_remove(struct spi_device *spi)
+{
+ mtd_device_unregister(dev_get_drvdata(&spi->dev));
+
+ return 0;
+}
+
+static const struct of_device_id spinand_dt[] = {
+ { .compatible = "spinand,mt29f", },
+ {}
+};
+
+/*
+ * Device name structure description
+ */
+static struct spi_driver spinand_driver = {
+ .driver = {
+ .name = "mt29f",
+ .bus = &spi_bus_type,
+ .owner = THIS_MODULE,
+ .of_match_table = spinand_dt,
+ },
+ .probe = spinand_probe,
+ .remove = spinand_remove,
+};
+
+module_spi_driver(spinand_driver);
+
+MODULE_DESCRIPTION("SPI NAND driver for Micron");
+MODULE_AUTHOR("Henry Pan <hspan@micron.com>, Kamlakant Patel <kamlakant.patel@broadcom.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/staging/mt29f_spinand/mt29f_spinand.h b/drivers/staging/mt29f_spinand/mt29f_spinand.h
new file mode 100644
index 000000000..7f2c24dc5
--- /dev/null
+++ b/drivers/staging/mt29f_spinand/mt29f_spinand.h
@@ -0,0 +1,107 @@
+/*-
+ * Copyright 2013 Broadcom Corporation
+ *
+ * Copyright (c) 2009-2010 Micron Technology, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ * Henry Pan <hspan@micron.com>
+ *
+ * based on nand.h
+ */
+#ifndef __LINUX_MTD_SPI_NAND_H
+#define __LINUX_MTD_SPI_NAND_H
+
+#include <linux/wait.h>
+#include <linux/spinlock.h>
+#include <linux/mtd/mtd.h>
+
+/* cmd */
+#define CMD_READ 0x13
+#define CMD_READ_RDM 0x03
+#define CMD_PROG_PAGE_CLRCACHE 0x02
+#define CMD_PROG_PAGE 0x84
+#define CMD_PROG_PAGE_EXC 0x10
+#define CMD_ERASE_BLK 0xd8
+#define CMD_WR_ENABLE 0x06
+#define CMD_WR_DISABLE 0x04
+#define CMD_READ_ID 0x9f
+#define CMD_RESET 0xff
+#define CMD_READ_REG 0x0f
+#define CMD_WRITE_REG 0x1f
+
+/* feature/ status reg */
+#define REG_BLOCK_LOCK 0xa0
+#define REG_OTP 0xb0
+#define REG_STATUS 0xc0/* timing */
+
+/* status */
+#define STATUS_OIP_MASK 0x01
+#define STATUS_READY (0 << 0)
+#define STATUS_BUSY (1 << 0)
+
+#define STATUS_E_FAIL_MASK 0x04
+#define STATUS_E_FAIL (1 << 2)
+
+#define STATUS_P_FAIL_MASK 0x08
+#define STATUS_P_FAIL (1 << 3)
+
+#define STATUS_ECC_MASK 0x30
+#define STATUS_ECC_1BIT_CORRECTED (1 << 4)
+#define STATUS_ECC_ERROR (2 << 4)
+#define STATUS_ECC_RESERVED (3 << 4)
+
+/*ECC enable defines*/
+#define OTP_ECC_MASK 0x10
+#define OTP_ECC_OFF 0
+#define OTP_ECC_ON 1
+
+#define ECC_DISABLED
+#define ECC_IN_NAND
+#define ECC_SOFT
+
+/* block lock */
+#define BL_ALL_LOCKED 0x38
+#define BL_1_2_LOCKED 0x30
+#define BL_1_4_LOCKED 0x28
+#define BL_1_8_LOCKED 0x20
+#define BL_1_16_LOCKED 0x18
+#define BL_1_32_LOCKED 0x10
+#define BL_1_64_LOCKED 0x08
+#define BL_ALL_UNLOCKED 0
+
+struct spinand_info {
+ struct nand_ecclayout *ecclayout;
+ struct spi_device *spi;
+ void *priv;
+};
+
+struct spinand_state {
+ uint32_t col;
+ uint32_t row;
+ int buf_ptr;
+ u8 *buf;
+};
+
+struct spinand_cmd {
+ u8 cmd;
+ u32 n_addr; /* Number of address */
+ u8 addr[3]; /* Reg Offset */
+ u32 n_dummy; /* Dummy use */
+ u32 n_tx; /* Number of tx bytes */
+ u8 *tx_buf; /* Tx buf */
+ u32 n_rx; /* Number of rx bytes */
+ u8 *rx_buf; /* Rx buf */
+};
+
+extern int spinand_mtd(struct mtd_info *mtd);
+extern void spinand_mtd_release(struct mtd_info *mtd);
+
+#endif /* __LINUX_MTD_SPI_NAND_H */
diff --git a/drivers/staging/netlogic/Kconfig b/drivers/staging/netlogic/Kconfig
new file mode 100644
index 000000000..d660de51b
--- /dev/null
+++ b/drivers/staging/netlogic/Kconfig
@@ -0,0 +1,7 @@
+config NETLOGIC_XLR_NET
+ tristate "Netlogic XLR/XLS network device"
+ depends on CPU_XLR
+ select PHYLIB
+ ---help---
+ This driver support Netlogic XLR/XLS on chip gigabit
+ Ethernet.
diff --git a/drivers/staging/netlogic/Makefile b/drivers/staging/netlogic/Makefile
new file mode 100644
index 000000000..f7355e3e9
--- /dev/null
+++ b/drivers/staging/netlogic/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_NETLOGIC_XLR_NET) += xlr_net.o platform_net.o
diff --git a/drivers/staging/netlogic/TODO b/drivers/staging/netlogic/TODO
new file mode 100644
index 000000000..8f172b017
--- /dev/null
+++ b/drivers/staging/netlogic/TODO
@@ -0,0 +1,11 @@
+* Implementing 64bit stat counter in software
+* All memory allocation should be changed to DMA allocations
+* Changing comments in to linux standred format
+
+Please send patches
+To:
+Ganesan Ramalingam <ganesanr@broadcom.com>
+Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+Cc:
+Jayachandran Chandrashekaran Nair <jchandra@broadcom.com>
+
diff --git a/drivers/staging/netlogic/platform_net.c b/drivers/staging/netlogic/platform_net.c
new file mode 100644
index 000000000..77c3c3522
--- /dev/null
+++ b/drivers/staging/netlogic/platform_net.c
@@ -0,0 +1,245 @@
+/*
+ * Copyright (c) 2003-2012 Broadcom 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
+ * 1. Redistributions of source code must retain the above copyright
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the Broadcom
+ * license below:
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY BROADCOM ``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 BROADCOM OR CONTRIBUTORS 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.
+ */
+
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/delay.h>
+#include <linux/ioport.h>
+#include <linux/resource.h>
+#include <linux/phy.h>
+
+#include <asm/netlogic/haldefs.h>
+#include <asm/netlogic/common.h>
+#include <asm/netlogic/xlr/fmn.h>
+#include <asm/netlogic/xlr/xlr.h>
+#include <asm/netlogic/psb-bootinfo.h>
+#include <asm/netlogic/xlr/pic.h>
+#include <asm/netlogic/xlr/iomap.h>
+
+#include "platform_net.h"
+
+/* Linux Net */
+#define MAX_NUM_GMAC 8
+#define MAX_NUM_XLS_GMAC 8
+#define MAX_NUM_XLR_GMAC 4
+
+
+static u32 xlr_gmac_offsets[] = {
+ NETLOGIC_IO_GMAC_0_OFFSET, NETLOGIC_IO_GMAC_1_OFFSET,
+ NETLOGIC_IO_GMAC_2_OFFSET, NETLOGIC_IO_GMAC_3_OFFSET,
+ NETLOGIC_IO_GMAC_4_OFFSET, NETLOGIC_IO_GMAC_5_OFFSET,
+ NETLOGIC_IO_GMAC_6_OFFSET, NETLOGIC_IO_GMAC_7_OFFSET
+};
+
+static u32 xlr_gmac_irqs[] = { PIC_GMAC_0_IRQ, PIC_GMAC_1_IRQ,
+ PIC_GMAC_2_IRQ, PIC_GMAC_3_IRQ,
+ PIC_GMAC_4_IRQ, PIC_GMAC_5_IRQ,
+ PIC_GMAC_6_IRQ, PIC_GMAC_7_IRQ
+};
+
+static struct resource xlr_net0_res[8];
+static struct resource xlr_net1_res[8];
+static u32 __iomem *gmac4_addr;
+static u32 __iomem *gpio_addr;
+
+static void xlr_resource_init(struct resource *res, int offset, int irq)
+{
+ res->name = "gmac";
+
+ res->start = CPHYSADDR(nlm_mmio_base(offset));
+ res->end = res->start + 0xfff;
+ res->flags = IORESOURCE_MEM;
+
+ res++;
+ res->name = "gmac";
+ res->start = res->end = irq;
+ res->flags = IORESOURCE_IRQ;
+}
+
+static struct platform_device *gmac_controller2_init(void *gmac0_addr)
+{
+ int mac;
+ static struct xlr_net_data ndata1 = {
+ .phy_interface = PHY_INTERFACE_MODE_SGMII,
+ .rfr_station = FMN_STNID_GMAC1_FR_0,
+ .bucket_size = xlr_board_fmn_config.bucket_size,
+ .gmac_fmn_info = &xlr_board_fmn_config.gmac[1],
+ };
+
+ static struct platform_device xlr_net_dev1 = {
+ .name = "xlr-net",
+ .id = 1,
+ .dev.platform_data = &ndata1,
+ };
+
+ gmac4_addr = ioremap(CPHYSADDR(
+ nlm_mmio_base(NETLOGIC_IO_GMAC_4_OFFSET)), 0xfff);
+ ndata1.serdes_addr = gmac4_addr;
+ ndata1.pcs_addr = gmac4_addr;
+ ndata1.mii_addr = gmac0_addr;
+ ndata1.gpio_addr = gpio_addr;
+ ndata1.cpu_mask = nlm_current_node()->coremask;
+
+ xlr_net_dev1.resource = xlr_net1_res;
+
+ for (mac = 0; mac < 4; mac++) {
+ ndata1.tx_stnid[mac] = FMN_STNID_GMAC1_TX0 + mac;
+ ndata1.phy_addr[mac] = mac + 4 + 0x10;
+
+ xlr_resource_init(&xlr_net1_res[mac * 2],
+ xlr_gmac_offsets[mac + 4],
+ xlr_gmac_irqs[mac + 4]);
+ }
+ xlr_net_dev1.num_resources = 8;
+
+ return &xlr_net_dev1;
+}
+
+static void xls_gmac_init(void)
+{
+ int mac;
+ struct platform_device *xlr_net_dev1;
+ void __iomem *gmac0_addr = ioremap(CPHYSADDR(
+ nlm_mmio_base(NETLOGIC_IO_GMAC_0_OFFSET)), 0xfff);
+
+ static struct xlr_net_data ndata0 = {
+ .rfr_station = FMN_STNID_GMACRFR_0,
+ .bucket_size = xlr_board_fmn_config.bucket_size,
+ .gmac_fmn_info = &xlr_board_fmn_config.gmac[0],
+ };
+
+ static struct platform_device xlr_net_dev0 = {
+ .name = "xlr-net",
+ .id = 0,
+ };
+ xlr_net_dev0.dev.platform_data = &ndata0;
+ ndata0.serdes_addr = gmac0_addr;
+ ndata0.pcs_addr = gmac0_addr;
+ ndata0.mii_addr = gmac0_addr;
+
+ /* Passing GPIO base for serdes init. Only needed on sgmii ports */
+ gpio_addr = ioremap(CPHYSADDR(
+ nlm_mmio_base(NETLOGIC_IO_GPIO_OFFSET)), 0xfff);
+ ndata0.gpio_addr = gpio_addr;
+ ndata0.cpu_mask = nlm_current_node()->coremask;
+
+ xlr_net_dev0.resource = xlr_net0_res;
+
+ switch (nlm_prom_info.board_major_version) {
+ case 12:
+ /* first block RGMII or XAUI, use RGMII */
+ ndata0.phy_interface = PHY_INTERFACE_MODE_RGMII,
+ ndata0.tx_stnid[0] = FMN_STNID_GMAC0_TX0;
+ ndata0.phy_addr[0] = 0;
+
+ xlr_net_dev0.num_resources = 2;
+
+ xlr_resource_init(&xlr_net0_res[0], xlr_gmac_offsets[0],
+ xlr_gmac_irqs[0]);
+ platform_device_register(&xlr_net_dev0);
+
+ /* second block is XAUI, not supported yet */
+ break;
+ default:
+ /* default XLS config, all ports SGMII */
+ ndata0.phy_interface = PHY_INTERFACE_MODE_SGMII;
+ for (mac = 0; mac < 4; mac++) {
+ ndata0.tx_stnid[mac] = FMN_STNID_GMAC0_TX0 + mac;
+ ndata0.phy_addr[mac] = mac + 0x10;
+
+ xlr_resource_init(&xlr_net0_res[mac * 2],
+ xlr_gmac_offsets[mac],
+ xlr_gmac_irqs[mac]);
+ }
+ xlr_net_dev0.num_resources = 8;
+ platform_device_register(&xlr_net_dev0);
+
+ xlr_net_dev1 = gmac_controller2_init(gmac0_addr);
+ platform_device_register(xlr_net_dev1);
+ }
+}
+
+static void xlr_gmac_init(void)
+{
+ int mac;
+
+ /* assume all GMACs for now */
+ static struct xlr_net_data ndata0 = {
+ .phy_interface = PHY_INTERFACE_MODE_RGMII,
+ .serdes_addr = NULL,
+ .pcs_addr = NULL,
+ .rfr_station = FMN_STNID_GMACRFR_0,
+ .bucket_size = xlr_board_fmn_config.bucket_size,
+ .gmac_fmn_info = &xlr_board_fmn_config.gmac[0],
+ .gpio_addr = NULL,
+ };
+
+
+ static struct platform_device xlr_net_dev0 = {
+ .name = "xlr-net",
+ .id = 0,
+ .dev.platform_data = &ndata0,
+ };
+ ndata0.mii_addr = ioremap(CPHYSADDR(
+ nlm_mmio_base(NETLOGIC_IO_GMAC_0_OFFSET)), 0xfff);
+
+ ndata0.cpu_mask = nlm_current_node()->coremask;
+
+ for (mac = 0; mac < MAX_NUM_XLR_GMAC; mac++) {
+ ndata0.tx_stnid[mac] = FMN_STNID_GMAC0_TX0 + mac;
+ ndata0.phy_addr[mac] = mac;
+ xlr_resource_init(&xlr_net0_res[mac * 2], xlr_gmac_offsets[mac],
+ xlr_gmac_irqs[mac]);
+ }
+ xlr_net_dev0.num_resources = 8;
+ xlr_net_dev0.resource = xlr_net0_res;
+
+ platform_device_register(&xlr_net_dev0);
+}
+
+static int __init xlr_net_init(void)
+{
+ if (nlm_chip_is_xls())
+ xls_gmac_init();
+ else
+ xlr_gmac_init();
+
+ return 0;
+}
+
+arch_initcall(xlr_net_init);
diff --git a/drivers/staging/netlogic/platform_net.h b/drivers/staging/netlogic/platform_net.h
new file mode 100644
index 000000000..e1b27f649
--- /dev/null
+++ b/drivers/staging/netlogic/platform_net.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2003-2012 Broadcom 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
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the Broadcom
+ * license below:
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY BROADCOM ``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 BROADCOM OR CONTRIBUTORS 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.
+ */
+
+#define PORTS_PER_CONTROLLER 4
+
+struct xlr_net_data {
+ int cpu_mask;
+ u32 __iomem *mii_addr;
+ u32 __iomem *serdes_addr;
+ u32 __iomem *pcs_addr;
+ u32 __iomem *gpio_addr;
+ int phy_interface;
+ int rfr_station;
+ int tx_stnid[PORTS_PER_CONTROLLER];
+ int *bucket_size;
+ int phy_addr[PORTS_PER_CONTROLLER];
+ struct xlr_fmn_info *gmac_fmn_info;
+};
diff --git a/drivers/staging/netlogic/xlr_net.c b/drivers/staging/netlogic/xlr_net.c
new file mode 100644
index 000000000..8ae01753b
--- /dev/null
+++ b/drivers/staging/netlogic/xlr_net.c
@@ -0,0 +1,1144 @@
+/*
+ * Copyright (c) 2003-2012 Broadcom 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
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the Broadcom
+ * license below:
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY BROADCOM ``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 BROADCOM OR CONTRIBUTORS 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.
+ */
+#include <linux/phy.h>
+#include <linux/delay.h>
+#include <linux/netdevice.h>
+#include <linux/smp.h>
+#include <linux/ethtool.h>
+#include <linux/module.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/jiffies.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+
+#include <asm/mipsregs.h>
+/*
+ * fmn.h - For FMN credit configuration and registering fmn_handler.
+ * FMN is communication mechanism that allows processing agents within
+ * XLR/XLS to communicate each other.
+ */
+#include <asm/netlogic/xlr/fmn.h>
+
+#include "platform_net.h"
+#include "xlr_net.h"
+
+/*
+ * The readl/writel implementation byteswaps on XLR/XLS, so
+ * we need to use __raw_ IO to read the NAE registers
+ * because they are in the big-endian MMIO area on the SoC.
+ */
+static inline void xlr_nae_wreg(u32 __iomem *base, unsigned int reg, u32 val)
+{
+ __raw_writel(val, base + reg);
+}
+
+static inline u32 xlr_nae_rdreg(u32 __iomem *base, unsigned int reg)
+{
+ return __raw_readl(base + reg);
+}
+
+static inline void xlr_reg_update(u32 *base_addr,
+ u32 off, u32 val, u32 mask)
+{
+ u32 tmp;
+
+ tmp = xlr_nae_rdreg(base_addr, off);
+ xlr_nae_wreg(base_addr, off, (tmp & ~mask) | (val & mask));
+}
+
+#define MAC_SKB_BACK_PTR_SIZE SMP_CACHE_BYTES
+
+static int send_to_rfr_fifo(struct xlr_net_priv *priv, void *addr)
+{
+ struct nlm_fmn_msg msg;
+ int ret = 0, num_try = 0, stnid;
+ unsigned long paddr, mflags;
+
+ paddr = virt_to_bus(addr);
+ msg.msg0 = (u64)paddr & 0xffffffffe0ULL;
+ msg.msg1 = 0;
+ msg.msg2 = 0;
+ msg.msg3 = 0;
+ stnid = priv->nd->rfr_station;
+ do {
+ mflags = nlm_cop2_enable_irqsave();
+ ret = nlm_fmn_send(1, 0, stnid, &msg);
+ nlm_cop2_disable_irqrestore(mflags);
+ if (ret == 0)
+ return 0;
+ } while (++num_try < 10000);
+
+ pr_err("Send to RFR failed in RX path\n");
+ return ret;
+}
+
+static inline unsigned char *xlr_alloc_skb(void)
+{
+ struct sk_buff *skb;
+ int buf_len = sizeof(struct sk_buff *);
+ unsigned char *skb_data;
+
+ /* skb->data is cache aligned */
+ skb = alloc_skb(XLR_RX_BUF_SIZE, GFP_ATOMIC);
+ if (!skb)
+ return NULL;
+ skb_data = skb->data;
+ skb_put(skb, MAC_SKB_BACK_PTR_SIZE);
+ skb_pull(skb, MAC_SKB_BACK_PTR_SIZE);
+ memcpy(skb_data, &skb, buf_len);
+
+ return skb->data;
+}
+
+static void xlr_net_fmn_handler(int bkt, int src_stnid, int size,
+ int code, struct nlm_fmn_msg *msg, void *arg)
+{
+ struct sk_buff *skb;
+ void *skb_data = NULL;
+ struct net_device *ndev;
+ struct xlr_net_priv *priv;
+ u32 port, length;
+ unsigned char *addr;
+ struct xlr_adapter *adapter = (struct xlr_adapter *) arg;
+
+ length = (msg->msg0 >> 40) & 0x3fff;
+ if (length == 0) {
+ addr = bus_to_virt(msg->msg0 & 0xffffffffffULL);
+ addr = addr - MAC_SKB_BACK_PTR_SIZE;
+ skb = (struct sk_buff *) *(unsigned long *)addr;
+ dev_kfree_skb_any((struct sk_buff *)addr);
+ } else {
+ addr = (unsigned char *)
+ bus_to_virt(msg->msg0 & 0xffffffffe0ULL);
+ length = length - BYTE_OFFSET - MAC_CRC_LEN;
+ port = ((int)msg->msg0) & 0x0f;
+ addr = addr - MAC_SKB_BACK_PTR_SIZE;
+ skb = (struct sk_buff *) *(unsigned long *)addr;
+ skb->dev = adapter->netdev[port];
+ if (skb->dev == NULL)
+ return;
+ ndev = skb->dev;
+ priv = netdev_priv(ndev);
+
+ /* 16 byte IP header align */
+ skb_reserve(skb, BYTE_OFFSET);
+ skb_put(skb, length);
+ skb->protocol = eth_type_trans(skb, skb->dev);
+ skb->dev->last_rx = jiffies;
+ netif_rx(skb);
+ /* Fill rx ring */
+ skb_data = xlr_alloc_skb();
+ if (skb_data)
+ send_to_rfr_fifo(priv, skb_data);
+ }
+}
+
+/*
+ * Ethtool operation
+ */
+static int xlr_get_settings(struct net_device *ndev, struct ethtool_cmd *ecmd)
+{
+ struct xlr_net_priv *priv = netdev_priv(ndev);
+ struct phy_device *phydev = priv->mii_bus->phy_map[priv->phy_addr];
+
+ if (!phydev)
+ return -ENODEV;
+ return phy_ethtool_gset(phydev, ecmd);
+}
+
+static int xlr_set_settings(struct net_device *ndev, struct ethtool_cmd *ecmd)
+{
+ struct xlr_net_priv *priv = netdev_priv(ndev);
+ struct phy_device *phydev = priv->mii_bus->phy_map[priv->phy_addr];
+
+ if (!phydev)
+ return -ENODEV;
+ return phy_ethtool_sset(phydev, ecmd);
+}
+
+static struct ethtool_ops xlr_ethtool_ops = {
+ .get_settings = xlr_get_settings,
+ .set_settings = xlr_set_settings,
+};
+
+/*
+ * Net operations
+ */
+static int xlr_net_fill_rx_ring(struct net_device *ndev)
+{
+ void *skb_data;
+ struct xlr_net_priv *priv = netdev_priv(ndev);
+ int i;
+
+ for (i = 0; i < MAX_FRIN_SPILL/4; i++) {
+ skb_data = xlr_alloc_skb();
+ if (!skb_data) {
+ pr_err("SKB allocation failed\n");
+ return -ENOMEM;
+ }
+ send_to_rfr_fifo(priv, skb_data);
+ }
+ pr_info("Rx ring setup done\n");
+ return 0;
+}
+
+static int xlr_net_open(struct net_device *ndev)
+{
+ u32 err;
+ struct xlr_net_priv *priv = netdev_priv(ndev);
+ struct phy_device *phydev = priv->mii_bus->phy_map[priv->phy_addr];
+
+ /* schedule a link state check */
+ phy_start(phydev);
+
+ err = phy_start_aneg(phydev);
+ if (err) {
+ pr_err("Autoneg failed\n");
+ return err;
+ }
+ /* Setup the speed from PHY to internal reg*/
+ xlr_set_gmac_speed(priv);
+
+ netif_tx_start_all_queues(ndev);
+
+ return 0;
+}
+
+static int xlr_net_stop(struct net_device *ndev)
+{
+ struct xlr_net_priv *priv = netdev_priv(ndev);
+ struct phy_device *phydev = priv->mii_bus->phy_map[priv->phy_addr];
+
+ phy_stop(phydev);
+ netif_tx_stop_all_queues(ndev);
+ return 0;
+}
+
+static void xlr_make_tx_desc(struct nlm_fmn_msg *msg, unsigned long addr,
+ struct sk_buff *skb)
+{
+ unsigned long physkb = virt_to_phys(skb);
+ int cpu_core = nlm_core_id();
+ int fr_stn_id = cpu_core * 8 + XLR_FB_STN; /* FB to 6th bucket */
+
+ msg->msg0 = (((u64)1 << 63) | /* End of packet descriptor */
+ ((u64)127 << 54) | /* No Free back */
+ (u64)skb->len << 40 | /* Length of data */
+ ((u64)addr));
+ msg->msg1 = (((u64)1 << 63) |
+ ((u64)fr_stn_id << 54) | /* Free back id */
+ (u64)0 << 40 | /* Set len to 0 */
+ ((u64)physkb & 0xffffffff)); /* 32bit address */
+ msg->msg2 = msg->msg3 = 0;
+}
+
+static void __maybe_unused xlr_wakeup_queue(unsigned long dev)
+{
+ struct net_device *ndev = (struct net_device *) dev;
+ struct xlr_net_priv *priv = netdev_priv(ndev);
+ struct phy_device *phydev = priv->mii_bus->phy_map[priv->phy_addr];
+
+ if (phydev->link)
+ netif_tx_wake_queue(netdev_get_tx_queue(ndev, priv->wakeup_q));
+}
+
+static netdev_tx_t xlr_net_start_xmit(struct sk_buff *skb,
+ struct net_device *ndev)
+{
+ struct nlm_fmn_msg msg;
+ struct xlr_net_priv *priv = netdev_priv(ndev);
+ int ret;
+ u32 flags;
+
+ xlr_make_tx_desc(&msg, virt_to_phys(skb->data), skb);
+ flags = nlm_cop2_enable_irqsave();
+ ret = nlm_fmn_send(2, 0, priv->tx_stnid, &msg);
+ nlm_cop2_disable_irqrestore(flags);
+ if (ret)
+ dev_kfree_skb_any(skb);
+ return NETDEV_TX_OK;
+}
+
+static u16 xlr_net_select_queue(struct net_device *ndev, struct sk_buff *skb,
+ void *accel_priv,
+ select_queue_fallback_t fallback)
+{
+ return (u16)smp_processor_id();
+}
+
+static void xlr_hw_set_mac_addr(struct net_device *ndev)
+{
+ struct xlr_net_priv *priv = netdev_priv(ndev);
+
+ /* set mac station address */
+ xlr_nae_wreg(priv->base_addr, R_MAC_ADDR0,
+ ((ndev->dev_addr[5] << 24) | (ndev->dev_addr[4] << 16) |
+ (ndev->dev_addr[3] << 8) | (ndev->dev_addr[2])));
+ xlr_nae_wreg(priv->base_addr, R_MAC_ADDR0 + 1,
+ ((ndev->dev_addr[1] << 24) | (ndev->dev_addr[0] << 16)));
+
+ xlr_nae_wreg(priv->base_addr, R_MAC_ADDR_MASK2, 0xffffffff);
+ xlr_nae_wreg(priv->base_addr, R_MAC_ADDR_MASK2 + 1, 0xffffffff);
+ xlr_nae_wreg(priv->base_addr, R_MAC_ADDR_MASK3, 0xffffffff);
+ xlr_nae_wreg(priv->base_addr, R_MAC_ADDR_MASK3 + 1, 0xffffffff);
+
+ xlr_nae_wreg(priv->base_addr, R_MAC_FILTER_CONFIG,
+ (1 << O_MAC_FILTER_CONFIG__BROADCAST_EN) |
+ (1 << O_MAC_FILTER_CONFIG__ALL_MCAST_EN) |
+ (1 << O_MAC_FILTER_CONFIG__MAC_ADDR0_VALID));
+
+ if (priv->nd->phy_interface == PHY_INTERFACE_MODE_RGMII ||
+ priv->nd->phy_interface == PHY_INTERFACE_MODE_SGMII)
+ xlr_reg_update(priv->base_addr, R_IPG_IFG, MAC_B2B_IPG, 0x7f);
+}
+
+static int xlr_net_set_mac_addr(struct net_device *ndev, void *data)
+{
+ int err;
+
+ err = eth_mac_addr(ndev, data);
+ if (err)
+ return err;
+ xlr_hw_set_mac_addr(ndev);
+ return 0;
+}
+
+static void xlr_set_rx_mode(struct net_device *ndev)
+{
+ struct xlr_net_priv *priv = netdev_priv(ndev);
+ u32 regval;
+
+ regval = xlr_nae_rdreg(priv->base_addr, R_MAC_FILTER_CONFIG);
+
+ if (ndev->flags & IFF_PROMISC) {
+ regval |= (1 << O_MAC_FILTER_CONFIG__BROADCAST_EN) |
+ (1 << O_MAC_FILTER_CONFIG__PAUSE_FRAME_EN) |
+ (1 << O_MAC_FILTER_CONFIG__ALL_MCAST_EN) |
+ (1 << O_MAC_FILTER_CONFIG__ALL_UCAST_EN);
+ } else {
+ regval &= ~((1 << O_MAC_FILTER_CONFIG__PAUSE_FRAME_EN) |
+ (1 << O_MAC_FILTER_CONFIG__ALL_UCAST_EN));
+ }
+
+ xlr_nae_wreg(priv->base_addr, R_MAC_FILTER_CONFIG, regval);
+}
+
+static void xlr_stats(struct net_device *ndev, struct rtnl_link_stats64 *stats)
+{
+ struct xlr_net_priv *priv = netdev_priv(ndev);
+
+ stats->rx_packets = xlr_nae_rdreg(priv->base_addr, RX_PACKET_COUNTER);
+ stats->tx_packets = xlr_nae_rdreg(priv->base_addr, TX_PACKET_COUNTER);
+ stats->rx_bytes = xlr_nae_rdreg(priv->base_addr, RX_BYTE_COUNTER);
+ stats->tx_bytes = xlr_nae_rdreg(priv->base_addr, TX_BYTE_COUNTER);
+ stats->tx_errors = xlr_nae_rdreg(priv->base_addr, TX_FCS_ERROR_COUNTER);
+ stats->rx_dropped = xlr_nae_rdreg(priv->base_addr,
+ RX_DROP_PACKET_COUNTER);
+ stats->tx_dropped = xlr_nae_rdreg(priv->base_addr,
+ TX_DROP_FRAME_COUNTER);
+
+ stats->multicast = xlr_nae_rdreg(priv->base_addr,
+ RX_MULTICAST_PACKET_COUNTER);
+ stats->collisions = xlr_nae_rdreg(priv->base_addr,
+ TX_TOTAL_COLLISION_COUNTER);
+
+ stats->rx_length_errors = xlr_nae_rdreg(priv->base_addr,
+ RX_FRAME_LENGTH_ERROR_COUNTER);
+ stats->rx_over_errors = xlr_nae_rdreg(priv->base_addr,
+ RX_DROP_PACKET_COUNTER);
+ stats->rx_crc_errors = xlr_nae_rdreg(priv->base_addr,
+ RX_FCS_ERROR_COUNTER);
+ stats->rx_frame_errors = xlr_nae_rdreg(priv->base_addr,
+ RX_ALIGNMENT_ERROR_COUNTER);
+
+ stats->rx_fifo_errors = xlr_nae_rdreg(priv->base_addr,
+ RX_DROP_PACKET_COUNTER);
+ stats->rx_missed_errors = xlr_nae_rdreg(priv->base_addr,
+ RX_CARRIER_SENSE_ERROR_COUNTER);
+
+ stats->rx_errors = (stats->rx_over_errors + stats->rx_crc_errors +
+ stats->rx_frame_errors + stats->rx_fifo_errors +
+ stats->rx_missed_errors);
+
+ stats->tx_aborted_errors = xlr_nae_rdreg(priv->base_addr,
+ TX_EXCESSIVE_COLLISION_PACKET_COUNTER);
+ stats->tx_carrier_errors = xlr_nae_rdreg(priv->base_addr,
+ TX_DROP_FRAME_COUNTER);
+ stats->tx_fifo_errors = xlr_nae_rdreg(priv->base_addr,
+ TX_DROP_FRAME_COUNTER);
+}
+
+static struct rtnl_link_stats64 *xlr_get_stats64(struct net_device *ndev,
+ struct rtnl_link_stats64 *stats)
+{
+ xlr_stats(ndev, stats);
+ return stats;
+}
+
+static struct net_device_ops xlr_netdev_ops = {
+ .ndo_open = xlr_net_open,
+ .ndo_stop = xlr_net_stop,
+ .ndo_start_xmit = xlr_net_start_xmit,
+ .ndo_select_queue = xlr_net_select_queue,
+ .ndo_set_mac_address = xlr_net_set_mac_addr,
+ .ndo_set_rx_mode = xlr_set_rx_mode,
+ .ndo_get_stats64 = xlr_get_stats64,
+};
+
+/*
+ * Gmac init
+ */
+static void *xlr_config_spill(struct xlr_net_priv *priv, int reg_start_0,
+ int reg_start_1, int reg_size, int size)
+{
+ void *spill;
+ u32 *base;
+ unsigned long phys_addr;
+ u32 spill_size;
+
+ base = priv->base_addr;
+ spill_size = size;
+ spill = kmalloc(spill_size + SMP_CACHE_BYTES, GFP_ATOMIC);
+ if (!spill)
+ pr_err("Unable to allocate memory for spill area!\n");
+
+ spill = PTR_ALIGN(spill, SMP_CACHE_BYTES);
+ phys_addr = virt_to_phys(spill);
+ dev_dbg(&priv->ndev->dev, "Allocated spill %d bytes at %lx\n",
+ size, phys_addr);
+ xlr_nae_wreg(base, reg_start_0, (phys_addr >> 5) & 0xffffffff);
+ xlr_nae_wreg(base, reg_start_1, ((u64)phys_addr >> 37) & 0x07);
+ xlr_nae_wreg(base, reg_size, spill_size);
+
+ return spill;
+}
+
+/*
+ * Configure the 6 FIFO's that are used by the network accelarator to
+ * communicate with the rest of the XLx device. 4 of the FIFO's are for
+ * packets from NA --> cpu (called Class FIFO's) and 2 are for feeding
+ * the NA with free descriptors.
+ */
+static void xlr_config_fifo_spill_area(struct xlr_net_priv *priv)
+{
+ priv->frin_spill = xlr_config_spill(priv,
+ R_REG_FRIN_SPILL_MEM_START_0,
+ R_REG_FRIN_SPILL_MEM_START_1,
+ R_REG_FRIN_SPILL_MEM_SIZE,
+ MAX_FRIN_SPILL *
+ sizeof(u64));
+ priv->frout_spill = xlr_config_spill(priv,
+ R_FROUT_SPILL_MEM_START_0,
+ R_FROUT_SPILL_MEM_START_1,
+ R_FROUT_SPILL_MEM_SIZE,
+ MAX_FROUT_SPILL *
+ sizeof(u64));
+ priv->class_0_spill = xlr_config_spill(priv,
+ R_CLASS0_SPILL_MEM_START_0,
+ R_CLASS0_SPILL_MEM_START_1,
+ R_CLASS0_SPILL_MEM_SIZE,
+ MAX_CLASS_0_SPILL *
+ sizeof(u64));
+ priv->class_1_spill = xlr_config_spill(priv,
+ R_CLASS1_SPILL_MEM_START_0,
+ R_CLASS1_SPILL_MEM_START_1,
+ R_CLASS1_SPILL_MEM_SIZE,
+ MAX_CLASS_1_SPILL *
+ sizeof(u64));
+ priv->class_2_spill = xlr_config_spill(priv,
+ R_CLASS2_SPILL_MEM_START_0,
+ R_CLASS2_SPILL_MEM_START_1,
+ R_CLASS2_SPILL_MEM_SIZE,
+ MAX_CLASS_2_SPILL *
+ sizeof(u64));
+ priv->class_3_spill = xlr_config_spill(priv,
+ R_CLASS3_SPILL_MEM_START_0,
+ R_CLASS3_SPILL_MEM_START_1,
+ R_CLASS3_SPILL_MEM_SIZE,
+ MAX_CLASS_3_SPILL *
+ sizeof(u64));
+}
+
+/*
+ * Configure PDE to Round-Robin distribution of packets to the
+ * available cpu
+ */
+static void xlr_config_pde(struct xlr_net_priv *priv)
+{
+ int i = 0;
+ u64 bkt_map = 0;
+
+ /* Each core has 8 buckets(station) */
+ for (i = 0; i < hweight32(priv->nd->cpu_mask); i++)
+ bkt_map |= (0xff << (i * 8));
+
+ xlr_nae_wreg(priv->base_addr, R_PDE_CLASS_0, (bkt_map & 0xffffffff));
+ xlr_nae_wreg(priv->base_addr, R_PDE_CLASS_0 + 1,
+ ((bkt_map >> 32) & 0xffffffff));
+
+ xlr_nae_wreg(priv->base_addr, R_PDE_CLASS_1, (bkt_map & 0xffffffff));
+ xlr_nae_wreg(priv->base_addr, R_PDE_CLASS_1 + 1,
+ ((bkt_map >> 32) & 0xffffffff));
+
+ xlr_nae_wreg(priv->base_addr, R_PDE_CLASS_2, (bkt_map & 0xffffffff));
+ xlr_nae_wreg(priv->base_addr, R_PDE_CLASS_2 + 1,
+ ((bkt_map >> 32) & 0xffffffff));
+
+ xlr_nae_wreg(priv->base_addr, R_PDE_CLASS_3, (bkt_map & 0xffffffff));
+ xlr_nae_wreg(priv->base_addr, R_PDE_CLASS_3 + 1,
+ ((bkt_map >> 32) & 0xffffffff));
+}
+
+/*
+ * Setup the Message ring credits, bucket size and other
+ * common configuration
+ */
+static int xlr_config_common(struct xlr_net_priv *priv)
+{
+ struct xlr_fmn_info *gmac = priv->nd->gmac_fmn_info;
+ int start_stn_id = gmac->start_stn_id;
+ int end_stn_id = gmac->end_stn_id;
+ int *bucket_size = priv->nd->bucket_size;
+ int i, j, err;
+
+ /* Setting non-core MsgBktSize(0x321 - 0x325) */
+ for (i = start_stn_id; i <= end_stn_id; i++) {
+ xlr_nae_wreg(priv->base_addr,
+ R_GMAC_RFR0_BUCKET_SIZE + i - start_stn_id,
+ bucket_size[i]);
+ }
+
+ /*
+ * Setting non-core Credit counter register
+ * Distributing Gmac's credit to CPU's
+ */
+ for (i = 0; i < 8; i++) {
+ for (j = 0; j < 8; j++)
+ xlr_nae_wreg(priv->base_addr,
+ (R_CC_CPU0_0 + (i * 8)) + j,
+ gmac->credit_config[(i * 8) + j]);
+ }
+
+ xlr_nae_wreg(priv->base_addr, R_MSG_TX_THRESHOLD, 3);
+ xlr_nae_wreg(priv->base_addr, R_DMACR0, 0xffffffff);
+ xlr_nae_wreg(priv->base_addr, R_DMACR1, 0xffffffff);
+ xlr_nae_wreg(priv->base_addr, R_DMACR2, 0xffffffff);
+ xlr_nae_wreg(priv->base_addr, R_DMACR3, 0xffffffff);
+ xlr_nae_wreg(priv->base_addr, R_FREEQCARVE, 0);
+
+ err = xlr_net_fill_rx_ring(priv->ndev);
+ if (err)
+ return err;
+ nlm_register_fmn_handler(start_stn_id, end_stn_id, xlr_net_fmn_handler,
+ priv->adapter);
+ return 0;
+}
+
+static void xlr_config_translate_table(struct xlr_net_priv *priv)
+{
+ u32 cpu_mask;
+ u32 val;
+ int bkts[32]; /* one bucket is assumed for each cpu */
+ int b1, b2, c1, c2, i, j, k;
+ int use_bkt;
+
+ use_bkt = 0;
+ cpu_mask = priv->nd->cpu_mask;
+
+ pr_info("Using %s-based distribution\n",
+ (use_bkt) ? "bucket" : "class");
+ j = 0;
+ for (i = 0; i < 32; i++) {
+ if ((1 << i) & cpu_mask) {
+ /* for each cpu, mark the 4+threadid bucket */
+ bkts[j] = ((i / 4) * 8) + (i % 4);
+ j++;
+ }
+ }
+
+ /*configure the 128 * 9 Translation table to send to available buckets*/
+ k = 0;
+ c1 = 3;
+ c2 = 0;
+ for (i = 0; i < 64; i++) {
+ /*
+ * On use_bkt set the b0, b1 are used, else
+ * the 4 classes are used, here implemented
+ * a logic to distribute the packets to the
+ * buckets equally or based on the class
+ */
+ c1 = (c1 + 1) & 3;
+ c2 = (c1 + 1) & 3;
+ b1 = bkts[k];
+ k = (k + 1) % j;
+ b2 = bkts[k];
+ k = (k + 1) % j;
+
+ val = ((c1 << 23) | (b1 << 17) | (use_bkt << 16) |
+ (c2 << 7) | (b2 << 1) | (use_bkt << 0));
+ dev_dbg(&priv->ndev->dev, "Table[%d] b1=%d b2=%d c1=%d c2=%d\n",
+ i, b1, b2, c1, c2);
+ xlr_nae_wreg(priv->base_addr, R_TRANSLATETABLE + i, val);
+ c1 = c2;
+ }
+}
+
+static void xlr_config_parser(struct xlr_net_priv *priv)
+{
+ u32 val;
+
+ /* Mark it as ETHERNET type */
+ xlr_nae_wreg(priv->base_addr, R_L2TYPE_0, 0x01);
+
+ /* Use 7bit CRChash for flow classification with 127 as CRC polynomial*/
+ xlr_nae_wreg(priv->base_addr, R_PARSERCONFIGREG,
+ ((0x7f << 8) | (1 << 1)));
+
+ /* configure the parser : L2 Type is configured in the bootloader */
+ /* extract IP: src, dest protocol */
+ xlr_nae_wreg(priv->base_addr, R_L3CTABLE,
+ (9 << 20) | (1 << 19) | (1 << 18) | (0x01 << 16) |
+ (0x0800 << 0));
+ xlr_nae_wreg(priv->base_addr, R_L3CTABLE + 1,
+ (9 << 25) | (1 << 21) | (12 << 14) | (4 << 10) |
+ (16 << 4) | 4);
+
+ /* Configure to extract SRC port and Dest port for TCP and UDP pkts */
+ xlr_nae_wreg(priv->base_addr, R_L4CTABLE, 6);
+ xlr_nae_wreg(priv->base_addr, R_L4CTABLE + 2, 17);
+ val = ((0 << 21) | (2 << 17) | (2 << 11) | (2 << 7));
+ xlr_nae_wreg(priv->base_addr, R_L4CTABLE + 1, val);
+ xlr_nae_wreg(priv->base_addr, R_L4CTABLE + 3, val);
+
+ xlr_config_translate_table(priv);
+}
+
+static int xlr_phy_write(u32 *base_addr, int phy_addr, int regnum, u16 val)
+{
+ unsigned long timeout, stoptime, checktime;
+ int timedout;
+
+ /* 100ms timeout*/
+ timeout = msecs_to_jiffies(100);
+ stoptime = jiffies + timeout;
+ timedout = 0;
+
+ xlr_nae_wreg(base_addr, R_MII_MGMT_ADDRESS, (phy_addr << 8) | regnum);
+
+ /* Write the data which starts the write cycle */
+ xlr_nae_wreg(base_addr, R_MII_MGMT_WRITE_DATA, (u32) val);
+
+ /* poll for the read cycle to complete */
+ while (!timedout) {
+ checktime = jiffies;
+ if (xlr_nae_rdreg(base_addr, R_MII_MGMT_INDICATORS) == 0)
+ break;
+ timedout = time_after(checktime, stoptime);
+ }
+ if (timedout) {
+ pr_info("Phy device write err: device busy");
+ return -EBUSY;
+ }
+
+ return 0;
+}
+
+static int xlr_phy_read(u32 *base_addr, int phy_addr, int regnum)
+{
+ unsigned long timeout, stoptime, checktime;
+ int timedout;
+
+ /* 100ms timeout*/
+ timeout = msecs_to_jiffies(100);
+ stoptime = jiffies + timeout;
+ timedout = 0;
+
+ /* setup the phy reg to be used */
+ xlr_nae_wreg(base_addr, R_MII_MGMT_ADDRESS,
+ (phy_addr << 8) | (regnum << 0));
+
+ /* Issue the read command */
+ xlr_nae_wreg(base_addr, R_MII_MGMT_COMMAND,
+ (1 << O_MII_MGMT_COMMAND__rstat));
+
+ /* poll for the read cycle to complete */
+ while (!timedout) {
+ checktime = jiffies;
+ if (xlr_nae_rdreg(base_addr, R_MII_MGMT_INDICATORS) == 0)
+ break;
+ timedout = time_after(checktime, stoptime);
+ }
+ if (timedout) {
+ pr_info("Phy device read err: device busy");
+ return -EBUSY;
+ }
+
+ /* clear the read cycle */
+ xlr_nae_wreg(base_addr, R_MII_MGMT_COMMAND, 0);
+
+ /* Read the data */
+ return xlr_nae_rdreg(base_addr, R_MII_MGMT_STATUS);
+}
+
+static int xlr_mii_write(struct mii_bus *bus, int phy_addr, int regnum, u16 val)
+{
+ struct xlr_net_priv *priv = bus->priv;
+ int ret;
+
+ ret = xlr_phy_write(priv->mii_addr, phy_addr, regnum, val);
+ dev_dbg(&priv->ndev->dev, "mii_write phy %d : %d <- %x [%x]\n",
+ phy_addr, regnum, val, ret);
+ return ret;
+}
+
+static int xlr_mii_read(struct mii_bus *bus, int phy_addr, int regnum)
+{
+ struct xlr_net_priv *priv = bus->priv;
+ int ret;
+
+ ret = xlr_phy_read(priv->mii_addr, phy_addr, regnum);
+ dev_dbg(&priv->ndev->dev, "mii_read phy %d : %d [%x]\n",
+ phy_addr, regnum, ret);
+ return ret;
+}
+
+/*
+ * XLR ports are RGMII. XLS ports are SGMII mostly except the port0,
+ * which can be configured either SGMII or RGMII, considered SGMII
+ * by default, if board setup to RGMII the port_type need to set
+ * accordingly.Serdes and PCS layer need to configured for SGMII
+ */
+static void xlr_sgmii_init(struct xlr_net_priv *priv)
+{
+ int phy;
+
+ xlr_phy_write(priv->serdes_addr, 26, 0, 0x6DB0);
+ xlr_phy_write(priv->serdes_addr, 26, 1, 0xFFFF);
+ xlr_phy_write(priv->serdes_addr, 26, 2, 0xB6D0);
+ xlr_phy_write(priv->serdes_addr, 26, 3, 0x00FF);
+ xlr_phy_write(priv->serdes_addr, 26, 4, 0x0000);
+ xlr_phy_write(priv->serdes_addr, 26, 5, 0x0000);
+ xlr_phy_write(priv->serdes_addr, 26, 6, 0x0005);
+ xlr_phy_write(priv->serdes_addr, 26, 7, 0x0001);
+ xlr_phy_write(priv->serdes_addr, 26, 8, 0x0000);
+ xlr_phy_write(priv->serdes_addr, 26, 9, 0x0000);
+ xlr_phy_write(priv->serdes_addr, 26, 10, 0x0000);
+
+ /* program GPIO values for serdes init parameters */
+ xlr_nae_wreg(priv->gpio_addr, 0x20, 0x7e6802);
+ xlr_nae_wreg(priv->gpio_addr, 0x10, 0x7104);
+
+ xlr_nae_wreg(priv->gpio_addr, 0x22, 0x7e6802);
+ xlr_nae_wreg(priv->gpio_addr, 0x21, 0x7104);
+
+ /* enable autoneg - more magic */
+ phy = priv->phy_addr % 4 + 27;
+ xlr_phy_write(priv->pcs_addr, phy, 0, 0x1000);
+ xlr_phy_write(priv->pcs_addr, phy, 0, 0x0200);
+}
+
+void xlr_set_gmac_speed(struct xlr_net_priv *priv)
+{
+ struct phy_device *phydev = priv->mii_bus->phy_map[priv->phy_addr];
+ int speed;
+
+ if (phydev->interface == PHY_INTERFACE_MODE_SGMII)
+ xlr_sgmii_init(priv);
+
+ if (phydev->speed != priv->phy_speed) {
+ speed = phydev->speed;
+ if (speed == SPEED_1000) {
+ /* Set interface to Byte mode */
+ xlr_nae_wreg(priv->base_addr, R_MAC_CONFIG_2, 0x7217);
+ priv->phy_speed = speed;
+ } else if (speed == SPEED_100 || speed == SPEED_10) {
+ /* Set interface to Nibble mode */
+ xlr_nae_wreg(priv->base_addr, R_MAC_CONFIG_2, 0x7117);
+ priv->phy_speed = speed;
+ }
+ /* Set SGMII speed in Interface control reg */
+ if (phydev->interface == PHY_INTERFACE_MODE_SGMII) {
+ if (speed == SPEED_10)
+ xlr_nae_wreg(priv->base_addr,
+ R_INTERFACE_CONTROL, SGMII_SPEED_10);
+ if (speed == SPEED_100)
+ xlr_nae_wreg(priv->base_addr,
+ R_INTERFACE_CONTROL, SGMII_SPEED_100);
+ if (speed == SPEED_1000)
+ xlr_nae_wreg(priv->base_addr,
+ R_INTERFACE_CONTROL, SGMII_SPEED_1000);
+ }
+ if (speed == SPEED_10)
+ xlr_nae_wreg(priv->base_addr, R_CORECONTROL, 0x2);
+ if (speed == SPEED_100)
+ xlr_nae_wreg(priv->base_addr, R_CORECONTROL, 0x1);
+ if (speed == SPEED_1000)
+ xlr_nae_wreg(priv->base_addr, R_CORECONTROL, 0x0);
+ }
+ pr_info("gmac%d : %dMbps\n", priv->port_id, priv->phy_speed);
+}
+
+static void xlr_gmac_link_adjust(struct net_device *ndev)
+{
+ struct xlr_net_priv *priv = netdev_priv(ndev);
+ struct phy_device *phydev = priv->mii_bus->phy_map[priv->phy_addr];
+ u32 intreg;
+
+ intreg = xlr_nae_rdreg(priv->base_addr, R_INTREG);
+ if (phydev->link) {
+ if (phydev->speed != priv->phy_speed) {
+ xlr_set_gmac_speed(priv);
+ pr_info("gmac%d : Link up\n", priv->port_id);
+ }
+ } else {
+ xlr_set_gmac_speed(priv);
+ pr_info("gmac%d : Link down\n", priv->port_id);
+ }
+}
+
+static int xlr_mii_probe(struct xlr_net_priv *priv)
+{
+ struct phy_device *phydev = priv->mii_bus->phy_map[priv->phy_addr];
+
+ if (!phydev) {
+ pr_err("no PHY found on phy_addr %d\n", priv->phy_addr);
+ return -ENODEV;
+ }
+
+ /* Attach MAC to PHY */
+ phydev = phy_connect(priv->ndev, dev_name(&phydev->dev),
+ &xlr_gmac_link_adjust, priv->nd->phy_interface);
+
+ if (IS_ERR(phydev)) {
+ pr_err("could not attach PHY\n");
+ return PTR_ERR(phydev);
+ }
+ phydev->supported &= (ADVERTISED_10baseT_Full
+ | ADVERTISED_10baseT_Half
+ | ADVERTISED_100baseT_Full
+ | ADVERTISED_100baseT_Half
+ | ADVERTISED_1000baseT_Full
+ | ADVERTISED_Autoneg
+ | ADVERTISED_MII);
+
+ phydev->advertising = phydev->supported;
+ pr_info("attached PHY driver [%s] (mii_bus:phy_addr=%s\n",
+ phydev->drv->name, dev_name(&phydev->dev));
+ return 0;
+}
+
+static int xlr_setup_mdio(struct xlr_net_priv *priv,
+ struct platform_device *pdev)
+{
+ int err;
+
+ priv->mii_bus = mdiobus_alloc();
+ if (!priv->mii_bus) {
+ pr_err("mdiobus alloc failed\n");
+ return -ENOMEM;
+ }
+
+ priv->mii_bus->priv = priv;
+ priv->mii_bus->name = "xlr-mdio";
+ snprintf(priv->mii_bus->id, MII_BUS_ID_SIZE, "%s-%d",
+ priv->mii_bus->name, priv->port_id);
+ priv->mii_bus->read = xlr_mii_read;
+ priv->mii_bus->write = xlr_mii_write;
+ priv->mii_bus->parent = &pdev->dev;
+ priv->mii_bus->irq = kmalloc(sizeof(int)*PHY_MAX_ADDR, GFP_KERNEL);
+ if (priv->mii_bus->irq == NULL) {
+ pr_err("irq alloc failed\n");
+ mdiobus_free(priv->mii_bus);
+ return -ENOMEM;
+ }
+
+ priv->mii_bus->irq[priv->phy_addr] = priv->ndev->irq;
+
+ /* Scan only the enabled address */
+ priv->mii_bus->phy_mask = ~(1 << priv->phy_addr);
+
+ /* setting clock divisor to 54 */
+ xlr_nae_wreg(priv->base_addr, R_MII_MGMT_CONFIG, 0x7);
+
+ err = mdiobus_register(priv->mii_bus);
+ if (err) {
+ mdiobus_free(priv->mii_bus);
+ pr_err("mdio bus registration failed\n");
+ return err;
+ }
+
+ pr_info("Registered mdio bus id : %s\n", priv->mii_bus->id);
+ err = xlr_mii_probe(priv);
+ if (err) {
+ mdiobus_free(priv->mii_bus);
+ return err;
+ }
+ return 0;
+}
+
+static void xlr_port_enable(struct xlr_net_priv *priv)
+{
+ u32 prid = (read_c0_prid() & 0xf000);
+
+ /* Setup MAC_CONFIG reg if (xls & rgmii) */
+ if ((prid == 0x8000 || prid == 0x4000 || prid == 0xc000) &&
+ priv->nd->phy_interface == PHY_INTERFACE_MODE_RGMII)
+ xlr_reg_update(priv->base_addr, R_RX_CONTROL,
+ (1 << O_RX_CONTROL__RGMII), (1 << O_RX_CONTROL__RGMII));
+
+ /* Rx Tx enable */
+ xlr_reg_update(priv->base_addr, R_MAC_CONFIG_1,
+ ((1 << O_MAC_CONFIG_1__rxen) | (1 << O_MAC_CONFIG_1__txen) |
+ (1 << O_MAC_CONFIG_1__rxfc) | (1 << O_MAC_CONFIG_1__txfc)),
+ ((1 << O_MAC_CONFIG_1__rxen) | (1 << O_MAC_CONFIG_1__txen) |
+ (1 << O_MAC_CONFIG_1__rxfc) | (1 << O_MAC_CONFIG_1__txfc)));
+
+ /* Setup tx control reg */
+ xlr_reg_update(priv->base_addr, R_TX_CONTROL,
+ ((1 << O_TX_CONTROL__TxEnable) |
+ (512 << O_TX_CONTROL__TxThreshold)), 0x3fff);
+
+ /* Setup rx control reg */
+ xlr_reg_update(priv->base_addr, R_RX_CONTROL,
+ 1 << O_RX_CONTROL__RxEnable, 1 << O_RX_CONTROL__RxEnable);
+}
+
+static void xlr_port_disable(struct xlr_net_priv *priv)
+{
+ /* Setup MAC_CONFIG reg */
+ /* Rx Tx disable*/
+ xlr_reg_update(priv->base_addr, R_MAC_CONFIG_1,
+ ((1 << O_MAC_CONFIG_1__rxen) | (1 << O_MAC_CONFIG_1__txen) |
+ (1 << O_MAC_CONFIG_1__rxfc) | (1 << O_MAC_CONFIG_1__txfc)),
+ 0x0);
+
+ /* Setup tx control reg */
+ xlr_reg_update(priv->base_addr, R_TX_CONTROL,
+ ((1 << O_TX_CONTROL__TxEnable) |
+ (512 << O_TX_CONTROL__TxThreshold)), 0);
+
+ /* Setup rx control reg */
+ xlr_reg_update(priv->base_addr, R_RX_CONTROL,
+ 1 << O_RX_CONTROL__RxEnable, 0);
+}
+
+/*
+ * Initialization of gmac
+ */
+static int xlr_gmac_init(struct xlr_net_priv *priv,
+ struct platform_device *pdev)
+{
+ int ret;
+
+ pr_info("Initializing the gmac%d\n", priv->port_id);
+
+ xlr_port_disable(priv);
+
+ xlr_nae_wreg(priv->base_addr, R_DESC_PACK_CTRL,
+ (1 << O_DESC_PACK_CTRL__MaxEntry)
+ | (BYTE_OFFSET << O_DESC_PACK_CTRL__ByteOffset)
+ | (1600 << O_DESC_PACK_CTRL__RegularSize));
+
+ ret = xlr_setup_mdio(priv, pdev);
+ if (ret)
+ return ret;
+ xlr_port_enable(priv);
+
+ /* Enable Full-duplex/1000Mbps/CRC */
+ xlr_nae_wreg(priv->base_addr, R_MAC_CONFIG_2, 0x7217);
+ /* speed 2.5Mhz */
+ xlr_nae_wreg(priv->base_addr, R_CORECONTROL, 0x02);
+ /* Setup Interrupt mask reg */
+ xlr_nae_wreg(priv->base_addr, R_INTMASK,
+ (1 << O_INTMASK__TxIllegal) |
+ (1 << O_INTMASK__MDInt) |
+ (1 << O_INTMASK__TxFetchError) |
+ (1 << O_INTMASK__P2PSpillEcc) |
+ (1 << O_INTMASK__TagFull) |
+ (1 << O_INTMASK__Underrun) |
+ (1 << O_INTMASK__Abort)
+ );
+
+ /* Clear all stats */
+ xlr_reg_update(priv->base_addr, R_STATCTRL,
+ 0, 1 << O_STATCTRL__ClrCnt);
+ xlr_reg_update(priv->base_addr, R_STATCTRL, 1 << 2,
+ 1 << 2);
+ return 0;
+}
+
+static int xlr_net_probe(struct platform_device *pdev)
+{
+ struct xlr_net_priv *priv = NULL;
+ struct net_device *ndev;
+ struct resource *res;
+ struct xlr_adapter *adapter;
+ int err, port;
+
+ pr_info("XLR/XLS Ethernet Driver controller %d\n", pdev->id);
+ /*
+ * Allocate our adapter data structure and attach it to the device.
+ */
+ adapter = (struct xlr_adapter *)
+ devm_kzalloc(&pdev->dev, sizeof(*adapter), GFP_KERNEL);
+ if (!adapter) {
+ err = -ENOMEM;
+ return err;
+ }
+
+ /*
+ * XLR and XLS have 1 and 2 NAE controller respectively
+ * Each controller has 4 gmac ports, mapping each controller
+ * under one parent device, 4 gmac ports under one device.
+ */
+ for (port = 0; port < pdev->num_resources/2; port++) {
+ ndev = alloc_etherdev_mq(sizeof(struct xlr_net_priv), 32);
+ if (!ndev) {
+ pr_err("Allocation of Ethernet device failed\n");
+ return -ENOMEM;
+ }
+
+ priv = netdev_priv(ndev);
+ priv->pdev = pdev;
+ priv->ndev = ndev;
+ priv->port_id = (pdev->id * 4) + port;
+ priv->nd = (struct xlr_net_data *)pdev->dev.platform_data;
+ res = platform_get_resource(pdev, IORESOURCE_MEM, port);
+
+ if (res == NULL) {
+ pr_err("No memory resource for MAC %d\n",
+ priv->port_id);
+ err = -ENODEV;
+ goto err_gmac;
+ }
+ priv->base_addr = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(priv->base_addr)) {
+ err = PTR_ERR(priv->base_addr);
+ goto err_gmac;
+ }
+ priv->adapter = adapter;
+ adapter->netdev[port] = ndev;
+
+ res = platform_get_resource(pdev, IORESOURCE_IRQ, port);
+ if (res == NULL) {
+ pr_err("No irq resource for MAC %d\n", priv->port_id);
+ err = -ENODEV;
+ goto err_gmac;
+ }
+
+ ndev->irq = res->start;
+
+ priv->phy_addr = priv->nd->phy_addr[port];
+ priv->tx_stnid = priv->nd->tx_stnid[port];
+ priv->mii_addr = priv->nd->mii_addr;
+ priv->serdes_addr = priv->nd->serdes_addr;
+ priv->pcs_addr = priv->nd->pcs_addr;
+ priv->gpio_addr = priv->nd->gpio_addr;
+
+ ndev->netdev_ops = &xlr_netdev_ops;
+ ndev->watchdog_timeo = HZ;
+
+ /* Setup Mac address and Rx mode */
+ eth_hw_addr_random(ndev);
+ xlr_hw_set_mac_addr(ndev);
+ xlr_set_rx_mode(ndev);
+
+ priv->num_rx_desc += MAX_NUM_DESC_SPILL;
+ ndev->ethtool_ops = &xlr_ethtool_ops;
+ SET_NETDEV_DEV(ndev, &pdev->dev);
+
+ xlr_config_fifo_spill_area(priv);
+ /* Configure PDE to Round-Robin pkt distribution */
+ xlr_config_pde(priv);
+ xlr_config_parser(priv);
+
+ /* Call init with respect to port */
+ if (strcmp(res->name, "gmac") == 0) {
+ err = xlr_gmac_init(priv, pdev);
+ if (err) {
+ pr_err("gmac%d init failed\n", priv->port_id);
+ goto err_gmac;
+ }
+ }
+
+ if (priv->port_id == 0 || priv->port_id == 4) {
+ err = xlr_config_common(priv);
+ if (err)
+ goto err_netdev;
+ }
+
+ err = register_netdev(ndev);
+ if (err) {
+ pr_err("Registering netdev failed for gmac%d\n",
+ priv->port_id);
+ goto err_netdev;
+ }
+ platform_set_drvdata(pdev, priv);
+ }
+
+ return 0;
+
+err_netdev:
+ mdiobus_free(priv->mii_bus);
+err_gmac:
+ free_netdev(ndev);
+ return err;
+}
+
+static int xlr_net_remove(struct platform_device *pdev)
+{
+ struct xlr_net_priv *priv = platform_get_drvdata(pdev);
+
+ unregister_netdev(priv->ndev);
+ mdiobus_unregister(priv->mii_bus);
+ mdiobus_free(priv->mii_bus);
+ free_netdev(priv->ndev);
+ return 0;
+}
+
+static struct platform_driver xlr_net_driver = {
+ .probe = xlr_net_probe,
+ .remove = xlr_net_remove,
+ .driver = {
+ .name = "xlr-net",
+ },
+};
+
+module_platform_driver(xlr_net_driver);
+
+MODULE_AUTHOR("Ganesan Ramalingam <ganesanr@broadcom.com>");
+MODULE_DESCRIPTION("Ethernet driver for Netlogic XLR/XLS");
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_ALIAS("platform:xlr-net");
diff --git a/drivers/staging/netlogic/xlr_net.h b/drivers/staging/netlogic/xlr_net.h
new file mode 100644
index 000000000..13e03f0a0
--- /dev/null
+++ b/drivers/staging/netlogic/xlr_net.h
@@ -0,0 +1,1105 @@
+/*
+ * Copyright (c) 2003-2012 Broadcom 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
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the Broadcom
+ * license below:
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY BROADCOM ``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 BROADCOM OR CONTRIBUTORS 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.
+ */
+/* #define MAC_SPLIT_MODE */
+
+#define MAC_SPACING 0x400
+#define XGMAC_SPACING 0x400
+
+/* PE-MCXMAC register and bit field definitions */
+#define R_MAC_CONFIG_1 0x00
+#define O_MAC_CONFIG_1__srst 31
+#define O_MAC_CONFIG_1__simr 30
+#define O_MAC_CONFIG_1__hrrmc 18
+#define W_MAC_CONFIG_1__hrtmc 2
+#define O_MAC_CONFIG_1__hrrfn 16
+#define W_MAC_CONFIG_1__hrtfn 2
+#define O_MAC_CONFIG_1__intlb 8
+#define O_MAC_CONFIG_1__rxfc 5
+#define O_MAC_CONFIG_1__txfc 4
+#define O_MAC_CONFIG_1__srxen 3
+#define O_MAC_CONFIG_1__rxen 2
+#define O_MAC_CONFIG_1__stxen 1
+#define O_MAC_CONFIG_1__txen 0
+#define R_MAC_CONFIG_2 0x01
+#define O_MAC_CONFIG_2__prlen 12
+#define W_MAC_CONFIG_2__prlen 4
+#define O_MAC_CONFIG_2__speed 8
+#define W_MAC_CONFIG_2__speed 2
+#define O_MAC_CONFIG_2__hugen 5
+#define O_MAC_CONFIG_2__flchk 4
+#define O_MAC_CONFIG_2__crce 1
+#define O_MAC_CONFIG_2__fulld 0
+#define R_IPG_IFG 0x02
+#define O_IPG_IFG__ipgr1 24
+#define W_IPG_IFG__ipgr1 7
+#define O_IPG_IFG__ipgr2 16
+#define W_IPG_IFG__ipgr2 7
+#define O_IPG_IFG__mifg 8
+#define W_IPG_IFG__mifg 8
+#define O_IPG_IFG__ipgt 0
+#define W_IPG_IFG__ipgt 7
+#define R_HALF_DUPLEX 0x03
+#define O_HALF_DUPLEX__abebt 24
+#define W_HALF_DUPLEX__abebt 4
+#define O_HALF_DUPLEX__abebe 19
+#define O_HALF_DUPLEX__bpnb 18
+#define O_HALF_DUPLEX__nobo 17
+#define O_HALF_DUPLEX__edxsdfr 16
+#define O_HALF_DUPLEX__retry 12
+#define W_HALF_DUPLEX__retry 4
+#define O_HALF_DUPLEX__lcol 0
+#define W_HALF_DUPLEX__lcol 10
+#define R_MAXIMUM_FRAME_LENGTH 0x04
+#define O_MAXIMUM_FRAME_LENGTH__maxf 0
+#define W_MAXIMUM_FRAME_LENGTH__maxf 16
+#define R_TEST 0x07
+#define O_TEST__mbof 3
+#define O_TEST__rthdf 2
+#define O_TEST__tpause 1
+#define O_TEST__sstct 0
+#define R_MII_MGMT_CONFIG 0x08
+#define O_MII_MGMT_CONFIG__scinc 5
+#define O_MII_MGMT_CONFIG__spre 4
+#define O_MII_MGMT_CONFIG__clks 3
+#define W_MII_MGMT_CONFIG__clks 3
+#define R_MII_MGMT_COMMAND 0x09
+#define O_MII_MGMT_COMMAND__scan 1
+#define O_MII_MGMT_COMMAND__rstat 0
+#define R_MII_MGMT_ADDRESS 0x0A
+#define O_MII_MGMT_ADDRESS__fiad 8
+#define W_MII_MGMT_ADDRESS__fiad 5
+#define O_MII_MGMT_ADDRESS__fgad 5
+#define W_MII_MGMT_ADDRESS__fgad 0
+#define R_MII_MGMT_WRITE_DATA 0x0B
+#define O_MII_MGMT_WRITE_DATA__ctld 0
+#define W_MII_MGMT_WRITE_DATA__ctld 16
+#define R_MII_MGMT_STATUS 0x0C
+#define R_MII_MGMT_INDICATORS 0x0D
+#define O_MII_MGMT_INDICATORS__nvalid 2
+#define O_MII_MGMT_INDICATORS__scan 1
+#define O_MII_MGMT_INDICATORS__busy 0
+#define R_INTERFACE_CONTROL 0x0E
+#define O_INTERFACE_CONTROL__hrstint 31
+#define O_INTERFACE_CONTROL__tbimode 27
+#define O_INTERFACE_CONTROL__ghdmode 26
+#define O_INTERFACE_CONTROL__lhdmode 25
+#define O_INTERFACE_CONTROL__phymod 24
+#define O_INTERFACE_CONTROL__hrrmi 23
+#define O_INTERFACE_CONTROL__rspd 16
+#define O_INTERFACE_CONTROL__hr100 15
+#define O_INTERFACE_CONTROL__frcq 10
+#define O_INTERFACE_CONTROL__nocfr 9
+#define O_INTERFACE_CONTROL__dlfct 8
+#define O_INTERFACE_CONTROL__enjab 0
+#define R_INTERFACE_STATUS 0x0F
+#define O_INTERFACE_STATUS__xsdfr 9
+#define O_INTERFACE_STATUS__ssrr 8
+#define W_INTERFACE_STATUS__ssrr 5
+#define O_INTERFACE_STATUS__miilf 3
+#define O_INTERFACE_STATUS__locar 2
+#define O_INTERFACE_STATUS__sqerr 1
+#define O_INTERFACE_STATUS__jabber 0
+#define R_STATION_ADDRESS_LS 0x10
+#define R_STATION_ADDRESS_MS 0x11
+
+/* A-XGMAC register and bit field definitions */
+#define R_XGMAC_CONFIG_0 0x00
+#define O_XGMAC_CONFIG_0__hstmacrst 31
+#define O_XGMAC_CONFIG_0__hstrstrctl 23
+#define O_XGMAC_CONFIG_0__hstrstrfn 22
+#define O_XGMAC_CONFIG_0__hstrsttctl 18
+#define O_XGMAC_CONFIG_0__hstrsttfn 17
+#define O_XGMAC_CONFIG_0__hstrstmiim 16
+#define O_XGMAC_CONFIG_0__hstloopback 8
+#define R_XGMAC_CONFIG_1 0x01
+#define O_XGMAC_CONFIG_1__hsttctlen 31
+#define O_XGMAC_CONFIG_1__hsttfen 30
+#define O_XGMAC_CONFIG_1__hstrctlen 29
+#define O_XGMAC_CONFIG_1__hstrfen 28
+#define O_XGMAC_CONFIG_1__tfen 26
+#define O_XGMAC_CONFIG_1__rfen 24
+#define O_XGMAC_CONFIG_1__hstrctlshrtp 12
+#define O_XGMAC_CONFIG_1__hstdlyfcstx 10
+#define W_XGMAC_CONFIG_1__hstdlyfcstx 2
+#define O_XGMAC_CONFIG_1__hstdlyfcsrx 8
+#define W_XGMAC_CONFIG_1__hstdlyfcsrx 2
+#define O_XGMAC_CONFIG_1__hstppen 7
+#define O_XGMAC_CONFIG_1__hstbytswp 6
+#define O_XGMAC_CONFIG_1__hstdrplt64 5
+#define O_XGMAC_CONFIG_1__hstprmscrx 4
+#define O_XGMAC_CONFIG_1__hstlenchk 3
+#define O_XGMAC_CONFIG_1__hstgenfcs 2
+#define O_XGMAC_CONFIG_1__hstpadmode 0
+#define W_XGMAC_CONFIG_1__hstpadmode 2
+#define R_XGMAC_CONFIG_2 0x02
+#define O_XGMAC_CONFIG_2__hsttctlfrcp 31
+#define O_XGMAC_CONFIG_2__hstmlnkflth 27
+#define O_XGMAC_CONFIG_2__hstalnkflth 26
+#define O_XGMAC_CONFIG_2__rflnkflt 24
+#define W_XGMAC_CONFIG_2__rflnkflt 2
+#define O_XGMAC_CONFIG_2__hstipgextmod 16
+#define W_XGMAC_CONFIG_2__hstipgextmod 5
+#define O_XGMAC_CONFIG_2__hstrctlfrcp 15
+#define O_XGMAC_CONFIG_2__hstipgexten 5
+#define O_XGMAC_CONFIG_2__hstmipgext 0
+#define W_XGMAC_CONFIG_2__hstmipgext 5
+#define R_XGMAC_CONFIG_3 0x03
+#define O_XGMAC_CONFIG_3__hstfltrfrm 31
+#define W_XGMAC_CONFIG_3__hstfltrfrm 16
+#define O_XGMAC_CONFIG_3__hstfltrfrmdc 15
+#define W_XGMAC_CONFIG_3__hstfltrfrmdc 16
+#define R_XGMAC_STATION_ADDRESS_LS 0x04
+#define O_XGMAC_STATION_ADDRESS_LS__hstmacadr0 0
+#define W_XGMAC_STATION_ADDRESS_LS__hstmacadr0 32
+#define R_XGMAC_STATION_ADDRESS_MS 0x05
+#define R_XGMAC_MAX_FRAME_LEN 0x08
+#define O_XGMAC_MAX_FRAME_LEN__hstmxfrmwctx 16
+#define W_XGMAC_MAX_FRAME_LEN__hstmxfrmwctx 14
+#define O_XGMAC_MAX_FRAME_LEN__hstmxfrmbcrx 0
+#define W_XGMAC_MAX_FRAME_LEN__hstmxfrmbcrx 16
+#define R_XGMAC_REV_LEVEL 0x0B
+#define O_XGMAC_REV_LEVEL__revlvl 0
+#define W_XGMAC_REV_LEVEL__revlvl 15
+#define R_XGMAC_MIIM_COMMAND 0x10
+#define O_XGMAC_MIIM_COMMAND__hstldcmd 3
+#define O_XGMAC_MIIM_COMMAND__hstmiimcmd 0
+#define W_XGMAC_MIIM_COMMAND__hstmiimcmd 3
+#define R_XGMAC_MIIM_FILED 0x11
+#define O_XGMAC_MIIM_FILED__hststfield 30
+#define W_XGMAC_MIIM_FILED__hststfield 2
+#define O_XGMAC_MIIM_FILED__hstopfield 28
+#define W_XGMAC_MIIM_FILED__hstopfield 2
+#define O_XGMAC_MIIM_FILED__hstphyadx 23
+#define W_XGMAC_MIIM_FILED__hstphyadx 5
+#define O_XGMAC_MIIM_FILED__hstregadx 18
+#define W_XGMAC_MIIM_FILED__hstregadx 5
+#define O_XGMAC_MIIM_FILED__hsttafield 16
+#define W_XGMAC_MIIM_FILED__hsttafield 2
+#define O_XGMAC_MIIM_FILED__miimrddat 0
+#define W_XGMAC_MIIM_FILED__miimrddat 16
+#define R_XGMAC_MIIM_CONFIG 0x12
+#define O_XGMAC_MIIM_CONFIG__hstnopram 7
+#define O_XGMAC_MIIM_CONFIG__hstclkdiv 0
+#define W_XGMAC_MIIM_CONFIG__hstclkdiv 7
+#define R_XGMAC_MIIM_LINK_FAIL_VECTOR 0x13
+#define O_XGMAC_MIIM_LINK_FAIL_VECTOR__miimlfvec 0
+#define W_XGMAC_MIIM_LINK_FAIL_VECTOR__miimlfvec 32
+#define R_XGMAC_MIIM_INDICATOR 0x14
+#define O_XGMAC_MIIM_INDICATOR__miimphylf 4
+#define O_XGMAC_MIIM_INDICATOR__miimmoncplt 3
+#define O_XGMAC_MIIM_INDICATOR__miimmonvld 2
+#define O_XGMAC_MIIM_INDICATOR__miimmon 1
+#define O_XGMAC_MIIM_INDICATOR__miimbusy 0
+
+/* GMAC stats registers */
+#define R_RBYT 0x27
+#define R_RPKT 0x28
+#define R_RFCS 0x29
+#define R_RMCA 0x2A
+#define R_RBCA 0x2B
+#define R_RXCF 0x2C
+#define R_RXPF 0x2D
+#define R_RXUO 0x2E
+#define R_RALN 0x2F
+#define R_RFLR 0x30
+#define R_RCDE 0x31
+#define R_RCSE 0x32
+#define R_RUND 0x33
+#define R_ROVR 0x34
+#define R_TBYT 0x38
+#define R_TPKT 0x39
+#define R_TMCA 0x3A
+#define R_TBCA 0x3B
+#define R_TXPF 0x3C
+#define R_TDFR 0x3D
+#define R_TEDF 0x3E
+#define R_TSCL 0x3F
+#define R_TMCL 0x40
+#define R_TLCL 0x41
+#define R_TXCL 0x42
+#define R_TNCL 0x43
+#define R_TJBR 0x46
+#define R_TFCS 0x47
+#define R_TXCF 0x48
+#define R_TOVR 0x49
+#define R_TUND 0x4A
+#define R_TFRG 0x4B
+
+/* Glue logic register and bit field definitions */
+#define R_MAC_ADDR0 0x50
+#define R_MAC_ADDR1 0x52
+#define R_MAC_ADDR2 0x54
+#define R_MAC_ADDR3 0x56
+#define R_MAC_ADDR_MASK2 0x58
+#define R_MAC_ADDR_MASK3 0x5A
+#define R_MAC_FILTER_CONFIG 0x5C
+#define O_MAC_FILTER_CONFIG__BROADCAST_EN 10
+#define O_MAC_FILTER_CONFIG__PAUSE_FRAME_EN 9
+#define O_MAC_FILTER_CONFIG__ALL_MCAST_EN 8
+#define O_MAC_FILTER_CONFIG__ALL_UCAST_EN 7
+#define O_MAC_FILTER_CONFIG__HASH_MCAST_EN 6
+#define O_MAC_FILTER_CONFIG__HASH_UCAST_EN 5
+#define O_MAC_FILTER_CONFIG__ADDR_MATCH_DISC 4
+#define O_MAC_FILTER_CONFIG__MAC_ADDR3_VALID 3
+#define O_MAC_FILTER_CONFIG__MAC_ADDR2_VALID 2
+#define O_MAC_FILTER_CONFIG__MAC_ADDR1_VALID 1
+#define O_MAC_FILTER_CONFIG__MAC_ADDR0_VALID 0
+#define R_HASH_TABLE_VECTOR 0x30
+#define R_TX_CONTROL 0x0A0
+#define O_TX_CONTROL__Tx15Halt 31
+#define O_TX_CONTROL__Tx14Halt 30
+#define O_TX_CONTROL__Tx13Halt 29
+#define O_TX_CONTROL__Tx12Halt 28
+#define O_TX_CONTROL__Tx11Halt 27
+#define O_TX_CONTROL__Tx10Halt 26
+#define O_TX_CONTROL__Tx9Halt 25
+#define O_TX_CONTROL__Tx8Halt 24
+#define O_TX_CONTROL__Tx7Halt 23
+#define O_TX_CONTROL__Tx6Halt 22
+#define O_TX_CONTROL__Tx5Halt 21
+#define O_TX_CONTROL__Tx4Halt 20
+#define O_TX_CONTROL__Tx3Halt 19
+#define O_TX_CONTROL__Tx2Halt 18
+#define O_TX_CONTROL__Tx1Halt 17
+#define O_TX_CONTROL__Tx0Halt 16
+#define O_TX_CONTROL__TxIdle 15
+#define O_TX_CONTROL__TxEnable 14
+#define O_TX_CONTROL__TxThreshold 0
+#define W_TX_CONTROL__TxThreshold 14
+#define R_RX_CONTROL 0x0A1
+#define O_RX_CONTROL__RGMII 10
+#define O_RX_CONTROL__SoftReset 2
+#define O_RX_CONTROL__RxHalt 1
+#define O_RX_CONTROL__RxEnable 0
+#define R_DESC_PACK_CTRL 0x0A2
+#define O_DESC_PACK_CTRL__ByteOffset 17
+#define W_DESC_PACK_CTRL__ByteOffset 3
+#define O_DESC_PACK_CTRL__PrePadEnable 16
+#define O_DESC_PACK_CTRL__MaxEntry 14
+#define W_DESC_PACK_CTRL__MaxEntry 2
+#define O_DESC_PACK_CTRL__RegularSize 0
+#define W_DESC_PACK_CTRL__RegularSize 14
+#define R_STATCTRL 0x0A3
+#define O_STATCTRL__OverFlowEn 4
+#define O_STATCTRL__GIG 3
+#define O_STATCTRL__Sten 2
+#define O_STATCTRL__ClrCnt 1
+#define O_STATCTRL__AutoZ 0
+#define R_L2ALLOCCTRL 0x0A4
+#define O_L2ALLOCCTRL__TxL2Allocate 9
+#define W_L2ALLOCCTRL__TxL2Allocate 9
+#define O_L2ALLOCCTRL__RxL2Allocate 0
+#define W_L2ALLOCCTRL__RxL2Allocate 9
+#define R_INTMASK 0x0A5
+#define O_INTMASK__Spi4TxError 28
+#define O_INTMASK__Spi4RxError 27
+#define O_INTMASK__RGMIIHalfDupCollision 27
+#define O_INTMASK__Abort 26
+#define O_INTMASK__Underrun 25
+#define O_INTMASK__DiscardPacket 24
+#define O_INTMASK__AsyncFifoFull 23
+#define O_INTMASK__TagFull 22
+#define O_INTMASK__Class3Full 21
+#define O_INTMASK__C3EarlyFull 20
+#define O_INTMASK__Class2Full 19
+#define O_INTMASK__C2EarlyFull 18
+#define O_INTMASK__Class1Full 17
+#define O_INTMASK__C1EarlyFull 16
+#define O_INTMASK__Class0Full 15
+#define O_INTMASK__C0EarlyFull 14
+#define O_INTMASK__RxDataFull 13
+#define O_INTMASK__RxEarlyFull 12
+#define O_INTMASK__RFreeEmpty 9
+#define O_INTMASK__RFEarlyEmpty 8
+#define O_INTMASK__P2PSpillEcc 7
+#define O_INTMASK__FreeDescFull 5
+#define O_INTMASK__FreeEarlyFull 4
+#define O_INTMASK__TxFetchError 3
+#define O_INTMASK__StatCarry 2
+#define O_INTMASK__MDInt 1
+#define O_INTMASK__TxIllegal 0
+#define R_INTREG 0x0A6
+#define O_INTREG__Spi4TxError 28
+#define O_INTREG__Spi4RxError 27
+#define O_INTREG__RGMIIHalfDupCollision 27
+#define O_INTREG__Abort 26
+#define O_INTREG__Underrun 25
+#define O_INTREG__DiscardPacket 24
+#define O_INTREG__AsyncFifoFull 23
+#define O_INTREG__TagFull 22
+#define O_INTREG__Class3Full 21
+#define O_INTREG__C3EarlyFull 20
+#define O_INTREG__Class2Full 19
+#define O_INTREG__C2EarlyFull 18
+#define O_INTREG__Class1Full 17
+#define O_INTREG__C1EarlyFull 16
+#define O_INTREG__Class0Full 15
+#define O_INTREG__C0EarlyFull 14
+#define O_INTREG__RxDataFull 13
+#define O_INTREG__RxEarlyFull 12
+#define O_INTREG__RFreeEmpty 9
+#define O_INTREG__RFEarlyEmpty 8
+#define O_INTREG__P2PSpillEcc 7
+#define O_INTREG__FreeDescFull 5
+#define O_INTREG__FreeEarlyFull 4
+#define O_INTREG__TxFetchError 3
+#define O_INTREG__StatCarry 2
+#define O_INTREG__MDInt 1
+#define O_INTREG__TxIllegal 0
+#define R_TXRETRY 0x0A7
+#define O_TXRETRY__CollisionRetry 6
+#define O_TXRETRY__BusErrorRetry 5
+#define O_TXRETRY__UnderRunRetry 4
+#define O_TXRETRY__Retries 0
+#define W_TXRETRY__Retries 4
+#define R_CORECONTROL 0x0A8
+#define O_CORECONTROL__ErrorThread 4
+#define W_CORECONTROL__ErrorThread 7
+#define O_CORECONTROL__Shutdown 2
+#define O_CORECONTROL__Speed 0
+#define W_CORECONTROL__Speed 2
+#define R_BYTEOFFSET0 0x0A9
+#define R_BYTEOFFSET1 0x0AA
+#define R_L2TYPE_0 0x0F0
+#define O_L2TYPE__ExtraHdrProtoSize 26
+#define W_L2TYPE__ExtraHdrProtoSize 5
+#define O_L2TYPE__ExtraHdrProtoOffset 20
+#define W_L2TYPE__ExtraHdrProtoOffset 6
+#define O_L2TYPE__ExtraHeaderSize 14
+#define W_L2TYPE__ExtraHeaderSize 6
+#define O_L2TYPE__ProtoOffset 8
+#define W_L2TYPE__ProtoOffset 6
+#define O_L2TYPE__L2HdrOffset 2
+#define W_L2TYPE__L2HdrOffset 6
+#define O_L2TYPE__L2Proto 0
+#define W_L2TYPE__L2Proto 2
+#define R_L2TYPE_1 0xF0
+#define R_L2TYPE_2 0xF0
+#define R_L2TYPE_3 0xF0
+#define R_PARSERCONFIGREG 0x100
+#define O_PARSERCONFIGREG__CRCHashPoly 8
+#define W_PARSERCONFIGREG__CRCHashPoly 7
+#define O_PARSERCONFIGREG__PrePadOffset 4
+#define W_PARSERCONFIGREG__PrePadOffset 4
+#define O_PARSERCONFIGREG__UseCAM 2
+#define O_PARSERCONFIGREG__UseHASH 1
+#define O_PARSERCONFIGREG__UseProto 0
+#define R_L3CTABLE 0x140
+#define O_L3CTABLE__Offset0 25
+#define W_L3CTABLE__Offset0 7
+#define O_L3CTABLE__Len0 21
+#define W_L3CTABLE__Len0 4
+#define O_L3CTABLE__Offset1 14
+#define W_L3CTABLE__Offset1 7
+#define O_L3CTABLE__Len1 10
+#define W_L3CTABLE__Len1 4
+#define O_L3CTABLE__Offset2 4
+#define W_L3CTABLE__Offset2 6
+#define O_L3CTABLE__Len2 0
+#define W_L3CTABLE__Len2 4
+#define O_L3CTABLE__L3HdrOffset 26
+#define W_L3CTABLE__L3HdrOffset 6
+#define O_L3CTABLE__L4ProtoOffset 20
+#define W_L3CTABLE__L4ProtoOffset 6
+#define O_L3CTABLE__IPChksumCompute 19
+#define O_L3CTABLE__L4Classify 18
+#define O_L3CTABLE__L2Proto 16
+#define W_L3CTABLE__L2Proto 2
+#define O_L3CTABLE__L3ProtoKey 0
+#define W_L3CTABLE__L3ProtoKey 16
+#define R_L4CTABLE 0x160
+#define O_L4CTABLE__Offset0 21
+#define W_L4CTABLE__Offset0 6
+#define O_L4CTABLE__Len0 17
+#define W_L4CTABLE__Len0 4
+#define O_L4CTABLE__Offset1 11
+#define W_L4CTABLE__Offset1 6
+#define O_L4CTABLE__Len1 7
+#define W_L4CTABLE__Len1 4
+#define O_L4CTABLE__TCPChksumEnable 0
+#define R_CAM4X128TABLE 0x172
+#define O_CAM4X128TABLE__ClassId 7
+#define W_CAM4X128TABLE__ClassId 2
+#define O_CAM4X128TABLE__BucketId 1
+#define W_CAM4X128TABLE__BucketId 6
+#define O_CAM4X128TABLE__UseBucket 0
+#define R_CAM4X128KEY 0x180
+#define R_TRANSLATETABLE 0x1A0
+#define R_DMACR0 0x200
+#define O_DMACR0__Data0WrMaxCr 27
+#define W_DMACR0__Data0WrMaxCr 3
+#define O_DMACR0__Data0RdMaxCr 24
+#define W_DMACR0__Data0RdMaxCr 3
+#define O_DMACR0__Data1WrMaxCr 21
+#define W_DMACR0__Data1WrMaxCr 3
+#define O_DMACR0__Data1RdMaxCr 18
+#define W_DMACR0__Data1RdMaxCr 3
+#define O_DMACR0__Data2WrMaxCr 15
+#define W_DMACR0__Data2WrMaxCr 3
+#define O_DMACR0__Data2RdMaxCr 12
+#define W_DMACR0__Data2RdMaxCr 3
+#define O_DMACR0__Data3WrMaxCr 9
+#define W_DMACR0__Data3WrMaxCr 3
+#define O_DMACR0__Data3RdMaxCr 6
+#define W_DMACR0__Data3RdMaxCr 3
+#define O_DMACR0__Data4WrMaxCr 3
+#define W_DMACR0__Data4WrMaxCr 3
+#define O_DMACR0__Data4RdMaxCr 0
+#define W_DMACR0__Data4RdMaxCr 3
+#define R_DMACR1 0x201
+#define O_DMACR1__Data5WrMaxCr 27
+#define W_DMACR1__Data5WrMaxCr 3
+#define O_DMACR1__Data5RdMaxCr 24
+#define W_DMACR1__Data5RdMaxCr 3
+#define O_DMACR1__Data6WrMaxCr 21
+#define W_DMACR1__Data6WrMaxCr 3
+#define O_DMACR1__Data6RdMaxCr 18
+#define W_DMACR1__Data6RdMaxCr 3
+#define O_DMACR1__Data7WrMaxCr 15
+#define W_DMACR1__Data7WrMaxCr 3
+#define O_DMACR1__Data7RdMaxCr 12
+#define W_DMACR1__Data7RdMaxCr 3
+#define O_DMACR1__Data8WrMaxCr 9
+#define W_DMACR1__Data8WrMaxCr 3
+#define O_DMACR1__Data8RdMaxCr 6
+#define W_DMACR1__Data8RdMaxCr 3
+#define O_DMACR1__Data9WrMaxCr 3
+#define W_DMACR1__Data9WrMaxCr 3
+#define O_DMACR1__Data9RdMaxCr 0
+#define W_DMACR1__Data9RdMaxCr 3
+#define R_DMACR2 0x202
+#define O_DMACR2__Data10WrMaxCr 27
+#define W_DMACR2__Data10WrMaxCr 3
+#define O_DMACR2__Data10RdMaxCr 24
+#define W_DMACR2__Data10RdMaxCr 3
+#define O_DMACR2__Data11WrMaxCr 21
+#define W_DMACR2__Data11WrMaxCr 3
+#define O_DMACR2__Data11RdMaxCr 18
+#define W_DMACR2__Data11RdMaxCr 3
+#define O_DMACR2__Data12WrMaxCr 15
+#define W_DMACR2__Data12WrMaxCr 3
+#define O_DMACR2__Data12RdMaxCr 12
+#define W_DMACR2__Data12RdMaxCr 3
+#define O_DMACR2__Data13WrMaxCr 9
+#define W_DMACR2__Data13WrMaxCr 3
+#define O_DMACR2__Data13RdMaxCr 6
+#define W_DMACR2__Data13RdMaxCr 3
+#define O_DMACR2__Data14WrMaxCr 3
+#define W_DMACR2__Data14WrMaxCr 3
+#define O_DMACR2__Data14RdMaxCr 0
+#define W_DMACR2__Data14RdMaxCr 3
+#define R_DMACR3 0x203
+#define O_DMACR3__Data15WrMaxCr 27
+#define W_DMACR3__Data15WrMaxCr 3
+#define O_DMACR3__Data15RdMaxCr 24
+#define W_DMACR3__Data15RdMaxCr 3
+#define O_DMACR3__SpClassWrMaxCr 21
+#define W_DMACR3__SpClassWrMaxCr 3
+#define O_DMACR3__SpClassRdMaxCr 18
+#define W_DMACR3__SpClassRdMaxCr 3
+#define O_DMACR3__JumFrInWrMaxCr 15
+#define W_DMACR3__JumFrInWrMaxCr 3
+#define O_DMACR3__JumFrInRdMaxCr 12
+#define W_DMACR3__JumFrInRdMaxCr 3
+#define O_DMACR3__RegFrInWrMaxCr 9
+#define W_DMACR3__RegFrInWrMaxCr 3
+#define O_DMACR3__RegFrInRdMaxCr 6
+#define W_DMACR3__RegFrInRdMaxCr 3
+#define O_DMACR3__FrOutWrMaxCr 3
+#define W_DMACR3__FrOutWrMaxCr 3
+#define O_DMACR3__FrOutRdMaxCr 0
+#define W_DMACR3__FrOutRdMaxCr 3
+#define R_REG_FRIN_SPILL_MEM_START_0 0x204
+#define O_REG_FRIN_SPILL_MEM_START_0__RegFrInSpillMemStart0 0
+#define W_REG_FRIN_SPILL_MEM_START_0__RegFrInSpillMemStart0 32
+#define R_REG_FRIN_SPILL_MEM_START_1 0x205
+#define O_REG_FRIN_SPILL_MEM_START_1__RegFrInSpillMemStart1 0
+#define W_REG_FRIN_SPILL_MEM_START_1__RegFrInSpillMemStart1 3
+#define R_REG_FRIN_SPILL_MEM_SIZE 0x206
+#define O_REG_FRIN_SPILL_MEM_SIZE__RegFrInSpillMemSize 0
+#define W_REG_FRIN_SPILL_MEM_SIZE__RegFrInSpillMemSize 32
+#define R_FROUT_SPILL_MEM_START_0 0x207
+#define O_FROUT_SPILL_MEM_START_0__FrOutSpillMemStart0 0
+#define W_FROUT_SPILL_MEM_START_0__FrOutSpillMemStart0 32
+#define R_FROUT_SPILL_MEM_START_1 0x208
+#define O_FROUT_SPILL_MEM_START_1__FrOutSpillMemStart1 0
+#define W_FROUT_SPILL_MEM_START_1__FrOutSpillMemStart1 3
+#define R_FROUT_SPILL_MEM_SIZE 0x209
+#define O_FROUT_SPILL_MEM_SIZE__FrOutSpillMemSize 0
+#define W_FROUT_SPILL_MEM_SIZE__FrOutSpillMemSize 32
+#define R_CLASS0_SPILL_MEM_START_0 0x20A
+#define O_CLASS0_SPILL_MEM_START_0__Class0SpillMemStart0 0
+#define W_CLASS0_SPILL_MEM_START_0__Class0SpillMemStart0 32
+#define R_CLASS0_SPILL_MEM_START_1 0x20B
+#define O_CLASS0_SPILL_MEM_START_1__Class0SpillMemStart1 0
+#define W_CLASS0_SPILL_MEM_START_1__Class0SpillMemStart1 3
+#define R_CLASS0_SPILL_MEM_SIZE 0x20C
+#define O_CLASS0_SPILL_MEM_SIZE__Class0SpillMemSize 0
+#define W_CLASS0_SPILL_MEM_SIZE__Class0SpillMemSize 32
+#define R_JUMFRIN_SPILL_MEM_START_0 0x20D
+#define O_JUMFRIN_SPILL_MEM_START_0__JumFrInSpillMemStar0 0
+#define W_JUMFRIN_SPILL_MEM_START_0__JumFrInSpillMemStar0 32
+#define R_JUMFRIN_SPILL_MEM_START_1 0x20E
+#define O_JUMFRIN_SPILL_MEM_START_1__JumFrInSpillMemStart1 0
+#define W_JUMFRIN_SPILL_MEM_START_1__JumFrInSpillMemStart1 3
+#define R_JUMFRIN_SPILL_MEM_SIZE 0x20F
+#define O_JUMFRIN_SPILL_MEM_SIZE__JumFrInSpillMemSize 0
+#define W_JUMFRIN_SPILL_MEM_SIZE__JumFrInSpillMemSize 32
+#define R_CLASS1_SPILL_MEM_START_0 0x210
+#define O_CLASS1_SPILL_MEM_START_0__Class1SpillMemStart0 0
+#define W_CLASS1_SPILL_MEM_START_0__Class1SpillMemStart0 32
+#define R_CLASS1_SPILL_MEM_START_1 0x211
+#define O_CLASS1_SPILL_MEM_START_1__Class1SpillMemStart1 0
+#define W_CLASS1_SPILL_MEM_START_1__Class1SpillMemStart1 3
+#define R_CLASS1_SPILL_MEM_SIZE 0x212
+#define O_CLASS1_SPILL_MEM_SIZE__Class1SpillMemSize 0
+#define W_CLASS1_SPILL_MEM_SIZE__Class1SpillMemSize 32
+#define R_CLASS2_SPILL_MEM_START_0 0x213
+#define O_CLASS2_SPILL_MEM_START_0__Class2SpillMemStart0 0
+#define W_CLASS2_SPILL_MEM_START_0__Class2SpillMemStart0 32
+#define R_CLASS2_SPILL_MEM_START_1 0x214
+#define O_CLASS2_SPILL_MEM_START_1__Class2SpillMemStart1 0
+#define W_CLASS2_SPILL_MEM_START_1__Class2SpillMemStart1 3
+#define R_CLASS2_SPILL_MEM_SIZE 0x215
+#define O_CLASS2_SPILL_MEM_SIZE__Class2SpillMemSize 0
+#define W_CLASS2_SPILL_MEM_SIZE__Class2SpillMemSize 32
+#define R_CLASS3_SPILL_MEM_START_0 0x216
+#define O_CLASS3_SPILL_MEM_START_0__Class3SpillMemStart0 0
+#define W_CLASS3_SPILL_MEM_START_0__Class3SpillMemStart0 32
+#define R_CLASS3_SPILL_MEM_START_1 0x217
+#define O_CLASS3_SPILL_MEM_START_1__Class3SpillMemStart1 0
+#define W_CLASS3_SPILL_MEM_START_1__Class3SpillMemStart1 3
+#define R_CLASS3_SPILL_MEM_SIZE 0x218
+#define O_CLASS3_SPILL_MEM_SIZE__Class3SpillMemSize 0
+#define W_CLASS3_SPILL_MEM_SIZE__Class3SpillMemSize 32
+#define R_REG_FRIN1_SPILL_MEM_START_0 0x219
+#define R_REG_FRIN1_SPILL_MEM_START_1 0x21a
+#define R_REG_FRIN1_SPILL_MEM_SIZE 0x21b
+#define R_SPIHNGY0 0x219
+#define O_SPIHNGY0__EG_HNGY_THRESH_0 24
+#define W_SPIHNGY0__EG_HNGY_THRESH_0 7
+#define O_SPIHNGY0__EG_HNGY_THRESH_1 16
+#define W_SPIHNGY0__EG_HNGY_THRESH_1 7
+#define O_SPIHNGY0__EG_HNGY_THRESH_2 8
+#define W_SPIHNGY0__EG_HNGY_THRESH_2 7
+#define O_SPIHNGY0__EG_HNGY_THRESH_3 0
+#define W_SPIHNGY0__EG_HNGY_THRESH_3 7
+#define R_SPIHNGY1 0x21A
+#define O_SPIHNGY1__EG_HNGY_THRESH_4 24
+#define W_SPIHNGY1__EG_HNGY_THRESH_4 7
+#define O_SPIHNGY1__EG_HNGY_THRESH_5 16
+#define W_SPIHNGY1__EG_HNGY_THRESH_5 7
+#define O_SPIHNGY1__EG_HNGY_THRESH_6 8
+#define W_SPIHNGY1__EG_HNGY_THRESH_6 7
+#define O_SPIHNGY1__EG_HNGY_THRESH_7 0
+#define W_SPIHNGY1__EG_HNGY_THRESH_7 7
+#define R_SPIHNGY2 0x21B
+#define O_SPIHNGY2__EG_HNGY_THRESH_8 24
+#define W_SPIHNGY2__EG_HNGY_THRESH_8 7
+#define O_SPIHNGY2__EG_HNGY_THRESH_9 16
+#define W_SPIHNGY2__EG_HNGY_THRESH_9 7
+#define O_SPIHNGY2__EG_HNGY_THRESH_10 8
+#define W_SPIHNGY2__EG_HNGY_THRESH_10 7
+#define O_SPIHNGY2__EG_HNGY_THRESH_11 0
+#define W_SPIHNGY2__EG_HNGY_THRESH_11 7
+#define R_SPIHNGY3 0x21C
+#define O_SPIHNGY3__EG_HNGY_THRESH_12 24
+#define W_SPIHNGY3__EG_HNGY_THRESH_12 7
+#define O_SPIHNGY3__EG_HNGY_THRESH_13 16
+#define W_SPIHNGY3__EG_HNGY_THRESH_13 7
+#define O_SPIHNGY3__EG_HNGY_THRESH_14 8
+#define W_SPIHNGY3__EG_HNGY_THRESH_14 7
+#define O_SPIHNGY3__EG_HNGY_THRESH_15 0
+#define W_SPIHNGY3__EG_HNGY_THRESH_15 7
+#define R_SPISTRV0 0x21D
+#define O_SPISTRV0__EG_STRV_THRESH_0 24
+#define W_SPISTRV0__EG_STRV_THRESH_0 7
+#define O_SPISTRV0__EG_STRV_THRESH_1 16
+#define W_SPISTRV0__EG_STRV_THRESH_1 7
+#define O_SPISTRV0__EG_STRV_THRESH_2 8
+#define W_SPISTRV0__EG_STRV_THRESH_2 7
+#define O_SPISTRV0__EG_STRV_THRESH_3 0
+#define W_SPISTRV0__EG_STRV_THRESH_3 7
+#define R_SPISTRV1 0x21E
+#define O_SPISTRV1__EG_STRV_THRESH_4 24
+#define W_SPISTRV1__EG_STRV_THRESH_4 7
+#define O_SPISTRV1__EG_STRV_THRESH_5 16
+#define W_SPISTRV1__EG_STRV_THRESH_5 7
+#define O_SPISTRV1__EG_STRV_THRESH_6 8
+#define W_SPISTRV1__EG_STRV_THRESH_6 7
+#define O_SPISTRV1__EG_STRV_THRESH_7 0
+#define W_SPISTRV1__EG_STRV_THRESH_7 7
+#define R_SPISTRV2 0x21F
+#define O_SPISTRV2__EG_STRV_THRESH_8 24
+#define W_SPISTRV2__EG_STRV_THRESH_8 7
+#define O_SPISTRV2__EG_STRV_THRESH_9 16
+#define W_SPISTRV2__EG_STRV_THRESH_9 7
+#define O_SPISTRV2__EG_STRV_THRESH_10 8
+#define W_SPISTRV2__EG_STRV_THRESH_10 7
+#define O_SPISTRV2__EG_STRV_THRESH_11 0
+#define W_SPISTRV2__EG_STRV_THRESH_11 7
+#define R_SPISTRV3 0x220
+#define O_SPISTRV3__EG_STRV_THRESH_12 24
+#define W_SPISTRV3__EG_STRV_THRESH_12 7
+#define O_SPISTRV3__EG_STRV_THRESH_13 16
+#define W_SPISTRV3__EG_STRV_THRESH_13 7
+#define O_SPISTRV3__EG_STRV_THRESH_14 8
+#define W_SPISTRV3__EG_STRV_THRESH_14 7
+#define O_SPISTRV3__EG_STRV_THRESH_15 0
+#define W_SPISTRV3__EG_STRV_THRESH_15 7
+#define R_TXDATAFIFO0 0x221
+#define O_TXDATAFIFO0__Tx0DataFifoStart 24
+#define W_TXDATAFIFO0__Tx0DataFifoStart 7
+#define O_TXDATAFIFO0__Tx0DataFifoSize 16
+#define W_TXDATAFIFO0__Tx0DataFifoSize 7
+#define O_TXDATAFIFO0__Tx1DataFifoStart 8
+#define W_TXDATAFIFO0__Tx1DataFifoStart 7
+#define O_TXDATAFIFO0__Tx1DataFifoSize 0
+#define W_TXDATAFIFO0__Tx1DataFifoSize 7
+#define R_TXDATAFIFO1 0x222
+#define O_TXDATAFIFO1__Tx2DataFifoStart 24
+#define W_TXDATAFIFO1__Tx2DataFifoStart 7
+#define O_TXDATAFIFO1__Tx2DataFifoSize 16
+#define W_TXDATAFIFO1__Tx2DataFifoSize 7
+#define O_TXDATAFIFO1__Tx3DataFifoStart 8
+#define W_TXDATAFIFO1__Tx3DataFifoStart 7
+#define O_TXDATAFIFO1__Tx3DataFifoSize 0
+#define W_TXDATAFIFO1__Tx3DataFifoSize 7
+#define R_TXDATAFIFO2 0x223
+#define O_TXDATAFIFO2__Tx4DataFifoStart 24
+#define W_TXDATAFIFO2__Tx4DataFifoStart 7
+#define O_TXDATAFIFO2__Tx4DataFifoSize 16
+#define W_TXDATAFIFO2__Tx4DataFifoSize 7
+#define O_TXDATAFIFO2__Tx5DataFifoStart 8
+#define W_TXDATAFIFO2__Tx5DataFifoStart 7
+#define O_TXDATAFIFO2__Tx5DataFifoSize 0
+#define W_TXDATAFIFO2__Tx5DataFifoSize 7
+#define R_TXDATAFIFO3 0x224
+#define O_TXDATAFIFO3__Tx6DataFifoStart 24
+#define W_TXDATAFIFO3__Tx6DataFifoStart 7
+#define O_TXDATAFIFO3__Tx6DataFifoSize 16
+#define W_TXDATAFIFO3__Tx6DataFifoSize 7
+#define O_TXDATAFIFO3__Tx7DataFifoStart 8
+#define W_TXDATAFIFO3__Tx7DataFifoStart 7
+#define O_TXDATAFIFO3__Tx7DataFifoSize 0
+#define W_TXDATAFIFO3__Tx7DataFifoSize 7
+#define R_TXDATAFIFO4 0x225
+#define O_TXDATAFIFO4__Tx8DataFifoStart 24
+#define W_TXDATAFIFO4__Tx8DataFifoStart 7
+#define O_TXDATAFIFO4__Tx8DataFifoSize 16
+#define W_TXDATAFIFO4__Tx8DataFifoSize 7
+#define O_TXDATAFIFO4__Tx9DataFifoStart 8
+#define W_TXDATAFIFO4__Tx9DataFifoStart 7
+#define O_TXDATAFIFO4__Tx9DataFifoSize 0
+#define W_TXDATAFIFO4__Tx9DataFifoSize 7
+#define R_TXDATAFIFO5 0x226
+#define O_TXDATAFIFO5__Tx10DataFifoStart 24
+#define W_TXDATAFIFO5__Tx10DataFifoStart 7
+#define O_TXDATAFIFO5__Tx10DataFifoSize 16
+#define W_TXDATAFIFO5__Tx10DataFifoSize 7
+#define O_TXDATAFIFO5__Tx11DataFifoStart 8
+#define W_TXDATAFIFO5__Tx11DataFifoStart 7
+#define O_TXDATAFIFO5__Tx11DataFifoSize 0
+#define W_TXDATAFIFO5__Tx11DataFifoSize 7
+#define R_TXDATAFIFO6 0x227
+#define O_TXDATAFIFO6__Tx12DataFifoStart 24
+#define W_TXDATAFIFO6__Tx12DataFifoStart 7
+#define O_TXDATAFIFO6__Tx12DataFifoSize 16
+#define W_TXDATAFIFO6__Tx12DataFifoSize 7
+#define O_TXDATAFIFO6__Tx13DataFifoStart 8
+#define W_TXDATAFIFO6__Tx13DataFifoStart 7
+#define O_TXDATAFIFO6__Tx13DataFifoSize 0
+#define W_TXDATAFIFO6__Tx13DataFifoSize 7
+#define R_TXDATAFIFO7 0x228
+#define O_TXDATAFIFO7__Tx14DataFifoStart 24
+#define W_TXDATAFIFO7__Tx14DataFifoStart 7
+#define O_TXDATAFIFO7__Tx14DataFifoSize 16
+#define W_TXDATAFIFO7__Tx14DataFifoSize 7
+#define O_TXDATAFIFO7__Tx15DataFifoStart 8
+#define W_TXDATAFIFO7__Tx15DataFifoStart 7
+#define O_TXDATAFIFO7__Tx15DataFifoSize 0
+#define W_TXDATAFIFO7__Tx15DataFifoSize 7
+#define R_RXDATAFIFO0 0x229
+#define O_RXDATAFIFO0__Rx0DataFifoStart 24
+#define W_RXDATAFIFO0__Rx0DataFifoStart 7
+#define O_RXDATAFIFO0__Rx0DataFifoSize 16
+#define W_RXDATAFIFO0__Rx0DataFifoSize 7
+#define O_RXDATAFIFO0__Rx1DataFifoStart 8
+#define W_RXDATAFIFO0__Rx1DataFifoStart 7
+#define O_RXDATAFIFO0__Rx1DataFifoSize 0
+#define W_RXDATAFIFO0__Rx1DataFifoSize 7
+#define R_RXDATAFIFO1 0x22A
+#define O_RXDATAFIFO1__Rx2DataFifoStart 24
+#define W_RXDATAFIFO1__Rx2DataFifoStart 7
+#define O_RXDATAFIFO1__Rx2DataFifoSize 16
+#define W_RXDATAFIFO1__Rx2DataFifoSize 7
+#define O_RXDATAFIFO1__Rx3DataFifoStart 8
+#define W_RXDATAFIFO1__Rx3DataFifoStart 7
+#define O_RXDATAFIFO1__Rx3DataFifoSize 0
+#define W_RXDATAFIFO1__Rx3DataFifoSize 7
+#define R_RXDATAFIFO2 0x22B
+#define O_RXDATAFIFO2__Rx4DataFifoStart 24
+#define W_RXDATAFIFO2__Rx4DataFifoStart 7
+#define O_RXDATAFIFO2__Rx4DataFifoSize 16
+#define W_RXDATAFIFO2__Rx4DataFifoSize 7
+#define O_RXDATAFIFO2__Rx5DataFifoStart 8
+#define W_RXDATAFIFO2__Rx5DataFifoStart 7
+#define O_RXDATAFIFO2__Rx5DataFifoSize 0
+#define W_RXDATAFIFO2__Rx5DataFifoSize 7
+#define R_RXDATAFIFO3 0x22C
+#define O_RXDATAFIFO3__Rx6DataFifoStart 24
+#define W_RXDATAFIFO3__Rx6DataFifoStart 7
+#define O_RXDATAFIFO3__Rx6DataFifoSize 16
+#define W_RXDATAFIFO3__Rx6DataFifoSize 7
+#define O_RXDATAFIFO3__Rx7DataFifoStart 8
+#define W_RXDATAFIFO3__Rx7DataFifoStart 7
+#define O_RXDATAFIFO3__Rx7DataFifoSize 0
+#define W_RXDATAFIFO3__Rx7DataFifoSize 7
+#define R_RXDATAFIFO4 0x22D
+#define O_RXDATAFIFO4__Rx8DataFifoStart 24
+#define W_RXDATAFIFO4__Rx8DataFifoStart 7
+#define O_RXDATAFIFO4__Rx8DataFifoSize 16
+#define W_RXDATAFIFO4__Rx8DataFifoSize 7
+#define O_RXDATAFIFO4__Rx9DataFifoStart 8
+#define W_RXDATAFIFO4__Rx9DataFifoStart 7
+#define O_RXDATAFIFO4__Rx9DataFifoSize 0
+#define W_RXDATAFIFO4__Rx9DataFifoSize 7
+#define R_RXDATAFIFO5 0x22E
+#define O_RXDATAFIFO5__Rx10DataFifoStart 24
+#define W_RXDATAFIFO5__Rx10DataFifoStart 7
+#define O_RXDATAFIFO5__Rx10DataFifoSize 16
+#define W_RXDATAFIFO5__Rx10DataFifoSize 7
+#define O_RXDATAFIFO5__Rx11DataFifoStart 8
+#define W_RXDATAFIFO5__Rx11DataFifoStart 7
+#define O_RXDATAFIFO5__Rx11DataFifoSize 0
+#define W_RXDATAFIFO5__Rx11DataFifoSize 7
+#define R_RXDATAFIFO6 0x22F
+#define O_RXDATAFIFO6__Rx12DataFifoStart 24
+#define W_RXDATAFIFO6__Rx12DataFifoStart 7
+#define O_RXDATAFIFO6__Rx12DataFifoSize 16
+#define W_RXDATAFIFO6__Rx12DataFifoSize 7
+#define O_RXDATAFIFO6__Rx13DataFifoStart 8
+#define W_RXDATAFIFO6__Rx13DataFifoStart 7
+#define O_RXDATAFIFO6__Rx13DataFifoSize 0
+#define W_RXDATAFIFO6__Rx13DataFifoSize 7
+#define R_RXDATAFIFO7 0x230
+#define O_RXDATAFIFO7__Rx14DataFifoStart 24
+#define W_RXDATAFIFO7__Rx14DataFifoStart 7
+#define O_RXDATAFIFO7__Rx14DataFifoSize 16
+#define W_RXDATAFIFO7__Rx14DataFifoSize 7
+#define O_RXDATAFIFO7__Rx15DataFifoStart 8
+#define W_RXDATAFIFO7__Rx15DataFifoStart 7
+#define O_RXDATAFIFO7__Rx15DataFifoSize 0
+#define W_RXDATAFIFO7__Rx15DataFifoSize 7
+#define R_XGMACPADCALIBRATION 0x231
+#define R_FREEQCARVE 0x233
+#define R_SPI4STATICDELAY0 0x240
+#define O_SPI4STATICDELAY0__DataLine7 28
+#define W_SPI4STATICDELAY0__DataLine7 4
+#define O_SPI4STATICDELAY0__DataLine6 24
+#define W_SPI4STATICDELAY0__DataLine6 4
+#define O_SPI4STATICDELAY0__DataLine5 20
+#define W_SPI4STATICDELAY0__DataLine5 4
+#define O_SPI4STATICDELAY0__DataLine4 16
+#define W_SPI4STATICDELAY0__DataLine4 4
+#define O_SPI4STATICDELAY0__DataLine3 12
+#define W_SPI4STATICDELAY0__DataLine3 4
+#define O_SPI4STATICDELAY0__DataLine2 8
+#define W_SPI4STATICDELAY0__DataLine2 4
+#define O_SPI4STATICDELAY0__DataLine1 4
+#define W_SPI4STATICDELAY0__DataLine1 4
+#define O_SPI4STATICDELAY0__DataLine0 0
+#define W_SPI4STATICDELAY0__DataLine0 4
+#define R_SPI4STATICDELAY1 0x241
+#define O_SPI4STATICDELAY1__DataLine15 28
+#define W_SPI4STATICDELAY1__DataLine15 4
+#define O_SPI4STATICDELAY1__DataLine14 24
+#define W_SPI4STATICDELAY1__DataLine14 4
+#define O_SPI4STATICDELAY1__DataLine13 20
+#define W_SPI4STATICDELAY1__DataLine13 4
+#define O_SPI4STATICDELAY1__DataLine12 16
+#define W_SPI4STATICDELAY1__DataLine12 4
+#define O_SPI4STATICDELAY1__DataLine11 12
+#define W_SPI4STATICDELAY1__DataLine11 4
+#define O_SPI4STATICDELAY1__DataLine10 8
+#define W_SPI4STATICDELAY1__DataLine10 4
+#define O_SPI4STATICDELAY1__DataLine9 4
+#define W_SPI4STATICDELAY1__DataLine9 4
+#define O_SPI4STATICDELAY1__DataLine8 0
+#define W_SPI4STATICDELAY1__DataLine8 4
+#define R_SPI4STATICDELAY2 0x242
+#define O_SPI4STATICDELAY0__TxStat1 8
+#define W_SPI4STATICDELAY0__TxStat1 4
+#define O_SPI4STATICDELAY0__TxStat0 4
+#define W_SPI4STATICDELAY0__TxStat0 4
+#define O_SPI4STATICDELAY0__RxControl 0
+#define W_SPI4STATICDELAY0__RxControl 4
+#define R_SPI4CONTROL 0x243
+#define O_SPI4CONTROL__StaticDelay 2
+#define O_SPI4CONTROL__LVDS_LVTTL 1
+#define O_SPI4CONTROL__SPI4Enable 0
+#define R_CLASSWATERMARKS 0x244
+#define O_CLASSWATERMARKS__Class0Watermark 24
+#define W_CLASSWATERMARKS__Class0Watermark 5
+#define O_CLASSWATERMARKS__Class1Watermark 16
+#define W_CLASSWATERMARKS__Class1Watermark 5
+#define O_CLASSWATERMARKS__Class3Watermark 0
+#define W_CLASSWATERMARKS__Class3Watermark 5
+#define R_RXWATERMARKS1 0x245
+#define O_RXWATERMARKS__Rx0DataWatermark 24
+#define W_RXWATERMARKS__Rx0DataWatermark 7
+#define O_RXWATERMARKS__Rx1DataWatermark 16
+#define W_RXWATERMARKS__Rx1DataWatermark 7
+#define O_RXWATERMARKS__Rx3DataWatermark 0
+#define W_RXWATERMARKS__Rx3DataWatermark 7
+#define R_RXWATERMARKS2 0x246
+#define O_RXWATERMARKS__Rx4DataWatermark 24
+#define W_RXWATERMARKS__Rx4DataWatermark 7
+#define O_RXWATERMARKS__Rx5DataWatermark 16
+#define W_RXWATERMARKS__Rx5DataWatermark 7
+#define O_RXWATERMARKS__Rx6DataWatermark 8
+#define W_RXWATERMARKS__Rx6DataWatermark 7
+#define O_RXWATERMARKS__Rx7DataWatermark 0
+#define W_RXWATERMARKS__Rx7DataWatermark 7
+#define R_RXWATERMARKS3 0x247
+#define O_RXWATERMARKS__Rx8DataWatermark 24
+#define W_RXWATERMARKS__Rx8DataWatermark 7
+#define O_RXWATERMARKS__Rx9DataWatermark 16
+#define W_RXWATERMARKS__Rx9DataWatermark 7
+#define O_RXWATERMARKS__Rx10DataWatermark 8
+#define W_RXWATERMARKS__Rx10DataWatermark 7
+#define O_RXWATERMARKS__Rx11DataWatermark 0
+#define W_RXWATERMARKS__Rx11DataWatermark 7
+#define R_RXWATERMARKS4 0x248
+#define O_RXWATERMARKS__Rx12DataWatermark 24
+#define W_RXWATERMARKS__Rx12DataWatermark 7
+#define O_RXWATERMARKS__Rx13DataWatermark 16
+#define W_RXWATERMARKS__Rx13DataWatermark 7
+#define O_RXWATERMARKS__Rx14DataWatermark 8
+#define W_RXWATERMARKS__Rx14DataWatermark 7
+#define O_RXWATERMARKS__Rx15DataWatermark 0
+#define W_RXWATERMARKS__Rx15DataWatermark 7
+#define R_FREEWATERMARKS 0x249
+#define O_FREEWATERMARKS__FreeOutWatermark 16
+#define W_FREEWATERMARKS__FreeOutWatermark 16
+#define O_FREEWATERMARKS__JumFrWatermark 8
+#define W_FREEWATERMARKS__JumFrWatermark 7
+#define O_FREEWATERMARKS__RegFrWatermark 0
+#define W_FREEWATERMARKS__RegFrWatermark 7
+#define R_EGRESSFIFOCARVINGSLOTS 0x24a
+
+#define CTRL_RES0 0
+#define CTRL_RES1 1
+#define CTRL_REG_FREE 2
+#define CTRL_JUMBO_FREE 3
+#define CTRL_CONT 4
+#define CTRL_EOP 5
+#define CTRL_START 6
+#define CTRL_SNGL 7
+
+#define CTRL_B0_NOT_EOP 0
+#define CTRL_B0_EOP 1
+
+#define R_ROUND_ROBIN_TABLE 0
+#define R_PDE_CLASS_0 0x300
+#define R_PDE_CLASS_1 0x302
+#define R_PDE_CLASS_2 0x304
+#define R_PDE_CLASS_3 0x306
+
+#define R_MSG_TX_THRESHOLD 0x308
+
+#define R_GMAC_JFR0_BUCKET_SIZE 0x320
+#define R_GMAC_RFR0_BUCKET_SIZE 0x321
+#define R_GMAC_TX0_BUCKET_SIZE 0x322
+#define R_GMAC_TX1_BUCKET_SIZE 0x323
+#define R_GMAC_TX2_BUCKET_SIZE 0x324
+#define R_GMAC_TX3_BUCKET_SIZE 0x325
+#define R_GMAC_JFR1_BUCKET_SIZE 0x326
+#define R_GMAC_RFR1_BUCKET_SIZE 0x327
+
+#define R_XGS_TX0_BUCKET_SIZE 0x320
+#define R_XGS_TX1_BUCKET_SIZE 0x321
+#define R_XGS_TX2_BUCKET_SIZE 0x322
+#define R_XGS_TX3_BUCKET_SIZE 0x323
+#define R_XGS_TX4_BUCKET_SIZE 0x324
+#define R_XGS_TX5_BUCKET_SIZE 0x325
+#define R_XGS_TX6_BUCKET_SIZE 0x326
+#define R_XGS_TX7_BUCKET_SIZE 0x327
+#define R_XGS_TX8_BUCKET_SIZE 0x328
+#define R_XGS_TX9_BUCKET_SIZE 0x329
+#define R_XGS_TX10_BUCKET_SIZE 0x32A
+#define R_XGS_TX11_BUCKET_SIZE 0x32B
+#define R_XGS_TX12_BUCKET_SIZE 0x32C
+#define R_XGS_TX13_BUCKET_SIZE 0x32D
+#define R_XGS_TX14_BUCKET_SIZE 0x32E
+#define R_XGS_TX15_BUCKET_SIZE 0x32F
+#define R_XGS_JFR_BUCKET_SIZE 0x330
+#define R_XGS_RFR_BUCKET_SIZE 0x331
+
+#define R_CC_CPU0_0 0x380
+#define R_CC_CPU1_0 0x388
+#define R_CC_CPU2_0 0x390
+#define R_CC_CPU3_0 0x398
+#define R_CC_CPU4_0 0x3a0
+#define R_CC_CPU5_0 0x3a8
+#define R_CC_CPU6_0 0x3b0
+#define R_CC_CPU7_0 0x3b8
+
+#define XLR_GMAC_BLK_SZ (XLR_IO_GMAC_1_OFFSET - \
+ XLR_IO_GMAC_0_OFFSET)
+
+/* Constants used for configuring the devices */
+
+#define XLR_FB_STN 6 /* Bucket used for Tx freeback */
+
+#define MAC_B2B_IPG 88
+
+#define XLR_NET_PREPAD_LEN 32
+
+/* frame sizes need to be cacheline aligned */
+#define MAX_FRAME_SIZE (1536 + XLR_NET_PREPAD_LEN)
+#define MAX_FRAME_SIZE_JUMBO 9216
+
+#define MAC_SKB_BACK_PTR_SIZE SMP_CACHE_BYTES
+#define MAC_PREPAD 0
+#define BYTE_OFFSET 2
+#define XLR_RX_BUF_SIZE (MAX_FRAME_SIZE + BYTE_OFFSET + \
+ MAC_PREPAD + MAC_SKB_BACK_PTR_SIZE + SMP_CACHE_BYTES)
+#define MAC_CRC_LEN 4
+#define MAX_NUM_MSGRNG_STN_CC 128
+#define MAX_MSG_SND_ATTEMPTS 100 /* 13 stns x 4 entry msg/stn +
+ headroom */
+
+#define MAC_FRIN_TO_BE_SENT_THRESHOLD 16
+
+#define MAX_NUM_DESC_SPILL 1024
+#define MAX_FRIN_SPILL (MAX_NUM_DESC_SPILL << 2)
+#define MAX_FROUT_SPILL (MAX_NUM_DESC_SPILL << 2)
+#define MAX_CLASS_0_SPILL (MAX_NUM_DESC_SPILL << 2)
+#define MAX_CLASS_1_SPILL (MAX_NUM_DESC_SPILL << 2)
+#define MAX_CLASS_2_SPILL (MAX_NUM_DESC_SPILL << 2)
+#define MAX_CLASS_3_SPILL (MAX_NUM_DESC_SPILL << 2)
+
+enum {
+ SGMII_SPEED_10 = 0x00000000,
+ SGMII_SPEED_100 = 0x02000000,
+ SGMII_SPEED_1000 = 0x04000000,
+};
+
+enum tsv_rsv_reg {
+ TX_RX_64_BYTE_FRAME = 0x20,
+ TX_RX_64_127_BYTE_FRAME,
+ TX_RX_128_255_BYTE_FRAME,
+ TX_RX_256_511_BYTE_FRAME,
+ TX_RX_512_1023_BYTE_FRAME,
+ TX_RX_1024_1518_BYTE_FRAME,
+ TX_RX_1519_1522_VLAN_BYTE_FRAME,
+
+ RX_BYTE_COUNTER = 0x27,
+ RX_PACKET_COUNTER,
+ RX_FCS_ERROR_COUNTER,
+ RX_MULTICAST_PACKET_COUNTER,
+ RX_BROADCAST_PACKET_COUNTER,
+ RX_CONTROL_FRAME_PACKET_COUNTER,
+ RX_PAUSE_FRAME_PACKET_COUNTER,
+ RX_UNKNOWN_OP_CODE_COUNTER,
+ RX_ALIGNMENT_ERROR_COUNTER,
+ RX_FRAME_LENGTH_ERROR_COUNTER,
+ RX_CODE_ERROR_COUNTER,
+ RX_CARRIER_SENSE_ERROR_COUNTER,
+ RX_UNDERSIZE_PACKET_COUNTER,
+ RX_OVERSIZE_PACKET_COUNTER,
+ RX_FRAGMENTS_COUNTER,
+ RX_JABBER_COUNTER,
+ RX_DROP_PACKET_COUNTER,
+
+ TX_BYTE_COUNTER = 0x38,
+ TX_PACKET_COUNTER,
+ TX_MULTICAST_PACKET_COUNTER,
+ TX_BROADCAST_PACKET_COUNTER,
+ TX_PAUSE_CONTROL_FRAME_COUNTER,
+ TX_DEFERRAL_PACKET_COUNTER,
+ TX_EXCESSIVE_DEFERRAL_PACKET_COUNTER,
+ TX_SINGLE_COLLISION_PACKET_COUNTER,
+ TX_MULTI_COLLISION_PACKET_COUNTER,
+ TX_LATE_COLLISION_PACKET_COUNTER,
+ TX_EXCESSIVE_COLLISION_PACKET_COUNTER,
+ TX_TOTAL_COLLISION_COUNTER,
+ TX_PAUSE_FRAME_HONERED_COUNTER,
+ TX_DROP_FRAME_COUNTER,
+ TX_JABBER_FRAME_COUNTER,
+ TX_FCS_ERROR_COUNTER,
+ TX_CONTROL_FRAME_COUNTER,
+ TX_OVERSIZE_FRAME_COUNTER,
+ TX_UNDERSIZE_FRAME_COUNTER,
+ TX_FRAGMENT_FRAME_COUNTER,
+
+ CARRY_REG_1 = 0x4c,
+ CARRY_REG_2 = 0x4d,
+};
+
+struct xlr_adapter {
+ struct net_device *netdev[4];
+};
+
+struct xlr_net_priv {
+ u32 __iomem *base_addr;
+ struct net_device *ndev;
+ struct xlr_adapter *adapter;
+ struct mii_bus *mii_bus;
+ int num_rx_desc;
+ int phy_addr; /* PHY addr on MDIO bus */
+ int pcs_id; /* PCS id on MDIO bus */
+ int port_id; /* Port(gmac/xgmac) number, i.e 0-7 */
+ int tx_stnid;
+ u32 __iomem *mii_addr;
+ u32 __iomem *serdes_addr;
+ u32 __iomem *pcs_addr;
+ u32 __iomem *gpio_addr;
+ int phy_speed;
+ int port_type;
+ struct timer_list queue_timer;
+ int wakeup_q;
+ struct platform_device *pdev;
+ struct xlr_net_data *nd;
+
+ u64 *frin_spill;
+ u64 *frout_spill;
+ u64 *class_0_spill;
+ u64 *class_1_spill;
+ u64 *class_2_spill;
+ u64 *class_3_spill;
+};
+
+extern void xlr_set_gmac_speed(struct xlr_net_priv *priv);
diff --git a/drivers/staging/nvec/Kconfig b/drivers/staging/nvec/Kconfig
new file mode 100644
index 000000000..e3a89fb1a
--- /dev/null
+++ b/drivers/staging/nvec/Kconfig
@@ -0,0 +1,53 @@
+config MFD_NVEC
+ tristate "NV Tegra Embedded Controller SMBus Interface"
+ depends on I2C && GPIOLIB && ARCH_TEGRA
+ select MFD_CORE
+ help
+ Say Y here to enable support for a nVidia compliant embedded
+ controller.
+
+ To compile this driver as a module, say M here: the module will be
+ called mfd-nvec
+
+config KEYBOARD_NVEC
+ tristate "Keyboard on nVidia compliant EC"
+ depends on MFD_NVEC && INPUT
+ help
+ Say Y here to enable support for a keyboard connected to
+ a nVidia compliant embedded controller.
+
+ To compile this driver as a module, say M here: the module will be
+ called keyboard-nvec
+
+config SERIO_NVEC_PS2
+ tristate "PS2 on nVidia EC"
+ depends on MFD_NVEC && SERIO
+ help
+ Say Y here to enable support for a Touchpad / Mouse connected
+ to a nVidia compliant embedded controller.
+
+ To compile this driver as a module, say M here: the module will be
+ called serio-nvec-ps2
+
+
+config NVEC_POWER
+ tristate "NVEC charger and battery"
+ depends on MFD_NVEC && POWER_SUPPLY
+ help
+ Say Y to enable support for battery and charger interface for
+ nVidia compliant embedded controllers.
+
+ To compile this driver as a module, say M here: the module will be
+ called nvec-power
+
+
+config NVEC_PAZ00
+ tristate "Support for OEM specific functions on Compal PAZ00 based devices"
+ depends on MFD_NVEC && LEDS_CLASS
+ help
+ Say Y to enable control of the yellow side leds on Compal PAZ00 based
+ devices, e.g. Toshbia AC100 and Dynabooks AZ netbooks.
+
+ To compile this driver as a module, say M here: the module will be
+ called nvec-paz00
+
diff --git a/drivers/staging/nvec/Makefile b/drivers/staging/nvec/Makefile
new file mode 100644
index 000000000..0db0e1f43
--- /dev/null
+++ b/drivers/staging/nvec/Makefile
@@ -0,0 +1,5 @@
+obj-$(CONFIG_SERIO_NVEC_PS2) += nvec_ps2.o
+obj-$(CONFIG_MFD_NVEC) += nvec.o
+obj-$(CONFIG_NVEC_POWER) += nvec_power.o
+obj-$(CONFIG_KEYBOARD_NVEC) += nvec_kbd.o
+obj-$(CONFIG_NVEC_PAZ00) += nvec_paz00.o
diff --git a/drivers/staging/nvec/README b/drivers/staging/nvec/README
new file mode 100644
index 000000000..9a320b7fd
--- /dev/null
+++ b/drivers/staging/nvec/README
@@ -0,0 +1,14 @@
+NVEC: An NVidia compliant Embedded Controller Protocol Implemenation
+
+This is an implementation of the NVEC protocol used to communicate with an
+embedded controller (EC) via I2C bus. The EC is an I2C master while the host
+processor is the I2C slave. Requests from the host processor to the EC are
+started by triggering a gpio line.
+
+There is no written documentation of the protocol available to the public,
+but the source code[1] of the published nvec reference drivers can be a guide.
+This driver is currently only used by the AC100 project[2], but it is likely,
+that other Tegra boards (not yet mainlined, if ever) also use it.
+
+[1] e.g. http://nv-tegra.nvidia.com/gitweb/?p=linux-2.6.git;a=tree;f=arch/arm/mach-tegra/nvec;hb=android-tegra-2.6.32
+[2] http://gitorious.org/ac100, http://launchpad.net/ac100
diff --git a/drivers/staging/nvec/TODO b/drivers/staging/nvec/TODO
new file mode 100644
index 000000000..e5ae42a0b
--- /dev/null
+++ b/drivers/staging/nvec/TODO
@@ -0,0 +1,8 @@
+ToDo list (incomplete, unordered)
+ - add compile as module support
+ - move half of the nvec init stuff to i2c-tegra.c
+ - move event handling to nvec_events
+ - finish suspend/resume support
+ - modifiy the sync_write method to return the received
+ message in a variable (and return the error code).
+ - add support for more device implementations
diff --git a/drivers/staging/nvec/nvec-keytable.h b/drivers/staging/nvec/nvec-keytable.h
new file mode 100644
index 000000000..1dc22cb88
--- /dev/null
+++ b/drivers/staging/nvec/nvec-keytable.h
@@ -0,0 +1,307 @@
+/*
+ * drivers/input/keyboard/tegra-nvec.c
+ *
+ * Keyboard class input driver for keyboards connected to an NvEc compliant
+ * embedded controller
+ *
+ * Copyright (c) 2009, NVIDIA Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+static unsigned short code_tab_102us[] = {
+ /* 0x00 */
+ KEY_GRAVE,
+ KEY_ESC,
+ KEY_1,
+ KEY_2,
+ KEY_3,
+ KEY_4,
+ KEY_5,
+ KEY_6,
+ KEY_7,
+ KEY_8,
+ KEY_9,
+ KEY_0,
+ KEY_MINUS,
+ KEY_EQUAL,
+ KEY_BACKSPACE,
+ KEY_TAB,
+ /* 0x10 */
+ KEY_Q,
+ KEY_W,
+ KEY_E,
+ KEY_R,
+ KEY_T,
+ KEY_Y,
+ KEY_U,
+ KEY_I,
+ KEY_O,
+ KEY_P,
+ KEY_LEFTBRACE,
+ KEY_RIGHTBRACE,
+ KEY_ENTER,
+ KEY_LEFTCTRL,
+ KEY_A,
+ KEY_S,
+ /* 0x20 */
+ KEY_D,
+ KEY_F,
+ KEY_G,
+ KEY_H,
+ KEY_J,
+ KEY_K,
+ KEY_L,
+ KEY_SEMICOLON,
+ KEY_APOSTROPHE,
+ KEY_GRAVE,
+ KEY_LEFTSHIFT,
+ KEY_BACKSLASH,
+ KEY_Z,
+ KEY_X,
+ KEY_C,
+ KEY_V,
+ /* 0x30 */
+ KEY_B,
+ KEY_N,
+ KEY_M,
+ KEY_COMMA,
+ KEY_DOT,
+ KEY_SLASH,
+ KEY_RIGHTSHIFT,
+ KEY_KPASTERISK,
+ KEY_LEFTALT,
+ KEY_SPACE,
+ KEY_CAPSLOCK,
+ KEY_F1,
+ KEY_F2,
+ KEY_F3,
+ KEY_F4,
+ KEY_F5,
+ /* 0x40 */
+ KEY_F6,
+ KEY_F7,
+ KEY_F8,
+ KEY_F9,
+ KEY_F10,
+ KEY_FN,
+ /* VK_SCROLL */
+ 0,
+ KEY_KP7,
+ KEY_KP8,
+ KEY_KP9,
+ KEY_KPMINUS,
+ KEY_KP4,
+ KEY_KP5,
+ KEY_KP6,
+ KEY_KPPLUS,
+ KEY_KP1,
+ /* 0x50 */
+ KEY_KP2,
+ KEY_KP3,
+ KEY_KP0,
+ KEY_KPDOT,
+ /* VK_SNAPSHOT */
+ KEY_MENU,
+ KEY_POWER,
+ /* VK_OEM_102 */
+ KEY_102ND,
+ KEY_F11,
+ KEY_F12,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ /* 0x60 */
+ 0,
+ 0,
+ 0,
+ KEY_SEARCH,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ /* 0x70 */
+ 0,
+ 0,
+ 0,
+ KEY_KP5,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ KEY_KP9,
+};
+
+static unsigned short extcode_tab_us102[] = {
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ /* 0x10 */
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ /* VK_MEDIA_NEXT_TRACK */
+ 0,
+ 0,
+ 0,
+ /* VK_RETURN */
+ 0,
+ KEY_RIGHTCTRL,
+ 0,
+ 0,
+ /* 0x20 */
+ KEY_MUTE,
+ /* VK_LAUNCH_APP1 */
+ 0,
+ /* VK_MEDIA_PLAY_PAUSE */
+ 0,
+ 0,
+ /* VK_MEDIA_STOP */
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ /* 0x30 */
+ KEY_VOLUMEUP,
+ 0,
+ /* VK_BROWSER_HOME */
+ 0,
+ 0,
+ 0,
+ /* VK_DIVIDE */
+ KEY_KPSLASH,
+ 0,
+ /* VK_SNAPSHOT */
+ KEY_SYSRQ,
+ /* VK_RMENU */
+ KEY_RIGHTALT,
+ /* VK_OEM_NV_BACKLIGHT_UP */
+ 0,
+ /* VK_OEM_NV_BACKLIGHT_DN */
+ 0,
+ /* VK_OEM_NV_BACKLIGHT_AUTOTOGGLE */
+ 0,
+ /* VK_OEM_NV_POWER_INFO */
+ 0,
+ /* VK_OEM_NV_WIFI_TOGGLE */
+ 0,
+ /* VK_OEM_NV_DISPLAY_SELECT */
+ 0,
+ /* VK_OEM_NV_AIRPLANE_TOGGLE */
+ 0,
+ /* 0x40 */
+ 0,
+ KEY_LEFT,
+ 0,
+ 0,
+ 0,
+ 0,
+ KEY_CANCEL,
+ KEY_HOME,
+ KEY_UP,
+ KEY_PAGEUP,
+ 0,
+ KEY_LEFT,
+ 0,
+ KEY_RIGHT,
+ 0,
+ KEY_END,
+ /* 0x50 */
+ KEY_DOWN,
+ KEY_PAGEDOWN,
+ KEY_INSERT,
+ KEY_DELETE,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ KEY_LEFTMETA,
+ 0,
+ KEY_ESC,
+ KEY_KPMINUS,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ /* VK_BROWSER_SEARCH */
+ 0,
+ /* VK_BROWSER_FAVORITES */
+ 0,
+ /* VK_BROWSER_REFRESH */
+ 0,
+ /* VK_BROWSER_STOP */
+ 0,
+ /* VK_BROWSER_FORWARD */
+ 0,
+ /* VK_BROWSER_BACK */
+ 0,
+ /* VK_LAUNCH_APP2 */
+ 0,
+ /* VK_LAUNCH_MAIL */
+ 0,
+ /* VK_LAUNCH_MEDIA_SELECT */
+ 0,
+};
+
+static unsigned short *code_tabs[] = { code_tab_102us, extcode_tab_us102 };
diff --git a/drivers/staging/nvec/nvec.c b/drivers/staging/nvec/nvec.c
new file mode 100644
index 000000000..1bdc8d001
--- /dev/null
+++ b/drivers/staging/nvec/nvec.c
@@ -0,0 +1,983 @@
+/*
+ * NVEC: NVIDIA compliant embedded controller interface
+ *
+ * Copyright (C) 2011 The AC100 Kernel Team <ac100@lists.lauchpad.net>
+ *
+ * Authors: Pierre-Hugues Husson <phhusson@free.fr>
+ * Ilya Petrov <ilya.muromec@gmail.com>
+ * Marc Dietrich <marvin24@gmx.de>
+ * Julian Andres Klode <jak@jak-linux.org>
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ */
+
+/* #define DEBUG */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/atomic.h>
+#include <linux/clk.h>
+#include <linux/completion.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/gpio.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/irq.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
+#include <linux/list.h>
+#include <linux/mfd/core.h>
+#include <linux/mutex.h>
+#include <linux/notifier.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/workqueue.h>
+
+#include "nvec.h"
+
+#define I2C_CNFG 0x00
+#define I2C_CNFG_PACKET_MODE_EN (1<<10)
+#define I2C_CNFG_NEW_MASTER_SFM (1<<11)
+#define I2C_CNFG_DEBOUNCE_CNT_SHIFT 12
+
+#define I2C_SL_CNFG 0x20
+#define I2C_SL_NEWSL (1<<2)
+#define I2C_SL_NACK (1<<1)
+#define I2C_SL_RESP (1<<0)
+#define I2C_SL_IRQ (1<<3)
+#define END_TRANS (1<<4)
+#define RCVD (1<<2)
+#define RNW (1<<1)
+
+#define I2C_SL_RCVD 0x24
+#define I2C_SL_STATUS 0x28
+#define I2C_SL_ADDR1 0x2c
+#define I2C_SL_ADDR2 0x30
+#define I2C_SL_DELAY_COUNT 0x3c
+
+/**
+ * enum nvec_msg_category - Message categories for nvec_msg_alloc()
+ * @NVEC_MSG_RX: The message is an incoming message (from EC)
+ * @NVEC_MSG_TX: The message is an outgoing message (to EC)
+ */
+enum nvec_msg_category {
+ NVEC_MSG_RX,
+ NVEC_MSG_TX,
+};
+
+enum nvec_sleep_subcmds {
+ GLOBAL_EVENTS,
+ AP_PWR_DOWN,
+ AP_SUSPEND,
+};
+
+#define CNF_EVENT_REPORTING 0x01
+#define GET_FIRMWARE_VERSION 0x15
+#define LID_SWITCH BIT(1)
+#define PWR_BUTTON BIT(15)
+
+static struct nvec_chip *nvec_power_handle;
+
+static const struct mfd_cell nvec_devices[] = {
+ {
+ .name = "nvec-kbd",
+ },
+ {
+ .name = "nvec-mouse",
+ },
+ {
+ .name = "nvec-power",
+ .id = 0,
+ },
+ {
+ .name = "nvec-power",
+ .id = 1,
+ },
+ {
+ .name = "nvec-paz00",
+ },
+};
+
+/**
+ * nvec_register_notifier - Register a notifier with nvec
+ * @nvec: A &struct nvec_chip
+ * @nb: The notifier block to register
+ *
+ * Registers a notifier with @nvec. The notifier will be added to an atomic
+ * notifier chain that is called for all received messages except those that
+ * correspond to a request initiated by nvec_write_sync().
+ */
+int nvec_register_notifier(struct nvec_chip *nvec, struct notifier_block *nb,
+ unsigned int events)
+{
+ return atomic_notifier_chain_register(&nvec->notifier_list, nb);
+}
+EXPORT_SYMBOL_GPL(nvec_register_notifier);
+
+/**
+ * nvec_unregister_notifier - Unregister a notifier with nvec
+ * @nvec: A &struct nvec_chip
+ * @nb: The notifier block to unregister
+ *
+ * Unregisters a notifier with @nvec. The notifier will be removed from the
+ * atomic notifier chain.
+ */
+int nvec_unregister_notifier(struct nvec_chip *nvec, struct notifier_block *nb)
+{
+ return atomic_notifier_chain_unregister(&nvec->notifier_list, nb);
+}
+EXPORT_SYMBOL_GPL(nvec_unregister_notifier);
+
+/**
+ * nvec_status_notifier - The final notifier
+ *
+ * Prints a message about control events not handled in the notifier
+ * chain.
+ */
+static int nvec_status_notifier(struct notifier_block *nb,
+ unsigned long event_type, void *data)
+{
+ struct nvec_chip *nvec = container_of(nb, struct nvec_chip,
+ nvec_status_notifier);
+ unsigned char *msg = (unsigned char *)data;
+
+ if (event_type != NVEC_CNTL)
+ return NOTIFY_DONE;
+
+ dev_warn(nvec->dev, "unhandled msg type %ld\n", event_type);
+ print_hex_dump(KERN_WARNING, "payload: ", DUMP_PREFIX_NONE, 16, 1,
+ msg, msg[1] + 2, true);
+
+ return NOTIFY_OK;
+}
+
+/**
+ * nvec_msg_alloc:
+ * @nvec: A &struct nvec_chip
+ * @category: Pool category, see &enum nvec_msg_category
+ *
+ * Allocate a single &struct nvec_msg object from the message pool of
+ * @nvec. The result shall be passed to nvec_msg_free() if no longer
+ * used.
+ *
+ * Outgoing messages are placed in the upper 75% of the pool, keeping the
+ * lower 25% available for RX buffers only. The reason is to prevent a
+ * situation where all buffers are full and a message is thus endlessly
+ * retried because the response could never be processed.
+ */
+static struct nvec_msg *nvec_msg_alloc(struct nvec_chip *nvec,
+ enum nvec_msg_category category)
+{
+ int i = (category == NVEC_MSG_TX) ? (NVEC_POOL_SIZE / 4) : 0;
+
+ for (; i < NVEC_POOL_SIZE; i++) {
+ if (atomic_xchg(&nvec->msg_pool[i].used, 1) == 0) {
+ dev_vdbg(nvec->dev, "INFO: Allocate %i\n", i);
+ return &nvec->msg_pool[i];
+ }
+ }
+
+ dev_err(nvec->dev, "could not allocate %s buffer\n",
+ (category == NVEC_MSG_TX) ? "TX" : "RX");
+
+ return NULL;
+}
+
+/**
+ * nvec_msg_free:
+ * @nvec: A &struct nvec_chip
+ * @msg: A message (must be allocated by nvec_msg_alloc() and belong to @nvec)
+ *
+ * Free the given message
+ */
+void nvec_msg_free(struct nvec_chip *nvec, struct nvec_msg *msg)
+{
+ if (msg != &nvec->tx_scratch)
+ dev_vdbg(nvec->dev, "INFO: Free %ti\n", msg - nvec->msg_pool);
+ atomic_set(&msg->used, 0);
+}
+EXPORT_SYMBOL_GPL(nvec_msg_free);
+
+/**
+ * nvec_msg_is_event - Return %true if @msg is an event
+ * @msg: A message
+ */
+static bool nvec_msg_is_event(struct nvec_msg *msg)
+{
+ return msg->data[0] >> 7;
+}
+
+/**
+ * nvec_msg_size - Get the size of a message
+ * @msg: The message to get the size for
+ *
+ * This only works for received messages, not for outgoing messages.
+ */
+static size_t nvec_msg_size(struct nvec_msg *msg)
+{
+ bool is_event = nvec_msg_is_event(msg);
+ int event_length = (msg->data[0] & 0x60) >> 5;
+
+ /* for variable size, payload size in byte 1 + count (1) + cmd (1) */
+ if (!is_event || event_length == NVEC_VAR_SIZE)
+ return (msg->pos || msg->size) ? (msg->data[1] + 2) : 0;
+ else if (event_length == NVEC_2BYTES)
+ return 2;
+ else if (event_length == NVEC_3BYTES)
+ return 3;
+ return 0;
+}
+
+/**
+ * nvec_gpio_set_value - Set the GPIO value
+ * @nvec: A &struct nvec_chip
+ * @value: The value to write (0 or 1)
+ *
+ * Like gpio_set_value(), but generating debugging information
+ */
+static void nvec_gpio_set_value(struct nvec_chip *nvec, int value)
+{
+ dev_dbg(nvec->dev, "GPIO changed from %u to %u\n",
+ gpio_get_value(nvec->gpio), value);
+ gpio_set_value(nvec->gpio, value);
+}
+
+/**
+ * nvec_write_async - Asynchronously write a message to NVEC
+ * @nvec: An nvec_chip instance
+ * @data: The message data, starting with the request type
+ * @size: The size of @data
+ *
+ * Queue a single message to be transferred to the embedded controller
+ * and return immediately.
+ *
+ * Returns: 0 on success, a negative error code on failure. If a failure
+ * occurred, the nvec driver may print an error.
+ */
+int nvec_write_async(struct nvec_chip *nvec, const unsigned char *data,
+ short size)
+{
+ struct nvec_msg *msg;
+ unsigned long flags;
+
+ msg = nvec_msg_alloc(nvec, NVEC_MSG_TX);
+
+ if (msg == NULL)
+ return -ENOMEM;
+
+ msg->data[0] = size;
+ memcpy(msg->data + 1, data, size);
+ msg->size = size + 1;
+
+ spin_lock_irqsave(&nvec->tx_lock, flags);
+ list_add_tail(&msg->node, &nvec->tx_data);
+ spin_unlock_irqrestore(&nvec->tx_lock, flags);
+
+ schedule_work(&nvec->tx_work);
+
+ return 0;
+}
+EXPORT_SYMBOL(nvec_write_async);
+
+/**
+ * nvec_write_sync - Write a message to nvec and read the response
+ * @nvec: An &struct nvec_chip
+ * @data: The data to write
+ * @size: The size of @data
+ *
+ * This is similar to nvec_write_async(), but waits for the
+ * request to be answered before returning. This function
+ * uses a mutex and can thus not be called from e.g.
+ * interrupt handlers.
+ *
+ * Returns: A pointer to the response message on success,
+ * %NULL on failure. Free with nvec_msg_free() once no longer
+ * used.
+ */
+struct nvec_msg *nvec_write_sync(struct nvec_chip *nvec,
+ const unsigned char *data, short size)
+{
+ struct nvec_msg *msg;
+
+ mutex_lock(&nvec->sync_write_mutex);
+
+ nvec->sync_write_pending = (data[1] << 8) + data[0];
+
+ if (nvec_write_async(nvec, data, size) < 0) {
+ mutex_unlock(&nvec->sync_write_mutex);
+ return NULL;
+ }
+
+ dev_dbg(nvec->dev, "nvec_sync_write: 0x%04x\n",
+ nvec->sync_write_pending);
+ if (!(wait_for_completion_timeout(&nvec->sync_write,
+ msecs_to_jiffies(2000)))) {
+ dev_warn(nvec->dev, "timeout waiting for sync write to complete\n");
+ mutex_unlock(&nvec->sync_write_mutex);
+ return NULL;
+ }
+
+ dev_dbg(nvec->dev, "nvec_sync_write: pong!\n");
+
+ msg = nvec->last_sync_msg;
+
+ mutex_unlock(&nvec->sync_write_mutex);
+
+ return msg;
+}
+EXPORT_SYMBOL(nvec_write_sync);
+
+/**
+ * nvec_toggle_global_events - enables or disables global event reporting
+ * @nvec: nvec handle
+ * @state: true for enable, false for disable
+ *
+ * This switches on/off global event reports by the embedded controller.
+ */
+static void nvec_toggle_global_events(struct nvec_chip *nvec, bool state)
+{
+ unsigned char global_events[] = { NVEC_SLEEP, GLOBAL_EVENTS, state };
+
+ nvec_write_async(nvec, global_events, 3);
+}
+
+/**
+ * nvec_event_mask - fill the command string with event bitfield
+ * ev: points to event command string
+ * mask: bit to insert into the event mask
+ *
+ * Configure event command expects a 32 bit bitfield which describes
+ * which events to enable. The bitfield has the following structure
+ * (from highest byte to lowest):
+ * system state bits 7-0
+ * system state bits 15-8
+ * oem system state bits 7-0
+ * oem system state bits 15-8
+ */
+static void nvec_event_mask(char *ev, u32 mask)
+{
+ ev[3] = mask >> 16 & 0xff;
+ ev[4] = mask >> 24 & 0xff;
+ ev[5] = mask >> 0 & 0xff;
+ ev[6] = mask >> 8 & 0xff;
+}
+
+/**
+ * nvec_request_master - Process outgoing messages
+ * @work: A &struct work_struct (the tx_worker member of &struct nvec_chip)
+ *
+ * Processes all outgoing requests by sending the request and awaiting the
+ * response, then continuing with the next request. Once a request has a
+ * matching response, it will be freed and removed from the list.
+ */
+static void nvec_request_master(struct work_struct *work)
+{
+ struct nvec_chip *nvec = container_of(work, struct nvec_chip, tx_work);
+ unsigned long flags;
+ long err;
+ struct nvec_msg *msg;
+
+ spin_lock_irqsave(&nvec->tx_lock, flags);
+ while (!list_empty(&nvec->tx_data)) {
+ msg = list_first_entry(&nvec->tx_data, struct nvec_msg, node);
+ spin_unlock_irqrestore(&nvec->tx_lock, flags);
+ nvec_gpio_set_value(nvec, 0);
+ err = wait_for_completion_interruptible_timeout(
+ &nvec->ec_transfer, msecs_to_jiffies(5000));
+
+ if (err == 0) {
+ dev_warn(nvec->dev, "timeout waiting for ec transfer\n");
+ nvec_gpio_set_value(nvec, 1);
+ msg->pos = 0;
+ }
+
+ spin_lock_irqsave(&nvec->tx_lock, flags);
+
+ if (err > 0) {
+ list_del_init(&msg->node);
+ nvec_msg_free(nvec, msg);
+ }
+ }
+ spin_unlock_irqrestore(&nvec->tx_lock, flags);
+}
+
+/**
+ * parse_msg - Print some information and call the notifiers on an RX message
+ * @nvec: A &struct nvec_chip
+ * @msg: A message received by @nvec
+ *
+ * Paarse some pieces of the message and then call the chain of notifiers
+ * registered via nvec_register_notifier.
+ */
+static int parse_msg(struct nvec_chip *nvec, struct nvec_msg *msg)
+{
+ if ((msg->data[0] & 1 << 7) == 0 && msg->data[3]) {
+ dev_err(nvec->dev, "ec responded %*ph\n", 4, msg->data);
+ return -EINVAL;
+ }
+
+ if ((msg->data[0] >> 7) == 1 && (msg->data[0] & 0x0f) == 5)
+ print_hex_dump(KERN_WARNING, "ec system event ",
+ DUMP_PREFIX_NONE, 16, 1, msg->data,
+ msg->data[1] + 2, true);
+
+ atomic_notifier_call_chain(&nvec->notifier_list, msg->data[0] & 0x8f,
+ msg->data);
+
+ return 0;
+}
+
+/**
+ * nvec_dispatch - Process messages received from the EC
+ * @work: A &struct work_struct (the tx_worker member of &struct nvec_chip)
+ *
+ * Process messages previously received from the EC and put into the RX
+ * queue of the &struct nvec_chip instance associated with @work.
+ */
+static void nvec_dispatch(struct work_struct *work)
+{
+ struct nvec_chip *nvec = container_of(work, struct nvec_chip, rx_work);
+ unsigned long flags;
+ struct nvec_msg *msg;
+
+ spin_lock_irqsave(&nvec->rx_lock, flags);
+ while (!list_empty(&nvec->rx_data)) {
+ msg = list_first_entry(&nvec->rx_data, struct nvec_msg, node);
+ list_del_init(&msg->node);
+ spin_unlock_irqrestore(&nvec->rx_lock, flags);
+
+ if (nvec->sync_write_pending ==
+ (msg->data[2] << 8) + msg->data[0]) {
+ dev_dbg(nvec->dev, "sync write completed!\n");
+ nvec->sync_write_pending = 0;
+ nvec->last_sync_msg = msg;
+ complete(&nvec->sync_write);
+ } else {
+ parse_msg(nvec, msg);
+ nvec_msg_free(nvec, msg);
+ }
+ spin_lock_irqsave(&nvec->rx_lock, flags);
+ }
+ spin_unlock_irqrestore(&nvec->rx_lock, flags);
+}
+
+/**
+ * nvec_tx_completed - Complete the current transfer
+ * @nvec: A &struct nvec_chip
+ *
+ * This is called when we have received an END_TRANS on a TX transfer.
+ */
+static void nvec_tx_completed(struct nvec_chip *nvec)
+{
+ /* We got an END_TRANS, let's skip this, maybe there's an event */
+ if (nvec->tx->pos != nvec->tx->size) {
+ dev_err(nvec->dev, "premature END_TRANS, resending\n");
+ nvec->tx->pos = 0;
+ nvec_gpio_set_value(nvec, 0);
+ } else {
+ nvec->state = 0;
+ }
+}
+
+/**
+ * nvec_rx_completed - Complete the current transfer
+ * @nvec: A &struct nvec_chip
+ *
+ * This is called when we have received an END_TRANS on a RX transfer.
+ */
+static void nvec_rx_completed(struct nvec_chip *nvec)
+{
+ if (nvec->rx->pos != nvec_msg_size(nvec->rx)) {
+ dev_err(nvec->dev, "RX incomplete: Expected %u bytes, got %u\n",
+ (uint) nvec_msg_size(nvec->rx),
+ (uint) nvec->rx->pos);
+
+ nvec_msg_free(nvec, nvec->rx);
+ nvec->state = 0;
+
+ /* Battery quirk - Often incomplete, and likes to crash */
+ if (nvec->rx->data[0] == NVEC_BAT)
+ complete(&nvec->ec_transfer);
+
+ return;
+ }
+
+ spin_lock(&nvec->rx_lock);
+
+ /* add the received data to the work list
+ and move the ring buffer pointer to the next entry */
+ list_add_tail(&nvec->rx->node, &nvec->rx_data);
+
+ spin_unlock(&nvec->rx_lock);
+
+ nvec->state = 0;
+
+ if (!nvec_msg_is_event(nvec->rx))
+ complete(&nvec->ec_transfer);
+
+ schedule_work(&nvec->rx_work);
+}
+
+/**
+ * nvec_invalid_flags - Send an error message about invalid flags and jump
+ * @nvec: The nvec device
+ * @status: The status flags
+ * @reset: Whether we shall jump to state 0.
+ */
+static void nvec_invalid_flags(struct nvec_chip *nvec, unsigned int status,
+ bool reset)
+{
+ dev_err(nvec->dev, "unexpected status flags 0x%02x during state %i\n",
+ status, nvec->state);
+ if (reset)
+ nvec->state = 0;
+}
+
+/**
+ * nvec_tx_set - Set the message to transfer (nvec->tx)
+ * @nvec: A &struct nvec_chip
+ *
+ * Gets the first entry from the tx_data list of @nvec and sets the
+ * tx member to it. If the tx_data list is empty, this uses the
+ * tx_scratch message to send a no operation message.
+ */
+static void nvec_tx_set(struct nvec_chip *nvec)
+{
+ spin_lock(&nvec->tx_lock);
+ if (list_empty(&nvec->tx_data)) {
+ dev_err(nvec->dev, "empty tx - sending no-op\n");
+ memcpy(nvec->tx_scratch.data, "\x02\x07\x02", 3);
+ nvec->tx_scratch.size = 3;
+ nvec->tx_scratch.pos = 0;
+ nvec->tx = &nvec->tx_scratch;
+ list_add_tail(&nvec->tx->node, &nvec->tx_data);
+ } else {
+ nvec->tx = list_first_entry(&nvec->tx_data, struct nvec_msg,
+ node);
+ nvec->tx->pos = 0;
+ }
+ spin_unlock(&nvec->tx_lock);
+
+ dev_dbg(nvec->dev, "Sending message of length %u, command 0x%x\n",
+ (uint)nvec->tx->size, nvec->tx->data[1]);
+}
+
+/**
+ * nvec_interrupt - Interrupt handler
+ * @irq: The IRQ
+ * @dev: The nvec device
+ *
+ * Interrupt handler that fills our RX buffers and empties our TX
+ * buffers. This uses a finite state machine with ridiculous amounts
+ * of error checking, in order to be fairly reliable.
+ */
+static irqreturn_t nvec_interrupt(int irq, void *dev)
+{
+ unsigned long status;
+ unsigned int received = 0;
+ unsigned char to_send = 0xff;
+ const unsigned long irq_mask = I2C_SL_IRQ | END_TRANS | RCVD | RNW;
+ struct nvec_chip *nvec = dev;
+ unsigned int state = nvec->state;
+
+ status = readl(nvec->base + I2C_SL_STATUS);
+
+ /* Filter out some errors */
+ if ((status & irq_mask) == 0 && (status & ~irq_mask) != 0) {
+ dev_err(nvec->dev, "unexpected irq mask %lx\n", status);
+ return IRQ_HANDLED;
+ }
+ if ((status & I2C_SL_IRQ) == 0) {
+ dev_err(nvec->dev, "Spurious IRQ\n");
+ return IRQ_HANDLED;
+ }
+
+ /* The EC did not request a read, so it send us something, read it */
+ if ((status & RNW) == 0) {
+ received = readl(nvec->base + I2C_SL_RCVD);
+ if (status & RCVD)
+ writel(0, nvec->base + I2C_SL_RCVD);
+ }
+
+ if (status == (I2C_SL_IRQ | RCVD))
+ nvec->state = 0;
+
+ switch (nvec->state) {
+ case 0: /* Verify that its a transfer start, the rest later */
+ if (status != (I2C_SL_IRQ | RCVD))
+ nvec_invalid_flags(nvec, status, false);
+ break;
+ case 1: /* command byte */
+ if (status != I2C_SL_IRQ) {
+ nvec_invalid_flags(nvec, status, true);
+ } else {
+ nvec->rx = nvec_msg_alloc(nvec, NVEC_MSG_RX);
+ /* Should not happen in a normal world */
+ if (unlikely(nvec->rx == NULL)) {
+ nvec->state = 0;
+ break;
+ }
+ nvec->rx->data[0] = received;
+ nvec->rx->pos = 1;
+ nvec->state = 2;
+ }
+ break;
+ case 2: /* first byte after command */
+ if (status == (I2C_SL_IRQ | RNW | RCVD)) {
+ udelay(33);
+ if (nvec->rx->data[0] != 0x01) {
+ dev_err(nvec->dev,
+ "Read without prior read command\n");
+ nvec->state = 0;
+ break;
+ }
+ nvec_msg_free(nvec, nvec->rx);
+ nvec->state = 3;
+ nvec_tx_set(nvec);
+ BUG_ON(nvec->tx->size < 1);
+ to_send = nvec->tx->data[0];
+ nvec->tx->pos = 1;
+ } else if (status == (I2C_SL_IRQ)) {
+ BUG_ON(nvec->rx == NULL);
+ nvec->rx->data[1] = received;
+ nvec->rx->pos = 2;
+ nvec->state = 4;
+ } else {
+ nvec_invalid_flags(nvec, status, true);
+ }
+ break;
+ case 3: /* EC does a block read, we transmit data */
+ if (status & END_TRANS) {
+ nvec_tx_completed(nvec);
+ } else if ((status & RNW) == 0 || (status & RCVD)) {
+ nvec_invalid_flags(nvec, status, true);
+ } else if (nvec->tx && nvec->tx->pos < nvec->tx->size) {
+ to_send = nvec->tx->data[nvec->tx->pos++];
+ } else {
+ dev_err(nvec->dev, "tx buffer underflow on %p (%u > %u)\n",
+ nvec->tx,
+ (uint) (nvec->tx ? nvec->tx->pos : 0),
+ (uint) (nvec->tx ? nvec->tx->size : 0));
+ nvec->state = 0;
+ }
+ break;
+ case 4: /* EC does some write, we read the data */
+ if ((status & (END_TRANS | RNW)) == END_TRANS)
+ nvec_rx_completed(nvec);
+ else if (status & (RNW | RCVD))
+ nvec_invalid_flags(nvec, status, true);
+ else if (nvec->rx && nvec->rx->pos < NVEC_MSG_SIZE)
+ nvec->rx->data[nvec->rx->pos++] = received;
+ else
+ dev_err(nvec->dev,
+ "RX buffer overflow on %p: Trying to write byte %u of %u\n",
+ nvec->rx, nvec->rx ? nvec->rx->pos : 0,
+ NVEC_MSG_SIZE);
+ break;
+ default:
+ nvec->state = 0;
+ }
+
+ /* If we are told that a new transfer starts, verify it */
+ if ((status & (RCVD | RNW)) == RCVD) {
+ if (received != nvec->i2c_addr)
+ dev_err(nvec->dev,
+ "received address 0x%02x, expected 0x%02x\n",
+ received, nvec->i2c_addr);
+ nvec->state = 1;
+ }
+
+ /* Send data if requested, but not on end of transmission */
+ if ((status & (RNW | END_TRANS)) == RNW)
+ writel(to_send, nvec->base + I2C_SL_RCVD);
+
+ /* If we have send the first byte */
+ if (status == (I2C_SL_IRQ | RNW | RCVD))
+ nvec_gpio_set_value(nvec, 1);
+
+ dev_dbg(nvec->dev,
+ "Handled: %s 0x%02x, %s 0x%02x in state %u [%s%s%s]\n",
+ (status & RNW) == 0 ? "received" : "R=",
+ received,
+ (status & (RNW | END_TRANS)) ? "sent" : "S=",
+ to_send,
+ state,
+ status & END_TRANS ? " END_TRANS" : "",
+ status & RCVD ? " RCVD" : "",
+ status & RNW ? " RNW" : "");
+
+
+ /*
+ * TODO: A correct fix needs to be found for this.
+ *
+ * We experience less incomplete messages with this delay than without
+ * it, but we don't know why. Help is appreciated.
+ */
+ udelay(100);
+
+ return IRQ_HANDLED;
+}
+
+static void tegra_init_i2c_slave(struct nvec_chip *nvec)
+{
+ u32 val;
+
+ clk_prepare_enable(nvec->i2c_clk);
+
+ reset_control_assert(nvec->rst);
+ udelay(2);
+ reset_control_deassert(nvec->rst);
+
+ val = I2C_CNFG_NEW_MASTER_SFM | I2C_CNFG_PACKET_MODE_EN |
+ (0x2 << I2C_CNFG_DEBOUNCE_CNT_SHIFT);
+ writel(val, nvec->base + I2C_CNFG);
+
+ clk_set_rate(nvec->i2c_clk, 8 * 80000);
+
+ writel(I2C_SL_NEWSL, nvec->base + I2C_SL_CNFG);
+ writel(0x1E, nvec->base + I2C_SL_DELAY_COUNT);
+
+ writel(nvec->i2c_addr>>1, nvec->base + I2C_SL_ADDR1);
+ writel(0, nvec->base + I2C_SL_ADDR2);
+
+ enable_irq(nvec->irq);
+}
+
+#ifdef CONFIG_PM_SLEEP
+static void nvec_disable_i2c_slave(struct nvec_chip *nvec)
+{
+ disable_irq(nvec->irq);
+ writel(I2C_SL_NEWSL | I2C_SL_NACK, nvec->base + I2C_SL_CNFG);
+ clk_disable_unprepare(nvec->i2c_clk);
+}
+#endif
+
+static void nvec_power_off(void)
+{
+ char ap_pwr_down[] = { NVEC_SLEEP, AP_PWR_DOWN };
+
+ nvec_toggle_global_events(nvec_power_handle, false);
+ nvec_write_async(nvec_power_handle, ap_pwr_down, 2);
+}
+
+/*
+ * Parse common device tree data
+ */
+static int nvec_i2c_parse_dt_pdata(struct nvec_chip *nvec)
+{
+ nvec->gpio = of_get_named_gpio(nvec->dev->of_node, "request-gpios", 0);
+
+ if (nvec->gpio < 0) {
+ dev_err(nvec->dev, "no gpio specified");
+ return -ENODEV;
+ }
+
+ if (of_property_read_u32(nvec->dev->of_node, "slave-addr",
+ &nvec->i2c_addr)) {
+ dev_err(nvec->dev, "no i2c address specified");
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+static int tegra_nvec_probe(struct platform_device *pdev)
+{
+ int err, ret;
+ struct clk *i2c_clk;
+ struct nvec_chip *nvec;
+ struct nvec_msg *msg;
+ struct resource *res;
+ void __iomem *base;
+ char get_firmware_version[] = { NVEC_CNTL, GET_FIRMWARE_VERSION },
+ unmute_speakers[] = { NVEC_OEM0, 0x10, 0x59, 0x95 },
+ enable_event[7] = { NVEC_SYS, CNF_EVENT_REPORTING, true };
+
+ if (!pdev->dev.of_node) {
+ dev_err(&pdev->dev, "must be instantiated using device tree\n");
+ return -ENODEV;
+ }
+
+ nvec = devm_kzalloc(&pdev->dev, sizeof(struct nvec_chip), GFP_KERNEL);
+ if (!nvec)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, nvec);
+ nvec->dev = &pdev->dev;
+
+ err = nvec_i2c_parse_dt_pdata(nvec);
+ if (err < 0)
+ return err;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(base))
+ return PTR_ERR(base);
+
+ nvec->irq = platform_get_irq(pdev, 0);
+ if (nvec->irq < 0) {
+ dev_err(&pdev->dev, "no irq resource?\n");
+ return -ENODEV;
+ }
+
+ i2c_clk = devm_clk_get(&pdev->dev, "div-clk");
+ if (IS_ERR(i2c_clk)) {
+ dev_err(nvec->dev, "failed to get controller clock\n");
+ return -ENODEV;
+ }
+
+ nvec->rst = devm_reset_control_get(&pdev->dev, "i2c");
+ if (IS_ERR(nvec->rst)) {
+ dev_err(nvec->dev, "failed to get controller reset\n");
+ return PTR_ERR(nvec->rst);
+ }
+
+ nvec->base = base;
+ nvec->i2c_clk = i2c_clk;
+ nvec->rx = &nvec->msg_pool[0];
+
+ ATOMIC_INIT_NOTIFIER_HEAD(&nvec->notifier_list);
+
+ init_completion(&nvec->sync_write);
+ init_completion(&nvec->ec_transfer);
+ mutex_init(&nvec->sync_write_mutex);
+ spin_lock_init(&nvec->tx_lock);
+ spin_lock_init(&nvec->rx_lock);
+ INIT_LIST_HEAD(&nvec->rx_data);
+ INIT_LIST_HEAD(&nvec->tx_data);
+ INIT_WORK(&nvec->rx_work, nvec_dispatch);
+ INIT_WORK(&nvec->tx_work, nvec_request_master);
+
+ err = devm_gpio_request_one(&pdev->dev, nvec->gpio, GPIOF_OUT_INIT_HIGH,
+ "nvec gpio");
+ if (err < 0) {
+ dev_err(nvec->dev, "couldn't request gpio\n");
+ return -ENODEV;
+ }
+
+ err = devm_request_irq(&pdev->dev, nvec->irq, nvec_interrupt, 0,
+ "nvec", nvec);
+ if (err) {
+ dev_err(nvec->dev, "couldn't request irq\n");
+ return -ENODEV;
+ }
+ disable_irq(nvec->irq);
+
+ tegra_init_i2c_slave(nvec);
+
+ /* enable event reporting */
+ nvec_toggle_global_events(nvec, true);
+
+ nvec->nvec_status_notifier.notifier_call = nvec_status_notifier;
+ nvec_register_notifier(nvec, &nvec->nvec_status_notifier, 0);
+
+ nvec_power_handle = nvec;
+ pm_power_off = nvec_power_off;
+
+ /* Get Firmware Version */
+ msg = nvec_write_sync(nvec, get_firmware_version, 2);
+
+ if (msg) {
+ dev_warn(nvec->dev, "ec firmware version %02x.%02x.%02x / %02x\n",
+ msg->data[4], msg->data[5], msg->data[6], msg->data[7]);
+
+ nvec_msg_free(nvec, msg);
+ }
+
+ ret = mfd_add_devices(nvec->dev, 0, nvec_devices,
+ ARRAY_SIZE(nvec_devices), NULL, 0, NULL);
+ if (ret)
+ dev_err(nvec->dev, "error adding subdevices\n");
+
+ /* unmute speakers? */
+ nvec_write_async(nvec, unmute_speakers, 4);
+
+ /* enable lid switch event */
+ nvec_event_mask(enable_event, LID_SWITCH);
+ nvec_write_async(nvec, enable_event, 7);
+
+ /* enable power button event */
+ nvec_event_mask(enable_event, PWR_BUTTON);
+ nvec_write_async(nvec, enable_event, 7);
+
+ return 0;
+}
+
+static int tegra_nvec_remove(struct platform_device *pdev)
+{
+ struct nvec_chip *nvec = platform_get_drvdata(pdev);
+
+ nvec_toggle_global_events(nvec, false);
+ mfd_remove_devices(nvec->dev);
+ nvec_unregister_notifier(nvec, &nvec->nvec_status_notifier);
+ cancel_work_sync(&nvec->rx_work);
+ cancel_work_sync(&nvec->tx_work);
+ /* FIXME: needs check wether nvec is responsible for power off */
+ pm_power_off = NULL;
+
+ return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int nvec_suspend(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct nvec_chip *nvec = platform_get_drvdata(pdev);
+ struct nvec_msg *msg;
+ char ap_suspend[] = { NVEC_SLEEP, AP_SUSPEND };
+
+ dev_dbg(nvec->dev, "suspending\n");
+
+ /* keep these sync or you'll break suspend */
+ nvec_toggle_global_events(nvec, false);
+
+ msg = nvec_write_sync(nvec, ap_suspend, sizeof(ap_suspend));
+ nvec_msg_free(nvec, msg);
+
+ nvec_disable_i2c_slave(nvec);
+
+ return 0;
+}
+
+static int nvec_resume(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct nvec_chip *nvec = platform_get_drvdata(pdev);
+
+ dev_dbg(nvec->dev, "resuming\n");
+ tegra_init_i2c_slave(nvec);
+ nvec_toggle_global_events(nvec, true);
+
+ return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(nvec_pm_ops, nvec_suspend, nvec_resume);
+
+/* Match table for of_platform binding */
+static const struct of_device_id nvidia_nvec_of_match[] = {
+ { .compatible = "nvidia,nvec", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, nvidia_nvec_of_match);
+
+static struct platform_driver nvec_device_driver = {
+ .probe = tegra_nvec_probe,
+ .remove = tegra_nvec_remove,
+ .driver = {
+ .name = "nvec",
+ .pm = &nvec_pm_ops,
+ .of_match_table = nvidia_nvec_of_match,
+ }
+};
+
+module_platform_driver(nvec_device_driver);
+
+MODULE_ALIAS("platform:nvec");
+MODULE_DESCRIPTION("NVIDIA compliant embedded controller interface");
+MODULE_AUTHOR("Marc Dietrich <marvin24@gmx.de>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/nvec/nvec.h b/drivers/staging/nvec/nvec.h
new file mode 100644
index 000000000..e27137505
--- /dev/null
+++ b/drivers/staging/nvec/nvec.h
@@ -0,0 +1,183 @@
+/*
+ * NVEC: NVIDIA compliant embedded controller interface
+ *
+ * Copyright (C) 2011 The AC100 Kernel Team <ac100@lists.launchpad.net>
+ *
+ * Authors: Pierre-Hugues Husson <phhusson@free.fr>
+ * Ilya Petrov <ilya.muromec@gmail.com>
+ * Marc Dietrich <marvin24@gmx.de>
+ * Julian Andres Klode <jak@jak-linux.org>
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ */
+
+#ifndef __LINUX_MFD_NVEC
+#define __LINUX_MFD_NVEC
+
+#include <linux/atomic.h>
+#include <linux/clk.h>
+#include <linux/completion.h>
+#include <linux/list.h>
+#include <linux/mutex.h>
+#include <linux/notifier.h>
+#include <linux/reset.h>
+#include <linux/spinlock.h>
+#include <linux/workqueue.h>
+
+/* NVEC_POOL_SIZE - Size of the pool in &struct nvec_msg */
+#define NVEC_POOL_SIZE 64
+
+/*
+ * NVEC_MSG_SIZE - Maximum size of the data field of &struct nvec_msg.
+ *
+ * A message must store up to a SMBus block operation which consists of
+ * one command byte, one count byte, and up to 32 payload bytes = 34
+ * byte.
+ */
+#define NVEC_MSG_SIZE 34
+
+/**
+ * enum nvec_event_size - The size of an event message
+ * @NVEC_2BYTES: The message has one command byte and one data byte
+ * @NVEC_3BYTES: The message has one command byte and two data bytes
+ * @NVEC_VAR_SIZE: The message has one command byte, one count byte, and has
+ * up to as many bytes as the number in the count byte. The
+ * maximum is 32
+ *
+ * Events can be fixed or variable sized. This is useless on other message
+ * types, which are always variable sized.
+ */
+enum nvec_event_size {
+ NVEC_2BYTES,
+ NVEC_3BYTES,
+ NVEC_VAR_SIZE,
+};
+
+/**
+ * enum nvec_msg_type - The type of a message
+ * @NVEC_SYS: A system request/response
+ * @NVEC_BAT: A battery request/response
+ * @NVEC_KBD: A keyboard request/response
+ * @NVEC_PS2: A mouse request/response
+ * @NVEC_CNTL: A EC control request/response
+ * @NVEC_KB_EVT: An event from the keyboard
+ * @NVEC_PS2_EVT: An event from the mouse
+ *
+ * Events can be fixed or variable sized. This is useless on other message
+ * types, which are always variable sized.
+ */
+enum nvec_msg_type {
+ NVEC_SYS = 1,
+ NVEC_BAT,
+ NVEC_GPIO,
+ NVEC_SLEEP,
+ NVEC_KBD,
+ NVEC_PS2,
+ NVEC_CNTL,
+ NVEC_OEM0 = 0x0d,
+ NVEC_KB_EVT = 0x80,
+ NVEC_PS2_EVT,
+};
+
+/**
+ * struct nvec_msg - A buffer for a single message
+ * @node: Messages are part of various lists in a &struct nvec_chip
+ * @data: The data of the message
+ * @size: For TX messages, the number of bytes used in @data
+ * @pos: For RX messages, the current position to write to. For TX messages,
+ * the position to read from.
+ * @used: Used for the message pool to mark a message as free/allocated.
+ *
+ * This structure is used to hold outgoing and incoming messages. Outgoing
+ * messages have a different format than incoming messages, and that is not
+ * documented yet.
+ */
+struct nvec_msg {
+ struct list_head node;
+ unsigned char data[NVEC_MSG_SIZE];
+ unsigned short size;
+ unsigned short pos;
+ atomic_t used;
+};
+
+/**
+ * struct nvec_chip - A single connection to an NVIDIA Embedded controller
+ * @dev: The device
+ * @gpio: The same as for &struct nvec_platform_data
+ * @irq: The IRQ of the I2C device
+ * @i2c_addr: The address of the I2C slave
+ * @base: The base of the memory mapped region of the I2C device
+ * @i2c_clk: The clock of the I2C device
+ * @rst: The reset of the I2C device
+ * @notifier_list: Notifiers to be called on received messages, see
+ * nvec_register_notifier()
+ * @rx_data: Received messages that have to be processed
+ * @tx_data: Messages waiting to be sent to the controller
+ * @nvec_status_notifier: Internal notifier (see nvec_status_notifier())
+ * @rx_work: A work structure for the RX worker nvec_dispatch()
+ * @tx_work: A work structure for the TX worker nvec_request_master()
+ * @wq: The work queue in which @rx_work and @tx_work are executed
+ * @rx: The message currently being retrieved or %NULL
+ * @msg_pool: A pool of messages for allocation
+ * @tx: The message currently being transferred
+ * @tx_scratch: Used for building pseudo messages
+ * @ec_transfer: A completion that will be completed once a message has been
+ * received (see nvec_rx_completed())
+ * @tx_lock: Spinlock for modifications on @tx_data
+ * @rx_lock: Spinlock for modifications on @rx_data
+ * @sync_write_mutex: A mutex for nvec_write_sync()
+ * @sync_write: A completion to signal that a synchronous message is complete
+ * @sync_write_pending: The first two bytes of the request (type and subtype)
+ * @last_sync_msg: The last synchronous message.
+ * @state: State of our finite state machine used in nvec_interrupt()
+ */
+struct nvec_chip {
+ struct device *dev;
+ int gpio;
+ int irq;
+ int i2c_addr;
+ void __iomem *base;
+ struct clk *i2c_clk;
+ struct reset_control *rst;
+ struct atomic_notifier_head notifier_list;
+ struct list_head rx_data, tx_data;
+ struct notifier_block nvec_status_notifier;
+ struct work_struct rx_work, tx_work;
+ struct workqueue_struct *wq;
+ struct nvec_msg msg_pool[NVEC_POOL_SIZE];
+ struct nvec_msg *rx;
+
+ struct nvec_msg *tx;
+ struct nvec_msg tx_scratch;
+ struct completion ec_transfer;
+
+ spinlock_t tx_lock, rx_lock;
+
+ /* sync write stuff */
+ struct mutex sync_write_mutex;
+ struct completion sync_write;
+ u16 sync_write_pending;
+ struct nvec_msg *last_sync_msg;
+
+ int state;
+};
+
+extern int nvec_write_async(struct nvec_chip *nvec, const unsigned char *data,
+ short size);
+
+extern struct nvec_msg *nvec_write_sync(struct nvec_chip *nvec,
+ const unsigned char *data, short size);
+
+extern int nvec_register_notifier(struct nvec_chip *nvec,
+ struct notifier_block *nb,
+ unsigned int events);
+
+extern int nvec_unregister_notifier(struct nvec_chip *dev,
+ struct notifier_block *nb);
+
+extern void nvec_msg_free(struct nvec_chip *nvec, struct nvec_msg *msg);
+
+#endif
diff --git a/drivers/staging/nvec/nvec_kbd.c b/drivers/staging/nvec/nvec_kbd.c
new file mode 100644
index 000000000..e881e6b26
--- /dev/null
+++ b/drivers/staging/nvec/nvec_kbd.c
@@ -0,0 +1,192 @@
+/*
+ * nvec_kbd: keyboard driver for a NVIDIA compliant embedded controller
+ *
+ * Copyright (C) 2011 The AC100 Kernel Team <ac100@lists.launchpad.net>
+ *
+ * Authors: Pierre-Hugues Husson <phhusson@free.fr>
+ * Marc Dietrich <marvin24@gmx.de>
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/input.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+
+#include "nvec-keytable.h"
+#include "nvec.h"
+
+enum kbd_subcmds {
+ CNFG_WAKE = 3,
+ CNFG_WAKE_KEY_REPORTING,
+ SET_LEDS = 0xed,
+ ENABLE_KBD = 0xf4,
+ DISABLE_KBD,
+};
+
+static unsigned char keycodes[ARRAY_SIZE(code_tab_102us)
+ + ARRAY_SIZE(extcode_tab_us102)];
+
+struct nvec_keys {
+ struct input_dev *input;
+ struct notifier_block notifier;
+ struct nvec_chip *nvec;
+ bool caps_lock;
+};
+
+static struct nvec_keys keys_dev;
+
+static void nvec_kbd_toggle_led(void)
+{
+ char buf[] = { NVEC_KBD, SET_LEDS, 0 };
+
+ keys_dev.caps_lock = !keys_dev.caps_lock;
+
+ if (keys_dev.caps_lock)
+ /* should be BIT(0) only, firmware bug? */
+ buf[2] = BIT(0) | BIT(1) | BIT(2);
+
+ nvec_write_async(keys_dev.nvec, buf, sizeof(buf));
+}
+
+static int nvec_keys_notifier(struct notifier_block *nb,
+ unsigned long event_type, void *data)
+{
+ int code, state;
+ unsigned char *msg = (unsigned char *)data;
+
+ if (event_type == NVEC_KB_EVT) {
+ int _size = (msg[0] & (3 << 5)) >> 5;
+
+/* power on/off button */
+ if (_size == NVEC_VAR_SIZE)
+ return NOTIFY_STOP;
+
+ if (_size == NVEC_3BYTES)
+ msg++;
+
+ code = msg[1] & 0x7f;
+ state = msg[1] & 0x80;
+
+ if (code_tabs[_size][code] == KEY_CAPSLOCK && state)
+ nvec_kbd_toggle_led();
+
+ input_report_key(keys_dev.input, code_tabs[_size][code],
+ !state);
+ input_sync(keys_dev.input);
+
+ return NOTIFY_STOP;
+ }
+
+ return NOTIFY_DONE;
+}
+
+static int nvec_kbd_event(struct input_dev *dev, unsigned int type,
+ unsigned int code, int value)
+{
+ struct nvec_chip *nvec = keys_dev.nvec;
+ char buf[] = { NVEC_KBD, SET_LEDS, 0 };
+
+ if (type == EV_REP)
+ return 0;
+
+ if (type != EV_LED)
+ return -1;
+
+ if (code != LED_CAPSL)
+ return -1;
+
+ buf[2] = !!value;
+ nvec_write_async(nvec, buf, sizeof(buf));
+
+ return 0;
+}
+
+static int nvec_kbd_probe(struct platform_device *pdev)
+{
+ struct nvec_chip *nvec = dev_get_drvdata(pdev->dev.parent);
+ int i, j, err;
+ struct input_dev *idev;
+ char clear_leds[] = { NVEC_KBD, SET_LEDS, 0 },
+ enable_kbd[] = { NVEC_KBD, ENABLE_KBD },
+ cnfg_wake[] = { NVEC_KBD, CNFG_WAKE, true, true },
+ cnfg_wake_key_reporting[] = { NVEC_KBD, CNFG_WAKE_KEY_REPORTING,
+ true };
+
+ j = 0;
+
+ for (i = 0; i < ARRAY_SIZE(code_tab_102us); ++i)
+ keycodes[j++] = code_tab_102us[i];
+
+ for (i = 0; i < ARRAY_SIZE(extcode_tab_us102); ++i)
+ keycodes[j++] = extcode_tab_us102[i];
+
+ idev = devm_input_allocate_device(&pdev->dev);
+ idev->name = "nvec keyboard";
+ idev->phys = "nvec";
+ idev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP) | BIT_MASK(EV_LED);
+ idev->ledbit[0] = BIT_MASK(LED_CAPSL);
+ idev->event = nvec_kbd_event;
+ idev->keycode = keycodes;
+ idev->keycodesize = sizeof(unsigned char);
+ idev->keycodemax = ARRAY_SIZE(keycodes);
+
+ for (i = 0; i < ARRAY_SIZE(keycodes); ++i)
+ set_bit(keycodes[i], idev->keybit);
+
+ clear_bit(0, idev->keybit);
+ err = input_register_device(idev);
+ if (err)
+ return err;
+
+ keys_dev.input = idev;
+ keys_dev.notifier.notifier_call = nvec_keys_notifier;
+ keys_dev.nvec = nvec;
+ nvec_register_notifier(nvec, &keys_dev.notifier, 0);
+
+ /* Enable keyboard */
+ nvec_write_async(nvec, enable_kbd, 2);
+
+ /* configures wake on special keys */
+ nvec_write_async(nvec, cnfg_wake, 4);
+ /* enable wake key reporting */
+ nvec_write_async(nvec, cnfg_wake_key_reporting, 3);
+
+ /* Disable caps lock LED */
+ nvec_write_async(nvec, clear_leds, sizeof(clear_leds));
+
+ return 0;
+}
+
+static int nvec_kbd_remove(struct platform_device *pdev)
+{
+ struct nvec_chip *nvec = dev_get_drvdata(pdev->dev.parent);
+ char disable_kbd[] = { NVEC_KBD, DISABLE_KBD },
+ uncnfg_wake_key_reporting[] = { NVEC_KBD, CNFG_WAKE_KEY_REPORTING,
+ false };
+ nvec_write_async(nvec, uncnfg_wake_key_reporting, 3);
+ nvec_write_async(nvec, disable_kbd, 2);
+ nvec_unregister_notifier(nvec, &keys_dev.notifier);
+
+ return 0;
+}
+
+static struct platform_driver nvec_kbd_driver = {
+ .probe = nvec_kbd_probe,
+ .remove = nvec_kbd_remove,
+ .driver = {
+ .name = "nvec-kbd",
+ },
+};
+
+module_platform_driver(nvec_kbd_driver);
+
+MODULE_AUTHOR("Marc Dietrich <marvin24@gmx.de>");
+MODULE_DESCRIPTION("NVEC keyboard driver");
+MODULE_ALIAS("platform:nvec-kbd");
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/nvec/nvec_paz00.c b/drivers/staging/nvec/nvec_paz00.c
new file mode 100644
index 000000000..68146bfee
--- /dev/null
+++ b/drivers/staging/nvec/nvec_paz00.c
@@ -0,0 +1,98 @@
+/*
+ * nvec_paz00: OEM specific driver for Compal PAZ00 based devices
+ *
+ * Copyright (C) 2011 The AC100 Kernel Team <ac100@lists.launchpad.net>
+ *
+ * Authors: Ilya Petrov <ilya.muromec@gmail.com>
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/err.h>
+#include <linux/slab.h>
+#include <linux/leds.h>
+#include <linux/platform_device.h>
+#include "nvec.h"
+
+#define to_nvec_led(led_cdev) \
+ container_of(led_cdev, struct nvec_led, cdev)
+
+#define NVEC_LED_REQ {'\x0d', '\x10', '\x45', '\x10', '\x00'}
+
+#define NVEC_LED_MAX 8
+
+struct nvec_led {
+ struct led_classdev cdev;
+ struct nvec_chip *nvec;
+};
+
+static void nvec_led_brightness_set(struct led_classdev *led_cdev,
+ enum led_brightness value)
+{
+ struct nvec_led *led = to_nvec_led(led_cdev);
+ unsigned char buf[] = NVEC_LED_REQ;
+
+ buf[4] = value;
+
+ nvec_write_async(led->nvec, buf, sizeof(buf));
+
+ led->cdev.brightness = value;
+
+}
+
+static int nvec_paz00_probe(struct platform_device *pdev)
+{
+ struct nvec_chip *nvec = dev_get_drvdata(pdev->dev.parent);
+ struct nvec_led *led;
+ int ret = 0;
+
+ led = devm_kzalloc(&pdev->dev, sizeof(*led), GFP_KERNEL);
+ if (!led)
+ return -ENOMEM;
+
+ led->cdev.max_brightness = NVEC_LED_MAX;
+
+ led->cdev.brightness_set = nvec_led_brightness_set;
+ led->cdev.name = "paz00-led";
+ led->cdev.flags |= LED_CORE_SUSPENDRESUME;
+ led->nvec = nvec;
+
+ platform_set_drvdata(pdev, led);
+
+ ret = led_classdev_register(&pdev->dev, &led->cdev);
+ if (ret < 0)
+ return ret;
+
+ /* to expose the default value to userspace */
+ led->cdev.brightness = 0;
+
+ return 0;
+}
+
+static int nvec_paz00_remove(struct platform_device *pdev)
+{
+ struct nvec_led *led = platform_get_drvdata(pdev);
+
+ led_classdev_unregister(&led->cdev);
+
+ return 0;
+}
+
+static struct platform_driver nvec_paz00_driver = {
+ .probe = nvec_paz00_probe,
+ .remove = nvec_paz00_remove,
+ .driver = {
+ .name = "nvec-paz00",
+ },
+};
+
+module_platform_driver(nvec_paz00_driver);
+
+MODULE_AUTHOR("Ilya Petrov <ilya.muromec@gmail.com>");
+MODULE_DESCRIPTION("Tegra NVEC PAZ00 driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:nvec-paz00");
diff --git a/drivers/staging/nvec/nvec_power.c b/drivers/staging/nvec/nvec_power.c
new file mode 100644
index 000000000..04a7402ae
--- /dev/null
+++ b/drivers/staging/nvec/nvec_power.c
@@ -0,0 +1,449 @@
+/*
+ * nvec_power: power supply driver for a NVIDIA compliant embedded controller
+ *
+ * Copyright (C) 2011 The AC100 Kernel Team <ac100@lists.launchpad.net>
+ *
+ * Authors: Ilya Petrov <ilya.muromec@gmail.com>
+ * Marc Dietrich <marvin24@gmx.de>
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/err.h>
+#include <linux/power_supply.h>
+#include <linux/slab.h>
+#include <linux/workqueue.h>
+#include <linux/delay.h>
+
+#include "nvec.h"
+
+#define GET_SYSTEM_STATUS 0x00
+
+struct nvec_power {
+ struct notifier_block notifier;
+ struct delayed_work poller;
+ struct nvec_chip *nvec;
+ int on;
+ int bat_present;
+ int bat_status;
+ int bat_voltage_now;
+ int bat_current_now;
+ int bat_current_avg;
+ int time_remain;
+ int charge_full_design;
+ int charge_last_full;
+ int critical_capacity;
+ int capacity_remain;
+ int bat_temperature;
+ int bat_cap;
+ int bat_type_enum;
+ char bat_manu[30];
+ char bat_model[30];
+ char bat_type[30];
+};
+
+enum {
+ SLOT_STATUS,
+ VOLTAGE,
+ TIME_REMAINING,
+ CURRENT,
+ AVERAGE_CURRENT,
+ AVERAGING_TIME_INTERVAL,
+ CAPACITY_REMAINING,
+ LAST_FULL_CHARGE_CAPACITY,
+ DESIGN_CAPACITY,
+ CRITICAL_CAPACITY,
+ TEMPERATURE,
+ MANUFACTURER,
+ MODEL,
+ TYPE,
+};
+
+enum {
+ AC,
+ BAT,
+};
+
+struct bat_response {
+ u8 event_type;
+ u8 length;
+ u8 sub_type;
+ u8 status;
+ /* payload */
+ union {
+ char plc[30];
+ u16 plu;
+ s16 pls;
+ };
+};
+
+static struct power_supply *nvec_bat_psy;
+static struct power_supply *nvec_psy;
+
+static int nvec_power_notifier(struct notifier_block *nb,
+ unsigned long event_type, void *data)
+{
+ struct nvec_power *power =
+ container_of(nb, struct nvec_power, notifier);
+ struct bat_response *res = (struct bat_response *)data;
+
+ if (event_type != NVEC_SYS)
+ return NOTIFY_DONE;
+
+ if (res->sub_type == 0) {
+ if (power->on != res->plu) {
+ power->on = res->plu;
+ power_supply_changed(nvec_psy);
+ }
+ return NOTIFY_STOP;
+ }
+ return NOTIFY_OK;
+}
+
+static const int bat_init[] = {
+ LAST_FULL_CHARGE_CAPACITY, DESIGN_CAPACITY, CRITICAL_CAPACITY,
+ MANUFACTURER, MODEL, TYPE,
+};
+
+static void get_bat_mfg_data(struct nvec_power *power)
+{
+ int i;
+ char buf[] = { NVEC_BAT, SLOT_STATUS };
+
+ for (i = 0; i < ARRAY_SIZE(bat_init); i++) {
+ buf[1] = bat_init[i];
+ nvec_write_async(power->nvec, buf, 2);
+ }
+}
+
+static int nvec_power_bat_notifier(struct notifier_block *nb,
+ unsigned long event_type, void *data)
+{
+ struct nvec_power *power =
+ container_of(nb, struct nvec_power, notifier);
+ struct bat_response *res = (struct bat_response *)data;
+ int status_changed = 0;
+
+ if (event_type != NVEC_BAT)
+ return NOTIFY_DONE;
+
+ switch (res->sub_type) {
+ case SLOT_STATUS:
+ if (res->plc[0] & 1) {
+ if (power->bat_present == 0) {
+ status_changed = 1;
+ get_bat_mfg_data(power);
+ }
+
+ power->bat_present = 1;
+
+ switch ((res->plc[0] >> 1) & 3) {
+ case 0:
+ power->bat_status =
+ POWER_SUPPLY_STATUS_NOT_CHARGING;
+ break;
+ case 1:
+ power->bat_status =
+ POWER_SUPPLY_STATUS_CHARGING;
+ break;
+ case 2:
+ power->bat_status =
+ POWER_SUPPLY_STATUS_DISCHARGING;
+ break;
+ default:
+ power->bat_status = POWER_SUPPLY_STATUS_UNKNOWN;
+ }
+ } else {
+ if (power->bat_present == 1)
+ status_changed = 1;
+
+ power->bat_present = 0;
+ power->bat_status = POWER_SUPPLY_STATUS_UNKNOWN;
+ }
+ power->bat_cap = res->plc[1];
+ if (status_changed)
+ power_supply_changed(nvec_bat_psy);
+ break;
+ case VOLTAGE:
+ power->bat_voltage_now = res->plu * 1000;
+ break;
+ case TIME_REMAINING:
+ power->time_remain = res->plu * 3600;
+ break;
+ case CURRENT:
+ power->bat_current_now = res->pls * 1000;
+ break;
+ case AVERAGE_CURRENT:
+ power->bat_current_avg = res->pls * 1000;
+ break;
+ case CAPACITY_REMAINING:
+ power->capacity_remain = res->plu * 1000;
+ break;
+ case LAST_FULL_CHARGE_CAPACITY:
+ power->charge_last_full = res->plu * 1000;
+ break;
+ case DESIGN_CAPACITY:
+ power->charge_full_design = res->plu * 1000;
+ break;
+ case CRITICAL_CAPACITY:
+ power->critical_capacity = res->plu * 1000;
+ break;
+ case TEMPERATURE:
+ power->bat_temperature = res->plu - 2732;
+ break;
+ case MANUFACTURER:
+ memcpy(power->bat_manu, &res->plc, res->length - 2);
+ power->bat_model[res->length - 2] = '\0';
+ break;
+ case MODEL:
+ memcpy(power->bat_model, &res->plc, res->length - 2);
+ power->bat_model[res->length - 2] = '\0';
+ break;
+ case TYPE:
+ memcpy(power->bat_type, &res->plc, res->length - 2);
+ power->bat_type[res->length - 2] = '\0';
+ /* this differs a little from the spec
+ fill in more if you find some */
+ if (!strncmp(power->bat_type, "Li", 30))
+ power->bat_type_enum = POWER_SUPPLY_TECHNOLOGY_LION;
+ else
+ power->bat_type_enum = POWER_SUPPLY_TECHNOLOGY_UNKNOWN;
+ break;
+ default:
+ return NOTIFY_STOP;
+ }
+
+ return NOTIFY_STOP;
+}
+
+static int nvec_power_get_property(struct power_supply *psy,
+ enum power_supply_property psp,
+ union power_supply_propval *val)
+{
+ struct nvec_power *power = dev_get_drvdata(psy->dev.parent);
+
+ switch (psp) {
+ case POWER_SUPPLY_PROP_ONLINE:
+ val->intval = power->on;
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int nvec_battery_get_property(struct power_supply *psy,
+ enum power_supply_property psp,
+ union power_supply_propval *val)
+{
+ struct nvec_power *power = dev_get_drvdata(psy->dev.parent);
+
+ switch (psp) {
+ case POWER_SUPPLY_PROP_STATUS:
+ val->intval = power->bat_status;
+ break;
+ case POWER_SUPPLY_PROP_CAPACITY:
+ val->intval = power->bat_cap;
+ break;
+ case POWER_SUPPLY_PROP_PRESENT:
+ val->intval = power->bat_present;
+ break;
+ case POWER_SUPPLY_PROP_VOLTAGE_NOW:
+ val->intval = power->bat_voltage_now;
+ break;
+ case POWER_SUPPLY_PROP_CURRENT_NOW:
+ val->intval = power->bat_current_now;
+ break;
+ case POWER_SUPPLY_PROP_CURRENT_AVG:
+ val->intval = power->bat_current_avg;
+ break;
+ case POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW:
+ val->intval = power->time_remain;
+ break;
+ case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN:
+ val->intval = power->charge_full_design;
+ break;
+ case POWER_SUPPLY_PROP_CHARGE_FULL:
+ val->intval = power->charge_last_full;
+ break;
+ case POWER_SUPPLY_PROP_CHARGE_EMPTY:
+ val->intval = power->critical_capacity;
+ break;
+ case POWER_SUPPLY_PROP_CHARGE_NOW:
+ val->intval = power->capacity_remain;
+ break;
+ case POWER_SUPPLY_PROP_TEMP:
+ val->intval = power->bat_temperature;
+ break;
+ case POWER_SUPPLY_PROP_MANUFACTURER:
+ val->strval = power->bat_manu;
+ break;
+ case POWER_SUPPLY_PROP_MODEL_NAME:
+ val->strval = power->bat_model;
+ break;
+ case POWER_SUPPLY_PROP_TECHNOLOGY:
+ val->intval = power->bat_type_enum;
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static enum power_supply_property nvec_power_props[] = {
+ POWER_SUPPLY_PROP_ONLINE,
+};
+
+static enum power_supply_property nvec_battery_props[] = {
+ POWER_SUPPLY_PROP_STATUS,
+ POWER_SUPPLY_PROP_PRESENT,
+ POWER_SUPPLY_PROP_CAPACITY,
+ POWER_SUPPLY_PROP_VOLTAGE_NOW,
+ POWER_SUPPLY_PROP_CURRENT_NOW,
+#ifdef EC_FULL_DIAG
+ POWER_SUPPLY_PROP_CURRENT_AVG,
+ POWER_SUPPLY_PROP_TEMP,
+ POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW,
+#endif
+ POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
+ POWER_SUPPLY_PROP_CHARGE_FULL,
+ POWER_SUPPLY_PROP_CHARGE_EMPTY,
+ POWER_SUPPLY_PROP_CHARGE_NOW,
+ POWER_SUPPLY_PROP_MANUFACTURER,
+ POWER_SUPPLY_PROP_MODEL_NAME,
+ POWER_SUPPLY_PROP_TECHNOLOGY,
+};
+
+static char *nvec_power_supplied_to[] = {
+ "battery",
+};
+
+static const struct power_supply_desc nvec_bat_psy_desc = {
+ .name = "battery",
+ .type = POWER_SUPPLY_TYPE_BATTERY,
+ .properties = nvec_battery_props,
+ .num_properties = ARRAY_SIZE(nvec_battery_props),
+ .get_property = nvec_battery_get_property,
+};
+
+static const struct power_supply_desc nvec_psy_desc = {
+ .name = "ac",
+ .type = POWER_SUPPLY_TYPE_MAINS,
+ .properties = nvec_power_props,
+ .num_properties = ARRAY_SIZE(nvec_power_props),
+ .get_property = nvec_power_get_property,
+};
+
+static int counter;
+static int const bat_iter[] = {
+ SLOT_STATUS, VOLTAGE, CURRENT, CAPACITY_REMAINING,
+#ifdef EC_FULL_DIAG
+ AVERAGE_CURRENT, TEMPERATURE, TIME_REMAINING,
+#endif
+};
+
+static void nvec_power_poll(struct work_struct *work)
+{
+ char buf[] = { NVEC_SYS, GET_SYSTEM_STATUS };
+ struct nvec_power *power = container_of(work, struct nvec_power,
+ poller.work);
+
+ if (counter >= ARRAY_SIZE(bat_iter))
+ counter = 0;
+
+/* AC status via sys req */
+ nvec_write_async(power->nvec, buf, 2);
+ msleep(100);
+
+/* select a battery request function via round robin
+ doing it all at once seems to overload the power supply */
+ buf[0] = NVEC_BAT;
+ buf[1] = bat_iter[counter++];
+ nvec_write_async(power->nvec, buf, 2);
+
+ schedule_delayed_work(to_delayed_work(work), msecs_to_jiffies(5000));
+};
+
+static int nvec_power_probe(struct platform_device *pdev)
+{
+ struct power_supply **psy;
+ const struct power_supply_desc *psy_desc;
+ struct nvec_power *power;
+ struct nvec_chip *nvec = dev_get_drvdata(pdev->dev.parent);
+ struct power_supply_config psy_cfg = {};
+
+ power = devm_kzalloc(&pdev->dev, sizeof(struct nvec_power), GFP_NOWAIT);
+ if (!power)
+ return -ENOMEM;
+
+ dev_set_drvdata(&pdev->dev, power);
+ power->nvec = nvec;
+
+ switch (pdev->id) {
+ case AC:
+ psy = &nvec_psy;
+ psy_desc = &nvec_psy_desc;
+ psy_cfg.supplied_to = nvec_power_supplied_to;
+ psy_cfg.num_supplicants = ARRAY_SIZE(nvec_power_supplied_to);
+
+ power->notifier.notifier_call = nvec_power_notifier;
+
+ INIT_DELAYED_WORK(&power->poller, nvec_power_poll);
+ schedule_delayed_work(&power->poller, msecs_to_jiffies(5000));
+ break;
+ case BAT:
+ psy = &nvec_bat_psy;
+ psy_desc = &nvec_bat_psy_desc;
+
+ power->notifier.notifier_call = nvec_power_bat_notifier;
+ break;
+ default:
+ return -ENODEV;
+ }
+
+ nvec_register_notifier(nvec, &power->notifier, NVEC_SYS);
+
+ if (pdev->id == BAT)
+ get_bat_mfg_data(power);
+
+ *psy = power_supply_register(&pdev->dev, psy_desc, &psy_cfg);
+
+ return PTR_ERR_OR_ZERO(*psy);
+}
+
+static int nvec_power_remove(struct platform_device *pdev)
+{
+ struct nvec_power *power = platform_get_drvdata(pdev);
+
+ cancel_delayed_work_sync(&power->poller);
+ nvec_unregister_notifier(power->nvec, &power->notifier);
+ switch (pdev->id) {
+ case AC:
+ power_supply_unregister(nvec_psy);
+ break;
+ case BAT:
+ power_supply_unregister(nvec_bat_psy);
+ }
+
+ return 0;
+}
+
+static struct platform_driver nvec_power_driver = {
+ .probe = nvec_power_probe,
+ .remove = nvec_power_remove,
+ .driver = {
+ .name = "nvec-power",
+ }
+};
+
+module_platform_driver(nvec_power_driver);
+
+MODULE_AUTHOR("Ilya Petrov <ilya.muromec@gmail.com>");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("NVEC battery and AC driver");
+MODULE_ALIAS("platform:nvec-power");
diff --git a/drivers/staging/nvec/nvec_ps2.c b/drivers/staging/nvec/nvec_ps2.c
new file mode 100644
index 000000000..6ebbc8232
--- /dev/null
+++ b/drivers/staging/nvec/nvec_ps2.c
@@ -0,0 +1,189 @@
+/*
+ * nvec_ps2: mouse driver for a NVIDIA compliant embedded controller
+ *
+ * Copyright (C) 2011 The AC100 Kernel Team <ac100@lists.launchpad.net>
+ *
+ * Authors: Pierre-Hugues Husson <phhusson@free.fr>
+ * Ilya Petrov <ilya.muromec@gmail.com>
+ * Marc Dietrich <marvin24@gmx.de>
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/serio.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+
+#include "nvec.h"
+
+#define PACKET_SIZE 6
+
+#define ENABLE_MOUSE 0xf4
+#define DISABLE_MOUSE 0xf5
+#define PSMOUSE_RST 0xff
+
+#ifdef NVEC_PS2_DEBUG
+#define NVEC_PHD(str, buf, len) \
+ print_hex_dump(KERN_DEBUG, str, DUMP_PREFIX_NONE, \
+ 16, 1, buf, len, false)
+#else
+#define NVEC_PHD(str, buf, len)
+#endif
+
+enum ps2_subcmds {
+ SEND_COMMAND = 1,
+ RECEIVE_N,
+ AUTO_RECEIVE_N,
+ CANCEL_AUTO_RECEIVE,
+};
+
+struct nvec_ps2 {
+ struct serio *ser_dev;
+ struct notifier_block notifier;
+ struct nvec_chip *nvec;
+};
+
+static struct nvec_ps2 ps2_dev;
+
+static int ps2_startstreaming(struct serio *ser_dev)
+{
+ unsigned char buf[] = { NVEC_PS2, AUTO_RECEIVE_N, PACKET_SIZE };
+
+ return nvec_write_async(ps2_dev.nvec, buf, sizeof(buf));
+}
+
+static void ps2_stopstreaming(struct serio *ser_dev)
+{
+ unsigned char buf[] = { NVEC_PS2, CANCEL_AUTO_RECEIVE };
+
+ nvec_write_async(ps2_dev.nvec, buf, sizeof(buf));
+}
+
+static int ps2_sendcommand(struct serio *ser_dev, unsigned char cmd)
+{
+ unsigned char buf[] = { NVEC_PS2, SEND_COMMAND, ENABLE_MOUSE, 1 };
+
+ buf[2] = cmd & 0xff;
+
+ dev_dbg(&ser_dev->dev, "Sending ps2 cmd %02x\n", cmd);
+ return nvec_write_async(ps2_dev.nvec, buf, sizeof(buf));
+}
+
+static int nvec_ps2_notifier(struct notifier_block *nb,
+ unsigned long event_type, void *data)
+{
+ int i;
+ unsigned char *msg = (unsigned char *)data;
+
+ switch (event_type) {
+ case NVEC_PS2_EVT:
+ for (i = 0; i < msg[1]; i++)
+ serio_interrupt(ps2_dev.ser_dev, msg[2 + i], 0);
+ NVEC_PHD("ps/2 mouse event: ", &msg[2], msg[1]);
+ return NOTIFY_STOP;
+
+ case NVEC_PS2:
+ if (msg[2] == 1) {
+ for (i = 0; i < (msg[1] - 2); i++)
+ serio_interrupt(ps2_dev.ser_dev, msg[i + 4], 0);
+ NVEC_PHD("ps/2 mouse reply: ", &msg[4], msg[1] - 2);
+ }
+
+ else if (msg[1] != 2) /* !ack */
+ NVEC_PHD("unhandled mouse event: ", msg, msg[1] + 2);
+ return NOTIFY_STOP;
+ }
+
+ return NOTIFY_DONE;
+}
+
+static int nvec_mouse_probe(struct platform_device *pdev)
+{
+ struct nvec_chip *nvec = dev_get_drvdata(pdev->dev.parent);
+ struct serio *ser_dev;
+ char mouse_reset[] = { NVEC_PS2, SEND_COMMAND, PSMOUSE_RST, 3 };
+
+ ser_dev = devm_kzalloc(&pdev->dev, sizeof(struct serio), GFP_KERNEL);
+ if (!ser_dev)
+ return -ENOMEM;
+
+ ser_dev->id.type = SERIO_PS_PSTHRU;
+ ser_dev->write = ps2_sendcommand;
+ ser_dev->start = ps2_startstreaming;
+ ser_dev->stop = ps2_stopstreaming;
+
+ strlcpy(ser_dev->name, "nvec mouse", sizeof(ser_dev->name));
+ strlcpy(ser_dev->phys, "nvec", sizeof(ser_dev->phys));
+
+ ps2_dev.ser_dev = ser_dev;
+ ps2_dev.notifier.notifier_call = nvec_ps2_notifier;
+ ps2_dev.nvec = nvec;
+ nvec_register_notifier(nvec, &ps2_dev.notifier, 0);
+
+ serio_register_port(ser_dev);
+
+ /* mouse reset */
+ nvec_write_async(nvec, mouse_reset, sizeof(mouse_reset));
+
+ return 0;
+}
+
+static int nvec_mouse_remove(struct platform_device *pdev)
+{
+ struct nvec_chip *nvec = dev_get_drvdata(pdev->dev.parent);
+
+ ps2_sendcommand(ps2_dev.ser_dev, DISABLE_MOUSE);
+ ps2_stopstreaming(ps2_dev.ser_dev);
+ nvec_unregister_notifier(nvec, &ps2_dev.notifier);
+ serio_unregister_port(ps2_dev.ser_dev);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int nvec_mouse_suspend(struct device *dev)
+{
+ /* disable mouse */
+ ps2_sendcommand(ps2_dev.ser_dev, DISABLE_MOUSE);
+
+ /* send cancel autoreceive */
+ ps2_stopstreaming(ps2_dev.ser_dev);
+
+ return 0;
+}
+
+static int nvec_mouse_resume(struct device *dev)
+{
+ /* start streaming */
+ ps2_startstreaming(ps2_dev.ser_dev);
+
+ /* enable mouse */
+ ps2_sendcommand(ps2_dev.ser_dev, ENABLE_MOUSE);
+
+ return 0;
+}
+#endif
+
+static const SIMPLE_DEV_PM_OPS(nvec_mouse_pm_ops, nvec_mouse_suspend,
+ nvec_mouse_resume);
+
+static struct platform_driver nvec_mouse_driver = {
+ .probe = nvec_mouse_probe,
+ .remove = nvec_mouse_remove,
+ .driver = {
+ .name = "nvec-mouse",
+ .pm = &nvec_mouse_pm_ops,
+ },
+};
+
+module_platform_driver(nvec_mouse_driver);
+
+MODULE_DESCRIPTION("NVEC mouse driver");
+MODULE_AUTHOR("Marc Dietrich <marvin24@gmx.de>");
+MODULE_ALIAS("platform:nvec-mouse");
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/octeon-usb/Kconfig b/drivers/staging/octeon-usb/Kconfig
new file mode 100644
index 000000000..16ea17ff3
--- /dev/null
+++ b/drivers/staging/octeon-usb/Kconfig
@@ -0,0 +1,10 @@
+config OCTEON_USB
+ tristate "Cavium Networks Octeon USB support"
+ depends on CAVIUM_OCTEON_SOC && USB
+ help
+ This driver supports USB host controller on some Cavium
+ Networks' products in the Octeon family.
+
+ To compile this driver as a module, choose M here. The module
+ will be called octeon-usb.
+
diff --git a/drivers/staging/octeon-usb/Makefile b/drivers/staging/octeon-usb/Makefile
new file mode 100644
index 000000000..5588be395
--- /dev/null
+++ b/drivers/staging/octeon-usb/Makefile
@@ -0,0 +1 @@
+obj-${CONFIG_OCTEON_USB} := octeon-hcd.o
diff --git a/drivers/staging/octeon-usb/TODO b/drivers/staging/octeon-usb/TODO
new file mode 100644
index 000000000..cc58a7e88
--- /dev/null
+++ b/drivers/staging/octeon-usb/TODO
@@ -0,0 +1,11 @@
+This driver is functional and has been tested on EdgeRouter Lite with
+USB mass storage.
+
+TODO:
+ - kernel coding style
+ - checkpatch warnings
+ - dead code elimination
+ - device tree bindings
+ - possibly eliminate the extra "hardware abstraction layer"
+
+Contact: Aaro Koskinen <aaro.koskinen@iki.fi>
diff --git a/drivers/staging/octeon-usb/octeon-hcd.c b/drivers/staging/octeon-usb/octeon-hcd.c
new file mode 100644
index 000000000..9e5476e35
--- /dev/null
+++ b/drivers/staging/octeon-usb/octeon-hcd.c
@@ -0,0 +1,3770 @@
+/*
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Copyright (C) 2008 Cavium Networks
+ *
+ * Some parts of the code were originally released under BSD license:
+ *
+ * Copyright (c) 2003-2010 Cavium Networks (support@cavium.com). All rights
+ * reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * * Neither the name of Cavium Networks nor the names of
+ * its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written
+ * permission.
+ *
+ * This Software, including technical data, may be subject to U.S. export
+ * control laws, including the U.S. Export Administration Act and its associated
+ * regulations, and may be subject to export or import regulations in other
+ * countries.
+ *
+ * TO THE MAXIMUM EXTENT PERMITTED BY LAW, THE SOFTWARE IS PROVIDED "AS IS"
+ * AND WITH ALL FAULTS AND CAVIUM NETWORKS MAKES NO PROMISES, REPRESENTATIONS OR
+ * WARRANTIES, EITHER EXPRESS, IMPLIED, STATUTORY, OR OTHERWISE, WITH RESPECT TO
+ * THE SOFTWARE, INCLUDING ITS CONDITION, ITS CONFORMITY TO ANY REPRESENTATION
+ * OR DESCRIPTION, OR THE EXISTENCE OF ANY LATENT OR PATENT DEFECTS, AND CAVIUM
+ * SPECIFICALLY DISCLAIMS ALL IMPLIED (IF ANY) WARRANTIES OF TITLE,
+ * MERCHANTABILITY, NONINFRINGEMENT, FITNESS FOR A PARTICULAR PURPOSE, LACK OF
+ * VIRUSES, ACCURACY OR COMPLETENESS, QUIET ENJOYMENT, QUIET POSSESSION OR
+ * CORRESPONDENCE TO DESCRIPTION. THE ENTIRE RISK ARISING OUT OF USE OR
+ * PERFORMANCE OF THE SOFTWARE LIES WITH YOU.
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/pci.h>
+#include <linux/prefetch.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/usb.h>
+
+#include <linux/time.h>
+#include <linux/delay.h>
+
+#include <asm/octeon/cvmx.h>
+#include <asm/octeon/cvmx-iob-defs.h>
+
+#include <linux/usb/hcd.h>
+
+#include <linux/err.h>
+
+#include <asm/octeon/octeon.h>
+#include <asm/octeon/cvmx-helper.h>
+#include <asm/octeon/cvmx-sysinfo.h>
+#include <asm/octeon/cvmx-helper-board.h>
+
+#include "octeon-hcd.h"
+
+/**
+ * enum cvmx_usb_speed - the possible USB device speeds
+ *
+ * @CVMX_USB_SPEED_HIGH: Device is operation at 480Mbps
+ * @CVMX_USB_SPEED_FULL: Device is operation at 12Mbps
+ * @CVMX_USB_SPEED_LOW: Device is operation at 1.5Mbps
+ */
+enum cvmx_usb_speed {
+ CVMX_USB_SPEED_HIGH = 0,
+ CVMX_USB_SPEED_FULL = 1,
+ CVMX_USB_SPEED_LOW = 2,
+};
+
+/**
+ * enum cvmx_usb_transfer - the possible USB transfer types
+ *
+ * @CVMX_USB_TRANSFER_CONTROL: USB transfer type control for hub and status
+ * transfers
+ * @CVMX_USB_TRANSFER_ISOCHRONOUS: USB transfer type isochronous for low
+ * priority periodic transfers
+ * @CVMX_USB_TRANSFER_BULK: USB transfer type bulk for large low priority
+ * transfers
+ * @CVMX_USB_TRANSFER_INTERRUPT: USB transfer type interrupt for high priority
+ * periodic transfers
+ */
+enum cvmx_usb_transfer {
+ CVMX_USB_TRANSFER_CONTROL = 0,
+ CVMX_USB_TRANSFER_ISOCHRONOUS = 1,
+ CVMX_USB_TRANSFER_BULK = 2,
+ CVMX_USB_TRANSFER_INTERRUPT = 3,
+};
+
+/**
+ * enum cvmx_usb_direction - the transfer directions
+ *
+ * @CVMX_USB_DIRECTION_OUT: Data is transferring from Octeon to the device/host
+ * @CVMX_USB_DIRECTION_IN: Data is transferring from the device/host to Octeon
+ */
+enum cvmx_usb_direction {
+ CVMX_USB_DIRECTION_OUT,
+ CVMX_USB_DIRECTION_IN,
+};
+
+/**
+ * enum cvmx_usb_complete - possible callback function status codes
+ *
+ * @CVMX_USB_COMPLETE_SUCCESS: The transaction / operation finished without
+ * any errors
+ * @CVMX_USB_COMPLETE_SHORT: FIXME: This is currently not implemented
+ * @CVMX_USB_COMPLETE_CANCEL: The transaction was canceled while in flight
+ * by a user call to cvmx_usb_cancel
+ * @CVMX_USB_COMPLETE_ERROR: The transaction aborted with an unexpected
+ * error status
+ * @CVMX_USB_COMPLETE_STALL: The transaction received a USB STALL response
+ * from the device
+ * @CVMX_USB_COMPLETE_XACTERR: The transaction failed with an error from the
+ * device even after a number of retries
+ * @CVMX_USB_COMPLETE_DATATGLERR: The transaction failed with a data toggle
+ * error even after a number of retries
+ * @CVMX_USB_COMPLETE_BABBLEERR: The transaction failed with a babble error
+ * @CVMX_USB_COMPLETE_FRAMEERR: The transaction failed with a frame error
+ * even after a number of retries
+ */
+enum cvmx_usb_complete {
+ CVMX_USB_COMPLETE_SUCCESS,
+ CVMX_USB_COMPLETE_SHORT,
+ CVMX_USB_COMPLETE_CANCEL,
+ CVMX_USB_COMPLETE_ERROR,
+ CVMX_USB_COMPLETE_STALL,
+ CVMX_USB_COMPLETE_XACTERR,
+ CVMX_USB_COMPLETE_DATATGLERR,
+ CVMX_USB_COMPLETE_BABBLEERR,
+ CVMX_USB_COMPLETE_FRAMEERR,
+};
+
+/**
+ * struct cvmx_usb_port_status - the USB port status information
+ *
+ * @port_enabled: 1 = Usb port is enabled, 0 = disabled
+ * @port_over_current: 1 = Over current detected, 0 = Over current not
+ * detected. Octeon doesn't support over current detection.
+ * @port_powered: 1 = Port power is being supplied to the device, 0 =
+ * power is off. Octeon doesn't support turning port power
+ * off.
+ * @port_speed: Current port speed.
+ * @connected: 1 = A device is connected to the port, 0 = No device is
+ * connected.
+ * @connect_change: 1 = Device connected state changed since the last set
+ * status call.
+ */
+struct cvmx_usb_port_status {
+ uint32_t reserved : 25;
+ uint32_t port_enabled : 1;
+ uint32_t port_over_current : 1;
+ uint32_t port_powered : 1;
+ enum cvmx_usb_speed port_speed : 2;
+ uint32_t connected : 1;
+ uint32_t connect_change : 1;
+};
+
+/**
+ * struct cvmx_usb_iso_packet - descriptor for Isochronous packets
+ *
+ * @offset: This is the offset in bytes into the main buffer where this data
+ * is stored.
+ * @length: This is the length in bytes of the data.
+ * @status: This is the status of this individual packet transfer.
+ */
+struct cvmx_usb_iso_packet {
+ int offset;
+ int length;
+ enum cvmx_usb_complete status;
+};
+
+/**
+ * enum cvmx_usb_initialize_flags - flags used by the initialization function
+ *
+ * @CVMX_USB_INITIALIZE_FLAGS_CLOCK_XO_XI: The USB port uses a 12MHz crystal
+ * as clock source at USB_XO and
+ * USB_XI.
+ * @CVMX_USB_INITIALIZE_FLAGS_CLOCK_XO_GND: The USB port uses 12/24/48MHz 2.5V
+ * board clock source at USB_XO.
+ * USB_XI should be tied to GND.
+ * @CVMX_USB_INITIALIZE_FLAGS_CLOCK_MHZ_MASK: Mask for clock speed field
+ * @CVMX_USB_INITIALIZE_FLAGS_CLOCK_12MHZ: Speed of reference clock or
+ * crystal
+ * @CVMX_USB_INITIALIZE_FLAGS_CLOCK_24MHZ: Speed of reference clock
+ * @CVMX_USB_INITIALIZE_FLAGS_CLOCK_48MHZ: Speed of reference clock
+ * @CVMX_USB_INITIALIZE_FLAGS_NO_DMA: Disable DMA and used polled IO for
+ * data transfer use for the USB
+ */
+enum cvmx_usb_initialize_flags {
+ CVMX_USB_INITIALIZE_FLAGS_CLOCK_XO_XI = 1 << 0,
+ CVMX_USB_INITIALIZE_FLAGS_CLOCK_XO_GND = 1 << 1,
+ CVMX_USB_INITIALIZE_FLAGS_CLOCK_MHZ_MASK = 3 << 3,
+ CVMX_USB_INITIALIZE_FLAGS_CLOCK_12MHZ = 1 << 3,
+ CVMX_USB_INITIALIZE_FLAGS_CLOCK_24MHZ = 2 << 3,
+ CVMX_USB_INITIALIZE_FLAGS_CLOCK_48MHZ = 3 << 3,
+ /* Bits 3-4 used to encode the clock frequency */
+ CVMX_USB_INITIALIZE_FLAGS_NO_DMA = 1 << 5,
+};
+
+/**
+ * enum cvmx_usb_pipe_flags - internal flags for a pipe.
+ *
+ * @CVMX_USB_PIPE_FLAGS_SCHEDULED: Used internally to determine if a pipe is
+ * actively using hardware.
+ * @CVMX_USB_PIPE_FLAGS_NEED_PING: Used internally to determine if a high speed
+ * pipe is in the ping state.
+ */
+enum cvmx_usb_pipe_flags {
+ CVMX_USB_PIPE_FLAGS_SCHEDULED = 1 << 17,
+ CVMX_USB_PIPE_FLAGS_NEED_PING = 1 << 18,
+};
+
+/* Maximum number of times to retry failed transactions */
+#define MAX_RETRIES 3
+
+/* Maximum number of hardware channels supported by the USB block */
+#define MAX_CHANNELS 8
+
+/*
+ * The low level hardware can transfer a maximum of this number of bytes in each
+ * transfer. The field is 19 bits wide
+ */
+#define MAX_TRANSFER_BYTES ((1<<19)-1)
+
+/*
+ * The low level hardware can transfer a maximum of this number of packets in
+ * each transfer. The field is 10 bits wide
+ */
+#define MAX_TRANSFER_PACKETS ((1<<10)-1)
+
+/**
+ * Logical transactions may take numerous low level
+ * transactions, especially when splits are concerned. This
+ * enum represents all of the possible stages a transaction can
+ * be in. Note that split completes are always even. This is so
+ * the NAK handler can backup to the previous low level
+ * transaction with a simple clearing of bit 0.
+ */
+enum cvmx_usb_stage {
+ CVMX_USB_STAGE_NON_CONTROL,
+ CVMX_USB_STAGE_NON_CONTROL_SPLIT_COMPLETE,
+ CVMX_USB_STAGE_SETUP,
+ CVMX_USB_STAGE_SETUP_SPLIT_COMPLETE,
+ CVMX_USB_STAGE_DATA,
+ CVMX_USB_STAGE_DATA_SPLIT_COMPLETE,
+ CVMX_USB_STAGE_STATUS,
+ CVMX_USB_STAGE_STATUS_SPLIT_COMPLETE,
+};
+
+/**
+ * struct cvmx_usb_transaction - describes each pending USB transaction
+ * regardless of type. These are linked together
+ * to form a list of pending requests for a pipe.
+ *
+ * @node: List node for transactions in the pipe.
+ * @type: Type of transaction, duplicated of the pipe.
+ * @flags: State flags for this transaction.
+ * @buffer: User's physical buffer address to read/write.
+ * @buffer_length: Size of the user's buffer in bytes.
+ * @control_header: For control transactions, physical address of the 8
+ * byte standard header.
+ * @iso_start_frame: For ISO transactions, the starting frame number.
+ * @iso_number_packets: For ISO transactions, the number of packets in the
+ * request.
+ * @iso_packets: For ISO transactions, the sub packets in the request.
+ * @actual_bytes: Actual bytes transfer for this transaction.
+ * @stage: For control transactions, the current stage.
+ * @urb: URB.
+ */
+struct cvmx_usb_transaction {
+ struct list_head node;
+ enum cvmx_usb_transfer type;
+ uint64_t buffer;
+ int buffer_length;
+ uint64_t control_header;
+ int iso_start_frame;
+ int iso_number_packets;
+ struct cvmx_usb_iso_packet *iso_packets;
+ int xfersize;
+ int pktcnt;
+ int retries;
+ int actual_bytes;
+ enum cvmx_usb_stage stage;
+ struct urb *urb;
+};
+
+/**
+ * struct cvmx_usb_pipe - a pipe represents a virtual connection between Octeon
+ * and some USB device. It contains a list of pending
+ * request to the device.
+ *
+ * @node: List node for pipe list
+ * @next: Pipe after this one in the list
+ * @transactions: List of pending transactions
+ * @interval: For periodic pipes, the interval between packets in
+ * frames
+ * @next_tx_frame: The next frame this pipe is allowed to transmit on
+ * @flags: State flags for this pipe
+ * @device_speed: Speed of device connected to this pipe
+ * @transfer_type: Type of transaction supported by this pipe
+ * @transfer_dir: IN or OUT. Ignored for Control
+ * @multi_count: Max packet in a row for the device
+ * @max_packet: The device's maximum packet size in bytes
+ * @device_addr: USB device address at other end of pipe
+ * @endpoint_num: USB endpoint number at other end of pipe
+ * @hub_device_addr: Hub address this device is connected to
+ * @hub_port: Hub port this device is connected to
+ * @pid_toggle: This toggles between 0/1 on every packet send to track
+ * the data pid needed
+ * @channel: Hardware DMA channel for this pipe
+ * @split_sc_frame: The low order bits of the frame number the split
+ * complete should be sent on
+ */
+struct cvmx_usb_pipe {
+ struct list_head node;
+ struct list_head transactions;
+ uint64_t interval;
+ uint64_t next_tx_frame;
+ enum cvmx_usb_pipe_flags flags;
+ enum cvmx_usb_speed device_speed;
+ enum cvmx_usb_transfer transfer_type;
+ enum cvmx_usb_direction transfer_dir;
+ int multi_count;
+ uint16_t max_packet;
+ uint8_t device_addr;
+ uint8_t endpoint_num;
+ uint8_t hub_device_addr;
+ uint8_t hub_port;
+ uint8_t pid_toggle;
+ uint8_t channel;
+ int8_t split_sc_frame;
+};
+
+struct cvmx_usb_tx_fifo {
+ struct {
+ int channel;
+ int size;
+ uint64_t address;
+ } entry[MAX_CHANNELS+1];
+ int head;
+ int tail;
+};
+
+/**
+ * struct cvmx_usb_state - the state of the USB block
+ *
+ * init_flags: Flags passed to initialize.
+ * index: Which USB block this is for.
+ * idle_hardware_channels: Bit set for every idle hardware channel.
+ * usbcx_hprt: Stored port status so we don't need to read a CSR to
+ * determine splits.
+ * pipe_for_channel: Map channels to pipes.
+ * pipe: Storage for pipes.
+ * indent: Used by debug output to indent functions.
+ * port_status: Last port status used for change notification.
+ * idle_pipes: List of open pipes that have no transactions.
+ * active_pipes: Active pipes indexed by transfer type.
+ * frame_number: Increments every SOF interrupt for time keeping.
+ * active_split: Points to the current active split, or NULL.
+ */
+struct cvmx_usb_state {
+ int init_flags;
+ int index;
+ int idle_hardware_channels;
+ union cvmx_usbcx_hprt usbcx_hprt;
+ struct cvmx_usb_pipe *pipe_for_channel[MAX_CHANNELS];
+ int indent;
+ struct cvmx_usb_port_status port_status;
+ struct list_head idle_pipes;
+ struct list_head active_pipes[4];
+ uint64_t frame_number;
+ struct cvmx_usb_transaction *active_split;
+ struct cvmx_usb_tx_fifo periodic;
+ struct cvmx_usb_tx_fifo nonperiodic;
+};
+
+struct octeon_hcd {
+ spinlock_t lock;
+ struct cvmx_usb_state usb;
+};
+
+/* This macro spins on a register waiting for it to reach a condition. */
+#define CVMX_WAIT_FOR_FIELD32(address, _union, cond, timeout_usec) \
+ ({int result; \
+ do { \
+ uint64_t done = cvmx_get_cycle() + (uint64_t)timeout_usec * \
+ octeon_get_clock_rate() / 1000000; \
+ union _union c; \
+ \
+ while (1) { \
+ c.u32 = cvmx_usb_read_csr32(usb, address); \
+ \
+ if (cond) { \
+ result = 0; \
+ break; \
+ } else if (cvmx_get_cycle() > done) { \
+ result = -1; \
+ break; \
+ } else \
+ cvmx_wait(100); \
+ } \
+ } while (0); \
+ result; })
+
+/*
+ * This macro logically sets a single field in a CSR. It does the sequence
+ * read, modify, and write
+ */
+#define USB_SET_FIELD32(address, _union, field, value) \
+ do { \
+ union _union c; \
+ \
+ c.u32 = cvmx_usb_read_csr32(usb, address); \
+ c.s.field = value; \
+ cvmx_usb_write_csr32(usb, address, c.u32); \
+ } while (0)
+
+/* Returns the IO address to push/pop stuff data from the FIFOs */
+#define USB_FIFO_ADDRESS(channel, usb_index) \
+ (CVMX_USBCX_GOTGCTL(usb_index) + ((channel)+1)*0x1000)
+
+/**
+ * struct octeon_temp_buffer - a bounce buffer for USB transfers
+ * @orig_buffer: the original buffer passed by the USB stack
+ * @data: the newly allocated temporary buffer (excluding meta-data)
+ *
+ * Both the DMA engine and FIFO mode will always transfer full 32-bit words. If
+ * the buffer is too short, we need to allocate a temporary one, and this struct
+ * represents it.
+ */
+struct octeon_temp_buffer {
+ void *orig_buffer;
+ u8 data[0];
+};
+
+static inline struct octeon_hcd *cvmx_usb_to_octeon(struct cvmx_usb_state *p)
+{
+ return container_of(p, struct octeon_hcd, usb);
+}
+
+static inline struct usb_hcd *octeon_to_hcd(struct octeon_hcd *p)
+{
+ return container_of((void *)p, struct usb_hcd, hcd_priv);
+}
+
+/**
+ * octeon_alloc_temp_buffer - allocate a temporary buffer for USB transfer
+ * (if needed)
+ * @urb: URB.
+ * @mem_flags: Memory allocation flags.
+ *
+ * This function allocates a temporary bounce buffer whenever it's needed
+ * due to HW limitations.
+ */
+static int octeon_alloc_temp_buffer(struct urb *urb, gfp_t mem_flags)
+{
+ struct octeon_temp_buffer *temp;
+
+ if (urb->num_sgs || urb->sg ||
+ (urb->transfer_flags & URB_NO_TRANSFER_DMA_MAP) ||
+ !(urb->transfer_buffer_length % sizeof(u32)))
+ return 0;
+
+ temp = kmalloc(ALIGN(urb->transfer_buffer_length, sizeof(u32)) +
+ sizeof(*temp), mem_flags);
+ if (!temp)
+ return -ENOMEM;
+
+ temp->orig_buffer = urb->transfer_buffer;
+ if (usb_urb_dir_out(urb))
+ memcpy(temp->data, urb->transfer_buffer,
+ urb->transfer_buffer_length);
+ urb->transfer_buffer = temp->data;
+ urb->transfer_flags |= URB_ALIGNED_TEMP_BUFFER;
+
+ return 0;
+}
+
+/**
+ * octeon_free_temp_buffer - free a temporary buffer used by USB transfers.
+ * @urb: URB.
+ *
+ * Frees a buffer allocated by octeon_alloc_temp_buffer().
+ */
+static void octeon_free_temp_buffer(struct urb *urb)
+{
+ struct octeon_temp_buffer *temp;
+
+ if (!(urb->transfer_flags & URB_ALIGNED_TEMP_BUFFER))
+ return;
+
+ temp = container_of(urb->transfer_buffer, struct octeon_temp_buffer,
+ data);
+ if (usb_urb_dir_in(urb))
+ memcpy(temp->orig_buffer, urb->transfer_buffer,
+ urb->actual_length);
+ urb->transfer_buffer = temp->orig_buffer;
+ urb->transfer_flags &= ~URB_ALIGNED_TEMP_BUFFER;
+ kfree(temp);
+}
+
+/**
+ * octeon_map_urb_for_dma - Octeon-specific map_urb_for_dma().
+ * @hcd: USB HCD structure.
+ * @urb: URB.
+ * @mem_flags: Memory allocation flags.
+ */
+static int octeon_map_urb_for_dma(struct usb_hcd *hcd, struct urb *urb,
+ gfp_t mem_flags)
+{
+ int ret;
+
+ ret = octeon_alloc_temp_buffer(urb, mem_flags);
+ if (ret)
+ return ret;
+
+ ret = usb_hcd_map_urb_for_dma(hcd, urb, mem_flags);
+ if (ret)
+ octeon_free_temp_buffer(urb);
+
+ return ret;
+}
+
+/**
+ * octeon_unmap_urb_for_dma - Octeon-specific unmap_urb_for_dma()
+ * @hcd: USB HCD structure.
+ * @urb: URB.
+ */
+static void octeon_unmap_urb_for_dma(struct usb_hcd *hcd, struct urb *urb)
+{
+ usb_hcd_unmap_urb_for_dma(hcd, urb);
+ octeon_free_temp_buffer(urb);
+}
+
+/**
+ * Read a USB 32bit CSR. It performs the necessary address swizzle
+ * for 32bit CSRs and logs the value in a readable format if
+ * debugging is on.
+ *
+ * @usb: USB block this access is for
+ * @address: 64bit address to read
+ *
+ * Returns: Result of the read
+ */
+static inline uint32_t cvmx_usb_read_csr32(struct cvmx_usb_state *usb,
+ uint64_t address)
+{
+ uint32_t result = cvmx_read64_uint32(address ^ 4);
+ return result;
+}
+
+
+/**
+ * Write a USB 32bit CSR. It performs the necessary address
+ * swizzle for 32bit CSRs and logs the value in a readable format
+ * if debugging is on.
+ *
+ * @usb: USB block this access is for
+ * @address: 64bit address to write
+ * @value: Value to write
+ */
+static inline void cvmx_usb_write_csr32(struct cvmx_usb_state *usb,
+ uint64_t address, uint32_t value)
+{
+ cvmx_write64_uint32(address ^ 4, value);
+ cvmx_read64_uint64(CVMX_USBNX_DMA0_INB_CHN0(usb->index));
+}
+
+/**
+ * Return non zero if this pipe connects to a non HIGH speed
+ * device through a high speed hub.
+ *
+ * @usb: USB block this access is for
+ * @pipe: Pipe to check
+ *
+ * Returns: Non zero if we need to do split transactions
+ */
+static inline int cvmx_usb_pipe_needs_split(struct cvmx_usb_state *usb,
+ struct cvmx_usb_pipe *pipe)
+{
+ return pipe->device_speed != CVMX_USB_SPEED_HIGH &&
+ usb->usbcx_hprt.s.prtspd == CVMX_USB_SPEED_HIGH;
+}
+
+
+/**
+ * Trivial utility function to return the correct PID for a pipe
+ *
+ * @pipe: pipe to check
+ *
+ * Returns: PID for pipe
+ */
+static inline int cvmx_usb_get_data_pid(struct cvmx_usb_pipe *pipe)
+{
+ if (pipe->pid_toggle)
+ return 2; /* Data1 */
+ return 0; /* Data0 */
+}
+
+static void cvmx_fifo_setup(struct cvmx_usb_state *usb)
+{
+ union cvmx_usbcx_ghwcfg3 usbcx_ghwcfg3;
+ union cvmx_usbcx_gnptxfsiz npsiz;
+ union cvmx_usbcx_hptxfsiz psiz;
+
+ usbcx_ghwcfg3.u32 = cvmx_usb_read_csr32(usb,
+ CVMX_USBCX_GHWCFG3(usb->index));
+
+ /*
+ * Program the USBC_GRXFSIZ register to select the size of the receive
+ * FIFO (25%).
+ */
+ USB_SET_FIELD32(CVMX_USBCX_GRXFSIZ(usb->index), cvmx_usbcx_grxfsiz,
+ rxfdep, usbcx_ghwcfg3.s.dfifodepth / 4);
+
+ /*
+ * Program the USBC_GNPTXFSIZ register to select the size and the start
+ * address of the non-periodic transmit FIFO for nonperiodic
+ * transactions (50%).
+ */
+ npsiz.u32 = cvmx_usb_read_csr32(usb, CVMX_USBCX_GNPTXFSIZ(usb->index));
+ npsiz.s.nptxfdep = usbcx_ghwcfg3.s.dfifodepth / 2;
+ npsiz.s.nptxfstaddr = usbcx_ghwcfg3.s.dfifodepth / 4;
+ cvmx_usb_write_csr32(usb, CVMX_USBCX_GNPTXFSIZ(usb->index), npsiz.u32);
+
+ /*
+ * Program the USBC_HPTXFSIZ register to select the size and start
+ * address of the periodic transmit FIFO for periodic transactions
+ * (25%).
+ */
+ psiz.u32 = cvmx_usb_read_csr32(usb, CVMX_USBCX_HPTXFSIZ(usb->index));
+ psiz.s.ptxfsize = usbcx_ghwcfg3.s.dfifodepth / 4;
+ psiz.s.ptxfstaddr = 3 * usbcx_ghwcfg3.s.dfifodepth / 4;
+ cvmx_usb_write_csr32(usb, CVMX_USBCX_HPTXFSIZ(usb->index), psiz.u32);
+
+ /* Flush all FIFOs */
+ USB_SET_FIELD32(CVMX_USBCX_GRSTCTL(usb->index),
+ cvmx_usbcx_grstctl, txfnum, 0x10);
+ USB_SET_FIELD32(CVMX_USBCX_GRSTCTL(usb->index),
+ cvmx_usbcx_grstctl, txfflsh, 1);
+ CVMX_WAIT_FOR_FIELD32(CVMX_USBCX_GRSTCTL(usb->index),
+ cvmx_usbcx_grstctl, c.s.txfflsh == 0, 100);
+ USB_SET_FIELD32(CVMX_USBCX_GRSTCTL(usb->index),
+ cvmx_usbcx_grstctl, rxfflsh, 1);
+ CVMX_WAIT_FOR_FIELD32(CVMX_USBCX_GRSTCTL(usb->index),
+ cvmx_usbcx_grstctl, c.s.rxfflsh == 0, 100);
+}
+
+/**
+ * Shutdown a USB port after a call to cvmx_usb_initialize().
+ * The port should be disabled with all pipes closed when this
+ * function is called.
+ *
+ * @usb: USB device state populated by cvmx_usb_initialize().
+ *
+ * Returns: 0 or a negative error code.
+ */
+static int cvmx_usb_shutdown(struct cvmx_usb_state *usb)
+{
+ union cvmx_usbnx_clk_ctl usbn_clk_ctl;
+
+ /* Make sure all pipes are closed */
+ if (!list_empty(&usb->idle_pipes) ||
+ !list_empty(&usb->active_pipes[CVMX_USB_TRANSFER_ISOCHRONOUS]) ||
+ !list_empty(&usb->active_pipes[CVMX_USB_TRANSFER_INTERRUPT]) ||
+ !list_empty(&usb->active_pipes[CVMX_USB_TRANSFER_CONTROL]) ||
+ !list_empty(&usb->active_pipes[CVMX_USB_TRANSFER_BULK]))
+ return -EBUSY;
+
+ /* Disable the clocks and put them in power on reset */
+ usbn_clk_ctl.u64 = cvmx_read64_uint64(CVMX_USBNX_CLK_CTL(usb->index));
+ usbn_clk_ctl.s.enable = 1;
+ usbn_clk_ctl.s.por = 1;
+ usbn_clk_ctl.s.hclk_rst = 1;
+ usbn_clk_ctl.s.prst = 0;
+ usbn_clk_ctl.s.hrst = 0;
+ cvmx_write64_uint64(CVMX_USBNX_CLK_CTL(usb->index), usbn_clk_ctl.u64);
+ return 0;
+}
+
+/**
+ * Initialize a USB port for use. This must be called before any
+ * other access to the Octeon USB port is made. The port starts
+ * off in the disabled state.
+ *
+ * @dev: Pointer to struct device for logging purposes.
+ * @usb: Pointer to struct cvmx_usb_state.
+ *
+ * Returns: 0 or a negative error code.
+ */
+static int cvmx_usb_initialize(struct device *dev,
+ struct cvmx_usb_state *usb)
+{
+ int channel;
+ int divisor;
+ int retries = 0;
+ union cvmx_usbcx_hcfg usbcx_hcfg;
+ union cvmx_usbnx_clk_ctl usbn_clk_ctl;
+ union cvmx_usbcx_gintsts usbc_gintsts;
+ union cvmx_usbcx_gahbcfg usbcx_gahbcfg;
+ union cvmx_usbcx_gintmsk usbcx_gintmsk;
+ union cvmx_usbcx_gusbcfg usbcx_gusbcfg;
+ union cvmx_usbnx_usbp_ctl_status usbn_usbp_ctl_status;
+
+retry:
+ /*
+ * Power On Reset and PHY Initialization
+ *
+ * 1. Wait for DCOK to assert (nothing to do)
+ *
+ * 2a. Write USBN0/1_CLK_CTL[POR] = 1 and
+ * USBN0/1_CLK_CTL[HRST,PRST,HCLK_RST] = 0
+ */
+ usbn_clk_ctl.u64 = cvmx_read64_uint64(CVMX_USBNX_CLK_CTL(usb->index));
+ usbn_clk_ctl.s.por = 1;
+ usbn_clk_ctl.s.hrst = 0;
+ usbn_clk_ctl.s.prst = 0;
+ usbn_clk_ctl.s.hclk_rst = 0;
+ usbn_clk_ctl.s.enable = 0;
+ /*
+ * 2b. Select the USB reference clock/crystal parameters by writing
+ * appropriate values to USBN0/1_CLK_CTL[P_C_SEL, P_RTYPE, P_COM_ON]
+ */
+ if (usb->init_flags & CVMX_USB_INITIALIZE_FLAGS_CLOCK_XO_GND) {
+ /*
+ * The USB port uses 12/24/48MHz 2.5V board clock
+ * source at USB_XO. USB_XI should be tied to GND.
+ * Most Octeon evaluation boards require this setting
+ */
+ if (OCTEON_IS_MODEL(OCTEON_CN3XXX) ||
+ OCTEON_IS_MODEL(OCTEON_CN56XX) ||
+ OCTEON_IS_MODEL(OCTEON_CN50XX))
+ /* From CN56XX,CN50XX,CN31XX,CN30XX manuals */
+ usbn_clk_ctl.s.p_rtype = 2; /* p_rclk=1 & p_xenbn=0 */
+ else
+ /* From CN52XX manual */
+ usbn_clk_ctl.s.p_rtype = 1;
+
+ switch (usb->init_flags &
+ CVMX_USB_INITIALIZE_FLAGS_CLOCK_MHZ_MASK) {
+ case CVMX_USB_INITIALIZE_FLAGS_CLOCK_12MHZ:
+ usbn_clk_ctl.s.p_c_sel = 0;
+ break;
+ case CVMX_USB_INITIALIZE_FLAGS_CLOCK_24MHZ:
+ usbn_clk_ctl.s.p_c_sel = 1;
+ break;
+ case CVMX_USB_INITIALIZE_FLAGS_CLOCK_48MHZ:
+ usbn_clk_ctl.s.p_c_sel = 2;
+ break;
+ }
+ } else {
+ /*
+ * The USB port uses a 12MHz crystal as clock source
+ * at USB_XO and USB_XI
+ */
+ if (OCTEON_IS_MODEL(OCTEON_CN3XXX))
+ /* From CN31XX,CN30XX manual */
+ usbn_clk_ctl.s.p_rtype = 3; /* p_rclk=1 & p_xenbn=1 */
+ else
+ /* From CN56XX,CN52XX,CN50XX manuals. */
+ usbn_clk_ctl.s.p_rtype = 0;
+
+ usbn_clk_ctl.s.p_c_sel = 0;
+ }
+ /*
+ * 2c. Select the HCLK via writing USBN0/1_CLK_CTL[DIVIDE, DIVIDE2] and
+ * setting USBN0/1_CLK_CTL[ENABLE] = 1. Divide the core clock down
+ * such that USB is as close as possible to 125Mhz
+ */
+ divisor = DIV_ROUND_UP(octeon_get_clock_rate(), 125000000);
+ /* Lower than 4 doesn't seem to work properly */
+ if (divisor < 4)
+ divisor = 4;
+ usbn_clk_ctl.s.divide = divisor;
+ usbn_clk_ctl.s.divide2 = 0;
+ cvmx_write64_uint64(CVMX_USBNX_CLK_CTL(usb->index), usbn_clk_ctl.u64);
+
+ /* 2d. Write USBN0/1_CLK_CTL[HCLK_RST] = 1 */
+ usbn_clk_ctl.s.hclk_rst = 1;
+ cvmx_write64_uint64(CVMX_USBNX_CLK_CTL(usb->index), usbn_clk_ctl.u64);
+ /* 2e. Wait 64 core-clock cycles for HCLK to stabilize */
+ cvmx_wait(64);
+ /*
+ * 3. Program the power-on reset field in the USBN clock-control
+ * register:
+ * USBN_CLK_CTL[POR] = 0
+ */
+ usbn_clk_ctl.s.por = 0;
+ cvmx_write64_uint64(CVMX_USBNX_CLK_CTL(usb->index), usbn_clk_ctl.u64);
+ /* 4. Wait 1 ms for PHY clock to start */
+ mdelay(1);
+ /*
+ * 5. Program the Reset input from automatic test equipment field in the
+ * USBP control and status register:
+ * USBN_USBP_CTL_STATUS[ATE_RESET] = 1
+ */
+ usbn_usbp_ctl_status.u64 =
+ cvmx_read64_uint64(CVMX_USBNX_USBP_CTL_STATUS(usb->index));
+ usbn_usbp_ctl_status.s.ate_reset = 1;
+ cvmx_write64_uint64(CVMX_USBNX_USBP_CTL_STATUS(usb->index),
+ usbn_usbp_ctl_status.u64);
+ /* 6. Wait 10 cycles */
+ cvmx_wait(10);
+ /*
+ * 7. Clear ATE_RESET field in the USBN clock-control register:
+ * USBN_USBP_CTL_STATUS[ATE_RESET] = 0
+ */
+ usbn_usbp_ctl_status.s.ate_reset = 0;
+ cvmx_write64_uint64(CVMX_USBNX_USBP_CTL_STATUS(usb->index),
+ usbn_usbp_ctl_status.u64);
+ /*
+ * 8. Program the PHY reset field in the USBN clock-control register:
+ * USBN_CLK_CTL[PRST] = 1
+ */
+ usbn_clk_ctl.s.prst = 1;
+ cvmx_write64_uint64(CVMX_USBNX_CLK_CTL(usb->index), usbn_clk_ctl.u64);
+ /*
+ * 9. Program the USBP control and status register to select host or
+ * device mode. USBN_USBP_CTL_STATUS[HST_MODE] = 0 for host, = 1 for
+ * device
+ */
+ usbn_usbp_ctl_status.s.hst_mode = 0;
+ cvmx_write64_uint64(CVMX_USBNX_USBP_CTL_STATUS(usb->index),
+ usbn_usbp_ctl_status.u64);
+ /* 10. Wait 1 us */
+ udelay(1);
+ /*
+ * 11. Program the hreset_n field in the USBN clock-control register:
+ * USBN_CLK_CTL[HRST] = 1
+ */
+ usbn_clk_ctl.s.hrst = 1;
+ cvmx_write64_uint64(CVMX_USBNX_CLK_CTL(usb->index), usbn_clk_ctl.u64);
+ /* 12. Proceed to USB core initialization */
+ usbn_clk_ctl.s.enable = 1;
+ cvmx_write64_uint64(CVMX_USBNX_CLK_CTL(usb->index), usbn_clk_ctl.u64);
+ udelay(1);
+
+ /*
+ * USB Core Initialization
+ *
+ * 1. Read USBC_GHWCFG1, USBC_GHWCFG2, USBC_GHWCFG3, USBC_GHWCFG4 to
+ * determine USB core configuration parameters.
+ *
+ * Nothing needed
+ *
+ * 2. Program the following fields in the global AHB configuration
+ * register (USBC_GAHBCFG)
+ * DMA mode, USBC_GAHBCFG[DMAEn]: 1 = DMA mode, 0 = slave mode
+ * Burst length, USBC_GAHBCFG[HBSTLEN] = 0
+ * Nonperiodic TxFIFO empty level (slave mode only),
+ * USBC_GAHBCFG[NPTXFEMPLVL]
+ * Periodic TxFIFO empty level (slave mode only),
+ * USBC_GAHBCFG[PTXFEMPLVL]
+ * Global interrupt mask, USBC_GAHBCFG[GLBLINTRMSK] = 1
+ */
+ usbcx_gahbcfg.u32 = 0;
+ usbcx_gahbcfg.s.dmaen = !(usb->init_flags &
+ CVMX_USB_INITIALIZE_FLAGS_NO_DMA);
+ usbcx_gahbcfg.s.hbstlen = 0;
+ usbcx_gahbcfg.s.nptxfemplvl = 1;
+ usbcx_gahbcfg.s.ptxfemplvl = 1;
+ usbcx_gahbcfg.s.glblintrmsk = 1;
+ cvmx_usb_write_csr32(usb, CVMX_USBCX_GAHBCFG(usb->index),
+ usbcx_gahbcfg.u32);
+
+ /*
+ * 3. Program the following fields in USBC_GUSBCFG register.
+ * HS/FS timeout calibration, USBC_GUSBCFG[TOUTCAL] = 0
+ * ULPI DDR select, USBC_GUSBCFG[DDRSEL] = 0
+ * USB turnaround time, USBC_GUSBCFG[USBTRDTIM] = 0x5
+ * PHY low-power clock select, USBC_GUSBCFG[PHYLPWRCLKSEL] = 0
+ */
+ usbcx_gusbcfg.u32 = cvmx_usb_read_csr32(usb,
+ CVMX_USBCX_GUSBCFG(usb->index));
+ usbcx_gusbcfg.s.toutcal = 0;
+ usbcx_gusbcfg.s.ddrsel = 0;
+ usbcx_gusbcfg.s.usbtrdtim = 0x5;
+ usbcx_gusbcfg.s.phylpwrclksel = 0;
+ cvmx_usb_write_csr32(usb, CVMX_USBCX_GUSBCFG(usb->index),
+ usbcx_gusbcfg.u32);
+
+ /*
+ * 4. The software must unmask the following bits in the USBC_GINTMSK
+ * register.
+ * OTG interrupt mask, USBC_GINTMSK[OTGINTMSK] = 1
+ * Mode mismatch interrupt mask, USBC_GINTMSK[MODEMISMSK] = 1
+ */
+ usbcx_gintmsk.u32 = cvmx_usb_read_csr32(usb,
+ CVMX_USBCX_GINTMSK(usb->index));
+ usbcx_gintmsk.s.otgintmsk = 1;
+ usbcx_gintmsk.s.modemismsk = 1;
+ usbcx_gintmsk.s.hchintmsk = 1;
+ usbcx_gintmsk.s.sofmsk = 0;
+ /* We need RX FIFO interrupts if we don't have DMA */
+ if (usb->init_flags & CVMX_USB_INITIALIZE_FLAGS_NO_DMA)
+ usbcx_gintmsk.s.rxflvlmsk = 1;
+ cvmx_usb_write_csr32(usb, CVMX_USBCX_GINTMSK(usb->index),
+ usbcx_gintmsk.u32);
+
+ /*
+ * Disable all channel interrupts. We'll enable them per channel later.
+ */
+ for (channel = 0; channel < 8; channel++)
+ cvmx_usb_write_csr32(usb,
+ CVMX_USBCX_HCINTMSKX(channel, usb->index),
+ 0);
+
+ /*
+ * Host Port Initialization
+ *
+ * 1. Program the host-port interrupt-mask field to unmask,
+ * USBC_GINTMSK[PRTINT] = 1
+ */
+ USB_SET_FIELD32(CVMX_USBCX_GINTMSK(usb->index),
+ cvmx_usbcx_gintmsk, prtintmsk, 1);
+ USB_SET_FIELD32(CVMX_USBCX_GINTMSK(usb->index),
+ cvmx_usbcx_gintmsk, disconnintmsk, 1);
+
+ /*
+ * 2. Program the USBC_HCFG register to select full-speed host
+ * or high-speed host.
+ */
+ usbcx_hcfg.u32 = cvmx_usb_read_csr32(usb, CVMX_USBCX_HCFG(usb->index));
+ usbcx_hcfg.s.fslssupp = 0;
+ usbcx_hcfg.s.fslspclksel = 0;
+ cvmx_usb_write_csr32(usb, CVMX_USBCX_HCFG(usb->index), usbcx_hcfg.u32);
+
+ cvmx_fifo_setup(usb);
+
+ /*
+ * If the controller is getting port events right after the reset, it
+ * means the initialization failed. Try resetting the controller again
+ * in such case. This is seen to happen after cold boot on DSR-1000N.
+ */
+ usbc_gintsts.u32 = cvmx_usb_read_csr32(usb,
+ CVMX_USBCX_GINTSTS(usb->index));
+ cvmx_usb_write_csr32(usb, CVMX_USBCX_GINTSTS(usb->index),
+ usbc_gintsts.u32);
+ dev_dbg(dev, "gintsts after reset: 0x%x\n", (int)usbc_gintsts.u32);
+ if (!usbc_gintsts.s.disconnint && !usbc_gintsts.s.prtint)
+ return 0;
+ if (retries++ >= 5)
+ return -EAGAIN;
+ dev_info(dev, "controller reset failed (gintsts=0x%x) - retrying\n",
+ (int)usbc_gintsts.u32);
+ msleep(50);
+ cvmx_usb_shutdown(usb);
+ msleep(50);
+ goto retry;
+}
+
+/**
+ * Reset a USB port. After this call succeeds, the USB port is
+ * online and servicing requests.
+ *
+ * @usb: USB device state populated by cvmx_usb_initialize().
+ */
+static void cvmx_usb_reset_port(struct cvmx_usb_state *usb)
+{
+ usb->usbcx_hprt.u32 = cvmx_usb_read_csr32(usb,
+ CVMX_USBCX_HPRT(usb->index));
+
+ /* Program the port reset bit to start the reset process */
+ USB_SET_FIELD32(CVMX_USBCX_HPRT(usb->index), cvmx_usbcx_hprt,
+ prtrst, 1);
+
+ /*
+ * Wait at least 50ms (high speed), or 10ms (full speed) for the reset
+ * process to complete.
+ */
+ mdelay(50);
+
+ /* Program the port reset bit to 0, USBC_HPRT[PRTRST] = 0 */
+ USB_SET_FIELD32(CVMX_USBCX_HPRT(usb->index), cvmx_usbcx_hprt,
+ prtrst, 0);
+
+ /*
+ * Read the port speed field to get the enumerated speed,
+ * USBC_HPRT[PRTSPD].
+ */
+ usb->usbcx_hprt.u32 = cvmx_usb_read_csr32(usb,
+ CVMX_USBCX_HPRT(usb->index));
+}
+
+
+/**
+ * Disable a USB port. After this call the USB port will not
+ * generate data transfers and will not generate events.
+ * Transactions in process will fail and call their
+ * associated callbacks.
+ *
+ * @usb: USB device state populated by cvmx_usb_initialize().
+ *
+ * Returns: 0 or a negative error code.
+ */
+static int cvmx_usb_disable(struct cvmx_usb_state *usb)
+{
+ /* Disable the port */
+ USB_SET_FIELD32(CVMX_USBCX_HPRT(usb->index), cvmx_usbcx_hprt,
+ prtena, 1);
+ return 0;
+}
+
+
+/**
+ * Get the current state of the USB port. Use this call to
+ * determine if the usb port has anything connected, is enabled,
+ * or has some sort of error condition. The return value of this
+ * call has "changed" bits to signal of the value of some fields
+ * have changed between calls.
+ *
+ * @usb: USB device state populated by cvmx_usb_initialize().
+ *
+ * Returns: Port status information
+ */
+static struct cvmx_usb_port_status cvmx_usb_get_status(
+ struct cvmx_usb_state *usb)
+{
+ union cvmx_usbcx_hprt usbc_hprt;
+ struct cvmx_usb_port_status result;
+
+ memset(&result, 0, sizeof(result));
+
+ usbc_hprt.u32 = cvmx_usb_read_csr32(usb, CVMX_USBCX_HPRT(usb->index));
+ result.port_enabled = usbc_hprt.s.prtena;
+ result.port_over_current = usbc_hprt.s.prtovrcurract;
+ result.port_powered = usbc_hprt.s.prtpwr;
+ result.port_speed = usbc_hprt.s.prtspd;
+ result.connected = usbc_hprt.s.prtconnsts;
+ result.connect_change =
+ (result.connected != usb->port_status.connected);
+
+ return result;
+}
+
+/**
+ * Open a virtual pipe between the host and a USB device. A pipe
+ * must be opened before data can be transferred between a device
+ * and Octeon.
+ *
+ * @usb: USB device state populated by cvmx_usb_initialize().
+ * @device_addr:
+ * USB device address to open the pipe to
+ * (0-127).
+ * @endpoint_num:
+ * USB endpoint number to open the pipe to
+ * (0-15).
+ * @device_speed:
+ * The speed of the device the pipe is going
+ * to. This must match the device's speed,
+ * which may be different than the port speed.
+ * @max_packet: The maximum packet length the device can
+ * transmit/receive (low speed=0-8, full
+ * speed=0-1023, high speed=0-1024). This value
+ * comes from the standard endpoint descriptor
+ * field wMaxPacketSize bits <10:0>.
+ * @transfer_type:
+ * The type of transfer this pipe is for.
+ * @transfer_dir:
+ * The direction the pipe is in. This is not
+ * used for control pipes.
+ * @interval: For ISOCHRONOUS and INTERRUPT transfers,
+ * this is how often the transfer is scheduled
+ * for. All other transfers should specify
+ * zero. The units are in frames (8000/sec at
+ * high speed, 1000/sec for full speed).
+ * @multi_count:
+ * For high speed devices, this is the maximum
+ * allowed number of packet per microframe.
+ * Specify zero for non high speed devices. This
+ * value comes from the standard endpoint descriptor
+ * field wMaxPacketSize bits <12:11>.
+ * @hub_device_addr:
+ * Hub device address this device is connected
+ * to. Devices connected directly to Octeon
+ * use zero. This is only used when the device
+ * is full/low speed behind a high speed hub.
+ * The address will be of the high speed hub,
+ * not and full speed hubs after it.
+ * @hub_port: Which port on the hub the device is
+ * connected. Use zero for devices connected
+ * directly to Octeon. Like hub_device_addr,
+ * this is only used for full/low speed
+ * devices behind a high speed hub.
+ *
+ * Returns: A non-NULL value is a pipe. NULL means an error.
+ */
+static struct cvmx_usb_pipe *cvmx_usb_open_pipe(struct cvmx_usb_state *usb,
+ int device_addr,
+ int endpoint_num,
+ enum cvmx_usb_speed
+ device_speed,
+ int max_packet,
+ enum cvmx_usb_transfer
+ transfer_type,
+ enum cvmx_usb_direction
+ transfer_dir,
+ int interval, int multi_count,
+ int hub_device_addr,
+ int hub_port)
+{
+ struct cvmx_usb_pipe *pipe;
+
+ pipe = kzalloc(sizeof(*pipe), GFP_ATOMIC);
+ if (!pipe)
+ return NULL;
+ if ((device_speed == CVMX_USB_SPEED_HIGH) &&
+ (transfer_dir == CVMX_USB_DIRECTION_OUT) &&
+ (transfer_type == CVMX_USB_TRANSFER_BULK))
+ pipe->flags |= CVMX_USB_PIPE_FLAGS_NEED_PING;
+ pipe->device_addr = device_addr;
+ pipe->endpoint_num = endpoint_num;
+ pipe->device_speed = device_speed;
+ pipe->max_packet = max_packet;
+ pipe->transfer_type = transfer_type;
+ pipe->transfer_dir = transfer_dir;
+ INIT_LIST_HEAD(&pipe->transactions);
+
+ /*
+ * All pipes use interval to rate limit NAK processing. Force an
+ * interval if one wasn't supplied
+ */
+ if (!interval)
+ interval = 1;
+ if (cvmx_usb_pipe_needs_split(usb, pipe)) {
+ pipe->interval = interval*8;
+ /* Force start splits to be schedule on uFrame 0 */
+ pipe->next_tx_frame = ((usb->frame_number+7)&~7) +
+ pipe->interval;
+ } else {
+ pipe->interval = interval;
+ pipe->next_tx_frame = usb->frame_number + pipe->interval;
+ }
+ pipe->multi_count = multi_count;
+ pipe->hub_device_addr = hub_device_addr;
+ pipe->hub_port = hub_port;
+ pipe->pid_toggle = 0;
+ pipe->split_sc_frame = -1;
+ list_add_tail(&pipe->node, &usb->idle_pipes);
+
+ /*
+ * We don't need to tell the hardware about this pipe yet since
+ * it doesn't have any submitted requests
+ */
+
+ return pipe;
+}
+
+
+/**
+ * Poll the RX FIFOs and remove data as needed. This function is only used
+ * in non DMA mode. It is very important that this function be called quickly
+ * enough to prevent FIFO overflow.
+ *
+ * @usb: USB device state populated by cvmx_usb_initialize().
+ */
+static void cvmx_usb_poll_rx_fifo(struct cvmx_usb_state *usb)
+{
+ union cvmx_usbcx_grxstsph rx_status;
+ int channel;
+ int bytes;
+ uint64_t address;
+ uint32_t *ptr;
+
+ rx_status.u32 = cvmx_usb_read_csr32(usb,
+ CVMX_USBCX_GRXSTSPH(usb->index));
+ /* Only read data if IN data is there */
+ if (rx_status.s.pktsts != 2)
+ return;
+ /* Check if no data is available */
+ if (!rx_status.s.bcnt)
+ return;
+
+ channel = rx_status.s.chnum;
+ bytes = rx_status.s.bcnt;
+ if (!bytes)
+ return;
+
+ /* Get where the DMA engine would have written this data */
+ address = cvmx_read64_uint64(CVMX_USBNX_DMA0_INB_CHN0(usb->index) +
+ channel * 8);
+
+ ptr = cvmx_phys_to_ptr(address);
+ cvmx_write64_uint64(CVMX_USBNX_DMA0_INB_CHN0(usb->index) + channel * 8,
+ address + bytes);
+
+ /* Loop writing the FIFO data for this packet into memory */
+ while (bytes > 0) {
+ *ptr++ = cvmx_usb_read_csr32(usb,
+ USB_FIFO_ADDRESS(channel, usb->index));
+ bytes -= 4;
+ }
+ CVMX_SYNCW;
+}
+
+
+/**
+ * Fill the TX hardware fifo with data out of the software
+ * fifos
+ *
+ * @usb: USB device state populated by cvmx_usb_initialize().
+ * @fifo: Software fifo to use
+ * @available: Amount of space in the hardware fifo
+ *
+ * Returns: Non zero if the hardware fifo was too small and needs
+ * to be serviced again.
+ */
+static int cvmx_usb_fill_tx_hw(struct cvmx_usb_state *usb,
+ struct cvmx_usb_tx_fifo *fifo, int available)
+{
+ /*
+ * We're done either when there isn't anymore space or the software FIFO
+ * is empty
+ */
+ while (available && (fifo->head != fifo->tail)) {
+ int i = fifo->tail;
+ const uint32_t *ptr = cvmx_phys_to_ptr(fifo->entry[i].address);
+ uint64_t csr_address = USB_FIFO_ADDRESS(fifo->entry[i].channel,
+ usb->index) ^ 4;
+ int words = available;
+
+ /* Limit the amount of data to waht the SW fifo has */
+ if (fifo->entry[i].size <= available) {
+ words = fifo->entry[i].size;
+ fifo->tail++;
+ if (fifo->tail > MAX_CHANNELS)
+ fifo->tail = 0;
+ }
+
+ /* Update the next locations and counts */
+ available -= words;
+ fifo->entry[i].address += words * 4;
+ fifo->entry[i].size -= words;
+
+ /*
+ * Write the HW fifo data. The read every three writes is due
+ * to an errata on CN3XXX chips
+ */
+ while (words > 3) {
+ cvmx_write64_uint32(csr_address, *ptr++);
+ cvmx_write64_uint32(csr_address, *ptr++);
+ cvmx_write64_uint32(csr_address, *ptr++);
+ cvmx_read64_uint64(
+ CVMX_USBNX_DMA0_INB_CHN0(usb->index));
+ words -= 3;
+ }
+ cvmx_write64_uint32(csr_address, *ptr++);
+ if (--words) {
+ cvmx_write64_uint32(csr_address, *ptr++);
+ if (--words)
+ cvmx_write64_uint32(csr_address, *ptr++);
+ }
+ cvmx_read64_uint64(CVMX_USBNX_DMA0_INB_CHN0(usb->index));
+ }
+ return fifo->head != fifo->tail;
+}
+
+
+/**
+ * Check the hardware FIFOs and fill them as needed
+ *
+ * @usb: USB device state populated by cvmx_usb_initialize().
+ */
+static void cvmx_usb_poll_tx_fifo(struct cvmx_usb_state *usb)
+{
+ if (usb->periodic.head != usb->periodic.tail) {
+ union cvmx_usbcx_hptxsts tx_status;
+
+ tx_status.u32 = cvmx_usb_read_csr32(usb,
+ CVMX_USBCX_HPTXSTS(usb->index));
+ if (cvmx_usb_fill_tx_hw(usb, &usb->periodic,
+ tx_status.s.ptxfspcavail))
+ USB_SET_FIELD32(CVMX_USBCX_GINTMSK(usb->index),
+ cvmx_usbcx_gintmsk, ptxfempmsk, 1);
+ else
+ USB_SET_FIELD32(CVMX_USBCX_GINTMSK(usb->index),
+ cvmx_usbcx_gintmsk, ptxfempmsk, 0);
+ }
+
+ if (usb->nonperiodic.head != usb->nonperiodic.tail) {
+ union cvmx_usbcx_gnptxsts tx_status;
+
+ tx_status.u32 = cvmx_usb_read_csr32(usb,
+ CVMX_USBCX_GNPTXSTS(usb->index));
+ if (cvmx_usb_fill_tx_hw(usb, &usb->nonperiodic,
+ tx_status.s.nptxfspcavail))
+ USB_SET_FIELD32(CVMX_USBCX_GINTMSK(usb->index),
+ cvmx_usbcx_gintmsk, nptxfempmsk, 1);
+ else
+ USB_SET_FIELD32(CVMX_USBCX_GINTMSK(usb->index),
+ cvmx_usbcx_gintmsk, nptxfempmsk, 0);
+ }
+}
+
+
+/**
+ * Fill the TX FIFO with an outgoing packet
+ *
+ * @usb: USB device state populated by cvmx_usb_initialize().
+ * @channel: Channel number to get packet from
+ */
+static void cvmx_usb_fill_tx_fifo(struct cvmx_usb_state *usb, int channel)
+{
+ union cvmx_usbcx_hccharx hcchar;
+ union cvmx_usbcx_hcspltx usbc_hcsplt;
+ union cvmx_usbcx_hctsizx usbc_hctsiz;
+ struct cvmx_usb_tx_fifo *fifo;
+
+ /* We only need to fill data on outbound channels */
+ hcchar.u32 = cvmx_usb_read_csr32(usb,
+ CVMX_USBCX_HCCHARX(channel, usb->index));
+ if (hcchar.s.epdir != CVMX_USB_DIRECTION_OUT)
+ return;
+
+ /* OUT Splits only have data on the start and not the complete */
+ usbc_hcsplt.u32 = cvmx_usb_read_csr32(usb,
+ CVMX_USBCX_HCSPLTX(channel, usb->index));
+ if (usbc_hcsplt.s.spltena && usbc_hcsplt.s.compsplt)
+ return;
+
+ /*
+ * Find out how many bytes we need to fill and convert it into 32bit
+ * words.
+ */
+ usbc_hctsiz.u32 = cvmx_usb_read_csr32(usb,
+ CVMX_USBCX_HCTSIZX(channel, usb->index));
+ if (!usbc_hctsiz.s.xfersize)
+ return;
+
+ if ((hcchar.s.eptype == CVMX_USB_TRANSFER_INTERRUPT) ||
+ (hcchar.s.eptype == CVMX_USB_TRANSFER_ISOCHRONOUS))
+ fifo = &usb->periodic;
+ else
+ fifo = &usb->nonperiodic;
+
+ fifo->entry[fifo->head].channel = channel;
+ fifo->entry[fifo->head].address =
+ cvmx_read64_uint64(CVMX_USBNX_DMA0_OUTB_CHN0(usb->index) +
+ channel * 8);
+ fifo->entry[fifo->head].size = (usbc_hctsiz.s.xfersize+3)>>2;
+ fifo->head++;
+ if (fifo->head > MAX_CHANNELS)
+ fifo->head = 0;
+
+ cvmx_usb_poll_tx_fifo(usb);
+}
+
+/**
+ * Perform channel specific setup for Control transactions. All
+ * the generic stuff will already have been done in cvmx_usb_start_channel().
+ *
+ * @usb: USB device state populated by cvmx_usb_initialize().
+ * @channel: Channel to setup
+ * @pipe: Pipe for control transaction
+ */
+static void cvmx_usb_start_channel_control(struct cvmx_usb_state *usb,
+ int channel,
+ struct cvmx_usb_pipe *pipe)
+{
+ struct octeon_hcd *priv = cvmx_usb_to_octeon(usb);
+ struct usb_hcd *hcd = octeon_to_hcd(priv);
+ struct device *dev = hcd->self.controller;
+ struct cvmx_usb_transaction *transaction =
+ list_first_entry(&pipe->transactions, typeof(*transaction),
+ node);
+ struct usb_ctrlrequest *header =
+ cvmx_phys_to_ptr(transaction->control_header);
+ int bytes_to_transfer = transaction->buffer_length -
+ transaction->actual_bytes;
+ int packets_to_transfer;
+ union cvmx_usbcx_hctsizx usbc_hctsiz;
+
+ usbc_hctsiz.u32 = cvmx_usb_read_csr32(usb,
+ CVMX_USBCX_HCTSIZX(channel, usb->index));
+
+ switch (transaction->stage) {
+ case CVMX_USB_STAGE_NON_CONTROL:
+ case CVMX_USB_STAGE_NON_CONTROL_SPLIT_COMPLETE:
+ dev_err(dev, "%s: ERROR - Non control stage\n", __func__);
+ break;
+ case CVMX_USB_STAGE_SETUP:
+ usbc_hctsiz.s.pid = 3; /* Setup */
+ bytes_to_transfer = sizeof(*header);
+ /* All Control operations start with a setup going OUT */
+ USB_SET_FIELD32(CVMX_USBCX_HCCHARX(channel, usb->index),
+ cvmx_usbcx_hccharx, epdir,
+ CVMX_USB_DIRECTION_OUT);
+ /*
+ * Setup send the control header instead of the buffer data. The
+ * buffer data will be used in the next stage
+ */
+ cvmx_write64_uint64(CVMX_USBNX_DMA0_OUTB_CHN0(usb->index) +
+ channel * 8,
+ transaction->control_header);
+ break;
+ case CVMX_USB_STAGE_SETUP_SPLIT_COMPLETE:
+ usbc_hctsiz.s.pid = 3; /* Setup */
+ bytes_to_transfer = 0;
+ /* All Control operations start with a setup going OUT */
+ USB_SET_FIELD32(CVMX_USBCX_HCCHARX(channel, usb->index),
+ cvmx_usbcx_hccharx, epdir,
+ CVMX_USB_DIRECTION_OUT);
+
+ USB_SET_FIELD32(CVMX_USBCX_HCSPLTX(channel, usb->index),
+ cvmx_usbcx_hcspltx, compsplt, 1);
+ break;
+ case CVMX_USB_STAGE_DATA:
+ usbc_hctsiz.s.pid = cvmx_usb_get_data_pid(pipe);
+ if (cvmx_usb_pipe_needs_split(usb, pipe)) {
+ if (header->bRequestType & USB_DIR_IN)
+ bytes_to_transfer = 0;
+ else if (bytes_to_transfer > pipe->max_packet)
+ bytes_to_transfer = pipe->max_packet;
+ }
+ USB_SET_FIELD32(CVMX_USBCX_HCCHARX(channel, usb->index),
+ cvmx_usbcx_hccharx, epdir,
+ ((header->bRequestType & USB_DIR_IN) ?
+ CVMX_USB_DIRECTION_IN :
+ CVMX_USB_DIRECTION_OUT));
+ break;
+ case CVMX_USB_STAGE_DATA_SPLIT_COMPLETE:
+ usbc_hctsiz.s.pid = cvmx_usb_get_data_pid(pipe);
+ if (!(header->bRequestType & USB_DIR_IN))
+ bytes_to_transfer = 0;
+ USB_SET_FIELD32(CVMX_USBCX_HCCHARX(channel, usb->index),
+ cvmx_usbcx_hccharx, epdir,
+ ((header->bRequestType & USB_DIR_IN) ?
+ CVMX_USB_DIRECTION_IN :
+ CVMX_USB_DIRECTION_OUT));
+ USB_SET_FIELD32(CVMX_USBCX_HCSPLTX(channel, usb->index),
+ cvmx_usbcx_hcspltx, compsplt, 1);
+ break;
+ case CVMX_USB_STAGE_STATUS:
+ usbc_hctsiz.s.pid = cvmx_usb_get_data_pid(pipe);
+ bytes_to_transfer = 0;
+ USB_SET_FIELD32(CVMX_USBCX_HCCHARX(channel, usb->index),
+ cvmx_usbcx_hccharx, epdir,
+ ((header->bRequestType & USB_DIR_IN) ?
+ CVMX_USB_DIRECTION_OUT :
+ CVMX_USB_DIRECTION_IN));
+ break;
+ case CVMX_USB_STAGE_STATUS_SPLIT_COMPLETE:
+ usbc_hctsiz.s.pid = cvmx_usb_get_data_pid(pipe);
+ bytes_to_transfer = 0;
+ USB_SET_FIELD32(CVMX_USBCX_HCCHARX(channel, usb->index),
+ cvmx_usbcx_hccharx, epdir,
+ ((header->bRequestType & USB_DIR_IN) ?
+ CVMX_USB_DIRECTION_OUT :
+ CVMX_USB_DIRECTION_IN));
+ USB_SET_FIELD32(CVMX_USBCX_HCSPLTX(channel, usb->index),
+ cvmx_usbcx_hcspltx, compsplt, 1);
+ break;
+ }
+
+ /*
+ * Make sure the transfer never exceeds the byte limit of the hardware.
+ * Further bytes will be sent as continued transactions
+ */
+ if (bytes_to_transfer > MAX_TRANSFER_BYTES) {
+ /* Round MAX_TRANSFER_BYTES to a multiple of out packet size */
+ bytes_to_transfer = MAX_TRANSFER_BYTES / pipe->max_packet;
+ bytes_to_transfer *= pipe->max_packet;
+ }
+
+ /*
+ * Calculate the number of packets to transfer. If the length is zero
+ * we still need to transfer one packet
+ */
+ packets_to_transfer = DIV_ROUND_UP(bytes_to_transfer,
+ pipe->max_packet);
+ if (packets_to_transfer == 0)
+ packets_to_transfer = 1;
+ else if ((packets_to_transfer > 1) &&
+ (usb->init_flags & CVMX_USB_INITIALIZE_FLAGS_NO_DMA)) {
+ /*
+ * Limit to one packet when not using DMA. Channels must be
+ * restarted between every packet for IN transactions, so there
+ * is no reason to do multiple packets in a row
+ */
+ packets_to_transfer = 1;
+ bytes_to_transfer = packets_to_transfer * pipe->max_packet;
+ } else if (packets_to_transfer > MAX_TRANSFER_PACKETS) {
+ /*
+ * Limit the number of packet and data transferred to what the
+ * hardware can handle
+ */
+ packets_to_transfer = MAX_TRANSFER_PACKETS;
+ bytes_to_transfer = packets_to_transfer * pipe->max_packet;
+ }
+
+ usbc_hctsiz.s.xfersize = bytes_to_transfer;
+ usbc_hctsiz.s.pktcnt = packets_to_transfer;
+
+ cvmx_usb_write_csr32(usb, CVMX_USBCX_HCTSIZX(channel, usb->index),
+ usbc_hctsiz.u32);
+}
+
+
+/**
+ * Start a channel to perform the pipe's head transaction
+ *
+ * @usb: USB device state populated by cvmx_usb_initialize().
+ * @channel: Channel to setup
+ * @pipe: Pipe to start
+ */
+static void cvmx_usb_start_channel(struct cvmx_usb_state *usb, int channel,
+ struct cvmx_usb_pipe *pipe)
+{
+ struct cvmx_usb_transaction *transaction =
+ list_first_entry(&pipe->transactions, typeof(*transaction),
+ node);
+
+ /* Make sure all writes to the DMA region get flushed */
+ CVMX_SYNCW;
+
+ /* Attach the channel to the pipe */
+ usb->pipe_for_channel[channel] = pipe;
+ pipe->channel = channel;
+ pipe->flags |= CVMX_USB_PIPE_FLAGS_SCHEDULED;
+
+ /* Mark this channel as in use */
+ usb->idle_hardware_channels &= ~(1<<channel);
+
+ /* Enable the channel interrupt bits */
+ {
+ union cvmx_usbcx_hcintx usbc_hcint;
+ union cvmx_usbcx_hcintmskx usbc_hcintmsk;
+ union cvmx_usbcx_haintmsk usbc_haintmsk;
+
+ /* Clear all channel status bits */
+ usbc_hcint.u32 = cvmx_usb_read_csr32(usb,
+ CVMX_USBCX_HCINTX(channel, usb->index));
+
+ cvmx_usb_write_csr32(usb,
+ CVMX_USBCX_HCINTX(channel, usb->index),
+ usbc_hcint.u32);
+
+ usbc_hcintmsk.u32 = 0;
+ usbc_hcintmsk.s.chhltdmsk = 1;
+ if (usb->init_flags & CVMX_USB_INITIALIZE_FLAGS_NO_DMA) {
+ /*
+ * Channels need these extra interrupts when we aren't
+ * in DMA mode.
+ */
+ usbc_hcintmsk.s.datatglerrmsk = 1;
+ usbc_hcintmsk.s.frmovrunmsk = 1;
+ usbc_hcintmsk.s.bblerrmsk = 1;
+ usbc_hcintmsk.s.xacterrmsk = 1;
+ if (cvmx_usb_pipe_needs_split(usb, pipe)) {
+ /*
+ * Splits don't generate xfercompl, so we need
+ * ACK and NYET.
+ */
+ usbc_hcintmsk.s.nyetmsk = 1;
+ usbc_hcintmsk.s.ackmsk = 1;
+ }
+ usbc_hcintmsk.s.nakmsk = 1;
+ usbc_hcintmsk.s.stallmsk = 1;
+ usbc_hcintmsk.s.xfercomplmsk = 1;
+ }
+ cvmx_usb_write_csr32(usb,
+ CVMX_USBCX_HCINTMSKX(channel, usb->index),
+ usbc_hcintmsk.u32);
+
+ /* Enable the channel interrupt to propagate */
+ usbc_haintmsk.u32 = cvmx_usb_read_csr32(usb,
+ CVMX_USBCX_HAINTMSK(usb->index));
+ usbc_haintmsk.s.haintmsk |= 1<<channel;
+ cvmx_usb_write_csr32(usb, CVMX_USBCX_HAINTMSK(usb->index),
+ usbc_haintmsk.u32);
+ }
+
+ /* Setup the location the DMA engine uses. */
+ {
+ uint64_t reg;
+ uint64_t dma_address = transaction->buffer +
+ transaction->actual_bytes;
+
+ if (transaction->type == CVMX_USB_TRANSFER_ISOCHRONOUS)
+ dma_address = transaction->buffer +
+ transaction->iso_packets[0].offset +
+ transaction->actual_bytes;
+
+ if (pipe->transfer_dir == CVMX_USB_DIRECTION_OUT)
+ reg = CVMX_USBNX_DMA0_OUTB_CHN0(usb->index);
+ else
+ reg = CVMX_USBNX_DMA0_INB_CHN0(usb->index);
+ cvmx_write64_uint64(reg + channel * 8, dma_address);
+ }
+
+ /* Setup both the size of the transfer and the SPLIT characteristics */
+ {
+ union cvmx_usbcx_hcspltx usbc_hcsplt = {.u32 = 0};
+ union cvmx_usbcx_hctsizx usbc_hctsiz = {.u32 = 0};
+ int packets_to_transfer;
+ int bytes_to_transfer = transaction->buffer_length -
+ transaction->actual_bytes;
+
+ /*
+ * ISOCHRONOUS transactions store each individual transfer size
+ * in the packet structure, not the global buffer_length
+ */
+ if (transaction->type == CVMX_USB_TRANSFER_ISOCHRONOUS)
+ bytes_to_transfer =
+ transaction->iso_packets[0].length -
+ transaction->actual_bytes;
+
+ /*
+ * We need to do split transactions when we are talking to non
+ * high speed devices that are behind a high speed hub
+ */
+ if (cvmx_usb_pipe_needs_split(usb, pipe)) {
+ /*
+ * On the start split phase (stage is even) record the
+ * frame number we will need to send the split complete.
+ * We only store the lower two bits since the time ahead
+ * can only be two frames
+ */
+ if ((transaction->stage&1) == 0) {
+ if (transaction->type == CVMX_USB_TRANSFER_BULK)
+ pipe->split_sc_frame =
+ (usb->frame_number + 1) & 0x7f;
+ else
+ pipe->split_sc_frame =
+ (usb->frame_number + 2) & 0x7f;
+ } else
+ pipe->split_sc_frame = -1;
+
+ usbc_hcsplt.s.spltena = 1;
+ usbc_hcsplt.s.hubaddr = pipe->hub_device_addr;
+ usbc_hcsplt.s.prtaddr = pipe->hub_port;
+ usbc_hcsplt.s.compsplt = (transaction->stage ==
+ CVMX_USB_STAGE_NON_CONTROL_SPLIT_COMPLETE);
+
+ /*
+ * SPLIT transactions can only ever transmit one data
+ * packet so limit the transfer size to the max packet
+ * size
+ */
+ if (bytes_to_transfer > pipe->max_packet)
+ bytes_to_transfer = pipe->max_packet;
+
+ /*
+ * ISOCHRONOUS OUT splits are unique in that they limit
+ * data transfers to 188 byte chunks representing the
+ * begin/middle/end of the data or all
+ */
+ if (!usbc_hcsplt.s.compsplt &&
+ (pipe->transfer_dir ==
+ CVMX_USB_DIRECTION_OUT) &&
+ (pipe->transfer_type ==
+ CVMX_USB_TRANSFER_ISOCHRONOUS)) {
+ /*
+ * Clear the split complete frame number as
+ * there isn't going to be a split complete
+ */
+ pipe->split_sc_frame = -1;
+ /*
+ * See if we've started this transfer and sent
+ * data
+ */
+ if (transaction->actual_bytes == 0) {
+ /*
+ * Nothing sent yet, this is either a
+ * begin or the entire payload
+ */
+ if (bytes_to_transfer <= 188)
+ /* Entire payload in one go */
+ usbc_hcsplt.s.xactpos = 3;
+ else
+ /* First part of payload */
+ usbc_hcsplt.s.xactpos = 2;
+ } else {
+ /*
+ * Continuing the previous data, we must
+ * either be in the middle or at the end
+ */
+ if (bytes_to_transfer <= 188)
+ /* End of payload */
+ usbc_hcsplt.s.xactpos = 1;
+ else
+ /* Middle of payload */
+ usbc_hcsplt.s.xactpos = 0;
+ }
+ /*
+ * Again, the transfer size is limited to 188
+ * bytes
+ */
+ if (bytes_to_transfer > 188)
+ bytes_to_transfer = 188;
+ }
+ }
+
+ /*
+ * Make sure the transfer never exceeds the byte limit of the
+ * hardware. Further bytes will be sent as continued
+ * transactions
+ */
+ if (bytes_to_transfer > MAX_TRANSFER_BYTES) {
+ /*
+ * Round MAX_TRANSFER_BYTES to a multiple of out packet
+ * size
+ */
+ bytes_to_transfer = MAX_TRANSFER_BYTES /
+ pipe->max_packet;
+ bytes_to_transfer *= pipe->max_packet;
+ }
+
+ /*
+ * Calculate the number of packets to transfer. If the length is
+ * zero we still need to transfer one packet
+ */
+ packets_to_transfer =
+ DIV_ROUND_UP(bytes_to_transfer, pipe->max_packet);
+ if (packets_to_transfer == 0)
+ packets_to_transfer = 1;
+ else if ((packets_to_transfer > 1) &&
+ (usb->init_flags &
+ CVMX_USB_INITIALIZE_FLAGS_NO_DMA)) {
+ /*
+ * Limit to one packet when not using DMA. Channels must
+ * be restarted between every packet for IN
+ * transactions, so there is no reason to do multiple
+ * packets in a row
+ */
+ packets_to_transfer = 1;
+ bytes_to_transfer = packets_to_transfer *
+ pipe->max_packet;
+ } else if (packets_to_transfer > MAX_TRANSFER_PACKETS) {
+ /*
+ * Limit the number of packet and data transferred to
+ * what the hardware can handle
+ */
+ packets_to_transfer = MAX_TRANSFER_PACKETS;
+ bytes_to_transfer = packets_to_transfer *
+ pipe->max_packet;
+ }
+
+ usbc_hctsiz.s.xfersize = bytes_to_transfer;
+ usbc_hctsiz.s.pktcnt = packets_to_transfer;
+
+ /* Update the DATA0/DATA1 toggle */
+ usbc_hctsiz.s.pid = cvmx_usb_get_data_pid(pipe);
+ /*
+ * High speed pipes may need a hardware ping before they start
+ */
+ if (pipe->flags & CVMX_USB_PIPE_FLAGS_NEED_PING)
+ usbc_hctsiz.s.dopng = 1;
+
+ cvmx_usb_write_csr32(usb,
+ CVMX_USBCX_HCSPLTX(channel, usb->index),
+ usbc_hcsplt.u32);
+ cvmx_usb_write_csr32(usb,
+ CVMX_USBCX_HCTSIZX(channel, usb->index),
+ usbc_hctsiz.u32);
+ }
+
+ /* Setup the Host Channel Characteristics Register */
+ {
+ union cvmx_usbcx_hccharx usbc_hcchar = {.u32 = 0};
+
+ /*
+ * Set the startframe odd/even properly. This is only used for
+ * periodic
+ */
+ usbc_hcchar.s.oddfrm = usb->frame_number&1;
+
+ /*
+ * Set the number of back to back packets allowed by this
+ * endpoint. Split transactions interpret "ec" as the number of
+ * immediate retries of failure. These retries happen too
+ * quickly, so we disable these entirely for splits
+ */
+ if (cvmx_usb_pipe_needs_split(usb, pipe))
+ usbc_hcchar.s.ec = 1;
+ else if (pipe->multi_count < 1)
+ usbc_hcchar.s.ec = 1;
+ else if (pipe->multi_count > 3)
+ usbc_hcchar.s.ec = 3;
+ else
+ usbc_hcchar.s.ec = pipe->multi_count;
+
+ /* Set the rest of the endpoint specific settings */
+ usbc_hcchar.s.devaddr = pipe->device_addr;
+ usbc_hcchar.s.eptype = transaction->type;
+ usbc_hcchar.s.lspddev =
+ (pipe->device_speed == CVMX_USB_SPEED_LOW);
+ usbc_hcchar.s.epdir = pipe->transfer_dir;
+ usbc_hcchar.s.epnum = pipe->endpoint_num;
+ usbc_hcchar.s.mps = pipe->max_packet;
+ cvmx_usb_write_csr32(usb,
+ CVMX_USBCX_HCCHARX(channel, usb->index),
+ usbc_hcchar.u32);
+ }
+
+ /* Do transaction type specific fixups as needed */
+ switch (transaction->type) {
+ case CVMX_USB_TRANSFER_CONTROL:
+ cvmx_usb_start_channel_control(usb, channel, pipe);
+ break;
+ case CVMX_USB_TRANSFER_BULK:
+ case CVMX_USB_TRANSFER_INTERRUPT:
+ break;
+ case CVMX_USB_TRANSFER_ISOCHRONOUS:
+ if (!cvmx_usb_pipe_needs_split(usb, pipe)) {
+ /*
+ * ISO transactions require different PIDs depending on
+ * direction and how many packets are needed
+ */
+ if (pipe->transfer_dir == CVMX_USB_DIRECTION_OUT) {
+ if (pipe->multi_count < 2) /* Need DATA0 */
+ USB_SET_FIELD32(
+ CVMX_USBCX_HCTSIZX(channel,
+ usb->index),
+ cvmx_usbcx_hctsizx, pid, 0);
+ else /* Need MDATA */
+ USB_SET_FIELD32(
+ CVMX_USBCX_HCTSIZX(channel,
+ usb->index),
+ cvmx_usbcx_hctsizx, pid, 3);
+ }
+ }
+ break;
+ }
+ {
+ union cvmx_usbcx_hctsizx usbc_hctsiz = {.u32 =
+ cvmx_usb_read_csr32(usb,
+ CVMX_USBCX_HCTSIZX(channel, usb->index))};
+ transaction->xfersize = usbc_hctsiz.s.xfersize;
+ transaction->pktcnt = usbc_hctsiz.s.pktcnt;
+ }
+ /* Remeber when we start a split transaction */
+ if (cvmx_usb_pipe_needs_split(usb, pipe))
+ usb->active_split = transaction;
+ USB_SET_FIELD32(CVMX_USBCX_HCCHARX(channel, usb->index),
+ cvmx_usbcx_hccharx, chena, 1);
+ if (usb->init_flags & CVMX_USB_INITIALIZE_FLAGS_NO_DMA)
+ cvmx_usb_fill_tx_fifo(usb, channel);
+}
+
+
+/**
+ * Find a pipe that is ready to be scheduled to hardware.
+ * @usb: USB device state populated by cvmx_usb_initialize().
+ * @list: Pipe list to search
+ * @current_frame:
+ * Frame counter to use as a time reference.
+ *
+ * Returns: Pipe or NULL if none are ready
+ */
+static struct cvmx_usb_pipe *cvmx_usb_find_ready_pipe(
+ struct cvmx_usb_state *usb,
+ struct list_head *list,
+ uint64_t current_frame)
+{
+ struct cvmx_usb_pipe *pipe;
+
+ list_for_each_entry(pipe, list, node) {
+ struct cvmx_usb_transaction *t =
+ list_first_entry(&pipe->transactions, typeof(*t),
+ node);
+ if (!(pipe->flags & CVMX_USB_PIPE_FLAGS_SCHEDULED) && t &&
+ (pipe->next_tx_frame <= current_frame) &&
+ ((pipe->split_sc_frame == -1) ||
+ ((((int)current_frame - (int)pipe->split_sc_frame)
+ & 0x7f) < 0x40)) &&
+ (!usb->active_split || (usb->active_split == t))) {
+ prefetch(t);
+ return pipe;
+ }
+ }
+ return NULL;
+}
+
+
+/**
+ * Called whenever a pipe might need to be scheduled to the
+ * hardware.
+ *
+ * @usb: USB device state populated by cvmx_usb_initialize().
+ * @is_sof: True if this schedule was called on a SOF interrupt.
+ */
+static void cvmx_usb_schedule(struct cvmx_usb_state *usb, int is_sof)
+{
+ int channel;
+ struct cvmx_usb_pipe *pipe;
+ int need_sof;
+ enum cvmx_usb_transfer ttype;
+
+ if (usb->init_flags & CVMX_USB_INITIALIZE_FLAGS_NO_DMA) {
+ /*
+ * Without DMA we need to be careful to not schedule something
+ * at the end of a frame and cause an overrun.
+ */
+ union cvmx_usbcx_hfnum hfnum = {
+ .u32 = cvmx_usb_read_csr32(usb,
+ CVMX_USBCX_HFNUM(usb->index))
+ };
+
+ union cvmx_usbcx_hfir hfir = {
+ .u32 = cvmx_usb_read_csr32(usb,
+ CVMX_USBCX_HFIR(usb->index))
+ };
+
+ if (hfnum.s.frrem < hfir.s.frint/4)
+ goto done;
+ }
+
+ while (usb->idle_hardware_channels) {
+ /* Find an idle channel */
+ channel = __fls(usb->idle_hardware_channels);
+ if (unlikely(channel > 7))
+ break;
+
+ /* Find a pipe needing service */
+ pipe = NULL;
+ if (is_sof) {
+ /*
+ * Only process periodic pipes on SOF interrupts. This
+ * way we are sure that the periodic data is sent in the
+ * beginning of the frame
+ */
+ pipe = cvmx_usb_find_ready_pipe(usb,
+ usb->active_pipes +
+ CVMX_USB_TRANSFER_ISOCHRONOUS,
+ usb->frame_number);
+ if (likely(!pipe))
+ pipe = cvmx_usb_find_ready_pipe(usb,
+ usb->active_pipes +
+ CVMX_USB_TRANSFER_INTERRUPT,
+ usb->frame_number);
+ }
+ if (likely(!pipe)) {
+ pipe = cvmx_usb_find_ready_pipe(usb,
+ usb->active_pipes +
+ CVMX_USB_TRANSFER_CONTROL,
+ usb->frame_number);
+ if (likely(!pipe))
+ pipe = cvmx_usb_find_ready_pipe(usb,
+ usb->active_pipes +
+ CVMX_USB_TRANSFER_BULK,
+ usb->frame_number);
+ }
+ if (!pipe)
+ break;
+
+ cvmx_usb_start_channel(usb, channel, pipe);
+ }
+
+done:
+ /*
+ * Only enable SOF interrupts when we have transactions pending in the
+ * future that might need to be scheduled
+ */
+ need_sof = 0;
+ for (ttype = CVMX_USB_TRANSFER_CONTROL;
+ ttype <= CVMX_USB_TRANSFER_INTERRUPT; ttype++) {
+ list_for_each_entry(pipe, &usb->active_pipes[ttype], node) {
+ if (pipe->next_tx_frame > usb->frame_number) {
+ need_sof = 1;
+ break;
+ }
+ }
+ }
+ USB_SET_FIELD32(CVMX_USBCX_GINTMSK(usb->index),
+ cvmx_usbcx_gintmsk, sofmsk, need_sof);
+}
+
+static void octeon_usb_urb_complete_callback(struct cvmx_usb_state *usb,
+ enum cvmx_usb_complete status,
+ struct cvmx_usb_pipe *pipe,
+ struct cvmx_usb_transaction
+ *transaction,
+ int bytes_transferred,
+ struct urb *urb)
+{
+ struct octeon_hcd *priv = cvmx_usb_to_octeon(usb);
+ struct usb_hcd *hcd = octeon_to_hcd(priv);
+ struct device *dev = hcd->self.controller;
+
+ if (likely(status == CVMX_USB_COMPLETE_SUCCESS))
+ urb->actual_length = bytes_transferred;
+ else
+ urb->actual_length = 0;
+
+ urb->hcpriv = NULL;
+
+ /* For Isochronous transactions we need to update the URB packet status
+ list from data in our private copy */
+ if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) {
+ int i;
+ /*
+ * The pointer to the private list is stored in the setup_packet
+ * field.
+ */
+ struct cvmx_usb_iso_packet *iso_packet =
+ (struct cvmx_usb_iso_packet *) urb->setup_packet;
+ /* Recalculate the transfer size by adding up each packet */
+ urb->actual_length = 0;
+ for (i = 0; i < urb->number_of_packets; i++) {
+ if (iso_packet[i].status ==
+ CVMX_USB_COMPLETE_SUCCESS) {
+ urb->iso_frame_desc[i].status = 0;
+ urb->iso_frame_desc[i].actual_length =
+ iso_packet[i].length;
+ urb->actual_length +=
+ urb->iso_frame_desc[i].actual_length;
+ } else {
+ dev_dbg(dev, "ISOCHRONOUS packet=%d of %d status=%d pipe=%p transaction=%p size=%d\n",
+ i, urb->number_of_packets,
+ iso_packet[i].status, pipe,
+ transaction, iso_packet[i].length);
+ urb->iso_frame_desc[i].status = -EREMOTEIO;
+ }
+ }
+ /* Free the private list now that we don't need it anymore */
+ kfree(iso_packet);
+ urb->setup_packet = NULL;
+ }
+
+ switch (status) {
+ case CVMX_USB_COMPLETE_SUCCESS:
+ urb->status = 0;
+ break;
+ case CVMX_USB_COMPLETE_CANCEL:
+ if (urb->status == 0)
+ urb->status = -ENOENT;
+ break;
+ case CVMX_USB_COMPLETE_STALL:
+ dev_dbg(dev, "status=stall pipe=%p transaction=%p size=%d\n",
+ pipe, transaction, bytes_transferred);
+ urb->status = -EPIPE;
+ break;
+ case CVMX_USB_COMPLETE_BABBLEERR:
+ dev_dbg(dev, "status=babble pipe=%p transaction=%p size=%d\n",
+ pipe, transaction, bytes_transferred);
+ urb->status = -EPIPE;
+ break;
+ case CVMX_USB_COMPLETE_SHORT:
+ dev_dbg(dev, "status=short pipe=%p transaction=%p size=%d\n",
+ pipe, transaction, bytes_transferred);
+ urb->status = -EREMOTEIO;
+ break;
+ case CVMX_USB_COMPLETE_ERROR:
+ case CVMX_USB_COMPLETE_XACTERR:
+ case CVMX_USB_COMPLETE_DATATGLERR:
+ case CVMX_USB_COMPLETE_FRAMEERR:
+ dev_dbg(dev, "status=%d pipe=%p transaction=%p size=%d\n",
+ status, pipe, transaction, bytes_transferred);
+ urb->status = -EPROTO;
+ break;
+ }
+ usb_hcd_unlink_urb_from_ep(octeon_to_hcd(priv), urb);
+ spin_unlock(&priv->lock);
+ usb_hcd_giveback_urb(octeon_to_hcd(priv), urb, urb->status);
+ spin_lock(&priv->lock);
+}
+
+/**
+ * Signal the completion of a transaction and free it. The
+ * transaction will be removed from the pipe transaction list.
+ *
+ * @usb: USB device state populated by cvmx_usb_initialize().
+ * @pipe: Pipe the transaction is on
+ * @transaction:
+ * Transaction that completed
+ * @complete_code:
+ * Completion code
+ */
+static void cvmx_usb_perform_complete(struct cvmx_usb_state *usb,
+ struct cvmx_usb_pipe *pipe,
+ struct cvmx_usb_transaction *transaction,
+ enum cvmx_usb_complete complete_code)
+{
+ /* If this was a split then clear our split in progress marker */
+ if (usb->active_split == transaction)
+ usb->active_split = NULL;
+
+ /*
+ * Isochronous transactions need extra processing as they might not be
+ * done after a single data transfer
+ */
+ if (unlikely(transaction->type == CVMX_USB_TRANSFER_ISOCHRONOUS)) {
+ /* Update the number of bytes transferred in this ISO packet */
+ transaction->iso_packets[0].length = transaction->actual_bytes;
+ transaction->iso_packets[0].status = complete_code;
+
+ /*
+ * If there are more ISOs pending and we succeeded, schedule the
+ * next one
+ */
+ if ((transaction->iso_number_packets > 1) &&
+ (complete_code == CVMX_USB_COMPLETE_SUCCESS)) {
+ /* No bytes transferred for this packet as of yet */
+ transaction->actual_bytes = 0;
+ /* One less ISO waiting to transfer */
+ transaction->iso_number_packets--;
+ /* Increment to the next location in our packet array */
+ transaction->iso_packets++;
+ transaction->stage = CVMX_USB_STAGE_NON_CONTROL;
+ return;
+ }
+ }
+
+ /* Remove the transaction from the pipe list */
+ list_del(&transaction->node);
+ if (list_empty(&pipe->transactions))
+ list_move_tail(&pipe->node, &usb->idle_pipes);
+ octeon_usb_urb_complete_callback(usb, complete_code, pipe,
+ transaction,
+ transaction->actual_bytes,
+ transaction->urb);
+ kfree(transaction);
+}
+
+
+/**
+ * Submit a usb transaction to a pipe. Called for all types
+ * of transactions.
+ *
+ * @usb:
+ * @pipe: Which pipe to submit to.
+ * @type: Transaction type
+ * @buffer: User buffer for the transaction
+ * @buffer_length:
+ * User buffer's length in bytes
+ * @control_header:
+ * For control transactions, the 8 byte standard header
+ * @iso_start_frame:
+ * For ISO transactions, the start frame
+ * @iso_number_packets:
+ * For ISO, the number of packet in the transaction.
+ * @iso_packets:
+ * A description of each ISO packet
+ * @urb: URB for the callback
+ *
+ * Returns: Transaction or NULL on failure.
+ */
+static struct cvmx_usb_transaction *cvmx_usb_submit_transaction(
+ struct cvmx_usb_state *usb,
+ struct cvmx_usb_pipe *pipe,
+ enum cvmx_usb_transfer type,
+ uint64_t buffer,
+ int buffer_length,
+ uint64_t control_header,
+ int iso_start_frame,
+ int iso_number_packets,
+ struct cvmx_usb_iso_packet *iso_packets,
+ struct urb *urb)
+{
+ struct cvmx_usb_transaction *transaction;
+
+ if (unlikely(pipe->transfer_type != type))
+ return NULL;
+
+ transaction = kzalloc(sizeof(*transaction), GFP_ATOMIC);
+ if (unlikely(!transaction))
+ return NULL;
+
+ transaction->type = type;
+ transaction->buffer = buffer;
+ transaction->buffer_length = buffer_length;
+ transaction->control_header = control_header;
+ /* FIXME: This is not used, implement it. */
+ transaction->iso_start_frame = iso_start_frame;
+ transaction->iso_number_packets = iso_number_packets;
+ transaction->iso_packets = iso_packets;
+ transaction->urb = urb;
+ if (transaction->type == CVMX_USB_TRANSFER_CONTROL)
+ transaction->stage = CVMX_USB_STAGE_SETUP;
+ else
+ transaction->stage = CVMX_USB_STAGE_NON_CONTROL;
+
+ if (!list_empty(&pipe->transactions)) {
+ list_add_tail(&transaction->node, &pipe->transactions);
+ } else {
+ list_add_tail(&transaction->node, &pipe->transactions);
+ list_move_tail(&pipe->node,
+ &usb->active_pipes[pipe->transfer_type]);
+
+ /*
+ * We may need to schedule the pipe if this was the head of the
+ * pipe.
+ */
+ cvmx_usb_schedule(usb, 0);
+ }
+
+ return transaction;
+}
+
+
+/**
+ * Call to submit a USB Bulk transfer to a pipe.
+ *
+ * @usb: USB device state populated by cvmx_usb_initialize().
+ * @pipe: Handle to the pipe for the transfer.
+ * @urb: URB.
+ *
+ * Returns: A submitted transaction or NULL on failure.
+ */
+static struct cvmx_usb_transaction *cvmx_usb_submit_bulk(
+ struct cvmx_usb_state *usb,
+ struct cvmx_usb_pipe *pipe,
+ struct urb *urb)
+{
+ return cvmx_usb_submit_transaction(usb, pipe, CVMX_USB_TRANSFER_BULK,
+ urb->transfer_dma,
+ urb->transfer_buffer_length,
+ 0, /* control_header */
+ 0, /* iso_start_frame */
+ 0, /* iso_number_packets */
+ NULL, /* iso_packets */
+ urb);
+}
+
+
+/**
+ * Call to submit a USB Interrupt transfer to a pipe.
+ *
+ * @usb: USB device state populated by cvmx_usb_initialize().
+ * @pipe: Handle to the pipe for the transfer.
+ * @urb: URB returned when the callback is called.
+ *
+ * Returns: A submitted transaction or NULL on failure.
+ */
+static struct cvmx_usb_transaction *cvmx_usb_submit_interrupt(
+ struct cvmx_usb_state *usb,
+ struct cvmx_usb_pipe *pipe,
+ struct urb *urb)
+{
+ return cvmx_usb_submit_transaction(usb, pipe,
+ CVMX_USB_TRANSFER_INTERRUPT,
+ urb->transfer_dma,
+ urb->transfer_buffer_length,
+ 0, /* control_header */
+ 0, /* iso_start_frame */
+ 0, /* iso_number_packets */
+ NULL, /* iso_packets */
+ urb);
+}
+
+
+/**
+ * Call to submit a USB Control transfer to a pipe.
+ *
+ * @usb: USB device state populated by cvmx_usb_initialize().
+ * @pipe: Handle to the pipe for the transfer.
+ * @urb: URB.
+ *
+ * Returns: A submitted transaction or NULL on failure.
+ */
+static struct cvmx_usb_transaction *cvmx_usb_submit_control(
+ struct cvmx_usb_state *usb,
+ struct cvmx_usb_pipe *pipe,
+ struct urb *urb)
+{
+ int buffer_length = urb->transfer_buffer_length;
+ uint64_t control_header = urb->setup_dma;
+ struct usb_ctrlrequest *header = cvmx_phys_to_ptr(control_header);
+
+ if ((header->bRequestType & USB_DIR_IN) == 0)
+ buffer_length = le16_to_cpu(header->wLength);
+
+ return cvmx_usb_submit_transaction(usb, pipe,
+ CVMX_USB_TRANSFER_CONTROL,
+ urb->transfer_dma, buffer_length,
+ control_header,
+ 0, /* iso_start_frame */
+ 0, /* iso_number_packets */
+ NULL, /* iso_packets */
+ urb);
+}
+
+
+/**
+ * Call to submit a USB Isochronous transfer to a pipe.
+ *
+ * @usb: USB device state populated by cvmx_usb_initialize().
+ * @pipe: Handle to the pipe for the transfer.
+ * @urb: URB returned when the callback is called.
+ *
+ * Returns: A submitted transaction or NULL on failure.
+ */
+static struct cvmx_usb_transaction *cvmx_usb_submit_isochronous(
+ struct cvmx_usb_state *usb,
+ struct cvmx_usb_pipe *pipe,
+ struct urb *urb)
+{
+ struct cvmx_usb_iso_packet *packets;
+
+ packets = (struct cvmx_usb_iso_packet *) urb->setup_packet;
+ return cvmx_usb_submit_transaction(usb, pipe,
+ CVMX_USB_TRANSFER_ISOCHRONOUS,
+ urb->transfer_dma,
+ urb->transfer_buffer_length,
+ 0, /* control_header */
+ urb->start_frame,
+ urb->number_of_packets,
+ packets, urb);
+}
+
+
+/**
+ * Cancel one outstanding request in a pipe. Canceling a request
+ * can fail if the transaction has already completed before cancel
+ * is called. Even after a successful cancel call, it may take
+ * a frame or two for the cvmx_usb_poll() function to call the
+ * associated callback.
+ *
+ * @usb: USB device state populated by cvmx_usb_initialize().
+ * @pipe: Pipe to cancel requests in.
+ * @transaction: Transaction to cancel, returned by the submit function.
+ *
+ * Returns: 0 or a negative error code.
+ */
+static int cvmx_usb_cancel(struct cvmx_usb_state *usb,
+ struct cvmx_usb_pipe *pipe,
+ struct cvmx_usb_transaction *transaction)
+{
+ /*
+ * If the transaction is the HEAD of the queue and scheduled. We need to
+ * treat it special
+ */
+ if (list_first_entry(&pipe->transactions, typeof(*transaction), node) ==
+ transaction && (pipe->flags & CVMX_USB_PIPE_FLAGS_SCHEDULED)) {
+ union cvmx_usbcx_hccharx usbc_hcchar;
+
+ usb->pipe_for_channel[pipe->channel] = NULL;
+ pipe->flags &= ~CVMX_USB_PIPE_FLAGS_SCHEDULED;
+
+ CVMX_SYNCW;
+
+ usbc_hcchar.u32 = cvmx_usb_read_csr32(usb,
+ CVMX_USBCX_HCCHARX(pipe->channel, usb->index));
+ /*
+ * If the channel isn't enabled then the transaction already
+ * completed.
+ */
+ if (usbc_hcchar.s.chena) {
+ usbc_hcchar.s.chdis = 1;
+ cvmx_usb_write_csr32(usb,
+ CVMX_USBCX_HCCHARX(pipe->channel,
+ usb->index),
+ usbc_hcchar.u32);
+ }
+ }
+ cvmx_usb_perform_complete(usb, pipe, transaction,
+ CVMX_USB_COMPLETE_CANCEL);
+ return 0;
+}
+
+
+/**
+ * Cancel all outstanding requests in a pipe. Logically all this
+ * does is call cvmx_usb_cancel() in a loop.
+ *
+ * @usb: USB device state populated by cvmx_usb_initialize().
+ * @pipe: Pipe to cancel requests in.
+ *
+ * Returns: 0 or a negative error code.
+ */
+static int cvmx_usb_cancel_all(struct cvmx_usb_state *usb,
+ struct cvmx_usb_pipe *pipe)
+{
+ struct cvmx_usb_transaction *transaction, *next;
+
+ /* Simply loop through and attempt to cancel each transaction */
+ list_for_each_entry_safe(transaction, next, &pipe->transactions, node) {
+ int result = cvmx_usb_cancel(usb, pipe, transaction);
+
+ if (unlikely(result != 0))
+ return result;
+ }
+ return 0;
+}
+
+
+/**
+ * Close a pipe created with cvmx_usb_open_pipe().
+ *
+ * @usb: USB device state populated by cvmx_usb_initialize().
+ * @pipe: Pipe to close.
+ *
+ * Returns: 0 or a negative error code. EBUSY is returned if the pipe has
+ * outstanding transfers.
+ */
+static int cvmx_usb_close_pipe(struct cvmx_usb_state *usb,
+ struct cvmx_usb_pipe *pipe)
+{
+ /* Fail if the pipe has pending transactions */
+ if (!list_empty(&pipe->transactions))
+ return -EBUSY;
+
+ list_del(&pipe->node);
+ kfree(pipe);
+
+ return 0;
+}
+
+/**
+ * Get the current USB protocol level frame number. The frame
+ * number is always in the range of 0-0x7ff.
+ *
+ * @usb: USB device state populated by cvmx_usb_initialize().
+ *
+ * Returns: USB frame number
+ */
+static int cvmx_usb_get_frame_number(struct cvmx_usb_state *usb)
+{
+ int frame_number;
+ union cvmx_usbcx_hfnum usbc_hfnum;
+
+ usbc_hfnum.u32 = cvmx_usb_read_csr32(usb, CVMX_USBCX_HFNUM(usb->index));
+ frame_number = usbc_hfnum.s.frnum;
+
+ return frame_number;
+}
+
+
+/**
+ * Poll a channel for status
+ *
+ * @usb: USB device
+ * @channel: Channel to poll
+ *
+ * Returns: Zero on success
+ */
+static int cvmx_usb_poll_channel(struct cvmx_usb_state *usb, int channel)
+{
+ struct octeon_hcd *priv = cvmx_usb_to_octeon(usb);
+ struct usb_hcd *hcd = octeon_to_hcd(priv);
+ struct device *dev = hcd->self.controller;
+ union cvmx_usbcx_hcintx usbc_hcint;
+ union cvmx_usbcx_hctsizx usbc_hctsiz;
+ union cvmx_usbcx_hccharx usbc_hcchar;
+ struct cvmx_usb_pipe *pipe;
+ struct cvmx_usb_transaction *transaction;
+ int bytes_this_transfer;
+ int bytes_in_last_packet;
+ int packets_processed;
+ int buffer_space_left;
+
+ /* Read the interrupt status bits for the channel */
+ usbc_hcint.u32 = cvmx_usb_read_csr32(usb,
+ CVMX_USBCX_HCINTX(channel, usb->index));
+
+ if (usb->init_flags & CVMX_USB_INITIALIZE_FLAGS_NO_DMA) {
+ usbc_hcchar.u32 = cvmx_usb_read_csr32(usb,
+ CVMX_USBCX_HCCHARX(channel, usb->index));
+
+ if (usbc_hcchar.s.chena && usbc_hcchar.s.chdis) {
+ /*
+ * There seems to be a bug in CN31XX which can cause
+ * interrupt IN transfers to get stuck until we do a
+ * write of HCCHARX without changing things
+ */
+ cvmx_usb_write_csr32(usb,
+ CVMX_USBCX_HCCHARX(channel,
+ usb->index),
+ usbc_hcchar.u32);
+ return 0;
+ }
+
+ /*
+ * In non DMA mode the channels don't halt themselves. We need
+ * to manually disable channels that are left running
+ */
+ if (!usbc_hcint.s.chhltd) {
+ if (usbc_hcchar.s.chena) {
+ union cvmx_usbcx_hcintmskx hcintmsk;
+ /* Disable all interrupts except CHHLTD */
+ hcintmsk.u32 = 0;
+ hcintmsk.s.chhltdmsk = 1;
+ cvmx_usb_write_csr32(usb,
+ CVMX_USBCX_HCINTMSKX(channel,
+ usb->index),
+ hcintmsk.u32);
+ usbc_hcchar.s.chdis = 1;
+ cvmx_usb_write_csr32(usb,
+ CVMX_USBCX_HCCHARX(channel,
+ usb->index),
+ usbc_hcchar.u32);
+ return 0;
+ } else if (usbc_hcint.s.xfercompl) {
+ /*
+ * Successful IN/OUT with transfer complete.
+ * Channel halt isn't needed.
+ */
+ } else {
+ dev_err(dev, "USB%d: Channel %d interrupt without halt\n",
+ usb->index, channel);
+ return 0;
+ }
+ }
+ } else {
+ /*
+ * There is are no interrupts that we need to process when the
+ * channel is still running
+ */
+ if (!usbc_hcint.s.chhltd)
+ return 0;
+ }
+
+ /* Disable the channel interrupts now that it is done */
+ cvmx_usb_write_csr32(usb, CVMX_USBCX_HCINTMSKX(channel, usb->index), 0);
+ usb->idle_hardware_channels |= (1<<channel);
+
+ /* Make sure this channel is tied to a valid pipe */
+ pipe = usb->pipe_for_channel[channel];
+ prefetch(pipe);
+ if (!pipe)
+ return 0;
+ transaction = list_first_entry(&pipe->transactions,
+ typeof(*transaction),
+ node);
+ prefetch(transaction);
+
+ /*
+ * Disconnect this pipe from the HW channel. Later the schedule
+ * function will figure out which pipe needs to go
+ */
+ usb->pipe_for_channel[channel] = NULL;
+ pipe->flags &= ~CVMX_USB_PIPE_FLAGS_SCHEDULED;
+
+ /*
+ * Read the channel config info so we can figure out how much data
+ * transferred
+ */
+ usbc_hcchar.u32 = cvmx_usb_read_csr32(usb,
+ CVMX_USBCX_HCCHARX(channel, usb->index));
+ usbc_hctsiz.u32 = cvmx_usb_read_csr32(usb,
+ CVMX_USBCX_HCTSIZX(channel, usb->index));
+
+ /*
+ * Calculating the number of bytes successfully transferred is dependent
+ * on the transfer direction
+ */
+ packets_processed = transaction->pktcnt - usbc_hctsiz.s.pktcnt;
+ if (usbc_hcchar.s.epdir) {
+ /*
+ * IN transactions are easy. For every byte received the
+ * hardware decrements xfersize. All we need to do is subtract
+ * the current value of xfersize from its starting value and we
+ * know how many bytes were written to the buffer
+ */
+ bytes_this_transfer = transaction->xfersize -
+ usbc_hctsiz.s.xfersize;
+ } else {
+ /*
+ * OUT transaction don't decrement xfersize. Instead pktcnt is
+ * decremented on every successful packet send. The hardware
+ * does this when it receives an ACK, or NYET. If it doesn't
+ * receive one of these responses pktcnt doesn't change
+ */
+ bytes_this_transfer = packets_processed * usbc_hcchar.s.mps;
+ /*
+ * The last packet may not be a full transfer if we didn't have
+ * enough data
+ */
+ if (bytes_this_transfer > transaction->xfersize)
+ bytes_this_transfer = transaction->xfersize;
+ }
+ /* Figure out how many bytes were in the last packet of the transfer */
+ if (packets_processed)
+ bytes_in_last_packet = bytes_this_transfer -
+ (packets_processed - 1) * usbc_hcchar.s.mps;
+ else
+ bytes_in_last_packet = bytes_this_transfer;
+
+ /*
+ * As a special case, setup transactions output the setup header, not
+ * the user's data. For this reason we don't count setup data as bytes
+ * transferred
+ */
+ if ((transaction->stage == CVMX_USB_STAGE_SETUP) ||
+ (transaction->stage == CVMX_USB_STAGE_SETUP_SPLIT_COMPLETE))
+ bytes_this_transfer = 0;
+
+ /*
+ * Add the bytes transferred to the running total. It is important that
+ * bytes_this_transfer doesn't count any data that needs to be
+ * retransmitted
+ */
+ transaction->actual_bytes += bytes_this_transfer;
+ if (transaction->type == CVMX_USB_TRANSFER_ISOCHRONOUS)
+ buffer_space_left = transaction->iso_packets[0].length -
+ transaction->actual_bytes;
+ else
+ buffer_space_left = transaction->buffer_length -
+ transaction->actual_bytes;
+
+ /*
+ * We need to remember the PID toggle state for the next transaction.
+ * The hardware already updated it for the next transaction
+ */
+ pipe->pid_toggle = !(usbc_hctsiz.s.pid == 0);
+
+ /*
+ * For high speed bulk out, assume the next transaction will need to do
+ * a ping before proceeding. If this isn't true the ACK processing below
+ * will clear this flag
+ */
+ if ((pipe->device_speed == CVMX_USB_SPEED_HIGH) &&
+ (pipe->transfer_type == CVMX_USB_TRANSFER_BULK) &&
+ (pipe->transfer_dir == CVMX_USB_DIRECTION_OUT))
+ pipe->flags |= CVMX_USB_PIPE_FLAGS_NEED_PING;
+
+ if (unlikely(WARN_ON_ONCE(bytes_this_transfer < 0))) {
+ /*
+ * In some rare cases the DMA engine seems to get stuck and
+ * keeps substracting same byte count over and over again. In
+ * such case we just need to fail every transaction.
+ */
+ cvmx_usb_perform_complete(usb, pipe, transaction,
+ CVMX_USB_COMPLETE_ERROR);
+ return 0;
+ }
+
+ if (usbc_hcint.s.stall) {
+ /*
+ * STALL as a response means this transaction cannot be
+ * completed because the device can't process transactions. Tell
+ * the user. Any data that was transferred will be counted on
+ * the actual bytes transferred
+ */
+ pipe->pid_toggle = 0;
+ cvmx_usb_perform_complete(usb, pipe, transaction,
+ CVMX_USB_COMPLETE_STALL);
+ } else if (usbc_hcint.s.xacterr) {
+ /*
+ * XactErr as a response means the device signaled
+ * something wrong with the transfer. For example, PID
+ * toggle errors cause these.
+ */
+ cvmx_usb_perform_complete(usb, pipe, transaction,
+ CVMX_USB_COMPLETE_XACTERR);
+ } else if (usbc_hcint.s.bblerr) {
+ /* Babble Error (BblErr) */
+ cvmx_usb_perform_complete(usb, pipe, transaction,
+ CVMX_USB_COMPLETE_BABBLEERR);
+ } else if (usbc_hcint.s.datatglerr) {
+ /* Data toggle error */
+ cvmx_usb_perform_complete(usb, pipe, transaction,
+ CVMX_USB_COMPLETE_DATATGLERR);
+ } else if (usbc_hcint.s.nyet) {
+ /*
+ * NYET as a response is only allowed in three cases: as a
+ * response to a ping, as a response to a split transaction, and
+ * as a response to a bulk out. The ping case is handled by
+ * hardware, so we only have splits and bulk out
+ */
+ if (!cvmx_usb_pipe_needs_split(usb, pipe)) {
+ transaction->retries = 0;
+ /*
+ * If there is more data to go then we need to try
+ * again. Otherwise this transaction is complete
+ */
+ if ((buffer_space_left == 0) ||
+ (bytes_in_last_packet < pipe->max_packet))
+ cvmx_usb_perform_complete(usb, pipe,
+ transaction,
+ CVMX_USB_COMPLETE_SUCCESS);
+ } else {
+ /*
+ * Split transactions retry the split complete 4 times
+ * then rewind to the start split and do the entire
+ * transactions again
+ */
+ transaction->retries++;
+ if ((transaction->retries & 0x3) == 0) {
+ /*
+ * Rewind to the beginning of the transaction by
+ * anding off the split complete bit
+ */
+ transaction->stage &= ~1;
+ pipe->split_sc_frame = -1;
+ }
+ }
+ } else if (usbc_hcint.s.ack) {
+ transaction->retries = 0;
+ /*
+ * The ACK bit can only be checked after the other error bits.
+ * This is because a multi packet transfer may succeed in a
+ * number of packets and then get a different response on the
+ * last packet. In this case both ACK and the last response bit
+ * will be set. If none of the other response bits is set, then
+ * the last packet must have been an ACK
+ *
+ * Since we got an ACK, we know we don't need to do a ping on
+ * this pipe
+ */
+ pipe->flags &= ~CVMX_USB_PIPE_FLAGS_NEED_PING;
+
+ switch (transaction->type) {
+ case CVMX_USB_TRANSFER_CONTROL:
+ switch (transaction->stage) {
+ case CVMX_USB_STAGE_NON_CONTROL:
+ case CVMX_USB_STAGE_NON_CONTROL_SPLIT_COMPLETE:
+ /* This should be impossible */
+ cvmx_usb_perform_complete(usb, pipe,
+ transaction, CVMX_USB_COMPLETE_ERROR);
+ break;
+ case CVMX_USB_STAGE_SETUP:
+ pipe->pid_toggle = 1;
+ if (cvmx_usb_pipe_needs_split(usb, pipe))
+ transaction->stage =
+ CVMX_USB_STAGE_SETUP_SPLIT_COMPLETE;
+ else {
+ struct usb_ctrlrequest *header =
+ cvmx_phys_to_ptr(transaction->control_header);
+ if (header->wLength)
+ transaction->stage =
+ CVMX_USB_STAGE_DATA;
+ else
+ transaction->stage =
+ CVMX_USB_STAGE_STATUS;
+ }
+ break;
+ case CVMX_USB_STAGE_SETUP_SPLIT_COMPLETE:
+ {
+ struct usb_ctrlrequest *header =
+ cvmx_phys_to_ptr(transaction->control_header);
+ if (header->wLength)
+ transaction->stage =
+ CVMX_USB_STAGE_DATA;
+ else
+ transaction->stage =
+ CVMX_USB_STAGE_STATUS;
+ }
+ break;
+ case CVMX_USB_STAGE_DATA:
+ if (cvmx_usb_pipe_needs_split(usb, pipe)) {
+ transaction->stage =
+ CVMX_USB_STAGE_DATA_SPLIT_COMPLETE;
+ /*
+ * For setup OUT data that are splits,
+ * the hardware doesn't appear to count
+ * transferred data. Here we manually
+ * update the data transferred
+ */
+ if (!usbc_hcchar.s.epdir) {
+ if (buffer_space_left < pipe->max_packet)
+ transaction->actual_bytes +=
+ buffer_space_left;
+ else
+ transaction->actual_bytes +=
+ pipe->max_packet;
+ }
+ } else if ((buffer_space_left == 0) ||
+ (bytes_in_last_packet <
+ pipe->max_packet)) {
+ pipe->pid_toggle = 1;
+ transaction->stage =
+ CVMX_USB_STAGE_STATUS;
+ }
+ break;
+ case CVMX_USB_STAGE_DATA_SPLIT_COMPLETE:
+ if ((buffer_space_left == 0) ||
+ (bytes_in_last_packet <
+ pipe->max_packet)) {
+ pipe->pid_toggle = 1;
+ transaction->stage =
+ CVMX_USB_STAGE_STATUS;
+ } else {
+ transaction->stage =
+ CVMX_USB_STAGE_DATA;
+ }
+ break;
+ case CVMX_USB_STAGE_STATUS:
+ if (cvmx_usb_pipe_needs_split(usb, pipe))
+ transaction->stage =
+ CVMX_USB_STAGE_STATUS_SPLIT_COMPLETE;
+ else
+ cvmx_usb_perform_complete(usb, pipe,
+ transaction,
+ CVMX_USB_COMPLETE_SUCCESS);
+ break;
+ case CVMX_USB_STAGE_STATUS_SPLIT_COMPLETE:
+ cvmx_usb_perform_complete(usb, pipe,
+ transaction,
+ CVMX_USB_COMPLETE_SUCCESS);
+ break;
+ }
+ break;
+ case CVMX_USB_TRANSFER_BULK:
+ case CVMX_USB_TRANSFER_INTERRUPT:
+ /*
+ * The only time a bulk transfer isn't complete when it
+ * finishes with an ACK is during a split transaction.
+ * For splits we need to continue the transfer if more
+ * data is needed
+ */
+ if (cvmx_usb_pipe_needs_split(usb, pipe)) {
+ if (transaction->stage ==
+ CVMX_USB_STAGE_NON_CONTROL)
+ transaction->stage =
+ CVMX_USB_STAGE_NON_CONTROL_SPLIT_COMPLETE;
+ else {
+ if (buffer_space_left &&
+ (bytes_in_last_packet ==
+ pipe->max_packet))
+ transaction->stage =
+ CVMX_USB_STAGE_NON_CONTROL;
+ else {
+ if (transaction->type ==
+ CVMX_USB_TRANSFER_INTERRUPT)
+ pipe->next_tx_frame +=
+ pipe->interval;
+ cvmx_usb_perform_complete(
+ usb,
+ pipe,
+ transaction,
+ CVMX_USB_COMPLETE_SUCCESS);
+ }
+ }
+ } else {
+ if ((pipe->device_speed ==
+ CVMX_USB_SPEED_HIGH) &&
+ (pipe->transfer_type ==
+ CVMX_USB_TRANSFER_BULK) &&
+ (pipe->transfer_dir ==
+ CVMX_USB_DIRECTION_OUT) &&
+ (usbc_hcint.s.nak))
+ pipe->flags |=
+ CVMX_USB_PIPE_FLAGS_NEED_PING;
+ if (!buffer_space_left ||
+ (bytes_in_last_packet <
+ pipe->max_packet)) {
+ if (transaction->type ==
+ CVMX_USB_TRANSFER_INTERRUPT)
+ pipe->next_tx_frame +=
+ pipe->interval;
+ cvmx_usb_perform_complete(usb, pipe,
+ transaction,
+ CVMX_USB_COMPLETE_SUCCESS);
+ }
+ }
+ break;
+ case CVMX_USB_TRANSFER_ISOCHRONOUS:
+ if (cvmx_usb_pipe_needs_split(usb, pipe)) {
+ /*
+ * ISOCHRONOUS OUT splits don't require a
+ * complete split stage. Instead they use a
+ * sequence of begin OUT splits to transfer the
+ * data 188 bytes at a time. Once the transfer
+ * is complete, the pipe sleeps until the next
+ * schedule interval
+ */
+ if (pipe->transfer_dir ==
+ CVMX_USB_DIRECTION_OUT) {
+ /*
+ * If no space left or this wasn't a max
+ * size packet then this transfer is
+ * complete. Otherwise start it again to
+ * send the next 188 bytes
+ */
+ if (!buffer_space_left ||
+ (bytes_this_transfer < 188)) {
+ pipe->next_tx_frame +=
+ pipe->interval;
+ cvmx_usb_perform_complete(usb,
+ pipe, transaction,
+ CVMX_USB_COMPLETE_SUCCESS);
+ }
+ } else {
+ if (transaction->stage ==
+ CVMX_USB_STAGE_NON_CONTROL_SPLIT_COMPLETE) {
+ /*
+ * We are in the incoming data
+ * phase. Keep getting data
+ * until we run out of space or
+ * get a small packet
+ */
+ if ((buffer_space_left == 0) ||
+ (bytes_in_last_packet <
+ pipe->max_packet)) {
+ pipe->next_tx_frame +=
+ pipe->interval;
+ cvmx_usb_perform_complete(
+ usb,
+ pipe,
+ transaction,
+ CVMX_USB_COMPLETE_SUCCESS);
+ }
+ } else
+ transaction->stage =
+ CVMX_USB_STAGE_NON_CONTROL_SPLIT_COMPLETE;
+ }
+ } else {
+ pipe->next_tx_frame += pipe->interval;
+ cvmx_usb_perform_complete(usb, pipe,
+ transaction,
+ CVMX_USB_COMPLETE_SUCCESS);
+ }
+ break;
+ }
+ } else if (usbc_hcint.s.nak) {
+ /*
+ * If this was a split then clear our split in progress marker.
+ */
+ if (usb->active_split == transaction)
+ usb->active_split = NULL;
+ /*
+ * NAK as a response means the device couldn't accept the
+ * transaction, but it should be retried in the future. Rewind
+ * to the beginning of the transaction by anding off the split
+ * complete bit. Retry in the next interval
+ */
+ transaction->retries = 0;
+ transaction->stage &= ~1;
+ pipe->next_tx_frame += pipe->interval;
+ if (pipe->next_tx_frame < usb->frame_number)
+ pipe->next_tx_frame = usb->frame_number +
+ pipe->interval -
+ (usb->frame_number - pipe->next_tx_frame) %
+ pipe->interval;
+ } else {
+ struct cvmx_usb_port_status port;
+
+ port = cvmx_usb_get_status(usb);
+ if (port.port_enabled) {
+ /* We'll retry the exact same transaction again */
+ transaction->retries++;
+ } else {
+ /*
+ * We get channel halted interrupts with no result bits
+ * sets when the cable is unplugged
+ */
+ cvmx_usb_perform_complete(usb, pipe, transaction,
+ CVMX_USB_COMPLETE_ERROR);
+ }
+ }
+ return 0;
+}
+
+static void octeon_usb_port_callback(struct cvmx_usb_state *usb)
+{
+ struct octeon_hcd *priv = cvmx_usb_to_octeon(usb);
+
+ spin_unlock(&priv->lock);
+ usb_hcd_poll_rh_status(octeon_to_hcd(priv));
+ spin_lock(&priv->lock);
+}
+
+/**
+ * Poll the USB block for status and call all needed callback
+ * handlers. This function is meant to be called in the interrupt
+ * handler for the USB controller. It can also be called
+ * periodically in a loop for non-interrupt based operation.
+ *
+ * @usb: USB device state populated by cvmx_usb_initialize().
+ *
+ * Returns: 0 or a negative error code.
+ */
+static int cvmx_usb_poll(struct cvmx_usb_state *usb)
+{
+ union cvmx_usbcx_hfnum usbc_hfnum;
+ union cvmx_usbcx_gintsts usbc_gintsts;
+
+ prefetch_range(usb, sizeof(*usb));
+
+ /* Update the frame counter */
+ usbc_hfnum.u32 = cvmx_usb_read_csr32(usb, CVMX_USBCX_HFNUM(usb->index));
+ if ((usb->frame_number&0x3fff) > usbc_hfnum.s.frnum)
+ usb->frame_number += 0x4000;
+ usb->frame_number &= ~0x3fffull;
+ usb->frame_number |= usbc_hfnum.s.frnum;
+
+ /* Read the pending interrupts */
+ usbc_gintsts.u32 = cvmx_usb_read_csr32(usb,
+ CVMX_USBCX_GINTSTS(usb->index));
+
+ /* Clear the interrupts now that we know about them */
+ cvmx_usb_write_csr32(usb, CVMX_USBCX_GINTSTS(usb->index),
+ usbc_gintsts.u32);
+
+ if (usbc_gintsts.s.rxflvl) {
+ /*
+ * RxFIFO Non-Empty (RxFLvl)
+ * Indicates that there is at least one packet pending to be
+ * read from the RxFIFO.
+ *
+ * In DMA mode this is handled by hardware
+ */
+ if (usb->init_flags & CVMX_USB_INITIALIZE_FLAGS_NO_DMA)
+ cvmx_usb_poll_rx_fifo(usb);
+ }
+ if (usbc_gintsts.s.ptxfemp || usbc_gintsts.s.nptxfemp) {
+ /* Fill the Tx FIFOs when not in DMA mode */
+ if (usb->init_flags & CVMX_USB_INITIALIZE_FLAGS_NO_DMA)
+ cvmx_usb_poll_tx_fifo(usb);
+ }
+ if (usbc_gintsts.s.disconnint || usbc_gintsts.s.prtint) {
+ union cvmx_usbcx_hprt usbc_hprt;
+ /*
+ * Disconnect Detected Interrupt (DisconnInt)
+ * Asserted when a device disconnect is detected.
+ *
+ * Host Port Interrupt (PrtInt)
+ * The core sets this bit to indicate a change in port status of
+ * one of the O2P USB core ports in Host mode. The application
+ * must read the Host Port Control and Status (HPRT) register to
+ * determine the exact event that caused this interrupt. The
+ * application must clear the appropriate status bit in the Host
+ * Port Control and Status register to clear this bit.
+ *
+ * Call the user's port callback
+ */
+ octeon_usb_port_callback(usb);
+ /* Clear the port change bits */
+ usbc_hprt.u32 = cvmx_usb_read_csr32(usb,
+ CVMX_USBCX_HPRT(usb->index));
+ usbc_hprt.s.prtena = 0;
+ cvmx_usb_write_csr32(usb, CVMX_USBCX_HPRT(usb->index),
+ usbc_hprt.u32);
+ }
+ if (usbc_gintsts.s.hchint) {
+ /*
+ * Host Channels Interrupt (HChInt)
+ * The core sets this bit to indicate that an interrupt is
+ * pending on one of the channels of the core (in Host mode).
+ * The application must read the Host All Channels Interrupt
+ * (HAINT) register to determine the exact number of the channel
+ * on which the interrupt occurred, and then read the
+ * corresponding Host Channel-n Interrupt (HCINTn) register to
+ * determine the exact cause of the interrupt. The application
+ * must clear the appropriate status bit in the HCINTn register
+ * to clear this bit.
+ */
+ union cvmx_usbcx_haint usbc_haint;
+
+ usbc_haint.u32 = cvmx_usb_read_csr32(usb,
+ CVMX_USBCX_HAINT(usb->index));
+ while (usbc_haint.u32) {
+ int channel;
+
+ channel = __fls(usbc_haint.u32);
+ cvmx_usb_poll_channel(usb, channel);
+ usbc_haint.u32 ^= 1<<channel;
+ }
+ }
+
+ cvmx_usb_schedule(usb, usbc_gintsts.s.sof);
+
+ return 0;
+}
+
+/* convert between an HCD pointer and the corresponding struct octeon_hcd */
+static inline struct octeon_hcd *hcd_to_octeon(struct usb_hcd *hcd)
+{
+ return (struct octeon_hcd *)(hcd->hcd_priv);
+}
+
+static irqreturn_t octeon_usb_irq(struct usb_hcd *hcd)
+{
+ struct octeon_hcd *priv = hcd_to_octeon(hcd);
+ unsigned long flags;
+
+ spin_lock_irqsave(&priv->lock, flags);
+ cvmx_usb_poll(&priv->usb);
+ spin_unlock_irqrestore(&priv->lock, flags);
+ return IRQ_HANDLED;
+}
+
+static int octeon_usb_start(struct usb_hcd *hcd)
+{
+ hcd->state = HC_STATE_RUNNING;
+ return 0;
+}
+
+static void octeon_usb_stop(struct usb_hcd *hcd)
+{
+ hcd->state = HC_STATE_HALT;
+}
+
+static int octeon_usb_get_frame_number(struct usb_hcd *hcd)
+{
+ struct octeon_hcd *priv = hcd_to_octeon(hcd);
+
+ return cvmx_usb_get_frame_number(&priv->usb);
+}
+
+static int octeon_usb_urb_enqueue(struct usb_hcd *hcd,
+ struct urb *urb,
+ gfp_t mem_flags)
+{
+ struct octeon_hcd *priv = hcd_to_octeon(hcd);
+ struct device *dev = hcd->self.controller;
+ struct cvmx_usb_transaction *transaction = NULL;
+ struct cvmx_usb_pipe *pipe;
+ unsigned long flags;
+ struct cvmx_usb_iso_packet *iso_packet;
+ struct usb_host_endpoint *ep = urb->ep;
+ int rc;
+
+ urb->status = 0;
+ spin_lock_irqsave(&priv->lock, flags);
+
+ rc = usb_hcd_link_urb_to_ep(hcd, urb);
+ if (rc) {
+ spin_unlock_irqrestore(&priv->lock, flags);
+ return rc;
+ }
+
+ if (!ep->hcpriv) {
+ enum cvmx_usb_transfer transfer_type;
+ enum cvmx_usb_speed speed;
+ int split_device = 0;
+ int split_port = 0;
+
+ switch (usb_pipetype(urb->pipe)) {
+ case PIPE_ISOCHRONOUS:
+ transfer_type = CVMX_USB_TRANSFER_ISOCHRONOUS;
+ break;
+ case PIPE_INTERRUPT:
+ transfer_type = CVMX_USB_TRANSFER_INTERRUPT;
+ break;
+ case PIPE_CONTROL:
+ transfer_type = CVMX_USB_TRANSFER_CONTROL;
+ break;
+ default:
+ transfer_type = CVMX_USB_TRANSFER_BULK;
+ break;
+ }
+ switch (urb->dev->speed) {
+ case USB_SPEED_LOW:
+ speed = CVMX_USB_SPEED_LOW;
+ break;
+ case USB_SPEED_FULL:
+ speed = CVMX_USB_SPEED_FULL;
+ break;
+ default:
+ speed = CVMX_USB_SPEED_HIGH;
+ break;
+ }
+ /*
+ * For slow devices on high speed ports we need to find the hub
+ * that does the speed translation so we know where to send the
+ * split transactions.
+ */
+ if (speed != CVMX_USB_SPEED_HIGH) {
+ /*
+ * Start at this device and work our way up the usb
+ * tree.
+ */
+ struct usb_device *dev = urb->dev;
+
+ while (dev->parent) {
+ /*
+ * If our parent is high speed then he'll
+ * receive the splits.
+ */
+ if (dev->parent->speed == USB_SPEED_HIGH) {
+ split_device = dev->parent->devnum;
+ split_port = dev->portnum;
+ break;
+ }
+ /*
+ * Move up the tree one level. If we make it all
+ * the way up the tree, then the port must not
+ * be in high speed mode and we don't need a
+ * split.
+ */
+ dev = dev->parent;
+ }
+ }
+ pipe = cvmx_usb_open_pipe(&priv->usb, usb_pipedevice(urb->pipe),
+ usb_pipeendpoint(urb->pipe), speed,
+ le16_to_cpu(ep->desc.wMaxPacketSize)
+ & 0x7ff,
+ transfer_type,
+ usb_pipein(urb->pipe) ?
+ CVMX_USB_DIRECTION_IN :
+ CVMX_USB_DIRECTION_OUT,
+ urb->interval,
+ (le16_to_cpu(ep->desc.wMaxPacketSize)
+ >> 11) & 0x3,
+ split_device, split_port);
+ if (!pipe) {
+ usb_hcd_unlink_urb_from_ep(hcd, urb);
+ spin_unlock_irqrestore(&priv->lock, flags);
+ dev_dbg(dev, "Failed to create pipe\n");
+ return -ENOMEM;
+ }
+ ep->hcpriv = pipe;
+ } else {
+ pipe = ep->hcpriv;
+ }
+
+ switch (usb_pipetype(urb->pipe)) {
+ case PIPE_ISOCHRONOUS:
+ dev_dbg(dev, "Submit isochronous to %d.%d\n",
+ usb_pipedevice(urb->pipe),
+ usb_pipeendpoint(urb->pipe));
+ /*
+ * Allocate a structure to use for our private list of
+ * isochronous packets.
+ */
+ iso_packet = kmalloc(urb->number_of_packets *
+ sizeof(struct cvmx_usb_iso_packet),
+ GFP_ATOMIC);
+ if (iso_packet) {
+ int i;
+ /* Fill the list with the data from the URB */
+ for (i = 0; i < urb->number_of_packets; i++) {
+ iso_packet[i].offset =
+ urb->iso_frame_desc[i].offset;
+ iso_packet[i].length =
+ urb->iso_frame_desc[i].length;
+ iso_packet[i].status =
+ CVMX_USB_COMPLETE_ERROR;
+ }
+ /*
+ * Store a pointer to the list in the URB setup_packet
+ * field. We know this currently isn't being used and
+ * this saves us a bunch of logic.
+ */
+ urb->setup_packet = (char *)iso_packet;
+ transaction = cvmx_usb_submit_isochronous(&priv->usb,
+ pipe, urb);
+ /*
+ * If submit failed we need to free our private packet
+ * list.
+ */
+ if (!transaction) {
+ urb->setup_packet = NULL;
+ kfree(iso_packet);
+ }
+ }
+ break;
+ case PIPE_INTERRUPT:
+ dev_dbg(dev, "Submit interrupt to %d.%d\n",
+ usb_pipedevice(urb->pipe),
+ usb_pipeendpoint(urb->pipe));
+ transaction = cvmx_usb_submit_interrupt(&priv->usb, pipe, urb);
+ break;
+ case PIPE_CONTROL:
+ dev_dbg(dev, "Submit control to %d.%d\n",
+ usb_pipedevice(urb->pipe),
+ usb_pipeendpoint(urb->pipe));
+ transaction = cvmx_usb_submit_control(&priv->usb, pipe, urb);
+ break;
+ case PIPE_BULK:
+ dev_dbg(dev, "Submit bulk to %d.%d\n",
+ usb_pipedevice(urb->pipe),
+ usb_pipeendpoint(urb->pipe));
+ transaction = cvmx_usb_submit_bulk(&priv->usb, pipe, urb);
+ break;
+ }
+ if (!transaction) {
+ usb_hcd_unlink_urb_from_ep(hcd, urb);
+ spin_unlock_irqrestore(&priv->lock, flags);
+ dev_dbg(dev, "Failed to submit\n");
+ return -ENOMEM;
+ }
+ urb->hcpriv = transaction;
+ spin_unlock_irqrestore(&priv->lock, flags);
+ return 0;
+}
+
+static int octeon_usb_urb_dequeue(struct usb_hcd *hcd,
+ struct urb *urb,
+ int status)
+{
+ struct octeon_hcd *priv = hcd_to_octeon(hcd);
+ unsigned long flags;
+ int rc;
+
+ if (!urb->dev)
+ return -EINVAL;
+
+ spin_lock_irqsave(&priv->lock, flags);
+
+ rc = usb_hcd_check_unlink_urb(hcd, urb, status);
+ if (rc)
+ goto out;
+
+ urb->status = status;
+ cvmx_usb_cancel(&priv->usb, urb->ep->hcpriv, urb->hcpriv);
+
+out:
+ spin_unlock_irqrestore(&priv->lock, flags);
+
+ return rc;
+}
+
+static void octeon_usb_endpoint_disable(struct usb_hcd *hcd,
+ struct usb_host_endpoint *ep)
+{
+ struct device *dev = hcd->self.controller;
+
+ if (ep->hcpriv) {
+ struct octeon_hcd *priv = hcd_to_octeon(hcd);
+ struct cvmx_usb_pipe *pipe = ep->hcpriv;
+ unsigned long flags;
+
+ spin_lock_irqsave(&priv->lock, flags);
+ cvmx_usb_cancel_all(&priv->usb, pipe);
+ if (cvmx_usb_close_pipe(&priv->usb, pipe))
+ dev_dbg(dev, "Closing pipe %p failed\n", pipe);
+ spin_unlock_irqrestore(&priv->lock, flags);
+ ep->hcpriv = NULL;
+ }
+}
+
+static int octeon_usb_hub_status_data(struct usb_hcd *hcd, char *buf)
+{
+ struct octeon_hcd *priv = hcd_to_octeon(hcd);
+ struct cvmx_usb_port_status port_status;
+ unsigned long flags;
+
+ spin_lock_irqsave(&priv->lock, flags);
+ port_status = cvmx_usb_get_status(&priv->usb);
+ spin_unlock_irqrestore(&priv->lock, flags);
+ buf[0] = 0;
+ buf[0] = port_status.connect_change << 1;
+
+ return buf[0] != 0;
+}
+
+static int octeon_usb_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
+ u16 wIndex, char *buf, u16 wLength)
+{
+ struct octeon_hcd *priv = hcd_to_octeon(hcd);
+ struct device *dev = hcd->self.controller;
+ struct cvmx_usb_port_status usb_port_status;
+ struct cvmx_usb_state *usb = &priv->usb;
+ int port_status;
+ struct usb_hub_descriptor *desc;
+ unsigned long flags;
+
+ switch (typeReq) {
+ case ClearHubFeature:
+ dev_dbg(dev, "ClearHubFeature\n");
+ switch (wValue) {
+ case C_HUB_LOCAL_POWER:
+ case C_HUB_OVER_CURRENT:
+ /* Nothing required here */
+ break;
+ default:
+ return -EINVAL;
+ }
+ break;
+ case ClearPortFeature:
+ dev_dbg(dev, "ClearPortFeature\n");
+ if (wIndex != 1) {
+ dev_dbg(dev, " INVALID\n");
+ return -EINVAL;
+ }
+
+ switch (wValue) {
+ case USB_PORT_FEAT_ENABLE:
+ dev_dbg(dev, " ENABLE\n");
+ spin_lock_irqsave(&priv->lock, flags);
+ cvmx_usb_disable(&priv->usb);
+ spin_unlock_irqrestore(&priv->lock, flags);
+ break;
+ case USB_PORT_FEAT_SUSPEND:
+ dev_dbg(dev, " SUSPEND\n");
+ /* Not supported on Octeon */
+ break;
+ case USB_PORT_FEAT_POWER:
+ dev_dbg(dev, " POWER\n");
+ /* Not supported on Octeon */
+ break;
+ case USB_PORT_FEAT_INDICATOR:
+ dev_dbg(dev, " INDICATOR\n");
+ /* Port inidicator not supported */
+ break;
+ case USB_PORT_FEAT_C_CONNECTION:
+ dev_dbg(dev, " C_CONNECTION\n");
+ /* Clears drivers internal connect status change flag */
+ spin_lock_irqsave(&priv->lock, flags);
+ priv->usb.port_status =
+ cvmx_usb_get_status(&priv->usb);
+ spin_unlock_irqrestore(&priv->lock, flags);
+ break;
+ case USB_PORT_FEAT_C_RESET:
+ dev_dbg(dev, " C_RESET\n");
+ /*
+ * Clears the driver's internal Port Reset Change flag.
+ */
+ spin_lock_irqsave(&priv->lock, flags);
+ priv->usb.port_status =
+ cvmx_usb_get_status(&priv->usb);
+ spin_unlock_irqrestore(&priv->lock, flags);
+ break;
+ case USB_PORT_FEAT_C_ENABLE:
+ dev_dbg(dev, " C_ENABLE\n");
+ /*
+ * Clears the driver's internal Port Enable/Disable
+ * Change flag.
+ */
+ spin_lock_irqsave(&priv->lock, flags);
+ priv->usb.port_status =
+ cvmx_usb_get_status(&priv->usb);
+ spin_unlock_irqrestore(&priv->lock, flags);
+ break;
+ case USB_PORT_FEAT_C_SUSPEND:
+ dev_dbg(dev, " C_SUSPEND\n");
+ /*
+ * Clears the driver's internal Port Suspend Change
+ * flag, which is set when resume signaling on the host
+ * port is complete.
+ */
+ break;
+ case USB_PORT_FEAT_C_OVER_CURRENT:
+ dev_dbg(dev, " C_OVER_CURRENT\n");
+ /* Clears the driver's overcurrent Change flag */
+ spin_lock_irqsave(&priv->lock, flags);
+ priv->usb.port_status =
+ cvmx_usb_get_status(&priv->usb);
+ spin_unlock_irqrestore(&priv->lock, flags);
+ break;
+ default:
+ dev_dbg(dev, " UNKNOWN\n");
+ return -EINVAL;
+ }
+ break;
+ case GetHubDescriptor:
+ dev_dbg(dev, "GetHubDescriptor\n");
+ desc = (struct usb_hub_descriptor *)buf;
+ desc->bDescLength = 9;
+ desc->bDescriptorType = 0x29;
+ desc->bNbrPorts = 1;
+ desc->wHubCharacteristics = cpu_to_le16(0x08);
+ desc->bPwrOn2PwrGood = 1;
+ desc->bHubContrCurrent = 0;
+ desc->u.hs.DeviceRemovable[0] = 0;
+ desc->u.hs.DeviceRemovable[1] = 0xff;
+ break;
+ case GetHubStatus:
+ dev_dbg(dev, "GetHubStatus\n");
+ *(__le32 *) buf = 0;
+ break;
+ case GetPortStatus:
+ dev_dbg(dev, "GetPortStatus\n");
+ if (wIndex != 1) {
+ dev_dbg(dev, " INVALID\n");
+ return -EINVAL;
+ }
+
+ spin_lock_irqsave(&priv->lock, flags);
+ usb_port_status = cvmx_usb_get_status(&priv->usb);
+ spin_unlock_irqrestore(&priv->lock, flags);
+ port_status = 0;
+
+ if (usb_port_status.connect_change) {
+ port_status |= (1 << USB_PORT_FEAT_C_CONNECTION);
+ dev_dbg(dev, " C_CONNECTION\n");
+ }
+
+ if (usb_port_status.port_enabled) {
+ port_status |= (1 << USB_PORT_FEAT_C_ENABLE);
+ dev_dbg(dev, " C_ENABLE\n");
+ }
+
+ if (usb_port_status.connected) {
+ port_status |= (1 << USB_PORT_FEAT_CONNECTION);
+ dev_dbg(dev, " CONNECTION\n");
+ }
+
+ if (usb_port_status.port_enabled) {
+ port_status |= (1 << USB_PORT_FEAT_ENABLE);
+ dev_dbg(dev, " ENABLE\n");
+ }
+
+ if (usb_port_status.port_over_current) {
+ port_status |= (1 << USB_PORT_FEAT_OVER_CURRENT);
+ dev_dbg(dev, " OVER_CURRENT\n");
+ }
+
+ if (usb_port_status.port_powered) {
+ port_status |= (1 << USB_PORT_FEAT_POWER);
+ dev_dbg(dev, " POWER\n");
+ }
+
+ if (usb_port_status.port_speed == CVMX_USB_SPEED_HIGH) {
+ port_status |= USB_PORT_STAT_HIGH_SPEED;
+ dev_dbg(dev, " HIGHSPEED\n");
+ } else if (usb_port_status.port_speed == CVMX_USB_SPEED_LOW) {
+ port_status |= (1 << USB_PORT_FEAT_LOWSPEED);
+ dev_dbg(dev, " LOWSPEED\n");
+ }
+
+ *((__le32 *) buf) = cpu_to_le32(port_status);
+ break;
+ case SetHubFeature:
+ dev_dbg(dev, "SetHubFeature\n");
+ /* No HUB features supported */
+ break;
+ case SetPortFeature:
+ dev_dbg(dev, "SetPortFeature\n");
+ if (wIndex != 1) {
+ dev_dbg(dev, " INVALID\n");
+ return -EINVAL;
+ }
+
+ switch (wValue) {
+ case USB_PORT_FEAT_SUSPEND:
+ dev_dbg(dev, " SUSPEND\n");
+ return -EINVAL;
+ case USB_PORT_FEAT_POWER:
+ dev_dbg(dev, " POWER\n");
+ /*
+ * Program the port power bit to drive VBUS on the USB.
+ */
+ spin_lock_irqsave(&priv->lock, flags);
+ USB_SET_FIELD32(CVMX_USBCX_HPRT(usb->index),
+ cvmx_usbcx_hprt, prtpwr, 1);
+ spin_unlock_irqrestore(&priv->lock, flags);
+ return 0;
+ case USB_PORT_FEAT_RESET:
+ dev_dbg(dev, " RESET\n");
+ spin_lock_irqsave(&priv->lock, flags);
+ cvmx_usb_reset_port(&priv->usb);
+ spin_unlock_irqrestore(&priv->lock, flags);
+ return 0;
+ case USB_PORT_FEAT_INDICATOR:
+ dev_dbg(dev, " INDICATOR\n");
+ /* Not supported */
+ break;
+ default:
+ dev_dbg(dev, " UNKNOWN\n");
+ return -EINVAL;
+ }
+ break;
+ default:
+ dev_dbg(dev, "Unknown root hub request\n");
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static const struct hc_driver octeon_hc_driver = {
+ .description = "Octeon USB",
+ .product_desc = "Octeon Host Controller",
+ .hcd_priv_size = sizeof(struct octeon_hcd),
+ .irq = octeon_usb_irq,
+ .flags = HCD_MEMORY | HCD_USB2,
+ .start = octeon_usb_start,
+ .stop = octeon_usb_stop,
+ .urb_enqueue = octeon_usb_urb_enqueue,
+ .urb_dequeue = octeon_usb_urb_dequeue,
+ .endpoint_disable = octeon_usb_endpoint_disable,
+ .get_frame_number = octeon_usb_get_frame_number,
+ .hub_status_data = octeon_usb_hub_status_data,
+ .hub_control = octeon_usb_hub_control,
+ .map_urb_for_dma = octeon_map_urb_for_dma,
+ .unmap_urb_for_dma = octeon_unmap_urb_for_dma,
+};
+
+static int octeon_usb_probe(struct platform_device *pdev)
+{
+ int status;
+ int initialize_flags;
+ int usb_num;
+ struct resource *res_mem;
+ struct device_node *usbn_node;
+ int irq = platform_get_irq(pdev, 0);
+ struct device *dev = &pdev->dev;
+ struct octeon_hcd *priv;
+ struct usb_hcd *hcd;
+ u32 clock_rate = 48000000;
+ bool is_crystal_clock = false;
+ const char *clock_type;
+ int i;
+
+ if (dev->of_node == NULL) {
+ dev_err(dev, "Error: empty of_node\n");
+ return -ENXIO;
+ }
+ usbn_node = dev->of_node->parent;
+
+ i = of_property_read_u32(usbn_node,
+ "refclk-frequency", &clock_rate);
+ if (i) {
+ dev_err(dev, "No USBN \"refclk-frequency\"\n");
+ return -ENXIO;
+ }
+ switch (clock_rate) {
+ case 12000000:
+ initialize_flags = CVMX_USB_INITIALIZE_FLAGS_CLOCK_12MHZ;
+ break;
+ case 24000000:
+ initialize_flags = CVMX_USB_INITIALIZE_FLAGS_CLOCK_24MHZ;
+ break;
+ case 48000000:
+ initialize_flags = CVMX_USB_INITIALIZE_FLAGS_CLOCK_48MHZ;
+ break;
+ default:
+ dev_err(dev, "Illebal USBN \"refclk-frequency\" %u\n",
+ clock_rate);
+ return -ENXIO;
+
+ }
+
+ i = of_property_read_string(usbn_node,
+ "refclk-type", &clock_type);
+
+ if (!i && strcmp("crystal", clock_type) == 0)
+ is_crystal_clock = true;
+
+ if (is_crystal_clock)
+ initialize_flags |= CVMX_USB_INITIALIZE_FLAGS_CLOCK_XO_XI;
+ else
+ initialize_flags |= CVMX_USB_INITIALIZE_FLAGS_CLOCK_XO_GND;
+
+ res_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (res_mem == NULL) {
+ dev_err(dev, "found no memory resource\n");
+ return -ENXIO;
+ }
+ usb_num = (res_mem->start >> 44) & 1;
+
+ if (irq < 0) {
+ /* Defective device tree, but we know how to fix it. */
+ irq_hw_number_t hwirq = usb_num ? (1 << 6) + 17 : 56;
+
+ irq = irq_create_mapping(NULL, hwirq);
+ }
+
+ /*
+ * Set the DMA mask to 64bits so we get buffers already translated for
+ * DMA.
+ */
+ dev->coherent_dma_mask = ~0;
+ dev->dma_mask = &dev->coherent_dma_mask;
+
+ /*
+ * Only cn52XX and cn56XX have DWC_OTG USB hardware and the
+ * IOB priority registers. Under heavy network load USB
+ * hardware can be starved by the IOB causing a crash. Give
+ * it a priority boost if it has been waiting more than 400
+ * cycles to avoid this situation.
+ *
+ * Testing indicates that a cnt_val of 8192 is not sufficient,
+ * but no failures are seen with 4096. We choose a value of
+ * 400 to give a safety factor of 10.
+ */
+ if (OCTEON_IS_MODEL(OCTEON_CN52XX) || OCTEON_IS_MODEL(OCTEON_CN56XX)) {
+ union cvmx_iob_n2c_l2c_pri_cnt pri_cnt;
+
+ pri_cnt.u64 = 0;
+ pri_cnt.s.cnt_enb = 1;
+ pri_cnt.s.cnt_val = 400;
+ cvmx_write_csr(CVMX_IOB_N2C_L2C_PRI_CNT, pri_cnt.u64);
+ }
+
+ hcd = usb_create_hcd(&octeon_hc_driver, dev, dev_name(dev));
+ if (!hcd) {
+ dev_dbg(dev, "Failed to allocate memory for HCD\n");
+ return -1;
+ }
+ hcd->uses_new_polling = 1;
+ priv = (struct octeon_hcd *)hcd->hcd_priv;
+
+ spin_lock_init(&priv->lock);
+
+ priv->usb.init_flags = initialize_flags;
+
+ /* Initialize the USB state structure */
+ priv->usb.index = usb_num;
+ INIT_LIST_HEAD(&priv->usb.idle_pipes);
+ for (i = 0; i < ARRAY_SIZE(priv->usb.active_pipes); i++)
+ INIT_LIST_HEAD(&priv->usb.active_pipes[i]);
+
+ /* Due to an errata, CN31XX doesn't support DMA */
+ if (OCTEON_IS_MODEL(OCTEON_CN31XX)) {
+ priv->usb.init_flags |= CVMX_USB_INITIALIZE_FLAGS_NO_DMA;
+ /* Only use one channel with non DMA */
+ priv->usb.idle_hardware_channels = 0x1;
+ } else if (OCTEON_IS_MODEL(OCTEON_CN5XXX)) {
+ /* CN5XXX have an errata with channel 3 */
+ priv->usb.idle_hardware_channels = 0xf7;
+ } else {
+ priv->usb.idle_hardware_channels = 0xff;
+ }
+
+ status = cvmx_usb_initialize(dev, &priv->usb);
+ if (status) {
+ dev_dbg(dev, "USB initialization failed with %d\n", status);
+ kfree(hcd);
+ return -1;
+ }
+
+ status = usb_add_hcd(hcd, irq, 0);
+ if (status) {
+ dev_dbg(dev, "USB add HCD failed with %d\n", status);
+ kfree(hcd);
+ return -1;
+ }
+ device_wakeup_enable(hcd->self.controller);
+
+ dev_info(dev, "Registered HCD for port %d on irq %d\n", usb_num, irq);
+
+ return 0;
+}
+
+static int octeon_usb_remove(struct platform_device *pdev)
+{
+ int status;
+ struct device *dev = &pdev->dev;
+ struct usb_hcd *hcd = dev_get_drvdata(dev);
+ struct octeon_hcd *priv = hcd_to_octeon(hcd);
+ unsigned long flags;
+
+ usb_remove_hcd(hcd);
+ spin_lock_irqsave(&priv->lock, flags);
+ status = cvmx_usb_shutdown(&priv->usb);
+ spin_unlock_irqrestore(&priv->lock, flags);
+ if (status)
+ dev_dbg(dev, "USB shutdown failed with %d\n", status);
+
+ kfree(hcd);
+
+ return 0;
+}
+
+static const struct of_device_id octeon_usb_match[] = {
+ {
+ .compatible = "cavium,octeon-5750-usbc",
+ },
+ {},
+};
+
+static struct platform_driver octeon_usb_driver = {
+ .driver = {
+ .name = "OcteonUSB",
+ .of_match_table = octeon_usb_match,
+ },
+ .probe = octeon_usb_probe,
+ .remove = octeon_usb_remove,
+};
+
+static int __init octeon_usb_driver_init(void)
+{
+ if (usb_disabled())
+ return 0;
+
+ return platform_driver_register(&octeon_usb_driver);
+}
+module_init(octeon_usb_driver_init);
+
+static void __exit octeon_usb_driver_exit(void)
+{
+ if (usb_disabled())
+ return;
+
+ platform_driver_unregister(&octeon_usb_driver);
+}
+module_exit(octeon_usb_driver_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Cavium, Inc. <support@cavium.com>");
+MODULE_DESCRIPTION("Cavium Inc. OCTEON USB Host driver.");
diff --git a/drivers/staging/octeon-usb/octeon-hcd.h b/drivers/staging/octeon-usb/octeon-hcd.h
new file mode 100644
index 000000000..3e351ab74
--- /dev/null
+++ b/drivers/staging/octeon-usb/octeon-hcd.h
@@ -0,0 +1,1846 @@
+/*
+ * Octeon HCD hardware register definitions.
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Some parts of the code were originally released under BSD license:
+ *
+ * Copyright (c) 2003-2010 Cavium Networks (support@cavium.com). All rights
+ * reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * * Neither the name of Cavium Networks nor the names of
+ * its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written
+ * permission.
+ *
+ * This Software, including technical data, may be subject to U.S. export
+ * control laws, including the U.S. Export Administration Act and its associated
+ * regulations, and may be subject to export or import regulations in other
+ * countries.
+ *
+ * TO THE MAXIMUM EXTENT PERMITTED BY LAW, THE SOFTWARE IS PROVIDED "AS IS"
+ * AND WITH ALL FAULTS AND CAVIUM NETWORKS MAKES NO PROMISES, REPRESENTATIONS OR
+ * WARRANTIES, EITHER EXPRESS, IMPLIED, STATUTORY, OR OTHERWISE, WITH RESPECT TO
+ * THE SOFTWARE, INCLUDING ITS CONDITION, ITS CONFORMITY TO ANY REPRESENTATION
+ * OR DESCRIPTION, OR THE EXISTENCE OF ANY LATENT OR PATENT DEFECTS, AND CAVIUM
+ * SPECIFICALLY DISCLAIMS ALL IMPLIED (IF ANY) WARRANTIES OF TITLE,
+ * MERCHANTABILITY, NONINFRINGEMENT, FITNESS FOR A PARTICULAR PURPOSE, LACK OF
+ * VIRUSES, ACCURACY OR COMPLETENESS, QUIET ENJOYMENT, QUIET POSSESSION OR
+ * CORRESPONDENCE TO DESCRIPTION. THE ENTIRE RISK ARISING OUT OF USE OR
+ * PERFORMANCE OF THE SOFTWARE LIES WITH YOU.
+ */
+
+#ifndef __OCTEON_HCD_H__
+#define __OCTEON_HCD_H__
+
+#include <asm/bitfield.h>
+
+#define CVMX_USBCXBASE 0x00016F0010000000ull
+#define CVMX_USBCXREG1(reg, bid) \
+ (CVMX_ADD_IO_SEG(CVMX_USBCXBASE | reg) + \
+ ((bid) & 1) * 0x100000000000ull)
+#define CVMX_USBCXREG2(reg, bid, off) \
+ (CVMX_ADD_IO_SEG(CVMX_USBCXBASE | reg) + \
+ (((off) & 7) + ((bid) & 1) * 0x8000000000ull) * 32)
+
+#define CVMX_USBCX_GAHBCFG(bid) CVMX_USBCXREG1(0x008, bid)
+#define CVMX_USBCX_GHWCFG3(bid) CVMX_USBCXREG1(0x04c, bid)
+#define CVMX_USBCX_GINTMSK(bid) CVMX_USBCXREG1(0x018, bid)
+#define CVMX_USBCX_GINTSTS(bid) CVMX_USBCXREG1(0x014, bid)
+#define CVMX_USBCX_GNPTXFSIZ(bid) CVMX_USBCXREG1(0x028, bid)
+#define CVMX_USBCX_GNPTXSTS(bid) CVMX_USBCXREG1(0x02c, bid)
+#define CVMX_USBCX_GOTGCTL(bid) CVMX_USBCXREG1(0x000, bid)
+#define CVMX_USBCX_GRSTCTL(bid) CVMX_USBCXREG1(0x010, bid)
+#define CVMX_USBCX_GRXFSIZ(bid) CVMX_USBCXREG1(0x024, bid)
+#define CVMX_USBCX_GRXSTSPH(bid) CVMX_USBCXREG1(0x020, bid)
+#define CVMX_USBCX_GUSBCFG(bid) CVMX_USBCXREG1(0x00c, bid)
+#define CVMX_USBCX_HAINT(bid) CVMX_USBCXREG1(0x414, bid)
+#define CVMX_USBCX_HAINTMSK(bid) CVMX_USBCXREG1(0x418, bid)
+#define CVMX_USBCX_HCCHARX(off, bid) CVMX_USBCXREG2(0x500, bid, off)
+#define CVMX_USBCX_HCFG(bid) CVMX_USBCXREG1(0x400, bid)
+#define CVMX_USBCX_HCINTMSKX(off, bid) CVMX_USBCXREG2(0x50c, bid, off)
+#define CVMX_USBCX_HCINTX(off, bid) CVMX_USBCXREG2(0x508, bid, off)
+#define CVMX_USBCX_HCSPLTX(off, bid) CVMX_USBCXREG2(0x504, bid, off)
+#define CVMX_USBCX_HCTSIZX(off, bid) CVMX_USBCXREG2(0x510, bid, off)
+#define CVMX_USBCX_HFIR(bid) CVMX_USBCXREG1(0x404, bid)
+#define CVMX_USBCX_HFNUM(bid) CVMX_USBCXREG1(0x408, bid)
+#define CVMX_USBCX_HPRT(bid) CVMX_USBCXREG1(0x440, bid)
+#define CVMX_USBCX_HPTXFSIZ(bid) CVMX_USBCXREG1(0x100, bid)
+#define CVMX_USBCX_HPTXSTS(bid) CVMX_USBCXREG1(0x410, bid)
+
+#define CVMX_USBNXBID1(bid) (((bid) & 1) * 0x10000000ull)
+#define CVMX_USBNXBID2(bid) (((bid) & 1) * 0x100000000000ull)
+
+#define CVMX_USBNXREG1(reg, bid) \
+ (CVMX_ADD_IO_SEG(0x0001180068000000ull | reg) + CVMX_USBNXBID1(bid))
+#define CVMX_USBNXREG2(reg, bid) \
+ (CVMX_ADD_IO_SEG(0x00016F0000000000ull | reg) + CVMX_USBNXBID2(bid))
+
+#define CVMX_USBNX_CLK_CTL(bid) CVMX_USBNXREG1(0x10, bid)
+#define CVMX_USBNX_DMA0_INB_CHN0(bid) CVMX_USBNXREG2(0x818, bid)
+#define CVMX_USBNX_DMA0_OUTB_CHN0(bid) CVMX_USBNXREG2(0x858, bid)
+#define CVMX_USBNX_USBP_CTL_STATUS(bid) CVMX_USBNXREG1(0x18, bid)
+
+/**
+ * cvmx_usbc#_gahbcfg
+ *
+ * Core AHB Configuration Register (GAHBCFG)
+ *
+ * This register can be used to configure the core after power-on or a change in
+ * mode of operation. This register mainly contains AHB system-related
+ * configuration parameters. The AHB is the processor interface to the O2P USB
+ * core. In general, software need not know about this interface except to
+ * program the values as specified.
+ *
+ * The application must program this register as part of the O2P USB core
+ * initialization. Do not change this register after the initial programming.
+ */
+union cvmx_usbcx_gahbcfg {
+ uint32_t u32;
+ /**
+ * struct cvmx_usbcx_gahbcfg_s
+ * @ptxfemplvl: Periodic TxFIFO Empty Level (PTxFEmpLvl)
+ * Software should set this bit to 0x1.
+ * Indicates when the Periodic TxFIFO Empty Interrupt bit in the
+ * Core Interrupt register (GINTSTS.PTxFEmp) is triggered. This
+ * bit is used only in Slave mode.
+ * * 1'b0: GINTSTS.PTxFEmp interrupt indicates that the Periodic
+ * TxFIFO is half empty
+ * * 1'b1: GINTSTS.PTxFEmp interrupt indicates that the Periodic
+ * TxFIFO is completely empty
+ * @nptxfemplvl: Non-Periodic TxFIFO Empty Level (NPTxFEmpLvl)
+ * Software should set this bit to 0x1.
+ * Indicates when the Non-Periodic TxFIFO Empty Interrupt bit in
+ * the Core Interrupt register (GINTSTS.NPTxFEmp) is triggered.
+ * This bit is used only in Slave mode.
+ * * 1'b0: GINTSTS.NPTxFEmp interrupt indicates that the Non-
+ * Periodic TxFIFO is half empty
+ * * 1'b1: GINTSTS.NPTxFEmp interrupt indicates that the Non-
+ * Periodic TxFIFO is completely empty
+ * @dmaen: DMA Enable (DMAEn)
+ * * 1'b0: Core operates in Slave mode
+ * * 1'b1: Core operates in a DMA mode
+ * @hbstlen: Burst Length/Type (HBstLen)
+ * This field has not effect and should be left as 0x0.
+ * @glblintrmsk: Global Interrupt Mask (GlblIntrMsk)
+ * Software should set this field to 0x1.
+ * The application uses this bit to mask or unmask the interrupt
+ * line assertion to itself. Irrespective of this bit's setting,
+ * the interrupt status registers are updated by the core.
+ * * 1'b0: Mask the interrupt assertion to the application.
+ * * 1'b1: Unmask the interrupt assertion to the application.
+ */
+ struct cvmx_usbcx_gahbcfg_s {
+ __BITFIELD_FIELD(uint32_t reserved_9_31 : 23,
+ __BITFIELD_FIELD(uint32_t ptxfemplvl : 1,
+ __BITFIELD_FIELD(uint32_t nptxfemplvl : 1,
+ __BITFIELD_FIELD(uint32_t reserved_6_6 : 1,
+ __BITFIELD_FIELD(uint32_t dmaen : 1,
+ __BITFIELD_FIELD(uint32_t hbstlen : 4,
+ __BITFIELD_FIELD(uint32_t glblintrmsk : 1,
+ ;)))))))
+ } s;
+};
+
+/**
+ * cvmx_usbc#_ghwcfg3
+ *
+ * User HW Config3 Register (GHWCFG3)
+ *
+ * This register contains the configuration options of the O2P USB core.
+ */
+union cvmx_usbcx_ghwcfg3 {
+ uint32_t u32;
+ /**
+ * struct cvmx_usbcx_ghwcfg3_s
+ * @dfifodepth: DFIFO Depth (DfifoDepth)
+ * This value is in terms of 32-bit words.
+ * * Minimum value is 32
+ * * Maximum value is 32768
+ * @ahbphysync: AHB and PHY Synchronous (AhbPhySync)
+ * Indicates whether AHB and PHY clocks are synchronous to
+ * each other.
+ * * 1'b0: No
+ * * 1'b1: Yes
+ * This bit is tied to 1.
+ * @rsttype: Reset Style for Clocked always Blocks in RTL (RstType)
+ * * 1'b0: Asynchronous reset is used in the core
+ * * 1'b1: Synchronous reset is used in the core
+ * @optfeature: Optional Features Removed (OptFeature)
+ * Indicates whether the User ID register, GPIO interface ports,
+ * and SOF toggle and counter ports were removed for gate count
+ * optimization.
+ * @vendor_control_interface_support: Vendor Control Interface Support
+ * * 1'b0: Vendor Control Interface is not available on the core.
+ * * 1'b1: Vendor Control Interface is available.
+ * @i2c_selection: I2C Selection
+ * * 1'b0: I2C Interface is not available on the core.
+ * * 1'b1: I2C Interface is available on the core.
+ * @otgen: OTG Function Enabled (OtgEn)
+ * The application uses this bit to indicate the O2P USB core's
+ * OTG capabilities.
+ * * 1'b0: Not OTG capable
+ * * 1'b1: OTG Capable
+ * @pktsizewidth: Width of Packet Size Counters (PktSizeWidth)
+ * * 3'b000: 4 bits
+ * * 3'b001: 5 bits
+ * * 3'b010: 6 bits
+ * * 3'b011: 7 bits
+ * * 3'b100: 8 bits
+ * * 3'b101: 9 bits
+ * * 3'b110: 10 bits
+ * * Others: Reserved
+ * @xfersizewidth: Width of Transfer Size Counters (XferSizeWidth)
+ * * 4'b0000: 11 bits
+ * * 4'b0001: 12 bits
+ * - ...
+ * * 4'b1000: 19 bits
+ * * Others: Reserved
+ */
+ struct cvmx_usbcx_ghwcfg3_s {
+ __BITFIELD_FIELD(uint32_t dfifodepth : 16,
+ __BITFIELD_FIELD(uint32_t reserved_13_15 : 3,
+ __BITFIELD_FIELD(uint32_t ahbphysync : 1,
+ __BITFIELD_FIELD(uint32_t rsttype : 1,
+ __BITFIELD_FIELD(uint32_t optfeature : 1,
+ __BITFIELD_FIELD(uint32_t vendor_control_interface_support : 1,
+ __BITFIELD_FIELD(uint32_t i2c_selection : 1,
+ __BITFIELD_FIELD(uint32_t otgen : 1,
+ __BITFIELD_FIELD(uint32_t pktsizewidth : 3,
+ __BITFIELD_FIELD(uint32_t xfersizewidth : 4,
+ ;))))))))))
+ } s;
+};
+
+/**
+ * cvmx_usbc#_gintmsk
+ *
+ * Core Interrupt Mask Register (GINTMSK)
+ *
+ * This register works with the Core Interrupt register to interrupt the
+ * application. When an interrupt bit is masked, the interrupt associated with
+ * that bit will not be generated. However, the Core Interrupt (GINTSTS)
+ * register bit corresponding to that interrupt will still be set.
+ * Mask interrupt: 1'b0, Unmask interrupt: 1'b1
+ */
+union cvmx_usbcx_gintmsk {
+ uint32_t u32;
+ /**
+ * struct cvmx_usbcx_gintmsk_s
+ * @wkupintmsk: Resume/Remote Wakeup Detected Interrupt Mask
+ * (WkUpIntMsk)
+ * @sessreqintmsk: Session Request/New Session Detected Interrupt Mask
+ * (SessReqIntMsk)
+ * @disconnintmsk: Disconnect Detected Interrupt Mask (DisconnIntMsk)
+ * @conidstschngmsk: Connector ID Status Change Mask (ConIDStsChngMsk)
+ * @ptxfempmsk: Periodic TxFIFO Empty Mask (PTxFEmpMsk)
+ * @hchintmsk: Host Channels Interrupt Mask (HChIntMsk)
+ * @prtintmsk: Host Port Interrupt Mask (PrtIntMsk)
+ * @fetsuspmsk: Data Fetch Suspended Mask (FetSuspMsk)
+ * @incomplpmsk: Incomplete Periodic Transfer Mask (incomplPMsk)
+ * Incomplete Isochronous OUT Transfer Mask
+ * (incompISOOUTMsk)
+ * @incompisoinmsk: Incomplete Isochronous IN Transfer Mask
+ * (incompISOINMsk)
+ * @oepintmsk: OUT Endpoints Interrupt Mask (OEPIntMsk)
+ * @inepintmsk: IN Endpoints Interrupt Mask (INEPIntMsk)
+ * @epmismsk: Endpoint Mismatch Interrupt Mask (EPMisMsk)
+ * @eopfmsk: End of Periodic Frame Interrupt Mask (EOPFMsk)
+ * @isooutdropmsk: Isochronous OUT Packet Dropped Interrupt Mask
+ * (ISOOutDropMsk)
+ * @enumdonemsk: Enumeration Done Mask (EnumDoneMsk)
+ * @usbrstmsk: USB Reset Mask (USBRstMsk)
+ * @usbsuspmsk: USB Suspend Mask (USBSuspMsk)
+ * @erlysuspmsk: Early Suspend Mask (ErlySuspMsk)
+ * @i2cint: I2C Interrupt Mask (I2CINT)
+ * @ulpickintmsk: ULPI Carkit Interrupt Mask (ULPICKINTMsk)
+ * I2C Carkit Interrupt Mask (I2CCKINTMsk)
+ * @goutnakeffmsk: Global OUT NAK Effective Mask (GOUTNakEffMsk)
+ * @ginnakeffmsk: Global Non-Periodic IN NAK Effective Mask
+ * (GINNakEffMsk)
+ * @nptxfempmsk: Non-Periodic TxFIFO Empty Mask (NPTxFEmpMsk)
+ * @rxflvlmsk: Receive FIFO Non-Empty Mask (RxFLvlMsk)
+ * @sofmsk: Start of (micro)Frame Mask (SofMsk)
+ * @otgintmsk: OTG Interrupt Mask (OTGIntMsk)
+ * @modemismsk: Mode Mismatch Interrupt Mask (ModeMisMsk)
+ */
+ struct cvmx_usbcx_gintmsk_s {
+ __BITFIELD_FIELD(uint32_t wkupintmsk : 1,
+ __BITFIELD_FIELD(uint32_t sessreqintmsk : 1,
+ __BITFIELD_FIELD(uint32_t disconnintmsk : 1,
+ __BITFIELD_FIELD(uint32_t conidstschngmsk : 1,
+ __BITFIELD_FIELD(uint32_t reserved_27_27 : 1,
+ __BITFIELD_FIELD(uint32_t ptxfempmsk : 1,
+ __BITFIELD_FIELD(uint32_t hchintmsk : 1,
+ __BITFIELD_FIELD(uint32_t prtintmsk : 1,
+ __BITFIELD_FIELD(uint32_t reserved_23_23 : 1,
+ __BITFIELD_FIELD(uint32_t fetsuspmsk : 1,
+ __BITFIELD_FIELD(uint32_t incomplpmsk : 1,
+ __BITFIELD_FIELD(uint32_t incompisoinmsk : 1,
+ __BITFIELD_FIELD(uint32_t oepintmsk : 1,
+ __BITFIELD_FIELD(uint32_t inepintmsk : 1,
+ __BITFIELD_FIELD(uint32_t epmismsk : 1,
+ __BITFIELD_FIELD(uint32_t reserved_16_16 : 1,
+ __BITFIELD_FIELD(uint32_t eopfmsk : 1,
+ __BITFIELD_FIELD(uint32_t isooutdropmsk : 1,
+ __BITFIELD_FIELD(uint32_t enumdonemsk : 1,
+ __BITFIELD_FIELD(uint32_t usbrstmsk : 1,
+ __BITFIELD_FIELD(uint32_t usbsuspmsk : 1,
+ __BITFIELD_FIELD(uint32_t erlysuspmsk : 1,
+ __BITFIELD_FIELD(uint32_t i2cint : 1,
+ __BITFIELD_FIELD(uint32_t ulpickintmsk : 1,
+ __BITFIELD_FIELD(uint32_t goutnakeffmsk : 1,
+ __BITFIELD_FIELD(uint32_t ginnakeffmsk : 1,
+ __BITFIELD_FIELD(uint32_t nptxfempmsk : 1,
+ __BITFIELD_FIELD(uint32_t rxflvlmsk : 1,
+ __BITFIELD_FIELD(uint32_t sofmsk : 1,
+ __BITFIELD_FIELD(uint32_t otgintmsk : 1,
+ __BITFIELD_FIELD(uint32_t modemismsk : 1,
+ __BITFIELD_FIELD(uint32_t reserved_0_0 : 1,
+ ;))))))))))))))))))))))))))))))))
+ } s;
+};
+
+/**
+ * cvmx_usbc#_gintsts
+ *
+ * Core Interrupt Register (GINTSTS)
+ *
+ * This register interrupts the application for system-level events in the
+ * current mode of operation (Device mode or Host mode). It is shown in
+ * Interrupt. Some of the bits in this register are valid only in Host mode,
+ * while others are valid in Device mode only. This register also indicates the
+ * current mode of operation. In order to clear the interrupt status bits of
+ * type R_SS_WC, the application must write 1'b1 into the bit. The FIFO status
+ * interrupts are read only; once software reads from or writes to the FIFO
+ * while servicing these interrupts, FIFO interrupt conditions are cleared
+ * automatically.
+ */
+union cvmx_usbcx_gintsts {
+ uint32_t u32;
+ /**
+ * struct cvmx_usbcx_gintsts_s
+ * @wkupint: Resume/Remote Wakeup Detected Interrupt (WkUpInt)
+ * In Device mode, this interrupt is asserted when a resume is
+ * detected on the USB. In Host mode, this interrupt is asserted
+ * when a remote wakeup is detected on the USB.
+ * For more information on how to use this interrupt, see "Partial
+ * Power-Down and Clock Gating Programming Model" on
+ * page 353.
+ * @sessreqint: Session Request/New Session Detected Interrupt
+ * (SessReqInt)
+ * In Host mode, this interrupt is asserted when a session request
+ * is detected from the device. In Device mode, this interrupt is
+ * asserted when the utmiotg_bvalid signal goes high.
+ * For more information on how to use this interrupt, see "Partial
+ * Power-Down and Clock Gating Programming Model" on
+ * page 353.
+ * @disconnint: Disconnect Detected Interrupt (DisconnInt)
+ * Asserted when a device disconnect is detected.
+ * @conidstschng: Connector ID Status Change (ConIDStsChng)
+ * The core sets this bit when there is a change in connector ID
+ * status.
+ * @ptxfemp: Periodic TxFIFO Empty (PTxFEmp)
+ * Asserted when the Periodic Transmit FIFO is either half or
+ * completely empty and there is space for at least one entry to be
+ * written in the Periodic Request Queue. The half or completely
+ * empty status is determined by the Periodic TxFIFO Empty Level
+ * bit in the Core AHB Configuration register
+ * (GAHBCFG.PTxFEmpLvl).
+ * @hchint: Host Channels Interrupt (HChInt)
+ * The core sets this bit to indicate that an interrupt is pending
+ * on one of the channels of the core (in Host mode). The
+ * application must read the Host All Channels Interrupt (HAINT)
+ * register to determine the exact number of the channel on which
+ * the interrupt occurred, and then read the corresponding Host
+ * Channel-n Interrupt (HCINTn) register to determine the exact
+ * cause of the interrupt. The application must clear the
+ * appropriate status bit in the HCINTn register to clear this bit.
+ * @prtint: Host Port Interrupt (PrtInt)
+ * The core sets this bit to indicate a change in port status of
+ * one of the O2P USB core ports in Host mode. The application must
+ * read the Host Port Control and Status (HPRT) register to
+ * determine the exact event that caused this interrupt. The
+ * application must clear the appropriate status bit in the Host
+ * Port Control and Status register to clear this bit.
+ * @fetsusp: Data Fetch Suspended (FetSusp)
+ * This interrupt is valid only in DMA mode. This interrupt
+ * indicates that the core has stopped fetching data for IN
+ * endpoints due to the unavailability of TxFIFO space or Request
+ * Queue space. This interrupt is used by the application for an
+ * endpoint mismatch algorithm.
+ * @incomplp: Incomplete Periodic Transfer (incomplP)
+ * In Host mode, the core sets this interrupt bit when there are
+ * incomplete periodic transactions still pending which are
+ * scheduled for the current microframe.
+ * Incomplete Isochronous OUT Transfer (incompISOOUT)
+ * The Device mode, the core sets this interrupt to indicate that
+ * there is at least one isochronous OUT endpoint on which the
+ * transfer is not completed in the current microframe. This
+ * interrupt is asserted along with the End of Periodic Frame
+ * Interrupt (EOPF) bit in this register.
+ * @incompisoin: Incomplete Isochronous IN Transfer (incompISOIN)
+ * The core sets this interrupt to indicate that there is at least
+ * one isochronous IN endpoint on which the transfer is not
+ * completed in the current microframe. This interrupt is asserted
+ * along with the End of Periodic Frame Interrupt (EOPF) bit in
+ * this register.
+ * @oepint: OUT Endpoints Interrupt (OEPInt)
+ * The core sets this bit to indicate that an interrupt is pending
+ * on one of the OUT endpoints of the core (in Device mode). The
+ * application must read the Device All Endpoints Interrupt
+ * (DAINT) register to determine the exact number of the OUT
+ * endpoint on which the interrupt occurred, and then read the
+ * corresponding Device OUT Endpoint-n Interrupt (DOEPINTn)
+ * register to determine the exact cause of the interrupt. The
+ * application must clear the appropriate status bit in the
+ * corresponding DOEPINTn register to clear this bit.
+ * @iepint: IN Endpoints Interrupt (IEPInt)
+ * The core sets this bit to indicate that an interrupt is pending
+ * on one of the IN endpoints of the core (in Device mode). The
+ * application must read the Device All Endpoints Interrupt
+ * (DAINT) register to determine the exact number of the IN
+ * endpoint on which the interrupt occurred, and then read the
+ * corresponding Device IN Endpoint-n Interrupt (DIEPINTn)
+ * register to determine the exact cause of the interrupt. The
+ * application must clear the appropriate status bit in the
+ * corresponding DIEPINTn register to clear this bit.
+ * @epmis: Endpoint Mismatch Interrupt (EPMis)
+ * Indicates that an IN token has been received for a non-periodic
+ * endpoint, but the data for another endpoint is present in the
+ * top of the Non-Periodic Transmit FIFO and the IN endpoint
+ * mismatch count programmed by the application has expired.
+ * @eopf: End of Periodic Frame Interrupt (EOPF)
+ * Indicates that the period specified in the Periodic Frame
+ * Interval field of the Device Configuration register
+ * (DCFG.PerFrInt) has been reached in the current microframe.
+ * @isooutdrop: Isochronous OUT Packet Dropped Interrupt (ISOOutDrop)
+ * The core sets this bit when it fails to write an isochronous OUT
+ * packet into the RxFIFO because the RxFIFO doesn't have
+ * enough space to accommodate a maximum packet size packet
+ * for the isochronous OUT endpoint.
+ * @enumdone: Enumeration Done (EnumDone)
+ * The core sets this bit to indicate that speed enumeration is
+ * complete. The application must read the Device Status (DSTS)
+ * register to obtain the enumerated speed.
+ * @usbrst: USB Reset (USBRst)
+ * The core sets this bit to indicate that a reset is detected on
+ * the USB.
+ * @usbsusp: USB Suspend (USBSusp)
+ * The core sets this bit to indicate that a suspend was detected
+ * on the USB. The core enters the Suspended state when there
+ * is no activity on the phy_line_state_i signal for an extended
+ * period of time.
+ * @erlysusp: Early Suspend (ErlySusp)
+ * The core sets this bit to indicate that an Idle state has been
+ * detected on the USB for 3 ms.
+ * @i2cint: I2C Interrupt (I2CINT)
+ * This bit is always 0x0.
+ * @ulpickint: ULPI Carkit Interrupt (ULPICKINT)
+ * This bit is always 0x0.
+ * @goutnakeff: Global OUT NAK Effective (GOUTNakEff)
+ * Indicates that the Set Global OUT NAK bit in the Device Control
+ * register (DCTL.SGOUTNak), set by the application, has taken
+ * effect in the core. This bit can be cleared by writing the Clear
+ * Global OUT NAK bit in the Device Control register
+ * (DCTL.CGOUTNak).
+ * @ginnakeff: Global IN Non-Periodic NAK Effective (GINNakEff)
+ * Indicates that the Set Global Non-Periodic IN NAK bit in the
+ * Device Control register (DCTL.SGNPInNak), set by the
+ * application, has taken effect in the core. That is, the core has
+ * sampled the Global IN NAK bit set by the application. This bit
+ * can be cleared by clearing the Clear Global Non-Periodic IN
+ * NAK bit in the Device Control register (DCTL.CGNPInNak).
+ * This interrupt does not necessarily mean that a NAK handshake
+ * is sent out on the USB. The STALL bit takes precedence over
+ * the NAK bit.
+ * @nptxfemp: Non-Periodic TxFIFO Empty (NPTxFEmp)
+ * This interrupt is asserted when the Non-Periodic TxFIFO is
+ * either half or completely empty, and there is space for at least
+ * one entry to be written to the Non-Periodic Transmit Request
+ * Queue. The half or completely empty status is determined by
+ * the Non-Periodic TxFIFO Empty Level bit in the Core AHB
+ * Configuration register (GAHBCFG.NPTxFEmpLvl).
+ * @rxflvl: RxFIFO Non-Empty (RxFLvl)
+ * Indicates that there is at least one packet pending to be read
+ * from the RxFIFO.
+ * @sof: Start of (micro)Frame (Sof)
+ * In Host mode, the core sets this bit to indicate that an SOF
+ * (FS), micro-SOF (HS), or Keep-Alive (LS) is transmitted on the
+ * USB. The application must write a 1 to this bit to clear the
+ * interrupt.
+ * In Device mode, in the core sets this bit to indicate that an
+ * SOF token has been received on the USB. The application can read
+ * the Device Status register to get the current (micro)frame
+ * number. This interrupt is seen only when the core is operating
+ * at either HS or FS.
+ * @otgint: OTG Interrupt (OTGInt)
+ * The core sets this bit to indicate an OTG protocol event. The
+ * application must read the OTG Interrupt Status (GOTGINT)
+ * register to determine the exact event that caused this
+ * interrupt. The application must clear the appropriate status bit
+ * in the GOTGINT register to clear this bit.
+ * @modemis: Mode Mismatch Interrupt (ModeMis)
+ * The core sets this bit when the application is trying to access:
+ * * A Host mode register, when the core is operating in Device
+ * mode
+ * * A Device mode register, when the core is operating in Host
+ * mode
+ * The register access is completed on the AHB with an OKAY
+ * response, but is ignored by the core internally and doesn't
+ * affect the operation of the core.
+ * @curmod: Current Mode of Operation (CurMod)
+ * Indicates the current mode of operation.
+ * * 1'b0: Device mode
+ * * 1'b1: Host mode
+ */
+ struct cvmx_usbcx_gintsts_s {
+ __BITFIELD_FIELD(uint32_t wkupint : 1,
+ __BITFIELD_FIELD(uint32_t sessreqint : 1,
+ __BITFIELD_FIELD(uint32_t disconnint : 1,
+ __BITFIELD_FIELD(uint32_t conidstschng : 1,
+ __BITFIELD_FIELD(uint32_t reserved_27_27 : 1,
+ __BITFIELD_FIELD(uint32_t ptxfemp : 1,
+ __BITFIELD_FIELD(uint32_t hchint : 1,
+ __BITFIELD_FIELD(uint32_t prtint : 1,
+ __BITFIELD_FIELD(uint32_t reserved_23_23 : 1,
+ __BITFIELD_FIELD(uint32_t fetsusp : 1,
+ __BITFIELD_FIELD(uint32_t incomplp : 1,
+ __BITFIELD_FIELD(uint32_t incompisoin : 1,
+ __BITFIELD_FIELD(uint32_t oepint : 1,
+ __BITFIELD_FIELD(uint32_t iepint : 1,
+ __BITFIELD_FIELD(uint32_t epmis : 1,
+ __BITFIELD_FIELD(uint32_t reserved_16_16 : 1,
+ __BITFIELD_FIELD(uint32_t eopf : 1,
+ __BITFIELD_FIELD(uint32_t isooutdrop : 1,
+ __BITFIELD_FIELD(uint32_t enumdone : 1,
+ __BITFIELD_FIELD(uint32_t usbrst : 1,
+ __BITFIELD_FIELD(uint32_t usbsusp : 1,
+ __BITFIELD_FIELD(uint32_t erlysusp : 1,
+ __BITFIELD_FIELD(uint32_t i2cint : 1,
+ __BITFIELD_FIELD(uint32_t ulpickint : 1,
+ __BITFIELD_FIELD(uint32_t goutnakeff : 1,
+ __BITFIELD_FIELD(uint32_t ginnakeff : 1,
+ __BITFIELD_FIELD(uint32_t nptxfemp : 1,
+ __BITFIELD_FIELD(uint32_t rxflvl : 1,
+ __BITFIELD_FIELD(uint32_t sof : 1,
+ __BITFIELD_FIELD(uint32_t otgint : 1,
+ __BITFIELD_FIELD(uint32_t modemis : 1,
+ __BITFIELD_FIELD(uint32_t curmod : 1,
+ ;))))))))))))))))))))))))))))))))
+ } s;
+};
+
+/**
+ * cvmx_usbc#_gnptxfsiz
+ *
+ * Non-Periodic Transmit FIFO Size Register (GNPTXFSIZ)
+ *
+ * The application can program the RAM size and the memory start address for the
+ * Non-Periodic TxFIFO.
+ */
+union cvmx_usbcx_gnptxfsiz {
+ uint32_t u32;
+ /**
+ * struct cvmx_usbcx_gnptxfsiz_s
+ * @nptxfdep: Non-Periodic TxFIFO Depth (NPTxFDep)
+ * This value is in terms of 32-bit words.
+ * Minimum value is 16
+ * Maximum value is 32768
+ * @nptxfstaddr: Non-Periodic Transmit RAM Start Address (NPTxFStAddr)
+ * This field contains the memory start address for Non-Periodic
+ * Transmit FIFO RAM.
+ */
+ struct cvmx_usbcx_gnptxfsiz_s {
+ __BITFIELD_FIELD(uint32_t nptxfdep : 16,
+ __BITFIELD_FIELD(uint32_t nptxfstaddr : 16,
+ ;))
+ } s;
+};
+
+/**
+ * cvmx_usbc#_gnptxsts
+ *
+ * Non-Periodic Transmit FIFO/Queue Status Register (GNPTXSTS)
+ *
+ * This read-only register contains the free space information for the
+ * Non-Periodic TxFIFO and the Non-Periodic Transmit Request Queue.
+ */
+union cvmx_usbcx_gnptxsts {
+ uint32_t u32;
+ /**
+ * struct cvmx_usbcx_gnptxsts_s
+ * @nptxqtop: Top of the Non-Periodic Transmit Request Queue (NPTxQTop)
+ * Entry in the Non-Periodic Tx Request Queue that is currently
+ * being processed by the MAC.
+ * * Bits [30:27]: Channel/endpoint number
+ * * Bits [26:25]:
+ * - 2'b00: IN/OUT token
+ * - 2'b01: Zero-length transmit packet (device IN/host OUT)
+ * - 2'b10: PING/CSPLIT token
+ * - 2'b11: Channel halt command
+ * * Bit [24]: Terminate (last entry for selected channel/endpoint)
+ * @nptxqspcavail: Non-Periodic Transmit Request Queue Space Available
+ * (NPTxQSpcAvail)
+ * Indicates the amount of free space available in the Non-
+ * Periodic Transmit Request Queue. This queue holds both IN
+ * and OUT requests in Host mode. Device mode has only IN
+ * requests.
+ * * 8'h0: Non-Periodic Transmit Request Queue is full
+ * * 8'h1: 1 location available
+ * * 8'h2: 2 locations available
+ * * n: n locations available (0..8)
+ * * Others: Reserved
+ * @nptxfspcavail: Non-Periodic TxFIFO Space Avail (NPTxFSpcAvail)
+ * Indicates the amount of free space available in the Non-
+ * Periodic TxFIFO.
+ * Values are in terms of 32-bit words.
+ * * 16'h0: Non-Periodic TxFIFO is full
+ * * 16'h1: 1 word available
+ * * 16'h2: 2 words available
+ * * 16'hn: n words available (where 0..32768)
+ * * 16'h8000: 32768 words available
+ * * Others: Reserved
+ */
+ struct cvmx_usbcx_gnptxsts_s {
+ __BITFIELD_FIELD(uint32_t reserved_31_31 : 1,
+ __BITFIELD_FIELD(uint32_t nptxqtop : 7,
+ __BITFIELD_FIELD(uint32_t nptxqspcavail : 8,
+ __BITFIELD_FIELD(uint32_t nptxfspcavail : 16,
+ ;))))
+ } s;
+};
+
+/**
+ * cvmx_usbc#_grstctl
+ *
+ * Core Reset Register (GRSTCTL)
+ *
+ * The application uses this register to reset various hardware features inside
+ * the core.
+ */
+union cvmx_usbcx_grstctl {
+ uint32_t u32;
+ /**
+ * struct cvmx_usbcx_grstctl_s
+ * @ahbidle: AHB Master Idle (AHBIdle)
+ * Indicates that the AHB Master State Machine is in the IDLE
+ * condition.
+ * @dmareq: DMA Request Signal (DMAReq)
+ * Indicates that the DMA request is in progress. Used for debug.
+ * @txfnum: TxFIFO Number (TxFNum)
+ * This is the FIFO number that must be flushed using the TxFIFO
+ * Flush bit. This field must not be changed until the core clears
+ * the TxFIFO Flush bit.
+ * * 5'h0: Non-Periodic TxFIFO flush
+ * * 5'h1: Periodic TxFIFO 1 flush in Device mode or Periodic
+ * TxFIFO flush in Host mode
+ * * 5'h2: Periodic TxFIFO 2 flush in Device mode
+ * - ...
+ * * 5'hF: Periodic TxFIFO 15 flush in Device mode
+ * * 5'h10: Flush all the Periodic and Non-Periodic TxFIFOs in the
+ * core
+ * @txfflsh: TxFIFO Flush (TxFFlsh)
+ * This bit selectively flushes a single or all transmit FIFOs, but
+ * cannot do so if the core is in the midst of a transaction.
+ * The application must only write this bit after checking that the
+ * core is neither writing to the TxFIFO nor reading from the
+ * TxFIFO.
+ * The application must wait until the core clears this bit before
+ * performing any operations. This bit takes 8 clocks (of phy_clk
+ * or hclk, whichever is slower) to clear.
+ * @rxfflsh: RxFIFO Flush (RxFFlsh)
+ * The application can flush the entire RxFIFO using this bit, but
+ * must first ensure that the core is not in the middle of a
+ * transaction.
+ * The application must only write to this bit after checking that
+ * the core is neither reading from the RxFIFO nor writing to the
+ * RxFIFO.
+ * The application must wait until the bit is cleared before
+ * performing any other operations. This bit will take 8 clocks
+ * (slowest of PHY or AHB clock) to clear.
+ * @intknqflsh: IN Token Sequence Learning Queue Flush (INTknQFlsh)
+ * The application writes this bit to flush the IN Token Sequence
+ * Learning Queue.
+ * @frmcntrrst: Host Frame Counter Reset (FrmCntrRst)
+ * The application writes this bit to reset the (micro)frame number
+ * counter inside the core. When the (micro)frame counter is reset,
+ * the subsequent SOF sent out by the core will have a
+ * (micro)frame number of 0.
+ * @hsftrst: HClk Soft Reset (HSftRst)
+ * The application uses this bit to flush the control logic in the
+ * AHB Clock domain. Only AHB Clock Domain pipelines are reset.
+ * * FIFOs are not flushed with this bit.
+ * * All state machines in the AHB clock domain are reset to the
+ * Idle state after terminating the transactions on the AHB,
+ * following the protocol.
+ * * CSR control bits used by the AHB clock domain state
+ * machines are cleared.
+ * * To clear this interrupt, status mask bits that control the
+ * interrupt status and are generated by the AHB clock domain
+ * state machine are cleared.
+ * * Because interrupt status bits are not cleared, the application
+ * can get the status of any core events that occurred after it set
+ * this bit.
+ * This is a self-clearing bit that the core clears after all
+ * necessary logic is reset in the core. This may take several
+ * clocks, depending on the core's current state.
+ * @csftrst: Core Soft Reset (CSftRst)
+ * Resets the hclk and phy_clock domains as follows:
+ * * Clears the interrupts and all the CSR registers except the
+ * following register bits:
+ * - PCGCCTL.RstPdwnModule
+ * - PCGCCTL.GateHclk
+ * - PCGCCTL.PwrClmp
+ * - PCGCCTL.StopPPhyLPwrClkSelclk
+ * - GUSBCFG.PhyLPwrClkSel
+ * - GUSBCFG.DDRSel
+ * - GUSBCFG.PHYSel
+ * - GUSBCFG.FSIntf
+ * - GUSBCFG.ULPI_UTMI_Sel
+ * - GUSBCFG.PHYIf
+ * - HCFG.FSLSPclkSel
+ * - DCFG.DevSpd
+ * * All module state machines (except the AHB Slave Unit) are
+ * reset to the IDLE state, and all the transmit FIFOs and the
+ * receive FIFO are flushed.
+ * * Any transactions on the AHB Master are terminated as soon
+ * as possible, after gracefully completing the last data phase of
+ * an AHB transfer. Any transactions on the USB are terminated
+ * immediately.
+ * The application can write to this bit any time it wants to reset
+ * the core. This is a self-clearing bit and the core clears this
+ * bit after all the necessary logic is reset in the core, which
+ * may take several clocks, depending on the current state of the
+ * core. Once this bit is cleared software should wait at least 3
+ * PHY clocks before doing any access to the PHY domain
+ * (synchronization delay). Software should also should check that
+ * bit 31 of this register is 1 (AHB Master is IDLE) before
+ * starting any operation.
+ * Typically software reset is used during software development
+ * and also when you dynamically change the PHY selection bits
+ * in the USB configuration registers listed above. When you
+ * change the PHY, the corresponding clock for the PHY is
+ * selected and used in the PHY domain. Once a new clock is
+ * selected, the PHY domain has to be reset for proper operation.
+ */
+ struct cvmx_usbcx_grstctl_s {
+ __BITFIELD_FIELD(uint32_t ahbidle : 1,
+ __BITFIELD_FIELD(uint32_t dmareq : 1,
+ __BITFIELD_FIELD(uint32_t reserved_11_29 : 19,
+ __BITFIELD_FIELD(uint32_t txfnum : 5,
+ __BITFIELD_FIELD(uint32_t txfflsh : 1,
+ __BITFIELD_FIELD(uint32_t rxfflsh : 1,
+ __BITFIELD_FIELD(uint32_t intknqflsh : 1,
+ __BITFIELD_FIELD(uint32_t frmcntrrst : 1,
+ __BITFIELD_FIELD(uint32_t hsftrst : 1,
+ __BITFIELD_FIELD(uint32_t csftrst : 1,
+ ;))))))))))
+ } s;
+};
+
+/**
+ * cvmx_usbc#_grxfsiz
+ *
+ * Receive FIFO Size Register (GRXFSIZ)
+ *
+ * The application can program the RAM size that must be allocated to the
+ * RxFIFO.
+ */
+union cvmx_usbcx_grxfsiz {
+ uint32_t u32;
+ /**
+ * struct cvmx_usbcx_grxfsiz_s
+ * @rxfdep: RxFIFO Depth (RxFDep)
+ * This value is in terms of 32-bit words.
+ * * Minimum value is 16
+ * * Maximum value is 32768
+ */
+ struct cvmx_usbcx_grxfsiz_s {
+ __BITFIELD_FIELD(uint32_t reserved_16_31 : 16,
+ __BITFIELD_FIELD(uint32_t rxfdep : 16,
+ ;))
+ } s;
+};
+
+/**
+ * cvmx_usbc#_grxstsph
+ *
+ * Receive Status Read and Pop Register, Host Mode (GRXSTSPH)
+ *
+ * A read to the Receive Status Read and Pop register returns and additionally
+ * pops the top data entry out of the RxFIFO.
+ * This Description is only valid when the core is in Host Mode. For Device Mode
+ * use USBC_GRXSTSPD instead.
+ * NOTE: GRXSTSPH and GRXSTSPD are physically the same register and share the
+ * same offset in the O2P USB core. The offset difference shown in this
+ * document is for software clarity and is actually ignored by the
+ * hardware.
+ */
+union cvmx_usbcx_grxstsph {
+ uint32_t u32;
+ /**
+ * struct cvmx_usbcx_grxstsph_s
+ * @pktsts: Packet Status (PktSts)
+ * Indicates the status of the received packet
+ * * 4'b0010: IN data packet received
+ * * 4'b0011: IN transfer completed (triggers an interrupt)
+ * * 4'b0101: Data toggle error (triggers an interrupt)
+ * * 4'b0111: Channel halted (triggers an interrupt)
+ * * Others: Reserved
+ * @dpid: Data PID (DPID)
+ * * 2'b00: DATA0
+ * * 2'b10: DATA1
+ * * 2'b01: DATA2
+ * * 2'b11: MDATA
+ * @bcnt: Byte Count (BCnt)
+ * Indicates the byte count of the received IN data packet
+ * @chnum: Channel Number (ChNum)
+ * Indicates the channel number to which the current received
+ * packet belongs.
+ */
+ struct cvmx_usbcx_grxstsph_s {
+ __BITFIELD_FIELD(uint32_t reserved_21_31 : 11,
+ __BITFIELD_FIELD(uint32_t pktsts : 4,
+ __BITFIELD_FIELD(uint32_t dpid : 2,
+ __BITFIELD_FIELD(uint32_t bcnt : 11,
+ __BITFIELD_FIELD(uint32_t chnum : 4,
+ ;)))))
+ } s;
+};
+
+/**
+ * cvmx_usbc#_gusbcfg
+ *
+ * Core USB Configuration Register (GUSBCFG)
+ *
+ * This register can be used to configure the core after power-on or a changing
+ * to Host mode or Device mode. It contains USB and USB-PHY related
+ * configuration parameters. The application must program this register before
+ * starting any transactions on either the AHB or the USB. Do not make changes
+ * to this register after the initial programming.
+ */
+union cvmx_usbcx_gusbcfg {
+ uint32_t u32;
+ /**
+ * struct cvmx_usbcx_gusbcfg_s
+ * @otgi2csel: UTMIFS or I2C Interface Select (OtgI2CSel)
+ * This bit is always 0x0.
+ * @phylpwrclksel: PHY Low-Power Clock Select (PhyLPwrClkSel)
+ * Software should set this bit to 0x0.
+ * Selects either 480-MHz or 48-MHz (low-power) PHY mode. In
+ * FS and LS modes, the PHY can usually operate on a 48-MHz
+ * clock to save power.
+ * * 1'b0: 480-MHz Internal PLL clock
+ * * 1'b1: 48-MHz External Clock
+ * In 480 MHz mode, the UTMI interface operates at either 60 or
+ * 30-MHz, depending upon whether 8- or 16-bit data width is
+ * selected. In 48-MHz mode, the UTMI interface operates at 48
+ * MHz in FS mode and at either 48 or 6 MHz in LS mode
+ * (depending on the PHY vendor).
+ * This bit drives the utmi_fsls_low_power core output signal, and
+ * is valid only for UTMI+ PHYs.
+ * @usbtrdtim: USB Turnaround Time (USBTrdTim)
+ * Sets the turnaround time in PHY clocks.
+ * Specifies the response time for a MAC request to the Packet
+ * FIFO Controller (PFC) to fetch data from the DFIFO (SPRAM).
+ * This must be programmed to 0x5.
+ * @hnpcap: HNP-Capable (HNPCap)
+ * This bit is always 0x0.
+ * @srpcap: SRP-Capable (SRPCap)
+ * This bit is always 0x0.
+ * @ddrsel: ULPI DDR Select (DDRSel)
+ * Software should set this bit to 0x0.
+ * @physel: USB 2.0 High-Speed PHY or USB 1.1 Full-Speed Serial
+ * Software should set this bit to 0x0.
+ * @fsintf: Full-Speed Serial Interface Select (FSIntf)
+ * Software should set this bit to 0x0.
+ * @ulpi_utmi_sel: ULPI or UTMI+ Select (ULPI_UTMI_Sel)
+ * This bit is always 0x0.
+ * @phyif: PHY Interface (PHYIf)
+ * This bit is always 0x1.
+ * @toutcal: HS/FS Timeout Calibration (TOutCal)
+ * The number of PHY clocks that the application programs in this
+ * field is added to the high-speed/full-speed interpacket timeout
+ * duration in the core to account for any additional delays
+ * introduced by the PHY. This may be required, since the delay
+ * introduced by the PHY in generating the linestate condition may
+ * vary from one PHY to another.
+ * The USB standard timeout value for high-speed operation is
+ * 736 to 816 (inclusive) bit times. The USB standard timeout
+ * value for full-speed operation is 16 to 18 (inclusive) bit
+ * times. The application must program this field based on the
+ * speed of enumeration. The number of bit times added per PHY
+ * clock are:
+ * High-speed operation:
+ * * One 30-MHz PHY clock = 16 bit times
+ * * One 60-MHz PHY clock = 8 bit times
+ * Full-speed operation:
+ * * One 30-MHz PHY clock = 0.4 bit times
+ * * One 60-MHz PHY clock = 0.2 bit times
+ * * One 48-MHz PHY clock = 0.25 bit times
+ */
+ struct cvmx_usbcx_gusbcfg_s {
+ __BITFIELD_FIELD(uint32_t reserved_17_31 : 15,
+ __BITFIELD_FIELD(uint32_t otgi2csel : 1,
+ __BITFIELD_FIELD(uint32_t phylpwrclksel : 1,
+ __BITFIELD_FIELD(uint32_t reserved_14_14 : 1,
+ __BITFIELD_FIELD(uint32_t usbtrdtim : 4,
+ __BITFIELD_FIELD(uint32_t hnpcap : 1,
+ __BITFIELD_FIELD(uint32_t srpcap : 1,
+ __BITFIELD_FIELD(uint32_t ddrsel : 1,
+ __BITFIELD_FIELD(uint32_t physel : 1,
+ __BITFIELD_FIELD(uint32_t fsintf : 1,
+ __BITFIELD_FIELD(uint32_t ulpi_utmi_sel : 1,
+ __BITFIELD_FIELD(uint32_t phyif : 1,
+ __BITFIELD_FIELD(uint32_t toutcal : 3,
+ ;)))))))))))))
+ } s;
+};
+
+/**
+ * cvmx_usbc#_haint
+ *
+ * Host All Channels Interrupt Register (HAINT)
+ *
+ * When a significant event occurs on a channel, the Host All Channels Interrupt
+ * register interrupts the application using the Host Channels Interrupt bit of
+ * the Core Interrupt register (GINTSTS.HChInt). This is shown in Interrupt.
+ * There is one interrupt bit per channel, up to a maximum of 16 bits. Bits in
+ * this register are set and cleared when the application sets and clears bits
+ * in the corresponding Host Channel-n Interrupt register.
+ */
+union cvmx_usbcx_haint {
+ uint32_t u32;
+ /**
+ * struct cvmx_usbcx_haint_s
+ * @haint: Channel Interrupts (HAINT)
+ * One bit per channel: Bit 0 for Channel 0, bit 15 for Channel 15
+ */
+ struct cvmx_usbcx_haint_s {
+ __BITFIELD_FIELD(uint32_t reserved_16_31 : 16,
+ __BITFIELD_FIELD(uint32_t haint : 16,
+ ;))
+ } s;
+};
+
+/**
+ * cvmx_usbc#_haintmsk
+ *
+ * Host All Channels Interrupt Mask Register (HAINTMSK)
+ *
+ * The Host All Channel Interrupt Mask register works with the Host All Channel
+ * Interrupt register to interrupt the application when an event occurs on a
+ * channel. There is one interrupt mask bit per channel, up to a maximum of 16
+ * bits.
+ * Mask interrupt: 1'b0 Unmask interrupt: 1'b1
+ */
+union cvmx_usbcx_haintmsk {
+ uint32_t u32;
+ /**
+ * struct cvmx_usbcx_haintmsk_s
+ * @haintmsk: Channel Interrupt Mask (HAINTMsk)
+ * One bit per channel: Bit 0 for channel 0, bit 15 for channel 15
+ */
+ struct cvmx_usbcx_haintmsk_s {
+ __BITFIELD_FIELD(uint32_t reserved_16_31 : 16,
+ __BITFIELD_FIELD(uint32_t haintmsk : 16,
+ ;))
+ } s;
+};
+
+/**
+ * cvmx_usbc#_hcchar#
+ *
+ * Host Channel-n Characteristics Register (HCCHAR)
+ *
+ */
+union cvmx_usbcx_hccharx {
+ uint32_t u32;
+ /**
+ * struct cvmx_usbcx_hccharx_s
+ * @chena: Channel Enable (ChEna)
+ * This field is set by the application and cleared by the OTG
+ * host.
+ * * 1'b0: Channel disabled
+ * * 1'b1: Channel enabled
+ * @chdis: Channel Disable (ChDis)
+ * The application sets this bit to stop transmitting/receiving
+ * data on a channel, even before the transfer for that channel is
+ * complete. The application must wait for the Channel Disabled
+ * interrupt before treating the channel as disabled.
+ * @oddfrm: Odd Frame (OddFrm)
+ * This field is set (reset) by the application to indicate that
+ * the OTG host must perform a transfer in an odd (micro)frame.
+ * This field is applicable for only periodic (isochronous and
+ * interrupt) transactions.
+ * * 1'b0: Even (micro)frame
+ * * 1'b1: Odd (micro)frame
+ * @devaddr: Device Address (DevAddr)
+ * This field selects the specific device serving as the data
+ * source or sink.
+ * @ec: Multi Count (MC) / Error Count (EC)
+ * When the Split Enable bit of the Host Channel-n Split Control
+ * register (HCSPLTn.SpltEna) is reset (1'b0), this field indicates
+ * to the host the number of transactions that should be executed
+ * per microframe for this endpoint.
+ * * 2'b00: Reserved. This field yields undefined results.
+ * * 2'b01: 1 transaction
+ * * 2'b10: 2 transactions to be issued for this endpoint per
+ * microframe
+ * * 2'b11: 3 transactions to be issued for this endpoint per
+ * microframe
+ * When HCSPLTn.SpltEna is set (1'b1), this field indicates the
+ * number of immediate retries to be performed for a periodic split
+ * transactions on transaction errors. This field must be set to at
+ * least 2'b01.
+ * @eptype: Endpoint Type (EPType)
+ * Indicates the transfer type selected.
+ * * 2'b00: Control
+ * * 2'b01: Isochronous
+ * * 2'b10: Bulk
+ * * 2'b11: Interrupt
+ * @lspddev: Low-Speed Device (LSpdDev)
+ * This field is set by the application to indicate that this
+ * channel is communicating to a low-speed device.
+ * @epdir: Endpoint Direction (EPDir)
+ * Indicates whether the transaction is IN or OUT.
+ * * 1'b0: OUT
+ * * 1'b1: IN
+ * @epnum: Endpoint Number (EPNum)
+ * Indicates the endpoint number on the device serving as the
+ * data source or sink.
+ * @mps: Maximum Packet Size (MPS)
+ * Indicates the maximum packet size of the associated endpoint.
+ */
+ struct cvmx_usbcx_hccharx_s {
+ __BITFIELD_FIELD(uint32_t chena : 1,
+ __BITFIELD_FIELD(uint32_t chdis : 1,
+ __BITFIELD_FIELD(uint32_t oddfrm : 1,
+ __BITFIELD_FIELD(uint32_t devaddr : 7,
+ __BITFIELD_FIELD(uint32_t ec : 2,
+ __BITFIELD_FIELD(uint32_t eptype : 2,
+ __BITFIELD_FIELD(uint32_t lspddev : 1,
+ __BITFIELD_FIELD(uint32_t reserved_16_16 : 1,
+ __BITFIELD_FIELD(uint32_t epdir : 1,
+ __BITFIELD_FIELD(uint32_t epnum : 4,
+ __BITFIELD_FIELD(uint32_t mps : 11,
+ ;)))))))))))
+ } s;
+};
+
+/**
+ * cvmx_usbc#_hcfg
+ *
+ * Host Configuration Register (HCFG)
+ *
+ * This register configures the core after power-on. Do not make changes to this
+ * register after initializing the host.
+ */
+union cvmx_usbcx_hcfg {
+ uint32_t u32;
+ /**
+ * struct cvmx_usbcx_hcfg_s
+ * @fslssupp: FS- and LS-Only Support (FSLSSupp)
+ * The application uses this bit to control the core's enumeration
+ * speed. Using this bit, the application can make the core
+ * enumerate as a FS host, even if the connected device supports
+ * HS traffic. Do not make changes to this field after initial
+ * programming.
+ * * 1'b0: HS/FS/LS, based on the maximum speed supported by
+ * the connected device
+ * * 1'b1: FS/LS-only, even if the connected device can support HS
+ * @fslspclksel: FS/LS PHY Clock Select (FSLSPclkSel)
+ * When the core is in FS Host mode
+ * * 2'b00: PHY clock is running at 30/60 MHz
+ * * 2'b01: PHY clock is running at 48 MHz
+ * * Others: Reserved
+ * When the core is in LS Host mode
+ * * 2'b00: PHY clock is running at 30/60 MHz. When the
+ * UTMI+/ULPI PHY Low Power mode is not selected, use
+ * 30/60 MHz.
+ * * 2'b01: PHY clock is running at 48 MHz. When the UTMI+
+ * PHY Low Power mode is selected, use 48MHz if the PHY
+ * supplies a 48 MHz clock during LS mode.
+ * * 2'b10: PHY clock is running at 6 MHz. In USB 1.1 FS mode,
+ * use 6 MHz when the UTMI+ PHY Low Power mode is
+ * selected and the PHY supplies a 6 MHz clock during LS
+ * mode. If you select a 6 MHz clock during LS mode, you must
+ * do a soft reset.
+ * * 2'b11: Reserved
+ */
+ struct cvmx_usbcx_hcfg_s {
+ __BITFIELD_FIELD(uint32_t reserved_3_31 : 29,
+ __BITFIELD_FIELD(uint32_t fslssupp : 1,
+ __BITFIELD_FIELD(uint32_t fslspclksel : 2,
+ ;)))
+ } s;
+};
+
+/**
+ * cvmx_usbc#_hcint#
+ *
+ * Host Channel-n Interrupt Register (HCINT)
+ *
+ * This register indicates the status of a channel with respect to USB- and
+ * AHB-related events. The application must read this register when the Host
+ * Channels Interrupt bit of the Core Interrupt register (GINTSTS.HChInt) is
+ * set. Before the application can read this register, it must first read
+ * the Host All Channels Interrupt (HAINT) register to get the exact channel
+ * number for the Host Channel-n Interrupt register. The application must clear
+ * the appropriate bit in this register to clear the corresponding bits in the
+ * HAINT and GINTSTS registers.
+ */
+union cvmx_usbcx_hcintx {
+ uint32_t u32;
+ /**
+ * struct cvmx_usbcx_hcintx_s
+ * @datatglerr: Data Toggle Error (DataTglErr)
+ * @frmovrun: Frame Overrun (FrmOvrun)
+ * @bblerr: Babble Error (BblErr)
+ * @xacterr: Transaction Error (XactErr)
+ * @nyet: NYET Response Received Interrupt (NYET)
+ * @ack: ACK Response Received Interrupt (ACK)
+ * @nak: NAK Response Received Interrupt (NAK)
+ * @stall: STALL Response Received Interrupt (STALL)
+ * @ahberr: This bit is always 0x0.
+ * @chhltd: Channel Halted (ChHltd)
+ * Indicates the transfer completed abnormally either because of
+ * any USB transaction error or in response to disable request by
+ * the application.
+ * @xfercompl: Transfer Completed (XferCompl)
+ * Transfer completed normally without any errors.
+ */
+ struct cvmx_usbcx_hcintx_s {
+ __BITFIELD_FIELD(uint32_t reserved_11_31 : 21,
+ __BITFIELD_FIELD(uint32_t datatglerr : 1,
+ __BITFIELD_FIELD(uint32_t frmovrun : 1,
+ __BITFIELD_FIELD(uint32_t bblerr : 1,
+ __BITFIELD_FIELD(uint32_t xacterr : 1,
+ __BITFIELD_FIELD(uint32_t nyet : 1,
+ __BITFIELD_FIELD(uint32_t ack : 1,
+ __BITFIELD_FIELD(uint32_t nak : 1,
+ __BITFIELD_FIELD(uint32_t stall : 1,
+ __BITFIELD_FIELD(uint32_t ahberr : 1,
+ __BITFIELD_FIELD(uint32_t chhltd : 1,
+ __BITFIELD_FIELD(uint32_t xfercompl : 1,
+ ;))))))))))))
+ } s;
+};
+
+/**
+ * cvmx_usbc#_hcintmsk#
+ *
+ * Host Channel-n Interrupt Mask Register (HCINTMSKn)
+ *
+ * This register reflects the mask for each channel status described in the
+ * previous section.
+ * Mask interrupt: 1'b0 Unmask interrupt: 1'b1
+ */
+union cvmx_usbcx_hcintmskx {
+ uint32_t u32;
+ /**
+ * struct cvmx_usbcx_hcintmskx_s
+ * @datatglerrmsk: Data Toggle Error Mask (DataTglErrMsk)
+ * @frmovrunmsk: Frame Overrun Mask (FrmOvrunMsk)
+ * @bblerrmsk: Babble Error Mask (BblErrMsk)
+ * @xacterrmsk: Transaction Error Mask (XactErrMsk)
+ * @nyetmsk: NYET Response Received Interrupt Mask (NyetMsk)
+ * @ackmsk: ACK Response Received Interrupt Mask (AckMsk)
+ * @nakmsk: NAK Response Received Interrupt Mask (NakMsk)
+ * @stallmsk: STALL Response Received Interrupt Mask (StallMsk)
+ * @ahberrmsk: AHB Error Mask (AHBErrMsk)
+ * @chhltdmsk: Channel Halted Mask (ChHltdMsk)
+ * @xfercomplmsk: Transfer Completed Mask (XferComplMsk)
+ */
+ struct cvmx_usbcx_hcintmskx_s {
+ __BITFIELD_FIELD(uint32_t reserved_11_31 : 21,
+ __BITFIELD_FIELD(uint32_t datatglerrmsk : 1,
+ __BITFIELD_FIELD(uint32_t frmovrunmsk : 1,
+ __BITFIELD_FIELD(uint32_t bblerrmsk : 1,
+ __BITFIELD_FIELD(uint32_t xacterrmsk : 1,
+ __BITFIELD_FIELD(uint32_t nyetmsk : 1,
+ __BITFIELD_FIELD(uint32_t ackmsk : 1,
+ __BITFIELD_FIELD(uint32_t nakmsk : 1,
+ __BITFIELD_FIELD(uint32_t stallmsk : 1,
+ __BITFIELD_FIELD(uint32_t ahberrmsk : 1,
+ __BITFIELD_FIELD(uint32_t chhltdmsk : 1,
+ __BITFIELD_FIELD(uint32_t xfercomplmsk : 1,
+ ;))))))))))))
+ } s;
+};
+
+/**
+ * cvmx_usbc#_hcsplt#
+ *
+ * Host Channel-n Split Control Register (HCSPLT)
+ *
+ */
+union cvmx_usbcx_hcspltx {
+ uint32_t u32;
+ /**
+ * struct cvmx_usbcx_hcspltx_s
+ * @spltena: Split Enable (SpltEna)
+ * The application sets this field to indicate that this channel is
+ * enabled to perform split transactions.
+ * @compsplt: Do Complete Split (CompSplt)
+ * The application sets this field to request the OTG host to
+ * perform a complete split transaction.
+ * @xactpos: Transaction Position (XactPos)
+ * This field is used to determine whether to send all, first,
+ * middle, or last payloads with each OUT transaction.
+ * * 2'b11: All. This is the entire data payload is of this
+ * transaction (which is less than or equal to 188 bytes).
+ * * 2'b10: Begin. This is the first data payload of this
+ * transaction (which is larger than 188 bytes).
+ * * 2'b00: Mid. This is the middle payload of this transaction
+ * (which is larger than 188 bytes).
+ * * 2'b01: End. This is the last payload of this transaction
+ * (which is larger than 188 bytes).
+ * @hubaddr: Hub Address (HubAddr)
+ * This field holds the device address of the transaction
+ * translator's hub.
+ * @prtaddr: Port Address (PrtAddr)
+ * This field is the port number of the recipient transaction
+ * translator.
+ */
+ struct cvmx_usbcx_hcspltx_s {
+ __BITFIELD_FIELD(uint32_t spltena : 1,
+ __BITFIELD_FIELD(uint32_t reserved_17_30 : 14,
+ __BITFIELD_FIELD(uint32_t compsplt : 1,
+ __BITFIELD_FIELD(uint32_t xactpos : 2,
+ __BITFIELD_FIELD(uint32_t hubaddr : 7,
+ __BITFIELD_FIELD(uint32_t prtaddr : 7,
+ ;))))))
+ } s;
+};
+
+/**
+ * cvmx_usbc#_hctsiz#
+ *
+ * Host Channel-n Transfer Size Register (HCTSIZ)
+ *
+ */
+union cvmx_usbcx_hctsizx {
+ uint32_t u32;
+ /**
+ * struct cvmx_usbcx_hctsizx_s
+ * @dopng: Do Ping (DoPng)
+ * Setting this field to 1 directs the host to do PING protocol.
+ * @pid: PID (Pid)
+ * The application programs this field with the type of PID to use
+ * for the initial transaction. The host will maintain this field
+ * for the rest of the transfer.
+ * * 2'b00: DATA0
+ * * 2'b01: DATA2
+ * * 2'b10: DATA1
+ * * 2'b11: MDATA (non-control)/SETUP (control)
+ * @pktcnt: Packet Count (PktCnt)
+ * This field is programmed by the application with the expected
+ * number of packets to be transmitted (OUT) or received (IN).
+ * The host decrements this count on every successful
+ * transmission or reception of an OUT/IN packet. Once this count
+ * reaches zero, the application is interrupted to indicate normal
+ * completion.
+ * @xfersize: Transfer Size (XferSize)
+ * For an OUT, this field is the number of data bytes the host will
+ * send during the transfer.
+ * For an IN, this field is the buffer size that the application
+ * has reserved for the transfer. The application is expected to
+ * program this field as an integer multiple of the maximum packet
+ * size for IN transactions (periodic and non-periodic).
+ */
+ struct cvmx_usbcx_hctsizx_s {
+ __BITFIELD_FIELD(uint32_t dopng : 1,
+ __BITFIELD_FIELD(uint32_t pid : 2,
+ __BITFIELD_FIELD(uint32_t pktcnt : 10,
+ __BITFIELD_FIELD(uint32_t xfersize : 19,
+ ;))))
+ } s;
+};
+
+/**
+ * cvmx_usbc#_hfir
+ *
+ * Host Frame Interval Register (HFIR)
+ *
+ * This register stores the frame interval information for the current speed to
+ * which the O2P USB core has enumerated.
+ */
+union cvmx_usbcx_hfir {
+ uint32_t u32;
+ /**
+ * struct cvmx_usbcx_hfir_s
+ * @frint: Frame Interval (FrInt)
+ * The value that the application programs to this field specifies
+ * the interval between two consecutive SOFs (FS) or micro-
+ * SOFs (HS) or Keep-Alive tokens (HS). This field contains the
+ * number of PHY clocks that constitute the required frame
+ * interval. The default value set in this field for a FS operation
+ * when the PHY clock frequency is 60 MHz. The application can
+ * write a value to this register only after the Port Enable bit of
+ * the Host Port Control and Status register (HPRT.PrtEnaPort)
+ * has been set. If no value is programmed, the core calculates
+ * the value based on the PHY clock specified in the FS/LS PHY
+ * Clock Select field of the Host Configuration register
+ * (HCFG.FSLSPclkSel). Do not change the value of this field
+ * after the initial configuration.
+ * * 125 us (PHY clock frequency for HS)
+ * * 1 ms (PHY clock frequency for FS/LS)
+ */
+ struct cvmx_usbcx_hfir_s {
+ __BITFIELD_FIELD(uint32_t reserved_16_31 : 16,
+ __BITFIELD_FIELD(uint32_t frint : 16,
+ ;))
+ } s;
+};
+
+/**
+ * cvmx_usbc#_hfnum
+ *
+ * Host Frame Number/Frame Time Remaining Register (HFNUM)
+ *
+ * This register indicates the current frame number.
+ * It also indicates the time remaining (in terms of the number of PHY clocks)
+ * in the current (micro)frame.
+ */
+union cvmx_usbcx_hfnum {
+ uint32_t u32;
+ /**
+ * struct cvmx_usbcx_hfnum_s
+ * @frrem: Frame Time Remaining (FrRem)
+ * Indicates the amount of time remaining in the current
+ * microframe (HS) or frame (FS/LS), in terms of PHY clocks.
+ * This field decrements on each PHY clock. When it reaches
+ * zero, this field is reloaded with the value in the Frame
+ * Interval register and a new SOF is transmitted on the USB.
+ * @frnum: Frame Number (FrNum)
+ * This field increments when a new SOF is transmitted on the
+ * USB, and is reset to 0 when it reaches 16'h3FFF.
+ */
+ struct cvmx_usbcx_hfnum_s {
+ __BITFIELD_FIELD(uint32_t frrem : 16,
+ __BITFIELD_FIELD(uint32_t frnum : 16,
+ ;))
+ } s;
+};
+
+/**
+ * cvmx_usbc#_hprt
+ *
+ * Host Port Control and Status Register (HPRT)
+ *
+ * This register is available in both Host and Device modes.
+ * Currently, the OTG Host supports only one port.
+ * A single register holds USB port-related information such as USB reset,
+ * enable, suspend, resume, connect status, and test mode for each port. The
+ * R_SS_WC bits in this register can trigger an interrupt to the application
+ * through the Host Port Interrupt bit of the Core Interrupt register
+ * (GINTSTS.PrtInt). On a Port Interrupt, the application must read this
+ * register and clear the bit that caused the interrupt. For the R_SS_WC bits,
+ * the application must write a 1 to the bit to clear the interrupt.
+ */
+union cvmx_usbcx_hprt {
+ uint32_t u32;
+ /**
+ * struct cvmx_usbcx_hprt_s
+ * @prtspd: Port Speed (PrtSpd)
+ * Indicates the speed of the device attached to this port.
+ * * 2'b00: High speed
+ * * 2'b01: Full speed
+ * * 2'b10: Low speed
+ * * 2'b11: Reserved
+ * @prttstctl: Port Test Control (PrtTstCtl)
+ * The application writes a nonzero value to this field to put
+ * the port into a Test mode, and the corresponding pattern is
+ * signaled on the port.
+ * * 4'b0000: Test mode disabled
+ * * 4'b0001: Test_J mode
+ * * 4'b0010: Test_K mode
+ * * 4'b0011: Test_SE0_NAK mode
+ * * 4'b0100: Test_Packet mode
+ * * 4'b0101: Test_Force_Enable
+ * * Others: Reserved
+ * PrtSpd must be zero (i.e. the interface must be in high-speed
+ * mode) to use the PrtTstCtl test modes.
+ * @prtpwr: Port Power (PrtPwr)
+ * The application uses this field to control power to this port,
+ * and the core clears this bit on an overcurrent condition.
+ * * 1'b0: Power off
+ * * 1'b1: Power on
+ * @prtlnsts: Port Line Status (PrtLnSts)
+ * Indicates the current logic level USB data lines
+ * * Bit [10]: Logic level of D-
+ * * Bit [11]: Logic level of D+
+ * @prtrst: Port Reset (PrtRst)
+ * When the application sets this bit, a reset sequence is
+ * started on this port. The application must time the reset
+ * period and clear this bit after the reset sequence is
+ * complete.
+ * * 1'b0: Port not in reset
+ * * 1'b1: Port in reset
+ * The application must leave this bit set for at least a
+ * minimum duration mentioned below to start a reset on the
+ * port. The application can leave it set for another 10 ms in
+ * addition to the required minimum duration, before clearing
+ * the bit, even though there is no maximum limit set by the
+ * USB standard.
+ * * High speed: 50 ms
+ * * Full speed/Low speed: 10 ms
+ * @prtsusp: Port Suspend (PrtSusp)
+ * The application sets this bit to put this port in Suspend
+ * mode. The core only stops sending SOFs when this is set.
+ * To stop the PHY clock, the application must set the Port
+ * Clock Stop bit, which will assert the suspend input pin of
+ * the PHY.
+ * The read value of this bit reflects the current suspend
+ * status of the port. This bit is cleared by the core after a
+ * remote wakeup signal is detected or the application sets
+ * the Port Reset bit or Port Resume bit in this register or the
+ * Resume/Remote Wakeup Detected Interrupt bit or
+ * Disconnect Detected Interrupt bit in the Core Interrupt
+ * register (GINTSTS.WkUpInt or GINTSTS.DisconnInt,
+ * respectively).
+ * * 1'b0: Port not in Suspend mode
+ * * 1'b1: Port in Suspend mode
+ * @prtres: Port Resume (PrtRes)
+ * The application sets this bit to drive resume signaling on
+ * the port. The core continues to drive the resume signal
+ * until the application clears this bit.
+ * If the core detects a USB remote wakeup sequence, as
+ * indicated by the Port Resume/Remote Wakeup Detected
+ * Interrupt bit of the Core Interrupt register
+ * (GINTSTS.WkUpInt), the core starts driving resume
+ * signaling without application intervention and clears this bit
+ * when it detects a disconnect condition. The read value of
+ * this bit indicates whether the core is currently driving
+ * resume signaling.
+ * * 1'b0: No resume driven
+ * * 1'b1: Resume driven
+ * @prtovrcurrchng: Port Overcurrent Change (PrtOvrCurrChng)
+ * The core sets this bit when the status of the Port
+ * Overcurrent Active bit (bit 4) in this register changes.
+ * @prtovrcurract: Port Overcurrent Active (PrtOvrCurrAct)
+ * Indicates the overcurrent condition of the port.
+ * * 1'b0: No overcurrent condition
+ * * 1'b1: Overcurrent condition
+ * @prtenchng: Port Enable/Disable Change (PrtEnChng)
+ * The core sets this bit when the status of the Port Enable bit
+ * [2] of this register changes.
+ * @prtena: Port Enable (PrtEna)
+ * A port is enabled only by the core after a reset sequence,
+ * and is disabled by an overcurrent condition, a disconnect
+ * condition, or by the application clearing this bit. The
+ * application cannot set this bit by a register write. It can only
+ * clear it to disable the port. This bit does not trigger any
+ * interrupt to the application.
+ * * 1'b0: Port disabled
+ * * 1'b1: Port enabled
+ * @prtconndet: Port Connect Detected (PrtConnDet)
+ * The core sets this bit when a device connection is detected
+ * to trigger an interrupt to the application using the Host Port
+ * Interrupt bit of the Core Interrupt register (GINTSTS.PrtInt).
+ * The application must write a 1 to this bit to clear the
+ * interrupt.
+ * @prtconnsts: Port Connect Status (PrtConnSts)
+ * * 0: No device is attached to the port.
+ * * 1: A device is attached to the port.
+ */
+ struct cvmx_usbcx_hprt_s {
+ __BITFIELD_FIELD(uint32_t reserved_19_31 : 13,
+ __BITFIELD_FIELD(uint32_t prtspd : 2,
+ __BITFIELD_FIELD(uint32_t prttstctl : 4,
+ __BITFIELD_FIELD(uint32_t prtpwr : 1,
+ __BITFIELD_FIELD(uint32_t prtlnsts : 2,
+ __BITFIELD_FIELD(uint32_t reserved_9_9 : 1,
+ __BITFIELD_FIELD(uint32_t prtrst : 1,
+ __BITFIELD_FIELD(uint32_t prtsusp : 1,
+ __BITFIELD_FIELD(uint32_t prtres : 1,
+ __BITFIELD_FIELD(uint32_t prtovrcurrchng : 1,
+ __BITFIELD_FIELD(uint32_t prtovrcurract : 1,
+ __BITFIELD_FIELD(uint32_t prtenchng : 1,
+ __BITFIELD_FIELD(uint32_t prtena : 1,
+ __BITFIELD_FIELD(uint32_t prtconndet : 1,
+ __BITFIELD_FIELD(uint32_t prtconnsts : 1,
+ ;)))))))))))))))
+ } s;
+};
+
+/**
+ * cvmx_usbc#_hptxfsiz
+ *
+ * Host Periodic Transmit FIFO Size Register (HPTXFSIZ)
+ *
+ * This register holds the size and the memory start address of the Periodic
+ * TxFIFO, as shown in Figures 310 and 311.
+ */
+union cvmx_usbcx_hptxfsiz {
+ uint32_t u32;
+ /**
+ * struct cvmx_usbcx_hptxfsiz_s
+ * @ptxfsize: Host Periodic TxFIFO Depth (PTxFSize)
+ * This value is in terms of 32-bit words.
+ * * Minimum value is 16
+ * * Maximum value is 32768
+ * @ptxfstaddr: Host Periodic TxFIFO Start Address (PTxFStAddr)
+ */
+ struct cvmx_usbcx_hptxfsiz_s {
+ __BITFIELD_FIELD(uint32_t ptxfsize : 16,
+ __BITFIELD_FIELD(uint32_t ptxfstaddr : 16,
+ ;))
+ } s;
+};
+
+/**
+ * cvmx_usbc#_hptxsts
+ *
+ * Host Periodic Transmit FIFO/Queue Status Register (HPTXSTS)
+ *
+ * This read-only register contains the free space information for the Periodic
+ * TxFIFO and the Periodic Transmit Request Queue
+ */
+union cvmx_usbcx_hptxsts {
+ uint32_t u32;
+ /**
+ * struct cvmx_usbcx_hptxsts_s
+ * @ptxqtop: Top of the Periodic Transmit Request Queue (PTxQTop)
+ * This indicates the entry in the Periodic Tx Request Queue that
+ * is currently being processes by the MAC.
+ * This register is used for debugging.
+ * * Bit [31]: Odd/Even (micro)frame
+ * - 1'b0: send in even (micro)frame
+ * - 1'b1: send in odd (micro)frame
+ * * Bits [30:27]: Channel/endpoint number
+ * * Bits [26:25]: Type
+ * - 2'b00: IN/OUT
+ * - 2'b01: Zero-length packet
+ * - 2'b10: CSPLIT
+ * - 2'b11: Disable channel command
+ * * Bit [24]: Terminate (last entry for the selected
+ * channel/endpoint)
+ * @ptxqspcavail: Periodic Transmit Request Queue Space Available
+ * (PTxQSpcAvail)
+ * Indicates the number of free locations available to be written
+ * in the Periodic Transmit Request Queue. This queue holds both
+ * IN and OUT requests.
+ * * 8'h0: Periodic Transmit Request Queue is full
+ * * 8'h1: 1 location available
+ * * 8'h2: 2 locations available
+ * * n: n locations available (0..8)
+ * * Others: Reserved
+ * @ptxfspcavail: Periodic Transmit Data FIFO Space Available
+ * (PTxFSpcAvail)
+ * Indicates the number of free locations available to be written
+ * to in the Periodic TxFIFO.
+ * Values are in terms of 32-bit words
+ * * 16'h0: Periodic TxFIFO is full
+ * * 16'h1: 1 word available
+ * * 16'h2: 2 words available
+ * * 16'hn: n words available (where 0..32768)
+ * * 16'h8000: 32768 words available
+ * * Others: Reserved
+ */
+ struct cvmx_usbcx_hptxsts_s {
+ __BITFIELD_FIELD(uint32_t ptxqtop : 8,
+ __BITFIELD_FIELD(uint32_t ptxqspcavail : 8,
+ __BITFIELD_FIELD(uint32_t ptxfspcavail : 16,
+ ;)))
+ } s;
+};
+
+/**
+ * cvmx_usbn#_clk_ctl
+ *
+ * USBN_CLK_CTL = USBN's Clock Control
+ *
+ * This register is used to control the frequency of the hclk and the
+ * hreset and phy_rst signals.
+ */
+union cvmx_usbnx_clk_ctl {
+ uint64_t u64;
+ /**
+ * struct cvmx_usbnx_clk_ctl_s
+ * @divide2: The 'hclk' used by the USB subsystem is derived
+ * from the eclk.
+ * Also see the field DIVIDE. DIVIDE2<1> must currently
+ * be zero because it is not implemented, so the maximum
+ * ratio of eclk/hclk is currently 16.
+ * The actual divide number for hclk is:
+ * (DIVIDE2 + 1) * (DIVIDE + 1)
+ * @hclk_rst: When this field is '0' the HCLK-DIVIDER used to
+ * generate the hclk in the USB Subsystem is held
+ * in reset. This bit must be set to '0' before
+ * changing the value os DIVIDE in this register.
+ * The reset to the HCLK_DIVIDERis also asserted
+ * when core reset is asserted.
+ * @p_x_on: Force USB-PHY on during suspend.
+ * '1' USB-PHY XO block is powered-down during
+ * suspend.
+ * '0' USB-PHY XO block is powered-up during
+ * suspend.
+ * The value of this field must be set while POR is
+ * active.
+ * @p_rtype: PHY reference clock type
+ * On CN50XX/CN52XX/CN56XX the values are:
+ * '0' The USB-PHY uses a 12MHz crystal as a clock source
+ * at the USB_XO and USB_XI pins.
+ * '1' Reserved.
+ * '2' The USB_PHY uses 12/24/48MHz 2.5V board clock at the
+ * USB_XO pin. USB_XI should be tied to ground in this
+ * case.
+ * '3' Reserved.
+ * On CN3xxx bits 14 and 15 are p_xenbn and p_rclk and values are:
+ * '0' Reserved.
+ * '1' Reserved.
+ * '2' The PHY PLL uses the XO block output as a reference.
+ * The XO block uses an external clock supplied on the
+ * XO pin. USB_XI should be tied to ground for this
+ * usage.
+ * '3' The XO block uses the clock from a crystal.
+ * @p_com_on: '0' Force USB-PHY XO Bias, Bandgap and PLL to
+ * remain powered in Suspend Mode.
+ * '1' The USB-PHY XO Bias, Bandgap and PLL are
+ * powered down in suspend mode.
+ * The value of this field must be set while POR is
+ * active.
+ * @p_c_sel: Phy clock speed select.
+ * Selects the reference clock / crystal frequency.
+ * '11': Reserved
+ * '10': 48 MHz (reserved when a crystal is used)
+ * '01': 24 MHz (reserved when a crystal is used)
+ * '00': 12 MHz
+ * The value of this field must be set while POR is
+ * active.
+ * NOTE: if a crystal is used as a reference clock,
+ * this field must be set to 12 MHz.
+ * @cdiv_byp: Used to enable the bypass input to the USB_CLK_DIV.
+ * @sd_mode: Scaledown mode for the USBC. Control timing events
+ * in the USBC, for normal operation this must be '0'.
+ * @s_bist: Starts bist on the hclk memories, during the '0'
+ * to '1' transition.
+ * @por: Power On Reset for the PHY.
+ * Resets all the PHYS registers and state machines.
+ * @enable: When '1' allows the generation of the hclk. When
+ * '0' the hclk will not be generated. SEE DIVIDE
+ * field of this register.
+ * @prst: When this field is '0' the reset associated with
+ * the phy_clk functionality in the USB Subsystem is
+ * help in reset. This bit should not be set to '1'
+ * until the time it takes 6 clocks (hclk or phy_clk,
+ * whichever is slower) has passed. Under normal
+ * operation once this bit is set to '1' it should not
+ * be set to '0'.
+ * @hrst: When this field is '0' the reset associated with
+ * the hclk functioanlity in the USB Subsystem is
+ * held in reset.This bit should not be set to '1'
+ * until 12ms after phy_clk is stable. Under normal
+ * operation, once this bit is set to '1' it should
+ * not be set to '0'.
+ * @divide: The frequency of 'hclk' used by the USB subsystem
+ * is the eclk frequency divided by the value of
+ * (DIVIDE2 + 1) * (DIVIDE + 1), also see the field
+ * DIVIDE2 of this register.
+ * The hclk frequency should be less than 125Mhz.
+ * After writing a value to this field the SW should
+ * read the field for the value written.
+ * The ENABLE field of this register should not be set
+ * until AFTER this field is set and then read.
+ */
+ struct cvmx_usbnx_clk_ctl_s {
+ __BITFIELD_FIELD(uint64_t reserved_20_63 : 44,
+ __BITFIELD_FIELD(uint64_t divide2 : 2,
+ __BITFIELD_FIELD(uint64_t hclk_rst : 1,
+ __BITFIELD_FIELD(uint64_t p_x_on : 1,
+ __BITFIELD_FIELD(uint64_t p_rtype : 2,
+ __BITFIELD_FIELD(uint64_t p_com_on : 1,
+ __BITFIELD_FIELD(uint64_t p_c_sel : 2,
+ __BITFIELD_FIELD(uint64_t cdiv_byp : 1,
+ __BITFIELD_FIELD(uint64_t sd_mode : 2,
+ __BITFIELD_FIELD(uint64_t s_bist : 1,
+ __BITFIELD_FIELD(uint64_t por : 1,
+ __BITFIELD_FIELD(uint64_t enable : 1,
+ __BITFIELD_FIELD(uint64_t prst : 1,
+ __BITFIELD_FIELD(uint64_t hrst : 1,
+ __BITFIELD_FIELD(uint64_t divide : 3,
+ ;)))))))))))))))
+ } s;
+};
+
+/**
+ * cvmx_usbn#_usbp_ctl_status
+ *
+ * USBN_USBP_CTL_STATUS = USBP Control And Status Register
+ *
+ * Contains general control and status information for the USBN block.
+ */
+union cvmx_usbnx_usbp_ctl_status {
+ uint64_t u64;
+ /**
+ * struct cvmx_usbnx_usbp_ctl_status_s
+ * @txrisetune: HS Transmitter Rise/Fall Time Adjustment
+ * @txvreftune: HS DC Voltage Level Adjustment
+ * @txfslstune: FS/LS Source Impedence Adjustment
+ * @txhsxvtune: Transmitter High-Speed Crossover Adjustment
+ * @sqrxtune: Squelch Threshold Adjustment
+ * @compdistune: Disconnect Threshold Adjustment
+ * @otgtune: VBUS Valid Threshold Adjustment
+ * @otgdisable: OTG Block Disable
+ * @portreset: Per_Port Reset
+ * @drvvbus: Drive VBUS
+ * @lsbist: Low-Speed BIST Enable.
+ * @fsbist: Full-Speed BIST Enable.
+ * @hsbist: High-Speed BIST Enable.
+ * @bist_done: PHY Bist Done.
+ * Asserted at the end of the PHY BIST sequence.
+ * @bist_err: PHY Bist Error.
+ * Indicates an internal error was detected during
+ * the BIST sequence.
+ * @tdata_out: PHY Test Data Out.
+ * Presents either internaly generated signals or
+ * test register contents, based upon the value of
+ * test_data_out_sel.
+ * @siddq: Drives the USBP (USB-PHY) SIDDQ input.
+ * Normally should be set to zero.
+ * When customers have no intent to use USB PHY
+ * interface, they should:
+ * - still provide 3.3V to USB_VDD33, and
+ * - tie USB_REXT to 3.3V supply, and
+ * - set USBN*_USBP_CTL_STATUS[SIDDQ]=1
+ * @txpreemphasistune: HS Transmitter Pre-Emphasis Enable
+ * @dma_bmode: When set to 1 the L2C DMA address will be updated
+ * with byte-counts between packets. When set to 0
+ * the L2C DMA address is incremented to the next
+ * 4-byte aligned address after adding byte-count.
+ * @usbc_end: Bigendian input to the USB Core. This should be
+ * set to '0' for operation.
+ * @usbp_bist: PHY, This is cleared '0' to run BIST on the USBP.
+ * @tclk: PHY Test Clock, used to load TDATA_IN to the USBP.
+ * @dp_pulld: PHY DP_PULLDOWN input to the USB-PHY.
+ * This signal enables the pull-down resistance on
+ * the D+ line. '1' pull down-resistance is connected
+ * to D+/ '0' pull down resistance is not connected
+ * to D+. When an A/B device is acting as a host
+ * (downstream-facing port), dp_pulldown and
+ * dm_pulldown are enabled. This must not toggle
+ * during normal opeartion.
+ * @dm_pulld: PHY DM_PULLDOWN input to the USB-PHY.
+ * This signal enables the pull-down resistance on
+ * the D- line. '1' pull down-resistance is connected
+ * to D-. '0' pull down resistance is not connected
+ * to D-. When an A/B device is acting as a host
+ * (downstream-facing port), dp_pulldown and
+ * dm_pulldown are enabled. This must not toggle
+ * during normal opeartion.
+ * @hst_mode: When '0' the USB is acting as HOST, when '1'
+ * USB is acting as device. This field needs to be
+ * set while the USB is in reset.
+ * @tuning: Transmitter Tuning for High-Speed Operation.
+ * Tunes the current supply and rise/fall output
+ * times for high-speed operation.
+ * [20:19] == 11: Current supply increased
+ * approximately 9%
+ * [20:19] == 10: Current supply increased
+ * approximately 4.5%
+ * [20:19] == 01: Design default.
+ * [20:19] == 00: Current supply decreased
+ * approximately 4.5%
+ * [22:21] == 11: Rise and fall times are increased.
+ * [22:21] == 10: Design default.
+ * [22:21] == 01: Rise and fall times are decreased.
+ * [22:21] == 00: Rise and fall times are decreased
+ * further as compared to the 01 setting.
+ * @tx_bs_enh: Transmit Bit Stuffing on [15:8].
+ * Enables or disables bit stuffing on data[15:8]
+ * when bit-stuffing is enabled.
+ * @tx_bs_en: Transmit Bit Stuffing on [7:0].
+ * Enables or disables bit stuffing on data[7:0]
+ * when bit-stuffing is enabled.
+ * @loop_enb: PHY Loopback Test Enable.
+ * '1': During data transmission the receive is
+ * enabled.
+ * '0': During data transmission the receive is
+ * disabled.
+ * Must be '0' for normal operation.
+ * @vtest_enb: Analog Test Pin Enable.
+ * '1' The PHY's analog_test pin is enabled for the
+ * input and output of applicable analog test signals.
+ * '0' THe analog_test pin is disabled.
+ * @bist_enb: Built-In Self Test Enable.
+ * Used to activate BIST in the PHY.
+ * @tdata_sel: Test Data Out Select.
+ * '1' test_data_out[3:0] (PHY) register contents
+ * are output. '0' internaly generated signals are
+ * output.
+ * @taddr_in: Mode Address for Test Interface.
+ * Specifies the register address for writing to or
+ * reading from the PHY test interface register.
+ * @tdata_in: Internal Testing Register Input Data and Select
+ * This is a test bus. Data is present on [3:0],
+ * and its corresponding select (enable) is present
+ * on bits [7:4].
+ * @ate_reset: Reset input from automatic test equipment.
+ * This is a test signal. When the USB Core is
+ * powered up (not in Susned Mode), an automatic
+ * tester can use this to disable phy_clock and
+ * free_clk, then re-eanable them with an aligned
+ * phase.
+ * '1': The phy_clk and free_clk outputs are
+ * disabled. "0": The phy_clock and free_clk outputs
+ * are available within a specific period after the
+ * de-assertion.
+ */
+ struct cvmx_usbnx_usbp_ctl_status_s {
+ __BITFIELD_FIELD(uint64_t txrisetune : 1,
+ __BITFIELD_FIELD(uint64_t txvreftune : 4,
+ __BITFIELD_FIELD(uint64_t txfslstune : 4,
+ __BITFIELD_FIELD(uint64_t txhsxvtune : 2,
+ __BITFIELD_FIELD(uint64_t sqrxtune : 3,
+ __BITFIELD_FIELD(uint64_t compdistune : 3,
+ __BITFIELD_FIELD(uint64_t otgtune : 3,
+ __BITFIELD_FIELD(uint64_t otgdisable : 1,
+ __BITFIELD_FIELD(uint64_t portreset : 1,
+ __BITFIELD_FIELD(uint64_t drvvbus : 1,
+ __BITFIELD_FIELD(uint64_t lsbist : 1,
+ __BITFIELD_FIELD(uint64_t fsbist : 1,
+ __BITFIELD_FIELD(uint64_t hsbist : 1,
+ __BITFIELD_FIELD(uint64_t bist_done : 1,
+ __BITFIELD_FIELD(uint64_t bist_err : 1,
+ __BITFIELD_FIELD(uint64_t tdata_out : 4,
+ __BITFIELD_FIELD(uint64_t siddq : 1,
+ __BITFIELD_FIELD(uint64_t txpreemphasistune : 1,
+ __BITFIELD_FIELD(uint64_t dma_bmode : 1,
+ __BITFIELD_FIELD(uint64_t usbc_end : 1,
+ __BITFIELD_FIELD(uint64_t usbp_bist : 1,
+ __BITFIELD_FIELD(uint64_t tclk : 1,
+ __BITFIELD_FIELD(uint64_t dp_pulld : 1,
+ __BITFIELD_FIELD(uint64_t dm_pulld : 1,
+ __BITFIELD_FIELD(uint64_t hst_mode : 1,
+ __BITFIELD_FIELD(uint64_t tuning : 4,
+ __BITFIELD_FIELD(uint64_t tx_bs_enh : 1,
+ __BITFIELD_FIELD(uint64_t tx_bs_en : 1,
+ __BITFIELD_FIELD(uint64_t loop_enb : 1,
+ __BITFIELD_FIELD(uint64_t vtest_enb : 1,
+ __BITFIELD_FIELD(uint64_t bist_enb : 1,
+ __BITFIELD_FIELD(uint64_t tdata_sel : 1,
+ __BITFIELD_FIELD(uint64_t taddr_in : 4,
+ __BITFIELD_FIELD(uint64_t tdata_in : 8,
+ __BITFIELD_FIELD(uint64_t ate_reset : 1,
+ ;)))))))))))))))))))))))))))))))))))
+ } s;
+};
+
+#endif /* __OCTEON_HCD_H__ */
diff --git a/drivers/staging/octeon/Kconfig b/drivers/staging/octeon/Kconfig
new file mode 100644
index 000000000..6e1d5f8d3
--- /dev/null
+++ b/drivers/staging/octeon/Kconfig
@@ -0,0 +1,13 @@
+config OCTEON_ETHERNET
+ tristate "Cavium Networks Octeon Ethernet support"
+ depends on CAVIUM_OCTEON_SOC && NETDEVICES
+ select PHYLIB
+ select MDIO_OCTEON
+ help
+ This driver supports the builtin ethernet ports on Cavium
+ Networks' products in the Octeon family. This driver supports the
+ CN3XXX and CN5XXX Octeon processors.
+
+ To compile this driver as a module, choose M here. The module
+ will be called octeon-ethernet.
+
diff --git a/drivers/staging/octeon/Makefile b/drivers/staging/octeon/Makefile
new file mode 100644
index 000000000..9012dee0c
--- /dev/null
+++ b/drivers/staging/octeon/Makefile
@@ -0,0 +1,23 @@
+# This file is subject to the terms and conditions of the GNU General Public
+# License. See the file "COPYING" in the main directory of this archive
+# for more details.
+#
+# Copyright (C) 2005-2009 Cavium Networks
+#
+
+#
+# Makefile for Cavium OCTEON on-board ethernet driver
+#
+
+obj-${CONFIG_OCTEON_ETHERNET} := octeon-ethernet.o
+
+octeon-ethernet-y := ethernet.o
+octeon-ethernet-y += ethernet-mdio.o
+octeon-ethernet-y += ethernet-mem.o
+octeon-ethernet-y += ethernet-rgmii.o
+octeon-ethernet-y += ethernet-rx.o
+octeon-ethernet-y += ethernet-sgmii.o
+octeon-ethernet-y += ethernet-spi.o
+octeon-ethernet-y += ethernet-tx.o
+octeon-ethernet-y += ethernet-xaui.o
+
diff --git a/drivers/staging/octeon/ethernet-defines.h b/drivers/staging/octeon/ethernet-defines.h
new file mode 100644
index 000000000..2a98a2153
--- /dev/null
+++ b/drivers/staging/octeon/ethernet-defines.h
@@ -0,0 +1,102 @@
+/**********************************************************************
+ * Author: Cavium Networks
+ *
+ * Contact: support@caviumnetworks.com
+ * This file is part of the OCTEON SDK
+ *
+ * Copyright (c) 2003-2007 Cavium Networks
+ *
+ * This file 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.
+ *
+ * This file is distributed in the hope that it will be useful, but
+ * AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or
+ * NONINFRINGEMENT. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this file; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ * or visit http://www.gnu.org/licenses/.
+ *
+ * This file may also be available under a different license from Cavium.
+ * Contact Cavium Networks for more information
+**********************************************************************/
+
+/*
+ * A few defines are used to control the operation of this driver:
+ * CONFIG_CAVIUM_RESERVE32
+ * This kernel config options controls the amount of memory configured
+ * in a wired TLB entry for all processes to share. If this is set, the
+ * driver will use this memory instead of kernel memory for pools. This
+ * allows 32bit userspace application to access the buffers, but also
+ * requires all received packets to be copied.
+ * USE_SKBUFFS_IN_HW
+ * Tells the driver to populate the packet buffers with kernel skbuffs.
+ * This allows the driver to receive packets without copying them. It also
+ * means that 32bit userspace can't access the packet buffers.
+ * USE_HW_TCPUDP_CHECKSUM
+ * Controls if the Octeon TCP/UDP checksum engine is used for packet
+ * output. If this is zero, the kernel will perform the checksum in
+ * software.
+ * USE_ASYNC_IOBDMA
+ * Use asynchronous IO access to hardware. This uses Octeon's asynchronous
+ * IOBDMAs to issue IO accesses without stalling. Set this to zero
+ * to disable this. Note that IOBDMAs require CVMSEG.
+ * REUSE_SKBUFFS_WITHOUT_FREE
+ * Allows the TX path to free an skbuff into the FPA hardware pool. This
+ * can significantly improve performance for forwarding and bridging, but
+ * may be somewhat dangerous. Checks are made, but if any buffer is reused
+ * without the proper Linux cleanup, the networking stack may have very
+ * bizarre bugs.
+ */
+#ifndef __ETHERNET_DEFINES_H__
+#define __ETHERNET_DEFINES_H__
+
+#include <asm/octeon/cvmx-config.h>
+
+
+#define OCTEON_ETHERNET_VERSION "1.9"
+
+#ifndef CONFIG_CAVIUM_RESERVE32
+#define CONFIG_CAVIUM_RESERVE32 0
+#endif
+
+#define USE_SKBUFFS_IN_HW 1
+#ifdef CONFIG_NETFILTER
+#define REUSE_SKBUFFS_WITHOUT_FREE 0
+#else
+#define REUSE_SKBUFFS_WITHOUT_FREE 1
+#endif
+
+#define USE_HW_TCPUDP_CHECKSUM 1
+
+/* Enable Random Early Dropping under load */
+#define USE_RED 1
+#define USE_ASYNC_IOBDMA (CONFIG_CAVIUM_OCTEON_CVMSEG_SIZE > 0)
+
+/*
+ * Allow SW based preamble removal at 10Mbps to workaround PHYs giving
+ * us bad preambles.
+ */
+#define USE_10MBPS_PREAMBLE_WORKAROUND 1
+/*
+ * Use this to have all FPA frees also tell the L2 not to write data
+ * to memory.
+ */
+#define DONT_WRITEBACK(x) (x)
+/* Use this to not have FPA frees control L2 */
+/*#define DONT_WRITEBACK(x) 0 */
+
+/* Maximum number of SKBs to try to free per xmit packet. */
+#define MAX_OUT_QUEUE_DEPTH 1000
+
+#define FAU_TOTAL_TX_TO_CLEAN (CVMX_FAU_REG_END - sizeof(uint32_t))
+#define FAU_NUM_PACKET_BUFFERS_TO_FREE (FAU_TOTAL_TX_TO_CLEAN - sizeof(uint32_t))
+
+#define TOTAL_NUMBER_OF_PORTS (CVMX_PIP_NUM_INPUT_PORTS+1)
+
+
+#endif /* __ETHERNET_DEFINES_H__ */
diff --git a/drivers/staging/octeon/ethernet-mdio.c b/drivers/staging/octeon/ethernet-mdio.c
new file mode 100644
index 000000000..40dab11e5
--- /dev/null
+++ b/drivers/staging/octeon/ethernet-mdio.c
@@ -0,0 +1,206 @@
+/**********************************************************************
+ * Author: Cavium Networks
+ *
+ * Contact: support@caviumnetworks.com
+ * This file is part of the OCTEON SDK
+ *
+ * Copyright (c) 2003-2007 Cavium Networks
+ *
+ * This file 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.
+ *
+ * This file is distributed in the hope that it will be useful, but
+ * AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or
+ * NONINFRINGEMENT. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this file; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ * or visit http://www.gnu.org/licenses/.
+ *
+ * This file may also be available under a different license from Cavium.
+ * Contact Cavium Networks for more information
+**********************************************************************/
+#include <linux/kernel.h>
+#include <linux/ethtool.h>
+#include <linux/phy.h>
+#include <linux/ratelimit.h>
+#include <linux/of_mdio.h>
+
+#include <net/dst.h>
+
+#include <asm/octeon/octeon.h>
+
+#include "ethernet-defines.h"
+#include "octeon-ethernet.h"
+#include "ethernet-mdio.h"
+#include "ethernet-util.h"
+
+#include <asm/octeon/cvmx-helper-board.h>
+
+#include <asm/octeon/cvmx-smix-defs.h>
+
+static void cvm_oct_get_drvinfo(struct net_device *dev,
+ struct ethtool_drvinfo *info)
+{
+ strlcpy(info->driver, "cavium-ethernet", sizeof(info->driver));
+ strlcpy(info->version, OCTEON_ETHERNET_VERSION, sizeof(info->version));
+ strlcpy(info->bus_info, "Builtin", sizeof(info->bus_info));
+}
+
+static int cvm_oct_get_settings(struct net_device *dev, struct ethtool_cmd *cmd)
+{
+ struct octeon_ethernet *priv = netdev_priv(dev);
+
+ if (priv->phydev)
+ return phy_ethtool_gset(priv->phydev, cmd);
+
+ return -EINVAL;
+}
+
+static int cvm_oct_set_settings(struct net_device *dev, struct ethtool_cmd *cmd)
+{
+ struct octeon_ethernet *priv = netdev_priv(dev);
+
+ if (!capable(CAP_NET_ADMIN))
+ return -EPERM;
+
+ if (priv->phydev)
+ return phy_ethtool_sset(priv->phydev, cmd);
+
+ return -EINVAL;
+}
+
+static int cvm_oct_nway_reset(struct net_device *dev)
+{
+ struct octeon_ethernet *priv = netdev_priv(dev);
+
+ if (!capable(CAP_NET_ADMIN))
+ return -EPERM;
+
+ if (priv->phydev)
+ return phy_start_aneg(priv->phydev);
+
+ return -EINVAL;
+}
+
+const struct ethtool_ops cvm_oct_ethtool_ops = {
+ .get_drvinfo = cvm_oct_get_drvinfo,
+ .get_settings = cvm_oct_get_settings,
+ .set_settings = cvm_oct_set_settings,
+ .nway_reset = cvm_oct_nway_reset,
+ .get_link = ethtool_op_get_link,
+};
+
+/**
+ * cvm_oct_ioctl - IOCTL support for PHY control
+ * @dev: Device to change
+ * @rq: the request
+ * @cmd: the command
+ *
+ * Returns Zero on success
+ */
+int cvm_oct_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
+{
+ struct octeon_ethernet *priv = netdev_priv(dev);
+
+ if (!netif_running(dev))
+ return -EINVAL;
+
+ if (!priv->phydev)
+ return -EINVAL;
+
+ return phy_mii_ioctl(priv->phydev, rq, cmd);
+}
+
+static void cvm_oct_note_carrier(struct octeon_ethernet *priv,
+ cvmx_helper_link_info_t li)
+{
+ if (li.s.link_up) {
+ pr_notice_ratelimited("%s: %u Mbps %s duplex, port %d\n",
+ netdev_name(priv->netdev), li.s.speed,
+ (li.s.full_duplex) ? "Full" : "Half",
+ priv->port);
+ } else {
+ pr_notice_ratelimited("%s: Link down\n",
+ netdev_name(priv->netdev));
+ }
+}
+
+void cvm_oct_adjust_link(struct net_device *dev)
+{
+ struct octeon_ethernet *priv = netdev_priv(dev);
+ cvmx_helper_link_info_t link_info;
+
+ if (priv->last_link != priv->phydev->link) {
+ priv->last_link = priv->phydev->link;
+ link_info.u64 = 0;
+ link_info.s.link_up = priv->last_link ? 1 : 0;
+ link_info.s.full_duplex = priv->phydev->duplex ? 1 : 0;
+ link_info.s.speed = priv->phydev->speed;
+
+ cvmx_helper_link_set(priv->port, link_info);
+ cvm_oct_note_carrier(priv, link_info);
+ }
+}
+
+int cvm_oct_common_stop(struct net_device *dev)
+{
+ struct octeon_ethernet *priv = netdev_priv(dev);
+ cvmx_helper_link_info_t link_info;
+
+ priv->poll = NULL;
+
+ if (priv->phydev)
+ phy_disconnect(priv->phydev);
+ priv->phydev = NULL;
+
+ if (priv->last_link) {
+ link_info.u64 = 0;
+ priv->last_link = 0;
+
+ cvmx_helper_link_set(priv->port, link_info);
+ cvm_oct_note_carrier(priv, link_info);
+ }
+ return 0;
+}
+
+/**
+ * cvm_oct_phy_setup_device - setup the PHY
+ *
+ * @dev: Device to setup
+ *
+ * Returns Zero on success, negative on failure
+ */
+int cvm_oct_phy_setup_device(struct net_device *dev)
+{
+ struct octeon_ethernet *priv = netdev_priv(dev);
+ struct device_node *phy_node;
+
+ if (!priv->of_node)
+ goto no_phy;
+
+ phy_node = of_parse_phandle(priv->of_node, "phy-handle", 0);
+ if (!phy_node)
+ goto no_phy;
+
+ priv->phydev = of_phy_connect(dev, phy_node, cvm_oct_adjust_link, 0,
+ PHY_INTERFACE_MODE_GMII);
+
+ if (priv->phydev == NULL)
+ return -ENODEV;
+
+ priv->last_link = 0;
+ phy_start_aneg(priv->phydev);
+
+ return 0;
+no_phy:
+ /* If there is no phy, assume a direct MAC connection and that
+ * the link is up.
+ */
+ netif_carrier_on(dev);
+ return 0;
+}
diff --git a/drivers/staging/octeon/ethernet-mdio.h b/drivers/staging/octeon/ethernet-mdio.h
new file mode 100644
index 000000000..6191b0850
--- /dev/null
+++ b/drivers/staging/octeon/ethernet-mdio.h
@@ -0,0 +1,47 @@
+/*********************************************************************
+ * Author: Cavium Networks
+ *
+ * Contact: support@caviumnetworks.com
+ * This file is part of the OCTEON SDK
+ *
+ * Copyright (c) 2003-2007 Cavium Networks
+ *
+ * This file 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.
+ *
+ * This file is distributed in the hope that it will be useful, but
+ * AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or
+ * NONINFRINGEMENT. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this file; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ * or visit http://www.gnu.org/licenses/.
+ *
+ * This file may also be available under a different license from Cavium.
+ * Contact Cavium Networks for more information
+*********************************************************************/
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/ip.h>
+#include <linux/string.h>
+#include <linux/ethtool.h>
+#include <linux/seq_file.h>
+#include <linux/proc_fs.h>
+#include <net/dst.h>
+#ifdef CONFIG_XFRM
+#include <linux/xfrm.h>
+#include <net/xfrm.h>
+#endif /* CONFIG_XFRM */
+
+extern const struct ethtool_ops cvm_oct_ethtool_ops;
+
+extern void octeon_mdiobus_force_mod_depencency(void);
+
+int cvm_oct_ioctl(struct net_device *dev, struct ifreq *rq, int cmd);
+int cvm_oct_phy_setup_device(struct net_device *dev);
diff --git a/drivers/staging/octeon/ethernet-mem.c b/drivers/staging/octeon/ethernet-mem.c
new file mode 100644
index 000000000..964da860f
--- /dev/null
+++ b/drivers/staging/octeon/ethernet-mem.c
@@ -0,0 +1,176 @@
+/**********************************************************************
+ * Author: Cavium Networks
+ *
+ * Contact: support@caviumnetworks.com
+ * This file is part of the OCTEON SDK
+ *
+ * Copyright (c) 2003-2010 Cavium Networks
+ *
+ * This file 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.
+ *
+ * This file is distributed in the hope that it will be useful, but
+ * AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or
+ * NONINFRINGEMENT. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this file; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ * or visit http://www.gnu.org/licenses/.
+ *
+ * This file may also be available under a different license from Cavium.
+ * Contact Cavium Networks for more information
+**********************************************************************/
+#include <linux/kernel.h>
+#include <linux/netdevice.h>
+#include <linux/slab.h>
+
+#include <asm/octeon/octeon.h>
+
+#include "ethernet-mem.h"
+#include "ethernet-defines.h"
+
+#include <asm/octeon/cvmx-fpa.h>
+
+/**
+ * cvm_oct_fill_hw_skbuff - fill the supplied hardware pool with skbuffs
+ * @pool: Pool to allocate an skbuff for
+ * @size: Size of the buffer needed for the pool
+ * @elements: Number of buffers to allocate
+ *
+ * Returns the actual number of buffers allocated.
+ */
+static int cvm_oct_fill_hw_skbuff(int pool, int size, int elements)
+{
+ int freed = elements;
+
+ while (freed) {
+ struct sk_buff *skb = dev_alloc_skb(size + 256);
+
+ if (unlikely(skb == NULL))
+ break;
+ skb_reserve(skb, 256 - (((unsigned long)skb->data) & 0x7f));
+ *(struct sk_buff **)(skb->data - sizeof(void *)) = skb;
+ cvmx_fpa_free(skb->data, pool, DONT_WRITEBACK(size / 128));
+ freed--;
+ }
+ return elements - freed;
+}
+
+/**
+ * cvm_oct_free_hw_skbuff- free hardware pool skbuffs
+ * @pool: Pool to allocate an skbuff for
+ * @size: Size of the buffer needed for the pool
+ * @elements: Number of buffers to allocate
+ */
+static void cvm_oct_free_hw_skbuff(int pool, int size, int elements)
+{
+ char *memory;
+
+ do {
+ memory = cvmx_fpa_alloc(pool);
+ if (memory) {
+ struct sk_buff *skb =
+ *(struct sk_buff **)(memory - sizeof(void *));
+ elements--;
+ dev_kfree_skb(skb);
+ }
+ } while (memory);
+
+ if (elements < 0)
+ pr_warn("Freeing of pool %u had too many skbuffs (%d)\n",
+ pool, elements);
+ else if (elements > 0)
+ pr_warn("Freeing of pool %u is missing %d skbuffs\n",
+ pool, elements);
+}
+
+/**
+ * cvm_oct_fill_hw_memory - fill a hardware pool with memory.
+ * @pool: Pool to populate
+ * @size: Size of each buffer in the pool
+ * @elements: Number of buffers to allocate
+ *
+ * Returns the actual number of buffers allocated.
+ */
+static int cvm_oct_fill_hw_memory(int pool, int size, int elements)
+{
+ char *memory;
+ char *fpa;
+ int freed = elements;
+
+ while (freed) {
+ /*
+ * FPA memory must be 128 byte aligned. Since we are
+ * aligning we need to save the original pointer so we
+ * can feed it to kfree when the memory is returned to
+ * the kernel.
+ *
+ * We allocate an extra 256 bytes to allow for
+ * alignment and space for the original pointer saved
+ * just before the block.
+ */
+ memory = kmalloc(size + 256, GFP_ATOMIC);
+ if (unlikely(memory == NULL)) {
+ pr_warn("Unable to allocate %u bytes for FPA pool %d\n",
+ elements * size, pool);
+ break;
+ }
+ fpa = (char *)(((unsigned long)memory + 256) & ~0x7fUL);
+ *((char **)fpa - 1) = memory;
+ cvmx_fpa_free(fpa, pool, 0);
+ freed--;
+ }
+ return elements - freed;
+}
+
+/**
+ * cvm_oct_free_hw_memory - Free memory allocated by cvm_oct_fill_hw_memory
+ * @pool: FPA pool to free
+ * @size: Size of each buffer in the pool
+ * @elements: Number of buffers that should be in the pool
+ */
+static void cvm_oct_free_hw_memory(int pool, int size, int elements)
+{
+ char *memory;
+ char *fpa;
+
+ do {
+ fpa = cvmx_fpa_alloc(pool);
+ if (fpa) {
+ elements--;
+ fpa = (char *)phys_to_virt(cvmx_ptr_to_phys(fpa));
+ memory = *((char **)fpa - 1);
+ kfree(memory);
+ }
+ } while (fpa);
+
+ if (elements < 0)
+ pr_warn("Freeing of pool %u had too many buffers (%d)\n",
+ pool, elements);
+ else if (elements > 0)
+ pr_warn("Warning: Freeing of pool %u is missing %d buffers\n",
+ pool, elements);
+}
+
+int cvm_oct_mem_fill_fpa(int pool, int size, int elements)
+{
+ int freed;
+
+ if (USE_SKBUFFS_IN_HW && pool == CVMX_FPA_PACKET_POOL)
+ freed = cvm_oct_fill_hw_skbuff(pool, size, elements);
+ else
+ freed = cvm_oct_fill_hw_memory(pool, size, elements);
+ return freed;
+}
+
+void cvm_oct_mem_empty_fpa(int pool, int size, int elements)
+{
+ if (USE_SKBUFFS_IN_HW && pool == CVMX_FPA_PACKET_POOL)
+ cvm_oct_free_hw_skbuff(pool, size, elements);
+ else
+ cvm_oct_free_hw_memory(pool, size, elements);
+}
diff --git a/drivers/staging/octeon/ethernet-mem.h b/drivers/staging/octeon/ethernet-mem.h
new file mode 100644
index 000000000..713f2edc8
--- /dev/null
+++ b/drivers/staging/octeon/ethernet-mem.h
@@ -0,0 +1,29 @@
+/*********************************************************************
+ * Author: Cavium Networks
+ *
+ * Contact: support@caviumnetworks.com
+ * This file is part of the OCTEON SDK
+ *
+ * Copyright (c) 2003-2007 Cavium Networks
+ *
+ * This file 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.
+ *
+ * This file is distributed in the hope that it will be useful, but
+ * AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or
+ * NONINFRINGEMENT. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this file; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ * or visit http://www.gnu.org/licenses/.
+ *
+ * This file may also be available under a different license from Cavium.
+ * Contact Cavium Networks for more information
+********************************************************************/
+
+int cvm_oct_mem_fill_fpa(int pool, int size, int elements);
+void cvm_oct_mem_empty_fpa(int pool, int size, int elements);
diff --git a/drivers/staging/octeon/ethernet-rgmii.c b/drivers/staging/octeon/ethernet-rgmii.c
new file mode 100644
index 000000000..e36f9bc69
--- /dev/null
+++ b/drivers/staging/octeon/ethernet-rgmii.c
@@ -0,0 +1,448 @@
+/*********************************************************************
+ * Author: Cavium Networks
+ *
+ * Contact: support@caviumnetworks.com
+ * This file is part of the OCTEON SDK
+ *
+ * Copyright (c) 2003-2007 Cavium Networks
+ *
+ * This file 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.
+ *
+ * This file is distributed in the hope that it will be useful, but
+ * AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or
+ * NONINFRINGEMENT. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this file; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ * or visit http://www.gnu.org/licenses/.
+ *
+ * This file may also be available under a different license from Cavium.
+ * Contact Cavium Networks for more information
+**********************************************************************/
+#include <linux/kernel.h>
+#include <linux/netdevice.h>
+#include <linux/interrupt.h>
+#include <linux/phy.h>
+#include <linux/ratelimit.h>
+#include <net/dst.h>
+
+#include <asm/octeon/octeon.h>
+
+#include "ethernet-defines.h"
+#include "octeon-ethernet.h"
+#include "ethernet-util.h"
+#include "ethernet-mdio.h"
+
+#include <asm/octeon/cvmx-helper.h>
+
+#include <asm/octeon/cvmx-ipd-defs.h>
+#include <asm/octeon/cvmx-npi-defs.h>
+#include <asm/octeon/cvmx-gmxx-defs.h>
+
+static DEFINE_SPINLOCK(global_register_lock);
+
+static int number_rgmii_ports;
+
+static void cvm_oct_rgmii_poll(struct net_device *dev)
+{
+ struct octeon_ethernet *priv = netdev_priv(dev);
+ unsigned long flags = 0;
+ cvmx_helper_link_info_t link_info;
+ int use_global_register_lock = (priv->phydev == NULL);
+
+ BUG_ON(in_interrupt());
+ if (use_global_register_lock) {
+ /*
+ * Take the global register lock since we are going to
+ * touch registers that affect more than one port.
+ */
+ spin_lock_irqsave(&global_register_lock, flags);
+ } else {
+ mutex_lock(&priv->phydev->bus->mdio_lock);
+ }
+
+ link_info = cvmx_helper_link_get(priv->port);
+ if (link_info.u64 == priv->link_info) {
+
+ /*
+ * If the 10Mbps preamble workaround is supported and we're
+ * at 10Mbps we may need to do some special checking.
+ */
+ if (USE_10MBPS_PREAMBLE_WORKAROUND &&
+ (link_info.s.speed == 10)) {
+
+ /*
+ * Read the GMXX_RXX_INT_REG[PCTERR] bit and
+ * see if we are getting preamble errors.
+ */
+ int interface = INTERFACE(priv->port);
+ int index = INDEX(priv->port);
+ union cvmx_gmxx_rxx_int_reg gmxx_rxx_int_reg;
+
+ gmxx_rxx_int_reg.u64 =
+ cvmx_read_csr(CVMX_GMXX_RXX_INT_REG
+ (index, interface));
+ if (gmxx_rxx_int_reg.s.pcterr) {
+
+ /*
+ * We are getting preamble errors at
+ * 10Mbps. Most likely the PHY is
+ * giving us packets with mis aligned
+ * preambles. In order to get these
+ * packets we need to disable preamble
+ * checking and do it in software.
+ */
+ union cvmx_gmxx_rxx_frm_ctl gmxx_rxx_frm_ctl;
+ union cvmx_ipd_sub_port_fcs ipd_sub_port_fcs;
+
+ /* Disable preamble checking */
+ gmxx_rxx_frm_ctl.u64 =
+ cvmx_read_csr(CVMX_GMXX_RXX_FRM_CTL
+ (index, interface));
+ gmxx_rxx_frm_ctl.s.pre_chk = 0;
+ cvmx_write_csr(CVMX_GMXX_RXX_FRM_CTL
+ (index, interface),
+ gmxx_rxx_frm_ctl.u64);
+
+ /* Disable FCS stripping */
+ ipd_sub_port_fcs.u64 =
+ cvmx_read_csr(CVMX_IPD_SUB_PORT_FCS);
+ ipd_sub_port_fcs.s.port_bit &=
+ 0xffffffffull ^ (1ull << priv->port);
+ cvmx_write_csr(CVMX_IPD_SUB_PORT_FCS,
+ ipd_sub_port_fcs.u64);
+
+ /* Clear any error bits */
+ cvmx_write_csr(CVMX_GMXX_RXX_INT_REG
+ (index, interface),
+ gmxx_rxx_int_reg.u64);
+ printk_ratelimited("%s: Using 10Mbps with software preamble removal\n",
+ dev->name);
+ }
+ }
+
+ if (use_global_register_lock)
+ spin_unlock_irqrestore(&global_register_lock, flags);
+ else
+ mutex_unlock(&priv->phydev->bus->mdio_lock);
+ return;
+ }
+
+ /* If the 10Mbps preamble workaround is allowed we need to on
+ preamble checking, FCS stripping, and clear error bits on
+ every speed change. If errors occur during 10Mbps operation
+ the above code will change this stuff */
+ if (USE_10MBPS_PREAMBLE_WORKAROUND) {
+
+ union cvmx_gmxx_rxx_frm_ctl gmxx_rxx_frm_ctl;
+ union cvmx_ipd_sub_port_fcs ipd_sub_port_fcs;
+ union cvmx_gmxx_rxx_int_reg gmxx_rxx_int_reg;
+ int interface = INTERFACE(priv->port);
+ int index = INDEX(priv->port);
+
+ /* Enable preamble checking */
+ gmxx_rxx_frm_ctl.u64 =
+ cvmx_read_csr(CVMX_GMXX_RXX_FRM_CTL(index, interface));
+ gmxx_rxx_frm_ctl.s.pre_chk = 1;
+ cvmx_write_csr(CVMX_GMXX_RXX_FRM_CTL(index, interface),
+ gmxx_rxx_frm_ctl.u64);
+ /* Enable FCS stripping */
+ ipd_sub_port_fcs.u64 = cvmx_read_csr(CVMX_IPD_SUB_PORT_FCS);
+ ipd_sub_port_fcs.s.port_bit |= 1ull << priv->port;
+ cvmx_write_csr(CVMX_IPD_SUB_PORT_FCS, ipd_sub_port_fcs.u64);
+ /* Clear any error bits */
+ gmxx_rxx_int_reg.u64 =
+ cvmx_read_csr(CVMX_GMXX_RXX_INT_REG(index, interface));
+ cvmx_write_csr(CVMX_GMXX_RXX_INT_REG(index, interface),
+ gmxx_rxx_int_reg.u64);
+ }
+ if (priv->phydev == NULL) {
+ link_info = cvmx_helper_link_autoconf(priv->port);
+ priv->link_info = link_info.u64;
+ }
+
+ if (use_global_register_lock)
+ spin_unlock_irqrestore(&global_register_lock, flags);
+ else
+ mutex_unlock(&priv->phydev->bus->mdio_lock);
+
+ if (priv->phydev == NULL) {
+ /* Tell core. */
+ if (link_info.s.link_up) {
+ if (!netif_carrier_ok(dev))
+ netif_carrier_on(dev);
+ if (priv->queue != -1)
+ printk_ratelimited("%s: %u Mbps %s duplex, port %2d, queue %2d\n",
+ dev->name, link_info.s.speed,
+ (link_info.s.full_duplex) ?
+ "Full" : "Half",
+ priv->port, priv->queue);
+ else
+ printk_ratelimited("%s: %u Mbps %s duplex, port %2d, POW\n",
+ dev->name, link_info.s.speed,
+ (link_info.s.full_duplex) ?
+ "Full" : "Half",
+ priv->port);
+ } else {
+ if (netif_carrier_ok(dev))
+ netif_carrier_off(dev);
+ printk_ratelimited("%s: Link down\n", dev->name);
+ }
+ }
+}
+
+static irqreturn_t cvm_oct_rgmii_rml_interrupt(int cpl, void *dev_id)
+{
+ union cvmx_npi_rsl_int_blocks rsl_int_blocks;
+ int index;
+ irqreturn_t return_status = IRQ_NONE;
+
+ rsl_int_blocks.u64 = cvmx_read_csr(CVMX_NPI_RSL_INT_BLOCKS);
+
+ /* Check and see if this interrupt was caused by the GMX0 block */
+ if (rsl_int_blocks.s.gmx0) {
+
+ int interface = 0;
+ /* Loop through every port of this interface */
+ for (index = 0;
+ index < cvmx_helper_ports_on_interface(interface);
+ index++) {
+
+ /* Read the GMX interrupt status bits */
+ union cvmx_gmxx_rxx_int_reg gmx_rx_int_reg;
+
+ gmx_rx_int_reg.u64 =
+ cvmx_read_csr(CVMX_GMXX_RXX_INT_REG
+ (index, interface));
+ gmx_rx_int_reg.u64 &=
+ cvmx_read_csr(CVMX_GMXX_RXX_INT_EN
+ (index, interface));
+ /* Poll the port if inband status changed */
+ if (gmx_rx_int_reg.s.phy_dupx
+ || gmx_rx_int_reg.s.phy_link
+ || gmx_rx_int_reg.s.phy_spd) {
+
+ struct net_device *dev =
+ cvm_oct_device[cvmx_helper_get_ipd_port
+ (interface, index)];
+ struct octeon_ethernet *priv = netdev_priv(dev);
+
+ if (dev &&
+ !atomic_read(&cvm_oct_poll_queue_stopping))
+ queue_work(cvm_oct_poll_queue,
+ &priv->port_work);
+
+ gmx_rx_int_reg.u64 = 0;
+ gmx_rx_int_reg.s.phy_dupx = 1;
+ gmx_rx_int_reg.s.phy_link = 1;
+ gmx_rx_int_reg.s.phy_spd = 1;
+ cvmx_write_csr(CVMX_GMXX_RXX_INT_REG
+ (index, interface),
+ gmx_rx_int_reg.u64);
+ return_status = IRQ_HANDLED;
+ }
+ }
+ }
+
+ /* Check and see if this interrupt was caused by the GMX1 block */
+ if (rsl_int_blocks.s.gmx1) {
+
+ int interface = 1;
+ /* Loop through every port of this interface */
+ for (index = 0;
+ index < cvmx_helper_ports_on_interface(interface);
+ index++) {
+
+ /* Read the GMX interrupt status bits */
+ union cvmx_gmxx_rxx_int_reg gmx_rx_int_reg;
+
+ gmx_rx_int_reg.u64 =
+ cvmx_read_csr(CVMX_GMXX_RXX_INT_REG
+ (index, interface));
+ gmx_rx_int_reg.u64 &=
+ cvmx_read_csr(CVMX_GMXX_RXX_INT_EN
+ (index, interface));
+ /* Poll the port if inband status changed */
+ if (gmx_rx_int_reg.s.phy_dupx
+ || gmx_rx_int_reg.s.phy_link
+ || gmx_rx_int_reg.s.phy_spd) {
+
+ struct net_device *dev =
+ cvm_oct_device[cvmx_helper_get_ipd_port
+ (interface, index)];
+ struct octeon_ethernet *priv = netdev_priv(dev);
+
+ if (dev &&
+ !atomic_read(&cvm_oct_poll_queue_stopping))
+ queue_work(cvm_oct_poll_queue,
+ &priv->port_work);
+
+ gmx_rx_int_reg.u64 = 0;
+ gmx_rx_int_reg.s.phy_dupx = 1;
+ gmx_rx_int_reg.s.phy_link = 1;
+ gmx_rx_int_reg.s.phy_spd = 1;
+ cvmx_write_csr(CVMX_GMXX_RXX_INT_REG
+ (index, interface),
+ gmx_rx_int_reg.u64);
+ return_status = IRQ_HANDLED;
+ }
+ }
+ }
+ return return_status;
+}
+
+int cvm_oct_rgmii_open(struct net_device *dev)
+{
+ union cvmx_gmxx_prtx_cfg gmx_cfg;
+ struct octeon_ethernet *priv = netdev_priv(dev);
+ int interface = INTERFACE(priv->port);
+ int index = INDEX(priv->port);
+ cvmx_helper_link_info_t link_info;
+ int rv;
+
+ rv = cvm_oct_phy_setup_device(dev);
+ if (rv)
+ return rv;
+
+ gmx_cfg.u64 = cvmx_read_csr(CVMX_GMXX_PRTX_CFG(index, interface));
+ gmx_cfg.s.en = 1;
+ cvmx_write_csr(CVMX_GMXX_PRTX_CFG(index, interface), gmx_cfg.u64);
+
+ if (!octeon_is_simulation()) {
+ if (priv->phydev) {
+ int r = phy_read_status(priv->phydev);
+
+ if (r == 0 && priv->phydev->link == 0)
+ netif_carrier_off(dev);
+ cvm_oct_adjust_link(dev);
+ } else {
+ link_info = cvmx_helper_link_get(priv->port);
+ if (!link_info.s.link_up)
+ netif_carrier_off(dev);
+ priv->poll = cvm_oct_rgmii_poll;
+ }
+ }
+
+ return 0;
+}
+
+int cvm_oct_rgmii_stop(struct net_device *dev)
+{
+ union cvmx_gmxx_prtx_cfg gmx_cfg;
+ struct octeon_ethernet *priv = netdev_priv(dev);
+ int interface = INTERFACE(priv->port);
+ int index = INDEX(priv->port);
+
+ gmx_cfg.u64 = cvmx_read_csr(CVMX_GMXX_PRTX_CFG(index, interface));
+ gmx_cfg.s.en = 0;
+ cvmx_write_csr(CVMX_GMXX_PRTX_CFG(index, interface), gmx_cfg.u64);
+ return cvm_oct_common_stop(dev);
+}
+
+static void cvm_oct_rgmii_immediate_poll(struct work_struct *work)
+{
+ struct octeon_ethernet *priv =
+ container_of(work, struct octeon_ethernet, port_work);
+ cvm_oct_rgmii_poll(cvm_oct_device[priv->port]);
+}
+
+int cvm_oct_rgmii_init(struct net_device *dev)
+{
+ struct octeon_ethernet *priv = netdev_priv(dev);
+ int r;
+
+ cvm_oct_common_init(dev);
+ dev->netdev_ops->ndo_stop(dev);
+ INIT_WORK(&priv->port_work, cvm_oct_rgmii_immediate_poll);
+ /*
+ * Due to GMX errata in CN3XXX series chips, it is necessary
+ * to take the link down immediately when the PHY changes
+ * state. In order to do this we call the poll function every
+ * time the RGMII inband status changes. This may cause
+ * problems if the PHY doesn't implement inband status
+ * properly.
+ */
+ if (number_rgmii_ports == 0) {
+ r = request_irq(OCTEON_IRQ_RML, cvm_oct_rgmii_rml_interrupt,
+ IRQF_SHARED, "RGMII", &number_rgmii_ports);
+ if (r != 0)
+ return r;
+ }
+ number_rgmii_ports++;
+
+ /*
+ * Only true RGMII ports need to be polled. In GMII mode, port
+ * 0 is really a RGMII port.
+ */
+ if (((priv->imode == CVMX_HELPER_INTERFACE_MODE_GMII)
+ && (priv->port == 0))
+ || (priv->imode == CVMX_HELPER_INTERFACE_MODE_RGMII)) {
+
+ if (!octeon_is_simulation()) {
+
+ union cvmx_gmxx_rxx_int_en gmx_rx_int_en;
+ int interface = INTERFACE(priv->port);
+ int index = INDEX(priv->port);
+
+ /*
+ * Enable interrupts on inband status changes
+ * for this port.
+ */
+ gmx_rx_int_en.u64 = 0;
+ gmx_rx_int_en.s.phy_dupx = 1;
+ gmx_rx_int_en.s.phy_link = 1;
+ gmx_rx_int_en.s.phy_spd = 1;
+ cvmx_write_csr(CVMX_GMXX_RXX_INT_EN(index, interface),
+ gmx_rx_int_en.u64);
+ }
+ }
+
+ return 0;
+}
+
+void cvm_oct_rgmii_uninit(struct net_device *dev)
+{
+ struct octeon_ethernet *priv = netdev_priv(dev);
+
+ cvm_oct_common_uninit(dev);
+
+ /*
+ * Only true RGMII ports need to be polled. In GMII mode, port
+ * 0 is really a RGMII port.
+ */
+ if (((priv->imode == CVMX_HELPER_INTERFACE_MODE_GMII)
+ && (priv->port == 0))
+ || (priv->imode == CVMX_HELPER_INTERFACE_MODE_RGMII)) {
+
+ if (!octeon_is_simulation()) {
+
+ union cvmx_gmxx_rxx_int_en gmx_rx_int_en;
+ int interface = INTERFACE(priv->port);
+ int index = INDEX(priv->port);
+
+ /*
+ * Disable interrupts on inband status changes
+ * for this port.
+ */
+ gmx_rx_int_en.u64 =
+ cvmx_read_csr(CVMX_GMXX_RXX_INT_EN
+ (index, interface));
+ gmx_rx_int_en.s.phy_dupx = 0;
+ gmx_rx_int_en.s.phy_link = 0;
+ gmx_rx_int_en.s.phy_spd = 0;
+ cvmx_write_csr(CVMX_GMXX_RXX_INT_EN(index, interface),
+ gmx_rx_int_en.u64);
+ }
+ }
+
+ /* Remove the interrupt handler when the last port is removed. */
+ number_rgmii_ports--;
+ if (number_rgmii_ports == 0)
+ free_irq(OCTEON_IRQ_RML, &number_rgmii_ports);
+ cancel_work_sync(&priv->port_work);
+}
diff --git a/drivers/staging/octeon/ethernet-rx.c b/drivers/staging/octeon/ethernet-rx.c
new file mode 100644
index 000000000..22667dbb1
--- /dev/null
+++ b/drivers/staging/octeon/ethernet-rx.c
@@ -0,0 +1,489 @@
+/**********************************************************************
+ * Author: Cavium Networks
+ *
+ * Contact: support@caviumnetworks.com
+ * This file is part of the OCTEON SDK
+ *
+ * Copyright (c) 2003-2010 Cavium Networks
+ *
+ * This file 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.
+ *
+ * This file is distributed in the hope that it will be useful, but
+ * AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or
+ * NONINFRINGEMENT. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this file; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ * or visit http://www.gnu.org/licenses/.
+ *
+ * This file may also be available under a different license from Cavium.
+ * Contact Cavium Networks for more information
+**********************************************************************/
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/cache.h>
+#include <linux/cpumask.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/ip.h>
+#include <linux/string.h>
+#include <linux/prefetch.h>
+#include <linux/ratelimit.h>
+#include <linux/smp.h>
+#include <linux/interrupt.h>
+#include <net/dst.h>
+#ifdef CONFIG_XFRM
+#include <linux/xfrm.h>
+#include <net/xfrm.h>
+#endif /* CONFIG_XFRM */
+
+#include <linux/atomic.h>
+
+#include <asm/octeon/octeon.h>
+
+#include "ethernet-defines.h"
+#include "ethernet-mem.h"
+#include "ethernet-rx.h"
+#include "octeon-ethernet.h"
+#include "ethernet-util.h"
+
+#include <asm/octeon/cvmx-helper.h>
+#include <asm/octeon/cvmx-wqe.h>
+#include <asm/octeon/cvmx-fau.h>
+#include <asm/octeon/cvmx-pow.h>
+#include <asm/octeon/cvmx-pip.h>
+#include <asm/octeon/cvmx-scratch.h>
+
+#include <asm/octeon/cvmx-gmxx-defs.h>
+
+static struct napi_struct cvm_oct_napi;
+
+/**
+ * cvm_oct_do_interrupt - interrupt handler.
+ *
+ * The interrupt occurs whenever the POW has packets in our group.
+ *
+ */
+static irqreturn_t cvm_oct_do_interrupt(int cpl, void *dev_id)
+{
+ /* Disable the IRQ and start napi_poll. */
+ disable_irq_nosync(OCTEON_IRQ_WORKQ0 + pow_receive_group);
+ napi_schedule(&cvm_oct_napi);
+
+ return IRQ_HANDLED;
+}
+
+/**
+ * cvm_oct_check_rcv_error - process receive errors
+ * @work: Work queue entry pointing to the packet.
+ *
+ * Returns Non-zero if the packet can be dropped, zero otherwise.
+ */
+static inline int cvm_oct_check_rcv_error(cvmx_wqe_t *work)
+{
+ if ((work->word2.snoip.err_code == 10) && (work->len <= 64)) {
+ /*
+ * Ignore length errors on min size packets. Some
+ * equipment incorrectly pads packets to 64+4FCS
+ * instead of 60+4FCS. Note these packets still get
+ * counted as frame errors.
+ */
+ } else
+ if (USE_10MBPS_PREAMBLE_WORKAROUND
+ && ((work->word2.snoip.err_code == 5)
+ || (work->word2.snoip.err_code == 7))) {
+
+ /*
+ * We received a packet with either an alignment error
+ * or a FCS error. This may be signalling that we are
+ * running 10Mbps with GMXX_RXX_FRM_CTL[PRE_CHK]
+ * off. If this is the case we need to parse the
+ * packet to determine if we can remove a non spec
+ * preamble and generate a correct packet.
+ */
+ int interface = cvmx_helper_get_interface_num(work->ipprt);
+ int index = cvmx_helper_get_interface_index_num(work->ipprt);
+ union cvmx_gmxx_rxx_frm_ctl gmxx_rxx_frm_ctl;
+
+ gmxx_rxx_frm_ctl.u64 =
+ cvmx_read_csr(CVMX_GMXX_RXX_FRM_CTL(index, interface));
+ if (gmxx_rxx_frm_ctl.s.pre_chk == 0) {
+
+ uint8_t *ptr =
+ cvmx_phys_to_ptr(work->packet_ptr.s.addr);
+ int i = 0;
+
+ while (i < work->len - 1) {
+ if (*ptr != 0x55)
+ break;
+ ptr++;
+ i++;
+ }
+
+ if (*ptr == 0xd5) {
+ /*
+ printk_ratelimited("Port %d received 0xd5 preamble\n",
+ work->ipprt);
+ */
+ work->packet_ptr.s.addr += i + 1;
+ work->len -= i + 5;
+ } else if ((*ptr & 0xf) == 0xd) {
+ /*
+ printk_ratelimited("Port %d received 0x?d preamble\n",
+ work->ipprt);
+ */
+ work->packet_ptr.s.addr += i;
+ work->len -= i + 4;
+ for (i = 0; i < work->len; i++) {
+ *ptr =
+ ((*ptr & 0xf0) >> 4) |
+ ((*(ptr + 1) & 0xf) << 4);
+ ptr++;
+ }
+ } else {
+ printk_ratelimited("Port %d unknown preamble, packet dropped\n",
+ work->ipprt);
+ /*
+ cvmx_helper_dump_packet(work);
+ */
+ cvm_oct_free_work(work);
+ return 1;
+ }
+ }
+ } else {
+ printk_ratelimited("Port %d receive error code %d, packet dropped\n",
+ work->ipprt, work->word2.snoip.err_code);
+ cvm_oct_free_work(work);
+ return 1;
+ }
+
+ return 0;
+}
+
+/**
+ * cvm_oct_napi_poll - the NAPI poll function.
+ * @napi: The NAPI instance, or null if called from cvm_oct_poll_controller
+ * @budget: Maximum number of packets to receive.
+ *
+ * Returns the number of packets processed.
+ */
+static int cvm_oct_napi_poll(struct napi_struct *napi, int budget)
+{
+ const int coreid = cvmx_get_core_num();
+ uint64_t old_group_mask;
+ uint64_t old_scratch;
+ int rx_count = 0;
+ int did_work_request = 0;
+ int packet_not_copied;
+
+ /* Prefetch cvm_oct_device since we know we need it soon */
+ prefetch(cvm_oct_device);
+
+ if (USE_ASYNC_IOBDMA) {
+ /* Save scratch in case userspace is using it */
+ CVMX_SYNCIOBDMA;
+ old_scratch = cvmx_scratch_read64(CVMX_SCR_SCRATCH);
+ }
+
+ /* Only allow work for our group (and preserve priorities) */
+ old_group_mask = cvmx_read_csr(CVMX_POW_PP_GRP_MSKX(coreid));
+ cvmx_write_csr(CVMX_POW_PP_GRP_MSKX(coreid),
+ (old_group_mask & ~0xFFFFull) | 1 << pow_receive_group);
+
+ if (USE_ASYNC_IOBDMA) {
+ cvmx_pow_work_request_async(CVMX_SCR_SCRATCH, CVMX_POW_NO_WAIT);
+ did_work_request = 1;
+ }
+
+ while (rx_count < budget) {
+ struct sk_buff *skb = NULL;
+ struct sk_buff **pskb = NULL;
+ int skb_in_hw;
+ cvmx_wqe_t *work;
+
+ if (USE_ASYNC_IOBDMA && did_work_request)
+ work = cvmx_pow_work_response_async(CVMX_SCR_SCRATCH);
+ else
+ work = cvmx_pow_work_request_sync(CVMX_POW_NO_WAIT);
+
+ prefetch(work);
+ did_work_request = 0;
+ if (work == NULL) {
+ union cvmx_pow_wq_int wq_int;
+
+ wq_int.u64 = 0;
+ wq_int.s.iq_dis = 1 << pow_receive_group;
+ wq_int.s.wq_int = 1 << pow_receive_group;
+ cvmx_write_csr(CVMX_POW_WQ_INT, wq_int.u64);
+ break;
+ }
+ pskb = (struct sk_buff **)(cvm_oct_get_buffer_ptr(work->packet_ptr) -
+ sizeof(void *));
+ prefetch(pskb);
+
+ if (USE_ASYNC_IOBDMA && rx_count < (budget - 1)) {
+ cvmx_pow_work_request_async_nocheck(CVMX_SCR_SCRATCH,
+ CVMX_POW_NO_WAIT);
+ did_work_request = 1;
+ }
+ rx_count++;
+
+ skb_in_hw = USE_SKBUFFS_IN_HW && work->word2.s.bufs == 1;
+ if (likely(skb_in_hw)) {
+ skb = *pskb;
+ prefetch(&skb->head);
+ prefetch(&skb->len);
+ }
+ prefetch(cvm_oct_device[work->ipprt]);
+
+ /* Immediately throw away all packets with receive errors */
+ if (unlikely(work->word2.snoip.rcv_error)) {
+ if (cvm_oct_check_rcv_error(work))
+ continue;
+ }
+
+ /*
+ * We can only use the zero copy path if skbuffs are
+ * in the FPA pool and the packet fits in a single
+ * buffer.
+ */
+ if (likely(skb_in_hw)) {
+ skb->data = skb->head + work->packet_ptr.s.addr -
+ cvmx_ptr_to_phys(skb->head);
+ prefetch(skb->data);
+ skb->len = work->len;
+ skb_set_tail_pointer(skb, skb->len);
+ packet_not_copied = 1;
+ } else {
+ /*
+ * We have to copy the packet. First allocate
+ * an skbuff for it.
+ */
+ skb = dev_alloc_skb(work->len);
+ if (!skb) {
+ cvm_oct_free_work(work);
+ continue;
+ }
+
+ /*
+ * Check if we've received a packet that was
+ * entirely stored in the work entry.
+ */
+ if (unlikely(work->word2.s.bufs == 0)) {
+ uint8_t *ptr = work->packet_data;
+
+ if (likely(!work->word2.s.not_IP)) {
+ /*
+ * The beginning of the packet
+ * moves for IP packets.
+ */
+ if (work->word2.s.is_v6)
+ ptr += 2;
+ else
+ ptr += 6;
+ }
+ memcpy(skb_put(skb, work->len), ptr, work->len);
+ /* No packet buffers to free */
+ } else {
+ int segments = work->word2.s.bufs;
+ union cvmx_buf_ptr segment_ptr =
+ work->packet_ptr;
+ int len = work->len;
+
+ while (segments--) {
+ union cvmx_buf_ptr next_ptr =
+ *(union cvmx_buf_ptr *)cvmx_phys_to_ptr(segment_ptr.s.addr - 8);
+
+ /*
+ * Octeon Errata PKI-100: The segment size is
+ * wrong. Until it is fixed, calculate the
+ * segment size based on the packet pool
+ * buffer size. When it is fixed, the
+ * following line should be replaced with this
+ * one: int segment_size =
+ * segment_ptr.s.size;
+ */
+ int segment_size =
+ CVMX_FPA_PACKET_POOL_SIZE -
+ (segment_ptr.s.addr -
+ (((segment_ptr.s.addr >> 7) -
+ segment_ptr.s.back) << 7));
+ /*
+ * Don't copy more than what
+ * is left in the packet.
+ */
+ if (segment_size > len)
+ segment_size = len;
+ /* Copy the data into the packet */
+ memcpy(skb_put(skb, segment_size),
+ cvmx_phys_to_ptr(segment_ptr.s.addr),
+ segment_size);
+ len -= segment_size;
+ segment_ptr = next_ptr;
+ }
+ }
+ packet_not_copied = 0;
+ }
+
+ if (likely((work->ipprt < TOTAL_NUMBER_OF_PORTS) &&
+ cvm_oct_device[work->ipprt])) {
+ struct net_device *dev = cvm_oct_device[work->ipprt];
+ struct octeon_ethernet *priv = netdev_priv(dev);
+
+ /*
+ * Only accept packets for devices that are
+ * currently up.
+ */
+ if (likely(dev->flags & IFF_UP)) {
+ skb->protocol = eth_type_trans(skb, dev);
+ skb->dev = dev;
+
+ if (unlikely(work->word2.s.not_IP ||
+ work->word2.s.IP_exc ||
+ work->word2.s.L4_error ||
+ !work->word2.s.tcp_or_udp))
+ skb->ip_summed = CHECKSUM_NONE;
+ else
+ skb->ip_summed = CHECKSUM_UNNECESSARY;
+
+ /* Increment RX stats for virtual ports */
+ if (work->ipprt >= CVMX_PIP_NUM_INPUT_PORTS) {
+#ifdef CONFIG_64BIT
+ atomic64_add(1,
+ (atomic64_t *)&priv->stats.rx_packets);
+ atomic64_add(skb->len,
+ (atomic64_t *)&priv->stats.rx_bytes);
+#else
+ atomic_add(1,
+ (atomic_t *)&priv->stats.rx_packets);
+ atomic_add(skb->len,
+ (atomic_t *)&priv->stats.rx_bytes);
+#endif
+ }
+ netif_receive_skb(skb);
+ } else {
+ /* Drop any packet received for a device that isn't up */
+ /*
+ printk_ratelimited("%s: Device not up, packet dropped\n",
+ dev->name);
+ */
+#ifdef CONFIG_64BIT
+ atomic64_add(1,
+ (atomic64_t *)&priv->stats.rx_dropped);
+#else
+ atomic_add(1,
+ (atomic_t *)&priv->stats.rx_dropped);
+#endif
+ dev_kfree_skb_irq(skb);
+ }
+ } else {
+ /*
+ * Drop any packet received for a device that
+ * doesn't exist.
+ */
+ printk_ratelimited("Port %d not controlled by Linux, packet dropped\n",
+ work->ipprt);
+ dev_kfree_skb_irq(skb);
+ }
+ /*
+ * Check to see if the skbuff and work share the same
+ * packet buffer.
+ */
+ if (USE_SKBUFFS_IN_HW && likely(packet_not_copied)) {
+ /*
+ * This buffer needs to be replaced, increment
+ * the number of buffers we need to free by
+ * one.
+ */
+ cvmx_fau_atomic_add32(FAU_NUM_PACKET_BUFFERS_TO_FREE,
+ 1);
+
+ cvmx_fpa_free(work, CVMX_FPA_WQE_POOL,
+ DONT_WRITEBACK(1));
+ } else {
+ cvm_oct_free_work(work);
+ }
+ }
+ /* Restore the original POW group mask */
+ cvmx_write_csr(CVMX_POW_PP_GRP_MSKX(coreid), old_group_mask);
+ if (USE_ASYNC_IOBDMA) {
+ /* Restore the scratch area */
+ cvmx_scratch_write64(CVMX_SCR_SCRATCH, old_scratch);
+ }
+ cvm_oct_rx_refill_pool(0);
+
+ if (rx_count < budget && napi != NULL) {
+ /* No more work */
+ napi_complete(napi);
+ enable_irq(OCTEON_IRQ_WORKQ0 + pow_receive_group);
+ }
+ return rx_count;
+}
+
+#ifdef CONFIG_NET_POLL_CONTROLLER
+/**
+ * cvm_oct_poll_controller - poll for receive packets
+ * device.
+ *
+ * @dev: Device to poll. Unused
+ */
+void cvm_oct_poll_controller(struct net_device *dev)
+{
+ cvm_oct_napi_poll(NULL, 16);
+}
+#endif
+
+void cvm_oct_rx_initialize(void)
+{
+ int i;
+ struct net_device *dev_for_napi = NULL;
+ union cvmx_pow_wq_int_thrx int_thr;
+ union cvmx_pow_wq_int_pc int_pc;
+
+ for (i = 0; i < TOTAL_NUMBER_OF_PORTS; i++) {
+ if (cvm_oct_device[i]) {
+ dev_for_napi = cvm_oct_device[i];
+ break;
+ }
+ }
+
+ if (NULL == dev_for_napi)
+ panic("No net_devices were allocated.");
+
+ netif_napi_add(dev_for_napi, &cvm_oct_napi, cvm_oct_napi_poll,
+ rx_napi_weight);
+ napi_enable(&cvm_oct_napi);
+
+ /* Register an IRQ handler to receive POW interrupts */
+ i = request_irq(OCTEON_IRQ_WORKQ0 + pow_receive_group,
+ cvm_oct_do_interrupt, 0, "Ethernet", cvm_oct_device);
+
+ if (i)
+ panic("Could not acquire Ethernet IRQ %d\n",
+ OCTEON_IRQ_WORKQ0 + pow_receive_group);
+
+ disable_irq_nosync(OCTEON_IRQ_WORKQ0 + pow_receive_group);
+
+ int_thr.u64 = 0;
+ int_thr.s.tc_en = 1;
+ int_thr.s.tc_thr = 1;
+ /* Enable POW interrupt when our port has at least one packet */
+ cvmx_write_csr(CVMX_POW_WQ_INT_THRX(pow_receive_group), int_thr.u64);
+
+ int_pc.u64 = 0;
+ int_pc.s.pc_thr = 5;
+ cvmx_write_csr(CVMX_POW_WQ_INT_PC, int_pc.u64);
+
+ /* Schedule NAPI now. This will indirectly enable the interrupt. */
+ napi_schedule(&cvm_oct_napi);
+}
+
+void cvm_oct_rx_shutdown(void)
+{
+ netif_napi_del(&cvm_oct_napi);
+}
diff --git a/drivers/staging/octeon/ethernet-rx.h b/drivers/staging/octeon/ethernet-rx.h
new file mode 100644
index 000000000..9240c85ce
--- /dev/null
+++ b/drivers/staging/octeon/ethernet-rx.h
@@ -0,0 +1,52 @@
+/*********************************************************************
+ * Author: Cavium Networks
+ *
+ * Contact: support@caviumnetworks.com
+ * This file is part of the OCTEON SDK
+ *
+ * Copyright (c) 2003-2007 Cavium Networks
+ *
+ * This file 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.
+ *
+ * This file is distributed in the hope that it will be useful, but
+ * AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or
+ * NONINFRINGEMENT. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this file; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ * or visit http://www.gnu.org/licenses/.
+ *
+ * This file may also be available under a different license from Cavium.
+ * Contact Cavium Networks for more information
+*********************************************************************/
+#include <asm/octeon/cvmx-fau.h>
+
+void cvm_oct_poll_controller(struct net_device *dev);
+void cvm_oct_rx_initialize(void);
+void cvm_oct_rx_shutdown(void);
+
+static inline void cvm_oct_rx_refill_pool(int fill_threshold)
+{
+ int number_to_free;
+ int num_freed;
+ /* Refill the packet buffer pool */
+ number_to_free =
+ cvmx_fau_fetch_and_add32(FAU_NUM_PACKET_BUFFERS_TO_FREE, 0);
+
+ if (number_to_free > fill_threshold) {
+ cvmx_fau_atomic_add32(FAU_NUM_PACKET_BUFFERS_TO_FREE,
+ -number_to_free);
+ num_freed = cvm_oct_mem_fill_fpa(CVMX_FPA_PACKET_POOL,
+ CVMX_FPA_PACKET_POOL_SIZE,
+ number_to_free);
+ if (num_freed != number_to_free) {
+ cvmx_fau_atomic_add32(FAU_NUM_PACKET_BUFFERS_TO_FREE,
+ number_to_free - num_freed);
+ }
+ }
+}
diff --git a/drivers/staging/octeon/ethernet-sgmii.c b/drivers/staging/octeon/ethernet-sgmii.c
new file mode 100644
index 000000000..21a7a17ac
--- /dev/null
+++ b/drivers/staging/octeon/ethernet-sgmii.c
@@ -0,0 +1,141 @@
+/**********************************************************************
+ * Author: Cavium Networks
+ *
+ * Contact: support@caviumnetworks.com
+ * This file is part of the OCTEON SDK
+ *
+ * Copyright (c) 2003-2007 Cavium Networks
+ *
+ * This file 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.
+ *
+ * This file is distributed in the hope that it will be useful, but
+ * AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or
+ * NONINFRINGEMENT. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this file; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ * or visit http://www.gnu.org/licenses/.
+ *
+ * This file may also be available under a different license from Cavium.
+ * Contact Cavium Networks for more information
+**********************************************************************/
+#include <linux/phy.h>
+#include <linux/kernel.h>
+#include <linux/netdevice.h>
+#include <linux/ratelimit.h>
+#include <net/dst.h>
+
+#include <asm/octeon/octeon.h>
+
+#include "ethernet-defines.h"
+#include "octeon-ethernet.h"
+#include "ethernet-util.h"
+#include "ethernet-mdio.h"
+
+#include <asm/octeon/cvmx-helper.h>
+
+#include <asm/octeon/cvmx-gmxx-defs.h>
+
+static void cvm_oct_sgmii_poll(struct net_device *dev)
+{
+ struct octeon_ethernet *priv = netdev_priv(dev);
+ cvmx_helper_link_info_t link_info;
+
+ link_info = cvmx_helper_link_get(priv->port);
+ if (link_info.u64 == priv->link_info)
+ return;
+
+ link_info = cvmx_helper_link_autoconf(priv->port);
+ priv->link_info = link_info.u64;
+
+ /* Tell Linux */
+ if (link_info.s.link_up) {
+
+ if (!netif_carrier_ok(dev))
+ netif_carrier_on(dev);
+ if (priv->queue != -1)
+ printk_ratelimited
+ ("%s: %u Mbps %s duplex, port %2d, queue %2d\n",
+ dev->name, link_info.s.speed,
+ (link_info.s.full_duplex) ? "Full" : "Half",
+ priv->port, priv->queue);
+ else
+ printk_ratelimited
+ ("%s: %u Mbps %s duplex, port %2d, POW\n",
+ dev->name, link_info.s.speed,
+ (link_info.s.full_duplex) ? "Full" : "Half",
+ priv->port);
+ } else {
+ if (netif_carrier_ok(dev))
+ netif_carrier_off(dev);
+ printk_ratelimited("%s: Link down\n", dev->name);
+ }
+}
+
+int cvm_oct_sgmii_open(struct net_device *dev)
+{
+ union cvmx_gmxx_prtx_cfg gmx_cfg;
+ struct octeon_ethernet *priv = netdev_priv(dev);
+ int interface = INTERFACE(priv->port);
+ int index = INDEX(priv->port);
+ cvmx_helper_link_info_t link_info;
+ int rv;
+
+ rv = cvm_oct_phy_setup_device(dev);
+ if (rv)
+ return rv;
+
+ gmx_cfg.u64 = cvmx_read_csr(CVMX_GMXX_PRTX_CFG(index, interface));
+ gmx_cfg.s.en = 1;
+ cvmx_write_csr(CVMX_GMXX_PRTX_CFG(index, interface), gmx_cfg.u64);
+
+ if (octeon_is_simulation())
+ return 0;
+
+ if (priv->phydev) {
+ int r = phy_read_status(priv->phydev);
+
+ if (r == 0 && priv->phydev->link == 0)
+ netif_carrier_off(dev);
+ cvm_oct_adjust_link(dev);
+ } else {
+ link_info = cvmx_helper_link_get(priv->port);
+ if (!link_info.s.link_up)
+ netif_carrier_off(dev);
+ priv->poll = cvm_oct_sgmii_poll;
+ cvm_oct_sgmii_poll(dev);
+ }
+ return 0;
+}
+
+int cvm_oct_sgmii_stop(struct net_device *dev)
+{
+ union cvmx_gmxx_prtx_cfg gmx_cfg;
+ struct octeon_ethernet *priv = netdev_priv(dev);
+ int interface = INTERFACE(priv->port);
+ int index = INDEX(priv->port);
+
+ gmx_cfg.u64 = cvmx_read_csr(CVMX_GMXX_PRTX_CFG(index, interface));
+ gmx_cfg.s.en = 0;
+ cvmx_write_csr(CVMX_GMXX_PRTX_CFG(index, interface), gmx_cfg.u64);
+ return cvm_oct_common_stop(dev);
+}
+
+int cvm_oct_sgmii_init(struct net_device *dev)
+{
+ cvm_oct_common_init(dev);
+ dev->netdev_ops->ndo_stop(dev);
+
+ /* FIXME: Need autoneg logic */
+ return 0;
+}
+
+void cvm_oct_sgmii_uninit(struct net_device *dev)
+{
+ cvm_oct_common_uninit(dev);
+}
diff --git a/drivers/staging/octeon/ethernet-spi.c b/drivers/staging/octeon/ethernet-spi.c
new file mode 100644
index 000000000..5108bc0bb
--- /dev/null
+++ b/drivers/staging/octeon/ethernet-spi.c
@@ -0,0 +1,292 @@
+/**********************************************************************
+ * Author: Cavium Networks
+ *
+ * Contact: support@caviumnetworks.com
+ * This file is part of the OCTEON SDK
+ *
+ * Copyright (c) 2003-2007 Cavium Networks
+ *
+ * This file 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.
+ *
+ * This file is distributed in the hope that it will be useful, but
+ * AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or
+ * NONINFRINGEMENT. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this file; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ * or visit http://www.gnu.org/licenses/.
+ *
+ * This file may also be available under a different license from Cavium.
+ * Contact Cavium Networks for more information
+**********************************************************************/
+#include <linux/kernel.h>
+#include <linux/netdevice.h>
+#include <linux/interrupt.h>
+#include <net/dst.h>
+
+#include <asm/octeon/octeon.h>
+
+#include "ethernet-defines.h"
+#include "octeon-ethernet.h"
+#include "ethernet-util.h"
+
+#include <asm/octeon/cvmx-spi.h>
+
+#include <asm/octeon/cvmx-npi-defs.h>
+#include <asm/octeon/cvmx-spxx-defs.h>
+#include <asm/octeon/cvmx-stxx-defs.h>
+
+static int number_spi_ports;
+static int need_retrain[2] = { 0, 0 };
+
+static irqreturn_t cvm_oct_spi_rml_interrupt(int cpl, void *dev_id)
+{
+ irqreturn_t return_status = IRQ_NONE;
+ union cvmx_npi_rsl_int_blocks rsl_int_blocks;
+
+ /* Check and see if this interrupt was caused by the GMX block */
+ rsl_int_blocks.u64 = cvmx_read_csr(CVMX_NPI_RSL_INT_BLOCKS);
+ if (rsl_int_blocks.s.spx1) { /* 19 - SPX1_INT_REG & STX1_INT_REG */
+
+ union cvmx_spxx_int_reg spx_int_reg;
+ union cvmx_stxx_int_reg stx_int_reg;
+
+ spx_int_reg.u64 = cvmx_read_csr(CVMX_SPXX_INT_REG(1));
+ cvmx_write_csr(CVMX_SPXX_INT_REG(1), spx_int_reg.u64);
+ if (!need_retrain[1]) {
+
+ spx_int_reg.u64 &= cvmx_read_csr(CVMX_SPXX_INT_MSK(1));
+ if (spx_int_reg.s.spf)
+ pr_err("SPI1: SRX Spi4 interface down\n");
+ if (spx_int_reg.s.calerr)
+ pr_err("SPI1: SRX Spi4 Calendar table parity error\n");
+ if (spx_int_reg.s.syncerr)
+ pr_err("SPI1: SRX Consecutive Spi4 DIP4 errors have exceeded SPX_ERR_CTL[ERRCNT]\n");
+ if (spx_int_reg.s.diperr)
+ pr_err("SPI1: SRX Spi4 DIP4 error\n");
+ if (spx_int_reg.s.tpaovr)
+ pr_err("SPI1: SRX Selected port has hit TPA overflow\n");
+ if (spx_int_reg.s.rsverr)
+ pr_err("SPI1: SRX Spi4 reserved control word detected\n");
+ if (spx_int_reg.s.drwnng)
+ pr_err("SPI1: SRX Spi4 receive FIFO drowning/overflow\n");
+ if (spx_int_reg.s.clserr)
+ pr_err("SPI1: SRX Spi4 packet closed on non-16B alignment without EOP\n");
+ if (spx_int_reg.s.spiovr)
+ pr_err("SPI1: SRX Spi4 async FIFO overflow\n");
+ if (spx_int_reg.s.abnorm)
+ pr_err("SPI1: SRX Abnormal packet termination (ERR bit)\n");
+ if (spx_int_reg.s.prtnxa)
+ pr_err("SPI1: SRX Port out of range\n");
+ }
+
+ stx_int_reg.u64 = cvmx_read_csr(CVMX_STXX_INT_REG(1));
+ cvmx_write_csr(CVMX_STXX_INT_REG(1), stx_int_reg.u64);
+ if (!need_retrain[1]) {
+
+ stx_int_reg.u64 &= cvmx_read_csr(CVMX_STXX_INT_MSK(1));
+ if (stx_int_reg.s.syncerr)
+ pr_err("SPI1: STX Interface encountered a fatal error\n");
+ if (stx_int_reg.s.frmerr)
+ pr_err("SPI1: STX FRMCNT has exceeded STX_DIP_CNT[MAXFRM]\n");
+ if (stx_int_reg.s.unxfrm)
+ pr_err("SPI1: STX Unexpected framing sequence\n");
+ if (stx_int_reg.s.nosync)
+ pr_err("SPI1: STX ERRCNT has exceeded STX_DIP_CNT[MAXDIP]\n");
+ if (stx_int_reg.s.diperr)
+ pr_err("SPI1: STX DIP2 error on the Spi4 Status channel\n");
+ if (stx_int_reg.s.datovr)
+ pr_err("SPI1: STX Spi4 FIFO overflow error\n");
+ if (stx_int_reg.s.ovrbst)
+ pr_err("SPI1: STX Transmit packet burst too big\n");
+ if (stx_int_reg.s.calpar1)
+ pr_err("SPI1: STX Calendar Table Parity Error Bank1\n");
+ if (stx_int_reg.s.calpar0)
+ pr_err("SPI1: STX Calendar Table Parity Error Bank0\n");
+ }
+
+ cvmx_write_csr(CVMX_SPXX_INT_MSK(1), 0);
+ cvmx_write_csr(CVMX_STXX_INT_MSK(1), 0);
+ need_retrain[1] = 1;
+ return_status = IRQ_HANDLED;
+ }
+
+ if (rsl_int_blocks.s.spx0) { /* 18 - SPX0_INT_REG & STX0_INT_REG */
+ union cvmx_spxx_int_reg spx_int_reg;
+ union cvmx_stxx_int_reg stx_int_reg;
+
+ spx_int_reg.u64 = cvmx_read_csr(CVMX_SPXX_INT_REG(0));
+ cvmx_write_csr(CVMX_SPXX_INT_REG(0), spx_int_reg.u64);
+ if (!need_retrain[0]) {
+
+ spx_int_reg.u64 &= cvmx_read_csr(CVMX_SPXX_INT_MSK(0));
+ if (spx_int_reg.s.spf)
+ pr_err("SPI0: SRX Spi4 interface down\n");
+ if (spx_int_reg.s.calerr)
+ pr_err("SPI0: SRX Spi4 Calendar table parity error\n");
+ if (spx_int_reg.s.syncerr)
+ pr_err("SPI0: SRX Consecutive Spi4 DIP4 errors have exceeded SPX_ERR_CTL[ERRCNT]\n");
+ if (spx_int_reg.s.diperr)
+ pr_err("SPI0: SRX Spi4 DIP4 error\n");
+ if (spx_int_reg.s.tpaovr)
+ pr_err("SPI0: SRX Selected port has hit TPA overflow\n");
+ if (spx_int_reg.s.rsverr)
+ pr_err("SPI0: SRX Spi4 reserved control word detected\n");
+ if (spx_int_reg.s.drwnng)
+ pr_err("SPI0: SRX Spi4 receive FIFO drowning/overflow\n");
+ if (spx_int_reg.s.clserr)
+ pr_err("SPI0: SRX Spi4 packet closed on non-16B alignment without EOP\n");
+ if (spx_int_reg.s.spiovr)
+ pr_err("SPI0: SRX Spi4 async FIFO overflow\n");
+ if (spx_int_reg.s.abnorm)
+ pr_err("SPI0: SRX Abnormal packet termination (ERR bit)\n");
+ if (spx_int_reg.s.prtnxa)
+ pr_err("SPI0: SRX Port out of range\n");
+ }
+
+ stx_int_reg.u64 = cvmx_read_csr(CVMX_STXX_INT_REG(0));
+ cvmx_write_csr(CVMX_STXX_INT_REG(0), stx_int_reg.u64);
+ if (!need_retrain[0]) {
+
+ stx_int_reg.u64 &= cvmx_read_csr(CVMX_STXX_INT_MSK(0));
+ if (stx_int_reg.s.syncerr)
+ pr_err("SPI0: STX Interface encountered a fatal error\n");
+ if (stx_int_reg.s.frmerr)
+ pr_err("SPI0: STX FRMCNT has exceeded STX_DIP_CNT[MAXFRM]\n");
+ if (stx_int_reg.s.unxfrm)
+ pr_err("SPI0: STX Unexpected framing sequence\n");
+ if (stx_int_reg.s.nosync)
+ pr_err("SPI0: STX ERRCNT has exceeded STX_DIP_CNT[MAXDIP]\n");
+ if (stx_int_reg.s.diperr)
+ pr_err("SPI0: STX DIP2 error on the Spi4 Status channel\n");
+ if (stx_int_reg.s.datovr)
+ pr_err("SPI0: STX Spi4 FIFO overflow error\n");
+ if (stx_int_reg.s.ovrbst)
+ pr_err("SPI0: STX Transmit packet burst too big\n");
+ if (stx_int_reg.s.calpar1)
+ pr_err("SPI0: STX Calendar Table Parity Error Bank1\n");
+ if (stx_int_reg.s.calpar0)
+ pr_err("SPI0: STX Calendar Table Parity Error Bank0\n");
+ }
+
+ cvmx_write_csr(CVMX_SPXX_INT_MSK(0), 0);
+ cvmx_write_csr(CVMX_STXX_INT_MSK(0), 0);
+ need_retrain[0] = 1;
+ return_status = IRQ_HANDLED;
+ }
+
+ return return_status;
+}
+
+static void cvm_oct_spi_enable_error_reporting(int interface)
+{
+ union cvmx_spxx_int_msk spxx_int_msk;
+ union cvmx_stxx_int_msk stxx_int_msk;
+
+ spxx_int_msk.u64 = cvmx_read_csr(CVMX_SPXX_INT_MSK(interface));
+ spxx_int_msk.s.calerr = 1;
+ spxx_int_msk.s.syncerr = 1;
+ spxx_int_msk.s.diperr = 1;
+ spxx_int_msk.s.tpaovr = 1;
+ spxx_int_msk.s.rsverr = 1;
+ spxx_int_msk.s.drwnng = 1;
+ spxx_int_msk.s.clserr = 1;
+ spxx_int_msk.s.spiovr = 1;
+ spxx_int_msk.s.abnorm = 1;
+ spxx_int_msk.s.prtnxa = 1;
+ cvmx_write_csr(CVMX_SPXX_INT_MSK(interface), spxx_int_msk.u64);
+
+ stxx_int_msk.u64 = cvmx_read_csr(CVMX_STXX_INT_MSK(interface));
+ stxx_int_msk.s.frmerr = 1;
+ stxx_int_msk.s.unxfrm = 1;
+ stxx_int_msk.s.nosync = 1;
+ stxx_int_msk.s.diperr = 1;
+ stxx_int_msk.s.datovr = 1;
+ stxx_int_msk.s.ovrbst = 1;
+ stxx_int_msk.s.calpar1 = 1;
+ stxx_int_msk.s.calpar0 = 1;
+ cvmx_write_csr(CVMX_STXX_INT_MSK(interface), stxx_int_msk.u64);
+}
+
+static void cvm_oct_spi_poll(struct net_device *dev)
+{
+ static int spi4000_port;
+ struct octeon_ethernet *priv = netdev_priv(dev);
+ int interface;
+
+ for (interface = 0; interface < 2; interface++) {
+
+ if ((priv->port == interface * 16) && need_retrain[interface]) {
+
+ if (cvmx_spi_restart_interface
+ (interface, CVMX_SPI_MODE_DUPLEX, 10) == 0) {
+ need_retrain[interface] = 0;
+ cvm_oct_spi_enable_error_reporting(interface);
+ }
+ }
+
+ /*
+ * The SPI4000 TWSI interface is very slow. In order
+ * not to bring the system to a crawl, we only poll a
+ * single port every second. This means negotiation
+ * speed changes take up to 10 seconds, but at least
+ * we don't waste absurd amounts of time waiting for
+ * TWSI.
+ */
+ if (priv->port == spi4000_port) {
+ /*
+ * This function does nothing if it is called on an
+ * interface without a SPI4000.
+ */
+ cvmx_spi4000_check_speed(interface, priv->port);
+ /*
+ * Normal ordering increments. By decrementing
+ * we only match once per iteration.
+ */
+ spi4000_port--;
+ if (spi4000_port < 0)
+ spi4000_port = 10;
+ }
+ }
+}
+
+int cvm_oct_spi_init(struct net_device *dev)
+{
+ int r;
+ struct octeon_ethernet *priv = netdev_priv(dev);
+
+ if (number_spi_ports == 0) {
+ r = request_irq(OCTEON_IRQ_RML, cvm_oct_spi_rml_interrupt,
+ IRQF_SHARED, "SPI", &number_spi_ports);
+ if (r)
+ return r;
+ }
+ number_spi_ports++;
+
+ if ((priv->port == 0) || (priv->port == 16)) {
+ cvm_oct_spi_enable_error_reporting(INTERFACE(priv->port));
+ priv->poll = cvm_oct_spi_poll;
+ }
+ cvm_oct_common_init(dev);
+ return 0;
+}
+
+void cvm_oct_spi_uninit(struct net_device *dev)
+{
+ int interface;
+
+ cvm_oct_common_uninit(dev);
+ number_spi_ports--;
+ if (number_spi_ports == 0) {
+ for (interface = 0; interface < 2; interface++) {
+ cvmx_write_csr(CVMX_SPXX_INT_MSK(interface), 0);
+ cvmx_write_csr(CVMX_STXX_INT_MSK(interface), 0);
+ }
+ free_irq(OCTEON_IRQ_RML, &number_spi_ports);
+ }
+}
diff --git a/drivers/staging/octeon/ethernet-tx.c b/drivers/staging/octeon/ethernet-tx.c
new file mode 100644
index 000000000..5b9ac1f6d
--- /dev/null
+++ b/drivers/staging/octeon/ethernet-tx.c
@@ -0,0 +1,763 @@
+/*********************************************************************
+ * Author: Cavium Networks
+ *
+ * Contact: support@caviumnetworks.com
+ * This file is part of the OCTEON SDK
+ *
+ * Copyright (c) 2003-2010 Cavium Networks
+ *
+ * This file 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.
+ *
+ * This file is distributed in the hope that it will be useful, but
+ * AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or
+ * NONINFRINGEMENT. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this file; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ * or visit http://www.gnu.org/licenses/.
+ *
+ * This file may also be available under a different license from Cavium.
+ * Contact Cavium Networks for more information
+*********************************************************************/
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/ip.h>
+#include <linux/ratelimit.h>
+#include <linux/string.h>
+#include <linux/interrupt.h>
+#include <net/dst.h>
+#ifdef CONFIG_XFRM
+#include <linux/xfrm.h>
+#include <net/xfrm.h>
+#endif /* CONFIG_XFRM */
+
+#include <linux/atomic.h>
+
+#include <asm/octeon/octeon.h>
+
+#include "ethernet-defines.h"
+#include "octeon-ethernet.h"
+#include "ethernet-tx.h"
+#include "ethernet-util.h"
+
+#include <asm/octeon/cvmx-wqe.h>
+#include <asm/octeon/cvmx-fau.h>
+#include <asm/octeon/cvmx-pip.h>
+#include <asm/octeon/cvmx-pko.h>
+#include <asm/octeon/cvmx-helper.h>
+
+#include <asm/octeon/cvmx-gmxx-defs.h>
+
+#define CVM_OCT_SKB_CB(skb) ((u64 *)((skb)->cb))
+
+/*
+ * You can define GET_SKBUFF_QOS() to override how the skbuff output
+ * function determines which output queue is used. The default
+ * implementation always uses the base queue for the port. If, for
+ * example, you wanted to use the skb->priority field, define
+ * GET_SKBUFF_QOS as: #define GET_SKBUFF_QOS(skb) ((skb)->priority)
+ */
+#ifndef GET_SKBUFF_QOS
+#define GET_SKBUFF_QOS(skb) 0
+#endif
+
+static void cvm_oct_tx_do_cleanup(unsigned long arg);
+static DECLARE_TASKLET(cvm_oct_tx_cleanup_tasklet, cvm_oct_tx_do_cleanup, 0);
+
+/* Maximum number of SKBs to try to free per xmit packet. */
+#define MAX_SKB_TO_FREE (MAX_OUT_QUEUE_DEPTH * 2)
+
+static inline int32_t cvm_oct_adjust_skb_to_free(int32_t skb_to_free, int fau)
+{
+ int32_t undo;
+
+ undo = skb_to_free > 0 ? MAX_SKB_TO_FREE : skb_to_free +
+ MAX_SKB_TO_FREE;
+ if (undo > 0)
+ cvmx_fau_atomic_add32(fau, -undo);
+ skb_to_free = -skb_to_free > MAX_SKB_TO_FREE ? MAX_SKB_TO_FREE :
+ -skb_to_free;
+ return skb_to_free;
+}
+
+static void cvm_oct_kick_tx_poll_watchdog(void)
+{
+ union cvmx_ciu_timx ciu_timx;
+
+ ciu_timx.u64 = 0;
+ ciu_timx.s.one_shot = 1;
+ ciu_timx.s.len = cvm_oct_tx_poll_interval;
+ cvmx_write_csr(CVMX_CIU_TIMX(1), ciu_timx.u64);
+}
+
+static void cvm_oct_free_tx_skbs(struct net_device *dev)
+{
+ int32_t skb_to_free;
+ int qos, queues_per_port;
+ int total_freed = 0;
+ int total_remaining = 0;
+ unsigned long flags;
+ struct octeon_ethernet *priv = netdev_priv(dev);
+
+ queues_per_port = cvmx_pko_get_num_queues(priv->port);
+ /* Drain any pending packets in the free list */
+ for (qos = 0; qos < queues_per_port; qos++) {
+ if (skb_queue_len(&priv->tx_free_list[qos]) == 0)
+ continue;
+ skb_to_free = cvmx_fau_fetch_and_add32(priv->fau+qos*4,
+ MAX_SKB_TO_FREE);
+ skb_to_free = cvm_oct_adjust_skb_to_free(skb_to_free,
+ priv->fau+qos*4);
+
+
+ total_freed += skb_to_free;
+ if (skb_to_free > 0) {
+ struct sk_buff *to_free_list = NULL;
+
+ spin_lock_irqsave(&priv->tx_free_list[qos].lock, flags);
+ while (skb_to_free > 0) {
+ struct sk_buff *t;
+
+ t = __skb_dequeue(&priv->tx_free_list[qos]);
+ t->next = to_free_list;
+ to_free_list = t;
+ skb_to_free--;
+ }
+ spin_unlock_irqrestore(&priv->tx_free_list[qos].lock,
+ flags);
+ /* Do the actual freeing outside of the lock. */
+ while (to_free_list) {
+ struct sk_buff *t = to_free_list;
+
+ to_free_list = to_free_list->next;
+ dev_kfree_skb_any(t);
+ }
+ }
+ total_remaining += skb_queue_len(&priv->tx_free_list[qos]);
+ }
+ if (total_freed >= 0 && netif_queue_stopped(dev))
+ netif_wake_queue(dev);
+ if (total_remaining)
+ cvm_oct_kick_tx_poll_watchdog();
+}
+
+/**
+ * cvm_oct_xmit - transmit a packet
+ * @skb: Packet to send
+ * @dev: Device info structure
+ *
+ * Returns Always returns NETDEV_TX_OK
+ */
+int cvm_oct_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+ cvmx_pko_command_word0_t pko_command;
+ union cvmx_buf_ptr hw_buffer;
+ uint64_t old_scratch;
+ uint64_t old_scratch2;
+ int qos;
+ int i;
+ enum {QUEUE_CORE, QUEUE_HW, QUEUE_DROP} queue_type;
+ struct octeon_ethernet *priv = netdev_priv(dev);
+ struct sk_buff *to_free_list;
+ int32_t skb_to_free;
+ int32_t buffers_to_free;
+ u32 total_to_clean;
+ unsigned long flags;
+#if REUSE_SKBUFFS_WITHOUT_FREE
+ unsigned char *fpa_head;
+#endif
+
+ /*
+ * Prefetch the private data structure. It is larger than the
+ * one cache line.
+ */
+ prefetch(priv);
+
+ /*
+ * The check on CVMX_PKO_QUEUES_PER_PORT_* is designed to
+ * completely remove "qos" in the event neither interface
+ * supports multiple queues per port.
+ */
+ if ((CVMX_PKO_QUEUES_PER_PORT_INTERFACE0 > 1) ||
+ (CVMX_PKO_QUEUES_PER_PORT_INTERFACE1 > 1)) {
+ qos = GET_SKBUFF_QOS(skb);
+ if (qos <= 0)
+ qos = 0;
+ else if (qos >= cvmx_pko_get_num_queues(priv->port))
+ qos = 0;
+ } else
+ qos = 0;
+
+ if (USE_ASYNC_IOBDMA) {
+ /* Save scratch in case userspace is using it */
+ CVMX_SYNCIOBDMA;
+ old_scratch = cvmx_scratch_read64(CVMX_SCR_SCRATCH);
+ old_scratch2 = cvmx_scratch_read64(CVMX_SCR_SCRATCH + 8);
+
+ /*
+ * Fetch and increment the number of packets to be
+ * freed.
+ */
+ cvmx_fau_async_fetch_and_add32(CVMX_SCR_SCRATCH + 8,
+ FAU_NUM_PACKET_BUFFERS_TO_FREE,
+ 0);
+ cvmx_fau_async_fetch_and_add32(CVMX_SCR_SCRATCH,
+ priv->fau + qos * 4,
+ MAX_SKB_TO_FREE);
+ }
+
+ /*
+ * We have space for 6 segment pointers, If there will be more
+ * than that, we must linearize.
+ */
+ if (unlikely(skb_shinfo(skb)->nr_frags > 5)) {
+ if (unlikely(__skb_linearize(skb))) {
+ queue_type = QUEUE_DROP;
+ if (USE_ASYNC_IOBDMA) {
+ /*
+ * Get the number of skbuffs in use
+ * by the hardware
+ */
+ CVMX_SYNCIOBDMA;
+ skb_to_free =
+ cvmx_scratch_read64(CVMX_SCR_SCRATCH);
+ } else {
+ /*
+ * Get the number of skbuffs in use
+ * by the hardware
+ */
+ skb_to_free = cvmx_fau_fetch_and_add32(
+ priv->fau + qos * 4, MAX_SKB_TO_FREE);
+ }
+ skb_to_free = cvm_oct_adjust_skb_to_free(skb_to_free,
+ priv->fau + qos * 4);
+ spin_lock_irqsave(&priv->tx_free_list[qos].lock, flags);
+ goto skip_xmit;
+ }
+ }
+
+ /*
+ * The CN3XXX series of parts has an errata (GMX-401) which
+ * causes the GMX block to hang if a collision occurs towards
+ * the end of a <68 byte packet. As a workaround for this, we
+ * pad packets to be 68 bytes whenever we are in half duplex
+ * mode. We don't handle the case of having a small packet but
+ * no room to add the padding. The kernel should always give
+ * us at least a cache line
+ */
+ if ((skb->len < 64) && OCTEON_IS_MODEL(OCTEON_CN3XXX)) {
+ union cvmx_gmxx_prtx_cfg gmx_prt_cfg;
+ int interface = INTERFACE(priv->port);
+ int index = INDEX(priv->port);
+
+ if (interface < 2) {
+ /* We only need to pad packet in half duplex mode */
+ gmx_prt_cfg.u64 =
+ cvmx_read_csr(CVMX_GMXX_PRTX_CFG(index, interface));
+ if (gmx_prt_cfg.s.duplex == 0) {
+ int add_bytes = 64 - skb->len;
+
+ if ((skb_tail_pointer(skb) + add_bytes) <=
+ skb_end_pointer(skb))
+ memset(__skb_put(skb, add_bytes), 0,
+ add_bytes);
+ }
+ }
+ }
+
+ /* Build the PKO command */
+ pko_command.u64 = 0;
+#ifdef __LITTLE_ENDIAN
+ pko_command.s.le = 1;
+#endif
+ pko_command.s.n2 = 1; /* Don't pollute L2 with the outgoing packet */
+ pko_command.s.segs = 1;
+ pko_command.s.total_bytes = skb->len;
+ pko_command.s.size0 = CVMX_FAU_OP_SIZE_32;
+ pko_command.s.subone0 = 1;
+
+ pko_command.s.dontfree = 1;
+
+ /* Build the PKO buffer pointer */
+ hw_buffer.u64 = 0;
+ if (skb_shinfo(skb)->nr_frags == 0) {
+ hw_buffer.s.addr = XKPHYS_TO_PHYS((u64)skb->data);
+ hw_buffer.s.pool = 0;
+ hw_buffer.s.size = skb->len;
+ } else {
+ hw_buffer.s.addr = XKPHYS_TO_PHYS((u64)skb->data);
+ hw_buffer.s.pool = 0;
+ hw_buffer.s.size = skb_headlen(skb);
+ CVM_OCT_SKB_CB(skb)[0] = hw_buffer.u64;
+ for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
+ struct skb_frag_struct *fs = skb_shinfo(skb)->frags + i;
+
+ hw_buffer.s.addr = XKPHYS_TO_PHYS(
+ (u64)(page_address(fs->page.p) +
+ fs->page_offset));
+ hw_buffer.s.size = fs->size;
+ CVM_OCT_SKB_CB(skb)[i + 1] = hw_buffer.u64;
+ }
+ hw_buffer.s.addr = XKPHYS_TO_PHYS((u64)CVM_OCT_SKB_CB(skb));
+ hw_buffer.s.size = skb_shinfo(skb)->nr_frags + 1;
+ pko_command.s.segs = skb_shinfo(skb)->nr_frags + 1;
+ pko_command.s.gather = 1;
+ goto dont_put_skbuff_in_hw;
+ }
+
+ /*
+ * See if we can put this skb in the FPA pool. Any strange
+ * behavior from the Linux networking stack will most likely
+ * be caused by a bug in the following code. If some field is
+ * in use by the network stack and gets carried over when a
+ * buffer is reused, bad things may happen. If in doubt and
+ * you dont need the absolute best performance, disable the
+ * define REUSE_SKBUFFS_WITHOUT_FREE. The reuse of buffers has
+ * shown a 25% increase in performance under some loads.
+ */
+#if REUSE_SKBUFFS_WITHOUT_FREE
+ fpa_head = skb->head + 256 - ((unsigned long)skb->head & 0x7f);
+ if (unlikely(skb->data < fpa_head)) {
+ /*
+ * printk("TX buffer beginning can't meet FPA
+ * alignment constraints\n");
+ */
+ goto dont_put_skbuff_in_hw;
+ }
+ if (unlikely
+ ((skb_end_pointer(skb) - fpa_head) < CVMX_FPA_PACKET_POOL_SIZE)) {
+ /*
+ printk("TX buffer isn't large enough for the FPA\n");
+ */
+ goto dont_put_skbuff_in_hw;
+ }
+ if (unlikely(skb_shared(skb))) {
+ /*
+ printk("TX buffer sharing data with someone else\n");
+ */
+ goto dont_put_skbuff_in_hw;
+ }
+ if (unlikely(skb_cloned(skb))) {
+ /*
+ printk("TX buffer has been cloned\n");
+ */
+ goto dont_put_skbuff_in_hw;
+ }
+ if (unlikely(skb_header_cloned(skb))) {
+ /*
+ printk("TX buffer header has been cloned\n");
+ */
+ goto dont_put_skbuff_in_hw;
+ }
+ if (unlikely(skb->destructor)) {
+ /*
+ printk("TX buffer has a destructor\n");
+ */
+ goto dont_put_skbuff_in_hw;
+ }
+ if (unlikely(skb_shinfo(skb)->nr_frags)) {
+ /*
+ printk("TX buffer has fragments\n");
+ */
+ goto dont_put_skbuff_in_hw;
+ }
+ if (unlikely
+ (skb->truesize !=
+ sizeof(*skb) + skb_end_offset(skb))) {
+ /*
+ printk("TX buffer truesize has been changed\n");
+ */
+ goto dont_put_skbuff_in_hw;
+ }
+
+ /*
+ * We can use this buffer in the FPA. We don't need the FAU
+ * update anymore
+ */
+ pko_command.s.dontfree = 0;
+
+ hw_buffer.s.back = ((unsigned long)skb->data >> 7) -
+ ((unsigned long)fpa_head >> 7);
+
+ *(struct sk_buff **)(fpa_head - sizeof(void *)) = skb;
+
+ /*
+ * The skbuff will be reused without ever being freed. We must
+ * cleanup a bunch of core things.
+ */
+ dst_release(skb_dst(skb));
+ skb_dst_set(skb, NULL);
+#ifdef CONFIG_XFRM
+ secpath_put(skb->sp);
+ skb->sp = NULL;
+#endif
+ nf_reset(skb);
+
+#ifdef CONFIG_NET_SCHED
+ skb->tc_index = 0;
+#ifdef CONFIG_NET_CLS_ACT
+ skb->tc_verd = 0;
+#endif /* CONFIG_NET_CLS_ACT */
+#endif /* CONFIG_NET_SCHED */
+#endif /* REUSE_SKBUFFS_WITHOUT_FREE */
+
+dont_put_skbuff_in_hw:
+
+ /* Check if we can use the hardware checksumming */
+ if (USE_HW_TCPUDP_CHECKSUM && (skb->protocol == htons(ETH_P_IP)) &&
+ (ip_hdr(skb)->version == 4) && (ip_hdr(skb)->ihl == 5) &&
+ ((ip_hdr(skb)->frag_off == 0) || (ip_hdr(skb)->frag_off == htons(1 << 14)))
+ && ((ip_hdr(skb)->protocol == IPPROTO_TCP)
+ || (ip_hdr(skb)->protocol == IPPROTO_UDP))) {
+ /* Use hardware checksum calc */
+ pko_command.s.ipoffp1 = sizeof(struct ethhdr) + 1;
+ }
+
+ if (USE_ASYNC_IOBDMA) {
+ /* Get the number of skbuffs in use by the hardware */
+ CVMX_SYNCIOBDMA;
+ skb_to_free = cvmx_scratch_read64(CVMX_SCR_SCRATCH);
+ buffers_to_free = cvmx_scratch_read64(CVMX_SCR_SCRATCH + 8);
+ } else {
+ /* Get the number of skbuffs in use by the hardware */
+ skb_to_free = cvmx_fau_fetch_and_add32(priv->fau + qos * 4,
+ MAX_SKB_TO_FREE);
+ buffers_to_free =
+ cvmx_fau_fetch_and_add32(FAU_NUM_PACKET_BUFFERS_TO_FREE, 0);
+ }
+
+ skb_to_free = cvm_oct_adjust_skb_to_free(skb_to_free, priv->fau+qos*4);
+
+ /*
+ * If we're sending faster than the receive can free them then
+ * don't do the HW free.
+ */
+ if ((buffers_to_free < -100) && !pko_command.s.dontfree)
+ pko_command.s.dontfree = 1;
+
+ if (pko_command.s.dontfree) {
+ queue_type = QUEUE_CORE;
+ pko_command.s.reg0 = priv->fau+qos*4;
+ } else {
+ queue_type = QUEUE_HW;
+ }
+ if (USE_ASYNC_IOBDMA)
+ cvmx_fau_async_fetch_and_add32(
+ CVMX_SCR_SCRATCH, FAU_TOTAL_TX_TO_CLEAN, 1);
+
+ spin_lock_irqsave(&priv->tx_free_list[qos].lock, flags);
+
+ /* Drop this packet if we have too many already queued to the HW */
+ if (unlikely(skb_queue_len(&priv->tx_free_list[qos]) >=
+ MAX_OUT_QUEUE_DEPTH)) {
+
+ if (dev->tx_queue_len != 0) {
+ /* Drop the lock when notifying the core. */
+ spin_unlock_irqrestore(&priv->tx_free_list[qos].lock,
+ flags);
+ netif_stop_queue(dev);
+ spin_lock_irqsave(&priv->tx_free_list[qos].lock,
+ flags);
+ } else {
+ /* If not using normal queueing. */
+ queue_type = QUEUE_DROP;
+ goto skip_xmit;
+ }
+ }
+
+ cvmx_pko_send_packet_prepare(priv->port, priv->queue + qos,
+ CVMX_PKO_LOCK_NONE);
+
+ /* Send the packet to the output queue */
+ if (unlikely(cvmx_pko_send_packet_finish(priv->port,
+ priv->queue + qos,
+ pko_command, hw_buffer,
+ CVMX_PKO_LOCK_NONE))) {
+ printk_ratelimited("%s: Failed to send the packet\n",
+ dev->name);
+ queue_type = QUEUE_DROP;
+ }
+skip_xmit:
+ to_free_list = NULL;
+
+ switch (queue_type) {
+ case QUEUE_DROP:
+ skb->next = to_free_list;
+ to_free_list = skb;
+ priv->stats.tx_dropped++;
+ break;
+ case QUEUE_HW:
+ cvmx_fau_atomic_add32(FAU_NUM_PACKET_BUFFERS_TO_FREE, -1);
+ break;
+ case QUEUE_CORE:
+ __skb_queue_tail(&priv->tx_free_list[qos], skb);
+ break;
+ default:
+ BUG();
+ }
+
+ while (skb_to_free > 0) {
+ struct sk_buff *t = __skb_dequeue(&priv->tx_free_list[qos]);
+
+ t->next = to_free_list;
+ to_free_list = t;
+ skb_to_free--;
+ }
+
+ spin_unlock_irqrestore(&priv->tx_free_list[qos].lock, flags);
+
+ /* Do the actual freeing outside of the lock. */
+ while (to_free_list) {
+ struct sk_buff *t = to_free_list;
+
+ to_free_list = to_free_list->next;
+ dev_kfree_skb_any(t);
+ }
+
+ if (USE_ASYNC_IOBDMA) {
+ CVMX_SYNCIOBDMA;
+ total_to_clean = cvmx_scratch_read64(CVMX_SCR_SCRATCH);
+ /* Restore the scratch area */
+ cvmx_scratch_write64(CVMX_SCR_SCRATCH, old_scratch);
+ cvmx_scratch_write64(CVMX_SCR_SCRATCH + 8, old_scratch2);
+ } else {
+ total_to_clean = cvmx_fau_fetch_and_add32(
+ FAU_TOTAL_TX_TO_CLEAN, 1);
+ }
+
+ if (total_to_clean & 0x3ff) {
+ /*
+ * Schedule the cleanup tasklet every 1024 packets for
+ * the pathological case of high traffic on one port
+ * delaying clean up of packets on a different port
+ * that is blocked waiting for the cleanup.
+ */
+ tasklet_schedule(&cvm_oct_tx_cleanup_tasklet);
+ }
+
+ cvm_oct_kick_tx_poll_watchdog();
+
+ return NETDEV_TX_OK;
+}
+
+/**
+ * cvm_oct_xmit_pow - transmit a packet to the POW
+ * @skb: Packet to send
+ * @dev: Device info structure
+
+ * Returns Always returns zero
+ */
+int cvm_oct_xmit_pow(struct sk_buff *skb, struct net_device *dev)
+{
+ struct octeon_ethernet *priv = netdev_priv(dev);
+ void *packet_buffer;
+ void *copy_location;
+
+ /* Get a work queue entry */
+ cvmx_wqe_t *work = cvmx_fpa_alloc(CVMX_FPA_WQE_POOL);
+
+ if (unlikely(work == NULL)) {
+ printk_ratelimited("%s: Failed to allocate a work queue entry\n",
+ dev->name);
+ priv->stats.tx_dropped++;
+ dev_kfree_skb_any(skb);
+ return 0;
+ }
+
+ /* Get a packet buffer */
+ packet_buffer = cvmx_fpa_alloc(CVMX_FPA_PACKET_POOL);
+ if (unlikely(packet_buffer == NULL)) {
+ printk_ratelimited("%s: Failed to allocate a packet buffer\n",
+ dev->name);
+ cvmx_fpa_free(work, CVMX_FPA_WQE_POOL, DONT_WRITEBACK(1));
+ priv->stats.tx_dropped++;
+ dev_kfree_skb_any(skb);
+ return 0;
+ }
+
+ /*
+ * Calculate where we need to copy the data to. We need to
+ * leave 8 bytes for a next pointer (unused). We also need to
+ * include any configure skip. Then we need to align the IP
+ * packet src and dest into the same 64bit word. The below
+ * calculation may add a little extra, but that doesn't
+ * hurt.
+ */
+ copy_location = packet_buffer + sizeof(uint64_t);
+ copy_location += ((CVMX_HELPER_FIRST_MBUFF_SKIP + 7) & 0xfff8) + 6;
+
+ /*
+ * We have to copy the packet since whoever processes this
+ * packet will free it to a hardware pool. We can't use the
+ * trick of counting outstanding packets like in
+ * cvm_oct_xmit.
+ */
+ memcpy(copy_location, skb->data, skb->len);
+
+ /*
+ * Fill in some of the work queue fields. We may need to add
+ * more if the software at the other end needs them.
+ */
+ work->hw_chksum = skb->csum;
+ work->len = skb->len;
+ work->ipprt = priv->port;
+ work->qos = priv->port & 0x7;
+ work->grp = pow_send_group;
+ work->tag_type = CVMX_HELPER_INPUT_TAG_TYPE;
+ work->tag = pow_send_group; /* FIXME */
+ /* Default to zero. Sets of zero later are commented out */
+ work->word2.u64 = 0;
+ work->word2.s.bufs = 1;
+ work->packet_ptr.u64 = 0;
+ work->packet_ptr.s.addr = cvmx_ptr_to_phys(copy_location);
+ work->packet_ptr.s.pool = CVMX_FPA_PACKET_POOL;
+ work->packet_ptr.s.size = CVMX_FPA_PACKET_POOL_SIZE;
+ work->packet_ptr.s.back = (copy_location - packet_buffer) >> 7;
+
+ if (skb->protocol == htons(ETH_P_IP)) {
+ work->word2.s.ip_offset = 14;
+#if 0
+ work->word2.s.vlan_valid = 0; /* FIXME */
+ work->word2.s.vlan_cfi = 0; /* FIXME */
+ work->word2.s.vlan_id = 0; /* FIXME */
+ work->word2.s.dec_ipcomp = 0; /* FIXME */
+#endif
+ work->word2.s.tcp_or_udp =
+ (ip_hdr(skb)->protocol == IPPROTO_TCP)
+ || (ip_hdr(skb)->protocol == IPPROTO_UDP);
+#if 0
+ /* FIXME */
+ work->word2.s.dec_ipsec = 0;
+ /* We only support IPv4 right now */
+ work->word2.s.is_v6 = 0;
+ /* Hardware would set to zero */
+ work->word2.s.software = 0;
+ /* No error, packet is internal */
+ work->word2.s.L4_error = 0;
+#endif
+ work->word2.s.is_frag = !((ip_hdr(skb)->frag_off == 0)
+ || (ip_hdr(skb)->frag_off ==
+ 1 << 14));
+#if 0
+ /* Assume Linux is sending a good packet */
+ work->word2.s.IP_exc = 0;
+#endif
+ work->word2.s.is_bcast = (skb->pkt_type == PACKET_BROADCAST);
+ work->word2.s.is_mcast = (skb->pkt_type == PACKET_MULTICAST);
+#if 0
+ /* This is an IP packet */
+ work->word2.s.not_IP = 0;
+ /* No error, packet is internal */
+ work->word2.s.rcv_error = 0;
+ /* No error, packet is internal */
+ work->word2.s.err_code = 0;
+#endif
+
+ /*
+ * When copying the data, include 4 bytes of the
+ * ethernet header to align the same way hardware
+ * does.
+ */
+ memcpy(work->packet_data, skb->data + 10,
+ sizeof(work->packet_data));
+ } else {
+#if 0
+ work->word2.snoip.vlan_valid = 0; /* FIXME */
+ work->word2.snoip.vlan_cfi = 0; /* FIXME */
+ work->word2.snoip.vlan_id = 0; /* FIXME */
+ work->word2.snoip.software = 0; /* Hardware would set to zero */
+#endif
+ work->word2.snoip.is_rarp = skb->protocol == htons(ETH_P_RARP);
+ work->word2.snoip.is_arp = skb->protocol == htons(ETH_P_ARP);
+ work->word2.snoip.is_bcast =
+ (skb->pkt_type == PACKET_BROADCAST);
+ work->word2.snoip.is_mcast =
+ (skb->pkt_type == PACKET_MULTICAST);
+ work->word2.snoip.not_IP = 1; /* IP was done up above */
+#if 0
+ /* No error, packet is internal */
+ work->word2.snoip.rcv_error = 0;
+ /* No error, packet is internal */
+ work->word2.snoip.err_code = 0;
+#endif
+ memcpy(work->packet_data, skb->data, sizeof(work->packet_data));
+ }
+
+ /* Submit the packet to the POW */
+ cvmx_pow_work_submit(work, work->tag, work->tag_type, work->qos,
+ work->grp);
+ priv->stats.tx_packets++;
+ priv->stats.tx_bytes += skb->len;
+ dev_consume_skb_any(skb);
+ return 0;
+}
+
+/**
+ * cvm_oct_tx_shutdown_dev - free all skb that are currently queued for TX.
+ * @dev: Device being shutdown
+ *
+ */
+void cvm_oct_tx_shutdown_dev(struct net_device *dev)
+{
+ struct octeon_ethernet *priv = netdev_priv(dev);
+ unsigned long flags;
+ int qos;
+
+ for (qos = 0; qos < 16; qos++) {
+ spin_lock_irqsave(&priv->tx_free_list[qos].lock, flags);
+ while (skb_queue_len(&priv->tx_free_list[qos]))
+ dev_kfree_skb_any(__skb_dequeue
+ (&priv->tx_free_list[qos]));
+ spin_unlock_irqrestore(&priv->tx_free_list[qos].lock, flags);
+ }
+}
+
+static void cvm_oct_tx_do_cleanup(unsigned long arg)
+{
+ int port;
+
+ for (port = 0; port < TOTAL_NUMBER_OF_PORTS; port++) {
+ if (cvm_oct_device[port]) {
+ struct net_device *dev = cvm_oct_device[port];
+
+ cvm_oct_free_tx_skbs(dev);
+ }
+ }
+}
+
+static irqreturn_t cvm_oct_tx_cleanup_watchdog(int cpl, void *dev_id)
+{
+ /* Disable the interrupt. */
+ cvmx_write_csr(CVMX_CIU_TIMX(1), 0);
+ /* Do the work in the tasklet. */
+ tasklet_schedule(&cvm_oct_tx_cleanup_tasklet);
+ return IRQ_HANDLED;
+}
+
+void cvm_oct_tx_initialize(void)
+{
+ int i;
+
+ /* Disable the interrupt. */
+ cvmx_write_csr(CVMX_CIU_TIMX(1), 0);
+ /* Register an IRQ handler to receive CIU_TIMX(1) interrupts */
+ i = request_irq(OCTEON_IRQ_TIMER1,
+ cvm_oct_tx_cleanup_watchdog, 0,
+ "Ethernet", cvm_oct_device);
+
+ if (i)
+ panic("Could not acquire Ethernet IRQ %d\n", OCTEON_IRQ_TIMER1);
+}
+
+void cvm_oct_tx_shutdown(void)
+{
+ /* Free the interrupt handler */
+ free_irq(OCTEON_IRQ_TIMER1, cvm_oct_device);
+}
diff --git a/drivers/staging/octeon/ethernet-tx.h b/drivers/staging/octeon/ethernet-tx.h
new file mode 100644
index 000000000..547680c6c
--- /dev/null
+++ b/drivers/staging/octeon/ethernet-tx.h
@@ -0,0 +1,34 @@
+/*********************************************************************
+ * Author: Cavium Networks
+ *
+ * Contact: support@caviumnetworks.com
+ * This file is part of the OCTEON SDK
+ *
+ * Copyright (c) 2003-2007 Cavium Networks
+ *
+ * This file 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.
+ *
+ * This file is distributed in the hope that it will be useful, but
+ * AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or
+ * NONINFRINGEMENT. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this file; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ * or visit http://www.gnu.org/licenses/.
+ *
+ * This file may also be available under a different license from Cavium.
+ * Contact Cavium Networks for more information
+*********************************************************************/
+
+int cvm_oct_xmit(struct sk_buff *skb, struct net_device *dev);
+int cvm_oct_xmit_pow(struct sk_buff *skb, struct net_device *dev);
+int cvm_oct_transmit_qos(struct net_device *dev, void *work_queue_entry,
+ int do_free, int qos);
+void cvm_oct_tx_initialize(void);
+void cvm_oct_tx_shutdown(void);
+void cvm_oct_tx_shutdown_dev(struct net_device *dev);
diff --git a/drivers/staging/octeon/ethernet-util.h b/drivers/staging/octeon/ethernet-util.h
new file mode 100644
index 000000000..0f9b4a18f
--- /dev/null
+++ b/drivers/staging/octeon/ethernet-util.h
@@ -0,0 +1,70 @@
+/**********************************************************************
+ * Author: Cavium Networks
+ *
+ * Contact: support@caviumnetworks.com
+ * This file is part of the OCTEON SDK
+ *
+ * Copyright (c) 2003-2007 Cavium Networks
+ *
+ * This file 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.
+ *
+ * This file is distributed in the hope that it will be useful, but
+ * AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or
+ * NONINFRINGEMENT. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this file; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ * or visit http://www.gnu.org/licenses/.
+ *
+ * This file may also be available under a different license from Cavium.
+ * Contact Cavium Networks for more information
+*********************************************************************/
+
+/**
+ * cvm_oct_get_buffer_ptr - convert packet data address to pointer
+ * @packet_ptr: Packet data hardware address
+ *
+ * Returns Packet buffer pointer
+ */
+static inline void *cvm_oct_get_buffer_ptr(union cvmx_buf_ptr packet_ptr)
+{
+ return cvmx_phys_to_ptr(((packet_ptr.s.addr >> 7) - packet_ptr.s.back)
+ << 7);
+}
+
+/**
+ * INTERFACE - convert IPD port to logical interface
+ * @ipd_port: Port to check
+ *
+ * Returns Logical interface
+ */
+static inline int INTERFACE(int ipd_port)
+{
+ if (ipd_port < 32) /* Interface 0 or 1 for RGMII,GMII,SPI, etc */
+ return ipd_port >> 4;
+ else if (ipd_port < 36) /* Interface 2 for NPI */
+ return 2;
+ else if (ipd_port < 40) /* Interface 3 for loopback */
+ return 3;
+ else if (ipd_port == 40) /* Non existent interface for POW0 */
+ return 4;
+ panic("Illegal ipd_port %d passed to INTERFACE\n", ipd_port);
+}
+
+/**
+ * INDEX - convert IPD/PKO port number to the port's interface index
+ * @ipd_port: Port to check
+ *
+ * Returns Index into interface port list
+ */
+static inline int INDEX(int ipd_port)
+{
+ if (ipd_port < 32)
+ return ipd_port & 15;
+ return ipd_port & 3;
+}
diff --git a/drivers/staging/octeon/ethernet-xaui.c b/drivers/staging/octeon/ethernet-xaui.c
new file mode 100644
index 000000000..fd9d103d8
--- /dev/null
+++ b/drivers/staging/octeon/ethernet-xaui.c
@@ -0,0 +1,144 @@
+/**********************************************************************
+ * Author: Cavium Networks
+ *
+ * Contact: support@caviumnetworks.com
+ * This file is part of the OCTEON SDK
+ *
+ * Copyright (c) 2003-2007 Cavium Networks
+ *
+ * This file 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.
+ *
+ * This file is distributed in the hope that it will be useful, but
+ * AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or
+ * NONINFRINGEMENT. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this file; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ * or visit http://www.gnu.org/licenses/.
+ *
+ * This file may also be available under a different license from Cavium.
+ * Contact Cavium Networks for more information
+**********************************************************************/
+#include <linux/phy.h>
+#include <linux/kernel.h>
+#include <linux/netdevice.h>
+#include <linux/ratelimit.h>
+#include <net/dst.h>
+
+#include <asm/octeon/octeon.h>
+
+#include "ethernet-defines.h"
+#include "octeon-ethernet.h"
+#include "ethernet-util.h"
+#include "ethernet-mdio.h"
+
+#include <asm/octeon/cvmx-helper.h>
+
+#include <asm/octeon/cvmx-gmxx-defs.h>
+
+static void cvm_oct_xaui_poll(struct net_device *dev)
+{
+ struct octeon_ethernet *priv = netdev_priv(dev);
+ cvmx_helper_link_info_t link_info;
+
+ link_info = cvmx_helper_link_get(priv->port);
+ if (link_info.u64 == priv->link_info)
+ return;
+
+ link_info = cvmx_helper_link_autoconf(priv->port);
+ priv->link_info = link_info.u64;
+
+ /* Tell Linux */
+ if (link_info.s.link_up) {
+
+ if (!netif_carrier_ok(dev))
+ netif_carrier_on(dev);
+ if (priv->queue != -1)
+ printk_ratelimited
+ ("%s: %u Mbps %s duplex, port %2d, queue %2d\n",
+ dev->name, link_info.s.speed,
+ (link_info.s.full_duplex) ? "Full" : "Half",
+ priv->port, priv->queue);
+ else
+ printk_ratelimited
+ ("%s: %u Mbps %s duplex, port %2d, POW\n",
+ dev->name, link_info.s.speed,
+ (link_info.s.full_duplex) ? "Full" : "Half",
+ priv->port);
+ } else {
+ if (netif_carrier_ok(dev))
+ netif_carrier_off(dev);
+ printk_ratelimited("%s: Link down\n", dev->name);
+ }
+}
+
+int cvm_oct_xaui_open(struct net_device *dev)
+{
+ union cvmx_gmxx_prtx_cfg gmx_cfg;
+ struct octeon_ethernet *priv = netdev_priv(dev);
+ int interface = INTERFACE(priv->port);
+ int index = INDEX(priv->port);
+ cvmx_helper_link_info_t link_info;
+ int rv;
+
+ rv = cvm_oct_phy_setup_device(dev);
+ if (rv)
+ return rv;
+
+ gmx_cfg.u64 = cvmx_read_csr(CVMX_GMXX_PRTX_CFG(index, interface));
+ gmx_cfg.s.en = 1;
+ cvmx_write_csr(CVMX_GMXX_PRTX_CFG(index, interface), gmx_cfg.u64);
+
+ if (octeon_is_simulation())
+ return 0;
+
+ if (priv->phydev) {
+ int r = phy_read_status(priv->phydev);
+
+ if (r == 0 && priv->phydev->link == 0)
+ netif_carrier_off(dev);
+ cvm_oct_adjust_link(dev);
+ } else {
+ link_info = cvmx_helper_link_get(priv->port);
+ if (!link_info.s.link_up)
+ netif_carrier_off(dev);
+ priv->poll = cvm_oct_xaui_poll;
+ cvm_oct_xaui_poll(dev);
+ }
+ return 0;
+}
+
+int cvm_oct_xaui_stop(struct net_device *dev)
+{
+ union cvmx_gmxx_prtx_cfg gmx_cfg;
+ struct octeon_ethernet *priv = netdev_priv(dev);
+ int interface = INTERFACE(priv->port);
+ int index = INDEX(priv->port);
+
+ gmx_cfg.u64 = cvmx_read_csr(CVMX_GMXX_PRTX_CFG(index, interface));
+ gmx_cfg.s.en = 0;
+ cvmx_write_csr(CVMX_GMXX_PRTX_CFG(index, interface), gmx_cfg.u64);
+ return cvm_oct_common_stop(dev);
+}
+
+int cvm_oct_xaui_init(struct net_device *dev)
+{
+ struct octeon_ethernet *priv = netdev_priv(dev);
+
+ cvm_oct_common_init(dev);
+ dev->netdev_ops->ndo_stop(dev);
+ if (!octeon_is_simulation() && priv->phydev == NULL)
+ priv->poll = cvm_oct_xaui_poll;
+
+ return 0;
+}
+
+void cvm_oct_xaui_uninit(struct net_device *dev)
+{
+ cvm_oct_common_uninit(dev);
+}
diff --git a/drivers/staging/octeon/ethernet.c b/drivers/staging/octeon/ethernet.c
new file mode 100644
index 000000000..fbbe86648
--- /dev/null
+++ b/drivers/staging/octeon/ethernet.c
@@ -0,0 +1,891 @@
+/**********************************************************************
+ * Author: Cavium Networks
+ *
+ * Contact: support@caviumnetworks.com
+ * This file is part of the OCTEON SDK
+ *
+ * Copyright (c) 2003-2007 Cavium Networks
+ *
+ * This file 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.
+ *
+ * This file is distributed in the hope that it will be useful, but
+ * AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or
+ * NONINFRINGEMENT. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this file; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ * or visit http://www.gnu.org/licenses/.
+ *
+ * This file may also be available under a different license from Cavium.
+ * Contact Cavium Networks for more information
+**********************************************************************/
+#include <linux/platform_device.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/phy.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/of_net.h>
+
+#include <net/dst.h>
+
+#include <asm/octeon/octeon.h>
+
+#include "ethernet-defines.h"
+#include "octeon-ethernet.h"
+#include "ethernet-mem.h"
+#include "ethernet-rx.h"
+#include "ethernet-tx.h"
+#include "ethernet-mdio.h"
+#include "ethernet-util.h"
+
+#include <asm/octeon/cvmx-pip.h>
+#include <asm/octeon/cvmx-pko.h>
+#include <asm/octeon/cvmx-fau.h>
+#include <asm/octeon/cvmx-ipd.h>
+#include <asm/octeon/cvmx-helper.h>
+
+#include <asm/octeon/cvmx-gmxx-defs.h>
+#include <asm/octeon/cvmx-smix-defs.h>
+
+static int num_packet_buffers = 1024;
+module_param(num_packet_buffers, int, 0444);
+MODULE_PARM_DESC(num_packet_buffers, "\n"
+ "\tNumber of packet buffers to allocate and store in the\n"
+ "\tFPA. By default, 1024 packet buffers are used.\n");
+
+int pow_receive_group = 15;
+module_param(pow_receive_group, int, 0444);
+MODULE_PARM_DESC(pow_receive_group, "\n"
+ "\tPOW group to receive packets from. All ethernet hardware\n"
+ "\twill be configured to send incoming packets to this POW\n"
+ "\tgroup. Also any other software can submit packets to this\n"
+ "\tgroup for the kernel to process.");
+
+int pow_send_group = -1;
+module_param(pow_send_group, int, 0644);
+MODULE_PARM_DESC(pow_send_group, "\n"
+ "\tPOW group to send packets to other software on. This\n"
+ "\tcontrols the creation of the virtual device pow0.\n"
+ "\talways_use_pow also depends on this value.");
+
+int always_use_pow;
+module_param(always_use_pow, int, 0444);
+MODULE_PARM_DESC(always_use_pow, "\n"
+ "\tWhen set, always send to the pow group. This will cause\n"
+ "\tpackets sent to real ethernet devices to be sent to the\n"
+ "\tPOW group instead of the hardware. Unless some other\n"
+ "\tapplication changes the config, packets will still be\n"
+ "\treceived from the low level hardware. Use this option\n"
+ "\tto allow a CVMX app to intercept all packets from the\n"
+ "\tlinux kernel. You must specify pow_send_group along with\n"
+ "\tthis option.");
+
+char pow_send_list[128] = "";
+module_param_string(pow_send_list, pow_send_list, sizeof(pow_send_list), 0444);
+MODULE_PARM_DESC(pow_send_list, "\n"
+ "\tComma separated list of ethernet devices that should use the\n"
+ "\tPOW for transmit instead of the actual ethernet hardware. This\n"
+ "\tis a per port version of always_use_pow. always_use_pow takes\n"
+ "\tprecedence over this list. For example, setting this to\n"
+ "\t\"eth2,spi3,spi7\" would cause these three devices to transmit\n"
+ "\tusing the pow_send_group.");
+
+int rx_napi_weight = 32;
+module_param(rx_napi_weight, int, 0444);
+MODULE_PARM_DESC(rx_napi_weight, "The NAPI WEIGHT parameter.");
+
+/**
+ * cvm_oct_poll_queue - Workqueue for polling operations.
+ */
+struct workqueue_struct *cvm_oct_poll_queue;
+
+/**
+ * cvm_oct_poll_queue_stopping - flag to indicate polling should stop.
+ *
+ * Set to one right before cvm_oct_poll_queue is destroyed.
+ */
+atomic_t cvm_oct_poll_queue_stopping = ATOMIC_INIT(0);
+
+/**
+ * Array of every ethernet device owned by this driver indexed by
+ * the ipd input port number.
+ */
+struct net_device *cvm_oct_device[TOTAL_NUMBER_OF_PORTS];
+
+u64 cvm_oct_tx_poll_interval;
+
+static void cvm_oct_rx_refill_worker(struct work_struct *work);
+static DECLARE_DELAYED_WORK(cvm_oct_rx_refill_work, cvm_oct_rx_refill_worker);
+
+static void cvm_oct_rx_refill_worker(struct work_struct *work)
+{
+ /*
+ * FPA 0 may have been drained, try to refill it if we need
+ * more than num_packet_buffers / 2, otherwise normal receive
+ * processing will refill it. If it were drained, no packets
+ * could be received so cvm_oct_napi_poll would never be
+ * invoked to do the refill.
+ */
+ cvm_oct_rx_refill_pool(num_packet_buffers / 2);
+
+ if (!atomic_read(&cvm_oct_poll_queue_stopping))
+ queue_delayed_work(cvm_oct_poll_queue,
+ &cvm_oct_rx_refill_work, HZ);
+}
+
+static void cvm_oct_periodic_worker(struct work_struct *work)
+{
+ struct octeon_ethernet *priv = container_of(work,
+ struct octeon_ethernet,
+ port_periodic_work.work);
+
+ if (priv->poll)
+ priv->poll(cvm_oct_device[priv->port]);
+
+ cvm_oct_device[priv->port]->netdev_ops->ndo_get_stats(
+ cvm_oct_device[priv->port]);
+
+ if (!atomic_read(&cvm_oct_poll_queue_stopping))
+ queue_delayed_work(cvm_oct_poll_queue,
+ &priv->port_periodic_work, HZ);
+}
+
+static void cvm_oct_configure_common_hw(void)
+{
+ /* Setup the FPA */
+ cvmx_fpa_enable();
+ cvm_oct_mem_fill_fpa(CVMX_FPA_PACKET_POOL, CVMX_FPA_PACKET_POOL_SIZE,
+ num_packet_buffers);
+ cvm_oct_mem_fill_fpa(CVMX_FPA_WQE_POOL, CVMX_FPA_WQE_POOL_SIZE,
+ num_packet_buffers);
+ if (CVMX_FPA_OUTPUT_BUFFER_POOL != CVMX_FPA_PACKET_POOL)
+ cvm_oct_mem_fill_fpa(CVMX_FPA_OUTPUT_BUFFER_POOL,
+ CVMX_FPA_OUTPUT_BUFFER_POOL_SIZE, 128);
+
+#ifdef __LITTLE_ENDIAN
+ {
+ union cvmx_ipd_ctl_status ipd_ctl_status;
+ ipd_ctl_status.u64 = cvmx_read_csr(CVMX_IPD_CTL_STATUS);
+ ipd_ctl_status.s.pkt_lend = 1;
+ ipd_ctl_status.s.wqe_lend = 1;
+ cvmx_write_csr(CVMX_IPD_CTL_STATUS, ipd_ctl_status.u64);
+ }
+#endif
+
+ if (USE_RED)
+ cvmx_helper_setup_red(num_packet_buffers / 4,
+ num_packet_buffers / 8);
+
+}
+
+/**
+ * cvm_oct_free_work- Free a work queue entry
+ *
+ * @work_queue_entry: Work queue entry to free
+ *
+ * Returns Zero on success, Negative on failure.
+ */
+int cvm_oct_free_work(void *work_queue_entry)
+{
+ cvmx_wqe_t *work = work_queue_entry;
+
+ int segments = work->word2.s.bufs;
+ union cvmx_buf_ptr segment_ptr = work->packet_ptr;
+
+ while (segments--) {
+ union cvmx_buf_ptr next_ptr = *(union cvmx_buf_ptr *)
+ cvmx_phys_to_ptr(segment_ptr.s.addr - 8);
+ if (unlikely(!segment_ptr.s.i))
+ cvmx_fpa_free(cvm_oct_get_buffer_ptr(segment_ptr),
+ segment_ptr.s.pool,
+ DONT_WRITEBACK(CVMX_FPA_PACKET_POOL_SIZE /
+ 128));
+ segment_ptr = next_ptr;
+ }
+ cvmx_fpa_free(work, CVMX_FPA_WQE_POOL, DONT_WRITEBACK(1));
+
+ return 0;
+}
+EXPORT_SYMBOL(cvm_oct_free_work);
+
+/**
+ * cvm_oct_common_get_stats - get the low level ethernet statistics
+ * @dev: Device to get the statistics from
+ *
+ * Returns Pointer to the statistics
+ */
+static struct net_device_stats *cvm_oct_common_get_stats(struct net_device *dev)
+{
+ cvmx_pip_port_status_t rx_status;
+ cvmx_pko_port_status_t tx_status;
+ struct octeon_ethernet *priv = netdev_priv(dev);
+
+ if (priv->port < CVMX_PIP_NUM_INPUT_PORTS) {
+ if (octeon_is_simulation()) {
+ /* The simulator doesn't support statistics */
+ memset(&rx_status, 0, sizeof(rx_status));
+ memset(&tx_status, 0, sizeof(tx_status));
+ } else {
+ cvmx_pip_get_port_status(priv->port, 1, &rx_status);
+ cvmx_pko_get_port_status(priv->port, 1, &tx_status);
+ }
+
+ priv->stats.rx_packets += rx_status.inb_packets;
+ priv->stats.tx_packets += tx_status.packets;
+ priv->stats.rx_bytes += rx_status.inb_octets;
+ priv->stats.tx_bytes += tx_status.octets;
+ priv->stats.multicast += rx_status.multicast_packets;
+ priv->stats.rx_crc_errors += rx_status.inb_errors;
+ priv->stats.rx_frame_errors += rx_status.fcs_align_err_packets;
+
+ /*
+ * The drop counter must be incremented atomically
+ * since the RX tasklet also increments it.
+ */
+#ifdef CONFIG_64BIT
+ atomic64_add(rx_status.dropped_packets,
+ (atomic64_t *)&priv->stats.rx_dropped);
+#else
+ atomic_add(rx_status.dropped_packets,
+ (atomic_t *)&priv->stats.rx_dropped);
+#endif
+ }
+
+ return &priv->stats;
+}
+
+/**
+ * cvm_oct_common_change_mtu - change the link MTU
+ * @dev: Device to change
+ * @new_mtu: The new MTU
+ *
+ * Returns Zero on success
+ */
+static int cvm_oct_common_change_mtu(struct net_device *dev, int new_mtu)
+{
+ struct octeon_ethernet *priv = netdev_priv(dev);
+ int interface = INTERFACE(priv->port);
+ int index = INDEX(priv->port);
+#if defined(CONFIG_VLAN_8021Q) || defined(CONFIG_VLAN_8021Q_MODULE)
+ int vlan_bytes = 4;
+#else
+ int vlan_bytes = 0;
+#endif
+
+ /*
+ * Limit the MTU to make sure the ethernet packets are between
+ * 64 bytes and 65535 bytes.
+ */
+ if ((new_mtu + 14 + 4 + vlan_bytes < 64)
+ || (new_mtu + 14 + 4 + vlan_bytes > 65392)) {
+ pr_err("MTU must be between %d and %d.\n",
+ 64 - 14 - 4 - vlan_bytes, 65392 - 14 - 4 - vlan_bytes);
+ return -EINVAL;
+ }
+ dev->mtu = new_mtu;
+
+ if ((interface < 2)
+ && (cvmx_helper_interface_get_mode(interface) !=
+ CVMX_HELPER_INTERFACE_MODE_SPI)) {
+ /* Add ethernet header and FCS, and VLAN if configured. */
+ int max_packet = new_mtu + 14 + 4 + vlan_bytes;
+
+ if (OCTEON_IS_MODEL(OCTEON_CN3XXX)
+ || OCTEON_IS_MODEL(OCTEON_CN58XX)) {
+ /* Signal errors on packets larger than the MTU */
+ cvmx_write_csr(CVMX_GMXX_RXX_FRM_MAX(index, interface),
+ max_packet);
+ } else {
+ /*
+ * Set the hardware to truncate packets larger
+ * than the MTU and smaller the 64 bytes.
+ */
+ union cvmx_pip_frm_len_chkx frm_len_chk;
+
+ frm_len_chk.u64 = 0;
+ frm_len_chk.s.minlen = 64;
+ frm_len_chk.s.maxlen = max_packet;
+ cvmx_write_csr(CVMX_PIP_FRM_LEN_CHKX(interface),
+ frm_len_chk.u64);
+ }
+ /*
+ * Set the hardware to truncate packets larger than
+ * the MTU. The jabber register must be set to a
+ * multiple of 8 bytes, so round up.
+ */
+ cvmx_write_csr(CVMX_GMXX_RXX_JABBER(index, interface),
+ (max_packet + 7) & ~7u);
+ }
+ return 0;
+}
+
+/**
+ * cvm_oct_common_set_multicast_list - set the multicast list
+ * @dev: Device to work on
+ */
+static void cvm_oct_common_set_multicast_list(struct net_device *dev)
+{
+ union cvmx_gmxx_prtx_cfg gmx_cfg;
+ struct octeon_ethernet *priv = netdev_priv(dev);
+ int interface = INTERFACE(priv->port);
+ int index = INDEX(priv->port);
+
+ if ((interface < 2)
+ && (cvmx_helper_interface_get_mode(interface) !=
+ CVMX_HELPER_INTERFACE_MODE_SPI)) {
+ union cvmx_gmxx_rxx_adr_ctl control;
+
+ control.u64 = 0;
+ control.s.bcst = 1; /* Allow broadcast MAC addresses */
+
+ if (!netdev_mc_empty(dev) || (dev->flags & IFF_ALLMULTI) ||
+ (dev->flags & IFF_PROMISC))
+ /* Force accept multicast packets */
+ control.s.mcst = 2;
+ else
+ /* Force reject multicast packets */
+ control.s.mcst = 1;
+
+ if (dev->flags & IFF_PROMISC)
+ /*
+ * Reject matches if promisc. Since CAM is
+ * shut off, should accept everything.
+ */
+ control.s.cam_mode = 0;
+ else
+ /* Filter packets based on the CAM */
+ control.s.cam_mode = 1;
+
+ gmx_cfg.u64 =
+ cvmx_read_csr(CVMX_GMXX_PRTX_CFG(index, interface));
+ cvmx_write_csr(CVMX_GMXX_PRTX_CFG(index, interface),
+ gmx_cfg.u64 & ~1ull);
+
+ cvmx_write_csr(CVMX_GMXX_RXX_ADR_CTL(index, interface),
+ control.u64);
+ if (dev->flags & IFF_PROMISC)
+ cvmx_write_csr(CVMX_GMXX_RXX_ADR_CAM_EN
+ (index, interface), 0);
+ else
+ cvmx_write_csr(CVMX_GMXX_RXX_ADR_CAM_EN
+ (index, interface), 1);
+
+ cvmx_write_csr(CVMX_GMXX_PRTX_CFG(index, interface),
+ gmx_cfg.u64);
+ }
+}
+
+/**
+ * cvm_oct_common_set_mac_address - set the hardware MAC address for a device
+ * @dev: The device in question.
+ * @addr: Address structure to change it too.
+
+ * Returns Zero on success
+ */
+static int cvm_oct_set_mac_filter(struct net_device *dev)
+{
+ struct octeon_ethernet *priv = netdev_priv(dev);
+ union cvmx_gmxx_prtx_cfg gmx_cfg;
+ int interface = INTERFACE(priv->port);
+ int index = INDEX(priv->port);
+
+ if ((interface < 2)
+ && (cvmx_helper_interface_get_mode(interface) !=
+ CVMX_HELPER_INTERFACE_MODE_SPI)) {
+ int i;
+ uint8_t *ptr = dev->dev_addr;
+ uint64_t mac = 0;
+
+ for (i = 0; i < 6; i++)
+ mac = (mac << 8) | (uint64_t)ptr[i];
+
+ gmx_cfg.u64 =
+ cvmx_read_csr(CVMX_GMXX_PRTX_CFG(index, interface));
+ cvmx_write_csr(CVMX_GMXX_PRTX_CFG(index, interface),
+ gmx_cfg.u64 & ~1ull);
+
+ cvmx_write_csr(CVMX_GMXX_SMACX(index, interface), mac);
+ cvmx_write_csr(CVMX_GMXX_RXX_ADR_CAM0(index, interface),
+ ptr[0]);
+ cvmx_write_csr(CVMX_GMXX_RXX_ADR_CAM1(index, interface),
+ ptr[1]);
+ cvmx_write_csr(CVMX_GMXX_RXX_ADR_CAM2(index, interface),
+ ptr[2]);
+ cvmx_write_csr(CVMX_GMXX_RXX_ADR_CAM3(index, interface),
+ ptr[3]);
+ cvmx_write_csr(CVMX_GMXX_RXX_ADR_CAM4(index, interface),
+ ptr[4]);
+ cvmx_write_csr(CVMX_GMXX_RXX_ADR_CAM5(index, interface),
+ ptr[5]);
+ cvm_oct_common_set_multicast_list(dev);
+ cvmx_write_csr(CVMX_GMXX_PRTX_CFG(index, interface),
+ gmx_cfg.u64);
+ }
+ return 0;
+}
+
+static int cvm_oct_common_set_mac_address(struct net_device *dev, void *addr)
+{
+ int r = eth_mac_addr(dev, addr);
+
+ if (r)
+ return r;
+ return cvm_oct_set_mac_filter(dev);
+}
+
+/**
+ * cvm_oct_common_init - per network device initialization
+ * @dev: Device to initialize
+ *
+ * Returns Zero on success
+ */
+int cvm_oct_common_init(struct net_device *dev)
+{
+ struct octeon_ethernet *priv = netdev_priv(dev);
+ const u8 *mac = NULL;
+
+ if (priv->of_node)
+ mac = of_get_mac_address(priv->of_node);
+
+ if (mac)
+ ether_addr_copy(dev->dev_addr, mac);
+ else
+ eth_hw_addr_random(dev);
+
+ /*
+ * Force the interface to use the POW send if always_use_pow
+ * was specified or it is in the pow send list.
+ */
+ if ((pow_send_group != -1)
+ && (always_use_pow || strstr(pow_send_list, dev->name)))
+ priv->queue = -1;
+
+ if (priv->queue != -1) {
+ dev->features |= NETIF_F_SG;
+ if (USE_HW_TCPUDP_CHECKSUM)
+ dev->features |= NETIF_F_IP_CSUM;
+ }
+
+ /* We do our own locking, Linux doesn't need to */
+ dev->features |= NETIF_F_LLTX;
+ dev->ethtool_ops = &cvm_oct_ethtool_ops;
+
+ cvm_oct_set_mac_filter(dev);
+ dev->netdev_ops->ndo_change_mtu(dev, dev->mtu);
+
+ /*
+ * Zero out stats for port so we won't mistakenly show
+ * counters from the bootloader.
+ */
+ memset(dev->netdev_ops->ndo_get_stats(dev), 0,
+ sizeof(struct net_device_stats));
+
+ return 0;
+}
+
+void cvm_oct_common_uninit(struct net_device *dev)
+{
+ struct octeon_ethernet *priv = netdev_priv(dev);
+
+ if (priv->phydev)
+ phy_disconnect(priv->phydev);
+}
+
+static const struct net_device_ops cvm_oct_npi_netdev_ops = {
+ .ndo_init = cvm_oct_common_init,
+ .ndo_uninit = cvm_oct_common_uninit,
+ .ndo_start_xmit = cvm_oct_xmit,
+ .ndo_set_rx_mode = cvm_oct_common_set_multicast_list,
+ .ndo_set_mac_address = cvm_oct_common_set_mac_address,
+ .ndo_do_ioctl = cvm_oct_ioctl,
+ .ndo_change_mtu = cvm_oct_common_change_mtu,
+ .ndo_get_stats = cvm_oct_common_get_stats,
+#ifdef CONFIG_NET_POLL_CONTROLLER
+ .ndo_poll_controller = cvm_oct_poll_controller,
+#endif
+};
+static const struct net_device_ops cvm_oct_xaui_netdev_ops = {
+ .ndo_init = cvm_oct_xaui_init,
+ .ndo_uninit = cvm_oct_xaui_uninit,
+ .ndo_open = cvm_oct_xaui_open,
+ .ndo_stop = cvm_oct_xaui_stop,
+ .ndo_start_xmit = cvm_oct_xmit,
+ .ndo_set_rx_mode = cvm_oct_common_set_multicast_list,
+ .ndo_set_mac_address = cvm_oct_common_set_mac_address,
+ .ndo_do_ioctl = cvm_oct_ioctl,
+ .ndo_change_mtu = cvm_oct_common_change_mtu,
+ .ndo_get_stats = cvm_oct_common_get_stats,
+#ifdef CONFIG_NET_POLL_CONTROLLER
+ .ndo_poll_controller = cvm_oct_poll_controller,
+#endif
+};
+static const struct net_device_ops cvm_oct_sgmii_netdev_ops = {
+ .ndo_init = cvm_oct_sgmii_init,
+ .ndo_uninit = cvm_oct_sgmii_uninit,
+ .ndo_open = cvm_oct_sgmii_open,
+ .ndo_stop = cvm_oct_sgmii_stop,
+ .ndo_start_xmit = cvm_oct_xmit,
+ .ndo_set_rx_mode = cvm_oct_common_set_multicast_list,
+ .ndo_set_mac_address = cvm_oct_common_set_mac_address,
+ .ndo_do_ioctl = cvm_oct_ioctl,
+ .ndo_change_mtu = cvm_oct_common_change_mtu,
+ .ndo_get_stats = cvm_oct_common_get_stats,
+#ifdef CONFIG_NET_POLL_CONTROLLER
+ .ndo_poll_controller = cvm_oct_poll_controller,
+#endif
+};
+static const struct net_device_ops cvm_oct_spi_netdev_ops = {
+ .ndo_init = cvm_oct_spi_init,
+ .ndo_uninit = cvm_oct_spi_uninit,
+ .ndo_start_xmit = cvm_oct_xmit,
+ .ndo_set_rx_mode = cvm_oct_common_set_multicast_list,
+ .ndo_set_mac_address = cvm_oct_common_set_mac_address,
+ .ndo_do_ioctl = cvm_oct_ioctl,
+ .ndo_change_mtu = cvm_oct_common_change_mtu,
+ .ndo_get_stats = cvm_oct_common_get_stats,
+#ifdef CONFIG_NET_POLL_CONTROLLER
+ .ndo_poll_controller = cvm_oct_poll_controller,
+#endif
+};
+static const struct net_device_ops cvm_oct_rgmii_netdev_ops = {
+ .ndo_init = cvm_oct_rgmii_init,
+ .ndo_uninit = cvm_oct_rgmii_uninit,
+ .ndo_open = cvm_oct_rgmii_open,
+ .ndo_stop = cvm_oct_rgmii_stop,
+ .ndo_start_xmit = cvm_oct_xmit,
+ .ndo_set_rx_mode = cvm_oct_common_set_multicast_list,
+ .ndo_set_mac_address = cvm_oct_common_set_mac_address,
+ .ndo_do_ioctl = cvm_oct_ioctl,
+ .ndo_change_mtu = cvm_oct_common_change_mtu,
+ .ndo_get_stats = cvm_oct_common_get_stats,
+#ifdef CONFIG_NET_POLL_CONTROLLER
+ .ndo_poll_controller = cvm_oct_poll_controller,
+#endif
+};
+static const struct net_device_ops cvm_oct_pow_netdev_ops = {
+ .ndo_init = cvm_oct_common_init,
+ .ndo_start_xmit = cvm_oct_xmit_pow,
+ .ndo_set_rx_mode = cvm_oct_common_set_multicast_list,
+ .ndo_set_mac_address = cvm_oct_common_set_mac_address,
+ .ndo_do_ioctl = cvm_oct_ioctl,
+ .ndo_change_mtu = cvm_oct_common_change_mtu,
+ .ndo_get_stats = cvm_oct_common_get_stats,
+#ifdef CONFIG_NET_POLL_CONTROLLER
+ .ndo_poll_controller = cvm_oct_poll_controller,
+#endif
+};
+
+static struct device_node *cvm_oct_of_get_child(
+ const struct device_node *parent, int reg_val)
+{
+ struct device_node *node = NULL;
+ int size;
+ const __be32 *addr;
+
+ for (;;) {
+ node = of_get_next_child(parent, node);
+ if (!node)
+ break;
+ addr = of_get_property(node, "reg", &size);
+ if (addr && (be32_to_cpu(*addr) == reg_val))
+ break;
+ }
+ return node;
+}
+
+static struct device_node *cvm_oct_node_for_port(struct device_node *pip,
+ int interface, int port)
+{
+ struct device_node *ni, *np;
+
+ ni = cvm_oct_of_get_child(pip, interface);
+ if (!ni)
+ return NULL;
+
+ np = cvm_oct_of_get_child(ni, port);
+ of_node_put(ni);
+
+ return np;
+}
+
+static int cvm_oct_probe(struct platform_device *pdev)
+{
+ int num_interfaces;
+ int interface;
+ int fau = FAU_NUM_PACKET_BUFFERS_TO_FREE;
+ int qos;
+ struct device_node *pip;
+
+ octeon_mdiobus_force_mod_depencency();
+ pr_notice("cavium-ethernet %s\n", OCTEON_ETHERNET_VERSION);
+
+ pip = pdev->dev.of_node;
+ if (!pip) {
+ pr_err("Error: No 'pip' in /aliases\n");
+ return -EINVAL;
+ }
+
+ cvm_oct_poll_queue = create_singlethread_workqueue("octeon-ethernet");
+ if (cvm_oct_poll_queue == NULL) {
+ pr_err("octeon-ethernet: Cannot create workqueue");
+ return -ENOMEM;
+ }
+
+ cvm_oct_configure_common_hw();
+
+ cvmx_helper_initialize_packet_io_global();
+
+ /* Change the input group for all ports before input is enabled */
+ num_interfaces = cvmx_helper_get_number_of_interfaces();
+ for (interface = 0; interface < num_interfaces; interface++) {
+ int num_ports = cvmx_helper_ports_on_interface(interface);
+ int port;
+
+ for (port = cvmx_helper_get_ipd_port(interface, 0);
+ port < cvmx_helper_get_ipd_port(interface, num_ports);
+ port++) {
+ union cvmx_pip_prt_tagx pip_prt_tagx;
+
+ pip_prt_tagx.u64 =
+ cvmx_read_csr(CVMX_PIP_PRT_TAGX(port));
+ pip_prt_tagx.s.grp = pow_receive_group;
+ cvmx_write_csr(CVMX_PIP_PRT_TAGX(port),
+ pip_prt_tagx.u64);
+ }
+ }
+
+ cvmx_helper_ipd_and_packet_input_enable();
+
+ memset(cvm_oct_device, 0, sizeof(cvm_oct_device));
+
+ /*
+ * Initialize the FAU used for counting packet buffers that
+ * need to be freed.
+ */
+ cvmx_fau_atomic_write32(FAU_NUM_PACKET_BUFFERS_TO_FREE, 0);
+
+ /* Initialize the FAU used for counting tx SKBs that need to be freed */
+ cvmx_fau_atomic_write32(FAU_TOTAL_TX_TO_CLEAN, 0);
+
+ if ((pow_send_group != -1)) {
+ struct net_device *dev;
+
+ pr_info("\tConfiguring device for POW only access\n");
+ dev = alloc_etherdev(sizeof(struct octeon_ethernet));
+ if (dev) {
+ /* Initialize the device private structure. */
+ struct octeon_ethernet *priv = netdev_priv(dev);
+
+ dev->netdev_ops = &cvm_oct_pow_netdev_ops;
+ priv->imode = CVMX_HELPER_INTERFACE_MODE_DISABLED;
+ priv->port = CVMX_PIP_NUM_INPUT_PORTS;
+ priv->queue = -1;
+ strcpy(dev->name, "pow%d");
+ for (qos = 0; qos < 16; qos++)
+ skb_queue_head_init(&priv->tx_free_list[qos]);
+
+ if (register_netdev(dev) < 0) {
+ pr_err("Failed to register ethernet device for POW\n");
+ free_netdev(dev);
+ } else {
+ cvm_oct_device[CVMX_PIP_NUM_INPUT_PORTS] = dev;
+ pr_info("%s: POW send group %d, receive group %d\n",
+ dev->name, pow_send_group,
+ pow_receive_group);
+ }
+ } else {
+ pr_err("Failed to allocate ethernet device for POW\n");
+ }
+ }
+
+ num_interfaces = cvmx_helper_get_number_of_interfaces();
+ for (interface = 0; interface < num_interfaces; interface++) {
+ cvmx_helper_interface_mode_t imode =
+ cvmx_helper_interface_get_mode(interface);
+ int num_ports = cvmx_helper_ports_on_interface(interface);
+ int port;
+ int port_index;
+
+ for (port_index = 0,
+ port = cvmx_helper_get_ipd_port(interface, 0);
+ port < cvmx_helper_get_ipd_port(interface, num_ports);
+ port_index++, port++) {
+ struct octeon_ethernet *priv;
+ struct net_device *dev =
+ alloc_etherdev(sizeof(struct octeon_ethernet));
+ if (!dev) {
+ pr_err("Failed to allocate ethernet device for port %d\n",
+ port);
+ continue;
+ }
+
+ /* Initialize the device private structure. */
+ priv = netdev_priv(dev);
+ priv->netdev = dev;
+ priv->of_node = cvm_oct_node_for_port(pip, interface,
+ port_index);
+
+ INIT_DELAYED_WORK(&priv->port_periodic_work,
+ cvm_oct_periodic_worker);
+ priv->imode = imode;
+ priv->port = port;
+ priv->queue = cvmx_pko_get_base_queue(priv->port);
+ priv->fau = fau - cvmx_pko_get_num_queues(port) * 4;
+ for (qos = 0; qos < 16; qos++)
+ skb_queue_head_init(&priv->tx_free_list[qos]);
+ for (qos = 0; qos < cvmx_pko_get_num_queues(port);
+ qos++)
+ cvmx_fau_atomic_write32(priv->fau + qos * 4, 0);
+
+ switch (priv->imode) {
+
+ /* These types don't support ports to IPD/PKO */
+ case CVMX_HELPER_INTERFACE_MODE_DISABLED:
+ case CVMX_HELPER_INTERFACE_MODE_PCIE:
+ case CVMX_HELPER_INTERFACE_MODE_PICMG:
+ break;
+
+ case CVMX_HELPER_INTERFACE_MODE_NPI:
+ dev->netdev_ops = &cvm_oct_npi_netdev_ops;
+ strcpy(dev->name, "npi%d");
+ break;
+
+ case CVMX_HELPER_INTERFACE_MODE_XAUI:
+ dev->netdev_ops = &cvm_oct_xaui_netdev_ops;
+ strcpy(dev->name, "xaui%d");
+ break;
+
+ case CVMX_HELPER_INTERFACE_MODE_LOOP:
+ dev->netdev_ops = &cvm_oct_npi_netdev_ops;
+ strcpy(dev->name, "loop%d");
+ break;
+
+ case CVMX_HELPER_INTERFACE_MODE_SGMII:
+ dev->netdev_ops = &cvm_oct_sgmii_netdev_ops;
+ strcpy(dev->name, "eth%d");
+ break;
+
+ case CVMX_HELPER_INTERFACE_MODE_SPI:
+ dev->netdev_ops = &cvm_oct_spi_netdev_ops;
+ strcpy(dev->name, "spi%d");
+ break;
+
+ case CVMX_HELPER_INTERFACE_MODE_RGMII:
+ case CVMX_HELPER_INTERFACE_MODE_GMII:
+ dev->netdev_ops = &cvm_oct_rgmii_netdev_ops;
+ strcpy(dev->name, "eth%d");
+ break;
+ }
+
+ if (!dev->netdev_ops) {
+ free_netdev(dev);
+ } else if (register_netdev(dev) < 0) {
+ pr_err("Failed to register ethernet device for interface %d, port %d\n",
+ interface, priv->port);
+ free_netdev(dev);
+ } else {
+ cvm_oct_device[priv->port] = dev;
+ fau -=
+ cvmx_pko_get_num_queues(priv->port) *
+ sizeof(uint32_t);
+ queue_delayed_work(cvm_oct_poll_queue,
+ &priv->port_periodic_work, HZ);
+ }
+ }
+ }
+
+ cvm_oct_tx_initialize();
+ cvm_oct_rx_initialize();
+
+ /*
+ * 150 uS: about 10 1500-byte packets at 1GE.
+ */
+ cvm_oct_tx_poll_interval = 150 * (octeon_get_clock_rate() / 1000000);
+
+ queue_delayed_work(cvm_oct_poll_queue, &cvm_oct_rx_refill_work, HZ);
+
+ return 0;
+}
+
+static int cvm_oct_remove(struct platform_device *pdev)
+{
+ int port;
+
+ /* Disable POW interrupt */
+ cvmx_write_csr(CVMX_POW_WQ_INT_THRX(pow_receive_group), 0);
+
+ cvmx_ipd_disable();
+
+ /* Free the interrupt handler */
+ free_irq(OCTEON_IRQ_WORKQ0 + pow_receive_group, cvm_oct_device);
+
+ atomic_inc_return(&cvm_oct_poll_queue_stopping);
+ cancel_delayed_work_sync(&cvm_oct_rx_refill_work);
+
+ cvm_oct_rx_shutdown();
+ cvm_oct_tx_shutdown();
+
+ cvmx_pko_disable();
+
+ /* Free the ethernet devices */
+ for (port = 0; port < TOTAL_NUMBER_OF_PORTS; port++) {
+ if (cvm_oct_device[port]) {
+ struct net_device *dev = cvm_oct_device[port];
+ struct octeon_ethernet *priv = netdev_priv(dev);
+
+ cancel_delayed_work_sync(&priv->port_periodic_work);
+
+ cvm_oct_tx_shutdown_dev(dev);
+ unregister_netdev(dev);
+ free_netdev(dev);
+ cvm_oct_device[port] = NULL;
+ }
+ }
+
+ destroy_workqueue(cvm_oct_poll_queue);
+
+ cvmx_pko_shutdown();
+
+ cvmx_ipd_free_ptr();
+
+ /* Free the HW pools */
+ cvm_oct_mem_empty_fpa(CVMX_FPA_PACKET_POOL, CVMX_FPA_PACKET_POOL_SIZE,
+ num_packet_buffers);
+ cvm_oct_mem_empty_fpa(CVMX_FPA_WQE_POOL, CVMX_FPA_WQE_POOL_SIZE,
+ num_packet_buffers);
+ if (CVMX_FPA_OUTPUT_BUFFER_POOL != CVMX_FPA_PACKET_POOL)
+ cvm_oct_mem_empty_fpa(CVMX_FPA_OUTPUT_BUFFER_POOL,
+ CVMX_FPA_OUTPUT_BUFFER_POOL_SIZE, 128);
+ return 0;
+}
+
+static const struct of_device_id cvm_oct_match[] = {
+ {
+ .compatible = "cavium,octeon-3860-pip",
+ },
+ {},
+};
+MODULE_DEVICE_TABLE(of, cvm_oct_match);
+
+static struct platform_driver cvm_oct_driver = {
+ .probe = cvm_oct_probe,
+ .remove = cvm_oct_remove,
+ .driver = {
+ .name = KBUILD_MODNAME,
+ .of_match_table = cvm_oct_match,
+ },
+};
+
+module_platform_driver(cvm_oct_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Cavium Networks <support@caviumnetworks.com>");
+MODULE_DESCRIPTION("Cavium Networks Octeon ethernet driver.");
diff --git a/drivers/staging/octeon/octeon-ethernet.h b/drivers/staging/octeon/octeon-ethernet.h
new file mode 100644
index 000000000..f48dc766f
--- /dev/null
+++ b/drivers/staging/octeon/octeon-ethernet.h
@@ -0,0 +1,104 @@
+/**********************************************************************
+ * Author: Cavium Networks
+ *
+ * Contact: support@caviumnetworks.com
+ * This file is part of the OCTEON SDK
+ *
+ * Copyright (c) 2003-2010 Cavium Networks
+ *
+ * This file 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.
+ *
+ * This file is distributed in the hope that it will be useful, but
+ * AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or
+ * NONINFRINGEMENT. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this file; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ * or visit http://www.gnu.org/licenses/.
+ *
+ * This file may also be available under a different license from Cavium.
+ * Contact Cavium Networks for more information
+**********************************************************************/
+
+/*
+ * External interface for the Cavium Octeon ethernet driver.
+ */
+#ifndef OCTEON_ETHERNET_H
+#define OCTEON_ETHERNET_H
+
+#include <linux/of.h>
+
+/**
+ * This is the definition of the Ethernet driver's private
+ * driver state stored in netdev_priv(dev).
+ */
+struct octeon_ethernet {
+ /* PKO hardware output port */
+ int port;
+ /* PKO hardware queue for the port */
+ int queue;
+ /* Hardware fetch and add to count outstanding tx buffers */
+ int fau;
+ /* My netdev. */
+ struct net_device *netdev;
+ /*
+ * Type of port. This is one of the enums in
+ * cvmx_helper_interface_mode_t
+ */
+ int imode;
+ /* List of outstanding tx buffers per queue */
+ struct sk_buff_head tx_free_list[16];
+ /* Device statistics */
+ struct net_device_stats stats;
+ struct phy_device *phydev;
+ unsigned int last_link;
+ /* Last negotiated link state */
+ uint64_t link_info;
+ /* Called periodically to check link status */
+ void (*poll)(struct net_device *dev);
+ struct delayed_work port_periodic_work;
+ struct work_struct port_work; /* may be unused. */
+ struct device_node *of_node;
+};
+
+int cvm_oct_free_work(void *work_queue_entry);
+
+extern int cvm_oct_rgmii_init(struct net_device *dev);
+extern void cvm_oct_rgmii_uninit(struct net_device *dev);
+extern int cvm_oct_rgmii_open(struct net_device *dev);
+extern int cvm_oct_rgmii_stop(struct net_device *dev);
+
+extern int cvm_oct_sgmii_init(struct net_device *dev);
+extern void cvm_oct_sgmii_uninit(struct net_device *dev);
+extern int cvm_oct_sgmii_open(struct net_device *dev);
+extern int cvm_oct_sgmii_stop(struct net_device *dev);
+
+extern int cvm_oct_spi_init(struct net_device *dev);
+extern void cvm_oct_spi_uninit(struct net_device *dev);
+extern int cvm_oct_xaui_init(struct net_device *dev);
+extern void cvm_oct_xaui_uninit(struct net_device *dev);
+extern int cvm_oct_xaui_open(struct net_device *dev);
+extern int cvm_oct_xaui_stop(struct net_device *dev);
+
+extern int cvm_oct_common_init(struct net_device *dev);
+extern void cvm_oct_common_uninit(struct net_device *dev);
+void cvm_oct_adjust_link(struct net_device *dev);
+int cvm_oct_common_stop(struct net_device *dev);
+
+extern int always_use_pow;
+extern int pow_send_group;
+extern int pow_receive_group;
+extern char pow_send_list[];
+extern struct net_device *cvm_oct_device[];
+extern struct workqueue_struct *cvm_oct_poll_queue;
+extern atomic_t cvm_oct_poll_queue_stopping;
+extern u64 cvm_oct_tx_poll_interval;
+
+extern int rx_napi_weight;
+
+#endif
diff --git a/drivers/staging/olpc_dcon/Kconfig b/drivers/staging/olpc_dcon/Kconfig
new file mode 100644
index 000000000..d277f0487
--- /dev/null
+++ b/drivers/staging/olpc_dcon/Kconfig
@@ -0,0 +1,35 @@
+config FB_OLPC_DCON
+ tristate "One Laptop Per Child Display CONtroller support"
+ depends on OLPC && FB
+ depends on I2C
+ depends on (GPIO_CS5535 || GPIO_CS5535=n)
+ select BACKLIGHT_CLASS_DEVICE
+ ---help---
+ In order to support very low power operation, the XO laptop uses a
+ secondary Display CONtroller, or DCON. This secondary controller
+ is present in the video pipeline between the primary display
+ controller (integrate into the processor or chipset) and the LCD
+ panel. It allows the main processor/display controller to be
+ completely powered off while still retaining an image on the display.
+ This controller is only available on OLPC platforms. Unless you have
+ one of these platforms, you will want to say 'N'.
+
+config FB_OLPC_DCON_1
+ bool "OLPC XO-1 DCON support"
+ depends on FB_OLPC_DCON && GPIO_CS5535
+ default y
+ ---help---
+ Enable support for the DCON in XO-1 model laptops. The kernel
+ communicates with the DCON using model-specific code. If you
+ have an XO-1 (or if you're unsure what model you have), you should
+ say 'Y'.
+
+config FB_OLPC_DCON_1_5
+ bool "OLPC XO-1.5 DCON support"
+ depends on FB_OLPC_DCON && ACPI
+ default y
+ ---help---
+ Enable support for the DCON in XO-1.5 model laptops. The kernel
+ communicates with the DCON using model-specific code. If you
+ have an XO-1.5 (or if you're unsure what model you have), you
+ should say 'Y'.
diff --git a/drivers/staging/olpc_dcon/Makefile b/drivers/staging/olpc_dcon/Makefile
new file mode 100644
index 000000000..36c7e67fe
--- /dev/null
+++ b/drivers/staging/olpc_dcon/Makefile
@@ -0,0 +1,6 @@
+olpc-dcon-objs += olpc_dcon.o
+olpc-dcon-$(CONFIG_FB_OLPC_DCON_1) += olpc_dcon_xo_1.o
+olpc-dcon-$(CONFIG_FB_OLPC_DCON_1_5) += olpc_dcon_xo_1_5.o
+obj-$(CONFIG_FB_OLPC_DCON) += olpc-dcon.o
+
+
diff --git a/drivers/staging/olpc_dcon/TODO b/drivers/staging/olpc_dcon/TODO
new file mode 100644
index 000000000..61c2e65ac
--- /dev/null
+++ b/drivers/staging/olpc_dcon/TODO
@@ -0,0 +1,9 @@
+TODO:
+ - see if vx855 gpio API can be made similar enough to cs5535 so we can
+ share more code
+ - allow simultaneous XO-1 and XO-1.5 support
+
+Please send patches to Greg Kroah-Hartman <greg@kroah.com> and
+copy:
+ Daniel Drake <dsd@laptop.org>
+ Jens Frederich <jfrederich@gmail.com>
diff --git a/drivers/staging/olpc_dcon/olpc_dcon.c b/drivers/staging/olpc_dcon/olpc_dcon.c
new file mode 100644
index 000000000..d115f5c0e
--- /dev/null
+++ b/drivers/staging/olpc_dcon/olpc_dcon.c
@@ -0,0 +1,817 @@
+/*
+ * Mainly by David Woodhouse, somewhat modified by Jordan Crouse
+ *
+ * Copyright © 2006-2007 Red Hat, Inc.
+ * Copyright © 2006-2007 Advanced Micro Devices, Inc.
+ * Copyright © 2009 VIA Technology, Inc.
+ * Copyright (c) 2010-2011 Andres Salomon <dilinger@queued.net>
+ *
+ * This program is free software. You can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/kernel.h>
+#include <linux/fb.h>
+#include <linux/console.h>
+#include <linux/i2c.h>
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/backlight.h>
+#include <linux/device.h>
+#include <linux/uaccess.h>
+#include <linux/ctype.h>
+#include <linux/reboot.h>
+#include <linux/olpc-ec.h>
+#include <asm/tsc.h>
+#include <asm/olpc.h>
+
+#include "olpc_dcon.h"
+
+/* Module definitions */
+
+static ushort resumeline = 898;
+module_param(resumeline, ushort, 0444);
+
+static struct dcon_platform_data *pdata;
+
+/* I2C structures */
+
+/* Platform devices */
+static struct platform_device *dcon_device;
+
+static unsigned short normal_i2c[] = { 0x0d, I2C_CLIENT_END };
+
+static s32 dcon_write(struct dcon_priv *dcon, u8 reg, u16 val)
+{
+ return i2c_smbus_write_word_data(dcon->client, reg, val);
+}
+
+static s32 dcon_read(struct dcon_priv *dcon, u8 reg)
+{
+ return i2c_smbus_read_word_data(dcon->client, reg);
+}
+
+/* ===== API functions - these are called by a variety of users ==== */
+
+static int dcon_hw_init(struct dcon_priv *dcon, int is_init)
+{
+ uint16_t ver;
+ int rc = 0;
+
+ ver = dcon_read(dcon, DCON_REG_ID);
+ if ((ver >> 8) != 0xDC) {
+ pr_err("DCON ID not 0xDCxx: 0x%04x instead.\n", ver);
+ rc = -ENXIO;
+ goto err;
+ }
+
+ if (is_init) {
+ pr_info("Discovered DCON version %x\n", ver & 0xFF);
+ rc = pdata->init(dcon);
+ if (rc != 0) {
+ pr_err("Unable to init.\n");
+ goto err;
+ }
+ }
+
+ if (ver < 0xdc02) {
+ dev_err(&dcon->client->dev,
+ "DCON v1 is unsupported, giving up..\n");
+ rc = -ENODEV;
+ goto err;
+ }
+
+ /* SDRAM setup/hold time */
+ dcon_write(dcon, 0x3a, 0xc040);
+ dcon_write(dcon, DCON_REG_MEM_OPT_A, 0x0000); /* clear option bits */
+ dcon_write(dcon, DCON_REG_MEM_OPT_A,
+ MEM_DLL_CLOCK_DELAY | MEM_POWER_DOWN);
+ dcon_write(dcon, DCON_REG_MEM_OPT_B, MEM_SOFT_RESET);
+
+ /* Colour swizzle, AA, no passthrough, backlight */
+ if (is_init) {
+ dcon->disp_mode = MODE_PASSTHRU | MODE_BL_ENABLE |
+ MODE_CSWIZZLE | MODE_COL_AA;
+ }
+ dcon_write(dcon, DCON_REG_MODE, dcon->disp_mode);
+
+
+ /* Set the scanline to interrupt on during resume */
+ dcon_write(dcon, DCON_REG_SCAN_INT, resumeline);
+
+err:
+ return rc;
+}
+
+/*
+ * The smbus doesn't always come back due to what is believed to be
+ * hardware (power rail) bugs. For older models where this is known to
+ * occur, our solution is to attempt to wait for the bus to stabilize;
+ * if it doesn't happen, cut power to the dcon, repower it, and wait
+ * for the bus to stabilize. Rinse, repeat until we have a working
+ * smbus. For newer models, we simply BUG(); we want to know if this
+ * still happens despite the power fixes that have been made!
+ */
+static int dcon_bus_stabilize(struct dcon_priv *dcon, int is_powered_down)
+{
+ unsigned long timeout;
+ u8 pm;
+ int x;
+
+power_up:
+ if (is_powered_down) {
+ pm = 1;
+ x = olpc_ec_cmd(EC_DCON_POWER_MODE, &pm, 1, NULL, 0);
+ if (x) {
+ pr_warn("unable to force dcon to power up: %d!\n", x);
+ return x;
+ }
+ usleep_range(10000, 11000); /* we'll be conservative */
+ }
+
+ pdata->bus_stabilize_wiggle();
+
+ for (x = -1, timeout = 50; timeout && x < 0; timeout--) {
+ usleep_range(1000, 1100);
+ x = dcon_read(dcon, DCON_REG_ID);
+ }
+ if (x < 0) {
+ pr_err("unable to stabilize dcon's smbus, reasserting power and praying.\n");
+ BUG_ON(olpc_board_at_least(olpc_board(0xc2)));
+ pm = 0;
+ olpc_ec_cmd(EC_DCON_POWER_MODE, &pm, 1, NULL, 0);
+ msleep(100);
+ is_powered_down = 1;
+ goto power_up; /* argh, stupid hardware.. */
+ }
+
+ if (is_powered_down)
+ return dcon_hw_init(dcon, 0);
+ return 0;
+}
+
+static void dcon_set_backlight(struct dcon_priv *dcon, u8 level)
+{
+ dcon->bl_val = level;
+ dcon_write(dcon, DCON_REG_BRIGHT, dcon->bl_val);
+
+ /* Purposely turn off the backlight when we go to level 0 */
+ if (dcon->bl_val == 0) {
+ dcon->disp_mode &= ~MODE_BL_ENABLE;
+ dcon_write(dcon, DCON_REG_MODE, dcon->disp_mode);
+ } else if (!(dcon->disp_mode & MODE_BL_ENABLE)) {
+ dcon->disp_mode |= MODE_BL_ENABLE;
+ dcon_write(dcon, DCON_REG_MODE, dcon->disp_mode);
+ }
+}
+
+/* Set the output type to either color or mono */
+static int dcon_set_mono_mode(struct dcon_priv *dcon, bool enable_mono)
+{
+ if (dcon->mono == enable_mono)
+ return 0;
+
+ dcon->mono = enable_mono;
+
+ if (enable_mono) {
+ dcon->disp_mode &= ~(MODE_CSWIZZLE | MODE_COL_AA);
+ dcon->disp_mode |= MODE_MONO_LUMA;
+ } else {
+ dcon->disp_mode &= ~(MODE_MONO_LUMA);
+ dcon->disp_mode |= MODE_CSWIZZLE | MODE_COL_AA;
+ }
+
+ dcon_write(dcon, DCON_REG_MODE, dcon->disp_mode);
+ return 0;
+}
+
+/* For now, this will be really stupid - we need to address how
+ * DCONLOAD works in a sleep and account for it accordingly
+ */
+
+static void dcon_sleep(struct dcon_priv *dcon, bool sleep)
+{
+ int x;
+
+ /* Turn off the backlight and put the DCON to sleep */
+
+ if (dcon->asleep == sleep)
+ return;
+
+ if (!olpc_board_at_least(olpc_board(0xc2)))
+ return;
+
+ if (sleep) {
+ u8 pm = 0;
+
+ x = olpc_ec_cmd(EC_DCON_POWER_MODE, &pm, 1, NULL, 0);
+ if (x)
+ pr_warn("unable to force dcon to power down: %d!\n", x);
+ else
+ dcon->asleep = sleep;
+ } else {
+ /* Only re-enable the backlight if the backlight value is set */
+ if (dcon->bl_val != 0)
+ dcon->disp_mode |= MODE_BL_ENABLE;
+ x = dcon_bus_stabilize(dcon, 1);
+ if (x)
+ pr_warn("unable to reinit dcon hardware: %d!\n", x);
+ else
+ dcon->asleep = sleep;
+
+ /* Restore backlight */
+ dcon_set_backlight(dcon, dcon->bl_val);
+ }
+
+ /* We should turn off some stuff in the framebuffer - but what? */
+}
+
+/* the DCON seems to get confused if we change DCONLOAD too
+ * frequently -- i.e., approximately faster than frame time.
+ * normally we don't change it this fast, so in general we won't
+ * delay here.
+ */
+static void dcon_load_holdoff(struct dcon_priv *dcon)
+{
+ struct timespec delta_t, now;
+
+ while (1) {
+ getnstimeofday(&now);
+ delta_t = timespec_sub(now, dcon->load_time);
+ if (delta_t.tv_sec != 0 ||
+ delta_t.tv_nsec > NSEC_PER_MSEC * 20) {
+ break;
+ }
+ mdelay(4);
+ }
+}
+
+static bool dcon_blank_fb(struct dcon_priv *dcon, bool blank)
+{
+ int err;
+
+ console_lock();
+ if (!lock_fb_info(dcon->fbinfo)) {
+ console_unlock();
+ dev_err(&dcon->client->dev, "unable to lock framebuffer\n");
+ return false;
+ }
+
+ dcon->ignore_fb_events = true;
+ err = fb_blank(dcon->fbinfo,
+ blank ? FB_BLANK_POWERDOWN : FB_BLANK_UNBLANK);
+ dcon->ignore_fb_events = false;
+ unlock_fb_info(dcon->fbinfo);
+ console_unlock();
+
+ if (err) {
+ dev_err(&dcon->client->dev, "couldn't %sblank framebuffer\n",
+ blank ? "" : "un");
+ return false;
+ }
+ return true;
+}
+
+/* Set the source of the display (CPU or DCON) */
+static void dcon_source_switch(struct work_struct *work)
+{
+ struct dcon_priv *dcon = container_of(work, struct dcon_priv,
+ switch_source);
+ int source = dcon->pending_src;
+
+ if (dcon->curr_src == source)
+ return;
+
+ dcon_load_holdoff(dcon);
+
+ dcon->switched = false;
+
+ switch (source) {
+ case DCON_SOURCE_CPU:
+ pr_info("dcon_source_switch to CPU\n");
+ /* Enable the scanline interrupt bit */
+ if (dcon_write(dcon, DCON_REG_MODE,
+ dcon->disp_mode | MODE_SCAN_INT))
+ pr_err("couldn't enable scanline interrupt!\n");
+ else
+ /* Wait up to one second for the scanline interrupt */
+ wait_event_timeout(dcon->waitq, dcon->switched, HZ);
+
+ if (!dcon->switched)
+ pr_err("Timeout entering CPU mode; expect a screen glitch.\n");
+
+ /* Turn off the scanline interrupt */
+ if (dcon_write(dcon, DCON_REG_MODE, dcon->disp_mode))
+ pr_err("couldn't disable scanline interrupt!\n");
+
+ /*
+ * Ideally we'd like to disable interrupts here so that the
+ * fb unblanking and DCON turn on happen at a known time value;
+ * however, we can't do that right now with fb_blank
+ * messing with semaphores.
+ *
+ * For now, we just hope..
+ */
+ if (!dcon_blank_fb(dcon, false)) {
+ pr_err("Failed to enter CPU mode\n");
+ dcon->pending_src = DCON_SOURCE_DCON;
+ return;
+ }
+
+ /* And turn off the DCON */
+ pdata->set_dconload(1);
+ getnstimeofday(&dcon->load_time);
+
+ pr_info("The CPU has control\n");
+ break;
+ case DCON_SOURCE_DCON:
+ {
+ struct timespec delta_t;
+
+ pr_info("dcon_source_switch to DCON\n");
+
+ /* Clear DCONLOAD - this implies that the DCON is in control */
+ pdata->set_dconload(0);
+ getnstimeofday(&dcon->load_time);
+
+ wait_event_timeout(dcon->waitq, dcon->switched, HZ/2);
+
+ if (!dcon->switched) {
+ pr_err("Timeout entering DCON mode; expect a screen glitch.\n");
+ } else {
+ /* sometimes the DCON doesn't follow its own rules,
+ * and doesn't wait for two vsync pulses before
+ * ack'ing the frame load with an IRQ. the result
+ * is that the display shows the *previously*
+ * loaded frame. we can detect this by looking at
+ * the time between asserting DCONLOAD and the IRQ --
+ * if it's less than 20msec, then the DCON couldn't
+ * have seen two VSYNC pulses. in that case we
+ * deassert and reassert, and hope for the best.
+ * see http://dev.laptop.org/ticket/9664
+ */
+ delta_t = timespec_sub(dcon->irq_time, dcon->load_time);
+ if (dcon->switched && delta_t.tv_sec == 0 &&
+ delta_t.tv_nsec < NSEC_PER_MSEC * 20) {
+ pr_err("missed loading, retrying\n");
+ pdata->set_dconload(1);
+ mdelay(41);
+ pdata->set_dconload(0);
+ getnstimeofday(&dcon->load_time);
+ mdelay(41);
+ }
+ }
+
+ dcon_blank_fb(dcon, true);
+ pr_info("The DCON has control\n");
+ break;
+ }
+ default:
+ BUG();
+ }
+
+ dcon->curr_src = source;
+}
+
+static void dcon_set_source(struct dcon_priv *dcon, int arg)
+{
+ if (dcon->pending_src == arg)
+ return;
+
+ dcon->pending_src = arg;
+
+ if (dcon->curr_src != arg)
+ schedule_work(&dcon->switch_source);
+}
+
+static void dcon_set_source_sync(struct dcon_priv *dcon, int arg)
+{
+ dcon_set_source(dcon, arg);
+ flush_scheduled_work();
+}
+
+static ssize_t dcon_mode_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct dcon_priv *dcon = dev_get_drvdata(dev);
+
+ return sprintf(buf, "%4.4X\n", dcon->disp_mode);
+}
+
+static ssize_t dcon_sleep_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct dcon_priv *dcon = dev_get_drvdata(dev);
+
+ return sprintf(buf, "%d\n", dcon->asleep);
+}
+
+static ssize_t dcon_freeze_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct dcon_priv *dcon = dev_get_drvdata(dev);
+
+ return sprintf(buf, "%d\n", dcon->curr_src == DCON_SOURCE_DCON ? 1 : 0);
+}
+
+static ssize_t dcon_mono_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct dcon_priv *dcon = dev_get_drvdata(dev);
+
+ return sprintf(buf, "%d\n", dcon->mono);
+}
+
+static ssize_t dcon_resumeline_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return sprintf(buf, "%d\n", resumeline);
+}
+
+static ssize_t dcon_mono_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ unsigned long enable_mono;
+ int rc;
+
+ rc = kstrtoul(buf, 10, &enable_mono);
+ if (rc)
+ return rc;
+
+ dcon_set_mono_mode(dev_get_drvdata(dev), enable_mono ? true : false);
+
+ return count;
+}
+
+static ssize_t dcon_freeze_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct dcon_priv *dcon = dev_get_drvdata(dev);
+ unsigned long output;
+ int ret;
+
+ ret = kstrtoul(buf, 10, &output);
+ if (ret)
+ return ret;
+
+ pr_info("dcon_freeze_store: %lu\n", output);
+
+ switch (output) {
+ case 0:
+ dcon_set_source(dcon, DCON_SOURCE_CPU);
+ break;
+ case 1:
+ dcon_set_source_sync(dcon, DCON_SOURCE_DCON);
+ break;
+ case 2: /* normally unused */
+ dcon_set_source(dcon, DCON_SOURCE_DCON);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return count;
+}
+
+static ssize_t dcon_resumeline_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ unsigned short rl;
+ int rc;
+
+ rc = kstrtou16(buf, 10, &rl);
+ if (rc)
+ return rc;
+
+ resumeline = rl;
+ dcon_write(dev_get_drvdata(dev), DCON_REG_SCAN_INT, resumeline);
+
+ return count;
+}
+
+static ssize_t dcon_sleep_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ unsigned long output;
+ int ret;
+
+ ret = kstrtoul(buf, 10, &output);
+ if (ret)
+ return ret;
+
+ dcon_sleep(dev_get_drvdata(dev), output ? true : false);
+ return count;
+}
+
+static struct device_attribute dcon_device_files[] = {
+ __ATTR(mode, 0444, dcon_mode_show, NULL),
+ __ATTR(sleep, 0644, dcon_sleep_show, dcon_sleep_store),
+ __ATTR(freeze, 0644, dcon_freeze_show, dcon_freeze_store),
+ __ATTR(monochrome, 0644, dcon_mono_show, dcon_mono_store),
+ __ATTR(resumeline, 0644, dcon_resumeline_show, dcon_resumeline_store),
+};
+
+static int dcon_bl_update(struct backlight_device *dev)
+{
+ struct dcon_priv *dcon = bl_get_data(dev);
+ u8 level = dev->props.brightness & 0x0F;
+
+ if (dev->props.power != FB_BLANK_UNBLANK)
+ level = 0;
+
+ if (level != dcon->bl_val)
+ dcon_set_backlight(dcon, level);
+
+ /* power down the DCON when the screen is blanked */
+ if (!dcon->ignore_fb_events)
+ dcon_sleep(dcon, !!(dev->props.state & BL_CORE_FBBLANK));
+
+ return 0;
+}
+
+static int dcon_bl_get(struct backlight_device *dev)
+{
+ struct dcon_priv *dcon = bl_get_data(dev);
+
+ return dcon->bl_val;
+}
+
+static const struct backlight_ops dcon_bl_ops = {
+ .update_status = dcon_bl_update,
+ .get_brightness = dcon_bl_get,
+};
+
+static struct backlight_properties dcon_bl_props = {
+ .max_brightness = 15,
+ .type = BACKLIGHT_RAW,
+ .power = FB_BLANK_UNBLANK,
+};
+
+static int dcon_reboot_notify(struct notifier_block *nb,
+ unsigned long foo, void *bar)
+{
+ struct dcon_priv *dcon = container_of(nb, struct dcon_priv, reboot_nb);
+
+ if (!dcon || !dcon->client)
+ return NOTIFY_DONE;
+
+ /* Turn off the DCON. Entirely. */
+ dcon_write(dcon, DCON_REG_MODE, 0x39);
+ dcon_write(dcon, DCON_REG_MODE, 0x32);
+ return NOTIFY_DONE;
+}
+
+static int unfreeze_on_panic(struct notifier_block *nb,
+ unsigned long e, void *p)
+{
+ pdata->set_dconload(1);
+ return NOTIFY_DONE;
+}
+
+static struct notifier_block dcon_panic_nb = {
+ .notifier_call = unfreeze_on_panic,
+};
+
+static int dcon_detect(struct i2c_client *client, struct i2c_board_info *info)
+{
+ strlcpy(info->type, "olpc_dcon", I2C_NAME_SIZE);
+
+ return 0;
+}
+
+static int dcon_probe(struct i2c_client *client, const struct i2c_device_id *id)
+{
+ struct dcon_priv *dcon;
+ int rc, i, j;
+
+ if (!pdata)
+ return -ENXIO;
+
+ dcon = kzalloc(sizeof(*dcon), GFP_KERNEL);
+ if (!dcon)
+ return -ENOMEM;
+
+ dcon->client = client;
+ init_waitqueue_head(&dcon->waitq);
+ INIT_WORK(&dcon->switch_source, dcon_source_switch);
+ dcon->reboot_nb.notifier_call = dcon_reboot_notify;
+ dcon->reboot_nb.priority = -1;
+
+ i2c_set_clientdata(client, dcon);
+
+ if (num_registered_fb < 1) {
+ dev_err(&client->dev, "DCON driver requires a registered fb\n");
+ rc = -EIO;
+ goto einit;
+ }
+ dcon->fbinfo = registered_fb[0];
+
+ rc = dcon_hw_init(dcon, 1);
+ if (rc)
+ goto einit;
+
+ /* Add the DCON device */
+
+ dcon_device = platform_device_alloc("dcon", -1);
+
+ if (dcon_device == NULL) {
+ pr_err("Unable to create the DCON device\n");
+ rc = -ENOMEM;
+ goto eirq;
+ }
+ rc = platform_device_add(dcon_device);
+ platform_set_drvdata(dcon_device, dcon);
+
+ if (rc) {
+ pr_err("Unable to add the DCON device\n");
+ goto edev;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(dcon_device_files); i++) {
+ rc = device_create_file(&dcon_device->dev,
+ &dcon_device_files[i]);
+ if (rc) {
+ dev_err(&dcon_device->dev, "Cannot create sysfs file\n");
+ goto ecreate;
+ }
+ }
+
+ dcon->bl_val = dcon_read(dcon, DCON_REG_BRIGHT) & 0x0F;
+
+ /* Add the backlight device for the DCON */
+ dcon_bl_props.brightness = dcon->bl_val;
+ dcon->bl_dev = backlight_device_register("dcon-bl", &dcon_device->dev,
+ dcon, &dcon_bl_ops, &dcon_bl_props);
+ if (IS_ERR(dcon->bl_dev)) {
+ dev_err(&client->dev, "cannot register backlight dev (%ld)\n",
+ PTR_ERR(dcon->bl_dev));
+ dcon->bl_dev = NULL;
+ }
+
+ register_reboot_notifier(&dcon->reboot_nb);
+ atomic_notifier_chain_register(&panic_notifier_list, &dcon_panic_nb);
+
+ return 0;
+
+ ecreate:
+ for (j = 0; j < i; j++)
+ device_remove_file(&dcon_device->dev, &dcon_device_files[j]);
+ edev:
+ platform_device_unregister(dcon_device);
+ dcon_device = NULL;
+ eirq:
+ free_irq(DCON_IRQ, dcon);
+ einit:
+ kfree(dcon);
+ return rc;
+}
+
+static int dcon_remove(struct i2c_client *client)
+{
+ struct dcon_priv *dcon = i2c_get_clientdata(client);
+
+ unregister_reboot_notifier(&dcon->reboot_nb);
+ atomic_notifier_chain_unregister(&panic_notifier_list, &dcon_panic_nb);
+
+ free_irq(DCON_IRQ, dcon);
+
+ backlight_device_unregister(dcon->bl_dev);
+
+ if (dcon_device != NULL)
+ platform_device_unregister(dcon_device);
+ cancel_work_sync(&dcon->switch_source);
+
+ kfree(dcon);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int dcon_suspend(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct dcon_priv *dcon = i2c_get_clientdata(client);
+
+ if (!dcon->asleep) {
+ /* Set up the DCON to have the source */
+ dcon_set_source_sync(dcon, DCON_SOURCE_DCON);
+ }
+
+ return 0;
+}
+
+static int dcon_resume(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct dcon_priv *dcon = i2c_get_clientdata(client);
+
+ if (!dcon->asleep) {
+ dcon_bus_stabilize(dcon, 0);
+ dcon_set_source(dcon, DCON_SOURCE_CPU);
+ }
+
+ return 0;
+}
+
+#else
+
+#define dcon_suspend NULL
+#define dcon_resume NULL
+
+#endif /* CONFIG_PM */
+
+
+irqreturn_t dcon_interrupt(int irq, void *id)
+{
+ struct dcon_priv *dcon = id;
+ u8 status;
+
+ if (pdata->read_status(&status))
+ return IRQ_NONE;
+
+ switch (status & 3) {
+ case 3:
+ pr_debug("DCONLOAD_MISSED interrupt\n");
+ break;
+
+ case 2: /* switch to DCON mode */
+ case 1: /* switch to CPU mode */
+ dcon->switched = true;
+ getnstimeofday(&dcon->irq_time);
+ wake_up(&dcon->waitq);
+ break;
+
+ case 0:
+ /* workaround resume case: the DCON (on 1.5) doesn't
+ * ever assert status 0x01 when switching to CPU mode
+ * during resume. this is because DCONLOAD is de-asserted
+ * _immediately_ upon exiting S3, so the actual release
+ * of the DCON happened long before this point.
+ * see http://dev.laptop.org/ticket/9869
+ */
+ if (dcon->curr_src != dcon->pending_src && !dcon->switched) {
+ dcon->switched = true;
+ getnstimeofday(&dcon->irq_time);
+ wake_up(&dcon->waitq);
+ pr_debug("switching w/ status 0/0\n");
+ } else {
+ pr_debug("scanline interrupt w/CPU\n");
+ }
+ }
+
+ return IRQ_HANDLED;
+}
+
+static const struct dev_pm_ops dcon_pm_ops = {
+ .suspend = dcon_suspend,
+ .resume = dcon_resume,
+};
+
+static const struct i2c_device_id dcon_idtable[] = {
+ { "olpc_dcon", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, dcon_idtable);
+
+static struct i2c_driver dcon_driver = {
+ .driver = {
+ .name = "olpc_dcon",
+ .pm = &dcon_pm_ops,
+ },
+ .class = I2C_CLASS_DDC | I2C_CLASS_HWMON,
+ .id_table = dcon_idtable,
+ .probe = dcon_probe,
+ .remove = dcon_remove,
+ .detect = dcon_detect,
+ .address_list = normal_i2c,
+};
+
+static int __init olpc_dcon_init(void)
+{
+#ifdef CONFIG_FB_OLPC_DCON_1_5
+ /* XO-1.5 */
+ if (olpc_board_at_least(olpc_board(0xd0)))
+ pdata = &dcon_pdata_xo_1_5;
+#endif
+#ifdef CONFIG_FB_OLPC_DCON_1
+ if (!pdata)
+ pdata = &dcon_pdata_xo_1;
+#endif
+
+ return i2c_add_driver(&dcon_driver);
+}
+
+static void __exit olpc_dcon_exit(void)
+{
+ i2c_del_driver(&dcon_driver);
+}
+
+module_init(olpc_dcon_init);
+module_exit(olpc_dcon_exit);
+
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/olpc_dcon/olpc_dcon.h b/drivers/staging/olpc_dcon/olpc_dcon.h
new file mode 100644
index 000000000..aec98958f
--- /dev/null
+++ b/drivers/staging/olpc_dcon/olpc_dcon.h
@@ -0,0 +1,111 @@
+#ifndef OLPC_DCON_H_
+#define OLPC_DCON_H_
+
+#include <linux/notifier.h>
+#include <linux/workqueue.h>
+
+/* DCON registers */
+
+#define DCON_REG_ID 0
+#define DCON_REG_MODE 1
+
+#define MODE_PASSTHRU (1<<0)
+#define MODE_SLEEP (1<<1)
+#define MODE_SLEEP_AUTO (1<<2)
+#define MODE_BL_ENABLE (1<<3)
+#define MODE_BLANK (1<<4)
+#define MODE_CSWIZZLE (1<<5)
+#define MODE_COL_AA (1<<6)
+#define MODE_MONO_LUMA (1<<7)
+#define MODE_SCAN_INT (1<<8)
+#define MODE_CLOCKDIV (1<<9)
+#define MODE_DEBUG (1<<14)
+#define MODE_SELFTEST (1<<15)
+
+#define DCON_REG_HRES 0x2
+#define DCON_REG_HTOTAL 0x3
+#define DCON_REG_HSYNC_WIDTH 0x4
+#define DCON_REG_VRES 0x5
+#define DCON_REG_VTOTAL 0x6
+#define DCON_REG_VSYNC_WIDTH 0x7
+#define DCON_REG_TIMEOUT 0x8
+#define DCON_REG_SCAN_INT 0x9
+#define DCON_REG_BRIGHT 0xa
+#define DCON_REG_MEM_OPT_A 0x41
+#define DCON_REG_MEM_OPT_B 0x42
+
+/* Load Delay Locked Loop (DLL) settings for clock delay */
+#define MEM_DLL_CLOCK_DELAY (1<<0)
+/* Memory controller power down function */
+#define MEM_POWER_DOWN (1<<8)
+/* Memory controller software reset */
+#define MEM_SOFT_RESET (1<<0)
+
+/* Status values */
+
+#define DCONSTAT_SCANINT 0
+#define DCONSTAT_SCANINT_DCON 1
+#define DCONSTAT_DISPLAYLOAD 2
+#define DCONSTAT_MISSED 3
+
+/* Source values */
+
+#define DCON_SOURCE_DCON 0
+#define DCON_SOURCE_CPU 1
+
+/* Interrupt */
+#define DCON_IRQ 6
+
+struct dcon_priv {
+ struct i2c_client *client;
+ struct fb_info *fbinfo;
+ struct backlight_device *bl_dev;
+
+ wait_queue_head_t waitq;
+ struct work_struct switch_source;
+ struct notifier_block reboot_nb;
+
+ /* Shadow register for the DCON_REG_MODE register */
+ u8 disp_mode;
+
+ /* The current backlight value - this saves us some smbus traffic */
+ u8 bl_val;
+
+ /* Current source, initialized at probe time */
+ int curr_src;
+
+ /* Desired source */
+ int pending_src;
+
+ /* Variables used during switches */
+ bool switched;
+ struct timespec irq_time;
+ struct timespec load_time;
+
+ /* Current output type; true == mono, false == color */
+ bool mono;
+ bool asleep;
+ /* This get set while controlling fb blank state from the driver */
+ bool ignore_fb_events;
+};
+
+struct dcon_platform_data {
+ int (*init)(struct dcon_priv *);
+ void (*bus_stabilize_wiggle)(void);
+ void (*set_dconload)(int);
+ int (*read_status)(u8 *);
+};
+
+#include <linux/interrupt.h>
+
+extern irqreturn_t dcon_interrupt(int irq, void *id);
+
+#ifdef CONFIG_FB_OLPC_DCON_1
+extern struct dcon_platform_data dcon_pdata_xo_1;
+#endif
+
+#ifdef CONFIG_FB_OLPC_DCON_1_5
+extern struct dcon_platform_data dcon_pdata_xo_1_5;
+#endif
+
+#endif
diff --git a/drivers/staging/olpc_dcon/olpc_dcon_xo_1.c b/drivers/staging/olpc_dcon/olpc_dcon_xo_1.c
new file mode 100644
index 000000000..0c5a10c69
--- /dev/null
+++ b/drivers/staging/olpc_dcon/olpc_dcon_xo_1.c
@@ -0,0 +1,205 @@
+/*
+ * Mainly by David Woodhouse, somewhat modified by Jordan Crouse
+ *
+ * Copyright © 2006-2007 Red Hat, Inc.
+ * Copyright © 2006-2007 Advanced Micro Devices, Inc.
+ * Copyright © 2009 VIA Technology, Inc.
+ * Copyright (c) 2010 Andres Salomon <dilinger@queued.net>
+ *
+ * This program is free software. You can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/cs5535.h>
+#include <linux/gpio.h>
+#include <linux/delay.h>
+#include <asm/olpc.h>
+
+#include "olpc_dcon.h"
+
+static int dcon_init_xo_1(struct dcon_priv *dcon)
+{
+ unsigned char lob;
+
+ if (gpio_request(OLPC_GPIO_DCON_STAT0, "OLPC-DCON")) {
+ pr_err("failed to request STAT0 GPIO\n");
+ return -EIO;
+ }
+ if (gpio_request(OLPC_GPIO_DCON_STAT1, "OLPC-DCON")) {
+ pr_err("failed to request STAT1 GPIO\n");
+ goto err_gp_stat1;
+ }
+ if (gpio_request(OLPC_GPIO_DCON_IRQ, "OLPC-DCON")) {
+ pr_err("failed to request IRQ GPIO\n");
+ goto err_gp_irq;
+ }
+ if (gpio_request(OLPC_GPIO_DCON_LOAD, "OLPC-DCON")) {
+ pr_err("failed to request LOAD GPIO\n");
+ goto err_gp_load;
+ }
+ if (gpio_request(OLPC_GPIO_DCON_BLANK, "OLPC-DCON")) {
+ pr_err("failed to request BLANK GPIO\n");
+ goto err_gp_blank;
+ }
+
+ /* Turn off the event enable for GPIO7 just to be safe */
+ cs5535_gpio_clear(OLPC_GPIO_DCON_IRQ, GPIO_EVENTS_ENABLE);
+
+ /*
+ * Determine the current state by reading the GPIO bit; earlier
+ * stages of the boot process have established the state.
+ *
+ * Note that we read GPIO_OUTPUT_VAL rather than GPIO_READ_BACK here;
+ * this is because OFW will disable input for the pin and set a value..
+ * READ_BACK will only contain a valid value if input is enabled and
+ * then a value is set. So, future readings of the pin can use
+ * READ_BACK, but the first one cannot. Awesome, huh?
+ */
+ dcon->curr_src = cs5535_gpio_isset(OLPC_GPIO_DCON_LOAD, GPIO_OUTPUT_VAL)
+ ? DCON_SOURCE_CPU
+ : DCON_SOURCE_DCON;
+ dcon->pending_src = dcon->curr_src;
+
+ /* Set the directions for the GPIO pins */
+ gpio_direction_input(OLPC_GPIO_DCON_STAT0);
+ gpio_direction_input(OLPC_GPIO_DCON_STAT1);
+ gpio_direction_input(OLPC_GPIO_DCON_IRQ);
+ gpio_direction_input(OLPC_GPIO_DCON_BLANK);
+ gpio_direction_output(OLPC_GPIO_DCON_LOAD,
+ dcon->curr_src == DCON_SOURCE_CPU);
+
+ /* Set up the interrupt mappings */
+
+ /* Set the IRQ to pair 2 */
+ cs5535_gpio_setup_event(OLPC_GPIO_DCON_IRQ, 2, 0);
+
+ /* Enable group 2 to trigger the DCON interrupt */
+ cs5535_gpio_set_irq(2, DCON_IRQ);
+
+ /* Select edge level for interrupt (in PIC) */
+ lob = inb(0x4d0);
+ lob &= ~(1 << DCON_IRQ);
+ outb(lob, 0x4d0);
+
+ /* Register the interrupt handler */
+ if (request_irq(DCON_IRQ, &dcon_interrupt, 0, "DCON", dcon)) {
+ pr_err("failed to request DCON's irq\n");
+ goto err_req_irq;
+ }
+
+ /* Clear INV_EN for GPIO7 (DCONIRQ) */
+ cs5535_gpio_clear(OLPC_GPIO_DCON_IRQ, GPIO_INPUT_INVERT);
+
+ /* Enable filter for GPIO12 (DCONBLANK) */
+ cs5535_gpio_set(OLPC_GPIO_DCON_BLANK, GPIO_INPUT_FILTER);
+
+ /* Disable filter for GPIO7 */
+ cs5535_gpio_clear(OLPC_GPIO_DCON_IRQ, GPIO_INPUT_FILTER);
+
+ /* Disable event counter for GPIO7 (DCONIRQ) and GPIO12 (DCONBLANK) */
+ cs5535_gpio_clear(OLPC_GPIO_DCON_IRQ, GPIO_INPUT_EVENT_COUNT);
+ cs5535_gpio_clear(OLPC_GPIO_DCON_BLANK, GPIO_INPUT_EVENT_COUNT);
+
+ /* Add GPIO12 to the Filter Event Pair #7 */
+ cs5535_gpio_set(OLPC_GPIO_DCON_BLANK, GPIO_FE7_SEL);
+
+ /* Turn off negative Edge Enable for GPIO12 */
+ cs5535_gpio_clear(OLPC_GPIO_DCON_BLANK, GPIO_NEGATIVE_EDGE_EN);
+
+ /* Enable negative Edge Enable for GPIO7 */
+ cs5535_gpio_set(OLPC_GPIO_DCON_IRQ, GPIO_NEGATIVE_EDGE_EN);
+
+ /* Zero the filter amount for Filter Event Pair #7 */
+ cs5535_gpio_set(0, GPIO_FLTR7_AMOUNT);
+
+ /* Clear the negative edge status for GPIO7 and GPIO12 */
+ cs5535_gpio_set(OLPC_GPIO_DCON_IRQ, GPIO_NEGATIVE_EDGE_STS);
+ cs5535_gpio_set(OLPC_GPIO_DCON_BLANK, GPIO_NEGATIVE_EDGE_STS);
+
+ /* FIXME: Clear the positive status as well, just to be sure */
+ cs5535_gpio_set(OLPC_GPIO_DCON_IRQ, GPIO_POSITIVE_EDGE_STS);
+ cs5535_gpio_set(OLPC_GPIO_DCON_BLANK, GPIO_POSITIVE_EDGE_STS);
+
+ /* Enable events for GPIO7 (DCONIRQ) and GPIO12 (DCONBLANK) */
+ cs5535_gpio_set(OLPC_GPIO_DCON_IRQ, GPIO_EVENTS_ENABLE);
+ cs5535_gpio_set(OLPC_GPIO_DCON_BLANK, GPIO_EVENTS_ENABLE);
+
+ return 0;
+
+err_req_irq:
+ gpio_free(OLPC_GPIO_DCON_BLANK);
+err_gp_blank:
+ gpio_free(OLPC_GPIO_DCON_LOAD);
+err_gp_load:
+ gpio_free(OLPC_GPIO_DCON_IRQ);
+err_gp_irq:
+ gpio_free(OLPC_GPIO_DCON_STAT1);
+err_gp_stat1:
+ gpio_free(OLPC_GPIO_DCON_STAT0);
+ return -EIO;
+}
+
+static void dcon_wiggle_xo_1(void)
+{
+ int x;
+
+ /*
+ * According to HiMax, when powering the DCON up we should hold
+ * SMB_DATA high for 8 SMB_CLK cycles. This will force the DCON
+ * state machine to reset to a (sane) initial state. Mitch Bradley
+ * did some testing and discovered that holding for 16 SMB_CLK cycles
+ * worked a lot more reliably, so that's what we do here.
+ *
+ * According to the cs5536 spec, to set GPIO14 to SMB_CLK we must
+ * simultaneously set AUX1 IN/OUT to GPIO14; ditto for SMB_DATA and
+ * GPIO15.
+ */
+ cs5535_gpio_set(OLPC_GPIO_SMB_CLK, GPIO_OUTPUT_VAL);
+ cs5535_gpio_set(OLPC_GPIO_SMB_DATA, GPIO_OUTPUT_VAL);
+ cs5535_gpio_set(OLPC_GPIO_SMB_CLK, GPIO_OUTPUT_ENABLE);
+ cs5535_gpio_set(OLPC_GPIO_SMB_DATA, GPIO_OUTPUT_ENABLE);
+ cs5535_gpio_clear(OLPC_GPIO_SMB_CLK, GPIO_OUTPUT_AUX1);
+ cs5535_gpio_clear(OLPC_GPIO_SMB_DATA, GPIO_OUTPUT_AUX1);
+ cs5535_gpio_clear(OLPC_GPIO_SMB_CLK, GPIO_OUTPUT_AUX2);
+ cs5535_gpio_clear(OLPC_GPIO_SMB_DATA, GPIO_OUTPUT_AUX2);
+ cs5535_gpio_clear(OLPC_GPIO_SMB_CLK, GPIO_INPUT_AUX1);
+ cs5535_gpio_clear(OLPC_GPIO_SMB_DATA, GPIO_INPUT_AUX1);
+
+ for (x = 0; x < 16; x++) {
+ udelay(5);
+ cs5535_gpio_clear(OLPC_GPIO_SMB_CLK, GPIO_OUTPUT_VAL);
+ udelay(5);
+ cs5535_gpio_set(OLPC_GPIO_SMB_CLK, GPIO_OUTPUT_VAL);
+ }
+ udelay(5);
+ cs5535_gpio_set(OLPC_GPIO_SMB_CLK, GPIO_OUTPUT_AUX1);
+ cs5535_gpio_set(OLPC_GPIO_SMB_DATA, GPIO_OUTPUT_AUX1);
+ cs5535_gpio_set(OLPC_GPIO_SMB_CLK, GPIO_INPUT_AUX1);
+ cs5535_gpio_set(OLPC_GPIO_SMB_DATA, GPIO_INPUT_AUX1);
+}
+
+static void dcon_set_dconload_1(int val)
+{
+ gpio_set_value(OLPC_GPIO_DCON_LOAD, val);
+}
+
+static int dcon_read_status_xo_1(u8 *status)
+{
+ *status = gpio_get_value(OLPC_GPIO_DCON_STAT0);
+ *status |= gpio_get_value(OLPC_GPIO_DCON_STAT1) << 1;
+
+ /* Clear the negative edge status for GPIO7 */
+ cs5535_gpio_set(OLPC_GPIO_DCON_IRQ, GPIO_NEGATIVE_EDGE_STS);
+
+ return 0;
+}
+
+struct dcon_platform_data dcon_pdata_xo_1 = {
+ .init = dcon_init_xo_1,
+ .bus_stabilize_wiggle = dcon_wiggle_xo_1,
+ .set_dconload = dcon_set_dconload_1,
+ .read_status = dcon_read_status_xo_1,
+};
diff --git a/drivers/staging/olpc_dcon/olpc_dcon_xo_1_5.c b/drivers/staging/olpc_dcon/olpc_dcon_xo_1_5.c
new file mode 100644
index 000000000..6a4d379c1
--- /dev/null
+++ b/drivers/staging/olpc_dcon/olpc_dcon_xo_1_5.c
@@ -0,0 +1,161 @@
+/*
+ * Copyright (c) 2009,2010 One Laptop per Child
+ *
+ * This program is free software. You can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/acpi.h>
+#include <linux/delay.h>
+#include <linux/gpio.h>
+#include <asm/olpc.h>
+
+/* TODO: this eventually belongs in linux/vx855.h */
+#define NR_VX855_GPI 14
+#define NR_VX855_GPO 13
+#define NR_VX855_GPIO 15
+
+#define VX855_GPI(n) (n)
+#define VX855_GPO(n) (NR_VX855_GPI + (n))
+#define VX855_GPIO(n) (NR_VX855_GPI + NR_VX855_GPO + (n))
+
+#include "olpc_dcon.h"
+
+/* Hardware setup on the XO 1.5:
+ * DCONLOAD connects to VX855_GPIO1 (not SMBCK2)
+ * DCONBLANK connects to VX855_GPIO8 (not SSPICLK) unused in driver
+ * DCONSTAT0 connects to VX855_GPI10 (not SSPISDI)
+ * DCONSTAT1 connects to VX855_GPI11 (not nSSPISS)
+ * DCONIRQ connects to VX855_GPIO12
+ * DCONSMBDATA connects to VX855 graphics CRTSPD
+ * DCONSMBCLK connects to VX855 graphics CRTSPCLK
+ */
+
+#define VX855_GENL_PURPOSE_OUTPUT 0x44c /* PMIO_Rx4c-4f */
+#define VX855_GPI_STATUS_CHG 0x450 /* PMIO_Rx50 */
+#define VX855_GPI_SCI_SMI 0x452 /* PMIO_Rx52 */
+#define BIT_GPIO12 0x40
+
+#define PREFIX "OLPC DCON:"
+
+static void dcon_clear_irq(void)
+{
+ /* irq status will appear in PMIO_Rx50[6] (RW1C) on gpio12 */
+ outb(BIT_GPIO12, VX855_GPI_STATUS_CHG);
+}
+
+static int dcon_was_irq(void)
+{
+ u_int8_t tmp;
+
+ /* irq status will appear in PMIO_Rx50[6] on gpio12 */
+ tmp = inb(VX855_GPI_STATUS_CHG);
+ return !!(tmp & BIT_GPIO12);
+
+ return 0;
+}
+
+static int dcon_init_xo_1_5(struct dcon_priv *dcon)
+{
+ unsigned int irq;
+
+ dcon_clear_irq();
+
+ /* set PMIO_Rx52[6] to enable SCI/SMI on gpio12 */
+ outb(inb(VX855_GPI_SCI_SMI)|BIT_GPIO12, VX855_GPI_SCI_SMI);
+
+ /* Determine the current state of DCONLOAD, likely set by firmware */
+ /* GPIO1 */
+ dcon->curr_src = (inl(VX855_GENL_PURPOSE_OUTPUT) & 0x1000) ?
+ DCON_SOURCE_CPU : DCON_SOURCE_DCON;
+ dcon->pending_src = dcon->curr_src;
+
+ /* we're sharing the IRQ with ACPI */
+ irq = acpi_gbl_FADT.sci_interrupt;
+ if (request_irq(irq, &dcon_interrupt, IRQF_SHARED, "DCON", dcon)) {
+ pr_err("DCON (IRQ%d) allocation failed\n", irq);
+ return 1;
+ }
+
+ return 0;
+}
+
+static void set_i2c_line(int sda, int scl)
+{
+ unsigned char tmp;
+ unsigned int port = 0x26;
+
+ /* FIXME: This directly accesses the CRT GPIO controller !!! */
+ outb(port, 0x3c4);
+ tmp = inb(0x3c5);
+
+ if (scl)
+ tmp |= 0x20;
+ else
+ tmp &= ~0x20;
+
+ if (sda)
+ tmp |= 0x10;
+ else
+ tmp &= ~0x10;
+
+ tmp |= 0x01;
+
+ outb(port, 0x3c4);
+ outb(tmp, 0x3c5);
+}
+
+
+static void dcon_wiggle_xo_1_5(void)
+{
+ int x;
+
+ /*
+ * According to HiMax, when powering the DCON up we should hold
+ * SMB_DATA high for 8 SMB_CLK cycles. This will force the DCON
+ * state machine to reset to a (sane) initial state. Mitch Bradley
+ * did some testing and discovered that holding for 16 SMB_CLK cycles
+ * worked a lot more reliably, so that's what we do here.
+ */
+ set_i2c_line(1, 1);
+
+ for (x = 0; x < 16; x++) {
+ udelay(5);
+ set_i2c_line(1, 0);
+ udelay(5);
+ set_i2c_line(1, 1);
+ }
+ udelay(5);
+
+ /* set PMIO_Rx52[6] to enable SCI/SMI on gpio12 */
+ outb(inb(VX855_GPI_SCI_SMI)|BIT_GPIO12, VX855_GPI_SCI_SMI);
+}
+
+static void dcon_set_dconload_xo_1_5(int val)
+{
+ gpio_set_value(VX855_GPIO(1), val);
+}
+
+static int dcon_read_status_xo_1_5(u8 *status)
+{
+ if (!dcon_was_irq())
+ return -1;
+
+ /* i believe this is the same as "inb(0x44b) & 3" */
+ *status = gpio_get_value(VX855_GPI(10));
+ *status |= gpio_get_value(VX855_GPI(11)) << 1;
+
+ dcon_clear_irq();
+
+ return 0;
+}
+
+struct dcon_platform_data dcon_pdata_xo_1_5 = {
+ .init = dcon_init_xo_1_5,
+ .bus_stabilize_wiggle = dcon_wiggle_xo_1_5,
+ .set_dconload = dcon_set_dconload_xo_1_5,
+ .read_status = dcon_read_status_xo_1_5,
+};
diff --git a/drivers/staging/ozwpan/Kconfig b/drivers/staging/ozwpan/Kconfig
new file mode 100644
index 000000000..7904caec5
--- /dev/null
+++ b/drivers/staging/ozwpan/Kconfig
@@ -0,0 +1,9 @@
+config USB_WPAN_HCD
+ tristate "USB over WiFi Host Controller"
+ depends on USB && NET
+ help
+ A driver for USB Host Controllers that are compatible with
+ Ozmo Devices USB over WiFi technology.
+
+ To compile this driver a module, choose M here: the module
+ will be called "ozwpan".
diff --git a/drivers/staging/ozwpan/Makefile b/drivers/staging/ozwpan/Makefile
new file mode 100644
index 000000000..29529c1a8
--- /dev/null
+++ b/drivers/staging/ozwpan/Makefile
@@ -0,0 +1,16 @@
+# -----------------------------------------------------------------------------
+# Copyright (c) 2011 Ozmo Inc
+# Released under the GNU General Public License Version 2 (GPLv2).
+# -----------------------------------------------------------------------------
+
+obj-$(CONFIG_USB_WPAN_HCD) += ozwpan.o
+ozwpan-y := \
+ ozmain.o \
+ ozpd.o \
+ ozusbsvc.o \
+ ozusbsvc1.o \
+ ozhcd.o \
+ ozeltbuf.o \
+ ozproto.o \
+ ozcdev.o \
+ ozurbparanoia.o
diff --git a/drivers/staging/ozwpan/README b/drivers/staging/ozwpan/README
new file mode 100644
index 000000000..7c055ec99
--- /dev/null
+++ b/drivers/staging/ozwpan/README
@@ -0,0 +1,25 @@
+OZWPAN USB Host Controller Driver
+---------------------------------
+This driver is a USB HCD driver that does not have an associated a physical
+device but instead uses Wi-Fi to communicate with the wireless peripheral.
+The USB requests are converted into a layer 2 network protocol and transmitted
+on the network using an ethertype (0x892e) regestered to Ozmo Device Inc.
+This driver is compatible with existing wireless devices that use Ozmo Devices
+technology.
+
+To operate the driver must be bound to a suitable network interface. This can
+be done when the module is loaded (specifying the name of the network interface
+as a parameter - e.g. 'insmod ozwpan g_net_dev=go0') or can be bound after
+loading using an ioctl call. See the ozappif.h file and the ioctls
+OZ_IOCTL_ADD_BINDING and OZ_IOCTL_REMOVE_BINDING.
+
+The devices connect to the host use Wi-Fi Direct so a network card that supports
+Wi-Fi direct is required. A recent version (0.8.x or later) version of the
+wpa_supplicant can be used to setup the network interface to create a persistent
+autonomous group (for older pre-WFD peripherals) or put in a listen state to
+allow group negotiation to occur for more recent devices that support WFD.
+
+The protocol used over the network does not directly mimic the USB bus
+transactions as this would be rather busy and inefficient. Instead the chapter 9
+requests are converted into a request/response pair of messages. (See
+ozprotocol.h for data structures used in the protocol).
diff --git a/drivers/staging/ozwpan/TODO b/drivers/staging/ozwpan/TODO
new file mode 100644
index 000000000..f32c1c0bc
--- /dev/null
+++ b/drivers/staging/ozwpan/TODO
@@ -0,0 +1,14 @@
+TODO:
+ - Convert event tracing code to in-kernel tracing infrastructure
+ - Check for remaining ioctl & check if that can be converted into
+ sysfs entries
+ - Convert debug prints to appropriate dev_debug or something better
+ - Modify Kconfig to add CONFIG option for enabling/disabling event
+ tracing.
+ - check USB HCD implementation is complete and correct.
+ - code review by USB developer community.
+ - testing with as many devices as possible.
+
+Please send any patches for this driver to
+Shigekatsu Tateno <shigekatsu.tateno@atmel.com>
+and Greg Kroah-Hartman <gregkh@linuxfoundation.org>.
diff --git a/drivers/staging/ozwpan/ozappif.h b/drivers/staging/ozwpan/ozappif.h
new file mode 100644
index 000000000..ea1b271fd
--- /dev/null
+++ b/drivers/staging/ozwpan/ozappif.h
@@ -0,0 +1,36 @@
+/* -----------------------------------------------------------------------------
+ * Copyright (c) 2011 Ozmo Inc
+ * Released under the GNU General Public License Version 2 (GPLv2).
+ * -----------------------------------------------------------------------------
+ */
+#ifndef _OZAPPIF_H
+#define _OZAPPIF_H
+
+#define OZ_IOCTL_MAGIC 0xf4
+
+struct oz_mac_addr {
+ __u8 a[6];
+};
+
+#define OZ_MAX_PDS 8
+
+struct oz_pd_list {
+ __u32 count;
+ struct oz_mac_addr addr[OZ_MAX_PDS];
+};
+
+#define OZ_MAX_BINDING_LEN 32
+
+struct oz_binding_info {
+ char name[OZ_MAX_BINDING_LEN];
+};
+
+#define OZ_IOCTL_GET_PD_LIST _IOR(OZ_IOCTL_MAGIC, 0, struct oz_pd_list)
+#define OZ_IOCTL_SET_ACTIVE_PD _IOW(OZ_IOCTL_MAGIC, 1, struct oz_mac_addr)
+#define OZ_IOCTL_GET_ACTIVE_PD _IOR(OZ_IOCTL_MAGIC, 2, struct oz_mac_addr)
+#define OZ_IOCTL_ADD_BINDING _IOW(OZ_IOCTL_MAGIC, 3, struct oz_binding_info)
+#define OZ_IOCTL_REMOVE_BINDING _IOW(OZ_IOCTL_MAGIC, 4, struct oz_binding_info)
+#define OZ_IOCTL_MAX 5
+
+
+#endif /* _OZAPPIF_H */
diff --git a/drivers/staging/ozwpan/ozcdev.c b/drivers/staging/ozwpan/ozcdev.c
new file mode 100644
index 000000000..da0e1fd50
--- /dev/null
+++ b/drivers/staging/ozwpan/ozcdev.c
@@ -0,0 +1,554 @@
+/* -----------------------------------------------------------------------------
+ * Copyright (c) 2011 Ozmo Inc
+ * Released under the GNU General Public License Version 2 (GPLv2).
+ * -----------------------------------------------------------------------------
+ */
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/cdev.h>
+#include <linux/uaccess.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/poll.h>
+#include <linux/sched.h>
+#include "ozdbg.h"
+#include "ozprotocol.h"
+#include "ozappif.h"
+#include "ozeltbuf.h"
+#include "ozpd.h"
+#include "ozproto.h"
+#include "ozcdev.h"
+
+#define OZ_RD_BUF_SZ 256
+struct oz_cdev {
+ dev_t devnum;
+ struct cdev cdev;
+ wait_queue_head_t rdq;
+ spinlock_t lock;
+ u8 active_addr[ETH_ALEN];
+ struct oz_pd *active_pd;
+};
+
+/* Per PD context for the serial service stored in the PD. */
+struct oz_serial_ctx {
+ atomic_t ref_count;
+ u8 tx_seq_num;
+ u8 rx_seq_num;
+ u8 rd_buf[OZ_RD_BUF_SZ];
+ int rd_in;
+ int rd_out;
+};
+
+static struct oz_cdev g_cdev;
+static struct class *g_oz_class;
+
+/*
+ * Context: process and softirq
+ */
+static struct oz_serial_ctx *oz_cdev_claim_ctx(struct oz_pd *pd)
+{
+ struct oz_serial_ctx *ctx;
+
+ spin_lock_bh(&pd->app_lock[OZ_APPID_SERIAL]);
+ ctx = (struct oz_serial_ctx *) pd->app_ctx[OZ_APPID_SERIAL];
+ if (ctx)
+ atomic_inc(&ctx->ref_count);
+ spin_unlock_bh(&pd->app_lock[OZ_APPID_SERIAL]);
+ return ctx;
+}
+
+/*
+ * Context: softirq or process
+ */
+static void oz_cdev_release_ctx(struct oz_serial_ctx *ctx)
+{
+ if (atomic_dec_and_test(&ctx->ref_count)) {
+ oz_dbg(ON, "Dealloc serial context\n");
+ kfree(ctx);
+ }
+}
+
+/*
+ * Context: process
+ */
+static int oz_cdev_open(struct inode *inode, struct file *filp)
+{
+ struct oz_cdev *dev = container_of(inode->i_cdev, struct oz_cdev, cdev);
+
+ oz_dbg(ON, "major = %d minor = %d\n", imajor(inode), iminor(inode));
+
+ filp->private_data = dev;
+ return 0;
+}
+
+/*
+ * Context: process
+ */
+static int oz_cdev_release(struct inode *inode, struct file *filp)
+{
+ return 0;
+}
+
+/*
+ * Context: process
+ */
+static ssize_t oz_cdev_read(struct file *filp, char __user *buf, size_t count,
+ loff_t *fpos)
+{
+ int n;
+ int ix;
+
+ struct oz_pd *pd;
+ struct oz_serial_ctx *ctx;
+
+ spin_lock_bh(&g_cdev.lock);
+ pd = g_cdev.active_pd;
+ if (pd)
+ oz_pd_get(pd);
+ spin_unlock_bh(&g_cdev.lock);
+ if (pd == NULL)
+ return -1;
+ ctx = oz_cdev_claim_ctx(pd);
+ if (ctx == NULL)
+ goto out2;
+ n = ctx->rd_in - ctx->rd_out;
+ if (n < 0)
+ n += OZ_RD_BUF_SZ;
+ if (count > n)
+ count = n;
+ ix = ctx->rd_out;
+ n = OZ_RD_BUF_SZ - ix;
+ if (n > count)
+ n = count;
+ if (copy_to_user(buf, &ctx->rd_buf[ix], n)) {
+ count = 0;
+ goto out1;
+ }
+ ix += n;
+ if (ix == OZ_RD_BUF_SZ)
+ ix = 0;
+ if (n < count) {
+ if (copy_to_user(&buf[n], ctx->rd_buf, count-n)) {
+ count = 0;
+ goto out1;
+ }
+ ix = count-n;
+ }
+ ctx->rd_out = ix;
+out1:
+ oz_cdev_release_ctx(ctx);
+out2:
+ oz_pd_put(pd);
+ return count;
+}
+
+/*
+ * Context: process
+ */
+static ssize_t oz_cdev_write(struct file *filp, const char __user *buf,
+ size_t count, loff_t *fpos)
+{
+ struct oz_pd *pd;
+ struct oz_elt_buf *eb;
+ struct oz_elt_info *ei;
+ struct oz_elt *elt;
+ struct oz_app_hdr *app_hdr;
+ struct oz_serial_ctx *ctx;
+
+ if (count > sizeof(ei->data) - sizeof(*elt) - sizeof(*app_hdr))
+ return -EINVAL;
+
+ spin_lock_bh(&g_cdev.lock);
+ pd = g_cdev.active_pd;
+ if (pd)
+ oz_pd_get(pd);
+ spin_unlock_bh(&g_cdev.lock);
+ if (pd == NULL)
+ return -ENXIO;
+ if (!(pd->state & OZ_PD_S_CONNECTED))
+ return -EAGAIN;
+ eb = &pd->elt_buff;
+ ei = oz_elt_info_alloc(eb);
+ if (ei == NULL) {
+ count = 0;
+ goto out;
+ }
+ elt = (struct oz_elt *)ei->data;
+ app_hdr = (struct oz_app_hdr *)(elt+1);
+ elt->length = sizeof(struct oz_app_hdr) + count;
+ elt->type = OZ_ELT_APP_DATA;
+ ei->app_id = OZ_APPID_SERIAL;
+ ei->length = elt->length + sizeof(struct oz_elt);
+ app_hdr->app_id = OZ_APPID_SERIAL;
+ if (copy_from_user(app_hdr+1, buf, count))
+ goto out;
+ spin_lock_bh(&pd->app_lock[OZ_APPID_USB]);
+ ctx = (struct oz_serial_ctx *) pd->app_ctx[OZ_APPID_SERIAL];
+ if (ctx) {
+ app_hdr->elt_seq_num = ctx->tx_seq_num++;
+ if (ctx->tx_seq_num == 0)
+ ctx->tx_seq_num = 1;
+ spin_lock(&eb->lock);
+ if (oz_queue_elt_info(eb, 0, 0, ei) == 0)
+ ei = NULL;
+ spin_unlock(&eb->lock);
+ }
+ spin_unlock_bh(&pd->app_lock[OZ_APPID_USB]);
+out:
+ if (ei) {
+ count = 0;
+ spin_lock_bh(&eb->lock);
+ oz_elt_info_free(eb, ei);
+ spin_unlock_bh(&eb->lock);
+ }
+ oz_pd_put(pd);
+ return count;
+}
+
+/*
+ * Context: process
+ */
+static int oz_set_active_pd(const u8 *addr)
+{
+ int rc = 0;
+ struct oz_pd *pd;
+ struct oz_pd *old_pd;
+
+ pd = oz_pd_find(addr);
+ if (pd) {
+ spin_lock_bh(&g_cdev.lock);
+ ether_addr_copy(g_cdev.active_addr, addr);
+ old_pd = g_cdev.active_pd;
+ g_cdev.active_pd = pd;
+ spin_unlock_bh(&g_cdev.lock);
+ if (old_pd)
+ oz_pd_put(old_pd);
+ } else {
+ if (is_zero_ether_addr(addr)) {
+ spin_lock_bh(&g_cdev.lock);
+ pd = g_cdev.active_pd;
+ g_cdev.active_pd = NULL;
+ memset(g_cdev.active_addr, 0,
+ sizeof(g_cdev.active_addr));
+ spin_unlock_bh(&g_cdev.lock);
+ if (pd)
+ oz_pd_put(pd);
+ } else {
+ rc = -1;
+ }
+ }
+ return rc;
+}
+
+/*
+ * Context: process
+ */
+static long oz_cdev_ioctl(struct file *filp, unsigned int cmd,
+ unsigned long arg)
+{
+ int rc = 0;
+
+ if (_IOC_TYPE(cmd) != OZ_IOCTL_MAGIC)
+ return -ENOTTY;
+ if (_IOC_NR(cmd) > OZ_IOCTL_MAX)
+ return -ENOTTY;
+ if (_IOC_DIR(cmd) & _IOC_READ)
+ rc = !access_ok(VERIFY_WRITE, (void __user *)arg,
+ _IOC_SIZE(cmd));
+ else if (_IOC_DIR(cmd) & _IOC_WRITE)
+ rc = !access_ok(VERIFY_READ, (void __user *)arg,
+ _IOC_SIZE(cmd));
+ if (rc)
+ return -EFAULT;
+ switch (cmd) {
+ case OZ_IOCTL_GET_PD_LIST: {
+ struct oz_pd_list list;
+
+ oz_dbg(ON, "OZ_IOCTL_GET_PD_LIST\n");
+ memset(&list, 0, sizeof(list));
+ list.count = oz_get_pd_list(list.addr, OZ_MAX_PDS);
+ if (copy_to_user((void __user *)arg, &list,
+ sizeof(list)))
+ return -EFAULT;
+ }
+ break;
+ case OZ_IOCTL_SET_ACTIVE_PD: {
+ u8 addr[ETH_ALEN];
+
+ oz_dbg(ON, "OZ_IOCTL_SET_ACTIVE_PD\n");
+ if (copy_from_user(addr, (void __user *)arg, ETH_ALEN))
+ return -EFAULT;
+ rc = oz_set_active_pd(addr);
+ }
+ break;
+ case OZ_IOCTL_GET_ACTIVE_PD: {
+ u8 addr[ETH_ALEN];
+
+ oz_dbg(ON, "OZ_IOCTL_GET_ACTIVE_PD\n");
+ spin_lock_bh(&g_cdev.lock);
+ ether_addr_copy(addr, g_cdev.active_addr);
+ spin_unlock_bh(&g_cdev.lock);
+ if (copy_to_user((void __user *)arg, addr, ETH_ALEN))
+ return -EFAULT;
+ }
+ break;
+ case OZ_IOCTL_ADD_BINDING:
+ case OZ_IOCTL_REMOVE_BINDING: {
+ struct oz_binding_info b;
+
+ if (copy_from_user(&b, (void __user *)arg,
+ sizeof(struct oz_binding_info))) {
+ return -EFAULT;
+ }
+ /* Make sure name is null terminated. */
+ b.name[OZ_MAX_BINDING_LEN-1] = 0;
+ if (cmd == OZ_IOCTL_ADD_BINDING)
+ oz_binding_add(b.name);
+ else
+ oz_binding_remove(b.name);
+ }
+ break;
+ }
+ return rc;
+}
+
+/*
+ * Context: process
+ */
+static unsigned int oz_cdev_poll(struct file *filp, poll_table *wait)
+{
+ unsigned int ret = 0;
+ struct oz_cdev *dev = filp->private_data;
+
+ oz_dbg(ON, "Poll called wait = %p\n", wait);
+ spin_lock_bh(&dev->lock);
+ if (dev->active_pd) {
+ struct oz_serial_ctx *ctx = oz_cdev_claim_ctx(dev->active_pd);
+
+ if (ctx) {
+ if (ctx->rd_in != ctx->rd_out)
+ ret |= POLLIN | POLLRDNORM;
+ oz_cdev_release_ctx(ctx);
+ }
+ }
+ spin_unlock_bh(&dev->lock);
+ if (wait)
+ poll_wait(filp, &dev->rdq, wait);
+ return ret;
+}
+
+/*
+ */
+static const struct file_operations oz_fops = {
+ .owner = THIS_MODULE,
+ .open = oz_cdev_open,
+ .release = oz_cdev_release,
+ .read = oz_cdev_read,
+ .write = oz_cdev_write,
+ .unlocked_ioctl = oz_cdev_ioctl,
+ .poll = oz_cdev_poll
+};
+
+/*
+ * Context: process
+ */
+int oz_cdev_register(void)
+{
+ int err;
+ struct device *dev;
+
+ memset(&g_cdev, 0, sizeof(g_cdev));
+ err = alloc_chrdev_region(&g_cdev.devnum, 0, 1, "ozwpan");
+ if (err < 0)
+ return err;
+ oz_dbg(ON, "Alloc dev number %d:%d\n",
+ MAJOR(g_cdev.devnum), MINOR(g_cdev.devnum));
+ cdev_init(&g_cdev.cdev, &oz_fops);
+ g_cdev.cdev.owner = THIS_MODULE;
+ spin_lock_init(&g_cdev.lock);
+ init_waitqueue_head(&g_cdev.rdq);
+ err = cdev_add(&g_cdev.cdev, g_cdev.devnum, 1);
+ if (err < 0) {
+ oz_dbg(ON, "Failed to add cdev\n");
+ goto unregister;
+ }
+ g_oz_class = class_create(THIS_MODULE, "ozmo_wpan");
+ if (IS_ERR(g_oz_class)) {
+ oz_dbg(ON, "Failed to register ozmo_wpan class\n");
+ err = PTR_ERR(g_oz_class);
+ goto delete;
+ }
+ dev = device_create(g_oz_class, NULL, g_cdev.devnum, NULL, "ozwpan");
+ if (IS_ERR(dev)) {
+ oz_dbg(ON, "Failed to create sysfs entry for cdev\n");
+ err = PTR_ERR(dev);
+ goto delete;
+ }
+ return 0;
+
+delete:
+ cdev_del(&g_cdev.cdev);
+unregister:
+ unregister_chrdev_region(g_cdev.devnum, 1);
+ return err;
+}
+
+/*
+ * Context: process
+ */
+int oz_cdev_deregister(void)
+{
+ cdev_del(&g_cdev.cdev);
+ unregister_chrdev_region(g_cdev.devnum, 1);
+ if (g_oz_class) {
+ device_destroy(g_oz_class, g_cdev.devnum);
+ class_destroy(g_oz_class);
+ }
+ return 0;
+}
+
+/*
+ * Context: process
+ */
+int oz_cdev_init(void)
+{
+ oz_app_enable(OZ_APPID_SERIAL, 1);
+ return 0;
+}
+
+/*
+ * Context: process
+ */
+void oz_cdev_term(void)
+{
+ oz_app_enable(OZ_APPID_SERIAL, 0);
+}
+
+/*
+ * Context: softirq-serialized
+ */
+int oz_cdev_start(struct oz_pd *pd, int resume)
+{
+ struct oz_serial_ctx *ctx;
+ struct oz_serial_ctx *old_ctx;
+
+ if (resume) {
+ oz_dbg(ON, "Serial service resumed\n");
+ return 0;
+ }
+ ctx = kzalloc(sizeof(struct oz_serial_ctx), GFP_ATOMIC);
+ if (ctx == NULL)
+ return -ENOMEM;
+ atomic_set(&ctx->ref_count, 1);
+ ctx->tx_seq_num = 1;
+ spin_lock_bh(&pd->app_lock[OZ_APPID_SERIAL]);
+ old_ctx = pd->app_ctx[OZ_APPID_SERIAL];
+ if (old_ctx) {
+ spin_unlock_bh(&pd->app_lock[OZ_APPID_SERIAL]);
+ kfree(ctx);
+ } else {
+ pd->app_ctx[OZ_APPID_SERIAL] = ctx;
+ spin_unlock_bh(&pd->app_lock[OZ_APPID_SERIAL]);
+ }
+ spin_lock(&g_cdev.lock);
+ if ((g_cdev.active_pd == NULL) &&
+ ether_addr_equal(pd->mac_addr, g_cdev.active_addr)) {
+ oz_pd_get(pd);
+ g_cdev.active_pd = pd;
+ oz_dbg(ON, "Active PD arrived\n");
+ }
+ spin_unlock(&g_cdev.lock);
+ oz_dbg(ON, "Serial service started\n");
+ return 0;
+}
+
+/*
+ * Context: softirq or process
+ */
+void oz_cdev_stop(struct oz_pd *pd, int pause)
+{
+ struct oz_serial_ctx *ctx;
+
+ if (pause) {
+ oz_dbg(ON, "Serial service paused\n");
+ return;
+ }
+ spin_lock_bh(&pd->app_lock[OZ_APPID_SERIAL]);
+ ctx = (struct oz_serial_ctx *) pd->app_ctx[OZ_APPID_SERIAL];
+ pd->app_ctx[OZ_APPID_SERIAL] = NULL;
+ spin_unlock_bh(&pd->app_lock[OZ_APPID_SERIAL]);
+ if (ctx)
+ oz_cdev_release_ctx(ctx);
+ spin_lock(&g_cdev.lock);
+ if (pd == g_cdev.active_pd)
+ g_cdev.active_pd = NULL;
+ else
+ pd = NULL;
+ spin_unlock(&g_cdev.lock);
+ if (pd) {
+ oz_pd_put(pd);
+ oz_dbg(ON, "Active PD departed\n");
+ }
+ oz_dbg(ON, "Serial service stopped\n");
+}
+
+/*
+ * Context: softirq-serialized
+ */
+void oz_cdev_rx(struct oz_pd *pd, struct oz_elt *elt)
+{
+ struct oz_serial_ctx *ctx;
+ struct oz_app_hdr *app_hdr;
+ u8 *data;
+ int len;
+ int space;
+ int copy_sz;
+ int ix;
+
+ ctx = oz_cdev_claim_ctx(pd);
+ if (ctx == NULL) {
+ oz_dbg(ON, "Cannot claim serial context\n");
+ return;
+ }
+
+ app_hdr = (struct oz_app_hdr *)(elt+1);
+ /* If sequence number is non-zero then check it is not a duplicate.
+ */
+ if (app_hdr->elt_seq_num != 0) {
+ if (((ctx->rx_seq_num - app_hdr->elt_seq_num) & 0x80) == 0) {
+ /* Reject duplicate element. */
+ oz_dbg(ON, "Duplicate element:%02x %02x\n",
+ app_hdr->elt_seq_num, ctx->rx_seq_num);
+ goto out;
+ }
+ }
+ ctx->rx_seq_num = app_hdr->elt_seq_num;
+ len = elt->length - sizeof(struct oz_app_hdr);
+ data = ((u8 *)(elt+1)) + sizeof(struct oz_app_hdr);
+ if (len <= 0)
+ goto out;
+ space = ctx->rd_out - ctx->rd_in - 1;
+ if (space < 0)
+ space += OZ_RD_BUF_SZ;
+ if (len > space) {
+ oz_dbg(ON, "Not enough space:%d %d\n", len, space);
+ len = space;
+ }
+ ix = ctx->rd_in;
+ copy_sz = OZ_RD_BUF_SZ - ix;
+ if (copy_sz > len)
+ copy_sz = len;
+ memcpy(&ctx->rd_buf[ix], data, copy_sz);
+ len -= copy_sz;
+ ix += copy_sz;
+ if (ix == OZ_RD_BUF_SZ)
+ ix = 0;
+ if (len) {
+ memcpy(ctx->rd_buf, data+copy_sz, len);
+ ix = len;
+ }
+ ctx->rd_in = ix;
+ wake_up(&g_cdev.rdq);
+out:
+ oz_cdev_release_ctx(ctx);
+}
diff --git a/drivers/staging/ozwpan/ozcdev.h b/drivers/staging/ozwpan/ozcdev.h
new file mode 100644
index 000000000..dd11935a0
--- /dev/null
+++ b/drivers/staging/ozwpan/ozcdev.h
@@ -0,0 +1,17 @@
+/* -----------------------------------------------------------------------------
+ * Copyright (c) 2011 Ozmo Inc
+ * Released under the GNU General Public License Version 2 (GPLv2).
+ * -----------------------------------------------------------------------------
+ */
+#ifndef _OZCDEV_H
+#define _OZCDEV_H
+
+int oz_cdev_register(void);
+int oz_cdev_deregister(void);
+int oz_cdev_init(void);
+void oz_cdev_term(void);
+int oz_cdev_start(struct oz_pd *pd, int resume);
+void oz_cdev_stop(struct oz_pd *pd, int pause);
+void oz_cdev_rx(struct oz_pd *pd, struct oz_elt *elt);
+
+#endif /* _OZCDEV_H */
diff --git a/drivers/staging/ozwpan/ozdbg.h b/drivers/staging/ozwpan/ozdbg.h
new file mode 100644
index 000000000..b86a2b7e0
--- /dev/null
+++ b/drivers/staging/ozwpan/ozdbg.h
@@ -0,0 +1,54 @@
+/* -----------------------------------------------------------------------------
+ * Copyright (c) 2011 Ozmo Inc
+ * Released under the GNU General Public License Version 2 (GPLv2).
+ * ---------------------------------------------------------------------------*/
+
+#ifndef _OZDBG_H
+#define _OZDBG_H
+
+#define OZ_WANT_DBG 0
+#define OZ_WANT_VERBOSE_DBG 1
+
+#define OZ_DBG_ON 0x0
+#define OZ_DBG_STREAM 0x1
+#define OZ_DBG_URB 0x2
+#define OZ_DBG_CTRL_DETAIL 0x4
+#define OZ_DBG_HUB 0x8
+#define OZ_DBG_RX_FRAMES 0x10
+#define OZ_DBG_TX_FRAMES 0x20
+
+#define OZ_DEFAULT_DBG_MASK \
+ ( \
+ /* OZ_DBG_STREAM | */ \
+ /* OZ_DBG_URB | */ \
+ /* OZ_DBG_CTRL_DETAIL | */ \
+ OZ_DBG_HUB | \
+ /* OZ_DBG_RX_FRAMES | */ \
+ /* OZ_DBG_TX_FRAMES | */ \
+ 0)
+
+extern unsigned int oz_dbg_mask;
+
+#define oz_want_dbg(mask) \
+ ((OZ_WANT_DBG && (OZ_DBG_##mask == OZ_DBG_ON)) || \
+ (OZ_WANT_VERBOSE_DBG && (OZ_DBG_##mask & oz_dbg_mask)))
+
+#define oz_dbg(mask, fmt, ...) \
+do { \
+ if (oz_want_dbg(mask)) \
+ pr_debug(fmt, ##__VA_ARGS__); \
+} while (0)
+
+#define oz_cdev_dbg(cdev, mask, fmt, ...) \
+do { \
+ if (oz_want_dbg(mask)) \
+ netdev_dbg((cdev)->dev, fmt, ##__VA_ARGS__); \
+} while (0)
+
+#define oz_pd_dbg(pd, mask, fmt, ...) \
+do { \
+ if (oz_want_dbg(mask)) \
+ pr_debug(fmt, ##__VA_ARGS__); \
+} while (0)
+
+#endif /* _OZDBG_H */
diff --git a/drivers/staging/ozwpan/ozeltbuf.c b/drivers/staging/ozwpan/ozeltbuf.c
new file mode 100644
index 000000000..01b25da44
--- /dev/null
+++ b/drivers/staging/ozwpan/ozeltbuf.c
@@ -0,0 +1,252 @@
+/* -----------------------------------------------------------------------------
+ * Copyright (c) 2011 Ozmo Inc
+ * Released under the GNU General Public License Version 2 (GPLv2).
+ * -----------------------------------------------------------------------------
+ */
+#include <linux/module.h>
+#include <linux/netdevice.h>
+#include "ozdbg.h"
+#include "ozprotocol.h"
+#include "ozeltbuf.h"
+#include "ozpd.h"
+
+/*
+ * Context: softirq-serialized
+ */
+void oz_elt_buf_init(struct oz_elt_buf *buf)
+{
+ memset(buf, 0, sizeof(struct oz_elt_buf));
+ INIT_LIST_HEAD(&buf->stream_list);
+ INIT_LIST_HEAD(&buf->order_list);
+ INIT_LIST_HEAD(&buf->isoc_list);
+ spin_lock_init(&buf->lock);
+}
+
+/*
+ * Context: softirq or process
+ */
+void oz_elt_buf_term(struct oz_elt_buf *buf)
+{
+ struct oz_elt_info *ei, *n;
+
+ list_for_each_entry_safe(ei, n, &buf->isoc_list, link_order)
+ kfree(ei);
+ list_for_each_entry_safe(ei, n, &buf->order_list, link_order)
+ kfree(ei);
+}
+
+/*
+ * Context: softirq or process
+ */
+struct oz_elt_info *oz_elt_info_alloc(struct oz_elt_buf *buf)
+{
+ struct oz_elt_info *ei;
+
+ ei = kmem_cache_zalloc(oz_elt_info_cache, GFP_ATOMIC);
+ if (ei) {
+ INIT_LIST_HEAD(&ei->link);
+ INIT_LIST_HEAD(&ei->link_order);
+ }
+ return ei;
+}
+
+/*
+ * Precondition: oz_elt_buf.lock must be held.
+ * Context: softirq or process
+ */
+void oz_elt_info_free(struct oz_elt_buf *buf, struct oz_elt_info *ei)
+{
+ if (ei)
+ kmem_cache_free(oz_elt_info_cache, ei);
+}
+
+/*------------------------------------------------------------------------------
+ * Context: softirq
+ */
+void oz_elt_info_free_chain(struct oz_elt_buf *buf, struct list_head *list)
+{
+ struct oz_elt_info *ei, *n;
+
+ spin_lock_bh(&buf->lock);
+ list_for_each_entry_safe(ei, n, list->next, link)
+ oz_elt_info_free(buf, ei);
+ spin_unlock_bh(&buf->lock);
+}
+
+int oz_elt_stream_create(struct oz_elt_buf *buf, u8 id, int max_buf_count)
+{
+ struct oz_elt_stream *st;
+
+ oz_dbg(ON, "%s: (0x%x)\n", __func__, id);
+
+ st = kzalloc(sizeof(struct oz_elt_stream), GFP_ATOMIC);
+ if (st == NULL)
+ return -ENOMEM;
+ atomic_set(&st->ref_count, 1);
+ st->id = id;
+ st->max_buf_count = max_buf_count;
+ INIT_LIST_HEAD(&st->elt_list);
+ spin_lock_bh(&buf->lock);
+ list_add_tail(&st->link, &buf->stream_list);
+ spin_unlock_bh(&buf->lock);
+ return 0;
+}
+
+int oz_elt_stream_delete(struct oz_elt_buf *buf, u8 id)
+{
+ struct list_head *e, *n;
+ struct oz_elt_stream *st = NULL;
+
+ oz_dbg(ON, "%s: (0x%x)\n", __func__, id);
+ spin_lock_bh(&buf->lock);
+ list_for_each(e, &buf->stream_list) {
+ st = list_entry(e, struct oz_elt_stream, link);
+ if (st->id == id) {
+ list_del(e);
+ break;
+ }
+ st = NULL;
+ }
+ if (!st) {
+ spin_unlock_bh(&buf->lock);
+ return -1;
+ }
+ list_for_each_safe(e, n, &st->elt_list) {
+ struct oz_elt_info *ei =
+ list_entry(e, struct oz_elt_info, link);
+ list_del_init(&ei->link);
+ list_del_init(&ei->link_order);
+ st->buf_count -= ei->length;
+ oz_dbg(STREAM, "Stream down: %d %d %d\n",
+ st->buf_count, ei->length, atomic_read(&st->ref_count));
+ oz_elt_stream_put(st);
+ oz_elt_info_free(buf, ei);
+ }
+ spin_unlock_bh(&buf->lock);
+ oz_elt_stream_put(st);
+ return 0;
+}
+
+void oz_elt_stream_get(struct oz_elt_stream *st)
+{
+ atomic_inc(&st->ref_count);
+}
+
+void oz_elt_stream_put(struct oz_elt_stream *st)
+{
+ if (atomic_dec_and_test(&st->ref_count)) {
+ oz_dbg(ON, "Stream destroyed\n");
+ kfree(st);
+ }
+}
+
+/*
+ * Precondition: Element buffer lock must be held.
+ * If this function fails the caller is responsible for deallocating the elt
+ * info structure.
+ */
+int oz_queue_elt_info(struct oz_elt_buf *buf, u8 isoc, u8 id,
+ struct oz_elt_info *ei)
+{
+ struct oz_elt_stream *st = NULL;
+ struct list_head *e;
+
+ if (id) {
+ list_for_each(e, &buf->stream_list) {
+ st = list_entry(e, struct oz_elt_stream, link);
+ if (st->id == id)
+ break;
+ }
+ if (e == &buf->stream_list) {
+ /* Stream specified but stream not known so fail.
+ * Caller deallocates element info. */
+ return -1;
+ }
+ }
+ if (st) {
+ /* If this is an ISOC fixed element that needs a frame number
+ * then insert that now. Earlier we stored the unit count in
+ * this field.
+ */
+ struct oz_isoc_fixed *body = (struct oz_isoc_fixed *)
+ &ei->data[sizeof(struct oz_elt)];
+ if ((body->app_id == OZ_APPID_USB) && (body->type
+ == OZ_USB_ENDPOINT_DATA) &&
+ (body->format == OZ_DATA_F_ISOC_FIXED)) {
+ u8 unit_count = body->frame_number;
+
+ body->frame_number = st->frame_number;
+ st->frame_number += unit_count;
+ }
+ /* Claim stream and update accounts */
+ oz_elt_stream_get(st);
+ ei->stream = st;
+ st->buf_count += ei->length;
+ /* Add to list in stream. */
+ list_add_tail(&ei->link, &st->elt_list);
+ oz_dbg(STREAM, "Stream up: %d %d\n", st->buf_count, ei->length);
+ /* Check if we have too much buffered for this stream. If so
+ * start dropping elements until we are back in bounds.
+ */
+ while ((st->buf_count > st->max_buf_count) &&
+ !list_empty(&st->elt_list)) {
+ struct oz_elt_info *ei2 =
+ list_first_entry(&st->elt_list,
+ struct oz_elt_info, link);
+ list_del_init(&ei2->link);
+ list_del_init(&ei2->link_order);
+ st->buf_count -= ei2->length;
+ oz_elt_info_free(buf, ei2);
+ oz_elt_stream_put(st);
+ }
+ }
+ list_add_tail(&ei->link_order, isoc ?
+ &buf->isoc_list : &buf->order_list);
+ return 0;
+}
+
+int oz_select_elts_for_tx(struct oz_elt_buf *buf, u8 isoc, unsigned *len,
+ unsigned max_len, struct list_head *list)
+{
+ int count = 0;
+ struct list_head *el;
+ struct oz_elt_info *ei, *n;
+
+ spin_lock_bh(&buf->lock);
+ if (isoc)
+ el = &buf->isoc_list;
+ else
+ el = &buf->order_list;
+
+ list_for_each_entry_safe(ei, n, el, link_order) {
+ if ((*len + ei->length) <= max_len) {
+ struct oz_app_hdr *app_hdr = (struct oz_app_hdr *)
+ &ei->data[sizeof(struct oz_elt)];
+ app_hdr->elt_seq_num = buf->tx_seq_num[ei->app_id]++;
+ if (buf->tx_seq_num[ei->app_id] == 0)
+ buf->tx_seq_num[ei->app_id] = 1;
+ *len += ei->length;
+ list_del(&ei->link);
+ list_del(&ei->link_order);
+ if (ei->stream) {
+ ei->stream->buf_count -= ei->length;
+ oz_dbg(STREAM, "Stream down: %d %d\n",
+ ei->stream->buf_count, ei->length);
+ oz_elt_stream_put(ei->stream);
+ ei->stream = NULL;
+ }
+ INIT_LIST_HEAD(&ei->link_order);
+ list_add_tail(&ei->link, list);
+ count++;
+ } else {
+ break;
+ }
+ }
+ spin_unlock_bh(&buf->lock);
+ return count;
+}
+
+int oz_are_elts_available(struct oz_elt_buf *buf)
+{
+ return !list_empty(&buf->order_list);
+}
diff --git a/drivers/staging/ozwpan/ozeltbuf.h b/drivers/staging/ozwpan/ozeltbuf.h
new file mode 100644
index 000000000..f09f5fe3f
--- /dev/null
+++ b/drivers/staging/ozwpan/ozeltbuf.h
@@ -0,0 +1,65 @@
+/* -----------------------------------------------------------------------------
+ * Copyright (c) 2011 Ozmo Inc
+ * Released under the GNU General Public License Version 2 (GPLv2).
+ * -----------------------------------------------------------------------------
+ */
+#ifndef _OZELTBUF_H
+#define _OZELTBUF_H
+
+#include "ozprotocol.h"
+
+/*-----------------------------------------------------------------------------
+ */
+struct oz_pd;
+typedef void (*oz_elt_callback_t)(struct oz_pd *pd, long context);
+
+struct oz_elt_stream {
+ struct list_head link;
+ struct list_head elt_list;
+ atomic_t ref_count;
+ unsigned buf_count;
+ unsigned max_buf_count;
+ u8 frame_number;
+ u8 id;
+};
+
+#define OZ_MAX_ELT_PAYLOAD 255
+struct oz_elt_info {
+ struct list_head link;
+ struct list_head link_order;
+ u8 flags;
+ u8 app_id;
+ oz_elt_callback_t callback;
+ long context;
+ struct oz_elt_stream *stream;
+ u8 data[sizeof(struct oz_elt) + OZ_MAX_ELT_PAYLOAD];
+ int length;
+};
+/* Flags values */
+#define OZ_EI_F_MARKED 0x1
+
+struct oz_elt_buf {
+ spinlock_t lock;
+ struct list_head stream_list;
+ struct list_head order_list;
+ struct list_head isoc_list;
+ u8 tx_seq_num[OZ_NB_APPS];
+};
+
+void oz_elt_buf_init(struct oz_elt_buf *buf);
+void oz_elt_buf_term(struct oz_elt_buf *buf);
+struct oz_elt_info *oz_elt_info_alloc(struct oz_elt_buf *buf);
+void oz_elt_info_free(struct oz_elt_buf *buf, struct oz_elt_info *ei);
+void oz_elt_info_free_chain(struct oz_elt_buf *buf, struct list_head *list);
+int oz_elt_stream_create(struct oz_elt_buf *buf, u8 id, int max_buf_count);
+int oz_elt_stream_delete(struct oz_elt_buf *buf, u8 id);
+void oz_elt_stream_get(struct oz_elt_stream *st);
+void oz_elt_stream_put(struct oz_elt_stream *st);
+int oz_queue_elt_info(struct oz_elt_buf *buf, u8 isoc, u8 id,
+ struct oz_elt_info *ei);
+int oz_select_elts_for_tx(struct oz_elt_buf *buf, u8 isoc, unsigned *len,
+ unsigned max_len, struct list_head *list);
+int oz_are_elts_available(struct oz_elt_buf *buf);
+
+#endif /* _OZELTBUF_H */
+
diff --git a/drivers/staging/ozwpan/ozhcd.c b/drivers/staging/ozwpan/ozhcd.c
new file mode 100644
index 000000000..784b5ecfa
--- /dev/null
+++ b/drivers/staging/ozwpan/ozhcd.c
@@ -0,0 +1,2301 @@
+/* -----------------------------------------------------------------------------
+ * Copyright (c) 2011 Ozmo Inc
+ * Released under the GNU General Public License Version 2 (GPLv2).
+ *
+ * This file provides the implementation of a USB host controller device that
+ * does not have any associated hardware. Instead the virtual device is
+ * connected to the WiFi network and emulates the operation of a USB hcd by
+ * receiving and sending network frames.
+ * Note:
+ * We take great pains to reduce the amount of code where interrupts need to be
+ * disabled and in this respect we are different from standard HCD's. In
+ * particular we don't want in_irq() code bleeding over to the protocol side of
+ * the driver.
+ * The troublesome functions are the urb enqueue and dequeue functions both of
+ * which can be called in_irq(). So for these functions we put the urbs into a
+ * queue and request a tasklet to process them. This means that a spinlock with
+ * interrupts disabled must be held for insertion and removal but most code is
+ * is in tasklet or soft irq context. The lock that protects this list is called
+ * the tasklet lock and serves the purpose of the 'HCD lock' which must be held
+ * when calling the following functions.
+ * usb_hcd_link_urb_to_ep()
+ * usb_hcd_unlink_urb_from_ep()
+ * usb_hcd_flush_endpoint()
+ * usb_hcd_check_unlink_urb()
+ * -----------------------------------------------------------------------------
+ */
+#include <linux/platform_device.h>
+#include <linux/usb.h>
+#include <linux/slab.h>
+#include <linux/export.h>
+#include "linux/usb/hcd.h"
+#include <asm/unaligned.h>
+#include "ozdbg.h"
+#include "ozusbif.h"
+#include "ozurbparanoia.h"
+#include "ozhcd.h"
+
+/*
+ * Number of units of buffering to capture for an isochronous IN endpoint before
+ * allowing data to be indicated up.
+ */
+#define OZ_IN_BUFFERING_UNITS 100
+
+/* Name of our platform device.
+ */
+#define OZ_PLAT_DEV_NAME "ozwpan"
+
+/*EP0 timeout before ep0 request is again added to TX queue. (13*8 = 98mSec)
+ */
+#define EP0_TIMEOUT_COUNTER 13
+
+/* Debounce time HCD driver should wait before unregistering.
+ */
+#define OZ_HUB_DEBOUNCE_TIMEOUT 1500
+
+/*
+ * Used to link urbs together and also store some status information for each
+ * urb.
+ * A cache of these are kept in a pool to reduce number of calls to kmalloc.
+ */
+struct oz_urb_link {
+ struct list_head link;
+ struct urb *urb;
+ struct oz_port *port;
+ u8 req_id;
+ u8 ep_num;
+ unsigned submit_counter;
+};
+
+static struct kmem_cache *oz_urb_link_cache;
+
+/* Holds state information about a USB endpoint.
+ */
+#define OZ_EP_BUFFER_SIZE_ISOC (1024 * 24)
+#define OZ_EP_BUFFER_SIZE_INT 512
+struct oz_endpoint {
+ struct list_head urb_list; /* List of oz_urb_link items. */
+ struct list_head link; /* For isoc ep, links in to isoc
+ lists of oz_port. */
+ struct timespec timestamp;
+ int credit;
+ int credit_ceiling;
+ u8 ep_num;
+ u8 attrib;
+ u8 *buffer;
+ int buffer_size;
+ int in_ix;
+ int out_ix;
+ int buffered_units;
+ unsigned flags;
+ int start_frame;
+};
+
+/* Bits in the flags field. */
+#define OZ_F_EP_BUFFERING 0x1
+#define OZ_F_EP_HAVE_STREAM 0x2
+
+/* Holds state information about a USB interface.
+ */
+struct oz_interface {
+ unsigned ep_mask;
+ u8 alt;
+};
+
+/* Holds state information about an hcd port.
+ */
+#define OZ_NB_ENDPOINTS 16
+struct oz_port {
+ unsigned flags;
+ unsigned status;
+ void *hpd;
+ struct oz_hcd *ozhcd;
+ spinlock_t port_lock;
+ u8 bus_addr;
+ u8 next_req_id;
+ u8 config_num;
+ int num_iface;
+ struct oz_interface *iface;
+ struct oz_endpoint *out_ep[OZ_NB_ENDPOINTS];
+ struct oz_endpoint *in_ep[OZ_NB_ENDPOINTS];
+ struct list_head isoc_out_ep;
+ struct list_head isoc_in_ep;
+};
+
+#define OZ_PORT_F_PRESENT 0x1
+#define OZ_PORT_F_CHANGED 0x2
+#define OZ_PORT_F_DYING 0x4
+
+/* Data structure in the private context area of struct usb_hcd.
+ */
+#define OZ_NB_PORTS 8
+struct oz_hcd {
+ spinlock_t hcd_lock;
+ struct list_head urb_pending_list;
+ struct list_head urb_cancel_list;
+ struct list_head orphanage;
+ int conn_port; /* Port that is currently connecting, -1 if none.*/
+ struct oz_port ports[OZ_NB_PORTS];
+ uint flags;
+ struct usb_hcd *hcd;
+};
+
+/* Bits in flags field.
+ */
+#define OZ_HDC_F_SUSPENDED 0x1
+
+/*
+ * Static function prototypes.
+ */
+static int oz_hcd_start(struct usb_hcd *hcd);
+static void oz_hcd_stop(struct usb_hcd *hcd);
+static void oz_hcd_shutdown(struct usb_hcd *hcd);
+static int oz_hcd_urb_enqueue(struct usb_hcd *hcd, struct urb *urb,
+ gfp_t mem_flags);
+static int oz_hcd_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status);
+static void oz_hcd_endpoint_disable(struct usb_hcd *hcd,
+ struct usb_host_endpoint *ep);
+static void oz_hcd_endpoint_reset(struct usb_hcd *hcd,
+ struct usb_host_endpoint *ep);
+static int oz_hcd_get_frame_number(struct usb_hcd *hcd);
+static int oz_hcd_hub_status_data(struct usb_hcd *hcd, char *buf);
+static int oz_hcd_hub_control(struct usb_hcd *hcd, u16 req_type, u16 wvalue,
+ u16 windex, char *buf, u16 wlength);
+static int oz_hcd_bus_suspend(struct usb_hcd *hcd);
+static int oz_hcd_bus_resume(struct usb_hcd *hcd);
+static int oz_plat_probe(struct platform_device *dev);
+static int oz_plat_remove(struct platform_device *dev);
+static void oz_plat_shutdown(struct platform_device *dev);
+static int oz_plat_suspend(struct platform_device *dev, pm_message_t msg);
+static int oz_plat_resume(struct platform_device *dev);
+static void oz_urb_process_tasklet(unsigned long unused);
+static int oz_build_endpoints_for_config(struct usb_hcd *hcd,
+ struct oz_port *port, struct usb_host_config *config,
+ gfp_t mem_flags);
+static void oz_clean_endpoints_for_config(struct usb_hcd *hcd,
+ struct oz_port *port);
+static int oz_build_endpoints_for_interface(struct usb_hcd *hcd,
+ struct oz_port *port,
+ struct usb_host_interface *intf, gfp_t mem_flags);
+static void oz_clean_endpoints_for_interface(struct usb_hcd *hcd,
+ struct oz_port *port, int if_ix);
+static void oz_process_ep0_urb(struct oz_hcd *ozhcd, struct urb *urb,
+ gfp_t mem_flags);
+static struct oz_urb_link *oz_remove_urb(struct oz_endpoint *ep,
+ struct urb *urb);
+static void oz_hcd_clear_orphanage(struct oz_hcd *ozhcd, int status);
+
+/*
+ * Static external variables.
+ */
+static struct platform_device *g_plat_dev;
+static struct oz_hcd *g_ozhcd;
+static DEFINE_SPINLOCK(g_hcdlock); /* Guards g_ozhcd. */
+static const char g_hcd_name[] = "Ozmo WPAN";
+static DEFINE_SPINLOCK(g_tasklet_lock);
+static struct tasklet_struct g_urb_process_tasklet;
+static struct tasklet_struct g_urb_cancel_tasklet;
+static atomic_t g_pending_urbs = ATOMIC_INIT(0);
+static atomic_t g_usb_frame_number = ATOMIC_INIT(0);
+static const struct hc_driver g_oz_hc_drv = {
+ .description = g_hcd_name,
+ .product_desc = "Ozmo Devices WPAN",
+ .hcd_priv_size = sizeof(struct oz_hcd),
+ .flags = HCD_USB11,
+ .start = oz_hcd_start,
+ .stop = oz_hcd_stop,
+ .shutdown = oz_hcd_shutdown,
+ .urb_enqueue = oz_hcd_urb_enqueue,
+ .urb_dequeue = oz_hcd_urb_dequeue,
+ .endpoint_disable = oz_hcd_endpoint_disable,
+ .endpoint_reset = oz_hcd_endpoint_reset,
+ .get_frame_number = oz_hcd_get_frame_number,
+ .hub_status_data = oz_hcd_hub_status_data,
+ .hub_control = oz_hcd_hub_control,
+ .bus_suspend = oz_hcd_bus_suspend,
+ .bus_resume = oz_hcd_bus_resume,
+};
+
+static struct platform_driver g_oz_plat_drv = {
+ .probe = oz_plat_probe,
+ .remove = oz_plat_remove,
+ .shutdown = oz_plat_shutdown,
+ .suspend = oz_plat_suspend,
+ .resume = oz_plat_resume,
+ .driver = {
+ .name = OZ_PLAT_DEV_NAME,
+ },
+};
+
+/*
+ * Gets our private context area (which is of type struct oz_hcd) from the
+ * usb_hcd structure.
+ * Context: any
+ */
+static inline struct oz_hcd *oz_hcd_private(struct usb_hcd *hcd)
+{
+ return (struct oz_hcd *)hcd->hcd_priv;
+}
+
+/*
+ * Searches list of ports to find the index of the one with a specified USB
+ * bus address. If none of the ports has the bus address then the connection
+ * port is returned, if there is one or -1 otherwise.
+ * Context: any
+ */
+static int oz_get_port_from_addr(struct oz_hcd *ozhcd, u8 bus_addr)
+{
+ int i;
+
+ for (i = 0; i < OZ_NB_PORTS; i++) {
+ if (ozhcd->ports[i].bus_addr == bus_addr)
+ return i;
+ }
+ return ozhcd->conn_port;
+}
+
+/*
+ * Context: any
+ */
+static struct oz_urb_link *oz_alloc_urb_link(void)
+{
+ return kmem_cache_alloc(oz_urb_link_cache, GFP_ATOMIC);
+}
+
+/*
+ * Context: any
+ */
+static void oz_free_urb_link(struct oz_urb_link *urbl)
+{
+ if (!urbl)
+ return;
+
+ kmem_cache_free(oz_urb_link_cache, urbl);
+}
+
+/*
+ * Allocates endpoint structure and optionally a buffer. If a buffer is
+ * allocated it immediately follows the endpoint structure.
+ * Context: softirq
+ */
+static struct oz_endpoint *oz_ep_alloc(int buffer_size, gfp_t mem_flags)
+{
+ struct oz_endpoint *ep;
+
+ ep = kzalloc(sizeof(struct oz_endpoint)+buffer_size, mem_flags);
+ if (!ep)
+ return NULL;
+
+ INIT_LIST_HEAD(&ep->urb_list);
+ INIT_LIST_HEAD(&ep->link);
+ ep->credit = -1;
+ if (buffer_size) {
+ ep->buffer_size = buffer_size;
+ ep->buffer = (u8 *)(ep+1);
+ }
+
+ return ep;
+}
+
+/*
+ * Pre-condition: Must be called with g_tasklet_lock held and interrupts
+ * disabled.
+ * Context: softirq or process
+ */
+static struct oz_urb_link *oz_uncancel_urb(struct oz_hcd *ozhcd,
+ struct urb *urb)
+{
+ struct oz_urb_link *urbl;
+
+ list_for_each_entry(urbl, &ozhcd->urb_cancel_list, link) {
+ if (urb == urbl->urb) {
+ list_del_init(&urbl->link);
+ return urbl;
+ }
+ }
+ return NULL;
+}
+
+/*
+ * This is called when we have finished processing an urb. It unlinks it from
+ * the ep and returns it to the core.
+ * Context: softirq or process
+ */
+static void oz_complete_urb(struct usb_hcd *hcd, struct urb *urb,
+ int status)
+{
+ struct oz_hcd *ozhcd = oz_hcd_private(hcd);
+ unsigned long irq_state;
+ struct oz_urb_link *cancel_urbl;
+
+ spin_lock_irqsave(&g_tasklet_lock, irq_state);
+ usb_hcd_unlink_urb_from_ep(hcd, urb);
+ /* Clear hcpriv which will prevent it being put in the cancel list
+ * in the event that an attempt is made to cancel it.
+ */
+ urb->hcpriv = NULL;
+ /* Walk the cancel list in case the urb is already sitting there.
+ * Since we process the cancel list in a tasklet rather than in
+ * the dequeue function this could happen.
+ */
+ cancel_urbl = oz_uncancel_urb(ozhcd, urb);
+ /* Note: we release lock but do not enable local irqs.
+ * It appears that usb_hcd_giveback_urb() expects irqs to be disabled,
+ * or at least other host controllers disable interrupts at this point
+ * so we do the same. We must, however, release the lock otherwise a
+ * deadlock will occur if an urb is submitted to our driver in the urb
+ * completion function. Because we disable interrupts it is possible
+ * that the urb_enqueue function can be called with them disabled.
+ */
+ spin_unlock(&g_tasklet_lock);
+ if (oz_forget_urb(urb)) {
+ oz_dbg(ON, "ERROR Unknown URB %p\n", urb);
+ } else {
+ atomic_dec(&g_pending_urbs);
+ usb_hcd_giveback_urb(hcd, urb, status);
+ }
+ spin_lock(&g_tasklet_lock);
+ spin_unlock_irqrestore(&g_tasklet_lock, irq_state);
+ oz_free_urb_link(cancel_urbl);
+}
+
+/*
+ * Deallocates an endpoint including deallocating any associated stream and
+ * returning any queued urbs to the core.
+ * Context: softirq
+ */
+static void oz_ep_free(struct oz_port *port, struct oz_endpoint *ep)
+{
+ if (port) {
+ LIST_HEAD(list);
+ struct oz_hcd *ozhcd = port->ozhcd;
+
+ if (ep->flags & OZ_F_EP_HAVE_STREAM)
+ oz_usb_stream_delete(port->hpd, ep->ep_num);
+ /* Transfer URBs to the orphanage while we hold the lock. */
+ spin_lock_bh(&ozhcd->hcd_lock);
+ /* Note: this works even if ep->urb_list is empty.*/
+ list_replace_init(&ep->urb_list, &list);
+ /* Put the URBs in the orphanage. */
+ list_splice_tail(&list, &ozhcd->orphanage);
+ spin_unlock_bh(&ozhcd->hcd_lock);
+ }
+ oz_dbg(ON, "Freeing endpoint memory\n");
+ kfree(ep);
+}
+
+/*
+ * Context: softirq
+ */
+static void oz_complete_buffered_urb(struct oz_port *port,
+ struct oz_endpoint *ep,
+ struct urb *urb)
+{
+ int data_len, available_space, copy_len;
+
+ data_len = ep->buffer[ep->out_ix];
+ if (data_len <= urb->transfer_buffer_length)
+ available_space = data_len;
+ else
+ available_space = urb->transfer_buffer_length;
+
+ if (++ep->out_ix == ep->buffer_size)
+ ep->out_ix = 0;
+ copy_len = ep->buffer_size - ep->out_ix;
+ if (copy_len >= available_space)
+ copy_len = available_space;
+ memcpy(urb->transfer_buffer, &ep->buffer[ep->out_ix], copy_len);
+
+ if (copy_len < available_space) {
+ memcpy((urb->transfer_buffer + copy_len), ep->buffer,
+ (available_space - copy_len));
+ ep->out_ix = available_space - copy_len;
+ } else {
+ ep->out_ix += copy_len;
+ }
+ urb->actual_length = available_space;
+ if (ep->out_ix == ep->buffer_size)
+ ep->out_ix = 0;
+
+ ep->buffered_units--;
+ oz_dbg(ON, "Trying to give back buffered frame of size=%d\n",
+ available_space);
+ oz_complete_urb(port->ozhcd->hcd, urb, 0);
+}
+
+/*
+ * Context: softirq
+ */
+static int oz_enqueue_ep_urb(struct oz_port *port, u8 ep_addr, int in_dir,
+ struct urb *urb, u8 req_id)
+{
+ struct oz_urb_link *urbl;
+ struct oz_endpoint *ep = NULL;
+ int err = 0;
+
+ if (ep_addr >= OZ_NB_ENDPOINTS) {
+ oz_dbg(ON, "%s: Invalid endpoint number\n", __func__);
+ return -EINVAL;
+ }
+ urbl = oz_alloc_urb_link();
+ if (!urbl)
+ return -ENOMEM;
+ urbl->submit_counter = 0;
+ urbl->urb = urb;
+ urbl->req_id = req_id;
+ urbl->ep_num = ep_addr;
+ /* Hold lock while we insert the URB into the list within the
+ * endpoint structure.
+ */
+ spin_lock_bh(&port->ozhcd->hcd_lock);
+ /* If the urb has been unlinked while out of any list then
+ * complete it now.
+ */
+ if (urb->unlinked) {
+ spin_unlock_bh(&port->ozhcd->hcd_lock);
+ oz_dbg(ON, "urb %p unlinked so complete immediately\n", urb);
+ oz_complete_urb(port->ozhcd->hcd, urb, 0);
+ oz_free_urb_link(urbl);
+ return 0;
+ }
+
+ if (in_dir)
+ ep = port->in_ep[ep_addr];
+ else
+ ep = port->out_ep[ep_addr];
+ if (!ep) {
+ err = -ENOMEM;
+ goto out;
+ }
+
+ /*For interrupt endpoint check for buffered data
+ * & complete urb
+ */
+ if (((ep->attrib & USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_INT)
+ && ep->buffered_units > 0) {
+ oz_free_urb_link(urbl);
+ spin_unlock_bh(&port->ozhcd->hcd_lock);
+ oz_complete_buffered_urb(port, ep, urb);
+ return 0;
+ }
+
+ if (port->hpd) {
+ list_add_tail(&urbl->link, &ep->urb_list);
+ if (!in_dir && ep_addr && (ep->credit < 0)) {
+ getrawmonotonic(&ep->timestamp);
+ ep->credit = 0;
+ }
+ } else {
+ err = -EPIPE;
+ }
+out:
+ spin_unlock_bh(&port->ozhcd->hcd_lock);
+ if (err)
+ oz_free_urb_link(urbl);
+ return err;
+}
+
+/*
+ * Removes an urb from the queue in the endpoint.
+ * Returns 0 if it is found and -EIDRM otherwise.
+ * Context: softirq
+ */
+static int oz_dequeue_ep_urb(struct oz_port *port, u8 ep_addr, int in_dir,
+ struct urb *urb)
+{
+ struct oz_urb_link *urbl = NULL;
+ struct oz_endpoint *ep;
+
+ spin_lock_bh(&port->ozhcd->hcd_lock);
+ if (in_dir)
+ ep = port->in_ep[ep_addr];
+ else
+ ep = port->out_ep[ep_addr];
+ if (ep) {
+ struct list_head *e;
+
+ list_for_each(e, &ep->urb_list) {
+ urbl = list_entry(e, struct oz_urb_link, link);
+ if (urbl->urb == urb) {
+ list_del_init(e);
+ break;
+ }
+ urbl = NULL;
+ }
+ }
+ spin_unlock_bh(&port->ozhcd->hcd_lock);
+ oz_free_urb_link(urbl);
+ return urbl ? 0 : -EIDRM;
+}
+
+/*
+ * Finds an urb given its request id.
+ * Context: softirq
+ */
+static struct urb *oz_find_urb_by_id(struct oz_port *port, int ep_ix,
+ u8 req_id)
+{
+ struct oz_hcd *ozhcd = port->ozhcd;
+ struct urb *urb = NULL;
+ struct oz_urb_link *urbl;
+ struct oz_endpoint *ep;
+
+ spin_lock_bh(&ozhcd->hcd_lock);
+ ep = port->out_ep[ep_ix];
+ if (ep) {
+ struct list_head *e;
+
+ list_for_each(e, &ep->urb_list) {
+ urbl = list_entry(e, struct oz_urb_link, link);
+ if (urbl->req_id == req_id) {
+ urb = urbl->urb;
+ list_del_init(e);
+ break;
+ }
+ }
+ }
+ spin_unlock_bh(&ozhcd->hcd_lock);
+ /* If urb is non-zero then we we must have an urb link to delete.
+ */
+ if (urb)
+ oz_free_urb_link(urbl);
+ return urb;
+}
+
+/*
+ * Pre-condition: Port lock must be held.
+ * Context: softirq
+ */
+static void oz_acquire_port(struct oz_port *port, void *hpd)
+{
+ INIT_LIST_HEAD(&port->isoc_out_ep);
+ INIT_LIST_HEAD(&port->isoc_in_ep);
+ port->flags |= OZ_PORT_F_PRESENT | OZ_PORT_F_CHANGED;
+ port->status |= USB_PORT_STAT_CONNECTION |
+ (USB_PORT_STAT_C_CONNECTION << 16);
+ oz_usb_get(hpd);
+ port->hpd = hpd;
+}
+
+/*
+ * Context: softirq
+ */
+static struct oz_hcd *oz_hcd_claim(void)
+{
+ struct oz_hcd *ozhcd;
+
+ spin_lock_bh(&g_hcdlock);
+ ozhcd = g_ozhcd;
+ if (ozhcd)
+ usb_get_hcd(ozhcd->hcd);
+ spin_unlock_bh(&g_hcdlock);
+ return ozhcd;
+}
+
+/*
+ * Context: softirq
+ */
+static inline void oz_hcd_put(struct oz_hcd *ozhcd)
+{
+ if (ozhcd)
+ usb_put_hcd(ozhcd->hcd);
+}
+
+/*
+ * This is called by the protocol handler to notify that a PD has arrived.
+ * We allocate a port to associate with the PD and create a structure for
+ * endpoint 0. This port is made the connection port.
+ * In the event that one of the other port is already a connection port then
+ * we fail.
+ * TODO We should be able to do better than fail and should be able remember
+ * that this port needs configuring and make it the connection port once the
+ * current connection port has been assigned an address. Collisions here are
+ * probably very rare indeed.
+ * Context: softirq
+ */
+struct oz_port *oz_hcd_pd_arrived(void *hpd)
+{
+ int i;
+ struct oz_port *hport;
+ struct oz_hcd *ozhcd;
+ struct oz_endpoint *ep;
+
+ ozhcd = oz_hcd_claim();
+ if (!ozhcd)
+ return NULL;
+ /* Allocate an endpoint object in advance (before holding hcd lock) to
+ * use for out endpoint 0.
+ */
+ ep = oz_ep_alloc(0, GFP_ATOMIC);
+ if (!ep)
+ goto err_put;
+
+ spin_lock_bh(&ozhcd->hcd_lock);
+ if (ozhcd->conn_port >= 0)
+ goto err_unlock;
+
+ for (i = 0; i < OZ_NB_PORTS; i++) {
+ struct oz_port *port = &ozhcd->ports[i];
+
+ spin_lock(&port->port_lock);
+ if (!(port->flags & (OZ_PORT_F_PRESENT | OZ_PORT_F_CHANGED))) {
+ oz_acquire_port(port, hpd);
+ spin_unlock(&port->port_lock);
+ break;
+ }
+ spin_unlock(&port->port_lock);
+ }
+ if (i == OZ_NB_PORTS)
+ goto err_unlock;
+
+ ozhcd->conn_port = i;
+ hport = &ozhcd->ports[i];
+ hport->out_ep[0] = ep;
+ spin_unlock_bh(&ozhcd->hcd_lock);
+ if (ozhcd->flags & OZ_HDC_F_SUSPENDED)
+ usb_hcd_resume_root_hub(ozhcd->hcd);
+ usb_hcd_poll_rh_status(ozhcd->hcd);
+ oz_hcd_put(ozhcd);
+
+ return hport;
+
+err_unlock:
+ spin_unlock_bh(&ozhcd->hcd_lock);
+ oz_ep_free(NULL, ep);
+err_put:
+ oz_hcd_put(ozhcd);
+ return NULL;
+}
+
+/*
+ * This is called by the protocol handler to notify that the PD has gone away.
+ * We need to deallocate all resources and then request that the root hub is
+ * polled. We release the reference we hold on the PD.
+ * Context: softirq
+ */
+void oz_hcd_pd_departed(struct oz_port *port)
+{
+ struct oz_hcd *ozhcd;
+ void *hpd;
+ struct oz_endpoint *ep = NULL;
+
+ if (port == NULL) {
+ oz_dbg(ON, "%s: port = 0\n", __func__);
+ return;
+ }
+ ozhcd = port->ozhcd;
+ if (ozhcd == NULL)
+ return;
+ /* Check if this is the connection port - if so clear it.
+ */
+ spin_lock_bh(&ozhcd->hcd_lock);
+ if ((ozhcd->conn_port >= 0) &&
+ (port == &ozhcd->ports[ozhcd->conn_port])) {
+ oz_dbg(ON, "Clearing conn_port\n");
+ ozhcd->conn_port = -1;
+ }
+ spin_lock(&port->port_lock);
+ port->flags |= OZ_PORT_F_DYING;
+ spin_unlock(&port->port_lock);
+ spin_unlock_bh(&ozhcd->hcd_lock);
+
+ oz_clean_endpoints_for_config(ozhcd->hcd, port);
+ spin_lock_bh(&port->port_lock);
+ hpd = port->hpd;
+ port->hpd = NULL;
+ port->bus_addr = 0xff;
+ port->config_num = 0;
+ port->flags &= ~(OZ_PORT_F_PRESENT | OZ_PORT_F_DYING);
+ port->flags |= OZ_PORT_F_CHANGED;
+ port->status &= ~(USB_PORT_STAT_CONNECTION | USB_PORT_STAT_ENABLE);
+ port->status |= (USB_PORT_STAT_C_CONNECTION << 16);
+ /* If there is an endpont 0 then clear the pointer while we hold
+ * the spinlock be we deallocate it after releasing the lock.
+ */
+ if (port->out_ep[0]) {
+ ep = port->out_ep[0];
+ port->out_ep[0] = NULL;
+ }
+ spin_unlock_bh(&port->port_lock);
+ if (ep)
+ oz_ep_free(port, ep);
+ usb_hcd_poll_rh_status(ozhcd->hcd);
+ oz_usb_put(hpd);
+}
+
+/*
+ * Context: softirq
+ */
+void oz_hcd_pd_reset(void *hpd, void *hport)
+{
+ /* Cleanup the current configuration and report reset to the core.
+ */
+ struct oz_port *port = hport;
+ struct oz_hcd *ozhcd = port->ozhcd;
+
+ oz_dbg(ON, "PD Reset\n");
+ spin_lock_bh(&port->port_lock);
+ port->flags |= OZ_PORT_F_CHANGED;
+ port->status |= USB_PORT_STAT_RESET;
+ port->status |= (USB_PORT_STAT_C_RESET << 16);
+ spin_unlock_bh(&port->port_lock);
+ oz_clean_endpoints_for_config(ozhcd->hcd, port);
+ usb_hcd_poll_rh_status(ozhcd->hcd);
+}
+
+/*
+ * Context: softirq
+ */
+void oz_hcd_get_desc_cnf(void *hport, u8 req_id, u8 status, const u8 *desc,
+ u8 length, u16 offset, u16 total_size)
+{
+ struct oz_port *port = hport;
+ struct urb *urb;
+ int err = 0;
+
+ oz_dbg(ON, "oz_hcd_get_desc_cnf length = %d offs = %d tot_size = %d\n",
+ length, offset, total_size);
+ urb = oz_find_urb_by_id(port, 0, req_id);
+ if (!urb)
+ return;
+ if (status == 0) {
+ unsigned int copy_len;
+ unsigned int required_size = urb->transfer_buffer_length;
+
+ if (required_size > total_size)
+ required_size = total_size;
+ copy_len = required_size-offset;
+ if (length <= copy_len)
+ copy_len = length;
+ memcpy(urb->transfer_buffer+offset, desc, copy_len);
+ offset += copy_len;
+ if (offset < required_size) {
+ struct usb_ctrlrequest *setup =
+ (struct usb_ctrlrequest *)urb->setup_packet;
+ unsigned wvalue = le16_to_cpu(setup->wValue);
+
+ if (oz_enqueue_ep_urb(port, 0, 0, urb, req_id))
+ err = -ENOMEM;
+ else if (oz_usb_get_desc_req(port->hpd, req_id,
+ setup->bRequestType, (u8)(wvalue>>8),
+ (u8)wvalue, setup->wIndex, offset,
+ required_size-offset)) {
+ oz_dequeue_ep_urb(port, 0, 0, urb);
+ err = -ENOMEM;
+ }
+ if (err == 0)
+ return;
+ }
+ }
+ urb->actual_length = total_size;
+ oz_complete_urb(port->ozhcd->hcd, urb, 0);
+}
+
+/*
+ * Context: softirq
+ */
+static void oz_display_conf_type(u8 t)
+{
+ switch (t) {
+ case USB_REQ_GET_STATUS:
+ oz_dbg(ON, "USB_REQ_GET_STATUS - cnf\n");
+ break;
+ case USB_REQ_CLEAR_FEATURE:
+ oz_dbg(ON, "USB_REQ_CLEAR_FEATURE - cnf\n");
+ break;
+ case USB_REQ_SET_FEATURE:
+ oz_dbg(ON, "USB_REQ_SET_FEATURE - cnf\n");
+ break;
+ case USB_REQ_SET_ADDRESS:
+ oz_dbg(ON, "USB_REQ_SET_ADDRESS - cnf\n");
+ break;
+ case USB_REQ_GET_DESCRIPTOR:
+ oz_dbg(ON, "USB_REQ_GET_DESCRIPTOR - cnf\n");
+ break;
+ case USB_REQ_SET_DESCRIPTOR:
+ oz_dbg(ON, "USB_REQ_SET_DESCRIPTOR - cnf\n");
+ break;
+ case USB_REQ_GET_CONFIGURATION:
+ oz_dbg(ON, "USB_REQ_GET_CONFIGURATION - cnf\n");
+ break;
+ case USB_REQ_SET_CONFIGURATION:
+ oz_dbg(ON, "USB_REQ_SET_CONFIGURATION - cnf\n");
+ break;
+ case USB_REQ_GET_INTERFACE:
+ oz_dbg(ON, "USB_REQ_GET_INTERFACE - cnf\n");
+ break;
+ case USB_REQ_SET_INTERFACE:
+ oz_dbg(ON, "USB_REQ_SET_INTERFACE - cnf\n");
+ break;
+ case USB_REQ_SYNCH_FRAME:
+ oz_dbg(ON, "USB_REQ_SYNCH_FRAME - cnf\n");
+ break;
+ }
+}
+
+/*
+ * Context: softirq
+ */
+static void oz_hcd_complete_set_config(struct oz_port *port, struct urb *urb,
+ u8 rcode, u8 config_num)
+{
+ int rc = 0;
+ struct usb_hcd *hcd = port->ozhcd->hcd;
+
+ if (rcode == 0) {
+ port->config_num = config_num;
+ oz_clean_endpoints_for_config(hcd, port);
+ if (oz_build_endpoints_for_config(hcd, port,
+ &urb->dev->config[port->config_num-1], GFP_ATOMIC)) {
+ rc = -ENOMEM;
+ }
+ } else {
+ rc = -ENOMEM;
+ }
+ oz_complete_urb(hcd, urb, rc);
+}
+
+/*
+ * Context: softirq
+ */
+static void oz_hcd_complete_set_interface(struct oz_port *port, struct urb *urb,
+ u8 rcode, u8 if_num, u8 alt)
+{
+ struct usb_hcd *hcd = port->ozhcd->hcd;
+ int rc = 0;
+
+ if ((rcode == 0) && (port->config_num > 0)) {
+ struct usb_host_config *config;
+ struct usb_host_interface *intf;
+
+ oz_dbg(ON, "Set interface %d alt %d\n", if_num, alt);
+ oz_clean_endpoints_for_interface(hcd, port, if_num);
+ config = &urb->dev->config[port->config_num-1];
+ intf = &config->intf_cache[if_num]->altsetting[alt];
+ if (oz_build_endpoints_for_interface(hcd, port, intf,
+ GFP_ATOMIC))
+ rc = -ENOMEM;
+ else
+ port->iface[if_num].alt = alt;
+ } else {
+ rc = -ENOMEM;
+ }
+ oz_complete_urb(hcd, urb, rc);
+}
+
+/*
+ * Context: softirq
+ */
+void oz_hcd_control_cnf(void *hport, u8 req_id, u8 rcode, const u8 *data,
+ int data_len)
+{
+ struct oz_port *port = hport;
+ struct urb *urb;
+ struct usb_ctrlrequest *setup;
+ struct usb_hcd *hcd = port->ozhcd->hcd;
+ unsigned windex;
+ unsigned wvalue;
+
+ oz_dbg(ON, "oz_hcd_control_cnf rcode=%u len=%d\n", rcode, data_len);
+ urb = oz_find_urb_by_id(port, 0, req_id);
+ if (!urb) {
+ oz_dbg(ON, "URB not found\n");
+ return;
+ }
+ setup = (struct usb_ctrlrequest *)urb->setup_packet;
+ windex = le16_to_cpu(setup->wIndex);
+ wvalue = le16_to_cpu(setup->wValue);
+ if ((setup->bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD) {
+ /* Standard requests */
+ oz_display_conf_type(setup->bRequest);
+ switch (setup->bRequest) {
+ case USB_REQ_SET_CONFIGURATION:
+ oz_hcd_complete_set_config(port, urb, rcode,
+ (u8)wvalue);
+ break;
+ case USB_REQ_SET_INTERFACE:
+ oz_hcd_complete_set_interface(port, urb, rcode,
+ (u8)windex, (u8)wvalue);
+ break;
+ default:
+ oz_complete_urb(hcd, urb, 0);
+ }
+
+ } else {
+ int copy_len;
+
+ oz_dbg(ON, "VENDOR-CLASS - cnf\n");
+ if (data_len) {
+ if (data_len <= urb->transfer_buffer_length)
+ copy_len = data_len;
+ else
+ copy_len = urb->transfer_buffer_length;
+ memcpy(urb->transfer_buffer, data, copy_len);
+ urb->actual_length = copy_len;
+ }
+ oz_complete_urb(hcd, urb, 0);
+ }
+}
+
+/*
+ * Context: softirq-serialized
+ */
+static int oz_hcd_buffer_data(struct oz_endpoint *ep, const u8 *data,
+ int data_len)
+{
+ int space;
+ int copy_len;
+
+ if (!ep->buffer)
+ return -1;
+ space = ep->out_ix-ep->in_ix-1;
+ if (space < 0)
+ space += ep->buffer_size;
+ if (space < (data_len+1)) {
+ oz_dbg(ON, "Buffer full\n");
+ return -1;
+ }
+ ep->buffer[ep->in_ix] = (u8)data_len;
+ if (++ep->in_ix == ep->buffer_size)
+ ep->in_ix = 0;
+ copy_len = ep->buffer_size - ep->in_ix;
+ if (copy_len > data_len)
+ copy_len = data_len;
+ memcpy(&ep->buffer[ep->in_ix], data, copy_len);
+
+ if (copy_len < data_len) {
+ memcpy(ep->buffer, data+copy_len, data_len-copy_len);
+ ep->in_ix = data_len-copy_len;
+ } else {
+ ep->in_ix += copy_len;
+ }
+ if (ep->in_ix == ep->buffer_size)
+ ep->in_ix = 0;
+ ep->buffered_units++;
+ return 0;
+}
+
+/*
+ * Context: softirq-serialized
+ */
+void oz_hcd_data_ind(void *hport, u8 endpoint, const u8 *data, int data_len)
+{
+ struct oz_port *port = (struct oz_port *)hport;
+ struct oz_endpoint *ep;
+ struct oz_hcd *ozhcd = port->ozhcd;
+
+ spin_lock_bh(&ozhcd->hcd_lock);
+ ep = port->in_ep[endpoint & USB_ENDPOINT_NUMBER_MASK];
+ if (ep == NULL)
+ goto done;
+ switch (ep->attrib & USB_ENDPOINT_XFERTYPE_MASK) {
+ case USB_ENDPOINT_XFER_INT:
+ case USB_ENDPOINT_XFER_BULK:
+ if (!list_empty(&ep->urb_list)) {
+ struct oz_urb_link *urbl =
+ list_first_entry(&ep->urb_list,
+ struct oz_urb_link, link);
+ struct urb *urb;
+ int copy_len;
+
+ list_del_init(&urbl->link);
+ spin_unlock_bh(&ozhcd->hcd_lock);
+ urb = urbl->urb;
+ oz_free_urb_link(urbl);
+ if (data_len <= urb->transfer_buffer_length)
+ copy_len = data_len;
+ else
+ copy_len = urb->transfer_buffer_length;
+ memcpy(urb->transfer_buffer, data, copy_len);
+ urb->actual_length = copy_len;
+ oz_complete_urb(port->ozhcd->hcd, urb, 0);
+ return;
+ }
+ oz_dbg(ON, "buffering frame as URB is not available\n");
+ oz_hcd_buffer_data(ep, data, data_len);
+ break;
+ case USB_ENDPOINT_XFER_ISOC:
+ oz_hcd_buffer_data(ep, data, data_len);
+ break;
+ }
+done:
+ spin_unlock_bh(&ozhcd->hcd_lock);
+}
+
+/*
+ * Context: unknown
+ */
+static inline int oz_usb_get_frame_number(void)
+{
+ return atomic_inc_return(&g_usb_frame_number);
+}
+
+/*
+ * Context: softirq
+ */
+int oz_hcd_heartbeat(void *hport)
+{
+ int rc = 0;
+ struct oz_port *port = hport;
+ struct oz_hcd *ozhcd = port->ozhcd;
+ struct oz_urb_link *urbl, *n;
+ LIST_HEAD(xfr_list);
+ struct urb *urb;
+ struct oz_endpoint *ep;
+ struct timespec ts, delta;
+
+ getrawmonotonic(&ts);
+ /* Check the OUT isoc endpoints to see if any URB data can be sent.
+ */
+ spin_lock_bh(&ozhcd->hcd_lock);
+ list_for_each_entry(ep, &port->isoc_out_ep, link) {
+ if (ep->credit < 0)
+ continue;
+ delta = timespec_sub(ts, ep->timestamp);
+ ep->credit += div_u64(timespec_to_ns(&delta), NSEC_PER_MSEC);
+ if (ep->credit > ep->credit_ceiling)
+ ep->credit = ep->credit_ceiling;
+ ep->timestamp = ts;
+ while (ep->credit && !list_empty(&ep->urb_list)) {
+ urbl = list_first_entry(&ep->urb_list,
+ struct oz_urb_link, link);
+ urb = urbl->urb;
+ if ((ep->credit + 1) < urb->number_of_packets)
+ break;
+ ep->credit -= urb->number_of_packets;
+ if (ep->credit < 0)
+ ep->credit = 0;
+ list_move_tail(&urbl->link, &xfr_list);
+ }
+ }
+ spin_unlock_bh(&ozhcd->hcd_lock);
+ /* Send to PD and complete URBs.
+ */
+ list_for_each_entry_safe(urbl, n, &xfr_list, link) {
+ urb = urbl->urb;
+ list_del_init(&urbl->link);
+ urb->error_count = 0;
+ urb->start_frame = oz_usb_get_frame_number();
+ oz_usb_send_isoc(port->hpd, urbl->ep_num, urb);
+ oz_free_urb_link(urbl);
+ oz_complete_urb(port->ozhcd->hcd, urb, 0);
+ }
+ /* Check the IN isoc endpoints to see if any URBs can be completed.
+ */
+ spin_lock_bh(&ozhcd->hcd_lock);
+ list_for_each_entry(ep, &port->isoc_in_ep, link) {
+ if (ep->flags & OZ_F_EP_BUFFERING) {
+ if (ep->buffered_units >= OZ_IN_BUFFERING_UNITS) {
+ ep->flags &= ~OZ_F_EP_BUFFERING;
+ ep->credit = 0;
+ ep->timestamp = ts;
+ ep->start_frame = 0;
+ }
+ continue;
+ }
+ delta = timespec_sub(ts, ep->timestamp);
+ ep->credit += div_u64(timespec_to_ns(&delta), NSEC_PER_MSEC);
+ ep->timestamp = ts;
+ list_for_each_entry_safe(urbl, n, &ep->urb_list, link) {
+ struct urb *urb = urbl->urb;
+ int len = 0;
+ int copy_len;
+ int i;
+
+ if (ep->credit < urb->number_of_packets)
+ break;
+ if (ep->buffered_units < urb->number_of_packets)
+ break;
+ urb->actual_length = 0;
+ for (i = 0; i < urb->number_of_packets; i++) {
+ len = ep->buffer[ep->out_ix];
+ if (++ep->out_ix == ep->buffer_size)
+ ep->out_ix = 0;
+ copy_len = ep->buffer_size - ep->out_ix;
+ if (copy_len > len)
+ copy_len = len;
+ memcpy(urb->transfer_buffer,
+ &ep->buffer[ep->out_ix], copy_len);
+ if (copy_len < len) {
+ memcpy(urb->transfer_buffer+copy_len,
+ ep->buffer, len-copy_len);
+ ep->out_ix = len-copy_len;
+ } else
+ ep->out_ix += copy_len;
+ if (ep->out_ix == ep->buffer_size)
+ ep->out_ix = 0;
+ urb->iso_frame_desc[i].offset =
+ urb->actual_length;
+ urb->actual_length += len;
+ urb->iso_frame_desc[i].actual_length = len;
+ urb->iso_frame_desc[i].status = 0;
+ }
+ ep->buffered_units -= urb->number_of_packets;
+ urb->error_count = 0;
+ urb->start_frame = ep->start_frame;
+ ep->start_frame += urb->number_of_packets;
+ list_move_tail(&urbl->link, &xfr_list);
+ ep->credit -= urb->number_of_packets;
+ }
+ }
+ if (!list_empty(&port->isoc_out_ep) || !list_empty(&port->isoc_in_ep))
+ rc = 1;
+ spin_unlock_bh(&ozhcd->hcd_lock);
+ /* Complete the filled URBs.
+ */
+ list_for_each_entry_safe(urbl, n, &xfr_list, link) {
+ urb = urbl->urb;
+ list_del_init(&urbl->link);
+ oz_free_urb_link(urbl);
+ oz_complete_urb(port->ozhcd->hcd, urb, 0);
+ }
+ /* Check if there are any ep0 requests that have timed out.
+ * If so resent to PD.
+ */
+ ep = port->out_ep[0];
+ if (ep) {
+ spin_lock_bh(&ozhcd->hcd_lock);
+ list_for_each_entry_safe(urbl, n, &ep->urb_list, link) {
+ if (urbl->submit_counter > EP0_TIMEOUT_COUNTER) {
+ oz_dbg(ON, "Request 0x%p timeout\n", urbl->urb);
+ list_move_tail(&urbl->link, &xfr_list);
+ urbl->submit_counter = 0;
+ } else {
+ urbl->submit_counter++;
+ }
+ }
+ if (!list_empty(&ep->urb_list))
+ rc = 1;
+ spin_unlock_bh(&ozhcd->hcd_lock);
+ list_for_each_entry_safe(urbl, n, &xfr_list, link) {
+ oz_dbg(ON, "Resending request to PD\n");
+ oz_process_ep0_urb(ozhcd, urbl->urb, GFP_ATOMIC);
+ oz_free_urb_link(urbl);
+ }
+ }
+ return rc;
+}
+
+/*
+ * Context: softirq
+ */
+static int oz_build_endpoints_for_interface(struct usb_hcd *hcd,
+ struct oz_port *port,
+ struct usb_host_interface *intf, gfp_t mem_flags)
+{
+ struct oz_hcd *ozhcd = port->ozhcd;
+ int i;
+ int if_ix = intf->desc.bInterfaceNumber;
+ int request_heartbeat = 0;
+
+ oz_dbg(ON, "interface[%d] = %p\n", if_ix, intf);
+ if (if_ix >= port->num_iface || port->iface == NULL)
+ return -ENOMEM;
+ for (i = 0; i < intf->desc.bNumEndpoints; i++) {
+ struct usb_host_endpoint *hep = &intf->endpoint[i];
+ u8 ep_addr = hep->desc.bEndpointAddress;
+ u8 ep_num = ep_addr & USB_ENDPOINT_NUMBER_MASK;
+ struct oz_endpoint *ep;
+ int buffer_size = 0;
+
+ oz_dbg(ON, "%d bEndpointAddress = %x\n", i, ep_addr);
+ if (ep_addr & USB_ENDPOINT_DIR_MASK) {
+ switch (hep->desc.bmAttributes &
+ USB_ENDPOINT_XFERTYPE_MASK) {
+ case USB_ENDPOINT_XFER_ISOC:
+ buffer_size = OZ_EP_BUFFER_SIZE_ISOC;
+ break;
+ case USB_ENDPOINT_XFER_INT:
+ buffer_size = OZ_EP_BUFFER_SIZE_INT;
+ break;
+ }
+ }
+
+ ep = oz_ep_alloc(buffer_size, mem_flags);
+ if (!ep) {
+ oz_clean_endpoints_for_interface(hcd, port, if_ix);
+ return -ENOMEM;
+ }
+ ep->attrib = hep->desc.bmAttributes;
+ ep->ep_num = ep_num;
+ if ((ep->attrib & USB_ENDPOINT_XFERTYPE_MASK)
+ == USB_ENDPOINT_XFER_ISOC) {
+ oz_dbg(ON, "wMaxPacketSize = %d\n",
+ usb_endpoint_maxp(&hep->desc));
+ ep->credit_ceiling = 200;
+ if (ep_addr & USB_ENDPOINT_DIR_MASK) {
+ ep->flags |= OZ_F_EP_BUFFERING;
+ } else {
+ ep->flags |= OZ_F_EP_HAVE_STREAM;
+ if (oz_usb_stream_create(port->hpd, ep_num))
+ ep->flags &= ~OZ_F_EP_HAVE_STREAM;
+ }
+ }
+ spin_lock_bh(&ozhcd->hcd_lock);
+ if (ep_addr & USB_ENDPOINT_DIR_MASK) {
+ port->in_ep[ep_num] = ep;
+ port->iface[if_ix].ep_mask |=
+ (1<<(ep_num+OZ_NB_ENDPOINTS));
+ if ((ep->attrib & USB_ENDPOINT_XFERTYPE_MASK)
+ == USB_ENDPOINT_XFER_ISOC) {
+ list_add_tail(&ep->link, &port->isoc_in_ep);
+ request_heartbeat = 1;
+ }
+ } else {
+ port->out_ep[ep_num] = ep;
+ port->iface[if_ix].ep_mask |= (1<<ep_num);
+ if ((ep->attrib & USB_ENDPOINT_XFERTYPE_MASK)
+ == USB_ENDPOINT_XFER_ISOC) {
+ list_add_tail(&ep->link, &port->isoc_out_ep);
+ request_heartbeat = 1;
+ }
+ }
+ spin_unlock_bh(&ozhcd->hcd_lock);
+ if (request_heartbeat && port->hpd)
+ oz_usb_request_heartbeat(port->hpd);
+ }
+ return 0;
+}
+
+/*
+ * Context: softirq
+ */
+static void oz_clean_endpoints_for_interface(struct usb_hcd *hcd,
+ struct oz_port *port, int if_ix)
+{
+ struct oz_hcd *ozhcd = port->ozhcd;
+ unsigned mask;
+ int i;
+ LIST_HEAD(ep_list);
+ struct oz_endpoint *ep, *n;
+
+ oz_dbg(ON, "Deleting endpoints for interface %d\n", if_ix);
+ if (if_ix >= port->num_iface)
+ return;
+ spin_lock_bh(&ozhcd->hcd_lock);
+ mask = port->iface[if_ix].ep_mask;
+ port->iface[if_ix].ep_mask = 0;
+ for (i = 0; i < OZ_NB_ENDPOINTS; i++) {
+ struct list_head *e;
+ /* Gather OUT endpoints.
+ */
+ if ((mask & (1<<i)) && port->out_ep[i]) {
+ e = &port->out_ep[i]->link;
+ port->out_ep[i] = NULL;
+ /* Remove from isoc list if present.
+ */
+ list_move_tail(e, &ep_list);
+ }
+ /* Gather IN endpoints.
+ */
+ if ((mask & (1<<(i+OZ_NB_ENDPOINTS))) && port->in_ep[i]) {
+ e = &port->in_ep[i]->link;
+ port->in_ep[i] = NULL;
+ list_move_tail(e, &ep_list);
+ }
+ }
+ spin_unlock_bh(&ozhcd->hcd_lock);
+ list_for_each_entry_safe(ep, n, &ep_list, link) {
+ list_del_init(&ep->link);
+ oz_ep_free(port, ep);
+ }
+}
+
+/*
+ * Context: softirq
+ */
+static int oz_build_endpoints_for_config(struct usb_hcd *hcd,
+ struct oz_port *port, struct usb_host_config *config,
+ gfp_t mem_flags)
+{
+ struct oz_hcd *ozhcd = port->ozhcd;
+ int i;
+ int num_iface = config->desc.bNumInterfaces;
+
+ if (num_iface) {
+ struct oz_interface *iface;
+
+ iface = kmalloc_array(num_iface, sizeof(struct oz_interface),
+ mem_flags | __GFP_ZERO);
+ if (!iface)
+ return -ENOMEM;
+ spin_lock_bh(&ozhcd->hcd_lock);
+ port->iface = iface;
+ port->num_iface = num_iface;
+ spin_unlock_bh(&ozhcd->hcd_lock);
+ }
+ for (i = 0; i < num_iface; i++) {
+ struct usb_host_interface *intf =
+ &config->intf_cache[i]->altsetting[0];
+ if (oz_build_endpoints_for_interface(hcd, port, intf,
+ mem_flags))
+ goto fail;
+ }
+ return 0;
+fail:
+ oz_clean_endpoints_for_config(hcd, port);
+ return -1;
+}
+
+/*
+ * Context: softirq
+ */
+static void oz_clean_endpoints_for_config(struct usb_hcd *hcd,
+ struct oz_port *port)
+{
+ struct oz_hcd *ozhcd = port->ozhcd;
+ int i;
+
+ oz_dbg(ON, "Deleting endpoints for configuration\n");
+ for (i = 0; i < port->num_iface; i++)
+ oz_clean_endpoints_for_interface(hcd, port, i);
+ spin_lock_bh(&ozhcd->hcd_lock);
+ if (port->iface) {
+ oz_dbg(ON, "Freeing interfaces object\n");
+ kfree(port->iface);
+ port->iface = NULL;
+ }
+ port->num_iface = 0;
+ spin_unlock_bh(&ozhcd->hcd_lock);
+}
+
+/*
+ * Context: tasklet
+ */
+static void *oz_claim_hpd(struct oz_port *port)
+{
+ void *hpd;
+ struct oz_hcd *ozhcd = port->ozhcd;
+
+ spin_lock_bh(&ozhcd->hcd_lock);
+ hpd = port->hpd;
+ if (hpd)
+ oz_usb_get(hpd);
+ spin_unlock_bh(&ozhcd->hcd_lock);
+ return hpd;
+}
+
+/*
+ * Context: tasklet
+ */
+static void oz_process_ep0_urb(struct oz_hcd *ozhcd, struct urb *urb,
+ gfp_t mem_flags)
+{
+ struct usb_ctrlrequest *setup;
+ unsigned windex;
+ unsigned wvalue;
+ unsigned wlength;
+ void *hpd;
+ u8 req_id;
+ int rc = 0;
+ unsigned complete = 0;
+
+ int port_ix = -1;
+ struct oz_port *port = NULL;
+
+ oz_dbg(URB, "[%s]:(%p)\n", __func__, urb);
+ port_ix = oz_get_port_from_addr(ozhcd, urb->dev->devnum);
+ if (port_ix < 0) {
+ rc = -EPIPE;
+ goto out;
+ }
+ port = &ozhcd->ports[port_ix];
+ if (((port->flags & OZ_PORT_F_PRESENT) == 0)
+ || (port->flags & OZ_PORT_F_DYING)) {
+ oz_dbg(ON, "Refusing URB port_ix = %d devnum = %d\n",
+ port_ix, urb->dev->devnum);
+ rc = -EPIPE;
+ goto out;
+ }
+ /* Store port in private context data.
+ */
+ urb->hcpriv = port;
+ setup = (struct usb_ctrlrequest *)urb->setup_packet;
+ windex = le16_to_cpu(setup->wIndex);
+ wvalue = le16_to_cpu(setup->wValue);
+ wlength = le16_to_cpu(setup->wLength);
+ oz_dbg(CTRL_DETAIL, "bRequestType = %x\n", setup->bRequestType);
+ oz_dbg(CTRL_DETAIL, "bRequest = %x\n", setup->bRequest);
+ oz_dbg(CTRL_DETAIL, "wValue = %x\n", wvalue);
+ oz_dbg(CTRL_DETAIL, "wIndex = %x\n", windex);
+ oz_dbg(CTRL_DETAIL, "wLength = %x\n", wlength);
+
+ req_id = port->next_req_id++;
+ hpd = oz_claim_hpd(port);
+ if (hpd == NULL) {
+ oz_dbg(ON, "Cannot claim port\n");
+ rc = -EPIPE;
+ goto out;
+ }
+
+ if ((setup->bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD) {
+ /* Standard requests
+ */
+ switch (setup->bRequest) {
+ case USB_REQ_GET_DESCRIPTOR:
+ oz_dbg(ON, "USB_REQ_GET_DESCRIPTOR - req\n");
+ break;
+ case USB_REQ_SET_ADDRESS:
+ oz_dbg(ON, "USB_REQ_SET_ADDRESS - req\n");
+ oz_dbg(ON, "Port %d address is 0x%x\n",
+ ozhcd->conn_port,
+ (u8)le16_to_cpu(setup->wValue));
+ spin_lock_bh(&ozhcd->hcd_lock);
+ if (ozhcd->conn_port >= 0) {
+ ozhcd->ports[ozhcd->conn_port].bus_addr =
+ (u8)le16_to_cpu(setup->wValue);
+ oz_dbg(ON, "Clearing conn_port\n");
+ ozhcd->conn_port = -1;
+ }
+ spin_unlock_bh(&ozhcd->hcd_lock);
+ complete = 1;
+ break;
+ case USB_REQ_SET_CONFIGURATION:
+ oz_dbg(ON, "USB_REQ_SET_CONFIGURATION - req\n");
+ break;
+ case USB_REQ_GET_CONFIGURATION:
+ /* We short circuit this case and reply directly since
+ * we have the selected configuration number cached.
+ */
+ oz_dbg(ON, "USB_REQ_GET_CONFIGURATION - reply now\n");
+ if (urb->transfer_buffer_length >= 1) {
+ urb->actual_length = 1;
+ *((u8 *)urb->transfer_buffer) =
+ port->config_num;
+ complete = 1;
+ } else {
+ rc = -EPIPE;
+ }
+ break;
+ case USB_REQ_GET_INTERFACE:
+ /* We short circuit this case and reply directly since
+ * we have the selected interface alternative cached.
+ */
+ oz_dbg(ON, "USB_REQ_GET_INTERFACE - reply now\n");
+ if (urb->transfer_buffer_length >= 1) {
+ urb->actual_length = 1;
+ *((u8 *)urb->transfer_buffer) =
+ port->iface[(u8)windex].alt;
+ oz_dbg(ON, "interface = %d alt = %d\n",
+ windex, port->iface[(u8)windex].alt);
+ complete = 1;
+ } else {
+ rc = -EPIPE;
+ }
+ break;
+ case USB_REQ_SET_INTERFACE:
+ oz_dbg(ON, "USB_REQ_SET_INTERFACE - req\n");
+ break;
+ }
+ }
+ if (!rc && !complete) {
+ int data_len = 0;
+
+ if ((setup->bRequestType & USB_DIR_IN) == 0)
+ data_len = wlength;
+ urb->actual_length = data_len;
+ if (oz_usb_control_req(port->hpd, req_id, setup,
+ urb->transfer_buffer, data_len)) {
+ rc = -ENOMEM;
+ } else {
+ /* Note: we are queuing the request after we have
+ * submitted it to be transmitted. If the request were
+ * to complete before we queued it then it would not
+ * be found in the queue. It seems impossible for
+ * this to happen but if it did the request would
+ * be resubmitted so the problem would hopefully
+ * resolve itself. Putting the request into the
+ * queue before it has been sent is worse since the
+ * urb could be cancelled while we are using it
+ * to build the request.
+ */
+ if (oz_enqueue_ep_urb(port, 0, 0, urb, req_id))
+ rc = -ENOMEM;
+ }
+ }
+ oz_usb_put(hpd);
+out:
+ if (rc || complete) {
+ oz_dbg(ON, "Completing request locally\n");
+ oz_complete_urb(ozhcd->hcd, urb, rc);
+ } else {
+ oz_usb_request_heartbeat(port->hpd);
+ }
+}
+
+/*
+ * Context: tasklet
+ */
+static int oz_urb_process(struct oz_hcd *ozhcd, struct urb *urb)
+{
+ int rc = 0;
+ struct oz_port *port = urb->hcpriv;
+ u8 ep_addr;
+
+ /* When we are paranoid we keep a list of urbs which we check against
+ * before handing one back. This is just for debugging during
+ * development and should be turned off in the released driver.
+ */
+ oz_remember_urb(urb);
+ /* Check buffer is valid.
+ */
+ if (!urb->transfer_buffer && urb->transfer_buffer_length)
+ return -EINVAL;
+ /* Check if there is a device at the port - refuse if not.
+ */
+ if ((port->flags & OZ_PORT_F_PRESENT) == 0)
+ return -EPIPE;
+ ep_addr = usb_pipeendpoint(urb->pipe);
+ if (ep_addr) {
+ /* If the request is not for EP0 then queue it.
+ */
+ if (oz_enqueue_ep_urb(port, ep_addr, usb_pipein(urb->pipe),
+ urb, 0))
+ rc = -EPIPE;
+ } else {
+ oz_process_ep0_urb(ozhcd, urb, GFP_ATOMIC);
+ }
+ return rc;
+}
+
+/*
+ * Context: tasklet
+ */
+static void oz_urb_process_tasklet(unsigned long unused)
+{
+ unsigned long irq_state;
+ struct urb *urb;
+ struct oz_hcd *ozhcd = oz_hcd_claim();
+ struct oz_urb_link *urbl, *n;
+ int rc = 0;
+
+ if (ozhcd == NULL)
+ return;
+ /* This is called from a tasklet so is in softirq context but the urb
+ * list is filled from any context so we need to lock
+ * appropriately while removing urbs.
+ */
+ spin_lock_irqsave(&g_tasklet_lock, irq_state);
+ list_for_each_entry_safe(urbl, n, &ozhcd->urb_pending_list, link) {
+ list_del_init(&urbl->link);
+ spin_unlock_irqrestore(&g_tasklet_lock, irq_state);
+ urb = urbl->urb;
+ oz_free_urb_link(urbl);
+ rc = oz_urb_process(ozhcd, urb);
+ if (rc)
+ oz_complete_urb(ozhcd->hcd, urb, rc);
+ spin_lock_irqsave(&g_tasklet_lock, irq_state);
+ }
+ spin_unlock_irqrestore(&g_tasklet_lock, irq_state);
+ oz_hcd_put(ozhcd);
+}
+
+/*
+ * This function searches for the urb in any of the lists it could be in.
+ * If it is found it is removed from the list and completed. If the urb is
+ * being processed then it won't be in a list so won't be found. However, the
+ * call to usb_hcd_check_unlink_urb() will set the value of the unlinked field
+ * to a non-zero value. When an attempt is made to put the urb back in a list
+ * the unlinked field will be checked and the urb will then be completed.
+ * Context: tasklet
+ */
+static void oz_urb_cancel(struct oz_port *port, u8 ep_num, struct urb *urb)
+{
+ struct oz_urb_link *urbl = NULL;
+ struct list_head *e;
+ struct oz_hcd *ozhcd;
+ unsigned long irq_state;
+ u8 ix;
+
+ if (port == NULL) {
+ oz_dbg(ON, "%s: ERROR: (%p) port is null\n", __func__, urb);
+ return;
+ }
+ ozhcd = port->ozhcd;
+ if (ozhcd == NULL) {
+ oz_dbg(ON, "%s; ERROR: (%p) ozhcd is null\n", __func__, urb);
+ return;
+ }
+
+ /* Look in the tasklet queue.
+ */
+ spin_lock_irqsave(&g_tasklet_lock, irq_state);
+ list_for_each(e, &ozhcd->urb_cancel_list) {
+ urbl = list_entry(e, struct oz_urb_link, link);
+ if (urb == urbl->urb) {
+ list_del_init(e);
+ spin_unlock_irqrestore(&g_tasklet_lock, irq_state);
+ goto out2;
+ }
+ }
+ spin_unlock_irqrestore(&g_tasklet_lock, irq_state);
+ urbl = NULL;
+
+ /* Look in the orphanage.
+ */
+ spin_lock_irqsave(&ozhcd->hcd_lock, irq_state);
+ list_for_each(e, &ozhcd->orphanage) {
+ urbl = list_entry(e, struct oz_urb_link, link);
+ if (urbl->urb == urb) {
+ list_del(e);
+ oz_dbg(ON, "Found urb in orphanage\n");
+ goto out;
+ }
+ }
+ ix = (ep_num & 0xf);
+ urbl = NULL;
+ if ((ep_num & USB_DIR_IN) && ix)
+ urbl = oz_remove_urb(port->in_ep[ix], urb);
+ else
+ urbl = oz_remove_urb(port->out_ep[ix], urb);
+out:
+ spin_unlock_irqrestore(&ozhcd->hcd_lock, irq_state);
+out2:
+ if (urbl) {
+ urb->actual_length = 0;
+ oz_free_urb_link(urbl);
+ oz_complete_urb(ozhcd->hcd, urb, -EPIPE);
+ }
+}
+
+/*
+ * Context: tasklet
+ */
+static void oz_urb_cancel_tasklet(unsigned long unused)
+{
+ unsigned long irq_state;
+ struct urb *urb;
+ struct oz_urb_link *urbl, *n;
+ struct oz_hcd *ozhcd = oz_hcd_claim();
+
+ if (ozhcd == NULL)
+ return;
+ spin_lock_irqsave(&g_tasklet_lock, irq_state);
+ list_for_each_entry_safe(urbl, n, &ozhcd->urb_cancel_list, link) {
+ list_del_init(&urbl->link);
+ spin_unlock_irqrestore(&g_tasklet_lock, irq_state);
+ urb = urbl->urb;
+ if (urb->unlinked)
+ oz_urb_cancel(urbl->port, urbl->ep_num, urb);
+ oz_free_urb_link(urbl);
+ spin_lock_irqsave(&g_tasklet_lock, irq_state);
+ }
+ spin_unlock_irqrestore(&g_tasklet_lock, irq_state);
+ oz_hcd_put(ozhcd);
+}
+
+/*
+ * Context: unknown
+ */
+static void oz_hcd_clear_orphanage(struct oz_hcd *ozhcd, int status)
+{
+ if (ozhcd) {
+ struct oz_urb_link *urbl, *n;
+
+ list_for_each_entry_safe(urbl, n, &ozhcd->orphanage, link) {
+ list_del(&urbl->link);
+ oz_complete_urb(ozhcd->hcd, urbl->urb, status);
+ oz_free_urb_link(urbl);
+ }
+ }
+}
+
+/*
+ * Context: unknown
+ */
+static int oz_hcd_start(struct usb_hcd *hcd)
+{
+ hcd->power_budget = 200;
+ hcd->state = HC_STATE_RUNNING;
+ hcd->uses_new_polling = 1;
+ return 0;
+}
+
+/*
+ * Context: unknown
+ */
+static void oz_hcd_stop(struct usb_hcd *hcd)
+{
+}
+
+/*
+ * Context: unknown
+ */
+static void oz_hcd_shutdown(struct usb_hcd *hcd)
+{
+}
+
+/*
+ * Called to queue an urb for the device.
+ * This function should return a non-zero error code if it fails the urb but
+ * should not call usb_hcd_giveback_urb().
+ * Context: any
+ */
+static int oz_hcd_urb_enqueue(struct usb_hcd *hcd, struct urb *urb,
+ gfp_t mem_flags)
+{
+ struct oz_hcd *ozhcd = oz_hcd_private(hcd);
+ int rc;
+ int port_ix;
+ struct oz_port *port;
+ unsigned long irq_state;
+ struct oz_urb_link *urbl;
+
+ oz_dbg(URB, "%s: (%p)\n", __func__, urb);
+ if (unlikely(ozhcd == NULL)) {
+ oz_dbg(URB, "Refused urb(%p) not ozhcd\n", urb);
+ return -EPIPE;
+ }
+ if (unlikely(hcd->state != HC_STATE_RUNNING)) {
+ oz_dbg(URB, "Refused urb(%p) not running\n", urb);
+ return -EPIPE;
+ }
+ port_ix = oz_get_port_from_addr(ozhcd, urb->dev->devnum);
+ if (port_ix < 0)
+ return -EPIPE;
+ port = &ozhcd->ports[port_ix];
+ if (port == NULL)
+ return -EPIPE;
+ if (!(port->flags & OZ_PORT_F_PRESENT) ||
+ (port->flags & OZ_PORT_F_CHANGED)) {
+ oz_dbg(ON, "Refusing URB port_ix = %d devnum = %d\n",
+ port_ix, urb->dev->devnum);
+ return -EPIPE;
+ }
+ urb->hcpriv = port;
+ /* Put request in queue for processing by tasklet.
+ */
+ urbl = oz_alloc_urb_link();
+ if (unlikely(urbl == NULL))
+ return -ENOMEM;
+ urbl->urb = urb;
+ spin_lock_irqsave(&g_tasklet_lock, irq_state);
+ rc = usb_hcd_link_urb_to_ep(hcd, urb);
+ if (unlikely(rc)) {
+ spin_unlock_irqrestore(&g_tasklet_lock, irq_state);
+ oz_free_urb_link(urbl);
+ return rc;
+ }
+ list_add_tail(&urbl->link, &ozhcd->urb_pending_list);
+ spin_unlock_irqrestore(&g_tasklet_lock, irq_state);
+ tasklet_schedule(&g_urb_process_tasklet);
+ atomic_inc(&g_pending_urbs);
+ return 0;
+}
+
+/*
+ * Context: tasklet
+ */
+static struct oz_urb_link *oz_remove_urb(struct oz_endpoint *ep,
+ struct urb *urb)
+{
+ struct oz_urb_link *urbl;
+
+ if (unlikely(ep == NULL))
+ return NULL;
+
+ list_for_each_entry(urbl, &ep->urb_list, link) {
+ if (urbl->urb == urb) {
+ list_del_init(&urbl->link);
+ if (usb_pipeisoc(urb->pipe)) {
+ ep->credit -= urb->number_of_packets;
+ if (ep->credit < 0)
+ ep->credit = 0;
+ }
+ return urbl;
+ }
+ }
+ return NULL;
+}
+
+/*
+ * Called to dequeue a previously submitted urb for the device.
+ * Context: any
+ */
+static int oz_hcd_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status)
+{
+ struct oz_hcd *ozhcd = oz_hcd_private(hcd);
+ struct oz_urb_link *urbl;
+ int rc;
+ unsigned long irq_state;
+
+ oz_dbg(URB, "%s: (%p)\n", __func__, urb);
+ urbl = oz_alloc_urb_link();
+ if (unlikely(urbl == NULL))
+ return -ENOMEM;
+ spin_lock_irqsave(&g_tasklet_lock, irq_state);
+ /* The following function checks the urb is still in the queue
+ * maintained by the core and that the unlinked field is zero.
+ * If both are true the function sets the unlinked field and returns
+ * zero. Otherwise it returns an error.
+ */
+ rc = usb_hcd_check_unlink_urb(hcd, urb, status);
+ /* We have to check we haven't completed the urb or are about
+ * to complete it. When we do we set hcpriv to 0 so if this has
+ * already happened we don't put the urb in the cancel queue.
+ */
+ if ((rc == 0) && urb->hcpriv) {
+ urbl->urb = urb;
+ urbl->port = (struct oz_port *)urb->hcpriv;
+ urbl->ep_num = usb_pipeendpoint(urb->pipe);
+ if (usb_pipein(urb->pipe))
+ urbl->ep_num |= USB_DIR_IN;
+ list_add_tail(&urbl->link, &ozhcd->urb_cancel_list);
+ spin_unlock_irqrestore(&g_tasklet_lock, irq_state);
+ tasklet_schedule(&g_urb_cancel_tasklet);
+ } else {
+ spin_unlock_irqrestore(&g_tasklet_lock, irq_state);
+ oz_free_urb_link(urbl);
+ }
+ return rc;
+}
+
+/*
+ * Context: unknown
+ */
+static void oz_hcd_endpoint_disable(struct usb_hcd *hcd,
+ struct usb_host_endpoint *ep)
+{
+}
+
+/*
+ * Context: unknown
+ */
+static void oz_hcd_endpoint_reset(struct usb_hcd *hcd,
+ struct usb_host_endpoint *ep)
+{
+}
+
+/*
+ * Context: unknown
+ */
+static int oz_hcd_get_frame_number(struct usb_hcd *hcd)
+{
+ oz_dbg(ON, "oz_hcd_get_frame_number\n");
+ return oz_usb_get_frame_number();
+}
+
+/*
+ * Context: softirq
+ * This is called as a consquence of us calling usb_hcd_poll_rh_status() and we
+ * always do that in softirq context.
+ */
+static int oz_hcd_hub_status_data(struct usb_hcd *hcd, char *buf)
+{
+ struct oz_hcd *ozhcd = oz_hcd_private(hcd);
+ int i;
+
+ buf[0] = 0;
+ buf[1] = 0;
+
+ spin_lock_bh(&ozhcd->hcd_lock);
+ for (i = 0; i < OZ_NB_PORTS; i++) {
+ if (ozhcd->ports[i].flags & OZ_PORT_F_CHANGED) {
+ oz_dbg(HUB, "Port %d changed\n", i);
+ ozhcd->ports[i].flags &= ~OZ_PORT_F_CHANGED;
+ if (i < 7)
+ buf[0] |= 1 << (i + 1);
+ else
+ buf[1] |= 1 << (i - 7);
+ }
+ }
+ spin_unlock_bh(&ozhcd->hcd_lock);
+ if (buf[0] != 0 || buf[1] != 0)
+ return 2;
+ return 0;
+}
+
+/*
+ * Context: process
+ */
+static void oz_get_hub_descriptor(struct usb_hcd *hcd,
+ struct usb_hub_descriptor *desc)
+{
+ memset(desc, 0, sizeof(*desc));
+ desc->bDescriptorType = 0x29;
+ desc->bDescLength = 9;
+ desc->wHubCharacteristics = cpu_to_le16(0x0001);
+ desc->bNbrPorts = OZ_NB_PORTS;
+}
+
+/*
+ * Context: process
+ */
+static int oz_set_port_feature(struct usb_hcd *hcd, u16 wvalue, u16 windex)
+{
+ struct oz_port *port;
+ u8 port_id = (u8)windex;
+ struct oz_hcd *ozhcd = oz_hcd_private(hcd);
+ unsigned set_bits = 0;
+ unsigned clear_bits = 0;
+
+ if ((port_id < 1) || (port_id > OZ_NB_PORTS))
+ return -EPIPE;
+ port = &ozhcd->ports[port_id-1];
+ switch (wvalue) {
+ case USB_PORT_FEAT_CONNECTION:
+ oz_dbg(HUB, "USB_PORT_FEAT_CONNECTION\n");
+ break;
+ case USB_PORT_FEAT_ENABLE:
+ oz_dbg(HUB, "USB_PORT_FEAT_ENABLE\n");
+ break;
+ case USB_PORT_FEAT_SUSPEND:
+ oz_dbg(HUB, "USB_PORT_FEAT_SUSPEND\n");
+ break;
+ case USB_PORT_FEAT_OVER_CURRENT:
+ oz_dbg(HUB, "USB_PORT_FEAT_OVER_CURRENT\n");
+ break;
+ case USB_PORT_FEAT_RESET:
+ oz_dbg(HUB, "USB_PORT_FEAT_RESET\n");
+ set_bits = USB_PORT_STAT_ENABLE | (USB_PORT_STAT_C_RESET<<16);
+ clear_bits = USB_PORT_STAT_RESET;
+ ozhcd->ports[port_id-1].bus_addr = 0;
+ break;
+ case USB_PORT_FEAT_POWER:
+ oz_dbg(HUB, "USB_PORT_FEAT_POWER\n");
+ set_bits |= USB_PORT_STAT_POWER;
+ break;
+ case USB_PORT_FEAT_LOWSPEED:
+ oz_dbg(HUB, "USB_PORT_FEAT_LOWSPEED\n");
+ break;
+ case USB_PORT_FEAT_C_CONNECTION:
+ oz_dbg(HUB, "USB_PORT_FEAT_C_CONNECTION\n");
+ break;
+ case USB_PORT_FEAT_C_ENABLE:
+ oz_dbg(HUB, "USB_PORT_FEAT_C_ENABLE\n");
+ break;
+ case USB_PORT_FEAT_C_SUSPEND:
+ oz_dbg(HUB, "USB_PORT_FEAT_C_SUSPEND\n");
+ break;
+ case USB_PORT_FEAT_C_OVER_CURRENT:
+ oz_dbg(HUB, "USB_PORT_FEAT_C_OVER_CURRENT\n");
+ break;
+ case USB_PORT_FEAT_C_RESET:
+ oz_dbg(HUB, "USB_PORT_FEAT_C_RESET\n");
+ break;
+ case USB_PORT_FEAT_TEST:
+ oz_dbg(HUB, "USB_PORT_FEAT_TEST\n");
+ break;
+ case USB_PORT_FEAT_INDICATOR:
+ oz_dbg(HUB, "USB_PORT_FEAT_INDICATOR\n");
+ break;
+ default:
+ oz_dbg(HUB, "Other %d\n", wvalue);
+ break;
+ }
+ if (set_bits || clear_bits) {
+ spin_lock_bh(&port->port_lock);
+ port->status &= ~clear_bits;
+ port->status |= set_bits;
+ spin_unlock_bh(&port->port_lock);
+ }
+ oz_dbg(HUB, "Port[%d] status = 0x%x\n", port_id, port->status);
+ return 0;
+}
+
+/*
+ * Context: process
+ */
+static int oz_clear_port_feature(struct usb_hcd *hcd, u16 wvalue, u16 windex)
+{
+ struct oz_port *port;
+ u8 port_id = (u8)windex;
+ struct oz_hcd *ozhcd = oz_hcd_private(hcd);
+ unsigned clear_bits = 0;
+
+ if ((port_id < 1) || (port_id > OZ_NB_PORTS))
+ return -EPIPE;
+ port = &ozhcd->ports[port_id-1];
+ switch (wvalue) {
+ case USB_PORT_FEAT_CONNECTION:
+ oz_dbg(HUB, "USB_PORT_FEAT_CONNECTION\n");
+ break;
+ case USB_PORT_FEAT_ENABLE:
+ oz_dbg(HUB, "USB_PORT_FEAT_ENABLE\n");
+ clear_bits = USB_PORT_STAT_ENABLE;
+ break;
+ case USB_PORT_FEAT_SUSPEND:
+ oz_dbg(HUB, "USB_PORT_FEAT_SUSPEND\n");
+ break;
+ case USB_PORT_FEAT_OVER_CURRENT:
+ oz_dbg(HUB, "USB_PORT_FEAT_OVER_CURRENT\n");
+ break;
+ case USB_PORT_FEAT_RESET:
+ oz_dbg(HUB, "USB_PORT_FEAT_RESET\n");
+ break;
+ case USB_PORT_FEAT_POWER:
+ oz_dbg(HUB, "USB_PORT_FEAT_POWER\n");
+ clear_bits |= USB_PORT_STAT_POWER;
+ break;
+ case USB_PORT_FEAT_LOWSPEED:
+ oz_dbg(HUB, "USB_PORT_FEAT_LOWSPEED\n");
+ break;
+ case USB_PORT_FEAT_C_CONNECTION:
+ oz_dbg(HUB, "USB_PORT_FEAT_C_CONNECTION\n");
+ clear_bits = USB_PORT_STAT_C_CONNECTION << 16;
+ break;
+ case USB_PORT_FEAT_C_ENABLE:
+ oz_dbg(HUB, "USB_PORT_FEAT_C_ENABLE\n");
+ clear_bits = USB_PORT_STAT_C_ENABLE << 16;
+ break;
+ case USB_PORT_FEAT_C_SUSPEND:
+ oz_dbg(HUB, "USB_PORT_FEAT_C_SUSPEND\n");
+ break;
+ case USB_PORT_FEAT_C_OVER_CURRENT:
+ oz_dbg(HUB, "USB_PORT_FEAT_C_OVER_CURRENT\n");
+ break;
+ case USB_PORT_FEAT_C_RESET:
+ oz_dbg(HUB, "USB_PORT_FEAT_C_RESET\n");
+ clear_bits = USB_PORT_FEAT_C_RESET << 16;
+ break;
+ case USB_PORT_FEAT_TEST:
+ oz_dbg(HUB, "USB_PORT_FEAT_TEST\n");
+ break;
+ case USB_PORT_FEAT_INDICATOR:
+ oz_dbg(HUB, "USB_PORT_FEAT_INDICATOR\n");
+ break;
+ default:
+ oz_dbg(HUB, "Other %d\n", wvalue);
+ break;
+ }
+ if (clear_bits) {
+ spin_lock_bh(&port->port_lock);
+ port->status &= ~clear_bits;
+ spin_unlock_bh(&port->port_lock);
+ }
+ oz_dbg(HUB, "Port[%d] status = 0x%x\n",
+ port_id, ozhcd->ports[port_id-1].status);
+ return 0;
+}
+
+/*
+ * Context: process
+ */
+static int oz_get_port_status(struct usb_hcd *hcd, u16 windex, char *buf)
+{
+ struct oz_hcd *ozhcd;
+ u32 status;
+
+ if ((windex < 1) || (windex > OZ_NB_PORTS))
+ return -EPIPE;
+ ozhcd = oz_hcd_private(hcd);
+ oz_dbg(HUB, "GetPortStatus windex = %d\n", windex);
+ status = ozhcd->ports[windex-1].status;
+ put_unaligned(cpu_to_le32(status), (__le32 *)buf);
+ oz_dbg(HUB, "Port[%d] status = %x\n", windex, status);
+ return 0;
+}
+
+/*
+ * Context: process
+ */
+static int oz_hcd_hub_control(struct usb_hcd *hcd, u16 req_type, u16 wvalue,
+ u16 windex, char *buf, u16 wlength)
+{
+ int err = 0;
+
+ switch (req_type) {
+ case ClearHubFeature:
+ oz_dbg(HUB, "ClearHubFeature: %d\n", req_type);
+ break;
+ case ClearPortFeature:
+ err = oz_clear_port_feature(hcd, wvalue, windex);
+ break;
+ case GetHubDescriptor:
+ oz_get_hub_descriptor(hcd, (struct usb_hub_descriptor *)buf);
+ break;
+ case GetHubStatus:
+ oz_dbg(HUB, "GetHubStatus: req_type = 0x%x\n", req_type);
+ put_unaligned(cpu_to_le32(0), (__le32 *)buf);
+ break;
+ case GetPortStatus:
+ err = oz_get_port_status(hcd, windex, buf);
+ break;
+ case SetHubFeature:
+ oz_dbg(HUB, "SetHubFeature: %d\n", req_type);
+ break;
+ case SetPortFeature:
+ err = oz_set_port_feature(hcd, wvalue, windex);
+ break;
+ default:
+ oz_dbg(HUB, "Other: %d\n", req_type);
+ break;
+ }
+ return err;
+}
+
+/*
+ * Context: process
+ */
+static int oz_hcd_bus_suspend(struct usb_hcd *hcd)
+{
+ struct oz_hcd *ozhcd;
+
+ ozhcd = oz_hcd_private(hcd);
+ spin_lock_bh(&ozhcd->hcd_lock);
+ hcd->state = HC_STATE_SUSPENDED;
+ ozhcd->flags |= OZ_HDC_F_SUSPENDED;
+ spin_unlock_bh(&ozhcd->hcd_lock);
+ return 0;
+}
+
+/*
+ * Context: process
+ */
+static int oz_hcd_bus_resume(struct usb_hcd *hcd)
+{
+ struct oz_hcd *ozhcd;
+
+ ozhcd = oz_hcd_private(hcd);
+ spin_lock_bh(&ozhcd->hcd_lock);
+ ozhcd->flags &= ~OZ_HDC_F_SUSPENDED;
+ hcd->state = HC_STATE_RUNNING;
+ spin_unlock_bh(&ozhcd->hcd_lock);
+ return 0;
+}
+
+static void oz_plat_shutdown(struct platform_device *dev)
+{
+}
+
+/*
+ * Context: process
+ */
+static int oz_plat_probe(struct platform_device *dev)
+{
+ int i;
+ int err;
+ struct usb_hcd *hcd;
+ struct oz_hcd *ozhcd;
+
+ hcd = usb_create_hcd(&g_oz_hc_drv, &dev->dev, dev_name(&dev->dev));
+ if (hcd == NULL) {
+ oz_dbg(ON, "Failed to created hcd object OK\n");
+ return -ENOMEM;
+ }
+ ozhcd = oz_hcd_private(hcd);
+ memset(ozhcd, 0, sizeof(*ozhcd));
+ INIT_LIST_HEAD(&ozhcd->urb_pending_list);
+ INIT_LIST_HEAD(&ozhcd->urb_cancel_list);
+ INIT_LIST_HEAD(&ozhcd->orphanage);
+ ozhcd->hcd = hcd;
+ ozhcd->conn_port = -1;
+ spin_lock_init(&ozhcd->hcd_lock);
+ for (i = 0; i < OZ_NB_PORTS; i++) {
+ struct oz_port *port = &ozhcd->ports[i];
+
+ port->ozhcd = ozhcd;
+ port->flags = 0;
+ port->status = 0;
+ port->bus_addr = 0xff;
+ spin_lock_init(&port->port_lock);
+ }
+ err = usb_add_hcd(hcd, 0, 0);
+ if (err) {
+ oz_dbg(ON, "Failed to add hcd object OK\n");
+ usb_put_hcd(hcd);
+ return -1;
+ }
+ device_wakeup_enable(hcd->self.controller);
+
+ spin_lock_bh(&g_hcdlock);
+ g_ozhcd = ozhcd;
+ spin_unlock_bh(&g_hcdlock);
+ return 0;
+}
+
+/*
+ * Context: unknown
+ */
+static int oz_plat_remove(struct platform_device *dev)
+{
+ struct usb_hcd *hcd = platform_get_drvdata(dev);
+ struct oz_hcd *ozhcd;
+
+ if (hcd == NULL)
+ return -1;
+ ozhcd = oz_hcd_private(hcd);
+ spin_lock_bh(&g_hcdlock);
+ if (ozhcd == g_ozhcd)
+ g_ozhcd = NULL;
+ spin_unlock_bh(&g_hcdlock);
+ oz_dbg(ON, "Clearing orphanage\n");
+ oz_hcd_clear_orphanage(ozhcd, -EPIPE);
+ oz_dbg(ON, "Removing hcd\n");
+ usb_remove_hcd(hcd);
+ usb_put_hcd(hcd);
+ return 0;
+}
+
+/*
+ * Context: unknown
+ */
+static int oz_plat_suspend(struct platform_device *dev, pm_message_t msg)
+{
+ return 0;
+}
+
+
+/*
+ * Context: unknown
+ */
+static int oz_plat_resume(struct platform_device *dev)
+{
+ return 0;
+}
+
+/*
+ * Context: process
+ */
+int oz_hcd_init(void)
+{
+ int err;
+
+ if (usb_disabled())
+ return -ENODEV;
+
+ oz_urb_link_cache = KMEM_CACHE(oz_urb_link, 0);
+ if (!oz_urb_link_cache)
+ return -ENOMEM;
+
+ tasklet_init(&g_urb_process_tasklet, oz_urb_process_tasklet, 0);
+ tasklet_init(&g_urb_cancel_tasklet, oz_urb_cancel_tasklet, 0);
+ err = platform_driver_register(&g_oz_plat_drv);
+ oz_dbg(ON, "platform_driver_register() returned %d\n", err);
+ if (err)
+ goto error;
+ g_plat_dev = platform_device_alloc(OZ_PLAT_DEV_NAME, -1);
+ if (g_plat_dev == NULL) {
+ err = -ENOMEM;
+ goto error1;
+ }
+ oz_dbg(ON, "platform_device_alloc() succeeded\n");
+ err = platform_device_add(g_plat_dev);
+ if (err)
+ goto error2;
+ oz_dbg(ON, "platform_device_add() succeeded\n");
+ return 0;
+error2:
+ platform_device_put(g_plat_dev);
+error1:
+ platform_driver_unregister(&g_oz_plat_drv);
+error:
+ tasklet_disable(&g_urb_process_tasklet);
+ tasklet_disable(&g_urb_cancel_tasklet);
+ oz_dbg(ON, "oz_hcd_init() failed %d\n", err);
+ return err;
+}
+
+/*
+ * Context: process
+ */
+void oz_hcd_term(void)
+{
+ msleep(OZ_HUB_DEBOUNCE_TIMEOUT);
+ tasklet_kill(&g_urb_process_tasklet);
+ tasklet_kill(&g_urb_cancel_tasklet);
+ platform_device_unregister(g_plat_dev);
+ platform_driver_unregister(&g_oz_plat_drv);
+ oz_dbg(ON, "Pending urbs:%d\n", atomic_read(&g_pending_urbs));
+ kmem_cache_destroy(oz_urb_link_cache);
+}
diff --git a/drivers/staging/ozwpan/ozhcd.h b/drivers/staging/ozwpan/ozhcd.h
new file mode 100644
index 000000000..55e97b1c7
--- /dev/null
+++ b/drivers/staging/ozwpan/ozhcd.h
@@ -0,0 +1,15 @@
+/* -----------------------------------------------------------------------------
+ * Copyright (c) 2011 Ozmo Inc
+ * Released under the GNU General Public License Version 2 (GPLv2).
+ * ---------------------------------------------------------------------------*/
+#ifndef _OZHCD_H
+#define _OZHCD_H
+
+int oz_hcd_init(void);
+void oz_hcd_term(void);
+struct oz_port *oz_hcd_pd_arrived(void *ctx);
+void oz_hcd_pd_departed(struct oz_port *hport);
+void oz_hcd_pd_reset(void *hpd, void *hport);
+
+#endif /* _OZHCD_H */
+
diff --git a/drivers/staging/ozwpan/ozmain.c b/drivers/staging/ozwpan/ozmain.c
new file mode 100644
index 000000000..74ef34815
--- /dev/null
+++ b/drivers/staging/ozwpan/ozmain.c
@@ -0,0 +1,71 @@
+/* -----------------------------------------------------------------------------
+ * Copyright (c) 2011 Ozmo Inc
+ * Released under the GNU General Public License Version 2 (GPLv2).
+ * -----------------------------------------------------------------------------
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/timer.h>
+#include <linux/sched.h>
+#include <linux/netdevice.h>
+#include <linux/errno.h>
+#include <linux/ieee80211.h>
+#include "ozdbg.h"
+#include "ozpd.h"
+#include "ozproto.h"
+#include "ozcdev.h"
+
+unsigned int oz_dbg_mask = OZ_DEFAULT_DBG_MASK;
+
+/*
+ * The name of the 802.11 mac device. Empty string is the default value but a
+ * value can be supplied as a parameter to the module. An empty string means
+ * bind to nothing. '*' means bind to all netcards - this includes non-802.11
+ * netcards. Bindings can be added later using an IOCTL.
+ */
+static char *g_net_dev = "";
+module_param(g_net_dev, charp, S_IRUGO);
+MODULE_PARM_DESC(g_net_dev, "The device(s) to bind to; "
+ "'*' means all, '' (empty string; default) means none.");
+
+/*
+ * Context: process
+ */
+static int __init ozwpan_init(void)
+{
+ int err;
+
+ err = oz_cdev_register();
+ if (err)
+ return err;
+ err = oz_protocol_init(g_net_dev);
+ if (err)
+ goto err_protocol;
+ oz_app_enable(OZ_APPID_USB, 1);
+ oz_apps_init();
+ return 0;
+
+err_protocol:
+ oz_cdev_deregister();
+ return err;
+}
+
+/*
+ * Context: process
+ */
+static void __exit ozwpan_exit(void)
+{
+ oz_protocol_term();
+ oz_apps_term();
+ oz_cdev_deregister();
+}
+
+module_init(ozwpan_init);
+module_exit(ozwpan_exit);
+
+MODULE_AUTHOR("Chris Kelly");
+MODULE_DESCRIPTION("Ozmo Devices USB over WiFi hcd driver");
+MODULE_VERSION("1.0.13");
+MODULE_LICENSE("GPL");
+
diff --git a/drivers/staging/ozwpan/ozpd.c b/drivers/staging/ozwpan/ozpd.c
new file mode 100644
index 000000000..021d74a13
--- /dev/null
+++ b/drivers/staging/ozwpan/ozpd.c
@@ -0,0 +1,886 @@
+/* -----------------------------------------------------------------------------
+ * Copyright (c) 2011 Ozmo Inc
+ * Released under the GNU General Public License Version 2 (GPLv2).
+ * -----------------------------------------------------------------------------
+ */
+
+#include <linux/module.h>
+#include <linux/timer.h>
+#include <linux/sched.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/errno.h>
+#include "ozdbg.h"
+#include "ozprotocol.h"
+#include "ozeltbuf.h"
+#include "ozpd.h"
+#include "ozproto.h"
+#include "ozcdev.h"
+#include "ozusbsvc.h"
+#include <asm/unaligned.h>
+#include <linux/uaccess.h>
+#include <net/psnap.h>
+
+static struct oz_tx_frame *oz_tx_frame_alloc(struct oz_pd *pd);
+static void oz_tx_frame_free(struct oz_pd *pd, struct oz_tx_frame *f);
+static void oz_tx_isoc_free(struct oz_pd *pd, struct oz_tx_frame *f);
+static struct sk_buff *oz_build_frame(struct oz_pd *pd, struct oz_tx_frame *f);
+static int oz_send_isoc_frame(struct oz_pd *pd);
+static void oz_retire_frame(struct oz_pd *pd, struct oz_tx_frame *f);
+static void oz_isoc_stream_free(struct oz_isoc_stream *st);
+static int oz_send_next_queued_frame(struct oz_pd *pd, int more_data);
+static void oz_isoc_destructor(struct sk_buff *skb);
+
+/*
+ * Counts the uncompleted isoc frames submitted to netcard.
+ */
+static atomic_t g_submitted_isoc = ATOMIC_INIT(0);
+
+/* Application handler functions.
+ */
+static const struct oz_app_if g_app_if[OZ_NB_APPS] = {
+ [OZ_APPID_USB] = {
+ .init = oz_usb_init,
+ .term = oz_usb_term,
+ .start = oz_usb_start,
+ .stop = oz_usb_stop,
+ .rx = oz_usb_rx,
+ .heartbeat = oz_usb_heartbeat,
+ .farewell = oz_usb_farewell,
+ },
+ [OZ_APPID_SERIAL] = {
+ .init = oz_cdev_init,
+ .term = oz_cdev_term,
+ .start = oz_cdev_start,
+ .stop = oz_cdev_stop,
+ .rx = oz_cdev_rx,
+ },
+};
+
+
+/*
+ * Context: softirq or process
+ */
+void oz_pd_set_state(struct oz_pd *pd, unsigned state)
+{
+ pd->state = state;
+ switch (state) {
+ case OZ_PD_S_IDLE:
+ oz_pd_dbg(pd, ON, "PD State: OZ_PD_S_IDLE\n");
+ break;
+ case OZ_PD_S_CONNECTED:
+ oz_pd_dbg(pd, ON, "PD State: OZ_PD_S_CONNECTED\n");
+ break;
+ case OZ_PD_S_STOPPED:
+ oz_pd_dbg(pd, ON, "PD State: OZ_PD_S_STOPPED\n");
+ break;
+ case OZ_PD_S_SLEEP:
+ oz_pd_dbg(pd, ON, "PD State: OZ_PD_S_SLEEP\n");
+ break;
+ }
+}
+
+/*
+ * Context: softirq or process
+ */
+void oz_pd_get(struct oz_pd *pd)
+{
+ atomic_inc(&pd->ref_count);
+}
+
+/*
+ * Context: softirq or process
+ */
+void oz_pd_put(struct oz_pd *pd)
+{
+ if (atomic_dec_and_test(&pd->ref_count))
+ oz_pd_destroy(pd);
+}
+
+/*
+ * Context: softirq-serialized
+ */
+struct oz_pd *oz_pd_alloc(const u8 *mac_addr)
+{
+ struct oz_pd *pd;
+ int i;
+
+ pd = kzalloc(sizeof(struct oz_pd), GFP_ATOMIC);
+ if (!pd)
+ return NULL;
+
+ atomic_set(&pd->ref_count, 2);
+ for (i = 0; i < OZ_NB_APPS; i++)
+ spin_lock_init(&pd->app_lock[i]);
+ pd->last_rx_pkt_num = 0xffffffff;
+ oz_pd_set_state(pd, OZ_PD_S_IDLE);
+ pd->max_tx_size = OZ_MAX_TX_SIZE;
+ ether_addr_copy(pd->mac_addr, mac_addr);
+ oz_elt_buf_init(&pd->elt_buff);
+ spin_lock_init(&pd->tx_frame_lock);
+ INIT_LIST_HEAD(&pd->tx_queue);
+ INIT_LIST_HEAD(&pd->farewell_list);
+ pd->last_sent_frame = &pd->tx_queue;
+ spin_lock_init(&pd->stream_lock);
+ INIT_LIST_HEAD(&pd->stream_list);
+ tasklet_init(&pd->heartbeat_tasklet, oz_pd_heartbeat_handler,
+ (unsigned long)pd);
+ tasklet_init(&pd->timeout_tasklet, oz_pd_timeout_handler,
+ (unsigned long)pd);
+ hrtimer_init(&pd->heartbeat, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+ hrtimer_init(&pd->timeout, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+ pd->heartbeat.function = oz_pd_heartbeat_event;
+ pd->timeout.function = oz_pd_timeout_event;
+
+ return pd;
+}
+
+/*
+ * Context: softirq or process
+ */
+static void oz_pd_free(struct work_struct *work)
+{
+ struct list_head *e, *n;
+ struct oz_pd *pd;
+
+ oz_pd_dbg(pd, ON, "Destroying PD\n");
+ pd = container_of(work, struct oz_pd, workitem);
+ /*Disable timer tasklets*/
+ tasklet_kill(&pd->heartbeat_tasklet);
+ tasklet_kill(&pd->timeout_tasklet);
+
+ /* Free streams, queued tx frames and farewells. */
+
+ list_for_each_safe(e, n, &pd->stream_list)
+ oz_isoc_stream_free(list_entry(e, struct oz_isoc_stream, link));
+
+ list_for_each_safe(e, n, &pd->tx_queue) {
+ struct oz_tx_frame *f = list_entry(e, struct oz_tx_frame, link);
+
+ if (f->skb != NULL)
+ kfree_skb(f->skb);
+ oz_retire_frame(pd, f);
+ }
+
+ oz_elt_buf_term(&pd->elt_buff);
+
+ list_for_each_safe(e, n, &pd->farewell_list)
+ kfree(list_entry(e, struct oz_farewell, link));
+
+ if (pd->net_dev)
+ dev_put(pd->net_dev);
+ kfree(pd);
+}
+
+/*
+ * Context: softirq or Process
+ */
+void oz_pd_destroy(struct oz_pd *pd)
+{
+ if (hrtimer_active(&pd->timeout))
+ hrtimer_cancel(&pd->timeout);
+ if (hrtimer_active(&pd->heartbeat))
+ hrtimer_cancel(&pd->heartbeat);
+
+ INIT_WORK(&pd->workitem, oz_pd_free);
+ if (!schedule_work(&pd->workitem))
+ oz_pd_dbg(pd, ON, "failed to schedule workitem\n");
+}
+
+/*
+ * Context: softirq-serialized
+ */
+int oz_services_start(struct oz_pd *pd, u16 apps, int resume)
+{
+ int i, rc = 0;
+
+ oz_pd_dbg(pd, ON, "%s: (0x%x) resume(%d)\n", __func__, apps, resume);
+ for (i = 0; i < OZ_NB_APPS; i++) {
+ if (g_app_if[i].start && (apps & (1 << i))) {
+ if (g_app_if[i].start(pd, resume)) {
+ rc = -1;
+ oz_pd_dbg(pd, ON,
+ "Unable to start service %d\n", i);
+ break;
+ }
+ spin_lock_bh(&g_polling_lock);
+ pd->total_apps |= (1 << i);
+ if (resume)
+ pd->paused_apps &= ~(1 << i);
+ spin_unlock_bh(&g_polling_lock);
+ }
+ }
+ return rc;
+}
+
+/*
+ * Context: softirq or process
+ */
+void oz_services_stop(struct oz_pd *pd, u16 apps, int pause)
+{
+ int i;
+
+ oz_pd_dbg(pd, ON, "%s: (0x%x) pause(%d)\n", __func__, apps, pause);
+ for (i = 0; i < OZ_NB_APPS; i++) {
+ if (g_app_if[i].stop && (apps & (1 << i))) {
+ spin_lock_bh(&g_polling_lock);
+ if (pause) {
+ pd->paused_apps |= (1 << i);
+ } else {
+ pd->total_apps &= ~(1 << i);
+ pd->paused_apps &= ~(1 << i);
+ }
+ spin_unlock_bh(&g_polling_lock);
+ g_app_if[i].stop(pd, pause);
+ }
+ }
+}
+
+/*
+ * Context: softirq
+ */
+void oz_pd_heartbeat(struct oz_pd *pd, u16 apps)
+{
+ int i, more = 0;
+
+ for (i = 0; i < OZ_NB_APPS; i++) {
+ if (g_app_if[i].heartbeat && (apps & (1 << i))) {
+ if (g_app_if[i].heartbeat(pd))
+ more = 1;
+ }
+ }
+ if ((!more) && (hrtimer_active(&pd->heartbeat)))
+ hrtimer_cancel(&pd->heartbeat);
+ if (pd->mode & OZ_F_ISOC_ANYTIME) {
+ int count = 8;
+
+ while (count-- && (oz_send_isoc_frame(pd) >= 0))
+ ;
+ }
+}
+
+/*
+ * Context: softirq or process
+ */
+void oz_pd_stop(struct oz_pd *pd)
+{
+ u16 stop_apps;
+
+ oz_dbg(ON, "oz_pd_stop() State = 0x%x\n", pd->state);
+ oz_pd_indicate_farewells(pd);
+ spin_lock_bh(&g_polling_lock);
+ stop_apps = pd->total_apps;
+ pd->total_apps = 0;
+ pd->paused_apps = 0;
+ spin_unlock_bh(&g_polling_lock);
+ oz_services_stop(pd, stop_apps, 0);
+ spin_lock_bh(&g_polling_lock);
+ oz_pd_set_state(pd, OZ_PD_S_STOPPED);
+ /* Remove from PD list.*/
+ list_del(&pd->link);
+ spin_unlock_bh(&g_polling_lock);
+ oz_dbg(ON, "pd ref count = %d\n", atomic_read(&pd->ref_count));
+ oz_pd_put(pd);
+}
+
+/*
+ * Context: softirq
+ */
+int oz_pd_sleep(struct oz_pd *pd)
+{
+ int do_stop = 0;
+ u16 stop_apps;
+
+ spin_lock_bh(&g_polling_lock);
+ if (pd->state & (OZ_PD_S_SLEEP | OZ_PD_S_STOPPED)) {
+ spin_unlock_bh(&g_polling_lock);
+ return 0;
+ }
+ if (pd->keep_alive && pd->session_id)
+ oz_pd_set_state(pd, OZ_PD_S_SLEEP);
+ else
+ do_stop = 1;
+
+ stop_apps = pd->total_apps;
+ spin_unlock_bh(&g_polling_lock);
+ if (do_stop) {
+ oz_pd_stop(pd);
+ } else {
+ oz_services_stop(pd, stop_apps, 1);
+ oz_timer_add(pd, OZ_TIMER_STOP, pd->keep_alive);
+ }
+ return do_stop;
+}
+
+/*
+ * Context: softirq
+ */
+static struct oz_tx_frame *oz_tx_frame_alloc(struct oz_pd *pd)
+{
+ struct oz_tx_frame *f;
+
+ f = kmem_cache_alloc(oz_tx_frame_cache, GFP_ATOMIC);
+ if (f) {
+ f->total_size = sizeof(struct oz_hdr);
+ INIT_LIST_HEAD(&f->link);
+ INIT_LIST_HEAD(&f->elt_list);
+ }
+ return f;
+}
+
+/*
+ * Context: softirq or process
+ */
+static void oz_tx_isoc_free(struct oz_pd *pd, struct oz_tx_frame *f)
+{
+ pd->nb_queued_isoc_frames--;
+ list_del_init(&f->link);
+
+ kmem_cache_free(oz_tx_frame_cache, f);
+
+ oz_dbg(TX_FRAMES, "Releasing ISOC Frame isoc_nb= %d\n",
+ pd->nb_queued_isoc_frames);
+}
+
+/*
+ * Context: softirq or process
+ */
+static void oz_tx_frame_free(struct oz_pd *pd, struct oz_tx_frame *f)
+{
+ kmem_cache_free(oz_tx_frame_cache, f);
+}
+
+/*
+ * Context: softirq-serialized
+ */
+static void oz_set_more_bit(struct sk_buff *skb)
+{
+ struct oz_hdr *oz_hdr = (struct oz_hdr *)skb_network_header(skb);
+
+ oz_hdr->control |= OZ_F_MORE_DATA;
+}
+
+/*
+ * Context: softirq-serialized
+ */
+static void oz_set_last_pkt_nb(struct oz_pd *pd, struct sk_buff *skb)
+{
+ struct oz_hdr *oz_hdr = (struct oz_hdr *)skb_network_header(skb);
+
+ oz_hdr->last_pkt_num = pd->trigger_pkt_num & OZ_LAST_PN_MASK;
+}
+
+/*
+ * Context: softirq
+ */
+int oz_prepare_frame(struct oz_pd *pd, int empty)
+{
+ struct oz_tx_frame *f;
+
+ if ((pd->mode & OZ_MODE_MASK) != OZ_MODE_TRIGGERED)
+ return -1;
+ if (pd->nb_queued_frames >= OZ_MAX_QUEUED_FRAMES)
+ return -1;
+ if (!empty && !oz_are_elts_available(&pd->elt_buff))
+ return -1;
+ f = oz_tx_frame_alloc(pd);
+ if (f == NULL)
+ return -1;
+ f->skb = NULL;
+ f->hdr.control =
+ (OZ_PROTOCOL_VERSION<<OZ_VERSION_SHIFT) | OZ_F_ACK_REQUESTED;
+ ++pd->last_tx_pkt_num;
+ put_unaligned(cpu_to_le32(pd->last_tx_pkt_num), &f->hdr.pkt_num);
+ if (empty == 0) {
+ oz_select_elts_for_tx(&pd->elt_buff, 0, &f->total_size,
+ pd->max_tx_size, &f->elt_list);
+ }
+ spin_lock(&pd->tx_frame_lock);
+ list_add_tail(&f->link, &pd->tx_queue);
+ pd->nb_queued_frames++;
+ spin_unlock(&pd->tx_frame_lock);
+ return 0;
+}
+
+/*
+ * Context: softirq-serialized
+ */
+static struct sk_buff *oz_build_frame(struct oz_pd *pd, struct oz_tx_frame *f)
+{
+ struct sk_buff *skb;
+ struct net_device *dev = pd->net_dev;
+ struct oz_hdr *oz_hdr;
+ struct oz_elt *elt;
+ struct oz_elt_info *ei;
+
+ /* Allocate skb with enough space for the lower layers as well
+ * as the space we need.
+ */
+ skb = alloc_skb(f->total_size + OZ_ALLOCATED_SPACE(dev), GFP_ATOMIC);
+ if (skb == NULL)
+ return NULL;
+ /* Reserve the head room for lower layers.
+ */
+ skb_reserve(skb, LL_RESERVED_SPACE(dev));
+ skb_reset_network_header(skb);
+ skb->dev = dev;
+ skb->protocol = htons(OZ_ETHERTYPE);
+ if (dev_hard_header(skb, dev, OZ_ETHERTYPE, pd->mac_addr,
+ dev->dev_addr, skb->len) < 0)
+ goto fail;
+ /* Push the tail to the end of the area we are going to copy to.
+ */
+ oz_hdr = (struct oz_hdr *)skb_put(skb, f->total_size);
+ f->hdr.last_pkt_num = pd->trigger_pkt_num & OZ_LAST_PN_MASK;
+ memcpy(oz_hdr, &f->hdr, sizeof(struct oz_hdr));
+ /* Copy the elements into the frame body.
+ */
+ elt = (struct oz_elt *)(oz_hdr+1);
+ list_for_each_entry(ei, &f->elt_list, link) {
+ memcpy(elt, ei->data, ei->length);
+ elt = oz_next_elt(elt);
+ }
+ return skb;
+fail:
+ kfree_skb(skb);
+ return NULL;
+}
+
+/*
+ * Context: softirq or process
+ */
+static void oz_retire_frame(struct oz_pd *pd, struct oz_tx_frame *f)
+{
+ struct oz_elt_info *ei, *n;
+
+ list_for_each_entry_safe(ei, n, &f->elt_list, link) {
+ list_del_init(&ei->link);
+ if (ei->callback)
+ ei->callback(pd, ei->context);
+ spin_lock_bh(&pd->elt_buff.lock);
+ oz_elt_info_free(&pd->elt_buff, ei);
+ spin_unlock_bh(&pd->elt_buff.lock);
+ }
+ oz_tx_frame_free(pd, f);
+}
+
+/*
+ * Context: softirq-serialized
+ */
+static int oz_send_next_queued_frame(struct oz_pd *pd, int more_data)
+{
+ struct sk_buff *skb;
+ struct oz_tx_frame *f;
+ struct list_head *e;
+
+ spin_lock(&pd->tx_frame_lock);
+ e = pd->last_sent_frame->next;
+ if (e == &pd->tx_queue) {
+ spin_unlock(&pd->tx_frame_lock);
+ return -1;
+ }
+ f = list_entry(e, struct oz_tx_frame, link);
+
+ if (f->skb != NULL) {
+ skb = f->skb;
+ oz_tx_isoc_free(pd, f);
+ spin_unlock(&pd->tx_frame_lock);
+ if (more_data)
+ oz_set_more_bit(skb);
+ oz_set_last_pkt_nb(pd, skb);
+ if ((int)atomic_read(&g_submitted_isoc) <
+ OZ_MAX_SUBMITTED_ISOC) {
+ if (dev_queue_xmit(skb) < 0) {
+ oz_dbg(TX_FRAMES, "Dropping ISOC Frame\n");
+ return -1;
+ }
+ atomic_inc(&g_submitted_isoc);
+ oz_dbg(TX_FRAMES, "Sending ISOC Frame, nb_isoc= %d\n",
+ pd->nb_queued_isoc_frames);
+ return 0;
+ }
+ kfree_skb(skb);
+ oz_dbg(TX_FRAMES, "Dropping ISOC Frame>\n");
+ return -1;
+ }
+
+ pd->last_sent_frame = e;
+ skb = oz_build_frame(pd, f);
+ spin_unlock(&pd->tx_frame_lock);
+ if (!skb)
+ return -1;
+ if (more_data)
+ oz_set_more_bit(skb);
+ oz_dbg(TX_FRAMES, "TX frame PN=0x%x\n", f->hdr.pkt_num);
+ if (dev_queue_xmit(skb) < 0)
+ return -1;
+
+ return 0;
+}
+
+/*
+ * Context: softirq-serialized
+ */
+void oz_send_queued_frames(struct oz_pd *pd, int backlog)
+{
+ while (oz_prepare_frame(pd, 0) >= 0)
+ backlog++;
+
+ switch (pd->mode & (OZ_F_ISOC_NO_ELTS | OZ_F_ISOC_ANYTIME)) {
+
+ case OZ_F_ISOC_NO_ELTS: {
+ backlog += pd->nb_queued_isoc_frames;
+ if (backlog <= 0)
+ goto out;
+ if (backlog > OZ_MAX_SUBMITTED_ISOC)
+ backlog = OZ_MAX_SUBMITTED_ISOC;
+ break;
+ }
+ case OZ_NO_ELTS_ANYTIME: {
+ if ((backlog <= 0) && (pd->isoc_sent == 0))
+ goto out;
+ break;
+ }
+ default: {
+ if (backlog <= 0)
+ goto out;
+ break;
+ }
+ }
+ while (backlog--) {
+ if (oz_send_next_queued_frame(pd, backlog) < 0)
+ break;
+ }
+ return;
+
+out: oz_prepare_frame(pd, 1);
+ oz_send_next_queued_frame(pd, 0);
+}
+
+/*
+ * Context: softirq
+ */
+static int oz_send_isoc_frame(struct oz_pd *pd)
+{
+ struct sk_buff *skb;
+ struct net_device *dev = pd->net_dev;
+ struct oz_hdr *oz_hdr;
+ struct oz_elt *elt;
+ struct oz_elt_info *ei;
+ LIST_HEAD(list);
+ int total_size = sizeof(struct oz_hdr);
+
+ oz_select_elts_for_tx(&pd->elt_buff, 1, &total_size,
+ pd->max_tx_size, &list);
+ if (list_empty(&list))
+ return 0;
+ skb = alloc_skb(total_size + OZ_ALLOCATED_SPACE(dev), GFP_ATOMIC);
+ if (skb == NULL) {
+ oz_dbg(ON, "Cannot alloc skb\n");
+ oz_elt_info_free_chain(&pd->elt_buff, &list);
+ return -1;
+ }
+ skb_reserve(skb, LL_RESERVED_SPACE(dev));
+ skb_reset_network_header(skb);
+ skb->dev = dev;
+ skb->protocol = htons(OZ_ETHERTYPE);
+ if (dev_hard_header(skb, dev, OZ_ETHERTYPE, pd->mac_addr,
+ dev->dev_addr, skb->len) < 0) {
+ kfree_skb(skb);
+ return -1;
+ }
+ oz_hdr = (struct oz_hdr *)skb_put(skb, total_size);
+ oz_hdr->control = (OZ_PROTOCOL_VERSION<<OZ_VERSION_SHIFT) | OZ_F_ISOC;
+ oz_hdr->last_pkt_num = pd->trigger_pkt_num & OZ_LAST_PN_MASK;
+ elt = (struct oz_elt *)(oz_hdr+1);
+
+ list_for_each_entry(ei, &list, link) {
+ memcpy(elt, ei->data, ei->length);
+ elt = oz_next_elt(elt);
+ }
+ dev_queue_xmit(skb);
+ oz_elt_info_free_chain(&pd->elt_buff, &list);
+ return 0;
+}
+
+/*
+ * Context: softirq-serialized
+ */
+void oz_retire_tx_frames(struct oz_pd *pd, u8 lpn)
+{
+ struct oz_tx_frame *f, *tmp = NULL;
+ u8 diff;
+ u32 pkt_num;
+
+ LIST_HEAD(list);
+
+ spin_lock(&pd->tx_frame_lock);
+ list_for_each_entry(f, &pd->tx_queue, link) {
+ pkt_num = le32_to_cpu(get_unaligned(&f->hdr.pkt_num));
+ diff = (lpn - (pkt_num & OZ_LAST_PN_MASK)) & OZ_LAST_PN_MASK;
+ if ((diff > OZ_LAST_PN_HALF_CYCLE) || (pkt_num == 0))
+ break;
+ oz_dbg(TX_FRAMES, "Releasing pkt_num= %u, nb= %d\n",
+ pkt_num, pd->nb_queued_frames);
+ tmp = f;
+ pd->nb_queued_frames--;
+ }
+ if (tmp)
+ list_cut_position(&list, &pd->tx_queue, &tmp->link);
+ pd->last_sent_frame = &pd->tx_queue;
+ spin_unlock(&pd->tx_frame_lock);
+
+ list_for_each_entry_safe(f, tmp, &list, link)
+ oz_retire_frame(pd, f);
+}
+
+/*
+ * Precondition: stream_lock must be held.
+ * Context: softirq
+ */
+static struct oz_isoc_stream *pd_stream_find(struct oz_pd *pd, u8 ep_num)
+{
+ struct oz_isoc_stream *st;
+
+ list_for_each_entry(st, &pd->stream_list, link) {
+ if (st->ep_num == ep_num)
+ return st;
+ }
+ return NULL;
+}
+
+/*
+ * Context: softirq
+ */
+int oz_isoc_stream_create(struct oz_pd *pd, u8 ep_num)
+{
+ struct oz_isoc_stream *st;
+
+ st = kzalloc(sizeof(struct oz_isoc_stream), GFP_ATOMIC);
+ if (!st)
+ return -ENOMEM;
+ st->ep_num = ep_num;
+ spin_lock_bh(&pd->stream_lock);
+ if (!pd_stream_find(pd, ep_num)) {
+ list_add(&st->link, &pd->stream_list);
+ st = NULL;
+ }
+ spin_unlock_bh(&pd->stream_lock);
+ kfree(st);
+ return 0;
+}
+
+/*
+ * Context: softirq or process
+ */
+static void oz_isoc_stream_free(struct oz_isoc_stream *st)
+{
+ kfree_skb(st->skb);
+ kfree(st);
+}
+
+/*
+ * Context: softirq
+ */
+int oz_isoc_stream_delete(struct oz_pd *pd, u8 ep_num)
+{
+ struct oz_isoc_stream *st;
+
+ spin_lock_bh(&pd->stream_lock);
+ st = pd_stream_find(pd, ep_num);
+ if (st)
+ list_del(&st->link);
+ spin_unlock_bh(&pd->stream_lock);
+ if (st)
+ oz_isoc_stream_free(st);
+ return 0;
+}
+
+/*
+ * Context: any
+ */
+static void oz_isoc_destructor(struct sk_buff *skb)
+{
+ atomic_dec(&g_submitted_isoc);
+}
+
+/*
+ * Context: softirq
+ */
+int oz_send_isoc_unit(struct oz_pd *pd, u8 ep_num, const u8 *data, int len)
+{
+ struct net_device *dev = pd->net_dev;
+ struct oz_isoc_stream *st;
+ u8 nb_units = 0;
+ struct sk_buff *skb = NULL;
+ struct oz_hdr *oz_hdr = NULL;
+ int size = 0;
+
+ spin_lock_bh(&pd->stream_lock);
+ st = pd_stream_find(pd, ep_num);
+ if (st) {
+ skb = st->skb;
+ st->skb = NULL;
+ nb_units = st->nb_units;
+ st->nb_units = 0;
+ oz_hdr = st->oz_hdr;
+ size = st->size;
+ }
+ spin_unlock_bh(&pd->stream_lock);
+ if (!st)
+ return 0;
+ if (!skb) {
+ /* Allocate enough space for max size frame. */
+ skb = alloc_skb(pd->max_tx_size + OZ_ALLOCATED_SPACE(dev),
+ GFP_ATOMIC);
+ if (skb == NULL)
+ return 0;
+ /* Reserve the head room for lower layers. */
+ skb_reserve(skb, LL_RESERVED_SPACE(dev));
+ skb_reset_network_header(skb);
+ skb->dev = dev;
+ skb->protocol = htons(OZ_ETHERTYPE);
+ /* For audio packet set priority to AC_VO */
+ skb->priority = 0x7;
+ size = sizeof(struct oz_hdr) + sizeof(struct oz_isoc_large);
+ oz_hdr = (struct oz_hdr *)skb_put(skb, size);
+ }
+ memcpy(skb_put(skb, len), data, len);
+ size += len;
+ if (++nb_units < pd->ms_per_isoc) {
+ spin_lock_bh(&pd->stream_lock);
+ st->skb = skb;
+ st->nb_units = nb_units;
+ st->oz_hdr = oz_hdr;
+ st->size = size;
+ spin_unlock_bh(&pd->stream_lock);
+ } else {
+ struct oz_hdr oz;
+ struct oz_isoc_large iso;
+
+ spin_lock_bh(&pd->stream_lock);
+ iso.frame_number = st->frame_num;
+ st->frame_num += nb_units;
+ spin_unlock_bh(&pd->stream_lock);
+ oz.control =
+ (OZ_PROTOCOL_VERSION<<OZ_VERSION_SHIFT) | OZ_F_ISOC;
+ oz.last_pkt_num = pd->trigger_pkt_num & OZ_LAST_PN_MASK;
+ oz.pkt_num = 0;
+ iso.endpoint = ep_num;
+ iso.format = OZ_DATA_F_ISOC_LARGE;
+ iso.ms_data = nb_units;
+ memcpy(oz_hdr, &oz, sizeof(oz));
+ memcpy(oz_hdr+1, &iso, sizeof(iso));
+ if (dev_hard_header(skb, dev, OZ_ETHERTYPE, pd->mac_addr,
+ dev->dev_addr, skb->len) < 0)
+ goto out;
+
+ skb->destructor = oz_isoc_destructor;
+ /*Queue for Xmit if mode is not ANYTIME*/
+ if (!(pd->mode & OZ_F_ISOC_ANYTIME)) {
+ struct oz_tx_frame *isoc_unit = NULL;
+ int nb = pd->nb_queued_isoc_frames;
+
+ if (nb >= pd->isoc_latency) {
+ struct oz_tx_frame *f;
+
+ oz_dbg(TX_FRAMES, "Dropping ISOC Unit nb= %d\n",
+ nb);
+ spin_lock(&pd->tx_frame_lock);
+ list_for_each_entry(f, &pd->tx_queue, link) {
+ if (f->skb != NULL) {
+ oz_tx_isoc_free(pd, f);
+ break;
+ }
+ }
+ spin_unlock(&pd->tx_frame_lock);
+ }
+ isoc_unit = oz_tx_frame_alloc(pd);
+ if (isoc_unit == NULL)
+ goto out;
+ isoc_unit->hdr = oz;
+ isoc_unit->skb = skb;
+ spin_lock_bh(&pd->tx_frame_lock);
+ list_add_tail(&isoc_unit->link, &pd->tx_queue);
+ pd->nb_queued_isoc_frames++;
+ spin_unlock_bh(&pd->tx_frame_lock);
+ oz_dbg(TX_FRAMES,
+ "Added ISOC Frame to Tx Queue isoc_nb= %d, nb= %d\n",
+ pd->nb_queued_isoc_frames, pd->nb_queued_frames);
+ return 0;
+ }
+
+ /*In ANYTIME mode Xmit unit immediately*/
+ if (atomic_read(&g_submitted_isoc) < OZ_MAX_SUBMITTED_ISOC) {
+ atomic_inc(&g_submitted_isoc);
+ if (dev_queue_xmit(skb) < 0)
+ return -1;
+ return 0;
+ }
+
+out: kfree_skb(skb);
+ return -1;
+
+ }
+ return 0;
+}
+
+/*
+ * Context: process
+ */
+void oz_apps_init(void)
+{
+ int i;
+
+ for (i = 0; i < OZ_NB_APPS; i++) {
+ if (g_app_if[i].init)
+ g_app_if[i].init();
+ }
+}
+
+/*
+ * Context: process
+ */
+void oz_apps_term(void)
+{
+ int i;
+
+ /* Terminate all the apps. */
+ for (i = 0; i < OZ_NB_APPS; i++) {
+ if (g_app_if[i].term)
+ g_app_if[i].term();
+ }
+}
+
+/*
+ * Context: softirq-serialized
+ */
+void oz_handle_app_elt(struct oz_pd *pd, u8 app_id, struct oz_elt *elt)
+{
+ if (app_id < OZ_NB_APPS && g_app_if[app_id].rx)
+ g_app_if[app_id].rx(pd, elt);
+}
+
+/*
+ * Context: softirq or process
+ */
+void oz_pd_indicate_farewells(struct oz_pd *pd)
+{
+ struct oz_farewell *f;
+ const struct oz_app_if *ai = &g_app_if[OZ_APPID_USB];
+
+ while (1) {
+ spin_lock_bh(&g_polling_lock);
+ if (list_empty(&pd->farewell_list)) {
+ spin_unlock_bh(&g_polling_lock);
+ break;
+ }
+ f = list_first_entry(&pd->farewell_list,
+ struct oz_farewell, link);
+ list_del(&f->link);
+ spin_unlock_bh(&g_polling_lock);
+ if (ai->farewell)
+ ai->farewell(pd, f->ep_num, f->report, f->len);
+ kfree(f);
+ }
+}
diff --git a/drivers/staging/ozwpan/ozpd.h b/drivers/staging/ozwpan/ozpd.h
new file mode 100644
index 000000000..212fab0d8
--- /dev/null
+++ b/drivers/staging/ozwpan/ozpd.h
@@ -0,0 +1,134 @@
+/* -----------------------------------------------------------------------------
+ * Copyright (c) 2011 Ozmo Inc
+ * Released under the GNU General Public License Version 2 (GPLv2).
+ * -----------------------------------------------------------------------------
+ */
+#ifndef _OZPD_H_
+#define _OZPD_H_
+
+#include <linux/interrupt.h>
+#include "ozeltbuf.h"
+
+/* PD state
+ */
+#define OZ_PD_S_IDLE 0x1
+#define OZ_PD_S_CONNECTED 0x2
+#define OZ_PD_S_SLEEP 0x4
+#define OZ_PD_S_STOPPED 0x8
+
+/* Timer event types.
+ */
+#define OZ_TIMER_TOUT 1
+#define OZ_TIMER_HEARTBEAT 2
+#define OZ_TIMER_STOP 3
+
+/*
+ *External spinlock variable
+ */
+extern spinlock_t g_polling_lock;
+
+/* Data structure that hold information on a frame for transmisson. This is
+ * built when the frame is first transmitted and is used to rebuild the frame
+ * if a re-transmission is required.
+ */
+struct oz_tx_frame {
+ struct list_head link;
+ struct list_head elt_list;
+ struct oz_hdr hdr;
+ struct sk_buff *skb;
+ int total_size;
+};
+
+struct oz_isoc_stream {
+ struct list_head link;
+ u8 ep_num;
+ u8 frame_num;
+ u8 nb_units;
+ int size;
+ struct sk_buff *skb;
+ struct oz_hdr *oz_hdr;
+};
+
+struct oz_farewell {
+ struct list_head link;
+ u8 ep_num;
+ u8 index;
+ u8 len;
+ u8 report[0];
+};
+
+/* Data structure that holds information on a specific peripheral device (PD).
+ */
+struct oz_pd {
+ struct list_head link;
+ atomic_t ref_count;
+ u8 mac_addr[ETH_ALEN];
+ unsigned state;
+ unsigned state_flags;
+ unsigned send_flags;
+ u16 total_apps;
+ u16 paused_apps;
+ u8 session_id;
+ u8 param_rsp_status;
+ u8 pd_info;
+ u8 isoc_sent;
+ u32 last_rx_pkt_num;
+ u32 last_tx_pkt_num;
+ struct timespec last_rx_timestamp;
+ u32 trigger_pkt_num;
+ unsigned long pulse_time;
+ unsigned long pulse_period;
+ unsigned long presleep;
+ unsigned long keep_alive;
+ struct oz_elt_buf elt_buff;
+ void *app_ctx[OZ_NB_APPS];
+ spinlock_t app_lock[OZ_NB_APPS];
+ int max_tx_size;
+ u8 mode;
+ u8 ms_per_isoc;
+ unsigned isoc_latency;
+ unsigned max_stream_buffering;
+ int nb_queued_frames;
+ int nb_queued_isoc_frames;
+ spinlock_t tx_frame_lock;
+ struct list_head *last_sent_frame;
+ struct list_head tx_queue;
+ struct list_head farewell_list;
+ spinlock_t stream_lock;
+ struct list_head stream_list;
+ struct net_device *net_dev;
+ struct hrtimer heartbeat;
+ struct hrtimer timeout;
+ u8 timeout_type;
+ struct tasklet_struct heartbeat_tasklet;
+ struct tasklet_struct timeout_tasklet;
+ struct work_struct workitem;
+};
+
+#define OZ_MAX_QUEUED_FRAMES 4
+
+struct oz_pd *oz_pd_alloc(const u8 *mac_addr);
+void oz_pd_destroy(struct oz_pd *pd);
+void oz_pd_get(struct oz_pd *pd);
+void oz_pd_put(struct oz_pd *pd);
+void oz_pd_set_state(struct oz_pd *pd, unsigned state);
+void oz_pd_indicate_farewells(struct oz_pd *pd);
+int oz_pd_sleep(struct oz_pd *pd);
+void oz_pd_stop(struct oz_pd *pd);
+void oz_pd_heartbeat(struct oz_pd *pd, u16 apps);
+int oz_services_start(struct oz_pd *pd, u16 apps, int resume);
+void oz_services_stop(struct oz_pd *pd, u16 apps, int pause);
+int oz_prepare_frame(struct oz_pd *pd, int empty);
+void oz_send_queued_frames(struct oz_pd *pd, int backlog);
+void oz_retire_tx_frames(struct oz_pd *pd, u8 lpn);
+int oz_isoc_stream_create(struct oz_pd *pd, u8 ep_num);
+int oz_isoc_stream_delete(struct oz_pd *pd, u8 ep_num);
+int oz_send_isoc_unit(struct oz_pd *pd, u8 ep_num, const u8 *data, int len);
+void oz_handle_app_elt(struct oz_pd *pd, u8 app_id, struct oz_elt *elt);
+void oz_apps_init(void);
+void oz_apps_term(void);
+
+extern struct kmem_cache *oz_elt_info_cache;
+extern struct kmem_cache *oz_tx_frame_cache;
+
+#endif /* Sentry */
diff --git a/drivers/staging/ozwpan/ozproto.c b/drivers/staging/ozwpan/ozproto.c
new file mode 100644
index 000000000..1ba24a2ae
--- /dev/null
+++ b/drivers/staging/ozwpan/ozproto.c
@@ -0,0 +1,813 @@
+/* -----------------------------------------------------------------------------
+ * Copyright (c) 2011 Ozmo Inc
+ * Released under the GNU General Public License Version 2 (GPLv2).
+ * -----------------------------------------------------------------------------
+ */
+
+#include <linux/module.h>
+#include <linux/timer.h>
+#include <linux/sched.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/errno.h>
+#include <linux/ieee80211.h>
+#include <linux/slab.h>
+#include "ozdbg.h"
+#include "ozprotocol.h"
+#include "ozeltbuf.h"
+#include "ozpd.h"
+#include "ozproto.h"
+#include "ozusbsvc.h"
+
+#include "ozappif.h"
+#include <asm/unaligned.h>
+#include <linux/uaccess.h>
+#include <net/psnap.h>
+
+#define OZ_CF_CONN_SUCCESS 1
+#define OZ_CF_CONN_FAILURE 2
+
+#define OZ_DO_STOP 1
+#define OZ_DO_SLEEP 2
+
+struct oz_binding {
+ struct packet_type ptype;
+ char name[OZ_MAX_BINDING_LEN];
+ struct list_head link;
+};
+
+/*
+ * External variable
+ */
+
+DEFINE_SPINLOCK(g_polling_lock);
+/*
+ * Static external variables.
+ */
+static LIST_HEAD(g_pd_list);
+static LIST_HEAD(g_binding);
+static DEFINE_SPINLOCK(g_binding_lock);
+static struct sk_buff_head g_rx_queue;
+static u8 g_session_id;
+static u16 g_apps = 0x1;
+static int g_processing_rx;
+
+struct kmem_cache *oz_elt_info_cache;
+struct kmem_cache *oz_tx_frame_cache;
+
+/*
+ * Context: softirq-serialized
+ */
+static u8 oz_get_new_session_id(u8 exclude)
+{
+ if (++g_session_id == 0)
+ g_session_id = 1;
+ if (g_session_id == exclude) {
+ if (++g_session_id == 0)
+ g_session_id = 1;
+ }
+ return g_session_id;
+}
+
+/*
+ * Context: softirq-serialized
+ */
+static void oz_send_conn_rsp(struct oz_pd *pd, u8 status)
+{
+ struct sk_buff *skb;
+ struct net_device *dev = pd->net_dev;
+ struct oz_hdr *oz_hdr;
+ struct oz_elt *elt;
+ struct oz_elt_connect_rsp *body;
+
+ int sz = sizeof(struct oz_hdr) + sizeof(struct oz_elt) +
+ sizeof(struct oz_elt_connect_rsp);
+ skb = alloc_skb(sz + OZ_ALLOCATED_SPACE(dev), GFP_ATOMIC);
+ if (skb == NULL)
+ return;
+ skb_reserve(skb, LL_RESERVED_SPACE(dev));
+ skb_reset_network_header(skb);
+ oz_hdr = (struct oz_hdr *)skb_put(skb, sz);
+ elt = (struct oz_elt *)(oz_hdr+1);
+ body = (struct oz_elt_connect_rsp *)(elt+1);
+ skb->dev = dev;
+ skb->protocol = htons(OZ_ETHERTYPE);
+ /* Fill in device header */
+ if (dev_hard_header(skb, dev, OZ_ETHERTYPE, pd->mac_addr,
+ dev->dev_addr, skb->len) < 0) {
+ kfree_skb(skb);
+ return;
+ }
+ oz_hdr->control = OZ_PROTOCOL_VERSION<<OZ_VERSION_SHIFT;
+ oz_hdr->last_pkt_num = 0;
+ put_unaligned(0, &oz_hdr->pkt_num);
+ elt->type = OZ_ELT_CONNECT_RSP;
+ elt->length = sizeof(struct oz_elt_connect_rsp);
+ memset(body, 0, sizeof(struct oz_elt_connect_rsp));
+ body->status = status;
+ if (status == 0) {
+ body->mode = pd->mode;
+ body->session_id = pd->session_id;
+ put_unaligned(cpu_to_le16(pd->total_apps), &body->apps);
+ }
+ oz_dbg(ON, "TX: OZ_ELT_CONNECT_RSP %d", status);
+ dev_queue_xmit(skb);
+}
+
+/*
+ * Context: softirq-serialized
+ */
+static void pd_set_keepalive(struct oz_pd *pd, u8 kalive)
+{
+ unsigned long keep_alive = kalive & OZ_KALIVE_VALUE_MASK;
+
+ switch (kalive & OZ_KALIVE_TYPE_MASK) {
+ case OZ_KALIVE_SPECIAL:
+ pd->keep_alive = keep_alive * 1000*60*60*24*20;
+ break;
+ case OZ_KALIVE_SECS:
+ pd->keep_alive = keep_alive*1000;
+ break;
+ case OZ_KALIVE_MINS:
+ pd->keep_alive = keep_alive*1000*60;
+ break;
+ case OZ_KALIVE_HOURS:
+ pd->keep_alive = keep_alive*1000*60*60;
+ break;
+ default:
+ pd->keep_alive = 0;
+ }
+ oz_dbg(ON, "Keepalive = %lu mSec\n", pd->keep_alive);
+}
+
+/*
+ * Context: softirq-serialized
+ */
+static void pd_set_presleep(struct oz_pd *pd, u8 presleep, u8 start_timer)
+{
+ if (presleep)
+ pd->presleep = presleep*100;
+ else
+ pd->presleep = OZ_PRESLEEP_TOUT;
+ if (start_timer) {
+ spin_unlock(&g_polling_lock);
+ oz_timer_add(pd, OZ_TIMER_TOUT, pd->presleep);
+ spin_lock(&g_polling_lock);
+ }
+ oz_dbg(ON, "Presleep time = %lu mSec\n", pd->presleep);
+}
+
+/*
+ * Context: softirq-serialized
+ */
+static struct oz_pd *oz_connect_req(struct oz_pd *cur_pd, struct oz_elt *elt,
+ const u8 *pd_addr, struct net_device *net_dev)
+{
+ struct oz_pd *pd;
+ struct oz_elt_connect_req *body =
+ (struct oz_elt_connect_req *)(elt+1);
+ u8 rsp_status = OZ_STATUS_SUCCESS;
+ u8 stop_needed = 0;
+ u16 new_apps = g_apps;
+ struct net_device *old_net_dev = NULL;
+ struct oz_pd *free_pd = NULL;
+
+ if (cur_pd) {
+ pd = cur_pd;
+ spin_lock_bh(&g_polling_lock);
+ } else {
+ struct oz_pd *pd2 = NULL;
+ struct list_head *e;
+
+ pd = oz_pd_alloc(pd_addr);
+ if (pd == NULL)
+ return NULL;
+ getnstimeofday(&pd->last_rx_timestamp);
+ spin_lock_bh(&g_polling_lock);
+ list_for_each(e, &g_pd_list) {
+ pd2 = list_entry(e, struct oz_pd, link);
+ if (ether_addr_equal(pd2->mac_addr, pd_addr)) {
+ free_pd = pd;
+ pd = pd2;
+ break;
+ }
+ }
+ if (pd != pd2)
+ list_add_tail(&pd->link, &g_pd_list);
+ }
+ if (pd == NULL) {
+ spin_unlock_bh(&g_polling_lock);
+ return NULL;
+ }
+ if (pd->net_dev != net_dev) {
+ old_net_dev = pd->net_dev;
+ dev_hold(net_dev);
+ pd->net_dev = net_dev;
+ }
+ oz_dbg(ON, "Host vendor: %d\n", body->host_vendor);
+ pd->max_tx_size = OZ_MAX_TX_SIZE;
+ pd->mode = body->mode;
+ pd->pd_info = body->pd_info;
+ if (pd->mode & OZ_F_ISOC_NO_ELTS) {
+ pd->ms_per_isoc = body->ms_per_isoc;
+ if (!pd->ms_per_isoc)
+ pd->ms_per_isoc = 4;
+
+ switch (body->ms_isoc_latency & OZ_LATENCY_MASK) {
+ case OZ_ONE_MS_LATENCY:
+ pd->isoc_latency = (body->ms_isoc_latency &
+ ~OZ_LATENCY_MASK) / pd->ms_per_isoc;
+ break;
+ case OZ_TEN_MS_LATENCY:
+ pd->isoc_latency = ((body->ms_isoc_latency &
+ ~OZ_LATENCY_MASK) * 10) / pd->ms_per_isoc;
+ break;
+ default:
+ pd->isoc_latency = OZ_MAX_TX_QUEUE_ISOC;
+ }
+ }
+ if (body->max_len_div16)
+ pd->max_tx_size = ((u16)body->max_len_div16)<<4;
+ oz_dbg(ON, "Max frame:%u Ms per isoc:%u\n",
+ pd->max_tx_size, pd->ms_per_isoc);
+ pd->max_stream_buffering = 3*1024;
+ pd->pulse_period = OZ_QUANTUM;
+ pd_set_presleep(pd, body->presleep, 0);
+ pd_set_keepalive(pd, body->keep_alive);
+
+ new_apps &= le16_to_cpu(get_unaligned(&body->apps));
+ if ((new_apps & 0x1) && (body->session_id)) {
+ if (pd->session_id) {
+ if (pd->session_id != body->session_id) {
+ rsp_status = OZ_STATUS_SESSION_MISMATCH;
+ goto done;
+ }
+ } else {
+ new_apps &= ~0x1; /* Resume not permitted */
+ pd->session_id =
+ oz_get_new_session_id(body->session_id);
+ }
+ } else {
+ if (pd->session_id && !body->session_id) {
+ rsp_status = OZ_STATUS_SESSION_TEARDOWN;
+ stop_needed = 1;
+ } else {
+ new_apps &= ~0x1; /* Resume not permitted */
+ pd->session_id =
+ oz_get_new_session_id(body->session_id);
+ }
+ }
+done:
+ if (rsp_status == OZ_STATUS_SUCCESS) {
+ u16 start_apps = new_apps & ~pd->total_apps & ~0x1;
+ u16 stop_apps = pd->total_apps & ~new_apps & ~0x1;
+ u16 resume_apps = new_apps & pd->paused_apps & ~0x1;
+
+ spin_unlock_bh(&g_polling_lock);
+ oz_pd_set_state(pd, OZ_PD_S_CONNECTED);
+ oz_dbg(ON, "new_apps=0x%x total_apps=0x%x paused_apps=0x%x\n",
+ new_apps, pd->total_apps, pd->paused_apps);
+ if (start_apps) {
+ if (oz_services_start(pd, start_apps, 0))
+ rsp_status = OZ_STATUS_TOO_MANY_PDS;
+ }
+ if (resume_apps)
+ if (oz_services_start(pd, resume_apps, 1))
+ rsp_status = OZ_STATUS_TOO_MANY_PDS;
+ if (stop_apps)
+ oz_services_stop(pd, stop_apps, 0);
+ oz_pd_request_heartbeat(pd);
+ } else {
+ spin_unlock_bh(&g_polling_lock);
+ }
+ oz_send_conn_rsp(pd, rsp_status);
+ if (rsp_status != OZ_STATUS_SUCCESS) {
+ if (stop_needed)
+ oz_pd_stop(pd);
+ oz_pd_put(pd);
+ pd = NULL;
+ }
+ if (old_net_dev)
+ dev_put(old_net_dev);
+ if (free_pd)
+ oz_pd_destroy(free_pd);
+ return pd;
+}
+
+/*
+ * Context: softirq-serialized
+ */
+static void oz_add_farewell(struct oz_pd *pd, u8 ep_num, u8 index,
+ const u8 *report, u8 len)
+{
+ struct oz_farewell *f;
+ struct oz_farewell *f2;
+ int found = 0;
+
+ f = kmalloc(sizeof(struct oz_farewell) + len, GFP_ATOMIC);
+ if (!f)
+ return;
+ f->ep_num = ep_num;
+ f->index = index;
+ f->len = len;
+ memcpy(f->report, report, len);
+ oz_dbg(ON, "RX: Adding farewell report\n");
+ spin_lock(&g_polling_lock);
+ list_for_each_entry(f2, &pd->farewell_list, link) {
+ if ((f2->ep_num == ep_num) && (f2->index == index)) {
+ found = 1;
+ list_del(&f2->link);
+ break;
+ }
+ }
+ list_add_tail(&f->link, &pd->farewell_list);
+ spin_unlock(&g_polling_lock);
+ if (found)
+ kfree(f2);
+}
+
+/*
+ * Context: softirq-serialized
+ */
+static void oz_rx_frame(struct sk_buff *skb)
+{
+ u8 *mac_hdr;
+ u8 *src_addr;
+ struct oz_elt *elt;
+ int length;
+ struct oz_pd *pd = NULL;
+ struct oz_hdr *oz_hdr = (struct oz_hdr *)skb_network_header(skb);
+ struct timespec current_time;
+ int dup = 0;
+ u32 pkt_num;
+
+ oz_dbg(RX_FRAMES, "RX frame PN=0x%x LPN=0x%x control=0x%x\n",
+ oz_hdr->pkt_num, oz_hdr->last_pkt_num, oz_hdr->control);
+ mac_hdr = skb_mac_header(skb);
+ src_addr = &mac_hdr[ETH_ALEN];
+ length = skb->len;
+
+ /* Check the version field */
+ if (oz_get_prot_ver(oz_hdr->control) != OZ_PROTOCOL_VERSION) {
+ oz_dbg(ON, "Incorrect protocol version: %d\n",
+ oz_get_prot_ver(oz_hdr->control));
+ goto done;
+ }
+
+ pkt_num = le32_to_cpu(get_unaligned(&oz_hdr->pkt_num));
+
+ pd = oz_pd_find(src_addr);
+ if (pd) {
+ if (!(pd->state & OZ_PD_S_CONNECTED))
+ oz_pd_set_state(pd, OZ_PD_S_CONNECTED);
+ getnstimeofday(&current_time);
+ if ((current_time.tv_sec != pd->last_rx_timestamp.tv_sec) ||
+ (pd->presleep < MSEC_PER_SEC)) {
+ oz_timer_add(pd, OZ_TIMER_TOUT, pd->presleep);
+ pd->last_rx_timestamp = current_time;
+ }
+ if (pkt_num != pd->last_rx_pkt_num) {
+ pd->last_rx_pkt_num = pkt_num;
+ } else {
+ dup = 1;
+ oz_dbg(ON, "Duplicate frame\n");
+ }
+ }
+
+ if (pd && !dup && ((pd->mode & OZ_MODE_MASK) == OZ_MODE_TRIGGERED)) {
+ oz_dbg(RX_FRAMES, "Received TRIGGER Frame\n");
+ pd->last_sent_frame = &pd->tx_queue;
+ if (oz_hdr->control & OZ_F_ACK) {
+ /* Retire completed frames */
+ oz_retire_tx_frames(pd, oz_hdr->last_pkt_num);
+ }
+ if ((oz_hdr->control & OZ_F_ACK_REQUESTED) &&
+ (pd->state == OZ_PD_S_CONNECTED)) {
+ int backlog = pd->nb_queued_frames;
+
+ pd->trigger_pkt_num = pkt_num;
+ /* Send queued frames */
+ oz_send_queued_frames(pd, backlog);
+ }
+ }
+
+ length -= sizeof(struct oz_hdr);
+ elt = (struct oz_elt *)((u8 *)oz_hdr + sizeof(struct oz_hdr));
+
+ while (length >= sizeof(struct oz_elt)) {
+ length -= sizeof(struct oz_elt) + elt->length;
+ if (length < 0)
+ break;
+ switch (elt->type) {
+ case OZ_ELT_CONNECT_REQ:
+ oz_dbg(ON, "RX: OZ_ELT_CONNECT_REQ\n");
+ pd = oz_connect_req(pd, elt, src_addr, skb->dev);
+ break;
+ case OZ_ELT_DISCONNECT:
+ oz_dbg(ON, "RX: OZ_ELT_DISCONNECT\n");
+ if (pd)
+ oz_pd_sleep(pd);
+ break;
+ case OZ_ELT_UPDATE_PARAM_REQ: {
+ struct oz_elt_update_param *body =
+ (struct oz_elt_update_param *)(elt + 1);
+ oz_dbg(ON, "RX: OZ_ELT_UPDATE_PARAM_REQ\n");
+ if (pd && (pd->state & OZ_PD_S_CONNECTED)) {
+ spin_lock(&g_polling_lock);
+ pd_set_keepalive(pd, body->keepalive);
+ pd_set_presleep(pd, body->presleep, 1);
+ spin_unlock(&g_polling_lock);
+ }
+ }
+ break;
+ case OZ_ELT_FAREWELL_REQ: {
+ struct oz_elt_farewell *body =
+ (struct oz_elt_farewell *)(elt + 1);
+ oz_dbg(ON, "RX: OZ_ELT_FAREWELL_REQ\n");
+ oz_add_farewell(pd, body->ep_num,
+ body->index, body->report,
+ elt->length + 1 - sizeof(*body));
+ }
+ break;
+ case OZ_ELT_APP_DATA:
+ if (pd && (pd->state & OZ_PD_S_CONNECTED)) {
+ struct oz_app_hdr *app_hdr =
+ (struct oz_app_hdr *)(elt+1);
+ if (dup)
+ break;
+ oz_handle_app_elt(pd, app_hdr->app_id, elt);
+ }
+ break;
+ default:
+ oz_dbg(ON, "RX: Unknown elt %02x\n", elt->type);
+ }
+ elt = oz_next_elt(elt);
+ }
+done:
+ if (pd)
+ oz_pd_put(pd);
+ consume_skb(skb);
+}
+
+/*
+ * Context: process
+ */
+void oz_protocol_term(void)
+{
+ struct oz_binding *b, *t;
+
+ /* Walk the list of bindings and remove each one.
+ */
+ spin_lock_bh(&g_binding_lock);
+ list_for_each_entry_safe(b, t, &g_binding, link) {
+ list_del(&b->link);
+ spin_unlock_bh(&g_binding_lock);
+ dev_remove_pack(&b->ptype);
+ if (b->ptype.dev)
+ dev_put(b->ptype.dev);
+ kfree(b);
+ spin_lock_bh(&g_binding_lock);
+ }
+ spin_unlock_bh(&g_binding_lock);
+ /* Walk the list of PDs and stop each one. This causes the PD to be
+ * removed from the list so we can just pull each one from the head
+ * of the list.
+ */
+ spin_lock_bh(&g_polling_lock);
+ while (!list_empty(&g_pd_list)) {
+ struct oz_pd *pd =
+ list_first_entry(&g_pd_list, struct oz_pd, link);
+ oz_pd_get(pd);
+ spin_unlock_bh(&g_polling_lock);
+ oz_pd_stop(pd);
+ oz_pd_put(pd);
+ spin_lock_bh(&g_polling_lock);
+ }
+ spin_unlock_bh(&g_polling_lock);
+ oz_dbg(ON, "Protocol stopped\n");
+
+ kmem_cache_destroy(oz_tx_frame_cache);
+ kmem_cache_destroy(oz_elt_info_cache);
+}
+
+/*
+ * Context: softirq
+ */
+void oz_pd_heartbeat_handler(unsigned long data)
+{
+ struct oz_pd *pd = (struct oz_pd *)data;
+ u16 apps = 0;
+
+ spin_lock_bh(&g_polling_lock);
+ if (pd->state & OZ_PD_S_CONNECTED)
+ apps = pd->total_apps;
+ spin_unlock_bh(&g_polling_lock);
+ if (apps)
+ oz_pd_heartbeat(pd, apps);
+ oz_pd_put(pd);
+}
+
+/*
+ * Context: softirq
+ */
+void oz_pd_timeout_handler(unsigned long data)
+{
+ int type;
+ struct oz_pd *pd = (struct oz_pd *)data;
+
+ spin_lock_bh(&g_polling_lock);
+ type = pd->timeout_type;
+ spin_unlock_bh(&g_polling_lock);
+ switch (type) {
+ case OZ_TIMER_TOUT:
+ oz_pd_sleep(pd);
+ break;
+ case OZ_TIMER_STOP:
+ oz_pd_stop(pd);
+ break;
+ }
+ oz_pd_put(pd);
+}
+
+/*
+ * Context: Interrupt
+ */
+enum hrtimer_restart oz_pd_heartbeat_event(struct hrtimer *timer)
+{
+ struct oz_pd *pd;
+
+ pd = container_of(timer, struct oz_pd, heartbeat);
+ hrtimer_forward_now(timer, ktime_set(pd->pulse_period /
+ MSEC_PER_SEC, (pd->pulse_period % MSEC_PER_SEC) * NSEC_PER_MSEC));
+ oz_pd_get(pd);
+ tasklet_schedule(&pd->heartbeat_tasklet);
+ return HRTIMER_RESTART;
+}
+
+/*
+ * Context: Interrupt
+ */
+enum hrtimer_restart oz_pd_timeout_event(struct hrtimer *timer)
+{
+ struct oz_pd *pd;
+
+ pd = container_of(timer, struct oz_pd, timeout);
+ oz_pd_get(pd);
+ tasklet_schedule(&pd->timeout_tasklet);
+ return HRTIMER_NORESTART;
+}
+
+/*
+ * Context: softirq or process
+ */
+void oz_timer_add(struct oz_pd *pd, int type, unsigned long due_time)
+{
+ spin_lock_bh(&g_polling_lock);
+ switch (type) {
+ case OZ_TIMER_TOUT:
+ case OZ_TIMER_STOP:
+ if (hrtimer_active(&pd->timeout)) {
+ hrtimer_set_expires(&pd->timeout, ktime_set(due_time /
+ MSEC_PER_SEC, (due_time % MSEC_PER_SEC) *
+ NSEC_PER_MSEC));
+ hrtimer_start_expires(&pd->timeout, HRTIMER_MODE_REL);
+ } else {
+ hrtimer_start(&pd->timeout, ktime_set(due_time /
+ MSEC_PER_SEC, (due_time % MSEC_PER_SEC) *
+ NSEC_PER_MSEC), HRTIMER_MODE_REL);
+ }
+ pd->timeout_type = type;
+ break;
+ case OZ_TIMER_HEARTBEAT:
+ if (!hrtimer_active(&pd->heartbeat))
+ hrtimer_start(&pd->heartbeat, ktime_set(due_time /
+ MSEC_PER_SEC, (due_time % MSEC_PER_SEC) *
+ NSEC_PER_MSEC), HRTIMER_MODE_REL);
+ break;
+ }
+ spin_unlock_bh(&g_polling_lock);
+}
+
+/*
+ * Context: softirq or process
+ */
+void oz_pd_request_heartbeat(struct oz_pd *pd)
+{
+ oz_timer_add(pd, OZ_TIMER_HEARTBEAT, pd->pulse_period > 0 ?
+ pd->pulse_period : OZ_QUANTUM);
+}
+
+/*
+ * Context: softirq or process
+ */
+struct oz_pd *oz_pd_find(const u8 *mac_addr)
+{
+ struct oz_pd *pd;
+
+ spin_lock_bh(&g_polling_lock);
+ list_for_each_entry(pd, &g_pd_list, link) {
+ if (ether_addr_equal(pd->mac_addr, mac_addr)) {
+ oz_pd_get(pd);
+ spin_unlock_bh(&g_polling_lock);
+ return pd;
+ }
+ }
+ spin_unlock_bh(&g_polling_lock);
+ return NULL;
+}
+
+/*
+ * Context: process
+ */
+void oz_app_enable(int app_id, int enable)
+{
+ if (app_id < OZ_NB_APPS) {
+ spin_lock_bh(&g_polling_lock);
+ if (enable)
+ g_apps |= (1<<app_id);
+ else
+ g_apps &= ~(1<<app_id);
+ spin_unlock_bh(&g_polling_lock);
+ }
+}
+
+/*
+ * Context: softirq
+ */
+static int oz_pkt_recv(struct sk_buff *skb, struct net_device *dev,
+ struct packet_type *pt, struct net_device *orig_dev)
+{
+ skb = skb_share_check(skb, GFP_ATOMIC);
+ if (skb == NULL)
+ return 0;
+ spin_lock_bh(&g_rx_queue.lock);
+ if (g_processing_rx) {
+ /* We already hold the lock so use __ variant.
+ */
+ __skb_queue_head(&g_rx_queue, skb);
+ spin_unlock_bh(&g_rx_queue.lock);
+ } else {
+ g_processing_rx = 1;
+ do {
+
+ spin_unlock_bh(&g_rx_queue.lock);
+ oz_rx_frame(skb);
+ spin_lock_bh(&g_rx_queue.lock);
+ if (skb_queue_empty(&g_rx_queue)) {
+ g_processing_rx = 0;
+ spin_unlock_bh(&g_rx_queue.lock);
+ break;
+ }
+ /* We already hold the lock so use __ variant.
+ */
+ skb = __skb_dequeue(&g_rx_queue);
+ } while (1);
+ }
+ return 0;
+}
+
+/*
+ * Context: process
+ */
+void oz_binding_add(const char *net_dev)
+{
+ struct oz_binding *binding;
+
+ binding = kzalloc(sizeof(struct oz_binding), GFP_KERNEL);
+ if (!binding)
+ return;
+
+ binding->ptype.type = htons(OZ_ETHERTYPE);
+ binding->ptype.func = oz_pkt_recv;
+ if (net_dev && *net_dev) {
+ memcpy(binding->name, net_dev, OZ_MAX_BINDING_LEN);
+ oz_dbg(ON, "Adding binding: %s\n", net_dev);
+ binding->ptype.dev = dev_get_by_name(&init_net, net_dev);
+ if (binding->ptype.dev == NULL) {
+ oz_dbg(ON, "Netdev %s not found\n", net_dev);
+ kfree(binding);
+ return;
+ }
+ }
+ dev_add_pack(&binding->ptype);
+ spin_lock_bh(&g_binding_lock);
+ list_add_tail(&binding->link, &g_binding);
+ spin_unlock_bh(&g_binding_lock);
+}
+
+/*
+ * Context: process
+ */
+static void pd_stop_all_for_device(struct net_device *net_dev)
+{
+ LIST_HEAD(h);
+ struct oz_pd *pd;
+ struct oz_pd *n;
+
+ spin_lock_bh(&g_polling_lock);
+ list_for_each_entry_safe(pd, n, &g_pd_list, link) {
+ if (pd->net_dev == net_dev) {
+ list_move(&pd->link, &h);
+ oz_pd_get(pd);
+ }
+ }
+ spin_unlock_bh(&g_polling_lock);
+ while (!list_empty(&h)) {
+ pd = list_first_entry(&h, struct oz_pd, link);
+ oz_pd_stop(pd);
+ oz_pd_put(pd);
+ }
+}
+
+/*
+ * Context: process
+ */
+void oz_binding_remove(const char *net_dev)
+{
+ struct oz_binding *binding;
+ int found = 0;
+
+ oz_dbg(ON, "Removing binding: %s\n", net_dev);
+ spin_lock_bh(&g_binding_lock);
+ list_for_each_entry(binding, &g_binding, link) {
+ if (strncmp(binding->name, net_dev, OZ_MAX_BINDING_LEN) == 0) {
+ oz_dbg(ON, "Binding '%s' found\n", net_dev);
+ found = 1;
+ break;
+ }
+ }
+ spin_unlock_bh(&g_binding_lock);
+ if (found) {
+ dev_remove_pack(&binding->ptype);
+ if (binding->ptype.dev) {
+ dev_put(binding->ptype.dev);
+ pd_stop_all_for_device(binding->ptype.dev);
+ }
+ list_del(&binding->link);
+ kfree(binding);
+ }
+}
+
+/*
+ * Context: process
+ */
+static char *oz_get_next_device_name(char *s, char *dname, int max_size)
+{
+ while (*s == ',')
+ s++;
+ while (*s && (*s != ',') && max_size > 1) {
+ *dname++ = *s++;
+ max_size--;
+ }
+ *dname = 0;
+ return s;
+}
+
+/*
+ * Context: process
+ */
+int oz_protocol_init(char *devs)
+{
+ oz_elt_info_cache = KMEM_CACHE(oz_elt_info, 0);
+ if (!oz_elt_info_cache)
+ return -ENOMEM;
+
+ oz_tx_frame_cache = KMEM_CACHE(oz_tx_frame, 0);
+ if (!oz_tx_frame_cache) {
+ kmem_cache_destroy(oz_elt_info_cache);
+ return -ENOMEM;
+ }
+
+ skb_queue_head_init(&g_rx_queue);
+ if (devs[0] == '*') {
+ oz_binding_add(NULL);
+ } else {
+ char d[32];
+
+ while (*devs) {
+ devs = oz_get_next_device_name(devs, d, sizeof(d));
+ if (d[0])
+ oz_binding_add(d);
+ }
+ }
+ return 0;
+}
+
+/*
+ * Context: process
+ */
+int oz_get_pd_list(struct oz_mac_addr *addr, int max_count)
+{
+ struct oz_pd *pd;
+ int count = 0;
+
+ spin_lock_bh(&g_polling_lock);
+ list_for_each_entry(pd, &g_pd_list, link) {
+ if (count >= max_count)
+ break;
+ ether_addr_copy((u8 *)&addr[count++], pd->mac_addr);
+ }
+ spin_unlock_bh(&g_polling_lock);
+ return count;
+}
+
diff --git a/drivers/staging/ozwpan/ozproto.h b/drivers/staging/ozwpan/ozproto.h
new file mode 100644
index 000000000..30c2db91c
--- /dev/null
+++ b/drivers/staging/ozwpan/ozproto.h
@@ -0,0 +1,62 @@
+/* -----------------------------------------------------------------------------
+ * Copyright (c) 2011 Ozmo Inc
+ * Released under the GNU General Public License Version 2 (GPLv2).
+ * -----------------------------------------------------------------------------
+ */
+#ifndef _OZPROTO_H
+#define _OZPROTO_H
+
+#include <asm/byteorder.h>
+#include "ozdbg.h"
+#include "ozappif.h"
+
+#define OZ_ALLOCATED_SPACE(__x) (LL_RESERVED_SPACE(__x)+(__x)->needed_tailroom)
+
+/* Quantum in MS */
+#define OZ_QUANTUM 8
+/* Default timeouts.
+ */
+#define OZ_PRESLEEP_TOUT 11
+
+/* Maximun sizes of tx frames. */
+#define OZ_MAX_TX_SIZE 760
+
+/* Maximum number of uncompleted isoc frames that can be pending in network. */
+#define OZ_MAX_SUBMITTED_ISOC 16
+
+/* Maximum number of uncompleted isoc frames that can be pending in Tx Queue. */
+#define OZ_MAX_TX_QUEUE_ISOC 32
+
+/* Application handler functions.
+ */
+struct oz_app_if {
+ int (*init)(void);
+ void (*term)(void);
+ int (*start)(struct oz_pd *pd, int resume);
+ void (*stop)(struct oz_pd *pd, int pause);
+ void (*rx)(struct oz_pd *pd, struct oz_elt *elt);
+ int (*heartbeat)(struct oz_pd *pd);
+ void (*farewell)(struct oz_pd *pd, u8 ep_num, u8 *data, u8 len);
+};
+
+int oz_protocol_init(char *devs);
+void oz_protocol_term(void);
+int oz_get_pd_list(struct oz_mac_addr *addr, int max_count);
+void oz_app_enable(int app_id, int enable);
+struct oz_pd *oz_pd_find(const u8 *mac_addr);
+void oz_binding_add(const char *net_dev);
+void oz_binding_remove(const char *net_dev);
+void oz_timer_add(struct oz_pd *pd, int type, unsigned long due_time);
+void oz_timer_delete(struct oz_pd *pd, int type);
+void oz_pd_request_heartbeat(struct oz_pd *pd);
+void oz_pd_heartbeat_handler(unsigned long data);
+void oz_pd_timeout_handler(unsigned long data);
+enum hrtimer_restart oz_pd_heartbeat_event(struct hrtimer *timer);
+enum hrtimer_restart oz_pd_timeout_event(struct hrtimer *timer);
+int oz_get_pd_status_list(char *pd_list, int max_count);
+int oz_get_binding_list(char *buf, int max_if);
+
+extern struct kmem_cache *oz_elt_info_cache;
+extern struct kmem_cache *oz_tx_frame_cache;
+
+#endif /* _OZPROTO_H */
diff --git a/drivers/staging/ozwpan/ozprotocol.h b/drivers/staging/ozwpan/ozprotocol.h
new file mode 100644
index 000000000..464207259
--- /dev/null
+++ b/drivers/staging/ozwpan/ozprotocol.h
@@ -0,0 +1,375 @@
+/* -----------------------------------------------------------------------------
+ * Copyright (c) 2011 Ozmo Inc
+ * Released under the GNU General Public License Version 2 (GPLv2).
+ * -----------------------------------------------------------------------------
+ */
+#ifndef _OZPROTOCOL_H
+#define _OZPROTOCOL_H
+
+#define PACKED __packed
+
+#define OZ_ETHERTYPE 0x892e
+
+/* Status codes
+ */
+#define OZ_STATUS_SUCCESS 0
+#define OZ_STATUS_INVALID_PARAM 1
+#define OZ_STATUS_TOO_MANY_PDS 2
+#define OZ_STATUS_NOT_ALLOWED 4
+#define OZ_STATUS_SESSION_MISMATCH 5
+#define OZ_STATUS_SESSION_TEARDOWN 6
+
+/* This is the generic element header.
+ Every element starts with this.
+ */
+struct oz_elt {
+ u8 type;
+ u8 length;
+} PACKED;
+
+#define oz_next_elt(__elt) \
+ (struct oz_elt *)((u8 *)((__elt) + 1) + (__elt)->length)
+
+/* Protocol element IDs.
+ */
+#define OZ_ELT_CONNECT_REQ 0x06
+#define OZ_ELT_CONNECT_RSP 0x07
+#define OZ_ELT_DISCONNECT 0x08
+#define OZ_ELT_UPDATE_PARAM_REQ 0x11
+#define OZ_ELT_FAREWELL_REQ 0x12
+#define OZ_ELT_APP_DATA 0x31
+
+/* This is the Ozmo header which is the first Ozmo specific part
+ * of a frame and comes after the MAC header.
+ */
+struct oz_hdr {
+ u8 control;
+ u8 last_pkt_num;
+ u32 pkt_num;
+} PACKED;
+
+#define OZ_PROTOCOL_VERSION 0x1
+/* Bits in the control field. */
+#define OZ_VERSION_MASK 0xc
+#define OZ_VERSION_SHIFT 2
+#define OZ_F_ACK 0x10
+#define OZ_F_ISOC 0x20
+#define OZ_F_MORE_DATA 0x40
+#define OZ_F_ACK_REQUESTED 0x80
+
+#define oz_get_prot_ver(__x) (((__x) & OZ_VERSION_MASK) >> OZ_VERSION_SHIFT)
+
+/* Used to select the bits of packet number to put in the last_pkt_num.
+ */
+#define OZ_LAST_PN_MASK 0x00ff
+
+#define OZ_LAST_PN_HALF_CYCLE 127
+
+#define OZ_LATENCY_MASK 0xc0
+#define OZ_ONE_MS_LATENCY 0x40
+#define OZ_TEN_MS_LATENCY 0x80
+
+/* Connect request data structure.
+ */
+struct oz_elt_connect_req {
+ u8 mode;
+ u8 resv1[16];
+ u8 pd_info;
+ u8 session_id;
+ u8 presleep;
+ u8 ms_isoc_latency;
+ u8 host_vendor;
+ u8 keep_alive;
+ u16 apps;
+ u8 max_len_div16;
+ u8 ms_per_isoc;
+ u8 resv3[2];
+} PACKED;
+
+/* mode field bits.
+ */
+#define OZ_MODE_POLLED 0x0
+#define OZ_MODE_TRIGGERED 0x1
+#define OZ_MODE_MASK 0xf
+#define OZ_F_ISOC_NO_ELTS 0x40
+#define OZ_F_ISOC_ANYTIME 0x80
+#define OZ_NO_ELTS_ANYTIME 0xc0
+
+/* Keep alive field.
+ */
+#define OZ_KALIVE_TYPE_MASK 0xc0
+#define OZ_KALIVE_VALUE_MASK 0x3f
+#define OZ_KALIVE_SPECIAL 0x00
+#define OZ_KALIVE_SECS 0x40
+#define OZ_KALIVE_MINS 0x80
+#define OZ_KALIVE_HOURS 0xc0
+
+/* Connect response data structure.
+ */
+struct oz_elt_connect_rsp {
+ u8 mode;
+ u8 status;
+ u8 resv1[3];
+ u8 session_id;
+ u16 apps;
+ u32 resv2;
+} PACKED;
+
+struct oz_elt_farewell {
+ u8 ep_num;
+ u8 index;
+ u8 report[1];
+} PACKED;
+
+struct oz_elt_update_param {
+ u8 resv1[16];
+ u8 presleep;
+ u8 resv2;
+ u8 host_vendor;
+ u8 keepalive;
+} PACKED;
+
+/* Header common to all application elements.
+ */
+struct oz_app_hdr {
+ u8 app_id;
+ u8 elt_seq_num;
+} PACKED;
+
+/* Values for app_id.
+ */
+#define OZ_APPID_USB 0x1
+#define OZ_APPID_SERIAL 0x4
+#define OZ_APPID_MAX OZ_APPID_SERIAL
+#define OZ_NB_APPS (OZ_APPID_MAX+1)
+
+/* USB header common to all elements for the USB application.
+ * This header extends the oz_app_hdr and comes directly after
+ * the element header in a USB application.
+ */
+struct oz_usb_hdr {
+ u8 app_id;
+ u8 elt_seq_num;
+ u8 type;
+} PACKED;
+
+
+
+/* USB requests element subtypes (type field of hs_usb_hdr).
+ */
+#define OZ_GET_DESC_REQ 1
+#define OZ_GET_DESC_RSP 2
+#define OZ_SET_CONFIG_REQ 3
+#define OZ_SET_CONFIG_RSP 4
+#define OZ_SET_INTERFACE_REQ 5
+#define OZ_SET_INTERFACE_RSP 6
+#define OZ_VENDOR_CLASS_REQ 7
+#define OZ_VENDOR_CLASS_RSP 8
+#define OZ_GET_STATUS_REQ 9
+#define OZ_GET_STATUS_RSP 10
+#define OZ_CLEAR_FEATURE_REQ 11
+#define OZ_CLEAR_FEATURE_RSP 12
+#define OZ_SET_FEATURE_REQ 13
+#define OZ_SET_FEATURE_RSP 14
+#define OZ_GET_CONFIGURATION_REQ 15
+#define OZ_GET_CONFIGURATION_RSP 16
+#define OZ_GET_INTERFACE_REQ 17
+#define OZ_GET_INTERFACE_RSP 18
+#define OZ_SYNCH_FRAME_REQ 19
+#define OZ_SYNCH_FRAME_RSP 20
+#define OZ_USB_ENDPOINT_DATA 23
+
+#define OZ_REQD_D2H 0x80
+
+struct oz_get_desc_req {
+ u8 app_id;
+ u8 elt_seq_num;
+ u8 type;
+ u8 req_id;
+ u16 offset;
+ u16 size;
+ u8 req_type;
+ u8 desc_type;
+ __le16 w_index;
+ u8 index;
+} PACKED;
+
+/* Values for desc_type field.
+*/
+#define OZ_DESC_DEVICE 0x01
+#define OZ_DESC_CONFIG 0x02
+#define OZ_DESC_STRING 0x03
+
+/* Values for req_type field.
+ */
+#define OZ_RECP_MASK 0x1F
+#define OZ_RECP_DEVICE 0x00
+#define OZ_RECP_INTERFACE 0x01
+#define OZ_RECP_ENDPOINT 0x02
+
+#define OZ_REQT_MASK 0x60
+#define OZ_REQT_STD 0x00
+#define OZ_REQT_CLASS 0x20
+#define OZ_REQT_VENDOR 0x40
+
+struct oz_get_desc_rsp {
+ u8 app_id;
+ u8 elt_seq_num;
+ u8 type;
+ u8 req_id;
+ __le16 offset;
+ __le16 total_size;
+ u8 rcode;
+ u8 data[1];
+} PACKED;
+
+struct oz_feature_req {
+ u8 app_id;
+ u8 elt_seq_num;
+ u8 type;
+ u8 req_id;
+ u8 recipient;
+ u8 index;
+ u16 feature;
+} PACKED;
+
+struct oz_feature_rsp {
+ u8 app_id;
+ u8 elt_seq_num;
+ u8 type;
+ u8 req_id;
+ u8 rcode;
+} PACKED;
+
+struct oz_set_config_req {
+ u8 app_id;
+ u8 elt_seq_num;
+ u8 type;
+ u8 req_id;
+ u8 index;
+} PACKED;
+
+struct oz_set_config_rsp {
+ u8 app_id;
+ u8 elt_seq_num;
+ u8 type;
+ u8 req_id;
+ u8 rcode;
+} PACKED;
+
+struct oz_set_interface_req {
+ u8 app_id;
+ u8 elt_seq_num;
+ u8 type;
+ u8 req_id;
+ u8 index;
+ u8 alternative;
+} PACKED;
+
+struct oz_set_interface_rsp {
+ u8 app_id;
+ u8 elt_seq_num;
+ u8 type;
+ u8 req_id;
+ u8 rcode;
+} PACKED;
+
+struct oz_get_interface_req {
+ u8 app_id;
+ u8 elt_seq_num;
+ u8 type;
+ u8 req_id;
+ u8 index;
+} PACKED;
+
+struct oz_get_interface_rsp {
+ u8 app_id;
+ u8 elt_seq_num;
+ u8 type;
+ u8 req_id;
+ u8 rcode;
+ u8 alternative;
+} PACKED;
+
+struct oz_vendor_class_req {
+ u8 app_id;
+ u8 elt_seq_num;
+ u8 type;
+ u8 req_id;
+ u8 req_type;
+ u8 request;
+ u16 value;
+ u16 index;
+ u8 data[1];
+} PACKED;
+
+struct oz_vendor_class_rsp {
+ u8 app_id;
+ u8 elt_seq_num;
+ u8 type;
+ u8 req_id;
+ u8 rcode;
+ u8 data[1];
+} PACKED;
+
+struct oz_data {
+ u8 app_id;
+ u8 elt_seq_num;
+ u8 type;
+ u8 endpoint;
+ u8 format;
+} PACKED;
+
+struct oz_isoc_fixed {
+ u8 app_id;
+ u8 elt_seq_num;
+ u8 type;
+ u8 endpoint;
+ u8 format;
+ u8 unit_size;
+ u8 frame_number;
+ u8 data[1];
+} PACKED;
+
+struct oz_multiple_fixed {
+ u8 app_id;
+ u8 elt_seq_num;
+ u8 type;
+ u8 endpoint;
+ u8 format;
+ u8 unit_size;
+ u8 data[1];
+} PACKED;
+
+struct oz_fragmented {
+ u8 app_id;
+ u8 elt_seq_num;
+ u8 type;
+ u8 endpoint;
+ u8 format;
+ u16 total_size;
+ u16 offset;
+ u8 data[1];
+} PACKED;
+
+/* Note: the following does not get packaged in an element in the same way
+ * that other data formats are packaged. Instead the data is put in a frame
+ * directly after the oz_header and is the only permitted data in such a
+ * frame. The length of the data is directly determined from the frame size.
+ */
+struct oz_isoc_large {
+ u8 endpoint;
+ u8 format;
+ u8 ms_data;
+ u8 frame_number;
+} PACKED;
+
+#define OZ_DATA_F_TYPE_MASK 0xF
+#define OZ_DATA_F_MULTIPLE_FIXED 0x1
+#define OZ_DATA_F_MULTIPLE_VAR 0x2
+#define OZ_DATA_F_ISOC_FIXED 0x3
+#define OZ_DATA_F_ISOC_VAR 0x4
+#define OZ_DATA_F_FRAGMENTED 0x5
+#define OZ_DATA_F_ISOC_LARGE 0x7
+
+#endif /* _OZPROTOCOL_H */
diff --git a/drivers/staging/ozwpan/ozurbparanoia.c b/drivers/staging/ozwpan/ozurbparanoia.c
new file mode 100644
index 000000000..cf6278a19
--- /dev/null
+++ b/drivers/staging/ozwpan/ozurbparanoia.c
@@ -0,0 +1,54 @@
+/* -----------------------------------------------------------------------------
+ * Copyright (c) 2011 Ozmo Inc
+ * Released under the GNU General Public License Version 2 (GPLv2).
+ * -----------------------------------------------------------------------------
+ */
+#include <linux/usb.h>
+#include "ozdbg.h"
+
+#ifdef WANT_URB_PARANOIA
+
+#include "ozurbparanoia.h"
+
+#define OZ_MAX_URBS 1000
+struct urb *g_urb_memory[OZ_MAX_URBS];
+int g_nb_urbs;
+DEFINE_SPINLOCK(g_urb_mem_lock);
+
+void oz_remember_urb(struct urb *urb)
+{
+ unsigned long irq_state;
+
+ spin_lock_irqsave(&g_urb_mem_lock, irq_state);
+ if (g_nb_urbs < OZ_MAX_URBS) {
+ g_urb_memory[g_nb_urbs++] = urb;
+ oz_dbg(ON, "urb up = %d %p\n", g_nb_urbs, urb);
+ } else {
+ oz_dbg(ON, "ERROR urb buffer full\n");
+ }
+ spin_unlock_irqrestore(&g_urb_mem_lock, irq_state);
+}
+
+/*
+ */
+int oz_forget_urb(struct urb *urb)
+{
+ unsigned long irq_state;
+ int i;
+ int rc = -1;
+
+ spin_lock_irqsave(&g_urb_mem_lock, irq_state);
+ for (i = 0; i < g_nb_urbs; i++) {
+ if (g_urb_memory[i] == urb) {
+ rc = 0;
+ if (--g_nb_urbs > i)
+ memcpy(&g_urb_memory[i], &g_urb_memory[i+1],
+ (g_nb_urbs - i) * sizeof(struct urb *));
+ oz_dbg(ON, "urb down = %d %p\n", g_nb_urbs, urb);
+ }
+ }
+ spin_unlock_irqrestore(&g_urb_mem_lock, irq_state);
+ return rc;
+}
+#endif /* #ifdef WANT_URB_PARANOIA */
+
diff --git a/drivers/staging/ozwpan/ozurbparanoia.h b/drivers/staging/ozwpan/ozurbparanoia.h
new file mode 100644
index 000000000..5080ea76f
--- /dev/null
+++ b/drivers/staging/ozwpan/ozurbparanoia.h
@@ -0,0 +1,19 @@
+#ifndef _OZURBPARANOIA_H
+#define _OZURBPARANOIA_H
+/* -----------------------------------------------------------------------------
+ * Released under the GNU General Public License Version 2 (GPLv2).
+ * Copyright (c) 2011 Ozmo Inc
+ * -----------------------------------------------------------------------------
+ */
+
+#ifdef WANT_URB_PARANOIA
+void oz_remember_urb(struct urb *urb);
+int oz_forget_urb(struct urb *urb);
+#else
+static inline void oz_remember_urb(struct urb *urb) {}
+static inline int oz_forget_urb(struct urb *urb) { return 0; }
+#endif /* WANT_URB_PARANOIA */
+
+
+#endif /* _OZURBPARANOIA_H */
+
diff --git a/drivers/staging/ozwpan/ozusbif.h b/drivers/staging/ozwpan/ozusbif.h
new file mode 100644
index 000000000..d2a608534
--- /dev/null
+++ b/drivers/staging/ozwpan/ozusbif.h
@@ -0,0 +1,43 @@
+/* -----------------------------------------------------------------------------
+ * Copyright (c) 2011 Ozmo Inc
+ * Released under the GNU General Public License Version 2 (GPLv2).
+ * -----------------------------------------------------------------------------
+ */
+#ifndef _OZUSBIF_H
+#define _OZUSBIF_H
+
+#include <linux/usb.h>
+
+/* Reference counting functions.
+ */
+void oz_usb_get(void *hpd);
+void oz_usb_put(void *hpd);
+
+/* Stream functions.
+ */
+int oz_usb_stream_create(void *hpd, u8 ep_num);
+int oz_usb_stream_delete(void *hpd, u8 ep_num);
+
+/* Request functions.
+ */
+int oz_usb_control_req(void *hpd, u8 req_id, struct usb_ctrlrequest *setup,
+ const u8 *data, int data_len);
+int oz_usb_get_desc_req(void *hpd, u8 req_id, u8 req_type, u8 desc_type,
+ u8 index, __le16 windex, int offset, int len);
+int oz_usb_send_isoc(void *hpd, u8 ep_num, struct urb *urb);
+void oz_usb_request_heartbeat(void *hpd);
+
+/* Confirmation functions.
+ */
+void oz_hcd_get_desc_cnf(void *hport, u8 req_id, u8 status,
+ const u8 *desc, u8 length, u16 offset, u16 total_size);
+void oz_hcd_control_cnf(void *hport, u8 req_id, u8 rcode,
+ const u8 *data, int data_len);
+
+/* Indication functions.
+ */
+void oz_hcd_data_ind(void *hport, u8 endpoint, const u8 *data, int data_len);
+
+int oz_hcd_heartbeat(void *hport);
+
+#endif /* _OZUSBIF_H */
diff --git a/drivers/staging/ozwpan/ozusbsvc.c b/drivers/staging/ozwpan/ozusbsvc.c
new file mode 100644
index 000000000..bf15dc301
--- /dev/null
+++ b/drivers/staging/ozwpan/ozusbsvc.c
@@ -0,0 +1,263 @@
+/* -----------------------------------------------------------------------------
+ * Copyright (c) 2011 Ozmo Inc
+ * Released under the GNU General Public License Version 2 (GPLv2).
+ *
+ * This file provides protocol independent part of the implementation of the USB
+ * service for a PD.
+ * The implementation of this service is split into two parts the first of which
+ * is protocol independent and the second contains protocol specific details.
+ * This split is to allow alternative protocols to be defined.
+ * The implementation of this service uses ozhcd.c to implement a USB HCD.
+ * -----------------------------------------------------------------------------
+ */
+
+#include <linux/module.h>
+#include <linux/timer.h>
+#include <linux/sched.h>
+#include <linux/netdevice.h>
+#include <linux/errno.h>
+#include <linux/input.h>
+#include <asm/unaligned.h>
+#include "ozdbg.h"
+#include "ozprotocol.h"
+#include "ozeltbuf.h"
+#include "ozpd.h"
+#include "ozproto.h"
+#include "ozusbif.h"
+#include "ozhcd.h"
+#include "ozusbsvc.h"
+
+/*
+ * This is called once when the driver is loaded to initialise the USB service.
+ * Context: process
+ */
+int oz_usb_init(void)
+{
+ return oz_hcd_init();
+}
+
+/*
+ * This is called once when the driver is unloaded to terminate the USB service.
+ * Context: process
+ */
+void oz_usb_term(void)
+{
+ oz_hcd_term();
+}
+
+/*
+ * This is called when the USB service is started or resumed for a PD.
+ * Context: softirq
+ */
+int oz_usb_start(struct oz_pd *pd, int resume)
+{
+ int rc = 0;
+ struct oz_usb_ctx *usb_ctx;
+ struct oz_usb_ctx *old_ctx;
+
+ if (resume) {
+ oz_dbg(ON, "USB service resumed\n");
+ return 0;
+ }
+ oz_dbg(ON, "USB service started\n");
+ /* Create a USB context in case we need one. If we find the PD already
+ * has a USB context then we will destroy it.
+ */
+ usb_ctx = kzalloc(sizeof(struct oz_usb_ctx), GFP_ATOMIC);
+ if (usb_ctx == NULL)
+ return -ENOMEM;
+ atomic_set(&usb_ctx->ref_count, 1);
+ usb_ctx->pd = pd;
+ usb_ctx->stopped = 0;
+ /* Install the USB context if the PD doesn't already have one.
+ * If it does already have one then destroy the one we have just
+ * created.
+ */
+ spin_lock_bh(&pd->app_lock[OZ_APPID_USB]);
+ old_ctx = pd->app_ctx[OZ_APPID_USB];
+ if (old_ctx == NULL)
+ pd->app_ctx[OZ_APPID_USB] = usb_ctx;
+ oz_usb_get(pd->app_ctx[OZ_APPID_USB]);
+ spin_unlock_bh(&pd->app_lock[OZ_APPID_USB]);
+ if (old_ctx) {
+ oz_dbg(ON, "Already have USB context\n");
+ kfree(usb_ctx);
+ usb_ctx = old_ctx;
+ } else if (usb_ctx) {
+ /* Take a reference to the PD. This will be released when
+ * the USB context is destroyed.
+ */
+ oz_pd_get(pd);
+ }
+ /* If we already had a USB context and had obtained a port from
+ * the USB HCD then just reset the port. If we didn't have a port
+ * then report the arrival to the USB HCD so we get one.
+ */
+ if (usb_ctx->hport) {
+ oz_hcd_pd_reset(usb_ctx, usb_ctx->hport);
+ } else {
+ usb_ctx->hport = oz_hcd_pd_arrived(usb_ctx);
+ if (usb_ctx->hport == NULL) {
+ oz_dbg(ON, "USB hub returned null port\n");
+ spin_lock_bh(&pd->app_lock[OZ_APPID_USB]);
+ pd->app_ctx[OZ_APPID_USB] = NULL;
+ spin_unlock_bh(&pd->app_lock[OZ_APPID_USB]);
+ oz_usb_put(usb_ctx);
+ rc = -1;
+ }
+ }
+ oz_usb_put(usb_ctx);
+ return rc;
+}
+
+/*
+ * This is called when the USB service is stopped or paused for a PD.
+ * Context: softirq or process
+ */
+void oz_usb_stop(struct oz_pd *pd, int pause)
+{
+ struct oz_usb_ctx *usb_ctx;
+
+ if (pause) {
+ oz_dbg(ON, "USB service paused\n");
+ return;
+ }
+ spin_lock_bh(&pd->app_lock[OZ_APPID_USB]);
+ usb_ctx = (struct oz_usb_ctx *) pd->app_ctx[OZ_APPID_USB];
+ pd->app_ctx[OZ_APPID_USB] = NULL;
+ spin_unlock_bh(&pd->app_lock[OZ_APPID_USB]);
+ if (usb_ctx) {
+ struct timespec ts, now;
+
+ getnstimeofday(&ts);
+ oz_dbg(ON, "USB service stopping...\n");
+ usb_ctx->stopped = 1;
+ /* At this point the reference count on the usb context should
+ * be 2 - one from when we created it and one from the hcd
+ * which claims a reference. Since stopped = 1 no one else
+ * should get in but someone may already be in. So wait
+ * until they leave but timeout after 1 second.
+ */
+ while ((atomic_read(&usb_ctx->ref_count) > 2)) {
+ getnstimeofday(&now);
+ /*Approx 1 Sec. this is not perfect calculation*/
+ if (now.tv_sec != ts.tv_sec)
+ break;
+ }
+ oz_dbg(ON, "USB service stopped\n");
+ oz_hcd_pd_departed(usb_ctx->hport);
+ /* Release the reference taken in oz_usb_start.
+ */
+ oz_usb_put(usb_ctx);
+ }
+}
+
+/*
+ * This increments the reference count of the context area for a specific PD.
+ * This ensures this context area does not disappear while still in use.
+ * Context: softirq
+ */
+void oz_usb_get(void *hpd)
+{
+ struct oz_usb_ctx *usb_ctx = (struct oz_usb_ctx *)hpd;
+
+ atomic_inc(&usb_ctx->ref_count);
+}
+
+/*
+ * This decrements the reference count of the context area for a specific PD
+ * and destroys the context area if the reference count becomes zero.
+ * Context: irq or process
+ */
+void oz_usb_put(void *hpd)
+{
+ struct oz_usb_ctx *usb_ctx = (struct oz_usb_ctx *)hpd;
+
+ if (atomic_dec_and_test(&usb_ctx->ref_count)) {
+ oz_dbg(ON, "Dealloc USB context\n");
+ oz_pd_put(usb_ctx->pd);
+ kfree(usb_ctx);
+ }
+}
+
+/*
+ * Context: softirq
+ */
+int oz_usb_heartbeat(struct oz_pd *pd)
+{
+ struct oz_usb_ctx *usb_ctx;
+ int rc = 0;
+
+ spin_lock_bh(&pd->app_lock[OZ_APPID_USB]);
+ usb_ctx = (struct oz_usb_ctx *) pd->app_ctx[OZ_APPID_USB];
+ if (usb_ctx)
+ oz_usb_get(usb_ctx);
+ spin_unlock_bh(&pd->app_lock[OZ_APPID_USB]);
+ if (usb_ctx == NULL)
+ return rc;
+ if (usb_ctx->stopped)
+ goto done;
+ if (usb_ctx->hport)
+ if (oz_hcd_heartbeat(usb_ctx->hport))
+ rc = 1;
+done:
+ oz_usb_put(usb_ctx);
+ return rc;
+}
+
+/*
+ * Context: softirq
+ */
+int oz_usb_stream_create(void *hpd, u8 ep_num)
+{
+ struct oz_usb_ctx *usb_ctx = (struct oz_usb_ctx *)hpd;
+ struct oz_pd *pd = usb_ctx->pd;
+
+ oz_dbg(ON, "%s: (0x%x)\n", __func__, ep_num);
+ if (pd->mode & OZ_F_ISOC_NO_ELTS) {
+ oz_isoc_stream_create(pd, ep_num);
+ } else {
+ oz_pd_get(pd);
+ if (oz_elt_stream_create(&pd->elt_buff, ep_num,
+ 4*pd->max_tx_size)) {
+ oz_pd_put(pd);
+ return -1;
+ }
+ }
+ return 0;
+}
+
+/*
+ * Context: softirq
+ */
+int oz_usb_stream_delete(void *hpd, u8 ep_num)
+{
+ struct oz_usb_ctx *usb_ctx = (struct oz_usb_ctx *)hpd;
+
+ if (usb_ctx) {
+ struct oz_pd *pd = usb_ctx->pd;
+
+ if (pd) {
+ oz_dbg(ON, "%s: (0x%x)\n", __func__, ep_num);
+ if (pd->mode & OZ_F_ISOC_NO_ELTS) {
+ oz_isoc_stream_delete(pd, ep_num);
+ } else {
+ if (oz_elt_stream_delete(&pd->elt_buff, ep_num))
+ return -1;
+ oz_pd_put(pd);
+ }
+ }
+ }
+ return 0;
+}
+
+/*
+ * Context: softirq or process
+ */
+void oz_usb_request_heartbeat(void *hpd)
+{
+ struct oz_usb_ctx *usb_ctx = (struct oz_usb_ctx *)hpd;
+
+ if (usb_ctx && usb_ctx->pd)
+ oz_pd_request_heartbeat(usb_ctx->pd);
+}
diff --git a/drivers/staging/ozwpan/ozusbsvc.h b/drivers/staging/ozwpan/ozusbsvc.h
new file mode 100644
index 000000000..58e05a59b
--- /dev/null
+++ b/drivers/staging/ozwpan/ozusbsvc.h
@@ -0,0 +1,32 @@
+/* -----------------------------------------------------------------------------
+ * Copyright (c) 2011 Ozmo Inc
+ * Released under the GNU General Public License Version 2 (GPLv2).
+ * -----------------------------------------------------------------------------
+ */
+#ifndef _OZUSBSVC_H
+#define _OZUSBSVC_H
+
+/*------------------------------------------------------------------------------
+ * Per PD context info stored in application context area of PD.
+ * This object is reference counted to ensure it doesn't disappear while
+ * still in use.
+ */
+struct oz_usb_ctx {
+ atomic_t ref_count;
+ u8 tx_seq_num;
+ u8 rx_seq_num;
+ struct oz_pd *pd;
+ void *hport;
+ int stopped;
+};
+
+int oz_usb_init(void);
+void oz_usb_term(void);
+int oz_usb_start(struct oz_pd *pd, int resume);
+void oz_usb_stop(struct oz_pd *pd, int pause);
+void oz_usb_rx(struct oz_pd *pd, struct oz_elt *elt);
+int oz_usb_heartbeat(struct oz_pd *pd);
+void oz_usb_farewell(struct oz_pd *pd, u8 ep_num, u8 *data, u8 len);
+
+#endif /* _OZUSBSVC_H */
+
diff --git a/drivers/staging/ozwpan/ozusbsvc1.c b/drivers/staging/ozwpan/ozusbsvc1.c
new file mode 100644
index 000000000..f660bb198
--- /dev/null
+++ b/drivers/staging/ozwpan/ozusbsvc1.c
@@ -0,0 +1,462 @@
+/* -----------------------------------------------------------------------------
+ * Copyright (c) 2011 Ozmo Inc
+ * Released under the GNU General Public License Version 2 (GPLv2).
+ *
+ * This file implements the protocol specific parts of the USB service for a PD.
+ * -----------------------------------------------------------------------------
+ */
+#include <linux/module.h>
+#include <linux/timer.h>
+#include <linux/sched.h>
+#include <linux/netdevice.h>
+#include <linux/errno.h>
+#include <linux/input.h>
+#include <asm/unaligned.h>
+#include "ozdbg.h"
+#include "ozprotocol.h"
+#include "ozeltbuf.h"
+#include "ozpd.h"
+#include "ozproto.h"
+#include "ozusbif.h"
+#include "ozhcd.h"
+#include "ozusbsvc.h"
+
+#define MAX_ISOC_FIXED_DATA (253-sizeof(struct oz_isoc_fixed))
+
+/*
+ * Context: softirq
+ */
+static int oz_usb_submit_elt(struct oz_elt_buf *eb, struct oz_elt_info *ei,
+ struct oz_usb_ctx *usb_ctx, u8 strid, u8 isoc)
+{
+ int ret;
+ struct oz_elt *elt = (struct oz_elt *)ei->data;
+ struct oz_app_hdr *app_hdr = (struct oz_app_hdr *)(elt+1);
+
+ elt->type = OZ_ELT_APP_DATA;
+ ei->app_id = OZ_APPID_USB;
+ ei->length = elt->length + sizeof(struct oz_elt);
+ app_hdr->app_id = OZ_APPID_USB;
+ spin_lock_bh(&eb->lock);
+ if (isoc == 0) {
+ app_hdr->elt_seq_num = usb_ctx->tx_seq_num++;
+ if (usb_ctx->tx_seq_num == 0)
+ usb_ctx->tx_seq_num = 1;
+ }
+ ret = oz_queue_elt_info(eb, isoc, strid, ei);
+ if (ret)
+ oz_elt_info_free(eb, ei);
+ spin_unlock_bh(&eb->lock);
+ return ret;
+}
+
+/*
+ * Context: softirq
+ */
+int oz_usb_get_desc_req(void *hpd, u8 req_id, u8 req_type, u8 desc_type,
+ u8 index, __le16 windex, int offset, int len)
+{
+ struct oz_usb_ctx *usb_ctx = hpd;
+ struct oz_pd *pd = usb_ctx->pd;
+ struct oz_elt *elt;
+ struct oz_get_desc_req *body;
+ struct oz_elt_buf *eb = &pd->elt_buff;
+ struct oz_elt_info *ei = oz_elt_info_alloc(&pd->elt_buff);
+
+ oz_dbg(ON, " req_type = 0x%x\n", req_type);
+ oz_dbg(ON, " desc_type = 0x%x\n", desc_type);
+ oz_dbg(ON, " index = 0x%x\n", index);
+ oz_dbg(ON, " windex = 0x%x\n", windex);
+ oz_dbg(ON, " offset = 0x%x\n", offset);
+ oz_dbg(ON, " len = 0x%x\n", len);
+ if (len > 200)
+ len = 200;
+ if (ei == NULL)
+ return -1;
+ elt = (struct oz_elt *)ei->data;
+ elt->length = sizeof(struct oz_get_desc_req);
+ body = (struct oz_get_desc_req *)(elt+1);
+ body->type = OZ_GET_DESC_REQ;
+ body->req_id = req_id;
+ put_unaligned(cpu_to_le16(offset), &body->offset);
+ put_unaligned(cpu_to_le16(len), &body->size);
+ body->req_type = req_type;
+ body->desc_type = desc_type;
+ body->w_index = windex;
+ body->index = index;
+ return oz_usb_submit_elt(eb, ei, usb_ctx, 0, 0);
+}
+
+/*
+ * Context: tasklet
+ */
+static int oz_usb_set_config_req(void *hpd, u8 req_id, u8 index)
+{
+ struct oz_usb_ctx *usb_ctx = hpd;
+ struct oz_pd *pd = usb_ctx->pd;
+ struct oz_elt *elt;
+ struct oz_elt_buf *eb = &pd->elt_buff;
+ struct oz_elt_info *ei = oz_elt_info_alloc(&pd->elt_buff);
+ struct oz_set_config_req *body;
+
+ if (ei == NULL)
+ return -1;
+ elt = (struct oz_elt *)ei->data;
+ elt->length = sizeof(struct oz_set_config_req);
+ body = (struct oz_set_config_req *)(elt+1);
+ body->type = OZ_SET_CONFIG_REQ;
+ body->req_id = req_id;
+ body->index = index;
+ return oz_usb_submit_elt(eb, ei, usb_ctx, 0, 0);
+}
+
+/*
+ * Context: tasklet
+ */
+static int oz_usb_set_interface_req(void *hpd, u8 req_id, u8 index, u8 alt)
+{
+ struct oz_usb_ctx *usb_ctx = hpd;
+ struct oz_pd *pd = usb_ctx->pd;
+ struct oz_elt *elt;
+ struct oz_elt_buf *eb = &pd->elt_buff;
+ struct oz_elt_info *ei = oz_elt_info_alloc(&pd->elt_buff);
+ struct oz_set_interface_req *body;
+
+ if (ei == NULL)
+ return -1;
+ elt = (struct oz_elt *)ei->data;
+ elt->length = sizeof(struct oz_set_interface_req);
+ body = (struct oz_set_interface_req *)(elt+1);
+ body->type = OZ_SET_INTERFACE_REQ;
+ body->req_id = req_id;
+ body->index = index;
+ body->alternative = alt;
+ return oz_usb_submit_elt(eb, ei, usb_ctx, 0, 0);
+}
+
+/*
+ * Context: tasklet
+ */
+static int oz_usb_set_clear_feature_req(void *hpd, u8 req_id, u8 type,
+ u8 recipient, u8 index, __le16 feature)
+{
+ struct oz_usb_ctx *usb_ctx = hpd;
+ struct oz_pd *pd = usb_ctx->pd;
+ struct oz_elt *elt;
+ struct oz_elt_buf *eb = &pd->elt_buff;
+ struct oz_elt_info *ei = oz_elt_info_alloc(&pd->elt_buff);
+ struct oz_feature_req *body;
+
+ if (ei == NULL)
+ return -1;
+ elt = (struct oz_elt *)ei->data;
+ elt->length = sizeof(struct oz_feature_req);
+ body = (struct oz_feature_req *)(elt+1);
+ body->type = type;
+ body->req_id = req_id;
+ body->recipient = recipient;
+ body->index = index;
+ put_unaligned(feature, &body->feature);
+ return oz_usb_submit_elt(eb, ei, usb_ctx, 0, 0);
+}
+
+/*
+ * Context: tasklet
+ */
+static int oz_usb_vendor_class_req(void *hpd, u8 req_id, u8 req_type,
+ u8 request, __le16 value, __le16 index, const u8 *data, int data_len)
+{
+ struct oz_usb_ctx *usb_ctx = hpd;
+ struct oz_pd *pd = usb_ctx->pd;
+ struct oz_elt *elt;
+ struct oz_elt_buf *eb = &pd->elt_buff;
+ struct oz_elt_info *ei = oz_elt_info_alloc(&pd->elt_buff);
+ struct oz_vendor_class_req *body;
+
+ if (ei == NULL)
+ return -1;
+ elt = (struct oz_elt *)ei->data;
+ elt->length = sizeof(struct oz_vendor_class_req) - 1 + data_len;
+ body = (struct oz_vendor_class_req *)(elt+1);
+ body->type = OZ_VENDOR_CLASS_REQ;
+ body->req_id = req_id;
+ body->req_type = req_type;
+ body->request = request;
+ put_unaligned(value, &body->value);
+ put_unaligned(index, &body->index);
+ if (data_len)
+ memcpy(body->data, data, data_len);
+ return oz_usb_submit_elt(eb, ei, usb_ctx, 0, 0);
+}
+
+/*
+ * Context: tasklet
+ */
+int oz_usb_control_req(void *hpd, u8 req_id, struct usb_ctrlrequest *setup,
+ const u8 *data, int data_len)
+{
+ unsigned wvalue = le16_to_cpu(setup->wValue);
+ unsigned windex = le16_to_cpu(setup->wIndex);
+ unsigned wlength = le16_to_cpu(setup->wLength);
+ int rc = 0;
+
+ if ((setup->bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD) {
+ switch (setup->bRequest) {
+ case USB_REQ_GET_DESCRIPTOR:
+ rc = oz_usb_get_desc_req(hpd, req_id,
+ setup->bRequestType, (u8)(wvalue>>8),
+ (u8)wvalue, setup->wIndex, 0, wlength);
+ break;
+ case USB_REQ_SET_CONFIGURATION:
+ rc = oz_usb_set_config_req(hpd, req_id, (u8)wvalue);
+ break;
+ case USB_REQ_SET_INTERFACE: {
+ u8 if_num = (u8)windex;
+ u8 alt = (u8)wvalue;
+
+ rc = oz_usb_set_interface_req(hpd, req_id,
+ if_num, alt);
+ }
+ break;
+ case USB_REQ_SET_FEATURE:
+ rc = oz_usb_set_clear_feature_req(hpd, req_id,
+ OZ_SET_FEATURE_REQ,
+ setup->bRequestType & 0xf, (u8)windex,
+ setup->wValue);
+ break;
+ case USB_REQ_CLEAR_FEATURE:
+ rc = oz_usb_set_clear_feature_req(hpd, req_id,
+ OZ_CLEAR_FEATURE_REQ,
+ setup->bRequestType & 0xf,
+ (u8)windex, setup->wValue);
+ break;
+ }
+ } else {
+ rc = oz_usb_vendor_class_req(hpd, req_id, setup->bRequestType,
+ setup->bRequest, setup->wValue, setup->wIndex,
+ data, data_len);
+ }
+ return rc;
+}
+
+/*
+ * Context: softirq
+ */
+int oz_usb_send_isoc(void *hpd, u8 ep_num, struct urb *urb)
+{
+ struct oz_usb_ctx *usb_ctx = hpd;
+ struct oz_pd *pd = usb_ctx->pd;
+ struct oz_elt_buf *eb;
+ int i;
+ int hdr_size;
+ u8 *data;
+ struct usb_iso_packet_descriptor *desc;
+
+ if (pd->mode & OZ_F_ISOC_NO_ELTS) {
+ for (i = 0; i < urb->number_of_packets; i++) {
+ u8 *data;
+
+ desc = &urb->iso_frame_desc[i];
+ data = ((u8 *)urb->transfer_buffer)+desc->offset;
+ oz_send_isoc_unit(pd, ep_num, data, desc->length);
+ }
+ return 0;
+ }
+
+ hdr_size = sizeof(struct oz_isoc_fixed) - 1;
+ eb = &pd->elt_buff;
+ i = 0;
+ while (i < urb->number_of_packets) {
+ struct oz_elt_info *ei = oz_elt_info_alloc(eb);
+ struct oz_elt *elt;
+ struct oz_isoc_fixed *body;
+ int unit_count;
+ int unit_size;
+ int rem;
+
+ if (ei == NULL)
+ return -1;
+ rem = MAX_ISOC_FIXED_DATA;
+ elt = (struct oz_elt *)ei->data;
+ body = (struct oz_isoc_fixed *)(elt + 1);
+ body->type = OZ_USB_ENDPOINT_DATA;
+ body->endpoint = ep_num;
+ body->format = OZ_DATA_F_ISOC_FIXED;
+ unit_size = urb->iso_frame_desc[i].length;
+ body->unit_size = (u8)unit_size;
+ data = ((u8 *)(elt+1)) + hdr_size;
+ unit_count = 0;
+ while (i < urb->number_of_packets) {
+ desc = &urb->iso_frame_desc[i];
+ if ((unit_size == desc->length) &&
+ (desc->length <= rem)) {
+ memcpy(data, ((u8 *)urb->transfer_buffer) +
+ desc->offset, unit_size);
+ data += unit_size;
+ rem -= unit_size;
+ unit_count++;
+ desc->status = 0;
+ desc->actual_length = desc->length;
+ i++;
+ } else {
+ break;
+ }
+ }
+ elt->length = hdr_size + MAX_ISOC_FIXED_DATA - rem;
+ /* Store the number of units in body->frame_number for the
+ * moment. This field will be correctly determined before
+ * the element is sent. */
+ body->frame_number = (u8)unit_count;
+ oz_usb_submit_elt(eb, ei, usb_ctx, ep_num,
+ pd->mode & OZ_F_ISOC_ANYTIME);
+ }
+ return 0;
+}
+
+/*
+ * Context: softirq-serialized
+ */
+static void oz_usb_handle_ep_data(struct oz_usb_ctx *usb_ctx,
+ struct oz_usb_hdr *usb_hdr, int len)
+{
+ struct oz_data *data_hdr = (struct oz_data *)usb_hdr;
+
+ switch (data_hdr->format) {
+ case OZ_DATA_F_MULTIPLE_FIXED: {
+ struct oz_multiple_fixed *body =
+ (struct oz_multiple_fixed *)data_hdr;
+ u8 *data = body->data;
+ unsigned int n;
+ if (!body->unit_size ||
+ len < sizeof(struct oz_multiple_fixed) - 1)
+ break;
+ n = (len - (sizeof(struct oz_multiple_fixed) - 1))
+ / body->unit_size;
+ while (n--) {
+ oz_hcd_data_ind(usb_ctx->hport, body->endpoint,
+ data, body->unit_size);
+ data += body->unit_size;
+ }
+ }
+ break;
+ case OZ_DATA_F_ISOC_FIXED: {
+ struct oz_isoc_fixed *body =
+ (struct oz_isoc_fixed *)data_hdr;
+ int data_len = len-sizeof(struct oz_isoc_fixed)+1;
+ int unit_size = body->unit_size;
+ u8 *data = body->data;
+ int count;
+ int i;
+
+ if (!unit_size)
+ break;
+ count = data_len/unit_size;
+ for (i = 0; i < count; i++) {
+ oz_hcd_data_ind(usb_ctx->hport,
+ body->endpoint, data, unit_size);
+ data += unit_size;
+ }
+ }
+ break;
+ }
+
+}
+
+/*
+ * This is called when the PD has received a USB element. The type of element
+ * is determined and is then passed to an appropriate handler function.
+ * Context: softirq-serialized
+ */
+void oz_usb_rx(struct oz_pd *pd, struct oz_elt *elt)
+{
+ struct oz_usb_hdr *usb_hdr = (struct oz_usb_hdr *)(elt + 1);
+ struct oz_usb_ctx *usb_ctx;
+
+ spin_lock_bh(&pd->app_lock[OZ_APPID_USB]);
+ usb_ctx = (struct oz_usb_ctx *)pd->app_ctx[OZ_APPID_USB];
+ if (usb_ctx)
+ oz_usb_get(usb_ctx);
+ spin_unlock_bh(&pd->app_lock[OZ_APPID_USB]);
+ if (usb_ctx == NULL)
+ return; /* Context has gone so nothing to do. */
+ if (usb_ctx->stopped)
+ goto done;
+ /* If sequence number is non-zero then check it is not a duplicate.
+ * Zero sequence numbers are always accepted.
+ */
+ if (usb_hdr->elt_seq_num != 0) {
+ if (((usb_ctx->rx_seq_num - usb_hdr->elt_seq_num) & 0x80) == 0)
+ /* Reject duplicate element. */
+ goto done;
+ }
+ usb_ctx->rx_seq_num = usb_hdr->elt_seq_num;
+ switch (usb_hdr->type) {
+ case OZ_GET_DESC_RSP: {
+ struct oz_get_desc_rsp *body =
+ (struct oz_get_desc_rsp *)usb_hdr;
+ u16 offs, total_size;
+ u8 data_len;
+
+ if (elt->length < sizeof(struct oz_get_desc_rsp) - 1)
+ break;
+ data_len = elt->length -
+ (sizeof(struct oz_get_desc_rsp) - 1);
+ offs = le16_to_cpu(get_unaligned(&body->offset));
+ total_size =
+ le16_to_cpu(get_unaligned(&body->total_size));
+ oz_dbg(ON, "USB_REQ_GET_DESCRIPTOR - cnf\n");
+ oz_hcd_get_desc_cnf(usb_ctx->hport, body->req_id,
+ body->rcode, body->data,
+ data_len, offs, total_size);
+ }
+ break;
+ case OZ_SET_CONFIG_RSP: {
+ struct oz_set_config_rsp *body =
+ (struct oz_set_config_rsp *)usb_hdr;
+ oz_hcd_control_cnf(usb_ctx->hport, body->req_id,
+ body->rcode, NULL, 0);
+ }
+ break;
+ case OZ_SET_INTERFACE_RSP: {
+ struct oz_set_interface_rsp *body =
+ (struct oz_set_interface_rsp *)usb_hdr;
+ oz_hcd_control_cnf(usb_ctx->hport,
+ body->req_id, body->rcode, NULL, 0);
+ }
+ break;
+ case OZ_VENDOR_CLASS_RSP: {
+ struct oz_vendor_class_rsp *body =
+ (struct oz_vendor_class_rsp *)usb_hdr;
+ oz_hcd_control_cnf(usb_ctx->hport, body->req_id,
+ body->rcode, body->data, elt->length-
+ sizeof(struct oz_vendor_class_rsp)+1);
+ }
+ break;
+ case OZ_USB_ENDPOINT_DATA:
+ oz_usb_handle_ep_data(usb_ctx, usb_hdr, elt->length);
+ break;
+ }
+done:
+ oz_usb_put(usb_ctx);
+}
+
+/*
+ * Context: softirq, process
+ */
+void oz_usb_farewell(struct oz_pd *pd, u8 ep_num, u8 *data, u8 len)
+{
+ struct oz_usb_ctx *usb_ctx;
+
+ spin_lock_bh(&pd->app_lock[OZ_APPID_USB]);
+ usb_ctx = (struct oz_usb_ctx *)pd->app_ctx[OZ_APPID_USB];
+ if (usb_ctx)
+ oz_usb_get(usb_ctx);
+ spin_unlock_bh(&pd->app_lock[OZ_APPID_USB]);
+ if (usb_ctx == NULL)
+ return; /* Context has gone so nothing to do. */
+ if (!usb_ctx->stopped) {
+ oz_dbg(ON, "Farewell indicated ep = 0x%x\n", ep_num);
+ oz_hcd_data_ind(usb_ctx->hport, ep_num, data, len);
+ }
+ oz_usb_put(usb_ctx);
+}
diff --git a/drivers/staging/panel/Kconfig b/drivers/staging/panel/Kconfig
new file mode 100644
index 000000000..3defa0133
--- /dev/null
+++ b/drivers/staging/panel/Kconfig
@@ -0,0 +1,278 @@
+config PANEL
+ tristate "Parallel port LCD/Keypad Panel support"
+ depends on PARPORT
+ ---help---
+ Say Y here if you have an HD44780 or KS-0074 LCD connected to your
+ parallel port. This driver also features 4 and 6-key keypads. The LCD
+ is accessible through the /dev/lcd char device (10, 156), and the
+ keypad through /dev/keypad (10, 185). Both require misc device to be
+ enabled. This code can either be compiled as a module, or linked into
+ the kernel and started at boot. If you don't understand what all this
+ is about, say N.
+
+config PANEL_PARPORT
+ int "Default parallel port number (0=LPT1)"
+ depends on PANEL
+ range 0 255
+ default "0"
+ ---help---
+ This is the index of the parallel port the panel is connected to. One
+ driver instance only supports one parallel port, so if your keypad
+ and LCD are connected to two separate ports, you have to start two
+ modules with different arguments. Numbering starts with '0' for LPT1,
+ and so on.
+
+config PANEL_PROFILE
+ int "Default panel profile (0-5, 0=custom)"
+ depends on PANEL
+ range 0 5
+ default "5"
+ ---help---
+ To ease configuration, the driver supports different configuration
+ profiles for past and recent wirings. These profiles can also be
+ used to define an approximative configuration, completed by a few
+ other options. Here are the profiles :
+
+ 0 = custom (see further)
+ 1 = 2x16 parallel LCD, old keypad
+ 2 = 2x16 serial LCD (KS-0074), new keypad
+ 3 = 2x16 parallel LCD (Hantronix), no keypad
+ 4 = 2x16 parallel LCD (Nexcom NSA1045) with Nexcom's keypad
+ 5 = 2x40 parallel LCD (old one), with old keypad
+
+ Custom configurations allow you to define how your display is
+ wired to the parallel port, and how it works. This is only intended
+ for experts.
+
+config PANEL_KEYPAD
+ depends on PANEL && PANEL_PROFILE="0"
+ int "Keypad type (0=none, 1=old 6 keys, 2=new 6 keys, 3=Nexcom 4 keys)"
+ range 0 3
+ default 0
+ ---help---
+ This enables and configures a keypad connected to the parallel port.
+ The keys will be read from character device 10,185. Valid values are :
+
+ 0 : do not enable this driver
+ 1 : old 6 keys keypad
+ 2 : new 6 keys keypad, as used on the server at www.ant-computing.com
+ 3 : Nexcom NSA1045's 4 keys keypad
+
+ New profiles can be described in the driver source. The driver also
+ supports simultaneous keys pressed when the keypad supports them.
+
+config PANEL_LCD
+ depends on PANEL && PANEL_PROFILE="0"
+ int "LCD type (0=none, 1=custom, 2=old //, 3=ks0074, 4=hantronix, 5=Nexcom)"
+ range 0 5
+ default 0
+ ---help---
+ This enables and configures an LCD connected to the parallel port.
+ The driver includes an interpreter for escape codes starting with
+ '\e[L' which are specific to the LCD, and a few ANSI codes. The
+ driver will be registered as character device 10,156, usually
+ under the name '/dev/lcd'. There are a total of 6 supported types :
+
+ 0 : do not enable the driver
+ 1 : custom configuration and wiring (see further)
+ 2 : 2x16 & 2x40 parallel LCD (old wiring)
+ 3 : 2x16 serial LCD (KS-0074 based)
+ 4 : 2x16 parallel LCD (Hantronix wiring)
+ 5 : 2x16 parallel LCD (Nexcom wiring)
+
+ When type '1' is specified, other options will appear to configure
+ more precise aspects (wiring, dimensions, protocol, ...). Please note
+ that those values changed from the 2.4 driver for better consistency.
+
+config PANEL_LCD_HEIGHT
+ depends on PANEL && PANEL_PROFILE="0" && PANEL_LCD="1"
+ int "Number of lines on the LCD (1-2)"
+ range 1 2
+ default 2
+ ---help---
+ This is the number of visible character lines on the LCD in custom profile.
+ It can either be 1 or 2.
+
+config PANEL_LCD_WIDTH
+ depends on PANEL && PANEL_PROFILE="0" && PANEL_LCD="1"
+ int "Number of characters per line on the LCD (1-40)"
+ range 1 40
+ default 40
+ ---help---
+ This is the number of characters per line on the LCD in custom profile.
+ Common values are 16,20,24,40.
+
+config PANEL_LCD_BWIDTH
+ depends on PANEL && PANEL_PROFILE="0" && PANEL_LCD="1"
+ int "Internal LCD line width (1-40, 40 by default)"
+ range 1 40
+ default 40
+ ---help---
+ Most LCDs use a standard controller which supports hardware lines of 40
+ characters, although sometimes only 16, 20 or 24 of them are really wired
+ to the terminal. This results in some non-visible but addressable characters,
+ and is the case for most parallel LCDs. Other LCDs, and some serial ones,
+ however, use the same line width internally as what is visible. The KS0074
+ for example, uses 16 characters per line for 16 visible characters per line.
+
+ This option lets you configure the value used by your LCD in 'custom' profile.
+ If you don't know, put '40' here.
+
+config PANEL_LCD_HWIDTH
+ depends on PANEL && PANEL_PROFILE="0" && PANEL_LCD="1"
+ int "Hardware LCD line width (1-64, 64 by default)"
+ range 1 64
+ default 64
+ ---help---
+ Most LCDs use a single address bit to differentiate line 0 and line 1. Since
+ some of them need to be able to address 40 chars with the lower bits, they
+ often use the immediately superior power of 2, which is 64, to address the
+ next line.
+
+ If you don't know what your LCD uses, in doubt let 16 here for a 2x16, and
+ 64 here for a 2x40.
+
+config PANEL_LCD_CHARSET
+ depends on PANEL && PANEL_PROFILE="0" && PANEL_LCD="1"
+ int "LCD character set (0=normal, 1=KS0074)"
+ range 0 1
+ default 0
+ ---help---
+ Some controllers such as the KS0074 use a somewhat strange character set
+ where many symbols are at unusual places. The driver knows how to map
+ 'standard' ASCII characters to the character sets used by these controllers.
+ Valid values are :
+
+ 0 : normal (untranslated) character set
+ 1 : KS0074 character set
+
+ If you don't know, use the normal one (0).
+
+config PANEL_LCD_PROTO
+ depends on PANEL && PANEL_PROFILE="0" && PANEL_LCD="1"
+ int "LCD communication mode (0=parallel 8 bits, 1=serial)"
+ range 0 1
+ default 0
+ ---help---
+ This driver now supports any serial or parallel LCD wired to a parallel
+ port. But before assigning signals, the driver needs to know if it will
+ be driving a serial LCD or a parallel one. Serial LCDs only use 2 wires
+ (SDA/SCL), while parallel ones use 2 or 3 wires for the control signals
+ (E, RS, sometimes RW), and 4 or 8 for the data. Use 0 here for a 8 bits
+ parallel LCD, and 1 for a serial LCD.
+
+config PANEL_LCD_PIN_E
+ depends on PANEL && PANEL_PROFILE="0" && PANEL_LCD="1" && PANEL_LCD_PROTO="0"
+ int "Parallel port pin number & polarity connected to the LCD E signal (-17...17) "
+ range -17 17
+ default 14
+ ---help---
+ This describes the number of the parallel port pin to which the LCD 'E'
+ signal has been connected. It can be :
+
+ 0 : no connection (eg: connected to ground)
+ 1..17 : directly connected to any of these pins on the DB25 plug
+ -1..-17 : connected to the same pin through an inverter (eg: transistor).
+
+ Default for the 'E' pin in custom profile is '14' (AUTOFEED).
+
+config PANEL_LCD_PIN_RS
+ depends on PANEL && PANEL_PROFILE="0" && PANEL_LCD="1" && PANEL_LCD_PROTO="0"
+ int "Parallel port pin number & polarity connected to the LCD RS signal (-17...17) "
+ range -17 17
+ default 17
+ ---help---
+ This describes the number of the parallel port pin to which the LCD 'RS'
+ signal has been connected. It can be :
+
+ 0 : no connection (eg: connected to ground)
+ 1..17 : directly connected to any of these pins on the DB25 plug
+ -1..-17 : connected to the same pin through an inverter (eg: transistor).
+
+ Default for the 'RS' pin in custom profile is '17' (SELECT IN).
+
+config PANEL_LCD_PIN_RW
+ depends on PANEL && PANEL_PROFILE="0" && PANEL_LCD="1" && PANEL_LCD_PROTO="0"
+ int "Parallel port pin number & polarity connected to the LCD RW signal (-17...17) "
+ range -17 17
+ default 16
+ ---help---
+ This describes the number of the parallel port pin to which the LCD 'RW'
+ signal has been connected. It can be :
+
+ 0 : no connection (eg: connected to ground)
+ 1..17 : directly connected to any of these pins on the DB25 plug
+ -1..-17 : connected to the same pin through an inverter (eg: transistor).
+
+ Default for the 'RW' pin in custom profile is '16' (INIT).
+
+config PANEL_LCD_PIN_SCL
+ depends on PANEL && PANEL_PROFILE="0" && PANEL_LCD="1" && PANEL_LCD_PROTO!="0"
+ int "Parallel port pin number & polarity connected to the LCD SCL signal (-17...17) "
+ range -17 17
+ default 1
+ ---help---
+ This describes the number of the parallel port pin to which the serial
+ LCD 'SCL' signal has been connected. It can be :
+
+ 0 : no connection (eg: connected to ground)
+ 1..17 : directly connected to any of these pins on the DB25 plug
+ -1..-17 : connected to the same pin through an inverter (eg: transistor).
+
+ Default for the 'SCL' pin in custom profile is '1' (STROBE).
+
+config PANEL_LCD_PIN_SDA
+ depends on PANEL && PANEL_PROFILE="0" && PANEL_LCD="1" && PANEL_LCD_PROTO!="0"
+ int "Parallel port pin number & polarity connected to the LCD SDA signal (-17...17) "
+ range -17 17
+ default 2
+ ---help---
+ This describes the number of the parallel port pin to which the serial
+ LCD 'SDA' signal has been connected. It can be :
+
+ 0 : no connection (eg: connected to ground)
+ 1..17 : directly connected to any of these pins on the DB25 plug
+ -1..-17 : connected to the same pin through an inverter (eg: transistor).
+
+ Default for the 'SDA' pin in custom profile is '2' (D0).
+
+config PANEL_LCD_PIN_BL
+ depends on PANEL && PANEL_PROFILE="0" && PANEL_LCD="1"
+ int "Parallel port pin number & polarity connected to the LCD backlight signal (-17...17) "
+ range -17 17
+ default 0
+ ---help---
+ This describes the number of the parallel port pin to which the LCD 'BL' signal
+ has been connected. It can be :
+
+ 0 : no connection (eg: connected to ground)
+ 1..17 : directly connected to any of these pins on the DB25 plug
+ -1..-17 : connected to the same pin through an inverter (eg: transistor).
+
+ Default for the 'BL' pin in custom profile is '0' (uncontrolled).
+
+config PANEL_CHANGE_MESSAGE
+ depends on PANEL
+ bool "Change LCD initialization message ?"
+ default "n"
+ ---help---
+ This allows you to replace the boot message indicating the kernel version
+ and the driver version with a custom message. This is useful on appliances
+ where a simple 'Starting system' message can be enough to stop a customer
+ from worrying.
+
+ If you say 'Y' here, you'll be able to choose a message yourself. Otherwise,
+ say 'N' and keep the default message with the version.
+
+config PANEL_BOOT_MESSAGE
+ depends on PANEL && PANEL_CHANGE_MESSAGE="y"
+ string "New initialization message"
+ default ""
+ ---help---
+ This allows you to replace the boot message indicating the kernel version
+ and the driver version with a custom message. This is useful on appliances
+ where a simple 'Starting system' message can be enough to stop a customer
+ from worrying.
+
+ An empty message will only clear the display at driver init time. Any other
+ printf()-formatted message is valid with newline and escape codes.
diff --git a/drivers/staging/panel/Makefile b/drivers/staging/panel/Makefile
new file mode 100644
index 000000000..747c238b8
--- /dev/null
+++ b/drivers/staging/panel/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_PANEL) += panel.o
diff --git a/drivers/staging/panel/TODO b/drivers/staging/panel/TODO
new file mode 100644
index 000000000..2db3f994b
--- /dev/null
+++ b/drivers/staging/panel/TODO
@@ -0,0 +1,8 @@
+TODO:
+ - checkpatch.pl cleanups
+ - review major/minor usages
+ - review userspace api
+ - see if all of this could be easier done in userspace instead.
+
+Please send patches to Greg Kroah-Hartman <greg@kroah.com> and
+Willy Tarreau <willy@meta-x.org>
diff --git a/drivers/staging/panel/lcd-panel-cgram.txt b/drivers/staging/panel/lcd-panel-cgram.txt
new file mode 100644
index 000000000..7f82c9057
--- /dev/null
+++ b/drivers/staging/panel/lcd-panel-cgram.txt
@@ -0,0 +1,24 @@
+Some LCDs allow you to define up to 8 characters, mapped to ASCII
+characters 0 to 7. The escape code to define a new character is
+'\e[LG' followed by one digit from 0 to 7, representing the character
+number, and up to 8 couples of hex digits terminated by a semi-colon
+(';'). Each couple of digits represents a line, with 1-bits for each
+illuminated pixel with LSB on the right. Lines are numbered from the
+top of the character to the bottom. On a 5x7 matrix, only the 5 lower
+bits of the 7 first bytes are used for each character. If the string
+is incomplete, only complete lines will be redefined. Here are some
+examples :
+
+ printf "\e[LG0010101050D1F0C04;" => 0 = [enter]
+ printf "\e[LG1040E1F0000000000;" => 1 = [up]
+ printf "\e[LG2000000001F0E0400;" => 2 = [down]
+ printf "\e[LG3040E1F001F0E0400;" => 3 = [up-down]
+ printf "\e[LG40002060E1E0E0602;" => 4 = [left]
+ printf "\e[LG500080C0E0F0E0C08;" => 5 = [right]
+ printf "\e[LG60016051516141400;" => 6 = "IP"
+
+ printf "\e[LG00103071F1F070301;" => big speaker
+ printf "\e[LG00002061E1E060200;" => small speaker
+
+Willy
+
diff --git a/drivers/staging/panel/panel.c b/drivers/staging/panel/panel.c
new file mode 100644
index 000000000..ea54fb4ec
--- /dev/null
+++ b/drivers/staging/panel/panel.c
@@ -0,0 +1,2436 @@
+/*
+ * Front panel driver for Linux
+ * Copyright (C) 2000-2008, Willy Tarreau <w@1wt.eu>
+ *
+ * This program is free software; you can redistribute it and/or
+ * 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 code drives an LCD module (/dev/lcd), and a keypad (/dev/keypad)
+ * connected to a parallel printer port.
+ *
+ * The LCD module may either be an HD44780-like 8-bit parallel LCD, or a 1-bit
+ * serial module compatible with Samsung's KS0074. The pins may be connected in
+ * any combination, everything is programmable.
+ *
+ * The keypad consists in a matrix of push buttons connecting input pins to
+ * data output pins or to the ground. The combinations have to be hard-coded
+ * in the driver, though several profiles exist and adding new ones is easy.
+ *
+ * Several profiles are provided for commonly found LCD+keypad modules on the
+ * market, such as those found in Nexcom's appliances.
+ *
+ * FIXME:
+ * - the initialization/deinitialization process is very dirty and should
+ * be rewritten. It may even be buggy.
+ *
+ * TODO:
+ * - document 24 keys keyboard (3 rows of 8 cols, 32 diodes + 2 inputs)
+ * - make the LCD a part of a virtual screen of Vx*Vy
+ * - make the inputs list smp-safe
+ * - change the keyboard to a double mapping : signals -> key_id -> values
+ * so that applications can change values without knowing signals
+ *
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/module.h>
+
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/spinlock.h>
+#include <linux/interrupt.h>
+#include <linux/miscdevice.h>
+#include <linux/slab.h>
+#include <linux/ioport.h>
+#include <linux/fcntl.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <linux/ctype.h>
+#include <linux/parport.h>
+#include <linux/list.h>
+#include <linux/notifier.h>
+#include <linux/reboot.h>
+#include <generated/utsrelease.h>
+
+#include <linux/io.h>
+#include <linux/uaccess.h>
+
+#define LCD_MINOR 156
+#define KEYPAD_MINOR 185
+
+#define PANEL_VERSION "0.9.5"
+
+#define LCD_MAXBYTES 256 /* max burst write */
+
+#define KEYPAD_BUFFER 64
+
+/* poll the keyboard this every second */
+#define INPUT_POLL_TIME (HZ/50)
+/* a key starts to repeat after this times INPUT_POLL_TIME */
+#define KEYPAD_REP_START (10)
+/* a key repeats this times INPUT_POLL_TIME */
+#define KEYPAD_REP_DELAY (2)
+
+/* keep the light on this times INPUT_POLL_TIME for each flash */
+#define FLASH_LIGHT_TEMPO (200)
+
+/* converts an r_str() input to an active high, bits string : 000BAOSE */
+#define PNL_PINPUT(a) ((((unsigned char)(a)) ^ 0x7F) >> 3)
+
+#define PNL_PBUSY 0x80 /* inverted input, active low */
+#define PNL_PACK 0x40 /* direct input, active low */
+#define PNL_POUTPA 0x20 /* direct input, active high */
+#define PNL_PSELECD 0x10 /* direct input, active high */
+#define PNL_PERRORP 0x08 /* direct input, active low */
+
+#define PNL_PBIDIR 0x20 /* bi-directional ports */
+/* high to read data in or-ed with data out */
+#define PNL_PINTEN 0x10
+#define PNL_PSELECP 0x08 /* inverted output, active low */
+#define PNL_PINITP 0x04 /* direct output, active low */
+#define PNL_PAUTOLF 0x02 /* inverted output, active low */
+#define PNL_PSTROBE 0x01 /* inverted output */
+
+#define PNL_PD0 0x01
+#define PNL_PD1 0x02
+#define PNL_PD2 0x04
+#define PNL_PD3 0x08
+#define PNL_PD4 0x10
+#define PNL_PD5 0x20
+#define PNL_PD6 0x40
+#define PNL_PD7 0x80
+
+#define PIN_NONE 0
+#define PIN_STROBE 1
+#define PIN_D0 2
+#define PIN_D1 3
+#define PIN_D2 4
+#define PIN_D3 5
+#define PIN_D4 6
+#define PIN_D5 7
+#define PIN_D6 8
+#define PIN_D7 9
+#define PIN_AUTOLF 14
+#define PIN_INITP 16
+#define PIN_SELECP 17
+#define PIN_NOT_SET 127
+
+#define LCD_FLAG_S 0x0001
+#define LCD_FLAG_ID 0x0002
+#define LCD_FLAG_B 0x0004 /* blink on */
+#define LCD_FLAG_C 0x0008 /* cursor on */
+#define LCD_FLAG_D 0x0010 /* display on */
+#define LCD_FLAG_F 0x0020 /* large font mode */
+#define LCD_FLAG_N 0x0040 /* 2-rows mode */
+#define LCD_FLAG_L 0x0080 /* backlight enabled */
+
+/* LCD commands */
+#define LCD_CMD_DISPLAY_CLEAR 0x01 /* Clear entire display */
+
+#define LCD_CMD_ENTRY_MODE 0x04 /* Set entry mode */
+#define LCD_CMD_CURSOR_INC 0x02 /* Increment cursor */
+
+#define LCD_CMD_DISPLAY_CTRL 0x08 /* Display control */
+#define LCD_CMD_DISPLAY_ON 0x04 /* Set display on */
+#define LCD_CMD_CURSOR_ON 0x02 /* Set cursor on */
+#define LCD_CMD_BLINK_ON 0x01 /* Set blink on */
+
+#define LCD_CMD_SHIFT 0x10 /* Shift cursor/display */
+#define LCD_CMD_DISPLAY_SHIFT 0x08 /* Shift display instead of cursor */
+#define LCD_CMD_SHIFT_RIGHT 0x04 /* Shift display/cursor to the right */
+
+#define LCD_CMD_FUNCTION_SET 0x20 /* Set function */
+#define LCD_CMD_DATA_LEN_8BITS 0x10 /* Set data length to 8 bits */
+#define LCD_CMD_TWO_LINES 0x08 /* Set to two display lines */
+#define LCD_CMD_FONT_5X10_DOTS 0x04 /* Set char font to 5x10 dots */
+
+#define LCD_CMD_SET_CGRAM_ADDR 0x40 /* Set char generator RAM address */
+
+#define LCD_CMD_SET_DDRAM_ADDR 0x80 /* Set display data RAM address */
+
+#define LCD_ESCAPE_LEN 24 /* max chars for LCD escape command */
+#define LCD_ESCAPE_CHAR 27 /* use char 27 for escape command */
+
+#define NOT_SET -1
+
+/* macros to simplify use of the parallel port */
+#define r_ctr(x) (parport_read_control((x)->port))
+#define r_dtr(x) (parport_read_data((x)->port))
+#define r_str(x) (parport_read_status((x)->port))
+#define w_ctr(x, y) (parport_write_control((x)->port, (y)))
+#define w_dtr(x, y) (parport_write_data((x)->port, (y)))
+
+/* this defines which bits are to be used and which ones to be ignored */
+/* logical or of the output bits involved in the scan matrix */
+static __u8 scan_mask_o;
+/* logical or of the input bits involved in the scan matrix */
+static __u8 scan_mask_i;
+
+typedef __u64 pmask_t;
+
+enum input_type {
+ INPUT_TYPE_STD,
+ INPUT_TYPE_KBD,
+};
+
+enum input_state {
+ INPUT_ST_LOW,
+ INPUT_ST_RISING,
+ INPUT_ST_HIGH,
+ INPUT_ST_FALLING,
+};
+
+struct logical_input {
+ struct list_head list;
+ pmask_t mask;
+ pmask_t value;
+ enum input_type type;
+ enum input_state state;
+ __u8 rise_time, fall_time;
+ __u8 rise_timer, fall_timer, high_timer;
+
+ union {
+ struct { /* valid when type == INPUT_TYPE_STD */
+ void (*press_fct)(int);
+ void (*release_fct)(int);
+ int press_data;
+ int release_data;
+ } std;
+ struct { /* valid when type == INPUT_TYPE_KBD */
+ /* strings can be non null-terminated */
+ char press_str[sizeof(void *) + sizeof(int)];
+ char repeat_str[sizeof(void *) + sizeof(int)];
+ char release_str[sizeof(void *) + sizeof(int)];
+ } kbd;
+ } u;
+};
+
+static LIST_HEAD(logical_inputs); /* list of all defined logical inputs */
+
+/* physical contacts history
+ * Physical contacts are a 45 bits string of 9 groups of 5 bits each.
+ * The 8 lower groups correspond to output bits 0 to 7, and the 9th group
+ * corresponds to the ground.
+ * Within each group, bits are stored in the same order as read on the port :
+ * BAPSE (busy=4, ack=3, paper empty=2, select=1, error=0).
+ * So, each __u64 (or pmask_t) is represented like this :
+ * 0000000000000000000BAPSEBAPSEBAPSEBAPSEBAPSEBAPSEBAPSEBAPSEBAPSE
+ * <-----unused------><gnd><d07><d06><d05><d04><d03><d02><d01><d00>
+ */
+
+/* what has just been read from the I/O ports */
+static pmask_t phys_read;
+/* previous phys_read */
+static pmask_t phys_read_prev;
+/* stabilized phys_read (phys_read|phys_read_prev) */
+static pmask_t phys_curr;
+/* previous phys_curr */
+static pmask_t phys_prev;
+/* 0 means that at least one logical signal needs be computed */
+static char inputs_stable;
+
+/* these variables are specific to the keypad */
+static struct {
+ bool enabled;
+} keypad;
+
+static char keypad_buffer[KEYPAD_BUFFER];
+static int keypad_buflen;
+static int keypad_start;
+static char keypressed;
+static wait_queue_head_t keypad_read_wait;
+
+/* lcd-specific variables */
+static struct {
+ bool enabled;
+ bool initialized;
+ bool must_clear;
+
+ int height;
+ int width;
+ int bwidth;
+ int hwidth;
+ int charset;
+ int proto;
+ int light_tempo;
+
+ /* TODO: use union here? */
+ struct {
+ int e;
+ int rs;
+ int rw;
+ int cl;
+ int da;
+ int bl;
+ } pins;
+
+ /* contains the LCD config state */
+ unsigned long int flags;
+
+ /* Contains the LCD X and Y offset */
+ struct {
+ unsigned long int x;
+ unsigned long int y;
+ } addr;
+
+ /* Current escape sequence and it's length or -1 if outside */
+ struct {
+ char buf[LCD_ESCAPE_LEN + 1];
+ int len;
+ } esc_seq;
+} lcd;
+
+/* Needed only for init */
+static int selected_lcd_type = NOT_SET;
+
+/*
+ * Bit masks to convert LCD signals to parallel port outputs.
+ * _d_ are values for data port, _c_ are for control port.
+ * [0] = signal OFF, [1] = signal ON, [2] = mask
+ */
+#define BIT_CLR 0
+#define BIT_SET 1
+#define BIT_MSK 2
+#define BIT_STATES 3
+/*
+ * one entry for each bit on the LCD
+ */
+#define LCD_BIT_E 0
+#define LCD_BIT_RS 1
+#define LCD_BIT_RW 2
+#define LCD_BIT_BL 3
+#define LCD_BIT_CL 4
+#define LCD_BIT_DA 5
+#define LCD_BITS 6
+
+/*
+ * each bit can be either connected to a DATA or CTRL port
+ */
+#define LCD_PORT_C 0
+#define LCD_PORT_D 1
+#define LCD_PORTS 2
+
+static unsigned char lcd_bits[LCD_PORTS][LCD_BITS][BIT_STATES];
+
+/*
+ * LCD protocols
+ */
+#define LCD_PROTO_PARALLEL 0
+#define LCD_PROTO_SERIAL 1
+#define LCD_PROTO_TI_DA8XX_LCD 2
+
+/*
+ * LCD character sets
+ */
+#define LCD_CHARSET_NORMAL 0
+#define LCD_CHARSET_KS0074 1
+
+/*
+ * LCD types
+ */
+#define LCD_TYPE_NONE 0
+#define LCD_TYPE_CUSTOM 1
+#define LCD_TYPE_OLD 2
+#define LCD_TYPE_KS0074 3
+#define LCD_TYPE_HANTRONIX 4
+#define LCD_TYPE_NEXCOM 5
+
+/*
+ * keypad types
+ */
+#define KEYPAD_TYPE_NONE 0
+#define KEYPAD_TYPE_OLD 1
+#define KEYPAD_TYPE_NEW 2
+#define KEYPAD_TYPE_NEXCOM 3
+
+/*
+ * panel profiles
+ */
+#define PANEL_PROFILE_CUSTOM 0
+#define PANEL_PROFILE_OLD 1
+#define PANEL_PROFILE_NEW 2
+#define PANEL_PROFILE_HANTRONIX 3
+#define PANEL_PROFILE_NEXCOM 4
+#define PANEL_PROFILE_LARGE 5
+
+/*
+ * Construct custom config from the kernel's configuration
+ */
+#define DEFAULT_PARPORT 0
+#define DEFAULT_PROFILE PANEL_PROFILE_LARGE
+#define DEFAULT_KEYPAD_TYPE KEYPAD_TYPE_OLD
+#define DEFAULT_LCD_TYPE LCD_TYPE_OLD
+#define DEFAULT_LCD_HEIGHT 2
+#define DEFAULT_LCD_WIDTH 40
+#define DEFAULT_LCD_BWIDTH 40
+#define DEFAULT_LCD_HWIDTH 64
+#define DEFAULT_LCD_CHARSET LCD_CHARSET_NORMAL
+#define DEFAULT_LCD_PROTO LCD_PROTO_PARALLEL
+
+#define DEFAULT_LCD_PIN_E PIN_AUTOLF
+#define DEFAULT_LCD_PIN_RS PIN_SELECP
+#define DEFAULT_LCD_PIN_RW PIN_INITP
+#define DEFAULT_LCD_PIN_SCL PIN_STROBE
+#define DEFAULT_LCD_PIN_SDA PIN_D0
+#define DEFAULT_LCD_PIN_BL PIN_NOT_SET
+
+#ifdef CONFIG_PANEL_PARPORT
+#undef DEFAULT_PARPORT
+#define DEFAULT_PARPORT CONFIG_PANEL_PARPORT
+#endif
+
+#ifdef CONFIG_PANEL_PROFILE
+#undef DEFAULT_PROFILE
+#define DEFAULT_PROFILE CONFIG_PANEL_PROFILE
+#endif
+
+#if DEFAULT_PROFILE == 0 /* custom */
+#ifdef CONFIG_PANEL_KEYPAD
+#undef DEFAULT_KEYPAD_TYPE
+#define DEFAULT_KEYPAD_TYPE CONFIG_PANEL_KEYPAD
+#endif
+
+#ifdef CONFIG_PANEL_LCD
+#undef DEFAULT_LCD_TYPE
+#define DEFAULT_LCD_TYPE CONFIG_PANEL_LCD
+#endif
+
+#ifdef CONFIG_PANEL_LCD_HEIGHT
+#undef DEFAULT_LCD_HEIGHT
+#define DEFAULT_LCD_HEIGHT CONFIG_PANEL_LCD_HEIGHT
+#endif
+
+#ifdef CONFIG_PANEL_LCD_WIDTH
+#undef DEFAULT_LCD_WIDTH
+#define DEFAULT_LCD_WIDTH CONFIG_PANEL_LCD_WIDTH
+#endif
+
+#ifdef CONFIG_PANEL_LCD_BWIDTH
+#undef DEFAULT_LCD_BWIDTH
+#define DEFAULT_LCD_BWIDTH CONFIG_PANEL_LCD_BWIDTH
+#endif
+
+#ifdef CONFIG_PANEL_LCD_HWIDTH
+#undef DEFAULT_LCD_HWIDTH
+#define DEFAULT_LCD_HWIDTH CONFIG_PANEL_LCD_HWIDTH
+#endif
+
+#ifdef CONFIG_PANEL_LCD_CHARSET
+#undef DEFAULT_LCD_CHARSET
+#define DEFAULT_LCD_CHARSET CONFIG_PANEL_LCD_CHARSET
+#endif
+
+#ifdef CONFIG_PANEL_LCD_PROTO
+#undef DEFAULT_LCD_PROTO
+#define DEFAULT_LCD_PROTO CONFIG_PANEL_LCD_PROTO
+#endif
+
+#ifdef CONFIG_PANEL_LCD_PIN_E
+#undef DEFAULT_LCD_PIN_E
+#define DEFAULT_LCD_PIN_E CONFIG_PANEL_LCD_PIN_E
+#endif
+
+#ifdef CONFIG_PANEL_LCD_PIN_RS
+#undef DEFAULT_LCD_PIN_RS
+#define DEFAULT_LCD_PIN_RS CONFIG_PANEL_LCD_PIN_RS
+#endif
+
+#ifdef CONFIG_PANEL_LCD_PIN_RW
+#undef DEFAULT_LCD_PIN_RW
+#define DEFAULT_LCD_PIN_RW CONFIG_PANEL_LCD_PIN_RW
+#endif
+
+#ifdef CONFIG_PANEL_LCD_PIN_SCL
+#undef DEFAULT_LCD_PIN_SCL
+#define DEFAULT_LCD_PIN_SCL CONFIG_PANEL_LCD_PIN_SCL
+#endif
+
+#ifdef CONFIG_PANEL_LCD_PIN_SDA
+#undef DEFAULT_LCD_PIN_SDA
+#define DEFAULT_LCD_PIN_SDA CONFIG_PANEL_LCD_PIN_SDA
+#endif
+
+#ifdef CONFIG_PANEL_LCD_PIN_BL
+#undef DEFAULT_LCD_PIN_BL
+#define DEFAULT_LCD_PIN_BL CONFIG_PANEL_LCD_PIN_BL
+#endif
+
+#endif /* DEFAULT_PROFILE == 0 */
+
+/* global variables */
+
+/* Device single-open policy control */
+static atomic_t lcd_available = ATOMIC_INIT(1);
+static atomic_t keypad_available = ATOMIC_INIT(1);
+
+static struct pardevice *pprt;
+
+static int keypad_initialized;
+
+static void (*lcd_write_cmd)(int);
+static void (*lcd_write_data)(int);
+static void (*lcd_clear_fast)(void);
+
+static DEFINE_SPINLOCK(pprt_lock);
+static struct timer_list scan_timer;
+
+MODULE_DESCRIPTION("Generic parallel port LCD/Keypad driver");
+
+static int parport = DEFAULT_PARPORT;
+module_param(parport, int, 0000);
+MODULE_PARM_DESC(parport, "Parallel port index (0=lpt1, 1=lpt2, ...)");
+
+static int profile = DEFAULT_PROFILE;
+module_param(profile, int, 0000);
+MODULE_PARM_DESC(profile,
+ "1=16x2 old kp; 2=serial 16x2, new kp; 3=16x2 hantronix; "
+ "4=16x2 nexcom; default=40x2, old kp");
+
+static int keypad_type = NOT_SET;
+module_param(keypad_type, int, 0000);
+MODULE_PARM_DESC(keypad_type,
+ "Keypad type: 0=none, 1=old 6 keys, 2=new 6+1 keys, 3=nexcom 4 keys");
+
+static int lcd_type = NOT_SET;
+module_param(lcd_type, int, 0000);
+MODULE_PARM_DESC(lcd_type,
+ "LCD type: 0=none, 1=compiled-in, 2=old, 3=serial ks0074, 4=hantronix, 5=nexcom");
+
+static int lcd_height = NOT_SET;
+module_param(lcd_height, int, 0000);
+MODULE_PARM_DESC(lcd_height, "Number of lines on the LCD");
+
+static int lcd_width = NOT_SET;
+module_param(lcd_width, int, 0000);
+MODULE_PARM_DESC(lcd_width, "Number of columns on the LCD");
+
+static int lcd_bwidth = NOT_SET; /* internal buffer width (usually 40) */
+module_param(lcd_bwidth, int, 0000);
+MODULE_PARM_DESC(lcd_bwidth, "Internal LCD line width (40)");
+
+static int lcd_hwidth = NOT_SET; /* hardware buffer width (usually 64) */
+module_param(lcd_hwidth, int, 0000);
+MODULE_PARM_DESC(lcd_hwidth, "LCD line hardware address (64)");
+
+static int lcd_charset = NOT_SET;
+module_param(lcd_charset, int, 0000);
+MODULE_PARM_DESC(lcd_charset, "LCD character set: 0=standard, 1=KS0074");
+
+static int lcd_proto = NOT_SET;
+module_param(lcd_proto, int, 0000);
+MODULE_PARM_DESC(lcd_proto,
+ "LCD communication: 0=parallel (//), 1=serial, 2=TI LCD Interface");
+
+/*
+ * These are the parallel port pins the LCD control signals are connected to.
+ * Set this to 0 if the signal is not used. Set it to its opposite value
+ * (negative) if the signal is negated. -MAXINT is used to indicate that the
+ * pin has not been explicitly specified.
+ *
+ * WARNING! no check will be performed about collisions with keypad !
+ */
+
+static int lcd_e_pin = PIN_NOT_SET;
+module_param(lcd_e_pin, int, 0000);
+MODULE_PARM_DESC(lcd_e_pin,
+ "# of the // port pin connected to LCD 'E' signal, with polarity (-17..17)");
+
+static int lcd_rs_pin = PIN_NOT_SET;
+module_param(lcd_rs_pin, int, 0000);
+MODULE_PARM_DESC(lcd_rs_pin,
+ "# of the // port pin connected to LCD 'RS' signal, with polarity (-17..17)");
+
+static int lcd_rw_pin = PIN_NOT_SET;
+module_param(lcd_rw_pin, int, 0000);
+MODULE_PARM_DESC(lcd_rw_pin,
+ "# of the // port pin connected to LCD 'RW' signal, with polarity (-17..17)");
+
+static int lcd_cl_pin = PIN_NOT_SET;
+module_param(lcd_cl_pin, int, 0000);
+MODULE_PARM_DESC(lcd_cl_pin,
+ "# of the // port pin connected to serial LCD 'SCL' signal, with polarity (-17..17)");
+
+static int lcd_da_pin = PIN_NOT_SET;
+module_param(lcd_da_pin, int, 0000);
+MODULE_PARM_DESC(lcd_da_pin,
+ "# of the // port pin connected to serial LCD 'SDA' signal, with polarity (-17..17)");
+
+static int lcd_bl_pin = PIN_NOT_SET;
+module_param(lcd_bl_pin, int, 0000);
+MODULE_PARM_DESC(lcd_bl_pin,
+ "# of the // port pin connected to LCD backlight, with polarity (-17..17)");
+
+/* Deprecated module parameters - consider not using them anymore */
+
+static int lcd_enabled = NOT_SET;
+module_param(lcd_enabled, int, 0000);
+MODULE_PARM_DESC(lcd_enabled, "Deprecated option, use lcd_type instead");
+
+static int keypad_enabled = NOT_SET;
+module_param(keypad_enabled, int, 0000);
+MODULE_PARM_DESC(keypad_enabled, "Deprecated option, use keypad_type instead");
+
+
+static const unsigned char *lcd_char_conv;
+
+/* for some LCD drivers (ks0074) we need a charset conversion table. */
+static const unsigned char lcd_char_conv_ks0074[256] = {
+ /* 0|8 1|9 2|A 3|B 4|C 5|D 6|E 7|F */
+ /* 0x00 */ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ /* 0x08 */ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ /* 0x10 */ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+ /* 0x18 */ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
+ /* 0x20 */ 0x20, 0x21, 0x22, 0x23, 0xa2, 0x25, 0x26, 0x27,
+ /* 0x28 */ 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
+ /* 0x30 */ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
+ /* 0x38 */ 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
+ /* 0x40 */ 0xa0, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
+ /* 0x48 */ 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
+ /* 0x50 */ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
+ /* 0x58 */ 0x58, 0x59, 0x5a, 0xfa, 0xfb, 0xfc, 0x1d, 0xc4,
+ /* 0x60 */ 0x96, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,
+ /* 0x68 */ 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,
+ /* 0x70 */ 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77,
+ /* 0x78 */ 0x78, 0x79, 0x7a, 0xfd, 0xfe, 0xff, 0xce, 0x20,
+ /* 0x80 */ 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
+ /* 0x88 */ 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f,
+ /* 0x90 */ 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97,
+ /* 0x98 */ 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,
+ /* 0xA0 */ 0x20, 0x40, 0xb1, 0xa1, 0x24, 0xa3, 0xfe, 0x5f,
+ /* 0xA8 */ 0x22, 0xc8, 0x61, 0x14, 0x97, 0x2d, 0xad, 0x96,
+ /* 0xB0 */ 0x80, 0x8c, 0x82, 0x83, 0x27, 0x8f, 0x86, 0xdd,
+ /* 0xB8 */ 0x2c, 0x81, 0x6f, 0x15, 0x8b, 0x8a, 0x84, 0x60,
+ /* 0xC0 */ 0xe2, 0xe2, 0xe2, 0x5b, 0x5b, 0xae, 0xbc, 0xa9,
+ /* 0xC8 */ 0xc5, 0xbf, 0xc6, 0xf1, 0xe3, 0xe3, 0xe3, 0xe3,
+ /* 0xD0 */ 0x44, 0x5d, 0xa8, 0xe4, 0xec, 0xec, 0x5c, 0x78,
+ /* 0xD8 */ 0xab, 0xa6, 0xe5, 0x5e, 0x5e, 0xe6, 0xaa, 0xbe,
+ /* 0xE0 */ 0x7f, 0xe7, 0xaf, 0x7b, 0x7b, 0xaf, 0xbd, 0xc8,
+ /* 0xE8 */ 0xa4, 0xa5, 0xc7, 0xf6, 0xa7, 0xe8, 0x69, 0x69,
+ /* 0xF0 */ 0xed, 0x7d, 0xa8, 0xe4, 0xec, 0x5c, 0x5c, 0x25,
+ /* 0xF8 */ 0xac, 0xa6, 0xea, 0xef, 0x7e, 0xeb, 0xb2, 0x79,
+};
+
+static const char old_keypad_profile[][4][9] = {
+ {"S0", "Left\n", "Left\n", ""},
+ {"S1", "Down\n", "Down\n", ""},
+ {"S2", "Up\n", "Up\n", ""},
+ {"S3", "Right\n", "Right\n", ""},
+ {"S4", "Esc\n", "Esc\n", ""},
+ {"S5", "Ret\n", "Ret\n", ""},
+ {"", "", "", ""}
+};
+
+/* signals, press, repeat, release */
+static const char new_keypad_profile[][4][9] = {
+ {"S0", "Left\n", "Left\n", ""},
+ {"S1", "Down\n", "Down\n", ""},
+ {"S2", "Up\n", "Up\n", ""},
+ {"S3", "Right\n", "Right\n", ""},
+ {"S4s5", "", "Esc\n", "Esc\n"},
+ {"s4S5", "", "Ret\n", "Ret\n"},
+ {"S4S5", "Help\n", "", ""},
+ /* add new signals above this line */
+ {"", "", "", ""}
+};
+
+/* signals, press, repeat, release */
+static const char nexcom_keypad_profile[][4][9] = {
+ {"a-p-e-", "Down\n", "Down\n", ""},
+ {"a-p-E-", "Ret\n", "Ret\n", ""},
+ {"a-P-E-", "Esc\n", "Esc\n", ""},
+ {"a-P-e-", "Up\n", "Up\n", ""},
+ /* add new signals above this line */
+ {"", "", "", ""}
+};
+
+static const char (*keypad_profile)[4][9] = old_keypad_profile;
+
+/* FIXME: this should be converted to a bit array containing signals states */
+static struct {
+ unsigned char e; /* parallel LCD E (data latch on falling edge) */
+ unsigned char rs; /* parallel LCD RS (0 = cmd, 1 = data) */
+ unsigned char rw; /* parallel LCD R/W (0 = W, 1 = R) */
+ unsigned char bl; /* parallel LCD backlight (0 = off, 1 = on) */
+ unsigned char cl; /* serial LCD clock (latch on rising edge) */
+ unsigned char da; /* serial LCD data */
+} bits;
+
+static void init_scan_timer(void);
+
+/* sets data port bits according to current signals values */
+static int set_data_bits(void)
+{
+ int val, bit;
+
+ val = r_dtr(pprt);
+ for (bit = 0; bit < LCD_BITS; bit++)
+ val &= lcd_bits[LCD_PORT_D][bit][BIT_MSK];
+
+ val |= lcd_bits[LCD_PORT_D][LCD_BIT_E][bits.e]
+ | lcd_bits[LCD_PORT_D][LCD_BIT_RS][bits.rs]
+ | lcd_bits[LCD_PORT_D][LCD_BIT_RW][bits.rw]
+ | lcd_bits[LCD_PORT_D][LCD_BIT_BL][bits.bl]
+ | lcd_bits[LCD_PORT_D][LCD_BIT_CL][bits.cl]
+ | lcd_bits[LCD_PORT_D][LCD_BIT_DA][bits.da];
+
+ w_dtr(pprt, val);
+ return val;
+}
+
+/* sets ctrl port bits according to current signals values */
+static int set_ctrl_bits(void)
+{
+ int val, bit;
+
+ val = r_ctr(pprt);
+ for (bit = 0; bit < LCD_BITS; bit++)
+ val &= lcd_bits[LCD_PORT_C][bit][BIT_MSK];
+
+ val |= lcd_bits[LCD_PORT_C][LCD_BIT_E][bits.e]
+ | lcd_bits[LCD_PORT_C][LCD_BIT_RS][bits.rs]
+ | lcd_bits[LCD_PORT_C][LCD_BIT_RW][bits.rw]
+ | lcd_bits[LCD_PORT_C][LCD_BIT_BL][bits.bl]
+ | lcd_bits[LCD_PORT_C][LCD_BIT_CL][bits.cl]
+ | lcd_bits[LCD_PORT_C][LCD_BIT_DA][bits.da];
+
+ w_ctr(pprt, val);
+ return val;
+}
+
+/* sets ctrl & data port bits according to current signals values */
+static void panel_set_bits(void)
+{
+ set_data_bits();
+ set_ctrl_bits();
+}
+
+/*
+ * Converts a parallel port pin (from -25 to 25) to data and control ports
+ * masks, and data and control port bits. The signal will be considered
+ * unconnected if it's on pin 0 or an invalid pin (<-25 or >25).
+ *
+ * Result will be used this way :
+ * out(dport, in(dport) & d_val[2] | d_val[signal_state])
+ * out(cport, in(cport) & c_val[2] | c_val[signal_state])
+ */
+static void pin_to_bits(int pin, unsigned char *d_val, unsigned char *c_val)
+{
+ int d_bit, c_bit, inv;
+
+ d_val[0] = 0;
+ c_val[0] = 0;
+ d_val[1] = 0;
+ c_val[1] = 0;
+ d_val[2] = 0xFF;
+ c_val[2] = 0xFF;
+
+ if (pin == 0)
+ return;
+
+ inv = (pin < 0);
+ if (inv)
+ pin = -pin;
+
+ d_bit = 0;
+ c_bit = 0;
+
+ switch (pin) {
+ case PIN_STROBE: /* strobe, inverted */
+ c_bit = PNL_PSTROBE;
+ inv = !inv;
+ break;
+ case PIN_D0...PIN_D7: /* D0 - D7 = 2 - 9 */
+ d_bit = 1 << (pin - 2);
+ break;
+ case PIN_AUTOLF: /* autofeed, inverted */
+ c_bit = PNL_PAUTOLF;
+ inv = !inv;
+ break;
+ case PIN_INITP: /* init, direct */
+ c_bit = PNL_PINITP;
+ break;
+ case PIN_SELECP: /* select_in, inverted */
+ c_bit = PNL_PSELECP;
+ inv = !inv;
+ break;
+ default: /* unknown pin, ignore */
+ break;
+ }
+
+ if (c_bit) {
+ c_val[2] &= ~c_bit;
+ c_val[!inv] = c_bit;
+ } else if (d_bit) {
+ d_val[2] &= ~d_bit;
+ d_val[!inv] = d_bit;
+ }
+}
+
+/* sleeps that many milliseconds with a reschedule */
+static void long_sleep(int ms)
+{
+ if (in_interrupt()) {
+ mdelay(ms);
+ } else {
+ __set_current_state(TASK_INTERRUPTIBLE);
+ schedule_timeout((ms * HZ + 999) / 1000);
+ }
+}
+
+/* send a serial byte to the LCD panel. The caller is responsible for locking
+ if needed. */
+static void lcd_send_serial(int byte)
+{
+ int bit;
+
+ /* the data bit is set on D0, and the clock on STROBE.
+ * LCD reads D0 on STROBE's rising edge. */
+ for (bit = 0; bit < 8; bit++) {
+ bits.cl = BIT_CLR; /* CLK low */
+ panel_set_bits();
+ bits.da = byte & 1;
+ panel_set_bits();
+ udelay(2); /* maintain the data during 2 us before CLK up */
+ bits.cl = BIT_SET; /* CLK high */
+ panel_set_bits();
+ udelay(1); /* maintain the strobe during 1 us */
+ byte >>= 1;
+ }
+}
+
+/* turn the backlight on or off */
+static void lcd_backlight(int on)
+{
+ if (lcd.pins.bl == PIN_NONE)
+ return;
+
+ /* The backlight is activated by setting the AUTOFEED line to +5V */
+ spin_lock_irq(&pprt_lock);
+ bits.bl = on;
+ panel_set_bits();
+ spin_unlock_irq(&pprt_lock);
+}
+
+/* send a command to the LCD panel in serial mode */
+static void lcd_write_cmd_s(int cmd)
+{
+ spin_lock_irq(&pprt_lock);
+ lcd_send_serial(0x1F); /* R/W=W, RS=0 */
+ lcd_send_serial(cmd & 0x0F);
+ lcd_send_serial((cmd >> 4) & 0x0F);
+ udelay(40); /* the shortest command takes at least 40 us */
+ spin_unlock_irq(&pprt_lock);
+}
+
+/* send data to the LCD panel in serial mode */
+static void lcd_write_data_s(int data)
+{
+ spin_lock_irq(&pprt_lock);
+ lcd_send_serial(0x5F); /* R/W=W, RS=1 */
+ lcd_send_serial(data & 0x0F);
+ lcd_send_serial((data >> 4) & 0x0F);
+ udelay(40); /* the shortest data takes at least 40 us */
+ spin_unlock_irq(&pprt_lock);
+}
+
+/* send a command to the LCD panel in 8 bits parallel mode */
+static void lcd_write_cmd_p8(int cmd)
+{
+ spin_lock_irq(&pprt_lock);
+ /* present the data to the data port */
+ w_dtr(pprt, cmd);
+ udelay(20); /* maintain the data during 20 us before the strobe */
+
+ bits.e = BIT_SET;
+ bits.rs = BIT_CLR;
+ bits.rw = BIT_CLR;
+ set_ctrl_bits();
+
+ udelay(40); /* maintain the strobe during 40 us */
+
+ bits.e = BIT_CLR;
+ set_ctrl_bits();
+
+ udelay(120); /* the shortest command takes at least 120 us */
+ spin_unlock_irq(&pprt_lock);
+}
+
+/* send data to the LCD panel in 8 bits parallel mode */
+static void lcd_write_data_p8(int data)
+{
+ spin_lock_irq(&pprt_lock);
+ /* present the data to the data port */
+ w_dtr(pprt, data);
+ udelay(20); /* maintain the data during 20 us before the strobe */
+
+ bits.e = BIT_SET;
+ bits.rs = BIT_SET;
+ bits.rw = BIT_CLR;
+ set_ctrl_bits();
+
+ udelay(40); /* maintain the strobe during 40 us */
+
+ bits.e = BIT_CLR;
+ set_ctrl_bits();
+
+ udelay(45); /* the shortest data takes at least 45 us */
+ spin_unlock_irq(&pprt_lock);
+}
+
+/* send a command to the TI LCD panel */
+static void lcd_write_cmd_tilcd(int cmd)
+{
+ spin_lock_irq(&pprt_lock);
+ /* present the data to the control port */
+ w_ctr(pprt, cmd);
+ udelay(60);
+ spin_unlock_irq(&pprt_lock);
+}
+
+/* send data to the TI LCD panel */
+static void lcd_write_data_tilcd(int data)
+{
+ spin_lock_irq(&pprt_lock);
+ /* present the data to the data port */
+ w_dtr(pprt, data);
+ udelay(60);
+ spin_unlock_irq(&pprt_lock);
+}
+
+static void lcd_gotoxy(void)
+{
+ lcd_write_cmd(LCD_CMD_SET_DDRAM_ADDR
+ | (lcd.addr.y ? lcd.hwidth : 0)
+ /* we force the cursor to stay at the end of the
+ line if it wants to go farther */
+ | ((lcd.addr.x < lcd.bwidth) ? lcd.addr.x &
+ (lcd.hwidth - 1) : lcd.bwidth - 1));
+}
+
+static void lcd_print(char c)
+{
+ if (lcd.addr.x < lcd.bwidth) {
+ if (lcd_char_conv != NULL)
+ c = lcd_char_conv[(unsigned char)c];
+ lcd_write_data(c);
+ lcd.addr.x++;
+ }
+ /* prevents the cursor from wrapping onto the next line */
+ if (lcd.addr.x == lcd.bwidth)
+ lcd_gotoxy();
+}
+
+/* fills the display with spaces and resets X/Y */
+static void lcd_clear_fast_s(void)
+{
+ int pos;
+
+ lcd.addr.x = 0;
+ lcd.addr.y = 0;
+ lcd_gotoxy();
+
+ spin_lock_irq(&pprt_lock);
+ for (pos = 0; pos < lcd.height * lcd.hwidth; pos++) {
+ lcd_send_serial(0x5F); /* R/W=W, RS=1 */
+ lcd_send_serial(' ' & 0x0F);
+ lcd_send_serial((' ' >> 4) & 0x0F);
+ udelay(40); /* the shortest data takes at least 40 us */
+ }
+ spin_unlock_irq(&pprt_lock);
+
+ lcd.addr.x = 0;
+ lcd.addr.y = 0;
+ lcd_gotoxy();
+}
+
+/* fills the display with spaces and resets X/Y */
+static void lcd_clear_fast_p8(void)
+{
+ int pos;
+
+ lcd.addr.x = 0;
+ lcd.addr.y = 0;
+ lcd_gotoxy();
+
+ spin_lock_irq(&pprt_lock);
+ for (pos = 0; pos < lcd.height * lcd.hwidth; pos++) {
+ /* present the data to the data port */
+ w_dtr(pprt, ' ');
+
+ /* maintain the data during 20 us before the strobe */
+ udelay(20);
+
+ bits.e = BIT_SET;
+ bits.rs = BIT_SET;
+ bits.rw = BIT_CLR;
+ set_ctrl_bits();
+
+ /* maintain the strobe during 40 us */
+ udelay(40);
+
+ bits.e = BIT_CLR;
+ set_ctrl_bits();
+
+ /* the shortest data takes at least 45 us */
+ udelay(45);
+ }
+ spin_unlock_irq(&pprt_lock);
+
+ lcd.addr.x = 0;
+ lcd.addr.y = 0;
+ lcd_gotoxy();
+}
+
+/* fills the display with spaces and resets X/Y */
+static void lcd_clear_fast_tilcd(void)
+{
+ int pos;
+
+ lcd.addr.x = 0;
+ lcd.addr.y = 0;
+ lcd_gotoxy();
+
+ spin_lock_irq(&pprt_lock);
+ for (pos = 0; pos < lcd.height * lcd.hwidth; pos++) {
+ /* present the data to the data port */
+ w_dtr(pprt, ' ');
+ udelay(60);
+ }
+
+ spin_unlock_irq(&pprt_lock);
+
+ lcd.addr.x = 0;
+ lcd.addr.y = 0;
+ lcd_gotoxy();
+}
+
+/* clears the display and resets X/Y */
+static void lcd_clear_display(void)
+{
+ lcd_write_cmd(LCD_CMD_DISPLAY_CLEAR);
+ lcd.addr.x = 0;
+ lcd.addr.y = 0;
+ /* we must wait a few milliseconds (15) */
+ long_sleep(15);
+}
+
+static void lcd_init_display(void)
+{
+ lcd.flags = ((lcd.height > 1) ? LCD_FLAG_N : 0)
+ | LCD_FLAG_D | LCD_FLAG_C | LCD_FLAG_B;
+
+ long_sleep(20); /* wait 20 ms after power-up for the paranoid */
+
+ /* 8bits, 1 line, small fonts; let's do it 3 times */
+ lcd_write_cmd(LCD_CMD_FUNCTION_SET | LCD_CMD_DATA_LEN_8BITS);
+ long_sleep(10);
+ lcd_write_cmd(LCD_CMD_FUNCTION_SET | LCD_CMD_DATA_LEN_8BITS);
+ long_sleep(10);
+ lcd_write_cmd(LCD_CMD_FUNCTION_SET | LCD_CMD_DATA_LEN_8BITS);
+ long_sleep(10);
+
+ /* set font height and lines number */
+ lcd_write_cmd(LCD_CMD_FUNCTION_SET | LCD_CMD_DATA_LEN_8BITS
+ | ((lcd.flags & LCD_FLAG_F) ? LCD_CMD_FONT_5X10_DOTS : 0)
+ | ((lcd.flags & LCD_FLAG_N) ? LCD_CMD_TWO_LINES : 0)
+ );
+ long_sleep(10);
+
+ /* display off, cursor off, blink off */
+ lcd_write_cmd(LCD_CMD_DISPLAY_CTRL);
+ long_sleep(10);
+
+ lcd_write_cmd(LCD_CMD_DISPLAY_CTRL /* set display mode */
+ | ((lcd.flags & LCD_FLAG_D) ? LCD_CMD_DISPLAY_ON : 0)
+ | ((lcd.flags & LCD_FLAG_C) ? LCD_CMD_CURSOR_ON : 0)
+ | ((lcd.flags & LCD_FLAG_B) ? LCD_CMD_BLINK_ON : 0)
+ );
+
+ lcd_backlight((lcd.flags & LCD_FLAG_L) ? 1 : 0);
+
+ long_sleep(10);
+
+ /* entry mode set : increment, cursor shifting */
+ lcd_write_cmd(LCD_CMD_ENTRY_MODE | LCD_CMD_CURSOR_INC);
+
+ lcd_clear_display();
+}
+
+/*
+ * These are the file operation function for user access to /dev/lcd
+ * This function can also be called from inside the kernel, by
+ * setting file and ppos to NULL.
+ *
+ */
+
+static inline int handle_lcd_special_code(void)
+{
+ /* LCD special codes */
+
+ int processed = 0;
+
+ char *esc = lcd.esc_seq.buf + 2;
+ int oldflags = lcd.flags;
+
+ /* check for display mode flags */
+ switch (*esc) {
+ case 'D': /* Display ON */
+ lcd.flags |= LCD_FLAG_D;
+ processed = 1;
+ break;
+ case 'd': /* Display OFF */
+ lcd.flags &= ~LCD_FLAG_D;
+ processed = 1;
+ break;
+ case 'C': /* Cursor ON */
+ lcd.flags |= LCD_FLAG_C;
+ processed = 1;
+ break;
+ case 'c': /* Cursor OFF */
+ lcd.flags &= ~LCD_FLAG_C;
+ processed = 1;
+ break;
+ case 'B': /* Blink ON */
+ lcd.flags |= LCD_FLAG_B;
+ processed = 1;
+ break;
+ case 'b': /* Blink OFF */
+ lcd.flags &= ~LCD_FLAG_B;
+ processed = 1;
+ break;
+ case '+': /* Back light ON */
+ lcd.flags |= LCD_FLAG_L;
+ processed = 1;
+ break;
+ case '-': /* Back light OFF */
+ lcd.flags &= ~LCD_FLAG_L;
+ processed = 1;
+ break;
+ case '*':
+ /* flash back light using the keypad timer */
+ if (scan_timer.function != NULL) {
+ if (lcd.light_tempo == 0
+ && ((lcd.flags & LCD_FLAG_L) == 0))
+ lcd_backlight(1);
+ lcd.light_tempo = FLASH_LIGHT_TEMPO;
+ }
+ processed = 1;
+ break;
+ case 'f': /* Small Font */
+ lcd.flags &= ~LCD_FLAG_F;
+ processed = 1;
+ break;
+ case 'F': /* Large Font */
+ lcd.flags |= LCD_FLAG_F;
+ processed = 1;
+ break;
+ case 'n': /* One Line */
+ lcd.flags &= ~LCD_FLAG_N;
+ processed = 1;
+ break;
+ case 'N': /* Two Lines */
+ lcd.flags |= LCD_FLAG_N;
+ break;
+ case 'l': /* Shift Cursor Left */
+ if (lcd.addr.x > 0) {
+ /* back one char if not at end of line */
+ if (lcd.addr.x < lcd.bwidth)
+ lcd_write_cmd(LCD_CMD_SHIFT);
+ lcd.addr.x--;
+ }
+ processed = 1;
+ break;
+ case 'r': /* shift cursor right */
+ if (lcd.addr.x < lcd.width) {
+ /* allow the cursor to pass the end of the line */
+ if (lcd.addr.x < (lcd.bwidth - 1))
+ lcd_write_cmd(LCD_CMD_SHIFT |
+ LCD_CMD_SHIFT_RIGHT);
+ lcd.addr.x++;
+ }
+ processed = 1;
+ break;
+ case 'L': /* shift display left */
+ lcd_write_cmd(LCD_CMD_SHIFT | LCD_CMD_DISPLAY_SHIFT);
+ processed = 1;
+ break;
+ case 'R': /* shift display right */
+ lcd_write_cmd(LCD_CMD_SHIFT | LCD_CMD_DISPLAY_SHIFT |
+ LCD_CMD_SHIFT_RIGHT);
+ processed = 1;
+ break;
+ case 'k': { /* kill end of line */
+ int x;
+
+ for (x = lcd.addr.x; x < lcd.bwidth; x++)
+ lcd_write_data(' ');
+
+ /* restore cursor position */
+ lcd_gotoxy();
+ processed = 1;
+ break;
+ }
+ case 'I': /* reinitialize display */
+ lcd_init_display();
+ processed = 1;
+ break;
+ case 'G': {
+ /* Generator : LGcxxxxx...xx; must have <c> between '0'
+ * and '7', representing the numerical ASCII code of the
+ * redefined character, and <xx...xx> a sequence of 16
+ * hex digits representing 8 bytes for each character.
+ * Most LCDs will only use 5 lower bits of the 7 first
+ * bytes.
+ */
+
+ unsigned char cgbytes[8];
+ unsigned char cgaddr;
+ int cgoffset;
+ int shift;
+ char value;
+ int addr;
+
+ if (strchr(esc, ';') == NULL)
+ break;
+
+ esc++;
+
+ cgaddr = *(esc++) - '0';
+ if (cgaddr > 7) {
+ processed = 1;
+ break;
+ }
+
+ cgoffset = 0;
+ shift = 0;
+ value = 0;
+ while (*esc && cgoffset < 8) {
+ shift ^= 4;
+ if (*esc >= '0' && *esc <= '9') {
+ value |= (*esc - '0') << shift;
+ } else if (*esc >= 'A' && *esc <= 'Z') {
+ value |= (*esc - 'A' + 10) << shift;
+ } else if (*esc >= 'a' && *esc <= 'z') {
+ value |= (*esc - 'a' + 10) << shift;
+ } else {
+ esc++;
+ continue;
+ }
+
+ if (shift == 0) {
+ cgbytes[cgoffset++] = value;
+ value = 0;
+ }
+
+ esc++;
+ }
+
+ lcd_write_cmd(LCD_CMD_SET_CGRAM_ADDR | (cgaddr * 8));
+ for (addr = 0; addr < cgoffset; addr++)
+ lcd_write_data(cgbytes[addr]);
+
+ /* ensures that we stop writing to CGRAM */
+ lcd_gotoxy();
+ processed = 1;
+ break;
+ }
+ case 'x': /* gotoxy : LxXXX[yYYY]; */
+ case 'y': /* gotoxy : LyYYY[xXXX]; */
+ if (strchr(esc, ';') == NULL)
+ break;
+
+ while (*esc) {
+ if (*esc == 'x') {
+ esc++;
+ if (kstrtoul(esc, 10, &lcd.addr.x) < 0)
+ break;
+ } else if (*esc == 'y') {
+ esc++;
+ if (kstrtoul(esc, 10, &lcd.addr.y) < 0)
+ break;
+ } else {
+ break;
+ }
+ }
+
+ lcd_gotoxy();
+ processed = 1;
+ break;
+ }
+
+ /* TODO: This indent party here got ugly, clean it! */
+ /* Check whether one flag was changed */
+ if (oldflags != lcd.flags) {
+ /* check whether one of B,C,D flags were changed */
+ if ((oldflags ^ lcd.flags) &
+ (LCD_FLAG_B | LCD_FLAG_C | LCD_FLAG_D))
+ /* set display mode */
+ lcd_write_cmd(LCD_CMD_DISPLAY_CTRL
+ | ((lcd.flags & LCD_FLAG_D)
+ ? LCD_CMD_DISPLAY_ON : 0)
+ | ((lcd.flags & LCD_FLAG_C)
+ ? LCD_CMD_CURSOR_ON : 0)
+ | ((lcd.flags & LCD_FLAG_B)
+ ? LCD_CMD_BLINK_ON : 0));
+ /* check whether one of F,N flags was changed */
+ else if ((oldflags ^ lcd.flags) & (LCD_FLAG_F | LCD_FLAG_N))
+ lcd_write_cmd(LCD_CMD_FUNCTION_SET
+ | LCD_CMD_DATA_LEN_8BITS
+ | ((lcd.flags & LCD_FLAG_F)
+ ? LCD_CMD_TWO_LINES : 0)
+ | ((lcd.flags & LCD_FLAG_N)
+ ? LCD_CMD_FONT_5X10_DOTS
+ : 0));
+ /* check whether L flag was changed */
+ else if ((oldflags ^ lcd.flags) & (LCD_FLAG_L)) {
+ if (lcd.flags & (LCD_FLAG_L))
+ lcd_backlight(1);
+ else if (lcd.light_tempo == 0)
+ /* switch off the light only when the tempo
+ lighting is gone */
+ lcd_backlight(0);
+ }
+ }
+
+ return processed;
+}
+
+static void lcd_write_char(char c)
+{
+ /* first, we'll test if we're in escape mode */
+ if ((c != '\n') && lcd.esc_seq.len >= 0) {
+ /* yes, let's add this char to the buffer */
+ lcd.esc_seq.buf[lcd.esc_seq.len++] = c;
+ lcd.esc_seq.buf[lcd.esc_seq.len] = 0;
+ } else {
+ /* aborts any previous escape sequence */
+ lcd.esc_seq.len = -1;
+
+ switch (c) {
+ case LCD_ESCAPE_CHAR:
+ /* start of an escape sequence */
+ lcd.esc_seq.len = 0;
+ lcd.esc_seq.buf[lcd.esc_seq.len] = 0;
+ break;
+ case '\b':
+ /* go back one char and clear it */
+ if (lcd.addr.x > 0) {
+ /* check if we're not at the
+ end of the line */
+ if (lcd.addr.x < lcd.bwidth)
+ /* back one char */
+ lcd_write_cmd(LCD_CMD_SHIFT);
+ lcd.addr.x--;
+ }
+ /* replace with a space */
+ lcd_write_data(' ');
+ /* back one char again */
+ lcd_write_cmd(LCD_CMD_SHIFT);
+ break;
+ case '\014':
+ /* quickly clear the display */
+ lcd_clear_fast();
+ break;
+ case '\n':
+ /* flush the remainder of the current line and
+ go to the beginning of the next line */
+ for (; lcd.addr.x < lcd.bwidth; lcd.addr.x++)
+ lcd_write_data(' ');
+ lcd.addr.x = 0;
+ lcd.addr.y = (lcd.addr.y + 1) % lcd.height;
+ lcd_gotoxy();
+ break;
+ case '\r':
+ /* go to the beginning of the same line */
+ lcd.addr.x = 0;
+ lcd_gotoxy();
+ break;
+ case '\t':
+ /* print a space instead of the tab */
+ lcd_print(' ');
+ break;
+ default:
+ /* simply print this char */
+ lcd_print(c);
+ break;
+ }
+ }
+
+ /* now we'll see if we're in an escape mode and if the current
+ escape sequence can be understood. */
+ if (lcd.esc_seq.len >= 2) {
+ int processed = 0;
+
+ if (!strcmp(lcd.esc_seq.buf, "[2J")) {
+ /* clear the display */
+ lcd_clear_fast();
+ processed = 1;
+ } else if (!strcmp(lcd.esc_seq.buf, "[H")) {
+ /* cursor to home */
+ lcd.addr.x = 0;
+ lcd.addr.y = 0;
+ lcd_gotoxy();
+ processed = 1;
+ }
+ /* codes starting with ^[[L */
+ else if ((lcd.esc_seq.len >= 3) &&
+ (lcd.esc_seq.buf[0] == '[') &&
+ (lcd.esc_seq.buf[1] == 'L')) {
+ processed = handle_lcd_special_code();
+ }
+
+ /* LCD special escape codes */
+ /* flush the escape sequence if it's been processed
+ or if it is getting too long. */
+ if (processed || (lcd.esc_seq.len >= LCD_ESCAPE_LEN))
+ lcd.esc_seq.len = -1;
+ } /* escape codes */
+}
+
+static ssize_t lcd_write(struct file *file,
+ const char __user *buf, size_t count, loff_t *ppos)
+{
+ const char __user *tmp = buf;
+ char c;
+
+ for (; count-- > 0; (*ppos)++, tmp++) {
+ if (!in_interrupt() && (((count + 1) & 0x1f) == 0))
+ /* let's be a little nice with other processes
+ that need some CPU */
+ schedule();
+
+ if (get_user(c, tmp))
+ return -EFAULT;
+
+ lcd_write_char(c);
+ }
+
+ return tmp - buf;
+}
+
+static int lcd_open(struct inode *inode, struct file *file)
+{
+ if (!atomic_dec_and_test(&lcd_available))
+ return -EBUSY; /* open only once at a time */
+
+ if (file->f_mode & FMODE_READ) /* device is write-only */
+ return -EPERM;
+
+ if (lcd.must_clear) {
+ lcd_clear_display();
+ lcd.must_clear = false;
+ }
+ return nonseekable_open(inode, file);
+}
+
+static int lcd_release(struct inode *inode, struct file *file)
+{
+ atomic_inc(&lcd_available);
+ return 0;
+}
+
+static const struct file_operations lcd_fops = {
+ .write = lcd_write,
+ .open = lcd_open,
+ .release = lcd_release,
+ .llseek = no_llseek,
+};
+
+static struct miscdevice lcd_dev = {
+ .minor = LCD_MINOR,
+ .name = "lcd",
+ .fops = &lcd_fops,
+};
+
+/* public function usable from the kernel for any purpose */
+static void panel_lcd_print(const char *s)
+{
+ const char *tmp = s;
+ int count = strlen(s);
+
+ if (lcd.enabled && lcd.initialized) {
+ for (; count-- > 0; tmp++) {
+ if (!in_interrupt() && (((count + 1) & 0x1f) == 0))
+ /* let's be a little nice with other processes
+ that need some CPU */
+ schedule();
+
+ lcd_write_char(*tmp);
+ }
+ }
+}
+
+/* initialize the LCD driver */
+static void lcd_init(void)
+{
+ switch (selected_lcd_type) {
+ case LCD_TYPE_OLD:
+ /* parallel mode, 8 bits */
+ lcd.proto = LCD_PROTO_PARALLEL;
+ lcd.charset = LCD_CHARSET_NORMAL;
+ lcd.pins.e = PIN_STROBE;
+ lcd.pins.rs = PIN_AUTOLF;
+
+ lcd.width = 40;
+ lcd.bwidth = 40;
+ lcd.hwidth = 64;
+ lcd.height = 2;
+ break;
+ case LCD_TYPE_KS0074:
+ /* serial mode, ks0074 */
+ lcd.proto = LCD_PROTO_SERIAL;
+ lcd.charset = LCD_CHARSET_KS0074;
+ lcd.pins.bl = PIN_AUTOLF;
+ lcd.pins.cl = PIN_STROBE;
+ lcd.pins.da = PIN_D0;
+
+ lcd.width = 16;
+ lcd.bwidth = 40;
+ lcd.hwidth = 16;
+ lcd.height = 2;
+ break;
+ case LCD_TYPE_NEXCOM:
+ /* parallel mode, 8 bits, generic */
+ lcd.proto = LCD_PROTO_PARALLEL;
+ lcd.charset = LCD_CHARSET_NORMAL;
+ lcd.pins.e = PIN_AUTOLF;
+ lcd.pins.rs = PIN_SELECP;
+ lcd.pins.rw = PIN_INITP;
+
+ lcd.width = 16;
+ lcd.bwidth = 40;
+ lcd.hwidth = 64;
+ lcd.height = 2;
+ break;
+ case LCD_TYPE_CUSTOM:
+ /* customer-defined */
+ lcd.proto = DEFAULT_LCD_PROTO;
+ lcd.charset = DEFAULT_LCD_CHARSET;
+ /* default geometry will be set later */
+ break;
+ case LCD_TYPE_HANTRONIX:
+ /* parallel mode, 8 bits, hantronix-like */
+ default:
+ lcd.proto = LCD_PROTO_PARALLEL;
+ lcd.charset = LCD_CHARSET_NORMAL;
+ lcd.pins.e = PIN_STROBE;
+ lcd.pins.rs = PIN_SELECP;
+
+ lcd.width = 16;
+ lcd.bwidth = 40;
+ lcd.hwidth = 64;
+ lcd.height = 2;
+ break;
+ }
+
+ /* Overwrite with module params set on loading */
+ if (lcd_height != NOT_SET)
+ lcd.height = lcd_height;
+ if (lcd_width != NOT_SET)
+ lcd.width = lcd_width;
+ if (lcd_bwidth != NOT_SET)
+ lcd.bwidth = lcd_bwidth;
+ if (lcd_hwidth != NOT_SET)
+ lcd.hwidth = lcd_hwidth;
+ if (lcd_charset != NOT_SET)
+ lcd.charset = lcd_charset;
+ if (lcd_proto != NOT_SET)
+ lcd.proto = lcd_proto;
+ if (lcd_e_pin != PIN_NOT_SET)
+ lcd.pins.e = lcd_e_pin;
+ if (lcd_rs_pin != PIN_NOT_SET)
+ lcd.pins.rs = lcd_rs_pin;
+ if (lcd_rw_pin != PIN_NOT_SET)
+ lcd.pins.rw = lcd_rw_pin;
+ if (lcd_cl_pin != PIN_NOT_SET)
+ lcd.pins.cl = lcd_cl_pin;
+ if (lcd_da_pin != PIN_NOT_SET)
+ lcd.pins.da = lcd_da_pin;
+ if (lcd_bl_pin != PIN_NOT_SET)
+ lcd.pins.bl = lcd_bl_pin;
+
+ /* this is used to catch wrong and default values */
+ if (lcd.width <= 0)
+ lcd.width = DEFAULT_LCD_WIDTH;
+ if (lcd.bwidth <= 0)
+ lcd.bwidth = DEFAULT_LCD_BWIDTH;
+ if (lcd.hwidth <= 0)
+ lcd.hwidth = DEFAULT_LCD_HWIDTH;
+ if (lcd.height <= 0)
+ lcd.height = DEFAULT_LCD_HEIGHT;
+
+ if (lcd.proto == LCD_PROTO_SERIAL) { /* SERIAL */
+ lcd_write_cmd = lcd_write_cmd_s;
+ lcd_write_data = lcd_write_data_s;
+ lcd_clear_fast = lcd_clear_fast_s;
+
+ if (lcd.pins.cl == PIN_NOT_SET)
+ lcd.pins.cl = DEFAULT_LCD_PIN_SCL;
+ if (lcd.pins.da == PIN_NOT_SET)
+ lcd.pins.da = DEFAULT_LCD_PIN_SDA;
+
+ } else if (lcd.proto == LCD_PROTO_PARALLEL) { /* PARALLEL */
+ lcd_write_cmd = lcd_write_cmd_p8;
+ lcd_write_data = lcd_write_data_p8;
+ lcd_clear_fast = lcd_clear_fast_p8;
+
+ if (lcd.pins.e == PIN_NOT_SET)
+ lcd.pins.e = DEFAULT_LCD_PIN_E;
+ if (lcd.pins.rs == PIN_NOT_SET)
+ lcd.pins.rs = DEFAULT_LCD_PIN_RS;
+ if (lcd.pins.rw == PIN_NOT_SET)
+ lcd.pins.rw = DEFAULT_LCD_PIN_RW;
+ } else {
+ lcd_write_cmd = lcd_write_cmd_tilcd;
+ lcd_write_data = lcd_write_data_tilcd;
+ lcd_clear_fast = lcd_clear_fast_tilcd;
+ }
+
+ if (lcd.pins.bl == PIN_NOT_SET)
+ lcd.pins.bl = DEFAULT_LCD_PIN_BL;
+
+ if (lcd.pins.e == PIN_NOT_SET)
+ lcd.pins.e = PIN_NONE;
+ if (lcd.pins.rs == PIN_NOT_SET)
+ lcd.pins.rs = PIN_NONE;
+ if (lcd.pins.rw == PIN_NOT_SET)
+ lcd.pins.rw = PIN_NONE;
+ if (lcd.pins.bl == PIN_NOT_SET)
+ lcd.pins.bl = PIN_NONE;
+ if (lcd.pins.cl == PIN_NOT_SET)
+ lcd.pins.cl = PIN_NONE;
+ if (lcd.pins.da == PIN_NOT_SET)
+ lcd.pins.da = PIN_NONE;
+
+ if (lcd.charset == NOT_SET)
+ lcd.charset = DEFAULT_LCD_CHARSET;
+
+ if (lcd.charset == LCD_CHARSET_KS0074)
+ lcd_char_conv = lcd_char_conv_ks0074;
+ else
+ lcd_char_conv = NULL;
+
+ if (lcd.pins.bl != PIN_NONE)
+ init_scan_timer();
+
+ pin_to_bits(lcd.pins.e, lcd_bits[LCD_PORT_D][LCD_BIT_E],
+ lcd_bits[LCD_PORT_C][LCD_BIT_E]);
+ pin_to_bits(lcd.pins.rs, lcd_bits[LCD_PORT_D][LCD_BIT_RS],
+ lcd_bits[LCD_PORT_C][LCD_BIT_RS]);
+ pin_to_bits(lcd.pins.rw, lcd_bits[LCD_PORT_D][LCD_BIT_RW],
+ lcd_bits[LCD_PORT_C][LCD_BIT_RW]);
+ pin_to_bits(lcd.pins.bl, lcd_bits[LCD_PORT_D][LCD_BIT_BL],
+ lcd_bits[LCD_PORT_C][LCD_BIT_BL]);
+ pin_to_bits(lcd.pins.cl, lcd_bits[LCD_PORT_D][LCD_BIT_CL],
+ lcd_bits[LCD_PORT_C][LCD_BIT_CL]);
+ pin_to_bits(lcd.pins.da, lcd_bits[LCD_PORT_D][LCD_BIT_DA],
+ lcd_bits[LCD_PORT_C][LCD_BIT_DA]);
+
+ /* before this line, we must NOT send anything to the display.
+ * Since lcd_init_display() needs to write data, we have to
+ * enable mark the LCD initialized just before. */
+ lcd.initialized = true;
+ lcd_init_display();
+
+ /* display a short message */
+#ifdef CONFIG_PANEL_CHANGE_MESSAGE
+#ifdef CONFIG_PANEL_BOOT_MESSAGE
+ panel_lcd_print("\x1b[Lc\x1b[Lb\x1b[L*" CONFIG_PANEL_BOOT_MESSAGE);
+#endif
+#else
+ panel_lcd_print("\x1b[Lc\x1b[Lb\x1b[L*Linux-" UTS_RELEASE "\nPanel-"
+ PANEL_VERSION);
+#endif
+ lcd.addr.x = 0;
+ lcd.addr.y = 0;
+ /* clear the display on the next device opening */
+ lcd.must_clear = true;
+ lcd_gotoxy();
+}
+
+/*
+ * These are the file operation function for user access to /dev/keypad
+ */
+
+static ssize_t keypad_read(struct file *file,
+ char __user *buf, size_t count, loff_t *ppos)
+{
+ unsigned i = *ppos;
+ char __user *tmp = buf;
+
+ if (keypad_buflen == 0) {
+ if (file->f_flags & O_NONBLOCK)
+ return -EAGAIN;
+
+ if (wait_event_interruptible(keypad_read_wait,
+ keypad_buflen != 0))
+ return -EINTR;
+ }
+
+ for (; count-- > 0 && (keypad_buflen > 0);
+ ++i, ++tmp, --keypad_buflen) {
+ put_user(keypad_buffer[keypad_start], tmp);
+ keypad_start = (keypad_start + 1) % KEYPAD_BUFFER;
+ }
+ *ppos = i;
+
+ return tmp - buf;
+}
+
+static int keypad_open(struct inode *inode, struct file *file)
+{
+ if (!atomic_dec_and_test(&keypad_available))
+ return -EBUSY; /* open only once at a time */
+
+ if (file->f_mode & FMODE_WRITE) /* device is read-only */
+ return -EPERM;
+
+ keypad_buflen = 0; /* flush the buffer on opening */
+ return 0;
+}
+
+static int keypad_release(struct inode *inode, struct file *file)
+{
+ atomic_inc(&keypad_available);
+ return 0;
+}
+
+static const struct file_operations keypad_fops = {
+ .read = keypad_read, /* read */
+ .open = keypad_open, /* open */
+ .release = keypad_release, /* close */
+ .llseek = default_llseek,
+};
+
+static struct miscdevice keypad_dev = {
+ .minor = KEYPAD_MINOR,
+ .name = "keypad",
+ .fops = &keypad_fops,
+};
+
+static void keypad_send_key(const char *string, int max_len)
+{
+ /* send the key to the device only if a process is attached to it. */
+ if (!atomic_read(&keypad_available)) {
+ while (max_len-- && keypad_buflen < KEYPAD_BUFFER && *string) {
+ keypad_buffer[(keypad_start + keypad_buflen++) %
+ KEYPAD_BUFFER] = *string++;
+ }
+ wake_up_interruptible(&keypad_read_wait);
+ }
+}
+
+/* this function scans all the bits involving at least one logical signal,
+ * and puts the results in the bitfield "phys_read" (one bit per established
+ * contact), and sets "phys_read_prev" to "phys_read".
+ *
+ * Note: to debounce input signals, we will only consider as switched a signal
+ * which is stable across 2 measures. Signals which are different between two
+ * reads will be kept as they previously were in their logical form (phys_prev).
+ * A signal which has just switched will have a 1 in
+ * (phys_read ^ phys_read_prev).
+ */
+static void phys_scan_contacts(void)
+{
+ int bit, bitval;
+ char oldval;
+ char bitmask;
+ char gndmask;
+
+ phys_prev = phys_curr;
+ phys_read_prev = phys_read;
+ phys_read = 0; /* flush all signals */
+
+ /* keep track of old value, with all outputs disabled */
+ oldval = r_dtr(pprt) | scan_mask_o;
+ /* activate all keyboard outputs (active low) */
+ w_dtr(pprt, oldval & ~scan_mask_o);
+
+ /* will have a 1 for each bit set to gnd */
+ bitmask = PNL_PINPUT(r_str(pprt)) & scan_mask_i;
+ /* disable all matrix signals */
+ w_dtr(pprt, oldval);
+
+ /* now that all outputs are cleared, the only active input bits are
+ * directly connected to the ground
+ */
+
+ /* 1 for each grounded input */
+ gndmask = PNL_PINPUT(r_str(pprt)) & scan_mask_i;
+
+ /* grounded inputs are signals 40-44 */
+ phys_read |= (pmask_t) gndmask << 40;
+
+ if (bitmask != gndmask) {
+ /* since clearing the outputs changed some inputs, we know
+ * that some input signals are currently tied to some outputs.
+ * So we'll scan them.
+ */
+ for (bit = 0; bit < 8; bit++) {
+ bitval = 1 << bit;
+
+ if (!(scan_mask_o & bitval)) /* skip unused bits */
+ continue;
+
+ w_dtr(pprt, oldval & ~bitval); /* enable this output */
+ bitmask = PNL_PINPUT(r_str(pprt)) & ~gndmask;
+ phys_read |= (pmask_t) bitmask << (5 * bit);
+ }
+ w_dtr(pprt, oldval); /* disable all outputs */
+ }
+ /* this is easy: use old bits when they are flapping,
+ * use new ones when stable */
+ phys_curr = (phys_prev & (phys_read ^ phys_read_prev)) |
+ (phys_read & ~(phys_read ^ phys_read_prev));
+}
+
+static inline int input_state_high(struct logical_input *input)
+{
+#if 0
+ /* FIXME:
+ * this is an invalid test. It tries to catch
+ * transitions from single-key to multiple-key, but
+ * doesn't take into account the contacts polarity.
+ * The only solution to the problem is to parse keys
+ * from the most complex to the simplest combinations,
+ * and mark them as 'caught' once a combination
+ * matches, then unmatch it for all other ones.
+ */
+
+ /* try to catch dangerous transitions cases :
+ * someone adds a bit, so this signal was a false
+ * positive resulting from a transition. We should
+ * invalidate the signal immediately and not call the
+ * release function.
+ * eg: 0 -(press A)-> A -(press B)-> AB : don't match A's release.
+ */
+ if (((phys_prev & input->mask) == input->value) &&
+ ((phys_curr & input->mask) > input->value)) {
+ input->state = INPUT_ST_LOW; /* invalidate */
+ return 1;
+ }
+#endif
+
+ if ((phys_curr & input->mask) == input->value) {
+ if ((input->type == INPUT_TYPE_STD) &&
+ (input->high_timer == 0)) {
+ input->high_timer++;
+ if (input->u.std.press_fct != NULL)
+ input->u.std.press_fct(input->u.std.press_data);
+ } else if (input->type == INPUT_TYPE_KBD) {
+ /* will turn on the light */
+ keypressed = 1;
+
+ if (input->high_timer == 0) {
+ char *press_str = input->u.kbd.press_str;
+
+ if (press_str[0]) {
+ int s = sizeof(input->u.kbd.press_str);
+
+ keypad_send_key(press_str, s);
+ }
+ }
+
+ if (input->u.kbd.repeat_str[0]) {
+ char *repeat_str = input->u.kbd.repeat_str;
+
+ if (input->high_timer >= KEYPAD_REP_START) {
+ int s = sizeof(input->u.kbd.repeat_str);
+
+ input->high_timer -= KEYPAD_REP_DELAY;
+ keypad_send_key(repeat_str, s);
+ }
+ /* we will need to come back here soon */
+ inputs_stable = 0;
+ }
+
+ if (input->high_timer < 255)
+ input->high_timer++;
+ }
+ return 1;
+ }
+
+ /* else signal falling down. Let's fall through. */
+ input->state = INPUT_ST_FALLING;
+ input->fall_timer = 0;
+
+ return 0;
+}
+
+static inline void input_state_falling(struct logical_input *input)
+{
+#if 0
+ /* FIXME !!! same comment as in input_state_high */
+ if (((phys_prev & input->mask) == input->value) &&
+ ((phys_curr & input->mask) > input->value)) {
+ input->state = INPUT_ST_LOW; /* invalidate */
+ return;
+ }
+#endif
+
+ if ((phys_curr & input->mask) == input->value) {
+ if (input->type == INPUT_TYPE_KBD) {
+ /* will turn on the light */
+ keypressed = 1;
+
+ if (input->u.kbd.repeat_str[0]) {
+ char *repeat_str = input->u.kbd.repeat_str;
+
+ if (input->high_timer >= KEYPAD_REP_START) {
+ int s = sizeof(input->u.kbd.repeat_str);
+
+ input->high_timer -= KEYPAD_REP_DELAY;
+ keypad_send_key(repeat_str, s);
+ }
+ /* we will need to come back here soon */
+ inputs_stable = 0;
+ }
+
+ if (input->high_timer < 255)
+ input->high_timer++;
+ }
+ input->state = INPUT_ST_HIGH;
+ } else if (input->fall_timer >= input->fall_time) {
+ /* call release event */
+ if (input->type == INPUT_TYPE_STD) {
+ void (*release_fct)(int) = input->u.std.release_fct;
+
+ if (release_fct != NULL)
+ release_fct(input->u.std.release_data);
+ } else if (input->type == INPUT_TYPE_KBD) {
+ char *release_str = input->u.kbd.release_str;
+
+ if (release_str[0]) {
+ int s = sizeof(input->u.kbd.release_str);
+
+ keypad_send_key(release_str, s);
+ }
+ }
+
+ input->state = INPUT_ST_LOW;
+ } else {
+ input->fall_timer++;
+ inputs_stable = 0;
+ }
+}
+
+static void panel_process_inputs(void)
+{
+ struct list_head *item;
+ struct logical_input *input;
+
+ keypressed = 0;
+ inputs_stable = 1;
+ list_for_each(item, &logical_inputs) {
+ input = list_entry(item, struct logical_input, list);
+
+ switch (input->state) {
+ case INPUT_ST_LOW:
+ if ((phys_curr & input->mask) != input->value)
+ break;
+ /* if all needed ones were already set previously,
+ * this means that this logical signal has been
+ * activated by the releasing of another combined
+ * signal, so we don't want to match.
+ * eg: AB -(release B)-> A -(release A)-> 0 :
+ * don't match A.
+ */
+ if ((phys_prev & input->mask) == input->value)
+ break;
+ input->rise_timer = 0;
+ input->state = INPUT_ST_RISING;
+ /* no break here, fall through */
+ case INPUT_ST_RISING:
+ if ((phys_curr & input->mask) != input->value) {
+ input->state = INPUT_ST_LOW;
+ break;
+ }
+ if (input->rise_timer < input->rise_time) {
+ inputs_stable = 0;
+ input->rise_timer++;
+ break;
+ }
+ input->high_timer = 0;
+ input->state = INPUT_ST_HIGH;
+ /* no break here, fall through */
+ case INPUT_ST_HIGH:
+ if (input_state_high(input))
+ break;
+ /* no break here, fall through */
+ case INPUT_ST_FALLING:
+ input_state_falling(input);
+ }
+ }
+}
+
+static void panel_scan_timer(void)
+{
+ if (keypad.enabled && keypad_initialized) {
+ if (spin_trylock_irq(&pprt_lock)) {
+ phys_scan_contacts();
+
+ /* no need for the parport anymore */
+ spin_unlock_irq(&pprt_lock);
+ }
+
+ if (!inputs_stable || phys_curr != phys_prev)
+ panel_process_inputs();
+ }
+
+ if (lcd.enabled && lcd.initialized) {
+ if (keypressed) {
+ if (lcd.light_tempo == 0
+ && ((lcd.flags & LCD_FLAG_L) == 0))
+ lcd_backlight(1);
+ lcd.light_tempo = FLASH_LIGHT_TEMPO;
+ } else if (lcd.light_tempo > 0) {
+ lcd.light_tempo--;
+ if (lcd.light_tempo == 0
+ && ((lcd.flags & LCD_FLAG_L) == 0))
+ lcd_backlight(0);
+ }
+ }
+
+ mod_timer(&scan_timer, jiffies + INPUT_POLL_TIME);
+}
+
+static void init_scan_timer(void)
+{
+ if (scan_timer.function != NULL)
+ return; /* already started */
+
+ setup_timer(&scan_timer, (void *)&panel_scan_timer, 0);
+ scan_timer.expires = jiffies + INPUT_POLL_TIME;
+ add_timer(&scan_timer);
+}
+
+/* converts a name of the form "({BbAaPpSsEe}{01234567-})*" to a series of bits.
+ * if <omask> or <imask> are non-null, they will be or'ed with the bits
+ * corresponding to out and in bits respectively.
+ * returns 1 if ok, 0 if error (in which case, nothing is written).
+ */
+static int input_name2mask(const char *name, pmask_t *mask, pmask_t *value,
+ char *imask, char *omask)
+{
+ static char sigtab[10] = "EeSsPpAaBb";
+ char im, om;
+ pmask_t m, v;
+
+ om = 0ULL;
+ im = 0ULL;
+ m = 0ULL;
+ v = 0ULL;
+ while (*name) {
+ int in, out, bit, neg;
+
+ for (in = 0; (in < sizeof(sigtab)) && (sigtab[in] != *name);
+ in++)
+ ;
+
+ if (in >= sizeof(sigtab))
+ return 0; /* input name not found */
+ neg = (in & 1); /* odd (lower) names are negated */
+ in >>= 1;
+ im |= (1 << in);
+
+ name++;
+ if (isdigit(*name)) {
+ out = *name - '0';
+ om |= (1 << out);
+ } else if (*name == '-') {
+ out = 8;
+ } else {
+ return 0; /* unknown bit name */
+ }
+
+ bit = (out * 5) + in;
+
+ m |= 1ULL << bit;
+ if (!neg)
+ v |= 1ULL << bit;
+ name++;
+ }
+ *mask = m;
+ *value = v;
+ if (imask)
+ *imask |= im;
+ if (omask)
+ *omask |= om;
+ return 1;
+}
+
+/* tries to bind a key to the signal name <name>. The key will send the
+ * strings <press>, <repeat>, <release> for these respective events.
+ * Returns the pointer to the new key if ok, NULL if the key could not be bound.
+ */
+static struct logical_input *panel_bind_key(const char *name, const char *press,
+ const char *repeat,
+ const char *release)
+{
+ struct logical_input *key;
+
+ key = kzalloc(sizeof(*key), GFP_KERNEL);
+ if (!key)
+ return NULL;
+
+ if (!input_name2mask(name, &key->mask, &key->value, &scan_mask_i,
+ &scan_mask_o)) {
+ kfree(key);
+ return NULL;
+ }
+
+ key->type = INPUT_TYPE_KBD;
+ key->state = INPUT_ST_LOW;
+ key->rise_time = 1;
+ key->fall_time = 1;
+
+ strncpy(key->u.kbd.press_str, press, sizeof(key->u.kbd.press_str));
+ strncpy(key->u.kbd.repeat_str, repeat, sizeof(key->u.kbd.repeat_str));
+ strncpy(key->u.kbd.release_str, release,
+ sizeof(key->u.kbd.release_str));
+ list_add(&key->list, &logical_inputs);
+ return key;
+}
+
+#if 0
+/* tries to bind a callback function to the signal name <name>. The function
+ * <press_fct> will be called with the <press_data> arg when the signal is
+ * activated, and so on for <release_fct>/<release_data>
+ * Returns the pointer to the new signal if ok, NULL if the signal could not
+ * be bound.
+ */
+static struct logical_input *panel_bind_callback(char *name,
+ void (*press_fct)(int),
+ int press_data,
+ void (*release_fct)(int),
+ int release_data)
+{
+ struct logical_input *callback;
+
+ callback = kmalloc(sizeof(*callback), GFP_KERNEL);
+ if (!callback)
+ return NULL;
+
+ memset(callback, 0, sizeof(struct logical_input));
+ if (!input_name2mask(name, &callback->mask, &callback->value,
+ &scan_mask_i, &scan_mask_o))
+ return NULL;
+
+ callback->type = INPUT_TYPE_STD;
+ callback->state = INPUT_ST_LOW;
+ callback->rise_time = 1;
+ callback->fall_time = 1;
+ callback->u.std.press_fct = press_fct;
+ callback->u.std.press_data = press_data;
+ callback->u.std.release_fct = release_fct;
+ callback->u.std.release_data = release_data;
+ list_add(&callback->list, &logical_inputs);
+ return callback;
+}
+#endif
+
+static void keypad_init(void)
+{
+ int keynum;
+
+ init_waitqueue_head(&keypad_read_wait);
+ keypad_buflen = 0; /* flushes any eventual noisy keystroke */
+
+ /* Let's create all known keys */
+
+ for (keynum = 0; keypad_profile[keynum][0][0]; keynum++) {
+ panel_bind_key(keypad_profile[keynum][0],
+ keypad_profile[keynum][1],
+ keypad_profile[keynum][2],
+ keypad_profile[keynum][3]);
+ }
+
+ init_scan_timer();
+ keypad_initialized = 1;
+}
+
+/**************************************************/
+/* device initialization */
+/**************************************************/
+
+static int panel_notify_sys(struct notifier_block *this, unsigned long code,
+ void *unused)
+{
+ if (lcd.enabled && lcd.initialized) {
+ switch (code) {
+ case SYS_DOWN:
+ panel_lcd_print
+ ("\x0cReloading\nSystem...\x1b[Lc\x1b[Lb\x1b[L+");
+ break;
+ case SYS_HALT:
+ panel_lcd_print
+ ("\x0cSystem Halted.\x1b[Lc\x1b[Lb\x1b[L+");
+ break;
+ case SYS_POWER_OFF:
+ panel_lcd_print("\x0cPower off.\x1b[Lc\x1b[Lb\x1b[L+");
+ break;
+ default:
+ break;
+ }
+ }
+ return NOTIFY_DONE;
+}
+
+static struct notifier_block panel_notifier = {
+ panel_notify_sys,
+ NULL,
+ 0
+};
+
+static void panel_attach(struct parport *port)
+{
+ if (port->number != parport)
+ return;
+
+ if (pprt) {
+ pr_err("%s: port->number=%d parport=%d, already registered!\n",
+ __func__, port->number, parport);
+ return;
+ }
+
+ pprt = parport_register_device(port, "panel", NULL, NULL, /* pf, kf */
+ NULL,
+ /*PARPORT_DEV_EXCL */
+ 0, (void *)&pprt);
+ if (pprt == NULL) {
+ pr_err("%s: port->number=%d parport=%d, parport_register_device() failed\n",
+ __func__, port->number, parport);
+ return;
+ }
+
+ if (parport_claim(pprt)) {
+ pr_err("could not claim access to parport%d. Aborting.\n",
+ parport);
+ goto err_unreg_device;
+ }
+
+ /* must init LCD first, just in case an IRQ from the keypad is
+ * generated at keypad init
+ */
+ if (lcd.enabled) {
+ lcd_init();
+ if (misc_register(&lcd_dev))
+ goto err_unreg_device;
+ }
+
+ if (keypad.enabled) {
+ keypad_init();
+ if (misc_register(&keypad_dev))
+ goto err_lcd_unreg;
+ }
+ register_reboot_notifier(&panel_notifier);
+ return;
+
+err_lcd_unreg:
+ if (lcd.enabled)
+ misc_deregister(&lcd_dev);
+err_unreg_device:
+ parport_unregister_device(pprt);
+ pprt = NULL;
+}
+
+static void panel_detach(struct parport *port)
+{
+ if (port->number != parport)
+ return;
+
+ if (!pprt) {
+ pr_err("%s: port->number=%d parport=%d, nothing to unregister.\n",
+ __func__, port->number, parport);
+ return;
+ }
+
+ unregister_reboot_notifier(&panel_notifier);
+
+ if (keypad.enabled && keypad_initialized) {
+ misc_deregister(&keypad_dev);
+ keypad_initialized = 0;
+ }
+
+ if (lcd.enabled && lcd.initialized) {
+ misc_deregister(&lcd_dev);
+ lcd.initialized = false;
+ }
+
+ parport_release(pprt);
+ parport_unregister_device(pprt);
+ pprt = NULL;
+}
+
+static struct parport_driver panel_driver = {
+ .name = "panel",
+ .attach = panel_attach,
+ .detach = panel_detach,
+};
+
+/* init function */
+static int __init panel_init_module(void)
+{
+ int selected_keypad_type = NOT_SET, err;
+
+ /* take care of an eventual profile */
+ switch (profile) {
+ case PANEL_PROFILE_CUSTOM:
+ /* custom profile */
+ selected_keypad_type = DEFAULT_KEYPAD_TYPE;
+ selected_lcd_type = DEFAULT_LCD_TYPE;
+ break;
+ case PANEL_PROFILE_OLD:
+ /* 8 bits, 2*16, old keypad */
+ selected_keypad_type = KEYPAD_TYPE_OLD;
+ selected_lcd_type = LCD_TYPE_OLD;
+
+ /* TODO: This two are a little hacky, sort it out later */
+ if (lcd_width == NOT_SET)
+ lcd_width = 16;
+ if (lcd_hwidth == NOT_SET)
+ lcd_hwidth = 16;
+ break;
+ case PANEL_PROFILE_NEW:
+ /* serial, 2*16, new keypad */
+ selected_keypad_type = KEYPAD_TYPE_NEW;
+ selected_lcd_type = LCD_TYPE_KS0074;
+ break;
+ case PANEL_PROFILE_HANTRONIX:
+ /* 8 bits, 2*16 hantronix-like, no keypad */
+ selected_keypad_type = KEYPAD_TYPE_NONE;
+ selected_lcd_type = LCD_TYPE_HANTRONIX;
+ break;
+ case PANEL_PROFILE_NEXCOM:
+ /* generic 8 bits, 2*16, nexcom keypad, eg. Nexcom. */
+ selected_keypad_type = KEYPAD_TYPE_NEXCOM;
+ selected_lcd_type = LCD_TYPE_NEXCOM;
+ break;
+ case PANEL_PROFILE_LARGE:
+ /* 8 bits, 2*40, old keypad */
+ selected_keypad_type = KEYPAD_TYPE_OLD;
+ selected_lcd_type = LCD_TYPE_OLD;
+ break;
+ }
+
+ /*
+ * Overwrite selection with module param values (both keypad and lcd),
+ * where the deprecated params have lower prio.
+ */
+ if (keypad_enabled != NOT_SET)
+ selected_keypad_type = keypad_enabled;
+ if (keypad_type != NOT_SET)
+ selected_keypad_type = keypad_type;
+
+ keypad.enabled = (selected_keypad_type > 0);
+
+ if (lcd_enabled != NOT_SET)
+ selected_lcd_type = lcd_enabled;
+ if (lcd_type != NOT_SET)
+ selected_lcd_type = lcd_type;
+
+ lcd.enabled = (selected_lcd_type > 0);
+
+ if (lcd.enabled) {
+ /*
+ * Init lcd struct with load-time values to preserve exact
+ * current functionality (at least for now).
+ */
+ lcd.height = lcd_height;
+ lcd.width = lcd_width;
+ lcd.bwidth = lcd_bwidth;
+ lcd.hwidth = lcd_hwidth;
+ lcd.charset = lcd_charset;
+ lcd.proto = lcd_proto;
+ lcd.pins.e = lcd_e_pin;
+ lcd.pins.rs = lcd_rs_pin;
+ lcd.pins.rw = lcd_rw_pin;
+ lcd.pins.cl = lcd_cl_pin;
+ lcd.pins.da = lcd_da_pin;
+ lcd.pins.bl = lcd_bl_pin;
+
+ /* Leave it for now, just in case */
+ lcd.esc_seq.len = -1;
+ }
+
+ switch (selected_keypad_type) {
+ case KEYPAD_TYPE_OLD:
+ keypad_profile = old_keypad_profile;
+ break;
+ case KEYPAD_TYPE_NEW:
+ keypad_profile = new_keypad_profile;
+ break;
+ case KEYPAD_TYPE_NEXCOM:
+ keypad_profile = nexcom_keypad_profile;
+ break;
+ default:
+ keypad_profile = NULL;
+ break;
+ }
+
+ if (!lcd.enabled && !keypad.enabled) {
+ /* no device enabled, let's exit */
+ pr_err("driver version " PANEL_VERSION " disabled.\n");
+ return -ENODEV;
+ }
+
+ err = parport_register_driver(&panel_driver);
+ if (err) {
+ pr_err("could not register with parport. Aborting.\n");
+ return err;
+ }
+
+ if (pprt)
+ pr_info("driver version " PANEL_VERSION
+ " registered on parport%d (io=0x%lx).\n", parport,
+ pprt->port->base);
+ else
+ pr_info("driver version " PANEL_VERSION
+ " not yet registered\n");
+ return 0;
+}
+
+static void __exit panel_cleanup_module(void)
+{
+
+ if (scan_timer.function != NULL)
+ del_timer_sync(&scan_timer);
+
+ if (pprt != NULL) {
+ if (keypad.enabled) {
+ misc_deregister(&keypad_dev);
+ keypad_initialized = 0;
+ }
+
+ if (lcd.enabled) {
+ panel_lcd_print("\x0cLCD driver " PANEL_VERSION
+ "\nunloaded.\x1b[Lc\x1b[Lb\x1b[L-");
+ misc_deregister(&lcd_dev);
+ lcd.initialized = false;
+ }
+
+ /* TODO: free all input signals */
+ parport_release(pprt);
+ parport_unregister_device(pprt);
+ pprt = NULL;
+ }
+ parport_unregister_driver(&panel_driver);
+}
+
+module_init(panel_init_module);
+module_exit(panel_cleanup_module);
+MODULE_AUTHOR("Willy Tarreau");
+MODULE_LICENSE("GPL");
+
+/*
+ * Local variables:
+ * c-indent-level: 4
+ * tab-width: 8
+ * End:
+ */
diff --git a/drivers/staging/rtl8188eu/Kconfig b/drivers/staging/rtl8188eu/Kconfig
new file mode 100644
index 000000000..5a38b4149
--- /dev/null
+++ b/drivers/staging/rtl8188eu/Kconfig
@@ -0,0 +1,20 @@
+config R8188EU
+ tristate "Realtek RTL8188EU Wireless LAN NIC driver"
+ depends on WLAN && USB
+ select WIRELESS_EXT
+ select WEXT_PRIV
+ ---help---
+ This option adds the Realtek RTL8188EU USB device such as TP-Link TL-WN725N.
+ If built as a module, it will be called r8188eu.
+
+if R8188EU
+
+config 88EU_AP_MODE
+ bool "Realtek RTL8188EU AP mode"
+ default y
+ ---help---
+ This option enables Access Point mode. Unless you know that your system
+ will never be used as an AP, or the target system has limited memory,
+ "Y" should be selected.
+
+endif
diff --git a/drivers/staging/rtl8188eu/Makefile b/drivers/staging/rtl8188eu/Makefile
new file mode 100644
index 000000000..31ac15961
--- /dev/null
+++ b/drivers/staging/rtl8188eu/Makefile
@@ -0,0 +1,55 @@
+r8188eu-y := \
+ core/rtw_ap.o \
+ core/rtw_cmd.o \
+ core/rtw_debug.o \
+ core/rtw_efuse.o \
+ core/rtw_ieee80211.o \
+ core/rtw_ioctl_set.o \
+ core/rtw_iol.o \
+ core/rtw_led.o \
+ core/rtw_mlme.o \
+ core/rtw_mlme_ext.o \
+ core/rtw_pwrctrl.o \
+ core/rtw_recv.o \
+ core/rtw_rf.o \
+ core/rtw_security.o \
+ core/rtw_sreset.o \
+ core/rtw_sta_mgt.o \
+ core/rtw_wlan_util.o \
+ core/rtw_xmit.o \
+ hal/fw.o \
+ hal/mac_cfg.o \
+ hal/bb_cfg.o \
+ hal/rf_cfg.o \
+ hal/pwrseqcmd.o \
+ hal/pwrseq.o \
+ hal/Hal8188ERateAdaptive.o\
+ hal/hal_intf.o \
+ hal/hal_com.o \
+ hal/odm.o \
+ hal/odm_HWConfig.o \
+ hal/odm_RTL8188E.o \
+ hal/rtl8188e_cmd.o \
+ hal/rtl8188e_dm.o \
+ hal/rtl8188e_hal_init.o \
+ hal/phy.o \
+ hal/rf.o \
+ hal/rtl8188e_rxdesc.o \
+ hal/rtl8188e_xmit.o \
+ hal/rtl8188eu_led.o \
+ hal/rtl8188eu_recv.o \
+ hal/rtl8188eu_xmit.o \
+ hal/usb_halinit.o \
+ os_dep/ioctl_linux.o \
+ os_dep/mlme_linux.o \
+ os_dep/os_intfs.o \
+ os_dep/osdep_service.o \
+ os_dep/recv_linux.o \
+ os_dep/rtw_android.o \
+ os_dep/usb_intf.o \
+ os_dep/usb_ops_linux.o \
+ os_dep/xmit_linux.o
+
+obj-$(CONFIG_R8188EU) := r8188eu.o
+
+ccflags-y += -D__CHECK_ENDIAN__ -I$(src)/include
diff --git a/drivers/staging/rtl8188eu/TODO b/drivers/staging/rtl8188eu/TODO
new file mode 100644
index 000000000..b574b235b
--- /dev/null
+++ b/drivers/staging/rtl8188eu/TODO
@@ -0,0 +1,19 @@
+TODO:
+- find and remove remaining code valid only for 5 HGz. Most of the obvious
+ ones have been removed, but things like channel > 14 still exist.
+- find and remove any code for other chips that is left over
+- convert any remaining unusual variable types
+- find codes that can use %pM and %Nph formatting
+- checkpatch.pl fixes - most of the remaining ones are lines too long. Many
+ of them will require refactoring
+- merge Realtek's bugfixes and new features into the driver
+- switch to use LIB80211
+- switch to use MAC80211
+- figure out what to do with this code in rtw_recv_indicatepkt():
+ rcu_read_lock();
+ rcu_dereference(padapter->pnetdev->rx_handler_data);
+ rcu_read_unlock();
+ Perhaps delete it, perhaps assign to some local variable.
+
+Please send any patches to Greg Kroah-Hartman <gregkh@linux.com>,
+and Larry Finger <Larry.Finger@lwfinger.net>.
diff --git a/drivers/staging/rtl8188eu/core/rtw_ap.c b/drivers/staging/rtl8188eu/core/rtw_ap.c
new file mode 100644
index 000000000..e65ee6e85
--- /dev/null
+++ b/drivers/staging/rtl8188eu/core/rtw_ap.c
@@ -0,0 +1,1963 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2012 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ *
+ ******************************************************************************/
+#define _RTW_AP_C_
+
+#include <osdep_service.h>
+#include <drv_types.h>
+#include <wifi.h>
+#include <ieee80211.h>
+#include <asm/unaligned.h>
+
+#ifdef CONFIG_88EU_AP_MODE
+
+void init_mlme_ap_info(struct adapter *padapter)
+{
+ struct mlme_priv *pmlmepriv = &(padapter->mlmepriv);
+ struct sta_priv *pstapriv = &padapter->stapriv;
+ struct wlan_acl_pool *pacl_list = &pstapriv->acl_list;
+
+
+ spin_lock_init(&pmlmepriv->bcn_update_lock);
+
+ /* for ACL */
+ _rtw_init_queue(&pacl_list->acl_node_q);
+
+ start_ap_mode(padapter);
+}
+
+void free_mlme_ap_info(struct adapter *padapter)
+{
+ struct sta_info *psta = NULL;
+ struct sta_priv *pstapriv = &padapter->stapriv;
+ struct mlme_priv *pmlmepriv = &(padapter->mlmepriv);
+ struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv;
+ struct mlme_ext_info *pmlmeinfo = &(pmlmeext->mlmext_info);
+
+ pmlmepriv->update_bcn = false;
+ pmlmeext->bstart_bss = false;
+
+ rtw_sta_flush(padapter);
+
+ pmlmeinfo->state = _HW_STATE_NOLINK_;
+
+ /* free_assoc_sta_resources */
+ rtw_free_all_stainfo(padapter);
+
+ /* free bc/mc sta_info */
+ psta = rtw_get_bcmc_stainfo(padapter);
+ spin_lock_bh(&(pstapriv->sta_hash_lock));
+ rtw_free_stainfo(padapter, psta);
+ spin_unlock_bh(&(pstapriv->sta_hash_lock));
+}
+
+static void update_BCNTIM(struct adapter *padapter)
+{
+ struct sta_priv *pstapriv = &padapter->stapriv;
+ struct mlme_ext_priv *pmlmeext = &(padapter->mlmeextpriv);
+ struct mlme_ext_info *pmlmeinfo = &(pmlmeext->mlmext_info);
+ struct wlan_bssid_ex *pnetwork_mlmeext = &(pmlmeinfo->network);
+ unsigned char *pie = pnetwork_mlmeext->IEs;
+
+ /* update TIM IE */
+ if (true) {
+ u8 *p, *dst_ie, *premainder_ie = NULL;
+ u8 *pbackup_remainder_ie = NULL;
+ uint offset, tmp_len, tim_ielen, tim_ie_offset, remainder_ielen;
+
+ p = rtw_get_ie(pie + _FIXED_IE_LENGTH_, _TIM_IE_, &tim_ielen, pnetwork_mlmeext->IELength - _FIXED_IE_LENGTH_);
+ if (p != NULL && tim_ielen > 0) {
+ tim_ielen += 2;
+ premainder_ie = p+tim_ielen;
+ tim_ie_offset = (int)(p - pie);
+ remainder_ielen = pnetwork_mlmeext->IELength - tim_ie_offset - tim_ielen;
+ /* append TIM IE from dst_ie offset */
+ dst_ie = p;
+ } else {
+ tim_ielen = 0;
+
+ /* calculate head_len */
+ offset = _FIXED_IE_LENGTH_;
+ offset += pnetwork_mlmeext->Ssid.SsidLength + 2;
+
+ /* get supported rates len */
+ p = rtw_get_ie(pie + _BEACON_IE_OFFSET_, _SUPPORTEDRATES_IE_, &tmp_len, (pnetwork_mlmeext->IELength - _BEACON_IE_OFFSET_));
+ if (p != NULL)
+ offset += tmp_len+2;
+
+ /* DS Parameter Set IE, len = 3 */
+ offset += 3;
+
+ premainder_ie = pie + offset;
+
+ remainder_ielen = pnetwork_mlmeext->IELength - offset - tim_ielen;
+
+ /* append TIM IE from offset */
+ dst_ie = pie + offset;
+ }
+
+ if (remainder_ielen > 0) {
+ pbackup_remainder_ie = rtw_malloc(remainder_ielen);
+ if (pbackup_remainder_ie && premainder_ie)
+ memcpy(pbackup_remainder_ie, premainder_ie, remainder_ielen);
+ }
+ *dst_ie++ = _TIM_IE_;
+
+ if ((pstapriv->tim_bitmap&0xff00) && (pstapriv->tim_bitmap&0x00fc))
+ tim_ielen = 5;
+ else
+ tim_ielen = 4;
+
+ *dst_ie++ = tim_ielen;
+
+ *dst_ie++ = 0;/* DTIM count */
+ *dst_ie++ = 1;/* DTIM period */
+
+ if (pstapriv->tim_bitmap&BIT(0))/* for bc/mc frames */
+ *dst_ie++ = BIT(0);/* bitmap ctrl */
+ else
+ *dst_ie++ = 0;
+
+ if (tim_ielen == 4) {
+ *dst_ie++ = pstapriv->tim_bitmap & 0xff;
+ } else if (tim_ielen == 5) {
+ put_unaligned_le16(pstapriv->tim_bitmap, dst_ie);
+ dst_ie += 2;
+ }
+
+ /* copy remainder IE */
+ if (pbackup_remainder_ie) {
+ memcpy(dst_ie, pbackup_remainder_ie, remainder_ielen);
+
+ kfree(pbackup_remainder_ie);
+ }
+ offset = (uint)(dst_ie - pie);
+ pnetwork_mlmeext->IELength = offset + remainder_ielen;
+ }
+
+ set_tx_beacon_cmd(padapter);
+}
+
+void rtw_add_bcn_ie(struct adapter *padapter, struct wlan_bssid_ex *pnetwork, u8 index, u8 *data, u8 len)
+{
+ struct ndis_802_11_var_ie *pIE;
+ u8 bmatch = false;
+ u8 *pie = pnetwork->IEs;
+ u8 *p = NULL, *dst_ie = NULL, *premainder_ie = NULL;
+ u8 *pbackup_remainder_ie = NULL;
+ u32 i, offset, ielen = 0, ie_offset, remainder_ielen = 0;
+
+ for (i = sizeof(struct ndis_802_11_fixed_ie); i < pnetwork->IELength;) {
+ pIE = (struct ndis_802_11_var_ie *)(pnetwork->IEs + i);
+
+ if (pIE->ElementID > index) {
+ break;
+ } else if (pIE->ElementID == index) { /* already exist the same IE */
+ p = (u8 *)pIE;
+ ielen = pIE->Length;
+ bmatch = true;
+ break;
+ }
+ p = (u8 *)pIE;
+ ielen = pIE->Length;
+ i += (pIE->Length + 2);
+ }
+
+ if (p != NULL && ielen > 0) {
+ ielen += 2;
+
+ premainder_ie = p+ielen;
+
+ ie_offset = (int)(p - pie);
+
+ remainder_ielen = pnetwork->IELength - ie_offset - ielen;
+
+ if (bmatch)
+ dst_ie = p;
+ else
+ dst_ie = (p+ielen);
+ }
+
+ if (remainder_ielen > 0) {
+ pbackup_remainder_ie = rtw_malloc(remainder_ielen);
+ if (pbackup_remainder_ie && premainder_ie)
+ memcpy(pbackup_remainder_ie, premainder_ie, remainder_ielen);
+ }
+
+ *dst_ie++ = index;
+ *dst_ie++ = len;
+
+ memcpy(dst_ie, data, len);
+ dst_ie += len;
+
+ /* copy remainder IE */
+ if (pbackup_remainder_ie) {
+ memcpy(dst_ie, pbackup_remainder_ie, remainder_ielen);
+
+ kfree(pbackup_remainder_ie);
+ }
+
+ offset = (uint)(dst_ie - pie);
+ pnetwork->IELength = offset + remainder_ielen;
+}
+
+void rtw_remove_bcn_ie(struct adapter *padapter, struct wlan_bssid_ex *pnetwork, u8 index)
+{
+ u8 *p, *dst_ie = NULL, *premainder_ie = NULL;
+ u8 *pbackup_remainder_ie = NULL;
+ uint offset, ielen, ie_offset, remainder_ielen = 0;
+ u8 *pie = pnetwork->IEs;
+
+ p = rtw_get_ie(pie + _FIXED_IE_LENGTH_, index, &ielen,
+ pnetwork->IELength - _FIXED_IE_LENGTH_);
+ if (p != NULL && ielen > 0) {
+ ielen += 2;
+
+ premainder_ie = p+ielen;
+
+ ie_offset = (int)(p - pie);
+
+ remainder_ielen = pnetwork->IELength - ie_offset - ielen;
+
+ dst_ie = p;
+ }
+
+ if (remainder_ielen > 0) {
+ pbackup_remainder_ie = rtw_malloc(remainder_ielen);
+ if (pbackup_remainder_ie && premainder_ie)
+ memcpy(pbackup_remainder_ie, premainder_ie, remainder_ielen);
+ }
+
+ /* copy remainder IE */
+ if (pbackup_remainder_ie) {
+ memcpy(dst_ie, pbackup_remainder_ie, remainder_ielen);
+
+ kfree(pbackup_remainder_ie);
+ }
+
+ offset = (uint)(dst_ie - pie);
+ pnetwork->IELength = offset + remainder_ielen;
+}
+
+static u8 chk_sta_is_alive(struct sta_info *psta)
+{
+ u8 ret = false;
+
+ if ((psta->sta_stats.last_rx_data_pkts + psta->sta_stats.last_rx_ctrl_pkts) ==
+ (psta->sta_stats.rx_data_pkts + psta->sta_stats.rx_ctrl_pkts))
+ ;
+ else
+ ret = true;
+
+ sta_update_last_rx_pkts(psta);
+
+ return ret;
+}
+
+void expire_timeout_chk(struct adapter *padapter)
+{
+ struct list_head *phead, *plist;
+ u8 updated = 0;
+ struct sta_info *psta = NULL;
+ struct sta_priv *pstapriv = &padapter->stapriv;
+ u8 chk_alive_num = 0;
+ char chk_alive_list[NUM_STA];
+ int i;
+
+ spin_lock_bh(&pstapriv->auth_list_lock);
+
+ phead = &pstapriv->auth_list;
+ plist = phead->next;
+
+ /* check auth_queue */
+ while (phead != plist) {
+ psta = container_of(plist, struct sta_info, auth_list);
+ plist = plist->next;
+
+ if (psta->expire_to > 0) {
+ psta->expire_to--;
+ if (psta->expire_to == 0) {
+ list_del_init(&psta->auth_list);
+ pstapriv->auth_list_cnt--;
+
+ DBG_88E("auth expire %6ph\n",
+ psta->hwaddr);
+
+ spin_unlock_bh(&pstapriv->auth_list_lock);
+
+ spin_lock_bh(&(pstapriv->sta_hash_lock));
+ rtw_free_stainfo(padapter, psta);
+ spin_unlock_bh(&(pstapriv->sta_hash_lock));
+
+ spin_lock_bh(&pstapriv->auth_list_lock);
+ }
+ }
+
+ }
+ spin_unlock_bh(&pstapriv->auth_list_lock);
+
+ psta = NULL;
+
+ spin_lock_bh(&pstapriv->asoc_list_lock);
+
+ phead = &pstapriv->asoc_list;
+ plist = phead->next;
+
+ /* check asoc_queue */
+ while (phead != plist) {
+ psta = container_of(plist, struct sta_info, asoc_list);
+ plist = plist->next;
+
+ if (chk_sta_is_alive(psta) || !psta->expire_to) {
+ psta->expire_to = pstapriv->expire_to;
+ psta->keep_alive_trycnt = 0;
+ psta->under_exist_checking = 0;
+ } else {
+ psta->expire_to--;
+ }
+
+ if (psta->expire_to <= 0) {
+ struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv;
+
+ if (padapter->registrypriv.wifi_spec == 1) {
+ psta->expire_to = pstapriv->expire_to;
+ continue;
+ }
+
+ if (psta->state & WIFI_SLEEP_STATE) {
+ if (!(psta->state & WIFI_STA_ALIVE_CHK_STATE)) {
+ /* to check if alive by another methods if station is at ps mode. */
+ psta->expire_to = pstapriv->expire_to;
+ psta->state |= WIFI_STA_ALIVE_CHK_STATE;
+
+ /* to update bcn with tim_bitmap for this station */
+ pstapriv->tim_bitmap |= BIT(psta->aid);
+ update_beacon(padapter, _TIM_IE_, NULL, false);
+
+ if (!pmlmeext->active_keep_alive_check)
+ continue;
+ }
+ }
+ if (pmlmeext->active_keep_alive_check) {
+ int stainfo_offset;
+
+ stainfo_offset = rtw_stainfo_offset(pstapriv, psta);
+ if (stainfo_offset_valid(stainfo_offset))
+ chk_alive_list[chk_alive_num++] = stainfo_offset;
+ continue;
+ }
+
+ list_del_init(&psta->asoc_list);
+ pstapriv->asoc_list_cnt--;
+
+ DBG_88E("asoc expire %pM, state = 0x%x\n", (psta->hwaddr), psta->state);
+ updated = ap_free_sta(padapter, psta, true, WLAN_REASON_DEAUTH_LEAVING);
+ } else {
+ /* TODO: Aging mechanism to digest frames in sleep_q to avoid running out of xmitframe */
+ if (psta->sleepq_len > (NR_XMITFRAME/pstapriv->asoc_list_cnt) &&
+ padapter->xmitpriv.free_xmitframe_cnt < (NR_XMITFRAME/pstapriv->asoc_list_cnt/2)) {
+ DBG_88E("%s sta:%pM, sleepq_len:%u, free_xmitframe_cnt:%u, asoc_list_cnt:%u, clear sleep_q\n", __func__,
+ (psta->hwaddr), psta->sleepq_len,
+ padapter->xmitpriv.free_xmitframe_cnt,
+ pstapriv->asoc_list_cnt);
+ wakeup_sta_to_xmit(padapter, psta);
+ }
+ }
+ }
+
+ spin_unlock_bh(&pstapriv->asoc_list_lock);
+
+ if (chk_alive_num) {
+ u8 backup_oper_channel = 0;
+ struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv;
+ /* switch to correct channel of current network before issue keep-alive frames */
+ if (rtw_get_oper_ch(padapter) != pmlmeext->cur_channel) {
+ backup_oper_channel = rtw_get_oper_ch(padapter);
+ SelectChannel(padapter, pmlmeext->cur_channel);
+ }
+
+ /* issue null data to check sta alive*/
+ for (i = 0; i < chk_alive_num; i++) {
+ int ret = _FAIL;
+
+ psta = rtw_get_stainfo_by_offset(pstapriv, chk_alive_list[i]);
+
+ if (psta->state & WIFI_SLEEP_STATE)
+ ret = issue_nulldata(padapter, psta->hwaddr, 0, 1, 50);
+ else
+ ret = issue_nulldata(padapter, psta->hwaddr, 0, 3, 50);
+
+ psta->keep_alive_trycnt++;
+ if (ret == _SUCCESS) {
+ DBG_88E("asoc check, sta(%pM) is alive\n", (psta->hwaddr));
+ psta->expire_to = pstapriv->expire_to;
+ psta->keep_alive_trycnt = 0;
+ continue;
+ } else if (psta->keep_alive_trycnt <= 3) {
+ DBG_88E("ack check for asoc expire, keep_alive_trycnt =%d\n", psta->keep_alive_trycnt);
+ psta->expire_to = 1;
+ continue;
+ }
+
+ psta->keep_alive_trycnt = 0;
+
+ DBG_88E("asoc expire %pM, state = 0x%x\n", (psta->hwaddr), psta->state);
+ spin_lock_bh(&pstapriv->asoc_list_lock);
+ list_del_init(&psta->asoc_list);
+ pstapriv->asoc_list_cnt--;
+ updated = ap_free_sta(padapter, psta, true, WLAN_REASON_DEAUTH_LEAVING);
+ spin_unlock_bh(&pstapriv->asoc_list_lock);
+ }
+
+ if (backup_oper_channel > 0) /* back to the original operation channel */
+ SelectChannel(padapter, backup_oper_channel);
+ }
+
+ associated_clients_update(padapter, updated);
+}
+
+void add_RATid(struct adapter *padapter, struct sta_info *psta, u8 rssi_level)
+{
+ int i;
+ u8 rf_type;
+ u32 init_rate = 0;
+ unsigned char sta_band = 0, raid, shortGIrate = false;
+ unsigned char limit;
+ unsigned int tx_ra_bitmap = 0;
+ struct ht_priv *psta_ht = NULL;
+ struct mlme_priv *pmlmepriv = &(padapter->mlmepriv);
+ struct wlan_bssid_ex *pcur_network = (struct wlan_bssid_ex *)&pmlmepriv->cur_network.network;
+
+ if (psta)
+ psta_ht = &psta->htpriv;
+ else
+ return;
+
+ if (!(psta->state & _FW_LINKED))
+ return;
+
+ /* b/g mode ra_bitmap */
+ for (i = 0; i < sizeof(psta->bssrateset); i++) {
+ if (psta->bssrateset[i])
+ tx_ra_bitmap |= rtw_get_bit_value_from_ieee_value(psta->bssrateset[i]&0x7f);
+ }
+ /* n mode ra_bitmap */
+ if (psta_ht->ht_option) {
+ rtw_hal_get_hwreg(padapter, HW_VAR_RF_TYPE, (u8 *)(&rf_type));
+ if (rf_type == RF_2T2R)
+ limit = 16;/* 2R */
+ else
+ limit = 8;/* 1R */
+
+ for (i = 0; i < limit; i++) {
+ if (psta_ht->ht_cap.supp_mcs_set[i/8] & BIT(i%8))
+ tx_ra_bitmap |= BIT(i+12);
+ }
+
+ /* max short GI rate */
+ shortGIrate = psta_ht->sgi;
+ }
+
+ if (pcur_network->Configuration.DSConfig > 14) {
+ /* 5G band */
+ if (tx_ra_bitmap & 0xffff000)
+ sta_band |= WIRELESS_11_5N | WIRELESS_11A;
+ else
+ sta_band |= WIRELESS_11A;
+ } else {
+ if (tx_ra_bitmap & 0xffff000)
+ sta_band |= WIRELESS_11_24N | WIRELESS_11G | WIRELESS_11B;
+ else if (tx_ra_bitmap & 0xff0)
+ sta_band |= WIRELESS_11G | WIRELESS_11B;
+ else
+ sta_band |= WIRELESS_11B;
+ }
+
+ psta->wireless_mode = sta_band;
+
+ raid = networktype_to_raid(sta_band);
+ init_rate = get_highest_rate_idx(tx_ra_bitmap&0x0fffffff)&0x3f;
+
+ if (psta->aid < NUM_STA) {
+ u8 arg = 0;
+
+ arg = psta->mac_id&0x1f;
+
+ arg |= BIT(7);/* support entry 2~31 */
+
+ if (shortGIrate)
+ arg |= BIT(5);
+
+ tx_ra_bitmap |= ((raid<<28)&0xf0000000);
+
+ DBG_88E("%s => mac_id:%d , raid:%d , bitmap = 0x%x, arg = 0x%x\n",
+ __func__, psta->mac_id, raid, tx_ra_bitmap, arg);
+
+ /* bitmap[0:27] = tx_rate_bitmap */
+ /* bitmap[28:31]= Rate Adaptive id */
+ /* arg[0:4] = macid */
+ /* arg[5] = Short GI */
+ rtw_hal_add_ra_tid(padapter, tx_ra_bitmap, arg, rssi_level);
+
+ if (shortGIrate)
+ init_rate |= BIT(6);
+
+ /* set ra_id, init_rate */
+ psta->raid = raid;
+ psta->init_rate = init_rate;
+
+ } else {
+ DBG_88E("station aid %d exceed the max number\n", psta->aid);
+ }
+}
+
+static void update_bmc_sta(struct adapter *padapter)
+{
+ u32 init_rate = 0;
+ unsigned char network_type, raid;
+ int i, supportRateNum = 0;
+ unsigned int tx_ra_bitmap = 0;
+ struct mlme_priv *pmlmepriv = &(padapter->mlmepriv);
+ struct wlan_bssid_ex *pcur_network = (struct wlan_bssid_ex *)&pmlmepriv->cur_network.network;
+ struct sta_info *psta = rtw_get_bcmc_stainfo(padapter);
+
+ if (psta) {
+ psta->aid = 0;/* default set to 0 */
+ psta->mac_id = psta->aid + 1;
+
+ psta->qos_option = 0;
+ psta->htpriv.ht_option = false;
+
+ psta->ieee8021x_blocked = 0;
+
+ memset((void *)&psta->sta_stats, 0, sizeof(struct stainfo_stats));
+
+ /* prepare for add_RATid */
+ supportRateNum = rtw_get_rateset_len((u8 *)&pcur_network->SupportedRates);
+ network_type = rtw_check_network_type((u8 *)&pcur_network->SupportedRates, supportRateNum, 1);
+
+ memcpy(psta->bssrateset, &pcur_network->SupportedRates, supportRateNum);
+ psta->bssratelen = supportRateNum;
+
+ /* b/g mode ra_bitmap */
+ for (i = 0; i < supportRateNum; i++) {
+ if (psta->bssrateset[i])
+ tx_ra_bitmap |= rtw_get_bit_value_from_ieee_value(psta->bssrateset[i]&0x7f);
+ }
+
+ if (pcur_network->Configuration.DSConfig > 14) {
+ /* force to A mode. 5G doesn't support CCK rates */
+ network_type = WIRELESS_11A;
+ tx_ra_bitmap = 0x150; /* 6, 12, 24 Mbps */
+ } else {
+ /* force to b mode */
+ network_type = WIRELESS_11B;
+ tx_ra_bitmap = 0xf;
+ }
+
+ raid = networktype_to_raid(network_type);
+ init_rate = get_highest_rate_idx(tx_ra_bitmap&0x0fffffff)&0x3f;
+
+ /* ap mode */
+ rtw_hal_set_odm_var(padapter, HAL_ODM_STA_INFO, psta, true);
+
+ {
+ u8 arg = 0;
+
+ arg = psta->mac_id&0x1f;
+ arg |= BIT(7);
+ tx_ra_bitmap |= ((raid<<28)&0xf0000000);
+ DBG_88E("update_bmc_sta, mask = 0x%x, arg = 0x%x\n", tx_ra_bitmap, arg);
+
+ /* bitmap[0:27] = tx_rate_bitmap */
+ /* bitmap[28:31]= Rate Adaptive id */
+ /* arg[0:4] = macid */
+ /* arg[5] = Short GI */
+ rtw_hal_add_ra_tid(padapter, tx_ra_bitmap, arg, 0);
+ }
+ /* set ra_id, init_rate */
+ psta->raid = raid;
+ psta->init_rate = init_rate;
+
+ rtw_stassoc_hw_rpt(padapter, psta);
+
+ spin_lock_bh(&psta->lock);
+ psta->state = _FW_LINKED;
+ spin_unlock_bh(&psta->lock);
+
+ } else {
+ DBG_88E("add_RATid_bmc_sta error!\n");
+ }
+}
+
+/* notes: */
+/* AID: 1~MAX for sta and 0 for bc/mc in ap/adhoc mode */
+/* MAC_ID = AID+1 for sta in ap/adhoc mode */
+/* MAC_ID = 1 for bc/mc for sta/ap/adhoc */
+/* MAC_ID = 0 for bssid for sta/ap/adhoc */
+/* CAM_ID = 0~3 for default key, cmd_id = macid + 3, macid = aid+1; */
+
+void update_sta_info_apmode(struct adapter *padapter, struct sta_info *psta)
+{
+ struct mlme_priv *pmlmepriv = &(padapter->mlmepriv);
+ struct security_priv *psecuritypriv = &padapter->securitypriv;
+ struct mlme_ext_priv *pmlmeext = &(padapter->mlmeextpriv);
+ struct ht_priv *phtpriv_ap = &pmlmepriv->htpriv;
+ struct ht_priv *phtpriv_sta = &psta->htpriv;
+
+ psta->mac_id = psta->aid+1;
+ DBG_88E("%s\n", __func__);
+
+ /* ap mode */
+ rtw_hal_set_odm_var(padapter, HAL_ODM_STA_INFO, psta, true);
+
+ if (psecuritypriv->dot11AuthAlgrthm == dot11AuthAlgrthm_8021X)
+ psta->ieee8021x_blocked = true;
+ else
+ psta->ieee8021x_blocked = false;
+
+
+ /* update sta's cap */
+
+ /* ERP */
+ VCS_update(padapter, psta);
+ /* HT related cap */
+ if (phtpriv_sta->ht_option) {
+ /* check if sta supports rx ampdu */
+ phtpriv_sta->ampdu_enable = phtpriv_ap->ampdu_enable;
+
+ /* check if sta support s Short GI */
+ if ((phtpriv_sta->ht_cap.cap_info & phtpriv_ap->ht_cap.cap_info) & (IEEE80211_HT_CAP_SGI_20 | IEEE80211_HT_CAP_SGI_40))
+ phtpriv_sta->sgi = true;
+
+ /* bwmode */
+ if ((phtpriv_sta->ht_cap.cap_info & phtpriv_ap->ht_cap.cap_info) & IEEE80211_HT_CAP_SUP_WIDTH) {
+ phtpriv_sta->bwmode = pmlmeext->cur_bwmode;
+ phtpriv_sta->ch_offset = pmlmeext->cur_ch_offset;
+ }
+ psta->qos_option = true;
+ } else {
+ phtpriv_sta->ampdu_enable = false;
+ phtpriv_sta->sgi = false;
+ phtpriv_sta->bwmode = HT_CHANNEL_WIDTH_20;
+ phtpriv_sta->ch_offset = HAL_PRIME_CHNL_OFFSET_DONT_CARE;
+ }
+
+ /* Rx AMPDU */
+ send_delba(padapter, 0, psta->hwaddr);/* recipient */
+
+ /* TX AMPDU */
+ send_delba(padapter, 1, psta->hwaddr);/* originator */
+ phtpriv_sta->agg_enable_bitmap = 0x0;/* reset */
+ phtpriv_sta->candidate_tid_bitmap = 0x0;/* reset */
+
+ /* todo: init other variables */
+
+ memset((void *)&psta->sta_stats, 0, sizeof(struct stainfo_stats));
+
+ spin_lock_bh(&psta->lock);
+ psta->state |= _FW_LINKED;
+ spin_unlock_bh(&psta->lock);
+}
+
+static void update_hw_ht_param(struct adapter *padapter)
+{
+ unsigned char max_AMPDU_len;
+ unsigned char min_MPDU_spacing;
+ struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv;
+ struct mlme_ext_info *pmlmeinfo = &(pmlmeext->mlmext_info);
+
+ DBG_88E("%s\n", __func__);
+
+ /* handle A-MPDU parameter field */
+ /*
+ AMPDU_para [1:0]:Max AMPDU Len => 0:8k , 1:16k, 2:32k, 3:64k
+ AMPDU_para [4:2]:Min MPDU Start Spacing
+ */
+ max_AMPDU_len = pmlmeinfo->HT_caps.u.HT_cap_element.AMPDU_para & 0x03;
+
+ min_MPDU_spacing = (pmlmeinfo->HT_caps.u.HT_cap_element.AMPDU_para & 0x1c) >> 2;
+
+ rtw_hal_set_hwreg(padapter, HW_VAR_AMPDU_MIN_SPACE, (u8 *)(&min_MPDU_spacing));
+
+ rtw_hal_set_hwreg(padapter, HW_VAR_AMPDU_FACTOR, (u8 *)(&max_AMPDU_len));
+
+ /* */
+ /* Config SM Power Save setting */
+ /* */
+ pmlmeinfo->SM_PS = (le16_to_cpu(pmlmeinfo->HT_caps.u.HT_cap_element.HT_caps_info) & 0x0C) >> 2;
+ if (pmlmeinfo->SM_PS == WLAN_HT_CAP_SM_PS_STATIC)
+ DBG_88E("%s(): WLAN_HT_CAP_SM_PS_STATIC\n", __func__);
+}
+
+static void start_bss_network(struct adapter *padapter, u8 *pbuf)
+{
+ u8 *p;
+ u8 val8, cur_channel, cur_bwmode, cur_ch_offset;
+ u16 bcn_interval;
+ u32 acparm;
+ int ie_len;
+ struct registry_priv *pregpriv = &padapter->registrypriv;
+ struct mlme_priv *pmlmepriv = &(padapter->mlmepriv);
+ struct security_priv *psecuritypriv = &(padapter->securitypriv);
+ struct wlan_bssid_ex *pnetwork = (struct wlan_bssid_ex *)&pmlmepriv->cur_network.network;
+ struct mlme_ext_priv *pmlmeext = &(padapter->mlmeextpriv);
+ struct mlme_ext_info *pmlmeinfo = &(pmlmeext->mlmext_info);
+ struct wlan_bssid_ex *pnetwork_mlmeext = &(pmlmeinfo->network);
+ struct HT_info_element *pht_info = NULL;
+
+ bcn_interval = (u16)pnetwork->Configuration.BeaconPeriod;
+ cur_channel = pnetwork->Configuration.DSConfig;
+ cur_bwmode = HT_CHANNEL_WIDTH_20;
+ cur_ch_offset = HAL_PRIME_CHNL_OFFSET_DONT_CARE;
+
+
+ /* check if there is wps ie, */
+ /* if there is wpsie in beacon, the hostapd will update beacon twice when stating hostapd, */
+ /* and at first time the security ie (RSN/WPA IE) will not include in beacon. */
+ if (!rtw_get_wps_ie(pnetwork->IEs+_FIXED_IE_LENGTH_, pnetwork->IELength-_FIXED_IE_LENGTH_, NULL, NULL))
+ pmlmeext->bstart_bss = true;
+
+ /* todo: update wmm, ht cap */
+ if (pmlmepriv->qospriv.qos_option)
+ pmlmeinfo->WMM_enable = true;
+ if (pmlmepriv->htpriv.ht_option) {
+ pmlmeinfo->WMM_enable = true;
+ pmlmeinfo->HT_enable = true;
+
+ update_hw_ht_param(padapter);
+ }
+
+ if (pmlmepriv->cur_network.join_res != true) { /* setting only at first time */
+ /* WEP Key will be set before this function, do not clear CAM. */
+ if ((psecuritypriv->dot11PrivacyAlgrthm != _WEP40_) &&
+ (psecuritypriv->dot11PrivacyAlgrthm != _WEP104_))
+ flush_all_cam_entry(padapter); /* clear CAM */
+ }
+
+ /* set MSR to AP_Mode */
+ Set_MSR(padapter, _HW_STATE_AP_);
+
+ /* Set BSSID REG */
+ rtw_hal_set_hwreg(padapter, HW_VAR_BSSID, pnetwork->MacAddress);
+
+ /* Set EDCA param reg */
+ acparm = 0x002F3217; /* VO */
+ rtw_hal_set_hwreg(padapter, HW_VAR_AC_PARAM_VO, (u8 *)(&acparm));
+ acparm = 0x005E4317; /* VI */
+ rtw_hal_set_hwreg(padapter, HW_VAR_AC_PARAM_VI, (u8 *)(&acparm));
+ acparm = 0x005ea42b;
+ rtw_hal_set_hwreg(padapter, HW_VAR_AC_PARAM_BE, (u8 *)(&acparm));
+ acparm = 0x0000A444; /* BK */
+ rtw_hal_set_hwreg(padapter, HW_VAR_AC_PARAM_BK, (u8 *)(&acparm));
+
+ /* Set Security */
+ val8 = (psecuritypriv->dot11AuthAlgrthm == dot11AuthAlgrthm_8021X) ? 0xcc : 0xcf;
+ rtw_hal_set_hwreg(padapter, HW_VAR_SEC_CFG, (u8 *)(&val8));
+
+ /* Beacon Control related register */
+ rtw_hal_set_hwreg(padapter, HW_VAR_BEACON_INTERVAL, (u8 *)(&bcn_interval));
+
+ UpdateBrateTbl(padapter, pnetwork->SupportedRates);
+ rtw_hal_set_hwreg(padapter, HW_VAR_BASIC_RATE, pnetwork->SupportedRates);
+
+ if (!pmlmepriv->cur_network.join_res) { /* setting only at first time */
+ /* turn on all dynamic functions */
+ Switch_DM_Func(padapter, DYNAMIC_ALL_FUNC_ENABLE, true);
+ }
+ /* set channel, bwmode */
+ p = rtw_get_ie((pnetwork->IEs + sizeof(struct ndis_802_11_fixed_ie)), _HT_ADD_INFO_IE_, &ie_len, (pnetwork->IELength - sizeof(struct ndis_802_11_fixed_ie)));
+ if (p && ie_len) {
+ pht_info = (struct HT_info_element *)(p+2);
+
+ if ((pregpriv->cbw40_enable) && (pht_info->infos[0] & BIT(2))) {
+ /* switch to the 40M Hz mode */
+ cur_bwmode = HT_CHANNEL_WIDTH_40;
+ switch (pht_info->infos[0] & 0x3) {
+ case 1:
+ cur_ch_offset = HAL_PRIME_CHNL_OFFSET_LOWER;
+ break;
+ case 3:
+ cur_ch_offset = HAL_PRIME_CHNL_OFFSET_UPPER;
+ break;
+ default:
+ cur_ch_offset = HAL_PRIME_CHNL_OFFSET_DONT_CARE;
+ break;
+ }
+ }
+ }
+ /* TODO: need to judge the phy parameters on concurrent mode for single phy */
+ set_channel_bwmode(padapter, cur_channel, cur_ch_offset, cur_bwmode);
+
+ DBG_88E("CH =%d, BW =%d, offset =%d\n", cur_channel, cur_bwmode, cur_ch_offset);
+
+ /* */
+ pmlmeext->cur_channel = cur_channel;
+ pmlmeext->cur_bwmode = cur_bwmode;
+ pmlmeext->cur_ch_offset = cur_ch_offset;
+ pmlmeext->cur_wireless_mode = pmlmepriv->cur_network.network_type;
+
+ /* update cur_wireless_mode */
+ update_wireless_mode(padapter);
+
+ /* update capability after cur_wireless_mode updated */
+ update_capinfo(padapter, rtw_get_capability((struct wlan_bssid_ex *)pnetwork));
+
+ /* let pnetwork_mlmeext == pnetwork_mlme. */
+ memcpy(pnetwork_mlmeext, pnetwork, pnetwork->Length);
+
+ if (pmlmeext->bstart_bss) {
+ update_beacon(padapter, _TIM_IE_, NULL, false);
+
+ /* issue beacon frame */
+ if (send_beacon(padapter) == _FAIL)
+ DBG_88E("issue_beacon, fail!\n");
+ }
+
+ /* update bc/mc sta_info */
+ update_bmc_sta(padapter);
+}
+
+int rtw_check_beacon_data(struct adapter *padapter, u8 *pbuf, int len)
+{
+ int ret = _SUCCESS;
+ u8 *p;
+ u8 *pHT_caps_ie = NULL;
+ u8 *pHT_info_ie = NULL;
+ struct sta_info *psta = NULL;
+ u16 cap, ht_cap = false;
+ uint ie_len = 0;
+ int group_cipher, pairwise_cipher;
+ u8 channel, network_type, supportRate[NDIS_802_11_LENGTH_RATES_EX];
+ int supportRateNum = 0;
+ u8 OUI1[] = {0x00, 0x50, 0xf2, 0x01};
+ u8 WMM_PARA_IE[] = {0x00, 0x50, 0xf2, 0x02, 0x01, 0x01};
+ struct registry_priv *pregistrypriv = &padapter->registrypriv;
+ struct security_priv *psecuritypriv = &padapter->securitypriv;
+ struct mlme_priv *pmlmepriv = &(padapter->mlmepriv);
+ struct wlan_bssid_ex *pbss_network = (struct wlan_bssid_ex *)&pmlmepriv->cur_network.network;
+ u8 *ie = pbss_network->IEs;
+
+ /* SSID */
+ /* Supported rates */
+ /* DS Params */
+ /* WLAN_EID_COUNTRY */
+ /* ERP Information element */
+ /* Extended supported rates */
+ /* WPA/WPA2 */
+ /* Wi-Fi Wireless Multimedia Extensions */
+ /* ht_capab, ht_oper */
+ /* WPS IE */
+
+ DBG_88E("%s, len =%d\n", __func__, len);
+
+ if (check_fwstate(pmlmepriv, WIFI_AP_STATE) != true)
+ return _FAIL;
+
+
+ if (len > MAX_IE_SZ)
+ return _FAIL;
+
+ pbss_network->IELength = len;
+
+ memset(ie, 0, MAX_IE_SZ);
+
+ memcpy(ie, pbuf, pbss_network->IELength);
+
+
+ if (pbss_network->InfrastructureMode != Ndis802_11APMode)
+ return _FAIL;
+
+ pbss_network->Rssi = 0;
+
+ ether_addr_copy(pbss_network->MacAddress, myid(&(padapter->eeprompriv)));
+
+ /* beacon interval */
+ p = rtw_get_beacon_interval_from_ie(ie);/* 8: TimeStamp, 2: Beacon Interval 2:Capability */
+ pbss_network->Configuration.BeaconPeriod = get_unaligned_le16(p);
+
+ /* capability */
+ cap = get_unaligned_le16(ie);
+
+ /* SSID */
+ p = rtw_get_ie(ie + _BEACON_IE_OFFSET_, _SSID_IE_, &ie_len, (pbss_network->IELength - _BEACON_IE_OFFSET_));
+ if (p && ie_len > 0) {
+ memset(&pbss_network->Ssid, 0, sizeof(struct ndis_802_11_ssid));
+ memcpy(pbss_network->Ssid.Ssid, (p + 2), ie_len);
+ pbss_network->Ssid.SsidLength = ie_len;
+ }
+
+ /* channel */
+ channel = 0;
+ pbss_network->Configuration.Length = 0;
+ p = rtw_get_ie(ie + _BEACON_IE_OFFSET_, _DSSET_IE_, &ie_len, (pbss_network->IELength - _BEACON_IE_OFFSET_));
+ if (p && ie_len > 0)
+ channel = *(p + 2);
+
+ pbss_network->Configuration.DSConfig = channel;
+
+ memset(supportRate, 0, NDIS_802_11_LENGTH_RATES_EX);
+ /* get supported rates */
+ p = rtw_get_ie(ie + _BEACON_IE_OFFSET_, _SUPPORTEDRATES_IE_, &ie_len, (pbss_network->IELength - _BEACON_IE_OFFSET_));
+ if (p != NULL) {
+ memcpy(supportRate, p+2, ie_len);
+ supportRateNum = ie_len;
+ }
+
+ /* get ext_supported rates */
+ p = rtw_get_ie(ie + _BEACON_IE_OFFSET_, _EXT_SUPPORTEDRATES_IE_, &ie_len, pbss_network->IELength - _BEACON_IE_OFFSET_);
+ if (p != NULL) {
+ memcpy(supportRate+supportRateNum, p+2, ie_len);
+ supportRateNum += ie_len;
+ }
+
+ network_type = rtw_check_network_type(supportRate, supportRateNum, channel);
+
+ rtw_set_supported_rate(pbss_network->SupportedRates, network_type);
+
+ /* parsing ERP_IE */
+ p = rtw_get_ie(ie + _BEACON_IE_OFFSET_, _ERPINFO_IE_, &ie_len, (pbss_network->IELength - _BEACON_IE_OFFSET_));
+ if (p && ie_len > 0)
+ ERP_IE_handler(padapter, (struct ndis_802_11_var_ie *)p);
+
+ /* update privacy/security */
+ if (cap & BIT(4))
+ pbss_network->Privacy = 1;
+ else
+ pbss_network->Privacy = 0;
+
+ psecuritypriv->wpa_psk = 0;
+
+ /* wpa2 */
+ group_cipher = 0;
+ pairwise_cipher = 0;
+ psecuritypriv->wpa2_group_cipher = _NO_PRIVACY_;
+ psecuritypriv->wpa2_pairwise_cipher = _NO_PRIVACY_;
+ p = rtw_get_ie(ie + _BEACON_IE_OFFSET_, _RSN_IE_2_, &ie_len, (pbss_network->IELength - _BEACON_IE_OFFSET_));
+ if (p && ie_len > 0) {
+ if (rtw_parse_wpa2_ie(p, ie_len+2, &group_cipher, &pairwise_cipher, NULL) == _SUCCESS) {
+ psecuritypriv->dot11AuthAlgrthm = dot11AuthAlgrthm_8021X;
+
+ psecuritypriv->dot8021xalg = 1;/* psk, todo:802.1x */
+ psecuritypriv->wpa_psk |= BIT(1);
+
+ psecuritypriv->wpa2_group_cipher = group_cipher;
+ psecuritypriv->wpa2_pairwise_cipher = pairwise_cipher;
+ }
+ }
+ /* wpa */
+ ie_len = 0;
+ group_cipher = 0;
+ pairwise_cipher = 0;
+ psecuritypriv->wpa_group_cipher = _NO_PRIVACY_;
+ psecuritypriv->wpa_pairwise_cipher = _NO_PRIVACY_;
+ for (p = ie + _BEACON_IE_OFFSET_;; p += (ie_len + 2)) {
+ p = rtw_get_ie(p, _SSN_IE_1_, &ie_len,
+ (pbss_network->IELength - _BEACON_IE_OFFSET_ - (ie_len + 2)));
+ if ((p) && (!memcmp(p+2, OUI1, 4))) {
+ if (rtw_parse_wpa_ie(p, ie_len+2, &group_cipher,
+ &pairwise_cipher, NULL) == _SUCCESS) {
+ psecuritypriv->dot11AuthAlgrthm = dot11AuthAlgrthm_8021X;
+
+ psecuritypriv->dot8021xalg = 1;/* psk, todo:802.1x */
+
+ psecuritypriv->wpa_psk |= BIT(0);
+
+ psecuritypriv->wpa_group_cipher = group_cipher;
+ psecuritypriv->wpa_pairwise_cipher = pairwise_cipher;
+ }
+ break;
+ }
+ if ((p == NULL) || (ie_len == 0))
+ break;
+ }
+
+ /* wmm */
+ ie_len = 0;
+ pmlmepriv->qospriv.qos_option = 0;
+ if (pregistrypriv->wmm_enable) {
+ for (p = ie + _BEACON_IE_OFFSET_;; p += (ie_len + 2)) {
+ p = rtw_get_ie(p, _VENDOR_SPECIFIC_IE_, &ie_len,
+ (pbss_network->IELength - _BEACON_IE_OFFSET_ - (ie_len + 2)));
+ if ((p) && !memcmp(p+2, WMM_PARA_IE, 6)) {
+ pmlmepriv->qospriv.qos_option = 1;
+
+ *(p+8) |= BIT(7);/* QoS Info, support U-APSD */
+
+ /* disable all ACM bits since the WMM admission control is not supported */
+ *(p + 10) &= ~BIT(4); /* BE */
+ *(p + 14) &= ~BIT(4); /* BK */
+ *(p + 18) &= ~BIT(4); /* VI */
+ *(p + 22) &= ~BIT(4); /* VO */
+ break;
+ }
+
+ if ((p == NULL) || (ie_len == 0))
+ break;
+ }
+ }
+ /* parsing HT_CAP_IE */
+ p = rtw_get_ie(ie + _BEACON_IE_OFFSET_, _HT_CAPABILITY_IE_, &ie_len,
+ (pbss_network->IELength - _BEACON_IE_OFFSET_));
+ if (p && ie_len > 0) {
+ u8 rf_type;
+ struct rtw_ieee80211_ht_cap *pht_cap = (struct rtw_ieee80211_ht_cap *)(p+2);
+
+ pHT_caps_ie = p;
+ ht_cap = true;
+ network_type |= WIRELESS_11_24N;
+
+ rtw_hal_get_hwreg(padapter, HW_VAR_RF_TYPE, (u8 *)(&rf_type));
+
+ if ((psecuritypriv->wpa_pairwise_cipher & WPA_CIPHER_CCMP) ||
+ (psecuritypriv->wpa2_pairwise_cipher & WPA_CIPHER_CCMP))
+ pht_cap->ampdu_params_info |= (IEEE80211_HT_CAP_AMPDU_DENSITY&(0x07<<2));
+ else
+ pht_cap->ampdu_params_info |= (IEEE80211_HT_CAP_AMPDU_DENSITY&0x00);
+
+ /* set Max Rx AMPDU size to 64K */
+ pht_cap->ampdu_params_info |= (IEEE80211_HT_CAP_AMPDU_FACTOR & 0x03);
+
+ if (rf_type == RF_1T1R) {
+ pht_cap->supp_mcs_set[0] = 0xff;
+ pht_cap->supp_mcs_set[1] = 0x0;
+ }
+ memcpy(&pmlmepriv->htpriv.ht_cap, p+2, ie_len);
+ }
+
+ /* parsing HT_INFO_IE */
+ p = rtw_get_ie(ie + _BEACON_IE_OFFSET_, _HT_ADD_INFO_IE_, &ie_len,
+ (pbss_network->IELength - _BEACON_IE_OFFSET_));
+ if (p && ie_len > 0)
+ pHT_info_ie = p;
+ switch (network_type) {
+ case WIRELESS_11B:
+ pbss_network->NetworkTypeInUse = Ndis802_11DS;
+ break;
+ case WIRELESS_11G:
+ case WIRELESS_11BG:
+ case WIRELESS_11G_24N:
+ case WIRELESS_11BG_24N:
+ pbss_network->NetworkTypeInUse = Ndis802_11OFDM24;
+ break;
+ case WIRELESS_11A:
+ pbss_network->NetworkTypeInUse = Ndis802_11OFDM5;
+ break;
+ default:
+ pbss_network->NetworkTypeInUse = Ndis802_11OFDM24;
+ break;
+ }
+
+ pmlmepriv->cur_network.network_type = network_type;
+
+ pmlmepriv->htpriv.ht_option = false;
+
+ if ((psecuritypriv->wpa2_pairwise_cipher & WPA_CIPHER_TKIP) ||
+ (psecuritypriv->wpa_pairwise_cipher & WPA_CIPHER_TKIP)) {
+ /* todo: */
+ /* ht_cap = false; */
+ }
+
+ /* ht_cap */
+ if (pregistrypriv->ht_enable && ht_cap) {
+ pmlmepriv->htpriv.ht_option = true;
+ pmlmepriv->qospriv.qos_option = 1;
+
+ if (pregistrypriv->ampdu_enable == 1)
+ pmlmepriv->htpriv.ampdu_enable = true;
+ HT_caps_handler(padapter, (struct ndis_802_11_var_ie *)pHT_caps_ie);
+
+ HT_info_handler(padapter, (struct ndis_802_11_var_ie *)pHT_info_ie);
+ }
+
+ pbss_network->Length = get_wlan_bssid_ex_sz((struct wlan_bssid_ex *)pbss_network);
+
+ /* issue beacon to start bss network */
+ start_bss_network(padapter, (u8 *)pbss_network);
+
+ /* alloc sta_info for ap itself */
+ psta = rtw_get_stainfo(&padapter->stapriv, pbss_network->MacAddress);
+ if (!psta) {
+ psta = rtw_alloc_stainfo(&padapter->stapriv, pbss_network->MacAddress);
+ if (psta == NULL)
+ return _FAIL;
+ }
+
+ /* fix bug of flush_cam_entry at STOP AP mode */
+ psta->state |= WIFI_AP_STATE;
+ rtw_indicate_connect(padapter);
+ pmlmepriv->cur_network.join_res = true;/* for check if already set beacon */
+ return ret;
+}
+
+void rtw_set_macaddr_acl(struct adapter *padapter, int mode)
+{
+ struct sta_priv *pstapriv = &padapter->stapriv;
+ struct wlan_acl_pool *pacl_list = &pstapriv->acl_list;
+
+ DBG_88E("%s, mode =%d\n", __func__, mode);
+
+ pacl_list->mode = mode;
+}
+
+int rtw_acl_add_sta(struct adapter *padapter, u8 *addr)
+{
+ struct list_head *plist, *phead;
+ u8 added = false;
+ int i, ret = 0;
+ struct rtw_wlan_acl_node *paclnode;
+ struct sta_priv *pstapriv = &padapter->stapriv;
+ struct wlan_acl_pool *pacl_list = &pstapriv->acl_list;
+ struct __queue *pacl_node_q = &pacl_list->acl_node_q;
+
+ DBG_88E("%s(acl_num =%d) =%pM\n", __func__, pacl_list->num, (addr));
+
+ if ((NUM_ACL-1) < pacl_list->num)
+ return -1;
+
+ spin_lock_bh(&(pacl_node_q->lock));
+
+ phead = get_list_head(pacl_node_q);
+ plist = phead->next;
+
+ while (phead != plist) {
+ paclnode = container_of(plist, struct rtw_wlan_acl_node, list);
+ plist = plist->next;
+
+ if (!memcmp(paclnode->addr, addr, ETH_ALEN)) {
+ if (paclnode->valid) {
+ added = true;
+ DBG_88E("%s, sta has been added\n", __func__);
+ break;
+ }
+ }
+ }
+
+ spin_unlock_bh(&(pacl_node_q->lock));
+
+ if (added)
+ return ret;
+
+ spin_lock_bh(&(pacl_node_q->lock));
+
+ for (i = 0; i < NUM_ACL; i++) {
+ paclnode = &pacl_list->aclnode[i];
+
+ if (!paclnode->valid) {
+ INIT_LIST_HEAD(&paclnode->list);
+
+ ether_addr_copy(paclnode->addr, addr);
+
+ paclnode->valid = true;
+
+ list_add_tail(&paclnode->list, get_list_head(pacl_node_q));
+
+ pacl_list->num++;
+
+ break;
+ }
+ }
+
+ DBG_88E("%s, acl_num =%d\n", __func__, pacl_list->num);
+
+ spin_unlock_bh(&(pacl_node_q->lock));
+
+ return ret;
+}
+
+int rtw_acl_remove_sta(struct adapter *padapter, u8 *addr)
+{
+ struct list_head *plist, *phead;
+ struct rtw_wlan_acl_node *paclnode;
+ struct sta_priv *pstapriv = &padapter->stapriv;
+ struct wlan_acl_pool *pacl_list = &pstapriv->acl_list;
+ struct __queue *pacl_node_q = &pacl_list->acl_node_q;
+
+ DBG_88E("%s(acl_num =%d) =%pM\n", __func__, pacl_list->num, (addr));
+
+ spin_lock_bh(&(pacl_node_q->lock));
+
+ phead = get_list_head(pacl_node_q);
+ plist = phead->next;
+
+ while (phead != plist) {
+ paclnode = container_of(plist, struct rtw_wlan_acl_node, list);
+ plist = plist->next;
+
+ if (!memcmp(paclnode->addr, addr, ETH_ALEN)) {
+ if (paclnode->valid) {
+ paclnode->valid = false;
+
+ list_del_init(&paclnode->list);
+
+ pacl_list->num--;
+ }
+ }
+ }
+
+ spin_unlock_bh(&(pacl_node_q->lock));
+
+ DBG_88E("%s, acl_num =%d\n", __func__, pacl_list->num);
+ return 0;
+}
+
+static void update_bcn_fixed_ie(struct adapter *padapter)
+{
+ DBG_88E("%s\n", __func__);
+}
+
+static void update_bcn_erpinfo_ie(struct adapter *padapter)
+{
+ struct mlme_priv *pmlmepriv = &(padapter->mlmepriv);
+ struct mlme_ext_priv *pmlmeext = &(padapter->mlmeextpriv);
+ struct mlme_ext_info *pmlmeinfo = &(pmlmeext->mlmext_info);
+ struct wlan_bssid_ex *pnetwork = &(pmlmeinfo->network);
+ unsigned char *p, *ie = pnetwork->IEs;
+ u32 len = 0;
+
+ DBG_88E("%s, ERP_enable =%d\n", __func__, pmlmeinfo->ERP_enable);
+
+ if (!pmlmeinfo->ERP_enable)
+ return;
+
+ /* parsing ERP_IE */
+ p = rtw_get_ie(ie + _BEACON_IE_OFFSET_, _ERPINFO_IE_, &len,
+ (pnetwork->IELength - _BEACON_IE_OFFSET_));
+ if (p && len > 0) {
+ struct ndis_802_11_var_ie *pIE = (struct ndis_802_11_var_ie *)p;
+
+ if (pmlmepriv->num_sta_non_erp == 1)
+ pIE->data[0] |= RTW_ERP_INFO_NON_ERP_PRESENT|RTW_ERP_INFO_USE_PROTECTION;
+ else
+ pIE->data[0] &= ~(RTW_ERP_INFO_NON_ERP_PRESENT|RTW_ERP_INFO_USE_PROTECTION);
+
+ if (pmlmepriv->num_sta_no_short_preamble > 0)
+ pIE->data[0] |= RTW_ERP_INFO_BARKER_PREAMBLE_MODE;
+ else
+ pIE->data[0] &= ~(RTW_ERP_INFO_BARKER_PREAMBLE_MODE);
+
+ ERP_IE_handler(padapter, pIE);
+ }
+}
+
+static void update_bcn_htcap_ie(struct adapter *padapter)
+{
+ DBG_88E("%s\n", __func__);
+}
+
+static void update_bcn_htinfo_ie(struct adapter *padapter)
+{
+ DBG_88E("%s\n", __func__);
+}
+
+static void update_bcn_rsn_ie(struct adapter *padapter)
+{
+ DBG_88E("%s\n", __func__);
+}
+
+static void update_bcn_wpa_ie(struct adapter *padapter)
+{
+ DBG_88E("%s\n", __func__);
+}
+
+static void update_bcn_wmm_ie(struct adapter *padapter)
+{
+ DBG_88E("%s\n", __func__);
+}
+
+static void update_bcn_wps_ie(struct adapter *padapter)
+{
+ u8 *pwps_ie = NULL, *pwps_ie_src;
+ u8 *premainder_ie, *pbackup_remainder_ie = NULL;
+ uint wps_ielen = 0, wps_offset, remainder_ielen;
+ struct mlme_priv *pmlmepriv = &(padapter->mlmepriv);
+ struct mlme_ext_priv *pmlmeext = &(padapter->mlmeextpriv);
+ struct mlme_ext_info *pmlmeinfo = &(pmlmeext->mlmext_info);
+ struct wlan_bssid_ex *pnetwork = &(pmlmeinfo->network);
+ unsigned char *ie = pnetwork->IEs;
+ u32 ielen = pnetwork->IELength;
+
+ DBG_88E("%s\n", __func__);
+
+ pwps_ie_src = pmlmepriv->wps_beacon_ie;
+ if (pwps_ie_src == NULL)
+ return;
+
+ pwps_ie = rtw_get_wps_ie(ie+_FIXED_IE_LENGTH_, ielen-_FIXED_IE_LENGTH_, NULL, &wps_ielen);
+
+ if (pwps_ie == NULL || wps_ielen == 0)
+ return;
+
+ wps_offset = (uint)(pwps_ie-ie);
+
+ premainder_ie = pwps_ie + wps_ielen;
+
+ remainder_ielen = ielen - wps_offset - wps_ielen;
+
+ if (remainder_ielen > 0) {
+ pbackup_remainder_ie = rtw_malloc(remainder_ielen);
+ if (pbackup_remainder_ie)
+ memcpy(pbackup_remainder_ie, premainder_ie, remainder_ielen);
+ }
+
+ wps_ielen = (uint)pwps_ie_src[1];/* to get ie data len */
+ if ((wps_offset+wps_ielen+2+remainder_ielen) <= MAX_IE_SZ) {
+ memcpy(pwps_ie, pwps_ie_src, wps_ielen+2);
+ pwps_ie += (wps_ielen+2);
+
+ if (pbackup_remainder_ie)
+ memcpy(pwps_ie, pbackup_remainder_ie, remainder_ielen);
+
+ /* update IELength */
+ pnetwork->IELength = wps_offset + (wps_ielen+2) + remainder_ielen;
+ }
+
+ kfree(pbackup_remainder_ie);
+}
+
+static void update_bcn_p2p_ie(struct adapter *padapter)
+{
+}
+
+static void update_bcn_vendor_spec_ie(struct adapter *padapter, u8 *oui)
+{
+ DBG_88E("%s\n", __func__);
+
+ if (!memcmp(RTW_WPA_OUI, oui, 4))
+ update_bcn_wpa_ie(padapter);
+ else if (!memcmp(WMM_OUI, oui, 4))
+ update_bcn_wmm_ie(padapter);
+ else if (!memcmp(WPS_OUI, oui, 4))
+ update_bcn_wps_ie(padapter);
+ else if (!memcmp(P2P_OUI, oui, 4))
+ update_bcn_p2p_ie(padapter);
+ else
+ DBG_88E("unknown OUI type!\n");
+}
+
+void update_beacon(struct adapter *padapter, u8 ie_id, u8 *oui, u8 tx)
+{
+ struct mlme_priv *pmlmepriv;
+ struct mlme_ext_priv *pmlmeext;
+
+ if (!padapter)
+ return;
+
+ pmlmepriv = &(padapter->mlmepriv);
+ pmlmeext = &(padapter->mlmeextpriv);
+
+ if (!pmlmeext->bstart_bss)
+ return;
+
+ spin_lock_bh(&pmlmepriv->bcn_update_lock);
+
+ switch (ie_id) {
+ case 0xFF:
+ update_bcn_fixed_ie(padapter);/* 8: TimeStamp, 2: Beacon Interval 2:Capability */
+ break;
+ case _TIM_IE_:
+ update_BCNTIM(padapter);
+ break;
+ case _ERPINFO_IE_:
+ update_bcn_erpinfo_ie(padapter);
+ break;
+ case _HT_CAPABILITY_IE_:
+ update_bcn_htcap_ie(padapter);
+ break;
+ case _RSN_IE_2_:
+ update_bcn_rsn_ie(padapter);
+ break;
+ case _HT_ADD_INFO_IE_:
+ update_bcn_htinfo_ie(padapter);
+ break;
+ case _VENDOR_SPECIFIC_IE_:
+ update_bcn_vendor_spec_ie(padapter, oui);
+ break;
+ default:
+ break;
+ }
+
+ pmlmepriv->update_bcn = true;
+
+ spin_unlock_bh(&pmlmepriv->bcn_update_lock);
+
+ if (tx)
+ set_tx_beacon_cmd(padapter);
+}
+
+/*
+op_mode
+Set to 0 (HT pure) under the following conditions
+ - all STAs in the BSS are 20/40 MHz HT in 20/40 MHz BSS or
+ - all STAs in the BSS are 20 MHz HT in 20 MHz BSS
+Set to 1 (HT non-member protection) if there may be non-HT STAs
+ in both the primary and the secondary channel
+Set to 2 if only HT STAs are associated in BSS,
+ however and at least one 20 MHz HT STA is associated
+Set to 3 (HT mixed mode) when one or more non-HT STAs are associated
+ (currently non-GF HT station is considered as non-HT STA also)
+*/
+static int rtw_ht_operation_update(struct adapter *padapter)
+{
+ u16 cur_op_mode, new_op_mode;
+ int op_mode_changes = 0;
+ struct mlme_priv *pmlmepriv = &(padapter->mlmepriv);
+ struct ht_priv *phtpriv_ap = &pmlmepriv->htpriv;
+
+ if (pmlmepriv->htpriv.ht_option)
+ return 0;
+
+ DBG_88E("%s current operation mode = 0x%X\n",
+ __func__, pmlmepriv->ht_op_mode);
+
+ if (!(pmlmepriv->ht_op_mode & HT_INFO_OPERATION_MODE_NON_GF_DEVS_PRESENT) &&
+ pmlmepriv->num_sta_ht_no_gf) {
+ pmlmepriv->ht_op_mode |=
+ HT_INFO_OPERATION_MODE_NON_GF_DEVS_PRESENT;
+ op_mode_changes++;
+ } else if ((pmlmepriv->ht_op_mode &
+ HT_INFO_OPERATION_MODE_NON_GF_DEVS_PRESENT) &&
+ pmlmepriv->num_sta_ht_no_gf == 0) {
+ pmlmepriv->ht_op_mode &=
+ ~HT_INFO_OPERATION_MODE_NON_GF_DEVS_PRESENT;
+ op_mode_changes++;
+ }
+
+ if (!(pmlmepriv->ht_op_mode & HT_INFO_OPERATION_MODE_NON_HT_STA_PRESENT) &&
+ (pmlmepriv->num_sta_no_ht || pmlmepriv->olbc_ht)) {
+ pmlmepriv->ht_op_mode |= HT_INFO_OPERATION_MODE_NON_HT_STA_PRESENT;
+ op_mode_changes++;
+ } else if ((pmlmepriv->ht_op_mode &
+ HT_INFO_OPERATION_MODE_NON_HT_STA_PRESENT) &&
+ (pmlmepriv->num_sta_no_ht == 0 && !pmlmepriv->olbc_ht)) {
+ pmlmepriv->ht_op_mode &=
+ ~HT_INFO_OPERATION_MODE_NON_HT_STA_PRESENT;
+ op_mode_changes++;
+ }
+
+ /* Note: currently we switch to the MIXED op mode if HT non-greenfield
+ * station is associated. Probably it's a theoretical case, since
+ * it looks like all known HT STAs support greenfield.
+ */
+ new_op_mode = 0;
+ if (pmlmepriv->num_sta_no_ht ||
+ (pmlmepriv->ht_op_mode & HT_INFO_OPERATION_MODE_NON_GF_DEVS_PRESENT))
+ new_op_mode = OP_MODE_MIXED;
+ else if ((phtpriv_ap->ht_cap.cap_info & IEEE80211_HT_CAP_SUP_WIDTH) &&
+ pmlmepriv->num_sta_ht_20mhz)
+ new_op_mode = OP_MODE_20MHZ_HT_STA_ASSOCED;
+ else if (pmlmepriv->olbc_ht)
+ new_op_mode = OP_MODE_MAY_BE_LEGACY_STAS;
+ else
+ new_op_mode = OP_MODE_PURE;
+
+ cur_op_mode = pmlmepriv->ht_op_mode & HT_INFO_OPERATION_MODE_OP_MODE_MASK;
+ if (cur_op_mode != new_op_mode) {
+ pmlmepriv->ht_op_mode &= ~HT_INFO_OPERATION_MODE_OP_MODE_MASK;
+ pmlmepriv->ht_op_mode |= new_op_mode;
+ op_mode_changes++;
+ }
+
+ DBG_88E("%s new operation mode = 0x%X changes =%d\n",
+ __func__, pmlmepriv->ht_op_mode, op_mode_changes);
+
+ return op_mode_changes;
+}
+
+void associated_clients_update(struct adapter *padapter, u8 updated)
+{
+ /* update associated stations cap. */
+ if (updated) {
+ struct list_head *phead, *plist;
+ struct sta_info *psta = NULL;
+ struct sta_priv *pstapriv = &padapter->stapriv;
+
+ spin_lock_bh(&pstapriv->asoc_list_lock);
+
+ phead = &pstapriv->asoc_list;
+ plist = phead->next;
+
+ /* check asoc_queue */
+ while (phead != plist) {
+ psta = container_of(plist, struct sta_info, asoc_list);
+
+ plist = plist->next;
+
+ VCS_update(padapter, psta);
+ }
+ spin_unlock_bh(&pstapriv->asoc_list_lock);
+ }
+}
+
+/* called > TSR LEVEL for USB or SDIO Interface*/
+void bss_cap_update_on_sta_join(struct adapter *padapter, struct sta_info *psta)
+{
+ u8 beacon_updated = false;
+ struct mlme_priv *pmlmepriv = &(padapter->mlmepriv);
+ struct mlme_ext_priv *pmlmeext = &(padapter->mlmeextpriv);
+
+ if (!(psta->flags & WLAN_STA_SHORT_PREAMBLE)) {
+ if (!psta->no_short_preamble_set) {
+ psta->no_short_preamble_set = 1;
+
+ pmlmepriv->num_sta_no_short_preamble++;
+
+ if ((pmlmeext->cur_wireless_mode > WIRELESS_11B) &&
+ (pmlmepriv->num_sta_no_short_preamble == 1)) {
+ beacon_updated = true;
+ update_beacon(padapter, 0xFF, NULL, true);
+ }
+ }
+ } else {
+ if (psta->no_short_preamble_set) {
+ psta->no_short_preamble_set = 0;
+
+ pmlmepriv->num_sta_no_short_preamble--;
+
+ if ((pmlmeext->cur_wireless_mode > WIRELESS_11B) &&
+ (pmlmepriv->num_sta_no_short_preamble == 0)) {
+ beacon_updated = true;
+ update_beacon(padapter, 0xFF, NULL, true);
+ }
+ }
+ }
+
+ if (psta->flags & WLAN_STA_NONERP) {
+ if (!psta->nonerp_set) {
+ psta->nonerp_set = 1;
+
+ pmlmepriv->num_sta_non_erp++;
+
+ if (pmlmepriv->num_sta_non_erp == 1) {
+ beacon_updated = true;
+ update_beacon(padapter, _ERPINFO_IE_, NULL, true);
+ }
+ }
+ } else {
+ if (psta->nonerp_set) {
+ psta->nonerp_set = 0;
+
+ pmlmepriv->num_sta_non_erp--;
+
+ if (pmlmepriv->num_sta_non_erp == 0) {
+ beacon_updated = true;
+ update_beacon(padapter, _ERPINFO_IE_, NULL, true);
+ }
+ }
+ }
+
+ if (!(psta->capability & WLAN_CAPABILITY_SHORT_SLOT)) {
+ if (!psta->no_short_slot_time_set) {
+ psta->no_short_slot_time_set = 1;
+
+ pmlmepriv->num_sta_no_short_slot_time++;
+
+ if ((pmlmeext->cur_wireless_mode > WIRELESS_11B) &&
+ (pmlmepriv->num_sta_no_short_slot_time == 1)) {
+ beacon_updated = true;
+ update_beacon(padapter, 0xFF, NULL, true);
+ }
+ }
+ } else {
+ if (psta->no_short_slot_time_set) {
+ psta->no_short_slot_time_set = 0;
+
+ pmlmepriv->num_sta_no_short_slot_time--;
+
+ if ((pmlmeext->cur_wireless_mode > WIRELESS_11B) &&
+ (pmlmepriv->num_sta_no_short_slot_time == 0)) {
+ beacon_updated = true;
+ update_beacon(padapter, 0xFF, NULL, true);
+ }
+ }
+ }
+
+ if (psta->flags & WLAN_STA_HT) {
+ u16 ht_capab = psta->htpriv.ht_cap.cap_info;
+
+ DBG_88E("HT: STA %pM HT Capabilities Info: 0x%04x\n",
+ (psta->hwaddr), ht_capab);
+
+ if (psta->no_ht_set) {
+ psta->no_ht_set = 0;
+ pmlmepriv->num_sta_no_ht--;
+ }
+
+ if ((ht_capab & IEEE80211_HT_CAP_GRN_FLD) == 0) {
+ if (!psta->no_ht_gf_set) {
+ psta->no_ht_gf_set = 1;
+ pmlmepriv->num_sta_ht_no_gf++;
+ }
+ DBG_88E("%s STA %pM - no greenfield, num of non-gf stations %d\n",
+ __func__, (psta->hwaddr),
+ pmlmepriv->num_sta_ht_no_gf);
+ }
+
+ if ((ht_capab & IEEE80211_HT_CAP_SUP_WIDTH) == 0) {
+ if (!psta->ht_20mhz_set) {
+ psta->ht_20mhz_set = 1;
+ pmlmepriv->num_sta_ht_20mhz++;
+ }
+ DBG_88E("%s STA %pM - 20 MHz HT, num of 20MHz HT STAs %d\n",
+ __func__, (psta->hwaddr),
+ pmlmepriv->num_sta_ht_20mhz);
+ }
+ } else {
+ if (!psta->no_ht_set) {
+ psta->no_ht_set = 1;
+ pmlmepriv->num_sta_no_ht++;
+ }
+ if (pmlmepriv->htpriv.ht_option) {
+ DBG_88E("%s STA %pM - no HT, num of non-HT stations %d\n",
+ __func__, (psta->hwaddr),
+ pmlmepriv->num_sta_no_ht);
+ }
+ }
+
+ if (rtw_ht_operation_update(padapter) > 0) {
+ update_beacon(padapter, _HT_CAPABILITY_IE_, NULL, false);
+ update_beacon(padapter, _HT_ADD_INFO_IE_, NULL, true);
+ }
+
+ /* update associated stations cap. */
+ associated_clients_update(padapter, beacon_updated);
+
+ DBG_88E("%s, updated =%d\n", __func__, beacon_updated);
+}
+
+u8 bss_cap_update_on_sta_leave(struct adapter *padapter, struct sta_info *psta)
+{
+ u8 beacon_updated = false;
+ struct mlme_priv *pmlmepriv = &(padapter->mlmepriv);
+ struct mlme_ext_priv *pmlmeext = &(padapter->mlmeextpriv);
+
+ if (!psta)
+ return beacon_updated;
+
+ if (psta->no_short_preamble_set) {
+ psta->no_short_preamble_set = 0;
+ pmlmepriv->num_sta_no_short_preamble--;
+ if (pmlmeext->cur_wireless_mode > WIRELESS_11B &&
+ pmlmepriv->num_sta_no_short_preamble == 0) {
+ beacon_updated = true;
+ update_beacon(padapter, 0xFF, NULL, true);
+ }
+ }
+
+ if (psta->nonerp_set) {
+ psta->nonerp_set = 0;
+ pmlmepriv->num_sta_non_erp--;
+ if (pmlmepriv->num_sta_non_erp == 0) {
+ beacon_updated = true;
+ update_beacon(padapter, _ERPINFO_IE_, NULL, true);
+ }
+ }
+
+ if (psta->no_short_slot_time_set) {
+ psta->no_short_slot_time_set = 0;
+ pmlmepriv->num_sta_no_short_slot_time--;
+ if (pmlmeext->cur_wireless_mode > WIRELESS_11B &&
+ pmlmepriv->num_sta_no_short_slot_time == 0) {
+ beacon_updated = true;
+ update_beacon(padapter, 0xFF, NULL, true);
+ }
+ }
+
+ if (psta->no_ht_gf_set) {
+ psta->no_ht_gf_set = 0;
+ pmlmepriv->num_sta_ht_no_gf--;
+ }
+
+ if (psta->no_ht_set) {
+ psta->no_ht_set = 0;
+ pmlmepriv->num_sta_no_ht--;
+ }
+
+ if (psta->ht_20mhz_set) {
+ psta->ht_20mhz_set = 0;
+ pmlmepriv->num_sta_ht_20mhz--;
+ }
+
+ if (rtw_ht_operation_update(padapter) > 0) {
+ update_beacon(padapter, _HT_CAPABILITY_IE_, NULL, false);
+ update_beacon(padapter, _HT_ADD_INFO_IE_, NULL, true);
+ }
+
+ /* update associated stations cap. */
+
+ DBG_88E("%s, updated =%d\n", __func__, beacon_updated);
+
+ return beacon_updated;
+}
+
+u8 ap_free_sta(struct adapter *padapter, struct sta_info *psta,
+ bool active, u16 reason)
+{
+ u8 beacon_updated = false;
+ struct sta_priv *pstapriv = &padapter->stapriv;
+
+ if (!psta)
+ return beacon_updated;
+
+ /* tear down Rx AMPDU */
+ send_delba(padapter, 0, psta->hwaddr);/* recipient */
+
+ /* tear down TX AMPDU */
+ send_delba(padapter, 1, psta->hwaddr);/* originator */
+ psta->htpriv.agg_enable_bitmap = 0x0;/* reset */
+ psta->htpriv.candidate_tid_bitmap = 0x0;/* reset */
+
+ if (active)
+ issue_deauth(padapter, psta->hwaddr, reason);
+
+ /* clear cam entry / key */
+ rtw_clearstakey_cmd(padapter, (u8 *)psta, (u8)(psta->mac_id + 3), true);
+
+
+ spin_lock_bh(&psta->lock);
+ psta->state &= ~_FW_LINKED;
+ spin_unlock_bh(&psta->lock);
+
+ rtw_indicate_sta_disassoc_event(padapter, psta);
+
+ report_del_sta_event(padapter, psta->hwaddr, reason);
+
+ beacon_updated = bss_cap_update_on_sta_leave(padapter, psta);
+
+ spin_lock_bh(&(pstapriv->sta_hash_lock));
+ rtw_free_stainfo(padapter, psta);
+ spin_unlock_bh(&(pstapriv->sta_hash_lock));
+
+ return beacon_updated;
+}
+
+int rtw_ap_inform_ch_switch(struct adapter *padapter, u8 new_ch, u8 ch_offset)
+{
+ struct list_head *phead, *plist;
+ struct sta_info *psta = NULL;
+ struct sta_priv *pstapriv = &padapter->stapriv;
+ struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv;
+ struct mlme_ext_info *pmlmeinfo = &(pmlmeext->mlmext_info);
+ u8 bc_addr[ETH_ALEN] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
+
+ if ((pmlmeinfo->state&0x03) != WIFI_FW_AP_STATE)
+ return 0;
+
+ DBG_88E(FUNC_NDEV_FMT" with ch:%u, offset:%u\n",
+ FUNC_NDEV_ARG(padapter->pnetdev), new_ch, ch_offset);
+
+ spin_lock_bh(&pstapriv->asoc_list_lock);
+ phead = &pstapriv->asoc_list;
+ plist = phead->next;
+
+ /* for each sta in asoc_queue */
+ while (phead != plist) {
+ psta = container_of(plist, struct sta_info, asoc_list);
+ plist = plist->next;
+
+ issue_action_spct_ch_switch(padapter, psta->hwaddr, new_ch, ch_offset);
+ psta->expire_to = ((pstapriv->expire_to * 2) > 5) ? 5 : (pstapriv->expire_to * 2);
+ }
+ spin_unlock_bh(&pstapriv->asoc_list_lock);
+
+ issue_action_spct_ch_switch(padapter, bc_addr, new_ch, ch_offset);
+
+ return 0;
+}
+
+int rtw_sta_flush(struct adapter *padapter)
+{
+ struct list_head *phead, *plist;
+ struct sta_info *psta = NULL;
+ struct sta_priv *pstapriv = &padapter->stapriv;
+ struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv;
+ struct mlme_ext_info *pmlmeinfo = &(pmlmeext->mlmext_info);
+ u8 bc_addr[ETH_ALEN] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
+
+ DBG_88E(FUNC_NDEV_FMT"\n", FUNC_NDEV_ARG(padapter->pnetdev));
+
+ if ((pmlmeinfo->state&0x03) != WIFI_FW_AP_STATE)
+ return 0;
+
+ spin_lock_bh(&pstapriv->asoc_list_lock);
+ phead = &pstapriv->asoc_list;
+ plist = phead->next;
+
+ /* free sta asoc_queue */
+ while (phead != plist) {
+ psta = container_of(plist, struct sta_info, asoc_list);
+
+ plist = plist->next;
+
+ list_del_init(&psta->asoc_list);
+ pstapriv->asoc_list_cnt--;
+
+ ap_free_sta(padapter, psta, true, WLAN_REASON_DEAUTH_LEAVING);
+ }
+ spin_unlock_bh(&pstapriv->asoc_list_lock);
+
+
+ issue_deauth(padapter, bc_addr, WLAN_REASON_DEAUTH_LEAVING);
+
+ associated_clients_update(padapter, true);
+
+ return 0;
+}
+
+/* called > TSR LEVEL for USB or SDIO Interface*/
+void sta_info_update(struct adapter *padapter, struct sta_info *psta)
+{
+ int flags = psta->flags;
+ struct mlme_priv *pmlmepriv = &(padapter->mlmepriv);
+
+ /* update wmm cap. */
+ if (WLAN_STA_WME&flags)
+ psta->qos_option = 1;
+ else
+ psta->qos_option = 0;
+
+ if (pmlmepriv->qospriv.qos_option == 0)
+ psta->qos_option = 0;
+
+ /* update 802.11n ht cap. */
+ if (WLAN_STA_HT&flags) {
+ psta->htpriv.ht_option = true;
+ psta->qos_option = 1;
+ } else {
+ psta->htpriv.ht_option = false;
+ }
+
+ if (!pmlmepriv->htpriv.ht_option)
+ psta->htpriv.ht_option = false;
+
+ update_sta_info_apmode(padapter, psta);
+}
+
+/* called >= TSR LEVEL for USB or SDIO Interface*/
+void ap_sta_info_defer_update(struct adapter *padapter, struct sta_info *psta)
+{
+ if (psta->state & _FW_LINKED) {
+ /* add ratid */
+ add_RATid(padapter, psta, 0);/* DM_RATR_STA_INIT */
+ }
+}
+
+void start_ap_mode(struct adapter *padapter)
+{
+ int i;
+ struct mlme_priv *pmlmepriv = &(padapter->mlmepriv);
+ struct sta_priv *pstapriv = &padapter->stapriv;
+ struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv;
+ struct wlan_acl_pool *pacl_list = &pstapriv->acl_list;
+
+ pmlmepriv->update_bcn = false;
+
+ pmlmeext->bstart_bss = false;
+
+ pmlmepriv->num_sta_non_erp = 0;
+
+ pmlmepriv->num_sta_no_short_slot_time = 0;
+
+ pmlmepriv->num_sta_no_short_preamble = 0;
+
+ pmlmepriv->num_sta_ht_no_gf = 0;
+ pmlmepriv->num_sta_no_ht = 0;
+ pmlmepriv->num_sta_ht_20mhz = 0;
+
+ pmlmepriv->olbc = false;
+
+ pmlmepriv->olbc_ht = false;
+
+ pmlmepriv->ht_op_mode = 0;
+
+ for (i = 0; i < NUM_STA; i++)
+ pstapriv->sta_aid[i] = NULL;
+
+ pmlmepriv->wps_beacon_ie = NULL;
+ pmlmepriv->wps_probe_resp_ie = NULL;
+ pmlmepriv->wps_assoc_resp_ie = NULL;
+
+ pmlmepriv->p2p_beacon_ie = NULL;
+ pmlmepriv->p2p_probe_resp_ie = NULL;
+
+ /* for ACL */
+ INIT_LIST_HEAD(&(pacl_list->acl_node_q.queue));
+ pacl_list->num = 0;
+ pacl_list->mode = 0;
+ for (i = 0; i < NUM_ACL; i++) {
+ INIT_LIST_HEAD(&pacl_list->aclnode[i].list);
+ pacl_list->aclnode[i].valid = false;
+ }
+}
+
+void stop_ap_mode(struct adapter *padapter)
+{
+ struct list_head *phead, *plist;
+ struct rtw_wlan_acl_node *paclnode;
+ struct sta_info *psta = NULL;
+ struct sta_priv *pstapriv = &padapter->stapriv;
+ struct mlme_priv *pmlmepriv = &(padapter->mlmepriv);
+ struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv;
+ struct wlan_acl_pool *pacl_list = &pstapriv->acl_list;
+ struct __queue *pacl_node_q = &pacl_list->acl_node_q;
+
+ pmlmepriv->update_bcn = false;
+ pmlmeext->bstart_bss = false;
+
+ /* reset and init security priv , this can refine with rtw_reset_securitypriv */
+ memset((unsigned char *)&padapter->securitypriv, 0, sizeof(struct security_priv));
+ padapter->securitypriv.ndisauthtype = Ndis802_11AuthModeOpen;
+ padapter->securitypriv.ndisencryptstatus = Ndis802_11WEPDisabled;
+
+ /* for ACL */
+ spin_lock_bh(&(pacl_node_q->lock));
+ phead = get_list_head(pacl_node_q);
+ plist = phead->next;
+ while (phead != plist) {
+ paclnode = container_of(plist, struct rtw_wlan_acl_node, list);
+ plist = plist->next;
+
+ if (paclnode->valid) {
+ paclnode->valid = false;
+
+ list_del_init(&paclnode->list);
+
+ pacl_list->num--;
+ }
+ }
+ spin_unlock_bh(&(pacl_node_q->lock));
+
+ DBG_88E("%s, free acl_node_queue, num =%d\n", __func__, pacl_list->num);
+
+ rtw_sta_flush(padapter);
+
+ /* free_assoc_sta_resources */
+ rtw_free_all_stainfo(padapter);
+
+ psta = rtw_get_bcmc_stainfo(padapter);
+ spin_lock_bh(&(pstapriv->sta_hash_lock));
+ rtw_free_stainfo(padapter, psta);
+ spin_unlock_bh(&(pstapriv->sta_hash_lock));
+
+ rtw_init_bcmc_stainfo(padapter);
+
+ rtw_free_mlme_priv_ie_data(pmlmepriv);
+}
+
+#endif /* CONFIG_88EU_AP_MODE */
diff --git a/drivers/staging/rtl8188eu/core/rtw_cmd.c b/drivers/staging/rtl8188eu/core/rtw_cmd.c
new file mode 100644
index 000000000..89b5e48ed
--- /dev/null
+++ b/drivers/staging/rtl8188eu/core/rtw_cmd.c
@@ -0,0 +1,1395 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2012 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ *
+ ******************************************************************************/
+#define _RTW_CMD_C_
+
+#include <osdep_service.h>
+#include <drv_types.h>
+#include <recv_osdep.h>
+#include <mlme_osdep.h>
+#include <rtw_mlme_ext.h>
+
+/*
+Caller and the rtw_cmd_thread can protect cmd_q by spin_lock.
+No irqsave is necessary.
+*/
+
+int rtw_init_cmd_priv(struct cmd_priv *pcmdpriv)
+{
+ sema_init(&(pcmdpriv->cmd_queue_sema), 0);
+ sema_init(&(pcmdpriv->terminate_cmdthread_sema), 0);
+
+ _rtw_init_queue(&(pcmdpriv->cmd_queue));
+ return _SUCCESS;
+}
+
+/*
+Calling Context:
+
+rtw_enqueue_cmd can only be called between kernel thread,
+since only spin_lock is used.
+
+ISR/Call-Back functions can't call this sub-function.
+
+*/
+
+static int _rtw_enqueue_cmd(struct __queue *queue, struct cmd_obj *obj)
+{
+ unsigned long irqL;
+
+
+ if (obj == NULL)
+ goto exit;
+
+ spin_lock_irqsave(&queue->lock, irqL);
+
+ list_add_tail(&obj->list, &queue->queue);
+
+ spin_unlock_irqrestore(&queue->lock, irqL);
+
+exit:
+
+
+ return _SUCCESS;
+}
+
+struct cmd_obj *rtw_dequeue_cmd(struct __queue *queue)
+{
+ unsigned long irqL;
+ struct cmd_obj *obj;
+
+
+ spin_lock_irqsave(&queue->lock, irqL);
+ if (list_empty(&(queue->queue))) {
+ obj = NULL;
+ } else {
+ obj = container_of((&queue->queue)->next, struct cmd_obj, list);
+ list_del_init(&obj->list);
+ }
+
+ spin_unlock_irqrestore(&queue->lock, irqL);
+
+
+ return obj;
+}
+
+static int rtw_cmd_filter(struct cmd_priv *pcmdpriv, struct cmd_obj *cmd_obj)
+{
+ u8 bAllow = false; /* set to true to allow enqueuing cmd when hw_init_completed is false */
+
+ /* To decide allow or not */
+ if ((pcmdpriv->padapter->pwrctrlpriv.bHWPwrPindetect) &&
+ (!pcmdpriv->padapter->registrypriv.usbss_enable)) {
+ if (cmd_obj->cmdcode == GEN_CMD_CODE(_Set_Drv_Extra)) {
+ struct drvextra_cmd_parm *pdrvextra_cmd_parm = (struct drvextra_cmd_parm *)cmd_obj->parmbuf;
+
+ if (pdrvextra_cmd_parm->ec_id == POWER_SAVING_CTRL_WK_CID)
+ bAllow = true;
+ }
+ }
+
+ if (cmd_obj->cmdcode == GEN_CMD_CODE(_SetChannelPlan))
+ bAllow = true;
+
+ if ((!pcmdpriv->padapter->hw_init_completed && !bAllow) ||
+ !pcmdpriv->cmdthd_running) /* com_thread not running */
+ return _FAIL;
+ return _SUCCESS;
+}
+
+u32 rtw_enqueue_cmd(struct cmd_priv *pcmdpriv, struct cmd_obj *cmd_obj)
+{
+ int res = _FAIL;
+ struct adapter *padapter = pcmdpriv->padapter;
+
+
+ if (cmd_obj == NULL)
+ goto exit;
+
+ cmd_obj->padapter = padapter;
+
+ res = rtw_cmd_filter(pcmdpriv, cmd_obj);
+ if (_FAIL == res) {
+ rtw_free_cmd_obj(cmd_obj);
+ goto exit;
+ }
+
+ res = _rtw_enqueue_cmd(&pcmdpriv->cmd_queue, cmd_obj);
+
+ if (res == _SUCCESS)
+ up(&pcmdpriv->cmd_queue_sema);
+
+exit:
+
+
+ return res;
+}
+
+void rtw_free_cmd_obj(struct cmd_obj *pcmd)
+{
+
+ if ((pcmd->cmdcode != _JoinBss_CMD_) && (pcmd->cmdcode != _CreateBss_CMD_)) {
+ /* free parmbuf in cmd_obj */
+ kfree(pcmd->parmbuf);
+ }
+
+ if (pcmd->rsp != NULL) {
+ if (pcmd->rspsz != 0) {
+ /* free rsp in cmd_obj */
+ kfree(pcmd->rsp);
+ }
+ }
+
+ /* free cmd_obj */
+ kfree(pcmd);
+
+}
+
+int rtw_cmd_thread(void *context)
+{
+ u8 ret;
+ struct cmd_obj *pcmd;
+ u8 (*cmd_hdl)(struct adapter *padapter, u8 *pbuf);
+ void (*pcmd_callback)(struct adapter *dev, struct cmd_obj *pcmd);
+ struct adapter *padapter = context;
+ struct cmd_priv *pcmdpriv = &(padapter->cmdpriv);
+
+ allow_signal(SIGTERM);
+
+ pcmdpriv->cmdthd_running = true;
+ up(&pcmdpriv->terminate_cmdthread_sema);
+
+ RT_TRACE(_module_rtl871x_cmd_c_, _drv_info_, ("start r871x rtw_cmd_thread !!!!\n"));
+
+ while (1) {
+ if (_rtw_down_sema(&pcmdpriv->cmd_queue_sema) == _FAIL)
+ break;
+
+ if (padapter->bDriverStopped ||
+ padapter->bSurpriseRemoved) {
+ DBG_88E("%s: DriverStopped(%d) SurpriseRemoved(%d) break at line %d\n",
+ __func__, padapter->bDriverStopped, padapter->bSurpriseRemoved, __LINE__);
+ break;
+ }
+_next:
+ if (padapter->bDriverStopped ||
+ padapter->bSurpriseRemoved) {
+ DBG_88E("%s: DriverStopped(%d) SurpriseRemoved(%d) break at line %d\n",
+ __func__, padapter->bDriverStopped, padapter->bSurpriseRemoved, __LINE__);
+ break;
+ }
+
+ pcmd = rtw_dequeue_cmd(&pcmdpriv->cmd_queue);
+ if (!pcmd)
+ continue;
+
+ if (_FAIL == rtw_cmd_filter(pcmdpriv, pcmd)) {
+ pcmd->res = H2C_DROPPED;
+ goto post_process;
+ }
+
+ if (pcmd->cmdcode < ARRAY_SIZE(wlancmds)) {
+ cmd_hdl = wlancmds[pcmd->cmdcode].h2cfuns;
+
+ if (cmd_hdl) {
+ ret = cmd_hdl(pcmd->padapter, pcmd->parmbuf);
+ pcmd->res = ret;
+ }
+ } else {
+ pcmd->res = H2C_PARAMETERS_ERROR;
+ }
+
+ cmd_hdl = NULL;
+
+post_process:
+
+ /* call callback function for post-processed */
+ if (pcmd->cmdcode < ARRAY_SIZE(rtw_cmd_callback)) {
+ pcmd_callback = rtw_cmd_callback[pcmd->cmdcode].callback;
+ if (pcmd_callback == NULL) {
+ RT_TRACE(_module_rtl871x_cmd_c_, _drv_info_, ("mlme_cmd_hdl(): pcmd_callback = 0x%p, cmdcode = 0x%x\n", pcmd_callback, pcmd->cmdcode));
+ rtw_free_cmd_obj(pcmd);
+ } else {
+ /* todo: !!! fill rsp_buf to pcmd->rsp if (pcmd->rsp!= NULL) */
+ pcmd_callback(pcmd->padapter, pcmd);/* need conider that free cmd_obj in rtw_cmd_callback */
+ }
+ } else {
+ RT_TRACE(_module_rtl871x_cmd_c_, _drv_err_, ("%s: cmdcode = 0x%x callback not defined!\n", __func__, pcmd->cmdcode));
+ rtw_free_cmd_obj(pcmd);
+ }
+
+ if (signal_pending(current))
+ flush_signals(current);
+
+ goto _next;
+ }
+ pcmdpriv->cmdthd_running = false;
+
+ /* free all cmd_obj resources */
+ do {
+ pcmd = rtw_dequeue_cmd(&pcmdpriv->cmd_queue);
+ if (pcmd == NULL)
+ break;
+
+ /* DBG_88E("%s: leaving... drop cmdcode:%u\n", __func__, pcmd->cmdcode); */
+
+ rtw_free_cmd_obj(pcmd);
+ } while (1);
+
+ up(&pcmdpriv->terminate_cmdthread_sema);
+
+
+ complete_and_exit(NULL, 0);
+}
+
+/*
+rtw_sitesurvey_cmd(~)
+ ### NOTE:#### (!!!!)
+ MUST TAKE CARE THAT BEFORE CALLING THIS FUNC, YOU SHOULD HAVE LOCKED pmlmepriv->lock
+*/
+u8 rtw_sitesurvey_cmd(struct adapter *padapter, struct ndis_802_11_ssid *ssid, int ssid_num,
+ struct rtw_ieee80211_channel *ch, int ch_num)
+{
+ u8 res = _FAIL;
+ struct cmd_obj *ph2c;
+ struct sitesurvey_parm *psurveyPara;
+ struct cmd_priv *pcmdpriv = &padapter->cmdpriv;
+ struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
+
+ if (check_fwstate(pmlmepriv, _FW_LINKED) == true)
+ rtw_lps_ctrl_wk_cmd(padapter, LPS_CTRL_SCAN, 1);
+
+ ph2c = kzalloc(sizeof(struct cmd_obj), GFP_ATOMIC);
+ if (ph2c == NULL)
+ return _FAIL;
+
+ psurveyPara = kzalloc(sizeof(struct sitesurvey_parm), GFP_ATOMIC);
+ if (psurveyPara == NULL) {
+ kfree(ph2c);
+ return _FAIL;
+ }
+
+ rtw_free_network_queue(padapter, false);
+
+ RT_TRACE(_module_rtl871x_cmd_c_, _drv_info_, ("%s: flush network queue\n", __func__));
+
+ init_h2fwcmd_w_parm_no_rsp(ph2c, psurveyPara, GEN_CMD_CODE(_SiteSurvey));
+
+ /* psurveyPara->bsslimit = 48; */
+ psurveyPara->scan_mode = pmlmepriv->scan_mode;
+
+ /* prepare ssid list */
+ if (ssid) {
+ int i;
+
+ for (i = 0; i < ssid_num && i < RTW_SSID_SCAN_AMOUNT; i++) {
+ if (ssid[i].SsidLength) {
+ memcpy(&psurveyPara->ssid[i], &ssid[i], sizeof(struct ndis_802_11_ssid));
+ psurveyPara->ssid_num++;
+ }
+ }
+ }
+
+ /* prepare channel list */
+ if (ch) {
+ int i;
+
+ for (i = 0; i < ch_num && i < RTW_CHANNEL_SCAN_AMOUNT; i++) {
+ if (ch[i].hw_value && !(ch[i].flags & RTW_IEEE80211_CHAN_DISABLED)) {
+ memcpy(&psurveyPara->ch[i], &ch[i], sizeof(struct rtw_ieee80211_channel));
+ psurveyPara->ch_num++;
+ }
+ }
+ }
+
+ set_fwstate(pmlmepriv, _FW_UNDER_SURVEY);
+
+ res = rtw_enqueue_cmd(pcmdpriv, ph2c);
+
+ if (res == _SUCCESS) {
+ pmlmepriv->scan_start_time = jiffies;
+
+ mod_timer(&pmlmepriv->scan_to_timer,
+ jiffies + msecs_to_jiffies(SCANNING_TIMEOUT));
+
+ rtw_led_control(padapter, LED_CTL_SITE_SURVEY);
+
+ pmlmepriv->scan_interval = SCAN_INTERVAL;/* 30*2 sec = 60sec */
+ } else {
+ _clr_fwstate_(pmlmepriv, _FW_UNDER_SURVEY);
+ }
+
+
+ return res;
+}
+
+void rtw_readtssi_cmdrsp_callback(struct adapter *padapter, struct cmd_obj *pcmd)
+{
+
+ kfree(pcmd->parmbuf);
+ kfree(pcmd);
+}
+
+u8 rtw_createbss_cmd(struct adapter *padapter)
+{
+ struct cmd_obj *pcmd;
+ struct cmd_priv *pcmdpriv = &padapter->cmdpriv;
+ struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
+ struct wlan_bssid_ex *pdev_network = &padapter->registrypriv.dev_network;
+ u8 res = _SUCCESS;
+
+
+ rtw_led_control(padapter, LED_CTL_START_TO_LINK);
+
+ if (pmlmepriv->assoc_ssid.SsidLength == 0)
+ RT_TRACE(_module_rtl871x_cmd_c_, _drv_info_, (" createbss for Any SSid:%s\n", pmlmepriv->assoc_ssid.Ssid));
+ else
+ RT_TRACE(_module_rtl871x_cmd_c_, _drv_info_, (" createbss for SSid:%s\n", pmlmepriv->assoc_ssid.Ssid));
+
+ pcmd = kzalloc(sizeof(struct cmd_obj), GFP_KERNEL);
+ if (pcmd == NULL) {
+ res = _FAIL;
+ goto exit;
+ }
+
+ INIT_LIST_HEAD(&pcmd->list);
+ pcmd->cmdcode = _CreateBss_CMD_;
+ pcmd->parmbuf = (unsigned char *)pdev_network;
+ pcmd->cmdsz = get_wlan_bssid_ex_sz((struct wlan_bssid_ex *)pdev_network);
+ pcmd->rsp = NULL;
+ pcmd->rspsz = 0;
+ pdev_network->Length = pcmd->cmdsz;
+ res = rtw_enqueue_cmd(pcmdpriv, pcmd);
+exit:
+
+
+ return res;
+}
+
+u8 rtw_joinbss_cmd(struct adapter *padapter, struct wlan_network *pnetwork)
+{
+ u8 res = _SUCCESS;
+ uint t_len = 0;
+ struct wlan_bssid_ex *psecnetwork;
+ struct cmd_obj *pcmd;
+ struct cmd_priv *pcmdpriv = &padapter->cmdpriv;
+ struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
+ struct qos_priv *pqospriv = &pmlmepriv->qospriv;
+ struct security_priv *psecuritypriv = &padapter->securitypriv;
+ struct registry_priv *pregistrypriv = &padapter->registrypriv;
+ struct ht_priv *phtpriv = &pmlmepriv->htpriv;
+ enum ndis_802_11_network_infra ndis_network_mode = pnetwork->network.InfrastructureMode;
+ struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv;
+ struct mlme_ext_info *pmlmeinfo = &(pmlmeext->mlmext_info);
+
+
+ rtw_led_control(padapter, LED_CTL_START_TO_LINK);
+
+ if (pmlmepriv->assoc_ssid.SsidLength == 0)
+ RT_TRACE(_module_rtl871x_cmd_c_, _drv_info_, ("+Join cmd: Any SSid\n"));
+ else
+ RT_TRACE(_module_rtl871x_cmd_c_, _drv_notice_, ("+Join cmd: SSid =[%s]\n", pmlmepriv->assoc_ssid.Ssid));
+
+ pcmd = kzalloc(sizeof(struct cmd_obj), GFP_ATOMIC);
+ if (pcmd == NULL) {
+ res = _FAIL;
+ RT_TRACE(_module_rtl871x_cmd_c_, _drv_err_, ("rtw_joinbss_cmd: memory allocate for cmd_obj fail!!!\n"));
+ goto exit;
+ }
+ /* for IEs is fix buf size */
+ t_len = sizeof(struct wlan_bssid_ex);
+
+
+ /* for hidden ap to set fw_state here */
+ if (!check_fwstate(pmlmepriv, WIFI_STATION_STATE|WIFI_ADHOC_STATE)) {
+ switch (ndis_network_mode) {
+ case Ndis802_11IBSS:
+ set_fwstate(pmlmepriv, WIFI_ADHOC_STATE);
+ break;
+ case Ndis802_11Infrastructure:
+ set_fwstate(pmlmepriv, WIFI_STATION_STATE);
+ break;
+ case Ndis802_11APMode:
+ case Ndis802_11AutoUnknown:
+ case Ndis802_11InfrastructureMax:
+ break;
+ }
+ }
+
+ psecnetwork = (struct wlan_bssid_ex *)&psecuritypriv->sec_bss;
+ if (psecnetwork == NULL) {
+ kfree(pcmd);
+
+ res = _FAIL;
+
+ RT_TRACE(_module_rtl871x_cmd_c_, _drv_err_, ("rtw_joinbss_cmd :psecnetwork == NULL!!!\n"));
+
+ goto exit;
+ }
+
+ memset(psecnetwork, 0, t_len);
+
+ memcpy(psecnetwork, &pnetwork->network, get_wlan_bssid_ex_sz(&pnetwork->network));
+
+ psecuritypriv->authenticator_ie[0] = (unsigned char)psecnetwork->IELength;
+
+ if ((psecnetwork->IELength-12) < (256-1))
+ memcpy(&psecuritypriv->authenticator_ie[1], &psecnetwork->IEs[12], psecnetwork->IELength-12);
+ else
+ memcpy(&psecuritypriv->authenticator_ie[1], &psecnetwork->IEs[12], (256-1));
+
+ psecnetwork->IELength = 0;
+ /* Added by Albert 2009/02/18 */
+ /* If the driver wants to use the bssid to create the connection. */
+ /* If not, we have to copy the connecting AP's MAC address to it so that */
+ /* the driver just has the bssid information for PMKIDList searching. */
+
+ if (!pmlmepriv->assoc_by_bssid)
+ memcpy(&pmlmepriv->assoc_bssid[0], &pnetwork->network.MacAddress[0], ETH_ALEN);
+
+ psecnetwork->IELength = rtw_restruct_sec_ie(padapter, &pnetwork->network.IEs[0], &psecnetwork->IEs[0], pnetwork->network.IELength);
+
+
+ pqospriv->qos_option = 0;
+
+ if (pregistrypriv->wmm_enable) {
+ u32 tmp_len;
+
+ tmp_len = rtw_restruct_wmm_ie(padapter, &pnetwork->network.IEs[0], &psecnetwork->IEs[0], pnetwork->network.IELength, psecnetwork->IELength);
+
+ if (psecnetwork->IELength != tmp_len) {
+ psecnetwork->IELength = tmp_len;
+ pqospriv->qos_option = 1; /* There is WMM IE in this corresp. beacon */
+ } else {
+ pqospriv->qos_option = 0;/* There is no WMM IE in this corresp. beacon */
+ }
+ }
+
+ phtpriv->ht_option = false;
+ if (pregistrypriv->ht_enable) {
+ /*
+ * Added by Albert 2010/06/23
+ * For the WEP mode, we will use the bg mode to do
+ * the connection to avoid some IOT issue.
+ * Especially for Realtek 8192u SoftAP.
+ */
+ if ((padapter->securitypriv.dot11PrivacyAlgrthm != _WEP40_) &&
+ (padapter->securitypriv.dot11PrivacyAlgrthm != _WEP104_) &&
+ (padapter->securitypriv.dot11PrivacyAlgrthm != _TKIP_)) {
+ /* rtw_restructure_ht_ie */
+ rtw_restructure_ht_ie(padapter, &pnetwork->network.IEs[0], &psecnetwork->IEs[0],
+ pnetwork->network.IELength, &psecnetwork->IELength);
+ }
+ }
+
+ pmlmeinfo->assoc_AP_vendor = check_assoc_AP(pnetwork->network.IEs, pnetwork->network.IELength);
+
+ if (pmlmeinfo->assoc_AP_vendor == HT_IOT_PEER_TENDA)
+ padapter->pwrctrlpriv.smart_ps = 0;
+ else
+ padapter->pwrctrlpriv.smart_ps = padapter->registrypriv.smart_ps;
+
+ DBG_88E("%s: smart_ps =%d\n", __func__, padapter->pwrctrlpriv.smart_ps);
+
+ pcmd->cmdsz = get_wlan_bssid_ex_sz(psecnetwork);/* get cmdsz before endian conversion */
+
+ INIT_LIST_HEAD(&pcmd->list);
+ pcmd->cmdcode = _JoinBss_CMD_;/* GEN_CMD_CODE(_JoinBss) */
+ pcmd->parmbuf = (unsigned char *)psecnetwork;
+ pcmd->rsp = NULL;
+ pcmd->rspsz = 0;
+
+ res = rtw_enqueue_cmd(pcmdpriv, pcmd);
+
+exit:
+
+
+ return res;
+}
+
+u8 rtw_disassoc_cmd(struct adapter *padapter, u32 deauth_timeout_ms, bool enqueue) /* for sta_mode */
+{
+ struct cmd_obj *cmdobj = NULL;
+ struct disconnect_parm *param = NULL;
+ struct cmd_priv *cmdpriv = &padapter->cmdpriv;
+ u8 res = _SUCCESS;
+
+
+ RT_TRACE(_module_rtl871x_cmd_c_, _drv_notice_, ("+rtw_disassoc_cmd\n"));
+
+ /* prepare cmd parameter */
+ param = kzalloc(sizeof(*param), GFP_KERNEL);
+ if (param == NULL) {
+ res = _FAIL;
+ goto exit;
+ }
+ param->deauth_timeout_ms = deauth_timeout_ms;
+
+ if (enqueue) {
+ /* need enqueue, prepare cmd_obj and enqueue */
+ cmdobj = kzalloc(sizeof(*cmdobj), GFP_KERNEL);
+ if (cmdobj == NULL) {
+ res = _FAIL;
+ kfree(param);
+ goto exit;
+ }
+ init_h2fwcmd_w_parm_no_rsp(cmdobj, param, _DisConnect_CMD_);
+ res = rtw_enqueue_cmd(cmdpriv, cmdobj);
+ } else {
+ /* no need to enqueue, do the cmd hdl directly and free cmd parameter */
+ if (H2C_SUCCESS != disconnect_hdl(padapter, (u8 *)param))
+ res = _FAIL;
+ kfree(param);
+ }
+
+exit:
+
+
+ return res;
+}
+
+u8 rtw_setopmode_cmd(struct adapter *padapter, enum ndis_802_11_network_infra networktype)
+{
+ struct cmd_obj *ph2c;
+ struct setopmode_parm *psetop;
+
+ struct cmd_priv *pcmdpriv = &padapter->cmdpriv;
+ u8 res = _SUCCESS;
+
+
+ ph2c = kzalloc(sizeof(struct cmd_obj), GFP_KERNEL);
+ if (ph2c == NULL) {
+ res = false;
+ goto exit;
+ }
+ psetop = kzalloc(sizeof(struct setopmode_parm), GFP_KERNEL);
+
+ if (psetop == NULL) {
+ kfree(ph2c);
+ res = false;
+ goto exit;
+ }
+
+ init_h2fwcmd_w_parm_no_rsp(ph2c, psetop, _SetOpMode_CMD_);
+ psetop->mode = (u8)networktype;
+
+ res = rtw_enqueue_cmd(pcmdpriv, ph2c);
+
+exit:
+
+
+ return res;
+}
+
+u8 rtw_setstakey_cmd(struct adapter *padapter, u8 *psta, u8 unicast_key)
+{
+ struct cmd_obj *ph2c;
+ struct set_stakey_parm *psetstakey_para;
+ struct cmd_priv *pcmdpriv = &padapter->cmdpriv;
+ struct set_stakey_rsp *psetstakey_rsp = NULL;
+
+ struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
+ struct security_priv *psecuritypriv = &padapter->securitypriv;
+ struct sta_info *sta = (struct sta_info *)psta;
+ u8 res = _SUCCESS;
+
+
+ ph2c = kzalloc(sizeof(struct cmd_obj), GFP_KERNEL);
+ if (ph2c == NULL) {
+ res = _FAIL;
+ goto exit;
+ }
+
+ psetstakey_para = kzalloc(sizeof(struct set_stakey_parm), GFP_KERNEL);
+ if (psetstakey_para == NULL) {
+ kfree(ph2c);
+ res = _FAIL;
+ goto exit;
+ }
+
+ psetstakey_rsp = kzalloc(sizeof(struct set_stakey_rsp), GFP_KERNEL);
+ if (psetstakey_rsp == NULL) {
+ kfree(ph2c);
+ kfree(psetstakey_para);
+ res = _FAIL;
+ goto exit;
+ }
+
+ init_h2fwcmd_w_parm_no_rsp(ph2c, psetstakey_para, _SetStaKey_CMD_);
+ ph2c->rsp = (u8 *)psetstakey_rsp;
+ ph2c->rspsz = sizeof(struct set_stakey_rsp);
+
+ ether_addr_copy(psetstakey_para->addr, sta->hwaddr);
+
+ if (check_fwstate(pmlmepriv, WIFI_STATION_STATE))
+ psetstakey_para->algorithm = (unsigned char)psecuritypriv->dot11PrivacyAlgrthm;
+ else
+ GET_ENCRY_ALGO(psecuritypriv, sta, psetstakey_para->algorithm, false);
+
+ if (unicast_key)
+ memcpy(&psetstakey_para->key, &sta->dot118021x_UncstKey, 16);
+ else
+ memcpy(&psetstakey_para->key, &psecuritypriv->dot118021XGrpKey[psecuritypriv->dot118021XGrpKeyid].skey, 16);
+
+ /* jeff: set this because at least sw key is ready */
+ padapter->securitypriv.busetkipkey = true;
+
+ res = rtw_enqueue_cmd(pcmdpriv, ph2c);
+
+exit:
+
+
+ return res;
+}
+
+u8 rtw_clearstakey_cmd(struct adapter *padapter, u8 *psta, u8 entry, u8 enqueue)
+{
+ struct cmd_obj *ph2c;
+ struct set_stakey_parm *psetstakey_para;
+ struct cmd_priv *pcmdpriv = &padapter->cmdpriv;
+ struct set_stakey_rsp *psetstakey_rsp = NULL;
+ struct sta_info *sta = (struct sta_info *)psta;
+ u8 res = _SUCCESS;
+
+
+ if (!enqueue) {
+ clear_cam_entry(padapter, entry);
+ } else {
+ ph2c = kzalloc(sizeof(struct cmd_obj), GFP_ATOMIC);
+ if (ph2c == NULL) {
+ res = _FAIL;
+ goto exit;
+ }
+
+ psetstakey_para = kzalloc(sizeof(struct set_stakey_parm), GFP_ATOMIC);
+ if (psetstakey_para == NULL) {
+ kfree(ph2c);
+ res = _FAIL;
+ goto exit;
+ }
+
+ psetstakey_rsp = kzalloc(sizeof(struct set_stakey_rsp), GFP_ATOMIC);
+ if (psetstakey_rsp == NULL) {
+ kfree(ph2c);
+ kfree(psetstakey_para);
+ res = _FAIL;
+ goto exit;
+ }
+
+ init_h2fwcmd_w_parm_no_rsp(ph2c, psetstakey_para, _SetStaKey_CMD_);
+ ph2c->rsp = (u8 *)psetstakey_rsp;
+ ph2c->rspsz = sizeof(struct set_stakey_rsp);
+
+ ether_addr_copy(psetstakey_para->addr, sta->hwaddr);
+
+ psetstakey_para->algorithm = _NO_PRIVACY_;
+
+ psetstakey_para->id = entry;
+
+ res = rtw_enqueue_cmd(pcmdpriv, ph2c);
+ }
+exit:
+
+
+ return res;
+}
+
+u8 rtw_addbareq_cmd(struct adapter *padapter, u8 tid, u8 *addr)
+{
+ struct cmd_priv *pcmdpriv = &padapter->cmdpriv;
+ struct cmd_obj *ph2c;
+ struct addBaReq_parm *paddbareq_parm;
+ u8 res = _SUCCESS;
+
+
+ ph2c = kzalloc(sizeof(struct cmd_obj), GFP_KERNEL);
+ if (ph2c == NULL) {
+ res = _FAIL;
+ goto exit;
+ }
+
+ paddbareq_parm = kzalloc(sizeof(struct addBaReq_parm), GFP_KERNEL);
+ if (paddbareq_parm == NULL) {
+ kfree(ph2c);
+ res = _FAIL;
+ goto exit;
+ }
+
+ paddbareq_parm->tid = tid;
+ memcpy(paddbareq_parm->addr, addr, ETH_ALEN);
+
+ init_h2fwcmd_w_parm_no_rsp(ph2c, paddbareq_parm, GEN_CMD_CODE(_AddBAReq));
+
+ /* DBG_88E("rtw_addbareq_cmd, tid =%d\n", tid); */
+
+ /* rtw_enqueue_cmd(pcmdpriv, ph2c); */
+ res = rtw_enqueue_cmd(pcmdpriv, ph2c);
+
+exit:
+
+
+ return res;
+}
+
+u8 rtw_dynamic_chk_wk_cmd(struct adapter *padapter)
+{
+ struct cmd_obj *ph2c;
+ struct drvextra_cmd_parm *pdrvextra_cmd_parm;
+ struct cmd_priv *pcmdpriv = &padapter->cmdpriv;
+ u8 res = _SUCCESS;
+
+
+ ph2c = kzalloc(sizeof(struct cmd_obj), GFP_ATOMIC);
+ if (ph2c == NULL) {
+ res = _FAIL;
+ goto exit;
+ }
+
+ pdrvextra_cmd_parm = kzalloc(sizeof(struct drvextra_cmd_parm), GFP_ATOMIC);
+ if (pdrvextra_cmd_parm == NULL) {
+ kfree(ph2c);
+ res = _FAIL;
+ goto exit;
+ }
+
+ pdrvextra_cmd_parm->ec_id = DYNAMIC_CHK_WK_CID;
+ pdrvextra_cmd_parm->type_size = 0;
+ pdrvextra_cmd_parm->pbuf = (u8 *)padapter;
+
+ init_h2fwcmd_w_parm_no_rsp(ph2c, pdrvextra_cmd_parm, GEN_CMD_CODE(_Set_Drv_Extra));
+
+
+ /* rtw_enqueue_cmd(pcmdpriv, ph2c); */
+ res = rtw_enqueue_cmd(pcmdpriv, ph2c);
+exit:
+ return res;
+}
+
+u8 rtw_set_chplan_cmd(struct adapter *padapter, u8 chplan, u8 enqueue)
+{
+ struct cmd_obj *pcmdobj;
+ struct SetChannelPlan_param *setChannelPlan_param;
+ struct cmd_priv *pcmdpriv = &padapter->cmdpriv;
+
+ u8 res = _SUCCESS;
+
+
+ RT_TRACE(_module_rtl871x_cmd_c_, _drv_notice_, ("+rtw_set_chplan_cmd\n"));
+
+ /* check input parameter */
+ if (!rtw_is_channel_plan_valid(chplan)) {
+ res = _FAIL;
+ goto exit;
+ }
+
+ /* prepare cmd parameter */
+ setChannelPlan_param = kzalloc(sizeof(struct SetChannelPlan_param), GFP_KERNEL);
+ if (setChannelPlan_param == NULL) {
+ res = _FAIL;
+ goto exit;
+ }
+ setChannelPlan_param->channel_plan = chplan;
+
+ if (enqueue) {
+ /* need enqueue, prepare cmd_obj and enqueue */
+ pcmdobj = kzalloc(sizeof(struct cmd_obj), GFP_KERNEL);
+ if (pcmdobj == NULL) {
+ kfree(setChannelPlan_param);
+ res = _FAIL;
+ goto exit;
+ }
+
+ init_h2fwcmd_w_parm_no_rsp(pcmdobj, setChannelPlan_param, GEN_CMD_CODE(_SetChannelPlan));
+ res = rtw_enqueue_cmd(pcmdpriv, pcmdobj);
+ } else {
+ /* no need to enqueue, do the cmd hdl directly and free cmd parameter */
+ if (H2C_SUCCESS != set_chplan_hdl(padapter, (unsigned char *)setChannelPlan_param))
+ res = _FAIL;
+
+ kfree(setChannelPlan_param);
+ }
+
+ /* do something based on res... */
+ if (res == _SUCCESS)
+ padapter->mlmepriv.ChannelPlan = chplan;
+
+exit:
+
+
+ return res;
+}
+
+static void traffic_status_watchdog(struct adapter *padapter)
+{
+ u8 bEnterPS;
+ u8 bBusyTraffic = false, bTxBusyTraffic = false, bRxBusyTraffic = false;
+ u8 bHigherBusyTraffic = false, bHigherBusyRxTraffic = false, bHigherBusyTxTraffic = false;
+ struct mlme_priv *pmlmepriv = &(padapter->mlmepriv);
+
+ /* */
+ /* Determine if our traffic is busy now */
+ /* */
+ if (check_fwstate(pmlmepriv, _FW_LINKED)) {
+ if (pmlmepriv->LinkDetectInfo.NumRxOkInPeriod > 100 ||
+ pmlmepriv->LinkDetectInfo.NumTxOkInPeriod > 100) {
+ bBusyTraffic = true;
+
+ if (pmlmepriv->LinkDetectInfo.NumRxOkInPeriod > pmlmepriv->LinkDetectInfo.NumTxOkInPeriod)
+ bRxBusyTraffic = true;
+ else
+ bTxBusyTraffic = true;
+ }
+
+ /* Higher Tx/Rx data. */
+ if (pmlmepriv->LinkDetectInfo.NumRxOkInPeriod > 4000 ||
+ pmlmepriv->LinkDetectInfo.NumTxOkInPeriod > 4000) {
+ bHigherBusyTraffic = true;
+
+ if (pmlmepriv->LinkDetectInfo.NumRxOkInPeriod > pmlmepriv->LinkDetectInfo.NumTxOkInPeriod)
+ bHigherBusyRxTraffic = true;
+ else
+ bHigherBusyTxTraffic = true;
+ }
+
+ /* check traffic for powersaving. */
+ if (((pmlmepriv->LinkDetectInfo.NumRxUnicastOkInPeriod + pmlmepriv->LinkDetectInfo.NumTxOkInPeriod) > 8) ||
+ (pmlmepriv->LinkDetectInfo.NumRxUnicastOkInPeriod > 2))
+ bEnterPS = false;
+ else
+ bEnterPS = true;
+
+ /* LeisurePS only work in infra mode. */
+ if (bEnterPS)
+ LPS_Enter(padapter);
+ else
+ LPS_Leave(padapter);
+ } else {
+ LPS_Leave(padapter);
+ }
+
+ pmlmepriv->LinkDetectInfo.NumRxOkInPeriod = 0;
+ pmlmepriv->LinkDetectInfo.NumTxOkInPeriod = 0;
+ pmlmepriv->LinkDetectInfo.NumRxUnicastOkInPeriod = 0;
+ pmlmepriv->LinkDetectInfo.bBusyTraffic = bBusyTraffic;
+ pmlmepriv->LinkDetectInfo.bTxBusyTraffic = bTxBusyTraffic;
+ pmlmepriv->LinkDetectInfo.bRxBusyTraffic = bRxBusyTraffic;
+ pmlmepriv->LinkDetectInfo.bHigherBusyTraffic = bHigherBusyTraffic;
+ pmlmepriv->LinkDetectInfo.bHigherBusyRxTraffic = bHigherBusyRxTraffic;
+ pmlmepriv->LinkDetectInfo.bHigherBusyTxTraffic = bHigherBusyTxTraffic;
+}
+
+static void dynamic_chk_wk_hdl(struct adapter *padapter, u8 *pbuf, int sz)
+{
+ struct mlme_priv *pmlmepriv;
+
+ padapter = (struct adapter *)pbuf;
+ pmlmepriv = &(padapter->mlmepriv);
+
+#ifdef CONFIG_88EU_AP_MODE
+ if (check_fwstate(pmlmepriv, WIFI_AP_STATE) == true)
+ expire_timeout_chk(padapter);
+#endif
+
+ linked_status_chk(padapter);
+ traffic_status_watchdog(padapter);
+
+ rtw_hal_dm_watchdog(padapter);
+}
+
+static void lps_ctrl_wk_hdl(struct adapter *padapter, u8 lps_ctrl_type)
+{
+ struct pwrctrl_priv *pwrpriv = &padapter->pwrctrlpriv;
+ struct mlme_priv *pmlmepriv = &(padapter->mlmepriv);
+ u8 mstatus;
+
+
+ if ((check_fwstate(pmlmepriv, WIFI_ADHOC_MASTER_STATE) == true) ||
+ (check_fwstate(pmlmepriv, WIFI_ADHOC_STATE) == true))
+ return;
+
+ switch (lps_ctrl_type) {
+ case LPS_CTRL_SCAN:
+ if (check_fwstate(pmlmepriv, _FW_LINKED) == true) {
+ /* connect */
+ LPS_Leave(padapter);
+ }
+ break;
+ case LPS_CTRL_JOINBSS:
+ LPS_Leave(padapter);
+ break;
+ case LPS_CTRL_CONNECT:
+ mstatus = 1;/* connect */
+ /* Reset LPS Setting */
+ padapter->pwrctrlpriv.LpsIdleCount = 0;
+ rtw_hal_set_hwreg(padapter, HW_VAR_H2C_FW_JOINBSSRPT, (u8 *)(&mstatus));
+ break;
+ case LPS_CTRL_DISCONNECT:
+ mstatus = 0;/* disconnect */
+ LPS_Leave(padapter);
+ rtw_hal_set_hwreg(padapter, HW_VAR_H2C_FW_JOINBSSRPT, (u8 *)(&mstatus));
+ break;
+ case LPS_CTRL_SPECIAL_PACKET:
+ /* DBG_88E("LPS_CTRL_SPECIAL_PACKET\n"); */
+ pwrpriv->DelayLPSLastTimeStamp = jiffies;
+ LPS_Leave(padapter);
+ break;
+ case LPS_CTRL_LEAVE:
+ LPS_Leave(padapter);
+ break;
+ default:
+ break;
+ }
+
+}
+
+u8 rtw_lps_ctrl_wk_cmd(struct adapter *padapter, u8 lps_ctrl_type, u8 enqueue)
+{
+ struct cmd_obj *ph2c;
+ struct drvextra_cmd_parm *pdrvextra_cmd_parm;
+ struct cmd_priv *pcmdpriv = &padapter->cmdpriv;
+ /* struct pwrctrl_priv *pwrctrlpriv = &padapter->pwrctrlpriv; */
+ u8 res = _SUCCESS;
+
+ if (enqueue) {
+ ph2c = kzalloc(sizeof(struct cmd_obj), GFP_ATOMIC);
+ if (ph2c == NULL) {
+ res = _FAIL;
+ goto exit;
+ }
+
+ pdrvextra_cmd_parm = kzalloc(sizeof(struct drvextra_cmd_parm), GFP_ATOMIC);
+ if (pdrvextra_cmd_parm == NULL) {
+ kfree(ph2c);
+ res = _FAIL;
+ goto exit;
+ }
+
+ pdrvextra_cmd_parm->ec_id = LPS_CTRL_WK_CID;
+ pdrvextra_cmd_parm->type_size = lps_ctrl_type;
+ pdrvextra_cmd_parm->pbuf = NULL;
+
+ init_h2fwcmd_w_parm_no_rsp(ph2c, pdrvextra_cmd_parm, GEN_CMD_CODE(_Set_Drv_Extra));
+
+ res = rtw_enqueue_cmd(pcmdpriv, ph2c);
+ } else {
+ lps_ctrl_wk_hdl(padapter, lps_ctrl_type);
+ }
+
+exit:
+
+
+ return res;
+}
+
+static void rpt_timer_setting_wk_hdl(struct adapter *padapter, u16 min_time)
+{
+ rtw_hal_set_hwreg(padapter, HW_VAR_RPT_TIMER_SETTING, (u8 *)(&min_time));
+}
+
+u8 rtw_rpt_timer_cfg_cmd(struct adapter *padapter, u16 min_time)
+{
+ struct cmd_obj *ph2c;
+ struct drvextra_cmd_parm *pdrvextra_cmd_parm;
+ struct cmd_priv *pcmdpriv = &padapter->cmdpriv;
+
+ u8 res = _SUCCESS;
+
+ ph2c = kzalloc(sizeof(struct cmd_obj), GFP_ATOMIC);
+ if (ph2c == NULL) {
+ res = _FAIL;
+ goto exit;
+ }
+
+ pdrvextra_cmd_parm = kzalloc(sizeof(struct drvextra_cmd_parm), GFP_ATOMIC);
+ if (pdrvextra_cmd_parm == NULL) {
+ kfree(ph2c);
+ res = _FAIL;
+ goto exit;
+ }
+
+ pdrvextra_cmd_parm->ec_id = RTP_TIMER_CFG_WK_CID;
+ pdrvextra_cmd_parm->type_size = min_time;
+ pdrvextra_cmd_parm->pbuf = NULL;
+ init_h2fwcmd_w_parm_no_rsp(ph2c, pdrvextra_cmd_parm, GEN_CMD_CODE(_Set_Drv_Extra));
+ res = rtw_enqueue_cmd(pcmdpriv, ph2c);
+exit:
+
+
+ return res;
+}
+
+static void antenna_select_wk_hdl(struct adapter *padapter, u8 antenna)
+{
+ rtw_hal_set_hwreg(padapter, HW_VAR_ANTENNA_DIVERSITY_SELECT, (u8 *)(&antenna));
+}
+
+u8 rtw_antenna_select_cmd(struct adapter *padapter, u8 antenna, u8 enqueue)
+{
+ struct cmd_obj *ph2c;
+ struct drvextra_cmd_parm *pdrvextra_cmd_parm;
+ struct cmd_priv *pcmdpriv = &padapter->cmdpriv;
+ u8 support_ant_div;
+ u8 res = _SUCCESS;
+
+ rtw_hal_get_def_var(padapter, HAL_DEF_IS_SUPPORT_ANT_DIV, &support_ant_div);
+ if (!support_ant_div)
+ return res;
+
+ if (enqueue) {
+ ph2c = kzalloc(sizeof(struct cmd_obj), GFP_KERNEL);
+ if (ph2c == NULL) {
+ res = _FAIL;
+ goto exit;
+ }
+
+ pdrvextra_cmd_parm = kzalloc(sizeof(struct drvextra_cmd_parm), GFP_KERNEL);
+ if (pdrvextra_cmd_parm == NULL) {
+ kfree(ph2c);
+ res = _FAIL;
+ goto exit;
+ }
+
+ pdrvextra_cmd_parm->ec_id = ANT_SELECT_WK_CID;
+ pdrvextra_cmd_parm->type_size = antenna;
+ pdrvextra_cmd_parm->pbuf = NULL;
+ init_h2fwcmd_w_parm_no_rsp(ph2c, pdrvextra_cmd_parm, GEN_CMD_CODE(_Set_Drv_Extra));
+
+ res = rtw_enqueue_cmd(pcmdpriv, ph2c);
+ } else {
+ antenna_select_wk_hdl(padapter, antenna);
+ }
+exit:
+
+
+ return res;
+}
+
+u8 rtw_ps_cmd(struct adapter *padapter)
+{
+ struct cmd_obj *ppscmd;
+ struct drvextra_cmd_parm *pdrvextra_cmd_parm;
+ struct cmd_priv *pcmdpriv = &padapter->cmdpriv;
+
+ u8 res = _SUCCESS;
+
+ ppscmd = kzalloc(sizeof(struct cmd_obj), GFP_ATOMIC);
+ if (ppscmd == NULL) {
+ res = _FAIL;
+ goto exit;
+ }
+
+ pdrvextra_cmd_parm = kzalloc(sizeof(struct drvextra_cmd_parm), GFP_ATOMIC);
+ if (pdrvextra_cmd_parm == NULL) {
+ kfree(ppscmd);
+ res = _FAIL;
+ goto exit;
+ }
+
+ pdrvextra_cmd_parm->ec_id = POWER_SAVING_CTRL_WK_CID;
+ pdrvextra_cmd_parm->pbuf = NULL;
+ init_h2fwcmd_w_parm_no_rsp(ppscmd, pdrvextra_cmd_parm, GEN_CMD_CODE(_Set_Drv_Extra));
+
+ res = rtw_enqueue_cmd(pcmdpriv, ppscmd);
+
+exit:
+
+
+ return res;
+}
+
+#ifdef CONFIG_88EU_AP_MODE
+
+static void rtw_chk_hi_queue_hdl(struct adapter *padapter)
+{
+ int cnt = 0;
+ struct sta_info *psta_bmc;
+ struct sta_priv *pstapriv = &padapter->stapriv;
+
+ psta_bmc = rtw_get_bcmc_stainfo(padapter);
+ if (!psta_bmc)
+ return;
+
+ if (psta_bmc->sleepq_len == 0) {
+ u8 val = 0;
+
+ /* while ((rtw_read32(padapter, 0x414)&0x00ffff00)!= 0) */
+ /* while ((rtw_read32(padapter, 0x414)&0x0000ff00)!= 0) */
+
+ rtw_hal_get_hwreg(padapter, HW_VAR_CHK_HI_QUEUE_EMPTY, &val);
+
+ while (!val) {
+ msleep(100);
+
+ cnt++;
+
+ if (cnt > 10)
+ break;
+
+ rtw_hal_get_hwreg(padapter, HW_VAR_CHK_HI_QUEUE_EMPTY, &val);
+ }
+
+ if (cnt <= 10) {
+ pstapriv->tim_bitmap &= ~BIT(0);
+ pstapriv->sta_dz_bitmap &= ~BIT(0);
+
+ update_beacon(padapter, _TIM_IE_, NULL, false);
+ } else { /* re check again */
+ rtw_chk_hi_queue_cmd(padapter);
+ }
+ }
+}
+
+u8 rtw_chk_hi_queue_cmd(struct adapter *padapter)
+{
+ struct cmd_obj *ph2c;
+ struct drvextra_cmd_parm *pdrvextra_cmd_parm;
+ struct cmd_priv *pcmdpriv = &padapter->cmdpriv;
+ u8 res = _SUCCESS;
+
+ ph2c = kzalloc(sizeof(struct cmd_obj), GFP_KERNEL);
+ if (ph2c == NULL) {
+ res = _FAIL;
+ goto exit;
+ }
+
+ pdrvextra_cmd_parm = kzalloc(sizeof(struct drvextra_cmd_parm), GFP_KERNEL);
+ if (pdrvextra_cmd_parm == NULL) {
+ kfree(ph2c);
+ res = _FAIL;
+ goto exit;
+ }
+
+ pdrvextra_cmd_parm->ec_id = CHECK_HIQ_WK_CID;
+ pdrvextra_cmd_parm->type_size = 0;
+ pdrvextra_cmd_parm->pbuf = NULL;
+
+ init_h2fwcmd_w_parm_no_rsp(ph2c, pdrvextra_cmd_parm, GEN_CMD_CODE(_Set_Drv_Extra));
+
+ res = rtw_enqueue_cmd(pcmdpriv, ph2c);
+exit:
+ return res;
+}
+#endif
+
+u8 rtw_drvextra_cmd_hdl(struct adapter *padapter, unsigned char *pbuf)
+{
+ struct drvextra_cmd_parm *pdrvextra_cmd;
+
+ if (!pbuf)
+ return H2C_PARAMETERS_ERROR;
+
+ pdrvextra_cmd = (struct drvextra_cmd_parm *)pbuf;
+
+ switch (pdrvextra_cmd->ec_id) {
+ case DYNAMIC_CHK_WK_CID:
+ dynamic_chk_wk_hdl(padapter, pdrvextra_cmd->pbuf, pdrvextra_cmd->type_size);
+ break;
+ case POWER_SAVING_CTRL_WK_CID:
+ rtw_ps_processor(padapter);
+ break;
+ case LPS_CTRL_WK_CID:
+ lps_ctrl_wk_hdl(padapter, (u8)pdrvextra_cmd->type_size);
+ break;
+ case RTP_TIMER_CFG_WK_CID:
+ rpt_timer_setting_wk_hdl(padapter, pdrvextra_cmd->type_size);
+ break;
+ case ANT_SELECT_WK_CID:
+ antenna_select_wk_hdl(padapter, pdrvextra_cmd->type_size);
+ break;
+#ifdef CONFIG_88EU_AP_MODE
+ case CHECK_HIQ_WK_CID:
+ rtw_chk_hi_queue_hdl(padapter);
+ break;
+#endif /* CONFIG_88EU_AP_MODE */
+ default:
+ break;
+ }
+
+ if (pdrvextra_cmd->pbuf && pdrvextra_cmd->type_size > 0)
+ kfree(pdrvextra_cmd->pbuf);
+
+ return H2C_SUCCESS;
+}
+
+void rtw_survey_cmd_callback(struct adapter *padapter, struct cmd_obj *pcmd)
+{
+ struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
+
+
+ if (pcmd->res == H2C_DROPPED) {
+ /* TODO: cancel timer and do timeout handler directly... */
+ /* need to make timeout handlerOS independent */
+ mod_timer(&pmlmepriv->scan_to_timer,
+ jiffies + msecs_to_jiffies(1));
+ } else if (pcmd->res != H2C_SUCCESS) {
+ mod_timer(&pmlmepriv->scan_to_timer,
+ jiffies + msecs_to_jiffies(1));
+ RT_TRACE(_module_rtl871x_cmd_c_, _drv_err_, ("\n ********Error: MgntActrtw_set_802_11_bssid_LIST_SCAN Fail ************\n\n."));
+ }
+
+ /* free cmd */
+ rtw_free_cmd_obj(pcmd);
+
+}
+void rtw_disassoc_cmd_callback(struct adapter *padapter, struct cmd_obj *pcmd)
+{
+ struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
+
+
+ if (pcmd->res != H2C_SUCCESS) {
+ spin_lock_bh(&pmlmepriv->lock);
+ set_fwstate(pmlmepriv, _FW_LINKED);
+ spin_unlock_bh(&pmlmepriv->lock);
+
+ RT_TRACE(_module_rtl871x_cmd_c_, _drv_err_, ("\n ***Error: disconnect_cmd_callback Fail ***\n."));
+ return;
+ }
+
+ /* free cmd */
+ rtw_free_cmd_obj(pcmd);
+}
+
+void rtw_joinbss_cmd_callback(struct adapter *padapter, struct cmd_obj *pcmd)
+{
+ struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
+
+
+ if (pcmd->res == H2C_DROPPED) {
+ /* TODO: cancel timer and do timeout handler directly... */
+ /* need to make timeout handlerOS independent */
+ mod_timer(&pmlmepriv->assoc_timer,
+ jiffies + msecs_to_jiffies(1));
+ } else if (pcmd->res != H2C_SUCCESS) {
+ RT_TRACE(_module_rtl871x_cmd_c_, _drv_err_, ("********Error:rtw_select_and_join_from_scanned_queue Wait Sema Fail ************\n"));
+ mod_timer(&pmlmepriv->assoc_timer,
+ jiffies + msecs_to_jiffies(1));
+ }
+
+ rtw_free_cmd_obj(pcmd);
+
+}
+
+void rtw_createbss_cmd_callback(struct adapter *padapter, struct cmd_obj *pcmd)
+{
+ struct sta_info *psta = NULL;
+ struct wlan_network *pwlan = NULL;
+ struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
+ struct wlan_bssid_ex *pnetwork = (struct wlan_bssid_ex *)pcmd->parmbuf;
+ struct wlan_network *tgt_network = &(pmlmepriv->cur_network);
+
+
+ if (pcmd->res != H2C_SUCCESS) {
+ RT_TRACE(_module_rtl871x_cmd_c_, _drv_err_, ("\n ********Error: rtw_createbss_cmd_callback Fail ************\n\n."));
+ mod_timer(&pmlmepriv->assoc_timer,
+ jiffies + msecs_to_jiffies(1));
+ }
+
+ del_timer_sync(&pmlmepriv->assoc_timer);
+
+ spin_lock_bh(&pmlmepriv->lock);
+
+ if (check_fwstate(pmlmepriv, WIFI_AP_STATE)) {
+ psta = rtw_get_stainfo(&padapter->stapriv, pnetwork->MacAddress);
+ if (!psta) {
+ psta = rtw_alloc_stainfo(&padapter->stapriv, pnetwork->MacAddress);
+ if (psta == NULL) {
+ RT_TRACE(_module_rtl871x_cmd_c_, _drv_err_, ("\nCan't alloc sta_info when createbss_cmd_callback\n"));
+ goto createbss_cmd_fail;
+ }
+ }
+
+ rtw_indicate_connect(padapter);
+ } else {
+ pwlan = _rtw_alloc_network(pmlmepriv);
+ spin_lock_bh(&(pmlmepriv->scanned_queue.lock));
+ if (pwlan == NULL) {
+ pwlan = rtw_get_oldest_wlan_network(&pmlmepriv->scanned_queue);
+ if (pwlan == NULL) {
+ RT_TRACE(_module_rtl871x_cmd_c_, _drv_err_, ("\n Error: can't get pwlan in rtw_joinbss_event_callback\n"));
+ spin_unlock_bh(&pmlmepriv->scanned_queue.lock);
+ goto createbss_cmd_fail;
+ }
+ pwlan->last_scanned = jiffies;
+ } else {
+ list_add_tail(&(pwlan->list), &pmlmepriv->scanned_queue.queue);
+ }
+
+ pnetwork->Length = get_wlan_bssid_ex_sz(pnetwork);
+ memcpy(&(pwlan->network), pnetwork, pnetwork->Length);
+
+ memcpy(&tgt_network->network, pnetwork, (get_wlan_bssid_ex_sz(pnetwork)));
+
+ _clr_fwstate_(pmlmepriv, _FW_UNDER_LINKING);
+
+ spin_unlock_bh(&pmlmepriv->scanned_queue.lock);
+ /* we will set _FW_LINKED when there is one more sat to join us (rtw_stassoc_event_callback) */
+ }
+
+createbss_cmd_fail:
+
+ spin_unlock_bh(&pmlmepriv->lock);
+
+ rtw_free_cmd_obj(pcmd);
+
+}
+
+void rtw_setstaKey_cmdrsp_callback(struct adapter *padapter, struct cmd_obj *pcmd)
+{
+ struct sta_priv *pstapriv = &padapter->stapriv;
+ struct set_stakey_rsp *psetstakey_rsp = (struct set_stakey_rsp *)(pcmd->rsp);
+ struct sta_info *psta = rtw_get_stainfo(pstapriv, psetstakey_rsp->addr);
+
+
+ if (psta == NULL) {
+ RT_TRACE(_module_rtl871x_cmd_c_, _drv_err_, ("\nERROR: rtw_setstaKey_cmdrsp_callback => can't get sta_info\n\n"));
+ goto exit;
+ }
+exit:
+ rtw_free_cmd_obj(pcmd);
+}
+
+void rtw_setassocsta_cmdrsp_callback(struct adapter *padapter, struct cmd_obj *pcmd)
+{
+ struct sta_priv *pstapriv = &padapter->stapriv;
+ struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
+ struct set_assocsta_parm *passocsta_parm = (struct set_assocsta_parm *)(pcmd->parmbuf);
+ struct set_assocsta_rsp *passocsta_rsp = (struct set_assocsta_rsp *)(pcmd->rsp);
+ struct sta_info *psta = rtw_get_stainfo(pstapriv, passocsta_parm->addr);
+
+
+ if (psta == NULL) {
+ RT_TRACE(_module_rtl871x_cmd_c_, _drv_err_, ("\nERROR: setassocsta_cmdrsp_callbac => can't get sta_info\n\n"));
+ goto exit;
+ }
+
+ psta->aid = passocsta_rsp->cam_id;
+ psta->mac_id = passocsta_rsp->cam_id;
+
+ spin_lock_bh(&pmlmepriv->lock);
+
+ if ((check_fwstate(pmlmepriv, WIFI_MP_STATE) == true) && (check_fwstate(pmlmepriv, _FW_UNDER_LINKING) == true))
+ _clr_fwstate_(pmlmepriv, _FW_UNDER_LINKING);
+
+ set_fwstate(pmlmepriv, _FW_LINKED);
+ spin_unlock_bh(&pmlmepriv->lock);
+
+exit:
+ rtw_free_cmd_obj(pcmd);
+
+}
diff --git a/drivers/staging/rtl8188eu/core/rtw_debug.c b/drivers/staging/rtl8188eu/core/rtw_debug.c
new file mode 100644
index 000000000..bc3fe10ff
--- /dev/null
+++ b/drivers/staging/rtl8188eu/core/rtw_debug.c
@@ -0,0 +1,947 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2012 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ *
+ ******************************************************************************/
+#define _RTW_DEBUG_C_
+
+#include <rtw_debug.h>
+#include <usb_ops_linux.h>
+
+int proc_get_drv_version(char *page, char **start,
+ off_t offset, int count,
+ int *eof, void *data)
+{
+ int len = 0;
+
+ len += snprintf(page + len, count - len, "%s\n", DRIVERVERSION);
+
+ *eof = 1;
+ return len;
+}
+
+int proc_get_write_reg(char *page, char **start,
+ off_t offset, int count,
+ int *eof, void *data)
+{
+ *eof = 1;
+ return 0;
+}
+
+int proc_set_write_reg(struct file *file, const char __user *buffer,
+ unsigned long count, void *data)
+{
+ struct net_device *dev = data;
+ struct adapter *padapter = (struct adapter *)rtw_netdev_priv(dev);
+ char tmp[32];
+ u32 addr, val, len;
+
+ if (count < 3) {
+ DBG_88E("argument size is less than 3\n");
+ return -EFAULT;
+ }
+
+ if (buffer && !copy_from_user(tmp, buffer, sizeof(tmp))) {
+ int num = sscanf(tmp, "%x %x %x", &addr, &val, &len);
+
+ if (num != 3) {
+ DBG_88E("invalid write_reg parameter!\n");
+ return count;
+ }
+ switch (len) {
+ case 1:
+ usb_write8(padapter, addr, (u8)val);
+ break;
+ case 2:
+ usb_write16(padapter, addr, (u16)val);
+ break;
+ case 4:
+ usb_write32(padapter, addr, val);
+ break;
+ default:
+ DBG_88E("error write length =%d", len);
+ break;
+ }
+ }
+ return count;
+}
+
+static u32 proc_get_read_addr = 0xeeeeeeee;
+static u32 proc_get_read_len = 0x4;
+
+int proc_get_read_reg(char *page, char **start,
+ off_t offset, int count,
+ int *eof, void *data)
+{
+ struct net_device *dev = data;
+ struct adapter *padapter = (struct adapter *)rtw_netdev_priv(dev);
+
+ int len = 0;
+
+ if (proc_get_read_addr == 0xeeeeeeee) {
+ *eof = 1;
+ return len;
+ }
+
+ switch (proc_get_read_len) {
+ case 1:
+ len += snprintf(page + len, count - len, "usb_read8(0x%x)=0x%x\n", proc_get_read_addr, usb_read8(padapter, proc_get_read_addr));
+ break;
+ case 2:
+ len += snprintf(page + len, count - len, "usb_read16(0x%x)=0x%x\n", proc_get_read_addr, usb_read16(padapter, proc_get_read_addr));
+ break;
+ case 4:
+ len += snprintf(page + len, count - len, "usb_read32(0x%x)=0x%x\n", proc_get_read_addr, usb_read32(padapter, proc_get_read_addr));
+ break;
+ default:
+ len += snprintf(page + len, count - len, "error read length=%d\n", proc_get_read_len);
+ break;
+ }
+
+ *eof = 1;
+ return len;
+}
+
+int proc_set_read_reg(struct file *file, const char __user *buffer,
+ unsigned long count, void *data)
+{
+ char tmp[16];
+ u32 addr, len;
+
+ if (count < 2) {
+ DBG_88E("argument size is less than 2\n");
+ return -EFAULT;
+ }
+
+ if (buffer && !copy_from_user(tmp, buffer, sizeof(tmp))) {
+ int num = sscanf(tmp, "%x %x", &addr, &len);
+
+ if (num != 2) {
+ DBG_88E("invalid read_reg parameter!\n");
+ return count;
+ }
+
+ proc_get_read_addr = addr;
+
+ proc_get_read_len = len;
+ }
+
+ return count;
+}
+
+int proc_get_fwstate(char *page, char **start,
+ off_t offset, int count,
+ int *eof, void *data)
+{
+ struct net_device *dev = data;
+ struct adapter *padapter = (struct adapter *)rtw_netdev_priv(dev);
+ struct mlme_priv *pmlmepriv = &(padapter->mlmepriv);
+
+ int len = 0;
+
+ len += snprintf(page + len, count - len, "fwstate=0x%x\n", get_fwstate(pmlmepriv));
+
+ *eof = 1;
+ return len;
+}
+
+int proc_get_sec_info(char *page, char **start,
+ off_t offset, int count,
+ int *eof, void *data)
+{
+ struct net_device *dev = data;
+ struct adapter *padapter = (struct adapter *)rtw_netdev_priv(dev);
+ struct security_priv *psecuritypriv = &padapter->securitypriv;
+
+ int len = 0;
+
+ len += snprintf(page + len, count - len, "auth_alg=0x%x, enc_alg=0x%x, auth_type=0x%x, enc_type=0x%x\n",
+ psecuritypriv->dot11AuthAlgrthm, psecuritypriv->dot11PrivacyAlgrthm,
+ psecuritypriv->ndisauthtype, psecuritypriv->ndisencryptstatus);
+
+ *eof = 1;
+ return len;
+}
+
+int proc_get_mlmext_state(char *page, char **start,
+ off_t offset, int count,
+ int *eof, void *data)
+{
+ struct net_device *dev = data;
+ struct adapter *padapter = (struct adapter *)rtw_netdev_priv(dev);
+ struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv;
+ struct mlme_ext_info *pmlmeinfo = &(pmlmeext->mlmext_info);
+
+ int len = 0;
+
+ len += snprintf(page + len, count - len, "pmlmeinfo->state=0x%x\n", pmlmeinfo->state);
+
+ *eof = 1;
+ return len;
+}
+
+int proc_get_qos_option(char *page, char **start,
+ off_t offset, int count,
+ int *eof, void *data)
+{
+ struct net_device *dev = data;
+ struct adapter *padapter = (struct adapter *)rtw_netdev_priv(dev);
+ struct mlme_priv *pmlmepriv = &(padapter->mlmepriv);
+
+ int len = 0;
+
+ len += snprintf(page + len, count - len, "qos_option=%d\n", pmlmepriv->qospriv.qos_option);
+
+ *eof = 1;
+ return len;
+}
+
+int proc_get_ht_option(char *page, char **start,
+ off_t offset, int count,
+ int *eof, void *data)
+{
+ struct net_device *dev = data;
+ struct adapter *padapter = (struct adapter *)rtw_netdev_priv(dev);
+ struct mlme_priv *pmlmepriv = &(padapter->mlmepriv);
+
+ int len = 0;
+ len += snprintf(page + len, count - len, "ht_option=%d\n", pmlmepriv->htpriv.ht_option);
+ *eof = 1;
+ return len;
+}
+
+int proc_get_rf_info(char *page, char **start,
+ off_t offset, int count,
+ int *eof, void *data)
+{
+ struct net_device *dev = data;
+ struct adapter *padapter = (struct adapter *)rtw_netdev_priv(dev);
+ struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv;
+ int len = 0;
+
+ len += snprintf(page + len, count - len, "cur_ch=%d, cur_bw=%d, cur_ch_offset=%d\n",
+ pmlmeext->cur_channel, pmlmeext->cur_bwmode, pmlmeext->cur_ch_offset);
+ *eof = 1;
+ return len;
+}
+
+int proc_get_ap_info(char *page, char **start,
+ off_t offset, int count,
+ int *eof, void *data)
+{
+ struct sta_info *psta;
+ struct net_device *dev = data;
+ struct adapter *padapter = (struct adapter *)rtw_netdev_priv(dev);
+ struct mlme_priv *pmlmepriv = &(padapter->mlmepriv);
+ struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv;
+ struct wlan_network *cur_network = &(pmlmepriv->cur_network);
+ struct sta_priv *pstapriv = &padapter->stapriv;
+ int len = 0;
+
+ psta = rtw_get_stainfo(pstapriv, cur_network->network.MacAddress);
+ if (psta) {
+ int i;
+ struct recv_reorder_ctrl *preorder_ctrl;
+
+ len += snprintf(page + len, count - len, "SSID=%s\n", cur_network->network.Ssid.Ssid);
+ len += snprintf(page + len, count - len, "sta's macaddr:%pM\n", psta->hwaddr);
+ len += snprintf(page + len, count - len, "cur_channel=%d, cur_bwmode=%d, cur_ch_offset=%d\n", pmlmeext->cur_channel, pmlmeext->cur_bwmode, pmlmeext->cur_ch_offset);
+ len += snprintf(page + len, count - len, "rtsen=%d, cts2slef=%d\n", psta->rtsen, psta->cts2self);
+ len += snprintf(page + len, count - len, "state=0x%x, aid=%d, macid=%d, raid=%d\n", psta->state, psta->aid, psta->mac_id, psta->raid);
+ len += snprintf(page + len, count - len, "qos_en=%d, ht_en=%d, init_rate=%d\n", psta->qos_option, psta->htpriv.ht_option, psta->init_rate);
+ len += snprintf(page + len, count - len, "bwmode=%d, ch_offset=%d, sgi=%d\n", psta->htpriv.bwmode, psta->htpriv.ch_offset, psta->htpriv.sgi);
+ len += snprintf(page + len, count - len, "ampdu_enable = %d\n", psta->htpriv.ampdu_enable);
+ len += snprintf(page + len, count - len, "agg_enable_bitmap=%x, candidate_tid_bitmap=%x\n", psta->htpriv.agg_enable_bitmap, psta->htpriv.candidate_tid_bitmap);
+
+ for (i = 0; i < 16; i++) {
+ preorder_ctrl = &psta->recvreorder_ctrl[i];
+ if (preorder_ctrl->enable)
+ len += snprintf(page + len, count - len, "tid=%d, indicate_seq=%d\n", i, preorder_ctrl->indicate_seq);
+ }
+ } else {
+ len += snprintf(page + len, count - len, "can't get sta's macaddr, cur_network's macaddr: %pM\n", cur_network->network.MacAddress);
+ }
+
+ *eof = 1;
+ return len;
+}
+
+int proc_get_adapter_state(char *page, char **start,
+ off_t offset, int count,
+ int *eof, void *data)
+{
+ struct net_device *dev = data;
+ struct adapter *padapter = (struct adapter *)rtw_netdev_priv(dev);
+ int len = 0;
+
+ len += snprintf(page + len, count - len, "bSurpriseRemoved=%d, bDriverStopped=%d\n",
+ padapter->bSurpriseRemoved, padapter->bDriverStopped);
+
+ *eof = 1;
+ return len;
+}
+
+int proc_get_trx_info(char *page, char **start,
+ off_t offset, int count,
+ int *eof, void *data)
+{
+ struct net_device *dev = data;
+ struct adapter *padapter = (struct adapter *)rtw_netdev_priv(dev);
+ struct xmit_priv *pxmitpriv = &padapter->xmitpriv;
+ struct recv_priv *precvpriv = &padapter->recvpriv;
+ int len = 0;
+
+ len += snprintf(page + len, count - len, "free_xmitbuf_cnt=%d, free_xmitframe_cnt=%d, free_ext_xmitbuf_cnt=%d, free_recvframe_cnt=%d\n",
+ pxmitpriv->free_xmitbuf_cnt, pxmitpriv->free_xmitframe_cnt, pxmitpriv->free_xmit_extbuf_cnt, precvpriv->free_recvframe_cnt);
+ len += snprintf(page + len, count - len, "rx_urb_pending_cn=%d\n", precvpriv->rx_pending_cnt);
+
+ *eof = 1;
+ return len;
+}
+
+int proc_get_mac_reg_dump1(char *page, char **start,
+ off_t offset, int count,
+ int *eof, void *data)
+{
+ struct net_device *dev = data;
+ struct adapter *padapter = (struct adapter *)rtw_netdev_priv(dev);
+ int len = 0;
+ int i, j = 1;
+
+ len += snprintf(page + len, count - len, "\n======= MAC REG =======\n");
+
+ for (i = 0x0; i < 0x300; i += 4) {
+ if (j%4 == 1)
+ len += snprintf(page + len, count - len, "0x%02x", i);
+ len += snprintf(page + len, count - len, " 0x%08x ", usb_read32(padapter, i));
+ if ((j++)%4 == 0)
+ len += snprintf(page + len, count - len, "\n");
+ }
+
+ *eof = 1;
+ return len;
+}
+
+int proc_get_mac_reg_dump2(char *page, char **start,
+ off_t offset, int count,
+ int *eof, void *data)
+{
+ struct net_device *dev = data;
+ struct adapter *padapter = (struct adapter *)rtw_netdev_priv(dev);
+ int len = 0;
+ int i, j = 1;
+
+ len += snprintf(page + len, count - len, "\n======= MAC REG =======\n");
+ memset(page, 0, count);
+ for (i = 0x300; i < 0x600; i += 4) {
+ if (j%4 == 1)
+ len += snprintf(page + len, count - len, "0x%02x", i);
+ len += snprintf(page + len, count - len, " 0x%08x ", usb_read32(padapter, i));
+ if ((j++)%4 == 0)
+ len += snprintf(page + len, count - len, "\n");
+ }
+
+ *eof = 1;
+ return len;
+}
+
+int proc_get_mac_reg_dump3(char *page, char **start,
+ off_t offset, int count,
+ int *eof, void *data)
+{
+ struct net_device *dev = data;
+ struct adapter *padapter = (struct adapter *)rtw_netdev_priv(dev);
+ int len = 0;
+ int i, j = 1;
+
+ len += snprintf(page + len, count - len, "\n======= MAC REG =======\n");
+
+ for (i = 0x600; i < 0x800; i += 4) {
+ if (j%4 == 1)
+ len += snprintf(page + len, count - len, "0x%02x", i);
+ len += snprintf(page + len, count - len, " 0x%08x ", usb_read32(padapter, i));
+ if ((j++)%4 == 0)
+ len += snprintf(page + len, count - len, "\n");
+ }
+
+ *eof = 1;
+ return len;
+}
+
+int proc_get_bb_reg_dump1(char *page, char **start,
+ off_t offset, int count,
+ int *eof, void *data)
+{
+ struct net_device *dev = data;
+ struct adapter *padapter = (struct adapter *)rtw_netdev_priv(dev);
+ int len = 0;
+ int i, j = 1;
+
+ len += snprintf(page + len, count - len, "\n======= BB REG =======\n");
+ for (i = 0x800; i < 0xB00; i += 4) {
+ if (j%4 == 1)
+ len += snprintf(page + len, count - len, "0x%02x", i);
+ len += snprintf(page + len, count - len, " 0x%08x ", usb_read32(padapter, i));
+ if ((j++)%4 == 0)
+ len += snprintf(page + len, count - len, "\n");
+ }
+ *eof = 1;
+ return len;
+}
+
+int proc_get_bb_reg_dump2(char *page, char **start,
+ off_t offset, int count,
+ int *eof, void *data)
+{
+ struct net_device *dev = data;
+ struct adapter *padapter = (struct adapter *)rtw_netdev_priv(dev);
+ int len = 0;
+ int i, j = 1;
+
+ len += snprintf(page + len, count - len, "\n======= BB REG =======\n");
+ for (i = 0xB00; i < 0xE00; i += 4) {
+ if (j%4 == 1)
+ len += snprintf(page + len, count - len, "0x%02x", i);
+ len += snprintf(page + len, count - len, " 0x%08x ", usb_read32(padapter, i));
+ if ((j++)%4 == 0)
+ len += snprintf(page + len, count - len, "\n");
+ }
+ *eof = 1;
+ return len;
+}
+
+int proc_get_bb_reg_dump3(char *page, char **start,
+ off_t offset, int count,
+ int *eof, void *data)
+{
+ struct net_device *dev = data;
+ struct adapter *padapter = (struct adapter *)rtw_netdev_priv(dev);
+ int len = 0;
+ int i, j = 1;
+
+ len += snprintf(page + len, count - len, "\n======= BB REG =======\n");
+ for (i = 0xE00; i < 0x1000; i += 4) {
+ if (j%4 == 1)
+ len += snprintf(page + len, count - len, "0x%02x", i);
+ len += snprintf(page + len, count - len, " 0x%08x ", usb_read32(padapter, i));
+ if ((j++)%4 == 0)
+ len += snprintf(page + len, count - len, "\n");
+ }
+ *eof = 1;
+ return len;
+}
+
+int proc_get_rf_reg_dump1(char *page, char **start,
+ off_t offset, int count,
+ int *eof, void *data)
+{
+ struct net_device *dev = data;
+ struct adapter *padapter = (struct adapter *)rtw_netdev_priv(dev);
+ int len = 0;
+ int i, j = 1, path;
+ u32 value;
+
+ len += snprintf(page + len, count - len, "\n======= RF REG =======\n");
+ path = 1;
+ len += snprintf(page + len, count - len, "\nRF_Path(%x)\n", path);
+ for (i = 0; i < 0xC0; i++) {
+ value = rtw_hal_read_rfreg(padapter, path, i, 0xffffffff);
+ if (j%4 == 1)
+ len += snprintf(page + len, count - len, "0x%02x ", i);
+ len += snprintf(page + len, count - len, " 0x%08x ", value);
+ if ((j++)%4 == 0)
+ len += snprintf(page + len, count - len, "\n");
+ }
+ *eof = 1;
+ return len;
+}
+
+int proc_get_rf_reg_dump2(char *page, char **start,
+ off_t offset, int count,
+ int *eof, void *data)
+{
+ struct net_device *dev = data;
+ struct adapter *padapter = (struct adapter *)rtw_netdev_priv(dev);
+ int len = 0;
+ int i, j = 1, path;
+ u32 value;
+
+ len += snprintf(page + len, count - len, "\n======= RF REG =======\n");
+ path = 1;
+ len += snprintf(page + len, count - len, "\nRF_Path(%x)\n", path);
+ for (i = 0xC0; i < 0x100; i++) {
+ value = rtw_hal_read_rfreg(padapter, path, i, 0xffffffff);
+ if (j%4 == 1)
+ len += snprintf(page + len, count - len, "0x%02x ", i);
+ len += snprintf(page + len, count - len, " 0x%08x ", value);
+ if ((j++)%4 == 0)
+ len += snprintf(page + len, count - len, "\n");
+ }
+ *eof = 1;
+ return len;
+}
+
+int proc_get_rf_reg_dump3(char *page, char **start,
+ off_t offset, int count,
+ int *eof, void *data)
+{
+ struct net_device *dev = data;
+ struct adapter *padapter = (struct adapter *)rtw_netdev_priv(dev);
+ int len = 0;
+ int i, j = 1, path;
+ u32 value;
+
+ len += snprintf(page + len, count - len, "\n======= RF REG =======\n");
+ path = 2;
+ len += snprintf(page + len, count - len, "\nRF_Path(%x)\n", path);
+ for (i = 0; i < 0xC0; i++) {
+ value = rtw_hal_read_rfreg(padapter, path, i, 0xffffffff);
+ if (j%4 == 1)
+ len += snprintf(page + len, count - len, "0x%02x ", i);
+ len += snprintf(page + len, count - len, " 0x%08x ", value);
+ if ((j++)%4 == 0)
+ len += snprintf(page + len, count - len, "\n");
+ }
+
+ *eof = 1;
+ return len;
+}
+
+
+int proc_get_rf_reg_dump4(char *page, char **start,
+ off_t offset, int count,
+ int *eof, void *data)
+{
+ struct net_device *dev = data;
+ struct adapter *padapter = (struct adapter *)rtw_netdev_priv(dev);
+ int len = 0;
+ int i, j = 1, path;
+ u32 value;
+
+ len += snprintf(page + len, count - len, "\n======= RF REG =======\n");
+ path = 2;
+ len += snprintf(page + len, count - len, "\nRF_Path(%x)\n", path);
+ for (i = 0xC0; i < 0x100; i++) {
+ value = rtw_hal_read_rfreg(padapter, path, i, 0xffffffff);
+ if (j%4 == 1)
+ len += snprintf(page + len, count - len, "0x%02x ", i);
+ len += snprintf(page + len, count - len, " 0x%08x ", value);
+ if ((j++)%4 == 0)
+ len += snprintf(page + len, count - len, "\n");
+ }
+ *eof = 1;
+ return len;
+}
+
+
+
+int proc_get_rx_signal(char *page, char **start,
+ off_t offset, int count,
+ int *eof, void *data)
+{
+ struct net_device *dev = data;
+ struct adapter *padapter = (struct adapter *)rtw_netdev_priv(dev);
+ int len = 0;
+
+ len = snprintf(page + len, count,
+ "rssi:%d\n"
+ "rxpwdb:%d\n"
+ "signal_strength:%u\n"
+ "signal_qual:%u\n"
+ "noise:%u\n",
+ padapter->recvpriv.rssi,
+ padapter->recvpriv.rxpwdb,
+ padapter->recvpriv.signal_strength,
+ padapter->recvpriv.signal_qual,
+ padapter->recvpriv.noise
+ );
+
+ *eof = 1;
+ return len;
+}
+
+int proc_set_rx_signal(struct file *file, const char __user *buffer,
+ unsigned long count, void *data)
+{
+ struct net_device *dev = data;
+ struct adapter *padapter = (struct adapter *)rtw_netdev_priv(dev);
+ char tmp[32];
+ u32 is_signal_dbg;
+ s32 signal_strength;
+
+ if (count < 1)
+ return -EFAULT;
+
+ if (buffer && !copy_from_user(tmp, buffer, sizeof(tmp))) {
+ int num = sscanf(tmp, "%u %u", &is_signal_dbg, &signal_strength);
+ is_signal_dbg = is_signal_dbg == 0 ? 0 : 1;
+ if (is_signal_dbg && num != 2)
+ return count;
+
+ signal_strength = signal_strength > 100 ? 100 : signal_strength;
+ signal_strength = signal_strength < 0 ? 0 : signal_strength;
+
+ padapter->recvpriv.is_signal_dbg = is_signal_dbg;
+ padapter->recvpriv.signal_strength_dbg = signal_strength;
+
+ if (is_signal_dbg)
+ DBG_88E("set %s %u\n", "DBG_SIGNAL_STRENGTH", signal_strength);
+ else
+ DBG_88E("set %s\n", "HW_SIGNAL_STRENGTH");
+ }
+ return count;
+}
+
+int proc_get_ht_enable(char *page, char **start,
+ off_t offset, int count,
+ int *eof, void *data)
+{
+ struct net_device *dev = data;
+ struct adapter *padapter = (struct adapter *)rtw_netdev_priv(dev);
+ struct registry_priv *pregpriv = &padapter->registrypriv;
+ int len = 0;
+
+ if (pregpriv)
+ len += snprintf(page + len, count - len,
+ "%d\n",
+ pregpriv->ht_enable
+ );
+ *eof = 1;
+ return len;
+}
+
+int proc_set_ht_enable(struct file *file, const char __user *buffer,
+ unsigned long count, void *data)
+{
+ struct net_device *dev = data;
+ struct adapter *padapter = (struct adapter *)rtw_netdev_priv(dev);
+ struct registry_priv *pregpriv = &padapter->registrypriv;
+ char tmp[32];
+ s32 mode = 0;
+
+ if (count < 1)
+ return -EFAULT;
+
+ if (buffer && !copy_from_user(tmp, buffer, sizeof(tmp))) {
+ if (pregpriv) {
+ pregpriv->ht_enable = mode;
+ pr_info("ht_enable=%d\n", pregpriv->ht_enable);
+ }
+ }
+
+ return count;
+}
+
+int proc_get_cbw40_enable(char *page, char **start,
+ off_t offset, int count,
+ int *eof, void *data)
+{
+ struct net_device *dev = data;
+ struct adapter *padapter = (struct adapter *)rtw_netdev_priv(dev);
+ struct registry_priv *pregpriv = &padapter->registrypriv;
+
+ int len = 0;
+
+ if (pregpriv)
+ len += snprintf(page + len, count - len,
+ "%d\n",
+ pregpriv->cbw40_enable
+ );
+
+ *eof = 1;
+ return len;
+}
+
+int proc_set_cbw40_enable(struct file *file, const char __user *buffer,
+ unsigned long count, void *data)
+{
+ struct net_device *dev = data;
+ struct adapter *padapter = (struct adapter *)rtw_netdev_priv(dev);
+ struct registry_priv *pregpriv = &padapter->registrypriv;
+ char tmp[32];
+ s32 mode = 0;
+
+ if (count < 1)
+ return -EFAULT;
+
+ if (buffer && !copy_from_user(tmp, buffer, sizeof(tmp))) {
+ if (pregpriv) {
+ pregpriv->cbw40_enable = mode;
+ pr_info("cbw40_enable=%d\n", mode);
+ }
+ }
+ return count;
+}
+
+int proc_get_ampdu_enable(char *page, char **start,
+ off_t offset, int count,
+ int *eof, void *data)
+{
+ struct net_device *dev = data;
+ struct adapter *padapter = (struct adapter *)rtw_netdev_priv(dev);
+ struct registry_priv *pregpriv = &padapter->registrypriv;
+
+ int len = 0;
+
+ if (pregpriv)
+ len += snprintf(page + len, count - len,
+ "%d\n",
+ pregpriv->ampdu_enable
+ );
+
+ *eof = 1;
+ return len;
+}
+
+int proc_set_ampdu_enable(struct file *file, const char __user *buffer,
+ unsigned long count, void *data)
+{
+ struct net_device *dev = data;
+ struct adapter *padapter = (struct adapter *)rtw_netdev_priv(dev);
+ struct registry_priv *pregpriv = &padapter->registrypriv;
+ char tmp[32];
+ s32 mode = 0;
+
+ if (count < 1)
+ return -EFAULT;
+
+ if (buffer && !copy_from_user(tmp, buffer, sizeof(tmp))) {
+ if (pregpriv) {
+ pregpriv->ampdu_enable = mode;
+ pr_info("ampdu_enable=%d\n", mode);
+ }
+ }
+ return count;
+}
+
+int proc_get_two_path_rssi(char *page, char **start,
+ off_t offset, int count,
+ int *eof, void *data)
+{
+ struct net_device *dev = data;
+ struct adapter *padapter = (struct adapter *)rtw_netdev_priv(dev);
+
+ int len = 0;
+
+ if (padapter)
+ len += snprintf(page + len, count - len,
+ "%d %d\n",
+ padapter->recvpriv.RxRssi[0],
+ padapter->recvpriv.RxRssi[1]
+ );
+
+ *eof = 1;
+ return len;
+}
+
+int proc_get_rx_stbc(char *page, char **start,
+ off_t offset, int count,
+ int *eof, void *data)
+{
+ struct net_device *dev = data;
+ struct adapter *padapter = (struct adapter *)rtw_netdev_priv(dev);
+ struct registry_priv *pregpriv = &padapter->registrypriv;
+
+ int len = 0;
+
+ if (pregpriv)
+ len += snprintf(page + len, count - len,
+ "%d\n",
+ pregpriv->rx_stbc
+ );
+
+ *eof = 1;
+ return len;
+}
+
+int proc_set_rx_stbc(struct file *file, const char __user *buffer,
+ unsigned long count, void *data)
+{
+ struct net_device *dev = data;
+ struct adapter *padapter = (struct adapter *)rtw_netdev_priv(dev);
+ struct registry_priv *pregpriv = &padapter->registrypriv;
+ char tmp[32];
+ u32 mode = 0;
+
+ if (count < 1)
+ return -EFAULT;
+
+ if (buffer && !copy_from_user(tmp, buffer, sizeof(tmp))) {
+ if (pregpriv) {
+ pregpriv->rx_stbc = mode;
+ netdev_info(dev, "rx_stbc=%d\n", mode);
+ }
+ }
+ return count;
+}
+
+int proc_get_rssi_disp(char *page, char **start,
+ off_t offset, int count,
+ int *eof, void *data)
+{
+ *eof = 1;
+ return 0;
+}
+
+int proc_set_rssi_disp(struct file *file, const char __user *buffer,
+ unsigned long count, void *data)
+{
+ struct net_device *dev = data;
+ struct adapter *padapter = (struct adapter *)rtw_netdev_priv(dev);
+ char tmp[32];
+ u32 enable = 0;
+
+ if (count < 1) {
+ DBG_88E("argument size is less than 1\n");
+ return -EFAULT;
+ }
+
+ if (buffer && !copy_from_user(tmp, buffer, sizeof(tmp))) {
+ int num = sscanf(tmp, "%x", &enable);
+
+ if (num != 1) {
+ DBG_88E("invalid set_rssi_disp parameter!\n");
+ return count;
+ }
+
+ if (enable) {
+ DBG_88E("Turn On Rx RSSI Display Function\n");
+ padapter->bRxRSSIDisplay = enable;
+ } else {
+ DBG_88E("Turn Off Rx RSSI Display Function\n");
+ padapter->bRxRSSIDisplay = 0;
+ }
+ }
+ return count;
+}
+
+#ifdef CONFIG_88EU_AP_MODE
+
+int proc_get_all_sta_info(char *page, char **start,
+ off_t offset, int count,
+ int *eof, void *data)
+{
+ struct sta_info *psta;
+ struct net_device *dev = data;
+ struct adapter *padapter = (struct adapter *)rtw_netdev_priv(dev);
+ struct sta_priv *pstapriv = &padapter->stapriv;
+ int i, j;
+ struct list_head *plist, *phead;
+ struct recv_reorder_ctrl *preorder_ctrl;
+ int len = 0;
+
+
+ len += snprintf(page + len, count - len, "sta_dz_bitmap=0x%x, tim_bitmap=0x%x\n", pstapriv->sta_dz_bitmap, pstapriv->tim_bitmap);
+
+ spin_lock_bh(&pstapriv->sta_hash_lock);
+
+ for (i = 0; i < NUM_STA; i++) {
+ phead = &(pstapriv->sta_hash[i]);
+ plist = phead->next;
+
+ while (phead != plist) {
+ psta = container_of(plist, struct sta_info, hash_list);
+
+ plist = plist->next;
+
+ len += snprintf(page + len, count - len, "sta's macaddr: %pM\n", psta->hwaddr);
+ len += snprintf(page + len, count - len, "rtsen=%d, cts2slef=%d\n", psta->rtsen, psta->cts2self);
+ len += snprintf(page + len, count - len, "state=0x%x, aid=%d, macid=%d, raid=%d\n", psta->state, psta->aid, psta->mac_id, psta->raid);
+ len += snprintf(page + len, count - len, "qos_en=%d, ht_en=%d, init_rate=%d\n", psta->qos_option, psta->htpriv.ht_option, psta->init_rate);
+ len += snprintf(page + len, count - len, "bwmode=%d, ch_offset=%d, sgi=%d\n", psta->htpriv.bwmode, psta->htpriv.ch_offset, psta->htpriv.sgi);
+ len += snprintf(page + len, count - len, "ampdu_enable = %d\n", psta->htpriv.ampdu_enable);
+ len += snprintf(page + len, count - len, "agg_enable_bitmap=%x, candidate_tid_bitmap=%x\n", psta->htpriv.agg_enable_bitmap, psta->htpriv.candidate_tid_bitmap);
+ len += snprintf(page + len, count - len, "sleepq_len=%d\n", psta->sleepq_len);
+ len += snprintf(page + len, count - len, "capability=0x%x\n", psta->capability);
+ len += snprintf(page + len, count - len, "flags=0x%x\n", psta->flags);
+ len += snprintf(page + len, count - len, "wpa_psk=0x%x\n", psta->wpa_psk);
+ len += snprintf(page + len, count - len, "wpa2_group_cipher=0x%x\n", psta->wpa2_group_cipher);
+ len += snprintf(page + len, count - len, "wpa2_pairwise_cipher=0x%x\n", psta->wpa2_pairwise_cipher);
+ len += snprintf(page + len, count - len, "qos_info=0x%x\n", psta->qos_info);
+ len += snprintf(page + len, count - len, "dot118021XPrivacy=0x%x\n", psta->dot118021XPrivacy);
+
+ for (j = 0; j < 16; j++) {
+ preorder_ctrl = &psta->recvreorder_ctrl[j];
+ if (preorder_ctrl->enable)
+ len += snprintf(page + len, count - len, "tid=%d, indicate_seq=%d\n", j, preorder_ctrl->indicate_seq);
+ }
+ }
+ }
+ spin_unlock_bh(&pstapriv->sta_hash_lock);
+
+ *eof = 1;
+ return len;
+}
+#endif
+
+int proc_get_best_channel(char *page, char **start,
+ off_t offset, int count,
+ int *eof, void *data)
+{
+ struct net_device *dev = data;
+ struct adapter *padapter = (struct adapter *)rtw_netdev_priv(dev);
+ struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv;
+ int len = 0;
+ u32 i, best_channel_24G = 1, best_channel_5G = 36, index_24G = 0, index_5G = 0;
+
+ for (i = 0; pmlmeext->channel_set[i].ChannelNum != 0; i++) {
+ if (pmlmeext->channel_set[i].ChannelNum == 1)
+ index_24G = i;
+ if (pmlmeext->channel_set[i].ChannelNum == 36)
+ index_5G = i;
+ }
+
+ for (i = 0; pmlmeext->channel_set[i].ChannelNum != 0; i++) {
+ /* 2.4G */
+ if (pmlmeext->channel_set[i].ChannelNum == 6) {
+ if (pmlmeext->channel_set[i].rx_count < pmlmeext->channel_set[index_24G].rx_count) {
+ index_24G = i;
+ best_channel_24G = pmlmeext->channel_set[i].ChannelNum;
+ }
+ }
+
+ /* 5G */
+ if (pmlmeext->channel_set[i].ChannelNum >= 36 &&
+ pmlmeext->channel_set[i].ChannelNum < 140) {
+ /* Find primary channel */
+ if (((pmlmeext->channel_set[i].ChannelNum - 36) % 8 == 0) &&
+ (pmlmeext->channel_set[i].rx_count < pmlmeext->channel_set[index_5G].rx_count)) {
+ index_5G = i;
+ best_channel_5G = pmlmeext->channel_set[i].ChannelNum;
+ }
+ }
+
+ if (pmlmeext->channel_set[i].ChannelNum >= 149 &&
+ pmlmeext->channel_set[i].ChannelNum < 165) {
+ /* find primary channel */
+ if (((pmlmeext->channel_set[i].ChannelNum - 149) % 8 == 0) &&
+ (pmlmeext->channel_set[i].rx_count < pmlmeext->channel_set[index_5G].rx_count)) {
+ index_5G = i;
+ best_channel_5G = pmlmeext->channel_set[i].ChannelNum;
+ }
+ }
+ /* debug */
+ len += snprintf(page + len, count - len, "The rx cnt of channel %3d = %d\n",
+ pmlmeext->channel_set[i].ChannelNum, pmlmeext->channel_set[i].rx_count);
+ }
+
+ len += snprintf(page + len, count - len, "best_channel_5G = %d\n", best_channel_5G);
+ len += snprintf(page + len, count - len, "best_channel_24G = %d\n", best_channel_24G);
+
+ *eof = 1;
+ return len;
+}
diff --git a/drivers/staging/rtl8188eu/core/rtw_efuse.c b/drivers/staging/rtl8188eu/core/rtw_efuse.c
new file mode 100644
index 000000000..b66746160
--- /dev/null
+++ b/drivers/staging/rtl8188eu/core/rtw_efuse.c
@@ -0,0 +1,1014 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2011 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ *
+ ******************************************************************************/
+#define _RTW_EFUSE_C_
+
+#include <osdep_service.h>
+#include <drv_types.h>
+#include <rtw_efuse.h>
+#include <usb_ops_linux.h>
+#include <rtl8188e_hal.h>
+#include <rtw_iol.h>
+
+#define REG_EFUSE_CTRL 0x0030
+#define EFUSE_CTRL REG_EFUSE_CTRL /* E-Fuse Control. */
+
+enum{
+ VOLTAGE_V25 = 0x03,
+ LDOE25_SHIFT = 28 ,
+ };
+
+/*
+ * Function: Efuse_PowerSwitch
+ *
+ * Overview: When we want to enable write operation, we should change to
+ * pwr on state. When we stop write, we should switch to 500k mode
+ * and disable LDO 2.5V.
+ */
+
+void Efuse_PowerSwitch(
+ struct adapter *pAdapter,
+ u8 bWrite,
+ u8 PwrState)
+{
+ u8 tempval;
+ u16 tmpV16;
+
+ if (PwrState) {
+ usb_write8(pAdapter, REG_EFUSE_ACCESS, EFUSE_ACCESS_ON);
+
+ /* 1.2V Power: From VDDON with Power Cut(0x0000h[15]), defualt valid */
+ tmpV16 = usb_read16(pAdapter, REG_SYS_ISO_CTRL);
+ if (!(tmpV16 & PWC_EV12V)) {
+ tmpV16 |= PWC_EV12V;
+ usb_write16(pAdapter, REG_SYS_ISO_CTRL, tmpV16);
+ }
+ /* Reset: 0x0000h[28], default valid */
+ tmpV16 = usb_read16(pAdapter, REG_SYS_FUNC_EN);
+ if (!(tmpV16 & FEN_ELDR)) {
+ tmpV16 |= FEN_ELDR;
+ usb_write16(pAdapter, REG_SYS_FUNC_EN, tmpV16);
+ }
+
+ /* Clock: Gated(0x0008h[5]) 8M(0x0008h[1]) clock from ANA, default valid */
+ tmpV16 = usb_read16(pAdapter, REG_SYS_CLKR);
+ if ((!(tmpV16 & LOADER_CLK_EN)) || (!(tmpV16 & ANA8M))) {
+ tmpV16 |= (LOADER_CLK_EN | ANA8M);
+ usb_write16(pAdapter, REG_SYS_CLKR, tmpV16);
+ }
+
+ if (bWrite) {
+ /* Enable LDO 2.5V before read/write action */
+ tempval = usb_read8(pAdapter, EFUSE_TEST+3);
+ tempval &= 0x0F;
+ tempval |= (VOLTAGE_V25 << 4);
+ usb_write8(pAdapter, EFUSE_TEST+3, (tempval | 0x80));
+ }
+ } else {
+ usb_write8(pAdapter, REG_EFUSE_ACCESS, EFUSE_ACCESS_OFF);
+
+ if (bWrite) {
+ /* Disable LDO 2.5V after read/write action */
+ tempval = usb_read8(pAdapter, EFUSE_TEST+3);
+ usb_write8(pAdapter, EFUSE_TEST+3, (tempval & 0x7F));
+ }
+ }
+}
+
+static void
+efuse_phymap_to_logical(u8 *phymap, u16 _offset, u16 _size_byte, u8 *pbuf)
+{
+ u8 *efuseTbl = NULL;
+ u8 rtemp8;
+ u16 eFuse_Addr = 0;
+ u8 offset, wren;
+ u16 i, j;
+ u16 **eFuseWord = NULL;
+ u16 efuse_utilized = 0;
+ u8 u1temp = 0;
+
+ efuseTbl = kzalloc(EFUSE_MAP_LEN_88E, GFP_KERNEL);
+ if (efuseTbl == NULL) {
+ DBG_88E("%s: alloc efuseTbl fail!\n", __func__);
+ return;
+ }
+
+ eFuseWord = (u16 **)rtw_malloc2d(EFUSE_MAX_SECTION_88E, EFUSE_MAX_WORD_UNIT, sizeof(u16));
+ if (eFuseWord == NULL) {
+ DBG_88E("%s: alloc eFuseWord fail!\n", __func__);
+ goto eFuseWord_failed;
+ }
+
+ /* 0. Refresh efuse init map as all oxFF. */
+ for (i = 0; i < EFUSE_MAX_SECTION_88E; i++)
+ for (j = 0; j < EFUSE_MAX_WORD_UNIT; j++)
+ eFuseWord[i][j] = 0xFFFF;
+
+ /* */
+ /* 1. Read the first byte to check if efuse is empty!!! */
+ /* */
+ /* */
+ rtemp8 = *(phymap+eFuse_Addr);
+ if (rtemp8 != 0xFF) {
+ efuse_utilized++;
+ eFuse_Addr++;
+ } else {
+ DBG_88E("EFUSE is empty efuse_Addr-%d efuse_data =%x\n", eFuse_Addr, rtemp8);
+ goto exit;
+ }
+
+ /* */
+ /* 2. Read real efuse content. Filter PG header and every section data. */
+ /* */
+ while ((rtemp8 != 0xFF) && (eFuse_Addr < EFUSE_REAL_CONTENT_LEN_88E)) {
+ /* Check PG header for section num. */
+ if ((rtemp8 & 0x1F) == 0x0F) { /* extended header */
+ u1temp = (rtemp8 & 0xE0) >> 5;
+ rtemp8 = *(phymap+eFuse_Addr);
+ if ((rtemp8 & 0x0F) == 0x0F) {
+ eFuse_Addr++;
+ rtemp8 = *(phymap+eFuse_Addr);
+
+ if (rtemp8 != 0xFF && (eFuse_Addr < EFUSE_REAL_CONTENT_LEN_88E))
+ eFuse_Addr++;
+ continue;
+ } else {
+ offset = ((rtemp8 & 0xF0) >> 1) | u1temp;
+ wren = rtemp8 & 0x0F;
+ eFuse_Addr++;
+ }
+ } else {
+ offset = (rtemp8 >> 4) & 0x0f;
+ wren = rtemp8 & 0x0f;
+ }
+
+ if (offset < EFUSE_MAX_SECTION_88E) {
+ /* Get word enable value from PG header */
+ for (i = 0; i < EFUSE_MAX_WORD_UNIT; i++) {
+ /* Check word enable condition in the section */
+ if (!(wren & 0x01)) {
+ rtemp8 = *(phymap+eFuse_Addr);
+ eFuse_Addr++;
+ efuse_utilized++;
+ eFuseWord[offset][i] = (rtemp8 & 0xff);
+ if (eFuse_Addr >= EFUSE_REAL_CONTENT_LEN_88E)
+ break;
+ rtemp8 = *(phymap+eFuse_Addr);
+ eFuse_Addr++;
+ efuse_utilized++;
+ eFuseWord[offset][i] |= (((u16)rtemp8 << 8) & 0xff00);
+
+ if (eFuse_Addr >= EFUSE_REAL_CONTENT_LEN_88E)
+ break;
+ }
+ wren >>= 1;
+ }
+ }
+ /* Read next PG header */
+ rtemp8 = *(phymap+eFuse_Addr);
+
+ if (rtemp8 != 0xFF && (eFuse_Addr < EFUSE_REAL_CONTENT_LEN_88E)) {
+ efuse_utilized++;
+ eFuse_Addr++;
+ }
+ }
+
+ /* */
+ /* 3. Collect 16 sections and 4 word unit into Efuse map. */
+ /* */
+ for (i = 0; i < EFUSE_MAX_SECTION_88E; i++) {
+ for (j = 0; j < EFUSE_MAX_WORD_UNIT; j++) {
+ efuseTbl[(i*8)+(j*2)] = (eFuseWord[i][j] & 0xff);
+ efuseTbl[(i*8)+((j*2)+1)] = ((eFuseWord[i][j] >> 8) & 0xff);
+ }
+ }
+
+ /* */
+ /* 4. Copy from Efuse map to output pointer memory!!! */
+ /* */
+ for (i = 0; i < _size_byte; i++)
+ pbuf[i] = efuseTbl[_offset+i];
+
+ /* */
+ /* 5. Calculate Efuse utilization. */
+ /* */
+
+exit:
+ kfree(eFuseWord);
+
+eFuseWord_failed:
+ kfree(efuseTbl);
+}
+
+static void efuse_read_phymap_from_txpktbuf(
+ struct adapter *adapter,
+ int bcnhead, /* beacon head, where FW store len(2-byte) and efuse physical map. */
+ u8 *content, /* buffer to store efuse physical map */
+ u16 *size /* for efuse content: the max byte to read. will update to byte read */
+ )
+{
+ u16 dbg_addr = 0;
+ u32 start = 0, passing_time = 0;
+ u8 reg_0x143 = 0;
+ u32 lo32 = 0, hi32 = 0;
+ u16 len = 0, count = 0;
+ int i = 0;
+ u16 limit = *size;
+
+ u8 *pos = content;
+
+ if (bcnhead < 0) /* if not valid */
+ bcnhead = usb_read8(adapter, REG_TDECTRL+1);
+
+ DBG_88E("%s bcnhead:%d\n", __func__, bcnhead);
+
+ usb_write8(adapter, REG_PKT_BUFF_ACCESS_CTRL, TXPKT_BUF_SELECT);
+
+ dbg_addr = bcnhead*128/8; /* 8-bytes addressing */
+
+ while (1) {
+ usb_write16(adapter, REG_PKTBUF_DBG_ADDR, dbg_addr+i);
+
+ usb_write8(adapter, REG_TXPKTBUF_DBG, 0);
+ start = jiffies;
+ while (!(reg_0x143 = usb_read8(adapter, REG_TXPKTBUF_DBG)) &&
+ (passing_time = rtw_get_passing_time_ms(start)) < 1000) {
+ DBG_88E("%s polling reg_0x143:0x%02x, reg_0x106:0x%02x\n", __func__, reg_0x143, usb_read8(adapter, 0x106));
+ usleep_range(1000, 2000);
+ }
+
+ lo32 = usb_read32(adapter, REG_PKTBUF_DBG_DATA_L);
+ hi32 = usb_read32(adapter, REG_PKTBUF_DBG_DATA_H);
+
+ if (i == 0) {
+ u8 lenc[2];
+ u16 lenbak, aaabak;
+ u16 aaa;
+ lenc[0] = usb_read8(adapter, REG_PKTBUF_DBG_DATA_L);
+ lenc[1] = usb_read8(adapter, REG_PKTBUF_DBG_DATA_L+1);
+
+ aaabak = le16_to_cpup((__le16 *)lenc);
+ lenbak = le16_to_cpu(*((__le16 *)lenc));
+ aaa = le16_to_cpup((__le16 *)&lo32);
+ len = le16_to_cpu(*((__le16 *)&lo32));
+
+ limit = (len-2 < limit) ? len-2 : limit;
+
+ DBG_88E("%s len:%u, lenbak:%u, aaa:%u, aaabak:%u\n", __func__, len, lenbak, aaa, aaabak);
+
+ memcpy(pos, ((u8 *)&lo32)+2, (limit >= count+2) ? 2 : limit-count);
+ count += (limit >= count+2) ? 2 : limit-count;
+ pos = content+count;
+
+ } else {
+ memcpy(pos, ((u8 *)&lo32), (limit >= count+4) ? 4 : limit-count);
+ count += (limit >= count+4) ? 4 : limit-count;
+ pos = content+count;
+ }
+
+ if (limit > count && len-2 > count) {
+ memcpy(pos, (u8 *)&hi32, (limit >= count+4) ? 4 : limit-count);
+ count += (limit >= count+4) ? 4 : limit-count;
+ pos = content+count;
+ }
+
+ if (limit <= count || len-2 <= count)
+ break;
+ i++;
+ }
+ usb_write8(adapter, REG_PKT_BUFF_ACCESS_CTRL, DISABLE_TRXPKT_BUF_ACCESS);
+ DBG_88E("%s read count:%u\n", __func__, count);
+ *size = count;
+}
+
+static s32 iol_read_efuse(struct adapter *padapter, u8 txpktbuf_bndy, u16 offset, u16 size_byte, u8 *logical_map)
+{
+ s32 status = _FAIL;
+ u8 physical_map[512];
+ u16 size = 512;
+
+ usb_write8(padapter, REG_TDECTRL+1, txpktbuf_bndy);
+ memset(physical_map, 0xFF, 512);
+ usb_write8(padapter, REG_PKT_BUFF_ACCESS_CTRL, TXPKT_BUF_SELECT);
+ status = iol_execute(padapter, CMD_READ_EFUSE_MAP);
+ if (status == _SUCCESS)
+ efuse_read_phymap_from_txpktbuf(padapter, txpktbuf_bndy, physical_map, &size);
+ efuse_phymap_to_logical(physical_map, offset, size_byte, logical_map);
+ return status;
+}
+
+void efuse_ReadEFuse(struct adapter *Adapter, u8 efuseType, u16 _offset, u16 _size_byte, u8 *pbuf)
+{
+
+ if (rtw_IOL_applied(Adapter)) {
+ rtw_hal_power_on(Adapter);
+ iol_mode_enable(Adapter, 1);
+ iol_read_efuse(Adapter, 0, _offset, _size_byte, pbuf);
+ iol_mode_enable(Adapter, 0);
+ }
+}
+
+/* Do not support BT */
+void EFUSE_GetEfuseDefinition(struct adapter *pAdapter, u8 efuseType, u8 type, void *pOut)
+{
+ switch (type) {
+ case TYPE_EFUSE_MAX_SECTION:
+ {
+ u8 *pMax_section;
+ pMax_section = pOut;
+ *pMax_section = EFUSE_MAX_SECTION_88E;
+ }
+ break;
+ case TYPE_EFUSE_REAL_CONTENT_LEN:
+ {
+ u16 *pu2Tmp;
+ pu2Tmp = pOut;
+ *pu2Tmp = EFUSE_REAL_CONTENT_LEN_88E;
+ }
+ break;
+ case TYPE_EFUSE_CONTENT_LEN_BANK:
+ {
+ u16 *pu2Tmp;
+ pu2Tmp = pOut;
+ *pu2Tmp = EFUSE_REAL_CONTENT_LEN_88E;
+ }
+ break;
+ case TYPE_AVAILABLE_EFUSE_BYTES_BANK:
+ {
+ u16 *pu2Tmp;
+ pu2Tmp = pOut;
+ *pu2Tmp = (u16)(EFUSE_REAL_CONTENT_LEN_88E-EFUSE_OOB_PROTECT_BYTES_88E);
+ }
+ break;
+ case TYPE_AVAILABLE_EFUSE_BYTES_TOTAL:
+ {
+ u16 *pu2Tmp;
+ pu2Tmp = pOut;
+ *pu2Tmp = (u16)(EFUSE_REAL_CONTENT_LEN_88E-EFUSE_OOB_PROTECT_BYTES_88E);
+ }
+ break;
+ case TYPE_EFUSE_MAP_LEN:
+ {
+ u16 *pu2Tmp;
+ pu2Tmp = pOut;
+ *pu2Tmp = (u16)EFUSE_MAP_LEN_88E;
+ }
+ break;
+ case TYPE_EFUSE_PROTECT_BYTES_BANK:
+ {
+ u8 *pu1Tmp;
+ pu1Tmp = pOut;
+ *pu1Tmp = (u8)(EFUSE_OOB_PROTECT_BYTES_88E);
+ }
+ break;
+ default:
+ {
+ u8 *pu1Tmp;
+ pu1Tmp = pOut;
+ *pu1Tmp = 0;
+ }
+ break;
+ }
+}
+
+u8 Efuse_WordEnableDataWrite(struct adapter *pAdapter, u16 efuse_addr, u8 word_en, u8 *data)
+{
+ u16 tmpaddr = 0;
+ u16 start_addr = efuse_addr;
+ u8 badworden = 0x0F;
+ u8 tmpdata[8];
+
+ memset((void *)tmpdata, 0xff, PGPKT_DATA_SIZE);
+
+ if (!(word_en&BIT0)) {
+ tmpaddr = start_addr;
+ efuse_OneByteWrite(pAdapter, start_addr++, data[0]);
+ efuse_OneByteWrite(pAdapter, start_addr++, data[1]);
+
+ efuse_OneByteRead(pAdapter, tmpaddr, &tmpdata[0]);
+ efuse_OneByteRead(pAdapter, tmpaddr+1, &tmpdata[1]);
+ if ((data[0] != tmpdata[0]) || (data[1] != tmpdata[1]))
+ badworden &= (~BIT0);
+ }
+ if (!(word_en&BIT1)) {
+ tmpaddr = start_addr;
+ efuse_OneByteWrite(pAdapter, start_addr++, data[2]);
+ efuse_OneByteWrite(pAdapter, start_addr++, data[3]);
+
+ efuse_OneByteRead(pAdapter, tmpaddr, &tmpdata[2]);
+ efuse_OneByteRead(pAdapter, tmpaddr+1, &tmpdata[3]);
+ if ((data[2] != tmpdata[2]) || (data[3] != tmpdata[3]))
+ badworden &= (~BIT1);
+ }
+ if (!(word_en&BIT2)) {
+ tmpaddr = start_addr;
+ efuse_OneByteWrite(pAdapter, start_addr++, data[4]);
+ efuse_OneByteWrite(pAdapter, start_addr++, data[5]);
+
+ efuse_OneByteRead(pAdapter, tmpaddr, &tmpdata[4]);
+ efuse_OneByteRead(pAdapter, tmpaddr+1, &tmpdata[5]);
+ if ((data[4] != tmpdata[4]) || (data[5] != tmpdata[5]))
+ badworden &= (~BIT2);
+ }
+ if (!(word_en&BIT3)) {
+ tmpaddr = start_addr;
+ efuse_OneByteWrite(pAdapter, start_addr++, data[6]);
+ efuse_OneByteWrite(pAdapter, start_addr++, data[7]);
+
+ efuse_OneByteRead(pAdapter, tmpaddr, &tmpdata[6]);
+ efuse_OneByteRead(pAdapter, tmpaddr+1, &tmpdata[7]);
+ if ((data[6] != tmpdata[6]) || (data[7] != tmpdata[7]))
+ badworden &= (~BIT3);
+ }
+ return badworden;
+}
+
+static u16 Efuse_GetCurrentSize(struct adapter *pAdapter)
+{
+ int bContinual = true;
+ u16 efuse_addr = 0;
+ u8 hoffset = 0, hworden = 0;
+ u8 efuse_data, word_cnts = 0;
+
+ rtw_hal_get_hwreg(pAdapter, HW_VAR_EFUSE_BYTES, (u8 *)&efuse_addr);
+
+ while (bContinual &&
+ efuse_OneByteRead(pAdapter, efuse_addr, &efuse_data) &&
+ AVAILABLE_EFUSE_ADDR(efuse_addr)) {
+ if (efuse_data != 0xFF) {
+ if ((efuse_data&0x1F) == 0x0F) { /* extended header */
+ hoffset = efuse_data;
+ efuse_addr++;
+ efuse_OneByteRead(pAdapter, efuse_addr, &efuse_data);
+ if ((efuse_data & 0x0F) == 0x0F) {
+ efuse_addr++;
+ continue;
+ } else {
+ hoffset = ((hoffset & 0xE0) >> 5) | ((efuse_data & 0xF0) >> 1);
+ hworden = efuse_data & 0x0F;
+ }
+ } else {
+ hoffset = (efuse_data>>4) & 0x0F;
+ hworden = efuse_data & 0x0F;
+ }
+ word_cnts = Efuse_CalculateWordCnts(hworden);
+ /* read next header */
+ efuse_addr = efuse_addr + (word_cnts*2)+1;
+ } else {
+ bContinual = false;
+ }
+ }
+
+ rtw_hal_set_hwreg(pAdapter, HW_VAR_EFUSE_BYTES, (u8 *)&efuse_addr);
+
+ return efuse_addr;
+}
+
+int Efuse_PgPacketRead(struct adapter *pAdapter, u8 offset, u8 *data)
+{
+ u8 ReadState = PG_STATE_HEADER;
+ int bContinual = true;
+ int bDataEmpty = true;
+ u8 efuse_data, word_cnts = 0;
+ u16 efuse_addr = 0;
+ u8 hoffset = 0, hworden = 0;
+ u8 tmpidx = 0;
+ u8 tmpdata[8];
+ u8 max_section = 0;
+ u8 tmp_header = 0;
+
+ EFUSE_GetEfuseDefinition(pAdapter, EFUSE_WIFI, TYPE_EFUSE_MAX_SECTION, (void *)&max_section);
+
+ if (data == NULL)
+ return false;
+ if (offset > max_section)
+ return false;
+
+ memset((void *)data, 0xff, sizeof(u8)*PGPKT_DATA_SIZE);
+ memset((void *)tmpdata, 0xff, sizeof(u8)*PGPKT_DATA_SIZE);
+
+ /* <Roger_TODO> Efuse has been pre-programmed dummy 5Bytes at the end of Efuse by CP. */
+ /* Skip dummy parts to prevent unexpected data read from Efuse. */
+ /* By pass right now. 2009.02.19. */
+ while (bContinual && AVAILABLE_EFUSE_ADDR(efuse_addr)) {
+ /* Header Read ------------- */
+ if (ReadState & PG_STATE_HEADER) {
+ if (efuse_OneByteRead(pAdapter, efuse_addr, &efuse_data) && (efuse_data != 0xFF)) {
+ if (EXT_HEADER(efuse_data)) {
+ tmp_header = efuse_data;
+ efuse_addr++;
+ efuse_OneByteRead(pAdapter, efuse_addr, &efuse_data);
+ if (!ALL_WORDS_DISABLED(efuse_data)) {
+ hoffset = ((tmp_header & 0xE0) >> 5) | ((efuse_data & 0xF0) >> 1);
+ hworden = efuse_data & 0x0F;
+ } else {
+ DBG_88E("Error, All words disabled\n");
+ efuse_addr++;
+ continue;
+ }
+ } else {
+ hoffset = (efuse_data>>4) & 0x0F;
+ hworden = efuse_data & 0x0F;
+ }
+ word_cnts = Efuse_CalculateWordCnts(hworden);
+ bDataEmpty = true;
+
+ if (hoffset == offset) {
+ for (tmpidx = 0; tmpidx < word_cnts*2; tmpidx++) {
+ if (efuse_OneByteRead(pAdapter, efuse_addr+1+tmpidx, &efuse_data)) {
+ tmpdata[tmpidx] = efuse_data;
+ if (efuse_data != 0xff)
+ bDataEmpty = false;
+ }
+ }
+ if (bDataEmpty == false) {
+ ReadState = PG_STATE_DATA;
+ } else {/* read next header */
+ efuse_addr = efuse_addr + (word_cnts*2)+1;
+ ReadState = PG_STATE_HEADER;
+ }
+ } else {/* read next header */
+ efuse_addr = efuse_addr + (word_cnts*2)+1;
+ ReadState = PG_STATE_HEADER;
+ }
+ } else {
+ bContinual = false;
+ }
+ } else if (ReadState & PG_STATE_DATA) {
+ /* Data section Read ------------- */
+ efuse_WordEnableDataRead(hworden, tmpdata, data);
+ efuse_addr = efuse_addr + (word_cnts*2)+1;
+ ReadState = PG_STATE_HEADER;
+ }
+
+ }
+
+ if ((data[0] == 0xff) && (data[1] == 0xff) && (data[2] == 0xff) && (data[3] == 0xff) &&
+ (data[4] == 0xff) && (data[5] == 0xff) && (data[6] == 0xff) && (data[7] == 0xff))
+ return false;
+ else
+ return true;
+}
+
+static bool hal_EfuseFixHeaderProcess(struct adapter *pAdapter, u8 efuseType, struct pgpkt *pFixPkt, u16 *pAddr)
+{
+ u8 originaldata[8], badworden = 0;
+ u16 efuse_addr = *pAddr;
+ u32 PgWriteSuccess = 0;
+
+ memset((void *)originaldata, 0xff, 8);
+
+ if (Efuse_PgPacketRead(pAdapter, pFixPkt->offset, originaldata)) {
+ /* check if data exist */
+ badworden = Efuse_WordEnableDataWrite(pAdapter, efuse_addr+1, pFixPkt->word_en, originaldata);
+
+ if (badworden != 0xf) { /* write fail */
+ PgWriteSuccess = Efuse_PgPacketWrite(pAdapter, pFixPkt->offset, badworden, originaldata);
+
+ if (!PgWriteSuccess)
+ return false;
+ else
+ efuse_addr = Efuse_GetCurrentSize(pAdapter);
+ } else {
+ efuse_addr = efuse_addr + (pFixPkt->word_cnts*2) + 1;
+ }
+ } else {
+ efuse_addr = efuse_addr + (pFixPkt->word_cnts*2) + 1;
+ }
+ *pAddr = efuse_addr;
+ return true;
+}
+
+static bool hal_EfusePgPacketWrite2ByteHeader(struct adapter *pAdapter, u8 efuseType, u16 *pAddr, struct pgpkt *pTargetPkt)
+{
+ bool bRet = false;
+ u16 efuse_addr = *pAddr, efuse_max_available_len = 0;
+ u8 pg_header = 0, tmp_header = 0, pg_header_temp = 0;
+ u8 repeatcnt = 0;
+
+ EFUSE_GetEfuseDefinition(pAdapter, efuseType, TYPE_AVAILABLE_EFUSE_BYTES_BANK, (void *)&efuse_max_available_len);
+
+ while (efuse_addr < efuse_max_available_len) {
+ pg_header = ((pTargetPkt->offset & 0x07) << 5) | 0x0F;
+ efuse_OneByteWrite(pAdapter, efuse_addr, pg_header);
+ efuse_OneByteRead(pAdapter, efuse_addr, &tmp_header);
+
+ while (tmp_header == 0xFF) {
+ if (repeatcnt++ > EFUSE_REPEAT_THRESHOLD_)
+ return false;
+
+ efuse_OneByteWrite(pAdapter, efuse_addr, pg_header);
+ efuse_OneByteRead(pAdapter, efuse_addr, &tmp_header);
+ }
+
+ /* to write ext_header */
+ if (tmp_header == pg_header) {
+ efuse_addr++;
+ pg_header_temp = pg_header;
+ pg_header = ((pTargetPkt->offset & 0x78) << 1) | pTargetPkt->word_en;
+
+ efuse_OneByteWrite(pAdapter, efuse_addr, pg_header);
+ efuse_OneByteRead(pAdapter, efuse_addr, &tmp_header);
+
+ while (tmp_header == 0xFF) {
+ if (repeatcnt++ > EFUSE_REPEAT_THRESHOLD_)
+ return false;
+
+ efuse_OneByteWrite(pAdapter, efuse_addr, pg_header);
+ efuse_OneByteRead(pAdapter, efuse_addr, &tmp_header);
+ }
+
+ if ((tmp_header & 0x0F) == 0x0F) { /* word_en PG fail */
+ if (repeatcnt++ > EFUSE_REPEAT_THRESHOLD_) {
+ return false;
+ }
+ efuse_addr++;
+ continue;
+ } else if (pg_header != tmp_header) { /* offset PG fail */
+ struct pgpkt fixPkt;
+ fixPkt.offset = ((pg_header_temp & 0xE0) >> 5) | ((tmp_header & 0xF0) >> 1);
+ fixPkt.word_en = tmp_header & 0x0F;
+ fixPkt.word_cnts = Efuse_CalculateWordCnts(fixPkt.word_en);
+ if (!hal_EfuseFixHeaderProcess(pAdapter, efuseType, &fixPkt, &efuse_addr))
+ return false;
+ } else {
+ bRet = true;
+ break;
+ }
+ } else if ((tmp_header & 0x1F) == 0x0F) { /* wrong extended header */
+ efuse_addr += 2;
+ continue;
+ }
+ }
+
+ *pAddr = efuse_addr;
+ return bRet;
+}
+
+static bool hal_EfusePgPacketWrite1ByteHeader(struct adapter *pAdapter, u8 efuseType, u16 *pAddr, struct pgpkt *pTargetPkt)
+{
+ bool bRet = false;
+ u8 pg_header = 0, tmp_header = 0;
+ u16 efuse_addr = *pAddr;
+ u8 repeatcnt = 0;
+
+ pg_header = ((pTargetPkt->offset << 4) & 0xf0) | pTargetPkt->word_en;
+
+ efuse_OneByteWrite(pAdapter, efuse_addr, pg_header);
+ efuse_OneByteRead(pAdapter, efuse_addr, &tmp_header);
+
+ while (tmp_header == 0xFF) {
+ if (repeatcnt++ > EFUSE_REPEAT_THRESHOLD_)
+ return false;
+ efuse_OneByteWrite(pAdapter, efuse_addr, pg_header);
+ efuse_OneByteRead(pAdapter, efuse_addr, &tmp_header);
+ }
+
+ if (pg_header == tmp_header) {
+ bRet = true;
+ } else {
+ struct pgpkt fixPkt;
+ fixPkt.offset = (tmp_header>>4) & 0x0F;
+ fixPkt.word_en = tmp_header & 0x0F;
+ fixPkt.word_cnts = Efuse_CalculateWordCnts(fixPkt.word_en);
+ if (!hal_EfuseFixHeaderProcess(pAdapter, efuseType, &fixPkt, &efuse_addr))
+ return false;
+ }
+
+ *pAddr = efuse_addr;
+ return bRet;
+}
+
+static bool hal_EfusePgPacketWriteData(struct adapter *pAdapter, u8 efuseType, u16 *pAddr, struct pgpkt *pTargetPkt)
+{
+ u16 efuse_addr = *pAddr;
+ u8 badworden = 0;
+ u32 PgWriteSuccess = 0;
+
+ badworden = 0x0f;
+ badworden = Efuse_WordEnableDataWrite(pAdapter, efuse_addr+1, pTargetPkt->word_en, pTargetPkt->data);
+ if (badworden == 0x0F) {
+ /* write ok */
+ return true;
+ }
+ /* reorganize other pg packet */
+ PgWriteSuccess = Efuse_PgPacketWrite(pAdapter, pTargetPkt->offset, badworden, pTargetPkt->data);
+ if (!PgWriteSuccess)
+ return false;
+ else
+ return true;
+}
+
+static bool
+hal_EfusePgPacketWriteHeader(
+ struct adapter *pAdapter,
+ u8 efuseType,
+ u16 *pAddr,
+ struct pgpkt *pTargetPkt)
+{
+ bool bRet = false;
+
+ if (pTargetPkt->offset >= EFUSE_MAX_SECTION_BASE)
+ bRet = hal_EfusePgPacketWrite2ByteHeader(pAdapter, efuseType, pAddr, pTargetPkt);
+ else
+ bRet = hal_EfusePgPacketWrite1ByteHeader(pAdapter, efuseType, pAddr, pTargetPkt);
+
+ return bRet;
+}
+
+static bool wordEnMatched(struct pgpkt *pTargetPkt, struct pgpkt *pCurPkt,
+ u8 *pWden)
+{
+ u8 match_word_en = 0x0F; /* default all words are disabled */
+
+ /* check if the same words are enabled both target and current PG packet */
+ if (((pTargetPkt->word_en & BIT0) == 0) &&
+ ((pCurPkt->word_en & BIT0) == 0))
+ match_word_en &= ~BIT0; /* enable word 0 */
+ if (((pTargetPkt->word_en & BIT1) == 0) &&
+ ((pCurPkt->word_en & BIT1) == 0))
+ match_word_en &= ~BIT1; /* enable word 1 */
+ if (((pTargetPkt->word_en & BIT2) == 0) &&
+ ((pCurPkt->word_en & BIT2) == 0))
+ match_word_en &= ~BIT2; /* enable word 2 */
+ if (((pTargetPkt->word_en & BIT3) == 0) &&
+ ((pCurPkt->word_en & BIT3) == 0))
+ match_word_en &= ~BIT3; /* enable word 3 */
+
+ *pWden = match_word_en;
+
+ if (match_word_en != 0xf)
+ return true;
+ else
+ return false;
+}
+
+static bool hal_EfuseCheckIfDatafollowed(struct adapter *pAdapter, u8 word_cnts, u16 startAddr)
+{
+ bool bRet = false;
+ u8 i, efuse_data;
+
+ for (i = 0; i < (word_cnts*2); i++) {
+ if (efuse_OneByteRead(pAdapter, (startAddr+i), &efuse_data) && (efuse_data != 0xFF))
+ bRet = true;
+ }
+ return bRet;
+}
+
+static bool hal_EfusePartialWriteCheck(struct adapter *pAdapter, u8 efuseType, u16 *pAddr, struct pgpkt *pTargetPkt)
+{
+ bool bRet = false;
+ u8 i, efuse_data = 0, cur_header = 0;
+ u8 matched_wden = 0, badworden = 0;
+ u16 startAddr = 0, efuse_max_available_len = 0, efuse_max = 0;
+ struct pgpkt curPkt;
+
+ EFUSE_GetEfuseDefinition(pAdapter, efuseType, TYPE_AVAILABLE_EFUSE_BYTES_BANK, (void *)&efuse_max_available_len);
+ EFUSE_GetEfuseDefinition(pAdapter, efuseType, TYPE_EFUSE_REAL_CONTENT_LEN, (void *)&efuse_max);
+
+ rtw_hal_get_hwreg(pAdapter, HW_VAR_EFUSE_BYTES, (u8 *)&startAddr);
+ startAddr %= EFUSE_REAL_CONTENT_LEN;
+
+ while (1) {
+ if (startAddr >= efuse_max_available_len) {
+ bRet = false;
+ break;
+ }
+
+ if (efuse_OneByteRead(pAdapter, startAddr, &efuse_data) && (efuse_data != 0xFF)) {
+ if (EXT_HEADER(efuse_data)) {
+ cur_header = efuse_data;
+ startAddr++;
+ efuse_OneByteRead(pAdapter, startAddr, &efuse_data);
+ if (ALL_WORDS_DISABLED(efuse_data)) {
+ bRet = false;
+ break;
+ } else {
+ curPkt.offset = ((cur_header & 0xE0) >> 5) | ((efuse_data & 0xF0) >> 1);
+ curPkt.word_en = efuse_data & 0x0F;
+ }
+ } else {
+ cur_header = efuse_data;
+ curPkt.offset = (cur_header>>4) & 0x0F;
+ curPkt.word_en = cur_header & 0x0F;
+ }
+
+ curPkt.word_cnts = Efuse_CalculateWordCnts(curPkt.word_en);
+ /* if same header is found but no data followed */
+ /* write some part of data followed by the header. */
+ if ((curPkt.offset == pTargetPkt->offset) &&
+ (!hal_EfuseCheckIfDatafollowed(pAdapter, curPkt.word_cnts, startAddr+1)) &&
+ wordEnMatched(pTargetPkt, &curPkt, &matched_wden)) {
+ /* Here to write partial data */
+ badworden = Efuse_WordEnableDataWrite(pAdapter, startAddr+1, matched_wden, pTargetPkt->data);
+ if (badworden != 0x0F) {
+ u32 PgWriteSuccess = 0;
+ /* if write fail on some words, write these bad words again */
+
+ PgWriteSuccess = Efuse_PgPacketWrite(pAdapter, pTargetPkt->offset, badworden, pTargetPkt->data);
+
+ if (!PgWriteSuccess) {
+ bRet = false; /* write fail, return */
+ break;
+ }
+ }
+ /* partial write ok, update the target packet for later use */
+ for (i = 0; i < 4; i++) {
+ if ((matched_wden & (0x1<<i)) == 0) /* this word has been written */
+ pTargetPkt->word_en |= (0x1<<i); /* disable the word */
+ }
+ pTargetPkt->word_cnts = Efuse_CalculateWordCnts(pTargetPkt->word_en);
+ }
+ /* read from next header */
+ startAddr = startAddr + (curPkt.word_cnts*2) + 1;
+ } else {
+ /* not used header, 0xff */
+ *pAddr = startAddr;
+ bRet = true;
+ break;
+ }
+ }
+ return bRet;
+}
+
+static bool
+hal_EfusePgCheckAvailableAddr(
+ struct adapter *pAdapter,
+ u8 efuseType
+ )
+{
+ u16 efuse_max_available_len = 0;
+
+ /* Change to check TYPE_EFUSE_MAP_LEN , because 8188E raw 256, logic map over 256. */
+ EFUSE_GetEfuseDefinition(pAdapter, EFUSE_WIFI, TYPE_EFUSE_MAP_LEN, (void *)&efuse_max_available_len);
+
+ if (Efuse_GetCurrentSize(pAdapter) >= efuse_max_available_len)
+ return false;
+ return true;
+}
+
+static void hal_EfuseConstructPGPkt(u8 offset, u8 word_en, u8 *pData, struct pgpkt *pTargetPkt)
+{
+ memset((void *)pTargetPkt->data, 0xFF, sizeof(u8)*8);
+ pTargetPkt->offset = offset;
+ pTargetPkt->word_en = word_en;
+ efuse_WordEnableDataRead(word_en, pData, pTargetPkt->data);
+ pTargetPkt->word_cnts = Efuse_CalculateWordCnts(pTargetPkt->word_en);
+}
+
+bool Efuse_PgPacketWrite(struct adapter *pAdapter, u8 offset, u8 word_en, u8 *pData)
+{
+ struct pgpkt targetPkt;
+ u16 startAddr = 0;
+ u8 efuseType = EFUSE_WIFI;
+
+ if (!hal_EfusePgCheckAvailableAddr(pAdapter, efuseType))
+ return false;
+
+ hal_EfuseConstructPGPkt(offset, word_en, pData, &targetPkt);
+
+ if (!hal_EfusePartialWriteCheck(pAdapter, efuseType, &startAddr, &targetPkt))
+ return false;
+
+ if (!hal_EfusePgPacketWriteHeader(pAdapter, efuseType, &startAddr, &targetPkt))
+ return false;
+
+ if (!hal_EfusePgPacketWriteData(pAdapter, efuseType, &startAddr, &targetPkt))
+ return false;
+
+ return true;
+}
+
+u8 Efuse_CalculateWordCnts(u8 word_en)
+{
+ u8 word_cnts = 0;
+ if (!(word_en & BIT(0)))
+ word_cnts++; /* 0 : write enable */
+ if (!(word_en & BIT(1)))
+ word_cnts++;
+ if (!(word_en & BIT(2)))
+ word_cnts++;
+ if (!(word_en & BIT(3)))
+ word_cnts++;
+ return word_cnts;
+}
+
+u8 efuse_OneByteRead(struct adapter *pAdapter, u16 addr, u8 *data)
+{
+ u8 tmpidx = 0;
+ u8 result;
+
+ usb_write8(pAdapter, EFUSE_CTRL+1, (u8)(addr & 0xff));
+ usb_write8(pAdapter, EFUSE_CTRL+2, ((u8)((addr>>8) & 0x03)) |
+ (usb_read8(pAdapter, EFUSE_CTRL+2) & 0xFC));
+
+ usb_write8(pAdapter, EFUSE_CTRL+3, 0x72);/* read cmd */
+
+ while (!(0x80 & usb_read8(pAdapter, EFUSE_CTRL+3)) && (tmpidx < 100))
+ tmpidx++;
+ if (tmpidx < 100) {
+ *data = usb_read8(pAdapter, EFUSE_CTRL);
+ result = true;
+ } else {
+ *data = 0xff;
+ result = false;
+ }
+ return result;
+}
+
+u8 efuse_OneByteWrite(struct adapter *pAdapter, u16 addr, u8 data)
+{
+ u8 tmpidx = 0;
+ u8 result;
+
+ usb_write8(pAdapter, EFUSE_CTRL+1, (u8)(addr&0xff));
+ usb_write8(pAdapter, EFUSE_CTRL+2,
+ (usb_read8(pAdapter, EFUSE_CTRL+2) & 0xFC) |
+ (u8)((addr>>8) & 0x03));
+ usb_write8(pAdapter, EFUSE_CTRL, data);/* data */
+
+ usb_write8(pAdapter, EFUSE_CTRL+3, 0xF2);/* write cmd */
+
+ while ((0x80 & usb_read8(pAdapter, EFUSE_CTRL+3)) && (tmpidx < 100))
+ tmpidx++;
+
+ if (tmpidx < 100)
+ result = true;
+ else
+ result = false;
+
+ return result;
+}
+
+/*
+ * Overview: Read allowed word in current efuse section data.
+ */
+void efuse_WordEnableDataRead(u8 word_en, u8 *sourdata, u8 *targetdata)
+{
+ if (!(word_en&BIT(0))) {
+ targetdata[0] = sourdata[0];
+ targetdata[1] = sourdata[1];
+ }
+ if (!(word_en&BIT(1))) {
+ targetdata[2] = sourdata[2];
+ targetdata[3] = sourdata[3];
+ }
+ if (!(word_en&BIT(2))) {
+ targetdata[4] = sourdata[4];
+ targetdata[5] = sourdata[5];
+ }
+ if (!(word_en&BIT(3))) {
+ targetdata[6] = sourdata[6];
+ targetdata[7] = sourdata[7];
+ }
+}
+
+/*
+ * Overview: Read All Efuse content
+ */
+static void Efuse_ReadAllMap(struct adapter *pAdapter, u8 efuseType, u8 *Efuse)
+{
+ u16 mapLen = 0;
+
+ Efuse_PowerSwitch(pAdapter, false, true);
+
+ EFUSE_GetEfuseDefinition(pAdapter, efuseType, TYPE_EFUSE_MAP_LEN, (void *)&mapLen);
+
+ efuse_ReadEFuse(pAdapter, efuseType, 0, mapLen, Efuse);
+
+ Efuse_PowerSwitch(pAdapter, false, false);
+}
+
+/*
+ * Overview: Transfer current EFUSE content to shadow init and modify map.
+ */
+void EFUSE_ShadowMapUpdate(
+ struct adapter *pAdapter,
+ u8 efuseType)
+{
+ struct eeprom_priv *pEEPROM = GET_EEPROM_EFUSE_PRIV(pAdapter);
+ u16 mapLen = 0;
+
+ EFUSE_GetEfuseDefinition(pAdapter, efuseType, TYPE_EFUSE_MAP_LEN, (void *)&mapLen);
+
+ if (pEEPROM->bautoload_fail_flag)
+ memset(pEEPROM->efuse_eeprom_data, 0xFF, mapLen);
+ else
+ Efuse_ReadAllMap(pAdapter, efuseType, pEEPROM->efuse_eeprom_data);
+}
diff --git a/drivers/staging/rtl8188eu/core/rtw_ieee80211.c b/drivers/staging/rtl8188eu/core/rtw_ieee80211.c
new file mode 100644
index 000000000..11b780d6c
--- /dev/null
+++ b/drivers/staging/rtl8188eu/core/rtw_ieee80211.c
@@ -0,0 +1,1395 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2011 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ *
+ ******************************************************************************/
+#define _IEEE80211_C
+
+#include <drv_types.h>
+#include <osdep_intf.h>
+#include <ieee80211.h>
+#include <wifi.h>
+#include <osdep_service.h>
+#include <wlan_bssdef.h>
+
+u8 RTW_WPA_OUI_TYPE[] = { 0x00, 0x50, 0xf2, 1 };
+u8 WPA_AUTH_KEY_MGMT_NONE[] = { 0x00, 0x50, 0xf2, 0 };
+u8 WPA_AUTH_KEY_MGMT_UNSPEC_802_1X[] = { 0x00, 0x50, 0xf2, 1 };
+u8 WPA_AUTH_KEY_MGMT_PSK_OVER_802_1X[] = { 0x00, 0x50, 0xf2, 2 };
+u8 WPA_CIPHER_SUITE_NONE[] = { 0x00, 0x50, 0xf2, 0 };
+u8 WPA_CIPHER_SUITE_WEP40[] = { 0x00, 0x50, 0xf2, 1 };
+u8 WPA_CIPHER_SUITE_TKIP[] = { 0x00, 0x50, 0xf2, 2 };
+u8 WPA_CIPHER_SUITE_WRAP[] = { 0x00, 0x50, 0xf2, 3 };
+u8 WPA_CIPHER_SUITE_CCMP[] = { 0x00, 0x50, 0xf2, 4 };
+u8 WPA_CIPHER_SUITE_WEP104[] = { 0x00, 0x50, 0xf2, 5 };
+
+u16 RSN_VERSION_BSD = 1;
+u8 RSN_AUTH_KEY_MGMT_UNSPEC_802_1X[] = { 0x00, 0x0f, 0xac, 1 };
+u8 RSN_AUTH_KEY_MGMT_PSK_OVER_802_1X[] = { 0x00, 0x0f, 0xac, 2 };
+u8 RSN_CIPHER_SUITE_NONE[] = { 0x00, 0x0f, 0xac, 0 };
+u8 RSN_CIPHER_SUITE_WEP40[] = { 0x00, 0x0f, 0xac, 1 };
+u8 RSN_CIPHER_SUITE_TKIP[] = { 0x00, 0x0f, 0xac, 2 };
+u8 RSN_CIPHER_SUITE_WRAP[] = { 0x00, 0x0f, 0xac, 3 };
+u8 RSN_CIPHER_SUITE_CCMP[] = { 0x00, 0x0f, 0xac, 4 };
+u8 RSN_CIPHER_SUITE_WEP104[] = { 0x00, 0x0f, 0xac, 5 };
+/* */
+/* for adhoc-master to generate ie and provide supported-rate to fw */
+/* */
+
+static u8 WIFI_CCKRATES[] = {
+ (IEEE80211_CCK_RATE_1MB | IEEE80211_BASIC_RATE_MASK),
+ (IEEE80211_CCK_RATE_2MB | IEEE80211_BASIC_RATE_MASK),
+ (IEEE80211_CCK_RATE_5MB | IEEE80211_BASIC_RATE_MASK),
+ (IEEE80211_CCK_RATE_11MB | IEEE80211_BASIC_RATE_MASK)
+ };
+
+static u8 WIFI_OFDMRATES[] = {
+ (IEEE80211_OFDM_RATE_6MB),
+ (IEEE80211_OFDM_RATE_9MB),
+ (IEEE80211_OFDM_RATE_12MB),
+ (IEEE80211_OFDM_RATE_18MB),
+ (IEEE80211_OFDM_RATE_24MB),
+ IEEE80211_OFDM_RATE_36MB,
+ IEEE80211_OFDM_RATE_48MB,
+ IEEE80211_OFDM_RATE_54MB
+ };
+
+
+int rtw_get_bit_value_from_ieee_value(u8 val)
+{
+ unsigned char dot11_rate_table[] = {
+ 2, 4, 11, 22, 12, 18, 24, 36, 48,
+ 72, 96, 108, 0}; /* last element must be zero!! */
+
+ int i = 0;
+ while (dot11_rate_table[i] != 0) {
+ if (dot11_rate_table[i] == val)
+ return BIT(i);
+ i++;
+ }
+ return 0;
+}
+
+uint rtw_is_cckrates_included(u8 *rate)
+{
+ u32 i = 0;
+
+ while (rate[i] != 0) {
+ if ((((rate[i]) & 0x7f) == 2) || (((rate[i]) & 0x7f) == 4) ||
+ (((rate[i]) & 0x7f) == 11) || (((rate[i]) & 0x7f) == 22))
+ return true;
+ i++;
+ }
+ return false;
+}
+
+uint rtw_is_cckratesonly_included(u8 *rate)
+{
+ u32 i = 0;
+
+ while (rate[i] != 0) {
+ if ((((rate[i]) & 0x7f) != 2) && (((rate[i]) & 0x7f) != 4) &&
+ (((rate[i]) & 0x7f) != 11) && (((rate[i]) & 0x7f) != 22))
+ return false;
+ i++;
+ }
+
+ return true;
+}
+
+int rtw_check_network_type(unsigned char *rate, int ratelen, int channel)
+{
+ if (channel > 14) {
+ if ((rtw_is_cckrates_included(rate)) == true)
+ return WIRELESS_INVALID;
+ else
+ return WIRELESS_11A;
+ } else { /* could be pure B, pure G, or B/G */
+ if ((rtw_is_cckratesonly_included(rate)) == true)
+ return WIRELESS_11B;
+ else if ((rtw_is_cckrates_included(rate)) == true)
+ return WIRELESS_11BG;
+ else
+ return WIRELESS_11G;
+ }
+}
+
+u8 *rtw_set_fixed_ie(unsigned char *pbuf, unsigned int len, unsigned char *source,
+ unsigned int *frlen)
+{
+ memcpy((void *)pbuf, (void *)source, len);
+ *frlen = *frlen + len;
+ return pbuf + len;
+}
+
+/* rtw_set_ie will update frame length */
+u8 *rtw_set_ie
+(
+ u8 *pbuf,
+ int index,
+ uint len,
+ u8 *source,
+ uint *frlen /* frame length */
+)
+{
+ *pbuf = (u8)index;
+
+ *(pbuf + 1) = (u8)len;
+
+ if (len > 0)
+ memcpy((void *)(pbuf + 2), (void *)source, len);
+
+ *frlen = *frlen + (len + 2);
+
+ return pbuf + len + 2;
+}
+
+inline u8 *rtw_set_ie_ch_switch(u8 *buf, u32 *buf_len, u8 ch_switch_mode,
+ u8 new_ch, u8 ch_switch_cnt)
+{
+ u8 ie_data[3];
+
+ ie_data[0] = ch_switch_mode;
+ ie_data[1] = new_ch;
+ ie_data[2] = ch_switch_cnt;
+ return rtw_set_ie(buf, WLAN_EID_CHANNEL_SWITCH, 3, ie_data, buf_len);
+}
+
+inline u8 secondary_ch_offset_to_hal_ch_offset(u8 ch_offset)
+{
+ if (ch_offset == SCN)
+ return HAL_PRIME_CHNL_OFFSET_DONT_CARE;
+ else if (ch_offset == SCA)
+ return HAL_PRIME_CHNL_OFFSET_UPPER;
+ else if (ch_offset == SCB)
+ return HAL_PRIME_CHNL_OFFSET_LOWER;
+
+ return HAL_PRIME_CHNL_OFFSET_DONT_CARE;
+}
+
+inline u8 hal_ch_offset_to_secondary_ch_offset(u8 ch_offset)
+{
+ if (ch_offset == HAL_PRIME_CHNL_OFFSET_DONT_CARE)
+ return SCN;
+ else if (ch_offset == HAL_PRIME_CHNL_OFFSET_LOWER)
+ return SCB;
+ else if (ch_offset == HAL_PRIME_CHNL_OFFSET_UPPER)
+ return SCA;
+
+ return SCN;
+}
+
+inline u8 *rtw_set_ie_secondary_ch_offset(u8 *buf, u32 *buf_len, u8 secondary_ch_offset)
+{
+ return rtw_set_ie(buf, WLAN_EID_SECONDARY_CHANNEL_OFFSET, 1, &secondary_ch_offset, buf_len);
+}
+
+inline u8 *rtw_set_ie_mesh_ch_switch_parm(u8 *buf, u32 *buf_len, u8 ttl,
+ u8 flags, u16 reason, u16 precedence)
+{
+ u8 ie_data[6];
+
+ ie_data[0] = ttl;
+ ie_data[1] = flags;
+ *(u16 *)(ie_data+2) = cpu_to_le16(reason);
+ *(u16 *)(ie_data+4) = cpu_to_le16(precedence);
+
+ return rtw_set_ie(buf, 0x118, 6, ie_data, buf_len);
+}
+
+/*----------------------------------------------------------------------------
+index: the information element id index, limit is the limit for search
+-----------------------------------------------------------------------------*/
+u8 *rtw_get_ie(u8 *pbuf, int index, int *len, int limit)
+{
+ int tmp, i;
+ u8 *p;
+ if (limit < 1)
+ return NULL;
+
+ p = pbuf;
+ i = 0;
+ *len = 0;
+ while (1) {
+ if (*p == index) {
+ *len = *(p + 1);
+ return p;
+ } else {
+ tmp = *(p + 1);
+ p += (tmp + 2);
+ i += (tmp + 2);
+ }
+ if (i >= limit)
+ break;
+ }
+ return NULL;
+}
+
+/**
+ * rtw_get_ie_ex - Search specific IE from a series of IEs
+ * @in_ie: Address of IEs to search
+ * @in_len: Length limit from in_ie
+ * @eid: Element ID to match
+ * @oui: OUI to match
+ * @oui_len: OUI length
+ * @ie: If not NULL and the specific IE is found, the IE will be copied to the buf starting from the specific IE
+ * @ielen: If not NULL and the specific IE is found, will set to the length of the entire IE
+ *
+ * Returns: The address of the specific IE found, or NULL
+ */
+u8 *rtw_get_ie_ex(u8 *in_ie, uint in_len, u8 eid, u8 *oui, u8 oui_len, u8 *ie, uint *ielen)
+{
+ uint cnt;
+ u8 *target_ie = NULL;
+
+
+ if (ielen)
+ *ielen = 0;
+
+ if (!in_ie || in_len <= 0)
+ return target_ie;
+
+ cnt = 0;
+
+ while (cnt < in_len) {
+ if (eid == in_ie[cnt] && (!oui || !memcmp(&in_ie[cnt+2], oui, oui_len))) {
+ target_ie = &in_ie[cnt];
+
+ if (ie)
+ memcpy(ie, &in_ie[cnt], in_ie[cnt+1]+2);
+
+ if (ielen)
+ *ielen = in_ie[cnt+1]+2;
+
+ break;
+ } else {
+ cnt += in_ie[cnt+1]+2; /* goto next */
+ }
+ }
+ return target_ie;
+}
+
+/**
+ * rtw_ies_remove_ie - Find matching IEs and remove
+ * @ies: Address of IEs to search
+ * @ies_len: Pointer of length of ies, will update to new length
+ * @offset: The offset to start scarch
+ * @eid: Element ID to match
+ * @oui: OUI to match
+ * @oui_len: OUI length
+ *
+ * Returns: _SUCCESS: ies is updated, _FAIL: not updated
+ */
+int rtw_ies_remove_ie(u8 *ies, uint *ies_len, uint offset, u8 eid, u8 *oui, u8 oui_len)
+{
+ int ret = _FAIL;
+ u8 *target_ie;
+ u32 target_ielen;
+ u8 *start;
+ uint search_len;
+
+ if (!ies || !ies_len || *ies_len <= offset)
+ goto exit;
+
+ start = ies + offset;
+ search_len = *ies_len - offset;
+
+ while (1) {
+ target_ie = rtw_get_ie_ex(start, search_len, eid, oui, oui_len, NULL, &target_ielen);
+ if (target_ie && target_ielen) {
+ u8 buf[MAX_IE_SZ] = {0};
+ u8 *remain_ies = target_ie + target_ielen;
+ uint remain_len = search_len - (remain_ies - start);
+
+ memcpy(buf, remain_ies, remain_len);
+ memcpy(target_ie, buf, remain_len);
+ *ies_len = *ies_len - target_ielen;
+ ret = _SUCCESS;
+
+ start = target_ie;
+ search_len = remain_len;
+ } else {
+ break;
+ }
+ }
+exit:
+ return ret;
+}
+
+void rtw_set_supported_rate(u8 *SupportedRates, uint mode)
+{
+
+ memset(SupportedRates, 0, NDIS_802_11_LENGTH_RATES_EX);
+
+ switch (mode) {
+ case WIRELESS_11B:
+ memcpy(SupportedRates, WIFI_CCKRATES, IEEE80211_CCK_RATE_LEN);
+ break;
+ case WIRELESS_11G:
+ case WIRELESS_11A:
+ case WIRELESS_11_5N:
+ case WIRELESS_11A_5N:/* Todo: no basic rate for ofdm ? */
+ memcpy(SupportedRates, WIFI_OFDMRATES, IEEE80211_NUM_OFDM_RATESLEN);
+ break;
+ case WIRELESS_11BG:
+ case WIRELESS_11G_24N:
+ case WIRELESS_11_24N:
+ case WIRELESS_11BG_24N:
+ memcpy(SupportedRates, WIFI_CCKRATES, IEEE80211_CCK_RATE_LEN);
+ memcpy(SupportedRates + IEEE80211_CCK_RATE_LEN, WIFI_OFDMRATES, IEEE80211_NUM_OFDM_RATESLEN);
+ break;
+ }
+}
+
+uint rtw_get_rateset_len(u8 *rateset)
+{
+ uint i = 0;
+ while (1) {
+ if ((rateset[i]) == 0)
+ break;
+ if (i > 12)
+ break;
+ i++;
+ }
+ return i;
+}
+
+int rtw_generate_ie(struct registry_priv *pregistrypriv)
+{
+ u8 wireless_mode;
+ int sz = 0, rateLen;
+ struct wlan_bssid_ex *pdev_network = &pregistrypriv->dev_network;
+ u8 *ie = pdev_network->IEs;
+
+
+ /* timestamp will be inserted by hardware */
+ sz += 8;
+ ie += sz;
+
+ /* beacon interval : 2bytes */
+ *(__le16 *)ie = cpu_to_le16((u16)pdev_network->Configuration.BeaconPeriod);/* BCN_INTERVAL; */
+ sz += 2;
+ ie += 2;
+
+ /* capability info */
+ *(u16 *)ie = 0;
+
+ *(__le16 *)ie |= cpu_to_le16(cap_IBSS);
+
+ if (pregistrypriv->preamble == PREAMBLE_SHORT)
+ *(__le16 *)ie |= cpu_to_le16(cap_ShortPremble);
+
+ if (pdev_network->Privacy)
+ *(__le16 *)ie |= cpu_to_le16(cap_Privacy);
+
+ sz += 2;
+ ie += 2;
+
+ /* SSID */
+ ie = rtw_set_ie(ie, _SSID_IE_, pdev_network->Ssid.SsidLength, pdev_network->Ssid.Ssid, &sz);
+
+ /* supported rates */
+ if (pregistrypriv->wireless_mode == WIRELESS_11ABGN) {
+ if (pdev_network->Configuration.DSConfig > 14)
+ wireless_mode = WIRELESS_11A_5N;
+ else
+ wireless_mode = WIRELESS_11BG_24N;
+ } else {
+ wireless_mode = pregistrypriv->wireless_mode;
+ }
+
+ rtw_set_supported_rate(pdev_network->SupportedRates, wireless_mode);
+
+ rateLen = rtw_get_rateset_len(pdev_network->SupportedRates);
+
+ if (rateLen > 8) {
+ ie = rtw_set_ie(ie, _SUPPORTEDRATES_IE_, 8, pdev_network->SupportedRates, &sz);
+ /* ie = rtw_set_ie(ie, _EXT_SUPPORTEDRATES_IE_, (rateLen - 8), (pdev_network->SupportedRates + 8), &sz); */
+ } else {
+ ie = rtw_set_ie(ie, _SUPPORTEDRATES_IE_, rateLen, pdev_network->SupportedRates, &sz);
+ }
+
+ /* DS parameter set */
+ ie = rtw_set_ie(ie, _DSSET_IE_, 1, (u8 *)&(pdev_network->Configuration.DSConfig), &sz);
+
+ /* IBSS Parameter Set */
+
+ ie = rtw_set_ie(ie, _IBSS_PARA_IE_, 2, (u8 *)&(pdev_network->Configuration.ATIMWindow), &sz);
+
+ if (rateLen > 8)
+ ie = rtw_set_ie(ie, _EXT_SUPPORTEDRATES_IE_, (rateLen - 8), (pdev_network->SupportedRates + 8), &sz);
+
+ return sz;
+}
+
+unsigned char *rtw_get_wpa_ie(unsigned char *pie, int *wpa_ie_len, int limit)
+{
+ int len;
+ u16 val16;
+ __le16 le_tmp;
+ unsigned char wpa_oui_type[] = {0x00, 0x50, 0xf2, 0x01};
+ u8 *pbuf = pie;
+ int limit_new = limit;
+
+ while (1) {
+ pbuf = rtw_get_ie(pbuf, _WPA_IE_ID_, &len, limit_new);
+
+ if (pbuf) {
+ /* check if oui matches... */
+ if (!memcmp((pbuf + 2), wpa_oui_type, sizeof(wpa_oui_type)) == false)
+ goto check_next_ie;
+
+ /* check version... */
+ memcpy((u8 *)&le_tmp, (pbuf + 6), sizeof(val16));
+
+ val16 = le16_to_cpu(le_tmp);
+ if (val16 != 0x0001)
+ goto check_next_ie;
+ *wpa_ie_len = *(pbuf + 1);
+ return pbuf;
+ } else {
+ *wpa_ie_len = 0;
+ return NULL;
+ }
+
+check_next_ie:
+ limit_new = limit - (pbuf - pie) - 2 - len;
+ if (limit_new <= 0)
+ break;
+ pbuf += (2 + len);
+ }
+ *wpa_ie_len = 0;
+ return NULL;
+}
+
+unsigned char *rtw_get_wpa2_ie(unsigned char *pie, int *rsn_ie_len, int limit)
+{
+
+ return rtw_get_ie(pie, _WPA2_IE_ID_, rsn_ie_len, limit);
+}
+
+int rtw_get_wpa_cipher_suite(u8 *s)
+{
+ if (!memcmp(s, WPA_CIPHER_SUITE_NONE, WPA_SELECTOR_LEN))
+ return WPA_CIPHER_NONE;
+ if (!memcmp(s, WPA_CIPHER_SUITE_WEP40, WPA_SELECTOR_LEN))
+ return WPA_CIPHER_WEP40;
+ if (!memcmp(s, WPA_CIPHER_SUITE_TKIP, WPA_SELECTOR_LEN))
+ return WPA_CIPHER_TKIP;
+ if (!memcmp(s, WPA_CIPHER_SUITE_CCMP, WPA_SELECTOR_LEN))
+ return WPA_CIPHER_CCMP;
+ if (!memcmp(s, WPA_CIPHER_SUITE_WEP104, WPA_SELECTOR_LEN))
+ return WPA_CIPHER_WEP104;
+
+ return 0;
+}
+
+int rtw_get_wpa2_cipher_suite(u8 *s)
+{
+ if (!memcmp(s, RSN_CIPHER_SUITE_NONE, RSN_SELECTOR_LEN))
+ return WPA_CIPHER_NONE;
+ if (!memcmp(s, RSN_CIPHER_SUITE_WEP40, RSN_SELECTOR_LEN))
+ return WPA_CIPHER_WEP40;
+ if (!memcmp(s, RSN_CIPHER_SUITE_TKIP, RSN_SELECTOR_LEN))
+ return WPA_CIPHER_TKIP;
+ if (!memcmp(s, RSN_CIPHER_SUITE_CCMP, RSN_SELECTOR_LEN))
+ return WPA_CIPHER_CCMP;
+ if (!memcmp(s, RSN_CIPHER_SUITE_WEP104, RSN_SELECTOR_LEN))
+ return WPA_CIPHER_WEP104;
+
+ return 0;
+}
+
+
+int rtw_parse_wpa_ie(u8 *wpa_ie, int wpa_ie_len, int *group_cipher, int *pairwise_cipher, int *is_8021x)
+{
+ int i, ret = _SUCCESS;
+ int left, count;
+ u8 *pos;
+ u8 SUITE_1X[4] = {0x00, 0x50, 0xf2, 1};
+
+ if (wpa_ie_len <= 0) {
+ /* No WPA IE - fail silently */
+ return _FAIL;
+ }
+
+
+ if ((*wpa_ie != _WPA_IE_ID_) || (*(wpa_ie+1) != (u8)(wpa_ie_len - 2)) ||
+ (memcmp(wpa_ie+2, RTW_WPA_OUI_TYPE, WPA_SELECTOR_LEN)))
+ return _FAIL;
+
+ pos = wpa_ie;
+
+ pos += 8;
+ left = wpa_ie_len - 8;
+
+
+ /* group_cipher */
+ if (left >= WPA_SELECTOR_LEN) {
+ *group_cipher = rtw_get_wpa_cipher_suite(pos);
+ pos += WPA_SELECTOR_LEN;
+ left -= WPA_SELECTOR_LEN;
+ } else if (left > 0) {
+ RT_TRACE(_module_rtl871x_mlme_c_, _drv_err_, ("%s: ie length mismatch, %u too much", __func__, left));
+ return _FAIL;
+ }
+
+ /* pairwise_cipher */
+ if (left >= 2) {
+ count = get_unaligned_le16(pos);
+ pos += 2;
+ left -= 2;
+
+ if (count == 0 || left < count * WPA_SELECTOR_LEN) {
+ RT_TRACE(_module_rtl871x_mlme_c_, _drv_err_, ("%s: ie count botch (pairwise), "
+ "count %u left %u", __func__, count, left));
+ return _FAIL;
+ }
+
+ for (i = 0; i < count; i++) {
+ *pairwise_cipher |= rtw_get_wpa_cipher_suite(pos);
+
+ pos += WPA_SELECTOR_LEN;
+ left -= WPA_SELECTOR_LEN;
+ }
+ } else if (left == 1) {
+ RT_TRACE(_module_rtl871x_mlme_c_, _drv_err_, ("%s: ie too short (for key mgmt)", __func__));
+ return _FAIL;
+ }
+
+ if (is_8021x) {
+ if (left >= 6) {
+ pos += 2;
+ if (!memcmp(pos, SUITE_1X, 4)) {
+ RT_TRACE(_module_rtl871x_mlme_c_, _drv_info_, ("%s : there has 802.1x auth\n", __func__));
+ *is_8021x = 1;
+ }
+ }
+ }
+
+ return ret;
+}
+
+int rtw_parse_wpa2_ie(u8 *rsn_ie, int rsn_ie_len, int *group_cipher, int *pairwise_cipher, int *is_8021x)
+{
+ int i, ret = _SUCCESS;
+ int left, count;
+ u8 *pos;
+ u8 SUITE_1X[4] = {0x00, 0x0f, 0xac, 0x01};
+
+ if (rsn_ie_len <= 0) {
+ /* No RSN IE - fail silently */
+ return _FAIL;
+ }
+
+
+ if ((*rsn_ie != _WPA2_IE_ID_) || (*(rsn_ie+1) != (u8)(rsn_ie_len - 2)))
+ return _FAIL;
+
+ pos = rsn_ie;
+ pos += 4;
+ left = rsn_ie_len - 4;
+
+ /* group_cipher */
+ if (left >= RSN_SELECTOR_LEN) {
+ *group_cipher = rtw_get_wpa2_cipher_suite(pos);
+
+ pos += RSN_SELECTOR_LEN;
+ left -= RSN_SELECTOR_LEN;
+
+ } else if (left > 0) {
+ RT_TRACE(_module_rtl871x_mlme_c_, _drv_err_, ("%s: ie length mismatch, %u too much", __func__, left));
+ return _FAIL;
+ }
+
+ /* pairwise_cipher */
+ if (left >= 2) {
+ count = get_unaligned_le16(pos);
+ pos += 2;
+ left -= 2;
+
+ if (count == 0 || left < count * RSN_SELECTOR_LEN) {
+ RT_TRACE(_module_rtl871x_mlme_c_, _drv_err_, ("%s: ie count botch (pairwise), "
+ "count %u left %u", __func__, count, left));
+ return _FAIL;
+ }
+
+ for (i = 0; i < count; i++) {
+ *pairwise_cipher |= rtw_get_wpa2_cipher_suite(pos);
+
+ pos += RSN_SELECTOR_LEN;
+ left -= RSN_SELECTOR_LEN;
+ }
+
+ } else if (left == 1) {
+ RT_TRACE(_module_rtl871x_mlme_c_, _drv_err_, ("%s: ie too short (for key mgmt)", __func__));
+
+ return _FAIL;
+ }
+
+ if (is_8021x) {
+ if (left >= 6) {
+ pos += 2;
+ if (!memcmp(pos, SUITE_1X, 4)) {
+ RT_TRACE(_module_rtl871x_mlme_c_, _drv_info_, ("%s (): there has 802.1x auth\n", __func__));
+ *is_8021x = 1;
+ }
+ }
+ }
+ return ret;
+}
+
+int rtw_get_sec_ie(u8 *in_ie, uint in_len, u8 *rsn_ie, u16 *rsn_len, u8 *wpa_ie, u16 *wpa_len)
+{
+ u8 authmode, sec_idx, i;
+ u8 wpa_oui[4] = {0x0, 0x50, 0xf2, 0x01};
+ uint cnt;
+
+
+ /* Search required WPA or WPA2 IE and copy to sec_ie[] */
+
+ cnt = _TIMESTAMP_ + _BEACON_ITERVAL_ + _CAPABILITY_;
+
+ sec_idx = 0;
+
+ while (cnt < in_len) {
+ authmode = in_ie[cnt];
+
+ if ((authmode == _WPA_IE_ID_) && (!memcmp(&in_ie[cnt+2], &wpa_oui[0], 4))) {
+ RT_TRACE(_module_rtl871x_mlme_c_, _drv_info_,
+ ("\n rtw_get_wpa_ie: sec_idx =%d in_ie[cnt+1]+2 =%d\n",
+ sec_idx, in_ie[cnt+1]+2));
+
+ if (wpa_ie) {
+ memcpy(wpa_ie, &in_ie[cnt], in_ie[cnt+1]+2);
+
+ for (i = 0; i < (in_ie[cnt+1]+2); i += 8) {
+ RT_TRACE(_module_rtl871x_mlme_c_, _drv_info_,
+ ("\n %2x,%2x,%2x,%2x,%2x,%2x,%2x,%2x\n",
+ wpa_ie[i], wpa_ie[i+1], wpa_ie[i+2], wpa_ie[i+3], wpa_ie[i+4],
+ wpa_ie[i+5], wpa_ie[i+6], wpa_ie[i+7]));
+ }
+ }
+
+ *wpa_len = in_ie[cnt+1]+2;
+ cnt += in_ie[cnt+1]+2; /* get next */
+ } else {
+ if (authmode == _WPA2_IE_ID_) {
+ RT_TRACE(_module_rtl871x_mlme_c_, _drv_info_,
+ ("\n get_rsn_ie: sec_idx =%d in_ie[cnt+1]+2 =%d\n",
+ sec_idx, in_ie[cnt+1]+2));
+
+ if (rsn_ie) {
+ memcpy(rsn_ie, &in_ie[cnt], in_ie[cnt+1]+2);
+
+ for (i = 0; i < (in_ie[cnt+1]+2); i += 8) {
+ RT_TRACE(_module_rtl871x_mlme_c_, _drv_info_,
+ ("\n %2x,%2x,%2x,%2x,%2x,%2x,%2x,%2x\n",
+ rsn_ie[i], rsn_ie[i+1], rsn_ie[i+2], rsn_ie[i+3], rsn_ie[i+4],
+ rsn_ie[i+5], rsn_ie[i+6], rsn_ie[i+7]));
+ }
+ }
+
+ *rsn_len = in_ie[cnt+1]+2;
+ cnt += in_ie[cnt+1]+2; /* get next */
+ } else {
+ cnt += in_ie[cnt+1]+2; /* get next */
+ }
+ }
+ }
+
+
+ return *rsn_len + *wpa_len;
+}
+
+u8 rtw_is_wps_ie(u8 *ie_ptr, uint *wps_ielen)
+{
+ u8 match = false;
+ u8 eid, wps_oui[4] = {0x0, 0x50, 0xf2, 0x04};
+
+ if (ie_ptr == NULL)
+ return match;
+
+ eid = ie_ptr[0];
+
+ if ((eid == _WPA_IE_ID_) && (!memcmp(&ie_ptr[2], wps_oui, 4))) {
+ *wps_ielen = ie_ptr[1]+2;
+ match = true;
+ }
+ return match;
+}
+
+/**
+ * rtw_get_wps_ie - Search WPS IE from a series of IEs
+ * @in_ie: Address of IEs to search
+ * @in_len: Length limit from in_ie
+ * @wps_ie: If not NULL and WPS IE is found, WPS IE will be copied to the buf starting from wps_ie
+ * @wps_ielen: If not NULL and WPS IE is found, will set to the length of the entire WPS IE
+ *
+ * Returns: The address of the WPS IE found, or NULL
+ */
+u8 *rtw_get_wps_ie(u8 *in_ie, uint in_len, u8 *wps_ie, uint *wps_ielen)
+{
+ uint cnt;
+ u8 *wpsie_ptr = NULL;
+ u8 eid, wps_oui[4] = {0x0, 0x50, 0xf2, 0x04};
+
+ if (wps_ielen)
+ *wps_ielen = 0;
+
+ if (!in_ie || in_len <= 0)
+ return wpsie_ptr;
+
+ cnt = 0;
+
+ while (cnt < in_len) {
+ eid = in_ie[cnt];
+
+ if ((eid == _WPA_IE_ID_) && (!memcmp(&in_ie[cnt+2], wps_oui, 4))) {
+ wpsie_ptr = &in_ie[cnt];
+
+ if (wps_ie)
+ memcpy(wps_ie, &in_ie[cnt], in_ie[cnt+1]+2);
+
+ if (wps_ielen)
+ *wps_ielen = in_ie[cnt+1]+2;
+
+ cnt += in_ie[cnt+1]+2;
+
+ break;
+ } else {
+ cnt += in_ie[cnt+1]+2; /* goto next */
+ }
+ }
+ return wpsie_ptr;
+}
+
+/**
+ * rtw_get_wps_attr - Search a specific WPS attribute from a given WPS IE
+ * @wps_ie: Address of WPS IE to search
+ * @wps_ielen: Length limit from wps_ie
+ * @target_attr_id: The attribute ID of WPS attribute to search
+ * @buf_attr: If not NULL and the WPS attribute is found, WPS attribute will be copied to the buf starting from buf_attr
+ * @len_attr: If not NULL and the WPS attribute is found, will set to the length of the entire WPS attribute
+ *
+ * Returns: the address of the specific WPS attribute found, or NULL
+ */
+u8 *rtw_get_wps_attr(u8 *wps_ie, uint wps_ielen, u16 target_attr_id, u8 *buf_attr, u32 *len_attr)
+{
+ u8 *attr_ptr = NULL;
+ u8 *target_attr_ptr = NULL;
+ u8 wps_oui[4] = {0x00, 0x50, 0xF2, 0x04};
+
+ if (len_attr)
+ *len_attr = 0;
+
+ if ((wps_ie[0] != _VENDOR_SPECIFIC_IE_) ||
+ (memcmp(wps_ie + 2, wps_oui, 4)))
+ return attr_ptr;
+
+ /* 6 = 1(Element ID) + 1(Length) + 4(WPS OUI) */
+ attr_ptr = wps_ie + 6; /* goto first attr */
+
+ while (attr_ptr - wps_ie < wps_ielen) {
+ /* 4 = 2(Attribute ID) + 2(Length) */
+ u16 attr_id = get_unaligned_be16(attr_ptr);
+ u16 attr_data_len = get_unaligned_be16(attr_ptr + 2);
+ u16 attr_len = attr_data_len + 4;
+
+ if (attr_id == target_attr_id) {
+ target_attr_ptr = attr_ptr;
+ if (buf_attr)
+ memcpy(buf_attr, attr_ptr, attr_len);
+ if (len_attr)
+ *len_attr = attr_len;
+ break;
+ } else {
+ attr_ptr += attr_len; /* goto next */
+ }
+ }
+ return target_attr_ptr;
+}
+
+/**
+ * rtw_get_wps_attr_content - Search a specific WPS attribute content from a given WPS IE
+ * @wps_ie: Address of WPS IE to search
+ * @wps_ielen: Length limit from wps_ie
+ * @target_attr_id: The attribute ID of WPS attribute to search
+ * @buf_content: If not NULL and the WPS attribute is found, WPS attribute content will be copied to the buf starting from buf_content
+ * @len_content: If not NULL and the WPS attribute is found, will set to the length of the WPS attribute content
+ *
+ * Returns: the address of the specific WPS attribute content found, or NULL
+ */
+u8 *rtw_get_wps_attr_content(u8 *wps_ie, uint wps_ielen, u16 target_attr_id, u8 *buf_content, uint *len_content)
+{
+ u8 *attr_ptr;
+ u32 attr_len;
+
+ if (len_content)
+ *len_content = 0;
+
+ attr_ptr = rtw_get_wps_attr(wps_ie, wps_ielen, target_attr_id, NULL, &attr_len);
+
+ if (attr_ptr && attr_len) {
+ if (buf_content)
+ memcpy(buf_content, attr_ptr+4, attr_len-4);
+
+ if (len_content)
+ *len_content = attr_len-4;
+
+ return attr_ptr+4;
+ }
+
+ return NULL;
+}
+
+static int rtw_ieee802_11_parse_vendor_specific(u8 *pos, uint elen,
+ struct rtw_ieee802_11_elems *elems,
+ int show_errors)
+{
+ unsigned int oui;
+
+ /* first 3 bytes in vendor specific information element are the IEEE
+ * OUI of the vendor. The following byte is used a vendor specific
+ * sub-type. */
+ if (elen < 4) {
+ if (show_errors) {
+ DBG_88E("short vendor specific information element ignored (len=%lu)\n",
+ (unsigned long)elen);
+ }
+ return -1;
+ }
+
+ oui = RTW_GET_BE24(pos);
+ switch (oui) {
+ case OUI_MICROSOFT:
+ /* Microsoft/Wi-Fi information elements are further typed and
+ * subtyped */
+ switch (pos[3]) {
+ case 1:
+ /* Microsoft OUI (00:50:F2) with OUI Type 1:
+ * real WPA information element */
+ elems->wpa_ie = pos;
+ elems->wpa_ie_len = elen;
+ break;
+ case WME_OUI_TYPE: /* this is a Wi-Fi WME info. element */
+ if (elen < 5) {
+ DBG_88E("short WME information element ignored (len=%lu)\n",
+ (unsigned long)elen);
+ return -1;
+ }
+ switch (pos[4]) {
+ case WME_OUI_SUBTYPE_INFORMATION_ELEMENT:
+ case WME_OUI_SUBTYPE_PARAMETER_ELEMENT:
+ elems->wme = pos;
+ elems->wme_len = elen;
+ break;
+ case WME_OUI_SUBTYPE_TSPEC_ELEMENT:
+ elems->wme_tspec = pos;
+ elems->wme_tspec_len = elen;
+ break;
+ default:
+ DBG_88E("unknown WME information element ignored (subtype=%d len=%lu)\n",
+ pos[4], (unsigned long)elen);
+ return -1;
+ }
+ break;
+ case 4:
+ /* Wi-Fi Protected Setup (WPS) IE */
+ elems->wps_ie = pos;
+ elems->wps_ie_len = elen;
+ break;
+ default:
+ DBG_88E("Unknown Microsoft information element ignored (type=%d len=%lu)\n",
+ pos[3], (unsigned long)elen);
+ return -1;
+ }
+ break;
+
+ case OUI_BROADCOM:
+ switch (pos[3]) {
+ case VENDOR_HT_CAPAB_OUI_TYPE:
+ elems->vendor_ht_cap = pos;
+ elems->vendor_ht_cap_len = elen;
+ break;
+ default:
+ DBG_88E("Unknown Broadcom information element ignored (type=%d len=%lu)\n",
+ pos[3], (unsigned long)elen);
+ return -1;
+ }
+ break;
+ default:
+ DBG_88E("unknown vendor specific information element ignored (vendor OUI %02x:%02x:%02x len=%lu)\n",
+ pos[0], pos[1], pos[2], (unsigned long)elen);
+ return -1;
+ }
+ return 0;
+}
+
+/**
+ * ieee802_11_parse_elems - Parse information elements in management frames
+ * @start: Pointer to the start of IEs
+ * @len: Length of IE buffer in octets
+ * @elems: Data structure for parsed elements
+ * @show_errors: Whether to show parsing errors in debug log
+ * Returns: Parsing result
+ */
+enum parse_res rtw_ieee802_11_parse_elems(u8 *start, uint len,
+ struct rtw_ieee802_11_elems *elems,
+ int show_errors)
+{
+ uint left = len;
+ u8 *pos = start;
+ int unknown = 0;
+
+ memset(elems, 0, sizeof(*elems));
+
+ while (left >= 2) {
+ u8 id, elen;
+
+ id = *pos++;
+ elen = *pos++;
+ left -= 2;
+
+ if (elen > left) {
+ if (show_errors) {
+ DBG_88E("IEEE 802.11 element parse failed (id=%d elen=%d left=%lu)\n",
+ id, elen, (unsigned long)left);
+ }
+ return ParseFailed;
+ }
+
+ switch (id) {
+ case WLAN_EID_SSID:
+ elems->ssid = pos;
+ elems->ssid_len = elen;
+ break;
+ case WLAN_EID_SUPP_RATES:
+ elems->supp_rates = pos;
+ elems->supp_rates_len = elen;
+ break;
+ case WLAN_EID_FH_PARAMS:
+ elems->fh_params = pos;
+ elems->fh_params_len = elen;
+ break;
+ case WLAN_EID_DS_PARAMS:
+ elems->ds_params = pos;
+ elems->ds_params_len = elen;
+ break;
+ case WLAN_EID_CF_PARAMS:
+ elems->cf_params = pos;
+ elems->cf_params_len = elen;
+ break;
+ case WLAN_EID_TIM:
+ elems->tim = pos;
+ elems->tim_len = elen;
+ break;
+ case WLAN_EID_IBSS_PARAMS:
+ elems->ibss_params = pos;
+ elems->ibss_params_len = elen;
+ break;
+ case WLAN_EID_CHALLENGE:
+ elems->challenge = pos;
+ elems->challenge_len = elen;
+ break;
+ case WLAN_EID_ERP_INFO:
+ elems->erp_info = pos;
+ elems->erp_info_len = elen;
+ break;
+ case WLAN_EID_EXT_SUPP_RATES:
+ elems->ext_supp_rates = pos;
+ elems->ext_supp_rates_len = elen;
+ break;
+ case WLAN_EID_VENDOR_SPECIFIC:
+ if (rtw_ieee802_11_parse_vendor_specific(pos, elen, elems, show_errors))
+ unknown++;
+ break;
+ case WLAN_EID_RSN:
+ elems->rsn_ie = pos;
+ elems->rsn_ie_len = elen;
+ break;
+ case WLAN_EID_PWR_CAPABILITY:
+ elems->power_cap = pos;
+ elems->power_cap_len = elen;
+ break;
+ case WLAN_EID_SUPPORTED_CHANNELS:
+ elems->supp_channels = pos;
+ elems->supp_channels_len = elen;
+ break;
+ case WLAN_EID_MOBILITY_DOMAIN:
+ elems->mdie = pos;
+ elems->mdie_len = elen;
+ break;
+ case WLAN_EID_FAST_BSS_TRANSITION:
+ elems->ftie = pos;
+ elems->ftie_len = elen;
+ break;
+ case WLAN_EID_TIMEOUT_INTERVAL:
+ elems->timeout_int = pos;
+ elems->timeout_int_len = elen;
+ break;
+ case WLAN_EID_HT_CAP:
+ elems->ht_capabilities = pos;
+ elems->ht_capabilities_len = elen;
+ break;
+ case WLAN_EID_HT_OPERATION:
+ elems->ht_operation = pos;
+ elems->ht_operation_len = elen;
+ break;
+ default:
+ unknown++;
+ if (!show_errors)
+ break;
+ DBG_88E("IEEE 802.11 element parse ignored unknown element (id=%d elen=%d)\n",
+ id, elen);
+ break;
+ }
+ left -= elen;
+ pos += elen;
+ }
+ if (left)
+ return ParseFailed;
+ return unknown ? ParseUnknown : ParseOK;
+}
+
+void rtw_macaddr_cfg(u8 *mac_addr)
+{
+ u8 mac[ETH_ALEN];
+
+ if (mac_addr == NULL)
+ return;
+
+ if (rtw_initmac && mac_pton(rtw_initmac, mac)) {
+ /* Users specify the mac address */
+ memcpy(mac_addr, mac, ETH_ALEN);
+ } else {
+ /* Use the mac address stored in the Efuse */
+ memcpy(mac, mac_addr, ETH_ALEN);
+ }
+
+ if (((mac[0] == 0xff) && (mac[1] == 0xff) && (mac[2] == 0xff) &&
+ (mac[3] == 0xff) && (mac[4] == 0xff) && (mac[5] == 0xff)) ||
+ ((mac[0] == 0x0) && (mac[1] == 0x0) && (mac[2] == 0x0) &&
+ (mac[3] == 0x0) && (mac[4] == 0x0) && (mac[5] == 0x0))) {
+ mac[0] = 0x00;
+ mac[1] = 0xe0;
+ mac[2] = 0x4c;
+ mac[3] = 0x87;
+ mac[4] = 0x00;
+ mac[5] = 0x00;
+ /* use default mac address */
+ memcpy(mac_addr, mac, ETH_ALEN);
+ DBG_88E("MAC Address from efuse error, assign default one !!!\n");
+ }
+
+ DBG_88E("rtw_macaddr_cfg MAC Address = %pM\n", (mac_addr));
+}
+
+void dump_ies(u8 *buf, u32 buf_len)
+{
+ u8 *pos = (u8 *)buf;
+ u8 id, len;
+
+ while (pos-buf <= buf_len) {
+ id = *pos;
+ len = *(pos+1);
+
+ DBG_88E("%s ID:%u, LEN:%u\n", __func__, id, len);
+ dump_wps_ie(pos, len);
+
+ pos += (2 + len);
+ }
+}
+
+void dump_wps_ie(u8 *ie, u32 ie_len)
+{
+ u8 *pos = (u8 *)ie;
+ u16 id;
+ u16 len;
+ u8 *wps_ie;
+ uint wps_ielen;
+
+ wps_ie = rtw_get_wps_ie(ie, ie_len, NULL, &wps_ielen);
+ if (wps_ie != ie || wps_ielen == 0)
+ return;
+
+ pos += 6;
+ while (pos-ie < ie_len) {
+ id = get_unaligned_be16(pos);
+ len = get_unaligned_be16(pos + 2);
+ DBG_88E("%s ID:0x%04x, LEN:%u\n", __func__, id, len);
+ pos += (4+len);
+ }
+}
+
+/* Baron adds to avoid FreeBSD warning */
+int ieee80211_is_empty_essid(const char *essid, int essid_len)
+{
+ /* Single white space is for Linksys APs */
+ if (essid_len == 1 && essid[0] == ' ')
+ return 1;
+
+ /* Otherwise, if the entire essid is 0, we assume it is hidden */
+ while (essid_len) {
+ essid_len--;
+ if (essid[essid_len] != '\0')
+ return 0;
+ }
+
+ return 1;
+}
+
+int ieee80211_get_hdrlen(u16 fc)
+{
+ int hdrlen = 24;
+
+ switch (WLAN_FC_GET_TYPE(fc)) {
+ case RTW_IEEE80211_FTYPE_DATA:
+ if (fc & RTW_IEEE80211_STYPE_QOS_DATA)
+ hdrlen += 2;
+ if ((fc & RTW_IEEE80211_FCTL_FROMDS) && (fc & RTW_IEEE80211_FCTL_TODS))
+ hdrlen += 6; /* Addr4 */
+ break;
+ case RTW_IEEE80211_FTYPE_CTL:
+ switch (WLAN_FC_GET_STYPE(fc)) {
+ case RTW_IEEE80211_STYPE_CTS:
+ case RTW_IEEE80211_STYPE_ACK:
+ hdrlen = 10;
+ break;
+ default:
+ hdrlen = 16;
+ break;
+ }
+ break;
+ }
+
+ return hdrlen;
+}
+
+static int rtw_get_cipher_info(struct wlan_network *pnetwork)
+{
+ u32 wpa_ielen;
+ unsigned char *pbuf;
+ int group_cipher = 0, pairwise_cipher = 0, is8021x = 0;
+ int ret = _FAIL;
+ pbuf = rtw_get_wpa_ie(&pnetwork->network.IEs[12], &wpa_ielen, pnetwork->network.IELength-12);
+
+ if (pbuf && (wpa_ielen > 0)) {
+ RT_TRACE(_module_rtl871x_mlme_c_, _drv_info_, ("rtw_get_cipher_info: wpa_ielen: %d", wpa_ielen));
+ if (_SUCCESS == rtw_parse_wpa_ie(pbuf, wpa_ielen+2, &group_cipher, &pairwise_cipher, &is8021x)) {
+ pnetwork->BcnInfo.pairwise_cipher = pairwise_cipher;
+ pnetwork->BcnInfo.group_cipher = group_cipher;
+ pnetwork->BcnInfo.is_8021x = is8021x;
+ RT_TRACE(_module_rtl871x_mlme_c_, _drv_info_, ("%s: pnetwork->pairwise_cipher: %d, is_8021x is %d",
+ __func__, pnetwork->BcnInfo.pairwise_cipher, pnetwork->BcnInfo.is_8021x));
+ ret = _SUCCESS;
+ }
+ } else {
+ pbuf = rtw_get_wpa2_ie(&pnetwork->network.IEs[12], &wpa_ielen, pnetwork->network.IELength-12);
+
+ if (pbuf && (wpa_ielen > 0)) {
+ RT_TRACE(_module_rtl871x_mlme_c_, _drv_info_, ("get RSN IE\n"));
+ if (_SUCCESS == rtw_parse_wpa2_ie(pbuf, wpa_ielen+2, &group_cipher, &pairwise_cipher, &is8021x)) {
+ RT_TRACE(_module_rtl871x_mlme_c_, _drv_info_, ("get RSN IE OK!!!\n"));
+ pnetwork->BcnInfo.pairwise_cipher = pairwise_cipher;
+ pnetwork->BcnInfo.group_cipher = group_cipher;
+ pnetwork->BcnInfo.is_8021x = is8021x;
+ RT_TRACE(_module_rtl871x_mlme_c_, _drv_info_, ("%s: pnetwork->pairwise_cipher: %d,"
+ "pnetwork->group_cipher is %d, is_8021x is %d", __func__, pnetwork->BcnInfo.pairwise_cipher,
+ pnetwork->BcnInfo.group_cipher, pnetwork->BcnInfo.is_8021x));
+ ret = _SUCCESS;
+ }
+ }
+ }
+
+ return ret;
+}
+
+void rtw_get_bcn_info(struct wlan_network *pnetwork)
+{
+ unsigned short cap = 0;
+ u8 bencrypt = 0;
+ __le16 le_tmp;
+ u16 wpa_len = 0, rsn_len = 0;
+ struct HT_info_element *pht_info = NULL;
+ struct rtw_ieee80211_ht_cap *pht_cap = NULL;
+ unsigned int len;
+ unsigned char *p;
+
+ memcpy(&le_tmp, rtw_get_capability_from_ie(pnetwork->network.IEs), 2);
+ cap = le16_to_cpu(le_tmp);
+ if (cap & WLAN_CAPABILITY_PRIVACY) {
+ bencrypt = 1;
+ pnetwork->network.Privacy = 1;
+ } else {
+ pnetwork->BcnInfo.encryp_protocol = ENCRYP_PROTOCOL_OPENSYS;
+ }
+ rtw_get_sec_ie(pnetwork->network.IEs, pnetwork->network.IELength, NULL, &rsn_len, NULL, &wpa_len);
+ RT_TRACE(_module_rtl871x_mlme_c_, _drv_info_, ("rtw_get_bcn_info: ssid =%s\n", pnetwork->network.Ssid.Ssid));
+ RT_TRACE(_module_rtl871x_mlme_c_, _drv_info_, ("rtw_get_bcn_info: wpa_len =%d rsn_len =%d\n", wpa_len, rsn_len));
+ RT_TRACE(_module_rtl871x_mlme_c_, _drv_info_, ("rtw_get_bcn_info: ssid =%s\n", pnetwork->network.Ssid.Ssid));
+ RT_TRACE(_module_rtl871x_mlme_c_, _drv_info_, ("rtw_get_bcn_info: wpa_len =%d rsn_len =%d\n", wpa_len, rsn_len));
+
+ if (rsn_len > 0) {
+ pnetwork->BcnInfo.encryp_protocol = ENCRYP_PROTOCOL_WPA2;
+ } else if (wpa_len > 0) {
+ pnetwork->BcnInfo.encryp_protocol = ENCRYP_PROTOCOL_WPA;
+ } else {
+ if (bencrypt)
+ pnetwork->BcnInfo.encryp_protocol = ENCRYP_PROTOCOL_WEP;
+ }
+ RT_TRACE(_module_rtl871x_mlme_c_, _drv_info_, ("rtw_get_bcn_info: pnetwork->encryp_protocol is %x\n",
+ pnetwork->BcnInfo.encryp_protocol));
+ RT_TRACE(_module_rtl871x_mlme_c_, _drv_info_, ("rtw_get_bcn_info: pnetwork->encryp_protocol is %x\n",
+ pnetwork->BcnInfo.encryp_protocol));
+ rtw_get_cipher_info(pnetwork);
+
+ /* get bwmode and ch_offset */
+ /* parsing HT_CAP_IE */
+ p = rtw_get_ie(pnetwork->network.IEs + _FIXED_IE_LENGTH_, _HT_CAPABILITY_IE_, &len, pnetwork->network.IELength - _FIXED_IE_LENGTH_);
+ if (p && len > 0) {
+ pht_cap = (struct rtw_ieee80211_ht_cap *)(p + 2);
+ pnetwork->BcnInfo.ht_cap_info = pht_cap->cap_info;
+ } else {
+ pnetwork->BcnInfo.ht_cap_info = 0;
+ }
+ /* parsing HT_INFO_IE */
+ p = rtw_get_ie(pnetwork->network.IEs + _FIXED_IE_LENGTH_, _HT_ADD_INFO_IE_, &len, pnetwork->network.IELength - _FIXED_IE_LENGTH_);
+ if (p && len > 0) {
+ pht_info = (struct HT_info_element *)(p + 2);
+ pnetwork->BcnInfo.ht_info_infos_0 = pht_info->infos[0];
+ } else {
+ pnetwork->BcnInfo.ht_info_infos_0 = 0;
+ }
+}
+
+/* show MCS rate, unit: 100Kbps */
+u16 rtw_mcs_rate(u8 rf_type, u8 bw_40MHz, u8 short_GI_20, u8 short_GI_40, unsigned char *MCS_rate)
+{
+ u16 max_rate = 0;
+
+ if (rf_type == RF_1T1R) {
+ if (MCS_rate[0] & BIT(7))
+ max_rate = (bw_40MHz) ? ((short_GI_40) ? 1500 : 1350) : ((short_GI_20) ? 722 : 650);
+ else if (MCS_rate[0] & BIT(6))
+ max_rate = (bw_40MHz) ? ((short_GI_40) ? 1350 : 1215) : ((short_GI_20) ? 650 : 585);
+ else if (MCS_rate[0] & BIT(5))
+ max_rate = (bw_40MHz) ? ((short_GI_40) ? 1200 : 1080) : ((short_GI_20) ? 578 : 520);
+ else if (MCS_rate[0] & BIT(4))
+ max_rate = (bw_40MHz) ? ((short_GI_40) ? 900 : 810) : ((short_GI_20) ? 433 : 390);
+ else if (MCS_rate[0] & BIT(3))
+ max_rate = (bw_40MHz) ? ((short_GI_40) ? 600 : 540) : ((short_GI_20) ? 289 : 260);
+ else if (MCS_rate[0] & BIT(2))
+ max_rate = (bw_40MHz) ? ((short_GI_40) ? 450 : 405) : ((short_GI_20) ? 217 : 195);
+ else if (MCS_rate[0] & BIT(1))
+ max_rate = (bw_40MHz) ? ((short_GI_40) ? 300 : 270) : ((short_GI_20) ? 144 : 130);
+ else if (MCS_rate[0] & BIT(0))
+ max_rate = (bw_40MHz) ? ((short_GI_40) ? 150 : 135) : ((short_GI_20) ? 72 : 65);
+ } else {
+ if (MCS_rate[1]) {
+ if (MCS_rate[1] & BIT(7))
+ max_rate = (bw_40MHz) ? ((short_GI_40) ? 3000 : 2700) : ((short_GI_20) ? 1444 : 1300);
+ else if (MCS_rate[1] & BIT(6))
+ max_rate = (bw_40MHz) ? ((short_GI_40) ? 2700 : 2430) : ((short_GI_20) ? 1300 : 1170);
+ else if (MCS_rate[1] & BIT(5))
+ max_rate = (bw_40MHz) ? ((short_GI_40) ? 2400 : 2160) : ((short_GI_20) ? 1156 : 1040);
+ else if (MCS_rate[1] & BIT(4))
+ max_rate = (bw_40MHz) ? ((short_GI_40) ? 1800 : 1620) : ((short_GI_20) ? 867 : 780);
+ else if (MCS_rate[1] & BIT(3))
+ max_rate = (bw_40MHz) ? ((short_GI_40) ? 1200 : 1080) : ((short_GI_20) ? 578 : 520);
+ else if (MCS_rate[1] & BIT(2))
+ max_rate = (bw_40MHz) ? ((short_GI_40) ? 900 : 810) : ((short_GI_20) ? 433 : 390);
+ else if (MCS_rate[1] & BIT(1))
+ max_rate = (bw_40MHz) ? ((short_GI_40) ? 600 : 540) : ((short_GI_20) ? 289 : 260);
+ else if (MCS_rate[1] & BIT(0))
+ max_rate = (bw_40MHz) ? ((short_GI_40) ? 300 : 270) : ((short_GI_20) ? 144 : 130);
+ } else {
+ if (MCS_rate[0] & BIT(7))
+ max_rate = (bw_40MHz) ? ((short_GI_40) ? 1500 : 1350) : ((short_GI_20) ? 722 : 650);
+ else if (MCS_rate[0] & BIT(6))
+ max_rate = (bw_40MHz) ? ((short_GI_40) ? 1350 : 1215) : ((short_GI_20) ? 650 : 585);
+ else if (MCS_rate[0] & BIT(5))
+ max_rate = (bw_40MHz) ? ((short_GI_40) ? 1200 : 1080) : ((short_GI_20) ? 578 : 520);
+ else if (MCS_rate[0] & BIT(4))
+ max_rate = (bw_40MHz) ? ((short_GI_40) ? 900 : 810) : ((short_GI_20) ? 433 : 390);
+ else if (MCS_rate[0] & BIT(3))
+ max_rate = (bw_40MHz) ? ((short_GI_40) ? 600 : 540) : ((short_GI_20) ? 289 : 260);
+ else if (MCS_rate[0] & BIT(2))
+ max_rate = (bw_40MHz) ? ((short_GI_40) ? 450 : 405) : ((short_GI_20) ? 217 : 195);
+ else if (MCS_rate[0] & BIT(1))
+ max_rate = (bw_40MHz) ? ((short_GI_40) ? 300 : 270) : ((short_GI_20) ? 144 : 130);
+ else if (MCS_rate[0] & BIT(0))
+ max_rate = (bw_40MHz) ? ((short_GI_40) ? 150 : 135) : ((short_GI_20) ? 72 : 65);
+ }
+ }
+ return max_rate;
+}
+
+int rtw_action_frame_parse(const u8 *frame, u32 frame_len, u8 *category, u8 *action)
+{
+ const u8 *frame_body = frame + sizeof(struct rtw_ieee80211_hdr_3addr);
+ u16 fc;
+ u8 c, a = 0;
+
+ fc = le16_to_cpu(((struct rtw_ieee80211_hdr_3addr *)frame)->frame_ctl);
+
+ if ((fc & (RTW_IEEE80211_FCTL_FTYPE|RTW_IEEE80211_FCTL_STYPE)) !=
+ (RTW_IEEE80211_FTYPE_MGMT|RTW_IEEE80211_STYPE_ACTION))
+ return false;
+
+ c = frame_body[0];
+
+ switch (c) {
+ case RTW_WLAN_CATEGORY_P2P: /* vendor-specific */
+ break;
+ default:
+ a = frame_body[1];
+ }
+
+ if (category)
+ *category = c;
+ if (action)
+ *action = a;
+
+ return true;
+}
+
+static const char *_action_public_str[] = {
+ "ACT_PUB_BSSCOEXIST",
+ "ACT_PUB_DSE_ENABLE",
+ "ACT_PUB_DSE_DEENABLE",
+ "ACT_PUB_DSE_REG_LOCATION",
+ "ACT_PUB_EXT_CHL_SWITCH",
+ "ACT_PUB_DSE_MSR_REQ",
+ "ACT_PUB_DSE_MSR_RPRT",
+ "ACT_PUB_MP",
+ "ACT_PUB_DSE_PWR_CONSTRAINT",
+ "ACT_PUB_VENDOR",
+ "ACT_PUB_GAS_INITIAL_REQ",
+ "ACT_PUB_GAS_INITIAL_RSP",
+ "ACT_PUB_GAS_COMEBACK_REQ",
+ "ACT_PUB_GAS_COMEBACK_RSP",
+ "ACT_PUB_TDLS_DISCOVERY_RSP",
+ "ACT_PUB_LOCATION_TRACK",
+ "ACT_PUB_RSVD",
+};
+
+const char *action_public_str(u8 action)
+{
+ action = (action >= ACT_PUBLIC_MAX) ? ACT_PUBLIC_MAX : action;
+ return _action_public_str[action];
+}
diff --git a/drivers/staging/rtl8188eu/core/rtw_ioctl_set.c b/drivers/staging/rtl8188eu/core/rtw_ioctl_set.c
new file mode 100644
index 000000000..969150a48
--- /dev/null
+++ b/drivers/staging/rtl8188eu/core/rtw_ioctl_set.c
@@ -0,0 +1,663 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2012 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ *
+ ******************************************************************************/
+#define _RTW_IOCTL_SET_C_
+
+
+#include <osdep_service.h>
+#include <drv_types.h>
+#include <rtw_ioctl_set.h>
+#include <hal_intf.h>
+
+extern void indicate_wx_scan_complete_event(struct adapter *padapter);
+
+#define IS_MAC_ADDRESS_BROADCAST(addr) \
+(\
+ ((addr[0] == 0xff) && (addr[1] == 0xff) && \
+ (addr[2] == 0xff) && (addr[3] == 0xff) && \
+ (addr[4] == 0xff) && (addr[5] == 0xff)) ? true : false \
+)
+
+u8 rtw_do_join(struct adapter *padapter)
+{
+ struct list_head *plist, *phead;
+ u8 *pibss = NULL;
+ struct mlme_priv *pmlmepriv = &(padapter->mlmepriv);
+ struct __queue *queue = &(pmlmepriv->scanned_queue);
+ u8 ret = _SUCCESS;
+
+
+ spin_lock_bh(&(pmlmepriv->scanned_queue.lock));
+ phead = get_list_head(queue);
+ plist = phead->next;
+
+ RT_TRACE(_module_rtl871x_ioctl_set_c_, _drv_info_, ("\n rtw_do_join: phead = %p; plist = %p\n\n\n", phead, plist));
+
+ pmlmepriv->cur_network.join_res = -2;
+
+ set_fwstate(pmlmepriv, _FW_UNDER_LINKING);
+
+ pmlmepriv->pscanned = plist;
+
+ pmlmepriv->to_join = true;
+
+ if (list_empty(&queue->queue)) {
+ spin_unlock_bh(&(pmlmepriv->scanned_queue.lock));
+ _clr_fwstate_(pmlmepriv, _FW_UNDER_LINKING);
+
+ /* when set_ssid/set_bssid for rtw_do_join(), but scanning queue is empty */
+ /* we try to issue sitesurvey firstly */
+
+ if (!pmlmepriv->LinkDetectInfo.bBusyTraffic ||
+ pmlmepriv->to_roaming > 0) {
+ RT_TRACE(_module_rtl871x_ioctl_set_c_, _drv_info_, ("rtw_do_join(): site survey if scanned_queue is empty\n."));
+ /* submit site_survey_cmd */
+ ret = rtw_sitesurvey_cmd(padapter, &pmlmepriv->assoc_ssid, 1, NULL, 0);
+ if (_SUCCESS != ret) {
+ pmlmepriv->to_join = false;
+ RT_TRACE(_module_rtl871x_ioctl_set_c_, _drv_err_, ("rtw_do_join(): site survey return error\n."));
+ }
+ } else {
+ pmlmepriv->to_join = false;
+ ret = _FAIL;
+ }
+
+ goto exit;
+ } else {
+ int select_ret;
+
+ spin_unlock_bh(&(pmlmepriv->scanned_queue.lock));
+ select_ret = rtw_select_and_join_from_scanned_queue(pmlmepriv);
+ if (select_ret == _SUCCESS) {
+ pmlmepriv->to_join = false;
+ mod_timer(&pmlmepriv->assoc_timer,
+ jiffies + msecs_to_jiffies(MAX_JOIN_TIMEOUT));
+ } else {
+ if (check_fwstate(pmlmepriv, WIFI_ADHOC_STATE) == true) {
+ /* submit createbss_cmd to change to a ADHOC_MASTER */
+
+ /* pmlmepriv->lock has been acquired by caller... */
+ struct wlan_bssid_ex *pdev_network = &(padapter->registrypriv.dev_network);
+
+ pmlmepriv->fw_state = WIFI_ADHOC_MASTER_STATE;
+
+ pibss = padapter->registrypriv.dev_network.MacAddress;
+
+ memcpy(&pdev_network->Ssid, &pmlmepriv->assoc_ssid, sizeof(struct ndis_802_11_ssid));
+
+ rtw_update_registrypriv_dev_network(padapter);
+
+ rtw_generate_random_ibss(pibss);
+
+ if (rtw_createbss_cmd(padapter) != _SUCCESS) {
+ RT_TRACE(_module_rtl871x_ioctl_set_c_, _drv_err_, ("***Error =>do_goin: rtw_createbss_cmd status FAIL***\n "));
+ ret = false;
+ goto exit;
+ }
+ pmlmepriv->to_join = false;
+
+ RT_TRACE(_module_rtl871x_ioctl_set_c_, _drv_info_,
+ ("***Error => rtw_select_and_join_from_scanned_queue FAIL under STA_Mode***\n "));
+ } else {
+ /* can't associate ; reset under-linking */
+ _clr_fwstate_(pmlmepriv, _FW_UNDER_LINKING);
+
+ /* when set_ssid/set_bssid for rtw_do_join(), but there are no desired bss in scanning queue */
+ /* we try to issue sitesurvey firstly */
+ if (!pmlmepriv->LinkDetectInfo.bBusyTraffic ||
+ pmlmepriv->to_roaming > 0) {
+ ret = rtw_sitesurvey_cmd(padapter, &pmlmepriv->assoc_ssid, 1, NULL, 0);
+ if (_SUCCESS != ret) {
+ pmlmepriv->to_join = false;
+ RT_TRACE(_module_rtl871x_ioctl_set_c_, _drv_err_, ("do_join(): site survey return error\n."));
+ }
+ } else {
+ ret = _FAIL;
+ pmlmepriv->to_join = false;
+ }
+ }
+ }
+ }
+
+exit:
+
+
+ return ret;
+}
+
+u8 rtw_set_802_11_bssid(struct adapter *padapter, u8 *bssid)
+{
+ u8 status = _SUCCESS;
+ u32 cur_time = 0;
+ struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
+
+
+ DBG_88E_LEVEL(_drv_info_, "set bssid:%pM\n", bssid);
+
+ if ((bssid[0] == 0x00 && bssid[1] == 0x00 && bssid[2] == 0x00 &&
+ bssid[3] == 0x00 && bssid[4] == 0x00 && bssid[5] == 0x00) ||
+ (bssid[0] == 0xFF && bssid[1] == 0xFF && bssid[2] == 0xFF &&
+ bssid[3] == 0xFF && bssid[4] == 0xFF && bssid[5] == 0xFF)) {
+ status = _FAIL;
+ goto exit;
+ }
+
+ spin_lock_bh(&pmlmepriv->lock);
+
+
+ DBG_88E("Set BSSID under fw_state = 0x%08x\n", get_fwstate(pmlmepriv));
+ if (check_fwstate(pmlmepriv, _FW_UNDER_SURVEY) == true)
+ goto handle_tkip_countermeasure;
+ else if (check_fwstate(pmlmepriv, _FW_UNDER_LINKING))
+ goto release_mlme_lock;
+
+ if (check_fwstate(pmlmepriv, _FW_LINKED|WIFI_ADHOC_MASTER_STATE)) {
+ RT_TRACE(_module_rtl871x_ioctl_set_c_, _drv_info_, ("set_bssid: _FW_LINKED||WIFI_ADHOC_MASTER_STATE\n"));
+
+ if (!memcmp(&pmlmepriv->cur_network.network.MacAddress, bssid, ETH_ALEN)) {
+ if (check_fwstate(pmlmepriv, WIFI_STATION_STATE) == false)
+ goto release_mlme_lock;/* it means driver is in WIFI_ADHOC_MASTER_STATE, we needn't create bss again. */
+ } else {
+ RT_TRACE(_module_rtl871x_ioctl_set_c_, _drv_info_, ("Set BSSID not the same bssid\n"));
+ RT_TRACE(_module_rtl871x_ioctl_set_c_, _drv_info_, ("set_bssid =%pM\n", (bssid)));
+ RT_TRACE(_module_rtl871x_ioctl_set_c_, _drv_info_, ("cur_bssid =%pM\n", (pmlmepriv->cur_network.network.MacAddress)));
+
+ rtw_disassoc_cmd(padapter, 0, true);
+
+ if (check_fwstate(pmlmepriv, _FW_LINKED) == true)
+ rtw_indicate_disconnect(padapter);
+
+ rtw_free_assoc_resources(padapter, 1);
+
+ if ((check_fwstate(pmlmepriv, WIFI_ADHOC_MASTER_STATE) == true)) {
+ _clr_fwstate_(pmlmepriv, WIFI_ADHOC_MASTER_STATE);
+ set_fwstate(pmlmepriv, WIFI_ADHOC_STATE);
+ }
+ }
+ }
+
+handle_tkip_countermeasure:
+ /* should we add something here...? */
+
+ if (padapter->securitypriv.btkip_countermeasure) {
+ cur_time = jiffies;
+
+ if ((cur_time - padapter->securitypriv.btkip_countermeasure_time) > 60 * HZ) {
+ padapter->securitypriv.btkip_countermeasure = false;
+ padapter->securitypriv.btkip_countermeasure_time = 0;
+ } else {
+ status = _FAIL;
+ goto release_mlme_lock;
+ }
+ }
+
+ memcpy(&pmlmepriv->assoc_bssid, bssid, ETH_ALEN);
+ pmlmepriv->assoc_by_bssid = true;
+
+ if (check_fwstate(pmlmepriv, _FW_UNDER_SURVEY))
+ pmlmepriv->to_join = true;
+ else
+ status = rtw_do_join(padapter);
+
+release_mlme_lock:
+ spin_unlock_bh(&pmlmepriv->lock);
+
+exit:
+ RT_TRACE(_module_rtl871x_ioctl_set_c_, _drv_err_,
+ ("rtw_set_802_11_bssid: status=%d\n", status));
+
+
+ return status;
+}
+
+u8 rtw_set_802_11_ssid(struct adapter *padapter, struct ndis_802_11_ssid *ssid)
+{
+ u8 status = _SUCCESS;
+ u32 cur_time = 0;
+
+ struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
+ struct wlan_network *pnetwork = &pmlmepriv->cur_network;
+
+
+ DBG_88E_LEVEL(_drv_info_, "set ssid [%s] fw_state=0x%08x\n",
+ ssid->Ssid, get_fwstate(pmlmepriv));
+
+ if (!padapter->hw_init_completed) {
+ RT_TRACE(_module_rtl871x_ioctl_set_c_, _drv_err_,
+ ("set_ssid: hw_init_completed == false =>exit!!!\n"));
+ status = _FAIL;
+ goto exit;
+ }
+
+ spin_lock_bh(&pmlmepriv->lock);
+
+ DBG_88E("Set SSID under fw_state = 0x%08x\n", get_fwstate(pmlmepriv));
+ if (check_fwstate(pmlmepriv, _FW_UNDER_SURVEY) == true)
+ goto handle_tkip_countermeasure;
+ else if (check_fwstate(pmlmepriv, _FW_UNDER_LINKING) == true)
+ goto release_mlme_lock;
+
+ if (check_fwstate(pmlmepriv, _FW_LINKED|WIFI_ADHOC_MASTER_STATE)) {
+ RT_TRACE(_module_rtl871x_ioctl_set_c_, _drv_info_,
+ ("set_ssid: _FW_LINKED||WIFI_ADHOC_MASTER_STATE\n"));
+
+ if ((pmlmepriv->assoc_ssid.SsidLength == ssid->SsidLength) &&
+ (!memcmp(&pmlmepriv->assoc_ssid.Ssid, ssid->Ssid, ssid->SsidLength))) {
+ if ((check_fwstate(pmlmepriv, WIFI_STATION_STATE) == false)) {
+ RT_TRACE(_module_rtl871x_ioctl_set_c_, _drv_err_,
+ ("Set SSID is the same ssid, fw_state = 0x%08x\n",
+ get_fwstate(pmlmepriv)));
+
+ if (!rtw_is_same_ibss(padapter, pnetwork)) {
+ /* if in WIFI_ADHOC_MASTER_STATE | WIFI_ADHOC_STATE, create bss or rejoin again */
+ rtw_disassoc_cmd(padapter, 0, true);
+
+ if (check_fwstate(pmlmepriv, _FW_LINKED) == true)
+ rtw_indicate_disconnect(padapter);
+
+ rtw_free_assoc_resources(padapter, 1);
+
+ if (check_fwstate(pmlmepriv, WIFI_ADHOC_MASTER_STATE) == true) {
+ _clr_fwstate_(pmlmepriv, WIFI_ADHOC_MASTER_STATE);
+ set_fwstate(pmlmepriv, WIFI_ADHOC_STATE);
+ }
+ } else {
+ goto release_mlme_lock;/* it means driver is in WIFI_ADHOC_MASTER_STATE, we needn't create bss again. */
+ }
+ } else {
+ rtw_lps_ctrl_wk_cmd(padapter, LPS_CTRL_JOINBSS, 1);
+ }
+ } else {
+ RT_TRACE(_module_rtl871x_ioctl_set_c_, _drv_info_, ("Set SSID not the same ssid\n"));
+ RT_TRACE(_module_rtl871x_ioctl_set_c_, _drv_info_, ("set_ssid =[%s] len = 0x%x\n", ssid->Ssid, (unsigned int)ssid->SsidLength));
+ RT_TRACE(_module_rtl871x_ioctl_set_c_, _drv_info_, ("assoc_ssid =[%s] len = 0x%x\n", pmlmepriv->assoc_ssid.Ssid, (unsigned int)pmlmepriv->assoc_ssid.SsidLength));
+
+ rtw_disassoc_cmd(padapter, 0, true);
+
+ if (check_fwstate(pmlmepriv, _FW_LINKED) == true)
+ rtw_indicate_disconnect(padapter);
+
+ rtw_free_assoc_resources(padapter, 1);
+
+ if (check_fwstate(pmlmepriv, WIFI_ADHOC_MASTER_STATE) == true) {
+ _clr_fwstate_(pmlmepriv, WIFI_ADHOC_MASTER_STATE);
+ set_fwstate(pmlmepriv, WIFI_ADHOC_STATE);
+ }
+ }
+ }
+
+handle_tkip_countermeasure:
+
+ if (padapter->securitypriv.btkip_countermeasure) {
+ cur_time = jiffies;
+
+ if ((cur_time - padapter->securitypriv.btkip_countermeasure_time) > 60 * HZ) {
+ padapter->securitypriv.btkip_countermeasure = false;
+ padapter->securitypriv.btkip_countermeasure_time = 0;
+ } else {
+ status = _FAIL;
+ goto release_mlme_lock;
+ }
+ }
+
+ memcpy(&pmlmepriv->assoc_ssid, ssid, sizeof(struct ndis_802_11_ssid));
+ pmlmepriv->assoc_by_bssid = false;
+
+ if (check_fwstate(pmlmepriv, _FW_UNDER_SURVEY) == true)
+ pmlmepriv->to_join = true;
+ else
+ status = rtw_do_join(padapter);
+
+release_mlme_lock:
+ spin_unlock_bh(&pmlmepriv->lock);
+
+exit:
+ RT_TRACE(_module_rtl871x_ioctl_set_c_, _drv_err_,
+ ("-rtw_set_802_11_ssid: status =%d\n", status));
+ return status;
+}
+
+u8 rtw_set_802_11_infrastructure_mode(struct adapter *padapter,
+ enum ndis_802_11_network_infra networktype)
+{
+ struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
+ struct wlan_network *cur_network = &pmlmepriv->cur_network;
+ enum ndis_802_11_network_infra *pold_state = &(cur_network->network.InfrastructureMode);
+
+
+ RT_TRACE(_module_rtl871x_ioctl_set_c_, _drv_notice_,
+ ("+rtw_set_802_11_infrastructure_mode: old =%d new =%d fw_state = 0x%08x\n",
+ *pold_state, networktype, get_fwstate(pmlmepriv)));
+
+ if (*pold_state != networktype) {
+ spin_lock_bh(&pmlmepriv->lock);
+
+ RT_TRACE(_module_rtl871x_ioctl_set_c_, _drv_info_, (" change mode!"));
+ /* DBG_88E("change mode, old_mode =%d, new_mode =%d, fw_state = 0x%x\n", *pold_state, networktype, get_fwstate(pmlmepriv)); */
+
+ if (*pold_state == Ndis802_11APMode) {
+ /* change to other mode from Ndis802_11APMode */
+ cur_network->join_res = -1;
+
+#ifdef CONFIG_88EU_AP_MODE
+ stop_ap_mode(padapter);
+#endif
+ }
+
+ if ((check_fwstate(pmlmepriv, _FW_LINKED)) ||
+ (*pold_state == Ndis802_11IBSS))
+ rtw_disassoc_cmd(padapter, 0, true);
+
+ if ((check_fwstate(pmlmepriv, _FW_LINKED)) ||
+ (check_fwstate(pmlmepriv, WIFI_ADHOC_MASTER_STATE)))
+ rtw_free_assoc_resources(padapter, 1);
+
+ if ((*pold_state == Ndis802_11Infrastructure) || (*pold_state == Ndis802_11IBSS)) {
+ if (check_fwstate(pmlmepriv, _FW_LINKED) == true)
+ rtw_indicate_disconnect(padapter); /* will clr Linked_state; before this function, we must have checked whether issue dis-assoc_cmd or not */
+ }
+
+ *pold_state = networktype;
+
+ _clr_fwstate_(pmlmepriv, ~WIFI_NULL_STATE);
+
+ switch (networktype) {
+ case Ndis802_11IBSS:
+ set_fwstate(pmlmepriv, WIFI_ADHOC_STATE);
+ break;
+ case Ndis802_11Infrastructure:
+ set_fwstate(pmlmepriv, WIFI_STATION_STATE);
+ break;
+ case Ndis802_11APMode:
+ set_fwstate(pmlmepriv, WIFI_AP_STATE);
+#ifdef CONFIG_88EU_AP_MODE
+ start_ap_mode(padapter);
+#endif
+ break;
+ case Ndis802_11AutoUnknown:
+ case Ndis802_11InfrastructureMax:
+ break;
+ }
+ spin_unlock_bh(&pmlmepriv->lock);
+ }
+
+
+ return true;
+}
+
+
+u8 rtw_set_802_11_disassociate(struct adapter *padapter)
+{
+ struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
+
+
+ spin_lock_bh(&pmlmepriv->lock);
+
+ if (check_fwstate(pmlmepriv, _FW_LINKED)) {
+ RT_TRACE(_module_rtl871x_ioctl_set_c_, _drv_info_,
+ ("MgntActrtw_set_802_11_disassociate: rtw_indicate_disconnect\n"));
+
+ rtw_disassoc_cmd(padapter, 0, true);
+ rtw_indicate_disconnect(padapter);
+ rtw_free_assoc_resources(padapter, 1);
+ rtw_pwr_wakeup(padapter);
+ }
+
+ spin_unlock_bh(&pmlmepriv->lock);
+
+
+ return true;
+}
+
+u8 rtw_set_802_11_bssid_list_scan(struct adapter *padapter, struct ndis_802_11_ssid *pssid, int ssid_max_num)
+{
+ struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
+ u8 res = true;
+
+
+ RT_TRACE(_module_rtl871x_ioctl_set_c_, _drv_err_, ("+rtw_set_802_11_bssid_list_scan(), fw_state =%x\n", get_fwstate(pmlmepriv)));
+
+ if (padapter == NULL) {
+ res = false;
+ goto exit;
+ }
+ if (!padapter->hw_init_completed) {
+ res = false;
+ RT_TRACE(_module_rtl871x_ioctl_set_c_, _drv_err_, ("\n === rtw_set_802_11_bssid_list_scan:hw_init_completed == false ===\n"));
+ goto exit;
+ }
+
+ if ((check_fwstate(pmlmepriv, _FW_UNDER_SURVEY|_FW_UNDER_LINKING)) ||
+ (pmlmepriv->LinkDetectInfo.bBusyTraffic)) {
+ /* Scan or linking is in progress, do nothing. */
+ RT_TRACE(_module_rtl871x_ioctl_set_c_, _drv_err_, ("rtw_set_802_11_bssid_list_scan fail since fw_state = %x\n", get_fwstate(pmlmepriv)));
+ res = true;
+
+ if (check_fwstate(pmlmepriv,
+ (_FW_UNDER_SURVEY|_FW_UNDER_LINKING)) == true)
+ RT_TRACE(_module_rtl871x_ioctl_set_c_, _drv_err_, ("\n###_FW_UNDER_SURVEY|_FW_UNDER_LINKING\n\n"));
+ else
+ RT_TRACE(_module_rtl871x_ioctl_set_c_, _drv_err_, ("\n###pmlmepriv->sitesurveyctrl.traffic_busy == true\n\n"));
+
+ } else {
+ if (rtw_is_scan_deny(padapter)) {
+ DBG_88E(FUNC_ADPT_FMT": scan deny\n", FUNC_ADPT_ARG(padapter));
+ indicate_wx_scan_complete_event(padapter);
+ return _SUCCESS;
+ }
+
+ spin_lock_bh(&pmlmepriv->lock);
+
+ res = rtw_sitesurvey_cmd(padapter, pssid, ssid_max_num, NULL, 0);
+
+ spin_unlock_bh(&pmlmepriv->lock);
+ }
+exit:
+
+
+ return res;
+}
+
+u8 rtw_set_802_11_authentication_mode(struct adapter *padapter, enum ndis_802_11_auth_mode authmode)
+{
+ struct security_priv *psecuritypriv = &padapter->securitypriv;
+ int res;
+ u8 ret;
+
+
+ RT_TRACE(_module_rtl871x_ioctl_set_c_, _drv_info_, ("set_802_11_auth.mode(): mode =%x\n", authmode));
+
+ psecuritypriv->ndisauthtype = authmode;
+
+ RT_TRACE(_module_rtl871x_ioctl_set_c_, _drv_info_,
+ ("rtw_set_802_11_authentication_mode:psecuritypriv->ndisauthtype=%d",
+ psecuritypriv->ndisauthtype));
+
+ if (psecuritypriv->ndisauthtype > 3)
+ psecuritypriv->dot11AuthAlgrthm = dot11AuthAlgrthm_8021X;
+
+ res = rtw_set_auth(padapter, psecuritypriv);
+
+ if (res == _SUCCESS)
+ ret = true;
+ else
+ ret = false;
+
+
+ return ret;
+}
+
+u8 rtw_set_802_11_add_wep(struct adapter *padapter, struct ndis_802_11_wep *wep)
+{
+ int keyid, res;
+ struct security_priv *psecuritypriv = &(padapter->securitypriv);
+ u8 ret = _SUCCESS;
+
+
+ keyid = wep->KeyIndex & 0x3fffffff;
+
+ if (keyid >= 4) {
+ RT_TRACE(_module_rtl871x_ioctl_set_c_, _drv_err_, ("MgntActrtw_set_802_11_add_wep:keyid>4 =>fail\n"));
+ ret = false;
+ goto exit;
+ }
+
+ switch (wep->KeyLength) {
+ case 5:
+ psecuritypriv->dot11PrivacyAlgrthm = _WEP40_;
+ RT_TRACE(_module_rtl871x_ioctl_set_c_, _drv_info_, ("MgntActrtw_set_802_11_add_wep:wep->KeyLength = 5\n"));
+ break;
+ case 13:
+ psecuritypriv->dot11PrivacyAlgrthm = _WEP104_;
+ RT_TRACE(_module_rtl871x_ioctl_set_c_, _drv_info_, ("MgntActrtw_set_802_11_add_wep:wep->KeyLength = 13\n"));
+ break;
+ default:
+ psecuritypriv->dot11PrivacyAlgrthm = _NO_PRIVACY_;
+ RT_TRACE(_module_rtl871x_ioctl_set_c_, _drv_info_, ("MgntActrtw_set_802_11_add_wep:wep->KeyLength!= 5 or 13\n"));
+ break;
+ }
+ RT_TRACE(_module_rtl871x_ioctl_set_c_, _drv_info_,
+ ("rtw_set_802_11_add_wep:before memcpy, wep->KeyLength = 0x%x wep->KeyIndex = 0x%x keyid =%x\n",
+ wep->KeyLength, wep->KeyIndex, keyid));
+
+ memcpy(&(psecuritypriv->dot11DefKey[keyid].skey[0]), &(wep->KeyMaterial), wep->KeyLength);
+
+ psecuritypriv->dot11DefKeylen[keyid] = wep->KeyLength;
+
+ psecuritypriv->dot11PrivacyKeyIndex = keyid;
+
+ RT_TRACE(_module_rtl871x_ioctl_set_c_, _drv_info_,
+ ("rtw_set_802_11_add_wep:security key material : %x %x %x %x %x %x %x %x %x %x %x %x %x\n",
+ psecuritypriv->dot11DefKey[keyid].skey[0],
+ psecuritypriv->dot11DefKey[keyid].skey[1],
+ psecuritypriv->dot11DefKey[keyid].skey[2],
+ psecuritypriv->dot11DefKey[keyid].skey[3],
+ psecuritypriv->dot11DefKey[keyid].skey[4],
+ psecuritypriv->dot11DefKey[keyid].skey[5],
+ psecuritypriv->dot11DefKey[keyid].skey[6],
+ psecuritypriv->dot11DefKey[keyid].skey[7],
+ psecuritypriv->dot11DefKey[keyid].skey[8],
+ psecuritypriv->dot11DefKey[keyid].skey[9],
+ psecuritypriv->dot11DefKey[keyid].skey[10],
+ psecuritypriv->dot11DefKey[keyid].skey[11],
+ psecuritypriv->dot11DefKey[keyid].skey[12]));
+
+ res = rtw_set_key(padapter, psecuritypriv, keyid, 1);
+
+ if (res == _FAIL)
+ ret = false;
+exit:
+ return ret;
+}
+
+/*
+* rtw_get_cur_max_rate -
+* @adapter: pointer to struct adapter structure
+*
+* Return 0 or 100Kbps
+*/
+u16 rtw_get_cur_max_rate(struct adapter *adapter)
+{
+ int i = 0;
+ u8 *p;
+ u16 rate = 0, max_rate = 0;
+ struct mlme_ext_priv *pmlmeext = &adapter->mlmeextpriv;
+ struct mlme_ext_info *pmlmeinfo = &(pmlmeext->mlmext_info);
+ struct registry_priv *pregistrypriv = &adapter->registrypriv;
+ struct mlme_priv *pmlmepriv = &adapter->mlmepriv;
+ struct wlan_bssid_ex *pcur_bss = &pmlmepriv->cur_network.network;
+ struct rtw_ieee80211_ht_cap *pht_capie;
+ u8 rf_type = 0;
+ u8 bw_40MHz = 0, short_GI_20 = 0, short_GI_40 = 0;
+ u16 mcs_rate = 0;
+ u32 ht_ielen = 0;
+
+ if (adapter->registrypriv.mp_mode == 1) {
+ if (check_fwstate(pmlmepriv, WIFI_MP_STATE))
+ return 0;
+ }
+
+ if ((!check_fwstate(pmlmepriv, _FW_LINKED)) &&
+ (!check_fwstate(pmlmepriv, WIFI_ADHOC_MASTER_STATE)))
+ return 0;
+
+ if (pmlmeext->cur_wireless_mode & (WIRELESS_11_24N|WIRELESS_11_5N)) {
+ p = rtw_get_ie(&pcur_bss->IEs[12], _HT_CAPABILITY_IE_, &ht_ielen, pcur_bss->IELength-12);
+ if (p && ht_ielen > 0) {
+ pht_capie = (struct rtw_ieee80211_ht_cap *)(p+2);
+
+ memcpy(&mcs_rate, pht_capie->supp_mcs_set, 2);
+
+ /* cur_bwmod is updated by beacon, pmlmeinfo is updated by association response */
+ bw_40MHz = (pmlmeext->cur_bwmode && (HT_INFO_HT_PARAM_REC_TRANS_CHNL_WIDTH & pmlmeinfo->HT_info.infos[0])) ? 1 : 0;
+
+ short_GI_20 = (le16_to_cpu(pmlmeinfo->HT_caps.u.HT_cap_element.HT_caps_info) & IEEE80211_HT_CAP_SGI_20) ? 1 : 0;
+ short_GI_40 = (le16_to_cpu(pmlmeinfo->HT_caps.u.HT_cap_element.HT_caps_info) & IEEE80211_HT_CAP_SGI_40) ? 1 : 0;
+
+ rtw_hal_get_hwreg(adapter, HW_VAR_RF_TYPE, (u8 *)(&rf_type));
+ max_rate = rtw_mcs_rate(
+ rf_type,
+ bw_40MHz & (pregistrypriv->cbw40_enable),
+ short_GI_20,
+ short_GI_40,
+ pmlmeinfo->HT_caps.u.HT_cap_element.MCS_rate
+ );
+ }
+ } else {
+ while ((pcur_bss->SupportedRates[i] != 0) && (pcur_bss->SupportedRates[i] != 0xFF)) {
+ rate = pcur_bss->SupportedRates[i]&0x7F;
+ if (rate > max_rate)
+ max_rate = rate;
+ i++;
+ }
+
+ max_rate = max_rate*10/2;
+ }
+
+ return max_rate;
+}
+
+/*
+* rtw_set_country -
+* @adapter: pointer to struct adapter structure
+* @country_code: string of country code
+*
+* Return _SUCCESS or _FAIL
+*/
+int rtw_set_country(struct adapter *adapter, const char *country_code)
+{
+ int channel_plan = RT_CHANNEL_DOMAIN_WORLD_WIDE_5G;
+
+ DBG_88E("%s country_code:%s\n", __func__, country_code);
+
+ /* TODO: should have a table to match country code and RT_CHANNEL_DOMAIN */
+ /* TODO: should consider 2-character and 3-character country code */
+ if (0 == strcmp(country_code, "US"))
+ channel_plan = RT_CHANNEL_DOMAIN_FCC;
+ else if (0 == strcmp(country_code, "EU"))
+ channel_plan = RT_CHANNEL_DOMAIN_ETSI;
+ else if (0 == strcmp(country_code, "JP"))
+ channel_plan = RT_CHANNEL_DOMAIN_MKK;
+ else if (0 == strcmp(country_code, "CN"))
+ channel_plan = RT_CHANNEL_DOMAIN_CHINA;
+ else
+ DBG_88E("%s unknown country_code:%s\n", __func__, country_code);
+
+ return rtw_set_chplan_cmd(adapter, channel_plan, 1);
+}
diff --git a/drivers/staging/rtl8188eu/core/rtw_iol.c b/drivers/staging/rtl8188eu/core/rtw_iol.c
new file mode 100644
index 000000000..cdcf0eacc
--- /dev/null
+++ b/drivers/staging/rtl8188eu/core/rtw_iol.c
@@ -0,0 +1,31 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2011 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ *
+ ******************************************************************************/
+
+#include<rtw_iol.h>
+
+bool rtw_IOL_applied(struct adapter *adapter)
+{
+ if (1 == adapter->registrypriv.fw_iol)
+ return true;
+
+ if ((2 == adapter->registrypriv.fw_iol) && (!adapter_to_dvobj(adapter)->ishighspeed))
+ return true;
+ return false;
+}
diff --git a/drivers/staging/rtl8188eu/core/rtw_led.c b/drivers/staging/rtl8188eu/core/rtw_led.c
new file mode 100644
index 000000000..94405dc44
--- /dev/null
+++ b/drivers/staging/rtl8188eu/core/rtw_led.c
@@ -0,0 +1,497 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2012 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * 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.
+ *
+ *
+ ******************************************************************************/
+
+#include <drv_types.h>
+#include "rtw_led.h"
+
+/* */
+/* Description: */
+/* Callback function of LED BlinkTimer, */
+/* it just schedules to corresponding BlinkWorkItem/led_blink_hdl */
+/* */
+void BlinkTimerCallback(unsigned long data)
+{
+ struct LED_871x *pLed = (struct LED_871x *)data;
+ struct adapter *padapter = pLed->padapter;
+
+ if ((padapter->bSurpriseRemoved) || (padapter->bDriverStopped))
+ return;
+
+ schedule_work(&(pLed->BlinkWorkItem));
+}
+
+/* */
+/* Description: */
+/* Callback function of LED BlinkWorkItem. */
+/* */
+void BlinkWorkItemCallback(struct work_struct *work)
+{
+ struct LED_871x *pLed = container_of(work, struct LED_871x, BlinkWorkItem);
+ BlinkHandler(pLed);
+}
+
+/* */
+/* Description: */
+/* Reset status of LED_871x object. */
+/* */
+void ResetLedStatus(struct LED_871x *pLed)
+{
+ pLed->CurrLedState = RTW_LED_OFF; /* Current LED state. */
+ pLed->bLedOn = false; /* true if LED is ON, false if LED is OFF. */
+
+ pLed->bLedBlinkInProgress = false; /* true if it is blinking, false o.w.. */
+ pLed->bLedWPSBlinkInProgress = false;
+
+ pLed->BlinkTimes = 0; /* Number of times to toggle led state for blinking. */
+ pLed->BlinkingLedState = LED_UNKNOWN; /* Next state for blinking, either RTW_LED_ON or RTW_LED_OFF are. */
+
+ pLed->bLedNoLinkBlinkInProgress = false;
+ pLed->bLedLinkBlinkInProgress = false;
+ pLed->bLedStartToLinkBlinkInProgress = false;
+ pLed->bLedScanBlinkInProgress = false;
+}
+
+/*Description: */
+/* Initialize an LED_871x object. */
+void InitLed871x(struct adapter *padapter, struct LED_871x *pLed)
+{
+ pLed->padapter = padapter;
+
+ ResetLedStatus(pLed);
+
+ setup_timer(&(pLed->BlinkTimer), BlinkTimerCallback,
+ (unsigned long)pLed);
+
+ INIT_WORK(&(pLed->BlinkWorkItem), BlinkWorkItemCallback);
+}
+
+
+/* */
+/* Description: */
+/* DeInitialize an LED_871x object. */
+/* */
+void DeInitLed871x(struct LED_871x *pLed)
+{
+ cancel_work_sync(&(pLed->BlinkWorkItem));
+ del_timer_sync(&(pLed->BlinkTimer));
+ ResetLedStatus(pLed);
+}
+
+/* */
+/* Description: */
+/* Implementation of LED blinking behavior. */
+/* It toggle off LED and schedule corresponding timer if necessary. */
+/* */
+
+static void SwLedBlink1(struct LED_871x *pLed)
+{
+ struct adapter *padapter = pLed->padapter;
+ struct mlme_priv *pmlmepriv = &(padapter->mlmepriv);
+ u8 bStopBlinking = false;
+
+ /* Change LED according to BlinkingLedState specified. */
+ if (pLed->BlinkingLedState == RTW_LED_ON) {
+ SwLedOn(padapter, pLed);
+ RT_TRACE(_module_rtl8712_led_c_, _drv_info_, ("Blinktimes (%d): turn on\n", pLed->BlinkTimes));
+ } else {
+ SwLedOff(padapter, pLed);
+ RT_TRACE(_module_rtl8712_led_c_, _drv_info_, ("Blinktimes (%d): turn off\n", pLed->BlinkTimes));
+ }
+
+ if (padapter->pwrctrlpriv.rf_pwrstate != rf_on) {
+ SwLedOff(padapter, pLed);
+ ResetLedStatus(pLed);
+ return;
+ }
+
+ switch (pLed->CurrLedState) {
+ case LED_BLINK_SLOWLY:
+ if (pLed->bLedOn)
+ pLed->BlinkingLedState = RTW_LED_OFF;
+ else
+ pLed->BlinkingLedState = RTW_LED_ON;
+ mod_timer(&pLed->BlinkTimer, jiffies +
+ msecs_to_jiffies(LED_BLINK_NO_LINK_INTERVAL_ALPHA));
+ break;
+ case LED_BLINK_NORMAL:
+ if (pLed->bLedOn)
+ pLed->BlinkingLedState = RTW_LED_OFF;
+ else
+ pLed->BlinkingLedState = RTW_LED_ON;
+ mod_timer(&pLed->BlinkTimer, jiffies +
+ msecs_to_jiffies(LED_BLINK_LINK_INTERVAL_ALPHA));
+ break;
+ case LED_BLINK_SCAN:
+ pLed->BlinkTimes--;
+ if (pLed->BlinkTimes == 0)
+ bStopBlinking = true;
+ if (bStopBlinking) {
+ if (check_fwstate(pmlmepriv, _FW_LINKED)) {
+ pLed->bLedLinkBlinkInProgress = true;
+ pLed->CurrLedState = LED_BLINK_NORMAL;
+ if (pLed->bLedOn)
+ pLed->BlinkingLedState = RTW_LED_OFF;
+ else
+ pLed->BlinkingLedState = RTW_LED_ON;
+ mod_timer(&pLed->BlinkTimer, jiffies +
+ msecs_to_jiffies(LED_BLINK_LINK_INTERVAL_ALPHA));
+ RT_TRACE(_module_rtl8712_led_c_, _drv_info_, ("CurrLedState %d\n", pLed->CurrLedState));
+ } else if (!check_fwstate(pmlmepriv, _FW_LINKED)) {
+ pLed->bLedNoLinkBlinkInProgress = true;
+ pLed->CurrLedState = LED_BLINK_SLOWLY;
+ if (pLed->bLedOn)
+ pLed->BlinkingLedState = RTW_LED_OFF;
+ else
+ pLed->BlinkingLedState = RTW_LED_ON;
+ mod_timer(&pLed->BlinkTimer, jiffies +
+ msecs_to_jiffies(LED_BLINK_NO_LINK_INTERVAL_ALPHA));
+ RT_TRACE(_module_rtl8712_led_c_, _drv_info_, ("CurrLedState %d\n", pLed->CurrLedState));
+ }
+ pLed->bLedScanBlinkInProgress = false;
+ } else {
+ if (pLed->bLedOn)
+ pLed->BlinkingLedState = RTW_LED_OFF;
+ else
+ pLed->BlinkingLedState = RTW_LED_ON;
+ mod_timer(&pLed->BlinkTimer, jiffies +
+ msecs_to_jiffies(LED_BLINK_SCAN_INTERVAL_ALPHA));
+ }
+ break;
+ case LED_BLINK_TXRX:
+ pLed->BlinkTimes--;
+ if (pLed->BlinkTimes == 0)
+ bStopBlinking = true;
+ if (bStopBlinking) {
+ if (check_fwstate(pmlmepriv, _FW_LINKED)) {
+ pLed->bLedLinkBlinkInProgress = true;
+ pLed->CurrLedState = LED_BLINK_NORMAL;
+ if (pLed->bLedOn)
+ pLed->BlinkingLedState = RTW_LED_OFF;
+ else
+ pLed->BlinkingLedState = RTW_LED_ON;
+ mod_timer(&pLed->BlinkTimer, jiffies +
+ msecs_to_jiffies(LED_BLINK_LINK_INTERVAL_ALPHA));
+ RT_TRACE(_module_rtl8712_led_c_, _drv_info_, ("CurrLedState %d\n", pLed->CurrLedState));
+ } else if (!check_fwstate(pmlmepriv, _FW_LINKED)) {
+ pLed->bLedNoLinkBlinkInProgress = true;
+ pLed->CurrLedState = LED_BLINK_SLOWLY;
+ if (pLed->bLedOn)
+ pLed->BlinkingLedState = RTW_LED_OFF;
+ else
+ pLed->BlinkingLedState = RTW_LED_ON;
+ mod_timer(&pLed->BlinkTimer, jiffies +
+ msecs_to_jiffies(LED_BLINK_NO_LINK_INTERVAL_ALPHA));
+ RT_TRACE(_module_rtl8712_led_c_, _drv_info_, ("CurrLedState %d\n", pLed->CurrLedState));
+ }
+ pLed->BlinkTimes = 0;
+ pLed->bLedBlinkInProgress = false;
+ } else {
+ if (pLed->bLedOn)
+ pLed->BlinkingLedState = RTW_LED_OFF;
+ else
+ pLed->BlinkingLedState = RTW_LED_ON;
+ mod_timer(&pLed->BlinkTimer, jiffies +
+ msecs_to_jiffies(LED_BLINK_FASTER_INTERVAL_ALPHA));
+ }
+ break;
+ case LED_BLINK_WPS:
+ if (pLed->bLedOn)
+ pLed->BlinkingLedState = RTW_LED_OFF;
+ else
+ pLed->BlinkingLedState = RTW_LED_ON;
+ mod_timer(&pLed->BlinkTimer, jiffies +
+ msecs_to_jiffies(LED_BLINK_SCAN_INTERVAL_ALPHA));
+ break;
+ case LED_BLINK_WPS_STOP: /* WPS success */
+ if (pLed->BlinkingLedState == RTW_LED_ON)
+ bStopBlinking = false;
+ else
+ bStopBlinking = true;
+
+ if (bStopBlinking) {
+ pLed->bLedLinkBlinkInProgress = true;
+ pLed->CurrLedState = LED_BLINK_NORMAL;
+ if (pLed->bLedOn)
+ pLed->BlinkingLedState = RTW_LED_OFF;
+ else
+ pLed->BlinkingLedState = RTW_LED_ON;
+ mod_timer(&pLed->BlinkTimer, jiffies +
+ msecs_to_jiffies(LED_BLINK_LINK_INTERVAL_ALPHA));
+ RT_TRACE(_module_rtl8712_led_c_, _drv_info_, ("CurrLedState %d\n", pLed->CurrLedState));
+
+ pLed->bLedWPSBlinkInProgress = false;
+ } else {
+ pLed->BlinkingLedState = RTW_LED_OFF;
+ mod_timer(&pLed->BlinkTimer, jiffies +
+ msecs_to_jiffies(LED_BLINK_WPS_SUCCESS_INTERVAL_ALPHA));
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+ /* ALPHA, added by chiyoko, 20090106 */
+static void SwLedControlMode1(struct adapter *padapter, enum LED_CTL_MODE LedAction)
+{
+ struct led_priv *ledpriv = &(padapter->ledpriv);
+ struct LED_871x *pLed = &(ledpriv->SwLed0);
+ struct mlme_priv *pmlmepriv = &(padapter->mlmepriv);
+
+ switch (LedAction) {
+ case LED_CTL_POWER_ON:
+ case LED_CTL_START_TO_LINK:
+ case LED_CTL_NO_LINK:
+ if (!pLed->bLedNoLinkBlinkInProgress) {
+ if (pLed->CurrLedState == LED_BLINK_SCAN || IS_LED_WPS_BLINKING(pLed))
+ return;
+ if (pLed->bLedLinkBlinkInProgress) {
+ del_timer_sync(&(pLed->BlinkTimer));
+ pLed->bLedLinkBlinkInProgress = false;
+ }
+ if (pLed->bLedBlinkInProgress) {
+ del_timer_sync(&(pLed->BlinkTimer));
+ pLed->bLedBlinkInProgress = false;
+ }
+
+ pLed->bLedNoLinkBlinkInProgress = true;
+ pLed->CurrLedState = LED_BLINK_SLOWLY;
+ if (pLed->bLedOn)
+ pLed->BlinkingLedState = RTW_LED_OFF;
+ else
+ pLed->BlinkingLedState = RTW_LED_ON;
+ mod_timer(&pLed->BlinkTimer, jiffies +
+ msecs_to_jiffies(LED_BLINK_NO_LINK_INTERVAL_ALPHA));
+ }
+ break;
+ case LED_CTL_LINK:
+ if (!pLed->bLedLinkBlinkInProgress) {
+ if (pLed->CurrLedState == LED_BLINK_SCAN || IS_LED_WPS_BLINKING(pLed))
+ return;
+ if (pLed->bLedNoLinkBlinkInProgress) {
+ del_timer_sync(&(pLed->BlinkTimer));
+ pLed->bLedNoLinkBlinkInProgress = false;
+ }
+ if (pLed->bLedBlinkInProgress) {
+ del_timer_sync(&(pLed->BlinkTimer));
+ pLed->bLedBlinkInProgress = false;
+ }
+ pLed->bLedLinkBlinkInProgress = true;
+ pLed->CurrLedState = LED_BLINK_NORMAL;
+ if (pLed->bLedOn)
+ pLed->BlinkingLedState = RTW_LED_OFF;
+ else
+ pLed->BlinkingLedState = RTW_LED_ON;
+ mod_timer(&pLed->BlinkTimer, jiffies +
+ msecs_to_jiffies(LED_BLINK_LINK_INTERVAL_ALPHA));
+ }
+ break;
+ case LED_CTL_SITE_SURVEY:
+ if ((pmlmepriv->LinkDetectInfo.bBusyTraffic) && (check_fwstate(pmlmepriv, _FW_LINKED))) {
+ ;
+ } else if (!pLed->bLedScanBlinkInProgress) {
+ if (IS_LED_WPS_BLINKING(pLed))
+ return;
+ if (pLed->bLedNoLinkBlinkInProgress) {
+ del_timer_sync(&(pLed->BlinkTimer));
+ pLed->bLedNoLinkBlinkInProgress = false;
+ }
+ if (pLed->bLedLinkBlinkInProgress) {
+ del_timer_sync(&(pLed->BlinkTimer));
+ pLed->bLedLinkBlinkInProgress = false;
+ }
+ if (pLed->bLedBlinkInProgress) {
+ del_timer_sync(&(pLed->BlinkTimer));
+ pLed->bLedBlinkInProgress = false;
+ }
+ pLed->bLedScanBlinkInProgress = true;
+ pLed->CurrLedState = LED_BLINK_SCAN;
+ pLed->BlinkTimes = 24;
+ if (pLed->bLedOn)
+ pLed->BlinkingLedState = RTW_LED_OFF;
+ else
+ pLed->BlinkingLedState = RTW_LED_ON;
+ mod_timer(&pLed->BlinkTimer, jiffies +
+ msecs_to_jiffies(LED_BLINK_SCAN_INTERVAL_ALPHA));
+ }
+ break;
+ case LED_CTL_TX:
+ case LED_CTL_RX:
+ if (!pLed->bLedBlinkInProgress) {
+ if (pLed->CurrLedState == LED_BLINK_SCAN || IS_LED_WPS_BLINKING(pLed))
+ return;
+ if (pLed->bLedNoLinkBlinkInProgress) {
+ del_timer_sync(&(pLed->BlinkTimer));
+ pLed->bLedNoLinkBlinkInProgress = false;
+ }
+ if (pLed->bLedLinkBlinkInProgress) {
+ del_timer_sync(&(pLed->BlinkTimer));
+ pLed->bLedLinkBlinkInProgress = false;
+ }
+ pLed->bLedBlinkInProgress = true;
+ pLed->CurrLedState = LED_BLINK_TXRX;
+ pLed->BlinkTimes = 2;
+ if (pLed->bLedOn)
+ pLed->BlinkingLedState = RTW_LED_OFF;
+ else
+ pLed->BlinkingLedState = RTW_LED_ON;
+ mod_timer(&pLed->BlinkTimer, jiffies +
+ msecs_to_jiffies(LED_BLINK_FASTER_INTERVAL_ALPHA));
+ }
+ break;
+ case LED_CTL_START_WPS: /* wait until xinpin finish */
+ case LED_CTL_START_WPS_BOTTON:
+ if (!pLed->bLedWPSBlinkInProgress) {
+ if (pLed->bLedNoLinkBlinkInProgress) {
+ del_timer_sync(&(pLed->BlinkTimer));
+ pLed->bLedNoLinkBlinkInProgress = false;
+ }
+ if (pLed->bLedLinkBlinkInProgress) {
+ del_timer_sync(&(pLed->BlinkTimer));
+ pLed->bLedLinkBlinkInProgress = false;
+ }
+ if (pLed->bLedBlinkInProgress) {
+ del_timer_sync(&(pLed->BlinkTimer));
+ pLed->bLedBlinkInProgress = false;
+ }
+ if (pLed->bLedScanBlinkInProgress) {
+ del_timer_sync(&(pLed->BlinkTimer));
+ pLed->bLedScanBlinkInProgress = false;
+ }
+ pLed->bLedWPSBlinkInProgress = true;
+ pLed->CurrLedState = LED_BLINK_WPS;
+ if (pLed->bLedOn)
+ pLed->BlinkingLedState = RTW_LED_OFF;
+ else
+ pLed->BlinkingLedState = RTW_LED_ON;
+ mod_timer(&pLed->BlinkTimer, jiffies +
+ msecs_to_jiffies(LED_BLINK_SCAN_INTERVAL_ALPHA));
+ }
+ break;
+ case LED_CTL_STOP_WPS:
+ if (pLed->bLedNoLinkBlinkInProgress) {
+ del_timer_sync(&(pLed->BlinkTimer));
+ pLed->bLedNoLinkBlinkInProgress = false;
+ }
+ if (pLed->bLedLinkBlinkInProgress) {
+ del_timer_sync(&(pLed->BlinkTimer));
+ pLed->bLedLinkBlinkInProgress = false;
+ }
+ if (pLed->bLedBlinkInProgress) {
+ del_timer_sync(&(pLed->BlinkTimer));
+ pLed->bLedBlinkInProgress = false;
+ }
+ if (pLed->bLedScanBlinkInProgress) {
+ del_timer_sync(&(pLed->BlinkTimer));
+ pLed->bLedScanBlinkInProgress = false;
+ }
+ if (pLed->bLedWPSBlinkInProgress)
+ del_timer_sync(&(pLed->BlinkTimer));
+ else
+ pLed->bLedWPSBlinkInProgress = true;
+ pLed->CurrLedState = LED_BLINK_WPS_STOP;
+ if (pLed->bLedOn) {
+ pLed->BlinkingLedState = RTW_LED_OFF;
+ mod_timer(&pLed->BlinkTimer, jiffies +
+ msecs_to_jiffies(LED_BLINK_WPS_SUCCESS_INTERVAL_ALPHA));
+ } else {
+ pLed->BlinkingLedState = RTW_LED_ON;
+ mod_timer(&pLed->BlinkTimer,
+ jiffies + msecs_to_jiffies(0));
+ }
+ break;
+ case LED_CTL_STOP_WPS_FAIL:
+ if (pLed->bLedWPSBlinkInProgress) {
+ del_timer_sync(&(pLed->BlinkTimer));
+ pLed->bLedWPSBlinkInProgress = false;
+ }
+ pLed->bLedNoLinkBlinkInProgress = true;
+ pLed->CurrLedState = LED_BLINK_SLOWLY;
+ if (pLed->bLedOn)
+ pLed->BlinkingLedState = RTW_LED_OFF;
+ else
+ pLed->BlinkingLedState = RTW_LED_ON;
+ mod_timer(&pLed->BlinkTimer, jiffies +
+ msecs_to_jiffies(LED_BLINK_NO_LINK_INTERVAL_ALPHA));
+ break;
+ case LED_CTL_POWER_OFF:
+ pLed->CurrLedState = RTW_LED_OFF;
+ pLed->BlinkingLedState = RTW_LED_OFF;
+ if (pLed->bLedNoLinkBlinkInProgress) {
+ del_timer_sync(&(pLed->BlinkTimer));
+ pLed->bLedNoLinkBlinkInProgress = false;
+ }
+ if (pLed->bLedLinkBlinkInProgress) {
+ del_timer_sync(&(pLed->BlinkTimer));
+ pLed->bLedLinkBlinkInProgress = false;
+ }
+ if (pLed->bLedBlinkInProgress) {
+ del_timer_sync(&(pLed->BlinkTimer));
+ pLed->bLedBlinkInProgress = false;
+ }
+ if (pLed->bLedWPSBlinkInProgress) {
+ del_timer_sync(&(pLed->BlinkTimer));
+ pLed->bLedWPSBlinkInProgress = false;
+ }
+ if (pLed->bLedScanBlinkInProgress) {
+ del_timer_sync(&(pLed->BlinkTimer));
+ pLed->bLedScanBlinkInProgress = false;
+ }
+ SwLedOff(padapter, pLed);
+ break;
+ default:
+ break;
+ }
+
+ RT_TRACE(_module_rtl8712_led_c_, _drv_info_, ("Led %d\n", pLed->CurrLedState));
+}
+
+/* */
+/* Description: */
+/* Handler function of LED Blinking. */
+/* */
+void BlinkHandler(struct LED_871x *pLed)
+{
+ struct adapter *padapter = pLed->padapter;
+
+ if ((padapter->bSurpriseRemoved) || (padapter->bDriverStopped))
+ return;
+
+ SwLedBlink1(pLed);
+}
+
+void LedControl8188eu(struct adapter *padapter, enum LED_CTL_MODE LedAction)
+{
+ struct led_priv *ledpriv = &(padapter->ledpriv);
+
+ if ((padapter->bSurpriseRemoved) || (padapter->bDriverStopped) ||
+ (!padapter->hw_init_completed))
+ return;
+
+ if (!ledpriv->bRegUseLed)
+ return;
+
+ if ((padapter->pwrctrlpriv.rf_pwrstate != rf_on &&
+ padapter->pwrctrlpriv.rfoff_reason > RF_CHANGE_BY_PS) &&
+ (LedAction == LED_CTL_TX || LedAction == LED_CTL_RX ||
+ LedAction == LED_CTL_SITE_SURVEY ||
+ LedAction == LED_CTL_LINK ||
+ LedAction == LED_CTL_NO_LINK ||
+ LedAction == LED_CTL_POWER_ON))
+ return;
+
+ SwLedControlMode1(padapter, LedAction);
+}
diff --git a/drivers/staging/rtl8188eu/core/rtw_mlme.c b/drivers/staging/rtl8188eu/core/rtw_mlme.c
new file mode 100644
index 000000000..6c91aa58d
--- /dev/null
+++ b/drivers/staging/rtl8188eu/core/rtw_mlme.c
@@ -0,0 +1,2174 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2011 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ *
+ ******************************************************************************/
+#define _RTW_MLME_C_
+
+
+#include <osdep_service.h>
+#include <drv_types.h>
+#include <recv_osdep.h>
+#include <xmit_osdep.h>
+#include <hal_intf.h>
+#include <mlme_osdep.h>
+#include <sta_info.h>
+#include <wifi.h>
+#include <wlan_bssdef.h>
+#include <rtw_ioctl_set.h>
+#include <linux/vmalloc.h>
+
+extern unsigned char MCS_rate_2R[16];
+extern unsigned char MCS_rate_1R[16];
+
+int rtw_init_mlme_priv(struct adapter *padapter)
+{
+ int i;
+ u8 *pbuf;
+ struct wlan_network *pnetwork;
+ struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
+ int res = _SUCCESS;
+
+ /* We don't need to memset padapter->XXX to zero, because adapter is allocated by vzalloc(). */
+
+ pmlmepriv->nic_hdl = (u8 *)padapter;
+
+ pmlmepriv->pscanned = NULL;
+ pmlmepriv->fw_state = 0;
+ pmlmepriv->cur_network.network.InfrastructureMode = Ndis802_11AutoUnknown;
+ pmlmepriv->scan_mode = SCAN_ACTIVE;/* 1: active, 0: pasive. Maybe someday we should rename this varable to "active_mode" (Jeff) */
+
+ spin_lock_init(&(pmlmepriv->lock));
+ _rtw_init_queue(&(pmlmepriv->free_bss_pool));
+ _rtw_init_queue(&(pmlmepriv->scanned_queue));
+
+ set_scanned_network_val(pmlmepriv, 0);
+
+ memset(&pmlmepriv->assoc_ssid, 0, sizeof(struct ndis_802_11_ssid));
+
+ pbuf = vzalloc(MAX_BSS_CNT * (sizeof(struct wlan_network)));
+
+ if (pbuf == NULL) {
+ res = _FAIL;
+ goto exit;
+ }
+ pmlmepriv->free_bss_buf = pbuf;
+
+ pnetwork = (struct wlan_network *)pbuf;
+
+ for (i = 0; i < MAX_BSS_CNT; i++) {
+ INIT_LIST_HEAD(&(pnetwork->list));
+
+ list_add_tail(&(pnetwork->list), &(pmlmepriv->free_bss_pool.queue));
+
+ pnetwork++;
+ }
+
+ /* allocate DMA-able/Non-Page memory for cmd_buf and rsp_buf */
+
+ rtw_clear_scan_deny(padapter);
+
+ rtw_init_mlme_timer(padapter);
+
+exit:
+ return res;
+}
+
+#if defined(CONFIG_88EU_AP_MODE)
+static void rtw_free_mlme_ie_data(u8 **ppie, u32 *plen)
+{
+ kfree(*ppie);
+ *plen = 0;
+ *ppie = NULL;
+}
+
+void rtw_free_mlme_priv_ie_data(struct mlme_priv *pmlmepriv)
+{
+ rtw_buf_free(&pmlmepriv->assoc_req, &pmlmepriv->assoc_req_len);
+ rtw_buf_free(&pmlmepriv->assoc_rsp, &pmlmepriv->assoc_rsp_len);
+ rtw_free_mlme_ie_data(&pmlmepriv->wps_beacon_ie, &pmlmepriv->wps_beacon_ie_len);
+ rtw_free_mlme_ie_data(&pmlmepriv->wps_probe_req_ie, &pmlmepriv->wps_probe_req_ie_len);
+ rtw_free_mlme_ie_data(&pmlmepriv->wps_probe_resp_ie, &pmlmepriv->wps_probe_resp_ie_len);
+ rtw_free_mlme_ie_data(&pmlmepriv->wps_assoc_resp_ie, &pmlmepriv->wps_assoc_resp_ie_len);
+
+ rtw_free_mlme_ie_data(&pmlmepriv->p2p_beacon_ie, &pmlmepriv->p2p_beacon_ie_len);
+ rtw_free_mlme_ie_data(&pmlmepriv->p2p_probe_req_ie, &pmlmepriv->p2p_probe_req_ie_len);
+ rtw_free_mlme_ie_data(&pmlmepriv->p2p_probe_resp_ie, &pmlmepriv->p2p_probe_resp_ie_len);
+ rtw_free_mlme_ie_data(&pmlmepriv->p2p_go_probe_resp_ie, &pmlmepriv->p2p_go_probe_resp_ie_len);
+ rtw_free_mlme_ie_data(&pmlmepriv->p2p_assoc_req_ie, &pmlmepriv->p2p_assoc_req_ie_len);
+}
+#else
+void rtw_free_mlme_priv_ie_data(struct mlme_priv *pmlmepriv)
+{
+}
+#endif
+
+void rtw_free_mlme_priv(struct mlme_priv *pmlmepriv)
+{
+ rtw_free_mlme_priv_ie_data(pmlmepriv);
+
+ if (pmlmepriv) {
+ if (pmlmepriv->free_bss_buf)
+ vfree(pmlmepriv->free_bss_buf);
+ }
+}
+
+struct wlan_network *_rtw_alloc_network(struct mlme_priv *pmlmepriv)/* _queue *free_queue) */
+{
+ struct wlan_network *pnetwork;
+ struct __queue *free_queue = &pmlmepriv->free_bss_pool;
+ struct list_head *plist = NULL;
+
+ spin_lock_bh(&free_queue->lock);
+
+ if (list_empty(&free_queue->queue)) {
+ pnetwork = NULL;
+ goto exit;
+ }
+ plist = free_queue->queue.next;
+
+ pnetwork = container_of(plist , struct wlan_network, list);
+
+ list_del_init(&pnetwork->list);
+
+ RT_TRACE(_module_rtl871x_mlme_c_, _drv_info_, ("_rtw_alloc_network: ptr=%p\n", plist));
+ pnetwork->network_type = 0;
+ pnetwork->fixed = false;
+ pnetwork->last_scanned = jiffies;
+ pnetwork->aid = 0;
+ pnetwork->join_res = 0;
+
+ pmlmepriv->num_of_scanned++;
+
+exit:
+ spin_unlock_bh(&free_queue->lock);
+
+ return pnetwork;
+}
+
+static void _rtw_free_network(struct mlme_priv *pmlmepriv , struct wlan_network *pnetwork, u8 isfreeall)
+{
+ u32 curr_time, delta_time;
+ u32 lifetime = SCANQUEUE_LIFETIME;
+ struct __queue *free_queue = &(pmlmepriv->free_bss_pool);
+
+ if (pnetwork == NULL)
+ return;
+
+ if (pnetwork->fixed)
+ return;
+ curr_time = jiffies;
+ if ((check_fwstate(pmlmepriv, WIFI_ADHOC_MASTER_STATE)) ||
+ (check_fwstate(pmlmepriv, WIFI_ADHOC_STATE)))
+ lifetime = 1;
+ if (!isfreeall) {
+ delta_time = (curr_time - pnetwork->last_scanned)/HZ;
+ if (delta_time < lifetime)/* unit:sec */
+ return;
+ }
+ spin_lock_bh(&free_queue->lock);
+ list_del_init(&(pnetwork->list));
+ list_add_tail(&(pnetwork->list), &(free_queue->queue));
+ pmlmepriv->num_of_scanned--;
+ spin_unlock_bh(&free_queue->lock);
+}
+
+void _rtw_free_network_nolock(struct mlme_priv *pmlmepriv, struct wlan_network *pnetwork)
+{
+ struct __queue *free_queue = &(pmlmepriv->free_bss_pool);
+
+ if (pnetwork == NULL)
+ return;
+ if (pnetwork->fixed)
+ return;
+ list_del_init(&(pnetwork->list));
+ list_add_tail(&(pnetwork->list), get_list_head(free_queue));
+ pmlmepriv->num_of_scanned--;
+}
+
+/*
+ return the wlan_network with the matching addr
+
+ Shall be calle under atomic context... to avoid possible racing condition...
+*/
+struct wlan_network *rtw_find_network(struct __queue *scanned_queue, u8 *addr)
+{
+ struct list_head *phead, *plist;
+ struct wlan_network *pnetwork = NULL;
+ u8 zero_addr[ETH_ALEN] = {0, 0, 0, 0, 0, 0};
+
+ if (!memcmp(zero_addr, addr, ETH_ALEN)) {
+ pnetwork = NULL;
+ goto exit;
+ }
+ phead = get_list_head(scanned_queue);
+ plist = phead->next;
+
+ while (plist != phead) {
+ pnetwork = container_of(plist, struct wlan_network , list);
+ if (!memcmp(addr, pnetwork->network.MacAddress, ETH_ALEN))
+ break;
+ plist = plist->next;
+ }
+ if (plist == phead)
+ pnetwork = NULL;
+exit:
+ return pnetwork;
+}
+
+
+void rtw_free_network_queue(struct adapter *padapter, u8 isfreeall)
+{
+ struct list_head *phead, *plist;
+ struct wlan_network *pnetwork;
+ struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
+ struct __queue *scanned_queue = &pmlmepriv->scanned_queue;
+
+ spin_lock_bh(&scanned_queue->lock);
+
+ phead = get_list_head(scanned_queue);
+ plist = phead->next;
+
+ while (phead != plist) {
+ pnetwork = container_of(plist, struct wlan_network, list);
+
+ plist = plist->next;
+
+ _rtw_free_network(pmlmepriv, pnetwork, isfreeall);
+ }
+ spin_unlock_bh(&scanned_queue->lock);
+}
+
+int rtw_if_up(struct adapter *padapter)
+{
+ int res;
+
+ if (padapter->bDriverStopped || padapter->bSurpriseRemoved ||
+ (check_fwstate(&padapter->mlmepriv, _FW_LINKED) == false)) {
+ RT_TRACE(_module_rtl871x_mlme_c_, _drv_info_,
+ ("rtw_if_up:bDriverStopped(%d) OR bSurpriseRemoved(%d)",
+ padapter->bDriverStopped, padapter->bSurpriseRemoved));
+ res = false;
+ } else {
+ res = true;
+ }
+ return res;
+}
+
+void rtw_generate_random_ibss(u8 *pibss)
+{
+ u32 curtime = jiffies;
+
+ pibss[0] = 0x02; /* in ad-hoc mode bit1 must set to 1 */
+ pibss[1] = 0x11;
+ pibss[2] = 0x87;
+ pibss[3] = (u8)(curtime & 0xff);/* p[0]; */
+ pibss[4] = (u8)((curtime>>8) & 0xff);/* p[1]; */
+ pibss[5] = (u8)((curtime>>16) & 0xff);/* p[2]; */
+ return;
+}
+
+u8 *rtw_get_capability_from_ie(u8 *ie)
+{
+ return ie + 8 + 2;
+}
+
+
+u16 rtw_get_capability(struct wlan_bssid_ex *bss)
+{
+ __le16 val;
+
+ memcpy((u8 *)&val, rtw_get_capability_from_ie(bss->IEs), 2);
+
+ return le16_to_cpu(val);
+}
+
+u8 *rtw_get_beacon_interval_from_ie(u8 *ie)
+{
+ return ie + 8;
+}
+
+static struct wlan_network *rtw_alloc_network(struct mlme_priv *pmlmepriv)
+{
+ return _rtw_alloc_network(pmlmepriv);
+}
+
+static void rtw_free_network_nolock(struct mlme_priv *pmlmepriv,
+ struct wlan_network *pnetwork)
+{
+ _rtw_free_network_nolock(pmlmepriv, pnetwork);
+}
+
+int rtw_is_same_ibss(struct adapter *adapter, struct wlan_network *pnetwork)
+{
+ int ret = true;
+ struct security_priv *psecuritypriv = &adapter->securitypriv;
+
+ if ((psecuritypriv->dot11PrivacyAlgrthm != _NO_PRIVACY_) &&
+ (pnetwork->network.Privacy == 0))
+ ret = false;
+ else if ((psecuritypriv->dot11PrivacyAlgrthm == _NO_PRIVACY_) &&
+ (pnetwork->network.Privacy == 1))
+ ret = false;
+ else
+ ret = true;
+ return ret;
+}
+
+static int is_same_ess(struct wlan_bssid_ex *a, struct wlan_bssid_ex *b)
+{
+ return (a->Ssid.SsidLength == b->Ssid.SsidLength) &&
+ !memcmp(a->Ssid.Ssid, b->Ssid.Ssid, a->Ssid.SsidLength);
+}
+
+int is_same_network(struct wlan_bssid_ex *src, struct wlan_bssid_ex *dst)
+{
+ u16 s_cap, d_cap;
+ __le16 le_scap, le_dcap;
+
+ memcpy((u8 *)&le_scap, rtw_get_capability_from_ie(src->IEs), 2);
+ memcpy((u8 *)&le_dcap, rtw_get_capability_from_ie(dst->IEs), 2);
+
+
+ s_cap = le16_to_cpu(le_scap);
+ d_cap = le16_to_cpu(le_dcap);
+
+ return ((src->Ssid.SsidLength == dst->Ssid.SsidLength) &&
+ ((!memcmp(src->MacAddress, dst->MacAddress, ETH_ALEN)) == true) &&
+ ((!memcmp(src->Ssid.Ssid, dst->Ssid.Ssid, src->Ssid.SsidLength)) == true) &&
+ ((s_cap & WLAN_CAPABILITY_IBSS) ==
+ (d_cap & WLAN_CAPABILITY_IBSS)) &&
+ ((s_cap & WLAN_CAPABILITY_BSS) ==
+ (d_cap & WLAN_CAPABILITY_BSS)));
+}
+
+struct wlan_network *rtw_get_oldest_wlan_network(struct __queue *scanned_queue)
+{
+ struct list_head *plist, *phead;
+ struct wlan_network *pwlan = NULL;
+ struct wlan_network *oldest = NULL;
+
+ phead = get_list_head(scanned_queue);
+
+ plist = phead->next;
+
+ while (1) {
+ if (phead == plist)
+ break;
+
+ pwlan = container_of(plist, struct wlan_network, list);
+
+ if (!pwlan->fixed) {
+ if (oldest == NULL || time_after(oldest->last_scanned, pwlan->last_scanned))
+ oldest = pwlan;
+ }
+
+ plist = plist->next;
+ }
+ return oldest;
+}
+
+void update_network(struct wlan_bssid_ex *dst, struct wlan_bssid_ex *src,
+ struct adapter *padapter, bool update_ie)
+{
+ long rssi_ori = dst->Rssi;
+ u8 sq_smp = src->PhyInfo.SignalQuality;
+ u8 ss_final;
+ u8 sq_final;
+ long rssi_final;
+
+ rtw_hal_antdiv_rssi_compared(padapter, dst, src); /* this will update src.Rssi, need consider again */
+
+ /* The rule below is 1/5 for sample value, 4/5 for history value */
+ if (check_fwstate(&padapter->mlmepriv, _FW_LINKED) && is_same_network(&(padapter->mlmepriv.cur_network.network), src)) {
+ /* Take the recvpriv's value for the connected AP*/
+ ss_final = padapter->recvpriv.signal_strength;
+ sq_final = padapter->recvpriv.signal_qual;
+ /* the rssi value here is undecorated, and will be used for antenna diversity */
+ if (sq_smp != 101) /* from the right channel */
+ rssi_final = (src->Rssi+dst->Rssi*4)/5;
+ else
+ rssi_final = rssi_ori;
+ } else {
+ if (sq_smp != 101) { /* from the right channel */
+ ss_final = ((u32)(src->PhyInfo.SignalStrength)+(u32)(dst->PhyInfo.SignalStrength)*4)/5;
+ sq_final = ((u32)(src->PhyInfo.SignalQuality)+(u32)(dst->PhyInfo.SignalQuality)*4)/5;
+ rssi_final = (src->Rssi+dst->Rssi*4)/5;
+ } else {
+ /* bss info not receiving from the right channel, use the original RX signal infos */
+ ss_final = dst->PhyInfo.SignalStrength;
+ sq_final = dst->PhyInfo.SignalQuality;
+ rssi_final = dst->Rssi;
+ }
+ }
+ if (update_ie)
+ memcpy((u8 *)dst, (u8 *)src, get_wlan_bssid_ex_sz(src));
+ dst->PhyInfo.SignalStrength = ss_final;
+ dst->PhyInfo.SignalQuality = sq_final;
+ dst->Rssi = rssi_final;
+
+}
+
+static void update_current_network(struct adapter *adapter, struct wlan_bssid_ex *pnetwork)
+{
+ struct mlme_priv *pmlmepriv = &(adapter->mlmepriv);
+
+ if ((check_fwstate(pmlmepriv, _FW_LINKED) == true) &&
+ (is_same_network(&(pmlmepriv->cur_network.network), pnetwork))) {
+ update_network(&(pmlmepriv->cur_network.network), pnetwork, adapter, true);
+ rtw_update_protection(adapter, (pmlmepriv->cur_network.network.IEs) + sizeof(struct ndis_802_11_fixed_ie),
+ pmlmepriv->cur_network.network.IELength);
+ }
+}
+
+/*
+Caller must hold pmlmepriv->lock first.
+*/
+void rtw_update_scanned_network(struct adapter *adapter, struct wlan_bssid_ex *target)
+{
+ struct list_head *plist, *phead;
+ u32 bssid_ex_sz;
+ struct mlme_priv *pmlmepriv = &(adapter->mlmepriv);
+ struct __queue *queue = &(pmlmepriv->scanned_queue);
+ struct wlan_network *pnetwork = NULL;
+ struct wlan_network *oldest = NULL;
+
+ spin_lock_bh(&queue->lock);
+ phead = get_list_head(queue);
+ plist = phead->next;
+
+ while (phead != plist) {
+ pnetwork = container_of(plist, struct wlan_network, list);
+
+ if (is_same_network(&(pnetwork->network), target))
+ break;
+ if ((oldest == ((struct wlan_network *)0)) ||
+ time_after(oldest->last_scanned, pnetwork->last_scanned))
+ oldest = pnetwork;
+ plist = plist->next;
+ }
+ /* If we didn't find a match, then get a new network slot to initialize
+ * with this beacon's information */
+ if (phead == plist) {
+ if (list_empty(&(pmlmepriv->free_bss_pool.queue))) {
+ /* If there are no more slots, expire the oldest */
+ pnetwork = oldest;
+
+ rtw_hal_get_def_var(adapter, HAL_DEF_CURRENT_ANTENNA, &(target->PhyInfo.Optimum_antenna));
+ memcpy(&(pnetwork->network), target, get_wlan_bssid_ex_sz(target));
+ /* variable initialize */
+ pnetwork->fixed = false;
+ pnetwork->last_scanned = jiffies;
+
+ pnetwork->network_type = 0;
+ pnetwork->aid = 0;
+ pnetwork->join_res = 0;
+
+ /* bss info not receiving from the right channel */
+ if (pnetwork->network.PhyInfo.SignalQuality == 101)
+ pnetwork->network.PhyInfo.SignalQuality = 0;
+ } else {
+ /* Otherwise just pull from the free list */
+
+ pnetwork = rtw_alloc_network(pmlmepriv); /* will update scan_time */
+
+ if (pnetwork == NULL) {
+ RT_TRACE(_module_rtl871x_mlme_c_, _drv_err_, ("\n\n\nsomething wrong here\n\n\n"));
+ goto exit;
+ }
+
+ bssid_ex_sz = get_wlan_bssid_ex_sz(target);
+ target->Length = bssid_ex_sz;
+ rtw_hal_get_def_var(adapter, HAL_DEF_CURRENT_ANTENNA, &(target->PhyInfo.Optimum_antenna));
+ memcpy(&(pnetwork->network), target, bssid_ex_sz);
+
+ pnetwork->last_scanned = jiffies;
+
+ /* bss info not receiving from the right channel */
+ if (pnetwork->network.PhyInfo.SignalQuality == 101)
+ pnetwork->network.PhyInfo.SignalQuality = 0;
+ list_add_tail(&(pnetwork->list), &(queue->queue));
+ }
+ } else {
+ /* we have an entry and we are going to update it. But this entry may
+ * be already expired. In this case we do the same as we found a new
+ * net and call the new_net handler
+ */
+ bool update_ie = true;
+
+ pnetwork->last_scanned = jiffies;
+
+ /* target.Reserved[0]== 1, means that scanned network is a bcn frame. */
+ if ((pnetwork->network.IELength > target->IELength) && (target->Reserved[0] == 1))
+ update_ie = false;
+
+ update_network(&(pnetwork->network), target, adapter, update_ie);
+ }
+
+exit:
+ spin_unlock_bh(&queue->lock);
+
+}
+
+static void rtw_add_network(struct adapter *adapter,
+ struct wlan_bssid_ex *pnetwork)
+{
+ update_current_network(adapter, pnetwork);
+ rtw_update_scanned_network(adapter, pnetwork);
+}
+
+/*
+ * select the desired network based on the capability of the (i)bss.
+ * check items: (1) security
+ * (2) network_type
+ * (3) WMM
+ * (4) HT
+ * (5) others
+ */
+static int rtw_is_desired_network(struct adapter *adapter, struct wlan_network *pnetwork)
+{
+ struct security_priv *psecuritypriv = &adapter->securitypriv;
+ struct mlme_priv *pmlmepriv = &adapter->mlmepriv;
+ u32 desired_encmode;
+ u32 privacy;
+
+ /* u8 wps_ie[512]; */
+ uint wps_ielen;
+
+ int bselected = true;
+
+ desired_encmode = psecuritypriv->ndisencryptstatus;
+ privacy = pnetwork->network.Privacy;
+
+ if (check_fwstate(pmlmepriv, WIFI_UNDER_WPS)) {
+ if (rtw_get_wps_ie(pnetwork->network.IEs+_FIXED_IE_LENGTH_, pnetwork->network.IELength-_FIXED_IE_LENGTH_, NULL, &wps_ielen) != NULL)
+ return true;
+ else
+ return false;
+ }
+ if (adapter->registrypriv.wifi_spec == 1) { /* for correct flow of 8021X to do.... */
+ if ((desired_encmode == Ndis802_11EncryptionDisabled) && (privacy != 0))
+ bselected = false;
+ }
+
+
+ if ((desired_encmode != Ndis802_11EncryptionDisabled) && (privacy == 0)) {
+ DBG_88E("desired_encmode: %d, privacy: %d\n", desired_encmode, privacy);
+ bselected = false;
+ }
+
+ if (check_fwstate(pmlmepriv, WIFI_ADHOC_STATE) == true) {
+ if (pnetwork->network.InfrastructureMode != pmlmepriv->cur_network.network.InfrastructureMode)
+ bselected = false;
+ }
+
+
+ return bselected;
+}
+
+/* TODO: Perry: For Power Management */
+void rtw_atimdone_event_callback(struct adapter *adapter , u8 *pbuf)
+{
+ RT_TRACE(_module_rtl871x_mlme_c_, _drv_err_, ("receive atimdone_evet\n"));
+ return;
+}
+
+
+void rtw_survey_event_callback(struct adapter *adapter, u8 *pbuf)
+{
+ u32 len;
+ struct wlan_bssid_ex *pnetwork;
+ struct mlme_priv *pmlmepriv = &(adapter->mlmepriv);
+
+ pnetwork = (struct wlan_bssid_ex *)pbuf;
+
+ RT_TRACE(_module_rtl871x_mlme_c_, _drv_info_, ("rtw_survey_event_callback, ssid=%s\n", pnetwork->Ssid.Ssid));
+
+ len = get_wlan_bssid_ex_sz(pnetwork);
+ if (len > (sizeof(struct wlan_bssid_ex))) {
+ RT_TRACE(_module_rtl871x_mlme_c_, _drv_err_, ("\n****rtw_survey_event_callback: return a wrong bss ***\n"));
+ return;
+ }
+ spin_lock_bh(&pmlmepriv->lock);
+
+ /* update IBSS_network 's timestamp */
+ if ((check_fwstate(pmlmepriv, WIFI_ADHOC_MASTER_STATE)) == true) {
+ if (!memcmp(&(pmlmepriv->cur_network.network.MacAddress), pnetwork->MacAddress, ETH_ALEN)) {
+ struct wlan_network *ibss_wlan = NULL;
+
+ memcpy(pmlmepriv->cur_network.network.IEs, pnetwork->IEs, 8);
+ spin_lock_bh(&(pmlmepriv->scanned_queue.lock));
+ ibss_wlan = rtw_find_network(&pmlmepriv->scanned_queue, pnetwork->MacAddress);
+ if (ibss_wlan) {
+ memcpy(ibss_wlan->network.IEs , pnetwork->IEs, 8);
+ spin_unlock_bh(&pmlmepriv->scanned_queue.lock);
+ goto exit;
+ }
+ spin_unlock_bh(&pmlmepriv->scanned_queue.lock);
+ }
+ }
+
+ /* lock pmlmepriv->lock when you accessing network_q */
+ if ((check_fwstate(pmlmepriv, _FW_UNDER_LINKING)) == false) {
+ if (pnetwork->Ssid.Ssid[0] == 0)
+ pnetwork->Ssid.SsidLength = 0;
+ rtw_add_network(adapter, pnetwork);
+ }
+
+exit:
+
+ spin_unlock_bh(&pmlmepriv->lock);
+ return;
+}
+
+void rtw_surveydone_event_callback(struct adapter *adapter, u8 *pbuf)
+{
+ struct mlme_priv *pmlmepriv = &(adapter->mlmepriv);
+ struct mlme_ext_priv *pmlmeext;
+
+ spin_lock_bh(&pmlmepriv->lock);
+
+ if (pmlmepriv->wps_probe_req_ie) {
+ pmlmepriv->wps_probe_req_ie_len = 0;
+ kfree(pmlmepriv->wps_probe_req_ie);
+ pmlmepriv->wps_probe_req_ie = NULL;
+ }
+
+ RT_TRACE(_module_rtl871x_mlme_c_, _drv_info_, ("rtw_surveydone_event_callback: fw_state:%x\n\n", get_fwstate(pmlmepriv)));
+
+ if (check_fwstate(pmlmepriv, _FW_UNDER_SURVEY)) {
+ del_timer_sync(&pmlmepriv->scan_to_timer);
+ _clr_fwstate_(pmlmepriv, _FW_UNDER_SURVEY);
+ } else {
+ RT_TRACE(_module_rtl871x_mlme_c_, _drv_err_, ("nic status=%x, survey done event comes too late!\n", get_fwstate(pmlmepriv)));
+ }
+
+ rtw_set_signal_stat_timer(&adapter->recvpriv);
+
+ if (pmlmepriv->to_join) {
+ if ((check_fwstate(pmlmepriv, WIFI_ADHOC_STATE) == true)) {
+ if (check_fwstate(pmlmepriv, _FW_LINKED) == false) {
+ set_fwstate(pmlmepriv, _FW_UNDER_LINKING);
+
+ if (rtw_select_and_join_from_scanned_queue(pmlmepriv) == _SUCCESS) {
+ mod_timer(&pmlmepriv->assoc_timer,
+ jiffies + msecs_to_jiffies(MAX_JOIN_TIMEOUT));
+ } else {
+ struct wlan_bssid_ex *pdev_network = &(adapter->registrypriv.dev_network);
+ u8 *pibss = adapter->registrypriv.dev_network.MacAddress;
+
+ _clr_fwstate_(pmlmepriv, _FW_UNDER_SURVEY);
+
+ RT_TRACE(_module_rtl871x_mlme_c_, _drv_err_, ("switching to adhoc master\n"));
+
+ memcpy(&pdev_network->Ssid, &pmlmepriv->assoc_ssid, sizeof(struct ndis_802_11_ssid));
+
+ rtw_update_registrypriv_dev_network(adapter);
+ rtw_generate_random_ibss(pibss);
+
+ pmlmepriv->fw_state = WIFI_ADHOC_MASTER_STATE;
+
+ if (rtw_createbss_cmd(adapter) != _SUCCESS)
+ RT_TRACE(_module_rtl871x_mlme_c_, _drv_err_, ("Error=>rtw_createbss_cmd status FAIL\n"));
+ pmlmepriv->to_join = false;
+ }
+ }
+ } else {
+ int s_ret;
+ set_fwstate(pmlmepriv, _FW_UNDER_LINKING);
+ pmlmepriv->to_join = false;
+ s_ret = rtw_select_and_join_from_scanned_queue(pmlmepriv);
+ if (_SUCCESS == s_ret) {
+ mod_timer(&pmlmepriv->assoc_timer,
+ jiffies + msecs_to_jiffies(MAX_JOIN_TIMEOUT));
+ } else if (s_ret == 2) { /* there is no need to wait for join */
+ _clr_fwstate_(pmlmepriv, _FW_UNDER_LINKING);
+ rtw_indicate_connect(adapter);
+ } else {
+ DBG_88E("try_to_join, but select scanning queue fail, to_roaming:%d\n", pmlmepriv->to_roaming);
+ if (pmlmepriv->to_roaming != 0) {
+ if (--pmlmepriv->to_roaming == 0 ||
+ _SUCCESS != rtw_sitesurvey_cmd(adapter, &pmlmepriv->assoc_ssid, 1, NULL, 0)) {
+ pmlmepriv->to_roaming = 0;
+ rtw_free_assoc_resources(adapter, 1);
+ rtw_indicate_disconnect(adapter);
+ } else {
+ pmlmepriv->to_join = true;
+ }
+ }
+ _clr_fwstate_(pmlmepriv, _FW_UNDER_LINKING);
+ }
+ }
+ }
+
+ indicate_wx_scan_complete_event(adapter);
+
+ spin_unlock_bh(&pmlmepriv->lock);
+
+ rtw_os_xmit_schedule(adapter);
+
+ pmlmeext = &adapter->mlmeextpriv;
+}
+
+void rtw_dummy_event_callback(struct adapter *adapter , u8 *pbuf)
+{
+}
+
+void rtw_fwdbg_event_callback(struct adapter *adapter , u8 *pbuf)
+{
+}
+
+static void free_scanqueue(struct mlme_priv *pmlmepriv)
+{
+ struct __queue *free_queue = &pmlmepriv->free_bss_pool;
+ struct __queue *scan_queue = &pmlmepriv->scanned_queue;
+ struct list_head *plist, *phead, *ptemp;
+
+ RT_TRACE(_module_rtl871x_mlme_c_, _drv_notice_, ("+free_scanqueue\n"));
+ spin_lock_bh(&scan_queue->lock);
+ spin_lock_bh(&free_queue->lock);
+
+ phead = get_list_head(scan_queue);
+ plist = phead->next;
+
+ while (plist != phead) {
+ ptemp = plist->next;
+ list_del_init(plist);
+ list_add_tail(plist, &free_queue->queue);
+ plist = ptemp;
+ pmlmepriv->num_of_scanned--;
+ }
+
+ spin_unlock_bh(&free_queue->lock);
+ spin_unlock_bh(&scan_queue->lock);
+}
+
+/*
+*rtw_free_assoc_resources: the caller has to lock pmlmepriv->lock
+*/
+void rtw_free_assoc_resources(struct adapter *adapter, int lock_scanned_queue)
+{
+ struct wlan_network *pwlan = NULL;
+ struct mlme_priv *pmlmepriv = &adapter->mlmepriv;
+ struct sta_priv *pstapriv = &adapter->stapriv;
+ struct wlan_network *tgt_network = &pmlmepriv->cur_network;
+
+ RT_TRACE(_module_rtl871x_mlme_c_, _drv_notice_, ("+rtw_free_assoc_resources\n"));
+ RT_TRACE(_module_rtl871x_mlme_c_, _drv_info_,
+ ("tgt_network->network.MacAddress=%pM ssid=%s\n",
+ tgt_network->network.MacAddress, tgt_network->network.Ssid.Ssid));
+
+ if (check_fwstate(pmlmepriv, WIFI_STATION_STATE | WIFI_AP_STATE)) {
+ struct sta_info *psta;
+
+ psta = rtw_get_stainfo(&adapter->stapriv, tgt_network->network.MacAddress);
+
+ spin_lock_bh(&(pstapriv->sta_hash_lock));
+ rtw_free_stainfo(adapter, psta);
+ spin_unlock_bh(&pstapriv->sta_hash_lock);
+ }
+
+ if (check_fwstate(pmlmepriv, WIFI_ADHOC_STATE | WIFI_ADHOC_MASTER_STATE | WIFI_AP_STATE)) {
+ struct sta_info *psta;
+
+ rtw_free_all_stainfo(adapter);
+
+ psta = rtw_get_bcmc_stainfo(adapter);
+ spin_lock_bh(&(pstapriv->sta_hash_lock));
+ rtw_free_stainfo(adapter, psta);
+ spin_unlock_bh(&pstapriv->sta_hash_lock);
+
+ rtw_init_bcmc_stainfo(adapter);
+ }
+
+ if (lock_scanned_queue)
+ spin_lock_bh(&(pmlmepriv->scanned_queue.lock));
+
+ pwlan = rtw_find_network(&pmlmepriv->scanned_queue, tgt_network->network.MacAddress);
+ if (pwlan)
+ pwlan->fixed = false;
+ else
+ RT_TRACE(_module_rtl871x_mlme_c_, _drv_err_, ("rtw_free_assoc_resources:pwlan==NULL\n\n"));
+
+ if ((check_fwstate(pmlmepriv, WIFI_ADHOC_MASTER_STATE) && (adapter->stapriv.asoc_sta_count == 1)))
+ rtw_free_network_nolock(pmlmepriv, pwlan);
+
+ if (lock_scanned_queue)
+ spin_unlock_bh(&pmlmepriv->scanned_queue.lock);
+ pmlmepriv->key_mask = 0;
+}
+
+/*
+*rtw_indicate_connect: the caller has to lock pmlmepriv->lock
+*/
+void rtw_indicate_connect(struct adapter *padapter)
+{
+ struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
+
+ RT_TRACE(_module_rtl871x_mlme_c_, _drv_err_, ("+rtw_indicate_connect\n"));
+
+ pmlmepriv->to_join = false;
+
+ if (!check_fwstate(&padapter->mlmepriv, _FW_LINKED)) {
+ set_fwstate(pmlmepriv, _FW_LINKED);
+
+ rtw_led_control(padapter, LED_CTL_LINK);
+
+ rtw_os_indicate_connect(padapter);
+ }
+
+ pmlmepriv->to_roaming = 0;
+
+ rtw_set_scan_deny(padapter, 3000);
+
+ RT_TRACE(_module_rtl871x_mlme_c_, _drv_err_, ("-rtw_indicate_connect: fw_state=0x%08x\n", get_fwstate(pmlmepriv)));
+}
+
+/*
+*rtw_indicate_disconnect: the caller has to lock pmlmepriv->lock
+*/
+void rtw_indicate_disconnect(struct adapter *padapter)
+{
+ struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
+
+ RT_TRACE(_module_rtl871x_mlme_c_, _drv_err_, ("+rtw_indicate_disconnect\n"));
+
+ _clr_fwstate_(pmlmepriv, _FW_UNDER_LINKING | WIFI_UNDER_WPS);
+
+
+ if (pmlmepriv->to_roaming > 0)
+ _clr_fwstate_(pmlmepriv, _FW_LINKED);
+
+ if (check_fwstate(&padapter->mlmepriv, _FW_LINKED) ||
+ (pmlmepriv->to_roaming <= 0)) {
+ rtw_os_indicate_disconnect(padapter);
+
+ _clr_fwstate_(pmlmepriv, _FW_LINKED);
+ rtw_led_control(padapter, LED_CTL_NO_LINK);
+ rtw_clear_scan_deny(padapter);
+ }
+
+ rtw_lps_ctrl_wk_cmd(padapter, LPS_CTRL_DISCONNECT, 1);
+}
+
+inline void rtw_indicate_scan_done(struct adapter *padapter, bool aborted)
+{
+ rtw_os_indicate_scan_done(padapter, aborted);
+}
+
+void rtw_scan_abort(struct adapter *adapter)
+{
+ u32 start;
+ struct mlme_priv *pmlmepriv = &(adapter->mlmepriv);
+ struct mlme_ext_priv *pmlmeext = &(adapter->mlmeextpriv);
+
+ start = jiffies;
+ pmlmeext->scan_abort = true;
+ while (check_fwstate(pmlmepriv, _FW_UNDER_SURVEY) &&
+ rtw_get_passing_time_ms(start) <= 200) {
+ if (adapter->bDriverStopped || adapter->bSurpriseRemoved)
+ break;
+ DBG_88E(FUNC_NDEV_FMT"fw_state=_FW_UNDER_SURVEY!\n", FUNC_NDEV_ARG(adapter->pnetdev));
+ msleep(20);
+ }
+ if (check_fwstate(pmlmepriv, _FW_UNDER_SURVEY)) {
+ if (!adapter->bDriverStopped && !adapter->bSurpriseRemoved)
+ DBG_88E(FUNC_NDEV_FMT"waiting for scan_abort time out!\n", FUNC_NDEV_ARG(adapter->pnetdev));
+ rtw_indicate_scan_done(adapter, true);
+ }
+ pmlmeext->scan_abort = false;
+}
+
+static struct sta_info *rtw_joinbss_update_stainfo(struct adapter *padapter, struct wlan_network *pnetwork)
+{
+ int i;
+ struct sta_info *bmc_sta, *psta = NULL;
+ struct recv_reorder_ctrl *preorder_ctrl;
+ struct sta_priv *pstapriv = &padapter->stapriv;
+
+ psta = rtw_get_stainfo(pstapriv, pnetwork->network.MacAddress);
+ if (psta == NULL)
+ psta = rtw_alloc_stainfo(pstapriv, pnetwork->network.MacAddress);
+
+ if (psta) { /* update ptarget_sta */
+ DBG_88E("%s\n", __func__);
+ psta->aid = pnetwork->join_res;
+ psta->mac_id = 0;
+ /* sta mode */
+ rtw_hal_set_odm_var(padapter, HAL_ODM_STA_INFO, psta, true);
+ /* security related */
+ if (padapter->securitypriv.dot11AuthAlgrthm == dot11AuthAlgrthm_8021X) {
+ padapter->securitypriv.binstallGrpkey = false;
+ padapter->securitypriv.busetkipkey = false;
+ padapter->securitypriv.bgrpkey_handshake = false;
+ psta->ieee8021x_blocked = true;
+ psta->dot118021XPrivacy = padapter->securitypriv.dot11PrivacyAlgrthm;
+ memset((u8 *)&psta->dot118021x_UncstKey, 0, sizeof(union Keytype));
+ memset((u8 *)&psta->dot11tkiprxmickey, 0, sizeof(union Keytype));
+ memset((u8 *)&psta->dot11tkiptxmickey, 0, sizeof(union Keytype));
+ memset((u8 *)&psta->dot11txpn, 0, sizeof(union pn48));
+ memset((u8 *)&psta->dot11rxpn, 0, sizeof(union pn48));
+ }
+ /*
+ * Commented by Albert 2012/07/21
+ * When doing the WPS, the wps_ie_len won't equal to 0
+ * And the Wi-Fi driver shouldn't allow the data
+ * packet to be tramsmitted.
+ */
+ if (padapter->securitypriv.wps_ie_len != 0) {
+ psta->ieee8021x_blocked = true;
+ padapter->securitypriv.wps_ie_len = 0;
+ }
+ /* for A-MPDU Rx reordering buffer control for bmc_sta & sta_info */
+ /* if A-MPDU Rx is enabled, resetting rx_ordering_ctrl wstart_b(indicate_seq) to default value = 0xffff */
+ /* todo: check if AP can send A-MPDU packets */
+ for (i = 0; i < 16; i++) {
+ /* preorder_ctrl = &precvpriv->recvreorder_ctrl[i]; */
+ preorder_ctrl = &psta->recvreorder_ctrl[i];
+ preorder_ctrl->enable = false;
+ preorder_ctrl->indicate_seq = 0xffff;
+ preorder_ctrl->wend_b = 0xffff;
+ preorder_ctrl->wsize_b = 64;/* max_ampdu_sz; ex. 32(kbytes) -> wsize_b = 32 */
+ }
+ bmc_sta = rtw_get_bcmc_stainfo(padapter);
+ if (bmc_sta) {
+ for (i = 0; i < 16; i++) {
+ /* preorder_ctrl = &precvpriv->recvreorder_ctrl[i]; */
+ preorder_ctrl = &bmc_sta->recvreorder_ctrl[i];
+ preorder_ctrl->enable = false;
+ preorder_ctrl->indicate_seq = 0xffff;
+ preorder_ctrl->wend_b = 0xffff;
+ preorder_ctrl->wsize_b = 64;/* max_ampdu_sz; ex. 32(kbytes) -> wsize_b = 32 */
+ }
+ }
+ /* misc. */
+ update_sta_info(padapter, psta);
+ }
+ return psta;
+}
+
+/* pnetwork: returns from rtw_joinbss_event_callback */
+/* ptarget_wlan: found from scanned_queue */
+static void rtw_joinbss_update_network(struct adapter *padapter, struct wlan_network *ptarget_wlan, struct wlan_network *pnetwork)
+{
+ struct mlme_priv *pmlmepriv = &(padapter->mlmepriv);
+ struct wlan_network *cur_network = &(pmlmepriv->cur_network);
+
+ DBG_88E("%s\n", __func__);
+
+ RT_TRACE(_module_rtl871x_mlme_c_, _drv_info_,
+ ("\nfw_state:%x, BSSID:%pM\n",
+ get_fwstate(pmlmepriv), pnetwork->network.MacAddress));
+
+
+ /* why not use ptarget_wlan?? */
+ memcpy(&cur_network->network, &pnetwork->network, pnetwork->network.Length);
+ /* some IEs in pnetwork is wrong, so we should use ptarget_wlan IEs */
+ cur_network->network.IELength = ptarget_wlan->network.IELength;
+ memcpy(&cur_network->network.IEs[0], &ptarget_wlan->network.IEs[0], MAX_IE_SZ);
+
+ cur_network->aid = pnetwork->join_res;
+
+
+ rtw_set_signal_stat_timer(&padapter->recvpriv);
+ padapter->recvpriv.signal_strength = ptarget_wlan->network.PhyInfo.SignalStrength;
+ padapter->recvpriv.signal_qual = ptarget_wlan->network.PhyInfo.SignalQuality;
+ /* the ptarget_wlan->network.Rssi is raw data, we use ptarget_wlan->network.PhyInfo.SignalStrength instead (has scaled) */
+ padapter->recvpriv.rssi = translate_percentage_to_dbm(ptarget_wlan->network.PhyInfo.SignalStrength);
+ rtw_set_signal_stat_timer(&padapter->recvpriv);
+
+ /* update fw_state will clr _FW_UNDER_LINKING here indirectly */
+ switch (pnetwork->network.InfrastructureMode) {
+ case Ndis802_11Infrastructure:
+ if (pmlmepriv->fw_state&WIFI_UNDER_WPS)
+ pmlmepriv->fw_state = WIFI_STATION_STATE|WIFI_UNDER_WPS;
+ else
+ pmlmepriv->fw_state = WIFI_STATION_STATE;
+ break;
+ case Ndis802_11IBSS:
+ pmlmepriv->fw_state = WIFI_ADHOC_STATE;
+ break;
+ default:
+ pmlmepriv->fw_state = WIFI_NULL_STATE;
+ RT_TRACE(_module_rtl871x_mlme_c_, _drv_err_, ("Invalid network_mode\n"));
+ break;
+ }
+
+ rtw_update_protection(padapter, (cur_network->network.IEs) +
+ sizeof(struct ndis_802_11_fixed_ie),
+ (cur_network->network.IELength));
+ rtw_update_ht_cap(padapter, cur_network->network.IEs, cur_network->network.IELength);
+}
+
+/* Notes: the function could be > passive_level (the same context as Rx tasklet) */
+/* pnetwork: returns from rtw_joinbss_event_callback */
+/* ptarget_wlan: found from scanned_queue */
+/* if join_res > 0, for (fw_state == WIFI_STATION_STATE), we check if "ptarget_sta" & "ptarget_wlan" exist. */
+/* if join_res > 0, for (fw_state == WIFI_ADHOC_STATE), we only check if "ptarget_wlan" exist. */
+/* if join_res > 0, update "cur_network->network" from "pnetwork->network" if (ptarget_wlan != NULL). */
+
+void rtw_joinbss_event_prehandle(struct adapter *adapter, u8 *pbuf)
+{
+ struct sta_info *ptarget_sta = NULL, *pcur_sta = NULL;
+ struct sta_priv *pstapriv = &adapter->stapriv;
+ struct mlme_priv *pmlmepriv = &(adapter->mlmepriv);
+ struct wlan_network *pnetwork = (struct wlan_network *)pbuf;
+ struct wlan_network *cur_network = &(pmlmepriv->cur_network);
+ struct wlan_network *pcur_wlan = NULL, *ptarget_wlan = NULL;
+ unsigned int the_same_macaddr = false;
+
+ RT_TRACE(_module_rtl871x_mlme_c_, _drv_info_, ("joinbss event call back received with res=%d\n", pnetwork->join_res));
+
+ rtw_get_encrypt_decrypt_from_registrypriv(adapter);
+
+
+ if (pmlmepriv->assoc_ssid.SsidLength == 0)
+ RT_TRACE(_module_rtl871x_mlme_c_, _drv_err_, ("@@@@@ joinbss event call back for Any SSid\n"));
+ else
+ RT_TRACE(_module_rtl871x_mlme_c_, _drv_err_, ("@@@@@ rtw_joinbss_event_callback for SSid:%s\n", pmlmepriv->assoc_ssid.Ssid));
+
+ the_same_macaddr = !memcmp(pnetwork->network.MacAddress, cur_network->network.MacAddress, ETH_ALEN);
+
+ pnetwork->network.Length = get_wlan_bssid_ex_sz(&pnetwork->network);
+ if (pnetwork->network.Length > sizeof(struct wlan_bssid_ex)) {
+ RT_TRACE(_module_rtl871x_mlme_c_, _drv_err_, ("\n\n ***joinbss_evt_callback return a wrong bss ***\n\n"));
+ return;
+ }
+
+ spin_lock_bh(&pmlmepriv->lock);
+
+ RT_TRACE(_module_rtl871x_mlme_c_, _drv_info_, ("\nrtw_joinbss_event_callback!! _enter_critical\n"));
+
+ if (pnetwork->join_res > 0) {
+ spin_lock_bh(&(pmlmepriv->scanned_queue.lock));
+ if (check_fwstate(pmlmepriv, _FW_UNDER_LINKING)) {
+ /* s1. find ptarget_wlan */
+ if (check_fwstate(pmlmepriv, _FW_LINKED)) {
+ if (the_same_macaddr) {
+ ptarget_wlan = rtw_find_network(&pmlmepriv->scanned_queue, cur_network->network.MacAddress);
+ } else {
+ pcur_wlan = rtw_find_network(&pmlmepriv->scanned_queue, cur_network->network.MacAddress);
+ if (pcur_wlan)
+ pcur_wlan->fixed = false;
+
+ pcur_sta = rtw_get_stainfo(pstapriv, cur_network->network.MacAddress);
+ if (pcur_sta) {
+ spin_lock_bh(&(pstapriv->sta_hash_lock));
+ rtw_free_stainfo(adapter, pcur_sta);
+ spin_unlock_bh(&pstapriv->sta_hash_lock);
+ }
+
+ ptarget_wlan = rtw_find_network(&pmlmepriv->scanned_queue, pnetwork->network.MacAddress);
+ if (check_fwstate(pmlmepriv, WIFI_STATION_STATE) == true) {
+ if (ptarget_wlan)
+ ptarget_wlan->fixed = true;
+ }
+ }
+ } else {
+ ptarget_wlan = rtw_find_network(&pmlmepriv->scanned_queue, pnetwork->network.MacAddress);
+ if (check_fwstate(pmlmepriv, WIFI_STATION_STATE) == true) {
+ if (ptarget_wlan)
+ ptarget_wlan->fixed = true;
+ }
+ }
+
+ /* s2. update cur_network */
+ if (ptarget_wlan) {
+ rtw_joinbss_update_network(adapter, ptarget_wlan, pnetwork);
+ } else {
+ RT_TRACE(_module_rtl871x_mlme_c_, _drv_err_, ("Can't find ptarget_wlan when joinbss_event callback\n"));
+ spin_unlock_bh(&pmlmepriv->scanned_queue.lock);
+ goto ignore_joinbss_callback;
+ }
+
+
+ /* s3. find ptarget_sta & update ptarget_sta after update cur_network only for station mode */
+ if (check_fwstate(pmlmepriv, WIFI_STATION_STATE) == true) {
+ ptarget_sta = rtw_joinbss_update_stainfo(adapter, pnetwork);
+ if (ptarget_sta == NULL) {
+ RT_TRACE(_module_rtl871x_mlme_c_, _drv_err_, ("Can't update stainfo when joinbss_event callback\n"));
+ spin_unlock_bh(&pmlmepriv->scanned_queue.lock);
+ goto ignore_joinbss_callback;
+ }
+ }
+
+ /* s4. indicate connect */
+ if (check_fwstate(pmlmepriv, WIFI_STATION_STATE) == true) {
+ rtw_indicate_connect(adapter);
+ } else {
+ /* adhoc mode will rtw_indicate_connect when rtw_stassoc_event_callback */
+ RT_TRACE(_module_rtl871x_mlme_c_, _drv_info_, ("adhoc mode, fw_state:%x", get_fwstate(pmlmepriv)));
+ }
+
+ /* s5. Cancle assoc_timer */
+ del_timer_sync(&pmlmepriv->assoc_timer);
+
+ RT_TRACE(_module_rtl871x_mlme_c_, _drv_info_, ("Cancle assoc_timer\n"));
+
+ } else {
+ RT_TRACE(_module_rtl871x_mlme_c_, _drv_err_, ("rtw_joinbss_event_callback err: fw_state:%x", get_fwstate(pmlmepriv)));
+ spin_unlock_bh(&pmlmepriv->scanned_queue.lock);
+ goto ignore_joinbss_callback;
+ }
+
+ spin_unlock_bh(&pmlmepriv->scanned_queue.lock);
+
+ } else if (pnetwork->join_res == -4) {
+ rtw_reset_securitypriv(adapter);
+ mod_timer(&pmlmepriv->assoc_timer,
+ jiffies + msecs_to_jiffies(1));
+
+ if ((check_fwstate(pmlmepriv, _FW_UNDER_LINKING)) == true) {
+ RT_TRACE(_module_rtl871x_mlme_c_, _drv_err_, ("fail! clear _FW_UNDER_LINKING ^^^fw_state=%x\n", get_fwstate(pmlmepriv)));
+ _clr_fwstate_(pmlmepriv, _FW_UNDER_LINKING);
+ }
+ } else { /* if join_res < 0 (join fails), then try again */
+ mod_timer(&pmlmepriv->assoc_timer,
+ jiffies + msecs_to_jiffies(1));
+ _clr_fwstate_(pmlmepriv, _FW_UNDER_LINKING);
+ }
+
+ignore_joinbss_callback:
+ spin_unlock_bh(&pmlmepriv->lock);
+}
+
+void rtw_joinbss_event_callback(struct adapter *adapter, u8 *pbuf)
+{
+ struct wlan_network *pnetwork = (struct wlan_network *)pbuf;
+
+ mlmeext_joinbss_event_callback(adapter, pnetwork->join_res);
+
+ rtw_os_xmit_schedule(adapter);
+}
+
+static u8 search_max_mac_id(struct adapter *padapter)
+{
+ u8 mac_id;
+#if defined(CONFIG_88EU_AP_MODE)
+ u8 aid;
+ struct mlme_priv *pmlmepriv = &(padapter->mlmepriv);
+ struct sta_priv *pstapriv = &padapter->stapriv;
+#endif
+ struct mlme_ext_priv *pmlmeext = &(padapter->mlmeextpriv);
+ struct mlme_ext_info *pmlmeinfo = &(pmlmeext->mlmext_info);
+
+#if defined(CONFIG_88EU_AP_MODE)
+ if (check_fwstate(pmlmepriv, WIFI_AP_STATE)) {
+ for (aid = (pstapriv->max_num_sta); aid > 0; aid--) {
+ if (pstapriv->sta_aid[aid-1] != NULL)
+ break;
+ }
+ mac_id = aid + 1;
+ } else
+#endif
+ {/* adhoc id = 31~2 */
+ for (mac_id = (NUM_STA-1); mac_id >= IBSS_START_MAC_ID; mac_id--) {
+ if (pmlmeinfo->FW_sta_info[mac_id].status == 1)
+ break;
+ }
+ }
+ return mac_id;
+}
+
+/* FOR AP , AD-HOC mode */
+void rtw_stassoc_hw_rpt(struct adapter *adapter, struct sta_info *psta)
+{
+ u16 media_status;
+ u8 macid;
+
+ if (psta == NULL)
+ return;
+
+ macid = search_max_mac_id(adapter);
+ rtw_hal_set_hwreg(adapter, HW_VAR_TX_RPT_MAX_MACID, (u8 *)&macid);
+ media_status = (psta->mac_id<<8)|1; /* MACID|OPMODE:1 connect */
+ rtw_hal_set_hwreg(adapter, HW_VAR_H2C_MEDIA_STATUS_RPT, (u8 *)&media_status);
+}
+
+void rtw_stassoc_event_callback(struct adapter *adapter, u8 *pbuf)
+{
+ struct sta_info *psta;
+ struct mlme_priv *pmlmepriv = &(adapter->mlmepriv);
+ struct stassoc_event *pstassoc = (struct stassoc_event *)pbuf;
+ struct wlan_network *cur_network = &(pmlmepriv->cur_network);
+ struct wlan_network *ptarget_wlan = NULL;
+
+ if (rtw_access_ctrl(adapter, pstassoc->macaddr) == false)
+ return;
+
+#if defined(CONFIG_88EU_AP_MODE)
+ if (check_fwstate(pmlmepriv, WIFI_AP_STATE)) {
+ psta = rtw_get_stainfo(&adapter->stapriv, pstassoc->macaddr);
+ if (psta) {
+ ap_sta_info_defer_update(adapter, psta);
+ rtw_stassoc_hw_rpt(adapter, psta);
+ }
+ return;
+ }
+#endif
+ /* for AD-HOC mode */
+ psta = rtw_get_stainfo(&adapter->stapriv, pstassoc->macaddr);
+ if (psta != NULL) {
+ /* the sta have been in sta_info_queue => do nothing */
+ RT_TRACE(_module_rtl871x_mlme_c_, _drv_err_, ("Error: rtw_stassoc_event_callback: sta has been in sta_hash_queue\n"));
+ return; /* between drv has received this event before and fw have not yet to set key to CAM_ENTRY) */
+ }
+ psta = rtw_alloc_stainfo(&adapter->stapriv, pstassoc->macaddr);
+ if (psta == NULL) {
+ RT_TRACE(_module_rtl871x_mlme_c_, _drv_err_, ("Can't alloc sta_info when rtw_stassoc_event_callback\n"));
+ return;
+ }
+ /* to do: init sta_info variable */
+ psta->qos_option = 0;
+ psta->mac_id = (uint)pstassoc->cam_id;
+ DBG_88E("%s\n", __func__);
+ /* for ad-hoc mode */
+ rtw_hal_set_odm_var(adapter, HAL_ODM_STA_INFO, psta, true);
+ rtw_stassoc_hw_rpt(adapter, psta);
+ if (adapter->securitypriv.dot11AuthAlgrthm == dot11AuthAlgrthm_8021X)
+ psta->dot118021XPrivacy = adapter->securitypriv.dot11PrivacyAlgrthm;
+ psta->ieee8021x_blocked = false;
+ spin_lock_bh(&pmlmepriv->lock);
+ if ((check_fwstate(pmlmepriv, WIFI_ADHOC_MASTER_STATE)) ||
+ (check_fwstate(pmlmepriv, WIFI_ADHOC_STATE))) {
+ if (adapter->stapriv.asoc_sta_count == 2) {
+ spin_lock_bh(&(pmlmepriv->scanned_queue.lock));
+ ptarget_wlan = rtw_find_network(&pmlmepriv->scanned_queue, cur_network->network.MacAddress);
+ if (ptarget_wlan)
+ ptarget_wlan->fixed = true;
+ spin_unlock_bh(&pmlmepriv->scanned_queue.lock);
+ /* a sta + bc/mc_stainfo (not Ibss_stainfo) */
+ rtw_indicate_connect(adapter);
+ }
+ }
+ spin_unlock_bh(&pmlmepriv->lock);
+ mlmeext_sta_add_event_callback(adapter, psta);
+}
+
+void rtw_stadel_event_callback(struct adapter *adapter, u8 *pbuf)
+{
+ int mac_id = -1;
+ struct sta_info *psta;
+ struct wlan_network *pwlan = NULL;
+ struct wlan_bssid_ex *pdev_network = NULL;
+ u8 *pibss = NULL;
+ struct mlme_priv *pmlmepriv = &(adapter->mlmepriv);
+ struct stadel_event *pstadel = (struct stadel_event *)pbuf;
+ struct sta_priv *pstapriv = &adapter->stapriv;
+ struct wlan_network *tgt_network = &(pmlmepriv->cur_network);
+
+ psta = rtw_get_stainfo(&adapter->stapriv, pstadel->macaddr);
+ if (psta)
+ mac_id = psta->mac_id;
+ else
+ mac_id = pstadel->mac_id;
+
+ DBG_88E("%s(mac_id=%d)=%pM\n", __func__, mac_id, pstadel->macaddr);
+
+ if (mac_id >= 0) {
+ u16 media_status;
+ media_status = (mac_id<<8)|0; /* MACID|OPMODE:0 means disconnect */
+ /* for STA, AP, ADHOC mode, report disconnect stauts to FW */
+ rtw_hal_set_hwreg(adapter, HW_VAR_H2C_MEDIA_STATUS_RPT, (u8 *)&media_status);
+ }
+
+ if (check_fwstate(pmlmepriv, WIFI_AP_STATE))
+ return;
+
+ mlmeext_sta_del_event_callback(adapter);
+
+ spin_lock_bh(&pmlmepriv->lock);
+
+ if (check_fwstate(pmlmepriv, WIFI_STATION_STATE)) {
+ if (pmlmepriv->to_roaming > 0)
+ pmlmepriv->to_roaming--; /* this stadel_event is caused by roaming, decrease to_roaming */
+ else if (pmlmepriv->to_roaming == 0)
+ pmlmepriv->to_roaming = adapter->registrypriv.max_roaming_times;
+
+ if (*((unsigned short *)(pstadel->rsvd)) != WLAN_REASON_EXPIRATION_CHK)
+ pmlmepriv->to_roaming = 0; /* don't roam */
+
+ rtw_free_uc_swdec_pending_queue(adapter);
+
+ rtw_free_assoc_resources(adapter, 1);
+ rtw_indicate_disconnect(adapter);
+ spin_lock_bh(&(pmlmepriv->scanned_queue.lock));
+ /* remove the network entry in scanned_queue */
+ pwlan = rtw_find_network(&pmlmepriv->scanned_queue, tgt_network->network.MacAddress);
+ if (pwlan) {
+ pwlan->fixed = false;
+ rtw_free_network_nolock(pmlmepriv, pwlan);
+ }
+ spin_unlock_bh(&pmlmepriv->scanned_queue.lock);
+ _rtw_roaming(adapter, tgt_network);
+ }
+ if (check_fwstate(pmlmepriv, WIFI_ADHOC_MASTER_STATE) ||
+ check_fwstate(pmlmepriv, WIFI_ADHOC_STATE)) {
+ spin_lock_bh(&(pstapriv->sta_hash_lock));
+ rtw_free_stainfo(adapter, psta);
+ spin_unlock_bh(&pstapriv->sta_hash_lock);
+
+ if (adapter->stapriv.asoc_sta_count == 1) { /* a sta + bc/mc_stainfo (not Ibss_stainfo) */
+ spin_lock_bh(&(pmlmepriv->scanned_queue.lock));
+ /* free old ibss network */
+ pwlan = rtw_find_network(&pmlmepriv->scanned_queue, tgt_network->network.MacAddress);
+ if (pwlan) {
+ pwlan->fixed = false;
+ rtw_free_network_nolock(pmlmepriv, pwlan);
+ }
+ spin_unlock_bh(&pmlmepriv->scanned_queue.lock);
+ /* re-create ibss */
+ pdev_network = &(adapter->registrypriv.dev_network);
+ pibss = adapter->registrypriv.dev_network.MacAddress;
+
+ memcpy(pdev_network, &tgt_network->network, get_wlan_bssid_ex_sz(&tgt_network->network));
+
+ memcpy(&pdev_network->Ssid, &pmlmepriv->assoc_ssid, sizeof(struct ndis_802_11_ssid));
+
+ rtw_update_registrypriv_dev_network(adapter);
+
+ rtw_generate_random_ibss(pibss);
+
+ if (check_fwstate(pmlmepriv, WIFI_ADHOC_STATE)) {
+ set_fwstate(pmlmepriv, WIFI_ADHOC_MASTER_STATE);
+ _clr_fwstate_(pmlmepriv, WIFI_ADHOC_STATE);
+ }
+
+ if (rtw_createbss_cmd(adapter) != _SUCCESS)
+ RT_TRACE(_module_rtl871x_ioctl_set_c_, _drv_err_, ("***Error=>stadel_event_callback: rtw_createbss_cmd status FAIL***\n "));
+ }
+ }
+ spin_unlock_bh(&pmlmepriv->lock);
+}
+
+void rtw_cpwm_event_callback(struct adapter *padapter, u8 *pbuf)
+{
+ RT_TRACE(_module_rtl871x_mlme_c_, _drv_err_, ("+rtw_cpwm_event_callback !!!\n"));
+}
+
+/*
+* _rtw_join_timeout_handler - Timeout/faliure handler for CMD JoinBss
+* @adapter: pointer to struct adapter structure
+*/
+void _rtw_join_timeout_handler (unsigned long data)
+{
+ struct adapter *adapter = (struct adapter *)data;
+ struct mlme_priv *pmlmepriv = &adapter->mlmepriv;
+ int do_join_r;
+
+ DBG_88E("%s, fw_state=%x\n", __func__, get_fwstate(pmlmepriv));
+
+ if (adapter->bDriverStopped || adapter->bSurpriseRemoved)
+ return;
+
+
+ spin_lock_bh(&pmlmepriv->lock);
+
+ if (pmlmepriv->to_roaming > 0) { /* join timeout caused by roaming */
+ while (1) {
+ pmlmepriv->to_roaming--;
+ if (pmlmepriv->to_roaming != 0) { /* try another , */
+ DBG_88E("%s try another roaming\n", __func__);
+ do_join_r = rtw_do_join(adapter);
+ if (_SUCCESS != do_join_r) {
+ DBG_88E("%s roaming do_join return %d\n", __func__ , do_join_r);
+ continue;
+ }
+ break;
+ } else {
+ DBG_88E("%s We've try roaming but fail\n", __func__);
+ rtw_indicate_disconnect(adapter);
+ break;
+ }
+ }
+ } else {
+ rtw_indicate_disconnect(adapter);
+ free_scanqueue(pmlmepriv);/* */
+ }
+ spin_unlock_bh(&pmlmepriv->lock);
+}
+
+/*
+* rtw_scan_timeout_handler - Timeout/Faliure handler for CMD SiteSurvey
+* @adapter: pointer to struct adapter structure
+*/
+void rtw_scan_timeout_handler (unsigned long data)
+{
+ struct adapter *adapter = (struct adapter *)data;
+ struct mlme_priv *pmlmepriv = &adapter->mlmepriv;
+
+ DBG_88E(FUNC_ADPT_FMT" fw_state=%x\n", FUNC_ADPT_ARG(adapter), get_fwstate(pmlmepriv));
+ spin_lock_bh(&pmlmepriv->lock);
+ _clr_fwstate_(pmlmepriv, _FW_UNDER_SURVEY);
+ spin_unlock_bh(&pmlmepriv->lock);
+ rtw_indicate_scan_done(adapter, true);
+}
+
+static void rtw_auto_scan_handler(struct adapter *padapter)
+{
+ struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
+
+ /* auto site survey per 60sec */
+ if (pmlmepriv->scan_interval > 0) {
+ pmlmepriv->scan_interval--;
+ if (pmlmepriv->scan_interval == 0) {
+ DBG_88E("%s\n", __func__);
+ rtw_set_802_11_bssid_list_scan(padapter, NULL, 0);
+ pmlmepriv->scan_interval = SCAN_INTERVAL;/* 30*2 sec = 60sec */
+ }
+ }
+}
+
+void rtw_dynamic_check_timer_handlder(unsigned long data)
+{
+ struct adapter *adapter = (struct adapter *)data;
+ struct registry_priv *pregistrypriv = &adapter->registrypriv;
+
+ if (!adapter)
+ return;
+ if (!adapter->hw_init_completed)
+ goto exit;
+ if ((adapter->bDriverStopped) || (adapter->bSurpriseRemoved))
+ goto exit;
+ if (adapter->net_closed)
+ goto exit;
+ rtw_dynamic_chk_wk_cmd(adapter);
+
+ if (pregistrypriv->wifi_spec == 1) {
+ /* auto site survey */
+ rtw_auto_scan_handler(adapter);
+ }
+exit:
+ mod_timer(&adapter->mlmepriv.dynamic_chk_timer,
+ jiffies + msecs_to_jiffies(2000));
+}
+
+#define RTW_SCAN_RESULT_EXPIRE 2000
+
+/*
+* Select a new join candidate from the original @param candidate and @param competitor
+* @return true: candidate is updated
+* @return false: candidate is not updated
+*/
+static int rtw_check_join_candidate(struct mlme_priv *pmlmepriv
+ , struct wlan_network **candidate, struct wlan_network *competitor)
+{
+ int updated = false;
+ struct adapter *adapter = container_of(pmlmepriv, struct adapter, mlmepriv);
+
+
+ /* check bssid, if needed */
+ if (pmlmepriv->assoc_by_bssid) {
+ if (memcmp(competitor->network.MacAddress, pmlmepriv->assoc_bssid, ETH_ALEN))
+ goto exit;
+ }
+
+ /* check ssid, if needed */
+ if (pmlmepriv->assoc_ssid.SsidLength) {
+ if (competitor->network.Ssid.SsidLength != pmlmepriv->assoc_ssid.SsidLength ||
+ !memcmp(competitor->network.Ssid.Ssid, pmlmepriv->assoc_ssid.Ssid, pmlmepriv->assoc_ssid.SsidLength) == false)
+ goto exit;
+ }
+
+ if (rtw_is_desired_network(adapter, competitor) == false)
+ goto exit;
+
+ if (pmlmepriv->to_roaming) {
+ if (rtw_get_passing_time_ms((u32)competitor->last_scanned) >= RTW_SCAN_RESULT_EXPIRE ||
+ is_same_ess(&competitor->network, &pmlmepriv->cur_network.network) == false)
+ goto exit;
+ }
+
+ if (*candidate == NULL || (*candidate)->network.Rssi < competitor->network.Rssi) {
+ *candidate = competitor;
+ updated = true;
+ }
+ if (updated) {
+ DBG_88E("[by_bssid:%u][assoc_ssid:%s]new candidate: %s(%pM rssi:%d\n",
+ pmlmepriv->assoc_by_bssid,
+ pmlmepriv->assoc_ssid.Ssid,
+ (*candidate)->network.Ssid.Ssid,
+ (*candidate)->network.MacAddress,
+ (int)(*candidate)->network.Rssi);
+ DBG_88E("[to_roaming:%u]\n", pmlmepriv->to_roaming);
+ }
+
+exit:
+ return updated;
+}
+
+/*
+Calling context:
+The caller of the sub-routine will be in critical section...
+The caller must hold the following spinlock
+pmlmepriv->lock
+*/
+
+int rtw_select_and_join_from_scanned_queue(struct mlme_priv *pmlmepriv)
+{
+ int ret;
+ struct list_head *phead;
+ struct adapter *adapter;
+ struct __queue *queue = &(pmlmepriv->scanned_queue);
+ struct wlan_network *pnetwork = NULL;
+ struct wlan_network *candidate = NULL;
+ u8 supp_ant_div = false;
+
+ spin_lock_bh(&(pmlmepriv->scanned_queue.lock));
+ phead = get_list_head(queue);
+ adapter = (struct adapter *)pmlmepriv->nic_hdl;
+ pmlmepriv->pscanned = phead->next;
+ while (phead != pmlmepriv->pscanned) {
+ pnetwork = container_of(pmlmepriv->pscanned, struct wlan_network, list);
+ if (pnetwork == NULL) {
+ RT_TRACE(_module_rtl871x_mlme_c_, _drv_err_, ("%s return _FAIL:(pnetwork==NULL)\n", __func__));
+ ret = _FAIL;
+ goto exit;
+ }
+ pmlmepriv->pscanned = pmlmepriv->pscanned->next;
+ rtw_check_join_candidate(pmlmepriv, &candidate, pnetwork);
+ }
+ if (candidate == NULL) {
+ DBG_88E("%s: return _FAIL(candidate==NULL)\n", __func__);
+ ret = _FAIL;
+ goto exit;
+ } else {
+ DBG_88E("%s: candidate: %s(%pM ch:%u)\n", __func__,
+ candidate->network.Ssid.Ssid, candidate->network.MacAddress,
+ candidate->network.Configuration.DSConfig);
+ }
+
+
+ /* check for situation of _FW_LINKED */
+ if (check_fwstate(pmlmepriv, _FW_LINKED) == true) {
+ DBG_88E("%s: _FW_LINKED while ask_for_joinbss!!!\n", __func__);
+
+ rtw_disassoc_cmd(adapter, 0, true);
+ rtw_indicate_disconnect(adapter);
+ rtw_free_assoc_resources(adapter, 0);
+ }
+
+ rtw_hal_get_def_var(adapter, HAL_DEF_IS_SUPPORT_ANT_DIV, &(supp_ant_div));
+ if (supp_ant_div) {
+ u8 cur_ant;
+ rtw_hal_get_def_var(adapter, HAL_DEF_CURRENT_ANTENNA, &(cur_ant));
+ DBG_88E("#### Opt_Ant_(%s), cur_Ant(%s)\n",
+ (2 == candidate->network.PhyInfo.Optimum_antenna) ? "A" : "B",
+ (2 == cur_ant) ? "A" : "B"
+ );
+ }
+
+ ret = rtw_joinbss_cmd(adapter, candidate);
+
+exit:
+ spin_unlock_bh(&pmlmepriv->scanned_queue.lock);
+ return ret;
+}
+
+int rtw_set_auth(struct adapter *adapter, struct security_priv *psecuritypriv)
+{
+ struct cmd_obj *pcmd;
+ struct setauth_parm *psetauthparm;
+ struct cmd_priv *pcmdpriv = &(adapter->cmdpriv);
+ int res = _SUCCESS;
+
+ pcmd = kzalloc(sizeof(struct cmd_obj), GFP_KERNEL);
+ if (pcmd == NULL) {
+ res = _FAIL; /* try again */
+ goto exit;
+ }
+
+ psetauthparm = kzalloc(sizeof(struct setauth_parm), GFP_KERNEL);
+ if (psetauthparm == NULL) {
+ kfree(pcmd);
+ res = _FAIL;
+ goto exit;
+ }
+ memset(psetauthparm, 0, sizeof(struct setauth_parm));
+ psetauthparm->mode = (unsigned char)psecuritypriv->dot11AuthAlgrthm;
+ pcmd->cmdcode = _SetAuth_CMD_;
+ pcmd->parmbuf = (unsigned char *)psetauthparm;
+ pcmd->cmdsz = (sizeof(struct setauth_parm));
+ pcmd->rsp = NULL;
+ pcmd->rspsz = 0;
+ INIT_LIST_HEAD(&pcmd->list);
+ RT_TRACE(_module_rtl871x_mlme_c_, _drv_err_,
+ ("after enqueue set_auth_cmd, auth_mode=%x\n",
+ psecuritypriv->dot11AuthAlgrthm));
+ res = rtw_enqueue_cmd(pcmdpriv, pcmd);
+exit:
+ return res;
+}
+
+int rtw_set_key(struct adapter *adapter, struct security_priv *psecuritypriv, int keyid, u8 set_tx)
+{
+ u8 keylen;
+ struct cmd_obj *pcmd;
+ struct setkey_parm *psetkeyparm;
+ struct cmd_priv *pcmdpriv = &(adapter->cmdpriv);
+ struct mlme_priv *pmlmepriv = &(adapter->mlmepriv);
+ int res = _SUCCESS;
+
+ pcmd = kzalloc(sizeof(struct cmd_obj), GFP_KERNEL);
+ if (pcmd == NULL)
+ return _FAIL; /* try again */
+
+ psetkeyparm = kzalloc(sizeof(struct setkey_parm), GFP_KERNEL);
+ if (psetkeyparm == NULL) {
+ res = _FAIL;
+ goto err_free_cmd;
+ }
+
+ memset(psetkeyparm, 0, sizeof(struct setkey_parm));
+
+ if (psecuritypriv->dot11AuthAlgrthm == dot11AuthAlgrthm_8021X) {
+ psetkeyparm->algorithm = (unsigned char)psecuritypriv->dot118021XGrpPrivacy;
+ RT_TRACE(_module_rtl871x_mlme_c_, _drv_err_,
+ ("\n rtw_set_key: psetkeyparm->algorithm=(unsigned char)psecuritypriv->dot118021XGrpPrivacy=%d\n",
+ psetkeyparm->algorithm));
+ } else {
+ psetkeyparm->algorithm = (u8)psecuritypriv->dot11PrivacyAlgrthm;
+ RT_TRACE(_module_rtl871x_mlme_c_, _drv_err_,
+ ("\n rtw_set_key: psetkeyparm->algorithm=(u8)psecuritypriv->dot11PrivacyAlgrthm=%d\n",
+ psetkeyparm->algorithm));
+ }
+ psetkeyparm->keyid = (u8)keyid;/* 0~3 */
+ psetkeyparm->set_tx = set_tx;
+ pmlmepriv->key_mask |= BIT(psetkeyparm->keyid);
+ DBG_88E("==> rtw_set_key algorithm(%x), keyid(%x), key_mask(%x)\n",
+ psetkeyparm->algorithm, psetkeyparm->keyid, pmlmepriv->key_mask);
+ RT_TRACE(_module_rtl871x_mlme_c_, _drv_err_,
+ ("\n rtw_set_key: psetkeyparm->algorithm=%d psetkeyparm->keyid=(u8)keyid=%d\n",
+ psetkeyparm->algorithm, keyid));
+
+ switch (psetkeyparm->algorithm) {
+ case _WEP40_:
+ keylen = 5;
+ memcpy(&(psetkeyparm->key[0]), &(psecuritypriv->dot11DefKey[keyid].skey[0]), keylen);
+ break;
+ case _WEP104_:
+ keylen = 13;
+ memcpy(&(psetkeyparm->key[0]), &(psecuritypriv->dot11DefKey[keyid].skey[0]), keylen);
+ break;
+ case _TKIP_:
+ keylen = 16;
+ memcpy(&psetkeyparm->key, &psecuritypriv->dot118021XGrpKey[keyid], keylen);
+ psetkeyparm->grpkey = 1;
+ break;
+ case _AES_:
+ keylen = 16;
+ memcpy(&psetkeyparm->key, &psecuritypriv->dot118021XGrpKey[keyid], keylen);
+ psetkeyparm->grpkey = 1;
+ break;
+ default:
+ RT_TRACE(_module_rtl871x_mlme_c_, _drv_err_,
+ ("\n rtw_set_key:psecuritypriv->dot11PrivacyAlgrthm=%x (must be 1 or 2 or 4 or 5)\n",
+ psecuritypriv->dot11PrivacyAlgrthm));
+ res = _FAIL;
+ goto err_free_parm;
+ }
+ pcmd->cmdcode = _SetKey_CMD_;
+ pcmd->parmbuf = (u8 *)psetkeyparm;
+ pcmd->cmdsz = (sizeof(struct setkey_parm));
+ pcmd->rsp = NULL;
+ pcmd->rspsz = 0;
+ INIT_LIST_HEAD(&pcmd->list);
+ res = rtw_enqueue_cmd(pcmdpriv, pcmd);
+ return res;
+
+err_free_parm:
+ kfree(psetkeyparm);
+err_free_cmd:
+ kfree(pcmd);
+ return res;
+}
+
+/* adjust IEs for rtw_joinbss_cmd in WMM */
+int rtw_restruct_wmm_ie(struct adapter *adapter, u8 *in_ie, u8 *out_ie, uint in_len, uint initial_out_len)
+{
+ unsigned int ielength = 0;
+ unsigned int i, j;
+
+ i = 12; /* after the fixed IE */
+ while (i < in_len) {
+ ielength = initial_out_len;
+
+ if (in_ie[i] == 0xDD && in_ie[i+2] == 0x00 && in_ie[i+3] == 0x50 && in_ie[i+4] == 0xF2 && in_ie[i+5] == 0x02 && i+5 < in_len) {
+ /* WMM element ID and OUI */
+ /* Append WMM IE to the last index of out_ie */
+
+ for (j = i; j < i + 9; j++) {
+ out_ie[ielength] = in_ie[j];
+ ielength++;
+ }
+ out_ie[initial_out_len + 1] = 0x07;
+ out_ie[initial_out_len + 6] = 0x00;
+ out_ie[initial_out_len + 8] = 0x00;
+ break;
+ }
+ i += (in_ie[i+1]+2); /* to the next IE element */
+ }
+ return ielength;
+}
+
+/*
+ * Ported from 8185: IsInPreAuthKeyList().
+ * (Renamed from SecIsInPreAuthKeyList(), 2006-10-13.)
+ * Added by Annie, 2006-05-07.
+ * Search by BSSID,
+ * Return Value:
+ * -1 :if there is no pre-auth key in the table
+ * >= 0 :if there is pre-auth key, and return the entry id
+ */
+static int SecIsInPMKIDList(struct adapter *Adapter, u8 *bssid)
+{
+ struct security_priv *psecuritypriv = &Adapter->securitypriv;
+ int i = 0;
+
+ do {
+ if ((psecuritypriv->PMKIDList[i].bUsed) &&
+ (!memcmp(psecuritypriv->PMKIDList[i].Bssid, bssid, ETH_ALEN))) {
+ break;
+ } else {
+ i++;
+ /* continue; */
+ }
+
+ } while (i < NUM_PMKID_CACHE);
+
+ if (i == NUM_PMKID_CACHE)
+ i = -1;/* Could not find. */
+
+ return i;
+}
+
+/* */
+/* Check the RSN IE length */
+/* If the RSN IE length <= 20, the RSN IE didn't include the PMKID information */
+/* 0-11th element in the array are the fixed IE */
+/* 12th element in the array is the IE */
+/* 13th element in the array is the IE length */
+/* */
+
+static int rtw_append_pmkid(struct adapter *Adapter, int iEntry, u8 *ie, uint ie_len)
+{
+ struct security_priv *psecuritypriv = &Adapter->securitypriv;
+
+ if (ie[13] <= 20) {
+ /* The RSN IE didn't include the PMK ID, append the PMK information */
+ ie[ie_len] = 1;
+ ie_len++;
+ ie[ie_len] = 0; /* PMKID count = 0x0100 */
+ ie_len++;
+ memcpy(&ie[ie_len], &psecuritypriv->PMKIDList[iEntry].PMKID, 16);
+
+ ie_len += 16;
+ ie[13] += 18;/* PMKID length = 2+16 */
+ }
+ return ie_len;
+}
+
+int rtw_restruct_sec_ie(struct adapter *adapter, u8 *in_ie, u8 *out_ie, uint in_len)
+{
+ u8 authmode;
+ uint ielength;
+ int iEntry;
+
+ struct mlme_priv *pmlmepriv = &adapter->mlmepriv;
+ struct security_priv *psecuritypriv = &adapter->securitypriv;
+ uint ndisauthmode = psecuritypriv->ndisauthtype;
+ uint ndissecuritytype = psecuritypriv->ndisencryptstatus;
+
+ RT_TRACE(_module_rtl871x_mlme_c_, _drv_notice_,
+ ("+rtw_restruct_sec_ie: ndisauthmode=%d ndissecuritytype=%d\n",
+ ndisauthmode, ndissecuritytype));
+
+ /* copy fixed ie only */
+ memcpy(out_ie, in_ie, 12);
+ ielength = 12;
+ if ((ndisauthmode == Ndis802_11AuthModeWPA) ||
+ (ndisauthmode == Ndis802_11AuthModeWPAPSK))
+ authmode = _WPA_IE_ID_;
+ if ((ndisauthmode == Ndis802_11AuthModeWPA2) ||
+ (ndisauthmode == Ndis802_11AuthModeWPA2PSK))
+ authmode = _WPA2_IE_ID_;
+
+ if (check_fwstate(pmlmepriv, WIFI_UNDER_WPS)) {
+ memcpy(out_ie+ielength, psecuritypriv->wps_ie, psecuritypriv->wps_ie_len);
+
+ ielength += psecuritypriv->wps_ie_len;
+ } else if ((authmode == _WPA_IE_ID_) || (authmode == _WPA2_IE_ID_)) {
+ /* copy RSN or SSN */
+ memcpy(&out_ie[ielength], &psecuritypriv->supplicant_ie[0], psecuritypriv->supplicant_ie[1]+2);
+ ielength += psecuritypriv->supplicant_ie[1]+2;
+ rtw_report_sec_ie(adapter, authmode, psecuritypriv->supplicant_ie);
+ }
+
+ iEntry = SecIsInPMKIDList(adapter, pmlmepriv->assoc_bssid);
+ if (iEntry < 0) {
+ return ielength;
+ } else {
+ if (authmode == _WPA2_IE_ID_)
+ ielength = rtw_append_pmkid(adapter, iEntry, out_ie, ielength);
+ }
+ return ielength;
+}
+
+void rtw_init_registrypriv_dev_network(struct adapter *adapter)
+{
+ struct registry_priv *pregistrypriv = &adapter->registrypriv;
+ struct eeprom_priv *peepriv = &adapter->eeprompriv;
+ struct wlan_bssid_ex *pdev_network = &pregistrypriv->dev_network;
+ u8 *myhwaddr = myid(peepriv);
+
+ memcpy(pdev_network->MacAddress, myhwaddr, ETH_ALEN);
+
+ memcpy(&pdev_network->Ssid, &pregistrypriv->ssid, sizeof(struct ndis_802_11_ssid));
+
+ pdev_network->Configuration.Length = sizeof(struct ndis_802_11_config);
+ pdev_network->Configuration.BeaconPeriod = 100;
+ pdev_network->Configuration.FHConfig.Length = 0;
+ pdev_network->Configuration.FHConfig.HopPattern = 0;
+ pdev_network->Configuration.FHConfig.HopSet = 0;
+ pdev_network->Configuration.FHConfig.DwellTime = 0;
+}
+
+void rtw_update_registrypriv_dev_network(struct adapter *adapter)
+{
+ int sz = 0;
+ struct registry_priv *pregistrypriv = &adapter->registrypriv;
+ struct wlan_bssid_ex *pdev_network = &pregistrypriv->dev_network;
+ struct security_priv *psecuritypriv = &adapter->securitypriv;
+ struct wlan_network *cur_network = &adapter->mlmepriv.cur_network;
+
+ pdev_network->Privacy = (psecuritypriv->dot11PrivacyAlgrthm > 0 ? 1 : 0); /* adhoc no 802.1x */
+
+ pdev_network->Rssi = 0;
+
+ switch (pregistrypriv->wireless_mode) {
+ case WIRELESS_11B:
+ pdev_network->NetworkTypeInUse = (Ndis802_11DS);
+ break;
+ case WIRELESS_11G:
+ case WIRELESS_11BG:
+ case WIRELESS_11_24N:
+ case WIRELESS_11G_24N:
+ case WIRELESS_11BG_24N:
+ pdev_network->NetworkTypeInUse = (Ndis802_11OFDM24);
+ break;
+ case WIRELESS_11A:
+ case WIRELESS_11A_5N:
+ pdev_network->NetworkTypeInUse = (Ndis802_11OFDM5);
+ break;
+ case WIRELESS_11ABGN:
+ if (pregistrypriv->channel > 14)
+ pdev_network->NetworkTypeInUse = (Ndis802_11OFDM5);
+ else
+ pdev_network->NetworkTypeInUse = (Ndis802_11OFDM24);
+ break;
+ default:
+ /* TODO */
+ break;
+ }
+
+ pdev_network->Configuration.DSConfig = (pregistrypriv->channel);
+ RT_TRACE(_module_rtl871x_mlme_c_, _drv_info_,
+ ("pregistrypriv->channel=%d, pdev_network->Configuration.DSConfig=0x%x\n",
+ pregistrypriv->channel, pdev_network->Configuration.DSConfig));
+
+ if (cur_network->network.InfrastructureMode == Ndis802_11IBSS)
+ pdev_network->Configuration.ATIMWindow = (0);
+
+ pdev_network->InfrastructureMode = (cur_network->network.InfrastructureMode);
+
+ /* 1. Supported rates */
+ /* 2. IE */
+
+ sz = rtw_generate_ie(pregistrypriv);
+ pdev_network->IELength = sz;
+ pdev_network->Length = get_wlan_bssid_ex_sz((struct wlan_bssid_ex *)pdev_network);
+
+ /* notes: translate IELength & Length after assign the Length to cmdsz in createbss_cmd(); */
+ /* pdev_network->IELength = cpu_to_le32(sz); */
+}
+
+void rtw_get_encrypt_decrypt_from_registrypriv(struct adapter *adapter)
+{
+}
+
+/* the function is at passive_level */
+void rtw_joinbss_reset(struct adapter *padapter)
+{
+ u8 threshold;
+ struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
+ struct ht_priv *phtpriv = &pmlmepriv->htpriv;
+
+ /* todo: if you want to do something io/reg/hw setting before join_bss, please add code here */
+ pmlmepriv->num_FortyMHzIntolerant = 0;
+
+ pmlmepriv->num_sta_no_ht = 0;
+
+ phtpriv->ampdu_enable = false;/* reset to disabled */
+
+ /* TH = 1 => means that invalidate usb rx aggregation */
+ /* TH = 0 => means that validate usb rx aggregation, use init value. */
+ if (phtpriv->ht_option) {
+ if (padapter->registrypriv.wifi_spec == 1)
+ threshold = 1;
+ else
+ threshold = 0;
+ rtw_hal_set_hwreg(padapter, HW_VAR_RXDMA_AGG_PG_TH, (u8 *)(&threshold));
+ } else {
+ threshold = 1;
+ rtw_hal_set_hwreg(padapter, HW_VAR_RXDMA_AGG_PG_TH, (u8 *)(&threshold));
+ }
+}
+
+/* the function is >= passive_level */
+unsigned int rtw_restructure_ht_ie(struct adapter *padapter, u8 *in_ie, u8 *out_ie, uint in_len, uint *pout_len)
+{
+ u32 ielen, out_len;
+ enum ht_cap_ampdu_factor max_rx_ampdu_factor;
+ unsigned char *p;
+ struct rtw_ieee80211_ht_cap ht_capie;
+ unsigned char WMM_IE[] = {0x00, 0x50, 0xf2, 0x02, 0x00, 0x01, 0x00};
+ struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
+ struct qos_priv *pqospriv = &pmlmepriv->qospriv;
+ struct ht_priv *phtpriv = &pmlmepriv->htpriv;
+ u32 rx_packet_offset, max_recvbuf_sz;
+
+
+ phtpriv->ht_option = false;
+
+ p = rtw_get_ie(in_ie+12, _HT_CAPABILITY_IE_, &ielen, in_len-12);
+
+ if (p && ielen > 0) {
+ if (pqospriv->qos_option == 0) {
+ out_len = *pout_len;
+ rtw_set_ie(out_ie+out_len, _VENDOR_SPECIFIC_IE_,
+ _WMM_IE_Length_, WMM_IE, pout_len);
+
+ pqospriv->qos_option = 1;
+ }
+
+ out_len = *pout_len;
+
+ memset(&ht_capie, 0, sizeof(struct rtw_ieee80211_ht_cap));
+
+ ht_capie.cap_info = IEEE80211_HT_CAP_SUP_WIDTH |
+ IEEE80211_HT_CAP_SGI_20 |
+ IEEE80211_HT_CAP_SGI_40 |
+ IEEE80211_HT_CAP_TX_STBC |
+ IEEE80211_HT_CAP_DSSSCCK40;
+
+ rtw_hal_get_def_var(padapter, HAL_DEF_RX_PACKET_OFFSET, &rx_packet_offset);
+ rtw_hal_get_def_var(padapter, HAL_DEF_MAX_RECVBUF_SZ, &max_recvbuf_sz);
+
+ /*
+ AMPDU_para [1:0]:Max AMPDU Len => 0:8k , 1:16k, 2:32k, 3:64k
+ AMPDU_para [4:2]:Min MPDU Start Spacing
+ */
+
+ rtw_hal_get_def_var(padapter, HW_VAR_MAX_RX_AMPDU_FACTOR, &max_rx_ampdu_factor);
+ ht_capie.ampdu_params_info = (max_rx_ampdu_factor&0x03);
+
+ if (padapter->securitypriv.dot11PrivacyAlgrthm == _AES_)
+ ht_capie.ampdu_params_info |= (IEEE80211_HT_CAP_AMPDU_DENSITY&(0x07<<2));
+ else
+ ht_capie.ampdu_params_info |= (IEEE80211_HT_CAP_AMPDU_DENSITY&0x00);
+
+
+ rtw_set_ie(out_ie+out_len, _HT_CAPABILITY_IE_,
+ sizeof(struct rtw_ieee80211_ht_cap), (unsigned char *)&ht_capie, pout_len);
+
+ phtpriv->ht_option = true;
+
+ p = rtw_get_ie(in_ie+12, _HT_ADD_INFO_IE_, &ielen, in_len-12);
+ if (p && (ielen == sizeof(struct ieee80211_ht_addt_info))) {
+ out_len = *pout_len;
+ rtw_set_ie(out_ie+out_len, _HT_ADD_INFO_IE_, ielen, p+2 , pout_len);
+ }
+ }
+ return phtpriv->ht_option;
+}
+
+/* the function is > passive_level (in critical_section) */
+void rtw_update_ht_cap(struct adapter *padapter, u8 *pie, uint ie_len)
+{
+ u8 *p, max_ampdu_sz;
+ int len;
+ struct rtw_ieee80211_ht_cap *pht_capie;
+ struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
+ struct ht_priv *phtpriv = &pmlmepriv->htpriv;
+ struct registry_priv *pregistrypriv = &padapter->registrypriv;
+ struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv;
+ struct mlme_ext_info *pmlmeinfo = &(pmlmeext->mlmext_info);
+
+ if (!phtpriv->ht_option)
+ return;
+
+ if ((!pmlmeinfo->HT_info_enable) || (!pmlmeinfo->HT_caps_enable))
+ return;
+
+ DBG_88E("+rtw_update_ht_cap()\n");
+
+ /* maybe needs check if ap supports rx ampdu. */
+ if ((!phtpriv->ampdu_enable) && (pregistrypriv->ampdu_enable == 1)) {
+ if (pregistrypriv->wifi_spec == 1)
+ phtpriv->ampdu_enable = false;
+ else
+ phtpriv->ampdu_enable = true;
+ } else if (pregistrypriv->ampdu_enable == 2) {
+ phtpriv->ampdu_enable = true;
+ }
+
+
+ /* check Max Rx A-MPDU Size */
+ len = 0;
+ p = rtw_get_ie(pie+sizeof(struct ndis_802_11_fixed_ie), _HT_CAPABILITY_IE_, &len, ie_len-sizeof(struct ndis_802_11_fixed_ie));
+ if (p && len > 0) {
+ pht_capie = (struct rtw_ieee80211_ht_cap *)(p+2);
+ max_ampdu_sz = pht_capie->ampdu_params_info & IEEE80211_HT_CAP_AMPDU_FACTOR;
+ max_ampdu_sz = 1 << (max_ampdu_sz+3); /* max_ampdu_sz (kbytes); */
+ phtpriv->rx_ampdu_maxlen = max_ampdu_sz;
+ }
+ len = 0;
+ p = rtw_get_ie(pie+sizeof(struct ndis_802_11_fixed_ie), _HT_ADD_INFO_IE_, &len, ie_len-sizeof(struct ndis_802_11_fixed_ie));
+
+ /* update cur_bwmode & cur_ch_offset */
+ if ((pregistrypriv->cbw40_enable) &&
+ (le16_to_cpu(pmlmeinfo->HT_caps.u.HT_cap_element.HT_caps_info) & BIT(1)) &&
+ (pmlmeinfo->HT_info.infos[0] & BIT(2))) {
+ int i;
+ u8 rf_type;
+
+ padapter->HalFunc.GetHwRegHandler(padapter, HW_VAR_RF_TYPE, (u8 *)(&rf_type));
+
+ /* update the MCS rates */
+ for (i = 0; i < 16; i++) {
+ if ((rf_type == RF_1T1R) || (rf_type == RF_1T2R))
+ pmlmeinfo->HT_caps.u.HT_cap_element.MCS_rate[i] &= MCS_rate_1R[i];
+ else
+ pmlmeinfo->HT_caps.u.HT_cap_element.MCS_rate[i] &= MCS_rate_2R[i];
+ }
+ /* switch to the 40M Hz mode according to the AP */
+ pmlmeext->cur_bwmode = HT_CHANNEL_WIDTH_40;
+ switch ((pmlmeinfo->HT_info.infos[0] & 0x3)) {
+ case HT_EXTCHNL_OFFSET_UPPER:
+ pmlmeext->cur_ch_offset = HAL_PRIME_CHNL_OFFSET_LOWER;
+ break;
+ case HT_EXTCHNL_OFFSET_LOWER:
+ pmlmeext->cur_ch_offset = HAL_PRIME_CHNL_OFFSET_UPPER;
+ break;
+ default:
+ pmlmeext->cur_ch_offset = HAL_PRIME_CHNL_OFFSET_DONT_CARE;
+ break;
+ }
+ }
+
+ /* Config SM Power Save setting */
+ pmlmeinfo->SM_PS = (le16_to_cpu(pmlmeinfo->HT_caps.u.HT_cap_element.HT_caps_info) & 0x0C) >> 2;
+ if (pmlmeinfo->SM_PS == WLAN_HT_CAP_SM_PS_STATIC)
+ DBG_88E("%s(): WLAN_HT_CAP_SM_PS_STATIC\n", __func__);
+
+ /* Config current HT Protection mode. */
+ pmlmeinfo->HT_protection = pmlmeinfo->HT_info.infos[1] & 0x3;
+}
+
+void rtw_issue_addbareq_cmd(struct adapter *padapter, struct xmit_frame *pxmitframe)
+{
+ u8 issued;
+ int priority;
+ struct sta_info *psta = NULL;
+ struct ht_priv *phtpriv;
+ struct pkt_attrib *pattrib = &pxmitframe->attrib;
+ s32 bmcst = IS_MCAST(pattrib->ra);
+
+ if (bmcst || (padapter->mlmepriv.LinkDetectInfo.NumTxOkInPeriod < 100))
+ return;
+
+ priority = pattrib->priority;
+
+ if (pattrib->psta)
+ psta = pattrib->psta;
+ else
+ psta = rtw_get_stainfo(&padapter->stapriv, pattrib->ra);
+
+ if (psta == NULL)
+ return;
+
+ phtpriv = &psta->htpriv;
+
+ if ((phtpriv->ht_option) && (phtpriv->ampdu_enable)) {
+ issued = (phtpriv->agg_enable_bitmap>>priority)&0x1;
+ issued |= (phtpriv->candidate_tid_bitmap>>priority)&0x1;
+
+ if (0 == issued) {
+ DBG_88E("rtw_issue_addbareq_cmd, p=%d\n", priority);
+ psta->htpriv.candidate_tid_bitmap |= BIT((u8)priority);
+ rtw_addbareq_cmd(padapter, (u8)priority, pattrib->ra);
+ }
+ }
+}
+
+void rtw_roaming(struct adapter *padapter, struct wlan_network *tgt_network)
+{
+ struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
+
+ spin_lock_bh(&pmlmepriv->lock);
+ _rtw_roaming(padapter, tgt_network);
+ spin_unlock_bh(&pmlmepriv->lock);
+}
+void _rtw_roaming(struct adapter *padapter, struct wlan_network *tgt_network)
+{
+ struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
+ int do_join_r;
+
+ struct wlan_network *pnetwork;
+
+ if (tgt_network != NULL)
+ pnetwork = tgt_network;
+ else
+ pnetwork = &pmlmepriv->cur_network;
+
+ if (0 < pmlmepriv->to_roaming) {
+ DBG_88E("roaming from %s(%pM length:%d\n",
+ pnetwork->network.Ssid.Ssid, pnetwork->network.MacAddress,
+ pnetwork->network.Ssid.SsidLength);
+ memcpy(&pmlmepriv->assoc_ssid, &pnetwork->network.Ssid, sizeof(struct ndis_802_11_ssid));
+
+ pmlmepriv->assoc_by_bssid = false;
+
+ while (1) {
+ do_join_r = rtw_do_join(padapter);
+ if (_SUCCESS == do_join_r) {
+ break;
+ } else {
+ DBG_88E("roaming do_join return %d\n", do_join_r);
+ pmlmepriv->to_roaming--;
+
+ if (0 < pmlmepriv->to_roaming) {
+ continue;
+ } else {
+ DBG_88E("%s(%d) -to roaming fail, indicate_disconnect\n", __func__, __LINE__);
+ rtw_indicate_disconnect(padapter);
+ break;
+ }
+ }
+ }
+ }
+}
diff --git a/drivers/staging/rtl8188eu/core/rtw_mlme_ext.c b/drivers/staging/rtl8188eu/core/rtw_mlme_ext.c
new file mode 100644
index 000000000..be9e34a0d
--- /dev/null
+++ b/drivers/staging/rtl8188eu/core/rtw_mlme_ext.c
@@ -0,0 +1,5587 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2012 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ *
+ ******************************************************************************/
+#define _RTW_MLME_EXT_C_
+
+#include <osdep_service.h>
+#include <drv_types.h>
+#include <wifi.h>
+#include <rtw_mlme_ext.h>
+#include <wlan_bssdef.h>
+#include <mlme_osdep.h>
+#include <recv_osdep.h>
+
+static struct mlme_handler mlme_sta_tbl[] = {
+ {WIFI_ASSOCREQ, "OnAssocReq", &OnAssocReq},
+ {WIFI_ASSOCRSP, "OnAssocRsp", &OnAssocRsp},
+ {WIFI_REASSOCREQ, "OnReAssocReq", &OnAssocReq},
+ {WIFI_REASSOCRSP, "OnReAssocRsp", &OnAssocRsp},
+ {WIFI_PROBEREQ, "OnProbeReq", &OnProbeReq},
+ {WIFI_PROBERSP, "OnProbeRsp", &OnProbeRsp},
+
+ /*----------------------------------------------------------
+ below 2 are reserved
+ -----------------------------------------------------------*/
+ {0, "DoReserved", &DoReserved},
+ {0, "DoReserved", &DoReserved},
+ {WIFI_BEACON, "OnBeacon", &OnBeacon},
+ {WIFI_ATIM, "OnATIM", &OnAtim},
+ {WIFI_DISASSOC, "OnDisassoc", &OnDisassoc},
+ {WIFI_AUTH, "OnAuth", &OnAuthClient},
+ {WIFI_DEAUTH, "OnDeAuth", &OnDeAuth},
+ {WIFI_ACTION, "OnAction", &OnAction},
+};
+
+static struct action_handler OnAction_tbl[] = {
+ {RTW_WLAN_CATEGORY_SPECTRUM_MGMT, "ACTION_SPECTRUM_MGMT", on_action_spct},
+ {RTW_WLAN_CATEGORY_QOS, "ACTION_QOS", &OnAction_qos},
+ {RTW_WLAN_CATEGORY_DLS, "ACTION_DLS", &OnAction_dls},
+ {RTW_WLAN_CATEGORY_BACK, "ACTION_BACK", &OnAction_back},
+ {RTW_WLAN_CATEGORY_PUBLIC, "ACTION_PUBLIC", on_action_public},
+ {RTW_WLAN_CATEGORY_RADIO_MEASUREMENT, "ACTION_RADIO_MEASUREMENT", &DoReserved},
+ {RTW_WLAN_CATEGORY_FT, "ACTION_FT", &DoReserved},
+ {RTW_WLAN_CATEGORY_HT, "ACTION_HT", &OnAction_ht},
+ {RTW_WLAN_CATEGORY_SA_QUERY, "ACTION_SA_QUERY", &DoReserved},
+ {RTW_WLAN_CATEGORY_WMM, "ACTION_WMM", &OnAction_wmm},
+ {RTW_WLAN_CATEGORY_P2P, "ACTION_P2P", &OnAction_p2p},
+};
+
+
+static u8 null_addr[ETH_ALEN] = {0, 0, 0, 0, 0, 0};
+
+/**************************************************
+OUI definitions for the vendor specific IE
+***************************************************/
+unsigned char RTW_WPA_OUI[] = {0x00, 0x50, 0xf2, 0x01};
+unsigned char WMM_OUI[] = {0x00, 0x50, 0xf2, 0x02};
+unsigned char WPS_OUI[] = {0x00, 0x50, 0xf2, 0x04};
+unsigned char P2P_OUI[] = {0x50, 0x6F, 0x9A, 0x09};
+unsigned char WFD_OUI[] = {0x50, 0x6F, 0x9A, 0x0A};
+
+unsigned char WMM_INFO_OUI[] = {0x00, 0x50, 0xf2, 0x02, 0x00, 0x01};
+unsigned char WMM_PARA_OUI[] = {0x00, 0x50, 0xf2, 0x02, 0x01, 0x01};
+
+unsigned char WPA_TKIP_CIPHER[4] = {0x00, 0x50, 0xf2, 0x02};
+unsigned char RSN_TKIP_CIPHER[4] = {0x00, 0x0f, 0xac, 0x02};
+
+extern unsigned char REALTEK_96B_IE[];
+
+/********************************************************
+MCS rate definitions
+*********************************************************/
+unsigned char MCS_rate_2R[16] = {0xff, 0xff, 0x0, 0x0, 0x01, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0};
+unsigned char MCS_rate_1R[16] = {0xff, 0x00, 0x0, 0x0, 0x01, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0};
+
+/********************************************************
+ChannelPlan definitions
+*********************************************************/
+static struct rt_channel_plan_2g RTW_ChannelPlan2G[RT_CHANNEL_DOMAIN_2G_MAX] = {
+ {{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13}, 13}, /* 0x00, RT_CHANNEL_DOMAIN_2G_WORLD , Passive scan CH 12, 13 */
+ {{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13}, 13}, /* 0x01, RT_CHANNEL_DOMAIN_2G_ETSI1 */
+ {{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}, 11}, /* 0x02, RT_CHANNEL_DOMAIN_2G_FCC1 */
+ {{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14}, 14}, /* 0x03, RT_CHANNEL_DOMAIN_2G_MIKK1 */
+ {{10, 11, 12, 13}, 4}, /* 0x04, RT_CHANNEL_DOMAIN_2G_ETSI2 */
+ {{}, 0}, /* 0x05, RT_CHANNEL_DOMAIN_2G_NULL */
+};
+
+static struct rt_channel_plan_map RTW_ChannelPlanMap[RT_CHANNEL_DOMAIN_MAX] = {
+ /* 0x00 ~ 0x1F , Old Define ===== */
+ {0x02}, /* 0x00, RT_CHANNEL_DOMAIN_FCC */
+ {0x02}, /* 0x01, RT_CHANNEL_DOMAIN_IC */
+ {0x01}, /* 0x02, RT_CHANNEL_DOMAIN_ETSI */
+ {0x01}, /* 0x03, RT_CHANNEL_DOMAIN_SPAIN */
+ {0x01}, /* 0x04, RT_CHANNEL_DOMAIN_FRANCE */
+ {0x03}, /* 0x05, RT_CHANNEL_DOMAIN_MKK */
+ {0x03}, /* 0x06, RT_CHANNEL_DOMAIN_MKK1 */
+ {0x01}, /* 0x07, RT_CHANNEL_DOMAIN_ISRAEL */
+ {0x03}, /* 0x08, RT_CHANNEL_DOMAIN_TELEC */
+ {0x03}, /* 0x09, RT_CHANNEL_DOMAIN_GLOBAL_DOAMIN */
+ {0x00}, /* 0x0A, RT_CHANNEL_DOMAIN_WORLD_WIDE_13 */
+ {0x02}, /* 0x0B, RT_CHANNEL_DOMAIN_TAIWAN */
+ {0x01}, /* 0x0C, RT_CHANNEL_DOMAIN_CHINA */
+ {0x02}, /* 0x0D, RT_CHANNEL_DOMAIN_SINGAPORE_INDIA_MEXICO */
+ {0x02}, /* 0x0E, RT_CHANNEL_DOMAIN_KOREA */
+ {0x02}, /* 0x0F, RT_CHANNEL_DOMAIN_TURKEY */
+ {0x01}, /* 0x10, RT_CHANNEL_DOMAIN_JAPAN */
+ {0x02}, /* 0x11, RT_CHANNEL_DOMAIN_FCC_NO_DFS */
+ {0x01}, /* 0x12, RT_CHANNEL_DOMAIN_JAPAN_NO_DFS */
+ {0x00}, /* 0x13, RT_CHANNEL_DOMAIN_WORLD_WIDE_5G */
+ {0x02}, /* 0x14, RT_CHANNEL_DOMAIN_TAIWAN_NO_DFS */
+ {0x00}, /* 0x15, RT_CHANNEL_DOMAIN_ETSI_NO_DFS */
+ {0x00}, /* 0x16, RT_CHANNEL_DOMAIN_KOREA_NO_DFS */
+ {0x03}, /* 0x17, RT_CHANNEL_DOMAIN_JAPAN_NO_DFS */
+ {0x05}, /* 0x18, RT_CHANNEL_DOMAIN_PAKISTAN_NO_DFS */
+ {0x02}, /* 0x19, RT_CHANNEL_DOMAIN_TAIWAN2_NO_DFS */
+ {0x00}, /* 0x1A, */
+ {0x00}, /* 0x1B, */
+ {0x00}, /* 0x1C, */
+ {0x00}, /* 0x1D, */
+ {0x00}, /* 0x1E, */
+ {0x05}, /* 0x1F, RT_CHANNEL_DOMAIN_WORLD_WIDE_ONLY_5G */
+ /* 0x20 ~ 0x7F , New Define ===== */
+ {0x00}, /* 0x20, RT_CHANNEL_DOMAIN_WORLD_NULL */
+ {0x01}, /* 0x21, RT_CHANNEL_DOMAIN_ETSI1_NULL */
+ {0x02}, /* 0x22, RT_CHANNEL_DOMAIN_FCC1_NULL */
+ {0x03}, /* 0x23, RT_CHANNEL_DOMAIN_MKK1_NULL */
+ {0x04}, /* 0x24, RT_CHANNEL_DOMAIN_ETSI2_NULL */
+ {0x02}, /* 0x25, RT_CHANNEL_DOMAIN_FCC1_FCC1 */
+ {0x00}, /* 0x26, RT_CHANNEL_DOMAIN_WORLD_ETSI1 */
+ {0x03}, /* 0x27, RT_CHANNEL_DOMAIN_MKK1_MKK1 */
+ {0x00}, /* 0x28, RT_CHANNEL_DOMAIN_WORLD_KCC1 */
+ {0x00}, /* 0x29, RT_CHANNEL_DOMAIN_WORLD_FCC2 */
+ {0x00}, /* 0x2A, */
+ {0x00}, /* 0x2B, */
+ {0x00}, /* 0x2C, */
+ {0x00}, /* 0x2D, */
+ {0x00}, /* 0x2E, */
+ {0x00}, /* 0x2F, */
+ {0x00}, /* 0x30, RT_CHANNEL_DOMAIN_WORLD_FCC3 */
+ {0x00}, /* 0x31, RT_CHANNEL_DOMAIN_WORLD_FCC4 */
+ {0x00}, /* 0x32, RT_CHANNEL_DOMAIN_WORLD_FCC5 */
+ {0x00}, /* 0x33, RT_CHANNEL_DOMAIN_WORLD_FCC6 */
+ {0x02}, /* 0x34, RT_CHANNEL_DOMAIN_FCC1_FCC7 */
+ {0x00}, /* 0x35, RT_CHANNEL_DOMAIN_WORLD_ETSI2 */
+ {0x00}, /* 0x36, RT_CHANNEL_DOMAIN_WORLD_ETSI3 */
+ {0x03}, /* 0x37, RT_CHANNEL_DOMAIN_MKK1_MKK2 */
+ {0x03}, /* 0x38, RT_CHANNEL_DOMAIN_MKK1_MKK3 */
+ {0x02}, /* 0x39, RT_CHANNEL_DOMAIN_FCC1_NCC1 */
+ {0x00}, /* 0x3A, */
+ {0x00}, /* 0x3B, */
+ {0x00}, /* 0x3C, */
+ {0x00}, /* 0x3D, */
+ {0x00}, /* 0x3E, */
+ {0x00}, /* 0x3F, */
+ {0x02}, /* 0x40, RT_CHANNEL_DOMAIN_FCC1_NCC2 */
+ {0x03}, /* 0x41, RT_CHANNEL_DOMAIN_GLOBAL_DOAMIN_2G */
+};
+
+static struct rt_channel_plan_map RTW_CHANNEL_PLAN_MAP_REALTEK_DEFINE = {0x03}; /* use the combination for max channel numbers */
+
+/*
+ * Search the @param channel_num in given @param channel_set
+ * @ch_set: the given channel set
+ * @ch: the given channel number
+ *
+ * return the index of channel_num in channel_set, -1 if not found
+ */
+int rtw_ch_set_search_ch(struct rt_channel_info *ch_set, const u32 ch)
+{
+ int i;
+ for (i = 0; ch_set[i].ChannelNum != 0; i++) {
+ if (ch == ch_set[i].ChannelNum)
+ break;
+ }
+
+ if (i >= ch_set[i].ChannelNum)
+ return -1;
+ return i;
+}
+
+/****************************************************************************
+
+Following are the initialization functions for WiFi MLME
+
+*****************************************************************************/
+
+int init_hw_mlme_ext(struct adapter *padapter)
+{
+ struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv;
+
+ set_channel_bwmode(padapter, pmlmeext->cur_channel, pmlmeext->cur_ch_offset, pmlmeext->cur_bwmode);
+ return _SUCCESS;
+}
+
+static void init_mlme_ext_priv_value(struct adapter *padapter)
+{
+ struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv;
+ struct mlme_ext_info *pmlmeinfo = &(pmlmeext->mlmext_info);
+ unsigned char mixed_datarate[NumRates] = {
+ _1M_RATE_, _2M_RATE_, _5M_RATE_, _11M_RATE_, _6M_RATE_,
+ _9M_RATE_, _12M_RATE_, _18M_RATE_, _24M_RATE_, _36M_RATE_,
+ _48M_RATE_, _54M_RATE_, 0xff
+ };
+ unsigned char mixed_basicrate[NumRates] = {
+ _1M_RATE_, _2M_RATE_, _5M_RATE_, _11M_RATE_, _6M_RATE_,
+ _12M_RATE_, _24M_RATE_, 0xff,
+ };
+
+ atomic_set(&pmlmeext->event_seq, 0);
+ pmlmeext->mgnt_seq = 0;/* reset to zero when disconnect at client mode */
+
+ pmlmeext->cur_channel = padapter->registrypriv.channel;
+ pmlmeext->cur_bwmode = HT_CHANNEL_WIDTH_20;
+ pmlmeext->cur_ch_offset = HAL_PRIME_CHNL_OFFSET_DONT_CARE;
+ pmlmeext->oper_channel = pmlmeext->cur_channel;
+ pmlmeext->oper_bwmode = pmlmeext->cur_bwmode;
+ pmlmeext->oper_ch_offset = pmlmeext->cur_ch_offset;
+ pmlmeext->retry = 0;
+
+ pmlmeext->cur_wireless_mode = padapter->registrypriv.wireless_mode;
+
+ memcpy(pmlmeext->datarate, mixed_datarate, NumRates);
+ memcpy(pmlmeext->basicrate, mixed_basicrate, NumRates);
+
+ pmlmeext->tx_rate = IEEE80211_CCK_RATE_1MB;
+
+ pmlmeext->sitesurvey_res.state = SCAN_DISABLE;
+ pmlmeext->sitesurvey_res.channel_idx = 0;
+ pmlmeext->sitesurvey_res.bss_cnt = 0;
+ pmlmeext->scan_abort = false;
+
+ pmlmeinfo->state = WIFI_FW_NULL_STATE;
+ pmlmeinfo->reauth_count = 0;
+ pmlmeinfo->reassoc_count = 0;
+ pmlmeinfo->link_count = 0;
+ pmlmeinfo->auth_seq = 0;
+ pmlmeinfo->auth_algo = dot11AuthAlgrthm_Open;
+ pmlmeinfo->key_index = 0;
+ pmlmeinfo->iv = 0;
+
+ pmlmeinfo->enc_algo = _NO_PRIVACY_;
+ pmlmeinfo->authModeToggle = 0;
+
+ memset(pmlmeinfo->chg_txt, 0, 128);
+
+ pmlmeinfo->slotTime = SHORT_SLOT_TIME;
+ pmlmeinfo->preamble_mode = PREAMBLE_AUTO;
+
+ pmlmeinfo->dialogToken = 0;
+
+ pmlmeext->action_public_rxseq = 0xffff;
+ pmlmeext->action_public_dialog_token = 0xff;
+}
+
+static int has_channel(struct rt_channel_info *channel_set,
+ u8 chanset_size,
+ u8 chan) {
+ int i;
+
+ for (i = 0; i < chanset_size; i++) {
+ if (channel_set[i].ChannelNum == chan)
+ return 1;
+ }
+ return 0;
+}
+
+static void init_channel_list(struct adapter *padapter, struct rt_channel_info *channel_set,
+ u8 chanset_size,
+ struct p2p_channels *channel_list) {
+ struct p2p_oper_class_map op_class[] = {
+ { IEEE80211G, 81, 1, 13, 1, BW20 },
+ { IEEE80211G, 82, 14, 14, 1, BW20 },
+ { -1, 0, 0, 0, 0, BW20 }
+ };
+
+ int cla, op;
+
+ cla = 0;
+
+ for (op = 0; op_class[op].op_class; op++) {
+ u8 ch;
+ struct p2p_oper_class_map *o = &op_class[op];
+ struct p2p_reg_class *reg = NULL;
+
+ for (ch = o->min_chan; ch <= o->max_chan; ch += o->inc) {
+ if (!has_channel(channel_set, chanset_size, ch)) {
+ continue;
+ }
+
+ if ((0 == padapter->registrypriv.ht_enable) && (8 == o->inc))
+ continue;
+
+ if ((0 == (padapter->registrypriv.cbw40_enable & BIT(1))) &&
+ ((BW40MINUS == o->bw) || (BW40PLUS == o->bw)))
+ continue;
+
+ if (reg == NULL) {
+ reg = &channel_list->reg_class[cla];
+ cla++;
+ reg->reg_class = o->op_class;
+ reg->channels = 0;
+ }
+ reg->channel[reg->channels] = ch;
+ reg->channels++;
+ }
+ }
+ channel_list->reg_classes = cla;
+}
+
+static u8 init_channel_set(struct adapter *padapter, u8 ChannelPlan, struct rt_channel_info *channel_set)
+{
+ u8 index, chanset_size = 0;
+ u8 b2_4GBand = false;
+ u8 Index2G = 0;
+
+ memset(channel_set, 0, sizeof(struct rt_channel_info) * MAX_CHANNEL_NUM);
+
+ if (ChannelPlan >= RT_CHANNEL_DOMAIN_MAX && ChannelPlan != RT_CHANNEL_DOMAIN_REALTEK_DEFINE) {
+ DBG_88E("ChannelPlan ID %x error !!!!!\n", ChannelPlan);
+ return chanset_size;
+ }
+
+ if (padapter->registrypriv.wireless_mode & WIRELESS_11G) {
+ b2_4GBand = true;
+ if (RT_CHANNEL_DOMAIN_REALTEK_DEFINE == ChannelPlan)
+ Index2G = RTW_CHANNEL_PLAN_MAP_REALTEK_DEFINE.Index2G;
+ else
+ Index2G = RTW_ChannelPlanMap[ChannelPlan].Index2G;
+ }
+
+ if (b2_4GBand) {
+ for (index = 0; index < RTW_ChannelPlan2G[Index2G].Len; index++) {
+ channel_set[chanset_size].ChannelNum = RTW_ChannelPlan2G[Index2G].Channel[index];
+
+ if ((RT_CHANNEL_DOMAIN_GLOBAL_DOAMIN == ChannelPlan) ||/* Channel 1~11 is active, and 12~14 is passive */
+ (RT_CHANNEL_DOMAIN_GLOBAL_DOAMIN_2G == ChannelPlan)) {
+ if (channel_set[chanset_size].ChannelNum >= 1 && channel_set[chanset_size].ChannelNum <= 11)
+ channel_set[chanset_size].ScanType = SCAN_ACTIVE;
+ else if ((channel_set[chanset_size].ChannelNum >= 12 && channel_set[chanset_size].ChannelNum <= 14))
+ channel_set[chanset_size].ScanType = SCAN_PASSIVE;
+ } else if (RT_CHANNEL_DOMAIN_WORLD_WIDE_13 == ChannelPlan ||
+ RT_CHANNEL_DOMAIN_2G_WORLD == Index2G) {/* channel 12~13, passive scan */
+ if (channel_set[chanset_size].ChannelNum <= 11)
+ channel_set[chanset_size].ScanType = SCAN_ACTIVE;
+ else
+ channel_set[chanset_size].ScanType = SCAN_PASSIVE;
+ } else {
+ channel_set[chanset_size].ScanType = SCAN_ACTIVE;
+ }
+
+ chanset_size++;
+ }
+ }
+ return chanset_size;
+}
+
+int init_mlme_ext_priv(struct adapter *padapter)
+{
+ struct registry_priv *pregistrypriv = &padapter->registrypriv;
+ struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv;
+ struct mlme_priv *pmlmepriv = &(padapter->mlmepriv);
+ struct mlme_ext_info *pmlmeinfo = &(pmlmeext->mlmext_info);
+
+ pmlmeext->padapter = padapter;
+
+ init_mlme_ext_priv_value(padapter);
+ pmlmeinfo->bAcceptAddbaReq = pregistrypriv->bAcceptAddbaReq;
+
+ init_mlme_ext_timer(padapter);
+
+#ifdef CONFIG_88EU_AP_MODE
+ init_mlme_ap_info(padapter);
+#endif
+
+ pmlmeext->max_chan_nums = init_channel_set(padapter, pmlmepriv->ChannelPlan, pmlmeext->channel_set);
+ init_channel_list(padapter, pmlmeext->channel_set, pmlmeext->max_chan_nums, &pmlmeext->channel_list);
+
+ pmlmeext->chan_scan_time = SURVEY_TO;
+ pmlmeext->mlmeext_init = true;
+
+
+ pmlmeext->active_keep_alive_check = true;
+
+ return _SUCCESS;
+}
+
+void free_mlme_ext_priv(struct mlme_ext_priv *pmlmeext)
+{
+ struct adapter *padapter = pmlmeext->padapter;
+
+ if (!padapter)
+ return;
+
+ if (padapter->bDriverStopped) {
+ del_timer_sync(&pmlmeext->survey_timer);
+ del_timer_sync(&pmlmeext->link_timer);
+ }
+}
+
+static void _mgt_dispatcher(struct adapter *padapter, struct mlme_handler *ptable, struct recv_frame *precv_frame)
+{
+ u8 bc_addr[ETH_ALEN] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
+ u8 *pframe = precv_frame->rx_data;
+
+ if (ptable->func) {
+ /* receive the frames that ra(a1) is my address or ra(a1) is bc address. */
+ if (memcmp(GetAddr1Ptr(pframe), myid(&padapter->eeprompriv), ETH_ALEN) &&
+ memcmp(GetAddr1Ptr(pframe), bc_addr, ETH_ALEN))
+ return;
+ ptable->func(padapter, precv_frame);
+ }
+}
+
+void mgt_dispatcher(struct adapter *padapter, struct recv_frame *precv_frame)
+{
+ int index;
+ struct mlme_handler *ptable;
+#ifdef CONFIG_88EU_AP_MODE
+ struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
+#endif /* CONFIG_88EU_AP_MODE */
+ u8 bc_addr[ETH_ALEN] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
+ u8 *pframe = precv_frame->rx_data;
+ struct sta_info *psta = rtw_get_stainfo(&padapter->stapriv, GetAddr2Ptr(pframe));
+
+ RT_TRACE(_module_rtl871x_mlme_c_, _drv_info_,
+ ("+mgt_dispatcher: type(0x%x) subtype(0x%x)\n",
+ GetFrameType(pframe), GetFrameSubType(pframe)));
+
+ if (GetFrameType(pframe) != WIFI_MGT_TYPE) {
+ RT_TRACE(_module_rtl871x_mlme_c_, _drv_err_, ("mgt_dispatcher: type(0x%x) error!\n", GetFrameType(pframe)));
+ return;
+ }
+
+ /* receive the frames that ra(a1) is my address or ra(a1) is bc address. */
+ if (memcmp(GetAddr1Ptr(pframe), myid(&padapter->eeprompriv), ETH_ALEN) &&
+ memcmp(GetAddr1Ptr(pframe), bc_addr, ETH_ALEN))
+ return;
+
+ ptable = mlme_sta_tbl;
+
+ index = GetFrameSubType(pframe) >> 4;
+
+ if (index > 13) {
+ RT_TRACE(_module_rtl871x_mlme_c_, _drv_err_, ("Currently we do not support reserved sub-fr-type=%d\n", index));
+ return;
+ }
+ ptable += index;
+
+ if (psta != NULL) {
+ if (GetRetry(pframe)) {
+ if (precv_frame->attrib.seq_num ==
+ psta->RxMgmtFrameSeqNum) {
+ /* drop the duplicate management frame */
+ DBG_88E("Drop duplicate management frame with seq_num=%d.\n",
+ precv_frame->attrib.seq_num);
+ return;
+ }
+ }
+ psta->RxMgmtFrameSeqNum = precv_frame->attrib.seq_num;
+ }
+
+#ifdef CONFIG_88EU_AP_MODE
+ switch (GetFrameSubType(pframe)) {
+ case WIFI_AUTH:
+ if (check_fwstate(pmlmepriv, WIFI_AP_STATE))
+ ptable->func = &OnAuth;
+ else
+ ptable->func = &OnAuthClient;
+ /* fall through */
+ case WIFI_ASSOCREQ:
+ case WIFI_REASSOCREQ:
+ case WIFI_PROBEREQ:
+ case WIFI_BEACON:
+ case WIFI_ACTION:
+ _mgt_dispatcher(padapter, ptable, precv_frame);
+ break;
+ default:
+ _mgt_dispatcher(padapter, ptable, precv_frame);
+ break;
+ }
+#else
+ _mgt_dispatcher(padapter, ptable, precv_frame);
+#endif
+}
+
+/****************************************************************************
+
+Following are the callback functions for each subtype of the management frames
+
+*****************************************************************************/
+
+unsigned int OnProbeReq(struct adapter *padapter, struct recv_frame *precv_frame)
+{
+ unsigned int ielen;
+ unsigned char *p;
+ struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
+ struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv;
+ struct mlme_ext_info *pmlmeinfo = &(pmlmeext->mlmext_info);
+ struct wlan_bssid_ex *cur = &(pmlmeinfo->network);
+ u8 *pframe = precv_frame->rx_data;
+ uint len = precv_frame->len;
+ u8 is_valid_p2p_probereq = false;
+
+ if (check_fwstate(pmlmepriv, WIFI_STATION_STATE))
+ return _SUCCESS;
+
+ if (!check_fwstate(pmlmepriv, _FW_LINKED) &&
+ !check_fwstate(pmlmepriv, WIFI_ADHOC_MASTER_STATE|WIFI_AP_STATE))
+ return _SUCCESS;
+
+ p = rtw_get_ie(pframe + WLAN_HDR_A3_LEN + _PROBEREQ_IE_OFFSET_, _SSID_IE_, (int *)&ielen,
+ len - WLAN_HDR_A3_LEN - _PROBEREQ_IE_OFFSET_);
+
+ /* check (wildcard) SSID */
+ if (p != NULL) {
+ if (is_valid_p2p_probereq)
+ goto _issue_probersp;
+
+ if ((ielen != 0 && memcmp((void *)(p+2), (void *)cur->Ssid.Ssid, cur->Ssid.SsidLength)) ||
+ (ielen == 0 && pmlmeinfo->hidden_ssid_mode))
+ return _SUCCESS;
+
+_issue_probersp:
+
+ if (check_fwstate(pmlmepriv, _FW_LINKED) &&
+ pmlmepriv->cur_network.join_res)
+ issue_probersp(padapter, get_sa(pframe), is_valid_p2p_probereq);
+ }
+ return _SUCCESS;
+}
+
+unsigned int OnProbeRsp(struct adapter *padapter, struct recv_frame *precv_frame)
+{
+ struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv;
+
+ if (pmlmeext->sitesurvey_res.state == SCAN_PROCESS) {
+ report_survey_event(padapter, precv_frame);
+ return _SUCCESS;
+ }
+
+ return _SUCCESS;
+}
+
+unsigned int OnBeacon(struct adapter *padapter, struct recv_frame *precv_frame)
+{
+ int cam_idx;
+ struct sta_info *psta;
+ struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv;
+ struct mlme_ext_info *pmlmeinfo = &(pmlmeext->mlmext_info);
+ struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
+ struct sta_priv *pstapriv = &padapter->stapriv;
+ u8 *pframe = precv_frame->rx_data;
+ uint len = precv_frame->len;
+ struct wlan_bssid_ex *pbss;
+ int ret = _SUCCESS;
+ struct wlan_bssid_ex *pnetwork = &(pmlmeinfo->network);
+
+ if (pmlmeext->sitesurvey_res.state == SCAN_PROCESS) {
+ report_survey_event(padapter, precv_frame);
+ return _SUCCESS;
+ }
+
+ if (!memcmp(GetAddr3Ptr(pframe), pnetwork->MacAddress, ETH_ALEN)) {
+ if (pmlmeinfo->state & WIFI_FW_AUTH_NULL) {
+ /* we should update current network before auth, or some IE is wrong */
+ pbss = (struct wlan_bssid_ex *)rtw_malloc(sizeof(struct wlan_bssid_ex));
+ if (pbss) {
+ if (collect_bss_info(padapter, precv_frame, pbss) == _SUCCESS) {
+ update_network(&(pmlmepriv->cur_network.network), pbss, padapter, true);
+ rtw_get_bcn_info(&(pmlmepriv->cur_network));
+ }
+ kfree(pbss);
+ }
+
+ /* check the vendor of the assoc AP */
+ pmlmeinfo->assoc_AP_vendor = check_assoc_AP(pframe+sizeof(struct rtw_ieee80211_hdr_3addr), len-sizeof(struct rtw_ieee80211_hdr_3addr));
+
+ /* update TSF Value */
+ update_TSF(pmlmeext, pframe, len);
+
+ /* start auth */
+ start_clnt_auth(padapter);
+
+ return _SUCCESS;
+ }
+
+ if (((pmlmeinfo->state&0x03) == WIFI_FW_STATION_STATE) && (pmlmeinfo->state & WIFI_FW_ASSOC_SUCCESS)) {
+ psta = rtw_get_stainfo(pstapriv, GetAddr2Ptr(pframe));
+ if (psta != NULL) {
+ ret = rtw_check_bcn_info(padapter, pframe, len);
+ if (!ret) {
+ DBG_88E_LEVEL(_drv_info_, "ap has changed, disconnect now\n ");
+ receive_disconnect(padapter, pmlmeinfo->network.MacAddress , 65535);
+ return _SUCCESS;
+ }
+ /* update WMM, ERP in the beacon */
+ /* todo: the timer is used instead of the number of the beacon received */
+ if ((sta_rx_pkts(psta) & 0xf) == 0)
+ update_beacon_info(padapter, pframe, len, psta);
+ }
+ } else if ((pmlmeinfo->state&0x03) == WIFI_FW_ADHOC_STATE) {
+ psta = rtw_get_stainfo(pstapriv, GetAddr2Ptr(pframe));
+ if (psta != NULL) {
+ /* update WMM, ERP in the beacon */
+ /* todo: the timer is used instead of the number of the beacon received */
+ if ((sta_rx_pkts(psta) & 0xf) == 0)
+ update_beacon_info(padapter, pframe, len, psta);
+ } else {
+ /* allocate a new CAM entry for IBSS station */
+ cam_idx = allocate_fw_sta_entry(padapter);
+ if (cam_idx == NUM_STA)
+ goto _END_ONBEACON_;
+
+ /* get supported rate */
+ if (update_sta_support_rate(padapter, (pframe + WLAN_HDR_A3_LEN + _BEACON_IE_OFFSET_), (len - WLAN_HDR_A3_LEN - _BEACON_IE_OFFSET_), cam_idx) == _FAIL) {
+ pmlmeinfo->FW_sta_info[cam_idx].status = 0;
+ goto _END_ONBEACON_;
+ }
+
+ /* update TSF Value */
+ update_TSF(pmlmeext, pframe, len);
+
+ /* report sta add event */
+ report_add_sta_event(padapter, GetAddr2Ptr(pframe), cam_idx);
+ }
+ }
+ }
+
+_END_ONBEACON_:
+
+ return _SUCCESS;
+}
+
+unsigned int OnAuth(struct adapter *padapter, struct recv_frame *precv_frame)
+{
+#ifdef CONFIG_88EU_AP_MODE
+ unsigned int auth_mode, ie_len;
+ u16 seq;
+ unsigned char *sa, *p;
+ u16 algorithm;
+ int status;
+ static struct sta_info stat;
+ struct sta_info *pstat = NULL;
+ struct sta_priv *pstapriv = &padapter->stapriv;
+ struct security_priv *psecuritypriv = &padapter->securitypriv;
+ struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv;
+ struct mlme_ext_info *pmlmeinfo = &(pmlmeext->mlmext_info);
+ u8 *pframe = precv_frame->rx_data;
+ uint len = precv_frame->len;
+
+ if ((pmlmeinfo->state&0x03) != WIFI_FW_AP_STATE)
+ return _FAIL;
+
+ DBG_88E("+OnAuth\n");
+
+ sa = GetAddr2Ptr(pframe);
+
+ auth_mode = psecuritypriv->dot11AuthAlgrthm;
+ seq = le16_to_cpu(*(__le16 *)((size_t)pframe + WLAN_HDR_A3_LEN + 2));
+ algorithm = le16_to_cpu(*(__le16 *)((size_t)pframe + WLAN_HDR_A3_LEN));
+
+ DBG_88E("auth alg=%x, seq=%X\n", algorithm, seq);
+
+ if (auth_mode == 2 && psecuritypriv->dot11PrivacyAlgrthm != _WEP40_ &&
+ psecuritypriv->dot11PrivacyAlgrthm != _WEP104_)
+ auth_mode = 0;
+
+ if ((algorithm > 0 && auth_mode == 0) || /* rx a shared-key auth but shared not enabled */
+ (algorithm == 0 && auth_mode == 1)) { /* rx a open-system auth but shared-key is enabled */
+ DBG_88E("auth rejected due to bad alg [alg=%d, auth_mib=%d] %02X%02X%02X%02X%02X%02X\n",
+ algorithm, auth_mode, sa[0], sa[1], sa[2], sa[3], sa[4], sa[5]);
+
+ status = _STATS_NO_SUPP_ALG_;
+
+ goto auth_fail;
+ }
+
+ if (!rtw_access_ctrl(padapter, sa)) {
+ status = _STATS_UNABLE_HANDLE_STA_;
+ goto auth_fail;
+ }
+
+ pstat = rtw_get_stainfo(pstapriv, sa);
+ if (pstat == NULL) {
+ /* allocate a new one */
+ DBG_88E("going to alloc stainfo for sa=%pM\n", sa);
+ pstat = rtw_alloc_stainfo(pstapriv, sa);
+ if (pstat == NULL) {
+ DBG_88E(" Exceed the upper limit of supported clients...\n");
+ status = _STATS_UNABLE_HANDLE_STA_;
+ goto auth_fail;
+ }
+
+ pstat->state = WIFI_FW_AUTH_NULL;
+ pstat->auth_seq = 0;
+ } else {
+ spin_lock_bh(&pstapriv->asoc_list_lock);
+ if (!list_empty(&pstat->asoc_list)) {
+ list_del_init(&pstat->asoc_list);
+ pstapriv->asoc_list_cnt--;
+ }
+ spin_unlock_bh(&pstapriv->asoc_list_lock);
+
+ if (seq == 1) {
+ /* TODO: STA re_auth and auth timeout */
+ }
+ }
+
+ spin_lock_bh(&pstapriv->auth_list_lock);
+ if (list_empty(&pstat->auth_list)) {
+ list_add_tail(&pstat->auth_list, &pstapriv->auth_list);
+ pstapriv->auth_list_cnt++;
+ }
+ spin_unlock_bh(&pstapriv->auth_list_lock);
+
+ if (pstat->auth_seq == 0)
+ pstat->expire_to = pstapriv->auth_to;
+
+ if ((pstat->auth_seq + 1) != seq) {
+ DBG_88E("(1)auth rejected because out of seq [rx_seq=%d, exp_seq=%d]!\n",
+ seq, pstat->auth_seq+1);
+ status = _STATS_OUT_OF_AUTH_SEQ_;
+ goto auth_fail;
+ }
+
+ if (algorithm == 0 && (auth_mode == 0 || auth_mode == 2)) {
+ if (seq == 1) {
+ pstat->state &= ~WIFI_FW_AUTH_NULL;
+ pstat->state |= WIFI_FW_AUTH_SUCCESS;
+ pstat->expire_to = pstapriv->assoc_to;
+ pstat->authalg = algorithm;
+ } else {
+ DBG_88E("(2)auth rejected because out of seq [rx_seq=%d, exp_seq=%d]!\n",
+ seq, pstat->auth_seq+1);
+ status = _STATS_OUT_OF_AUTH_SEQ_;
+ goto auth_fail;
+ }
+ } else { /* shared system or auto authentication */
+ if (seq == 1) {
+ /* prepare for the challenging txt... */
+
+ pstat->state &= ~WIFI_FW_AUTH_NULL;
+ pstat->state |= WIFI_FW_AUTH_STATE;
+ pstat->authalg = algorithm;
+ pstat->auth_seq = 2;
+ } else if (seq == 3) {
+ /* checking for challenging txt... */
+ DBG_88E("checking for challenging txt...\n");
+
+ p = rtw_get_ie(pframe + WLAN_HDR_A3_LEN + 4 + _AUTH_IE_OFFSET_ , _CHLGETXT_IE_, (int *)&ie_len,
+ len - WLAN_HDR_A3_LEN - _AUTH_IE_OFFSET_ - 4);
+
+ if ((p == NULL) || (ie_len <= 0)) {
+ DBG_88E("auth rejected because challenge failure!(1)\n");
+ status = _STATS_CHALLENGE_FAIL_;
+ goto auth_fail;
+ }
+
+ if (!memcmp((void *)(p + 2), pstat->chg_txt, 128)) {
+ pstat->state &= (~WIFI_FW_AUTH_STATE);
+ pstat->state |= WIFI_FW_AUTH_SUCCESS;
+ /* challenging txt is correct... */
+ pstat->expire_to = pstapriv->assoc_to;
+ } else {
+ DBG_88E("auth rejected because challenge failure!\n");
+ status = _STATS_CHALLENGE_FAIL_;
+ goto auth_fail;
+ }
+ } else {
+ DBG_88E("(3)auth rejected because out of seq [rx_seq=%d, exp_seq=%d]!\n",
+ seq, pstat->auth_seq+1);
+ status = _STATS_OUT_OF_AUTH_SEQ_;
+ goto auth_fail;
+ }
+ }
+
+ /* Now, we are going to issue_auth... */
+ pstat->auth_seq = seq + 1;
+
+#ifdef CONFIG_88EU_AP_MODE
+ issue_auth(padapter, pstat, (unsigned short)(_STATS_SUCCESSFUL_));
+#endif
+
+ if (pstat->state & WIFI_FW_AUTH_SUCCESS)
+ pstat->auth_seq = 0;
+
+ return _SUCCESS;
+
+auth_fail:
+
+ if (pstat)
+ rtw_free_stainfo(padapter , pstat);
+
+ pstat = &stat;
+ memset((char *)pstat, '\0', sizeof(stat));
+ pstat->auth_seq = 2;
+ memcpy(pstat->hwaddr, sa, 6);
+
+#ifdef CONFIG_88EU_AP_MODE
+ issue_auth(padapter, pstat, (unsigned short)status);
+#endif
+
+#endif
+ return _FAIL;
+}
+
+unsigned int OnAuthClient(struct adapter *padapter, struct recv_frame *precv_frame)
+{
+ unsigned int seq, len, status, offset;
+ unsigned char *p;
+ unsigned int go2asoc = 0;
+ struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv;
+ struct mlme_ext_info *pmlmeinfo = &(pmlmeext->mlmext_info);
+ u8 *pframe = precv_frame->rx_data;
+ uint pkt_len = precv_frame->len;
+
+ DBG_88E("%s\n", __func__);
+
+ /* check A1 matches or not */
+ if (memcmp(myid(&(padapter->eeprompriv)), get_da(pframe), ETH_ALEN))
+ return _SUCCESS;
+
+ if (!(pmlmeinfo->state & WIFI_FW_AUTH_STATE))
+ return _SUCCESS;
+
+ offset = (GetPrivacy(pframe)) ? 4 : 0;
+
+ seq = le16_to_cpu(*(__le16 *)((size_t)pframe + WLAN_HDR_A3_LEN + offset + 2));
+ status = le16_to_cpu(*(__le16 *)((size_t)pframe + WLAN_HDR_A3_LEN + offset + 4));
+
+ if (status != 0) {
+ DBG_88E("clnt auth fail, status: %d\n", status);
+ if (status == 13) { /* pmlmeinfo->auth_algo == dot11AuthAlgrthm_Auto) */
+ if (pmlmeinfo->auth_algo == dot11AuthAlgrthm_Shared)
+ pmlmeinfo->auth_algo = dot11AuthAlgrthm_Open;
+ else
+ pmlmeinfo->auth_algo = dot11AuthAlgrthm_Shared;
+ }
+
+ set_link_timer(pmlmeext, 1);
+ goto authclnt_fail;
+ }
+
+ if (seq == 2) {
+ if (pmlmeinfo->auth_algo == dot11AuthAlgrthm_Shared) {
+ /* legendary shared system */
+ p = rtw_get_ie(pframe + WLAN_HDR_A3_LEN + _AUTH_IE_OFFSET_, _CHLGETXT_IE_, (int *)&len,
+ pkt_len - WLAN_HDR_A3_LEN - _AUTH_IE_OFFSET_);
+
+ if (p == NULL)
+ goto authclnt_fail;
+
+ memcpy((void *)(pmlmeinfo->chg_txt), (void *)(p + 2), len);
+ pmlmeinfo->auth_seq = 3;
+ issue_auth(padapter, NULL, 0);
+ set_link_timer(pmlmeext, REAUTH_TO);
+
+ return _SUCCESS;
+ } else {
+ /* open system */
+ go2asoc = 1;
+ }
+ } else if (seq == 4) {
+ if (pmlmeinfo->auth_algo == dot11AuthAlgrthm_Shared)
+ go2asoc = 1;
+ else
+ goto authclnt_fail;
+ } else {
+ /* this is also illegal */
+ goto authclnt_fail;
+ }
+
+ if (go2asoc) {
+ DBG_88E_LEVEL(_drv_info_, "auth success, start assoc\n");
+ start_clnt_assoc(padapter);
+ return _SUCCESS;
+ }
+authclnt_fail:
+ return _FAIL;
+}
+
+unsigned int OnAssocReq(struct adapter *padapter, struct recv_frame *precv_frame)
+{
+#ifdef CONFIG_88EU_AP_MODE
+ u16 capab_info;
+ struct rtw_ieee802_11_elems elems;
+ struct sta_info *pstat;
+ unsigned char reassoc, *p, *pos, *wpa_ie;
+ unsigned char WMM_IE[] = {0x00, 0x50, 0xf2, 0x02, 0x00, 0x01};
+ int i, ie_len, wpa_ie_len, left;
+ unsigned char supportRate[16];
+ int supportRateNum;
+ unsigned short status = _STATS_SUCCESSFUL_;
+ unsigned short frame_type, ie_offset = 0;
+ struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
+ struct security_priv *psecuritypriv = &padapter->securitypriv;
+ struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv;
+ struct mlme_ext_info *pmlmeinfo = &(pmlmeext->mlmext_info);
+ struct wlan_bssid_ex *cur = &(pmlmeinfo->network);
+ struct sta_priv *pstapriv = &padapter->stapriv;
+ u8 *pframe = precv_frame->rx_data;
+ uint pkt_len = precv_frame->len;
+
+ if ((pmlmeinfo->state&0x03) != WIFI_FW_AP_STATE)
+ return _FAIL;
+
+ frame_type = GetFrameSubType(pframe);
+ if (frame_type == WIFI_ASSOCREQ) {
+ reassoc = 0;
+ ie_offset = _ASOCREQ_IE_OFFSET_;
+ } else { /* WIFI_REASSOCREQ */
+ reassoc = 1;
+ ie_offset = _REASOCREQ_IE_OFFSET_;
+ }
+
+
+ if (pkt_len < IEEE80211_3ADDR_LEN + ie_offset) {
+ DBG_88E("handle_assoc(reassoc=%d) - too short payload (len=%lu)"
+ "\n", reassoc, (unsigned long)pkt_len);
+ return _FAIL;
+ }
+
+ pstat = rtw_get_stainfo(pstapriv, GetAddr2Ptr(pframe));
+ if (pstat == NULL) {
+ status = _RSON_CLS2_;
+ goto asoc_class2_error;
+ }
+
+ capab_info = get_unaligned_le16(pframe + WLAN_HDR_A3_LEN);
+
+ left = pkt_len - (IEEE80211_3ADDR_LEN + ie_offset);
+ pos = pframe + (IEEE80211_3ADDR_LEN + ie_offset);
+
+
+ DBG_88E("%s\n", __func__);
+
+ /* check if this stat has been successfully authenticated/assocated */
+ if (!((pstat->state) & WIFI_FW_AUTH_SUCCESS)) {
+ if (!((pstat->state) & WIFI_FW_ASSOC_SUCCESS)) {
+ status = _RSON_CLS2_;
+ goto asoc_class2_error;
+ } else {
+ pstat->state &= (~WIFI_FW_ASSOC_SUCCESS);
+ pstat->state |= WIFI_FW_ASSOC_STATE;
+ }
+ } else {
+ pstat->state &= (~WIFI_FW_AUTH_SUCCESS);
+ pstat->state |= WIFI_FW_ASSOC_STATE;
+ }
+ pstat->capability = capab_info;
+ /* now parse all ieee802_11 ie to point to elems */
+ if (rtw_ieee802_11_parse_elems(pos, left, &elems, 1) == ParseFailed ||
+ !elems.ssid) {
+ DBG_88E("STA %pM sent invalid association request\n",
+ pstat->hwaddr);
+ status = _STATS_FAILURE_;
+ goto OnAssocReqFail;
+ }
+
+
+ /* now we should check all the fields... */
+ /* checking SSID */
+ p = rtw_get_ie(pframe + WLAN_HDR_A3_LEN + ie_offset, _SSID_IE_, &ie_len,
+ pkt_len - WLAN_HDR_A3_LEN - ie_offset);
+ if (p == NULL)
+ status = _STATS_FAILURE_;
+
+ if (ie_len == 0) { /* broadcast ssid, however it is not allowed in assocreq */
+ status = _STATS_FAILURE_;
+ } else {
+ /* check if ssid match */
+ if (memcmp((void *)(p+2), cur->Ssid.Ssid, cur->Ssid.SsidLength))
+ status = _STATS_FAILURE_;
+
+ if (ie_len != cur->Ssid.SsidLength)
+ status = _STATS_FAILURE_;
+ }
+
+ if (_STATS_SUCCESSFUL_ != status)
+ goto OnAssocReqFail;
+
+ /* check if the supported rate is ok */
+ p = rtw_get_ie(pframe + WLAN_HDR_A3_LEN + ie_offset, _SUPPORTEDRATES_IE_, &ie_len, pkt_len - WLAN_HDR_A3_LEN - ie_offset);
+ if (p == NULL) {
+ DBG_88E("Rx a sta assoc-req which supported rate is empty!\n");
+ /* use our own rate set as statoin used */
+ /* memcpy(supportRate, AP_BSSRATE, AP_BSSRATE_LEN); */
+ /* supportRateNum = AP_BSSRATE_LEN; */
+
+ status = _STATS_FAILURE_;
+ goto OnAssocReqFail;
+ } else {
+ memcpy(supportRate, p+2, ie_len);
+ supportRateNum = ie_len;
+
+ p = rtw_get_ie(pframe + WLAN_HDR_A3_LEN + ie_offset, _EXT_SUPPORTEDRATES_IE_ , &ie_len,
+ pkt_len - WLAN_HDR_A3_LEN - ie_offset);
+ if (p != NULL) {
+ if (supportRateNum <= sizeof(supportRate)) {
+ memcpy(supportRate+supportRateNum, p+2, ie_len);
+ supportRateNum += ie_len;
+ }
+ }
+ }
+
+ /* todo: mask supportRate between AP & STA -> move to update raid */
+ /* get_matched_rate(pmlmeext, supportRate, &supportRateNum, 0); */
+
+ /* update station supportRate */
+ pstat->bssratelen = supportRateNum;
+ memcpy(pstat->bssrateset, supportRate, supportRateNum);
+ UpdateBrateTblForSoftAP(pstat->bssrateset, pstat->bssratelen);
+
+ /* check RSN/WPA/WPS */
+ pstat->dot8021xalg = 0;
+ pstat->wpa_psk = 0;
+ pstat->wpa_group_cipher = 0;
+ pstat->wpa2_group_cipher = 0;
+ pstat->wpa_pairwise_cipher = 0;
+ pstat->wpa2_pairwise_cipher = 0;
+ memset(pstat->wpa_ie, 0, sizeof(pstat->wpa_ie));
+ if ((psecuritypriv->wpa_psk & BIT(1)) && elems.rsn_ie) {
+ int group_cipher = 0, pairwise_cipher = 0;
+
+ wpa_ie = elems.rsn_ie;
+ wpa_ie_len = elems.rsn_ie_len;
+
+ if (rtw_parse_wpa2_ie(wpa_ie-2, wpa_ie_len+2, &group_cipher, &pairwise_cipher, NULL) == _SUCCESS) {
+ pstat->dot8021xalg = 1;/* psk, todo:802.1x */
+ pstat->wpa_psk |= BIT(1);
+
+ pstat->wpa2_group_cipher = group_cipher&psecuritypriv->wpa2_group_cipher;
+ pstat->wpa2_pairwise_cipher = pairwise_cipher&psecuritypriv->wpa2_pairwise_cipher;
+
+ if (!pstat->wpa2_group_cipher)
+ status = WLAN_STATUS_GROUP_CIPHER_NOT_VALID;
+
+ if (!pstat->wpa2_pairwise_cipher)
+ status = WLAN_STATUS_PAIRWISE_CIPHER_NOT_VALID;
+ } else {
+ status = WLAN_STATUS_INVALID_IE;
+ }
+ } else if ((psecuritypriv->wpa_psk & BIT(0)) && elems.wpa_ie) {
+ int group_cipher = 0, pairwise_cipher = 0;
+
+ wpa_ie = elems.wpa_ie;
+ wpa_ie_len = elems.wpa_ie_len;
+
+ if (rtw_parse_wpa_ie(wpa_ie-2, wpa_ie_len+2, &group_cipher, &pairwise_cipher, NULL) == _SUCCESS) {
+ pstat->dot8021xalg = 1;/* psk, todo:802.1x */
+ pstat->wpa_psk |= BIT(0);
+
+ pstat->wpa_group_cipher = group_cipher&psecuritypriv->wpa_group_cipher;
+ pstat->wpa_pairwise_cipher = pairwise_cipher&psecuritypriv->wpa_pairwise_cipher;
+
+ if (!pstat->wpa_group_cipher)
+ status = WLAN_STATUS_GROUP_CIPHER_NOT_VALID;
+
+ if (!pstat->wpa_pairwise_cipher)
+ status = WLAN_STATUS_PAIRWISE_CIPHER_NOT_VALID;
+ } else {
+ status = WLAN_STATUS_INVALID_IE;
+ }
+ } else {
+ wpa_ie = NULL;
+ wpa_ie_len = 0;
+ }
+
+ if (_STATS_SUCCESSFUL_ != status)
+ goto OnAssocReqFail;
+
+ pstat->flags &= ~(WLAN_STA_WPS | WLAN_STA_MAYBE_WPS);
+ if (wpa_ie == NULL) {
+ if (elems.wps_ie) {
+ DBG_88E("STA included WPS IE in "
+ "(Re)Association Request - assume WPS is "
+ "used\n");
+ pstat->flags |= WLAN_STA_WPS;
+ /* wpabuf_free(sta->wps_ie); */
+ /* sta->wps_ie = wpabuf_alloc_copy(elems.wps_ie + 4, */
+ /* elems.wps_ie_len - 4); */
+ } else {
+ DBG_88E("STA did not include WPA/RSN IE "
+ "in (Re)Association Request - possible WPS "
+ "use\n");
+ pstat->flags |= WLAN_STA_MAYBE_WPS;
+ }
+
+
+ /* AP support WPA/RSN, and sta is going to do WPS, but AP is not ready */
+ /* that the selected registrar of AP is _FLASE */
+ if ((psecuritypriv->wpa_psk > 0) && (pstat->flags & (WLAN_STA_WPS|WLAN_STA_MAYBE_WPS))) {
+ if (pmlmepriv->wps_beacon_ie) {
+ u8 selected_registrar = 0;
+
+ rtw_get_wps_attr_content(pmlmepriv->wps_beacon_ie, pmlmepriv->wps_beacon_ie_len, WPS_ATTR_SELECTED_REGISTRAR , &selected_registrar, NULL);
+
+ if (!selected_registrar) {
+ DBG_88E("selected_registrar is false , or AP is not ready to do WPS\n");
+
+ status = _STATS_UNABLE_HANDLE_STA_;
+
+ goto OnAssocReqFail;
+ }
+ }
+ }
+ } else {
+ int copy_len;
+
+ if (psecuritypriv->wpa_psk == 0) {
+ DBG_88E("STA %pM: WPA/RSN IE in association "
+ "request, but AP don't support WPA/RSN\n", pstat->hwaddr);
+
+ status = WLAN_STATUS_INVALID_IE;
+
+ goto OnAssocReqFail;
+ }
+
+ if (elems.wps_ie) {
+ DBG_88E("STA included WPS IE in "
+ "(Re)Association Request - WPS is "
+ "used\n");
+ pstat->flags |= WLAN_STA_WPS;
+ copy_len = 0;
+ } else {
+ copy_len = ((wpa_ie_len+2) > sizeof(pstat->wpa_ie)) ? (sizeof(pstat->wpa_ie)) : (wpa_ie_len+2);
+ }
+ if (copy_len > 0)
+ memcpy(pstat->wpa_ie, wpa_ie-2, copy_len);
+ }
+ /* check if there is WMM IE & support WWM-PS */
+ pstat->flags &= ~WLAN_STA_WME;
+ pstat->qos_option = 0;
+ pstat->qos_info = 0;
+ pstat->has_legacy_ac = true;
+ pstat->uapsd_vo = 0;
+ pstat->uapsd_vi = 0;
+ pstat->uapsd_be = 0;
+ pstat->uapsd_bk = 0;
+ if (pmlmepriv->qospriv.qos_option) {
+ p = pframe + WLAN_HDR_A3_LEN + ie_offset; ie_len = 0;
+ for (;;) {
+ p = rtw_get_ie(p, _VENDOR_SPECIFIC_IE_, &ie_len, pkt_len - WLAN_HDR_A3_LEN - ie_offset);
+ if (p != NULL) {
+ if (!memcmp(p+2, WMM_IE, 6)) {
+ pstat->flags |= WLAN_STA_WME;
+
+ pstat->qos_option = 1;
+ pstat->qos_info = *(p+8);
+
+ pstat->max_sp_len = (pstat->qos_info>>5)&0x3;
+
+ if ((pstat->qos_info&0xf) != 0xf)
+ pstat->has_legacy_ac = true;
+ else
+ pstat->has_legacy_ac = false;
+
+ if (pstat->qos_info&0xf) {
+ if (pstat->qos_info&BIT(0))
+ pstat->uapsd_vo = BIT(0)|BIT(1);
+ else
+ pstat->uapsd_vo = 0;
+
+ if (pstat->qos_info&BIT(1))
+ pstat->uapsd_vi = BIT(0)|BIT(1);
+ else
+ pstat->uapsd_vi = 0;
+
+ if (pstat->qos_info&BIT(2))
+ pstat->uapsd_bk = BIT(0)|BIT(1);
+ else
+ pstat->uapsd_bk = 0;
+
+ if (pstat->qos_info&BIT(3))
+ pstat->uapsd_be = BIT(0)|BIT(1);
+ else
+ pstat->uapsd_be = 0;
+ }
+ break;
+ }
+ } else {
+ break;
+ }
+ p = p + ie_len + 2;
+ }
+ }
+
+ /* save HT capabilities in the sta object */
+ memset(&pstat->htpriv.ht_cap, 0, sizeof(struct rtw_ieee80211_ht_cap));
+ if (elems.ht_capabilities && elems.ht_capabilities_len >= sizeof(struct rtw_ieee80211_ht_cap)) {
+ pstat->flags |= WLAN_STA_HT;
+
+ pstat->flags |= WLAN_STA_WME;
+
+ memcpy(&pstat->htpriv.ht_cap, elems.ht_capabilities, sizeof(struct rtw_ieee80211_ht_cap));
+ } else {
+ pstat->flags &= ~WLAN_STA_HT;
+ }
+ if ((!pmlmepriv->htpriv.ht_option) && (pstat->flags&WLAN_STA_HT)) {
+ status = _STATS_FAILURE_;
+ goto OnAssocReqFail;
+ }
+
+ if ((pstat->flags & WLAN_STA_HT) &&
+ ((pstat->wpa2_pairwise_cipher&WPA_CIPHER_TKIP) ||
+ (pstat->wpa_pairwise_cipher&WPA_CIPHER_TKIP))) {
+ DBG_88E("HT: %pM tried to "
+ "use TKIP with HT association\n", pstat->hwaddr);
+
+ /* status = WLAN_STATUS_CIPHER_REJECTED_PER_POLICY; */
+ /* goto OnAssocReqFail; */
+ }
+
+ pstat->flags |= WLAN_STA_NONERP;
+ for (i = 0; i < pstat->bssratelen; i++) {
+ if ((pstat->bssrateset[i] & 0x7f) > 22) {
+ pstat->flags &= ~WLAN_STA_NONERP;
+ break;
+ }
+ }
+
+ if (pstat->capability & WLAN_CAPABILITY_SHORT_PREAMBLE)
+ pstat->flags |= WLAN_STA_SHORT_PREAMBLE;
+ else
+ pstat->flags &= ~WLAN_STA_SHORT_PREAMBLE;
+
+
+
+ if (status != _STATS_SUCCESSFUL_)
+ goto OnAssocReqFail;
+
+ /* TODO: identify_proprietary_vendor_ie(); */
+ /* Realtek proprietary IE */
+ /* identify if this is Broadcom sta */
+ /* identify if this is ralink sta */
+ /* Customer proprietary IE */
+
+ /* get a unique AID */
+ if (pstat->aid > 0) {
+ DBG_88E(" old AID %d\n", pstat->aid);
+ } else {
+ for (pstat->aid = 1; pstat->aid <= NUM_STA; pstat->aid++)
+ if (pstapriv->sta_aid[pstat->aid - 1] == NULL)
+ break;
+
+ /* if (pstat->aid > NUM_STA) { */
+ if (pstat->aid > pstapriv->max_num_sta) {
+ pstat->aid = 0;
+
+ DBG_88E(" no room for more AIDs\n");
+
+ status = WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA;
+
+ goto OnAssocReqFail;
+ } else {
+ pstapriv->sta_aid[pstat->aid - 1] = pstat;
+ DBG_88E("allocate new AID=(%d)\n", pstat->aid);
+ }
+ }
+
+ pstat->state &= (~WIFI_FW_ASSOC_STATE);
+ pstat->state |= WIFI_FW_ASSOC_SUCCESS;
+
+ spin_lock_bh(&pstapriv->auth_list_lock);
+ if (!list_empty(&pstat->auth_list)) {
+ list_del_init(&pstat->auth_list);
+ pstapriv->auth_list_cnt--;
+ }
+ spin_unlock_bh(&pstapriv->auth_list_lock);
+
+ spin_lock_bh(&pstapriv->asoc_list_lock);
+ if (list_empty(&pstat->asoc_list)) {
+ pstat->expire_to = pstapriv->expire_to;
+ list_add_tail(&pstat->asoc_list, &pstapriv->asoc_list);
+ pstapriv->asoc_list_cnt++;
+ }
+ spin_unlock_bh(&pstapriv->asoc_list_lock);
+
+ /* now the station is qualified to join our BSS... */
+ if (pstat && (pstat->state & WIFI_FW_ASSOC_SUCCESS) && (_STATS_SUCCESSFUL_ == status)) {
+#ifdef CONFIG_88EU_AP_MODE
+ /* 1 bss_cap_update & sta_info_update */
+ bss_cap_update_on_sta_join(padapter, pstat);
+ sta_info_update(padapter, pstat);
+
+ /* issue assoc rsp before notify station join event. */
+ if (frame_type == WIFI_ASSOCREQ)
+ issue_asocrsp(padapter, status, pstat, WIFI_ASSOCRSP);
+ else
+ issue_asocrsp(padapter, status, pstat, WIFI_REASSOCRSP);
+
+ /* 2 - report to upper layer */
+ DBG_88E("indicate_sta_join_event to upper layer - hostapd\n");
+ rtw_indicate_sta_assoc_event(padapter, pstat);
+
+ /* 3-(1) report sta add event */
+ report_add_sta_event(padapter, pstat->hwaddr, pstat->aid);
+#endif
+ }
+
+ return _SUCCESS;
+
+asoc_class2_error:
+
+#ifdef CONFIG_88EU_AP_MODE
+ issue_deauth(padapter, (void *)GetAddr2Ptr(pframe), status);
+#endif
+
+ return _FAIL;
+
+OnAssocReqFail:
+
+
+#ifdef CONFIG_88EU_AP_MODE
+ pstat->aid = 0;
+ if (frame_type == WIFI_ASSOCREQ)
+ issue_asocrsp(padapter, status, pstat, WIFI_ASSOCRSP);
+ else
+ issue_asocrsp(padapter, status, pstat, WIFI_REASSOCRSP);
+#endif
+
+
+#endif /* CONFIG_88EU_AP_MODE */
+
+ return _FAIL;
+}
+
+unsigned int OnAssocRsp(struct adapter *padapter, struct recv_frame *precv_frame)
+{
+ uint i;
+ int res;
+ unsigned short status;
+ struct ndis_802_11_var_ie *pIE;
+ struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
+ struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv;
+ struct mlme_ext_info *pmlmeinfo = &(pmlmeext->mlmext_info);
+ /* struct wlan_bssid_ex *cur_network = &(pmlmeinfo->network); */
+ u8 *pframe = precv_frame->rx_data;
+ uint pkt_len = precv_frame->len;
+
+ DBG_88E("%s\n", __func__);
+
+ /* check A1 matches or not */
+ if (memcmp(myid(&(padapter->eeprompriv)), get_da(pframe), ETH_ALEN))
+ return _SUCCESS;
+
+ if (!(pmlmeinfo->state & (WIFI_FW_AUTH_SUCCESS | WIFI_FW_ASSOC_STATE)))
+ return _SUCCESS;
+
+ if (pmlmeinfo->state & WIFI_FW_ASSOC_SUCCESS)
+ return _SUCCESS;
+
+ del_timer_sync(&pmlmeext->link_timer);
+
+ /* status */
+ status = le16_to_cpu(*(__le16 *)(pframe + WLAN_HDR_A3_LEN + 2));
+ if (status > 0) {
+ DBG_88E("assoc reject, status code: %d\n", status);
+ pmlmeinfo->state = WIFI_FW_NULL_STATE;
+ res = -4;
+ goto report_assoc_result;
+ }
+
+ /* get capabilities */
+ pmlmeinfo->capability = le16_to_cpu(*(__le16 *)(pframe + WLAN_HDR_A3_LEN));
+
+ /* set slot time */
+ pmlmeinfo->slotTime = (pmlmeinfo->capability & BIT(10)) ? 9 : 20;
+
+ /* AID */
+ pmlmeinfo->aid = (int)(le16_to_cpu(*(__le16 *)(pframe + WLAN_HDR_A3_LEN + 4))&0x3fff);
+ res = pmlmeinfo->aid;
+
+ /* following are moved to join event callback function */
+ /* to handle HT, WMM, rate adaptive, update MAC reg */
+ /* for not to handle the synchronous IO in the tasklet */
+ for (i = (6 + WLAN_HDR_A3_LEN); i < pkt_len;) {
+ pIE = (struct ndis_802_11_var_ie *)(pframe + i);
+
+ switch (pIE->ElementID) {
+ case _VENDOR_SPECIFIC_IE_:
+ if (!memcmp(pIE->data, WMM_PARA_OUI, 6)) /* WMM */
+ WMM_param_handler(padapter, pIE);
+ break;
+ case _HT_CAPABILITY_IE_: /* HT caps */
+ HT_caps_handler(padapter, pIE);
+ break;
+ case _HT_EXTRA_INFO_IE_: /* HT info */
+ HT_info_handler(padapter, pIE);
+ break;
+ case _ERPINFO_IE_:
+ ERP_IE_handler(padapter, pIE);
+ default:
+ break;
+ }
+
+ i += (pIE->Length + 2);
+ }
+
+ pmlmeinfo->state &= (~WIFI_FW_ASSOC_STATE);
+ pmlmeinfo->state |= WIFI_FW_ASSOC_SUCCESS;
+
+ /* Update Basic Rate Table for spec, 2010-12-28 , by thomas */
+ UpdateBrateTbl(padapter, pmlmeinfo->network.SupportedRates);
+
+report_assoc_result:
+ if (res > 0) {
+ rtw_buf_update(&pmlmepriv->assoc_rsp, &pmlmepriv->assoc_rsp_len, pframe, pkt_len);
+ } else {
+ rtw_buf_free(&pmlmepriv->assoc_rsp, &pmlmepriv->assoc_rsp_len);
+ }
+
+ report_join_res(padapter, res);
+
+ return _SUCCESS;
+}
+
+unsigned int OnDeAuth(struct adapter *padapter, struct recv_frame *precv_frame)
+{
+ unsigned short reason;
+ struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
+ struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv;
+ struct mlme_ext_info *pmlmeinfo = &(pmlmeext->mlmext_info);
+ u8 *pframe = precv_frame->rx_data;
+ struct wlan_bssid_ex *pnetwork = &(pmlmeinfo->network);
+
+ /* check A3 */
+ if (memcmp(GetAddr3Ptr(pframe), pnetwork->MacAddress, ETH_ALEN))
+ return _SUCCESS;
+
+ reason = le16_to_cpu(*(__le16 *)(pframe + WLAN_HDR_A3_LEN));
+
+ DBG_88E("%s Reason code(%d)\n", __func__, reason);
+
+#ifdef CONFIG_88EU_AP_MODE
+ if (check_fwstate(pmlmepriv, WIFI_AP_STATE)) {
+ struct sta_info *psta;
+ struct sta_priv *pstapriv = &padapter->stapriv;
+
+ DBG_88E_LEVEL(_drv_always_, "ap recv deauth reason code(%d) sta:%pM\n",
+ reason, GetAddr2Ptr(pframe));
+
+ psta = rtw_get_stainfo(pstapriv, GetAddr2Ptr(pframe));
+ if (psta) {
+ u8 updated = 0;
+
+ spin_lock_bh(&pstapriv->asoc_list_lock);
+ if (!list_empty(&psta->asoc_list)) {
+ list_del_init(&psta->asoc_list);
+ pstapriv->asoc_list_cnt--;
+ updated = ap_free_sta(padapter, psta, false, reason);
+ }
+ spin_unlock_bh(&pstapriv->asoc_list_lock);
+
+ associated_clients_update(padapter, updated);
+ }
+
+
+ return _SUCCESS;
+ } else
+#endif
+ {
+ DBG_88E_LEVEL(_drv_always_, "sta recv deauth reason code(%d) sta:%pM\n",
+ reason, GetAddr3Ptr(pframe));
+
+ receive_disconnect(padapter, GetAddr3Ptr(pframe) , reason);
+ }
+ pmlmepriv->LinkDetectInfo.bBusyTraffic = false;
+ return _SUCCESS;
+}
+
+unsigned int OnDisassoc(struct adapter *padapter, struct recv_frame *precv_frame)
+{
+ u16 reason;
+ struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
+ struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv;
+ struct mlme_ext_info *pmlmeinfo = &(pmlmeext->mlmext_info);
+ u8 *pframe = precv_frame->rx_data;
+ struct wlan_bssid_ex *pnetwork = &(pmlmeinfo->network);
+
+ /* check A3 */
+ if (memcmp(GetAddr3Ptr(pframe), pnetwork->MacAddress, ETH_ALEN))
+ return _SUCCESS;
+
+ reason = le16_to_cpu(*(__le16 *)(pframe + WLAN_HDR_A3_LEN));
+
+ DBG_88E("%s Reason code(%d)\n", __func__, reason);
+
+#ifdef CONFIG_88EU_AP_MODE
+ if (check_fwstate(pmlmepriv, WIFI_AP_STATE)) {
+ struct sta_info *psta;
+ struct sta_priv *pstapriv = &padapter->stapriv;
+
+ DBG_88E_LEVEL(_drv_always_, "ap recv disassoc reason code(%d) sta:%pM\n",
+ reason, GetAddr2Ptr(pframe));
+
+ psta = rtw_get_stainfo(pstapriv, GetAddr2Ptr(pframe));
+ if (psta) {
+ u8 updated = 0;
+
+ spin_lock_bh(&pstapriv->asoc_list_lock);
+ if (!list_empty(&psta->asoc_list)) {
+ list_del_init(&psta->asoc_list);
+ pstapriv->asoc_list_cnt--;
+ updated = ap_free_sta(padapter, psta, false, reason);
+ }
+ spin_unlock_bh(&pstapriv->asoc_list_lock);
+
+ associated_clients_update(padapter, updated);
+ }
+
+ return _SUCCESS;
+ } else
+#endif
+ {
+ DBG_88E_LEVEL(_drv_always_, "ap recv disassoc reason code(%d) sta:%pM\n",
+ reason, GetAddr3Ptr(pframe));
+
+ receive_disconnect(padapter, GetAddr3Ptr(pframe), reason);
+ }
+ pmlmepriv->LinkDetectInfo.bBusyTraffic = false;
+ return _SUCCESS;
+}
+
+unsigned int OnAtim(struct adapter *padapter, struct recv_frame *precv_frame)
+{
+ DBG_88E("%s\n", __func__);
+ return _SUCCESS;
+}
+
+unsigned int on_action_spct(struct adapter *padapter, struct recv_frame *precv_frame)
+{
+ struct sta_info *psta = NULL;
+ struct sta_priv *pstapriv = &padapter->stapriv;
+ u8 *pframe = precv_frame->rx_data;
+ u8 *frame_body = (u8 *)(pframe + sizeof(struct rtw_ieee80211_hdr_3addr));
+ u8 category;
+ u8 action;
+
+ DBG_88E(FUNC_NDEV_FMT"\n", FUNC_NDEV_ARG(padapter->pnetdev));
+
+ psta = rtw_get_stainfo(pstapriv, GetAddr2Ptr(pframe));
+
+ if (!psta)
+ goto exit;
+
+ category = frame_body[0];
+ if (category != RTW_WLAN_CATEGORY_SPECTRUM_MGMT)
+ goto exit;
+
+ action = frame_body[1];
+ switch (action) {
+ case RTW_WLAN_ACTION_SPCT_MSR_REQ:
+ case RTW_WLAN_ACTION_SPCT_MSR_RPRT:
+ case RTW_WLAN_ACTION_SPCT_TPC_REQ:
+ case RTW_WLAN_ACTION_SPCT_TPC_RPRT:
+ break;
+ case RTW_WLAN_ACTION_SPCT_CHL_SWITCH:
+ break;
+ default:
+ break;
+ }
+
+exit:
+ return _FAIL;
+}
+
+unsigned int OnAction_qos(struct adapter *padapter, struct recv_frame *precv_frame)
+{
+ return _SUCCESS;
+}
+
+unsigned int OnAction_dls(struct adapter *padapter, struct recv_frame *precv_frame)
+{
+ return _SUCCESS;
+}
+
+unsigned int OnAction_back(struct adapter *padapter, struct recv_frame *precv_frame)
+{
+ u8 *addr;
+ struct sta_info *psta = NULL;
+ struct recv_reorder_ctrl *preorder_ctrl;
+ unsigned char *frame_body;
+ unsigned char category, action;
+ unsigned short tid, status, reason_code = 0;
+ struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv;
+ struct mlme_ext_info *pmlmeinfo = &(pmlmeext->mlmext_info);
+ u8 *pframe = precv_frame->rx_data;
+ struct sta_priv *pstapriv = &padapter->stapriv;
+ /* check RA matches or not */
+ if (memcmp(myid(&(padapter->eeprompriv)), GetAddr1Ptr(pframe),
+ ETH_ALEN))/* for if1, sta/ap mode */
+ return _SUCCESS;
+
+ DBG_88E("%s\n", __func__);
+
+ if ((pmlmeinfo->state&0x03) != WIFI_FW_AP_STATE)
+ if (!(pmlmeinfo->state & WIFI_FW_ASSOC_SUCCESS))
+ return _SUCCESS;
+
+ addr = GetAddr2Ptr(pframe);
+ psta = rtw_get_stainfo(pstapriv, addr);
+
+ if (psta == NULL)
+ return _SUCCESS;
+
+ frame_body = (unsigned char *)(pframe + sizeof(struct rtw_ieee80211_hdr_3addr));
+
+ category = frame_body[0];
+ if (category == RTW_WLAN_CATEGORY_BACK) { /* representing Block Ack */
+ if (!pmlmeinfo->HT_enable)
+ return _SUCCESS;
+ action = frame_body[1];
+ DBG_88E("%s, action=%d\n", __func__, action);
+ switch (action) {
+ case RTW_WLAN_ACTION_ADDBA_REQ: /* ADDBA request */
+ memcpy(&(pmlmeinfo->ADDBA_req), &(frame_body[2]), sizeof(struct ADDBA_request));
+ process_addba_req(padapter, (u8 *)&(pmlmeinfo->ADDBA_req), addr);
+
+ if (pmlmeinfo->bAcceptAddbaReq)
+ issue_action_BA(padapter, addr, RTW_WLAN_ACTION_ADDBA_RESP, 0);
+ else
+ issue_action_BA(padapter, addr, RTW_WLAN_ACTION_ADDBA_RESP, 37);/* reject ADDBA Req */
+ break;
+ case RTW_WLAN_ACTION_ADDBA_RESP: /* ADDBA response */
+ status = get_unaligned_le16(&frame_body[3]);
+ tid = (frame_body[5] >> 2) & 0x7;
+ if (status == 0) { /* successful */
+ DBG_88E("agg_enable for TID=%d\n", tid);
+ psta->htpriv.agg_enable_bitmap |= 1 << tid;
+ psta->htpriv.candidate_tid_bitmap &= ~BIT(tid);
+ } else {
+ psta->htpriv.agg_enable_bitmap &= ~BIT(tid);
+ }
+ break;
+ case RTW_WLAN_ACTION_DELBA: /* DELBA */
+ if ((frame_body[3] & BIT(3)) == 0) {
+ psta->htpriv.agg_enable_bitmap &= ~(1 << ((frame_body[3] >> 4) & 0xf));
+ psta->htpriv.candidate_tid_bitmap &= ~(1 << ((frame_body[3] >> 4) & 0xf));
+ reason_code = get_unaligned_le16(&frame_body[4]);
+ } else if ((frame_body[3] & BIT(3)) == BIT(3)) {
+ tid = (frame_body[3] >> 4) & 0x0F;
+ preorder_ctrl = &psta->recvreorder_ctrl[tid];
+ preorder_ctrl->enable = false;
+ preorder_ctrl->indicate_seq = 0xffff;
+ }
+ DBG_88E("%s(): DELBA: %x(%x)\n", __func__, pmlmeinfo->agg_enable_bitmap, reason_code);
+ /* todo: how to notify the host while receiving DELETE BA */
+ break;
+ default:
+ break;
+ }
+ }
+ return _SUCCESS;
+}
+
+static s32 rtw_action_public_decache(struct recv_frame *recv_frame, s32 token)
+{
+ struct adapter *adapter = recv_frame->adapter;
+ struct mlme_ext_priv *mlmeext = &(adapter->mlmeextpriv);
+ u8 *frame = recv_frame->rx_data;
+ u16 seq_ctrl = ((recv_frame->attrib.seq_num&0xffff) << 4) |
+ (recv_frame->attrib.frag_num & 0xf);
+
+ if (GetRetry(frame)) {
+ if (token >= 0) {
+ if ((seq_ctrl == mlmeext->action_public_rxseq) && (token == mlmeext->action_public_dialog_token)) {
+ DBG_88E(FUNC_ADPT_FMT" seq_ctrl = 0x%x, rxseq = 0x%x, token:%d\n",
+ FUNC_ADPT_ARG(adapter), seq_ctrl, mlmeext->action_public_rxseq, token);
+ return _FAIL;
+ }
+ } else {
+ if (seq_ctrl == mlmeext->action_public_rxseq) {
+ DBG_88E(FUNC_ADPT_FMT" seq_ctrl = 0x%x, rxseq = 0x%x\n",
+ FUNC_ADPT_ARG(adapter), seq_ctrl, mlmeext->action_public_rxseq);
+ return _FAIL;
+ }
+ }
+ }
+
+ mlmeext->action_public_rxseq = seq_ctrl;
+
+ if (token >= 0)
+ mlmeext->action_public_dialog_token = token;
+
+ return _SUCCESS;
+}
+
+static unsigned int on_action_public_p2p(struct recv_frame *precv_frame)
+{
+ u8 *pframe = precv_frame->rx_data;
+ u8 *frame_body;
+ u8 dialogToken = 0;
+ frame_body = (unsigned char *)(pframe + sizeof(struct rtw_ieee80211_hdr_3addr));
+
+ dialogToken = frame_body[7];
+
+ if (rtw_action_public_decache(precv_frame, dialogToken) == _FAIL)
+ return _FAIL;
+
+ return _SUCCESS;
+}
+
+static unsigned int on_action_public_vendor(struct recv_frame *precv_frame)
+{
+ unsigned int ret = _FAIL;
+ u8 *pframe = precv_frame->rx_data;
+ u8 *frame_body = pframe + sizeof(struct rtw_ieee80211_hdr_3addr);
+
+ if (!memcmp(frame_body + 2, P2P_OUI, 4))
+ ret = on_action_public_p2p(precv_frame);
+
+ return ret;
+}
+
+static unsigned int on_action_public_default(struct recv_frame *precv_frame, u8 action)
+{
+ unsigned int ret = _FAIL;
+ u8 *pframe = precv_frame->rx_data;
+ u8 *frame_body = pframe + sizeof(struct rtw_ieee80211_hdr_3addr);
+ u8 token;
+
+ token = frame_body[2];
+
+ if (rtw_action_public_decache(precv_frame, token) == _FAIL)
+ goto exit;
+
+ ret = _SUCCESS;
+
+exit:
+ return ret;
+}
+
+unsigned int on_action_public(struct adapter *padapter, struct recv_frame *precv_frame)
+{
+ unsigned int ret = _FAIL;
+ u8 *pframe = precv_frame->rx_data;
+ u8 *frame_body = pframe + sizeof(struct rtw_ieee80211_hdr_3addr);
+ u8 category, action;
+
+ /* check RA matches or not */
+ if (memcmp(myid(&(padapter->eeprompriv)), GetAddr1Ptr(pframe), ETH_ALEN))
+ goto exit;
+
+ category = frame_body[0];
+ if (category != RTW_WLAN_CATEGORY_PUBLIC)
+ goto exit;
+
+ action = frame_body[1];
+ switch (action) {
+ case ACT_PUBLIC_VENDOR:
+ ret = on_action_public_vendor(precv_frame);
+ break;
+ default:
+ ret = on_action_public_default(precv_frame, action);
+ break;
+ }
+
+exit:
+ return ret;
+}
+
+unsigned int OnAction_ht(struct adapter *padapter, struct recv_frame *precv_frame)
+{
+ return _SUCCESS;
+}
+
+unsigned int OnAction_wmm(struct adapter *padapter, struct recv_frame *precv_frame)
+{
+ return _SUCCESS;
+}
+
+unsigned int OnAction_p2p(struct adapter *padapter, struct recv_frame *precv_frame)
+{
+ return _SUCCESS;
+}
+
+unsigned int OnAction(struct adapter *padapter, struct recv_frame *precv_frame)
+{
+ int i;
+ unsigned char category;
+ struct action_handler *ptable;
+ unsigned char *frame_body;
+ u8 *pframe = precv_frame->rx_data;
+
+ frame_body = (unsigned char *)(pframe + sizeof(struct rtw_ieee80211_hdr_3addr));
+
+ category = frame_body[0];
+
+ for (i = 0; i < sizeof(OnAction_tbl)/sizeof(struct action_handler); i++) {
+ ptable = &OnAction_tbl[i];
+ if (category == ptable->num)
+ ptable->func(padapter, precv_frame);
+ }
+ return _SUCCESS;
+}
+
+unsigned int DoReserved(struct adapter *padapter, struct recv_frame *precv_frame)
+{
+ return _SUCCESS;
+}
+
+struct xmit_frame *alloc_mgtxmitframe(struct xmit_priv *pxmitpriv)
+{
+ struct xmit_frame *pmgntframe;
+ struct xmit_buf *pxmitbuf;
+
+ pmgntframe = rtw_alloc_xmitframe(pxmitpriv);
+ if (pmgntframe == NULL) {
+ DBG_88E("%s, alloc xmitframe fail\n", __func__);
+ return NULL;
+ }
+
+ pxmitbuf = rtw_alloc_xmitbuf_ext(pxmitpriv);
+ if (pxmitbuf == NULL) {
+ DBG_88E("%s, alloc xmitbuf fail\n", __func__);
+ rtw_free_xmitframe(pxmitpriv, pmgntframe);
+ return NULL;
+ }
+ pmgntframe->frame_tag = MGNT_FRAMETAG;
+ pmgntframe->pxmitbuf = pxmitbuf;
+ pmgntframe->buf_addr = pxmitbuf->pbuf;
+ pxmitbuf->priv_data = pmgntframe;
+ return pmgntframe;
+}
+
+/****************************************************************************
+
+Following are some TX functions for WiFi MLME
+
+*****************************************************************************/
+
+void update_mgnt_tx_rate(struct adapter *padapter, u8 rate)
+{
+ struct mlme_ext_priv *pmlmeext = &(padapter->mlmeextpriv);
+
+ pmlmeext->tx_rate = rate;
+ DBG_88E("%s(): rate = %x\n", __func__, rate);
+}
+
+void update_mgntframe_attrib(struct adapter *padapter, struct pkt_attrib *pattrib)
+{
+ struct mlme_ext_priv *pmlmeext = &(padapter->mlmeextpriv);
+
+ memset((u8 *)(pattrib), 0, sizeof(struct pkt_attrib));
+
+ pattrib->hdrlen = 24;
+ pattrib->nr_frags = 1;
+ pattrib->priority = 7;
+ pattrib->mac_id = 0;
+ pattrib->qsel = 0x12;
+
+ pattrib->pktlen = 0;
+
+ if (pmlmeext->cur_wireless_mode & WIRELESS_11B)
+ pattrib->raid = 6;/* b mode */
+ else
+ pattrib->raid = 5;/* a/g mode */
+
+ pattrib->encrypt = _NO_PRIVACY_;
+ pattrib->bswenc = false;
+
+ pattrib->qos_en = false;
+ pattrib->ht_en = false;
+ pattrib->bwmode = HT_CHANNEL_WIDTH_20;
+ pattrib->ch_offset = HAL_PRIME_CHNL_OFFSET_DONT_CARE;
+ pattrib->sgi = false;
+
+ pattrib->seqnum = pmlmeext->mgnt_seq;
+
+ pattrib->retry_ctrl = true;
+}
+
+void dump_mgntframe(struct adapter *padapter, struct xmit_frame *pmgntframe)
+{
+ if (padapter->bSurpriseRemoved || padapter->bDriverStopped)
+ return;
+
+ rtw_hal_mgnt_xmit(padapter, pmgntframe);
+}
+
+s32 dump_mgntframe_and_wait(struct adapter *padapter, struct xmit_frame *pmgntframe, int timeout_ms)
+{
+ s32 ret = _FAIL;
+ struct xmit_buf *pxmitbuf = pmgntframe->pxmitbuf;
+ struct submit_ctx sctx;
+
+ if (padapter->bSurpriseRemoved || padapter->bDriverStopped)
+ return ret;
+
+ rtw_sctx_init(&sctx, timeout_ms);
+ pxmitbuf->sctx = &sctx;
+
+ ret = rtw_hal_mgnt_xmit(padapter, pmgntframe);
+
+ if (ret == _SUCCESS)
+ ret = rtw_sctx_wait(&sctx);
+
+ return ret;
+}
+
+s32 dump_mgntframe_and_wait_ack(struct adapter *padapter, struct xmit_frame *pmgntframe)
+{
+ s32 ret = _FAIL;
+ u32 timeout_ms = 500;/* 500ms */
+ struct xmit_priv *pxmitpriv = &padapter->xmitpriv;
+
+ if (padapter->bSurpriseRemoved || padapter->bDriverStopped)
+ return -1;
+
+ _enter_critical_mutex(&pxmitpriv->ack_tx_mutex, NULL);
+ pxmitpriv->ack_tx = true;
+
+ pmgntframe->ack_report = 1;
+ if (rtw_hal_mgnt_xmit(padapter, pmgntframe) == _SUCCESS) {
+ ret = rtw_ack_tx_wait(pxmitpriv, timeout_ms);
+ }
+
+ pxmitpriv->ack_tx = false;
+ mutex_unlock(&pxmitpriv->ack_tx_mutex);
+
+ return ret;
+}
+
+static int update_hidden_ssid(u8 *ies, u32 ies_len, u8 hidden_ssid_mode)
+{
+ u8 *ssid_ie;
+ int ssid_len_ori;
+ int len_diff = 0;
+
+ ssid_ie = rtw_get_ie(ies, WLAN_EID_SSID, &ssid_len_ori, ies_len);
+
+ if (ssid_ie && ssid_len_ori > 0) {
+ switch (hidden_ssid_mode) {
+ case 1: {
+ u8 *next_ie = ssid_ie + 2 + ssid_len_ori;
+ u32 remain_len = 0;
+
+ remain_len = ies_len - (next_ie - ies);
+
+ ssid_ie[1] = 0;
+ memcpy(ssid_ie+2, next_ie, remain_len);
+ len_diff -= ssid_len_ori;
+
+ break;
+ }
+ case 2:
+ memset(&ssid_ie[2], 0, ssid_len_ori);
+ break;
+ default:
+ break;
+ }
+ }
+
+ return len_diff;
+}
+
+void issue_beacon(struct adapter *padapter, int timeout_ms)
+{
+ struct xmit_frame *pmgntframe;
+ struct pkt_attrib *pattrib;
+ unsigned char *pframe;
+ struct rtw_ieee80211_hdr *pwlanhdr;
+ __le16 *fctrl;
+ unsigned int rate_len;
+ struct xmit_priv *pxmitpriv = &(padapter->xmitpriv);
+ struct mlme_priv *pmlmepriv = &(padapter->mlmepriv);
+ struct mlme_ext_priv *pmlmeext = &(padapter->mlmeextpriv);
+ struct mlme_ext_info *pmlmeinfo = &(pmlmeext->mlmext_info);
+ struct wlan_bssid_ex *cur_network = &(pmlmeinfo->network);
+ u8 bc_addr[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
+
+ pmgntframe = alloc_mgtxmitframe(pxmitpriv);
+ if (pmgntframe == NULL) {
+ DBG_88E("%s, alloc mgnt frame fail\n", __func__);
+ return;
+ }
+#if defined(CONFIG_88EU_AP_MODE)
+ spin_lock_bh(&pmlmepriv->bcn_update_lock);
+#endif /* if defined (CONFIG_88EU_AP_MODE) */
+
+ /* update attribute */
+ pattrib = &pmgntframe->attrib;
+ update_mgntframe_attrib(padapter, pattrib);
+ pattrib->qsel = 0x10;
+
+ memset(pmgntframe->buf_addr, 0, WLANHDR_OFFSET + TXDESC_OFFSET);
+
+ pframe = (u8 *)(pmgntframe->buf_addr) + TXDESC_OFFSET;
+ pwlanhdr = (struct rtw_ieee80211_hdr *)pframe;
+
+
+ fctrl = &(pwlanhdr->frame_ctl);
+ *(fctrl) = 0;
+
+ memcpy(pwlanhdr->addr1, bc_addr, ETH_ALEN);
+ memcpy(pwlanhdr->addr2, myid(&(padapter->eeprompriv)), ETH_ALEN);
+ memcpy(pwlanhdr->addr3, cur_network->MacAddress, ETH_ALEN);
+
+ SetSeqNum(pwlanhdr, 0/*pmlmeext->mgnt_seq*/);
+ /* pmlmeext->mgnt_seq++; */
+ SetFrameSubType(pframe, WIFI_BEACON);
+
+ pframe += sizeof(struct rtw_ieee80211_hdr_3addr);
+ pattrib->pktlen = sizeof(struct rtw_ieee80211_hdr_3addr);
+
+ if ((pmlmeinfo->state&0x03) == WIFI_FW_AP_STATE) {
+ int len_diff;
+ u8 *wps_ie;
+ uint wps_ielen;
+ u8 sr = 0;
+ memcpy(pframe, cur_network->IEs, cur_network->IELength);
+ len_diff = update_hidden_ssid(
+ pframe+_BEACON_IE_OFFSET_
+ , cur_network->IELength-_BEACON_IE_OFFSET_
+ , pmlmeinfo->hidden_ssid_mode
+ );
+ pframe += (cur_network->IELength+len_diff);
+ pattrib->pktlen += (cur_network->IELength+len_diff);
+ wps_ie = rtw_get_wps_ie(pmgntframe->buf_addr+TXDESC_OFFSET+sizeof(struct rtw_ieee80211_hdr_3addr)+_BEACON_IE_OFFSET_,
+ pattrib->pktlen-sizeof(struct rtw_ieee80211_hdr_3addr)-_BEACON_IE_OFFSET_, NULL, &wps_ielen);
+ if (wps_ie && wps_ielen > 0)
+ rtw_get_wps_attr_content(wps_ie, wps_ielen, WPS_ATTR_SELECTED_REGISTRAR, (u8 *)(&sr), NULL);
+ if (sr != 0)
+ set_fwstate(pmlmepriv, WIFI_UNDER_WPS);
+ else
+ _clr_fwstate_(pmlmepriv, WIFI_UNDER_WPS);
+
+ goto _issue_bcn;
+ }
+
+ /* below for ad-hoc mode */
+
+ /* timestamp will be inserted by hardware */
+ pframe += 8;
+ pattrib->pktlen += 8;
+
+ /* beacon interval: 2 bytes */
+
+ memcpy(pframe, (unsigned char *)(rtw_get_beacon_interval_from_ie(cur_network->IEs)), 2);
+
+ pframe += 2;
+ pattrib->pktlen += 2;
+
+ /* capability info: 2 bytes */
+
+ memcpy(pframe, (unsigned char *)(rtw_get_capability_from_ie(cur_network->IEs)), 2);
+
+ pframe += 2;
+ pattrib->pktlen += 2;
+
+ /* SSID */
+ pframe = rtw_set_ie(pframe, _SSID_IE_, cur_network->Ssid.SsidLength, cur_network->Ssid.Ssid, &pattrib->pktlen);
+
+ /* supported rates... */
+ rate_len = rtw_get_rateset_len(cur_network->SupportedRates);
+ pframe = rtw_set_ie(pframe, _SUPPORTEDRATES_IE_, ((rate_len > 8) ? 8 : rate_len), cur_network->SupportedRates, &pattrib->pktlen);
+
+ /* DS parameter set */
+ pframe = rtw_set_ie(pframe, _DSSET_IE_, 1, (unsigned char *)&(cur_network->Configuration.DSConfig), &pattrib->pktlen);
+
+ {
+ u8 erpinfo = 0;
+ u32 ATIMWindow;
+ /* IBSS Parameter Set... */
+ ATIMWindow = 0;
+ pframe = rtw_set_ie(pframe, _IBSS_PARA_IE_, 2, (unsigned char *)(&ATIMWindow), &pattrib->pktlen);
+
+ /* ERP IE */
+ pframe = rtw_set_ie(pframe, _ERPINFO_IE_, 1, &erpinfo, &pattrib->pktlen);
+ }
+
+ /* EXTERNDED SUPPORTED RATE */
+ if (rate_len > 8)
+ pframe = rtw_set_ie(pframe, _EXT_SUPPORTEDRATES_IE_, (rate_len - 8), (cur_network->SupportedRates + 8), &pattrib->pktlen);
+ /* todo:HT for adhoc */
+_issue_bcn:
+
+#if defined(CONFIG_88EU_AP_MODE)
+ pmlmepriv->update_bcn = false;
+
+ spin_unlock_bh(&pmlmepriv->bcn_update_lock);
+#endif /* if defined (CONFIG_88EU_AP_MODE) */
+
+ if ((pattrib->pktlen + TXDESC_SIZE) > 512) {
+ DBG_88E("beacon frame too large\n");
+ return;
+ }
+
+ pattrib->last_txcmdsz = pattrib->pktlen;
+
+ /* DBG_88E("issue bcn_sz=%d\n", pattrib->last_txcmdsz); */
+ if (timeout_ms > 0)
+ dump_mgntframe_and_wait(padapter, pmgntframe, timeout_ms);
+ else
+ dump_mgntframe(padapter, pmgntframe);
+}
+
+void issue_probersp(struct adapter *padapter, unsigned char *da, u8 is_valid_p2p_probereq)
+{
+ struct xmit_frame *pmgntframe;
+ struct pkt_attrib *pattrib;
+ unsigned char *pframe;
+ struct rtw_ieee80211_hdr *pwlanhdr;
+ __le16 *fctrl;
+ unsigned char *mac, *bssid;
+ struct xmit_priv *pxmitpriv = &(padapter->xmitpriv);
+#if defined(CONFIG_88EU_AP_MODE)
+ u8 *pwps_ie;
+ uint wps_ielen;
+ struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
+#endif /* if defined (CONFIG_88EU_AP_MODE) */
+ struct mlme_ext_priv *pmlmeext = &(padapter->mlmeextpriv);
+ struct mlme_ext_info *pmlmeinfo = &(pmlmeext->mlmext_info);
+ struct wlan_bssid_ex *cur_network = &(pmlmeinfo->network);
+ unsigned int rate_len;
+
+ pmgntframe = alloc_mgtxmitframe(pxmitpriv);
+ if (pmgntframe == NULL) {
+ DBG_88E("%s, alloc mgnt frame fail\n", __func__);
+ return;
+ }
+
+ /* update attribute */
+ pattrib = &pmgntframe->attrib;
+ update_mgntframe_attrib(padapter, pattrib);
+
+ memset(pmgntframe->buf_addr, 0, WLANHDR_OFFSET + TXDESC_OFFSET);
+
+ pframe = (u8 *)(pmgntframe->buf_addr) + TXDESC_OFFSET;
+ pwlanhdr = (struct rtw_ieee80211_hdr *)pframe;
+
+ mac = myid(&(padapter->eeprompriv));
+ bssid = cur_network->MacAddress;
+
+ fctrl = &(pwlanhdr->frame_ctl);
+ *(fctrl) = 0;
+ memcpy(pwlanhdr->addr1, da, ETH_ALEN);
+ memcpy(pwlanhdr->addr2, mac, ETH_ALEN);
+ memcpy(pwlanhdr->addr3, bssid, ETH_ALEN);
+
+ SetSeqNum(pwlanhdr, pmlmeext->mgnt_seq);
+ pmlmeext->mgnt_seq++;
+ SetFrameSubType(fctrl, WIFI_PROBERSP);
+
+ pattrib->hdrlen = sizeof(struct rtw_ieee80211_hdr_3addr);
+ pattrib->pktlen = pattrib->hdrlen;
+ pframe += pattrib->hdrlen;
+
+ if (cur_network->IELength > MAX_IE_SZ)
+ return;
+
+#if defined(CONFIG_88EU_AP_MODE)
+ if ((pmlmeinfo->state&0x03) == WIFI_FW_AP_STATE) {
+ pwps_ie = rtw_get_wps_ie(cur_network->IEs+_FIXED_IE_LENGTH_, cur_network->IELength-_FIXED_IE_LENGTH_, NULL, &wps_ielen);
+
+ /* inerset & update wps_probe_resp_ie */
+ if ((pmlmepriv->wps_probe_resp_ie != NULL) && pwps_ie && (wps_ielen > 0)) {
+ uint wps_offset, remainder_ielen;
+ u8 *premainder_ie;
+
+ wps_offset = (uint)(pwps_ie - cur_network->IEs);
+
+ premainder_ie = pwps_ie + wps_ielen;
+
+ remainder_ielen = cur_network->IELength - wps_offset - wps_ielen;
+
+ memcpy(pframe, cur_network->IEs, wps_offset);
+ pframe += wps_offset;
+ pattrib->pktlen += wps_offset;
+
+ wps_ielen = (uint)pmlmepriv->wps_probe_resp_ie[1];/* to get ie data len */
+ if ((wps_offset+wps_ielen+2) <= MAX_IE_SZ) {
+ memcpy(pframe, pmlmepriv->wps_probe_resp_ie, wps_ielen+2);
+ pframe += wps_ielen+2;
+ pattrib->pktlen += wps_ielen+2;
+ }
+
+ if ((wps_offset+wps_ielen+2+remainder_ielen) <= MAX_IE_SZ) {
+ memcpy(pframe, premainder_ie, remainder_ielen);
+ pframe += remainder_ielen;
+ pattrib->pktlen += remainder_ielen;
+ }
+ } else {
+ memcpy(pframe, cur_network->IEs, cur_network->IELength);
+ pframe += cur_network->IELength;
+ pattrib->pktlen += cur_network->IELength;
+ }
+ } else
+#endif
+ {
+ /* timestamp will be inserted by hardware */
+ pframe += 8;
+ pattrib->pktlen += 8;
+
+ /* beacon interval: 2 bytes */
+
+ memcpy(pframe, (unsigned char *)(rtw_get_beacon_interval_from_ie(cur_network->IEs)), 2);
+
+ pframe += 2;
+ pattrib->pktlen += 2;
+
+ /* capability info: 2 bytes */
+
+ memcpy(pframe, (unsigned char *)(rtw_get_capability_from_ie(cur_network->IEs)), 2);
+
+ pframe += 2;
+ pattrib->pktlen += 2;
+
+ /* below for ad-hoc mode */
+
+ /* SSID */
+ pframe = rtw_set_ie(pframe, _SSID_IE_, cur_network->Ssid.SsidLength, cur_network->Ssid.Ssid, &pattrib->pktlen);
+
+ /* supported rates... */
+ rate_len = rtw_get_rateset_len(cur_network->SupportedRates);
+ pframe = rtw_set_ie(pframe, _SUPPORTEDRATES_IE_, ((rate_len > 8) ? 8 : rate_len), cur_network->SupportedRates, &pattrib->pktlen);
+
+ /* DS parameter set */
+ pframe = rtw_set_ie(pframe, _DSSET_IE_, 1, (unsigned char *)&(cur_network->Configuration.DSConfig), &pattrib->pktlen);
+
+ if ((pmlmeinfo->state&0x03) == WIFI_FW_ADHOC_STATE) {
+ u8 erpinfo = 0;
+ u32 ATIMWindow;
+ /* IBSS Parameter Set... */
+ /* ATIMWindow = cur->Configuration.ATIMWindow; */
+ ATIMWindow = 0;
+ pframe = rtw_set_ie(pframe, _IBSS_PARA_IE_, 2, (unsigned char *)(&ATIMWindow), &pattrib->pktlen);
+
+ /* ERP IE */
+ pframe = rtw_set_ie(pframe, _ERPINFO_IE_, 1, &erpinfo, &pattrib->pktlen);
+ }
+
+
+ /* EXTERNDED SUPPORTED RATE */
+ if (rate_len > 8)
+ pframe = rtw_set_ie(pframe, _EXT_SUPPORTEDRATES_IE_, (rate_len - 8), (cur_network->SupportedRates + 8), &pattrib->pktlen);
+ /* todo:HT for adhoc */
+ }
+
+ pattrib->last_txcmdsz = pattrib->pktlen;
+
+ dump_mgntframe(padapter, pmgntframe);
+
+ return;
+}
+
+static int _issue_probereq(struct adapter *padapter, struct ndis_802_11_ssid *pssid, u8 *da, int wait_ack)
+{
+ int ret = _FAIL;
+ struct xmit_frame *pmgntframe;
+ struct pkt_attrib *pattrib;
+ unsigned char *pframe;
+ struct rtw_ieee80211_hdr *pwlanhdr;
+ __le16 *fctrl;
+ unsigned char *mac;
+ unsigned char bssrate[NumRates];
+ struct xmit_priv *pxmitpriv = &(padapter->xmitpriv);
+ struct mlme_priv *pmlmepriv = &(padapter->mlmepriv);
+ struct mlme_ext_priv *pmlmeext = &(padapter->mlmeextpriv);
+ int bssrate_len = 0;
+ u8 bc_addr[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
+
+ RT_TRACE(_module_rtl871x_mlme_c_, _drv_notice_, ("+issue_probereq\n"));
+
+ pmgntframe = alloc_mgtxmitframe(pxmitpriv);
+ if (pmgntframe == NULL)
+ goto exit;
+
+ /* update attribute */
+ pattrib = &pmgntframe->attrib;
+ update_mgntframe_attrib(padapter, pattrib);
+
+
+ memset(pmgntframe->buf_addr, 0, WLANHDR_OFFSET + TXDESC_OFFSET);
+
+ pframe = (u8 *)(pmgntframe->buf_addr) + TXDESC_OFFSET;
+ pwlanhdr = (struct rtw_ieee80211_hdr *)pframe;
+
+ mac = myid(&(padapter->eeprompriv));
+
+ fctrl = &(pwlanhdr->frame_ctl);
+ *(fctrl) = 0;
+
+ if (da) {
+ /* unicast probe request frame */
+ memcpy(pwlanhdr->addr1, da, ETH_ALEN);
+ memcpy(pwlanhdr->addr3, da, ETH_ALEN);
+ } else {
+ /* broadcast probe request frame */
+ memcpy(pwlanhdr->addr1, bc_addr, ETH_ALEN);
+ memcpy(pwlanhdr->addr3, bc_addr, ETH_ALEN);
+ }
+
+ memcpy(pwlanhdr->addr2, mac, ETH_ALEN);
+
+ SetSeqNum(pwlanhdr, pmlmeext->mgnt_seq);
+ pmlmeext->mgnt_seq++;
+ SetFrameSubType(pframe, WIFI_PROBEREQ);
+
+ pframe += sizeof(struct rtw_ieee80211_hdr_3addr);
+ pattrib->pktlen = sizeof(struct rtw_ieee80211_hdr_3addr);
+
+ if (pssid)
+ pframe = rtw_set_ie(pframe, _SSID_IE_, pssid->SsidLength, pssid->Ssid, &(pattrib->pktlen));
+ else
+ pframe = rtw_set_ie(pframe, _SSID_IE_, 0, NULL, &(pattrib->pktlen));
+
+ get_rate_set(padapter, bssrate, &bssrate_len);
+
+ if (bssrate_len > 8) {
+ pframe = rtw_set_ie(pframe, _SUPPORTEDRATES_IE_ , 8, bssrate, &(pattrib->pktlen));
+ pframe = rtw_set_ie(pframe, _EXT_SUPPORTEDRATES_IE_ , (bssrate_len - 8), (bssrate + 8), &(pattrib->pktlen));
+ } else {
+ pframe = rtw_set_ie(pframe, _SUPPORTEDRATES_IE_ , bssrate_len , bssrate, &(pattrib->pktlen));
+ }
+
+ /* add wps_ie for wps2.0 */
+ if (pmlmepriv->wps_probe_req_ie_len > 0 && pmlmepriv->wps_probe_req_ie) {
+ memcpy(pframe, pmlmepriv->wps_probe_req_ie, pmlmepriv->wps_probe_req_ie_len);
+ pframe += pmlmepriv->wps_probe_req_ie_len;
+ pattrib->pktlen += pmlmepriv->wps_probe_req_ie_len;
+ }
+
+ pattrib->last_txcmdsz = pattrib->pktlen;
+
+ RT_TRACE(_module_rtl871x_mlme_c_, _drv_notice_,
+ ("issuing probe_req, tx_len=%d\n", pattrib->last_txcmdsz));
+
+ if (wait_ack) {
+ ret = dump_mgntframe_and_wait_ack(padapter, pmgntframe);
+ } else {
+ dump_mgntframe(padapter, pmgntframe);
+ ret = _SUCCESS;
+ }
+
+exit:
+ return ret;
+}
+
+inline void issue_probereq(struct adapter *padapter, struct ndis_802_11_ssid *pssid, u8 *da)
+{
+ _issue_probereq(padapter, pssid, da, false);
+}
+
+int issue_probereq_ex(struct adapter *padapter, struct ndis_802_11_ssid *pssid, u8 *da,
+ int try_cnt, int wait_ms)
+{
+ int ret;
+ int i = 0;
+ u32 start = jiffies;
+
+ do {
+ ret = _issue_probereq(padapter, pssid, da, wait_ms > 0 ? true : false);
+
+ i++;
+
+ if (padapter->bDriverStopped || padapter->bSurpriseRemoved)
+ break;
+
+ if (i < try_cnt && wait_ms > 0 && ret == _FAIL)
+ msleep(wait_ms);
+
+ } while ((i < try_cnt) && ((ret == _FAIL) || (wait_ms == 0)));
+
+ if (ret != _FAIL) {
+ ret = _SUCCESS;
+ goto exit;
+ }
+
+ if (try_cnt && wait_ms) {
+ if (da)
+ DBG_88E(FUNC_ADPT_FMT" to %pM, ch:%u%s, %d/%d in %u ms\n",
+ FUNC_ADPT_ARG(padapter), da, rtw_get_oper_ch(padapter),
+ ret == _SUCCESS ? ", acked" : "", i, try_cnt, rtw_get_passing_time_ms(start));
+ else
+ DBG_88E(FUNC_ADPT_FMT", ch:%u%s, %d/%d in %u ms\n",
+ FUNC_ADPT_ARG(padapter), rtw_get_oper_ch(padapter),
+ ret == _SUCCESS ? ", acked" : "", i, try_cnt, rtw_get_passing_time_ms(start));
+ }
+exit:
+ return ret;
+}
+
+/* if psta == NULL, indicate we are station(client) now... */
+void issue_auth(struct adapter *padapter, struct sta_info *psta, unsigned short status)
+{
+ struct xmit_frame *pmgntframe;
+ struct pkt_attrib *pattrib;
+ unsigned char *pframe;
+ struct rtw_ieee80211_hdr *pwlanhdr;
+ __le16 *fctrl;
+ unsigned int val32;
+ u16 val16;
+#ifdef CONFIG_88EU_AP_MODE
+ __le16 le_val16;
+#endif
+ int use_shared_key = 0;
+ struct xmit_priv *pxmitpriv = &(padapter->xmitpriv);
+ struct mlme_ext_priv *pmlmeext = &(padapter->mlmeextpriv);
+ struct mlme_ext_info *pmlmeinfo = &(pmlmeext->mlmext_info);
+ struct wlan_bssid_ex *pnetwork = &(pmlmeinfo->network);
+
+ pmgntframe = alloc_mgtxmitframe(pxmitpriv);
+ if (pmgntframe == NULL)
+ return;
+
+ /* update attribute */
+ pattrib = &pmgntframe->attrib;
+ update_mgntframe_attrib(padapter, pattrib);
+
+ memset(pmgntframe->buf_addr, 0, WLANHDR_OFFSET + TXDESC_OFFSET);
+
+ pframe = (u8 *)(pmgntframe->buf_addr) + TXDESC_OFFSET;
+ pwlanhdr = (struct rtw_ieee80211_hdr *)pframe;
+
+ fctrl = &(pwlanhdr->frame_ctl);
+ *(fctrl) = 0;
+
+ SetSeqNum(pwlanhdr, pmlmeext->mgnt_seq);
+ pmlmeext->mgnt_seq++;
+ SetFrameSubType(pframe, WIFI_AUTH);
+
+ pframe += sizeof(struct rtw_ieee80211_hdr_3addr);
+ pattrib->pktlen = sizeof(struct rtw_ieee80211_hdr_3addr);
+
+
+ if (psta) {/* for AP mode */
+#ifdef CONFIG_88EU_AP_MODE
+
+ memcpy(pwlanhdr->addr1, psta->hwaddr, ETH_ALEN);
+ memcpy(pwlanhdr->addr2, myid(&(padapter->eeprompriv)), ETH_ALEN);
+ memcpy(pwlanhdr->addr3, myid(&(padapter->eeprompriv)), ETH_ALEN);
+
+
+ /* setting auth algo number */
+ val16 = (u16)psta->authalg;
+
+ if (status != _STATS_SUCCESSFUL_)
+ val16 = 0;
+
+ if (val16) {
+ le_val16 = cpu_to_le16(val16);
+ use_shared_key = 1;
+ } else {
+ le_val16 = 0;
+ }
+
+ pframe = rtw_set_fixed_ie(pframe, _AUTH_ALGM_NUM_, (unsigned char *)&le_val16, &(pattrib->pktlen));
+
+ /* setting auth seq number */
+ val16 = (u16)psta->auth_seq;
+ le_val16 = cpu_to_le16(val16);
+ pframe = rtw_set_fixed_ie(pframe, _AUTH_SEQ_NUM_, (unsigned char *)&le_val16, &(pattrib->pktlen));
+
+ /* setting status code... */
+ val16 = status;
+ le_val16 = cpu_to_le16(val16);
+ pframe = rtw_set_fixed_ie(pframe, _STATUS_CODE_, (unsigned char *)&le_val16, &(pattrib->pktlen));
+
+ /* added challenging text... */
+ if ((psta->auth_seq == 2) && (psta->state & WIFI_FW_AUTH_STATE) && (use_shared_key == 1))
+ pframe = rtw_set_ie(pframe, _CHLGETXT_IE_, 128, psta->chg_txt, &(pattrib->pktlen));
+#endif
+ } else {
+ __le32 le_tmp32;
+ __le16 le_tmp16;
+ memcpy(pwlanhdr->addr1, pnetwork->MacAddress, ETH_ALEN);
+ memcpy(pwlanhdr->addr2, myid(&padapter->eeprompriv), ETH_ALEN);
+ memcpy(pwlanhdr->addr3, pnetwork->MacAddress, ETH_ALEN);
+
+ /* setting auth algo number */
+ val16 = (pmlmeinfo->auth_algo == dot11AuthAlgrthm_Shared) ? 1 : 0;/* 0:OPEN System, 1:Shared key */
+ if (val16)
+ use_shared_key = 1;
+
+ /* setting IV for auth seq #3 */
+ if ((pmlmeinfo->auth_seq == 3) && (pmlmeinfo->state & WIFI_FW_AUTH_STATE) && (use_shared_key == 1)) {
+ val32 = (pmlmeinfo->iv++) | (pmlmeinfo->key_index << 30);
+ le_tmp32 = cpu_to_le32(val32);
+ pframe = rtw_set_fixed_ie(pframe, 4, (unsigned char *)&le_tmp32, &(pattrib->pktlen));
+
+ pattrib->iv_len = 4;
+ }
+
+ le_tmp16 = cpu_to_le16(val16);
+ pframe = rtw_set_fixed_ie(pframe, _AUTH_ALGM_NUM_, (unsigned char *)&le_tmp16, &(pattrib->pktlen));
+
+ /* setting auth seq number */
+ val16 = pmlmeinfo->auth_seq;
+ le_tmp16 = cpu_to_le16(val16);
+ pframe = rtw_set_fixed_ie(pframe, _AUTH_SEQ_NUM_, (unsigned char *)&le_tmp16, &(pattrib->pktlen));
+
+
+ /* setting status code... */
+ le_tmp16 = cpu_to_le16(status);
+ pframe = rtw_set_fixed_ie(pframe, _STATUS_CODE_, (unsigned char *)&le_tmp16, &(pattrib->pktlen));
+
+ /* then checking to see if sending challenging text... */
+ if ((pmlmeinfo->auth_seq == 3) && (pmlmeinfo->state & WIFI_FW_AUTH_STATE) && (use_shared_key == 1)) {
+ pframe = rtw_set_ie(pframe, _CHLGETXT_IE_, 128, pmlmeinfo->chg_txt, &(pattrib->pktlen));
+
+ SetPrivacy(fctrl);
+
+ pattrib->hdrlen = sizeof(struct rtw_ieee80211_hdr_3addr);
+
+ pattrib->encrypt = _WEP40_;
+
+ pattrib->icv_len = 4;
+
+ pattrib->pktlen += pattrib->icv_len;
+ }
+ }
+
+ pattrib->last_txcmdsz = pattrib->pktlen;
+
+ rtw_wep_encrypt(padapter, (u8 *)pmgntframe);
+ DBG_88E("%s\n", __func__);
+ dump_mgntframe(padapter, pmgntframe);
+
+ return;
+}
+
+
+void issue_asocrsp(struct adapter *padapter, unsigned short status, struct sta_info *pstat, int pkt_type)
+{
+#ifdef CONFIG_88EU_AP_MODE
+ struct xmit_frame *pmgntframe;
+ struct rtw_ieee80211_hdr *pwlanhdr;
+ struct pkt_attrib *pattrib;
+ unsigned char *pbuf, *pframe;
+ unsigned short val;
+ __le16 *fctrl;
+ struct xmit_priv *pxmitpriv = &(padapter->xmitpriv);
+ struct mlme_priv *pmlmepriv = &(padapter->mlmepriv);
+ struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv;
+ struct mlme_ext_info *pmlmeinfo = &(pmlmeext->mlmext_info);
+ struct wlan_bssid_ex *pnetwork = &(pmlmeinfo->network);
+ u8 *ie = pnetwork->IEs;
+ __le16 lestatus, leval;
+
+ DBG_88E("%s\n", __func__);
+
+ pmgntframe = alloc_mgtxmitframe(pxmitpriv);
+ if (pmgntframe == NULL)
+ return;
+
+ /* update attribute */
+ pattrib = &pmgntframe->attrib;
+ update_mgntframe_attrib(padapter, pattrib);
+
+
+ memset(pmgntframe->buf_addr, 0, WLANHDR_OFFSET + TXDESC_OFFSET);
+
+ pframe = (u8 *)(pmgntframe->buf_addr) + TXDESC_OFFSET;
+ pwlanhdr = (struct rtw_ieee80211_hdr *)pframe;
+
+ fctrl = &(pwlanhdr->frame_ctl);
+ *(fctrl) = 0;
+
+ memcpy((void *)GetAddr1Ptr(pwlanhdr), pstat->hwaddr, ETH_ALEN);
+ memcpy((void *)GetAddr2Ptr(pwlanhdr), myid(&(padapter->eeprompriv)), ETH_ALEN);
+ memcpy((void *)GetAddr3Ptr(pwlanhdr), pnetwork->MacAddress, ETH_ALEN);
+
+
+ SetSeqNum(pwlanhdr, pmlmeext->mgnt_seq);
+ pmlmeext->mgnt_seq++;
+ if ((pkt_type == WIFI_ASSOCRSP) || (pkt_type == WIFI_REASSOCRSP))
+ SetFrameSubType(pwlanhdr, pkt_type);
+ else
+ return;
+
+ pattrib->hdrlen = sizeof(struct rtw_ieee80211_hdr_3addr);
+ pattrib->pktlen += pattrib->hdrlen;
+ pframe += pattrib->hdrlen;
+
+ /* capability */
+ val = *(unsigned short *)rtw_get_capability_from_ie(ie);
+
+ pframe = rtw_set_fixed_ie(pframe, _CAPABILITY_ , (unsigned char *)&val, &(pattrib->pktlen));
+
+ lestatus = cpu_to_le16(status);
+ pframe = rtw_set_fixed_ie(pframe , _STATUS_CODE_ , (unsigned char *)&lestatus, &(pattrib->pktlen));
+
+ leval = cpu_to_le16(pstat->aid | BIT(14) | BIT(15));
+ pframe = rtw_set_fixed_ie(pframe, _ASOC_ID_ , (unsigned char *)&leval, &(pattrib->pktlen));
+
+ if (pstat->bssratelen <= 8) {
+ pframe = rtw_set_ie(pframe, _SUPPORTEDRATES_IE_, pstat->bssratelen, pstat->bssrateset, &(pattrib->pktlen));
+ } else {
+ pframe = rtw_set_ie(pframe, _SUPPORTEDRATES_IE_, 8, pstat->bssrateset, &(pattrib->pktlen));
+ pframe = rtw_set_ie(pframe, _EXT_SUPPORTEDRATES_IE_, (pstat->bssratelen-8), pstat->bssrateset+8, &(pattrib->pktlen));
+ }
+
+ if ((pstat->flags & WLAN_STA_HT) && (pmlmepriv->htpriv.ht_option)) {
+ uint ie_len = 0;
+
+ /* FILL HT CAP INFO IE */
+ pbuf = rtw_get_ie(ie + _BEACON_IE_OFFSET_, _HT_CAPABILITY_IE_, &ie_len, (pnetwork->IELength - _BEACON_IE_OFFSET_));
+ if (pbuf && ie_len > 0) {
+ memcpy(pframe, pbuf, ie_len+2);
+ pframe += (ie_len+2);
+ pattrib->pktlen += (ie_len+2);
+ }
+
+ /* FILL HT ADD INFO IE */
+ pbuf = rtw_get_ie(ie + _BEACON_IE_OFFSET_, _HT_ADD_INFO_IE_, &ie_len, (pnetwork->IELength - _BEACON_IE_OFFSET_));
+ if (pbuf && ie_len > 0) {
+ memcpy(pframe, pbuf, ie_len+2);
+ pframe += (ie_len+2);
+ pattrib->pktlen += (ie_len+2);
+ }
+ }
+
+ /* FILL WMM IE */
+ if ((pstat->flags & WLAN_STA_WME) && (pmlmepriv->qospriv.qos_option)) {
+ uint ie_len = 0;
+ unsigned char WMM_PARA_IE[] = {0x00, 0x50, 0xf2, 0x02, 0x01, 0x01};
+
+ for (pbuf = ie + _BEACON_IE_OFFSET_;; pbuf += (ie_len + 2)) {
+ pbuf = rtw_get_ie(pbuf, _VENDOR_SPECIFIC_IE_, &ie_len, (pnetwork->IELength - _BEACON_IE_OFFSET_ - (ie_len + 2)));
+ if (pbuf && !memcmp(pbuf+2, WMM_PARA_IE, 6)) {
+ memcpy(pframe, pbuf, ie_len+2);
+ pframe += (ie_len+2);
+ pattrib->pktlen += (ie_len+2);
+ break;
+ }
+
+ if ((pbuf == NULL) || (ie_len == 0))
+ break;
+ }
+ }
+
+ if (pmlmeinfo->assoc_AP_vendor == HT_IOT_PEER_REALTEK)
+ pframe = rtw_set_ie(pframe, _VENDOR_SPECIFIC_IE_, 6 , REALTEK_96B_IE, &(pattrib->pktlen));
+
+ /* add WPS IE ie for wps 2.0 */
+ if (pmlmepriv->wps_assoc_resp_ie && pmlmepriv->wps_assoc_resp_ie_len > 0) {
+ memcpy(pframe, pmlmepriv->wps_assoc_resp_ie, pmlmepriv->wps_assoc_resp_ie_len);
+
+ pframe += pmlmepriv->wps_assoc_resp_ie_len;
+ pattrib->pktlen += pmlmepriv->wps_assoc_resp_ie_len;
+ }
+
+ pattrib->last_txcmdsz = pattrib->pktlen;
+ dump_mgntframe(padapter, pmgntframe);
+#endif
+}
+
+void issue_assocreq(struct adapter *padapter)
+{
+ int ret = _FAIL;
+ struct xmit_frame *pmgntframe;
+ struct pkt_attrib *pattrib;
+ unsigned char *pframe, *p;
+ struct rtw_ieee80211_hdr *pwlanhdr;
+ __le16 *fctrl;
+ __le16 le_tmp;
+ unsigned int i, j, ie_len, index = 0;
+ unsigned char rf_type, bssrate[NumRates], sta_bssrate[NumRates];
+ struct ndis_802_11_var_ie *pIE;
+ struct registry_priv *pregpriv = &padapter->registrypriv;
+ struct xmit_priv *pxmitpriv = &(padapter->xmitpriv);
+ struct mlme_priv *pmlmepriv = &(padapter->mlmepriv);
+ struct mlme_ext_priv *pmlmeext = &(padapter->mlmeextpriv);
+ struct mlme_ext_info *pmlmeinfo = &(pmlmeext->mlmext_info);
+ int bssrate_len = 0, sta_bssrate_len = 0;
+ struct wlan_bssid_ex *pnetwork = &(pmlmeinfo->network);
+
+ pmgntframe = alloc_mgtxmitframe(pxmitpriv);
+ if (pmgntframe == NULL)
+ goto exit;
+
+ /* update attribute */
+ pattrib = &pmgntframe->attrib;
+ update_mgntframe_attrib(padapter, pattrib);
+
+ memset(pmgntframe->buf_addr, 0, WLANHDR_OFFSET + TXDESC_OFFSET);
+ pframe = (u8 *)(pmgntframe->buf_addr) + TXDESC_OFFSET;
+ pwlanhdr = (struct rtw_ieee80211_hdr *)pframe;
+
+ fctrl = &(pwlanhdr->frame_ctl);
+ *(fctrl) = 0;
+ memcpy(pwlanhdr->addr1, pnetwork->MacAddress, ETH_ALEN);
+ memcpy(pwlanhdr->addr2, myid(&(padapter->eeprompriv)), ETH_ALEN);
+ memcpy(pwlanhdr->addr3, pnetwork->MacAddress, ETH_ALEN);
+
+ SetSeqNum(pwlanhdr, pmlmeext->mgnt_seq);
+ pmlmeext->mgnt_seq++;
+ SetFrameSubType(pframe, WIFI_ASSOCREQ);
+
+ pframe += sizeof(struct rtw_ieee80211_hdr_3addr);
+ pattrib->pktlen = sizeof(struct rtw_ieee80211_hdr_3addr);
+
+ /* caps */
+
+ memcpy(pframe, rtw_get_capability_from_ie(pmlmeinfo->network.IEs), 2);
+
+ pframe += 2;
+ pattrib->pktlen += 2;
+
+ /* listen interval */
+ /* todo: listen interval for power saving */
+ le_tmp = cpu_to_le16(3);
+ memcpy(pframe , (unsigned char *)&le_tmp, 2);
+ pframe += 2;
+ pattrib->pktlen += 2;
+
+ /* SSID */
+ pframe = rtw_set_ie(pframe, _SSID_IE_, pmlmeinfo->network.Ssid.SsidLength, pmlmeinfo->network.Ssid.Ssid, &(pattrib->pktlen));
+
+ /* supported rate & extended supported rate */
+
+ /* Check if the AP's supported rates are also supported by STA. */
+ get_rate_set(padapter, sta_bssrate, &sta_bssrate_len);
+
+ if (pmlmeext->cur_channel == 14)/* for JAPAN, channel 14 can only uses B Mode(CCK) */
+ sta_bssrate_len = 4;
+
+ for (i = 0; i < NDIS_802_11_LENGTH_RATES_EX; i++) {
+ if (pmlmeinfo->network.SupportedRates[i] == 0)
+ break;
+ DBG_88E("network.SupportedRates[%d]=%02X\n", i, pmlmeinfo->network.SupportedRates[i]);
+ }
+
+ for (i = 0; i < NDIS_802_11_LENGTH_RATES_EX; i++) {
+ if (pmlmeinfo->network.SupportedRates[i] == 0)
+ break;
+
+ /* Check if the AP's supported rates are also supported by STA. */
+ for (j = 0; j < sta_bssrate_len; j++) {
+ /* Avoid the proprietary data rate (22Mbps) of Handlink WSG-4000 AP */
+ if ((pmlmeinfo->network.SupportedRates[i]|IEEE80211_BASIC_RATE_MASK)
+ == (sta_bssrate[j]|IEEE80211_BASIC_RATE_MASK))
+ break;
+ }
+
+ if (j == sta_bssrate_len) {
+ /* the rate is not supported by STA */
+ DBG_88E("%s(): the rate[%d]=%02X is not supported by STA!\n", __func__, i, pmlmeinfo->network.SupportedRates[i]);
+ } else {
+ /* the rate is supported by STA */
+ bssrate[index++] = pmlmeinfo->network.SupportedRates[i];
+ }
+ }
+
+ bssrate_len = index;
+ DBG_88E("bssrate_len=%d\n", bssrate_len);
+
+ if (bssrate_len == 0) {
+ rtw_free_xmitbuf(pxmitpriv, pmgntframe->pxmitbuf);
+ rtw_free_xmitframe(pxmitpriv, pmgntframe);
+ goto exit; /* don't connect to AP if no joint supported rate */
+ }
+
+
+ if (bssrate_len > 8) {
+ pframe = rtw_set_ie(pframe, _SUPPORTEDRATES_IE_ , 8, bssrate, &(pattrib->pktlen));
+ pframe = rtw_set_ie(pframe, _EXT_SUPPORTEDRATES_IE_ , (bssrate_len - 8), (bssrate + 8), &(pattrib->pktlen));
+ } else {
+ pframe = rtw_set_ie(pframe, _SUPPORTEDRATES_IE_ , bssrate_len , bssrate, &(pattrib->pktlen));
+ }
+
+ /* RSN */
+ p = rtw_get_ie((pmlmeinfo->network.IEs + sizeof(struct ndis_802_11_fixed_ie)), _RSN_IE_2_, &ie_len, (pmlmeinfo->network.IELength - sizeof(struct ndis_802_11_fixed_ie)));
+ if (p != NULL)
+ pframe = rtw_set_ie(pframe, _RSN_IE_2_, ie_len, (p + 2), &(pattrib->pktlen));
+
+ /* HT caps */
+ if (padapter->mlmepriv.htpriv.ht_option) {
+ p = rtw_get_ie((pmlmeinfo->network.IEs + sizeof(struct ndis_802_11_fixed_ie)), _HT_CAPABILITY_IE_, &ie_len, (pmlmeinfo->network.IELength - sizeof(struct ndis_802_11_fixed_ie)));
+ if ((p != NULL) && (!(is_ap_in_tkip(padapter)))) {
+ memcpy(&(pmlmeinfo->HT_caps), (p + 2), sizeof(struct HT_caps_element));
+
+ /* to disable 40M Hz support while gd_bw_40MHz_en = 0 */
+ if (pregpriv->cbw40_enable == 0)
+ pmlmeinfo->HT_caps.u.HT_cap_element.HT_caps_info &= cpu_to_le16(~(BIT(6) | BIT(1)));
+ else
+ pmlmeinfo->HT_caps.u.HT_cap_element.HT_caps_info |= cpu_to_le16(BIT(1));
+
+ /* todo: disable SM power save mode */
+ pmlmeinfo->HT_caps.u.HT_cap_element.HT_caps_info |= cpu_to_le16(0x000c);
+
+ rtw_hal_get_hwreg(padapter, HW_VAR_RF_TYPE, (u8 *)(&rf_type));
+ switch (rf_type) {
+ case RF_1T1R:
+ if (pregpriv->rx_stbc)
+ pmlmeinfo->HT_caps.u.HT_cap_element.HT_caps_info |= cpu_to_le16(0x0100);/* RX STBC One spatial stream */
+ memcpy(pmlmeinfo->HT_caps.u.HT_cap_element.MCS_rate, MCS_rate_1R, 16);
+ break;
+ case RF_2T2R:
+ case RF_1T2R:
+ default:
+ if ((pregpriv->rx_stbc == 0x3) ||/* enable for 2.4/5 GHz */
+ ((pmlmeext->cur_wireless_mode & WIRELESS_11_24N) && (pregpriv->rx_stbc == 0x1)) || /* enable for 2.4GHz */
+ (pregpriv->wifi_spec == 1)) {
+ DBG_88E("declare supporting RX STBC\n");
+ pmlmeinfo->HT_caps.u.HT_cap_element.HT_caps_info |= cpu_to_le16(0x0200);/* RX STBC two spatial stream */
+ }
+ memcpy(pmlmeinfo->HT_caps.u.HT_cap_element.MCS_rate, MCS_rate_2R, 16);
+ break;
+ }
+ pframe = rtw_set_ie(pframe, _HT_CAPABILITY_IE_, ie_len , (u8 *)(&(pmlmeinfo->HT_caps)), &(pattrib->pktlen));
+ }
+ }
+
+ /* vendor specific IE, such as WPA, WMM, WPS */
+ for (i = sizeof(struct ndis_802_11_fixed_ie); i < pmlmeinfo->network.IELength;) {
+ pIE = (struct ndis_802_11_var_ie *)(pmlmeinfo->network.IEs + i);
+
+ switch (pIE->ElementID) {
+ case _VENDOR_SPECIFIC_IE_:
+ if ((!memcmp(pIE->data, RTW_WPA_OUI, 4)) ||
+ (!memcmp(pIE->data, WMM_OUI, 4)) ||
+ (!memcmp(pIE->data, WPS_OUI, 4))) {
+ if (!padapter->registrypriv.wifi_spec) {
+ /* Commented by Kurt 20110629 */
+ /* In some older APs, WPS handshake */
+ /* would be fail if we append vender extensions informations to AP */
+ if (!memcmp(pIE->data, WPS_OUI, 4))
+ pIE->Length = 14;
+ }
+ pframe = rtw_set_ie(pframe, _VENDOR_SPECIFIC_IE_, pIE->Length, pIE->data, &(pattrib->pktlen));
+ }
+ break;
+ default:
+ break;
+ }
+ i += (pIE->Length + 2);
+ }
+
+ if (pmlmeinfo->assoc_AP_vendor == HT_IOT_PEER_REALTEK)
+ pframe = rtw_set_ie(pframe, _VENDOR_SPECIFIC_IE_, 6 , REALTEK_96B_IE, &(pattrib->pktlen));
+
+ pattrib->last_txcmdsz = pattrib->pktlen;
+ dump_mgntframe(padapter, pmgntframe);
+
+ ret = _SUCCESS;
+
+exit:
+ if (ret == _SUCCESS)
+ rtw_buf_update(&pmlmepriv->assoc_req, &pmlmepriv->assoc_req_len, (u8 *)pwlanhdr, pattrib->pktlen);
+ else
+ rtw_buf_free(&pmlmepriv->assoc_req, &pmlmepriv->assoc_req_len);
+
+ return;
+}
+
+/* when wait_ack is true, this function should be called at process context */
+static int _issue_nulldata(struct adapter *padapter, unsigned char *da, unsigned int power_mode, int wait_ack)
+{
+ int ret = _FAIL;
+ struct xmit_frame *pmgntframe;
+ struct pkt_attrib *pattrib;
+ unsigned char *pframe;
+ struct rtw_ieee80211_hdr *pwlanhdr;
+ __le16 *fctrl;
+ struct xmit_priv *pxmitpriv;
+ struct mlme_ext_priv *pmlmeext;
+ struct mlme_ext_info *pmlmeinfo;
+ struct wlan_bssid_ex *pnetwork;
+
+ if (!padapter)
+ goto exit;
+
+ pxmitpriv = &(padapter->xmitpriv);
+ pmlmeext = &(padapter->mlmeextpriv);
+ pmlmeinfo = &(pmlmeext->mlmext_info);
+ pnetwork = &(pmlmeinfo->network);
+
+ pmgntframe = alloc_mgtxmitframe(pxmitpriv);
+ if (pmgntframe == NULL)
+ goto exit;
+
+ /* update attribute */
+ pattrib = &pmgntframe->attrib;
+ update_mgntframe_attrib(padapter, pattrib);
+ pattrib->retry_ctrl = false;
+
+ memset(pmgntframe->buf_addr, 0, WLANHDR_OFFSET + TXDESC_OFFSET);
+
+ pframe = (u8 *)(pmgntframe->buf_addr) + TXDESC_OFFSET;
+ pwlanhdr = (struct rtw_ieee80211_hdr *)pframe;
+
+ fctrl = &(pwlanhdr->frame_ctl);
+ *(fctrl) = 0;
+
+ if ((pmlmeinfo->state&0x03) == WIFI_FW_AP_STATE)
+ SetFrDs(fctrl);
+ else if ((pmlmeinfo->state&0x03) == WIFI_FW_STATION_STATE)
+ SetToDs(fctrl);
+
+ if (power_mode)
+ SetPwrMgt(fctrl);
+
+ memcpy(pwlanhdr->addr1, da, ETH_ALEN);
+ memcpy(pwlanhdr->addr2, myid(&(padapter->eeprompriv)), ETH_ALEN);
+ memcpy(pwlanhdr->addr3, pnetwork->MacAddress, ETH_ALEN);
+
+ SetSeqNum(pwlanhdr, pmlmeext->mgnt_seq);
+ pmlmeext->mgnt_seq++;
+ SetFrameSubType(pframe, WIFI_DATA_NULL);
+
+ pframe += sizeof(struct rtw_ieee80211_hdr_3addr);
+ pattrib->pktlen = sizeof(struct rtw_ieee80211_hdr_3addr);
+
+ pattrib->last_txcmdsz = pattrib->pktlen;
+
+ if (wait_ack) {
+ ret = dump_mgntframe_and_wait_ack(padapter, pmgntframe);
+ } else {
+ dump_mgntframe(padapter, pmgntframe);
+ ret = _SUCCESS;
+ }
+
+exit:
+ return ret;
+}
+
+
+/* when wait_ms > 0 , this function should be called at process context */
+/* da == NULL for station mode */
+int issue_nulldata(struct adapter *padapter, unsigned char *da, unsigned int power_mode, int try_cnt, int wait_ms)
+{
+ int ret;
+ int i = 0;
+ u32 start = jiffies;
+ struct mlme_ext_priv *pmlmeext = &(padapter->mlmeextpriv);
+ struct mlme_ext_info *pmlmeinfo = &(pmlmeext->mlmext_info);
+ struct wlan_bssid_ex *pnetwork = &(pmlmeinfo->network);
+
+ /* da == NULL, assume it's null data for sta to ap*/
+ if (da == NULL)
+ da = pnetwork->MacAddress;
+
+ do {
+ ret = _issue_nulldata(padapter, da, power_mode, wait_ms > 0 ? true : false);
+
+ i++;
+
+ if (padapter->bDriverStopped || padapter->bSurpriseRemoved)
+ break;
+
+ if (i < try_cnt && wait_ms > 0 && ret == _FAIL)
+ msleep(wait_ms);
+ } while ((i < try_cnt) && ((ret == _FAIL) || (wait_ms == 0)));
+
+ if (ret != _FAIL) {
+ ret = _SUCCESS;
+ goto exit;
+ }
+
+ if (try_cnt && wait_ms) {
+ if (da)
+ DBG_88E(FUNC_ADPT_FMT" to %pM, ch:%u%s, %d/%d in %u ms\n",
+ FUNC_ADPT_ARG(padapter), da, rtw_get_oper_ch(padapter),
+ ret == _SUCCESS ? ", acked" : "", i, try_cnt, rtw_get_passing_time_ms(start));
+ else
+ DBG_88E(FUNC_ADPT_FMT", ch:%u%s, %d/%d in %u ms\n",
+ FUNC_ADPT_ARG(padapter), rtw_get_oper_ch(padapter),
+ ret == _SUCCESS ? ", acked" : "", i, try_cnt, rtw_get_passing_time_ms(start));
+ }
+exit:
+ return ret;
+}
+
+/* when wait_ack is true, this function should be called at process context */
+static int _issue_qos_nulldata(struct adapter *padapter, unsigned char *da, u16 tid, int wait_ack)
+{
+ int ret = _FAIL;
+ struct xmit_frame *pmgntframe;
+ struct pkt_attrib *pattrib;
+ unsigned char *pframe;
+ struct rtw_ieee80211_hdr *pwlanhdr;
+ __le16 *fctrl;
+ unsigned short *qc;
+ struct xmit_priv *pxmitpriv = &(padapter->xmitpriv);
+ struct mlme_ext_priv *pmlmeext = &(padapter->mlmeextpriv);
+ struct mlme_ext_info *pmlmeinfo = &(pmlmeext->mlmext_info);
+ struct wlan_bssid_ex *pnetwork = &(pmlmeinfo->network);
+
+ DBG_88E("%s\n", __func__);
+
+ pmgntframe = alloc_mgtxmitframe(pxmitpriv);
+ if (pmgntframe == NULL)
+ goto exit;
+
+ /* update attribute */
+ pattrib = &pmgntframe->attrib;
+ update_mgntframe_attrib(padapter, pattrib);
+
+ pattrib->hdrlen += 2;
+ pattrib->qos_en = true;
+ pattrib->eosp = 1;
+ pattrib->ack_policy = 0;
+ pattrib->mdata = 0;
+
+ memset(pmgntframe->buf_addr, 0, WLANHDR_OFFSET + TXDESC_OFFSET);
+
+ pframe = (u8 *)(pmgntframe->buf_addr) + TXDESC_OFFSET;
+ pwlanhdr = (struct rtw_ieee80211_hdr *)pframe;
+
+ fctrl = &(pwlanhdr->frame_ctl);
+ *(fctrl) = 0;
+
+ if ((pmlmeinfo->state&0x03) == WIFI_FW_AP_STATE)
+ SetFrDs(fctrl);
+ else if ((pmlmeinfo->state&0x03) == WIFI_FW_STATION_STATE)
+ SetToDs(fctrl);
+
+ if (pattrib->mdata)
+ SetMData(fctrl);
+
+ qc = (unsigned short *)(pframe + pattrib->hdrlen - 2);
+
+ SetPriority(qc, tid);
+
+ SetEOSP(qc, pattrib->eosp);
+
+ SetAckpolicy(qc, pattrib->ack_policy);
+
+ memcpy(pwlanhdr->addr1, da, ETH_ALEN);
+ memcpy(pwlanhdr->addr2, myid(&(padapter->eeprompriv)), ETH_ALEN);
+ memcpy(pwlanhdr->addr3, pnetwork->MacAddress, ETH_ALEN);
+
+ SetSeqNum(pwlanhdr, pmlmeext->mgnt_seq);
+ pmlmeext->mgnt_seq++;
+ SetFrameSubType(pframe, WIFI_QOS_DATA_NULL);
+
+ pframe += sizeof(struct rtw_ieee80211_hdr_3addr_qos);
+ pattrib->pktlen = sizeof(struct rtw_ieee80211_hdr_3addr_qos);
+
+ pattrib->last_txcmdsz = pattrib->pktlen;
+
+ if (wait_ack) {
+ ret = dump_mgntframe_and_wait_ack(padapter, pmgntframe);
+ } else {
+ dump_mgntframe(padapter, pmgntframe);
+ ret = _SUCCESS;
+ }
+
+exit:
+ return ret;
+}
+
+/* when wait_ms > 0 , this function should be called at process context */
+/* da == NULL for station mode */
+int issue_qos_nulldata(struct adapter *padapter, unsigned char *da, u16 tid, int try_cnt, int wait_ms)
+{
+ int ret;
+ int i = 0;
+ u32 start = jiffies;
+ struct mlme_ext_priv *pmlmeext = &(padapter->mlmeextpriv);
+ struct mlme_ext_info *pmlmeinfo = &(pmlmeext->mlmext_info);
+ struct wlan_bssid_ex *pnetwork = &(pmlmeinfo->network);
+
+ /* da == NULL, assume it's null data for sta to ap*/
+ if (da == NULL)
+ da = pnetwork->MacAddress;
+
+ do {
+ ret = _issue_qos_nulldata(padapter, da, tid, wait_ms > 0 ? true : false);
+
+ i++;
+
+ if (padapter->bDriverStopped || padapter->bSurpriseRemoved)
+ break;
+
+ if (i < try_cnt && wait_ms > 0 && ret == _FAIL)
+ msleep(wait_ms);
+ } while ((i < try_cnt) && ((ret == _FAIL) || (wait_ms == 0)));
+
+ if (ret != _FAIL) {
+ ret = _SUCCESS;
+ goto exit;
+ }
+
+ if (try_cnt && wait_ms) {
+ if (da)
+ DBG_88E(FUNC_ADPT_FMT" to %pM, ch:%u%s, %d/%d in %u ms\n",
+ FUNC_ADPT_ARG(padapter), da, rtw_get_oper_ch(padapter),
+ ret == _SUCCESS ? ", acked" : "", i, try_cnt, rtw_get_passing_time_ms(start));
+ else
+ DBG_88E(FUNC_ADPT_FMT", ch:%u%s, %d/%d in %u ms\n",
+ FUNC_ADPT_ARG(padapter), rtw_get_oper_ch(padapter),
+ ret == _SUCCESS ? ", acked" : "", i, try_cnt, rtw_get_passing_time_ms(start));
+ }
+exit:
+ return ret;
+}
+
+static int _issue_deauth(struct adapter *padapter, unsigned char *da, unsigned short reason, u8 wait_ack)
+{
+ struct xmit_frame *pmgntframe;
+ struct pkt_attrib *pattrib;
+ unsigned char *pframe;
+ struct rtw_ieee80211_hdr *pwlanhdr;
+ __le16 *fctrl;
+ struct xmit_priv *pxmitpriv = &(padapter->xmitpriv);
+ struct mlme_ext_priv *pmlmeext = &(padapter->mlmeextpriv);
+ struct mlme_ext_info *pmlmeinfo = &(pmlmeext->mlmext_info);
+ struct wlan_bssid_ex *pnetwork = &(pmlmeinfo->network);
+ int ret = _FAIL;
+ __le16 le_tmp;
+
+ pmgntframe = alloc_mgtxmitframe(pxmitpriv);
+ if (pmgntframe == NULL)
+ goto exit;
+
+ /* update attribute */
+ pattrib = &pmgntframe->attrib;
+ update_mgntframe_attrib(padapter, pattrib);
+ pattrib->retry_ctrl = false;
+
+ memset(pmgntframe->buf_addr, 0, WLANHDR_OFFSET + TXDESC_OFFSET);
+
+ pframe = (u8 *)(pmgntframe->buf_addr) + TXDESC_OFFSET;
+ pwlanhdr = (struct rtw_ieee80211_hdr *)pframe;
+
+ fctrl = &(pwlanhdr->frame_ctl);
+ *(fctrl) = 0;
+
+ memcpy(pwlanhdr->addr1, da, ETH_ALEN);
+ memcpy(pwlanhdr->addr2, myid(&(padapter->eeprompriv)), ETH_ALEN);
+ memcpy(pwlanhdr->addr3, pnetwork->MacAddress, ETH_ALEN);
+
+ SetSeqNum(pwlanhdr, pmlmeext->mgnt_seq);
+ pmlmeext->mgnt_seq++;
+ SetFrameSubType(pframe, WIFI_DEAUTH);
+
+ pframe += sizeof(struct rtw_ieee80211_hdr_3addr);
+ pattrib->pktlen = sizeof(struct rtw_ieee80211_hdr_3addr);
+
+ le_tmp = cpu_to_le16(reason);
+ pframe = rtw_set_fixed_ie(pframe, _RSON_CODE_ , (unsigned char *)&le_tmp, &(pattrib->pktlen));
+
+ pattrib->last_txcmdsz = pattrib->pktlen;
+
+
+ if (wait_ack) {
+ ret = dump_mgntframe_and_wait_ack(padapter, pmgntframe);
+ } else {
+ dump_mgntframe(padapter, pmgntframe);
+ ret = _SUCCESS;
+ }
+
+exit:
+ return ret;
+}
+
+int issue_deauth(struct adapter *padapter, unsigned char *da, unsigned short reason)
+{
+ DBG_88E("%s to %pM\n", __func__, da);
+ return _issue_deauth(padapter, da, reason, false);
+}
+
+int issue_deauth_ex(struct adapter *padapter, u8 *da, unsigned short reason, int try_cnt,
+ int wait_ms)
+{
+ int ret;
+ int i = 0;
+ u32 start = jiffies;
+
+ do {
+ ret = _issue_deauth(padapter, da, reason, wait_ms > 0 ? true : false);
+
+ i++;
+
+ if (padapter->bDriverStopped || padapter->bSurpriseRemoved)
+ break;
+
+ if (i < try_cnt && wait_ms > 0 && ret == _FAIL)
+ msleep(wait_ms);
+ } while ((i < try_cnt) && ((ret == _FAIL) || (wait_ms == 0)));
+
+ if (ret != _FAIL) {
+ ret = _SUCCESS;
+ goto exit;
+ }
+
+ if (try_cnt && wait_ms) {
+ if (da)
+ DBG_88E(FUNC_ADPT_FMT" to %pM, ch:%u%s, %d/%d in %u ms\n",
+ FUNC_ADPT_ARG(padapter), da, rtw_get_oper_ch(padapter),
+ ret == _SUCCESS ? ", acked" : "", i, try_cnt, rtw_get_passing_time_ms(start));
+ else
+ DBG_88E(FUNC_ADPT_FMT", ch:%u%s, %d/%d in %u ms\n",
+ FUNC_ADPT_ARG(padapter), rtw_get_oper_ch(padapter),
+ ret == _SUCCESS ? ", acked" : "", i, try_cnt, rtw_get_passing_time_ms(start));
+ }
+exit:
+ return ret;
+}
+
+void issue_action_spct_ch_switch(struct adapter *padapter, u8 *ra, u8 new_ch, u8 ch_offset)
+{
+ struct xmit_frame *pmgntframe;
+ struct pkt_attrib *pattrib;
+ unsigned char *pframe;
+ struct rtw_ieee80211_hdr *pwlanhdr;
+ __le16 *fctrl;
+ struct xmit_priv *pxmitpriv = &(padapter->xmitpriv);
+ struct mlme_ext_priv *pmlmeext = &(padapter->mlmeextpriv);
+
+
+ DBG_88E(FUNC_NDEV_FMT" ra =%pM, ch:%u, offset:%u\n",
+ FUNC_NDEV_ARG(padapter->pnetdev), ra, new_ch, ch_offset);
+
+ pmgntframe = alloc_mgtxmitframe(pxmitpriv);
+ if (pmgntframe == NULL)
+ return;
+
+ /* update attribute */
+ pattrib = &pmgntframe->attrib;
+ update_mgntframe_attrib(padapter, pattrib);
+
+ memset(pmgntframe->buf_addr, 0, WLANHDR_OFFSET + TXDESC_OFFSET);
+
+ pframe = (u8 *)(pmgntframe->buf_addr) + TXDESC_OFFSET;
+ pwlanhdr = (struct rtw_ieee80211_hdr *)pframe;
+
+ fctrl = &(pwlanhdr->frame_ctl);
+ *(fctrl) = 0;
+
+ memcpy(pwlanhdr->addr1, ra, ETH_ALEN); /* RA */
+ memcpy(pwlanhdr->addr2, myid(&(padapter->eeprompriv)), ETH_ALEN); /* TA */
+ memcpy(pwlanhdr->addr3, ra, ETH_ALEN); /* DA = RA */
+
+ SetSeqNum(pwlanhdr, pmlmeext->mgnt_seq);
+ pmlmeext->mgnt_seq++;
+ SetFrameSubType(pframe, WIFI_ACTION);
+
+ pframe += sizeof(struct rtw_ieee80211_hdr_3addr);
+ pattrib->pktlen = sizeof(struct rtw_ieee80211_hdr_3addr);
+
+ /* category, action */
+ {
+ u8 category, action;
+ category = RTW_WLAN_CATEGORY_SPECTRUM_MGMT;
+ action = RTW_WLAN_ACTION_SPCT_CHL_SWITCH;
+
+ pframe = rtw_set_fixed_ie(pframe, 1, &(category), &(pattrib->pktlen));
+ pframe = rtw_set_fixed_ie(pframe, 1, &(action), &(pattrib->pktlen));
+ }
+
+ pframe = rtw_set_ie_ch_switch(pframe, &(pattrib->pktlen), 0, new_ch, 0);
+ pframe = rtw_set_ie_secondary_ch_offset(pframe, &(pattrib->pktlen),
+ hal_ch_offset_to_secondary_ch_offset(ch_offset));
+
+ pattrib->last_txcmdsz = pattrib->pktlen;
+
+ dump_mgntframe(padapter, pmgntframe);
+}
+
+void issue_action_BA(struct adapter *padapter, unsigned char *raddr, unsigned char action, unsigned short status)
+{
+ u8 category = RTW_WLAN_CATEGORY_BACK;
+ u16 start_seq;
+ u16 BA_para_set;
+ u16 reason_code;
+ u16 BA_timeout_value;
+ __le16 le_tmp;
+ u16 BA_starting_seqctrl = 0;
+ enum ht_cap_ampdu_factor max_rx_ampdu_factor;
+ struct xmit_frame *pmgntframe;
+ struct pkt_attrib *pattrib;
+ u8 *pframe;
+ struct rtw_ieee80211_hdr *pwlanhdr;
+ __le16 *fctrl;
+ struct xmit_priv *pxmitpriv = &(padapter->xmitpriv);
+ struct mlme_ext_priv *pmlmeext = &(padapter->mlmeextpriv);
+ struct mlme_ext_info *pmlmeinfo = &(pmlmeext->mlmext_info);
+ struct sta_info *psta;
+ struct sta_priv *pstapriv = &padapter->stapriv;
+ struct registry_priv *pregpriv = &padapter->registrypriv;
+ struct wlan_bssid_ex *pnetwork = &(pmlmeinfo->network);
+
+ DBG_88E("%s, category=%d, action=%d, status=%d\n", __func__, category, action, status);
+
+ pmgntframe = alloc_mgtxmitframe(pxmitpriv);
+ if (pmgntframe == NULL)
+ return;
+
+ /* update attribute */
+ pattrib = &pmgntframe->attrib;
+ update_mgntframe_attrib(padapter, pattrib);
+
+ memset(pmgntframe->buf_addr, 0, WLANHDR_OFFSET + TXDESC_OFFSET);
+
+ pframe = (u8 *)(pmgntframe->buf_addr) + TXDESC_OFFSET;
+ pwlanhdr = (struct rtw_ieee80211_hdr *)pframe;
+
+ fctrl = &(pwlanhdr->frame_ctl);
+ *(fctrl) = 0;
+
+ /* memcpy(pwlanhdr->addr1, get_my_bssid(&(pmlmeinfo->network)), ETH_ALEN); */
+ memcpy(pwlanhdr->addr1, raddr, ETH_ALEN);
+ memcpy(pwlanhdr->addr2, myid(&(padapter->eeprompriv)), ETH_ALEN);
+ memcpy(pwlanhdr->addr3, pnetwork->MacAddress, ETH_ALEN);
+
+ SetSeqNum(pwlanhdr, pmlmeext->mgnt_seq);
+ pmlmeext->mgnt_seq++;
+ SetFrameSubType(pframe, WIFI_ACTION);
+
+ pframe += sizeof(struct rtw_ieee80211_hdr_3addr);
+ pattrib->pktlen = sizeof(struct rtw_ieee80211_hdr_3addr);
+
+ pframe = rtw_set_fixed_ie(pframe, 1, &(category), &(pattrib->pktlen));
+ pframe = rtw_set_fixed_ie(pframe, 1, &(action), &(pattrib->pktlen));
+
+ if (category == 3) {
+ switch (action) {
+ case 0: /* ADDBA req */
+ do {
+ pmlmeinfo->dialogToken++;
+ } while (pmlmeinfo->dialogToken == 0);
+ pframe = rtw_set_fixed_ie(pframe, 1, &(pmlmeinfo->dialogToken), &(pattrib->pktlen));
+
+ BA_para_set = 0x1002 | ((status & 0xf) << 2); /* immediate ack & 64 buffer size */
+ le_tmp = cpu_to_le16(BA_para_set);
+ pframe = rtw_set_fixed_ie(pframe, 2, (unsigned char *)(&(le_tmp)), &(pattrib->pktlen));
+
+ BA_timeout_value = 5000;/* 5ms */
+ le_tmp = cpu_to_le16(BA_timeout_value);
+ pframe = rtw_set_fixed_ie(pframe, 2, (unsigned char *)(&(le_tmp)), &(pattrib->pktlen));
+
+ psta = rtw_get_stainfo(pstapriv, raddr);
+ if (psta != NULL) {
+ start_seq = (psta->sta_xmitpriv.txseq_tid[status & 0x07]&0xfff) + 1;
+
+ DBG_88E("BA_starting_seqctrl=%d for TID=%d\n", start_seq, status & 0x07);
+
+ psta->BA_starting_seqctrl[status & 0x07] = start_seq;
+
+ BA_starting_seqctrl = start_seq << 4;
+ }
+ le_tmp = cpu_to_le16(BA_starting_seqctrl);
+ pframe = rtw_set_fixed_ie(pframe, 2, (unsigned char *)(&(le_tmp)), &(pattrib->pktlen));
+ break;
+ case 1: /* ADDBA rsp */
+ pframe = rtw_set_fixed_ie(pframe, 1, &(pmlmeinfo->ADDBA_req.dialog_token), &(pattrib->pktlen));
+ pframe = rtw_set_fixed_ie(pframe, 2, (unsigned char *)(&status), &(pattrib->pktlen));
+
+ BA_para_set = le16_to_cpu(pmlmeinfo->ADDBA_req.BA_para_set) & 0x3f;
+ rtw_hal_get_def_var(padapter, HW_VAR_MAX_RX_AMPDU_FACTOR, &max_rx_ampdu_factor);
+ switch (max_rx_ampdu_factor) {
+ case MAX_AMPDU_FACTOR_64K:
+ BA_para_set |= 0x1000; /* 64 buffer size */
+ break;
+ case MAX_AMPDU_FACTOR_32K:
+ BA_para_set |= 0x0800; /* 32 buffer size */
+ break;
+ case MAX_AMPDU_FACTOR_16K:
+ BA_para_set |= 0x0400; /* 16 buffer size */
+ break;
+ case MAX_AMPDU_FACTOR_8K:
+ BA_para_set |= 0x0200; /* 8 buffer size */
+ break;
+ default:
+ BA_para_set |= 0x1000; /* 64 buffer size */
+ break;
+ }
+
+ if (pregpriv->ampdu_amsdu == 0)/* disabled */
+ BA_para_set = BA_para_set & ~BIT(0);
+ else if (pregpriv->ampdu_amsdu == 1)/* enabled */
+ BA_para_set = BA_para_set | BIT(0);
+ le_tmp = cpu_to_le16(BA_para_set);
+
+ pframe = rtw_set_fixed_ie(pframe, 2, (unsigned char *)(&(le_tmp)), &(pattrib->pktlen));
+ pframe = rtw_set_fixed_ie(pframe, 2, (unsigned char *)(&(pmlmeinfo->ADDBA_req.BA_timeout_value)), &(pattrib->pktlen));
+ break;
+ case 2:/* DELBA */
+ BA_para_set = (status & 0x1F) << 3;
+ le_tmp = cpu_to_le16(BA_para_set);
+ pframe = rtw_set_fixed_ie(pframe, 2, (unsigned char *)(&(le_tmp)), &(pattrib->pktlen));
+
+ reason_code = 37;/* Requested from peer STA as it does not want to use the mechanism */
+ le_tmp = cpu_to_le16(reason_code);
+ pframe = rtw_set_fixed_ie(pframe, 2, (unsigned char *)(&(le_tmp)), &(pattrib->pktlen));
+ break;
+ default:
+ break;
+ }
+ }
+
+ pattrib->last_txcmdsz = pattrib->pktlen;
+
+ dump_mgntframe(padapter, pmgntframe);
+}
+
+static void issue_action_BSSCoexistPacket(struct adapter *padapter)
+{
+ struct list_head *plist, *phead;
+ unsigned char category, action;
+ struct xmit_frame *pmgntframe;
+ struct pkt_attrib *pattrib;
+ unsigned char *pframe;
+ struct rtw_ieee80211_hdr *pwlanhdr;
+ __le16 *fctrl;
+ struct wlan_network *pnetwork = NULL;
+ struct xmit_priv *pxmitpriv = &(padapter->xmitpriv);
+ struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
+ struct mlme_ext_priv *pmlmeext = &(padapter->mlmeextpriv);
+ struct mlme_ext_info *pmlmeinfo = &(pmlmeext->mlmext_info);
+ struct __queue *queue = &(pmlmepriv->scanned_queue);
+ u8 InfoContent[16] = {0};
+ u8 ICS[8][15];
+ struct wlan_bssid_ex *cur_network = &(pmlmeinfo->network);
+
+ if ((pmlmepriv->num_FortyMHzIntolerant == 0) || (pmlmepriv->num_sta_no_ht == 0))
+ return;
+
+ if (pmlmeinfo->bwmode_updated)
+ return;
+
+
+ DBG_88E("%s\n", __func__);
+
+
+ category = RTW_WLAN_CATEGORY_PUBLIC;
+ action = ACT_PUBLIC_BSSCOEXIST;
+
+ pmgntframe = alloc_mgtxmitframe(pxmitpriv);
+ if (pmgntframe == NULL)
+ return;
+
+ /* update attribute */
+ pattrib = &pmgntframe->attrib;
+ update_mgntframe_attrib(padapter, pattrib);
+
+ memset(pmgntframe->buf_addr, 0, WLANHDR_OFFSET + TXDESC_OFFSET);
+
+ pframe = (u8 *)(pmgntframe->buf_addr) + TXDESC_OFFSET;
+ pwlanhdr = (struct rtw_ieee80211_hdr *)pframe;
+
+ fctrl = &(pwlanhdr->frame_ctl);
+ *(fctrl) = 0;
+
+ memcpy(pwlanhdr->addr1, cur_network->MacAddress, ETH_ALEN);
+ memcpy(pwlanhdr->addr2, myid(&(padapter->eeprompriv)), ETH_ALEN);
+ memcpy(pwlanhdr->addr3, cur_network->MacAddress, ETH_ALEN);
+
+ SetSeqNum(pwlanhdr, pmlmeext->mgnt_seq);
+ pmlmeext->mgnt_seq++;
+ SetFrameSubType(pframe, WIFI_ACTION);
+
+ pframe += sizeof(struct rtw_ieee80211_hdr_3addr);
+ pattrib->pktlen = sizeof(struct rtw_ieee80211_hdr_3addr);
+
+ pframe = rtw_set_fixed_ie(pframe, 1, &(category), &(pattrib->pktlen));
+ pframe = rtw_set_fixed_ie(pframe, 1, &(action), &(pattrib->pktlen));
+
+
+ /* */
+ if (pmlmepriv->num_FortyMHzIntolerant > 0) {
+ u8 iedata = 0;
+
+ iedata |= BIT(2);/* 20 MHz BSS Width Request */
+
+ pframe = rtw_set_ie(pframe, EID_BSSCoexistence, 1, &iedata, &(pattrib->pktlen));
+ }
+
+
+ /* */
+ memset(ICS, 0, sizeof(ICS));
+ if (pmlmepriv->num_sta_no_ht > 0) {
+ int i;
+
+ spin_lock_bh(&(pmlmepriv->scanned_queue.lock));
+
+ phead = get_list_head(queue);
+ plist = phead->next;
+
+ while (phead != plist) {
+ int len;
+ u8 *p;
+ struct wlan_bssid_ex *pbss_network;
+
+ pnetwork = container_of(plist, struct wlan_network, list);
+
+ plist = plist->next;
+
+ pbss_network = (struct wlan_bssid_ex *)&pnetwork->network;
+
+ p = rtw_get_ie(pbss_network->IEs + _FIXED_IE_LENGTH_, _HT_CAPABILITY_IE_, &len, pbss_network->IELength - _FIXED_IE_LENGTH_);
+ if ((p == NULL) || (len == 0)) { /* non-HT */
+ if ((pbss_network->Configuration.DSConfig <= 0) || (pbss_network->Configuration.DSConfig > 14))
+ continue;
+
+ ICS[0][pbss_network->Configuration.DSConfig] = 1;
+
+ if (ICS[0][0] == 0)
+ ICS[0][0] = 1;
+ }
+ }
+ spin_unlock_bh(&pmlmepriv->scanned_queue.lock);
+
+ for (i = 0; i < 8; i++) {
+ if (ICS[i][0] == 1) {
+ int j, k = 0;
+
+ InfoContent[k] = i;
+ /* SET_BSS_INTOLERANT_ELE_REG_CLASS(InfoContent, i); */
+ k++;
+
+ for (j = 1; j <= 14; j++) {
+ if (ICS[i][j] == 1) {
+ if (k < 16) {
+ InfoContent[k] = j; /* channel number */
+ /* SET_BSS_INTOLERANT_ELE_CHANNEL(InfoContent+k, j); */
+ k++;
+ }
+ }
+ }
+
+ pframe = rtw_set_ie(pframe, EID_BSSIntolerantChlReport, k, InfoContent, &(pattrib->pktlen));
+ }
+ }
+ }
+
+
+ pattrib->last_txcmdsz = pattrib->pktlen;
+
+ dump_mgntframe(padapter, pmgntframe);
+}
+
+unsigned int send_delba(struct adapter *padapter, u8 initiator, u8 *addr)
+{
+ struct sta_priv *pstapriv = &padapter->stapriv;
+ struct sta_info *psta = NULL;
+ /* struct recv_reorder_ctrl *preorder_ctrl; */
+ struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv;
+ struct mlme_ext_info *pmlmeinfo = &(pmlmeext->mlmext_info);
+ u16 tid;
+
+ if ((pmlmeinfo->state&0x03) != WIFI_FW_AP_STATE)
+ if (!(pmlmeinfo->state & WIFI_FW_ASSOC_SUCCESS))
+ return _SUCCESS;
+
+ psta = rtw_get_stainfo(pstapriv, addr);
+ if (psta == NULL)
+ return _SUCCESS;
+
+ if (initiator == 0) { /* recipient */
+ for (tid = 0; tid < MAXTID; tid++) {
+ if (psta->recvreorder_ctrl[tid].enable) {
+ DBG_88E("rx agg disable tid(%d)\n", tid);
+ issue_action_BA(padapter, addr, RTW_WLAN_ACTION_DELBA, (((tid << 1) | initiator)&0x1F));
+ psta->recvreorder_ctrl[tid].enable = false;
+ psta->recvreorder_ctrl[tid].indicate_seq = 0xffff;
+ }
+ }
+ } else if (initiator == 1) { /* originator */
+ for (tid = 0; tid < MAXTID; tid++) {
+ if (psta->htpriv.agg_enable_bitmap & BIT(tid)) {
+ DBG_88E("tx agg disable tid(%d)\n", tid);
+ issue_action_BA(padapter, addr, RTW_WLAN_ACTION_DELBA, (((tid << 1) | initiator)&0x1F));
+ psta->htpriv.agg_enable_bitmap &= ~BIT(tid);
+ psta->htpriv.candidate_tid_bitmap &= ~BIT(tid);
+ }
+ }
+ }
+
+ return _SUCCESS;
+}
+
+unsigned int send_beacon(struct adapter *padapter)
+{
+ u8 bxmitok = false;
+ int issue = 0;
+ int poll = 0;
+
+ u32 start = jiffies;
+
+ rtw_hal_set_hwreg(padapter, HW_VAR_BCN_VALID, NULL);
+ do {
+ issue_beacon(padapter, 100);
+ issue++;
+ do {
+ yield();
+ rtw_hal_get_hwreg(padapter, HW_VAR_BCN_VALID, (u8 *)(&bxmitok));
+ poll++;
+ } while ((poll%10) != 0 && !bxmitok && !padapter->bSurpriseRemoved && !padapter->bDriverStopped);
+ } while (!bxmitok && issue < 100 && !padapter->bSurpriseRemoved && !padapter->bDriverStopped);
+
+ if (padapter->bSurpriseRemoved || padapter->bDriverStopped)
+ return _FAIL;
+ if (!bxmitok) {
+ DBG_88E("%s fail! %u ms\n", __func__, rtw_get_passing_time_ms(start));
+ return _FAIL;
+ } else {
+ u32 passing_time = rtw_get_passing_time_ms(start);
+
+ if (passing_time > 100 || issue > 3)
+ DBG_88E("%s success, issue:%d, poll:%d, %u ms\n", __func__, issue, poll, rtw_get_passing_time_ms(start));
+ return _SUCCESS;
+ }
+}
+
+/****************************************************************************
+
+Following are some utility functions for WiFi MLME
+
+*****************************************************************************/
+
+void site_survey(struct adapter *padapter)
+{
+ unsigned char survey_channel = 0, val8;
+ enum rt_scan_type ScanType = SCAN_PASSIVE;
+ struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv;
+ struct mlme_ext_info *pmlmeinfo = &(pmlmeext->mlmext_info);
+ u32 initialgain = 0;
+ struct rtw_ieee80211_channel *ch;
+
+ if (pmlmeext->sitesurvey_res.channel_idx < pmlmeext->sitesurvey_res.ch_num) {
+ ch = &pmlmeext->sitesurvey_res.ch[pmlmeext->sitesurvey_res.channel_idx];
+ survey_channel = ch->hw_value;
+ ScanType = (ch->flags & RTW_IEEE80211_CHAN_PASSIVE_SCAN) ? SCAN_PASSIVE : SCAN_ACTIVE;
+ }
+
+
+ if (survey_channel != 0) {
+ /* PAUSE 4-AC Queue when site_survey */
+ /* rtw_hal_get_hwreg(padapter, HW_VAR_TXPAUSE, (u8 *)(&val8)); */
+ /* val8 |= 0x0f; */
+ /* rtw_hal_set_hwreg(padapter, HW_VAR_TXPAUSE, (u8 *)(&val8)); */
+ if (pmlmeext->sitesurvey_res.channel_idx == 0)
+ set_channel_bwmode(padapter, survey_channel, HAL_PRIME_CHNL_OFFSET_DONT_CARE, HT_CHANNEL_WIDTH_20);
+ else
+ SelectChannel(padapter, survey_channel);
+
+ if (ScanType == SCAN_ACTIVE) { /* obey the channel plan setting... */
+ int i;
+ for (i = 0; i < RTW_SSID_SCAN_AMOUNT; i++) {
+ if (pmlmeext->sitesurvey_res.ssid[i].SsidLength) {
+ /* todo: to issue two probe req??? */
+ issue_probereq(padapter, &(pmlmeext->sitesurvey_res.ssid[i]), NULL);
+ /* msleep(SURVEY_TO>>1); */
+ issue_probereq(padapter, &(pmlmeext->sitesurvey_res.ssid[i]), NULL);
+ }
+ }
+
+ if (pmlmeext->sitesurvey_res.scan_mode == SCAN_ACTIVE) {
+ /* todo: to issue two probe req??? */
+ issue_probereq(padapter, NULL, NULL);
+ /* msleep(SURVEY_TO>>1); */
+ issue_probereq(padapter, NULL, NULL);
+ }
+
+ if (pmlmeext->sitesurvey_res.scan_mode == SCAN_ACTIVE) {
+ /* todo: to issue two probe req??? */
+ issue_probereq(padapter, NULL, NULL);
+ /* msleep(SURVEY_TO>>1); */
+ issue_probereq(padapter, NULL, NULL);
+ }
+ }
+
+ set_survey_timer(pmlmeext, pmlmeext->chan_scan_time);
+ } else {
+
+ /* 20100721:Interrupt scan operation here. */
+ /* For SW antenna diversity before link, it needs to switch to another antenna and scan again. */
+ /* It compares the scan result and select better one to do connection. */
+ if (rtw_hal_antdiv_before_linked(padapter)) {
+ pmlmeext->sitesurvey_res.bss_cnt = 0;
+ pmlmeext->sitesurvey_res.channel_idx = -1;
+ pmlmeext->chan_scan_time = SURVEY_TO / 2;
+ set_survey_timer(pmlmeext, pmlmeext->chan_scan_time);
+ return;
+ }
+
+ pmlmeext->sitesurvey_res.state = SCAN_COMPLETE;
+
+ /* switch back to the original channel */
+
+ set_channel_bwmode(padapter, pmlmeext->cur_channel, pmlmeext->cur_ch_offset, pmlmeext->cur_bwmode);
+
+ /* flush 4-AC Queue after site_survey */
+ /* val8 = 0; */
+ /* rtw_hal_set_hwreg(padapter, HW_VAR_TXPAUSE, (u8 *)(&val8)); */
+
+ /* config MSR */
+ Set_MSR(padapter, (pmlmeinfo->state & 0x3));
+
+ initialgain = 0xff; /* restore RX GAIN */
+ rtw_hal_set_hwreg(padapter, HW_VAR_INITIAL_GAIN, (u8 *)(&initialgain));
+ /* turn on dynamic functions */
+ Restore_DM_Func_Flag(padapter);
+ /* Switch_DM_Func(padapter, DYNAMIC_ALL_FUNC_ENABLE, true); */
+
+ if (is_client_associated_to_ap(padapter))
+ issue_nulldata(padapter, NULL, 0, 3, 500);
+
+ val8 = 0; /* survey done */
+ rtw_hal_set_hwreg(padapter, HW_VAR_MLME_SITESURVEY, (u8 *)(&val8));
+
+ report_surveydone_event(padapter);
+
+ pmlmeext->chan_scan_time = SURVEY_TO;
+ pmlmeext->sitesurvey_res.state = SCAN_DISABLE;
+
+ issue_action_BSSCoexistPacket(padapter);
+ issue_action_BSSCoexistPacket(padapter);
+ issue_action_BSSCoexistPacket(padapter);
+ }
+ return;
+}
+
+/* collect bss info from Beacon and Probe request/response frames. */
+u8 collect_bss_info(struct adapter *padapter, struct recv_frame *precv_frame, struct wlan_bssid_ex *bssid)
+{
+ int i;
+ u32 len;
+ u8 *p;
+ u16 val16, subtype;
+ u8 *pframe = precv_frame->rx_data;
+ u32 packet_len = precv_frame->len;
+ u8 ie_offset;
+ struct registry_priv *pregistrypriv = &padapter->registrypriv;
+ struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv;
+ struct mlme_ext_info *pmlmeinfo = &(pmlmeext->mlmext_info);
+
+ len = packet_len - sizeof(struct rtw_ieee80211_hdr_3addr);
+
+ if (len > MAX_IE_SZ)
+ return _FAIL;
+
+ memset(bssid, 0, sizeof(struct wlan_bssid_ex));
+
+ subtype = GetFrameSubType(pframe);
+
+ if (subtype == WIFI_BEACON) {
+ bssid->Reserved[0] = 1;
+ ie_offset = _BEACON_IE_OFFSET_;
+ } else {
+ /* FIXME : more type */
+ if (subtype == WIFI_PROBEREQ) {
+ ie_offset = _PROBEREQ_IE_OFFSET_;
+ bssid->Reserved[0] = 2;
+ } else if (subtype == WIFI_PROBERSP) {
+ ie_offset = _PROBERSP_IE_OFFSET_;
+ bssid->Reserved[0] = 3;
+ } else {
+ bssid->Reserved[0] = 0;
+ ie_offset = _FIXED_IE_LENGTH_;
+ }
+ }
+
+ bssid->Length = sizeof(struct wlan_bssid_ex) - MAX_IE_SZ + len;
+
+ /* below is to copy the information element */
+ bssid->IELength = len;
+ memcpy(bssid->IEs, (pframe + sizeof(struct rtw_ieee80211_hdr_3addr)), bssid->IELength);
+
+ /* get the signal strength in dBM.raw data */
+ bssid->Rssi = precv_frame->attrib.phy_info.recvpower;
+ bssid->PhyInfo.SignalQuality = precv_frame->attrib.phy_info.SignalQuality;/* in percentage */
+ bssid->PhyInfo.SignalStrength = precv_frame->attrib.phy_info.SignalStrength;/* in percentage */
+ rtw_hal_get_def_var(padapter, HAL_DEF_CURRENT_ANTENNA, &bssid->PhyInfo.Optimum_antenna);
+
+ /* checking SSID */
+ p = rtw_get_ie(bssid->IEs + ie_offset, _SSID_IE_, &len, bssid->IELength - ie_offset);
+ if (p == NULL) {
+ DBG_88E("marc: cannot find SSID for survey event\n");
+ return _FAIL;
+ }
+
+ if (len) {
+ if (len > NDIS_802_11_LENGTH_SSID) {
+ DBG_88E("%s()-%d: IE too long (%d) for survey event\n", __func__, __LINE__, len);
+ return _FAIL;
+ }
+ memcpy(bssid->Ssid.Ssid, (p + 2), len);
+ bssid->Ssid.SsidLength = len;
+ } else {
+ bssid->Ssid.SsidLength = 0;
+ }
+
+ memset(bssid->SupportedRates, 0, NDIS_802_11_LENGTH_RATES_EX);
+
+ /* checking rate info... */
+ i = 0;
+ p = rtw_get_ie(bssid->IEs + ie_offset, _SUPPORTEDRATES_IE_, &len, bssid->IELength - ie_offset);
+ if (p != NULL) {
+ if (len > NDIS_802_11_LENGTH_RATES_EX) {
+ DBG_88E("%s()-%d: IE too long (%d) for survey event\n", __func__, __LINE__, len);
+ return _FAIL;
+ }
+ memcpy(bssid->SupportedRates, (p + 2), len);
+ i = len;
+ }
+
+ p = rtw_get_ie(bssid->IEs + ie_offset, _EXT_SUPPORTEDRATES_IE_, &len, bssid->IELength - ie_offset);
+ if (p != NULL) {
+ if (len > (NDIS_802_11_LENGTH_RATES_EX-i)) {
+ DBG_88E("%s()-%d: IE too long (%d) for survey event\n", __func__, __LINE__, len);
+ return _FAIL;
+ }
+ memcpy(bssid->SupportedRates + i, (p + 2), len);
+ }
+
+ /* todo: */
+ bssid->NetworkTypeInUse = Ndis802_11OFDM24;
+
+ if (bssid->IELength < 12)
+ return _FAIL;
+
+ /* Checking for DSConfig */
+ p = rtw_get_ie(bssid->IEs + ie_offset, _DSSET_IE_, &len, bssid->IELength - ie_offset);
+
+ bssid->Configuration.DSConfig = 0;
+ bssid->Configuration.Length = 0;
+
+ if (p) {
+ bssid->Configuration.DSConfig = *(p + 2);
+ } else {/* In 5G, some ap do not have DSSET IE */
+ /* checking HT info for channel */
+ p = rtw_get_ie(bssid->IEs + ie_offset, _HT_ADD_INFO_IE_, &len, bssid->IELength - ie_offset);
+ if (p) {
+ struct HT_info_element *HT_info = (struct HT_info_element *)(p + 2);
+ bssid->Configuration.DSConfig = HT_info->primary_channel;
+ } else { /* use current channel */
+ bssid->Configuration.DSConfig = rtw_get_oper_ch(padapter);
+ }
+ }
+
+ if (subtype == WIFI_PROBEREQ) {
+ /* FIXME */
+ bssid->InfrastructureMode = Ndis802_11Infrastructure;
+ memcpy(bssid->MacAddress, GetAddr2Ptr(pframe), ETH_ALEN);
+ bssid->Privacy = 1;
+ return _SUCCESS;
+ }
+
+ bssid->Configuration.BeaconPeriod =
+ get_unaligned_le16(rtw_get_beacon_interval_from_ie(bssid->IEs));
+
+ val16 = rtw_get_capability((struct wlan_bssid_ex *)bssid);
+
+ if (val16 & BIT(0)) {
+ bssid->InfrastructureMode = Ndis802_11Infrastructure;
+ memcpy(bssid->MacAddress, GetAddr2Ptr(pframe), ETH_ALEN);
+ } else {
+ bssid->InfrastructureMode = Ndis802_11IBSS;
+ memcpy(bssid->MacAddress, GetAddr3Ptr(pframe), ETH_ALEN);
+ }
+
+ if (val16 & BIT(4))
+ bssid->Privacy = 1;
+ else
+ bssid->Privacy = 0;
+
+ bssid->Configuration.ATIMWindow = 0;
+
+ /* 20/40 BSS Coexistence check */
+ if ((pregistrypriv->wifi_spec == 1) && (!pmlmeinfo->bwmode_updated)) {
+ struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
+ p = rtw_get_ie(bssid->IEs + ie_offset, _HT_CAPABILITY_IE_, &len, bssid->IELength - ie_offset);
+ if (p && len > 0) {
+ struct HT_caps_element *pHT_caps;
+ pHT_caps = (struct HT_caps_element *)(p + 2);
+
+ if (le16_to_cpu(pHT_caps->u.HT_cap_element.HT_caps_info)&BIT(14))
+ pmlmepriv->num_FortyMHzIntolerant++;
+ } else {
+ pmlmepriv->num_sta_no_ht++;
+ }
+ }
+
+ /* mark bss info receiving from nearby channel as SignalQuality 101 */
+ if (bssid->Configuration.DSConfig != rtw_get_oper_ch(padapter))
+ bssid->PhyInfo.SignalQuality = 101;
+ return _SUCCESS;
+}
+
+void start_create_ibss(struct adapter *padapter)
+{
+ unsigned short caps;
+ u8 val8;
+ u8 join_type;
+ struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv;
+ struct mlme_ext_info *pmlmeinfo = &(pmlmeext->mlmext_info);
+ struct wlan_bssid_ex *pnetwork = (struct wlan_bssid_ex *)(&(pmlmeinfo->network));
+ pmlmeext->cur_channel = (u8)pnetwork->Configuration.DSConfig;
+ pmlmeinfo->bcn_interval = get_beacon_interval(pnetwork);
+
+ /* update wireless mode */
+ update_wireless_mode(padapter);
+
+ /* update capability */
+ caps = rtw_get_capability((struct wlan_bssid_ex *)pnetwork);
+ update_capinfo(padapter, caps);
+ if (caps&cap_IBSS) {/* adhoc master */
+ val8 = 0xcf;
+ rtw_hal_set_hwreg(padapter, HW_VAR_SEC_CFG, (u8 *)(&val8));
+
+ /* switch channel */
+ /* SelectChannel(padapter, pmlmeext->cur_channel, HAL_PRIME_CHNL_OFFSET_DONT_CARE); */
+ set_channel_bwmode(padapter, pmlmeext->cur_channel, HAL_PRIME_CHNL_OFFSET_DONT_CARE, HT_CHANNEL_WIDTH_20);
+
+ beacon_timing_control(padapter);
+
+ /* set msr to WIFI_FW_ADHOC_STATE */
+ pmlmeinfo->state = WIFI_FW_ADHOC_STATE;
+ Set_MSR(padapter, (pmlmeinfo->state & 0x3));
+
+ /* issue beacon */
+ if (send_beacon(padapter) == _FAIL) {
+ RT_TRACE(_module_rtl871x_mlme_c_, _drv_err_, ("issuing beacon frame fail....\n"));
+
+ report_join_res(padapter, -1);
+ pmlmeinfo->state = WIFI_FW_NULL_STATE;
+ } else {
+ rtw_hal_set_hwreg(padapter, HW_VAR_BSSID, padapter->registrypriv.dev_network.MacAddress);
+ join_type = 0;
+ rtw_hal_set_hwreg(padapter, HW_VAR_MLME_JOIN, (u8 *)(&join_type));
+
+ report_join_res(padapter, 1);
+ pmlmeinfo->state |= WIFI_FW_ASSOC_SUCCESS;
+ }
+ } else {
+ DBG_88E("start_create_ibss, invalid cap:%x\n", caps);
+ return;
+ }
+}
+
+void start_clnt_join(struct adapter *padapter)
+{
+ unsigned short caps;
+ u8 val8;
+ struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv;
+ struct mlme_ext_info *pmlmeinfo = &(pmlmeext->mlmext_info);
+ struct wlan_bssid_ex *pnetwork = (struct wlan_bssid_ex *)(&(pmlmeinfo->network));
+ int beacon_timeout;
+
+ pmlmeext->cur_channel = (u8)pnetwork->Configuration.DSConfig;
+ pmlmeinfo->bcn_interval = get_beacon_interval(pnetwork);
+
+ /* update wireless mode */
+ update_wireless_mode(padapter);
+
+ /* update capability */
+ caps = rtw_get_capability((struct wlan_bssid_ex *)pnetwork);
+ update_capinfo(padapter, caps);
+ if (caps&cap_ESS) {
+ Set_MSR(padapter, WIFI_FW_STATION_STATE);
+
+ val8 = (pmlmeinfo->auth_algo == dot11AuthAlgrthm_8021X) ? 0xcc : 0xcf;
+
+ rtw_hal_set_hwreg(padapter, HW_VAR_SEC_CFG, (u8 *)(&val8));
+
+ /* switch channel */
+ set_channel_bwmode(padapter, pmlmeext->cur_channel, pmlmeext->cur_ch_offset, pmlmeext->cur_bwmode);
+
+ /* here wait for receiving the beacon to start auth */
+ /* and enable a timer */
+ beacon_timeout = decide_wait_for_beacon_timeout(pmlmeinfo->bcn_interval);
+ set_link_timer(pmlmeext, beacon_timeout);
+ mod_timer(&padapter->mlmepriv.assoc_timer, jiffies +
+ msecs_to_jiffies((REAUTH_TO * REAUTH_LIMIT) + (REASSOC_TO * REASSOC_LIMIT) + beacon_timeout));
+
+ pmlmeinfo->state = WIFI_FW_AUTH_NULL | WIFI_FW_STATION_STATE;
+ } else if (caps&cap_IBSS) { /* adhoc client */
+ Set_MSR(padapter, WIFI_FW_ADHOC_STATE);
+
+ val8 = 0xcf;
+ rtw_hal_set_hwreg(padapter, HW_VAR_SEC_CFG, (u8 *)(&val8));
+
+ /* switch channel */
+ set_channel_bwmode(padapter, pmlmeext->cur_channel, pmlmeext->cur_ch_offset, pmlmeext->cur_bwmode);
+
+ beacon_timing_control(padapter);
+
+ pmlmeinfo->state = WIFI_FW_ADHOC_STATE;
+
+ report_join_res(padapter, 1);
+ } else {
+ return;
+ }
+}
+
+void start_clnt_auth(struct adapter *padapter)
+{
+ struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv;
+ struct mlme_ext_info *pmlmeinfo = &(pmlmeext->mlmext_info);
+
+ del_timer_sync(&pmlmeext->link_timer);
+
+ pmlmeinfo->state &= (~WIFI_FW_AUTH_NULL);
+ pmlmeinfo->state |= WIFI_FW_AUTH_STATE;
+
+ pmlmeinfo->auth_seq = 1;
+ pmlmeinfo->reauth_count = 0;
+ pmlmeinfo->reassoc_count = 0;
+ pmlmeinfo->link_count = 0;
+ pmlmeext->retry = 0;
+
+
+ /* Because of AP's not receiving deauth before */
+ /* AP may: 1)not response auth or 2)deauth us after link is complete */
+ /* issue deauth before issuing auth to deal with the situation */
+ /* Commented by Albert 2012/07/21 */
+ /* For the Win8 P2P connection, it will be hard to have a successful connection if this Wi-Fi doesn't connect to it. */
+ issue_deauth(padapter, (&(pmlmeinfo->network))->MacAddress, WLAN_REASON_DEAUTH_LEAVING);
+
+ DBG_88E_LEVEL(_drv_info_, "start auth\n");
+ issue_auth(padapter, NULL, 0);
+
+ set_link_timer(pmlmeext, REAUTH_TO);
+}
+
+
+void start_clnt_assoc(struct adapter *padapter)
+{
+ struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv;
+ struct mlme_ext_info *pmlmeinfo = &(pmlmeext->mlmext_info);
+
+ del_timer_sync(&pmlmeext->link_timer);
+
+ pmlmeinfo->state &= (~(WIFI_FW_AUTH_NULL | WIFI_FW_AUTH_STATE));
+ pmlmeinfo->state |= (WIFI_FW_AUTH_SUCCESS | WIFI_FW_ASSOC_STATE);
+
+ issue_assocreq(padapter);
+
+ set_link_timer(pmlmeext, REASSOC_TO);
+}
+
+unsigned int receive_disconnect(struct adapter *padapter, unsigned char *MacAddr, unsigned short reason)
+{
+ struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv;
+ struct mlme_ext_info *pmlmeinfo = &(pmlmeext->mlmext_info);
+ struct wlan_bssid_ex *pnetwork = &(pmlmeinfo->network);
+
+ /* check A3 */
+ if (memcmp(MacAddr, pnetwork->MacAddress, ETH_ALEN))
+ return _SUCCESS;
+
+ DBG_88E("%s\n", __func__);
+
+ if ((pmlmeinfo->state&0x03) == WIFI_FW_STATION_STATE) {
+ if (pmlmeinfo->state & WIFI_FW_ASSOC_SUCCESS) {
+ pmlmeinfo->state = WIFI_FW_NULL_STATE;
+ report_del_sta_event(padapter, MacAddr, reason);
+ } else if (pmlmeinfo->state & WIFI_FW_LINKING_STATE) {
+ pmlmeinfo->state = WIFI_FW_NULL_STATE;
+ report_join_res(padapter, -2);
+ }
+ }
+ return _SUCCESS;
+}
+
+static void process_80211d(struct adapter *padapter, struct wlan_bssid_ex *bssid)
+{
+ struct registry_priv *pregistrypriv;
+ struct mlme_ext_priv *pmlmeext;
+ struct rt_channel_info *chplan_new;
+ u8 channel;
+ u8 i;
+
+ pregistrypriv = &padapter->registrypriv;
+ pmlmeext = &padapter->mlmeextpriv;
+
+ /* Adjust channel plan by AP Country IE */
+ if (pregistrypriv->enable80211d &&
+ (!pmlmeext->update_channel_plan_by_ap_done)) {
+ u8 *ie, *p;
+ u32 len;
+ struct rt_channel_plan chplan_ap;
+ struct rt_channel_info chplan_sta[MAX_CHANNEL_NUM];
+ u8 country[4];
+ u8 fcn; /* first channel number */
+ u8 noc; /* number of channel */
+ u8 j, k;
+
+ ie = rtw_get_ie(bssid->IEs + _FIXED_IE_LENGTH_, _COUNTRY_IE_, &len, bssid->IELength - _FIXED_IE_LENGTH_);
+ if (!ie)
+ return;
+ if (len < 6)
+ return;
+ ie += 2;
+ p = ie;
+ ie += len;
+
+ memset(country, 0, 4);
+ memcpy(country, p, 3);
+ p += 3;
+ RT_TRACE(_module_rtl871x_mlme_c_, _drv_notice_,
+ ("%s: 802.11d country =%s\n", __func__, country));
+
+ i = 0;
+ while ((ie - p) >= 3) {
+ fcn = *(p++);
+ noc = *(p++);
+ p++;
+
+ for (j = 0; j < noc; j++) {
+ if (fcn <= 14)
+ channel = fcn + j; /* 2.4 GHz */
+ else
+ channel = fcn + j*4; /* 5 GHz */
+
+ chplan_ap.Channel[i++] = channel;
+ }
+ }
+ chplan_ap.Len = i;
+
+ memcpy(chplan_sta, pmlmeext->channel_set, sizeof(chplan_sta));
+
+ memset(pmlmeext->channel_set, 0, sizeof(pmlmeext->channel_set));
+ chplan_new = pmlmeext->channel_set;
+
+ i = 0;
+ j = 0;
+ k = 0;
+ if (pregistrypriv->wireless_mode & WIRELESS_11G) {
+ do {
+ if ((i == MAX_CHANNEL_NUM) ||
+ (chplan_sta[i].ChannelNum == 0) ||
+ (chplan_sta[i].ChannelNum > 14))
+ break;
+
+ if ((j == chplan_ap.Len) || (chplan_ap.Channel[j] > 14))
+ break;
+
+ if (chplan_sta[i].ChannelNum == chplan_ap.Channel[j]) {
+ chplan_new[k].ChannelNum = chplan_ap.Channel[j];
+ chplan_new[k].ScanType = SCAN_ACTIVE;
+ i++;
+ j++;
+ k++;
+ } else if (chplan_sta[i].ChannelNum < chplan_ap.Channel[j]) {
+ chplan_new[k].ChannelNum = chplan_sta[i].ChannelNum;
+ chplan_new[k].ScanType = SCAN_PASSIVE;
+ i++;
+ k++;
+ } else if (chplan_sta[i].ChannelNum > chplan_ap.Channel[j]) {
+ chplan_new[k].ChannelNum = chplan_ap.Channel[j];
+ chplan_new[k].ScanType = SCAN_ACTIVE;
+ j++;
+ k++;
+ }
+ } while (1);
+
+ /* change AP not support channel to Passive scan */
+ while ((i < MAX_CHANNEL_NUM) &&
+ (chplan_sta[i].ChannelNum != 0) &&
+ (chplan_sta[i].ChannelNum <= 14)) {
+ chplan_new[k].ChannelNum = chplan_sta[i].ChannelNum;
+ chplan_new[k].ScanType = SCAN_PASSIVE;
+ i++;
+ k++;
+ }
+
+ /* add channel AP supported */
+ while ((j < chplan_ap.Len) && (chplan_ap.Channel[j] <= 14)) {
+ chplan_new[k].ChannelNum = chplan_ap.Channel[j];
+ chplan_new[k].ScanType = SCAN_ACTIVE;
+ j++;
+ k++;
+ }
+ } else {
+ /* keep original STA 2.4G channel plan */
+ while ((i < MAX_CHANNEL_NUM) &&
+ (chplan_sta[i].ChannelNum != 0) &&
+ (chplan_sta[i].ChannelNum <= 14)) {
+ chplan_new[k].ChannelNum = chplan_sta[i].ChannelNum;
+ chplan_new[k].ScanType = chplan_sta[i].ScanType;
+ i++;
+ k++;
+ }
+
+ /* skip AP 2.4G channel plan */
+ while ((j < chplan_ap.Len) && (chplan_ap.Channel[j] <= 14))
+ j++;
+ }
+
+ /* keep original STA 5G channel plan */
+ while ((i < MAX_CHANNEL_NUM) && (chplan_sta[i].ChannelNum != 0)) {
+ chplan_new[k].ChannelNum = chplan_sta[i].ChannelNum;
+ chplan_new[k].ScanType = chplan_sta[i].ScanType;
+ i++;
+ k++;
+ }
+
+ pmlmeext->update_channel_plan_by_ap_done = 1;
+ }
+
+ /* If channel is used by AP, set channel scan type to active */
+ channel = bssid->Configuration.DSConfig;
+ chplan_new = pmlmeext->channel_set;
+ i = 0;
+ while ((i < MAX_CHANNEL_NUM) && (chplan_new[i].ChannelNum != 0)) {
+ if (chplan_new[i].ChannelNum == channel) {
+ if (chplan_new[i].ScanType == SCAN_PASSIVE) {
+ chplan_new[i].ScanType = SCAN_ACTIVE;
+ RT_TRACE(_module_rtl871x_mlme_c_, _drv_notice_,
+ ("%s: change channel %d scan type from passive to active\n",
+ __func__, channel));
+ }
+ break;
+ }
+ i++;
+ }
+}
+
+/****************************************************************************
+
+Following are the functions to report events
+
+*****************************************************************************/
+
+void report_survey_event(struct adapter *padapter,
+ struct recv_frame *precv_frame)
+{
+ struct cmd_obj *pcmd_obj;
+ u8 *pevtcmd;
+ u32 cmdsz;
+ struct survey_event *psurvey_evt;
+ struct C2HEvent_Header *pc2h_evt_hdr;
+ struct mlme_ext_priv *pmlmeext;
+ struct cmd_priv *pcmdpriv;
+
+ if (!padapter)
+ return;
+
+ pmlmeext = &padapter->mlmeextpriv;
+ pcmdpriv = &padapter->cmdpriv;
+
+
+ pcmd_obj = kzalloc(sizeof(struct cmd_obj), GFP_ATOMIC);
+ if (pcmd_obj == NULL)
+ return;
+
+ cmdsz = sizeof(struct survey_event) + sizeof(struct C2HEvent_Header);
+ pevtcmd = kzalloc(cmdsz, GFP_ATOMIC);
+ if (pevtcmd == NULL) {
+ kfree(pcmd_obj);
+ return;
+ }
+
+ INIT_LIST_HEAD(&pcmd_obj->list);
+
+ pcmd_obj->cmdcode = GEN_CMD_CODE(_Set_MLME_EVT);
+ pcmd_obj->cmdsz = cmdsz;
+ pcmd_obj->parmbuf = pevtcmd;
+
+ pcmd_obj->rsp = NULL;
+ pcmd_obj->rspsz = 0;
+
+ pc2h_evt_hdr = (struct C2HEvent_Header *)(pevtcmd);
+ pc2h_evt_hdr->len = sizeof(struct survey_event);
+ pc2h_evt_hdr->ID = GEN_EVT_CODE(_Survey);
+ pc2h_evt_hdr->seq = atomic_inc_return(&pmlmeext->event_seq);
+
+ psurvey_evt = (struct survey_event *)(pevtcmd + sizeof(struct C2HEvent_Header));
+
+ if (collect_bss_info(padapter, precv_frame, (struct wlan_bssid_ex *)&psurvey_evt->bss) == _FAIL) {
+ kfree(pcmd_obj);
+ kfree(pevtcmd);
+ return;
+ }
+
+ process_80211d(padapter, &psurvey_evt->bss);
+
+ rtw_enqueue_cmd(pcmdpriv, pcmd_obj);
+
+ pmlmeext->sitesurvey_res.bss_cnt++;
+
+ return;
+}
+
+void report_surveydone_event(struct adapter *padapter)
+{
+ struct cmd_obj *pcmd_obj;
+ u8 *pevtcmd;
+ u32 cmdsz;
+ struct surveydone_event *psurveydone_evt;
+ struct C2HEvent_Header *pc2h_evt_hdr;
+ struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv;
+ struct cmd_priv *pcmdpriv = &padapter->cmdpriv;
+
+ pcmd_obj = kzalloc(sizeof(struct cmd_obj), GFP_KERNEL);
+ if (pcmd_obj == NULL)
+ return;
+
+ cmdsz = sizeof(struct surveydone_event) + sizeof(struct C2HEvent_Header);
+ pevtcmd = kzalloc(cmdsz, GFP_KERNEL);
+ if (pevtcmd == NULL) {
+ kfree(pcmd_obj);
+ return;
+ }
+
+ INIT_LIST_HEAD(&pcmd_obj->list);
+
+ pcmd_obj->cmdcode = GEN_CMD_CODE(_Set_MLME_EVT);
+ pcmd_obj->cmdsz = cmdsz;
+ pcmd_obj->parmbuf = pevtcmd;
+
+ pcmd_obj->rsp = NULL;
+ pcmd_obj->rspsz = 0;
+
+ pc2h_evt_hdr = (struct C2HEvent_Header *)(pevtcmd);
+ pc2h_evt_hdr->len = sizeof(struct surveydone_event);
+ pc2h_evt_hdr->ID = GEN_EVT_CODE(_SurveyDone);
+ pc2h_evt_hdr->seq = atomic_inc_return(&pmlmeext->event_seq);
+
+ psurveydone_evt = (struct surveydone_event *)(pevtcmd + sizeof(struct C2HEvent_Header));
+ psurveydone_evt->bss_cnt = pmlmeext->sitesurvey_res.bss_cnt;
+
+ DBG_88E("survey done event(%x)\n", psurveydone_evt->bss_cnt);
+
+ rtw_enqueue_cmd(pcmdpriv, pcmd_obj);
+
+ return;
+}
+
+void report_join_res(struct adapter *padapter, int res)
+{
+ struct cmd_obj *pcmd_obj;
+ u8 *pevtcmd;
+ u32 cmdsz;
+ struct joinbss_event *pjoinbss_evt;
+ struct C2HEvent_Header *pc2h_evt_hdr;
+ struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv;
+ struct mlme_ext_info *pmlmeinfo = &(pmlmeext->mlmext_info);
+ struct cmd_priv *pcmdpriv = &padapter->cmdpriv;
+
+ pcmd_obj = kzalloc(sizeof(struct cmd_obj), GFP_ATOMIC);
+ if (pcmd_obj == NULL)
+ return;
+
+ cmdsz = sizeof(struct joinbss_event) + sizeof(struct C2HEvent_Header);
+ pevtcmd = kzalloc(cmdsz, GFP_ATOMIC);
+ if (pevtcmd == NULL) {
+ kfree(pcmd_obj);
+ return;
+ }
+
+ INIT_LIST_HEAD(&pcmd_obj->list);
+
+ pcmd_obj->cmdcode = GEN_CMD_CODE(_Set_MLME_EVT);
+ pcmd_obj->cmdsz = cmdsz;
+ pcmd_obj->parmbuf = pevtcmd;
+
+ pcmd_obj->rsp = NULL;
+ pcmd_obj->rspsz = 0;
+
+ pc2h_evt_hdr = (struct C2HEvent_Header *)(pevtcmd);
+ pc2h_evt_hdr->len = sizeof(struct joinbss_event);
+ pc2h_evt_hdr->ID = GEN_EVT_CODE(_JoinBss);
+ pc2h_evt_hdr->seq = atomic_inc_return(&pmlmeext->event_seq);
+
+ pjoinbss_evt = (struct joinbss_event *)(pevtcmd + sizeof(struct C2HEvent_Header));
+ memcpy((unsigned char *)(&(pjoinbss_evt->network.network)), &(pmlmeinfo->network), sizeof(struct wlan_bssid_ex));
+ pjoinbss_evt->network.join_res = res;
+ pjoinbss_evt->network.aid = res;
+
+ DBG_88E("report_join_res(%d)\n", res);
+
+
+ rtw_joinbss_event_prehandle(padapter, (u8 *)&pjoinbss_evt->network);
+
+
+ rtw_enqueue_cmd(pcmdpriv, pcmd_obj);
+
+ return;
+}
+
+void report_del_sta_event(struct adapter *padapter, unsigned char *MacAddr, unsigned short reason)
+{
+ struct cmd_obj *pcmd_obj;
+ u8 *pevtcmd;
+ u32 cmdsz;
+ struct sta_info *psta;
+ int mac_id;
+ struct stadel_event *pdel_sta_evt;
+ struct C2HEvent_Header *pc2h_evt_hdr;
+ struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv;
+ struct cmd_priv *pcmdpriv = &padapter->cmdpriv;
+
+ pcmd_obj = kzalloc(sizeof(struct cmd_obj), GFP_KERNEL);
+ if (pcmd_obj == NULL)
+ return;
+
+ cmdsz = sizeof(struct stadel_event) + sizeof(struct C2HEvent_Header);
+ pevtcmd = kzalloc(cmdsz, GFP_KERNEL);
+ if (pevtcmd == NULL) {
+ kfree(pcmd_obj);
+ return;
+ }
+
+ INIT_LIST_HEAD(&pcmd_obj->list);
+
+ pcmd_obj->cmdcode = GEN_CMD_CODE(_Set_MLME_EVT);
+ pcmd_obj->cmdsz = cmdsz;
+ pcmd_obj->parmbuf = pevtcmd;
+
+ pcmd_obj->rsp = NULL;
+ pcmd_obj->rspsz = 0;
+
+ pc2h_evt_hdr = (struct C2HEvent_Header *)(pevtcmd);
+ pc2h_evt_hdr->len = sizeof(struct stadel_event);
+ pc2h_evt_hdr->ID = GEN_EVT_CODE(_DelSTA);
+ pc2h_evt_hdr->seq = atomic_inc_return(&pmlmeext->event_seq);
+
+ pdel_sta_evt = (struct stadel_event *)(pevtcmd + sizeof(struct C2HEvent_Header));
+ memcpy((unsigned char *)(&(pdel_sta_evt->macaddr)), MacAddr, ETH_ALEN);
+ memcpy((unsigned char *)(pdel_sta_evt->rsvd), (unsigned char *)(&reason), 2);
+
+
+ psta = rtw_get_stainfo(&padapter->stapriv, MacAddr);
+ if (psta)
+ mac_id = (int)psta->mac_id;
+ else
+ mac_id = -1;
+
+ pdel_sta_evt->mac_id = mac_id;
+
+ DBG_88E("report_del_sta_event: delete STA, mac_id =%d\n", mac_id);
+
+ rtw_enqueue_cmd(pcmdpriv, pcmd_obj);
+
+ return;
+}
+
+void report_add_sta_event(struct adapter *padapter, unsigned char *MacAddr, int cam_idx)
+{
+ struct cmd_obj *pcmd_obj;
+ u8 *pevtcmd;
+ u32 cmdsz;
+ struct stassoc_event *padd_sta_evt;
+ struct C2HEvent_Header *pc2h_evt_hdr;
+ struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv;
+ struct cmd_priv *pcmdpriv = &padapter->cmdpriv;
+
+ pcmd_obj = kzalloc(sizeof(struct cmd_obj), GFP_KERNEL);
+ if (pcmd_obj == NULL)
+ return;
+
+ cmdsz = sizeof(struct stassoc_event) + sizeof(struct C2HEvent_Header);
+ pevtcmd = kzalloc(cmdsz, GFP_KERNEL);
+ if (pevtcmd == NULL) {
+ kfree(pcmd_obj);
+ return;
+ }
+
+ INIT_LIST_HEAD(&pcmd_obj->list);
+
+ pcmd_obj->cmdcode = GEN_CMD_CODE(_Set_MLME_EVT);
+ pcmd_obj->cmdsz = cmdsz;
+ pcmd_obj->parmbuf = pevtcmd;
+
+ pcmd_obj->rsp = NULL;
+ pcmd_obj->rspsz = 0;
+
+ pc2h_evt_hdr = (struct C2HEvent_Header *)(pevtcmd);
+ pc2h_evt_hdr->len = sizeof(struct stassoc_event);
+ pc2h_evt_hdr->ID = GEN_EVT_CODE(_AddSTA);
+ pc2h_evt_hdr->seq = atomic_inc_return(&pmlmeext->event_seq);
+
+ padd_sta_evt = (struct stassoc_event *)(pevtcmd + sizeof(struct C2HEvent_Header));
+ memcpy((unsigned char *)(&(padd_sta_evt->macaddr)), MacAddr, ETH_ALEN);
+ padd_sta_evt->cam_id = cam_idx;
+
+ DBG_88E("report_add_sta_event: add STA\n");
+
+ rtw_enqueue_cmd(pcmdpriv, pcmd_obj);
+
+ return;
+}
+
+
+/****************************************************************************
+
+Following are the event callback functions
+
+*****************************************************************************/
+
+/* for sta/adhoc mode */
+void update_sta_info(struct adapter *padapter, struct sta_info *psta)
+{
+ struct mlme_priv *pmlmepriv = &(padapter->mlmepriv);
+ struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv;
+ struct mlme_ext_info *pmlmeinfo = &(pmlmeext->mlmext_info);
+
+ /* ERP */
+ VCS_update(padapter, psta);
+
+ /* HT */
+ if (pmlmepriv->htpriv.ht_option) {
+ psta->htpriv.ht_option = true;
+
+ psta->htpriv.ampdu_enable = pmlmepriv->htpriv.ampdu_enable;
+
+ if (support_short_GI(padapter, &(pmlmeinfo->HT_caps)))
+ psta->htpriv.sgi = true;
+
+ psta->qos_option = true;
+ } else {
+ psta->htpriv.ht_option = false;
+
+ psta->htpriv.ampdu_enable = false;
+
+ psta->htpriv.sgi = false;
+ psta->qos_option = false;
+ }
+ psta->htpriv.bwmode = pmlmeext->cur_bwmode;
+ psta->htpriv.ch_offset = pmlmeext->cur_ch_offset;
+
+ psta->htpriv.agg_enable_bitmap = 0x0;/* reset */
+ psta->htpriv.candidate_tid_bitmap = 0x0;/* reset */
+
+ /* QoS */
+ if (pmlmepriv->qospriv.qos_option)
+ psta->qos_option = true;
+
+
+ psta->state = _FW_LINKED;
+}
+
+void mlmeext_joinbss_event_callback(struct adapter *padapter, int join_res)
+{
+ struct sta_info *psta, *psta_bmc;
+ struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv;
+ struct mlme_ext_info *pmlmeinfo = &(pmlmeext->mlmext_info);
+ struct wlan_bssid_ex *cur_network = &(pmlmeinfo->network);
+ struct sta_priv *pstapriv = &padapter->stapriv;
+ u8 join_type;
+ u16 media_status;
+
+ if (join_res < 0) {
+ join_type = 1;
+ rtw_hal_set_hwreg(padapter, HW_VAR_MLME_JOIN, (u8 *)(&join_type));
+ rtw_hal_set_hwreg(padapter, HW_VAR_BSSID, null_addr);
+
+ /* restore to initial setting. */
+ update_tx_basic_rate(padapter, padapter->registrypriv.wireless_mode);
+
+ goto exit_mlmeext_joinbss_event_callback;
+ }
+
+ if ((pmlmeinfo->state&0x03) == WIFI_FW_ADHOC_STATE) {
+ /* for bc/mc */
+ psta_bmc = rtw_get_bcmc_stainfo(padapter);
+ if (psta_bmc) {
+ pmlmeinfo->FW_sta_info[psta_bmc->mac_id].psta = psta_bmc;
+ update_bmc_sta_support_rate(padapter, psta_bmc->mac_id);
+ Update_RA_Entry(padapter, psta_bmc->mac_id);
+ }
+ }
+
+
+ /* turn on dynamic functions */
+ Switch_DM_Func(padapter, DYNAMIC_ALL_FUNC_ENABLE, true);
+
+ /* update IOT-related issue */
+ update_IOT_info(padapter);
+
+ rtw_hal_set_hwreg(padapter, HW_VAR_BASIC_RATE, cur_network->SupportedRates);
+
+ /* BCN interval */
+ rtw_hal_set_hwreg(padapter, HW_VAR_BEACON_INTERVAL, (u8 *)(&pmlmeinfo->bcn_interval));
+
+ /* update capability */
+ update_capinfo(padapter, pmlmeinfo->capability);
+
+ /* WMM, Update EDCA param */
+ WMMOnAssocRsp(padapter);
+
+ /* HT */
+ HTOnAssocRsp(padapter);
+
+ set_channel_bwmode(padapter, pmlmeext->cur_channel, pmlmeext->cur_ch_offset, pmlmeext->cur_bwmode);
+
+ psta = rtw_get_stainfo(pstapriv, cur_network->MacAddress);
+ if (psta) { /* only for infra. mode */
+ pmlmeinfo->FW_sta_info[psta->mac_id].psta = psta;
+
+ psta->wireless_mode = pmlmeext->cur_wireless_mode;
+
+ /* set per sta rate after updating HT cap. */
+ set_sta_rate(padapter, psta);
+ rtw_hal_set_hwreg(padapter, HW_VAR_TX_RPT_MAX_MACID, (u8 *)&psta->mac_id);
+ media_status = (psta->mac_id<<8)|1; /* MACID|OPMODE: 1 means connect */
+ rtw_hal_set_hwreg(padapter, HW_VAR_H2C_MEDIA_STATUS_RPT, (u8 *)&media_status);
+ }
+
+ join_type = 2;
+ rtw_hal_set_hwreg(padapter, HW_VAR_MLME_JOIN, (u8 *)(&join_type));
+
+ if ((pmlmeinfo->state&0x03) == WIFI_FW_STATION_STATE) {
+ /* correcting TSF */
+ correct_TSF(padapter, pmlmeext);
+ }
+ rtw_lps_ctrl_wk_cmd(padapter, LPS_CTRL_CONNECT, 0);
+
+exit_mlmeext_joinbss_event_callback:
+
+ DBG_88E("=>%s\n", __func__);
+}
+
+void mlmeext_sta_add_event_callback(struct adapter *padapter, struct sta_info *psta)
+{
+ struct mlme_ext_priv *pmlmeext = &(padapter->mlmeextpriv);
+ struct mlme_ext_info *pmlmeinfo = &(pmlmeext->mlmext_info);
+ u8 join_type;
+
+ DBG_88E("%s\n", __func__);
+
+ if ((pmlmeinfo->state&0x03) == WIFI_FW_ADHOC_STATE) {
+ if (pmlmeinfo->state & WIFI_FW_ASSOC_SUCCESS) {/* adhoc master or sta_count>1 */
+ /* nothing to do */
+ } else { /* adhoc client */
+ /* correcting TSF */
+ correct_TSF(padapter, pmlmeext);
+
+ /* start beacon */
+ if (send_beacon(padapter) == _FAIL) {
+ pmlmeinfo->FW_sta_info[psta->mac_id].status = 0;
+ pmlmeinfo->state ^= WIFI_FW_ADHOC_STATE;
+ return;
+ }
+ pmlmeinfo->state |= WIFI_FW_ASSOC_SUCCESS;
+ }
+
+ join_type = 2;
+ rtw_hal_set_hwreg(padapter, HW_VAR_MLME_JOIN, (u8 *)(&join_type));
+ }
+
+ pmlmeinfo->FW_sta_info[psta->mac_id].psta = psta;
+
+ /* rate radaptive */
+ Update_RA_Entry(padapter, psta->mac_id);
+
+ /* update adhoc sta_info */
+ update_sta_info(padapter, psta);
+}
+
+void mlmeext_sta_del_event_callback(struct adapter *padapter)
+{
+ struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv;
+ struct mlme_ext_info *pmlmeinfo = &(pmlmeext->mlmext_info);
+
+ if (is_client_associated_to_ap(padapter) || is_IBSS_empty(padapter)) {
+ rtw_hal_set_hwreg(padapter, HW_VAR_MLME_DISCONNECT, NULL);
+ rtw_hal_set_hwreg(padapter, HW_VAR_BSSID, null_addr);
+
+ /* restore to initial setting. */
+ update_tx_basic_rate(padapter, padapter->registrypriv.wireless_mode);
+
+ /* switch to the 20M Hz mode after disconnect */
+ pmlmeext->cur_bwmode = HT_CHANNEL_WIDTH_20;
+ pmlmeext->cur_ch_offset = HAL_PRIME_CHNL_OFFSET_DONT_CARE;
+
+ /* SelectChannel(padapter, pmlmeext->cur_channel, pmlmeext->cur_ch_offset); */
+ set_channel_bwmode(padapter, pmlmeext->cur_channel, pmlmeext->cur_ch_offset, pmlmeext->cur_bwmode);
+
+
+ flush_all_cam_entry(padapter);
+
+ pmlmeinfo->state = WIFI_FW_NULL_STATE;
+
+ /* set MSR to no link state -> infra. mode */
+ Set_MSR(padapter, _HW_STATE_STATION_);
+
+ del_timer_sync(&pmlmeext->link_timer);
+ }
+}
+
+/****************************************************************************
+
+Following are the functions for the timer handlers
+
+*****************************************************************************/
+void _linked_rx_signal_strehgth_display(struct adapter *padapter);
+void _linked_rx_signal_strehgth_display(struct adapter *padapter)
+{
+ struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv;
+ struct mlme_ext_info *pmlmeinfo = &(pmlmeext->mlmext_info);
+ u8 mac_id;
+ int UndecoratedSmoothedPWDB;
+ if ((pmlmeinfo->state&0x03) == WIFI_FW_STATION_STATE)
+ mac_id = 0;
+ else if ((pmlmeinfo->state&0x03) == _HW_STATE_AP_)
+ mac_id = 2;
+
+ rtw_hal_get_def_var(padapter, HW_DEF_RA_INFO_DUMP, &mac_id);
+
+ rtw_hal_get_def_var(padapter, HAL_DEF_UNDERCORATEDSMOOTHEDPWDB, &UndecoratedSmoothedPWDB);
+ DBG_88E("UndecoratedSmoothedPWDB:%d\n", UndecoratedSmoothedPWDB);
+}
+
+static u8 chk_ap_is_alive(struct adapter *padapter, struct sta_info *psta)
+{
+ u8 ret = false;
+
+ if ((sta_rx_data_pkts(psta) == sta_last_rx_data_pkts(psta)) &&
+ sta_rx_beacon_pkts(psta) == sta_last_rx_beacon_pkts(psta) &&
+ sta_rx_probersp_pkts(psta) == sta_last_rx_probersp_pkts(psta))
+ ret = false;
+ else
+ ret = true;
+
+ sta_update_last_rx_pkts(psta);
+
+ return ret;
+}
+
+void linked_status_chk(struct adapter *padapter)
+{
+ u32 i;
+ struct sta_info *psta;
+ struct xmit_priv *pxmitpriv = &(padapter->xmitpriv);
+ struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv;
+ struct mlme_ext_info *pmlmeinfo = &(pmlmeext->mlmext_info);
+ struct sta_priv *pstapriv = &padapter->stapriv;
+
+ if (padapter->bRxRSSIDisplay)
+ _linked_rx_signal_strehgth_display(padapter);
+
+ if (is_client_associated_to_ap(padapter)) {
+ /* linked infrastructure client mode */
+
+ int tx_chk = _SUCCESS, rx_chk = _SUCCESS;
+ int rx_chk_limit;
+
+ rx_chk_limit = 4;
+ psta = rtw_get_stainfo(pstapriv, pmlmeinfo->network.MacAddress);
+ if (psta != NULL) {
+ bool is_p2p_enable = false;
+
+ if (!chk_ap_is_alive(padapter, psta))
+ rx_chk = _FAIL;
+
+ if (pxmitpriv->last_tx_pkts == pxmitpriv->tx_pkts)
+ tx_chk = _FAIL;
+
+ if (pmlmeext->active_keep_alive_check && (rx_chk == _FAIL || tx_chk == _FAIL)) {
+ u8 backup_oper_channel = 0;
+
+ /* switch to correct channel of current network before issue keep-alive frames */
+ if (rtw_get_oper_ch(padapter) != pmlmeext->cur_channel) {
+ backup_oper_channel = rtw_get_oper_ch(padapter);
+ SelectChannel(padapter, pmlmeext->cur_channel);
+ }
+
+ if (rx_chk != _SUCCESS)
+ issue_probereq_ex(padapter, &pmlmeinfo->network.Ssid, psta->hwaddr, 3, 1);
+
+ if ((tx_chk != _SUCCESS && pmlmeinfo->link_count++ == 0xf) || rx_chk != _SUCCESS) {
+ tx_chk = issue_nulldata(padapter, psta->hwaddr, 0, 3, 1);
+ /* if tx acked and p2p disabled, set rx_chk _SUCCESS to reset retry count */
+ if (tx_chk == _SUCCESS && !is_p2p_enable)
+ rx_chk = _SUCCESS;
+ }
+
+ /* back to the original operation channel */
+ if (backup_oper_channel > 0)
+ SelectChannel(padapter, backup_oper_channel);
+ } else {
+ if (rx_chk != _SUCCESS) {
+ if (pmlmeext->retry == 0) {
+ issue_probereq(padapter, &pmlmeinfo->network.Ssid, pmlmeinfo->network.MacAddress);
+ issue_probereq(padapter, &pmlmeinfo->network.Ssid, pmlmeinfo->network.MacAddress);
+ issue_probereq(padapter, &pmlmeinfo->network.Ssid, pmlmeinfo->network.MacAddress);
+ }
+ }
+
+ if (tx_chk != _SUCCESS && pmlmeinfo->link_count++ == 0xf) {
+ tx_chk = issue_nulldata(padapter, NULL, 0, 1, 0);
+ }
+ }
+
+ if (rx_chk == _FAIL) {
+ pmlmeext->retry++;
+ if (pmlmeext->retry > rx_chk_limit) {
+ DBG_88E_LEVEL(_drv_always_, FUNC_ADPT_FMT" disconnect or roaming\n",
+ FUNC_ADPT_ARG(padapter));
+ receive_disconnect(padapter, pmlmeinfo->network.MacAddress,
+ WLAN_REASON_EXPIRATION_CHK);
+ return;
+ }
+ } else {
+ pmlmeext->retry = 0;
+ }
+
+ if (tx_chk == _FAIL) {
+ pmlmeinfo->link_count &= 0xf;
+ } else {
+ pxmitpriv->last_tx_pkts = pxmitpriv->tx_pkts;
+ pmlmeinfo->link_count = 0;
+ }
+ } /* end of if ((psta = rtw_get_stainfo(pstapriv, passoc_res->network.MacAddress)) != NULL) */
+ } else if (is_client_associated_to_ibss(padapter)) {
+ /* linked IBSS mode */
+ /* for each assoc list entry to check the rx pkt counter */
+ for (i = IBSS_START_MAC_ID; i < NUM_STA; i++) {
+ if (pmlmeinfo->FW_sta_info[i].status == 1) {
+ psta = pmlmeinfo->FW_sta_info[i].psta;
+
+ if (NULL == psta)
+ continue;
+ if (pmlmeinfo->FW_sta_info[i].rx_pkt == sta_rx_pkts(psta)) {
+ if (pmlmeinfo->FW_sta_info[i].retry < 3) {
+ pmlmeinfo->FW_sta_info[i].retry++;
+ } else {
+ pmlmeinfo->FW_sta_info[i].retry = 0;
+ pmlmeinfo->FW_sta_info[i].status = 0;
+ report_del_sta_event(padapter, psta->hwaddr
+ , 65535/* indicate disconnect caused by no rx */
+ );
+ }
+ } else {
+ pmlmeinfo->FW_sta_info[i].retry = 0;
+ pmlmeinfo->FW_sta_info[i].rx_pkt = (u32)sta_rx_pkts(psta);
+ }
+ }
+ }
+ }
+}
+
+void survey_timer_hdl(unsigned long data)
+{
+ struct adapter *padapter = (struct adapter *)data;
+ struct cmd_obj *ph2c;
+ struct sitesurvey_parm *psurveyPara;
+ struct cmd_priv *pcmdpriv = &padapter->cmdpriv;
+ struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv;
+
+ /* issue rtw_sitesurvey_cmd */
+ if (pmlmeext->sitesurvey_res.state > SCAN_START) {
+ if (pmlmeext->sitesurvey_res.state == SCAN_PROCESS)
+ pmlmeext->sitesurvey_res.channel_idx++;
+
+ if (pmlmeext->scan_abort) {
+ pmlmeext->sitesurvey_res.channel_idx = pmlmeext->sitesurvey_res.ch_num;
+ DBG_88E("%s idx:%d\n", __func__
+ , pmlmeext->sitesurvey_res.channel_idx);
+
+ pmlmeext->scan_abort = false;/* reset */
+ }
+
+ ph2c = kzalloc(sizeof(struct cmd_obj), GFP_ATOMIC);
+ if (ph2c == NULL)
+ goto exit_survey_timer_hdl;
+
+ psurveyPara = kzalloc(sizeof(struct sitesurvey_parm), GFP_ATOMIC);
+ if (psurveyPara == NULL) {
+ kfree(ph2c);
+ goto exit_survey_timer_hdl;
+ }
+
+ init_h2fwcmd_w_parm_no_rsp(ph2c, psurveyPara, GEN_CMD_CODE(_SiteSurvey));
+ rtw_enqueue_cmd(pcmdpriv, ph2c);
+ }
+
+
+exit_survey_timer_hdl:
+ return;
+}
+
+void link_timer_hdl(unsigned long data)
+{
+ struct adapter *padapter = (struct adapter *)data;
+ struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv;
+ struct mlme_ext_info *pmlmeinfo = &(pmlmeext->mlmext_info);
+
+ if (pmlmeinfo->state & WIFI_FW_AUTH_NULL) {
+ DBG_88E("link_timer_hdl:no beacon while connecting\n");
+ pmlmeinfo->state = WIFI_FW_NULL_STATE;
+ report_join_res(padapter, -3);
+ } else if (pmlmeinfo->state & WIFI_FW_AUTH_STATE) {
+ /* re-auth timer */
+ if (++pmlmeinfo->reauth_count > REAUTH_LIMIT) {
+ pmlmeinfo->state = 0;
+ report_join_res(padapter, -1);
+ return;
+ }
+
+ DBG_88E("link_timer_hdl: auth timeout and try again\n");
+ pmlmeinfo->auth_seq = 1;
+ issue_auth(padapter, NULL, 0);
+ set_link_timer(pmlmeext, REAUTH_TO);
+ } else if (pmlmeinfo->state & WIFI_FW_ASSOC_STATE) {
+ /* re-assoc timer */
+ if (++pmlmeinfo->reassoc_count > REASSOC_LIMIT) {
+ pmlmeinfo->state = WIFI_FW_NULL_STATE;
+ report_join_res(padapter, -2);
+ return;
+ }
+
+ DBG_88E("link_timer_hdl: assoc timeout and try again\n");
+ issue_assocreq(padapter);
+ set_link_timer(pmlmeext, REASSOC_TO);
+ }
+ return;
+}
+
+void addba_timer_hdl(unsigned long data)
+{
+ struct sta_info *psta = (struct sta_info *)data;
+ struct ht_priv *phtpriv;
+
+ if (!psta)
+ return;
+
+ phtpriv = &psta->htpriv;
+
+ if ((phtpriv->ht_option) && (phtpriv->ampdu_enable)) {
+ if (phtpriv->candidate_tid_bitmap)
+ phtpriv->candidate_tid_bitmap = 0x0;
+ }
+}
+
+u8 setopmode_hdl(struct adapter *padapter, u8 *pbuf)
+{
+ u8 type;
+ struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv;
+ struct mlme_ext_info *pmlmeinfo = &(pmlmeext->mlmext_info);
+ struct setopmode_parm *psetop = (struct setopmode_parm *)pbuf;
+
+ if (psetop->mode == Ndis802_11APMode) {
+ pmlmeinfo->state = WIFI_FW_AP_STATE;
+ type = _HW_STATE_AP_;
+ } else if (psetop->mode == Ndis802_11Infrastructure) {
+ pmlmeinfo->state &= ~(BIT(0)|BIT(1));/* clear state */
+ pmlmeinfo->state |= WIFI_FW_STATION_STATE;/* set to STATION_STATE */
+ type = _HW_STATE_STATION_;
+ } else if (psetop->mode == Ndis802_11IBSS) {
+ type = _HW_STATE_ADHOC_;
+ } else {
+ type = _HW_STATE_NOLINK_;
+ }
+
+ rtw_hal_set_hwreg(padapter, HW_VAR_SET_OPMODE, (u8 *)(&type));
+ /* Set_NETYPE0_MSR(padapter, type); */
+
+ return H2C_SUCCESS;
+}
+
+u8 createbss_hdl(struct adapter *padapter, u8 *pbuf)
+{
+ struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv;
+ struct mlme_ext_info *pmlmeinfo = &(pmlmeext->mlmext_info);
+ struct wlan_bssid_ex *pnetwork = (struct wlan_bssid_ex *)(&(pmlmeinfo->network));
+ struct wlan_bssid_ex *pparm = (struct wlan_bssid_ex *)pbuf;
+ /* u32 initialgain; */
+
+
+ if (pparm->InfrastructureMode == Ndis802_11APMode) {
+#ifdef CONFIG_88EU_AP_MODE
+
+ if (pmlmeinfo->state == WIFI_FW_AP_STATE) {
+ /* todo: */
+ return H2C_SUCCESS;
+ }
+#endif
+ }
+
+ /* below is for ad-hoc master */
+ if (pparm->InfrastructureMode == Ndis802_11IBSS) {
+ rtw_joinbss_reset(padapter);
+
+ pmlmeext->cur_bwmode = HT_CHANNEL_WIDTH_20;
+ pmlmeext->cur_ch_offset = HAL_PRIME_CHNL_OFFSET_DONT_CARE;
+ pmlmeinfo->ERP_enable = 0;
+ pmlmeinfo->WMM_enable = 0;
+ pmlmeinfo->HT_enable = 0;
+ pmlmeinfo->HT_caps_enable = 0;
+ pmlmeinfo->HT_info_enable = 0;
+ pmlmeinfo->agg_enable_bitmap = 0;
+ pmlmeinfo->candidate_tid_bitmap = 0;
+
+ /* disable dynamic functions, such as high power, DIG */
+ Save_DM_Func_Flag(padapter);
+ Switch_DM_Func(padapter, DYNAMIC_FUNC_DISABLE, false);
+
+ /* config the initial gain under linking, need to write the BB registers */
+ /* initialgain = 0x1E; */
+ /* rtw_hal_set_hwreg(padapter, HW_VAR_INITIAL_GAIN, (u8 *)(&initialgain)); */
+
+ /* cancel link timer */
+ del_timer_sync(&pmlmeext->link_timer);
+
+ /* clear CAM */
+ flush_all_cam_entry(padapter);
+
+ memcpy(pnetwork, pbuf, FIELD_OFFSET(struct wlan_bssid_ex, IELength));
+ pnetwork->IELength = ((struct wlan_bssid_ex *)pbuf)->IELength;
+
+ if (pnetwork->IELength > MAX_IE_SZ)/* Check pbuf->IELength */
+ return H2C_PARAMETERS_ERROR;
+
+ memcpy(pnetwork->IEs, ((struct wlan_bssid_ex *)pbuf)->IEs, pnetwork->IELength);
+
+ start_create_ibss(padapter);
+ }
+
+ return H2C_SUCCESS;
+}
+
+u8 join_cmd_hdl(struct adapter *padapter, u8 *pbuf)
+{
+ u8 join_type;
+ struct ndis_802_11_var_ie *pIE;
+ struct registry_priv *pregpriv = &padapter->registrypriv;
+ struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv;
+ struct mlme_ext_info *pmlmeinfo = &(pmlmeext->mlmext_info);
+ struct wlan_bssid_ex *pnetwork = (struct wlan_bssid_ex *)(&(pmlmeinfo->network));
+ struct wlan_bssid_ex *pparm = (struct wlan_bssid_ex *)pbuf;
+ u32 i;
+
+ /* check already connecting to AP or not */
+ if (pmlmeinfo->state & WIFI_FW_ASSOC_SUCCESS) {
+ if (pmlmeinfo->state & WIFI_FW_STATION_STATE)
+ issue_deauth_ex(padapter, pnetwork->MacAddress, WLAN_REASON_DEAUTH_LEAVING, 5, 100);
+
+ pmlmeinfo->state = WIFI_FW_NULL_STATE;
+
+ /* clear CAM */
+ flush_all_cam_entry(padapter);
+
+ del_timer_sync(&pmlmeext->link_timer);
+
+ /* set MSR to nolink -> infra. mode */
+ Set_MSR(padapter, _HW_STATE_STATION_);
+
+
+ rtw_hal_set_hwreg(padapter, HW_VAR_MLME_DISCONNECT, NULL);
+ }
+
+ rtw_antenna_select_cmd(padapter, pparm->PhyInfo.Optimum_antenna, false);
+
+ rtw_joinbss_reset(padapter);
+
+ pmlmeext->cur_bwmode = HT_CHANNEL_WIDTH_20;
+ pmlmeext->cur_ch_offset = HAL_PRIME_CHNL_OFFSET_DONT_CARE;
+ pmlmeinfo->ERP_enable = 0;
+ pmlmeinfo->WMM_enable = 0;
+ pmlmeinfo->HT_enable = 0;
+ pmlmeinfo->HT_caps_enable = 0;
+ pmlmeinfo->HT_info_enable = 0;
+ pmlmeinfo->agg_enable_bitmap = 0;
+ pmlmeinfo->candidate_tid_bitmap = 0;
+ pmlmeinfo->bwmode_updated = false;
+
+ memcpy(pnetwork, pbuf, FIELD_OFFSET(struct wlan_bssid_ex, IELength));
+ pnetwork->IELength = ((struct wlan_bssid_ex *)pbuf)->IELength;
+
+ if (pnetwork->IELength > MAX_IE_SZ)/* Check pbuf->IELength */
+ return H2C_PARAMETERS_ERROR;
+
+ memcpy(pnetwork->IEs, ((struct wlan_bssid_ex *)pbuf)->IEs, pnetwork->IELength);
+
+ /* Check AP vendor to move rtw_joinbss_cmd() */
+
+ for (i = sizeof(struct ndis_802_11_fixed_ie); i < pnetwork->IELength;) {
+ pIE = (struct ndis_802_11_var_ie *)(pnetwork->IEs + i);
+
+ switch (pIE->ElementID) {
+ case _VENDOR_SPECIFIC_IE_:/* Get WMM IE. */
+ if (!memcmp(pIE->data, WMM_OUI, 4))
+ pmlmeinfo->WMM_enable = 1;
+ break;
+ case _HT_CAPABILITY_IE_: /* Get HT Cap IE. */
+ pmlmeinfo->HT_caps_enable = 1;
+ break;
+ case _HT_EXTRA_INFO_IE_: /* Get HT Info IE. */
+ pmlmeinfo->HT_info_enable = 1;
+
+ /* spec case only for cisco's ap because cisco's ap issue assoc rsp using mcs rate @40MHz or @20MHz */
+ {
+ struct HT_info_element *pht_info = (struct HT_info_element *)(pIE->data);
+
+ if ((pregpriv->cbw40_enable) && (pht_info->infos[0] & BIT(2))) {
+ /* switch to the 40M Hz mode according to the AP */
+ pmlmeext->cur_bwmode = HT_CHANNEL_WIDTH_40;
+ switch (pht_info->infos[0] & 0x3) {
+ case 1:
+ pmlmeext->cur_ch_offset = HAL_PRIME_CHNL_OFFSET_LOWER;
+ break;
+ case 3:
+ pmlmeext->cur_ch_offset = HAL_PRIME_CHNL_OFFSET_UPPER;
+ break;
+ default:
+ pmlmeext->cur_ch_offset = HAL_PRIME_CHNL_OFFSET_DONT_CARE;
+ break;
+ }
+
+ DBG_88E("set ch/bw before connected\n");
+ }
+ }
+ break;
+ default:
+ break;
+ }
+
+ i += (pIE->Length + 2);
+ }
+ /* disable dynamic functions, such as high power, DIG */
+
+ /* config the initial gain under linking, need to write the BB registers */
+
+ rtw_hal_set_hwreg(padapter, HW_VAR_BSSID, pmlmeinfo->network.MacAddress);
+ join_type = 0;
+ rtw_hal_set_hwreg(padapter, HW_VAR_MLME_JOIN, (u8 *)(&join_type));
+
+ /* cancel link timer */
+ del_timer_sync(&pmlmeext->link_timer);
+
+ start_clnt_join(padapter);
+
+ return H2C_SUCCESS;
+}
+
+u8 disconnect_hdl(struct adapter *padapter, unsigned char *pbuf)
+{
+ struct disconnect_parm *param = (struct disconnect_parm *)pbuf;
+ struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv;
+ struct mlme_ext_info *pmlmeinfo = &(pmlmeext->mlmext_info);
+ struct wlan_bssid_ex *pnetwork = (struct wlan_bssid_ex *)(&(pmlmeinfo->network));
+ u8 val8;
+
+ if (is_client_associated_to_ap(padapter))
+ issue_deauth_ex(padapter, pnetwork->MacAddress, WLAN_REASON_DEAUTH_LEAVING, param->deauth_timeout_ms/100, 100);
+
+ rtw_hal_set_hwreg(padapter, HW_VAR_MLME_DISCONNECT, NULL);
+ rtw_hal_set_hwreg(padapter, HW_VAR_BSSID, null_addr);
+
+ /* restore to initial setting. */
+ update_tx_basic_rate(padapter, padapter->registrypriv.wireless_mode);
+
+ if (((pmlmeinfo->state&0x03) == WIFI_FW_ADHOC_STATE) || ((pmlmeinfo->state&0x03) == WIFI_FW_AP_STATE)) {
+ /* Stop BCN */
+ val8 = 0;
+ rtw_hal_set_hwreg(padapter, HW_VAR_BCN_FUNC, (u8 *)(&val8));
+ }
+
+
+ /* set MSR to no link state -> infra. mode */
+ Set_MSR(padapter, _HW_STATE_STATION_);
+
+ pmlmeinfo->state = WIFI_FW_NULL_STATE;
+
+ /* switch to the 20M Hz mode after disconnect */
+ pmlmeext->cur_bwmode = HT_CHANNEL_WIDTH_20;
+ pmlmeext->cur_ch_offset = HAL_PRIME_CHNL_OFFSET_DONT_CARE;
+
+ set_channel_bwmode(padapter, pmlmeext->cur_channel, pmlmeext->cur_ch_offset, pmlmeext->cur_bwmode);
+
+ flush_all_cam_entry(padapter);
+
+ del_timer_sync(&pmlmeext->link_timer);
+
+ rtw_free_uc_swdec_pending_queue(padapter);
+
+ return H2C_SUCCESS;
+}
+
+static int rtw_scan_ch_decision(struct adapter *padapter, struct rtw_ieee80211_channel *out,
+ u32 out_num, struct rtw_ieee80211_channel *in, u32 in_num)
+{
+ int i, j;
+ int set_idx;
+ struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv;
+
+ /* clear out first */
+ memset(out, 0, sizeof(struct rtw_ieee80211_channel)*out_num);
+
+ /* acquire channels from in */
+ j = 0;
+ for (i = 0; i < in_num; i++) {
+ set_idx = rtw_ch_set_search_ch(pmlmeext->channel_set, in[i].hw_value);
+ if (in[i].hw_value && !(in[i].flags & RTW_IEEE80211_CHAN_DISABLED) &&
+ set_idx >= 0) {
+ out[j] = in[i];
+
+ if (pmlmeext->channel_set[set_idx].ScanType == SCAN_PASSIVE)
+ out[j].flags &= RTW_IEEE80211_CHAN_PASSIVE_SCAN;
+
+ j++;
+ }
+ if (j >= out_num)
+ break;
+ }
+
+ /* if out is empty, use channel_set as default */
+ if (j == 0) {
+ for (i = 0; i < pmlmeext->max_chan_nums; i++) {
+ out[i].hw_value = pmlmeext->channel_set[i].ChannelNum;
+
+ if (pmlmeext->channel_set[i].ScanType == SCAN_PASSIVE)
+ out[i].flags &= RTW_IEEE80211_CHAN_PASSIVE_SCAN;
+
+ j++;
+ }
+ }
+
+ return j;
+}
+
+u8 sitesurvey_cmd_hdl(struct adapter *padapter, u8 *pbuf)
+{
+ struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv;
+ struct sitesurvey_parm *pparm = (struct sitesurvey_parm *)pbuf;
+ u8 bdelayscan = false;
+ u8 val8;
+ u32 initialgain;
+ u32 i;
+
+ if (pmlmeext->sitesurvey_res.state == SCAN_DISABLE) {
+ /* for first time sitesurvey_cmd */
+ rtw_hal_set_hwreg(padapter, HW_VAR_CHECK_TXBUF, NULL);
+
+ pmlmeext->sitesurvey_res.state = SCAN_START;
+ pmlmeext->sitesurvey_res.bss_cnt = 0;
+ pmlmeext->sitesurvey_res.channel_idx = 0;
+
+ for (i = 0; i < RTW_SSID_SCAN_AMOUNT; i++) {
+ if (pparm->ssid[i].SsidLength) {
+ memcpy(pmlmeext->sitesurvey_res.ssid[i].Ssid, pparm->ssid[i].Ssid, IW_ESSID_MAX_SIZE);
+ pmlmeext->sitesurvey_res.ssid[i].SsidLength = pparm->ssid[i].SsidLength;
+ } else {
+ pmlmeext->sitesurvey_res.ssid[i].SsidLength = 0;
+ }
+ }
+
+ pmlmeext->sitesurvey_res.ch_num = rtw_scan_ch_decision(padapter
+ , pmlmeext->sitesurvey_res.ch, RTW_CHANNEL_SCAN_AMOUNT
+ , pparm->ch, pparm->ch_num
+ );
+
+ pmlmeext->sitesurvey_res.scan_mode = pparm->scan_mode;
+
+ /* issue null data if associating to the AP */
+ if (is_client_associated_to_ap(padapter)) {
+ pmlmeext->sitesurvey_res.state = SCAN_TXNULL;
+
+ issue_nulldata(padapter, NULL, 1, 3, 500);
+
+ bdelayscan = true;
+ }
+ if (bdelayscan) {
+ /* delay 50ms to protect nulldata(1). */
+ set_survey_timer(pmlmeext, 50);
+ return H2C_SUCCESS;
+ }
+ }
+
+ if ((pmlmeext->sitesurvey_res.state == SCAN_START) || (pmlmeext->sitesurvey_res.state == SCAN_TXNULL)) {
+ /* disable dynamic functions, such as high power, DIG */
+ Save_DM_Func_Flag(padapter);
+ Switch_DM_Func(padapter, DYNAMIC_FUNC_DISABLE, false);
+
+ /* config the initial gain under scanning, need to write the BB registers */
+ initialgain = 0x1E;
+
+ rtw_hal_set_hwreg(padapter, HW_VAR_INITIAL_GAIN, (u8 *)(&initialgain));
+
+ /* set MSR to no link state */
+ Set_MSR(padapter, _HW_STATE_NOLINK_);
+
+ val8 = 1; /* under site survey */
+ rtw_hal_set_hwreg(padapter, HW_VAR_MLME_SITESURVEY, (u8 *)(&val8));
+
+ pmlmeext->sitesurvey_res.state = SCAN_PROCESS;
+ }
+
+ site_survey(padapter);
+
+ return H2C_SUCCESS;
+}
+
+u8 setauth_hdl(struct adapter *padapter, unsigned char *pbuf)
+{
+ struct setauth_parm *pparm = (struct setauth_parm *)pbuf;
+ struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv;
+ struct mlme_ext_info *pmlmeinfo = &(pmlmeext->mlmext_info);
+
+ if (pparm->mode < 4)
+ pmlmeinfo->auth_algo = pparm->mode;
+ return H2C_SUCCESS;
+}
+
+u8 setkey_hdl(struct adapter *padapter, u8 *pbuf)
+{
+ unsigned short ctrl;
+ struct setkey_parm *pparm = (struct setkey_parm *)pbuf;
+ struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv;
+ struct mlme_ext_info *pmlmeinfo = &(pmlmeext->mlmext_info);
+ unsigned char null_sta[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+
+ /* main tx key for wep. */
+ if (pparm->set_tx)
+ pmlmeinfo->key_index = pparm->keyid;
+
+ /* write cam */
+ ctrl = BIT(15) | ((pparm->algorithm) << 2) | pparm->keyid;
+
+ DBG_88E_LEVEL(_drv_info_, "set group key to hw: alg:%d(WEP40-1 WEP104-5 TKIP-2 AES-4) "
+ "keyid:%d\n", pparm->algorithm, pparm->keyid);
+ write_cam(padapter, pparm->keyid, ctrl, null_sta, pparm->key);
+
+ return H2C_SUCCESS;
+}
+
+u8 set_stakey_hdl(struct adapter *padapter, u8 *pbuf)
+{
+ u16 ctrl = 0;
+ u8 cam_id;/* cam_entry */
+ struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv;
+ struct mlme_ext_info *pmlmeinfo = &(pmlmeext->mlmext_info);
+ struct set_stakey_parm *pparm = (struct set_stakey_parm *)pbuf;
+
+ /* cam_entry: */
+ /* 0~3 for default key */
+
+ /* for concurrent mode (ap+sta): */
+ /* default key is disable, using sw encrypt/decrypt */
+ /* cam_entry = 4 for sta mode (macid = 0) */
+ /* cam_entry(macid+3) = 5 ~ N for ap mode (aid = 1~N, macid = 2 ~N) */
+
+ /* for concurrent mode (sta+sta): */
+ /* default key is disable, using sw encrypt/decrypt */
+ /* cam_entry = 4 mapping to macid = 0 */
+ /* cam_entry = 5 mapping to macid = 2 */
+
+ cam_id = 4;
+
+ DBG_88E_LEVEL(_drv_info_, "set pairwise key to hw: alg:%d(WEP40-1 WEP104-5 TKIP-2 AES-4) camid:%d\n",
+ pparm->algorithm, cam_id);
+ if ((pmlmeinfo->state&0x03) == WIFI_FW_AP_STATE) {
+ struct sta_info *psta;
+ struct sta_priv *pstapriv = &padapter->stapriv;
+
+ if (pparm->algorithm == _NO_PRIVACY_) /* clear cam entry */ {
+ clear_cam_entry(padapter, pparm->id);
+ return H2C_SUCCESS_RSP;
+ }
+
+ psta = rtw_get_stainfo(pstapriv, pparm->addr);
+ if (psta) {
+ ctrl = BIT(15) | ((pparm->algorithm) << 2);
+
+ DBG_88E("r871x_set_stakey_hdl(): enc_algorithm=%d\n", pparm->algorithm);
+
+ if ((psta->mac_id < 1) || (psta->mac_id > (NUM_STA-4))) {
+ DBG_88E("r871x_set_stakey_hdl():set_stakey failed, mac_id(aid)=%d\n", psta->mac_id);
+ return H2C_REJECTED;
+ }
+
+ cam_id = psta->mac_id + 3;/* 0~3 for default key, cmd_id = macid + 3, macid = aid+1; */
+
+ DBG_88E("Write CAM, mac_addr =%x:%x:%x:%x:%x:%x, cam_entry=%d\n", pparm->addr[0],
+ pparm->addr[1], pparm->addr[2], pparm->addr[3], pparm->addr[4],
+ pparm->addr[5], cam_id);
+
+ write_cam(padapter, cam_id, ctrl, pparm->addr, pparm->key);
+
+ return H2C_SUCCESS_RSP;
+ } else {
+ DBG_88E("r871x_set_stakey_hdl(): sta has been free\n");
+ return H2C_REJECTED;
+ }
+ }
+
+ /* below for sta mode */
+
+ if (pparm->algorithm == _NO_PRIVACY_) { /* clear cam entry */
+ clear_cam_entry(padapter, pparm->id);
+ return H2C_SUCCESS;
+ }
+ ctrl = BIT(15) | ((pparm->algorithm) << 2);
+ write_cam(padapter, cam_id, ctrl, pparm->addr, pparm->key);
+ pmlmeinfo->enc_algo = pparm->algorithm;
+ return H2C_SUCCESS;
+}
+
+u8 add_ba_hdl(struct adapter *padapter, unsigned char *pbuf)
+{
+ struct addBaReq_parm *pparm = (struct addBaReq_parm *)pbuf;
+ struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv;
+ struct mlme_ext_info *pmlmeinfo = &(pmlmeext->mlmext_info);
+
+ struct sta_info *psta = rtw_get_stainfo(&padapter->stapriv, pparm->addr);
+
+ if (!psta)
+ return H2C_SUCCESS;
+
+ if (((pmlmeinfo->state & WIFI_FW_ASSOC_SUCCESS) && (pmlmeinfo->HT_enable)) ||
+ ((pmlmeinfo->state&0x03) == WIFI_FW_AP_STATE)) {
+ issue_action_BA(padapter, pparm->addr, RTW_WLAN_ACTION_ADDBA_REQ, (u16)pparm->tid);
+ mod_timer(&psta->addba_retry_timer,
+ jiffies + msecs_to_jiffies(ADDBA_TO));
+ } else {
+ psta->htpriv.candidate_tid_bitmap &= ~BIT(pparm->tid);
+ }
+ return H2C_SUCCESS;
+}
+
+u8 set_tx_beacon_cmd(struct adapter *padapter)
+{
+ struct cmd_obj *ph2c;
+ struct wlan_bssid_ex *ptxBeacon_parm;
+ struct cmd_priv *pcmdpriv = &(padapter->cmdpriv);
+ struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv;
+ struct mlme_ext_info *pmlmeinfo = &(pmlmeext->mlmext_info);
+ u8 res = _SUCCESS;
+ int len_diff = 0;
+
+
+ ph2c = kzalloc(sizeof(struct cmd_obj), GFP_KERNEL);
+ if (ph2c == NULL) {
+ res = _FAIL;
+ goto exit;
+ }
+
+ ptxBeacon_parm = kmemdup(&(pmlmeinfo->network),
+ sizeof(struct wlan_bssid_ex), GFP_KERNEL);
+ if (ptxBeacon_parm == NULL) {
+ kfree(ph2c);
+ res = _FAIL;
+ goto exit;
+ }
+
+ len_diff = update_hidden_ssid(ptxBeacon_parm->IEs+_BEACON_IE_OFFSET_,
+ ptxBeacon_parm->IELength-_BEACON_IE_OFFSET_,
+ pmlmeinfo->hidden_ssid_mode);
+ ptxBeacon_parm->IELength += len_diff;
+
+ init_h2fwcmd_w_parm_no_rsp(ph2c, ptxBeacon_parm, GEN_CMD_CODE(_TX_Beacon));
+
+ res = rtw_enqueue_cmd(pcmdpriv, ph2c);
+
+
+exit:
+
+
+ return res;
+}
+
+u8 mlme_evt_hdl(struct adapter *padapter, unsigned char *pbuf)
+{
+ u8 evt_code;
+ u16 evt_sz;
+ uint *peventbuf;
+ void (*event_callback)(struct adapter *dev, u8 *pbuf);
+
+ peventbuf = (uint *)pbuf;
+ evt_sz = (u16)(*peventbuf&0xffff);
+ evt_code = (u8)((*peventbuf>>16)&0xff);
+
+ /* checking if event code is valid */
+ if (evt_code >= MAX_C2HEVT) {
+ RT_TRACE(_module_rtl871x_cmd_c_, _drv_err_, ("\nEvent Code(%d) mismatch!\n", evt_code));
+ goto _abort_event_;
+ }
+
+ /* checking if event size match the event parm size */
+ if ((wlanevents[evt_code].parmsize != 0) &&
+ (wlanevents[evt_code].parmsize != evt_sz)) {
+ RT_TRACE(_module_rtl871x_cmd_c_, _drv_err_,
+ ("\nEvent(%d) Parm Size mismatch (%d vs %d)!\n",
+ evt_code, wlanevents[evt_code].parmsize, evt_sz));
+ goto _abort_event_;
+ }
+
+ peventbuf += 2;
+
+ if (peventbuf) {
+ event_callback = wlanevents[evt_code].event_callback;
+ event_callback(padapter, (u8 *)peventbuf);
+
+ }
+
+_abort_event_:
+ return H2C_SUCCESS;
+}
+
+u8 tx_beacon_hdl(struct adapter *padapter, unsigned char *pbuf)
+{
+ if (send_beacon(padapter) == _FAIL) {
+ DBG_88E("issue_beacon, fail!\n");
+ return H2C_PARAMETERS_ERROR;
+ }
+#ifdef CONFIG_88EU_AP_MODE
+ else { /* tx bc/mc frames after update TIM */
+ struct sta_info *psta_bmc;
+ struct list_head *xmitframe_plist, *xmitframe_phead;
+ struct xmit_frame *pxmitframe = NULL;
+ struct sta_priv *pstapriv = &padapter->stapriv;
+
+ /* for BC/MC Frames */
+ psta_bmc = rtw_get_bcmc_stainfo(padapter);
+ if (!psta_bmc)
+ return H2C_SUCCESS;
+
+ if ((pstapriv->tim_bitmap&BIT(0)) && (psta_bmc->sleepq_len > 0)) {
+ msleep(10);/* 10ms, ATIM(HIQ) Windows */
+ spin_lock_bh(&psta_bmc->sleep_q.lock);
+
+ xmitframe_phead = get_list_head(&psta_bmc->sleep_q);
+ xmitframe_plist = xmitframe_phead->next;
+
+ while (xmitframe_phead != xmitframe_plist) {
+ pxmitframe = container_of(xmitframe_plist, struct xmit_frame, list);
+
+ xmitframe_plist = xmitframe_plist->next;
+
+ list_del_init(&pxmitframe->list);
+
+ psta_bmc->sleepq_len--;
+ if (psta_bmc->sleepq_len > 0)
+ pxmitframe->attrib.mdata = 1;
+ else
+ pxmitframe->attrib.mdata = 0;
+
+ pxmitframe->attrib.triggered = 1;
+
+ pxmitframe->attrib.qsel = 0x11;/* HIQ */
+
+ spin_unlock_bh(&psta_bmc->sleep_q.lock);
+ if (rtw_hal_xmit(padapter, pxmitframe))
+ rtw_os_xmit_complete(padapter, pxmitframe);
+ spin_lock_bh(&psta_bmc->sleep_q.lock);
+ }
+ spin_unlock_bh(&psta_bmc->sleep_q.lock);
+ }
+ }
+#endif
+ return H2C_SUCCESS;
+}
+
+u8 set_ch_hdl(struct adapter *padapter, u8 *pbuf)
+{
+ struct set_ch_parm *set_ch_parm;
+ struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv;
+
+ if (!pbuf)
+ return H2C_PARAMETERS_ERROR;
+
+ set_ch_parm = (struct set_ch_parm *)pbuf;
+
+ DBG_88E(FUNC_NDEV_FMT" ch:%u, bw:%u, ch_offset:%u\n",
+ FUNC_NDEV_ARG(padapter->pnetdev),
+ set_ch_parm->ch, set_ch_parm->bw, set_ch_parm->ch_offset);
+
+ pmlmeext->cur_channel = set_ch_parm->ch;
+ pmlmeext->cur_ch_offset = set_ch_parm->ch_offset;
+ pmlmeext->cur_bwmode = set_ch_parm->bw;
+
+ set_channel_bwmode(padapter, set_ch_parm->ch, set_ch_parm->ch_offset, set_ch_parm->bw);
+
+ return H2C_SUCCESS;
+}
+
+u8 set_chplan_hdl(struct adapter *padapter, unsigned char *pbuf)
+{
+ struct SetChannelPlan_param *setChannelPlan_param;
+ struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv;
+
+ if (!pbuf)
+ return H2C_PARAMETERS_ERROR;
+
+ setChannelPlan_param = (struct SetChannelPlan_param *)pbuf;
+
+ pmlmeext->max_chan_nums = init_channel_set(padapter, setChannelPlan_param->channel_plan, pmlmeext->channel_set);
+ init_channel_list(padapter, pmlmeext->channel_set, pmlmeext->max_chan_nums, &pmlmeext->channel_list);
+
+ return H2C_SUCCESS;
+}
diff --git a/drivers/staging/rtl8188eu/core/rtw_pwrctrl.c b/drivers/staging/rtl8188eu/core/rtw_pwrctrl.c
new file mode 100644
index 000000000..ec0a8a4cd
--- /dev/null
+++ b/drivers/staging/rtl8188eu/core/rtw_pwrctrl.c
@@ -0,0 +1,672 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2012 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ *
+ ******************************************************************************/
+#define _RTW_PWRCTRL_C_
+
+#include <osdep_service.h>
+#include <drv_types.h>
+#include <osdep_intf.h>
+#include <usb_ops_linux.h>
+#include <linux/usb.h>
+
+static int rtw_hw_suspend(struct adapter *padapter)
+{
+ struct pwrctrl_priv *pwrpriv = &padapter->pwrctrlpriv;
+ struct net_device *pnetdev = padapter->pnetdev;
+
+
+ if ((!padapter->bup) || (padapter->bDriverStopped) ||
+ (padapter->bSurpriseRemoved)) {
+ DBG_88E("padapter->bup=%d bDriverStopped=%d bSurpriseRemoved = %d\n",
+ padapter->bup, padapter->bDriverStopped,
+ padapter->bSurpriseRemoved);
+ goto error_exit;
+ }
+
+ /* system suspend */
+ LeaveAllPowerSaveMode(padapter);
+
+ DBG_88E("==> rtw_hw_suspend\n");
+ _enter_pwrlock(&pwrpriv->lock);
+ pwrpriv->bips_processing = true;
+ /* s1. */
+ if (pnetdev) {
+ netif_carrier_off(pnetdev);
+ netif_tx_stop_all_queues(pnetdev);
+ }
+
+ /* s2. */
+ rtw_disassoc_cmd(padapter, 500, false);
+
+ /* s2-2. indicate disconnect to os */
+ {
+ struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
+
+ if (check_fwstate(pmlmepriv, _FW_LINKED)) {
+ _clr_fwstate_(pmlmepriv, _FW_LINKED);
+
+ rtw_led_control(padapter, LED_CTL_NO_LINK);
+
+ rtw_os_indicate_disconnect(padapter);
+
+ /* donnot enqueue cmd */
+ rtw_lps_ctrl_wk_cmd(padapter, LPS_CTRL_DISCONNECT, 0);
+ }
+ }
+ /* s2-3. */
+ rtw_free_assoc_resources(padapter, 1);
+
+ /* s2-4. */
+ rtw_free_network_queue(padapter, true);
+ rtw_ips_dev_unload(padapter);
+ pwrpriv->rf_pwrstate = rf_off;
+ pwrpriv->bips_processing = false;
+
+ _exit_pwrlock(&pwrpriv->lock);
+
+ return 0;
+
+error_exit:
+ DBG_88E("%s, failed\n", __func__);
+ return -1;
+}
+
+static int rtw_hw_resume(struct adapter *padapter)
+{
+ struct pwrctrl_priv *pwrpriv = &padapter->pwrctrlpriv;
+ struct net_device *pnetdev = padapter->pnetdev;
+
+
+ /* system resume */
+ DBG_88E("==> rtw_hw_resume\n");
+ _enter_pwrlock(&pwrpriv->lock);
+ pwrpriv->bips_processing = true;
+ rtw_reset_drv_sw(padapter);
+
+ if (pm_netdev_open(pnetdev, false) != 0) {
+ _exit_pwrlock(&pwrpriv->lock);
+ goto error_exit;
+ }
+
+ netif_device_attach(pnetdev);
+ netif_carrier_on(pnetdev);
+
+ if (!netif_queue_stopped(pnetdev))
+ netif_start_queue(pnetdev);
+ else
+ netif_wake_queue(pnetdev);
+
+ pwrpriv->bkeepfwalive = false;
+ pwrpriv->brfoffbyhw = false;
+
+ pwrpriv->rf_pwrstate = rf_on;
+ pwrpriv->bips_processing = false;
+
+ _exit_pwrlock(&pwrpriv->lock);
+
+
+ return 0;
+error_exit:
+ DBG_88E("%s, Open net dev failed\n", __func__);
+ return -1;
+}
+
+void ips_enter(struct adapter *padapter)
+{
+ struct pwrctrl_priv *pwrpriv = &padapter->pwrctrlpriv;
+ struct xmit_priv *pxmit_priv = &padapter->xmitpriv;
+
+ if (padapter->registrypriv.mp_mode == 1)
+ return;
+
+ if (pxmit_priv->free_xmitbuf_cnt != NR_XMITBUFF ||
+ pxmit_priv->free_xmit_extbuf_cnt != NR_XMIT_EXTBUFF) {
+ DBG_88E_LEVEL(_drv_info_, "There are some pkts to transmit\n");
+ DBG_88E_LEVEL(_drv_info_, "free_xmitbuf_cnt: %d, free_xmit_extbuf_cnt: %d\n",
+ pxmit_priv->free_xmitbuf_cnt, pxmit_priv->free_xmit_extbuf_cnt);
+ return;
+ }
+
+ _enter_pwrlock(&pwrpriv->lock);
+
+ pwrpriv->bips_processing = true;
+
+ /* syn ips_mode with request */
+ pwrpriv->ips_mode = pwrpriv->ips_mode_req;
+
+ pwrpriv->ips_enter_cnts++;
+ DBG_88E("==>ips_enter cnts:%d\n", pwrpriv->ips_enter_cnts);
+ if (rf_off == pwrpriv->change_rfpwrstate) {
+ pwrpriv->bpower_saving = true;
+ DBG_88E_LEVEL(_drv_info_, "nolinked power save enter\n");
+
+ if (pwrpriv->ips_mode == IPS_LEVEL_2)
+ pwrpriv->bkeepfwalive = true;
+
+ rtw_ips_pwr_down(padapter);
+ pwrpriv->rf_pwrstate = rf_off;
+ }
+ pwrpriv->bips_processing = false;
+
+ _exit_pwrlock(&pwrpriv->lock);
+}
+
+int ips_leave(struct adapter *padapter)
+{
+ struct pwrctrl_priv *pwrpriv = &padapter->pwrctrlpriv;
+ struct security_priv *psecuritypriv = &(padapter->securitypriv);
+ struct mlme_priv *pmlmepriv = &(padapter->mlmepriv);
+ int result = _SUCCESS;
+ int keyid;
+
+
+ _enter_pwrlock(&pwrpriv->lock);
+
+ if ((pwrpriv->rf_pwrstate == rf_off) && (!pwrpriv->bips_processing)) {
+ pwrpriv->bips_processing = true;
+ pwrpriv->change_rfpwrstate = rf_on;
+ pwrpriv->ips_leave_cnts++;
+ DBG_88E("==>ips_leave cnts:%d\n", pwrpriv->ips_leave_cnts);
+
+ result = rtw_ips_pwr_up(padapter);
+ if (result == _SUCCESS) {
+ pwrpriv->rf_pwrstate = rf_on;
+ }
+ DBG_88E_LEVEL(_drv_info_, "nolinked power save leave\n");
+
+ if ((_WEP40_ == psecuritypriv->dot11PrivacyAlgrthm) || (_WEP104_ == psecuritypriv->dot11PrivacyAlgrthm)) {
+ DBG_88E("==>%s, channel(%d), processing(%x)\n", __func__, padapter->mlmeextpriv.cur_channel, pwrpriv->bips_processing);
+ set_channel_bwmode(padapter, padapter->mlmeextpriv.cur_channel, HAL_PRIME_CHNL_OFFSET_DONT_CARE, HT_CHANNEL_WIDTH_20);
+ for (keyid = 0; keyid < 4; keyid++) {
+ if (pmlmepriv->key_mask & BIT(keyid)) {
+ if (keyid == psecuritypriv->dot11PrivacyKeyIndex)
+ result = rtw_set_key(padapter, psecuritypriv, keyid, 1);
+ else
+ result = rtw_set_key(padapter, psecuritypriv, keyid, 0);
+ }
+ }
+ }
+
+ DBG_88E("==> ips_leave.....LED(0x%08x)...\n", usb_read32(padapter, 0x4c));
+ pwrpriv->bips_processing = false;
+
+ pwrpriv->bkeepfwalive = false;
+ pwrpriv->bpower_saving = false;
+ }
+
+ _exit_pwrlock(&pwrpriv->lock);
+
+ return result;
+}
+
+static bool rtw_pwr_unassociated_idle(struct adapter *adapter)
+{
+ struct mlme_priv *pmlmepriv = &(adapter->mlmepriv);
+ bool ret = false;
+
+ if (time_after_eq(adapter->pwrctrlpriv.ips_deny_time, jiffies))
+ goto exit;
+
+ if (check_fwstate(pmlmepriv, WIFI_ASOC_STATE|WIFI_SITE_MONITOR) ||
+ check_fwstate(pmlmepriv, WIFI_UNDER_LINKING|WIFI_UNDER_WPS) ||
+ check_fwstate(pmlmepriv, WIFI_AP_STATE) ||
+ check_fwstate(pmlmepriv, WIFI_ADHOC_MASTER_STATE|WIFI_ADHOC_STATE))
+ goto exit;
+
+ ret = true;
+
+exit:
+ return ret;
+}
+
+void rtw_ps_processor(struct adapter *padapter)
+{
+ struct pwrctrl_priv *pwrpriv = &padapter->pwrctrlpriv;
+ struct mlme_priv *pmlmepriv = &(padapter->mlmepriv);
+ enum rt_rf_power_state rfpwrstate;
+
+ pwrpriv->ps_processing = true;
+
+ if (pwrpriv->bips_processing)
+ goto exit;
+
+ if (padapter->pwrctrlpriv.bHWPwrPindetect) {
+ rfpwrstate = RfOnOffDetect(padapter);
+ DBG_88E("@@@@- #2 %s==> rfstate:%s\n", __func__, (rfpwrstate == rf_on) ? "rf_on" : "rf_off");
+
+ if (rfpwrstate != pwrpriv->rf_pwrstate) {
+ if (rfpwrstate == rf_off) {
+ pwrpriv->change_rfpwrstate = rf_off;
+ pwrpriv->brfoffbyhw = true;
+ rtw_hw_suspend(padapter);
+ } else {
+ pwrpriv->change_rfpwrstate = rf_on;
+ rtw_hw_resume(padapter);
+ }
+ DBG_88E("current rf_pwrstate(%s)\n", (pwrpriv->rf_pwrstate == rf_off) ? "rf_off" : "rf_on");
+ }
+ pwrpriv->pwr_state_check_cnts++;
+ }
+
+ if (pwrpriv->ips_mode_req == IPS_NONE)
+ goto exit;
+
+ if (!rtw_pwr_unassociated_idle(padapter))
+ goto exit;
+
+ if ((pwrpriv->rf_pwrstate == rf_on) && ((pwrpriv->pwr_state_check_cnts%4) == 0)) {
+ DBG_88E("==>%s .fw_state(%x)\n", __func__, get_fwstate(pmlmepriv));
+ pwrpriv->change_rfpwrstate = rf_off;
+
+ ips_enter(padapter);
+ }
+exit:
+ rtw_set_pwr_state_check_timer(&padapter->pwrctrlpriv);
+ pwrpriv->ps_processing = false;
+}
+
+static void pwr_state_check_handler(unsigned long data)
+{
+ struct adapter *padapter = (struct adapter *)data;
+ rtw_ps_cmd(padapter);
+}
+
+/*
+ *
+ * Parameters
+ * padapter
+ * pslv power state level, only could be PS_STATE_S0 ~ PS_STATE_S4
+ *
+ */
+void rtw_set_rpwm(struct adapter *padapter, u8 pslv)
+{
+ u8 rpwm;
+ struct pwrctrl_priv *pwrpriv = &padapter->pwrctrlpriv;
+
+ pslv = PS_STATE(pslv);
+
+ if (pwrpriv->btcoex_rfon) {
+ if (pslv < PS_STATE_S4)
+ pslv = PS_STATE_S3;
+ }
+
+ if ((pwrpriv->rpwm == pslv)) {
+ RT_TRACE(_module_rtl871x_pwrctrl_c_, _drv_err_,
+ ("%s: Already set rpwm[0x%02X], new=0x%02X!\n", __func__, pwrpriv->rpwm, pslv));
+ return;
+ }
+
+ if ((padapter->bSurpriseRemoved) ||
+ (!padapter->hw_init_completed)) {
+ RT_TRACE(_module_rtl871x_pwrctrl_c_, _drv_err_,
+ ("%s: SurpriseRemoved(%d) hw_init_completed(%d)\n",
+ __func__, padapter->bSurpriseRemoved, padapter->hw_init_completed));
+
+ pwrpriv->cpwm = PS_STATE_S4;
+
+ return;
+ }
+
+ if (padapter->bDriverStopped) {
+ RT_TRACE(_module_rtl871x_pwrctrl_c_, _drv_err_,
+ ("%s: change power state(0x%02X) when DriverStopped\n", __func__, pslv));
+
+ if (pslv < PS_STATE_S2) {
+ RT_TRACE(_module_rtl871x_pwrctrl_c_, _drv_err_,
+ ("%s: Reject to enter PS_STATE(0x%02X) lower than S2 when DriverStopped!!\n", __func__, pslv));
+ return;
+ }
+ }
+
+ rpwm = pslv | pwrpriv->tog;
+ RT_TRACE(_module_rtl871x_pwrctrl_c_, _drv_notice_,
+ ("rtw_set_rpwm: rpwm=0x%02x cpwm=0x%02x\n", rpwm, pwrpriv->cpwm));
+
+ pwrpriv->rpwm = pslv;
+
+ rtw_hal_set_hwreg(padapter, HW_VAR_SET_RPWM, (u8 *)(&rpwm));
+
+ pwrpriv->tog += 0x80;
+ pwrpriv->cpwm = pslv;
+}
+
+static u8 PS_RDY_CHECK(struct adapter *padapter)
+{
+ u32 curr_time, delta_time;
+ struct pwrctrl_priv *pwrpriv = &padapter->pwrctrlpriv;
+ struct mlme_priv *pmlmepriv = &(padapter->mlmepriv);
+
+
+ curr_time = jiffies;
+ delta_time = curr_time - pwrpriv->DelayLPSLastTimeStamp;
+
+ if (delta_time < LPS_DELAY_TIME)
+ return false;
+
+ if ((check_fwstate(pmlmepriv, _FW_LINKED) == false) ||
+ (check_fwstate(pmlmepriv, _FW_UNDER_SURVEY)) ||
+ (check_fwstate(pmlmepriv, WIFI_AP_STATE)) ||
+ (check_fwstate(pmlmepriv, WIFI_ADHOC_MASTER_STATE)) ||
+ (check_fwstate(pmlmepriv, WIFI_ADHOC_STATE)))
+ return false;
+ if (pwrpriv->bInSuspend)
+ return false;
+ if ((padapter->securitypriv.dot11AuthAlgrthm == dot11AuthAlgrthm_8021X) && (padapter->securitypriv.binstallGrpkey == false)) {
+ DBG_88E("Group handshake still in progress !!!\n");
+ return false;
+ }
+ return true;
+}
+
+void rtw_set_ps_mode(struct adapter *padapter, u8 ps_mode, u8 smart_ps, u8 bcn_ant_mode)
+{
+ struct pwrctrl_priv *pwrpriv = &padapter->pwrctrlpriv;
+
+ RT_TRACE(_module_rtl871x_pwrctrl_c_, _drv_notice_,
+ ("%s: PowerMode=%d Smart_PS=%d\n",
+ __func__, ps_mode, smart_ps));
+
+ if (ps_mode > PM_Card_Disable) {
+ RT_TRACE(_module_rtl871x_pwrctrl_c_, _drv_err_, ("ps_mode:%d error\n", ps_mode));
+ return;
+ }
+
+ if (pwrpriv->pwr_mode == ps_mode) {
+ if (PS_MODE_ACTIVE == ps_mode)
+ return;
+
+ if ((pwrpriv->smart_ps == smart_ps) &&
+ (pwrpriv->bcn_ant_mode == bcn_ant_mode))
+ return;
+ }
+
+ /* if (pwrpriv->pwr_mode == PS_MODE_ACTIVE) */
+ if (ps_mode == PS_MODE_ACTIVE) {
+ if (PS_RDY_CHECK(padapter)) {
+ DBG_88E("%s: Enter 802.11 power save\n", __func__);
+ pwrpriv->bFwCurrentInPSMode = true;
+ pwrpriv->pwr_mode = ps_mode;
+ pwrpriv->smart_ps = smart_ps;
+ pwrpriv->bcn_ant_mode = bcn_ant_mode;
+ rtw_hal_set_hwreg(padapter, HW_VAR_H2C_FW_PWRMODE, (u8 *)(&ps_mode));
+ rtw_set_rpwm(padapter, PS_STATE_S2);
+ }
+ }
+}
+
+/*
+ * Return:
+ * 0: Leave OK
+ * -1: Timeout
+ * -2: Other error
+ */
+s32 LPS_RF_ON_check(struct adapter *padapter, u32 delay_ms)
+{
+ u32 start_time;
+ u8 bAwake = false;
+ s32 err = 0;
+
+
+ start_time = jiffies;
+ while (1) {
+ rtw_hal_get_hwreg(padapter, HW_VAR_FWLPS_RF_ON, &bAwake);
+ if (bAwake)
+ break;
+
+ if (padapter->bSurpriseRemoved) {
+ err = -2;
+ DBG_88E("%s: device surprise removed!!\n", __func__);
+ break;
+ }
+
+ if (rtw_get_passing_time_ms(start_time) > delay_ms) {
+ err = -1;
+ DBG_88E("%s: Wait for FW LPS leave more than %u ms!!!\n", __func__, delay_ms);
+ break;
+ }
+ msleep(1);
+ }
+
+ return err;
+}
+
+/* */
+/* Description: */
+/* Enter the leisure power save mode. */
+/* */
+void LPS_Enter(struct adapter *padapter)
+{
+ struct pwrctrl_priv *pwrpriv = &padapter->pwrctrlpriv;
+
+ if (PS_RDY_CHECK(padapter) == false)
+ return;
+
+ if (pwrpriv->bLeisurePs) {
+ /* Idle for a while if we connect to AP a while ago. */
+ if (pwrpriv->LpsIdleCount >= 2) { /* 4 Sec */
+ if (pwrpriv->pwr_mode == PS_MODE_ACTIVE) {
+ pwrpriv->bpower_saving = true;
+ DBG_88E("%s smart_ps:%d\n", __func__, pwrpriv->smart_ps);
+ /* For Tenda W311R IOT issue */
+ rtw_set_ps_mode(padapter, pwrpriv->power_mgnt, pwrpriv->smart_ps, 0);
+ }
+ } else {
+ pwrpriv->LpsIdleCount++;
+ }
+ }
+}
+
+#define LPS_LEAVE_TIMEOUT_MS 100
+
+/* Description: */
+/* Leave the leisure power save mode. */
+void LPS_Leave(struct adapter *padapter)
+{
+ struct pwrctrl_priv *pwrpriv = &padapter->pwrctrlpriv;
+
+ if (pwrpriv->bLeisurePs) {
+ if (pwrpriv->pwr_mode != PS_MODE_ACTIVE) {
+ rtw_set_ps_mode(padapter, PS_MODE_ACTIVE, 0, 0);
+
+ if (pwrpriv->pwr_mode == PS_MODE_ACTIVE)
+ LPS_RF_ON_check(padapter, LPS_LEAVE_TIMEOUT_MS);
+ }
+ }
+
+ pwrpriv->bpower_saving = false;
+}
+
+/* */
+/* Description: Leave all power save mode: LPS, FwLPS, IPS if needed. */
+/* Move code to function by tynli. 2010.03.26. */
+/* */
+void LeaveAllPowerSaveMode(struct adapter *Adapter)
+{
+ struct mlme_priv *pmlmepriv = &(Adapter->mlmepriv);
+ u8 enqueue = 0;
+
+ if (check_fwstate(pmlmepriv, _FW_LINKED))
+ rtw_lps_ctrl_wk_cmd(Adapter, LPS_CTRL_LEAVE, enqueue);
+}
+
+void rtw_init_pwrctrl_priv(struct adapter *padapter)
+{
+ struct pwrctrl_priv *pwrctrlpriv = &padapter->pwrctrlpriv;
+
+ _init_pwrlock(&pwrctrlpriv->lock);
+ pwrctrlpriv->rf_pwrstate = rf_on;
+ pwrctrlpriv->ips_enter_cnts = 0;
+ pwrctrlpriv->ips_leave_cnts = 0;
+ pwrctrlpriv->bips_processing = false;
+
+ pwrctrlpriv->ips_mode = padapter->registrypriv.ips_mode;
+ pwrctrlpriv->ips_mode_req = padapter->registrypriv.ips_mode;
+
+ pwrctrlpriv->pwr_state_check_interval = RTW_PWR_STATE_CHK_INTERVAL;
+ pwrctrlpriv->pwr_state_check_cnts = 0;
+ pwrctrlpriv->bInternalAutoSuspend = false;
+ pwrctrlpriv->bInSuspend = false;
+ pwrctrlpriv->bkeepfwalive = false;
+
+ pwrctrlpriv->LpsIdleCount = 0;
+ if (padapter->registrypriv.mp_mode == 1)
+ pwrctrlpriv->power_mgnt = PS_MODE_ACTIVE;
+ else
+ pwrctrlpriv->power_mgnt = padapter->registrypriv.power_mgnt;/* PS_MODE_MIN; */
+ pwrctrlpriv->bLeisurePs = (PS_MODE_ACTIVE != pwrctrlpriv->power_mgnt) ? true : false;
+
+ pwrctrlpriv->bFwCurrentInPSMode = false;
+
+ pwrctrlpriv->rpwm = 0;
+ pwrctrlpriv->cpwm = PS_STATE_S4;
+
+ pwrctrlpriv->pwr_mode = PS_MODE_ACTIVE;
+ pwrctrlpriv->smart_ps = padapter->registrypriv.smart_ps;
+ pwrctrlpriv->bcn_ant_mode = 0;
+
+ pwrctrlpriv->tog = 0x80;
+
+ pwrctrlpriv->btcoex_rfon = false;
+
+ setup_timer(&pwrctrlpriv->pwr_state_check_timer,
+ pwr_state_check_handler,
+ (unsigned long)padapter);
+}
+
+inline void rtw_set_ips_deny(struct adapter *padapter, u32 ms)
+{
+ struct pwrctrl_priv *pwrpriv = &padapter->pwrctrlpriv;
+ pwrpriv->ips_deny_time = jiffies + msecs_to_jiffies(ms);
+}
+
+/*
+* rtw_pwr_wakeup - Wake the NIC up from: 1)IPS. 2)USB autosuspend
+* @adapter: pointer to struct adapter structure
+* @ips_deffer_ms: the ms will prevent from falling into IPS after wakeup
+* Return _SUCCESS or _FAIL
+*/
+
+int _rtw_pwr_wakeup(struct adapter *padapter, u32 ips_deffer_ms, const char *caller)
+{
+ struct pwrctrl_priv *pwrpriv = &padapter->pwrctrlpriv;
+ struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
+ unsigned long expires;
+ int ret = _SUCCESS;
+
+ expires = jiffies + msecs_to_jiffies(ips_deffer_ms);
+ if (time_before(pwrpriv->ips_deny_time, expires))
+ pwrpriv->ips_deny_time = jiffies + msecs_to_jiffies(ips_deffer_ms);
+
+{
+ u32 start = jiffies;
+ if (pwrpriv->ps_processing) {
+ DBG_88E("%s wait ps_processing...\n", __func__);
+ while (pwrpriv->ps_processing && rtw_get_passing_time_ms(start) <= 3000)
+ usleep_range(1000, 3000);
+ if (pwrpriv->ps_processing)
+ DBG_88E("%s wait ps_processing timeout\n", __func__);
+ else
+ DBG_88E("%s wait ps_processing done\n", __func__);
+ }
+}
+
+ /* System suspend is not allowed to wakeup */
+ if ((!pwrpriv->bInternalAutoSuspend) && (pwrpriv->bInSuspend)) {
+ ret = _FAIL;
+ goto exit;
+ }
+
+ /* block??? */
+ if ((pwrpriv->bInternalAutoSuspend) && (padapter->net_closed)) {
+ ret = _FAIL;
+ goto exit;
+ }
+
+ /* I think this should be check in IPS, LPS, autosuspend functions... */
+ if (check_fwstate(pmlmepriv, _FW_LINKED)) {
+ ret = _SUCCESS;
+ goto exit;
+ }
+ if (rf_off == pwrpriv->rf_pwrstate) {
+ DBG_88E("%s call ips_leave....\n", __func__);
+ if (_FAIL == ips_leave(padapter)) {
+ DBG_88E("======> ips_leave fail.............\n");
+ ret = _FAIL;
+ goto exit;
+ }
+ }
+
+ /* TODO: the following checking need to be merged... */
+ if (padapter->bDriverStopped || !padapter->bup ||
+ !padapter->hw_init_completed) {
+ DBG_88E("%s: bDriverStopped=%d, bup=%d, hw_init_completed =%u\n"
+ , caller
+ , padapter->bDriverStopped
+ , padapter->bup
+ , padapter->hw_init_completed);
+ ret = false;
+ goto exit;
+ }
+
+exit:
+ expires = jiffies + msecs_to_jiffies(ips_deffer_ms);
+ if (time_before(pwrpriv->ips_deny_time, expires))
+ pwrpriv->ips_deny_time = jiffies + msecs_to_jiffies(ips_deffer_ms);
+ return ret;
+}
+
+int rtw_pm_set_lps(struct adapter *padapter, u8 mode)
+{
+ int ret = 0;
+ struct pwrctrl_priv *pwrctrlpriv = &padapter->pwrctrlpriv;
+
+ if (mode < PS_MODE_NUM) {
+ if (pwrctrlpriv->power_mgnt != mode) {
+ if (PS_MODE_ACTIVE == mode)
+ LeaveAllPowerSaveMode(padapter);
+ else
+ pwrctrlpriv->LpsIdleCount = 2;
+ pwrctrlpriv->power_mgnt = mode;
+ pwrctrlpriv->bLeisurePs = (PS_MODE_ACTIVE != pwrctrlpriv->power_mgnt) ? true : false;
+ }
+ } else {
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+int rtw_pm_set_ips(struct adapter *padapter, u8 mode)
+{
+ struct pwrctrl_priv *pwrctrlpriv = &padapter->pwrctrlpriv;
+
+ if (mode == IPS_NORMAL || mode == IPS_LEVEL_2) {
+ rtw_ips_mode_req(pwrctrlpriv, mode);
+ DBG_88E("%s %s\n", __func__, mode == IPS_NORMAL ? "IPS_NORMAL" : "IPS_LEVEL_2");
+ return 0;
+ } else if (mode == IPS_NONE) {
+ rtw_ips_mode_req(pwrctrlpriv, mode);
+ DBG_88E("%s %s\n", __func__, "IPS_NONE");
+ if ((padapter->bSurpriseRemoved == 0) && (_FAIL == rtw_pwr_wakeup(padapter)))
+ return -EFAULT;
+ } else {
+ return -EINVAL;
+ }
+ return 0;
+}
diff --git a/drivers/staging/rtl8188eu/core/rtw_recv.c b/drivers/staging/rtl8188eu/core/rtw_recv.c
new file mode 100644
index 000000000..cda725a8f
--- /dev/null
+++ b/drivers/staging/rtl8188eu/core/rtw_recv.c
@@ -0,0 +1,2188 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2012 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ *
+ ******************************************************************************/
+#define _RTW_RECV_C_
+
+#include <osdep_service.h>
+#include <drv_types.h>
+#include <recv_osdep.h>
+#include <mlme_osdep.h>
+#include <wifi.h>
+#include <linux/vmalloc.h>
+
+#define ETHERNET_HEADER_SIZE 14 /* Ethernet Header Length */
+#define LLC_HEADER_SIZE 6 /* LLC Header Length */
+
+static u8 SNAP_ETH_TYPE_IPX[2] = {0x81, 0x37};
+static u8 SNAP_ETH_TYPE_APPLETALK_AARP[2] = {0x80, 0xf3};
+
+/* Bridge-Tunnel header (for EtherTypes ETH_P_AARP and ETH_P_IPX) */
+static u8 rtw_bridge_tunnel_header[] = {
+ 0xaa, 0xaa, 0x03, 0x00, 0x00, 0xf8
+};
+
+static u8 rtw_rfc1042_header[] = {
+ 0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00
+};
+
+void rtw_signal_stat_timer_hdl(unsigned long data);
+
+void _rtw_init_sta_recv_priv(struct sta_recv_priv *psta_recvpriv)
+{
+
+ memset((u8 *)psta_recvpriv, 0, sizeof(struct sta_recv_priv));
+
+ spin_lock_init(&psta_recvpriv->lock);
+
+ _rtw_init_queue(&psta_recvpriv->defrag_q);
+
+}
+
+int _rtw_init_recv_priv(struct recv_priv *precvpriv, struct adapter *padapter)
+{
+ int i;
+
+ struct recv_frame *precvframe;
+
+ int res = _SUCCESS;
+
+ _rtw_init_queue(&precvpriv->free_recv_queue);
+ _rtw_init_queue(&precvpriv->recv_pending_queue);
+ _rtw_init_queue(&precvpriv->uc_swdec_pending_queue);
+
+ precvpriv->adapter = padapter;
+
+ precvpriv->free_recvframe_cnt = NR_RECVFRAME;
+
+ precvpriv->pallocated_frame_buf = vzalloc(NR_RECVFRAME * sizeof(struct recv_frame) + RXFRAME_ALIGN_SZ);
+
+ if (precvpriv->pallocated_frame_buf == NULL) {
+ res = _FAIL;
+ goto exit;
+ }
+
+ precvpriv->precv_frame_buf = (u8 *)N_BYTE_ALIGMENT((size_t)(precvpriv->pallocated_frame_buf), RXFRAME_ALIGN_SZ);
+
+ precvframe = (struct recv_frame *)precvpriv->precv_frame_buf;
+
+ for (i = 0; i < NR_RECVFRAME; i++) {
+ INIT_LIST_HEAD(&(precvframe->list));
+
+ list_add_tail(&(precvframe->list),
+ &(precvpriv->free_recv_queue.queue));
+
+ res = rtw_os_recv_resource_alloc(padapter, precvframe);
+
+ precvframe->len = 0;
+
+ precvframe->adapter = padapter;
+ precvframe++;
+ }
+ precvpriv->rx_pending_cnt = 1;
+
+ res = rtw_hal_init_recv_priv(padapter);
+
+ setup_timer(&precvpriv->signal_stat_timer,
+ rtw_signal_stat_timer_hdl,
+ (unsigned long)padapter);
+
+ precvpriv->signal_stat_sampling_interval = 1000; /* ms */
+
+ rtw_set_signal_stat_timer(precvpriv);
+exit:
+
+
+ return res;
+}
+
+void _rtw_free_recv_priv(struct recv_priv *precvpriv)
+{
+ struct adapter *padapter = precvpriv->adapter;
+
+
+ rtw_free_uc_swdec_pending_queue(padapter);
+
+ if (precvpriv->pallocated_frame_buf) {
+ vfree(precvpriv->pallocated_frame_buf);
+ }
+
+ rtw_hal_free_recv_priv(padapter);
+
+}
+
+struct recv_frame *_rtw_alloc_recvframe(struct __queue *pfree_recv_queue)
+{
+ struct recv_frame *hdr;
+ struct list_head *plist, *phead;
+ struct adapter *padapter;
+ struct recv_priv *precvpriv;
+
+ if (list_empty(&pfree_recv_queue->queue)) {
+ hdr = NULL;
+ } else {
+ phead = get_list_head(pfree_recv_queue);
+
+ plist = phead->next;
+
+ hdr = container_of(plist, struct recv_frame, list);
+
+ list_del_init(&hdr->list);
+ padapter = hdr->adapter;
+ if (padapter != NULL) {
+ precvpriv = &padapter->recvpriv;
+ if (pfree_recv_queue == &precvpriv->free_recv_queue)
+ precvpriv->free_recvframe_cnt--;
+ }
+ }
+
+
+ return (struct recv_frame *)hdr;
+}
+
+struct recv_frame *rtw_alloc_recvframe(struct __queue *pfree_recv_queue)
+{
+ struct recv_frame *precvframe;
+
+ spin_lock_bh(&pfree_recv_queue->lock);
+
+ precvframe = _rtw_alloc_recvframe(pfree_recv_queue);
+
+ spin_unlock_bh(&pfree_recv_queue->lock);
+
+ return precvframe;
+}
+
+void rtw_init_recvframe(struct recv_frame *precvframe, struct recv_priv *precvpriv)
+{
+ /* Perry: This can be removed */
+ INIT_LIST_HEAD(&precvframe->list);
+
+ precvframe->len = 0;
+}
+
+int rtw_free_recvframe(struct recv_frame *precvframe,
+ struct __queue *pfree_recv_queue)
+{
+ struct adapter *padapter;
+ struct recv_priv *precvpriv;
+
+ if (!precvframe)
+ return _FAIL;
+ padapter = precvframe->adapter;
+ precvpriv = &padapter->recvpriv;
+ if (precvframe->pkt) {
+ dev_kfree_skb_any(precvframe->pkt);/* free skb by driver */
+ precvframe->pkt = NULL;
+ }
+
+ spin_lock_bh(&pfree_recv_queue->lock);
+
+ list_del_init(&(precvframe->list));
+
+ precvframe->len = 0;
+
+ list_add_tail(&(precvframe->list), get_list_head(pfree_recv_queue));
+
+ if (padapter != NULL) {
+ if (pfree_recv_queue == &precvpriv->free_recv_queue)
+ precvpriv->free_recvframe_cnt++;
+ }
+
+ spin_unlock_bh(&pfree_recv_queue->lock);
+
+
+ return _SUCCESS;
+}
+
+int _rtw_enqueue_recvframe(struct recv_frame *precvframe, struct __queue *queue)
+{
+ struct adapter *padapter = precvframe->adapter;
+ struct recv_priv *precvpriv = &padapter->recvpriv;
+
+
+ list_del_init(&(precvframe->list));
+ list_add_tail(&(precvframe->list), get_list_head(queue));
+
+ if (padapter != NULL) {
+ if (queue == &precvpriv->free_recv_queue)
+ precvpriv->free_recvframe_cnt++;
+ }
+
+
+ return _SUCCESS;
+}
+
+int rtw_enqueue_recvframe(struct recv_frame *precvframe, struct __queue *queue)
+{
+ int ret;
+
+ spin_lock_bh(&queue->lock);
+ ret = _rtw_enqueue_recvframe(precvframe, queue);
+ spin_unlock_bh(&queue->lock);
+
+ return ret;
+}
+
+/*
+caller : defrag ; recvframe_chk_defrag in recv_thread (passive)
+pframequeue: defrag_queue : will be accessed in recv_thread (passive)
+
+using spinlock to protect
+
+*/
+
+void rtw_free_recvframe_queue(struct __queue *pframequeue, struct __queue *pfree_recv_queue)
+{
+ struct recv_frame *hdr;
+ struct list_head *plist, *phead;
+
+ spin_lock(&pframequeue->lock);
+
+ phead = get_list_head(pframequeue);
+ plist = phead->next;
+
+ while (phead != plist) {
+ hdr = container_of(plist, struct recv_frame, list);
+
+ plist = plist->next;
+
+ rtw_free_recvframe((struct recv_frame *)hdr, pfree_recv_queue);
+ }
+
+ spin_unlock(&pframequeue->lock);
+
+}
+
+u32 rtw_free_uc_swdec_pending_queue(struct adapter *adapter)
+{
+ u32 cnt = 0;
+ struct recv_frame *pending_frame;
+ while ((pending_frame = rtw_alloc_recvframe(&adapter->recvpriv.uc_swdec_pending_queue))) {
+ rtw_free_recvframe(pending_frame, &adapter->recvpriv.free_recv_queue);
+ DBG_88E("%s: dequeue uc_swdec_pending_queue\n", __func__);
+ cnt++;
+ }
+
+ return cnt;
+}
+
+static int recvframe_chkmic(struct adapter *adapter,
+ struct recv_frame *precvframe)
+{
+ int i, res = _SUCCESS;
+ u32 datalen;
+ u8 miccode[8];
+ u8 bmic_err = false, brpt_micerror = true;
+ u8 *pframe, *payload, *pframemic;
+ u8 *mickey;
+ struct sta_info *stainfo;
+ struct rx_pkt_attrib *prxattrib = &precvframe->attrib;
+ struct security_priv *psecuritypriv = &adapter->securitypriv;
+
+ struct mlme_ext_priv *pmlmeext = &adapter->mlmeextpriv;
+ struct mlme_ext_info *pmlmeinfo = &(pmlmeext->mlmext_info);
+
+ stainfo = rtw_get_stainfo(&adapter->stapriv, &prxattrib->ta[0]);
+
+ if (prxattrib->encrypt == _TKIP_) {
+ RT_TRACE(_module_rtl871x_recv_c_, _drv_info_, ("\n recvframe_chkmic:prxattrib->encrypt==_TKIP_\n"));
+ RT_TRACE(_module_rtl871x_recv_c_, _drv_info_, ("\n recvframe_chkmic:da=0x%02x:0x%02x:0x%02x:0x%02x:0x%02x:0x%02x\n",
+ prxattrib->ra[0], prxattrib->ra[1], prxattrib->ra[2], prxattrib->ra[3], prxattrib->ra[4], prxattrib->ra[5]));
+
+ /* calculate mic code */
+ if (stainfo != NULL) {
+ if (IS_MCAST(prxattrib->ra)) {
+ if (!psecuritypriv) {
+ res = _FAIL;
+ RT_TRACE(_module_rtl871x_recv_c_, _drv_err_, ("\n recvframe_chkmic:didn't install group key!!!!!!!!!!\n"));
+ DBG_88E("\n recvframe_chkmic:didn't install group key!!!!!!!!!!\n");
+ goto exit;
+ }
+ mickey = &psecuritypriv->dot118021XGrprxmickey[prxattrib->key_index].skey[0];
+
+ RT_TRACE(_module_rtl871x_recv_c_, _drv_info_, ("\n recvframe_chkmic: bcmc key\n"));
+ } else {
+ mickey = &stainfo->dot11tkiprxmickey.skey[0];
+ RT_TRACE(_module_rtl871x_recv_c_, _drv_err_, ("\n recvframe_chkmic: unicast key\n"));
+ }
+
+ /* icv_len included the mic code */
+ datalen = precvframe->len-prxattrib->hdrlen -
+ prxattrib->iv_len-prxattrib->icv_len-8;
+ pframe = precvframe->rx_data;
+ payload = pframe+prxattrib->hdrlen+prxattrib->iv_len;
+
+ RT_TRACE(_module_rtl871x_recv_c_, _drv_info_, ("\n prxattrib->iv_len=%d prxattrib->icv_len=%d\n", prxattrib->iv_len, prxattrib->icv_len));
+ rtw_seccalctkipmic(mickey, pframe, payload, datalen, &miccode[0],
+ (unsigned char)prxattrib->priority); /* care the length of the data */
+
+ pframemic = payload+datalen;
+
+ bmic_err = false;
+
+ for (i = 0; i < 8; i++) {
+ if (miccode[i] != *(pframemic+i)) {
+ RT_TRACE(_module_rtl871x_recv_c_, _drv_err_,
+ ("recvframe_chkmic:miccode[%d](%02x)!=*(pframemic+%d)(%02x) ",
+ i, miccode[i], i, *(pframemic+i)));
+ bmic_err = true;
+ }
+ }
+
+ if (bmic_err) {
+ RT_TRACE(_module_rtl871x_recv_c_, _drv_err_,
+ ("\n *(pframemic-8)-*(pframemic-1)=0x%02x:0x%02x:0x%02x:0x%02x:0x%02x:0x%02x:0x%02x:0x%02x\n",
+ *(pframemic-8), *(pframemic-7), *(pframemic-6),
+ *(pframemic-5), *(pframemic-4), *(pframemic-3),
+ *(pframemic-2), *(pframemic-1)));
+ RT_TRACE(_module_rtl871x_recv_c_, _drv_err_,
+ ("\n *(pframemic-16)-*(pframemic-9)=0x%02x:0x%02x:0x%02x:0x%02x:0x%02x:0x%02x:0x%02x:0x%02x\n",
+ *(pframemic-16), *(pframemic-15), *(pframemic-14),
+ *(pframemic-13), *(pframemic-12), *(pframemic-11),
+ *(pframemic-10), *(pframemic-9)));
+ {
+ uint i;
+ RT_TRACE(_module_rtl871x_recv_c_, _drv_err_,
+ ("\n ======demp packet (len=%d)======\n",
+ precvframe->len));
+ for (i = 0; i < precvframe->len; i += 8) {
+ RT_TRACE(_module_rtl871x_recv_c_,
+ _drv_err_,
+ ("0x%02x:0x%02x:0x%02x:0x%02x:0x%02x:0x%02x:0x%02x:0x%02x",
+ *(precvframe->rx_data+i),
+ *(precvframe->rx_data+i+1),
+ *(precvframe->rx_data+i+2),
+ *(precvframe->rx_data+i+3),
+ *(precvframe->rx_data+i+4),
+ *(precvframe->rx_data+i+5),
+ *(precvframe->rx_data+i+6),
+ *(precvframe->rx_data+i+7)));
+ }
+ RT_TRACE(_module_rtl871x_recv_c_,
+ _drv_err_,
+ ("\n ====== demp packet end [len=%d]======\n",
+ precvframe->len));
+ RT_TRACE(_module_rtl871x_recv_c_,
+ _drv_err_,
+ ("\n hrdlen=%d,\n",
+ prxattrib->hdrlen));
+ }
+
+ RT_TRACE(_module_rtl871x_recv_c_, _drv_err_,
+ ("ra=0x%.2x 0x%.2x 0x%.2x 0x%.2x 0x%.2x 0x%.2x psecuritypriv->binstallGrpkey=%d ",
+ prxattrib->ra[0], prxattrib->ra[1], prxattrib->ra[2],
+ prxattrib->ra[3], prxattrib->ra[4], prxattrib->ra[5], psecuritypriv->binstallGrpkey));
+
+ /* double check key_index for some timing issue , */
+ /* cannot compare with psecuritypriv->dot118021XGrpKeyid also cause timing issue */
+ if ((IS_MCAST(prxattrib->ra) == true) && (prxattrib->key_index != pmlmeinfo->key_index))
+ brpt_micerror = false;
+
+ if ((prxattrib->bdecrypted) && (brpt_micerror)) {
+ rtw_handle_tkip_mic_err(adapter, (u8)IS_MCAST(prxattrib->ra));
+ RT_TRACE(_module_rtl871x_recv_c_, _drv_err_, (" mic error :prxattrib->bdecrypted=%d ", prxattrib->bdecrypted));
+ DBG_88E(" mic error :prxattrib->bdecrypted=%d\n", prxattrib->bdecrypted);
+ } else {
+ RT_TRACE(_module_rtl871x_recv_c_, _drv_err_, (" mic error :prxattrib->bdecrypted=%d ", prxattrib->bdecrypted));
+ DBG_88E(" mic error :prxattrib->bdecrypted=%d\n", prxattrib->bdecrypted);
+ }
+ res = _FAIL;
+ } else {
+ /* mic checked ok */
+ if ((!psecuritypriv->bcheck_grpkey) && (IS_MCAST(prxattrib->ra))) {
+ psecuritypriv->bcheck_grpkey = true;
+ RT_TRACE(_module_rtl871x_recv_c_, _drv_err_, ("psecuritypriv->bcheck_grpkey = true"));
+ }
+ }
+ } else {
+ RT_TRACE(_module_rtl871x_recv_c_, _drv_err_, ("recvframe_chkmic: rtw_get_stainfo==NULL!!!\n"));
+ }
+
+ recvframe_pull_tail(precvframe, 8);
+ }
+
+exit:
+
+
+ return res;
+}
+
+/* decrypt and set the ivlen, icvlen of the recv_frame */
+static struct recv_frame *decryptor(struct adapter *padapter,
+ struct recv_frame *precv_frame)
+{
+ struct rx_pkt_attrib *prxattrib = &precv_frame->attrib;
+ struct security_priv *psecuritypriv = &padapter->securitypriv;
+ struct recv_frame *return_packet = precv_frame;
+ u32 res = _SUCCESS;
+
+ RT_TRACE(_module_rtl871x_recv_c_, _drv_info_, ("prxstat->decrypted=%x prxattrib->encrypt=0x%03x\n", prxattrib->bdecrypted, prxattrib->encrypt));
+
+ if (prxattrib->encrypt > 0) {
+ u8 *iv = precv_frame->rx_data+prxattrib->hdrlen;
+ prxattrib->key_index = (((iv[3])>>6)&0x3);
+
+ if (prxattrib->key_index > WEP_KEYS) {
+ DBG_88E("prxattrib->key_index(%d)>WEP_KEYS\n", prxattrib->key_index);
+
+ switch (prxattrib->encrypt) {
+ case _WEP40_:
+ case _WEP104_:
+ prxattrib->key_index = psecuritypriv->dot11PrivacyKeyIndex;
+ break;
+ case _TKIP_:
+ case _AES_:
+ default:
+ prxattrib->key_index = psecuritypriv->dot118021XGrpKeyid;
+ break;
+ }
+ }
+ }
+
+ if ((prxattrib->encrypt > 0) && ((prxattrib->bdecrypted == 0) || (psecuritypriv->sw_decrypt))) {
+ psecuritypriv->hw_decrypted = false;
+
+ switch (prxattrib->encrypt) {
+ case _WEP40_:
+ case _WEP104_:
+ rtw_wep_decrypt(padapter, (u8 *)precv_frame);
+ break;
+ case _TKIP_:
+ res = rtw_tkip_decrypt(padapter, (u8 *)precv_frame);
+ break;
+ case _AES_:
+ res = rtw_aes_decrypt(padapter, (u8 *)precv_frame);
+ break;
+ default:
+ break;
+ }
+ } else if (prxattrib->bdecrypted == 1 && prxattrib->encrypt > 0 &&
+ (psecuritypriv->busetkipkey == 1 || prxattrib->encrypt != _TKIP_))
+ psecuritypriv->hw_decrypted = true;
+
+ if (res == _FAIL) {
+ rtw_free_recvframe(return_packet, &padapter->recvpriv.free_recv_queue);
+ return_packet = NULL;
+ }
+
+
+ return return_packet;
+}
+
+/* set the security information in the recv_frame */
+static struct recv_frame *portctrl(struct adapter *adapter,
+ struct recv_frame *precv_frame)
+{
+ u8 *psta_addr, *ptr;
+ uint auth_alg;
+ struct recv_frame *pfhdr;
+ struct sta_info *psta;
+ struct sta_priv *pstapriv;
+ struct recv_frame *prtnframe;
+ u16 ether_type;
+ u16 eapol_type = 0x888e;/* for Funia BD's WPA issue */
+ struct rx_pkt_attrib *pattrib;
+ __be16 be_tmp;
+
+
+ pstapriv = &adapter->stapriv;
+
+ auth_alg = adapter->securitypriv.dot11AuthAlgrthm;
+
+ ptr = precv_frame->rx_data;
+ pfhdr = precv_frame;
+ pattrib = &pfhdr->attrib;
+ psta_addr = pattrib->ta;
+ psta = rtw_get_stainfo(pstapriv, psta_addr);
+
+ prtnframe = NULL;
+
+ RT_TRACE(_module_rtl871x_recv_c_, _drv_info_, ("########portctrl:adapter->securitypriv.dot11AuthAlgrthm=%d\n", adapter->securitypriv.dot11AuthAlgrthm));
+
+ if (auth_alg == 2) {
+ /* get ether_type */
+ ptr = ptr + pfhdr->attrib.hdrlen + LLC_HEADER_SIZE;
+ memcpy(&be_tmp, ptr, 2);
+ ether_type = ntohs(be_tmp);
+
+ if ((psta != NULL) && (psta->ieee8021x_blocked)) {
+ /* blocked */
+ /* only accept EAPOL frame */
+ RT_TRACE(_module_rtl871x_recv_c_, _drv_info_, ("########portctrl:psta->ieee8021x_blocked==1\n"));
+
+ if (ether_type == eapol_type) {
+ prtnframe = precv_frame;
+ } else {
+ /* free this frame */
+ rtw_free_recvframe(precv_frame, &adapter->recvpriv.free_recv_queue);
+ prtnframe = NULL;
+ }
+ } else {
+ /* allowed */
+ /* check decryption status, and decrypt the frame if needed */
+ RT_TRACE(_module_rtl871x_recv_c_, _drv_info_, ("########portctrl:psta->ieee8021x_blocked==0\n"));
+ RT_TRACE(_module_rtl871x_recv_c_, _drv_info_,
+ ("portctrl:precv_frame->hdr.attrib.privacy=%x\n",
+ precv_frame->attrib.privacy));
+
+ if (pattrib->bdecrypted == 0)
+ RT_TRACE(_module_rtl871x_recv_c_, _drv_info_, ("portctrl:prxstat->decrypted=%x\n", pattrib->bdecrypted));
+
+ prtnframe = precv_frame;
+ /* check is the EAPOL frame or not (Rekey) */
+ if (ether_type == eapol_type) {
+ RT_TRACE(_module_rtl871x_recv_c_, _drv_notice_, ("########portctrl:ether_type==0x888e\n"));
+ /* check Rekey */
+
+ prtnframe = precv_frame;
+ } else {
+ RT_TRACE(_module_rtl871x_recv_c_, _drv_info_, ("########portctrl:ether_type=0x%04x\n", ether_type));
+ }
+ }
+ } else {
+ prtnframe = precv_frame;
+ }
+
+
+ return prtnframe;
+}
+
+static int recv_decache(struct recv_frame *precv_frame, u8 bretry,
+ struct stainfo_rxcache *prxcache)
+{
+ int tid = precv_frame->attrib.priority;
+
+ u16 seq_ctrl = ((precv_frame->attrib.seq_num&0xffff) << 4) |
+ (precv_frame->attrib.frag_num & 0xf);
+
+
+ if (tid > 15) {
+ RT_TRACE(_module_rtl871x_recv_c_, _drv_notice_, ("recv_decache, (tid>15)! seq_ctrl=0x%x, tid=0x%x\n", seq_ctrl, tid));
+
+ return _FAIL;
+ }
+
+ if (1) {/* if (bretry) */
+ if (seq_ctrl == prxcache->tid_rxseq[tid]) {
+ RT_TRACE(_module_rtl871x_recv_c_, _drv_notice_, ("recv_decache, seq_ctrl=0x%x, tid=0x%x, tid_rxseq=0x%x\n", seq_ctrl, tid, prxcache->tid_rxseq[tid]));
+
+ return _FAIL;
+ }
+ }
+
+ prxcache->tid_rxseq[tid] = seq_ctrl;
+
+
+ return _SUCCESS;
+}
+
+static void process_pwrbit_data(struct adapter *padapter,
+ struct recv_frame *precv_frame)
+{
+#ifdef CONFIG_88EU_AP_MODE
+ unsigned char pwrbit;
+ u8 *ptr = precv_frame->rx_data;
+ struct rx_pkt_attrib *pattrib = &precv_frame->attrib;
+ struct sta_priv *pstapriv = &padapter->stapriv;
+ struct sta_info *psta = NULL;
+
+ psta = rtw_get_stainfo(pstapriv, pattrib->src);
+
+ pwrbit = GetPwrMgt(ptr);
+
+ if (psta) {
+ if (pwrbit) {
+ if (!(psta->state & WIFI_SLEEP_STATE))
+ stop_sta_xmit(padapter, psta);
+ } else {
+ if (psta->state & WIFI_SLEEP_STATE)
+ wakeup_sta_to_xmit(padapter, psta);
+ }
+ }
+
+#endif
+}
+
+static void process_wmmps_data(struct adapter *padapter,
+ struct recv_frame *precv_frame)
+{
+#ifdef CONFIG_88EU_AP_MODE
+ struct rx_pkt_attrib *pattrib = &precv_frame->attrib;
+ struct sta_priv *pstapriv = &padapter->stapriv;
+ struct sta_info *psta = NULL;
+
+ psta = rtw_get_stainfo(pstapriv, pattrib->src);
+
+ if (!psta)
+ return;
+
+ if (!psta->qos_option)
+ return;
+
+ if (!(psta->qos_info&0xf))
+ return;
+
+ if (psta->state&WIFI_SLEEP_STATE) {
+ u8 wmmps_ac = 0;
+
+ switch (pattrib->priority) {
+ case 1:
+ case 2:
+ wmmps_ac = psta->uapsd_bk&BIT(1);
+ break;
+ case 4:
+ case 5:
+ wmmps_ac = psta->uapsd_vi&BIT(1);
+ break;
+ case 6:
+ case 7:
+ wmmps_ac = psta->uapsd_vo&BIT(1);
+ break;
+ case 0:
+ case 3:
+ default:
+ wmmps_ac = psta->uapsd_be&BIT(1);
+ break;
+ }
+
+ if (wmmps_ac) {
+ if (psta->sleepq_ac_len > 0) {
+ /* process received triggered frame */
+ xmit_delivery_enabled_frames(padapter, psta);
+ } else {
+ /* issue one qos null frame with More data bit = 0 and the EOSP bit set (= 1) */
+ issue_qos_nulldata(padapter, psta->hwaddr, (u16)pattrib->priority, 0, 0);
+ }
+ }
+ }
+
+#endif
+}
+
+static void count_rx_stats(struct adapter *padapter,
+ struct recv_frame *prframe,
+ struct sta_info *sta)
+{
+ int sz;
+ struct sta_info *psta = NULL;
+ struct stainfo_stats *pstats = NULL;
+ struct rx_pkt_attrib *pattrib = &prframe->attrib;
+ struct recv_priv *precvpriv = &padapter->recvpriv;
+
+ sz = prframe->len;
+ precvpriv->rx_bytes += sz;
+
+ padapter->mlmepriv.LinkDetectInfo.NumRxOkInPeriod++;
+
+ if ((!MacAddr_isBcst(pattrib->dst)) && (!IS_MCAST(pattrib->dst)))
+ padapter->mlmepriv.LinkDetectInfo.NumRxUnicastOkInPeriod++;
+
+ if (sta)
+ psta = sta;
+ else
+ psta = prframe->psta;
+
+ if (psta) {
+ pstats = &psta->sta_stats;
+
+ pstats->rx_data_pkts++;
+ pstats->rx_bytes += sz;
+ }
+}
+
+int sta2sta_data_frame(
+ struct adapter *adapter,
+ struct recv_frame *precv_frame,
+ struct sta_info **psta
+);
+
+int sta2sta_data_frame(struct adapter *adapter, struct recv_frame *precv_frame,
+ struct sta_info **psta)
+{
+ u8 *ptr = precv_frame->rx_data;
+ int ret = _SUCCESS;
+ struct rx_pkt_attrib *pattrib = &precv_frame->attrib;
+ struct sta_priv *pstapriv = &adapter->stapriv;
+ struct mlme_priv *pmlmepriv = &adapter->mlmepriv;
+ u8 *mybssid = get_bssid(pmlmepriv);
+ u8 *myhwaddr = myid(&adapter->eeprompriv);
+ u8 *sta_addr = NULL;
+ int bmcast = IS_MCAST(pattrib->dst);
+
+
+ if ((check_fwstate(pmlmepriv, WIFI_ADHOC_STATE) == true) ||
+ (check_fwstate(pmlmepriv, WIFI_ADHOC_MASTER_STATE) == true)) {
+ /* filter packets that SA is myself or multicast or broadcast */
+ if (!memcmp(myhwaddr, pattrib->src, ETH_ALEN)) {
+ RT_TRACE(_module_rtl871x_recv_c_, _drv_err_, (" SA==myself\n"));
+ ret = _FAIL;
+ goto exit;
+ }
+
+ if ((memcmp(myhwaddr, pattrib->dst, ETH_ALEN)) && (!bmcast)) {
+ ret = _FAIL;
+ goto exit;
+ }
+
+ if (!memcmp(pattrib->bssid, "\x0\x0\x0\x0\x0\x0", ETH_ALEN) ||
+ !memcmp(mybssid, "\x0\x0\x0\x0\x0\x0", ETH_ALEN) ||
+ memcmp(pattrib->bssid, mybssid, ETH_ALEN)) {
+ ret = _FAIL;
+ goto exit;
+ }
+
+ sta_addr = pattrib->src;
+ } else if (check_fwstate(pmlmepriv, WIFI_STATION_STATE)) {
+ /* For Station mode, sa and bssid should always be BSSID, and DA is my mac-address */
+ if (memcmp(pattrib->bssid, pattrib->src, ETH_ALEN)) {
+ RT_TRACE(_module_rtl871x_recv_c_, _drv_err_, ("bssid!=TA under STATION_MODE; drop pkt\n"));
+ ret = _FAIL;
+ goto exit;
+ }
+ sta_addr = pattrib->bssid;
+ } else if (check_fwstate(pmlmepriv, WIFI_AP_STATE)) {
+ if (bmcast) {
+ /* For AP mode, if DA == MCAST, then BSSID should be also MCAST */
+ if (!IS_MCAST(pattrib->bssid)) {
+ ret = _FAIL;
+ goto exit;
+ }
+ } else { /* not mc-frame */
+ /* For AP mode, if DA is non-MCAST, then it must be BSSID, and bssid == BSSID */
+ if (memcmp(pattrib->bssid, pattrib->dst, ETH_ALEN)) {
+ ret = _FAIL;
+ goto exit;
+ }
+
+ sta_addr = pattrib->src;
+ }
+ } else if (check_fwstate(pmlmepriv, WIFI_MP_STATE)) {
+ memcpy(pattrib->dst, GetAddr1Ptr(ptr), ETH_ALEN);
+ memcpy(pattrib->src, GetAddr2Ptr(ptr), ETH_ALEN);
+ memcpy(pattrib->bssid, GetAddr3Ptr(ptr), ETH_ALEN);
+ memcpy(pattrib->ra, pattrib->dst, ETH_ALEN);
+ memcpy(pattrib->ta, pattrib->src, ETH_ALEN);
+
+ sta_addr = mybssid;
+ } else {
+ ret = _FAIL;
+ }
+
+ if (bmcast)
+ *psta = rtw_get_bcmc_stainfo(adapter);
+ else
+ *psta = rtw_get_stainfo(pstapriv, sta_addr); /* get ap_info */
+
+ if (*psta == NULL) {
+ RT_TRACE(_module_rtl871x_recv_c_, _drv_err_, ("can't get psta under sta2sta_data_frame ; drop pkt\n"));
+ ret = _FAIL;
+ goto exit;
+ }
+
+exit:
+ return ret;
+}
+
+static int ap2sta_data_frame(
+ struct adapter *adapter,
+ struct recv_frame *precv_frame,
+ struct sta_info **psta)
+{
+ u8 *ptr = precv_frame->rx_data;
+ struct rx_pkt_attrib *pattrib = &precv_frame->attrib;
+ int ret = _SUCCESS;
+ struct sta_priv *pstapriv = &adapter->stapriv;
+ struct mlme_priv *pmlmepriv = &adapter->mlmepriv;
+ u8 *mybssid = get_bssid(pmlmepriv);
+ u8 *myhwaddr = myid(&adapter->eeprompriv);
+ int bmcast = IS_MCAST(pattrib->dst);
+
+
+ if ((check_fwstate(pmlmepriv, WIFI_STATION_STATE) == true) &&
+ (check_fwstate(pmlmepriv, _FW_LINKED) == true ||
+ check_fwstate(pmlmepriv, _FW_UNDER_LINKING))) {
+ /* filter packets that SA is myself or multicast or broadcast */
+ if (!memcmp(myhwaddr, pattrib->src, ETH_ALEN)) {
+ RT_TRACE(_module_rtl871x_recv_c_, _drv_err_, (" SA==myself\n"));
+ ret = _FAIL;
+ goto exit;
+ }
+
+ /* da should be for me */
+ if ((memcmp(myhwaddr, pattrib->dst, ETH_ALEN)) && (!bmcast)) {
+ RT_TRACE(_module_rtl871x_recv_c_, _drv_info_,
+ (" ap2sta_data_frame: compare DA fail; DA=%pM\n", (pattrib->dst)));
+ ret = _FAIL;
+ goto exit;
+ }
+
+ /* check BSSID */
+ if (!memcmp(pattrib->bssid, "\x0\x0\x0\x0\x0\x0", ETH_ALEN) ||
+ !memcmp(mybssid, "\x0\x0\x0\x0\x0\x0", ETH_ALEN) ||
+ (memcmp(pattrib->bssid, mybssid, ETH_ALEN))) {
+ RT_TRACE(_module_rtl871x_recv_c_, _drv_info_,
+ (" ap2sta_data_frame: compare BSSID fail ; BSSID=%pM\n", (pattrib->bssid)));
+ RT_TRACE(_module_rtl871x_recv_c_, _drv_info_, ("mybssid=%pM\n", (mybssid)));
+
+ if (!bmcast) {
+ DBG_88E("issue_deauth to the nonassociated ap=%pM for the reason(7)\n", (pattrib->bssid));
+ issue_deauth(adapter, pattrib->bssid, WLAN_REASON_CLASS3_FRAME_FROM_NONASSOC_STA);
+ }
+
+ ret = _FAIL;
+ goto exit;
+ }
+
+ if (bmcast)
+ *psta = rtw_get_bcmc_stainfo(adapter);
+ else
+ *psta = rtw_get_stainfo(pstapriv, pattrib->bssid); /* get ap_info */
+
+ if (*psta == NULL) {
+ RT_TRACE(_module_rtl871x_recv_c_, _drv_err_, ("ap2sta: can't get psta under STATION_MODE ; drop pkt\n"));
+ ret = _FAIL;
+ goto exit;
+ }
+
+ /* if ((GetFrameSubType(ptr) & WIFI_QOS_DATA_TYPE) == WIFI_QOS_DATA_TYPE) { */
+ /* */
+
+ if (GetFrameSubType(ptr) & BIT(6)) {
+ /* No data, will not indicate to upper layer, temporily count it here */
+ count_rx_stats(adapter, precv_frame, *psta);
+ ret = RTW_RX_HANDLED;
+ goto exit;
+ }
+ } else if ((check_fwstate(pmlmepriv, WIFI_MP_STATE) == true) &&
+ (check_fwstate(pmlmepriv, _FW_LINKED) == true)) {
+ memcpy(pattrib->dst, GetAddr1Ptr(ptr), ETH_ALEN);
+ memcpy(pattrib->src, GetAddr2Ptr(ptr), ETH_ALEN);
+ memcpy(pattrib->bssid, GetAddr3Ptr(ptr), ETH_ALEN);
+ memcpy(pattrib->ra, pattrib->dst, ETH_ALEN);
+ memcpy(pattrib->ta, pattrib->src, ETH_ALEN);
+
+ /* */
+ memcpy(pattrib->bssid, mybssid, ETH_ALEN);
+
+ *psta = rtw_get_stainfo(pstapriv, pattrib->bssid); /* get sta_info */
+ if (*psta == NULL) {
+ RT_TRACE(_module_rtl871x_recv_c_, _drv_err_, ("can't get psta under MP_MODE ; drop pkt\n"));
+ ret = _FAIL;
+ goto exit;
+ }
+ } else if (check_fwstate(pmlmepriv, WIFI_AP_STATE)) {
+ /* Special case */
+ ret = RTW_RX_HANDLED;
+ goto exit;
+ } else {
+ if (!memcmp(myhwaddr, pattrib->dst, ETH_ALEN) && (!bmcast)) {
+ *psta = rtw_get_stainfo(pstapriv, pattrib->bssid); /* get sta_info */
+ if (*psta == NULL) {
+ DBG_88E("issue_deauth to the ap =%pM for the reason(7)\n", (pattrib->bssid));
+
+ issue_deauth(adapter, pattrib->bssid, WLAN_REASON_CLASS3_FRAME_FROM_NONASSOC_STA);
+ }
+ }
+
+ ret = _FAIL;
+ }
+
+exit:
+
+
+ return ret;
+}
+
+static int sta2ap_data_frame(struct adapter *adapter,
+ struct recv_frame *precv_frame,
+ struct sta_info **psta)
+{
+ struct rx_pkt_attrib *pattrib = &precv_frame->attrib;
+ struct sta_priv *pstapriv = &adapter->stapriv;
+ struct mlme_priv *pmlmepriv = &adapter->mlmepriv;
+ u8 *ptr = precv_frame->rx_data;
+ unsigned char *mybssid = get_bssid(pmlmepriv);
+ int ret = _SUCCESS;
+
+
+ if (check_fwstate(pmlmepriv, WIFI_AP_STATE) == true) {
+ /* For AP mode, RA = BSSID, TX = STA(SRC_ADDR), A3 = DST_ADDR */
+ if (memcmp(pattrib->bssid, mybssid, ETH_ALEN)) {
+ ret = _FAIL;
+ goto exit;
+ }
+
+ *psta = rtw_get_stainfo(pstapriv, pattrib->src);
+ if (*psta == NULL) {
+ RT_TRACE(_module_rtl871x_recv_c_, _drv_err_, ("can't get psta under AP_MODE; drop pkt\n"));
+ DBG_88E("issue_deauth to sta=%pM for the reason(7)\n", (pattrib->src));
+
+ issue_deauth(adapter, pattrib->src, WLAN_REASON_CLASS3_FRAME_FROM_NONASSOC_STA);
+
+ ret = RTW_RX_HANDLED;
+ goto exit;
+ }
+
+ process_pwrbit_data(adapter, precv_frame);
+
+ if ((GetFrameSubType(ptr) & WIFI_QOS_DATA_TYPE) == WIFI_QOS_DATA_TYPE) {
+ process_wmmps_data(adapter, precv_frame);
+ }
+
+ if (GetFrameSubType(ptr) & BIT(6)) {
+ /* No data, will not indicate to upper layer, temporily count it here */
+ count_rx_stats(adapter, precv_frame, *psta);
+ ret = RTW_RX_HANDLED;
+ goto exit;
+ }
+ } else {
+ u8 *myhwaddr = myid(&adapter->eeprompriv);
+ if (memcmp(pattrib->ra, myhwaddr, ETH_ALEN)) {
+ ret = RTW_RX_HANDLED;
+ goto exit;
+ }
+ DBG_88E("issue_deauth to sta=%pM for the reason(7)\n", (pattrib->src));
+ issue_deauth(adapter, pattrib->src, WLAN_REASON_CLASS3_FRAME_FROM_NONASSOC_STA);
+ ret = RTW_RX_HANDLED;
+ goto exit;
+ }
+
+exit:
+
+
+ return ret;
+}
+
+static int validate_recv_ctrl_frame(struct adapter *padapter,
+ struct recv_frame *precv_frame)
+{
+#ifdef CONFIG_88EU_AP_MODE
+ struct rx_pkt_attrib *pattrib = &precv_frame->attrib;
+ struct sta_priv *pstapriv = &padapter->stapriv;
+ u8 *pframe = precv_frame->rx_data;
+
+ if (GetFrameType(pframe) != WIFI_CTRL_TYPE)
+ return _FAIL;
+
+ /* receive the frames that ra(a1) is my address */
+ if (memcmp(GetAddr1Ptr(pframe), myid(&padapter->eeprompriv), ETH_ALEN))
+ return _FAIL;
+
+ /* only handle ps-poll */
+ if (GetFrameSubType(pframe) == WIFI_PSPOLL) {
+ u16 aid;
+ u8 wmmps_ac = 0;
+ struct sta_info *psta = NULL;
+
+ aid = GetAid(pframe);
+ psta = rtw_get_stainfo(pstapriv, GetAddr2Ptr(pframe));
+
+ if ((psta == NULL) || (psta->aid != aid))
+ return _FAIL;
+
+ /* for rx pkt statistics */
+ psta->sta_stats.rx_ctrl_pkts++;
+
+ switch (pattrib->priority) {
+ case 1:
+ case 2:
+ wmmps_ac = psta->uapsd_bk&BIT(0);
+ break;
+ case 4:
+ case 5:
+ wmmps_ac = psta->uapsd_vi&BIT(0);
+ break;
+ case 6:
+ case 7:
+ wmmps_ac = psta->uapsd_vo&BIT(0);
+ break;
+ case 0:
+ case 3:
+ default:
+ wmmps_ac = psta->uapsd_be&BIT(0);
+ break;
+ }
+
+ if (wmmps_ac)
+ return _FAIL;
+
+ if (psta->state & WIFI_STA_ALIVE_CHK_STATE) {
+ DBG_88E("%s alive check-rx ps-poll\n", __func__);
+ psta->expire_to = pstapriv->expire_to;
+ psta->state ^= WIFI_STA_ALIVE_CHK_STATE;
+ }
+
+ if ((psta->state&WIFI_SLEEP_STATE) && (pstapriv->sta_dz_bitmap&BIT(psta->aid))) {
+ struct list_head *xmitframe_plist, *xmitframe_phead;
+ struct xmit_frame *pxmitframe = NULL;
+
+ spin_lock_bh(&psta->sleep_q.lock);
+
+ xmitframe_phead = get_list_head(&psta->sleep_q);
+ xmitframe_plist = xmitframe_phead->next;
+
+ if (xmitframe_phead != xmitframe_plist) {
+ pxmitframe = container_of(xmitframe_plist, struct xmit_frame, list);
+
+ xmitframe_plist = xmitframe_plist->next;
+
+ list_del_init(&pxmitframe->list);
+
+ psta->sleepq_len--;
+
+ if (psta->sleepq_len > 0)
+ pxmitframe->attrib.mdata = 1;
+ else
+ pxmitframe->attrib.mdata = 0;
+
+ pxmitframe->attrib.triggered = 1;
+
+ spin_unlock_bh(&psta->sleep_q.lock);
+ if (rtw_hal_xmit(padapter, pxmitframe) == true)
+ rtw_os_xmit_complete(padapter, pxmitframe);
+ spin_lock_bh(&psta->sleep_q.lock);
+
+ if (psta->sleepq_len == 0) {
+ pstapriv->tim_bitmap &= ~BIT(psta->aid);
+
+ /* update BCN for TIM IE */
+ /* update_BCNTIM(padapter); */
+ update_beacon(padapter, _TIM_IE_, NULL, false);
+ }
+ } else {
+ if (pstapriv->tim_bitmap&BIT(psta->aid)) {
+ if (psta->sleepq_len == 0) {
+ DBG_88E("no buffered packets to xmit\n");
+
+ /* issue nulldata with More data bit = 0 to indicate we have no buffered packets */
+ issue_nulldata(padapter, psta->hwaddr, 0, 0, 0);
+ } else {
+ DBG_88E("error!psta->sleepq_len=%d\n", psta->sleepq_len);
+ psta->sleepq_len = 0;
+ }
+
+ pstapriv->tim_bitmap &= ~BIT(psta->aid);
+
+ /* update BCN for TIM IE */
+ /* update_BCNTIM(padapter); */
+ update_beacon(padapter, _TIM_IE_, NULL, false);
+ }
+ }
+
+ spin_unlock_bh(&psta->sleep_q.lock);
+ }
+ }
+
+#endif
+
+ return _FAIL;
+}
+
+struct recv_frame *recvframe_chk_defrag(struct adapter *padapter,
+ struct recv_frame *precv_frame);
+
+static int validate_recv_mgnt_frame(struct adapter *padapter,
+ struct recv_frame *precv_frame)
+{
+ struct sta_info *psta;
+
+ RT_TRACE(_module_rtl871x_recv_c_, _drv_info_, ("+validate_recv_mgnt_frame\n"));
+
+ precv_frame = recvframe_chk_defrag(padapter, precv_frame);
+ if (precv_frame == NULL) {
+ RT_TRACE(_module_rtl871x_recv_c_, _drv_notice_, ("%s: fragment packet\n", __func__));
+ return _SUCCESS;
+ }
+
+ /* for rx pkt statistics */
+ psta = rtw_get_stainfo(&padapter->stapriv,
+ GetAddr2Ptr(precv_frame->rx_data));
+ if (psta) {
+ psta->sta_stats.rx_mgnt_pkts++;
+ if (GetFrameSubType(precv_frame->rx_data) == WIFI_BEACON) {
+ psta->sta_stats.rx_beacon_pkts++;
+ } else if (GetFrameSubType(precv_frame->rx_data) == WIFI_PROBEREQ) {
+ psta->sta_stats.rx_probereq_pkts++;
+ } else if (GetFrameSubType(precv_frame->rx_data) == WIFI_PROBERSP) {
+ if (!memcmp(padapter->eeprompriv.mac_addr,
+ GetAddr1Ptr(precv_frame->rx_data), ETH_ALEN))
+ psta->sta_stats.rx_probersp_pkts++;
+ else if (is_broadcast_mac_addr(GetAddr1Ptr(precv_frame->rx_data)) ||
+ is_multicast_mac_addr(GetAddr1Ptr(precv_frame->rx_data)))
+ psta->sta_stats.rx_probersp_bm_pkts++;
+ else
+ psta->sta_stats.rx_probersp_uo_pkts++;
+ }
+ }
+
+ mgt_dispatcher(padapter, precv_frame);
+
+ return _SUCCESS;
+}
+
+static int validate_recv_data_frame(struct adapter *adapter,
+ struct recv_frame *precv_frame)
+{
+ u8 bretry;
+ u8 *psa, *pda, *pbssid;
+ struct sta_info *psta = NULL;
+ u8 *ptr = precv_frame->rx_data;
+ struct rx_pkt_attrib *pattrib = &precv_frame->attrib;
+ struct security_priv *psecuritypriv = &adapter->securitypriv;
+ int ret = _SUCCESS;
+
+
+ bretry = GetRetry(ptr);
+ pda = get_da(ptr);
+ psa = get_sa(ptr);
+ pbssid = get_hdr_bssid(ptr);
+
+ if (pbssid == NULL) {
+ ret = _FAIL;
+ goto exit;
+ }
+
+ memcpy(pattrib->dst, pda, ETH_ALEN);
+ memcpy(pattrib->src, psa, ETH_ALEN);
+
+ memcpy(pattrib->bssid, pbssid, ETH_ALEN);
+
+ switch (pattrib->to_fr_ds) {
+ case 0:
+ memcpy(pattrib->ra, pda, ETH_ALEN);
+ memcpy(pattrib->ta, psa, ETH_ALEN);
+ ret = sta2sta_data_frame(adapter, precv_frame, &psta);
+ break;
+ case 1:
+ memcpy(pattrib->ra, pda, ETH_ALEN);
+ memcpy(pattrib->ta, pbssid, ETH_ALEN);
+ ret = ap2sta_data_frame(adapter, precv_frame, &psta);
+ break;
+ case 2:
+ memcpy(pattrib->ra, pbssid, ETH_ALEN);
+ memcpy(pattrib->ta, psa, ETH_ALEN);
+ ret = sta2ap_data_frame(adapter, precv_frame, &psta);
+ break;
+ case 3:
+ memcpy(pattrib->ra, GetAddr1Ptr(ptr), ETH_ALEN);
+ memcpy(pattrib->ta, GetAddr2Ptr(ptr), ETH_ALEN);
+ ret = _FAIL;
+ RT_TRACE(_module_rtl871x_recv_c_, _drv_err_, (" case 3\n"));
+ break;
+ default:
+ ret = _FAIL;
+ break;
+ }
+
+ if (ret == _FAIL) {
+ goto exit;
+ } else if (ret == RTW_RX_HANDLED) {
+ goto exit;
+ }
+
+ if (psta == NULL) {
+ RT_TRACE(_module_rtl871x_recv_c_, _drv_err_, (" after to_fr_ds_chk; psta==NULL\n"));
+ ret = _FAIL;
+ goto exit;
+ }
+
+ /* psta->rssi = prxcmd->rssi; */
+ /* psta->signal_quality = prxcmd->sq; */
+ precv_frame->psta = psta;
+
+ pattrib->amsdu = 0;
+ pattrib->ack_policy = 0;
+ /* parsing QC field */
+ if (pattrib->qos == 1) {
+ pattrib->priority = GetPriority((ptr + 24));
+ pattrib->ack_policy = GetAckpolicy((ptr + 24));
+ pattrib->amsdu = GetAMsdu((ptr + 24));
+ pattrib->hdrlen = pattrib->to_fr_ds == 3 ? 32 : 26;
+
+ if (pattrib->priority != 0 && pattrib->priority != 3)
+ adapter->recvpriv.bIsAnyNonBEPkts = true;
+ } else {
+ pattrib->priority = 0;
+ pattrib->hdrlen = pattrib->to_fr_ds == 3 ? 30 : 24;
+ }
+
+ if (pattrib->order)/* HT-CTRL 11n */
+ pattrib->hdrlen += 4;
+
+ precv_frame->preorder_ctrl = &psta->recvreorder_ctrl[pattrib->priority];
+
+ /* decache, drop duplicate recv packets */
+ if (recv_decache(precv_frame, bretry, &psta->sta_recvpriv.rxcache) == _FAIL) {
+ RT_TRACE(_module_rtl871x_recv_c_, _drv_err_, ("decache : drop pkt\n"));
+ ret = _FAIL;
+ goto exit;
+ }
+
+ if (pattrib->privacy) {
+ RT_TRACE(_module_rtl871x_recv_c_, _drv_info_, ("validate_recv_data_frame:pattrib->privacy=%x\n", pattrib->privacy));
+ RT_TRACE(_module_rtl871x_recv_c_, _drv_info_, ("\n ^^^^^^^^^^^IS_MCAST(pattrib->ra(0x%02x))=%d^^^^^^^^^^^^^^^6\n", pattrib->ra[0], IS_MCAST(pattrib->ra)));
+
+ GET_ENCRY_ALGO(psecuritypriv, psta, pattrib->encrypt, IS_MCAST(pattrib->ra));
+
+ RT_TRACE(_module_rtl871x_recv_c_, _drv_info_, ("\n pattrib->encrypt=%d\n", pattrib->encrypt));
+
+ SET_ICE_IV_LEN(pattrib->iv_len, pattrib->icv_len, pattrib->encrypt);
+ } else {
+ pattrib->encrypt = 0;
+ pattrib->iv_len = 0;
+ pattrib->icv_len = 0;
+ }
+
+exit:
+
+
+ return ret;
+}
+
+static int validate_recv_frame(struct adapter *adapter,
+ struct recv_frame *precv_frame)
+{
+ /* shall check frame subtype, to / from ds, da, bssid */
+
+ /* then call check if rx seq/frag. duplicated. */
+
+ u8 type;
+ u8 subtype;
+ int retval = _SUCCESS;
+ u8 bDumpRxPkt;
+ struct rx_pkt_attrib *pattrib = &precv_frame->attrib;
+ u8 *ptr = precv_frame->rx_data;
+ u8 ver = (unsigned char)(*ptr)&0x3;
+ struct mlme_ext_priv *pmlmeext = &adapter->mlmeextpriv;
+
+
+ if (pmlmeext->sitesurvey_res.state == SCAN_PROCESS) {
+ int ch_set_idx = rtw_ch_set_search_ch(pmlmeext->channel_set, rtw_get_oper_ch(adapter));
+ if (ch_set_idx >= 0)
+ pmlmeext->channel_set[ch_set_idx].rx_count++;
+ }
+
+ /* add version chk */
+ if (ver != 0) {
+ RT_TRACE(_module_rtl871x_recv_c_, _drv_err_, ("validate_recv_data_frame fail! (ver!=0)\n"));
+ retval = _FAIL;
+ goto exit;
+ }
+
+ type = GetFrameType(ptr);
+ subtype = GetFrameSubType(ptr); /* bit(7)~bit(2) */
+
+ pattrib->to_fr_ds = get_tofr_ds(ptr);
+
+ pattrib->frag_num = GetFragNum(ptr);
+ pattrib->seq_num = GetSequence(ptr);
+
+ pattrib->pw_save = GetPwrMgt(ptr);
+ pattrib->mfrag = GetMFrag(ptr);
+ pattrib->mdata = GetMData(ptr);
+ pattrib->privacy = GetPrivacy(ptr);
+ pattrib->order = GetOrder(ptr);
+
+ /* Dump rx packets */
+ rtw_hal_get_def_var(adapter, HAL_DEF_DBG_DUMP_RXPKT, &(bDumpRxPkt));
+ if (bDumpRxPkt == 1) {/* dump all rx packets */
+ int i;
+ DBG_88E("#############################\n");
+
+ for (i = 0; i < 64; i = i+8)
+ DBG_88E("%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X:\n", *(ptr+i),
+ *(ptr+i+1), *(ptr+i+2), *(ptr+i+3), *(ptr+i+4), *(ptr+i+5), *(ptr+i+6), *(ptr+i+7));
+ DBG_88E("#############################\n");
+ } else if (bDumpRxPkt == 2) {
+ if (type == WIFI_MGT_TYPE) {
+ int i;
+ DBG_88E("#############################\n");
+
+ for (i = 0; i < 64; i = i+8)
+ DBG_88E("%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X:\n", *(ptr+i),
+ *(ptr+i+1), *(ptr+i+2), *(ptr+i+3), *(ptr+i+4), *(ptr+i+5), *(ptr+i+6), *(ptr+i+7));
+ DBG_88E("#############################\n");
+ }
+ } else if (bDumpRxPkt == 3) {
+ if (type == WIFI_DATA_TYPE) {
+ int i;
+ DBG_88E("#############################\n");
+
+ for (i = 0; i < 64; i = i+8)
+ DBG_88E("%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X:\n", *(ptr+i),
+ *(ptr+i+1), *(ptr+i+2), *(ptr+i+3), *(ptr+i+4), *(ptr+i+5), *(ptr+i+6), *(ptr+i+7));
+ DBG_88E("#############################\n");
+ }
+ }
+ switch (type) {
+ case WIFI_MGT_TYPE: /* mgnt */
+ retval = validate_recv_mgnt_frame(adapter, precv_frame);
+ if (retval == _FAIL)
+ RT_TRACE(_module_rtl871x_recv_c_, _drv_err_, ("validate_recv_mgnt_frame fail\n"));
+ retval = _FAIL; /* only data frame return _SUCCESS */
+ break;
+ case WIFI_CTRL_TYPE: /* ctrl */
+ retval = validate_recv_ctrl_frame(adapter, precv_frame);
+ if (retval == _FAIL)
+ RT_TRACE(_module_rtl871x_recv_c_, _drv_err_, ("validate_recv_ctrl_frame fail\n"));
+ retval = _FAIL; /* only data frame return _SUCCESS */
+ break;
+ case WIFI_DATA_TYPE: /* data */
+ rtw_led_control(adapter, LED_CTL_RX);
+ pattrib->qos = (subtype & BIT(7)) ? 1 : 0;
+ retval = validate_recv_data_frame(adapter, precv_frame);
+ if (retval == _FAIL) {
+ struct recv_priv *precvpriv = &adapter->recvpriv;
+ precvpriv->rx_drop++;
+ }
+ break;
+ default:
+ RT_TRACE(_module_rtl871x_recv_c_, _drv_err_, ("validate_recv_data_frame fail! type= 0x%x\n", type));
+ retval = _FAIL;
+ break;
+ }
+
+exit:
+
+
+ return retval;
+}
+
+/* remove the wlanhdr and add the eth_hdr */
+
+static int wlanhdr_to_ethhdr(struct recv_frame *precvframe)
+{
+ int rmv_len;
+ u16 eth_type, len;
+ __be16 be_tmp;
+ u8 bsnaphdr;
+ u8 *psnap_type;
+ struct ieee80211_snap_hdr *psnap;
+
+ struct adapter *adapter = precvframe->adapter;
+ struct mlme_priv *pmlmepriv = &adapter->mlmepriv;
+ u8 *ptr = precvframe->rx_data;
+ struct rx_pkt_attrib *pattrib = &precvframe->attrib;
+
+ if (pattrib->encrypt)
+ recvframe_pull_tail(precvframe, pattrib->icv_len);
+
+ psnap = (struct ieee80211_snap_hdr *)(ptr+pattrib->hdrlen + pattrib->iv_len);
+ psnap_type = ptr+pattrib->hdrlen + pattrib->iv_len+SNAP_SIZE;
+ /* convert hdr + possible LLC headers into Ethernet header */
+ if ((!memcmp(psnap, rtw_rfc1042_header, SNAP_SIZE) &&
+ (!memcmp(psnap_type, SNAP_ETH_TYPE_IPX, 2) == false) &&
+ (!memcmp(psnap_type, SNAP_ETH_TYPE_APPLETALK_AARP, 2) == false)) ||
+ !memcmp(psnap, rtw_bridge_tunnel_header, SNAP_SIZE)) {
+ /* remove RFC1042 or Bridge-Tunnel encapsulation and replace EtherType */
+ bsnaphdr = true;
+ } else {
+ /* Leave Ethernet header part of hdr and full payload */
+ bsnaphdr = false;
+ }
+
+ rmv_len = pattrib->hdrlen + pattrib->iv_len + (bsnaphdr ? SNAP_SIZE : 0);
+ len = precvframe->len - rmv_len;
+
+ RT_TRACE(_module_rtl871x_recv_c_, _drv_info_,
+ ("\n===pattrib->hdrlen: %x, pattrib->iv_len:%x===\n\n", pattrib->hdrlen, pattrib->iv_len));
+
+ memcpy(&be_tmp, ptr+rmv_len, 2);
+ eth_type = ntohs(be_tmp); /* pattrib->ether_type */
+ pattrib->eth_type = eth_type;
+
+ if ((check_fwstate(pmlmepriv, WIFI_MP_STATE))) {
+ ptr += rmv_len;
+ *ptr = 0x87;
+ *(ptr+1) = 0x12;
+
+ eth_type = 0x8712;
+ /* append rx status for mp test packets */
+ ptr = recvframe_pull(precvframe, (rmv_len-sizeof(struct ethhdr)+2)-24);
+ memcpy(ptr, get_rxmem(precvframe), 24);
+ ptr += 24;
+ } else {
+ ptr = recvframe_pull(precvframe, (rmv_len-sizeof(struct ethhdr) + (bsnaphdr ? 2 : 0)));
+ }
+
+ memcpy(ptr, pattrib->dst, ETH_ALEN);
+ memcpy(ptr+ETH_ALEN, pattrib->src, ETH_ALEN);
+
+ if (!bsnaphdr) {
+ be_tmp = htons(len);
+ memcpy(ptr+12, &be_tmp, 2);
+ }
+
+ return _SUCCESS;
+}
+
+/* perform defrag */
+static struct recv_frame *recvframe_defrag(struct adapter *adapter,
+ struct __queue *defrag_q)
+{
+ struct list_head *plist, *phead;
+ u8 wlanhdr_offset;
+ u8 curfragnum;
+ struct recv_frame *pfhdr, *pnfhdr;
+ struct recv_frame *prframe, *pnextrframe;
+ struct __queue *pfree_recv_queue;
+
+
+ curfragnum = 0;
+ pfree_recv_queue = &adapter->recvpriv.free_recv_queue;
+
+ phead = get_list_head(defrag_q);
+ plist = phead->next;
+ pfhdr = container_of(plist, struct recv_frame, list);
+ prframe = (struct recv_frame *)pfhdr;
+ list_del_init(&(prframe->list));
+
+ if (curfragnum != pfhdr->attrib.frag_num) {
+ /* the first fragment number must be 0 */
+ /* free the whole queue */
+ rtw_free_recvframe(prframe, pfree_recv_queue);
+ rtw_free_recvframe_queue(defrag_q, pfree_recv_queue);
+
+ return NULL;
+ }
+
+ curfragnum++;
+
+ plist = get_list_head(defrag_q);
+
+ plist = plist->next;
+
+ while (phead != plist) {
+ pnfhdr = container_of(plist, struct recv_frame, list);
+ pnextrframe = (struct recv_frame *)pnfhdr;
+
+ /* check the fragment sequence (2nd ~n fragment frame) */
+
+ if (curfragnum != pnfhdr->attrib.frag_num) {
+ /* the fragment number must be increasing (after decache) */
+ /* release the defrag_q & prframe */
+ rtw_free_recvframe(prframe, pfree_recv_queue);
+ rtw_free_recvframe_queue(defrag_q, pfree_recv_queue);
+ return NULL;
+ }
+
+ curfragnum++;
+
+ /* copy the 2nd~n fragment frame's payload to the first fragment */
+ /* get the 2nd~last fragment frame's payload */
+
+ wlanhdr_offset = pnfhdr->attrib.hdrlen + pnfhdr->attrib.iv_len;
+
+ recvframe_pull(pnextrframe, wlanhdr_offset);
+
+ /* append to first fragment frame's tail (if privacy frame, pull the ICV) */
+ recvframe_pull_tail(prframe, pfhdr->attrib.icv_len);
+
+ /* memcpy */
+ memcpy(pfhdr->rx_tail, pnfhdr->rx_data, pnfhdr->len);
+
+ recvframe_put(prframe, pnfhdr->len);
+
+ pfhdr->attrib.icv_len = pnfhdr->attrib.icv_len;
+ plist = plist->next;
+ }
+
+ /* free the defrag_q queue and return the prframe */
+ rtw_free_recvframe_queue(defrag_q, pfree_recv_queue);
+
+ RT_TRACE(_module_rtl871x_recv_c_, _drv_info_, ("Performance defrag!!!!!\n"));
+
+
+ return prframe;
+}
+
+/* check if need to defrag, if needed queue the frame to defrag_q */
+struct recv_frame *recvframe_chk_defrag(struct adapter *padapter,
+ struct recv_frame *precv_frame)
+{
+ u8 ismfrag;
+ u8 fragnum;
+ u8 *psta_addr;
+ struct recv_frame *pfhdr;
+ struct sta_info *psta;
+ struct sta_priv *pstapriv;
+ struct list_head *phead;
+ struct recv_frame *prtnframe = NULL;
+ struct __queue *pfree_recv_queue, *pdefrag_q;
+
+
+ pstapriv = &padapter->stapriv;
+
+ pfhdr = precv_frame;
+
+ pfree_recv_queue = &padapter->recvpriv.free_recv_queue;
+
+ /* need to define struct of wlan header frame ctrl */
+ ismfrag = pfhdr->attrib.mfrag;
+ fragnum = pfhdr->attrib.frag_num;
+
+ psta_addr = pfhdr->attrib.ta;
+ psta = rtw_get_stainfo(pstapriv, psta_addr);
+ if (psta == NULL) {
+ u8 type = GetFrameType(pfhdr->rx_data);
+ if (type != WIFI_DATA_TYPE) {
+ psta = rtw_get_bcmc_stainfo(padapter);
+ pdefrag_q = &psta->sta_recvpriv.defrag_q;
+ } else {
+ pdefrag_q = NULL;
+ }
+ } else {
+ pdefrag_q = &psta->sta_recvpriv.defrag_q;
+ }
+
+ if ((ismfrag == 0) && (fragnum == 0))
+ prtnframe = precv_frame;/* isn't a fragment frame */
+
+ if (ismfrag == 1) {
+ /* 0~(n-1) fragment frame */
+ /* enqueue to defraf_g */
+ if (pdefrag_q != NULL) {
+ if (fragnum == 0) {
+ /* the first fragment */
+ if (!list_empty(&pdefrag_q->queue)) {
+ /* free current defrag_q */
+ rtw_free_recvframe_queue(pdefrag_q, pfree_recv_queue);
+ }
+ }
+
+ /* Then enqueue the 0~(n-1) fragment into the defrag_q */
+
+ phead = get_list_head(pdefrag_q);
+ list_add_tail(&pfhdr->list, phead);
+
+ RT_TRACE(_module_rtl871x_recv_c_, _drv_info_, ("Enqueuq: ismfrag=%d, fragnum=%d\n", ismfrag, fragnum));
+
+ prtnframe = NULL;
+ } else {
+ /* can't find this ta's defrag_queue, so free this recv_frame */
+ rtw_free_recvframe(precv_frame, pfree_recv_queue);
+ prtnframe = NULL;
+ RT_TRACE(_module_rtl871x_recv_c_, _drv_err_, ("Free because pdefrag_q==NULL: ismfrag=%d, fragnum=%d\n", ismfrag, fragnum));
+ }
+ }
+
+ if ((ismfrag == 0) && (fragnum != 0)) {
+ /* the last fragment frame */
+ /* enqueue the last fragment */
+ if (pdefrag_q != NULL) {
+ phead = get_list_head(pdefrag_q);
+ list_add_tail(&pfhdr->list, phead);
+
+ /* call recvframe_defrag to defrag */
+ RT_TRACE(_module_rtl871x_recv_c_, _drv_info_, ("defrag: ismfrag=%d, fragnum=%d\n", ismfrag, fragnum));
+ precv_frame = recvframe_defrag(padapter, pdefrag_q);
+ prtnframe = precv_frame;
+ } else {
+ /* can't find this ta's defrag_queue, so free this recv_frame */
+ rtw_free_recvframe(precv_frame, pfree_recv_queue);
+ prtnframe = NULL;
+ RT_TRACE(_module_rtl871x_recv_c_, _drv_err_, ("Free because pdefrag_q==NULL: ismfrag=%d, fragnum=%d\n", ismfrag, fragnum));
+ }
+ }
+
+ if ((prtnframe != NULL) && (prtnframe->attrib.privacy)) {
+ /* after defrag we must check tkip mic code */
+ if (recvframe_chkmic(padapter, prtnframe) == _FAIL) {
+ RT_TRACE(_module_rtl871x_recv_c_, _drv_err_, ("recvframe_chkmic(padapter, prtnframe)==_FAIL\n"));
+ rtw_free_recvframe(prtnframe, pfree_recv_queue);
+ prtnframe = NULL;
+ }
+ }
+
+
+ return prtnframe;
+}
+
+static int amsdu_to_msdu(struct adapter *padapter, struct recv_frame *prframe)
+{
+ int a_len, padding_len;
+ u16 eth_type, nSubframe_Length;
+ u8 nr_subframes, i;
+ unsigned char *pdata;
+ struct rx_pkt_attrib *pattrib;
+ unsigned char *data_ptr;
+ struct sk_buff *sub_skb, *subframes[MAX_SUBFRAME_COUNT];
+ struct recv_priv *precvpriv = &padapter->recvpriv;
+ struct __queue *pfree_recv_queue = &(precvpriv->free_recv_queue);
+ nr_subframes = 0;
+
+ pattrib = &prframe->attrib;
+
+ recvframe_pull(prframe, prframe->attrib.hdrlen);
+
+ if (prframe->attrib.iv_len > 0)
+ recvframe_pull(prframe, prframe->attrib.iv_len);
+
+ a_len = prframe->len;
+
+ pdata = prframe->rx_data;
+
+ while (a_len > ETH_HLEN) {
+ /* Offset 12 denote 2 mac address */
+ nSubframe_Length = get_unaligned_be16(pdata + 12);
+
+ if (a_len < (ETHERNET_HEADER_SIZE + nSubframe_Length)) {
+ DBG_88E("nRemain_Length is %d and nSubframe_Length is : %d\n", a_len, nSubframe_Length);
+ goto exit;
+ }
+
+ /* move the data point to data content */
+ pdata += ETH_HLEN;
+ a_len -= ETH_HLEN;
+
+ /* Allocate new skb for releasing to upper layer */
+ sub_skb = dev_alloc_skb(nSubframe_Length + 12);
+ if (sub_skb) {
+ skb_reserve(sub_skb, 12);
+ data_ptr = (u8 *)skb_put(sub_skb, nSubframe_Length);
+ memcpy(data_ptr, pdata, nSubframe_Length);
+ } else {
+ sub_skb = skb_clone(prframe->pkt, GFP_ATOMIC);
+ if (sub_skb) {
+ sub_skb->data = pdata;
+ sub_skb->len = nSubframe_Length;
+ skb_set_tail_pointer(sub_skb, nSubframe_Length);
+ } else {
+ DBG_88E("skb_clone() Fail!!! , nr_subframes=%d\n", nr_subframes);
+ break;
+ }
+ }
+
+ subframes[nr_subframes++] = sub_skb;
+
+ if (nr_subframes >= MAX_SUBFRAME_COUNT) {
+ DBG_88E("ParseSubframe(): Too many Subframes! Packets dropped!\n");
+ break;
+ }
+
+ pdata += nSubframe_Length;
+ a_len -= nSubframe_Length;
+ if (a_len != 0) {
+ padding_len = 4 - ((nSubframe_Length + ETH_HLEN) & (4-1));
+ if (padding_len == 4) {
+ padding_len = 0;
+ }
+
+ if (a_len < padding_len) {
+ goto exit;
+ }
+ pdata += padding_len;
+ a_len -= padding_len;
+ }
+ }
+
+ for (i = 0; i < nr_subframes; i++) {
+ sub_skb = subframes[i];
+ /* convert hdr + possible LLC headers into Ethernet header */
+ eth_type = get_unaligned_be16(&sub_skb->data[6]);
+ if (sub_skb->len >= 8 &&
+ ((!memcmp(sub_skb->data, rtw_rfc1042_header, SNAP_SIZE) &&
+ eth_type != ETH_P_AARP && eth_type != ETH_P_IPX) ||
+ !memcmp(sub_skb->data, rtw_bridge_tunnel_header, SNAP_SIZE))) {
+ /* remove RFC1042 or Bridge-Tunnel encapsulation and replace EtherType */
+ skb_pull(sub_skb, SNAP_SIZE);
+ memcpy(skb_push(sub_skb, ETH_ALEN), pattrib->src, ETH_ALEN);
+ memcpy(skb_push(sub_skb, ETH_ALEN), pattrib->dst, ETH_ALEN);
+ } else {
+ __be16 len;
+ /* Leave Ethernet header part of hdr and full payload */
+ len = htons(sub_skb->len);
+ memcpy(skb_push(sub_skb, 2), &len, 2);
+ memcpy(skb_push(sub_skb, ETH_ALEN), pattrib->src, ETH_ALEN);
+ memcpy(skb_push(sub_skb, ETH_ALEN), pattrib->dst, ETH_ALEN);
+ }
+
+ /* Indicate the packets to upper layer */
+ /* Insert NAT2.5 RX here! */
+ sub_skb->protocol = eth_type_trans(sub_skb, padapter->pnetdev);
+ sub_skb->dev = padapter->pnetdev;
+
+ sub_skb->ip_summed = CHECKSUM_NONE;
+
+ netif_rx(sub_skb);
+ }
+
+exit:
+
+ prframe->len = 0;
+ rtw_free_recvframe(prframe, pfree_recv_queue);/* free this recv_frame */
+
+ return _SUCCESS;
+}
+
+static int check_indicate_seq(struct recv_reorder_ctrl *preorder_ctrl, u16 seq_num)
+{
+ u8 wsize = preorder_ctrl->wsize_b;
+ u16 wend = (preorder_ctrl->indicate_seq + wsize - 1) & 0xFFF;/* 4096; */
+
+ /* Rx Reorder initialize condition. */
+ if (preorder_ctrl->indicate_seq == 0xFFFF)
+ preorder_ctrl->indicate_seq = seq_num;
+
+ /* Drop out the packet which SeqNum is smaller than WinStart */
+ if (SN_LESS(seq_num, preorder_ctrl->indicate_seq))
+ return false;
+
+ /* */
+ /* Sliding window manipulation. Conditions includes: */
+ /* 1. Incoming SeqNum is equal to WinStart =>Window shift 1 */
+ /* 2. Incoming SeqNum is larger than the WinEnd => Window shift N */
+ /* */
+ if (SN_EQUAL(seq_num, preorder_ctrl->indicate_seq)) {
+ preorder_ctrl->indicate_seq = (preorder_ctrl->indicate_seq + 1) & 0xFFF;
+ } else if (SN_LESS(wend, seq_num)) {
+ if (seq_num >= (wsize - 1))
+ preorder_ctrl->indicate_seq = seq_num + 1 - wsize;
+ else
+ preorder_ctrl->indicate_seq = 0xFFF - (wsize - (seq_num + 1)) + 1;
+ }
+
+ return true;
+}
+
+static int enqueue_reorder_recvframe(struct recv_reorder_ctrl *preorder_ctrl,
+ struct recv_frame *prframe)
+{
+ struct rx_pkt_attrib *pattrib = &prframe->attrib;
+ struct __queue *ppending_recvframe_queue = &preorder_ctrl->pending_recvframe_queue;
+ struct list_head *phead, *plist;
+ struct recv_frame *hdr;
+ struct rx_pkt_attrib *pnextattrib;
+
+ phead = get_list_head(ppending_recvframe_queue);
+ plist = phead->next;
+
+ while (phead != plist) {
+ hdr = container_of(plist, struct recv_frame, list);
+ pnextattrib = &hdr->attrib;
+
+ if (SN_LESS(pnextattrib->seq_num, pattrib->seq_num))
+ plist = plist->next;
+ else if (SN_EQUAL(pnextattrib->seq_num, pattrib->seq_num))
+ return false;
+ else
+ break;
+ }
+
+ list_del_init(&(prframe->list));
+
+ list_add_tail(&(prframe->list), plist);
+ return true;
+}
+
+static int recv_indicatepkts_in_order(struct adapter *padapter, struct recv_reorder_ctrl *preorder_ctrl, int bforced)
+{
+ struct list_head *phead, *plist;
+ struct recv_frame *prframe;
+ struct recv_frame *prhdr;
+ struct rx_pkt_attrib *pattrib;
+ int bPktInBuf = false;
+ struct recv_priv *precvpriv = &padapter->recvpriv;
+ struct __queue *ppending_recvframe_queue = &preorder_ctrl->pending_recvframe_queue;
+
+ phead = get_list_head(ppending_recvframe_queue);
+ plist = phead->next;
+
+ /* Handling some condition for forced indicate case. */
+ if (bforced) {
+ if (list_empty(phead))
+ return true;
+
+ prhdr = container_of(plist, struct recv_frame, list);
+ pattrib = &prhdr->attrib;
+ preorder_ctrl->indicate_seq = pattrib->seq_num;
+ }
+
+ /* Prepare indication list and indication. */
+ /* Check if there is any packet need indicate. */
+ while (!list_empty(phead)) {
+ prhdr = container_of(plist, struct recv_frame, list);
+ prframe = (struct recv_frame *)prhdr;
+ pattrib = &prframe->attrib;
+
+ if (!SN_LESS(preorder_ctrl->indicate_seq, pattrib->seq_num)) {
+ RT_TRACE(_module_rtl871x_recv_c_, _drv_notice_,
+ ("recv_indicatepkts_in_order: indicate=%d seq=%d amsdu=%d\n",
+ preorder_ctrl->indicate_seq, pattrib->seq_num, pattrib->amsdu));
+ plist = plist->next;
+ list_del_init(&(prframe->list));
+
+ if (SN_EQUAL(preorder_ctrl->indicate_seq, pattrib->seq_num))
+ preorder_ctrl->indicate_seq = (preorder_ctrl->indicate_seq + 1) & 0xFFF;
+
+ /* Set this as a lock to make sure that only one thread is indicating packet. */
+
+ /* indicate this recv_frame */
+ if (!pattrib->amsdu) {
+ if ((!padapter->bDriverStopped) &&
+ (!padapter->bSurpriseRemoved))
+ rtw_recv_indicatepkt(padapter, prframe);/* indicate this recv_frame */
+ } else if (pattrib->amsdu == 1) {
+ if (amsdu_to_msdu(padapter, prframe) != _SUCCESS)
+ rtw_free_recvframe(prframe, &precvpriv->free_recv_queue);
+ } else {
+ /* error condition; */
+ }
+
+ /* Update local variables. */
+ bPktInBuf = false;
+ } else {
+ bPktInBuf = true;
+ break;
+ }
+ }
+ return bPktInBuf;
+}
+
+static int recv_indicatepkt_reorder(struct adapter *padapter,
+ struct recv_frame *prframe)
+{
+ int retval = _SUCCESS;
+ struct rx_pkt_attrib *pattrib = &prframe->attrib;
+ struct recv_reorder_ctrl *preorder_ctrl = prframe->preorder_ctrl;
+ struct __queue *ppending_recvframe_queue = &preorder_ctrl->pending_recvframe_queue;
+
+ if (!pattrib->amsdu) {
+ /* s1. */
+ wlanhdr_to_ethhdr(prframe);
+
+ if ((pattrib->qos != 1) || (pattrib->eth_type == 0x0806) ||
+ (pattrib->ack_policy != 0)) {
+ if ((!padapter->bDriverStopped) &&
+ (!padapter->bSurpriseRemoved)) {
+ RT_TRACE(_module_rtl871x_recv_c_, _drv_notice_, ("@@@@ recv_indicatepkt_reorder -recv_func recv_indicatepkt\n"));
+
+ rtw_recv_indicatepkt(padapter, prframe);
+ return _SUCCESS;
+ }
+
+ return _FAIL;
+ }
+
+ if (!preorder_ctrl->enable) {
+ /* indicate this recv_frame */
+ preorder_ctrl->indicate_seq = pattrib->seq_num;
+ rtw_recv_indicatepkt(padapter, prframe);
+
+ preorder_ctrl->indicate_seq = (preorder_ctrl->indicate_seq + 1)%4096;
+ return _SUCCESS;
+ }
+ } else if (pattrib->amsdu == 1) { /* temp filter -> means didn't support A-MSDUs in a A-MPDU */
+ if (!preorder_ctrl->enable) {
+ preorder_ctrl->indicate_seq = pattrib->seq_num;
+ retval = amsdu_to_msdu(padapter, prframe);
+
+ preorder_ctrl->indicate_seq = (preorder_ctrl->indicate_seq + 1)%4096;
+ return retval;
+ }
+ }
+
+ spin_lock_bh(&ppending_recvframe_queue->lock);
+
+ RT_TRACE(_module_rtl871x_recv_c_, _drv_notice_,
+ ("recv_indicatepkt_reorder: indicate=%d seq=%d\n",
+ preorder_ctrl->indicate_seq, pattrib->seq_num));
+
+ /* s2. check if winstart_b(indicate_seq) needs to been updated */
+ if (!check_indicate_seq(preorder_ctrl, pattrib->seq_num)) {
+ rtw_recv_indicatepkt(padapter, prframe);
+
+ spin_unlock_bh(&ppending_recvframe_queue->lock);
+
+ goto _success_exit;
+ }
+
+ /* s3. Insert all packet into Reorder Queue to maintain its ordering. */
+ if (!enqueue_reorder_recvframe(preorder_ctrl, prframe))
+ goto _err_exit;
+
+ /* s4. */
+ /* Indication process. */
+ /* After Packet dropping and Sliding Window shifting as above, we can now just indicate the packets */
+ /* with the SeqNum smaller than latest WinStart and buffer other packets. */
+ /* */
+ /* For Rx Reorder condition: */
+ /* 1. All packets with SeqNum smaller than WinStart => Indicate */
+ /* 2. All packets with SeqNum larger than or equal to WinStart => Buffer it. */
+ /* */
+
+ /* recv_indicatepkts_in_order(padapter, preorder_ctrl, true); */
+ if (recv_indicatepkts_in_order(padapter, preorder_ctrl, false)) {
+ mod_timer(&preorder_ctrl->reordering_ctrl_timer,
+ jiffies + msecs_to_jiffies(REORDER_WAIT_TIME));
+ spin_unlock_bh(&ppending_recvframe_queue->lock);
+ } else {
+ spin_unlock_bh(&ppending_recvframe_queue->lock);
+ del_timer_sync(&preorder_ctrl->reordering_ctrl_timer);
+ }
+
+_success_exit:
+
+ return _SUCCESS;
+
+_err_exit:
+
+ spin_unlock_bh(&ppending_recvframe_queue->lock);
+
+ return _FAIL;
+}
+
+void rtw_reordering_ctrl_timeout_handler(unsigned long data)
+{
+ struct recv_reorder_ctrl *preorder_ctrl = (struct recv_reorder_ctrl *)data;
+ struct adapter *padapter = preorder_ctrl->padapter;
+ struct __queue *ppending_recvframe_queue = &preorder_ctrl->pending_recvframe_queue;
+
+ if (padapter->bDriverStopped || padapter->bSurpriseRemoved)
+ return;
+
+ spin_lock_bh(&ppending_recvframe_queue->lock);
+
+ if (recv_indicatepkts_in_order(padapter, preorder_ctrl, true) == true)
+ mod_timer(&preorder_ctrl->reordering_ctrl_timer,
+ jiffies + msecs_to_jiffies(REORDER_WAIT_TIME));
+
+ spin_unlock_bh(&ppending_recvframe_queue->lock);
+}
+
+static int process_recv_indicatepkts(struct adapter *padapter,
+ struct recv_frame *prframe)
+{
+ int retval = _SUCCESS;
+ struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
+ struct ht_priv *phtpriv = &pmlmepriv->htpriv;
+
+ if (phtpriv->ht_option) { /* B/G/N Mode */
+ if (recv_indicatepkt_reorder(padapter, prframe) != _SUCCESS) {
+ /* including perform A-MPDU Rx Ordering Buffer Control */
+ if ((!padapter->bDriverStopped) &&
+ (!padapter->bSurpriseRemoved)) {
+ retval = _FAIL;
+ return retval;
+ }
+ }
+ } else { /* B/G mode */
+ retval = wlanhdr_to_ethhdr(prframe);
+ if (retval != _SUCCESS) {
+ RT_TRACE(_module_rtl871x_recv_c_, _drv_err_, ("wlanhdr_to_ethhdr: drop pkt\n"));
+ return retval;
+ }
+
+ if ((!padapter->bDriverStopped) &&
+ (!padapter->bSurpriseRemoved)) {
+ /* indicate this recv_frame */
+ RT_TRACE(_module_rtl871x_recv_c_, _drv_notice_, ("@@@@ process_recv_indicatepkts- recv_func recv_indicatepkt\n"));
+ rtw_recv_indicatepkt(padapter, prframe);
+ } else {
+ RT_TRACE(_module_rtl871x_recv_c_, _drv_notice_, ("@@@@ process_recv_indicatepkts- recv_func free_indicatepkt\n"));
+
+ RT_TRACE(_module_rtl871x_recv_c_, _drv_notice_, ("recv_func:bDriverStopped(%d) OR bSurpriseRemoved(%d)", padapter->bDriverStopped, padapter->bSurpriseRemoved));
+ retval = _FAIL;
+ return retval;
+ }
+ }
+
+ return retval;
+}
+
+static int recv_func_prehandle(struct adapter *padapter,
+ struct recv_frame *rframe)
+{
+ int ret = _SUCCESS;
+ struct __queue *pfree_recv_queue = &padapter->recvpriv.free_recv_queue;
+
+ /* check the frame crtl field and decache */
+ ret = validate_recv_frame(padapter, rframe);
+ if (ret != _SUCCESS) {
+ RT_TRACE(_module_rtl871x_recv_c_, _drv_info_, ("recv_func: validate_recv_frame fail! drop pkt\n"));
+ rtw_free_recvframe(rframe, pfree_recv_queue);/* free this recv_frame */
+ goto exit;
+ }
+
+exit:
+ return ret;
+}
+
+static int recv_func_posthandle(struct adapter *padapter,
+ struct recv_frame *prframe)
+{
+ int ret = _SUCCESS;
+ struct recv_frame *orig_prframe = prframe;
+ struct recv_priv *precvpriv = &padapter->recvpriv;
+ struct __queue *pfree_recv_queue = &padapter->recvpriv.free_recv_queue;
+
+ /* DATA FRAME */
+ rtw_led_control(padapter, LED_CTL_RX);
+
+ prframe = decryptor(padapter, prframe);
+ if (prframe == NULL) {
+ RT_TRACE(_module_rtl871x_recv_c_, _drv_err_, ("decryptor: drop pkt\n"));
+ ret = _FAIL;
+ goto _recv_data_drop;
+ }
+
+ prframe = recvframe_chk_defrag(padapter, prframe);
+ if (prframe == NULL) {
+ RT_TRACE(_module_rtl871x_recv_c_, _drv_err_, ("recvframe_chk_defrag: drop pkt\n"));
+ goto _recv_data_drop;
+ }
+
+ prframe = portctrl(padapter, prframe);
+ if (prframe == NULL) {
+ RT_TRACE(_module_rtl871x_recv_c_, _drv_err_, ("portctrl: drop pkt\n"));
+ ret = _FAIL;
+ goto _recv_data_drop;
+ }
+
+ count_rx_stats(padapter, prframe, NULL);
+
+ ret = process_recv_indicatepkts(padapter, prframe);
+ if (ret != _SUCCESS) {
+ RT_TRACE(_module_rtl871x_recv_c_, _drv_err_, ("recv_func: process_recv_indicatepkts fail!\n"));
+ rtw_free_recvframe(orig_prframe, pfree_recv_queue);/* free this recv_frame */
+ goto _recv_data_drop;
+ }
+ return ret;
+
+_recv_data_drop:
+ precvpriv->rx_drop++;
+ return ret;
+}
+
+static int recv_func(struct adapter *padapter, struct recv_frame *rframe)
+{
+ int ret;
+ struct rx_pkt_attrib *prxattrib = &rframe->attrib;
+ struct security_priv *psecuritypriv = &padapter->securitypriv;
+ struct mlme_priv *mlmepriv = &padapter->mlmepriv;
+
+ /* check if need to handle uc_swdec_pending_queue*/
+ if (check_fwstate(mlmepriv, WIFI_STATION_STATE) && psecuritypriv->busetkipkey) {
+ struct recv_frame *pending_frame;
+
+ while ((pending_frame = rtw_alloc_recvframe(&padapter->recvpriv.uc_swdec_pending_queue))) {
+ if (recv_func_posthandle(padapter, pending_frame) == _SUCCESS)
+ DBG_88E("%s: dequeue uc_swdec_pending_queue\n", __func__);
+ }
+ }
+
+ ret = recv_func_prehandle(padapter, rframe);
+
+ if (ret == _SUCCESS) {
+ /* check if need to enqueue into uc_swdec_pending_queue*/
+ if (check_fwstate(mlmepriv, WIFI_STATION_STATE) &&
+ !IS_MCAST(prxattrib->ra) && prxattrib->encrypt > 0 &&
+ (prxattrib->bdecrypted == 0 || psecuritypriv->sw_decrypt) &&
+ !is_wep_enc(psecuritypriv->dot11PrivacyAlgrthm) &&
+ !psecuritypriv->busetkipkey) {
+ rtw_enqueue_recvframe(rframe, &padapter->recvpriv.uc_swdec_pending_queue);
+ DBG_88E("%s: no key, enqueue uc_swdec_pending_queue\n", __func__);
+ goto exit;
+ }
+
+ ret = recv_func_posthandle(padapter, rframe);
+ }
+
+exit:
+ return ret;
+}
+
+s32 rtw_recv_entry(struct recv_frame *precvframe)
+{
+ struct adapter *padapter;
+ struct recv_priv *precvpriv;
+ s32 ret = _SUCCESS;
+
+
+ padapter = precvframe->adapter;
+
+ precvpriv = &padapter->recvpriv;
+
+ ret = recv_func(padapter, precvframe);
+ if (ret == _FAIL) {
+ RT_TRACE(_module_rtl871x_recv_c_, _drv_info_, ("rtw_recv_entry: recv_func return fail!!!\n"));
+ goto _recv_entry_drop;
+ }
+
+ precvpriv->rx_pkts++;
+
+
+ return ret;
+
+_recv_entry_drop:
+ return ret;
+}
+
+void rtw_signal_stat_timer_hdl(unsigned long data)
+{
+ struct adapter *adapter = (struct adapter *)data;
+ struct recv_priv *recvpriv = &adapter->recvpriv;
+
+ u32 tmp_s, tmp_q;
+ u8 avg_signal_strength = 0;
+ u8 avg_signal_qual = 0;
+ u8 _alpha = 3; /* this value is based on converging_constant = 5000 and sampling_interval = 1000 */
+
+ if (adapter->recvpriv.is_signal_dbg) {
+ /* update the user specific value, signal_strength_dbg, to signal_strength, rssi */
+ adapter->recvpriv.signal_strength = adapter->recvpriv.signal_strength_dbg;
+ adapter->recvpriv.rssi = (s8)translate_percentage_to_dbm((u8)adapter->recvpriv.signal_strength_dbg);
+ } else {
+ if (recvpriv->signal_strength_data.update_req == 0) {/* update_req is clear, means we got rx */
+ avg_signal_strength = recvpriv->signal_strength_data.avg_val;
+ /* after avg_vals are acquired, we can re-stat the signal values */
+ recvpriv->signal_strength_data.update_req = 1;
+ }
+
+ if (recvpriv->signal_qual_data.update_req == 0) {/* update_req is clear, means we got rx */
+ avg_signal_qual = recvpriv->signal_qual_data.avg_val;
+ /* after avg_vals are acquired, we can re-stat the signal values */
+ recvpriv->signal_qual_data.update_req = 1;
+ }
+
+ /* update value of signal_strength, rssi, signal_qual */
+ if (check_fwstate(&adapter->mlmepriv, _FW_UNDER_SURVEY) == false) {
+ tmp_s = avg_signal_strength+(_alpha-1)*recvpriv->signal_strength;
+ if (tmp_s % _alpha)
+ tmp_s = tmp_s/_alpha + 1;
+ else
+ tmp_s = tmp_s/_alpha;
+ if (tmp_s > 100)
+ tmp_s = 100;
+
+ tmp_q = avg_signal_qual+(_alpha-1)*recvpriv->signal_qual;
+ if (tmp_q % _alpha)
+ tmp_q = tmp_q/_alpha + 1;
+ else
+ tmp_q = tmp_q/_alpha;
+ if (tmp_q > 100)
+ tmp_q = 100;
+
+ recvpriv->signal_strength = tmp_s;
+ recvpriv->rssi = (s8)translate_percentage_to_dbm(tmp_s);
+ recvpriv->signal_qual = tmp_q;
+ }
+ }
+ rtw_set_signal_stat_timer(recvpriv);
+}
diff --git a/drivers/staging/rtl8188eu/core/rtw_rf.c b/drivers/staging/rtl8188eu/core/rtw_rf.c
new file mode 100644
index 000000000..1170dd001
--- /dev/null
+++ b/drivers/staging/rtl8188eu/core/rtw_rf.c
@@ -0,0 +1,89 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2011 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ *
+ ******************************************************************************/
+#define _RTW_RF_C_
+
+#include <osdep_service.h>
+#include <drv_types.h>
+#include <recv_osdep.h>
+#include <xmit_osdep.h>
+
+
+struct ch_freq {
+ u32 channel;
+ u32 frequency;
+};
+
+static struct ch_freq ch_freq_map[] = {
+ {1, 2412}, {2, 2417}, {3, 2422}, {4, 2427}, {5, 2432},
+ {6, 2437}, {7, 2442}, {8, 2447}, {9, 2452}, {10, 2457},
+ {11, 2462}, {12, 2467}, {13, 2472}, {14, 2484},
+ /* UNII */
+ {36, 5180}, {40, 5200}, {44, 5220}, {48, 5240}, {52, 5260},
+ {56, 5280}, {60, 5300}, {64, 5320}, {149, 5745}, {153, 5765},
+ {157, 5785}, {161, 5805}, {165, 5825}, {167, 5835}, {169, 5845},
+ {171, 5855}, {173, 5865},
+ /* HiperLAN2 */
+ {100, 5500}, {104, 5520}, {108, 5540}, {112, 5560}, {116, 5580},
+ {120, 5600}, {124, 5620}, {128, 5640}, {132, 5660}, {136, 5680},
+ {140, 5700},
+ /* Japan MMAC */
+ {34, 5170}, {38, 5190}, {42, 5210}, {46, 5230},
+ /* Japan */
+ {184, 4920}, {188, 4940}, {192, 4960}, {196, 4980},
+ {208, 5040},/* Japan, means J08 */
+ {212, 5060},/* Japan, means J12 */
+ {216, 5080},/* Japan, means J16 */
+};
+
+static int ch_freq_map_num = (sizeof(ch_freq_map) / sizeof(struct ch_freq));
+
+u32 rtw_ch2freq(u32 channel)
+{
+ u8 i;
+ u32 freq = 0;
+
+ for (i = 0; i < ch_freq_map_num; i++) {
+ if (channel == ch_freq_map[i].channel) {
+ freq = ch_freq_map[i].frequency;
+ break;
+ }
+ }
+ if (i == ch_freq_map_num)
+ freq = 2412;
+
+ return freq;
+}
+
+u32 rtw_freq2ch(u32 freq)
+{
+ u8 i;
+ u32 ch = 0;
+
+ for (i = 0; i < ch_freq_map_num; i++) {
+ if (freq == ch_freq_map[i].frequency) {
+ ch = ch_freq_map[i].channel;
+ break;
+ }
+ }
+ if (i == ch_freq_map_num)
+ ch = 1;
+
+ return ch;
+}
diff --git a/drivers/staging/rtl8188eu/core/rtw_security.c b/drivers/staging/rtl8188eu/core/rtw_security.c
new file mode 100644
index 000000000..d870a5ce8
--- /dev/null
+++ b/drivers/staging/rtl8188eu/core/rtw_security.c
@@ -0,0 +1,1681 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2011 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ *
+ ******************************************************************************/
+#define _RTW_SECURITY_C_
+
+#include <osdep_service.h>
+#include <drv_types.h>
+#include <wifi.h>
+#include <osdep_intf.h>
+
+/* WEP related ===== */
+
+#define CRC32_POLY 0x04c11db7
+
+struct arc4context {
+ u32 x;
+ u32 y;
+ u8 state[256];
+};
+
+static void arcfour_init(struct arc4context *parc4ctx, u8 *key, u32 key_len)
+{
+ u32 t, u;
+ u32 keyindex;
+ u32 stateindex;
+ u8 *state;
+ u32 counter;
+ state = parc4ctx->state;
+ parc4ctx->x = 0;
+ parc4ctx->y = 0;
+ for (counter = 0; counter < 256; counter++)
+ state[counter] = (u8)counter;
+ keyindex = 0;
+ stateindex = 0;
+ for (counter = 0; counter < 256; counter++) {
+ t = state[counter];
+ stateindex = (stateindex + key[keyindex] + t) & 0xff;
+ u = state[stateindex];
+ state[stateindex] = (u8)t;
+ state[counter] = (u8)u;
+ if (++keyindex >= key_len)
+ keyindex = 0;
+ }
+}
+
+static u32 arcfour_byte(struct arc4context *parc4ctx)
+{
+ u32 x;
+ u32 y;
+ u32 sx, sy;
+ u8 *state;
+ state = parc4ctx->state;
+ x = (parc4ctx->x + 1) & 0xff;
+ sx = state[x];
+ y = (sx + parc4ctx->y) & 0xff;
+ sy = state[y];
+ parc4ctx->x = x;
+ parc4ctx->y = y;
+ state[y] = (u8)sx;
+ state[x] = (u8)sy;
+ return state[(sx + sy) & 0xff];
+}
+
+static void arcfour_encrypt(struct arc4context *parc4ctx, u8 *dest, u8 *src, u32 len)
+{
+ u32 i;
+ for (i = 0; i < len; i++)
+ dest[i] = src[i] ^ (unsigned char)arcfour_byte(parc4ctx);
+}
+
+static int bcrc32initialized;
+static u32 crc32_table[256];
+
+static u8 crc32_reverseBit(u8 data)
+{
+ return (u8)((data<<7)&0x80) | ((data<<5)&0x40) | ((data<<3)&0x20) |
+ ((data<<1)&0x10) | ((data>>1)&0x08) | ((data>>3)&0x04) |
+ ((data>>5)&0x02) | ((data>>7)&0x01);
+}
+
+static void crc32_init(void)
+{
+ if (bcrc32initialized == 1) {
+ return;
+ } else {
+ int i, j;
+ u32 c;
+ u8 *p = (u8 *)&c, *p1;
+ u8 k;
+
+ c = 0x12340000;
+
+ for (i = 0; i < 256; ++i) {
+ k = crc32_reverseBit((u8)i);
+ for (c = ((u32)k) << 24, j = 8; j > 0; --j)
+ c = c & 0x80000000 ? (c << 1) ^ CRC32_POLY : (c << 1);
+ p1 = (u8 *)&crc32_table[i];
+
+ p1[0] = crc32_reverseBit(p[3]);
+ p1[1] = crc32_reverseBit(p[2]);
+ p1[2] = crc32_reverseBit(p[1]);
+ p1[3] = crc32_reverseBit(p[0]);
+ }
+ bcrc32initialized = 1;
+ }
+}
+
+static __le32 getcrc32(u8 *buf, int len)
+{
+ u8 *p;
+ u32 crc;
+ if (bcrc32initialized == 0)
+ crc32_init();
+
+ crc = 0xffffffff; /* preload shift register, per CRC-32 spec */
+
+ for (p = buf; len > 0; ++p, --len)
+ crc = crc32_table[(crc ^ *p) & 0xff] ^ (crc >> 8);
+ return cpu_to_le32(~crc); /* transmit complement, per CRC-32 spec */
+}
+
+/*
+ Need to consider the fragment situation
+*/
+void rtw_wep_encrypt(struct adapter *padapter, u8 *pxmitframe)
+{ /* exclude ICV */
+
+ unsigned char crc[4];
+ struct arc4context mycontext;
+
+ int curfragnum, length;
+ u32 keylength;
+
+ u8 *pframe, *payload, *iv; /* wepkey */
+ u8 wepkey[16];
+ u8 hw_hdr_offset = 0;
+ struct pkt_attrib *pattrib = &((struct xmit_frame *)pxmitframe)->attrib;
+ struct security_priv *psecuritypriv = &padapter->securitypriv;
+ struct xmit_priv *pxmitpriv = &padapter->xmitpriv;
+
+
+ if (((struct xmit_frame *)pxmitframe)->buf_addr == NULL)
+ return;
+
+ hw_hdr_offset = TXDESC_SIZE +
+ (((struct xmit_frame *)pxmitframe)->pkt_offset * PACKET_OFFSET_SZ);
+
+ pframe = ((struct xmit_frame *)pxmitframe)->buf_addr + hw_hdr_offset;
+
+ /* start to encrypt each fragment */
+ if ((pattrib->encrypt == _WEP40_) || (pattrib->encrypt == _WEP104_)) {
+ keylength = psecuritypriv->dot11DefKeylen[psecuritypriv->dot11PrivacyKeyIndex];
+
+ for (curfragnum = 0; curfragnum < pattrib->nr_frags; curfragnum++) {
+ iv = pframe+pattrib->hdrlen;
+ memcpy(&wepkey[0], iv, 3);
+ memcpy(&wepkey[3], &psecuritypriv->dot11DefKey[psecuritypriv->dot11PrivacyKeyIndex].skey[0], keylength);
+ payload = pframe+pattrib->iv_len+pattrib->hdrlen;
+
+ if ((curfragnum+1) == pattrib->nr_frags) { /* the last fragment */
+ length = pattrib->last_txcmdsz-pattrib->hdrlen-pattrib->iv_len-pattrib->icv_len;
+
+ *((__le32 *)crc) = getcrc32(payload, length);
+
+ arcfour_init(&mycontext, wepkey, 3+keylength);
+ arcfour_encrypt(&mycontext, payload, payload, length);
+ arcfour_encrypt(&mycontext, payload+length, crc, 4);
+ } else {
+ length = pxmitpriv->frag_len-pattrib->hdrlen-pattrib->iv_len-pattrib->icv_len;
+ *((__le32 *)crc) = getcrc32(payload, length);
+ arcfour_init(&mycontext, wepkey, 3+keylength);
+ arcfour_encrypt(&mycontext, payload, payload, length);
+ arcfour_encrypt(&mycontext, payload+length, crc, 4);
+
+ pframe += pxmitpriv->frag_len;
+ pframe = (u8 *)round_up((size_t)(pframe), 4);
+ }
+ }
+ }
+
+}
+
+void rtw_wep_decrypt(struct adapter *padapter, u8 *precvframe)
+{
+ /* exclude ICV */
+ u8 crc[4];
+ struct arc4context mycontext;
+ int length;
+ u32 keylength;
+ u8 *pframe, *payload, *iv, wepkey[16];
+ u8 keyindex;
+ struct rx_pkt_attrib *prxattrib = &(((struct recv_frame *)precvframe)->attrib);
+ struct security_priv *psecuritypriv = &padapter->securitypriv;
+
+
+ pframe = (unsigned char *)((struct recv_frame *)precvframe)->rx_data;
+
+ /* start to decrypt recvframe */
+ if ((prxattrib->encrypt == _WEP40_) || (prxattrib->encrypt == _WEP104_)) {
+ iv = pframe+prxattrib->hdrlen;
+ keyindex = prxattrib->key_index;
+ keylength = psecuritypriv->dot11DefKeylen[keyindex];
+ memcpy(&wepkey[0], iv, 3);
+ memcpy(&wepkey[3], &psecuritypriv->dot11DefKey[keyindex].skey[0], keylength);
+ length = ((struct recv_frame *)precvframe)->len-prxattrib->hdrlen-prxattrib->iv_len;
+
+ payload = pframe+prxattrib->iv_len+prxattrib->hdrlen;
+
+ /* decrypt payload include icv */
+ arcfour_init(&mycontext, wepkey, 3+keylength);
+ arcfour_encrypt(&mycontext, payload, payload, length);
+
+ /* calculate icv and compare the icv */
+ *((__le32 *)crc) = getcrc32(payload, length - 4);
+
+ if (crc[3] != payload[length-1] ||
+ crc[2] != payload[length-2] ||
+ crc[1] != payload[length-3] ||
+ crc[0] != payload[length-4]) {
+ RT_TRACE(_module_rtl871x_security_c_, _drv_err_,
+ ("rtw_wep_decrypt:icv error crc (%4ph)!=payload (%4ph)\n",
+ &crc, &payload[length-4]));
+ }
+ }
+ return;
+}
+
+/* 3 ===== TKIP related ===== */
+
+static u32 secmicgetuint32(u8 *p)
+/* Convert from Byte[] to Us3232 in a portable way */
+{
+ s32 i;
+ u32 res = 0;
+ for (i = 0; i < 4; i++)
+ res |= ((u32)(*p++)) << (8*i);
+ return res;
+}
+
+static void secmicputuint32(u8 *p, u32 val)
+/* Convert from Us3232 to Byte[] in a portable way */
+{
+ long i;
+ for (i = 0; i < 4; i++) {
+ *p++ = (u8)(val & 0xff);
+ val >>= 8;
+ }
+}
+
+static void secmicclear(struct mic_data *pmicdata)
+{
+/* Reset the state to the empty message. */
+ pmicdata->L = pmicdata->K0;
+ pmicdata->R = pmicdata->K1;
+ pmicdata->nBytesInM = 0;
+ pmicdata->M = 0;
+}
+
+void rtw_secmicsetkey(struct mic_data *pmicdata, u8 *key)
+{
+ /* Set the key */
+ pmicdata->K0 = secmicgetuint32(key);
+ pmicdata->K1 = secmicgetuint32(key + 4);
+ /* and reset the message */
+ secmicclear(pmicdata);
+}
+
+void rtw_secmicappendbyte(struct mic_data *pmicdata, u8 b)
+{
+ /* Append the byte to our word-sized buffer */
+ pmicdata->M |= ((unsigned long)b) << (8*pmicdata->nBytesInM);
+ pmicdata->nBytesInM++;
+ /* Process the word if it is full. */
+ if (pmicdata->nBytesInM >= 4) {
+ pmicdata->L ^= pmicdata->M;
+ pmicdata->R ^= ROL32(pmicdata->L, 17);
+ pmicdata->L += pmicdata->R;
+ pmicdata->R ^= ((pmicdata->L & 0xff00ff00) >> 8) | ((pmicdata->L & 0x00ff00ff) << 8);
+ pmicdata->L += pmicdata->R;
+ pmicdata->R ^= ROL32(pmicdata->L, 3);
+ pmicdata->L += pmicdata->R;
+ pmicdata->R ^= ROR32(pmicdata->L, 2);
+ pmicdata->L += pmicdata->R;
+ /* Clear the buffer */
+ pmicdata->M = 0;
+ pmicdata->nBytesInM = 0;
+ }
+}
+
+void rtw_secmicappend(struct mic_data *pmicdata, u8 *src, u32 nbytes)
+{
+ /* This is simple */
+ while (nbytes > 0) {
+ rtw_secmicappendbyte(pmicdata, *src++);
+ nbytes--;
+ }
+}
+
+void rtw_secgetmic(struct mic_data *pmicdata, u8 *dst)
+{
+ /* Append the minimum padding */
+ rtw_secmicappendbyte(pmicdata, 0x5a);
+ rtw_secmicappendbyte(pmicdata, 0);
+ rtw_secmicappendbyte(pmicdata, 0);
+ rtw_secmicappendbyte(pmicdata, 0);
+ rtw_secmicappendbyte(pmicdata, 0);
+ /* and then zeroes until the length is a multiple of 4 */
+ while (pmicdata->nBytesInM != 0)
+ rtw_secmicappendbyte(pmicdata, 0);
+ /* The appendByte function has already computed the result. */
+ secmicputuint32(dst, pmicdata->L);
+ secmicputuint32(dst+4, pmicdata->R);
+ /* Reset to the empty message. */
+ secmicclear(pmicdata);
+}
+
+void rtw_seccalctkipmic(u8 *key, u8 *header, u8 *data, u32 data_len, u8 *mic_code, u8 pri)
+{
+ struct mic_data micdata;
+ u8 priority[4] = {0x0, 0x0, 0x0, 0x0};
+ rtw_secmicsetkey(&micdata, key);
+ priority[0] = pri;
+
+ /* Michael MIC pseudo header: DA, SA, 3 x 0, Priority */
+ if (header[1]&1) { /* ToDS == 1 */
+ rtw_secmicappend(&micdata, &header[16], 6); /* DA */
+ if (header[1]&2) /* From Ds == 1 */
+ rtw_secmicappend(&micdata, &header[24], 6);
+ else
+ rtw_secmicappend(&micdata, &header[10], 6);
+ } else { /* ToDS == 0 */
+ rtw_secmicappend(&micdata, &header[4], 6); /* DA */
+ if (header[1]&2) /* From Ds == 1 */
+ rtw_secmicappend(&micdata, &header[16], 6);
+ else
+ rtw_secmicappend(&micdata, &header[10], 6);
+ }
+ rtw_secmicappend(&micdata, &priority[0], 4);
+
+ rtw_secmicappend(&micdata, data, data_len);
+
+ rtw_secgetmic(&micdata, mic_code);
+}
+
+
+
+/* macros for extraction/creation of unsigned char/unsigned short values */
+#define RotR1(v16) ((((v16) >> 1) & 0x7FFF) ^ (((v16) & 1) << 15))
+#define Lo8(v16) ((u8)((v16) & 0x00FF))
+#define Hi8(v16) ((u8)(((v16) >> 8) & 0x00FF))
+#define Lo16(v32) ((u16)((v32) & 0xFFFF))
+#define Hi16(v32) ((u16)(((v32) >> 16) & 0xFFFF))
+#define Mk16(hi, lo) ((lo) ^ (((u16)(hi)) << 8))
+
+/* select the Nth 16-bit word of the temporal key unsigned char array TK[] */
+#define TK16(N) Mk16(tk[2*(N)+1], tk[2*(N)])
+
+/* S-box lookup: 16 bits --> 16 bits */
+#define _S_(v16) (Sbox1[0][Lo8(v16)] ^ Sbox1[1][Hi8(v16)])
+
+/* fixed algorithm "parameters" */
+#define PHASE1_LOOP_CNT 8 /* this needs to be "big enough" */
+#define TA_SIZE 6 /* 48-bit transmitter address */
+#define TK_SIZE 16 /* 128-bit temporal key */
+#define P1K_SIZE 10 /* 80-bit Phase1 key */
+#define RC4_KEY_SIZE 16 /* 128-bit RC4KEY (104 bits unknown) */
+
+/* 2-unsigned char by 2-unsigned char subset of the full AES S-box table */
+static const unsigned short Sbox1[2][256] = { /* Sbox for hash (can be in ROM) */
+{
+ 0xC6A5, 0xF884, 0xEE99, 0xF68D, 0xFF0D, 0xD6BD, 0xDEB1, 0x9154,
+ 0x6050, 0x0203, 0xCEA9, 0x567D, 0xE719, 0xB562, 0x4DE6, 0xEC9A,
+ 0x8F45, 0x1F9D, 0x8940, 0xFA87, 0xEF15, 0xB2EB, 0x8EC9, 0xFB0B,
+ 0x41EC, 0xB367, 0x5FFD, 0x45EA, 0x23BF, 0x53F7, 0xE496, 0x9B5B,
+ 0x75C2, 0xE11C, 0x3DAE, 0x4C6A, 0x6C5A, 0x7E41, 0xF502, 0x834F,
+ 0x685C, 0x51F4, 0xD134, 0xF908, 0xE293, 0xAB73, 0x6253, 0x2A3F,
+ 0x080C, 0x9552, 0x4665, 0x9D5E, 0x3028, 0x37A1, 0x0A0F, 0x2FB5,
+ 0x0E09, 0x2436, 0x1B9B, 0xDF3D, 0xCD26, 0x4E69, 0x7FCD, 0xEA9F,
+ 0x121B, 0x1D9E, 0x5874, 0x342E, 0x362D, 0xDCB2, 0xB4EE, 0x5BFB,
+ 0xA4F6, 0x764D, 0xB761, 0x7DCE, 0x527B, 0xDD3E, 0x5E71, 0x1397,
+ 0xA6F5, 0xB968, 0x0000, 0xC12C, 0x4060, 0xE31F, 0x79C8, 0xB6ED,
+ 0xD4BE, 0x8D46, 0x67D9, 0x724B, 0x94DE, 0x98D4, 0xB0E8, 0x854A,
+ 0xBB6B, 0xC52A, 0x4FE5, 0xED16, 0x86C5, 0x9AD7, 0x6655, 0x1194,
+ 0x8ACF, 0xE910, 0x0406, 0xFE81, 0xA0F0, 0x7844, 0x25BA, 0x4BE3,
+ 0xA2F3, 0x5DFE, 0x80C0, 0x058A, 0x3FAD, 0x21BC, 0x7048, 0xF104,
+ 0x63DF, 0x77C1, 0xAF75, 0x4263, 0x2030, 0xE51A, 0xFD0E, 0xBF6D,
+ 0x814C, 0x1814, 0x2635, 0xC32F, 0xBEE1, 0x35A2, 0x88CC, 0x2E39,
+ 0x9357, 0x55F2, 0xFC82, 0x7A47, 0xC8AC, 0xBAE7, 0x322B, 0xE695,
+ 0xC0A0, 0x1998, 0x9ED1, 0xA37F, 0x4466, 0x547E, 0x3BAB, 0x0B83,
+ 0x8CCA, 0xC729, 0x6BD3, 0x283C, 0xA779, 0xBCE2, 0x161D, 0xAD76,
+ 0xDB3B, 0x6456, 0x744E, 0x141E, 0x92DB, 0x0C0A, 0x486C, 0xB8E4,
+ 0x9F5D, 0xBD6E, 0x43EF, 0xC4A6, 0x39A8, 0x31A4, 0xD337, 0xF28B,
+ 0xD532, 0x8B43, 0x6E59, 0xDAB7, 0x018C, 0xB164, 0x9CD2, 0x49E0,
+ 0xD8B4, 0xACFA, 0xF307, 0xCF25, 0xCAAF, 0xF48E, 0x47E9, 0x1018,
+ 0x6FD5, 0xF088, 0x4A6F, 0x5C72, 0x3824, 0x57F1, 0x73C7, 0x9751,
+ 0xCB23, 0xA17C, 0xE89C, 0x3E21, 0x96DD, 0x61DC, 0x0D86, 0x0F85,
+ 0xE090, 0x7C42, 0x71C4, 0xCCAA, 0x90D8, 0x0605, 0xF701, 0x1C12,
+ 0xC2A3, 0x6A5F, 0xAEF9, 0x69D0, 0x1791, 0x9958, 0x3A27, 0x27B9,
+ 0xD938, 0xEB13, 0x2BB3, 0x2233, 0xD2BB, 0xA970, 0x0789, 0x33A7,
+ 0x2DB6, 0x3C22, 0x1592, 0xC920, 0x8749, 0xAAFF, 0x5078, 0xA57A,
+ 0x038F, 0x59F8, 0x0980, 0x1A17, 0x65DA, 0xD731, 0x84C6, 0xD0B8,
+ 0x82C3, 0x29B0, 0x5A77, 0x1E11, 0x7BCB, 0xA8FC, 0x6DD6, 0x2C3A,
+ },
+
+ { /* second half of table is unsigned char-reversed version of first! */
+ 0xA5C6, 0x84F8, 0x99EE, 0x8DF6, 0x0DFF, 0xBDD6, 0xB1DE, 0x5491,
+ 0x5060, 0x0302, 0xA9CE, 0x7D56, 0x19E7, 0x62B5, 0xE64D, 0x9AEC,
+ 0x458F, 0x9D1F, 0x4089, 0x87FA, 0x15EF, 0xEBB2, 0xC98E, 0x0BFB,
+ 0xEC41, 0x67B3, 0xFD5F, 0xEA45, 0xBF23, 0xF753, 0x96E4, 0x5B9B,
+ 0xC275, 0x1CE1, 0xAE3D, 0x6A4C, 0x5A6C, 0x417E, 0x02F5, 0x4F83,
+ 0x5C68, 0xF451, 0x34D1, 0x08F9, 0x93E2, 0x73AB, 0x5362, 0x3F2A,
+ 0x0C08, 0x5295, 0x6546, 0x5E9D, 0x2830, 0xA137, 0x0F0A, 0xB52F,
+ 0x090E, 0x3624, 0x9B1B, 0x3DDF, 0x26CD, 0x694E, 0xCD7F, 0x9FEA,
+ 0x1B12, 0x9E1D, 0x7458, 0x2E34, 0x2D36, 0xB2DC, 0xEEB4, 0xFB5B,
+ 0xF6A4, 0x4D76, 0x61B7, 0xCE7D, 0x7B52, 0x3EDD, 0x715E, 0x9713,
+ 0xF5A6, 0x68B9, 0x0000, 0x2CC1, 0x6040, 0x1FE3, 0xC879, 0xEDB6,
+ 0xBED4, 0x468D, 0xD967, 0x4B72, 0xDE94, 0xD498, 0xE8B0, 0x4A85,
+ 0x6BBB, 0x2AC5, 0xE54F, 0x16ED, 0xC586, 0xD79A, 0x5566, 0x9411,
+ 0xCF8A, 0x10E9, 0x0604, 0x81FE, 0xF0A0, 0x4478, 0xBA25, 0xE34B,
+ 0xF3A2, 0xFE5D, 0xC080, 0x8A05, 0xAD3F, 0xBC21, 0x4870, 0x04F1,
+ 0xDF63, 0xC177, 0x75AF, 0x6342, 0x3020, 0x1AE5, 0x0EFD, 0x6DBF,
+ 0x4C81, 0x1418, 0x3526, 0x2FC3, 0xE1BE, 0xA235, 0xCC88, 0x392E,
+ 0x5793, 0xF255, 0x82FC, 0x477A, 0xACC8, 0xE7BA, 0x2B32, 0x95E6,
+ 0xA0C0, 0x9819, 0xD19E, 0x7FA3, 0x6644, 0x7E54, 0xAB3B, 0x830B,
+ 0xCA8C, 0x29C7, 0xD36B, 0x3C28, 0x79A7, 0xE2BC, 0x1D16, 0x76AD,
+ 0x3BDB, 0x5664, 0x4E74, 0x1E14, 0xDB92, 0x0A0C, 0x6C48, 0xE4B8,
+ 0x5D9F, 0x6EBD, 0xEF43, 0xA6C4, 0xA839, 0xA431, 0x37D3, 0x8BF2,
+ 0x32D5, 0x438B, 0x596E, 0xB7DA, 0x8C01, 0x64B1, 0xD29C, 0xE049,
+ 0xB4D8, 0xFAAC, 0x07F3, 0x25CF, 0xAFCA, 0x8EF4, 0xE947, 0x1810,
+ 0xD56F, 0x88F0, 0x6F4A, 0x725C, 0x2438, 0xF157, 0xC773, 0x5197,
+ 0x23CB, 0x7CA1, 0x9CE8, 0x213E, 0xDD96, 0xDC61, 0x860D, 0x850F,
+ 0x90E0, 0x427C, 0xC471, 0xAACC, 0xD890, 0x0506, 0x01F7, 0x121C,
+ 0xA3C2, 0x5F6A, 0xF9AE, 0xD069, 0x9117, 0x5899, 0x273A, 0xB927,
+ 0x38D9, 0x13EB, 0xB32B, 0x3322, 0xBBD2, 0x70A9, 0x8907, 0xA733,
+ 0xB62D, 0x223C, 0x9215, 0x20C9, 0x4987, 0xFFAA, 0x7850, 0x7AA5,
+ 0x8F03, 0xF859, 0x8009, 0x171A, 0xDA65, 0x31D7, 0xC684, 0xB8D0,
+ 0xC382, 0xB029, 0x775A, 0x111E, 0xCB7B, 0xFCA8, 0xD66D, 0x3A2C,
+ }
+};
+
+ /*
+**********************************************************************
+* Routine: Phase 1 -- generate P1K, given TA, TK, IV32
+*
+* Inputs:
+* tk[] = temporal key [128 bits]
+* ta[] = transmitter's MAC address [ 48 bits]
+* iv32 = upper 32 bits of IV [ 32 bits]
+* Output:
+* p1k[] = Phase 1 key [ 80 bits]
+*
+* Note:
+* This function only needs to be called every 2**16 packets,
+* although in theory it could be called every packet.
+*
+**********************************************************************
+*/
+static void phase1(u16 *p1k, const u8 *tk, const u8 *ta, u32 iv32)
+{
+ int i;
+ /* Initialize the 80 bits of P1K[] from IV32 and TA[0..5] */
+ p1k[0] = Lo16(iv32);
+ p1k[1] = Hi16(iv32);
+ p1k[2] = Mk16(ta[1], ta[0]); /* use TA[] as little-endian */
+ p1k[3] = Mk16(ta[3], ta[2]);
+ p1k[4] = Mk16(ta[5], ta[4]);
+
+ /* Now compute an unbalanced Feistel cipher with 80-bit block */
+ /* size on the 80-bit block P1K[], using the 128-bit key TK[] */
+ for (i = 0; i < PHASE1_LOOP_CNT; i++) { /* Each add operation here is mod 2**16 */
+ p1k[0] += _S_(p1k[4] ^ TK16((i&1)+0));
+ p1k[1] += _S_(p1k[0] ^ TK16((i&1)+2));
+ p1k[2] += _S_(p1k[1] ^ TK16((i&1)+4));
+ p1k[3] += _S_(p1k[2] ^ TK16((i&1)+6));
+ p1k[4] += _S_(p1k[3] ^ TK16((i&1)+0));
+ p1k[4] += (unsigned short)i; /* avoid "slide attacks" */
+ }
+}
+
+/*
+**********************************************************************
+* Routine: Phase 2 -- generate RC4KEY, given TK, P1K, IV16
+*
+* Inputs:
+* tk[] = Temporal key [128 bits]
+* p1k[] = Phase 1 output key [ 80 bits]
+* iv16 = low 16 bits of IV counter [ 16 bits]
+* Output:
+* rc4key[] = the key used to encrypt the packet [128 bits]
+*
+* Note:
+* The value {TA, IV32, IV16} for Phase1/Phase2 must be unique
+* across all packets using the same key TK value. Then, for a
+* given value of TK[], this TKIP48 construction guarantees that
+* the final RC4KEY value is unique across all packets.
+*
+* Suggested implementation optimization: if PPK[] is "overlaid"
+* appropriately on RC4KEY[], there is no need for the final
+* for loop below that copies the PPK[] result into RC4KEY[].
+*
+**********************************************************************
+*/
+static void phase2(u8 *rc4key, const u8 *tk, const u16 *p1k, u16 iv16)
+{
+ int i;
+ u16 PPK[6]; /* temporary key for mixing */
+ /* Note: all adds in the PPK[] equations below are mod 2**16 */
+ for (i = 0; i < 5; i++)
+ PPK[i] = p1k[i]; /* first, copy P1K to PPK */
+ PPK[5] = p1k[4] + iv16; /* next, add in IV16 */
+
+ /* Bijective non-linear mixing of the 96 bits of PPK[0..5] */
+ PPK[0] += _S_(PPK[5] ^ TK16(0)); /* Mix key in each "round" */
+ PPK[1] += _S_(PPK[0] ^ TK16(1));
+ PPK[2] += _S_(PPK[1] ^ TK16(2));
+ PPK[3] += _S_(PPK[2] ^ TK16(3));
+ PPK[4] += _S_(PPK[3] ^ TK16(4));
+ PPK[5] += _S_(PPK[4] ^ TK16(5)); /* Total # S-box lookups == 6 */
+
+ /* Final sweep: bijective, "linear". Rotates kill LSB correlations */
+ PPK[0] += RotR1(PPK[5] ^ TK16(6));
+ PPK[1] += RotR1(PPK[0] ^ TK16(7)); /* Use all of TK[] in Phase2 */
+ PPK[2] += RotR1(PPK[1]);
+ PPK[3] += RotR1(PPK[2]);
+ PPK[4] += RotR1(PPK[3]);
+ PPK[5] += RotR1(PPK[4]);
+ /* Note: At this point, for a given key TK[0..15], the 96-bit output */
+ /* value PPK[0..5] is guaranteed to be unique, as a function */
+ /* of the 96-bit "input" value {TA, IV32, IV16}. That is, P1K */
+ /* is now a keyed permutation of {TA, IV32, IV16}. */
+
+ /* Set RC4KEY[0..3], which includes "cleartext" portion of RC4 key */
+ rc4key[0] = Hi8(iv16); /* RC4KEY[0..2] is the WEP IV */
+ rc4key[1] = (Hi8(iv16) | 0x20) & 0x7F; /* Help avoid weak (FMS) keys */
+ rc4key[2] = Lo8(iv16);
+ rc4key[3] = Lo8((PPK[5] ^ TK16(0)) >> 1);
+
+ /* Copy 96 bits of PPK[0..5] to RC4KEY[4..15] (little-endian) */
+ for (i = 0; i < 6; i++) {
+ rc4key[4+2*i] = Lo8(PPK[i]);
+ rc4key[5+2*i] = Hi8(PPK[i]);
+ }
+}
+
+/* The hlen isn't include the IV */
+u32 rtw_tkip_encrypt(struct adapter *padapter, u8 *pxmitframe)
+{ /* exclude ICV */
+ u16 pnl;
+ u32 pnh;
+ u8 rc4key[16];
+ u8 ttkey[16];
+ u8 crc[4];
+ u8 hw_hdr_offset = 0;
+ struct arc4context mycontext;
+ int curfragnum, length;
+
+ u8 *pframe, *payload, *iv, *prwskey;
+ union pn48 dot11txpn;
+ struct sta_info *stainfo;
+ struct pkt_attrib *pattrib = &((struct xmit_frame *)pxmitframe)->attrib;
+ struct security_priv *psecuritypriv = &padapter->securitypriv;
+ struct xmit_priv *pxmitpriv = &padapter->xmitpriv;
+ u32 res = _SUCCESS;
+
+ if (((struct xmit_frame *)pxmitframe)->buf_addr == NULL)
+ return _FAIL;
+
+ hw_hdr_offset = TXDESC_SIZE +
+ (((struct xmit_frame *)pxmitframe)->pkt_offset * PACKET_OFFSET_SZ);
+ pframe = ((struct xmit_frame *)pxmitframe)->buf_addr + hw_hdr_offset;
+ /* 4 start to encrypt each fragment */
+ if (pattrib->encrypt == _TKIP_) {
+ if (pattrib->psta)
+ stainfo = pattrib->psta;
+ else
+ stainfo = rtw_get_stainfo(&padapter->stapriv, &pattrib->ra[0]);
+
+ if (stainfo != NULL) {
+ RT_TRACE(_module_rtl871x_security_c_, _drv_err_, ("rtw_tkip_encrypt: stainfo!= NULL!!!\n"));
+
+ if (IS_MCAST(pattrib->ra))
+ prwskey = psecuritypriv->dot118021XGrpKey[psecuritypriv->dot118021XGrpKeyid].skey;
+ else
+ prwskey = &stainfo->dot118021x_UncstKey.skey[0];
+
+ for (curfragnum = 0; curfragnum < pattrib->nr_frags; curfragnum++) {
+ iv = pframe+pattrib->hdrlen;
+ payload = pframe+pattrib->iv_len+pattrib->hdrlen;
+
+ GET_TKIP_PN(iv, dot11txpn);
+
+ pnl = (u16)(dot11txpn.val);
+ pnh = (u32)(dot11txpn.val>>16);
+ phase1((u16 *)&ttkey[0], prwskey, &pattrib->ta[0], pnh);
+ phase2(&rc4key[0], prwskey, (u16 *)&ttkey[0], pnl);
+
+ if ((curfragnum+1) == pattrib->nr_frags) { /* 4 the last fragment */
+ length = pattrib->last_txcmdsz-pattrib->hdrlen-pattrib->iv_len-pattrib->icv_len;
+ RT_TRACE(_module_rtl871x_security_c_, _drv_info_,
+ ("pattrib->iv_len=%x, pattrib->icv_len=%x\n",
+ pattrib->iv_len, pattrib->icv_len));
+ *((__le32 *)crc) = getcrc32(payload, length);/* modified by Amy*/
+
+ arcfour_init(&mycontext, rc4key, 16);
+ arcfour_encrypt(&mycontext, payload, payload, length);
+ arcfour_encrypt(&mycontext, payload+length, crc, 4);
+ } else {
+ length = pxmitpriv->frag_len-pattrib->hdrlen-pattrib->iv_len-pattrib->icv_len;
+ *((__le32 *)crc) = getcrc32(payload, length);/* modified by Amy*/
+ arcfour_init(&mycontext, rc4key, 16);
+ arcfour_encrypt(&mycontext, payload, payload, length);
+ arcfour_encrypt(&mycontext, payload+length, crc, 4);
+
+ pframe += pxmitpriv->frag_len;
+ pframe = (u8 *)round_up((size_t)(pframe), 4);
+ }
+ }
+ } else {
+ RT_TRACE(_module_rtl871x_security_c_, _drv_err_, ("rtw_tkip_encrypt: stainfo==NULL!!!\n"));
+ res = _FAIL;
+ }
+ }
+ return res;
+}
+
+/* The hlen isn't include the IV */
+u32 rtw_tkip_decrypt(struct adapter *padapter, u8 *precvframe)
+{ /* exclude ICV */
+ u16 pnl;
+ u32 pnh;
+ u8 rc4key[16];
+ u8 ttkey[16];
+ u8 crc[4];
+ struct arc4context mycontext;
+ int length;
+
+ u8 *pframe, *payload, *iv, *prwskey;
+ union pn48 dot11txpn;
+ struct sta_info *stainfo;
+ struct rx_pkt_attrib *prxattrib = &((struct recv_frame *)precvframe)->attrib;
+ struct security_priv *psecuritypriv = &padapter->securitypriv;
+ u32 res = _SUCCESS;
+
+
+ pframe = (unsigned char *)((struct recv_frame *)precvframe)->rx_data;
+
+ /* 4 start to decrypt recvframe */
+ if (prxattrib->encrypt == _TKIP_) {
+ stainfo = rtw_get_stainfo(&padapter->stapriv, &prxattrib->ta[0]);
+ if (stainfo != NULL) {
+ if (IS_MCAST(prxattrib->ra)) {
+ if (!psecuritypriv->binstallGrpkey) {
+ res = _FAIL;
+ DBG_88E("%s:rx bc/mc packets, but didn't install group key!!!!!!!!!!\n", __func__);
+ goto exit;
+ }
+ prwskey = psecuritypriv->dot118021XGrpKey[prxattrib->key_index].skey;
+ } else {
+ RT_TRACE(_module_rtl871x_security_c_, _drv_err_, ("rtw_tkip_decrypt: stainfo!= NULL!!!\n"));
+ prwskey = &stainfo->dot118021x_UncstKey.skey[0];
+ }
+
+ iv = pframe+prxattrib->hdrlen;
+ payload = pframe+prxattrib->iv_len+prxattrib->hdrlen;
+ length = ((struct recv_frame *)precvframe)->len-prxattrib->hdrlen-prxattrib->iv_len;
+
+ GET_TKIP_PN(iv, dot11txpn);
+
+ pnl = (u16)(dot11txpn.val);
+ pnh = (u32)(dot11txpn.val>>16);
+
+ phase1((u16 *)&ttkey[0], prwskey, &prxattrib->ta[0], pnh);
+ phase2(&rc4key[0], prwskey, (unsigned short *)&ttkey[0], pnl);
+
+ /* 4 decrypt payload include icv */
+
+ arcfour_init(&mycontext, rc4key, 16);
+ arcfour_encrypt(&mycontext, payload, payload, length);
+
+ *((__le32 *)crc) = getcrc32(payload, length-4);
+
+ if (crc[3] != payload[length-1] ||
+ crc[2] != payload[length-2] ||
+ crc[1] != payload[length-3] ||
+ crc[0] != payload[length-4]) {
+ RT_TRACE(_module_rtl871x_security_c_, _drv_err_,
+ ("rtw_wep_decrypt:icv error crc (%4ph)!=payload (%4ph)\n",
+ &crc, &payload[length-4]));
+ res = _FAIL;
+ }
+ } else {
+ RT_TRACE(_module_rtl871x_security_c_, _drv_err_, ("rtw_tkip_decrypt: stainfo==NULL!!!\n"));
+ res = _FAIL;
+ }
+ }
+exit:
+ return res;
+}
+
+/* 3 ===== AES related ===== */
+
+
+#define MAX_MSG_SIZE 2048
+/*****************************/
+/******** SBOX Table *********/
+/*****************************/
+
+static u8 sbox_table[256] = {
+ 0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5,
+ 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76,
+ 0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0,
+ 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0,
+ 0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc,
+ 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15,
+ 0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a,
+ 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75,
+ 0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0,
+ 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84,
+ 0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b,
+ 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf,
+ 0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85,
+ 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8,
+ 0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5,
+ 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2,
+ 0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17,
+ 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73,
+ 0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88,
+ 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb,
+ 0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c,
+ 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79,
+ 0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9,
+ 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08,
+ 0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6,
+ 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a,
+ 0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e,
+ 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e,
+ 0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94,
+ 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf,
+ 0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68,
+ 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16
+};
+
+/*****************************/
+/**** Function Prototypes ****/
+/*****************************/
+
+static void bitwise_xor(u8 *ina, u8 *inb, u8 *out);
+static void construct_mic_iv(u8 *mic_header1, int qc_exists, int a4_exists, u8 *mpdu, uint payload_length, u8 *pn_vector);
+static void construct_mic_header1(u8 *mic_header1, int header_length, u8 *mpdu);
+static void construct_mic_header2(u8 *mic_header2, u8 *mpdu, int a4_exists, int qc_exists);
+static void construct_ctr_preload(u8 *ctr_preload, int a4_exists, int qc_exists, u8 *mpdu, u8 *pn_vector, int c);
+static void xor_128(u8 *a, u8 *b, u8 *out);
+static void xor_32(u8 *a, u8 *b, u8 *out);
+static u8 sbox(u8 a);
+static void next_key(u8 *key, int round);
+static void byte_sub(u8 *in, u8 *out);
+static void shift_row(u8 *in, u8 *out);
+static void mix_column(u8 *in, u8 *out);
+static void aes128k128d(u8 *key, u8 *data, u8 *ciphertext);
+
+/****************************************/
+/* aes128k128d() */
+/* Performs a 128 bit AES encrypt with */
+/* 128 bit data. */
+/****************************************/
+static void xor_128(u8 *a, u8 *b, u8 *out)
+{
+ int i;
+ for (i = 0; i < 16; i++)
+ out[i] = a[i] ^ b[i];
+}
+
+static void xor_32(u8 *a, u8 *b, u8 *out)
+{
+ int i;
+ for (i = 0; i < 4; i++)
+ out[i] = a[i] ^ b[i];
+}
+
+static u8 sbox(u8 a)
+{
+ return sbox_table[(int)a];
+}
+
+static void next_key(u8 *key, int round)
+{
+ u8 rcon;
+ u8 sbox_key[4];
+ u8 rcon_table[12] = {
+ 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80,
+ 0x1b, 0x36, 0x36, 0x36
+ };
+ sbox_key[0] = sbox(key[13]);
+ sbox_key[1] = sbox(key[14]);
+ sbox_key[2] = sbox(key[15]);
+ sbox_key[3] = sbox(key[12]);
+
+ rcon = rcon_table[round];
+
+ xor_32(&key[0], sbox_key, &key[0]);
+ key[0] = key[0] ^ rcon;
+
+ xor_32(&key[4], &key[0], &key[4]);
+ xor_32(&key[8], &key[4], &key[8]);
+ xor_32(&key[12], &key[8], &key[12]);
+}
+
+static void byte_sub(u8 *in, u8 *out)
+{
+ int i;
+ for (i = 0; i < 16; i++)
+ out[i] = sbox(in[i]);
+}
+
+static void shift_row(u8 *in, u8 *out)
+{
+ out[0] = in[0];
+ out[1] = in[5];
+ out[2] = in[10];
+ out[3] = in[15];
+ out[4] = in[4];
+ out[5] = in[9];
+ out[6] = in[14];
+ out[7] = in[3];
+ out[8] = in[8];
+ out[9] = in[13];
+ out[10] = in[2];
+ out[11] = in[7];
+ out[12] = in[12];
+ out[13] = in[1];
+ out[14] = in[6];
+ out[15] = in[11];
+}
+
+static void mix_column(u8 *in, u8 *out)
+{
+ int i;
+ u8 add1b[4];
+ u8 add1bf7[4];
+ u8 rotl[4];
+ u8 swap_halves[4];
+ u8 andf7[4];
+ u8 rotr[4];
+ u8 temp[4];
+ u8 tempb[4];
+ for (i = 0 ; i < 4; i++) {
+ if ((in[i] & 0x80) == 0x80)
+ add1b[i] = 0x1b;
+ else
+ add1b[i] = 0x00;
+ }
+
+ swap_halves[0] = in[2]; /* Swap halves */
+ swap_halves[1] = in[3];
+ swap_halves[2] = in[0];
+ swap_halves[3] = in[1];
+
+ rotl[0] = in[3]; /* Rotate left 8 bits */
+ rotl[1] = in[0];
+ rotl[2] = in[1];
+ rotl[3] = in[2];
+
+ andf7[0] = in[0] & 0x7f;
+ andf7[1] = in[1] & 0x7f;
+ andf7[2] = in[2] & 0x7f;
+ andf7[3] = in[3] & 0x7f;
+
+ for (i = 3; i > 0; i--) { /* logical shift left 1 bit */
+ andf7[i] = andf7[i] << 1;
+ if ((andf7[i-1] & 0x80) == 0x80)
+ andf7[i] = (andf7[i] | 0x01);
+ }
+ andf7[0] = andf7[0] << 1;
+ andf7[0] = andf7[0] & 0xfe;
+
+ xor_32(add1b, andf7, add1bf7);
+
+ xor_32(in, add1bf7, rotr);
+
+ temp[0] = rotr[0]; /* Rotate right 8 bits */
+ rotr[0] = rotr[1];
+ rotr[1] = rotr[2];
+ rotr[2] = rotr[3];
+ rotr[3] = temp[0];
+
+ xor_32(add1bf7, rotr, temp);
+ xor_32(swap_halves, rotl, tempb);
+ xor_32(temp, tempb, out);
+}
+
+static void aes128k128d(u8 *key, u8 *data, u8 *ciphertext)
+{
+ int round;
+ int i;
+ u8 intermediatea[16];
+ u8 intermediateb[16];
+ u8 round_key[16];
+ for (i = 0; i < 16; i++)
+ round_key[i] = key[i];
+ for (round = 0; round < 11; round++) {
+ if (round == 0) {
+ xor_128(round_key, data, ciphertext);
+ next_key(round_key, round);
+ } else if (round == 10) {
+ byte_sub(ciphertext, intermediatea);
+ shift_row(intermediatea, intermediateb);
+ xor_128(intermediateb, round_key, ciphertext);
+ } else { /* 1 - 9 */
+ byte_sub(ciphertext, intermediatea);
+ shift_row(intermediatea, intermediateb);
+ mix_column(&intermediateb[0], &intermediatea[0]);
+ mix_column(&intermediateb[4], &intermediatea[4]);
+ mix_column(&intermediateb[8], &intermediatea[8]);
+ mix_column(&intermediateb[12], &intermediatea[12]);
+ xor_128(intermediatea, round_key, ciphertext);
+ next_key(round_key, round);
+ }
+ }
+}
+
+/************************************************/
+/* construct_mic_iv() */
+/* Builds the MIC IV from header fields and PN */
+/************************************************/
+static void construct_mic_iv(u8 *mic_iv, int qc_exists, int a4_exists, u8 *mpdu,
+ uint payload_length, u8 *pn_vector)
+{
+ int i;
+ mic_iv[0] = 0x59;
+ if (qc_exists && a4_exists)
+ mic_iv[1] = mpdu[30] & 0x0f; /* QoS_TC */
+ if (qc_exists && !a4_exists)
+ mic_iv[1] = mpdu[24] & 0x0f; /* mute bits 7-4 */
+ if (!qc_exists)
+ mic_iv[1] = 0x00;
+ for (i = 2; i < 8; i++)
+ mic_iv[i] = mpdu[i + 8]; /* mic_iv[2:7] = A2[0:5] = mpdu[10:15] */
+ for (i = 8; i < 14; i++)
+ mic_iv[i] = pn_vector[13 - i]; /* mic_iv[8:13] = PN[5:0] */
+ mic_iv[14] = (unsigned char)(payload_length / 256);
+ mic_iv[15] = (unsigned char)(payload_length % 256);
+}
+
+/************************************************/
+/* construct_mic_header1() */
+/* Builds the first MIC header block from */
+/* header fields. */
+/************************************************/
+static void construct_mic_header1(u8 *mic_header1, int header_length, u8 *mpdu)
+{
+ mic_header1[0] = (u8)((header_length - 2) / 256);
+ mic_header1[1] = (u8)((header_length - 2) % 256);
+ mic_header1[2] = mpdu[0] & 0xcf; /* Mute CF poll & CF ack bits */
+ mic_header1[3] = mpdu[1] & 0xc7; /* Mute retry, more data and pwr mgt bits */
+ mic_header1[4] = mpdu[4]; /* A1 */
+ mic_header1[5] = mpdu[5];
+ mic_header1[6] = mpdu[6];
+ mic_header1[7] = mpdu[7];
+ mic_header1[8] = mpdu[8];
+ mic_header1[9] = mpdu[9];
+ mic_header1[10] = mpdu[10]; /* A2 */
+ mic_header1[11] = mpdu[11];
+ mic_header1[12] = mpdu[12];
+ mic_header1[13] = mpdu[13];
+ mic_header1[14] = mpdu[14];
+ mic_header1[15] = mpdu[15];
+}
+
+/************************************************/
+/* construct_mic_header2() */
+/* Builds the last MIC header block from */
+/* header fields. */
+/************************************************/
+static void construct_mic_header2(u8 *mic_header2, u8 *mpdu, int a4_exists, int qc_exists)
+{
+ int i;
+ for (i = 0; i < 16; i++)
+ mic_header2[i] = 0x00;
+
+ mic_header2[0] = mpdu[16]; /* A3 */
+ mic_header2[1] = mpdu[17];
+ mic_header2[2] = mpdu[18];
+ mic_header2[3] = mpdu[19];
+ mic_header2[4] = mpdu[20];
+ mic_header2[5] = mpdu[21];
+
+ mic_header2[6] = 0x00;
+ mic_header2[7] = 0x00; /* mpdu[23]; */
+
+ if (!qc_exists && a4_exists) {
+ for (i = 0; i < 6; i++)
+ mic_header2[8+i] = mpdu[24+i]; /* A4 */
+ }
+
+ if (qc_exists && !a4_exists) {
+ mic_header2[8] = mpdu[24] & 0x0f; /* mute bits 15 - 4 */
+ mic_header2[9] = mpdu[25] & 0x00;
+ }
+
+ if (qc_exists && a4_exists) {
+ for (i = 0; i < 6; i++)
+ mic_header2[8+i] = mpdu[24+i]; /* A4 */
+
+ mic_header2[14] = mpdu[30] & 0x0f;
+ mic_header2[15] = mpdu[31] & 0x00;
+ }
+
+}
+
+/************************************************/
+/* construct_mic_header2() */
+/* Builds the last MIC header block from */
+/* header fields. */
+/************************************************/
+static void construct_ctr_preload(u8 *ctr_preload, int a4_exists, int qc_exists, u8 *mpdu, u8 *pn_vector, int c)
+{
+ int i;
+ for (i = 0; i < 16; i++)
+ ctr_preload[i] = 0x00;
+ i = 0;
+
+ ctr_preload[0] = 0x01; /* flag */
+ if (qc_exists && a4_exists)
+ ctr_preload[1] = mpdu[30] & 0x0f; /* QoC_Control */
+ if (qc_exists && !a4_exists)
+ ctr_preload[1] = mpdu[24] & 0x0f;
+
+ for (i = 2; i < 8; i++)
+ ctr_preload[i] = mpdu[i + 8]; /* ctr_preload[2:7] = A2[0:5] = mpdu[10:15] */
+ for (i = 8; i < 14; i++)
+ ctr_preload[i] = pn_vector[13 - i]; /* ctr_preload[8:13] = PN[5:0] */
+ ctr_preload[14] = (unsigned char)(c / 256); /* Ctr */
+ ctr_preload[15] = (unsigned char)(c % 256);
+}
+
+/************************************/
+/* bitwise_xor() */
+/* A 128 bit, bitwise exclusive or */
+/************************************/
+static void bitwise_xor(u8 *ina, u8 *inb, u8 *out)
+{
+ int i;
+ for (i = 0; i < 16; i++)
+ out[i] = ina[i] ^ inb[i];
+}
+
+static int aes_cipher(u8 *key, uint hdrlen, u8 *pframe, uint plen)
+{
+ uint qc_exists, a4_exists, i, j, payload_remainder,
+ num_blocks, payload_index;
+
+ u8 pn_vector[6];
+ u8 mic_iv[16];
+ u8 mic_header1[16];
+ u8 mic_header2[16];
+ u8 ctr_preload[16];
+
+ /* Intermediate Buffers */
+ u8 chain_buffer[16];
+ u8 aes_out[16];
+ u8 padded_buffer[16];
+ u8 mic[8];
+ uint frtype = GetFrameType(pframe);
+ uint frsubtype = GetFrameSubType(pframe);
+
+ frsubtype >>= 4;
+
+ memset((void *)mic_iv, 0, 16);
+ memset((void *)mic_header1, 0, 16);
+ memset((void *)mic_header2, 0, 16);
+ memset((void *)ctr_preload, 0, 16);
+ memset((void *)chain_buffer, 0, 16);
+ memset((void *)aes_out, 0, 16);
+ memset((void *)padded_buffer, 0, 16);
+
+ if ((hdrlen == WLAN_HDR_A3_LEN) || (hdrlen == WLAN_HDR_A3_QOS_LEN))
+ a4_exists = 0;
+ else
+ a4_exists = 1;
+
+ if ((frtype == WIFI_DATA_CFACK) || (frtype == WIFI_DATA_CFPOLL) || (frtype == WIFI_DATA_CFACKPOLL)) {
+ qc_exists = 1;
+ if (hdrlen != WLAN_HDR_A3_QOS_LEN)
+ hdrlen += 2;
+ } else if ((frsubtype == 0x08) || (frsubtype == 0x09) || (frsubtype == 0x0a) || (frsubtype == 0x0b)) {
+ if (hdrlen != WLAN_HDR_A3_QOS_LEN)
+ hdrlen += 2;
+ qc_exists = 1;
+ } else {
+ qc_exists = 0;
+ }
+
+ pn_vector[0] = pframe[hdrlen];
+ pn_vector[1] = pframe[hdrlen+1];
+ pn_vector[2] = pframe[hdrlen+4];
+ pn_vector[3] = pframe[hdrlen+5];
+ pn_vector[4] = pframe[hdrlen+6];
+ pn_vector[5] = pframe[hdrlen+7];
+
+ construct_mic_iv(mic_iv, qc_exists, a4_exists, pframe, plen, pn_vector);
+
+ construct_mic_header1(mic_header1, hdrlen, pframe);
+ construct_mic_header2(mic_header2, pframe, a4_exists, qc_exists);
+
+ payload_remainder = plen % 16;
+ num_blocks = plen / 16;
+
+ /* Find start of payload */
+ payload_index = hdrlen + 8;
+
+ /* Calculate MIC */
+ aes128k128d(key, mic_iv, aes_out);
+ bitwise_xor(aes_out, mic_header1, chain_buffer);
+ aes128k128d(key, chain_buffer, aes_out);
+ bitwise_xor(aes_out, mic_header2, chain_buffer);
+ aes128k128d(key, chain_buffer, aes_out);
+
+ for (i = 0; i < num_blocks; i++) {
+ bitwise_xor(aes_out, &pframe[payload_index], chain_buffer);/* bitwise_xor(aes_out, &message[payload_index], chain_buffer); */
+
+ payload_index += 16;
+ aes128k128d(key, chain_buffer, aes_out);
+ }
+
+ /* Add on the final payload block if it needs padding */
+ if (payload_remainder > 0) {
+ for (j = 0; j < 16; j++)
+ padded_buffer[j] = 0x00;
+ for (j = 0; j < payload_remainder; j++)
+ padded_buffer[j] = pframe[payload_index++];/* padded_buffer[j] = message[payload_index++]; */
+ bitwise_xor(aes_out, padded_buffer, chain_buffer);
+ aes128k128d(key, chain_buffer, aes_out);
+ }
+
+ for (j = 0; j < 8; j++)
+ mic[j] = aes_out[j];
+
+ /* Insert MIC into payload */
+ for (j = 0; j < 8; j++)
+ pframe[payload_index+j] = mic[j];
+
+ payload_index = hdrlen + 8;
+ for (i = 0; i < num_blocks; i++) {
+ construct_ctr_preload(ctr_preload, a4_exists, qc_exists, pframe, pn_vector, i+1);
+ aes128k128d(key, ctr_preload, aes_out);
+ bitwise_xor(aes_out, &pframe[payload_index], chain_buffer);
+ for (j = 0; j < 16; j++)
+ pframe[payload_index++] = chain_buffer[j];
+ }
+
+ if (payload_remainder > 0) { /* If there is a short final block, then pad it,*/
+ /* encrypt it and copy the unpadded part back */
+ construct_ctr_preload(ctr_preload, a4_exists, qc_exists, pframe, pn_vector, num_blocks+1);
+
+ for (j = 0; j < 16; j++)
+ padded_buffer[j] = 0x00;
+ for (j = 0; j < payload_remainder; j++)
+ padded_buffer[j] = pframe[payload_index+j];
+ aes128k128d(key, ctr_preload, aes_out);
+ bitwise_xor(aes_out, padded_buffer, chain_buffer);
+ for (j = 0; j < payload_remainder; j++)
+ pframe[payload_index++] = chain_buffer[j];
+ }
+ /* Encrypt the MIC */
+ construct_ctr_preload(ctr_preload, a4_exists, qc_exists, pframe, pn_vector, 0);
+
+ for (j = 0; j < 16; j++)
+ padded_buffer[j] = 0x00;
+ for (j = 0; j < 8; j++)
+ padded_buffer[j] = pframe[j+hdrlen+8+plen];
+
+ aes128k128d(key, ctr_preload, aes_out);
+ bitwise_xor(aes_out, padded_buffer, chain_buffer);
+ for (j = 0; j < 8; j++)
+ pframe[payload_index++] = chain_buffer[j];
+ return _SUCCESS;
+}
+
+u32 rtw_aes_encrypt(struct adapter *padapter, u8 *pxmitframe)
+{ /* exclude ICV */
+
+ /*static*/
+/* unsigned char message[MAX_MSG_SIZE]; */
+
+ /* Intermediate Buffers */
+ int curfragnum, length;
+ u8 *pframe, *prwskey; /* *payload,*iv */
+ u8 hw_hdr_offset = 0;
+ struct sta_info *stainfo;
+ struct pkt_attrib *pattrib = &((struct xmit_frame *)pxmitframe)->attrib;
+ struct security_priv *psecuritypriv = &padapter->securitypriv;
+ struct xmit_priv *pxmitpriv = &padapter->xmitpriv;
+
+/* uint offset = 0; */
+ u32 res = _SUCCESS;
+
+ if (((struct xmit_frame *)pxmitframe)->buf_addr == NULL)
+ return _FAIL;
+
+ hw_hdr_offset = TXDESC_SIZE +
+ (((struct xmit_frame *)pxmitframe)->pkt_offset * PACKET_OFFSET_SZ);
+
+ pframe = ((struct xmit_frame *)pxmitframe)->buf_addr + hw_hdr_offset;
+
+ /* 4 start to encrypt each fragment */
+ if (pattrib->encrypt == _AES_) {
+ if (pattrib->psta)
+ stainfo = pattrib->psta;
+ else
+ stainfo = rtw_get_stainfo(&padapter->stapriv, &pattrib->ra[0]);
+
+ if (stainfo != NULL) {
+ RT_TRACE(_module_rtl871x_security_c_, _drv_err_, ("rtw_aes_encrypt: stainfo!= NULL!!!\n"));
+
+ if (IS_MCAST(pattrib->ra))
+ prwskey = psecuritypriv->dot118021XGrpKey[psecuritypriv->dot118021XGrpKeyid].skey;
+ else
+ prwskey = &stainfo->dot118021x_UncstKey.skey[0];
+ for (curfragnum = 0; curfragnum < pattrib->nr_frags; curfragnum++) {
+ if ((curfragnum+1) == pattrib->nr_frags) { /* 4 the last fragment */
+ length = pattrib->last_txcmdsz-pattrib->hdrlen-pattrib->iv_len-pattrib->icv_len;
+
+ aes_cipher(prwskey, pattrib->hdrlen, pframe, length);
+ } else{
+ length = pxmitpriv->frag_len-pattrib->hdrlen-pattrib->iv_len-pattrib->icv_len;
+
+ aes_cipher(prwskey, pattrib->hdrlen, pframe, length);
+ pframe += pxmitpriv->frag_len;
+ pframe = (u8 *)round_up((size_t)(pframe), 8);
+ }
+ }
+ } else{
+ RT_TRACE(_module_rtl871x_security_c_, _drv_err_, ("rtw_aes_encrypt: stainfo==NULL!!!\n"));
+ res = _FAIL;
+ }
+ }
+
+
+ return res;
+}
+
+static int aes_decipher(u8 *key, uint hdrlen,
+ u8 *pframe, uint plen)
+{
+ static u8 message[MAX_MSG_SIZE];
+ uint qc_exists, a4_exists, i, j, payload_remainder,
+ num_blocks, payload_index;
+ int res = _SUCCESS;
+ u8 pn_vector[6];
+ u8 mic_iv[16];
+ u8 mic_header1[16];
+ u8 mic_header2[16];
+ u8 ctr_preload[16];
+
+ /* Intermediate Buffers */
+ u8 chain_buffer[16];
+ u8 aes_out[16];
+ u8 padded_buffer[16];
+ u8 mic[8];
+
+/* uint offset = 0; */
+ uint frtype = GetFrameType(pframe);
+ uint frsubtype = GetFrameSubType(pframe);
+ frsubtype >>= 4;
+
+ memset((void *)mic_iv, 0, 16);
+ memset((void *)mic_header1, 0, 16);
+ memset((void *)mic_header2, 0, 16);
+ memset((void *)ctr_preload, 0, 16);
+ memset((void *)chain_buffer, 0, 16);
+ memset((void *)aes_out, 0, 16);
+ memset((void *)padded_buffer, 0, 16);
+
+ /* start to decrypt the payload */
+
+ num_blocks = (plen-8) / 16; /* plen including llc, payload_length and mic) */
+
+ payload_remainder = (plen-8) % 16;
+
+ pn_vector[0] = pframe[hdrlen];
+ pn_vector[1] = pframe[hdrlen+1];
+ pn_vector[2] = pframe[hdrlen+4];
+ pn_vector[3] = pframe[hdrlen+5];
+ pn_vector[4] = pframe[hdrlen+6];
+ pn_vector[5] = pframe[hdrlen+7];
+
+ if ((hdrlen == WLAN_HDR_A3_LEN) || (hdrlen == WLAN_HDR_A3_QOS_LEN))
+ a4_exists = 0;
+ else
+ a4_exists = 1;
+
+ if ((frtype == WIFI_DATA_CFACK) || (frtype == WIFI_DATA_CFPOLL) ||
+ (frtype == WIFI_DATA_CFACKPOLL)) {
+ qc_exists = 1;
+ if (hdrlen != WLAN_HDR_A3_QOS_LEN)
+ hdrlen += 2;
+ } else if ((frsubtype == 0x08) || (frsubtype == 0x09) ||
+ (frsubtype == 0x0a) || (frsubtype == 0x0b)) {
+ if (hdrlen != WLAN_HDR_A3_QOS_LEN)
+ hdrlen += 2;
+ qc_exists = 1;
+ } else {
+ qc_exists = 0;
+ }
+
+ /* now, decrypt pframe with hdrlen offset and plen long */
+
+ payload_index = hdrlen + 8; /* 8 is for extiv */
+
+ for (i = 0; i < num_blocks; i++) {
+ construct_ctr_preload(ctr_preload, a4_exists, qc_exists, pframe, pn_vector, i+1);
+
+ aes128k128d(key, ctr_preload, aes_out);
+ bitwise_xor(aes_out, &pframe[payload_index], chain_buffer);
+
+ for (j = 0; j < 16; j++)
+ pframe[payload_index++] = chain_buffer[j];
+ }
+
+ if (payload_remainder > 0) { /* If there is a short final block, then pad it,*/
+ /* encrypt it and copy the unpadded part back */
+ construct_ctr_preload(ctr_preload, a4_exists, qc_exists, pframe, pn_vector, num_blocks+1);
+
+ for (j = 0; j < 16; j++)
+ padded_buffer[j] = 0x00;
+ for (j = 0; j < payload_remainder; j++)
+ padded_buffer[j] = pframe[payload_index+j];
+ aes128k128d(key, ctr_preload, aes_out);
+ bitwise_xor(aes_out, padded_buffer, chain_buffer);
+ for (j = 0; j < payload_remainder; j++)
+ pframe[payload_index++] = chain_buffer[j];
+ }
+
+ /* start to calculate the mic */
+ if ((hdrlen+plen+8) <= MAX_MSG_SIZE)
+ memcpy(message, pframe, (hdrlen + plen+8)); /* 8 is for ext iv len */
+
+ pn_vector[0] = pframe[hdrlen];
+ pn_vector[1] = pframe[hdrlen+1];
+ pn_vector[2] = pframe[hdrlen+4];
+ pn_vector[3] = pframe[hdrlen+5];
+ pn_vector[4] = pframe[hdrlen+6];
+ pn_vector[5] = pframe[hdrlen+7];
+ construct_mic_iv(mic_iv, qc_exists, a4_exists, message, plen-8, pn_vector);
+
+ construct_mic_header1(mic_header1, hdrlen, message);
+ construct_mic_header2(mic_header2, message, a4_exists, qc_exists);
+
+ payload_remainder = (plen-8) % 16;
+ num_blocks = (plen-8) / 16;
+
+ /* Find start of payload */
+ payload_index = hdrlen + 8;
+
+ /* Calculate MIC */
+ aes128k128d(key, mic_iv, aes_out);
+ bitwise_xor(aes_out, mic_header1, chain_buffer);
+ aes128k128d(key, chain_buffer, aes_out);
+ bitwise_xor(aes_out, mic_header2, chain_buffer);
+ aes128k128d(key, chain_buffer, aes_out);
+
+ for (i = 0; i < num_blocks; i++) {
+ bitwise_xor(aes_out, &message[payload_index], chain_buffer);
+
+ payload_index += 16;
+ aes128k128d(key, chain_buffer, aes_out);
+ }
+
+ /* Add on the final payload block if it needs padding */
+ if (payload_remainder > 0) {
+ for (j = 0; j < 16; j++)
+ padded_buffer[j] = 0x00;
+ for (j = 0; j < payload_remainder; j++)
+ padded_buffer[j] = message[payload_index++];
+ bitwise_xor(aes_out, padded_buffer, chain_buffer);
+ aes128k128d(key, chain_buffer, aes_out);
+ }
+
+ for (j = 0 ; j < 8; j++)
+ mic[j] = aes_out[j];
+
+ /* Insert MIC into payload */
+ for (j = 0; j < 8; j++)
+ message[payload_index+j] = mic[j];
+
+ payload_index = hdrlen + 8;
+ for (i = 0; i < num_blocks; i++) {
+ construct_ctr_preload(ctr_preload, a4_exists, qc_exists, message, pn_vector, i+1);
+ aes128k128d(key, ctr_preload, aes_out);
+ bitwise_xor(aes_out, &message[payload_index], chain_buffer);
+ for (j = 0; j < 16; j++)
+ message[payload_index++] = chain_buffer[j];
+ }
+
+ if (payload_remainder > 0) { /* If there is a short final block, then pad it,*/
+ /* encrypt it and copy the unpadded part back */
+ construct_ctr_preload(ctr_preload, a4_exists, qc_exists, message, pn_vector, num_blocks+1);
+
+ for (j = 0; j < 16; j++)
+ padded_buffer[j] = 0x00;
+ for (j = 0; j < payload_remainder; j++)
+ padded_buffer[j] = message[payload_index+j];
+ aes128k128d(key, ctr_preload, aes_out);
+ bitwise_xor(aes_out, padded_buffer, chain_buffer);
+ for (j = 0; j < payload_remainder; j++)
+ message[payload_index++] = chain_buffer[j];
+ }
+
+ /* Encrypt the MIC */
+ construct_ctr_preload(ctr_preload, a4_exists, qc_exists, message, pn_vector, 0);
+
+ for (j = 0; j < 16; j++)
+ padded_buffer[j] = 0x00;
+ for (j = 0; j < 8; j++)
+ padded_buffer[j] = message[j+hdrlen+8+plen-8];
+
+ aes128k128d(key, ctr_preload, aes_out);
+ bitwise_xor(aes_out, padded_buffer, chain_buffer);
+ for (j = 0; j < 8; j++)
+ message[payload_index++] = chain_buffer[j];
+
+ /* compare the mic */
+ for (i = 0; i < 8; i++) {
+ if (pframe[hdrlen+8+plen-8+i] != message[hdrlen+8+plen-8+i]) {
+ RT_TRACE(_module_rtl871x_security_c_, _drv_err_,
+ ("aes_decipher:mic check error mic[%d]: pframe(%x)!=message(%x)\n",
+ i, pframe[hdrlen+8+plen-8+i], message[hdrlen+8+plen-8+i]));
+ DBG_88E("aes_decipher:mic check error mic[%d]: pframe(%x)!=message(%x)\n",
+ i, pframe[hdrlen+8+plen-8+i], message[hdrlen+8+plen-8+i]);
+ res = _FAIL;
+ }
+ }
+ return res;
+}
+
+u32 rtw_aes_decrypt(struct adapter *padapter, u8 *precvframe)
+{ /* exclude ICV */
+ /* Intermediate Buffers */
+ int length;
+ u8 *pframe, *prwskey; /* *payload,*iv */
+ struct sta_info *stainfo;
+ struct rx_pkt_attrib *prxattrib = &((struct recv_frame *)precvframe)->attrib;
+ struct security_priv *psecuritypriv = &padapter->securitypriv;
+ u32 res = _SUCCESS;
+ pframe = (unsigned char *)((struct recv_frame *)precvframe)->rx_data;
+ /* 4 start to encrypt each fragment */
+ if (prxattrib->encrypt == _AES_) {
+ stainfo = rtw_get_stainfo(&padapter->stapriv, &prxattrib->ta[0]);
+ if (stainfo != NULL) {
+ RT_TRACE(_module_rtl871x_security_c_, _drv_err_, ("rtw_aes_decrypt: stainfo!= NULL!!!\n"));
+
+ if (IS_MCAST(prxattrib->ra)) {
+ /* in concurrent we should use sw descrypt in group key, so we remove this message */
+ if (!psecuritypriv->binstallGrpkey) {
+ res = _FAIL;
+ DBG_88E("%s:rx bc/mc packets, but didn't install group key!!!!!!!!!!\n", __func__);
+ goto exit;
+ }
+ prwskey = psecuritypriv->dot118021XGrpKey[prxattrib->key_index].skey;
+ if (psecuritypriv->dot118021XGrpKeyid != prxattrib->key_index) {
+ DBG_88E("not match packet_index=%d, install_index=%d\n",
+ prxattrib->key_index, psecuritypriv->dot118021XGrpKeyid);
+ res = _FAIL;
+ goto exit;
+ }
+ } else {
+ prwskey = &stainfo->dot118021x_UncstKey.skey[0];
+ }
+ length = ((struct recv_frame *)precvframe)->len-prxattrib->hdrlen-prxattrib->iv_len;
+ res = aes_decipher(prwskey, prxattrib->hdrlen, pframe, length);
+ } else {
+ RT_TRACE(_module_rtl871x_security_c_, _drv_err_, ("rtw_aes_encrypt: stainfo==NULL!!!\n"));
+ res = _FAIL;
+ }
+ }
+exit:
+ return res;
+}
+
+/* AES tables*/
+const u32 Te0[256] = {
+ 0xc66363a5U, 0xf87c7c84U, 0xee777799U, 0xf67b7b8dU,
+ 0xfff2f20dU, 0xd66b6bbdU, 0xde6f6fb1U, 0x91c5c554U,
+ 0x60303050U, 0x02010103U, 0xce6767a9U, 0x562b2b7dU,
+ 0xe7fefe19U, 0xb5d7d762U, 0x4dababe6U, 0xec76769aU,
+ 0x8fcaca45U, 0x1f82829dU, 0x89c9c940U, 0xfa7d7d87U,
+ 0xeffafa15U, 0xb25959ebU, 0x8e4747c9U, 0xfbf0f00bU,
+ 0x41adadecU, 0xb3d4d467U, 0x5fa2a2fdU, 0x45afafeaU,
+ 0x239c9cbfU, 0x53a4a4f7U, 0xe4727296U, 0x9bc0c05bU,
+ 0x75b7b7c2U, 0xe1fdfd1cU, 0x3d9393aeU, 0x4c26266aU,
+ 0x6c36365aU, 0x7e3f3f41U, 0xf5f7f702U, 0x83cccc4fU,
+ 0x6834345cU, 0x51a5a5f4U, 0xd1e5e534U, 0xf9f1f108U,
+ 0xe2717193U, 0xabd8d873U, 0x62313153U, 0x2a15153fU,
+ 0x0804040cU, 0x95c7c752U, 0x46232365U, 0x9dc3c35eU,
+ 0x30181828U, 0x379696a1U, 0x0a05050fU, 0x2f9a9ab5U,
+ 0x0e070709U, 0x24121236U, 0x1b80809bU, 0xdfe2e23dU,
+ 0xcdebeb26U, 0x4e272769U, 0x7fb2b2cdU, 0xea75759fU,
+ 0x1209091bU, 0x1d83839eU, 0x582c2c74U, 0x341a1a2eU,
+ 0x361b1b2dU, 0xdc6e6eb2U, 0xb45a5aeeU, 0x5ba0a0fbU,
+ 0xa45252f6U, 0x763b3b4dU, 0xb7d6d661U, 0x7db3b3ceU,
+ 0x5229297bU, 0xdde3e33eU, 0x5e2f2f71U, 0x13848497U,
+ 0xa65353f5U, 0xb9d1d168U, 0x00000000U, 0xc1eded2cU,
+ 0x40202060U, 0xe3fcfc1fU, 0x79b1b1c8U, 0xb65b5bedU,
+ 0xd46a6abeU, 0x8dcbcb46U, 0x67bebed9U, 0x7239394bU,
+ 0x944a4adeU, 0x984c4cd4U, 0xb05858e8U, 0x85cfcf4aU,
+ 0xbbd0d06bU, 0xc5efef2aU, 0x4faaaae5U, 0xedfbfb16U,
+ 0x864343c5U, 0x9a4d4dd7U, 0x66333355U, 0x11858594U,
+ 0x8a4545cfU, 0xe9f9f910U, 0x04020206U, 0xfe7f7f81U,
+ 0xa05050f0U, 0x783c3c44U, 0x259f9fbaU, 0x4ba8a8e3U,
+ 0xa25151f3U, 0x5da3a3feU, 0x804040c0U, 0x058f8f8aU,
+ 0x3f9292adU, 0x219d9dbcU, 0x70383848U, 0xf1f5f504U,
+ 0x63bcbcdfU, 0x77b6b6c1U, 0xafdada75U, 0x42212163U,
+ 0x20101030U, 0xe5ffff1aU, 0xfdf3f30eU, 0xbfd2d26dU,
+ 0x81cdcd4cU, 0x180c0c14U, 0x26131335U, 0xc3ecec2fU,
+ 0xbe5f5fe1U, 0x359797a2U, 0x884444ccU, 0x2e171739U,
+ 0x93c4c457U, 0x55a7a7f2U, 0xfc7e7e82U, 0x7a3d3d47U,
+ 0xc86464acU, 0xba5d5de7U, 0x3219192bU, 0xe6737395U,
+ 0xc06060a0U, 0x19818198U, 0x9e4f4fd1U, 0xa3dcdc7fU,
+ 0x44222266U, 0x542a2a7eU, 0x3b9090abU, 0x0b888883U,
+ 0x8c4646caU, 0xc7eeee29U, 0x6bb8b8d3U, 0x2814143cU,
+ 0xa7dede79U, 0xbc5e5ee2U, 0x160b0b1dU, 0xaddbdb76U,
+ 0xdbe0e03bU, 0x64323256U, 0x743a3a4eU, 0x140a0a1eU,
+ 0x924949dbU, 0x0c06060aU, 0x4824246cU, 0xb85c5ce4U,
+ 0x9fc2c25dU, 0xbdd3d36eU, 0x43acacefU, 0xc46262a6U,
+ 0x399191a8U, 0x319595a4U, 0xd3e4e437U, 0xf279798bU,
+ 0xd5e7e732U, 0x8bc8c843U, 0x6e373759U, 0xda6d6db7U,
+ 0x018d8d8cU, 0xb1d5d564U, 0x9c4e4ed2U, 0x49a9a9e0U,
+ 0xd86c6cb4U, 0xac5656faU, 0xf3f4f407U, 0xcfeaea25U,
+ 0xca6565afU, 0xf47a7a8eU, 0x47aeaee9U, 0x10080818U,
+ 0x6fbabad5U, 0xf0787888U, 0x4a25256fU, 0x5c2e2e72U,
+ 0x381c1c24U, 0x57a6a6f1U, 0x73b4b4c7U, 0x97c6c651U,
+ 0xcbe8e823U, 0xa1dddd7cU, 0xe874749cU, 0x3e1f1f21U,
+ 0x964b4bddU, 0x61bdbddcU, 0x0d8b8b86U, 0x0f8a8a85U,
+ 0xe0707090U, 0x7c3e3e42U, 0x71b5b5c4U, 0xcc6666aaU,
+ 0x904848d8U, 0x06030305U, 0xf7f6f601U, 0x1c0e0e12U,
+ 0xc26161a3U, 0x6a35355fU, 0xae5757f9U, 0x69b9b9d0U,
+ 0x17868691U, 0x99c1c158U, 0x3a1d1d27U, 0x279e9eb9U,
+ 0xd9e1e138U, 0xebf8f813U, 0x2b9898b3U, 0x22111133U,
+ 0xd26969bbU, 0xa9d9d970U, 0x078e8e89U, 0x339494a7U,
+ 0x2d9b9bb6U, 0x3c1e1e22U, 0x15878792U, 0xc9e9e920U,
+ 0x87cece49U, 0xaa5555ffU, 0x50282878U, 0xa5dfdf7aU,
+ 0x038c8c8fU, 0x59a1a1f8U, 0x09898980U, 0x1a0d0d17U,
+ 0x65bfbfdaU, 0xd7e6e631U, 0x844242c6U, 0xd06868b8U,
+ 0x824141c3U, 0x299999b0U, 0x5a2d2d77U, 0x1e0f0f11U,
+ 0x7bb0b0cbU, 0xa85454fcU, 0x6dbbbbd6U, 0x2c16163aU,
+};
+
+const u32 Td0[256] = {
+ 0x51f4a750U, 0x7e416553U, 0x1a17a4c3U, 0x3a275e96U,
+ 0x3bab6bcbU, 0x1f9d45f1U, 0xacfa58abU, 0x4be30393U,
+ 0x2030fa55U, 0xad766df6U, 0x88cc7691U, 0xf5024c25U,
+ 0x4fe5d7fcU, 0xc52acbd7U, 0x26354480U, 0xb562a38fU,
+ 0xdeb15a49U, 0x25ba1b67U, 0x45ea0e98U, 0x5dfec0e1U,
+ 0xc32f7502U, 0x814cf012U, 0x8d4697a3U, 0x6bd3f9c6U,
+ 0x038f5fe7U, 0x15929c95U, 0xbf6d7aebU, 0x955259daU,
+ 0xd4be832dU, 0x587421d3U, 0x49e06929U, 0x8ec9c844U,
+ 0x75c2896aU, 0xf48e7978U, 0x99583e6bU, 0x27b971ddU,
+ 0xbee14fb6U, 0xf088ad17U, 0xc920ac66U, 0x7dce3ab4U,
+ 0x63df4a18U, 0xe51a3182U, 0x97513360U, 0x62537f45U,
+ 0xb16477e0U, 0xbb6bae84U, 0xfe81a01cU, 0xf9082b94U,
+ 0x70486858U, 0x8f45fd19U, 0x94de6c87U, 0x527bf8b7U,
+ 0xab73d323U, 0x724b02e2U, 0xe31f8f57U, 0x6655ab2aU,
+ 0xb2eb2807U, 0x2fb5c203U, 0x86c57b9aU, 0xd33708a5U,
+ 0x302887f2U, 0x23bfa5b2U, 0x02036abaU, 0xed16825cU,
+ 0x8acf1c2bU, 0xa779b492U, 0xf307f2f0U, 0x4e69e2a1U,
+ 0x65daf4cdU, 0x0605bed5U, 0xd134621fU, 0xc4a6fe8aU,
+ 0x342e539dU, 0xa2f355a0U, 0x058ae132U, 0xa4f6eb75U,
+ 0x0b83ec39U, 0x4060efaaU, 0x5e719f06U, 0xbd6e1051U,
+ 0x3e218af9U, 0x96dd063dU, 0xdd3e05aeU, 0x4de6bd46U,
+ 0x91548db5U, 0x71c45d05U, 0x0406d46fU, 0x605015ffU,
+ 0x1998fb24U, 0xd6bde997U, 0x894043ccU, 0x67d99e77U,
+ 0xb0e842bdU, 0x07898b88U, 0xe7195b38U, 0x79c8eedbU,
+ 0xa17c0a47U, 0x7c420fe9U, 0xf8841ec9U, 0x00000000U,
+ 0x09808683U, 0x322bed48U, 0x1e1170acU, 0x6c5a724eU,
+ 0xfd0efffbU, 0x0f853856U, 0x3daed51eU, 0x362d3927U,
+ 0x0a0fd964U, 0x685ca621U, 0x9b5b54d1U, 0x24362e3aU,
+ 0x0c0a67b1U, 0x9357e70fU, 0xb4ee96d2U, 0x1b9b919eU,
+ 0x80c0c54fU, 0x61dc20a2U, 0x5a774b69U, 0x1c121a16U,
+ 0xe293ba0aU, 0xc0a02ae5U, 0x3c22e043U, 0x121b171dU,
+ 0x0e090d0bU, 0xf28bc7adU, 0x2db6a8b9U, 0x141ea9c8U,
+ 0x57f11985U, 0xaf75074cU, 0xee99ddbbU, 0xa37f60fdU,
+ 0xf701269fU, 0x5c72f5bcU, 0x44663bc5U, 0x5bfb7e34U,
+ 0x8b432976U, 0xcb23c6dcU, 0xb6edfc68U, 0xb8e4f163U,
+ 0xd731dccaU, 0x42638510U, 0x13972240U, 0x84c61120U,
+ 0x854a247dU, 0xd2bb3df8U, 0xaef93211U, 0xc729a16dU,
+ 0x1d9e2f4bU, 0xdcb230f3U, 0x0d8652ecU, 0x77c1e3d0U,
+ 0x2bb3166cU, 0xa970b999U, 0x119448faU, 0x47e96422U,
+ 0xa8fc8cc4U, 0xa0f03f1aU, 0x567d2cd8U, 0x223390efU,
+ 0x87494ec7U, 0xd938d1c1U, 0x8ccaa2feU, 0x98d40b36U,
+ 0xa6f581cfU, 0xa57ade28U, 0xdab78e26U, 0x3fadbfa4U,
+ 0x2c3a9de4U, 0x5078920dU, 0x6a5fcc9bU, 0x547e4662U,
+ 0xf68d13c2U, 0x90d8b8e8U, 0x2e39f75eU, 0x82c3aff5U,
+ 0x9f5d80beU, 0x69d0937cU, 0x6fd52da9U, 0xcf2512b3U,
+ 0xc8ac993bU, 0x10187da7U, 0xe89c636eU, 0xdb3bbb7bU,
+ 0xcd267809U, 0x6e5918f4U, 0xec9ab701U, 0x834f9aa8U,
+ 0xe6956e65U, 0xaaffe67eU, 0x21bccf08U, 0xef15e8e6U,
+ 0xbae79bd9U, 0x4a6f36ceU, 0xea9f09d4U, 0x29b07cd6U,
+ 0x31a4b2afU, 0x2a3f2331U, 0xc6a59430U, 0x35a266c0U,
+ 0x744ebc37U, 0xfc82caa6U, 0xe090d0b0U, 0x33a7d815U,
+ 0xf104984aU, 0x41ecdaf7U, 0x7fcd500eU, 0x1791f62fU,
+ 0x764dd68dU, 0x43efb04dU, 0xccaa4d54U, 0xe49604dfU,
+ 0x9ed1b5e3U, 0x4c6a881bU, 0xc12c1fb8U, 0x4665517fU,
+ 0x9d5eea04U, 0x018c355dU, 0xfa877473U, 0xfb0b412eU,
+ 0xb3671d5aU, 0x92dbd252U, 0xe9105633U, 0x6dd64713U,
+ 0x9ad7618cU, 0x37a10c7aU, 0x59f8148eU, 0xeb133c89U,
+ 0xcea927eeU, 0xb761c935U, 0xe11ce5edU, 0x7a47b13cU,
+ 0x9cd2df59U, 0x55f2733fU, 0x1814ce79U, 0x73c737bfU,
+ 0x53f7cdeaU, 0x5ffdaa5bU, 0xdf3d6f14U, 0x7844db86U,
+ 0xcaaff381U, 0xb968c43eU, 0x3824342cU, 0xc2a3405fU,
+ 0x161dc372U, 0xbce2250cU, 0x283c498bU, 0xff0d9541U,
+ 0x39a80171U, 0x080cb3deU, 0xd8b4e49cU, 0x6456c190U,
+ 0x7bcb8461U, 0xd532b670U, 0x486c5c74U, 0xd0b85742U,
+};
+
+const u8 Td4s[256] = {
+ 0x52U, 0x09U, 0x6aU, 0xd5U, 0x30U, 0x36U, 0xa5U, 0x38U,
+ 0xbfU, 0x40U, 0xa3U, 0x9eU, 0x81U, 0xf3U, 0xd7U, 0xfbU,
+ 0x7cU, 0xe3U, 0x39U, 0x82U, 0x9bU, 0x2fU, 0xffU, 0x87U,
+ 0x34U, 0x8eU, 0x43U, 0x44U, 0xc4U, 0xdeU, 0xe9U, 0xcbU,
+ 0x54U, 0x7bU, 0x94U, 0x32U, 0xa6U, 0xc2U, 0x23U, 0x3dU,
+ 0xeeU, 0x4cU, 0x95U, 0x0bU, 0x42U, 0xfaU, 0xc3U, 0x4eU,
+ 0x08U, 0x2eU, 0xa1U, 0x66U, 0x28U, 0xd9U, 0x24U, 0xb2U,
+ 0x76U, 0x5bU, 0xa2U, 0x49U, 0x6dU, 0x8bU, 0xd1U, 0x25U,
+ 0x72U, 0xf8U, 0xf6U, 0x64U, 0x86U, 0x68U, 0x98U, 0x16U,
+ 0xd4U, 0xa4U, 0x5cU, 0xccU, 0x5dU, 0x65U, 0xb6U, 0x92U,
+ 0x6cU, 0x70U, 0x48U, 0x50U, 0xfdU, 0xedU, 0xb9U, 0xdaU,
+ 0x5eU, 0x15U, 0x46U, 0x57U, 0xa7U, 0x8dU, 0x9dU, 0x84U,
+ 0x90U, 0xd8U, 0xabU, 0x00U, 0x8cU, 0xbcU, 0xd3U, 0x0aU,
+ 0xf7U, 0xe4U, 0x58U, 0x05U, 0xb8U, 0xb3U, 0x45U, 0x06U,
+ 0xd0U, 0x2cU, 0x1eU, 0x8fU, 0xcaU, 0x3fU, 0x0fU, 0x02U,
+ 0xc1U, 0xafU, 0xbdU, 0x03U, 0x01U, 0x13U, 0x8aU, 0x6bU,
+ 0x3aU, 0x91U, 0x11U, 0x41U, 0x4fU, 0x67U, 0xdcU, 0xeaU,
+ 0x97U, 0xf2U, 0xcfU, 0xceU, 0xf0U, 0xb4U, 0xe6U, 0x73U,
+ 0x96U, 0xacU, 0x74U, 0x22U, 0xe7U, 0xadU, 0x35U, 0x85U,
+ 0xe2U, 0xf9U, 0x37U, 0xe8U, 0x1cU, 0x75U, 0xdfU, 0x6eU,
+ 0x47U, 0xf1U, 0x1aU, 0x71U, 0x1dU, 0x29U, 0xc5U, 0x89U,
+ 0x6fU, 0xb7U, 0x62U, 0x0eU, 0xaaU, 0x18U, 0xbeU, 0x1bU,
+ 0xfcU, 0x56U, 0x3eU, 0x4bU, 0xc6U, 0xd2U, 0x79U, 0x20U,
+ 0x9aU, 0xdbU, 0xc0U, 0xfeU, 0x78U, 0xcdU, 0x5aU, 0xf4U,
+ 0x1fU, 0xddU, 0xa8U, 0x33U, 0x88U, 0x07U, 0xc7U, 0x31U,
+ 0xb1U, 0x12U, 0x10U, 0x59U, 0x27U, 0x80U, 0xecU, 0x5fU,
+ 0x60U, 0x51U, 0x7fU, 0xa9U, 0x19U, 0xb5U, 0x4aU, 0x0dU,
+ 0x2dU, 0xe5U, 0x7aU, 0x9fU, 0x93U, 0xc9U, 0x9cU, 0xefU,
+ 0xa0U, 0xe0U, 0x3bU, 0x4dU, 0xaeU, 0x2aU, 0xf5U, 0xb0U,
+ 0xc8U, 0xebU, 0xbbU, 0x3cU, 0x83U, 0x53U, 0x99U, 0x61U,
+ 0x17U, 0x2bU, 0x04U, 0x7eU, 0xbaU, 0x77U, 0xd6U, 0x26U,
+ 0xe1U, 0x69U, 0x14U, 0x63U, 0x55U, 0x21U, 0x0cU, 0x7dU,
+};
+const u8 rcons[] = {
+ 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1B, 0x36
+ /* for 128-bit blocks, Rijndael never uses more than 10 rcon values */
+};
+
+/**
+ * Expand the cipher key into the encryption key schedule.
+ *
+ * @return the number of rounds for the given cipher key size.
+ */
+#define ROUND(i, d, s) \
+do { \
+ d##0 = TE0(s##0) ^ TE1(s##1) ^ TE2(s##2) ^ TE3(s##3) ^ rk[4 * i]; \
+ d##1 = TE0(s##1) ^ TE1(s##2) ^ TE2(s##3) ^ TE3(s##0) ^ rk[4 * i + 1]; \
+ d##2 = TE0(s##2) ^ TE1(s##3) ^ TE2(s##0) ^ TE3(s##1) ^ rk[4 * i + 2]; \
+ d##3 = TE0(s##3) ^ TE1(s##0) ^ TE2(s##1) ^ TE3(s##2) ^ rk[4 * i + 3]; \
+} while (0);
diff --git a/drivers/staging/rtl8188eu/core/rtw_sreset.c b/drivers/staging/rtl8188eu/core/rtw_sreset.c
new file mode 100644
index 000000000..e725a4708
--- /dev/null
+++ b/drivers/staging/rtl8188eu/core/rtw_sreset.c
@@ -0,0 +1,64 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2012 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ *
+ ******************************************************************************/
+
+#include <rtw_sreset.h>
+#include <usb_ops_linux.h>
+
+void sreset_init_value(struct adapter *padapter)
+{
+ struct hal_data_8188e *pHalData = GET_HAL_DATA(padapter);
+ struct sreset_priv *psrtpriv = &pHalData->srestpriv;
+
+ psrtpriv->Wifi_Error_Status = WIFI_STATUS_SUCCESS;
+}
+
+u8 sreset_get_wifi_status(struct adapter *padapter)
+{
+ struct hal_data_8188e *pHalData = GET_HAL_DATA(padapter);
+ struct sreset_priv *psrtpriv = &pHalData->srestpriv;
+
+ u8 status = WIFI_STATUS_SUCCESS;
+ u32 val32 = 0;
+
+ val32 = usb_read32(padapter, REG_TXDMA_STATUS);
+ if (val32 == 0xeaeaeaea) {
+ psrtpriv->Wifi_Error_Status = WIFI_IF_NOT_EXIST;
+ } else if (val32 != 0) {
+ DBG_88E("txdmastatu(%x)\n", val32);
+ psrtpriv->Wifi_Error_Status = WIFI_MAC_TXDMA_ERROR;
+ }
+
+ if (WIFI_STATUS_SUCCESS != psrtpriv->Wifi_Error_Status) {
+ DBG_88E("==>%s error_status(0x%x)\n", __func__, psrtpriv->Wifi_Error_Status);
+ status = psrtpriv->Wifi_Error_Status & (~(USB_READ_PORT_FAIL|USB_WRITE_PORT_FAIL));
+ }
+ DBG_88E("==> %s wifi_status(0x%x)\n", __func__, status);
+
+ /* status restore */
+ psrtpriv->Wifi_Error_Status = WIFI_STATUS_SUCCESS;
+
+ return status;
+}
+
+void sreset_set_wifi_error_status(struct adapter *padapter, u32 status)
+{
+ struct hal_data_8188e *pHalData = GET_HAL_DATA(padapter);
+ pHalData->srestpriv.Wifi_Error_Status = status;
+}
diff --git a/drivers/staging/rtl8188eu/core/rtw_sta_mgt.c b/drivers/staging/rtl8188eu/core/rtw_sta_mgt.c
new file mode 100644
index 000000000..dc9d0ddf6
--- /dev/null
+++ b/drivers/staging/rtl8188eu/core/rtw_sta_mgt.c
@@ -0,0 +1,565 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2011 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ *
+ ******************************************************************************/
+#define _RTW_STA_MGT_C_
+
+#include <osdep_service.h>
+#include <drv_types.h>
+#include <recv_osdep.h>
+#include <xmit_osdep.h>
+#include <mlme_osdep.h>
+#include <sta_info.h>
+#include <linux/vmalloc.h>
+
+static void _rtw_init_stainfo(struct sta_info *psta)
+{
+ memset((u8 *)psta, 0, sizeof(struct sta_info));
+
+ spin_lock_init(&psta->lock);
+ INIT_LIST_HEAD(&psta->list);
+ INIT_LIST_HEAD(&psta->hash_list);
+ _rtw_init_queue(&psta->sleep_q);
+ psta->sleepq_len = 0;
+
+ _rtw_init_sta_xmit_priv(&psta->sta_xmitpriv);
+ _rtw_init_sta_recv_priv(&psta->sta_recvpriv);
+
+#ifdef CONFIG_88EU_AP_MODE
+
+ INIT_LIST_HEAD(&psta->asoc_list);
+
+ INIT_LIST_HEAD(&psta->auth_list);
+
+ psta->expire_to = 0;
+
+ psta->flags = 0;
+
+ psta->capability = 0;
+
+ psta->bpairwise_key_installed = false;
+
+#ifdef CONFIG_88EU_AP_MODE
+ psta->nonerp_set = 0;
+ psta->no_short_slot_time_set = 0;
+ psta->no_short_preamble_set = 0;
+ psta->no_ht_gf_set = 0;
+ psta->no_ht_set = 0;
+ psta->ht_20mhz_set = 0;
+#endif
+
+ psta->under_exist_checking = 0;
+
+ psta->keep_alive_trycnt = 0;
+
+#endif /* CONFIG_88EU_AP_MODE */
+
+}
+
+u32 _rtw_init_sta_priv(struct sta_priv *pstapriv)
+{
+ struct sta_info *psta;
+ s32 i;
+
+
+ pstapriv->pallocated_stainfo_buf = vzalloc(sizeof(struct sta_info) * NUM_STA + 4);
+
+ if (!pstapriv->pallocated_stainfo_buf)
+ return _FAIL;
+
+ pstapriv->pstainfo_buf = pstapriv->pallocated_stainfo_buf + 4 -
+ ((size_t)(pstapriv->pallocated_stainfo_buf) & 3);
+
+ _rtw_init_queue(&pstapriv->free_sta_queue);
+
+ spin_lock_init(&pstapriv->sta_hash_lock);
+
+ pstapriv->asoc_sta_count = 0;
+ _rtw_init_queue(&pstapriv->sleep_q);
+ _rtw_init_queue(&pstapriv->wakeup_q);
+
+ psta = (struct sta_info *)(pstapriv->pstainfo_buf);
+
+ for (i = 0; i < NUM_STA; i++) {
+ _rtw_init_stainfo(psta);
+
+ INIT_LIST_HEAD(&(pstapriv->sta_hash[i]));
+
+ list_add_tail(&psta->list, get_list_head(&pstapriv->free_sta_queue));
+
+ psta++;
+ }
+
+#ifdef CONFIG_88EU_AP_MODE
+
+ pstapriv->sta_dz_bitmap = 0;
+ pstapriv->tim_bitmap = 0;
+
+ INIT_LIST_HEAD(&pstapriv->asoc_list);
+ INIT_LIST_HEAD(&pstapriv->auth_list);
+ spin_lock_init(&pstapriv->asoc_list_lock);
+ spin_lock_init(&pstapriv->auth_list_lock);
+ pstapriv->asoc_list_cnt = 0;
+ pstapriv->auth_list_cnt = 0;
+
+ pstapriv->auth_to = 3; /* 3*2 = 6 sec */
+ pstapriv->assoc_to = 3;
+ pstapriv->expire_to = 3; /* 3*2 = 6 sec */
+ pstapriv->max_num_sta = NUM_STA;
+#endif
+
+
+ return _SUCCESS;
+}
+
+inline int rtw_stainfo_offset(struct sta_priv *stapriv, struct sta_info *sta)
+{
+ int offset = (((u8 *)sta) - stapriv->pstainfo_buf)/sizeof(struct sta_info);
+
+ if (!stainfo_offset_valid(offset))
+ DBG_88E("%s invalid offset(%d), out of range!!!", __func__, offset);
+
+ return offset;
+}
+
+inline struct sta_info *rtw_get_stainfo_by_offset(struct sta_priv *stapriv, int offset)
+{
+ if (!stainfo_offset_valid(offset))
+ DBG_88E("%s invalid offset(%d), out of range!!!", __func__, offset);
+
+ return (struct sta_info *)(stapriv->pstainfo_buf + offset * sizeof(struct sta_info));
+}
+
+/* this function is used to free the memory of lock || sema for all stainfos */
+static void rtw_mfree_all_stainfo(struct sta_priv *pstapriv)
+{
+ struct list_head *plist, *phead;
+ struct sta_info *psta = NULL;
+
+
+ spin_lock_bh(&pstapriv->sta_hash_lock);
+
+ phead = get_list_head(&pstapriv->free_sta_queue);
+ plist = phead->next;
+
+ while (phead != plist) {
+ psta = container_of(plist, struct sta_info , list);
+ plist = plist->next;
+ }
+
+ spin_unlock_bh(&pstapriv->sta_hash_lock);
+
+}
+
+static void rtw_mfree_sta_priv_lock(struct sta_priv *pstapriv)
+{
+ rtw_mfree_all_stainfo(pstapriv); /* be done before free sta_hash_lock */
+}
+
+u32 _rtw_free_sta_priv(struct sta_priv *pstapriv)
+{
+ struct list_head *phead, *plist;
+ struct sta_info *psta = NULL;
+ struct recv_reorder_ctrl *preorder_ctrl;
+ int index;
+
+ if (pstapriv) {
+ /* delete all reordering_ctrl_timer */
+ spin_lock_bh(&pstapriv->sta_hash_lock);
+ for (index = 0; index < NUM_STA; index++) {
+ phead = &(pstapriv->sta_hash[index]);
+ plist = phead->next;
+
+ while (phead != plist) {
+ int i;
+ psta = container_of(plist, struct sta_info , hash_list);
+ plist = plist->next;
+
+ for (i = 0; i < 16; i++) {
+ preorder_ctrl = &psta->recvreorder_ctrl[i];
+ del_timer_sync(&preorder_ctrl->reordering_ctrl_timer);
+ }
+ }
+ }
+ spin_unlock_bh(&pstapriv->sta_hash_lock);
+ /*===============================*/
+
+ rtw_mfree_sta_priv_lock(pstapriv);
+
+ if (pstapriv->pallocated_stainfo_buf)
+ vfree(pstapriv->pallocated_stainfo_buf);
+ }
+
+ return _SUCCESS;
+}
+
+struct sta_info *rtw_alloc_stainfo(struct sta_priv *pstapriv, u8 *hwaddr)
+{
+ s32 index;
+ struct list_head *phash_list;
+ struct sta_info *psta;
+ struct __queue *pfree_sta_queue;
+ struct recv_reorder_ctrl *preorder_ctrl;
+ int i = 0;
+ u16 wRxSeqInitialValue = 0xffff;
+
+
+ pfree_sta_queue = &pstapriv->free_sta_queue;
+
+ spin_lock_bh(&(pfree_sta_queue->lock));
+
+ if (list_empty(&pfree_sta_queue->queue)) {
+ spin_unlock_bh(&pfree_sta_queue->lock);
+ psta = NULL;
+ } else {
+ psta = container_of((&pfree_sta_queue->queue)->next, struct sta_info, list);
+ list_del_init(&(psta->list));
+ spin_unlock_bh(&pfree_sta_queue->lock);
+ _rtw_init_stainfo(psta);
+ memcpy(psta->hwaddr, hwaddr, ETH_ALEN);
+ index = wifi_mac_hash(hwaddr);
+ RT_TRACE(_module_rtl871x_sta_mgt_c_, _drv_info_, ("rtw_alloc_stainfo: index=%x", index));
+ if (index >= NUM_STA) {
+ RT_TRACE(_module_rtl871x_sta_mgt_c_, _drv_err_, ("ERROR => rtw_alloc_stainfo: index >= NUM_STA"));
+ psta = NULL;
+ goto exit;
+ }
+ phash_list = &(pstapriv->sta_hash[index]);
+
+ spin_lock_bh(&(pstapriv->sta_hash_lock));
+
+ list_add_tail(&psta->hash_list, phash_list);
+
+ pstapriv->asoc_sta_count++;
+
+ spin_unlock_bh(&pstapriv->sta_hash_lock);
+
+/* Commented by Albert 2009/08/13 */
+/* For the SMC router, the sequence number of first packet of WPS handshake will be 0. */
+/* In this case, this packet will be dropped by recv_decache function if we use the 0x00 as the default value for tid_rxseq variable. */
+/* So, we initialize the tid_rxseq variable as the 0xffff. */
+
+ for (i = 0; i < 16; i++)
+ memcpy(&psta->sta_recvpriv.rxcache.tid_rxseq[i], &wRxSeqInitialValue, 2);
+
+ RT_TRACE(_module_rtl871x_sta_mgt_c_, _drv_info_,
+ ("alloc number_%d stainfo with hwaddr = %pM\n",
+ pstapriv->asoc_sta_count , hwaddr));
+
+ init_addba_retry_timer(pstapriv->padapter, psta);
+
+ /* for A-MPDU Rx reordering buffer control */
+ for (i = 0; i < 16; i++) {
+ preorder_ctrl = &psta->recvreorder_ctrl[i];
+
+ preorder_ctrl->padapter = pstapriv->padapter;
+
+ preorder_ctrl->enable = false;
+
+ preorder_ctrl->indicate_seq = 0xffff;
+ preorder_ctrl->wend_b = 0xffff;
+ preorder_ctrl->wsize_b = 64;/* 64; */
+
+ _rtw_init_queue(&preorder_ctrl->pending_recvframe_queue);
+
+ rtw_init_recv_timer(preorder_ctrl);
+ }
+
+ /* init for DM */
+ psta->rssi_stat.UndecoratedSmoothedPWDB = (-1);
+ psta->rssi_stat.UndecoratedSmoothedCCK = (-1);
+
+ /* init for the sequence number of received management frame */
+ psta->RxMgmtFrameSeqNum = 0xffff;
+ }
+
+exit:
+ return psta;
+}
+
+/* using pstapriv->sta_hash_lock to protect */
+u32 rtw_free_stainfo(struct adapter *padapter , struct sta_info *psta)
+{
+ int i;
+ struct __queue *pfree_sta_queue;
+ struct recv_reorder_ctrl *preorder_ctrl;
+ struct sta_xmit_priv *pstaxmitpriv;
+ struct xmit_priv *pxmitpriv = &padapter->xmitpriv;
+ struct sta_priv *pstapriv = &padapter->stapriv;
+
+
+ if (psta == NULL)
+ goto exit;
+
+ pfree_sta_queue = &pstapriv->free_sta_queue;
+
+ pstaxmitpriv = &psta->sta_xmitpriv;
+
+ spin_lock_bh(&pxmitpriv->lock);
+
+ rtw_free_xmitframe_queue(pxmitpriv, &psta->sleep_q);
+ psta->sleepq_len = 0;
+
+ rtw_free_xmitframe_queue(pxmitpriv, &pstaxmitpriv->vo_q.sta_pending);
+
+ list_del_init(&(pstaxmitpriv->vo_q.tx_pending));
+
+ rtw_free_xmitframe_queue(pxmitpriv, &pstaxmitpriv->vi_q.sta_pending);
+
+ list_del_init(&(pstaxmitpriv->vi_q.tx_pending));
+
+ rtw_free_xmitframe_queue(pxmitpriv, &pstaxmitpriv->bk_q.sta_pending);
+
+ list_del_init(&(pstaxmitpriv->bk_q.tx_pending));
+
+ rtw_free_xmitframe_queue(pxmitpriv, &pstaxmitpriv->be_q.sta_pending);
+
+ list_del_init(&(pstaxmitpriv->be_q.tx_pending));
+
+ spin_unlock_bh(&pxmitpriv->lock);
+
+ list_del_init(&psta->hash_list);
+ RT_TRACE(_module_rtl871x_sta_mgt_c_, _drv_err_, ("\n free number_%d stainfo with hwaddr=0x%.2x 0x%.2x 0x%.2x 0x%.2x 0x%.2x 0x%.2x\n", pstapriv->asoc_sta_count , psta->hwaddr[0], psta->hwaddr[1], psta->hwaddr[2], psta->hwaddr[3], psta->hwaddr[4], psta->hwaddr[5]));
+ pstapriv->asoc_sta_count--;
+
+ /* re-init sta_info; 20061114 */
+ _rtw_init_sta_xmit_priv(&psta->sta_xmitpriv);
+ _rtw_init_sta_recv_priv(&psta->sta_recvpriv);
+
+ del_timer_sync(&psta->addba_retry_timer);
+
+ /* for A-MPDU Rx reordering buffer control, cancel reordering_ctrl_timer */
+ for (i = 0; i < 16; i++) {
+ struct list_head *phead, *plist;
+ struct recv_frame *prhdr;
+ struct recv_frame *prframe;
+ struct __queue *ppending_recvframe_queue;
+ struct __queue *pfree_recv_queue = &padapter->recvpriv.free_recv_queue;
+
+ preorder_ctrl = &psta->recvreorder_ctrl[i];
+
+ del_timer_sync(&preorder_ctrl->reordering_ctrl_timer);
+
+ ppending_recvframe_queue = &preorder_ctrl->pending_recvframe_queue;
+
+ spin_lock_bh(&ppending_recvframe_queue->lock);
+
+ phead = get_list_head(ppending_recvframe_queue);
+ plist = phead->next;
+
+ while (!list_empty(phead)) {
+ prhdr = container_of(plist, struct recv_frame, list);
+ prframe = (struct recv_frame *)prhdr;
+
+ plist = plist->next;
+
+ list_del_init(&(prframe->list));
+
+ rtw_free_recvframe(prframe, pfree_recv_queue);
+ }
+
+ spin_unlock_bh(&ppending_recvframe_queue->lock);
+ }
+
+ if (!(psta->state & WIFI_AP_STATE))
+ rtw_hal_set_odm_var(padapter, HAL_ODM_STA_INFO, psta, false);
+
+#ifdef CONFIG_88EU_AP_MODE
+
+ spin_lock_bh(&pstapriv->auth_list_lock);
+ if (!list_empty(&psta->auth_list)) {
+ list_del_init(&psta->auth_list);
+ pstapriv->auth_list_cnt--;
+ }
+ spin_unlock_bh(&pstapriv->auth_list_lock);
+
+ psta->expire_to = 0;
+
+ psta->sleepq_ac_len = 0;
+ psta->qos_info = 0;
+
+ psta->max_sp_len = 0;
+ psta->uapsd_bk = 0;
+ psta->uapsd_be = 0;
+ psta->uapsd_vi = 0;
+ psta->uapsd_vo = 0;
+ psta->has_legacy_ac = 0;
+
+ pstapriv->sta_dz_bitmap &= ~BIT(psta->aid);
+ pstapriv->tim_bitmap &= ~BIT(psta->aid);
+
+ if ((psta->aid > 0) && (pstapriv->sta_aid[psta->aid - 1] == psta)) {
+ pstapriv->sta_aid[psta->aid - 1] = NULL;
+ psta->aid = 0;
+ }
+
+ psta->under_exist_checking = 0;
+
+#endif /* CONFIG_88EU_AP_MODE */
+
+ spin_lock_bh(&(pfree_sta_queue->lock));
+ list_add_tail(&psta->list, get_list_head(pfree_sta_queue));
+ spin_unlock_bh(&pfree_sta_queue->lock);
+
+exit:
+
+
+ return _SUCCESS;
+}
+
+/* free all stainfo which in sta_hash[all] */
+void rtw_free_all_stainfo(struct adapter *padapter)
+{
+ struct list_head *plist, *phead;
+ s32 index;
+ struct sta_info *psta = NULL;
+ struct sta_priv *pstapriv = &padapter->stapriv;
+ struct sta_info *pbcmc_stainfo = rtw_get_bcmc_stainfo(padapter);
+
+
+ if (pstapriv->asoc_sta_count == 1)
+ return;
+
+ spin_lock_bh(&pstapriv->sta_hash_lock);
+
+ for (index = 0; index < NUM_STA; index++) {
+ phead = &(pstapriv->sta_hash[index]);
+ plist = phead->next;
+
+ while (phead != plist) {
+ psta = container_of(plist, struct sta_info , hash_list);
+
+ plist = plist->next;
+
+ if (pbcmc_stainfo != psta)
+ rtw_free_stainfo(padapter , psta);
+ }
+ }
+ spin_unlock_bh(&pstapriv->sta_hash_lock);
+}
+
+/* any station allocated can be searched by hash list */
+struct sta_info *rtw_get_stainfo(struct sta_priv *pstapriv, u8 *hwaddr)
+{
+ struct list_head *plist, *phead;
+ struct sta_info *psta = NULL;
+ u32 index;
+ u8 *addr;
+ u8 bc_addr[ETH_ALEN] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
+
+
+ if (hwaddr == NULL)
+ return NULL;
+
+ if (IS_MCAST(hwaddr))
+ addr = bc_addr;
+ else
+ addr = hwaddr;
+
+ index = wifi_mac_hash(addr);
+
+ spin_lock_bh(&pstapriv->sta_hash_lock);
+
+ phead = &(pstapriv->sta_hash[index]);
+ plist = phead->next;
+
+ while (phead != plist) {
+ psta = container_of(plist, struct sta_info, hash_list);
+
+ if ((!memcmp(psta->hwaddr, addr, ETH_ALEN)) == true) {
+ /* if found the matched address */
+ break;
+ }
+ psta = NULL;
+ plist = plist->next;
+ }
+
+ spin_unlock_bh(&pstapriv->sta_hash_lock);
+ return psta;
+}
+
+u32 rtw_init_bcmc_stainfo(struct adapter *padapter)
+{
+ struct sta_info *psta;
+ u32 res = _SUCCESS;
+ unsigned char bcast_addr[ETH_ALEN] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
+ struct sta_priv *pstapriv = &padapter->stapriv;
+
+
+ psta = rtw_alloc_stainfo(pstapriv, bcast_addr);
+
+ if (psta == NULL) {
+ res = _FAIL;
+ RT_TRACE(_module_rtl871x_sta_mgt_c_, _drv_err_, ("rtw_alloc_stainfo fail"));
+ goto exit;
+ }
+
+ /* default broadcast & multicast use macid 1 */
+ psta->mac_id = 1;
+
+exit:
+ return res;
+}
+
+struct sta_info *rtw_get_bcmc_stainfo(struct adapter *padapter)
+{
+ struct sta_info *psta;
+ struct sta_priv *pstapriv = &padapter->stapriv;
+ u8 bc_addr[ETH_ALEN] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
+ psta = rtw_get_stainfo(pstapriv, bc_addr);
+ return psta;
+}
+
+u8 rtw_access_ctrl(struct adapter *padapter, u8 *mac_addr)
+{
+ u8 res = true;
+#ifdef CONFIG_88EU_AP_MODE
+ struct list_head *plist, *phead;
+ struct rtw_wlan_acl_node *paclnode;
+ u8 match = false;
+ struct sta_priv *pstapriv = &padapter->stapriv;
+ struct wlan_acl_pool *pacl_list = &pstapriv->acl_list;
+ struct __queue *pacl_node_q = &pacl_list->acl_node_q;
+
+ spin_lock_bh(&(pacl_node_q->lock));
+ phead = get_list_head(pacl_node_q);
+ plist = phead->next;
+ while (phead != plist) {
+ paclnode = container_of(plist, struct rtw_wlan_acl_node, list);
+ plist = plist->next;
+
+ if (!memcmp(paclnode->addr, mac_addr, ETH_ALEN)) {
+ if (paclnode->valid) {
+ match = true;
+ break;
+ }
+ }
+ }
+ spin_unlock_bh(&pacl_node_q->lock);
+
+ if (pacl_list->mode == 1)/* accept unless in deny list */
+ res = (match) ? false : true;
+ else if (pacl_list->mode == 2)/* deny unless in accept list */
+ res = (match) ? true : false;
+ else
+ res = true;
+
+#endif
+
+ return res;
+}
diff --git a/drivers/staging/rtl8188eu/core/rtw_wlan_util.c b/drivers/staging/rtl8188eu/core/rtw_wlan_util.c
new file mode 100644
index 000000000..2b371757c
--- /dev/null
+++ b/drivers/staging/rtl8188eu/core/rtw_wlan_util.c
@@ -0,0 +1,1611 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2012 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ *
+ ******************************************************************************/
+#define _RTW_WLAN_UTIL_C_
+
+#include <osdep_service.h>
+#include <drv_types.h>
+#include <wifi.h>
+
+static unsigned char ARTHEROS_OUI1[] = {0x00, 0x03, 0x7f};
+static unsigned char ARTHEROS_OUI2[] = {0x00, 0x13, 0x74};
+
+static unsigned char BROADCOM_OUI1[] = {0x00, 0x10, 0x18};
+static unsigned char BROADCOM_OUI2[] = {0x00, 0x0a, 0xf7};
+
+static unsigned char CISCO_OUI[] = {0x00, 0x40, 0x96};
+static unsigned char MARVELL_OUI[] = {0x00, 0x50, 0x43};
+static unsigned char RALINK_OUI[] = {0x00, 0x0c, 0x43};
+static unsigned char REALTEK_OUI[] = {0x00, 0xe0, 0x4c};
+static unsigned char AIRGOCAP_OUI[] = {0x00, 0x0a, 0xf5};
+static unsigned char EPIGRAM_OUI[] = {0x00, 0x90, 0x4c};
+
+unsigned char REALTEK_96B_IE[] = {0x00, 0xe0, 0x4c, 0x02, 0x01, 0x20};
+
+#define R2T_PHY_DELAY (0)
+
+/* define WAIT_FOR_BCN_TO_M (3000) */
+#define WAIT_FOR_BCN_TO_MIN (6000)
+#define WAIT_FOR_BCN_TO_MAX (20000)
+
+static u8 rtw_basic_rate_cck[4] = {
+ IEEE80211_CCK_RATE_1MB|IEEE80211_BASIC_RATE_MASK, IEEE80211_CCK_RATE_2MB|IEEE80211_BASIC_RATE_MASK,
+ IEEE80211_CCK_RATE_5MB|IEEE80211_BASIC_RATE_MASK, IEEE80211_CCK_RATE_11MB|IEEE80211_BASIC_RATE_MASK
+};
+
+static u8 rtw_basic_rate_ofdm[3] = {
+ IEEE80211_OFDM_RATE_6MB|IEEE80211_BASIC_RATE_MASK, IEEE80211_OFDM_RATE_12MB|IEEE80211_BASIC_RATE_MASK,
+ IEEE80211_OFDM_RATE_24MB|IEEE80211_BASIC_RATE_MASK
+};
+
+static u8 rtw_basic_rate_mix[7] = {
+ IEEE80211_CCK_RATE_1MB|IEEE80211_BASIC_RATE_MASK, IEEE80211_CCK_RATE_2MB|IEEE80211_BASIC_RATE_MASK,
+ IEEE80211_CCK_RATE_5MB|IEEE80211_BASIC_RATE_MASK, IEEE80211_CCK_RATE_11MB|IEEE80211_BASIC_RATE_MASK,
+ IEEE80211_OFDM_RATE_6MB|IEEE80211_BASIC_RATE_MASK, IEEE80211_OFDM_RATE_12MB|IEEE80211_BASIC_RATE_MASK,
+ IEEE80211_OFDM_RATE_24MB|IEEE80211_BASIC_RATE_MASK
+};
+
+int cckrates_included(unsigned char *rate, int ratelen)
+{
+ int i;
+
+ for (i = 0; i < ratelen; i++) {
+ if ((((rate[i]) & 0x7f) == 2) || (((rate[i]) & 0x7f) == 4) ||
+ (((rate[i]) & 0x7f) == 11) || (((rate[i]) & 0x7f) == 22))
+ return true;
+ }
+ return false;
+}
+
+int cckratesonly_included(unsigned char *rate, int ratelen)
+{
+ int i;
+
+ for (i = 0; i < ratelen; i++) {
+ if ((((rate[i]) & 0x7f) != 2) && (((rate[i]) & 0x7f) != 4) &&
+ (((rate[i]) & 0x7f) != 11) && (((rate[i]) & 0x7f) != 22))
+ return false;
+ }
+
+ return true;
+}
+
+unsigned char networktype_to_raid(unsigned char network_type)
+{
+ switch (network_type) {
+ case WIRELESS_11B:
+ return RATR_INX_WIRELESS_B;
+ case WIRELESS_11A:
+ case WIRELESS_11G:
+ return RATR_INX_WIRELESS_G;
+ case WIRELESS_11BG:
+ return RATR_INX_WIRELESS_GB;
+ case WIRELESS_11_24N:
+ case WIRELESS_11_5N:
+ return RATR_INX_WIRELESS_N;
+ case WIRELESS_11A_5N:
+ case WIRELESS_11G_24N:
+ return RATR_INX_WIRELESS_NG;
+ case WIRELESS_11BG_24N:
+ return RATR_INX_WIRELESS_NGB;
+ default:
+ return RATR_INX_WIRELESS_GB;
+ }
+}
+
+u8 judge_network_type(struct adapter *padapter, unsigned char *rate, int ratelen)
+{
+ u8 network_type = 0;
+ struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv;
+ struct mlme_ext_info *pmlmeinfo = &(pmlmeext->mlmext_info);
+
+ if (pmlmeext->cur_channel > 14) {
+ if (pmlmeinfo->HT_enable)
+ network_type = WIRELESS_11_5N;
+
+ network_type |= WIRELESS_11A;
+ } else {
+ if (pmlmeinfo->HT_enable)
+ network_type = WIRELESS_11_24N;
+
+ if ((cckratesonly_included(rate, ratelen)) == true)
+ network_type |= WIRELESS_11B;
+ else if ((cckrates_included(rate, ratelen)) == true)
+ network_type |= WIRELESS_11BG;
+ else
+ network_type |= WIRELESS_11G;
+ }
+ return network_type;
+}
+
+static unsigned char ratetbl_val_2wifirate(unsigned char rate)
+{
+ switch (rate & 0x7f) {
+ case 0:
+ return IEEE80211_CCK_RATE_1MB;
+ case 1:
+ return IEEE80211_CCK_RATE_2MB;
+ case 2:
+ return IEEE80211_CCK_RATE_5MB;
+ case 3:
+ return IEEE80211_CCK_RATE_11MB;
+ case 4:
+ return IEEE80211_OFDM_RATE_6MB;
+ case 5:
+ return IEEE80211_OFDM_RATE_9MB;
+ case 6:
+ return IEEE80211_OFDM_RATE_12MB;
+ case 7:
+ return IEEE80211_OFDM_RATE_18MB;
+ case 8:
+ return IEEE80211_OFDM_RATE_24MB;
+ case 9:
+ return IEEE80211_OFDM_RATE_36MB;
+ case 10:
+ return IEEE80211_OFDM_RATE_48MB;
+ case 11:
+ return IEEE80211_OFDM_RATE_54MB;
+ default:
+ return 0;
+ }
+}
+
+static int is_basicrate(struct adapter *padapter, unsigned char rate)
+{
+ int i;
+ unsigned char val;
+ struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv;
+
+ for (i = 0; i < NumRates; i++) {
+ val = pmlmeext->basicrate[i];
+
+ if ((val != 0xff) && (val != 0xfe)) {
+ if (rate == ratetbl_val_2wifirate(val))
+ return true;
+ }
+ }
+ return false;
+}
+
+static unsigned int ratetbl2rateset(struct adapter *padapter, unsigned char *rateset)
+{
+ int i;
+ unsigned char rate;
+ unsigned int len = 0;
+ struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv;
+
+ for (i = 0; i < NumRates; i++) {
+ rate = pmlmeext->datarate[i];
+
+ switch (rate) {
+ case 0xff:
+ return len;
+ case 0xfe:
+ continue;
+ default:
+ rate = ratetbl_val_2wifirate(rate);
+
+ if (is_basicrate(padapter, rate) == true)
+ rate |= IEEE80211_BASIC_RATE_MASK;
+
+ rateset[len] = rate;
+ len++;
+ break;
+ }
+ }
+ return len;
+}
+
+void get_rate_set(struct adapter *padapter, unsigned char *pbssrate, int *bssrate_len)
+{
+ unsigned char supportedrates[NumRates];
+
+ memset(supportedrates, 0, NumRates);
+ *bssrate_len = ratetbl2rateset(padapter, supportedrates);
+ memcpy(pbssrate, supportedrates, *bssrate_len);
+}
+
+void UpdateBrateTbl(struct adapter *Adapter, u8 *mbrate)
+{
+ u8 i;
+ u8 rate;
+
+ /* 1M, 2M, 5.5M, 11M, 6M, 12M, 24M are mandatory. */
+ for (i = 0; i < NDIS_802_11_LENGTH_RATES_EX; i++) {
+ rate = mbrate[i] & 0x7f;
+ switch (rate) {
+ case IEEE80211_CCK_RATE_1MB:
+ case IEEE80211_CCK_RATE_2MB:
+ case IEEE80211_CCK_RATE_5MB:
+ case IEEE80211_CCK_RATE_11MB:
+ case IEEE80211_OFDM_RATE_6MB:
+ case IEEE80211_OFDM_RATE_12MB:
+ case IEEE80211_OFDM_RATE_24MB:
+ mbrate[i] |= IEEE80211_BASIC_RATE_MASK;
+ break;
+ }
+ }
+}
+
+void UpdateBrateTblForSoftAP(u8 *bssrateset, u32 bssratelen)
+{
+ u8 i;
+ u8 rate;
+
+ for (i = 0; i < bssratelen; i++) {
+ rate = bssrateset[i] & 0x7f;
+ switch (rate) {
+ case IEEE80211_CCK_RATE_1MB:
+ case IEEE80211_CCK_RATE_2MB:
+ case IEEE80211_CCK_RATE_5MB:
+ case IEEE80211_CCK_RATE_11MB:
+ bssrateset[i] |= IEEE80211_BASIC_RATE_MASK;
+ break;
+ }
+ }
+}
+
+void Save_DM_Func_Flag(struct adapter *padapter)
+{
+ u8 saveflag = true;
+
+ rtw_hal_set_hwreg(padapter, HW_VAR_DM_FUNC_OP, (u8 *)(&saveflag));
+}
+
+void Restore_DM_Func_Flag(struct adapter *padapter)
+{
+ u8 saveflag = false;
+
+ rtw_hal_set_hwreg(padapter, HW_VAR_DM_FUNC_OP, (u8 *)(&saveflag));
+}
+
+void Switch_DM_Func(struct adapter *padapter, u32 mode, u8 enable)
+{
+ if (enable)
+ rtw_hal_set_hwreg(padapter, HW_VAR_DM_FUNC_SET, (u8 *)(&mode));
+ else
+ rtw_hal_set_hwreg(padapter, HW_VAR_DM_FUNC_CLR, (u8 *)(&mode));
+}
+
+static void Set_NETYPE0_MSR(struct adapter *padapter, u8 type)
+{
+ rtw_hal_set_hwreg(padapter, HW_VAR_MEDIA_STATUS, (u8 *)(&type));
+}
+
+void Set_MSR(struct adapter *padapter, u8 type)
+{
+ Set_NETYPE0_MSR(padapter, type);
+}
+
+inline u8 rtw_get_oper_ch(struct adapter *adapter)
+{
+ return adapter->mlmeextpriv.oper_channel;
+}
+
+inline void rtw_set_oper_ch(struct adapter *adapter, u8 ch)
+{
+ adapter->mlmeextpriv.oper_channel = ch;
+}
+
+inline u8 rtw_get_oper_bw(struct adapter *adapter)
+{
+ return adapter->mlmeextpriv.oper_bwmode;
+}
+
+inline void rtw_set_oper_bw(struct adapter *adapter, u8 bw)
+{
+ adapter->mlmeextpriv.oper_bwmode = bw;
+}
+
+inline u8 rtw_get_oper_choffset(struct adapter *adapter)
+{
+ return adapter->mlmeextpriv.oper_ch_offset;
+}
+
+inline void rtw_set_oper_choffset(struct adapter *adapter, u8 offset)
+{
+ adapter->mlmeextpriv.oper_ch_offset = offset;
+}
+
+void SelectChannel(struct adapter *padapter, unsigned char channel)
+{
+ /* saved channel info */
+ rtw_set_oper_ch(padapter, channel);
+ rtw_hal_set_chan(padapter, channel);
+}
+
+void SetBWMode(struct adapter *padapter, unsigned short bwmode,
+ unsigned char channel_offset)
+{
+ /* saved bw info */
+ rtw_set_oper_bw(padapter, bwmode);
+ rtw_set_oper_choffset(padapter, channel_offset);
+
+ rtw_hal_set_bwmode(padapter, (enum ht_channel_width)bwmode, channel_offset);
+}
+
+void set_channel_bwmode(struct adapter *padapter, unsigned char channel, unsigned char channel_offset, unsigned short bwmode)
+{
+ u8 center_ch;
+
+ if (padapter->bNotifyChannelChange)
+ DBG_88E("[%s] ch = %d, offset = %d, bwmode = %d\n", __func__, channel, channel_offset, bwmode);
+
+ if ((bwmode == HT_CHANNEL_WIDTH_20) ||
+ (channel_offset == HAL_PRIME_CHNL_OFFSET_DONT_CARE)) {
+ /* SelectChannel(padapter, channel); */
+ center_ch = channel;
+ } else {
+ /* switch to the proper channel */
+ if (channel_offset == HAL_PRIME_CHNL_OFFSET_LOWER) {
+ /* SelectChannel(padapter, channel + 2); */
+ center_ch = channel + 2;
+ } else {
+ /* SelectChannel(padapter, channel - 2); */
+ center_ch = channel - 2;
+ }
+ }
+
+ /* set Channel */
+ /* saved channel/bw info */
+ rtw_set_oper_ch(padapter, channel);
+ rtw_set_oper_bw(padapter, bwmode);
+ rtw_set_oper_choffset(padapter, channel_offset);
+
+ rtw_hal_set_chan(padapter, center_ch); /* set center channel */
+ SetBWMode(padapter, bwmode, channel_offset);
+}
+
+int get_bsstype(unsigned short capability)
+{
+ if (capability & BIT(0))
+ return WIFI_FW_AP_STATE;
+ else if (capability & BIT(1))
+ return WIFI_FW_ADHOC_STATE;
+ else
+ return 0;
+}
+
+u16 get_beacon_interval(struct wlan_bssid_ex *bss)
+{
+ __le16 val;
+ memcpy((unsigned char *)&val, rtw_get_beacon_interval_from_ie(bss->IEs), 2);
+
+ return le16_to_cpu(val);
+}
+
+int is_client_associated_to_ap(struct adapter *padapter)
+{
+ struct mlme_ext_priv *pmlmeext;
+ struct mlme_ext_info *pmlmeinfo;
+
+ if (!padapter)
+ return _FAIL;
+
+ pmlmeext = &padapter->mlmeextpriv;
+ pmlmeinfo = &(pmlmeext->mlmext_info);
+
+ if ((pmlmeinfo->state & WIFI_FW_ASSOC_SUCCESS) && ((pmlmeinfo->state&0x03) == WIFI_FW_STATION_STATE))
+ return true;
+ else
+ return _FAIL;
+}
+
+int is_client_associated_to_ibss(struct adapter *padapter)
+{
+ struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv;
+ struct mlme_ext_info *pmlmeinfo = &(pmlmeext->mlmext_info);
+
+ if ((pmlmeinfo->state & WIFI_FW_ASSOC_SUCCESS) && ((pmlmeinfo->state&0x03) == WIFI_FW_ADHOC_STATE))
+ return true;
+ else
+ return _FAIL;
+}
+
+int is_IBSS_empty(struct adapter *padapter)
+{
+ unsigned int i;
+ struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv;
+ struct mlme_ext_info *pmlmeinfo = &(pmlmeext->mlmext_info);
+
+ for (i = IBSS_START_MAC_ID; i < NUM_STA; i++) {
+ if (pmlmeinfo->FW_sta_info[i].status == 1)
+ return _FAIL;
+ }
+ return true;
+}
+
+unsigned int decide_wait_for_beacon_timeout(unsigned int bcn_interval)
+{
+ if ((bcn_interval << 2) < WAIT_FOR_BCN_TO_MIN)
+ return WAIT_FOR_BCN_TO_MIN;
+ else if ((bcn_interval << 2) > WAIT_FOR_BCN_TO_MAX)
+ return WAIT_FOR_BCN_TO_MAX;
+ else
+ return bcn_interval << 2;
+}
+
+void CAM_empty_entry(struct adapter *Adapter, u8 ucIndex)
+{
+ rtw_hal_set_hwreg(Adapter, HW_VAR_CAM_EMPTY_ENTRY, (u8 *)(&ucIndex));
+}
+
+void invalidate_cam_all(struct adapter *padapter)
+{
+ rtw_hal_set_hwreg(padapter, HW_VAR_CAM_INVALID_ALL, NULL);
+}
+
+void write_cam(struct adapter *padapter, u8 entry, u16 ctrl, u8 *mac, u8 *key)
+{
+ unsigned int i, val, addr;
+ int j;
+ u32 cam_val[2];
+
+ addr = entry << 3;
+
+ for (j = 5; j >= 0; j--) {
+ switch (j) {
+ case 0:
+ val = ctrl | (mac[0] << 16) | (mac[1] << 24);
+ break;
+ case 1:
+ val = mac[2] | (mac[3] << 8) | (mac[4] << 16) | (mac[5] << 24);
+ break;
+ default:
+ i = (j - 2) << 2;
+ val = key[i] | (key[i+1] << 8) | (key[i+2] << 16) | (key[i+3] << 24);
+ break;
+ }
+
+ cam_val[0] = val;
+ cam_val[1] = addr + (unsigned int)j;
+
+ rtw_hal_set_hwreg(padapter, HW_VAR_CAM_WRITE, (u8 *)cam_val);
+ }
+}
+
+void clear_cam_entry(struct adapter *padapter, u8 entry)
+{
+ unsigned char null_sta[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+ unsigned char null_key[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+
+ write_cam(padapter, entry, 0, null_sta, null_key);
+}
+
+int allocate_fw_sta_entry(struct adapter *padapter)
+{
+ unsigned int mac_id;
+ struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv;
+ struct mlme_ext_info *pmlmeinfo = &(pmlmeext->mlmext_info);
+
+ for (mac_id = IBSS_START_MAC_ID; mac_id < NUM_STA; mac_id++) {
+ if (pmlmeinfo->FW_sta_info[mac_id].status == 0) {
+ pmlmeinfo->FW_sta_info[mac_id].status = 1;
+ pmlmeinfo->FW_sta_info[mac_id].retry = 0;
+ break;
+ }
+ }
+
+ return mac_id;
+}
+
+void flush_all_cam_entry(struct adapter *padapter)
+{
+ struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv;
+ struct mlme_ext_info *pmlmeinfo = &(pmlmeext->mlmext_info);
+
+ rtw_hal_set_hwreg(padapter, HW_VAR_CAM_INVALID_ALL, NULL);
+
+ memset((u8 *)(pmlmeinfo->FW_sta_info), 0, sizeof(pmlmeinfo->FW_sta_info));
+}
+
+int WMM_param_handler(struct adapter *padapter, struct ndis_802_11_var_ie *pIE)
+{
+ /* struct registry_priv *pregpriv = &padapter->registrypriv; */
+ struct mlme_priv *pmlmepriv = &(padapter->mlmepriv);
+ struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv;
+ struct mlme_ext_info *pmlmeinfo = &(pmlmeext->mlmext_info);
+
+ if (pmlmepriv->qospriv.qos_option == 0) {
+ pmlmeinfo->WMM_enable = 0;
+ return _FAIL;
+ }
+
+ pmlmeinfo->WMM_enable = 1;
+ memcpy(&(pmlmeinfo->WMM_param), (pIE->data + 6), sizeof(struct WMM_para_element));
+ return true;
+}
+
+void WMMOnAssocRsp(struct adapter *padapter)
+{
+ u8 ACI, ACM, AIFS, ECWMin, ECWMax, aSifsTime;
+ u8 acm_mask;
+ u16 TXOP;
+ u32 acParm, i;
+ u32 edca[4], inx[4];
+ struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv;
+ struct mlme_ext_info *pmlmeinfo = &(pmlmeext->mlmext_info);
+ struct xmit_priv *pxmitpriv = &padapter->xmitpriv;
+ struct registry_priv *pregpriv = &padapter->registrypriv;
+
+ if (pmlmeinfo->WMM_enable == 0) {
+ padapter->mlmepriv.acm_mask = 0;
+ return;
+ }
+
+ acm_mask = 0;
+
+ if (pmlmeext->cur_wireless_mode == WIRELESS_11B)
+ aSifsTime = 10;
+ else
+ aSifsTime = 16;
+
+ for (i = 0; i < 4; i++) {
+ ACI = (pmlmeinfo->WMM_param.ac_param[i].ACI_AIFSN >> 5) & 0x03;
+ ACM = (pmlmeinfo->WMM_param.ac_param[i].ACI_AIFSN >> 4) & 0x01;
+
+ /* AIFS = AIFSN * slot time + SIFS - r2t phy delay */
+ AIFS = (pmlmeinfo->WMM_param.ac_param[i].ACI_AIFSN & 0x0f) * pmlmeinfo->slotTime + aSifsTime;
+
+ ECWMin = pmlmeinfo->WMM_param.ac_param[i].CW & 0x0f;
+ ECWMax = (pmlmeinfo->WMM_param.ac_param[i].CW & 0xf0) >> 4;
+ TXOP = le16_to_cpu(pmlmeinfo->WMM_param.ac_param[i].TXOP_limit);
+
+ acParm = AIFS | (ECWMin << 8) | (ECWMax << 12) | (TXOP << 16);
+
+ switch (ACI) {
+ case 0x0:
+ rtw_hal_set_hwreg(padapter, HW_VAR_AC_PARAM_BE, (u8 *)(&acParm));
+ acm_mask |= (ACM ? BIT(1) : 0);
+ edca[XMIT_BE_QUEUE] = acParm;
+ break;
+ case 0x1:
+ rtw_hal_set_hwreg(padapter, HW_VAR_AC_PARAM_BK, (u8 *)(&acParm));
+ edca[XMIT_BK_QUEUE] = acParm;
+ break;
+ case 0x2:
+ rtw_hal_set_hwreg(padapter, HW_VAR_AC_PARAM_VI, (u8 *)(&acParm));
+ acm_mask |= (ACM ? BIT(2) : 0);
+ edca[XMIT_VI_QUEUE] = acParm;
+ break;
+ case 0x3:
+ rtw_hal_set_hwreg(padapter, HW_VAR_AC_PARAM_VO, (u8 *)(&acParm));
+ acm_mask |= (ACM ? BIT(3) : 0);
+ edca[XMIT_VO_QUEUE] = acParm;
+ break;
+ }
+
+ DBG_88E("WMM(%x): %x, %x\n", ACI, ACM, acParm);
+ }
+
+ if (padapter->registrypriv.acm_method == 1)
+ rtw_hal_set_hwreg(padapter, HW_VAR_ACM_CTRL, (u8 *)(&acm_mask));
+ else
+ padapter->mlmepriv.acm_mask = acm_mask;
+
+ inx[0] = 0; inx[1] = 1; inx[2] = 2; inx[3] = 3;
+
+ if (pregpriv->wifi_spec == 1) {
+ u32 j, tmp, change_inx = false;
+
+ /* entry indx: 0->vo, 1->vi, 2->be, 3->bk. */
+ for (i = 0; i < 4; i++) {
+ for (j = i+1; j < 4; j++) {
+ /* compare CW and AIFS */
+ if ((edca[j] & 0xFFFF) < (edca[i] & 0xFFFF)) {
+ change_inx = true;
+ } else if ((edca[j] & 0xFFFF) == (edca[i] & 0xFFFF)) {
+ /* compare TXOP */
+ if ((edca[j] >> 16) > (edca[i] >> 16))
+ change_inx = true;
+ }
+
+ if (change_inx) {
+ tmp = edca[i];
+ edca[i] = edca[j];
+ edca[j] = tmp;
+
+ tmp = inx[i];
+ inx[i] = inx[j];
+ inx[j] = tmp;
+
+ change_inx = false;
+ }
+ }
+ }
+ }
+
+ for (i = 0; i < 4; i++) {
+ pxmitpriv->wmm_para_seq[i] = inx[i];
+ DBG_88E("wmm_para_seq(%d): %d\n", i, pxmitpriv->wmm_para_seq[i]);
+ }
+}
+
+static void bwmode_update_check(struct adapter *padapter, struct ndis_802_11_var_ie *pIE)
+{
+ unsigned char new_bwmode;
+ unsigned char new_ch_offset;
+ struct HT_info_element *pHT_info;
+ struct mlme_priv *pmlmepriv = &(padapter->mlmepriv);
+ struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv;
+ struct mlme_ext_info *pmlmeinfo = &(pmlmeext->mlmext_info);
+ struct registry_priv *pregistrypriv = &padapter->registrypriv;
+ struct ht_priv *phtpriv = &pmlmepriv->htpriv;
+
+ if (!pIE)
+ return;
+
+ if (!phtpriv)
+ return;
+
+ if (pIE->Length > sizeof(struct HT_info_element))
+ return;
+
+ pHT_info = (struct HT_info_element *)pIE->data;
+
+ if ((pHT_info->infos[0] & BIT(2)) && pregistrypriv->cbw40_enable) {
+ new_bwmode = HT_CHANNEL_WIDTH_40;
+
+ switch (pHT_info->infos[0] & 0x3) {
+ case 1:
+ new_ch_offset = HAL_PRIME_CHNL_OFFSET_LOWER;
+ break;
+ case 3:
+ new_ch_offset = HAL_PRIME_CHNL_OFFSET_UPPER;
+ break;
+ default:
+ new_ch_offset = HAL_PRIME_CHNL_OFFSET_DONT_CARE;
+ break;
+ }
+ } else {
+ new_bwmode = HT_CHANNEL_WIDTH_20;
+ new_ch_offset = HAL_PRIME_CHNL_OFFSET_DONT_CARE;
+ }
+
+ if ((new_bwmode != pmlmeext->cur_bwmode) ||
+ (new_ch_offset != pmlmeext->cur_ch_offset)) {
+ pmlmeinfo->bwmode_updated = true;
+
+ pmlmeext->cur_bwmode = new_bwmode;
+ pmlmeext->cur_ch_offset = new_ch_offset;
+
+ /* update HT info also */
+ HT_info_handler(padapter, pIE);
+ } else {
+ pmlmeinfo->bwmode_updated = false;
+ }
+
+ if (pmlmeinfo->bwmode_updated) {
+ struct sta_info *psta;
+ struct wlan_bssid_ex *cur_network = &(pmlmeinfo->network);
+ struct sta_priv *pstapriv = &padapter->stapriv;
+
+ /* set_channel_bwmode(padapter, pmlmeext->cur_channel, pmlmeext->cur_ch_offset, pmlmeext->cur_bwmode); */
+
+ /* update ap's stainfo */
+ psta = rtw_get_stainfo(pstapriv, cur_network->MacAddress);
+ if (psta) {
+ struct ht_priv *phtpriv_sta = &psta->htpriv;
+
+ if (phtpriv_sta->ht_option) {
+ /* bwmode */
+ phtpriv_sta->bwmode = pmlmeext->cur_bwmode;
+ phtpriv_sta->ch_offset = pmlmeext->cur_ch_offset;
+ } else {
+ phtpriv_sta->bwmode = HT_CHANNEL_WIDTH_20;
+ phtpriv_sta->ch_offset = HAL_PRIME_CHNL_OFFSET_DONT_CARE;
+ }
+ }
+ }
+}
+
+void HT_caps_handler(struct adapter *padapter, struct ndis_802_11_var_ie *pIE)
+{
+ unsigned int i;
+ u8 rf_type;
+ u8 max_AMPDU_len, min_MPDU_spacing;
+ struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv;
+ struct mlme_ext_info *pmlmeinfo = &(pmlmeext->mlmext_info);
+ struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
+ struct ht_priv *phtpriv = &pmlmepriv->htpriv;
+
+ if (pIE == NULL)
+ return;
+
+ if (!phtpriv->ht_option)
+ return;
+
+ pmlmeinfo->HT_caps_enable = 1;
+
+ for (i = 0; i < (pIE->Length); i++) {
+ if (i != 2) {
+ /* Got the endian issue here. */
+ pmlmeinfo->HT_caps.u.HT_cap[i] &= (pIE->data[i]);
+ } else {
+ /* modify from fw by Thomas 2010/11/17 */
+ if ((pmlmeinfo->HT_caps.u.HT_cap_element.AMPDU_para & 0x3) > (pIE->data[i] & 0x3))
+ max_AMPDU_len = pIE->data[i] & 0x3;
+ else
+ max_AMPDU_len = pmlmeinfo->HT_caps.u.HT_cap_element.AMPDU_para & 0x3;
+
+ if ((pmlmeinfo->HT_caps.u.HT_cap_element.AMPDU_para & 0x1c) > (pIE->data[i] & 0x1c))
+ min_MPDU_spacing = pmlmeinfo->HT_caps.u.HT_cap_element.AMPDU_para & 0x1c;
+ else
+ min_MPDU_spacing = pIE->data[i] & 0x1c;
+
+ pmlmeinfo->HT_caps.u.HT_cap_element.AMPDU_para = max_AMPDU_len | min_MPDU_spacing;
+ }
+ }
+
+ rtw_hal_get_hwreg(padapter, HW_VAR_RF_TYPE, (u8 *)(&rf_type));
+
+ /* update the MCS rates */
+ for (i = 0; i < 16; i++) {
+ if ((rf_type == RF_1T1R) || (rf_type == RF_1T2R))
+ pmlmeinfo->HT_caps.u.HT_cap_element.MCS_rate[i] &= MCS_rate_1R[i];
+ else
+ pmlmeinfo->HT_caps.u.HT_cap_element.MCS_rate[i] &= MCS_rate_2R[i];
+ }
+}
+
+void HT_info_handler(struct adapter *padapter, struct ndis_802_11_var_ie *pIE)
+{
+ struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv;
+ struct mlme_ext_info *pmlmeinfo = &(pmlmeext->mlmext_info);
+ struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
+ struct ht_priv *phtpriv = &pmlmepriv->htpriv;
+
+ if (pIE == NULL)
+ return;
+
+ if (!phtpriv->ht_option)
+ return;
+
+ if (pIE->Length > sizeof(struct HT_info_element))
+ return;
+
+ pmlmeinfo->HT_info_enable = 1;
+ memcpy(&(pmlmeinfo->HT_info), pIE->data, pIE->Length);
+}
+
+void HTOnAssocRsp(struct adapter *padapter)
+{
+ unsigned char max_AMPDU_len;
+ unsigned char min_MPDU_spacing;
+ /* struct registry_priv *pregpriv = &padapter->registrypriv; */
+ struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv;
+ struct mlme_ext_info *pmlmeinfo = &(pmlmeext->mlmext_info);
+
+ DBG_88E("%s\n", __func__);
+
+ if ((pmlmeinfo->HT_info_enable) && (pmlmeinfo->HT_caps_enable)) {
+ pmlmeinfo->HT_enable = 1;
+ } else {
+ pmlmeinfo->HT_enable = 0;
+ return;
+ }
+
+ /* handle A-MPDU parameter field */
+ /*
+ AMPDU_para [1:0]:Max AMPDU Len => 0:8k , 1:16k, 2:32k, 3:64k
+ AMPDU_para [4:2]:Min MPDU Start Spacing
+ */
+ max_AMPDU_len = pmlmeinfo->HT_caps.u.HT_cap_element.AMPDU_para & 0x03;
+
+ min_MPDU_spacing = (pmlmeinfo->HT_caps.u.HT_cap_element.AMPDU_para & 0x1c) >> 2;
+
+ rtw_hal_set_hwreg(padapter, HW_VAR_AMPDU_MIN_SPACE, (u8 *)(&min_MPDU_spacing));
+
+ rtw_hal_set_hwreg(padapter, HW_VAR_AMPDU_FACTOR, (u8 *)(&max_AMPDU_len));
+}
+
+void ERP_IE_handler(struct adapter *padapter, struct ndis_802_11_var_ie *pIE)
+{
+ struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv;
+ struct mlme_ext_info *pmlmeinfo = &(pmlmeext->mlmext_info);
+
+ if (pIE->Length > 1)
+ return;
+
+ pmlmeinfo->ERP_enable = 1;
+ memcpy(&(pmlmeinfo->ERP_IE), pIE->data, pIE->Length);
+}
+
+void VCS_update(struct adapter *padapter, struct sta_info *psta)
+{
+ struct registry_priv *pregpriv = &padapter->registrypriv;
+ struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv;
+ struct mlme_ext_info *pmlmeinfo = &(pmlmeext->mlmext_info);
+
+ switch (pregpriv->vrtl_carrier_sense) { /* 0:off 1:on 2:auto */
+ case 0: /* off */
+ psta->rtsen = 0;
+ psta->cts2self = 0;
+ break;
+ case 1: /* on */
+ if (pregpriv->vcs_type == 1) { /* 1:RTS/CTS 2:CTS to self */
+ psta->rtsen = 1;
+ psta->cts2self = 0;
+ } else {
+ psta->rtsen = 0;
+ psta->cts2self = 1;
+ }
+ break;
+ case 2: /* auto */
+ default:
+ if ((pmlmeinfo->ERP_enable) && (pmlmeinfo->ERP_IE & BIT(1))) {
+ if (pregpriv->vcs_type == 1) {
+ psta->rtsen = 1;
+ psta->cts2self = 0;
+ } else {
+ psta->rtsen = 0;
+ psta->cts2self = 1;
+ }
+ } else {
+ psta->rtsen = 0;
+ psta->cts2self = 0;
+ }
+ break;
+ }
+}
+
+int rtw_check_bcn_info(struct adapter *Adapter, u8 *pframe, u32 packet_len)
+{
+ unsigned int len;
+ unsigned char *p;
+ unsigned short val16, subtype;
+ struct wlan_network *cur_network = &(Adapter->mlmepriv.cur_network);
+ /* u8 wpa_ie[255], rsn_ie[255]; */
+ u16 wpa_len = 0, rsn_len = 0;
+ u8 encryp_protocol = 0;
+ struct wlan_bssid_ex *bssid;
+ int group_cipher = 0, pairwise_cipher = 0, is_8021x = 0;
+ unsigned char *pbuf;
+ u32 wpa_ielen = 0;
+ u8 *pbssid = GetAddr3Ptr(pframe);
+ struct HT_info_element *pht_info = NULL;
+ struct rtw_ieee80211_ht_cap *pht_cap = NULL;
+ u32 bcn_channel;
+ unsigned short ht_cap_info;
+ unsigned char ht_info_infos_0;
+ int ssid_len;
+
+ if (is_client_associated_to_ap(Adapter) == false)
+ return true;
+
+ len = packet_len - sizeof(struct rtw_ieee80211_hdr_3addr);
+
+ if (len > MAX_IE_SZ) {
+ DBG_88E("%s IE too long for survey event\n", __func__);
+ return _FAIL;
+ }
+
+ if (!memcmp(cur_network->network.MacAddress, pbssid, 6) == false) {
+ DBG_88E("Oops: rtw_check_network_encrypt linked but recv other bssid bcn\n%pM %pM\n",
+ (pbssid), (cur_network->network.MacAddress));
+ return true;
+ }
+
+ bssid = kzalloc(sizeof(struct wlan_bssid_ex), GFP_ATOMIC);
+ if (!bssid)
+ return _FAIL;
+
+ subtype = GetFrameSubType(pframe) >> 4;
+
+ if (subtype == WIFI_BEACON)
+ bssid->Reserved[0] = 1;
+
+ bssid->Length = sizeof(struct wlan_bssid_ex) - MAX_IE_SZ + len;
+
+ /* below is to copy the information element */
+ bssid->IELength = len;
+ memcpy(bssid->IEs, (pframe + sizeof(struct rtw_ieee80211_hdr_3addr)), bssid->IELength);
+
+ /* check bw and channel offset */
+ /* parsing HT_CAP_IE */
+ p = rtw_get_ie(bssid->IEs + _FIXED_IE_LENGTH_, _HT_CAPABILITY_IE_, &len, bssid->IELength - _FIXED_IE_LENGTH_);
+ if (p && len > 0) {
+ pht_cap = (struct rtw_ieee80211_ht_cap *)(p + 2);
+ ht_cap_info = pht_cap->cap_info;
+ } else {
+ ht_cap_info = 0;
+ }
+ /* parsing HT_INFO_IE */
+ p = rtw_get_ie(bssid->IEs + _FIXED_IE_LENGTH_, _HT_ADD_INFO_IE_, &len, bssid->IELength - _FIXED_IE_LENGTH_);
+ if (p && len > 0) {
+ pht_info = (struct HT_info_element *)(p + 2);
+ ht_info_infos_0 = pht_info->infos[0];
+ } else {
+ ht_info_infos_0 = 0;
+ }
+ if (ht_cap_info != cur_network->BcnInfo.ht_cap_info ||
+ ((ht_info_infos_0&0x03) != (cur_network->BcnInfo.ht_info_infos_0&0x03))) {
+ DBG_88E("%s bcn now: ht_cap_info:%x ht_info_infos_0:%x\n", __func__,
+ ht_cap_info, ht_info_infos_0);
+ DBG_88E("%s bcn link: ht_cap_info:%x ht_info_infos_0:%x\n", __func__,
+ cur_network->BcnInfo.ht_cap_info, cur_network->BcnInfo.ht_info_infos_0);
+ DBG_88E("%s bw mode change, disconnect\n", __func__);
+ /* bcn_info_update */
+ cur_network->BcnInfo.ht_cap_info = ht_cap_info;
+ cur_network->BcnInfo.ht_info_infos_0 = ht_info_infos_0;
+ /* to do : need to check that whether modify related register of BB or not */
+ /* goto _mismatch; */
+ }
+
+ /* Checking for channel */
+ p = rtw_get_ie(bssid->IEs + _FIXED_IE_LENGTH_, _DSSET_IE_, &len, bssid->IELength - _FIXED_IE_LENGTH_);
+ if (p) {
+ bcn_channel = *(p + 2);
+ } else {/* In 5G, some ap do not have DSSET IE checking HT info for channel */
+ p = rtw_get_ie(bssid->IEs + _FIXED_IE_LENGTH_, _HT_ADD_INFO_IE_, &len, bssid->IELength - _FIXED_IE_LENGTH_);
+ if (pht_info) {
+ bcn_channel = pht_info->primary_channel;
+ } else { /* we don't find channel IE, so don't check it */
+ DBG_88E("Oops: %s we don't find channel IE, so don't check it\n", __func__);
+ bcn_channel = Adapter->mlmeextpriv.cur_channel;
+ }
+ }
+ if (bcn_channel != Adapter->mlmeextpriv.cur_channel) {
+ DBG_88E("%s beacon channel:%d cur channel:%d disconnect\n", __func__,
+ bcn_channel, Adapter->mlmeextpriv.cur_channel);
+ goto _mismatch;
+ }
+
+ /* checking SSID */
+ ssid_len = 0;
+ p = rtw_get_ie(bssid->IEs + _FIXED_IE_LENGTH_, _SSID_IE_, &len, bssid->IELength - _FIXED_IE_LENGTH_);
+ if (p) {
+ ssid_len = *(p + 1);
+ if (ssid_len > NDIS_802_11_LENGTH_SSID)
+ ssid_len = 0;
+ }
+ memcpy(bssid->Ssid.Ssid, (p + 2), ssid_len);
+ bssid->Ssid.SsidLength = ssid_len;
+
+ RT_TRACE(_module_rtl871x_mlme_c_, _drv_info_, ("%s bssid.Ssid.Ssid:%s bssid.Ssid.SsidLength:%d "
+ "cur_network->network.Ssid.Ssid:%s len:%d\n", __func__, bssid->Ssid.Ssid,
+ bssid->Ssid.SsidLength, cur_network->network.Ssid.Ssid,
+ cur_network->network.Ssid.SsidLength));
+
+ if (memcmp(bssid->Ssid.Ssid, cur_network->network.Ssid.Ssid, 32) ||
+ bssid->Ssid.SsidLength != cur_network->network.Ssid.SsidLength) {
+ if (bssid->Ssid.Ssid[0] != '\0' && bssid->Ssid.SsidLength != 0) { /* not hidden ssid */
+ DBG_88E("%s(), SSID is not match return FAIL\n", __func__);
+ goto _mismatch;
+ }
+ }
+
+ /* check encryption info */
+ val16 = rtw_get_capability((struct wlan_bssid_ex *)bssid);
+
+ if (val16 & BIT(4))
+ bssid->Privacy = 1;
+ else
+ bssid->Privacy = 0;
+
+ RT_TRACE(_module_rtl871x_mlme_c_, _drv_info_,
+ ("%s(): cur_network->network.Privacy is %d, bssid.Privacy is %d\n",
+ __func__, cur_network->network.Privacy, bssid->Privacy));
+ if (cur_network->network.Privacy != bssid->Privacy) {
+ DBG_88E("%s(), privacy is not match return FAIL\n", __func__);
+ goto _mismatch;
+ }
+
+ rtw_get_sec_ie(bssid->IEs, bssid->IELength, NULL, &rsn_len, NULL, &wpa_len);
+
+ if (rsn_len > 0) {
+ encryp_protocol = ENCRYP_PROTOCOL_WPA2;
+ } else if (wpa_len > 0) {
+ encryp_protocol = ENCRYP_PROTOCOL_WPA;
+ } else {
+ if (bssid->Privacy)
+ encryp_protocol = ENCRYP_PROTOCOL_WEP;
+ }
+
+ if (cur_network->BcnInfo.encryp_protocol != encryp_protocol) {
+ DBG_88E("%s(): encryption protocol is not match , return FAIL\n", __func__);
+ goto _mismatch;
+ }
+
+ if (encryp_protocol == ENCRYP_PROTOCOL_WPA || encryp_protocol == ENCRYP_PROTOCOL_WPA2) {
+ pbuf = rtw_get_wpa_ie(&bssid->IEs[12], &wpa_ielen, bssid->IELength-12);
+ if (pbuf && (wpa_ielen > 0)) {
+ if (_SUCCESS == rtw_parse_wpa_ie(pbuf, wpa_ielen+2, &group_cipher, &pairwise_cipher, &is_8021x)) {
+ RT_TRACE(_module_rtl871x_mlme_c_, _drv_info_,
+ ("%s pnetwork->pairwise_cipher: %d, group_cipher is %d, is_8021x is %d\n", __func__,
+ pairwise_cipher, group_cipher, is_8021x));
+ }
+ } else {
+ pbuf = rtw_get_wpa2_ie(&bssid->IEs[12], &wpa_ielen, bssid->IELength-12);
+
+ if (pbuf && (wpa_ielen > 0)) {
+ if (_SUCCESS == rtw_parse_wpa2_ie(pbuf, wpa_ielen+2, &group_cipher, &pairwise_cipher, &is_8021x)) {
+ RT_TRACE(_module_rtl871x_mlme_c_, _drv_info_,
+ ("%s pnetwork->pairwise_cipher: %d, pnetwork->group_cipher is %d, is_802x is %d\n",
+ __func__, pairwise_cipher, group_cipher, is_8021x));
+ }
+ }
+ }
+
+ RT_TRACE(_module_rtl871x_mlme_c_, _drv_err_,
+ ("%s cur_network->group_cipher is %d: %d\n", __func__, cur_network->BcnInfo.group_cipher, group_cipher));
+ if (pairwise_cipher != cur_network->BcnInfo.pairwise_cipher || group_cipher != cur_network->BcnInfo.group_cipher) {
+ DBG_88E("%s pairwise_cipher(%x:%x) or group_cipher(%x:%x) is not match , return FAIL\n", __func__,
+ pairwise_cipher, cur_network->BcnInfo.pairwise_cipher,
+ group_cipher, cur_network->BcnInfo.group_cipher);
+ goto _mismatch;
+ }
+
+ if (is_8021x != cur_network->BcnInfo.is_8021x) {
+ DBG_88E("%s authentication is not match , return FAIL\n", __func__);
+ goto _mismatch;
+ }
+ }
+
+ kfree(bssid);
+ return _SUCCESS;
+
+_mismatch:
+ kfree(bssid);
+ return _FAIL;
+}
+
+void update_beacon_info(struct adapter *padapter, u8 *pframe, uint pkt_len, struct sta_info *psta)
+{
+ unsigned int i;
+ unsigned int len;
+ struct ndis_802_11_var_ie *pIE;
+
+ len = pkt_len - (_BEACON_IE_OFFSET_ + WLAN_HDR_A3_LEN);
+
+ for (i = 0; i < len;) {
+ pIE = (struct ndis_802_11_var_ie *)(pframe + (_BEACON_IE_OFFSET_ + WLAN_HDR_A3_LEN) + i);
+
+ switch (pIE->ElementID) {
+ case _HT_EXTRA_INFO_IE_: /* HT info */
+ /* HT_info_handler(padapter, pIE); */
+ bwmode_update_check(padapter, pIE);
+ break;
+ case _ERPINFO_IE_:
+ ERP_IE_handler(padapter, pIE);
+ VCS_update(padapter, psta);
+ break;
+ default:
+ break;
+ }
+
+ i += (pIE->Length + 2);
+ }
+}
+
+unsigned int is_ap_in_tkip(struct adapter *padapter)
+{
+ u32 i;
+ struct ndis_802_11_var_ie *pIE;
+ struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv;
+ struct mlme_ext_info *pmlmeinfo = &(pmlmeext->mlmext_info);
+ struct wlan_bssid_ex *cur_network = &(pmlmeinfo->network);
+
+ if (rtw_get_capability((struct wlan_bssid_ex *)cur_network) & WLAN_CAPABILITY_PRIVACY) {
+ for (i = sizeof(struct ndis_802_11_fixed_ie); i < pmlmeinfo->network.IELength;) {
+ pIE = (struct ndis_802_11_var_ie *)(pmlmeinfo->network.IEs + i);
+
+ switch (pIE->ElementID) {
+ case _VENDOR_SPECIFIC_IE_:
+ if ((!memcmp(pIE->data, RTW_WPA_OUI, 4)) && (!memcmp((pIE->data + 12), WPA_TKIP_CIPHER, 4)))
+ return true;
+ break;
+ case _RSN_IE_2_:
+ if (!memcmp((pIE->data + 8), RSN_TKIP_CIPHER, 4))
+ return true;
+ default:
+ break;
+ }
+
+ i += (pIE->Length + 2);
+ }
+ return false;
+ } else {
+ return false;
+ }
+}
+
+unsigned int should_forbid_n_rate(struct adapter *padapter)
+{
+ u32 i;
+ struct ndis_802_11_var_ie *pIE;
+ struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
+ struct wlan_bssid_ex *cur_network = &pmlmepriv->cur_network.network;
+
+ if (rtw_get_capability((struct wlan_bssid_ex *)cur_network) & WLAN_CAPABILITY_PRIVACY) {
+ for (i = sizeof(struct ndis_802_11_fixed_ie); i < cur_network->IELength;) {
+ pIE = (struct ndis_802_11_var_ie *)(cur_network->IEs + i);
+
+ switch (pIE->ElementID) {
+ case _VENDOR_SPECIFIC_IE_:
+ if (!memcmp(pIE->data, RTW_WPA_OUI, 4) &&
+ ((!memcmp((pIE->data + 12), WPA_CIPHER_SUITE_CCMP, 4)) ||
+ (!memcmp((pIE->data + 16), WPA_CIPHER_SUITE_CCMP, 4))))
+ return false;
+ break;
+ case _RSN_IE_2_:
+ if ((!memcmp((pIE->data + 8), RSN_CIPHER_SUITE_CCMP, 4)) ||
+ (!memcmp((pIE->data + 12), RSN_CIPHER_SUITE_CCMP, 4)))
+ return false;
+ default:
+ break;
+ }
+
+ i += (pIE->Length + 2);
+ }
+
+ return true;
+ } else {
+ return false;
+ }
+}
+
+unsigned int is_ap_in_wep(struct adapter *padapter)
+{
+ u32 i;
+ struct ndis_802_11_var_ie *pIE;
+ struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv;
+ struct mlme_ext_info *pmlmeinfo = &(pmlmeext->mlmext_info);
+ struct wlan_bssid_ex *cur_network = &(pmlmeinfo->network);
+
+ if (rtw_get_capability((struct wlan_bssid_ex *)cur_network) & WLAN_CAPABILITY_PRIVACY) {
+ for (i = sizeof(struct ndis_802_11_fixed_ie); i < pmlmeinfo->network.IELength;) {
+ pIE = (struct ndis_802_11_var_ie *)(pmlmeinfo->network.IEs + i);
+
+ switch (pIE->ElementID) {
+ case _VENDOR_SPECIFIC_IE_:
+ if (!memcmp(pIE->data, RTW_WPA_OUI, 4))
+ return false;
+ break;
+ case _RSN_IE_2_:
+ return false;
+ default:
+ break;
+ }
+ i += (pIE->Length + 2);
+ }
+ return true;
+ } else {
+ return false;
+ }
+}
+
+static int wifirate2_ratetbl_inx(unsigned char rate)
+{
+ rate = rate & 0x7f;
+
+ switch (rate) {
+ case 54*2:
+ return 11;
+ case 48*2:
+ return 10;
+ case 36*2:
+ return 9;
+ case 24*2:
+ return 8;
+ case 18*2:
+ return 7;
+ case 12*2:
+ return 6;
+ case 9*2:
+ return 5;
+ case 6*2:
+ return 4;
+ case 11*2:
+ return 3;
+ case 11:
+ return 2;
+ case 2*2:
+ return 1;
+ case 1*2:
+ return 0;
+ default:
+ return 0;
+ }
+}
+
+unsigned int update_basic_rate(unsigned char *ptn, unsigned int ptn_sz)
+{
+ unsigned int i, num_of_rate;
+ unsigned int mask = 0;
+
+ num_of_rate = (ptn_sz > NumRates) ? NumRates : ptn_sz;
+
+ for (i = 0; i < num_of_rate; i++) {
+ if ((*(ptn + i)) & 0x80)
+ mask |= 0x1 << wifirate2_ratetbl_inx(*(ptn + i));
+ }
+ return mask;
+}
+
+unsigned int update_supported_rate(unsigned char *ptn, unsigned int ptn_sz)
+{
+ unsigned int i, num_of_rate;
+ unsigned int mask = 0;
+
+ num_of_rate = (ptn_sz > NumRates) ? NumRates : ptn_sz;
+
+ for (i = 0; i < num_of_rate; i++)
+ mask |= 0x1 << wifirate2_ratetbl_inx(*(ptn + i));
+ return mask;
+}
+
+unsigned int update_MSC_rate(struct HT_caps_element *pHT_caps)
+{
+ unsigned int mask = 0;
+
+ mask = (pHT_caps->u.HT_cap_element.MCS_rate[0] << 12) | (pHT_caps->u.HT_cap_element.MCS_rate[1] << 20);
+
+ return mask;
+}
+
+int support_short_GI(struct adapter *padapter, struct HT_caps_element *pHT_caps)
+{
+ unsigned char bit_offset;
+ struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv;
+ struct mlme_ext_info *pmlmeinfo = &(pmlmeext->mlmext_info);
+
+ if (!(pmlmeinfo->HT_enable))
+ return _FAIL;
+
+ if (pmlmeinfo->assoc_AP_vendor == HT_IOT_PEER_RALINK)
+ return _FAIL;
+
+ bit_offset = (pmlmeext->cur_bwmode & HT_CHANNEL_WIDTH_40) ? 6 : 5;
+
+ if (__le16_to_cpu(pHT_caps->u.HT_cap_element.HT_caps_info) & (0x1 << bit_offset))
+ return _SUCCESS;
+ else
+ return _FAIL;
+}
+
+unsigned char get_highest_rate_idx(u32 mask)
+{
+ int i;
+ unsigned char rate_idx = 0;
+
+ for (i = 27; i >= 0; i--) {
+ if (mask & BIT(i)) {
+ rate_idx = i;
+ break;
+ }
+ }
+ return rate_idx;
+}
+
+void Update_RA_Entry(struct adapter *padapter, u32 mac_id)
+{
+ rtw_hal_update_ra_mask(padapter, mac_id, 0);
+}
+
+static void enable_rate_adaptive(struct adapter *padapter, u32 mac_id)
+{
+ Update_RA_Entry(padapter, mac_id);
+}
+
+void set_sta_rate(struct adapter *padapter, struct sta_info *psta)
+{
+ /* rate adaptive */
+ enable_rate_adaptive(padapter, psta->mac_id);
+}
+
+/* Update RRSR and Rate for USERATE */
+void update_tx_basic_rate(struct adapter *padapter, u8 wirelessmode)
+{
+ unsigned char supported_rates[NDIS_802_11_LENGTH_RATES_EX];
+ memset(supported_rates, 0, NDIS_802_11_LENGTH_RATES_EX);
+
+ if ((wirelessmode & WIRELESS_11B) && (wirelessmode == WIRELESS_11B))
+ memcpy(supported_rates, rtw_basic_rate_cck, 4);
+ else if (wirelessmode & WIRELESS_11B)
+ memcpy(supported_rates, rtw_basic_rate_mix, 7);
+ else
+ memcpy(supported_rates, rtw_basic_rate_ofdm, 3);
+
+
+ if (wirelessmode & WIRELESS_11B)
+ update_mgnt_tx_rate(padapter, IEEE80211_CCK_RATE_1MB);
+ else
+ update_mgnt_tx_rate(padapter, IEEE80211_OFDM_RATE_6MB);
+
+ rtw_hal_set_hwreg(padapter, HW_VAR_BASIC_RATE, supported_rates);
+}
+
+unsigned char check_assoc_AP(u8 *pframe, uint len)
+{
+ unsigned int i;
+ struct ndis_802_11_var_ie *pIE;
+ u8 epigram_vendor_flag;
+ u8 ralink_vendor_flag;
+ epigram_vendor_flag = 0;
+ ralink_vendor_flag = 0;
+
+ for (i = sizeof(struct ndis_802_11_fixed_ie); i < len;) {
+ pIE = (struct ndis_802_11_var_ie *)(pframe + i);
+
+ switch (pIE->ElementID) {
+ case _VENDOR_SPECIFIC_IE_:
+ if ((!memcmp(pIE->data, ARTHEROS_OUI1, 3)) ||
+ (!memcmp(pIE->data, ARTHEROS_OUI2, 3))) {
+ DBG_88E("link to Artheros AP\n");
+ return HT_IOT_PEER_ATHEROS;
+ } else if ((!memcmp(pIE->data, BROADCOM_OUI1, 3)) ||
+ (!memcmp(pIE->data, BROADCOM_OUI2, 3))) {
+ DBG_88E("link to Broadcom AP\n");
+ return HT_IOT_PEER_BROADCOM;
+ } else if (!memcmp(pIE->data, MARVELL_OUI, 3)) {
+ DBG_88E("link to Marvell AP\n");
+ return HT_IOT_PEER_MARVELL;
+ } else if (!memcmp(pIE->data, RALINK_OUI, 3)) {
+ if (!ralink_vendor_flag) {
+ ralink_vendor_flag = 1;
+ } else {
+ DBG_88E("link to Ralink AP\n");
+ return HT_IOT_PEER_RALINK;
+ }
+ } else if (!memcmp(pIE->data, CISCO_OUI, 3)) {
+ DBG_88E("link to Cisco AP\n");
+ return HT_IOT_PEER_CISCO;
+ } else if (!memcmp(pIE->data, REALTEK_OUI, 3)) {
+ DBG_88E("link to Realtek 96B\n");
+ return HT_IOT_PEER_REALTEK;
+ } else if (!memcmp(pIE->data, AIRGOCAP_OUI, 3)) {
+ DBG_88E("link to Airgo Cap\n");
+ return HT_IOT_PEER_AIRGO;
+ } else if (!memcmp(pIE->data, EPIGRAM_OUI, 3)) {
+ epigram_vendor_flag = 1;
+ if (ralink_vendor_flag) {
+ DBG_88E("link to Tenda W311R AP\n");
+ return HT_IOT_PEER_TENDA;
+ } else {
+ DBG_88E("Capture EPIGRAM_OUI\n");
+ }
+ } else {
+ break;
+ }
+
+ default:
+ break;
+ }
+ i += (pIE->Length + 2);
+ }
+
+ if (ralink_vendor_flag && !epigram_vendor_flag) {
+ DBG_88E("link to Ralink AP\n");
+ return HT_IOT_PEER_RALINK;
+ } else if (ralink_vendor_flag && epigram_vendor_flag) {
+ DBG_88E("link to Tenda W311R AP\n");
+ return HT_IOT_PEER_TENDA;
+ } else {
+ DBG_88E("link to new AP\n");
+ return HT_IOT_PEER_UNKNOWN;
+ }
+}
+
+void update_IOT_info(struct adapter *padapter)
+{
+ struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv;
+ struct mlme_ext_info *pmlmeinfo = &(pmlmeext->mlmext_info);
+
+ switch (pmlmeinfo->assoc_AP_vendor) {
+ case HT_IOT_PEER_MARVELL:
+ pmlmeinfo->turboMode_cts2self = 1;
+ pmlmeinfo->turboMode_rtsen = 0;
+ break;
+ case HT_IOT_PEER_RALINK:
+ pmlmeinfo->turboMode_cts2self = 0;
+ pmlmeinfo->turboMode_rtsen = 1;
+ /* disable high power */
+ Switch_DM_Func(padapter, (~DYNAMIC_BB_DYNAMIC_TXPWR), false);
+ break;
+ case HT_IOT_PEER_REALTEK:
+ /* rtw_write16(padapter, 0x4cc, 0xffff); */
+ /* rtw_write16(padapter, 0x546, 0x01c0); */
+ /* disable high power */
+ Switch_DM_Func(padapter, (~DYNAMIC_BB_DYNAMIC_TXPWR), false);
+ break;
+ default:
+ pmlmeinfo->turboMode_cts2self = 0;
+ pmlmeinfo->turboMode_rtsen = 1;
+ break;
+ }
+}
+
+void update_capinfo(struct adapter *Adapter, u16 updateCap)
+{
+ struct mlme_ext_priv *pmlmeext = &Adapter->mlmeextpriv;
+ struct mlme_ext_info *pmlmeinfo = &(pmlmeext->mlmext_info);
+ bool ShortPreamble;
+
+ /* Check preamble mode, 2005.01.06, by rcnjko. */
+ /* Mark to update preamble value forever, 2008.03.18 by lanhsin */
+
+ if (updateCap & cShortPreamble) { /* Short Preamble */
+ if (pmlmeinfo->preamble_mode != PREAMBLE_SHORT) { /* PREAMBLE_LONG or PREAMBLE_AUTO */
+ ShortPreamble = true;
+ pmlmeinfo->preamble_mode = PREAMBLE_SHORT;
+ rtw_hal_set_hwreg(Adapter, HW_VAR_ACK_PREAMBLE, (u8 *)&ShortPreamble);
+ }
+ } else { /* Long Preamble */
+ if (pmlmeinfo->preamble_mode != PREAMBLE_LONG) { /* PREAMBLE_SHORT or PREAMBLE_AUTO */
+ ShortPreamble = false;
+ pmlmeinfo->preamble_mode = PREAMBLE_LONG;
+ rtw_hal_set_hwreg(Adapter, HW_VAR_ACK_PREAMBLE, (u8 *)&ShortPreamble);
+ }
+ }
+
+ if (updateCap & cIBSS) {
+ /* Filen: See 802.11-2007 p.91 */
+ pmlmeinfo->slotTime = NON_SHORT_SLOT_TIME;
+ } else { /* Filen: See 802.11-2007 p.90 */
+ if (pmlmeext->cur_wireless_mode & (WIRELESS_11G | WIRELESS_11_24N)) {
+ if (updateCap & cShortSlotTime) { /* Short Slot Time */
+ if (pmlmeinfo->slotTime != SHORT_SLOT_TIME)
+ pmlmeinfo->slotTime = SHORT_SLOT_TIME;
+ } else { /* Long Slot Time */
+ if (pmlmeinfo->slotTime != NON_SHORT_SLOT_TIME)
+ pmlmeinfo->slotTime = NON_SHORT_SLOT_TIME;
+ }
+ } else if (pmlmeext->cur_wireless_mode & (WIRELESS_11A | WIRELESS_11_5N)) {
+ pmlmeinfo->slotTime = SHORT_SLOT_TIME;
+ } else {
+ /* B Mode */
+ pmlmeinfo->slotTime = NON_SHORT_SLOT_TIME;
+ }
+ }
+
+ rtw_hal_set_hwreg(Adapter, HW_VAR_SLOT_TIME, &pmlmeinfo->slotTime);
+}
+
+void update_wireless_mode(struct adapter *padapter)
+{
+ int ratelen, network_type = 0;
+ u32 SIFS_Timer;
+ struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv;
+ struct mlme_ext_info *pmlmeinfo = &(pmlmeext->mlmext_info);
+ struct wlan_bssid_ex *cur_network = &(pmlmeinfo->network);
+ unsigned char *rate = cur_network->SupportedRates;
+
+ ratelen = rtw_get_rateset_len(cur_network->SupportedRates);
+
+ if ((pmlmeinfo->HT_info_enable) && (pmlmeinfo->HT_caps_enable))
+ pmlmeinfo->HT_enable = 1;
+
+ if (pmlmeext->cur_channel > 14) {
+ if (pmlmeinfo->HT_enable)
+ network_type = WIRELESS_11_5N;
+
+ network_type |= WIRELESS_11A;
+ } else {
+ if (pmlmeinfo->HT_enable)
+ network_type = WIRELESS_11_24N;
+
+ if ((cckratesonly_included(rate, ratelen)) == true)
+ network_type |= WIRELESS_11B;
+ else if ((cckrates_included(rate, ratelen)) == true)
+ network_type |= WIRELESS_11BG;
+ else
+ network_type |= WIRELESS_11G;
+ }
+
+ pmlmeext->cur_wireless_mode = network_type & padapter->registrypriv.wireless_mode;
+
+ SIFS_Timer = 0x0a0a0808;/* 0x0808 -> for CCK, 0x0a0a -> for OFDM */
+ /* change this value if having IOT issues. */
+
+ padapter->HalFunc.SetHwRegHandler(padapter, HW_VAR_RESP_SIFS, (u8 *)&SIFS_Timer);
+
+ if (pmlmeext->cur_wireless_mode & WIRELESS_11B)
+ update_mgnt_tx_rate(padapter, IEEE80211_CCK_RATE_1MB);
+ else
+ update_mgnt_tx_rate(padapter, IEEE80211_OFDM_RATE_6MB);
+}
+
+void update_bmc_sta_support_rate(struct adapter *padapter, u32 mac_id)
+{
+ struct mlme_ext_priv *pmlmeext = &(padapter->mlmeextpriv);
+ struct mlme_ext_info *pmlmeinfo = &(pmlmeext->mlmext_info);
+
+ if (pmlmeext->cur_wireless_mode & WIRELESS_11B) {
+ /* Only B, B/G, and B/G/N AP could use CCK rate */
+ memcpy((pmlmeinfo->FW_sta_info[mac_id].SupportedRates), rtw_basic_rate_cck, 4);
+ } else {
+ memcpy((pmlmeinfo->FW_sta_info[mac_id].SupportedRates), rtw_basic_rate_ofdm, 3);
+ }
+}
+
+int update_sta_support_rate(struct adapter *padapter, u8 *pvar_ie, uint var_ie_len, int cam_idx)
+{
+ unsigned int ie_len;
+ struct ndis_802_11_var_ie *pIE;
+ int supportRateNum = 0;
+ struct mlme_ext_priv *pmlmeext = &(padapter->mlmeextpriv);
+ struct mlme_ext_info *pmlmeinfo = &(pmlmeext->mlmext_info);
+
+ pIE = (struct ndis_802_11_var_ie *)rtw_get_ie(pvar_ie, _SUPPORTEDRATES_IE_, &ie_len, var_ie_len);
+ if (pIE == NULL)
+ return _FAIL;
+ if (ie_len > NDIS_802_11_LENGTH_RATES_EX)
+ return _FAIL;
+
+ memcpy(pmlmeinfo->FW_sta_info[cam_idx].SupportedRates, pIE->data, ie_len);
+ supportRateNum = ie_len;
+
+ pIE = (struct ndis_802_11_var_ie *)rtw_get_ie(pvar_ie, _EXT_SUPPORTEDRATES_IE_, &ie_len, var_ie_len);
+ if (pIE) {
+ if (supportRateNum + ie_len > NDIS_802_11_LENGTH_RATES_EX)
+ return _FAIL;
+ memcpy((pmlmeinfo->FW_sta_info[cam_idx].SupportedRates + supportRateNum), pIE->data, ie_len);
+ }
+
+ return _SUCCESS;
+}
+
+void process_addba_req(struct adapter *padapter, u8 *paddba_req, u8 *addr)
+{
+ struct sta_info *psta;
+ u16 tid;
+ u16 param;
+ struct recv_reorder_ctrl *preorder_ctrl;
+ struct sta_priv *pstapriv = &padapter->stapriv;
+ struct ADDBA_request *preq = (struct ADDBA_request *)paddba_req;
+ struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv;
+ struct mlme_ext_info *pmlmeinfo = &(pmlmeext->mlmext_info);
+
+ psta = rtw_get_stainfo(pstapriv, addr);
+
+ if (psta) {
+ param = le16_to_cpu(preq->BA_para_set);
+ tid = (param>>2)&0x0f;
+ preorder_ctrl = &psta->recvreorder_ctrl[tid];
+ preorder_ctrl->indicate_seq = 0xffff;
+ preorder_ctrl->enable = (pmlmeinfo->bAcceptAddbaReq) ? true : false;
+ }
+}
+
+void update_TSF(struct mlme_ext_priv *pmlmeext, u8 *pframe, uint len)
+{
+ u8 *pIE;
+ __le32 *pbuf;
+
+ pIE = pframe + sizeof(struct rtw_ieee80211_hdr_3addr);
+ pbuf = (__le32 *)pIE;
+
+ pmlmeext->TSFValue = le32_to_cpu(*(pbuf+1));
+
+ pmlmeext->TSFValue = pmlmeext->TSFValue << 32;
+
+ pmlmeext->TSFValue |= le32_to_cpu(*pbuf);
+}
+
+void correct_TSF(struct adapter *padapter, struct mlme_ext_priv *pmlmeext)
+{
+ rtw_hal_set_hwreg(padapter, HW_VAR_CORRECT_TSF, NULL);
+}
+
+void beacon_timing_control(struct adapter *padapter)
+{
+ rtw_hal_bcn_related_reg_setting(padapter);
+}
diff --git a/drivers/staging/rtl8188eu/core/rtw_xmit.c b/drivers/staging/rtl8188eu/core/rtw_xmit.c
new file mode 100644
index 000000000..fda169d37
--- /dev/null
+++ b/drivers/staging/rtl8188eu/core/rtw_xmit.c
@@ -0,0 +1,2209 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2012 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ *
+ ******************************************************************************/
+#define _RTW_XMIT_C_
+
+#include <osdep_service.h>
+#include <drv_types.h>
+#include <wifi.h>
+#include <osdep_intf.h>
+#include <linux/vmalloc.h>
+
+static u8 P802_1H_OUI[P80211_OUI_LEN] = { 0x00, 0x00, 0xf8 };
+static u8 RFC1042_OUI[P80211_OUI_LEN] = { 0x00, 0x00, 0x00 };
+
+static void _init_txservq(struct tx_servq *ptxservq)
+{
+ INIT_LIST_HEAD(&ptxservq->tx_pending);
+ _rtw_init_queue(&ptxservq->sta_pending);
+ ptxservq->qcnt = 0;
+}
+
+void _rtw_init_sta_xmit_priv(struct sta_xmit_priv *psta_xmitpriv)
+{
+ memset((unsigned char *)psta_xmitpriv, 0, sizeof(struct sta_xmit_priv));
+ spin_lock_init(&psta_xmitpriv->lock);
+ _init_txservq(&psta_xmitpriv->be_q);
+ _init_txservq(&psta_xmitpriv->bk_q);
+ _init_txservq(&psta_xmitpriv->vi_q);
+ _init_txservq(&psta_xmitpriv->vo_q);
+ INIT_LIST_HEAD(&psta_xmitpriv->legacy_dz);
+ INIT_LIST_HEAD(&psta_xmitpriv->apsd);
+
+}
+
+s32 _rtw_init_xmit_priv(struct xmit_priv *pxmitpriv, struct adapter *padapter)
+{
+ int i;
+ struct xmit_buf *pxmitbuf;
+ struct xmit_frame *pxframe;
+ int res = _SUCCESS;
+ u32 max_xmit_extbuf_size = MAX_XMIT_EXTBUF_SZ;
+ u32 num_xmit_extbuf = NR_XMIT_EXTBUFF;
+
+
+ /* We don't need to memset padapter->XXX to zero, because adapter is allocated by vzalloc(). */
+
+ spin_lock_init(&pxmitpriv->lock);
+ sema_init(&pxmitpriv->xmit_sema, 0);
+ sema_init(&pxmitpriv->terminate_xmitthread_sema, 0);
+
+ /*
+ Please insert all the queue initializaiton using _rtw_init_queue below
+ */
+
+ pxmitpriv->adapter = padapter;
+
+ _rtw_init_queue(&pxmitpriv->be_pending);
+ _rtw_init_queue(&pxmitpriv->bk_pending);
+ _rtw_init_queue(&pxmitpriv->vi_pending);
+ _rtw_init_queue(&pxmitpriv->vo_pending);
+ _rtw_init_queue(&pxmitpriv->bm_pending);
+
+ _rtw_init_queue(&pxmitpriv->free_xmit_queue);
+
+ /*
+ Please allocate memory with the sz = (struct xmit_frame) * NR_XMITFRAME,
+ and initialize free_xmit_frame below.
+ Please also apply free_txobj to link_up all the xmit_frames...
+ */
+
+ pxmitpriv->pallocated_frame_buf = vzalloc(NR_XMITFRAME * sizeof(struct xmit_frame) + 4);
+
+ if (pxmitpriv->pallocated_frame_buf == NULL) {
+ pxmitpriv->pxmit_frame_buf = NULL;
+ RT_TRACE(_module_rtl871x_xmit_c_, _drv_err_, ("alloc xmit_frame fail!\n"));
+ res = _FAIL;
+ goto exit;
+ }
+ pxmitpriv->pxmit_frame_buf = (u8 *)N_BYTE_ALIGMENT((size_t)(pxmitpriv->pallocated_frame_buf), 4);
+ /* pxmitpriv->pxmit_frame_buf = pxmitpriv->pallocated_frame_buf + 4 - */
+ /* ((size_t) (pxmitpriv->pallocated_frame_buf) &3); */
+
+ pxframe = (struct xmit_frame *)pxmitpriv->pxmit_frame_buf;
+
+ for (i = 0; i < NR_XMITFRAME; i++) {
+ INIT_LIST_HEAD(&(pxframe->list));
+
+ pxframe->padapter = padapter;
+ pxframe->frame_tag = NULL_FRAMETAG;
+
+ pxframe->pkt = NULL;
+
+ pxframe->buf_addr = NULL;
+ pxframe->pxmitbuf = NULL;
+
+ list_add_tail(&(pxframe->list), &(pxmitpriv->free_xmit_queue.queue));
+
+ pxframe++;
+ }
+
+ pxmitpriv->free_xmitframe_cnt = NR_XMITFRAME;
+
+ pxmitpriv->frag_len = MAX_FRAG_THRESHOLD;
+
+ /* init xmit_buf */
+ _rtw_init_queue(&pxmitpriv->free_xmitbuf_queue);
+ _rtw_init_queue(&pxmitpriv->pending_xmitbuf_queue);
+
+ pxmitpriv->pallocated_xmitbuf = vzalloc(NR_XMITBUFF * sizeof(struct xmit_buf) + 4);
+
+ if (pxmitpriv->pallocated_xmitbuf == NULL) {
+ RT_TRACE(_module_rtl871x_xmit_c_, _drv_err_, ("alloc xmit_buf fail!\n"));
+ res = _FAIL;
+ goto exit;
+ }
+
+ pxmitpriv->pxmitbuf = (u8 *)N_BYTE_ALIGMENT((size_t)(pxmitpriv->pallocated_xmitbuf), 4);
+ /* pxmitpriv->pxmitbuf = pxmitpriv->pallocated_xmitbuf + 4 - */
+ /* ((size_t) (pxmitpriv->pallocated_xmitbuf) &3); */
+
+ pxmitbuf = (struct xmit_buf *)pxmitpriv->pxmitbuf;
+
+ for (i = 0; i < NR_XMITBUFF; i++) {
+ INIT_LIST_HEAD(&pxmitbuf->list);
+
+ pxmitbuf->priv_data = NULL;
+ pxmitbuf->padapter = padapter;
+ pxmitbuf->ext_tag = false;
+
+ /* Tx buf allocation may fail sometimes, so sleep and retry. */
+ res = rtw_os_xmit_resource_alloc(padapter, pxmitbuf, (MAX_XMITBUF_SZ + XMITBUF_ALIGN_SZ));
+ if (res == _FAIL) {
+ msleep(10);
+ res = rtw_os_xmit_resource_alloc(padapter, pxmitbuf, (MAX_XMITBUF_SZ + XMITBUF_ALIGN_SZ));
+ if (res == _FAIL) {
+ goto exit;
+ }
+ }
+
+ pxmitbuf->flags = XMIT_VO_QUEUE;
+
+ list_add_tail(&pxmitbuf->list, &(pxmitpriv->free_xmitbuf_queue.queue));
+ pxmitbuf++;
+ }
+
+ pxmitpriv->free_xmitbuf_cnt = NR_XMITBUFF;
+
+ /* Init xmit extension buff */
+ _rtw_init_queue(&pxmitpriv->free_xmit_extbuf_queue);
+
+ pxmitpriv->pallocated_xmit_extbuf = vzalloc(num_xmit_extbuf * sizeof(struct xmit_buf) + 4);
+
+ if (pxmitpriv->pallocated_xmit_extbuf == NULL) {
+ RT_TRACE(_module_rtl871x_xmit_c_, _drv_err_, ("alloc xmit_extbuf fail!\n"));
+ res = _FAIL;
+ goto exit;
+ }
+
+ pxmitpriv->pxmit_extbuf = (u8 *)N_BYTE_ALIGMENT((size_t)(pxmitpriv->pallocated_xmit_extbuf), 4);
+
+ pxmitbuf = (struct xmit_buf *)pxmitpriv->pxmit_extbuf;
+
+ for (i = 0; i < num_xmit_extbuf; i++) {
+ INIT_LIST_HEAD(&pxmitbuf->list);
+
+ pxmitbuf->priv_data = NULL;
+ pxmitbuf->padapter = padapter;
+ pxmitbuf->ext_tag = true;
+
+ res = rtw_os_xmit_resource_alloc(padapter, pxmitbuf, max_xmit_extbuf_size + XMITBUF_ALIGN_SZ);
+ if (res == _FAIL) {
+ res = _FAIL;
+ goto exit;
+ }
+
+ list_add_tail(&pxmitbuf->list, &(pxmitpriv->free_xmit_extbuf_queue.queue));
+ pxmitbuf++;
+ }
+
+ pxmitpriv->free_xmit_extbuf_cnt = num_xmit_extbuf;
+
+ rtw_alloc_hwxmits(padapter);
+ rtw_init_hwxmits(pxmitpriv->hwxmits, pxmitpriv->hwxmit_entry);
+
+ for (i = 0; i < 4; i++)
+ pxmitpriv->wmm_para_seq[i] = i;
+
+ pxmitpriv->txirp_cnt = 1;
+
+ sema_init(&(pxmitpriv->tx_retevt), 0);
+
+ /* per AC pending irp */
+ pxmitpriv->beq_cnt = 0;
+ pxmitpriv->bkq_cnt = 0;
+ pxmitpriv->viq_cnt = 0;
+ pxmitpriv->voq_cnt = 0;
+
+ pxmitpriv->ack_tx = false;
+ mutex_init(&pxmitpriv->ack_tx_mutex);
+ rtw_sctx_init(&pxmitpriv->ack_tx_ops, 0);
+
+ rtw_hal_init_xmit_priv(padapter);
+
+exit:
+
+
+ return res;
+}
+
+void _rtw_free_xmit_priv(struct xmit_priv *pxmitpriv)
+{
+ int i;
+ struct adapter *padapter = pxmitpriv->adapter;
+ struct xmit_frame *pxmitframe = (struct xmit_frame *)pxmitpriv->pxmit_frame_buf;
+ struct xmit_buf *pxmitbuf = (struct xmit_buf *)pxmitpriv->pxmitbuf;
+ u32 max_xmit_extbuf_size = MAX_XMIT_EXTBUF_SZ;
+ u32 num_xmit_extbuf = NR_XMIT_EXTBUFF;
+
+ if (pxmitpriv->pxmit_frame_buf == NULL)
+ return;
+
+ for (i = 0; i < NR_XMITFRAME; i++) {
+ rtw_os_xmit_complete(padapter, pxmitframe);
+
+ pxmitframe++;
+ }
+
+ for (i = 0; i < NR_XMITBUFF; i++) {
+ rtw_os_xmit_resource_free(padapter, pxmitbuf, (MAX_XMITBUF_SZ + XMITBUF_ALIGN_SZ));
+ pxmitbuf++;
+ }
+
+ if (pxmitpriv->pallocated_frame_buf)
+ vfree(pxmitpriv->pallocated_frame_buf);
+
+ if (pxmitpriv->pallocated_xmitbuf)
+ vfree(pxmitpriv->pallocated_xmitbuf);
+
+ /* free xmit extension buff */
+ pxmitbuf = (struct xmit_buf *)pxmitpriv->pxmit_extbuf;
+ for (i = 0; i < num_xmit_extbuf; i++) {
+ rtw_os_xmit_resource_free(padapter, pxmitbuf, (max_xmit_extbuf_size + XMITBUF_ALIGN_SZ));
+ pxmitbuf++;
+ }
+
+ if (pxmitpriv->pallocated_xmit_extbuf) {
+ vfree(pxmitpriv->pallocated_xmit_extbuf);
+ }
+
+ rtw_free_hwxmits(padapter);
+
+ mutex_destroy(&pxmitpriv->ack_tx_mutex);
+}
+
+static void update_attrib_vcs_info(struct adapter *padapter, struct xmit_frame *pxmitframe)
+{
+ u32 sz;
+ struct pkt_attrib *pattrib = &pxmitframe->attrib;
+ struct sta_info *psta = pattrib->psta;
+ struct mlme_ext_priv *pmlmeext = &(padapter->mlmeextpriv);
+ struct mlme_ext_info *pmlmeinfo = &(pmlmeext->mlmext_info);
+
+ if (pattrib->nr_frags != 1)
+ sz = padapter->xmitpriv.frag_len;
+ else /* no frag */
+ sz = pattrib->last_txcmdsz;
+
+ /* (1) RTS_Threshold is compared to the MPDU, not MSDU. */
+ /* (2) If there are more than one frag in this MSDU, only the first frag uses protection frame. */
+ /* Other fragments are protected by previous fragment. */
+ /* So we only need to check the length of first fragment. */
+ if (pmlmeext->cur_wireless_mode < WIRELESS_11_24N || padapter->registrypriv.wifi_spec) {
+ if (sz > padapter->registrypriv.rts_thresh) {
+ pattrib->vcs_mode = RTS_CTS;
+ } else {
+ if (psta->rtsen)
+ pattrib->vcs_mode = RTS_CTS;
+ else if (psta->cts2self)
+ pattrib->vcs_mode = CTS_TO_SELF;
+ else
+ pattrib->vcs_mode = NONE_VCS;
+ }
+ } else {
+ while (true) {
+ /* IOT action */
+ if ((pmlmeinfo->assoc_AP_vendor == HT_IOT_PEER_ATHEROS) && pattrib->ampdu_en &&
+ (padapter->securitypriv.dot11PrivacyAlgrthm == _AES_)) {
+ pattrib->vcs_mode = CTS_TO_SELF;
+ break;
+ }
+
+ /* check ERP protection */
+ if (psta->rtsen || psta->cts2self) {
+ if (psta->rtsen)
+ pattrib->vcs_mode = RTS_CTS;
+ else if (psta->cts2self)
+ pattrib->vcs_mode = CTS_TO_SELF;
+
+ break;
+ }
+
+ /* check HT op mode */
+ if (pattrib->ht_en) {
+ u8 htopmode = pmlmeinfo->HT_protection;
+ if ((pmlmeext->cur_bwmode && (htopmode == 2 || htopmode == 3)) ||
+ (!pmlmeext->cur_bwmode && htopmode == 3)) {
+ pattrib->vcs_mode = RTS_CTS;
+ break;
+ }
+ }
+
+ /* check rts */
+ if (sz > padapter->registrypriv.rts_thresh) {
+ pattrib->vcs_mode = RTS_CTS;
+ break;
+ }
+
+ /* to do list: check MIMO power save condition. */
+
+ /* check AMPDU aggregation for TXOP */
+ if (pattrib->ampdu_en) {
+ pattrib->vcs_mode = RTS_CTS;
+ break;
+ }
+
+ pattrib->vcs_mode = NONE_VCS;
+ break;
+ }
+ }
+}
+
+static void update_attrib_phy_info(struct pkt_attrib *pattrib, struct sta_info *psta)
+{
+ /*if (psta->rtsen)
+ pattrib->vcs_mode = RTS_CTS;
+ else if (psta->cts2self)
+ pattrib->vcs_mode = CTS_TO_SELF;
+ else
+ pattrib->vcs_mode = NONE_VCS;*/
+
+ pattrib->mdata = 0;
+ pattrib->eosp = 0;
+ pattrib->triggered = 0;
+
+ /* qos_en, ht_en, init rate, , bw, ch_offset, sgi */
+ pattrib->qos_en = psta->qos_option;
+
+ pattrib->raid = psta->raid;
+ pattrib->ht_en = psta->htpriv.ht_option;
+ pattrib->bwmode = psta->htpriv.bwmode;
+ pattrib->ch_offset = psta->htpriv.ch_offset;
+ pattrib->sgi = psta->htpriv.sgi;
+ pattrib->ampdu_en = false;
+ pattrib->retry_ctrl = false;
+}
+
+u8 qos_acm(u8 acm_mask, u8 priority)
+{
+ u8 change_priority = priority;
+
+ switch (priority) {
+ case 0:
+ case 3:
+ if (acm_mask & BIT(1))
+ change_priority = 1;
+ break;
+ case 1:
+ case 2:
+ break;
+ case 4:
+ case 5:
+ if (acm_mask & BIT(2))
+ change_priority = 0;
+ break;
+ case 6:
+ case 7:
+ if (acm_mask & BIT(3))
+ change_priority = 5;
+ break;
+ default:
+ DBG_88E("qos_acm(): invalid pattrib->priority: %d!!!\n", priority);
+ break;
+ }
+
+ return change_priority;
+}
+
+static void set_qos(struct pkt_file *ppktfile, struct pkt_attrib *pattrib)
+{
+ struct ethhdr etherhdr;
+ struct iphdr ip_hdr;
+ s32 user_prio = 0;
+
+ _rtw_open_pktfile(ppktfile->pkt, ppktfile);
+ _rtw_pktfile_read(ppktfile, (unsigned char *)&etherhdr, ETH_HLEN);
+
+ /* get user_prio from IP hdr */
+ if (pattrib->ether_type == 0x0800) {
+ _rtw_pktfile_read(ppktfile, (u8 *)&ip_hdr, sizeof(ip_hdr));
+/* user_prio = (ntohs(ip_hdr.tos) >> 5) & 0x3; */
+ user_prio = ip_hdr.tos >> 5;
+ } else if (pattrib->ether_type == 0x888e) {
+ /* "When priority processing of data frames is supported, */
+ /* a STA's SME should send EAPOL-Key frames at the highest priority." */
+ user_prio = 7;
+ }
+
+ pattrib->priority = user_prio;
+ pattrib->hdrlen = WLAN_HDR_A3_QOS_LEN;
+ pattrib->subtype = WIFI_QOS_DATA_TYPE;
+}
+
+static s32 update_attrib(struct adapter *padapter, struct sk_buff *pkt, struct pkt_attrib *pattrib)
+{
+ struct pkt_file pktfile;
+ struct sta_info *psta = NULL;
+ struct ethhdr etherhdr;
+
+ int bmcast;
+ struct sta_priv *pstapriv = &padapter->stapriv;
+ struct security_priv *psecuritypriv = &padapter->securitypriv;
+ struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
+ struct qos_priv *pqospriv = &pmlmepriv->qospriv;
+ int res = _SUCCESS;
+
+
+ _rtw_open_pktfile(pkt, &pktfile);
+ _rtw_pktfile_read(&pktfile, (u8 *)&etherhdr, ETH_HLEN);
+
+ pattrib->ether_type = ntohs(etherhdr.h_proto);
+
+ memcpy(pattrib->dst, &etherhdr.h_dest, ETH_ALEN);
+ memcpy(pattrib->src, &etherhdr.h_source, ETH_ALEN);
+
+ pattrib->pctrl = 0;
+
+ if (check_fwstate(pmlmepriv, WIFI_ADHOC_STATE) ||
+ check_fwstate(pmlmepriv, WIFI_ADHOC_MASTER_STATE)) {
+ memcpy(pattrib->ra, pattrib->dst, ETH_ALEN);
+ memcpy(pattrib->ta, pattrib->src, ETH_ALEN);
+ } else if (check_fwstate(pmlmepriv, WIFI_STATION_STATE)) {
+ memcpy(pattrib->ra, get_bssid(pmlmepriv), ETH_ALEN);
+ memcpy(pattrib->ta, pattrib->src, ETH_ALEN);
+ } else if (check_fwstate(pmlmepriv, WIFI_AP_STATE)) {
+ memcpy(pattrib->ra, pattrib->dst, ETH_ALEN);
+ memcpy(pattrib->ta, get_bssid(pmlmepriv), ETH_ALEN);
+ }
+
+ pattrib->pktlen = pktfile.pkt_len;
+
+ if (ETH_P_IP == pattrib->ether_type) {
+ /* The following is for DHCP and ARP packet, we use cck1M to tx these packets and let LPS awake some time */
+ /* to prevent DHCP protocol fail */
+ u8 tmp[24];
+ _rtw_pktfile_read(&pktfile, &tmp[0], 24);
+ pattrib->dhcp_pkt = 0;
+ if (pktfile.pkt_len > 282) {/* MINIMUM_DHCP_PACKET_SIZE) { */
+ if (ETH_P_IP == pattrib->ether_type) {/* IP header */
+ if (((tmp[21] == 68) && (tmp[23] == 67)) ||
+ ((tmp[21] == 67) && (tmp[23] == 68))) {
+ /* 68 : UDP BOOTP client */
+ /* 67 : UDP BOOTP server */
+ RT_TRACE(_module_rtl871x_xmit_c_, _drv_err_, ("====================== update_attrib: get DHCP Packet\n"));
+ /* Use low rate to send DHCP packet. */
+ pattrib->dhcp_pkt = 1;
+ }
+ }
+ }
+ } else if (0x888e == pattrib->ether_type) {
+ DBG_88E_LEVEL(_drv_info_, "send eapol packet\n");
+ }
+
+ if ((pattrib->ether_type == 0x888e) || (pattrib->dhcp_pkt == 1))
+ rtw_set_scan_deny(padapter, 3000);
+
+ /* If EAPOL , ARP , OR DHCP packet, driver must be in active mode. */
+ if ((pattrib->ether_type == 0x0806) || (pattrib->ether_type == 0x888e) || (pattrib->dhcp_pkt == 1))
+ rtw_lps_ctrl_wk_cmd(padapter, LPS_CTRL_SPECIAL_PACKET, 1);
+
+ bmcast = IS_MCAST(pattrib->ra);
+
+ /* get sta_info */
+ if (bmcast) {
+ psta = rtw_get_bcmc_stainfo(padapter);
+ } else {
+ psta = rtw_get_stainfo(pstapriv, pattrib->ra);
+ if (psta == NULL) { /* if we cannot get psta => drrp the pkt */
+ RT_TRACE(_module_rtl871x_xmit_c_, _drv_alert_, ("\nupdate_attrib => get sta_info fail, ra: %pM\n", (pattrib->ra)));
+ res = _FAIL;
+ goto exit;
+ } else if ((check_fwstate(pmlmepriv, WIFI_AP_STATE) == true) && (!(psta->state & _FW_LINKED))) {
+ res = _FAIL;
+ goto exit;
+ }
+ }
+
+ if (psta) {
+ pattrib->mac_id = psta->mac_id;
+ /* DBG_88E("%s ==> mac_id(%d)\n", __func__, pattrib->mac_id); */
+ pattrib->psta = psta;
+ } else {
+ /* if we cannot get psta => drop the pkt */
+ RT_TRACE(_module_rtl871x_xmit_c_, _drv_alert_, ("\nupdate_attrib => get sta_info fail, ra:%pM\n", (pattrib->ra)));
+ res = _FAIL;
+ goto exit;
+ }
+
+ pattrib->ack_policy = 0;
+ /* get ether_hdr_len */
+ pattrib->pkt_hdrlen = ETH_HLEN;/* pattrib->ether_type == 0x8100) ? (14 + 4): 14; vlan tag */
+
+ pattrib->hdrlen = WLAN_HDR_A3_LEN;
+ pattrib->subtype = WIFI_DATA_TYPE;
+ pattrib->priority = 0;
+
+ if (check_fwstate(pmlmepriv, WIFI_AP_STATE|WIFI_ADHOC_STATE|WIFI_ADHOC_MASTER_STATE)) {
+ if (psta->qos_option)
+ set_qos(&pktfile, pattrib);
+ } else {
+ if (pqospriv->qos_option) {
+ set_qos(&pktfile, pattrib);
+
+ if (pmlmepriv->acm_mask != 0)
+ pattrib->priority = qos_acm(pmlmepriv->acm_mask, pattrib->priority);
+ }
+ }
+
+ if (psta->ieee8021x_blocked) {
+ RT_TRACE(_module_rtl871x_xmit_c_, _drv_err_, ("\n psta->ieee8021x_blocked == true\n"));
+
+ pattrib->encrypt = 0;
+
+ if ((pattrib->ether_type != 0x888e) && !check_fwstate(pmlmepriv, WIFI_MP_STATE)) {
+ RT_TRACE(_module_rtl871x_xmit_c_, _drv_err_, ("\npsta->ieee8021x_blocked == true, pattrib->ether_type(%.4x) != 0x888e\n", pattrib->ether_type));
+ res = _FAIL;
+ goto exit;
+ }
+ } else {
+ GET_ENCRY_ALGO(psecuritypriv, psta, pattrib->encrypt, bmcast);
+
+ switch (psecuritypriv->dot11AuthAlgrthm) {
+ case dot11AuthAlgrthm_Open:
+ case dot11AuthAlgrthm_Shared:
+ case dot11AuthAlgrthm_Auto:
+ pattrib->key_idx = (u8)psecuritypriv->dot11PrivacyKeyIndex;
+ break;
+ case dot11AuthAlgrthm_8021X:
+ if (bmcast)
+ pattrib->key_idx = (u8)psecuritypriv->dot118021XGrpKeyid;
+ else
+ pattrib->key_idx = 0;
+ break;
+ default:
+ pattrib->key_idx = 0;
+ break;
+ }
+ }
+
+ switch (pattrib->encrypt) {
+ case _WEP40_:
+ case _WEP104_:
+ pattrib->iv_len = 4;
+ pattrib->icv_len = 4;
+ break;
+ case _TKIP_:
+ pattrib->iv_len = 8;
+ pattrib->icv_len = 4;
+
+ if (padapter->securitypriv.busetkipkey == _FAIL) {
+ RT_TRACE(_module_rtl871x_xmit_c_, _drv_err_,
+ ("\npadapter->securitypriv.busetkipkey(%d) == _FAIL drop packet\n",
+ padapter->securitypriv.busetkipkey));
+ res = _FAIL;
+ goto exit;
+ }
+ break;
+ case _AES_:
+ RT_TRACE(_module_rtl871x_xmit_c_, _drv_info_, ("pattrib->encrypt=%d (_AES_)\n", pattrib->encrypt));
+ pattrib->iv_len = 8;
+ pattrib->icv_len = 8;
+ break;
+ default:
+ pattrib->iv_len = 0;
+ pattrib->icv_len = 0;
+ break;
+ }
+
+ RT_TRACE(_module_rtl871x_xmit_c_, _drv_info_,
+ ("update_attrib: encrypt=%d securitypriv.sw_encrypt=%d\n",
+ pattrib->encrypt, padapter->securitypriv.sw_encrypt));
+
+ if (pattrib->encrypt &&
+ (padapter->securitypriv.sw_encrypt || !psecuritypriv->hw_decrypted)) {
+ pattrib->bswenc = true;
+ RT_TRACE(_module_rtl871x_xmit_c_, _drv_err_,
+ ("update_attrib: encrypt=%d securitypriv.hw_decrypted=%d bswenc = true\n",
+ pattrib->encrypt, padapter->securitypriv.sw_encrypt));
+ } else {
+ pattrib->bswenc = false;
+ RT_TRACE(_module_rtl871x_xmit_c_, _drv_info_, ("update_attrib: bswenc = false\n"));
+ }
+
+ update_attrib_phy_info(pattrib, psta);
+
+exit:
+
+
+ return res;
+}
+
+static s32 xmitframe_addmic(struct adapter *padapter, struct xmit_frame *pxmitframe)
+{
+ int curfragnum, length;
+ u8 *pframe, *payload, mic[8];
+ struct mic_data micdata;
+ struct sta_info *stainfo;
+ struct pkt_attrib *pattrib = &pxmitframe->attrib;
+ struct security_priv *psecuritypriv = &padapter->securitypriv;
+ struct xmit_priv *pxmitpriv = &padapter->xmitpriv;
+ u8 priority[4] = {0x0, 0x0, 0x0, 0x0};
+ u8 hw_hdr_offset = 0;
+ int bmcst = IS_MCAST(pattrib->ra);
+
+ if (pattrib->psta)
+ stainfo = pattrib->psta;
+ else
+ stainfo = rtw_get_stainfo(&padapter->stapriv , &pattrib->ra[0]);
+
+
+ hw_hdr_offset = TXDESC_SIZE + (pxmitframe->pkt_offset * PACKET_OFFSET_SZ);
+
+ if (pattrib->encrypt == _TKIP_) {/* if (psecuritypriv->dot11PrivacyAlgrthm == _TKIP_PRIVACY_) */
+ /* encode mic code */
+ if (stainfo != NULL) {
+ u8 null_key[16] = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0};
+
+ pframe = pxmitframe->buf_addr + hw_hdr_offset;
+
+ if (bmcst) {
+ if (!memcmp(psecuritypriv->dot118021XGrptxmickey[psecuritypriv->dot118021XGrpKeyid].skey, null_key, 16))
+ return _FAIL;
+ /* start to calculate the mic code */
+ rtw_secmicsetkey(&micdata, psecuritypriv->dot118021XGrptxmickey[psecuritypriv->dot118021XGrpKeyid].skey);
+ } else {
+ if (!memcmp(&stainfo->dot11tkiptxmickey.skey[0], null_key, 16)) {
+ /* DbgPrint("\nxmitframe_addmic:stainfo->dot11tkiptxmickey == 0\n"); */
+ /* msleep(10); */
+ return _FAIL;
+ }
+ /* start to calculate the mic code */
+ rtw_secmicsetkey(&micdata, &stainfo->dot11tkiptxmickey.skey[0]);
+ }
+
+ if (pframe[1]&1) { /* ToDS == 1 */
+ rtw_secmicappend(&micdata, &pframe[16], 6); /* DA */
+ if (pframe[1]&2) /* From Ds == 1 */
+ rtw_secmicappend(&micdata, &pframe[24], 6);
+ else
+ rtw_secmicappend(&micdata, &pframe[10], 6);
+ } else { /* ToDS == 0 */
+ rtw_secmicappend(&micdata, &pframe[4], 6); /* DA */
+ if (pframe[1]&2) /* From Ds == 1 */
+ rtw_secmicappend(&micdata, &pframe[16], 6);
+ else
+ rtw_secmicappend(&micdata, &pframe[10], 6);
+ }
+
+ if (pattrib->qos_en)
+ priority[0] = (u8)pxmitframe->attrib.priority;
+
+ rtw_secmicappend(&micdata, &priority[0], 4);
+
+ payload = pframe;
+
+ for (curfragnum = 0; curfragnum < pattrib->nr_frags; curfragnum++) {
+ payload = (u8 *)round_up((size_t)(payload), 4);
+ RT_TRACE(_module_rtl871x_xmit_c_, _drv_err_,
+ ("=== curfragnum=%d, pframe = 0x%.2x, 0x%.2x, 0x%.2x, 0x%.2x, 0x%.2x, 0x%.2x, 0x%.2x, 0x%.2x,!!!\n",
+ curfragnum, *payload, *(payload+1),
+ *(payload+2), *(payload+3),
+ *(payload+4), *(payload+5),
+ *(payload+6), *(payload+7)));
+
+ payload = payload+pattrib->hdrlen+pattrib->iv_len;
+ RT_TRACE(_module_rtl871x_xmit_c_, _drv_err_,
+ ("curfragnum=%d pattrib->hdrlen=%d pattrib->iv_len=%d",
+ curfragnum, pattrib->hdrlen, pattrib->iv_len));
+ if ((curfragnum+1) == pattrib->nr_frags) {
+ length = pattrib->last_txcmdsz-pattrib->hdrlen-pattrib->iv_len-((pattrib->bswenc) ? pattrib->icv_len : 0);
+ rtw_secmicappend(&micdata, payload, length);
+ payload = payload+length;
+ } else {
+ length = pxmitpriv->frag_len-pattrib->hdrlen-pattrib->iv_len-((pattrib->bswenc) ? pattrib->icv_len : 0);
+ rtw_secmicappend(&micdata, payload, length);
+ payload = payload+length+pattrib->icv_len;
+ RT_TRACE(_module_rtl871x_xmit_c_, _drv_err_, ("curfragnum=%d length=%d pattrib->icv_len=%d", curfragnum, length, pattrib->icv_len));
+ }
+ }
+ rtw_secgetmic(&micdata, &(mic[0]));
+ RT_TRACE(_module_rtl871x_xmit_c_, _drv_err_, ("xmitframe_addmic: before add mic code!!!\n"));
+ RT_TRACE(_module_rtl871x_xmit_c_, _drv_err_, ("xmitframe_addmic: pattrib->last_txcmdsz=%d!!!\n", pattrib->last_txcmdsz));
+ RT_TRACE(_module_rtl871x_xmit_c_, _drv_err_, ("xmitframe_addmic: mic[0]=0x%.2x , mic[1]=0x%.2x , mic[2]= 0x%.2x, mic[3]=0x%.2x\n\
+ mic[4]= 0x%.2x , mic[5]= 0x%.2x , mic[6]= 0x%.2x , mic[7]= 0x%.2x !!!!\n",
+ mic[0], mic[1], mic[2], mic[3], mic[4], mic[5], mic[6], mic[7]));
+ /* add mic code and add the mic code length in last_txcmdsz */
+
+ memcpy(payload, &(mic[0]), 8);
+ pattrib->last_txcmdsz += 8;
+
+ RT_TRACE(_module_rtl871x_xmit_c_, _drv_info_, ("\n ======== last pkt ========\n"));
+ payload = payload-pattrib->last_txcmdsz+8;
+ for (curfragnum = 0; curfragnum < pattrib->last_txcmdsz; curfragnum = curfragnum+8)
+ RT_TRACE(_module_rtl871x_xmit_c_, _drv_info_,
+ (" %.2x, %.2x, %.2x, %.2x, %.2x, %.2x, %.2x, %.2x ",
+ *(payload+curfragnum), *(payload+curfragnum+1),
+ *(payload+curfragnum+2), *(payload+curfragnum+3),
+ *(payload+curfragnum+4), *(payload+curfragnum+5),
+ *(payload+curfragnum+6), *(payload+curfragnum+7)));
+ } else {
+ RT_TRACE(_module_rtl871x_xmit_c_, _drv_err_, ("xmitframe_addmic: rtw_get_stainfo==NULL!!!\n"));
+ }
+ }
+
+
+ return _SUCCESS;
+}
+
+static s32 xmitframe_swencrypt(struct adapter *padapter, struct xmit_frame *pxmitframe)
+{
+ struct pkt_attrib *pattrib = &pxmitframe->attrib;
+
+
+ if (pattrib->bswenc) {
+ RT_TRACE(_module_rtl871x_xmit_c_, _drv_alert_, ("### xmitframe_swencrypt\n"));
+ switch (pattrib->encrypt) {
+ case _WEP40_:
+ case _WEP104_:
+ rtw_wep_encrypt(padapter, (u8 *)pxmitframe);
+ break;
+ case _TKIP_:
+ rtw_tkip_encrypt(padapter, (u8 *)pxmitframe);
+ break;
+ case _AES_:
+ rtw_aes_encrypt(padapter, (u8 *)pxmitframe);
+ break;
+ default:
+ break;
+ }
+ } else {
+ RT_TRACE(_module_rtl871x_xmit_c_, _drv_notice_, ("### xmitframe_hwencrypt\n"));
+ }
+
+
+ return _SUCCESS;
+}
+
+s32 rtw_make_wlanhdr(struct adapter *padapter, u8 *hdr, struct pkt_attrib *pattrib)
+{
+ u16 *qc;
+
+ struct rtw_ieee80211_hdr *pwlanhdr = (struct rtw_ieee80211_hdr *)hdr;
+ struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
+ struct qos_priv *pqospriv = &pmlmepriv->qospriv;
+ u8 qos_option = false;
+
+ int res = _SUCCESS;
+ __le16 *fctrl = &pwlanhdr->frame_ctl;
+
+ struct sta_info *psta;
+
+ int bmcst = IS_MCAST(pattrib->ra);
+
+
+ if (pattrib->psta) {
+ psta = pattrib->psta;
+ } else {
+ if (bmcst) {
+ psta = rtw_get_bcmc_stainfo(padapter);
+ } else {
+ psta = rtw_get_stainfo(&padapter->stapriv, pattrib->ra);
+ }
+ }
+
+ memset(hdr, 0, WLANHDR_OFFSET);
+
+ SetFrameSubType(fctrl, pattrib->subtype);
+
+ if (pattrib->subtype & WIFI_DATA_TYPE) {
+ if ((check_fwstate(pmlmepriv, WIFI_STATION_STATE) == true)) {
+ /* to_ds = 1, fr_ds = 0; */
+ /* Data transfer to AP */
+ SetToDs(fctrl);
+ memcpy(pwlanhdr->addr1, get_bssid(pmlmepriv), ETH_ALEN);
+ memcpy(pwlanhdr->addr2, pattrib->src, ETH_ALEN);
+ memcpy(pwlanhdr->addr3, pattrib->dst, ETH_ALEN);
+
+ if (pqospriv->qos_option)
+ qos_option = true;
+ } else if (check_fwstate(pmlmepriv, WIFI_AP_STATE)) {
+ /* to_ds = 0, fr_ds = 1; */
+ SetFrDs(fctrl);
+ memcpy(pwlanhdr->addr1, pattrib->dst, ETH_ALEN);
+ memcpy(pwlanhdr->addr2, get_bssid(pmlmepriv), ETH_ALEN);
+ memcpy(pwlanhdr->addr3, pattrib->src, ETH_ALEN);
+
+ if (psta->qos_option)
+ qos_option = true;
+ } else if (check_fwstate(pmlmepriv, WIFI_ADHOC_STATE) ||
+ check_fwstate(pmlmepriv, WIFI_ADHOC_MASTER_STATE)) {
+ memcpy(pwlanhdr->addr1, pattrib->dst, ETH_ALEN);
+ memcpy(pwlanhdr->addr2, pattrib->src, ETH_ALEN);
+ memcpy(pwlanhdr->addr3, get_bssid(pmlmepriv), ETH_ALEN);
+
+ if (psta->qos_option)
+ qos_option = true;
+ } else {
+ RT_TRACE(_module_rtl871x_xmit_c_, _drv_err_, ("fw_state:%x is not allowed to xmit frame\n", get_fwstate(pmlmepriv)));
+ res = _FAIL;
+ goto exit;
+ }
+
+ if (pattrib->mdata)
+ SetMData(fctrl);
+
+ if (pattrib->encrypt)
+ SetPrivacy(fctrl);
+
+ if (qos_option) {
+ qc = (unsigned short *)(hdr + pattrib->hdrlen - 2);
+
+ if (pattrib->priority)
+ SetPriority(qc, pattrib->priority);
+
+ SetEOSP(qc, pattrib->eosp);
+
+ SetAckpolicy(qc, pattrib->ack_policy);
+ }
+
+ /* TODO: fill HT Control Field */
+
+ /* Update Seq Num will be handled by f/w */
+ if (psta) {
+ psta->sta_xmitpriv.txseq_tid[pattrib->priority]++;
+ psta->sta_xmitpriv.txseq_tid[pattrib->priority] &= 0xFFF;
+
+ pattrib->seqnum = psta->sta_xmitpriv.txseq_tid[pattrib->priority];
+
+ SetSeqNum(hdr, pattrib->seqnum);
+
+ /* check if enable ampdu */
+ if (pattrib->ht_en && psta->htpriv.ampdu_enable) {
+ if (psta->htpriv.agg_enable_bitmap & BIT(pattrib->priority))
+ pattrib->ampdu_en = true;
+ }
+
+ /* re-check if enable ampdu by BA_starting_seqctrl */
+ if (pattrib->ampdu_en) {
+ u16 tx_seq;
+
+ tx_seq = psta->BA_starting_seqctrl[pattrib->priority & 0x0f];
+
+ /* check BA_starting_seqctrl */
+ if (SN_LESS(pattrib->seqnum, tx_seq)) {
+ pattrib->ampdu_en = false;/* AGG BK */
+ } else if (SN_EQUAL(pattrib->seqnum, tx_seq)) {
+ psta->BA_starting_seqctrl[pattrib->priority & 0x0f] = (tx_seq+1)&0xfff;
+
+ pattrib->ampdu_en = true;/* AGG EN */
+ } else {
+ psta->BA_starting_seqctrl[pattrib->priority & 0x0f] = (pattrib->seqnum+1)&0xfff;
+ pattrib->ampdu_en = true;/* AGG EN */
+ }
+ }
+ }
+ }
+exit:
+
+ return res;
+}
+
+s32 rtw_txframes_pending(struct adapter *padapter)
+{
+ struct xmit_priv *pxmitpriv = &padapter->xmitpriv;
+
+ return (!list_empty(&pxmitpriv->be_pending.queue) ||
+ !list_empty(&pxmitpriv->bk_pending.queue) ||
+ !list_empty(&pxmitpriv->vi_pending.queue) ||
+ !list_empty(&pxmitpriv->vo_pending.queue));
+}
+
+s32 rtw_txframes_sta_ac_pending(struct adapter *padapter, struct pkt_attrib *pattrib)
+{
+ struct sta_info *psta;
+ struct tx_servq *ptxservq;
+ int priority = pattrib->priority;
+
+ psta = pattrib->psta;
+
+ switch (priority) {
+ case 1:
+ case 2:
+ ptxservq = &(psta->sta_xmitpriv.bk_q);
+ break;
+ case 4:
+ case 5:
+ ptxservq = &(psta->sta_xmitpriv.vi_q);
+ break;
+ case 6:
+ case 7:
+ ptxservq = &(psta->sta_xmitpriv.vo_q);
+ break;
+ case 0:
+ case 3:
+ default:
+ ptxservq = &(psta->sta_xmitpriv.be_q);
+ break;
+ }
+
+ return ptxservq->qcnt;
+}
+
+/*
+ * Calculate wlan 802.11 packet MAX size from pkt_attrib
+ * This function doesn't consider fragment case
+ */
+u32 rtw_calculate_wlan_pkt_size_by_attribue(struct pkt_attrib *pattrib)
+{
+ u32 len = 0;
+
+ len = pattrib->hdrlen + pattrib->iv_len; /* WLAN Header and IV */
+ len += SNAP_SIZE + sizeof(u16); /* LLC */
+ len += pattrib->pktlen;
+ if (pattrib->encrypt == _TKIP_)
+ len += 8; /* MIC */
+ len += ((pattrib->bswenc) ? pattrib->icv_len : 0); /* ICV */
+
+ return len;
+}
+
+/*
+
+This sub-routine will perform all the following:
+
+1. remove 802.3 header.
+2. create wlan_header, based on the info in pxmitframe
+3. append sta's iv/ext-iv
+4. append LLC
+5. move frag chunk from pframe to pxmitframe->mem
+6. apply sw-encrypt, if necessary.
+
+*/
+s32 rtw_xmitframe_coalesce(struct adapter *padapter, struct sk_buff *pkt, struct xmit_frame *pxmitframe)
+{
+ struct pkt_file pktfile;
+ s32 frg_inx, frg_len, mpdu_len, llc_sz, mem_sz;
+ size_t addr;
+ u8 *pframe, *mem_start;
+ u8 hw_hdr_offset;
+ struct sta_info *psta;
+ struct xmit_priv *pxmitpriv = &padapter->xmitpriv;
+ struct pkt_attrib *pattrib = &pxmitframe->attrib;
+ u8 *pbuf_start;
+ s32 bmcst = IS_MCAST(pattrib->ra);
+ s32 res = _SUCCESS;
+
+
+ psta = rtw_get_stainfo(&padapter->stapriv, pattrib->ra);
+
+ if (psta == NULL)
+ return _FAIL;
+
+ if (pxmitframe->buf_addr == NULL) {
+ DBG_88E("==> %s buf_addr == NULL\n", __func__);
+ return _FAIL;
+ }
+
+ pbuf_start = pxmitframe->buf_addr;
+
+ hw_hdr_offset = TXDESC_SIZE + (pxmitframe->pkt_offset * PACKET_OFFSET_SZ);
+
+ mem_start = pbuf_start + hw_hdr_offset;
+
+ if (rtw_make_wlanhdr(padapter, mem_start, pattrib) == _FAIL) {
+ RT_TRACE(_module_rtl871x_xmit_c_, _drv_err_, ("rtw_xmitframe_coalesce: rtw_make_wlanhdr fail; drop pkt\n"));
+ DBG_88E("rtw_xmitframe_coalesce: rtw_make_wlanhdr fail; drop pkt\n");
+ res = _FAIL;
+ goto exit;
+ }
+
+ _rtw_open_pktfile(pkt, &pktfile);
+ _rtw_pktfile_read(&pktfile, NULL, pattrib->pkt_hdrlen);
+
+ frg_inx = 0;
+ frg_len = pxmitpriv->frag_len - 4;/* 2346-4 = 2342 */
+
+ while (1) {
+ llc_sz = 0;
+
+ mpdu_len = frg_len;
+
+ pframe = mem_start;
+
+ SetMFrag(mem_start);
+
+ pframe += pattrib->hdrlen;
+ mpdu_len -= pattrib->hdrlen;
+
+ /* adding icv, if necessary... */
+ if (pattrib->iv_len) {
+ switch (pattrib->encrypt) {
+ case _WEP40_:
+ case _WEP104_:
+ WEP_IV(pattrib->iv, psta->dot11txpn, pattrib->key_idx);
+ break;
+ case _TKIP_:
+ if (bmcst)
+ TKIP_IV(pattrib->iv, psta->dot11txpn, pattrib->key_idx);
+ else
+ TKIP_IV(pattrib->iv, psta->dot11txpn, 0);
+ break;
+ case _AES_:
+ if (bmcst)
+ AES_IV(pattrib->iv, psta->dot11txpn, pattrib->key_idx);
+ else
+ AES_IV(pattrib->iv, psta->dot11txpn, 0);
+ break;
+ }
+
+ memcpy(pframe, pattrib->iv, pattrib->iv_len);
+
+ RT_TRACE(_module_rtl871x_xmit_c_, _drv_notice_,
+ ("rtw_xmitframe_coalesce: keyid=%d pattrib->iv[3]=%.2x pframe=%.2x %.2x %.2x %.2x\n",
+ padapter->securitypriv.dot11PrivacyKeyIndex, pattrib->iv[3], *pframe, *(pframe+1), *(pframe+2), *(pframe+3)));
+
+ pframe += pattrib->iv_len;
+
+ mpdu_len -= pattrib->iv_len;
+ }
+
+ if (frg_inx == 0) {
+ llc_sz = rtw_put_snap(pframe, pattrib->ether_type);
+ pframe += llc_sz;
+ mpdu_len -= llc_sz;
+ }
+
+ if ((pattrib->icv_len > 0) && (pattrib->bswenc)) {
+ mpdu_len -= pattrib->icv_len;
+ }
+
+ if (bmcst) {
+ /* don't do fragment to broadcat/multicast packets */
+ mem_sz = _rtw_pktfile_read(&pktfile, pframe, pattrib->pktlen);
+ } else {
+ mem_sz = _rtw_pktfile_read(&pktfile, pframe, mpdu_len);
+ }
+
+ pframe += mem_sz;
+
+ if ((pattrib->icv_len > 0) && (pattrib->bswenc)) {
+ memcpy(pframe, pattrib->icv, pattrib->icv_len);
+ pframe += pattrib->icv_len;
+ }
+
+ frg_inx++;
+
+ if (bmcst || rtw_endofpktfile(&pktfile)) {
+ pattrib->nr_frags = frg_inx;
+
+ pattrib->last_txcmdsz = pattrib->hdrlen + pattrib->iv_len + ((pattrib->nr_frags == 1) ? llc_sz : 0) +
+ ((pattrib->bswenc) ? pattrib->icv_len : 0) + mem_sz;
+
+ ClearMFrag(mem_start);
+
+ break;
+ } else {
+ RT_TRACE(_module_rtl871x_xmit_c_, _drv_err_, ("%s: There're still something in packet!\n", __func__));
+ }
+
+ addr = (size_t)(pframe);
+
+ mem_start = (unsigned char *)round_up(addr, 4) + hw_hdr_offset;
+ memcpy(mem_start, pbuf_start + hw_hdr_offset, pattrib->hdrlen);
+ }
+
+ if (xmitframe_addmic(padapter, pxmitframe) == _FAIL) {
+ RT_TRACE(_module_rtl871x_xmit_c_, _drv_err_, ("xmitframe_addmic(padapter, pxmitframe) == _FAIL\n"));
+ DBG_88E("xmitframe_addmic(padapter, pxmitframe) == _FAIL\n");
+ res = _FAIL;
+ goto exit;
+ }
+
+ xmitframe_swencrypt(padapter, pxmitframe);
+
+ if (!bmcst)
+ update_attrib_vcs_info(padapter, pxmitframe);
+ else
+ pattrib->vcs_mode = NONE_VCS;
+
+exit:
+
+
+ return res;
+}
+
+/* Logical Link Control(LLC) SubNetwork Attachment Point(SNAP) header
+ * IEEE LLC/SNAP header contains 8 octets
+ * First 3 octets comprise the LLC portion
+ * SNAP portion, 5 octets, is divided into two fields:
+ * Organizationally Unique Identifier(OUI), 3 octets,
+ * type, defined by that organization, 2 octets.
+ */
+s32 rtw_put_snap(u8 *data, u16 h_proto)
+{
+ struct ieee80211_snap_hdr *snap;
+ u8 *oui;
+
+
+ snap = (struct ieee80211_snap_hdr *)data;
+ snap->dsap = 0xaa;
+ snap->ssap = 0xaa;
+ snap->ctrl = 0x03;
+
+ if (h_proto == 0x8137 || h_proto == 0x80f3)
+ oui = P802_1H_OUI;
+ else
+ oui = RFC1042_OUI;
+
+ snap->oui[0] = oui[0];
+ snap->oui[1] = oui[1];
+ snap->oui[2] = oui[2];
+
+ *(__be16 *)(data + SNAP_SIZE) = htons(h_proto);
+
+
+ return SNAP_SIZE + sizeof(u16);
+}
+
+void rtw_update_protection(struct adapter *padapter, u8 *ie, uint ie_len)
+{
+ uint protection;
+ u8 *perp;
+ int erp_len;
+ struct xmit_priv *pxmitpriv = &padapter->xmitpriv;
+ struct registry_priv *pregistrypriv = &padapter->registrypriv;
+
+
+ switch (pxmitpriv->vcs_setting) {
+ case DISABLE_VCS:
+ pxmitpriv->vcs = NONE_VCS;
+ break;
+ case ENABLE_VCS:
+ break;
+ case AUTO_VCS:
+ default:
+ perp = rtw_get_ie(ie, _ERPINFO_IE_, &erp_len, ie_len);
+ if (perp == NULL) {
+ pxmitpriv->vcs = NONE_VCS;
+ } else {
+ protection = (*(perp + 2)) & BIT(1);
+ if (protection) {
+ if (pregistrypriv->vcs_type == RTS_CTS)
+ pxmitpriv->vcs = RTS_CTS;
+ else
+ pxmitpriv->vcs = CTS_TO_SELF;
+ } else {
+ pxmitpriv->vcs = NONE_VCS;
+ }
+ }
+ break;
+ }
+
+}
+
+void rtw_count_tx_stats(struct adapter *padapter, struct xmit_frame *pxmitframe, int sz)
+{
+ struct sta_info *psta = NULL;
+ struct stainfo_stats *pstats = NULL;
+ struct xmit_priv *pxmitpriv = &padapter->xmitpriv;
+ struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
+
+ if ((pxmitframe->frame_tag&0x0f) == DATA_FRAMETAG) {
+ pxmitpriv->tx_bytes += sz;
+ pmlmepriv->LinkDetectInfo.NumTxOkInPeriod += pxmitframe->agg_num;
+
+ psta = pxmitframe->attrib.psta;
+ if (psta) {
+ pstats = &psta->sta_stats;
+ pstats->tx_pkts += pxmitframe->agg_num;
+ pstats->tx_bytes += sz;
+ }
+ }
+}
+
+struct xmit_buf *rtw_alloc_xmitbuf_ext(struct xmit_priv *pxmitpriv)
+{
+ unsigned long irql;
+ struct xmit_buf *pxmitbuf = NULL;
+ struct list_head *plist, *phead;
+ struct __queue *pfree_queue = &pxmitpriv->free_xmit_extbuf_queue;
+
+
+ spin_lock_irqsave(&pfree_queue->lock, irql);
+
+ if (list_empty(&pfree_queue->queue)) {
+ pxmitbuf = NULL;
+ } else {
+ phead = get_list_head(pfree_queue);
+
+ plist = phead->next;
+
+ pxmitbuf = container_of(plist, struct xmit_buf, list);
+
+ list_del_init(&(pxmitbuf->list));
+ }
+
+ if (pxmitbuf != NULL) {
+ pxmitpriv->free_xmit_extbuf_cnt--;
+
+ pxmitbuf->priv_data = NULL;
+ /* pxmitbuf->ext_tag = true; */
+
+ if (pxmitbuf->sctx) {
+ DBG_88E("%s pxmitbuf->sctx is not NULL\n", __func__);
+ rtw_sctx_done_err(&pxmitbuf->sctx, RTW_SCTX_DONE_BUF_ALLOC);
+ }
+ }
+
+ spin_unlock_irqrestore(&pfree_queue->lock, irql);
+
+
+ return pxmitbuf;
+}
+
+s32 rtw_free_xmitbuf_ext(struct xmit_priv *pxmitpriv, struct xmit_buf *pxmitbuf)
+{
+ unsigned long irql;
+ struct __queue *pfree_queue = &pxmitpriv->free_xmit_extbuf_queue;
+
+
+ if (pxmitbuf == NULL)
+ return _FAIL;
+
+ spin_lock_irqsave(&pfree_queue->lock, irql);
+
+ list_del_init(&pxmitbuf->list);
+
+ list_add_tail(&(pxmitbuf->list), get_list_head(pfree_queue));
+ pxmitpriv->free_xmit_extbuf_cnt++;
+
+ spin_unlock_irqrestore(&pfree_queue->lock, irql);
+
+
+ return _SUCCESS;
+}
+
+struct xmit_buf *rtw_alloc_xmitbuf(struct xmit_priv *pxmitpriv)
+{
+ unsigned long irql;
+ struct xmit_buf *pxmitbuf = NULL;
+ struct list_head *plist, *phead;
+ struct __queue *pfree_xmitbuf_queue = &pxmitpriv->free_xmitbuf_queue;
+
+
+ /* DBG_88E("+rtw_alloc_xmitbuf\n"); */
+
+ spin_lock_irqsave(&pfree_xmitbuf_queue->lock, irql);
+
+ if (list_empty(&pfree_xmitbuf_queue->queue)) {
+ pxmitbuf = NULL;
+ } else {
+ phead = get_list_head(pfree_xmitbuf_queue);
+
+ plist = phead->next;
+
+ pxmitbuf = container_of(plist, struct xmit_buf, list);
+
+ list_del_init(&(pxmitbuf->list));
+ }
+
+ if (pxmitbuf != NULL) {
+ pxmitpriv->free_xmitbuf_cnt--;
+ pxmitbuf->priv_data = NULL;
+ if (pxmitbuf->sctx) {
+ DBG_88E("%s pxmitbuf->sctx is not NULL\n", __func__);
+ rtw_sctx_done_err(&pxmitbuf->sctx, RTW_SCTX_DONE_BUF_ALLOC);
+ }
+ }
+ spin_unlock_irqrestore(&pfree_xmitbuf_queue->lock, irql);
+
+
+ return pxmitbuf;
+}
+
+s32 rtw_free_xmitbuf(struct xmit_priv *pxmitpriv, struct xmit_buf *pxmitbuf)
+{
+ unsigned long irql;
+ struct __queue *pfree_xmitbuf_queue = &pxmitpriv->free_xmitbuf_queue;
+
+ if (pxmitbuf == NULL)
+ return _FAIL;
+
+ if (pxmitbuf->sctx) {
+ DBG_88E("%s pxmitbuf->sctx is not NULL\n", __func__);
+ rtw_sctx_done_err(&pxmitbuf->sctx, RTW_SCTX_DONE_BUF_FREE);
+ }
+
+ if (pxmitbuf->ext_tag) {
+ rtw_free_xmitbuf_ext(pxmitpriv, pxmitbuf);
+ } else {
+ spin_lock_irqsave(&pfree_xmitbuf_queue->lock, irql);
+
+ list_del_init(&pxmitbuf->list);
+
+ list_add_tail(&(pxmitbuf->list), get_list_head(pfree_xmitbuf_queue));
+
+ pxmitpriv->free_xmitbuf_cnt++;
+ spin_unlock_irqrestore(&pfree_xmitbuf_queue->lock, irql);
+ }
+
+
+ return _SUCCESS;
+}
+
+/*
+Calling context:
+1. OS_TXENTRY
+2. RXENTRY (rx_thread or RX_ISR/RX_CallBack)
+
+If we turn on USE_RXTHREAD, then, no need for critical section.
+Otherwise, we must use _enter/_exit critical to protect free_xmit_queue...
+
+Must be very very cautious...
+
+*/
+
+struct xmit_frame *rtw_alloc_xmitframe(struct xmit_priv *pxmitpriv)/* _queue *pfree_xmit_queue) */
+{
+ /*
+ Please remember to use all the osdep_service api,
+ and lock/unlock or _enter/_exit critical to protect
+ pfree_xmit_queue
+ */
+
+ struct xmit_frame *pxframe = NULL;
+ struct list_head *plist, *phead;
+ struct __queue *pfree_xmit_queue = &pxmitpriv->free_xmit_queue;
+
+
+ spin_lock_bh(&pfree_xmit_queue->lock);
+
+ if (list_empty(&pfree_xmit_queue->queue)) {
+ RT_TRACE(_module_rtl871x_xmit_c_, _drv_info_, ("rtw_alloc_xmitframe:%d\n", pxmitpriv->free_xmitframe_cnt));
+ pxframe = NULL;
+ } else {
+ phead = get_list_head(pfree_xmit_queue);
+
+ plist = phead->next;
+
+ pxframe = container_of(plist, struct xmit_frame, list);
+
+ list_del_init(&(pxframe->list));
+ }
+
+ if (pxframe != NULL) { /* default value setting */
+ pxmitpriv->free_xmitframe_cnt--;
+
+ RT_TRACE(_module_rtl871x_xmit_c_, _drv_info_, ("rtw_alloc_xmitframe():free_xmitframe_cnt=%d\n", pxmitpriv->free_xmitframe_cnt));
+
+ pxframe->buf_addr = NULL;
+ pxframe->pxmitbuf = NULL;
+
+ memset(&pxframe->attrib, 0, sizeof(struct pkt_attrib));
+ /* pxframe->attrib.psta = NULL; */
+
+ pxframe->frame_tag = DATA_FRAMETAG;
+
+ pxframe->pkt = NULL;
+ pxframe->pkt_offset = 1;/* default use pkt_offset to fill tx desc */
+
+ pxframe->agg_num = 1;
+ pxframe->ack_report = 0;
+ }
+
+ spin_unlock_bh(&pfree_xmit_queue->lock);
+
+
+ return pxframe;
+}
+
+s32 rtw_free_xmitframe(struct xmit_priv *pxmitpriv, struct xmit_frame *pxmitframe)
+{
+ struct __queue *pfree_xmit_queue = &pxmitpriv->free_xmit_queue;
+ struct adapter *padapter = pxmitpriv->adapter;
+ struct sk_buff *pndis_pkt = NULL;
+
+
+ if (pxmitframe == NULL) {
+ RT_TRACE(_module_rtl871x_xmit_c_, _drv_err_, ("====== rtw_free_xmitframe():pxmitframe == NULL!!!!!!!!!!\n"));
+ goto exit;
+ }
+
+ spin_lock_bh(&pfree_xmit_queue->lock);
+
+ list_del_init(&pxmitframe->list);
+
+ if (pxmitframe->pkt) {
+ pndis_pkt = pxmitframe->pkt;
+ pxmitframe->pkt = NULL;
+ }
+
+ list_add_tail(&pxmitframe->list, get_list_head(pfree_xmit_queue));
+
+ pxmitpriv->free_xmitframe_cnt++;
+ RT_TRACE(_module_rtl871x_xmit_c_, _drv_debug_, ("rtw_free_xmitframe():free_xmitframe_cnt=%d\n", pxmitpriv->free_xmitframe_cnt));
+
+ spin_unlock_bh(&pfree_xmit_queue->lock);
+
+ if (pndis_pkt)
+ rtw_os_pkt_complete(padapter, pndis_pkt);
+
+exit:
+
+
+ return _SUCCESS;
+}
+
+void rtw_free_xmitframe_queue(struct xmit_priv *pxmitpriv, struct __queue *pframequeue)
+{
+ struct list_head *plist, *phead;
+ struct xmit_frame *pxmitframe;
+
+
+ spin_lock_bh(&(pframequeue->lock));
+
+ phead = get_list_head(pframequeue);
+ plist = phead->next;
+
+ while (phead != plist) {
+ pxmitframe = container_of(plist, struct xmit_frame, list);
+
+ plist = plist->next;
+
+ rtw_free_xmitframe(pxmitpriv, pxmitframe);
+ }
+ spin_unlock_bh(&(pframequeue->lock));
+
+}
+
+s32 rtw_xmitframe_enqueue(struct adapter *padapter, struct xmit_frame *pxmitframe)
+{
+ if (rtw_xmit_classifier(padapter, pxmitframe) == _FAIL) {
+ RT_TRACE(_module_rtl871x_xmit_c_, _drv_err_,
+ ("rtw_xmitframe_enqueue: drop xmit pkt for classifier fail\n"));
+/* pxmitframe->pkt = NULL; */
+ return _FAIL;
+ }
+
+ return _SUCCESS;
+}
+
+static struct xmit_frame *dequeue_one_xmitframe(struct xmit_priv *pxmitpriv, struct hw_xmit *phwxmit, struct tx_servq *ptxservq, struct __queue *pframe_queue)
+{
+ struct list_head *xmitframe_plist, *xmitframe_phead;
+ struct xmit_frame *pxmitframe = NULL;
+
+ xmitframe_phead = get_list_head(pframe_queue);
+ xmitframe_plist = xmitframe_phead->next;
+
+ if (xmitframe_phead != xmitframe_plist) {
+ pxmitframe = container_of(xmitframe_plist, struct xmit_frame, list);
+
+ xmitframe_plist = xmitframe_plist->next;
+
+ list_del_init(&pxmitframe->list);
+
+ ptxservq->qcnt--;
+ }
+ return pxmitframe;
+}
+
+struct xmit_frame *rtw_dequeue_xframe(struct xmit_priv *pxmitpriv, struct hw_xmit *phwxmit_i, int entry)
+{
+ struct list_head *sta_plist, *sta_phead;
+ struct hw_xmit *phwxmit;
+ struct tx_servq *ptxservq = NULL;
+ struct __queue *pframe_queue = NULL;
+ struct xmit_frame *pxmitframe = NULL;
+ struct adapter *padapter = pxmitpriv->adapter;
+ struct registry_priv *pregpriv = &padapter->registrypriv;
+ int i, inx[4];
+
+
+ inx[0] = 0; inx[1] = 1; inx[2] = 2; inx[3] = 3;
+
+ if (pregpriv->wifi_spec == 1) {
+ int j;
+
+ for (j = 0; j < 4; j++)
+ inx[j] = pxmitpriv->wmm_para_seq[j];
+ }
+
+ spin_lock_bh(&pxmitpriv->lock);
+
+ for (i = 0; i < entry; i++) {
+ phwxmit = phwxmit_i + inx[i];
+
+ sta_phead = get_list_head(phwxmit->sta_queue);
+ sta_plist = sta_phead->next;
+
+ while (sta_phead != sta_plist) {
+ ptxservq = container_of(sta_plist, struct tx_servq, tx_pending);
+
+ pframe_queue = &ptxservq->sta_pending;
+
+ pxmitframe = dequeue_one_xmitframe(pxmitpriv, phwxmit, ptxservq, pframe_queue);
+
+ if (pxmitframe) {
+ phwxmit->accnt--;
+
+ /* Remove sta node when there are no pending packets. */
+ if (list_empty(&pframe_queue->queue)) /* must be done after get_next and before break */
+ list_del_init(&ptxservq->tx_pending);
+ goto exit;
+ }
+
+ sta_plist = sta_plist->next;
+ }
+ }
+exit:
+ spin_unlock_bh(&pxmitpriv->lock);
+ return pxmitframe;
+}
+
+struct tx_servq *rtw_get_sta_pending(struct adapter *padapter, struct sta_info *psta, int up, u8 *ac)
+{
+ struct tx_servq *ptxservq;
+
+ switch (up) {
+ case 1:
+ case 2:
+ ptxservq = &(psta->sta_xmitpriv.bk_q);
+ *(ac) = 3;
+ RT_TRACE(_module_rtl871x_xmit_c_, _drv_info_, ("rtw_get_sta_pending : BK\n"));
+ break;
+ case 4:
+ case 5:
+ ptxservq = &(psta->sta_xmitpriv.vi_q);
+ *(ac) = 1;
+ RT_TRACE(_module_rtl871x_xmit_c_, _drv_info_, ("rtw_get_sta_pending : VI\n"));
+ break;
+ case 6:
+ case 7:
+ ptxservq = &(psta->sta_xmitpriv.vo_q);
+ *(ac) = 0;
+ RT_TRACE(_module_rtl871x_xmit_c_, _drv_info_, ("rtw_get_sta_pending : VO\n"));
+ break;
+ case 0:
+ case 3:
+ default:
+ ptxservq = &(psta->sta_xmitpriv.be_q);
+ *(ac) = 2;
+ RT_TRACE(_module_rtl871x_xmit_c_, _drv_info_, ("rtw_get_sta_pending : BE\n"));
+ break;
+ }
+
+
+ return ptxservq;
+}
+
+/*
+ * Will enqueue pxmitframe to the proper queue,
+ * and indicate it to xx_pending list.....
+ */
+s32 rtw_xmit_classifier(struct adapter *padapter, struct xmit_frame *pxmitframe)
+{
+ u8 ac_index;
+ struct sta_info *psta;
+ struct tx_servq *ptxservq;
+ struct pkt_attrib *pattrib = &pxmitframe->attrib;
+ struct sta_priv *pstapriv = &padapter->stapriv;
+ struct hw_xmit *phwxmits = padapter->xmitpriv.hwxmits;
+ int res = _SUCCESS;
+
+
+ if (pattrib->psta) {
+ psta = pattrib->psta;
+ } else {
+ psta = rtw_get_stainfo(pstapriv, pattrib->ra);
+ }
+
+ if (psta == NULL) {
+ res = _FAIL;
+ DBG_88E("rtw_xmit_classifier: psta == NULL\n");
+ RT_TRACE(_module_rtl871x_xmit_c_, _drv_err_, ("rtw_xmit_classifier: psta == NULL\n"));
+ goto exit;
+ }
+
+ ptxservq = rtw_get_sta_pending(padapter, psta, pattrib->priority, (u8 *)(&ac_index));
+
+ if (list_empty(&ptxservq->tx_pending))
+ list_add_tail(&ptxservq->tx_pending, get_list_head(phwxmits[ac_index].sta_queue));
+
+ list_add_tail(&pxmitframe->list, get_list_head(&ptxservq->sta_pending));
+ ptxservq->qcnt++;
+ phwxmits[ac_index].accnt++;
+exit:
+
+
+ return res;
+}
+
+void rtw_alloc_hwxmits(struct adapter *padapter)
+{
+ struct hw_xmit *hwxmits;
+ struct xmit_priv *pxmitpriv = &padapter->xmitpriv;
+
+ pxmitpriv->hwxmit_entry = HWXMIT_ENTRY;
+
+ pxmitpriv->hwxmits = kcalloc(pxmitpriv->hwxmit_entry,
+ sizeof(struct hw_xmit), GFP_KERNEL);
+
+ hwxmits = pxmitpriv->hwxmits;
+
+ hwxmits[0] .sta_queue = &pxmitpriv->vo_pending;
+ hwxmits[1] .sta_queue = &pxmitpriv->vi_pending;
+ hwxmits[2] .sta_queue = &pxmitpriv->be_pending;
+ hwxmits[3] .sta_queue = &pxmitpriv->bk_pending;
+}
+
+void rtw_free_hwxmits(struct adapter *padapter)
+{
+ struct hw_xmit *hwxmits;
+ struct xmit_priv *pxmitpriv = &padapter->xmitpriv;
+
+ hwxmits = pxmitpriv->hwxmits;
+ kfree(hwxmits);
+}
+
+void rtw_init_hwxmits(struct hw_xmit *phwxmit, int entry)
+{
+ int i;
+ for (i = 0; i < entry; i++, phwxmit++)
+ phwxmit->accnt = 0;
+}
+
+u32 rtw_get_ff_hwaddr(struct xmit_frame *pxmitframe)
+{
+ u32 addr;
+ struct pkt_attrib *pattrib = &pxmitframe->attrib;
+
+ switch (pattrib->qsel) {
+ case 0:
+ case 3:
+ addr = BE_QUEUE_INX;
+ break;
+ case 1:
+ case 2:
+ addr = BK_QUEUE_INX;
+ break;
+ case 4:
+ case 5:
+ addr = VI_QUEUE_INX;
+ break;
+ case 6:
+ case 7:
+ addr = VO_QUEUE_INX;
+ break;
+ case 0x10:
+ addr = BCN_QUEUE_INX;
+ break;
+ case 0x11:/* BC/MC in PS (HIQ) */
+ addr = HIGH_QUEUE_INX;
+ break;
+ case 0x12:
+ default:
+ addr = MGT_QUEUE_INX;
+ break;
+ }
+
+ return addr;
+}
+
+static void do_queue_select(struct adapter *padapter, struct pkt_attrib *pattrib)
+{
+ u8 qsel;
+
+ qsel = pattrib->priority;
+ RT_TRACE(_module_rtl871x_xmit_c_, _drv_info_, ("### do_queue_select priority=%d , qsel = %d\n", pattrib->priority , qsel));
+
+ pattrib->qsel = qsel;
+}
+
+/*
+ * The main transmit(tx) entry
+ *
+ * Return
+ * 1 enqueue
+ * 0 success, hardware will handle this xmit frame(packet)
+ * <0 fail
+ */
+s32 rtw_xmit(struct adapter *padapter, struct sk_buff **ppkt)
+{
+ struct xmit_priv *pxmitpriv = &padapter->xmitpriv;
+ struct xmit_frame *pxmitframe = NULL;
+ s32 res;
+
+ pxmitframe = rtw_alloc_xmitframe(pxmitpriv);
+ if (pxmitframe == NULL) {
+ RT_TRACE(_module_xmit_osdep_c_, _drv_err_, ("rtw_xmit: no more pxmitframe\n"));
+ DBG_88E("DBG_TX_DROP_FRAME %s no more pxmitframe\n", __func__);
+ return -1;
+ }
+
+ res = update_attrib(padapter, *ppkt, &pxmitframe->attrib);
+
+ if (res == _FAIL) {
+ RT_TRACE(_module_xmit_osdep_c_, _drv_err_, ("rtw_xmit: update attrib fail\n"));
+ rtw_free_xmitframe(pxmitpriv, pxmitframe);
+ return -1;
+ }
+ pxmitframe->pkt = *ppkt;
+
+ rtw_led_control(padapter, LED_CTL_TX);
+
+ do_queue_select(padapter, &pxmitframe->attrib);
+
+#ifdef CONFIG_88EU_AP_MODE
+ spin_lock_bh(&pxmitpriv->lock);
+ if (xmitframe_enqueue_for_sleeping_sta(padapter, pxmitframe)) {
+ spin_unlock_bh(&pxmitpriv->lock);
+ return 1;
+ }
+ spin_unlock_bh(&pxmitpriv->lock);
+#endif
+
+ if (rtw_hal_xmit(padapter, pxmitframe) == false)
+ return 1;
+
+ return 0;
+}
+
+#if defined(CONFIG_88EU_AP_MODE)
+
+int xmitframe_enqueue_for_sleeping_sta(struct adapter *padapter, struct xmit_frame *pxmitframe)
+{
+ int ret = false;
+ struct sta_info *psta = NULL;
+ struct sta_priv *pstapriv = &padapter->stapriv;
+ struct pkt_attrib *pattrib = &pxmitframe->attrib;
+ struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
+ int bmcst = IS_MCAST(pattrib->ra);
+
+ if (check_fwstate(pmlmepriv, WIFI_AP_STATE) == false)
+ return ret;
+
+ if (pattrib->psta)
+ psta = pattrib->psta;
+ else
+ psta = rtw_get_stainfo(pstapriv, pattrib->ra);
+
+ if (psta == NULL)
+ return ret;
+
+ if (pattrib->triggered == 1) {
+ if (bmcst)
+ pattrib->qsel = 0x11;/* HIQ */
+ return ret;
+ }
+
+ if (bmcst) {
+ spin_lock_bh(&psta->sleep_q.lock);
+
+ if (pstapriv->sta_dz_bitmap) {/* if any one sta is in ps mode */
+ list_del_init(&pxmitframe->list);
+
+ list_add_tail(&pxmitframe->list, get_list_head(&psta->sleep_q));
+
+ psta->sleepq_len++;
+
+ pstapriv->tim_bitmap |= BIT(0);/* */
+ pstapriv->sta_dz_bitmap |= BIT(0);
+
+ update_beacon(padapter, _TIM_IE_, NULL, false);/* tx bc/mc packets after update bcn */
+
+ ret = true;
+ }
+
+ spin_unlock_bh(&psta->sleep_q.lock);
+
+ return ret;
+ }
+
+ spin_lock_bh(&psta->sleep_q.lock);
+
+ if (psta->state&WIFI_SLEEP_STATE) {
+ u8 wmmps_ac = 0;
+
+ if (pstapriv->sta_dz_bitmap&BIT(psta->aid)) {
+ list_del_init(&pxmitframe->list);
+
+ list_add_tail(&pxmitframe->list, get_list_head(&psta->sleep_q));
+
+ psta->sleepq_len++;
+
+ switch (pattrib->priority) {
+ case 1:
+ case 2:
+ wmmps_ac = psta->uapsd_bk&BIT(0);
+ break;
+ case 4:
+ case 5:
+ wmmps_ac = psta->uapsd_vi&BIT(0);
+ break;
+ case 6:
+ case 7:
+ wmmps_ac = psta->uapsd_vo&BIT(0);
+ break;
+ case 0:
+ case 3:
+ default:
+ wmmps_ac = psta->uapsd_be&BIT(0);
+ break;
+ }
+
+ if (wmmps_ac)
+ psta->sleepq_ac_len++;
+
+ if (((psta->has_legacy_ac) && (!wmmps_ac)) ||
+ ((!psta->has_legacy_ac) && (wmmps_ac))) {
+ pstapriv->tim_bitmap |= BIT(psta->aid);
+
+ if (psta->sleepq_len == 1) {
+ /* update BCN for TIM IE */
+ update_beacon(padapter, _TIM_IE_, NULL, false);
+ }
+ }
+ ret = true;
+ }
+ }
+
+ spin_unlock_bh(&psta->sleep_q.lock);
+
+ return ret;
+}
+
+static void dequeue_xmitframes_to_sleeping_queue(struct adapter *padapter, struct sta_info *psta, struct __queue *pframequeue)
+{
+ struct list_head *plist, *phead;
+ u8 ac_index;
+ struct tx_servq *ptxservq;
+ struct pkt_attrib *pattrib;
+ struct xmit_frame *pxmitframe;
+ struct hw_xmit *phwxmits = padapter->xmitpriv.hwxmits;
+
+ phead = get_list_head(pframequeue);
+ plist = phead->next;
+
+ while (phead != plist) {
+ pxmitframe = container_of(plist, struct xmit_frame, list);
+
+ plist = plist->next;
+
+ xmitframe_enqueue_for_sleeping_sta(padapter, pxmitframe);
+
+ pattrib = &pxmitframe->attrib;
+
+ ptxservq = rtw_get_sta_pending(padapter, psta, pattrib->priority, (u8 *)(&ac_index));
+
+ ptxservq->qcnt--;
+ phwxmits[ac_index].accnt--;
+ }
+}
+
+void stop_sta_xmit(struct adapter *padapter, struct sta_info *psta)
+{
+ struct sta_info *psta_bmc;
+ struct sta_xmit_priv *pstaxmitpriv;
+ struct sta_priv *pstapriv = &padapter->stapriv;
+ struct xmit_priv *pxmitpriv = &padapter->xmitpriv;
+
+ pstaxmitpriv = &psta->sta_xmitpriv;
+
+ /* for BC/MC Frames */
+ psta_bmc = rtw_get_bcmc_stainfo(padapter);
+
+ spin_lock_bh(&pxmitpriv->lock);
+
+ psta->state |= WIFI_SLEEP_STATE;
+
+ pstapriv->sta_dz_bitmap |= BIT(psta->aid);
+
+ dequeue_xmitframes_to_sleeping_queue(padapter, psta, &pstaxmitpriv->vo_q.sta_pending);
+ list_del_init(&(pstaxmitpriv->vo_q.tx_pending));
+
+ dequeue_xmitframes_to_sleeping_queue(padapter, psta, &pstaxmitpriv->vi_q.sta_pending);
+ list_del_init(&(pstaxmitpriv->vi_q.tx_pending));
+
+ dequeue_xmitframes_to_sleeping_queue(padapter, psta, &pstaxmitpriv->be_q.sta_pending);
+ list_del_init(&(pstaxmitpriv->be_q.tx_pending));
+
+ dequeue_xmitframes_to_sleeping_queue(padapter, psta, &pstaxmitpriv->bk_q.sta_pending);
+ list_del_init(&(pstaxmitpriv->bk_q.tx_pending));
+
+ /* for BC/MC Frames */
+ pstaxmitpriv = &psta_bmc->sta_xmitpriv;
+ dequeue_xmitframes_to_sleeping_queue(padapter, psta_bmc, &pstaxmitpriv->be_q.sta_pending);
+ list_del_init(&(pstaxmitpriv->be_q.tx_pending));
+
+ spin_unlock_bh(&pxmitpriv->lock);
+}
+
+void wakeup_sta_to_xmit(struct adapter *padapter, struct sta_info *psta)
+{
+ u8 update_mask = 0, wmmps_ac = 0;
+ struct sta_info *psta_bmc;
+ struct list_head *xmitframe_plist, *xmitframe_phead;
+ struct xmit_frame *pxmitframe = NULL;
+ struct sta_priv *pstapriv = &padapter->stapriv;
+
+ spin_lock_bh(&psta->sleep_q.lock);
+
+ xmitframe_phead = get_list_head(&psta->sleep_q);
+ xmitframe_plist = xmitframe_phead->next;
+
+ while (xmitframe_phead != xmitframe_plist) {
+ pxmitframe = container_of(xmitframe_plist, struct xmit_frame, list);
+
+ xmitframe_plist = xmitframe_plist->next;
+
+ list_del_init(&pxmitframe->list);
+
+ switch (pxmitframe->attrib.priority) {
+ case 1:
+ case 2:
+ wmmps_ac = psta->uapsd_bk&BIT(1);
+ break;
+ case 4:
+ case 5:
+ wmmps_ac = psta->uapsd_vi&BIT(1);
+ break;
+ case 6:
+ case 7:
+ wmmps_ac = psta->uapsd_vo&BIT(1);
+ break;
+ case 0:
+ case 3:
+ default:
+ wmmps_ac = psta->uapsd_be&BIT(1);
+ break;
+ }
+
+ psta->sleepq_len--;
+ if (psta->sleepq_len > 0)
+ pxmitframe->attrib.mdata = 1;
+ else
+ pxmitframe->attrib.mdata = 0;
+
+ if (wmmps_ac) {
+ psta->sleepq_ac_len--;
+ if (psta->sleepq_ac_len > 0) {
+ pxmitframe->attrib.mdata = 1;
+ pxmitframe->attrib.eosp = 0;
+ } else {
+ pxmitframe->attrib.mdata = 0;
+ pxmitframe->attrib.eosp = 1;
+ }
+ }
+
+ pxmitframe->attrib.triggered = 1;
+
+ spin_unlock_bh(&psta->sleep_q.lock);
+ if (rtw_hal_xmit(padapter, pxmitframe))
+ rtw_os_xmit_complete(padapter, pxmitframe);
+ spin_lock_bh(&psta->sleep_q.lock);
+ }
+
+ if (psta->sleepq_len == 0) {
+ pstapriv->tim_bitmap &= ~BIT(psta->aid);
+
+ update_mask = BIT(0);
+
+ if (psta->state&WIFI_SLEEP_STATE)
+ psta->state ^= WIFI_SLEEP_STATE;
+
+ if (psta->state & WIFI_STA_ALIVE_CHK_STATE) {
+ psta->expire_to = pstapriv->expire_to;
+ psta->state ^= WIFI_STA_ALIVE_CHK_STATE;
+ }
+
+ pstapriv->sta_dz_bitmap &= ~BIT(psta->aid);
+ }
+
+ spin_unlock_bh(&psta->sleep_q.lock);
+
+ /* for BC/MC Frames */
+ psta_bmc = rtw_get_bcmc_stainfo(padapter);
+ if (!psta_bmc)
+ return;
+
+ if ((pstapriv->sta_dz_bitmap&0xfffe) == 0x0) { /* no any sta in ps mode */
+ spin_lock_bh(&psta_bmc->sleep_q.lock);
+
+ xmitframe_phead = get_list_head(&psta_bmc->sleep_q);
+ xmitframe_plist = xmitframe_phead->next;
+
+ while (xmitframe_phead != xmitframe_plist) {
+ pxmitframe = container_of(xmitframe_plist, struct xmit_frame, list);
+
+ xmitframe_plist = xmitframe_plist->next;
+
+ list_del_init(&pxmitframe->list);
+
+ psta_bmc->sleepq_len--;
+ if (psta_bmc->sleepq_len > 0)
+ pxmitframe->attrib.mdata = 1;
+ else
+ pxmitframe->attrib.mdata = 0;
+
+ pxmitframe->attrib.triggered = 1;
+
+ spin_unlock_bh(&psta_bmc->sleep_q.lock);
+ if (rtw_hal_xmit(padapter, pxmitframe))
+ rtw_os_xmit_complete(padapter, pxmitframe);
+ spin_lock_bh(&psta_bmc->sleep_q.lock);
+ }
+
+ if (psta_bmc->sleepq_len == 0) {
+ pstapriv->tim_bitmap &= ~BIT(0);
+ pstapriv->sta_dz_bitmap &= ~BIT(0);
+
+ update_mask |= BIT(1);
+ }
+
+ spin_unlock_bh(&psta_bmc->sleep_q.lock);
+ }
+
+ if (update_mask)
+ update_beacon(padapter, _TIM_IE_, NULL, false);
+}
+
+void xmit_delivery_enabled_frames(struct adapter *padapter, struct sta_info *psta)
+{
+ u8 wmmps_ac = 0;
+ struct list_head *xmitframe_plist, *xmitframe_phead;
+ struct xmit_frame *pxmitframe = NULL;
+ struct sta_priv *pstapriv = &padapter->stapriv;
+
+ spin_lock_bh(&psta->sleep_q.lock);
+
+ xmitframe_phead = get_list_head(&psta->sleep_q);
+ xmitframe_plist = xmitframe_phead->next;
+
+ while (xmitframe_phead != xmitframe_plist) {
+ pxmitframe = container_of(xmitframe_plist, struct xmit_frame, list);
+
+ xmitframe_plist = xmitframe_plist->next;
+
+ switch (pxmitframe->attrib.priority) {
+ case 1:
+ case 2:
+ wmmps_ac = psta->uapsd_bk&BIT(1);
+ break;
+ case 4:
+ case 5:
+ wmmps_ac = psta->uapsd_vi&BIT(1);
+ break;
+ case 6:
+ case 7:
+ wmmps_ac = psta->uapsd_vo&BIT(1);
+ break;
+ case 0:
+ case 3:
+ default:
+ wmmps_ac = psta->uapsd_be&BIT(1);
+ break;
+ }
+
+ if (!wmmps_ac)
+ continue;
+
+ list_del_init(&pxmitframe->list);
+
+ psta->sleepq_len--;
+ psta->sleepq_ac_len--;
+
+ if (psta->sleepq_ac_len > 0) {
+ pxmitframe->attrib.mdata = 1;
+ pxmitframe->attrib.eosp = 0;
+ } else {
+ pxmitframe->attrib.mdata = 0;
+ pxmitframe->attrib.eosp = 1;
+ }
+
+ pxmitframe->attrib.triggered = 1;
+
+ if (rtw_hal_xmit(padapter, pxmitframe) == true)
+ rtw_os_xmit_complete(padapter, pxmitframe);
+
+ if ((psta->sleepq_ac_len == 0) && (!psta->has_legacy_ac) && (wmmps_ac)) {
+ pstapriv->tim_bitmap &= ~BIT(psta->aid);
+
+ /* update BCN for TIM IE */
+ update_beacon(padapter, _TIM_IE_, NULL, false);
+ }
+ }
+
+ spin_unlock_bh(&psta->sleep_q.lock);
+}
+
+#endif
+
+void rtw_sctx_init(struct submit_ctx *sctx, int timeout_ms)
+{
+ sctx->timeout_ms = timeout_ms;
+ sctx->submit_time = jiffies;
+ init_completion(&sctx->done);
+ sctx->status = RTW_SCTX_SUBMITTED;
+}
+
+int rtw_sctx_wait(struct submit_ctx *sctx)
+{
+ int ret = _FAIL;
+ unsigned long expire;
+ int status = 0;
+
+ expire = sctx->timeout_ms ? msecs_to_jiffies(sctx->timeout_ms) : MAX_SCHEDULE_TIMEOUT;
+ if (!wait_for_completion_timeout(&sctx->done, expire)) {
+ /* timeout, do something?? */
+ status = RTW_SCTX_DONE_TIMEOUT;
+ DBG_88E("%s timeout\n", __func__);
+ } else {
+ status = sctx->status;
+ }
+
+ if (status == RTW_SCTX_DONE_SUCCESS)
+ ret = _SUCCESS;
+
+ return ret;
+}
+
+static bool rtw_sctx_chk_waring_status(int status)
+{
+ switch (status) {
+ case RTW_SCTX_DONE_UNKNOWN:
+ case RTW_SCTX_DONE_BUF_ALLOC:
+ case RTW_SCTX_DONE_BUF_FREE:
+
+ case RTW_SCTX_DONE_DRV_STOP:
+ case RTW_SCTX_DONE_DEV_REMOVE:
+ return true;
+ default:
+ return false;
+ }
+}
+
+void rtw_sctx_done_err(struct submit_ctx **sctx, int status)
+{
+ if (*sctx) {
+ if (rtw_sctx_chk_waring_status(status))
+ DBG_88E("%s status:%d\n", __func__, status);
+ (*sctx)->status = status;
+ complete(&((*sctx)->done));
+ *sctx = NULL;
+ }
+}
+
+void rtw_sctx_done(struct submit_ctx **sctx)
+{
+ rtw_sctx_done_err(sctx, RTW_SCTX_DONE_SUCCESS);
+}
+
+int rtw_ack_tx_wait(struct xmit_priv *pxmitpriv, u32 timeout_ms)
+{
+ struct submit_ctx *pack_tx_ops = &pxmitpriv->ack_tx_ops;
+
+ pack_tx_ops->submit_time = jiffies;
+ pack_tx_ops->timeout_ms = timeout_ms;
+ pack_tx_ops->status = RTW_SCTX_SUBMITTED;
+
+ return rtw_sctx_wait(pack_tx_ops);
+}
+
+void rtw_ack_tx_done(struct xmit_priv *pxmitpriv, int status)
+{
+ struct submit_ctx *pack_tx_ops = &pxmitpriv->ack_tx_ops;
+
+ if (pxmitpriv->ack_tx)
+ rtw_sctx_done_err(&pack_tx_ops, status);
+ else
+ DBG_88E("%s ack_tx not set\n", __func__);
+}
diff --git a/drivers/staging/rtl8188eu/hal/Hal8188ERateAdaptive.c b/drivers/staging/rtl8188eu/hal/Hal8188ERateAdaptive.c
new file mode 100644
index 000000000..082f0ca19
--- /dev/null
+++ b/drivers/staging/rtl8188eu/hal/Hal8188ERateAdaptive.c
@@ -0,0 +1,780 @@
+/*++
+Copyright (c) Realtek Semiconductor Corp. All rights reserved.
+
+Module Name:
+ RateAdaptive.c
+
+Abstract:
+ Implement Rate Adaptive functions for common operations.
+
+Major Change History:
+ When Who What
+ ---------- --------------- -------------------------------
+ 2011-08-12 Page Create.
+
+--*/
+#include "odm_precomp.h"
+
+/* Rate adaptive parameters */
+
+static u8 RETRY_PENALTY[PERENTRY][RETRYSIZE+1] = {
+ {5, 4, 3, 2, 0, 3}, /* 92 , idx = 0 */
+ {6, 5, 4, 3, 0, 4}, /* 86 , idx = 1 */
+ {6, 5, 4, 2, 0, 4}, /* 81 , idx = 2 */
+ {8, 7, 6, 4, 0, 6}, /* 75 , idx = 3 */
+ {10, 9, 8, 6, 0, 8}, /* 71 , idx = 4 */
+ {10, 9, 8, 4, 0, 8}, /* 66 , idx = 5 */
+ {10, 9, 8, 2, 0, 8}, /* 62 , idx = 6 */
+ {10, 9, 8, 0, 0, 8}, /* 59 , idx = 7 */
+ {18, 17, 16, 8, 0, 16}, /* 53 , idx = 8 */
+ {26, 25, 24, 16, 0, 24}, /* 50 , idx = 9 */
+ {34, 33, 32, 24, 0, 32}, /* 47 , idx = 0x0a */
+ {34, 31, 28, 20, 0, 32}, /* 43 , idx = 0x0b */
+ {34, 31, 27, 18, 0, 32}, /* 40 , idx = 0x0c */
+ {34, 31, 26, 16, 0, 32}, /* 37 , idx = 0x0d */
+ {34, 30, 22, 16, 0, 32}, /* 32 , idx = 0x0e */
+ {34, 30, 24, 16, 0, 32}, /* 26 , idx = 0x0f */
+ {49, 46, 40, 16, 0, 48}, /* 20 , idx = 0x10 */
+ {49, 45, 32, 0, 0, 48}, /* 17 , idx = 0x11 */
+ {49, 45, 22, 18, 0, 48}, /* 15 , idx = 0x12 */
+ {49, 40, 24, 16, 0, 48}, /* 12 , idx = 0x13 */
+ {49, 32, 18, 12, 0, 48}, /* 9 , idx = 0x14 */
+ {49, 22, 18, 14, 0, 48}, /* 6 , idx = 0x15 */
+ {49, 16, 16, 0, 0, 48}
+ }; /* 3, idx = 0x16 */
+
+static u8 PT_PENALTY[RETRYSIZE+1] = {34, 31, 30, 24, 0, 32};
+
+/* wilson modify */
+static u8 RETRY_PENALTY_IDX[2][RATESIZE] = {
+ {4, 4, 4, 5, 4, 4, 5, 7, 7, 7, 8, 0x0a, /* SS>TH */
+ 4, 4, 4, 4, 6, 0x0a, 0x0b, 0x0d,
+ 5, 5, 7, 7, 8, 0x0b, 0x0d, 0x0f}, /* 0329 R01 */
+ {0x0a, 0x0a, 0x0b, 0x0c, 0x0a,
+ 0x0a, 0x0b, 0x0c, 0x0d, 0x10, 0x13, 0x14, /* SS<TH */
+ 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x11, 0x13, 0x15,
+ 9, 9, 9, 9, 0x0c, 0x0e, 0x11, 0x13}
+ };
+
+static u8 RETRY_PENALTY_UP_IDX[RATESIZE] = {
+ 0x0c, 0x0d, 0x0d, 0x0f, 0x0d, 0x0e,
+ 0x0f, 0x0f, 0x10, 0x12, 0x13, 0x14, /* SS>TH */
+ 0x0f, 0x10, 0x10, 0x12, 0x12, 0x13, 0x14, 0x15,
+ 0x11, 0x11, 0x12, 0x13, 0x13, 0x13, 0x14, 0x15};
+
+static u8 RSSI_THRESHOLD[RATESIZE] = {
+ 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0x24, 0x26, 0x2a,
+ 0x18, 0x1a, 0x1d, 0x1f, 0x21, 0x27, 0x29, 0x2a,
+ 0, 0, 0, 0x1f, 0x23, 0x28, 0x2a, 0x2c};
+
+static u16 N_THRESHOLD_HIGH[RATESIZE] = {
+ 4, 4, 8, 16,
+ 24, 36, 48, 72, 96, 144, 192, 216,
+ 60, 80, 100, 160, 240, 400, 560, 640,
+ 300, 320, 480, 720, 1000, 1200, 1600, 2000};
+static u16 N_THRESHOLD_LOW[RATESIZE] = {
+ 2, 2, 4, 8,
+ 12, 18, 24, 36, 48, 72, 96, 108,
+ 30, 40, 50, 80, 120, 200, 280, 320,
+ 150, 160, 240, 360, 500, 600, 800, 1000};
+
+static u8 DROPING_NECESSARY[RATESIZE] = {
+ 1, 1, 1, 1,
+ 1, 2, 3, 4, 5, 6, 7, 8,
+ 1, 2, 3, 4, 5, 6, 7, 8,
+ 5, 6, 7, 8, 9, 10, 11, 12};
+
+static u8 PendingForRateUpFail[5] = {2, 10, 24, 40, 60};
+static u16 DynamicTxRPTTiming[6] = {
+ 0x186a, 0x30d4, 0x493e, 0x61a8, 0x7a12 , 0x927c}; /* 200ms-1200ms */
+
+/* End Rate adaptive parameters */
+
+static void odm_SetTxRPTTiming_8188E(
+ struct odm_dm_struct *dm_odm,
+ struct odm_ra_info *pRaInfo,
+ u8 extend
+ )
+{
+ u8 idx = 0;
+
+ for (idx = 0; idx < 5; idx++)
+ if (DynamicTxRPTTiming[idx] == pRaInfo->RptTime)
+ break;
+
+ if (extend == 0) { /* back to default timing */
+ idx = 0; /* 200ms */
+ } else if (extend == 1) {/* increase the timing */
+ idx += 1;
+ if (idx > 5)
+ idx = 5;
+ } else if (extend == 2) {/* decrease the timing */
+ if (idx != 0)
+ idx -= 1;
+ }
+ pRaInfo->RptTime = DynamicTxRPTTiming[idx];
+
+ ODM_RT_TRACE(dm_odm, ODM_COMP_RATE_ADAPTIVE, ODM_DBG_LOUD,
+ ("pRaInfo->RptTime = 0x%x\n", pRaInfo->RptTime));
+}
+
+static int odm_RateDown_8188E(struct odm_dm_struct *dm_odm,
+ struct odm_ra_info *pRaInfo)
+{
+ u8 RateID, LowestRate, HighestRate;
+ u8 i;
+
+ ODM_RT_TRACE(dm_odm, ODM_COMP_RATE_ADAPTIVE,
+ ODM_DBG_TRACE, ("=====>odm_RateDown_8188E()\n"));
+ if (NULL == pRaInfo) {
+ ODM_RT_TRACE(dm_odm, ODM_COMP_RATE_ADAPTIVE, ODM_DBG_LOUD,
+ ("odm_RateDown_8188E(): pRaInfo is NULL\n"));
+ return -1;
+ }
+ RateID = pRaInfo->PreRate;
+ LowestRate = pRaInfo->LowestRate;
+ HighestRate = pRaInfo->HighestRate;
+
+ ODM_RT_TRACE(dm_odm, ODM_COMP_RATE_ADAPTIVE, ODM_DBG_TRACE,
+ (" RateID =%d LowestRate =%d HighestRate =%d RateSGI =%d\n",
+ RateID, LowestRate, HighestRate, pRaInfo->RateSGI));
+ if (RateID > HighestRate) {
+ RateID = HighestRate;
+ } else if (pRaInfo->RateSGI) {
+ pRaInfo->RateSGI = 0;
+ } else if (RateID > LowestRate) {
+ if (RateID > 0) {
+ for (i = RateID-1; i > LowestRate; i--) {
+ if (pRaInfo->RAUseRate & BIT(i)) {
+ RateID = i;
+ goto RateDownFinish;
+ }
+ }
+ }
+ } else if (RateID <= LowestRate) {
+ RateID = LowestRate;
+ }
+RateDownFinish:
+ if (pRaInfo->RAWaitingCounter == 1) {
+ pRaInfo->RAWaitingCounter += 1;
+ pRaInfo->RAPendingCounter += 1;
+ } else if (pRaInfo->RAWaitingCounter == 0) {
+ ;
+ } else {
+ pRaInfo->RAWaitingCounter = 0;
+ pRaInfo->RAPendingCounter = 0;
+ }
+
+ if (pRaInfo->RAPendingCounter >= 4)
+ pRaInfo->RAPendingCounter = 4;
+
+ pRaInfo->DecisionRate = RateID;
+ odm_SetTxRPTTiming_8188E(dm_odm, pRaInfo, 2);
+ ODM_RT_TRACE(dm_odm, ODM_COMP_RATE_ADAPTIVE,
+ ODM_DBG_LOUD, ("Rate down, RPT Timing default\n"));
+ ODM_RT_TRACE(dm_odm, ODM_COMP_RATE_ADAPTIVE, ODM_DBG_TRACE,
+ ("RAWaitingCounter %d, RAPendingCounter %d",
+ pRaInfo->RAWaitingCounter, pRaInfo->RAPendingCounter));
+ ODM_RT_TRACE(dm_odm, ODM_COMP_RATE_ADAPTIVE, ODM_DBG_LOUD,
+ ("Rate down to RateID %d RateSGI %d\n", RateID, pRaInfo->RateSGI));
+ ODM_RT_TRACE(dm_odm, ODM_COMP_RATE_ADAPTIVE, ODM_DBG_TRACE,
+ ("<===== odm_RateDown_8188E()\n"));
+ return 0;
+}
+
+static int odm_RateUp_8188E(
+ struct odm_dm_struct *dm_odm,
+ struct odm_ra_info *pRaInfo
+ )
+{
+ u8 RateID, HighestRate;
+ u8 i;
+
+ ODM_RT_TRACE(dm_odm, ODM_COMP_RATE_ADAPTIVE,
+ ODM_DBG_TRACE, ("=====>odm_RateUp_8188E()\n"));
+ if (NULL == pRaInfo) {
+ ODM_RT_TRACE(dm_odm, ODM_COMP_RATE_ADAPTIVE, ODM_DBG_LOUD,
+ ("odm_RateUp_8188E(): pRaInfo is NULL\n"));
+ return -1;
+ }
+ RateID = pRaInfo->PreRate;
+ HighestRate = pRaInfo->HighestRate;
+ ODM_RT_TRACE(dm_odm, ODM_COMP_RATE_ADAPTIVE, ODM_DBG_TRACE,
+ (" RateID =%d HighestRate =%d\n",
+ RateID, HighestRate));
+ if (pRaInfo->RAWaitingCounter == 1) {
+ pRaInfo->RAWaitingCounter = 0;
+ pRaInfo->RAPendingCounter = 0;
+ } else if (pRaInfo->RAWaitingCounter > 1) {
+ pRaInfo->PreRssiStaRA = pRaInfo->RssiStaRA;
+ goto RateUpfinish;
+ }
+ odm_SetTxRPTTiming_8188E(dm_odm, pRaInfo, 0);
+ ODM_RT_TRACE(dm_odm, ODM_COMP_RATE_ADAPTIVE, ODM_DBG_LOUD,
+ ("odm_RateUp_8188E():Decrease RPT Timing\n"));
+
+ if (RateID < HighestRate) {
+ for (i = RateID+1; i <= HighestRate; i++) {
+ if (pRaInfo->RAUseRate & BIT(i)) {
+ RateID = i;
+ goto RateUpfinish;
+ }
+ }
+ } else if (RateID == HighestRate) {
+ if (pRaInfo->SGIEnable && (pRaInfo->RateSGI != 1))
+ pRaInfo->RateSGI = 1;
+ else if ((pRaInfo->SGIEnable) != 1)
+ pRaInfo->RateSGI = 0;
+ } else {
+ RateID = HighestRate;
+ }
+RateUpfinish:
+ if (pRaInfo->RAWaitingCounter ==
+ (4+PendingForRateUpFail[pRaInfo->RAPendingCounter]))
+ pRaInfo->RAWaitingCounter = 0;
+ else
+ pRaInfo->RAWaitingCounter++;
+
+ pRaInfo->DecisionRate = RateID;
+ ODM_RT_TRACE(dm_odm, ODM_COMP_RATE_ADAPTIVE, ODM_DBG_LOUD,
+ ("Rate up to RateID %d\n", RateID));
+ ODM_RT_TRACE(dm_odm, ODM_COMP_RATE_ADAPTIVE, ODM_DBG_TRACE,
+ ("RAWaitingCounter %d, RAPendingCounter %d",
+ pRaInfo->RAWaitingCounter, pRaInfo->RAPendingCounter));
+ ODM_RT_TRACE(dm_odm, ODM_COMP_RATE_ADAPTIVE,
+ ODM_DBG_TRACE, ("<===== odm_RateUp_8188E()\n"));
+ return 0;
+}
+
+static void odm_ResetRaCounter_8188E(struct odm_ra_info *pRaInfo)
+{
+ u8 RateID;
+
+ RateID = pRaInfo->DecisionRate;
+ pRaInfo->NscUp = (N_THRESHOLD_HIGH[RateID]+N_THRESHOLD_LOW[RateID])>>1;
+ pRaInfo->NscDown = (N_THRESHOLD_HIGH[RateID]+N_THRESHOLD_LOW[RateID])>>1;
+}
+
+static void odm_RateDecision_8188E(struct odm_dm_struct *dm_odm,
+ struct odm_ra_info *pRaInfo
+ )
+{
+ u8 RateID = 0, RtyPtID = 0, PenaltyID1 = 0, PenaltyID2 = 0, i = 0;
+ /* u32 pool_retry; */
+ static u8 DynamicTxRPTTimingCounter;
+
+ ODM_RT_TRACE(dm_odm, ODM_COMP_RATE_ADAPTIVE, ODM_DBG_TRACE,
+ ("=====>odm_RateDecision_8188E()\n"));
+
+ if (pRaInfo->Active && (pRaInfo->TOTAL > 0)) { /* STA used and data packet exits */
+ if ((pRaInfo->RssiStaRA < (pRaInfo->PreRssiStaRA - 3)) ||
+ (pRaInfo->RssiStaRA > (pRaInfo->PreRssiStaRA + 3))) {
+ pRaInfo->RAWaitingCounter = 0;
+ pRaInfo->RAPendingCounter = 0;
+ }
+ /* Start RA decision */
+ if (pRaInfo->PreRate > pRaInfo->HighestRate)
+ RateID = pRaInfo->HighestRate;
+ else
+ RateID = pRaInfo->PreRate;
+ if (pRaInfo->RssiStaRA > RSSI_THRESHOLD[RateID])
+ RtyPtID = 0;
+ else
+ RtyPtID = 1;
+ PenaltyID1 = RETRY_PENALTY_IDX[RtyPtID][RateID]; /* TODO by page */
+
+ ODM_RT_TRACE(dm_odm, ODM_COMP_RATE_ADAPTIVE, ODM_DBG_TRACE,
+ (" NscDown init is %d\n", pRaInfo->NscDown));
+
+ for (i = 0 ; i <= 4 ; i++)
+ pRaInfo->NscDown += pRaInfo->RTY[i] * RETRY_PENALTY[PenaltyID1][i];
+
+ ODM_RT_TRACE(dm_odm, ODM_COMP_RATE_ADAPTIVE, ODM_DBG_TRACE,
+ (" NscDown is %d, total*penalty[5] is %d\n", pRaInfo->NscDown,
+ (pRaInfo->TOTAL * RETRY_PENALTY[PenaltyID1][5])));
+
+ if (pRaInfo->NscDown > (pRaInfo->TOTAL * RETRY_PENALTY[PenaltyID1][5]))
+ pRaInfo->NscDown -= pRaInfo->TOTAL * RETRY_PENALTY[PenaltyID1][5];
+ else
+ pRaInfo->NscDown = 0;
+
+ /* rate up */
+ PenaltyID2 = RETRY_PENALTY_UP_IDX[RateID];
+ ODM_RT_TRACE(dm_odm, ODM_COMP_RATE_ADAPTIVE, ODM_DBG_TRACE,
+ (" NscUp init is %d\n", pRaInfo->NscUp));
+
+ for (i = 0 ; i <= 4 ; i++)
+ pRaInfo->NscUp += pRaInfo->RTY[i] * RETRY_PENALTY[PenaltyID2][i];
+
+ ODM_RT_TRACE(dm_odm, ODM_COMP_RATE_ADAPTIVE, ODM_DBG_TRACE,
+ ("NscUp is %d, total*up[5] is %d\n",
+ pRaInfo->NscUp, (pRaInfo->TOTAL * RETRY_PENALTY[PenaltyID2][5])));
+
+ if (pRaInfo->NscUp > (pRaInfo->TOTAL * RETRY_PENALTY[PenaltyID2][5]))
+ pRaInfo->NscUp -= pRaInfo->TOTAL * RETRY_PENALTY[PenaltyID2][5];
+ else
+ pRaInfo->NscUp = 0;
+
+ ODM_RT_TRACE(dm_odm, ODM_COMP_RATE_ADAPTIVE|ODM_COMP_INIT, ODM_DBG_LOUD,
+ (" RssiStaRa = %d RtyPtID =%d PenaltyID1 = 0x%x PenaltyID2 = 0x%x RateID =%d NscDown =%d NscUp =%d SGI =%d\n",
+ pRaInfo->RssiStaRA, RtyPtID, PenaltyID1, PenaltyID2, RateID, pRaInfo->NscDown, pRaInfo->NscUp, pRaInfo->RateSGI));
+ if ((pRaInfo->NscDown < N_THRESHOLD_LOW[RateID]) ||
+ (pRaInfo->DROP > DROPING_NECESSARY[RateID]))
+ odm_RateDown_8188E(dm_odm, pRaInfo);
+ else if (pRaInfo->NscUp > N_THRESHOLD_HIGH[RateID])
+ odm_RateUp_8188E(dm_odm, pRaInfo);
+
+ if (pRaInfo->DecisionRate > pRaInfo->HighestRate)
+ pRaInfo->DecisionRate = pRaInfo->HighestRate;
+
+ if ((pRaInfo->DecisionRate) == (pRaInfo->PreRate))
+ DynamicTxRPTTimingCounter += 1;
+ else
+ DynamicTxRPTTimingCounter = 0;
+
+ if (DynamicTxRPTTimingCounter >= 4) {
+ odm_SetTxRPTTiming_8188E(dm_odm, pRaInfo, 1);
+ ODM_RT_TRACE(dm_odm, ODM_COMP_RATE_ADAPTIVE,
+ ODM_DBG_LOUD, ("<===== Rate don't change 4 times, Extend RPT Timing\n"));
+ DynamicTxRPTTimingCounter = 0;
+ }
+
+ pRaInfo->PreRate = pRaInfo->DecisionRate; /* YJ, add, 120120 */
+
+ odm_ResetRaCounter_8188E(pRaInfo);
+ }
+ ODM_RT_TRACE(dm_odm, ODM_COMP_RATE_ADAPTIVE, ODM_DBG_TRACE, ("<===== odm_RateDecision_8188E()\n"));
+}
+
+static int odm_ARFBRefresh_8188E(struct odm_dm_struct *dm_odm, struct odm_ra_info *pRaInfo)
+{ /* Wilson 2011/10/26 */
+ struct adapter *adapt = dm_odm->Adapter;
+ u32 MaskFromReg;
+ s8 i;
+
+ switch (pRaInfo->RateID) {
+ case RATR_INX_WIRELESS_NGB:
+ pRaInfo->RAUseRate = (pRaInfo->RateMask)&0x0f8ff015;
+ break;
+ case RATR_INX_WIRELESS_NG:
+ pRaInfo->RAUseRate = (pRaInfo->RateMask)&0x0f8ff010;
+ break;
+ case RATR_INX_WIRELESS_NB:
+ pRaInfo->RAUseRate = (pRaInfo->RateMask)&0x0f8ff005;
+ break;
+ case RATR_INX_WIRELESS_N:
+ pRaInfo->RAUseRate = (pRaInfo->RateMask)&0x0f8ff000;
+ break;
+ case RATR_INX_WIRELESS_GB:
+ pRaInfo->RAUseRate = (pRaInfo->RateMask)&0x00000ff5;
+ break;
+ case RATR_INX_WIRELESS_G:
+ pRaInfo->RAUseRate = (pRaInfo->RateMask)&0x00000ff0;
+ break;
+ case RATR_INX_WIRELESS_B:
+ pRaInfo->RAUseRate = (pRaInfo->RateMask)&0x0000000d;
+ break;
+ case 12:
+ MaskFromReg = usb_read32(adapt, REG_ARFR0);
+ pRaInfo->RAUseRate = (pRaInfo->RateMask)&MaskFromReg;
+ break;
+ case 13:
+ MaskFromReg = usb_read32(adapt, REG_ARFR1);
+ pRaInfo->RAUseRate = (pRaInfo->RateMask)&MaskFromReg;
+ break;
+ case 14:
+ MaskFromReg = usb_read32(adapt, REG_ARFR2);
+ pRaInfo->RAUseRate = (pRaInfo->RateMask)&MaskFromReg;
+ break;
+ case 15:
+ MaskFromReg = usb_read32(adapt, REG_ARFR3);
+ pRaInfo->RAUseRate = (pRaInfo->RateMask)&MaskFromReg;
+ break;
+ default:
+ pRaInfo->RAUseRate = (pRaInfo->RateMask);
+ break;
+ }
+ /* Highest rate */
+ if (pRaInfo->RAUseRate) {
+ for (i = RATESIZE; i >= 0; i--) {
+ if ((pRaInfo->RAUseRate)&BIT(i)) {
+ pRaInfo->HighestRate = i;
+ break;
+ }
+ }
+ } else {
+ pRaInfo->HighestRate = 0;
+ }
+ /* Lowest rate */
+ if (pRaInfo->RAUseRate) {
+ for (i = 0; i < RATESIZE; i++) {
+ if ((pRaInfo->RAUseRate) & BIT(i)) {
+ pRaInfo->LowestRate = i;
+ break;
+ }
+ }
+ } else {
+ pRaInfo->LowestRate = 0;
+ }
+ if (pRaInfo->HighestRate > 0x13)
+ pRaInfo->PTModeSS = 3;
+ else if (pRaInfo->HighestRate > 0x0b)
+ pRaInfo->PTModeSS = 2;
+ else if (pRaInfo->HighestRate > 0x0b)
+ pRaInfo->PTModeSS = 1;
+ else
+ pRaInfo->PTModeSS = 0;
+ ODM_RT_TRACE(dm_odm, ODM_COMP_RATE_ADAPTIVE, ODM_DBG_LOUD,
+ ("ODM_ARFBRefresh_8188E(): PTModeSS =%d\n", pRaInfo->PTModeSS));
+
+ if (pRaInfo->DecisionRate > pRaInfo->HighestRate)
+ pRaInfo->DecisionRate = pRaInfo->HighestRate;
+
+ ODM_RT_TRACE(dm_odm, ODM_COMP_RATE_ADAPTIVE, ODM_DBG_LOUD,
+ ("ODM_ARFBRefresh_8188E(): RateID =%d RateMask =%8.8x RAUseRate =%8.8x HighestRate =%d, DecisionRate =%d\n",
+ pRaInfo->RateID, pRaInfo->RateMask, pRaInfo->RAUseRate, pRaInfo->HighestRate, pRaInfo->DecisionRate));
+ return 0;
+}
+
+static void odm_PTTryState_8188E(struct odm_ra_info *pRaInfo)
+{
+ pRaInfo->PTTryState = 0;
+ switch (pRaInfo->PTModeSS) {
+ case 3:
+ if (pRaInfo->DecisionRate >= 0x19)
+ pRaInfo->PTTryState = 1;
+ break;
+ case 2:
+ if (pRaInfo->DecisionRate >= 0x11)
+ pRaInfo->PTTryState = 1;
+ break;
+ case 1:
+ if (pRaInfo->DecisionRate >= 0x0a)
+ pRaInfo->PTTryState = 1;
+ break;
+ case 0:
+ if (pRaInfo->DecisionRate >= 0x03)
+ pRaInfo->PTTryState = 1;
+ break;
+ default:
+ pRaInfo->PTTryState = 0;
+ break;
+ }
+
+ if (pRaInfo->RssiStaRA < 48) {
+ pRaInfo->PTStage = 0;
+ } else if (pRaInfo->PTTryState == 1) {
+ if ((pRaInfo->PTStopCount >= 10) ||
+ (pRaInfo->PTPreRssi > pRaInfo->RssiStaRA + 5) ||
+ (pRaInfo->PTPreRssi < pRaInfo->RssiStaRA - 5) ||
+ (pRaInfo->DecisionRate != pRaInfo->PTPreRate)) {
+ if (pRaInfo->PTStage == 0)
+ pRaInfo->PTStage = 1;
+ else if (pRaInfo->PTStage == 1)
+ pRaInfo->PTStage = 3;
+ else
+ pRaInfo->PTStage = 5;
+
+ pRaInfo->PTPreRssi = pRaInfo->RssiStaRA;
+ pRaInfo->PTStopCount = 0;
+ } else {
+ pRaInfo->RAstage = 0;
+ pRaInfo->PTStopCount++;
+ }
+ } else {
+ pRaInfo->PTStage = 0;
+ pRaInfo->RAstage = 0;
+ }
+ pRaInfo->PTPreRate = pRaInfo->DecisionRate;
+}
+
+static void odm_PTDecision_8188E(struct odm_ra_info *pRaInfo)
+{
+ u8 j;
+ u8 temp_stage;
+ u32 numsc;
+ u32 num_total;
+ u8 stage_id;
+
+ numsc = 0;
+ num_total = pRaInfo->TOTAL * PT_PENALTY[5];
+ for (j = 0; j <= 4; j++) {
+ numsc += pRaInfo->RTY[j] * PT_PENALTY[j];
+ if (numsc > num_total)
+ break;
+ }
+
+ j >>= 1;
+ temp_stage = (pRaInfo->PTStage + 1) >> 1;
+ if (temp_stage > j)
+ stage_id = temp_stage-j;
+ else
+ stage_id = 0;
+
+ pRaInfo->PTSmoothFactor = (pRaInfo->PTSmoothFactor>>1) + (pRaInfo->PTSmoothFactor>>2) + stage_id*16+2;
+ if (pRaInfo->PTSmoothFactor > 192)
+ pRaInfo->PTSmoothFactor = 192;
+ stage_id = pRaInfo->PTSmoothFactor >> 6;
+ temp_stage = stage_id*2;
+ if (temp_stage != 0)
+ temp_stage -= 1;
+ if (pRaInfo->DROP > 3)
+ temp_stage = 0;
+ pRaInfo->PTStage = temp_stage;
+}
+
+static void
+odm_RATxRPTTimerSetting(
+ struct odm_dm_struct *dm_odm,
+ u16 minRptTime
+)
+{
+ ODM_RT_TRACE(dm_odm, ODM_COMP_RATE_ADAPTIVE, ODM_DBG_TRACE, (" =====>odm_RATxRPTTimerSetting()\n"));
+
+ if (dm_odm->CurrminRptTime != minRptTime) {
+ ODM_RT_TRACE(dm_odm, ODM_COMP_RATE_ADAPTIVE, ODM_DBG_LOUD,
+ (" CurrminRptTime = 0x%04x minRptTime = 0x%04x\n", dm_odm->CurrminRptTime, minRptTime));
+ rtw_rpt_timer_cfg_cmd(dm_odm->Adapter, minRptTime);
+ dm_odm->CurrminRptTime = minRptTime;
+ }
+ ODM_RT_TRACE(dm_odm, ODM_COMP_RATE_ADAPTIVE, ODM_DBG_TRACE, (" <===== odm_RATxRPTTimerSetting()\n"));
+}
+
+void
+ODM_RASupport_Init(
+ struct odm_dm_struct *dm_odm
+ )
+{
+ ODM_RT_TRACE(dm_odm, ODM_COMP_RATE_ADAPTIVE, ODM_DBG_LOUD, ("=====>ODM_RASupport_Init()\n"));
+
+ dm_odm->RaSupport88E = true;
+}
+
+int ODM_RAInfo_Init(struct odm_dm_struct *dm_odm, u8 macid)
+{
+ struct odm_ra_info *pRaInfo = &dm_odm->RAInfo[macid];
+ u8 WirelessMode = 0xFF; /* invalid value */
+ u8 max_rate_idx = 0x13; /* MCS7 */
+
+ if (dm_odm->pWirelessMode != NULL)
+ WirelessMode = *(dm_odm->pWirelessMode);
+
+ if (WirelessMode != 0xFF) {
+ if (WirelessMode & ODM_WM_N24G)
+ max_rate_idx = 0x13;
+ else if (WirelessMode & ODM_WM_G)
+ max_rate_idx = 0x0b;
+ else if (WirelessMode & ODM_WM_B)
+ max_rate_idx = 0x03;
+ }
+
+ ODM_RT_TRACE(dm_odm, ODM_COMP_RATE_ADAPTIVE, ODM_DBG_LOUD,
+ ("ODM_RAInfo_Init(): WirelessMode:0x%08x , max_raid_idx:0x%02x\n",
+ WirelessMode, max_rate_idx));
+
+ pRaInfo->DecisionRate = max_rate_idx;
+ pRaInfo->PreRate = max_rate_idx;
+ pRaInfo->HighestRate = max_rate_idx;
+ pRaInfo->LowestRate = 0;
+ pRaInfo->RateID = 0;
+ pRaInfo->RateMask = 0xffffffff;
+ pRaInfo->RssiStaRA = 0;
+ pRaInfo->PreRssiStaRA = 0;
+ pRaInfo->SGIEnable = 0;
+ pRaInfo->RAUseRate = 0xffffffff;
+ pRaInfo->NscDown = (N_THRESHOLD_HIGH[0x13]+N_THRESHOLD_LOW[0x13])/2;
+ pRaInfo->NscUp = (N_THRESHOLD_HIGH[0x13]+N_THRESHOLD_LOW[0x13])/2;
+ pRaInfo->RateSGI = 0;
+ pRaInfo->Active = 1; /* Active is not used at present. by page, 110819 */
+ pRaInfo->RptTime = 0x927c;
+ pRaInfo->DROP = 0;
+ pRaInfo->RTY[0] = 0;
+ pRaInfo->RTY[1] = 0;
+ pRaInfo->RTY[2] = 0;
+ pRaInfo->RTY[3] = 0;
+ pRaInfo->RTY[4] = 0;
+ pRaInfo->TOTAL = 0;
+ pRaInfo->RAWaitingCounter = 0;
+ pRaInfo->RAPendingCounter = 0;
+ pRaInfo->PTActive = 1; /* Active when this STA is use */
+ pRaInfo->PTTryState = 0;
+ pRaInfo->PTStage = 5; /* Need to fill into HW_PWR_STATUS */
+ pRaInfo->PTSmoothFactor = 192;
+ pRaInfo->PTStopCount = 0;
+ pRaInfo->PTPreRate = 0;
+ pRaInfo->PTPreRssi = 0;
+ pRaInfo->PTModeSS = 0;
+ pRaInfo->RAstage = 0;
+ return 0;
+}
+
+int ODM_RAInfo_Init_all(struct odm_dm_struct *dm_odm)
+{
+ u8 macid = 0;
+
+ ODM_RT_TRACE(dm_odm, ODM_COMP_RATE_ADAPTIVE, ODM_DBG_LOUD, ("=====>\n"));
+ dm_odm->CurrminRptTime = 0;
+
+ for (macid = 0; macid < ODM_ASSOCIATE_ENTRY_NUM; macid++)
+ ODM_RAInfo_Init(dm_odm, macid);
+
+ return 0;
+}
+
+u8 ODM_RA_GetShortGI_8188E(struct odm_dm_struct *dm_odm, u8 macid)
+{
+ if ((NULL == dm_odm) || (macid >= ASSOCIATE_ENTRY_NUM))
+ return 0;
+ ODM_RT_TRACE(dm_odm, ODM_COMP_RATE_ADAPTIVE, ODM_DBG_TRACE,
+ ("macid =%d SGI =%d\n", macid, dm_odm->RAInfo[macid].RateSGI));
+ return dm_odm->RAInfo[macid].RateSGI;
+}
+
+u8 ODM_RA_GetDecisionRate_8188E(struct odm_dm_struct *dm_odm, u8 macid)
+{
+ u8 DecisionRate = 0;
+
+ if ((NULL == dm_odm) || (macid >= ASSOCIATE_ENTRY_NUM))
+ return 0;
+ DecisionRate = dm_odm->RAInfo[macid].DecisionRate;
+ ODM_RT_TRACE(dm_odm, ODM_COMP_RATE_ADAPTIVE, ODM_DBG_TRACE,
+ (" macid =%d DecisionRate = 0x%x\n", macid, DecisionRate));
+ return DecisionRate;
+}
+
+u8 ODM_RA_GetHwPwrStatus_8188E(struct odm_dm_struct *dm_odm, u8 macid)
+{
+ u8 PTStage = 5;
+
+ if ((NULL == dm_odm) || (macid >= ASSOCIATE_ENTRY_NUM))
+ return 0;
+ PTStage = dm_odm->RAInfo[macid].PTStage;
+ ODM_RT_TRACE(dm_odm, ODM_COMP_RATE_ADAPTIVE, ODM_DBG_TRACE,
+ ("macid =%d PTStage = 0x%x\n", macid, PTStage));
+ return PTStage;
+}
+
+void ODM_RA_UpdateRateInfo_8188E(struct odm_dm_struct *dm_odm, u8 macid, u8 RateID, u32 RateMask, u8 SGIEnable)
+{
+ struct odm_ra_info *pRaInfo = NULL;
+
+ ODM_RT_TRACE(dm_odm, ODM_COMP_RATE_ADAPTIVE, ODM_DBG_LOUD,
+ ("macid =%d RateID = 0x%x RateMask = 0x%x SGIEnable =%d\n",
+ macid, RateID, RateMask, SGIEnable));
+ if ((NULL == dm_odm) || (macid >= ASSOCIATE_ENTRY_NUM))
+ return;
+
+ pRaInfo = &(dm_odm->RAInfo[macid]);
+ pRaInfo->RateID = RateID;
+ pRaInfo->RateMask = RateMask;
+ pRaInfo->SGIEnable = SGIEnable;
+ odm_ARFBRefresh_8188E(dm_odm, pRaInfo);
+}
+
+void ODM_RA_SetRSSI_8188E(struct odm_dm_struct *dm_odm, u8 macid, u8 Rssi)
+{
+ struct odm_ra_info *pRaInfo = NULL;
+
+ ODM_RT_TRACE(dm_odm, ODM_COMP_RATE_ADAPTIVE, ODM_DBG_TRACE,
+ (" macid =%d Rssi =%d\n", macid, Rssi));
+ if ((NULL == dm_odm) || (macid >= ASSOCIATE_ENTRY_NUM))
+ return;
+
+ pRaInfo = &(dm_odm->RAInfo[macid]);
+ pRaInfo->RssiStaRA = Rssi;
+}
+
+void ODM_RA_Set_TxRPT_Time(struct odm_dm_struct *dm_odm, u16 minRptTime)
+{
+ struct adapter *adapt = dm_odm->Adapter;
+
+ usb_write16(adapt, REG_TX_RPT_TIME, minRptTime);
+}
+
+void ODM_RA_TxRPT2Handle_8188E(struct odm_dm_struct *dm_odm, u8 *TxRPT_Buf, u16 TxRPT_Len, u32 macid_entry0, u32 macid_entry1)
+{
+ struct odm_ra_info *pRAInfo = NULL;
+ u8 MacId = 0;
+ u8 *pBuffer = NULL;
+ u32 valid = 0, ItemNum = 0;
+ u16 minRptTime = 0x927c;
+
+ ODM_RT_TRACE(dm_odm, ODM_COMP_RATE_ADAPTIVE, ODM_DBG_LOUD,
+ ("=====>ODM_RA_TxRPT2Handle_8188E(): valid0 =%d valid1 =%d BufferLength =%d\n",
+ macid_entry0, macid_entry1, TxRPT_Len));
+
+ ItemNum = TxRPT_Len >> 3;
+ pBuffer = TxRPT_Buf;
+
+ do {
+ if (MacId >= ASSOCIATE_ENTRY_NUM)
+ valid = 0;
+ else if (MacId >= 32)
+ valid = (1 << (MacId - 32)) & macid_entry1;
+ else
+ valid = (1 << MacId) & macid_entry0;
+
+ pRAInfo = &(dm_odm->RAInfo[MacId]);
+ if (valid) {
+ pRAInfo->RTY[0] = (u16)GET_TX_REPORT_TYPE1_RERTY_0(pBuffer);
+ pRAInfo->RTY[1] = (u16)GET_TX_REPORT_TYPE1_RERTY_1(pBuffer);
+ pRAInfo->RTY[2] = (u16)GET_TX_REPORT_TYPE1_RERTY_2(pBuffer);
+ pRAInfo->RTY[3] = (u16)GET_TX_REPORT_TYPE1_RERTY_3(pBuffer);
+ pRAInfo->RTY[4] = (u16)GET_TX_REPORT_TYPE1_RERTY_4(pBuffer);
+ pRAInfo->DROP = (u16)GET_TX_REPORT_TYPE1_DROP_0(pBuffer);
+ pRAInfo->TOTAL = pRAInfo->RTY[0] + pRAInfo->RTY[1] +
+ pRAInfo->RTY[2] + pRAInfo->RTY[3] +
+ pRAInfo->RTY[4] + pRAInfo->DROP;
+ if (pRAInfo->TOTAL != 0) {
+ ODM_RT_TRACE(dm_odm, ODM_COMP_RATE_ADAPTIVE, ODM_DBG_LOUD,
+ ("macid =%d Total =%d R0 =%d R1 =%d R2 =%d R3 =%d R4 =%d D0 =%d valid0 =%x valid1 =%x\n",
+ MacId, pRAInfo->TOTAL,
+ pRAInfo->RTY[0], pRAInfo->RTY[1],
+ pRAInfo->RTY[2], pRAInfo->RTY[3],
+ pRAInfo->RTY[4], pRAInfo->DROP,
+ macid_entry0 , macid_entry1));
+ if (pRAInfo->PTActive) {
+ if (pRAInfo->RAstage < 5)
+ odm_RateDecision_8188E(dm_odm, pRAInfo);
+ else if (pRAInfo->RAstage == 5) /* Power training try state */
+ odm_PTTryState_8188E(pRAInfo);
+ else /* RAstage == 6 */
+ odm_PTDecision_8188E(pRAInfo);
+
+ /* Stage_RA counter */
+ if (pRAInfo->RAstage <= 5)
+ pRAInfo->RAstage++;
+ else
+ pRAInfo->RAstage = 0;
+ } else {
+ odm_RateDecision_8188E(dm_odm, pRAInfo);
+ }
+ ODM_RT_TRACE(dm_odm, ODM_COMP_INIT, ODM_DBG_LOUD,
+ ("macid =%d R0 =%d R1 =%d R2 =%d R3 =%d R4 =%d drop =%d valid0 =%x RateID =%d SGI =%d\n",
+ MacId,
+ pRAInfo->RTY[0],
+ pRAInfo->RTY[1],
+ pRAInfo->RTY[2],
+ pRAInfo->RTY[3],
+ pRAInfo->RTY[4],
+ pRAInfo->DROP,
+ macid_entry0,
+ pRAInfo->DecisionRate,
+ pRAInfo->RateSGI));
+ } else {
+ ODM_RT_TRACE(dm_odm, ODM_COMP_RATE_ADAPTIVE, ODM_DBG_LOUD, (" TOTAL = 0!!!!\n"));
+ }
+ }
+
+ if (minRptTime > pRAInfo->RptTime)
+ minRptTime = pRAInfo->RptTime;
+
+ pBuffer += TX_RPT2_ITEM_SIZE;
+ MacId++;
+ } while (MacId < ItemNum);
+
+ odm_RATxRPTTimerSetting(dm_odm, minRptTime);
+
+ ODM_RT_TRACE(dm_odm, ODM_COMP_RATE_ADAPTIVE, ODM_DBG_LOUD, ("<===== ODM_RA_TxRPT2Handle_8188E()\n"));
+}
diff --git a/drivers/staging/rtl8188eu/hal/bb_cfg.c b/drivers/staging/rtl8188eu/hal/bb_cfg.c
new file mode 100644
index 000000000..8eb2b39a0
--- /dev/null
+++ b/drivers/staging/rtl8188eu/hal/bb_cfg.c
@@ -0,0 +1,730 @@
+/******************************************************************************
+*
+* Copyright(c) 2007 - 2011 Realtek Corporation. All rights reserved.
+*
+* This program is free software; you can redistribute it and/or modify it
+* under the terms of version 2 of the GNU General Public License as
+* published by the Free Software Foundation.
+*
+* This program is distributed in the hope that it will be useful, but WITHOUT
+* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+* more details.
+*
+* You should have received a copy of the GNU General Public License along with
+* this program; if not, write to the Free Software Foundation, Inc.,
+* 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+*
+*
+******************************************************************************/
+
+#include "odm_precomp.h"
+
+#include <phy.h>
+
+#define read_next_pair(array, v1, v2, i) \
+ do { \
+ i += 2; \
+ v1 = array[i]; \
+ v2 = array[i+1]; \
+ } while (0)
+
+
+/* AGC_TAB_1T.TXT */
+
+static u32 array_agc_tab_1t_8188e[] = {
+ 0xC78, 0xFB000001,
+ 0xC78, 0xFB010001,
+ 0xC78, 0xFB020001,
+ 0xC78, 0xFB030001,
+ 0xC78, 0xFB040001,
+ 0xC78, 0xFB050001,
+ 0xC78, 0xFA060001,
+ 0xC78, 0xF9070001,
+ 0xC78, 0xF8080001,
+ 0xC78, 0xF7090001,
+ 0xC78, 0xF60A0001,
+ 0xC78, 0xF50B0001,
+ 0xC78, 0xF40C0001,
+ 0xC78, 0xF30D0001,
+ 0xC78, 0xF20E0001,
+ 0xC78, 0xF10F0001,
+ 0xC78, 0xF0100001,
+ 0xC78, 0xEF110001,
+ 0xC78, 0xEE120001,
+ 0xC78, 0xED130001,
+ 0xC78, 0xEC140001,
+ 0xC78, 0xEB150001,
+ 0xC78, 0xEA160001,
+ 0xC78, 0xE9170001,
+ 0xC78, 0xE8180001,
+ 0xC78, 0xE7190001,
+ 0xC78, 0xE61A0001,
+ 0xC78, 0xE51B0001,
+ 0xC78, 0xE41C0001,
+ 0xC78, 0xE31D0001,
+ 0xC78, 0xE21E0001,
+ 0xC78, 0xE11F0001,
+ 0xC78, 0x8A200001,
+ 0xC78, 0x89210001,
+ 0xC78, 0x88220001,
+ 0xC78, 0x87230001,
+ 0xC78, 0x86240001,
+ 0xC78, 0x85250001,
+ 0xC78, 0x84260001,
+ 0xC78, 0x83270001,
+ 0xC78, 0x82280001,
+ 0xC78, 0x6B290001,
+ 0xC78, 0x6A2A0001,
+ 0xC78, 0x692B0001,
+ 0xC78, 0x682C0001,
+ 0xC78, 0x672D0001,
+ 0xC78, 0x662E0001,
+ 0xC78, 0x652F0001,
+ 0xC78, 0x64300001,
+ 0xC78, 0x63310001,
+ 0xC78, 0x62320001,
+ 0xC78, 0x61330001,
+ 0xC78, 0x46340001,
+ 0xC78, 0x45350001,
+ 0xC78, 0x44360001,
+ 0xC78, 0x43370001,
+ 0xC78, 0x42380001,
+ 0xC78, 0x41390001,
+ 0xC78, 0x403A0001,
+ 0xC78, 0x403B0001,
+ 0xC78, 0x403C0001,
+ 0xC78, 0x403D0001,
+ 0xC78, 0x403E0001,
+ 0xC78, 0x403F0001,
+ 0xC78, 0xFB400001,
+ 0xC78, 0xFB410001,
+ 0xC78, 0xFB420001,
+ 0xC78, 0xFB430001,
+ 0xC78, 0xFB440001,
+ 0xC78, 0xFB450001,
+ 0xC78, 0xFB460001,
+ 0xC78, 0xFB470001,
+ 0xC78, 0xFB480001,
+ 0xC78, 0xFA490001,
+ 0xC78, 0xF94A0001,
+ 0xC78, 0xF84B0001,
+ 0xC78, 0xF74C0001,
+ 0xC78, 0xF64D0001,
+ 0xC78, 0xF54E0001,
+ 0xC78, 0xF44F0001,
+ 0xC78, 0xF3500001,
+ 0xC78, 0xF2510001,
+ 0xC78, 0xF1520001,
+ 0xC78, 0xF0530001,
+ 0xC78, 0xEF540001,
+ 0xC78, 0xEE550001,
+ 0xC78, 0xED560001,
+ 0xC78, 0xEC570001,
+ 0xC78, 0xEB580001,
+ 0xC78, 0xEA590001,
+ 0xC78, 0xE95A0001,
+ 0xC78, 0xE85B0001,
+ 0xC78, 0xE75C0001,
+ 0xC78, 0xE65D0001,
+ 0xC78, 0xE55E0001,
+ 0xC78, 0xE45F0001,
+ 0xC78, 0xE3600001,
+ 0xC78, 0xE2610001,
+ 0xC78, 0xC3620001,
+ 0xC78, 0xC2630001,
+ 0xC78, 0xC1640001,
+ 0xC78, 0x8B650001,
+ 0xC78, 0x8A660001,
+ 0xC78, 0x89670001,
+ 0xC78, 0x88680001,
+ 0xC78, 0x87690001,
+ 0xC78, 0x866A0001,
+ 0xC78, 0x856B0001,
+ 0xC78, 0x846C0001,
+ 0xC78, 0x676D0001,
+ 0xC78, 0x666E0001,
+ 0xC78, 0x656F0001,
+ 0xC78, 0x64700001,
+ 0xC78, 0x63710001,
+ 0xC78, 0x62720001,
+ 0xC78, 0x61730001,
+ 0xC78, 0x60740001,
+ 0xC78, 0x46750001,
+ 0xC78, 0x45760001,
+ 0xC78, 0x44770001,
+ 0xC78, 0x43780001,
+ 0xC78, 0x42790001,
+ 0xC78, 0x417A0001,
+ 0xC78, 0x407B0001,
+ 0xC78, 0x407C0001,
+ 0xC78, 0x407D0001,
+ 0xC78, 0x407E0001,
+ 0xC78, 0x407F0001,
+};
+
+static bool set_baseband_agc_config(struct adapter *adapt)
+{
+ u32 i;
+ u32 arraylen = sizeof(array_agc_tab_1t_8188e)/sizeof(u32);
+ u32 *array = array_agc_tab_1t_8188e;
+
+ for (i = 0; i < arraylen; i += 2) {
+ u32 v1 = array[i];
+ u32 v2 = array[i+1];
+
+ if (v1 < 0xCDCDCDCD) {
+ phy_set_bb_reg(adapt, v1, bMaskDWord, v2);
+ udelay(1);
+ }
+ }
+ return true;
+}
+
+/* PHY_REG_1T.TXT */
+
+static u32 array_phy_reg_1t_8188e[] = {
+ 0x800, 0x80040000,
+ 0x804, 0x00000003,
+ 0x808, 0x0000FC00,
+ 0x80C, 0x0000000A,
+ 0x810, 0x10001331,
+ 0x814, 0x020C3D10,
+ 0x818, 0x02200385,
+ 0x81C, 0x00000000,
+ 0x820, 0x01000100,
+ 0x824, 0x00390204,
+ 0x828, 0x00000000,
+ 0x82C, 0x00000000,
+ 0x830, 0x00000000,
+ 0x834, 0x00000000,
+ 0x838, 0x00000000,
+ 0x83C, 0x00000000,
+ 0x840, 0x00010000,
+ 0x844, 0x00000000,
+ 0x848, 0x00000000,
+ 0x84C, 0x00000000,
+ 0x850, 0x00000000,
+ 0x854, 0x00000000,
+ 0x858, 0x569A11A9,
+ 0x85C, 0x01000014,
+ 0x860, 0x66F60110,
+ 0x864, 0x061F0649,
+ 0x868, 0x00000000,
+ 0x86C, 0x27272700,
+ 0x870, 0x07000760,
+ 0x874, 0x25004000,
+ 0x878, 0x00000808,
+ 0x87C, 0x00000000,
+ 0x880, 0xB0000C1C,
+ 0x884, 0x00000001,
+ 0x888, 0x00000000,
+ 0x88C, 0xCCC000C0,
+ 0x890, 0x00000800,
+ 0x894, 0xFFFFFFFE,
+ 0x898, 0x40302010,
+ 0x89C, 0x00706050,
+ 0x900, 0x00000000,
+ 0x904, 0x00000023,
+ 0x908, 0x00000000,
+ 0x90C, 0x81121111,
+ 0x910, 0x00000002,
+ 0x914, 0x00000201,
+ 0xA00, 0x00D047C8,
+ 0xA04, 0x80FF000C,
+ 0xA08, 0x8C838300,
+ 0xA0C, 0x2E7F120F,
+ 0xA10, 0x9500BB78,
+ 0xA14, 0x1114D028,
+ 0xA18, 0x00881117,
+ 0xA1C, 0x89140F00,
+ 0xA20, 0x1A1B0000,
+ 0xA24, 0x090E1317,
+ 0xA28, 0x00000204,
+ 0xA2C, 0x00D30000,
+ 0xA70, 0x101FBF00,
+ 0xA74, 0x00000007,
+ 0xA78, 0x00000900,
+ 0xA7C, 0x225B0606,
+ 0xA80, 0x218075B1,
+ 0xB2C, 0x80000000,
+ 0xC00, 0x48071D40,
+ 0xC04, 0x03A05611,
+ 0xC08, 0x000000E4,
+ 0xC0C, 0x6C6C6C6C,
+ 0xC10, 0x08800000,
+ 0xC14, 0x40000100,
+ 0xC18, 0x08800000,
+ 0xC1C, 0x40000100,
+ 0xC20, 0x00000000,
+ 0xC24, 0x00000000,
+ 0xC28, 0x00000000,
+ 0xC2C, 0x00000000,
+ 0xC30, 0x69E9AC47,
+ 0xC34, 0x469652AF,
+ 0xC38, 0x49795994,
+ 0xC3C, 0x0A97971C,
+ 0xC40, 0x1F7C403F,
+ 0xC44, 0x000100B7,
+ 0xC48, 0xEC020107,
+ 0xC4C, 0x007F037F,
+ 0xC50, 0x69553420,
+ 0xC54, 0x43BC0094,
+ 0xC58, 0x00013169,
+ 0xC5C, 0x00250492,
+ 0xC60, 0x00000000,
+ 0xC64, 0x7112848B,
+ 0xC68, 0x47C00BFF,
+ 0xC6C, 0x00000036,
+ 0xC70, 0x2C7F000D,
+ 0xC74, 0x020610DB,
+ 0xC78, 0x0000001F,
+ 0xC7C, 0x00B91612,
+ 0xC80, 0x390000E4,
+ 0xC84, 0x20F60000,
+ 0xC88, 0x40000100,
+ 0xC8C, 0x20200000,
+ 0xC90, 0x00091521,
+ 0xC94, 0x00000000,
+ 0xC98, 0x00121820,
+ 0xC9C, 0x00007F7F,
+ 0xCA0, 0x00000000,
+ 0xCA4, 0x000300A0,
+ 0xCA8, 0x00000000,
+ 0xCAC, 0x00000000,
+ 0xCB0, 0x00000000,
+ 0xCB4, 0x00000000,
+ 0xCB8, 0x00000000,
+ 0xCBC, 0x28000000,
+ 0xCC0, 0x00000000,
+ 0xCC4, 0x00000000,
+ 0xCC8, 0x00000000,
+ 0xCCC, 0x00000000,
+ 0xCD0, 0x00000000,
+ 0xCD4, 0x00000000,
+ 0xCD8, 0x64B22427,
+ 0xCDC, 0x00766932,
+ 0xCE0, 0x00222222,
+ 0xCE4, 0x00000000,
+ 0xCE8, 0x37644302,
+ 0xCEC, 0x2F97D40C,
+ 0xD00, 0x00000740,
+ 0xD04, 0x00020401,
+ 0xD08, 0x0000907F,
+ 0xD0C, 0x20010201,
+ 0xD10, 0xA0633333,
+ 0xD14, 0x3333BC43,
+ 0xD18, 0x7A8F5B6F,
+ 0xD2C, 0xCC979975,
+ 0xD30, 0x00000000,
+ 0xD34, 0x80608000,
+ 0xD38, 0x00000000,
+ 0xD3C, 0x00127353,
+ 0xD40, 0x00000000,
+ 0xD44, 0x00000000,
+ 0xD48, 0x00000000,
+ 0xD4C, 0x00000000,
+ 0xD50, 0x6437140A,
+ 0xD54, 0x00000000,
+ 0xD58, 0x00000282,
+ 0xD5C, 0x30032064,
+ 0xD60, 0x4653DE68,
+ 0xD64, 0x04518A3C,
+ 0xD68, 0x00002101,
+ 0xD6C, 0x2A201C16,
+ 0xD70, 0x1812362E,
+ 0xD74, 0x322C2220,
+ 0xD78, 0x000E3C24,
+ 0xE00, 0x2D2D2D2D,
+ 0xE04, 0x2D2D2D2D,
+ 0xE08, 0x0390272D,
+ 0xE10, 0x2D2D2D2D,
+ 0xE14, 0x2D2D2D2D,
+ 0xE18, 0x2D2D2D2D,
+ 0xE1C, 0x2D2D2D2D,
+ 0xE28, 0x00000000,
+ 0xE30, 0x1000DC1F,
+ 0xE34, 0x10008C1F,
+ 0xE38, 0x02140102,
+ 0xE3C, 0x681604C2,
+ 0xE40, 0x01007C00,
+ 0xE44, 0x01004800,
+ 0xE48, 0xFB000000,
+ 0xE4C, 0x000028D1,
+ 0xE50, 0x1000DC1F,
+ 0xE54, 0x10008C1F,
+ 0xE58, 0x02140102,
+ 0xE5C, 0x28160D05,
+ 0xE60, 0x00000008,
+ 0xE68, 0x001B25A4,
+ 0xE6C, 0x00C00014,
+ 0xE70, 0x00C00014,
+ 0xE74, 0x01000014,
+ 0xE78, 0x01000014,
+ 0xE7C, 0x01000014,
+ 0xE80, 0x01000014,
+ 0xE84, 0x00C00014,
+ 0xE88, 0x01000014,
+ 0xE8C, 0x00C00014,
+ 0xED0, 0x00C00014,
+ 0xED4, 0x00C00014,
+ 0xED8, 0x00C00014,
+ 0xEDC, 0x00000014,
+ 0xEE0, 0x00000014,
+ 0xEEC, 0x01C00014,
+ 0xF14, 0x00000003,
+ 0xF4C, 0x00000000,
+ 0xF00, 0x00000300,
+};
+
+static void rtl_bb_delay(struct adapter *adapt, u32 addr, u32 data)
+{
+ if (addr == 0xfe) {
+ msleep(50);
+ } else if (addr == 0xfd) {
+ mdelay(5);
+ } else if (addr == 0xfc) {
+ mdelay(1);
+ } else if (addr == 0xfb) {
+ udelay(50);
+ } else if (addr == 0xfa) {
+ udelay(5);
+ } else if (addr == 0xf9) {
+ udelay(1);
+ } else {
+ phy_set_bb_reg(adapt, addr, bMaskDWord, data);
+ /* Add 1us delay between BB/RF register setting. */
+ udelay(1);
+ }
+}
+
+static bool set_baseband_phy_config(struct adapter *adapt)
+{
+ u32 i;
+ u32 arraylen = sizeof(array_phy_reg_1t_8188e)/sizeof(u32);
+ u32 *array = array_phy_reg_1t_8188e;
+
+ for (i = 0; i < arraylen; i += 2) {
+ u32 v1 = array[i];
+ u32 v2 = array[i+1];
+
+ if (v1 < 0xCDCDCDCD)
+ rtl_bb_delay(adapt, v1, v2);
+ }
+ return true;
+}
+
+/* PHY_REG_PG.TXT */
+
+static u32 array_phy_reg_pg_8188e[] = {
+ 0xE00, 0xFFFFFFFF, 0x06070809,
+ 0xE04, 0xFFFFFFFF, 0x02020405,
+ 0xE08, 0x0000FF00, 0x00000006,
+ 0x86C, 0xFFFFFF00, 0x00020400,
+ 0xE10, 0xFFFFFFFF, 0x08090A0B,
+ 0xE14, 0xFFFFFFFF, 0x01030607,
+ 0xE18, 0xFFFFFFFF, 0x08090A0B,
+ 0xE1C, 0xFFFFFFFF, 0x01030607,
+ 0xE00, 0xFFFFFFFF, 0x00000000,
+ 0xE04, 0xFFFFFFFF, 0x00000000,
+ 0xE08, 0x0000FF00, 0x00000000,
+ 0x86C, 0xFFFFFF00, 0x00000000,
+ 0xE10, 0xFFFFFFFF, 0x00000000,
+ 0xE14, 0xFFFFFFFF, 0x00000000,
+ 0xE18, 0xFFFFFFFF, 0x00000000,
+ 0xE1C, 0xFFFFFFFF, 0x00000000,
+ 0xE00, 0xFFFFFFFF, 0x02020202,
+ 0xE04, 0xFFFFFFFF, 0x00020202,
+ 0xE08, 0x0000FF00, 0x00000000,
+ 0x86C, 0xFFFFFF00, 0x00000000,
+ 0xE10, 0xFFFFFFFF, 0x04040404,
+ 0xE14, 0xFFFFFFFF, 0x00020404,
+ 0xE18, 0xFFFFFFFF, 0x00000000,
+ 0xE1C, 0xFFFFFFFF, 0x00000000,
+ 0xE00, 0xFFFFFFFF, 0x02020202,
+ 0xE04, 0xFFFFFFFF, 0x00020202,
+ 0xE08, 0x0000FF00, 0x00000000,
+ 0x86C, 0xFFFFFF00, 0x00000000,
+ 0xE10, 0xFFFFFFFF, 0x04040404,
+ 0xE14, 0xFFFFFFFF, 0x00020404,
+ 0xE18, 0xFFFFFFFF, 0x00000000,
+ 0xE1C, 0xFFFFFFFF, 0x00000000,
+ 0xE00, 0xFFFFFFFF, 0x00000000,
+ 0xE04, 0xFFFFFFFF, 0x00000000,
+ 0xE08, 0x0000FF00, 0x00000000,
+ 0x86C, 0xFFFFFF00, 0x00000000,
+ 0xE10, 0xFFFFFFFF, 0x00000000,
+ 0xE14, 0xFFFFFFFF, 0x00000000,
+ 0xE18, 0xFFFFFFFF, 0x00000000,
+ 0xE1C, 0xFFFFFFFF, 0x00000000,
+ 0xE00, 0xFFFFFFFF, 0x02020202,
+ 0xE04, 0xFFFFFFFF, 0x00020202,
+ 0xE08, 0x0000FF00, 0x00000000,
+ 0x86C, 0xFFFFFF00, 0x00000000,
+ 0xE10, 0xFFFFFFFF, 0x04040404,
+ 0xE14, 0xFFFFFFFF, 0x00020404,
+ 0xE18, 0xFFFFFFFF, 0x00000000,
+ 0xE1C, 0xFFFFFFFF, 0x00000000,
+ 0xE00, 0xFFFFFFFF, 0x00000000,
+ 0xE04, 0xFFFFFFFF, 0x00000000,
+ 0xE08, 0x0000FF00, 0x00000000,
+ 0x86C, 0xFFFFFF00, 0x00000000,
+ 0xE10, 0xFFFFFFFF, 0x00000000,
+ 0xE14, 0xFFFFFFFF, 0x00000000,
+ 0xE18, 0xFFFFFFFF, 0x00000000,
+ 0xE1C, 0xFFFFFFFF, 0x00000000,
+ 0xE00, 0xFFFFFFFF, 0x00000000,
+ 0xE04, 0xFFFFFFFF, 0x00000000,
+ 0xE08, 0x0000FF00, 0x00000000,
+ 0x86C, 0xFFFFFF00, 0x00000000,
+ 0xE10, 0xFFFFFFFF, 0x00000000,
+ 0xE14, 0xFFFFFFFF, 0x00000000,
+ 0xE18, 0xFFFFFFFF, 0x00000000,
+ 0xE1C, 0xFFFFFFFF, 0x00000000,
+ 0xE00, 0xFFFFFFFF, 0x00000000,
+ 0xE04, 0xFFFFFFFF, 0x00000000,
+ 0xE08, 0x0000FF00, 0x00000000,
+ 0x86C, 0xFFFFFF00, 0x00000000,
+ 0xE10, 0xFFFFFFFF, 0x00000000,
+ 0xE14, 0xFFFFFFFF, 0x00000000,
+ 0xE18, 0xFFFFFFFF, 0x00000000,
+ 0xE1C, 0xFFFFFFFF, 0x00000000,
+ 0xE00, 0xFFFFFFFF, 0x00000000,
+ 0xE04, 0xFFFFFFFF, 0x00000000,
+ 0xE08, 0x0000FF00, 0x00000000,
+ 0x86C, 0xFFFFFF00, 0x00000000,
+ 0xE10, 0xFFFFFFFF, 0x00000000,
+ 0xE14, 0xFFFFFFFF, 0x00000000,
+ 0xE18, 0xFFFFFFFF, 0x00000000,
+ 0xE1C, 0xFFFFFFFF, 0x00000000,
+ 0xE00, 0xFFFFFFFF, 0x00000000,
+ 0xE04, 0xFFFFFFFF, 0x00000000,
+ 0xE08, 0x0000FF00, 0x00000000,
+ 0x86C, 0xFFFFFF00, 0x00000000,
+ 0xE10, 0xFFFFFFFF, 0x00000000,
+ 0xE14, 0xFFFFFFFF, 0x00000000,
+ 0xE18, 0xFFFFFFFF, 0x00000000,
+ 0xE1C, 0xFFFFFFFF, 0x00000000,
+
+};
+
+static void store_pwrindex_offset(struct adapter *Adapter, u32 regaddr, u32 bitmask, u32 data)
+{
+ struct hal_data_8188e *hal_data = GET_HAL_DATA(Adapter);
+ u8 pwrGrpCnt = hal_data->pwrGroupCnt;
+
+ if (regaddr == rTxAGC_A_Rate18_06)
+ hal_data->MCSTxPowerLevelOriginalOffset[pwrGrpCnt][0] = data;
+ if (regaddr == rTxAGC_A_Rate54_24)
+ hal_data->MCSTxPowerLevelOriginalOffset[pwrGrpCnt][1] = data;
+ if (regaddr == rTxAGC_A_CCK1_Mcs32)
+ hal_data->MCSTxPowerLevelOriginalOffset[pwrGrpCnt][6] = data;
+ if (regaddr == rTxAGC_B_CCK11_A_CCK2_11 && bitmask == 0xffffff00)
+ hal_data->MCSTxPowerLevelOriginalOffset[pwrGrpCnt][7] = data;
+ if (regaddr == rTxAGC_A_Mcs03_Mcs00)
+ hal_data->MCSTxPowerLevelOriginalOffset[pwrGrpCnt][2] = data;
+ if (regaddr == rTxAGC_A_Mcs07_Mcs04)
+ hal_data->MCSTxPowerLevelOriginalOffset[pwrGrpCnt][3] = data;
+ if (regaddr == rTxAGC_A_Mcs11_Mcs08)
+ hal_data->MCSTxPowerLevelOriginalOffset[pwrGrpCnt][4] = data;
+ if (regaddr == rTxAGC_A_Mcs15_Mcs12) {
+ hal_data->MCSTxPowerLevelOriginalOffset[pwrGrpCnt][5] = data;
+ if (hal_data->rf_type == RF_1T1R)
+ hal_data->pwrGroupCnt++;
+ }
+ if (regaddr == rTxAGC_B_Rate18_06)
+ hal_data->MCSTxPowerLevelOriginalOffset[pwrGrpCnt][8] = data;
+ if (regaddr == rTxAGC_B_Rate54_24)
+ hal_data->MCSTxPowerLevelOriginalOffset[pwrGrpCnt][9] = data;
+ if (regaddr == rTxAGC_B_CCK1_55_Mcs32)
+ hal_data->MCSTxPowerLevelOriginalOffset[pwrGrpCnt][14] = data;
+ if (regaddr == rTxAGC_B_CCK11_A_CCK2_11 && bitmask == 0x000000ff)
+ hal_data->MCSTxPowerLevelOriginalOffset[pwrGrpCnt][15] = data;
+ if (regaddr == rTxAGC_B_Mcs03_Mcs00)
+ hal_data->MCSTxPowerLevelOriginalOffset[pwrGrpCnt][10] = data;
+ if (regaddr == rTxAGC_B_Mcs07_Mcs04)
+ hal_data->MCSTxPowerLevelOriginalOffset[pwrGrpCnt][11] = data;
+ if (regaddr == rTxAGC_B_Mcs11_Mcs08)
+ hal_data->MCSTxPowerLevelOriginalOffset[pwrGrpCnt][12] = data;
+ if (regaddr == rTxAGC_B_Mcs15_Mcs12) {
+ hal_data->MCSTxPowerLevelOriginalOffset[pwrGrpCnt][13] = data;
+ if (hal_data->rf_type != RF_1T1R)
+ hal_data->pwrGroupCnt++;
+ }
+}
+
+static void rtl_addr_delay(struct adapter *adapt,
+ u32 addr, u32 bit_mask, u32 data)
+{
+ switch (addr) {
+ case 0xfe:
+ msleep(50);
+ break;
+ case 0xfd:
+ mdelay(5);
+ break;
+ case 0xfc:
+ mdelay(1);
+ break;
+ case 0xfb:
+ udelay(50);
+ break;
+ case 0xfa:
+ udelay(5);
+ break;
+ case 0xf9:
+ udelay(1);
+ break;
+ default:
+ store_pwrindex_offset(adapt, addr, bit_mask, data);
+ }
+}
+
+static bool config_bb_with_pgheader(struct adapter *adapt)
+{
+ u32 i = 0;
+ u32 arraylen = sizeof(array_phy_reg_pg_8188e) / sizeof(u32);
+ u32 *array = array_phy_reg_pg_8188e;
+
+ for (i = 0; i < arraylen; i += 3) {
+ u32 v1 = array[i];
+ u32 v2 = array[i+1];
+ u32 v3 = array[i+2];
+
+ if (v1 < 0xCDCDCDCD)
+ rtl_addr_delay(adapt, v1, v2, v3);
+ }
+ return true;
+}
+
+static void rtl88e_phy_init_bb_rf_register_definition(struct adapter *Adapter)
+{
+ struct hal_data_8188e *hal_data = GET_HAL_DATA(Adapter);
+ struct bb_reg_def *reg[4];
+
+ reg[RF_PATH_A] = &(hal_data->PHYRegDef[RF_PATH_A]);
+ reg[RF_PATH_B] = &(hal_data->PHYRegDef[RF_PATH_B]);
+ reg[RF_PATH_C] = &(hal_data->PHYRegDef[RF_PATH_C]);
+ reg[RF_PATH_D] = &(hal_data->PHYRegDef[RF_PATH_D]);
+
+ reg[RF_PATH_A]->rfintfs = rFPGA0_XAB_RFInterfaceSW;
+ reg[RF_PATH_B]->rfintfs = rFPGA0_XAB_RFInterfaceSW;
+ reg[RF_PATH_C]->rfintfs = rFPGA0_XCD_RFInterfaceSW;
+ reg[RF_PATH_D]->rfintfs = rFPGA0_XCD_RFInterfaceSW;
+
+ reg[RF_PATH_A]->rfintfi = rFPGA0_XAB_RFInterfaceRB;
+ reg[RF_PATH_B]->rfintfi = rFPGA0_XAB_RFInterfaceRB;
+ reg[RF_PATH_C]->rfintfi = rFPGA0_XCD_RFInterfaceRB;
+ reg[RF_PATH_D]->rfintfi = rFPGA0_XCD_RFInterfaceRB;
+
+ reg[RF_PATH_A]->rfintfo = rFPGA0_XA_RFInterfaceOE;
+ reg[RF_PATH_B]->rfintfo = rFPGA0_XB_RFInterfaceOE;
+
+ reg[RF_PATH_A]->rfintfe = rFPGA0_XA_RFInterfaceOE;
+ reg[RF_PATH_B]->rfintfe = rFPGA0_XB_RFInterfaceOE;
+
+ reg[RF_PATH_A]->rf3wireOffset = rFPGA0_XA_LSSIParameter;
+ reg[RF_PATH_B]->rf3wireOffset = rFPGA0_XB_LSSIParameter;
+
+ reg[RF_PATH_A]->rfLSSI_Select = rFPGA0_XAB_RFParameter;
+ reg[RF_PATH_B]->rfLSSI_Select = rFPGA0_XAB_RFParameter;
+ reg[RF_PATH_C]->rfLSSI_Select = rFPGA0_XCD_RFParameter;
+ reg[RF_PATH_D]->rfLSSI_Select = rFPGA0_XCD_RFParameter;
+
+ reg[RF_PATH_A]->rfTxGainStage = rFPGA0_TxGainStage;
+ reg[RF_PATH_B]->rfTxGainStage = rFPGA0_TxGainStage;
+ reg[RF_PATH_C]->rfTxGainStage = rFPGA0_TxGainStage;
+ reg[RF_PATH_D]->rfTxGainStage = rFPGA0_TxGainStage;
+
+ reg[RF_PATH_A]->rfHSSIPara1 = rFPGA0_XA_HSSIParameter1;
+ reg[RF_PATH_B]->rfHSSIPara1 = rFPGA0_XB_HSSIParameter1;
+
+ reg[RF_PATH_A]->rfHSSIPara2 = rFPGA0_XA_HSSIParameter2;
+ reg[RF_PATH_B]->rfHSSIPara2 = rFPGA0_XB_HSSIParameter2;
+
+ reg[RF_PATH_A]->rfSwitchControl = rFPGA0_XAB_SwitchControl;
+ reg[RF_PATH_B]->rfSwitchControl = rFPGA0_XAB_SwitchControl;
+ reg[RF_PATH_C]->rfSwitchControl = rFPGA0_XCD_SwitchControl;
+ reg[RF_PATH_D]->rfSwitchControl = rFPGA0_XCD_SwitchControl;
+
+ reg[RF_PATH_A]->rfAGCControl1 = rOFDM0_XAAGCCore1;
+ reg[RF_PATH_B]->rfAGCControl1 = rOFDM0_XBAGCCore1;
+ reg[RF_PATH_C]->rfAGCControl1 = rOFDM0_XCAGCCore1;
+ reg[RF_PATH_D]->rfAGCControl1 = rOFDM0_XDAGCCore1;
+
+ reg[RF_PATH_A]->rfAGCControl2 = rOFDM0_XAAGCCore2;
+ reg[RF_PATH_B]->rfAGCControl2 = rOFDM0_XBAGCCore2;
+ reg[RF_PATH_C]->rfAGCControl2 = rOFDM0_XCAGCCore2;
+ reg[RF_PATH_D]->rfAGCControl2 = rOFDM0_XDAGCCore2;
+
+ reg[RF_PATH_A]->rfRxIQImbalance = rOFDM0_XARxIQImbalance;
+ reg[RF_PATH_B]->rfRxIQImbalance = rOFDM0_XBRxIQImbalance;
+ reg[RF_PATH_C]->rfRxIQImbalance = rOFDM0_XCRxIQImbalance;
+ reg[RF_PATH_D]->rfRxIQImbalance = rOFDM0_XDRxIQImbalance;
+
+ reg[RF_PATH_A]->rfRxAFE = rOFDM0_XARxAFE;
+ reg[RF_PATH_B]->rfRxAFE = rOFDM0_XBRxAFE;
+ reg[RF_PATH_C]->rfRxAFE = rOFDM0_XCRxAFE;
+ reg[RF_PATH_D]->rfRxAFE = rOFDM0_XDRxAFE;
+
+ reg[RF_PATH_A]->rfTxIQImbalance = rOFDM0_XATxIQImbalance;
+ reg[RF_PATH_B]->rfTxIQImbalance = rOFDM0_XBTxIQImbalance;
+ reg[RF_PATH_C]->rfTxIQImbalance = rOFDM0_XCTxIQImbalance;
+ reg[RF_PATH_D]->rfTxIQImbalance = rOFDM0_XDTxIQImbalance;
+
+ reg[RF_PATH_A]->rfTxAFE = rOFDM0_XATxAFE;
+ reg[RF_PATH_B]->rfTxAFE = rOFDM0_XBTxAFE;
+ reg[RF_PATH_C]->rfTxAFE = rOFDM0_XCTxAFE;
+ reg[RF_PATH_D]->rfTxAFE = rOFDM0_XDTxAFE;
+
+ reg[RF_PATH_A]->rfLSSIReadBack = rFPGA0_XA_LSSIReadBack;
+ reg[RF_PATH_B]->rfLSSIReadBack = rFPGA0_XB_LSSIReadBack;
+ reg[RF_PATH_C]->rfLSSIReadBack = rFPGA0_XC_LSSIReadBack;
+ reg[RF_PATH_D]->rfLSSIReadBack = rFPGA0_XD_LSSIReadBack;
+
+ reg[RF_PATH_A]->rfLSSIReadBackPi = TransceiverA_HSPI_Readback;
+ reg[RF_PATH_B]->rfLSSIReadBackPi = TransceiverB_HSPI_Readback;
+}
+
+static bool config_parafile(struct adapter *adapt)
+{
+ struct eeprom_priv *pEEPROM = GET_EEPROM_EFUSE_PRIV(adapt);
+ struct hal_data_8188e *hal_data = GET_HAL_DATA(adapt);
+
+ set_baseband_phy_config(adapt);
+
+ /* If EEPROM or EFUSE autoload OK, We must config by PHY_REG_PG.txt */
+ if (!pEEPROM->bautoload_fail_flag) {
+ hal_data->pwrGroupCnt = 0;
+ config_bb_with_pgheader(adapt);
+ }
+ set_baseband_agc_config(adapt);
+ return true;
+}
+
+bool rtl88eu_phy_bb_config(struct adapter *adapt)
+{
+ int rtstatus = true;
+ struct hal_data_8188e *hal_data = GET_HAL_DATA(adapt);
+ u32 regval;
+ u8 crystal_cap;
+
+ rtl88e_phy_init_bb_rf_register_definition(adapt);
+
+ /* Enable BB and RF */
+ regval = usb_read16(adapt, REG_SYS_FUNC_EN);
+ usb_write16(adapt, REG_SYS_FUNC_EN, (u16)(regval|BIT13|BIT0|BIT1));
+
+ usb_write8(adapt, REG_RF_CTRL, RF_EN|RF_RSTB|RF_SDMRSTB);
+
+ usb_write8(adapt, REG_SYS_FUNC_EN, FEN_USBA | FEN_USBD | FEN_BB_GLB_RSTn | FEN_BBRSTB);
+
+ /* Config BB and AGC */
+ rtstatus = config_parafile(adapt);
+
+ /* write 0x24[16:11] = 0x24[22:17] = crystal_cap */
+ crystal_cap = hal_data->CrystalCap & 0x3F;
+ phy_set_bb_reg(adapt, REG_AFE_XTAL_CTRL, 0x7ff800, (crystal_cap | (crystal_cap << 6)));
+
+ return rtstatus;
+}
diff --git a/drivers/staging/rtl8188eu/hal/fw.c b/drivers/staging/rtl8188eu/hal/fw.c
new file mode 100644
index 000000000..823632011
--- /dev/null
+++ b/drivers/staging/rtl8188eu/hal/fw.c
@@ -0,0 +1,235 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2009-2013 Realtek Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ * The full GNU General Public License is included in this distribution in the
+ * file called LICENSE.
+ *
+ * Contact Information:
+ * wlanfae <wlanfae@realtek.com>
+ * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park,
+ * Hsinchu 300, Taiwan.
+ *
+ * Larry Finger <Larry.Finger@lwfinger.net>
+ *
+ *****************************************************************************/
+
+#include "fw.h"
+#include "drv_types.h"
+#include "usb_ops_linux.h"
+#include "rtl8188e_spec.h"
+#include "rtl8188e_hal.h"
+
+#include <linux/firmware.h>
+#include <linux/kmemleak.h>
+
+static void _rtl88e_enable_fw_download(struct adapter *adapt, bool enable)
+{
+ u8 tmp;
+
+ if (enable) {
+ tmp = usb_read8(adapt, REG_MCUFWDL);
+ usb_write8(adapt, REG_MCUFWDL, tmp | 0x01);
+
+ tmp = usb_read8(adapt, REG_MCUFWDL + 2);
+ usb_write8(adapt, REG_MCUFWDL + 2, tmp & 0xf7);
+ } else {
+ tmp = usb_read8(adapt, REG_MCUFWDL);
+ usb_write8(adapt, REG_MCUFWDL, tmp & 0xfe);
+
+ usb_write8(adapt, REG_MCUFWDL + 1, 0x00);
+ }
+}
+
+static void _rtl88e_fw_block_write(struct adapter *adapt,
+ const u8 *buffer, u32 size)
+{
+ u32 blk_sz = sizeof(u32);
+ u8 *buf_ptr = (u8 *)buffer;
+ u32 *pu4BytePtr = (u32 *)buffer;
+ u32 i, offset, blk_cnt, remain;
+
+ blk_cnt = size / blk_sz;
+ remain = size % blk_sz;
+
+ for (i = 0; i < blk_cnt; i++) {
+ offset = i * blk_sz;
+ usb_write32(adapt, (FW_8192C_START_ADDRESS + offset),
+ *(pu4BytePtr + i));
+ }
+
+ if (remain) {
+ offset = blk_cnt * blk_sz;
+ buf_ptr += offset;
+ for (i = 0; i < remain; i++) {
+ usb_write8(adapt, (FW_8192C_START_ADDRESS +
+ offset + i), *(buf_ptr + i));
+ }
+ }
+}
+
+static void _rtl88e_fill_dummy(u8 *pfwbuf, u32 *pfwlen)
+{
+ u32 fwlen = *pfwlen;
+ u8 remain = (u8)(fwlen % 4);
+
+ remain = (remain == 0) ? 0 : (4 - remain);
+
+ while (remain > 0) {
+ pfwbuf[fwlen] = 0;
+ fwlen++;
+ remain--;
+ }
+
+ *pfwlen = fwlen;
+}
+
+static void _rtl88e_fw_page_write(struct adapter *adapt,
+ u32 page, const u8 *buffer, u32 size)
+{
+ u8 value8;
+ u8 u8page = (u8)(page & 0x07);
+
+ value8 = (usb_read8(adapt, REG_MCUFWDL + 2) & 0xF8) | u8page;
+
+ usb_write8(adapt, (REG_MCUFWDL + 2), value8);
+ _rtl88e_fw_block_write(adapt, buffer, size);
+}
+
+static void _rtl88e_write_fw(struct adapter *adapt, u8 *buffer, u32 size)
+{
+ u8 *buf_ptr = buffer;
+ u32 page_no, remain;
+ u32 page, offset;
+
+ _rtl88e_fill_dummy(buf_ptr, &size);
+
+ page_no = size / FW_8192C_PAGE_SIZE;
+ remain = size % FW_8192C_PAGE_SIZE;
+
+ for (page = 0; page < page_no; page++) {
+ offset = page * FW_8192C_PAGE_SIZE;
+ _rtl88e_fw_page_write(adapt, page, (buf_ptr + offset),
+ FW_8192C_PAGE_SIZE);
+ }
+
+ if (remain) {
+ offset = page_no * FW_8192C_PAGE_SIZE;
+ page = page_no;
+ _rtl88e_fw_page_write(adapt, page, (buf_ptr + offset), remain);
+ }
+}
+
+static void rtl88e_firmware_selfreset(struct adapter *adapt)
+{
+ u8 u1b_tmp;
+
+ u1b_tmp = usb_read8(adapt, REG_SYS_FUNC_EN+1);
+ usb_write8(adapt, REG_SYS_FUNC_EN+1, (u1b_tmp & (~BIT(2))));
+ usb_write8(adapt, REG_SYS_FUNC_EN+1, (u1b_tmp | BIT(2)));
+}
+
+static int _rtl88e_fw_free_to_go(struct adapter *adapt)
+{
+ int err = -EIO;
+ u32 counter = 0;
+ u32 value32;
+
+ do {
+ value32 = usb_read32(adapt, REG_MCUFWDL);
+ if (value32 & FWDL_ChkSum_rpt)
+ break;
+ } while (counter++ < POLLING_READY_TIMEOUT_COUNT);
+
+ if (counter >= POLLING_READY_TIMEOUT_COUNT)
+ goto exit;
+
+ value32 = usb_read32(adapt, REG_MCUFWDL);
+ value32 |= MCUFWDL_RDY;
+ value32 &= ~WINTINI_RDY;
+ usb_write32(adapt, REG_MCUFWDL, value32);
+
+ rtl88e_firmware_selfreset(adapt);
+ counter = 0;
+
+ do {
+ value32 = usb_read32(adapt, REG_MCUFWDL);
+ if (value32 & WINTINI_RDY) {
+ err = 0;
+ goto exit;
+ }
+
+ udelay(FW_8192C_POLLING_DELAY);
+
+ } while (counter++ < POLLING_READY_TIMEOUT_COUNT);
+
+exit:
+ return err;
+}
+
+int rtl88eu_download_fw(struct adapter *adapt)
+{
+ struct hal_data_8188e *rtlhal = GET_HAL_DATA(adapt);
+ struct dvobj_priv *dvobj = adapter_to_dvobj(adapt);
+ struct device *device = dvobj_to_dev(dvobj);
+ const struct firmware *fw;
+ const char fw_name[] = "/*(DEBLOBBED)*/";
+ struct rtl92c_firmware_header *pfwheader = NULL;
+ u8 *pfwdata;
+ u32 fwsize;
+ int err;
+
+ if (reject_firmware(&fw, fw_name, device)) {
+ dev_err(device, "Firmware %s not available\n", fw_name);
+ return -ENOENT;
+ }
+
+ if (fw->size > FW_8188E_SIZE) {
+ dev_err(device, "Firmware size exceed 0x%X. Check it.\n",
+ FW_8188E_SIZE);
+ return -1;
+ }
+
+ pfwdata = kzalloc(FW_8188E_SIZE, GFP_KERNEL);
+ if (!pfwdata)
+ return -ENOMEM;
+
+ rtlhal->pfirmware = pfwdata;
+ memcpy(rtlhal->pfirmware, fw->data, fw->size);
+ rtlhal->fwsize = fw->size;
+ release_firmware(fw);
+
+ fwsize = rtlhal->fwsize;
+ pfwheader = (struct rtl92c_firmware_header *)pfwdata;
+
+ if (IS_FW_HEADER_EXIST(pfwheader)) {
+ pfwdata = pfwdata + 32;
+ fwsize = fwsize - 32;
+ }
+
+ if (usb_read8(adapt, REG_MCUFWDL) & RAM_DL_SEL) {
+ usb_write8(adapt, REG_MCUFWDL, 0);
+ rtl88e_firmware_selfreset(adapt);
+ }
+ _rtl88e_enable_fw_download(adapt, true);
+ usb_write8(adapt, REG_MCUFWDL, usb_read8(adapt, REG_MCUFWDL) | FWDL_ChkSum_rpt);
+ _rtl88e_write_fw(adapt, pfwdata, fwsize);
+ _rtl88e_enable_fw_download(adapt, false);
+
+ err = _rtl88e_fw_free_to_go(adapt);
+
+ return err;
+}
diff --git a/drivers/staging/rtl8188eu/hal/hal_com.c b/drivers/staging/rtl8188eu/hal/hal_com.c
new file mode 100644
index 000000000..170e3de5e
--- /dev/null
+++ b/drivers/staging/rtl8188eu/hal/hal_com.c
@@ -0,0 +1,321 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2011 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ *
+ ******************************************************************************/
+#include <osdep_service.h>
+#include <drv_types.h>
+
+#include <hal_intf.h>
+#include <hal_com.h>
+#include <rtl8188e_hal.h>
+
+#define _HAL_INIT_C_
+
+void dump_chip_info(struct HAL_VERSION chip_vers)
+{
+ uint cnt = 0;
+ char buf[128];
+
+ if (IS_81XXC(chip_vers)) {
+ cnt += sprintf((buf+cnt), "Chip Version Info: %s_",
+ IS_92C_SERIAL(chip_vers) ?
+ "CHIP_8192C" : "CHIP_8188C");
+ } else if (IS_92D(chip_vers)) {
+ cnt += sprintf((buf+cnt), "Chip Version Info: CHIP_8192D_");
+ } else if (IS_8723_SERIES(chip_vers)) {
+ cnt += sprintf((buf+cnt), "Chip Version Info: CHIP_8723A_");
+ } else if (IS_8188E(chip_vers)) {
+ cnt += sprintf((buf+cnt), "Chip Version Info: CHIP_8188E_");
+ }
+
+ cnt += sprintf((buf+cnt), "%s_", IS_NORMAL_CHIP(chip_vers) ?
+ "Normal_Chip" : "Test_Chip");
+ cnt += sprintf((buf+cnt), "%s_", IS_CHIP_VENDOR_TSMC(chip_vers) ?
+ "TSMC" : "UMC");
+ if (IS_A_CUT(chip_vers))
+ cnt += sprintf((buf+cnt), "A_CUT_");
+ else if (IS_B_CUT(chip_vers))
+ cnt += sprintf((buf+cnt), "B_CUT_");
+ else if (IS_C_CUT(chip_vers))
+ cnt += sprintf((buf+cnt), "C_CUT_");
+ else if (IS_D_CUT(chip_vers))
+ cnt += sprintf((buf+cnt), "D_CUT_");
+ else if (IS_E_CUT(chip_vers))
+ cnt += sprintf((buf+cnt), "E_CUT_");
+ else
+ cnt += sprintf((buf+cnt), "UNKNOWN_CUT(%d)_",
+ chip_vers.CUTVersion);
+
+ if (IS_1T1R(chip_vers))
+ cnt += sprintf((buf+cnt), "1T1R_");
+ else if (IS_1T2R(chip_vers))
+ cnt += sprintf((buf+cnt), "1T2R_");
+ else if (IS_2T2R(chip_vers))
+ cnt += sprintf((buf+cnt), "2T2R_");
+ else
+ cnt += sprintf((buf+cnt), "UNKNOWN_RFTYPE(%d)_",
+ chip_vers.RFType);
+
+ cnt += sprintf((buf+cnt), "RomVer(%d)\n", chip_vers.ROMVer);
+
+ pr_info("%s", buf);
+}
+
+#define CHAN_PLAN_HW 0x80
+
+u8 /* return the final channel plan decision */
+hal_com_get_channel_plan(struct adapter *padapter, u8 hw_channel_plan,
+ u8 sw_channel_plan, u8 def_channel_plan,
+ bool load_fail)
+{
+ u8 sw_cfg;
+ u8 chnlplan;
+
+ sw_cfg = true;
+ if (!load_fail) {
+ if (!rtw_is_channel_plan_valid(sw_channel_plan))
+ sw_cfg = false;
+ if (hw_channel_plan & CHAN_PLAN_HW)
+ sw_cfg = false;
+ }
+
+ if (sw_cfg)
+ chnlplan = sw_channel_plan;
+ else
+ chnlplan = hw_channel_plan & (~CHAN_PLAN_HW);
+
+ if (!rtw_is_channel_plan_valid(chnlplan))
+ chnlplan = def_channel_plan;
+
+ return chnlplan;
+}
+
+u8 MRateToHwRate(u8 rate)
+{
+ u8 ret = DESC_RATE1M;
+
+ switch (rate) {
+ /* CCK and OFDM non-HT rates */
+ case IEEE80211_CCK_RATE_1MB:
+ ret = DESC_RATE1M;
+ break;
+ case IEEE80211_CCK_RATE_2MB:
+ ret = DESC_RATE2M;
+ break;
+ case IEEE80211_CCK_RATE_5MB:
+ ret = DESC_RATE5_5M;
+ break;
+ case IEEE80211_CCK_RATE_11MB:
+ ret = DESC_RATE11M;
+ break;
+ case IEEE80211_OFDM_RATE_6MB:
+ ret = DESC_RATE6M;
+ break;
+ case IEEE80211_OFDM_RATE_9MB:
+ ret = DESC_RATE9M;
+ break;
+ case IEEE80211_OFDM_RATE_12MB:
+ ret = DESC_RATE12M;
+ break;
+ case IEEE80211_OFDM_RATE_18MB:
+ ret = DESC_RATE18M;
+ break;
+ case IEEE80211_OFDM_RATE_24MB:
+ ret = DESC_RATE24M;
+ break;
+ case IEEE80211_OFDM_RATE_36MB:
+ ret = DESC_RATE36M;
+ break;
+ case IEEE80211_OFDM_RATE_48MB:
+ ret = DESC_RATE48M;
+ break;
+ case IEEE80211_OFDM_RATE_54MB:
+ ret = DESC_RATE54M;
+ break;
+ default:
+ break;
+ }
+ return ret;
+}
+
+void HalSetBrateCfg(struct adapter *adapt, u8 *brates, u16 *rate_cfg)
+{
+ u8 i, is_brate, brate;
+
+ for (i = 0; i < NDIS_802_11_LENGTH_RATES_EX; i++) {
+ is_brate = brates[i] & IEEE80211_BASIC_RATE_MASK;
+ brate = brates[i] & 0x7f;
+
+ if (is_brate) {
+ switch (brate) {
+ case IEEE80211_CCK_RATE_1MB:
+ *rate_cfg |= RATE_1M;
+ break;
+ case IEEE80211_CCK_RATE_2MB:
+ *rate_cfg |= RATE_2M;
+ break;
+ case IEEE80211_CCK_RATE_5MB:
+ *rate_cfg |= RATE_5_5M;
+ break;
+ case IEEE80211_CCK_RATE_11MB:
+ *rate_cfg |= RATE_11M;
+ break;
+ case IEEE80211_OFDM_RATE_6MB:
+ *rate_cfg |= RATE_6M;
+ break;
+ case IEEE80211_OFDM_RATE_9MB:
+ *rate_cfg |= RATE_9M;
+ break;
+ case IEEE80211_OFDM_RATE_12MB:
+ *rate_cfg |= RATE_12M;
+ break;
+ case IEEE80211_OFDM_RATE_18MB:
+ *rate_cfg |= RATE_18M;
+ break;
+ case IEEE80211_OFDM_RATE_24MB:
+ *rate_cfg |= RATE_24M;
+ break;
+ case IEEE80211_OFDM_RATE_36MB:
+ *rate_cfg |= RATE_36M;
+ break;
+ case IEEE80211_OFDM_RATE_48MB:
+ *rate_cfg |= RATE_48M;
+ break;
+ case IEEE80211_OFDM_RATE_54MB:
+ *rate_cfg |= RATE_54M;
+ break;
+ }
+ }
+ }
+}
+
+static void one_out_pipe(struct adapter *adapter)
+{
+ struct dvobj_priv *pdvobjpriv = adapter_to_dvobj(adapter);
+
+ pdvobjpriv->Queue2Pipe[0] = pdvobjpriv->RtOutPipe[0];/* VO */
+ pdvobjpriv->Queue2Pipe[1] = pdvobjpriv->RtOutPipe[0];/* VI */
+ pdvobjpriv->Queue2Pipe[2] = pdvobjpriv->RtOutPipe[0];/* BE */
+ pdvobjpriv->Queue2Pipe[3] = pdvobjpriv->RtOutPipe[0];/* BK */
+
+ pdvobjpriv->Queue2Pipe[4] = pdvobjpriv->RtOutPipe[0];/* BCN */
+ pdvobjpriv->Queue2Pipe[5] = pdvobjpriv->RtOutPipe[0];/* MGT */
+ pdvobjpriv->Queue2Pipe[6] = pdvobjpriv->RtOutPipe[0];/* HIGH */
+ pdvobjpriv->Queue2Pipe[7] = pdvobjpriv->RtOutPipe[0];/* TXCMD */
+}
+
+static void two_out_pipe(struct adapter *adapter, bool wifi_cfg)
+{
+ struct dvobj_priv *pdvobjpriv = adapter_to_dvobj(adapter);
+
+ if (wifi_cfg) { /* WMM */
+ /* BK, BE, VI, VO, BCN, CMD, MGT, HIGH, HCCA */
+ /* 0, 1, 0, 1, 0, 0, 0, 0, 0}; */
+ /* 0:H, 1:L */
+
+ pdvobjpriv->Queue2Pipe[0] = pdvobjpriv->RtOutPipe[1];/* VO */
+ pdvobjpriv->Queue2Pipe[1] = pdvobjpriv->RtOutPipe[0];/* VI */
+ pdvobjpriv->Queue2Pipe[2] = pdvobjpriv->RtOutPipe[1];/* BE */
+ pdvobjpriv->Queue2Pipe[3] = pdvobjpriv->RtOutPipe[0];/* BK */
+
+ pdvobjpriv->Queue2Pipe[4] = pdvobjpriv->RtOutPipe[0];/* BCN */
+ pdvobjpriv->Queue2Pipe[5] = pdvobjpriv->RtOutPipe[0];/* MGT */
+ pdvobjpriv->Queue2Pipe[6] = pdvobjpriv->RtOutPipe[0];/* HIGH */
+ pdvobjpriv->Queue2Pipe[7] = pdvobjpriv->RtOutPipe[0];/* TXCMD */
+
+ } else {/* typical setting */
+ /* BK, BE, VI, VO, BCN, CMD, MGT, HIGH, HCCA */
+ /* 1, 1, 0, 0, 0, 0, 0, 0, 0}; */
+ /* 0:H, 1:L */
+
+ pdvobjpriv->Queue2Pipe[0] = pdvobjpriv->RtOutPipe[0];/* VO */
+ pdvobjpriv->Queue2Pipe[1] = pdvobjpriv->RtOutPipe[0];/* VI */
+ pdvobjpriv->Queue2Pipe[2] = pdvobjpriv->RtOutPipe[1];/* BE */
+ pdvobjpriv->Queue2Pipe[3] = pdvobjpriv->RtOutPipe[1];/* BK */
+
+ pdvobjpriv->Queue2Pipe[4] = pdvobjpriv->RtOutPipe[0];/* BCN */
+ pdvobjpriv->Queue2Pipe[5] = pdvobjpriv->RtOutPipe[0];/* MGT */
+ pdvobjpriv->Queue2Pipe[6] = pdvobjpriv->RtOutPipe[0];/* HIGH */
+ pdvobjpriv->Queue2Pipe[7] = pdvobjpriv->RtOutPipe[0];/* TXCMD */
+ }
+}
+
+static void three_out_pipe(struct adapter *adapter, bool wifi_cfg)
+{
+ struct dvobj_priv *pdvobjpriv = adapter_to_dvobj(adapter);
+
+ if (wifi_cfg) {/* for WMM */
+ /* BK, BE, VI, VO, BCN, CMD, MGT, HIGH, HCCA */
+ /* 1, 2, 1, 0, 0, 0, 0, 0, 0}; */
+ /* 0:H, 1:N, 2:L */
+
+ pdvobjpriv->Queue2Pipe[0] = pdvobjpriv->RtOutPipe[0];/* VO */
+ pdvobjpriv->Queue2Pipe[1] = pdvobjpriv->RtOutPipe[1];/* VI */
+ pdvobjpriv->Queue2Pipe[2] = pdvobjpriv->RtOutPipe[2];/* BE */
+ pdvobjpriv->Queue2Pipe[3] = pdvobjpriv->RtOutPipe[1];/* BK */
+
+ pdvobjpriv->Queue2Pipe[4] = pdvobjpriv->RtOutPipe[0];/* BCN */
+ pdvobjpriv->Queue2Pipe[5] = pdvobjpriv->RtOutPipe[0];/* MGT */
+ pdvobjpriv->Queue2Pipe[6] = pdvobjpriv->RtOutPipe[0];/* HIGH */
+ pdvobjpriv->Queue2Pipe[7] = pdvobjpriv->RtOutPipe[0];/* TXCMD */
+
+ } else {/* typical setting */
+ /* BK, BE, VI, VO, BCN, CMD, MGT, HIGH, HCCA */
+ /* 2, 2, 1, 0, 0, 0, 0, 0, 0}; */
+ /* 0:H, 1:N, 2:L */
+
+ pdvobjpriv->Queue2Pipe[0] = pdvobjpriv->RtOutPipe[0];/* VO */
+ pdvobjpriv->Queue2Pipe[1] = pdvobjpriv->RtOutPipe[1];/* VI */
+ pdvobjpriv->Queue2Pipe[2] = pdvobjpriv->RtOutPipe[2];/* BE */
+ pdvobjpriv->Queue2Pipe[3] = pdvobjpriv->RtOutPipe[2];/* BK */
+
+ pdvobjpriv->Queue2Pipe[4] = pdvobjpriv->RtOutPipe[0];/* BCN */
+ pdvobjpriv->Queue2Pipe[5] = pdvobjpriv->RtOutPipe[0];/* MGT */
+ pdvobjpriv->Queue2Pipe[6] = pdvobjpriv->RtOutPipe[0];/* HIGH */
+ pdvobjpriv->Queue2Pipe[7] = pdvobjpriv->RtOutPipe[0];/* TXCMD */
+ }
+}
+
+bool Hal_MappingOutPipe(struct adapter *adapter, u8 numoutpipe)
+{
+ struct registry_priv *pregistrypriv = &adapter->registrypriv;
+ bool wifi_cfg = (pregistrypriv->wifi_spec) ? true : false;
+ bool result = true;
+
+ switch (numoutpipe) {
+ case 2:
+ two_out_pipe(adapter, wifi_cfg);
+ break;
+ case 3:
+ three_out_pipe(adapter, wifi_cfg);
+ break;
+ case 1:
+ one_out_pipe(adapter);
+ break;
+ default:
+ result = false;
+ break;
+ }
+ return result;
+}
+
+void hal_init_macaddr(struct adapter *adapter)
+{
+ rtw_hal_set_hwreg(adapter, HW_VAR_MAC_ADDR,
+ adapter->eeprompriv.mac_addr);
+}
diff --git a/drivers/staging/rtl8188eu/hal/hal_intf.c b/drivers/staging/rtl8188eu/hal/hal_intf.c
new file mode 100644
index 000000000..4bdbed287
--- /dev/null
+++ b/drivers/staging/rtl8188eu/hal/hal_intf.c
@@ -0,0 +1,335 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2012 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ *
+ ******************************************************************************/
+
+#define _HAL_INTF_C_
+#include <osdep_service.h>
+#include <drv_types.h>
+#include <hal_intf.h>
+#include <usb_hal.h>
+
+void rtw_hal_chip_configure(struct adapter *adapt)
+{
+ if (adapt->HalFunc.intf_chip_configure)
+ adapt->HalFunc.intf_chip_configure(adapt);
+}
+
+void rtw_hal_read_chip_info(struct adapter *adapt)
+{
+ if (adapt->HalFunc.read_adapter_info)
+ adapt->HalFunc.read_adapter_info(adapt);
+}
+
+void rtw_hal_read_chip_version(struct adapter *adapt)
+{
+ if (adapt->HalFunc.read_chip_version)
+ adapt->HalFunc.read_chip_version(adapt);
+}
+
+void rtw_hal_def_value_init(struct adapter *adapt)
+{
+ if (adapt->HalFunc.init_default_value)
+ adapt->HalFunc.init_default_value(adapt);
+}
+
+void rtw_hal_free_data(struct adapter *adapt)
+{
+ if (adapt->HalFunc.free_hal_data)
+ adapt->HalFunc.free_hal_data(adapt);
+}
+
+void rtw_hal_dm_init(struct adapter *adapt)
+{
+ if (adapt->HalFunc.dm_init)
+ adapt->HalFunc.dm_init(adapt);
+}
+
+void rtw_hal_sw_led_init(struct adapter *adapt)
+{
+ if (adapt->HalFunc.InitSwLeds)
+ adapt->HalFunc.InitSwLeds(adapt);
+}
+
+void rtw_hal_sw_led_deinit(struct adapter *adapt)
+{
+ if (adapt->HalFunc.DeInitSwLeds)
+ adapt->HalFunc.DeInitSwLeds(adapt);
+}
+
+u32 rtw_hal_power_on(struct adapter *adapt)
+{
+ if (adapt->HalFunc.hal_power_on)
+ return adapt->HalFunc.hal_power_on(adapt);
+ return _FAIL;
+}
+
+uint rtw_hal_init(struct adapter *adapt)
+{
+ uint status = _SUCCESS;
+
+ adapt->hw_init_completed = false;
+
+ status = adapt->HalFunc.hal_init(adapt);
+
+ if (status == _SUCCESS) {
+ adapt->hw_init_completed = true;
+
+ if (adapt->registrypriv.notch_filter == 1)
+ rtw_hal_notch_filter(adapt, 1);
+
+ rtw_hal_reset_security_engine(adapt);
+ } else {
+ adapt->hw_init_completed = false;
+ DBG_88E("rtw_hal_init: hal__init fail\n");
+ }
+
+ RT_TRACE(_module_hal_init_c_, _drv_err_,
+ ("-rtl871x_hal_init:status=0x%x\n", status));
+
+ return status;
+}
+
+uint rtw_hal_deinit(struct adapter *adapt)
+{
+ uint status = _SUCCESS;
+
+ status = adapt->HalFunc.hal_deinit(adapt);
+
+ if (status == _SUCCESS)
+ adapt->hw_init_completed = false;
+ else
+ DBG_88E("\n rtw_hal_deinit: hal_init fail\n");
+
+ return status;
+}
+
+void rtw_hal_set_hwreg(struct adapter *adapt, u8 variable, u8 *val)
+{
+ if (adapt->HalFunc.SetHwRegHandler)
+ adapt->HalFunc.SetHwRegHandler(adapt, variable, val);
+}
+
+void rtw_hal_get_hwreg(struct adapter *adapt, u8 variable, u8 *val)
+{
+ if (adapt->HalFunc.GetHwRegHandler)
+ adapt->HalFunc.GetHwRegHandler(adapt, variable, val);
+}
+
+u8 rtw_hal_set_def_var(struct adapter *adapt, enum hal_def_variable var,
+ void *val)
+{
+ if (adapt->HalFunc.SetHalDefVarHandler)
+ return adapt->HalFunc.SetHalDefVarHandler(adapt, var, val);
+ return _FAIL;
+}
+
+u8 rtw_hal_get_def_var(struct adapter *adapt,
+ enum hal_def_variable var, void *val)
+{
+ if (adapt->HalFunc.GetHalDefVarHandler)
+ return adapt->HalFunc.GetHalDefVarHandler(adapt, var, val);
+ return _FAIL;
+}
+
+void rtw_hal_set_odm_var(struct adapter *adapt,
+ enum hal_odm_variable var, void *val1,
+ bool set)
+{
+ if (adapt->HalFunc.SetHalODMVarHandler)
+ adapt->HalFunc.SetHalODMVarHandler(adapt, var,
+ val1, set);
+}
+
+void rtw_hal_enable_interrupt(struct adapter *adapt)
+{
+ if (adapt->HalFunc.enable_interrupt)
+ adapt->HalFunc.enable_interrupt(adapt);
+ else
+ DBG_88E("%s: HalFunc.enable_interrupt is NULL!\n", __func__);
+}
+
+void rtw_hal_disable_interrupt(struct adapter *adapt)
+{
+ if (adapt->HalFunc.disable_interrupt)
+ adapt->HalFunc.disable_interrupt(adapt);
+ else
+ DBG_88E("%s: HalFunc.disable_interrupt is NULL!\n", __func__);
+}
+
+u32 rtw_hal_inirp_init(struct adapter *adapt)
+{
+ u32 rst = _FAIL;
+
+ if (adapt->HalFunc.inirp_init)
+ rst = adapt->HalFunc.inirp_init(adapt);
+ else
+ DBG_88E(" %s HalFunc.inirp_init is NULL!!!\n", __func__);
+ return rst;
+}
+
+u32 rtw_hal_inirp_deinit(struct adapter *adapt)
+{
+ if (adapt->HalFunc.inirp_deinit)
+ return adapt->HalFunc.inirp_deinit(adapt);
+
+ return _FAIL;
+}
+
+s32 rtw_hal_xmit(struct adapter *adapt, struct xmit_frame *pxmitframe)
+{
+ if (adapt->HalFunc.hal_xmit)
+ return adapt->HalFunc.hal_xmit(adapt, pxmitframe);
+
+ return false;
+}
+
+s32 rtw_hal_mgnt_xmit(struct adapter *adapt, struct xmit_frame *pmgntframe)
+{
+ s32 ret = _FAIL;
+ if (adapt->HalFunc.mgnt_xmit)
+ ret = adapt->HalFunc.mgnt_xmit(adapt, pmgntframe);
+ return ret;
+}
+
+s32 rtw_hal_init_xmit_priv(struct adapter *adapt)
+{
+ if (adapt->HalFunc.init_xmit_priv != NULL)
+ return adapt->HalFunc.init_xmit_priv(adapt);
+ return _FAIL;
+}
+
+s32 rtw_hal_init_recv_priv(struct adapter *adapt)
+{
+ if (adapt->HalFunc.init_recv_priv)
+ return adapt->HalFunc.init_recv_priv(adapt);
+
+ return _FAIL;
+}
+
+void rtw_hal_free_recv_priv(struct adapter *adapt)
+{
+ if (adapt->HalFunc.free_recv_priv)
+ adapt->HalFunc.free_recv_priv(adapt);
+}
+
+void rtw_hal_update_ra_mask(struct adapter *adapt, u32 mac_id, u8 rssi_level)
+{
+ struct mlme_priv *pmlmepriv = &(adapt->mlmepriv);
+
+ if (check_fwstate(pmlmepriv, WIFI_AP_STATE) == true) {
+#ifdef CONFIG_88EU_AP_MODE
+ struct sta_info *psta = NULL;
+ struct sta_priv *pstapriv = &adapt->stapriv;
+ if ((mac_id-1) > 0)
+ psta = pstapriv->sta_aid[(mac_id-1) - 1];
+ if (psta)
+ add_RATid(adapt, psta, 0);/* todo: based on rssi_level*/
+#endif
+ } else {
+ if (adapt->HalFunc.UpdateRAMaskHandler)
+ adapt->HalFunc.UpdateRAMaskHandler(adapt, mac_id,
+ rssi_level);
+ }
+}
+
+void rtw_hal_add_ra_tid(struct adapter *adapt, u32 bitmap, u8 arg,
+ u8 rssi_level)
+{
+ if (adapt->HalFunc.Add_RateATid)
+ adapt->HalFunc.Add_RateATid(adapt, bitmap, arg,
+ rssi_level);
+}
+
+u32 rtw_hal_read_rfreg(struct adapter *adapt, enum rf_radio_path rfpath,
+ u32 regaddr, u32 bitmask)
+{
+ u32 data = 0;
+
+ if (adapt->HalFunc.read_rfreg)
+ data = adapt->HalFunc.read_rfreg(adapt, rfpath, regaddr,
+ bitmask);
+ return data;
+}
+
+void rtw_hal_write_rfreg(struct adapter *adapt, enum rf_radio_path rfpath,
+ u32 regaddr, u32 bitmask, u32 data)
+{
+ if (adapt->HalFunc.write_rfreg)
+ adapt->HalFunc.write_rfreg(adapt, rfpath, regaddr,
+ bitmask, data);
+}
+
+void rtw_hal_set_bwmode(struct adapter *adapt,
+ enum ht_channel_width bandwidth, u8 offset)
+{
+ if (adapt->HalFunc.set_bwmode_handler)
+ adapt->HalFunc.set_bwmode_handler(adapt, bandwidth,
+ offset);
+}
+
+void rtw_hal_set_chan(struct adapter *adapt, u8 channel)
+{
+ if (adapt->HalFunc.set_channel_handler)
+ adapt->HalFunc.set_channel_handler(adapt, channel);
+}
+
+void rtw_hal_dm_watchdog(struct adapter *adapt)
+{
+ if (adapt->HalFunc.hal_dm_watchdog)
+ adapt->HalFunc.hal_dm_watchdog(adapt);
+}
+
+void rtw_hal_bcn_related_reg_setting(struct adapter *adapt)
+{
+ if (adapt->HalFunc.SetBeaconRelatedRegistersHandler)
+ adapt->HalFunc.SetBeaconRelatedRegistersHandler(adapt);
+}
+
+u8 rtw_hal_antdiv_before_linked(struct adapter *adapt)
+{
+ if (adapt->HalFunc.AntDivBeforeLinkHandler)
+ return adapt->HalFunc.AntDivBeforeLinkHandler(adapt);
+ return false;
+}
+
+void rtw_hal_antdiv_rssi_compared(struct adapter *adapt,
+ struct wlan_bssid_ex *dst,
+ struct wlan_bssid_ex *src)
+{
+ if (adapt->HalFunc.AntDivCompareHandler)
+ adapt->HalFunc.AntDivCompareHandler(adapt, dst, src);
+}
+
+void rtw_hal_sreset_init(struct adapter *adapt)
+{
+ if (adapt->HalFunc.sreset_init_value)
+ adapt->HalFunc.sreset_init_value(adapt);
+}
+
+void rtw_hal_notch_filter(struct adapter *adapter, bool enable)
+{
+ if (adapter->HalFunc.hal_notch_filter)
+ adapter->HalFunc.hal_notch_filter(adapter, enable);
+}
+
+void rtw_hal_reset_security_engine(struct adapter *adapter)
+{
+ if (adapter->HalFunc.hal_reset_security_engine)
+ adapter->HalFunc.hal_reset_security_engine(adapter);
+}
diff --git a/drivers/staging/rtl8188eu/hal/mac_cfg.c b/drivers/staging/rtl8188eu/hal/mac_cfg.c
new file mode 100644
index 000000000..febc83a5a
--- /dev/null
+++ b/drivers/staging/rtl8188eu/hal/mac_cfg.c
@@ -0,0 +1,134 @@
+/******************************************************************************
+*
+* Copyright(c) 2007 - 2011 Realtek Corporation. All rights reserved.
+*
+* This program is free software; you can redistribute it and/or modify it
+* under the terms of version 2 of the GNU General Public License as
+* published by the Free Software Foundation.
+*
+* This program is distributed in the hope that it will be useful, but WITHOUT
+* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+* more details.
+*
+* You should have received a copy of the GNU General Public License along with
+* this program; if not, write to the Free Software Foundation, Inc.,
+* 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+*
+*
+******************************************************************************/
+
+#include "odm_precomp.h"
+#include "phy.h"
+#include <rtw_iol.h>
+
+/* MAC_REG.TXT */
+
+static u32 array_MAC_REG_8188E[] = {
+ 0x026, 0x00000041,
+ 0x027, 0x00000035,
+ 0x428, 0x0000000A,
+ 0x429, 0x00000010,
+ 0x430, 0x00000000,
+ 0x431, 0x00000001,
+ 0x432, 0x00000002,
+ 0x433, 0x00000004,
+ 0x434, 0x00000005,
+ 0x435, 0x00000006,
+ 0x436, 0x00000007,
+ 0x437, 0x00000008,
+ 0x438, 0x00000000,
+ 0x439, 0x00000000,
+ 0x43A, 0x00000001,
+ 0x43B, 0x00000002,
+ 0x43C, 0x00000004,
+ 0x43D, 0x00000005,
+ 0x43E, 0x00000006,
+ 0x43F, 0x00000007,
+ 0x440, 0x0000005D,
+ 0x441, 0x00000001,
+ 0x442, 0x00000000,
+ 0x444, 0x00000015,
+ 0x445, 0x000000F0,
+ 0x446, 0x0000000F,
+ 0x447, 0x00000000,
+ 0x458, 0x00000041,
+ 0x459, 0x000000A8,
+ 0x45A, 0x00000072,
+ 0x45B, 0x000000B9,
+ 0x460, 0x00000066,
+ 0x461, 0x00000066,
+ 0x480, 0x00000008,
+ 0x4C8, 0x000000FF,
+ 0x4C9, 0x00000008,
+ 0x4CC, 0x000000FF,
+ 0x4CD, 0x000000FF,
+ 0x4CE, 0x00000001,
+ 0x4D3, 0x00000001,
+ 0x500, 0x00000026,
+ 0x501, 0x000000A2,
+ 0x502, 0x0000002F,
+ 0x503, 0x00000000,
+ 0x504, 0x00000028,
+ 0x505, 0x000000A3,
+ 0x506, 0x0000005E,
+ 0x507, 0x00000000,
+ 0x508, 0x0000002B,
+ 0x509, 0x000000A4,
+ 0x50A, 0x0000005E,
+ 0x50B, 0x00000000,
+ 0x50C, 0x0000004F,
+ 0x50D, 0x000000A4,
+ 0x50E, 0x00000000,
+ 0x50F, 0x00000000,
+ 0x512, 0x0000001C,
+ 0x514, 0x0000000A,
+ 0x516, 0x0000000A,
+ 0x525, 0x0000004F,
+ 0x550, 0x00000010,
+ 0x551, 0x00000010,
+ 0x559, 0x00000002,
+ 0x55D, 0x000000FF,
+ 0x605, 0x00000030,
+ 0x608, 0x0000000E,
+ 0x609, 0x0000002A,
+ 0x620, 0x000000FF,
+ 0x621, 0x000000FF,
+ 0x622, 0x000000FF,
+ 0x623, 0x000000FF,
+ 0x624, 0x000000FF,
+ 0x625, 0x000000FF,
+ 0x626, 0x000000FF,
+ 0x627, 0x000000FF,
+ 0x652, 0x00000020,
+ 0x63C, 0x0000000A,
+ 0x63D, 0x0000000A,
+ 0x63E, 0x0000000E,
+ 0x63F, 0x0000000E,
+ 0x640, 0x00000040,
+ 0x66E, 0x00000005,
+ 0x700, 0x00000021,
+ 0x701, 0x00000043,
+ 0x702, 0x00000065,
+ 0x703, 0x00000087,
+ 0x708, 0x00000021,
+ 0x709, 0x00000043,
+ 0x70A, 0x00000065,
+ 0x70B, 0x00000087,
+};
+
+bool rtl88eu_phy_mac_config(struct adapter *adapt)
+{
+ u32 i;
+ u32 arraylength;
+ u32 *ptrarray;
+
+ arraylength = sizeof(array_MAC_REG_8188E)/sizeof(u32);
+ ptrarray = array_MAC_REG_8188E;
+
+ for (i = 0; i < arraylength; i = i + 2)
+ usb_write8(adapt, ptrarray[i], (u8)ptrarray[i + 1]);
+
+ usb_write8(adapt, REG_MAX_AGGR_NUM, MAX_AGGR_NUM);
+ return true;
+}
diff --git a/drivers/staging/rtl8188eu/hal/odm.c b/drivers/staging/rtl8188eu/hal/odm.c
new file mode 100644
index 000000000..28b5e7bd4
--- /dev/null
+++ b/drivers/staging/rtl8188eu/hal/odm.c
@@ -0,0 +1,1368 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2011 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ *
+ ******************************************************************************/
+
+/* include files */
+
+#include "odm_precomp.h"
+#include "phy.h"
+
+u32 GlobalDebugLevel;
+static const u16 dB_Invert_Table[8][12] = {
+ {1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 4, 4},
+ {4, 5, 6, 6, 7, 8, 9, 10, 11, 13, 14, 16},
+ {18, 20, 22, 25, 28, 32, 35, 40, 45, 50, 56, 63},
+ {71, 79, 89, 100, 112, 126, 141, 158, 178, 200, 224, 251},
+ {282, 316, 355, 398, 447, 501, 562, 631, 708, 794, 891, 1000},
+ {1122, 1259, 1413, 1585, 1778, 1995, 2239, 2512, 2818, 3162, 3548, 3981},
+ {4467, 5012, 5623, 6310, 7079, 7943, 8913, 10000, 11220, 12589, 14125, 15849},
+ {17783, 19953, 22387, 25119, 28184, 31623, 35481, 39811, 44668, 50119, 56234, 65535}
+};
+
+/* avoid to warn in FreeBSD ==> To DO modify */
+static u32 EDCAParam[HT_IOT_PEER_MAX][3] = {
+ /* UL DL */
+ {0x5ea42b, 0x5ea42b, 0x5ea42b}, /* 0:unknown AP */
+ {0xa44f, 0x5ea44f, 0x5e431c}, /* 1:realtek AP */
+ {0x5ea42b, 0x5ea42b, 0x5ea42b}, /* 2:unknown AP => realtek_92SE */
+ {0x5ea32b, 0x5ea42b, 0x5e4322}, /* 3:broadcom AP */
+ {0x5ea422, 0x00a44f, 0x00a44f}, /* 4:ralink AP */
+ {0x5ea322, 0x00a630, 0x00a44f}, /* 5:atheros AP */
+ {0x5e4322, 0x5e4322, 0x5e4322},/* 6:cisco AP */
+ {0x5ea44f, 0x00a44f, 0x5ea42b}, /* 8:marvell AP */
+ {0x5ea42b, 0x5ea42b, 0x5ea42b}, /* 10:unknown AP=> 92U AP */
+ {0x5ea42b, 0xa630, 0x5e431c}, /* 11:airgocap AP */
+};
+
+/* Global var */
+u32 OFDMSwingTable[OFDM_TABLE_SIZE_92D] = {
+ 0x7f8001fe, /* 0, +6.0dB */
+ 0x788001e2, /* 1, +5.5dB */
+ 0x71c001c7, /* 2, +5.0dB */
+ 0x6b8001ae, /* 3, +4.5dB */
+ 0x65400195, /* 4, +4.0dB */
+ 0x5fc0017f, /* 5, +3.5dB */
+ 0x5a400169, /* 6, +3.0dB */
+ 0x55400155, /* 7, +2.5dB */
+ 0x50800142, /* 8, +2.0dB */
+ 0x4c000130, /* 9, +1.5dB */
+ 0x47c0011f, /* 10, +1.0dB */
+ 0x43c0010f, /* 11, +0.5dB */
+ 0x40000100, /* 12, +0dB */
+ 0x3c8000f2, /* 13, -0.5dB */
+ 0x390000e4, /* 14, -1.0dB */
+ 0x35c000d7, /* 15, -1.5dB */
+ 0x32c000cb, /* 16, -2.0dB */
+ 0x300000c0, /* 17, -2.5dB */
+ 0x2d4000b5, /* 18, -3.0dB */
+ 0x2ac000ab, /* 19, -3.5dB */
+ 0x288000a2, /* 20, -4.0dB */
+ 0x26000098, /* 21, -4.5dB */
+ 0x24000090, /* 22, -5.0dB */
+ 0x22000088, /* 23, -5.5dB */
+ 0x20000080, /* 24, -6.0dB */
+ 0x1e400079, /* 25, -6.5dB */
+ 0x1c800072, /* 26, -7.0dB */
+ 0x1b00006c, /* 27. -7.5dB */
+ 0x19800066, /* 28, -8.0dB */
+ 0x18000060, /* 29, -8.5dB */
+ 0x16c0005b, /* 30, -9.0dB */
+ 0x15800056, /* 31, -9.5dB */
+ 0x14400051, /* 32, -10.0dB */
+ 0x1300004c, /* 33, -10.5dB */
+ 0x12000048, /* 34, -11.0dB */
+ 0x11000044, /* 35, -11.5dB */
+ 0x10000040, /* 36, -12.0dB */
+ 0x0f00003c,/* 37, -12.5dB */
+ 0x0e400039,/* 38, -13.0dB */
+ 0x0d800036,/* 39, -13.5dB */
+ 0x0cc00033,/* 40, -14.0dB */
+ 0x0c000030,/* 41, -14.5dB */
+ 0x0b40002d,/* 42, -15.0dB */
+};
+
+u8 CCKSwingTable_Ch1_Ch13[CCK_TABLE_SIZE][8] = {
+ {0x36, 0x35, 0x2e, 0x25, 0x1c, 0x12, 0x09, 0x04}, /* 0, +0dB */
+ {0x33, 0x32, 0x2b, 0x23, 0x1a, 0x11, 0x08, 0x04}, /* 1, -0.5dB */
+ {0x30, 0x2f, 0x29, 0x21, 0x19, 0x10, 0x08, 0x03}, /* 2, -1.0dB */
+ {0x2d, 0x2d, 0x27, 0x1f, 0x18, 0x0f, 0x08, 0x03}, /* 3, -1.5dB */
+ {0x2b, 0x2a, 0x25, 0x1e, 0x16, 0x0e, 0x07, 0x03}, /* 4, -2.0dB */
+ {0x28, 0x28, 0x22, 0x1c, 0x15, 0x0d, 0x07, 0x03}, /* 5, -2.5dB */
+ {0x26, 0x25, 0x21, 0x1b, 0x14, 0x0d, 0x06, 0x03}, /* 6, -3.0dB */
+ {0x24, 0x23, 0x1f, 0x19, 0x13, 0x0c, 0x06, 0x03}, /* 7, -3.5dB */
+ {0x22, 0x21, 0x1d, 0x18, 0x11, 0x0b, 0x06, 0x02}, /* 8, -4.0dB */
+ {0x20, 0x20, 0x1b, 0x16, 0x11, 0x08, 0x05, 0x02}, /* 9, -4.5dB */
+ {0x1f, 0x1e, 0x1a, 0x15, 0x10, 0x0a, 0x05, 0x02}, /* 10, -5.0dB */
+ {0x1d, 0x1c, 0x18, 0x14, 0x0f, 0x0a, 0x05, 0x02}, /* 11, -5.5dB */
+ {0x1b, 0x1a, 0x17, 0x13, 0x0e, 0x09, 0x04, 0x02}, /* 12, -6.0dB */
+ {0x1a, 0x19, 0x16, 0x12, 0x0d, 0x09, 0x04, 0x02}, /* 13, -6.5dB */
+ {0x18, 0x17, 0x15, 0x11, 0x0c, 0x08, 0x04, 0x02}, /* 14, -7.0dB */
+ {0x17, 0x16, 0x13, 0x10, 0x0c, 0x08, 0x04, 0x02}, /* 15, -7.5dB */
+ {0x16, 0x15, 0x12, 0x0f, 0x0b, 0x07, 0x04, 0x01}, /* 16, -8.0dB */
+ {0x14, 0x14, 0x11, 0x0e, 0x0b, 0x07, 0x03, 0x02}, /* 17, -8.5dB */
+ {0x13, 0x13, 0x10, 0x0d, 0x0a, 0x06, 0x03, 0x01}, /* 18, -9.0dB */
+ {0x12, 0x12, 0x0f, 0x0c, 0x09, 0x06, 0x03, 0x01}, /* 19, -9.5dB */
+ {0x11, 0x11, 0x0f, 0x0c, 0x09, 0x06, 0x03, 0x01}, /* 20, -10.0dB */
+ {0x10, 0x10, 0x0e, 0x0b, 0x08, 0x05, 0x03, 0x01}, /* 21, -10.5dB */
+ {0x0f, 0x0f, 0x0d, 0x0b, 0x08, 0x05, 0x03, 0x01}, /* 22, -11.0dB */
+ {0x0e, 0x0e, 0x0c, 0x0a, 0x08, 0x05, 0x02, 0x01}, /* 23, -11.5dB */
+ {0x0d, 0x0d, 0x0c, 0x0a, 0x07, 0x05, 0x02, 0x01}, /* 24, -12.0dB */
+ {0x0d, 0x0c, 0x0b, 0x09, 0x07, 0x04, 0x02, 0x01}, /* 25, -12.5dB */
+ {0x0c, 0x0c, 0x0a, 0x09, 0x06, 0x04, 0x02, 0x01}, /* 26, -13.0dB */
+ {0x0b, 0x0b, 0x0a, 0x08, 0x06, 0x04, 0x02, 0x01}, /* 27, -13.5dB */
+ {0x0b, 0x0a, 0x09, 0x08, 0x06, 0x04, 0x02, 0x01}, /* 28, -14.0dB */
+ {0x0a, 0x0a, 0x09, 0x07, 0x05, 0x03, 0x02, 0x01}, /* 29, -14.5dB */
+ {0x0a, 0x09, 0x08, 0x07, 0x05, 0x03, 0x02, 0x01}, /* 30, -15.0dB */
+ {0x09, 0x09, 0x08, 0x06, 0x05, 0x03, 0x01, 0x01}, /* 31, -15.5dB */
+ {0x09, 0x08, 0x07, 0x06, 0x04, 0x03, 0x01, 0x01} /* 32, -16.0dB */
+};
+
+u8 CCKSwingTable_Ch14[CCK_TABLE_SIZE][8] = {
+ {0x36, 0x35, 0x2e, 0x1b, 0x00, 0x00, 0x00, 0x00}, /* 0, +0dB */
+ {0x33, 0x32, 0x2b, 0x19, 0x00, 0x00, 0x00, 0x00}, /* 1, -0.5dB */
+ {0x30, 0x2f, 0x29, 0x18, 0x00, 0x00, 0x00, 0x00}, /* 2, -1.0dB */
+ {0x2d, 0x2d, 0x17, 0x17, 0x00, 0x00, 0x00, 0x00}, /* 3, -1.5dB */
+ {0x2b, 0x2a, 0x25, 0x15, 0x00, 0x00, 0x00, 0x00}, /* 4, -2.0dB */
+ {0x28, 0x28, 0x24, 0x14, 0x00, 0x00, 0x00, 0x00}, /* 5, -2.5dB */
+ {0x26, 0x25, 0x21, 0x13, 0x00, 0x00, 0x00, 0x00}, /* 6, -3.0dB */
+ {0x24, 0x23, 0x1f, 0x12, 0x00, 0x00, 0x00, 0x00}, /* 7, -3.5dB */
+ {0x22, 0x21, 0x1d, 0x11, 0x00, 0x00, 0x00, 0x00}, /* 8, -4.0dB */
+ {0x20, 0x20, 0x1b, 0x10, 0x00, 0x00, 0x00, 0x00}, /* 9, -4.5dB */
+ {0x1f, 0x1e, 0x1a, 0x0f, 0x00, 0x00, 0x00, 0x00}, /* 10, -5.0dB */
+ {0x1d, 0x1c, 0x18, 0x0e, 0x00, 0x00, 0x00, 0x00}, /* 11, -5.5dB */
+ {0x1b, 0x1a, 0x17, 0x0e, 0x00, 0x00, 0x00, 0x00}, /* 12, -6.0dB */
+ {0x1a, 0x19, 0x16, 0x0d, 0x00, 0x00, 0x00, 0x00}, /* 13, -6.5dB */
+ {0x18, 0x17, 0x15, 0x0c, 0x00, 0x00, 0x00, 0x00}, /* 14, -7.0dB */
+ {0x17, 0x16, 0x13, 0x0b, 0x00, 0x00, 0x00, 0x00}, /* 15, -7.5dB */
+ {0x16, 0x15, 0x12, 0x0b, 0x00, 0x00, 0x00, 0x00}, /* 16, -8.0dB */
+ {0x14, 0x14, 0x11, 0x0a, 0x00, 0x00, 0x00, 0x00}, /* 17, -8.5dB */
+ {0x13, 0x13, 0x10, 0x0a, 0x00, 0x00, 0x00, 0x00}, /* 18, -9.0dB */
+ {0x12, 0x12, 0x0f, 0x09, 0x00, 0x00, 0x00, 0x00}, /* 19, -9.5dB */
+ {0x11, 0x11, 0x0f, 0x09, 0x00, 0x00, 0x00, 0x00}, /* 20, -10.0dB */
+ {0x10, 0x10, 0x0e, 0x08, 0x00, 0x00, 0x00, 0x00}, /* 21, -10.5dB */
+ {0x0f, 0x0f, 0x0d, 0x08, 0x00, 0x00, 0x00, 0x00}, /* 22, -11.0dB */
+ {0x0e, 0x0e, 0x0c, 0x07, 0x00, 0x00, 0x00, 0x00}, /* 23, -11.5dB */
+ {0x0d, 0x0d, 0x0c, 0x07, 0x00, 0x00, 0x00, 0x00}, /* 24, -12.0dB */
+ {0x0d, 0x0c, 0x0b, 0x06, 0x00, 0x00, 0x00, 0x00}, /* 25, -12.5dB */
+ {0x0c, 0x0c, 0x0a, 0x06, 0x00, 0x00, 0x00, 0x00}, /* 26, -13.0dB */
+ {0x0b, 0x0b, 0x0a, 0x06, 0x00, 0x00, 0x00, 0x00}, /* 27, -13.5dB */
+ {0x0b, 0x0a, 0x09, 0x05, 0x00, 0x00, 0x00, 0x00}, /* 28, -14.0dB */
+ {0x0a, 0x0a, 0x09, 0x05, 0x00, 0x00, 0x00, 0x00}, /* 29, -14.5dB */
+ {0x0a, 0x09, 0x08, 0x05, 0x00, 0x00, 0x00, 0x00}, /* 30, -15.0dB */
+ {0x09, 0x09, 0x08, 0x05, 0x00, 0x00, 0x00, 0x00}, /* 31, -15.5dB */
+ {0x09, 0x08, 0x07, 0x04, 0x00, 0x00, 0x00, 0x00} /* 32, -16.0dB */
+};
+
+
+#define RxDefaultAnt1 0x65a9
+#define RxDefaultAnt2 0x569a
+
+void ODM_InitDebugSetting(struct odm_dm_struct *pDM_Odm)
+{
+ pDM_Odm->DebugLevel = ODM_DBG_TRACE;
+
+ pDM_Odm->DebugComponents = 0;
+}
+
+/* 3 Export Interface */
+
+/* 2011/09/21 MH Add to describe different team necessary resource allocate?? */
+void ODM_DMInit(struct odm_dm_struct *pDM_Odm)
+{
+ /* 2012.05.03 Luke: For all IC series */
+ odm_CommonInfoSelfInit(pDM_Odm);
+ odm_CmnInfoInit_Debug(pDM_Odm);
+ odm_DIGInit(pDM_Odm);
+ odm_RateAdaptiveMaskInit(pDM_Odm);
+
+ odm_DynamicTxPowerInit(pDM_Odm);
+ odm_TXPowerTrackingInit(pDM_Odm);
+ ODM_EdcaTurboInit(pDM_Odm);
+ ODM_RAInfo_Init_all(pDM_Odm);
+ if ((pDM_Odm->AntDivType == CG_TRX_HW_ANTDIV) ||
+ (pDM_Odm->AntDivType == CGCS_RX_HW_ANTDIV) ||
+ (pDM_Odm->AntDivType == CG_TRX_SMART_ANTDIV))
+ odm_InitHybridAntDiv(pDM_Odm);
+}
+
+/* 2011/09/20 MH This is the entry pointer for all team to execute HW out source DM. */
+/* You can not add any dummy function here, be care, you can only use DM structure */
+/* to perform any new ODM_DM. */
+void ODM_DMWatchdog(struct odm_dm_struct *pDM_Odm)
+{
+ /* 2012.05.03 Luke: For all IC series */
+ odm_CmnInfoHook_Debug(pDM_Odm);
+ odm_CmnInfoUpdate_Debug(pDM_Odm);
+ odm_CommonInfoSelfUpdate(pDM_Odm);
+ odm_FalseAlarmCounterStatistics(pDM_Odm);
+ odm_RSSIMonitorCheck(pDM_Odm);
+
+ /* Fix Leave LPS issue */
+ odm_DIG(pDM_Odm);
+ odm_CCKPacketDetectionThresh(pDM_Odm);
+
+ if (*(pDM_Odm->pbPowerSaving))
+ return;
+
+ odm_RefreshRateAdaptiveMask(pDM_Odm);
+
+ if ((pDM_Odm->AntDivType == CG_TRX_HW_ANTDIV) ||
+ (pDM_Odm->AntDivType == CGCS_RX_HW_ANTDIV) ||
+ (pDM_Odm->AntDivType == CG_TRX_SMART_ANTDIV))
+ odm_HwAntDiv(pDM_Odm);
+
+ ODM_TXPowerTrackingCheck(pDM_Odm);
+ odm_EdcaTurboCheck(pDM_Odm);
+}
+
+/* Init /.. Fixed HW value. Only init time. */
+void ODM_CmnInfoInit(struct odm_dm_struct *pDM_Odm, enum odm_common_info_def CmnInfo, u32 Value)
+{
+ /* This section is used for init value */
+ switch (CmnInfo) {
+ /* Fixed ODM value. */
+ case ODM_CMNINFO_ABILITY:
+ pDM_Odm->SupportAbility = (u32)Value;
+ break;
+ case ODM_CMNINFO_PLATFORM:
+ pDM_Odm->SupportPlatform = (u8)Value;
+ break;
+ case ODM_CMNINFO_INTERFACE:
+ pDM_Odm->SupportInterface = (u8)Value;
+ break;
+ case ODM_CMNINFO_MP_TEST_CHIP:
+ pDM_Odm->bIsMPChip = (u8)Value;
+ break;
+ case ODM_CMNINFO_IC_TYPE:
+ pDM_Odm->SupportICType = Value;
+ break;
+ case ODM_CMNINFO_CUT_VER:
+ pDM_Odm->CutVersion = (u8)Value;
+ break;
+ case ODM_CMNINFO_FAB_VER:
+ pDM_Odm->FabVersion = (u8)Value;
+ break;
+ case ODM_CMNINFO_RF_TYPE:
+ pDM_Odm->RFType = (u8)Value;
+ break;
+ case ODM_CMNINFO_RF_ANTENNA_TYPE:
+ pDM_Odm->AntDivType = (u8)Value;
+ break;
+ case ODM_CMNINFO_BOARD_TYPE:
+ pDM_Odm->BoardType = (u8)Value;
+ break;
+ case ODM_CMNINFO_EXT_LNA:
+ pDM_Odm->ExtLNA = (u8)Value;
+ break;
+ case ODM_CMNINFO_EXT_PA:
+ pDM_Odm->ExtPA = (u8)Value;
+ break;
+ case ODM_CMNINFO_EXT_TRSW:
+ pDM_Odm->ExtTRSW = (u8)Value;
+ break;
+ case ODM_CMNINFO_PATCH_ID:
+ pDM_Odm->PatchID = (u8)Value;
+ break;
+ case ODM_CMNINFO_BINHCT_TEST:
+ pDM_Odm->bInHctTest = (bool)Value;
+ break;
+ case ODM_CMNINFO_BWIFI_TEST:
+ pDM_Odm->bWIFITest = (bool)Value;
+ break;
+ case ODM_CMNINFO_SMART_CONCURRENT:
+ pDM_Odm->bDualMacSmartConcurrent = (bool)Value;
+ break;
+ /* To remove the compiler warning, must add an empty default statement to handle the other values. */
+ default:
+ /* do nothing */
+ break;
+ }
+
+ /* Tx power tracking BB swing table. */
+ /* The base index = 12. +((12-n)/2)dB 13~?? = decrease tx pwr by -((n-12)/2)dB */
+ pDM_Odm->BbSwingIdxOfdm = 12; /* Set defalut value as index 12. */
+ pDM_Odm->BbSwingIdxOfdmCurrent = 12;
+ pDM_Odm->BbSwingFlagOfdm = false;
+}
+
+void ODM_CmnInfoHook(struct odm_dm_struct *pDM_Odm, enum odm_common_info_def CmnInfo, void *pValue)
+{
+ /* */
+ /* Hook call by reference pointer. */
+ /* */
+ switch (CmnInfo) {
+ /* Dynamic call by reference pointer. */
+ case ODM_CMNINFO_MAC_PHY_MODE:
+ pDM_Odm->pMacPhyMode = (u8 *)pValue;
+ break;
+ case ODM_CMNINFO_TX_UNI:
+ pDM_Odm->pNumTxBytesUnicast = (u64 *)pValue;
+ break;
+ case ODM_CMNINFO_RX_UNI:
+ pDM_Odm->pNumRxBytesUnicast = (u64 *)pValue;
+ break;
+ case ODM_CMNINFO_WM_MODE:
+ pDM_Odm->pWirelessMode = (u8 *)pValue;
+ break;
+ case ODM_CMNINFO_BAND:
+ pDM_Odm->pBandType = (u8 *)pValue;
+ break;
+ case ODM_CMNINFO_SEC_CHNL_OFFSET:
+ pDM_Odm->pSecChOffset = (u8 *)pValue;
+ break;
+ case ODM_CMNINFO_SEC_MODE:
+ pDM_Odm->pSecurity = (u8 *)pValue;
+ break;
+ case ODM_CMNINFO_BW:
+ pDM_Odm->pBandWidth = (u8 *)pValue;
+ break;
+ case ODM_CMNINFO_CHNL:
+ pDM_Odm->pChannel = (u8 *)pValue;
+ break;
+ case ODM_CMNINFO_DMSP_GET_VALUE:
+ pDM_Odm->pbGetValueFromOtherMac = (bool *)pValue;
+ break;
+ case ODM_CMNINFO_BUDDY_ADAPTOR:
+ pDM_Odm->pBuddyAdapter = (struct adapter **)pValue;
+ break;
+ case ODM_CMNINFO_DMSP_IS_MASTER:
+ pDM_Odm->pbMasterOfDMSP = (bool *)pValue;
+ break;
+ case ODM_CMNINFO_SCAN:
+ pDM_Odm->pbScanInProcess = (bool *)pValue;
+ break;
+ case ODM_CMNINFO_POWER_SAVING:
+ pDM_Odm->pbPowerSaving = (bool *)pValue;
+ break;
+ case ODM_CMNINFO_ONE_PATH_CCA:
+ pDM_Odm->pOnePathCCA = (u8 *)pValue;
+ break;
+ case ODM_CMNINFO_DRV_STOP:
+ pDM_Odm->pbDriverStopped = (bool *)pValue;
+ break;
+ case ODM_CMNINFO_PNP_IN:
+ pDM_Odm->pbDriverIsGoingToPnpSetPowerSleep = (bool *)pValue;
+ break;
+ case ODM_CMNINFO_INIT_ON:
+ pDM_Odm->pinit_adpt_in_progress = (bool *)pValue;
+ break;
+ case ODM_CMNINFO_ANT_TEST:
+ pDM_Odm->pAntennaTest = (u8 *)pValue;
+ break;
+ case ODM_CMNINFO_NET_CLOSED:
+ pDM_Odm->pbNet_closed = (bool *)pValue;
+ break;
+ case ODM_CMNINFO_MP_MODE:
+ pDM_Odm->mp_mode = (u8 *)pValue;
+ break;
+ /* To remove the compiler warning, must add an empty default statement to handle the other values. */
+ default:
+ /* do nothing */
+ break;
+ }
+}
+
+void ODM_CmnInfoPtrArrayHook(struct odm_dm_struct *pDM_Odm, enum odm_common_info_def CmnInfo, u16 Index, void *pValue)
+{
+ /* Hook call by reference pointer. */
+ switch (CmnInfo) {
+ /* Dynamic call by reference pointer. */
+ case ODM_CMNINFO_STA_STATUS:
+ pDM_Odm->pODM_StaInfo[Index] = (struct sta_info *)pValue;
+ break;
+ /* To remove the compiler warning, must add an empty default statement to handle the other values. */
+ default:
+ /* do nothing */
+ break;
+ }
+}
+
+/* Update Band/CHannel/.. The values are dynamic but non-per-packet. */
+void ODM_CmnInfoUpdate(struct odm_dm_struct *pDM_Odm, u32 CmnInfo, u64 Value)
+{
+ /* */
+ /* This init variable may be changed in run time. */
+ /* */
+ switch (CmnInfo) {
+ case ODM_CMNINFO_ABILITY:
+ pDM_Odm->SupportAbility = (u32)Value;
+ break;
+ case ODM_CMNINFO_RF_TYPE:
+ pDM_Odm->RFType = (u8)Value;
+ break;
+ case ODM_CMNINFO_WIFI_DIRECT:
+ pDM_Odm->bWIFI_Direct = (bool)Value;
+ break;
+ case ODM_CMNINFO_WIFI_DISPLAY:
+ pDM_Odm->bWIFI_Display = (bool)Value;
+ break;
+ case ODM_CMNINFO_LINK:
+ pDM_Odm->bLinked = (bool)Value;
+ break;
+ case ODM_CMNINFO_RSSI_MIN:
+ pDM_Odm->RSSI_Min = (u8)Value;
+ break;
+ case ODM_CMNINFO_DBG_COMP:
+ pDM_Odm->DebugComponents = Value;
+ break;
+ case ODM_CMNINFO_DBG_LEVEL:
+ pDM_Odm->DebugLevel = (u32)Value;
+ break;
+ case ODM_CMNINFO_RA_THRESHOLD_HIGH:
+ pDM_Odm->RateAdaptive.HighRSSIThresh = (u8)Value;
+ break;
+ case ODM_CMNINFO_RA_THRESHOLD_LOW:
+ pDM_Odm->RateAdaptive.LowRSSIThresh = (u8)Value;
+ break;
+ }
+}
+
+void odm_CommonInfoSelfInit(struct odm_dm_struct *pDM_Odm)
+{
+ struct adapter *adapter = pDM_Odm->Adapter;
+
+ pDM_Odm->bCckHighPower = (bool)phy_query_bb_reg(adapter, 0x824, BIT9);
+ pDM_Odm->RFPathRxEnable = (u8)phy_query_bb_reg(adapter, 0xc04, 0x0F);
+
+ ODM_InitDebugSetting(pDM_Odm);
+}
+
+void odm_CommonInfoSelfUpdate(struct odm_dm_struct *pDM_Odm)
+{
+ u8 EntryCnt = 0;
+ u8 i;
+ struct sta_info *pEntry;
+
+ if (*(pDM_Odm->pBandWidth) == ODM_BW40M) {
+ if (*(pDM_Odm->pSecChOffset) == 1)
+ pDM_Odm->ControlChannel = *(pDM_Odm->pChannel) - 2;
+ else if (*(pDM_Odm->pSecChOffset) == 2)
+ pDM_Odm->ControlChannel = *(pDM_Odm->pChannel) + 2;
+ } else {
+ pDM_Odm->ControlChannel = *(pDM_Odm->pChannel);
+ }
+
+ for (i = 0; i < ODM_ASSOCIATE_ENTRY_NUM; i++) {
+ pEntry = pDM_Odm->pODM_StaInfo[i];
+ if (IS_STA_VALID(pEntry))
+ EntryCnt++;
+ }
+ if (EntryCnt == 1)
+ pDM_Odm->bOneEntryOnly = true;
+ else
+ pDM_Odm->bOneEntryOnly = false;
+}
+
+void odm_CmnInfoInit_Debug(struct odm_dm_struct *pDM_Odm)
+{
+ ODM_RT_TRACE(pDM_Odm, ODM_COMP_COMMON, ODM_DBG_LOUD, ("odm_CmnInfoInit_Debug==>\n"));
+ ODM_RT_TRACE(pDM_Odm, ODM_COMP_COMMON, ODM_DBG_LOUD, ("SupportPlatform=%d\n", pDM_Odm->SupportPlatform));
+ ODM_RT_TRACE(pDM_Odm, ODM_COMP_COMMON, ODM_DBG_LOUD, ("SupportAbility=0x%x\n", pDM_Odm->SupportAbility));
+ ODM_RT_TRACE(pDM_Odm, ODM_COMP_COMMON, ODM_DBG_LOUD, ("SupportInterface=%d\n", pDM_Odm->SupportInterface));
+ ODM_RT_TRACE(pDM_Odm, ODM_COMP_COMMON, ODM_DBG_LOUD, ("SupportICType=0x%x\n", pDM_Odm->SupportICType));
+ ODM_RT_TRACE(pDM_Odm, ODM_COMP_COMMON, ODM_DBG_LOUD, ("CutVersion=%d\n", pDM_Odm->CutVersion));
+ ODM_RT_TRACE(pDM_Odm, ODM_COMP_COMMON, ODM_DBG_LOUD, ("FabVersion=%d\n", pDM_Odm->FabVersion));
+ ODM_RT_TRACE(pDM_Odm, ODM_COMP_COMMON, ODM_DBG_LOUD, ("RFType=%d\n", pDM_Odm->RFType));
+ ODM_RT_TRACE(pDM_Odm, ODM_COMP_COMMON, ODM_DBG_LOUD, ("BoardType=%d\n", pDM_Odm->BoardType));
+ ODM_RT_TRACE(pDM_Odm, ODM_COMP_COMMON, ODM_DBG_LOUD, ("ExtLNA=%d\n", pDM_Odm->ExtLNA));
+ ODM_RT_TRACE(pDM_Odm, ODM_COMP_COMMON, ODM_DBG_LOUD, ("ExtPA=%d\n", pDM_Odm->ExtPA));
+ ODM_RT_TRACE(pDM_Odm, ODM_COMP_COMMON, ODM_DBG_LOUD, ("ExtTRSW=%d\n", pDM_Odm->ExtTRSW));
+ ODM_RT_TRACE(pDM_Odm, ODM_COMP_COMMON, ODM_DBG_LOUD, ("PatchID=%d\n", pDM_Odm->PatchID));
+ ODM_RT_TRACE(pDM_Odm, ODM_COMP_COMMON, ODM_DBG_LOUD, ("bInHctTest=%d\n", pDM_Odm->bInHctTest));
+ ODM_RT_TRACE(pDM_Odm, ODM_COMP_COMMON, ODM_DBG_LOUD, ("bWIFITest=%d\n", pDM_Odm->bWIFITest));
+ ODM_RT_TRACE(pDM_Odm, ODM_COMP_COMMON, ODM_DBG_LOUD, ("bDualMacSmartConcurrent=%d\n", pDM_Odm->bDualMacSmartConcurrent));
+}
+
+void odm_CmnInfoHook_Debug(struct odm_dm_struct *pDM_Odm)
+{
+ ODM_RT_TRACE(pDM_Odm, ODM_COMP_COMMON, ODM_DBG_LOUD, ("odm_CmnInfoHook_Debug==>\n"));
+ ODM_RT_TRACE(pDM_Odm, ODM_COMP_COMMON, ODM_DBG_LOUD, ("pNumTxBytesUnicast=%llu\n", *(pDM_Odm->pNumTxBytesUnicast)));
+ ODM_RT_TRACE(pDM_Odm, ODM_COMP_COMMON, ODM_DBG_LOUD, ("pNumRxBytesUnicast=%llu\n", *(pDM_Odm->pNumRxBytesUnicast)));
+ ODM_RT_TRACE(pDM_Odm, ODM_COMP_COMMON, ODM_DBG_LOUD, ("pWirelessMode=0x%x\n", *(pDM_Odm->pWirelessMode)));
+ ODM_RT_TRACE(pDM_Odm, ODM_COMP_COMMON, ODM_DBG_LOUD, ("pSecChOffset=%d\n", *(pDM_Odm->pSecChOffset)));
+ ODM_RT_TRACE(pDM_Odm, ODM_COMP_COMMON, ODM_DBG_LOUD, ("pSecurity=%d\n", *(pDM_Odm->pSecurity)));
+ ODM_RT_TRACE(pDM_Odm, ODM_COMP_COMMON, ODM_DBG_LOUD, ("pBandWidth=%d\n", *(pDM_Odm->pBandWidth)));
+ ODM_RT_TRACE(pDM_Odm, ODM_COMP_COMMON, ODM_DBG_LOUD, ("pChannel=%d\n", *(pDM_Odm->pChannel)));
+
+ ODM_RT_TRACE(pDM_Odm, ODM_COMP_COMMON, ODM_DBG_LOUD, ("pbScanInProcess=%d\n", *(pDM_Odm->pbScanInProcess)));
+ ODM_RT_TRACE(pDM_Odm, ODM_COMP_COMMON, ODM_DBG_LOUD, ("pbPowerSaving=%d\n", *(pDM_Odm->pbPowerSaving)));
+}
+
+void odm_CmnInfoUpdate_Debug(struct odm_dm_struct *pDM_Odm)
+{
+ ODM_RT_TRACE(pDM_Odm, ODM_COMP_COMMON, ODM_DBG_LOUD, ("odm_CmnInfoUpdate_Debug==>\n"));
+ ODM_RT_TRACE(pDM_Odm, ODM_COMP_COMMON, ODM_DBG_LOUD, ("bWIFI_Direct=%d\n", pDM_Odm->bWIFI_Direct));
+ ODM_RT_TRACE(pDM_Odm, ODM_COMP_COMMON, ODM_DBG_LOUD, ("bWIFI_Display=%d\n", pDM_Odm->bWIFI_Display));
+ ODM_RT_TRACE(pDM_Odm, ODM_COMP_COMMON, ODM_DBG_LOUD, ("bLinked=%d\n", pDM_Odm->bLinked));
+ ODM_RT_TRACE(pDM_Odm, ODM_COMP_COMMON, ODM_DBG_LOUD, ("RSSI_Min=%d\n", pDM_Odm->RSSI_Min));
+}
+
+void ODM_Write_DIG(struct odm_dm_struct *pDM_Odm, u8 CurrentIGI)
+{
+ struct rtw_dig *pDM_DigTable = &pDM_Odm->DM_DigTable;
+ struct adapter *adapter = pDM_Odm->Adapter;
+
+ if (pDM_DigTable->CurIGValue != CurrentIGI) {
+ phy_set_bb_reg(adapter, ODM_REG_IGI_A_11N, ODM_BIT_IGI_11N, CurrentIGI);
+ pDM_DigTable->CurIGValue = CurrentIGI;
+ }
+}
+
+void odm_DIGInit(struct odm_dm_struct *pDM_Odm)
+{
+ struct adapter *adapter = pDM_Odm->Adapter;
+ struct rtw_dig *pDM_DigTable = &pDM_Odm->DM_DigTable;
+
+ pDM_DigTable->CurIGValue = (u8)phy_query_bb_reg(adapter, ODM_REG_IGI_A_11N, ODM_BIT_IGI_11N);
+ pDM_DigTable->RssiLowThresh = DM_DIG_THRESH_LOW;
+ pDM_DigTable->RssiHighThresh = DM_DIG_THRESH_HIGH;
+ pDM_DigTable->FALowThresh = DM_false_ALARM_THRESH_LOW;
+ pDM_DigTable->FAHighThresh = DM_false_ALARM_THRESH_HIGH;
+ pDM_DigTable->rx_gain_range_max = DM_DIG_MAX_NIC;
+ pDM_DigTable->rx_gain_range_min = DM_DIG_MIN_NIC;
+ pDM_DigTable->BackoffVal = DM_DIG_BACKOFF_DEFAULT;
+ pDM_DigTable->BackoffVal_range_max = DM_DIG_BACKOFF_MAX;
+ pDM_DigTable->BackoffVal_range_min = DM_DIG_BACKOFF_MIN;
+ pDM_DigTable->PreCCK_CCAThres = 0xFF;
+ pDM_DigTable->CurCCK_CCAThres = 0x83;
+ pDM_DigTable->ForbiddenIGI = DM_DIG_MIN_NIC;
+ pDM_DigTable->LargeFAHit = 0;
+ pDM_DigTable->Recover_cnt = 0;
+ pDM_DigTable->DIG_Dynamic_MIN_0 = DM_DIG_MIN_NIC;
+ pDM_DigTable->DIG_Dynamic_MIN_1 = DM_DIG_MIN_NIC;
+ pDM_DigTable->bMediaConnect_0 = false;
+ pDM_DigTable->bMediaConnect_1 = false;
+
+ /* To Initialize pDM_Odm->bDMInitialGainEnable == false to avoid DIG error */
+ pDM_Odm->bDMInitialGainEnable = true;
+}
+
+void odm_DIG(struct odm_dm_struct *pDM_Odm)
+{
+ struct rtw_dig *pDM_DigTable = &pDM_Odm->DM_DigTable;
+ struct false_alarm_stats *pFalseAlmCnt = &pDM_Odm->FalseAlmCnt;
+ u8 DIG_Dynamic_MIN;
+ u8 DIG_MaxOfMin;
+ bool FirstConnect, FirstDisConnect;
+ u8 dm_dig_max, dm_dig_min;
+ u8 CurrentIGI = pDM_DigTable->CurIGValue;
+
+ ODM_RT_TRACE(pDM_Odm, ODM_COMP_DIG, ODM_DBG_LOUD, ("odm_DIG()==>\n"));
+ if ((!(pDM_Odm->SupportAbility&ODM_BB_DIG)) || (!(pDM_Odm->SupportAbility&ODM_BB_FA_CNT))) {
+ ODM_RT_TRACE(pDM_Odm, ODM_COMP_DIG, ODM_DBG_LOUD,
+ ("odm_DIG() Return: SupportAbility ODM_BB_DIG or ODM_BB_FA_CNT is disabled\n"));
+ return;
+ }
+
+ if (*(pDM_Odm->pbScanInProcess)) {
+ ODM_RT_TRACE(pDM_Odm, ODM_COMP_DIG, ODM_DBG_LOUD, ("odm_DIG() Return: In Scan Progress\n"));
+ return;
+ }
+
+ /* add by Neil Chen to avoid PSD is processing */
+ if (pDM_Odm->bDMInitialGainEnable == false) {
+ ODM_RT_TRACE(pDM_Odm, ODM_COMP_DIG, ODM_DBG_LOUD, ("odm_DIG() Return: PSD is Processing\n"));
+ return;
+ }
+
+ DIG_Dynamic_MIN = pDM_DigTable->DIG_Dynamic_MIN_0;
+ FirstConnect = (pDM_Odm->bLinked) && (!pDM_DigTable->bMediaConnect_0);
+ FirstDisConnect = (!pDM_Odm->bLinked) && (pDM_DigTable->bMediaConnect_0);
+
+ /* 1 Boundary Decision */
+ dm_dig_max = DM_DIG_MAX_NIC;
+ dm_dig_min = DM_DIG_MIN_NIC;
+ DIG_MaxOfMin = DM_DIG_MAX_AP;
+
+ if (pDM_Odm->bLinked) {
+ /* 2 Modify DIG upper bound */
+ if ((pDM_Odm->RSSI_Min + 20) > dm_dig_max)
+ pDM_DigTable->rx_gain_range_max = dm_dig_max;
+ else if ((pDM_Odm->RSSI_Min + 20) < dm_dig_min)
+ pDM_DigTable->rx_gain_range_max = dm_dig_min;
+ else
+ pDM_DigTable->rx_gain_range_max = pDM_Odm->RSSI_Min + 20;
+ /* 2 Modify DIG lower bound */
+ if (pDM_Odm->bOneEntryOnly) {
+ if (pDM_Odm->RSSI_Min < dm_dig_min)
+ DIG_Dynamic_MIN = dm_dig_min;
+ else if (pDM_Odm->RSSI_Min > DIG_MaxOfMin)
+ DIG_Dynamic_MIN = DIG_MaxOfMin;
+ else
+ DIG_Dynamic_MIN = pDM_Odm->RSSI_Min;
+ ODM_RT_TRACE(pDM_Odm, ODM_COMP_DIG, ODM_DBG_LOUD,
+ ("odm_DIG() : bOneEntryOnly=true, DIG_Dynamic_MIN=0x%x\n",
+ DIG_Dynamic_MIN));
+ ODM_RT_TRACE(pDM_Odm, ODM_COMP_DIG, ODM_DBG_LOUD,
+ ("odm_DIG() : pDM_Odm->RSSI_Min=%d\n",
+ pDM_Odm->RSSI_Min));
+ } else if (pDM_Odm->SupportAbility & ODM_BB_ANT_DIV) {
+ /* 1 Lower Bound for 88E AntDiv */
+ if (pDM_Odm->AntDivType == CG_TRX_HW_ANTDIV) {
+ DIG_Dynamic_MIN = (u8)pDM_DigTable->AntDiv_RSSI_max;
+ ODM_RT_TRACE(pDM_Odm, ODM_COMP_ANT_DIV, ODM_DBG_LOUD,
+ ("odm_DIG(): pDM_DigTable->AntDiv_RSSI_max=%d\n",
+ pDM_DigTable->AntDiv_RSSI_max));
+ }
+ } else {
+ DIG_Dynamic_MIN = dm_dig_min;
+ }
+ } else {
+ pDM_DigTable->rx_gain_range_max = dm_dig_max;
+ DIG_Dynamic_MIN = dm_dig_min;
+ ODM_RT_TRACE(pDM_Odm, ODM_COMP_DIG, ODM_DBG_LOUD, ("odm_DIG() : No Link\n"));
+ }
+
+ /* 1 Modify DIG lower bound, deal with abnormally large false alarm */
+ if (pFalseAlmCnt->Cnt_all > 10000) {
+ ODM_RT_TRACE(pDM_Odm, ODM_COMP_DIG, ODM_DBG_LOUD, ("dm_DIG(): Abnornally false alarm case.\n"));
+
+ if (pDM_DigTable->LargeFAHit != 3)
+ pDM_DigTable->LargeFAHit++;
+ if (pDM_DigTable->ForbiddenIGI < CurrentIGI) {
+ pDM_DigTable->ForbiddenIGI = CurrentIGI;
+ pDM_DigTable->LargeFAHit = 1;
+ }
+
+ if (pDM_DigTable->LargeFAHit >= 3) {
+ if ((pDM_DigTable->ForbiddenIGI+1) > pDM_DigTable->rx_gain_range_max)
+ pDM_DigTable->rx_gain_range_min = pDM_DigTable->rx_gain_range_max;
+ else
+ pDM_DigTable->rx_gain_range_min = (pDM_DigTable->ForbiddenIGI + 1);
+ pDM_DigTable->Recover_cnt = 3600; /* 3600=2hr */
+ }
+
+ } else {
+ /* Recovery mechanism for IGI lower bound */
+ if (pDM_DigTable->Recover_cnt != 0) {
+ pDM_DigTable->Recover_cnt--;
+ } else {
+ if (pDM_DigTable->LargeFAHit < 3) {
+ if ((pDM_DigTable->ForbiddenIGI-1) < DIG_Dynamic_MIN) { /* DM_DIG_MIN) */
+ pDM_DigTable->ForbiddenIGI = DIG_Dynamic_MIN; /* DM_DIG_MIN; */
+ pDM_DigTable->rx_gain_range_min = DIG_Dynamic_MIN; /* DM_DIG_MIN; */
+ ODM_RT_TRACE(pDM_Odm, ODM_COMP_DIG, ODM_DBG_LOUD, ("odm_DIG(): Normal Case: At Lower Bound\n"));
+ } else {
+ pDM_DigTable->ForbiddenIGI--;
+ pDM_DigTable->rx_gain_range_min = (pDM_DigTable->ForbiddenIGI + 1);
+ ODM_RT_TRACE(pDM_Odm, ODM_COMP_DIG, ODM_DBG_LOUD, ("odm_DIG(): Normal Case: Approach Lower Bound\n"));
+ }
+ } else {
+ pDM_DigTable->LargeFAHit = 0;
+ }
+ }
+ }
+ ODM_RT_TRACE(pDM_Odm, ODM_COMP_DIG, ODM_DBG_LOUD,
+ ("odm_DIG(): pDM_DigTable->LargeFAHit=%d\n",
+ pDM_DigTable->LargeFAHit));
+
+ /* 1 Adjust initial gain by false alarm */
+ if (pDM_Odm->bLinked) {
+ ODM_RT_TRACE(pDM_Odm, ODM_COMP_DIG, ODM_DBG_LOUD, ("odm_DIG(): DIG AfterLink\n"));
+ if (FirstConnect) {
+ CurrentIGI = pDM_Odm->RSSI_Min;
+ ODM_RT_TRACE(pDM_Odm, ODM_COMP_DIG, ODM_DBG_LOUD, ("DIG: First Connect\n"));
+ } else {
+ if (pFalseAlmCnt->Cnt_all > DM_DIG_FA_TH2)
+ CurrentIGI = CurrentIGI + 4;/* pDM_DigTable->CurIGValue = pDM_DigTable->PreIGValue+2; */
+ else if (pFalseAlmCnt->Cnt_all > DM_DIG_FA_TH1)
+ CurrentIGI = CurrentIGI + 2;/* pDM_DigTable->CurIGValue = pDM_DigTable->PreIGValue+1; */
+ else if (pFalseAlmCnt->Cnt_all < DM_DIG_FA_TH0)
+ CurrentIGI = CurrentIGI - 2;/* pDM_DigTable->CurIGValue =pDM_DigTable->PreIGValue-1; */
+ }
+ } else {
+ ODM_RT_TRACE(pDM_Odm, ODM_COMP_DIG, ODM_DBG_LOUD, ("odm_DIG(): DIG BeforeLink\n"));
+ if (FirstDisConnect) {
+ CurrentIGI = pDM_DigTable->rx_gain_range_min;
+ ODM_RT_TRACE(pDM_Odm, ODM_COMP_DIG, ODM_DBG_LOUD, ("odm_DIG(): First DisConnect\n"));
+ } else {
+ /* 2012.03.30 LukeLee: enable DIG before link but with very high thresholds */
+ if (pFalseAlmCnt->Cnt_all > 10000)
+ CurrentIGI = CurrentIGI + 2;/* pDM_DigTable->CurIGValue = pDM_DigTable->PreIGValue+2; */
+ else if (pFalseAlmCnt->Cnt_all > 8000)
+ CurrentIGI = CurrentIGI + 1;/* pDM_DigTable->CurIGValue = pDM_DigTable->PreIGValue+1; */
+ else if (pFalseAlmCnt->Cnt_all < 500)
+ CurrentIGI = CurrentIGI - 1;/* pDM_DigTable->CurIGValue =pDM_DigTable->PreIGValue-1; */
+ ODM_RT_TRACE(pDM_Odm, ODM_COMP_DIG, ODM_DBG_LOUD, ("odm_DIG(): England DIG\n"));
+ }
+ }
+ ODM_RT_TRACE(pDM_Odm, ODM_COMP_DIG, ODM_DBG_LOUD, ("odm_DIG(): DIG End Adjust IGI\n"));
+ /* 1 Check initial gain by upper/lower bound */
+ if (CurrentIGI > pDM_DigTable->rx_gain_range_max)
+ CurrentIGI = pDM_DigTable->rx_gain_range_max;
+ if (CurrentIGI < pDM_DigTable->rx_gain_range_min)
+ CurrentIGI = pDM_DigTable->rx_gain_range_min;
+
+ ODM_RT_TRACE(pDM_Odm, ODM_COMP_DIG, ODM_DBG_LOUD,
+ ("odm_DIG(): rx_gain_range_max=0x%x, rx_gain_range_min=0x%x\n",
+ pDM_DigTable->rx_gain_range_max, pDM_DigTable->rx_gain_range_min));
+ ODM_RT_TRACE(pDM_Odm, ODM_COMP_DIG, ODM_DBG_LOUD, ("odm_DIG(): TotalFA=%d\n", pFalseAlmCnt->Cnt_all));
+ ODM_RT_TRACE(pDM_Odm, ODM_COMP_DIG, ODM_DBG_LOUD, ("odm_DIG(): CurIGValue=0x%x\n", CurrentIGI));
+
+ /* 2 High power RSSI threshold */
+
+ ODM_Write_DIG(pDM_Odm, CurrentIGI);/* ODM_Write_DIG(pDM_Odm, pDM_DigTable->CurIGValue); */
+ pDM_DigTable->bMediaConnect_0 = pDM_Odm->bLinked;
+ pDM_DigTable->DIG_Dynamic_MIN_0 = DIG_Dynamic_MIN;
+}
+
+/* 3============================================================ */
+/* 3 FASLE ALARM CHECK */
+/* 3============================================================ */
+
+void odm_FalseAlarmCounterStatistics(struct odm_dm_struct *pDM_Odm)
+{
+ struct adapter *adapter = pDM_Odm->Adapter;
+ u32 ret_value;
+ struct false_alarm_stats *FalseAlmCnt = &(pDM_Odm->FalseAlmCnt);
+
+ if (!(pDM_Odm->SupportAbility & ODM_BB_FA_CNT))
+ return;
+
+ /* hold ofdm counter */
+ phy_set_bb_reg(adapter, ODM_REG_OFDM_FA_HOLDC_11N, BIT31, 1); /* hold page C counter */
+ phy_set_bb_reg(adapter, ODM_REG_OFDM_FA_RSTD_11N, BIT31, 1); /* hold page D counter */
+
+ ret_value = phy_query_bb_reg(adapter, ODM_REG_OFDM_FA_TYPE1_11N, bMaskDWord);
+ FalseAlmCnt->Cnt_Fast_Fsync = (ret_value&0xffff);
+ FalseAlmCnt->Cnt_SB_Search_fail = (ret_value & 0xffff0000)>>16;
+ ret_value = phy_query_bb_reg(adapter, ODM_REG_OFDM_FA_TYPE2_11N, bMaskDWord);
+ FalseAlmCnt->Cnt_OFDM_CCA = (ret_value&0xffff);
+ FalseAlmCnt->Cnt_Parity_Fail = (ret_value & 0xffff0000)>>16;
+ ret_value = phy_query_bb_reg(adapter, ODM_REG_OFDM_FA_TYPE3_11N, bMaskDWord);
+ FalseAlmCnt->Cnt_Rate_Illegal = (ret_value&0xffff);
+ FalseAlmCnt->Cnt_Crc8_fail = (ret_value & 0xffff0000)>>16;
+ ret_value = phy_query_bb_reg(adapter, ODM_REG_OFDM_FA_TYPE4_11N, bMaskDWord);
+ FalseAlmCnt->Cnt_Mcs_fail = (ret_value&0xffff);
+
+ FalseAlmCnt->Cnt_Ofdm_fail = FalseAlmCnt->Cnt_Parity_Fail + FalseAlmCnt->Cnt_Rate_Illegal +
+ FalseAlmCnt->Cnt_Crc8_fail + FalseAlmCnt->Cnt_Mcs_fail +
+ FalseAlmCnt->Cnt_Fast_Fsync + FalseAlmCnt->Cnt_SB_Search_fail;
+
+ ret_value = phy_query_bb_reg(adapter, ODM_REG_SC_CNT_11N, bMaskDWord);
+ FalseAlmCnt->Cnt_BW_LSC = (ret_value&0xffff);
+ FalseAlmCnt->Cnt_BW_USC = (ret_value & 0xffff0000)>>16;
+
+ /* hold cck counter */
+ phy_set_bb_reg(adapter, ODM_REG_CCK_FA_RST_11N, BIT12, 1);
+ phy_set_bb_reg(adapter, ODM_REG_CCK_FA_RST_11N, BIT14, 1);
+
+ ret_value = phy_query_bb_reg(adapter, ODM_REG_CCK_FA_LSB_11N, bMaskByte0);
+ FalseAlmCnt->Cnt_Cck_fail = ret_value;
+ ret_value = phy_query_bb_reg(adapter, ODM_REG_CCK_FA_MSB_11N, bMaskByte3);
+ FalseAlmCnt->Cnt_Cck_fail += (ret_value & 0xff)<<8;
+
+ ret_value = phy_query_bb_reg(adapter, ODM_REG_CCK_CCA_CNT_11N, bMaskDWord);
+ FalseAlmCnt->Cnt_CCK_CCA = ((ret_value&0xFF)<<8) | ((ret_value&0xFF00)>>8);
+
+ FalseAlmCnt->Cnt_all = (FalseAlmCnt->Cnt_Fast_Fsync +
+ FalseAlmCnt->Cnt_SB_Search_fail +
+ FalseAlmCnt->Cnt_Parity_Fail +
+ FalseAlmCnt->Cnt_Rate_Illegal +
+ FalseAlmCnt->Cnt_Crc8_fail +
+ FalseAlmCnt->Cnt_Mcs_fail +
+ FalseAlmCnt->Cnt_Cck_fail);
+
+ FalseAlmCnt->Cnt_CCA_all = FalseAlmCnt->Cnt_OFDM_CCA + FalseAlmCnt->Cnt_CCK_CCA;
+
+ ODM_RT_TRACE(pDM_Odm, ODM_COMP_FA_CNT, ODM_DBG_LOUD, ("Enter odm_FalseAlarmCounterStatistics\n"));
+ ODM_RT_TRACE(pDM_Odm, ODM_COMP_FA_CNT, ODM_DBG_LOUD,
+ ("Cnt_Fast_Fsync=%d, Cnt_SB_Search_fail=%d\n",
+ FalseAlmCnt->Cnt_Fast_Fsync, FalseAlmCnt->Cnt_SB_Search_fail));
+ ODM_RT_TRACE(pDM_Odm, ODM_COMP_FA_CNT, ODM_DBG_LOUD,
+ ("Cnt_Parity_Fail=%d, Cnt_Rate_Illegal=%d\n",
+ FalseAlmCnt->Cnt_Parity_Fail, FalseAlmCnt->Cnt_Rate_Illegal));
+ ODM_RT_TRACE(pDM_Odm, ODM_COMP_FA_CNT, ODM_DBG_LOUD,
+ ("Cnt_Crc8_fail=%d, Cnt_Mcs_fail=%d\n",
+ FalseAlmCnt->Cnt_Crc8_fail, FalseAlmCnt->Cnt_Mcs_fail));
+ ODM_RT_TRACE(pDM_Odm, ODM_COMP_FA_CNT, ODM_DBG_LOUD, ("Cnt_Cck_fail=%d\n", FalseAlmCnt->Cnt_Cck_fail));
+ ODM_RT_TRACE(pDM_Odm, ODM_COMP_FA_CNT, ODM_DBG_LOUD, ("Cnt_Ofdm_fail=%d\n", FalseAlmCnt->Cnt_Ofdm_fail));
+ ODM_RT_TRACE(pDM_Odm, ODM_COMP_FA_CNT, ODM_DBG_LOUD, ("Total False Alarm=%d\n", FalseAlmCnt->Cnt_all));
+}
+
+/* 3============================================================ */
+/* 3 CCK Packet Detect Threshold */
+/* 3============================================================ */
+
+void odm_CCKPacketDetectionThresh(struct odm_dm_struct *pDM_Odm)
+{
+ u8 CurCCK_CCAThres;
+ struct false_alarm_stats *FalseAlmCnt = &(pDM_Odm->FalseAlmCnt);
+
+ if (!(pDM_Odm->SupportAbility & (ODM_BB_CCK_PD|ODM_BB_FA_CNT)))
+ return;
+ if (pDM_Odm->ExtLNA)
+ return;
+ if (pDM_Odm->bLinked) {
+ if (pDM_Odm->RSSI_Min > 25) {
+ CurCCK_CCAThres = 0xcd;
+ } else if ((pDM_Odm->RSSI_Min <= 25) && (pDM_Odm->RSSI_Min > 10)) {
+ CurCCK_CCAThres = 0x83;
+ } else {
+ if (FalseAlmCnt->Cnt_Cck_fail > 1000)
+ CurCCK_CCAThres = 0x83;
+ else
+ CurCCK_CCAThres = 0x40;
+ }
+ } else {
+ if (FalseAlmCnt->Cnt_Cck_fail > 1000)
+ CurCCK_CCAThres = 0x83;
+ else
+ CurCCK_CCAThres = 0x40;
+ }
+ ODM_Write_CCK_CCA_Thres(pDM_Odm, CurCCK_CCAThres);
+}
+
+void ODM_Write_CCK_CCA_Thres(struct odm_dm_struct *pDM_Odm, u8 CurCCK_CCAThres)
+{
+ struct rtw_dig *pDM_DigTable = &pDM_Odm->DM_DigTable;
+ struct adapter *adapt = pDM_Odm->Adapter;
+
+ if (pDM_DigTable->CurCCK_CCAThres != CurCCK_CCAThres) /* modify by Guo.Mingzhi 2012-01-03 */
+ usb_write8(adapt, ODM_REG_CCK_CCA_11N, CurCCK_CCAThres);
+ pDM_DigTable->PreCCK_CCAThres = pDM_DigTable->CurCCK_CCAThres;
+ pDM_DigTable->CurCCK_CCAThres = CurCCK_CCAThres;
+}
+
+void ODM_RF_Saving(struct odm_dm_struct *pDM_Odm, u8 bForceInNormal)
+{
+ struct adapter *adapter = pDM_Odm->Adapter;
+ struct rtl_ps *pDM_PSTable = &pDM_Odm->DM_PSTable;
+ u8 Rssi_Up_bound = 30;
+ u8 Rssi_Low_bound = 25;
+
+ if (pDM_Odm->PatchID == 40) { /* RT_CID_819x_FUNAI_TV */
+ Rssi_Up_bound = 50;
+ Rssi_Low_bound = 45;
+ }
+ if (pDM_PSTable->initialize == 0) {
+ pDM_PSTable->Reg874 = (phy_query_bb_reg(adapter, 0x874, bMaskDWord)&0x1CC000)>>14;
+ pDM_PSTable->RegC70 = (phy_query_bb_reg(adapter, 0xc70, bMaskDWord)&BIT3)>>3;
+ pDM_PSTable->Reg85C = (phy_query_bb_reg(adapter, 0x85c, bMaskDWord)&0xFF000000)>>24;
+ pDM_PSTable->RegA74 = (phy_query_bb_reg(adapter, 0xa74, bMaskDWord)&0xF000)>>12;
+ pDM_PSTable->initialize = 1;
+ }
+
+ if (!bForceInNormal) {
+ if (pDM_Odm->RSSI_Min != 0xFF) {
+ if (pDM_PSTable->PreRFState == RF_Normal) {
+ if (pDM_Odm->RSSI_Min >= Rssi_Up_bound)
+ pDM_PSTable->CurRFState = RF_Save;
+ else
+ pDM_PSTable->CurRFState = RF_Normal;
+ } else {
+ if (pDM_Odm->RSSI_Min <= Rssi_Low_bound)
+ pDM_PSTable->CurRFState = RF_Normal;
+ else
+ pDM_PSTable->CurRFState = RF_Save;
+ }
+ } else {
+ pDM_PSTable->CurRFState = RF_MAX;
+ }
+ } else {
+ pDM_PSTable->CurRFState = RF_Normal;
+ }
+
+ if (pDM_PSTable->PreRFState != pDM_PSTable->CurRFState) {
+ if (pDM_PSTable->CurRFState == RF_Save) {
+ phy_set_bb_reg(adapter, 0x874 , 0x1C0000, 0x2); /* Reg874[20:18]=3'b010 */
+ phy_set_bb_reg(adapter, 0xc70, BIT3, 0); /* RegC70[3]=1'b0 */
+ phy_set_bb_reg(adapter, 0x85c, 0xFF000000, 0x63); /* Reg85C[31:24]=0x63 */
+ phy_set_bb_reg(adapter, 0x874, 0xC000, 0x2); /* Reg874[15:14]=2'b10 */
+ phy_set_bb_reg(adapter, 0xa74, 0xF000, 0x3); /* RegA75[7:4]=0x3 */
+ phy_set_bb_reg(adapter, 0x818, BIT28, 0x0); /* Reg818[28]=1'b0 */
+ phy_set_bb_reg(adapter, 0x818, BIT28, 0x1); /* Reg818[28]=1'b1 */
+ } else {
+ phy_set_bb_reg(adapter, 0x874 , 0x1CC000, pDM_PSTable->Reg874);
+ phy_set_bb_reg(adapter, 0xc70, BIT3, pDM_PSTable->RegC70);
+ phy_set_bb_reg(adapter, 0x85c, 0xFF000000, pDM_PSTable->Reg85C);
+ phy_set_bb_reg(adapter, 0xa74, 0xF000, pDM_PSTable->RegA74);
+ phy_set_bb_reg(adapter, 0x818, BIT28, 0x0);
+ }
+ pDM_PSTable->PreRFState = pDM_PSTable->CurRFState;
+ }
+}
+
+/* 3============================================================ */
+/* 3 RATR MASK */
+/* 3============================================================ */
+/* 3============================================================ */
+/* 3 Rate Adaptive */
+/* 3============================================================ */
+
+void odm_RateAdaptiveMaskInit(struct odm_dm_struct *pDM_Odm)
+{
+ struct odm_rate_adapt *pOdmRA = &pDM_Odm->RateAdaptive;
+
+ pOdmRA->Type = DM_Type_ByDriver;
+ if (pOdmRA->Type == DM_Type_ByDriver)
+ pDM_Odm->bUseRAMask = true;
+ else
+ pDM_Odm->bUseRAMask = false;
+
+ pOdmRA->RATRState = DM_RATR_STA_INIT;
+ pOdmRA->HighRSSIThresh = 50;
+ pOdmRA->LowRSSIThresh = 20;
+}
+
+u32 ODM_Get_Rate_Bitmap(struct odm_dm_struct *pDM_Odm, u32 macid, u32 ra_mask, u8 rssi_level)
+{
+ struct sta_info *pEntry;
+ u32 rate_bitmap = 0x0fffffff;
+ u8 WirelessMode;
+
+ pEntry = pDM_Odm->pODM_StaInfo[macid];
+ if (!IS_STA_VALID(pEntry))
+ return ra_mask;
+
+ WirelessMode = pEntry->wireless_mode;
+
+ switch (WirelessMode) {
+ case ODM_WM_B:
+ if (ra_mask & 0x0000000c) /* 11M or 5.5M enable */
+ rate_bitmap = 0x0000000d;
+ else
+ rate_bitmap = 0x0000000f;
+ break;
+ case (ODM_WM_A|ODM_WM_G):
+ if (rssi_level == DM_RATR_STA_HIGH)
+ rate_bitmap = 0x00000f00;
+ else
+ rate_bitmap = 0x00000ff0;
+ break;
+ case (ODM_WM_B|ODM_WM_G):
+ if (rssi_level == DM_RATR_STA_HIGH)
+ rate_bitmap = 0x00000f00;
+ else if (rssi_level == DM_RATR_STA_MIDDLE)
+ rate_bitmap = 0x00000ff0;
+ else
+ rate_bitmap = 0x00000ff5;
+ break;
+ case (ODM_WM_B|ODM_WM_G|ODM_WM_N24G):
+ case (ODM_WM_A|ODM_WM_B|ODM_WM_G|ODM_WM_N24G):
+ if (pDM_Odm->RFType == ODM_1T2R || pDM_Odm->RFType == ODM_1T1R) {
+ if (rssi_level == DM_RATR_STA_HIGH) {
+ rate_bitmap = 0x000f0000;
+ } else if (rssi_level == DM_RATR_STA_MIDDLE) {
+ rate_bitmap = 0x000ff000;
+ } else {
+ if (*(pDM_Odm->pBandWidth) == ODM_BW40M)
+ rate_bitmap = 0x000ff015;
+ else
+ rate_bitmap = 0x000ff005;
+ }
+ } else {
+ if (rssi_level == DM_RATR_STA_HIGH) {
+ rate_bitmap = 0x0f8f0000;
+ } else if (rssi_level == DM_RATR_STA_MIDDLE) {
+ rate_bitmap = 0x0f8ff000;
+ } else {
+ if (*(pDM_Odm->pBandWidth) == ODM_BW40M)
+ rate_bitmap = 0x0f8ff015;
+ else
+ rate_bitmap = 0x0f8ff005;
+ }
+ }
+ break;
+ default:
+ /* case WIRELESS_11_24N: */
+ /* case WIRELESS_11_5N: */
+ if (pDM_Odm->RFType == RF_1T2R)
+ rate_bitmap = 0x000fffff;
+ else
+ rate_bitmap = 0x0fffffff;
+ break;
+ }
+
+ ODM_RT_TRACE(pDM_Odm, ODM_COMP_RA_MASK, ODM_DBG_LOUD,
+ (" ==> rssi_level:0x%02x, WirelessMode:0x%02x, rate_bitmap:0x%08x\n",
+ rssi_level, WirelessMode, rate_bitmap));
+
+ return rate_bitmap;
+}
+
+/*-----------------------------------------------------------------------------
+ * Function: odm_RefreshRateAdaptiveMask()
+ *
+ * Overview: Update rate table mask according to rssi
+ *
+ * Input: NONE
+ *
+ * Output: NONE
+ *
+ * Return: NONE
+ *
+ * Revised History:
+ * When Who Remark
+ * 05/27/2009 hpfan Create Version 0.
+ *
+ *---------------------------------------------------------------------------*/
+void odm_RefreshRateAdaptiveMask(struct odm_dm_struct *pDM_Odm)
+{
+ if (!(pDM_Odm->SupportAbility & ODM_BB_RA_MASK))
+ return;
+ /* */
+ /* 2011/09/29 MH In HW integration first stage, we provide 4 different handle to operate */
+ /* at the same time. In the stage2/3, we need to prive universal interface and merge all */
+ /* HW dynamic mechanism. */
+ /* */
+ odm_RefreshRateAdaptiveMaskCE(pDM_Odm);
+}
+
+void odm_RefreshRateAdaptiveMaskCE(struct odm_dm_struct *pDM_Odm)
+{
+ u8 i;
+ struct adapter *pAdapter = pDM_Odm->Adapter;
+
+ if (pAdapter->bDriverStopped) {
+ ODM_RT_TRACE(pDM_Odm, ODM_COMP_RA_MASK, ODM_DBG_TRACE, ("<---- odm_RefreshRateAdaptiveMask(): driver is going to unload\n"));
+ return;
+ }
+
+ if (!pDM_Odm->bUseRAMask) {
+ ODM_RT_TRACE(pDM_Odm, ODM_COMP_RA_MASK, ODM_DBG_LOUD, ("<---- odm_RefreshRateAdaptiveMask(): driver does not control rate adaptive mask\n"));
+ return;
+ }
+
+ for (i = 0; i < ODM_ASSOCIATE_ENTRY_NUM; i++) {
+ struct sta_info *pstat = pDM_Odm->pODM_StaInfo[i];
+ if (IS_STA_VALID(pstat)) {
+ if (ODM_RAStateCheck(pDM_Odm, pstat->rssi_stat.UndecoratedSmoothedPWDB, false , &pstat->rssi_level)) {
+ ODM_RT_TRACE(pDM_Odm, ODM_COMP_RA_MASK, ODM_DBG_LOUD,
+ ("RSSI:%d, RSSI_LEVEL:%d\n",
+ pstat->rssi_stat.UndecoratedSmoothedPWDB, pstat->rssi_level));
+ rtw_hal_update_ra_mask(pAdapter, i, pstat->rssi_level);
+ }
+ }
+ }
+}
+
+/* Return Value: bool */
+/* - true: RATRState is changed. */
+bool ODM_RAStateCheck(struct odm_dm_struct *pDM_Odm, s32 RSSI, bool bForceUpdate, u8 *pRATRState)
+{
+ struct odm_rate_adapt *pRA = &pDM_Odm->RateAdaptive;
+ const u8 GoUpGap = 5;
+ u8 HighRSSIThreshForRA = pRA->HighRSSIThresh;
+ u8 LowRSSIThreshForRA = pRA->LowRSSIThresh;
+ u8 RATRState;
+
+ /* Threshold Adjustment: */
+ /* when RSSI state trends to go up one or two levels, make sure RSSI is high enough. */
+ /* Here GoUpGap is added to solve the boundary's level alternation issue. */
+ switch (*pRATRState) {
+ case DM_RATR_STA_INIT:
+ case DM_RATR_STA_HIGH:
+ break;
+ case DM_RATR_STA_MIDDLE:
+ HighRSSIThreshForRA += GoUpGap;
+ break;
+ case DM_RATR_STA_LOW:
+ HighRSSIThreshForRA += GoUpGap;
+ LowRSSIThreshForRA += GoUpGap;
+ break;
+ default:
+ ODM_RT_ASSERT(pDM_Odm, false, ("wrong rssi level setting %d !", *pRATRState));
+ break;
+ }
+
+ /* Decide RATRState by RSSI. */
+ if (RSSI > HighRSSIThreshForRA)
+ RATRState = DM_RATR_STA_HIGH;
+ else if (RSSI > LowRSSIThreshForRA)
+ RATRState = DM_RATR_STA_MIDDLE;
+ else
+ RATRState = DM_RATR_STA_LOW;
+
+ if (*pRATRState != RATRState || bForceUpdate) {
+ ODM_RT_TRACE(pDM_Odm, ODM_COMP_RA_MASK, ODM_DBG_LOUD, ("RSSI Level %d -> %d\n", *pRATRState, RATRState));
+ *pRATRState = RATRState;
+ return true;
+ }
+ return false;
+}
+
+/* 3============================================================ */
+/* 3 Dynamic Tx Power */
+/* 3============================================================ */
+
+void odm_DynamicTxPowerInit(struct odm_dm_struct *pDM_Odm)
+{
+ struct adapter *Adapter = pDM_Odm->Adapter;
+ struct hal_data_8188e *pHalData = GET_HAL_DATA(Adapter);
+ struct dm_priv *pdmpriv = &pHalData->dmpriv;
+ pdmpriv->bDynamicTxPowerEnable = false;
+ pdmpriv->LastDTPLvl = TxHighPwrLevel_Normal;
+ pdmpriv->DynamicTxHighPowerLvl = TxHighPwrLevel_Normal;
+}
+
+/* 3============================================================ */
+/* 3 RSSI Monitor */
+/* 3============================================================ */
+
+void odm_RSSIMonitorCheck(struct odm_dm_struct *pDM_Odm)
+{
+ if (!(pDM_Odm->SupportAbility & ODM_BB_RSSI_MONITOR))
+ return;
+
+ /* */
+ /* 2011/09/29 MH In HW integration first stage, we provide 4 different handle to operate */
+ /* at the same time. In the stage2/3, we need to prive universal interface and merge all */
+ /* HW dynamic mechanism. */
+ /* */
+ odm_RSSIMonitorCheckCE(pDM_Odm);
+} /* odm_RSSIMonitorCheck */
+
+static void FindMinimumRSSI(struct adapter *pAdapter)
+{
+ struct hal_data_8188e *pHalData = GET_HAL_DATA(pAdapter);
+ struct dm_priv *pdmpriv = &pHalData->dmpriv;
+
+ /* 1 1.Unconditionally set RSSI */
+ pdmpriv->MinUndecoratedPWDBForDM = pdmpriv->EntryMinUndecoratedSmoothedPWDB;
+}
+
+void odm_RSSIMonitorCheckCE(struct odm_dm_struct *pDM_Odm)
+{
+ struct adapter *Adapter = pDM_Odm->Adapter;
+ struct hal_data_8188e *pHalData = GET_HAL_DATA(Adapter);
+ struct dm_priv *pdmpriv = &pHalData->dmpriv;
+ int i;
+ int tmpEntryMaxPWDB = 0, tmpEntryMinPWDB = 0xff;
+ u8 sta_cnt = 0;
+ u32 PWDB_rssi[NUM_STA] = {0};/* 0~15]:MACID, [16~31]:PWDB_rssi */
+ struct sta_info *psta;
+ u8 bcast_addr[ETH_ALEN] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
+
+ if (!check_fwstate(&Adapter->mlmepriv, _FW_LINKED))
+ return;
+
+ for (i = 0; i < ODM_ASSOCIATE_ENTRY_NUM; i++) {
+ psta = pDM_Odm->pODM_StaInfo[i];
+ if (IS_STA_VALID(psta) &&
+ (psta->state & WIFI_ASOC_STATE) &&
+ memcmp(psta->hwaddr, bcast_addr, ETH_ALEN) &&
+ memcmp(psta->hwaddr, myid(&Adapter->eeprompriv), ETH_ALEN)) {
+ if (psta->rssi_stat.UndecoratedSmoothedPWDB < tmpEntryMinPWDB)
+ tmpEntryMinPWDB = psta->rssi_stat.UndecoratedSmoothedPWDB;
+
+ if (psta->rssi_stat.UndecoratedSmoothedPWDB > tmpEntryMaxPWDB)
+ tmpEntryMaxPWDB = psta->rssi_stat.UndecoratedSmoothedPWDB;
+ if (psta->rssi_stat.UndecoratedSmoothedPWDB != (-1))
+ PWDB_rssi[sta_cnt++] = (psta->mac_id | (psta->rssi_stat.UndecoratedSmoothedPWDB<<16));
+ }
+ }
+
+ for (i = 0; i < sta_cnt; i++) {
+ if (PWDB_rssi[i] != (0)) {
+ if (pHalData->fw_ractrl) {
+ /* Report every sta's RSSI to FW */
+ } else {
+ ODM_RA_SetRSSI_8188E(
+ &(pHalData->odmpriv), (PWDB_rssi[i]&0xFF), (u8)((PWDB_rssi[i]>>16) & 0xFF));
+ }
+ }
+ }
+
+ if (tmpEntryMaxPWDB != 0) /* If associated entry is found */
+ pdmpriv->EntryMaxUndecoratedSmoothedPWDB = tmpEntryMaxPWDB;
+ else
+ pdmpriv->EntryMaxUndecoratedSmoothedPWDB = 0;
+
+ if (tmpEntryMinPWDB != 0xff) /* If associated entry is found */
+ pdmpriv->EntryMinUndecoratedSmoothedPWDB = tmpEntryMinPWDB;
+ else
+ pdmpriv->EntryMinUndecoratedSmoothedPWDB = 0;
+
+ FindMinimumRSSI(Adapter);
+ ODM_CmnInfoUpdate(&pHalData->odmpriv , ODM_CMNINFO_RSSI_MIN, pdmpriv->MinUndecoratedPWDBForDM);
+}
+
+/* 3============================================================ */
+/* 3 Tx Power Tracking */
+/* 3============================================================ */
+
+void odm_TXPowerTrackingInit(struct odm_dm_struct *pDM_Odm)
+{
+ odm_TXPowerTrackingThermalMeterInit(pDM_Odm);
+}
+
+void odm_TXPowerTrackingThermalMeterInit(struct odm_dm_struct *pDM_Odm)
+{
+ pDM_Odm->RFCalibrateInfo.bTXPowerTracking = true;
+ pDM_Odm->RFCalibrateInfo.TXPowercount = 0;
+ pDM_Odm->RFCalibrateInfo.bTXPowerTrackingInit = false;
+ if (*(pDM_Odm->mp_mode) != 1)
+ pDM_Odm->RFCalibrateInfo.TxPowerTrackControl = true;
+ MSG_88E("pDM_Odm TxPowerTrackControl = %d\n", pDM_Odm->RFCalibrateInfo.TxPowerTrackControl);
+
+ pDM_Odm->RFCalibrateInfo.TxPowerTrackControl = true;
+}
+
+void ODM_TXPowerTrackingCheck(struct odm_dm_struct *pDM_Odm)
+{
+ /* 2011/09/29 MH In HW integration first stage, we provide 4 different handle to operate */
+ /* at the same time. In the stage2/3, we need to prive universal interface and merge all */
+ /* HW dynamic mechanism. */
+ odm_TXPowerTrackingCheckCE(pDM_Odm);
+}
+
+void odm_TXPowerTrackingCheckCE(struct odm_dm_struct *pDM_Odm)
+{
+ struct adapter *Adapter = pDM_Odm->Adapter;
+
+ if (!(pDM_Odm->SupportAbility & ODM_RF_TX_PWR_TRACK))
+ return;
+
+ if (!pDM_Odm->RFCalibrateInfo.TM_Trigger) { /* at least delay 1 sec */
+ phy_set_rf_reg(Adapter, RF_PATH_A, RF_T_METER_88E, BIT17 | BIT16, 0x03);
+
+ pDM_Odm->RFCalibrateInfo.TM_Trigger = 1;
+ return;
+ } else {
+ rtl88eu_dm_txpower_tracking_callback_thermalmeter(Adapter);
+ pDM_Odm->RFCalibrateInfo.TM_Trigger = 0;
+ }
+}
+
+/* 3============================================================ */
+/* 3 SW Antenna Diversity */
+/* 3============================================================ */
+
+void odm_InitHybridAntDiv(struct odm_dm_struct *pDM_Odm)
+{
+ if (!(pDM_Odm->SupportAbility & ODM_BB_ANT_DIV)) {
+ ODM_RT_TRACE(pDM_Odm, ODM_COMP_ANT_DIV, ODM_DBG_LOUD, ("Return: Not Support HW AntDiv\n"));
+ return;
+ }
+
+ rtl88eu_dm_antenna_div_init(pDM_Odm);
+}
+
+void odm_HwAntDiv(struct odm_dm_struct *pDM_Odm)
+{
+ if (!(pDM_Odm->SupportAbility & ODM_BB_ANT_DIV)) {
+ ODM_RT_TRACE(pDM_Odm, ODM_COMP_ANT_DIV, ODM_DBG_LOUD, ("Return: Not Support HW AntDiv\n"));
+ return;
+ }
+
+ rtl88eu_dm_antenna_diversity(pDM_Odm);
+}
+
+/* EDCA Turbo */
+void ODM_EdcaTurboInit(struct odm_dm_struct *pDM_Odm)
+{
+ struct adapter *Adapter = pDM_Odm->Adapter;
+ pDM_Odm->DM_EDCA_Table.bCurrentTurboEDCA = false;
+ pDM_Odm->DM_EDCA_Table.bIsCurRDLState = false;
+ Adapter->recvpriv.bIsAnyNonBEPkts = false;
+
+ ODM_RT_TRACE(pDM_Odm, ODM_COMP_EDCA_TURBO, ODM_DBG_LOUD, ("Orginial VO PARAM: 0x%x\n", usb_read32(Adapter, ODM_EDCA_VO_PARAM)));
+ ODM_RT_TRACE(pDM_Odm, ODM_COMP_EDCA_TURBO, ODM_DBG_LOUD, ("Orginial VI PARAM: 0x%x\n", usb_read32(Adapter, ODM_EDCA_VI_PARAM)));
+ ODM_RT_TRACE(pDM_Odm, ODM_COMP_EDCA_TURBO, ODM_DBG_LOUD, ("Orginial BE PARAM: 0x%x\n", usb_read32(Adapter, ODM_EDCA_BE_PARAM)));
+ ODM_RT_TRACE(pDM_Odm, ODM_COMP_EDCA_TURBO, ODM_DBG_LOUD, ("Orginial BK PARAM: 0x%x\n", usb_read32(Adapter, ODM_EDCA_BK_PARAM)));
+} /* ODM_InitEdcaTurbo */
+
+void odm_EdcaTurboCheck(struct odm_dm_struct *pDM_Odm)
+{
+ /* 2011/09/29 MH In HW integration first stage, we provide 4 different handle to operate */
+ /* at the same time. In the stage2/3, we need to prive universal interface and merge all */
+ /* HW dynamic mechanism. */
+ ODM_RT_TRACE(pDM_Odm, ODM_COMP_EDCA_TURBO, ODM_DBG_LOUD, ("odm_EdcaTurboCheck========================>\n"));
+
+ if (!(pDM_Odm->SupportAbility & ODM_MAC_EDCA_TURBO))
+ return;
+
+ odm_EdcaTurboCheckCE(pDM_Odm);
+ ODM_RT_TRACE(pDM_Odm, ODM_COMP_EDCA_TURBO, ODM_DBG_LOUD, ("<========================odm_EdcaTurboCheck\n"));
+} /* odm_CheckEdcaTurbo */
+
+void odm_EdcaTurboCheckCE(struct odm_dm_struct *pDM_Odm)
+{
+ struct adapter *Adapter = pDM_Odm->Adapter;
+ u32 trafficIndex;
+ u32 edca_param;
+ u64 cur_tx_bytes = 0;
+ u64 cur_rx_bytes = 0;
+ u8 bbtchange = false;
+ struct hal_data_8188e *pHalData = GET_HAL_DATA(Adapter);
+ struct xmit_priv *pxmitpriv = &(Adapter->xmitpriv);
+ struct recv_priv *precvpriv = &(Adapter->recvpriv);
+ struct registry_priv *pregpriv = &Adapter->registrypriv;
+ struct mlme_ext_priv *pmlmeext = &(Adapter->mlmeextpriv);
+ struct mlme_ext_info *pmlmeinfo = &(pmlmeext->mlmext_info);
+
+ if ((pregpriv->wifi_spec == 1))/* (pmlmeinfo->HT_enable == 0)) */
+ goto dm_CheckEdcaTurbo_EXIT;
+
+ if (pmlmeinfo->assoc_AP_vendor >= HT_IOT_PEER_MAX)
+ goto dm_CheckEdcaTurbo_EXIT;
+
+ /* Check if the status needs to be changed. */
+ if ((bbtchange) || (!precvpriv->bIsAnyNonBEPkts)) {
+ cur_tx_bytes = pxmitpriv->tx_bytes - pxmitpriv->last_tx_bytes;
+ cur_rx_bytes = precvpriv->rx_bytes - precvpriv->last_rx_bytes;
+
+ /* traffic, TX or RX */
+ if ((pmlmeinfo->assoc_AP_vendor == HT_IOT_PEER_RALINK) ||
+ (pmlmeinfo->assoc_AP_vendor == HT_IOT_PEER_ATHEROS)) {
+ if (cur_tx_bytes > (cur_rx_bytes << 2)) {
+ /* Uplink TP is present. */
+ trafficIndex = UP_LINK;
+ } else {
+ /* Balance TP is present. */
+ trafficIndex = DOWN_LINK;
+ }
+ } else {
+ if (cur_rx_bytes > (cur_tx_bytes << 2)) {
+ /* Downlink TP is present. */
+ trafficIndex = DOWN_LINK;
+ } else {
+ /* Balance TP is present. */
+ trafficIndex = UP_LINK;
+ }
+ }
+
+ if ((pDM_Odm->DM_EDCA_Table.prv_traffic_idx != trafficIndex) || (!pDM_Odm->DM_EDCA_Table.bCurrentTurboEDCA)) {
+ if ((pmlmeinfo->assoc_AP_vendor == HT_IOT_PEER_CISCO) && (pmlmeext->cur_wireless_mode & WIRELESS_11_24N))
+ edca_param = EDCAParam[pmlmeinfo->assoc_AP_vendor][trafficIndex];
+ else
+ edca_param = EDCAParam[HT_IOT_PEER_UNKNOWN][trafficIndex];
+
+ usb_write32(Adapter, REG_EDCA_BE_PARAM, edca_param);
+
+ pDM_Odm->DM_EDCA_Table.prv_traffic_idx = trafficIndex;
+ }
+
+ pDM_Odm->DM_EDCA_Table.bCurrentTurboEDCA = true;
+ } else {
+ /* Turn Off EDCA turbo here. */
+ /* Restore original EDCA according to the declaration of AP. */
+ if (pDM_Odm->DM_EDCA_Table.bCurrentTurboEDCA) {
+ usb_write32(Adapter, REG_EDCA_BE_PARAM, pHalData->AcParam_BE);
+ pDM_Odm->DM_EDCA_Table.bCurrentTurboEDCA = false;
+ }
+ }
+
+dm_CheckEdcaTurbo_EXIT:
+ /* Set variables for next time. */
+ precvpriv->bIsAnyNonBEPkts = false;
+ pxmitpriv->last_tx_bytes = pxmitpriv->tx_bytes;
+ precvpriv->last_rx_bytes = precvpriv->rx_bytes;
+}
diff --git a/drivers/staging/rtl8188eu/hal/odm_HWConfig.c b/drivers/staging/rtl8188eu/hal/odm_HWConfig.c
new file mode 100644
index 000000000..36afe45d1
--- /dev/null
+++ b/drivers/staging/rtl8188eu/hal/odm_HWConfig.c
@@ -0,0 +1,433 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2011 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ *
+ ******************************************************************************/
+
+/* include files */
+
+#include "odm_precomp.h"
+
+#define READ_AND_CONFIG READ_AND_CONFIG_MP
+
+#define READ_AND_CONFIG_MP(ic, txt) (ODM_ReadAndConfig##txt##ic(dm_odm))
+#define READ_AND_CONFIG_TC(ic, txt) (ODM_ReadAndConfig_TC##txt##ic(dm_odm))
+
+static u8 odm_QueryRxPwrPercentage(s8 AntPower)
+{
+ if ((AntPower <= -100) || (AntPower >= 20))
+ return 0;
+ else if (AntPower >= 0)
+ return 100;
+ else
+ return 100+AntPower;
+}
+
+/* 2012/01/12 MH MOve some signal strength smooth method to MP HAL layer. */
+/* IF other SW team do not support the feature, remove this section.?? */
+static s32 odm_SignalScaleMapping_92CSeries(struct odm_dm_struct *dm_odm, s32 CurrSig)
+{
+ s32 RetSig = 0;
+
+ if (CurrSig >= 51 && CurrSig <= 100)
+ RetSig = 100;
+ else if (CurrSig >= 41 && CurrSig <= 50)
+ RetSig = 80 + ((CurrSig - 40)*2);
+ else if (CurrSig >= 31 && CurrSig <= 40)
+ RetSig = 66 + (CurrSig - 30);
+ else if (CurrSig >= 21 && CurrSig <= 30)
+ RetSig = 54 + (CurrSig - 20);
+ else if (CurrSig >= 10 && CurrSig <= 20)
+ RetSig = 42 + (((CurrSig - 10) * 2) / 3);
+ else if (CurrSig >= 5 && CurrSig <= 9)
+ RetSig = 22 + (((CurrSig - 5) * 3) / 2);
+ else if (CurrSig >= 1 && CurrSig <= 4)
+ RetSig = 6 + (((CurrSig - 1) * 3) / 2);
+ else
+ RetSig = CurrSig;
+ return RetSig;
+}
+
+static s32 odm_SignalScaleMapping(struct odm_dm_struct *dm_odm, s32 CurrSig)
+{
+ return odm_SignalScaleMapping_92CSeries(dm_odm, CurrSig);
+}
+
+static u8 odm_EVMdbToPercentage(s8 Value)
+{
+ /* -33dB~0dB to 0%~99% */
+ s8 ret_val;
+
+ ret_val = Value;
+
+ if (ret_val >= 0)
+ ret_val = 0;
+ if (ret_val <= -33)
+ ret_val = -33;
+
+ ret_val = 0 - ret_val;
+ ret_val *= 3;
+
+ if (ret_val == 99)
+ ret_val = 100;
+ return ret_val;
+}
+
+static void odm_RxPhyStatus92CSeries_Parsing(struct odm_dm_struct *dm_odm,
+ struct odm_phy_status_info *pPhyInfo,
+ u8 *pPhyStatus,
+ struct odm_per_pkt_info *pPktinfo)
+{
+ struct sw_ant_switch *pDM_SWAT_Table = &dm_odm->DM_SWAT_Table;
+ u8 i, Max_spatial_stream;
+ s8 rx_pwr[4], rx_pwr_all = 0;
+ u8 EVM, PWDB_ALL = 0, PWDB_ALL_BT;
+ u8 RSSI, total_rssi = 0;
+ u8 isCCKrate = 0;
+ u8 rf_rx_num = 0;
+ u8 cck_highpwr = 0;
+ u8 LNA_idx, VGA_idx;
+
+ struct phy_status_rpt *pPhyStaRpt = (struct phy_status_rpt *)pPhyStatus;
+
+ isCCKrate = ((pPktinfo->Rate >= DESC92C_RATE1M) && (pPktinfo->Rate <= DESC92C_RATE11M)) ? true : false;
+
+ pPhyInfo->RxMIMOSignalQuality[RF_PATH_A] = -1;
+ pPhyInfo->RxMIMOSignalQuality[RF_PATH_B] = -1;
+
+ if (isCCKrate) {
+ u8 cck_agc_rpt;
+
+ dm_odm->PhyDbgInfo.NumQryPhyStatusCCK++;
+ /* (1)Hardware does not provide RSSI for CCK */
+ /* (2)PWDB, Average PWDB cacluated by hardware (for rate adaptive) */
+
+ cck_highpwr = dm_odm->bCckHighPower;
+
+ cck_agc_rpt = pPhyStaRpt->cck_agc_rpt_ofdm_cfosho_a;
+
+ /* 2011.11.28 LukeLee: 88E use different LNA & VGA gain table */
+ /* The RSSI formula should be modified according to the gain table */
+ /* In 88E, cck_highpwr is always set to 1 */
+ LNA_idx = (cck_agc_rpt & 0xE0) >> 5;
+ VGA_idx = cck_agc_rpt & 0x1F;
+ switch (LNA_idx) {
+ case 7:
+ if (VGA_idx <= 27)
+ rx_pwr_all = -100 + 2*(27-VGA_idx); /* VGA_idx = 27~2 */
+ else
+ rx_pwr_all = -100;
+ break;
+ case 6:
+ rx_pwr_all = -48 + 2*(2-VGA_idx); /* VGA_idx = 2~0 */
+ break;
+ case 5:
+ rx_pwr_all = -42 + 2*(7-VGA_idx); /* VGA_idx = 7~5 */
+ break;
+ case 4:
+ rx_pwr_all = -36 + 2*(7-VGA_idx); /* VGA_idx = 7~4 */
+ break;
+ case 3:
+ rx_pwr_all = -24 + 2*(7-VGA_idx); /* VGA_idx = 7~0 */
+ break;
+ case 2:
+ if (cck_highpwr)
+ rx_pwr_all = -12 + 2*(5-VGA_idx); /* VGA_idx = 5~0 */
+ else
+ rx_pwr_all = -6 + 2*(5-VGA_idx);
+ break;
+ case 1:
+ rx_pwr_all = 8-2*VGA_idx;
+ break;
+ case 0:
+ rx_pwr_all = 14-2*VGA_idx;
+ break;
+ default:
+ break;
+ }
+ rx_pwr_all += 6;
+ PWDB_ALL = odm_QueryRxPwrPercentage(rx_pwr_all);
+ if (!cck_highpwr) {
+ if (PWDB_ALL >= 80)
+ PWDB_ALL = ((PWDB_ALL-80)<<1)+((PWDB_ALL-80)>>1)+80;
+ else if ((PWDB_ALL <= 78) && (PWDB_ALL >= 20))
+ PWDB_ALL += 3;
+ if (PWDB_ALL > 100)
+ PWDB_ALL = 100;
+ }
+
+ pPhyInfo->RxPWDBAll = PWDB_ALL;
+ pPhyInfo->BTRxRSSIPercentage = PWDB_ALL;
+ pPhyInfo->RecvSignalPower = rx_pwr_all;
+ /* (3) Get Signal Quality (EVM) */
+ if (pPktinfo->bPacketMatchBSSID) {
+ u8 SQ, SQ_rpt;
+
+ if (pPhyInfo->RxPWDBAll > 40 && !dm_odm->bInHctTest) {
+ SQ = 100;
+ } else {
+ SQ_rpt = pPhyStaRpt->cck_sig_qual_ofdm_pwdb_all;
+
+ if (SQ_rpt > 64)
+ SQ = 0;
+ else if (SQ_rpt < 20)
+ SQ = 100;
+ else
+ SQ = ((64-SQ_rpt) * 100) / 44;
+ }
+ pPhyInfo->SignalQuality = SQ;
+ pPhyInfo->RxMIMOSignalQuality[RF_PATH_A] = SQ;
+ pPhyInfo->RxMIMOSignalQuality[RF_PATH_B] = -1;
+ }
+ } else { /* is OFDM rate */
+ dm_odm->PhyDbgInfo.NumQryPhyStatusOFDM++;
+
+ /* (1)Get RSSI for HT rate */
+
+ for (i = RF_PATH_A; i < RF_PATH_MAX; i++) {
+ /* 2008/01/30 MH we will judge RF RX path now. */
+ if (dm_odm->RFPathRxEnable & BIT(i))
+ rf_rx_num++;
+
+ rx_pwr[i] = ((pPhyStaRpt->path_agc[i].gain & 0x3F)*2) - 110;
+
+ pPhyInfo->RxPwr[i] = rx_pwr[i];
+
+ /* Translate DBM to percentage. */
+ RSSI = odm_QueryRxPwrPercentage(rx_pwr[i]);
+ total_rssi += RSSI;
+
+ /* Modification for ext-LNA board */
+ if (dm_odm->BoardType == ODM_BOARD_HIGHPWR) {
+ if ((pPhyStaRpt->path_agc[i].trsw) == 1)
+ RSSI = (RSSI > 94) ? 100 : (RSSI + 6);
+ else
+ RSSI = (RSSI <= 16) ? (RSSI >> 3) : (RSSI - 16);
+
+ if ((RSSI <= 34) && (RSSI >= 4))
+ RSSI -= 4;
+ }
+
+ pPhyInfo->RxMIMOSignalStrength[i] = (u8)RSSI;
+
+ /* Get Rx snr value in DB */
+ pPhyInfo->RxSNR[i] = (s32)(pPhyStaRpt->path_rxsnr[i]/2);
+ dm_odm->PhyDbgInfo.RxSNRdB[i] = (s32)(pPhyStaRpt->path_rxsnr[i]/2);
+ }
+ /* (2)PWDB, Average PWDB cacluated by hardware (for rate adaptive) */
+ rx_pwr_all = (((pPhyStaRpt->cck_sig_qual_ofdm_pwdb_all) >> 1) & 0x7f) - 110;
+
+ PWDB_ALL = odm_QueryRxPwrPercentage(rx_pwr_all);
+ PWDB_ALL_BT = PWDB_ALL;
+
+ pPhyInfo->RxPWDBAll = PWDB_ALL;
+ pPhyInfo->BTRxRSSIPercentage = PWDB_ALL_BT;
+ pPhyInfo->RxPower = rx_pwr_all;
+ pPhyInfo->RecvSignalPower = rx_pwr_all;
+
+ /* (3)EVM of HT rate */
+ if (pPktinfo->Rate >= DESC92C_RATEMCS8 && pPktinfo->Rate <= DESC92C_RATEMCS15)
+ Max_spatial_stream = 2; /* both spatial stream make sense */
+ else
+ Max_spatial_stream = 1; /* only spatial stream 1 makes sense */
+
+ for (i = 0; i < Max_spatial_stream; i++) {
+ /* Do not use shift operation like "rx_evmX >>= 1" because the compilor of free build environment */
+ /* fill most significant bit to "zero" when doing shifting operation which may change a negative */
+ /* value to positive one, then the dbm value (which is supposed to be negative) is not correct anymore. */
+ EVM = odm_EVMdbToPercentage((pPhyStaRpt->stream_rxevm[i])); /* dbm */
+
+ if (pPktinfo->bPacketMatchBSSID) {
+ if (i == RF_PATH_A) /* Fill value in RFD, Get the first spatial stream only */
+ pPhyInfo->SignalQuality = (u8)(EVM & 0xff);
+ pPhyInfo->RxMIMOSignalQuality[i] = (u8)(EVM & 0xff);
+ }
+ }
+ }
+ /* UI BSS List signal strength(in percentage), make it good looking, from 0~100. */
+ /* It is assigned to the BSS List in GetValueFromBeaconOrProbeRsp(). */
+ if (isCCKrate) {
+ pPhyInfo->SignalStrength = (u8)(odm_SignalScaleMapping(dm_odm, PWDB_ALL));/* PWDB_ALL; */
+ } else {
+ if (rf_rx_num != 0)
+ pPhyInfo->SignalStrength = (u8)(odm_SignalScaleMapping(dm_odm, total_rssi /= rf_rx_num));
+ }
+
+ /* For 92C/92D HW (Hybrid) Antenna Diversity */
+ pDM_SWAT_Table->antsel = pPhyStaRpt->ant_sel;
+ /* For 88E HW Antenna Diversity */
+ dm_odm->DM_FatTable.antsel_rx_keep_0 = pPhyStaRpt->ant_sel;
+ dm_odm->DM_FatTable.antsel_rx_keep_1 = pPhyStaRpt->ant_sel_b;
+ dm_odm->DM_FatTable.antsel_rx_keep_2 = pPhyStaRpt->antsel_rx_keep_2;
+}
+
+static void odm_Process_RSSIForDM(struct odm_dm_struct *dm_odm,
+ struct odm_phy_status_info *pPhyInfo,
+ struct odm_per_pkt_info *pPktinfo)
+{
+ s32 UndecoratedSmoothedPWDB, UndecoratedSmoothedCCK;
+ s32 UndecoratedSmoothedOFDM, RSSI_Ave;
+ u8 isCCKrate = 0;
+ u8 RSSI_max, RSSI_min, i;
+ u32 OFDM_pkt = 0;
+ u32 Weighting = 0;
+ struct sta_info *pEntry;
+ u8 antsel_tr_mux;
+ struct fast_ant_train *pDM_FatTable = &dm_odm->DM_FatTable;
+
+ if (pPktinfo->StationID == 0xFF)
+ return;
+ pEntry = dm_odm->pODM_StaInfo[pPktinfo->StationID];
+ if (!IS_STA_VALID(pEntry))
+ return;
+ if ((!pPktinfo->bPacketMatchBSSID))
+ return;
+
+ isCCKrate = ((pPktinfo->Rate >= DESC92C_RATE1M) && (pPktinfo->Rate <= DESC92C_RATE11M)) ? true : false;
+
+ /* Smart Antenna Debug Message------------------ */
+
+ if (dm_odm->AntDivType == CG_TRX_SMART_ANTDIV) {
+ if (pDM_FatTable->FAT_State == FAT_TRAINING_STATE) {
+ if (pPktinfo->bPacketToSelf) {
+ antsel_tr_mux = (pDM_FatTable->antsel_rx_keep_2<<2) |
+ (pDM_FatTable->antsel_rx_keep_1<<1) |
+ pDM_FatTable->antsel_rx_keep_0;
+ pDM_FatTable->antSumRSSI[antsel_tr_mux] += pPhyInfo->RxPWDBAll;
+ pDM_FatTable->antRSSIcnt[antsel_tr_mux]++;
+ }
+ }
+ } else if ((dm_odm->AntDivType == CG_TRX_HW_ANTDIV) || (dm_odm->AntDivType == CGCS_RX_HW_ANTDIV)) {
+ if (pPktinfo->bPacketToSelf || pPktinfo->bPacketBeacon) {
+ antsel_tr_mux = (pDM_FatTable->antsel_rx_keep_2<<2) |
+ (pDM_FatTable->antsel_rx_keep_1<<1) | pDM_FatTable->antsel_rx_keep_0;
+ rtl88eu_dm_ant_sel_statistics(dm_odm, antsel_tr_mux, pPktinfo->StationID, pPhyInfo->RxPWDBAll);
+ }
+ }
+ /* Smart Antenna Debug Message------------------ */
+
+ UndecoratedSmoothedCCK = pEntry->rssi_stat.UndecoratedSmoothedCCK;
+ UndecoratedSmoothedOFDM = pEntry->rssi_stat.UndecoratedSmoothedOFDM;
+ UndecoratedSmoothedPWDB = pEntry->rssi_stat.UndecoratedSmoothedPWDB;
+
+ if (pPktinfo->bPacketToSelf || pPktinfo->bPacketBeacon) {
+ if (!isCCKrate) { /* ofdm rate */
+ if (pPhyInfo->RxMIMOSignalStrength[RF_PATH_B] == 0) {
+ RSSI_Ave = pPhyInfo->RxMIMOSignalStrength[RF_PATH_A];
+ } else {
+ if (pPhyInfo->RxMIMOSignalStrength[RF_PATH_A] > pPhyInfo->RxMIMOSignalStrength[RF_PATH_B]) {
+ RSSI_max = pPhyInfo->RxMIMOSignalStrength[RF_PATH_A];
+ RSSI_min = pPhyInfo->RxMIMOSignalStrength[RF_PATH_B];
+ } else {
+ RSSI_max = pPhyInfo->RxMIMOSignalStrength[RF_PATH_B];
+ RSSI_min = pPhyInfo->RxMIMOSignalStrength[RF_PATH_A];
+ }
+ if ((RSSI_max - RSSI_min) < 3)
+ RSSI_Ave = RSSI_max;
+ else if ((RSSI_max - RSSI_min) < 6)
+ RSSI_Ave = RSSI_max - 1;
+ else if ((RSSI_max - RSSI_min) < 10)
+ RSSI_Ave = RSSI_max - 2;
+ else
+ RSSI_Ave = RSSI_max - 3;
+ }
+
+ /* 1 Process OFDM RSSI */
+ if (UndecoratedSmoothedOFDM <= 0) { /* initialize */
+ UndecoratedSmoothedOFDM = pPhyInfo->RxPWDBAll;
+ } else {
+ if (pPhyInfo->RxPWDBAll > (u32)UndecoratedSmoothedOFDM) {
+ UndecoratedSmoothedOFDM =
+ (((UndecoratedSmoothedOFDM)*(Rx_Smooth_Factor-1)) +
+ (RSSI_Ave)) / (Rx_Smooth_Factor);
+ UndecoratedSmoothedOFDM = UndecoratedSmoothedOFDM + 1;
+ } else {
+ UndecoratedSmoothedOFDM =
+ (((UndecoratedSmoothedOFDM)*(Rx_Smooth_Factor-1)) +
+ (RSSI_Ave)) / (Rx_Smooth_Factor);
+ }
+ }
+
+ pEntry->rssi_stat.PacketMap = (pEntry->rssi_stat.PacketMap<<1) | BIT0;
+
+ } else {
+ RSSI_Ave = pPhyInfo->RxPWDBAll;
+
+ /* 1 Process CCK RSSI */
+ if (UndecoratedSmoothedCCK <= 0) { /* initialize */
+ UndecoratedSmoothedCCK = pPhyInfo->RxPWDBAll;
+ } else {
+ if (pPhyInfo->RxPWDBAll > (u32)UndecoratedSmoothedCCK) {
+ UndecoratedSmoothedCCK =
+ ((UndecoratedSmoothedCCK * (Rx_Smooth_Factor-1)) +
+ pPhyInfo->RxPWDBAll) / Rx_Smooth_Factor;
+ UndecoratedSmoothedCCK = UndecoratedSmoothedCCK + 1;
+ } else {
+ UndecoratedSmoothedCCK =
+ ((UndecoratedSmoothedCCK * (Rx_Smooth_Factor-1)) +
+ pPhyInfo->RxPWDBAll) / Rx_Smooth_Factor;
+ }
+ }
+ pEntry->rssi_stat.PacketMap = pEntry->rssi_stat.PacketMap<<1;
+ }
+ /* 2011.07.28 LukeLee: modified to prevent unstable CCK RSSI */
+ if (pEntry->rssi_stat.ValidBit >= 64)
+ pEntry->rssi_stat.ValidBit = 64;
+ else
+ pEntry->rssi_stat.ValidBit++;
+
+ for (i = 0; i < pEntry->rssi_stat.ValidBit; i++)
+ OFDM_pkt += (u8)(pEntry->rssi_stat.PacketMap>>i)&BIT0;
+
+ if (pEntry->rssi_stat.ValidBit == 64) {
+ Weighting = ((OFDM_pkt<<4) > 64) ? 64 : (OFDM_pkt<<4);
+ UndecoratedSmoothedPWDB = (Weighting*UndecoratedSmoothedOFDM+(64-Weighting)*UndecoratedSmoothedCCK)>>6;
+ } else {
+ if (pEntry->rssi_stat.ValidBit != 0)
+ UndecoratedSmoothedPWDB = (OFDM_pkt * UndecoratedSmoothedOFDM +
+ (pEntry->rssi_stat.ValidBit-OFDM_pkt) *
+ UndecoratedSmoothedCCK)/pEntry->rssi_stat.ValidBit;
+ else
+ UndecoratedSmoothedPWDB = 0;
+ }
+ pEntry->rssi_stat.UndecoratedSmoothedCCK = UndecoratedSmoothedCCK;
+ pEntry->rssi_stat.UndecoratedSmoothedOFDM = UndecoratedSmoothedOFDM;
+ pEntry->rssi_stat.UndecoratedSmoothedPWDB = UndecoratedSmoothedPWDB;
+ }
+}
+
+/* Endianness before calling this API */
+static void ODM_PhyStatusQuery_92CSeries(struct odm_dm_struct *dm_odm,
+ struct odm_phy_status_info *pPhyInfo,
+ u8 *pPhyStatus,
+ struct odm_per_pkt_info *pPktinfo)
+{
+ odm_RxPhyStatus92CSeries_Parsing(dm_odm, pPhyInfo, pPhyStatus,
+ pPktinfo);
+ if (dm_odm->RSSI_test) {
+ ;/* Select the packets to do RSSI checking for antenna switching. */
+ } else {
+ odm_Process_RSSIForDM(dm_odm, pPhyInfo, pPktinfo);
+ }
+}
+
+void ODM_PhyStatusQuery(struct odm_dm_struct *dm_odm,
+ struct odm_phy_status_info *pPhyInfo,
+ u8 *pPhyStatus, struct odm_per_pkt_info *pPktinfo)
+{
+ ODM_PhyStatusQuery_92CSeries(dm_odm, pPhyInfo, pPhyStatus, pPktinfo);
+}
diff --git a/drivers/staging/rtl8188eu/hal/odm_RTL8188E.c b/drivers/staging/rtl8188eu/hal/odm_RTL8188E.c
new file mode 100644
index 000000000..d3c687392
--- /dev/null
+++ b/drivers/staging/rtl8188eu/hal/odm_RTL8188E.c
@@ -0,0 +1,372 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2011 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ *
+ ******************************************************************************/
+
+#include "odm_precomp.h"
+#include "phy.h"
+
+static void dm_rx_hw_antena_div_init(struct odm_dm_struct *dm_odm)
+{
+ struct adapter *adapter = dm_odm->Adapter;
+ u32 value32;
+
+ if (*(dm_odm->mp_mode) == 1) {
+ dm_odm->AntDivType = CGCS_RX_SW_ANTDIV;
+ phy_set_bb_reg(adapter, ODM_REG_IGI_A_11N, BIT7, 0);
+ phy_set_bb_reg(adapter, ODM_REG_LNA_SWITCH_11N, BIT31, 1);
+ return;
+ }
+
+ /* MAC Setting */
+ value32 = phy_query_bb_reg(adapter, ODM_REG_ANTSEL_PIN_11N, bMaskDWord);
+ phy_set_bb_reg(adapter, ODM_REG_ANTSEL_PIN_11N, bMaskDWord,
+ value32|(BIT23|BIT25));
+ /* Pin Settings */
+ phy_set_bb_reg(adapter, ODM_REG_PIN_CTRL_11N, BIT9|BIT8, 0);
+ phy_set_bb_reg(adapter, ODM_REG_RX_ANT_CTRL_11N, BIT10, 0);
+ phy_set_bb_reg(adapter, ODM_REG_LNA_SWITCH_11N, BIT22, 1);
+ phy_set_bb_reg(adapter, ODM_REG_LNA_SWITCH_11N, BIT31, 1);
+ /* OFDM Settings */
+ phy_set_bb_reg(adapter, ODM_REG_ANTDIV_PARA1_11N, bMaskDWord,
+ 0x000000a0);
+ /* CCK Settings */
+ phy_set_bb_reg(adapter, ODM_REG_BB_PWR_SAV4_11N, BIT7, 1);
+ phy_set_bb_reg(adapter, ODM_REG_CCK_ANTDIV_PARA2_11N, BIT4, 1);
+ rtl88eu_dm_update_rx_idle_ant(dm_odm, MAIN_ANT);
+ phy_set_bb_reg(adapter, ODM_REG_ANT_MAPPING1_11N, 0xFFFF, 0x0201);
+}
+
+static void dm_trx_hw_antenna_div_init(struct odm_dm_struct *dm_odm)
+{
+ struct adapter *adapter = dm_odm->Adapter;
+ u32 value32;
+
+ if (*(dm_odm->mp_mode) == 1) {
+ dm_odm->AntDivType = CGCS_RX_SW_ANTDIV;
+ phy_set_bb_reg(adapter, ODM_REG_IGI_A_11N, BIT7, 0);
+ phy_set_bb_reg(adapter, ODM_REG_RX_ANT_CTRL_11N,
+ BIT5|BIT4|BIT3, 0);
+ return;
+ }
+
+ /* MAC Setting */
+ value32 = phy_query_bb_reg(adapter, ODM_REG_ANTSEL_PIN_11N, bMaskDWord);
+ phy_set_bb_reg(adapter, ODM_REG_ANTSEL_PIN_11N, bMaskDWord,
+ value32|(BIT23|BIT25));
+ /* Pin Settings */
+ phy_set_bb_reg(adapter, ODM_REG_PIN_CTRL_11N, BIT9|BIT8, 0);
+ phy_set_bb_reg(adapter, ODM_REG_RX_ANT_CTRL_11N, BIT10, 0);
+ phy_set_bb_reg(adapter, ODM_REG_LNA_SWITCH_11N, BIT22, 0);
+ phy_set_bb_reg(adapter, ODM_REG_LNA_SWITCH_11N, BIT31, 1);
+ /* OFDM Settings */
+ phy_set_bb_reg(adapter, ODM_REG_ANTDIV_PARA1_11N, bMaskDWord,
+ 0x000000a0);
+ /* CCK Settings */
+ phy_set_bb_reg(adapter, ODM_REG_BB_PWR_SAV4_11N, BIT7, 1);
+ phy_set_bb_reg(adapter, ODM_REG_CCK_ANTDIV_PARA2_11N, BIT4, 1);
+ /* Tx Settings */
+ phy_set_bb_reg(adapter, ODM_REG_TX_ANT_CTRL_11N, BIT21, 0);
+ rtl88eu_dm_update_rx_idle_ant(dm_odm, MAIN_ANT);
+
+ /* antenna mapping table */
+ if (!dm_odm->bIsMPChip) { /* testchip */
+ phy_set_bb_reg(adapter, ODM_REG_RX_DEFUALT_A_11N,
+ BIT10|BIT9|BIT8, 1);
+ phy_set_bb_reg(adapter, ODM_REG_RX_DEFUALT_A_11N,
+ BIT13|BIT12|BIT11, 2);
+ } else { /* MPchip */
+ phy_set_bb_reg(adapter, ODM_REG_ANT_MAPPING1_11N, bMaskDWord,
+ 0x0201);
+ }
+}
+
+static void dm_fast_training_init(struct odm_dm_struct *dm_odm)
+{
+ struct adapter *adapter = dm_odm->Adapter;
+ u32 value32, i;
+ struct fast_ant_train *dm_fat_tbl = &dm_odm->DM_FatTable;
+ u32 AntCombination = 2;
+
+ if (*(dm_odm->mp_mode) == 1) {
+ return;
+ }
+
+ for (i = 0; i < 6; i++) {
+ dm_fat_tbl->Bssid[i] = 0;
+ dm_fat_tbl->antSumRSSI[i] = 0;
+ dm_fat_tbl->antRSSIcnt[i] = 0;
+ dm_fat_tbl->antAveRSSI[i] = 0;
+ }
+ dm_fat_tbl->TrainIdx = 0;
+ dm_fat_tbl->FAT_State = FAT_NORMAL_STATE;
+
+ /* MAC Setting */
+ value32 = phy_query_bb_reg(adapter, 0x4c, bMaskDWord);
+ phy_set_bb_reg(adapter, 0x4c, bMaskDWord, value32|(BIT23|BIT25));
+ value32 = phy_query_bb_reg(adapter, 0x7B4, bMaskDWord);
+ phy_set_bb_reg(adapter, 0x7b4, bMaskDWord, value32|(BIT16|BIT17));
+
+ /* Match MAC ADDR */
+ phy_set_bb_reg(adapter, 0x7b4, 0xFFFF, 0);
+ phy_set_bb_reg(adapter, 0x7b0, bMaskDWord, 0);
+
+ phy_set_bb_reg(adapter, 0x870, BIT9|BIT8, 0);
+ phy_set_bb_reg(adapter, 0x864, BIT10, 0);
+ phy_set_bb_reg(adapter, 0xb2c, BIT22, 0);
+ phy_set_bb_reg(adapter, 0xb2c, BIT31, 1);
+ phy_set_bb_reg(adapter, 0xca4, bMaskDWord, 0x000000a0);
+
+ /* antenna mapping table */
+ if (AntCombination == 2) {
+ if (!dm_odm->bIsMPChip) { /* testchip */
+ phy_set_bb_reg(adapter, 0x858, BIT10|BIT9|BIT8, 1);
+ phy_set_bb_reg(adapter, 0x858, BIT13|BIT12|BIT11, 2);
+ } else { /* MPchip */
+ phy_set_bb_reg(adapter, 0x914, bMaskByte0, 1);
+ phy_set_bb_reg(adapter, 0x914, bMaskByte1, 2);
+ }
+ } else if (AntCombination == 7) {
+ if (!dm_odm->bIsMPChip) { /* testchip */
+ phy_set_bb_reg(adapter, 0x858, BIT10|BIT9|BIT8, 0);
+ phy_set_bb_reg(adapter, 0x858, BIT13|BIT12|BIT11, 1);
+ phy_set_bb_reg(adapter, 0x878, BIT16, 0);
+ phy_set_bb_reg(adapter, 0x858, BIT15|BIT14, 2);
+ phy_set_bb_reg(adapter, 0x878, BIT19|BIT18|BIT17, 3);
+ phy_set_bb_reg(adapter, 0x878, BIT22|BIT21|BIT20, 4);
+ phy_set_bb_reg(adapter, 0x878, BIT25|BIT24|BIT23, 5);
+ phy_set_bb_reg(adapter, 0x878, BIT28|BIT27|BIT26, 6);
+ phy_set_bb_reg(adapter, 0x878, BIT31|BIT30|BIT29, 7);
+ } else { /* MPchip */
+ phy_set_bb_reg(adapter, 0x914, bMaskByte0, 0);
+ phy_set_bb_reg(adapter, 0x914, bMaskByte1, 1);
+ phy_set_bb_reg(adapter, 0x914, bMaskByte2, 2);
+ phy_set_bb_reg(adapter, 0x914, bMaskByte3, 3);
+ phy_set_bb_reg(adapter, 0x918, bMaskByte0, 4);
+ phy_set_bb_reg(adapter, 0x918, bMaskByte1, 5);
+ phy_set_bb_reg(adapter, 0x918, bMaskByte2, 6);
+ phy_set_bb_reg(adapter, 0x918, bMaskByte3, 7);
+ }
+ }
+
+ /* Default Ant Setting when no fast training */
+ phy_set_bb_reg(adapter, 0x80c, BIT21, 1);
+ phy_set_bb_reg(adapter, 0x864, BIT5|BIT4|BIT3, 0);
+ phy_set_bb_reg(adapter, 0x864, BIT8|BIT7|BIT6, 1);
+
+ /* Enter Traing state */
+ phy_set_bb_reg(adapter, 0x864, BIT2|BIT1|BIT0, (AntCombination-1));
+ phy_set_bb_reg(adapter, 0xc50, BIT7, 1);
+}
+
+void rtl88eu_dm_antenna_div_init(struct odm_dm_struct *dm_odm)
+{
+ if (dm_odm->AntDivType == CGCS_RX_HW_ANTDIV)
+ dm_rx_hw_antena_div_init(dm_odm);
+ else if (dm_odm->AntDivType == CG_TRX_HW_ANTDIV)
+ dm_trx_hw_antenna_div_init(dm_odm);
+ else if (dm_odm->AntDivType == CG_TRX_SMART_ANTDIV)
+ dm_fast_training_init(dm_odm);
+}
+
+void rtl88eu_dm_update_rx_idle_ant(struct odm_dm_struct *dm_odm, u8 ant)
+{
+ struct fast_ant_train *dm_fat_tbl = &dm_odm->DM_FatTable;
+ struct adapter *adapter = dm_odm->Adapter;
+ u32 default_ant, optional_ant;
+
+ if (dm_fat_tbl->RxIdleAnt != ant) {
+ if (ant == MAIN_ANT) {
+ default_ant = (dm_odm->AntDivType == CG_TRX_HW_ANTDIV) ?
+ MAIN_ANT_CG_TRX : MAIN_ANT_CGCS_RX;
+ optional_ant = (dm_odm->AntDivType == CG_TRX_HW_ANTDIV) ?
+ AUX_ANT_CG_TRX : AUX_ANT_CGCS_RX;
+ } else {
+ default_ant = (dm_odm->AntDivType == CG_TRX_HW_ANTDIV) ?
+ AUX_ANT_CG_TRX : AUX_ANT_CGCS_RX;
+ optional_ant = (dm_odm->AntDivType == CG_TRX_HW_ANTDIV) ?
+ MAIN_ANT_CG_TRX : MAIN_ANT_CGCS_RX;
+ }
+
+ if (dm_odm->AntDivType == CG_TRX_HW_ANTDIV) {
+ phy_set_bb_reg(adapter, ODM_REG_RX_ANT_CTRL_11N,
+ BIT5|BIT4|BIT3, default_ant);
+ phy_set_bb_reg(adapter, ODM_REG_RX_ANT_CTRL_11N,
+ BIT8|BIT7|BIT6, optional_ant);
+ phy_set_bb_reg(adapter, ODM_REG_ANTSEL_CTRL_11N,
+ BIT14|BIT13|BIT12, default_ant);
+ phy_set_bb_reg(adapter, ODM_REG_RESP_TX_11N,
+ BIT6|BIT7, default_ant);
+ } else if (dm_odm->AntDivType == CGCS_RX_HW_ANTDIV) {
+ phy_set_bb_reg(adapter, ODM_REG_RX_ANT_CTRL_11N,
+ BIT5|BIT4|BIT3, default_ant);
+ phy_set_bb_reg(adapter, ODM_REG_RX_ANT_CTRL_11N,
+ BIT8|BIT7|BIT6, optional_ant);
+ }
+ }
+ dm_fat_tbl->RxIdleAnt = ant;
+}
+
+static void update_tx_ant_88eu(struct odm_dm_struct *dm_odm, u8 ant, u32 mac_id)
+{
+ struct fast_ant_train *dm_fat_tbl = &dm_odm->DM_FatTable;
+ u8 target_ant;
+
+ if (ant == MAIN_ANT)
+ target_ant = MAIN_ANT_CG_TRX;
+ else
+ target_ant = AUX_ANT_CG_TRX;
+ dm_fat_tbl->antsel_a[mac_id] = target_ant&BIT0;
+ dm_fat_tbl->antsel_b[mac_id] = (target_ant&BIT1)>>1;
+ dm_fat_tbl->antsel_c[mac_id] = (target_ant&BIT2)>>2;
+}
+
+void rtl88eu_dm_set_tx_ant_by_tx_info(struct odm_dm_struct *dm_odm,
+ u8 *desc, u8 mac_id)
+{
+ struct fast_ant_train *dm_fat_tbl = &dm_odm->DM_FatTable;
+
+ if ((dm_odm->AntDivType == CG_TRX_HW_ANTDIV) ||
+ (dm_odm->AntDivType == CG_TRX_SMART_ANTDIV)) {
+ SET_TX_DESC_ANTSEL_A_88E(desc, dm_fat_tbl->antsel_a[mac_id]);
+ SET_TX_DESC_ANTSEL_B_88E(desc, dm_fat_tbl->antsel_b[mac_id]);
+ SET_TX_DESC_ANTSEL_C_88E(desc, dm_fat_tbl->antsel_c[mac_id]);
+ }
+}
+
+void rtl88eu_dm_ant_sel_statistics(struct odm_dm_struct *dm_odm,
+ u8 antsel_tr_mux, u32 mac_id, u8 rx_pwdb_all)
+{
+ struct fast_ant_train *dm_fat_tbl = &dm_odm->DM_FatTable;
+ if (dm_odm->AntDivType == CG_TRX_HW_ANTDIV) {
+ if (antsel_tr_mux == MAIN_ANT_CG_TRX) {
+ dm_fat_tbl->MainAnt_Sum[mac_id] += rx_pwdb_all;
+ dm_fat_tbl->MainAnt_Cnt[mac_id]++;
+ } else {
+ dm_fat_tbl->AuxAnt_Sum[mac_id] += rx_pwdb_all;
+ dm_fat_tbl->AuxAnt_Cnt[mac_id]++;
+ }
+ } else if (dm_odm->AntDivType == CGCS_RX_HW_ANTDIV) {
+ if (antsel_tr_mux == MAIN_ANT_CGCS_RX) {
+ dm_fat_tbl->MainAnt_Sum[mac_id] += rx_pwdb_all;
+ dm_fat_tbl->MainAnt_Cnt[mac_id]++;
+ } else {
+ dm_fat_tbl->AuxAnt_Sum[mac_id] += rx_pwdb_all;
+ dm_fat_tbl->AuxAnt_Cnt[mac_id]++;
+ }
+ }
+}
+
+static void rtl88eu_dm_hw_ant_div(struct odm_dm_struct *dm_odm)
+{
+ struct fast_ant_train *dm_fat_tbl = &dm_odm->DM_FatTable;
+ struct rtw_dig *dig_table = &dm_odm->DM_DigTable;
+ struct sta_info *entry;
+ u32 i, min_rssi = 0xFF, ant_div_max_rssi = 0, max_rssi = 0;
+ u32 local_min_rssi, local_max_rssi;
+ u32 main_rssi, aux_rssi;
+ u8 RxIdleAnt = 0, target_ant = 7;
+
+ for (i = 0; i < ODM_ASSOCIATE_ENTRY_NUM; i++) {
+ entry = dm_odm->pODM_StaInfo[i];
+ if (IS_STA_VALID(entry)) {
+ /* 2 Caculate RSSI per Antenna */
+ main_rssi = (dm_fat_tbl->MainAnt_Cnt[i] != 0) ?
+ (dm_fat_tbl->MainAnt_Sum[i]/dm_fat_tbl->MainAnt_Cnt[i]) : 0;
+ aux_rssi = (dm_fat_tbl->AuxAnt_Cnt[i] != 0) ?
+ (dm_fat_tbl->AuxAnt_Sum[i]/dm_fat_tbl->AuxAnt_Cnt[i]) : 0;
+ target_ant = (main_rssi >= aux_rssi) ? MAIN_ANT : AUX_ANT;
+ /* 2 Select max_rssi for DIG */
+ local_max_rssi = (main_rssi > aux_rssi) ?
+ main_rssi : aux_rssi;
+ if ((local_max_rssi > ant_div_max_rssi) &&
+ (local_max_rssi < 40))
+ ant_div_max_rssi = local_max_rssi;
+ if (local_max_rssi > max_rssi)
+ max_rssi = local_max_rssi;
+
+ /* 2 Select RX Idle Antenna */
+ if ((dm_fat_tbl->RxIdleAnt == MAIN_ANT) &&
+ (main_rssi == 0))
+ main_rssi = aux_rssi;
+ else if ((dm_fat_tbl->RxIdleAnt == AUX_ANT) &&
+ (aux_rssi == 0))
+ aux_rssi = main_rssi;
+
+ local_min_rssi = (main_rssi > aux_rssi) ?
+ aux_rssi : main_rssi;
+ if (local_min_rssi < min_rssi) {
+ min_rssi = local_min_rssi;
+ RxIdleAnt = target_ant;
+ }
+ /* 2 Select TRX Antenna */
+ if (dm_odm->AntDivType == CG_TRX_HW_ANTDIV)
+ update_tx_ant_88eu(dm_odm, target_ant, i);
+ }
+ dm_fat_tbl->MainAnt_Sum[i] = 0;
+ dm_fat_tbl->AuxAnt_Sum[i] = 0;
+ dm_fat_tbl->MainAnt_Cnt[i] = 0;
+ dm_fat_tbl->AuxAnt_Cnt[i] = 0;
+ }
+
+ /* 2 Set RX Idle Antenna */
+ rtl88eu_dm_update_rx_idle_ant(dm_odm, RxIdleAnt);
+
+ dig_table->AntDiv_RSSI_max = ant_div_max_rssi;
+ dig_table->RSSI_max = max_rssi;
+}
+
+void rtl88eu_dm_antenna_diversity(struct odm_dm_struct *dm_odm)
+{
+ struct fast_ant_train *dm_fat_tbl = &dm_odm->DM_FatTable;
+ struct adapter *adapter = dm_odm->Adapter;
+
+ if (!(dm_odm->SupportAbility & ODM_BB_ANT_DIV))
+ return;
+ if (!dm_odm->bLinked) {
+ ODM_RT_TRACE(dm_odm, ODM_COMP_ANT_DIV, ODM_DBG_LOUD,
+ ("ODM_AntennaDiversity_88E(): No Link.\n"));
+ if (dm_fat_tbl->bBecomeLinked) {
+ ODM_RT_TRACE(dm_odm, ODM_COMP_ANT_DIV, ODM_DBG_LOUD,
+ ("Need to Turn off HW AntDiv\n"));
+ phy_set_bb_reg(adapter, ODM_REG_IGI_A_11N, BIT7, 0);
+ phy_set_bb_reg(adapter, ODM_REG_CCK_ANTDIV_PARA1_11N,
+ BIT15, 0);
+ if (dm_odm->AntDivType == CG_TRX_HW_ANTDIV)
+ phy_set_bb_reg(adapter, ODM_REG_TX_ANT_CTRL_11N,
+ BIT21, 0);
+ dm_fat_tbl->bBecomeLinked = dm_odm->bLinked;
+ }
+ return;
+ } else {
+ if (!dm_fat_tbl->bBecomeLinked) {
+ ODM_RT_TRACE(dm_odm, ODM_COMP_ANT_DIV, ODM_DBG_LOUD,
+ ("Need to Turn on HW AntDiv\n"));
+ phy_set_bb_reg(adapter, ODM_REG_IGI_A_11N, BIT7, 1);
+ phy_set_bb_reg(adapter, ODM_REG_CCK_ANTDIV_PARA1_11N,
+ BIT15, 1);
+ if (dm_odm->AntDivType == CG_TRX_HW_ANTDIV)
+ phy_set_bb_reg(adapter, ODM_REG_TX_ANT_CTRL_11N,
+ BIT21, 1);
+ dm_fat_tbl->bBecomeLinked = dm_odm->bLinked;
+ }
+ }
+ if ((dm_odm->AntDivType == CG_TRX_HW_ANTDIV) ||
+ (dm_odm->AntDivType == CGCS_RX_HW_ANTDIV))
+ rtl88eu_dm_hw_ant_div(dm_odm);
+}
diff --git a/drivers/staging/rtl8188eu/hal/phy.c b/drivers/staging/rtl8188eu/hal/phy.c
new file mode 100644
index 000000000..6e4c3ee03
--- /dev/null
+++ b/drivers/staging/rtl8188eu/hal/phy.c
@@ -0,0 +1,1470 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2011 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ *
+ ******************************************************************************/
+#define _RTL8188E_PHYCFG_C_
+
+#include <osdep_service.h>
+#include <drv_types.h>
+#include <rtw_iol.h>
+#include <rtl8188e_hal.h>
+#include <rf.h>
+#include <phy.h>
+
+#define MAX_PRECMD_CNT 16
+#define MAX_RFDEPENDCMD_CNT 16
+#define MAX_POSTCMD_CNT 16
+
+#define MAX_DOZE_WAITING_TIMES_9x 64
+
+static u32 cal_bit_shift(u32 bitmask)
+{
+ u32 i;
+
+ for (i = 0; i <= 31; i++) {
+ if (((bitmask >> i) & 0x1) == 1)
+ break;
+ }
+ return i;
+}
+
+u32 phy_query_bb_reg(struct adapter *adapt, u32 regaddr, u32 bitmask)
+{
+ u32 return_value = 0, original_value, bit_shift;
+
+ original_value = usb_read32(adapt, regaddr);
+ bit_shift = cal_bit_shift(bitmask);
+ return_value = (original_value & bitmask) >> bit_shift;
+ return return_value;
+}
+
+void phy_set_bb_reg(struct adapter *adapt, u32 regaddr, u32 bitmask, u32 data)
+{
+ u32 original_value, bit_shift;
+
+ if (bitmask != bMaskDWord) { /* if not "double word" write */
+ original_value = usb_read32(adapt, regaddr);
+ bit_shift = cal_bit_shift(bitmask);
+ data = (original_value & (~bitmask)) | (data << bit_shift);
+ }
+
+ usb_write32(adapt, regaddr, data);
+}
+
+static u32 rf_serial_read(struct adapter *adapt,
+ enum rf_radio_path rfpath, u32 offset)
+{
+ u32 ret = 0;
+ struct hal_data_8188e *hal_data = GET_HAL_DATA(adapt);
+ struct bb_reg_def *phyreg = &hal_data->PHYRegDef[rfpath];
+ u32 tmplong, tmplong2;
+ u8 rfpi_enable = 0;
+
+ offset &= 0xff;
+
+ tmplong = phy_query_bb_reg(adapt, rFPGA0_XA_HSSIParameter2, bMaskDWord);
+ if (rfpath == RF_PATH_A)
+ tmplong2 = tmplong;
+ else
+ tmplong2 = phy_query_bb_reg(adapt, phyreg->rfHSSIPara2,
+ bMaskDWord);
+
+ tmplong2 = (tmplong2 & (~bLSSIReadAddress)) |
+ (offset<<23) | bLSSIReadEdge;
+
+ phy_set_bb_reg(adapt, rFPGA0_XA_HSSIParameter2, bMaskDWord,
+ tmplong&(~bLSSIReadEdge));
+ udelay(10);
+
+ phy_set_bb_reg(adapt, phyreg->rfHSSIPara2, bMaskDWord, tmplong2);
+ udelay(100);
+
+ udelay(10);
+
+ if (rfpath == RF_PATH_A)
+ rfpi_enable = (u8)phy_query_bb_reg(adapt, rFPGA0_XA_HSSIParameter1, BIT8);
+ else if (rfpath == RF_PATH_B)
+ rfpi_enable = (u8)phy_query_bb_reg(adapt, rFPGA0_XB_HSSIParameter1, BIT8);
+
+ if (rfpi_enable)
+ ret = phy_query_bb_reg(adapt, phyreg->rfLSSIReadBackPi,
+ bLSSIReadBackData);
+ else
+ ret = phy_query_bb_reg(adapt, phyreg->rfLSSIReadBack,
+ bLSSIReadBackData);
+ return ret;
+}
+
+static void rf_serial_write(struct adapter *adapt,
+ enum rf_radio_path rfpath, u32 offset,
+ u32 data)
+{
+ u32 data_and_addr = 0;
+ struct hal_data_8188e *hal_data = GET_HAL_DATA(adapt);
+ struct bb_reg_def *phyreg = &hal_data->PHYRegDef[rfpath];
+
+ offset &= 0xff;
+ data_and_addr = ((offset<<20) | (data&0x000fffff)) & 0x0fffffff;
+ phy_set_bb_reg(adapt, phyreg->rf3wireOffset, bMaskDWord, data_and_addr);
+}
+
+u32 phy_query_rf_reg(struct adapter *adapt, enum rf_radio_path rf_path,
+ u32 reg_addr, u32 bit_mask)
+{
+ u32 original_value, readback_value, bit_shift;
+
+ original_value = rf_serial_read(adapt, rf_path, reg_addr);
+ bit_shift = cal_bit_shift(bit_mask);
+ readback_value = (original_value & bit_mask) >> bit_shift;
+ return readback_value;
+}
+
+void phy_set_rf_reg(struct adapter *adapt, enum rf_radio_path rf_path,
+ u32 reg_addr, u32 bit_mask, u32 data)
+{
+ u32 original_value, bit_shift;
+
+ /* RF data is 12 bits only */
+ if (bit_mask != bRFRegOffsetMask) {
+ original_value = rf_serial_read(adapt, rf_path, reg_addr);
+ bit_shift = cal_bit_shift(bit_mask);
+ data = (original_value & (~bit_mask)) | (data << bit_shift);
+ }
+
+ rf_serial_write(adapt, rf_path, reg_addr, data);
+}
+
+static void get_tx_power_index(struct adapter *adapt, u8 channel, u8 *cck_pwr,
+ u8 *ofdm_pwr, u8 *bw20_pwr, u8 *bw40_pwr)
+{
+ struct hal_data_8188e *hal_data = GET_HAL_DATA(adapt);
+ u8 index = (channel - 1);
+ u8 TxCount = 0, path_nums;
+
+ if ((RF_1T2R == hal_data->rf_type) || (RF_1T1R == hal_data->rf_type))
+ path_nums = 1;
+ else
+ path_nums = 2;
+
+ for (TxCount = 0; TxCount < path_nums; TxCount++) {
+ if (TxCount == RF_PATH_A) {
+ cck_pwr[TxCount] = hal_data->Index24G_CCK_Base[TxCount][index];
+ ofdm_pwr[TxCount] = hal_data->Index24G_BW40_Base[RF_PATH_A][index]+
+ hal_data->OFDM_24G_Diff[TxCount][RF_PATH_A];
+
+ bw20_pwr[TxCount] = hal_data->Index24G_BW40_Base[RF_PATH_A][index]+
+ hal_data->BW20_24G_Diff[TxCount][RF_PATH_A];
+ bw40_pwr[TxCount] = hal_data->Index24G_BW40_Base[TxCount][index];
+ } else if (TxCount == RF_PATH_B) {
+ cck_pwr[TxCount] = hal_data->Index24G_CCK_Base[TxCount][index];
+ ofdm_pwr[TxCount] = hal_data->Index24G_BW40_Base[RF_PATH_A][index]+
+ hal_data->BW20_24G_Diff[RF_PATH_A][index]+
+ hal_data->BW20_24G_Diff[TxCount][index];
+
+ bw20_pwr[TxCount] = hal_data->Index24G_BW40_Base[RF_PATH_A][index]+
+ hal_data->BW20_24G_Diff[TxCount][RF_PATH_A]+
+ hal_data->BW20_24G_Diff[TxCount][index];
+ bw40_pwr[TxCount] = hal_data->Index24G_BW40_Base[TxCount][index];
+ } else if (TxCount == RF_PATH_C) {
+ cck_pwr[TxCount] = hal_data->Index24G_CCK_Base[TxCount][index];
+ ofdm_pwr[TxCount] = hal_data->Index24G_BW40_Base[RF_PATH_A][index]+
+ hal_data->BW20_24G_Diff[RF_PATH_A][index]+
+ hal_data->BW20_24G_Diff[RF_PATH_B][index]+
+ hal_data->BW20_24G_Diff[TxCount][index];
+
+ bw20_pwr[TxCount] = hal_data->Index24G_BW40_Base[RF_PATH_A][index]+
+ hal_data->BW20_24G_Diff[RF_PATH_A][index]+
+ hal_data->BW20_24G_Diff[RF_PATH_B][index]+
+ hal_data->BW20_24G_Diff[TxCount][index];
+ bw40_pwr[TxCount] = hal_data->Index24G_BW40_Base[TxCount][index];
+ } else if (TxCount == RF_PATH_D) {
+ cck_pwr[TxCount] = hal_data->Index24G_CCK_Base[TxCount][index];
+ ofdm_pwr[TxCount] = hal_data->Index24G_BW40_Base[RF_PATH_A][index]+
+ hal_data->BW20_24G_Diff[RF_PATH_A][index]+
+ hal_data->BW20_24G_Diff[RF_PATH_B][index]+
+ hal_data->BW20_24G_Diff[RF_PATH_C][index]+
+ hal_data->BW20_24G_Diff[TxCount][index];
+
+ bw20_pwr[TxCount] = hal_data->Index24G_BW40_Base[RF_PATH_A][index]+
+ hal_data->BW20_24G_Diff[RF_PATH_A][index]+
+ hal_data->BW20_24G_Diff[RF_PATH_B][index]+
+ hal_data->BW20_24G_Diff[RF_PATH_C][index]+
+ hal_data->BW20_24G_Diff[TxCount][index];
+ bw40_pwr[TxCount] = hal_data->Index24G_BW40_Base[TxCount][index];
+ }
+ }
+}
+
+static void phy_power_index_check(struct adapter *adapt, u8 channel,
+ u8 *cck_pwr, u8 *ofdm_pwr, u8 *bw20_pwr,
+ u8 *bw40_pwr)
+{
+ struct hal_data_8188e *hal_data = GET_HAL_DATA(adapt);
+
+ hal_data->CurrentCckTxPwrIdx = cck_pwr[0];
+ hal_data->CurrentOfdm24GTxPwrIdx = ofdm_pwr[0];
+ hal_data->CurrentBW2024GTxPwrIdx = bw20_pwr[0];
+ hal_data->CurrentBW4024GTxPwrIdx = bw40_pwr[0];
+}
+
+void phy_set_tx_power_level(struct adapter *adapt, u8 channel)
+{
+ u8 cck_pwr[MAX_TX_COUNT] = {0};
+ u8 ofdm_pwr[MAX_TX_COUNT] = {0};/* [0]:RF-A, [1]:RF-B */
+ u8 bw20_pwr[MAX_TX_COUNT] = {0};
+ u8 bw40_pwr[MAX_TX_COUNT] = {0};
+
+ get_tx_power_index(adapt, channel, &cck_pwr[0], &ofdm_pwr[0],
+ &bw20_pwr[0], &bw40_pwr[0]);
+
+ phy_power_index_check(adapt, channel, &cck_pwr[0], &ofdm_pwr[0],
+ &bw20_pwr[0], &bw40_pwr[0]);
+
+ rtl88eu_phy_rf6052_set_cck_txpower(adapt, &cck_pwr[0]);
+ rtl88eu_phy_rf6052_set_ofdm_txpower(adapt, &ofdm_pwr[0], &bw20_pwr[0],
+ &bw40_pwr[0], channel);
+}
+
+static void phy_set_bw_mode_callback(struct adapter *adapt)
+{
+ struct hal_data_8188e *hal_data = GET_HAL_DATA(adapt);
+ u8 reg_bw_opmode;
+ u8 reg_prsr_rsc;
+
+ if (hal_data->rf_chip == RF_PSEUDO_11N)
+ return;
+
+ /* There is no 40MHz mode in RF_8225. */
+ if (hal_data->rf_chip == RF_8225)
+ return;
+
+ if (adapt->bDriverStopped)
+ return;
+
+ /* Set MAC register */
+
+ reg_bw_opmode = usb_read8(adapt, REG_BWOPMODE);
+ reg_prsr_rsc = usb_read8(adapt, REG_RRSR+2);
+
+ switch (hal_data->CurrentChannelBW) {
+ case HT_CHANNEL_WIDTH_20:
+ reg_bw_opmode |= BW_OPMODE_20MHZ;
+ usb_write8(adapt, REG_BWOPMODE, reg_bw_opmode);
+ break;
+ case HT_CHANNEL_WIDTH_40:
+ reg_bw_opmode &= ~BW_OPMODE_20MHZ;
+ usb_write8(adapt, REG_BWOPMODE, reg_bw_opmode);
+ reg_prsr_rsc = (reg_prsr_rsc&0x90) |
+ (hal_data->nCur40MhzPrimeSC<<5);
+ usb_write8(adapt, REG_RRSR+2, reg_prsr_rsc);
+ break;
+ default:
+ break;
+ }
+
+ /* Set PHY related register */
+ switch (hal_data->CurrentChannelBW) {
+ case HT_CHANNEL_WIDTH_20:
+ phy_set_bb_reg(adapt, rFPGA0_RFMOD, bRFMOD, 0x0);
+ phy_set_bb_reg(adapt, rFPGA1_RFMOD, bRFMOD, 0x0);
+ break;
+ case HT_CHANNEL_WIDTH_40:
+ phy_set_bb_reg(adapt, rFPGA0_RFMOD, bRFMOD, 0x1);
+ phy_set_bb_reg(adapt, rFPGA1_RFMOD, bRFMOD, 0x1);
+ /* Set Control channel to upper or lower.
+ * These settings are required only for 40MHz
+ */
+ phy_set_bb_reg(adapt, rCCK0_System, bCCKSideBand,
+ (hal_data->nCur40MhzPrimeSC>>1));
+ phy_set_bb_reg(adapt, rOFDM1_LSTF, 0xC00,
+ hal_data->nCur40MhzPrimeSC);
+ phy_set_bb_reg(adapt, 0x818, (BIT26 | BIT27),
+ (hal_data->nCur40MhzPrimeSC == HAL_PRIME_CHNL_OFFSET_LOWER) ? 2 : 1);
+ break;
+ default:
+ break;
+ }
+
+ /* Set RF related register */
+ if (hal_data->rf_chip == RF_6052)
+ rtl88eu_phy_rf6052_set_bandwidth(adapt, hal_data->CurrentChannelBW);
+}
+
+void phy_set_bw_mode(struct adapter *adapt, enum ht_channel_width bandwidth,
+ unsigned char offset)
+{
+ struct hal_data_8188e *hal_data = GET_HAL_DATA(adapt);
+ enum ht_channel_width tmp_bw = hal_data->CurrentChannelBW;
+
+ hal_data->CurrentChannelBW = bandwidth;
+ hal_data->nCur40MhzPrimeSC = offset;
+
+ if ((!adapt->bDriverStopped) && (!adapt->bSurpriseRemoved))
+ phy_set_bw_mode_callback(adapt);
+ else
+ hal_data->CurrentChannelBW = tmp_bw;
+}
+
+static void phy_sw_chnl_callback(struct adapter *adapt, u8 channel)
+{
+ u8 rf_path;
+ u32 param1, param2;
+ struct hal_data_8188e *hal_data = GET_HAL_DATA(adapt);
+
+ if (adapt->bNotifyChannelChange)
+ DBG_88E("[%s] ch = %d\n", __func__, channel);
+
+ phy_set_tx_power_level(adapt, channel);
+
+ param1 = RF_CHNLBW;
+ param2 = channel;
+ for (rf_path = 0; rf_path < hal_data->NumTotalRFPath; rf_path++) {
+ hal_data->RfRegChnlVal[rf_path] = (hal_data->RfRegChnlVal[rf_path] &
+ 0xfffffc00) | param2;
+ phy_set_rf_reg(adapt, (enum rf_radio_path)rf_path, param1,
+ bRFRegOffsetMask, hal_data->RfRegChnlVal[rf_path]);
+ }
+}
+
+void phy_sw_chnl(struct adapter *adapt, u8 channel)
+{
+ struct hal_data_8188e *hal_data = GET_HAL_DATA(adapt);
+ u8 tmpchannel = hal_data->CurrentChannel;
+
+ if (hal_data->rf_chip == RF_PSEUDO_11N)
+ return;
+
+ if (channel == 0)
+ channel = 1;
+
+ hal_data->CurrentChannel = channel;
+
+ if ((!adapt->bDriverStopped) && (!adapt->bSurpriseRemoved))
+ phy_sw_chnl_callback(adapt, channel);
+ else
+ hal_data->CurrentChannel = tmpchannel;
+}
+
+#define ODM_TXPWRTRACK_MAX_IDX_88E 6
+
+static u8 get_right_chnl_for_iqk(u8 chnl)
+{
+ u8 place;
+ u8 channel_all[ODM_TARGET_CHNL_NUM_2G_5G] = {
+ 36, 38, 40, 42, 44, 46, 48, 50, 52, 54, 56, 58, 60, 62, 64,
+ 100, 102, 104, 106, 108, 110, 112, 114, 116, 118, 120, 122,
+ 124, 126, 128, 130, 132, 134, 136, 138, 140, 149, 151, 153,
+ 155, 157, 159, 161, 163, 165
+ };
+
+ if (chnl > 14) {
+ for (place = 0; place < sizeof(channel_all); place++) {
+ if (channel_all[place] == chnl)
+ return ++place;
+ }
+ }
+ return 0;
+}
+
+void rtl88eu_dm_txpower_track_adjust(struct odm_dm_struct *dm_odm, u8 type,
+ u8 *direction, u32 *out_write_val)
+{
+ u8 pwr_value = 0;
+ /* Tx power tracking BB swing table. */
+ if (type == 0) { /* For OFDM adjust */
+ ODM_RT_TRACE(dm_odm, ODM_COMP_TX_PWR_TRACK, ODM_DBG_LOUD,
+ ("BbSwingIdxOfdm = %d BbSwingFlagOfdm=%d\n",
+ dm_odm->BbSwingIdxOfdm, dm_odm->BbSwingFlagOfdm));
+
+ if (dm_odm->BbSwingIdxOfdm <= dm_odm->BbSwingIdxOfdmBase) {
+ *direction = 1;
+ pwr_value = dm_odm->BbSwingIdxOfdmBase -
+ dm_odm->BbSwingIdxOfdm;
+ } else {
+ *direction = 2;
+ pwr_value = dm_odm->BbSwingIdxOfdm -
+ dm_odm->BbSwingIdxOfdmBase;
+ }
+
+ } else if (type == 1) { /* For CCK adjust. */
+ ODM_RT_TRACE(dm_odm, ODM_COMP_TX_PWR_TRACK, ODM_DBG_LOUD,
+ ("dm_odm->BbSwingIdxCck = %d dm_odm->BbSwingIdxCckBase = %d\n",
+ dm_odm->BbSwingIdxCck, dm_odm->BbSwingIdxCckBase));
+
+ if (dm_odm->BbSwingIdxCck <= dm_odm->BbSwingIdxCckBase) {
+ *direction = 1;
+ pwr_value = dm_odm->BbSwingIdxCckBase -
+ dm_odm->BbSwingIdxCck;
+ } else {
+ *direction = 2;
+ pwr_value = dm_odm->BbSwingIdxCck -
+ dm_odm->BbSwingIdxCckBase;
+ }
+
+ }
+
+ if (pwr_value >= ODM_TXPWRTRACK_MAX_IDX_88E && *direction == 1)
+ pwr_value = ODM_TXPWRTRACK_MAX_IDX_88E;
+
+ *out_write_val = pwr_value | (pwr_value<<8) | (pwr_value<<16) |
+ (pwr_value<<24);
+}
+
+static void dm_txpwr_track_setpwr(struct odm_dm_struct *dm_odm)
+{
+ if (dm_odm->BbSwingFlagOfdm || dm_odm->BbSwingFlagCck) {
+ ODM_RT_TRACE(dm_odm, ODM_COMP_TX_PWR_TRACK, ODM_DBG_LOUD,
+ ("dm_txpwr_track_setpwr CH=%d\n", *(dm_odm->pChannel)));
+ phy_set_tx_power_level(dm_odm->Adapter, *(dm_odm->pChannel));
+ dm_odm->BbSwingFlagOfdm = false;
+ dm_odm->BbSwingFlagCck = false;
+ }
+}
+
+void rtl88eu_dm_txpower_tracking_callback_thermalmeter(struct adapter *adapt)
+{
+ struct hal_data_8188e *hal_data = GET_HAL_DATA(adapt);
+ u8 thermal_val = 0, delta, delta_lck, delta_iqk, offset;
+ u8 thermal_avg_count = 0;
+ u32 thermal_avg = 0;
+ s32 ele_d, temp_cck;
+ s8 ofdm_index[2], cck_index = 0;
+ s8 ofdm_index_old[2] = {0, 0}, cck_index_old = 0;
+ u32 i = 0, j = 0;
+ bool is2t = false;
+
+ u8 ofdm_min_index = 6, rf; /* OFDM BB Swing should be less than +3.0dB */
+ s8 ofdm_index_mapping[2][index_mapping_NUM_88E] = {
+ /* 2.4G, decrease power */
+ {0, 0, 2, 3, 4, 4, 5, 6, 7, 7, 8, 9, 10, 10, 11},
+ /* 2.4G, increase power */
+ {0, 0, -1, -2, -3, -4, -4, -4, -4, -5, -7, -8, -9, -9, -10},
+ };
+ u8 thermal_mapping[2][index_mapping_NUM_88E] = {
+ /* 2.4G, decrease power */
+ {0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 27},
+ /* 2.4G, increase power */
+ {0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 25, 25, 25},
+ };
+ struct odm_dm_struct *dm_odm = &hal_data->odmpriv;
+
+ dm_txpwr_track_setpwr(dm_odm);
+
+ dm_odm->RFCalibrateInfo.TXPowerTrackingCallbackCnt++;
+ dm_odm->RFCalibrateInfo.bTXPowerTrackingInit = true;
+
+ dm_odm->RFCalibrateInfo.RegA24 = 0x090e1317;
+
+ thermal_val = (u8)phy_query_rf_reg(adapt, RF_PATH_A,
+ RF_T_METER_88E, 0xfc00);
+
+ if (is2t)
+ rf = 2;
+ else
+ rf = 1;
+
+ if (thermal_val) {
+ /* Query OFDM path A default setting */
+ ele_d = phy_query_bb_reg(adapt, rOFDM0_XATxIQImbalance, bMaskDWord)&bMaskOFDM_D;
+ for (i = 0; i < OFDM_TABLE_SIZE_92D; i++) {
+ if (ele_d == (OFDMSwingTable[i]&bMaskOFDM_D)) {
+ ofdm_index_old[0] = (u8)i;
+ dm_odm->BbSwingIdxOfdmBase = (u8)i;
+ break;
+ }
+ }
+
+ /* Query OFDM path B default setting */
+ if (is2t) {
+ ele_d = phy_query_bb_reg(adapt, rOFDM0_XBTxIQImbalance, bMaskDWord)&bMaskOFDM_D;
+ for (i = 0; i < OFDM_TABLE_SIZE_92D; i++) {
+ if (ele_d == (OFDMSwingTable[i]&bMaskOFDM_D)) {
+ ofdm_index_old[1] = (u8)i;
+ break;
+ }
+ }
+ }
+
+ /* Query CCK default setting From 0xa24 */
+ temp_cck = dm_odm->RFCalibrateInfo.RegA24;
+
+ for (i = 0; i < CCK_TABLE_SIZE; i++) {
+ if ((dm_odm->RFCalibrateInfo.bCCKinCH14 &&
+ memcmp(&temp_cck, &CCKSwingTable_Ch14[i][2], 4)) ||
+ memcmp(&temp_cck, &CCKSwingTable_Ch1_Ch13[i][2], 4)) {
+ cck_index_old = (u8)i;
+ dm_odm->BbSwingIdxCckBase = (u8)i;
+ break;
+ }
+ }
+
+ if (!dm_odm->RFCalibrateInfo.ThermalValue) {
+ dm_odm->RFCalibrateInfo.ThermalValue = hal_data->EEPROMThermalMeter;
+ dm_odm->RFCalibrateInfo.ThermalValue_LCK = thermal_val;
+ dm_odm->RFCalibrateInfo.ThermalValue_IQK = thermal_val;
+
+ for (i = 0; i < rf; i++)
+ dm_odm->RFCalibrateInfo.OFDM_index[i] = ofdm_index_old[i];
+ dm_odm->RFCalibrateInfo.CCK_index = cck_index_old;
+ }
+
+ /* calculate average thermal meter */
+ dm_odm->RFCalibrateInfo.ThermalValue_AVG[dm_odm->RFCalibrateInfo.ThermalValue_AVG_index] = thermal_val;
+ dm_odm->RFCalibrateInfo.ThermalValue_AVG_index++;
+ if (dm_odm->RFCalibrateInfo.ThermalValue_AVG_index == AVG_THERMAL_NUM_88E)
+ dm_odm->RFCalibrateInfo.ThermalValue_AVG_index = 0;
+
+ for (i = 0; i < AVG_THERMAL_NUM_88E; i++) {
+ if (dm_odm->RFCalibrateInfo.ThermalValue_AVG[i]) {
+ thermal_avg += dm_odm->RFCalibrateInfo.ThermalValue_AVG[i];
+ thermal_avg_count++;
+ }
+ }
+
+ if (thermal_avg_count)
+ thermal_val = (u8)(thermal_avg / thermal_avg_count);
+
+ if (dm_odm->RFCalibrateInfo.bDoneTxpower &&
+ !dm_odm->RFCalibrateInfo.bReloadtxpowerindex)
+ delta = abs(thermal_val - dm_odm->RFCalibrateInfo.ThermalValue);
+ else {
+ delta = abs(thermal_val - hal_data->EEPROMThermalMeter);
+ if (dm_odm->RFCalibrateInfo.bReloadtxpowerindex) {
+ dm_odm->RFCalibrateInfo.bReloadtxpowerindex = false;
+ dm_odm->RFCalibrateInfo.bDoneTxpower = false;
+ }
+ }
+
+ delta_lck = abs(dm_odm->RFCalibrateInfo.ThermalValue_LCK - thermal_val);
+ delta_iqk = abs(dm_odm->RFCalibrateInfo.ThermalValue_IQK - thermal_val);
+
+ /* Delta temperature is equal to or larger than 20 centigrade.*/
+ if ((delta_lck >= 8)) {
+ dm_odm->RFCalibrateInfo.ThermalValue_LCK = thermal_val;
+ rtl88eu_phy_lc_calibrate(adapt);
+ }
+
+ if (delta > 0 && dm_odm->RFCalibrateInfo.TxPowerTrackControl) {
+ delta = abs(hal_data->EEPROMThermalMeter - thermal_val);
+
+ /* calculate new OFDM / CCK offset */
+ if (thermal_val > hal_data->EEPROMThermalMeter)
+ j = 1;
+ else
+ j = 0;
+ for (offset = 0; offset < index_mapping_NUM_88E; offset++) {
+ if (delta < thermal_mapping[j][offset]) {
+ if (offset != 0)
+ offset--;
+ break;
+ }
+ }
+ if (offset >= index_mapping_NUM_88E)
+ offset = index_mapping_NUM_88E-1;
+
+ /* Updating ofdm_index values with new OFDM / CCK offset */
+ for (i = 0; i < rf; i++) {
+ ofdm_index[i] = dm_odm->RFCalibrateInfo.OFDM_index[i] + ofdm_index_mapping[j][offset];
+ if (ofdm_index[i] > OFDM_TABLE_SIZE_92D-1)
+ ofdm_index[i] = OFDM_TABLE_SIZE_92D-1;
+ else if (ofdm_index[i] < ofdm_min_index)
+ ofdm_index[i] = ofdm_min_index;
+ }
+
+ cck_index = dm_odm->RFCalibrateInfo.CCK_index + ofdm_index_mapping[j][offset];
+ if (cck_index > CCK_TABLE_SIZE-1)
+ cck_index = CCK_TABLE_SIZE-1;
+ else if (cck_index < 0)
+ cck_index = 0;
+
+ /* 2 temporarily remove bNOPG */
+ /* Config by SwingTable */
+ if (dm_odm->RFCalibrateInfo.TxPowerTrackControl) {
+ dm_odm->RFCalibrateInfo.bDoneTxpower = true;
+
+ /* Revse TX power table. */
+ dm_odm->BbSwingIdxOfdm = (u8)ofdm_index[0];
+ dm_odm->BbSwingIdxCck = (u8)cck_index;
+
+ if (dm_odm->BbSwingIdxOfdmCurrent != dm_odm->BbSwingIdxOfdm) {
+ dm_odm->BbSwingIdxOfdmCurrent = dm_odm->BbSwingIdxOfdm;
+ dm_odm->BbSwingFlagOfdm = true;
+ }
+
+ if (dm_odm->BbSwingIdxCckCurrent != dm_odm->BbSwingIdxCck) {
+ dm_odm->BbSwingIdxCckCurrent = dm_odm->BbSwingIdxCck;
+ dm_odm->BbSwingFlagCck = true;
+ }
+ }
+ }
+
+ /* Delta temperature is equal to or larger than 20 centigrade.*/
+ if (delta_iqk >= 8) {
+ dm_odm->RFCalibrateInfo.ThermalValue_IQK = thermal_val;
+ rtl88eu_phy_iq_calibrate(adapt, false);
+ }
+ /* update thermal meter value */
+ if (dm_odm->RFCalibrateInfo.TxPowerTrackControl)
+ dm_odm->RFCalibrateInfo.ThermalValue = thermal_val;
+ }
+ dm_odm->RFCalibrateInfo.TXPowercount = 0;
+}
+
+#define MAX_TOLERANCE 5
+
+static u8 phy_path_a_iqk(struct adapter *adapt, bool config_pathb)
+{
+ u32 reg_eac, reg_e94, reg_e9c, reg_ea4;
+ u8 result = 0x00;
+
+ /* 1 Tx IQK */
+ /* path-A IQK setting */
+ phy_set_bb_reg(adapt, rTx_IQK_Tone_A, bMaskDWord, 0x10008c1c);
+ phy_set_bb_reg(adapt, rRx_IQK_Tone_A, bMaskDWord, 0x30008c1c);
+ phy_set_bb_reg(adapt, rTx_IQK_PI_A, bMaskDWord, 0x8214032a);
+ phy_set_bb_reg(adapt, rRx_IQK_PI_A, bMaskDWord, 0x28160000);
+
+ /* LO calibration setting */
+ phy_set_bb_reg(adapt, rIQK_AGC_Rsp, bMaskDWord, 0x00462911);
+
+ /* One shot, path A LOK & IQK */
+ phy_set_bb_reg(adapt, rIQK_AGC_Pts, bMaskDWord, 0xf9000000);
+ phy_set_bb_reg(adapt, rIQK_AGC_Pts, bMaskDWord, 0xf8000000);
+
+ mdelay(IQK_DELAY_TIME_88E);
+
+ reg_eac = phy_query_bb_reg(adapt, rRx_Power_After_IQK_A_2, bMaskDWord);
+ reg_e94 = phy_query_bb_reg(adapt, rTx_Power_Before_IQK_A, bMaskDWord);
+ reg_e9c = phy_query_bb_reg(adapt, rTx_Power_After_IQK_A, bMaskDWord);
+ reg_ea4 = phy_query_bb_reg(adapt, rRx_Power_Before_IQK_A_2, bMaskDWord);
+
+ if (!(reg_eac & BIT28) &&
+ (((reg_e94 & 0x03FF0000)>>16) != 0x142) &&
+ (((reg_e9c & 0x03FF0000)>>16) != 0x42))
+ result |= 0x01;
+ return result;
+}
+
+static u8 phy_path_a_rx_iqk(struct adapter *adapt, bool configPathB)
+{
+ u32 reg_eac, reg_e94, reg_e9c, reg_ea4, u4tmp;
+ u8 result = 0x00;
+ struct hal_data_8188e *hal_data = GET_HAL_DATA(adapt);
+ struct odm_dm_struct *dm_odm = &hal_data->odmpriv;
+
+ /* 1 Get TXIMR setting */
+ /* modify RXIQK mode table */
+ phy_set_bb_reg(adapt, rFPGA0_IQK, bMaskDWord, 0x00000000);
+ phy_set_rf_reg(adapt, RF_PATH_A, RF_WE_LUT, bRFRegOffsetMask, 0x800a0);
+ phy_set_rf_reg(adapt, RF_PATH_A, RF_RCK_OS, bRFRegOffsetMask, 0x30000);
+ phy_set_rf_reg(adapt, RF_PATH_A, RF_TXPA_G1, bRFRegOffsetMask, 0x0000f);
+ phy_set_rf_reg(adapt, RF_PATH_A, RF_TXPA_G2, bRFRegOffsetMask, 0xf117B);
+
+ /* PA,PAD off */
+ phy_set_rf_reg(adapt, RF_PATH_A, 0xdf, bRFRegOffsetMask, 0x980);
+ phy_set_rf_reg(adapt, RF_PATH_A, 0x56, bRFRegOffsetMask, 0x51000);
+
+ phy_set_bb_reg(adapt, rFPGA0_IQK, bMaskDWord, 0x80800000);
+
+ /* IQK setting */
+ phy_set_bb_reg(adapt, rTx_IQK, bMaskDWord, 0x01007c00);
+ phy_set_bb_reg(adapt, rRx_IQK, bMaskDWord, 0x81004800);
+
+ /* path-A IQK setting */
+ phy_set_bb_reg(adapt, rTx_IQK_Tone_A, bMaskDWord, 0x10008c1c);
+ phy_set_bb_reg(adapt, rRx_IQK_Tone_A, bMaskDWord, 0x30008c1c);
+ phy_set_bb_reg(adapt, rTx_IQK_PI_A, bMaskDWord, 0x82160c1f);
+ phy_set_bb_reg(adapt, rRx_IQK_PI_A, bMaskDWord, 0x28160000);
+
+ /* LO calibration setting */
+ phy_set_bb_reg(adapt, rIQK_AGC_Rsp, bMaskDWord, 0x0046a911);
+
+ /* One shot, path A LOK & IQK */
+ phy_set_bb_reg(adapt, rIQK_AGC_Pts, bMaskDWord, 0xf9000000);
+ phy_set_bb_reg(adapt, rIQK_AGC_Pts, bMaskDWord, 0xf8000000);
+
+ /* delay x ms */
+ mdelay(IQK_DELAY_TIME_88E);
+
+ /* Check failed */
+ reg_eac = phy_query_bb_reg(adapt, rRx_Power_After_IQK_A_2, bMaskDWord);
+ reg_e94 = phy_query_bb_reg(adapt, rTx_Power_Before_IQK_A, bMaskDWord);
+ reg_e9c = phy_query_bb_reg(adapt, rTx_Power_After_IQK_A, bMaskDWord);
+
+ if (!(reg_eac & BIT28) &&
+ (((reg_e94 & 0x03FF0000)>>16) != 0x142) &&
+ (((reg_e9c & 0x03FF0000)>>16) != 0x42))
+ result |= 0x01;
+ else /* if Tx not OK, ignore Rx */
+ return result;
+
+ u4tmp = 0x80007C00 | (reg_e94&0x3FF0000) | ((reg_e9c&0x3FF0000) >> 16);
+ phy_set_bb_reg(adapt, rTx_IQK, bMaskDWord, u4tmp);
+
+ /* 1 RX IQK */
+ /* modify RXIQK mode table */
+ ODM_RT_TRACE(dm_odm, ODM_COMP_CALIBRATION, ODM_DBG_LOUD,
+ ("Path-A Rx IQK modify RXIQK mode table 2!\n"));
+ phy_set_bb_reg(adapt, rFPGA0_IQK, bMaskDWord, 0x00000000);
+ phy_set_rf_reg(adapt, RF_PATH_A, RF_WE_LUT, bRFRegOffsetMask, 0x800a0);
+ phy_set_rf_reg(adapt, RF_PATH_A, RF_RCK_OS, bRFRegOffsetMask, 0x30000);
+ phy_set_rf_reg(adapt, RF_PATH_A, RF_TXPA_G1, bRFRegOffsetMask, 0x0000f);
+ phy_set_rf_reg(adapt, RF_PATH_A, RF_TXPA_G2, bRFRegOffsetMask, 0xf7ffa);
+ phy_set_bb_reg(adapt, rFPGA0_IQK, bMaskDWord, 0x80800000);
+
+ /* IQK setting */
+ phy_set_bb_reg(adapt, rRx_IQK, bMaskDWord, 0x01004800);
+
+ /* path-A IQK setting */
+ phy_set_bb_reg(adapt, rTx_IQK_Tone_A, bMaskDWord, 0x38008c1c);
+ phy_set_bb_reg(adapt, rRx_IQK_Tone_A, bMaskDWord, 0x18008c1c);
+ phy_set_bb_reg(adapt, rTx_IQK_PI_A, bMaskDWord, 0x82160c05);
+ phy_set_bb_reg(adapt, rRx_IQK_PI_A, bMaskDWord, 0x28160c1f);
+
+ /* LO calibration setting */
+ phy_set_bb_reg(adapt, rIQK_AGC_Rsp, bMaskDWord, 0x0046a911);
+
+ phy_set_bb_reg(adapt, rIQK_AGC_Pts, bMaskDWord, 0xf9000000);
+ phy_set_bb_reg(adapt, rIQK_AGC_Pts, bMaskDWord, 0xf8000000);
+
+ mdelay(IQK_DELAY_TIME_88E);
+
+ /* Check failed */
+ reg_eac = phy_query_bb_reg(adapt, rRx_Power_After_IQK_A_2, bMaskDWord);
+ reg_e94 = phy_query_bb_reg(adapt, rTx_Power_Before_IQK_A, bMaskDWord);
+ reg_e9c = phy_query_bb_reg(adapt, rTx_Power_After_IQK_A, bMaskDWord);
+ reg_ea4 = phy_query_bb_reg(adapt, rRx_Power_Before_IQK_A_2, bMaskDWord);
+
+ /* reload RF 0xdf */
+ phy_set_bb_reg(adapt, rFPGA0_IQK, bMaskDWord, 0x00000000);
+ phy_set_rf_reg(adapt, RF_PATH_A, 0xdf, bRFRegOffsetMask, 0x180);
+
+ if (!(reg_eac & BIT27) && /* if Tx is OK, check whether Rx is OK */
+ (((reg_ea4 & 0x03FF0000)>>16) != 0x132) &&
+ (((reg_eac & 0x03FF0000)>>16) != 0x36))
+ result |= 0x02;
+ else
+ ODM_RT_TRACE(dm_odm, ODM_COMP_CALIBRATION, ODM_DBG_LOUD,
+ ("Path A Rx IQK fail!!\n"));
+
+ return result;
+}
+
+static u8 phy_path_b_iqk(struct adapter *adapt)
+{
+ u32 regeac, regeb4, regebc, regec4, regecc;
+ u8 result = 0x00;
+ struct hal_data_8188e *hal_data = GET_HAL_DATA(adapt);
+ struct odm_dm_struct *dm_odm = &hal_data->odmpriv;
+
+ /* One shot, path B LOK & IQK */
+ phy_set_bb_reg(adapt, rIQK_AGC_Cont, bMaskDWord, 0x00000002);
+ phy_set_bb_reg(adapt, rIQK_AGC_Cont, bMaskDWord, 0x00000000);
+
+ mdelay(IQK_DELAY_TIME_88E);
+
+ regeac = phy_query_bb_reg(adapt, rRx_Power_After_IQK_A_2, bMaskDWord);
+ regeb4 = phy_query_bb_reg(adapt, rTx_Power_Before_IQK_B, bMaskDWord);
+ regebc = phy_query_bb_reg(adapt, rTx_Power_After_IQK_B, bMaskDWord);
+ regec4 = phy_query_bb_reg(adapt, rRx_Power_Before_IQK_B_2, bMaskDWord);
+ regecc = phy_query_bb_reg(adapt, rRx_Power_After_IQK_B_2, bMaskDWord);
+
+ if (!(regeac & BIT31) &&
+ (((regeb4 & 0x03FF0000)>>16) != 0x142) &&
+ (((regebc & 0x03FF0000)>>16) != 0x42))
+ result |= 0x01;
+ else
+ return result;
+
+ if (!(regeac & BIT30) &&
+ (((regec4 & 0x03FF0000)>>16) != 0x132) &&
+ (((regecc & 0x03FF0000)>>16) != 0x36))
+ result |= 0x02;
+ else
+ ODM_RT_TRACE(dm_odm, ODM_COMP_CALIBRATION,
+ ODM_DBG_LOUD, ("Path B Rx IQK fail!!\n"));
+ return result;
+}
+
+static void patha_fill_iqk(struct adapter *adapt, bool iqkok, s32 result[][8],
+ u8 final_candidate, bool txonly)
+{
+ u32 oldval_0, x, tx0_a, reg;
+ s32 y, tx0_c;
+
+ if (final_candidate == 0xFF) {
+ return;
+ } else if (iqkok) {
+ oldval_0 = (phy_query_bb_reg(adapt, rOFDM0_XATxIQImbalance, bMaskDWord) >> 22) & 0x3FF;
+
+ x = result[final_candidate][0];
+ if ((x & 0x00000200) != 0)
+ x = x | 0xFFFFFC00;
+
+ tx0_a = (x * oldval_0) >> 8;
+ phy_set_bb_reg(adapt, rOFDM0_XATxIQImbalance, 0x3FF, tx0_a);
+ phy_set_bb_reg(adapt, rOFDM0_ECCAThreshold, BIT(31),
+ ((x * oldval_0>>7) & 0x1));
+
+ y = result[final_candidate][1];
+ if ((y & 0x00000200) != 0)
+ y = y | 0xFFFFFC00;
+
+ tx0_c = (y * oldval_0) >> 8;
+ phy_set_bb_reg(adapt, rOFDM0_XCTxAFE, 0xF0000000,
+ ((tx0_c&0x3C0)>>6));
+ phy_set_bb_reg(adapt, rOFDM0_XATxIQImbalance, 0x003F0000,
+ (tx0_c&0x3F));
+ phy_set_bb_reg(adapt, rOFDM0_ECCAThreshold, BIT(29),
+ ((y * oldval_0>>7) & 0x1));
+
+ if (txonly)
+ return;
+
+ reg = result[final_candidate][2];
+ phy_set_bb_reg(adapt, rOFDM0_XARxIQImbalance, 0x3FF, reg);
+
+ reg = result[final_candidate][3] & 0x3F;
+ phy_set_bb_reg(adapt, rOFDM0_XARxIQImbalance, 0xFC00, reg);
+
+ reg = (result[final_candidate][3] >> 6) & 0xF;
+ phy_set_bb_reg(adapt, rOFDM0_RxIQExtAnta, 0xF0000000, reg);
+ }
+}
+
+static void pathb_fill_iqk(struct adapter *adapt, bool iqkok, s32 result[][8],
+ u8 final_candidate, bool txonly)
+{
+ u32 oldval_1, x, tx1_a, reg;
+ s32 y, tx1_c;
+
+ if (final_candidate == 0xFF) {
+ return;
+ } else if (iqkok) {
+ oldval_1 = (phy_query_bb_reg(adapt, rOFDM0_XBTxIQImbalance, bMaskDWord) >> 22) & 0x3FF;
+
+ x = result[final_candidate][4];
+ if ((x & 0x00000200) != 0)
+ x = x | 0xFFFFFC00;
+ tx1_a = (x * oldval_1) >> 8;
+ phy_set_bb_reg(adapt, rOFDM0_XBTxIQImbalance, 0x3FF, tx1_a);
+
+ phy_set_bb_reg(adapt, rOFDM0_ECCAThreshold, BIT(27),
+ ((x * oldval_1>>7) & 0x1));
+
+ y = result[final_candidate][5];
+ if ((y & 0x00000200) != 0)
+ y = y | 0xFFFFFC00;
+
+ tx1_c = (y * oldval_1) >> 8;
+
+ phy_set_bb_reg(adapt, rOFDM0_XDTxAFE, 0xF0000000,
+ ((tx1_c&0x3C0)>>6));
+ phy_set_bb_reg(adapt, rOFDM0_XBTxIQImbalance, 0x003F0000,
+ (tx1_c&0x3F));
+ phy_set_bb_reg(adapt, rOFDM0_ECCAThreshold, BIT(25),
+ ((y * oldval_1>>7) & 0x1));
+
+ if (txonly)
+ return;
+
+ reg = result[final_candidate][6];
+ phy_set_bb_reg(adapt, rOFDM0_XBRxIQImbalance, 0x3FF, reg);
+
+ reg = result[final_candidate][7] & 0x3F;
+ phy_set_bb_reg(adapt, rOFDM0_XBRxIQImbalance, 0xFC00, reg);
+
+ reg = (result[final_candidate][7] >> 6) & 0xF;
+ phy_set_bb_reg(adapt, rOFDM0_AGCRSSITable, 0x0000F000, reg);
+ }
+}
+
+static void save_adda_registers(struct adapter *adapt, u32 *addareg,
+ u32 *backup, u32 register_num)
+{
+ u32 i;
+
+ for (i = 0; i < register_num; i++) {
+ backup[i] = phy_query_bb_reg(adapt, addareg[i], bMaskDWord);
+ }
+}
+
+static void save_mac_registers(struct adapter *adapt, u32 *mac_reg,
+ u32 *backup)
+{
+ u32 i;
+
+ for (i = 0; i < (IQK_MAC_REG_NUM - 1); i++) {
+ backup[i] = usb_read8(adapt, mac_reg[i]);
+ }
+ backup[i] = usb_read32(adapt, mac_reg[i]);
+}
+
+static void reload_adda_reg(struct adapter *adapt, u32 *adda_reg,
+ u32 *backup, u32 regiester_num)
+{
+ u32 i;
+
+ for (i = 0; i < regiester_num; i++)
+ phy_set_bb_reg(adapt, adda_reg[i], bMaskDWord, backup[i]);
+}
+
+static void reload_mac_registers(struct adapter *adapt,
+ u32 *mac_reg, u32 *backup)
+{
+ u32 i;
+
+ for (i = 0; i < (IQK_MAC_REG_NUM - 1); i++) {
+ usb_write8(adapt, mac_reg[i], (u8)backup[i]);
+ }
+ usb_write32(adapt, mac_reg[i], backup[i]);
+}
+
+static void path_adda_on(struct adapter *adapt, u32 *adda_reg,
+ bool is_path_a_on, bool is2t)
+{
+ u32 path_on;
+ u32 i;
+
+ if (!is2t) {
+ path_on = 0x0bdb25a0;
+ phy_set_bb_reg(adapt, adda_reg[0], bMaskDWord, 0x0b1b25a0);
+ } else {
+ path_on = is_path_a_on ? 0x04db25a4 : 0x0b1b25a4;
+ phy_set_bb_reg(adapt, adda_reg[0], bMaskDWord, path_on);
+ }
+
+ for (i = 1; i < IQK_ADDA_REG_NUM; i++)
+ phy_set_bb_reg(adapt, adda_reg[i], bMaskDWord, path_on);
+}
+
+static void mac_setting_calibration(struct adapter *adapt, u32 *mac_reg, u32 *backup)
+{
+ u32 i = 0;
+
+ usb_write8(adapt, mac_reg[i], 0x3F);
+
+ for (i = 1; i < (IQK_MAC_REG_NUM - 1); i++) {
+ usb_write8(adapt, mac_reg[i], (u8)(backup[i]&(~BIT3)));
+ }
+ usb_write8(adapt, mac_reg[i], (u8)(backup[i]&(~BIT5)));
+}
+
+static void path_a_standby(struct adapter *adapt)
+{
+
+ phy_set_bb_reg(adapt, rFPGA0_IQK, bMaskDWord, 0x0);
+ phy_set_bb_reg(adapt, 0x840, bMaskDWord, 0x00010000);
+ phy_set_bb_reg(adapt, rFPGA0_IQK, bMaskDWord, 0x80800000);
+}
+
+static void pi_mode_switch(struct adapter *adapt, bool pi_mode)
+{
+ u32 mode;
+
+ mode = pi_mode ? 0x01000100 : 0x01000000;
+ phy_set_bb_reg(adapt, rFPGA0_XA_HSSIParameter1, bMaskDWord, mode);
+ phy_set_bb_reg(adapt, rFPGA0_XB_HSSIParameter1, bMaskDWord, mode);
+}
+
+static bool simularity_compare(struct adapter *adapt, s32 resulta[][8],
+ u8 c1, u8 c2)
+{
+ u32 i, j, diff, sim_bitmap = 0, bound;
+ struct hal_data_8188e *hal_data = GET_HAL_DATA(adapt);
+ struct odm_dm_struct *dm_odm = &hal_data->odmpriv;
+ u8 final_candidate[2] = {0xFF, 0xFF}; /* for path A and path B */
+ bool result = true;
+ s32 tmp1 = 0, tmp2 = 0;
+
+ if ((dm_odm->RFType == ODM_2T2R) || (dm_odm->RFType == ODM_2T3R) ||
+ (dm_odm->RFType == ODM_2T4R))
+ bound = 8;
+ else
+ bound = 4;
+
+ for (i = 0; i < bound; i++) {
+ if ((i == 1) || (i == 3) || (i == 5) || (i == 7)) {
+ if ((resulta[c1][i] & 0x00000200) != 0)
+ tmp1 = resulta[c1][i] | 0xFFFFFC00;
+ else
+ tmp1 = resulta[c1][i];
+
+ if ((resulta[c2][i] & 0x00000200) != 0)
+ tmp2 = resulta[c2][i] | 0xFFFFFC00;
+ else
+ tmp2 = resulta[c2][i];
+ } else {
+ tmp1 = resulta[c1][i];
+ tmp2 = resulta[c2][i];
+ }
+
+ diff = (tmp1 > tmp2) ? (tmp1 - tmp2) : (tmp2 - tmp1);
+
+ if (diff > MAX_TOLERANCE) {
+ if ((i == 2 || i == 6) && !sim_bitmap) {
+ if (resulta[c1][i] + resulta[c1][i+1] == 0)
+ final_candidate[(i/4)] = c2;
+ else if (resulta[c2][i] + resulta[c2][i+1] == 0)
+ final_candidate[(i/4)] = c1;
+ else
+ sim_bitmap = sim_bitmap | (1<<i);
+ } else {
+ sim_bitmap = sim_bitmap | (1<<i);
+ }
+ }
+ }
+
+ if (sim_bitmap == 0) {
+ for (i = 0; i < (bound/4); i++) {
+ if (final_candidate[i] != 0xFF) {
+ for (j = i*4; j < (i+1)*4-2; j++)
+ resulta[3][j] = resulta[final_candidate[i]][j];
+ result = false;
+ }
+ }
+ return result;
+ } else {
+ if (!(sim_bitmap & 0x03)) { /* path A TX OK */
+ for (i = 0; i < 2; i++)
+ resulta[3][i] = resulta[c1][i];
+ }
+ if (!(sim_bitmap & 0x0c)) { /* path A RX OK */
+ for (i = 2; i < 4; i++)
+ resulta[3][i] = resulta[c1][i];
+ }
+
+ if (!(sim_bitmap & 0x30)) { /* path B TX OK */
+ for (i = 4; i < 6; i++)
+ resulta[3][i] = resulta[c1][i];
+ }
+
+ if (!(sim_bitmap & 0xc0)) { /* path B RX OK */
+ for (i = 6; i < 8; i++)
+ resulta[3][i] = resulta[c1][i];
+ }
+ return false;
+ }
+}
+
+static void phy_iq_calibrate(struct adapter *adapt, s32 result[][8],
+ u8 t, bool is2t)
+{
+ struct hal_data_8188e *hal_data = GET_HAL_DATA(adapt);
+ struct odm_dm_struct *dm_odm = &hal_data->odmpriv;
+ u32 i;
+ u8 path_a_ok, path_b_ok;
+ u32 adda_reg[IQK_ADDA_REG_NUM] = {
+ rFPGA0_XCD_SwitchControl, rBlue_Tooth,
+ rRx_Wait_CCA, rTx_CCK_RFON,
+ rTx_CCK_BBON, rTx_OFDM_RFON,
+ rTx_OFDM_BBON, rTx_To_Rx,
+ rTx_To_Tx, rRx_CCK,
+ rRx_OFDM, rRx_Wait_RIFS,
+ rRx_TO_Rx, rStandby,
+ rSleep, rPMPD_ANAEN};
+
+ u32 iqk_mac_reg[IQK_MAC_REG_NUM] = {
+ REG_TXPAUSE, REG_BCN_CTRL,
+ REG_BCN_CTRL_1, REG_GPIO_MUXCFG};
+
+ /* since 92C & 92D have the different define in IQK_BB_REG */
+ u32 iqk_bb_reg_92c[IQK_BB_REG_NUM] = {
+ rOFDM0_TRxPathEnable, rOFDM0_TRMuxPar,
+ rFPGA0_XCD_RFInterfaceSW, rConfig_AntA, rConfig_AntB,
+ rFPGA0_XAB_RFInterfaceSW, rFPGA0_XA_RFInterfaceOE,
+ rFPGA0_XB_RFInterfaceOE, rFPGA0_RFMOD};
+
+ u32 retry_count = 9;
+ if (*(dm_odm->mp_mode) == 1)
+ retry_count = 9;
+ else
+ retry_count = 2;
+
+ if (t == 0) {
+
+ /* Save ADDA parameters, turn Path A ADDA on */
+ save_adda_registers(adapt, adda_reg, dm_odm->RFCalibrateInfo.ADDA_backup,
+ IQK_ADDA_REG_NUM);
+ save_mac_registers(adapt, iqk_mac_reg,
+ dm_odm->RFCalibrateInfo.IQK_MAC_backup);
+ save_adda_registers(adapt, iqk_bb_reg_92c,
+ dm_odm->RFCalibrateInfo.IQK_BB_backup, IQK_BB_REG_NUM);
+ }
+
+ path_adda_on(adapt, adda_reg, true, is2t);
+ if (t == 0)
+ dm_odm->RFCalibrateInfo.bRfPiEnable = (u8)phy_query_bb_reg(adapt, rFPGA0_XA_HSSIParameter1,
+ BIT(8));
+
+ if (!dm_odm->RFCalibrateInfo.bRfPiEnable) {
+ /* Switch BB to PI mode to do IQ Calibration. */
+ pi_mode_switch(adapt, true);
+ }
+
+ /* BB setting */
+ phy_set_bb_reg(adapt, rFPGA0_RFMOD, BIT24, 0x00);
+ phy_set_bb_reg(adapt, rOFDM0_TRxPathEnable, bMaskDWord, 0x03a05600);
+ phy_set_bb_reg(adapt, rOFDM0_TRMuxPar, bMaskDWord, 0x000800e4);
+ phy_set_bb_reg(adapt, rFPGA0_XCD_RFInterfaceSW, bMaskDWord, 0x22204000);
+
+ phy_set_bb_reg(adapt, rFPGA0_XAB_RFInterfaceSW, BIT10, 0x01);
+ phy_set_bb_reg(adapt, rFPGA0_XAB_RFInterfaceSW, BIT26, 0x01);
+ phy_set_bb_reg(adapt, rFPGA0_XA_RFInterfaceOE, BIT10, 0x00);
+ phy_set_bb_reg(adapt, rFPGA0_XB_RFInterfaceOE, BIT10, 0x00);
+
+ if (is2t) {
+ phy_set_bb_reg(adapt, rFPGA0_XA_LSSIParameter, bMaskDWord,
+ 0x00010000);
+ phy_set_bb_reg(adapt, rFPGA0_XB_LSSIParameter, bMaskDWord,
+ 0x00010000);
+ }
+
+ /* MAC settings */
+ mac_setting_calibration(adapt, iqk_mac_reg,
+ dm_odm->RFCalibrateInfo.IQK_MAC_backup);
+
+ /* Page B init */
+ /* AP or IQK */
+ phy_set_bb_reg(adapt, rConfig_AntA, bMaskDWord, 0x0f600000);
+
+ if (is2t)
+ phy_set_bb_reg(adapt, rConfig_AntB, bMaskDWord, 0x0f600000);
+
+ /* IQ calibration setting */
+ phy_set_bb_reg(adapt, rFPGA0_IQK, bMaskDWord, 0x80800000);
+ phy_set_bb_reg(adapt, rTx_IQK, bMaskDWord, 0x01007c00);
+ phy_set_bb_reg(adapt, rRx_IQK, bMaskDWord, 0x81004800);
+
+ for (i = 0; i < retry_count; i++) {
+ path_a_ok = phy_path_a_iqk(adapt, is2t);
+ if (path_a_ok == 0x01) {
+ result[t][0] = (phy_query_bb_reg(adapt, rTx_Power_Before_IQK_A,
+ bMaskDWord)&0x3FF0000)>>16;
+ result[t][1] = (phy_query_bb_reg(adapt, rTx_Power_After_IQK_A,
+ bMaskDWord)&0x3FF0000)>>16;
+ break;
+ }
+ }
+
+ for (i = 0; i < retry_count; i++) {
+ path_a_ok = phy_path_a_rx_iqk(adapt, is2t);
+ if (path_a_ok == 0x03) {
+ result[t][2] = (phy_query_bb_reg(adapt, rRx_Power_Before_IQK_A_2,
+ bMaskDWord)&0x3FF0000)>>16;
+ result[t][3] = (phy_query_bb_reg(adapt, rRx_Power_After_IQK_A_2,
+ bMaskDWord)&0x3FF0000)>>16;
+ break;
+ } else {
+ ODM_RT_TRACE(dm_odm, ODM_COMP_CALIBRATION, ODM_DBG_LOUD,
+ ("Path A Rx IQK Fail!!\n"));
+ }
+ }
+
+ if (0x00 == path_a_ok) {
+ ODM_RT_TRACE(dm_odm, ODM_COMP_CALIBRATION, ODM_DBG_LOUD,
+ ("Path A IQK failed!!\n"));
+ }
+
+ if (is2t) {
+ path_a_standby(adapt);
+
+ /* Turn Path B ADDA on */
+ path_adda_on(adapt, adda_reg, false, is2t);
+
+ for (i = 0; i < retry_count; i++) {
+ path_b_ok = phy_path_b_iqk(adapt);
+ if (path_b_ok == 0x03) {
+ result[t][4] = (phy_query_bb_reg(adapt, rTx_Power_Before_IQK_B,
+ bMaskDWord)&0x3FF0000)>>16;
+ result[t][5] = (phy_query_bb_reg(adapt, rTx_Power_After_IQK_B,
+ bMaskDWord)&0x3FF0000)>>16;
+ result[t][6] = (phy_query_bb_reg(adapt, rRx_Power_Before_IQK_B_2,
+ bMaskDWord)&0x3FF0000)>>16;
+ result[t][7] = (phy_query_bb_reg(adapt, rRx_Power_After_IQK_B_2,
+ bMaskDWord)&0x3FF0000)>>16;
+ break;
+ } else if (i == (retry_count - 1) && path_b_ok == 0x01) { /* Tx IQK OK */
+ result[t][4] = (phy_query_bb_reg(adapt, rTx_Power_Before_IQK_B,
+ bMaskDWord)&0x3FF0000)>>16;
+ result[t][5] = (phy_query_bb_reg(adapt, rTx_Power_After_IQK_B,
+ bMaskDWord)&0x3FF0000)>>16;
+ }
+ }
+
+ if (0x00 == path_b_ok) {
+ ODM_RT_TRACE(dm_odm, ODM_COMP_CALIBRATION, ODM_DBG_LOUD,
+ ("Path B IQK failed!!\n"));
+ }
+ }
+
+ /* Back to BB mode, load original value */
+ phy_set_bb_reg(adapt, rFPGA0_IQK, bMaskDWord, 0);
+
+ if (t != 0) {
+ if (!dm_odm->RFCalibrateInfo.bRfPiEnable) {
+ /* Switch back BB to SI mode after
+ * finish IQ Calibration.
+ */
+ pi_mode_switch(adapt, false);
+ }
+
+ /* Reload ADDA power saving parameters */
+ reload_adda_reg(adapt, adda_reg, dm_odm->RFCalibrateInfo.ADDA_backup,
+ IQK_ADDA_REG_NUM);
+
+ /* Reload MAC parameters */
+ reload_mac_registers(adapt, iqk_mac_reg,
+ dm_odm->RFCalibrateInfo.IQK_MAC_backup);
+
+ reload_adda_reg(adapt, iqk_bb_reg_92c, dm_odm->RFCalibrateInfo.IQK_BB_backup,
+ IQK_BB_REG_NUM);
+
+ /* Restore RX initial gain */
+ phy_set_bb_reg(adapt, rFPGA0_XA_LSSIParameter,
+ bMaskDWord, 0x00032ed3);
+ if (is2t)
+ phy_set_bb_reg(adapt, rFPGA0_XB_LSSIParameter,
+ bMaskDWord, 0x00032ed3);
+
+ /* load 0xe30 IQC default value */
+ phy_set_bb_reg(adapt, rTx_IQK_Tone_A, bMaskDWord, 0x01008c00);
+ phy_set_bb_reg(adapt, rRx_IQK_Tone_A, bMaskDWord, 0x01008c00);
+ }
+}
+
+static void phy_lc_calibrate(struct adapter *adapt, bool is2t)
+{
+ u8 tmpreg;
+ u32 rf_a_mode = 0, rf_b_mode = 0, lc_cal;
+
+ /* Check continuous TX and Packet TX */
+ tmpreg = usb_read8(adapt, 0xd03);
+
+ if ((tmpreg&0x70) != 0)
+ usb_write8(adapt, 0xd03, tmpreg&0x8F);
+ else
+ usb_write8(adapt, REG_TXPAUSE, 0xFF);
+
+ if ((tmpreg&0x70) != 0) {
+ /* 1. Read original RF mode */
+ /* Path-A */
+ rf_a_mode = phy_query_rf_reg(adapt, RF_PATH_A, RF_AC,
+ bMask12Bits);
+
+ /* Path-B */
+ if (is2t)
+ rf_b_mode = phy_query_rf_reg(adapt, RF_PATH_B, RF_AC,
+ bMask12Bits);
+
+ /* 2. Set RF mode = standby mode */
+ /* Path-A */
+ phy_set_rf_reg(adapt, RF_PATH_A, RF_AC, bMask12Bits,
+ (rf_a_mode&0x8FFFF)|0x10000);
+
+ /* Path-B */
+ if (is2t)
+ phy_set_rf_reg(adapt, RF_PATH_B, RF_AC, bMask12Bits,
+ (rf_b_mode&0x8FFFF)|0x10000);
+ }
+
+ /* 3. Read RF reg18 */
+ lc_cal = phy_query_rf_reg(adapt, RF_PATH_A, RF_CHNLBW, bMask12Bits);
+
+ /* 4. Set LC calibration begin bit15 */
+ phy_set_rf_reg(adapt, RF_PATH_A, RF_CHNLBW, bMask12Bits,
+ lc_cal|0x08000);
+
+ msleep(100);
+
+ /* Restore original situation */
+ if ((tmpreg&0x70) != 0) {
+ /* Deal with continuous TX case */
+ /* Path-A */
+ usb_write8(adapt, 0xd03, tmpreg);
+ phy_set_rf_reg(adapt, RF_PATH_A, RF_AC, bMask12Bits, rf_a_mode);
+
+ /* Path-B */
+ if (is2t)
+ phy_set_rf_reg(adapt, RF_PATH_B, RF_AC, bMask12Bits,
+ rf_b_mode);
+ } else {
+ /* Deal with Packet TX case */
+ usb_write8(adapt, REG_TXPAUSE, 0x00);
+ }
+}
+
+void rtl88eu_phy_iq_calibrate(struct adapter *adapt, bool recovery)
+{
+ struct hal_data_8188e *hal_data = GET_HAL_DATA(adapt);
+ struct odm_dm_struct *dm_odm = &hal_data->odmpriv;
+ s32 result[4][8];
+ u8 i, final, chn_index;
+ bool pathaok, pathbok;
+ s32 reg_e94, reg_e9c, reg_ea4, reg_eac, reg_eb4, reg_ebc, reg_ec4,
+ reg_ecc;
+ bool is12simular, is13simular, is23simular;
+ bool singletone = false, carrier_sup = false;
+ u32 iqk_bb_reg_92c[IQK_BB_REG_NUM] = {
+ rOFDM0_XARxIQImbalance, rOFDM0_XBRxIQImbalance,
+ rOFDM0_ECCAThreshold, rOFDM0_AGCRSSITable,
+ rOFDM0_XATxIQImbalance, rOFDM0_XBTxIQImbalance,
+ rOFDM0_XCTxAFE, rOFDM0_XDTxAFE,
+ rOFDM0_RxIQExtAnta};
+ bool is2t;
+
+ is2t = (dm_odm->RFType == ODM_2T2R) ? true : false;
+
+ if (!(dm_odm->SupportAbility & ODM_RF_CALIBRATION))
+ return;
+
+ if (singletone || carrier_sup)
+ return;
+
+ if (recovery) {
+ ODM_RT_TRACE(dm_odm, ODM_COMP_INIT, ODM_DBG_LOUD,
+ ("phy_iq_calibrate: Return due to recovery!\n"));
+ reload_adda_reg(adapt, iqk_bb_reg_92c,
+ dm_odm->RFCalibrateInfo.IQK_BB_backup_recover, 9);
+ return;
+ }
+
+ for (i = 0; i < 8; i++) {
+ result[0][i] = 0;
+ result[1][i] = 0;
+ result[2][i] = 0;
+ if ((i == 0) || (i == 2) || (i == 4) || (i == 6))
+ result[3][i] = 0x100;
+ else
+ result[3][i] = 0;
+ }
+ final = 0xff;
+ pathaok = false;
+ pathbok = false;
+ is12simular = false;
+ is23simular = false;
+ is13simular = false;
+
+ for (i = 0; i < 3; i++) {
+ phy_iq_calibrate(adapt, result, i, is2t);
+
+ if (i == 1) {
+ is12simular = simularity_compare(adapt, result, 0, 1);
+ if (is12simular) {
+ final = 0;
+ break;
+ }
+ }
+
+ if (i == 2) {
+ is13simular = simularity_compare(adapt, result, 0, 2);
+ if (is13simular) {
+ final = 0;
+ break;
+ }
+ is23simular = simularity_compare(adapt, result, 1, 2);
+ if (is23simular)
+ final = 1;
+ else
+ final = 3;
+ }
+ }
+
+ for (i = 0; i < 4; i++) {
+ reg_e94 = result[i][0];
+ reg_e9c = result[i][1];
+ reg_ea4 = result[i][2];
+ reg_eac = result[i][3];
+ reg_eb4 = result[i][4];
+ reg_ebc = result[i][5];
+ reg_ec4 = result[i][6];
+ reg_ecc = result[i][7];
+ }
+
+ if (final != 0xff) {
+ reg_e94 = result[final][0];
+ reg_e9c = result[final][1];
+ reg_ea4 = result[final][2];
+ reg_eac = result[final][3];
+ reg_eb4 = result[final][4];
+ reg_ebc = result[final][5];
+ dm_odm->RFCalibrateInfo.RegE94 = reg_e94;
+ dm_odm->RFCalibrateInfo.RegE9C = reg_e9c;
+ dm_odm->RFCalibrateInfo.RegEB4 = reg_eb4;
+ dm_odm->RFCalibrateInfo.RegEBC = reg_ebc;
+ reg_ec4 = result[final][6];
+ reg_ecc = result[final][7];
+ pathaok = true;
+ pathbok = true;
+ } else {
+ ODM_RT_TRACE(dm_odm, ODM_COMP_CALIBRATION, ODM_DBG_LOUD,
+ ("IQK: FAIL use default value\n"));
+ dm_odm->RFCalibrateInfo.RegE94 = 0x100;
+ dm_odm->RFCalibrateInfo.RegEB4 = 0x100;
+ dm_odm->RFCalibrateInfo.RegE9C = 0x0;
+ dm_odm->RFCalibrateInfo.RegEBC = 0x0;
+ }
+ if (reg_e94 != 0)
+ patha_fill_iqk(adapt, pathaok, result, final,
+ (reg_ea4 == 0));
+ if (is2t) {
+ if (reg_eb4 != 0)
+ pathb_fill_iqk(adapt, pathbok, result, final,
+ (reg_ec4 == 0));
+ }
+
+ chn_index = get_right_chnl_for_iqk(hal_data->CurrentChannel);
+
+ if (final < 4) {
+ for (i = 0; i < IQK_Matrix_REG_NUM; i++)
+ dm_odm->RFCalibrateInfo.IQKMatrixRegSetting[chn_index].Value[0][i] = result[final][i];
+ dm_odm->RFCalibrateInfo.IQKMatrixRegSetting[chn_index].bIQKDone = true;
+ }
+
+ save_adda_registers(adapt, iqk_bb_reg_92c,
+ dm_odm->RFCalibrateInfo.IQK_BB_backup_recover, 9);
+}
+
+void rtl88eu_phy_lc_calibrate(struct adapter *adapt)
+{
+ bool singletone = false, carrier_sup = false;
+ u32 timeout = 2000, timecount = 0;
+ struct hal_data_8188e *hal_data = GET_HAL_DATA(adapt);
+ struct odm_dm_struct *dm_odm = &hal_data->odmpriv;
+
+ if (!(dm_odm->SupportAbility & ODM_RF_CALIBRATION))
+ return;
+ if (singletone || carrier_sup)
+ return;
+
+ while (*(dm_odm->pbScanInProcess) && timecount < timeout) {
+ mdelay(50);
+ timecount += 50;
+ }
+
+ dm_odm->RFCalibrateInfo.bLCKInProgress = true;
+
+ if (dm_odm->RFType == ODM_2T2R) {
+ phy_lc_calibrate(adapt, true);
+ } else {
+ /* For 88C 1T1R */
+ phy_lc_calibrate(adapt, false);
+ }
+
+ dm_odm->RFCalibrateInfo.bLCKInProgress = false;
+}
diff --git a/drivers/staging/rtl8188eu/hal/pwrseq.c b/drivers/staging/rtl8188eu/hal/pwrseq.c
new file mode 100644
index 000000000..20dce42ce
--- /dev/null
+++ b/drivers/staging/rtl8188eu/hal/pwrseq.c
@@ -0,0 +1,102 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2011 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ *
+ ******************************************************************************/
+
+#include "pwrseq.h"
+#include <rtl8188e_hal.h>
+
+/*
+ drivers should parse below arrays and do the corresponding actions
+*/
+/* 3 Power on Array */
+struct wl_pwr_cfg rtl8188E_power_on_flow[RTL8188E_TRANS_CARDEMU_TO_ACT_STEPS +
+ RTL8188E_TRANS_END_STEPS] = {
+ RTL8188E_TRANS_CARDEMU_TO_ACT
+ RTL8188E_TRANS_END
+};
+
+/* 3Radio off Array */
+struct wl_pwr_cfg rtl8188E_radio_off_flow[RTL8188E_TRANS_ACT_TO_CARDEMU_STEPS +
+ RTL8188E_TRANS_END_STEPS] = {
+ RTL8188E_TRANS_ACT_TO_CARDEMU
+ RTL8188E_TRANS_END
+};
+
+/* 3Card Disable Array */
+struct wl_pwr_cfg rtl8188E_card_disable_flow
+ [RTL8188E_TRANS_ACT_TO_CARDEMU_STEPS +
+ RTL8188E_TRANS_CARDEMU_TO_PDN_STEPS +
+ RTL8188E_TRANS_END_STEPS] = {
+ RTL8188E_TRANS_ACT_TO_CARDEMU
+ RTL8188E_TRANS_CARDEMU_TO_CARDDIS
+ RTL8188E_TRANS_END
+};
+
+/* 3 Card Enable Array */
+struct wl_pwr_cfg rtl8188E_card_enable_flow
+ [RTL8188E_TRANS_ACT_TO_CARDEMU_STEPS +
+ RTL8188E_TRANS_CARDEMU_TO_PDN_STEPS +
+ RTL8188E_TRANS_END_STEPS] = {
+ RTL8188E_TRANS_CARDDIS_TO_CARDEMU
+ RTL8188E_TRANS_CARDEMU_TO_ACT
+ RTL8188E_TRANS_END
+};
+
+/* 3Suspend Array */
+struct wl_pwr_cfg rtl8188E_suspend_flow[RTL8188E_TRANS_ACT_TO_CARDEMU_STEPS +
+ RTL8188E_TRANS_CARDEMU_TO_SUS_STEPS +
+ RTL8188E_TRANS_END_STEPS] = {
+ RTL8188E_TRANS_ACT_TO_CARDEMU
+ RTL8188E_TRANS_CARDEMU_TO_SUS
+ RTL8188E_TRANS_END
+};
+
+/* 3 Resume Array */
+struct wl_pwr_cfg rtl8188E_resume_flow[RTL8188E_TRANS_ACT_TO_CARDEMU_STEPS +
+ RTL8188E_TRANS_CARDEMU_TO_SUS_STEPS +
+ RTL8188E_TRANS_END_STEPS] = {
+ RTL8188E_TRANS_SUS_TO_CARDEMU
+ RTL8188E_TRANS_CARDEMU_TO_ACT
+ RTL8188E_TRANS_END
+};
+
+/* 3HWPDN Array */
+struct wl_pwr_cfg rtl8188E_hwpdn_flow[RTL8188E_TRANS_ACT_TO_CARDEMU_STEPS +
+ RTL8188E_TRANS_CARDEMU_TO_PDN_STEPS +
+ RTL8188E_TRANS_END_STEPS] = {
+ RTL8188E_TRANS_ACT_TO_CARDEMU
+ RTL8188E_TRANS_CARDEMU_TO_PDN
+ RTL8188E_TRANS_END
+};
+
+/* 3 Enter LPS */
+struct wl_pwr_cfg rtl8188E_enter_lps_flow[RTL8188E_TRANS_ACT_TO_LPS_STEPS +
+ RTL8188E_TRANS_END_STEPS] = {
+ /* FW behavior */
+ RTL8188E_TRANS_ACT_TO_LPS
+ RTL8188E_TRANS_END
+};
+
+/* 3 Leave LPS */
+struct wl_pwr_cfg rtl8188E_leave_lps_flow[RTL8188E_TRANS_LPS_TO_ACT_STEPS +
+ RTL8188E_TRANS_END_STEPS] = {
+ /* FW behavior */
+ RTL8188E_TRANS_LPS_TO_ACT
+ RTL8188E_TRANS_END
+};
diff --git a/drivers/staging/rtl8188eu/hal/pwrseqcmd.c b/drivers/staging/rtl8188eu/hal/pwrseqcmd.c
new file mode 100644
index 000000000..73e1f8b36
--- /dev/null
+++ b/drivers/staging/rtl8188eu/hal/pwrseqcmd.c
@@ -0,0 +1,122 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2011 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ ******************************************************************************/
+
+#include <pwrseqcmd.h>
+#include <usb_ops_linux.h>
+
+/* This routine deals with the Power Configuration CMDs parsing
+ * for RTL8723/RTL8188E Series IC.
+ */
+u8 rtl88eu_pwrseqcmdparsing(struct adapter *padapter, u8 cut_vers, u8 fab_vers,
+ u8 ifacetype, struct wl_pwr_cfg pwrseqcmd[])
+{
+ struct wl_pwr_cfg pwrcfgcmd = {0};
+ u8 poll_bit = false;
+ u32 aryidx = 0;
+ u8 value = 0;
+ u32 offset = 0;
+ u32 poll_count = 0; /* polling autoload done. */
+ u32 max_poll_count = 5000;
+
+ do {
+ pwrcfgcmd = pwrseqcmd[aryidx];
+
+ RT_TRACE(_module_hal_init_c_ , _drv_info_,
+ ("rtl88eu_pwrseqcmdparsing: offset(%#x) cut_msk(%#x)"
+ "fab_msk(%#x) interface_msk(%#x) base(%#x) cmd(%#x)"
+ "msk(%#x) value(%#x)\n",
+ GET_PWR_CFG_OFFSET(pwrcfgcmd),
+ GET_PWR_CFG_CUT_MASK(pwrcfgcmd),
+ GET_PWR_CFG_FAB_MASK(pwrcfgcmd),
+ GET_PWR_CFG_INTF_MASK(pwrcfgcmd),
+ GET_PWR_CFG_BASE(pwrcfgcmd),
+ GET_PWR_CFG_CMD(pwrcfgcmd),
+ GET_PWR_CFG_MASK(pwrcfgcmd),
+ GET_PWR_CFG_VALUE(pwrcfgcmd)));
+
+ /* Only Handle the command whose FAB, CUT, and Interface are matched */
+ if ((GET_PWR_CFG_FAB_MASK(pwrcfgcmd) & fab_vers) &&
+ (GET_PWR_CFG_CUT_MASK(pwrcfgcmd) & cut_vers) &&
+ (GET_PWR_CFG_INTF_MASK(pwrcfgcmd) & ifacetype)) {
+ switch (GET_PWR_CFG_CMD(pwrcfgcmd)) {
+ case PWR_CMD_READ:
+ RT_TRACE(_module_hal_init_c_, _drv_info_,
+ ("rtl88eu_pwrseqcmdparsing: PWR_CMD_READ\n"));
+ break;
+ case PWR_CMD_WRITE:
+ RT_TRACE(_module_hal_init_c_, _drv_info_,
+ ("rtl88eu_pwrseqcmdparsing: PWR_CMD_WRITE\n"));
+ offset = GET_PWR_CFG_OFFSET(pwrcfgcmd);
+
+ /* Read the value from system register */
+ value = usb_read8(padapter, offset);
+
+ value &= ~(GET_PWR_CFG_MASK(pwrcfgcmd));
+ value |= (GET_PWR_CFG_VALUE(pwrcfgcmd) &
+ GET_PWR_CFG_MASK(pwrcfgcmd));
+
+ /* Write the value back to system register */
+ usb_write8(padapter, offset, value);
+ break;
+ case PWR_CMD_POLLING:
+ RT_TRACE(_module_hal_init_c_, _drv_info_,
+ ("rtl88eu_pwrseqcmdparsing: PWR_CMD_POLLING\n"));
+
+ poll_bit = false;
+ offset = GET_PWR_CFG_OFFSET(pwrcfgcmd);
+ do {
+ value = usb_read8(padapter, offset);
+ value &= GET_PWR_CFG_MASK(pwrcfgcmd);
+
+ if (value == (GET_PWR_CFG_VALUE(pwrcfgcmd) &
+ GET_PWR_CFG_MASK(pwrcfgcmd)))
+ poll_bit = true;
+ else
+ udelay(10);
+
+ if (poll_count++ > max_poll_count) {
+ DBG_88E("Fail to polling Offset[%#x]\n", offset);
+ return false;
+ }
+ } while (!poll_bit);
+ break;
+ case PWR_CMD_DELAY:
+ RT_TRACE(_module_hal_init_c_, _drv_info_,
+ ("rtl88eu_pwrseqcmdparsing: PWR_CMD_DELAY\n"));
+ if (GET_PWR_CFG_VALUE(pwrcfgcmd) == PWRSEQ_DELAY_US)
+ udelay(GET_PWR_CFG_OFFSET(pwrcfgcmd));
+ else
+ udelay(GET_PWR_CFG_OFFSET(pwrcfgcmd)*1000);
+ break;
+ case PWR_CMD_END:
+ /* When this command is parsed, end the process */
+ RT_TRACE(_module_hal_init_c_, _drv_info_,
+ ("rtl88eu_pwrseqcmdparsing: PWR_CMD_END\n"));
+ return true;
+ default:
+ RT_TRACE(_module_hal_init_c_, _drv_err_,
+ ("rtl88eu_pwrseqcmdparsing: Unknown CMD!!\n"));
+ break;
+ }
+ }
+
+ aryidx++;/* Add Array Index */
+ } while (1);
+ return true;
+}
diff --git a/drivers/staging/rtl8188eu/hal/rf.c b/drivers/staging/rtl8188eu/hal/rf.c
new file mode 100644
index 000000000..097092772
--- /dev/null
+++ b/drivers/staging/rtl8188eu/hal/rf.c
@@ -0,0 +1,318 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2011 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ ******************************************************************************/
+
+#include <osdep_service.h>
+#include <drv_types.h>
+#include <phy.h>
+#include <rf.h>
+#include <rtl8188e_hal.h>
+
+void rtl88eu_phy_rf6052_set_bandwidth(struct adapter *adapt,
+ enum ht_channel_width bandwidth)
+{
+ struct hal_data_8188e *hal_data = GET_HAL_DATA(adapt);
+
+ switch (bandwidth) {
+ case HT_CHANNEL_WIDTH_20:
+ hal_data->RfRegChnlVal[0] = ((hal_data->RfRegChnlVal[0] &
+ 0xfffff3ff) | BIT(10) | BIT(11));
+ phy_set_rf_reg(adapt, RF_PATH_A, RF_CHNLBW, bRFRegOffsetMask,
+ hal_data->RfRegChnlVal[0]);
+ break;
+ case HT_CHANNEL_WIDTH_40:
+ hal_data->RfRegChnlVal[0] = ((hal_data->RfRegChnlVal[0] &
+ 0xfffff3ff) | BIT(10));
+ phy_set_rf_reg(adapt, RF_PATH_A, RF_CHNLBW, bRFRegOffsetMask,
+ hal_data->RfRegChnlVal[0]);
+ break;
+ default:
+ break;
+ }
+}
+
+void rtl88eu_phy_rf6052_set_cck_txpower(struct adapter *adapt, u8 *powerlevel)
+{
+ struct hal_data_8188e *hal_data = GET_HAL_DATA(adapt);
+ struct dm_priv *pdmpriv = &hal_data->dmpriv;
+ struct mlme_ext_priv *pmlmeext = &adapt->mlmeextpriv;
+ u32 tx_agc[2] = {0, 0}, tmpval = 0, pwrtrac_value;
+ u8 idx1, idx2;
+ u8 *ptr;
+ u8 direction;
+
+
+ if (pmlmeext->sitesurvey_res.state == SCAN_PROCESS) {
+ tx_agc[RF_PATH_A] = 0x3f3f3f3f;
+ tx_agc[RF_PATH_B] = 0x3f3f3f3f;
+ for (idx1 = RF_PATH_A; idx1 <= RF_PATH_B; idx1++) {
+ tx_agc[idx1] = powerlevel[idx1] |
+ (powerlevel[idx1]<<8) |
+ (powerlevel[idx1]<<16) |
+ (powerlevel[idx1]<<24);
+ if (tx_agc[idx1] > 0x20 && hal_data->ExternalPA)
+ tx_agc[idx1] = 0x20;
+ }
+ } else {
+ if (pdmpriv->DynamicTxHighPowerLvl == TxHighPwrLevel_Level1) {
+ tx_agc[RF_PATH_A] = 0x10101010;
+ tx_agc[RF_PATH_B] = 0x10101010;
+ } else if (pdmpriv->DynamicTxHighPowerLvl == TxHighPwrLevel_Level2) {
+ tx_agc[RF_PATH_A] = 0x00000000;
+ tx_agc[RF_PATH_B] = 0x00000000;
+ } else {
+ for (idx1 = RF_PATH_A; idx1 <= RF_PATH_B; idx1++) {
+ tx_agc[idx1] = powerlevel[idx1] |
+ (powerlevel[idx1]<<8) |
+ (powerlevel[idx1]<<16) |
+ (powerlevel[idx1]<<24);
+ }
+ if (hal_data->EEPROMRegulatory == 0) {
+ tmpval = hal_data->MCSTxPowerLevelOriginalOffset[0][6] +
+ (hal_data->MCSTxPowerLevelOriginalOffset[0][7]<<8);
+ tx_agc[RF_PATH_A] += tmpval;
+
+ tmpval = hal_data->MCSTxPowerLevelOriginalOffset[0][14] +
+ (hal_data->MCSTxPowerLevelOriginalOffset[0][15]<<24);
+ tx_agc[RF_PATH_B] += tmpval;
+ }
+ }
+ }
+ for (idx1 = RF_PATH_A; idx1 <= RF_PATH_B; idx1++) {
+ ptr = (u8 *)(&(tx_agc[idx1]));
+ for (idx2 = 0; idx2 < 4; idx2++) {
+ if (*ptr > RF6052_MAX_TX_PWR)
+ *ptr = RF6052_MAX_TX_PWR;
+ ptr++;
+ }
+ }
+ rtl88eu_dm_txpower_track_adjust(&hal_data->odmpriv, 1, &direction,
+ &pwrtrac_value);
+
+ if (direction == 1) {
+ /* Increase TX power */
+ tx_agc[0] += pwrtrac_value;
+ tx_agc[1] += pwrtrac_value;
+ } else if (direction == 2) {
+ /* Decrease TX power */
+ tx_agc[0] -= pwrtrac_value;
+ tx_agc[1] -= pwrtrac_value;
+ }
+
+ /* rf-A cck tx power */
+ tmpval = tx_agc[RF_PATH_A]&0xff;
+ phy_set_bb_reg(adapt, rTxAGC_A_CCK1_Mcs32, bMaskByte1, tmpval);
+ tmpval = tx_agc[RF_PATH_A]>>8;
+ phy_set_bb_reg(adapt, rTxAGC_B_CCK11_A_CCK2_11, 0xffffff00, tmpval);
+
+ /* rf-B cck tx power */
+ tmpval = tx_agc[RF_PATH_B]>>24;
+ phy_set_bb_reg(adapt, rTxAGC_B_CCK11_A_CCK2_11, bMaskByte0, tmpval);
+ tmpval = tx_agc[RF_PATH_B]&0x00ffffff;
+ phy_set_bb_reg(adapt, rTxAGC_B_CCK1_55_Mcs32, 0xffffff00, tmpval);
+}
+
+/* powerbase0 for OFDM rates */
+/* powerbase1 for HT MCS rates */
+static void getpowerbase88e(struct adapter *adapt, u8 *pwr_level_ofdm,
+ u8 *pwr_level_bw20, u8 *pwr_level_bw40,
+ u8 channel, u32 *ofdmbase, u32 *mcs_base)
+{
+ struct hal_data_8188e *hal_data = GET_HAL_DATA(adapt);
+ u32 powerbase0, powerbase1;
+ u8 i, powerlevel[2];
+
+ for (i = 0; i < 2; i++) {
+ powerbase0 = pwr_level_ofdm[i];
+
+ powerbase0 = (powerbase0<<24) | (powerbase0<<16) |
+ (powerbase0<<8) | powerbase0;
+ *(ofdmbase+i) = powerbase0;
+ }
+ for (i = 0; i < hal_data->NumTotalRFPath; i++) {
+ /* Check HT20 to HT40 diff */
+ if (hal_data->CurrentChannelBW == HT_CHANNEL_WIDTH_20)
+ powerlevel[i] = pwr_level_bw20[i];
+ else
+ powerlevel[i] = pwr_level_bw40[i];
+ powerbase1 = powerlevel[i];
+ powerbase1 = (powerbase1<<24) | (powerbase1<<16) |
+ (powerbase1<<8) | powerbase1;
+ *(mcs_base+i) = powerbase1;
+ }
+}
+static void get_rx_power_val_by_reg(struct adapter *adapt, u8 channel,
+ u8 index, u32 *powerbase0, u32 *powerbase1,
+ u32 *out_val)
+{
+ struct hal_data_8188e *hal_data = GET_HAL_DATA(adapt);
+ struct dm_priv *pdmpriv = &hal_data->dmpriv;
+ u8 i, chnlGroup = 0, pwr_diff_limit[4], customer_pwr_limit;
+ s8 pwr_diff = 0;
+ u32 write_val, customer_limit, rf;
+ u8 regulatory = hal_data->EEPROMRegulatory;
+
+ /* Index 0 & 1= legacy OFDM, 2-5=HT_MCS rate */
+
+ for (rf = 0; rf < 2; rf++) {
+ u8 j = index + (rf ? 8 : 0);
+
+ switch (regulatory) {
+ case 0:
+ chnlGroup = 0;
+ write_val = hal_data->MCSTxPowerLevelOriginalOffset[chnlGroup][index+(rf ? 8 : 0)] +
+ ((index < 2) ? powerbase0[rf] : powerbase1[rf]);
+ break;
+ case 1: /* Realtek regulatory */
+ /* increase power diff defined by Realtek for regulatory */
+ if (hal_data->pwrGroupCnt == 1)
+ chnlGroup = 0;
+ if (hal_data->pwrGroupCnt >= hal_data->PGMaxGroup) {
+ if (channel < 3)
+ chnlGroup = 0;
+ else if (channel < 6)
+ chnlGroup = 1;
+ else if (channel < 9)
+ chnlGroup = 2;
+ else if (channel < 12)
+ chnlGroup = 3;
+ else if (channel < 14)
+ chnlGroup = 4;
+ else if (channel == 14)
+ chnlGroup = 5;
+ }
+ write_val = hal_data->MCSTxPowerLevelOriginalOffset[chnlGroup][index+(rf ? 8 : 0)] +
+ ((index < 2) ? powerbase0[rf] : powerbase1[rf]);
+ break;
+ case 2: /* Better regulatory */
+ /* don't increase any power diff */
+ write_val = (index < 2) ? powerbase0[rf] : powerbase1[rf];
+ break;
+ case 3: /* Customer defined power diff. */
+ /* increase power diff defined by customer. */
+ chnlGroup = 0;
+
+ if (index < 2)
+ pwr_diff = hal_data->TxPwrLegacyHtDiff[rf][channel-1];
+ else if (hal_data->CurrentChannelBW == HT_CHANNEL_WIDTH_20)
+ pwr_diff = hal_data->TxPwrHt20Diff[rf][channel-1];
+
+ if (hal_data->CurrentChannelBW == HT_CHANNEL_WIDTH_40)
+ customer_pwr_limit = hal_data->PwrGroupHT40[rf][channel-1];
+ else
+ customer_pwr_limit = hal_data->PwrGroupHT20[rf][channel-1];
+
+ if (pwr_diff >= customer_pwr_limit)
+ pwr_diff = 0;
+ else
+ pwr_diff = customer_pwr_limit - pwr_diff;
+
+ for (i = 0; i < 4; i++) {
+ pwr_diff_limit[i] = (u8)((hal_data->MCSTxPowerLevelOriginalOffset[chnlGroup][j] &
+ (0x7f << (i * 8))) >> (i * 8));
+
+ if (pwr_diff_limit[i] > pwr_diff)
+ pwr_diff_limit[i] = pwr_diff;
+ }
+ customer_limit = (pwr_diff_limit[3]<<24) |
+ (pwr_diff_limit[2]<<16) |
+ (pwr_diff_limit[1]<<8) |
+ (pwr_diff_limit[0]);
+ write_val = customer_limit + ((index < 2) ? powerbase0[rf] : powerbase1[rf]);
+ break;
+ default:
+ chnlGroup = 0;
+ write_val = hal_data->MCSTxPowerLevelOriginalOffset[chnlGroup][j] +
+ ((index < 2) ? powerbase0[rf] : powerbase1[rf]);
+ break;
+ }
+/* 20100427 Joseph: Driver dynamic Tx power shall not affect Tx power. It shall be determined by power training mechanism. */
+/* Currently, we cannot fully disable driver dynamic tx power mechanism because it is referenced by BT coexist mechanism. */
+/* In the future, two mechanism shall be separated from each other and maintained independently. Thanks for Lanhsin's reminder. */
+ /* 92d do not need this */
+ if (pdmpriv->DynamicTxHighPowerLvl == TxHighPwrLevel_Level1)
+ write_val = 0x14141414;
+ else if (pdmpriv->DynamicTxHighPowerLvl == TxHighPwrLevel_Level2)
+ write_val = 0x00000000;
+
+ *(out_val+rf) = write_val;
+ }
+}
+
+static void write_ofdm_pwr_reg(struct adapter *adapt, u8 index, u32 *pvalue)
+{
+ u16 regoffset_a[6] = { rTxAGC_A_Rate18_06, rTxAGC_A_Rate54_24,
+ rTxAGC_A_Mcs03_Mcs00, rTxAGC_A_Mcs07_Mcs04,
+ rTxAGC_A_Mcs11_Mcs08, rTxAGC_A_Mcs15_Mcs12 };
+ u16 regoffset_b[6] = { rTxAGC_B_Rate18_06, rTxAGC_B_Rate54_24,
+ rTxAGC_B_Mcs03_Mcs00, rTxAGC_B_Mcs07_Mcs04,
+ rTxAGC_B_Mcs11_Mcs08, rTxAGC_B_Mcs15_Mcs12 };
+ u8 i, rf, pwr_val[4];
+ u32 write_val;
+ u16 regoffset;
+
+ for (rf = 0; rf < 2; rf++) {
+ write_val = pvalue[rf];
+ for (i = 0; i < 4; i++) {
+ pwr_val[i] = (u8)((write_val & (0x7f<<(i*8)))>>(i*8));
+ if (pwr_val[i] > RF6052_MAX_TX_PWR)
+ pwr_val[i] = RF6052_MAX_TX_PWR;
+ }
+ write_val = (pwr_val[3]<<24) | (pwr_val[2]<<16) |
+ (pwr_val[1]<<8) | pwr_val[0];
+
+ if (rf == 0)
+ regoffset = regoffset_a[index];
+ else
+ regoffset = regoffset_b[index];
+
+ phy_set_bb_reg(adapt, regoffset, bMaskDWord, write_val);
+ }
+}
+
+void rtl88eu_phy_rf6052_set_ofdm_txpower(struct adapter *adapt,
+ u8 *pwr_level_ofdm,
+ u8 *pwr_level_bw20,
+ u8 *pwr_level_bw40, u8 channel)
+{
+ struct hal_data_8188e *hal_data = GET_HAL_DATA(adapt);
+ u32 write_val[2], powerbase0[2], powerbase1[2], pwrtrac_value;
+ u8 direction;
+ u8 index = 0;
+
+ getpowerbase88e(adapt, pwr_level_ofdm, pwr_level_bw20, pwr_level_bw40,
+ channel, &powerbase0[0], &powerbase1[0]);
+
+ rtl88eu_dm_txpower_track_adjust(&hal_data->odmpriv, 0, &direction,
+ &pwrtrac_value);
+
+ for (index = 0; index < 6; index++) {
+ get_rx_power_val_by_reg(adapt, channel, index,
+ &powerbase0[0], &powerbase1[0],
+ &write_val[0]);
+
+ if (direction == 1) {
+ write_val[0] += pwrtrac_value;
+ write_val[1] += pwrtrac_value;
+ } else if (direction == 2) {
+ write_val[0] -= pwrtrac_value;
+ write_val[1] -= pwrtrac_value;
+ }
+ write_ofdm_pwr_reg(adapt, index, &write_val[0]);
+ }
+}
diff --git a/drivers/staging/rtl8188eu/hal/rf_cfg.c b/drivers/staging/rtl8188eu/hal/rf_cfg.c
new file mode 100644
index 000000000..455ecdc8d
--- /dev/null
+++ b/drivers/staging/rtl8188eu/hal/rf_cfg.c
@@ -0,0 +1,320 @@
+/******************************************************************************
+*
+* Copyright(c) 2007 - 2011 Realtek Corporation. All rights reserved.
+*
+* This program is free software; you can redistribute it and/or modify it
+* under the terms of version 2 of the GNU General Public License as
+* published by the Free Software Foundation.
+*
+* This program is distributed in the hope that it will be useful, but WITHOUT
+* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+* more details.
+*
+* You should have received a copy of the GNU General Public License along with
+* this program; if not, write to the Free Software Foundation, Inc.,
+* 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+*
+*
+******************************************************************************/
+
+#include "odm_precomp.h"
+
+#include <phy.h>
+
+static bool check_condition(struct adapter *adapt, const u32 condition)
+{
+ struct odm_dm_struct *odm = &GET_HAL_DATA(adapt)->odmpriv;
+ u32 _board = odm->BoardType;
+ u32 _platform = odm->SupportPlatform;
+ u32 _interface = odm->SupportInterface;
+ u32 cond = condition;
+
+ if (condition == 0xCDCDCDCD)
+ return true;
+
+ cond = condition & 0x000000FF;
+ if ((_board == cond) && cond != 0x00)
+ return false;
+
+ cond = condition & 0x0000FF00;
+ cond >>= 8;
+ if ((_interface & cond) == 0 && cond != 0x07)
+ return false;
+
+ cond = condition & 0x00FF0000;
+ cond >>= 16;
+ if ((_platform & cond) == 0 && cond != 0x0F)
+ return false;
+ return true;
+}
+
+/* RadioA_1T.TXT */
+
+static u32 Array_RadioA_1T_8188E[] = {
+ 0x000, 0x00030000,
+ 0x008, 0x00084000,
+ 0x018, 0x00000407,
+ 0x019, 0x00000012,
+ 0x01E, 0x00080009,
+ 0x01F, 0x00000880,
+ 0x02F, 0x0001A060,
+ 0x03F, 0x00000000,
+ 0x042, 0x000060C0,
+ 0x057, 0x000D0000,
+ 0x058, 0x000BE180,
+ 0x067, 0x00001552,
+ 0x083, 0x00000000,
+ 0x0B0, 0x000FF8FC,
+ 0x0B1, 0x00054400,
+ 0x0B2, 0x000CCC19,
+ 0x0B4, 0x00043003,
+ 0x0B6, 0x0004953E,
+ 0x0B7, 0x0001C718,
+ 0x0B8, 0x000060FF,
+ 0x0B9, 0x00080001,
+ 0x0BA, 0x00040000,
+ 0x0BB, 0x00000400,
+ 0x0BF, 0x000C0000,
+ 0x0C2, 0x00002400,
+ 0x0C3, 0x00000009,
+ 0x0C4, 0x00040C91,
+ 0x0C5, 0x00099999,
+ 0x0C6, 0x000000A3,
+ 0x0C7, 0x00088820,
+ 0x0C8, 0x00076C06,
+ 0x0C9, 0x00000000,
+ 0x0CA, 0x00080000,
+ 0x0DF, 0x00000180,
+ 0x0EF, 0x000001A0,
+ 0x051, 0x0006B27D,
+ 0xFF0F041F, 0xABCD,
+ 0x052, 0x0007E4DD,
+ 0xCDCDCDCD, 0xCDCD,
+ 0x052, 0x0007E49D,
+ 0xFF0F041F, 0xDEAD,
+ 0x053, 0x00000073,
+ 0x056, 0x00051FF3,
+ 0x035, 0x00000086,
+ 0x035, 0x00000186,
+ 0x035, 0x00000286,
+ 0x036, 0x00001C25,
+ 0x036, 0x00009C25,
+ 0x036, 0x00011C25,
+ 0x036, 0x00019C25,
+ 0x0B6, 0x00048538,
+ 0x018, 0x00000C07,
+ 0x05A, 0x0004BD00,
+ 0x019, 0x000739D0,
+ 0x034, 0x0000ADF3,
+ 0x034, 0x00009DF0,
+ 0x034, 0x00008DED,
+ 0x034, 0x00007DEA,
+ 0x034, 0x00006DE7,
+ 0x034, 0x000054EE,
+ 0x034, 0x000044EB,
+ 0x034, 0x000034E8,
+ 0x034, 0x0000246B,
+ 0x034, 0x00001468,
+ 0x034, 0x0000006D,
+ 0x000, 0x00030159,
+ 0x084, 0x00068200,
+ 0x086, 0x000000CE,
+ 0x087, 0x00048A00,
+ 0x08E, 0x00065540,
+ 0x08F, 0x00088000,
+ 0x0EF, 0x000020A0,
+ 0x03B, 0x000F02B0,
+ 0x03B, 0x000EF7B0,
+ 0x03B, 0x000D4FB0,
+ 0x03B, 0x000CF060,
+ 0x03B, 0x000B0090,
+ 0x03B, 0x000A0080,
+ 0x03B, 0x00090080,
+ 0x03B, 0x0008F780,
+ 0x03B, 0x000722B0,
+ 0x03B, 0x0006F7B0,
+ 0x03B, 0x00054FB0,
+ 0x03B, 0x0004F060,
+ 0x03B, 0x00030090,
+ 0x03B, 0x00020080,
+ 0x03B, 0x00010080,
+ 0x03B, 0x0000F780,
+ 0x0EF, 0x000000A0,
+ 0x000, 0x00010159,
+ 0x018, 0x0000F407,
+ 0xFFE, 0x00000000,
+ 0xFFE, 0x00000000,
+ 0x01F, 0x00080003,
+ 0xFFE, 0x00000000,
+ 0xFFE, 0x00000000,
+ 0x01E, 0x00000001,
+ 0x01F, 0x00080000,
+ 0x000, 0x00033E60,
+};
+
+#define READ_NEXT_PAIR(v1, v2, i) \
+do { \
+ i += 2; v1 = array[i]; \
+ v2 = array[i+1]; \
+} while (0)
+
+#define RFREG_OFFSET_MASK 0xfffff
+#define B3WIREADDREAALENGTH 0x400
+#define B3WIREDATALENGTH 0x800
+#define BRFSI_RFENV 0x10
+
+static void rtl_rfreg_delay(struct adapter *adapt, enum rf_radio_path rfpath, u32 addr, u32 mask, u32 data)
+{
+ if (addr == 0xfe) {
+ mdelay(50);
+ } else if (addr == 0xfd) {
+ mdelay(5);
+ } else if (addr == 0xfc) {
+ mdelay(1);
+ } else if (addr == 0xfb) {
+ udelay(50);
+ } else if (addr == 0xfa) {
+ udelay(5);
+ } else if (addr == 0xf9) {
+ udelay(1);
+ } else {
+ phy_set_rf_reg(adapt, rfpath, addr, mask, data);
+ udelay(1);
+ }
+}
+
+static void rtl8188e_config_rf_reg(struct adapter *adapt,
+ u32 addr, u32 data)
+{
+ u32 content = 0x1000; /*RF Content: radio_a_txt*/
+ u32 maskforphyset = (u32)(content & 0xE000);
+
+ rtl_rfreg_delay(adapt, RF90_PATH_A, addr | maskforphyset,
+ RFREG_OFFSET_MASK,
+ data);
+}
+
+static bool rtl88e_phy_config_rf_with_headerfile(struct adapter *adapt)
+{
+ u32 i;
+ u32 array_len = sizeof(Array_RadioA_1T_8188E)/sizeof(u32);
+ u32 *array = Array_RadioA_1T_8188E;
+
+ for (i = 0; i < array_len; i += 2) {
+ u32 v1 = array[i];
+ u32 v2 = array[i+1];
+
+ if (v1 < 0xCDCDCDCD) {
+ rtl8188e_config_rf_reg(adapt, v1, v2);
+ continue;
+ } else {
+ if (!check_condition(adapt, array[i])) {
+ READ_NEXT_PAIR(v1, v2, i);
+ while (v2 != 0xDEAD && v2 != 0xCDEF &&
+ v2 != 0xCDCD && i < array_len - 2)
+ READ_NEXT_PAIR(v1, v2, i);
+ i -= 2;
+ } else {
+ READ_NEXT_PAIR(v1, v2, i);
+ while (v2 != 0xDEAD && v2 != 0xCDEF &&
+ v2 != 0xCDCD && i < array_len - 2) {
+ rtl8188e_config_rf_reg(adapt, v1, v2);
+ READ_NEXT_PAIR(v1, v2, i);
+ }
+
+ while (v2 != 0xDEAD && i < array_len - 2)
+ READ_NEXT_PAIR(v1, v2, i);
+ }
+ }
+ }
+ return true;
+}
+
+static bool rf6052_conf_para(struct adapter *adapt)
+{
+ struct hal_data_8188e *hal_data = GET_HAL_DATA(adapt);
+ u32 u4val = 0;
+ u8 rfpath;
+ bool rtstatus = true;
+ struct bb_reg_def *pphyreg;
+
+ for (rfpath = 0; rfpath < hal_data->NumTotalRFPath; rfpath++) {
+ pphyreg = &hal_data->PHYRegDef[rfpath];
+
+ switch (rfpath) {
+ case RF90_PATH_A:
+ case RF90_PATH_C:
+ u4val = phy_query_bb_reg(adapt, pphyreg->rfintfs,
+ BRFSI_RFENV);
+ break;
+ case RF90_PATH_B:
+ case RF90_PATH_D:
+ u4val = phy_query_bb_reg(adapt, pphyreg->rfintfs,
+ BRFSI_RFENV << 16);
+ break;
+ }
+
+ phy_set_bb_reg(adapt, pphyreg->rfintfe, BRFSI_RFENV << 16, 0x1);
+ udelay(1);
+
+ phy_set_bb_reg(adapt, pphyreg->rfintfo, BRFSI_RFENV, 0x1);
+ udelay(1);
+
+ phy_set_bb_reg(adapt, pphyreg->rfHSSIPara2,
+ B3WIREADDREAALENGTH, 0x0);
+ udelay(1);
+
+ phy_set_bb_reg(adapt, pphyreg->rfHSSIPara2,
+ B3WIREDATALENGTH, 0x0);
+ udelay(1);
+
+ switch (rfpath) {
+ case RF90_PATH_A:
+ rtstatus = rtl88e_phy_config_rf_with_headerfile(adapt);
+ break;
+ case RF90_PATH_B:
+ rtstatus = rtl88e_phy_config_rf_with_headerfile(adapt);
+ break;
+ case RF90_PATH_C:
+ break;
+ case RF90_PATH_D:
+ break;
+ }
+
+ switch (rfpath) {
+ case RF90_PATH_A:
+ case RF90_PATH_C:
+ phy_set_bb_reg(adapt, pphyreg->rfintfs,
+ BRFSI_RFENV, u4val);
+ break;
+ case RF90_PATH_B:
+ case RF90_PATH_D:
+ phy_set_bb_reg(adapt, pphyreg->rfintfs,
+ BRFSI_RFENV << 16, u4val);
+ break;
+ }
+
+ if (rtstatus != true)
+ return false;
+ }
+
+ return rtstatus;
+}
+
+static bool rtl88e_phy_rf6052_config(struct adapter *adapt)
+{
+ struct hal_data_8188e *hal_data = GET_HAL_DATA(adapt);
+
+ if (hal_data->rf_type == RF_1T1R)
+ hal_data->NumTotalRFPath = 1;
+ else
+ hal_data->NumTotalRFPath = 2;
+
+ return rf6052_conf_para(adapt);
+}
+
+bool rtl88eu_phy_rf_config(struct adapter *adapt)
+{
+ return rtl88e_phy_rf6052_config(adapt);
+}
diff --git a/drivers/staging/rtl8188eu/hal/rtl8188e_cmd.c b/drivers/staging/rtl8188eu/hal/rtl8188e_cmd.c
new file mode 100644
index 000000000..86347f2cc
--- /dev/null
+++ b/drivers/staging/rtl8188eu/hal/rtl8188e_cmd.c
@@ -0,0 +1,674 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2011 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ *
+ ******************************************************************************/
+#define _RTL8188E_CMD_C_
+
+#include <osdep_service.h>
+#include <drv_types.h>
+#include <recv_osdep.h>
+#include <mlme_osdep.h>
+#include <rtw_ioctl_set.h>
+
+#include <rtl8188e_hal.h>
+
+#define RTL88E_MAX_H2C_BOX_NUMS 4
+#define RTL88E_MAX_CMD_LEN 7
+#define RTL88E_MESSAGE_BOX_SIZE 4
+#define RTL88E_EX_MESSAGE_BOX_SIZE 4
+
+static u8 _is_fw_read_cmd_down(struct adapter *adapt, u8 msgbox_num)
+{
+ u8 read_down = false;
+ int retry_cnts = 100;
+
+ u8 valid;
+
+ do {
+ valid = usb_read8(adapt, REG_HMETFR) & BIT(msgbox_num);
+ if (0 == valid)
+ read_down = true;
+ } while ((!read_down) && (retry_cnts--));
+
+ return read_down;
+}
+
+/*****************************************
+* H2C Msg format :
+* 0x1DF - 0x1D0
+*| 31 - 8 | 7-5 4 - 0 |
+*| h2c_msg |Class_ID CMD_ID |
+*
+* Extend 0x1FF - 0x1F0
+*|31 - 0 |
+*|ext_msg|
+******************************************/
+static s32 FillH2CCmd_88E(struct adapter *adapt, u8 ElementID, u32 CmdLen, u8 *pCmdBuffer)
+{
+ u8 bcmd_down = false;
+ s32 retry_cnts = 100;
+ u8 h2c_box_num;
+ u32 msgbox_addr;
+ u32 msgbox_ex_addr;
+ struct hal_data_8188e *haldata = GET_HAL_DATA(adapt);
+ u8 cmd_idx, ext_cmd_len;
+ u32 h2c_cmd = 0;
+ u32 h2c_cmd_ex = 0;
+ s32 ret = _FAIL;
+
+
+ if (!adapt->bFWReady) {
+ DBG_88E("FillH2CCmd_88E(): return H2C cmd because fw is not ready\n");
+ return ret;
+ }
+
+ if (!pCmdBuffer)
+ goto exit;
+ if (CmdLen > RTL88E_MAX_CMD_LEN)
+ goto exit;
+ if (adapt->bSurpriseRemoved)
+ goto exit;
+
+ /* pay attention to if race condition happened in H2C cmd setting. */
+ do {
+ h2c_box_num = haldata->LastHMEBoxNum;
+
+ if (!_is_fw_read_cmd_down(adapt, h2c_box_num)) {
+ DBG_88E(" fw read cmd failed...\n");
+ goto exit;
+ }
+
+ *(u8 *)(&h2c_cmd) = ElementID;
+
+ if (CmdLen <= 3) {
+ memcpy((u8 *)(&h2c_cmd)+1, pCmdBuffer, CmdLen);
+ } else {
+ memcpy((u8 *)(&h2c_cmd)+1, pCmdBuffer, 3);
+ ext_cmd_len = CmdLen-3;
+ memcpy((u8 *)(&h2c_cmd_ex), pCmdBuffer+3, ext_cmd_len);
+
+ /* Write Ext command */
+ msgbox_ex_addr = REG_HMEBOX_EXT_0 + (h2c_box_num * RTL88E_EX_MESSAGE_BOX_SIZE);
+ for (cmd_idx = 0; cmd_idx < ext_cmd_len; cmd_idx++) {
+ usb_write8(adapt, msgbox_ex_addr+cmd_idx, *((u8 *)(&h2c_cmd_ex)+cmd_idx));
+ }
+ }
+ /* Write command */
+ msgbox_addr = REG_HMEBOX_0 + (h2c_box_num * RTL88E_MESSAGE_BOX_SIZE);
+ for (cmd_idx = 0; cmd_idx < RTL88E_MESSAGE_BOX_SIZE; cmd_idx++) {
+ usb_write8(adapt, msgbox_addr+cmd_idx, *((u8 *)(&h2c_cmd)+cmd_idx));
+ }
+ bcmd_down = true;
+
+ haldata->LastHMEBoxNum = (h2c_box_num+1) % RTL88E_MAX_H2C_BOX_NUMS;
+
+ } while ((!bcmd_down) && (retry_cnts--));
+
+ ret = _SUCCESS;
+
+exit:
+
+
+ return ret;
+}
+
+u8 rtl8188e_set_raid_cmd(struct adapter *adapt, u32 mask)
+{
+ u8 buf[3];
+ u8 res = _SUCCESS;
+ struct hal_data_8188e *haldata = GET_HAL_DATA(adapt);
+
+ if (haldata->fw_ractrl) {
+
+ memset(buf, 0, 3);
+ put_unaligned_le32(mask, buf);
+
+ FillH2CCmd_88E(adapt, H2C_DM_MACID_CFG, 3, buf);
+ } else {
+ DBG_88E("==>%s fw dont support RA\n", __func__);
+ res = _FAIL;
+ }
+
+
+ return res;
+}
+
+/* bitmap[0:27] = tx_rate_bitmap */
+/* bitmap[28:31]= Rate Adaptive id */
+/* arg[0:4] = macid */
+/* arg[5] = Short GI */
+void rtl8188e_Add_RateATid(struct adapter *pAdapter, u32 bitmap, u8 arg, u8 rssi_level)
+{
+ struct hal_data_8188e *haldata = GET_HAL_DATA(pAdapter);
+
+ u8 macid, init_rate, raid, shortGIrate = false;
+
+ macid = arg&0x1f;
+
+ raid = (bitmap>>28) & 0x0f;
+ bitmap &= 0x0fffffff;
+
+ if (rssi_level != DM_RATR_STA_INIT)
+ bitmap = ODM_Get_Rate_Bitmap(&haldata->odmpriv, macid, bitmap, rssi_level);
+
+ bitmap |= ((raid<<28)&0xf0000000);
+
+ init_rate = get_highest_rate_idx(bitmap&0x0fffffff)&0x3f;
+
+ shortGIrate = (arg&BIT(5)) ? true : false;
+
+ if (shortGIrate)
+ init_rate |= BIT(6);
+
+ raid = (bitmap>>28) & 0x0f;
+
+ bitmap &= 0x0fffffff;
+
+ DBG_88E("%s=> mac_id:%d, raid:%d, ra_bitmap=0x%x, shortGIrate=0x%02x\n",
+ __func__, macid, raid, bitmap, shortGIrate);
+
+ ODM_RA_UpdateRateInfo_8188E(&(haldata->odmpriv), macid, raid, bitmap, shortGIrate);
+}
+
+void rtl8188e_set_FwPwrMode_cmd(struct adapter *adapt, u8 Mode)
+{
+ struct setpwrmode_parm H2CSetPwrMode;
+ struct pwrctrl_priv *pwrpriv = &adapt->pwrctrlpriv;
+ u8 RLBM = 0; /* 0:Min, 1:Max, 2:User define */
+
+ DBG_88E("%s: Mode=%d SmartPS=%d UAPSD=%d\n", __func__,
+ Mode, pwrpriv->smart_ps, adapt->registrypriv.uapsd_enable);
+
+ switch (Mode) {
+ case PS_MODE_ACTIVE:
+ H2CSetPwrMode.Mode = 0;
+ break;
+ case PS_MODE_MIN:
+ H2CSetPwrMode.Mode = 1;
+ break;
+ case PS_MODE_MAX:
+ RLBM = 1;
+ H2CSetPwrMode.Mode = 1;
+ break;
+ case PS_MODE_DTIM:
+ RLBM = 2;
+ H2CSetPwrMode.Mode = 1;
+ break;
+ case PS_MODE_UAPSD_WMM:
+ H2CSetPwrMode.Mode = 2;
+ break;
+ default:
+ H2CSetPwrMode.Mode = 0;
+ break;
+ }
+
+ H2CSetPwrMode.SmartPS_RLBM = (((pwrpriv->smart_ps<<4)&0xf0) | (RLBM & 0x0f));
+
+ H2CSetPwrMode.AwakeInterval = 1;
+
+ H2CSetPwrMode.bAllQueueUAPSD = adapt->registrypriv.uapsd_enable;
+
+ if (Mode > 0)
+ H2CSetPwrMode.PwrState = 0x00;/* AllON(0x0C), RFON(0x04), RFOFF(0x00) */
+ else
+ H2CSetPwrMode.PwrState = 0x0C;/* AllON(0x0C), RFON(0x04), RFOFF(0x00) */
+
+ FillH2CCmd_88E(adapt, H2C_PS_PWR_MODE, sizeof(H2CSetPwrMode), (u8 *)&H2CSetPwrMode);
+
+}
+
+void rtl8188e_set_FwMediaStatus_cmd(struct adapter *adapt, __le16 mstatus_rpt)
+{
+ u8 opmode, macid;
+ u16 mst_rpt = le16_to_cpu(mstatus_rpt);
+ opmode = (u8)mst_rpt;
+ macid = (u8)(mst_rpt >> 8);
+
+ DBG_88E("### %s: MStatus=%x MACID=%d\n", __func__, opmode, macid);
+ FillH2CCmd_88E(adapt, H2C_COM_MEDIA_STATUS_RPT, sizeof(mst_rpt), (u8 *)&mst_rpt);
+}
+
+static void ConstructBeacon(struct adapter *adapt, u8 *pframe, u32 *pLength)
+{
+ struct rtw_ieee80211_hdr *pwlanhdr;
+ __le16 *fctrl;
+ u32 rate_len, pktlen;
+ struct mlme_ext_priv *pmlmeext = &(adapt->mlmeextpriv);
+ struct mlme_ext_info *pmlmeinfo = &(pmlmeext->mlmext_info);
+ struct wlan_bssid_ex *cur_network = &(pmlmeinfo->network);
+ u8 bc_addr[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
+
+ pwlanhdr = (struct rtw_ieee80211_hdr *)pframe;
+
+ fctrl = &(pwlanhdr->frame_ctl);
+ *(fctrl) = 0;
+
+ memcpy(pwlanhdr->addr1, bc_addr, ETH_ALEN);
+ memcpy(pwlanhdr->addr2, myid(&(adapt->eeprompriv)), ETH_ALEN);
+ memcpy(pwlanhdr->addr3, cur_network->MacAddress, ETH_ALEN);
+
+ SetSeqNum(pwlanhdr, 0/*pmlmeext->mgnt_seq*/);
+ SetFrameSubType(pframe, WIFI_BEACON);
+
+ pframe += sizeof(struct rtw_ieee80211_hdr_3addr);
+ pktlen = sizeof(struct rtw_ieee80211_hdr_3addr);
+
+ /* timestamp will be inserted by hardware */
+ pframe += 8;
+ pktlen += 8;
+
+ /* beacon interval: 2 bytes */
+ memcpy(pframe, (unsigned char *)(rtw_get_beacon_interval_from_ie(cur_network->IEs)), 2);
+
+ pframe += 2;
+ pktlen += 2;
+
+ /* capability info: 2 bytes */
+ memcpy(pframe, (unsigned char *)(rtw_get_capability_from_ie(cur_network->IEs)), 2);
+
+ pframe += 2;
+ pktlen += 2;
+
+ if ((pmlmeinfo->state&0x03) == WIFI_FW_AP_STATE) {
+ pktlen += cur_network->IELength - sizeof(struct ndis_802_11_fixed_ie);
+ memcpy(pframe, cur_network->IEs+sizeof(struct ndis_802_11_fixed_ie), pktlen);
+
+ goto _ConstructBeacon;
+ }
+
+ /* below for ad-hoc mode */
+
+ /* SSID */
+ pframe = rtw_set_ie(pframe, _SSID_IE_, cur_network->Ssid.SsidLength, cur_network->Ssid.Ssid, &pktlen);
+
+ /* supported rates... */
+ rate_len = rtw_get_rateset_len(cur_network->SupportedRates);
+ pframe = rtw_set_ie(pframe, _SUPPORTEDRATES_IE_, ((rate_len > 8) ? 8 : rate_len), cur_network->SupportedRates, &pktlen);
+
+ /* DS parameter set */
+ pframe = rtw_set_ie(pframe, _DSSET_IE_, 1, (unsigned char *)&(cur_network->Configuration.DSConfig), &pktlen);
+
+ if ((pmlmeinfo->state&0x03) == WIFI_FW_ADHOC_STATE) {
+ u32 ATIMWindow;
+ /* IBSS Parameter Set... */
+ ATIMWindow = 0;
+ pframe = rtw_set_ie(pframe, _IBSS_PARA_IE_, 2, (unsigned char *)(&ATIMWindow), &pktlen);
+ }
+
+ /* todo: ERP IE */
+
+ /* EXTERNDED SUPPORTED RATE */
+ if (rate_len > 8)
+ pframe = rtw_set_ie(pframe, _EXT_SUPPORTEDRATES_IE_, (rate_len - 8), (cur_network->SupportedRates + 8), &pktlen);
+
+ /* todo:HT for adhoc */
+
+_ConstructBeacon:
+
+ if ((pktlen + TXDESC_SIZE) > 512) {
+ DBG_88E("beacon frame too large\n");
+ return;
+ }
+
+ *pLength = pktlen;
+}
+
+static void ConstructPSPoll(struct adapter *adapt, u8 *pframe, u32 *pLength)
+{
+ struct rtw_ieee80211_hdr *pwlanhdr;
+ struct mlme_ext_priv *pmlmeext = &(adapt->mlmeextpriv);
+ struct mlme_ext_info *pmlmeinfo = &(pmlmeext->mlmext_info);
+ __le16 *fctrl;
+ struct wlan_bssid_ex *pnetwork = &(pmlmeinfo->network);
+
+ pwlanhdr = (struct rtw_ieee80211_hdr *)pframe;
+
+ /* Frame control. */
+ fctrl = &(pwlanhdr->frame_ctl);
+ *(fctrl) = 0;
+ SetPwrMgt(fctrl);
+ SetFrameSubType(pframe, WIFI_PSPOLL);
+
+ /* AID. */
+ SetDuration(pframe, (pmlmeinfo->aid | 0xc000));
+
+ /* BSSID. */
+ memcpy(pwlanhdr->addr1, pnetwork->MacAddress, ETH_ALEN);
+
+ /* TA. */
+ memcpy(pwlanhdr->addr2, myid(&(adapt->eeprompriv)), ETH_ALEN);
+
+ *pLength = 16;
+}
+
+static void ConstructNullFunctionData(struct adapter *adapt, u8 *pframe,
+ u32 *pLength,
+ u8 *StaAddr,
+ u8 bQoS,
+ u8 AC,
+ u8 bEosp,
+ u8 bForcePowerSave)
+{
+ struct rtw_ieee80211_hdr *pwlanhdr;
+ __le16 *fctrl;
+ u32 pktlen;
+ struct mlme_priv *pmlmepriv = &adapt->mlmepriv;
+ struct wlan_network *cur_network = &pmlmepriv->cur_network;
+ struct mlme_ext_priv *pmlmeext = &(adapt->mlmeextpriv);
+ struct mlme_ext_info *pmlmeinfo = &(pmlmeext->mlmext_info);
+ struct wlan_bssid_ex *pnetwork = &(pmlmeinfo->network);
+
+ pwlanhdr = (struct rtw_ieee80211_hdr *)pframe;
+
+ fctrl = &pwlanhdr->frame_ctl;
+ *(fctrl) = 0;
+ if (bForcePowerSave)
+ SetPwrMgt(fctrl);
+
+ switch (cur_network->network.InfrastructureMode) {
+ case Ndis802_11Infrastructure:
+ SetToDs(fctrl);
+ memcpy(pwlanhdr->addr1, pnetwork->MacAddress, ETH_ALEN);
+ memcpy(pwlanhdr->addr2, myid(&(adapt->eeprompriv)), ETH_ALEN);
+ memcpy(pwlanhdr->addr3, StaAddr, ETH_ALEN);
+ break;
+ case Ndis802_11APMode:
+ SetFrDs(fctrl);
+ memcpy(pwlanhdr->addr1, StaAddr, ETH_ALEN);
+ memcpy(pwlanhdr->addr2, pnetwork->MacAddress, ETH_ALEN);
+ memcpy(pwlanhdr->addr3, myid(&(adapt->eeprompriv)), ETH_ALEN);
+ break;
+ case Ndis802_11IBSS:
+ default:
+ memcpy(pwlanhdr->addr1, StaAddr, ETH_ALEN);
+ memcpy(pwlanhdr->addr2, myid(&(adapt->eeprompriv)), ETH_ALEN);
+ memcpy(pwlanhdr->addr3, pnetwork->MacAddress, ETH_ALEN);
+ break;
+ }
+
+ SetSeqNum(pwlanhdr, 0);
+
+ if (bQoS) {
+ struct rtw_ieee80211_hdr_3addr_qos *pwlanqoshdr;
+
+ SetFrameSubType(pframe, WIFI_QOS_DATA_NULL);
+
+ pwlanqoshdr = (struct rtw_ieee80211_hdr_3addr_qos *)pframe;
+ SetPriority(&pwlanqoshdr->qc, AC);
+ SetEOSP(&pwlanqoshdr->qc, bEosp);
+
+ pktlen = sizeof(struct rtw_ieee80211_hdr_3addr_qos);
+ } else {
+ SetFrameSubType(pframe, WIFI_DATA_NULL);
+
+ pktlen = sizeof(struct rtw_ieee80211_hdr_3addr);
+ }
+
+ *pLength = pktlen;
+}
+
+static void ConstructProbeRsp(struct adapter *adapt, u8 *pframe, u32 *pLength, u8 *StaAddr, bool bHideSSID)
+{
+ struct rtw_ieee80211_hdr *pwlanhdr;
+ __le16 *fctrl;
+ u8 *mac, *bssid;
+ u32 pktlen;
+ struct mlme_ext_priv *pmlmeext = &(adapt->mlmeextpriv);
+ struct mlme_ext_info *pmlmeinfo = &(pmlmeext->mlmext_info);
+ struct wlan_bssid_ex *cur_network = &(pmlmeinfo->network);
+
+ pwlanhdr = (struct rtw_ieee80211_hdr *)pframe;
+
+ mac = myid(&(adapt->eeprompriv));
+ bssid = cur_network->MacAddress;
+
+ fctrl = &(pwlanhdr->frame_ctl);
+ *(fctrl) = 0;
+ memcpy(pwlanhdr->addr1, StaAddr, ETH_ALEN);
+ memcpy(pwlanhdr->addr2, mac, ETH_ALEN);
+ memcpy(pwlanhdr->addr3, bssid, ETH_ALEN);
+
+ SetSeqNum(pwlanhdr, 0);
+ SetFrameSubType(fctrl, WIFI_PROBERSP);
+
+ pktlen = sizeof(struct rtw_ieee80211_hdr_3addr);
+ pframe += pktlen;
+
+ if (cur_network->IELength > MAX_IE_SZ)
+ return;
+
+ memcpy(pframe, cur_network->IEs, cur_network->IELength);
+ pframe += cur_network->IELength;
+ pktlen += cur_network->IELength;
+
+ *pLength = pktlen;
+}
+
+/* */
+/* Description: Fill the reserved packets that FW will use to RSVD page. */
+/* Now we just send 4 types packet to rsvd page. */
+/* (1)Beacon, (2)Ps-poll, (3)Null data, (4)ProbeRsp. */
+/* Input: */
+/* bDLFinished - false: At the first time we will send all the packets as a large packet to Hw, */
+/* so we need to set the packet length to total length. */
+/* true: At the second time, we should send the first packet (default:beacon) */
+/* to Hw again and set the length in descriptor to the real beacon length. */
+/* 2009.10.15 by tynli. */
+static void SetFwRsvdPagePkt(struct adapter *adapt, bool bDLFinished)
+{
+ struct hal_data_8188e *haldata;
+ struct xmit_frame *pmgntframe;
+ struct pkt_attrib *pattrib;
+ struct xmit_priv *pxmitpriv;
+ struct mlme_ext_priv *pmlmeext;
+ struct mlme_ext_info *pmlmeinfo;
+ u32 BeaconLength = 0, ProbeRspLength = 0, PSPollLength;
+ u32 NullDataLength, QosNullLength;
+ u8 *ReservedPagePacket;
+ u8 PageNum, PageNeed, TxDescLen;
+ u16 BufIndex;
+ u32 TotalPacketLen;
+ struct rsvdpage_loc RsvdPageLoc;
+ struct wlan_bssid_ex *pnetwork;
+
+ DBG_88E("%s\n", __func__);
+ ReservedPagePacket = kzalloc(1000, GFP_KERNEL);
+ if (ReservedPagePacket == NULL) {
+ DBG_88E("%s: alloc ReservedPagePacket fail!\n", __func__);
+ return;
+ }
+
+ haldata = GET_HAL_DATA(adapt);
+ pxmitpriv = &adapt->xmitpriv;
+ pmlmeext = &adapt->mlmeextpriv;
+ pmlmeinfo = &pmlmeext->mlmext_info;
+ pnetwork = &(pmlmeinfo->network);
+
+ TxDescLen = TXDESC_SIZE;
+ PageNum = 0;
+
+ /* 3 (1) beacon * 2 pages */
+ BufIndex = TXDESC_OFFSET;
+ ConstructBeacon(adapt, &ReservedPagePacket[BufIndex], &BeaconLength);
+
+ /* When we count the first page size, we need to reserve description size for the RSVD */
+ /* packet, it will be filled in front of the packet in TXPKTBUF. */
+ PageNeed = (u8)PageNum_128(TxDescLen + BeaconLength);
+ /* To reserved 2 pages for beacon buffer. 2010.06.24. */
+ if (PageNeed == 1)
+ PageNeed += 1;
+ PageNum += PageNeed;
+ haldata->FwRsvdPageStartOffset = PageNum;
+
+ BufIndex += PageNeed*128;
+
+ /* 3 (2) ps-poll *1 page */
+ RsvdPageLoc.LocPsPoll = PageNum;
+ ConstructPSPoll(adapt, &ReservedPagePacket[BufIndex], &PSPollLength);
+ rtl8188e_fill_fake_txdesc(adapt, &ReservedPagePacket[BufIndex-TxDescLen], PSPollLength, true, false);
+
+ PageNeed = (u8)PageNum_128(TxDescLen + PSPollLength);
+ PageNum += PageNeed;
+
+ BufIndex += PageNeed*128;
+
+ /* 3 (3) null data * 1 page */
+ RsvdPageLoc.LocNullData = PageNum;
+ ConstructNullFunctionData(adapt, &ReservedPagePacket[BufIndex], &NullDataLength, pnetwork->MacAddress, false, 0, 0, false);
+ rtl8188e_fill_fake_txdesc(adapt, &ReservedPagePacket[BufIndex-TxDescLen], NullDataLength, false, false);
+
+ PageNeed = (u8)PageNum_128(TxDescLen + NullDataLength);
+ PageNum += PageNeed;
+
+ BufIndex += PageNeed*128;
+
+ /* 3 (4) probe response * 1page */
+ RsvdPageLoc.LocProbeRsp = PageNum;
+ ConstructProbeRsp(adapt, &ReservedPagePacket[BufIndex], &ProbeRspLength, pnetwork->MacAddress, false);
+ rtl8188e_fill_fake_txdesc(adapt, &ReservedPagePacket[BufIndex-TxDescLen], ProbeRspLength, false, false);
+
+ PageNeed = (u8)PageNum_128(TxDescLen + ProbeRspLength);
+ PageNum += PageNeed;
+
+ BufIndex += PageNeed*128;
+
+ /* 3 (5) Qos null data */
+ RsvdPageLoc.LocQosNull = PageNum;
+ ConstructNullFunctionData(adapt, &ReservedPagePacket[BufIndex],
+ &QosNullLength, pnetwork->MacAddress, true, 0, 0, false);
+ rtl8188e_fill_fake_txdesc(adapt, &ReservedPagePacket[BufIndex-TxDescLen], QosNullLength, false, false);
+
+ PageNeed = (u8)PageNum_128(TxDescLen + QosNullLength);
+ PageNum += PageNeed;
+
+ TotalPacketLen = BufIndex + QosNullLength;
+ pmgntframe = alloc_mgtxmitframe(pxmitpriv);
+ if (pmgntframe == NULL)
+ goto exit;
+
+ /* update attribute */
+ pattrib = &pmgntframe->attrib;
+ update_mgntframe_attrib(adapt, pattrib);
+ pattrib->qsel = 0x10;
+ pattrib->last_txcmdsz = TotalPacketLen - TXDESC_OFFSET;
+ pattrib->pktlen = pattrib->last_txcmdsz;
+ memcpy(pmgntframe->buf_addr, ReservedPagePacket, TotalPacketLen);
+
+ rtw_hal_mgnt_xmit(adapt, pmgntframe);
+
+ DBG_88E("%s: Set RSVD page location to Fw\n", __func__);
+ FillH2CCmd_88E(adapt, H2C_COM_RSVD_PAGE, sizeof(RsvdPageLoc), (u8 *)&RsvdPageLoc);
+
+exit:
+ kfree(ReservedPagePacket);
+}
+
+void rtl8188e_set_FwJoinBssReport_cmd(struct adapter *adapt, u8 mstatus)
+{
+ struct hal_data_8188e *haldata = GET_HAL_DATA(adapt);
+ struct mlme_ext_priv *pmlmeext = &(adapt->mlmeextpriv);
+ struct mlme_ext_info *pmlmeinfo = &(pmlmeext->mlmext_info);
+ bool bSendBeacon = false;
+ bool bcn_valid = false;
+ u8 DLBcnCount = 0;
+ u32 poll = 0;
+
+
+ DBG_88E("%s mstatus(%x)\n", __func__, mstatus);
+
+ if (mstatus == 1) {
+ /* We should set AID, correct TSF, HW seq enable before set JoinBssReport to Fw in 88/92C. */
+ /* Suggested by filen. Added by tynli. */
+ usb_write16(adapt, REG_BCN_PSR_RPT, (0xC000|pmlmeinfo->aid));
+ /* Do not set TSF again here or vWiFi beacon DMA INT will not work. */
+
+ /* Set REG_CR bit 8. DMA beacon by SW. */
+ haldata->RegCR_1 |= BIT0;
+ usb_write8(adapt, REG_CR+1, haldata->RegCR_1);
+
+ /* Disable Hw protection for a time which revserd for Hw sending beacon. */
+ /* Fix download reserved page packet fail that access collision with the protection time. */
+ /* 2010.05.11. Added by tynli. */
+ usb_write8(adapt, REG_BCN_CTRL, usb_read8(adapt, REG_BCN_CTRL)&(~BIT(3)));
+ usb_write8(adapt, REG_BCN_CTRL, usb_read8(adapt, REG_BCN_CTRL)|BIT(4));
+
+ if (haldata->RegFwHwTxQCtrl&BIT6) {
+ DBG_88E("HalDownloadRSVDPage(): There is an Adapter is sending beacon.\n");
+ bSendBeacon = true;
+ }
+
+ /* Set FWHW_TXQ_CTRL 0x422[6]=0 to tell Hw the packet is not a real beacon frame. */
+ usb_write8(adapt, REG_FWHW_TXQ_CTRL+2, (haldata->RegFwHwTxQCtrl&(~BIT6)));
+ haldata->RegFwHwTxQCtrl &= (~BIT6);
+
+ /* Clear beacon valid check bit. */
+ rtw_hal_set_hwreg(adapt, HW_VAR_BCN_VALID, NULL);
+ DLBcnCount = 0;
+ poll = 0;
+ do {
+ /* download rsvd page. */
+ SetFwRsvdPagePkt(adapt, false);
+ DLBcnCount++;
+ do {
+ yield();
+ /* mdelay(10); */
+ /* check rsvd page download OK. */
+ rtw_hal_get_hwreg(adapt, HW_VAR_BCN_VALID, (u8 *)(&bcn_valid));
+ poll++;
+ } while (!bcn_valid && (poll%10) != 0 && !adapt->bSurpriseRemoved && !adapt->bDriverStopped);
+ } while (!bcn_valid && DLBcnCount <= 100 && !adapt->bSurpriseRemoved && !adapt->bDriverStopped);
+
+ if (adapt->bSurpriseRemoved || adapt->bDriverStopped)
+ ;
+ else if (!bcn_valid)
+ DBG_88E("%s: 1 Download RSVD page failed! DLBcnCount:%u, poll:%u\n", __func__, DLBcnCount, poll);
+ else
+ DBG_88E("%s: 1 Download RSVD success! DLBcnCount:%u, poll:%u\n", __func__, DLBcnCount, poll);
+ /* */
+ /* We just can send the reserved page twice during the time that Tx thread is stopped (e.g. pnpsetpower) */
+ /* because we need to free the Tx BCN Desc which is used by the first reserved page packet. */
+ /* At run time, we cannot get the Tx Desc until it is released in TxHandleInterrupt() so we will return */
+ /* the beacon TCB in the following code. 2011.11.23. by tynli. */
+ /* */
+
+ /* Enable Bcn */
+ usb_write8(adapt, REG_BCN_CTRL, usb_read8(adapt, REG_BCN_CTRL)|BIT(3));
+ usb_write8(adapt, REG_BCN_CTRL, usb_read8(adapt, REG_BCN_CTRL)&(~BIT(4)));
+
+ /* To make sure that if there exists an adapter which would like to send beacon. */
+ /* If exists, the origianl value of 0x422[6] will be 1, we should check this to */
+ /* prevent from setting 0x422[6] to 0 after download reserved page, or it will cause */
+ /* the beacon cannot be sent by HW. */
+ /* 2010.06.23. Added by tynli. */
+ if (bSendBeacon) {
+ usb_write8(adapt, REG_FWHW_TXQ_CTRL+2, (haldata->RegFwHwTxQCtrl|BIT6));
+ haldata->RegFwHwTxQCtrl |= BIT6;
+ }
+
+ /* Update RSVD page location H2C to Fw. */
+ if (bcn_valid) {
+ rtw_hal_set_hwreg(adapt, HW_VAR_BCN_VALID, NULL);
+ DBG_88E("Set RSVD page location to Fw.\n");
+ }
+
+ /* Do not enable HW DMA BCN or it will cause Pcie interface hang by timing issue. 2011.11.24. by tynli. */
+ /* Clear CR[8] or beacon packet will not be send to TxBuf anymore. */
+ haldata->RegCR_1 &= (~BIT0);
+ usb_write8(adapt, REG_CR+1, haldata->RegCR_1);
+ }
+}
diff --git a/drivers/staging/rtl8188eu/hal/rtl8188e_dm.c b/drivers/staging/rtl8188eu/hal/rtl8188e_dm.c
new file mode 100644
index 000000000..01566210b
--- /dev/null
+++ b/drivers/staging/rtl8188eu/hal/rtl8188e_dm.c
@@ -0,0 +1,247 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2011 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ *
+ ******************************************************************************/
+/* */
+/* Description: */
+/* */
+/* This file is for 92CE/92CU dynamic mechanism only */
+/* */
+/* */
+/* */
+#define _RTL8188E_DM_C_
+
+#include <osdep_service.h>
+#include <drv_types.h>
+
+#include <rtl8188e_hal.h>
+
+/* Initialize GPIO setting registers */
+static void dm_InitGPIOSetting(struct adapter *Adapter)
+{
+ u8 tmp1byte;
+
+ tmp1byte = usb_read8(Adapter, REG_GPIO_MUXCFG);
+ tmp1byte &= (GPIOSEL_GPIO | ~GPIOSEL_ENBT);
+
+ usb_write8(Adapter, REG_GPIO_MUXCFG, tmp1byte);
+}
+
+/* */
+/* functions */
+/* */
+static void Init_ODM_ComInfo_88E(struct adapter *Adapter)
+{
+ struct hal_data_8188e *hal_data = GET_HAL_DATA(Adapter);
+ struct dm_priv *pdmpriv = &hal_data->dmpriv;
+ struct odm_dm_struct *dm_odm = &(hal_data->odmpriv);
+ u8 cut_ver, fab_ver;
+
+ /* Init Value */
+ memset(dm_odm, 0, sizeof(*dm_odm));
+
+ dm_odm->Adapter = Adapter;
+
+ ODM_CmnInfoInit(dm_odm, ODM_CMNINFO_PLATFORM, ODM_CE);
+
+ ODM_CmnInfoInit(dm_odm, ODM_CMNINFO_IC_TYPE, ODM_RTL8188E);
+
+ fab_ver = ODM_TSMC;
+ cut_ver = ODM_CUT_A;
+
+ ODM_CmnInfoInit(dm_odm, ODM_CMNINFO_FAB_VER, fab_ver);
+ ODM_CmnInfoInit(dm_odm, ODM_CMNINFO_CUT_VER, cut_ver);
+
+ ODM_CmnInfoInit(dm_odm, ODM_CMNINFO_MP_TEST_CHIP, IS_NORMAL_CHIP(hal_data->VersionID));
+
+ ODM_CmnInfoInit(dm_odm, ODM_CMNINFO_PATCH_ID, hal_data->CustomerID);
+ ODM_CmnInfoInit(dm_odm, ODM_CMNINFO_BWIFI_TEST, Adapter->registrypriv.wifi_spec);
+
+
+ if (hal_data->rf_type == RF_1T1R)
+ ODM_CmnInfoUpdate(dm_odm, ODM_CMNINFO_RF_TYPE, ODM_1T1R);
+ else if (hal_data->rf_type == RF_2T2R)
+ ODM_CmnInfoUpdate(dm_odm, ODM_CMNINFO_RF_TYPE, ODM_2T2R);
+ else if (hal_data->rf_type == RF_1T2R)
+ ODM_CmnInfoUpdate(dm_odm, ODM_CMNINFO_RF_TYPE, ODM_1T2R);
+
+ ODM_CmnInfoInit(dm_odm, ODM_CMNINFO_RF_ANTENNA_TYPE, hal_data->TRxAntDivType);
+
+ pdmpriv->InitODMFlag = ODM_RF_CALIBRATION |
+ ODM_RF_TX_PWR_TRACK;
+
+ ODM_CmnInfoUpdate(dm_odm, ODM_CMNINFO_ABILITY, pdmpriv->InitODMFlag);
+}
+
+static void Update_ODM_ComInfo_88E(struct adapter *Adapter)
+{
+ struct mlme_ext_priv *pmlmeext = &Adapter->mlmeextpriv;
+ struct mlme_priv *pmlmepriv = &Adapter->mlmepriv;
+ struct pwrctrl_priv *pwrctrlpriv = &Adapter->pwrctrlpriv;
+ struct hal_data_8188e *hal_data = GET_HAL_DATA(Adapter);
+ struct odm_dm_struct *dm_odm = &(hal_data->odmpriv);
+ struct dm_priv *pdmpriv = &hal_data->dmpriv;
+ int i;
+
+ pdmpriv->InitODMFlag = ODM_BB_DIG |
+ ODM_BB_RA_MASK |
+ ODM_BB_DYNAMIC_TXPWR |
+ ODM_BB_FA_CNT |
+ ODM_BB_RSSI_MONITOR |
+ ODM_BB_CCK_PD |
+ ODM_BB_PWR_SAVE |
+ ODM_MAC_EDCA_TURBO |
+ ODM_RF_CALIBRATION |
+ ODM_RF_TX_PWR_TRACK;
+ if (hal_data->AntDivCfg)
+ pdmpriv->InitODMFlag |= ODM_BB_ANT_DIV;
+
+ if (Adapter->registrypriv.mp_mode == 1) {
+ pdmpriv->InitODMFlag = ODM_RF_CALIBRATION |
+ ODM_RF_TX_PWR_TRACK;
+ }
+
+ ODM_CmnInfoUpdate(dm_odm, ODM_CMNINFO_ABILITY, pdmpriv->InitODMFlag);
+
+ ODM_CmnInfoHook(dm_odm, ODM_CMNINFO_TX_UNI, &(Adapter->xmitpriv.tx_bytes));
+ ODM_CmnInfoHook(dm_odm, ODM_CMNINFO_RX_UNI, &(Adapter->recvpriv.rx_bytes));
+ ODM_CmnInfoHook(dm_odm, ODM_CMNINFO_WM_MODE, &(pmlmeext->cur_wireless_mode));
+ ODM_CmnInfoHook(dm_odm, ODM_CMNINFO_SEC_CHNL_OFFSET, &(hal_data->nCur40MhzPrimeSC));
+ ODM_CmnInfoHook(dm_odm, ODM_CMNINFO_SEC_MODE, &(Adapter->securitypriv.dot11PrivacyAlgrthm));
+ ODM_CmnInfoHook(dm_odm, ODM_CMNINFO_BW, &(hal_data->CurrentChannelBW));
+ ODM_CmnInfoHook(dm_odm, ODM_CMNINFO_CHNL, &(hal_data->CurrentChannel));
+ ODM_CmnInfoHook(dm_odm, ODM_CMNINFO_NET_CLOSED, &(Adapter->net_closed));
+ ODM_CmnInfoHook(dm_odm, ODM_CMNINFO_MP_MODE, &(Adapter->registrypriv.mp_mode));
+ ODM_CmnInfoHook(dm_odm, ODM_CMNINFO_SCAN, &(pmlmepriv->bScanInProcess));
+ ODM_CmnInfoHook(dm_odm, ODM_CMNINFO_POWER_SAVING, &(pwrctrlpriv->bpower_saving));
+ ODM_CmnInfoInit(dm_odm, ODM_CMNINFO_RF_ANTENNA_TYPE, hal_data->TRxAntDivType);
+
+ for (i = 0; i < NUM_STA; i++)
+ ODM_CmnInfoPtrArrayHook(dm_odm, ODM_CMNINFO_STA_STATUS, i, NULL);
+}
+
+void rtl8188e_InitHalDm(struct adapter *Adapter)
+{
+ struct hal_data_8188e *hal_data = GET_HAL_DATA(Adapter);
+ struct dm_priv *pdmpriv = &hal_data->dmpriv;
+ struct odm_dm_struct *dm_odm = &(hal_data->odmpriv);
+
+ dm_InitGPIOSetting(Adapter);
+ pdmpriv->DM_Type = DM_Type_ByDriver;
+ pdmpriv->DMFlag = DYNAMIC_FUNC_DISABLE;
+ Update_ODM_ComInfo_88E(Adapter);
+ ODM_DMInit(dm_odm);
+ Adapter->fix_rate = 0xFF;
+}
+
+void rtl8188e_HalDmWatchDog(struct adapter *Adapter)
+{
+ bool fw_cur_in_ps = false;
+ bool fw_ps_awake = true;
+ u8 hw_init_completed = false;
+ struct hal_data_8188e *hal_data = GET_HAL_DATA(Adapter);
+ struct mlme_priv *pmlmepriv = NULL;
+ u8 bLinked = false;
+
+ hw_init_completed = Adapter->hw_init_completed;
+
+ if (!hw_init_completed)
+ goto skip_dm;
+
+ fw_cur_in_ps = Adapter->pwrctrlpriv.bFwCurrentInPSMode;
+ rtw_hal_get_hwreg(Adapter, HW_VAR_FWLPS_RF_ON, (u8 *)(&fw_ps_awake));
+
+ /* Fw is under p2p powersaving mode, driver should stop dynamic mechanism. */
+ /* modifed by thomas. 2011.06.11. */
+ if (Adapter->wdinfo.p2p_ps_mode)
+ fw_ps_awake = false;
+
+ /* ODM */
+ pmlmepriv = &Adapter->mlmepriv;
+
+ if ((check_fwstate(pmlmepriv, WIFI_AP_STATE)) ||
+ (check_fwstate(pmlmepriv, WIFI_ADHOC_STATE |
+ WIFI_ADHOC_MASTER_STATE))) {
+ if (Adapter->stapriv.asoc_sta_count > 2)
+ bLinked = true;
+ } else {/* Station mode */
+ if (check_fwstate(pmlmepriv, _FW_LINKED))
+ bLinked = true;
+ }
+
+ ODM_CmnInfoUpdate(&hal_data->odmpriv, ODM_CMNINFO_LINK, bLinked);
+ ODM_DMWatchdog(&hal_data->odmpriv);
+skip_dm:
+ /* Check GPIO to determine current RF on/off and Pbc status. */
+ /* Check Hardware Radio ON/OFF or not */
+ return;
+}
+
+void rtl8188e_init_dm_priv(struct adapter *Adapter)
+{
+ struct hal_data_8188e *hal_data = GET_HAL_DATA(Adapter);
+ struct dm_priv *pdmpriv = &hal_data->dmpriv;
+ struct odm_dm_struct *podmpriv = &hal_data->odmpriv;
+
+ memset(pdmpriv, 0, sizeof(struct dm_priv));
+ Init_ODM_ComInfo_88E(Adapter);
+ ODM_InitDebugSetting(podmpriv);
+}
+
+/* Add new function to reset the state of antenna diversity before link. */
+/* Compare RSSI for deciding antenna */
+void AntDivCompare8188E(struct adapter *Adapter, struct wlan_bssid_ex *dst, struct wlan_bssid_ex *src)
+{
+ struct hal_data_8188e *hal_data = GET_HAL_DATA(Adapter);
+
+ if (0 != hal_data->AntDivCfg) {
+ /* select optimum_antenna for before linked =>For antenna diversity */
+ if (dst->Rssi >= src->Rssi) {/* keep org parameter */
+ src->Rssi = dst->Rssi;
+ src->PhyInfo.Optimum_antenna = dst->PhyInfo.Optimum_antenna;
+ }
+ }
+}
+
+/* Add new function to reset the state of antenna diversity before link. */
+u8 AntDivBeforeLink8188E(struct adapter *Adapter)
+{
+ struct hal_data_8188e *hal_data = GET_HAL_DATA(Adapter);
+ struct odm_dm_struct *dm_odm = &hal_data->odmpriv;
+ struct sw_ant_switch *dm_swat_tbl = &dm_odm->DM_SWAT_Table;
+ struct mlme_priv *pmlmepriv = &(Adapter->mlmepriv);
+
+ /* Condition that does not need to use antenna diversity. */
+ if (hal_data->AntDivCfg == 0)
+ return false;
+
+ if (check_fwstate(pmlmepriv, _FW_LINKED))
+ return false;
+
+ if (dm_swat_tbl->SWAS_NoLink_State == 0) {
+ /* switch channel */
+ dm_swat_tbl->SWAS_NoLink_State = 1;
+ dm_swat_tbl->CurAntenna = (dm_swat_tbl->CurAntenna == Antenna_A) ? Antenna_B : Antenna_A;
+
+ rtw_antenna_select_cmd(Adapter, dm_swat_tbl->CurAntenna, false);
+ return true;
+ } else {
+ dm_swat_tbl->SWAS_NoLink_State = 0;
+ return false;
+ }
+}
diff --git a/drivers/staging/rtl8188eu/hal/rtl8188e_hal_init.c b/drivers/staging/rtl8188eu/hal/rtl8188e_hal_init.c
new file mode 100644
index 000000000..7904d2260
--- /dev/null
+++ b/drivers/staging/rtl8188eu/hal/rtl8188e_hal_init.c
@@ -0,0 +1,694 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2011 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ *
+ ******************************************************************************/
+#define _HAL_INIT_C_
+
+#include <linux/firmware.h>
+#include <linux/vmalloc.h>
+#include <drv_types.h>
+#include <rtw_efuse.h>
+#include <phy.h>
+#include <rtl8188e_hal.h>
+
+#include <rtw_iol.h>
+
+void iol_mode_enable(struct adapter *padapter, u8 enable)
+{
+ u8 reg_0xf0 = 0;
+
+ if (enable) {
+ /* Enable initial offload */
+ reg_0xf0 = usb_read8(padapter, REG_SYS_CFG);
+ usb_write8(padapter, REG_SYS_CFG, reg_0xf0|SW_OFFLOAD_EN);
+
+ if (!padapter->bFWReady) {
+ DBG_88E("bFWReady == false call reset 8051...\n");
+ _8051Reset88E(padapter);
+ }
+
+ } else {
+ /* disable initial offload */
+ reg_0xf0 = usb_read8(padapter, REG_SYS_CFG);
+ usb_write8(padapter, REG_SYS_CFG, reg_0xf0 & ~SW_OFFLOAD_EN);
+ }
+}
+
+s32 iol_execute(struct adapter *padapter, u8 control)
+{
+ s32 status = _FAIL;
+ u8 reg_0x88 = 0;
+ u32 start = 0, passing_time = 0;
+
+ control = control&0x0f;
+ reg_0x88 = usb_read8(padapter, REG_HMEBOX_E0);
+ usb_write8(padapter, REG_HMEBOX_E0, reg_0x88|control);
+
+ start = jiffies;
+ while ((reg_0x88 = usb_read8(padapter, REG_HMEBOX_E0)) & control &&
+ (passing_time = rtw_get_passing_time_ms(start)) < 1000) {
+ ;
+ }
+
+ reg_0x88 = usb_read8(padapter, REG_HMEBOX_E0);
+ status = (reg_0x88 & control) ? _FAIL : _SUCCESS;
+ if (reg_0x88 & control<<4)
+ status = _FAIL;
+ return status;
+}
+
+static s32 iol_InitLLTTable(struct adapter *padapter, u8 txpktbuf_bndy)
+{
+ s32 rst = _SUCCESS;
+ iol_mode_enable(padapter, 1);
+ usb_write8(padapter, REG_TDECTRL+1, txpktbuf_bndy);
+ rst = iol_execute(padapter, CMD_INIT_LLT);
+ iol_mode_enable(padapter, 0);
+ return rst;
+}
+
+
+s32 rtl8188e_iol_efuse_patch(struct adapter *padapter)
+{
+ s32 result = _SUCCESS;
+
+ DBG_88E("==> %s\n", __func__);
+ if (rtw_IOL_applied(padapter)) {
+ iol_mode_enable(padapter, 1);
+ result = iol_execute(padapter, CMD_READ_EFUSE_MAP);
+ if (result == _SUCCESS)
+ result = iol_execute(padapter, CMD_EFUSE_PATCH);
+
+ iol_mode_enable(padapter, 0);
+ }
+ return result;
+}
+
+#define MAX_REG_BOLCK_SIZE 196
+
+void _8051Reset88E(struct adapter *padapter)
+{
+ u8 u1bTmp;
+
+ u1bTmp = usb_read8(padapter, REG_SYS_FUNC_EN+1);
+ usb_write8(padapter, REG_SYS_FUNC_EN+1, u1bTmp&(~BIT2));
+ usb_write8(padapter, REG_SYS_FUNC_EN+1, u1bTmp|(BIT2));
+ DBG_88E("=====> _8051Reset88E(): 8051 reset success .\n");
+}
+
+void rtl8188e_InitializeFirmwareVars(struct adapter *padapter)
+{
+ struct hal_data_8188e *pHalData = GET_HAL_DATA(padapter);
+
+ /* Init Fw LPS related. */
+ padapter->pwrctrlpriv.bFwCurrentInPSMode = false;
+
+ /* Init H2C counter. by tynli. 2009.12.09. */
+ pHalData->LastHMEBoxNum = 0;
+}
+
+static void rtl8188e_free_hal_data(struct adapter *padapter)
+{
+ kfree(padapter->HalData);
+ padapter->HalData = NULL;
+}
+
+static struct HAL_VERSION ReadChipVersion8188E(struct adapter *padapter)
+{
+ u32 value32;
+ struct HAL_VERSION ChipVersion;
+ struct hal_data_8188e *pHalData;
+
+ pHalData = GET_HAL_DATA(padapter);
+
+ value32 = usb_read32(padapter, REG_SYS_CFG);
+ ChipVersion.ICType = CHIP_8188E;
+ ChipVersion.ChipType = ((value32 & RTL_ID) ? TEST_CHIP : NORMAL_CHIP);
+
+ ChipVersion.RFType = RF_TYPE_1T1R;
+ ChipVersion.VendorType = ((value32 & VENDOR_ID) ? CHIP_VENDOR_UMC : CHIP_VENDOR_TSMC);
+ ChipVersion.CUTVersion = (value32 & CHIP_VER_RTL_MASK)>>CHIP_VER_RTL_SHIFT; /* IC version (CUT) */
+
+ /* For regulator mode. by tynli. 2011.01.14 */
+ pHalData->RegulatorMode = ((value32 & TRP_BT_EN) ? RT_LDO_REGULATOR : RT_SWITCHING_REGULATOR);
+
+ ChipVersion.ROMVer = 0; /* ROM code version. */
+
+ dump_chip_info(ChipVersion);
+
+ pHalData->VersionID = ChipVersion;
+
+ if (IS_1T2R(ChipVersion)) {
+ pHalData->rf_type = RF_1T2R;
+ pHalData->NumTotalRFPath = 2;
+ } else if (IS_2T2R(ChipVersion)) {
+ pHalData->rf_type = RF_2T2R;
+ pHalData->NumTotalRFPath = 2;
+ } else{
+ pHalData->rf_type = RF_1T1R;
+ pHalData->NumTotalRFPath = 1;
+ }
+
+ MSG_88E("RF_Type is %x!!\n", pHalData->rf_type);
+
+ return ChipVersion;
+}
+
+static void rtl8188e_read_chip_version(struct adapter *padapter)
+{
+ ReadChipVersion8188E(padapter);
+}
+
+static void rtl8188e_SetHalODMVar(struct adapter *Adapter, enum hal_odm_variable eVariable, void *pValue1, bool bSet)
+{
+ struct hal_data_8188e *pHalData = GET_HAL_DATA(Adapter);
+ struct odm_dm_struct *podmpriv = &pHalData->odmpriv;
+ switch (eVariable) {
+ case HAL_ODM_STA_INFO:
+ {
+ struct sta_info *psta = pValue1;
+
+ if (bSet) {
+ DBG_88E("### Set STA_(%d) info\n", psta->mac_id);
+ ODM_CmnInfoPtrArrayHook(podmpriv, ODM_CMNINFO_STA_STATUS, psta->mac_id, psta);
+ ODM_RAInfo_Init(podmpriv, psta->mac_id);
+ } else {
+ DBG_88E("### Clean STA_(%d) info\n", psta->mac_id);
+ ODM_CmnInfoPtrArrayHook(podmpriv, ODM_CMNINFO_STA_STATUS, psta->mac_id, NULL);
+ }
+ }
+ break;
+ case HAL_ODM_P2P_STATE:
+ ODM_CmnInfoUpdate(podmpriv, ODM_CMNINFO_WIFI_DIRECT, bSet);
+ break;
+ case HAL_ODM_WIFI_DISPLAY_STATE:
+ ODM_CmnInfoUpdate(podmpriv, ODM_CMNINFO_WIFI_DISPLAY, bSet);
+ break;
+ default:
+ break;
+ }
+}
+
+static void hal_notch_filter_8188e(struct adapter *adapter, bool enable)
+{
+ if (enable) {
+ DBG_88E("Enable notch filter\n");
+ usb_write8(adapter, rOFDM0_RxDSP+1, usb_read8(adapter, rOFDM0_RxDSP+1) | BIT1);
+ } else {
+ DBG_88E("Disable notch filter\n");
+ usb_write8(adapter, rOFDM0_RxDSP+1, usb_read8(adapter, rOFDM0_RxDSP+1) & ~BIT1);
+ }
+}
+void rtl8188e_set_hal_ops(struct hal_ops *pHalFunc)
+{
+ pHalFunc->free_hal_data = &rtl8188e_free_hal_data;
+
+ pHalFunc->dm_init = &rtl8188e_init_dm_priv;
+
+ pHalFunc->read_chip_version = &rtl8188e_read_chip_version;
+
+ pHalFunc->set_bwmode_handler = &phy_set_bw_mode;
+ pHalFunc->set_channel_handler = &phy_sw_chnl;
+
+ pHalFunc->hal_dm_watchdog = &rtl8188e_HalDmWatchDog;
+
+ pHalFunc->Add_RateATid = &rtl8188e_Add_RateATid;
+
+ pHalFunc->AntDivBeforeLinkHandler = &AntDivBeforeLink8188E;
+ pHalFunc->AntDivCompareHandler = &AntDivCompare8188E;
+ pHalFunc->read_rfreg = &phy_query_rf_reg;
+ pHalFunc->write_rfreg = &phy_set_rf_reg;
+
+ pHalFunc->sreset_init_value = &sreset_init_value;
+ pHalFunc->sreset_get_wifi_status = &sreset_get_wifi_status;
+
+ pHalFunc->SetHalODMVarHandler = &rtl8188e_SetHalODMVar;
+
+ pHalFunc->hal_notch_filter = &hal_notch_filter_8188e;
+}
+
+/* */
+/* */
+/* LLT R/W/Init function */
+/* */
+/* */
+static s32 _LLTWrite(struct adapter *padapter, u32 address, u32 data)
+{
+ s32 status = _SUCCESS;
+ s32 count = 0;
+ u32 value = _LLT_INIT_ADDR(address) | _LLT_INIT_DATA(data) | _LLT_OP(_LLT_WRITE_ACCESS);
+ u16 LLTReg = REG_LLT_INIT;
+
+ usb_write32(padapter, LLTReg, value);
+
+ /* polling */
+ do {
+ value = usb_read32(padapter, LLTReg);
+ if (_LLT_NO_ACTIVE == _LLT_OP_VALUE(value))
+ break;
+
+ if (count > POLLING_LLT_THRESHOLD) {
+ RT_TRACE(_module_hal_init_c_, _drv_err_, ("Failed to polling write LLT done at address %d!\n", address));
+ status = _FAIL;
+ break;
+ }
+ } while (count++);
+
+ return status;
+}
+
+s32 InitLLTTable(struct adapter *padapter, u8 txpktbuf_bndy)
+{
+ s32 status = _FAIL;
+ u32 i;
+ u32 Last_Entry_Of_TxPktBuf = LAST_ENTRY_OF_TX_PKT_BUFFER;/* 176, 22k */
+
+ if (rtw_IOL_applied(padapter)) {
+ status = iol_InitLLTTable(padapter, txpktbuf_bndy);
+ } else {
+ for (i = 0; i < (txpktbuf_bndy - 1); i++) {
+ status = _LLTWrite(padapter, i, i + 1);
+ if (_SUCCESS != status)
+ return status;
+ }
+
+ /* end of list */
+ status = _LLTWrite(padapter, (txpktbuf_bndy - 1), 0xFF);
+ if (_SUCCESS != status)
+ return status;
+
+ /* Make the other pages as ring buffer */
+ /* This ring buffer is used as beacon buffer if we config this MAC as two MAC transfer. */
+ /* Otherwise used as local loopback buffer. */
+ for (i = txpktbuf_bndy; i < Last_Entry_Of_TxPktBuf; i++) {
+ status = _LLTWrite(padapter, i, (i + 1));
+ if (_SUCCESS != status)
+ return status;
+ }
+
+ /* Let last entry point to the start entry of ring buffer */
+ status = _LLTWrite(padapter, Last_Entry_Of_TxPktBuf, txpktbuf_bndy);
+ if (_SUCCESS != status) {
+ return status;
+ }
+ }
+
+ return status;
+}
+
+void
+Hal_InitPGData88E(struct adapter *padapter)
+{
+ struct eeprom_priv *pEEPROM = GET_EEPROM_EFUSE_PRIV(padapter);
+
+ if (!pEEPROM->bautoload_fail_flag) { /* autoload OK. */
+ if (!is_boot_from_eeprom(padapter)) {
+ /* Read EFUSE real map to shadow. */
+ EFUSE_ShadowMapUpdate(padapter, EFUSE_WIFI);
+ }
+ } else {/* autoload fail */
+ RT_TRACE(_module_hci_hal_init_c_, _drv_notice_, ("AutoLoad Fail reported from CR9346!!\n"));
+ /* update to default value 0xFF */
+ if (!is_boot_from_eeprom(padapter))
+ EFUSE_ShadowMapUpdate(padapter, EFUSE_WIFI);
+ }
+}
+
+void
+Hal_EfuseParseIDCode88E(
+ struct adapter *padapter,
+ u8 *hwinfo
+ )
+{
+ struct eeprom_priv *pEEPROM = GET_EEPROM_EFUSE_PRIV(padapter);
+ u16 EEPROMId;
+
+ /* Checl 0x8129 again for making sure autoload status!! */
+ EEPROMId = le16_to_cpu(*((__le16 *)hwinfo));
+ if (EEPROMId != RTL_EEPROM_ID) {
+ DBG_88E("EEPROM ID(%#x) is invalid!!\n", EEPROMId);
+ pEEPROM->bautoload_fail_flag = true;
+ } else {
+ pEEPROM->bautoload_fail_flag = false;
+ }
+
+ DBG_88E("EEPROM ID = 0x%04x\n", EEPROMId);
+}
+
+static void Hal_ReadPowerValueFromPROM_8188E(struct txpowerinfo24g *pwrInfo24G, u8 *PROMContent, bool AutoLoadFail)
+{
+ u32 rfPath, eeAddr = EEPROM_TX_PWR_INX_88E, group, TxCount = 0;
+
+ memset(pwrInfo24G, 0, sizeof(struct txpowerinfo24g));
+
+ if (AutoLoadFail) {
+ for (rfPath = 0; rfPath < MAX_RF_PATH; rfPath++) {
+ /* 2.4G default value */
+ for (group = 0; group < MAX_CHNL_GROUP_24G; group++) {
+ pwrInfo24G->IndexCCK_Base[rfPath][group] = EEPROM_DEFAULT_24G_INDEX;
+ pwrInfo24G->IndexBW40_Base[rfPath][group] = EEPROM_DEFAULT_24G_INDEX;
+ }
+ for (TxCount = 0; TxCount < MAX_TX_COUNT; TxCount++) {
+ if (TxCount == 0) {
+ pwrInfo24G->BW20_Diff[rfPath][0] = EEPROM_DEFAULT_24G_HT20_DIFF;
+ pwrInfo24G->OFDM_Diff[rfPath][0] = EEPROM_DEFAULT_24G_OFDM_DIFF;
+ } else {
+ pwrInfo24G->BW20_Diff[rfPath][TxCount] = EEPROM_DEFAULT_DIFF;
+ pwrInfo24G->BW40_Diff[rfPath][TxCount] = EEPROM_DEFAULT_DIFF;
+ pwrInfo24G->CCK_Diff[rfPath][TxCount] = EEPROM_DEFAULT_DIFF;
+ pwrInfo24G->OFDM_Diff[rfPath][TxCount] = EEPROM_DEFAULT_DIFF;
+ }
+ }
+ }
+ return;
+ }
+
+ for (rfPath = 0; rfPath < MAX_RF_PATH; rfPath++) {
+ /* 2.4G default value */
+ for (group = 0; group < MAX_CHNL_GROUP_24G; group++) {
+ pwrInfo24G->IndexCCK_Base[rfPath][group] = PROMContent[eeAddr++];
+ if (pwrInfo24G->IndexCCK_Base[rfPath][group] == 0xFF)
+ pwrInfo24G->IndexCCK_Base[rfPath][group] = EEPROM_DEFAULT_24G_INDEX;
+ }
+ for (group = 0; group < MAX_CHNL_GROUP_24G-1; group++) {
+ pwrInfo24G->IndexBW40_Base[rfPath][group] = PROMContent[eeAddr++];
+ if (pwrInfo24G->IndexBW40_Base[rfPath][group] == 0xFF)
+ pwrInfo24G->IndexBW40_Base[rfPath][group] = EEPROM_DEFAULT_24G_INDEX;
+ }
+ for (TxCount = 0; TxCount < MAX_TX_COUNT; TxCount++) {
+ if (TxCount == 0) {
+ pwrInfo24G->BW40_Diff[rfPath][TxCount] = 0;
+ if (PROMContent[eeAddr] == 0xFF) {
+ pwrInfo24G->BW20_Diff[rfPath][TxCount] = EEPROM_DEFAULT_24G_HT20_DIFF;
+ } else {
+ pwrInfo24G->BW20_Diff[rfPath][TxCount] = (PROMContent[eeAddr]&0xf0)>>4;
+ if (pwrInfo24G->BW20_Diff[rfPath][TxCount] & BIT3) /* 4bit sign number to 8 bit sign number */
+ pwrInfo24G->BW20_Diff[rfPath][TxCount] |= 0xF0;
+ }
+
+ if (PROMContent[eeAddr] == 0xFF) {
+ pwrInfo24G->OFDM_Diff[rfPath][TxCount] = EEPROM_DEFAULT_24G_OFDM_DIFF;
+ } else {
+ pwrInfo24G->OFDM_Diff[rfPath][TxCount] = (PROMContent[eeAddr]&0x0f);
+ if (pwrInfo24G->OFDM_Diff[rfPath][TxCount] & BIT3) /* 4bit sign number to 8 bit sign number */
+ pwrInfo24G->OFDM_Diff[rfPath][TxCount] |= 0xF0;
+ }
+ pwrInfo24G->CCK_Diff[rfPath][TxCount] = 0;
+ eeAddr++;
+ } else {
+ if (PROMContent[eeAddr] == 0xFF) {
+ pwrInfo24G->BW40_Diff[rfPath][TxCount] = EEPROM_DEFAULT_DIFF;
+ } else {
+ pwrInfo24G->BW40_Diff[rfPath][TxCount] = (PROMContent[eeAddr]&0xf0)>>4;
+ if (pwrInfo24G->BW40_Diff[rfPath][TxCount] & BIT3) /* 4bit sign number to 8 bit sign number */
+ pwrInfo24G->BW40_Diff[rfPath][TxCount] |= 0xF0;
+ }
+
+ if (PROMContent[eeAddr] == 0xFF) {
+ pwrInfo24G->BW20_Diff[rfPath][TxCount] = EEPROM_DEFAULT_DIFF;
+ } else {
+ pwrInfo24G->BW20_Diff[rfPath][TxCount] = (PROMContent[eeAddr]&0x0f);
+ if (pwrInfo24G->BW20_Diff[rfPath][TxCount] & BIT3) /* 4bit sign number to 8 bit sign number */
+ pwrInfo24G->BW20_Diff[rfPath][TxCount] |= 0xF0;
+ }
+ eeAddr++;
+
+ if (PROMContent[eeAddr] == 0xFF) {
+ pwrInfo24G->OFDM_Diff[rfPath][TxCount] = EEPROM_DEFAULT_DIFF;
+ } else {
+ pwrInfo24G->OFDM_Diff[rfPath][TxCount] = (PROMContent[eeAddr]&0xf0)>>4;
+ if (pwrInfo24G->OFDM_Diff[rfPath][TxCount] & BIT3) /* 4bit sign number to 8 bit sign number */
+ pwrInfo24G->OFDM_Diff[rfPath][TxCount] |= 0xF0;
+ }
+
+ if (PROMContent[eeAddr] == 0xFF) {
+ pwrInfo24G->CCK_Diff[rfPath][TxCount] = EEPROM_DEFAULT_DIFF;
+ } else {
+ pwrInfo24G->CCK_Diff[rfPath][TxCount] = (PROMContent[eeAddr]&0x0f);
+ if (pwrInfo24G->CCK_Diff[rfPath][TxCount] & BIT3) /* 4bit sign number to 8 bit sign number */
+ pwrInfo24G->CCK_Diff[rfPath][TxCount] |= 0xF0;
+ }
+ eeAddr++;
+ }
+ }
+ }
+}
+
+static u8 Hal_GetChnlGroup88E(u8 chnl, u8 *pGroup)
+{
+ u8 bIn24G = true;
+
+ if (chnl <= 14) {
+ bIn24G = true;
+
+ if (chnl < 3) /* Channel 1-2 */
+ *pGroup = 0;
+ else if (chnl < 6) /* Channel 3-5 */
+ *pGroup = 1;
+ else if (chnl < 9) /* Channel 6-8 */
+ *pGroup = 2;
+ else if (chnl < 12) /* Channel 9-11 */
+ *pGroup = 3;
+ else if (chnl < 14) /* Channel 12-13 */
+ *pGroup = 4;
+ else if (chnl == 14) /* Channel 14 */
+ *pGroup = 5;
+ } else {
+ bIn24G = false;
+
+ if (chnl <= 40)
+ *pGroup = 0;
+ else if (chnl <= 48)
+ *pGroup = 1;
+ else if (chnl <= 56)
+ *pGroup = 2;
+ else if (chnl <= 64)
+ *pGroup = 3;
+ else if (chnl <= 104)
+ *pGroup = 4;
+ else if (chnl <= 112)
+ *pGroup = 5;
+ else if (chnl <= 120)
+ *pGroup = 5;
+ else if (chnl <= 128)
+ *pGroup = 6;
+ else if (chnl <= 136)
+ *pGroup = 7;
+ else if (chnl <= 144)
+ *pGroup = 8;
+ else if (chnl <= 153)
+ *pGroup = 9;
+ else if (chnl <= 161)
+ *pGroup = 10;
+ else if (chnl <= 177)
+ *pGroup = 11;
+ }
+ return bIn24G;
+}
+
+void Hal_ReadPowerSavingMode88E(struct adapter *padapter, u8 *hwinfo, bool AutoLoadFail)
+{
+ if (AutoLoadFail) {
+ padapter->pwrctrlpriv.bHWPowerdown = false;
+ padapter->pwrctrlpriv.bSupportRemoteWakeup = false;
+ } else {
+ /* hw power down mode selection , 0:rf-off / 1:power down */
+
+ if (padapter->registrypriv.hwpdn_mode == 2)
+ padapter->pwrctrlpriv.bHWPowerdown = (hwinfo[EEPROM_RF_FEATURE_OPTION_88E] & BIT4);
+ else
+ padapter->pwrctrlpriv.bHWPowerdown = padapter->registrypriv.hwpdn_mode;
+
+ /* decide hw if support remote wakeup function */
+ /* if hw supported, 8051 (SIE) will generate WeakUP signal(D+/D- toggle) when autoresume */
+ padapter->pwrctrlpriv.bSupportRemoteWakeup = (hwinfo[EEPROM_USB_OPTIONAL_FUNCTION0] & BIT1) ? true : false;
+
+ DBG_88E("%s...bHWPwrPindetect(%x)-bHWPowerdown(%x) , bSupportRemoteWakeup(%x)\n", __func__,
+ padapter->pwrctrlpriv.bHWPwrPindetect, padapter->pwrctrlpriv.bHWPowerdown , padapter->pwrctrlpriv.bSupportRemoteWakeup);
+
+ DBG_88E("### PS params => power_mgnt(%x), usbss_enable(%x) ###\n", padapter->registrypriv.power_mgnt, padapter->registrypriv.usbss_enable);
+ }
+}
+
+void Hal_ReadTxPowerInfo88E(struct adapter *padapter, u8 *PROMContent, bool AutoLoadFail)
+{
+ struct hal_data_8188e *pHalData = GET_HAL_DATA(padapter);
+ struct txpowerinfo24g pwrInfo24G;
+ u8 rfPath, ch, group;
+ u8 bIn24G, TxCount;
+
+ Hal_ReadPowerValueFromPROM_8188E(&pwrInfo24G, PROMContent, AutoLoadFail);
+
+ if (!AutoLoadFail)
+ pHalData->bTXPowerDataReadFromEEPORM = true;
+
+ for (rfPath = 0; rfPath < pHalData->NumTotalRFPath; rfPath++) {
+ for (ch = 0; ch < CHANNEL_MAX_NUMBER; ch++) {
+ bIn24G = Hal_GetChnlGroup88E(ch, &group);
+ if (bIn24G) {
+ pHalData->Index24G_CCK_Base[rfPath][ch] = pwrInfo24G.IndexCCK_Base[rfPath][group];
+ if (ch == 14)
+ pHalData->Index24G_BW40_Base[rfPath][ch] = pwrInfo24G.IndexBW40_Base[rfPath][4];
+ else
+ pHalData->Index24G_BW40_Base[rfPath][ch] = pwrInfo24G.IndexBW40_Base[rfPath][group];
+ }
+ if (bIn24G) {
+ DBG_88E("======= Path %d, Channel %d =======\n", rfPath, ch);
+ DBG_88E("Index24G_CCK_Base[%d][%d] = 0x%x\n", rfPath, ch , pHalData->Index24G_CCK_Base[rfPath][ch]);
+ DBG_88E("Index24G_BW40_Base[%d][%d] = 0x%x\n", rfPath, ch , pHalData->Index24G_BW40_Base[rfPath][ch]);
+ }
+ }
+ for (TxCount = 0; TxCount < MAX_TX_COUNT; TxCount++) {
+ pHalData->CCK_24G_Diff[rfPath][TxCount] = pwrInfo24G.CCK_Diff[rfPath][TxCount];
+ pHalData->OFDM_24G_Diff[rfPath][TxCount] = pwrInfo24G.OFDM_Diff[rfPath][TxCount];
+ pHalData->BW20_24G_Diff[rfPath][TxCount] = pwrInfo24G.BW20_Diff[rfPath][TxCount];
+ pHalData->BW40_24G_Diff[rfPath][TxCount] = pwrInfo24G.BW40_Diff[rfPath][TxCount];
+ DBG_88E("======= TxCount %d =======\n", TxCount);
+ DBG_88E("CCK_24G_Diff[%d][%d] = %d\n", rfPath, TxCount, pHalData->CCK_24G_Diff[rfPath][TxCount]);
+ DBG_88E("OFDM_24G_Diff[%d][%d] = %d\n", rfPath, TxCount, pHalData->OFDM_24G_Diff[rfPath][TxCount]);
+ DBG_88E("BW20_24G_Diff[%d][%d] = %d\n", rfPath, TxCount, pHalData->BW20_24G_Diff[rfPath][TxCount]);
+ DBG_88E("BW40_24G_Diff[%d][%d] = %d\n", rfPath, TxCount, pHalData->BW40_24G_Diff[rfPath][TxCount]);
+ }
+ }
+
+ /* 2010/10/19 MH Add Regulator recognize for CU. */
+ if (!AutoLoadFail) {
+ pHalData->EEPROMRegulatory = (PROMContent[EEPROM_RF_BOARD_OPTION_88E]&0x7); /* bit0~2 */
+ if (PROMContent[EEPROM_RF_BOARD_OPTION_88E] == 0xFF)
+ pHalData->EEPROMRegulatory = (EEPROM_DEFAULT_BOARD_OPTION&0x7); /* bit0~2 */
+ } else {
+ pHalData->EEPROMRegulatory = 0;
+ }
+ DBG_88E("EEPROMRegulatory = 0x%x\n", pHalData->EEPROMRegulatory);
+}
+
+void Hal_EfuseParseXtal_8188E(struct adapter *pAdapter, u8 *hwinfo, bool AutoLoadFail)
+{
+ struct hal_data_8188e *pHalData = GET_HAL_DATA(pAdapter);
+
+ if (!AutoLoadFail) {
+ pHalData->CrystalCap = hwinfo[EEPROM_XTAL_88E];
+ if (pHalData->CrystalCap == 0xFF)
+ pHalData->CrystalCap = EEPROM_Default_CrystalCap_88E;
+ } else {
+ pHalData->CrystalCap = EEPROM_Default_CrystalCap_88E;
+ }
+ DBG_88E("CrystalCap: 0x%2x\n", pHalData->CrystalCap);
+}
+
+void Hal_EfuseParseBoardType88E(struct adapter *pAdapter, u8 *hwinfo, bool AutoLoadFail)
+{
+ struct hal_data_8188e *pHalData = GET_HAL_DATA(pAdapter);
+
+ if (!AutoLoadFail)
+ pHalData->BoardType = (hwinfo[EEPROM_RF_BOARD_OPTION_88E]
+ & 0xE0) >> 5;
+ else
+ pHalData->BoardType = 0;
+ DBG_88E("Board Type: 0x%2x\n", pHalData->BoardType);
+}
+
+void Hal_EfuseParseEEPROMVer88E(struct adapter *padapter, u8 *hwinfo, bool AutoLoadFail)
+{
+ struct hal_data_8188e *pHalData = GET_HAL_DATA(padapter);
+
+ if (!AutoLoadFail) {
+ pHalData->EEPROMVersion = hwinfo[EEPROM_VERSION_88E];
+ if (pHalData->EEPROMVersion == 0xFF)
+ pHalData->EEPROMVersion = EEPROM_Default_Version;
+ } else {
+ pHalData->EEPROMVersion = 1;
+ }
+ RT_TRACE(_module_hci_hal_init_c_, _drv_info_,
+ ("Hal_EfuseParseEEPROMVer(), EEVer = %d\n",
+ pHalData->EEPROMVersion));
+}
+
+void rtl8188e_EfuseParseChnlPlan(struct adapter *padapter, u8 *hwinfo, bool AutoLoadFail)
+{
+ padapter->mlmepriv.ChannelPlan =
+ hal_com_get_channel_plan(padapter,
+ hwinfo ? hwinfo[EEPROM_ChannelPlan_88E] : 0xFF,
+ padapter->registrypriv.channel_plan,
+ RT_CHANNEL_DOMAIN_WORLD_WIDE_13, AutoLoadFail);
+
+ DBG_88E("mlmepriv.ChannelPlan = 0x%02x\n", padapter->mlmepriv.ChannelPlan);
+}
+
+void Hal_EfuseParseCustomerID88E(struct adapter *padapter, u8 *hwinfo, bool AutoLoadFail)
+{
+ struct hal_data_8188e *pHalData = GET_HAL_DATA(padapter);
+
+ if (!AutoLoadFail) {
+ pHalData->EEPROMCustomerID = hwinfo[EEPROM_CUSTOMERID_88E];
+ } else {
+ pHalData->EEPROMCustomerID = 0;
+ pHalData->EEPROMSubCustomerID = 0;
+ }
+ DBG_88E("EEPROM Customer ID: 0x%2x\n", pHalData->EEPROMCustomerID);
+}
+
+void Hal_ReadAntennaDiversity88E(struct adapter *pAdapter, u8 *PROMContent, bool AutoLoadFail)
+{
+ struct hal_data_8188e *pHalData = GET_HAL_DATA(pAdapter);
+ struct registry_priv *registry_par = &pAdapter->registrypriv;
+
+ if (!AutoLoadFail) {
+ /* Antenna Diversity setting. */
+ if (registry_par->antdiv_cfg == 2) { /* 2:By EFUSE */
+ pHalData->AntDivCfg = (PROMContent[EEPROM_RF_BOARD_OPTION_88E]&0x18)>>3;
+ if (PROMContent[EEPROM_RF_BOARD_OPTION_88E] == 0xFF)
+ pHalData->AntDivCfg = (EEPROM_DEFAULT_BOARD_OPTION&0x18)>>3;
+ } else {
+ pHalData->AntDivCfg = registry_par->antdiv_cfg; /* 0:OFF , 1:ON, 2:By EFUSE */
+ }
+
+ if (registry_par->antdiv_type == 0) {
+ /* If TRxAntDivType is AUTO in advanced setting, use EFUSE value instead. */
+ pHalData->TRxAntDivType = PROMContent[EEPROM_RF_ANTENNA_OPT_88E];
+ if (pHalData->TRxAntDivType == 0xFF)
+ pHalData->TRxAntDivType = CG_TRX_HW_ANTDIV; /* For 88EE, 1Tx and 1RxCG are fixed.(1Ant, Tx and RxCG are both on aux port) */
+ } else {
+ pHalData->TRxAntDivType = registry_par->antdiv_type;
+ }
+
+ if (pHalData->TRxAntDivType == CG_TRX_HW_ANTDIV || pHalData->TRxAntDivType == CGCS_RX_HW_ANTDIV)
+ pHalData->AntDivCfg = 1; /* 0xC1[3] is ignored. */
+ } else {
+ pHalData->AntDivCfg = 0;
+ pHalData->TRxAntDivType = pHalData->TRxAntDivType; /* The value in the driver setting of device manager. */
+ }
+ DBG_88E("EEPROM : AntDivCfg = %x, TRxAntDivType = %x\n", pHalData->AntDivCfg, pHalData->TRxAntDivType);
+}
+
+void Hal_ReadThermalMeter_88E(struct adapter *Adapter, u8 *PROMContent, bool AutoloadFail)
+{
+ struct hal_data_8188e *pHalData = GET_HAL_DATA(Adapter);
+
+ /* ThermalMeter from EEPROM */
+ if (!AutoloadFail)
+ pHalData->EEPROMThermalMeter = PROMContent[EEPROM_THERMAL_METER_88E];
+ else
+ pHalData->EEPROMThermalMeter = EEPROM_Default_ThermalMeter_88E;
+
+ if (pHalData->EEPROMThermalMeter == 0xff || AutoloadFail) {
+ pHalData->bAPKThermalMeterIgnore = true;
+ pHalData->EEPROMThermalMeter = EEPROM_Default_ThermalMeter_88E;
+ }
+ DBG_88E("ThermalMeter = 0x%x\n", pHalData->EEPROMThermalMeter);
+}
diff --git a/drivers/staging/rtl8188eu/hal/rtl8188e_rxdesc.c b/drivers/staging/rtl8188eu/hal/rtl8188e_rxdesc.c
new file mode 100644
index 000000000..53cf3baf4
--- /dev/null
+++ b/drivers/staging/rtl8188eu/hal/rtl8188e_rxdesc.c
@@ -0,0 +1,205 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2011 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ *
+ ******************************************************************************/
+#define _RTL8188E_REDESC_C_
+
+#include <osdep_service.h>
+#include <drv_types.h>
+#include <rtl8188e_hal.h>
+
+static void process_rssi(struct adapter *padapter, struct recv_frame *prframe)
+{
+ struct rx_pkt_attrib *pattrib = &prframe->attrib;
+ struct signal_stat *signal_stat = &padapter->recvpriv.signal_strength_data;
+
+ if (signal_stat->update_req) {
+ signal_stat->total_num = 0;
+ signal_stat->total_val = 0;
+ signal_stat->update_req = 0;
+ }
+
+ signal_stat->total_num++;
+ signal_stat->total_val += pattrib->phy_info.SignalStrength;
+ signal_stat->avg_val = signal_stat->total_val / signal_stat->total_num;
+} /* Process_UI_RSSI_8192C */
+
+static void process_link_qual(struct adapter *padapter,
+ struct recv_frame *prframe)
+{
+ struct rx_pkt_attrib *pattrib;
+ struct signal_stat *signal_stat;
+
+ if (prframe == NULL || padapter == NULL)
+ return;
+
+ pattrib = &prframe->attrib;
+ signal_stat = &padapter->recvpriv.signal_qual_data;
+
+ if (signal_stat->update_req) {
+ signal_stat->total_num = 0;
+ signal_stat->total_val = 0;
+ signal_stat->update_req = 0;
+ }
+
+ signal_stat->total_num++;
+ signal_stat->total_val += pattrib->phy_info.SignalQuality;
+ signal_stat->avg_val = signal_stat->total_val / signal_stat->total_num;
+}
+
+void rtl8188e_process_phy_info(struct adapter *padapter, void *prframe)
+{
+ struct recv_frame *precvframe = (struct recv_frame *)prframe;
+
+ /* Check RSSI */
+ process_rssi(padapter, precvframe);
+ /* Check EVM */
+ process_link_qual(padapter, precvframe);
+}
+
+void update_recvframe_attrib_88e(struct recv_frame *precvframe,
+ struct recv_stat *prxstat)
+{
+ struct rx_pkt_attrib *pattrib;
+ struct recv_stat report;
+
+ report.rxdw0 = prxstat->rxdw0;
+ report.rxdw1 = prxstat->rxdw1;
+ report.rxdw2 = prxstat->rxdw2;
+ report.rxdw3 = prxstat->rxdw3;
+ report.rxdw4 = prxstat->rxdw4;
+ report.rxdw5 = prxstat->rxdw5;
+
+ pattrib = &precvframe->attrib;
+ memset(pattrib, 0, sizeof(struct rx_pkt_attrib));
+
+ pattrib->crc_err = (u8)((le32_to_cpu(report.rxdw0) >> 14) & 0x1);/* u8)prxreport->crc32; */
+
+ /* update rx report to recv_frame attribute */
+ pattrib->pkt_rpt_type = (u8)((le32_to_cpu(report.rxdw3) >> 14) & 0x3);/* prxreport->rpt_sel; */
+
+ if (pattrib->pkt_rpt_type == NORMAL_RX) { /* Normal rx packet */
+ pattrib->pkt_len = (u16)(le32_to_cpu(report.rxdw0) & 0x00003fff);/* u16)prxreport->pktlen; */
+ pattrib->drvinfo_sz = (u8)((le32_to_cpu(report.rxdw0) >> 16) & 0xf) * 8;/* u8)(prxreport->drvinfosize << 3); */
+
+ pattrib->physt = (u8)((le32_to_cpu(report.rxdw0) >> 26) & 0x1);/* u8)prxreport->physt; */
+
+ pattrib->bdecrypted = (le32_to_cpu(report.rxdw0) & BIT(27)) ? 0 : 1;/* u8)(prxreport->swdec ? 0 : 1); */
+ pattrib->encrypt = (u8)((le32_to_cpu(report.rxdw0) >> 20) & 0x7);/* u8)prxreport->security; */
+
+ pattrib->qos = (u8)((le32_to_cpu(report.rxdw0) >> 23) & 0x1);/* u8)prxreport->qos; */
+ pattrib->priority = (u8)((le32_to_cpu(report.rxdw1) >> 8) & 0xf);/* u8)prxreport->tid; */
+
+ pattrib->amsdu = (u8)((le32_to_cpu(report.rxdw1) >> 13) & 0x1);/* u8)prxreport->amsdu; */
+
+ pattrib->seq_num = (u16)(le32_to_cpu(report.rxdw2) & 0x00000fff);/* u16)prxreport->seq; */
+ pattrib->frag_num = (u8)((le32_to_cpu(report.rxdw2) >> 12) & 0xf);/* u8)prxreport->frag; */
+ pattrib->mfrag = (u8)((le32_to_cpu(report.rxdw1) >> 27) & 0x1);/* u8)prxreport->mf; */
+ pattrib->mdata = (u8)((le32_to_cpu(report.rxdw1) >> 26) & 0x1);/* u8)prxreport->md; */
+
+ pattrib->mcs_rate = (u8)(le32_to_cpu(report.rxdw3) & 0x3f);/* u8)prxreport->rxmcs; */
+ pattrib->rxht = (u8)((le32_to_cpu(report.rxdw3) >> 6) & 0x1);/* u8)prxreport->rxht; */
+
+ pattrib->icv_err = (u8)((le32_to_cpu(report.rxdw0) >> 15) & 0x1);/* u8)prxreport->icverr; */
+ pattrib->shift_sz = (u8)((le32_to_cpu(report.rxdw0) >> 24) & 0x3);
+ } else if (pattrib->pkt_rpt_type == TX_REPORT1) { /* CCX */
+ pattrib->pkt_len = TX_RPT1_PKT_LEN;
+ pattrib->drvinfo_sz = 0;
+ } else if (pattrib->pkt_rpt_type == TX_REPORT2) { /* TX RPT */
+ pattrib->pkt_len = (u16)(le32_to_cpu(report.rxdw0) & 0x3FF);/* Rx length[9:0] */
+ pattrib->drvinfo_sz = 0;
+
+ /* */
+ /* Get TX report MAC ID valid. */
+ /* */
+ pattrib->MacIDValidEntry[0] = le32_to_cpu(report.rxdw4);
+ pattrib->MacIDValidEntry[1] = le32_to_cpu(report.rxdw5);
+
+ } else if (pattrib->pkt_rpt_type == HIS_REPORT) { /* USB HISR RPT */
+ pattrib->pkt_len = (u16)(le32_to_cpu(report.rxdw0) & 0x00003fff);/* u16)prxreport->pktlen; */
+ }
+}
+
+/*
+ * Notice:
+ * Before calling this function,
+ * precvframe->rx_data should be ready!
+ */
+void update_recvframe_phyinfo_88e(struct recv_frame *precvframe,
+ struct phy_stat *pphy_status)
+{
+ struct adapter *padapter = precvframe->adapter;
+ struct rx_pkt_attrib *pattrib = &precvframe->attrib;
+ struct hal_data_8188e *pHalData = GET_HAL_DATA(padapter);
+ struct odm_phy_status_info *pPHYInfo = (struct odm_phy_status_info *)(&pattrib->phy_info);
+ u8 *wlanhdr;
+ struct odm_per_pkt_info pkt_info;
+ u8 *sa = NULL;
+ struct sta_priv *pstapriv;
+ struct sta_info *psta;
+
+ pkt_info.bPacketMatchBSSID = false;
+ pkt_info.bPacketToSelf = false;
+ pkt_info.bPacketBeacon = false;
+
+ wlanhdr = precvframe->rx_data;
+
+ pkt_info.bPacketMatchBSSID = ((!IsFrameTypeCtrl(wlanhdr)) &&
+ !pattrib->icv_err && !pattrib->crc_err &&
+ !memcmp(get_hdr_bssid(wlanhdr),
+ get_bssid(&padapter->mlmepriv), ETH_ALEN));
+
+ pkt_info.bPacketToSelf = pkt_info.bPacketMatchBSSID &&
+ (!memcmp(get_da(wlanhdr),
+ myid(&padapter->eeprompriv), ETH_ALEN));
+
+ pkt_info.bPacketBeacon = pkt_info.bPacketMatchBSSID &&
+ (GetFrameSubType(wlanhdr) == WIFI_BEACON);
+
+ if (pkt_info.bPacketBeacon) {
+ if (check_fwstate(&padapter->mlmepriv, WIFI_STATION_STATE))
+ sa = padapter->mlmepriv.cur_network.network.MacAddress;
+ /* to do Ad-hoc */
+ } else {
+ sa = get_sa(wlanhdr);
+ }
+
+ pstapriv = &padapter->stapriv;
+ pkt_info.StationID = 0xFF;
+ psta = rtw_get_stainfo(pstapriv, sa);
+ if (psta)
+ pkt_info.StationID = psta->mac_id;
+ pkt_info.Rate = pattrib->mcs_rate;
+
+ ODM_PhyStatusQuery(&pHalData->odmpriv, pPHYInfo, (u8 *)pphy_status, &(pkt_info));
+
+ precvframe->psta = NULL;
+ if (pkt_info.bPacketMatchBSSID &&
+ (check_fwstate(&padapter->mlmepriv, WIFI_AP_STATE))) {
+ if (psta) {
+ precvframe->psta = psta;
+ rtl8188e_process_phy_info(padapter, precvframe);
+ }
+ } else if (pkt_info.bPacketToSelf || pkt_info.bPacketBeacon) {
+ if (check_fwstate(&padapter->mlmepriv, WIFI_ADHOC_STATE|WIFI_ADHOC_MASTER_STATE)) {
+ if (psta)
+ precvframe->psta = psta;
+ }
+ rtl8188e_process_phy_info(padapter, precvframe);
+ }
+}
diff --git a/drivers/staging/rtl8188eu/hal/rtl8188e_xmit.c b/drivers/staging/rtl8188eu/hal/rtl8188e_xmit.c
new file mode 100644
index 000000000..a6ba53b48
--- /dev/null
+++ b/drivers/staging/rtl8188eu/hal/rtl8188e_xmit.c
@@ -0,0 +1,92 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2011 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ *
+ ******************************************************************************/
+#define _RTL8188E_XMIT_C_
+
+#include <osdep_service.h>
+#include <drv_types.h>
+#include <rtl8188e_hal.h>
+
+void dump_txrpt_ccx_88e(void *buf)
+{
+ struct txrpt_ccx_88e *txrpt_ccx = buf;
+
+ DBG_88E("%s:\n"
+ "tag1:%u, pkt_num:%u, txdma_underflow:%u, int_bt:%u, int_tri:%u, int_ccx:%u\n"
+ "mac_id:%u, pkt_ok:%u, bmc:%u\n"
+ "retry_cnt:%u, lifetime_over:%u, retry_over:%u\n"
+ "ccx_qtime:%u\n"
+ "final_data_rate:0x%02x\n"
+ "qsel:%u, sw:0x%03x\n",
+ __func__, txrpt_ccx->tag1, txrpt_ccx->pkt_num,
+ txrpt_ccx->txdma_underflow, txrpt_ccx->int_bt,
+ txrpt_ccx->int_tri, txrpt_ccx->int_ccx,
+ txrpt_ccx->mac_id, txrpt_ccx->pkt_ok, txrpt_ccx->bmc,
+ txrpt_ccx->retry_cnt, txrpt_ccx->lifetime_over,
+ txrpt_ccx->retry_over, txrpt_ccx_qtime_88e(txrpt_ccx),
+ txrpt_ccx->final_data_rate, txrpt_ccx->qsel,
+ txrpt_ccx_sw_88e(txrpt_ccx)
+ );
+}
+
+void handle_txrpt_ccx_88e(struct adapter *adapter, u8 *buf)
+{
+ struct txrpt_ccx_88e *txrpt_ccx = (struct txrpt_ccx_88e *)buf;
+
+ if (txrpt_ccx->int_ccx) {
+ if (txrpt_ccx->pkt_ok)
+ rtw_ack_tx_done(&adapter->xmitpriv,
+ RTW_SCTX_DONE_SUCCESS);
+ else
+ rtw_ack_tx_done(&adapter->xmitpriv,
+ RTW_SCTX_DONE_CCX_PKT_FAIL);
+ }
+}
+
+void _dbg_dump_tx_info(struct adapter *padapter, int frame_tag,
+ struct tx_desc *ptxdesc)
+{
+ u8 dmp_txpkt;
+ bool dump_txdesc = false;
+
+ rtw_hal_get_def_var(padapter, HAL_DEF_DBG_DUMP_TXPKT, &(dmp_txpkt));
+
+ if (dmp_txpkt == 1) {/* dump txdesc for data frame */
+ DBG_88E("dump tx_desc for data frame\n");
+ if ((frame_tag & 0x0f) == DATA_FRAMETAG)
+ dump_txdesc = true;
+ } else if (dmp_txpkt == 2) {/* dump txdesc for mgnt frame */
+ DBG_88E("dump tx_desc for mgnt frame\n");
+ if ((frame_tag & 0x0f) == MGNT_FRAMETAG)
+ dump_txdesc = true;
+ }
+
+ if (dump_txdesc) {
+ DBG_88E("=====================================\n");
+ DBG_88E("txdw0(0x%08x)\n", ptxdesc->txdw0);
+ DBG_88E("txdw1(0x%08x)\n", ptxdesc->txdw1);
+ DBG_88E("txdw2(0x%08x)\n", ptxdesc->txdw2);
+ DBG_88E("txdw3(0x%08x)\n", ptxdesc->txdw3);
+ DBG_88E("txdw4(0x%08x)\n", ptxdesc->txdw4);
+ DBG_88E("txdw5(0x%08x)\n", ptxdesc->txdw5);
+ DBG_88E("txdw6(0x%08x)\n", ptxdesc->txdw6);
+ DBG_88E("txdw7(0x%08x)\n", ptxdesc->txdw7);
+ DBG_88E("=====================================\n");
+ }
+}
diff --git a/drivers/staging/rtl8188eu/hal/rtl8188eu_led.c b/drivers/staging/rtl8188eu/hal/rtl8188eu_led.c
new file mode 100644
index 000000000..81d691ddd
--- /dev/null
+++ b/drivers/staging/rtl8188eu/hal/rtl8188eu_led.c
@@ -0,0 +1,93 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2011 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ *
+ ******************************************************************************/
+
+#include <osdep_service.h>
+#include <drv_types.h>
+#include <rtl8188e_hal.h>
+#include <rtl8188e_led.h>
+#include <usb_ops_linux.h>
+
+/* LED object. */
+
+/* LED_819xUsb routines. */
+/* Description: */
+/* Turn on LED according to LedPin specified. */
+void SwLedOn(struct adapter *padapter, struct LED_871x *pLed)
+{
+ u8 LedCfg;
+
+ if (padapter->bSurpriseRemoved || padapter->bDriverStopped)
+ return;
+ LedCfg = usb_read8(padapter, REG_LEDCFG2);
+ usb_write8(padapter, REG_LEDCFG2, (LedCfg&0xf0)|BIT5|BIT6); /* SW control led0 on. */
+ pLed->bLedOn = true;
+}
+
+/* Description: */
+/* Turn off LED according to LedPin specified. */
+void SwLedOff(struct adapter *padapter, struct LED_871x *pLed)
+{
+ u8 LedCfg;
+ struct hal_data_8188e *pHalData = GET_HAL_DATA(padapter);
+
+ if (padapter->bSurpriseRemoved || padapter->bDriverStopped)
+ goto exit;
+
+ LedCfg = usb_read8(padapter, REG_LEDCFG2);/* 0x4E */
+
+ if (pHalData->bLedOpenDrain) {
+ /* Open-drain arrangement for controlling the LED) */
+ LedCfg &= 0x90; /* Set to software control. */
+ usb_write8(padapter, REG_LEDCFG2, (LedCfg|BIT3));
+ LedCfg = usb_read8(padapter, REG_MAC_PINMUX_CFG);
+ LedCfg &= 0xFE;
+ usb_write8(padapter, REG_MAC_PINMUX_CFG, LedCfg);
+ } else {
+ usb_write8(padapter, REG_LEDCFG2, (LedCfg|BIT3|BIT5|BIT6));
+ }
+exit:
+ pLed->bLedOn = false;
+}
+
+/* Interface to manipulate LED objects. */
+/* Default LED behavior. */
+
+/* Description: */
+/* Initialize all LED_871x objects. */
+void rtl8188eu_InitSwLeds(struct adapter *padapter)
+{
+ struct led_priv *pledpriv = &(padapter->ledpriv);
+ struct hal_data_8188e *haldata = GET_HAL_DATA(padapter);
+
+ pledpriv->bRegUseLed = true;
+ pledpriv->LedControlHandler = LedControl8188eu;
+ haldata->bLedOpenDrain = true;
+
+ InitLed871x(padapter, &(pledpriv->SwLed0));
+}
+
+/* Description: */
+/* DeInitialize all LED_819xUsb objects. */
+void rtl8188eu_DeInitSwLeds(struct adapter *padapter)
+{
+ struct led_priv *ledpriv = &(padapter->ledpriv);
+
+ DeInitLed871x(&(ledpriv->SwLed0));
+}
diff --git a/drivers/staging/rtl8188eu/hal/rtl8188eu_recv.c b/drivers/staging/rtl8188eu/hal/rtl8188eu_recv.c
new file mode 100644
index 000000000..06d1e6544
--- /dev/null
+++ b/drivers/staging/rtl8188eu/hal/rtl8188eu_recv.c
@@ -0,0 +1,120 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2011 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ *
+ ******************************************************************************/
+#define _RTL8188EU_RECV_C_
+#include <osdep_service.h>
+#include <drv_types.h>
+#include <recv_osdep.h>
+#include <mlme_osdep.h>
+
+#include <usb_ops_linux.h>
+#include <wifi.h>
+
+#include <rtl8188e_hal.h>
+
+int rtl8188eu_init_recv_priv(struct adapter *padapter)
+{
+ struct recv_priv *precvpriv = &padapter->recvpriv;
+ int i, res = _SUCCESS;
+ struct recv_buf *precvbuf;
+
+ tasklet_init(&precvpriv->recv_tasklet,
+ (void(*)(unsigned long))rtl8188eu_recv_tasklet,
+ (unsigned long)padapter);
+
+ /* init recv_buf */
+ _rtw_init_queue(&precvpriv->free_recv_buf_queue);
+
+ precvpriv->pallocated_recv_buf =
+ kcalloc(NR_RECVBUFF, sizeof(struct recv_buf), GFP_KERNEL);
+ if (precvpriv->pallocated_recv_buf == NULL) {
+ res = _FAIL;
+ RT_TRACE(_module_rtl871x_recv_c_, _drv_err_,
+ ("alloc recv_buf fail!\n"));
+ goto exit;
+ }
+
+ precvpriv->precv_buf = precvpriv->pallocated_recv_buf;
+
+
+ precvbuf = (struct recv_buf *)precvpriv->precv_buf;
+
+ for (i = 0; i < NR_RECVBUFF; i++) {
+ res = rtw_os_recvbuf_resource_alloc(padapter, precvbuf);
+ if (res == _FAIL)
+ break;
+ precvbuf->adapter = padapter;
+ precvbuf++;
+ }
+ precvpriv->free_recv_buf_queue_cnt = NR_RECVBUFF;
+ skb_queue_head_init(&precvpriv->rx_skb_queue);
+ {
+ int i;
+ size_t tmpaddr = 0;
+ size_t alignm = 0;
+ struct sk_buff *pskb = NULL;
+
+ skb_queue_head_init(&precvpriv->free_recv_skb_queue);
+
+ for (i = 0; i < NR_PREALLOC_RECV_SKB; i++) {
+ pskb = __netdev_alloc_skb(padapter->pnetdev,
+ MAX_RECVBUF_SZ + RECVBUFF_ALIGN_SZ,
+ GFP_KERNEL);
+ if (pskb) {
+ pskb->dev = padapter->pnetdev;
+ tmpaddr = (size_t)pskb->data;
+ alignm = tmpaddr & (RECVBUFF_ALIGN_SZ-1);
+ skb_reserve(pskb, (RECVBUFF_ALIGN_SZ - alignm));
+
+ skb_queue_tail(&precvpriv->free_recv_skb_queue,
+ pskb);
+ }
+ pskb = NULL;
+ }
+ }
+exit:
+ return res;
+}
+
+void rtl8188eu_free_recv_priv(struct adapter *padapter)
+{
+ int i;
+ struct recv_buf *precvbuf;
+ struct recv_priv *precvpriv = &padapter->recvpriv;
+
+ precvbuf = (struct recv_buf *)precvpriv->precv_buf;
+
+ for (i = 0; i < NR_RECVBUFF; i++) {
+ usb_free_urb(precvbuf->purb);
+ precvbuf++;
+ }
+
+ kfree(precvpriv->pallocated_recv_buf);
+
+ if (skb_queue_len(&precvpriv->rx_skb_queue))
+ DBG_88E(KERN_WARNING "rx_skb_queue not empty\n");
+ skb_queue_purge(&precvpriv->rx_skb_queue);
+
+
+ if (skb_queue_len(&precvpriv->free_recv_skb_queue))
+ DBG_88E(KERN_WARNING "free_recv_skb_queue not empty, %d\n",
+ skb_queue_len(&precvpriv->free_recv_skb_queue));
+
+ skb_queue_purge(&precvpriv->free_recv_skb_queue);
+}
diff --git a/drivers/staging/rtl8188eu/hal/rtl8188eu_xmit.c b/drivers/staging/rtl8188eu/hal/rtl8188eu_xmit.c
new file mode 100644
index 000000000..594c1da9d
--- /dev/null
+++ b/drivers/staging/rtl8188eu/hal/rtl8188eu_xmit.c
@@ -0,0 +1,698 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2011 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ *
+ ******************************************************************************/
+#define _RTL8188E_XMIT_C_
+#include <osdep_service.h>
+#include <drv_types.h>
+#include <wifi.h>
+#include <osdep_intf.h>
+#include <usb_ops_linux.h>
+#include <rtl8188e_hal.h>
+
+s32 rtl8188eu_init_xmit_priv(struct adapter *adapt)
+{
+ struct xmit_priv *pxmitpriv = &adapt->xmitpriv;
+
+ tasklet_init(&pxmitpriv->xmit_tasklet,
+ (void(*)(unsigned long))rtl8188eu_xmit_tasklet,
+ (unsigned long)adapt);
+ return _SUCCESS;
+}
+
+static u8 urb_zero_packet_chk(struct adapter *adapt, int sz)
+{
+ u8 set_tx_desc_offset;
+ struct hal_data_8188e *haldata = GET_HAL_DATA(adapt);
+ set_tx_desc_offset = (((sz + TXDESC_SIZE) % haldata->UsbBulkOutSize) == 0) ? 1 : 0;
+
+ return set_tx_desc_offset;
+}
+
+static void rtl8188eu_cal_txdesc_chksum(struct tx_desc *ptxdesc)
+{
+ u16 *usptr = (u16 *)ptxdesc;
+ u32 count = 16; /* (32 bytes / 2 bytes per XOR) => 16 times */
+ u32 index;
+ u16 checksum = 0;
+
+ /* Clear first */
+ ptxdesc->txdw7 &= cpu_to_le32(0xffff0000);
+
+ for (index = 0; index < count; index++)
+ checksum = checksum ^ le16_to_cpu(*(__le16 *)(usptr + index));
+ ptxdesc->txdw7 |= cpu_to_le32(0x0000ffff & checksum);
+}
+
+/* Description: In normal chip, we should send some packet to Hw which will be used by Fw */
+/* in FW LPS mode. The function is to fill the Tx descriptor of this packets, then */
+/* Fw can tell Hw to send these packet derectly. */
+void rtl8188e_fill_fake_txdesc(struct adapter *adapt, u8 *desc, u32 BufferLen, u8 ispspoll, u8 is_btqosnull)
+{
+ struct tx_desc *ptxdesc;
+
+ /* Clear all status */
+ ptxdesc = (struct tx_desc *)desc;
+ memset(desc, 0, TXDESC_SIZE);
+
+ /* offset 0 */
+ ptxdesc->txdw0 |= cpu_to_le32(OWN | FSG | LSG); /* own, bFirstSeg, bLastSeg; */
+
+ ptxdesc->txdw0 |= cpu_to_le32(((TXDESC_SIZE+OFFSET_SZ)<<OFFSET_SHT)&0x00ff0000); /* 32 bytes for TX Desc */
+
+ ptxdesc->txdw0 |= cpu_to_le32(BufferLen&0x0000ffff); /* Buffer size + command header */
+
+ /* offset 4 */
+ ptxdesc->txdw1 |= cpu_to_le32((QSLT_MGNT<<QSEL_SHT)&0x00001f00); /* Fixed queue of Mgnt queue */
+
+ /* Set NAVUSEHDR to prevent Ps-poll AId filed to be changed to error vlaue by Hw. */
+ if (ispspoll) {
+ ptxdesc->txdw1 |= cpu_to_le32(NAVUSEHDR);
+ } else {
+ ptxdesc->txdw4 |= cpu_to_le32(BIT(7)); /* Hw set sequence number */
+ ptxdesc->txdw3 |= cpu_to_le32((8 << 28)); /* set bit3 to 1. Suugested by TimChen. 2009.12.29. */
+ }
+
+ if (is_btqosnull)
+ ptxdesc->txdw2 |= cpu_to_le32(BIT(23)); /* BT NULL */
+
+ /* offset 16 */
+ ptxdesc->txdw4 |= cpu_to_le32(BIT(8));/* driver uses rate */
+
+ /* USB interface drop packet if the checksum of descriptor isn't correct. */
+ /* Using this checksum can let hardware recovery from packet bulk out error (e.g. Cancel URC, Bulk out error.). */
+ rtl8188eu_cal_txdesc_chksum(ptxdesc);
+}
+
+static void fill_txdesc_sectype(struct pkt_attrib *pattrib, struct tx_desc *ptxdesc)
+{
+ if ((pattrib->encrypt > 0) && !pattrib->bswenc) {
+ switch (pattrib->encrypt) {
+ /* SEC_TYPE : 0:NO_ENC,1:WEP40/TKIP,2:WAPI,3:AES */
+ case _WEP40_:
+ case _WEP104_:
+ ptxdesc->txdw1 |= cpu_to_le32((0x01<<SEC_TYPE_SHT)&0x00c00000);
+ ptxdesc->txdw2 |= cpu_to_le32(0x7 << AMPDU_DENSITY_SHT);
+ break;
+ case _TKIP_:
+ case _TKIP_WTMIC_:
+ ptxdesc->txdw1 |= cpu_to_le32((0x01<<SEC_TYPE_SHT)&0x00c00000);
+ ptxdesc->txdw2 |= cpu_to_le32(0x7 << AMPDU_DENSITY_SHT);
+ break;
+ case _AES_:
+ ptxdesc->txdw1 |= cpu_to_le32((0x03<<SEC_TYPE_SHT)&0x00c00000);
+ ptxdesc->txdw2 |= cpu_to_le32(0x7 << AMPDU_DENSITY_SHT);
+ break;
+ case _NO_PRIVACY_:
+ default:
+ break;
+ }
+ }
+}
+
+static void fill_txdesc_vcs(struct pkt_attrib *pattrib, __le32 *pdw)
+{
+ switch (pattrib->vcs_mode) {
+ case RTS_CTS:
+ *pdw |= cpu_to_le32(RTS_EN);
+ break;
+ case CTS_TO_SELF:
+ *pdw |= cpu_to_le32(CTS_2_SELF);
+ break;
+ case NONE_VCS:
+ default:
+ break;
+ }
+ if (pattrib->vcs_mode) {
+ *pdw |= cpu_to_le32(HW_RTS_EN);
+ /* Set RTS BW */
+ if (pattrib->ht_en) {
+ *pdw |= (pattrib->bwmode&HT_CHANNEL_WIDTH_40) ? cpu_to_le32(BIT(27)) : 0;
+
+ if (pattrib->ch_offset == HAL_PRIME_CHNL_OFFSET_LOWER)
+ *pdw |= cpu_to_le32((0x01 << 28) & 0x30000000);
+ else if (pattrib->ch_offset == HAL_PRIME_CHNL_OFFSET_UPPER)
+ *pdw |= cpu_to_le32((0x02 << 28) & 0x30000000);
+ else if (pattrib->ch_offset == HAL_PRIME_CHNL_OFFSET_DONT_CARE)
+ *pdw |= 0;
+ else
+ *pdw |= cpu_to_le32((0x03 << 28) & 0x30000000);
+ }
+ }
+}
+
+static void fill_txdesc_phy(struct pkt_attrib *pattrib, __le32 *pdw)
+{
+ if (pattrib->ht_en) {
+ *pdw |= (pattrib->bwmode&HT_CHANNEL_WIDTH_40) ? cpu_to_le32(BIT(25)) : 0;
+
+ if (pattrib->ch_offset == HAL_PRIME_CHNL_OFFSET_LOWER)
+ *pdw |= cpu_to_le32((0x01 << DATA_SC_SHT) & 0x003f0000);
+ else if (pattrib->ch_offset == HAL_PRIME_CHNL_OFFSET_UPPER)
+ *pdw |= cpu_to_le32((0x02 << DATA_SC_SHT) & 0x003f0000);
+ else if (pattrib->ch_offset == HAL_PRIME_CHNL_OFFSET_DONT_CARE)
+ *pdw |= 0;
+ else
+ *pdw |= cpu_to_le32((0x03 << DATA_SC_SHT) & 0x003f0000);
+ }
+}
+
+static s32 update_txdesc(struct xmit_frame *pxmitframe, u8 *pmem, s32 sz, u8 bagg_pkt)
+{
+ int pull = 0;
+ uint qsel;
+ u8 data_rate, pwr_status, offset;
+ struct adapter *adapt = pxmitframe->padapter;
+ struct pkt_attrib *pattrib = &pxmitframe->attrib;
+ struct hal_data_8188e *haldata = GET_HAL_DATA(adapt);
+ struct tx_desc *ptxdesc = (struct tx_desc *)pmem;
+ struct mlme_ext_priv *pmlmeext = &adapt->mlmeextpriv;
+ struct mlme_ext_info *pmlmeinfo = &(pmlmeext->mlmext_info);
+ int bmcst = IS_MCAST(pattrib->ra);
+
+ if (adapt->registrypriv.mp_mode == 0) {
+ if ((!bagg_pkt) && (urb_zero_packet_chk(adapt, sz) == 0)) {
+ ptxdesc = (struct tx_desc *)(pmem+PACKET_OFFSET_SZ);
+ pull = 1;
+ }
+ }
+
+ memset(ptxdesc, 0, sizeof(struct tx_desc));
+
+ /* 4 offset 0 */
+ ptxdesc->txdw0 |= cpu_to_le32(OWN | FSG | LSG);
+ ptxdesc->txdw0 |= cpu_to_le32(sz & 0x0000ffff);/* update TXPKTSIZE */
+
+ offset = TXDESC_SIZE + OFFSET_SZ;
+
+ ptxdesc->txdw0 |= cpu_to_le32(((offset) << OFFSET_SHT) & 0x00ff0000);/* 32 bytes for TX Desc */
+
+ if (bmcst)
+ ptxdesc->txdw0 |= cpu_to_le32(BMC);
+
+ if (adapt->registrypriv.mp_mode == 0) {
+ if (!bagg_pkt) {
+ if ((pull) && (pxmitframe->pkt_offset > 0))
+ pxmitframe->pkt_offset = pxmitframe->pkt_offset - 1;
+ }
+ }
+
+ /* pkt_offset, unit:8 bytes padding */
+ if (pxmitframe->pkt_offset > 0)
+ ptxdesc->txdw1 |= cpu_to_le32((pxmitframe->pkt_offset << 26) & 0x7c000000);
+
+ /* driver uses rate */
+ ptxdesc->txdw4 |= cpu_to_le32(USERATE);/* rate control always by driver */
+
+ if ((pxmitframe->frame_tag & 0x0f) == DATA_FRAMETAG) {
+ /* offset 4 */
+ ptxdesc->txdw1 |= cpu_to_le32(pattrib->mac_id & 0x3F);
+
+ qsel = (uint)(pattrib->qsel & 0x0000001f);
+ ptxdesc->txdw1 |= cpu_to_le32((qsel << QSEL_SHT) & 0x00001f00);
+
+ ptxdesc->txdw1 |= cpu_to_le32((pattrib->raid << RATE_ID_SHT) & 0x000F0000);
+
+ fill_txdesc_sectype(pattrib, ptxdesc);
+
+ if (pattrib->ampdu_en) {
+ ptxdesc->txdw2 |= cpu_to_le32(AGG_EN);/* AGG EN */
+ ptxdesc->txdw6 = cpu_to_le32(0x6666f800);
+ } else {
+ ptxdesc->txdw2 |= cpu_to_le32(AGG_BK);/* AGG BK */
+ }
+
+ /* offset 8 */
+
+ /* offset 12 */
+ ptxdesc->txdw3 |= cpu_to_le32((pattrib->seqnum << SEQ_SHT) & 0x0FFF0000);
+
+ /* offset 16 , offset 20 */
+ if (pattrib->qos_en)
+ ptxdesc->txdw4 |= cpu_to_le32(QOS);/* QoS */
+
+ /* offset 20 */
+ if (pxmitframe->agg_num > 1)
+ ptxdesc->txdw5 |= cpu_to_le32((pxmitframe->agg_num << USB_TXAGG_NUM_SHT) & 0xFF000000);
+
+ if ((pattrib->ether_type != 0x888e) &&
+ (pattrib->ether_type != 0x0806) &&
+ (pattrib->ether_type != 0x88b4) &&
+ (pattrib->dhcp_pkt != 1)) {
+ /* Non EAP & ARP & DHCP type data packet */
+
+ fill_txdesc_vcs(pattrib, &ptxdesc->txdw4);
+ fill_txdesc_phy(pattrib, &ptxdesc->txdw4);
+
+ ptxdesc->txdw4 |= cpu_to_le32(0x00000008);/* RTS Rate=24M */
+ ptxdesc->txdw5 |= cpu_to_le32(0x0001ff00);/* DATA/RTS Rate FB LMT */
+
+ if (pattrib->ht_en) {
+ if (ODM_RA_GetShortGI_8188E(&haldata->odmpriv, pattrib->mac_id))
+ ptxdesc->txdw5 |= cpu_to_le32(SGI);/* SGI */
+ }
+ data_rate = ODM_RA_GetDecisionRate_8188E(&haldata->odmpriv, pattrib->mac_id);
+ ptxdesc->txdw5 |= cpu_to_le32(data_rate & 0x3F);
+ pwr_status = ODM_RA_GetHwPwrStatus_8188E(&haldata->odmpriv, pattrib->mac_id);
+ ptxdesc->txdw4 |= cpu_to_le32((pwr_status & 0x7) << PWR_STATUS_SHT);
+ } else {
+ /* EAP data packet and ARP packet and DHCP. */
+ /* Use the 1M data rate to send the EAP/ARP packet. */
+ /* This will maybe make the handshake smooth. */
+ ptxdesc->txdw2 |= cpu_to_le32(AGG_BK);/* AGG BK */
+ if (pmlmeinfo->preamble_mode == PREAMBLE_SHORT)
+ ptxdesc->txdw4 |= cpu_to_le32(BIT(24));/* DATA_SHORT */
+ ptxdesc->txdw5 |= cpu_to_le32(MRateToHwRate(pmlmeext->tx_rate));
+ }
+ } else if ((pxmitframe->frame_tag&0x0f) == MGNT_FRAMETAG) {
+ /* offset 4 */
+ ptxdesc->txdw1 |= cpu_to_le32(pattrib->mac_id & 0x3f);
+
+ qsel = (uint)(pattrib->qsel&0x0000001f);
+ ptxdesc->txdw1 |= cpu_to_le32((qsel << QSEL_SHT) & 0x00001f00);
+
+ ptxdesc->txdw1 |= cpu_to_le32((pattrib->raid << RATE_ID_SHT) & 0x000f0000);
+
+ /* offset 8 */
+ /* CCX-TXRPT ack for xmit mgmt frames. */
+ if (pxmitframe->ack_report)
+ ptxdesc->txdw2 |= cpu_to_le32(BIT(19));
+
+ /* offset 12 */
+ ptxdesc->txdw3 |= cpu_to_le32((pattrib->seqnum<<SEQ_SHT)&0x0FFF0000);
+
+ /* offset 20 */
+ ptxdesc->txdw5 |= cpu_to_le32(RTY_LMT_EN);/* retry limit enable */
+ if (pattrib->retry_ctrl)
+ ptxdesc->txdw5 |= cpu_to_le32(0x00180000);/* retry limit = 6 */
+ else
+ ptxdesc->txdw5 |= cpu_to_le32(0x00300000);/* retry limit = 12 */
+
+ ptxdesc->txdw5 |= cpu_to_le32(MRateToHwRate(pmlmeext->tx_rate));
+ } else if ((pxmitframe->frame_tag&0x0f) == TXAGG_FRAMETAG) {
+ DBG_88E("pxmitframe->frame_tag == TXAGG_FRAMETAG\n");
+ } else {
+ DBG_88E("pxmitframe->frame_tag = %d\n", pxmitframe->frame_tag);
+
+ /* offset 4 */
+ ptxdesc->txdw1 |= cpu_to_le32((4) & 0x3f);/* CAM_ID(MAC_ID) */
+
+ ptxdesc->txdw1 |= cpu_to_le32((6 << RATE_ID_SHT) & 0x000f0000);/* raid */
+
+ /* offset 8 */
+
+ /* offset 12 */
+ ptxdesc->txdw3 |= cpu_to_le32((pattrib->seqnum<<SEQ_SHT)&0x0fff0000);
+
+ /* offset 20 */
+ ptxdesc->txdw5 |= cpu_to_le32(MRateToHwRate(pmlmeext->tx_rate));
+ }
+
+ /* 2009.11.05. tynli_test. Suggested by SD4 Filen for FW LPS. */
+ /* (1) The sequence number of each non-Qos frame / broadcast / multicast / */
+ /* mgnt frame should be controlled by Hw because Fw will also send null data */
+ /* which we cannot control when Fw LPS enable. */
+ /* --> default enable non-Qos data sequense number. 2010.06.23. by tynli. */
+ /* (2) Enable HW SEQ control for beacon packet, because we use Hw beacon. */
+ /* (3) Use HW Qos SEQ to control the seq num of Ext port non-Qos packets. */
+ /* 2010.06.23. Added by tynli. */
+ if (!pattrib->qos_en) {
+ ptxdesc->txdw3 |= cpu_to_le32(EN_HWSEQ); /* Hw set sequence number */
+ ptxdesc->txdw4 |= cpu_to_le32(HW_SSN); /* Hw set sequence number */
+ }
+
+ rtl88eu_dm_set_tx_ant_by_tx_info(&haldata->odmpriv, pmem,
+ pattrib->mac_id);
+
+ rtl8188eu_cal_txdesc_chksum(ptxdesc);
+ _dbg_dump_tx_info(adapt, pxmitframe->frame_tag, ptxdesc);
+ return pull;
+}
+
+/* for non-agg data frame or management frame */
+static s32 rtw_dump_xframe(struct adapter *adapt, struct xmit_frame *pxmitframe)
+{
+ s32 ret = _SUCCESS;
+ s32 inner_ret = _SUCCESS;
+ int t, sz, w_sz, pull = 0;
+ u8 *mem_addr;
+ u32 ff_hwaddr;
+ struct xmit_buf *pxmitbuf = pxmitframe->pxmitbuf;
+ struct pkt_attrib *pattrib = &pxmitframe->attrib;
+ struct xmit_priv *pxmitpriv = &adapt->xmitpriv;
+ struct security_priv *psecuritypriv = &adapt->securitypriv;
+ if ((pxmitframe->frame_tag == DATA_FRAMETAG) &&
+ (pxmitframe->attrib.ether_type != 0x0806) &&
+ (pxmitframe->attrib.ether_type != 0x888e) &&
+ (pxmitframe->attrib.ether_type != 0x88b4) &&
+ (pxmitframe->attrib.dhcp_pkt != 1))
+ rtw_issue_addbareq_cmd(adapt, pxmitframe);
+ mem_addr = pxmitframe->buf_addr;
+
+ RT_TRACE(_module_rtl871x_xmit_c_, _drv_info_, ("rtw_dump_xframe()\n"));
+
+ for (t = 0; t < pattrib->nr_frags; t++) {
+ if (inner_ret != _SUCCESS && ret == _SUCCESS)
+ ret = _FAIL;
+
+ if (t != (pattrib->nr_frags - 1)) {
+ RT_TRACE(_module_rtl871x_xmit_c_, _drv_err_, ("pattrib->nr_frags=%d\n", pattrib->nr_frags));
+
+ sz = pxmitpriv->frag_len;
+ sz = sz - 4 - (psecuritypriv->sw_encrypt ? 0 : pattrib->icv_len);
+ } else {
+ /* no frag */
+ sz = pattrib->last_txcmdsz;
+ }
+
+ pull = update_txdesc(pxmitframe, mem_addr, sz, false);
+
+ if (pull) {
+ mem_addr += PACKET_OFFSET_SZ; /* pull txdesc head */
+ pxmitframe->buf_addr = mem_addr;
+ w_sz = sz + TXDESC_SIZE;
+ } else {
+ w_sz = sz + TXDESC_SIZE + PACKET_OFFSET_SZ;
+ }
+ ff_hwaddr = rtw_get_ff_hwaddr(pxmitframe);
+
+ inner_ret = usb_write_port(adapt, ff_hwaddr, w_sz, (unsigned char *)pxmitbuf);
+
+ rtw_count_tx_stats(adapt, pxmitframe, sz);
+
+ RT_TRACE(_module_rtl871x_xmit_c_, _drv_info_, ("rtw_write_port, w_sz=%d\n", w_sz));
+
+ mem_addr += w_sz;
+
+ mem_addr = (u8 *)round_up((size_t)mem_addr, 4);
+ }
+
+ rtw_free_xmitframe(pxmitpriv, pxmitframe);
+
+ if (ret != _SUCCESS)
+ rtw_sctx_done_err(&pxmitbuf->sctx, RTW_SCTX_DONE_UNKNOWN);
+
+ return ret;
+}
+
+static u32 xmitframe_need_length(struct xmit_frame *pxmitframe)
+{
+ struct pkt_attrib *pattrib = &pxmitframe->attrib;
+
+ u32 len = 0;
+
+ /* no consider fragement */
+ len = pattrib->hdrlen + pattrib->iv_len +
+ SNAP_SIZE + sizeof(u16) +
+ pattrib->pktlen +
+ ((pattrib->bswenc) ? pattrib->icv_len : 0);
+
+ if (pattrib->encrypt == _TKIP_)
+ len += 8;
+
+ return len;
+}
+
+s32 rtl8188eu_xmitframe_complete(struct adapter *adapt, struct xmit_priv *pxmitpriv, struct xmit_buf *pxmitbuf)
+{
+ struct hal_data_8188e *haldata = GET_HAL_DATA(adapt);
+ struct xmit_frame *pxmitframe = NULL;
+ struct xmit_frame *pfirstframe = NULL;
+
+ /* aggregate variable */
+ struct hw_xmit *phwxmit;
+ struct sta_info *psta = NULL;
+ struct tx_servq *ptxservq = NULL;
+
+ struct list_head *xmitframe_plist = NULL, *xmitframe_phead = NULL;
+
+ u32 pbuf; /* next pkt address */
+ u32 pbuf_tail; /* last pkt tail */
+ u32 len; /* packet length, except TXDESC_SIZE and PKT_OFFSET */
+
+ u32 bulksize = haldata->UsbBulkOutSize;
+ u8 desc_cnt;
+ u32 bulkptr;
+
+ /* dump frame variable */
+ u32 ff_hwaddr;
+
+ RT_TRACE(_module_rtl8192c_xmit_c_, _drv_info_, ("+xmitframe_complete\n"));
+
+ /* check xmitbuffer is ok */
+ if (pxmitbuf == NULL) {
+ pxmitbuf = rtw_alloc_xmitbuf(pxmitpriv);
+ if (pxmitbuf == NULL)
+ return false;
+ }
+
+ /* 3 1. pick up first frame */
+ do {
+ rtw_free_xmitframe(pxmitpriv, pxmitframe);
+
+ pxmitframe = rtw_dequeue_xframe(pxmitpriv, pxmitpriv->hwxmits, pxmitpriv->hwxmit_entry);
+ if (pxmitframe == NULL) {
+ /* no more xmit frame, release xmit buffer */
+ rtw_free_xmitbuf(pxmitpriv, pxmitbuf);
+ return false;
+ }
+
+ pxmitframe->pxmitbuf = pxmitbuf;
+ pxmitframe->buf_addr = pxmitbuf->pbuf;
+ pxmitbuf->priv_data = pxmitframe;
+
+ pxmitframe->agg_num = 1; /* alloc xmitframe should assign to 1. */
+ pxmitframe->pkt_offset = 1; /* first frame of aggregation, reserve offset */
+
+ rtw_xmitframe_coalesce(adapt, pxmitframe->pkt, pxmitframe);
+
+ /* always return ndis_packet after rtw_xmitframe_coalesce */
+ rtw_os_xmit_complete(adapt, pxmitframe);
+
+ break;
+ } while (1);
+
+ /* 3 2. aggregate same priority and same DA(AP or STA) frames */
+ pfirstframe = pxmitframe;
+ len = xmitframe_need_length(pfirstframe) + TXDESC_SIZE + (pfirstframe->pkt_offset*PACKET_OFFSET_SZ);
+ pbuf_tail = len;
+ pbuf = round_up(pbuf_tail, 8);
+
+ /* check pkt amount in one bulk */
+ desc_cnt = 0;
+ bulkptr = bulksize;
+ if (pbuf < bulkptr) {
+ desc_cnt++;
+ } else {
+ desc_cnt = 0;
+ bulkptr = ((pbuf / bulksize) + 1) * bulksize; /* round to next bulksize */
+ }
+
+ /* dequeue same priority packet from station tx queue */
+ psta = pfirstframe->attrib.psta;
+ switch (pfirstframe->attrib.priority) {
+ case 1:
+ case 2:
+ ptxservq = &(psta->sta_xmitpriv.bk_q);
+ phwxmit = pxmitpriv->hwxmits + 3;
+ break;
+ case 4:
+ case 5:
+ ptxservq = &(psta->sta_xmitpriv.vi_q);
+ phwxmit = pxmitpriv->hwxmits + 1;
+ break;
+ case 6:
+ case 7:
+ ptxservq = &(psta->sta_xmitpriv.vo_q);
+ phwxmit = pxmitpriv->hwxmits;
+ break;
+ case 0:
+ case 3:
+ default:
+ ptxservq = &(psta->sta_xmitpriv.be_q);
+ phwxmit = pxmitpriv->hwxmits + 2;
+ break;
+ }
+ spin_lock_bh(&pxmitpriv->lock);
+
+ xmitframe_phead = get_list_head(&ptxservq->sta_pending);
+ xmitframe_plist = xmitframe_phead->next;
+
+ while (xmitframe_phead != xmitframe_plist) {
+ pxmitframe = container_of(xmitframe_plist, struct xmit_frame, list);
+ xmitframe_plist = xmitframe_plist->next;
+
+ pxmitframe->agg_num = 0; /* not first frame of aggregation */
+ pxmitframe->pkt_offset = 0; /* not first frame of aggregation, no need to reserve offset */
+
+ len = xmitframe_need_length(pxmitframe) + TXDESC_SIZE + (pxmitframe->pkt_offset*PACKET_OFFSET_SZ);
+
+ if (round_up(pbuf + len, 8) > MAX_XMITBUF_SZ) {
+ pxmitframe->agg_num = 1;
+ pxmitframe->pkt_offset = 1;
+ break;
+ }
+ list_del_init(&pxmitframe->list);
+ ptxservq->qcnt--;
+ phwxmit->accnt--;
+
+ pxmitframe->buf_addr = pxmitbuf->pbuf + pbuf;
+
+ rtw_xmitframe_coalesce(adapt, pxmitframe->pkt, pxmitframe);
+ /* always return ndis_packet after rtw_xmitframe_coalesce */
+ rtw_os_xmit_complete(adapt, pxmitframe);
+
+ /* (len - TXDESC_SIZE) == pxmitframe->attrib.last_txcmdsz */
+ update_txdesc(pxmitframe, pxmitframe->buf_addr, pxmitframe->attrib.last_txcmdsz, true);
+
+ /* don't need xmitframe any more */
+ rtw_free_xmitframe(pxmitpriv, pxmitframe);
+
+ /* handle pointer and stop condition */
+ pbuf_tail = pbuf + len;
+ pbuf = round_up(pbuf_tail, 8);
+
+ pfirstframe->agg_num++;
+ if (MAX_TX_AGG_PACKET_NUMBER == pfirstframe->agg_num)
+ break;
+
+ if (pbuf < bulkptr) {
+ desc_cnt++;
+ if (desc_cnt == haldata->UsbTxAggDescNum)
+ break;
+ } else {
+ desc_cnt = 0;
+ bulkptr = ((pbuf / bulksize) + 1) * bulksize;
+ }
+ } /* end while (aggregate same priority and same DA(AP or STA) frames) */
+
+ if (list_empty(&ptxservq->sta_pending.queue))
+ list_del_init(&ptxservq->tx_pending);
+
+ spin_unlock_bh(&pxmitpriv->lock);
+ if ((pfirstframe->attrib.ether_type != 0x0806) &&
+ (pfirstframe->attrib.ether_type != 0x888e) &&
+ (pfirstframe->attrib.ether_type != 0x88b4) &&
+ (pfirstframe->attrib.dhcp_pkt != 1))
+ rtw_issue_addbareq_cmd(adapt, pfirstframe);
+ /* 3 3. update first frame txdesc */
+ if ((pbuf_tail % bulksize) == 0) {
+ /* remove pkt_offset */
+ pbuf_tail -= PACKET_OFFSET_SZ;
+ pfirstframe->buf_addr += PACKET_OFFSET_SZ;
+ pfirstframe->pkt_offset--;
+ }
+
+ update_txdesc(pfirstframe, pfirstframe->buf_addr, pfirstframe->attrib.last_txcmdsz, true);
+
+ /* 3 4. write xmit buffer to USB FIFO */
+ ff_hwaddr = rtw_get_ff_hwaddr(pfirstframe);
+ usb_write_port(adapt, ff_hwaddr, pbuf_tail, (u8 *)pxmitbuf);
+
+ /* 3 5. update statisitc */
+ pbuf_tail -= (pfirstframe->agg_num * TXDESC_SIZE);
+ pbuf_tail -= (pfirstframe->pkt_offset * PACKET_OFFSET_SZ);
+
+ rtw_count_tx_stats(adapt, pfirstframe, pbuf_tail);
+
+ rtw_free_xmitframe(pxmitpriv, pfirstframe);
+
+ return true;
+}
+
+static s32 xmitframe_direct(struct adapter *adapt, struct xmit_frame *pxmitframe)
+{
+ s32 res = _SUCCESS;
+
+ res = rtw_xmitframe_coalesce(adapt, pxmitframe->pkt, pxmitframe);
+ if (res == _SUCCESS)
+ rtw_dump_xframe(adapt, pxmitframe);
+ else
+ DBG_88E("==> %s xmitframe_coalsece failed\n", __func__);
+ return res;
+}
+
+/*
+ * Return
+ * true dump packet directly
+ * false enqueue packet
+ */
+static s32 pre_xmitframe(struct adapter *adapt, struct xmit_frame *pxmitframe)
+{
+ s32 res;
+ struct xmit_buf *pxmitbuf = NULL;
+ struct xmit_priv *pxmitpriv = &adapt->xmitpriv;
+ struct pkt_attrib *pattrib = &pxmitframe->attrib;
+ struct mlme_priv *pmlmepriv = &adapt->mlmepriv;
+
+ spin_lock_bh(&pxmitpriv->lock);
+
+ if (rtw_txframes_sta_ac_pending(adapt, pattrib) > 0)
+ goto enqueue;
+
+ if (check_fwstate(pmlmepriv, _FW_UNDER_SURVEY|_FW_UNDER_LINKING) == true)
+ goto enqueue;
+
+ pxmitbuf = rtw_alloc_xmitbuf(pxmitpriv);
+ if (pxmitbuf == NULL)
+ goto enqueue;
+
+ spin_unlock_bh(&pxmitpriv->lock);
+
+ pxmitframe->pxmitbuf = pxmitbuf;
+ pxmitframe->buf_addr = pxmitbuf->pbuf;
+ pxmitbuf->priv_data = pxmitframe;
+
+ if (xmitframe_direct(adapt, pxmitframe) != _SUCCESS) {
+ rtw_free_xmitbuf(pxmitpriv, pxmitbuf);
+ rtw_free_xmitframe(pxmitpriv, pxmitframe);
+ }
+
+ return true;
+
+enqueue:
+ res = rtw_xmitframe_enqueue(adapt, pxmitframe);
+ spin_unlock_bh(&pxmitpriv->lock);
+
+ if (res != _SUCCESS) {
+ RT_TRACE(_module_xmit_osdep_c_, _drv_err_, ("pre_xmitframe: enqueue xmitframe fail\n"));
+ rtw_free_xmitframe(pxmitpriv, pxmitframe);
+
+ /* Trick, make the statistics correct */
+ pxmitpriv->tx_pkts--;
+ pxmitpriv->tx_drop++;
+ return true;
+ }
+
+ return false;
+}
+
+s32 rtl8188eu_mgnt_xmit(struct adapter *adapt, struct xmit_frame *pmgntframe)
+{
+ return rtw_dump_xframe(adapt, pmgntframe);
+}
+
+/*
+ * Return
+ * true dump packet directly ok
+ * false temporary can't transmit packets to hardware
+ */
+s32 rtl8188eu_hal_xmit(struct adapter *adapt, struct xmit_frame *pxmitframe)
+{
+ return pre_xmitframe(adapt, pxmitframe);
+}
diff --git a/drivers/staging/rtl8188eu/hal/usb_halinit.c b/drivers/staging/rtl8188eu/hal/usb_halinit.c
new file mode 100644
index 000000000..7b01d5aa6
--- /dev/null
+++ b/drivers/staging/rtl8188eu/hal/usb_halinit.c
@@ -0,0 +1,2213 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2011 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ *
+ ******************************************************************************/
+#define _HCI_HAL_INIT_C_
+
+#include <osdep_service.h>
+#include <drv_types.h>
+#include <rtw_efuse.h>
+#include <fw.h>
+#include <rtl8188e_hal.h>
+#include <rtl8188e_led.h>
+#include <rtw_iol.h>
+#include <usb_hal.h>
+#include <phy.h>
+
+#define HAL_BB_ENABLE 1
+
+static void _ConfigNormalChipOutEP_8188E(struct adapter *adapt, u8 NumOutPipe)
+{
+ struct hal_data_8188e *haldata = GET_HAL_DATA(adapt);
+
+ switch (NumOutPipe) {
+ case 3:
+ haldata->OutEpQueueSel = TX_SELE_HQ | TX_SELE_LQ | TX_SELE_NQ;
+ haldata->OutEpNumber = 3;
+ break;
+ case 2:
+ haldata->OutEpQueueSel = TX_SELE_HQ | TX_SELE_NQ;
+ haldata->OutEpNumber = 2;
+ break;
+ case 1:
+ haldata->OutEpQueueSel = TX_SELE_HQ;
+ haldata->OutEpNumber = 1;
+ break;
+ default:
+ break;
+ }
+ DBG_88E("%s OutEpQueueSel(0x%02x), OutEpNumber(%d)\n", __func__, haldata->OutEpQueueSel, haldata->OutEpNumber);
+}
+
+static bool HalUsbSetQueuePipeMapping8188EUsb(struct adapter *adapt, u8 NumInPipe, u8 NumOutPipe)
+{
+ struct hal_data_8188e *haldata = GET_HAL_DATA(adapt);
+ bool result = false;
+
+ _ConfigNormalChipOutEP_8188E(adapt, NumOutPipe);
+
+ /* Normal chip with one IN and one OUT doesn't have interrupt IN EP. */
+ if (1 == haldata->OutEpNumber) {
+ if (1 != NumInPipe)
+ return result;
+ }
+
+ /* All config other than above support one Bulk IN and one Interrupt IN. */
+
+ result = Hal_MappingOutPipe(adapt, NumOutPipe);
+
+ return result;
+}
+
+static void rtl8188eu_interface_configure(struct adapter *adapt)
+{
+ struct hal_data_8188e *haldata = GET_HAL_DATA(adapt);
+ struct dvobj_priv *pdvobjpriv = adapter_to_dvobj(adapt);
+
+ if (pdvobjpriv->ishighspeed)
+ haldata->UsbBulkOutSize = USB_HIGH_SPEED_BULK_SIZE;/* 512 bytes */
+ else
+ haldata->UsbBulkOutSize = USB_FULL_SPEED_BULK_SIZE;/* 64 bytes */
+
+ haldata->interfaceIndex = pdvobjpriv->InterfaceNumber;
+
+ haldata->UsbTxAggMode = 1;
+ haldata->UsbTxAggDescNum = 0x6; /* only 4 bits */
+
+ haldata->UsbRxAggMode = USB_RX_AGG_DMA;/* USB_RX_AGG_DMA; */
+ haldata->UsbRxAggBlockCount = 8; /* unit : 512b */
+ haldata->UsbRxAggBlockTimeout = 0x6;
+ haldata->UsbRxAggPageCount = 48; /* uint :128 b 0x0A; 10 = MAX_RX_DMA_BUFFER_SIZE/2/haldata->UsbBulkOutSize */
+ haldata->UsbRxAggPageTimeout = 0x4; /* 6, absolute time = 34ms/(2^6) */
+
+ HalUsbSetQueuePipeMapping8188EUsb(adapt,
+ pdvobjpriv->RtNumInPipes, pdvobjpriv->RtNumOutPipes);
+}
+
+static u32 rtl8188eu_InitPowerOn(struct adapter *adapt)
+{
+ u16 value16;
+ /* HW Power on sequence */
+ struct hal_data_8188e *haldata = GET_HAL_DATA(adapt);
+ if (haldata->bMacPwrCtrlOn)
+ return _SUCCESS;
+
+ if (!rtl88eu_pwrseqcmdparsing(adapt, PWR_CUT_ALL_MSK,
+ PWR_FAB_ALL_MSK, PWR_INTF_USB_MSK,
+ Rtl8188E_NIC_PWR_ON_FLOW)) {
+ DBG_88E(KERN_ERR "%s: run power on flow fail\n", __func__);
+ return _FAIL;
+ }
+
+ /* Enable MAC DMA/WMAC/SCHEDULE/SEC block */
+ /* Set CR bit10 to enable 32k calibration. Suggested by SD1 Gimmy. Added by tynli. 2011.08.31. */
+ usb_write16(adapt, REG_CR, 0x00); /* suggseted by zhouzhou, by page, 20111230 */
+
+ /* Enable MAC DMA/WMAC/SCHEDULE/SEC block */
+ value16 = usb_read16(adapt, REG_CR);
+ value16 |= (HCI_TXDMA_EN | HCI_RXDMA_EN | TXDMA_EN | RXDMA_EN
+ | PROTOCOL_EN | SCHEDULE_EN | ENSEC | CALTMR_EN);
+ /* for SDIO - Set CR bit10 to enable 32k calibration. Suggested by SD1 Gimmy. Added by tynli. 2011.08.31. */
+
+ usb_write16(adapt, REG_CR, value16);
+ haldata->bMacPwrCtrlOn = true;
+
+ return _SUCCESS;
+}
+
+/* Shall USB interface init this? */
+static void _InitInterrupt(struct adapter *Adapter)
+{
+ u32 imr, imr_ex;
+ u8 usb_opt;
+ struct hal_data_8188e *haldata = GET_HAL_DATA(Adapter);
+
+ /* HISR write one to clear */
+ usb_write32(Adapter, REG_HISR_88E, 0xFFFFFFFF);
+ /* HIMR - */
+ imr = IMR_PSTIMEOUT_88E | IMR_TBDER_88E | IMR_CPWM_88E | IMR_CPWM2_88E;
+ usb_write32(Adapter, REG_HIMR_88E, imr);
+ haldata->IntrMask[0] = imr;
+
+ imr_ex = IMR_TXERR_88E | IMR_RXERR_88E | IMR_TXFOVW_88E | IMR_RXFOVW_88E;
+ usb_write32(Adapter, REG_HIMRE_88E, imr_ex);
+ haldata->IntrMask[1] = imr_ex;
+
+ /* REG_USB_SPECIAL_OPTION - BIT(4) */
+ /* 0; Use interrupt endpoint to upload interrupt pkt */
+ /* 1; Use bulk endpoint to upload interrupt pkt, */
+ usb_opt = usb_read8(Adapter, REG_USB_SPECIAL_OPTION);
+
+ if (!adapter_to_dvobj(Adapter)->ishighspeed)
+ usb_opt = usb_opt & (~INT_BULK_SEL);
+ else
+ usb_opt = usb_opt | (INT_BULK_SEL);
+
+ usb_write8(Adapter, REG_USB_SPECIAL_OPTION, usb_opt);
+}
+
+static void _InitQueueReservedPage(struct adapter *Adapter)
+{
+ struct hal_data_8188e *haldata = GET_HAL_DATA(Adapter);
+ struct registry_priv *pregistrypriv = &Adapter->registrypriv;
+ u32 numHQ = 0;
+ u32 numLQ = 0;
+ u32 numNQ = 0;
+ u32 numPubQ;
+ u32 value32;
+ u8 value8;
+ bool bWiFiConfig = pregistrypriv->wifi_spec;
+
+ if (bWiFiConfig) {
+ if (haldata->OutEpQueueSel & TX_SELE_HQ)
+ numHQ = 0x29;
+
+ if (haldata->OutEpQueueSel & TX_SELE_LQ)
+ numLQ = 0x1C;
+
+ /* NOTE: This step shall be proceed before writting REG_RQPN. */
+ if (haldata->OutEpQueueSel & TX_SELE_NQ)
+ numNQ = 0x1C;
+ value8 = (u8)_NPQ(numNQ);
+ usb_write8(Adapter, REG_RQPN_NPQ, value8);
+
+ numPubQ = 0xA8 - numHQ - numLQ - numNQ;
+
+ /* TX DMA */
+ value32 = _HPQ(numHQ) | _LPQ(numLQ) | _PUBQ(numPubQ) | LD_RQPN;
+ usb_write32(Adapter, REG_RQPN, value32);
+ } else {
+ usb_write16(Adapter, REG_RQPN_NPQ, 0x0000);/* Just follow MP Team,??? Georgia 03/28 */
+ usb_write16(Adapter, REG_RQPN_NPQ, 0x0d);
+ usb_write32(Adapter, REG_RQPN, 0x808E000d);/* reserve 7 page for LPS */
+ }
+}
+
+static void _InitTxBufferBoundary(struct adapter *Adapter, u8 txpktbuf_bndy)
+{
+ usb_write8(Adapter, REG_TXPKTBUF_BCNQ_BDNY, txpktbuf_bndy);
+ usb_write8(Adapter, REG_TXPKTBUF_MGQ_BDNY, txpktbuf_bndy);
+ usb_write8(Adapter, REG_TXPKTBUF_WMAC_LBK_BF_HD, txpktbuf_bndy);
+ usb_write8(Adapter, REG_TRXFF_BNDY, txpktbuf_bndy);
+ usb_write8(Adapter, REG_TDECTRL+1, txpktbuf_bndy);
+}
+
+static void _InitPageBoundary(struct adapter *Adapter)
+{
+ /* RX Page Boundary */
+ /* */
+ u16 rxff_bndy = MAX_RX_DMA_BUFFER_SIZE_88E-1;
+
+ usb_write16(Adapter, (REG_TRXFF_BNDY + 2), rxff_bndy);
+}
+
+static void _InitNormalChipRegPriority(struct adapter *Adapter, u16 beQ,
+ u16 bkQ, u16 viQ, u16 voQ, u16 mgtQ,
+ u16 hiQ)
+{
+ u16 value16 = (usb_read16(Adapter, REG_TRXDMA_CTRL) & 0x7);
+
+ value16 |= _TXDMA_BEQ_MAP(beQ) | _TXDMA_BKQ_MAP(bkQ) |
+ _TXDMA_VIQ_MAP(viQ) | _TXDMA_VOQ_MAP(voQ) |
+ _TXDMA_MGQ_MAP(mgtQ) | _TXDMA_HIQ_MAP(hiQ);
+
+ usb_write16(Adapter, REG_TRXDMA_CTRL, value16);
+}
+
+static void _InitNormalChipOneOutEpPriority(struct adapter *Adapter)
+{
+ struct hal_data_8188e *haldata = GET_HAL_DATA(Adapter);
+
+ u16 value = 0;
+ switch (haldata->OutEpQueueSel) {
+ case TX_SELE_HQ:
+ value = QUEUE_HIGH;
+ break;
+ case TX_SELE_LQ:
+ value = QUEUE_LOW;
+ break;
+ case TX_SELE_NQ:
+ value = QUEUE_NORMAL;
+ break;
+ default:
+ break;
+ }
+ _InitNormalChipRegPriority(Adapter, value, value, value, value,
+ value, value);
+}
+
+static void _InitNormalChipTwoOutEpPriority(struct adapter *Adapter)
+{
+ struct hal_data_8188e *haldata = GET_HAL_DATA(Adapter);
+ struct registry_priv *pregistrypriv = &Adapter->registrypriv;
+ u16 beQ, bkQ, viQ, voQ, mgtQ, hiQ;
+ u16 valueHi = 0;
+ u16 valueLow = 0;
+
+ switch (haldata->OutEpQueueSel) {
+ case (TX_SELE_HQ | TX_SELE_LQ):
+ valueHi = QUEUE_HIGH;
+ valueLow = QUEUE_LOW;
+ break;
+ case (TX_SELE_NQ | TX_SELE_LQ):
+ valueHi = QUEUE_NORMAL;
+ valueLow = QUEUE_LOW;
+ break;
+ case (TX_SELE_HQ | TX_SELE_NQ):
+ valueHi = QUEUE_HIGH;
+ valueLow = QUEUE_NORMAL;
+ break;
+ default:
+ break;
+ }
+
+ if (!pregistrypriv->wifi_spec) {
+ beQ = valueLow;
+ bkQ = valueLow;
+ viQ = valueHi;
+ voQ = valueHi;
+ mgtQ = valueHi;
+ hiQ = valueHi;
+ } else {/* for WMM ,CONFIG_OUT_EP_WIFI_MODE */
+ beQ = valueLow;
+ bkQ = valueHi;
+ viQ = valueHi;
+ voQ = valueLow;
+ mgtQ = valueHi;
+ hiQ = valueHi;
+ }
+ _InitNormalChipRegPriority(Adapter, beQ, bkQ, viQ, voQ, mgtQ, hiQ);
+}
+
+static void _InitNormalChipThreeOutEpPriority(struct adapter *Adapter)
+{
+ struct registry_priv *pregistrypriv = &Adapter->registrypriv;
+ u16 beQ, bkQ, viQ, voQ, mgtQ, hiQ;
+
+ if (!pregistrypriv->wifi_spec) {/* typical setting */
+ beQ = QUEUE_LOW;
+ bkQ = QUEUE_LOW;
+ viQ = QUEUE_NORMAL;
+ voQ = QUEUE_HIGH;
+ mgtQ = QUEUE_HIGH;
+ hiQ = QUEUE_HIGH;
+ } else {/* for WMM */
+ beQ = QUEUE_LOW;
+ bkQ = QUEUE_NORMAL;
+ viQ = QUEUE_NORMAL;
+ voQ = QUEUE_HIGH;
+ mgtQ = QUEUE_HIGH;
+ hiQ = QUEUE_HIGH;
+ }
+ _InitNormalChipRegPriority(Adapter, beQ, bkQ, viQ, voQ, mgtQ, hiQ);
+}
+
+static void _InitQueuePriority(struct adapter *Adapter)
+{
+ struct hal_data_8188e *haldata = GET_HAL_DATA(Adapter);
+
+ switch (haldata->OutEpNumber) {
+ case 1:
+ _InitNormalChipOneOutEpPriority(Adapter);
+ break;
+ case 2:
+ _InitNormalChipTwoOutEpPriority(Adapter);
+ break;
+ case 3:
+ _InitNormalChipThreeOutEpPriority(Adapter);
+ break;
+ default:
+ break;
+ }
+}
+
+static void _InitNetworkType(struct adapter *Adapter)
+{
+ u32 value32;
+
+ value32 = usb_read32(Adapter, REG_CR);
+ /* TODO: use the other function to set network type */
+ value32 = (value32 & ~MASK_NETTYPE) | _NETTYPE(NT_LINK_AP);
+
+ usb_write32(Adapter, REG_CR, value32);
+}
+
+static void _InitTransferPageSize(struct adapter *Adapter)
+{
+ /* Tx page size is always 128. */
+
+ u8 value8;
+ value8 = _PSRX(PBP_128) | _PSTX(PBP_128);
+ usb_write8(Adapter, REG_PBP, value8);
+}
+
+static void _InitDriverInfoSize(struct adapter *Adapter, u8 drvInfoSize)
+{
+ usb_write8(Adapter, REG_RX_DRVINFO_SZ, drvInfoSize);
+}
+
+static void _InitWMACSetting(struct adapter *Adapter)
+{
+ struct hal_data_8188e *haldata = GET_HAL_DATA(Adapter);
+
+ haldata->ReceiveConfig = RCR_AAP | RCR_APM | RCR_AM | RCR_AB |
+ RCR_CBSSID_DATA | RCR_CBSSID_BCN |
+ RCR_APP_ICV | RCR_AMF | RCR_HTC_LOC_CTRL |
+ RCR_APP_MIC | RCR_APP_PHYSTS;
+
+ /* some REG_RCR will be modified later by phy_ConfigMACWithHeaderFile() */
+ usb_write32(Adapter, REG_RCR, haldata->ReceiveConfig);
+
+ /* Accept all multicast address */
+ usb_write32(Adapter, REG_MAR, 0xFFFFFFFF);
+ usb_write32(Adapter, REG_MAR + 4, 0xFFFFFFFF);
+}
+
+static void _InitAdaptiveCtrl(struct adapter *Adapter)
+{
+ u16 value16;
+ u32 value32;
+
+ /* Response Rate Set */
+ value32 = usb_read32(Adapter, REG_RRSR);
+ value32 &= ~RATE_BITMAP_ALL;
+ value32 |= RATE_RRSR_CCK_ONLY_1M;
+ usb_write32(Adapter, REG_RRSR, value32);
+
+ /* CF-END Threshold */
+
+ /* SIFS (used in NAV) */
+ value16 = _SPEC_SIFS_CCK(0x10) | _SPEC_SIFS_OFDM(0x10);
+ usb_write16(Adapter, REG_SPEC_SIFS, value16);
+
+ /* Retry Limit */
+ value16 = _LRL(0x30) | _SRL(0x30);
+ usb_write16(Adapter, REG_RL, value16);
+}
+
+static void _InitEDCA(struct adapter *Adapter)
+{
+ /* Set Spec SIFS (used in NAV) */
+ usb_write16(Adapter, REG_SPEC_SIFS, 0x100a);
+ usb_write16(Adapter, REG_MAC_SPEC_SIFS, 0x100a);
+
+ /* Set SIFS for CCK */
+ usb_write16(Adapter, REG_SIFS_CTX, 0x100a);
+
+ /* Set SIFS for OFDM */
+ usb_write16(Adapter, REG_SIFS_TRX, 0x100a);
+
+ /* TXOP */
+ usb_write32(Adapter, REG_EDCA_BE_PARAM, 0x005EA42B);
+ usb_write32(Adapter, REG_EDCA_BK_PARAM, 0x0000A44F);
+ usb_write32(Adapter, REG_EDCA_VI_PARAM, 0x005EA324);
+ usb_write32(Adapter, REG_EDCA_VO_PARAM, 0x002FA226);
+}
+
+static void _InitRDGSetting(struct adapter *Adapter)
+{
+ usb_write8(Adapter, REG_RD_CTRL, 0xFF);
+ usb_write16(Adapter, REG_RD_NAV_NXT, 0x200);
+ usb_write8(Adapter, REG_RD_RESP_PKT_TH, 0x05);
+}
+
+static void _InitRxSetting(struct adapter *Adapter)
+{
+ usb_write32(Adapter, REG_MACID, 0x87654321);
+ usb_write32(Adapter, 0x0700, 0x87654321);
+}
+
+static void _InitRetryFunction(struct adapter *Adapter)
+{
+ u8 value8;
+
+ value8 = usb_read8(Adapter, REG_FWHW_TXQ_CTRL);
+ value8 |= EN_AMPDU_RTY_NEW;
+ usb_write8(Adapter, REG_FWHW_TXQ_CTRL, value8);
+
+ /* Set ACK timeout */
+ usb_write8(Adapter, REG_ACKTO, 0x40);
+}
+
+/*-----------------------------------------------------------------------------
+ * Function: usb_AggSettingTxUpdate()
+ *
+ * Overview: Separate TX/RX parameters update independent for TP detection and
+ * dynamic TX/RX aggreagtion parameters update.
+ *
+ * Input: struct adapter *
+ *
+ * Output/Return: NONE
+ *
+ * Revised History:
+ * When Who Remark
+ * 12/10/2010 MHC Separate to smaller function.
+ *
+ *---------------------------------------------------------------------------*/
+static void usb_AggSettingTxUpdate(struct adapter *Adapter)
+{
+ struct hal_data_8188e *haldata = GET_HAL_DATA(Adapter);
+ u32 value32;
+
+ if (Adapter->registrypriv.wifi_spec)
+ haldata->UsbTxAggMode = false;
+
+ if (haldata->UsbTxAggMode) {
+ value32 = usb_read32(Adapter, REG_TDECTRL);
+ value32 = value32 & ~(BLK_DESC_NUM_MASK << BLK_DESC_NUM_SHIFT);
+ value32 |= ((haldata->UsbTxAggDescNum & BLK_DESC_NUM_MASK) << BLK_DESC_NUM_SHIFT);
+
+ usb_write32(Adapter, REG_TDECTRL, value32);
+ }
+} /* usb_AggSettingTxUpdate */
+
+/*-----------------------------------------------------------------------------
+ * Function: usb_AggSettingRxUpdate()
+ *
+ * Overview: Separate TX/RX parameters update independent for TP detection and
+ * dynamic TX/RX aggreagtion parameters update.
+ *
+ * Input: struct adapter *
+ *
+ * Output/Return: NONE
+ *
+ * Revised History:
+ * When Who Remark
+ * 12/10/2010 MHC Separate to smaller function.
+ *
+ *---------------------------------------------------------------------------*/
+static void
+usb_AggSettingRxUpdate(
+ struct adapter *Adapter
+ )
+{
+ struct hal_data_8188e *haldata = GET_HAL_DATA(Adapter);
+ u8 valueDMA;
+ u8 valueUSB;
+
+ valueDMA = usb_read8(Adapter, REG_TRXDMA_CTRL);
+ valueUSB = usb_read8(Adapter, REG_USB_SPECIAL_OPTION);
+
+ switch (haldata->UsbRxAggMode) {
+ case USB_RX_AGG_DMA:
+ valueDMA |= RXDMA_AGG_EN;
+ valueUSB &= ~USB_AGG_EN;
+ break;
+ case USB_RX_AGG_USB:
+ valueDMA &= ~RXDMA_AGG_EN;
+ valueUSB |= USB_AGG_EN;
+ break;
+ case USB_RX_AGG_MIX:
+ valueDMA |= RXDMA_AGG_EN;
+ valueUSB |= USB_AGG_EN;
+ break;
+ case USB_RX_AGG_DISABLE:
+ default:
+ valueDMA &= ~RXDMA_AGG_EN;
+ valueUSB &= ~USB_AGG_EN;
+ break;
+ }
+
+ usb_write8(Adapter, REG_TRXDMA_CTRL, valueDMA);
+ usb_write8(Adapter, REG_USB_SPECIAL_OPTION, valueUSB);
+
+ switch (haldata->UsbRxAggMode) {
+ case USB_RX_AGG_DMA:
+ usb_write8(Adapter, REG_RXDMA_AGG_PG_TH, haldata->UsbRxAggPageCount);
+ usb_write8(Adapter, REG_RXDMA_AGG_PG_TH+1, haldata->UsbRxAggPageTimeout);
+ break;
+ case USB_RX_AGG_USB:
+ usb_write8(Adapter, REG_USB_AGG_TH, haldata->UsbRxAggBlockCount);
+ usb_write8(Adapter, REG_USB_AGG_TO, haldata->UsbRxAggBlockTimeout);
+ break;
+ case USB_RX_AGG_MIX:
+ usb_write8(Adapter, REG_RXDMA_AGG_PG_TH, haldata->UsbRxAggPageCount);
+ usb_write8(Adapter, REG_RXDMA_AGG_PG_TH+1, (haldata->UsbRxAggPageTimeout & 0x1F));/* 0x280[12:8] */
+ usb_write8(Adapter, REG_USB_AGG_TH, haldata->UsbRxAggBlockCount);
+ usb_write8(Adapter, REG_USB_AGG_TO, haldata->UsbRxAggBlockTimeout);
+ break;
+ case USB_RX_AGG_DISABLE:
+ default:
+ /* TODO: */
+ break;
+ }
+
+ switch (PBP_128) {
+ case PBP_128:
+ haldata->HwRxPageSize = 128;
+ break;
+ case PBP_64:
+ haldata->HwRxPageSize = 64;
+ break;
+ case PBP_256:
+ haldata->HwRxPageSize = 256;
+ break;
+ case PBP_512:
+ haldata->HwRxPageSize = 512;
+ break;
+ case PBP_1024:
+ haldata->HwRxPageSize = 1024;
+ break;
+ default:
+ break;
+ }
+} /* usb_AggSettingRxUpdate */
+
+static void InitUsbAggregationSetting(struct adapter *Adapter)
+{
+ struct hal_data_8188e *haldata = GET_HAL_DATA(Adapter);
+
+ /* Tx aggregation setting */
+ usb_AggSettingTxUpdate(Adapter);
+
+ /* Rx aggregation setting */
+ usb_AggSettingRxUpdate(Adapter);
+
+ /* 201/12/10 MH Add for USB agg mode dynamic switch. */
+ haldata->UsbRxHighSpeedMode = false;
+}
+
+static void _InitBeaconParameters(struct adapter *Adapter)
+{
+ struct hal_data_8188e *haldata = GET_HAL_DATA(Adapter);
+
+ usb_write16(Adapter, REG_BCN_CTRL, 0x1010);
+
+ /* TODO: Remove these magic number */
+ usb_write16(Adapter, REG_TBTT_PROHIBIT, 0x6404);/* ms */
+ usb_write8(Adapter, REG_DRVERLYINT, DRIVER_EARLY_INT_TIME);/* 5ms */
+ usb_write8(Adapter, REG_BCNDMATIM, BCN_DMA_ATIME_INT_TIME); /* 2ms */
+
+ /* Suggested by designer timchen. Change beacon AIFS to the largest number */
+ /* beacause test chip does not contension before sending beacon. by tynli. 2009.11.03 */
+ usb_write16(Adapter, REG_BCNTCFG, 0x660F);
+
+ haldata->RegBcnCtrlVal = usb_read8(Adapter, REG_BCN_CTRL);
+ haldata->RegTxPause = usb_read8(Adapter, REG_TXPAUSE);
+ haldata->RegFwHwTxQCtrl = usb_read8(Adapter, REG_FWHW_TXQ_CTRL+2);
+ haldata->RegReg542 = usb_read8(Adapter, REG_TBTT_PROHIBIT+2);
+ haldata->RegCR_1 = usb_read8(Adapter, REG_CR+1);
+}
+
+static void _BeaconFunctionEnable(struct adapter *Adapter,
+ bool Enable, bool Linked)
+{
+ usb_write8(Adapter, REG_BCN_CTRL, (BIT4 | BIT3 | BIT1));
+
+ usb_write8(Adapter, REG_RD_CTRL+1, 0x6F);
+}
+
+/* Set CCK and OFDM Block "ON" */
+static void _BBTurnOnBlock(struct adapter *Adapter)
+{
+ phy_set_bb_reg(Adapter, rFPGA0_RFMOD, bCCKEn, 0x1);
+ phy_set_bb_reg(Adapter, rFPGA0_RFMOD, bOFDMEn, 0x1);
+}
+
+enum {
+ Antenna_Lfet = 1,
+ Antenna_Right = 2,
+};
+
+static void _InitAntenna_Selection(struct adapter *Adapter)
+{
+ struct hal_data_8188e *haldata = GET_HAL_DATA(Adapter);
+
+ if (haldata->AntDivCfg == 0)
+ return;
+ DBG_88E("==> %s ....\n", __func__);
+
+ usb_write32(Adapter, REG_LEDCFG0, usb_read32(Adapter, REG_LEDCFG0)|BIT23);
+ phy_set_bb_reg(Adapter, rFPGA0_XAB_RFParameter, BIT13, 0x01);
+
+ if (phy_query_bb_reg(Adapter, rFPGA0_XA_RFInterfaceOE, 0x300) == Antenna_A)
+ haldata->CurAntenna = Antenna_A;
+ else
+ haldata->CurAntenna = Antenna_B;
+ DBG_88E("%s,Cur_ant:(%x)%s\n", __func__, haldata->CurAntenna, (haldata->CurAntenna == Antenna_A) ? "Antenna_A" : "Antenna_B");
+}
+
+/*-----------------------------------------------------------------------------
+ * Function: HwSuspendModeEnable92Cu()
+ *
+ * Overview: HW suspend mode switch.
+ *
+ * Input: NONE
+ *
+ * Output: NONE
+ *
+ * Return: NONE
+ *
+ * Revised History:
+ * When Who Remark
+ * 08/23/2010 MHC HW suspend mode switch test..
+ *---------------------------------------------------------------------------*/
+enum rt_rf_power_state RfOnOffDetect(struct adapter *adapt)
+{
+ u8 val8;
+ enum rt_rf_power_state rfpowerstate = rf_off;
+
+ if (adapt->pwrctrlpriv.bHWPowerdown) {
+ val8 = usb_read8(adapt, REG_HSISR);
+ DBG_88E("pwrdown, 0x5c(BIT7)=%02x\n", val8);
+ rfpowerstate = (val8 & BIT7) ? rf_off : rf_on;
+ } else { /* rf on/off */
+ usb_write8(adapt, REG_MAC_PINMUX_CFG, usb_read8(adapt, REG_MAC_PINMUX_CFG)&~(BIT3));
+ val8 = usb_read8(adapt, REG_GPIO_IO_SEL);
+ DBG_88E("GPIO_IN=%02x\n", val8);
+ rfpowerstate = (val8 & BIT3) ? rf_on : rf_off;
+ }
+ return rfpowerstate;
+} /* HalDetectPwrDownMode */
+
+static u32 rtl8188eu_hal_init(struct adapter *Adapter)
+{
+ u8 value8 = 0;
+ u16 value16;
+ u8 txpktbuf_bndy;
+ u32 status = _SUCCESS;
+ struct hal_data_8188e *haldata = GET_HAL_DATA(Adapter);
+ struct pwrctrl_priv *pwrctrlpriv = &Adapter->pwrctrlpriv;
+ struct registry_priv *pregistrypriv = &Adapter->registrypriv;
+ u32 init_start_time = jiffies;
+
+ #define HAL_INIT_PROFILE_TAG(stage) do {} while (0)
+
+
+ HAL_INIT_PROFILE_TAG(HAL_INIT_STAGES_BEGIN);
+
+ if (Adapter->pwrctrlpriv.bkeepfwalive) {
+
+ if (haldata->odmpriv.RFCalibrateInfo.bIQKInitialized) {
+ rtl88eu_phy_iq_calibrate(Adapter, true);
+ } else {
+ rtl88eu_phy_iq_calibrate(Adapter, false);
+ haldata->odmpriv.RFCalibrateInfo.bIQKInitialized = true;
+ }
+
+ ODM_TXPowerTrackingCheck(&haldata->odmpriv);
+ rtl88eu_phy_lc_calibrate(Adapter);
+
+ goto exit;
+ }
+
+ HAL_INIT_PROFILE_TAG(HAL_INIT_STAGES_INIT_PW_ON);
+ status = rtl8188eu_InitPowerOn(Adapter);
+ if (status == _FAIL) {
+ RT_TRACE(_module_hci_hal_init_c_, _drv_err_, ("Failed to init power on!\n"));
+ goto exit;
+ }
+
+ /* Save target channel */
+ haldata->CurrentChannel = 6;/* default set to 6 */
+
+ if (pwrctrlpriv->reg_rfoff) {
+ pwrctrlpriv->rf_pwrstate = rf_off;
+ }
+
+ /* 2010/08/09 MH We need to check if we need to turnon or off RF after detecting */
+ /* HW GPIO pin. Before PHY_RFConfig8192C. */
+ /* 2010/08/26 MH If Efuse does not support sective suspend then disable the function. */
+
+ if (!pregistrypriv->wifi_spec) {
+ txpktbuf_bndy = TX_PAGE_BOUNDARY_88E;
+ } else {
+ /* for WMM */
+ txpktbuf_bndy = WMM_NORMAL_TX_PAGE_BOUNDARY_88E;
+ }
+
+ HAL_INIT_PROFILE_TAG(HAL_INIT_STAGES_MISC01);
+ _InitQueueReservedPage(Adapter);
+ _InitQueuePriority(Adapter);
+ _InitPageBoundary(Adapter);
+ _InitTransferPageSize(Adapter);
+
+ _InitTxBufferBoundary(Adapter, 0);
+
+ HAL_INIT_PROFILE_TAG(HAL_INIT_STAGES_DOWNLOAD_FW);
+ if (Adapter->registrypriv.mp_mode == 1) {
+ _InitRxSetting(Adapter);
+ Adapter->bFWReady = false;
+ haldata->fw_ractrl = false;
+ } else {
+ status = rtl88eu_download_fw(Adapter);
+
+ if (status) {
+ DBG_88E("%s: Download Firmware failed!!\n", __func__);
+ Adapter->bFWReady = false;
+ haldata->fw_ractrl = false;
+ return status;
+ } else {
+ RT_TRACE(_module_hci_hal_init_c_, _drv_info_, ("Initializeadapt8192CSdio(): Download Firmware Success!!\n"));
+ Adapter->bFWReady = true;
+ haldata->fw_ractrl = false;
+ }
+ }
+ rtl8188e_InitializeFirmwareVars(Adapter);
+
+ rtl88eu_phy_mac_config(Adapter);
+
+ rtl88eu_phy_bb_config(Adapter);
+
+ rtl88eu_phy_rf_config(Adapter);
+
+ HAL_INIT_PROFILE_TAG(HAL_INIT_STAGES_EFUSE_PATCH);
+ status = rtl8188e_iol_efuse_patch(Adapter);
+ if (status == _FAIL) {
+ DBG_88E("%s rtl8188e_iol_efuse_patch failed\n", __func__);
+ goto exit;
+ }
+
+ _InitTxBufferBoundary(Adapter, txpktbuf_bndy);
+
+ HAL_INIT_PROFILE_TAG(HAL_INIT_STAGES_INIT_LLTT);
+ status = InitLLTTable(Adapter, txpktbuf_bndy);
+ if (status == _FAIL) {
+ RT_TRACE(_module_hci_hal_init_c_, _drv_err_, ("Failed to init LLT table\n"));
+ goto exit;
+ }
+
+ HAL_INIT_PROFILE_TAG(HAL_INIT_STAGES_MISC02);
+ /* Get Rx PHY status in order to report RSSI and others. */
+ _InitDriverInfoSize(Adapter, DRVINFO_SZ);
+
+ _InitInterrupt(Adapter);
+ hal_init_macaddr(Adapter);/* set mac_address */
+ _InitNetworkType(Adapter);/* set msr */
+ _InitWMACSetting(Adapter);
+ _InitAdaptiveCtrl(Adapter);
+ _InitEDCA(Adapter);
+ _InitRetryFunction(Adapter);
+ InitUsbAggregationSetting(Adapter);
+ _InitBeaconParameters(Adapter);
+ /* Init CR MACTXEN, MACRXEN after setting RxFF boundary REG_TRXFF_BNDY to patch */
+ /* Hw bug which Hw initials RxFF boundary size to a value which is larger than the real Rx buffer size in 88E. */
+ /* Enable MACTXEN/MACRXEN block */
+ value16 = usb_read16(Adapter, REG_CR);
+ value16 |= (MACTXEN | MACRXEN);
+ usb_write8(Adapter, REG_CR, value16);
+
+ if (haldata->bRDGEnable)
+ _InitRDGSetting(Adapter);
+
+ /* Enable TX Report */
+ /* Enable Tx Report Timer */
+ value8 = usb_read8(Adapter, REG_TX_RPT_CTRL);
+ usb_write8(Adapter, REG_TX_RPT_CTRL, (value8|BIT1|BIT0));
+ /* Set MAX RPT MACID */
+ usb_write8(Adapter, REG_TX_RPT_CTRL+1, 2);/* FOR sta mode ,0: bc/mc ,1:AP */
+ /* Tx RPT Timer. Unit: 32us */
+ usb_write16(Adapter, REG_TX_RPT_TIME, 0xCdf0);
+
+ usb_write8(Adapter, REG_EARLY_MODE_CONTROL, 0);
+
+ usb_write16(Adapter, REG_PKT_VO_VI_LIFE_TIME, 0x0400); /* unit: 256us. 256ms */
+ usb_write16(Adapter, REG_PKT_BE_BK_LIFE_TIME, 0x0400); /* unit: 256us. 256ms */
+
+ /* Keep RfRegChnlVal for later use. */
+ haldata->RfRegChnlVal[0] = phy_query_rf_reg(Adapter, (enum rf_radio_path)0, RF_CHNLBW, bRFRegOffsetMask);
+ haldata->RfRegChnlVal[1] = phy_query_rf_reg(Adapter, (enum rf_radio_path)1, RF_CHNLBW, bRFRegOffsetMask);
+
+HAL_INIT_PROFILE_TAG(HAL_INIT_STAGES_TURN_ON_BLOCK);
+ _BBTurnOnBlock(Adapter);
+
+HAL_INIT_PROFILE_TAG(HAL_INIT_STAGES_INIT_SECURITY);
+ invalidate_cam_all(Adapter);
+
+HAL_INIT_PROFILE_TAG(HAL_INIT_STAGES_MISC11);
+ /* 2010/12/17 MH We need to set TX power according to EFUSE content at first. */
+ phy_set_tx_power_level(Adapter, haldata->CurrentChannel);
+
+/* Move by Neo for USB SS to below setp */
+/* _RfPowerSave(Adapter); */
+
+ _InitAntenna_Selection(Adapter);
+
+ /* */
+ /* Disable BAR, suggested by Scott */
+ /* 2010.04.09 add by hpfan */
+ /* */
+ usb_write32(Adapter, REG_BAR_MODE_CTRL, 0x0201ffff);
+
+ /* HW SEQ CTRL */
+ /* set 0x0 to 0xFF by tynli. Default enable HW SEQ NUM. */
+ usb_write8(Adapter, REG_HWSEQ_CTRL, 0xFF);
+
+ if (pregistrypriv->wifi_spec)
+ usb_write16(Adapter, REG_FAST_EDCA_CTRL, 0);
+
+ /* Nav limit , suggest by scott */
+ usb_write8(Adapter, 0x652, 0x0);
+
+HAL_INIT_PROFILE_TAG(HAL_INIT_STAGES_INIT_HAL_DM);
+ rtl8188e_InitHalDm(Adapter);
+
+ /* 2010/08/11 MH Merge from 8192SE for Minicard init. We need to confirm current radio status */
+ /* and then decide to enable RF or not.!!!??? For Selective suspend mode. We may not */
+ /* call initstruct adapter. May cause some problem?? */
+ /* Fix the bug that Hw/Sw radio off before S3/S4, the RF off action will not be executed */
+ /* in MgntActSet_RF_State() after wake up, because the value of haldata->eRFPowerState */
+ /* is the same as eRfOff, we should change it to eRfOn after we config RF parameters. */
+ /* Added by tynli. 2010.03.30. */
+ pwrctrlpriv->rf_pwrstate = rf_on;
+
+ /* enable Tx report. */
+ usb_write8(Adapter, REG_FWHW_TXQ_CTRL+1, 0x0F);
+
+ /* Suggested by SD1 pisa. Added by tynli. 2011.10.21. */
+ usb_write8(Adapter, REG_EARLY_MODE_CONTROL+3, 0x01);/* Pretx_en, for WEP/TKIP SEC */
+
+ /* tynli_test_tx_report. */
+ usb_write16(Adapter, REG_TX_RPT_TIME, 0x3DF0);
+
+ /* enable tx DMA to drop the redundate data of packet */
+ usb_write16(Adapter, REG_TXDMA_OFFSET_CHK, (usb_read16(Adapter, REG_TXDMA_OFFSET_CHK) | DROP_DATA_EN));
+
+HAL_INIT_PROFILE_TAG(HAL_INIT_STAGES_IQK);
+ /* 2010/08/26 MH Merge from 8192CE. */
+ if (pwrctrlpriv->rf_pwrstate == rf_on) {
+ if (haldata->odmpriv.RFCalibrateInfo.bIQKInitialized) {
+ rtl88eu_phy_iq_calibrate(Adapter, true);
+ } else {
+ rtl88eu_phy_iq_calibrate(Adapter, false);
+ haldata->odmpriv.RFCalibrateInfo.bIQKInitialized = true;
+ }
+
+HAL_INIT_PROFILE_TAG(HAL_INIT_STAGES_PW_TRACK);
+
+ ODM_TXPowerTrackingCheck(&haldata->odmpriv);
+
+HAL_INIT_PROFILE_TAG(HAL_INIT_STAGES_LCK);
+ rtl88eu_phy_lc_calibrate(Adapter);
+ }
+
+/* HAL_INIT_PROFILE_TAG(HAL_INIT_STAGES_INIT_PABIAS); */
+/* _InitPABias(Adapter); */
+ usb_write8(Adapter, REG_USB_HRPWM, 0);
+
+ /* ack for xmit mgmt frames. */
+ usb_write32(Adapter, REG_FWHW_TXQ_CTRL, usb_read32(Adapter, REG_FWHW_TXQ_CTRL)|BIT(12));
+
+exit:
+HAL_INIT_PROFILE_TAG(HAL_INIT_STAGES_END);
+
+ DBG_88E("%s in %dms\n", __func__, rtw_get_passing_time_ms(init_start_time));
+
+
+ return status;
+}
+
+static void CardDisableRTL8188EU(struct adapter *Adapter)
+{
+ u8 val8;
+ struct hal_data_8188e *haldata = GET_HAL_DATA(Adapter);
+
+ RT_TRACE(_module_hci_hal_init_c_, _drv_info_, ("CardDisableRTL8188EU\n"));
+
+ /* Stop Tx Report Timer. 0x4EC[Bit1]=b'0 */
+ val8 = usb_read8(Adapter, REG_TX_RPT_CTRL);
+ usb_write8(Adapter, REG_TX_RPT_CTRL, val8&(~BIT1));
+
+ /* stop rx */
+ usb_write8(Adapter, REG_CR, 0x0);
+
+ /* Run LPS WL RFOFF flow */
+ rtl88eu_pwrseqcmdparsing(Adapter, PWR_CUT_ALL_MSK,
+ PWR_FAB_ALL_MSK, PWR_INTF_USB_MSK,
+ Rtl8188E_NIC_LPS_ENTER_FLOW);
+
+ /* 2. 0x1F[7:0] = 0 turn off RF */
+
+ val8 = usb_read8(Adapter, REG_MCUFWDL);
+ if ((val8 & RAM_DL_SEL) && Adapter->bFWReady) { /* 8051 RAM code */
+ /* Reset MCU 0x2[10]=0. */
+ val8 = usb_read8(Adapter, REG_SYS_FUNC_EN+1);
+ val8 &= ~BIT(2); /* 0x2[10], FEN_CPUEN */
+ usb_write8(Adapter, REG_SYS_FUNC_EN+1, val8);
+ }
+
+ /* reset MCU ready status */
+ usb_write8(Adapter, REG_MCUFWDL, 0);
+
+ /* YJ,add,111212 */
+ /* Disable 32k */
+ val8 = usb_read8(Adapter, REG_32K_CTRL);
+ usb_write8(Adapter, REG_32K_CTRL, val8&(~BIT0));
+
+ /* Card disable power action flow */
+ rtl88eu_pwrseqcmdparsing(Adapter, PWR_CUT_ALL_MSK,
+ PWR_FAB_ALL_MSK, PWR_INTF_USB_MSK,
+ Rtl8188E_NIC_DISABLE_FLOW);
+
+ /* Reset MCU IO Wrapper */
+ val8 = usb_read8(Adapter, REG_RSV_CTRL+1);
+ usb_write8(Adapter, REG_RSV_CTRL+1, (val8&(~BIT3)));
+ val8 = usb_read8(Adapter, REG_RSV_CTRL+1);
+ usb_write8(Adapter, REG_RSV_CTRL+1, val8|BIT3);
+
+ /* YJ,test add, 111207. For Power Consumption. */
+ val8 = usb_read8(Adapter, GPIO_IN);
+ usb_write8(Adapter, GPIO_OUT, val8);
+ usb_write8(Adapter, GPIO_IO_SEL, 0xFF);/* Reg0x46 */
+
+ val8 = usb_read8(Adapter, REG_GPIO_IO_SEL);
+ usb_write8(Adapter, REG_GPIO_IO_SEL, (val8<<4));
+ val8 = usb_read8(Adapter, REG_GPIO_IO_SEL+1);
+ usb_write8(Adapter, REG_GPIO_IO_SEL+1, val8|0x0F);/* Reg0x43 */
+ usb_write32(Adapter, REG_BB_PAD_CTRL, 0x00080808);/* set LNA ,TRSW,EX_PA Pin to output mode */
+ haldata->bMacPwrCtrlOn = false;
+ Adapter->bFWReady = false;
+}
+static void rtl8192cu_hw_power_down(struct adapter *adapt)
+{
+ /* 2010/-8/09 MH For power down module, we need to enable register block contrl reg at 0x1c. */
+ /* Then enable power down control bit of register 0x04 BIT4 and BIT15 as 1. */
+
+ /* Enable register area 0x0-0xc. */
+ usb_write8(adapt, REG_RSV_CTRL, 0x0);
+ usb_write16(adapt, REG_APS_FSMCO, 0x8812);
+}
+
+static u32 rtl8188eu_hal_deinit(struct adapter *Adapter)
+{
+
+ DBG_88E("==> %s\n", __func__);
+
+ usb_write32(Adapter, REG_HIMR_88E, IMR_DISABLED_88E);
+ usb_write32(Adapter, REG_HIMRE_88E, IMR_DISABLED_88E);
+
+ DBG_88E("bkeepfwalive(%x)\n", Adapter->pwrctrlpriv.bkeepfwalive);
+ if (Adapter->pwrctrlpriv.bkeepfwalive) {
+ if ((Adapter->pwrctrlpriv.bHWPwrPindetect) && (Adapter->pwrctrlpriv.bHWPowerdown))
+ rtl8192cu_hw_power_down(Adapter);
+ } else {
+ if (Adapter->hw_init_completed) {
+ CardDisableRTL8188EU(Adapter);
+
+ if ((Adapter->pwrctrlpriv.bHWPwrPindetect) && (Adapter->pwrctrlpriv.bHWPowerdown))
+ rtl8192cu_hw_power_down(Adapter);
+ }
+ }
+ return _SUCCESS;
+ }
+
+static unsigned int rtl8188eu_inirp_init(struct adapter *Adapter)
+{
+ u8 i;
+ struct recv_buf *precvbuf;
+ uint status;
+ struct recv_priv *precvpriv = &(Adapter->recvpriv);
+
+ status = _SUCCESS;
+
+ RT_TRACE(_module_hci_hal_init_c_, _drv_info_,
+ ("===> usb_inirp_init\n"));
+
+ precvpriv->ff_hwaddr = RECV_BULK_IN_ADDR;
+
+ /* issue Rx irp to receive data */
+ precvbuf = (struct recv_buf *)precvpriv->precv_buf;
+ for (i = 0; i < NR_RECVBUFF; i++) {
+ if (usb_read_port(Adapter, precvpriv->ff_hwaddr, 0, (unsigned char *)precvbuf) == false) {
+ RT_TRACE(_module_hci_hal_init_c_, _drv_err_, ("usb_rx_init: usb_read_port error\n"));
+ status = _FAIL;
+ goto exit;
+ }
+
+ precvbuf++;
+ precvpriv->free_recv_buf_queue_cnt--;
+ }
+
+exit:
+
+ RT_TRACE(_module_hci_hal_init_c_, _drv_info_, ("<=== usb_inirp_init\n"));
+
+
+ return status;
+}
+
+static unsigned int rtl8188eu_inirp_deinit(struct adapter *Adapter)
+{
+ RT_TRACE(_module_hci_hal_init_c_, _drv_info_, ("\n ===> usb_rx_deinit\n"));
+
+ usb_read_port_cancel(Adapter);
+
+ RT_TRACE(_module_hci_hal_init_c_, _drv_info_, ("\n <=== usb_rx_deinit\n"));
+
+ return _SUCCESS;
+}
+
+/* */
+/* */
+/* EEPROM/EFUSE Content Parsing */
+/* */
+/* */
+static void Hal_EfuseParsePIDVID_8188EU(struct adapter *adapt, u8 *hwinfo, bool AutoLoadFail)
+{
+ struct hal_data_8188e *haldata = GET_HAL_DATA(adapt);
+
+ if (!AutoLoadFail) {
+ /* VID, PID */
+ haldata->EEPROMVID = EF2BYTE(*(__le16 *)&hwinfo[EEPROM_VID_88EU]);
+ haldata->EEPROMPID = EF2BYTE(*(__le16 *)&hwinfo[EEPROM_PID_88EU]);
+
+ /* Customer ID, 0x00 and 0xff are reserved for Realtek. */
+ haldata->EEPROMCustomerID = *(u8 *)&hwinfo[EEPROM_CUSTOMERID_88E];
+ haldata->EEPROMSubCustomerID = EEPROM_Default_SubCustomerID;
+ } else {
+ haldata->EEPROMVID = EEPROM_Default_VID;
+ haldata->EEPROMPID = EEPROM_Default_PID;
+
+ /* Customer ID, 0x00 and 0xff are reserved for Realtek. */
+ haldata->EEPROMCustomerID = EEPROM_Default_CustomerID;
+ haldata->EEPROMSubCustomerID = EEPROM_Default_SubCustomerID;
+ }
+
+ DBG_88E("VID = 0x%04X, PID = 0x%04X\n", haldata->EEPROMVID, haldata->EEPROMPID);
+ DBG_88E("Customer ID: 0x%02X, SubCustomer ID: 0x%02X\n", haldata->EEPROMCustomerID, haldata->EEPROMSubCustomerID);
+}
+
+static void Hal_EfuseParseMACAddr_8188EU(struct adapter *adapt, u8 *hwinfo, bool AutoLoadFail)
+{
+ u16 i;
+ u8 sMacAddr[6] = {0x00, 0xE0, 0x4C, 0x81, 0x88, 0x02};
+ struct eeprom_priv *eeprom = GET_EEPROM_EFUSE_PRIV(adapt);
+
+ if (AutoLoadFail) {
+ for (i = 0; i < 6; i++)
+ eeprom->mac_addr[i] = sMacAddr[i];
+ } else {
+ /* Read Permanent MAC address */
+ memcpy(eeprom->mac_addr, &hwinfo[EEPROM_MAC_ADDR_88EU], ETH_ALEN);
+ }
+ RT_TRACE(_module_hci_hal_init_c_, _drv_notice_,
+ ("Hal_EfuseParseMACAddr_8188EU: Permanent Address = %pM\n",
+ eeprom->mac_addr));
+}
+
+static void
+readAdapterInfo_8188EU(
+ struct adapter *adapt
+ )
+{
+ struct eeprom_priv *eeprom = GET_EEPROM_EFUSE_PRIV(adapt);
+
+ /* parse the eeprom/efuse content */
+ Hal_EfuseParseIDCode88E(adapt, eeprom->efuse_eeprom_data);
+ Hal_EfuseParsePIDVID_8188EU(adapt, eeprom->efuse_eeprom_data, eeprom->bautoload_fail_flag);
+ Hal_EfuseParseMACAddr_8188EU(adapt, eeprom->efuse_eeprom_data, eeprom->bautoload_fail_flag);
+
+ Hal_ReadPowerSavingMode88E(adapt, eeprom->efuse_eeprom_data, eeprom->bautoload_fail_flag);
+ Hal_ReadTxPowerInfo88E(adapt, eeprom->efuse_eeprom_data, eeprom->bautoload_fail_flag);
+ Hal_EfuseParseEEPROMVer88E(adapt, eeprom->efuse_eeprom_data, eeprom->bautoload_fail_flag);
+ rtl8188e_EfuseParseChnlPlan(adapt, eeprom->efuse_eeprom_data, eeprom->bautoload_fail_flag);
+ Hal_EfuseParseXtal_8188E(adapt, eeprom->efuse_eeprom_data, eeprom->bautoload_fail_flag);
+ Hal_EfuseParseCustomerID88E(adapt, eeprom->efuse_eeprom_data, eeprom->bautoload_fail_flag);
+ Hal_ReadAntennaDiversity88E(adapt, eeprom->efuse_eeprom_data, eeprom->bautoload_fail_flag);
+ Hal_EfuseParseBoardType88E(adapt, eeprom->efuse_eeprom_data, eeprom->bautoload_fail_flag);
+ Hal_ReadThermalMeter_88E(adapt, eeprom->efuse_eeprom_data, eeprom->bautoload_fail_flag);
+
+}
+
+static void _ReadPROMContent(
+ struct adapter *Adapter
+ )
+{
+ struct eeprom_priv *eeprom = GET_EEPROM_EFUSE_PRIV(Adapter);
+ u8 eeValue;
+
+ /* check system boot selection */
+ eeValue = usb_read8(Adapter, REG_9346CR);
+ eeprom->EepromOrEfuse = (eeValue & BOOT_FROM_EEPROM) ? true : false;
+ eeprom->bautoload_fail_flag = (eeValue & EEPROM_EN) ? false : true;
+
+ DBG_88E("Boot from %s, Autoload %s !\n", (eeprom->EepromOrEfuse ? "EEPROM" : "EFUSE"),
+ (eeprom->bautoload_fail_flag ? "Fail" : "OK"));
+
+ Hal_InitPGData88E(Adapter);
+ readAdapterInfo_8188EU(Adapter);
+}
+
+static void _ReadRFType(struct adapter *Adapter)
+{
+ struct hal_data_8188e *haldata = GET_HAL_DATA(Adapter);
+
+ haldata->rf_chip = RF_6052;
+}
+
+static void _ReadAdapterInfo8188EU(struct adapter *Adapter)
+{
+ u32 start = jiffies;
+
+ MSG_88E("====> %s\n", __func__);
+
+ _ReadRFType(Adapter);/* rf_chip -> _InitRFType() */
+ _ReadPROMContent(Adapter);
+
+ MSG_88E("<==== %s in %d ms\n", __func__, rtw_get_passing_time_ms(start));
+}
+
+#define GPIO_DEBUG_PORT_NUM 0
+static void rtl8192cu_trigger_gpio_0(struct adapter *adapt)
+{
+}
+
+static void ResumeTxBeacon(struct adapter *adapt)
+{
+ struct hal_data_8188e *haldata = GET_HAL_DATA(adapt);
+
+ /* 2010.03.01. Marked by tynli. No need to call workitem beacause we record the value */
+ /* which should be read from register to a global variable. */
+
+ usb_write8(adapt, REG_FWHW_TXQ_CTRL+2, (haldata->RegFwHwTxQCtrl) | BIT6);
+ haldata->RegFwHwTxQCtrl |= BIT6;
+ usb_write8(adapt, REG_TBTT_PROHIBIT+1, 0xff);
+ haldata->RegReg542 |= BIT0;
+ usb_write8(adapt, REG_TBTT_PROHIBIT+2, haldata->RegReg542);
+}
+
+static void StopTxBeacon(struct adapter *adapt)
+{
+ struct hal_data_8188e *haldata = GET_HAL_DATA(adapt);
+
+ /* 2010.03.01. Marked by tynli. No need to call workitem beacause we record the value */
+ /* which should be read from register to a global variable. */
+
+ usb_write8(adapt, REG_FWHW_TXQ_CTRL+2, (haldata->RegFwHwTxQCtrl) & (~BIT6));
+ haldata->RegFwHwTxQCtrl &= (~BIT6);
+ usb_write8(adapt, REG_TBTT_PROHIBIT+1, 0x64);
+ haldata->RegReg542 &= ~(BIT0);
+ usb_write8(adapt, REG_TBTT_PROHIBIT+2, haldata->RegReg542);
+
+ /* todo: CheckFwRsvdPageContent(Adapter); 2010.06.23. Added by tynli. */
+}
+
+static void hw_var_set_opmode(struct adapter *Adapter, u8 variable, u8 *val)
+{
+ u8 val8;
+ u8 mode = *((u8 *)val);
+
+ /* disable Port0 TSF update */
+ usb_write8(Adapter, REG_BCN_CTRL, usb_read8(Adapter, REG_BCN_CTRL)|BIT(4));
+
+ /* set net_type */
+ val8 = usb_read8(Adapter, MSR)&0x0c;
+ val8 |= mode;
+ usb_write8(Adapter, MSR, val8);
+
+ DBG_88E("%s()-%d mode = %d\n", __func__, __LINE__, mode);
+
+ if ((mode == _HW_STATE_STATION_) || (mode == _HW_STATE_NOLINK_)) {
+ StopTxBeacon(Adapter);
+
+ usb_write8(Adapter, REG_BCN_CTRL, 0x19);/* disable atim wnd */
+ } else if ((mode == _HW_STATE_ADHOC_)) {
+ ResumeTxBeacon(Adapter);
+ usb_write8(Adapter, REG_BCN_CTRL, 0x1a);
+ } else if (mode == _HW_STATE_AP_) {
+ ResumeTxBeacon(Adapter);
+
+ usb_write8(Adapter, REG_BCN_CTRL, 0x12);
+
+ /* Set RCR */
+ usb_write32(Adapter, REG_RCR, 0x7000208e);/* CBSSID_DATA must set to 0,reject ICV_ERR packet */
+ /* enable to rx data frame */
+ usb_write16(Adapter, REG_RXFLTMAP2, 0xFFFF);
+ /* enable to rx ps-poll */
+ usb_write16(Adapter, REG_RXFLTMAP1, 0x0400);
+
+ /* Beacon Control related register for first time */
+ usb_write8(Adapter, REG_BCNDMATIM, 0x02); /* 2ms */
+
+ usb_write8(Adapter, REG_ATIMWND, 0x0a); /* 10ms */
+ usb_write16(Adapter, REG_BCNTCFG, 0x00);
+ usb_write16(Adapter, REG_TBTT_PROHIBIT, 0xff04);
+ usb_write16(Adapter, REG_TSFTR_SYN_OFFSET, 0x7fff);/* +32767 (~32ms) */
+
+ /* reset TSF */
+ usb_write8(Adapter, REG_DUAL_TSF_RST, BIT(0));
+
+ /* BIT3 - If set 0, hw will clr bcnq when tx becon ok/fail or port 0 */
+ usb_write8(Adapter, REG_MBID_NUM, usb_read8(Adapter, REG_MBID_NUM) | BIT(3) | BIT(4));
+
+ /* enable BCN0 Function for if1 */
+ /* don't enable update TSF0 for if1 (due to TSF update when beacon/probe rsp are received) */
+ usb_write8(Adapter, REG_BCN_CTRL, (DIS_TSF_UDT0_NORMAL_CHIP|EN_BCN_FUNCTION | BIT(1)));
+
+ /* dis BCN1 ATIM WND if if2 is station */
+ usb_write8(Adapter, REG_BCN_CTRL_1, usb_read8(Adapter, REG_BCN_CTRL_1) | BIT(0));
+ }
+}
+
+static void hw_var_set_macaddr(struct adapter *Adapter, u8 variable, u8 *val)
+{
+ u8 idx = 0;
+ u32 reg_macid;
+
+ reg_macid = REG_MACID;
+
+ for (idx = 0; idx < 6; idx++)
+ usb_write8(Adapter, (reg_macid+idx), val[idx]);
+}
+
+static void hw_var_set_bssid(struct adapter *Adapter, u8 variable, u8 *val)
+{
+ u8 idx = 0;
+ u32 reg_bssid;
+
+ reg_bssid = REG_BSSID;
+
+ for (idx = 0; idx < 6; idx++)
+ usb_write8(Adapter, (reg_bssid+idx), val[idx]);
+}
+
+static void hw_var_set_bcn_func(struct adapter *Adapter, u8 variable, u8 *val)
+{
+ u32 bcn_ctrl_reg;
+
+ bcn_ctrl_reg = REG_BCN_CTRL;
+
+ if (*((u8 *)val))
+ usb_write8(Adapter, bcn_ctrl_reg, (EN_BCN_FUNCTION | EN_TXBCN_RPT));
+ else
+ usb_write8(Adapter, bcn_ctrl_reg, usb_read8(Adapter, bcn_ctrl_reg)&(~(EN_BCN_FUNCTION | EN_TXBCN_RPT)));
+}
+
+static void SetHwReg8188EU(struct adapter *Adapter, u8 variable, u8 *val)
+{
+ struct hal_data_8188e *haldata = GET_HAL_DATA(Adapter);
+ struct dm_priv *pdmpriv = &haldata->dmpriv;
+ struct odm_dm_struct *podmpriv = &haldata->odmpriv;
+
+ switch (variable) {
+ case HW_VAR_MEDIA_STATUS:
+ {
+ u8 val8;
+
+ val8 = usb_read8(Adapter, MSR)&0x0c;
+ val8 |= *((u8 *)val);
+ usb_write8(Adapter, MSR, val8);
+ }
+ break;
+ case HW_VAR_MEDIA_STATUS1:
+ {
+ u8 val8;
+
+ val8 = usb_read8(Adapter, MSR) & 0x03;
+ val8 |= *((u8 *)val) << 2;
+ usb_write8(Adapter, MSR, val8);
+ }
+ break;
+ case HW_VAR_SET_OPMODE:
+ hw_var_set_opmode(Adapter, variable, val);
+ break;
+ case HW_VAR_MAC_ADDR:
+ hw_var_set_macaddr(Adapter, variable, val);
+ break;
+ case HW_VAR_BSSID:
+ hw_var_set_bssid(Adapter, variable, val);
+ break;
+ case HW_VAR_BASIC_RATE:
+ {
+ u16 BrateCfg = 0;
+ u8 RateIndex = 0;
+
+ /* 2007.01.16, by Emily */
+ /* Select RRSR (in Legacy-OFDM and CCK) */
+ /* For 8190, we select only 24M, 12M, 6M, 11M, 5.5M, 2M, and 1M from the Basic rate. */
+ /* We do not use other rates. */
+ HalSetBrateCfg(Adapter, val, &BrateCfg);
+ DBG_88E("HW_VAR_BASIC_RATE: BrateCfg(%#x)\n", BrateCfg);
+
+ /* 2011.03.30 add by Luke Lee */
+ /* CCK 2M ACK should be disabled for some BCM and Atheros AP IOT */
+ /* because CCK 2M has poor TXEVM */
+ /* CCK 5.5M & 11M ACK should be enabled for better performance */
+
+ BrateCfg = (BrateCfg | 0xd) & 0x15d;
+ haldata->BasicRateSet = BrateCfg;
+
+ BrateCfg |= 0x01; /* default enable 1M ACK rate */
+ /* Set RRSR rate table. */
+ usb_write8(Adapter, REG_RRSR, BrateCfg & 0xff);
+ usb_write8(Adapter, REG_RRSR+1, (BrateCfg >> 8) & 0xff);
+ usb_write8(Adapter, REG_RRSR+2, usb_read8(Adapter, REG_RRSR+2)&0xf0);
+
+ /* Set RTS initial rate */
+ while (BrateCfg > 0x1) {
+ BrateCfg >>= 1;
+ RateIndex++;
+ }
+ /* Ziv - Check */
+ usb_write8(Adapter, REG_INIRTS_RATE_SEL, RateIndex);
+ }
+ break;
+ case HW_VAR_TXPAUSE:
+ usb_write8(Adapter, REG_TXPAUSE, *((u8 *)val));
+ break;
+ case HW_VAR_BCN_FUNC:
+ hw_var_set_bcn_func(Adapter, variable, val);
+ break;
+ case HW_VAR_CORRECT_TSF:
+ {
+ u64 tsf;
+ struct mlme_ext_priv *pmlmeext = &Adapter->mlmeextpriv;
+ struct mlme_ext_info *pmlmeinfo = &(pmlmeext->mlmext_info);
+
+ tsf = pmlmeext->TSFValue - rtw_modular64(pmlmeext->TSFValue, (pmlmeinfo->bcn_interval*1024)) - 1024; /* us */
+
+ if (((pmlmeinfo->state&0x03) == WIFI_FW_ADHOC_STATE) || ((pmlmeinfo->state&0x03) == WIFI_FW_AP_STATE))
+ StopTxBeacon(Adapter);
+
+ /* disable related TSF function */
+ usb_write8(Adapter, REG_BCN_CTRL, usb_read8(Adapter, REG_BCN_CTRL)&(~BIT(3)));
+
+ usb_write32(Adapter, REG_TSFTR, tsf);
+ usb_write32(Adapter, REG_TSFTR+4, tsf>>32);
+
+ /* enable related TSF function */
+ usb_write8(Adapter, REG_BCN_CTRL, usb_read8(Adapter, REG_BCN_CTRL)|BIT(3));
+
+ if (((pmlmeinfo->state&0x03) == WIFI_FW_ADHOC_STATE) || ((pmlmeinfo->state&0x03) == WIFI_FW_AP_STATE))
+ ResumeTxBeacon(Adapter);
+ }
+ break;
+ case HW_VAR_CHECK_BSSID:
+ if (*((u8 *)val)) {
+ usb_write32(Adapter, REG_RCR, usb_read32(Adapter, REG_RCR)|RCR_CBSSID_DATA|RCR_CBSSID_BCN);
+ } else {
+ u32 val32;
+
+ val32 = usb_read32(Adapter, REG_RCR);
+
+ val32 &= ~(RCR_CBSSID_DATA | RCR_CBSSID_BCN);
+
+ usb_write32(Adapter, REG_RCR, val32);
+ }
+ break;
+ case HW_VAR_MLME_DISCONNECT:
+ /* Set RCR to not to receive data frame when NO LINK state */
+ /* reject all data frames */
+ usb_write16(Adapter, REG_RXFLTMAP2, 0x00);
+
+ /* reset TSF */
+ usb_write8(Adapter, REG_DUAL_TSF_RST, (BIT(0)|BIT(1)));
+
+ /* disable update TSF */
+ usb_write8(Adapter, REG_BCN_CTRL, usb_read8(Adapter, REG_BCN_CTRL)|BIT(4));
+ break;
+ case HW_VAR_MLME_SITESURVEY:
+ if (*((u8 *)val)) { /* under sitesurvey */
+ /* config RCR to receive different BSSID & not to receive data frame */
+ u32 v = usb_read32(Adapter, REG_RCR);
+ v &= ~(RCR_CBSSID_BCN);
+ usb_write32(Adapter, REG_RCR, v);
+ /* reject all data frame */
+ usb_write16(Adapter, REG_RXFLTMAP2, 0x00);
+
+ /* disable update TSF */
+ usb_write8(Adapter, REG_BCN_CTRL, usb_read8(Adapter, REG_BCN_CTRL)|BIT(4));
+ } else { /* sitesurvey done */
+ struct mlme_ext_priv *pmlmeext = &Adapter->mlmeextpriv;
+ struct mlme_ext_info *pmlmeinfo = &(pmlmeext->mlmext_info);
+
+ if ((is_client_associated_to_ap(Adapter)) ||
+ ((pmlmeinfo->state&0x03) == WIFI_FW_ADHOC_STATE)) {
+ /* enable to rx data frame */
+ usb_write16(Adapter, REG_RXFLTMAP2, 0xFFFF);
+
+ /* enable update TSF */
+ usb_write8(Adapter, REG_BCN_CTRL, usb_read8(Adapter, REG_BCN_CTRL)&(~BIT(4)));
+ } else if ((pmlmeinfo->state&0x03) == WIFI_FW_AP_STATE) {
+ usb_write16(Adapter, REG_RXFLTMAP2, 0xFFFF);
+ /* enable update TSF */
+ usb_write8(Adapter, REG_BCN_CTRL, usb_read8(Adapter, REG_BCN_CTRL)&(~BIT(4)));
+ }
+ if ((pmlmeinfo->state&0x03) == WIFI_FW_AP_STATE) {
+ usb_write32(Adapter, REG_RCR, usb_read32(Adapter, REG_RCR)|RCR_CBSSID_BCN);
+ } else {
+ if (Adapter->in_cta_test) {
+ u32 v = usb_read32(Adapter, REG_RCR);
+ v &= ~(RCR_CBSSID_DATA | RCR_CBSSID_BCN);/* RCR_ADF */
+ usb_write32(Adapter, REG_RCR, v);
+ } else {
+ usb_write32(Adapter, REG_RCR, usb_read32(Adapter, REG_RCR)|RCR_CBSSID_BCN);
+ }
+ }
+ }
+ break;
+ case HW_VAR_MLME_JOIN:
+ {
+ u8 RetryLimit = 0x30;
+ u8 type = *((u8 *)val);
+ struct mlme_priv *pmlmepriv = &Adapter->mlmepriv;
+
+ if (type == 0) { /* prepare to join */
+ /* enable to rx data frame.Accept all data frame */
+ usb_write16(Adapter, REG_RXFLTMAP2, 0xFFFF);
+
+ if (Adapter->in_cta_test) {
+ u32 v = usb_read32(Adapter, REG_RCR);
+ v &= ~(RCR_CBSSID_DATA | RCR_CBSSID_BCN);/* RCR_ADF */
+ usb_write32(Adapter, REG_RCR, v);
+ } else {
+ usb_write32(Adapter, REG_RCR, usb_read32(Adapter, REG_RCR)|RCR_CBSSID_DATA|RCR_CBSSID_BCN);
+ }
+
+ if (check_fwstate(pmlmepriv, WIFI_STATION_STATE))
+ RetryLimit = (haldata->CustomerID == RT_CID_CCX) ? 7 : 48;
+ else /* Ad-hoc Mode */
+ RetryLimit = 0x7;
+ } else if (type == 1) {
+ /* joinbss_event call back when join res < 0 */
+ usb_write16(Adapter, REG_RXFLTMAP2, 0x00);
+ } else if (type == 2) {
+ /* sta add event call back */
+ /* enable update TSF */
+ usb_write8(Adapter, REG_BCN_CTRL, usb_read8(Adapter, REG_BCN_CTRL)&(~BIT(4)));
+
+ if (check_fwstate(pmlmepriv, WIFI_ADHOC_STATE|WIFI_ADHOC_MASTER_STATE))
+ RetryLimit = 0x7;
+ }
+ usb_write16(Adapter, REG_RL, RetryLimit << RETRY_LIMIT_SHORT_SHIFT | RetryLimit << RETRY_LIMIT_LONG_SHIFT);
+ }
+ break;
+ case HW_VAR_BEACON_INTERVAL:
+ usb_write16(Adapter, REG_BCN_INTERVAL, *((u16 *)val));
+ break;
+ case HW_VAR_SLOT_TIME:
+ {
+ u8 u1bAIFS, aSifsTime;
+ struct mlme_ext_priv *pmlmeext = &Adapter->mlmeextpriv;
+ struct mlme_ext_info *pmlmeinfo = &(pmlmeext->mlmext_info);
+
+ usb_write8(Adapter, REG_SLOT, val[0]);
+
+ if (pmlmeinfo->WMM_enable == 0) {
+ if (pmlmeext->cur_wireless_mode == WIRELESS_11B)
+ aSifsTime = 10;
+ else
+ aSifsTime = 16;
+
+ u1bAIFS = aSifsTime + (2 * pmlmeinfo->slotTime);
+
+ /* <Roger_EXP> Temporary removed, 2008.06.20. */
+ usb_write8(Adapter, REG_EDCA_VO_PARAM, u1bAIFS);
+ usb_write8(Adapter, REG_EDCA_VI_PARAM, u1bAIFS);
+ usb_write8(Adapter, REG_EDCA_BE_PARAM, u1bAIFS);
+ usb_write8(Adapter, REG_EDCA_BK_PARAM, u1bAIFS);
+ }
+ }
+ break;
+ case HW_VAR_RESP_SIFS:
+ /* RESP_SIFS for CCK */
+ usb_write8(Adapter, REG_R2T_SIFS, val[0]); /* SIFS_T2T_CCK (0x08) */
+ usb_write8(Adapter, REG_R2T_SIFS+1, val[1]); /* SIFS_R2T_CCK(0x08) */
+ /* RESP_SIFS for OFDM */
+ usb_write8(Adapter, REG_T2T_SIFS, val[2]); /* SIFS_T2T_OFDM (0x0a) */
+ usb_write8(Adapter, REG_T2T_SIFS+1, val[3]); /* SIFS_R2T_OFDM(0x0a) */
+ break;
+ case HW_VAR_ACK_PREAMBLE:
+ {
+ u8 regTmp;
+ u8 bShortPreamble = *((bool *)val);
+ /* Joseph marked out for Netgear 3500 TKIP channel 7 issue.(Temporarily) */
+ regTmp = (haldata->nCur40MhzPrimeSC)<<5;
+ if (bShortPreamble)
+ regTmp |= 0x80;
+
+ usb_write8(Adapter, REG_RRSR+2, regTmp);
+ }
+ break;
+ case HW_VAR_SEC_CFG:
+ usb_write8(Adapter, REG_SECCFG, *((u8 *)val));
+ break;
+ case HW_VAR_DM_FLAG:
+ podmpriv->SupportAbility = *((u8 *)val);
+ break;
+ case HW_VAR_DM_FUNC_OP:
+ if (val[0])
+ podmpriv->BK_SupportAbility = podmpriv->SupportAbility;
+ else
+ podmpriv->SupportAbility = podmpriv->BK_SupportAbility;
+ break;
+ case HW_VAR_DM_FUNC_SET:
+ if (*((u32 *)val) == DYNAMIC_ALL_FUNC_ENABLE) {
+ pdmpriv->DMFlag = pdmpriv->InitDMFlag;
+ podmpriv->SupportAbility = pdmpriv->InitODMFlag;
+ } else {
+ podmpriv->SupportAbility |= *((u32 *)val);
+ }
+ break;
+ case HW_VAR_DM_FUNC_CLR:
+ podmpriv->SupportAbility &= *((u32 *)val);
+ break;
+ case HW_VAR_CAM_EMPTY_ENTRY:
+ {
+ u8 ucIndex = *((u8 *)val);
+ u8 i;
+ u32 ulCommand = 0;
+ u32 ulContent = 0;
+ u32 ulEncAlgo = CAM_AES;
+
+ for (i = 0; i < CAM_CONTENT_COUNT; i++) {
+ /* filled id in CAM config 2 byte */
+ if (i == 0)
+ ulContent |= (ucIndex & 0x03) | ((u16)(ulEncAlgo)<<2);
+ else
+ ulContent = 0;
+ /* polling bit, and No Write enable, and address */
+ ulCommand = CAM_CONTENT_COUNT*ucIndex+i;
+ ulCommand = ulCommand | CAM_POLLINIG|CAM_WRITE;
+ /* write content 0 is equall to mark invalid */
+ usb_write32(Adapter, WCAMI, ulContent); /* delay_ms(40); */
+ usb_write32(Adapter, RWCAM, ulCommand); /* delay_ms(40); */
+ }
+ }
+ break;
+ case HW_VAR_CAM_INVALID_ALL:
+ usb_write32(Adapter, RWCAM, BIT(31)|BIT(30));
+ break;
+ case HW_VAR_CAM_WRITE:
+ {
+ u32 cmd;
+ u32 *cam_val = (u32 *)val;
+ usb_write32(Adapter, WCAMI, cam_val[0]);
+
+ cmd = CAM_POLLINIG | CAM_WRITE | cam_val[1];
+ usb_write32(Adapter, RWCAM, cmd);
+ }
+ break;
+ case HW_VAR_AC_PARAM_VO:
+ usb_write32(Adapter, REG_EDCA_VO_PARAM, ((u32 *)(val))[0]);
+ break;
+ case HW_VAR_AC_PARAM_VI:
+ usb_write32(Adapter, REG_EDCA_VI_PARAM, ((u32 *)(val))[0]);
+ break;
+ case HW_VAR_AC_PARAM_BE:
+ haldata->AcParam_BE = ((u32 *)(val))[0];
+ usb_write32(Adapter, REG_EDCA_BE_PARAM, ((u32 *)(val))[0]);
+ break;
+ case HW_VAR_AC_PARAM_BK:
+ usb_write32(Adapter, REG_EDCA_BK_PARAM, ((u32 *)(val))[0]);
+ break;
+ case HW_VAR_ACM_CTRL:
+ {
+ u8 acm_ctrl = *((u8 *)val);
+ u8 AcmCtrl = usb_read8(Adapter, REG_ACMHWCTRL);
+
+ if (acm_ctrl > 1)
+ AcmCtrl = AcmCtrl | 0x1;
+
+ if (acm_ctrl & BIT(3))
+ AcmCtrl |= AcmHw_VoqEn;
+ else
+ AcmCtrl &= (~AcmHw_VoqEn);
+
+ if (acm_ctrl & BIT(2))
+ AcmCtrl |= AcmHw_ViqEn;
+ else
+ AcmCtrl &= (~AcmHw_ViqEn);
+
+ if (acm_ctrl & BIT(1))
+ AcmCtrl |= AcmHw_BeqEn;
+ else
+ AcmCtrl &= (~AcmHw_BeqEn);
+
+ DBG_88E("[HW_VAR_ACM_CTRL] Write 0x%X\n", AcmCtrl);
+ usb_write8(Adapter, REG_ACMHWCTRL, AcmCtrl);
+ }
+ break;
+ case HW_VAR_AMPDU_MIN_SPACE:
+ {
+ u8 MinSpacingToSet;
+ u8 SecMinSpace;
+
+ MinSpacingToSet = *((u8 *)val);
+ if (MinSpacingToSet <= 7) {
+ switch (Adapter->securitypriv.dot11PrivacyAlgrthm) {
+ case _NO_PRIVACY_:
+ case _AES_:
+ SecMinSpace = 0;
+ break;
+ case _WEP40_:
+ case _WEP104_:
+ case _TKIP_:
+ case _TKIP_WTMIC_:
+ SecMinSpace = 6;
+ break;
+ default:
+ SecMinSpace = 7;
+ break;
+ }
+ if (MinSpacingToSet < SecMinSpace)
+ MinSpacingToSet = SecMinSpace;
+ usb_write8(Adapter, REG_AMPDU_MIN_SPACE, (usb_read8(Adapter, REG_AMPDU_MIN_SPACE) & 0xf8) | MinSpacingToSet);
+ }
+ }
+ break;
+ case HW_VAR_AMPDU_FACTOR:
+ {
+ u8 RegToSet_Normal[4] = {0x41, 0xa8, 0x72, 0xb9};
+ u8 FactorToSet;
+ u8 *pRegToSet;
+ u8 index = 0;
+
+ pRegToSet = RegToSet_Normal; /* 0xb972a841; */
+ FactorToSet = *((u8 *)val);
+ if (FactorToSet <= 3) {
+ FactorToSet = 1 << (FactorToSet + 2);
+ if (FactorToSet > 0xf)
+ FactorToSet = 0xf;
+
+ for (index = 0; index < 4; index++) {
+ if ((pRegToSet[index] & 0xf0) > (FactorToSet<<4))
+ pRegToSet[index] = (pRegToSet[index] & 0x0f) | (FactorToSet<<4);
+
+ if ((pRegToSet[index] & 0x0f) > FactorToSet)
+ pRegToSet[index] = (pRegToSet[index] & 0xf0) | (FactorToSet);
+
+ usb_write8(Adapter, (REG_AGGLEN_LMT+index), pRegToSet[index]);
+ }
+ }
+ }
+ break;
+ case HW_VAR_RXDMA_AGG_PG_TH:
+ {
+ u8 threshold = *((u8 *)val);
+ if (threshold == 0)
+ threshold = haldata->UsbRxAggPageCount;
+ usb_write8(Adapter, REG_RXDMA_AGG_PG_TH, threshold);
+ }
+ break;
+ case HW_VAR_SET_RPWM:
+ break;
+ case HW_VAR_H2C_FW_PWRMODE:
+ {
+ u8 psmode = (*(u8 *)val);
+
+ /* Forece leave RF low power mode for 1T1R to prevent conficting setting in Fw power */
+ /* saving sequence. 2010.06.07. Added by tynli. Suggested by SD3 yschang. */
+ if ((psmode != PS_MODE_ACTIVE) && (!IS_92C_SERIAL(haldata->VersionID)))
+ ODM_RF_Saving(podmpriv, true);
+ rtl8188e_set_FwPwrMode_cmd(Adapter, psmode);
+ }
+ break;
+ case HW_VAR_H2C_FW_JOINBSSRPT:
+ {
+ u8 mstatus = (*(u8 *)val);
+ rtl8188e_set_FwJoinBssReport_cmd(Adapter, mstatus);
+ }
+ break;
+ case HW_VAR_INITIAL_GAIN:
+ {
+ struct rtw_dig *pDigTable = &podmpriv->DM_DigTable;
+ u32 rx_gain = ((u32 *)(val))[0];
+
+ if (rx_gain == 0xff) {/* restore rx gain */
+ ODM_Write_DIG(podmpriv, pDigTable->BackupIGValue);
+ } else {
+ pDigTable->BackupIGValue = pDigTable->CurIGValue;
+ ODM_Write_DIG(podmpriv, rx_gain);
+ }
+ }
+ break;
+ case HW_VAR_TRIGGER_GPIO_0:
+ rtl8192cu_trigger_gpio_0(Adapter);
+ break;
+ case HW_VAR_RPT_TIMER_SETTING:
+ {
+ u16 min_rpt_time = (*(u16 *)val);
+ ODM_RA_Set_TxRPT_Time(podmpriv, min_rpt_time);
+ }
+ break;
+ case HW_VAR_ANTENNA_DIVERSITY_SELECT:
+ {
+ u8 Optimum_antenna = (*(u8 *)val);
+ u8 Ant;
+ /* switch antenna to Optimum_antenna */
+ if (haldata->CurAntenna != Optimum_antenna) {
+ Ant = (Optimum_antenna == 2) ? MAIN_ANT : AUX_ANT;
+ rtl88eu_dm_update_rx_idle_ant(&haldata->odmpriv, Ant);
+
+ haldata->CurAntenna = Optimum_antenna;
+ }
+ }
+ break;
+ case HW_VAR_EFUSE_BYTES: /* To set EFUE total used bytes, added by Roger, 2008.12.22. */
+ haldata->EfuseUsedBytes = *((u16 *)val);
+ break;
+ case HW_VAR_FIFO_CLEARN_UP:
+ {
+ struct pwrctrl_priv *pwrpriv = &Adapter->pwrctrlpriv;
+ u8 trycnt = 100;
+
+ /* pause tx */
+ usb_write8(Adapter, REG_TXPAUSE, 0xff);
+
+ /* keep sn */
+ Adapter->xmitpriv.nqos_ssn = usb_read16(Adapter, REG_NQOS_SEQ);
+
+ if (!pwrpriv->bkeepfwalive) {
+ /* RX DMA stop */
+ usb_write32(Adapter, REG_RXPKT_NUM, (usb_read32(Adapter, REG_RXPKT_NUM)|RW_RELEASE_EN));
+ do {
+ if (!(usb_read32(Adapter, REG_RXPKT_NUM)&RXDMA_IDLE))
+ break;
+ } while (trycnt--);
+ if (trycnt == 0)
+ DBG_88E("Stop RX DMA failed......\n");
+
+ /* RQPN Load 0 */
+ usb_write16(Adapter, REG_RQPN_NPQ, 0x0);
+ usb_write32(Adapter, REG_RQPN, 0x80000000);
+ mdelay(10);
+ }
+ }
+ break;
+ case HW_VAR_CHECK_TXBUF:
+ break;
+ case HW_VAR_APFM_ON_MAC:
+ haldata->bMacPwrCtrlOn = *val;
+ DBG_88E("%s: bMacPwrCtrlOn=%d\n", __func__, haldata->bMacPwrCtrlOn);
+ break;
+ case HW_VAR_TX_RPT_MAX_MACID:
+ {
+ u8 maxMacid = *val;
+ DBG_88E("### MacID(%d),Set Max Tx RPT MID(%d)\n", maxMacid, maxMacid+1);
+ usb_write8(Adapter, REG_TX_RPT_CTRL+1, maxMacid+1);
+ }
+ break;
+ case HW_VAR_H2C_MEDIA_STATUS_RPT:
+ rtl8188e_set_FwMediaStatus_cmd(Adapter , (*(__le16 *)val));
+ break;
+ case HW_VAR_BCN_VALID:
+ /* BCN_VALID, BIT16 of REG_TDECTRL = BIT0 of REG_TDECTRL+2, write 1 to clear, Clear by sw */
+ usb_write8(Adapter, REG_TDECTRL+2, usb_read8(Adapter, REG_TDECTRL+2) | BIT0);
+ break;
+ default:
+ break;
+ }
+}
+
+static void GetHwReg8188EU(struct adapter *Adapter, u8 variable, u8 *val)
+{
+ struct hal_data_8188e *haldata = GET_HAL_DATA(Adapter);
+ struct odm_dm_struct *podmpriv = &haldata->odmpriv;
+
+ switch (variable) {
+ case HW_VAR_BASIC_RATE:
+ *((u16 *)(val)) = haldata->BasicRateSet;
+ case HW_VAR_TXPAUSE:
+ val[0] = usb_read8(Adapter, REG_TXPAUSE);
+ break;
+ case HW_VAR_BCN_VALID:
+ /* BCN_VALID, BIT16 of REG_TDECTRL = BIT0 of REG_TDECTRL+2 */
+ val[0] = (BIT0 & usb_read8(Adapter, REG_TDECTRL+2)) ? true : false;
+ break;
+ case HW_VAR_DM_FLAG:
+ val[0] = podmpriv->SupportAbility;
+ break;
+ case HW_VAR_RF_TYPE:
+ val[0] = haldata->rf_type;
+ break;
+ case HW_VAR_FWLPS_RF_ON:
+ {
+ /* When we halt NIC, we should check if FW LPS is leave. */
+ if (Adapter->pwrctrlpriv.rf_pwrstate == rf_off) {
+ /* If it is in HW/SW Radio OFF or IPS state, we do not check Fw LPS Leave, */
+ /* because Fw is unload. */
+ val[0] = true;
+ } else {
+ u32 valRCR;
+ valRCR = usb_read32(Adapter, REG_RCR);
+ valRCR &= 0x00070000;
+ if (valRCR)
+ val[0] = false;
+ else
+ val[0] = true;
+ }
+ }
+ break;
+ case HW_VAR_CURRENT_ANTENNA:
+ val[0] = haldata->CurAntenna;
+ break;
+ case HW_VAR_EFUSE_BYTES: /* To get EFUE total used bytes, added by Roger, 2008.12.22. */
+ *((u16 *)(val)) = haldata->EfuseUsedBytes;
+ break;
+ case HW_VAR_APFM_ON_MAC:
+ *val = haldata->bMacPwrCtrlOn;
+ break;
+ case HW_VAR_CHK_HI_QUEUE_EMPTY:
+ *val = ((usb_read32(Adapter, REG_HGQ_INFORMATION)&0x0000ff00) == 0) ? true : false;
+ break;
+ default:
+ break;
+ }
+
+}
+
+/* */
+/* Description: */
+/* Query setting of specified variable. */
+/* */
+static u8
+GetHalDefVar8188EUsb(
+ struct adapter *Adapter,
+ enum hal_def_variable eVariable,
+ void *pValue
+ )
+{
+ struct hal_data_8188e *haldata = GET_HAL_DATA(Adapter);
+ u8 bResult = _SUCCESS;
+
+ switch (eVariable) {
+ case HAL_DEF_UNDERCORATEDSMOOTHEDPWDB:
+ {
+ struct mlme_priv *pmlmepriv = &Adapter->mlmepriv;
+ struct sta_priv *pstapriv = &Adapter->stapriv;
+ struct sta_info *psta;
+ psta = rtw_get_stainfo(pstapriv, pmlmepriv->cur_network.network.MacAddress);
+ if (psta)
+ *((int *)pValue) = psta->rssi_stat.UndecoratedSmoothedPWDB;
+ }
+ break;
+ case HAL_DEF_IS_SUPPORT_ANT_DIV:
+ *((u8 *)pValue) = (haldata->AntDivCfg == 0) ? false : true;
+ break;
+ case HAL_DEF_CURRENT_ANTENNA:
+ *((u8 *)pValue) = haldata->CurAntenna;
+ break;
+ case HAL_DEF_DRVINFO_SZ:
+ *((u32 *)pValue) = DRVINFO_SZ;
+ break;
+ case HAL_DEF_MAX_RECVBUF_SZ:
+ *((u32 *)pValue) = MAX_RECVBUF_SZ;
+ break;
+ case HAL_DEF_RX_PACKET_OFFSET:
+ *((u32 *)pValue) = RXDESC_SIZE + DRVINFO_SZ;
+ break;
+ case HAL_DEF_DBG_DM_FUNC:
+ *((u32 *)pValue) = haldata->odmpriv.SupportAbility;
+ break;
+ case HAL_DEF_RA_DECISION_RATE:
+ {
+ u8 MacID = *((u8 *)pValue);
+ *((u8 *)pValue) = ODM_RA_GetDecisionRate_8188E(&(haldata->odmpriv), MacID);
+ }
+ break;
+ case HAL_DEF_RA_SGI:
+ {
+ u8 MacID = *((u8 *)pValue);
+ *((u8 *)pValue) = ODM_RA_GetShortGI_8188E(&(haldata->odmpriv), MacID);
+ }
+ break;
+ case HAL_DEF_PT_PWR_STATUS:
+ {
+ u8 MacID = *((u8 *)pValue);
+ *((u8 *)pValue) = ODM_RA_GetHwPwrStatus_8188E(&(haldata->odmpriv), MacID);
+ }
+ break;
+ case HW_VAR_MAX_RX_AMPDU_FACTOR:
+ *((u32 *)pValue) = MAX_AMPDU_FACTOR_64K;
+ break;
+ case HW_DEF_RA_INFO_DUMP:
+ {
+ u8 entry_id = *((u8 *)pValue);
+ if (check_fwstate(&Adapter->mlmepriv, _FW_LINKED)) {
+ DBG_88E("============ RA status check ===================\n");
+ DBG_88E("Mac_id:%d , RateID = %d, RAUseRate = 0x%08x, RateSGI = %d, DecisionRate = 0x%02x ,PTStage = %d\n",
+ entry_id,
+ haldata->odmpriv.RAInfo[entry_id].RateID,
+ haldata->odmpriv.RAInfo[entry_id].RAUseRate,
+ haldata->odmpriv.RAInfo[entry_id].RateSGI,
+ haldata->odmpriv.RAInfo[entry_id].DecisionRate,
+ haldata->odmpriv.RAInfo[entry_id].PTStage);
+ }
+ }
+ break;
+ case HW_DEF_ODM_DBG_FLAG:
+ {
+ struct odm_dm_struct *dm_ocm = &(haldata->odmpriv);
+ pr_info("dm_ocm->DebugComponents = 0x%llx\n", dm_ocm->DebugComponents);
+ }
+ break;
+ case HAL_DEF_DBG_DUMP_RXPKT:
+ *((u8 *)pValue) = haldata->bDumpRxPkt;
+ break;
+ case HAL_DEF_DBG_DUMP_TXPKT:
+ *((u8 *)pValue) = haldata->bDumpTxPkt;
+ break;
+ default:
+ bResult = _FAIL;
+ break;
+ }
+
+ return bResult;
+}
+
+/* */
+/* Description: */
+/* Change default setting of specified variable. */
+/* */
+static u8 SetHalDefVar8188EUsb(struct adapter *Adapter, enum hal_def_variable eVariable, void *pValue)
+{
+ struct hal_data_8188e *haldata = GET_HAL_DATA(Adapter);
+ u8 bResult = _SUCCESS;
+
+ switch (eVariable) {
+ case HAL_DEF_DBG_DM_FUNC:
+ {
+ u8 dm_func = *((u8 *)pValue);
+ struct odm_dm_struct *podmpriv = &haldata->odmpriv;
+
+ if (dm_func == 0) { /* disable all dynamic func */
+ podmpriv->SupportAbility = DYNAMIC_FUNC_DISABLE;
+ DBG_88E("==> Disable all dynamic function...\n");
+ } else if (dm_func == 1) {/* disable DIG */
+ podmpriv->SupportAbility &= (~DYNAMIC_BB_DIG);
+ DBG_88E("==> Disable DIG...\n");
+ } else if (dm_func == 2) {/* disable High power */
+ podmpriv->SupportAbility &= (~DYNAMIC_BB_DYNAMIC_TXPWR);
+ } else if (dm_func == 3) {/* disable tx power tracking */
+ podmpriv->SupportAbility &= (~DYNAMIC_RF_CALIBRATION);
+ DBG_88E("==> Disable tx power tracking...\n");
+ } else if (dm_func == 5) {/* disable antenna diversity */
+ podmpriv->SupportAbility &= (~DYNAMIC_BB_ANT_DIV);
+ } else if (dm_func == 6) {/* turn on all dynamic func */
+ if (!(podmpriv->SupportAbility & DYNAMIC_BB_DIG)) {
+ struct rtw_dig *pDigTable = &podmpriv->DM_DigTable;
+ pDigTable->CurIGValue = usb_read8(Adapter, 0xc50);
+ }
+ podmpriv->SupportAbility = DYNAMIC_ALL_FUNC_ENABLE;
+ DBG_88E("==> Turn on all dynamic function...\n");
+ }
+ }
+ break;
+ case HAL_DEF_DBG_DUMP_RXPKT:
+ haldata->bDumpRxPkt = *((u8 *)pValue);
+ break;
+ case HAL_DEF_DBG_DUMP_TXPKT:
+ haldata->bDumpTxPkt = *((u8 *)pValue);
+ break;
+ case HW_DEF_FA_CNT_DUMP:
+ {
+ u8 bRSSIDump = *((u8 *)pValue);
+ struct odm_dm_struct *dm_ocm = &(haldata->odmpriv);
+ if (bRSSIDump)
+ dm_ocm->DebugComponents = ODM_COMP_DIG|ODM_COMP_FA_CNT;
+ else
+ dm_ocm->DebugComponents = 0;
+ }
+ break;
+ case HW_DEF_ODM_DBG_FLAG:
+ {
+ u64 DebugComponents = *((u64 *)pValue);
+ struct odm_dm_struct *dm_ocm = &(haldata->odmpriv);
+ dm_ocm->DebugComponents = DebugComponents;
+ }
+ break;
+ default:
+ bResult = _FAIL;
+ break;
+ }
+
+ return bResult;
+}
+
+static void UpdateHalRAMask8188EUsb(struct adapter *adapt, u32 mac_id, u8 rssi_level)
+{
+ u8 init_rate = 0;
+ u8 networkType, raid;
+ u32 mask, rate_bitmap;
+ u8 shortGIrate = false;
+ int supportRateNum = 0;
+ struct sta_info *psta;
+ struct hal_data_8188e *haldata = GET_HAL_DATA(adapt);
+ struct mlme_ext_priv *pmlmeext = &adapt->mlmeextpriv;
+ struct mlme_ext_info *pmlmeinfo = &(pmlmeext->mlmext_info);
+ struct wlan_bssid_ex *cur_network = &(pmlmeinfo->network);
+
+ if (mac_id >= NUM_STA) /* CAM_SIZE */
+ return;
+ psta = pmlmeinfo->FW_sta_info[mac_id].psta;
+ if (psta == NULL)
+ return;
+ switch (mac_id) {
+ case 0:/* for infra mode */
+ supportRateNum = rtw_get_rateset_len(cur_network->SupportedRates);
+ networkType = judge_network_type(adapt, cur_network->SupportedRates, supportRateNum) & 0xf;
+ raid = networktype_to_raid(networkType);
+ mask = update_supported_rate(cur_network->SupportedRates, supportRateNum);
+ mask |= (pmlmeinfo->HT_enable) ? update_MSC_rate(&(pmlmeinfo->HT_caps)) : 0;
+ if (support_short_GI(adapt, &(pmlmeinfo->HT_caps)))
+ shortGIrate = true;
+ break;
+ case 1:/* for broadcast/multicast */
+ supportRateNum = rtw_get_rateset_len(pmlmeinfo->FW_sta_info[mac_id].SupportedRates);
+ if (pmlmeext->cur_wireless_mode & WIRELESS_11B)
+ networkType = WIRELESS_11B;
+ else
+ networkType = WIRELESS_11G;
+ raid = networktype_to_raid(networkType);
+ mask = update_basic_rate(cur_network->SupportedRates, supportRateNum);
+ break;
+ default: /* for each sta in IBSS */
+ supportRateNum = rtw_get_rateset_len(pmlmeinfo->FW_sta_info[mac_id].SupportedRates);
+ networkType = judge_network_type(adapt, pmlmeinfo->FW_sta_info[mac_id].SupportedRates, supportRateNum) & 0xf;
+ raid = networktype_to_raid(networkType);
+ mask = update_supported_rate(cur_network->SupportedRates, supportRateNum);
+
+ /* todo: support HT in IBSS */
+ break;
+ }
+
+ rate_bitmap = 0x0fffffff;
+ rate_bitmap = ODM_Get_Rate_Bitmap(&haldata->odmpriv, mac_id, mask, rssi_level);
+ DBG_88E("%s => mac_id:%d, networkType:0x%02x, mask:0x%08x\n\t ==> rssi_level:%d, rate_bitmap:0x%08x\n",
+ __func__, mac_id, networkType, mask, rssi_level, rate_bitmap);
+
+ mask &= rate_bitmap;
+
+ init_rate = get_highest_rate_idx(mask)&0x3f;
+
+ if (haldata->fw_ractrl) {
+ u8 arg;
+
+ arg = mac_id & 0x1f;/* MACID */
+ arg |= BIT(7);
+ if (shortGIrate)
+ arg |= BIT(5);
+ mask |= ((raid << 28) & 0xf0000000);
+ DBG_88E("update raid entry, mask=0x%x, arg=0x%x\n", mask, arg);
+ psta->ra_mask = mask;
+ mask |= ((raid << 28) & 0xf0000000);
+
+ /* to do ,for 8188E-SMIC */
+ rtl8188e_set_raid_cmd(adapt, mask);
+ } else {
+ ODM_RA_UpdateRateInfo_8188E(&(haldata->odmpriv),
+ mac_id,
+ raid,
+ mask,
+ shortGIrate
+ );
+ }
+ /* set ra_id */
+ psta->raid = raid;
+ psta->init_rate = init_rate;
+}
+
+static void SetBeaconRelatedRegisters8188EUsb(struct adapter *adapt)
+{
+ u32 value32;
+ struct mlme_ext_priv *pmlmeext = &(adapt->mlmeextpriv);
+ struct mlme_ext_info *pmlmeinfo = &(pmlmeext->mlmext_info);
+ u32 bcn_ctrl_reg = REG_BCN_CTRL;
+ /* reset TSF, enable update TSF, correcting TSF On Beacon */
+
+ /* BCN interval */
+ usb_write16(adapt, REG_BCN_INTERVAL, pmlmeinfo->bcn_interval);
+ usb_write8(adapt, REG_ATIMWND, 0x02);/* 2ms */
+
+ _InitBeaconParameters(adapt);
+
+ usb_write8(adapt, REG_SLOT, 0x09);
+
+ value32 = usb_read32(adapt, REG_TCR);
+ value32 &= ~TSFRST;
+ usb_write32(adapt, REG_TCR, value32);
+
+ value32 |= TSFRST;
+ usb_write32(adapt, REG_TCR, value32);
+
+ /* NOTE: Fix test chip's bug (about contention windows's randomness) */
+ usb_write8(adapt, REG_RXTSF_OFFSET_CCK, 0x50);
+ usb_write8(adapt, REG_RXTSF_OFFSET_OFDM, 0x50);
+
+ _BeaconFunctionEnable(adapt, true, true);
+
+ ResumeTxBeacon(adapt);
+
+ usb_write8(adapt, bcn_ctrl_reg, usb_read8(adapt, bcn_ctrl_reg)|BIT(1));
+}
+
+static void rtl8188eu_init_default_value(struct adapter *adapt)
+{
+ struct hal_data_8188e *haldata;
+ struct pwrctrl_priv *pwrctrlpriv;
+ u8 i;
+
+ haldata = GET_HAL_DATA(adapt);
+ pwrctrlpriv = &adapt->pwrctrlpriv;
+
+ /* init default value */
+ haldata->fw_ractrl = false;
+ if (!pwrctrlpriv->bkeepfwalive)
+ haldata->LastHMEBoxNum = 0;
+
+ /* init dm default value */
+ haldata->odmpriv.RFCalibrateInfo.bIQKInitialized = false;
+ haldata->odmpriv.RFCalibrateInfo.TM_Trigger = 0;/* for IQK */
+ haldata->pwrGroupCnt = 0;
+ haldata->PGMaxGroup = 13;
+ haldata->odmpriv.RFCalibrateInfo.ThermalValue_HP_index = 0;
+ for (i = 0; i < HP_THERMAL_NUM; i++)
+ haldata->odmpriv.RFCalibrateInfo.ThermalValue_HP[i] = 0;
+}
+
+void rtl8188eu_set_hal_ops(struct adapter *adapt)
+{
+ struct hal_ops *halfunc = &adapt->HalFunc;
+
+
+ adapt->HalData = kzalloc(sizeof(struct hal_data_8188e), GFP_KERNEL);
+ if (adapt->HalData == NULL)
+ DBG_88E("cant not alloc memory for HAL DATA\n");
+
+ halfunc->hal_power_on = rtl8188eu_InitPowerOn;
+ halfunc->hal_init = &rtl8188eu_hal_init;
+ halfunc->hal_deinit = &rtl8188eu_hal_deinit;
+
+ halfunc->inirp_init = &rtl8188eu_inirp_init;
+ halfunc->inirp_deinit = &rtl8188eu_inirp_deinit;
+
+ halfunc->init_xmit_priv = &rtl8188eu_init_xmit_priv;
+
+ halfunc->init_recv_priv = &rtl8188eu_init_recv_priv;
+ halfunc->free_recv_priv = &rtl8188eu_free_recv_priv;
+ halfunc->InitSwLeds = &rtl8188eu_InitSwLeds;
+ halfunc->DeInitSwLeds = &rtl8188eu_DeInitSwLeds;
+
+ halfunc->init_default_value = &rtl8188eu_init_default_value;
+ halfunc->intf_chip_configure = &rtl8188eu_interface_configure;
+ halfunc->read_adapter_info = &_ReadAdapterInfo8188EU;
+
+ halfunc->SetHwRegHandler = &SetHwReg8188EU;
+ halfunc->GetHwRegHandler = &GetHwReg8188EU;
+ halfunc->GetHalDefVarHandler = &GetHalDefVar8188EUsb;
+ halfunc->SetHalDefVarHandler = &SetHalDefVar8188EUsb;
+
+ halfunc->UpdateRAMaskHandler = &UpdateHalRAMask8188EUsb;
+ halfunc->SetBeaconRelatedRegistersHandler = &SetBeaconRelatedRegisters8188EUsb;
+
+ halfunc->hal_xmit = &rtl8188eu_hal_xmit;
+ halfunc->mgnt_xmit = &rtl8188eu_mgnt_xmit;
+
+ rtl8188e_set_hal_ops(halfunc);
+}
diff --git a/drivers/staging/rtl8188eu/include/Hal8188EPhyCfg.h b/drivers/staging/rtl8188eu/include/Hal8188EPhyCfg.h
new file mode 100644
index 000000000..20e6b40fc
--- /dev/null
+++ b/drivers/staging/rtl8188eu/include/Hal8188EPhyCfg.h
@@ -0,0 +1,238 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2011 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ *
+ ******************************************************************************/
+#ifndef __INC_HAL8188EPHYCFG_H__
+#define __INC_HAL8188EPHYCFG_H__
+
+
+/*--------------------------Define Parameters-------------------------------*/
+#define LOOP_LIMIT 5
+#define MAX_STALL_TIME 50 /* us */
+#define AntennaDiversityValue 0x80
+#define MAX_TXPWR_IDX_NMODE_92S 63
+#define Reset_Cnt_Limit 3
+
+#define IQK_MAC_REG_NUM 4
+#define IQK_ADDA_REG_NUM 16
+#define IQK_BB_REG_NUM 9
+#define HP_THERMAL_NUM 8
+
+#define MAX_AGGR_NUM 0x07
+
+
+/*--------------------------Define Parameters-------------------------------*/
+
+
+/*------------------------------Define structure----------------------------*/
+enum sw_chnl_cmd_id {
+ CmdID_End,
+ CmdID_SetTxPowerLevel,
+ CmdID_BBRegWrite10,
+ CmdID_WritePortUlong,
+ CmdID_WritePortUshort,
+ CmdID_WritePortUchar,
+ CmdID_RF_WriteReg,
+};
+
+/* 1. Switch channel related */
+struct sw_chnl_cmd {
+ enum sw_chnl_cmd_id CmdID;
+ u32 Para1;
+ u32 Para2;
+ u32 msDelay;
+};
+
+enum hw90_block {
+ HW90_BLOCK_MAC = 0,
+ HW90_BLOCK_PHY0 = 1,
+ HW90_BLOCK_PHY1 = 2,
+ HW90_BLOCK_RF = 3,
+ HW90_BLOCK_MAXIMUM = 4, /* Never use this */
+};
+
+enum rf_radio_path {
+ RF_PATH_A = 0, /* Radio Path A */
+ RF_PATH_B = 1, /* Radio Path B */
+ RF_PATH_C = 2, /* Radio Path C */
+ RF_PATH_D = 3, /* Radio Path D */
+};
+
+#define MAX_PG_GROUP 13
+
+#define RF_PATH_MAX 3
+#define MAX_RF_PATH RF_PATH_MAX
+#define MAX_TX_COUNT 4 /* path numbers */
+
+#define CHANNEL_MAX_NUMBER 14 /* 14 is the max chnl number */
+#define MAX_CHNL_GROUP_24G 6 /* ch1~2, ch3~5, ch6~8,
+ *ch9~11, ch12~13, CH 14
+ * total three groups */
+#define CHANNEL_GROUP_MAX_88E 6
+
+enum wireless_mode {
+ WIRELESS_MODE_UNKNOWN = 0x00,
+ WIRELESS_MODE_A = BIT2,
+ WIRELESS_MODE_B = BIT0,
+ WIRELESS_MODE_G = BIT1,
+ WIRELESS_MODE_AUTO = BIT5,
+ WIRELESS_MODE_N_24G = BIT3,
+ WIRELESS_MODE_N_5G = BIT4,
+ WIRELESS_MODE_AC = BIT6
+};
+
+enum phy_rate_tx_offset_area {
+ RA_OFFSET_LEGACY_OFDM1,
+ RA_OFFSET_LEGACY_OFDM2,
+ RA_OFFSET_HT_OFDM1,
+ RA_OFFSET_HT_OFDM2,
+ RA_OFFSET_HT_OFDM3,
+ RA_OFFSET_HT_OFDM4,
+ RA_OFFSET_HT_CCK,
+};
+
+/* BB/RF related */
+enum RF_TYPE_8190P {
+ RF_TYPE_MIN, /* 0 */
+ RF_8225 = 1, /* 1 11b/g RF for verification only */
+ RF_8256 = 2, /* 2 11b/g/n */
+ RF_8258 = 3, /* 3 11a/b/g/n RF */
+ RF_6052 = 4, /* 4 11b/g/n RF */
+ /* TODO: We should remove this psudo PHY RF after we get new RF. */
+ RF_PSEUDO_11N = 5, /* 5, It is a temporality RF. */
+};
+
+struct bb_reg_def {
+ u32 rfintfs; /* set software control: */
+ /* 0x870~0x877[8 bytes] */
+ u32 rfintfi; /* readback data: */
+ /* 0x8e0~0x8e7[8 bytes] */
+ u32 rfintfo; /* output data: */
+ /* 0x860~0x86f [16 bytes] */
+ u32 rfintfe; /* output enable: */
+ /* 0x860~0x86f [16 bytes] */
+ u32 rf3wireOffset; /* LSSI data: */
+ /* 0x840~0x84f [16 bytes] */
+ u32 rfLSSI_Select; /* BB Band Select: */
+ /* 0x878~0x87f [8 bytes] */
+ u32 rfTxGainStage; /* Tx gain stage: */
+ /* 0x80c~0x80f [4 bytes] */
+ u32 rfHSSIPara1; /* wire parameter control1 : */
+ /* 0x820~0x823,0x828~0x82b,
+ * 0x830~0x833, 0x838~0x83b [16 bytes] */
+ u32 rfHSSIPara2; /* wire parameter control2 : */
+ /* 0x824~0x827,0x82c~0x82f, 0x834~0x837,
+ * 0x83c~0x83f [16 bytes] */
+ u32 rfSwitchControl; /* Tx Rx antenna control : */
+ /* 0x858~0x85f [16 bytes] */
+ u32 rfAGCControl1; /* AGC parameter control1 : */
+ /* 0xc50~0xc53,0xc58~0xc5b, 0xc60~0xc63,
+ * 0xc68~0xc6b [16 bytes] */
+ u32 rfAGCControl2; /* AGC parameter control2 : */
+ /* 0xc54~0xc57,0xc5c~0xc5f, 0xc64~0xc67,
+ * 0xc6c~0xc6f [16 bytes] */
+ u32 rfRxIQImbalance; /* OFDM Rx IQ imbalance matrix : */
+ /* 0xc14~0xc17,0xc1c~0xc1f, 0xc24~0xc27,
+ * 0xc2c~0xc2f [16 bytes] */
+ u32 rfRxAFE; /* Rx IQ DC ofset and Rx digital filter,
+ * Rx DC notch filter : */
+ /* 0xc10~0xc13,0xc18~0xc1b, 0xc20~0xc23,
+ * 0xc28~0xc2b [16 bytes] */
+ u32 rfTxIQImbalance; /* OFDM Tx IQ imbalance matrix */
+ /* 0xc80~0xc83,0xc88~0xc8b, 0xc90~0xc93,
+ * 0xc98~0xc9b [16 bytes] */
+ u32 rfTxAFE; /* Tx IQ DC Offset and Tx DFIR type */
+ /* 0xc84~0xc87,0xc8c~0xc8f, 0xc94~0xc97,
+ * 0xc9c~0xc9f [16 bytes] */
+ u32 rfLSSIReadBack; /* LSSI RF readback data SI mode */
+ /* 0x8a0~0x8af [16 bytes] */
+ u32 rfLSSIReadBackPi; /* LSSI RF readback data PI mode 0x8b8-8bc for
+ * Path A and B */
+};
+
+struct ant_sel_ofdm {
+ u32 r_tx_antenna:4;
+ u32 r_ant_l:4;
+ u32 r_ant_non_ht:4;
+ u32 r_ant_ht1:4;
+ u32 r_ant_ht2:4;
+ u32 r_ant_ht_s1:4;
+ u32 r_ant_non_ht_s1:4;
+ u32 OFDM_TXSC:2;
+ u32 reserved:2;
+};
+
+struct ant_sel_cck {
+ u8 r_cckrx_enable_2:2;
+ u8 r_cckrx_enable:2;
+ u8 r_ccktx_enable:4;
+};
+
+/*------------------------------Define structure----------------------------*/
+
+
+/*------------------------Export global variable----------------------------*/
+/*------------------------Export global variable----------------------------*/
+
+
+/*------------------------Export Marco Definition---------------------------*/
+/*------------------------Export Marco Definition---------------------------*/
+
+
+/*--------------------------Exported Function prototype---------------------*/
+/* */
+/* BB and RF register read/write */
+/* */
+
+/* Read initi reg value for tx power setting. */
+void rtl8192c_PHY_GetHWRegOriginalValue(struct adapter *adapter);
+
+/* BB TX Power R/W */
+void PHY_GetTxPowerLevel8188E(struct adapter *adapter, u32 *powerlevel);
+
+void PHY_ScanOperationBackup8188E(struct adapter *Adapter, u8 Operation);
+
+/* Call after initialization */
+void ChkFwCmdIoDone(struct adapter *adapter);
+
+/* BB/MAC/RF other monitor API */
+void PHY_SetRFPathSwitch_8188E(struct adapter *adapter, bool main);
+
+void PHY_SwitchEphyParameter(struct adapter *adapter);
+
+void PHY_EnableHostClkReq(struct adapter *adapter);
+
+bool SetAntennaConfig92C(struct adapter *adapter, u8 defaultant);
+
+/*--------------------------Exported Function prototype---------------------*/
+
+#define PHY_SetMacReg PHY_SetBBReg
+
+#define SIC_HW_SUPPORT 0
+
+#define SIC_MAX_POLL_CNT 5
+
+#define SIC_CMD_READY 0
+#define SIC_CMD_WRITE 1
+#define SIC_CMD_READ 2
+
+#define SIC_CMD_REG 0x1EB /* 1byte */
+#define SIC_ADDR_REG 0x1E8 /* 1b9~1ba, 2 bytes */
+#define SIC_DATA_REG 0x1EC /* 1bc~1bf */
+
+#endif /* __INC_HAL8192CPHYCFG_H */
diff --git a/drivers/staging/rtl8188eu/include/Hal8188EPhyReg.h b/drivers/staging/rtl8188eu/include/Hal8188EPhyReg.h
new file mode 100644
index 000000000..9f2969bf8
--- /dev/null
+++ b/drivers/staging/rtl8188eu/include/Hal8188EPhyReg.h
@@ -0,0 +1,1094 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2011 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ *
+ ******************************************************************************/
+#ifndef __INC_HAL8188EPHYREG_H__
+#define __INC_HAL8188EPHYREG_H__
+/*--------------------------Define Parameters-------------------------------*/
+/* */
+/* BB-PHY register PMAC 0x100 PHY 0x800 - 0xEFF */
+/* 1. PMAC duplicate register due to connection: RF_Mode, TRxRN, NumOf L-STF */
+/* 2. 0x800/0x900/0xA00/0xC00/0xD00/0xE00 */
+/* 3. RF register 0x00-2E */
+/* 4. Bit Mask for BB/RF register */
+/* 5. Other definition for BB/RF R/W */
+/* */
+
+
+/* */
+/* 1. PMAC duplicate register due to connection: RF_Mode, TRxRN, NumOf L-STF */
+/* 1. Page1(0x100) */
+/* */
+#define rPMAC_Reset 0x100
+#define rPMAC_TxStart 0x104
+#define rPMAC_TxLegacySIG 0x108
+#define rPMAC_TxHTSIG1 0x10c
+#define rPMAC_TxHTSIG2 0x110
+#define rPMAC_PHYDebug 0x114
+#define rPMAC_TxPacketNum 0x118
+#define rPMAC_TxIdle 0x11c
+#define rPMAC_TxMACHeader0 0x120
+#define rPMAC_TxMACHeader1 0x124
+#define rPMAC_TxMACHeader2 0x128
+#define rPMAC_TxMACHeader3 0x12c
+#define rPMAC_TxMACHeader4 0x130
+#define rPMAC_TxMACHeader5 0x134
+#define rPMAC_TxDataType 0x138
+#define rPMAC_TxRandomSeed 0x13c
+#define rPMAC_CCKPLCPPreamble 0x140
+#define rPMAC_CCKPLCPHeader 0x144
+#define rPMAC_CCKCRC16 0x148
+#define rPMAC_OFDMRxCRC32OK 0x170
+#define rPMAC_OFDMRxCRC32Er 0x174
+#define rPMAC_OFDMRxParityEr 0x178
+#define rPMAC_OFDMRxCRC8Er 0x17c
+#define rPMAC_CCKCRxRC16Er 0x180
+#define rPMAC_CCKCRxRC32Er 0x184
+#define rPMAC_CCKCRxRC32OK 0x188
+#define rPMAC_TxStatus 0x18c
+
+/* 2. Page2(0x200) */
+/* The following two definition are only used for USB interface. */
+#define RF_BB_CMD_ADDR 0x02c0 /* RF/BB r/w cmd address. */
+#define RF_BB_CMD_DATA 0x02c4 /* RF/BB r/w cmd data. */
+
+/* 3. Page8(0x800) */
+#define rFPGA0_RFMOD 0x800 /* RF mode & CCK TxSC RF BW Setting */
+
+#define rFPGA0_TxInfo 0x804 /* Status report?? */
+#define rFPGA0_PSDFunction 0x808
+
+#define rFPGA0_TxGainStage 0x80c /* Set TX PWR init gain? */
+
+#define rFPGA0_RFTiming1 0x810 /* Useless now */
+#define rFPGA0_RFTiming2 0x814
+
+#define rFPGA0_XA_HSSIParameter1 0x820 /* RF 3 wire register */
+#define rFPGA0_XA_HSSIParameter2 0x824
+#define rFPGA0_XB_HSSIParameter1 0x828
+#define rFPGA0_XB_HSSIParameter2 0x82c
+
+#define rFPGA0_XA_LSSIParameter 0x840
+#define rFPGA0_XB_LSSIParameter 0x844
+
+#define rFPGA0_RFWakeUpParameter 0x850 /* Useless now */
+#define rFPGA0_RFSleepUpParameter 0x854
+
+#define rFPGA0_XAB_SwitchControl 0x858 /* RF Channel switch */
+#define rFPGA0_XCD_SwitchControl 0x85c
+
+#define rFPGA0_XA_RFInterfaceOE 0x860 /* RF Channel switch */
+#define rFPGA0_XB_RFInterfaceOE 0x864
+
+#define rFPGA0_XAB_RFInterfaceSW 0x870 /* RF Iface Software Control */
+#define rFPGA0_XCD_RFInterfaceSW 0x874
+
+#define rFPGA0_XAB_RFParameter 0x878 /* RF Parameter */
+#define rFPGA0_XCD_RFParameter 0x87c
+
+/* Crystal cap setting RF-R/W protection for parameter4?? */
+#define rFPGA0_AnalogParameter1 0x880
+#define rFPGA0_AnalogParameter2 0x884
+#define rFPGA0_AnalogParameter3 0x888
+/* enable ad/da clock1 for dual-phy */
+#define rFPGA0_AdDaClockEn 0x888
+#define rFPGA0_AnalogParameter4 0x88c
+
+#define rFPGA0_XA_LSSIReadBack 0x8a0 /* Tranceiver LSSI Readback */
+#define rFPGA0_XB_LSSIReadBack 0x8a4
+#define rFPGA0_XC_LSSIReadBack 0x8a8
+#define rFPGA0_XD_LSSIReadBack 0x8ac
+
+#define rFPGA0_PSDReport 0x8b4 /* Useless now */
+/* Transceiver A HSPI Readback */
+#define TransceiverA_HSPI_Readback 0x8b8
+/* Transceiver B HSPI Readback */
+#define TransceiverB_HSPI_Readback 0x8bc
+/* Useless now RF Interface Readback Value */
+#define rFPGA0_XAB_RFInterfaceRB 0x8e0
+#define rFPGA0_XCD_RFInterfaceRB 0x8e4 /* Useless now */
+
+/* 4. Page9(0x900) */
+/* RF mode & OFDM TxSC RF BW Setting?? */
+#define rFPGA1_RFMOD 0x900
+
+#define rFPGA1_TxBlock 0x904 /* Useless now */
+#define rFPGA1_DebugSelect 0x908 /* Useless now */
+#define rFPGA1_TxInfo 0x90c /* Useless now Status report */
+
+/* 5. PageA(0xA00) */
+/* Set Control channel to upper or lower - required only for 40MHz */
+#define rCCK0_System 0xa00
+
+/* Disable init gain now Select RX path by RSSI */
+#define rCCK0_AFESetting 0xa04
+/* Disable init gain now Init gain */
+#define rCCK0_CCA 0xa08
+
+/* AGC default value, saturation level Antenna Diversity, RX AGC, LNA Threshold,
+ * RX LNA Threshold useless now. Not the same as 90 series */
+#define rCCK0_RxAGC1 0xa0c
+#define rCCK0_RxAGC2 0xa10 /* AGC & DAGC */
+
+#define rCCK0_RxHP 0xa14
+
+/* Timing recovery & Channel estimation threshold */
+#define rCCK0_DSPParameter1 0xa18
+#define rCCK0_DSPParameter2 0xa1c /* SQ threshold */
+
+#define rCCK0_TxFilter1 0xa20
+#define rCCK0_TxFilter2 0xa24
+#define rCCK0_DebugPort 0xa28 /* debug port and Tx filter3 */
+#define rCCK0_FalseAlarmReport 0xa2c /* 0xa2d useless now */
+#define rCCK0_TRSSIReport 0xa50
+#define rCCK0_RxReport 0xa54 /* 0xa57 */
+#define rCCK0_FACounterLower 0xa5c /* 0xa5b */
+#define rCCK0_FACounterUpper 0xa58 /* 0xa5c */
+
+/* */
+/* PageB(0xB00) */
+/* */
+#define rPdp_AntA 0xb00
+#define rPdp_AntA_4 0xb04
+#define rConfig_Pmpd_AntA 0xb28
+#define rConfig_AntA 0xb68
+#define rConfig_AntB 0xb6c
+#define rPdp_AntB 0xb70
+#define rPdp_AntB_4 0xb74
+#define rConfig_Pmpd_AntB 0xb98
+#define rAPK 0xbd8
+
+/* */
+/* 6. PageC(0xC00) */
+/* */
+#define rOFDM0_LSTF 0xc00
+
+#define rOFDM0_TRxPathEnable 0xc04
+#define rOFDM0_TRMuxPar 0xc08
+#define rOFDM0_TRSWIsolation 0xc0c
+
+/* RxIQ DC offset, Rx digital filter, DC notch filter */
+#define rOFDM0_XARxAFE 0xc10
+#define rOFDM0_XARxIQImbalance 0xc14 /* RxIQ imblance matrix */
+#define rOFDM0_XBRxAFE 0xc18
+#define rOFDM0_XBRxIQImbalance 0xc1c
+#define rOFDM0_XCRxAFE 0xc20
+#define rOFDM0_XCRxIQImbalance 0xc24
+#define rOFDM0_XDRxAFE 0xc28
+#define rOFDM0_XDRxIQImbalance 0xc2c
+
+#define rOFDM0_RxDetector1 0xc30 /*PD,BW & SBD DM tune init gain*/
+#define rOFDM0_RxDetector2 0xc34 /* SBD & Fame Sync. */
+#define rOFDM0_RxDetector3 0xc38 /* Frame Sync. */
+#define rOFDM0_RxDetector4 0xc3c /* PD, SBD, Frame Sync & Short-GI */
+
+#define rOFDM0_RxDSP 0xc40 /* Rx Sync Path */
+#define rOFDM0_CFOandDAGC 0xc44 /* CFO & DAGC */
+#define rOFDM0_CCADropThreshold 0xc48 /* CCA Drop threshold */
+#define rOFDM0_ECCAThreshold 0xc4c /* energy CCA */
+
+#define rOFDM0_XAAGCCore1 0xc50 /* DIG */
+#define rOFDM0_XAAGCCore2 0xc54
+#define rOFDM0_XBAGCCore1 0xc58
+#define rOFDM0_XBAGCCore2 0xc5c
+#define rOFDM0_XCAGCCore1 0xc60
+#define rOFDM0_XCAGCCore2 0xc64
+#define rOFDM0_XDAGCCore1 0xc68
+#define rOFDM0_XDAGCCore2 0xc6c
+
+#define rOFDM0_AGCParameter1 0xc70
+#define rOFDM0_AGCParameter2 0xc74
+#define rOFDM0_AGCRSSITable 0xc78
+#define rOFDM0_HTSTFAGC 0xc7c
+
+#define rOFDM0_XATxIQImbalance 0xc80 /* TX PWR TRACK and DIG */
+#define rOFDM0_XATxAFE 0xc84
+#define rOFDM0_XBTxIQImbalance 0xc88
+#define rOFDM0_XBTxAFE 0xc8c
+#define rOFDM0_XCTxIQImbalance 0xc90
+#define rOFDM0_XCTxAFE 0xc94
+#define rOFDM0_XDTxIQImbalance 0xc98
+#define rOFDM0_XDTxAFE 0xc9c
+
+#define rOFDM0_RxIQExtAnta 0xca0
+#define rOFDM0_TxCoeff1 0xca4
+#define rOFDM0_TxCoeff2 0xca8
+#define rOFDM0_TxCoeff3 0xcac
+#define rOFDM0_TxCoeff4 0xcb0
+#define rOFDM0_TxCoeff5 0xcb4
+#define rOFDM0_TxCoeff6 0xcb8
+#define rOFDM0_RxHPParameter 0xce0
+#define rOFDM0_TxPseudoNoiseWgt 0xce4
+#define rOFDM0_FrameSync 0xcf0
+#define rOFDM0_DFSReport 0xcf4
+
+
+/* */
+/* 7. PageD(0xD00) */
+/* */
+#define rOFDM1_LSTF 0xd00
+#define rOFDM1_TRxPathEnable 0xd04
+
+#define rOFDM1_CFO 0xd08 /* No setting now */
+#define rOFDM1_CSI1 0xd10
+#define rOFDM1_SBD 0xd14
+#define rOFDM1_CSI2 0xd18
+#define rOFDM1_CFOTracking 0xd2c
+#define rOFDM1_TRxMesaure1 0xd34
+#define rOFDM1_IntfDet 0xd3c
+#define rOFDM1_PseudoNoiseStateAB 0xd50
+#define rOFDM1_PseudoNoiseStateCD 0xd54
+#define rOFDM1_RxPseudoNoiseWgt 0xd58
+
+#define rOFDM_PHYCounter1 0xda0 /* cca, parity fail */
+#define rOFDM_PHYCounter2 0xda4 /* rate illegal, crc8 fail */
+#define rOFDM_PHYCounter3 0xda8 /* MCS not support */
+
+#define rOFDM_ShortCFOAB 0xdac /* No setting now */
+#define rOFDM_ShortCFOCD 0xdb0
+#define rOFDM_LongCFOAB 0xdb4
+#define rOFDM_LongCFOCD 0xdb8
+#define rOFDM_TailCFOAB 0xdbc
+#define rOFDM_TailCFOCD 0xdc0
+#define rOFDM_PWMeasure1 0xdc4
+#define rOFDM_PWMeasure2 0xdc8
+#define rOFDM_BWReport 0xdcc
+#define rOFDM_AGCReport 0xdd0
+#define rOFDM_RxSNR 0xdd4
+#define rOFDM_RxEVMCSI 0xdd8
+#define rOFDM_SIGReport 0xddc
+
+
+/* */
+/* 8. PageE(0xE00) */
+/* */
+#define rTxAGC_A_Rate18_06 0xe00
+#define rTxAGC_A_Rate54_24 0xe04
+#define rTxAGC_A_CCK1_Mcs32 0xe08
+#define rTxAGC_A_Mcs03_Mcs00 0xe10
+#define rTxAGC_A_Mcs07_Mcs04 0xe14
+#define rTxAGC_A_Mcs11_Mcs08 0xe18
+#define rTxAGC_A_Mcs15_Mcs12 0xe1c
+
+#define rTxAGC_B_Rate18_06 0x830
+#define rTxAGC_B_Rate54_24 0x834
+#define rTxAGC_B_CCK1_55_Mcs32 0x838
+#define rTxAGC_B_Mcs03_Mcs00 0x83c
+#define rTxAGC_B_Mcs07_Mcs04 0x848
+#define rTxAGC_B_Mcs11_Mcs08 0x84c
+#define rTxAGC_B_Mcs15_Mcs12 0x868
+#define rTxAGC_B_CCK11_A_CCK2_11 0x86c
+
+#define rFPGA0_IQK 0xe28
+#define rTx_IQK_Tone_A 0xe30
+#define rRx_IQK_Tone_A 0xe34
+#define rTx_IQK_PI_A 0xe38
+#define rRx_IQK_PI_A 0xe3c
+
+#define rTx_IQK 0xe40
+#define rRx_IQK 0xe44
+#define rIQK_AGC_Pts 0xe48
+#define rIQK_AGC_Rsp 0xe4c
+#define rTx_IQK_Tone_B 0xe50
+#define rRx_IQK_Tone_B 0xe54
+#define rTx_IQK_PI_B 0xe58
+#define rRx_IQK_PI_B 0xe5c
+#define rIQK_AGC_Cont 0xe60
+
+#define rBlue_Tooth 0xe6c
+#define rRx_Wait_CCA 0xe70
+#define rTx_CCK_RFON 0xe74
+#define rTx_CCK_BBON 0xe78
+#define rTx_OFDM_RFON 0xe7c
+#define rTx_OFDM_BBON 0xe80
+#define rTx_To_Rx 0xe84
+#define rTx_To_Tx 0xe88
+#define rRx_CCK 0xe8c
+
+#define rTx_Power_Before_IQK_A 0xe94
+#define rTx_Power_After_IQK_A 0xe9c
+
+#define rRx_Power_Before_IQK_A 0xea0
+#define rRx_Power_Before_IQK_A_2 0xea4
+#define rRx_Power_After_IQK_A 0xea8
+#define rRx_Power_After_IQK_A_2 0xeac
+
+#define rTx_Power_Before_IQK_B 0xeb4
+#define rTx_Power_After_IQK_B 0xebc
+
+#define rRx_Power_Before_IQK_B 0xec0
+#define rRx_Power_Before_IQK_B_2 0xec4
+#define rRx_Power_After_IQK_B 0xec8
+#define rRx_Power_After_IQK_B_2 0xecc
+
+#define rRx_OFDM 0xed0
+#define rRx_Wait_RIFS 0xed4
+#define rRx_TO_Rx 0xed8
+#define rStandby 0xedc
+#define rSleep 0xee0
+#define rPMPD_ANAEN 0xeec
+
+/* */
+/* 7. RF Register 0x00-0x2E (RF 8256) */
+/* RF-0222D 0x00-3F */
+/* */
+/* Zebra1 */
+#define rZebra1_HSSIEnable 0x0 /* Useless now */
+#define rZebra1_TRxEnable1 0x1
+#define rZebra1_TRxEnable2 0x2
+#define rZebra1_AGC 0x4
+#define rZebra1_ChargePump 0x5
+#define rZebra1_Channel 0x7 /* RF channel switch */
+
+/* endif */
+#define rZebra1_TxGain 0x8 /* Useless now */
+#define rZebra1_TxLPF 0x9
+#define rZebra1_RxLPF 0xb
+#define rZebra1_RxHPFCorner 0xc
+
+/* Zebra4 */
+#define rGlobalCtrl 0 /* Useless now */
+#define rRTL8256_TxLPF 19
+#define rRTL8256_RxLPF 11
+
+/* RTL8258 */
+#define rRTL8258_TxLPF 0x11 /* Useless now */
+#define rRTL8258_RxLPF 0x13
+#define rRTL8258_RSSILPF 0xa
+
+/* */
+/* RL6052 Register definition */
+/* */
+#define RF_AC 0x00 /* */
+
+#define RF_IQADJ_G1 0x01 /* */
+#define RF_IQADJ_G2 0x02 /* */
+
+#define RF_POW_TRSW 0x05 /* */
+
+#define RF_GAIN_RX 0x06 /* */
+#define RF_GAIN_TX 0x07 /* */
+
+#define RF_TXM_IDAC 0x08 /* */
+#define RF_IPA_G 0x09 /* */
+#define RF_TXBIAS_G 0x0A
+#define RF_TXPA_AG 0x0B
+#define RF_IPA_A 0x0C /* */
+#define RF_TXBIAS_A 0x0D
+#define RF_BS_PA_APSET_G9_G11 0x0E
+#define RF_BS_IQGEN 0x0F /* */
+
+#define RF_MODE1 0x10 /* */
+#define RF_MODE2 0x11 /* */
+
+#define RF_RX_AGC_HP 0x12 /* */
+#define RF_TX_AGC 0x13 /* */
+#define RF_BIAS 0x14 /* */
+#define RF_IPA 0x15 /* */
+#define RF_TXBIAS 0x16
+#define RF_POW_ABILITY 0x17 /* */
+#define RF_CHNLBW 0x18 /* RF channel and BW switch */
+#define RF_TOP 0x19 /* */
+
+#define RF_RX_G1 0x1A /* */
+#define RF_RX_G2 0x1B /* */
+
+#define RF_RX_BB2 0x1C /* */
+#define RF_RX_BB1 0x1D /* */
+
+#define RF_RCK1 0x1E /* */
+#define RF_RCK2 0x1F /* */
+
+#define RF_TX_G1 0x20 /* */
+#define RF_TX_G2 0x21 /* */
+#define RF_TX_G3 0x22 /* */
+
+#define RF_TX_BB1 0x23 /* */
+
+#define RF_T_METER_92D 0x42 /* */
+#define RF_T_METER_88E 0x42 /* */
+#define RF_T_METER 0x24 /* */
+
+#define RF_SYN_G1 0x25 /* RF TX Power control */
+#define RF_SYN_G2 0x26 /* RF TX Power control */
+#define RF_SYN_G3 0x27 /* RF TX Power control */
+#define RF_SYN_G4 0x28 /* RF TX Power control */
+#define RF_SYN_G5 0x29 /* RF TX Power control */
+#define RF_SYN_G6 0x2A /* RF TX Power control */
+#define RF_SYN_G7 0x2B /* RF TX Power control */
+#define RF_SYN_G8 0x2C /* RF TX Power control */
+
+#define RF_RCK_OS 0x30 /* RF TX PA control */
+#define RF_TXPA_G1 0x31 /* RF TX PA control */
+#define RF_TXPA_G2 0x32 /* RF TX PA control */
+#define RF_TXPA_G3 0x33 /* RF TX PA control */
+#define RF_TX_BIAS_A 0x35
+#define RF_TX_BIAS_D 0x36
+#define RF_LOBF_9 0x38
+#define RF_RXRF_A3 0x3C /* */
+#define RF_TRSW 0x3F
+
+#define RF_TXRF_A2 0x41
+#define RF_TXPA_G4 0x46
+#define RF_TXPA_A4 0x4B
+#define RF_0x52 0x52
+#define RF_WE_LUT 0xEF
+
+
+/* */
+/* Bit Mask */
+/* */
+/* 1. Page1(0x100) */
+#define bBBResetB 0x100 /* Useless now? */
+#define bGlobalResetB 0x200
+#define bOFDMTxStart 0x4
+#define bCCKTxStart 0x8
+#define bCRC32Debug 0x100
+#define bPMACLoopback 0x10
+#define bTxLSIG 0xffffff
+#define bOFDMTxRate 0xf
+#define bOFDMTxReserved 0x10
+#define bOFDMTxLength 0x1ffe0
+#define bOFDMTxParity 0x20000
+#define bTxHTSIG1 0xffffff
+#define bTxHTMCSRate 0x7f
+#define bTxHTBW 0x80
+#define bTxHTLength 0xffff00
+#define bTxHTSIG2 0xffffff
+#define bTxHTSmoothing 0x1
+#define bTxHTSounding 0x2
+#define bTxHTReserved 0x4
+#define bTxHTAggreation 0x8
+#define bTxHTSTBC 0x30
+#define bTxHTAdvanceCoding 0x40
+#define bTxHTShortGI 0x80
+#define bTxHTNumberHT_LTF 0x300
+#define bTxHTCRC8 0x3fc00
+#define bCounterReset 0x10000
+#define bNumOfOFDMTx 0xffff
+#define bNumOfCCKTx 0xffff0000
+#define bTxIdleInterval 0xffff
+#define bOFDMService 0xffff0000
+#define bTxMACHeader 0xffffffff
+#define bTxDataInit 0xff
+#define bTxHTMode 0x100
+#define bTxDataType 0x30000
+#define bTxRandomSeed 0xffffffff
+#define bCCKTxPreamble 0x1
+#define bCCKTxSFD 0xffff0000
+#define bCCKTxSIG 0xff
+#define bCCKTxService 0xff00
+#define bCCKLengthExt 0x8000
+#define bCCKTxLength 0xffff0000
+#define bCCKTxCRC16 0xffff
+#define bCCKTxStatus 0x1
+#define bOFDMTxStatus 0x2
+
+#define IS_BB_REG_OFFSET_92S(_Offset) \
+ ((_Offset >= 0x800) && (_Offset <= 0xfff))
+
+/* 2. Page8(0x800) */
+#define bRFMOD 0x1 /* Reg 0x800 rFPGA0_RFMOD */
+#define bJapanMode 0x2
+#define bCCKTxSC 0x30
+#define bCCKEn 0x1000000
+#define bOFDMEn 0x2000000
+
+#define bOFDMRxADCPhase 0x10000 /* Useless now */
+#define bOFDMTxDACPhase 0x40000
+#define bXATxAGC 0x3f
+
+#define bAntennaSelect 0x0300
+
+#define bXBTxAGC 0xf00 /* Reg 80c rFPGA0_TxGainStage */
+#define bXCTxAGC 0xf000
+#define bXDTxAGC 0xf0000
+
+#define bPAStart 0xf0000000 /* Useless now */
+#define bTRStart 0x00f00000
+#define bRFStart 0x0000f000
+#define bBBStart 0x000000f0
+#define bBBCCKStart 0x0000000f
+#define bPAEnd 0xf /* Reg0x814 */
+#define bTREnd 0x0f000000
+#define bRFEnd 0x000f0000
+#define bCCAMask 0x000000f0 /* T2R */
+#define bR2RCCAMask 0x00000f00
+#define bHSSI_R2TDelay 0xf8000000
+#define bHSSI_T2RDelay 0xf80000
+#define bContTxHSSI 0x400 /* change gain at continue Tx */
+#define bIGFromCCK 0x200
+#define bAGCAddress 0x3f
+#define bRxHPTx 0x7000
+#define bRxHPT2R 0x38000
+#define bRxHPCCKIni 0xc0000
+#define bAGCTxCode 0xc00000
+#define bAGCRxCode 0x300000
+
+/* Reg 0x820~84f rFPGA0_XA_HSSIParameter1 */
+#define b3WireDataLength 0x800
+#define b3WireAddressLength 0x400
+
+#define b3WireRFPowerDown 0x1 /* Useless now */
+#define b5GPAPEPolarity 0x40000000
+#define b2GPAPEPolarity 0x80000000
+#define bRFSW_TxDefaultAnt 0x3
+#define bRFSW_TxOptionAnt 0x30
+#define bRFSW_RxDefaultAnt 0x300
+#define bRFSW_RxOptionAnt 0x3000
+#define bRFSI_3WireData 0x1
+#define bRFSI_3WireClock 0x2
+#define bRFSI_3WireLoad 0x4
+#define bRFSI_3WireRW 0x8
+#define bRFSI_3Wire 0xf
+
+#define bRFSI_RFENV 0x10 /* Reg 0x870 rFPGA0_XAB_RFInterfaceSW */
+
+#define bRFSI_TRSW 0x20 /* Useless now */
+#define bRFSI_TRSWB 0x40
+#define bRFSI_ANTSW 0x100
+#define bRFSI_ANTSWB 0x200
+#define bRFSI_PAPE 0x400
+#define bRFSI_PAPE5G 0x800
+#define bBandSelect 0x1
+#define bHTSIG2_GI 0x80
+#define bHTSIG2_Smoothing 0x01
+#define bHTSIG2_Sounding 0x02
+#define bHTSIG2_Aggreaton 0x08
+#define bHTSIG2_STBC 0x30
+#define bHTSIG2_AdvCoding 0x40
+#define bHTSIG2_NumOfHTLTF 0x300
+#define bHTSIG2_CRC8 0x3fc
+#define bHTSIG1_MCS 0x7f
+#define bHTSIG1_BandWidth 0x80
+#define bHTSIG1_HTLength 0xffff
+#define bLSIG_Rate 0xf
+#define bLSIG_Reserved 0x10
+#define bLSIG_Length 0x1fffe
+#define bLSIG_Parity 0x20
+#define bCCKRxPhase 0x4
+
+#define bLSSIReadAddress 0x7f800000 /* T65 RF */
+
+#define bLSSIReadEdge 0x80000000 /* LSSI "Read" edge signal */
+
+#define bLSSIReadBackData 0xfffff /* T65 RF */
+
+#define bLSSIReadOKFlag 0x1000 /* Useless now */
+#define bCCKSampleRate 0x8 /* 0: 44MHz, 1:88MHz */
+#define bRegulator0Standby 0x1
+#define bRegulatorPLLStandby 0x2
+#define bRegulator1Standby 0x4
+#define bPLLPowerUp 0x8
+#define bDPLLPowerUp 0x10
+#define bDA10PowerUp 0x20
+#define bAD7PowerUp 0x200
+#define bDA6PowerUp 0x2000
+#define bXtalPowerUp 0x4000
+#define b40MDClkPowerUP 0x8000
+#define bDA6DebugMode 0x20000
+#define bDA6Swing 0x380000
+
+/* Reg 0x880 rFPGA0_AnalogParameter1 20/40 CCK support switch 40/80 BB MHZ */
+#define bADClkPhase 0x4000000
+
+#define b80MClkDelay 0x18000000 /* Useless */
+#define bAFEWatchDogEnable 0x20000000
+
+/* Reg 0x884 rFPGA0_AnalogParameter2 Crystal cap */
+#define bXtalCap01 0xc0000000
+#define bXtalCap23 0x3
+#define bXtalCap92x 0x0f000000
+#define bXtalCap 0x0f000000
+
+#define bIntDifClkEnable 0x400 /* Useless */
+#define bExtSigClkEnable 0x800
+#define bBandgapMbiasPowerUp 0x10000
+#define bAD11SHGain 0xc0000
+#define bAD11InputRange 0x700000
+#define bAD11OPCurrent 0x3800000
+#define bIPathLoopback 0x4000000
+#define bQPathLoopback 0x8000000
+#define bAFELoopback 0x10000000
+#define bDA10Swing 0x7e0
+#define bDA10Reverse 0x800
+#define bDAClkSource 0x1000
+#define bAD7InputRange 0x6000
+#define bAD7Gain 0x38000
+#define bAD7OutputCMMode 0x40000
+#define bAD7InputCMMode 0x380000
+#define bAD7Current 0xc00000
+#define bRegulatorAdjust 0x7000000
+#define bAD11PowerUpAtTx 0x1
+#define bDA10PSAtTx 0x10
+#define bAD11PowerUpAtRx 0x100
+#define bDA10PSAtRx 0x1000
+#define bCCKRxAGCFormat 0x200
+#define bPSDFFTSamplepPoint 0xc000
+#define bPSDAverageNum 0x3000
+#define bIQPathControl 0xc00
+#define bPSDFreq 0x3ff
+#define bPSDAntennaPath 0x30
+#define bPSDIQSwitch 0x40
+#define bPSDRxTrigger 0x400000
+#define bPSDTxTrigger 0x80000000
+#define bPSDSineToneScale 0x7f000000
+#define bPSDReport 0xffff
+
+/* 3. Page9(0x900) */
+#define bOFDMTxSC 0x30000000 /* Useless */
+#define bCCKTxOn 0x1
+#define bOFDMTxOn 0x2
+#define bDebugPage 0xfff /* reset debug page and HWord, LWord */
+#define bDebugItem 0xff /* reset debug page and LWord */
+#define bAntL 0x10
+#define bAntNonHT 0x100
+#define bAntHT1 0x1000
+#define bAntHT2 0x10000
+#define bAntHT1S1 0x100000
+#define bAntNonHTS1 0x1000000
+
+/* 4. PageA(0xA00) */
+#define bCCKBBMode 0x3 /* Useless */
+#define bCCKTxPowerSaving 0x80
+#define bCCKRxPowerSaving 0x40
+
+#define bCCKSideBand 0x10 /* Reg 0xa00 rCCK0_System 20/40 */
+
+#define bCCKScramble 0x8 /* Useless */
+#define bCCKAntDiversity 0x8000
+#define bCCKCarrierRecovery 0x4000
+#define bCCKTxRate 0x3000
+#define bCCKDCCancel 0x0800
+#define bCCKISICancel 0x0400
+#define bCCKMatchFilter 0x0200
+#define bCCKEqualizer 0x0100
+#define bCCKPreambleDetect 0x800000
+#define bCCKFastFalseCCA 0x400000
+#define bCCKChEstStart 0x300000
+#define bCCKCCACount 0x080000
+#define bCCKcs_lim 0x070000
+#define bCCKBistMode 0x80000000
+#define bCCKCCAMask 0x40000000
+#define bCCKTxDACPhase 0x4
+#define bCCKRxADCPhase 0x20000000 /* r_rx_clk */
+#define bCCKr_cp_mode0 0x0100
+#define bCCKTxDCOffset 0xf0
+#define bCCKRxDCOffset 0xf
+#define bCCKCCAMode 0xc000
+#define bCCKFalseCS_lim 0x3f00
+#define bCCKCS_ratio 0xc00000
+#define bCCKCorgBit_sel 0x300000
+#define bCCKPD_lim 0x0f0000
+#define bCCKNewCCA 0x80000000
+#define bCCKRxHPofIG 0x8000
+#define bCCKRxIG 0x7f00
+#define bCCKLNAPolarity 0x800000
+#define bCCKRx1stGain 0x7f0000
+#define bCCKRFExtend 0x20000000 /* CCK Rx Iinital gain polarity */
+#define bCCKRxAGCSatLevel 0x1f000000
+#define bCCKRxAGCSatCount 0xe0
+#define bCCKRxRFSettle 0x1f /* AGCsamp_dly */
+#define bCCKFixedRxAGC 0x8000
+#define bCCKAntennaPolarity 0x2000
+#define bCCKTxFilterType 0x0c00
+#define bCCKRxAGCReportType 0x0300
+#define bCCKRxDAGCEn 0x80000000
+#define bCCKRxDAGCPeriod 0x20000000
+#define bCCKRxDAGCSatLevel 0x1f000000
+#define bCCKTimingRecovery 0x800000
+#define bCCKTxC0 0x3f0000
+#define bCCKTxC1 0x3f000000
+#define bCCKTxC2 0x3f
+#define bCCKTxC3 0x3f00
+#define bCCKTxC4 0x3f0000
+#define bCCKTxC5 0x3f000000
+#define bCCKTxC6 0x3f
+#define bCCKTxC7 0x3f00
+#define bCCKDebugPort 0xff0000
+#define bCCKDACDebug 0x0f000000
+#define bCCKFalseAlarmEnable 0x8000
+#define bCCKFalseAlarmRead 0x4000
+#define bCCKTRSSI 0x7f
+#define bCCKRxAGCReport 0xfe
+#define bCCKRxReport_AntSel 0x80000000
+#define bCCKRxReport_MFOff 0x40000000
+#define bCCKRxRxReport_SQLoss 0x20000000
+#define bCCKRxReport_Pktloss 0x10000000
+#define bCCKRxReport_Lockedbit 0x08000000
+#define bCCKRxReport_RateError 0x04000000
+#define bCCKRxReport_RxRate 0x03000000
+#define bCCKRxFACounterLower 0xff
+#define bCCKRxFACounterUpper 0xff000000
+#define bCCKRxHPAGCStart 0xe000
+#define bCCKRxHPAGCFinal 0x1c00
+#define bCCKRxFalseAlarmEnable 0x8000
+#define bCCKFACounterFreeze 0x4000
+#define bCCKTxPathSel 0x10000000
+#define bCCKDefaultRxPath 0xc000000
+#define bCCKOptionRxPath 0x3000000
+
+/* 5. PageC(0xC00) */
+#define bNumOfSTF 0x3 /* Useless */
+#define bShift_L 0xc0
+#define bGI_TH 0xc
+#define bRxPathA 0x1
+#define bRxPathB 0x2
+#define bRxPathC 0x4
+#define bRxPathD 0x8
+#define bTxPathA 0x1
+#define bTxPathB 0x2
+#define bTxPathC 0x4
+#define bTxPathD 0x8
+#define bTRSSIFreq 0x200
+#define bADCBackoff 0x3000
+#define bDFIRBackoff 0xc000
+#define bTRSSILatchPhase 0x10000
+#define bRxIDCOffset 0xff
+#define bRxQDCOffset 0xff00
+#define bRxDFIRMode 0x1800000
+#define bRxDCNFType 0xe000000
+#define bRXIQImb_A 0x3ff
+#define bRXIQImb_B 0xfc00
+#define bRXIQImb_C 0x3f0000
+#define bRXIQImb_D 0xffc00000
+#define bDC_dc_Notch 0x60000
+#define bRxNBINotch 0x1f000000
+#define bPD_TH 0xf
+#define bPD_TH_Opt2 0xc000
+#define bPWED_TH 0x700
+#define bIfMF_Win_L 0x800
+#define bPD_Option 0x1000
+#define bMF_Win_L 0xe000
+#define bBW_Search_L 0x30000
+#define bwin_enh_L 0xc0000
+#define bBW_TH 0x700000
+#define bED_TH2 0x3800000
+#define bBW_option 0x4000000
+#define bRatio_TH 0x18000000
+#define bWindow_L 0xe0000000
+#define bSBD_Option 0x1
+#define bFrame_TH 0x1c
+#define bFS_Option 0x60
+#define bDC_Slope_check 0x80
+#define bFGuard_Counter_DC_L 0xe00
+#define bFrame_Weight_Short 0x7000
+#define bSub_Tune 0xe00000
+#define bFrame_DC_Length 0xe000000
+#define bSBD_start_offset 0x30000000
+#define bFrame_TH_2 0x7
+#define bFrame_GI2_TH 0x38
+#define bGI2_Sync_en 0x40
+#define bSarch_Short_Early 0x300
+#define bSarch_Short_Late 0xc00
+#define bSarch_GI2_Late 0x70000
+#define bCFOAntSum 0x1
+#define bCFOAcc 0x2
+#define bCFOStartOffset 0xc
+#define bCFOLookBack 0x70
+#define bCFOSumWeight 0x80
+#define bDAGCEnable 0x10000
+#define bTXIQImb_A 0x3ff
+#define bTXIQImb_B 0xfc00
+#define bTXIQImb_C 0x3f0000
+#define bTXIQImb_D 0xffc00000
+#define bTxIDCOffset 0xff
+#define bTxQDCOffset 0xff00
+#define bTxDFIRMode 0x10000
+#define bTxPesudoNoiseOn 0x4000000
+#define bTxPesudoNoise_A 0xff
+#define bTxPesudoNoise_B 0xff00
+#define bTxPesudoNoise_C 0xff0000
+#define bTxPesudoNoise_D 0xff000000
+#define bCCADropOption 0x20000
+#define bCCADropThres 0xfff00000
+#define bEDCCA_H 0xf
+#define bEDCCA_L 0xf0
+#define bLambda_ED 0x300
+#define bRxInitialGain 0x7f
+#define bRxAntDivEn 0x80
+#define bRxAGCAddressForLNA 0x7f00
+#define bRxHighPowerFlow 0x8000
+#define bRxAGCFreezeThres 0xc0000
+#define bRxFreezeStep_AGC1 0x300000
+#define bRxFreezeStep_AGC2 0xc00000
+#define bRxFreezeStep_AGC3 0x3000000
+#define bRxFreezeStep_AGC0 0xc000000
+#define bRxRssi_Cmp_En 0x10000000
+#define bRxQuickAGCEn 0x20000000
+#define bRxAGCFreezeThresMode 0x40000000
+#define bRxOverFlowCheckType 0x80000000
+#define bRxAGCShift 0x7f
+#define bTRSW_Tri_Only 0x80
+#define bPowerThres 0x300
+#define bRxAGCEn 0x1
+#define bRxAGCTogetherEn 0x2
+#define bRxAGCMin 0x4
+#define bRxHP_Ini 0x7
+#define bRxHP_TRLNA 0x70
+#define bRxHP_RSSI 0x700
+#define bRxHP_BBP1 0x7000
+#define bRxHP_BBP2 0x70000
+#define bRxHP_BBP3 0x700000
+#define bRSSI_H 0x7f0000 /* threshold for high power */
+#define bRSSI_Gen 0x7f000000 /* threshold for ant diversity */
+#define bRxSettle_TRSW 0x7
+#define bRxSettle_LNA 0x38
+#define bRxSettle_RSSI 0x1c0
+#define bRxSettle_BBP 0xe00
+#define bRxSettle_RxHP 0x7000
+#define bRxSettle_AntSW_RSSI 0x38000
+#define bRxSettle_AntSW 0xc0000
+#define bRxProcessTime_DAGC 0x300000
+#define bRxSettle_HSSI 0x400000
+#define bRxProcessTime_BBPPW 0x800000
+#define bRxAntennaPowerShift 0x3000000
+#define bRSSITableSelect 0xc000000
+#define bRxHP_Final 0x7000000
+#define bRxHTSettle_BBP 0x7
+#define bRxHTSettle_HSSI 0x8
+#define bRxHTSettle_RxHP 0x70
+#define bRxHTSettle_BBPPW 0x80
+#define bRxHTSettle_Idle 0x300
+#define bRxHTSettle_Reserved 0x1c00
+#define bRxHTRxHPEn 0x8000
+#define bRxHTAGCFreezeThres 0x30000
+#define bRxHTAGCTogetherEn 0x40000
+#define bRxHTAGCMin 0x80000
+#define bRxHTAGCEn 0x100000
+#define bRxHTDAGCEn 0x200000
+#define bRxHTRxHP_BBP 0x1c00000
+#define bRxHTRxHP_Final 0xe0000000
+#define bRxPWRatioTH 0x3
+#define bRxPWRatioEn 0x4
+#define bRxMFHold 0x3800
+#define bRxPD_Delay_TH1 0x38
+#define bRxPD_Delay_TH2 0x1c0
+#define bRxPD_DC_COUNT_MAX 0x600
+#define bRxPD_Delay_TH 0x8000
+#define bRxProcess_Delay 0xf0000
+#define bRxSearchrange_GI2_Early 0x700000
+#define bRxFrame_Guard_Counter_L 0x3800000
+#define bRxSGI_Guard_L 0xc000000
+#define bRxSGI_Search_L 0x30000000
+#define bRxSGI_TH 0xc0000000
+#define bDFSCnt0 0xff
+#define bDFSCnt1 0xff00
+#define bDFSFlag 0xf0000
+#define bMFWeightSum 0x300000
+#define bMinIdxTH 0x7f000000
+#define bDAFormat 0x40000
+#define bTxChEmuEnable 0x01000000
+#define bTRSWIsolation_A 0x7f
+#define bTRSWIsolation_B 0x7f00
+#define bTRSWIsolation_C 0x7f0000
+#define bTRSWIsolation_D 0x7f000000
+#define bExtLNAGain 0x7c00
+
+/* 6. PageE(0xE00) */
+#define bSTBCEn 0x4 /* Useless */
+#define bAntennaMapping 0x10
+#define bNss 0x20
+#define bCFOAntSumD 0x200
+#define bPHYCounterReset 0x8000000
+#define bCFOReportGet 0x4000000
+#define bOFDMContinueTx 0x10000000
+#define bOFDMSingleCarrier 0x20000000
+#define bOFDMSingleTone 0x40000000
+#define bHTDetect 0x100
+#define bCFOEn 0x10000
+#define bCFOValue 0xfff00000
+#define bSigTone_Re 0x3f
+#define bSigTone_Im 0x7f00
+#define bCounter_CCA 0xffff
+#define bCounter_ParityFail 0xffff0000
+#define bCounter_RateIllegal 0xffff
+#define bCounter_CRC8Fail 0xffff0000
+#define bCounter_MCSNoSupport 0xffff
+#define bCounter_FastSync 0xffff
+#define bShortCFO 0xfff
+#define bShortCFOTLength 12 /* total */
+#define bShortCFOFLength 11 /* fraction */
+#define bLongCFO 0x7ff
+#define bLongCFOTLength 11
+#define bLongCFOFLength 11
+#define bTailCFO 0x1fff
+#define bTailCFOTLength 13
+#define bTailCFOFLength 12
+#define bmax_en_pwdB 0xffff
+#define bCC_power_dB 0xffff0000
+#define bnoise_pwdB 0xffff
+#define bPowerMeasTLength 10
+#define bPowerMeasFLength 3
+#define bRx_HT_BW 0x1
+#define bRxSC 0x6
+#define bRx_HT 0x8
+#define bNB_intf_det_on 0x1
+#define bIntf_win_len_cfg 0x30
+#define bNB_Intf_TH_cfg 0x1c0
+#define bRFGain 0x3f
+#define bTableSel 0x40
+#define bTRSW 0x80
+#define bRxSNR_A 0xff
+#define bRxSNR_B 0xff00
+#define bRxSNR_C 0xff0000
+#define bRxSNR_D 0xff000000
+#define bSNREVMTLength 8
+#define bSNREVMFLength 1
+#define bCSI1st 0xff
+#define bCSI2nd 0xff00
+#define bRxEVM1st 0xff0000
+#define bRxEVM2nd 0xff000000
+#define bSIGEVM 0xff
+#define bPWDB 0xff00
+#define bSGIEN 0x10000
+
+#define bSFactorQAM1 0xf /* Useless */
+#define bSFactorQAM2 0xf0
+#define bSFactorQAM3 0xf00
+#define bSFactorQAM4 0xf000
+#define bSFactorQAM5 0xf0000
+#define bSFactorQAM6 0xf0000
+#define bSFactorQAM7 0xf00000
+#define bSFactorQAM8 0xf000000
+#define bSFactorQAM9 0xf0000000
+#define bCSIScheme 0x100000
+
+#define bNoiseLvlTopSet 0x3 /* Useless */
+#define bChSmooth 0x4
+#define bChSmoothCfg1 0x38
+#define bChSmoothCfg2 0x1c0
+#define bChSmoothCfg3 0xe00
+#define bChSmoothCfg4 0x7000
+#define bMRCMode 0x800000
+#define bTHEVMCfg 0x7000000
+
+#define bLoopFitType 0x1 /* Useless */
+#define bUpdCFO 0x40
+#define bUpdCFOOffData 0x80
+#define bAdvUpdCFO 0x100
+#define bAdvTimeCtrl 0x800
+#define bUpdClko 0x1000
+#define bFC 0x6000
+#define bTrackingMode 0x8000
+#define bPhCmpEnable 0x10000
+#define bUpdClkoLTF 0x20000
+#define bComChCFO 0x40000
+#define bCSIEstiMode 0x80000
+#define bAdvUpdEqz 0x100000
+#define bUChCfg 0x7000000
+#define bUpdEqz 0x8000000
+
+/* Rx Pseduo noise */
+#define bRxPesudoNoiseOn 0x20000000 /* Useless */
+#define bRxPesudoNoise_A 0xff
+#define bRxPesudoNoise_B 0xff00
+#define bRxPesudoNoise_C 0xff0000
+#define bRxPesudoNoise_D 0xff000000
+#define bPesudoNoiseState_A 0xffff
+#define bPesudoNoiseState_B 0xffff0000
+#define bPesudoNoiseState_C 0xffff
+#define bPesudoNoiseState_D 0xffff0000
+
+/* 7. RF Register */
+/* Zebra1 */
+#define bZebra1_HSSIEnable 0x8 /* Useless */
+#define bZebra1_TRxControl 0xc00
+#define bZebra1_TRxGainSetting 0x07f
+#define bZebra1_RxCorner 0xc00
+#define bZebra1_TxChargePump 0x38
+#define bZebra1_RxChargePump 0x7
+#define bZebra1_ChannelNum 0xf80
+#define bZebra1_TxLPFBW 0x400
+#define bZebra1_RxLPFBW 0x600
+
+/* Zebra4 */
+#define bRTL8256RegModeCtrl1 0x100 /* Useless */
+#define bRTL8256RegModeCtrl0 0x40
+#define bRTL8256_TxLPFBW 0x18
+#define bRTL8256_RxLPFBW 0x600
+
+/* RTL8258 */
+#define bRTL8258_TxLPFBW 0xc /* Useless */
+#define bRTL8258_RxLPFBW 0xc00
+#define bRTL8258_RSSILPFBW 0xc0
+
+
+/* */
+/* Other Definition */
+/* */
+
+/* byte endable for sb_write */
+#define bByte0 0x1 /* Useless */
+#define bByte1 0x2
+#define bByte2 0x4
+#define bByte3 0x8
+#define bWord0 0x3
+#define bWord1 0xc
+#define bDWord 0xf
+
+/* for PutRegsetting & GetRegSetting BitMask */
+#define bMaskByte0 0xff /* Reg 0xc50 rOFDM0_XAAGCCore~0xC6f */
+#define bMaskByte1 0xff00
+#define bMaskByte2 0xff0000
+#define bMaskByte3 0xff000000
+#define bMaskHWord 0xffff0000
+#define bMaskLWord 0x0000ffff
+#define bMaskDWord 0xffffffff
+#define bMask12Bits 0xfff
+#define bMaskH4Bits 0xf0000000
+#define bMaskOFDM_D 0xffc00000
+#define bMaskCCK 0x3f3f3f3f
+
+/* for PutRFRegsetting & GetRFRegSetting BitMask */
+#define bRFRegOffsetMask 0xfffff
+
+#define bEnable 0x1 /* Useless */
+#define bDisable 0x0
+
+#define LeftAntenna 0x0 /* Useless */
+#define RightAntenna 0x1
+
+#define tCheckTxStatus 500 /* 500ms Useless */
+#define tUpdateRxCounter 100 /* 100ms */
+
+#define rateCCK 0 /* Useless */
+#define rateOFDM 1
+#define rateHT 2
+
+/* define Register-End */
+#define bPMAC_End 0x1ff /* Useless */
+#define bFPGAPHY0_End 0x8ff
+#define bFPGAPHY1_End 0x9ff
+#define bCCKPHY0_End 0xaff
+#define bOFDMPHY0_End 0xcff
+#define bOFDMPHY1_End 0xdff
+
+#define bPMACControl 0x0 /* Useless */
+#define bWMACControl 0x1
+#define bWNICControl 0x2
+
+#define PathA 0x0 /* Useless */
+#define PathB 0x1
+#define PathC 0x2
+#define PathD 0x3
+
+/*--------------------------Define Parameters-------------------------------*/
+
+
+#endif
diff --git a/drivers/staging/rtl8188eu/include/Hal8188ERateAdaptive.h b/drivers/staging/rtl8188eu/include/Hal8188ERateAdaptive.h
new file mode 100644
index 000000000..21996a117
--- /dev/null
+++ b/drivers/staging/rtl8188eu/include/Hal8188ERateAdaptive.h
@@ -0,0 +1,75 @@
+#ifndef __INC_RA_H
+#define __INC_RA_H
+/*++
+Copyright (c) Realtek Semiconductor Corp. All rights reserved.
+
+Module Name:
+ RateAdaptive.h
+
+Abstract:
+ Prototype of RA and related data structure.
+
+Major Change History:
+ When Who What
+ ---------- --------------- -------------------------------
+ 2011-08-12 Page Create.
+--*/
+
+/* Rate adaptive define */
+#define PERENTRY 23
+#define RETRYSIZE 5
+#define RATESIZE 28
+#define TX_RPT2_ITEM_SIZE 8
+
+/* */
+/* TX report 2 format in Rx desc */
+/* */
+#define GET_TX_RPT2_DESC_PKT_LEN_88E(__pRxStatusDesc) \
+ LE_BITS_TO_4BYTE(__pRxStatusDesc, 0, 9)
+#define GET_TX_RPT2_DESC_MACID_VALID_1_88E(__pRxStatusDesc) \
+ LE_BITS_TO_4BYTE(__pRxStatusDesc+16, 0, 32)
+#define GET_TX_RPT2_DESC_MACID_VALID_2_88E(__pRxStatusDesc) \
+ LE_BITS_TO_4BYTE(__pRxStatusDesc+20, 0, 32)
+
+#define GET_TX_REPORT_TYPE1_RERTY_0(__pAddr) \
+ LE_BITS_TO_4BYTE(__pAddr, 0, 16)
+#define GET_TX_REPORT_TYPE1_RERTY_1(__pAddr) \
+ LE_BITS_TO_1BYTE(__pAddr+2, 0, 8)
+#define GET_TX_REPORT_TYPE1_RERTY_2(__pAddr) \
+ LE_BITS_TO_1BYTE(__pAddr+3, 0, 8)
+#define GET_TX_REPORT_TYPE1_RERTY_3(__pAddr) \
+ LE_BITS_TO_1BYTE(__pAddr+4, 0, 8)
+#define GET_TX_REPORT_TYPE1_RERTY_4(__pAddr) \
+ LE_BITS_TO_1BYTE(__pAddr+4+1, 0, 8)
+#define GET_TX_REPORT_TYPE1_DROP_0(__pAddr) \
+ LE_BITS_TO_1BYTE(__pAddr+4+2, 0, 8)
+#define GET_TX_REPORT_TYPE1_DROP_1(__pAddr) \
+ LE_BITS_TO_1BYTE(__pAddr+4+3, 0, 8)
+
+/* End rate adaptive define */
+
+void ODM_RASupport_Init(struct odm_dm_struct *dm_odm);
+
+int ODM_RAInfo_Init_all(struct odm_dm_struct *dm_odm);
+
+int ODM_RAInfo_Init(struct odm_dm_struct *dm_odm, u8 MacID);
+
+u8 ODM_RA_GetShortGI_8188E(struct odm_dm_struct *dm_odm, u8 MacID);
+
+u8 ODM_RA_GetDecisionRate_8188E(struct odm_dm_struct *dm_odm, u8 MacID);
+
+u8 ODM_RA_GetHwPwrStatus_8188E(struct odm_dm_struct *dm_odm, u8 MacID);
+void ODM_RA_UpdateRateInfo_8188E(struct odm_dm_struct *dm_odm, u8 MacID,
+ u8 RateID, u32 RateMask,
+ u8 SGIEnable);
+
+void ODM_RA_SetRSSI_8188E(struct odm_dm_struct *dm_odm, u8 macid,
+ u8 rssi);
+
+void ODM_RA_TxRPT2Handle_8188E(struct odm_dm_struct *dm_odm,
+ u8 *txrpt_buf, u16 txrpt_len,
+ u32 validentry0, u32 validentry1);
+
+void ODM_RA_Set_TxRPT_Time(struct odm_dm_struct *dm_odm, u16 minRptTime);
+
+#endif
diff --git a/drivers/staging/rtl8188eu/include/HalHWImg8188E_FW.h b/drivers/staging/rtl8188eu/include/HalHWImg8188E_FW.h
new file mode 100644
index 000000000..1bf9bc70a
--- /dev/null
+++ b/drivers/staging/rtl8188eu/include/HalHWImg8188E_FW.h
@@ -0,0 +1,34 @@
+/******************************************************************************
+*
+* Copyright(c) 2007 - 2011 Realtek Corporation. All rights reserved.
+*
+* This program is free software; you can redistribute it and/or modify it
+* under the terms of version 2 of the GNU General Public License as
+* published by the Free Software Foundation.
+*
+* This program is distributed in the hope that it will be useful, but WITHOUT
+* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+* more details.
+*
+* You should have received a copy of the GNU General Public License along with
+* this program; if not, write to the Free Software Foundation, Inc.,
+* 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+*
+*
+******************************************************************************/
+
+#ifndef __INC_FW_8188E_HW_IMG_H
+#define __INC_FW_8188E_HW_IMG_H
+
+
+/******************************************************************************
+* FW_AP.TXT
+******************************************************************************/
+/******************************************************************************
+* FW_WoWLAN.TXT
+******************************************************************************/
+#define ArrayLength_8188E_FW_WoWLAN 15764
+extern const u8 Array_8188E_FW_WoWLAN[ArrayLength_8188E_FW_WoWLAN];
+
+#endif
diff --git a/drivers/staging/rtl8188eu/include/HalVerDef.h b/drivers/staging/rtl8188eu/include/HalVerDef.h
new file mode 100644
index 000000000..97047cf06
--- /dev/null
+++ b/drivers/staging/rtl8188eu/include/HalVerDef.h
@@ -0,0 +1,167 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2011 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ *
+ ******************************************************************************/
+#ifndef __HAL_VERSION_DEF_H__
+#define __HAL_VERSION_DEF_H__
+
+enum HAL_IC_TYPE {
+ CHIP_8192S = 0,
+ CHIP_8188C = 1,
+ CHIP_8192C = 2,
+ CHIP_8192D = 3,
+ CHIP_8723A = 4,
+ CHIP_8188E = 5,
+ CHIP_8881A = 6,
+ CHIP_8812A = 7,
+ CHIP_8821A = 8,
+ CHIP_8723B = 9,
+ CHIP_8192E = 10,
+};
+
+enum HAL_CHIP_TYPE {
+ TEST_CHIP = 0,
+ NORMAL_CHIP = 1,
+ FPGA = 2,
+};
+
+enum HAL_CUT_VERSION {
+ A_CUT_VERSION = 0,
+ B_CUT_VERSION = 1,
+ C_CUT_VERSION = 2,
+ D_CUT_VERSION = 3,
+ E_CUT_VERSION = 4,
+ F_CUT_VERSION = 5,
+ G_CUT_VERSION = 6,
+};
+
+enum HAL_VENDOR {
+ CHIP_VENDOR_TSMC = 0,
+ CHIP_VENDOR_UMC = 1,
+};
+
+enum HAL_RF_TYPE {
+ RF_TYPE_1T1R = 0,
+ RF_TYPE_1T2R = 1,
+ RF_TYPE_2T2R = 2,
+ RF_TYPE_2T3R = 3,
+ RF_TYPE_2T4R = 4,
+ RF_TYPE_3T3R = 5,
+ RF_TYPE_3T4R = 6,
+ RF_TYPE_4T4R = 7,
+};
+
+struct HAL_VERSION {
+ enum HAL_IC_TYPE ICType;
+ enum HAL_CHIP_TYPE ChipType;
+ enum HAL_CUT_VERSION CUTVersion;
+ enum HAL_VENDOR VendorType;
+ enum HAL_RF_TYPE RFType;
+ u8 ROMVer;
+};
+
+/* Get element */
+#define GET_CVID_IC_TYPE(version) (((version).ICType))
+#define GET_CVID_CHIP_TYPE(version) (((version).ChipType))
+#define GET_CVID_RF_TYPE(version) (((version).RFType))
+#define GET_CVID_MANUFACTUER(version) (((version).VendorType))
+#define GET_CVID_CUT_VERSION(version) (((version).CUTVersion))
+#define GET_CVID_ROM_VERSION(version) (((version).ROMVer) & ROM_VERSION_MASK)
+
+/* Common Macro. -- */
+/* HAL_VERSION VersionID */
+
+/* HAL_IC_TYPE_E */
+#define IS_81XXC(version) \
+ (((GET_CVID_IC_TYPE(version) == CHIP_8192C) || \
+ (GET_CVID_IC_TYPE(version) == CHIP_8188C)) ? true : false)
+#define IS_8723_SERIES(version) \
+ ((GET_CVID_IC_TYPE(version) == CHIP_8723A) ? true : false)
+#define IS_92D(version) \
+ ((GET_CVID_IC_TYPE(version) == CHIP_8192D) ? true : false)
+#define IS_8188E(version) \
+ ((GET_CVID_IC_TYPE(version) == CHIP_8188E) ? true : false)
+
+/* HAL_CHIP_TYPE_E */
+#define IS_TEST_CHIP(version) \
+ ((GET_CVID_CHIP_TYPE(version) == TEST_CHIP) ? true : false)
+#define IS_NORMAL_CHIP(version) \
+ ((GET_CVID_CHIP_TYPE(version) == NORMAL_CHIP) ? true : false)
+
+/* HAL_CUT_VERSION_E */
+#define IS_A_CUT(version) \
+ ((GET_CVID_CUT_VERSION(version) == A_CUT_VERSION) ? true : false)
+#define IS_B_CUT(version) \
+ ((GET_CVID_CUT_VERSION(version) == B_CUT_VERSION) ? true : false)
+#define IS_C_CUT(version) \
+ ((GET_CVID_CUT_VERSION(version) == C_CUT_VERSION) ? true : false)
+#define IS_D_CUT(version) \
+ ((GET_CVID_CUT_VERSION(version) == D_CUT_VERSION) ? true : false)
+#define IS_E_CUT(version) \
+ ((GET_CVID_CUT_VERSION(version) == E_CUT_VERSION) ? true : false)
+
+
+/* HAL_VENDOR_E */
+#define IS_CHIP_VENDOR_TSMC(version) \
+ ((GET_CVID_MANUFACTUER(version) == CHIP_VENDOR_TSMC) ? true : false)
+#define IS_CHIP_VENDOR_UMC(version) \
+ ((GET_CVID_MANUFACTUER(version) == CHIP_VENDOR_UMC) ? true : false)
+
+/* HAL_RF_TYPE_E */
+#define IS_1T1R(version) \
+ ((GET_CVID_RF_TYPE(version) == RF_TYPE_1T1R) ? true : false)
+#define IS_1T2R(version) \
+ ((GET_CVID_RF_TYPE(version) == RF_TYPE_1T2R) ? true : false)
+#define IS_2T2R(version) \
+ ((GET_CVID_RF_TYPE(version) == RF_TYPE_2T2R) ? true : false)
+
+/* Chip version Macro. -- */
+#define IS_81XXC_TEST_CHIP(version) \
+ ((IS_81XXC(version) && (!IS_NORMAL_CHIP(version))) ? true : false)
+
+#define IS_92C_SERIAL(version) \
+ ((IS_81XXC(version) && IS_2T2R(version)) ? true : false)
+#define IS_81xxC_VENDOR_UMC_A_CUT(version) \
+ (IS_81XXC(version) ? (IS_CHIP_VENDOR_UMC(version) ? \
+ (IS_A_CUT(version) ? true : false) : false) : false)
+#define IS_81xxC_VENDOR_UMC_B_CUT(version) \
+ (IS_81XXC(version) ? (IS_CHIP_VENDOR_UMC(version) ? \
+ (IS_B_CUT(version) ? true : false) : false) : false)
+#define IS_81xxC_VENDOR_UMC_C_CUT(version) \
+ (IS_81XXC(version) ? (IS_CHIP_VENDOR_UMC(version) ? \
+ (IS_C_CUT(version) ? true : false) : false) : false)
+
+#define IS_NORMAL_CHIP92D(version) \
+ ((IS_92D(version)) ? \
+ ((GET_CVID_CHIP_TYPE(version) == NORMAL_CHIP) ? true : false) : false)
+
+#define IS_92D_SINGLEPHY(version) \
+ ((IS_92D(version)) ? (IS_2T2R(version) ? true : false) : false)
+#define IS_92D_C_CUT(version) \
+ ((IS_92D(version)) ? (IS_C_CUT(version) ? true : false) : false)
+#define IS_92D_D_CUT(version) \
+ ((IS_92D(version)) ? (IS_D_CUT(version) ? true : false) : false)
+#define IS_92D_E_CUT(version) \
+ ((IS_92D(version)) ? (IS_E_CUT(version) ? true : false) : false)
+
+#define IS_8723A_A_CUT(version) \
+ ((IS_8723_SERIES(version)) ? (IS_A_CUT(version) ? true : false) : false)
+#define IS_8723A_B_CUT(version) \
+ ((IS_8723_SERIES(version)) ? (IS_B_CUT(version) ? true : false) : false)
+
+#endif
diff --git a/drivers/staging/rtl8188eu/include/basic_types.h b/drivers/staging/rtl8188eu/include/basic_types.h
new file mode 100644
index 000000000..8a7ca9926
--- /dev/null
+++ b/drivers/staging/rtl8188eu/include/basic_types.h
@@ -0,0 +1,184 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2011 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ *
+ ******************************************************************************/
+#ifndef __BASIC_TYPES_H__
+#define __BASIC_TYPES_H__
+
+#define SUCCESS 0
+#define FAIL (-1)
+
+#include <linux/types.h>
+#define NDIS_OID uint
+
+typedef void (*proc_t)(void *);
+
+#define FIELD_OFFSET(s, field) ((ssize_t)&((s *)(0))->field)
+
+#define MEM_ALIGNMENT_OFFSET (sizeof(size_t))
+#define MEM_ALIGNMENT_PADDING (sizeof(size_t) - 1)
+
+/* port from fw */
+/* TODO: Macros Below are Sync from SD7-Driver. It is necessary
+ * to check correctness */
+
+/*
+ * Call endian free function when
+ * 1. Read/write packet content.
+ * 2. Before write integer to IO.
+ * 3. After read integer from IO.
+*/
+
+/* Convert little data endian to host ordering */
+#define EF1BYTE(_val) \
+ ((u8)(_val))
+#define EF2BYTE(_val) \
+ (le16_to_cpu(_val))
+#define EF4BYTE(_val) \
+ (le32_to_cpu(_val))
+
+/* Read data from memory */
+#define READEF1BYTE(_ptr) \
+ EF1BYTE(*((u8 *)(_ptr)))
+/* Read le16 data from memory and convert to host ordering */
+#define READEF2BYTE(_ptr) \
+ EF2BYTE(*(_ptr))
+#define READEF4BYTE(_ptr) \
+ EF4BYTE(*(_ptr))
+
+/* Write data to memory */
+#define WRITEEF1BYTE(_ptr, _val) \
+ do { \
+ (*((u8 *)(_ptr))) = EF1BYTE(_val) \
+ } while (0)
+/* Write le data to memory in host ordering */
+#define WRITEEF2BYTE(_ptr, _val) \
+ do { \
+ (*((u16 *)(_ptr))) = EF2BYTE(_val) \
+ } while (0)
+
+#define WRITEEF4BYTE(_ptr, _val) \
+ do { \
+ (*((u32 *)(_ptr))) = EF2BYTE(_val) \
+ } while (0)
+
+/* Create a bit mask
+ * Examples:
+ * BIT_LEN_MASK_32(0) => 0x00000000
+ * BIT_LEN_MASK_32(1) => 0x00000001
+ * BIT_LEN_MASK_32(2) => 0x00000003
+ * BIT_LEN_MASK_32(32) => 0xFFFFFFFF
+ */
+#define BIT_LEN_MASK_32(__bitlen) \
+ (0xFFFFFFFF >> (32 - (__bitlen)))
+#define BIT_LEN_MASK_16(__bitlen) \
+ (0xFFFF >> (16 - (__bitlen)))
+#define BIT_LEN_MASK_8(__bitlen) \
+ (0xFF >> (8 - (__bitlen)))
+
+/* Create an offset bit mask
+ * Examples:
+ * BIT_OFFSET_LEN_MASK_32(0, 2) => 0x00000003
+ * BIT_OFFSET_LEN_MASK_32(16, 2) => 0x00030000
+ */
+#define BIT_OFFSET_LEN_MASK_32(__bitoffset, __bitlen) \
+ (BIT_LEN_MASK_32(__bitlen) << (__bitoffset))
+#define BIT_OFFSET_LEN_MASK_16(__bitoffset, __bitlen) \
+ (BIT_LEN_MASK_16(__bitlen) << (__bitoffset))
+#define BIT_OFFSET_LEN_MASK_8(__bitoffset, __bitlen) \
+ (BIT_LEN_MASK_8(__bitlen) << (__bitoffset))
+
+/*Description:
+ * Return 4-byte value in host byte ordering from
+ * 4-byte pointer in little-endian system.
+ */
+#define LE_P4BYTE_TO_HOST_4BYTE(__pstart) \
+ (EF4BYTE(*((__le32 *)(__pstart))))
+#define LE_P2BYTE_TO_HOST_2BYTE(__pstart) \
+ (EF2BYTE(*((__le16 *)(__pstart))))
+#define LE_P1BYTE_TO_HOST_1BYTE(__pstart) \
+ (EF1BYTE(*((u8 *)(__pstart))))
+
+/*Description:
+Translate subfield (continuous bits in little-endian) of 4-byte
+value to host byte ordering.*/
+#define LE_BITS_TO_4BYTE(__pstart, __bitoffset, __bitlen) \
+ ( \
+ (LE_P4BYTE_TO_HOST_4BYTE(__pstart) >> (__bitoffset)) & \
+ BIT_LEN_MASK_32(__bitlen) \
+ )
+#define LE_BITS_TO_2BYTE(__pstart, __bitoffset, __bitlen) \
+ ( \
+ (LE_P2BYTE_TO_HOST_2BYTE(__pstart) >> (__bitoffset)) & \
+ BIT_LEN_MASK_16(__bitlen) \
+ )
+#define LE_BITS_TO_1BYTE(__pstart, __bitoffset, __bitlen) \
+ ( \
+ (LE_P1BYTE_TO_HOST_1BYTE(__pstart) >> (__bitoffset)) & \
+ BIT_LEN_MASK_8(__bitlen) \
+ )
+
+/* Description:
+ * Mask subfield (continuous bits in little-endian) of 4-byte value
+ * and return the result in 4-byte value in host byte ordering.
+ */
+#define LE_BITS_CLEARED_TO_4BYTE(__pstart, __bitoffset, __bitlen) \
+ ( \
+ LE_P4BYTE_TO_HOST_4BYTE(__pstart) & \
+ (~BIT_OFFSET_LEN_MASK_32(__bitoffset, __bitlen)) \
+ )
+#define LE_BITS_CLEARED_TO_2BYTE(__pstart, __bitoffset, __bitlen) \
+ ( \
+ LE_P2BYTE_TO_HOST_2BYTE(__pstart) & \
+ (~BIT_OFFSET_LEN_MASK_16(__bitoffset, __bitlen)) \
+ )
+#define LE_BITS_CLEARED_TO_1BYTE(__pstart, __bitoffset, __bitlen) \
+ ( \
+ LE_P1BYTE_TO_HOST_1BYTE(__pstart) & \
+ (~BIT_OFFSET_LEN_MASK_8(__bitoffset, __bitlen)) \
+ )
+
+/* Description:
+ * Set subfield of little-endian 4-byte value to specified value.
+ */
+#define SET_BITS_TO_LE_4BYTE(__pstart, __bitoffset, __bitlen, __val) \
+ *((u32 *)(__pstart)) = \
+ ( \
+ LE_BITS_CLEARED_TO_4BYTE(__pstart, __bitoffset, __bitlen) | \
+ ((((u32)__val) & BIT_LEN_MASK_32(__bitlen)) << (__bitoffset)) \
+ )
+
+#define SET_BITS_TO_LE_2BYTE(__pstart, __bitoffset, __bitlen, __val) \
+ *((u16 *)(__pstart)) = \
+ ( \
+ LE_BITS_CLEARED_TO_2BYTE(__pstart, __bitoffset, __bitlen) | \
+ ((((u16)__val) & BIT_LEN_MASK_16(__bitlen)) << (__bitoffset)) \
+ );
+
+#define SET_BITS_TO_LE_1BYTE(__pstart, __bitoffset, __bitlen, __val) \
+ *((u8 *)(__pstart)) = EF1BYTE \
+ ( \
+ LE_BITS_CLEARED_TO_1BYTE(__pstart, __bitoffset, __bitlen) | \
+ ((((u8)__val) & BIT_LEN_MASK_8(__bitlen)) << (__bitoffset)) \
+ )
+
+/* Get the N-bytes aligment offset from the current length */
+#define N_BYTE_ALIGMENT(__value, __aligment) ((__aligment == 1) ? \
+ (__value) : (((__value + __aligment - 1) / __aligment) * __aligment))
+
+#endif /* __BASIC_TYPES_H__ */
diff --git a/drivers/staging/rtl8188eu/include/drv_types.h b/drivers/staging/rtl8188eu/include/drv_types.h
new file mode 100644
index 000000000..c81317906
--- /dev/null
+++ b/drivers/staging/rtl8188eu/include/drv_types.h
@@ -0,0 +1,254 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2012 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ *
+ ******************************************************************************/
+/*-----------------------------------------------------------------------------
+
+ For type defines and data structure defines
+
+------------------------------------------------------------------------------*/
+
+
+#ifndef __DRV_TYPES_H__
+#define __DRV_TYPES_H__
+
+#define DRV_NAME "r8188eu"
+
+#include <osdep_service.h>
+#include <wlan_bssdef.h>
+#include <rtw_ht.h>
+#include <rtw_cmd.h>
+#include <rtw_xmit.h>
+#include <rtw_recv.h>
+#include <hal_intf.h>
+#include <hal_com.h>
+#include <rtw_qos.h>
+#include <rtw_security.h>
+#include <rtw_pwrctrl.h>
+#include <rtw_eeprom.h>
+#include <sta_info.h>
+#include <rtw_mlme.h>
+#include <rtw_debug.h>
+#include <rtw_rf.h>
+#include <rtw_event.h>
+#include <rtw_led.h>
+#include <rtw_mlme_ext.h>
+#include <rtw_ap.h>
+
+#define SPEC_DEV_ID_NONE BIT(0)
+#define SPEC_DEV_ID_DISABLE_HT BIT(1)
+#define SPEC_DEV_ID_ENABLE_PS BIT(2)
+#define SPEC_DEV_ID_RF_CONFIG_1T1R BIT(3)
+#define SPEC_DEV_ID_RF_CONFIG_2T2R BIT(4)
+#define SPEC_DEV_ID_ASSIGN_IFNAME BIT(5)
+
+struct registry_priv {
+ u8 chip_version;
+ u8 rfintfs;
+ u8 lbkmode;
+ u8 hci;
+ struct ndis_802_11_ssid ssid;
+ u8 network_mode; /* infra, ad-hoc, auto */
+ u8 channel;/* ad-hoc support requirement */
+ u8 wireless_mode;/* A, B, G, auto */
+ u8 scan_mode;/* active, passive */
+ u8 radio_enable;
+ u8 preamble;/* long, short, auto */
+ u8 vrtl_carrier_sense;/* Enable, Disable, Auto */
+ u8 vcs_type;/* RTS/CTS, CTS-to-self */
+ u16 rts_thresh;
+ u16 frag_thresh;
+ u8 adhoc_tx_pwr;
+ u8 soft_ap;
+ u8 power_mgnt;
+ u8 ips_mode;
+ u8 smart_ps;
+ u8 long_retry_lmt;
+ u8 short_retry_lmt;
+ u16 busy_thresh;
+ u8 ack_policy;
+ u8 mp_mode;
+ u8 software_encrypt;
+ u8 software_decrypt;
+ u8 acm_method;
+ /* UAPSD */
+ u8 wmm_enable;
+ u8 uapsd_enable;
+ u8 uapsd_max_sp;
+ u8 uapsd_acbk_en;
+ u8 uapsd_acbe_en;
+ u8 uapsd_acvi_en;
+ u8 uapsd_acvo_en;
+
+ struct wlan_bssid_ex dev_network;
+
+ u8 ht_enable;
+ u8 cbw40_enable;
+ u8 ampdu_enable;/* for tx */
+ u8 rx_stbc;
+ u8 ampdu_amsdu;/* A-MPDU Supports A-MSDU is permitted */
+ u8 lowrate_two_xmit;
+
+ u8 rf_config;
+ u8 low_power;
+
+ u8 wifi_spec;/* !turbo_mode */
+
+ u8 channel_plan;
+ bool bAcceptAddbaReq;
+
+ u8 antdiv_cfg;
+ u8 antdiv_type;
+
+ u8 usbss_enable;/* 0:disable,1:enable */
+ u8 hwpdn_mode;/* 0:disable,1:enable,2:decide by EFUSE config */
+ u8 hwpwrp_detect;/* 0:disable,1:enable */
+
+ u8 hw_wps_pbc;/* 0:disable,1:enable */
+
+ u8 max_roaming_times; /* the max number driver will try */
+
+ u8 fw_iol; /* enable iol without other concern */
+
+ u8 enable80211d;
+
+ u8 ifname[16];
+ u8 if2name[16];
+
+ u8 notch_filter;
+};
+
+/* For registry parameters */
+#define RGTRY_OFT(field) ((u32)FIELD_OFFSET(struct registry_priv, field))
+#define RGTRY_SZ(field) sizeof(((struct registry_priv *)0)->field)
+#define BSSID_OFT(field) ((u32)FIELD_OFFSET(struct wlan_bssid_ex, field))
+#define BSSID_SZ(field) sizeof(((struct wlan_bssid_ex *)0)->field)
+
+#define MAX_CONTINUAL_URB_ERR 4
+
+struct dvobj_priv {
+ struct adapter *if1;
+ /* For 92D, DMDP have 2 interface. */
+ u8 InterfaceNumber;
+ u8 NumInterfaces;
+
+ /* In /Out Pipe information */
+ int RtInPipe[2];
+ int RtOutPipe[3];
+ u8 Queue2Pipe[HW_QUEUE_ENTRY];/* for out pipe mapping */
+
+/*-------- below is for USB INTERFACE --------*/
+
+ u8 nr_endpoint;
+ u8 ishighspeed;
+ u8 RtNumInPipes;
+ u8 RtNumOutPipes;
+ int ep_num[5]; /* endpoint number */
+ struct mutex usb_vendor_req_mutex;
+
+ u8 *usb_vendor_req_buf;
+
+ struct usb_interface *pusbintf;
+ struct usb_device *pusbdev;
+};
+
+static inline struct device *dvobj_to_dev(struct dvobj_priv *dvobj)
+{
+ /* todo: get interface type from dvobj and the return
+ * the dev accordingly */
+ return &dvobj->pusbintf->dev;
+};
+
+struct adapter {
+ int pid[3];/* process id from UI, 0:wps, 1:hostapd, 2:dhcpcd */
+ u16 chip_type;
+
+ struct dvobj_priv *dvobj;
+ struct mlme_priv mlmepriv;
+ struct mlme_ext_priv mlmeextpriv;
+ struct cmd_priv cmdpriv;
+ struct xmit_priv xmitpriv;
+ struct recv_priv recvpriv;
+ struct sta_priv stapriv;
+ struct security_priv securitypriv;
+ struct registry_priv registrypriv;
+ struct pwrctrl_priv pwrctrlpriv;
+ struct eeprom_priv eeprompriv;
+ struct led_priv ledpriv;
+
+#ifdef CONFIG_88EU_AP_MODE
+ struct hostapd_priv *phostapdpriv;
+#endif
+
+ struct wifidirect_info wdinfo;
+
+ void *HalData;
+ struct hal_ops HalFunc;
+
+ s32 bDriverStopped;
+ s32 bSurpriseRemoved;
+
+ u8 hw_init_completed;
+
+ void *cmdThread;
+ void *evtThread;
+ void (*intf_start)(struct adapter *adapter);
+ void (*intf_stop)(struct adapter *adapter);
+ struct net_device *pnetdev;
+
+ /* used by rtw_rereg_nd_name related function */
+ struct rereg_nd_name_data {
+ struct net_device *old_pnetdev;
+ char old_ifname[IFNAMSIZ];
+ u8 old_ips_mode;
+ u8 old_bRegUseLed;
+ } rereg_nd_name_priv;
+
+ int bup;
+ struct net_device_stats stats;
+ struct iw_statistics iwstats;
+ struct proc_dir_entry *dir_dev;/* for proc directory */
+
+ int net_closed;
+ u8 bFWReady;
+ u8 bReadPortCancel;
+ u8 bWritePortCancel;
+ u8 bRxRSSIDisplay;
+ /* The driver will show up the desired channel number
+ * when this flag is 1. */
+ u8 bNotifyChannelChange;
+
+ struct mutex hw_init_mutex;
+
+ spinlock_t br_ext_lock;
+
+ u8 fix_rate;
+
+ unsigned char in_cta_test;
+};
+
+#define adapter_to_dvobj(adapter) (adapter->dvobj)
+
+int rtw_handle_dualmac(struct adapter *adapter, bool init);
+
+static inline u8 *myid(struct eeprom_priv *peepriv)
+{
+ return peepriv->mac_addr;
+}
+
+#endif /* __DRV_TYPES_H__ */
diff --git a/drivers/staging/rtl8188eu/include/fw.h b/drivers/staging/rtl8188eu/include/fw.h
new file mode 100644
index 000000000..7884d8f65
--- /dev/null
+++ b/drivers/staging/rtl8188eu/include/fw.h
@@ -0,0 +1,59 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2009-2013 Realtek Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ * The full GNU General Public License is included in this distribution in the
+ * file called LICENSE.
+ *
+ * Contact Information:
+ * wlanfae <wlanfae@realtek.com>
+ * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park,
+ * Hsinchu 300, Taiwan.
+ * Larry Finger <Larry.Finger@lwfinger.net>
+ *
+ *****************************************************************************/
+#include "drv_types.h"
+#include <linux/types.h>
+
+#ifndef __RTL92C__FW__H__
+#define __RTL92C__FW__H__
+
+#define FW_8192C_START_ADDRESS 0x1000
+#define FW_8192C_PAGE_SIZE 4096
+#define FW_8192C_POLLING_DELAY 5
+
+struct rtl92c_firmware_header {
+ __le16 signature;
+ u8 category;
+ u8 function;
+ u16 version;
+ u8 subversion;
+ u8 rsvd1;
+ u8 month;
+ u8 date;
+ u8 hour;
+ u8 minute;
+ u16 ramcodesize;
+ u16 rsvd2;
+ u32 svnindex;
+ u32 rsvd3;
+ u32 rsvd4;
+ u32 rsvd5;
+};
+
+int rtl88eu_download_fw(struct adapter *adapt);
+
+#endif
diff --git a/drivers/staging/rtl8188eu/include/hal_com.h b/drivers/staging/rtl8188eu/include/hal_com.h
new file mode 100644
index 000000000..47715d949
--- /dev/null
+++ b/drivers/staging/rtl8188eu/include/hal_com.h
@@ -0,0 +1,169 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2011 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ *
+ ******************************************************************************/
+#ifndef __HAL_COMMON_H__
+#define __HAL_COMMON_H__
+
+/* */
+/* Rate Definition */
+/* */
+/* CCK */
+#define RATR_1M 0x00000001
+#define RATR_2M 0x00000002
+#define RATR_55M 0x00000004
+#define RATR_11M 0x00000008
+/* OFDM */
+#define RATR_6M 0x00000010
+#define RATR_9M 0x00000020
+#define RATR_12M 0x00000040
+#define RATR_18M 0x00000080
+#define RATR_24M 0x00000100
+#define RATR_36M 0x00000200
+#define RATR_48M 0x00000400
+#define RATR_54M 0x00000800
+/* MCS 1 Spatial Stream */
+#define RATR_MCS0 0x00001000
+#define RATR_MCS1 0x00002000
+#define RATR_MCS2 0x00004000
+#define RATR_MCS3 0x00008000
+#define RATR_MCS4 0x00010000
+#define RATR_MCS5 0x00020000
+#define RATR_MCS6 0x00040000
+#define RATR_MCS7 0x00080000
+/* MCS 2 Spatial Stream */
+#define RATR_MCS8 0x00100000
+#define RATR_MCS9 0x00200000
+#define RATR_MCS10 0x00400000
+#define RATR_MCS11 0x00800000
+#define RATR_MCS12 0x01000000
+#define RATR_MCS13 0x02000000
+#define RATR_MCS14 0x04000000
+#define RATR_MCS15 0x08000000
+
+/* CCK */
+#define RATE_1M BIT(0)
+#define RATE_2M BIT(1)
+#define RATE_5_5M BIT(2)
+#define RATE_11M BIT(3)
+/* OFDM */
+#define RATE_6M BIT(4)
+#define RATE_9M BIT(5)
+#define RATE_12M BIT(6)
+#define RATE_18M BIT(7)
+#define RATE_24M BIT(8)
+#define RATE_36M BIT(9)
+#define RATE_48M BIT(10)
+#define RATE_54M BIT(11)
+/* MCS 1 Spatial Stream */
+#define RATE_MCS0 BIT(12)
+#define RATE_MCS1 BIT(13)
+#define RATE_MCS2 BIT(14)
+#define RATE_MCS3 BIT(15)
+#define RATE_MCS4 BIT(16)
+#define RATE_MCS5 BIT(17)
+#define RATE_MCS6 BIT(18)
+#define RATE_MCS7 BIT(19)
+/* MCS 2 Spatial Stream */
+#define RATE_MCS8 BIT(20)
+#define RATE_MCS9 BIT(21)
+#define RATE_MCS10 BIT(22)
+#define RATE_MCS11 BIT(23)
+#define RATE_MCS12 BIT(24)
+#define RATE_MCS13 BIT(25)
+#define RATE_MCS14 BIT(26)
+#define RATE_MCS15 BIT(27)
+
+/* ALL CCK Rate */
+#define RATE_ALL_CCK (RATR_1M | RATR_2M | RATR_55M | RATR_11M)
+#define RATE_ALL_OFDM_AG (RATR_6M | RATR_9M | RATR_12M | RATR_18M | \
+ RATR_24M | RATR_36M | RATR_48M | RATR_54M)
+#define RATE_ALL_OFDM_1SS (RATR_MCS0 | RATR_MCS1 | RATR_MCS2 | \
+ RATR_MCS3 | RATR_MCS4 | RATR_MCS5|RATR_MCS6 | \
+ RATR_MCS7)
+#define RATE_ALL_OFDM_2SS (RATR_MCS8 | RATR_MCS9 | RATR_MCS10 | \
+ RATR_MCS11 | RATR_MCS12 | RATR_MCS13 | \
+ RATR_MCS14 | RATR_MCS15)
+
+/*------------------------------ Tx Desc definition Macro --------------------*/
+/* pragma mark -- Tx Desc related definition. -- */
+/* Rate */
+/* CCK Rates, TxHT = 0 */
+#define DESC_RATE1M 0x00
+#define DESC_RATE2M 0x01
+#define DESC_RATE5_5M 0x02
+#define DESC_RATE11M 0x03
+
+/* OFDM Rates, TxHT = 0 */
+#define DESC_RATE6M 0x04
+#define DESC_RATE9M 0x05
+#define DESC_RATE12M 0x06
+#define DESC_RATE18M 0x07
+#define DESC_RATE24M 0x08
+#define DESC_RATE36M 0x09
+#define DESC_RATE48M 0x0a
+#define DESC_RATE54M 0x0b
+
+/* MCS Rates, TxHT = 1 */
+#define DESC_RATEMCS0 0x0c
+#define DESC_RATEMCS1 0x0d
+#define DESC_RATEMCS2 0x0e
+#define DESC_RATEMCS3 0x0f
+#define DESC_RATEMCS4 0x10
+#define DESC_RATEMCS5 0x11
+#define DESC_RATEMCS6 0x12
+#define DESC_RATEMCS7 0x13
+#define DESC_RATEMCS8 0x14
+#define DESC_RATEMCS9 0x15
+#define DESC_RATEMCS10 0x16
+#define DESC_RATEMCS11 0x17
+#define DESC_RATEMCS12 0x18
+#define DESC_RATEMCS13 0x19
+#define DESC_RATEMCS14 0x1a
+#define DESC_RATEMCS15 0x1b
+#define DESC_RATEMCS15_SG 0x1c
+#define DESC_RATEMCS32 0x20
+
+/* 1 Byte long (in unit of TU) */
+#define REG_P2P_CTWIN 0x0572
+#define REG_NOA_DESC_SEL 0x05CF
+#define REG_NOA_DESC_DURATION 0x05E0
+#define REG_NOA_DESC_INTERVAL 0x05E4
+#define REG_NOA_DESC_START 0x05E8
+#define REG_NOA_DESC_COUNT 0x05EC
+
+#include "HalVerDef.h"
+void dump_chip_info(struct HAL_VERSION ChipVersion);
+
+
+/* return the final channel plan decision */
+u8 hal_com_get_channel_plan(struct adapter *padapter,
+ u8 hw_channel_plan,
+ u8 sw_channel_plan,
+ u8 def_channel_plan,
+ bool AutoLoadFail
+);
+
+u8 MRateToHwRate(u8 rate);
+
+void HalSetBrateCfg(struct adapter *Adapter, u8 *mBratesOS, u16 *pBrateCfg);
+
+bool Hal_MappingOutPipe(struct adapter *pAdapter, u8 NumOutPipe);
+
+void hal_init_macaddr(struct adapter *adapter);
+#endif /* __HAL_COMMON_H__ */
diff --git a/drivers/staging/rtl8188eu/include/hal_intf.h b/drivers/staging/rtl8188eu/include/hal_intf.h
new file mode 100644
index 000000000..3b476d80f
--- /dev/null
+++ b/drivers/staging/rtl8188eu/include/hal_intf.h
@@ -0,0 +1,325 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2012 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ *
+ ******************************************************************************/
+#ifndef __HAL_INTF_H__
+#define __HAL_INTF_H__
+
+#include <osdep_service.h>
+#include <drv_types.h>
+#include <Hal8188EPhyCfg.h>
+
+enum RTL871X_HCI_TYPE {
+ RTW_PCIE = BIT0,
+ RTW_USB = BIT1,
+ RTW_SDIO = BIT2,
+ RTW_GSPI = BIT3,
+};
+
+enum _CHIP_TYPE {
+ NULL_CHIP_TYPE,
+ RTL8712_8188S_8191S_8192S,
+ RTL8188C_8192C,
+ RTL8192D,
+ RTL8723A,
+ RTL8188E,
+ MAX_CHIP_TYPE
+};
+
+enum hw_variables {
+ HW_VAR_MEDIA_STATUS,
+ HW_VAR_MEDIA_STATUS1,
+ HW_VAR_SET_OPMODE,
+ HW_VAR_MAC_ADDR,
+ HW_VAR_BSSID,
+ HW_VAR_INIT_RTS_RATE,
+ HW_VAR_BASIC_RATE,
+ HW_VAR_TXPAUSE,
+ HW_VAR_BCN_FUNC,
+ HW_VAR_CORRECT_TSF,
+ HW_VAR_CHECK_BSSID,
+ HW_VAR_MLME_DISCONNECT,
+ HW_VAR_MLME_SITESURVEY,
+ HW_VAR_MLME_JOIN,
+ HW_VAR_BEACON_INTERVAL,
+ HW_VAR_SLOT_TIME,
+ HW_VAR_RESP_SIFS,
+ HW_VAR_ACK_PREAMBLE,
+ HW_VAR_SEC_CFG,
+ HW_VAR_BCN_VALID,
+ HW_VAR_RF_TYPE,
+ HW_VAR_DM_FLAG,
+ HW_VAR_DM_FUNC_OP,
+ HW_VAR_DM_FUNC_SET,
+ HW_VAR_DM_FUNC_CLR,
+ HW_VAR_CAM_EMPTY_ENTRY,
+ HW_VAR_CAM_INVALID_ALL,
+ HW_VAR_CAM_WRITE,
+ HW_VAR_CAM_READ,
+ HW_VAR_AC_PARAM_VO,
+ HW_VAR_AC_PARAM_VI,
+ HW_VAR_AC_PARAM_BE,
+ HW_VAR_AC_PARAM_BK,
+ HW_VAR_ACM_CTRL,
+ HW_VAR_AMPDU_MIN_SPACE,
+ HW_VAR_AMPDU_FACTOR,
+ HW_VAR_RXDMA_AGG_PG_TH,
+ HW_VAR_SET_RPWM,
+ HW_VAR_H2C_FW_PWRMODE,
+ HW_VAR_H2C_FW_JOINBSSRPT,
+ HW_VAR_FWLPS_RF_ON,
+ HW_VAR_H2C_FW_P2P_PS_OFFLOAD,
+ HW_VAR_TDLS_WRCR,
+ HW_VAR_TDLS_INIT_CH_SEN,
+ HW_VAR_TDLS_RS_RCR,
+ HW_VAR_TDLS_DONE_CH_SEN,
+ HW_VAR_INITIAL_GAIN,
+ HW_VAR_TRIGGER_GPIO_0,
+ HW_VAR_BT_SET_COEXIST,
+ HW_VAR_BT_ISSUE_DELBA,
+ HW_VAR_CURRENT_ANTENNA,
+ HW_VAR_ANTENNA_DIVERSITY_LINK,
+ HW_VAR_ANTENNA_DIVERSITY_SELECT,
+ HW_VAR_SWITCH_EPHY_WoWLAN,
+ HW_VAR_EFUSE_USAGE,
+ HW_VAR_EFUSE_BYTES,
+ HW_VAR_EFUSE_BT_USAGE,
+ HW_VAR_EFUSE_BT_BYTES,
+ HW_VAR_FIFO_CLEARN_UP,
+ HW_VAR_CHECK_TXBUF,
+ HW_VAR_APFM_ON_MAC, /* Auto FSM to Turn On, include clock, isolation,
+ * power control for MAC only */
+ /* The valid upper nav range for the HW updating, if the true value is
+ * larger than the upper range, the HW won't update it. */
+ /* Unit in microsecond. 0 means disable this function. */
+ HW_VAR_NAV_UPPER,
+ HW_VAR_RPT_TIMER_SETTING,
+ HW_VAR_TX_RPT_MAX_MACID,
+ HW_VAR_H2C_MEDIA_STATUS_RPT,
+ HW_VAR_CHK_HI_QUEUE_EMPTY,
+};
+
+enum hal_def_variable {
+ HAL_DEF_UNDERCORATEDSMOOTHEDPWDB,
+ HAL_DEF_IS_SUPPORT_ANT_DIV,
+ HAL_DEF_CURRENT_ANTENNA,
+ HAL_DEF_DRVINFO_SZ,
+ HAL_DEF_MAX_RECVBUF_SZ,
+ HAL_DEF_RX_PACKET_OFFSET,
+ HAL_DEF_DBG_DUMP_RXPKT,/* for dbg */
+ HAL_DEF_DBG_DM_FUNC,/* for dbg */
+ HAL_DEF_RA_DECISION_RATE,
+ HAL_DEF_RA_SGI,
+ HAL_DEF_PT_PWR_STATUS,
+ HW_VAR_MAX_RX_AMPDU_FACTOR,
+ HW_DEF_RA_INFO_DUMP,
+ HAL_DEF_DBG_DUMP_TXPKT,
+ HW_DEF_FA_CNT_DUMP,
+ HW_DEF_ODM_DBG_FLAG,
+};
+
+enum hal_odm_variable {
+ HAL_ODM_STA_INFO,
+ HAL_ODM_P2P_STATE,
+ HAL_ODM_WIFI_DISPLAY_STATE,
+};
+
+enum hal_intf_ps_func {
+ HAL_USB_SELECT_SUSPEND,
+ HAL_MAX_ID,
+};
+
+struct hal_ops {
+ u32 (*hal_power_on)(struct adapter *padapter);
+ u32 (*hal_init)(struct adapter *padapter);
+ u32 (*hal_deinit)(struct adapter *padapter);
+
+ void (*free_hal_data)(struct adapter *padapter);
+
+ u32 (*inirp_init)(struct adapter *padapter);
+ u32 (*inirp_deinit)(struct adapter *padapter);
+
+ s32 (*init_xmit_priv)(struct adapter *padapter);
+
+ s32 (*init_recv_priv)(struct adapter *padapter);
+ void (*free_recv_priv)(struct adapter *padapter);
+
+ void (*InitSwLeds)(struct adapter *padapter);
+ void (*DeInitSwLeds)(struct adapter *padapter);
+
+ void (*dm_init)(struct adapter *padapter);
+ void (*read_chip_version)(struct adapter *padapter);
+
+ void (*init_default_value)(struct adapter *padapter);
+
+ void (*intf_chip_configure)(struct adapter *padapter);
+
+ void (*read_adapter_info)(struct adapter *padapter);
+
+ void (*enable_interrupt)(struct adapter *padapter);
+ void (*disable_interrupt)(struct adapter *padapter);
+ s32 (*interrupt_handler)(struct adapter *padapter);
+
+ void (*set_bwmode_handler)(struct adapter *padapter,
+ enum ht_channel_width Bandwidth,
+ u8 Offset);
+ void (*set_channel_handler)(struct adapter *padapter, u8 channel);
+
+ void (*hal_dm_watchdog)(struct adapter *padapter);
+
+ void (*SetHwRegHandler)(struct adapter *padapter, u8 variable,
+ u8 *val);
+ void (*GetHwRegHandler)(struct adapter *padapter, u8 variable,
+ u8 *val);
+
+ u8 (*GetHalDefVarHandler)(struct adapter *padapter,
+ enum hal_def_variable eVariable,
+ void *pValue);
+ u8 (*SetHalDefVarHandler)(struct adapter *padapter,
+ enum hal_def_variable eVariable,
+ void *pValue);
+
+ void (*SetHalODMVarHandler)(struct adapter *padapter,
+ enum hal_odm_variable eVariable,
+ void *pValue1, bool bSet);
+
+ void (*UpdateRAMaskHandler)(struct adapter *padapter,
+ u32 mac_id, u8 rssi_level);
+ void (*SetBeaconRelatedRegistersHandler)(struct adapter *padapter);
+
+ void (*Add_RateATid)(struct adapter *adapter, u32 bitmap, u8 arg,
+ u8 rssi_level);
+
+ u8 (*AntDivBeforeLinkHandler)(struct adapter *adapter);
+ void (*AntDivCompareHandler)(struct adapter *adapter,
+ struct wlan_bssid_ex *dst,
+ struct wlan_bssid_ex *src);
+ s32 (*hal_xmit)(struct adapter *padapter,
+ struct xmit_frame *pxmitframe);
+ s32 (*mgnt_xmit)(struct adapter *padapter,
+ struct xmit_frame *pmgntframe);
+ u32 (*read_rfreg)(struct adapter *padapter,
+ enum rf_radio_path eRFPath, u32 RegAddr,
+ u32 BitMask);
+ void (*write_rfreg)(struct adapter *padapter,
+ enum rf_radio_path eRFPath, u32 RegAddr,
+ u32 BitMask, u32 Data);
+
+ void (*sreset_init_value)(struct adapter *padapter);
+ u8 (*sreset_get_wifi_status)(struct adapter *padapter);
+
+ void (*hal_notch_filter)(struct adapter *adapter, bool enable);
+ void (*hal_reset_security_engine)(struct adapter *adapter);
+};
+
+enum rt_eeprom_type {
+ EEPROM_93C46,
+ EEPROM_93C56,
+ EEPROM_BOOT_EFUSE,
+};
+
+#define RF_CHANGE_BY_INIT 0
+#define RF_CHANGE_BY_IPS BIT28
+#define RF_CHANGE_BY_PS BIT29
+#define RF_CHANGE_BY_HW BIT30
+#define RF_CHANGE_BY_SW BIT31
+
+enum hardware_type {
+ HARDWARE_TYPE_RTL8188EU,
+ HARDWARE_TYPE_MAX,
+};
+
+#define GET_EEPROM_EFUSE_PRIV(adapter) (&adapter->eeprompriv)
+
+#define is_boot_from_eeprom(adapter) (adapter->eeprompriv.EepromOrEfuse)
+
+void rtw_hal_def_value_init(struct adapter *padapter);
+
+void rtw_hal_free_data(struct adapter *padapter);
+
+void rtw_hal_dm_init(struct adapter *padapter);
+void rtw_hal_sw_led_init(struct adapter *padapter);
+void rtw_hal_sw_led_deinit(struct adapter *padapter);
+
+u32 rtw_hal_power_on(struct adapter *padapter);
+uint rtw_hal_init(struct adapter *padapter);
+uint rtw_hal_deinit(struct adapter *padapter);
+void rtw_hal_stop(struct adapter *padapter);
+void rtw_hal_set_hwreg(struct adapter *padapter, u8 variable, u8 *val);
+void rtw_hal_get_hwreg(struct adapter *padapter, u8 variable, u8 *val);
+
+void rtw_hal_chip_configure(struct adapter *padapter);
+void rtw_hal_read_chip_info(struct adapter *padapter);
+void rtw_hal_read_chip_version(struct adapter *padapter);
+
+u8 rtw_hal_set_def_var(struct adapter *padapter,
+ enum hal_def_variable eVariable, void *pValue);
+u8 rtw_hal_get_def_var(struct adapter *padapter,
+ enum hal_def_variable eVariable, void *pValue);
+
+void rtw_hal_set_odm_var(struct adapter *padapter,
+ enum hal_odm_variable eVariable, void *pValue1,
+ bool bSet);
+
+void rtw_hal_enable_interrupt(struct adapter *padapter);
+void rtw_hal_disable_interrupt(struct adapter *padapter);
+
+u32 rtw_hal_inirp_init(struct adapter *padapter);
+u32 rtw_hal_inirp_deinit(struct adapter *padapter);
+
+s32 rtw_hal_xmit(struct adapter *padapter, struct xmit_frame *pxmitframe);
+s32 rtw_hal_mgnt_xmit(struct adapter *padapter,
+ struct xmit_frame *pmgntframe);
+
+s32 rtw_hal_init_xmit_priv(struct adapter *padapter);
+
+s32 rtw_hal_init_recv_priv(struct adapter *padapter);
+void rtw_hal_free_recv_priv(struct adapter *padapter);
+
+void rtw_hal_update_ra_mask(struct adapter *padapter, u32 mac_id, u8 level);
+void rtw_hal_add_ra_tid(struct adapter *adapt, u32 bitmap, u8 arg, u8 level);
+void rtw_hal_clone_data(struct adapter *dst_adapt,
+ struct adapter *src_adapt);
+
+void rtw_hal_bcn_related_reg_setting(struct adapter *padapter);
+
+u32 rtw_hal_read_rfreg(struct adapter *padapter, enum rf_radio_path eRFPath,
+ u32 RegAddr, u32 BitMask);
+void rtw_hal_write_rfreg(struct adapter *padapter,
+ enum rf_radio_path eRFPath, u32 RegAddr,
+ u32 BitMask, u32 Data);
+
+void rtw_hal_set_bwmode(struct adapter *padapter,
+ enum ht_channel_width Bandwidth, u8 Offset);
+void rtw_hal_set_chan(struct adapter *padapter, u8 channel);
+void rtw_hal_dm_watchdog(struct adapter *padapter);
+
+u8 rtw_hal_antdiv_before_linked(struct adapter *padapter);
+void rtw_hal_antdiv_rssi_compared(struct adapter *padapter,
+ struct wlan_bssid_ex *dst,
+ struct wlan_bssid_ex *src);
+
+void rtw_hal_sreset_init(struct adapter *padapter);
+
+void rtw_hal_notch_filter(struct adapter *adapter, bool enable);
+void rtw_hal_reset_security_engine(struct adapter *adapter);
+
+void indicate_wx_scan_complete_event(struct adapter *padapter);
+u8 rtw_do_join(struct adapter *padapter);
+
+#endif /* __HAL_INTF_H__ */
diff --git a/drivers/staging/rtl8188eu/include/ieee80211.h b/drivers/staging/rtl8188eu/include/ieee80211.h
new file mode 100644
index 000000000..8fd35dcdb
--- /dev/null
+++ b/drivers/staging/rtl8188eu/include/ieee80211.h
@@ -0,0 +1,1260 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2011 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ *
+ ******************************************************************************/
+#ifndef __IEEE80211_H
+#define __IEEE80211_H
+
+#include <osdep_service.h>
+#include <drv_types.h>
+#include "wifi.h"
+#include <linux/wireless.h>
+
+#define MGMT_QUEUE_NUM 5
+
+#define ETH_ALEN 6
+#define ETH_TYPE_LEN 2
+#define PAYLOAD_TYPE_LEN 1
+
+#ifdef CONFIG_88EU_AP_MODE
+
+#define RTL_IOCTL_HOSTAPD (SIOCIWFIRSTPRIV + 28)
+
+/* RTL871X_IOCTL_HOSTAPD ioctl() cmd: */
+enum {
+ RTL871X_HOSTAPD_FLUSH = 1,
+ RTL871X_HOSTAPD_ADD_STA = 2,
+ RTL871X_HOSTAPD_REMOVE_STA = 3,
+ RTL871X_HOSTAPD_GET_INFO_STA = 4,
+ /* REMOVED: PRISM2_HOSTAPD_RESET_TXEXC_STA = 5, */
+ RTL871X_HOSTAPD_GET_WPAIE_STA = 5,
+ RTL871X_SET_ENCRYPTION = 6,
+ RTL871X_GET_ENCRYPTION = 7,
+ RTL871X_HOSTAPD_SET_FLAGS_STA = 8,
+ RTL871X_HOSTAPD_GET_RID = 9,
+ RTL871X_HOSTAPD_SET_RID = 10,
+ RTL871X_HOSTAPD_SET_ASSOC_AP_ADDR = 11,
+ RTL871X_HOSTAPD_SET_GENERIC_ELEMENT = 12,
+ RTL871X_HOSTAPD_MLME = 13,
+ RTL871X_HOSTAPD_SCAN_REQ = 14,
+ RTL871X_HOSTAPD_STA_CLEAR_STATS = 15,
+ RTL871X_HOSTAPD_SET_BEACON = 16,
+ RTL871X_HOSTAPD_SET_WPS_BEACON = 17,
+ RTL871X_HOSTAPD_SET_WPS_PROBE_RESP = 18,
+ RTL871X_HOSTAPD_SET_WPS_ASSOC_RESP = 19,
+ RTL871X_HOSTAPD_SET_HIDDEN_SSID = 20,
+ RTL871X_HOSTAPD_SET_MACADDR_ACL = 21,
+ RTL871X_HOSTAPD_ACL_ADD_STA = 22,
+ RTL871X_HOSTAPD_ACL_REMOVE_STA = 23,
+};
+
+/* STA flags */
+#define WLAN_STA_AUTH BIT(0)
+#define WLAN_STA_ASSOC BIT(1)
+#define WLAN_STA_PS BIT(2)
+#define WLAN_STA_TIM BIT(3)
+#define WLAN_STA_PERM BIT(4)
+#define WLAN_STA_AUTHORIZED BIT(5)
+#define WLAN_STA_PENDING_POLL BIT(6) /* pending activity poll not ACKed */
+#define WLAN_STA_SHORT_PREAMBLE BIT(7)
+#define WLAN_STA_PREAUTH BIT(8)
+#define WLAN_STA_WME BIT(9)
+#define WLAN_STA_MFP BIT(10)
+#define WLAN_STA_HT BIT(11)
+#define WLAN_STA_WPS BIT(12)
+#define WLAN_STA_MAYBE_WPS BIT(13)
+#define WLAN_STA_NONERP BIT(31)
+
+#endif
+
+#define IEEE_CMD_SET_WPA_PARAM 1
+#define IEEE_CMD_SET_WPA_IE 2
+#define IEEE_CMD_SET_ENCRYPTION 3
+#define IEEE_CMD_MLME 4
+
+#define IEEE_PARAM_WPA_ENABLED 1
+#define IEEE_PARAM_TKIP_COUNTERMEASURES 2
+#define IEEE_PARAM_DROP_UNENCRYPTED 3
+#define IEEE_PARAM_PRIVACY_INVOKED 4
+#define IEEE_PARAM_AUTH_ALGS 5
+#define IEEE_PARAM_IEEE_802_1X 6
+#define IEEE_PARAM_WPAX_SELECT 7
+
+#define AUTH_ALG_OPEN_SYSTEM 0x1
+#define AUTH_ALG_SHARED_KEY 0x2
+#define AUTH_ALG_LEAP 0x00000004
+
+#define IEEE_MLME_STA_DEAUTH 1
+#define IEEE_MLME_STA_DISASSOC 2
+
+#define IEEE_CRYPT_ERR_UNKNOWN_ALG 2
+#define IEEE_CRYPT_ERR_UNKNOWN_ADDR 3
+#define IEEE_CRYPT_ERR_CRYPT_INIT_FAILED 4
+#define IEEE_CRYPT_ERR_KEY_SET_FAILED 5
+#define IEEE_CRYPT_ERR_TX_KEY_SET_FAILED 6
+#define IEEE_CRYPT_ERR_CARD_CONF_FAILED 7
+
+
+#define IEEE_CRYPT_ALG_NAME_LEN 16
+
+#define WPA_CIPHER_NONE BIT(0)
+#define WPA_CIPHER_WEP40 BIT(1)
+#define WPA_CIPHER_WEP104 BIT(2)
+#define WPA_CIPHER_TKIP BIT(3)
+#define WPA_CIPHER_CCMP BIT(4)
+
+
+
+#define WPA_SELECTOR_LEN 4
+extern u8 RTW_WPA_OUI_TYPE[];
+extern u8 WPA_AUTH_KEY_MGMT_NONE[];
+extern u8 WPA_AUTH_KEY_MGMT_UNSPEC_802_1X[];
+extern u8 WPA_AUTH_KEY_MGMT_PSK_OVER_802_1X[];
+extern u8 WPA_CIPHER_SUITE_NONE[];
+extern u8 WPA_CIPHER_SUITE_WEP40[];
+extern u8 WPA_CIPHER_SUITE_TKIP[];
+extern u8 WPA_CIPHER_SUITE_WRAP[];
+extern u8 WPA_CIPHER_SUITE_CCMP[];
+extern u8 WPA_CIPHER_SUITE_WEP104[];
+
+
+#define RSN_HEADER_LEN 4
+#define RSN_SELECTOR_LEN 4
+
+extern u16 RSN_VERSION_BSD;
+extern u8 RSN_AUTH_KEY_MGMT_UNSPEC_802_1X[];
+extern u8 RSN_AUTH_KEY_MGMT_PSK_OVER_802_1X[];
+extern u8 RSN_CIPHER_SUITE_NONE[];
+extern u8 RSN_CIPHER_SUITE_WEP40[];
+extern u8 RSN_CIPHER_SUITE_TKIP[];
+extern u8 RSN_CIPHER_SUITE_WRAP[];
+extern u8 RSN_CIPHER_SUITE_CCMP[];
+extern u8 RSN_CIPHER_SUITE_WEP104[];
+
+enum ratr_table_mode {
+ RATR_INX_WIRELESS_NGB = 0, /* BGN 40 Mhz 2SS 1SS */
+ RATR_INX_WIRELESS_NG = 1, /* GN or N */
+ RATR_INX_WIRELESS_NB = 2, /* BGN 20 Mhz 2SS 1SS or BN */
+ RATR_INX_WIRELESS_N = 3,
+ RATR_INX_WIRELESS_GB = 4,
+ RATR_INX_WIRELESS_G = 5,
+ RATR_INX_WIRELESS_B = 6,
+ RATR_INX_WIRELESS_MC = 7,
+ RATR_INX_WIRELESS_AC_N = 8,
+};
+
+enum NETWORK_TYPE {
+ WIRELESS_INVALID = 0,
+ /* Sub-Element */
+ WIRELESS_11B = BIT(0), /* tx:cck only, rx:cck only, hw: cck */
+ WIRELESS_11G = BIT(1), /* tx:ofdm only, rx:ofdm & cck, hw:cck & ofdm*/
+ WIRELESS_11A = BIT(2), /* tx:ofdm only, rx: ofdm only, hw:ofdm only */
+ WIRELESS_11_24N = BIT(3), /* tx:MCS only, rx:MCS & cck, hw:MCS & cck */
+ WIRELESS_11_5N = BIT(4), /* tx:MCS only, rx:MCS & ofdm, hw:ofdm only */
+ WIRELESS_AC = BIT(6),
+
+ /* Combination */
+ /* tx: cck & ofdm, rx: cck & ofdm & MCS, hw: cck & ofdm */
+ WIRELESS_11BG = (WIRELESS_11B | WIRELESS_11G),
+ /* tx: ofdm & MCS, rx: ofdm & cck & MCS, hw: cck & ofdm */
+ WIRELESS_11G_24N = (WIRELESS_11G | WIRELESS_11_24N),
+ /* tx: ofdm & MCS, rx: ofdm & MCS, hw: ofdm only */
+ WIRELESS_11A_5N = (WIRELESS_11A | WIRELESS_11_5N),
+ /* tx: ofdm & cck & MCS, rx: ofdm & cck & MCS, hw: ofdm & cck */
+ WIRELESS_11BG_24N = (WIRELESS_11B | WIRELESS_11G | WIRELESS_11_24N),
+ /* tx: ofdm & MCS, rx: ofdm & MCS, hw: ofdm only */
+ WIRELESS_11AGN = (WIRELESS_11A | WIRELESS_11G | WIRELESS_11_24N |
+ WIRELESS_11_5N),
+ WIRELESS_11ABGN = (WIRELESS_11A | WIRELESS_11B | WIRELESS_11G |
+ WIRELESS_11_24N | WIRELESS_11_5N),
+};
+
+#define SUPPORTED_24G_NETTYPE_MSK \
+ (WIRELESS_11B | WIRELESS_11G | WIRELESS_11_24N)
+#define SUPPORTED_5G_NETTYPE_MSK \
+ (WIRELESS_11A | WIRELESS_11_5N)
+
+#define IsSupported24G(NetType) \
+ ((NetType) & SUPPORTED_24G_NETTYPE_MSK ? true : false)
+#define IsSupported5G(NetType) \
+ ((NetType) & SUPPORTED_5G_NETTYPE_MSK ? true : false)
+
+#define IsEnableHWCCK(NetType) \
+ IsSupported24G(NetType)
+#define IsEnableHWOFDM(NetType) \
+ ((NetType) & (WIRELESS_11G | WIRELESS_11_24N | \
+ SUPPORTED_5G_NETTYPE_MSK) ? true : false)
+
+#define IsSupportedRxCCK(NetType) IsEnableHWCCK(NetType)
+#define IsSupportedRxOFDM(NetType) IsEnableHWOFDM(NetType)
+#define IsSupportedRxMCS(NetType) IsEnableHWOFDM(NetType)
+
+#define IsSupportedTxCCK(NetType) \
+ ((NetType) & (WIRELESS_11B) ? true : false)
+#define IsSupportedTxOFDM(NetType) \
+ ((NetType) & (WIRELESS_11G|WIRELESS_11A) ? true : false)
+#define IsSupportedTxMCS(NetType) \
+ ((NetType) & (WIRELESS_11_24N|WIRELESS_11_5N) ? true : false)
+
+
+struct ieee_param {
+ u32 cmd;
+ u8 sta_addr[ETH_ALEN];
+ union {
+ struct {
+ u8 name;
+ u32 value;
+ } wpa_param;
+ struct {
+ u32 len;
+ u8 reserved[32];
+ u8 data[0];
+ } wpa_ie;
+ struct {
+ int command;
+ int reason_code;
+ } mlme;
+ struct {
+ u8 alg[IEEE_CRYPT_ALG_NAME_LEN];
+ u8 set_tx;
+ u32 err;
+ u8 idx;
+ u8 seq[8]; /* sequence counter (set: RX, get: TX) */
+ u16 key_len;
+ u8 key[0];
+ } crypt;
+#ifdef CONFIG_88EU_AP_MODE
+ struct {
+ u16 aid;
+ u16 capability;
+ int flags;
+ u8 tx_supp_rates[16];
+ struct rtw_ieee80211_ht_cap ht_cap;
+ } add_sta;
+ struct {
+ u8 reserved[2];/* for set max_num_sta */
+ u8 buf[0];
+ } bcn_ie;
+#endif
+
+ } u;
+};
+
+#ifdef CONFIG_88EU_AP_MODE
+struct ieee_param_ex {
+ u32 cmd;
+ u8 sta_addr[ETH_ALEN];
+ u8 data[0];
+};
+
+struct sta_data {
+ u16 aid;
+ u16 capability;
+ int flags;
+ u32 sta_set;
+ u8 tx_supp_rates[16];
+ u32 tx_supp_rates_len;
+ struct rtw_ieee80211_ht_cap ht_cap;
+ u64 rx_pkts;
+ u64 rx_bytes;
+ u64 rx_drops;
+ u64 tx_pkts;
+ u64 tx_bytes;
+ u64 tx_drops;
+};
+#endif
+
+#define IEEE80211_DATA_LEN 2304
+/* Maximum size for the MA-UNITDATA primitive, 802.11 standard section
+ 6.2.1.1.2.
+
+ The figure in section 7.1.2 suggests a body size of up to 2312
+ bytes is allowed, which is a bit confusing, I suspect this
+ represents the 2304 bytes of real data, plus a possible 8 bytes of
+ WEP IV and ICV. (this interpretation suggested by Ramiro Barreiro) */
+
+
+#define IEEE80211_HLEN 30
+#define IEEE80211_FRAME_LEN (IEEE80211_DATA_LEN + IEEE80211_HLEN)
+
+
+/* this is stolen from ipw2200 driver */
+#define IEEE_IBSS_MAC_HASH_SIZE 31
+
+struct ieee_ibss_seq {
+ u8 mac[ETH_ALEN];
+ u16 seq_num;
+ u16 frag_num;
+ unsigned long packet_time;
+ struct list_head list;
+};
+
+struct rtw_ieee80211_hdr {
+ __le16 frame_ctl;
+ __le16 duration_id;
+ u8 addr1[ETH_ALEN];
+ u8 addr2[ETH_ALEN];
+ u8 addr3[ETH_ALEN];
+ u16 seq_ctl;
+ u8 addr4[ETH_ALEN];
+} __packed;
+
+struct rtw_ieee80211_hdr_3addr {
+ __le16 frame_ctl;
+ __le16 duration_id;
+ u8 addr1[ETH_ALEN];
+ u8 addr2[ETH_ALEN];
+ u8 addr3[ETH_ALEN];
+ u16 seq_ctl;
+} __packed;
+
+struct rtw_ieee80211_hdr_qos {
+ __le16 frame_ctl;
+ __le16 duration_id;
+ u8 addr1[ETH_ALEN];
+ u8 addr2[ETH_ALEN];
+ u8 addr3[ETH_ALEN];
+ u16 seq_ctl;
+ u8 addr4[ETH_ALEN];
+ u16 qc;
+} __packed;
+
+struct rtw_ieee80211_hdr_3addr_qos {
+ __le16 frame_ctl;
+ __le16 duration_id;
+ u8 addr1[ETH_ALEN];
+ u8 addr2[ETH_ALEN];
+ u8 addr3[ETH_ALEN];
+ u16 seq_ctl;
+ u16 qc;
+} __packed;
+
+struct eapol {
+ u8 snap[6];
+ u16 ethertype;
+ u8 version;
+ u8 type;
+ u16 length;
+} __packed;
+
+enum eap_type {
+ EAP_PACKET = 0,
+ EAPOL_START,
+ EAPOL_LOGOFF,
+ EAPOL_KEY,
+ EAPOL_ENCAP_ASF_ALERT
+};
+
+#define IEEE80211_3ADDR_LEN 24
+#define IEEE80211_4ADDR_LEN 30
+#define IEEE80211_FCS_LEN 4
+
+#define MIN_FRAG_THRESHOLD 256U
+#define MAX_FRAG_THRESHOLD 2346U
+
+/* Frame control field constants */
+#define RTW_IEEE80211_FCTL_VERS 0x0003
+#define RTW_IEEE80211_FCTL_FTYPE 0x000c
+#define RTW_IEEE80211_FCTL_STYPE 0x00f0
+#define RTW_IEEE80211_FCTL_TODS 0x0100
+#define RTW_IEEE80211_FCTL_FROMDS 0x0200
+#define RTW_IEEE80211_FCTL_MOREFRAGS 0x0400
+#define RTW_IEEE80211_FCTL_RETRY 0x0800
+#define RTW_IEEE80211_FCTL_PM 0x1000
+#define RTW_IEEE80211_FCTL_MOREDATA 0x2000
+#define RTW_IEEE80211_FCTL_PROTECTED 0x4000
+#define RTW_IEEE80211_FCTL_ORDER 0x8000
+#define RTW_IEEE80211_FCTL_CTL_EXT 0x0f00
+
+#define RTW_IEEE80211_FTYPE_MGMT 0x0000
+#define RTW_IEEE80211_FTYPE_CTL 0x0004
+#define RTW_IEEE80211_FTYPE_DATA 0x0008
+#define RTW_IEEE80211_FTYPE_EXT 0x000c
+
+/* management */
+#define RTW_IEEE80211_STYPE_ASSOC_REQ 0x0000
+#define RTW_IEEE80211_STYPE_ASSOC_RESP 0x0010
+#define RTW_IEEE80211_STYPE_REASSOC_REQ 0x0020
+#define RTW_IEEE80211_STYPE_REASSOC_RESP 0x0030
+#define RTW_IEEE80211_STYPE_PROBE_REQ 0x0040
+#define RTW_IEEE80211_STYPE_PROBE_RESP 0x0050
+#define RTW_IEEE80211_STYPE_BEACON 0x0080
+#define RTW_IEEE80211_STYPE_ATIM 0x0090
+#define RTW_IEEE80211_STYPE_DISASSOC 0x00A0
+#define RTW_IEEE80211_STYPE_AUTH 0x00B0
+#define RTW_IEEE80211_STYPE_DEAUTH 0x00C0
+#define RTW_IEEE80211_STYPE_ACTION 0x00D0
+
+/* control */
+#define RTW_IEEE80211_STYPE_CTL_EXT 0x0060
+#define RTW_IEEE80211_STYPE_BACK_REQ 0x0080
+#define RTW_IEEE80211_STYPE_BACK 0x0090
+#define RTW_IEEE80211_STYPE_PSPOLL 0x00A0
+#define RTW_IEEE80211_STYPE_RTS 0x00B0
+#define RTW_IEEE80211_STYPE_CTS 0x00C0
+#define RTW_IEEE80211_STYPE_ACK 0x00D0
+#define RTW_IEEE80211_STYPE_CFEND 0x00E0
+#define RTW_IEEE80211_STYPE_CFENDACK 0x00F0
+
+/* data */
+#define RTW_IEEE80211_STYPE_DATA 0x0000
+#define RTW_IEEE80211_STYPE_DATA_CFACK 0x0010
+#define RTW_IEEE80211_STYPE_DATA_CFPOLL 0x0020
+#define RTW_IEEE80211_STYPE_DATA_CFACKPOLL 0x0030
+#define RTW_IEEE80211_STYPE_NULLFUNC 0x0040
+#define RTW_IEEE80211_STYPE_CFACK 0x0050
+#define RTW_IEEE80211_STYPE_CFPOLL 0x0060
+#define RTW_IEEE80211_STYPE_CFACKPOLL 0x0070
+#define RTW_IEEE80211_STYPE_QOS_DATA 0x0080
+#define RTW_IEEE80211_STYPE_QOS_DATA_CFACK 0x0090
+#define RTW_IEEE80211_STYPE_QOS_DATA_CFPOLL 0x00A0
+#define RTW_IEEE80211_STYPE_QOS_DATA_CFACKPOLL 0x00B0
+#define RTW_IEEE80211_STYPE_QOS_NULLFUNC 0x00C0
+#define RTW_IEEE80211_STYPE_QOS_CFACK 0x00D0
+#define RTW_IEEE80211_STYPE_QOS_CFPOLL 0x00E0
+#define RTW_IEEE80211_STYPE_QOS_CFACKPOLL 0x00F0
+
+/* sequence control field */
+#define RTW_IEEE80211_SCTL_FRAG 0x000F
+#define RTW_IEEE80211_SCTL_SEQ 0xFFF0
+
+
+#define RTW_ERP_INFO_NON_ERP_PRESENT BIT(0)
+#define RTW_ERP_INFO_USE_PROTECTION BIT(1)
+#define RTW_ERP_INFO_BARKER_PREAMBLE_MODE BIT(2)
+
+/* QoS, QOS */
+#define NORMAL_ACK 0
+#define NO_ACK 1
+#define NON_EXPLICIT_ACK 2
+#define BLOCK_ACK 3
+
+#ifndef ETH_P_PAE
+#define ETH_P_PAE 0x888E /* Port Access Entity (IEEE 802.1X) */
+#endif /* ETH_P_PAE */
+
+#define ETH_P_PREAUTH 0x88C7 /* IEEE 802.11i pre-authentication */
+
+#define ETH_P_ECONET 0x0018
+
+#ifndef ETH_P_80211_RAW
+#define ETH_P_80211_RAW (ETH_P_ECONET + 1)
+#endif
+
+/* IEEE 802.11 defines */
+
+#define P80211_OUI_LEN 3
+
+struct ieee80211_snap_hdr {
+ u8 dsap; /* always 0xAA */
+ u8 ssap; /* always 0xAA */
+ u8 ctrl; /* always 0x03 */
+ u8 oui[P80211_OUI_LEN]; /* organizational universal id */
+} __packed;
+
+#define SNAP_SIZE sizeof(struct ieee80211_snap_hdr)
+
+#define WLAN_FC_GET_TYPE(fc) ((fc) & RTW_IEEE80211_FCTL_FTYPE)
+#define WLAN_FC_GET_STYPE(fc) ((fc) & RTW_IEEE80211_FCTL_STYPE)
+
+#define WLAN_QC_GET_TID(qc) ((qc) & 0x0f)
+
+#define WLAN_GET_SEQ_FRAG(seq) ((seq) & RTW_IEEE80211_SCTL_FRAG)
+#define WLAN_GET_SEQ_SEQ(seq) ((seq) & RTW_IEEE80211_SCTL_SEQ)
+
+/* Authentication algorithms */
+#define WLAN_AUTH_OPEN 0
+#define WLAN_AUTH_SHARED_KEY 1
+
+#define WLAN_AUTH_CHALLENGE_LEN 128
+
+#define WLAN_CAPABILITY_BSS (1<<0)
+#define WLAN_CAPABILITY_IBSS (1<<1)
+#define WLAN_CAPABILITY_CF_POLLABLE (1<<2)
+#define WLAN_CAPABILITY_CF_POLL_REQUEST (1<<3)
+#define WLAN_CAPABILITY_PRIVACY (1<<4)
+#define WLAN_CAPABILITY_SHORT_PREAMBLE (1<<5)
+#define WLAN_CAPABILITY_PBCC (1<<6)
+#define WLAN_CAPABILITY_CHANNEL_AGILITY (1<<7)
+#define WLAN_CAPABILITY_SHORT_SLOT (1<<10)
+
+/* Status codes */
+#define WLAN_STATUS_SUCCESS 0
+#define WLAN_STATUS_UNSPECIFIED_FAILURE 1
+#define WLAN_STATUS_CAPS_UNSUPPORTED 10
+#define WLAN_STATUS_REASSOC_NO_ASSOC 11
+#define WLAN_STATUS_ASSOC_DENIED_UNSPEC 12
+#define WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG 13
+#define WLAN_STATUS_UNKNOWN_AUTH_TRANSACTION 14
+#define WLAN_STATUS_CHALLENGE_FAIL 15
+#define WLAN_STATUS_AUTH_TIMEOUT 16
+#define WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA 17
+#define WLAN_STATUS_ASSOC_DENIED_RATES 18
+/* 802.11b */
+#define WLAN_STATUS_ASSOC_DENIED_NOSHORT 19
+#define WLAN_STATUS_ASSOC_DENIED_NOPBCC 20
+#define WLAN_STATUS_ASSOC_DENIED_NOAGILITY 21
+
+/* Reason codes */
+#define WLAN_REASON_UNSPECIFIED 1
+#define WLAN_REASON_PREV_AUTH_NOT_VALID 2
+#define WLAN_REASON_DEAUTH_LEAVING 3
+#define WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY 4
+#define WLAN_REASON_DISASSOC_AP_BUSY 5
+#define WLAN_REASON_CLASS2_FRAME_FROM_NONAUTH_STA 6
+#define WLAN_REASON_CLASS3_FRAME_FROM_NONASSOC_STA 7
+#define WLAN_REASON_DISASSOC_STA_HAS_LEFT 8
+#define WLAN_REASON_STA_REQ_ASSOC_WITHOUT_AUTH 9
+#define WLAN_REASON_JOIN_WRONG_CHANNEL 65534
+#define WLAN_REASON_EXPIRATION_CHK 65535
+
+/* Information Element IDs */
+#define WLAN_EID_SSID 0
+#define WLAN_EID_SUPP_RATES 1
+#define WLAN_EID_FH_PARAMS 2
+#define WLAN_EID_DS_PARAMS 3
+#define WLAN_EID_CF_PARAMS 4
+#define WLAN_EID_TIM 5
+#define WLAN_EID_IBSS_PARAMS 6
+#define WLAN_EID_CHALLENGE 16
+/* EIDs defined by IEEE 802.11h - START */
+#define WLAN_EID_PWR_CONSTRAINT 32
+#define WLAN_EID_PWR_CAPABILITY 33
+#define WLAN_EID_TPC_REQUEST 34
+#define WLAN_EID_TPC_REPORT 35
+#define WLAN_EID_SUPPORTED_CHANNELS 36
+#define WLAN_EID_CHANNEL_SWITCH 37
+#define WLAN_EID_MEASURE_REQUEST 38
+#define WLAN_EID_MEASURE_REPORT 39
+#define WLAN_EID_QUITE 40
+#define WLAN_EID_IBSS_DFS 41
+/* EIDs defined by IEEE 802.11h - END */
+#define WLAN_EID_ERP_INFO 42
+#define WLAN_EID_HT_CAP 45
+#define WLAN_EID_RSN 48
+#define WLAN_EID_EXT_SUPP_RATES 50
+#define WLAN_EID_MOBILITY_DOMAIN 54
+#define WLAN_EID_FAST_BSS_TRANSITION 55
+#define WLAN_EID_TIMEOUT_INTERVAL 56
+#define WLAN_EID_RIC_DATA 57
+#define WLAN_EID_HT_OPERATION 61
+#define WLAN_EID_SECONDARY_CHANNEL_OFFSET 62
+#define WLAN_EID_20_40_BSS_COEXISTENCE 72
+#define WLAN_EID_20_40_BSS_INTOLERANT 73
+#define WLAN_EID_OVERLAPPING_BSS_SCAN_PARAMS 74
+#define WLAN_EID_MMIE 76
+#define WLAN_EID_VENDOR_SPECIFIC 221
+#define WLAN_EID_GENERIC (WLAN_EID_VENDOR_SPECIFIC)
+
+#define IEEE80211_MGMT_HDR_LEN 24
+#define IEEE80211_DATA_HDR3_LEN 24
+#define IEEE80211_DATA_HDR4_LEN 30
+
+
+#define IEEE80211_STATMASK_SIGNAL (1<<0)
+#define IEEE80211_STATMASK_RSSI (1<<1)
+#define IEEE80211_STATMASK_NOISE (1<<2)
+#define IEEE80211_STATMASK_RATE (1<<3)
+#define IEEE80211_STATMASK_WEMASK 0x7
+
+
+#define IEEE80211_CCK_MODULATION (1<<0)
+#define IEEE80211_OFDM_MODULATION (1<<1)
+
+#define IEEE80211_24GHZ_BAND (1<<0)
+#define IEEE80211_52GHZ_BAND (1<<1)
+
+#define IEEE80211_CCK_RATE_LEN 4
+#define IEEE80211_NUM_OFDM_RATESLEN 8
+
+
+#define IEEE80211_CCK_RATE_1MB 0x02
+#define IEEE80211_CCK_RATE_2MB 0x04
+#define IEEE80211_CCK_RATE_5MB 0x0B
+#define IEEE80211_CCK_RATE_11MB 0x16
+#define IEEE80211_OFDM_RATE_LEN 8
+#define IEEE80211_OFDM_RATE_6MB 0x0C
+#define IEEE80211_OFDM_RATE_9MB 0x12
+#define IEEE80211_OFDM_RATE_12MB 0x18
+#define IEEE80211_OFDM_RATE_18MB 0x24
+#define IEEE80211_OFDM_RATE_24MB 0x30
+#define IEEE80211_OFDM_RATE_36MB 0x48
+#define IEEE80211_OFDM_RATE_48MB 0x60
+#define IEEE80211_OFDM_RATE_54MB 0x6C
+#define IEEE80211_BASIC_RATE_MASK 0x80
+
+#define IEEE80211_CCK_RATE_1MB_MASK (1<<0)
+#define IEEE80211_CCK_RATE_2MB_MASK (1<<1)
+#define IEEE80211_CCK_RATE_5MB_MASK (1<<2)
+#define IEEE80211_CCK_RATE_11MB_MASK (1<<3)
+#define IEEE80211_OFDM_RATE_6MB_MASK (1<<4)
+#define IEEE80211_OFDM_RATE_9MB_MASK (1<<5)
+#define IEEE80211_OFDM_RATE_12MB_MASK (1<<6)
+#define IEEE80211_OFDM_RATE_18MB_MASK (1<<7)
+#define IEEE80211_OFDM_RATE_24MB_MASK (1<<8)
+#define IEEE80211_OFDM_RATE_36MB_MASK (1<<9)
+#define IEEE80211_OFDM_RATE_48MB_MASK (1<<10)
+#define IEEE80211_OFDM_RATE_54MB_MASK (1<<11)
+
+#define IEEE80211_CCK_RATES_MASK 0x0000000F
+#define IEEE80211_CCK_BASIC_RATES_MASK (IEEE80211_CCK_RATE_1MB_MASK | \
+ IEEE80211_CCK_RATE_2MB_MASK)
+#define IEEE80211_CCK_DEFAULT_RATES_MASK \
+ (IEEE80211_CCK_BASIC_RATES_MASK | \
+ IEEE80211_CCK_RATE_5MB_MASK | \
+ IEEE80211_CCK_RATE_11MB_MASK)
+
+#define IEEE80211_OFDM_RATES_MASK 0x00000FF0
+#define IEEE80211_OFDM_BASIC_RATES_MASK (IEEE80211_OFDM_RATE_6MB_MASK | \
+ IEEE80211_OFDM_RATE_12MB_MASK | \
+ IEEE80211_OFDM_RATE_24MB_MASK)
+#define IEEE80211_OFDM_DEFAULT_RATES_MASK \
+ (IEEE80211_OFDM_BASIC_RATES_MASK | \
+ IEEE80211_OFDM_RATE_9MB_MASK | \
+ IEEE80211_OFDM_RATE_18MB_MASK | \
+ IEEE80211_OFDM_RATE_36MB_MASK | \
+ IEEE80211_OFDM_RATE_48MB_MASK | \
+ IEEE80211_OFDM_RATE_54MB_MASK)
+#define IEEE80211_DEFAULT_RATES_MASK \
+ (IEEE80211_OFDM_DEFAULT_RATES_MASK | \
+ IEEE80211_CCK_DEFAULT_RATES_MASK)
+
+#define IEEE80211_NUM_OFDM_RATES 8
+#define IEEE80211_NUM_CCK_RATES 4
+#define IEEE80211_OFDM_SHIFT_MASK_A 4
+
+/* NOTE: This data is for statistical purposes; not all hardware provides this
+ * information for frames received. Not setting these will not cause
+ * any adverse affects. */
+struct ieee80211_rx_stats {
+ /* u32 mac_time[2]; */
+ s8 rssi;
+ u8 signal;
+ u8 noise;
+ u8 received_channel;
+ u16 rate; /* in 100 kbps */
+ /* u8 control; */
+ u8 mask;
+ u8 freq;
+ u16 len;
+};
+
+/* IEEE 802.11 requires that STA supports concurrent reception of at least
+ * three fragmented frames. This define can be increased to support more
+ * concurrent frames, but it should be noted that each entry can consume about
+ * 2 kB of RAM and increasing cache size will slow down frame reassembly. */
+#define IEEE80211_FRAG_CACHE_LEN 4
+
+struct ieee80211_frag_entry {
+ u32 first_frag_time;
+ uint seq;
+ uint last_frag;
+ uint qos; /* jackson */
+ uint tid; /* jackson */
+ struct sk_buff *skb;
+ u8 src_addr[ETH_ALEN];
+ u8 dst_addr[ETH_ALEN];
+};
+
+struct ieee80211_stats {
+ uint tx_unicast_frames;
+ uint tx_multicast_frames;
+ uint tx_fragments;
+ uint tx_unicast_octets;
+ uint tx_multicast_octets;
+ uint tx_deferred_transmissions;
+ uint tx_single_retry_frames;
+ uint tx_multiple_retry_frames;
+ uint tx_retry_limit_exceeded;
+ uint tx_discards;
+ uint rx_unicast_frames;
+ uint rx_multicast_frames;
+ uint rx_fragments;
+ uint rx_unicast_octets;
+ uint rx_multicast_octets;
+ uint rx_fcs_errors;
+ uint rx_discards_no_buffer;
+ uint tx_discards_wrong_sa;
+ uint rx_discards_undecryptable;
+ uint rx_message_in_msg_fragments;
+ uint rx_message_in_bad_msg_fragments;
+};
+
+struct ieee80211_softmac_stats {
+ uint rx_ass_ok;
+ uint rx_ass_err;
+ uint rx_probe_rq;
+ uint tx_probe_rs;
+ uint tx_beacons;
+ uint rx_auth_rq;
+ uint rx_auth_rs_ok;
+ uint rx_auth_rs_err;
+ uint tx_auth_rq;
+ uint no_auth_rs;
+ uint no_ass_rs;
+ uint tx_ass_rq;
+ uint rx_ass_rq;
+ uint tx_probe_rq;
+ uint reassoc;
+ uint swtxstop;
+ uint swtxawake;
+};
+
+#define SEC_KEY_1 (1<<0)
+#define SEC_KEY_2 (1<<1)
+#define SEC_KEY_3 (1<<2)
+#define SEC_KEY_4 (1<<3)
+#define SEC_ACTIVE_KEY (1<<4)
+#define SEC_AUTH_MODE (1<<5)
+#define SEC_UNICAST_GROUP (1<<6)
+#define SEC_LEVEL (1<<7)
+#define SEC_ENABLED (1<<8)
+
+#define SEC_LEVEL_0 0 /* None */
+#define SEC_LEVEL_1 1 /* WEP 40 and 104 bit */
+#define SEC_LEVEL_2 2 /* Level 1 + TKIP */
+#define SEC_LEVEL_2_CKIP 3 /* Level 1 + CKIP */
+#define SEC_LEVEL_3 4 /* Level 2 + CCMP */
+
+#define WEP_KEYS 4
+#define WEP_KEY_LEN 13
+
+struct ieee80211_security {
+ u16 active_key:2,
+ enabled:1,
+ auth_mode:2,
+ auth_algo:4,
+ unicast_uses_group:1;
+ u8 key_sizes[WEP_KEYS];
+ u8 keys[WEP_KEYS][WEP_KEY_LEN];
+ u8 level;
+ u16 flags;
+} __packed;
+
+/*
+
+ 802.11 data frame from AP
+
+ ,-------------------------------------------------------------------.
+Bytes | 2 | 2 | 6 | 6 | 6 | 2 | 0..2312 | 4 |
+ |------|------|---------|---------|---------|------|---------|------|
+Desc. | ctrl | dura | DA/RA | TA | SA | Sequ | frame | fcs |
+ | | tion | (BSSID) | | | ence | data | |
+ `-------------------------------------------------------------------'
+
+Total: 28-2340 bytes
+
+*/
+
+struct ieee80211_header_data {
+ u16 frame_ctl;
+ u16 duration_id;
+ u8 addr1[6];
+ u8 addr2[6];
+ u8 addr3[6];
+ u16 seq_ctrl;
+};
+
+#define BEACON_PROBE_SSID_ID_POSITION 12
+
+/* Management Frame Information Element Types */
+#define MFIE_TYPE_SSID 0
+#define MFIE_TYPE_RATES 1
+#define MFIE_TYPE_FH_SET 2
+#define MFIE_TYPE_DS_SET 3
+#define MFIE_TYPE_CF_SET 4
+#define MFIE_TYPE_TIM 5
+#define MFIE_TYPE_IBSS_SET 6
+#define MFIE_TYPE_CHALLENGE 16
+#define MFIE_TYPE_ERP 42
+#define MFIE_TYPE_RSN 48
+#define MFIE_TYPE_RATES_EX 50
+#define MFIE_TYPE_GENERIC 221
+
+struct ieee80211_info_element_hdr {
+ u8 id;
+ u8 len;
+} __packed;
+
+struct ieee80211_info_element {
+ u8 id;
+ u8 len;
+ u8 data[0];
+} __packed;
+
+/*
+ * These are the data types that can make up management packets
+ *
+ u16 auth_algorithm;
+ u16 auth_sequence;
+ u16 beacon_interval;
+ u16 capability;
+ u8 current_ap[ETH_ALEN];
+ u16 listen_interval;
+ struct {
+ u16 association_id:14, reserved:2;
+ } __packed;
+ u32 time_stamp[2];
+ u16 reason;
+ u16 status;
+*/
+
+#define IEEE80211_DEFAULT_TX_ESSID "Penguin"
+#define IEEE80211_DEFAULT_BASIC_RATE 10
+
+struct ieee80211_authentication {
+ struct ieee80211_header_data header;
+ u16 algorithm;
+ u16 transaction;
+ u16 status;
+ /* struct ieee80211_info_element_hdr info_element; */
+} __packed;
+
+struct ieee80211_probe_response {
+ struct ieee80211_header_data header;
+ u32 time_stamp[2];
+ u16 beacon_interval;
+ u16 capability;
+ struct ieee80211_info_element info_element;
+} __packed;
+
+struct ieee80211_probe_request {
+ struct ieee80211_header_data header;
+} __packed;
+
+struct ieee80211_assoc_request_frame {
+ struct rtw_ieee80211_hdr_3addr header;
+ u16 capability;
+ u16 listen_interval;
+ struct ieee80211_info_element_hdr info_element;
+} __packed;
+
+struct ieee80211_assoc_response_frame {
+ struct rtw_ieee80211_hdr_3addr header;
+ u16 capability;
+ u16 status;
+ u16 aid;
+} __packed;
+
+struct ieee80211_txb {
+ u8 nr_frags;
+ u8 encrypted;
+ u16 reserved;
+ u16 frag_size;
+ u16 payload_size;
+ struct sk_buff *fragments[0];
+};
+
+
+/* SWEEP TABLE ENTRIES NUMBER*/
+#define MAX_SWEEP_TAB_ENTRIES 42
+#define MAX_SWEEP_TAB_ENTRIES_PER_PACKET 7
+/* MAX_RATES_LENGTH needs to be 12. The spec says 8, and many APs
+ * only use 8, and then use extended rates for the remaining supported
+ * rates. Other APs, however, stick all of their supported rates on the
+ * main rates information element... */
+#define MAX_RATES_LENGTH ((u8)12)
+#define MAX_RATES_EX_LENGTH ((u8)16)
+#define MAX_NETWORK_COUNT 128
+#define MAX_CHANNEL_NUMBER 161
+#define IEEE80211_SOFTMAC_SCAN_TIME 400
+/* HZ / 2) */
+#define IEEE80211_SOFTMAC_ASSOC_RETRY_TIME (HZ * 2)
+
+#define CRC_LENGTH 4U
+
+#define MAX_WPA_IE_LEN (256)
+#define MAX_WPS_IE_LEN (512)
+#define MAX_P2P_IE_LEN (256)
+#define MAX_WFD_IE_LEN (128)
+
+#define NETWORK_EMPTY_ESSID (1<<0)
+#define NETWORK_HAS_OFDM (1<<1)
+#define NETWORK_HAS_CCK (1<<2)
+
+#define IEEE80211_DTIM_MBCAST 4
+#define IEEE80211_DTIM_UCAST 2
+#define IEEE80211_DTIM_VALID 1
+#define IEEE80211_DTIM_INVALID 0
+
+#define IEEE80211_PS_DISABLED 0
+#define IEEE80211_PS_UNICAST IEEE80211_DTIM_UCAST
+#define IEEE80211_PS_MBCAST IEEE80211_DTIM_MBCAST
+#define IW_ESSID_MAX_SIZE 32
+/*
+join_res:
+-1: authentication fail
+-2: association fail
+> 0: TID
+*/
+
+enum ieee80211_state {
+ /* the card is not linked at all */
+ IEEE80211_NOLINK = 0,
+
+ /* IEEE80211_ASSOCIATING* are for BSS client mode
+ * the driver shall not perform RX filtering unless
+ * the state is LINKED.
+ * The driver shall just check for the state LINKED and
+ * defaults to NOLINK for ALL the other states (including
+ * LINKED_SCANNING)
+ */
+
+ /* the association procedure will start (wq scheduling)*/
+ IEEE80211_ASSOCIATING,
+ IEEE80211_ASSOCIATING_RETRY,
+
+ /* the association procedure is sending AUTH request*/
+ IEEE80211_ASSOCIATING_AUTHENTICATING,
+
+ /* the association procedure has successfully authentcated
+ * and is sending association request
+ */
+ IEEE80211_ASSOCIATING_AUTHENTICATED,
+
+ /* the link is ok. the card associated to a BSS or linked
+ * to a ibss cell or acting as an AP and creating the bss
+ */
+ IEEE80211_LINKED,
+
+ /* same as LINKED, but the driver shall apply RX filter
+ * rules as we are in NO_LINK mode. As the card is still
+ * logically linked, but it is doing a syncro site survey
+ * then it will be back to LINKED state.
+ */
+ IEEE80211_LINKED_SCANNING,
+
+};
+
+#define DEFAULT_MAX_SCAN_AGE (15 * HZ)
+#define DEFAULT_FTS 2346
+
+static inline int is_multicast_mac_addr(const u8 *addr)
+{
+ return ((addr[0] != 0xff) && (0x01 & addr[0]));
+}
+
+static inline int is_broadcast_mac_addr(const u8 *addr)
+{
+ return (addr[0] == 0xff) && (addr[1] == 0xff) && (addr[2] == 0xff) &&
+ (addr[3] == 0xff) && (addr[4] == 0xff) && (addr[5] == 0xff);
+}
+
+#define CFG_IEEE80211_RESERVE_FCS (1<<0)
+#define CFG_IEEE80211_COMPUTE_FCS (1<<1)
+
+struct tx_pending {
+ int frag;
+ struct ieee80211_txb *txb;
+};
+
+#define MAXTID 16
+
+#define IEEE_A (1<<0)
+#define IEEE_B (1<<1)
+#define IEEE_G (1<<2)
+#define IEEE_MODE_MASK (IEEE_A|IEEE_B|IEEE_G)
+
+/* Baron move to ieee80211.c */
+int ieee80211_is_empty_essid(const char *essid, int essid_len);
+int ieee80211_get_hdrlen(u16 fc);
+
+/* Action category code */
+enum rtw_ieee80211_category {
+ RTW_WLAN_CATEGORY_SPECTRUM_MGMT = 0,
+ RTW_WLAN_CATEGORY_QOS = 1,
+ RTW_WLAN_CATEGORY_DLS = 2,
+ RTW_WLAN_CATEGORY_BACK = 3,
+ RTW_WLAN_CATEGORY_PUBLIC = 4, /* IEEE 802.11 public action frames */
+ RTW_WLAN_CATEGORY_RADIO_MEASUREMENT = 5,
+ RTW_WLAN_CATEGORY_FT = 6,
+ RTW_WLAN_CATEGORY_HT = 7,
+ RTW_WLAN_CATEGORY_SA_QUERY = 8,
+ RTW_WLAN_CATEGORY_TDLS = 12,
+ RTW_WLAN_CATEGORY_WMM = 17,
+ RTW_WLAN_CATEGORY_P2P = 0x7f,/* P2P action frames */
+};
+
+/* SPECTRUM_MGMT action code */
+enum rtw_ieee80211_spectrum_mgmt_actioncode {
+ RTW_WLAN_ACTION_SPCT_MSR_REQ = 0,
+ RTW_WLAN_ACTION_SPCT_MSR_RPRT = 1,
+ RTW_WLAN_ACTION_SPCT_TPC_REQ = 2,
+ RTW_WLAN_ACTION_SPCT_TPC_RPRT = 3,
+ RTW_WLAN_ACTION_SPCT_CHL_SWITCH = 4,
+ RTW_WLAN_ACTION_SPCT_EXT_CHL_SWITCH = 5,
+};
+
+enum _PUBLIC_ACTION {
+ ACT_PUBLIC_BSSCOEXIST = 0, /* 20/40 BSS Coexistence */
+ ACT_PUBLIC_DSE_ENABLE = 1,
+ ACT_PUBLIC_DSE_DEENABLE = 2,
+ ACT_PUBLIC_DSE_REG_LOCATION = 3,
+ ACT_PUBLIC_EXT_CHL_SWITCH = 4,
+ ACT_PUBLIC_DSE_MSR_REQ = 5,
+ ACT_PUBLIC_DSE_MSR_RPRT = 6,
+ ACT_PUBLIC_MP = 7, /* Measurement Pilot */
+ ACT_PUBLIC_DSE_PWR_CONSTRAINT = 8,
+ ACT_PUBLIC_VENDOR = 9, /* for WIFI_DIRECT */
+ ACT_PUBLIC_GAS_INITIAL_REQ = 10,
+ ACT_PUBLIC_GAS_INITIAL_RSP = 11,
+ ACT_PUBLIC_GAS_COMEBACK_REQ = 12,
+ ACT_PUBLIC_GAS_COMEBACK_RSP = 13,
+ ACT_PUBLIC_TDLS_DISCOVERY_RSP = 14,
+ ACT_PUBLIC_LOCATION_TRACK = 15,
+ ACT_PUBLIC_MAX
+};
+
+/* BACK action code */
+enum rtw_ieee80211_back_actioncode {
+ RTW_WLAN_ACTION_ADDBA_REQ = 0,
+ RTW_WLAN_ACTION_ADDBA_RESP = 1,
+ RTW_WLAN_ACTION_DELBA = 2,
+};
+
+/* HT features action code */
+enum rtw_ieee80211_ht_actioncode {
+ RTW_WLAN_ACTION_NOTIFY_CH_WIDTH = 0,
+ RTW_WLAN_ACTION_SM_PS = 1,
+ RTW_WLAN_ACTION_PSPM = 2,
+ RTW_WLAN_ACTION_PCO_PHASE = 3,
+ RTW_WLAN_ACTION_MIMO_CSI_MX = 4,
+ RTW_WLAN_ACTION_MIMO_NONCP_BF = 5,
+ RTW_WLAN_ACTION_MIMP_CP_BF = 6,
+ RTW_WLAN_ACTION_ASEL_INDICATES_FB = 7,
+ RTW_WLAN_ACTION_HI_INFO_EXCHG = 8,
+};
+
+/* BACK (block-ack) parties */
+enum rtw_ieee80211_back_parties {
+ RTW_WLAN_BACK_RECIPIENT = 0,
+ RTW_WLAN_BACK_INITIATOR = 1,
+ RTW_WLAN_BACK_TIMER = 2,
+};
+
+#define OUI_MICROSOFT 0x0050f2 /* Microsoft (also used in Wi-Fi specs)
+ * 00:50:F2 */
+#define WME_OUI_TYPE 2
+#define WME_OUI_SUBTYPE_INFORMATION_ELEMENT 0
+#define WME_OUI_SUBTYPE_PARAMETER_ELEMENT 1
+#define WME_OUI_SUBTYPE_TSPEC_ELEMENT 2
+#define WME_VERSION 1
+
+#define WME_ACTION_CODE_SETUP_REQUEST 0
+#define WME_ACTION_CODE_SETUP_RESPONSE 1
+#define WME_ACTION_CODE_TEARDOWN 2
+
+#define WME_SETUP_RESPONSE_STATUS_ADMISSION_ACCEPTED 0
+#define WME_SETUP_RESPONSE_STATUS_INVALID_PARAMETERS 1
+#define WME_SETUP_RESPONSE_STATUS_REFUSED 3
+
+#define WME_TSPEC_DIRECTION_UPLINK 0
+#define WME_TSPEC_DIRECTION_DOWNLINK 1
+#define WME_TSPEC_DIRECTION_BI_DIRECTIONAL 3
+
+
+#define OUI_BROADCOM 0x00904c /* Broadcom (Epigram) */
+
+#define VENDOR_HT_CAPAB_OUI_TYPE 0x33 /* 00-90-4c:0x33 */
+
+/**
+ * enum rtw_ieee80211_channel_flags - channel flags
+ *
+ * Channel flags set by the regulatory control code.
+ *
+ * @RTW_IEEE80211_CHAN_DISABLED: This channel is disabled.
+ * @RTW_IEEE80211_CHAN_PASSIVE_SCAN: Only passive scanning is permitted
+ * on this channel.
+ * @RTW_IEEE80211_CHAN_NO_IBSS: IBSS is not allowed on this channel.
+ * @RTW_IEEE80211_CHAN_RADAR: Radar detection is required on this channel.
+ * @RTW_IEEE80211_CHAN_NO_HT40PLUS: extension channel above this channel
+ * is not permitted.
+ * @RTW_IEEE80211_CHAN_NO_HT40MINUS: extension channel below this channel
+ * is not permitted.
+ */
+enum rtw_ieee80211_channel_flags {
+ RTW_IEEE80211_CHAN_DISABLED = 1<<0,
+ RTW_IEEE80211_CHAN_PASSIVE_SCAN = 1<<1,
+ RTW_IEEE80211_CHAN_NO_IBSS = 1<<2,
+ RTW_IEEE80211_CHAN_RADAR = 1<<3,
+ RTW_IEEE80211_CHAN_NO_HT40PLUS = 1<<4,
+ RTW_IEEE80211_CHAN_NO_HT40MINUS = 1<<5,
+};
+
+#define RTW_IEEE80211_CHAN_NO_HT40 \
+ (RTW_IEEE80211_CHAN_NO_HT40PLUS | RTW_IEEE80211_CHAN_NO_HT40MINUS)
+
+/* Represent channel details, subset of ieee80211_channel */
+struct rtw_ieee80211_channel {
+ u16 hw_value;
+ u32 flags;
+};
+
+#define CHAN_FMT \
+ "hw_value:%u, " \
+ "flags:0x%08x" \
+
+#define CHAN_ARG(channel) \
+ (channel)->hw_value \
+ , (channel)->flags \
+
+/* Parsed Information Elements */
+struct rtw_ieee802_11_elems {
+ u8 *ssid;
+ u8 ssid_len;
+ u8 *supp_rates;
+ u8 supp_rates_len;
+ u8 *fh_params;
+ u8 fh_params_len;
+ u8 *ds_params;
+ u8 ds_params_len;
+ u8 *cf_params;
+ u8 cf_params_len;
+ u8 *tim;
+ u8 tim_len;
+ u8 *ibss_params;
+ u8 ibss_params_len;
+ u8 *challenge;
+ u8 challenge_len;
+ u8 *erp_info;
+ u8 erp_info_len;
+ u8 *ext_supp_rates;
+ u8 ext_supp_rates_len;
+ u8 *wpa_ie;
+ u8 wpa_ie_len;
+ u8 *rsn_ie;
+ u8 rsn_ie_len;
+ u8 *wme;
+ u8 wme_len;
+ u8 *wme_tspec;
+ u8 wme_tspec_len;
+ u8 *wps_ie;
+ u8 wps_ie_len;
+ u8 *power_cap;
+ u8 power_cap_len;
+ u8 *supp_channels;
+ u8 supp_channels_len;
+ u8 *mdie;
+ u8 mdie_len;
+ u8 *ftie;
+ u8 ftie_len;
+ u8 *timeout_int;
+ u8 timeout_int_len;
+ u8 *ht_capabilities;
+ u8 ht_capabilities_len;
+ u8 *ht_operation;
+ u8 ht_operation_len;
+ u8 *vendor_ht_cap;
+ u8 vendor_ht_cap_len;
+};
+
+enum parse_res {
+ ParseOK = 0,
+ ParseUnknown = 1,
+ ParseFailed = -1
+};
+
+enum parse_res rtw_ieee802_11_parse_elems(u8 *start, uint len,
+ struct rtw_ieee802_11_elems *elems,
+ int show_errors);
+
+u8 *rtw_set_fixed_ie(unsigned char *pbuf, unsigned int len,
+ unsigned char *source, unsigned int *frlen);
+u8 *rtw_set_ie(u8 *pbuf, int index, uint len, u8 *source, uint *frlen);
+
+enum secondary_ch_offset {
+ SCN = 0, /* no secondary channel */
+ SCA = 1, /* secondary channel above */
+ SCB = 3, /* secondary channel below */
+};
+u8 secondary_ch_offset_to_hal_ch_offset(u8 ch_offset);
+u8 hal_ch_offset_to_secondary_ch_offset(u8 ch_offset);
+u8 *rtw_set_ie_ch_switch(u8 *buf, u32 *buf_len, u8 ch_switch_mode,
+ u8 new_ch, u8 ch_switch_cnt);
+u8 *rtw_set_ie_secondary_ch_offset(u8 *buf, u32 *buf_len,
+ u8 secondary_ch_offset);
+u8 *rtw_set_ie_mesh_ch_switch_parm(u8 *buf, u32 *buf_len, u8 ttl,
+ u8 flags, u16 reason, u16 precedence);
+
+u8 *rtw_get_ie(u8 *pbuf, int index, int *len, int limit);
+u8 *rtw_get_ie_ex(u8 *in_ie, uint in_len, u8 eid, u8 *oui,
+ u8 oui_len, u8 *ie, uint *ielen);
+int rtw_ies_remove_ie(u8 *ies, uint *ies_len, uint offset,
+ u8 eid, u8 *oui, u8 oui_len);
+
+void rtw_set_supported_rate(u8 *SupportedRates, uint mode);
+
+unsigned char *rtw_get_wpa_ie(unsigned char *pie, int *wpa_ie_len, int limit);
+unsigned char *rtw_get_wpa2_ie(unsigned char *pie, int *rsn_ie_len, int limit);
+int rtw_get_wpa_cipher_suite(u8 *s);
+int rtw_get_wpa2_cipher_suite(u8 *s);
+int rtw_get_wapi_ie(u8 *in_ie, uint in_len, u8 *wapi_ie, u16 *wapi_len);
+int rtw_parse_wpa_ie(u8 *wpa_ie, int wpa_ie_len, int *group_cipher,
+ int *pairwise_cipher, int *is_8021x);
+int rtw_parse_wpa2_ie(u8 *wpa_ie, int wpa_ie_len, int *group_cipher,
+ int *pairwise_cipher, int *is_8021x);
+
+int rtw_get_sec_ie(u8 *in_ie, uint in_len, u8 *rsn_ie, u16 *rsn_len,
+ u8 *wpa_ie, u16 *wpa_len);
+
+u8 rtw_is_wps_ie(u8 *ie_ptr, uint *wps_ielen);
+u8 *rtw_get_wps_ie(u8 *in_ie, uint in_len, u8 *wps_ie, uint *wps_ielen);
+u8 *rtw_get_wps_attr(u8 *wps_ie, uint wps_ielen, u16 target_attr_id,
+ u8 *buf_attr, u32 *len_attr);
+u8 *rtw_get_wps_attr_content(u8 *wps_ie, uint wps_ielen, u16 target_attr_id,
+ u8 *buf_content, uint *len_content);
+
+/**
+ * for_each_ie - iterate over continuous IEs
+ * @ie:
+ * @buf:
+ * @buf_len:
+ */
+#define for_each_ie(ie, buf, buf_len) \
+ for (ie = (void *)buf; (((u8 *)ie) - ((u8 *)buf) + 1) < buf_len; \
+ ie = (void *)(((u8 *)ie) + *(((u8 *)ie)+1) + 2))
+
+void dump_ies(u8 *buf, u32 buf_len);
+void dump_wps_ie(u8 *ie, u32 ie_len);
+
+uint rtw_get_rateset_len(u8 *rateset);
+
+struct registry_priv;
+int rtw_generate_ie(struct registry_priv *pregistrypriv);
+
+
+int rtw_get_bit_value_from_ieee_value(u8 val);
+
+uint rtw_is_cckrates_included(u8 *rate);
+
+uint rtw_is_cckratesonly_included(u8 *rate);
+
+int rtw_check_network_type(unsigned char *rate, int ratelen, int channel);
+
+void rtw_get_bcn_info(struct wlan_network *pnetwork);
+
+void rtw_macaddr_cfg(u8 *mac_addr);
+
+u16 rtw_mcs_rate(u8 rf_type, u8 bw_40MHz, u8 short_GI_20, u8 short_GI_40,
+ unsigned char *MCS_rate);
+
+int rtw_action_frame_parse(const u8 *frame, u32 frame_len, u8 *category,
+ u8 *action);
+const char *action_public_str(u8 action);
+
+#endif /* IEEE80211_H */
diff --git a/drivers/staging/rtl8188eu/include/ieee80211_ext.h b/drivers/staging/rtl8188eu/include/ieee80211_ext.h
new file mode 100644
index 000000000..15e53d380
--- /dev/null
+++ b/drivers/staging/rtl8188eu/include/ieee80211_ext.h
@@ -0,0 +1,290 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2011 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ *
+ ******************************************************************************/
+#ifndef __IEEE80211_EXT_H
+#define __IEEE80211_EXT_H
+
+#include <osdep_service.h>
+#include <drv_types.h>
+
+#define WMM_OUI_TYPE 2
+#define WMM_OUI_SUBTYPE_INFORMATION_ELEMENT 0
+#define WMM_OUI_SUBTYPE_PARAMETER_ELEMENT 1
+#define WMM_OUI_SUBTYPE_TSPEC_ELEMENT 2
+#define WMM_VERSION 1
+
+#define WPA_PROTO_WPA BIT(0)
+#define WPA_PROTO_RSN BIT(1)
+
+#define WPA_KEY_MGMT_IEEE8021X BIT(0)
+#define WPA_KEY_MGMT_PSK BIT(1)
+#define WPA_KEY_MGMT_NONE BIT(2)
+#define WPA_KEY_MGMT_IEEE8021X_NO_WPA BIT(3)
+#define WPA_KEY_MGMT_WPA_NONE BIT(4)
+
+
+#define WPA_CAPABILITY_PREAUTH BIT(0)
+#define WPA_CAPABILITY_MGMT_FRAME_PROTECTION BIT(6)
+#define WPA_CAPABILITY_PEERKEY_ENABLED BIT(9)
+
+
+#define PMKID_LEN 16
+
+
+struct wpa_ie_hdr {
+ u8 elem_id;
+ u8 len;
+ u8 oui[4]; /* 24-bit OUI followed by 8-bit OUI type */
+ u8 version[2]; /* little endian */
+} __packed;
+
+struct rsn_ie_hdr {
+ u8 elem_id; /* WLAN_EID_RSN */
+ u8 len;
+ u8 version[2]; /* little endian */
+} __packed;
+
+struct wme_ac_parameter {
+#if defined(__LITTLE_ENDIAN)
+ /* byte 1 */
+ u8 aifsn:4,
+ acm:1,
+ aci:2,
+ reserved:1;
+
+ /* byte 2 */
+ u8 eCWmin:4,
+ eCWmax:4;
+#elif defined(__BIG_ENDIAN)
+ /* byte 1 */
+ u8 reserved:1,
+ aci:2,
+ acm:1,
+ aifsn:4;
+
+ /* byte 2 */
+ u8 eCWmax:4,
+ eCWmin:4;
+#else
+#error "Please fix <endian.h>"
+#endif
+
+ /* bytes 3 & 4 */
+ u16 txopLimit;
+} __packed;
+
+struct wme_parameter_element {
+ /* required fields for WME version 1 */
+ u8 oui[3];
+ u8 oui_type;
+ u8 oui_subtype;
+ u8 version;
+ u8 acInfo;
+ u8 reserved;
+ struct wme_ac_parameter ac[4];
+
+} __packed;
+
+#define WPA_PUT_LE16(a, val) \
+ do { \
+ (a)[1] = ((u16)(val)) >> 8; \
+ (a)[0] = ((u16)(val)) & 0xff; \
+ } while (0)
+
+#define WPA_PUT_BE32(a, val) \
+ do { \
+ (a)[0] = (u8)((((u32) (val)) >> 24) & 0xff); \
+ (a)[1] = (u8)((((u32) (val)) >> 16) & 0xff); \
+ (a)[2] = (u8)((((u32) (val)) >> 8) & 0xff); \
+ (a)[3] = (u8)(((u32) (val)) & 0xff); \
+ } while (0)
+
+#define WPA_PUT_LE32(a, val) \
+ do { \
+ (a)[3] = (u8)((((u32) (val)) >> 24) & 0xff); \
+ (a)[2] = (u8)((((u32) (val)) >> 16) & 0xff); \
+ (a)[1] = (u8)((((u32) (val)) >> 8) & 0xff); \
+ (a)[0] = (u8)(((u32) (val)) & 0xff); \
+ } while (0)
+
+#define RSN_SELECTOR_PUT(a, val) WPA_PUT_BE32((u8 *)(a), (val))
+
+/* Action category code */
+enum ieee80211_category {
+ WLAN_CATEGORY_SPECTRUM_MGMT = 0,
+ WLAN_CATEGORY_QOS = 1,
+ WLAN_CATEGORY_DLS = 2,
+ WLAN_CATEGORY_BACK = 3,
+ WLAN_CATEGORY_HT = 7,
+ WLAN_CATEGORY_WMM = 17,
+};
+
+/* SPECTRUM_MGMT action code */
+enum ieee80211_spectrum_mgmt_actioncode {
+ WLAN_ACTION_SPCT_MSR_REQ = 0,
+ WLAN_ACTION_SPCT_MSR_RPRT = 1,
+ WLAN_ACTION_SPCT_TPC_REQ = 2,
+ WLAN_ACTION_SPCT_TPC_RPRT = 3,
+ WLAN_ACTION_SPCT_CHL_SWITCH = 4,
+ WLAN_ACTION_SPCT_EXT_CHL_SWITCH = 5,
+};
+
+/* BACK action code */
+enum ieee80211_back_actioncode {
+ WLAN_ACTION_ADDBA_REQ = 0,
+ WLAN_ACTION_ADDBA_RESP = 1,
+ WLAN_ACTION_DELBA = 2,
+};
+
+/* HT features action code */
+enum ieee80211_ht_actioncode {
+ WLAN_ACTION_NOTIFY_CH_WIDTH = 0,
+ WLAN_ACTION_SM_PS = 1,
+ WLAN_ACTION_PSPM = 2,
+ WLAN_ACTION_PCO_PHASE = 3,
+ WLAN_ACTION_MIMO_CSI_MX = 4,
+ WLAN_ACTION_MIMO_NONCP_BF = 5,
+ WLAN_ACTION_MIMP_CP_BF = 6,
+ WLAN_ACTION_ASEL_INDICATES_FB = 7,
+ WLAN_ACTION_HI_INFO_EXCHG = 8,
+};
+
+/* BACK (block-ack) parties */
+enum ieee80211_back_parties {
+ WLAN_BACK_RECIPIENT = 0,
+ WLAN_BACK_INITIATOR = 1,
+ WLAN_BACK_TIMER = 2,
+};
+
+struct ieee80211_mgmt {
+ u16 frame_control;
+ u16 duration;
+ u8 da[6];
+ u8 sa[6];
+ u8 bssid[6];
+ u16 seq_ctrl;
+ union {
+ struct {
+ u16 auth_alg;
+ u16 auth_transaction;
+ u16 status_code;
+ /* possibly followed by Challenge text */
+ u8 variable[0];
+ } __packed auth;
+ struct {
+ u16 reason_code;
+ } __packed deauth;
+ struct {
+ u16 capab_info;
+ u16 listen_interval;
+ /* followed by SSID and Supported rates */
+ u8 variable[0];
+ } __packed assoc_req;
+ struct {
+ u16 capab_info;
+ u16 status_code;
+ u16 aid;
+ /* followed by Supported rates */
+ u8 variable[0];
+ } __packed assoc_resp, reassoc_resp;
+ struct {
+ u16 capab_info;
+ u16 listen_interval;
+ u8 current_ap[6];
+ /* followed by SSID and Supported rates */
+ u8 variable[0];
+ } __packed reassoc_req;
+ struct {
+ u16 reason_code;
+ } __packed disassoc;
+ struct {
+ __le64 timestamp;
+ u16 beacon_int;
+ u16 capab_info;
+ /* followed by some of SSID, Supported rates,
+ * FH Params, DS Params, CF Params, IBSS Params, TIM */
+ u8 variable[0];
+ } __packed beacon;
+ struct {
+ /* only variable items: SSID, Supported rates */
+ u8 variable[0];
+ } __packed probe_req;
+ struct {
+ __le64 timestamp;
+ u16 beacon_int;
+ u16 capab_info;
+ /* followed by some of SSID, Supported rates,
+ * FH Params, DS Params, CF Params, IBSS Params */
+ u8 variable[0];
+ } __packed probe_resp;
+ struct {
+ u8 category;
+ union {
+ struct {
+ u8 action_code;
+ u8 dialog_token;
+ u8 status_code;
+ u8 variable[0];
+ } __packed wme_action;
+ struct {
+ u8 action_code;
+ u8 dialog_token;
+ u16 capab;
+ u16 timeout;
+ u16 start_seq_num;
+ } __packed addba_req;
+ struct {
+ u8 action_code;
+ u8 dialog_token;
+ u16 status;
+ u16 capab;
+ u16 timeout;
+ } __packed addba_resp;
+ struct {
+ u8 action_code;
+ u16 params;
+ u16 reason_code;
+ } __packed delba;
+ structi {
+ u8 action_code;
+ /* capab_info for open and confirm,
+ * reason for close
+ */
+ u16 aux;
+ /* Followed in plink_confirm by status
+ * code, AID and supported rates,
+ * and directly by supported rates in
+ * plink_open and plink_close
+ */
+ u8 variable[0];
+ } __packed plink_action;
+ struct{
+ u8 action_code;
+ u8 variable[0];
+ } __packed mesh_action;
+ } __packed u;
+ } __packed action;
+ } __packed u;
+} __packed;
+
+/* mgmt header + 1 byte category code */
+#define IEEE80211_MIN_ACTION_SIZE \
+ FIELD_OFFSET(struct ieee80211_mgmt, u.action.u)
+
+#endif
diff --git a/drivers/staging/rtl8188eu/include/mlme_osdep.h b/drivers/staging/rtl8188eu/include/mlme_osdep.h
new file mode 100644
index 000000000..ae1722c67
--- /dev/null
+++ b/drivers/staging/rtl8188eu/include/mlme_osdep.h
@@ -0,0 +1,35 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2011 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ *
+ ******************************************************************************/
+#ifndef __MLME_OSDEP_H_
+#define __MLME_OSDEP_H_
+
+#include <osdep_service.h>
+#include <drv_types.h>
+
+void rtw_init_mlme_timer(struct adapter *padapter);
+void rtw_os_indicate_disconnect(struct adapter *adapter);
+void rtw_os_indicate_connect(struct adapter *adapter);
+void rtw_os_indicate_scan_done(struct adapter *padapter, bool aborted);
+void rtw_report_sec_ie(struct adapter *adapter, u8 authmode, u8 *sec_ie);
+
+void rtw_reset_securitypriv(struct adapter *adapter);
+void indicate_wx_scan_complete_event(struct adapter *padapter);
+
+#endif /* _MLME_OSDEP_H_ */
diff --git a/drivers/staging/rtl8188eu/include/mp_custom_oid.h b/drivers/staging/rtl8188eu/include/mp_custom_oid.h
new file mode 100644
index 000000000..6fa52cf99
--- /dev/null
+++ b/drivers/staging/rtl8188eu/include/mp_custom_oid.h
@@ -0,0 +1,352 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2011 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ *
+ ******************************************************************************/
+#ifndef __CUSTOM_OID_H
+#define __CUSTOM_OID_H
+
+/* by Owen */
+/* 0xFF818000 - 0xFF81802F RTL8180 Mass Production Kit */
+/* 0xFF818500 - 0xFF81850F RTL8185 Setup Utility */
+/* 0xFF818580 - 0xFF81858F RTL8185 Phy Status Utility */
+
+/* */
+
+/* by Owen for Production Kit */
+/* For Production Kit with Agilent Equipments */
+/* in order to make our custom oids hopefully somewhat unique */
+/* we will use 0xFF (indicating implementation specific OID) */
+/* 81(first byte of non zero Realtek unique identifier) */
+/* 80 (second byte of non zero Realtek unique identifier) */
+/* XX (the custom OID number - providing 255 possible custom oids) */
+
+#define OID_RT_PRO_RESET_DUT 0xFF818000
+#define OID_RT_PRO_SET_DATA_RATE 0xFF818001
+#define OID_RT_PRO_START_TEST 0xFF818002
+#define OID_RT_PRO_STOP_TEST 0xFF818003
+#define OID_RT_PRO_SET_PREAMBLE 0xFF818004
+#define OID_RT_PRO_SET_SCRAMBLER 0xFF818005
+#define OID_RT_PRO_SET_FILTER_BB 0xFF818006
+#define OID_RT_PRO_SET_MANUAL_DIVERSITY_BB 0xFF818007
+#define OID_RT_PRO_SET_CHANNEL_DIRECT_CALL 0xFF818008
+#define OID_RT_PRO_SET_SLEEP_MODE_DIRECT_CALL 0xFF818009
+#define OID_RT_PRO_SET_WAKE_MODE_DIRECT_CALL 0xFF81800A
+
+#define OID_RT_PRO_SET_TX_ANTENNA_BB 0xFF81800D
+#define OID_RT_PRO_SET_ANTENNA_BB 0xFF81800E
+#define OID_RT_PRO_SET_CR_SCRAMBLER 0xFF81800F
+#define OID_RT_PRO_SET_CR_NEW_FILTER 0xFF818010
+#define OID_RT_PRO_SET_TX_POWER_CONTROL 0xFF818011
+#define OID_RT_PRO_SET_CR_TX_CONFIG 0xFF818012
+#define OID_RT_PRO_GET_TX_POWER_CONTROL 0xFF818013
+#define OID_RT_PRO_GET_CR_SIGNAL_QUALITY 0xFF818014
+#define OID_RT_PRO_SET_CR_SETPOINT 0xFF818015
+#define OID_RT_PRO_SET_INTEGRATOR 0xFF818016
+#define OID_RT_PRO_SET_SIGNAL_QUALITY 0xFF818017
+#define OID_RT_PRO_GET_INTEGRATOR 0xFF818018
+#define OID_RT_PRO_GET_SIGNAL_QUALITY 0xFF818019
+#define OID_RT_PRO_QUERY_EEPROM_TYPE 0xFF81801A
+#define OID_RT_PRO_WRITE_MAC_ADDRESS 0xFF81801B
+#define OID_RT_PRO_READ_MAC_ADDRESS 0xFF81801C
+#define OID_RT_PRO_WRITE_CIS_DATA 0xFF81801D
+#define OID_RT_PRO_READ_CIS_DATA 0xFF81801E
+#define OID_RT_PRO_WRITE_POWER_CONTROL 0xFF81801F
+#define OID_RT_PRO_READ_POWER_CONTROL 0xFF818020
+#define OID_RT_PRO_WRITE_EEPROM 0xFF818021
+#define OID_RT_PRO_READ_EEPROM 0xFF818022
+#define OID_RT_PRO_RESET_TX_PACKET_SENT 0xFF818023
+#define OID_RT_PRO_QUERY_TX_PACKET_SENT 0xFF818024
+#define OID_RT_PRO_RESET_RX_PACKET_RECEIVED 0xFF818025
+#define OID_RT_PRO_QUERY_RX_PACKET_RECEIVED 0xFF818026
+#define OID_RT_PRO_QUERY_RX_PACKET_CRC32_ERROR 0xFF818027
+#define OID_RT_PRO_QUERY_CURRENT_ADDRESS 0xFF818028
+#define OID_RT_PRO_QUERY_PERMANENT_ADDRESS 0xFF818029
+#define OID_RT_PRO_SET_PHILIPS_RF_PARAMETERS 0xFF81802A
+#define OID_RT_PRO_RECEIVE_PACKET 0xFF81802C
+/* added by Owen on 04/08/03 for Cameo's request */
+#define OID_RT_PRO_WRITE_EEPROM_BYTE 0xFF81802D
+#define OID_RT_PRO_READ_EEPROM_BYTE 0xFF81802E
+#define OID_RT_PRO_SET_MODULATION 0xFF81802F
+/* */
+
+/* Sean */
+#define OID_RT_DRIVER_OPTION 0xFF818080
+#define OID_RT_RF_OFF 0xFF818081
+#define OID_RT_AUTH_STATUS 0xFF818082
+
+/* */
+#define OID_RT_PRO_SET_CONTINUOUS_TX 0xFF81800B
+#define OID_RT_PRO_SET_SINGLE_CARRIER_TX 0xFF81800C
+#define OID_RT_PRO_SET_CARRIER_SUPPRESSION_TX 0xFF81802B
+#define OID_RT_PRO_SET_SINGLE_TONE_TX 0xFF818043
+/* */
+
+
+/* by Owen for RTL8185 Phy Status Report Utility */
+#define OID_RT_UTILITY_false_ALARM_COUNTERS 0xFF818580
+#define OID_RT_UTILITY_SELECT_DEBUG_MODE 0xFF818581
+#define OID_RT_UTILITY_SELECT_SUBCARRIER_NUMBER 0xFF818582
+#define OID_RT_UTILITY_GET_RSSI_STATUS 0xFF818583
+#define OID_RT_UTILITY_GET_FRAME_DETECTION_STATUS 0xFF818584
+#define OID_RT_UTILITY_GET_AGC_AND_FREQUENCY_OFFSET_ESTIMATION_STATUS \
+ 0xFF818585
+#define OID_RT_UTILITY_GET_CHANNEL_ESTIMATION_STATUS 0xFF818586
+/* */
+
+/* by Owen on 03/09/19-03/09/22 for RTL8185 */
+#define OID_RT_WIRELESS_MODE 0xFF818500
+#define OID_RT_SUPPORTED_RATES 0xFF818501
+#define OID_RT_DESIRED_RATES 0xFF818502
+#define OID_RT_WIRELESS_MODE_STARTING_ADHOC 0xFF818503
+/* */
+
+#define OID_RT_GET_CONNECT_STATE 0xFF030001
+#define OID_RT_RESCAN 0xFF030002
+#define OID_RT_SET_KEY_LENGTH 0xFF030003
+#define OID_RT_SET_DEFAULT_KEY_ID 0xFF030004
+
+#define OID_RT_SET_CHANNEL 0xFF010182
+#define OID_RT_SET_SNIFFER_MODE 0xFF010183
+#define OID_RT_GET_SIGNAL_QUALITY 0xFF010184
+#define OID_RT_GET_SMALL_PACKET_CRC 0xFF010185
+#define OID_RT_GET_MIDDLE_PACKET_CRC 0xFF010186
+#define OID_RT_GET_LARGE_PACKET_CRC 0xFF010187
+#define OID_RT_GET_TX_RETRY 0xFF010188
+#define OID_RT_GET_RX_RETRY 0xFF010189
+#define OID_RT_PRO_SET_FW_DIG_STATE 0xFF01018A/* S */
+#define OID_RT_PRO_SET_FW_RA_STATE 0xFF01018B/* S */
+
+#define OID_RT_GET_RX_TOTAL_PACKET 0xFF010190
+#define OID_RT_GET_TX_BEACON_OK 0xFF010191
+#define OID_RT_GET_TX_BEACON_ERR 0xFF010192
+#define OID_RT_GET_RX_ICV_ERR 0xFF010193
+#define OID_RT_SET_ENCRYPTION_ALGORITHM 0xFF010194
+#define OID_RT_SET_NO_AUTO_RESCAN 0xFF010195
+#define OID_RT_GET_PREAMBLE_MODE 0xFF010196
+#define OID_RT_GET_DRIVER_UP_DELTA_TIME 0xFF010197
+#define OID_RT_GET_AP_IP 0xFF010198
+#define OID_RT_GET_CHANNELPLAN 0xFF010199
+#define OID_RT_SET_PREAMBLE_MODE 0xFF01019A
+#define OID_RT_SET_BCN_INTVL 0xFF01019B
+#define OID_RT_GET_RF_VENDER 0xFF01019C
+#define OID_RT_DEDICATE_PROBE 0xFF01019D
+#define OID_RT_PRO_RX_FILTER_PATTERN 0xFF01019E
+
+#define OID_RT_GET_DCST_CURRENT_THRESHOLD 0xFF01019F
+
+#define OID_RT_GET_CCA_ERR 0xFF0101A0
+#define OID_RT_GET_CCA_UPGRADE_THRESHOLD 0xFF0101A1
+#define OID_RT_GET_CCA_FALLBACK_THRESHOLD 0xFF0101A2
+
+#define OID_RT_GET_CCA_UPGRADE_EVALUATE_TIMES 0xFF0101A3
+#define OID_RT_GET_CCA_FALLBACK_EVALUATE_TIMES 0xFF0101A4
+
+/* by Owen on 03/31/03 for Cameo's request */
+#define OID_RT_SET_RATE_ADAPTIVE 0xFF0101A5
+/* */
+#define OID_RT_GET_DCST_EVALUATE_PERIOD 0xFF0101A5
+#define OID_RT_GET_DCST_TIME_UNIT_INDEX 0xFF0101A6
+#define OID_RT_GET_TOTAL_TX_BYTES 0xFF0101A7
+#define OID_RT_GET_TOTAL_RX_BYTES 0xFF0101A8
+#define OID_RT_CURRENT_TX_POWER_LEVEL 0xFF0101A9
+#define OID_RT_GET_ENC_KEY_MISMATCH_COUNT 0xFF0101AA
+#define OID_RT_GET_ENC_KEY_MATCH_COUNT 0xFF0101AB
+#define OID_RT_GET_CHANNEL 0xFF0101AC
+
+#define OID_RT_SET_CHANNELPLAN 0xFF0101AD
+#define OID_RT_GET_HARDWARE_RADIO_OFF 0xFF0101AE
+#define OID_RT_CHANNELPLAN_BY_COUNTRY 0xFF0101AF
+#define OID_RT_SCAN_AVAILABLE_BSSID 0xFF0101B0
+#define OID_RT_GET_HARDWARE_VERSION 0xFF0101B1
+#define OID_RT_GET_IS_ROAMING 0xFF0101B2
+#define OID_RT_GET_IS_PRIVACY 0xFF0101B3
+#define OID_RT_GET_KEY_MISMATCH 0xFF0101B4
+#define OID_RT_SET_RSSI_ROAM_TRAFFIC_TH 0xFF0101B5
+#define OID_RT_SET_RSSI_ROAM_SIGNAL_TH 0xFF0101B6
+#define OID_RT_RESET_LOG 0xFF0101B7
+#define OID_RT_GET_LOG 0xFF0101B8
+#define OID_RT_SET_INDICATE_HIDDEN_AP 0xFF0101B9
+#define OID_RT_GET_HEADER_FAIL 0xFF0101BA
+#define OID_RT_SUPPORTED_WIRELESS_MODE 0xFF0101BB
+#define OID_RT_GET_CHANNEL_LIST 0xFF0101BC
+#define OID_RT_GET_SCAN_IN_PROGRESS 0xFF0101BD
+#define OID_RT_GET_TX_INFO 0xFF0101BE
+#define OID_RT_RF_READ_WRITE_OFFSET 0xFF0101BF
+#define OID_RT_RF_READ_WRITE 0xFF0101C0
+
+/* For Netgear request. 2005.01.13, by rcnjko. */
+#define OID_RT_FORCED_DATA_RATE 0xFF0101C1
+#define OID_RT_WIRELESS_MODE_FOR_SCAN_LIST 0xFF0101C2
+/* For Netgear request. 2005.02.17, by rcnjko. */
+#define OID_RT_GET_BSS_WIRELESS_MODE 0xFF0101C3
+/* For AZ project. 2005.06.27, by rcnjko. */
+#define OID_RT_SCAN_WITH_MAGIC_PACKET 0xFF0101C4
+
+/* Vincent 8185MP */
+#define OID_RT_PRO_RX_FILTER 0xFF0111C0
+
+#define OID_CE_USB_WRITE_REGISTRY 0xFF0111C1
+#define OID_CE_USB_READ_REGISTRY 0xFF0111C2
+
+#define OID_RT_PRO_SET_INITIAL_GA 0xFF0111C3
+#define OID_RT_PRO_SET_BB_RF_STANDBY_MODE 0xFF0111C4
+#define OID_RT_PRO_SET_BB_RF_SHUTDOWN_MODE 0xFF0111C5
+#define OID_RT_PRO_SET_TX_CHARGE_PUMP 0xFF0111C6
+#define OID_RT_PRO_SET_RX_CHARGE_PUMP 0xFF0111C7
+#define OID_RT_PRO_RF_WRITE_REGISTRY 0xFF0111C8
+#define OID_RT_PRO_RF_READ_REGISTRY 0xFF0111C9
+#define OID_RT_PRO_QUERY_RF_TYPE 0xFF0111CA
+
+/* AP OID */
+#define OID_RT_AP_GET_ASSOCIATED_STATION_LIST 0xFF010300
+#define OID_RT_AP_GET_CURRENT_TIME_STAMP 0xFF010301
+#define OID_RT_AP_SWITCH_INTO_AP_MODE 0xFF010302
+#define OID_RT_AP_SET_DTIM_PERIOD 0xFF010303
+/* Determine if driver supports AP mode. */
+#define OID_RT_AP_SUPPORTED 0xFF010304
+/* Set WPA-PSK passphrase into authenticator. */
+#define OID_RT_AP_SET_PASSPHRASE 0xFF010305
+
+/* 8187MP. 2004.09.06, by rcnjko. */
+#define OID_RT_PRO8187_WI_POLL 0xFF818780
+#define OID_RT_PRO_WRITE_BB_REG 0xFF818781
+#define OID_RT_PRO_READ_BB_REG 0xFF818782
+#define OID_RT_PRO_WRITE_RF_REG 0xFF818783
+#define OID_RT_PRO_READ_RF_REG 0xFF818784
+
+/* Meeting House. added by Annie, 2005-07-20. */
+#define OID_RT_MH_VENDER_ID 0xFFEDC100
+
+/* 8711 MP OID added 20051230. */
+#define OID_RT_PRO8711_JOIN_BSS 0xFF871100/* S */
+
+#define OID_RT_PRO_READ_REGISTER 0xFF871101 /* Q */
+#define OID_RT_PRO_WRITE_REGISTER 0xFF871102 /* S */
+
+#define OID_RT_PRO_BURST_READ_REGISTER 0xFF871103 /* Q */
+#define OID_RT_PRO_BURST_WRITE_REGISTER 0xFF871104 /* S */
+
+#define OID_RT_PRO_WRITE_TXCMD 0xFF871105 /* S */
+
+#define OID_RT_PRO_READ16_EEPROM 0xFF871106 /* Q */
+#define OID_RT_PRO_WRITE16_EEPROM 0xFF871107 /* S */
+
+#define OID_RT_PRO_H2C_SET_COMMAND 0xFF871108 /* S */
+#define OID_RT_PRO_H2C_QUERY_RESULT 0xFF871109 /* Q */
+
+#define OID_RT_PRO8711_WI_POLL 0xFF87110A /* Q */
+#define OID_RT_PRO8711_PKT_LOSS 0xFF87110B /* Q */
+#define OID_RT_RD_ATTRIB_MEM 0xFF87110C/* Q */
+#define OID_RT_WR_ATTRIB_MEM 0xFF87110D/* S */
+
+
+/* Method 2 for H2C/C2H */
+#define OID_RT_PRO_H2C_CMD_MODE 0xFF871110 /* S */
+#define OID_RT_PRO_H2C_CMD_RSP_MODE 0xFF871111 /* Q */
+#define OID_RT_PRO_H2C_CMD_EVENT_MODE 0xFF871112 /* S */
+#define OID_RT_PRO_WAIT_C2H_EVENT 0xFF871113 /* Q */
+#define OID_RT_PRO_RW_ACCESS_PROTOCOL_TEST 0xFF871114/* Q */
+
+#define OID_RT_PRO_SCSI_ACCESS_TEST 0xFF871115 /* Q, S */
+
+#define OID_RT_PRO_SCSI_TCPIPOFFLOAD_OUT 0xFF871116 /* S */
+#define OID_RT_PRO_SCSI_TCPIPOFFLOAD_IN 0xFF871117 /* Q,S */
+#define OID_RT_RRO_RX_PKT_VIA_IOCTRL 0xFF871118 /* Q */
+#define OID_RT_RRO_RX_PKTARRAY_VIA_IOCTRL 0xFF871119 /* Q */
+
+#define OID_RT_RPO_SET_PWRMGT_TEST 0xFF87111A /* S */
+#define OID_RT_PRO_QRY_PWRMGT_TEST 0XFF87111B /* Q */
+#define OID_RT_RPO_ASYNC_RWIO_TEST 0xFF87111C /* S */
+#define OID_RT_RPO_ASYNC_RWIO_POLL 0xFF87111D /* Q */
+#define OID_RT_PRO_SET_RF_INTFS 0xFF87111E /* S */
+#define OID_RT_POLL_RX_STATUS 0xFF87111F /* Q */
+
+#define OID_RT_PRO_CFG_DEBUG_MESSAGE 0xFF871120 /* Q,S */
+#define OID_RT_PRO_SET_DATA_RATE_EX 0xFF871121/* S */
+#define OID_RT_PRO_SET_BASIC_RATE 0xFF871122/* S */
+#define OID_RT_PRO_READ_TSSI 0xFF871123/* S */
+#define OID_RT_PRO_SET_POWER_TRACKING 0xFF871124/* S */
+
+
+#define OID_RT_PRO_QRY_PWRSTATE 0xFF871150 /* Q */
+#define OID_RT_PRO_SET_PWRSTATE 0xFF871151 /* S */
+
+/* Method 2 , using workitem */
+#define OID_RT_SET_READ_REG 0xFF871181 /* S */
+#define OID_RT_SET_WRITE_REG 0xFF871182 /* S */
+#define OID_RT_SET_BURST_READ_REG 0xFF871183 /* S */
+#define OID_RT_SET_BURST_WRITE_REG 0xFF871184 /* S */
+#define OID_RT_SET_WRITE_TXCMD 0xFF871185 /* S */
+#define OID_RT_SET_READ16_EEPROM 0xFF871186 /* S */
+#define OID_RT_SET_WRITE16_EEPROM 0xFF871187 /* S */
+#define OID_RT_QRY_POLL_WKITEM 0xFF871188 /* Q */
+
+/* For SDIO INTERFACE only */
+#define OID_RT_PRO_SYNCPAGERW_SRAM 0xFF8711A0 /* Q, S */
+#define OID_RT_PRO_871X_DRV_EXT 0xFF8711A1
+
+/* For USB INTERFACE only */
+#define OID_RT_PRO_USB_VENDOR_REQ 0xFF8711B0 /* Q, S */
+#define OID_RT_PRO_SCSI_AUTO_TEST 0xFF8711B1 /* S */
+#define OID_RT_PRO_USB_MAC_AC_FIFO_WRITE 0xFF8711B2 /* S */
+#define OID_RT_PRO_USB_MAC_RX_FIFO_READ 0xFF8711B3 /* Q */
+#define OID_RT_PRO_USB_MAC_RX_FIFO_POLLING 0xFF8711B4 /* Q */
+
+#define OID_RT_PRO_H2C_SET_RATE_TABLE 0xFF8711FB /* S */
+#define OID_RT_PRO_H2C_GET_RATE_TABLE 0xFF8711FC /* S */
+#define OID_RT_PRO_H2C_C2H_LBK_TEST 0xFF8711FE
+
+#define OID_RT_PRO_ENCRYPTION_CTRL 0xFF871200 /* Q, S */
+#define OID_RT_PRO_ADD_STA_INFO 0xFF871201 /* S */
+#define OID_RT_PRO_DELE_STA_INFO 0xFF871202 /* S */
+#define OID_RT_PRO_QUERY_DR_VARIABLE 0xFF871203 /* Q */
+
+#define OID_RT_PRO_RX_PACKET_TYPE 0xFF871204 /* Q, S */
+
+#define OID_RT_PRO_READ_EFUSE 0xFF871205 /* Q */
+#define OID_RT_PRO_WRITE_EFUSE 0xFF871206 /* S */
+#define OID_RT_PRO_RW_EFUSE_PGPKT 0xFF871207 /* Q, S */
+#define OID_RT_GET_EFUSE_CURRENT_SIZE 0xFF871208 /* Q */
+
+#define OID_RT_SET_BANDWIDTH 0xFF871209 /* S */
+#define OID_RT_SET_CRYSTAL_CAP 0xFF87120A /* S */
+
+#define OID_RT_SET_RX_PACKET_TYPE 0xFF87120B /* S */
+
+#define OID_RT_GET_EFUSE_MAX_SIZE 0xFF87120C /* Q */
+
+#define OID_RT_PRO_SET_TX_AGC_OFFSET 0xFF87120D /* S */
+
+#define OID_RT_PRO_SET_PKT_TEST_MODE 0xFF87120E /* S */
+
+#define OID_RT_PRO_FOR_EVM_TEST_SETTING 0xFF87120F /* S */
+
+#define OID_RT_PRO_GET_THERMAL_METER 0xFF871210 /* Q */
+
+#define OID_RT_RESET_PHY_RX_PACKET_COUNT 0xFF871211 /* S */
+#define OID_RT_GET_PHY_RX_PACKET_RECEIVED 0xFF871212 /* Q */
+#define OID_RT_GET_PHY_RX_PACKET_CRC32_ERROR 0xFF871213 /* Q */
+
+#define OID_RT_SET_POWER_DOWN 0xFF871214 /* S */
+
+#define OID_RT_GET_POWER_MODE 0xFF871215 /* Q */
+
+#define OID_RT_PRO_EFUSE 0xFF871216 /* Q, S */
+#define OID_RT_PRO_EFUSE_MAP 0xFF871217 /* Q, S */
+
+#endif /* ifndef __CUSTOM_OID_H */
diff --git a/drivers/staging/rtl8188eu/include/odm.h b/drivers/staging/rtl8188eu/include/odm.h
new file mode 100644
index 000000000..525eb100c
--- /dev/null
+++ b/drivers/staging/rtl8188eu/include/odm.h
@@ -0,0 +1,1109 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2011 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ *
+ ******************************************************************************/
+
+
+#ifndef __HALDMOUTSRC_H__
+#define __HALDMOUTSRC_H__
+
+/* Definition */
+/* Define all team support ability. */
+
+/* Define for all teams. Please Define the constant in your precomp header. */
+
+/* define DM_ODM_SUPPORT_AP 0 */
+/* define DM_ODM_SUPPORT_ADSL 0 */
+/* define DM_ODM_SUPPORT_CE 0 */
+/* define DM_ODM_SUPPORT_MP 1 */
+
+/* Define ODM SW team support flag. */
+
+/* Antenna Switch Relative Definition. */
+
+/* Add new function SwAntDivCheck8192C(). */
+/* This is the main function of Antenna diversity function before link. */
+/* Mainly, it just retains last scan result and scan again. */
+/* After that, it compares the scan result to see which one gets better
+ * RSSI. It selects antenna with better receiving power and returns better
+ * scan result. */
+
+#define TP_MODE 0
+#define RSSI_MODE 1
+#define TRAFFIC_LOW 0
+#define TRAFFIC_HIGH 1
+
+/* 3 Tx Power Tracking */
+/* 3============================================================ */
+#define DPK_DELTA_MAPPING_NUM 13
+#define index_mapping_HP_NUM 15
+
+
+/* */
+/* 3 PSD Handler */
+/* 3============================================================ */
+
+#define AFH_PSD 1 /* 0:normal PSD scan, 1: only do 20 pts PSD */
+#define MODE_40M 0 /* 0:20M, 1:40M */
+#define PSD_TH2 3
+#define PSD_CHM 20 /* Minimum channel number for BT AFH */
+#define SIR_STEP_SIZE 3
+#define Smooth_Size_1 5
+#define Smooth_TH_1 3
+#define Smooth_Size_2 10
+#define Smooth_TH_2 4
+#define Smooth_Size_3 20
+#define Smooth_TH_3 4
+#define Smooth_Step_Size 5
+#define Adaptive_SIR 1
+#define PSD_RESCAN 4
+#define PSD_SCAN_INTERVAL 700 /* ms */
+
+/* 8723A High Power IGI Setting */
+#define DM_DIG_HIGH_PWR_IGI_LOWER_BOUND 0x22
+#define DM_DIG_Gmode_HIGH_PWR_IGI_LOWER_BOUND 0x28
+#define DM_DIG_HIGH_PWR_THRESHOLD 0x3a
+
+/* LPS define */
+#define DM_DIG_FA_TH0_LPS 4 /* 4 in lps */
+#define DM_DIG_FA_TH1_LPS 15 /* 15 lps */
+#define DM_DIG_FA_TH2_LPS 30 /* 30 lps */
+#define RSSI_OFFSET_DIG 0x05;
+
+/* ANT Test */
+#define ANTTESTALL 0x00 /* Ant A or B will be Testing */
+#define ANTTESTA 0x01 /* Ant A will be Testing */
+#define ANTTESTB 0x02 /* Ant B will be testing */
+
+struct rtw_dig {
+ u8 Dig_Enable_Flag;
+ u8 Dig_Ext_Port_Stage;
+
+ int RssiLowThresh;
+ int RssiHighThresh;
+
+ u32 FALowThresh;
+ u32 FAHighThresh;
+
+ u8 CurSTAConnectState;
+ u8 PreSTAConnectState;
+ u8 CurMultiSTAConnectState;
+
+ u8 PreIGValue;
+ u8 CurIGValue;
+ u8 BackupIGValue;
+
+ s8 BackoffVal;
+ s8 BackoffVal_range_max;
+ s8 BackoffVal_range_min;
+ u8 rx_gain_range_max;
+ u8 rx_gain_range_min;
+ u8 Rssi_val_min;
+
+ u8 PreCCK_CCAThres;
+ u8 CurCCK_CCAThres;
+ u8 PreCCKPDState;
+ u8 CurCCKPDState;
+
+ u8 LargeFAHit;
+ u8 ForbiddenIGI;
+ u32 Recover_cnt;
+
+ u8 DIG_Dynamic_MIN_0;
+ u8 DIG_Dynamic_MIN_1;
+ bool bMediaConnect_0;
+ bool bMediaConnect_1;
+
+ u32 AntDiv_RSSI_max;
+ u32 RSSI_max;
+};
+
+struct rtl_ps {
+ u8 PreCCAState;
+ u8 CurCCAState;
+
+ u8 PreRFState;
+ u8 CurRFState;
+
+ int Rssi_val_min;
+
+ u8 initialize;
+ u32 Reg874, RegC70, Reg85C, RegA74;
+
+};
+
+struct false_alarm_stats {
+ u32 Cnt_Parity_Fail;
+ u32 Cnt_Rate_Illegal;
+ u32 Cnt_Crc8_fail;
+ u32 Cnt_Mcs_fail;
+ u32 Cnt_Ofdm_fail;
+ u32 Cnt_Cck_fail;
+ u32 Cnt_all;
+ u32 Cnt_Fast_Fsync;
+ u32 Cnt_SB_Search_fail;
+ u32 Cnt_OFDM_CCA;
+ u32 Cnt_CCK_CCA;
+ u32 Cnt_CCA_all;
+ u32 Cnt_BW_USC; /* Gary */
+ u32 Cnt_BW_LSC; /* Gary */
+};
+
+struct rx_hpc {
+ u8 RXHP_flag;
+ u8 PSD_func_trigger;
+ u8 PSD_bitmap_RXHP[80];
+ u8 Pre_IGI;
+ u8 Cur_IGI;
+ u8 Pre_pw_th;
+ u8 Cur_pw_th;
+ bool First_time_enter;
+ bool RXHP_enable;
+ u8 TP_Mode;
+ struct timer_list PSDTimer;
+};
+
+#define ASSOCIATE_ENTRY_NUM 32 /* Max size of AsocEntry[]. */
+#define ODM_ASSOCIATE_ENTRY_NUM ASSOCIATE_ENTRY_NUM
+
+/* This indicates two different steps. */
+/* In SWAW_STEP_PEAK, driver needs to switch antenna and listen to
+ * the signal on the air. */
+/* In SWAW_STEP_DETERMINE, driver just compares the signal captured in
+ * SWAW_STEP_PEAK with original RSSI to determine if it is necessary to
+ * switch antenna. */
+
+#define SWAW_STEP_PEAK 0
+#define SWAW_STEP_DETERMINE 1
+
+#define TP_MODE 0
+#define RSSI_MODE 1
+#define TRAFFIC_LOW 0
+#define TRAFFIC_HIGH 1
+
+struct sw_ant_switch {
+ u8 try_flag;
+ s32 PreRSSI;
+ u8 CurAntenna;
+ u8 PreAntenna;
+ u8 RSSI_Trying;
+ u8 TestMode;
+ u8 bTriggerAntennaSwitch;
+ u8 SelectAntennaMap;
+ u8 RSSI_target;
+
+ /* Before link Antenna Switch check */
+ u8 SWAS_NoLink_State;
+ u32 SWAS_NoLink_BK_Reg860;
+ bool ANTA_ON; /* To indicate Ant A is or not */
+ bool ANTB_ON; /* To indicate Ant B is on or not */
+
+ s32 RSSI_sum_A;
+ s32 RSSI_sum_B;
+ s32 RSSI_cnt_A;
+ s32 RSSI_cnt_B;
+ u64 lastTxOkCnt;
+ u64 lastRxOkCnt;
+ u64 TXByteCnt_A;
+ u64 TXByteCnt_B;
+ u64 RXByteCnt_A;
+ u64 RXByteCnt_B;
+ u8 TrafficLoad;
+ struct timer_list SwAntennaSwitchTimer;
+ /* Hybrid Antenna Diversity */
+ u32 CCK_Ant1_Cnt[ASSOCIATE_ENTRY_NUM];
+ u32 CCK_Ant2_Cnt[ASSOCIATE_ENTRY_NUM];
+ u32 OFDM_Ant1_Cnt[ASSOCIATE_ENTRY_NUM];
+ u32 OFDM_Ant2_Cnt[ASSOCIATE_ENTRY_NUM];
+ u32 RSSI_Ant1_Sum[ASSOCIATE_ENTRY_NUM];
+ u32 RSSI_Ant2_Sum[ASSOCIATE_ENTRY_NUM];
+ u8 TxAnt[ASSOCIATE_ENTRY_NUM];
+ u8 TargetSTA;
+ u8 antsel;
+ u8 RxIdleAnt;
+};
+
+struct edca_turbo {
+ bool bCurrentTurboEDCA;
+ bool bIsCurRDLState;
+ u32 prv_traffic_idx; /* edca turbo */
+};
+
+struct odm_rate_adapt {
+ u8 Type; /* DM_Type_ByFW/DM_Type_ByDriver */
+ u8 HighRSSIThresh; /* if RSSI > HighRSSIThresh => RATRState is DM_RATR_STA_HIGH */
+ u8 LowRSSIThresh; /* if RSSI <= LowRSSIThresh => RATRState is DM_RATR_STA_LOW */
+ u8 RATRState; /* Current RSSI level, DM_RATR_STA_HIGH/DM_RATR_STA_MIDDLE/DM_RATR_STA_LOW */
+ u32 LastRATR; /* RATR Register Content */
+};
+
+#define IQK_MAC_REG_NUM 4
+#define IQK_ADDA_REG_NUM 16
+#define IQK_BB_REG_NUM_MAX 10
+#define IQK_BB_REG_NUM 9
+#define HP_THERMAL_NUM 8
+
+#define AVG_THERMAL_NUM 8
+#define IQK_Matrix_REG_NUM 8
+#define IQK_Matrix_Settings_NUM 1+24+21
+
+#define DM_Type_ByFWi 0
+#define DM_Type_ByDriver 1
+
+/* Declare for common info */
+
+struct odm_phy_status_info {
+ u8 RxPWDBAll;
+ u8 SignalQuality; /* in 0-100 index. */
+ u8 RxMIMOSignalQuality[MAX_PATH_NUM_92CS]; /* EVM */
+ u8 RxMIMOSignalStrength[MAX_PATH_NUM_92CS];/* in 0~100 index */
+ s8 RxPower; /* in dBm Translate from PWdB */
+ s8 RecvSignalPower;/* Real power in dBm for this packet, no
+ * beautification and aggregation. Keep this raw
+ * info to be used for the other procedures. */
+ u8 BTRxRSSIPercentage;
+ u8 SignalStrength; /* in 0-100 index. */
+ u8 RxPwr[MAX_PATH_NUM_92CS];/* per-path's pwdb */
+ u8 RxSNR[MAX_PATH_NUM_92CS];/* per-path's SNR */
+};
+
+struct odm_phy_dbg_info {
+ /* ODM Write,debug info */
+ s8 RxSNRdB[MAX_PATH_NUM_92CS];
+ u64 NumQryPhyStatus;
+ u64 NumQryPhyStatusCCK;
+ u64 NumQryPhyStatusOFDM;
+ /* Others */
+ s32 RxEVM[MAX_PATH_NUM_92CS];
+};
+
+struct odm_per_pkt_info {
+ s8 Rate;
+ u8 StationID;
+ bool bPacketMatchBSSID;
+ bool bPacketToSelf;
+ bool bPacketBeacon;
+};
+
+struct odm_mac_status_info {
+ u8 test;
+};
+
+enum odm_ability {
+ /* BB Team */
+ ODM_DIG = 0x00000001,
+ ODM_HIGH_POWER = 0x00000002,
+ ODM_CCK_CCA_TH = 0x00000004,
+ ODM_FA_STATISTICS = 0x00000008,
+ ODM_RAMASK = 0x00000010,
+ ODM_RSSI_MONITOR = 0x00000020,
+ ODM_SW_ANTDIV = 0x00000040,
+ ODM_HW_ANTDIV = 0x00000080,
+ ODM_BB_PWRSV = 0x00000100,
+ ODM_2TPATHDIV = 0x00000200,
+ ODM_1TPATHDIV = 0x00000400,
+ ODM_PSD2AFH = 0x00000800
+};
+
+/* 2011/20/20 MH For MP driver RT_WLAN_STA = struct sta_info */
+/* Please declare below ODM relative info in your STA info structure. */
+
+struct odm_sta_info {
+ /* Driver Write */
+ bool bUsed; /* record the sta status link or not? */
+ u8 IOTPeer; /* Enum value. HT_IOT_PEER_E */
+
+ /* ODM Write */
+ /* 1 PHY_STATUS_INFO */
+ u8 RSSI_Path[4]; /* */
+ u8 RSSI_Ave;
+ u8 RXEVM[4];
+ u8 RXSNR[4];
+};
+
+/* 2011/10/20 MH Define Common info enum for all team. */
+
+enum odm_common_info_def {
+ /* Fixed value: */
+
+ /* HOOK BEFORE REG INIT----------- */
+ ODM_CMNINFO_PLATFORM = 0,
+ ODM_CMNINFO_ABILITY, /* ODM_ABILITY_E */
+ ODM_CMNINFO_INTERFACE, /* ODM_INTERFACE_E */
+ ODM_CMNINFO_MP_TEST_CHIP,
+ ODM_CMNINFO_IC_TYPE, /* ODM_IC_TYPE_E */
+ ODM_CMNINFO_CUT_VER, /* ODM_CUT_VERSION_E */
+ ODM_CMNINFO_FAB_VER, /* ODM_FAB_E */
+ ODM_CMNINFO_RF_TYPE, /* ODM_RF_PATH_E or ODM_RF_TYPE_E? */
+ ODM_CMNINFO_BOARD_TYPE, /* ODM_BOARD_TYPE_E */
+ ODM_CMNINFO_EXT_LNA, /* true */
+ ODM_CMNINFO_EXT_PA,
+ ODM_CMNINFO_EXT_TRSW,
+ ODM_CMNINFO_PATCH_ID, /* CUSTOMER ID */
+ ODM_CMNINFO_BINHCT_TEST,
+ ODM_CMNINFO_BWIFI_TEST,
+ ODM_CMNINFO_SMART_CONCURRENT,
+ /* HOOK BEFORE REG INIT----------- */
+
+ /* Dynamic value: */
+/* POINTER REFERENCE----------- */
+ ODM_CMNINFO_MAC_PHY_MODE, /* ODM_MAC_PHY_MODE_E */
+ ODM_CMNINFO_TX_UNI,
+ ODM_CMNINFO_RX_UNI,
+ ODM_CMNINFO_WM_MODE, /* ODM_WIRELESS_MODE_E */
+ ODM_CMNINFO_BAND, /* ODM_BAND_TYPE_E */
+ ODM_CMNINFO_SEC_CHNL_OFFSET, /* ODM_SEC_CHNL_OFFSET_E */
+ ODM_CMNINFO_SEC_MODE, /* ODM_SECURITY_E */
+ ODM_CMNINFO_BW, /* ODM_BW_E */
+ ODM_CMNINFO_CHNL,
+
+ ODM_CMNINFO_DMSP_GET_VALUE,
+ ODM_CMNINFO_BUDDY_ADAPTOR,
+ ODM_CMNINFO_DMSP_IS_MASTER,
+ ODM_CMNINFO_SCAN,
+ ODM_CMNINFO_POWER_SAVING,
+ ODM_CMNINFO_ONE_PATH_CCA, /* ODM_CCA_PATH_E */
+ ODM_CMNINFO_DRV_STOP,
+ ODM_CMNINFO_PNP_IN,
+ ODM_CMNINFO_INIT_ON,
+ ODM_CMNINFO_ANT_TEST,
+ ODM_CMNINFO_NET_CLOSED,
+ ODM_CMNINFO_MP_MODE,
+/* POINTER REFERENCE----------- */
+
+/* CALL BY VALUE------------- */
+ ODM_CMNINFO_WIFI_DIRECT,
+ ODM_CMNINFO_WIFI_DISPLAY,
+ ODM_CMNINFO_LINK,
+ ODM_CMNINFO_RSSI_MIN,
+ ODM_CMNINFO_DBG_COMP, /* u64 */
+ ODM_CMNINFO_DBG_LEVEL, /* u32 */
+ ODM_CMNINFO_RA_THRESHOLD_HIGH, /* u8 */
+ ODM_CMNINFO_RA_THRESHOLD_LOW, /* u8 */
+ ODM_CMNINFO_RF_ANTENNA_TYPE, /* u8 */
+ ODM_CMNINFO_BT_DISABLED,
+ ODM_CMNINFO_BT_OPERATION,
+ ODM_CMNINFO_BT_DIG,
+ ODM_CMNINFO_BT_BUSY, /* Check Bt is using or not */
+ ODM_CMNINFO_BT_DISABLE_EDCA,
+/* CALL BY VALUE-------------*/
+
+ /* Dynamic ptr array hook itms. */
+ ODM_CMNINFO_STA_STATUS,
+ ODM_CMNINFO_PHY_STATUS,
+ ODM_CMNINFO_MAC_STATUS,
+ ODM_CMNINFO_MAX,
+};
+
+/* 2011/10/20 MH Define ODM support ability. ODM_CMNINFO_ABILITY */
+
+enum odm_ability_def {
+ /* BB ODM section BIT 0-15 */
+ ODM_BB_DIG = BIT0,
+ ODM_BB_RA_MASK = BIT1,
+ ODM_BB_DYNAMIC_TXPWR = BIT2,
+ ODM_BB_FA_CNT = BIT3,
+ ODM_BB_RSSI_MONITOR = BIT4,
+ ODM_BB_CCK_PD = BIT5,
+ ODM_BB_ANT_DIV = BIT6,
+ ODM_BB_PWR_SAVE = BIT7,
+ ODM_BB_PWR_TRA = BIT8,
+ ODM_BB_RATE_ADAPTIVE = BIT9,
+ ODM_BB_PATH_DIV = BIT10,
+ ODM_BB_PSD = BIT11,
+ ODM_BB_RXHP = BIT12,
+
+ /* MAC DM section BIT 16-23 */
+ ODM_MAC_EDCA_TURBO = BIT16,
+ ODM_MAC_EARLY_MODE = BIT17,
+
+ /* RF ODM section BIT 24-31 */
+ ODM_RF_TX_PWR_TRACK = BIT24,
+ ODM_RF_RX_GAIN_TRACK = BIT25,
+ ODM_RF_CALIBRATION = BIT26,
+};
+
+#define ODM_RTL8188E BIT4
+
+/* ODM_CMNINFO_CUT_VER */
+enum odm_cut_version {
+ ODM_CUT_A = 1,
+ ODM_CUT_B = 2,
+ ODM_CUT_C = 3,
+ ODM_CUT_D = 4,
+ ODM_CUT_E = 5,
+ ODM_CUT_F = 6,
+ ODM_CUT_TEST = 7,
+};
+
+/* ODM_CMNINFO_FAB_VER */
+enum odm_fab_Version {
+ ODM_TSMC = 0,
+ ODM_UMC = 1,
+};
+
+/* ODM_CMNINFO_RF_TYPE */
+/* For example 1T2R (A+AB = BIT0|BIT4|BIT5) */
+enum odm_rf_path {
+ ODM_RF_TX_A = BIT0,
+ ODM_RF_TX_B = BIT1,
+ ODM_RF_TX_C = BIT2,
+ ODM_RF_TX_D = BIT3,
+ ODM_RF_RX_A = BIT4,
+ ODM_RF_RX_B = BIT5,
+ ODM_RF_RX_C = BIT6,
+ ODM_RF_RX_D = BIT7,
+};
+
+enum odm_rf_type {
+ ODM_1T1R = 0,
+ ODM_1T2R = 1,
+ ODM_2T2R = 2,
+ ODM_2T3R = 3,
+ ODM_2T4R = 4,
+ ODM_3T3R = 5,
+ ODM_3T4R = 6,
+ ODM_4T4R = 7,
+};
+
+/* ODM Dynamic common info value definition */
+
+enum odm_mac_phy_mode {
+ ODM_SMSP = 0,
+ ODM_DMSP = 1,
+ ODM_DMDP = 2,
+};
+
+enum odm_bt_coexist {
+ ODM_BT_BUSY = 1,
+ ODM_BT_ON = 2,
+ ODM_BT_OFF = 3,
+ ODM_BT_NONE = 4,
+};
+
+/* ODM_CMNINFO_OP_MODE */
+enum odm_operation_mode {
+ ODM_NO_LINK = BIT0,
+ ODM_LINK = BIT1,
+ ODM_SCAN = BIT2,
+ ODM_POWERSAVE = BIT3,
+ ODM_AP_MODE = BIT4,
+ ODM_CLIENT_MODE = BIT5,
+ ODM_AD_HOC = BIT6,
+ ODM_WIFI_DIRECT = BIT7,
+ ODM_WIFI_DISPLAY = BIT8,
+};
+
+/* ODM_CMNINFO_WM_MODE */
+enum odm_wireless_mode {
+ ODM_WM_UNKNOW = 0x0,
+ ODM_WM_B = BIT0,
+ ODM_WM_G = BIT1,
+ ODM_WM_A = BIT2,
+ ODM_WM_N24G = BIT3,
+ ODM_WM_N5G = BIT4,
+ ODM_WM_AUTO = BIT5,
+ ODM_WM_AC = BIT6,
+};
+
+/* ODM_CMNINFO_BAND */
+enum odm_band_type {
+ ODM_BAND_2_4G = BIT0,
+ ODM_BAND_5G = BIT1,
+};
+
+/* ODM_CMNINFO_SEC_CHNL_OFFSET */
+enum odm_sec_chnl_offset {
+ ODM_DONT_CARE = 0,
+ ODM_BELOW = 1,
+ ODM_ABOVE = 2
+};
+
+/* ODM_CMNINFO_SEC_MODE */
+enum odm_security {
+ ODM_SEC_OPEN = 0,
+ ODM_SEC_WEP40 = 1,
+ ODM_SEC_TKIP = 2,
+ ODM_SEC_RESERVE = 3,
+ ODM_SEC_AESCCMP = 4,
+ ODM_SEC_WEP104 = 5,
+ ODM_WEP_WPA_MIXED = 6, /* WEP + WPA */
+ ODM_SEC_SMS4 = 7,
+};
+
+/* ODM_CMNINFO_BW */
+enum odm_bw {
+ ODM_BW20M = 0,
+ ODM_BW40M = 1,
+ ODM_BW80M = 2,
+ ODM_BW160M = 3,
+ ODM_BW10M = 4,
+};
+
+/* ODM_CMNINFO_BOARD_TYPE */
+enum odm_board_type {
+ ODM_BOARD_NORMAL = 0,
+ ODM_BOARD_HIGHPWR = 1,
+ ODM_BOARD_MINICARD = 2,
+ ODM_BOARD_SLIM = 3,
+ ODM_BOARD_COMBO = 4,
+};
+
+/* ODM_CMNINFO_ONE_PATH_CCA */
+enum odm_cca_path {
+ ODM_CCA_2R = 0,
+ ODM_CCA_1R_A = 1,
+ ODM_CCA_1R_B = 2,
+};
+
+struct odm_ra_info {
+ u8 RateID;
+ u32 RateMask;
+ u32 RAUseRate;
+ u8 RateSGI;
+ u8 RssiStaRA;
+ u8 PreRssiStaRA;
+ u8 SGIEnable;
+ u8 DecisionRate;
+ u8 PreRate;
+ u8 HighestRate;
+ u8 LowestRate;
+ u32 NscUp;
+ u32 NscDown;
+ u16 RTY[5];
+ u32 TOTAL;
+ u16 DROP;
+ u8 Active;
+ u16 RptTime;
+ u8 RAWaitingCounter;
+ u8 RAPendingCounter;
+ u8 PTActive; /* on or off */
+ u8 PTTryState; /* 0 trying state, 1 for decision state */
+ u8 PTStage; /* 0~6 */
+ u8 PTStopCount; /* Stop PT counter */
+ u8 PTPreRate; /* if rate change do PT */
+ u8 PTPreRssi; /* if RSSI change 5% do PT */
+ u8 PTModeSS; /* decide whitch rate should do PT */
+ u8 RAstage; /* StageRA, decide how many times RA will be done
+ * between PT */
+ u8 PTSmoothFactor;
+};
+
+struct ijk_matrix_regs_set {
+ bool bIQKDone;
+ s32 Value[1][IQK_Matrix_REG_NUM];
+};
+
+struct odm_rf_cal {
+ /* for tx power tracking */
+ u32 RegA24; /* for TempCCK */
+ s32 RegE94;
+ s32 RegE9C;
+ s32 RegEB4;
+ s32 RegEBC;
+
+ u8 TXPowercount;
+ bool bTXPowerTrackingInit;
+ bool bTXPowerTracking;
+ u8 TxPowerTrackControl; /* for mp mode, turn off txpwrtracking
+ * as default */
+ u8 TM_Trigger;
+ u8 InternalPA5G[2]; /* pathA / pathB */
+
+ u8 ThermalMeter[2]; /* ThermalMeter, index 0 for RFIC0,
+ * and 1 for RFIC1 */
+ u8 ThermalValue;
+ u8 ThermalValue_LCK;
+ u8 ThermalValue_IQK;
+ u8 ThermalValue_DPK;
+ u8 ThermalValue_AVG[AVG_THERMAL_NUM];
+ u8 ThermalValue_AVG_index;
+ u8 ThermalValue_RxGain;
+ u8 ThermalValue_Crystal;
+ u8 ThermalValue_DPKstore;
+ u8 ThermalValue_DPKtrack;
+ bool TxPowerTrackingInProgress;
+ bool bDPKenable;
+
+ bool bReloadtxpowerindex;
+ u8 bRfPiEnable;
+ u32 TXPowerTrackingCallbackCnt; /* cosa add for debug */
+
+ u8 bCCKinCH14;
+ u8 CCK_index;
+ u8 OFDM_index[2];
+ bool bDoneTxpower;
+
+ u8 ThermalValue_HP[HP_THERMAL_NUM];
+ u8 ThermalValue_HP_index;
+ struct ijk_matrix_regs_set IQKMatrixRegSetting[IQK_Matrix_Settings_NUM];
+
+ u8 Delta_IQK;
+ u8 Delta_LCK;
+
+ /* for IQK */
+ u32 RegC04;
+ u32 Reg874;
+ u32 RegC08;
+ u32 RegB68;
+ u32 RegB6C;
+ u32 Reg870;
+ u32 Reg860;
+ u32 Reg864;
+
+ bool bIQKInitialized;
+ bool bLCKInProgress;
+ bool bAntennaDetected;
+ u32 ADDA_backup[IQK_ADDA_REG_NUM];
+ u32 IQK_MAC_backup[IQK_MAC_REG_NUM];
+ u32 IQK_BB_backup_recover[9];
+ u32 IQK_BB_backup[IQK_BB_REG_NUM];
+
+ /* for APK */
+ u32 APKoutput[2][2]; /* path A/B; output1_1a/output1_2a */
+ u8 bAPKdone;
+ u8 bAPKThermalMeterIgnore;
+ u8 bDPdone;
+ u8 bDPPathAOK;
+ u8 bDPPathBOK;
+};
+
+/* ODM Dynamic common info value definition */
+
+struct fast_ant_train {
+ u8 Bssid[6];
+ u8 antsel_rx_keep_0;
+ u8 antsel_rx_keep_1;
+ u8 antsel_rx_keep_2;
+ u32 antSumRSSI[7];
+ u32 antRSSIcnt[7];
+ u32 antAveRSSI[7];
+ u8 FAT_State;
+ u32 TrainIdx;
+ u8 antsel_a[ODM_ASSOCIATE_ENTRY_NUM];
+ u8 antsel_b[ODM_ASSOCIATE_ENTRY_NUM];
+ u8 antsel_c[ODM_ASSOCIATE_ENTRY_NUM];
+ u32 MainAnt_Sum[ODM_ASSOCIATE_ENTRY_NUM];
+ u32 AuxAnt_Sum[ODM_ASSOCIATE_ENTRY_NUM];
+ u32 MainAnt_Cnt[ODM_ASSOCIATE_ENTRY_NUM];
+ u32 AuxAnt_Cnt[ODM_ASSOCIATE_ENTRY_NUM];
+ u8 RxIdleAnt;
+ bool bBecomeLinked;
+};
+
+enum fat_state {
+ FAT_NORMAL_STATE = 0,
+ FAT_TRAINING_STATE = 1,
+};
+
+enum ant_div_type {
+ NO_ANTDIV = 0xFF,
+ CG_TRX_HW_ANTDIV = 0x01,
+ CGCS_RX_HW_ANTDIV = 0x02,
+ FIXED_HW_ANTDIV = 0x03,
+ CG_TRX_SMART_ANTDIV = 0x04,
+ CGCS_RX_SW_ANTDIV = 0x05,
+};
+
+/* Copy from SD4 defined structure. We use to support PHY DM integration. */
+struct odm_dm_struct {
+ /* Add for different team use temporarily */
+ struct adapter *Adapter; /* For CE/NIC team */
+ struct rtl8192cd_priv *priv; /* For AP/ADSL team */
+ /* WHen you use above pointers, they must be initialized. */
+ bool odm_ready;
+
+ struct rtl8192cd_priv *fake_priv;
+ u64 DebugComponents;
+ u32 DebugLevel;
+
+/* ODM HANDLE, DRIVER NEEDS NOT TO HOOK------ */
+ bool bCckHighPower;
+ u8 RFPathRxEnable; /* ODM_CMNINFO_RFPATH_ENABLE */
+ u8 ControlChannel;
+/* ODM HANDLE, DRIVER NEEDS NOT TO HOOK------ */
+
+/* 1 COMMON INFORMATION */
+ /* Init Value */
+/* HOOK BEFORE REG INIT----------- */
+ /* ODM Platform info AP/ADSL/CE/MP = 1/2/3/4 */
+ u8 SupportPlatform;
+ /* ODM Support Ability DIG/RATR/TX_PWR_TRACK/ ¡K¡K = 1/2/3/¡K */
+ u32 SupportAbility;
+ /* ODM PCIE/USB/SDIO/GSPI = 0/1/2/3 */
+ u8 SupportInterface;
+ /* ODM composite or independent. Bit oriented/ 92C+92D+ .... or any
+ * other type = 1/2/3/... */
+ u32 SupportICType;
+ /* Cut Version TestChip/A-cut/B-cut... = 0/1/2/3/... */
+ u8 CutVersion;
+ /* Fab Version TSMC/UMC = 0/1 */
+ u8 FabVersion;
+ /* RF Type 4T4R/3T3R/2T2R/1T2R/1T1R/... */
+ u8 RFType;
+ /* Board Type Normal/HighPower/MiniCard/SLIM/Combo/. = 0/1/2/3/4/. */
+ u8 BoardType;
+ /* with external LNA NO/Yes = 0/1 */
+ u8 ExtLNA;
+ /* with external PA NO/Yes = 0/1 */
+ u8 ExtPA;
+ /* with external TRSW NO/Yes = 0/1 */
+ u8 ExtTRSW;
+ u8 PatchID; /* Customer ID */
+ bool bInHctTest;
+ bool bWIFITest;
+
+ bool bDualMacSmartConcurrent;
+ u32 BK_SupportAbility;
+ u8 AntDivType;
+/* HOOK BEFORE REG INIT----------- */
+
+ /* Dynamic Value */
+/* POINTER REFERENCE----------- */
+
+ u8 u8_temp;
+ bool bool_temp;
+ struct adapter *adapter_temp;
+
+ /* MAC PHY Mode SMSP/DMSP/DMDP = 0/1/2 */
+ u8 *pMacPhyMode;
+ /* TX Unicast byte count */
+ u64 *pNumTxBytesUnicast;
+ /* RX Unicast byte count */
+ u64 *pNumRxBytesUnicast;
+ /* Wireless mode B/G/A/N = BIT0/BIT1/BIT2/BIT3 */
+ u8 *pWirelessMode; /* ODM_WIRELESS_MODE_E */
+ /* Frequence band 2.4G/5G = 0/1 */
+ u8 *pBandType;
+ /* Secondary channel offset don't_care/below/above = 0/1/2 */
+ u8 *pSecChOffset;
+ /* Security mode Open/WEP/AES/TKIP = 0/1/2/3 */
+ u8 *pSecurity;
+ /* BW info 20M/40M/80M = 0/1/2 */
+ u8 *pBandWidth;
+ /* Central channel location Ch1/Ch2/.... */
+ u8 *pChannel; /* central channel number */
+ /* Common info for 92D DMSP */
+
+ bool *pbGetValueFromOtherMac;
+ struct adapter **pBuddyAdapter;
+ bool *pbMasterOfDMSP; /* MAC0: master, MAC1: slave */
+ /* Common info for Status */
+ bool *pbScanInProcess;
+ bool *pbPowerSaving;
+ /* CCA Path 2-path/path-A/path-B = 0/1/2; using ODM_CCA_PATH_E. */
+ u8 *pOnePathCCA;
+ /* pMgntInfo->AntennaTest */
+ u8 *pAntennaTest;
+ bool *pbNet_closed;
+/* POINTER REFERENCE----------- */
+ /* */
+/* CALL BY VALUE------------- */
+ bool bWIFI_Direct;
+ bool bWIFI_Display;
+ bool bLinked;
+ u8 RSSI_Min;
+ u8 InterfaceIndex; /* Add for 92D dual MAC: 0--Mac0 1--Mac1 */
+ bool bIsMPChip;
+ bool bOneEntryOnly;
+ /* Common info for BTDM */
+ bool bBtDisabled; /* BT is disabled */
+ bool bBtHsOperation; /* BT HS mode is under progress */
+ u8 btHsDigVal; /* use BT rssi to decide the DIG value */
+ bool bBtDisableEdcaTurbo;/* Under some condition, don't enable the
+ * EDCA Turbo */
+ bool bBtBusy; /* BT is busy. */
+/* CALL BY VALUE------------- */
+
+ /* 2 Define STA info. */
+ /* _ODM_STA_INFO */
+ /* For MP, we need to reduce one array pointer for default port.?? */
+ struct sta_info *pODM_StaInfo[ODM_ASSOCIATE_ENTRY_NUM];
+
+ u16 CurrminRptTime;
+ struct odm_ra_info RAInfo[ODM_ASSOCIATE_ENTRY_NUM]; /* Use MacID as
+ * array index. STA MacID=0,
+ * VWiFi Client MacID={1, ODM_ASSOCIATE_ENTRY_NUM-1} */
+ /* */
+ /* 2012/02/14 MH Add to share 88E ra with other SW team. */
+ /* We need to colelct all support abilit to a proper area. */
+ /* */
+ bool RaSupport88E;
+
+ /* Define ........... */
+
+ /* Latest packet phy info (ODM write) */
+ struct odm_phy_dbg_info PhyDbgInfo;
+
+ /* Latest packet phy info (ODM write) */
+ struct odm_mac_status_info *pMacInfo;
+
+ /* Different Team independt structure?? */
+
+ /* ODM Structure */
+ struct fast_ant_train DM_FatTable;
+ struct rtw_dig DM_DigTable;
+ struct rtl_ps DM_PSTable;
+ struct rx_hpc DM_RXHP_Table;
+ struct false_alarm_stats FalseAlmCnt;
+ struct false_alarm_stats FlaseAlmCntBuddyAdapter;
+ struct sw_ant_switch DM_SWAT_Table;
+ bool RSSI_test;
+
+ struct edca_turbo DM_EDCA_Table;
+ u32 WMMEDCA_BE;
+ /* Copy from SD4 structure */
+ /* */
+ /* ================================================== */
+ /* */
+
+ bool *pbDriverStopped;
+ bool *pbDriverIsGoingToPnpSetPowerSleep;
+ bool *pinit_adpt_in_progress;
+
+ /* PSD */
+ bool bUserAssignLevel;
+ struct timer_list PSDTimer;
+ u8 RSSI_BT; /* come from BT */
+ bool bPSDinProcess;
+ bool bDMInitialGainEnable;
+
+ /* for rate adaptive, in fact, 88c/92c fw will handle this */
+ u8 bUseRAMask;
+
+ struct odm_rate_adapt RateAdaptive;
+
+ struct odm_rf_cal RFCalibrateInfo;
+
+ /* TX power tracking */
+ u8 BbSwingIdxOfdm;
+ u8 BbSwingIdxOfdmCurrent;
+ u8 BbSwingIdxOfdmBase;
+ bool BbSwingFlagOfdm;
+ u8 BbSwingIdxCck;
+ u8 BbSwingIdxCckCurrent;
+ u8 BbSwingIdxCckBase;
+ bool BbSwingFlagCck;
+ u8 *mp_mode;
+ /* ODM system resource. */
+
+ /* ODM relative time. */
+ struct timer_list PathDivSwitchTimer;
+ /* 2011.09.27 add for Path Diversity */
+ struct timer_list CCKPathDiversityTimer;
+ struct timer_list FastAntTrainingTimer;
+}; /* DM_Dynamic_Mechanism_Structure */
+
+#define ODM_RF_PATH_MAX 3
+
+enum ODM_RF_CONTENT {
+ odm_radioa_txt = 0x1000,
+ odm_radiob_txt = 0x1001,
+ odm_radioc_txt = 0x1002,
+ odm_radiod_txt = 0x1003
+};
+
+enum odm_bb_config_type {
+ CONFIG_BB_PHY_REG,
+ CONFIG_BB_AGC_TAB,
+ CONFIG_BB_AGC_TAB_2G,
+ CONFIG_BB_AGC_TAB_5G,
+ CONFIG_BB_PHY_REG_PG,
+};
+
+/* Status code */
+enum rt_status {
+ RT_STATUS_SUCCESS,
+ RT_STATUS_FAILURE,
+ RT_STATUS_PENDING,
+ RT_STATUS_RESOURCE,
+ RT_STATUS_INVALID_CONTEXT,
+ RT_STATUS_INVALID_PARAMETER,
+ RT_STATUS_NOT_SUPPORT,
+ RT_STATUS_OS_API_FAILED,
+};
+
+/* 3=========================================================== */
+/* 3 DIG */
+/* 3=========================================================== */
+
+enum dm_dig_op {
+ RT_TYPE_THRESH_HIGH = 0,
+ RT_TYPE_THRESH_LOW = 1,
+ RT_TYPE_BACKOFF = 2,
+ RT_TYPE_RX_GAIN_MIN = 3,
+ RT_TYPE_RX_GAIN_MAX = 4,
+ RT_TYPE_ENABLE = 5,
+ RT_TYPE_DISABLE = 6,
+ DIG_OP_TYPE_MAX
+};
+
+#define DM_DIG_THRESH_HIGH 40
+#define DM_DIG_THRESH_LOW 35
+
+#define DM_SCAN_RSSI_TH 0x14 /* scan return issue for LC */
+
+
+#define DM_false_ALARM_THRESH_LOW 400
+#define DM_false_ALARM_THRESH_HIGH 1000
+
+#define DM_DIG_MAX_NIC 0x4e
+#define DM_DIG_MIN_NIC 0x1e /* 0x22/0x1c */
+
+#define DM_DIG_MAX_AP 0x32
+#define DM_DIG_MIN_AP 0x20
+
+#define DM_DIG_MAX_NIC_HP 0x46
+#define DM_DIG_MIN_NIC_HP 0x2e
+
+#define DM_DIG_MAX_AP_HP 0x42
+#define DM_DIG_MIN_AP_HP 0x30
+
+/* vivi 92c&92d has different definition, 20110504 */
+/* this is for 92c */
+#define DM_DIG_FA_TH0 0x200/* 0x20 */
+#define DM_DIG_FA_TH1 0x300/* 0x100 */
+#define DM_DIG_FA_TH2 0x400/* 0x200 */
+/* this is for 92d */
+#define DM_DIG_FA_TH0_92D 0x100
+#define DM_DIG_FA_TH1_92D 0x400
+#define DM_DIG_FA_TH2_92D 0x600
+
+#define DM_DIG_BACKOFF_MAX 12
+#define DM_DIG_BACKOFF_MIN -4
+#define DM_DIG_BACKOFF_DEFAULT 10
+
+/* 3=========================================================== */
+/* 3 AGC RX High Power Mode */
+/* 3=========================================================== */
+#define LNA_Low_Gain_1 0x64
+#define LNA_Low_Gain_2 0x5A
+#define LNA_Low_Gain_3 0x58
+
+#define FA_RXHP_TH1 5000
+#define FA_RXHP_TH2 1500
+#define FA_RXHP_TH3 800
+#define FA_RXHP_TH4 600
+#define FA_RXHP_TH5 500
+
+/* 3=========================================================== */
+/* 3 EDCA */
+/* 3=========================================================== */
+
+/* 3=========================================================== */
+/* 3 Dynamic Tx Power */
+/* 3=========================================================== */
+/* Dynamic Tx Power Control Threshold */
+#define TX_POWER_NEAR_FIELD_THRESH_LVL2 74
+#define TX_POWER_NEAR_FIELD_THRESH_LVL1 67
+#define TX_POWER_NEAR_FIELD_THRESH_AP 0x3F
+
+#define TxHighPwrLevel_Normal 0
+#define TxHighPwrLevel_Level1 1
+#define TxHighPwrLevel_Level2 2
+#define TxHighPwrLevel_BT1 3
+#define TxHighPwrLevel_BT2 4
+#define TxHighPwrLevel_15 5
+#define TxHighPwrLevel_35 6
+#define TxHighPwrLevel_50 7
+#define TxHighPwrLevel_70 8
+#define TxHighPwrLevel_100 9
+
+/* 3=========================================================== */
+/* 3 Rate Adaptive */
+/* 3=========================================================== */
+#define DM_RATR_STA_INIT 0
+#define DM_RATR_STA_HIGH 1
+#define DM_RATR_STA_MIDDLE 2
+#define DM_RATR_STA_LOW 3
+
+/* 3=========================================================== */
+/* 3 BB Power Save */
+/* 3=========================================================== */
+
+
+enum dm_1r_cca {
+ CCA_1R = 0,
+ CCA_2R = 1,
+ CCA_MAX = 2,
+};
+
+enum dm_rf {
+ RF_Save = 0,
+ RF_Normal = 1,
+ RF_MAX = 2,
+};
+
+/* 3=========================================================== */
+/* 3 Antenna Diversity */
+/* 3=========================================================== */
+enum dm_swas {
+ Antenna_A = 1,
+ Antenna_B = 2,
+ Antenna_MAX = 3,
+};
+
+/* Maximal number of antenna detection mechanism needs to perform. */
+#define MAX_ANTENNA_DETECTION_CNT 10
+
+/* Extern Global Variables. */
+#define OFDM_TABLE_SIZE_92C 37
+#define OFDM_TABLE_SIZE_92D 43
+#define CCK_TABLE_SIZE 33
+
+extern u32 OFDMSwingTable[OFDM_TABLE_SIZE_92D];
+extern u8 CCKSwingTable_Ch1_Ch13[CCK_TABLE_SIZE][8];
+extern u8 CCKSwingTable_Ch14 [CCK_TABLE_SIZE][8];
+
+/* check Sta pointer valid or not */
+#define IS_STA_VALID(pSta) (pSta)
+/* 20100514 Joseph: Add definition for antenna switching test after link. */
+/* This indicates two different the steps. */
+/* In SWAW_STEP_PEAK, driver needs to switch antenna and listen to the
+ * signal on the air. */
+/* In SWAW_STEP_DETERMINE, driver just compares the signal captured in
+ * SWAW_STEP_PEAK */
+/* with original RSSI to determine if it is necessary to switch antenna. */
+#define SWAW_STEP_PEAK 0
+#define SWAW_STEP_DETERMINE 1
+
+#define dm_CheckTXPowerTracking ODM_TXPowerTrackingCheck
+#define dm_RF_Saving ODM_RF_Saving
+
+void ODM_RF_Saving(struct odm_dm_struct *pDM_Odm, u8 bForceInNormal);
+void ODM_TXPowerTrackingCheck(struct odm_dm_struct *pDM_Odm);
+void odm_DIGbyRSSI_LPS(struct odm_dm_struct *pDM_Odm);
+void ODM_Write_CCK_CCA_Thres(struct odm_dm_struct *pDM_Odm, u8 CurCCK_CCAThres);
+bool ODM_RAStateCheck(struct odm_dm_struct *pDM_Odm, s32 RSSI,
+ bool bForceUpdate, u8 *pRATRState);
+u32 ConvertTo_dB(u32 Value);
+u32 ODM_Get_Rate_Bitmap(struct odm_dm_struct *pDM_Odm, u32 macid,
+ u32 ra_mask, u8 rssi_level);
+void ODM_CmnInfoInit(struct odm_dm_struct *pDM_Odm,
+ enum odm_common_info_def CmnInfo, u32 Value);
+void ODM_CmnInfoUpdate(struct odm_dm_struct *pDM_Odm, u32 CmnInfo, u64 Value);
+void ODM_CmnInfoHook(struct odm_dm_struct *pDM_Odm,
+ enum odm_common_info_def CmnInfo, void *pValue);
+void ODM_CmnInfoPtrArrayHook(struct odm_dm_struct *pDM_Odm,
+ enum odm_common_info_def CmnInfo,
+ u16 Index, void *pValue);
+void ODM_DMInit(struct odm_dm_struct *pDM_Odm);
+void ODM_DMWatchdog(struct odm_dm_struct *pDM_Odm);
+void ODM_Write_DIG(struct odm_dm_struct *pDM_Odm, u8 CurrentIGI);
+
+#endif
diff --git a/drivers/staging/rtl8188eu/include/odm_HWConfig.h b/drivers/staging/rtl8188eu/include/odm_HWConfig.h
new file mode 100644
index 000000000..1de4e6399
--- /dev/null
+++ b/drivers/staging/rtl8188eu/include/odm_HWConfig.h
@@ -0,0 +1,126 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2011 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ *
+ ******************************************************************************/
+
+#ifndef __HALHWOUTSRC_H__
+#define __HALHWOUTSRC_H__
+
+/* Definition */
+/* CCK Rates, TxHT = 0 */
+#define DESC92C_RATE1M 0x00
+#define DESC92C_RATE2M 0x01
+#define DESC92C_RATE5_5M 0x02
+#define DESC92C_RATE11M 0x03
+
+/* OFDM Rates, TxHT = 0 */
+#define DESC92C_RATE6M 0x04
+#define DESC92C_RATE9M 0x05
+#define DESC92C_RATE12M 0x06
+#define DESC92C_RATE18M 0x07
+#define DESC92C_RATE24M 0x08
+#define DESC92C_RATE36M 0x09
+#define DESC92C_RATE48M 0x0a
+#define DESC92C_RATE54M 0x0b
+
+/* MCS Rates, TxHT = 1 */
+#define DESC92C_RATEMCS0 0x0c
+#define DESC92C_RATEMCS1 0x0d
+#define DESC92C_RATEMCS2 0x0e
+#define DESC92C_RATEMCS3 0x0f
+#define DESC92C_RATEMCS4 0x10
+#define DESC92C_RATEMCS5 0x11
+#define DESC92C_RATEMCS6 0x12
+#define DESC92C_RATEMCS7 0x13
+#define DESC92C_RATEMCS8 0x14
+#define DESC92C_RATEMCS9 0x15
+#define DESC92C_RATEMCS10 0x16
+#define DESC92C_RATEMCS11 0x17
+#define DESC92C_RATEMCS12 0x18
+#define DESC92C_RATEMCS13 0x19
+#define DESC92C_RATEMCS14 0x1a
+#define DESC92C_RATEMCS15 0x1b
+#define DESC92C_RATEMCS15_SG 0x1c
+#define DESC92C_RATEMCS32 0x20
+
+/* structure and define */
+
+struct phy_rx_agc_info {
+ #ifdef __LITTLE_ENDIAN
+ u8 gain:7, trsw:1;
+ #else
+ u8 trsw:1, gain:7;
+ #endif
+};
+
+struct phy_status_rpt {
+ struct phy_rx_agc_info path_agc[3];
+ u8 ch_corr[2];
+ u8 cck_sig_qual_ofdm_pwdb_all;
+ u8 cck_agc_rpt_ofdm_cfosho_a;
+ u8 cck_rpt_b_ofdm_cfosho_b;
+ u8 rsvd_1;/* ch_corr_msb; */
+ u8 noise_power_db_msb;
+ u8 path_cfotail[2];
+ u8 pcts_mask[2];
+ s8 stream_rxevm[2];
+ u8 path_rxsnr[3];
+ u8 noise_power_db_lsb;
+ u8 rsvd_2[3];
+ u8 stream_csi[2];
+ u8 stream_target_csi[2];
+ s8 sig_evm;
+ u8 rsvd_3;
+
+#ifdef __LITTLE_ENDIAN
+ u8 antsel_rx_keep_2:1; /* ex_intf_flg:1; */
+ u8 sgi_en:1;
+ u8 rxsc:2;
+ u8 idle_long:1;
+ u8 r_ant_train_en:1;
+ u8 ant_sel_b:1;
+ u8 ant_sel:1;
+#else /* _BIG_ENDIAN_ */
+ u8 ant_sel:1;
+ u8 ant_sel_b:1;
+ u8 r_ant_train_en:1;
+ u8 idle_long:1;
+ u8 rxsc:2;
+ u8 sgi_en:1;
+ u8 antsel_rx_keep_2:1; /* ex_intf_flg:1; */
+#endif
+};
+
+void odm_Init_RSSIForDM(struct odm_dm_struct *pDM_Odm);
+
+void ODM_PhyStatusQuery(struct odm_dm_struct *pDM_Odm,
+ struct odm_phy_status_info *pPhyInfo,
+ u8 *pPhyStatus,
+ struct odm_per_pkt_info *pPktinfo);
+
+void ODM_MacStatusQuery(struct odm_dm_struct *pDM_Odm,
+ u8 *pMacStatus,
+ u8 MacID,
+ bool bPacketMatchBSSID,
+ bool bPacketToSelf,
+ bool bPacketBeacon);
+
+enum HAL_STATUS ODM_ConfigBBWithHeaderFile(struct odm_dm_struct *pDM_Odm,
+ enum odm_bb_config_type ConfigType);
+
+#endif
diff --git a/drivers/staging/rtl8188eu/include/odm_RTL8188E.h b/drivers/staging/rtl8188eu/include/odm_RTL8188E.h
new file mode 100644
index 000000000..14dce6c4b
--- /dev/null
+++ b/drivers/staging/rtl8188eu/include/odm_RTL8188E.h
@@ -0,0 +1,52 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2011 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ *
+ ******************************************************************************/
+#ifndef __ODM_RTL8188E_H__
+#define __ODM_RTL8188E_H__
+
+#define MAIN_ANT 0
+#define AUX_ANT 1
+#define MAIN_ANT_CG_TRX 1
+#define AUX_ANT_CG_TRX 0
+#define MAIN_ANT_CGCS_RX 0
+#define AUX_ANT_CGCS_RX 1
+
+void ODM_DIG_LowerBound_88E(struct odm_dm_struct *pDM_Odm);
+
+void rtl88eu_dm_antenna_div_init(struct odm_dm_struct *dm_odm);
+
+void rtl88eu_dm_antenna_diversity(struct odm_dm_struct *dm_odm);
+
+void rtl88eu_dm_set_tx_ant_by_tx_info(struct odm_dm_struct *dm_odm, u8 *desc,
+ u8 mac_id);
+
+void rtl88eu_dm_update_rx_idle_ant(struct odm_dm_struct *dm_odm, u8 ant);
+
+void rtl88eu_dm_ant_sel_statistics(struct odm_dm_struct *dm_odm, u8 antsel_tr_mux,
+ u32 mac_id, u8 rx_pwdb_all);
+
+void odm_FastAntTraining(struct odm_dm_struct *pDM_Odm);
+
+void odm_FastAntTrainingCallback(struct odm_dm_struct *pDM_Odm);
+
+void odm_FastAntTrainingWorkItemCallback(struct odm_dm_struct *pDM_Odm);
+
+bool ODM_DynamicPrimaryCCA_DupRTS(struct odm_dm_struct *pDM_Odm);
+
+#endif
diff --git a/drivers/staging/rtl8188eu/include/odm_RegDefine11N.h b/drivers/staging/rtl8188eu/include/odm_RegDefine11N.h
new file mode 100644
index 000000000..5a61f902b
--- /dev/null
+++ b/drivers/staging/rtl8188eu/include/odm_RegDefine11N.h
@@ -0,0 +1,171 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2011 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ *
+ ******************************************************************************/
+
+#ifndef __ODM_REGDEFINE11N_H__
+#define __ODM_REGDEFINE11N_H__
+
+
+/* 2 RF REG LIST */
+#define ODM_REG_RF_MODE_11N 0x00
+#define ODM_REG_RF_0B_11N 0x0B
+#define ODM_REG_CHNBW_11N 0x18
+#define ODM_REG_T_METER_11N 0x24
+#define ODM_REG_RF_25_11N 0x25
+#define ODM_REG_RF_26_11N 0x26
+#define ODM_REG_RF_27_11N 0x27
+#define ODM_REG_RF_2B_11N 0x2B
+#define ODM_REG_RF_2C_11N 0x2C
+#define ODM_REG_RXRF_A3_11N 0x3C
+#define ODM_REG_T_METER_92D_11N 0x42
+#define ODM_REG_T_METER_88E_11N 0x42
+
+
+
+/* 2 BB REG LIST */
+/* PAGE 8 */
+#define ODM_REG_BB_CTRL_11N 0x800
+#define ODM_REG_RF_PIN_11N 0x804
+#define ODM_REG_PSD_CTRL_11N 0x808
+#define ODM_REG_TX_ANT_CTRL_11N 0x80C
+#define ODM_REG_BB_PWR_SAV5_11N 0x818
+#define ODM_REG_CCK_RPT_FORMAT_11N 0x824
+#define ODM_REG_RX_DEFUALT_A_11N 0x858
+#define ODM_REG_RX_DEFUALT_B_11N 0x85A
+#define ODM_REG_BB_PWR_SAV3_11N 0x85C
+#define ODM_REG_ANTSEL_CTRL_11N 0x860
+#define ODM_REG_RX_ANT_CTRL_11N 0x864
+#define ODM_REG_PIN_CTRL_11N 0x870
+#define ODM_REG_BB_PWR_SAV1_11N 0x874
+#define ODM_REG_ANTSEL_PATH_11N 0x878
+#define ODM_REG_BB_3WIRE_11N 0x88C
+#define ODM_REG_SC_CNT_11N 0x8C4
+#define ODM_REG_PSD_DATA_11N 0x8B4
+/* PAGE 9 */
+#define ODM_REG_ANT_MAPPING1_11N 0x914
+#define ODM_REG_ANT_MAPPING2_11N 0x918
+/* PAGE A */
+#define ODM_REG_CCK_ANTDIV_PARA1_11N 0xA00
+#define ODM_REG_CCK_CCA_11N 0xA0A
+#define ODM_REG_CCK_ANTDIV_PARA2_11N 0xA0C
+#define ODM_REG_CCK_ANTDIV_PARA3_11N 0xA10
+#define ODM_REG_CCK_ANTDIV_PARA4_11N 0xA14
+#define ODM_REG_CCK_FILTER_PARA1_11N 0xA22
+#define ODM_REG_CCK_FILTER_PARA2_11N 0xA23
+#define ODM_REG_CCK_FILTER_PARA3_11N 0xA24
+#define ODM_REG_CCK_FILTER_PARA4_11N 0xA25
+#define ODM_REG_CCK_FILTER_PARA5_11N 0xA26
+#define ODM_REG_CCK_FILTER_PARA6_11N 0xA27
+#define ODM_REG_CCK_FILTER_PARA7_11N 0xA28
+#define ODM_REG_CCK_FILTER_PARA8_11N 0xA29
+#define ODM_REG_CCK_FA_RST_11N 0xA2C
+#define ODM_REG_CCK_FA_MSB_11N 0xA58
+#define ODM_REG_CCK_FA_LSB_11N 0xA5C
+#define ODM_REG_CCK_CCA_CNT_11N 0xA60
+#define ODM_REG_BB_PWR_SAV4_11N 0xA74
+/* PAGE B */
+#define ODM_REG_LNA_SWITCH_11N 0xB2C
+#define ODM_REG_PATH_SWITCH_11N 0xB30
+#define ODM_REG_RSSI_CTRL_11N 0xB38
+#define ODM_REG_CONFIG_ANTA_11N 0xB68
+#define ODM_REG_RSSI_BT_11N 0xB9C
+/* PAGE C */
+#define ODM_REG_OFDM_FA_HOLDC_11N 0xC00
+#define ODM_REG_RX_PATH_11N 0xC04
+#define ODM_REG_TRMUX_11N 0xC08
+#define ODM_REG_OFDM_FA_RSTC_11N 0xC0C
+#define ODM_REG_RXIQI_MATRIX_11N 0xC14
+#define ODM_REG_TXIQK_MATRIX_LSB1_11N 0xC4C
+#define ODM_REG_IGI_A_11N 0xC50
+#define ODM_REG_ANTDIV_PARA2_11N 0xC54
+#define ODM_REG_IGI_B_11N 0xC58
+#define ODM_REG_ANTDIV_PARA3_11N 0xC5C
+#define ODM_REG_BB_PWR_SAV2_11N 0xC70
+#define ODM_REG_RX_OFF_11N 0xC7C
+#define ODM_REG_TXIQK_MATRIXA_11N 0xC80
+#define ODM_REG_TXIQK_MATRIXB_11N 0xC88
+#define ODM_REG_TXIQK_MATRIXA_LSB2_11N 0xC94
+#define ODM_REG_TXIQK_MATRIXB_LSB2_11N 0xC9C
+#define ODM_REG_RXIQK_MATRIX_LSB_11N 0xCA0
+#define ODM_REG_ANTDIV_PARA1_11N 0xCA4
+#define ODM_REG_OFDM_FA_TYPE1_11N 0xCF0
+/* PAGE D */
+#define ODM_REG_OFDM_FA_RSTD_11N 0xD00
+#define ODM_REG_OFDM_FA_TYPE2_11N 0xDA0
+#define ODM_REG_OFDM_FA_TYPE3_11N 0xDA4
+#define ODM_REG_OFDM_FA_TYPE4_11N 0xDA8
+/* PAGE E */
+#define ODM_REG_TXAGC_A_6_18_11N 0xE00
+#define ODM_REG_TXAGC_A_24_54_11N 0xE04
+#define ODM_REG_TXAGC_A_1_MCS32_11N 0xE08
+#define ODM_REG_TXAGC_A_MCS0_3_11N 0xE10
+#define ODM_REG_TXAGC_A_MCS4_7_11N 0xE14
+#define ODM_REG_TXAGC_A_MCS8_11_11N 0xE18
+#define ODM_REG_TXAGC_A_MCS12_15_11N 0xE1C
+#define ODM_REG_FPGA0_IQK_11N 0xE28
+#define ODM_REG_TXIQK_TONE_A_11N 0xE30
+#define ODM_REG_RXIQK_TONE_A_11N 0xE34
+#define ODM_REG_TXIQK_PI_A_11N 0xE38
+#define ODM_REG_RXIQK_PI_A_11N 0xE3C
+#define ODM_REG_TXIQK_11N 0xE40
+#define ODM_REG_RXIQK_11N 0xE44
+#define ODM_REG_IQK_AGC_PTS_11N 0xE48
+#define ODM_REG_IQK_AGC_RSP_11N 0xE4C
+#define ODM_REG_BLUETOOTH_11N 0xE6C
+#define ODM_REG_RX_WAIT_CCA_11N 0xE70
+#define ODM_REG_TX_CCK_RFON_11N 0xE74
+#define ODM_REG_TX_CCK_BBON_11N 0xE78
+#define ODM_REG_OFDM_RFON_11N 0xE7C
+#define ODM_REG_OFDM_BBON_11N 0xE80
+#define ODM_REG_TX2RX_11N 0xE84
+#define ODM_REG_TX2TX_11N 0xE88
+#define ODM_REG_RX_CCK_11N 0xE8C
+#define ODM_REG_RX_OFDM_11N 0xED0
+#define ODM_REG_RX_WAIT_RIFS_11N 0xED4
+#define ODM_REG_RX2RX_11N 0xED8
+#define ODM_REG_STANDBY_11N 0xEDC
+#define ODM_REG_SLEEP_11N 0xEE0
+#define ODM_REG_PMPD_ANAEN_11N 0xEEC
+
+
+
+
+
+
+
+/* 2 MAC REG LIST */
+#define ODM_REG_BB_RST_11N 0x02
+#define ODM_REG_ANTSEL_PIN_11N 0x4C
+#define ODM_REG_EARLY_MODE_11N 0x4D0
+#define ODM_REG_RSSI_MONITOR_11N 0x4FE
+#define ODM_REG_EDCA_VO_11N 0x500
+#define ODM_REG_EDCA_VI_11N 0x504
+#define ODM_REG_EDCA_BE_11N 0x508
+#define ODM_REG_EDCA_BK_11N 0x50C
+#define ODM_REG_TXPAUSE_11N 0x522
+#define ODM_REG_RESP_TX_11N 0x6D8
+#define ODM_REG_ANT_TRAIN_PARA1_11N 0x7b0
+#define ODM_REG_ANT_TRAIN_PARA2_11N 0x7b4
+
+
+/* DIG Related */
+#define ODM_BIT_IGI_11N 0x0000007F
+
+
+#endif
diff --git a/drivers/staging/rtl8188eu/include/odm_debug.h b/drivers/staging/rtl8188eu/include/odm_debug.h
new file mode 100644
index 000000000..914f831a5
--- /dev/null
+++ b/drivers/staging/rtl8188eu/include/odm_debug.h
@@ -0,0 +1,111 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2011 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ *
+ ******************************************************************************/
+
+
+#ifndef __ODM_DBG_H__
+#define __ODM_DBG_H__
+
+
+/* */
+/* Define the debug levels */
+/* */
+/* 1. DBG_TRACE and DBG_LOUD are used for normal cases. */
+/* They can help SW engineer to develop or trace states changed */
+/* and also help HW enginner to trace every operation to and from HW, */
+/* e.g IO, Tx, Rx. */
+/* */
+/* 2. DBG_WARNNING and DBG_SERIOUS are used for unusual or error cases, */
+/* which help us to debug SW or HW. */
+
+/* Never used in a call to ODM_RT_TRACE()! */
+#define ODM_DBG_OFF 1
+
+/* Fatal bug. */
+/* For example, Tx/Rx/IO locked up, OS hangs, memory access violation, */
+/* resource allocation failed, unexpected HW behavior, HW BUG and so on. */
+#define ODM_DBG_SERIOUS 2
+
+/* Abnormal, rare, or unexpeted cases. */
+/* For example, IRP/Packet/OID canceled, device suprisely unremoved and so on. */
+#define ODM_DBG_WARNING 3
+
+/* Normal case with useful information about current SW or HW state. */
+/* For example, Tx/Rx descriptor to fill, Tx/Rx descr. completed status, */
+/* SW protocol state change, dynamic mechanism state change and so on. */
+/* */
+#define ODM_DBG_LOUD 4
+
+/* Normal case with detail execution flow or information. */
+#define ODM_DBG_TRACE 5
+
+/* Define the tracing components */
+/* BB Functions */
+#define ODM_COMP_DIG BIT0
+#define ODM_COMP_RA_MASK BIT1
+#define ODM_COMP_DYNAMIC_TXPWR BIT2
+#define ODM_COMP_FA_CNT BIT3
+#define ODM_COMP_RSSI_MONITOR BIT4
+#define ODM_COMP_CCK_PD BIT5
+#define ODM_COMP_ANT_DIV BIT6
+#define ODM_COMP_PWR_SAVE BIT7
+#define ODM_COMP_PWR_TRA BIT8
+#define ODM_COMP_RATE_ADAPTIVE BIT9
+#define ODM_COMP_PATH_DIV BIT10
+#define ODM_COMP_PSD BIT11
+#define ODM_COMP_DYNAMIC_PRICCA BIT12
+#define ODM_COMP_RXHP BIT13
+/* MAC Functions */
+#define ODM_COMP_EDCA_TURBO BIT16
+#define ODM_COMP_EARLY_MODE BIT17
+/* RF Functions */
+#define ODM_COMP_TX_PWR_TRACK BIT24
+#define ODM_COMP_RX_GAIN_TRACK BIT25
+#define ODM_COMP_CALIBRATION BIT26
+/* Common Functions */
+#define ODM_COMP_COMMON BIT30
+#define ODM_COMP_INIT BIT31
+
+/*------------------------Export Marco Definition---------------------------*/
+#define RT_PRINTK(fmt, args...) \
+ pr_info("%s(): " fmt, __func__, ## args);
+
+#ifndef ASSERT
+ #define ASSERT(expr)
+#endif
+
+#define ODM_RT_TRACE(pDM_Odm, comp, level, fmt) \
+ if (((comp) & pDM_Odm->DebugComponents) && \
+ (level <= pDM_Odm->DebugLevel)) { \
+ pr_info("[ODM-8188E] "); \
+ RT_PRINTK fmt; \
+ }
+
+#define ODM_RT_ASSERT(pDM_Odm, expr, fmt) \
+ if (!(expr)) { \
+ pr_info("Assertion failed! %s at ......\n", #expr); \
+ pr_info(" ......%s,%s,line=%d\n", __FILE__, \
+ __func__, __LINE__); \
+ RT_PRINTK fmt; \
+ ASSERT(false); \
+ }
+
+void ODM_InitDebugSetting(struct odm_dm_struct *pDM_Odm);
+
+#endif /* __ODM_DBG_H__ */
diff --git a/drivers/staging/rtl8188eu/include/odm_precomp.h b/drivers/staging/rtl8188eu/include/odm_precomp.h
new file mode 100644
index 000000000..0f236da09
--- /dev/null
+++ b/drivers/staging/rtl8188eu/include/odm_precomp.h
@@ -0,0 +1,82 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2011 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ *
+ ******************************************************************************/
+
+#ifndef __ODM_PRECOMP_H__
+#define __ODM_PRECOMP_H__
+
+#include "odm_types.h"
+
+#define TEST_FALG___ 1
+
+/* 2 Config Flags and Structs - defined by each ODM Type */
+
+#include <osdep_service.h>
+#include <drv_types.h>
+#include <hal_intf.h>
+#include <usb_ops_linux.h>
+
+/* 2 OutSrc Header Files */
+
+#include "odm.h"
+#include "odm_HWConfig.h"
+#include "odm_debug.h"
+#include "odm_RegDefine11N.h"
+
+#include "Hal8188ERateAdaptive.h"/* for RA,Power training */
+#include "rtl8188e_hal.h"
+
+#include "odm_reg.h"
+
+#include "odm_RTL8188E.h"
+
+void odm_CmnInfoHook_Debug(struct odm_dm_struct *pDM_Odm);
+void odm_CmnInfoInit_Debug(struct odm_dm_struct *pDM_Odm);
+void odm_DIGInit(struct odm_dm_struct *pDM_Odm);
+void odm_RateAdaptiveMaskInit(struct odm_dm_struct *pDM_Odm);
+void odm_DynamicBBPowerSavingInit(struct odm_dm_struct *pDM_Odm);
+void odm_DynamicTxPowerInit(struct odm_dm_struct *pDM_Odm);
+void odm_TXPowerTrackingInit(struct odm_dm_struct *pDM_Odm);
+void ODM_EdcaTurboInit(struct odm_dm_struct *pDM_Odm);
+void odm_SwAntDivInit_NIC(struct odm_dm_struct *pDM_Odm);
+void odm_CmnInfoUpdate_Debug(struct odm_dm_struct *pDM_Odm);
+void odm_CommonInfoSelfUpdate(struct odm_dm_struct *pDM_Odm);
+void odm_FalseAlarmCounterStatistics(struct odm_dm_struct *pDM_Odm);
+void odm_DIG(struct odm_dm_struct *pDM_Odm);
+void odm_CCKPacketDetectionThresh(struct odm_dm_struct *pDM_Odm);
+void odm_RefreshRateAdaptiveMaskMP(struct odm_dm_struct *pDM_Odm);
+void odm_DynamicBBPowerSaving(struct odm_dm_struct *pDM_Odm);
+void odm_SwAntDivChkAntSwitch(struct odm_dm_struct *pDM_Odm, u8 Step);
+void odm_EdcaTurboCheck(struct odm_dm_struct *pDM_Odm);
+void odm_CommonInfoSelfInit(struct odm_dm_struct *pDM_Odm);
+void odm_RSSIMonitorCheck(struct odm_dm_struct *pDM_Odm);
+void odm_RefreshRateAdaptiveMask(struct odm_dm_struct *pDM_Odm);
+void odm_1R_CCA(struct odm_dm_struct *pDM_Odm);
+void odm_RefreshRateAdaptiveMaskCE(struct odm_dm_struct *pDM_Odm);
+void odm_RefreshRateAdaptiveMaskAPADSL(struct odm_dm_struct *pDM_Odm);
+void odm_DynamicTxPowerNIC(struct odm_dm_struct *pDM_Odm);
+void odm_RSSIMonitorCheckCE(struct odm_dm_struct *pDM_Odm);
+void odm_TXPowerTrackingThermalMeterInit(struct odm_dm_struct *pDM_Odm);
+void odm_EdcaTurboCheckCE(struct odm_dm_struct *pDM_Odm);
+void odm_TXPowerTrackingCheckCE(struct odm_dm_struct *pDM_Odm);
+void odm_SwAntDivChkAntSwitchCallback(void *FunctionContext);
+void odm_InitHybridAntDiv(struct odm_dm_struct *pDM_Odm);
+void odm_HwAntDiv(struct odm_dm_struct *pDM_Odm);
+
+#endif /* __ODM_PRECOMP_H__ */
diff --git a/drivers/staging/rtl8188eu/include/odm_reg.h b/drivers/staging/rtl8188eu/include/odm_reg.h
new file mode 100644
index 000000000..89bc46bc7
--- /dev/null
+++ b/drivers/staging/rtl8188eu/include/odm_reg.h
@@ -0,0 +1,119 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2011 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ *
+ ******************************************************************************/
+/* */
+/* File Name: odm_reg.h */
+/* */
+/* Description: */
+/* */
+/* This file is for general register definition. */
+/* */
+/* */
+/* */
+#ifndef __HAL_ODM_REG_H__
+#define __HAL_ODM_REG_H__
+
+/* */
+/* Register Definition */
+/* */
+
+/* MAC REG */
+#define ODM_BB_RESET 0x002
+#define ODM_DUMMY 0x4fe
+#define ODM_EDCA_VO_PARAM 0x500
+#define ODM_EDCA_VI_PARAM 0x504
+#define ODM_EDCA_BE_PARAM 0x508
+#define ODM_EDCA_BK_PARAM 0x50C
+#define ODM_TXPAUSE 0x522
+
+/* BB REG */
+#define ODM_FPGA_PHY0_PAGE8 0x800
+#define ODM_PSD_SETTING 0x808
+#define ODM_AFE_SETTING 0x818
+#define ODM_TXAGC_B_6_18 0x830
+#define ODM_TXAGC_B_24_54 0x834
+#define ODM_TXAGC_B_MCS32_5 0x838
+#define ODM_TXAGC_B_MCS0_MCS3 0x83c
+#define ODM_TXAGC_B_MCS4_MCS7 0x848
+#define ODM_TXAGC_B_MCS8_MCS11 0x84c
+#define ODM_ANALOG_REGISTER 0x85c
+#define ODM_RF_INTERFACE_OUTPUT 0x860
+#define ODM_TXAGC_B_MCS12_MCS15 0x868
+#define ODM_TXAGC_B_11_A_2_11 0x86c
+#define ODM_AD_DA_LSB_MASK 0x874
+#define ODM_ENABLE_3_WIRE 0x88c
+#define ODM_PSD_REPORT 0x8b4
+#define ODM_R_ANT_SELECT 0x90c
+#define ODM_CCK_ANT_SELECT 0xa07
+#define ODM_CCK_PD_THRESH 0xa0a
+#define ODM_CCK_RF_REG1 0xa11
+#define ODM_CCK_MATCH_FILTER 0xa20
+#define ODM_CCK_RAKE_MAC 0xa2e
+#define ODM_CCK_CNT_RESET 0xa2d
+#define ODM_CCK_TX_DIVERSITY 0xa2f
+#define ODM_CCK_FA_CNT_MSB 0xa5b
+#define ODM_CCK_FA_CNT_LSB 0xa5c
+#define ODM_CCK_NEW_FUNCTION 0xa75
+#define ODM_OFDM_PHY0_PAGE_C 0xc00
+#define ODM_OFDM_RX_ANT 0xc04
+#define ODM_R_A_RXIQI 0xc14
+#define ODM_R_A_AGC_CORE1 0xc50
+#define ODM_R_A_AGC_CORE2 0xc54
+#define ODM_R_B_AGC_CORE1 0xc58
+#define ODM_R_AGC_PAR 0xc70
+#define ODM_R_HTSTF_AGC_PAR 0xc7c
+#define ODM_TX_PWR_TRAINING_A 0xc90
+#define ODM_TX_PWR_TRAINING_B 0xc98
+#define ODM_OFDM_FA_CNT1 0xcf0
+#define ODM_OFDM_PHY0_PAGE_D 0xd00
+#define ODM_OFDM_FA_CNT2 0xda0
+#define ODM_OFDM_FA_CNT3 0xda4
+#define ODM_OFDM_FA_CNT4 0xda8
+#define ODM_TXAGC_A_6_18 0xe00
+#define ODM_TXAGC_A_24_54 0xe04
+#define ODM_TXAGC_A_1_MCS32 0xe08
+#define ODM_TXAGC_A_MCS0_MCS3 0xe10
+#define ODM_TXAGC_A_MCS4_MCS7 0xe14
+#define ODM_TXAGC_A_MCS8_MCS11 0xe18
+#define ODM_TXAGC_A_MCS12_MCS15 0xe1c
+
+/* RF REG */
+#define ODM_GAIN_SETTING 0x00
+#define ODM_CHANNEL 0x18
+
+/* Ant Detect Reg */
+#define ODM_DPDT 0x300
+
+/* PSD Init */
+#define ODM_PSDREG 0x808
+
+/* 92D Path Div */
+#define PATHDIV_REG 0xB30
+#define PATHDIV_TRI 0xBA0
+
+
+/* */
+/* Bitmap Definition */
+/* */
+
+#define BIT_FA_RESET BIT0
+
+
+
+#endif
diff --git a/drivers/staging/rtl8188eu/include/odm_types.h b/drivers/staging/rtl8188eu/include/odm_types.h
new file mode 100644
index 000000000..c1355b959
--- /dev/null
+++ b/drivers/staging/rtl8188eu/include/odm_types.h
@@ -0,0 +1,37 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2011 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ *
+ ******************************************************************************/
+#ifndef __ODM_TYPES_H__
+#define __ODM_TYPES_H__
+
+#define ODM_CE 0x04 /* BIT2 */
+
+enum HAL_STATUS {
+ HAL_STATUS_SUCCESS,
+ HAL_STATUS_FAILURE,
+};
+
+#define SET_TX_DESC_ANTSEL_A_88E(__pTxDesc, __Value) \
+ SET_BITS_TO_LE_4BYTE(__pTxDesc+8, 24, 1, __Value)
+#define SET_TX_DESC_ANTSEL_B_88E(__pTxDesc, __Value) \
+ SET_BITS_TO_LE_4BYTE(__pTxDesc+8, 25, 1, __Value)
+#define SET_TX_DESC_ANTSEL_C_88E(__pTxDesc, __Value) \
+ SET_BITS_TO_LE_4BYTE(__pTxDesc+28, 29, 1, __Value)
+
+#endif /* __ODM_TYPES_H__ */
diff --git a/drivers/staging/rtl8188eu/include/osdep_intf.h b/drivers/staging/rtl8188eu/include/osdep_intf.h
new file mode 100644
index 000000000..efa786887
--- /dev/null
+++ b/drivers/staging/rtl8188eu/include/osdep_intf.h
@@ -0,0 +1,51 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2011 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ *
+ ******************************************************************************/
+
+#ifndef __OSDEP_INTF_H_
+#define __OSDEP_INTF_H_
+
+#include <osdep_service.h>
+#include <drv_types.h>
+
+extern char *rtw_initmac;
+extern int rtw_mc2u_disable;
+
+u8 rtw_init_drv_sw(struct adapter *padapter);
+u8 rtw_free_drv_sw(struct adapter *padapter);
+u8 rtw_reset_drv_sw(struct adapter *padapter);
+
+u32 rtw_start_drv_threads(struct adapter *padapter);
+void rtw_stop_drv_threads (struct adapter *padapter);
+void rtw_cancel_all_timer(struct adapter *padapter);
+
+int rtw_ioctl(struct net_device *dev, struct ifreq *rq, int cmd);
+
+int rtw_init_netdev_name(struct net_device *pnetdev, const char *ifname);
+struct net_device *rtw_init_netdev(struct adapter *padapter);
+u16 rtw_recv_select_queue(struct sk_buff *skb);
+void rtw_proc_init_one(struct net_device *dev);
+void rtw_proc_remove_one(struct net_device *dev);
+
+int pm_netdev_open(struct net_device *pnetdev, u8 bnormal);
+void rtw_ips_dev_unload(struct adapter *padapter);
+int rtw_ips_pwr_up(struct adapter *padapter);
+void rtw_ips_pwr_down(struct adapter *padapter);
+
+#endif /* _OSDEP_INTF_H_ */
diff --git a/drivers/staging/rtl8188eu/include/osdep_service.h b/drivers/staging/rtl8188eu/include/osdep_service.h
new file mode 100644
index 000000000..515e94962
--- /dev/null
+++ b/drivers/staging/rtl8188eu/include/osdep_service.h
@@ -0,0 +1,171 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2011 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ *
+ ******************************************************************************/
+#ifndef __OSDEP_SERVICE_H_
+#define __OSDEP_SERVICE_H_
+
+#include <basic_types.h>
+
+#define _FAIL 0
+#define _SUCCESS 1
+#define RTW_RX_HANDLED 2
+
+#include <linux/spinlock.h>
+#include <linux/compiler.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/kref.h>
+#include <linux/netdevice.h>
+#include <linux/skbuff.h>
+#include <linux/circ_buf.h>
+#include <linux/uaccess.h>
+#include <asm/byteorder.h>
+#include <linux/atomic.h>
+#include <linux/io.h>
+#include <linux/semaphore.h>
+#include <linux/sem.h>
+#include <linux/sched.h>
+#include <linux/etherdevice.h>
+#include <linux/wireless.h>
+#include <net/iw_handler.h>
+#include <linux/if_arp.h>
+#include <linux/rtnetlink.h>
+#include <linux/delay.h>
+#include <linux/proc_fs.h> /* Necessary because we use the proc fs */
+#include <linux/interrupt.h> /* for struct tasklet_struct */
+#include <linux/ip.h>
+#include <linux/kthread.h>
+
+#include <linux/usb.h>
+#include <linux/usb/ch9.h>
+
+struct __queue {
+ struct list_head queue;
+ spinlock_t lock;
+};
+
+static inline struct list_head *get_list_head(struct __queue *queue)
+{
+ return &(queue->queue);
+}
+
+static inline int _enter_critical_mutex(struct mutex *pmutex,
+ unsigned long *pirqL)
+{
+ int ret;
+
+ ret = mutex_lock_interruptible(pmutex);
+ return ret;
+}
+
+static inline int rtw_netif_queue_stopped(struct net_device *pnetdev)
+{
+ return netif_tx_queue_stopped(netdev_get_tx_queue(pnetdev, 0)) &&
+ netif_tx_queue_stopped(netdev_get_tx_queue(pnetdev, 1)) &&
+ netif_tx_queue_stopped(netdev_get_tx_queue(pnetdev, 2)) &&
+ netif_tx_queue_stopped(netdev_get_tx_queue(pnetdev, 3));
+}
+
+
+#define BIT0 0x00000001
+#define BIT1 0x00000002
+#define BIT2 0x00000004
+#define BIT3 0x00000008
+#define BIT4 0x00000010
+#define BIT5 0x00000020
+#define BIT6 0x00000040
+#define BIT7 0x00000080
+#define BIT8 0x00000100
+#define BIT9 0x00000200
+#define BIT10 0x00000400
+#define BIT11 0x00000800
+#define BIT12 0x00001000
+#define BIT13 0x00002000
+#define BIT14 0x00004000
+#define BIT15 0x00008000
+#define BIT16 0x00010000
+#define BIT17 0x00020000
+#define BIT18 0x00040000
+#define BIT19 0x00080000
+#define BIT20 0x00100000
+#define BIT21 0x00200000
+#define BIT22 0x00400000
+#define BIT23 0x00800000
+#define BIT24 0x01000000
+#define BIT25 0x02000000
+#define BIT26 0x04000000
+#define BIT27 0x08000000
+#define BIT28 0x10000000
+#define BIT29 0x20000000
+#define BIT30 0x40000000
+#define BIT31 0x80000000
+#define BIT32 0x0100000000
+#define BIT33 0x0200000000
+#define BIT34 0x0400000000
+#define BIT35 0x0800000000
+#define BIT36 0x1000000000
+
+extern int RTW_STATUS_CODE(int error_code);
+
+#define rtw_update_mem_stat(flag, sz) do {} while (0)
+u8 *_rtw_malloc(u32 sz);
+#define rtw_malloc(sz) _rtw_malloc((sz))
+
+void *rtw_malloc2d(int h, int w, int size);
+
+u32 _rtw_down_sema(struct semaphore *sema);
+
+void _rtw_init_queue(struct __queue *pqueue);
+
+s32 rtw_get_passing_time_ms(u32 start);
+
+struct rtw_netdev_priv_indicator {
+ void *priv;
+ u32 sizeof_priv;
+};
+struct net_device *rtw_alloc_etherdev_with_old_priv(int sizeof_priv,
+ void *old_priv);
+
+#define rtw_netdev_priv(netdev) \
+ (((struct rtw_netdev_priv_indicator *)netdev_priv(netdev))->priv)
+void rtw_free_netdev(struct net_device *netdev);
+
+#define NDEV_FMT "%s"
+#define NDEV_ARG(ndev) ndev->name
+#define ADPT_FMT "%s"
+#define ADPT_ARG(adapter) adapter->pnetdev->name
+#define FUNC_NDEV_FMT "%s(%s)"
+#define FUNC_NDEV_ARG(ndev) __func__, ndev->name
+#define FUNC_ADPT_FMT "%s(%s)"
+#define FUNC_ADPT_ARG(adapter) __func__, adapter->pnetdev->name
+
+#define rtw_signal_process(pid, sig) kill_pid(find_vpid((pid)), (sig), 1)
+
+u64 rtw_modular64(u64 x, u64 y);
+
+/* Macros for handling unaligned memory accesses */
+
+#define RTW_GET_BE24(a) ((((u32)(a)[0]) << 16) | (((u32) (a)[1]) << 8) | \
+ ((u32)(a)[2]))
+
+void rtw_buf_free(u8 **buf, u32 *buf_len);
+void rtw_buf_update(u8 **buf, u32 *buf_len, u8 *src, u32 src_len);
+#endif
diff --git a/drivers/staging/rtl8188eu/include/phy.h b/drivers/staging/rtl8188eu/include/phy.h
new file mode 100644
index 000000000..9a9ab82a8
--- /dev/null
+++ b/drivers/staging/rtl8188eu/include/phy.h
@@ -0,0 +1,30 @@
+#include <odm.h>
+
+#define IQK_DELAY_TIME_88E 10
+#define index_mapping_NUM_88E 15
+#define AVG_THERMAL_NUM_88E 4
+#define ODM_TARGET_CHNL_NUM_2G_5G 59
+
+bool rtl88eu_phy_mac_config(struct adapter *adapt);
+bool rtl88eu_phy_rf_config(struct adapter *adapt);
+bool rtl88eu_phy_bb_config(struct adapter *adapt);
+
+u32 phy_query_bb_reg(struct adapter *adapt, u32 regaddr, u32 bitmask);
+void phy_set_bb_reg(struct adapter *adapt, u32 regaddr, u32 bitmask, u32 data);
+u32 phy_query_rf_reg(struct adapter *adapt, enum rf_radio_path rf_path,
+ u32 reg_addr, u32 bit_mask);
+void phy_set_rf_reg(struct adapter *adapt, enum rf_radio_path rf_path,
+ u32 reg_addr, u32 bit_mask, u32 data);
+
+void phy_set_tx_power_level(struct adapter *adapt, u8 channel);
+
+void phy_set_bw_mode(struct adapter *adapt, enum ht_channel_width bandwidth,
+ unsigned char offset);
+void phy_sw_chnl(struct adapter *adapt, u8 channel);
+
+void rtl88eu_dm_txpower_track_adjust(struct odm_dm_struct *dm_odm,
+ u8 type, u8 *dir, u32 *out_write);
+
+void rtl88eu_dm_txpower_tracking_callback_thermalmeter(struct adapter *adapt);
+void rtl88eu_phy_iq_calibrate(struct adapter *adapter, bool recovery);
+void rtl88eu_phy_lc_calibrate(struct adapter *adapter);
diff --git a/drivers/staging/rtl8188eu/include/pwrseq.h b/drivers/staging/rtl8188eu/include/pwrseq.h
new file mode 100644
index 000000000..43db92dcb
--- /dev/null
+++ b/drivers/staging/rtl8188eu/include/pwrseq.h
@@ -0,0 +1,341 @@
+
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2011 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ *
+ ******************************************************************************/
+
+#ifndef __HAL8188EPWRSEQ_H__
+#define __HAL8188EPWRSEQ_H__
+
+#include "pwrseqcmd.h"
+
+/*
+ Check document WM-20110607-Paul-RTL8188E_Power_Architecture-R02.vsd
+ There are 6 HW Power States:
+ 0: POFF--Power Off
+ 1: PDN--Power Down
+ 2: CARDEMU--Card Emulation
+ 3: ACT--Active Mode
+ 4: LPS--Low Power State
+ 5: SUS--Suspend
+
+ The transision from different states are defined below
+ TRANS_CARDEMU_TO_ACT
+ TRANS_ACT_TO_CARDEMU
+ TRANS_CARDEMU_TO_SUS
+ TRANS_SUS_TO_CARDEMU
+ TRANS_CARDEMU_TO_PDN
+ TRANS_ACT_TO_LPS
+ TRANS_LPS_TO_ACT
+
+ TRANS_END
+
+ PWR SEQ Version: rtl8188E_PwrSeq_V09.h
+*/
+#define RTL8188E_TRANS_CARDEMU_TO_ACT_STEPS 10
+#define RTL8188E_TRANS_ACT_TO_CARDEMU_STEPS 10
+#define RTL8188E_TRANS_CARDEMU_TO_SUS_STEPS 10
+#define RTL8188E_TRANS_SUS_TO_CARDEMU_STEPS 10
+#define RTL8188E_TRANS_CARDEMU_TO_PDN_STEPS 10
+#define RTL8188E_TRANS_PDN_TO_CARDEMU_STEPS 10
+#define RTL8188E_TRANS_ACT_TO_LPS_STEPS 15
+#define RTL8188E_TRANS_LPS_TO_ACT_STEPS 15
+#define RTL8188E_TRANS_END_STEPS 1
+
+
+#define RTL8188E_TRANS_CARDEMU_TO_ACT \
+ /* format
+ * { offset, cut_msk, fab_msk|interface_msk, base|cmd, msk, value
+ * },
+ * comment here
+ */ \
+ {0x0006, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, \
+ PWR_BASEADDR_MAC, PWR_CMD_POLLING, BIT1, BIT1}, \
+ /* wait till 0x04[17] = 1 power ready*/ \
+ {0x0002, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, \
+ PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT0|BIT1, 0}, \
+ /* 0x02[1:0] = 0 reset BB*/ \
+ {0x0026, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, \
+ PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT7, BIT7}, \
+ /*0x24[23] = 2b'01 schmit trigger */ \
+ {0x0005, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, \
+ PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT7, 0}, \
+ /* 0x04[15] = 0 disable HWPDN (control by DRV)*/ \
+ {0x0005, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, \
+ PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT4|BIT3, 0}, \
+ /*0x04[12:11] = 2b'00 disable WL suspend*/ \
+ {0x0005, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, \
+ PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT0, BIT0}, \
+ /*0x04[8] = 1 polling until return 0*/ \
+ {0x0005, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, \
+ PWR_BASEADDR_MAC, PWR_CMD_POLLING, BIT0, 0}, \
+ /*wait till 0x04[8] = 0*/ \
+ {0x0023, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, \
+ PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT4, 0}, \
+ /*LDO normal mode*/ \
+ {0x0074, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_SDIO_MSK, \
+ PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT4, BIT4}, \
+ /*SDIO Driving*/
+
+#define RTL8188E_TRANS_ACT_TO_CARDEMU \
+ /* format
+ * { offset, cut_msk, fab_msk|interface_msk, base|cmd, msk, value
+ * },
+ * comments here
+ */ \
+ {0x001F, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, \
+ PWR_BASEADDR_MAC, PWR_CMD_WRITE, 0xFF, 0}, \
+ /*0x1F[7:0] = 0 turn off RF*/ \
+ {0x0023, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, \
+ PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT4, BIT4}, \
+ /*LDO Sleep mode*/ \
+ {0x0005, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, \
+ PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT1, BIT1}, \
+ /*0x04[9] = 1 turn off MAC by HW state machine*/ \
+ {0x0005, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, \
+ PWR_BASEADDR_MAC, PWR_CMD_POLLING, BIT1, 0}, \
+ /*wait till 0x04[9] = 0 polling until return 0 to disable*/
+
+#define RTL8188E_TRANS_CARDEMU_TO_SUS \
+ /* format
+ * { offset, cut_msk, fab_msk|interface_msk, base|cmd, msk,
+ * value },
+ * comments here
+ */ \
+ {0x0005, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, \
+ PWR_INTF_USB_MSK|PWR_INTF_SDIO_MSK, PWR_BASEADDR_MAC, \
+ PWR_CMD_WRITE, BIT3|BIT4, BIT3}, \
+ /* 0x04[12:11] = 2b'01enable WL suspend */ \
+ {0x0005, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_PCI_MSK, \
+ PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT3|BIT4, BIT3|BIT4}, \
+ /* 0x04[12:11] = 2b'11enable WL suspend for PCIe */ \
+ {0x0007, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, \
+ PWR_INTF_USB_MSK|PWR_INTF_SDIO_MSK, PWR_BASEADDR_MAC, \
+ PWR_CMD_WRITE, 0xFF, BIT7}, \
+ /* 0x04[31:30] = 2b'10 enable enable bandgap mbias in suspend */\
+ {0x0041, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, \
+ PWR_INTF_USB_MSK|PWR_INTF_SDIO_MSK, PWR_BASEADDR_MAC, \
+ PWR_CMD_WRITE, BIT4, 0}, \
+ /*Clear SIC_EN register 0x40[12] = 1'b0 */ \
+ {0xfe10, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, \
+ PWR_INTF_USB_MSK|PWR_INTF_SDIO_MSK, PWR_BASEADDR_MAC, \
+ PWR_CMD_WRITE, BIT4, BIT4}, \
+ /*Set USB suspend enable local register 0xfe10[4]=1 */ \
+ {0x0086, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_SDIO_MSK, \
+ PWR_BASEADDR_SDIO, PWR_CMD_WRITE, BIT0, BIT0}, \
+ /*Set SDIO suspend local register*/ \
+ {0x0086, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_SDIO_MSK, \
+ PWR_BASEADDR_SDIO, PWR_CMD_POLLING, BIT1, 0}, \
+ /*wait power state to suspend*/
+
+#define RTL8188E_TRANS_SUS_TO_CARDEMU \
+ /* format
+ * { offset, cut_msk, fab_msk|interface_msk, base|cmd, msk,
+ * value },
+ * comments here
+ */ \
+ {0x0086, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_SDIO_MSK, \
+ PWR_BASEADDR_SDIO, PWR_CMD_WRITE, BIT0, 0}, \
+ /*Set SDIO suspend local register*/ \
+ {0x0086, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_SDIO_MSK, \
+ PWR_BASEADDR_SDIO, PWR_CMD_POLLING, BIT1, BIT1}, \
+ /*wait power state to suspend*/ \
+ {0x0005, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, \
+ PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT3|BIT4, 0}, \
+ /*0x04[12:11] = 2b'01enable WL suspend*/
+
+#define RTL8188E_TRANS_CARDEMU_TO_CARDDIS \
+ /* format
+ * { offset, cut_msk, fab_msk|interface_msk, base|cmd, msk,
+ * value },
+ * comments here
+ */ \
+ {0x0026, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, \
+ PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT7, BIT7}, \
+ /*0x24[23] = 2b'01 schmit trigger */ \
+ {0x0005, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, \
+ PWR_INTF_USB_MSK|PWR_INTF_SDIO_MSK, PWR_BASEADDR_MAC, \
+ PWR_CMD_WRITE, BIT3|BIT4, BIT3}, \
+ /*0x04[12:11] = 2b'01 enable WL suspend*/ \
+ {0x0007, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, \
+ PWR_INTF_USB_MSK|PWR_INTF_SDIO_MSK, PWR_BASEADDR_MAC, \
+ PWR_CMD_WRITE, 0xFF, 0}, \
+ /* 0x04[31:30] = 2b'10 enable enable bandgap mbias in suspend */\
+ {0x0041, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, \
+ PWR_INTF_USB_MSK|PWR_INTF_SDIO_MSK, PWR_BASEADDR_MAC, \
+ PWR_CMD_WRITE, BIT4, 0}, \
+ /*Clear SIC_EN register 0x40[12] = 1'b0 */ \
+ {0xfe10, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_USB_MSK, \
+ PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT4, BIT4}, \
+ /*Set USB suspend enable local register 0xfe10[4]=1 */ \
+ {0x0086, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_SDIO_MSK, \
+ PWR_BASEADDR_SDIO, PWR_CMD_WRITE, BIT0, BIT0}, \
+ /*Set SDIO suspend local register*/ \
+ {0x0086, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_SDIO_MSK, \
+ PWR_BASEADDR_SDIO, PWR_CMD_POLLING, BIT1, 0}, \
+ /*wait power state to suspend*/
+
+#define RTL8188E_TRANS_CARDDIS_TO_CARDEMU \
+ /* format
+ * { offset, cut_msk, fab_msk|interface_msk, base|cmd, msk,
+ * value },
+ * comments here
+ */ \
+ {0x0086, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_SDIO_MSK, \
+ PWR_BASEADDR_SDIO, PWR_CMD_WRITE, BIT0, 0}, \
+ /*Set SDIO suspend local register*/ \
+ {0x0086, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_SDIO_MSK, \
+ PWR_BASEADDR_SDIO, PWR_CMD_POLLING, BIT1, BIT1}, \
+ /*wait power state to suspend*/ \
+ {0x0005, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, \
+ PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT3|BIT4, 0}, \
+ /*0x04[12:11] = 2b'01enable WL suspend*/
+
+#define RTL8188E_TRANS_CARDEMU_TO_PDN \
+ /* format
+ * { offset, cut_msk, fab_msk|interface_msk, base|cmd, msk,
+ * value },
+ * comments here
+ */ \
+ {0x0006, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, \
+ PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT0, 0}, \
+ /* 0x04[16] = 0*/ \
+ {0x0005, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, \
+ PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT7, BIT7}, \
+ /* 0x04[15] = 1*/
+
+#define RTL8188E_TRANS_PDN_TO_CARDEMU \
+ /* format
+ * { offset, cut_msk, fab_msk|interface_msk, base|cmd, msk,
+ * value },
+ * comments here
+ */ \
+ {0x0005, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, \
+ PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT7, 0}, \
+ /* 0x04[15] = 0*/
+
+/* This is used by driver for LPSRadioOff Procedure, not for FW LPS Step */
+#define RTL8188E_TRANS_ACT_TO_LPS \
+ /* format
+ * { offset, cut_msk, fab_msk|interface_msk, base|cmd, msk,
+ * value },
+ * comments here
+ */ \
+ {0x0522, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, \
+ PWR_BASEADDR_MAC, PWR_CMD_WRITE, 0xFF, 0x7F},/*Tx Pause*/ \
+ {0x05F8, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, \
+ PWR_BASEADDR_MAC, PWR_CMD_POLLING, 0xFF, 0}, \
+ /*Should be zero if no packet is transmitting*/ \
+ {0x05F9, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, \
+ PWR_BASEADDR_MAC, PWR_CMD_POLLING, 0xFF, 0}, \
+ /*Should be zero if no packet is transmitting*/ \
+ {0x05FA, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, \
+ PWR_BASEADDR_MAC, PWR_CMD_POLLING, 0xFF, 0}, \
+ /*Should be zero if no packet is transmitting*/ \
+ {0x05FB, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, \
+ PWR_BASEADDR_MAC, PWR_CMD_POLLING, 0xFF, 0}, \
+ /*Should be zero if no packet is transmitting*/ \
+ {0x0002, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, \
+ PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT0, 0}, \
+ /*CCK and OFDM are disabled,and clock are gated*/ \
+ {0x0002, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, \
+ PWR_BASEADDR_MAC, PWR_CMD_DELAY, 0, \
+ PWRSEQ_DELAY_US},/*Delay 1us*/ \
+ {0x0100, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, \
+ PWR_BASEADDR_MAC, PWR_CMD_WRITE, 0xFF, 0x3F},/*Reset MAC TRX*/ \
+ {0x0101, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, \
+ PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT1, 0},/*check if removed later*/\
+ {0x0553, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, \
+ PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT5, BIT5}, \
+ /*Respond TxOK to scheduler*/
+
+
+#define RTL8188E_TRANS_LPS_TO_ACT \
+ /* format
+ * { offset, cut_msk, fab_msk|interface_msk, base|cmd, msk,
+ * value },
+ * comments here
+ */ \
+ {0x0080, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_SDIO_MSK, \
+ PWR_BASEADDR_SDIO, PWR_CMD_WRITE, 0xFF, 0x84}, /*SDIO RPWM*/ \
+ {0xFE58, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_USB_MSK, \
+ PWR_BASEADDR_MAC, PWR_CMD_WRITE, 0xFF, 0x84}, /*USB RPWM*/ \
+ {0x0361, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_PCI_MSK, \
+ PWR_BASEADDR_MAC, PWR_CMD_WRITE, 0xFF, 0x84}, /*PCIe RPWM*/ \
+ {0x0002, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, \
+ PWR_BASEADDR_MAC, PWR_CMD_DELAY, 0, PWRSEQ_DELAY_MS}, /*Delay*/ \
+ {0x0008, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, \
+ PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT4, 0}, \
+ /* 0x08[4] = 0 switch TSF to 40M */ \
+ {0x0109, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, \
+ PWR_BASEADDR_MAC, PWR_CMD_POLLING, BIT7, 0}, \
+ /* Polling 0x109[7]=0 TSF in 40M */ \
+ {0x0029, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, \
+ PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT6|BIT7, 0}, \
+ /* 0x29[7:6] = 2b'00 enable BB clock */ \
+ {0x0101, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, \
+ PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT1, BIT1}, \
+ /* 0x101[1] = 1 */ \
+ {0x0100, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, \
+ PWR_BASEADDR_MAC, PWR_CMD_WRITE, 0xFF, 0xFF}, \
+ /* 0x100[7:0] = 0xFF enable WMAC TRX */ \
+ {0x0002, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, \
+ PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT1|BIT0, BIT1|BIT0}, \
+ /* 0x02[1:0] = 2b'11 enable BB macro */ \
+ {0x0522, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, \
+ PWR_BASEADDR_MAC, PWR_CMD_WRITE, 0xFF, 0}, /*. 0x522 = 0*/
+
+#define RTL8188E_TRANS_END \
+ /* format
+ * { offset, cut_msk, fab_msk|interface_msk, base|cmd, msk,
+ * value },
+ * comments here
+ */ \
+ {0xFFFF, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, 0, \
+ PWR_CMD_END, 0, 0},
+
+
+extern struct wl_pwr_cfg rtl8188E_power_on_flow
+ [RTL8188E_TRANS_CARDEMU_TO_ACT_STEPS + RTL8188E_TRANS_END_STEPS];
+extern struct wl_pwr_cfg rtl8188E_radio_off_flow
+ [RTL8188E_TRANS_ACT_TO_CARDEMU_STEPS + RTL8188E_TRANS_END_STEPS];
+extern struct wl_pwr_cfg rtl8188E_card_disable_flow
+ [RTL8188E_TRANS_ACT_TO_CARDEMU_STEPS +
+ RTL8188E_TRANS_CARDEMU_TO_PDN_STEPS +
+ RTL8188E_TRANS_END_STEPS];
+extern struct wl_pwr_cfg rtl8188E_card_enable_flow
+ [RTL8188E_TRANS_ACT_TO_CARDEMU_STEPS +
+ RTL8188E_TRANS_CARDEMU_TO_PDN_STEPS +
+ RTL8188E_TRANS_END_STEPS];
+extern struct wl_pwr_cfg rtl8188E_suspend_flow[
+ RTL8188E_TRANS_ACT_TO_CARDEMU_STEPS +
+ RTL8188E_TRANS_CARDEMU_TO_SUS_STEPS +
+ RTL8188E_TRANS_END_STEPS];
+extern struct wl_pwr_cfg rtl8188E_resume_flow
+ [RTL8188E_TRANS_ACT_TO_CARDEMU_STEPS +
+ RTL8188E_TRANS_CARDEMU_TO_SUS_STEPS +
+ RTL8188E_TRANS_END_STEPS];
+extern struct wl_pwr_cfg rtl8188E_hwpdn_flow
+ [RTL8188E_TRANS_ACT_TO_CARDEMU_STEPS +
+ RTL8188E_TRANS_CARDEMU_TO_PDN_STEPS + RTL8188E_TRANS_END_STEPS];
+extern struct wl_pwr_cfg rtl8188E_enter_lps_flow
+ [RTL8188E_TRANS_ACT_TO_LPS_STEPS + RTL8188E_TRANS_END_STEPS];
+extern struct wl_pwr_cfg rtl8188E_leave_lps_flow
+ [RTL8188E_TRANS_LPS_TO_ACT_STEPS + RTL8188E_TRANS_END_STEPS];
+
+#endif /* __HAL8188EPWRSEQ_H__ */
diff --git a/drivers/staging/rtl8188eu/include/pwrseqcmd.h b/drivers/staging/rtl8188eu/include/pwrseqcmd.h
new file mode 100644
index 000000000..980a49769
--- /dev/null
+++ b/drivers/staging/rtl8188eu/include/pwrseqcmd.h
@@ -0,0 +1,90 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2011 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ *
+ ******************************************************************************/
+#ifndef __HALPWRSEQCMD_H__
+#define __HALPWRSEQCMD_H__
+
+#include <drv_types.h>
+
+/* The value of cmd: 4 bits */
+#define PWR_CMD_READ 0x00
+#define PWR_CMD_WRITE 0x01
+#define PWR_CMD_POLLING 0x02
+#define PWR_CMD_DELAY 0x03
+#define PWR_CMD_END 0x04
+
+/* The value of base: 4 bits */
+/* define the base address of each block */
+#define PWR_BASEADDR_MAC 0x00
+#define PWR_BASEADDR_USB 0x01
+#define PWR_BASEADDR_PCIE 0x02
+#define PWR_BASEADDR_SDIO 0x03
+
+/* The value of interface_msk: 4 bits */
+#define PWR_INTF_SDIO_MSK BIT(0)
+#define PWR_INTF_USB_MSK BIT(1)
+#define PWR_INTF_PCI_MSK BIT(2)
+#define PWR_INTF_ALL_MSK (BIT(0)|BIT(1)|BIT(2)|BIT(3))
+
+/* The value of fab_msk: 4 bits */
+#define PWR_FAB_TSMC_MSK BIT(0)
+#define PWR_FAB_UMC_MSK BIT(1)
+#define PWR_FAB_ALL_MSK (BIT(0)|BIT(1)|BIT(2)|BIT(3))
+
+/* The value of cut_msk: 8 bits */
+#define PWR_CUT_TESTCHIP_MSK BIT(0)
+#define PWR_CUT_A_MSK BIT(1)
+#define PWR_CUT_B_MSK BIT(2)
+#define PWR_CUT_C_MSK BIT(3)
+#define PWR_CUT_D_MSK BIT(4)
+#define PWR_CUT_E_MSK BIT(5)
+#define PWR_CUT_F_MSK BIT(6)
+#define PWR_CUT_G_MSK BIT(7)
+#define PWR_CUT_ALL_MSK 0xFF
+
+
+enum pwrseq_cmd_delat_unit {
+ PWRSEQ_DELAY_US,
+ PWRSEQ_DELAY_MS,
+};
+
+struct wl_pwr_cfg {
+ u16 offset;
+ u8 cut_msk;
+ u8 fab_msk:4;
+ u8 interface_msk:4;
+ u8 base:4;
+ u8 cmd:4;
+ u8 msk;
+ u8 value;
+};
+
+#define GET_PWR_CFG_OFFSET(__PWR_CMD) __PWR_CMD.offset
+#define GET_PWR_CFG_CUT_MASK(__PWR_CMD) __PWR_CMD.cut_msk
+#define GET_PWR_CFG_FAB_MASK(__PWR_CMD) __PWR_CMD.fab_msk
+#define GET_PWR_CFG_INTF_MASK(__PWR_CMD) __PWR_CMD.interface_msk
+#define GET_PWR_CFG_BASE(__PWR_CMD) __PWR_CMD.base
+#define GET_PWR_CFG_CMD(__PWR_CMD) __PWR_CMD.cmd
+#define GET_PWR_CFG_MASK(__PWR_CMD) __PWR_CMD.msk
+#define GET_PWR_CFG_VALUE(__PWR_CMD) __PWR_CMD.value
+
+u8 rtl88eu_pwrseqcmdparsing(struct adapter *padapter, u8 cut_vers, u8 fab_vers,
+ u8 ifacetype, struct wl_pwr_cfg pwrcfgCmd[]);
+
+#endif
diff --git a/drivers/staging/rtl8188eu/include/recv_osdep.h b/drivers/staging/rtl8188eu/include/recv_osdep.h
new file mode 100644
index 000000000..5aabd3984
--- /dev/null
+++ b/drivers/staging/rtl8188eu/include/recv_osdep.h
@@ -0,0 +1,51 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2011 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ *
+ ******************************************************************************/
+#ifndef __RECV_OSDEP_H_
+#define __RECV_OSDEP_H_
+
+#include <osdep_service.h>
+#include <drv_types.h>
+
+
+int _rtw_init_recv_priv(struct recv_priv *precvpriv, struct adapter *padapter);
+void _rtw_free_recv_priv(struct recv_priv *precvpriv);
+
+
+s32 rtw_recv_entry(struct recv_frame *precv_frame);
+int rtw_recv_indicatepkt(struct adapter *adapter,
+ struct recv_frame *recv_frame);
+void rtw_recv_returnpacket(struct net_device *cnxt, struct sk_buff *retpkt);
+
+void rtw_handle_tkip_mic_err(struct adapter *padapter, u8 bgroup);
+
+int rtw_init_recv_priv(struct recv_priv *precvpriv, struct adapter *padapter);
+void rtw_free_recv_priv(struct recv_priv *precvpriv);
+
+int rtw_os_recv_resource_alloc(struct adapter *adapt,
+ struct recv_frame *recvfr);
+
+int rtw_os_recvbuf_resource_alloc(struct adapter *adapt, struct recv_buf *buf);
+
+void rtw_init_recv_timer(struct recv_reorder_ctrl *preorder_ctrl);
+int _netdev_open(struct net_device *pnetdev);
+int netdev_open(struct net_device *pnetdev);
+int netdev_close(struct net_device *pnetdev);
+
+#endif /* */
diff --git a/drivers/staging/rtl8188eu/include/rf.h b/drivers/staging/rtl8188eu/include/rf.h
new file mode 100644
index 000000000..98a5551f5
--- /dev/null
+++ b/drivers/staging/rtl8188eu/include/rf.h
@@ -0,0 +1,11 @@
+#define RF6052_MAX_TX_PWR 0x3F
+#define RF6052_MAX_REG 0x3F
+
+void rtl88eu_phy_rf6052_set_bandwidth(struct adapter *adapt,
+ enum ht_channel_width bandwidth);
+void rtl88eu_phy_rf6052_set_cck_txpower(struct adapter *adapt,
+ u8 *powerlevel);
+void rtl88eu_phy_rf6052_set_ofdm_txpower(struct adapter *adapt,
+ u8 *powerlevel_ofdm,
+ u8 *powerlevel_bw20,
+ u8 *powerlevel_bw40, u8 channel);
diff --git a/drivers/staging/rtl8188eu/include/rtl8188e_cmd.h b/drivers/staging/rtl8188eu/include/rtl8188e_cmd.h
new file mode 100644
index 000000000..42b1f2242
--- /dev/null
+++ b/drivers/staging/rtl8188eu/include/rtl8188e_cmd.h
@@ -0,0 +1,116 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2011 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ *
+ ******************************************************************************/
+#ifndef __RTL8188E_CMD_H__
+#define __RTL8188E_CMD_H__
+
+enum RTL8188E_H2C_CMD_ID {
+ /* Class Common */
+ H2C_COM_RSVD_PAGE = 0x00,
+ H2C_COM_MEDIA_STATUS_RPT = 0x01,
+ H2C_COM_SCAN = 0x02,
+ H2C_COM_KEEP_ALIVE = 0x03,
+ H2C_COM_DISCNT_DECISION = 0x04,
+ H2C_COM_INIT_OFFLOAD = 0x06,
+ H2C_COM_REMOTE_WAKE_CTL = 0x07,
+ H2C_COM_AP_OFFLOAD = 0x08,
+ H2C_COM_BCN_RSVD_PAGE = 0x09,
+ H2C_COM_PROB_RSP_RSVD_PAGE = 0x0A,
+
+ /* Class PS */
+ H2C_PS_PWR_MODE = 0x20,
+ H2C_PS_TUNE_PARA = 0x21,
+ H2C_PS_TUNE_PARA_2 = 0x22,
+ H2C_PS_LPS_PARA = 0x23,
+ H2C_PS_P2P_OFFLOAD = 0x24,
+
+ /* Class DM */
+ H2C_DM_MACID_CFG = 0x40,
+ H2C_DM_TXBF = 0x41,
+
+ /* Class BT */
+ H2C_BT_COEX_MASK = 0x60,
+ H2C_BT_COEX_GPIO_MODE = 0x61,
+ H2C_BT_DAC_SWING_VAL = 0x62,
+ H2C_BT_PSD_RST = 0x63,
+
+ /* Class */
+ H2C_RESET_TSF = 0xc0,
+};
+
+struct cmd_msg_parm {
+ u8 eid; /* element id */
+ u8 sz; /* sz */
+ u8 buf[6];
+};
+
+enum {
+ PWRS
+};
+
+struct setpwrmode_parm {
+ u8 Mode;/* 0:Active,1:LPS,2:WMMPS */
+ u8 SmartPS_RLBM;/* LPS= 0:PS_Poll,1:PS_Poll,2:NullData,WMM= 0:PS_Poll,1:NullData */
+ u8 AwakeInterval; /* unit: beacon interval */
+ u8 bAllQueueUAPSD;
+ u8 PwrState;/* AllON(0x0c),RFON(0x04),RFOFF(0x00) */
+};
+
+struct H2C_SS_RFOFF_PARAM {
+ u8 ROFOn; /* 1: on, 0:off */
+ u16 gpio_period; /* unit: 1024 us */
+} __packed;
+
+struct joinbssrpt_parm {
+ u8 OpMode; /* RT_MEDIA_STATUS */
+};
+
+struct rsvdpage_loc {
+ u8 LocProbeRsp;
+ u8 LocPsPoll;
+ u8 LocNullData;
+ u8 LocQosNull;
+ u8 LocBTQosNull;
+};
+
+struct P2P_PS_Offload_t {
+ u8 Offload_En:1;
+ u8 role:1; /* 1: Owner, 0: Client */
+ u8 CTWindow_En:1;
+ u8 NoA0_En:1;
+ u8 NoA1_En:1;
+ u8 AllStaSleep:1; /* Only valid in Owner */
+ u8 discovery:1;
+ u8 rsvd:1;
+};
+
+struct P2P_PS_CTWPeriod_t {
+ u8 CTWPeriod; /* TU */
+};
+
+/* host message to firmware cmd */
+void rtl8188e_set_FwPwrMode_cmd(struct adapter *padapter, u8 Mode);
+void rtl8188e_set_FwJoinBssReport_cmd(struct adapter *padapter, u8 mstatus);
+u8 rtl8188e_set_raid_cmd(struct adapter *padapter, u32 mask);
+void rtl8188e_Add_RateATid(struct adapter *padapter, u32 bitmap, u8 arg,
+ u8 rssi_level);
+
+void rtl8188e_set_FwMediaStatus_cmd(struct adapter *adapt, __le16 mstatus_rpt);
+
+#endif/* __RTL8188E_CMD_H__ */
diff --git a/drivers/staging/rtl8188eu/include/rtl8188e_dm.h b/drivers/staging/rtl8188eu/include/rtl8188e_dm.h
new file mode 100644
index 000000000..5e0ac31ef
--- /dev/null
+++ b/drivers/staging/rtl8188eu/include/rtl8188e_dm.h
@@ -0,0 +1,61 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2011 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ *
+ ******************************************************************************/
+#ifndef __RTL8188E_DM_H__
+#define __RTL8188E_DM_H__
+enum{
+ UP_LINK,
+ DOWN_LINK,
+};
+/* duplicate code,will move to ODM ######### */
+#define IQK_MAC_REG_NUM 4
+#define IQK_ADDA_REG_NUM 16
+#define IQK_BB_REG_NUM 9
+#define HP_THERMAL_NUM 8
+/* duplicate code,will move to ODM ######### */
+struct dm_priv {
+ u8 DM_Type;
+ u8 DMFlag;
+ u8 InitDMFlag;
+ u32 InitODMFlag;
+
+ /* Upper and Lower Signal threshold for Rate Adaptive*/
+ int UndecoratedSmoothedPWDB;
+ int UndecoratedSmoothedCCK;
+ int EntryMinUndecoratedSmoothedPWDB;
+ int EntryMaxUndecoratedSmoothedPWDB;
+ int MinUndecoratedPWDBForDM;
+ int LastMinUndecoratedPWDBForDM;
+
+ /* for High Power */
+ u8 bDynamicTxPowerEnable;
+ u8 LastDTPLvl;
+ u8 DynamicTxHighPowerLvl;/* Tx Power Control for Near/Far Range */
+ u8 PowerIndex_backup[6];
+};
+
+void rtl8188e_init_dm_priv(struct adapter *adapt);
+void rtl8188e_InitHalDm(struct adapter *adapt);
+void rtl8188e_HalDmWatchDog(struct adapter *adapt);
+
+void AntDivCompare8188E(struct adapter *adapt, struct wlan_bssid_ex *dst,
+ struct wlan_bssid_ex *src);
+u8 AntDivBeforeLink8188E(struct adapter *adapt);
+
+#endif
diff --git a/drivers/staging/rtl8188eu/include/rtl8188e_hal.h b/drivers/staging/rtl8188eu/include/rtl8188e_hal.h
new file mode 100644
index 000000000..7b3c464e7
--- /dev/null
+++ b/drivers/staging/rtl8188eu/include/rtl8188e_hal.h
@@ -0,0 +1,428 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2011 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ *
+ ******************************************************************************/
+#ifndef __RTL8188E_HAL_H__
+#define __RTL8188E_HAL_H__
+
+
+/* include HAL Related header after HAL Related compiling flags */
+#include "rtl8188e_spec.h"
+#include "Hal8188EPhyReg.h"
+#include "Hal8188EPhyCfg.h"
+#include "rtl8188e_dm.h"
+#include "rtl8188e_recv.h"
+#include "rtl8188e_xmit.h"
+#include "rtl8188e_cmd.h"
+#include "pwrseq.h"
+#include "rtw_efuse.h"
+#include "rtw_sreset.h"
+#include "odm_precomp.h"
+
+/* Fw Array */
+#define Rtl8188E_FwImageArray Rtl8188EFwImgArray
+#define Rtl8188E_FWImgArrayLength Rtl8188EFWImgArrayLength
+
+#define RTL8188E_FW_UMC_IMG "/*(DEBLOBBED)*/"
+#define RTL8188E_PHY_REG "rtl8188E\\PHY_REG_1T.txt"
+#define RTL8188E_PHY_RADIO_A "rtl8188E\\radio_a_1T.txt"
+#define RTL8188E_PHY_RADIO_B "rtl8188E\\radio_b_1T.txt"
+#define RTL8188E_AGC_TAB "rtl8188E\\AGC_TAB_1T.txt"
+#define RTL8188E_PHY_MACREG "rtl8188E\\MAC_REG.txt"
+#define RTL8188E_PHY_REG_PG "rtl8188E\\PHY_REG_PG.txt"
+#define RTL8188E_PHY_REG_MP "rtl8188E\\PHY_REG_MP.txt"
+
+/* RTL8188E Power Configuration CMDs for USB/SDIO interfaces */
+#define Rtl8188E_NIC_PWR_ON_FLOW rtl8188E_power_on_flow
+#define Rtl8188E_NIC_RF_OFF_FLOW rtl8188E_radio_off_flow
+#define Rtl8188E_NIC_DISABLE_FLOW rtl8188E_card_disable_flow
+#define Rtl8188E_NIC_ENABLE_FLOW rtl8188E_card_enable_flow
+#define Rtl8188E_NIC_SUSPEND_FLOW rtl8188E_suspend_flow
+#define Rtl8188E_NIC_RESUME_FLOW rtl8188E_resume_flow
+#define Rtl8188E_NIC_PDN_FLOW rtl8188E_hwpdn_flow
+#define Rtl8188E_NIC_LPS_ENTER_FLOW rtl8188E_enter_lps_flow
+#define Rtl8188E_NIC_LPS_LEAVE_FLOW rtl8188E_leave_lps_flow
+
+#define DRVINFO_SZ 4 /* unit is 8bytes */
+#define PageNum_128(_Len) (u32)(((_Len)>>7) + ((_Len) & 0x7F ? 1 : 0))
+
+/* download firmware related data structure */
+#define FW_8188E_SIZE 0x4000 /* 16384,16k */
+#define FW_8188E_START_ADDRESS 0x1000
+#define FW_8188E_END_ADDRESS 0x1FFF /* 0x5FFF */
+
+#define MAX_PAGE_SIZE 4096 /* @ page : 4k bytes */
+
+#define IS_FW_HEADER_EXIST(_pFwHdr) \
+ ((le16_to_cpu(_pFwHdr->signature)&0xFFF0) == 0x92C0 || \
+ (le16_to_cpu(_pFwHdr->signature)&0xFFF0) == 0x88C0 || \
+ (le16_to_cpu(_pFwHdr->signature)&0xFFF0) == 0x2300 || \
+ (le16_to_cpu(_pFwHdr->signature)&0xFFF0) == 0x88E0)
+
+#define DRIVER_EARLY_INT_TIME 0x05
+#define BCN_DMA_ATIME_INT_TIME 0x02
+
+enum usb_rx_agg_mode {
+ USB_RX_AGG_DISABLE,
+ USB_RX_AGG_DMA,
+ USB_RX_AGG_USB,
+ USB_RX_AGG_MIX
+};
+
+#define MAX_RX_DMA_BUFFER_SIZE_88E \
+ 0x2400 /* 9k for 88E nornal chip , MaxRxBuff=10k-max(TxReportSize(64*8),
+ * WOLPattern(16*24)) */
+
+#define MAX_TX_REPORT_BUFFER_SIZE 0x0400 /* 1k */
+
+
+/* BK, BE, VI, VO, HCCA, MANAGEMENT, COMMAND, HIGH, BEACON. */
+#define MAX_TX_QUEUE 9
+
+#define TX_SELE_HQ BIT(0) /* High Queue */
+#define TX_SELE_LQ BIT(1) /* Low Queue */
+#define TX_SELE_NQ BIT(2) /* Normal Queue */
+
+/* Note: We will divide number of page equally for each queue other
+ * than public queue! */
+/* 22k = 22528 bytes = 176 pages (@page = 128 bytes) */
+/* must reserved about 7 pages for LPS => 176-7 = 169 (0xA9) */
+/* 2*BCN / 1*ps-poll / 1*null-data /1*prob_rsp /1*QOS null-data /1*BT QOS
+ * null-data */
+
+#define TX_TOTAL_PAGE_NUMBER_88E 0xA9/* 169 (21632=> 21k) */
+
+#define TX_PAGE_BOUNDARY_88E (TX_TOTAL_PAGE_NUMBER_88E + 1)
+
+/* Note: For Normal Chip Setting ,modify later */
+#define WMM_NORMAL_TX_TOTAL_PAGE_NUMBER \
+ TX_TOTAL_PAGE_NUMBER_88E /* 0xA9 , 0xb0=>176=>22k */
+#define WMM_NORMAL_TX_PAGE_BOUNDARY_88E \
+ (WMM_NORMAL_TX_TOTAL_PAGE_NUMBER + 1) /* 0xA9 */
+
+/* Chip specific */
+#define CHIP_BONDING_IDENTIFIER(_value) (((_value)>>22)&0x3)
+#define CHIP_BONDING_92C_1T2R 0x1
+#define CHIP_BONDING_88C_USB_MCARD 0x2
+#define CHIP_BONDING_88C_USB_HP 0x1
+#include "HalVerDef.h"
+#include "hal_com.h"
+
+/* Channel Plan */
+enum ChannelPlan {
+ CHPL_FCC = 0,
+ CHPL_IC = 1,
+ CHPL_ETSI = 2,
+ CHPL_SPA = 3,
+ CHPL_FRANCE = 4,
+ CHPL_MKK = 5,
+ CHPL_MKK1 = 6,
+ CHPL_ISRAEL = 7,
+ CHPL_TELEC = 8,
+ CHPL_GLOBAL = 9,
+ CHPL_WORLD = 10,
+};
+
+struct txpowerinfo24g {
+ u8 IndexCCK_Base[MAX_RF_PATH][MAX_CHNL_GROUP_24G];
+ u8 IndexBW40_Base[MAX_RF_PATH][MAX_CHNL_GROUP_24G];
+ /* If only one tx, only BW20 and OFDM are used. */
+ s8 CCK_Diff[MAX_RF_PATH][MAX_TX_COUNT];
+ s8 OFDM_Diff[MAX_RF_PATH][MAX_TX_COUNT];
+ s8 BW20_Diff[MAX_RF_PATH][MAX_TX_COUNT];
+ s8 BW40_Diff[MAX_RF_PATH][MAX_TX_COUNT];
+};
+
+#define EFUSE_REAL_CONTENT_LEN 512
+#define EFUSE_MAX_SECTION 16
+#define EFUSE_IC_ID_OFFSET 506 /* For some inferior IC purpose*/
+#define AVAILABLE_EFUSE_ADDR(addr) (addr < EFUSE_REAL_CONTENT_LEN)
+/* To prevent out of boundary programming case, */
+/* leave 1byte and program full section */
+/* 9bytes + 1byt + 5bytes and pre 1byte. */
+/* For worst case: */
+/* | 1byte|----8bytes----|1byte|--5bytes--| */
+/* | | Reserved(14bytes) | */
+
+/* PG data exclude header, dummy 6 bytes frome CP test and reserved 1byte. */
+#define EFUSE_OOB_PROTECT_BYTES 15
+
+#define HWSET_MAX_SIZE_88E 512
+
+#define EFUSE_REAL_CONTENT_LEN_88E 256
+#define EFUSE_MAP_LEN_88E 512
+#define EFUSE_MAP_LEN EFUSE_MAP_LEN_88E
+#define EFUSE_MAX_SECTION_88E 64
+#define EFUSE_MAX_WORD_UNIT_88E 4
+#define EFUSE_IC_ID_OFFSET_88E 506
+#define AVAILABLE_EFUSE_ADDR_88E(addr) \
+ (addr < EFUSE_REAL_CONTENT_LEN_88E)
+/* To prevent out of boundary programming case, leave 1byte and program
+ * full section */
+/* 9bytes + 1byt + 5bytes and pre 1byte. */
+/* For worst case: */
+/* | 2byte|----8bytes----|1byte|--7bytes--| 92D */
+/* PG data exclude header, dummy 7 bytes frome CP test and reserved 1byte. */
+#define EFUSE_OOB_PROTECT_BYTES_88E 18
+#define EFUSE_PROTECT_BYTES_BANK_88E 16
+
+/* EFUSE for BT definition */
+#define EFUSE_BT_REAL_CONTENT_LEN 1536 /* 512*3 */
+#define EFUSE_BT_MAP_LEN 1024 /* 1k bytes */
+#define EFUSE_BT_MAX_SECTION 128 /* 1024/8 */
+
+#define EFUSE_PROTECT_BYTES_BANK 16
+
+/* For RTL8723 WiFi/BT/GPS multi-function configuration. */
+enum rt_multi_func {
+ RT_MULTI_FUNC_NONE = 0x00,
+ RT_MULTI_FUNC_WIFI = 0x01,
+ RT_MULTI_FUNC_BT = 0x02,
+ RT_MULTI_FUNC_GPS = 0x04,
+};
+
+/* For RTL8723 regulator mode. */
+enum rt_regulator_mode {
+ RT_SWITCHING_REGULATOR = 0,
+ RT_LDO_REGULATOR = 1,
+};
+
+struct hal_data_8188e {
+ struct HAL_VERSION VersionID;
+ enum rt_regulator_mode RegulatorMode; /* switching regulator or LDO */
+ u16 CustomerID;
+ u8 *pfirmware;
+ u32 fwsize;
+ u16 FirmwareVersion;
+ u16 FirmwareVersionRev;
+ u16 FirmwareSubVersion;
+ u16 FirmwareSignature;
+ u8 PGMaxGroup;
+ /* current WIFI_PHY values */
+ u32 ReceiveConfig;
+ enum wireless_mode CurrentWirelessMode;
+ enum ht_channel_width CurrentChannelBW;
+ u8 CurrentChannel;
+ u8 nCur40MhzPrimeSC;/* Control channel sub-carrier */
+
+ u16 BasicRateSet;
+
+ /* rf_ctrl */
+ u8 rf_chip;
+ u8 rf_type;
+ u8 NumTotalRFPath;
+
+ u8 BoardType;
+
+ /* EEPROM setting. */
+ u16 EEPROMVID;
+ u16 EEPROMPID;
+ u16 EEPROMSVID;
+ u16 EEPROMSDID;
+ u8 EEPROMCustomerID;
+ u8 EEPROMSubCustomerID;
+ u8 EEPROMVersion;
+ u8 EEPROMRegulatory;
+
+ u8 bTXPowerDataReadFromEEPORM;
+ u8 EEPROMThermalMeter;
+ u8 bAPKThermalMeterIgnore;
+
+ bool EepromOrEfuse;
+ /* 92C:256bytes, 88E:512bytes, we use union set (512bytes) */
+ u8 EfuseMap[2][HWSET_MAX_SIZE_512];
+ u8 EfuseUsedPercentage;
+ struct efuse_hal EfuseHal;
+
+ u8 Index24G_CCK_Base[MAX_RF_PATH][CHANNEL_MAX_NUMBER];
+ u8 Index24G_BW40_Base[MAX_RF_PATH][CHANNEL_MAX_NUMBER];
+ /* If only one tx, only BW20 and OFDM are used. */
+ s8 CCK_24G_Diff[MAX_RF_PATH][MAX_TX_COUNT];
+ s8 OFDM_24G_Diff[MAX_RF_PATH][MAX_TX_COUNT];
+ s8 BW20_24G_Diff[MAX_RF_PATH][MAX_TX_COUNT];
+ s8 BW40_24G_Diff[MAX_RF_PATH][MAX_TX_COUNT];
+
+ u8 TxPwrLevelCck[RF_PATH_MAX][CHANNEL_MAX_NUMBER];
+ /* For HT 40MHZ pwr */
+ u8 TxPwrLevelHT40_1S[RF_PATH_MAX][CHANNEL_MAX_NUMBER];
+ /* For HT 40MHZ pwr */
+ u8 TxPwrLevelHT40_2S[RF_PATH_MAX][CHANNEL_MAX_NUMBER];
+ /* HT 20<->40 Pwr diff */
+ u8 TxPwrHt20Diff[RF_PATH_MAX][CHANNEL_MAX_NUMBER];
+ /* For HT<->legacy pwr diff */
+ u8 TxPwrLegacyHtDiff[RF_PATH_MAX][CHANNEL_MAX_NUMBER];
+ /* For power group */
+ u8 PwrGroupHT20[RF_PATH_MAX][CHANNEL_MAX_NUMBER];
+ u8 PwrGroupHT40[RF_PATH_MAX][CHANNEL_MAX_NUMBER];
+
+ u8 LegacyHTTxPowerDiff;/* Legacy to HT rate power diff */
+ /* The current Tx Power Level */
+ u8 CurrentCckTxPwrIdx;
+ u8 CurrentOfdm24GTxPwrIdx;
+ u8 CurrentBW2024GTxPwrIdx;
+ u8 CurrentBW4024GTxPwrIdx;
+
+
+ /* Read/write are allow for following hardware information variables */
+ u8 framesync;
+ u32 framesyncC34;
+ u8 framesyncMonitor;
+ u8 DefaultInitialGain[4];
+ u8 pwrGroupCnt;
+ u32 MCSTxPowerLevelOriginalOffset[MAX_PG_GROUP][16];
+ u32 CCKTxPowerLevelOriginalOffset;
+
+ u8 CrystalCap;
+ u32 AntennaTxPath; /* Antenna path Tx */
+ u32 AntennaRxPath; /* Antenna path Rx */
+ u8 BluetoothCoexist;
+ u8 ExternalPA;
+
+ u8 bLedOpenDrain; /* Open-drain support for controlling the LED.*/
+
+ u8 b1x1RecvCombine; /* for 1T1R receive combining */
+
+ u32 AcParam_BE; /* Original parameter for BE, use for EDCA turbo. */
+
+ struct bb_reg_def PHYRegDef[4]; /* Radio A/B/C/D */
+
+ u32 RfRegChnlVal[2];
+
+ /* RDG enable */
+ bool bRDGEnable;
+
+ /* for host message to fw */
+ u8 LastHMEBoxNum;
+
+ u8 fw_ractrl;
+ u8 RegTxPause;
+ /* Beacon function related global variable. */
+ u32 RegBcnCtrlVal;
+ u8 RegFwHwTxQCtrl;
+ u8 RegReg542;
+ u8 RegCR_1;
+
+ struct dm_priv dmpriv;
+ struct odm_dm_struct odmpriv;
+ struct sreset_priv srestpriv;
+
+ u8 CurAntenna;
+ u8 AntDivCfg;
+ u8 TRxAntDivType;
+
+
+ u8 bDumpRxPkt;/* for debug */
+ u8 bDumpTxPkt;/* for debug */
+ u8 FwRsvdPageStartOffset; /* Reserve page start offset except
+ * beacon in TxQ. */
+
+ /* 2010/08/09 MH Add CU power down mode. */
+ bool pwrdown;
+
+ /* Add for dual MAC 0--Mac0 1--Mac1 */
+ u32 interfaceIndex;
+
+ u8 OutEpQueueSel;
+ u8 OutEpNumber;
+
+ /* Add for USB aggreation mode dynamic shceme. */
+ bool UsbRxHighSpeedMode;
+
+ /* 2010/11/22 MH Add for slim combo debug mode selective. */
+ /* This is used for fix the drawback of CU TSMC-A/UMC-A cut.
+ * HW auto suspend ability. Close BT clock. */
+ bool SlimComboDbg;
+
+ u16 EfuseUsedBytes;
+
+ /* Auto FSM to Turn On, include clock, isolation, power control
+ * for MAC only */
+ u8 bMacPwrCtrlOn;
+
+ u32 UsbBulkOutSize;
+
+ /* Interrupt relatd register information. */
+ u32 IntArray[3];/* HISR0,HISR1,HSISR */
+ u32 IntrMask[3];
+ u8 C2hArray[16];
+ u8 UsbTxAggMode;
+ u8 UsbTxAggDescNum;
+ u16 HwRxPageSize; /* Hardware setting */
+ u32 MaxUsbRxAggBlock;
+
+ enum usb_rx_agg_mode UsbRxAggMode;
+ u8 UsbRxAggBlockCount; /* USB Block count. Block size is
+ * 512-byte in high speed and 64-byte
+ * in full speed */
+ u8 UsbRxAggBlockTimeout;
+ u8 UsbRxAggPageCount; /* 8192C DMA page count */
+ u8 UsbRxAggPageTimeout;
+};
+
+#define GET_HAL_DATA(__pAdapter) \
+ ((struct hal_data_8188e *)((__pAdapter)->HalData))
+#define GET_RF_TYPE(priv) (GET_HAL_DATA(priv)->rf_type)
+
+#define INCLUDE_MULTI_FUNC_BT(_Adapter) \
+ (GET_HAL_DATA(_Adapter)->MultiFunc & RT_MULTI_FUNC_BT)
+#define INCLUDE_MULTI_FUNC_GPS(_Adapter) \
+ (GET_HAL_DATA(_Adapter)->MultiFunc & RT_MULTI_FUNC_GPS)
+
+/* rtl8188e_hal_init.c */
+void _8051Reset88E(struct adapter *padapter);
+void rtl8188e_InitializeFirmwareVars(struct adapter *padapter);
+
+
+s32 InitLLTTable(struct adapter *padapter, u8 txpktbuf_bndy);
+
+/* EFuse */
+void Hal_InitPGData88E(struct adapter *padapter);
+void Hal_EfuseParseIDCode88E(struct adapter *padapter, u8 *hwinfo);
+void Hal_ReadTxPowerInfo88E(struct adapter *padapter, u8 *hwinfo,
+ bool AutoLoadFail);
+
+void Hal_EfuseParseEEPROMVer88E(struct adapter *padapter, u8 *hwinfo,
+ bool AutoLoadFail);
+void rtl8188e_EfuseParseChnlPlan(struct adapter *padapter, u8 *hwinfo,
+ bool AutoLoadFail);
+void Hal_EfuseParseCustomerID88E(struct adapter *padapter, u8 *hwinfo,
+ bool AutoLoadFail);
+void Hal_ReadAntennaDiversity88E(struct adapter *pAdapter, u8 *PROMContent,
+ bool AutoLoadFail);
+void Hal_ReadThermalMeter_88E(struct adapter *dapter, u8 *PROMContent,
+ bool AutoloadFail);
+void Hal_EfuseParseXtal_8188E(struct adapter *pAdapter, u8 *hwinfo,
+ bool AutoLoadFail);
+void Hal_EfuseParseBoardType88E(struct adapter *pAdapter, u8 *hwinfo,
+ bool AutoLoadFail);
+void Hal_ReadPowerSavingMode88E(struct adapter *pAdapter, u8 *hwinfo,
+ bool AutoLoadFail);
+
+void rtl8188e_set_hal_ops(struct hal_ops *pHalFunc);
+
+/* register */
+
+void rtl8188e_start_thread(struct adapter *padapter);
+void rtl8188e_stop_thread(struct adapter *padapter);
+
+s32 iol_execute(struct adapter *padapter, u8 control);
+void iol_mode_enable(struct adapter *padapter, u8 enable);
+s32 rtl8188e_iol_efuse_patch(struct adapter *padapter);
+void rtw_cancel_all_timer(struct adapter *padapter);
+
+#endif /* __RTL8188E_HAL_H__ */
diff --git a/drivers/staging/rtl8188eu/include/rtl8188e_led.h b/drivers/staging/rtl8188eu/include/rtl8188e_led.h
new file mode 100644
index 000000000..c0147e73c
--- /dev/null
+++ b/drivers/staging/rtl8188eu/include/rtl8188e_led.h
@@ -0,0 +1,35 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2011 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ *
+ ******************************************************************************/
+#ifndef __RTL8188E_LED_H__
+#define __RTL8188E_LED_H__
+
+#include <osdep_service.h>
+#include <drv_types.h>
+
+
+/* */
+/* Interface to manipulate LED objects. */
+/* */
+void rtl8188eu_InitSwLeds(struct adapter *padapter);
+void rtl8188eu_DeInitSwLeds(struct adapter *padapter);
+void SwLedOn(struct adapter *padapter, struct LED_871x *pLed);
+void SwLedOff(struct adapter *padapter, struct LED_871x *pLed);
+
+#endif
diff --git a/drivers/staging/rtl8188eu/include/rtl8188e_recv.h b/drivers/staging/rtl8188eu/include/rtl8188e_recv.h
new file mode 100644
index 000000000..5fed30d38
--- /dev/null
+++ b/drivers/staging/rtl8188eu/include/rtl8188e_recv.h
@@ -0,0 +1,69 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2011 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ *
+ ******************************************************************************/
+#ifndef __RTL8188E_RECV_H__
+#define __RTL8188E_RECV_H__
+
+#define TX_RPT1_PKT_LEN 8
+
+#define RECV_BLK_SZ 512
+#define RECV_BLK_CNT 16
+#define RECV_BLK_TH RECV_BLK_CNT
+#define RECV_BULK_IN_ADDR 0x80
+#define RECV_INT_IN_ADDR 0x81
+
+#define NR_PREALLOC_RECV_SKB (8)
+
+#define NR_RECVBUFF (4)
+
+#define MAX_RECVBUF_SZ (15360) /* 15k < 16k */
+
+struct phy_stat {
+ unsigned int phydw0;
+ unsigned int phydw1;
+ unsigned int phydw2;
+ unsigned int phydw3;
+ unsigned int phydw4;
+ unsigned int phydw5;
+ unsigned int phydw6;
+ unsigned int phydw7;
+};
+
+/* Rx smooth factor */
+#define Rx_Smooth_Factor (20)
+
+enum rx_packet_type {
+ NORMAL_RX,/* Normal rx packet */
+ TX_REPORT1,/* CCX */
+ TX_REPORT2,/* TX RPT */
+ HIS_REPORT,/* USB HISR RPT */
+};
+
+#define INTERRUPT_MSG_FORMAT_LEN 60
+s32 rtl8188eu_init_recv_priv(struct adapter *padapter);
+void rtl8188eu_free_recv_priv(struct adapter *padapter);
+void rtl8188eu_recv_hdl(struct adapter *padapter, struct recv_buf *precvbuf);
+void rtl8188eu_recv_tasklet(void *priv);
+void rtl8188e_query_rx_phy_status(struct recv_frame *fr, struct phy_stat *phy);
+void rtl8188e_process_phy_info(struct adapter *padapter, void *prframe);
+void update_recvframe_phyinfo_88e(struct recv_frame *fra, struct phy_stat *phy);
+void update_recvframe_attrib_88e(struct recv_frame *fra,
+ struct recv_stat *stat);
+
+#endif
diff --git a/drivers/staging/rtl8188eu/include/rtl8188e_spec.h b/drivers/staging/rtl8188eu/include/rtl8188e_spec.h
new file mode 100644
index 000000000..2c33eb30d
--- /dev/null
+++ b/drivers/staging/rtl8188eu/include/rtl8188e_spec.h
@@ -0,0 +1,1439 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2011 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ *******************************************************************************/
+#ifndef __RTL8188E_SPEC_H__
+#define __RTL8188E_SPEC_H__
+
+#ifndef BIT
+#define BIT(x) (1 << (x))
+#endif
+
+#define BIT0 0x00000001
+#define BIT1 0x00000002
+#define BIT2 0x00000004
+#define BIT3 0x00000008
+#define BIT4 0x00000010
+#define BIT5 0x00000020
+#define BIT6 0x00000040
+#define BIT7 0x00000080
+#define BIT8 0x00000100
+#define BIT9 0x00000200
+#define BIT10 0x00000400
+#define BIT11 0x00000800
+#define BIT12 0x00001000
+#define BIT13 0x00002000
+#define BIT14 0x00004000
+#define BIT15 0x00008000
+#define BIT16 0x00010000
+#define BIT17 0x00020000
+#define BIT18 0x00040000
+#define BIT19 0x00080000
+#define BIT20 0x00100000
+#define BIT21 0x00200000
+#define BIT22 0x00400000
+#define BIT23 0x00800000
+#define BIT24 0x01000000
+#define BIT25 0x02000000
+#define BIT26 0x04000000
+#define BIT27 0x08000000
+#define BIT28 0x10000000
+#define BIT29 0x20000000
+#define BIT30 0x40000000
+#define BIT31 0x80000000
+
+/* 8192C Regsiter offset definition */
+
+#define HAL_PS_TIMER_INT_DELAY 50 /* 50 microseconds */
+#define HAL_92C_NAV_UPPER_UNIT 128 /* micro-second */
+
+#define MAC_ADDR_LEN 6
+/* 8188E PKT_BUFF_ACCESS_CTRL value */
+#define TXPKT_BUF_SELECT 0x69
+#define RXPKT_BUF_SELECT 0xA5
+#define DISABLE_TRXPKT_BUF_ACCESS 0x0
+
+
+/* 0x0000h ~ 0x00FFh System Configuration */
+#define REG_SYS_ISO_CTRL 0x0000
+#define REG_SYS_FUNC_EN 0x0002
+#define REG_APS_FSMCO 0x0004
+#define REG_SYS_CLKR 0x0008
+#define REG_9346CR 0x000A
+#define REG_EE_VPD 0x000C
+#define REG_AFE_MISC 0x0010
+#define REG_SPS0_CTRL 0x0011
+#define REG_SPS_OCP_CFG 0x0018
+#define REG_RSV_CTRL 0x001C
+#define REG_RF_CTRL 0x001F
+#define REG_LDOA15_CTRL 0x0020
+#define REG_LDOV12D_CTRL 0x0021
+#define REG_LDOHCI12_CTRL 0x0022
+#define REG_LPLDO_CTRL 0x0023
+#define REG_AFE_XTAL_CTRL 0x0024
+#define REG_AFE_PLL_CTRL 0x0028
+#define REG_APE_PLL_CTRL_EXT 0x002c
+#define REG_EFUSE_CTRL 0x0030
+#define REG_EFUSE_TEST 0x0034
+#define REG_GPIO_MUXCFG 0x0040
+#define REG_GPIO_IO_SEL 0x0042
+#define REG_MAC_PINMUX_CFG 0x0043
+#define REG_GPIO_PIN_CTRL 0x0044
+#define REG_GPIO_INTM 0x0048
+#define REG_LEDCFG0 0x004C
+#define REG_LEDCFG1 0x004D
+#define REG_LEDCFG2 0x004E
+#define REG_LEDCFG3 0x004F
+#define REG_FSIMR 0x0050
+#define REG_FSISR 0x0054
+#define REG_HSIMR 0x0058
+#define REG_HSISR 0x005c
+#define REG_GPIO_PIN_CTRL_2 0x0060 /* RTL8723 WIFI/BT/GPS
+ * Multi-Function GPIO Pin Control. */
+#define REG_GPIO_IO_SEL_2 0x0062 /* RTL8723 WIFI/BT/GPS
+ * Multi-Function GPIO Select. */
+#define REG_BB_PAD_CTRL 0x0064
+#define REG_MULTI_FUNC_CTRL 0x0068 /* RTL8723 WIFI/BT/GPS
+ * Multi-Function control source. */
+#define REG_GPIO_OUTPUT 0x006c
+#define REG_AFE_XTAL_CTRL_EXT 0x0078 /* RTL8188E */
+#define REG_XCK_OUT_CTRL 0x007c /* RTL8188E */
+#define REG_MCUFWDL 0x0080
+#define REG_WOL_EVENT 0x0081 /* RTL8188E */
+#define REG_MCUTSTCFG 0x0084
+#define REG_HMEBOX_E0 0x0088
+#define REG_HMEBOX_E1 0x008A
+#define REG_HMEBOX_E2 0x008C
+#define REG_HMEBOX_E3 0x008E
+#define REG_HMEBOX_EXT_0 0x01F0
+#define REG_HMEBOX_EXT_1 0x01F4
+#define REG_HMEBOX_EXT_2 0x01F8
+#define REG_HMEBOX_EXT_3 0x01FC
+#define REG_HIMR_88E 0x00B0
+#define REG_HISR_88E 0x00B4
+#define REG_HIMRE_88E 0x00B8
+#define REG_HISRE_88E 0x00BC
+#define REG_EFUSE_ACCESS 0x00CF /* Efuse access protection
+ * for RTL8723 */
+#define REG_BIST_SCAN 0x00D0
+#define REG_BIST_RPT 0x00D4
+#define REG_BIST_ROM_RPT 0x00D8
+#define REG_USB_SIE_INTF 0x00E0
+#define REG_PCIE_MIO_INTF 0x00E4
+#define REG_PCIE_MIO_INTD 0x00E8
+#define REG_HPON_FSM 0x00EC
+#define REG_SYS_CFG 0x00F0
+#define REG_GPIO_OUTSTS 0x00F4 /* For RTL8723 only. */
+#define REG_TYPE_ID 0x00FC
+
+#define REG_MAC_PHY_CTRL_NORMAL 0x00f8
+
+/* 0x0100h ~ 0x01FFh MACTOP General Configuration */
+#define REG_CR 0x0100
+#define REG_PBP 0x0104
+#define REG_PKT_BUFF_ACCESS_CTRL 0x0106
+#define REG_TRXDMA_CTRL 0x010C
+#define REG_TRXFF_BNDY 0x0114
+#define REG_TRXFF_STATUS 0x0118
+#define REG_RXFF_PTR 0x011C
+/* define REG_HIMR 0x0120 */
+/* define REG_HISR 0x0124 */
+#define REG_HIMRE 0x0128
+#define REG_HISRE 0x012C
+#define REG_CPWM 0x012F
+#define REG_FWIMR 0x0130
+#define REG_FTIMR 0x0138
+#define REG_FWISR 0x0134
+#define REG_PKTBUF_DBG_CTRL 0x0140
+#define REG_PKTBUF_DBG_ADDR (REG_PKTBUF_DBG_CTRL)
+#define REG_RXPKTBUF_DBG (REG_PKTBUF_DBG_CTRL+2)
+#define REG_TXPKTBUF_DBG (REG_PKTBUF_DBG_CTRL+3)
+#define REG_RXPKTBUF_CTRL (REG_PKTBUF_DBG_CTRL+2)
+#define REG_PKTBUF_DBG_DATA_L 0x0144
+#define REG_PKTBUF_DBG_DATA_H 0x0148
+
+#define REG_TC0_CTRL 0x0150
+#define REG_TC1_CTRL 0x0154
+#define REG_TC2_CTRL 0x0158
+#define REG_TC3_CTRL 0x015C
+#define REG_TC4_CTRL 0x0160
+#define REG_TCUNIT_BASE 0x0164
+#define REG_MBIST_START 0x0174
+#define REG_MBIST_DONE 0x0178
+#define REG_MBIST_FAIL 0x017C
+#define REG_32K_CTRL 0x0194 /* RTL8188E */
+#define REG_C2HEVT_MSG_NORMAL 0x01A0
+#define REG_C2HEVT_CLEAR 0x01AF
+#define REG_MCUTST_1 0x01c0
+#define REG_FMETHR 0x01C8
+#define REG_HMETFR 0x01CC
+#define REG_HMEBOX_0 0x01D0
+#define REG_HMEBOX_1 0x01D4
+#define REG_HMEBOX_2 0x01D8
+#define REG_HMEBOX_3 0x01DC
+
+#define REG_LLT_INIT 0x01E0
+
+/* 0x0200h ~ 0x027Fh TXDMA Configuration */
+#define REG_RQPN 0x0200
+#define REG_FIFOPAGE 0x0204
+#define REG_TDECTRL 0x0208
+#define REG_TXDMA_OFFSET_CHK 0x020C
+#define REG_TXDMA_STATUS 0x0210
+#define REG_RQPN_NPQ 0x0214
+
+/* 0x0280h ~ 0x02FFh RXDMA Configuration */
+#define REG_RXDMA_AGG_PG_TH 0x0280
+#define REG_RXPKT_NUM 0x0284
+#define REG_RXDMA_STATUS 0x0288
+
+/* 0x0300h ~ 0x03FFh PCIe */
+#define REG_PCIE_CTRL_REG 0x0300
+#define REG_INT_MIG 0x0304 /* Interrupt Migration */
+#define REG_BCNQ_DESA 0x0308 /* TX Beacon Descr Address */
+#define REG_HQ_DESA 0x0310 /* TX High Queue Descr Addr */
+#define REG_MGQ_DESA 0x0318 /* TX Manage Queue Descr Addr*/
+#define REG_VOQ_DESA 0x0320 /* TX VO Queue Descr Addr */
+#define REG_VIQ_DESA 0x0328 /* TX VI Queue Descr Addr */
+#define REG_BEQ_DESA 0x0330 /* TX BE Queue Descr Addr */
+#define REG_BKQ_DESA 0x0338 /* TX BK Queue Descr Addr */
+#define REG_RX_DESA 0x0340 /* RX Queue Descr Addr */
+#define REG_MDIO 0x0354 /* MDIO for Access PCIE PHY */
+#define REG_DBG_SEL 0x0360 /* Debug Selection Register */
+#define REG_PCIE_HRPWM 0x0361 /* PCIe RPWM */
+#define REG_PCIE_HCPWM 0x0363 /* PCIe CPWM */
+#define REG_WATCH_DOG 0x0368
+
+/* RTL8723 series ------------------------------ */
+#define REG_PCIE_HISR 0x03A0
+
+/* spec version 11 */
+/* 0x0400h ~ 0x047Fh Protocol Configuration */
+#define REG_VOQ_INFORMATION 0x0400
+#define REG_VIQ_INFORMATION 0x0404
+#define REG_BEQ_INFORMATION 0x0408
+#define REG_BKQ_INFORMATION 0x040C
+#define REG_MGQ_INFORMATION 0x0410
+#define REG_HGQ_INFORMATION 0x0414
+#define REG_BCNQ_INFORMATION 0x0418
+#define REG_TXPKT_EMPTY 0x041A
+
+#define REG_CPU_MGQ_INFORMATION 0x041C
+#define REG_FWHW_TXQ_CTRL 0x0420
+#define REG_HWSEQ_CTRL 0x0423
+#define REG_TXPKTBUF_BCNQ_BDNY 0x0424
+#define REG_TXPKTBUF_MGQ_BDNY 0x0425
+#define REG_LIFETIME_EN 0x0426
+#define REG_MULTI_BCNQ_OFFSET 0x0427
+#define REG_SPEC_SIFS 0x0428
+#define REG_RL 0x042A
+#define REG_DARFRC 0x0430
+#define REG_RARFRC 0x0438
+#define REG_RRSR 0x0440
+#define REG_ARFR0 0x0444
+#define REG_ARFR1 0x0448
+#define REG_ARFR2 0x044C
+#define REG_ARFR3 0x0450
+#define REG_AGGLEN_LMT 0x0458
+#define REG_AMPDU_MIN_SPACE 0x045C
+#define REG_TXPKTBUF_WMAC_LBK_BF_HD 0x045D
+#define REG_FAST_EDCA_CTRL 0x0460
+#define REG_RD_RESP_PKT_TH 0x0463
+#define REG_INIRTS_RATE_SEL 0x0480
+/* define REG_INIDATA_RATE_SEL 0x0484 */
+#define REG_POWER_STATUS 0x04A4
+#define REG_POWER_STAGE1 0x04B4
+#define REG_POWER_STAGE2 0x04B8
+#define REG_PKT_VO_VI_LIFE_TIME 0x04C0
+#define REG_PKT_BE_BK_LIFE_TIME 0x04C2
+#define REG_STBC_SETTING 0x04C4
+#define REG_PROT_MODE_CTRL 0x04C8
+#define REG_MAX_AGGR_NUM 0x04CA
+#define REG_RTS_MAX_AGGR_NUM 0x04CB
+#define REG_BAR_MODE_CTRL 0x04CC
+#define REG_RA_TRY_RATE_AGG_LMT 0x04CF
+#define REG_EARLY_MODE_CONTROL 0x4D0
+#define REG_NQOS_SEQ 0x04DC
+#define REG_QOS_SEQ 0x04DE
+#define REG_NEED_CPU_HANDLE 0x04E0
+#define REG_PKT_LOSE_RPT 0x04E1
+#define REG_PTCL_ERR_STATUS 0x04E2
+#define REG_TX_RPT_CTRL 0x04EC
+#define REG_TX_RPT_TIME 0x04F0 /* 2 byte */
+#define REG_DUMMY 0x04FC
+
+/* 0x0500h ~ 0x05FFh EDCA Configuration */
+#define REG_EDCA_VO_PARAM 0x0500
+#define REG_EDCA_VI_PARAM 0x0504
+#define REG_EDCA_BE_PARAM 0x0508
+#define REG_EDCA_BK_PARAM 0x050C
+#define REG_BCNTCFG 0x0510
+#define REG_PIFS 0x0512
+#define REG_RDG_PIFS 0x0513
+#define REG_SIFS_CTX 0x0514
+#define REG_SIFS_TRX 0x0516
+#define REG_TSFTR_SYN_OFFSET 0x0518
+#define REG_AGGR_BREAK_TIME 0x051A
+#define REG_SLOT 0x051B
+#define REG_TX_PTCL_CTRL 0x0520
+#define REG_TXPAUSE 0x0522
+#define REG_DIS_TXREQ_CLR 0x0523
+#define REG_RD_CTRL 0x0524
+/* Format for offset 540h-542h: */
+/* [3:0]: TBTT prohibit setup in unit of 32us. The time for HW getting
+ * beacon content before TBTT. */
+/* [7:4]: Reserved. */
+/* [19:8]: TBTT prohibit hold in unit of 32us. The time for HW holding
+ * to send the beacon packet. */
+/* [23:20]: Reserved */
+/* Description: */
+/* | */
+/* |<--Setup--|--Hold------------>| */
+/* --------------|---------------------- */
+/* | */
+/* TBTT */
+/* Note: We cannot update beacon content to HW or send any AC packets during
+ * the time between Setup and Hold. */
+#define REG_TBTT_PROHIBIT 0x0540
+#define REG_RD_NAV_NXT 0x0544
+#define REG_NAV_PROT_LEN 0x0546
+#define REG_BCN_CTRL 0x0550
+#define REG_BCN_CTRL_1 0x0551
+#define REG_MBID_NUM 0x0552
+#define REG_DUAL_TSF_RST 0x0553
+#define REG_BCN_INTERVAL 0x0554
+#define REG_DRVERLYINT 0x0558
+#define REG_BCNDMATIM 0x0559
+#define REG_ATIMWND 0x055A
+#define REG_BCN_MAX_ERR 0x055D
+#define REG_RXTSF_OFFSET_CCK 0x055E
+#define REG_RXTSF_OFFSET_OFDM 0x055F
+#define REG_TSFTR 0x0560
+#define REG_TSFTR1 0x0568
+#define REG_ATIMWND_1 0x0570
+#define REG_PSTIMER 0x0580
+#define REG_TIMER0 0x0584
+#define REG_TIMER1 0x0588
+#define REG_ACMHWCTRL 0x05C0
+
+/* define REG_FW_TSF_SYNC_CNT 0x04A0 */
+#define REG_FW_RESET_TSF_CNT_1 0x05FC
+#define REG_FW_RESET_TSF_CNT_0 0x05FD
+#define REG_FW_BCN_DIS_CNT 0x05FE
+
+/* 0x0600h ~ 0x07FFh WMAC Configuration */
+#define REG_APSD_CTRL 0x0600
+#define REG_BWOPMODE 0x0603
+#define REG_TCR 0x0604
+#define REG_RCR 0x0608
+#define REG_RX_PKT_LIMIT 0x060C
+#define REG_RX_DLK_TIME 0x060D
+#define REG_RX_DRVINFO_SZ 0x060F
+
+#define REG_MACID 0x0610
+#define REG_BSSID 0x0618
+#define REG_MAR 0x0620
+#define REG_MBIDCAMCFG 0x0628
+
+#define REG_USTIME_EDCA 0x0638
+#define REG_MAC_SPEC_SIFS 0x063A
+
+/* 20100719 Joseph: Hardware register definition change. (HW datasheet v54) */
+/* [15:8]SIFS_R2T_OFDM, [7:0]SIFS_R2T_CCK */
+#define REG_R2T_SIFS 0x063C
+/* [15:8]SIFS_T2T_OFDM, [7:0]SIFS_T2T_CCK */
+#define REG_T2T_SIFS 0x063E
+#define REG_ACKTO 0x0640
+#define REG_CTS2TO 0x0641
+#define REG_EIFS 0x0642
+
+/* RXERR_RPT */
+#define RXERR_TYPE_OFDM_PPDU 0
+#define RXERR_TYPE_OFDM_false_ALARM 1
+#define RXERR_TYPE_OFDM_MPDU_OK 2
+#define RXERR_TYPE_OFDM_MPDU_FAIL 3
+#define RXERR_TYPE_CCK_PPDU 4
+#define RXERR_TYPE_CCK_false_ALARM 5
+#define RXERR_TYPE_CCK_MPDU_OK 6
+#define RXERR_TYPE_CCK_MPDU_FAIL 7
+#define RXERR_TYPE_HT_PPDU 8
+#define RXERR_TYPE_HT_false_ALARM 9
+#define RXERR_TYPE_HT_MPDU_TOTAL 10
+#define RXERR_TYPE_HT_MPDU_OK 11
+#define RXERR_TYPE_HT_MPDU_FAIL 12
+#define RXERR_TYPE_RX_FULL_DROP 15
+
+#define RXERR_COUNTER_MASK 0xFFFFF
+#define RXERR_RPT_RST BIT(27)
+#define _RXERR_RPT_SEL(type) ((type) << 28)
+
+/* Note: */
+/* The NAV upper value is very important to WiFi 11n 5.2.3 NAV test.
+ * The default value is always too small, but the WiFi TestPlan test
+ * by 25,000 microseconds of NAV through sending CTS in the air.
+ * We must update this value greater than 25,000 microseconds to pass
+ * the item. The offset of NAV_UPPER in 8192C Spec is incorrect, and
+ * the offset should be 0x0652. */
+#define REG_NAV_UPPER 0x0652 /* unit of 128 */
+
+/* WMA, BA, CCX */
+/* define REG_NAV_CTRL 0x0650 */
+#define REG_BACAMCMD 0x0654
+#define REG_BACAMCONTENT 0x0658
+#define REG_LBDLY 0x0660
+#define REG_FWDLY 0x0661
+#define REG_RXERR_RPT 0x0664
+#define REG_WMAC_TRXPTCL_CTL 0x0668
+
+/* Security */
+#define REG_CAMCMD 0x0670
+#define REG_CAMWRITE 0x0674
+#define REG_CAMREAD 0x0678
+#define REG_CAMDBG 0x067C
+#define REG_SECCFG 0x0680
+
+/* Power */
+#define REG_WOW_CTRL 0x0690
+#define REG_PS_RX_INFO 0x0692
+#define REG_UAPSD_TID 0x0693
+#define REG_WKFMCAM_CMD 0x0698
+#define REG_WKFMCAM_NUM_88E 0x698
+#define REG_RXFLTMAP0 0x06A0
+#define REG_RXFLTMAP1 0x06A2
+#define REG_RXFLTMAP2 0x06A4
+#define REG_BCN_PSR_RPT 0x06A8
+#define REG_BT_COEX_TABLE 0x06C0
+
+/* Hardware Port 2 */
+#define REG_MACID1 0x0700
+#define REG_BSSID1 0x0708
+
+/* 0xFE00h ~ 0xFE55h USB Configuration */
+#define REG_USB_INFO 0xFE17
+#define REG_USB_SPECIAL_OPTION 0xFE55
+#define REG_USB_DMA_AGG_TO 0xFE5B
+#define REG_USB_AGG_TO 0xFE5C
+#define REG_USB_AGG_TH 0xFE5D
+
+/* For normal chip */
+#define REG_NORMAL_SIE_VID 0xFE60 /* 0xFE60~0xFE61 */
+#define REG_NORMAL_SIE_PID 0xFE62 /* 0xFE62~0xFE63 */
+#define REG_NORMAL_SIE_OPTIONAL 0xFE64
+#define REG_NORMAL_SIE_EP 0xFE65 /* 0xFE65~0xFE67 */
+#define REG_NORMAL_SIE_PHY 0xFE68 /* 0xFE68~0xFE6B */
+#define REG_NORMAL_SIE_OPTIONAL2 0xFE6C
+#define REG_NORMAL_SIE_GPS_EP 0xFE6D /* 0xFE6D, for RTL8723 only. */
+#define REG_NORMAL_SIE_MAC_ADDR 0xFE70 /* 0xFE70~0xFE75 */
+#define REG_NORMAL_SIE_STRING 0xFE80 /* 0xFE80~0xFEDF */
+
+/* TODO: use these definition when using REG_xxx naming rule. */
+/* NOTE: DO NOT Remove these definition. Use later. */
+
+#define EFUSE_CTRL REG_EFUSE_CTRL /* E-Fuse Control. */
+#define EFUSE_TEST REG_EFUSE_TEST /* E-Fuse Test. */
+#define MSR (REG_CR + 2) /* Media Status reg */
+#define ISR REG_HISR_88E
+/* Timing Sync Function Timer Register. */
+#define TSFR REG_TSFTR
+
+#define PBP REG_PBP
+
+/* Redifine MACID register, to compatible prior ICs. */
+/* MAC ID Register, Offset 0x0050-0x0053 */
+#define IDR0 REG_MACID
+/* MAC ID Register, Offset 0x0054-0x0055 */
+#define IDR4 (REG_MACID + 4)
+
+/* 9. Security Control Registers (Offset: ) */
+/* IN 8190 Data Sheet is called CAMcmd */
+#define RWCAM REG_CAMCMD
+/* Software write CAM input content */
+#define WCAMI REG_CAMWRITE
+/* Software read/write CAM config */
+#define RCAMO REG_CAMREAD
+#define CAMDBG REG_CAMDBG
+/* Security Configuration Register */
+#define SECR REG_SECCFG
+
+/* Unused register */
+#define UnusedRegister 0x1BF
+#define DCAM UnusedRegister
+#define PSR UnusedRegister
+#define BBAddr UnusedRegister
+#define PhyDataR UnusedRegister
+
+/* Min Spacing related settings. */
+#define MAX_MSS_DENSITY_2T 0x13
+#define MAX_MSS_DENSITY_1T 0x0A
+
+/* EEPROM enable when set 1 */
+#define CmdEEPROM_En BIT5
+/* System EEPROM select, 0: boot from E-FUSE, 1: The EEPROM used is 9346 */
+#define CmdEERPOMSEL BIT4
+#define Cmd9346CR_9356SEL BIT4
+
+/* 8192C GPIO MUX Configuration Register (offset 0x40, 4 byte) */
+#define GPIOSEL_GPIO 0
+#define GPIOSEL_ENBT BIT5
+
+/* 8192C GPIO PIN Control Register (offset 0x44, 4 byte) */
+/* GPIO pins input value */
+#define GPIO_IN REG_GPIO_PIN_CTRL
+/* GPIO pins output value */
+#define GPIO_OUT (REG_GPIO_PIN_CTRL+1)
+/* GPIO pins output enable when a bit is set to "1"; otherwise,
+ * input is configured. */
+#define GPIO_IO_SEL (REG_GPIO_PIN_CTRL+2)
+#define GPIO_MOD (REG_GPIO_PIN_CTRL+3)
+
+/* 8723/8188E Host System Interrupt Mask Register (offset 0x58, 32 byte) */
+#define HSIMR_GPIO12_0_INT_EN BIT0
+#define HSIMR_SPS_OCP_INT_EN BIT5
+#define HSIMR_RON_INT_EN BIT6
+#define HSIMR_PDN_INT_EN BIT7
+#define HSIMR_GPIO9_INT_EN BIT25
+
+/* 8723/8188E Host System Interrupt Status Register (offset 0x5C, 32 byte) */
+#define HSISR_GPIO12_0_INT BIT0
+#define HSISR_SPS_OCP_INT BIT5
+#define HSISR_RON_INT_EN BIT6
+#define HSISR_PDNINT BIT7
+#define HSISR_GPIO9_INT BIT25
+
+/* 8192C (MSR) Media Status Register (Offset 0x4C, 8 bits) */
+/*
+Network Type
+00: No link
+01: Link in ad hoc network
+10: Link in infrastructure network
+11: AP mode
+Default: 00b.
+*/
+#define MSR_NOLINK 0x00
+#define MSR_ADHOC 0x01
+#define MSR_INFRA 0x02
+#define MSR_AP 0x03
+
+/* 88EU (MSR) Media Status Register (Offset 0x4C, 8 bits) */
+#define USB_INTR_CONTENT_C2H_OFFSET 0
+#define USB_INTR_CONTENT_CPWM1_OFFSET 16
+#define USB_INTR_CONTENT_CPWM2_OFFSET 20
+#define USB_INTR_CONTENT_HISR_OFFSET 48
+#define USB_INTR_CONTENT_HISRE_OFFSET 52
+
+/* 88E Driver Initialization Offload REG_FDHM0(Offset 0x88, 8 bits) */
+/* IOL config for REG_FDHM0(Reg0x88) */
+#define CMD_INIT_LLT BIT0
+#define CMD_READ_EFUSE_MAP BIT1
+#define CMD_EFUSE_PATCH BIT2
+#define CMD_IOCONFIG BIT3
+#define CMD_INIT_LLT_ERR BIT4
+#define CMD_READ_EFUSE_MAP_ERR BIT5
+#define CMD_EFUSE_PATCH_ERR BIT6
+#define CMD_IOCONFIG_ERR BIT7
+
+/* 6. Adaptive Control Registers (Offset: 0x0160 - 0x01CF) */
+/* 8192C Response Rate Set Register (offset 0x181, 24bits) */
+#define RRSR_1M BIT0
+#define RRSR_2M BIT1
+#define RRSR_5_5M BIT2
+#define RRSR_11M BIT3
+#define RRSR_6M BIT4
+#define RRSR_9M BIT5
+#define RRSR_12M BIT6
+#define RRSR_18M BIT7
+#define RRSR_24M BIT8
+#define RRSR_36M BIT9
+#define RRSR_48M BIT10
+#define RRSR_54M BIT11
+#define RRSR_MCS0 BIT12
+#define RRSR_MCS1 BIT13
+#define RRSR_MCS2 BIT14
+#define RRSR_MCS3 BIT15
+#define RRSR_MCS4 BIT16
+#define RRSR_MCS5 BIT17
+#define RRSR_MCS6 BIT18
+#define RRSR_MCS7 BIT19
+
+/* 8192C Response Rate Set Register (offset 0x1BF, 8bits) */
+/* WOL bit information */
+#define HAL92C_WOL_PTK_UPDATE_EVENT BIT0
+#define HAL92C_WOL_GTK_UPDATE_EVENT BIT1
+
+/* 8192C BW_OPMODE bits (Offset 0x203, 8bit) */
+#define BW_OPMODE_20MHZ BIT2
+#define BW_OPMODE_5G BIT1
+
+/* 8192C CAM Config Setting (offset 0x250, 1 byte) */
+#define CAM_VALID BIT15
+#define CAM_NOTVALID 0x0000
+#define CAM_USEDK BIT5
+
+#define CAM_CONTENT_COUNT 8
+
+#define CAM_NONE 0x0
+#define CAM_WEP40 0x01
+#define CAM_TKIP 0x02
+#define CAM_AES 0x04
+#define CAM_WEP104 0x05
+#define CAM_SMS4 0x6
+
+#define TOTAL_CAM_ENTRY 32
+#define HALF_CAM_ENTRY 16
+
+#define CAM_CONFIG_USEDK true
+#define CAM_CONFIG_NO_USEDK false
+
+#define CAM_WRITE BIT16
+#define CAM_READ 0x00000000
+#define CAM_POLLINIG BIT31
+
+#define SCR_UseDK 0x01
+#define SCR_TxSecEnable 0x02
+#define SCR_RxSecEnable 0x04
+
+/* 10. Power Save Control Registers (Offset: 0x0260 - 0x02DF) */
+#define WOW_PMEN BIT0 /* Power management Enable. */
+#define WOW_WOMEN BIT1 /* WoW function on or off. */
+#define WOW_MAGIC BIT2 /* Magic packet */
+#define WOW_UWF BIT3 /* Unicast Wakeup frame. */
+
+/* 12. Host Interrupt Status Registers (Offset: 0x0300 - 0x030F) */
+/* 8188 IMR/ISR bits */
+#define IMR_DISABLED_88E 0x0
+/* IMR DW0(0x0060-0063) Bit 0-31 */
+#define IMR_TXCCK_88E BIT30 /* TXRPT interrupt when CCX bit of the packet is set */
+#define IMR_PSTIMEOUT_88E BIT29 /* Power Save Time Out Interrupt */
+#define IMR_GTINT4_88E BIT28 /* When GTIMER4 expires, this bit is set to 1 */
+#define IMR_GTINT3_88E BIT27 /* When GTIMER3 expires, this bit is set to 1 */
+#define IMR_TBDER_88E BIT26 /* Transmit Beacon0 Error */
+#define IMR_TBDOK_88E BIT25 /* Transmit Beacon0 OK */
+#define IMR_TSF_BIT32_TOGGLE_88E BIT24 /* TSF Timer BIT32 toggle indication interrupt */
+#define IMR_BCNDMAINT0_88E BIT20 /* Beacon DMA Interrupt 0 */
+#define IMR_BCNDERR0_88E BIT16 /* Beacon Queue DMA Error 0 */
+#define IMR_HSISR_IND_ON_INT_88E BIT15 /* HSISR Indicator (HSIMR & HSISR is true, this bit is set to 1) */
+#define IMR_BCNDMAINT_E_88E BIT14 /* Beacon DMA Interrupt Extension for Win7 */
+#define IMR_ATIMEND_88E BIT12 /* CTWidnow End or ATIM Window End */
+#define IMR_HISR1_IND_INT_88E BIT11 /* HISR1 Indicator (HISR1 & HIMR1 is true, this bit is set to 1) */
+#define IMR_C2HCMD_88E BIT10 /* CPU to Host Command INT Status, Write 1 clear */
+#define IMR_CPWM2_88E BIT9 /* CPU power Mode exchange INT Status, Write 1 clear */
+#define IMR_CPWM_88E BIT8 /* CPU power Mode exchange INT Status, Write 1 clear */
+#define IMR_HIGHDOK_88E BIT7 /* High Queue DMA OK */
+#define IMR_MGNTDOK_88E BIT6 /* Management Queue DMA OK */
+#define IMR_BKDOK_88E BIT5 /* AC_BK DMA OK */
+#define IMR_BEDOK_88E BIT4 /* AC_BE DMA OK */
+#define IMR_VIDOK_88E BIT3 /* AC_VI DMA OK */
+#define IMR_VODOK_88E BIT2 /* AC_VO DMA OK */
+#define IMR_RDU_88E BIT1 /* Rx Descriptor Unavailable */
+#define IMR_ROK_88E BIT0 /* Receive DMA OK */
+
+/* IMR DW1(0x00B4-00B7) Bit 0-31 */
+#define IMR_BCNDMAINT7_88E BIT27 /* Beacon DMA Interrupt 7 */
+#define IMR_BCNDMAINT6_88E BIT26 /* Beacon DMA Interrupt 6 */
+#define IMR_BCNDMAINT5_88E BIT25 /* Beacon DMA Interrupt 5 */
+#define IMR_BCNDMAINT4_88E BIT24 /* Beacon DMA Interrupt 4 */
+#define IMR_BCNDMAINT3_88E BIT23 /* Beacon DMA Interrupt 3 */
+#define IMR_BCNDMAINT2_88E BIT22 /* Beacon DMA Interrupt 2 */
+#define IMR_BCNDMAINT1_88E BIT21 /* Beacon DMA Interrupt 1 */
+#define IMR_BCNDERR7_88E BIT20 /* Beacon DMA Error Int 7 */
+#define IMR_BCNDERR6_88E BIT19 /* Beacon DMA Error Int 6 */
+#define IMR_BCNDERR5_88E BIT18 /* Beacon DMA Error Int 5 */
+#define IMR_BCNDERR4_88E BIT17 /* Beacon DMA Error Int 4 */
+#define IMR_BCNDERR3_88E BIT16 /* Beacon DMA Error Int 3 */
+#define IMR_BCNDERR2_88E BIT15 /* Beacon DMA Error Int 2 */
+#define IMR_BCNDERR1_88E BIT14 /* Beacon DMA Error Int 1 */
+#define IMR_ATIMEND_E_88E BIT13 /* ATIM Window End Ext for Win7 */
+#define IMR_TXERR_88E BIT11 /* Tx Err Flag Int Status, write 1 clear. */
+#define IMR_RXERR_88E BIT10 /* Rx Err Flag INT Status, Write 1 clear */
+#define IMR_TXFOVW_88E BIT9 /* Transmit FIFO Overflow */
+#define IMR_RXFOVW_88E BIT8 /* Receive FIFO Overflow */
+
+#define HAL_NIC_UNPLUG_ISR 0xFFFFFFFF /* The value when the NIC is unplugged for PCI. */
+
+/* 8192C EFUSE */
+#define HWSET_MAX_SIZE 256
+#define HWSET_MAX_SIZE_88E 512
+
+/*===================================================================
+=====================================================================
+Here the register defines are for 92C. When the define is as same with 92C,
+we will use the 92C's define for the consistency
+So the following defines for 92C is not entire!!!!!!
+=====================================================================
+=====================================================================*/
+/*
+Based on Datasheet V33---090401
+Register Summary
+Current IOREG MAP
+0x0000h ~ 0x00FFh System Configuration (256 Bytes)
+0x0100h ~ 0x01FFh MACTOP General Configuration (256 Bytes)
+0x0200h ~ 0x027Fh TXDMA Configuration (128 Bytes)
+0x0280h ~ 0x02FFh RXDMA Configuration (128 Bytes)
+0x0300h ~ 0x03FFh PCIE EMAC Reserved Region (256 Bytes)
+0x0400h ~ 0x04FFh Protocol Configuration (256 Bytes)
+0x0500h ~ 0x05FFh EDCA Configuration (256 Bytes)
+0x0600h ~ 0x07FFh WMAC Configuration (512 Bytes)
+0x2000h ~ 0x3FFFh 8051 FW Download Region (8196 Bytes)
+*/
+/* 8192C (TXPAUSE) transmission pause (Offset 0x522, 8 bits) */
+/* Note: */
+/* The bits of stopping AC(VO/VI/BE/BK) queue in datasheet
+ * RTL8192S/RTL8192C are wrong, */
+/* the correct arragement is VO - Bit0, VI - Bit1, BE - Bit2,
+ * and BK - Bit3. */
+/* 8723 and 88E may be not correct either in the earlier version. */
+#define StopBecon BIT6
+#define StopHigh BIT5
+#define StopMgt BIT4
+#define StopBK BIT3
+#define StopBE BIT2
+#define StopVI BIT1
+#define StopVO BIT0
+
+/* 8192C (RCR) Receive Configuration Register(Offset 0x608, 32 bits) */
+#define RCR_APPFCS BIT31 /* WMAC append FCS after payload */
+#define RCR_APP_MIC BIT30
+#define RCR_APP_PHYSTS BIT28
+#define RCR_APP_ICV BIT29
+#define RCR_APP_PHYST_RXFF BIT28
+#define RCR_APP_BA_SSN BIT27 /* Accept BA SSN */
+#define RCR_ENMBID BIT24 /* Enable Multiple BssId. */
+#define RCR_LSIGEN BIT23
+#define RCR_MFBEN BIT22
+#define RCR_HTC_LOC_CTRL BIT14 /* MFC<--HTC=1 MFC-->HTC=0 */
+#define RCR_AMF BIT13 /* Accept management type frame */
+#define RCR_ACF BIT12 /* Accept control type frame */
+#define RCR_ADF BIT11 /* Accept data type frame */
+#define RCR_AICV BIT9 /* Accept ICV error packet */
+#define RCR_ACRC32 BIT8 /* Accept CRC32 error packet */
+#define RCR_CBSSID_BCN BIT7 /* Accept BSSID match packet
+ * (Rx beacon, probe rsp) */
+#define RCR_CBSSID_DATA BIT6 /* Accept BSSID match (Data)*/
+#define RCR_CBSSID RCR_CBSSID_DATA /* Accept BSSID match */
+#define RCR_APWRMGT BIT5 /* Accept power management pkt*/
+#define RCR_ADD3 BIT4 /* Accept address 3 match pkt */
+#define RCR_AB BIT3 /* Accept broadcast packet */
+#define RCR_AM BIT2 /* Accept multicast packet */
+#define RCR_APM BIT1 /* Accept physical match pkt */
+#define RCR_AAP BIT0 /* Accept all unicast packet */
+#define RCR_MXDMA_OFFSET 8
+#define RCR_FIFO_OFFSET 13
+
+/* 0xFE00h ~ 0xFE55h USB Configuration */
+#define REG_USB_INFO 0xFE17
+#define REG_USB_SPECIAL_OPTION 0xFE55
+#define REG_USB_DMA_AGG_TO 0xFE5B
+#define REG_USB_AGG_TO 0xFE5C
+#define REG_USB_AGG_TH 0xFE5D
+
+#define REG_USB_HRPWM 0xFE58
+#define REG_USB_HCPWM 0xFE57
+/* 8192C Regsiter Bit and Content definition */
+/* 0x0000h ~ 0x00FFh System Configuration */
+
+/* 2 SYS_ISO_CTRL */
+#define ISO_MD2PP BIT(0)
+#define ISO_UA2USB BIT(1)
+#define ISO_UD2CORE BIT(2)
+#define ISO_PA2PCIE BIT(3)
+#define ISO_PD2CORE BIT(4)
+#define ISO_IP2MAC BIT(5)
+#define ISO_DIOP BIT(6)
+#define ISO_DIOE BIT(7)
+#define ISO_EB2CORE BIT(8)
+#define ISO_DIOR BIT(9)
+#define PWC_EV12V BIT(15)
+
+/* 2 SYS_FUNC_EN */
+#define FEN_BBRSTB BIT(0)
+#define FEN_BB_GLB_RSTn BIT(1)
+#define FEN_USBA BIT(2)
+#define FEN_UPLL BIT(3)
+#define FEN_USBD BIT(4)
+#define FEN_DIO_PCIE BIT(5)
+#define FEN_PCIEA BIT(6)
+#define FEN_PPLL BIT(7)
+#define FEN_PCIED BIT(8)
+#define FEN_DIOE BIT(9)
+#define FEN_CPUEN BIT(10)
+#define FEN_DCORE BIT(11)
+#define FEN_ELDR BIT(12)
+#define FEN_DIO_RF BIT(13)
+#define FEN_HWPDN BIT(14)
+#define FEN_MREGEN BIT(15)
+
+/* 2 APS_FSMCO */
+#define PFM_LDALL BIT(0)
+#define PFM_ALDN BIT(1)
+#define PFM_LDKP BIT(2)
+#define PFM_WOWL BIT(3)
+#define EnPDN BIT(4)
+#define PDN_PL BIT(5)
+#define APFM_ONMAC BIT(8)
+#define APFM_OFF BIT(9)
+#define APFM_RSM BIT(10)
+#define AFSM_HSUS BIT(11)
+#define AFSM_PCIE BIT(12)
+#define APDM_MAC BIT(13)
+#define APDM_HOST BIT(14)
+#define APDM_HPDN BIT(15)
+#define RDY_MACON BIT(16)
+#define SUS_HOST BIT(17)
+#define ROP_ALD BIT(20)
+#define ROP_PWR BIT(21)
+#define ROP_SPS BIT(22)
+#define SOP_MRST BIT(25)
+#define SOP_FUSE BIT(26)
+#define SOP_ABG BIT(27)
+#define SOP_AMB BIT(28)
+#define SOP_RCK BIT(29)
+#define SOP_A8M BIT(30)
+#define XOP_BTCK BIT(31)
+
+/* 2 SYS_CLKR */
+#define ANAD16V_EN BIT(0)
+#define ANA8M BIT(1)
+#define MACSLP BIT(4)
+#define LOADER_CLK_EN BIT(5)
+
+/* 2 9346CR */
+
+#define BOOT_FROM_EEPROM BIT(4)
+#define EEPROM_EN BIT(5)
+
+/* 2 SPS0_CTRL */
+
+/* 2 SPS_OCP_CFG */
+
+/* 2 RF_CTRL */
+#define RF_EN BIT(0)
+#define RF_RSTB BIT(1)
+#define RF_SDMRSTB BIT(2)
+
+/* 2 LDOV12D_CTRL */
+#define LDV12_EN BIT(0)
+#define LDV12_SDBY BIT(1)
+#define LPLDO_HSM BIT(2)
+#define LPLDO_LSM_DIS BIT(3)
+#define _LDV12_VADJ(x) (((x) & 0xF) << 4)
+
+/* 2EFUSE_CTRL */
+#define ALD_EN BIT(18)
+#define EF_PD BIT(19)
+#define EF_FLAG BIT(31)
+
+/* 2 EFUSE_TEST (For RTL8723 partially) */
+#define EF_TRPT BIT(7)
+/* 00: Wifi Efuse, 01: BT Efuse0, 10: BT Efuse1, 11: BT Efuse2 */
+#define EF_CELL_SEL (BIT(8)|BIT(9))
+#define LDOE25_EN BIT(31)
+#define EFUSE_SEL(x) (((x) & 0x3) << 8)
+#define EFUSE_SEL_MASK 0x300
+#define EFUSE_WIFI_SEL_0 0x0
+#define EFUSE_BT_SEL_0 0x1
+#define EFUSE_BT_SEL_1 0x2
+#define EFUSE_BT_SEL_2 0x3
+
+#define EFUSE_ACCESS_ON 0x69 /* For RTL8723 only. */
+#define EFUSE_ACCESS_OFF 0x00 /* For RTL8723 only. */
+
+/* 2 8051FWDL */
+/* 2 MCUFWDL */
+#define MCUFWDL_EN BIT(0)
+#define MCUFWDL_RDY BIT(1)
+#define FWDL_ChkSum_rpt BIT(2)
+#define MACINI_RDY BIT(3)
+#define BBINI_RDY BIT(4)
+#define RFINI_RDY BIT(5)
+#define WINTINI_RDY BIT(6)
+#define RAM_DL_SEL BIT(7) /* 1:RAM, 0:ROM */
+#define ROM_DLEN BIT(19)
+#define CPRST BIT(23)
+
+/* 2 REG_SYS_CFG */
+#define XCLK_VLD BIT(0)
+#define ACLK_VLD BIT(1)
+#define UCLK_VLD BIT(2)
+#define PCLK_VLD BIT(3)
+#define PCIRSTB BIT(4)
+#define V15_VLD BIT(5)
+#define SW_OFFLOAD_EN BIT(7)
+#define SIC_IDLE BIT(8)
+#define BD_MAC2 BIT(9)
+#define BD_MAC1 BIT(10)
+#define IC_MACPHY_MODE BIT(11)
+#define CHIP_VER (BIT(12)|BIT(13)|BIT(14)|BIT(15))
+#define BT_FUNC BIT(16)
+#define VENDOR_ID BIT(19)
+#define PAD_HWPD_IDN BIT(22)
+#define TRP_VAUX_EN BIT(23) /* RTL ID */
+#define TRP_BT_EN BIT(24)
+#define BD_PKG_SEL BIT(25)
+#define BD_HCI_SEL BIT(26)
+#define TYPE_ID BIT(27)
+
+#define CHIP_VER_RTL_MASK 0xF000 /* Bit 12 ~ 15 */
+#define CHIP_VER_RTL_SHIFT 12
+
+/* 2REG_GPIO_OUTSTS (For RTL8723 only) */
+#define EFS_HCI_SEL (BIT(0)|BIT(1))
+#define PAD_HCI_SEL (BIT(2)|BIT(3))
+#define HCI_SEL (BIT(4)|BIT(5))
+#define PKG_SEL_HCI BIT(6)
+#define FEN_GPS BIT(7)
+#define FEN_BT BIT(8)
+#define FEN_WL BIT(9)
+#define FEN_PCI BIT(10)
+#define FEN_USB BIT(11)
+#define BTRF_HWPDN_N BIT(12)
+#define WLRF_HWPDN_N BIT(13)
+#define PDN_BT_N BIT(14)
+#define PDN_GPS_N BIT(15)
+#define BT_CTL_HWPDN BIT(16)
+#define GPS_CTL_HWPDN BIT(17)
+#define PPHY_SUSB BIT(20)
+#define UPHY_SUSB BIT(21)
+#define PCI_SUSEN BIT(22)
+#define USB_SUSEN BIT(23)
+#define RF_RL_ID (BIT(31)|BIT(30)|BIT(29)|BIT(28))
+
+/* 2SYS_CFG */
+#define RTL_ID BIT(23) /* TestChip ID, 1:Test(RLE); 0:MP(RL) */
+
+/* 0x0100h ~ 0x01FFh MACTOP General Configuration */
+
+/* 2 Function Enable Registers */
+/* 2 CR */
+
+#define HCI_TXDMA_EN BIT(0)
+#define HCI_RXDMA_EN BIT(1)
+#define TXDMA_EN BIT(2)
+#define RXDMA_EN BIT(3)
+#define PROTOCOL_EN BIT(4)
+#define SCHEDULE_EN BIT(5)
+#define MACTXEN BIT(6)
+#define MACRXEN BIT(7)
+#define ENSWBCN BIT(8)
+#define ENSEC BIT(9)
+#define CALTMR_EN BIT(10) /* 32k CAL TMR enable */
+
+/* Network type */
+#define _NETTYPE(x) (((x) & 0x3) << 16)
+#define MASK_NETTYPE 0x30000
+#define NT_NO_LINK 0x0
+#define NT_LINK_AD_HOC 0x1
+#define NT_LINK_AP 0x2
+#define NT_AS_AP 0x3
+
+/* 2 PBP - Page Size Register */
+#define GET_RX_PAGE_SIZE(value) ((value) & 0xF)
+#define GET_TX_PAGE_SIZE(value) (((value) & 0xF0) >> 4)
+#define _PSRX_MASK 0xF
+#define _PSTX_MASK 0xF0
+#define _PSRX(x) (x)
+#define _PSTX(x) ((x) << 4)
+
+#define PBP_64 0x0
+#define PBP_128 0x1
+#define PBP_256 0x2
+#define PBP_512 0x3
+#define PBP_1024 0x4
+
+/* 2 TX/RXDMA */
+#define RXDMA_ARBBW_EN BIT(0)
+#define RXSHFT_EN BIT(1)
+#define RXDMA_AGG_EN BIT(2)
+#define QS_VO_QUEUE BIT(8)
+#define QS_VI_QUEUE BIT(9)
+#define QS_BE_QUEUE BIT(10)
+#define QS_BK_QUEUE BIT(11)
+#define QS_MANAGER_QUEUE BIT(12)
+#define QS_HIGH_QUEUE BIT(13)
+
+#define HQSEL_VOQ BIT(0)
+#define HQSEL_VIQ BIT(1)
+#define HQSEL_BEQ BIT(2)
+#define HQSEL_BKQ BIT(3)
+#define HQSEL_MGTQ BIT(4)
+#define HQSEL_HIQ BIT(5)
+
+/* For normal driver, 0x10C */
+#define _TXDMA_HIQ_MAP(x) (((x)&0x3) << 14)
+#define _TXDMA_MGQ_MAP(x) (((x)&0x3) << 12)
+#define _TXDMA_BKQ_MAP(x) (((x)&0x3) << 10)
+#define _TXDMA_BEQ_MAP(x) (((x)&0x3) << 8)
+#define _TXDMA_VIQ_MAP(x) (((x)&0x3) << 6)
+#define _TXDMA_VOQ_MAP(x) (((x)&0x3) << 4)
+
+#define QUEUE_LOW 1
+#define QUEUE_NORMAL 2
+#define QUEUE_HIGH 3
+
+/* 2 TRXFF_BNDY */
+
+/* 2 LLT_INIT */
+#define _LLT_NO_ACTIVE 0x0
+#define _LLT_WRITE_ACCESS 0x1
+#define _LLT_READ_ACCESS 0x2
+
+#define _LLT_INIT_DATA(x) ((x) & 0xFF)
+#define _LLT_INIT_ADDR(x) (((x) & 0xFF) << 8)
+#define _LLT_OP(x) (((x) & 0x3) << 30)
+#define _LLT_OP_VALUE(x) (((x) >> 30) & 0x3)
+
+/* 0x0200h ~ 0x027Fh TXDMA Configuration */
+/* 2RQPN */
+#define _HPQ(x) ((x) & 0xFF)
+#define _LPQ(x) (((x) & 0xFF) << 8)
+#define _PUBQ(x) (((x) & 0xFF) << 16)
+/* NOTE: in RQPN_NPQ register */
+#define _NPQ(x) ((x) & 0xFF)
+
+#define HPQ_PUBLIC_DIS BIT(24)
+#define LPQ_PUBLIC_DIS BIT(25)
+#define LD_RQPN BIT(31)
+
+/* 2TDECTRL */
+#define BCN_VALID BIT(16)
+#define BCN_HEAD(x) (((x) & 0xFF) << 8)
+#define BCN_HEAD_MASK 0xFF00
+
+/* 2 TDECTL */
+#define BLK_DESC_NUM_SHIFT 4
+#define BLK_DESC_NUM_MASK 0xF
+
+/* 2 TXDMA_OFFSET_CHK */
+#define DROP_DATA_EN BIT(9)
+
+/* 0x0280h ~ 0x028Bh RX DMA Configuration */
+
+/* REG_RXDMA_CONTROL, 0x0286h */
+
+/* 2 REG_RXPKT_NUM, 0x0284 */
+#define RXPKT_RELEASE_POLL BIT(16)
+#define RXDMA_IDLE BIT(17)
+#define RW_RELEASE_EN BIT(18)
+
+/* 0x0400h ~ 0x047Fh Protocol Configuration */
+/* 2 FWHW_TXQ_CTRL */
+#define EN_AMPDU_RTY_NEW BIT(7)
+
+/* 2 SPEC SIFS */
+#define _SPEC_SIFS_CCK(x) ((x) & 0xFF)
+#define _SPEC_SIFS_OFDM(x) (((x) & 0xFF) << 8)
+
+/* 2 RL */
+#define RETRY_LIMIT_SHORT_SHIFT 8
+#define RETRY_LIMIT_LONG_SHIFT 0
+
+/* 0x0500h ~ 0x05FFh EDCA Configuration */
+
+/* 2 EDCA setting */
+#define AC_PARAM_TXOP_LIMIT_OFFSET 16
+#define AC_PARAM_ECW_MAX_OFFSET 12
+#define AC_PARAM_ECW_MIN_OFFSET 8
+#define AC_PARAM_AIFS_OFFSET 0
+
+#define _LRL(x) ((x) & 0x3F)
+#define _SRL(x) (((x) & 0x3F) << 8)
+
+/* 2 BCN_CTRL */
+#define EN_MBSSID BIT(1)
+#define EN_TXBCN_RPT BIT(2)
+#define EN_BCN_FUNCTION BIT(3)
+#define DIS_TSF_UPDATE BIT(3)
+
+/* The same function but different bit field. */
+#define DIS_TSF_UDT0_NORMAL_CHIP BIT(4)
+#define DIS_TSF_UDT0_TEST_CHIP BIT(5)
+#define STOP_BCNQ BIT(6)
+
+/* 2 ACMHWCTRL */
+#define AcmHw_HwEn BIT(0)
+#define AcmHw_BeqEn BIT(1)
+#define AcmHw_ViqEn BIT(2)
+#define AcmHw_VoqEn BIT(3)
+#define AcmHw_BeqStatus BIT(4)
+#define AcmHw_ViqStatus BIT(5)
+#define AcmHw_VoqStatus BIT(6)
+
+/* 0x0600h ~ 0x07FFh WMAC Configuration */
+/* 2APSD_CTRL */
+#define APSDOFF BIT(6)
+#define APSDOFF_STATUS BIT(7)
+
+#define RATE_BITMAP_ALL 0xFFFFF
+
+/* Only use CCK 1M rate for ACK */
+#define RATE_RRSR_CCK_ONLY_1M 0xFFFF1
+
+/* 2 TCR */
+#define TSFRST BIT(0)
+#define DIS_GCLK BIT(1)
+#define PAD_SEL BIT(2)
+#define PWR_ST BIT(6)
+#define PWRBIT_OW_EN BIT(7)
+#define ACRC BIT(8)
+#define CFENDFORM BIT(9)
+#define ICV BIT(10)
+
+/* 2 RCR */
+#define AAP BIT(0)
+#define APM BIT(1)
+#define AM BIT(2)
+#define AB BIT(3)
+#define ADD3 BIT(4)
+#define APWRMGT BIT(5)
+#define CBSSID BIT(6)
+#define CBSSID_DATA BIT(6)
+#define CBSSID_BCN BIT(7)
+#define ACRC32 BIT(8)
+#define AICV BIT(9)
+#define ADF BIT(11)
+#define ACF BIT(12)
+#define AMF BIT(13)
+#define HTC_LOC_CTRL BIT(14)
+#define UC_DATA_EN BIT(16)
+#define BM_DATA_EN BIT(17)
+#define MFBEN BIT(22)
+#define LSIGEN BIT(23)
+#define EnMBID BIT(24)
+#define APP_BASSN BIT(27)
+#define APP_PHYSTS BIT(28)
+#define APP_ICV BIT(29)
+#define APP_MIC BIT(30)
+#define APP_FCS BIT(31)
+
+/* 2 SECCFG */
+#define SCR_TxUseDK BIT(0) /* Force Tx Use Default Key */
+#define SCR_RxUseDK BIT(1) /* Force Rx Use Default Key */
+#define SCR_TxEncEnable BIT(2) /* Enable Tx Encryption */
+#define SCR_RxDecEnable BIT(3) /* Enable Rx Decryption */
+#define SCR_SKByA2 BIT(4) /* Search kEY BY A2 */
+#define SCR_NoSKMC BIT(5) /* No Key Search Multicast */
+#define SCR_TXBCUSEDK BIT(6) /* Force Tx Bcast pkt Use Default Key */
+#define SCR_RXBCUSEDK BIT(7) /* Force Rx Bcast pkt Use Default Key */
+
+/* RTL8188E SDIO Configuration */
+
+/* I/O bus domain address mapping */
+#define SDIO_LOCAL_BASE 0x10250000
+#define WLAN_IOREG_BASE 0x10260000
+#define FIRMWARE_FIFO_BASE 0x10270000
+#define TX_HIQ_BASE 0x10310000
+#define TX_MIQ_BASE 0x10320000
+#define TX_LOQ_BASE 0x10330000
+#define RX_RX0FF_BASE 0x10340000
+
+/* SDIO host local register space mapping. */
+#define SDIO_LOCAL_MSK 0x0FFF
+#define WLAN_IOREG_MSK 0x7FFF
+#define WLAN_FIFO_MSK 0x1FFF /* Aggregation Length[12:0] */
+#define WLAN_RX0FF_MSK 0x0003
+
+/* Without ref to the SDIO Device ID */
+#define SDIO_WITHOUT_REF_DEVICE_ID 0
+#define SDIO_LOCAL_DEVICE_ID 0 /* 0b[16], 000b[15:13] */
+#define WLAN_TX_HIQ_DEVICE_ID 4 /* 0b[16], 100b[15:13] */
+#define WLAN_TX_MIQ_DEVICE_ID 5 /* 0b[16], 101b[15:13] */
+#define WLAN_TX_LOQ_DEVICE_ID 6 /* 0b[16], 110b[15:13] */
+#define WLAN_RX0FF_DEVICE_ID 7 /* 0b[16], 111b[15:13] */
+#define WLAN_IOREG_DEVICE_ID 8 /* 1b[16] */
+
+/* SDIO Tx Free Page Index */
+#define HI_QUEUE_IDX 0
+#define MID_QUEUE_IDX 1
+#define LOW_QUEUE_IDX 2
+#define PUBLIC_QUEUE_IDX 3
+
+#define SDIO_MAX_TX_QUEUE 3 /* HIQ, MIQ and LOQ */
+#define SDIO_MAX_RX_QUEUE 1
+
+/* SDIO Tx Control */
+#define SDIO_REG_TX_CTRL 0x0000
+/* SDIO Host Interrupt Mask */
+#define SDIO_REG_HIMR 0x0014
+/* SDIO Host Interrupt Service Routine */
+#define SDIO_REG_HISR 0x0018
+/* HCI Current Power Mode */
+#define SDIO_REG_HCPWM 0x0019
+/* RXDMA Request Length */
+#define SDIO_REG_RX0_REQ_LEN 0x001C
+/* Free Tx Buffer Page */
+#define SDIO_REG_FREE_TXPG 0x0020
+/* HCI Current Power Mode 1 */
+#define SDIO_REG_HCPWM1 0x0024
+/* HCI Current Power Mode 2 */
+#define SDIO_REG_HCPWM2 0x0026
+/* HTSF Informaion */
+#define SDIO_REG_HTSFR_INFO 0x0030
+/* HCI Request Power Mode 1 */
+#define SDIO_REG_HRPWM1 0x0080
+/* HCI Request Power Mode 2 */
+#define SDIO_REG_HRPWM2 0x0082
+/* HCI Power Save Clock */
+#define SDIO_REG_HPS_CLKR 0x0084
+/* SDIO HCI Suspend Control */
+#define SDIO_REG_HSUS_CTRL 0x0086
+/* SDIO Host Extension Interrupt Mask Always */
+#define SDIO_REG_HIMR_ON 0x0090
+/* SDIO Host Extension Interrupt Status Always */
+#define SDIO_REG_HISR_ON 0x0091
+
+#define SDIO_HIMR_DISABLED 0
+
+/* RTL8188E SDIO Host Interrupt Mask Register */
+#define SDIO_HIMR_RX_REQUEST_MSK BIT0
+#define SDIO_HIMR_AVAL_MSK BIT1
+#define SDIO_HIMR_TXERR_MSK BIT2
+#define SDIO_HIMR_RXERR_MSK BIT3
+#define SDIO_HIMR_TXFOVW_MSK BIT4
+#define SDIO_HIMR_RXFOVW_MSK BIT5
+#define SDIO_HIMR_TXBCNOK_MSK BIT6
+#define SDIO_HIMR_TXBCNERR_MSK BIT7
+#define SDIO_HIMR_BCNERLY_INT_MSK BIT16
+#define SDIO_HIMR_C2HCMD_MSK BIT17
+#define SDIO_HIMR_CPWM1_MSK BIT18
+#define SDIO_HIMR_CPWM2_MSK BIT19
+#define SDIO_HIMR_HSISR_IND_MSK BIT20
+#define SDIO_HIMR_GTINT3_IND_MSK BIT21
+#define SDIO_HIMR_GTINT4_IND_MSK BIT22
+#define SDIO_HIMR_PSTIMEOUT_MSK BIT23
+#define SDIO_HIMR_OCPINT_MSK BIT24
+#define SDIO_HIMR_ATIMEND_MSK BIT25
+#define SDIO_HIMR_ATIMEND_E_MSK BIT26
+#define SDIO_HIMR_CTWEND_MSK BIT27
+
+/* RTL8188E SDIO Specific */
+#define SDIO_HIMR_MCU_ERR_MSK BIT28
+#define SDIO_HIMR_TSF_BIT32_TOGGLE_MSK BIT29
+
+/* SDIO Host Interrupt Service Routine */
+#define SDIO_HISR_RX_REQUEST BIT0
+#define SDIO_HISR_AVAL BIT1
+#define SDIO_HISR_TXERR BIT2
+#define SDIO_HISR_RXERR BIT3
+#define SDIO_HISR_TXFOVW BIT4
+#define SDIO_HISR_RXFOVW BIT5
+#define SDIO_HISR_TXBCNOK BIT6
+#define SDIO_HISR_TXBCNERR BIT7
+#define SDIO_HISR_BCNERLY_INT BIT16
+#define SDIO_HISR_C2HCMD BIT17
+#define SDIO_HISR_CPWM1 BIT18
+#define SDIO_HISR_CPWM2 BIT19
+#define SDIO_HISR_HSISR_IND BIT20
+#define SDIO_HISR_GTINT3_IND BIT21
+#define SDIO_HISR_GTINT4_IND BIT22
+#define SDIO_HISR_PSTIME BIT23
+#define SDIO_HISR_OCPINT BIT24
+#define SDIO_HISR_ATIMEND BIT25
+#define SDIO_HISR_ATIMEND_E BIT26
+#define SDIO_HISR_CTWEND BIT27
+
+/* RTL8188E SDIO Specific */
+#define SDIO_HISR_MCU_ERR BIT28
+#define SDIO_HISR_TSF_BIT32_TOGGLE BIT29
+
+#define MASK_SDIO_HISR_CLEAR \
+ (SDIO_HISR_TXERR | SDIO_HISR_RXERR | SDIO_HISR_TXFOVW |\
+ SDIO_HISR_RXFOVW | SDIO_HISR_TXBCNOK | SDIO_HISR_TXBCNERR |\
+ SDIO_HISR_C2HCMD | SDIO_HISR_CPWM1 | SDIO_HISR_CPWM2 |\
+ SDIO_HISR_HSISR_IND | SDIO_HISR_GTINT3_IND | SDIO_HISR_GTINT4_IND |\
+ SDIO_HISR_PSTIMEOUT | SDIO_HISR_OCPINT)
+
+/* SDIO HCI Suspend Control Register */
+#define HCI_RESUME_PWR_RDY BIT1
+#define HCI_SUS_CTRL BIT0
+
+/* SDIO Tx FIFO related */
+/* The number of Tx FIFO free page */
+#define SDIO_TX_FREE_PG_QUEUE 4
+#define SDIO_TX_FIFO_PAGE_SZ 128
+
+/* 0xFE00h ~ 0xFE55h USB Configuration */
+
+/* 2 USB Information (0xFE17) */
+#define USB_IS_HIGH_SPEED 0
+#define USB_IS_FULL_SPEED 1
+#define USB_SPEED_MASK BIT(5)
+
+#define USB_NORMAL_SIE_EP_MASK 0xF
+#define USB_NORMAL_SIE_EP_SHIFT 4
+
+/* 2 Special Option */
+#define USB_AGG_EN BIT(3)
+
+/* 0; Use interrupt endpoint to upload interrupt pkt */
+/* 1; Use bulk endpoint to upload interrupt pkt, */
+#define INT_BULK_SEL BIT(4)
+
+/* 2REG_C2HEVT_CLEAR */
+/* Set by driver and notify FW that the driver has read
+ * the C2H command message */
+#define C2H_EVT_HOST_CLOSE 0x00
+/* Set by FW indicating that FW had set the C2H command
+ * message and it's not yet read by driver. */
+#define C2H_EVT_FW_CLOSE 0xFF
+
+/* 2REG_MULTI_FUNC_CTRL(For RTL8723 Only) */
+/* Enable GPIO[9] as WiFi HW PDn source */
+#define WL_HWPDN_EN BIT0
+/* WiFi HW PDn polarity control */
+#define WL_HWPDN_SL BIT1
+/* WiFi function enable */
+#define WL_FUNC_EN BIT2
+/* Enable GPIO[9] as WiFi RF HW PDn source */
+#define WL_HWROF_EN BIT3
+/* Enable GPIO[11] as BT HW PDn source */
+#define BT_HWPDN_EN BIT16
+/* BT HW PDn polarity control */
+#define BT_HWPDN_SL BIT17
+/* BT function enable */
+#define BT_FUNC_EN BIT18
+/* Enable GPIO[11] as BT/GPS RF HW PDn source */
+#define BT_HWROF_EN BIT19
+/* Enable GPIO[10] as GPS HW PDn source */
+#define GPS_HWPDN_EN BIT20
+/* GPS HW PDn polarity control */
+#define GPS_HWPDN_SL BIT21
+/* GPS function enable */
+#define GPS_FUNC_EN BIT22
+
+/* 3 REG_LIFECTRL_CTRL */
+#define HAL92C_EN_PKT_LIFE_TIME_BK BIT3
+#define HAL92C_EN_PKT_LIFE_TIME_BE BIT2
+#define HAL92C_EN_PKT_LIFE_TIME_VI BIT1
+#define HAL92C_EN_PKT_LIFE_TIME_VO BIT0
+
+#define HAL92C_MSDU_LIFE_TIME_UNIT 128 /* in us */
+
+/* General definitions */
+#define LAST_ENTRY_OF_TX_PKT_BUFFER 176 /* 22k 22528 bytes */
+
+#define POLLING_LLT_THRESHOLD 20
+#define POLLING_READY_TIMEOUT_COUNT 1000
+/* GPIO BIT */
+#define HAL_8192C_HW_GPIO_WPS_BIT BIT2
+
+/* 8192C EEPROM/EFUSE share register definition. */
+
+/* EEPROM/Efuse PG Offset for 88EE/88EU/88ES */
+#define EEPROM_TX_PWR_INX_88E 0x10
+
+#define EEPROM_ChannelPlan_88E 0xB8
+#define EEPROM_XTAL_88E 0xB9
+#define EEPROM_THERMAL_METER_88E 0xBA
+#define EEPROM_IQK_LCK_88E 0xBB
+
+#define EEPROM_RF_BOARD_OPTION_88E 0xC1
+#define EEPROM_RF_FEATURE_OPTION_88E 0xC2
+#define EEPROM_RF_BT_SETTING_88E 0xC3
+#define EEPROM_VERSION_88E 0xC4
+#define EEPROM_CUSTOMERID_88E 0xC5
+#define EEPROM_RF_ANTENNA_OPT_88E 0xC9
+
+/* RTL88EE */
+#define EEPROM_MAC_ADDR_88EE 0xD0
+#define EEPROM_VID_88EE 0xD6
+#define EEPROM_DID_88EE 0xD8
+#define EEPROM_SVID_88EE 0xDA
+#define EEPROM_SMID_88EE 0xDC
+
+/* RTL88EU */
+#define EEPROM_MAC_ADDR_88EU 0xD7
+#define EEPROM_VID_88EU 0xD0
+#define EEPROM_PID_88EU 0xD2
+#define EEPROM_USB_OPTIONAL_FUNCTION0 0xD4
+
+/* RTL88ES */
+#define EEPROM_MAC_ADDR_88ES 0x11A
+
+/* EEPROM/Efuse Value Type */
+#define EETYPE_TX_PWR 0x0
+
+/* Default Value for EEPROM or EFUSE!!! */
+#define EEPROM_Default_TSSI 0x0
+#define EEPROM_Default_TxPowerDiff 0x0
+#define EEPROM_Default_CrystalCap 0x5
+/* Default: 2X2, RTL8192CE(QFPN68) */
+#define EEPROM_Default_BoardType 0x02
+#define EEPROM_Default_TxPower 0x1010
+#define EEPROM_Default_HT2T_TxPwr 0x10
+
+#define EEPROM_Default_LegacyHTTxPowerDiff 0x3
+#define EEPROM_Default_ThermalMeter 0x12
+
+#define EEPROM_Default_AntTxPowerDiff 0x0
+#define EEPROM_Default_TxPwDiff_CrystalCap 0x5
+#define EEPROM_Default_TxPowerLevel 0x2A
+
+#define EEPROM_Default_HT40_2SDiff 0x0
+/* HT20<->40 default Tx Power Index Difference */
+#define EEPROM_Default_HT20_Diff 2
+#define EEPROM_Default_LegacyHTTxPowerDiff 0x3
+#define EEPROM_Default_HT40_PwrMaxOffset 0
+#define EEPROM_Default_HT20_PwrMaxOffset 0
+
+#define EEPROM_Default_CrystalCap_88E 0x20
+#define EEPROM_Default_ThermalMeter_88E 0x18
+
+/* New EFUSE deafult value */
+#define EEPROM_DEFAULT_24G_INDEX 0x2D
+#define EEPROM_DEFAULT_24G_HT20_DIFF 0X02
+#define EEPROM_DEFAULT_24G_OFDM_DIFF 0X04
+
+#define EEPROM_DEFAULT_5G_INDEX 0X2A
+#define EEPROM_DEFAULT_5G_HT20_DIFF 0X00
+#define EEPROM_DEFAULT_5G_OFDM_DIFF 0X04
+
+#define EEPROM_DEFAULT_DIFF 0XFE
+#define EEPROM_DEFAULT_CHANNEL_PLAN 0x7F
+#define EEPROM_DEFAULT_BOARD_OPTION 0x00
+#define EEPROM_DEFAULT_FEATURE_OPTION 0x00
+#define EEPROM_DEFAULT_BT_OPTION 0x10
+
+/* For debug */
+#define EEPROM_Default_PID 0x1234
+#define EEPROM_Default_VID 0x5678
+#define EEPROM_Default_CustomerID 0xAB
+#define EEPROM_Default_CustomerID_8188E 0x00
+#define EEPROM_Default_SubCustomerID 0xCD
+#define EEPROM_Default_Version 0
+
+#define EEPROM_CHANNEL_PLAN_FCC 0x0
+#define EEPROM_CHANNEL_PLAN_IC 0x1
+#define EEPROM_CHANNEL_PLAN_ETSI 0x2
+#define EEPROM_CHANNEL_PLAN_SPA 0x3
+#define EEPROM_CHANNEL_PLAN_FRANCE 0x4
+#define EEPROM_CHANNEL_PLAN_MKK 0x5
+#define EEPROM_CHANNEL_PLAN_MKK1 0x6
+#define EEPROM_CHANNEL_PLAN_ISRAEL 0x7
+#define EEPROM_CHANNEL_PLAN_TELEC 0x8
+#define EEPROM_CHANNEL_PLAN_GLOBAL_DOMA 0x9
+#define EEPROM_CHANNEL_PLAN_WORLD_WIDE_13 0xA
+#define EEPROM_CHANNEL_PLAN_NCC 0xB
+#define EEPROM_USB_OPTIONAL1 0xE
+#define EEPROM_CHANNEL_PLAN_BY_HW_MASK 0x80
+
+#define EEPROM_CID_DEFAULT 0x0
+#define EEPROM_CID_TOSHIBA 0x4
+#define EEPROM_CID_CCX 0x10 /* CCX test. */
+#define EEPROM_CID_QMI 0x0D
+#define EEPROM_CID_WHQL 0xFE
+#define RTL_EEPROM_ID 0x8129
+
+#endif /* __RTL8188E_SPEC_H__ */
diff --git a/drivers/staging/rtl8188eu/include/rtl8188e_xmit.h b/drivers/staging/rtl8188eu/include/rtl8188e_xmit.h
new file mode 100644
index 000000000..0b96d42e2
--- /dev/null
+++ b/drivers/staging/rtl8188eu/include/rtl8188e_xmit.h
@@ -0,0 +1,177 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2011 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ *
+ ******************************************************************************/
+#ifndef __RTL8188E_XMIT_H__
+#define __RTL8188E_XMIT_H__
+
+#define MAX_TX_AGG_PACKET_NUMBER 0xFF
+/* */
+/* Queue Select Value in TxDesc */
+/* */
+#define QSLT_BK 0x2/* 0x01 */
+#define QSLT_BE 0x0
+#define QSLT_VI 0x5/* 0x4 */
+#define QSLT_VO 0x7/* 0x6 */
+#define QSLT_BEACON 0x10
+#define QSLT_HIGH 0x11
+#define QSLT_MGNT 0x12
+#define QSLT_CMD 0x13
+
+/* For 88e early mode */
+#define SET_EARLYMODE_PKTNUM(__pAddr, __Value) \
+ SET_BITS_TO_LE_4BYTE(__pAddr, 0, 3, __Value)
+#define SET_EARLYMODE_LEN0(__pAddr, __Value) \
+ SET_BITS_TO_LE_4BYTE(__pAddr, 4, 12, __Value)
+#define SET_EARLYMODE_LEN1(__pAddr, __Value) \
+ SET_BITS_TO_LE_4BYTE(__pAddr, 16, 12, __Value)
+#define SET_EARLYMODE_LEN2_1(__pAddr, __Value) \
+ SET_BITS_TO_LE_4BYTE(__pAddr, 28, 4, __Value)
+#define SET_EARLYMODE_LEN2_2(__pAddr, __Value) \
+ SET_BITS_TO_LE_4BYTE(__pAddr+4, 0, 8, __Value)
+#define SET_EARLYMODE_LEN3(__pAddr, __Value) \
+ SET_BITS_TO_LE_4BYTE(__pAddr+4, 8, 12, __Value)
+#define SET_EARLYMODE_LEN4(__pAddr, __Value) \
+ SET_BITS_TO_LE_4BYTE(__pAddr+4, 20, 12, __Value)
+
+/* */
+/* defined for TX DESC Operation */
+/* */
+
+#define MAX_TID (15)
+
+/* OFFSET 0 */
+#define OFFSET_SZ 0
+#define OFFSET_SHT 16
+#define BMC BIT(24)
+#define LSG BIT(26)
+#define FSG BIT(27)
+#define OWN BIT(31)
+
+
+/* OFFSET 4 */
+#define PKT_OFFSET_SZ 0
+#define QSEL_SHT 8
+#define RATE_ID_SHT 16
+#define NAVUSEHDR BIT(20)
+#define SEC_TYPE_SHT 22
+#define PKT_OFFSET_SHT 26
+
+/* OFFSET 8 */
+#define AGG_EN BIT(12)
+#define AGG_BK BIT(16)
+#define AMPDU_DENSITY_SHT 20
+#define ANTSEL_A BIT(24)
+#define ANTSEL_B BIT(25)
+#define TX_ANT_CCK_SHT 26
+#define TX_ANTL_SHT 28
+#define TX_ANT_HT_SHT 30
+
+/* OFFSET 12 */
+#define SEQ_SHT 16
+#define EN_HWSEQ BIT(31)
+
+/* OFFSET 16 */
+#define QOS BIT(6)
+#define HW_SSN BIT(7)
+#define USERATE BIT(8)
+#define DISDATAFB BIT(10)
+#define CTS_2_SELF BIT(11)
+#define RTS_EN BIT(12)
+#define HW_RTS_EN BIT(13)
+#define DATA_SHORT BIT(24)
+#define PWR_STATUS_SHT 15
+#define DATA_SC_SHT 20
+#define DATA_BW BIT(25)
+
+/* OFFSET 20 */
+#define RTY_LMT_EN BIT(17)
+
+enum TXDESC_SC {
+ SC_DONT_CARE = 0x00,
+ SC_UPPER = 0x01,
+ SC_LOWER = 0x02,
+ SC_DUPLICATE = 0x03
+};
+/* OFFSET 20 */
+#define SGI BIT(6)
+#define USB_TXAGG_NUM_SHT 24
+
+#define txdesc_set_ccx_sw_88e(txdesc, value) \
+ do { \
+ ((struct txdesc_88e *)(txdesc))->sw1 = (((value)>>8) & 0x0f); \
+ ((struct txdesc_88e *)(txdesc))->sw0 = ((value) & 0xff); \
+ } while (0)
+
+struct txrpt_ccx_88e {
+ /* offset 0 */
+ u8 tag1:1;
+ u8 pkt_num:3;
+ u8 txdma_underflow:1;
+ u8 int_bt:1;
+ u8 int_tri:1;
+ u8 int_ccx:1;
+
+ /* offset 1 */
+ u8 mac_id:6;
+ u8 pkt_ok:1;
+ u8 bmc:1;
+
+ /* offset 2 */
+ u8 retry_cnt:6;
+ u8 lifetime_over:1;
+ u8 retry_over:1;
+
+ /* offset 3 */
+ u8 ccx_qtime0;
+ u8 ccx_qtime1;
+
+ /* offset 5 */
+ u8 final_data_rate;
+
+ /* offset 6 */
+ u8 sw1:4;
+ u8 qsel:4;
+
+ /* offset 7 */
+ u8 sw0;
+};
+
+#define txrpt_ccx_sw_88e(txrpt_ccx) ((txrpt_ccx)->sw0 + ((txrpt_ccx)->sw1<<8))
+#define txrpt_ccx_qtime_88e(txrpt_ccx) \
+ ((txrpt_ccx)->ccx_qtime0+((txrpt_ccx)->ccx_qtime1<<8))
+
+void rtl8188e_fill_fake_txdesc(struct adapter *padapter, u8 *pDesc,
+ u32 BufferLen, u8 IsPsPoll, u8 IsBTQosNull);
+s32 rtl8188eu_init_xmit_priv(struct adapter *padapter);
+s32 rtl8188eu_hal_xmit(struct adapter *padapter, struct xmit_frame *frame);
+s32 rtl8188eu_mgnt_xmit(struct adapter *padapter, struct xmit_frame *frame);
+s32 rtl8188eu_xmit_buf_handler(struct adapter *padapter);
+#define hal_xmit_handler rtl8188eu_xmit_buf_handler
+void rtl8188eu_xmit_tasklet(void *priv);
+s32 rtl8188eu_xmitframe_complete(struct adapter *padapter,
+ struct xmit_priv *pxmitpriv,
+ struct xmit_buf *pxmitbuf);
+
+void dump_txrpt_ccx_88e(void *buf);
+void handle_txrpt_ccx_88e(struct adapter *adapter, u8 *buf);
+
+void _dbg_dump_tx_info(struct adapter *padapter, int frame_tag,
+ struct tx_desc *ptxdesc);
+
+#endif /* __RTL8188E_XMIT_H__ */
diff --git a/drivers/staging/rtl8188eu/include/rtw_android.h b/drivers/staging/rtl8188eu/include/rtw_android.h
new file mode 100644
index 000000000..e85bf1ff0
--- /dev/null
+++ b/drivers/staging/rtl8188eu/include/rtw_android.h
@@ -0,0 +1,64 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2011 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ *
+ ******************************************************************************/
+
+#ifndef __RTW_ANDROID_H__
+#define __RTW_ANDROID_H__
+
+#include <linux/module.h>
+#include <linux/netdevice.h>
+
+enum ANDROID_WIFI_CMD {
+ ANDROID_WIFI_CMD_START,
+ ANDROID_WIFI_CMD_STOP,
+ ANDROID_WIFI_CMD_SCAN_ACTIVE,
+ ANDROID_WIFI_CMD_SCAN_PASSIVE,
+ ANDROID_WIFI_CMD_RSSI,
+ ANDROID_WIFI_CMD_LINKSPEED,
+ ANDROID_WIFI_CMD_RXFILTER_START,
+ ANDROID_WIFI_CMD_RXFILTER_STOP,
+ ANDROID_WIFI_CMD_RXFILTER_ADD,
+ ANDROID_WIFI_CMD_RXFILTER_REMOVE,
+ ANDROID_WIFI_CMD_BTCOEXSCAN_START,
+ ANDROID_WIFI_CMD_BTCOEXSCAN_STOP,
+ ANDROID_WIFI_CMD_BTCOEXMODE,
+ ANDROID_WIFI_CMD_SETSUSPENDOPT,
+ ANDROID_WIFI_CMD_P2P_DEV_ADDR,
+ ANDROID_WIFI_CMD_SETFWPATH,
+ ANDROID_WIFI_CMD_SETBAND,
+ ANDROID_WIFI_CMD_GETBAND,
+ ANDROID_WIFI_CMD_COUNTRY,
+ ANDROID_WIFI_CMD_P2P_SET_NOA,
+ ANDROID_WIFI_CMD_P2P_GET_NOA,
+ ANDROID_WIFI_CMD_P2P_SET_PS,
+ ANDROID_WIFI_CMD_SET_AP_WPS_P2P_IE,
+ ANDROID_WIFI_CMD_MACADDR,
+ ANDROID_WIFI_CMD_BLOCK,
+ ANDROID_WIFI_CMD_WFD_ENABLE,
+ ANDROID_WIFI_CMD_WFD_DISABLE,
+ ANDROID_WIFI_CMD_WFD_SET_TCPPORT,
+ ANDROID_WIFI_CMD_WFD_SET_MAX_TPUT,
+ ANDROID_WIFI_CMD_WFD_SET_DEVTYPE,
+ ANDROID_WIFI_CMD_MAX
+};
+
+int rtw_android_cmdstr_to_num(char *cmdstr);
+int rtw_android_priv_cmd(struct net_device *net, struct ifreq *ifr, int cmd);
+
+#endif /* __RTW_ANDROID_H__ */
diff --git a/drivers/staging/rtl8188eu/include/rtw_ap.h b/drivers/staging/rtl8188eu/include/rtw_ap.h
new file mode 100644
index 000000000..923340159
--- /dev/null
+++ b/drivers/staging/rtl8188eu/include/rtw_ap.h
@@ -0,0 +1,65 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2012 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ *
+ ******************************************************************************/
+#ifndef __RTW_AP_H_
+#define __RTW_AP_H_
+
+#include <osdep_service.h>
+#include <drv_types.h>
+
+#ifdef CONFIG_88EU_AP_MODE
+
+/* external function */
+void rtw_indicate_sta_assoc_event(struct adapter *padapter,
+ struct sta_info *psta);
+void rtw_indicate_sta_disassoc_event(struct adapter *padapter,
+ struct sta_info *psta);
+void init_mlme_ap_info(struct adapter *padapter);
+void free_mlme_ap_info(struct adapter *padapter);
+void rtw_add_bcn_ie(struct adapter *padapter, struct wlan_bssid_ex *pnetwork,
+ u8 index, u8 *data, u8 len);
+void rtw_remove_bcn_ie(struct adapter *padapter,
+ struct wlan_bssid_ex *pnetwork, u8 index);
+void update_beacon(struct adapter *padapter, u8 ie_id,
+ u8 *oui, u8 tx);
+void add_RATid(struct adapter *padapter, struct sta_info *psta,
+ u8 rssi_level);
+void expire_timeout_chk(struct adapter *padapter);
+void update_sta_info_apmode(struct adapter *padapter, struct sta_info *psta);
+int rtw_check_beacon_data(struct adapter *padapter, u8 *pbuf, int len);
+void rtw_set_macaddr_acl(struct adapter *padapter, int mode);
+int rtw_acl_add_sta(struct adapter *padapter, u8 *addr);
+int rtw_acl_remove_sta(struct adapter *padapter, u8 *addr);
+
+#ifdef CONFIG_88EU_AP_MODE
+void associated_clients_update(struct adapter *padapter, u8 updated);
+void bss_cap_update_on_sta_join(struct adapter *padapter, struct sta_info *psta);
+u8 bss_cap_update_on_sta_leave(struct adapter *padapter, struct sta_info *psta);
+void sta_info_update(struct adapter *padapter, struct sta_info *psta);
+void ap_sta_info_defer_update(struct adapter *padapter, struct sta_info *psta);
+u8 ap_free_sta(struct adapter *padapter, struct sta_info *psta,
+ bool active, u16 reason);
+int rtw_sta_flush(struct adapter *padapter);
+int rtw_ap_inform_ch_switch(struct adapter *padapter, u8 new_ch, u8 ch_offset);
+void start_ap_mode(struct adapter *padapter);
+void stop_ap_mode(struct adapter *padapter);
+#endif
+#endif /* end of CONFIG_88EU_AP_MODE */
+
+#endif
diff --git a/drivers/staging/rtl8188eu/include/rtw_cmd.h b/drivers/staging/rtl8188eu/include/rtw_cmd.h
new file mode 100644
index 000000000..9e9f5f4af
--- /dev/null
+++ b/drivers/staging/rtl8188eu/include/rtw_cmd.h
@@ -0,0 +1,423 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2011 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ *
+ ******************************************************************************/
+#ifndef __RTW_CMD_H_
+#define __RTW_CMD_H_
+
+#include <wlan_bssdef.h>
+#include <rtw_rf.h>
+#include <rtw_led.h>
+
+#include <osdep_service.h>
+#include <ieee80211.h> /* <ieee80211/ieee80211.h> */
+
+#define MAX_CMDSZ 1024
+#define MAX_RSPSZ 512
+
+#define CMDBUFF_ALIGN_SZ 512
+
+struct cmd_obj {
+ struct adapter *padapter;
+ u16 cmdcode;
+ u8 res;
+ u8 *parmbuf;
+ u32 cmdsz;
+ u8 *rsp;
+ u32 rspsz;
+ struct list_head list;
+};
+
+struct cmd_priv {
+ struct semaphore cmd_queue_sema;
+ struct semaphore terminate_cmdthread_sema;
+ struct __queue cmd_queue;
+ u8 cmdthd_running;
+ struct adapter *padapter;
+};
+
+#define init_h2fwcmd_w_parm_no_rsp(pcmd, pparm, code) \
+do {\
+ INIT_LIST_HEAD(&pcmd->list);\
+ pcmd->cmdcode = code;\
+ pcmd->parmbuf = (u8 *)(pparm);\
+ pcmd->cmdsz = sizeof(*pparm);\
+ pcmd->rsp = NULL;\
+ pcmd->rspsz = 0;\
+} while (0)
+
+u32 rtw_enqueue_cmd(struct cmd_priv *pcmdpriv, struct cmd_obj *obj);
+struct cmd_obj *rtw_dequeue_cmd(struct __queue *queue);
+void rtw_free_cmd_obj(struct cmd_obj *pcmd);
+
+int rtw_cmd_thread(void *context);
+
+int rtw_init_cmd_priv(struct cmd_priv *pcmdpriv);
+
+enum rtw_drvextra_cmd_id {
+ NONE_WK_CID,
+ DYNAMIC_CHK_WK_CID,
+ DM_CTRL_WK_CID,
+ PBC_POLLING_WK_CID,
+ POWER_SAVING_CTRL_WK_CID,/* IPS,AUTOSuspend */
+ LPS_CTRL_WK_CID,
+ ANT_SELECT_WK_CID,
+ P2P_PS_WK_CID,
+ P2P_PROTO_WK_CID,
+ CHECK_HIQ_WK_CID,/* for softap mode, check hi queue if empty */
+ INTEl_WIDI_WK_CID,
+ C2H_WK_CID,
+ RTP_TIMER_CFG_WK_CID,
+ MAX_WK_CID
+};
+
+enum LPS_CTRL_TYPE {
+ LPS_CTRL_SCAN = 0,
+ LPS_CTRL_JOINBSS = 1,
+ LPS_CTRL_CONNECT = 2,
+ LPS_CTRL_DISCONNECT = 3,
+ LPS_CTRL_SPECIAL_PACKET = 4,
+ LPS_CTRL_LEAVE = 5,
+};
+
+enum RFINTFS {
+ SWSI,
+ HWSI,
+ HWPI,
+};
+
+/*
+Caller Mode: Infra, Ad-HoC(C)
+
+Notes: To disconnect the current associated BSS
+
+Command Mode
+
+*/
+struct disconnect_parm {
+ u32 deauth_timeout_ms;
+};
+
+struct setopmode_parm {
+ u8 mode;
+ u8 rsvd[3];
+};
+
+/*
+Caller Mode: AP, Ad-HoC, Infra
+
+Notes: To ask RTL8711 performing site-survey
+
+Command-Event Mode
+
+*/
+
+#define RTW_SSID_SCAN_AMOUNT 9 /* for WEXT_CSCAN_AMOUNT 9 */
+#define RTW_CHANNEL_SCAN_AMOUNT (14+37)
+struct sitesurvey_parm {
+ int scan_mode; /* active: 1, passive: 0 */
+ u8 ssid_num;
+ u8 ch_num;
+ struct ndis_802_11_ssid ssid[RTW_SSID_SCAN_AMOUNT];
+ struct rtw_ieee80211_channel ch[RTW_CHANNEL_SCAN_AMOUNT];
+};
+
+/*
+Caller Mode: Any
+
+Notes: To set the auth type of RTL8711. open/shared/802.1x
+
+Command Mode
+
+*/
+struct setauth_parm {
+ u8 mode; /* 0: legacy open, 1: legacy shared 2: 802.1x */
+ u8 _1x; /* 0: PSK, 1: TLS */
+ u8 rsvd[2];
+};
+
+/*
+Caller Mode: Infra
+
+a. algorithm: wep40, wep104, tkip & aes
+b. keytype: grp key/unicast key
+c. key contents
+
+when shared key ==> keyid is the camid
+when 802.1x ==> keyid [0:1] ==> grp key
+when 802.1x ==> keyid > 2 ==> unicast key
+
+*/
+struct setkey_parm {
+ u8 algorithm; /* could be none, wep40, TKIP, CCMP, wep104 */
+ u8 keyid;
+ u8 grpkey; /* 1: this is the grpkey for 802.1x.
+ * 0: this is the unicast key for 802.1x */
+ u8 set_tx; /* 1: main tx key for wep. 0: other key. */
+ u8 key[16]; /* this could be 40 or 104 */
+};
+
+/*
+When in AP or Ad-Hoc mode, this is used to
+allocate an sw/hw entry for a newly associated sta.
+
+Command
+
+when shared key ==> algorithm/keyid
+
+*/
+struct set_stakey_parm {
+ u8 addr[ETH_ALEN];
+ u8 algorithm;
+ u8 id;/* currently for erasing cam entry if
+ * algorithm == _NO_PRIVACY_ */
+ u8 key[16];
+};
+
+struct set_stakey_rsp {
+ u8 addr[ETH_ALEN];
+ u8 keyid;
+ u8 rsvd;
+};
+
+/*
+Caller Ad-Hoc/AP
+
+Command -Rsp(AID == CAMID) mode
+
+This is to force fw to add an sta_data entry per driver's request.
+
+FW will write an cam entry associated with it.
+
+*/
+struct set_assocsta_parm {
+ u8 addr[ETH_ALEN];
+};
+
+struct set_assocsta_rsp {
+ u8 cam_id;
+ u8 rsvd[3];
+};
+
+/*
+ Caller Ad-Hoc/AP
+
+ Command mode
+
+ This is to force fw to del an sta_data entry per driver's request
+
+ FW will invalidate the cam entry associated with it.
+
+*/
+struct del_assocsta_parm {
+ u8 addr[ETH_ALEN];
+};
+
+/*
+Caller Mode: AP/Ad-HoC(M)
+
+Notes: To notify fw that given staid has changed its power state
+
+Command Mode
+
+*/
+struct setstapwrstate_parm {
+ u8 staid;
+ u8 status;
+ u8 hwaddr[6];
+};
+
+/*
+ Notes: This command is used for H2C/C2H loopback testing
+
+ mac[0] == 0
+ ==> CMD mode, return H2C_SUCCESS.
+ The following condition must be ture under CMD mode
+ mac[1] == mac[4], mac[2] == mac[3], mac[0]=mac[5]= 0;
+ s0 == 0x1234, s1 == 0xabcd, w0 == 0x78563412, w1 == 0x5aa5def7;
+ s2 == (b1 << 8 | b0);
+
+ mac[0] == 1
+ ==> CMD_RSP mode, return H2C_SUCCESS_RSP
+
+ The rsp layout shall be:
+ rsp: parm:
+ mac[0] = mac[5];
+ mac[1] = mac[4];
+ mac[2] = mac[3];
+ mac[3] = mac[2];
+ mac[4] = mac[1];
+ mac[5] = mac[0];
+ s0 = s1;
+ s1 = swap16(s0);
+ w0 = swap32(w1);
+ b0 = b1
+ s2 = s0 + s1
+ b1 = b0
+ w1 = w0
+
+ mac[0] == 2
+ ==> CMD_EVENT mode, return H2C_SUCCESS
+ The event layout shall be:
+ event: parm:
+ mac[0] = mac[5];
+ mac[1] = mac[4];
+ mac[2] = event's seq no, starting from 1 to parm's marc[3]
+ mac[3] = mac[2];
+ mac[4] = mac[1];
+ mac[5] = mac[0];
+ s0 = swap16(s0) - event.mac[2];
+ s1 = s1 + event.mac[2];
+ w0 = swap32(w0);
+ b0 = b1
+ s2 = s0 + event.mac[2]
+ b1 = b0
+ w1 = swap32(w1) - event.mac[2];
+
+ parm->mac[3] is the total event counts that host requested.
+ event will be the same with the cmd's param.
+*/
+
+/* CMD param Format for driver extra cmd handler */
+struct drvextra_cmd_parm {
+ int ec_id; /* extra cmd id */
+ int type_size; /* Can use this field as the type id or command size */
+ unsigned char *pbuf;
+};
+
+struct addBaReq_parm {
+ unsigned int tid;
+ u8 addr[ETH_ALEN];
+};
+
+/*H2C Handler index: 46 */
+struct set_ch_parm {
+ u8 ch;
+ u8 bw;
+ u8 ch_offset;
+};
+
+/*H2C Handler index: 59 */
+struct SetChannelPlan_param {
+ u8 channel_plan;
+};
+
+#define GEN_CMD_CODE(cmd) cmd ## _CMD_
+
+/*
+
+Result:
+0x00: success
+0x01: success, and check Response.
+0x02: cmd ignored due to duplicated sequcne number
+0x03: cmd dropped due to invalid cmd code
+0x04: reserved.
+
+*/
+
+#define H2C_SUCCESS 0x00
+#define H2C_SUCCESS_RSP 0x01
+#define H2C_DROPPED 0x03
+#define H2C_PARAMETERS_ERROR 0x04
+#define H2C_REJECTED 0x05
+
+u8 rtw_sitesurvey_cmd(struct adapter *padapter, struct ndis_802_11_ssid *ssid,
+ int ssid_num, struct rtw_ieee80211_channel *ch,
+ int ch_num);
+u8 rtw_createbss_cmd(struct adapter *padapter);
+u8 rtw_setstakey_cmd(struct adapter *padapter, u8 *psta, u8 unicast_key);
+u8 rtw_clearstakey_cmd(struct adapter *padapter, u8 *psta, u8 entry,
+ u8 enqueue);
+u8 rtw_joinbss_cmd(struct adapter *padapter, struct wlan_network *pnetwork);
+u8 rtw_disassoc_cmd(struct adapter *padapter, u32 deauth_timeout_ms,
+ bool enqueue);
+u8 rtw_setopmode_cmd(struct adapter *padapter,
+ enum ndis_802_11_network_infra networktype);
+u8 rtw_addbareq_cmd(struct adapter *padapter, u8 tid, u8 *addr);
+
+u8 rtw_dynamic_chk_wk_cmd(struct adapter *adapter);
+
+u8 rtw_lps_ctrl_wk_cmd(struct adapter *padapter, u8 lps_ctrl_type, u8 enqueue);
+u8 rtw_rpt_timer_cfg_cmd(struct adapter *padapter, u16 minRptTime);
+
+u8 rtw_antenna_select_cmd(struct adapter *padapter, u8 antenna, u8 enqueue);
+u8 rtw_ps_cmd(struct adapter *padapter);
+
+#ifdef CONFIG_88EU_AP_MODE
+u8 rtw_chk_hi_queue_cmd(struct adapter *padapter);
+#endif
+
+u8 rtw_set_chplan_cmd(struct adapter *padapter, u8 chplan, u8 enqueue);
+u8 rtw_drvextra_cmd_hdl(struct adapter *padapter, unsigned char *pbuf);
+
+void rtw_survey_cmd_callback(struct adapter *padapter, struct cmd_obj *pcmd);
+void rtw_disassoc_cmd_callback(struct adapter *padapter, struct cmd_obj *pcmd);
+void rtw_joinbss_cmd_callback(struct adapter *padapter, struct cmd_obj *pcmd);
+void rtw_createbss_cmd_callback(struct adapter *adapt, struct cmd_obj *pcmd);
+void rtw_readtssi_cmdrsp_callback(struct adapter *adapt, struct cmd_obj *cmd);
+
+void rtw_setstaKey_cmdrsp_callback(struct adapter *adapt, struct cmd_obj *cmd);
+void rtw_setassocsta_cmdrsp_callback(struct adapter *adapt, struct cmd_obj *cm);
+void rtw_getrttbl_cmdrsp_callback(struct adapter *adapt, struct cmd_obj *cmd);
+
+struct _cmd_callback {
+ u32 cmd_code;
+ void (*callback)(struct adapter *padapter, struct cmd_obj *cmd);
+};
+
+enum rtw_h2c_cmd {
+ GEN_CMD_CODE(_JoinBss),
+ GEN_CMD_CODE(_DisConnect),
+ GEN_CMD_CODE(_CreateBss),
+ GEN_CMD_CODE(_SetOpMode),
+ GEN_CMD_CODE(_SiteSurvey),
+ GEN_CMD_CODE(_SetAuth),
+ GEN_CMD_CODE(_SetKey),
+ GEN_CMD_CODE(_SetStaKey),
+ GEN_CMD_CODE(_SetAssocSta),
+ GEN_CMD_CODE(_AddBAReq),
+ GEN_CMD_CODE(_SetChannel),
+ GEN_CMD_CODE(_TX_Beacon),
+ GEN_CMD_CODE(_Set_MLME_EVT),
+ GEN_CMD_CODE(_Set_Drv_Extra),
+ GEN_CMD_CODE(_SetChannelPlan),
+
+ MAX_H2CCMD
+};
+
+#ifdef _RTW_CMD_C_
+static struct _cmd_callback rtw_cmd_callback[] = {
+ {GEN_CMD_CODE(_JoinBss), &rtw_joinbss_cmd_callback},
+ {GEN_CMD_CODE(_DisConnect), &rtw_disassoc_cmd_callback},
+ {GEN_CMD_CODE(_CreateBss), &rtw_createbss_cmd_callback},
+ {GEN_CMD_CODE(_SetOpMode), NULL},
+ {GEN_CMD_CODE(_SiteSurvey), &rtw_survey_cmd_callback},
+ {GEN_CMD_CODE(_SetAuth), NULL},
+ {GEN_CMD_CODE(_SetKey), NULL},
+ {GEN_CMD_CODE(_SetStaKey), &rtw_setstaKey_cmdrsp_callback},
+ {GEN_CMD_CODE(_SetAssocSta), &rtw_setassocsta_cmdrsp_callback},
+ {GEN_CMD_CODE(_AddBAReq), NULL},
+ {GEN_CMD_CODE(_SetChannel), NULL},
+ {GEN_CMD_CODE(_TX_Beacon), NULL},
+ {GEN_CMD_CODE(_Set_MLME_EVT), NULL},
+ {GEN_CMD_CODE(_Set_Drv_Extra), NULL},
+ {GEN_CMD_CODE(_SetChannelPlan), NULL},
+};
+#endif
+
+#endif /* _CMD_H_ */
diff --git a/drivers/staging/rtl8188eu/include/rtw_debug.h b/drivers/staging/rtl8188eu/include/rtw_debug.h
new file mode 100644
index 000000000..971bf457f
--- /dev/null
+++ b/drivers/staging/rtl8188eu/include/rtw_debug.h
@@ -0,0 +1,266 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2011 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ *
+ ******************************************************************************/
+#ifndef __RTW_DEBUG_H__
+#define __RTW_DEBUG_H__
+
+#include <osdep_service.h>
+#include <drv_types.h>
+
+#define DRIVERVERSION "v4.1.4_6773.20130222"
+#define _drv_always_ 1
+#define _drv_emerg_ 2
+#define _drv_alert_ 3
+#define _drv_crit_ 4
+#define _drv_err_ 5
+#define _drv_warning_ 6
+#define _drv_notice_ 7
+#define _drv_info_ 8
+#define _drv_debug_ 9
+
+
+#define _module_rtl871x_xmit_c_ BIT(0)
+#define _module_xmit_osdep_c_ BIT(1)
+#define _module_rtl871x_recv_c_ BIT(2)
+#define _module_recv_osdep_c_ BIT(3)
+#define _module_rtl871x_mlme_c_ BIT(4)
+#define _module_mlme_osdep_c_ BIT(5)
+#define _module_rtl871x_sta_mgt_c_ BIT(6)
+#define _module_rtl871x_cmd_c_ BIT(7)
+#define _module_cmd_osdep_c_ BIT(8)
+#define _module_rtl871x_io_c_ BIT(9)
+#define _module_io_osdep_c_ BIT(10)
+#define _module_os_intfs_c_ BIT(11)
+#define _module_rtl871x_security_c_ BIT(12)
+#define _module_rtl871x_eeprom_c_ BIT(13)
+#define _module_hal_init_c_ BIT(14)
+#define _module_hci_hal_init_c_ BIT(15)
+#define _module_rtl871x_ioctl_c_ BIT(16)
+#define _module_rtl871x_ioctl_set_c_ BIT(17)
+#define _module_rtl871x_ioctl_query_c_ BIT(18)
+#define _module_rtl871x_pwrctrl_c_ BIT(19)
+#define _module_hci_intfs_c_ BIT(20)
+#define _module_hci_ops_c_ BIT(21)
+#define _module_osdep_service_c_ BIT(22)
+#define _module_mp_ BIT(23)
+#define _module_hci_ops_os_c_ BIT(24)
+#define _module_rtl871x_ioctl_os_c BIT(25)
+#define _module_rtl8712_cmd_c_ BIT(26)
+#define _module_rtl8192c_xmit_c_ BIT(27)
+#define _module_hal_xmit_c_ BIT(28)
+#define _module_efuse_ BIT(29)
+#define _module_rtl8712_recv_c_ BIT(30)
+#define _module_rtl8712_led_c_ BIT(31)
+
+#define DRIVER_PREFIX "R8188EU: "
+
+extern u32 GlobalDebugLevel;
+
+#define DBG_88E_LEVEL(_level, fmt, arg...) \
+ do { \
+ if (_level <= GlobalDebugLevel) \
+ pr_info(DRIVER_PREFIX"ERROR " fmt, ##arg); \
+ } while (0)
+
+#define DBG_88E(...) \
+ do { \
+ if (_drv_err_ <= GlobalDebugLevel) \
+ pr_info(DRIVER_PREFIX __VA_ARGS__); \
+ } while (0)
+
+#define MSG_88E(...) \
+ do { \
+ if (_drv_err_ <= GlobalDebugLevel) \
+ pr_info(DRIVER_PREFIX __VA_ARGS__); \
+ } while (0)
+
+#define RT_TRACE(_comp, _level, fmt) \
+ do { \
+ if (_level <= GlobalDebugLevel) { \
+ pr_info("%s [0x%08x,%d]", DRIVER_PREFIX, \
+ (unsigned int)_comp, _level); \
+ pr_info fmt; \
+ } \
+ } while (0)
+
+#define RT_PRINT_DATA(_comp, _level, _titlestring, _hexdata, _hexdatalen)\
+ do { \
+ if (_level <= GlobalDebugLevel) { \
+ int __i; \
+ u8 *ptr = (u8 *)_hexdata; \
+ pr_info("%s", DRIVER_PREFIX); \
+ pr_info(_titlestring); \
+ for (__i = 0; __i < (int)_hexdatalen; __i++) { \
+ pr_info("%02X%s", ptr[__i], \
+ (((__i + 1) % 4) == 0) ? \
+ " " : " "); \
+ if (((__i + 1) % 16) == 0) \
+ printk("\n"); \
+ } \
+ printk("\n"); \
+ } \
+ } while (0)
+
+int proc_get_drv_version(char *page, char **start,
+ off_t offset, int count,
+ int *eof, void *data);
+
+int proc_get_write_reg(char *page, char **start,
+ off_t offset, int count,
+ int *eof, void *data);
+
+int proc_set_write_reg(struct file *file, const char __user *buffer,
+ unsigned long count, void *data);
+int proc_get_read_reg(char *page, char **start,
+ off_t offset, int count,
+ int *eof, void *data);
+
+int proc_set_read_reg(struct file *file, const char __user *buffer,
+ unsigned long count, void *data);
+
+int proc_get_fwstate(char *page, char **start,
+ off_t offset, int count,
+ int *eof, void *data);
+int proc_get_sec_info(char *page, char **start,
+ off_t offset, int count,
+ int *eof, void *data);
+int proc_get_mlmext_state(char *page, char **start,
+ off_t offset, int count,
+ int *eof, void *data);
+
+int proc_get_qos_option(char *page, char **start,
+ off_t offset, int count,
+ int *eof, void *data);
+int proc_get_ht_option(char *page, char **start,
+ off_t offset, int count,
+ int *eof, void *data);
+int proc_get_rf_info(char *page, char **start,
+ off_t offset, int count,
+ int *eof, void *data);
+int proc_get_ap_info(char *page, char **start,
+ off_t offset, int count,
+ int *eof, void *data);
+
+int proc_get_adapter_state(char *page, char **start,
+ off_t offset, int count,
+ int *eof, void *data);
+
+int proc_get_trx_info(char *page, char **start,
+ off_t offset, int count,
+ int *eof, void *data);
+
+int proc_get_mac_reg_dump1(char *page, char **start,
+ off_t offset, int count,
+ int *eof, void *data);
+
+int proc_get_mac_reg_dump2(char *page, char **start,
+ off_t offset, int count,
+ int *eof, void *data);
+
+int proc_get_mac_reg_dump3(char *page, char **start,
+ off_t offset, int count,
+ int *eof, void *data);
+
+int proc_get_bb_reg_dump1(char *page, char **start,
+ off_t offset, int count,
+ int *eof, void *data);
+
+int proc_get_bb_reg_dump2(char *page, char **start,
+ off_t offset, int count,
+ int *eof, void *data);
+
+int proc_get_bb_reg_dump3(char *page, char **start,
+ off_t offset, int count,
+ int *eof, void *data);
+
+int proc_get_rf_reg_dump1(char *page, char **start,
+ off_t offset, int count,
+ int *eof, void *data);
+
+int proc_get_rf_reg_dump2(char *page, char **start,
+ off_t offset, int count,
+ int *eof, void *data);
+
+int proc_get_rf_reg_dump3(char *page, char **start,
+ off_t offset, int count,
+ int *eof, void *data);
+
+int proc_get_rf_reg_dump4(char *page, char **start,
+ off_t offset, int count,
+ int *eof, void *data);
+
+#ifdef CONFIG_88EU_AP_MODE
+
+int proc_get_all_sta_info(char *page, char **start,
+ off_t offset, int count,
+ int *eof, void *data);
+
+#endif
+
+int proc_get_best_channel(char *page, char **start,
+ off_t offset, int count,
+ int *eof, void *data);
+
+int proc_get_rx_signal(char *page, char **start,
+ off_t offset, int count,
+ int *eof, void *data);
+
+int proc_set_rx_signal(struct file *file, const char __user *buffer,
+ unsigned long count, void *data);
+
+int proc_get_ht_enable(char *page, char **start,
+ off_t offset, int count,
+ int *eof, void *data);
+
+int proc_set_ht_enable(struct file *file, const char __user *buffer,
+ unsigned long count, void *data);
+
+int proc_get_cbw40_enable(char *page, char **start,
+ off_t offset, int count,
+ int *eof, void *data);
+
+int proc_set_cbw40_enable(struct file *file, const char __user *buffer,
+ unsigned long count, void *data);
+
+int proc_get_ampdu_enable(char *page, char **start,
+ off_t offset, int count,
+ int *eof, void *data);
+
+int proc_set_ampdu_enable(struct file *file, const char __user *buffer,
+ unsigned long count, void *data);
+
+int proc_get_rx_stbc(char *page, char **start,
+ off_t offset, int count,
+ int *eof, void *data);
+
+int proc_set_rx_stbc(struct file *file, const char __user *buffer,
+ unsigned long count, void *data);
+
+int proc_get_two_path_rssi(char *page, char **start,
+ off_t offset, int count,
+ int *eof, void *data);
+
+int proc_get_rssi_disp(char *page, char **start,
+ off_t offset, int count,
+ int *eof, void *data);
+
+int proc_set_rssi_disp(struct file *file, const char __user *buffer,
+ unsigned long count, void *data);
+
+#endif /* __RTW_DEBUG_H__ */
diff --git a/drivers/staging/rtl8188eu/include/rtw_eeprom.h b/drivers/staging/rtl8188eu/include/rtw_eeprom.h
new file mode 100644
index 000000000..904fea1fa
--- /dev/null
+++ b/drivers/staging/rtl8188eu/include/rtw_eeprom.h
@@ -0,0 +1,130 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2011 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ *
+ ******************************************************************************/
+#ifndef __RTW_EEPROM_H__
+#define __RTW_EEPROM_H__
+
+#include <osdep_service.h>
+#include <drv_types.h>
+
+#define RTL8712_EEPROM_ID 0x8712
+
+#define HWSET_MAX_SIZE_512 512
+#define EEPROM_MAX_SIZE HWSET_MAX_SIZE_512
+
+#define CLOCK_RATE 50 /* 100us */
+
+/* EEPROM opcodes */
+#define EEPROM_READ_OPCODE 06
+#define EEPROM_WRITE_OPCODE 05
+#define EEPROM_ERASE_OPCODE 07
+#define EEPROM_EWEN_OPCODE 19 /* Erase/write enable */
+#define EEPROM_EWDS_OPCODE 16 /* Erase/write disable */
+
+/* Country codes */
+#define USA 0x555320
+#define EUROPE 0x1 /* temp, should be provided later */
+#define JAPAN 0x2 /* temp, should be provided later */
+
+#define EEPROM_CID_DEFAULT 0x0
+#define EEPROM_CID_ALPHA 0x1
+#define EEPROM_CID_Senao 0x3
+#define EEPROM_CID_NetCore 0x5
+#define EEPROM_CID_CAMEO 0X8
+#define EEPROM_CID_SITECOM 0x9
+#define EEPROM_CID_COREGA 0xB
+#define EEPROM_CID_EDIMAX_BELK 0xC
+#define EEPROM_CID_SERCOMM_BELK 0xE
+#define EEPROM_CID_CAMEO1 0xF
+#define EEPROM_CID_WNC_COREGA 0x12
+#define EEPROM_CID_CLEVO 0x13
+#define EEPROM_CID_WHQL 0xFE
+
+/* Customer ID, note that: */
+/* This variable is initiailzed through EEPROM or registry, */
+/* however, its definition may be different with that in EEPROM for */
+/* EEPROM size consideration. So, we have to perform proper translation
+ * between them. */
+/* Besides, CustomerID of registry has precedence of that of EEPROM. */
+/* defined below. 060703, by rcnjko. */
+enum RT_CUSTOMER_ID {
+ RT_CID_DEFAULT = 0,
+ RT_CID_8187_ALPHA0 = 1,
+ RT_CID_8187_SERCOMM_PS = 2,
+ RT_CID_8187_HW_LED = 3,
+ RT_CID_8187_NETGEAR = 4,
+ RT_CID_WHQL = 5,
+ RT_CID_819x_CAMEO = 6,
+ RT_CID_819x_RUNTOP = 7,
+ RT_CID_819x_Senao = 8,
+ RT_CID_TOSHIBA = 9, /* Merge by Jacken, 2008/01/31. */
+ RT_CID_819x_Netcore = 10,
+ RT_CID_Nettronix = 11,
+ RT_CID_DLINK = 12,
+ RT_CID_PRONET = 13,
+ RT_CID_COREGA = 14,
+ RT_CID_CHINA_MOBILE = 15,
+ RT_CID_819x_ALPHA = 16,
+ RT_CID_819x_Sitecom = 17,
+ RT_CID_CCX = 18, /* It's set under CCX logo test and isn't demanded
+ * for CCX functions, but for test behavior like retry
+ * limit and tx report. By Bruce, 2009-02-17. */
+ RT_CID_819x_Lenovo = 19,
+ RT_CID_819x_QMI = 20,
+ RT_CID_819x_Edimax_Belkin = 21,
+ RT_CID_819x_Sercomm_Belkin = 22,
+ RT_CID_819x_CAMEO1 = 23,
+ RT_CID_819x_MSI = 24,
+ RT_CID_819x_Acer = 25,
+ RT_CID_819x_AzWave_ASUS = 26,
+ RT_CID_819x_AzWave = 27, /* For AzWave in PCIe,i
+ * The ID is AzWave use and not only Asus */
+ RT_CID_819x_HP = 28,
+ RT_CID_819x_WNC_COREGA = 29,
+ RT_CID_819x_Arcadyan_Belkin = 30,
+ RT_CID_819x_SAMSUNG = 31,
+ RT_CID_819x_CLEVO = 32,
+ RT_CID_819x_DELL = 33,
+ RT_CID_819x_PRONETS = 34,
+ RT_CID_819x_Edimax_ASUS = 35,
+ RT_CID_819x_CAMEO_NETGEAR = 36,
+ RT_CID_PLANEX = 37,
+ RT_CID_CC_C = 38,
+ RT_CID_819x_Xavi = 39,
+ RT_CID_819x_FUNAI_TV = 40,
+ RT_CID_819x_ALPHA_WD = 41,
+};
+
+struct eeprom_priv {
+ u8 bautoload_fail_flag;
+ u8 bloadfile_fail_flag;
+ u8 bloadmac_fail_flag;
+ u8 mac_addr[6]; /* PermanentAddress */
+ u16 channel_plan;
+ u8 EepromOrEfuse;
+ u8 efuse_eeprom_data[HWSET_MAX_SIZE_512];
+};
+
+void eeprom_write16(struct adapter *padapter, u16 reg, u16 data);
+u16 eeprom_read16(struct adapter *padapter, u16 reg);
+void read_eeprom_content(struct adapter *padapter);
+void eeprom_read_sz(struct adapter *adapt, u16 reg, u8 *data, u32 sz);
+void read_eeprom_content_by_attrib(struct adapter *padapter);
+
+#endif /* __RTL871X_EEPROM_H__ */
diff --git a/drivers/staging/rtl8188eu/include/rtw_efuse.h b/drivers/staging/rtl8188eu/include/rtw_efuse.h
new file mode 100644
index 000000000..5660eed71
--- /dev/null
+++ b/drivers/staging/rtl8188eu/include/rtw_efuse.h
@@ -0,0 +1,118 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2011 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ *
+ ******************************************************************************/
+#ifndef __RTW_EFUSE_H__
+#define __RTW_EFUSE_H__
+
+#include <osdep_service.h>
+
+#define EFUSE_ERROE_HANDLE 1
+
+#define PG_STATE_HEADER 0x01
+#define PG_STATE_WORD_0 0x02
+#define PG_STATE_WORD_1 0x04
+#define PG_STATE_WORD_2 0x08
+#define PG_STATE_WORD_3 0x10
+#define PG_STATE_DATA 0x20
+
+#define PG_SWBYTE_H 0x01
+#define PG_SWBYTE_L 0x02
+
+#define PGPKT_DATA_SIZE 8
+
+#define EFUSE_WIFI 0
+#define EFUSE_BT 1
+
+enum _EFUSE_DEF_TYPE {
+ TYPE_EFUSE_MAX_SECTION = 0,
+ TYPE_EFUSE_REAL_CONTENT_LEN = 1,
+ TYPE_AVAILABLE_EFUSE_BYTES_BANK = 2,
+ TYPE_AVAILABLE_EFUSE_BYTES_TOTAL = 3,
+ TYPE_EFUSE_MAP_LEN = 4,
+ TYPE_EFUSE_PROTECT_BYTES_BANK = 5,
+ TYPE_EFUSE_CONTENT_LEN_BANK = 6,
+};
+
+/* E-Fuse */
+#define EFUSE_MAP_SIZE 512
+#define EFUSE_MAX_SIZE 256
+/* end of E-Fuse */
+
+#define EFUSE_MAX_MAP_LEN 512
+#define EFUSE_MAX_HW_SIZE 512
+#define EFUSE_MAX_SECTION_BASE 16
+
+#define EXT_HEADER(header) ((header & 0x1F) == 0x0F)
+#define ALL_WORDS_DISABLED(wde) ((wde & 0x0F) == 0x0F)
+#define GET_HDR_OFFSET_2_0(header) ((header & 0xE0) >> 5)
+
+#define EFUSE_REPEAT_THRESHOLD_ 3
+
+/* The following is for BT Efuse definition */
+#define EFUSE_BT_MAX_MAP_LEN 1024
+#define EFUSE_MAX_BANK 4
+#define EFUSE_MAX_BT_BANK (EFUSE_MAX_BANK-1)
+/*--------------------------Define Parameters-------------------------------*/
+#define EFUSE_MAX_WORD_UNIT 4
+
+/*------------------------------Define structure----------------------------*/
+struct pgpkt {
+ u8 offset;
+ u8 word_en;
+ u8 data[8];
+ u8 word_cnts;
+};
+
+/*------------------------------Define structure----------------------------*/
+struct efuse_hal {
+ u8 fakeEfuseBank;
+ u32 fakeEfuseUsedBytes;
+ u8 fakeEfuseContent[EFUSE_MAX_HW_SIZE];
+ u8 fakeEfuseInitMap[EFUSE_MAX_MAP_LEN];
+ u8 fakeEfuseModifiedMap[EFUSE_MAX_MAP_LEN];
+
+ u16 BTEfuseUsedBytes;
+ u8 BTEfuseUsedPercentage;
+ u8 BTEfuseContent[EFUSE_MAX_BT_BANK][EFUSE_MAX_HW_SIZE];
+ u8 BTEfuseInitMap[EFUSE_BT_MAX_MAP_LEN];
+ u8 BTEfuseModifiedMap[EFUSE_BT_MAX_MAP_LEN];
+
+ u16 fakeBTEfuseUsedBytes;
+ u8 fakeBTEfuseContent[EFUSE_MAX_BT_BANK][EFUSE_MAX_HW_SIZE];
+ u8 fakeBTEfuseInitMap[EFUSE_BT_MAX_MAP_LEN];
+ u8 fakeBTEfuseModifiedMap[EFUSE_BT_MAX_MAP_LEN];
+};
+
+u8 Efuse_CalculateWordCnts(u8 word_en);
+void EFUSE_GetEfuseDefinition(struct adapter *adapt, u8 type, u8 type1,
+ void *out);
+u8 efuse_OneByteRead(struct adapter *adapter, u16 addr, u8 *data);
+u8 efuse_OneByteWrite(struct adapter *adapter, u16 addr, u8 data);
+
+void efuse_ReadEFuse(struct adapter *Adapter, u8 efuseType, u16 _offset,
+ u16 _size_byte, u8 *pbuf);
+void Efuse_PowerSwitch(struct adapter *adapt, u8 bWrite, u8 PwrState);
+int Efuse_PgPacketRead(struct adapter *adapt, u8 offset, u8 *data);
+bool Efuse_PgPacketWrite(struct adapter *adapter, u8 offset, u8 word, u8 *data);
+void efuse_WordEnableDataRead(u8 word_en, u8 *sourdata, u8 *targetdata);
+u8 Efuse_WordEnableDataWrite(struct adapter *adapter, u16 efuse_addr,
+ u8 word_en, u8 *data);
+
+void EFUSE_ShadowMapUpdate(struct adapter *adapter, u8 efusetype);
+#endif
diff --git a/drivers/staging/rtl8188eu/include/rtw_event.h b/drivers/staging/rtl8188eu/include/rtw_event.h
new file mode 100644
index 000000000..52151dc44
--- /dev/null
+++ b/drivers/staging/rtl8188eu/include/rtw_event.h
@@ -0,0 +1,115 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2011 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ *
+ ******************************************************************************/
+#ifndef _RTW_EVENT_H_
+#define _RTW_EVENT_H_
+
+#include <osdep_service.h>
+
+#include <wlan_bssdef.h>
+#include <linux/semaphore.h>
+#include <linux/sem.h>
+
+/*
+Used to report a bss has been scanned
+*/
+struct survey_event {
+ struct wlan_bssid_ex bss;
+};
+
+/*
+Used to report that the requested site survey has been done.
+
+bss_cnt indicates the number of bss that has been reported.
+
+
+*/
+struct surveydone_event {
+ unsigned int bss_cnt;
+
+};
+
+/*
+Used to report the link result of joinning the given bss
+
+
+join_res:
+-1: authentication fail
+-2: association fail
+> 0: TID
+
+*/
+struct joinbss_event {
+ struct wlan_network network;
+};
+
+/*
+Used to report a given STA has joinned the created BSS.
+It is used in AP/Ad-HoC(M) mode.
+*/
+
+struct stassoc_event {
+ unsigned char macaddr[6];
+ unsigned char rsvd[2];
+ int cam_id;
+};
+
+struct stadel_event {
+ unsigned char macaddr[6];
+ unsigned char rsvd[2]; /* for reason */
+ int mac_id;
+};
+
+struct addba_event {
+ unsigned int tid;
+};
+
+#define GEN_EVT_CODE(event) event ## _EVT_
+
+struct fwevent {
+ u32 parmsize;
+ void (*event_callback)(struct adapter *dev, u8 *pbuf);
+};
+
+#define C2HEVENT_SZ 32
+
+struct event_node {
+ unsigned char *node;
+ unsigned char evt_code;
+ unsigned short evt_sz;
+ int *caller_ff_tail;
+ int caller_ff_sz;
+};
+
+struct c2hevent_queue {
+ int head;
+ int tail;
+ struct event_node nodes[C2HEVENT_SZ];
+ unsigned char seq;
+};
+
+#define NETWORK_QUEUE_SZ 4
+
+struct network_queue {
+ int head;
+ int tail;
+ struct wlan_bssid_ex networks[NETWORK_QUEUE_SZ];
+};
+
+#endif /* _WLANEVENT_H_ */
diff --git a/drivers/staging/rtl8188eu/include/rtw_ht.h b/drivers/staging/rtl8188eu/include/rtw_ht.h
new file mode 100644
index 000000000..beb210b37
--- /dev/null
+++ b/drivers/staging/rtl8188eu/include/rtw_ht.h
@@ -0,0 +1,44 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2011 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ *
+ ******************************************************************************/
+#ifndef _RTW_HT_H_
+#define _RTW_HT_H_
+
+#include <osdep_service.h>
+#include "wifi.h"
+
+struct ht_priv {
+ u32 ht_option;
+ u32 ampdu_enable;/* for enable Tx A-MPDU */
+ u32 tx_amsdu_enable;/* for enable Tx A-MSDU */
+ u32 tx_amdsu_maxlen; /* 1: 8k, 0:4k ; default:8k, for tx */
+ u32 rx_ampdu_maxlen; /* for rx reordering ctrl win_sz,
+ * updated when join_callback. */
+ u8 bwmode;/* */
+ u8 ch_offset;/* PRIME_CHNL_OFFSET */
+ u8 sgi;/* short GI */
+
+ /* for processing Tx A-MPDU */
+ u8 agg_enable_bitmap;
+ u8 candidate_tid_bitmap;
+
+ struct rtw_ieee80211_ht_cap ht_cap;
+};
+
+#endif /* _RTL871X_HT_H_ */
diff --git a/drivers/staging/rtl8188eu/include/rtw_ioctl.h b/drivers/staging/rtl8188eu/include/rtw_ioctl.h
new file mode 100644
index 000000000..f3aa924f2
--- /dev/null
+++ b/drivers/staging/rtl8188eu/include/rtw_ioctl.h
@@ -0,0 +1,122 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2011 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ *
+ ******************************************************************************/
+#ifndef _RTW_IOCTL_H_
+#define _RTW_IOCTL_H_
+
+#include <osdep_service.h>
+#include <drv_types.h>
+
+
+#ifndef OID_802_11_CAPABILITY
+ #define OID_802_11_CAPABILITY 0x0d010122
+#endif
+
+#ifndef OID_802_11_PMKID
+ #define OID_802_11_PMKID 0x0d010123
+#endif
+
+
+/* For DDK-defined OIDs */
+#define OID_NDIS_SEG1 0x00010100
+#define OID_NDIS_SEG2 0x00010200
+#define OID_NDIS_SEG3 0x00020100
+#define OID_NDIS_SEG4 0x01010100
+#define OID_NDIS_SEG5 0x01020100
+#define OID_NDIS_SEG6 0x01020200
+#define OID_NDIS_SEG7 0xFD010100
+#define OID_NDIS_SEG8 0x0D010100
+#define OID_NDIS_SEG9 0x0D010200
+#define OID_NDIS_SEG10 0x0D020200
+
+#define SZ_OID_NDIS_SEG1 23
+#define SZ_OID_NDIS_SEG2 3
+#define SZ_OID_NDIS_SEG3 6
+#define SZ_OID_NDIS_SEG4 6
+#define SZ_OID_NDIS_SEG5 4
+#define SZ_OID_NDIS_SEG6 8
+#define SZ_OID_NDIS_SEG7 7
+#define SZ_OID_NDIS_SEG8 36
+#define SZ_OID_NDIS_SEG9 24
+#define SZ_OID_NDIS_SEG10 19
+
+/* For Realtek-defined OIDs */
+#define OID_MP_SEG1 0xFF871100
+#define OID_MP_SEG2 0xFF818000
+
+#define OID_MP_SEG3 0xFF818700
+#define OID_MP_SEG4 0xFF011100
+
+#define DEBUG_OID(dbg, str) \
+ if ((!dbg)) { \
+ RT_TRACE(_module_rtl871x_ioctl_c_, _drv_info_, \
+ ("%s(%d): %s", __func__, __line__, str)); \
+ }
+
+enum oid_type {
+ QUERY_OID,
+ SET_OID
+};
+
+struct oid_funs_node {
+ unsigned int oid_start; /* the starting number for OID */
+ unsigned int oid_end; /* the ending number for OID */
+ struct oid_obj_priv *node_array;
+ unsigned int array_sz; /* the size of node_array */
+ int query_counter; /* count the number of query hits for this segment */
+ int set_counter; /* count the number of set hits for this segment */
+};
+
+struct oid_par_priv {
+ void *adapter_context;
+ NDIS_OID oid;
+ void *information_buf;
+ u32 information_buf_len;
+ u32 *bytes_rw;
+ u32 *bytes_needed;
+ enum oid_type type_of_oid;
+ u32 dbg;
+};
+
+struct oid_obj_priv {
+ unsigned char dbg; /* 0: without OID debug message
+ * 1: with OID debug message */
+ int (*oidfuns)(struct oid_par_priv *poid_par_priv);
+};
+
+#if defined(_RTW_MP_IOCTL_C_)
+static int oid_null_function(struct oid_par_priv *poid_par_priv) {
+ return NDIS_STATUS_SUCCESS;
+}
+#endif
+
+extern struct iw_handler_def rtw_handlers_def;
+
+int drv_query_info(struct net_device *miniportadaptercontext, NDIS_OID oid,
+ void *informationbuffer, u32 informationbufferlength,
+ u32 *byteswritten, u32 *bytesneeded);
+
+int drv_set_info(struct net_device *MiniportAdapterContext,
+ NDIS_OID oid, void *informationbuffer,
+ u32 informationbufferlength, u32 *bytesread,
+ u32 *bytesneeded);
+
+extern int ui_pid[3];
+
+#endif /* #ifndef __INC_CEINFO_ */
diff --git a/drivers/staging/rtl8188eu/include/rtw_ioctl_rtl.h b/drivers/staging/rtl8188eu/include/rtw_ioctl_rtl.h
new file mode 100644
index 000000000..8fa3858cb
--- /dev/null
+++ b/drivers/staging/rtl8188eu/include/rtw_ioctl_rtl.h
@@ -0,0 +1,79 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2011 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ *
+ ******************************************************************************/
+#ifndef _RTW_IOCTL_RTL_H_
+#define _RTW_IOCTL_RTL_H_
+
+#include <osdep_service.h>
+#include <drv_types.h>
+
+/* oid_rtl_seg_01_01 ************** */
+int oid_rt_get_signal_quality_hdl(struct oid_par_priv *poid_par_priv);/* 84 */
+int oid_rt_get_small_packet_crc_hdl(struct oid_par_priv *poid_par_priv);
+int oid_rt_get_middle_packet_crc_hdl(struct oid_par_priv *poid_par_priv);
+int oid_rt_get_large_packet_crc_hdl(struct oid_par_priv *poid_par_priv);
+int oid_rt_get_tx_retry_hdl(struct oid_par_priv *poid_par_priv);
+int oid_rt_get_rx_retry_hdl(struct oid_par_priv *poid_par_priv);
+int oid_rt_get_rx_total_packet_hdl(struct oid_par_priv *poid_par_priv);
+int oid_rt_get_tx_beacon_ok_hdl(struct oid_par_priv *poid_par_priv);
+int oid_rt_get_tx_beacon_err_hdl(struct oid_par_priv *poid_par_priv);
+
+int oid_rt_pro_set_fw_dig_state_hdl(struct oid_par_priv *poid_par_priv);/* 8a */
+int oid_rt_pro_set_fw_ra_state_hdl(struct oid_par_priv *poid_par_priv); /* 8b */
+
+int oid_rt_get_rx_icv_err_hdl(struct oid_par_priv *poid_par_priv);/* 93 */
+int oid_rt_set_encryption_algorithm_hdl(struct oid_par_priv *poid_par_priv);
+int oid_rt_get_preamble_mode_hdl(struct oid_par_priv *poid_par_priv);
+int oid_rt_get_ap_ip_hdl(struct oid_par_priv *poid_par_priv);
+int oid_rt_get_channelplan_hdl(struct oid_par_priv *poid_par_priv);
+int oid_rt_set_channelplan_hdl(struct oid_par_priv *poid_par_priv);
+int oid_rt_set_preamble_mode_hdl(struct oid_par_priv *poid_par_priv);
+int oid_rt_set_bcn_intvl_hdl(struct oid_par_priv *poid_par_priv);
+int oid_rt_dedicate_probe_hdl(struct oid_par_priv *poid_par_priv);
+int oid_rt_get_total_tx_bytes_hdl(struct oid_par_priv *poid_par_priv);
+int oid_rt_get_total_rx_bytes_hdl(struct oid_par_priv *poid_par_priv);
+int oid_rt_current_tx_power_level_hdl(struct oid_par_priv *poid_par_priv);
+int oid_rt_get_enc_key_mismatch_count_hdl(struct oid_par_priv *poid_par_priv);
+int oid_rt_get_enc_key_match_count_hdl(struct oid_par_priv *poid_par_priv);
+int oid_rt_get_channel_hdl(struct oid_par_priv *poid_par_priv);
+int oid_rt_get_hardware_radio_off_hdl(struct oid_par_priv *poid_par_priv);
+int oid_rt_get_key_mismatch_hdl(struct oid_par_priv *poid_par_priv);
+int oid_rt_supported_wireless_mode_hdl(struct oid_par_priv *poid_par_priv);
+int oid_rt_get_channel_list_hdl(struct oid_par_priv *poid_par_priv);
+int oid_rt_get_scan_in_progress_hdl(struct oid_par_priv *poid_par_priv);
+int oid_rt_forced_data_rate_hdl(struct oid_par_priv *poid_par_priv);
+int oid_rt_wireless_mode_for_scan_list_hdl(struct oid_par_priv *poid_par_priv);
+int oid_rt_get_bss_wireless_mode_hdl(struct oid_par_priv *poid_par_priv);
+int oid_rt_scan_with_magic_packet_hdl(struct oid_par_priv *poid_par_priv);
+
+/* oid_rtl_seg_01_03 section start ************** */
+int oid_rt_ap_get_associated_station_list_hdl(struct oid_par_priv *priv);
+int oid_rt_ap_switch_into_ap_mode_hdl(struct oid_par_priv *poid_par_priv);
+int oid_rt_ap_supported_hdl(struct oid_par_priv *poid_par_priv);
+int oid_rt_ap_set_passphrase_hdl(struct oid_par_priv *poid_par_priv);
+
+/* oid_rtl_seg_01_11 */
+int oid_rt_pro_rf_write_registry_hdl(struct oid_par_priv *poid_par_priv);
+int oid_rt_pro_rf_read_registry_hdl(struct oid_par_priv *poid_par_priv);
+
+/* oid_rtl_seg_03_00 section start ************** */
+int oid_rt_get_connect_state_hdl(struct oid_par_priv *poid_par_priv);
+int oid_rt_set_default_key_id_hdl(struct oid_par_priv *poid_par_priv);
+
+#endif
diff --git a/drivers/staging/rtl8188eu/include/rtw_ioctl_set.h b/drivers/staging/rtl8188eu/include/rtw_ioctl_set.h
new file mode 100644
index 000000000..fa9d655ea
--- /dev/null
+++ b/drivers/staging/rtl8188eu/include/rtw_ioctl_set.h
@@ -0,0 +1,42 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2011 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ *
+ ******************************************************************************/
+#ifndef __RTW_IOCTL_SET_H_
+#define __RTW_IOCTL_SET_H_
+
+#include <drv_types.h>
+
+
+typedef u8 NDIS_802_11_PMKID_VALUE[16];
+
+u8 rtw_set_802_11_authentication_mode(struct adapter *adapt,
+ enum ndis_802_11_auth_mode authmode);
+u8 rtw_set_802_11_bssid(struct adapter *adapter, u8 *bssid);
+u8 rtw_set_802_11_add_wep(struct adapter *adapter, struct ndis_802_11_wep *wep);
+u8 rtw_set_802_11_disassociate(struct adapter *adapter);
+u8 rtw_set_802_11_bssid_list_scan(struct adapter *adapter,
+ struct ndis_802_11_ssid *pssid,
+ int ssid_max_num);
+u8 rtw_set_802_11_infrastructure_mode(struct adapter *adapter,
+ enum ndis_802_11_network_infra type);
+u8 rtw_set_802_11_ssid(struct adapter *adapt, struct ndis_802_11_ssid *ssid);
+u16 rtw_get_cur_max_rate(struct adapter *adapter);
+int rtw_set_country(struct adapter *adapter, const char *country_code);
+
+#endif
diff --git a/drivers/staging/rtl8188eu/include/rtw_iol.h b/drivers/staging/rtl8188eu/include/rtw_iol.h
new file mode 100644
index 000000000..68aae7f0b
--- /dev/null
+++ b/drivers/staging/rtl8188eu/include/rtw_iol.h
@@ -0,0 +1,28 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2011 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ *
+ ******************************************************************************/
+#ifndef __RTW_IOL_H_
+#define __RTW_IOL_H_
+
+#include <osdep_service.h>
+#include <drv_types.h>
+
+bool rtw_IOL_applied(struct adapter *adapter);
+
+#endif /* __RTW_IOL_H_ */
diff --git a/drivers/staging/rtl8188eu/include/rtw_led.h b/drivers/staging/rtl8188eu/include/rtw_led.h
new file mode 100644
index 000000000..7a5303d50
--- /dev/null
+++ b/drivers/staging/rtl8188eu/include/rtw_led.h
@@ -0,0 +1,120 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2011 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * 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.
+ *
+ *
+ ******************************************************************************/
+#ifndef __RTW_LED_H_
+#define __RTW_LED_H_
+
+#include <osdep_service.h>
+#include <drv_types.h>
+
+#define LED_BLINK_NO_LINK_INTERVAL_ALPHA 1000
+#define LED_BLINK_LINK_INTERVAL_ALPHA 500 /* 500 */
+#define LED_BLINK_SCAN_INTERVAL_ALPHA 180 /* 150 */
+#define LED_BLINK_FASTER_INTERVAL_ALPHA 50
+#define LED_BLINK_WPS_SUCCESS_INTERVAL_ALPHA 5000
+
+enum LED_CTL_MODE {
+ LED_CTL_POWER_ON,
+ LED_CTL_LINK,
+ LED_CTL_NO_LINK,
+ LED_CTL_TX,
+ LED_CTL_RX ,
+ LED_CTL_SITE_SURVEY,
+ LED_CTL_POWER_OFF,
+ LED_CTL_START_TO_LINK,
+ LED_CTL_START_WPS,
+ LED_CTL_STOP_WPS,
+ LED_CTL_START_WPS_BOTTON,
+ LED_CTL_STOP_WPS_FAIL
+};
+
+enum LED_STATE_871x {
+ LED_UNKNOWN,
+ RTW_LED_ON,
+ RTW_LED_OFF,
+ LED_BLINK_NORMAL,
+ LED_BLINK_SLOWLY,
+ LED_BLINK_POWER_ON,
+ LED_BLINK_SCAN,
+ LED_BLINK_TXRX,
+ LED_BLINK_WPS,
+ LED_BLINK_WPS_STOP
+};
+
+struct LED_871x {
+ struct adapter *padapter;
+
+ enum LED_STATE_871x CurrLedState; /* Current LED state. */
+ enum LED_STATE_871x BlinkingLedState; /* Next state for blinking,
+ * either RTW_LED_ON or RTW_LED_OFF are. */
+
+ u8 bLedOn; /* true if LED is ON, false if LED is OFF. */
+
+ u8 bLedBlinkInProgress; /* true if it is blinking, false o.w.. */
+
+ u8 bLedWPSBlinkInProgress;
+
+ u32 BlinkTimes; /* Number of times to toggle led state for blinking. */
+
+ struct timer_list BlinkTimer; /* Timer object for led blinking. */
+
+ u8 bSWLedCtrl;
+
+ /* ALPHA, added by chiyoko, 20090106 */
+ u8 bLedNoLinkBlinkInProgress;
+ u8 bLedLinkBlinkInProgress;
+ u8 bLedStartToLinkBlinkInProgress;
+ u8 bLedScanBlinkInProgress;
+ struct work_struct BlinkWorkItem; /* Workitem used by BlinkTimer to
+ * manipulate H/W to blink LED. */
+};
+
+#define IS_LED_WPS_BLINKING(_LED_871x) \
+ (((struct LED_871x *)_LED_871x)->CurrLedState == LED_BLINK_WPS || \
+ ((struct LED_871x *)_LED_871x)->CurrLedState == LED_BLINK_WPS_STOP || \
+ ((struct LED_871x *)_LED_871x)->bLedWPSBlinkInProgress)
+
+void LedControl8188eu(struct adapter *padapter, enum LED_CTL_MODE LedAction);
+
+struct led_priv {
+ /* add for led control */
+ struct LED_871x SwLed0;
+ u8 bRegUseLed;
+ void (*LedControlHandler)(struct adapter *padapter,
+ enum LED_CTL_MODE LedAction);
+ /* add for led control */
+};
+
+#define rtw_led_control(adapt, action) \
+ do { \
+ if ((adapt)->ledpriv.LedControlHandler) \
+ (adapt)->ledpriv.LedControlHandler((adapt), (action)); \
+ } while (0)
+
+void BlinkTimerCallback(unsigned long data);
+void BlinkWorkItemCallback(struct work_struct *work);
+
+void ResetLedStatus(struct LED_871x *pLed);
+
+void InitLed871x(struct adapter *padapter, struct LED_871x *pLed);
+
+void DeInitLed871x(struct LED_871x *pLed);
+
+/* hal... */
+void BlinkHandler(struct LED_871x *pLed);
+void SwLedOn(struct adapter *padapter, struct LED_871x *pLed);
+void SwLedOff(struct adapter *padapter, struct LED_871x *pLed);
+
+#endif /* __RTW_LED_H_ */
diff --git a/drivers/staging/rtl8188eu/include/rtw_mlme.h b/drivers/staging/rtl8188eu/include/rtw_mlme.h
new file mode 100644
index 000000000..3f7d1e631
--- /dev/null
+++ b/drivers/staging/rtl8188eu/include/rtw_mlme.h
@@ -0,0 +1,591 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2011 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ *
+ ******************************************************************************/
+#ifndef __RTW_MLME_H_
+#define __RTW_MLME_H_
+
+#include <osdep_service.h>
+#include <mlme_osdep.h>
+#include <drv_types.h>
+#include <wlan_bssdef.h>
+
+#define MAX_BSS_CNT 128
+#define MAX_JOIN_TIMEOUT 6500
+
+/* Increase the scanning timeout because of increasing the SURVEY_TO value. */
+
+#define SCANNING_TIMEOUT 8000
+
+#define SCAN_INTERVAL (30) /* unit:2sec, 30*2=60sec */
+
+#define SCANQUEUE_LIFETIME 20 /* unit:sec */
+
+#define WIFI_NULL_STATE 0x00000000
+
+#define WIFI_ASOC_STATE 0x00000001 /* Under Linked state */
+#define WIFI_REASOC_STATE 0x00000002
+#define WIFI_SLEEP_STATE 0x00000004
+#define WIFI_STATION_STATE 0x00000008
+
+#define WIFI_AP_STATE 0x00000010
+#define WIFI_ADHOC_STATE 0x00000020
+#define WIFI_ADHOC_MASTER_STATE 0x00000040
+#define WIFI_UNDER_LINKING 0x00000080
+
+#define WIFI_UNDER_WPS 0x00000100
+#define WIFI_STA_ALIVE_CHK_STATE 0x00000400
+#define WIFI_SITE_MONITOR 0x00000800 /* to indicate the station is under site surveying */
+
+#define WIFI_MP_STATE 0x00010000
+#define WIFI_MP_CTX_BACKGROUND 0x00020000 /* in continuous tx background */
+#define WIFI_MP_CTX_ST 0x00040000 /* in continuous tx with single-tone */
+#define WIFI_MP_CTX_BACKGROUND_PENDING 0x00080000 /* pending in continuous tx background due to out of skb */
+#define WIFI_MP_CTX_CCK_HW 0x00100000 /* in continuous tx */
+#define WIFI_MP_CTX_CCK_CS 0x00200000 /* in continuous tx with carrier suppression */
+#define WIFI_MP_LPBK_STATE 0x00400000
+
+#define _FW_UNDER_LINKING WIFI_UNDER_LINKING
+#define _FW_LINKED WIFI_ASOC_STATE
+#define _FW_UNDER_SURVEY WIFI_SITE_MONITOR
+
+enum dot11AuthAlgrthmNum {
+ dot11AuthAlgrthm_Open = 0,
+ dot11AuthAlgrthm_Shared,
+ dot11AuthAlgrthm_8021X,
+ dot11AuthAlgrthm_Auto,
+ dot11AuthAlgrthm_WAPI,
+ dot11AuthAlgrthm_MaxNum
+};
+
+/* Scan type including active and passive scan. */
+enum rt_scan_type {
+ SCAN_PASSIVE,
+ SCAN_ACTIVE,
+ SCAN_MIX,
+};
+
+enum SCAN_RESULT_TYPE {
+ SCAN_RESULT_P2P_ONLY = 0, /* Will return all the P2P devices. */
+ SCAN_RESULT_ALL = 1, /* Will return all the scanned device,
+ * include AP. */
+ SCAN_RESULT_WFD_TYPE = 2 /* Will just return the correct WFD
+ * device. */
+ /* If this device is Miracast sink
+ * device, it will just return all the
+ * Miracast source devices. */
+};
+
+/*
+there are several "locks" in mlme_priv,
+since mlme_priv is a shared resource between many threads,
+like ISR/Call-Back functions, the OID handlers, and even timer functions.
+
+Each _queue has its own locks, already.
+Other items are protected by mlme_priv.lock.
+
+To avoid possible dead lock, any thread trying to modifiying mlme_priv
+SHALL not lock up more than one lock at a time!
+*/
+
+#define traffic_threshold 10
+#define traffic_scan_period 500
+
+struct rt_link_detect {
+ u32 NumTxOkInPeriod;
+ u32 NumRxOkInPeriod;
+ u32 NumRxUnicastOkInPeriod;
+ bool bBusyTraffic;
+ bool bTxBusyTraffic;
+ bool bRxBusyTraffic;
+ bool bHigherBusyTraffic; /* For interrupt migration purpose. */
+ bool bHigherBusyRxTraffic; /* We may disable Tx interrupt according
+ * to Rx traffic. */
+ bool bHigherBusyTxTraffic; /* We may disable Tx interrupt according
+ * to Tx traffic. */
+};
+
+struct profile_info {
+ u8 ssidlen;
+ u8 ssid[WLAN_SSID_MAXLEN];
+ u8 peermac[ETH_ALEN];
+};
+
+struct tx_invite_req_info {
+ u8 token;
+ u8 benable;
+ u8 go_ssid[WLAN_SSID_MAXLEN];
+ u8 ssidlen;
+ u8 go_bssid[ETH_ALEN];
+ u8 peer_macaddr[ETH_ALEN];
+ u8 operating_ch; /* This information will be set by using the
+ * p2p_set op_ch=x */
+ u8 peer_ch; /* The listen channel for peer P2P device */
+};
+
+struct tx_invite_resp_info {
+ u8 token; /* Used to record the dialog token of p2p invitation
+ * request frame. */
+};
+
+struct tx_provdisc_req_info {
+ u16 wps_config_method_request; /* Used when sending the
+ * provisioning request frame*/
+ u16 peer_channel_num[2]; /* The channel number which the
+ * receiver stands. */
+ struct ndis_802_11_ssid ssid;
+ u8 peerDevAddr[ETH_ALEN]; /* Peer device address */
+ u8 peerIFAddr[ETH_ALEN]; /* Peer interface address */
+ u8 benable; /* This provision discovery
+ * request frame is trigger
+ * to send or not */
+};
+
+/* When peer device issue prov_disc_req first, we should store the following
+ * information */
+/* The UI must know this information to know which config method the
+ * remote p2p device needs. */
+struct rx_provdisc_req_info {
+ u8 peerDevAddr[ETH_ALEN]; /* Peer device address */
+ u8 strconfig_method_desc_of_prov_disc_req[4]; /* description
+ * for the config method located in the provisioning
+ * discovery request frame. */
+};
+
+struct tx_nego_req_info {
+ u16 peer_channel_num[2]; /* The channel number. */
+ u8 peerDevAddr[ETH_ALEN]; /* Peer device address */
+ u8 benable; /* This negotiation request frame is
+ * trigger to send or not */
+};
+
+struct group_id_info {
+ u8 go_device_addr[ETH_ALEN]; /* The GO's device address of
+ * this P2P group */
+ u8 ssid[WLAN_SSID_MAXLEN]; /* The SSID of this P2P group */
+};
+
+struct scan_limit_info {
+ u8 scan_op_ch_only; /* When this flag is set, the driver
+ * should only scan the op. channel */
+ u8 operation_ch[2]; /* Store the op. chan of invitation */
+};
+
+struct wifidirect_info {
+ struct adapter *padapter;
+ struct timer_list find_phase_timer;
+ struct timer_list restore_p2p_state_timer;
+
+ /* Used to do the scanning. After confirming the peer is availalble,
+ * the driver transmits the P2P frame to peer. */
+ struct timer_list pre_tx_scan_timer;
+ struct timer_list reset_ch_sitesurvey;
+ struct timer_list reset_ch_sitesurvey2; /* Just for resetting the scan
+ * limit function by using p2p nego */
+ struct tx_provdisc_req_info tx_prov_disc_info;
+ struct rx_provdisc_req_info rx_prov_disc_info;
+ struct tx_invite_req_info invitereq_info;
+ /* Store the profile information of persistent group */
+ struct profile_info profileinfo[P2P_MAX_PERSISTENT_GROUP_NUM];
+ struct tx_invite_resp_info inviteresp_info;
+ struct tx_nego_req_info nego_req_info;
+ /* Store the group id info when doing the group negot handshake. */
+ struct group_id_info groupid_info;
+ /* Used for get the limit scan channel from the Invitation procedure */
+ struct scan_limit_info rx_invitereq_info;
+ /* Used for get the limit scan chan from the P2P negotiation handshake*/
+ struct scan_limit_info p2p_info;
+ enum P2P_ROLE role;
+ enum P2P_STATE pre_p2p_state;
+ enum P2P_STATE p2p_state;
+ /* The device address should be the mac address of this device. */
+ u8 device_addr[ETH_ALEN];
+ u8 interface_addr[ETH_ALEN];
+ u8 social_chan[4];
+ u8 listen_channel;
+ u8 operating_channel;
+ u8 listen_dwell; /* This value should be between 1 and 3 */
+ u8 support_rate[8];
+ u8 p2p_wildcard_ssid[P2P_WILDCARD_SSID_LEN];
+ u8 intent; /* should only include the intent value. */
+ u8 p2p_peer_interface_addr[ETH_ALEN];
+ u8 p2p_peer_device_addr[ETH_ALEN];
+ u8 peer_intent; /* Included the intent value and tie breaker value. */
+ /* Device name for displaying on searching device screen */
+ u8 device_name[WPS_MAX_DEVICE_NAME_LEN];
+ u8 device_name_len;
+ u8 profileindex; /* Used to point to the index of profileinfo array */
+ u8 peer_operating_ch;
+ u8 find_phase_state_exchange_cnt;
+ /* The device password ID for group negotiation */
+ u16 device_password_id_for_nego;
+ u8 negotiation_dialog_token;
+ /* SSID information for group negotitation */
+ u8 nego_ssid[WLAN_SSID_MAXLEN];
+ u8 nego_ssidlen;
+ u8 p2p_group_ssid[WLAN_SSID_MAXLEN];
+ u8 p2p_group_ssid_len;
+ /* Flag to know if the persistent function should be supported or not.*/
+ u8 persistent_supported;
+ /* In the Sigma test, the Sigma will provide this enable from the
+ * sta_set_p2p CAPI. */
+ /* 0: disable */
+ /* 1: enable */
+ u8 session_available; /* Flag to set the WFD session available to
+ * enable or disable "by Sigma" */
+ /* In the Sigma test, the Sigma will disable the session available
+ * by using the sta_preset CAPI. */
+ /* 0: disable */
+ /* 1: enable */
+ u8 wfd_tdls_enable; /* Flag to enable or disable the TDLS by WFD Sigma*/
+ /* 0: disable */
+ /* 1: enable */
+ u8 wfd_tdls_weaksec; /* Flag to enable or disable the weak security
+ * function for TDLS by WFD Sigma */
+ /* 0: disable */
+ /* In this case, the driver can't issue the tdsl
+ * setup request frame. */
+ /* 1: enable */
+ /* In this case, the driver can issue the tdls
+ * setup request frame */
+ /* even the current security is weak security. */
+
+ /* This field will store the WPS value (PIN value or PBC) that UI had
+ * got from the user. */
+ enum P2P_WPSINFO ui_got_wps_info;
+ u16 supported_wps_cm; /* This field describes the WPS config method
+ * which this driver supported. */
+ /* The value should be the combination of config
+ * method defined in page104 of WPS v2.0 spec.*/
+ /* This field will contain the length of body of P2P Channel List
+ * attribute of group negotiation response frame. */
+ uint channel_list_attr_len;
+ /* This field will contain the body of P2P Channel List attribute of
+ * group negotitation response frame. */
+ /* We will use the channel_cnt and channel_list fields when constructing
+ * the group negotiation confirm frame. */
+ u8 channel_list_attr[100];
+ enum P2P_PS_MODE p2p_ps_mode; /* indicate p2p ps mode */
+ enum P2P_PS_STATE p2p_ps_state; /* indicate p2p ps state */
+ u8 noa_index; /* Identifies and instance of Notice of Absence timing. */
+ u8 ctwindow; /* Client traffic window. A period of time in TU after TBTT. */
+ u8 opp_ps; /* opportunistic power save. */
+ u8 noa_num; /* number of NoA descriptor in P2P IE. */
+ u8 noa_count[P2P_MAX_NOA_NUM]; /* Count for owner, Type of client. */
+ /* Max duration for owner, preferred or min acceptable duration for
+ * client. */
+ u32 noa_duration[P2P_MAX_NOA_NUM];
+ /* Length of interval for owner, preferred or max acceptable interval
+ * of client. */
+ u32 noa_interval[P2P_MAX_NOA_NUM];
+ /* schedule expressed in terms of the lower 4 bytes of the TSF timer. */
+ u32 noa_start_time[P2P_MAX_NOA_NUM];
+};
+
+struct mlme_priv {
+ spinlock_t lock;
+ int fw_state; /* shall we protect this variable? maybe not necessarily... */
+ u8 bScanInProcess;
+ u8 to_join; /* flag */
+ u8 to_roaming; /* roaming trying times */
+
+ u8 *nic_hdl;
+
+ u8 not_indic_disco;
+ struct list_head *pscanned;
+ struct __queue free_bss_pool;
+ struct __queue scanned_queue;
+ u8 *free_bss_buf;
+ u32 num_of_scanned;
+
+ struct ndis_802_11_ssid assoc_ssid;
+ u8 assoc_bssid[6];
+
+ struct wlan_network cur_network;
+
+ u32 scan_interval;
+
+ struct timer_list assoc_timer;
+
+ uint assoc_by_bssid;
+ uint assoc_by_rssi;
+
+ struct timer_list scan_to_timer; /* driver itself handles scan_timeout status. */
+ u32 scan_start_time; /* used to evaluate the time spent in scanning */
+
+ struct qos_priv qospriv;
+
+ /* Number of non-HT AP/stations */
+ int num_sta_no_ht;
+
+ /* Number of HT AP/stations 20 MHz */
+ /* int num_sta_ht_20mhz; */
+
+ int num_FortyMHzIntolerant;
+ struct ht_priv htpriv;
+ struct rt_link_detect LinkDetectInfo;
+ struct timer_list dynamic_chk_timer; /* dynamic/periodic check timer */
+
+ u8 key_mask; /* use for ips to set wep key after ips_leave */
+ u8 acm_mask; /* for wmm acm mask */
+ u8 ChannelPlan;
+ enum rt_scan_type scan_mode; /* active: 1, passive: 0 */
+
+ /* u8 probereq_wpsie[MAX_WPS_IE_LEN];added in probe req */
+ /* int probereq_wpsie_len; */
+ u8 *wps_probe_req_ie;
+ u32 wps_probe_req_ie_len;
+
+ u8 *assoc_req;
+ u32 assoc_req_len;
+ u8 *assoc_rsp;
+ u32 assoc_rsp_len;
+
+#if defined(CONFIG_88EU_AP_MODE)
+ /* Number of associated Non-ERP stations (i.e., stations using 802.11b
+ * in 802.11g BSS) */
+ int num_sta_non_erp;
+
+ /* Number of associated stations that do not support Short Slot Time */
+ int num_sta_no_short_slot_time;
+
+ /* Number of associated stations that do not support Short Preamble */
+ int num_sta_no_short_preamble;
+
+ int olbc; /* Overlapping Legacy BSS Condition */
+
+ /* Number of HT assoc sta that do not support greenfield */
+ int num_sta_ht_no_gf;
+
+ /* Number of associated non-HT stations */
+ /* int num_sta_no_ht; */
+
+ /* Number of HT associated stations 20 MHz */
+ int num_sta_ht_20mhz;
+
+ /* Overlapping BSS information */
+ int olbc_ht;
+
+ u16 ht_op_mode;
+
+ u8 *wps_beacon_ie;
+ /* u8 *wps_probe_req_ie; */
+ u8 *wps_probe_resp_ie;
+ u8 *wps_assoc_resp_ie;
+
+ u32 wps_beacon_ie_len;
+ u32 wps_probe_resp_ie_len;
+ u32 wps_assoc_resp_ie_len;
+
+ u8 *p2p_beacon_ie;
+ u8 *p2p_probe_req_ie;
+ u8 *p2p_probe_resp_ie;
+ u8 *p2p_go_probe_resp_ie; /* for GO */
+ u8 *p2p_assoc_req_ie;
+
+ u32 p2p_beacon_ie_len;
+ u32 p2p_probe_req_ie_len;
+ u32 p2p_probe_resp_ie_len;
+ u32 p2p_go_probe_resp_ie_len; /* for GO */
+ u32 p2p_assoc_req_ie_len;
+ spinlock_t bcn_update_lock;
+ u8 update_bcn;
+#endif /* if defined (CONFIG_88EU_AP_MODE) */
+};
+
+#ifdef CONFIG_88EU_AP_MODE
+
+struct hostapd_priv {
+ struct adapter *padapter;
+};
+
+int hostapd_mode_init(struct adapter *padapter);
+void hostapd_mode_unload(struct adapter *padapter);
+#endif
+
+extern unsigned char WPA_TKIP_CIPHER[4];
+extern unsigned char RSN_TKIP_CIPHER[4];
+extern unsigned char REALTEK_96B_IE[];
+extern unsigned char MCS_rate_2R[16];
+extern unsigned char MCS_rate_1R[16];
+
+void rtw_joinbss_event_prehandle(struct adapter *adapter, u8 *pbuf);
+void rtw_survey_event_callback(struct adapter *adapter, u8 *pbuf);
+void rtw_surveydone_event_callback(struct adapter *adapter, u8 *pbuf);
+void rtw_joinbss_event_callback(struct adapter *adapter, u8 *pbuf);
+void rtw_stassoc_event_callback(struct adapter *adapter, u8 *pbuf);
+void rtw_stadel_event_callback(struct adapter *adapter, u8 *pbuf);
+void rtw_atimdone_event_callback(struct adapter *adapter, u8 *pbuf);
+void rtw_cpwm_event_callback(struct adapter *adapter, u8 *pbuf);
+void indicate_wx_scan_complete_event(struct adapter *padapter);
+void rtw_indicate_wx_assoc_event(struct adapter *padapter);
+void rtw_indicate_wx_disassoc_event(struct adapter *padapter);
+int event_thread(void *context);
+void rtw_free_network_queue(struct adapter *adapter, u8 isfreeall);
+int rtw_init_mlme_priv(struct adapter *adapter);
+void rtw_free_mlme_priv(struct mlme_priv *pmlmepriv);
+int rtw_select_and_join_from_scanned_queue(struct mlme_priv *pmlmepriv);
+int rtw_set_key(struct adapter *adapter, struct security_priv *psecuritypriv,
+ int keyid, u8 set_tx);
+int rtw_set_auth(struct adapter *adapter, struct security_priv *psecuritypriv);
+
+static inline u8 *get_bssid(struct mlme_priv *pmlmepriv)
+{ /* if sta_mode:pmlmepriv->cur_network.network.MacAddress=> bssid */
+ /* if adhoc_mode:pmlmepriv->cur_network.network.MacAddress=> ibss mac address */
+ return pmlmepriv->cur_network.network.MacAddress;
+}
+
+static inline int check_fwstate(struct mlme_priv *pmlmepriv, int state)
+{
+ if (pmlmepriv->fw_state & state)
+ return true;
+
+ return false;
+}
+
+static inline int get_fwstate(struct mlme_priv *pmlmepriv)
+{
+ return pmlmepriv->fw_state;
+}
+
+/*
+ * No Limit on the calling context,
+ * therefore set it to be the critical section...
+ *
+ * ### NOTE:#### (!!!!)
+ * MUST TAKE CARE THAT BEFORE CALLING THIS FUNC, YOU SHOULD HAVE LOCKED pmlmepriv->lock
+ */
+static inline void set_fwstate(struct mlme_priv *pmlmepriv, int state)
+{
+ pmlmepriv->fw_state |= state;
+ /* FOR HW integration */
+ if (_FW_UNDER_SURVEY == state)
+ pmlmepriv->bScanInProcess = true;
+}
+
+static inline void _clr_fwstate_(struct mlme_priv *pmlmepriv, int state)
+{
+ pmlmepriv->fw_state &= ~state;
+ /* FOR HW integration */
+ if (_FW_UNDER_SURVEY == state)
+ pmlmepriv->bScanInProcess = false;
+}
+
+/*
+ * No Limit on the calling context,
+ * therefore set it to be the critical section...
+ */
+static inline void clr_fwstate(struct mlme_priv *pmlmepriv, int state)
+{
+ spin_lock_bh(&pmlmepriv->lock);
+ if (check_fwstate(pmlmepriv, state) == true)
+ pmlmepriv->fw_state ^= state;
+ spin_unlock_bh(&pmlmepriv->lock);
+}
+
+static inline void clr_fwstate_ex(struct mlme_priv *pmlmepriv, int state)
+{
+ spin_lock_bh(&pmlmepriv->lock);
+ _clr_fwstate_(pmlmepriv, state);
+ spin_unlock_bh(&pmlmepriv->lock);
+}
+
+static inline void up_scanned_network(struct mlme_priv *pmlmepriv)
+{
+ spin_lock_bh(&pmlmepriv->lock);
+ pmlmepriv->num_of_scanned++;
+ spin_unlock_bh(&pmlmepriv->lock);
+}
+
+static inline void down_scanned_network(struct mlme_priv *pmlmepriv)
+{
+ spin_lock_bh(&pmlmepriv->lock);
+ pmlmepriv->num_of_scanned--;
+ spin_unlock_bh(&pmlmepriv->lock);
+}
+
+static inline void set_scanned_network_val(struct mlme_priv *pmlmepriv, int val)
+{
+ spin_lock_bh(&pmlmepriv->lock);
+ pmlmepriv->num_of_scanned = val;
+ spin_unlock_bh(&pmlmepriv->lock);
+}
+
+u16 rtw_get_capability(struct wlan_bssid_ex *bss);
+void rtw_update_scanned_network(struct adapter *adapter,
+ struct wlan_bssid_ex *target);
+void rtw_disconnect_hdl_under_linked(struct adapter *adapter,
+ struct sta_info *psta, u8 free_assoc);
+void rtw_generate_random_ibss(u8 *pibss);
+struct wlan_network *rtw_find_network(struct __queue *scanned_queue, u8 *addr);
+struct wlan_network *rtw_get_oldest_wlan_network(struct __queue *scanned_queue);
+
+void rtw_free_assoc_resources(struct adapter *adapter, int lock_scanned_queue);
+void rtw_indicate_disconnect(struct adapter *adapter);
+void rtw_indicate_connect(struct adapter *adapter);
+void rtw_indicate_scan_done(struct adapter *padapter, bool aborted);
+void rtw_scan_abort(struct adapter *adapter);
+
+int rtw_restruct_sec_ie(struct adapter *adapter, u8 *in_ie, u8 *out_ie,
+ uint in_len);
+int rtw_restruct_wmm_ie(struct adapter *adapter, u8 *in_ie, u8 *out_ie,
+ uint in_len, uint initial_out_len);
+void rtw_init_registrypriv_dev_network(struct adapter *adapter);
+
+void rtw_update_registrypriv_dev_network(struct adapter *adapter);
+
+void rtw_get_encrypt_decrypt_from_registrypriv(struct adapter *adapter);
+
+void _rtw_join_timeout_handler(unsigned long data);
+void rtw_scan_timeout_handler(unsigned long data);
+
+void rtw_dynamic_check_timer_handlder(unsigned long data);
+#define rtw_is_scan_deny(adapter) false
+#define rtw_clear_scan_deny(adapter) do {} while (0)
+#define rtw_set_scan_deny_timer_hdl(adapter) do {} while (0)
+#define rtw_set_scan_deny(adapter, ms) do {} while (0)
+
+void rtw_free_mlme_priv_ie_data(struct mlme_priv *pmlmepriv);
+
+struct wlan_network *_rtw_alloc_network(struct mlme_priv *pmlmepriv);
+
+void _rtw_free_network_nolock(struct mlme_priv *pmlmepriv,
+ struct wlan_network *pnetwork);
+
+int rtw_if_up(struct adapter *padapter);
+
+u8 *rtw_get_capability_from_ie(u8 *ie);
+u8 *rtw_get_beacon_interval_from_ie(u8 *ie);
+
+void rtw_joinbss_reset(struct adapter *padapter);
+
+unsigned int rtw_restructure_ht_ie(struct adapter *padapter, u8 *in_ie,
+ u8 *out_ie, uint in_len, uint *pout_len);
+void rtw_update_ht_cap(struct adapter *padapter, u8 *pie, uint ie_len);
+void rtw_issue_addbareq_cmd(struct adapter *padapter,
+ struct xmit_frame *pxmitframe);
+
+int rtw_is_same_ibss(struct adapter *adapter, struct wlan_network *pnetwork);
+int is_same_network(struct wlan_bssid_ex *src, struct wlan_bssid_ex *dst);
+
+void rtw_roaming(struct adapter *padapter, struct wlan_network *tgt_network);
+void _rtw_roaming(struct adapter *padapter, struct wlan_network *tgt_network);
+
+void rtw_stassoc_hw_rpt(struct adapter *adapter, struct sta_info *psta);
+
+#endif /* __RTL871X_MLME_H_ */
diff --git a/drivers/staging/rtl8188eu/include/rtw_mlme_ext.h b/drivers/staging/rtl8188eu/include/rtw_mlme_ext.h
new file mode 100644
index 000000000..2bebf46b0
--- /dev/null
+++ b/drivers/staging/rtl8188eu/include/rtw_mlme_ext.h
@@ -0,0 +1,806 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2011 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ *
+ ******************************************************************************/
+#ifndef __RTW_MLME_EXT_H_
+#define __RTW_MLME_EXT_H_
+
+#include <osdep_service.h>
+#include <drv_types.h>
+#include <wlan_bssdef.h>
+
+
+/* Commented by Albert 20101105 */
+/* Increase the SURVEY_TO value from 100 to 150 ( 100ms to 150ms ) */
+/* The Realtek 8188CE SoftAP will spend around 100ms to send the probe response after receiving the probe request. */
+/* So, this driver tried to extend the dwell time for each scanning channel. */
+/* This will increase the chance to receive the probe response from SoftAP. */
+
+#define SURVEY_TO (100)
+#define REAUTH_TO (300) /* 50) */
+#define REASSOC_TO (300) /* 50) */
+/* define DISCONNECT_TO (3000) */
+#define ADDBA_TO (2000)
+
+#define LINKED_TO (1) /* unit:2 sec, 1x2=2 sec */
+
+#define REAUTH_LIMIT (4)
+#define REASSOC_LIMIT (4)
+#define READDBA_LIMIT (2)
+
+#define ROAMING_LIMIT 8
+
+#define DYNAMIC_FUNC_DISABLE (0x0)
+
+/* ====== ODM_ABILITY_E ======== */
+/* BB ODM section BIT 0-15 */
+#define DYNAMIC_BB_DIG BIT(0)
+#define DYNAMIC_BB_RA_MASK BIT(1)
+#define DYNAMIC_BB_DYNAMIC_TXPWR BIT(2)
+#define DYNAMIC_BB_BB_FA_CNT BIT(3)
+
+#define DYNAMIC_BB_RSSI_MONITOR BIT(4)
+#define DYNAMIC_BB_CCK_PD BIT(5)
+#define DYNAMIC_BB_ANT_DIV BIT(6)
+#define DYNAMIC_BB_PWR_SAVE BIT(7)
+#define DYNAMIC_BB_PWR_TRA BIT(8)
+#define DYNAMIC_BB_RATE_ADAPTIVE BIT(9)
+#define DYNAMIC_BB_PATH_DIV BIT(10)
+#define DYNAMIC_BB_PSD BIT(11)
+
+/* MAC DM section BIT 16-23 */
+#define DYNAMIC_MAC_EDCA_TURBO BIT(16)
+#define DYNAMIC_MAC_EARLY_MODE BIT(17)
+
+/* RF ODM section BIT 24-31 */
+#define DYNAMIC_RF_TX_PWR_TRACK BIT(24)
+#define DYNAMIC_RF_RX_GAIN_TRACK BIT(25)
+#define DYNAMIC_RF_CALIBRATION BIT(26)
+
+#define DYNAMIC_ALL_FUNC_ENABLE 0xFFFFFFF
+
+#define _HW_STATE_NOLINK_ 0x00
+#define _HW_STATE_ADHOC_ 0x01
+#define _HW_STATE_STATION_ 0x02
+#define _HW_STATE_AP_ 0x03
+
+
+#define _1M_RATE_ 0
+#define _2M_RATE_ 1
+#define _5M_RATE_ 2
+#define _11M_RATE_ 3
+#define _6M_RATE_ 4
+#define _9M_RATE_ 5
+#define _12M_RATE_ 6
+#define _18M_RATE_ 7
+#define _24M_RATE_ 8
+#define _36M_RATE_ 9
+#define _48M_RATE_ 10
+#define _54M_RATE_ 11
+
+
+extern unsigned char RTW_WPA_OUI[];
+extern unsigned char WMM_OUI[];
+extern unsigned char WPS_OUI[];
+extern unsigned char WFD_OUI[];
+extern unsigned char P2P_OUI[];
+
+extern unsigned char WMM_INFO_OUI[];
+extern unsigned char WMM_PARA_OUI[];
+
+/* Channel Plan Type. */
+/* Note: */
+/* We just add new channel plan when the new channel plan is different
+ * from any of the following channel plan. */
+/* If you just want to customize the actions(scan period or join actions)
+ * about one of the channel plan, */
+/* customize them in struct rt_channel_info in the RT_CHANNEL_LIST. */
+enum RT_CHANNEL_DOMAIN {
+ /* old channel plan mapping ===== */
+ RT_CHANNEL_DOMAIN_FCC = 0x00,
+ RT_CHANNEL_DOMAIN_IC = 0x01,
+ RT_CHANNEL_DOMAIN_ETSI = 0x02,
+ RT_CHANNEL_DOMAIN_SPAIN = 0x03,
+ RT_CHANNEL_DOMAIN_FRANCE = 0x04,
+ RT_CHANNEL_DOMAIN_MKK = 0x05,
+ RT_CHANNEL_DOMAIN_MKK1 = 0x06,
+ RT_CHANNEL_DOMAIN_ISRAEL = 0x07,
+ RT_CHANNEL_DOMAIN_TELEC = 0x08,
+ RT_CHANNEL_DOMAIN_GLOBAL_DOAMIN = 0x09,
+ RT_CHANNEL_DOMAIN_WORLD_WIDE_13 = 0x0A,
+ RT_CHANNEL_DOMAIN_TAIWAN = 0x0B,
+ RT_CHANNEL_DOMAIN_CHINA = 0x0C,
+ RT_CHANNEL_DOMAIN_SINGAPORE_INDIA_MEXICO = 0x0D,
+ RT_CHANNEL_DOMAIN_KOREA = 0x0E,
+ RT_CHANNEL_DOMAIN_TURKEY = 0x0F,
+ RT_CHANNEL_DOMAIN_JAPAN = 0x10,
+ RT_CHANNEL_DOMAIN_FCC_NO_DFS = 0x11,
+ RT_CHANNEL_DOMAIN_JAPAN_NO_DFS = 0x12,
+ RT_CHANNEL_DOMAIN_WORLD_WIDE_5G = 0x13,
+ RT_CHANNEL_DOMAIN_TAIWAN_NO_DFS = 0x14,
+
+ /* new channel plan mapping, (2GDOMAIN_5GDOMAIN) ===== */
+ RT_CHANNEL_DOMAIN_WORLD_NULL = 0x20,
+ RT_CHANNEL_DOMAIN_ETSI1_NULL = 0x21,
+ RT_CHANNEL_DOMAIN_FCC1_NULL = 0x22,
+ RT_CHANNEL_DOMAIN_MKK1_NULL = 0x23,
+ RT_CHANNEL_DOMAIN_ETSI2_NULL = 0x24,
+ RT_CHANNEL_DOMAIN_FCC1_FCC1 = 0x25,
+ RT_CHANNEL_DOMAIN_WORLD_ETSI1 = 0x26,
+ RT_CHANNEL_DOMAIN_MKK1_MKK1 = 0x27,
+ RT_CHANNEL_DOMAIN_WORLD_KCC1 = 0x28,
+ RT_CHANNEL_DOMAIN_WORLD_FCC2 = 0x29,
+ RT_CHANNEL_DOMAIN_WORLD_FCC3 = 0x30,
+ RT_CHANNEL_DOMAIN_WORLD_FCC4 = 0x31,
+ RT_CHANNEL_DOMAIN_WORLD_FCC5 = 0x32,
+ RT_CHANNEL_DOMAIN_WORLD_FCC6 = 0x33,
+ RT_CHANNEL_DOMAIN_FCC1_FCC7 = 0x34,
+ RT_CHANNEL_DOMAIN_WORLD_ETSI2 = 0x35,
+ RT_CHANNEL_DOMAIN_WORLD_ETSI3 = 0x36,
+ RT_CHANNEL_DOMAIN_MKK1_MKK2 = 0x37,
+ RT_CHANNEL_DOMAIN_MKK1_MKK3 = 0x38,
+ RT_CHANNEL_DOMAIN_FCC1_NCC1 = 0x39,
+ RT_CHANNEL_DOMAIN_FCC1_NCC2 = 0x40,
+ RT_CHANNEL_DOMAIN_GLOBAL_DOAMIN_2G = 0x41,
+ /* Add new channel plan above this line=============== */
+ RT_CHANNEL_DOMAIN_MAX,
+ RT_CHANNEL_DOMAIN_REALTEK_DEFINE = 0x7F,
+};
+
+enum RT_CHANNEL_DOMAIN_2G {
+ RT_CHANNEL_DOMAIN_2G_WORLD = 0x00, /* Worldwide 13 */
+ RT_CHANNEL_DOMAIN_2G_ETSI1 = 0x01, /* Europe */
+ RT_CHANNEL_DOMAIN_2G_FCC1 = 0x02, /* US */
+ RT_CHANNEL_DOMAIN_2G_MKK1 = 0x03, /* Japan */
+ RT_CHANNEL_DOMAIN_2G_ETSI2 = 0x04, /* France */
+ RT_CHANNEL_DOMAIN_2G_NULL = 0x05,
+ /* Add new channel plan above this line=============== */
+ RT_CHANNEL_DOMAIN_2G_MAX,
+};
+
+#define rtw_is_channel_plan_valid(chplan) \
+ (chplan < RT_CHANNEL_DOMAIN_MAX || \
+ chplan == RT_CHANNEL_DOMAIN_REALTEK_DEFINE)
+
+struct rt_channel_plan {
+ unsigned char Channel[MAX_CHANNEL_NUM];
+ unsigned char Len;
+};
+
+struct rt_channel_plan_2g {
+ unsigned char Channel[MAX_CHANNEL_NUM_2G];
+ unsigned char Len;
+};
+
+struct rt_channel_plan_map {
+ unsigned char Index2G;
+};
+
+enum Associated_AP {
+ atherosAP = 0,
+ broadcomAP = 1,
+ ciscoAP = 2,
+ marvellAP = 3,
+ ralinkAP = 4,
+ realtekAP = 5,
+ airgocapAP = 6,
+ unknownAP = 7,
+ maxAP,
+};
+
+enum HT_IOT_PEER {
+ HT_IOT_PEER_UNKNOWN = 0,
+ HT_IOT_PEER_REALTEK = 1,
+ HT_IOT_PEER_REALTEK_92SE = 2,
+ HT_IOT_PEER_BROADCOM = 3,
+ HT_IOT_PEER_RALINK = 4,
+ HT_IOT_PEER_ATHEROS = 5,
+ HT_IOT_PEER_CISCO = 6,
+ HT_IOT_PEER_MERU = 7,
+ HT_IOT_PEER_MARVELL = 8,
+ HT_IOT_PEER_REALTEK_SOFTAP = 9,/* peer is RealTek SOFT_AP */
+ HT_IOT_PEER_SELF_SOFTAP = 10, /* Self is SoftAP */
+ HT_IOT_PEER_AIRGO = 11,
+ HT_IOT_PEER_INTEL = 12,
+ HT_IOT_PEER_RTK_APCLIENT = 13,
+ HT_IOT_PEER_REALTEK_81XX = 14,
+ HT_IOT_PEER_REALTEK_WOW = 15,
+ HT_IOT_PEER_TENDA = 16,
+ HT_IOT_PEER_MAX = 17
+};
+
+enum SCAN_STATE {
+ SCAN_DISABLE = 0,
+ SCAN_START = 1,
+ SCAN_TXNULL = 2,
+ SCAN_PROCESS = 3,
+ SCAN_COMPLETE = 4,
+ SCAN_STATE_MAX,
+};
+
+struct mlme_handler {
+ unsigned int num;
+ char *str;
+ unsigned int (*func)(struct adapter *adapt, struct recv_frame *frame);
+};
+
+struct action_handler {
+ unsigned int num;
+ char *str;
+ unsigned int (*func)(struct adapter *adapt, struct recv_frame *frame);
+};
+
+struct ss_res {
+ int state;
+ int bss_cnt;
+ int channel_idx;
+ int scan_mode;
+ u8 ssid_num;
+ u8 ch_num;
+ struct ndis_802_11_ssid ssid[RTW_SSID_SCAN_AMOUNT];
+ struct rtw_ieee80211_channel ch[RTW_CHANNEL_SCAN_AMOUNT];
+};
+
+/* define AP_MODE 0x0C */
+/* define STATION_MODE 0x08 */
+/* define AD_HOC_MODE 0x04 */
+/* define NO_LINK_MODE 0x00 */
+
+#define WIFI_FW_NULL_STATE _HW_STATE_NOLINK_
+#define WIFI_FW_STATION_STATE _HW_STATE_STATION_
+#define WIFI_FW_AP_STATE _HW_STATE_AP_
+#define WIFI_FW_ADHOC_STATE _HW_STATE_ADHOC_
+
+#define WIFI_FW_AUTH_NULL 0x00000100
+#define WIFI_FW_AUTH_STATE 0x00000200
+#define WIFI_FW_AUTH_SUCCESS 0x00000400
+
+#define WIFI_FW_ASSOC_STATE 0x00002000
+#define WIFI_FW_ASSOC_SUCCESS 0x00004000
+
+#define WIFI_FW_LINKING_STATE (WIFI_FW_AUTH_NULL | \
+ WIFI_FW_AUTH_STATE | \
+ WIFI_FW_AUTH_SUCCESS | \
+ WIFI_FW_ASSOC_STATE)
+
+struct FW_Sta_Info {
+ struct sta_info *psta;
+ u32 status;
+ u32 rx_pkt;
+ u32 retry;
+ unsigned char SupportedRates[NDIS_802_11_LENGTH_RATES_EX];
+};
+
+/*
+ * Usage:
+ * When one iface acted as AP mode and the other iface is STA mode and scanning,
+ * it should switch back to AP's operating channel periodically.
+ * Parameters info:
+ * When the driver scanned RTW_SCAN_NUM_OF_CH channels, it would switch back to
+ * AP's operating channel for
+ * RTW_STAY_AP_CH_MILLISECOND * SURVEY_TO milliseconds.
+ * Example:
+ * For chip supports 2.4G + 5GHz and AP mode is operating in channel 1,
+ * RTW_SCAN_NUM_OF_CH is 8, RTW_STAY_AP_CH_MS is 3 and SURVEY_TO is 100.
+ * When it's STA mode gets set_scan command,
+ * it would
+ * 1. Doing the scan on channel 1.2.3.4.5.6.7.8
+ * 2. Back to channel 1 for 300 milliseconds
+ * 3. Go through doing site survey on channel 9.10.11.36.40.44.48.52
+ * 4. Back to channel 1 for 300 milliseconds
+ * 5. ... and so on, till survey done.
+ */
+
+struct mlme_ext_info {
+ u32 state;
+ u32 reauth_count;
+ u32 reassoc_count;
+ u32 link_count;
+ u32 auth_seq;
+ u32 auth_algo; /* 802.11 auth, could be open, shared, auto */
+ u32 authModeToggle;
+ u32 enc_algo;/* encrypt algorithm; */
+ u32 key_index; /* this is only valid for legacy wep,
+ * 0~3 for key id. */
+ u32 iv;
+ u8 chg_txt[128];
+ u16 aid;
+ u16 bcn_interval;
+ u16 capability;
+ u8 assoc_AP_vendor;
+ u8 slotTime;
+ u8 preamble_mode;
+ u8 WMM_enable;
+ u8 ERP_enable;
+ u8 ERP_IE;
+ u8 HT_enable;
+ u8 HT_caps_enable;
+ u8 HT_info_enable;
+ u8 HT_protection;
+ u8 turboMode_cts2self;
+ u8 turboMode_rtsen;
+ u8 SM_PS;
+ u8 agg_enable_bitmap;
+ u8 ADDBA_retry_count;
+ u8 candidate_tid_bitmap;
+ u8 dialogToken;
+ /* Accept ADDBA Request */
+ bool bAcceptAddbaReq;
+ u8 bwmode_updated;
+ u8 hidden_ssid_mode;
+
+ struct ADDBA_request ADDBA_req;
+ struct WMM_para_element WMM_param;
+ struct HT_caps_element HT_caps;
+ struct HT_info_element HT_info;
+ struct wlan_bssid_ex network;/* join network or bss_network,
+ * if in ap mode, it is the same
+ * as cur_network.network */
+ struct FW_Sta_Info FW_sta_info[NUM_STA];
+};
+
+/* The channel information about this channel including joining,
+ * scanning, and power constraints. */
+struct rt_channel_info {
+ u8 ChannelNum; /* The channel number. */
+ enum rt_scan_type ScanType; /* Scan type such as passive
+ * or active scan. */
+ u32 rx_count;
+};
+
+int rtw_ch_set_search_ch(struct rt_channel_info *ch_set, const u32 ch);
+
+/* P2P_MAX_REG_CLASSES - Maximum number of regulatory classes */
+#define P2P_MAX_REG_CLASSES 10
+
+/* P2P_MAX_REG_CLASS_CHANNELS - Maximum number of chan per regulatory class */
+#define P2P_MAX_REG_CLASS_CHANNELS 20
+
+/* struct p2p_channels - List of supported channels */
+struct p2p_channels {
+ /* struct p2p_reg_class - Supported regulatory class */
+ struct p2p_reg_class {
+ /* reg_class - Regulatory class (IEEE 802.11-2007, Annex J) */
+ u8 reg_class;
+
+ /* channel - Supported channels */
+ u8 channel[P2P_MAX_REG_CLASS_CHANNELS];
+
+ /* channels - Number of channel entries in use */
+ size_t channels;
+ } reg_class[P2P_MAX_REG_CLASSES];
+
+ /* reg_classes - Number of reg_class entries in use */
+ size_t reg_classes;
+};
+
+struct p2p_oper_class_map {
+ enum hw_mode {IEEE80211G} mode;
+ u8 op_class;
+ u8 min_chan;
+ u8 max_chan;
+ u8 inc;
+ enum {BW20, BW40PLUS, BW40MINUS} bw;
+};
+
+struct mlme_ext_priv {
+ struct adapter *padapter;
+ u8 mlmeext_init;
+ atomic_t event_seq;
+ u16 mgnt_seq;
+
+ unsigned char cur_channel;
+ unsigned char cur_bwmode;
+ unsigned char cur_ch_offset;/* PRIME_CHNL_OFFSET */
+ unsigned char cur_wireless_mode; /* NETWORK_TYPE */
+
+ unsigned char oper_channel; /* saved chan info when call
+ * set_channel_bw */
+ unsigned char oper_bwmode;
+ unsigned char oper_ch_offset;/* PRIME_CHNL_OFFSET */
+
+ unsigned char max_chan_nums;
+ struct rt_channel_info channel_set[MAX_CHANNEL_NUM];
+ struct p2p_channels channel_list;
+ unsigned char basicrate[NumRates];
+ unsigned char datarate[NumRates];
+
+ struct ss_res sitesurvey_res;
+ struct mlme_ext_info mlmext_info;/* for sta/adhoc mode, including
+ * current scan/connecting/connected
+ * related info. For ap mode,
+ * network includes ap's cap_info*/
+ struct timer_list survey_timer;
+ struct timer_list link_timer;
+ u16 chan_scan_time;
+
+ u8 scan_abort;
+ u8 tx_rate; /* TXRATE when USERATE is set. */
+
+ u32 retry; /* retry for issue probereq */
+
+ u64 TSFValue;
+
+#ifdef CONFIG_88EU_AP_MODE
+ unsigned char bstart_bss;
+#endif
+ u8 update_channel_plan_by_ap_done;
+ /* recv_decache check for Action_public frame */
+ u8 action_public_dialog_token;
+ u16 action_public_rxseq;
+ u8 active_keep_alive_check;
+};
+
+int init_mlme_ext_priv(struct adapter *adapter);
+int init_hw_mlme_ext(struct adapter *padapter);
+void free_mlme_ext_priv(struct mlme_ext_priv *pmlmeext);
+extern void init_mlme_ext_timer(struct adapter *padapter);
+extern void init_addba_retry_timer(struct adapter *adapt, struct sta_info *sta);
+extern struct xmit_frame *alloc_mgtxmitframe(struct xmit_priv *pxmitpriv);
+
+unsigned char networktype_to_raid(unsigned char network_type);
+u8 judge_network_type(struct adapter *padapter, unsigned char *rate, int len);
+void get_rate_set(struct adapter *padapter, unsigned char *pbssrate, int *len);
+void UpdateBrateTbl(struct adapter *padapter, u8 *mBratesOS);
+void UpdateBrateTblForSoftAP(u8 *bssrateset, u32 bssratelen);
+
+void Save_DM_Func_Flag(struct adapter *padapter);
+void Restore_DM_Func_Flag(struct adapter *padapter);
+void Switch_DM_Func(struct adapter *padapter, u32 mode, u8 enable);
+
+void Set_MSR(struct adapter *padapter, u8 type);
+
+u8 rtw_get_oper_ch(struct adapter *adapter);
+void rtw_set_oper_ch(struct adapter *adapter, u8 ch);
+u8 rtw_get_oper_bw(struct adapter *adapter);
+void rtw_set_oper_bw(struct adapter *adapter, u8 bw);
+u8 rtw_get_oper_choffset(struct adapter *adapter);
+void rtw_set_oper_choffset(struct adapter *adapter, u8 offset);
+
+void set_channel_bwmode(struct adapter *padapter, unsigned char channel,
+ unsigned char channel_offset, unsigned short bwmode);
+void SelectChannel(struct adapter *padapter, unsigned char channel);
+void SetBWMode(struct adapter *padapter, unsigned short bwmode,
+ unsigned char channel_offset);
+
+unsigned int decide_wait_for_beacon_timeout(unsigned int bcn_interval);
+
+void write_cam(struct adapter *padapter, u8 entry, u16 ctrl, u8 *mac, u8 *key);
+void clear_cam_entry(struct adapter *padapter, u8 entry);
+
+void invalidate_cam_all(struct adapter *padapter);
+void CAM_empty_entry(struct adapter *Adapter, u8 ucIndex);
+
+int allocate_fw_sta_entry(struct adapter *padapter);
+void flush_all_cam_entry(struct adapter *padapter);
+
+void site_survey(struct adapter *padapter);
+u8 collect_bss_info(struct adapter *padapter, struct recv_frame *precv_frame,
+ struct wlan_bssid_ex *bssid);
+void update_network(struct wlan_bssid_ex *dst, struct wlan_bssid_ex *src,
+ struct adapter *adapter, bool update_ie);
+
+int get_bsstype(unsigned short capability);
+u16 get_beacon_interval(struct wlan_bssid_ex *bss);
+
+int is_client_associated_to_ap(struct adapter *padapter);
+int is_client_associated_to_ibss(struct adapter *padapter);
+int is_IBSS_empty(struct adapter *padapter);
+
+unsigned char check_assoc_AP(u8 *pframe, uint len);
+
+int WMM_param_handler(struct adapter *padapter, struct ndis_802_11_var_ie *pIE);
+void WMMOnAssocRsp(struct adapter *padapter);
+
+void HT_caps_handler(struct adapter *padapter, struct ndis_802_11_var_ie *pIE);
+void HT_info_handler(struct adapter *padapter, struct ndis_802_11_var_ie *pIE);
+void HTOnAssocRsp(struct adapter *padapter);
+
+void ERP_IE_handler(struct adapter *padapter, struct ndis_802_11_var_ie *pIE);
+void VCS_update(struct adapter *padapter, struct sta_info *psta);
+
+void update_beacon_info(struct adapter *padapter, u8 *pframe, uint len,
+ struct sta_info *psta);
+int rtw_check_bcn_info(struct adapter *Adapter, u8 *pframe, u32 packet_len);
+void update_IOT_info(struct adapter *padapter);
+void update_capinfo(struct adapter *adapter, u16 updatecap);
+void update_wireless_mode(struct adapter *padapter);
+void update_tx_basic_rate(struct adapter *padapter, u8 modulation);
+void update_bmc_sta_support_rate(struct adapter *padapter, u32 mac_id);
+int update_sta_support_rate(struct adapter *padapter, u8 *pvar_ie,
+ uint var_ie_len, int cam_idx);
+
+/* for sta/adhoc mode */
+void update_sta_info(struct adapter *padapter, struct sta_info *psta);
+unsigned int update_basic_rate(unsigned char *ptn, unsigned int ptn_sz);
+unsigned int update_supported_rate(unsigned char *ptn, unsigned int ptn_sz);
+unsigned int update_MSC_rate(struct HT_caps_element *pHT_caps);
+void Update_RA_Entry(struct adapter *padapter, u32 mac_id);
+void set_sta_rate(struct adapter *padapter, struct sta_info *psta);
+
+unsigned int receive_disconnect(struct adapter *padapter,
+ unsigned char *macaddr, unsigned short reason);
+
+unsigned char get_highest_rate_idx(u32 mask);
+int support_short_GI(struct adapter *padapter, struct HT_caps_element *caps);
+unsigned int is_ap_in_tkip(struct adapter *padapter);
+unsigned int is_ap_in_wep(struct adapter *padapter);
+unsigned int should_forbid_n_rate(struct adapter *padapter);
+
+void report_join_res(struct adapter *padapter, int res);
+void report_survey_event(struct adapter *padapter,
+ struct recv_frame *precv_frame);
+void report_surveydone_event(struct adapter *padapter);
+void report_del_sta_event(struct adapter *padapter,
+ unsigned char *addr, unsigned short reason);
+void report_add_sta_event(struct adapter *padapter, unsigned char *addr,
+ int cam_idx);
+
+void beacon_timing_control(struct adapter *padapter);
+extern u8 set_tx_beacon_cmd(struct adapter *padapter);
+unsigned int setup_beacon_frame(struct adapter *padapter,
+ unsigned char *beacon_frame);
+void update_mgnt_tx_rate(struct adapter *padapter, u8 rate);
+void update_mgntframe_attrib(struct adapter *padapter,
+ struct pkt_attrib *pattrib);
+void dump_mgntframe(struct adapter *padapter, struct xmit_frame *pmgntframe);
+s32 dump_mgntframe_and_wait(struct adapter *padapter,
+ struct xmit_frame *pmgntframe, int timeout_ms);
+s32 dump_mgntframe_and_wait_ack(struct adapter *padapter,
+ struct xmit_frame *pmgntframe);
+
+void issue_beacon(struct adapter *padapter, int timeout_ms);
+void issue_probersp(struct adapter *padapter, unsigned char *da,
+ u8 is_valid_p2p_probereq);
+void issue_assocreq(struct adapter *padapter);
+void issue_asocrsp(struct adapter *padapter, unsigned short status,
+ struct sta_info *pstat, int pkt_type);
+void issue_auth(struct adapter *padapter, struct sta_info *psta,
+ unsigned short status);
+void issue_probereq(struct adapter *padapter, struct ndis_802_11_ssid *pssid,
+ u8 *da);
+s32 issue_probereq_ex(struct adapter *adapter, struct ndis_802_11_ssid *pssid,
+ u8 *da, int try_cnt, int wait_ms);
+int issue_nulldata(struct adapter *padapter, unsigned char *da,
+ unsigned int power_mode, int try_cnt, int wait_ms);
+int issue_qos_nulldata(struct adapter *padapter, unsigned char *da,
+ u16 tid, int try_cnt, int wait_ms);
+int issue_deauth(struct adapter *padapter, unsigned char *da,
+ unsigned short reason);
+int issue_deauth_ex(struct adapter *padapter, u8 *da, unsigned short reason,
+ int try_cnt, int wait_ms);
+void issue_action_spct_ch_switch(struct adapter *padapter, u8 *ra, u8 new_ch,
+ u8 ch_offset);
+void issue_action_BA(struct adapter *padapter, unsigned char *raddr,
+ unsigned char action, unsigned short status);
+unsigned int send_delba(struct adapter *padapter, u8 initiator, u8 *addr);
+unsigned int send_beacon(struct adapter *padapter);
+
+void start_clnt_assoc(struct adapter *padapter);
+void start_clnt_auth(struct adapter *padapter);
+void start_clnt_join(struct adapter *padapter);
+void start_create_ibss(struct adapter *padapter);
+
+unsigned int OnAssocReq(struct adapter *padapter,
+ struct recv_frame *precv_frame);
+unsigned int OnAssocRsp(struct adapter *padapter,
+ struct recv_frame *precv_frame);
+unsigned int OnProbeReq(struct adapter *padapter,
+ struct recv_frame *precv_frame);
+unsigned int OnProbeRsp(struct adapter *padapter,
+ struct recv_frame *precv_frame);
+unsigned int DoReserved(struct adapter *padapter,
+ struct recv_frame *precv_frame);
+unsigned int OnBeacon(struct adapter *padapter,
+ struct recv_frame *precv_frame);
+unsigned int OnAtim(struct adapter *padapter,
+ struct recv_frame *precv_frame);
+unsigned int OnDisassoc(struct adapter *padapter,
+ struct recv_frame *precv_frame);
+unsigned int OnAuth(struct adapter *padapter,
+ struct recv_frame *precv_frame);
+unsigned int OnAuthClient(struct adapter *padapter,
+ struct recv_frame *precv_frame);
+unsigned int OnDeAuth(struct adapter *padapter,
+ struct recv_frame *precv_frame);
+unsigned int OnAction(struct adapter *padapter,
+ struct recv_frame *precv_frame);
+
+unsigned int on_action_spct(struct adapter *padapter,
+ struct recv_frame *precv_frame);
+unsigned int OnAction_qos(struct adapter *padapter,
+ struct recv_frame *precv_frame);
+unsigned int OnAction_dls(struct adapter *padapter,
+ struct recv_frame *precv_frame);
+unsigned int OnAction_back(struct adapter *padapter,
+ struct recv_frame *precv_frame);
+unsigned int on_action_public(struct adapter *padapter,
+ struct recv_frame *precv_frame);
+unsigned int OnAction_ht(struct adapter *padapter,
+ struct recv_frame *precv_frame);
+unsigned int OnAction_wmm(struct adapter *padapter,
+ struct recv_frame *precv_frame);
+unsigned int OnAction_p2p(struct adapter *padapter,
+ struct recv_frame *precv_frame);
+
+void mlmeext_joinbss_event_callback(struct adapter *padapter, int join_res);
+void mlmeext_sta_del_event_callback(struct adapter *padapter);
+void mlmeext_sta_add_event_callback(struct adapter *padapter,
+ struct sta_info *psta);
+
+void linked_status_chk(struct adapter *padapter);
+
+void survey_timer_hdl(unsigned long data);
+void link_timer_hdl(unsigned long data);
+void addba_timer_hdl(unsigned long data);
+
+#define set_survey_timer(mlmeext, ms) \
+ mod_timer(&mlmeext->survey_timer, jiffies + \
+ msecs_to_jiffies(ms))
+
+#define set_link_timer(mlmeext, ms) \
+ mod_timer(&mlmeext->link_timer, jiffies + \
+ msecs_to_jiffies(ms))
+
+int cckrates_included(unsigned char *rate, int ratelen);
+int cckratesonly_included(unsigned char *rate, int ratelen);
+
+void process_addba_req(struct adapter *padapter, u8 *paddba_req, u8 *addr);
+
+void update_TSF(struct mlme_ext_priv *pmlmeext, u8 *pframe, uint len);
+void correct_TSF(struct adapter *padapter, struct mlme_ext_priv *pmlmeext);
+
+struct cmd_hdl {
+ uint parmsize;
+ u8 (*h2cfuns)(struct adapter *padapter, u8 *pbuf);
+};
+
+u8 read_macreg_hdl(struct adapter *padapter, u8 *pbuf);
+u8 write_macreg_hdl(struct adapter *padapter, u8 *pbuf);
+u8 read_bbreg_hdl(struct adapter *padapter, u8 *pbuf);
+u8 write_bbreg_hdl(struct adapter *padapter, u8 *pbuf);
+u8 read_rfreg_hdl(struct adapter *padapter, u8 *pbuf);
+u8 write_rfreg_hdl(struct adapter *padapter, u8 *pbuf);
+u8 join_cmd_hdl(struct adapter *padapter, u8 *pbuf);
+u8 disconnect_hdl(struct adapter *padapter, u8 *pbuf);
+u8 createbss_hdl(struct adapter *padapter, u8 *pbuf);
+u8 setopmode_hdl(struct adapter *padapter, u8 *pbuf);
+u8 sitesurvey_cmd_hdl(struct adapter *padapter, u8 *pbuf);
+u8 setauth_hdl(struct adapter *padapter, u8 *pbuf);
+u8 setkey_hdl(struct adapter *padapter, u8 *pbuf);
+u8 set_stakey_hdl(struct adapter *padapter, u8 *pbuf);
+u8 set_assocsta_hdl(struct adapter *padapter, u8 *pbuf);
+u8 del_assocsta_hdl(struct adapter *padapter, u8 *pbuf);
+u8 add_ba_hdl(struct adapter *padapter, unsigned char *pbuf);
+
+u8 mlme_evt_hdl(struct adapter *padapter, unsigned char *pbuf);
+u8 h2c_msg_hdl(struct adapter *padapter, unsigned char *pbuf);
+u8 tx_beacon_hdl(struct adapter *padapter, unsigned char *pbuf);
+u8 set_ch_hdl(struct adapter *padapter, u8 *pbuf);
+u8 set_chplan_hdl(struct adapter *padapter, unsigned char *pbuf);
+u8 led_blink_hdl(struct adapter *padapter, unsigned char *pbuf);
+/* Handling DFS channel switch announcement ie. */
+u8 set_csa_hdl(struct adapter *padapter, unsigned char *pbuf);
+u8 tdls_hdl(struct adapter *padapter, unsigned char *pbuf);
+
+#define GEN_DRV_CMD_HANDLER(size, cmd) {size, &cmd ## _hdl},
+#define GEN_MLME_EXT_HANDLER(size, cmd) {size, cmd},
+
+#ifdef _RTW_CMD_C_
+
+static struct cmd_hdl wlancmds[] = {
+ GEN_MLME_EXT_HANDLER(sizeof(struct wlan_bssid_ex), join_cmd_hdl)
+ GEN_MLME_EXT_HANDLER(sizeof(struct disconnect_parm), disconnect_hdl)
+ GEN_MLME_EXT_HANDLER(sizeof(struct wlan_bssid_ex), createbss_hdl)
+ GEN_MLME_EXT_HANDLER(sizeof(struct setopmode_parm), setopmode_hdl)
+ GEN_MLME_EXT_HANDLER(sizeof(struct sitesurvey_parm), sitesurvey_cmd_hdl)
+ GEN_MLME_EXT_HANDLER(sizeof(struct setauth_parm), setauth_hdl)
+ GEN_MLME_EXT_HANDLER(sizeof(struct setkey_parm), setkey_hdl)
+ GEN_MLME_EXT_HANDLER(sizeof(struct set_stakey_parm), set_stakey_hdl)
+ GEN_MLME_EXT_HANDLER(sizeof(struct set_assocsta_parm), NULL)
+ GEN_MLME_EXT_HANDLER(sizeof(struct addBaReq_parm), add_ba_hdl)
+ GEN_MLME_EXT_HANDLER(sizeof(struct set_ch_parm), set_ch_hdl)
+ GEN_MLME_EXT_HANDLER(sizeof(struct wlan_bssid_ex), tx_beacon_hdl)
+ GEN_MLME_EXT_HANDLER(0, mlme_evt_hdl)
+ GEN_MLME_EXT_HANDLER(0, rtw_drvextra_cmd_hdl)
+ GEN_MLME_EXT_HANDLER(sizeof(struct SetChannelPlan_param), set_chplan_hdl)
+};
+
+#endif
+
+struct C2HEvent_Header {
+#ifdef __LITTLE_ENDIAN
+ unsigned int len:16;
+ unsigned int ID:8;
+ unsigned int seq:8;
+#elif defined(__BIG_ENDIAN)
+ unsigned int seq:8;
+ unsigned int ID:8;
+ unsigned int len:16;
+#endif
+ unsigned int rsvd;
+};
+
+void rtw_dummy_event_callback(struct adapter *adapter, u8 *pbuf);
+void rtw_fwdbg_event_callback(struct adapter *adapter, u8 *pbuf);
+
+enum rtw_c2h_event {
+ GEN_EVT_CODE(_Read_MACREG) = 0, /*0*/
+ GEN_EVT_CODE(_Read_BBREG),
+ GEN_EVT_CODE(_Read_RFREG),
+ GEN_EVT_CODE(_Read_EEPROM),
+ GEN_EVT_CODE(_Read_EFUSE),
+ GEN_EVT_CODE(_Read_CAM), /*5*/
+ GEN_EVT_CODE(_Get_BasicRate),
+ GEN_EVT_CODE(_Get_DataRate),
+ GEN_EVT_CODE(_Survey), /*8*/
+ GEN_EVT_CODE(_SurveyDone), /*9*/
+
+ GEN_EVT_CODE(_JoinBss) , /*10*/
+ GEN_EVT_CODE(_AddSTA),
+ GEN_EVT_CODE(_DelSTA),
+ GEN_EVT_CODE(_AtimDone),
+ GEN_EVT_CODE(_TX_Report),
+ GEN_EVT_CODE(_CCX_Report), /*15*/
+ GEN_EVT_CODE(_DTM_Report),
+ GEN_EVT_CODE(_TX_Rate_Statistics),
+ GEN_EVT_CODE(_C2HLBK),
+ GEN_EVT_CODE(_FWDBG),
+ GEN_EVT_CODE(_C2HFEEDBACK), /*20*/
+ GEN_EVT_CODE(_ADDBA),
+ GEN_EVT_CODE(_C2HBCN),
+ GEN_EVT_CODE(_ReportPwrState), /* filen: only for PCIE, USB */
+ GEN_EVT_CODE(_CloseRF), /* filen: only for PCIE,
+ * work around ASPM */
+ MAX_C2HEVT
+};
+
+
+#ifdef _RTW_MLME_EXT_C_
+
+static struct fwevent wlanevents[] = {
+ {0, rtw_dummy_event_callback}, /*0*/
+ {0, NULL},
+ {0, NULL},
+ {0, NULL},
+ {0, NULL},
+ {0, NULL},
+ {0, NULL},
+ {0, NULL},
+ {0, &rtw_survey_event_callback}, /*8*/
+ {sizeof(struct surveydone_event), &rtw_surveydone_event_callback},/*9*/
+ {0, &rtw_joinbss_event_callback}, /*10*/
+ {sizeof(struct stassoc_event), &rtw_stassoc_event_callback},
+ {sizeof(struct stadel_event), &rtw_stadel_event_callback},
+ {0, &rtw_atimdone_event_callback},
+ {0, rtw_dummy_event_callback},
+ {0, NULL}, /*15*/
+ {0, NULL},
+ {0, NULL},
+ {0, NULL},
+ {0, rtw_fwdbg_event_callback},
+ {0, NULL}, /*20*/
+ {0, NULL},
+ {0, NULL},
+ {0, &rtw_cpwm_event_callback},
+ {0, NULL},
+};
+
+#endif/* _RTL_MLME_EXT_C_ */
+
+#endif /* __RTW_MLME_EXT_H_ */
diff --git a/drivers/staging/rtl8188eu/include/rtw_mp_phy_regdef.h b/drivers/staging/rtl8188eu/include/rtw_mp_phy_regdef.h
new file mode 100644
index 000000000..30fd17f23
--- /dev/null
+++ b/drivers/staging/rtl8188eu/include/rtw_mp_phy_regdef.h
@@ -0,0 +1,1084 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2011 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ *
+ ******************************************************************************/
+/*****************************************************************************
+ *
+ * Module: __RTW_MP_PHY_REGDEF_H_
+ *
+ *
+ * Note: 1. Define PMAC/BB register map
+ * 2. Define RF register map
+ * 3. PMAC/BB register bit mask.
+ * 4. RF reg bit mask.
+ * 5. Other BB/RF relative definition.
+ *
+ *
+ * Export: Constants, macro, functions(API), global variables(None).
+ *
+ * Abbrev:
+ *
+ * History:
+ * Data Who Remark
+ * 08/07/2007 MHC 1. Porting from 9x series PHYCFG.h.
+ * 2. Reorganize code architecture.
+ * 09/25/2008 MH 1. Add RL6052 register definition
+ *
+ *****************************************************************************/
+#ifndef __RTW_MP_PHY_REGDEF_H_
+#define __RTW_MP_PHY_REGDEF_H_
+
+
+/*--------------------------Define Parameters-------------------------------*/
+
+/* */
+/* 8192S Regsiter offset definition */
+/* */
+
+/* */
+/* BB-PHY register PMAC 0x100 PHY 0x800 - 0xEFF */
+/* 1. PMAC duplicate register due to connection: RF_Mode, TRxRN, NumOf L-STF */
+/* 2. 0x800/0x900/0xA00/0xC00/0xD00/0xE00 */
+/* 3. RF register 0x00-2E */
+/* 4. Bit Mask for BB/RF register */
+/* 5. Other definition for BB/RF R/W */
+/* */
+
+
+/* */
+/* 1. PMAC duplicate register due to connection: RF_Mode, TRxRN, NumOf L-STF */
+/* 1. Page1(0x100) */
+/* */
+#define rPMAC_Reset 0x100
+#define rPMAC_TxStart 0x104
+#define rPMAC_TxLegacySIG 0x108
+#define rPMAC_TxHTSIG1 0x10c
+#define rPMAC_TxHTSIG2 0x110
+#define rPMAC_PHYDebug 0x114
+#define rPMAC_TxPacketNum 0x118
+#define rPMAC_TxIdle 0x11c
+#define rPMAC_TxMACHeader0 0x120
+#define rPMAC_TxMACHeader1 0x124
+#define rPMAC_TxMACHeader2 0x128
+#define rPMAC_TxMACHeader3 0x12c
+#define rPMAC_TxMACHeader4 0x130
+#define rPMAC_TxMACHeader5 0x134
+#define rPMAC_TxDataType 0x138
+#define rPMAC_TxRandomSeed 0x13c
+#define rPMAC_CCKPLCPPreamble 0x140
+#define rPMAC_CCKPLCPHeader 0x144
+#define rPMAC_CCKCRC16 0x148
+#define rPMAC_OFDMRxCRC32OK 0x170
+#define rPMAC_OFDMRxCRC32Er 0x174
+#define rPMAC_OFDMRxParityEr 0x178
+#define rPMAC_OFDMRxCRC8Er 0x17c
+#define rPMAC_CCKCRxRC16Er 0x180
+#define rPMAC_CCKCRxRC32Er 0x184
+#define rPMAC_CCKCRxRC32OK 0x188
+#define rPMAC_TxStatus 0x18c
+
+/* */
+/* 2. Page2(0x200) */
+/* */
+/* The following two definition are only used for USB interface. */
+/* define RF_BB_CMD_ADDR 0x02c0 RF/BB read/write command address. */
+/* define RF_BB_CMD_DATA 0x02c4 RF/BB read/write command data. */
+
+/* */
+/* 3. Page8(0x800) */
+/* */
+#define rFPGA0_RFMOD 0x800 /* RF mode & CCK TxSC RF BW Setting?? */
+
+#define rFPGA0_TxInfo 0x804 /* Status report?? */
+#define rFPGA0_PSDFunction 0x808
+
+#define rFPGA0_TxGainStage 0x80c /* Set TX PWR init gain? */
+
+#define rFPGA0_RFTiming1 0x810 /* Useless now */
+#define rFPGA0_RFTiming2 0x814
+/* define rFPGA0_XC_RFTiming 0x818 */
+/* define rFPGA0_XD_RFTiming 0x81c */
+
+#define rFPGA0_XA_HSSIParameter1 0x820 /* RF 3 wire register */
+#define rFPGA0_XA_HSSIParameter2 0x824
+#define rFPGA0_XB_HSSIParameter1 0x828
+#define rFPGA0_XB_HSSIParameter2 0x82c
+#define rFPGA0_XC_HSSIParameter1 0x830
+#define rFPGA0_XC_HSSIParameter2 0x834
+#define rFPGA0_XD_HSSIParameter1 0x838
+#define rFPGA0_XD_HSSIParameter2 0x83c
+#define rFPGA0_XA_LSSIParameter 0x840
+#define rFPGA0_XB_LSSIParameter 0x844
+#define rFPGA0_XC_LSSIParameter 0x848
+#define rFPGA0_XD_LSSIParameter 0x84c
+
+#define rFPGA0_RFWakeUpParameter 0x850 /* Useless now */
+#define rFPGA0_RFSleepUpParameter 0x854
+
+#define rFPGA0_XAB_SwitchControl 0x858 /* RF Channel switch */
+#define rFPGA0_XCD_SwitchControl 0x85c
+
+#define rFPGA0_XA_RFInterfaceOE 0x860 /* RF Channel switch */
+#define rFPGA0_XB_RFInterfaceOE 0x864
+#define rFPGA0_XC_RFInterfaceOE 0x868
+#define rFPGA0_XD_RFInterfaceOE 0x86c
+
+#define rFPGA0_XAB_RFInterfaceSW 0x870 /* RF Interface Software Control */
+#define rFPGA0_XCD_RFInterfaceSW 0x874
+
+#define rFPGA0_XAB_RFParameter 0x878 /* RF Parameter */
+#define rFPGA0_XCD_RFParameter 0x87c
+
+#define rFPGA0_AnalogParameter1 0x880 /* Crystal cap setting RF-R/W protection for parameter4?? */
+#define rFPGA0_AnalogParameter2 0x884
+#define rFPGA0_AnalogParameter3 0x888 /* Useless now */
+#define rFPGA0_AnalogParameter4 0x88c
+
+#define rFPGA0_XA_LSSIReadBack 0x8a0 /* Tranceiver LSSI Readback */
+#define rFPGA0_XB_LSSIReadBack 0x8a4
+#define rFPGA0_XC_LSSIReadBack 0x8a8
+#define rFPGA0_XD_LSSIReadBack 0x8ac
+
+#define rFPGA0_PSDReport 0x8b4 /* Useless now */
+#define rFPGA0_XAB_RFInterfaceRB 0x8e0 /* Useless now RF Interface Readback Value */
+#define rFPGA0_XCD_RFInterfaceRB 0x8e4 /* Useless now */
+
+/* */
+/* 4. Page9(0x900) */
+/* */
+#define rFPGA1_RFMOD 0x900 /* RF mode & OFDM TxSC RF BW Setting?? */
+
+#define rFPGA1_TxBlock 0x904 /* Useless now */
+#define rFPGA1_DebugSelect 0x908 /* Useless now */
+#define rFPGA1_TxInfo 0x90c /* Useless now Status report?? */
+
+/* */
+/* 5. PageA(0xA00) */
+/* */
+/* Set Control channel to upper or lower. These settings are required only for 40MHz */
+#define rCCK0_System 0xa00
+
+#define rCCK0_AFESetting 0xa04 /* Disable init gain now Select RX path by RSSI */
+#define rCCK0_CCA 0xa08 /* Disable init gain now Init gain */
+
+#define rCCK0_RxAGC1 0xa0c /* AGC default value, saturation level Antenna Diversity, RX AGC, LNA Threshold, RX LNA Threshold useless now. Not the same as 90 series */
+#define rCCK0_RxAGC2 0xa10 /* AGC & DAGC */
+
+#define rCCK0_RxHP 0xa14
+
+#define rCCK0_DSPParameter1 0xa18 /* Timing recovery & Channel estimation threshold */
+#define rCCK0_DSPParameter2 0xa1c /* SQ threshold */
+
+#define rCCK0_TxFilter1 0xa20
+#define rCCK0_TxFilter2 0xa24
+#define rCCK0_DebugPort 0xa28 /* debug port and Tx filter3 */
+#define rCCK0_FalseAlarmReport 0xa2c /* 0xa2d useless now 0xa30-a4f channel report */
+#define rCCK0_TRSSIReport 0xa50
+#define rCCK0_RxReport 0xa54 /* 0xa57 */
+#define rCCK0_FACounterLower 0xa5c /* 0xa5b */
+#define rCCK0_FACounterUpper 0xa58 /* 0xa5c */
+
+/* */
+/* 6. PageC(0xC00) */
+/* */
+#define rOFDM0_LSTF 0xc00
+
+#define rOFDM0_TRxPathEnable 0xc04
+#define rOFDM0_TRMuxPar 0xc08
+#define rOFDM0_TRSWIsolation 0xc0c
+
+#define rOFDM0_XARxAFE 0xc10 /* RxIQ DC offset, Rx digital filter, DC notch filter */
+#define rOFDM0_XARxIQImbalance 0xc14 /* RxIQ imblance matrix */
+#define rOFDM0_XBRxAFE 0xc18
+#define rOFDM0_XBRxIQImbalance 0xc1c
+#define rOFDM0_XCRxAFE 0xc20
+#define rOFDM0_XCRxIQImbalance 0xc24
+#define rOFDM0_XDRxAFE 0xc28
+#define rOFDM0_XDRxIQImbalance 0xc2c
+
+#define rOFDM0_RxDetector1 0xc30 /* PD,BW & SBD DM tune init gain */
+#define rOFDM0_RxDetector2 0xc34 /* SBD & Fame Sync. */
+#define rOFDM0_RxDetector3 0xc38 /* Frame Sync. */
+#define rOFDM0_RxDetector4 0xc3c /* PD, SBD, Frame Sync & Short-GI */
+
+#define rOFDM0_RxDSP 0xc40 /* Rx Sync Path */
+#define rOFDM0_CFOandDAGC 0xc44 /* CFO & DAGC */
+#define rOFDM0_CCADropThreshold 0xc48 /* CCA Drop threshold */
+#define rOFDM0_ECCAThreshold 0xc4c /* energy CCA */
+
+#define rOFDM0_XAAGCCore1 0xc50 /* DIG */
+#define rOFDM0_XAAGCCore2 0xc54
+#define rOFDM0_XBAGCCore1 0xc58
+#define rOFDM0_XBAGCCore2 0xc5c
+#define rOFDM0_XCAGCCore1 0xc60
+#define rOFDM0_XCAGCCore2 0xc64
+#define rOFDM0_XDAGCCore1 0xc68
+#define rOFDM0_XDAGCCore2 0xc6c
+
+#define rOFDM0_AGCParameter1 0xc70
+#define rOFDM0_AGCParameter2 0xc74
+#define rOFDM0_AGCRSSITable 0xc78
+#define rOFDM0_HTSTFAGC 0xc7c
+
+#define rOFDM0_XATxIQImbalance 0xc80 /* TX PWR TRACK and DIG */
+#define rOFDM0_XATxAFE 0xc84
+#define rOFDM0_XBTxIQImbalance 0xc88
+#define rOFDM0_XBTxAFE 0xc8c
+#define rOFDM0_XCTxIQImbalance 0xc90
+#define rOFDM0_XCTxAFE 0xc94
+#define rOFDM0_XDTxIQImbalance 0xc98
+#define rOFDM0_XDTxAFE 0xc9c
+#define rOFDM0_RxIQExtAnta 0xca0
+
+#define rOFDM0_RxHPParameter 0xce0
+#define rOFDM0_TxPseudoNoiseWgt 0xce4
+#define rOFDM0_FrameSync 0xcf0
+#define rOFDM0_DFSReport 0xcf4
+#define rOFDM0_TxCoeff1 0xca4
+#define rOFDM0_TxCoeff2 0xca8
+#define rOFDM0_TxCoeff3 0xcac
+#define rOFDM0_TxCoeff4 0xcb0
+#define rOFDM0_TxCoeff5 0xcb4
+#define rOFDM0_TxCoeff6 0xcb8
+
+/* 7. PageD(0xD00) */
+#define rOFDM1_LSTF 0xd00
+#define rOFDM1_TRxPathEnable 0xd04
+
+#define rOFDM1_CFO 0xd08 /* No setting now */
+#define rOFDM1_CSI1 0xd10
+#define rOFDM1_SBD 0xd14
+#define rOFDM1_CSI2 0xd18
+#define rOFDM1_CFOTracking 0xd2c
+#define rOFDM1_TRxMesaure1 0xd34
+#define rOFDM1_IntfDet 0xd3c
+#define rOFDM1_PseudoNoiseStateAB 0xd50
+#define rOFDM1_PseudoNoiseStateCD 0xd54
+#define rOFDM1_RxPseudoNoiseWgt 0xd58
+
+#define rOFDM_PHYCounter1 0xda0 /* cca, parity fail */
+#define rOFDM_PHYCounter2 0xda4 /* rate illegal, crc8 fail */
+#define rOFDM_PHYCounter3 0xda8 /* MCS not support */
+
+#define rOFDM_ShortCFOAB 0xdac /* No setting now */
+#define rOFDM_ShortCFOCD 0xdb0
+#define rOFDM_LongCFOAB 0xdb4
+#define rOFDM_LongCFOCD 0xdb8
+#define rOFDM_TailCFOAB 0xdbc
+#define rOFDM_TailCFOCD 0xdc0
+#define rOFDM_PWMeasure1 0xdc4
+#define rOFDM_PWMeasure2 0xdc8
+#define rOFDM_BWReport 0xdcc
+#define rOFDM_AGCReport 0xdd0
+#define rOFDM_RxSNR 0xdd4
+#define rOFDM_RxEVMCSI 0xdd8
+#define rOFDM_SIGReport 0xddc
+
+
+/* */
+/* 8. PageE(0xE00) */
+/* */
+#define rTxAGC_Rate18_06 0xe00
+#define rTxAGC_Rate54_24 0xe04
+#define rTxAGC_CCK_Mcs32 0xe08
+#define rTxAGC_Mcs03_Mcs00 0xe10
+#define rTxAGC_Mcs07_Mcs04 0xe14
+#define rTxAGC_Mcs11_Mcs08 0xe18
+#define rTxAGC_Mcs15_Mcs12 0xe1c
+
+/* Analog- control in RX_WAIT_CCA : REG: EE0 [Analog- Power & Control Register] */
+#define rRx_Wait_CCCA 0xe70
+#define rAnapar_Ctrl_BB 0xee0
+
+/* */
+/* 7. RF Register 0x00-0x2E (RF 8256) */
+/* RF-0222D 0x00-3F */
+/* */
+/* Zebra1 */
+#define RTL92SE_FPGA_VERIFY 0
+#define rZebra1_HSSIEnable 0x0 /* Useless now */
+#define rZebra1_TRxEnable1 0x1
+#define rZebra1_TRxEnable2 0x2
+#define rZebra1_AGC 0x4
+#define rZebra1_ChargePump 0x5
+/* if (RTL92SE_FPGA_VERIFY == 1) */
+#define rZebra1_Channel 0x7 /* RF channel switch */
+/* else */
+
+/* endif */
+#define rZebra1_TxGain 0x8 /* Useless now */
+#define rZebra1_TxLPF 0x9
+#define rZebra1_RxLPF 0xb
+#define rZebra1_RxHPFCorner 0xc
+
+/* Zebra4 */
+#define rGlobalCtrl 0 /* Useless now */
+#define rRTL8256_TxLPF 19
+#define rRTL8256_RxLPF 11
+
+/* RTL8258 */
+#define rRTL8258_TxLPF 0x11 /* Useless now */
+#define rRTL8258_RxLPF 0x13
+#define rRTL8258_RSSILPF 0xa
+
+/* */
+/* RL6052 Register definition */
+#define RF_AC 0x00 /* */
+
+#define RF_IQADJ_G1 0x01 /* */
+#define RF_IQADJ_G2 0x02 /* */
+#define RF_POW_TRSW 0x05 /* */
+
+#define RF_GAIN_RX 0x06 /* */
+#define RF_GAIN_TX 0x07 /* */
+
+#define RF_TXM_IDAC 0x08 /* */
+#define RF_BS_IQGEN 0x0F /* */
+
+#define RF_MODE1 0x10 /* */
+#define RF_MODE2 0x11 /* */
+
+#define RF_RX_AGC_HP 0x12 /* */
+#define RF_TX_AGC 0x13 /* */
+#define RF_BIAS 0x14 /* */
+#define RF_IPA 0x15 /* */
+#define RF_TXBIAS 0x16 /* */
+#define RF_POW_ABILITY 0x17 /* */
+#define RF_MODE_AG 0x18 /* */
+#define rRfChannel 0x18 /* RF channel and BW switch */
+#define RF_CHNLBW 0x18 /* RF channel and BW switch */
+#define RF_TOP 0x19 /* */
+
+#define RF_RX_G1 0x1A /* */
+#define RF_RX_G2 0x1B /* */
+
+#define RF_RX_BB2 0x1C /* */
+#define RF_RX_BB1 0x1D /* */
+
+#define RF_RCK1 0x1E /* */
+#define RF_RCK2 0x1F /* */
+
+#define RF_TX_G1 0x20 /* */
+#define RF_TX_G2 0x21 /* */
+#define RF_TX_G3 0x22 /* */
+
+#define RF_TX_BB1 0x23 /* */
+
+#define RF_T_METER 0x24 /* */
+
+#define RF_SYN_G1 0x25 /* RF TX Power control */
+#define RF_SYN_G2 0x26 /* RF TX Power control */
+#define RF_SYN_G3 0x27 /* RF TX Power control */
+#define RF_SYN_G4 0x28 /* RF TX Power control */
+#define RF_SYN_G5 0x29 /* RF TX Power control */
+#define RF_SYN_G6 0x2A /* RF TX Power control */
+#define RF_SYN_G7 0x2B /* RF TX Power control */
+#define RF_SYN_G8 0x2C /* RF TX Power control */
+
+#define RF_RCK_OS 0x30 /* RF TX PA control */
+#define RF_TXPA_G1 0x31 /* RF TX PA control */
+#define RF_TXPA_G2 0x32 /* RF TX PA control */
+#define RF_TXPA_G3 0x33 /* RF TX PA control */
+
+/* */
+/* Bit Mask */
+/* */
+/* 1. Page1(0x100) */
+#define bBBResetB 0x100 /* Useless now? */
+#define bGlobalResetB 0x200
+#define bOFDMTxStart 0x4
+#define bCCKTxStart 0x8
+#define bCRC32Debug 0x100
+#define bPMACLoopback 0x10
+#define bTxLSIG 0xffffff
+#define bOFDMTxRate 0xf
+#define bOFDMTxReserved 0x10
+#define bOFDMTxLength 0x1ffe0
+#define bOFDMTxParity 0x20000
+#define bTxHTSIG1 0xffffff
+#define bTxHTMCSRate 0x7f
+#define bTxHTBW 0x80
+#define bTxHTLength 0xffff00
+#define bTxHTSIG2 0xffffff
+#define bTxHTSmoothing 0x1
+#define bTxHTSounding 0x2
+#define bTxHTReserved 0x4
+#define bTxHTAggreation 0x8
+#define bTxHTSTBC 0x30
+#define bTxHTAdvanceCoding 0x40
+#define bTxHTShortGI 0x80
+#define bTxHTNumberHT_LTF 0x300
+#define bTxHTCRC8 0x3fc00
+#define bCounterReset 0x10000
+#define bNumOfOFDMTx 0xffff
+#define bNumOfCCKTx 0xffff0000
+#define bTxIdleInterval 0xffff
+#define bOFDMService 0xffff0000
+#define bTxMACHeader 0xffffffff
+#define bTxDataInit 0xff
+#define bTxHTMode 0x100
+#define bTxDataType 0x30000
+#define bTxRandomSeed 0xffffffff
+#define bCCKTxPreamble 0x1
+#define bCCKTxSFD 0xffff0000
+#define bCCKTxSIG 0xff
+#define bCCKTxService 0xff00
+#define bCCKLengthExt 0x8000
+#define bCCKTxLength 0xffff0000
+#define bCCKTxCRC16 0xffff
+#define bCCKTxStatus 0x1
+#define bOFDMTxStatus 0x2
+
+#define IS_BB_REG_OFFSET_92S(_Offset) ((_Offset >= 0x800) && (_Offset <= 0xfff))
+
+/* 2. Page8(0x800) */
+#define bRFMOD 0x1 /* Reg 0x800 rFPGA0_RFMOD */
+#define bJapanMode 0x2
+#define bCCKTxSC 0x30
+#define bCCKEn 0x1000000
+#define bOFDMEn 0x2000000
+
+#define bOFDMRxADCPhase 0x10000 /* Useless now */
+#define bOFDMTxDACPhase 0x40000
+#define bXATxAGC 0x3f
+
+#define bXBTxAGC 0xf00 /* Reg 80c rFPGA0_TxGainStage */
+#define bXCTxAGC 0xf000
+#define bXDTxAGC 0xf0000
+
+#define bPAStart 0xf0000000 /* Useless now */
+#define bTRStart 0x00f00000
+#define bRFStart 0x0000f000
+#define bBBStart 0x000000f0
+#define bBBCCKStart 0x0000000f
+#define bPAEnd 0xf /* Reg0x814 */
+#define bTREnd 0x0f000000
+#define bRFEnd 0x000f0000
+#define bCCAMask 0x000000f0 /* T2R */
+#define bR2RCCAMask 0x00000f00
+#define bHSSI_R2TDelay 0xf8000000
+#define bHSSI_T2RDelay 0xf80000
+#define bContTxHSSI 0x400 /* chane gain at continue Tx */
+#define bIGFromCCK 0x200
+#define bAGCAddress 0x3f
+#define bRxHPTx 0x7000
+#define bRxHPT2R 0x38000
+#define bRxHPCCKIni 0xc0000
+#define bAGCTxCode 0xc00000
+#define bAGCRxCode 0x300000
+
+#define b3WireDataLength 0x800 /* Reg 0x820~84f rFPGA0_XA_HSSIParameter1 */
+#define b3WireAddressLength 0x400
+
+#define b3WireRFPowerDown 0x1 /* Useless now */
+/* define bHWSISelect 0x8 */
+#define b5GPAPEPolarity 0x40000000
+#define b2GPAPEPolarity 0x80000000
+#define bRFSW_TxDefaultAnt 0x3
+#define bRFSW_TxOptionAnt 0x30
+#define bRFSW_RxDefaultAnt 0x300
+#define bRFSW_RxOptionAnt 0x3000
+#define bRFSI_3WireData 0x1
+#define bRFSI_3WireClock 0x2
+#define bRFSI_3WireLoad 0x4
+#define bRFSI_3WireRW 0x8
+#define bRFSI_3Wire 0xf
+
+#define bRFSI_RFENV 0x10 /* Reg 0x870 rFPGA0_XAB_RFInterfaceSW */
+
+#define bRFSI_TRSW 0x20 /* Useless now */
+#define bRFSI_TRSWB 0x40
+#define bRFSI_ANTSW 0x100
+#define bRFSI_ANTSWB 0x200
+#define bRFSI_PAPE 0x400
+#define bRFSI_PAPE5G 0x800
+#define bBandSelect 0x1
+#define bHTSIG2_GI 0x80
+#define bHTSIG2_Smoothing 0x01
+#define bHTSIG2_Sounding 0x02
+#define bHTSIG2_Aggreaton 0x08
+#define bHTSIG2_STBC 0x30
+#define bHTSIG2_AdvCoding 0x40
+#define bHTSIG2_NumOfHTLTF 0x300
+#define bHTSIG2_CRC8 0x3fc
+#define bHTSIG1_MCS 0x7f
+#define bHTSIG1_BandWidth 0x80
+#define bHTSIG1_HTLength 0xffff
+#define bLSIG_Rate 0xf
+#define bLSIG_Reserved 0x10
+#define bLSIG_Length 0x1fffe
+#define bLSIG_Parity 0x20
+#define bCCKRxPhase 0x4
+#if (RTL92SE_FPGA_VERIFY == 1)
+#define bLSSIReadAddress 0x3f000000 /* LSSI "Read" Address
+ Reg 0x824 rFPGA0_XA_HSSIParameter2 */
+#else
+#define bLSSIReadAddress 0x7f800000 /* T65 RF */
+#endif
+#define bLSSIReadEdge 0x80000000 /* LSSI "Read" edge signal */
+#if (RTL92SE_FPGA_VERIFY == 1)
+#define bLSSIReadBackData 0xfff /* Reg 0x8a0
+ rFPGA0_XA_LSSIReadBack */
+#else
+#define bLSSIReadBackData 0xfffff /* T65 RF */
+#endif
+#define bLSSIReadOKFlag 0x1000 /* Useless now */
+#define bCCKSampleRate 0x8 /* 0: 44MHz, 1:88MHz */
+#define bRegulator0Standby 0x1
+#define bRegulatorPLLStandby 0x2
+#define bRegulator1Standby 0x4
+#define bPLLPowerUp 0x8
+#define bDPLLPowerUp 0x10
+#define bDA10PowerUp 0x20
+#define bAD7PowerUp 0x200
+#define bDA6PowerUp 0x2000
+#define bXtalPowerUp 0x4000
+#define b40MDClkPowerUP 0x8000
+#define bDA6DebugMode 0x20000
+#define bDA6Swing 0x380000
+
+#define bADClkPhase 0x4000000 /* Reg 0x880
+ rFPGA0_AnalogParameter1 20/40 CCK support switch 40/80 BB MHZ */
+
+#define b80MClkDelay 0x18000000 /* Useless */
+#define bAFEWatchDogEnable 0x20000000
+
+#define bXtalCap01 0xc0000000 /* Reg 0x884
+ rFPGA0_AnalogParameter2 Crystal cap */
+#define bXtalCap23 0x3
+#define bXtalCap92x 0x0f000000
+#define bXtalCap 0x0f000000
+
+#define bIntDifClkEnable 0x400 /* Useless */
+#define bExtSigClkEnable 0x800
+#define bBandgapMbiasPowerUp 0x10000
+#define bAD11SHGain 0xc0000
+#define bAD11InputRange 0x700000
+#define bAD11OPCurrent 0x3800000
+#define bIPathLoopback 0x4000000
+#define bQPathLoopback 0x8000000
+#define bAFELoopback 0x10000000
+#define bDA10Swing 0x7e0
+#define bDA10Reverse 0x800
+#define bDAClkSource 0x1000
+#define bAD7InputRange 0x6000
+#define bAD7Gain 0x38000
+#define bAD7OutputCMMode 0x40000
+#define bAD7InputCMMode 0x380000
+#define bAD7Current 0xc00000
+#define bRegulatorAdjust 0x7000000
+#define bAD11PowerUpAtTx 0x1
+#define bDA10PSAtTx 0x10
+#define bAD11PowerUpAtRx 0x100
+#define bDA10PSAtRx 0x1000
+#define bCCKRxAGCFormat 0x200
+#define bPSDFFTSamplepPoint 0xc000
+#define bPSDAverageNum 0x3000
+#define bIQPathControl 0xc00
+#define bPSDFreq 0x3ff
+#define bPSDAntennaPath 0x30
+#define bPSDIQSwitch 0x40
+#define bPSDRxTrigger 0x400000
+#define bPSDTxTrigger 0x80000000
+#define bPSDSineToneScale 0x7f000000
+#define bPSDReport 0xffff
+
+/* 3. Page9(0x900) */
+#define bOFDMTxSC 0x30000000 /* Useless */
+#define bCCKTxOn 0x1
+#define bOFDMTxOn 0x2
+#define bDebugPage 0xfff /* reset debug page and HWord,
+ * LWord */
+#define bDebugItem 0xff /* reset debug page and LWord */
+#define bAntL 0x10
+#define bAntNonHT 0x100
+#define bAntHT1 0x1000
+#define bAntHT2 0x10000
+#define bAntHT1S1 0x100000
+#define bAntNonHTS1 0x1000000
+
+/* 4. PageA(0xA00) */
+#define bCCKBBMode 0x3 /* Useless */
+#define bCCKTxPowerSaving 0x80
+#define bCCKRxPowerSaving 0x40
+
+#define bCCKSideBand 0x10 /* Reg 0xa00 rCCK0 20/40 sw */
+
+#define bCCKScramble 0x8 /* Useless */
+#define bCCKAntDiversity 0x8000
+#define bCCKCarrierRecovery 0x4000
+#define bCCKTxRate 0x3000
+#define bCCKDCCancel 0x0800
+#define bCCKISICancel 0x0400
+#define bCCKMatchFilter 0x0200
+#define bCCKEqualizer 0x0100
+#define bCCKPreambleDetect 0x800000
+#define bCCKFastFalseCCA 0x400000
+#define bCCKChEstStart 0x300000
+#define bCCKCCACount 0x080000
+#define bCCKcs_lim 0x070000
+#define bCCKBistMode 0x80000000
+#define bCCKCCAMask 0x40000000
+#define bCCKTxDACPhase 0x4
+#define bCCKRxADCPhase 0x20000000 /* r_rx_clk */
+#define bCCKr_cp_mode0 0x0100
+#define bCCKTxDCOffset 0xf0
+#define bCCKRxDCOffset 0xf
+#define bCCKCCAMode 0xc000
+#define bCCKFalseCS_lim 0x3f00
+#define bCCKCS_ratio 0xc00000
+#define bCCKCorgBit_sel 0x300000
+#define bCCKPD_lim 0x0f0000
+#define bCCKNewCCA 0x80000000
+#define bCCKRxHPofIG 0x8000
+#define bCCKRxIG 0x7f00
+#define bCCKLNAPolarity 0x800000
+#define bCCKRx1stGain 0x7f0000
+#define bCCKRFExtend 0x20000000 /* CCK Rx init gain polar */
+#define bCCKRxAGCSatLevel 0x1f000000
+#define bCCKRxAGCSatCount 0xe0
+#define bCCKRxRFSettle 0x1f /* AGCsamp_dly */
+#define bCCKFixedRxAGC 0x8000
+#define bCCKAntennaPolarity 0x2000
+#define bCCKTxFilterType 0x0c00
+#define bCCKRxAGCReportType 0x0300
+#define bCCKRxDAGCEn 0x80000000
+#define bCCKRxDAGCPeriod 0x20000000
+#define bCCKRxDAGCSatLevel 0x1f000000
+#define bCCKTimingRecovery 0x800000
+#define bCCKTxC0 0x3f0000
+#define bCCKTxC1 0x3f000000
+#define bCCKTxC2 0x3f
+#define bCCKTxC3 0x3f00
+#define bCCKTxC4 0x3f0000
+#define bCCKTxC5 0x3f000000
+#define bCCKTxC6 0x3f
+#define bCCKTxC7 0x3f00
+#define bCCKDebugPort 0xff0000
+#define bCCKDACDebug 0x0f000000
+#define bCCKFalseAlarmEnable 0x8000
+#define bCCKFalseAlarmRead 0x4000
+#define bCCKTRSSI 0x7f
+#define bCCKRxAGCReport 0xfe
+#define bCCKRxReport_AntSel 0x80000000
+#define bCCKRxReport_MFOff 0x40000000
+#define bCCKRxRxReport_SQLoss 0x20000000
+#define bCCKRxReport_Pktloss 0x10000000
+#define bCCKRxReport_Lockedbit 0x08000000
+#define bCCKRxReport_RateError 0x04000000
+#define bCCKRxReport_RxRate 0x03000000
+#define bCCKRxFACounterLower 0xff
+#define bCCKRxFACounterUpper 0xff000000
+#define bCCKRxHPAGCStart 0xe000
+#define bCCKRxHPAGCFinal 0x1c00
+#define bCCKRxFalseAlarmEnable 0x8000
+#define bCCKFACounterFreeze 0x4000
+#define bCCKTxPathSel 0x10000000
+#define bCCKDefaultRxPath 0xc000000
+#define bCCKOptionRxPath 0x3000000
+
+/* 5. PageC(0xC00) */
+#define bNumOfSTF 0x3 /* Useless */
+#define bShift_L 0xc0
+#define bGI_TH 0xc
+#define bRxPathA 0x1
+#define bRxPathB 0x2
+#define bRxPathC 0x4
+#define bRxPathD 0x8
+#define bTxPathA 0x1
+#define bTxPathB 0x2
+#define bTxPathC 0x4
+#define bTxPathD 0x8
+#define bTRSSIFreq 0x200
+#define bADCBackoff 0x3000
+#define bDFIRBackoff 0xc000
+#define bTRSSILatchPhase 0x10000
+#define bRxIDCOffset 0xff
+#define bRxQDCOffset 0xff00
+#define bRxDFIRMode 0x1800000
+#define bRxDCNFType 0xe000000
+#define bRXIQImb_A 0x3ff
+#define bRXIQImb_B 0xfc00
+#define bRXIQImb_C 0x3f0000
+#define bRXIQImb_D 0xffc00000
+#define bDC_dc_Notch 0x60000
+#define bRxNBINotch 0x1f000000
+#define bPD_TH 0xf
+#define bPD_TH_Opt2 0xc000
+#define bPWED_TH 0x700
+#define bIfMF_Win_L 0x800
+#define bPD_Option 0x1000
+#define bMF_Win_L 0xe000
+#define bBW_Search_L 0x30000
+#define bwin_enh_L 0xc0000
+#define bBW_TH 0x700000
+#define bED_TH2 0x3800000
+#define bBW_option 0x4000000
+#define bRatio_TH 0x18000000
+#define bWindow_L 0xe0000000
+#define bSBD_Option 0x1
+#define bFrame_TH 0x1c
+#define bFS_Option 0x60
+#define bDC_Slope_check 0x80
+#define bFGuard_Counter_DC_L 0xe00
+#define bFrame_Weight_Short 0x7000
+#define bSub_Tune 0xe00000
+#define bFrame_DC_Length 0xe000000
+#define bSBD_start_offset 0x30000000
+#define bFrame_TH_2 0x7
+#define bFrame_GI2_TH 0x38
+#define bGI2_Sync_en 0x40
+#define bSarch_Short_Early 0x300
+#define bSarch_Short_Late 0xc00
+#define bSarch_GI2_Late 0x70000
+#define bCFOAntSum 0x1
+#define bCFOAcc 0x2
+#define bCFOStartOffset 0xc
+#define bCFOLookBack 0x70
+#define bCFOSumWeight 0x80
+#define bDAGCEnable 0x10000
+#define bTXIQImb_A 0x3ff
+#define bTXIQImb_B 0xfc00
+#define bTXIQImb_C 0x3f0000
+#define bTXIQImb_D 0xffc00000
+#define bTxIDCOffset 0xff
+#define bTxQDCOffset 0xff00
+#define bTxDFIRMode 0x10000
+#define bTxPesudoNoiseOn 0x4000000
+#define bTxPesudoNoise_A 0xff
+#define bTxPesudoNoise_B 0xff00
+#define bTxPesudoNoise_C 0xff0000
+#define bTxPesudoNoise_D 0xff000000
+#define bCCADropOption 0x20000
+#define bCCADropThres 0xfff00000
+#define bEDCCA_H 0xf
+#define bEDCCA_L 0xf0
+#define bLambda_ED 0x300
+#define bRxInitialGain 0x7f
+#define bRxAntDivEn 0x80
+#define bRxAGCAddressForLNA 0x7f00
+#define bRxHighPowerFlow 0x8000
+#define bRxAGCFreezeThres 0xc0000
+#define bRxFreezeStep_AGC1 0x300000
+#define bRxFreezeStep_AGC2 0xc00000
+#define bRxFreezeStep_AGC3 0x3000000
+#define bRxFreezeStep_AGC0 0xc000000
+#define bRxRssi_Cmp_En 0x10000000
+#define bRxQuickAGCEn 0x20000000
+#define bRxAGCFreezeThresMode 0x40000000
+#define bRxOverFlowCheckType 0x80000000
+#define bRxAGCShift 0x7f
+#define bTRSW_Tri_Only 0x80
+#define bPowerThres 0x300
+#define bRxAGCEn 0x1
+#define bRxAGCTogetherEn 0x2
+#define bRxAGCMin 0x4
+#define bRxHP_Ini 0x7
+#define bRxHP_TRLNA 0x70
+#define bRxHP_RSSI 0x700
+#define bRxHP_BBP1 0x7000
+#define bRxHP_BBP2 0x70000
+#define bRxHP_BBP3 0x700000
+#define bRSSI_H 0x7f0000 /* thresh for hi power */
+#define bRSSI_Gen 0x7f000000 /* thresh for ant div */
+#define bRxSettle_TRSW 0x7
+#define bRxSettle_LNA 0x38
+#define bRxSettle_RSSI 0x1c0
+#define bRxSettle_BBP 0xe00
+#define bRxSettle_RxHP 0x7000
+#define bRxSettle_AntSW_RSSI 0x38000
+#define bRxSettle_AntSW 0xc0000
+#define bRxProcessTime_DAGC 0x300000
+#define bRxSettle_HSSI 0x400000
+#define bRxProcessTime_BBPPW 0x800000
+#define bRxAntennaPowerShift 0x3000000
+#define bRSSITableSelect 0xc000000
+#define bRxHP_Final 0x7000000
+#define bRxHTSettle_BBP 0x7
+#define bRxHTSettle_HSSI 0x8
+#define bRxHTSettle_RxHP 0x70
+#define bRxHTSettle_BBPPW 0x80
+#define bRxHTSettle_Idle 0x300
+#define bRxHTSettle_Reserved 0x1c00
+#define bRxHTRxHPEn 0x8000
+#define bRxHTAGCFreezeThres 0x30000
+#define bRxHTAGCTogetherEn 0x40000
+#define bRxHTAGCMin 0x80000
+#define bRxHTAGCEn 0x100000
+#define bRxHTDAGCEn 0x200000
+#define bRxHTRxHP_BBP 0x1c00000
+#define bRxHTRxHP_Final 0xe0000000
+#define bRxPWRatioTH 0x3
+#define bRxPWRatioEn 0x4
+#define bRxMFHold 0x3800
+#define bRxPD_Delay_TH1 0x38
+#define bRxPD_Delay_TH2 0x1c0
+#define bRxPD_DC_COUNT_MAX 0x600
+/* define bRxMF_Hold 0x3800 */
+#define bRxPD_Delay_TH 0x8000
+#define bRxProcess_Delay 0xf0000
+#define bRxSearchrange_GI2_Early 0x700000
+#define bRxFrame_Guard_Counter_L 0x3800000
+#define bRxSGI_Guard_L 0xc000000
+#define bRxSGI_Search_L 0x30000000
+#define bRxSGI_TH 0xc0000000
+#define bDFSCnt0 0xff
+#define bDFSCnt1 0xff00
+#define bDFSFlag 0xf0000
+#define bMFWeightSum 0x300000
+#define bMinIdxTH 0x7f000000
+#define bDAFormat 0x40000
+#define bTxChEmuEnable 0x01000000
+#define bTRSWIsolation_A 0x7f
+#define bTRSWIsolation_B 0x7f00
+#define bTRSWIsolation_C 0x7f0000
+#define bTRSWIsolation_D 0x7f000000
+#define bExtLNAGain 0x7c00
+
+/* 6. PageE(0xE00) */
+#define bSTBCEn 0x4 /* Useless */
+#define bAntennaMapping 0x10
+#define bNss 0x20
+#define bCFOAntSumD 0x200
+#define bPHYCounterReset 0x8000000
+#define bCFOReportGet 0x4000000
+#define bOFDMContinueTx 0x10000000
+#define bOFDMSingleCarrier 0x20000000
+#define bOFDMSingleTone 0x40000000
+/* define bRxPath1 0x01 */
+/* define bRxPath2 0x02 */
+/* define bRxPath3 0x04 */
+/* define bRxPath4 0x08 */
+/* define bTxPath1 0x10 */
+/* define bTxPath2 0x20 */
+#define bHTDetect 0x100
+#define bCFOEn 0x10000
+#define bCFOValue 0xfff00000
+#define bSigTone_Re 0x3f
+#define bSigTone_Im 0x7f00
+#define bCounter_CCA 0xffff
+#define bCounter_ParityFail 0xffff0000
+#define bCounter_RateIllegal 0xffff
+#define bCounter_CRC8Fail 0xffff0000
+#define bCounter_MCSNoSupport 0xffff
+#define bCounter_FastSync 0xffff
+#define bShortCFO 0xfff
+#define bShortCFOTLength 12 /* total */
+#define bShortCFOFLength 11 /* fraction */
+#define bLongCFO 0x7ff
+#define bLongCFOTLength 11
+#define bLongCFOFLength 11
+#define bTailCFO 0x1fff
+#define bTailCFOTLength 13
+#define bTailCFOFLength 12
+#define bmax_en_pwdB 0xffff
+#define bCC_power_dB 0xffff0000
+#define bnoise_pwdB 0xffff
+#define bPowerMeasTLength 10
+#define bPowerMeasFLength 3
+#define bRx_HT_BW 0x1
+#define bRxSC 0x6
+#define bRx_HT 0x8
+#define bNB_intf_det_on 0x1
+#define bIntf_win_len_cfg 0x30
+#define bNB_Intf_TH_cfg 0x1c0
+#define bRFGain 0x3f
+#define bTableSel 0x40
+#define bTRSW 0x80
+#define bRxSNR_A 0xff
+#define bRxSNR_B 0xff00
+#define bRxSNR_C 0xff0000
+#define bRxSNR_D 0xff000000
+#define bSNREVMTLength 8
+#define bSNREVMFLength 1
+#define bCSI1st 0xff
+#define bCSI2nd 0xff00
+#define bRxEVM1st 0xff0000
+#define bRxEVM2nd 0xff000000
+#define bSIGEVM 0xff
+#define bPWDB 0xff00
+#define bSGIEN 0x10000
+
+#define bSFactorQAM1 0xf /* Useless */
+#define bSFactorQAM2 0xf0
+#define bSFactorQAM3 0xf00
+#define bSFactorQAM4 0xf000
+#define bSFactorQAM5 0xf0000
+#define bSFactorQAM6 0xf0000
+#define bSFactorQAM7 0xf00000
+#define bSFactorQAM8 0xf000000
+#define bSFactorQAM9 0xf0000000
+#define bCSIScheme 0x100000
+
+#define bNoiseLvlTopSet 0x3 /* Useless */
+#define bChSmooth 0x4
+#define bChSmoothCfg1 0x38
+#define bChSmoothCfg2 0x1c0
+#define bChSmoothCfg3 0xe00
+#define bChSmoothCfg4 0x7000
+#define bMRCMode 0x800000
+#define bTHEVMCfg 0x7000000
+
+#define bLoopFitType 0x1 /* Useless */
+#define bUpdCFO 0x40
+#define bUpdCFOOffData 0x80
+#define bAdvUpdCFO 0x100
+#define bAdvTimeCtrl 0x800
+#define bUpdClko 0x1000
+#define bFC 0x6000
+#define bTrackingMode 0x8000
+#define bPhCmpEnable 0x10000
+#define bUpdClkoLTF 0x20000
+#define bComChCFO 0x40000
+#define bCSIEstiMode 0x80000
+#define bAdvUpdEqz 0x100000
+#define bUChCfg 0x7000000
+#define bUpdEqz 0x8000000
+
+#define bTxAGCRate18_06 0x7f7f7f7f /* Useless */
+#define bTxAGCRate54_24 0x7f7f7f7f
+#define bTxAGCRateMCS32 0x7f
+#define bTxAGCRateCCK 0x7f00
+#define bTxAGCRateMCS3_MCS0 0x7f7f7f7f
+#define bTxAGCRateMCS7_MCS4 0x7f7f7f7f
+#define bTxAGCRateMCS11_MCS8 0x7f7f7f7f
+#define bTxAGCRateMCS15_MCS12 0x7f7f7f7f
+
+/* Rx Pseduo noise */
+#define bRxPesudoNoiseOn 0x20000000 /* Useless */
+#define bRxPesudoNoise_A 0xff
+#define bRxPesudoNoise_B 0xff00
+#define bRxPesudoNoise_C 0xff0000
+#define bRxPesudoNoise_D 0xff000000
+#define bPesudoNoiseState_A 0xffff
+#define bPesudoNoiseState_B 0xffff0000
+#define bPesudoNoiseState_C 0xffff
+#define bPesudoNoiseState_D 0xffff0000
+
+/* 7. RF Register */
+/* Zebra1 */
+#define bZebra1_HSSIEnable 0x8 /* Useless */
+#define bZebra1_TRxControl 0xc00
+#define bZebra1_TRxGainSetting 0x07f
+#define bZebra1_RxCorner 0xc00
+#define bZebra1_TxChargePump 0x38
+#define bZebra1_RxChargePump 0x7
+#define bZebra1_ChannelNum 0xf80
+#define bZebra1_TxLPFBW 0x400
+#define bZebra1_RxLPFBW 0x600
+
+/* Zebra4 */
+#define bRTL8256RegModeCtrl1 0x100 /* Useless */
+#define bRTL8256RegModeCtrl0 0x40
+#define bRTL8256_TxLPFBW 0x18
+#define bRTL8256_RxLPFBW 0x600
+
+/* RTL8258 */
+#define bRTL8258_TxLPFBW 0xc /* Useless */
+#define bRTL8258_RxLPFBW 0xc00
+#define bRTL8258_RSSILPFBW 0xc0
+
+
+/* */
+/* Other Definition */
+/* */
+
+/* byte endable for sb_write */
+#define bByte0 0x1 /* Useless */
+#define bByte1 0x2
+#define bByte2 0x4
+#define bByte3 0x8
+#define bWord0 0x3
+#define bWord1 0xc
+#define bDWord 0xf
+
+/* for PutRegsetting & GetRegSetting BitMask */
+#define bMaskByte0 0xff /* Reg 0xc50 rOFDM0_XAAGCCore~0xC6f */
+#define bMaskByte1 0xff00
+#define bMaskByte2 0xff0000
+#define bMaskByte3 0xff000000
+#define bMaskHWord 0xffff0000
+#define bMaskLWord 0x0000ffff
+#define bMaskDWord 0xffffffff
+#define bMaskH4Bits 0xf0000000
+#define bMaskOFDM_D 0xffc00000
+#define bMaskCCK 0x3f3f3f3f
+#define bMask12Bits 0xfff
+
+/* for PutRFRegsetting & GetRFRegSetting BitMask */
+#if (RTL92SE_FPGA_VERIFY == 1)
+#define bRFRegOffsetMask 0xfff
+#else
+#define bRFRegOffsetMask 0xfffff
+#endif
+#define bEnable 0x1 /* Useless */
+#define bDisabl 0x0
+
+#define LeftAntenna 0x0 /* Useless */
+#define RightAntenna 0x1
+
+#define tCheckTxStatus 500 /* 500ms Useless */
+#define tUpdateRxCounter 100 /* 100ms */
+
+#define rateCCK 0 /* Useless */
+#define rateOFDM 1
+#define rateHT 2
+
+/* define Register-End */
+#define bPMAC_End 0x1ff /* Useless */
+#define bFPGAPHY0_End 0x8ff
+#define bFPGAPHY1_End 0x9ff
+#define bCCKPHY0_End 0xaff
+#define bOFDMPHY0_End 0xcff
+#define bOFDMPHY1_End 0xdff
+
+/* define max debug item in each debug page */
+/* define bMaxItem_FPGA_PHY0 0x9 */
+/* define bMaxItem_FPGA_PHY1 0x3 */
+/* define bMaxItem_PHY_11B 0x16 */
+/* define bMaxItem_OFDM_PHY0 0x29 */
+/* define bMaxItem_OFDM_PHY1 0x0 */
+
+#define bPMACControl 0x0 /* Useless */
+#define bWMACControl 0x1
+#define bWNICControl 0x2
+
+#define RCR_AAP BIT(0) /* accept all physical address */
+#define RCR_APM BIT(1) /* accept physical match */
+#define RCR_AM BIT(2) /* accept multicast */
+#define RCR_AB BIT(3) /* accept broadcast */
+#define RCR_ACRC32 BIT(5) /* accept error packet */
+#define RCR_9356SEL BIT(6)
+#define RCR_AICV BIT(12) /* Accept ICV error packet */
+#define RCR_RXFTH0 (BIT(13)|BIT(14)|BIT(15)) /* Rx FIFO threshold */
+#define RCR_ADF BIT(18) /* Accept Data(frame type) frame */
+#define RCR_ACF BIT(19) /* Accept control frame */
+#define RCR_AMF BIT(20) /* Accept management frame */
+#define RCR_ADD3 BIT(21)
+#define RCR_APWRMGT BIT(22) /* Accept power management packet */
+#define RCR_CBSSID BIT(23) /* Accept BSSID match packet */
+#define RCR_ENMARP BIT(28) /* enable mac auto reset phy */
+#define RCR_EnCS1 BIT(29) /* enable carrier sense method 1 */
+#define RCR_EnCS2 BIT(30) /* enable carrier sense method 2 */
+#define RCR_OnlyErlPkt BIT(31) /* Rx Early mode is performed for
+ * packet size greater than 1536 */
+
+/*--------------------------Define Parameters-------------------------------*/
+
+
+#endif /* __INC_HAL8192SPHYREG_H */
diff --git a/drivers/staging/rtl8188eu/include/rtw_pwrctrl.h b/drivers/staging/rtl8188eu/include/rtw_pwrctrl.h
new file mode 100644
index 000000000..aa1fd87c4
--- /dev/null
+++ b/drivers/staging/rtl8188eu/include/rtw_pwrctrl.h
@@ -0,0 +1,270 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2012 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ *
+ ******************************************************************************/
+#ifndef __RTW_PWRCTRL_H_
+#define __RTW_PWRCTRL_H_
+
+#include <osdep_service.h>
+#include <drv_types.h>
+
+#define FW_PWR0 0
+#define FW_PWR1 1
+#define FW_PWR2 2
+#define FW_PWR3 3
+#define HW_PWR0 7
+#define HW_PWR1 6
+#define HW_PWR2 2
+#define HW_PWR3 0
+#define HW_PWR4 8
+
+#define FW_PWRMSK 0x7
+
+#define XMIT_ALIVE BIT(0)
+#define RECV_ALIVE BIT(1)
+#define CMD_ALIVE BIT(2)
+#define EVT_ALIVE BIT(3)
+
+enum power_mgnt {
+ PS_MODE_ACTIVE = 0,
+ PS_MODE_MIN,
+ PS_MODE_MAX,
+ PS_MODE_DTIM,
+ PS_MODE_VOIP,
+ PS_MODE_UAPSD_WMM,
+ PS_MODE_UAPSD,
+ PS_MODE_IBSS,
+ PS_MODE_WWLAN,
+ PM_Radio_Off,
+ PM_Card_Disable,
+ PS_MODE_NUM
+};
+
+/*
+ BIT[2:0] = HW state
+ BIT[3] = Protocol PS state, 0: register active state,
+ 1: register sleep state
+ BIT[4] = sub-state
+*/
+
+#define PS_DPS BIT(0)
+#define PS_LCLK (PS_DPS)
+#define PS_RF_OFF BIT(1)
+#define PS_ALL_ON BIT(2)
+#define PS_ST_ACTIVE BIT(3)
+
+#define PS_ISR_ENABLE BIT(4)
+#define PS_IMR_ENABLE BIT(5)
+#define PS_ACK BIT(6)
+#define PS_TOGGLE BIT(7)
+
+#define PS_STATE_MASK (0x0F)
+#define PS_STATE_HW_MASK (0x07)
+#define PS_SEQ_MASK (0xc0)
+
+#define PS_STATE(x) (PS_STATE_MASK & (x))
+#define PS_STATE_HW(x) (PS_STATE_HW_MASK & (x))
+#define PS_SEQ(x) (PS_SEQ_MASK & (x))
+
+#define PS_STATE_S0 (PS_DPS)
+#define PS_STATE_S1 (PS_LCLK)
+#define PS_STATE_S2 (PS_RF_OFF)
+#define PS_STATE_S3 (PS_ALL_ON)
+#define PS_STATE_S4 ((PS_ST_ACTIVE) | (PS_ALL_ON))
+
+#define PS_IS_RF_ON(x) ((x) & (PS_ALL_ON))
+#define PS_IS_ACTIVE(x) ((x) & (PS_ST_ACTIVE))
+#define CLR_PS_STATE(x) ((x) = ((x) & (0xF0)))
+
+struct reportpwrstate_parm {
+ unsigned char mode;
+ unsigned char state; /* the CPWM value */
+ unsigned short rsvd;
+};
+
+static inline void _init_pwrlock(struct semaphore *plock)
+{
+ sema_init(plock, 1);
+}
+
+static inline void _enter_pwrlock(struct semaphore *plock)
+{
+ _rtw_down_sema(plock);
+}
+
+static inline void _exit_pwrlock(struct semaphore *plock)
+{
+ up(plock);
+}
+
+#define LPS_DELAY_TIME 1*HZ /* 1 sec */
+
+#define EXE_PWR_NONE 0x01
+#define EXE_PWR_IPS 0x02
+#define EXE_PWR_LPS 0x04
+
+/* RF state. */
+enum rt_rf_power_state {
+ rf_on, /* RF is on after RFSleep or RFOff */
+ rf_sleep, /* 802.11 Power Save mode */
+ rf_off, /* HW/SW Radio OFF or Inactive Power Save */
+ /* Add the new RF state above this line===== */
+ rf_max
+};
+
+/* RF Off Level for IPS or HW/SW radio off */
+#define RT_RF_OFF_LEVL_ASPM BIT(0) /* PCI ASPM */
+#define RT_RF_OFF_LEVL_CLK_REQ BIT(1) /* PCI clock request */
+#define RT_RF_OFF_LEVL_PCI_D3 BIT(2) /* PCI D3 mode */
+#define RT_RF_OFF_LEVL_HALT_NIC BIT(3) /* NIC halt, re-init hw param*/
+#define RT_RF_OFF_LEVL_FREE_FW BIT(4) /* FW free, re-download the FW*/
+#define RT_RF_OFF_LEVL_FW_32K BIT(5) /* FW in 32k */
+#define RT_RF_PS_LEVEL_ALWAYS_ASPM BIT(6) /* Always enable ASPM and Clock
+ * Req in initialization. */
+#define RT_RF_LPS_DISALBE_2R BIT(30) /* When LPS is on, disable 2R
+ * if no packet is RX or TX. */
+#define RT_RF_LPS_LEVEL_ASPM BIT(31) /* LPS with ASPM */
+
+#define RT_IN_PS_LEVEL(ppsc, _PS_FLAG) \
+ ((ppsc->cur_ps_level & _PS_FLAG) ? true : false)
+#define RT_CLEAR_PS_LEVEL(ppsc, _PS_FLAG) \
+ (ppsc->cur_ps_level &= (~(_PS_FLAG)))
+#define RT_SET_PS_LEVEL(ppsc, _PS_FLAG) \
+ (ppsc->cur_ps_level |= _PS_FLAG)
+
+enum _PS_BBRegBackup_ {
+ PSBBREG_RF0 = 0,
+ PSBBREG_RF1,
+ PSBBREG_RF2,
+ PSBBREG_AFE0,
+ PSBBREG_TOTALCNT
+};
+
+enum { /* for ips_mode */
+ IPS_NONE = 0,
+ IPS_NORMAL,
+ IPS_LEVEL_2,
+};
+
+struct pwrctrl_priv {
+ struct semaphore lock;
+ volatile u8 rpwm; /* requested power state for fw */
+ volatile u8 cpwm; /* fw current power state. updated when
+ * 1. read from HCPWM 2. driver lowers power level */
+ volatile u8 tog; /* toggling */
+ volatile u8 cpwm_tog; /* toggling */
+
+ u8 pwr_mode;
+ u8 smart_ps;
+ u8 bcn_ant_mode;
+
+ u32 alives;
+ struct work_struct cpwm_event;
+ u8 bpower_saving;
+
+ u8 b_hw_radio_off;
+ u8 reg_rfoff;
+ u8 reg_pdnmode; /* powerdown mode */
+ u32 rfoff_reason;
+
+ /* RF OFF Level */
+ u32 cur_ps_level;
+ u32 reg_rfps_level;
+ uint ips_enter_cnts;
+ uint ips_leave_cnts;
+
+ u8 ips_mode;
+ u8 ips_mode_req; /* used to accept the mode setting request,
+ * will update to ipsmode later */
+ uint bips_processing;
+ unsigned long ips_deny_time; /* will deny IPS when system time less than this */
+ u8 ps_processing; /* temp used to mark whether in rtw_ps_processor */
+
+ u8 bLeisurePs;
+ u8 LpsIdleCount;
+ u8 power_mgnt;
+ u8 bFwCurrentInPSMode;
+ u32 DelayLPSLastTimeStamp;
+ u8 btcoex_rfon;
+ s32 pnp_current_pwr_state;
+ u8 pnp_bstop_trx;
+
+ u8 bInternalAutoSuspend;
+ u8 bInSuspend;
+ u8 bSupportRemoteWakeup;
+ struct timer_list pwr_state_check_timer;
+ int pwr_state_check_interval;
+ u8 pwr_state_check_cnts;
+
+ int ps_flag;
+
+ enum rt_rf_power_state rf_pwrstate;/* cur power state */
+ enum rt_rf_power_state change_rfpwrstate;
+
+ u8 wepkeymask;
+ u8 bHWPowerdown;/* if support hw power down */
+ u8 bHWPwrPindetect;
+ u8 bkeepfwalive;
+ u8 brfoffbyhw;
+ unsigned long PS_BBRegBackup[PSBBREG_TOTALCNT];
+};
+
+#define rtw_get_ips_mode_req(pwrctrlpriv) \
+ (pwrctrlpriv)->ips_mode_req
+
+#define rtw_ips_mode_req(pwrctrlpriv, ips_mode) \
+ ((pwrctrlpriv)->ips_mode_req = (ips_mode))
+
+#define RTW_PWR_STATE_CHK_INTERVAL 2000
+
+#define _rtw_set_pwr_state_check_timer(pwrctrlpriv, ms) \
+ mod_timer(&pwrctrlpriv->pwr_state_check_timer, \
+ jiffies + msecs_to_jiffies(ms))
+
+#define rtw_set_pwr_state_check_timer(pwrctrl) \
+ _rtw_set_pwr_state_check_timer((pwrctrl), \
+ (pwrctrl)->pwr_state_check_interval)
+
+void rtw_init_pwrctrl_priv(struct adapter *adapter);
+
+void rtw_set_ps_mode(struct adapter *adapter, u8 ps_mode, u8 smart_ps,
+ u8 bcn_ant_mode);
+void rtw_set_rpwm(struct adapter *adapter, u8 val8);
+void LeaveAllPowerSaveMode(struct adapter *adapter);
+void ips_enter(struct adapter *padapter);
+int ips_leave(struct adapter *padapter);
+
+void rtw_ps_processor(struct adapter *padapter);
+
+enum rt_rf_power_state RfOnOffDetect(struct adapter *iadapter);
+
+s32 LPS_RF_ON_check(struct adapter *adapter, u32 delay_ms);
+void LPS_Enter(struct adapter *adapter);
+void LPS_Leave(struct adapter *adapter);
+
+void rtw_set_ips_deny(struct adapter *adapter, u32 ms);
+int _rtw_pwr_wakeup(struct adapter *adapter, u32 ips_defer_ms,
+ const char *caller);
+#define rtw_pwr_wakeup(adapter) \
+ _rtw_pwr_wakeup(adapter, RTW_PWR_STATE_CHK_INTERVAL, __func__)
+#define rtw_pwr_wakeup_ex(adapter, ips_deffer_ms) \
+ _rtw_pwr_wakeup(adapter, ips_deffer_ms, __func__)
+int rtw_pm_set_ips(struct adapter *adapter, u8 mode);
+int rtw_pm_set_lps(struct adapter *adapter, u8 mode);
+
+#endif /* __RTL871X_PWRCTRL_H_ */
diff --git a/drivers/staging/rtl8188eu/include/rtw_qos.h b/drivers/staging/rtl8188eu/include/rtw_qos.h
new file mode 100644
index 000000000..bbee1ddc0
--- /dev/null
+++ b/drivers/staging/rtl8188eu/include/rtw_qos.h
@@ -0,0 +1,30 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2011 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ *
+ ******************************************************************************/
+#ifndef _RTW_QOS_H_
+#define _RTW_QOS_H_
+
+#include <osdep_service.h>
+
+struct qos_priv {
+ unsigned int qos_option; /* bit mask option: u-apsd,
+ * s-apsd, ts, block ack... */
+};
+
+#endif /* _RTL871X_QOS_H_ */
diff --git a/drivers/staging/rtl8188eu/include/rtw_recv.h b/drivers/staging/rtl8188eu/include/rtw_recv.h
new file mode 100644
index 000000000..eb1ac3d03
--- /dev/null
+++ b/drivers/staging/rtl8188eu/include/rtw_recv.h
@@ -0,0 +1,365 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2012 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ *
+ ******************************************************************************/
+#ifndef _RTW_RECV_H_
+#define _RTW_RECV_H_
+
+#include <osdep_service.h>
+#include <drv_types.h>
+
+
+#define NR_RECVFRAME 256
+
+#define RXFRAME_ALIGN 8
+#define RXFRAME_ALIGN_SZ (1<<RXFRAME_ALIGN)
+
+#define MAX_RXFRAME_CNT 512
+#define MAX_RX_NUMBLKS (32)
+#define RECVFRAME_HDR_ALIGN 128
+
+#define SNAP_SIZE sizeof(struct ieee80211_snap_hdr)
+
+#define MAX_SUBFRAME_COUNT 64
+
+/* for Rx reordering buffer control */
+struct recv_reorder_ctrl {
+ struct adapter *padapter;
+ u8 enable;
+ u16 indicate_seq;/* wstart_b, init_value=0xffff */
+ u16 wend_b;
+ u8 wsize_b;
+ struct __queue pending_recvframe_queue;
+ struct timer_list reordering_ctrl_timer;
+};
+
+struct stainfo_rxcache {
+ u16 tid_rxseq[16];
+/*
+ unsigned short tid0_rxseq;
+ unsigned short tid1_rxseq;
+ unsigned short tid2_rxseq;
+ unsigned short tid3_rxseq;
+ unsigned short tid4_rxseq;
+ unsigned short tid5_rxseq;
+ unsigned short tid6_rxseq;
+ unsigned short tid7_rxseq;
+ unsigned short tid8_rxseq;
+ unsigned short tid9_rxseq;
+ unsigned short tid10_rxseq;
+ unsigned short tid11_rxseq;
+ unsigned short tid12_rxseq;
+ unsigned short tid13_rxseq;
+ unsigned short tid14_rxseq;
+ unsigned short tid15_rxseq;
+*/
+};
+
+struct smooth_rssi_data {
+ u32 elements[100]; /* array to store values */
+ u32 index; /* index to current array to store */
+ u32 total_num; /* num of valid elements */
+ u32 total_val; /* sum of valid elements */
+};
+
+struct signal_stat {
+ u8 update_req; /* used to indicate */
+ u8 avg_val; /* avg of valid elements */
+ u32 total_num; /* num of valid elements */
+ u32 total_val; /* sum of valid elements */
+};
+#define MAX_PATH_NUM_92CS 3
+struct phy_info {
+ u8 RxPWDBAll;
+ u8 SignalQuality; /* in 0-100 index. */
+ u8 RxMIMOSignalQuality[MAX_PATH_NUM_92CS]; /* EVM */
+ u8 RxMIMOSignalStrength[MAX_PATH_NUM_92CS];/* in 0~100 index */
+ s8 RxPower; /* in dBm Translate from PWdB */
+/* Real power in dBm for this packet, no beautification and aggregation.
+ * Keep this raw info to be used for the other procedures. */
+ s8 recvpower;
+ u8 BTRxRSSIPercentage;
+ u8 SignalStrength; /* in 0-100 index. */
+ u8 RxPwr[MAX_PATH_NUM_92CS];/* per-path's pwdb */
+ u8 RxSNR[MAX_PATH_NUM_92CS];/* per-path's SNR */
+};
+
+struct rx_pkt_attrib {
+ u16 pkt_len;
+ u8 physt;
+ u8 drvinfo_sz;
+ u8 shift_sz;
+ u8 hdrlen; /* the WLAN Header Len */
+ u8 to_fr_ds;
+ u8 amsdu;
+ u8 qos;
+ u8 priority;
+ u8 pw_save;
+ u8 mdata;
+ u16 seq_num;
+ u8 frag_num;
+ u8 mfrag;
+ u8 order;
+ u8 privacy; /* in frame_ctrl field */
+ u8 bdecrypted;
+ u8 encrypt; /* when 0 indicate no encrypt. when non-zero,
+ * indicate the encrypt algorith */
+ u8 iv_len;
+ u8 icv_len;
+ u8 crc_err;
+ u8 icv_err;
+
+ u16 eth_type;
+
+ u8 dst[ETH_ALEN];
+ u8 src[ETH_ALEN];
+ u8 ta[ETH_ALEN];
+ u8 ra[ETH_ALEN];
+ u8 bssid[ETH_ALEN];
+
+ u8 ack_policy;
+
+ u8 key_index;
+
+ u8 mcs_rate;
+ u8 rxht;
+ u8 sgi;
+ u8 pkt_rpt_type;
+ u32 MacIDValidEntry[2]; /* 64 bits present 64 entry. */
+
+ struct phy_info phy_info;
+};
+
+
+/* These definition is used for Rx packet reordering. */
+#define SN_LESS(a, b) (((a - b) & 0x800) != 0)
+#define SN_EQUAL(a, b) (a == b)
+#define REORDER_WAIT_TIME (50) /* (ms) */
+
+#define RECVBUFF_ALIGN_SZ 8
+
+#define RXDESC_SIZE 24
+#define RXDESC_OFFSET RXDESC_SIZE
+
+struct recv_stat {
+ __le32 rxdw0;
+ __le32 rxdw1;
+ __le32 rxdw2;
+ __le32 rxdw3;
+ __le32 rxdw4;
+ __le32 rxdw5;
+};
+
+#define EOR BIT(30)
+
+/*
+accesser of recv_priv: rtw_recv_entry(dispatch / passive level);
+recv_thread(passive) ; returnpkt(dispatch)
+; halt(passive) ;
+
+using enter_critical section to protect
+*/
+struct recv_priv {
+ struct __queue free_recv_queue;
+ struct __queue recv_pending_queue;
+ struct __queue uc_swdec_pending_queue;
+ u8 *pallocated_frame_buf;
+ u8 *precv_frame_buf;
+ uint free_recvframe_cnt;
+ struct adapter *adapter;
+ u32 bIsAnyNonBEPkts;
+ u64 rx_bytes;
+ u64 rx_pkts;
+ u64 rx_drop;
+ u64 last_rx_bytes;
+
+ uint ff_hwaddr;
+ u8 rx_pending_cnt;
+
+ struct tasklet_struct irq_prepare_beacon_tasklet;
+ struct tasklet_struct recv_tasklet;
+ struct sk_buff_head free_recv_skb_queue;
+ struct sk_buff_head rx_skb_queue;
+ u8 *pallocated_recv_buf;
+ u8 *precv_buf; /* 4 alignment */
+ struct __queue free_recv_buf_queue;
+ u32 free_recv_buf_queue_cnt;
+ /* For display the phy informatiom */
+ u8 is_signal_dbg; /* for debug */
+ u8 signal_strength_dbg; /* for debug */
+ s8 rssi;
+ s8 rxpwdb;
+ u8 signal_strength;
+ u8 signal_qual;
+ u8 noise;
+ s8 RxRssi[2];
+
+ struct timer_list signal_stat_timer;
+ u32 signal_stat_sampling_interval;
+ struct signal_stat signal_qual_data;
+ struct signal_stat signal_strength_data;
+};
+
+#define rtw_set_signal_stat_timer(recvpriv) \
+ mod_timer(&(recvpriv)->signal_stat_timer, jiffies + \
+ msecs_to_jiffies((recvpriv)->signal_stat_sampling_interval))
+
+struct sta_recv_priv {
+ spinlock_t lock;
+ int option;
+ struct __queue defrag_q; /* keeping the fragment frame until defrag */
+ struct stainfo_rxcache rxcache;
+};
+
+struct recv_buf {
+ struct adapter *adapter;
+ struct urb *purb;
+ struct sk_buff *pskb;
+ u8 reuse;
+};
+
+/*
+ head ----->
+
+ data ----->
+
+ payload
+
+ tail ----->
+
+
+ end ----->
+
+ len = (unsigned int )(tail - data);
+
+*/
+struct recv_frame {
+ struct list_head list;
+ struct sk_buff *pkt;
+ struct sk_buff *pkt_newalloc;
+ struct adapter *adapter;
+ struct rx_pkt_attrib attrib;
+ uint len;
+ u8 *rx_head;
+ u8 *rx_data;
+ u8 *rx_tail;
+ u8 *rx_end;
+ struct sta_info *psta;
+ /* for A-MPDU Rx reordering buffer control */
+ struct recv_reorder_ctrl *preorder_ctrl;
+};
+
+struct recv_frame *_rtw_alloc_recvframe(struct __queue *pfree_recv_queue);
+struct recv_frame *rtw_alloc_recvframe(struct __queue *pfree_recv_queue);
+void rtw_init_recvframe(struct recv_frame *precvframe,
+ struct recv_priv *precvpriv);
+int rtw_free_recvframe(struct recv_frame *precvframe,
+ struct __queue *pfree_recv_queue);
+#define rtw_dequeue_recvframe(queue) rtw_alloc_recvframe(queue)
+int _rtw_enqueue_recvframe(struct recv_frame *precvframe,
+ struct __queue *queue);
+int rtw_enqueue_recvframe(struct recv_frame *precvframe, struct __queue *queue);
+void rtw_free_recvframe_queue(struct __queue *pframequeue,
+ struct __queue *pfree_recv_queue);
+u32 rtw_free_uc_swdec_pending_queue(struct adapter *adapter);
+
+void rtw_reordering_ctrl_timeout_handler(unsigned long data);
+
+static inline u8 *get_rxmem(struct recv_frame *precvframe)
+{
+ /* always return rx_head... */
+ if (precvframe == NULL)
+ return NULL;
+ return precvframe->rx_head;
+}
+
+static inline u8 *recvframe_pull(struct recv_frame *precvframe, int sz)
+{
+ /* rx_data += sz; move rx_data sz bytes hereafter */
+
+ /* used for extract sz bytes from rx_data, update rx_data and return
+ * the updated rx_data to the caller */
+
+ if (precvframe == NULL)
+ return NULL;
+ precvframe->rx_data += sz;
+ if (precvframe->rx_data > precvframe->rx_tail) {
+ precvframe->rx_data -= sz;
+ return NULL;
+ }
+ precvframe->len -= sz;
+ return precvframe->rx_data;
+}
+
+static inline u8 *recvframe_put(struct recv_frame *precvframe, int sz)
+{
+ /* used for append sz bytes from ptr to rx_tail, update rx_tail
+ * and return the updated rx_tail to the caller */
+ /* after putting, rx_tail must be still larger than rx_end. */
+
+ if (precvframe == NULL)
+ return NULL;
+
+ precvframe->rx_tail += sz;
+
+ if (precvframe->rx_tail > precvframe->rx_end) {
+ precvframe->rx_tail -= sz;
+ return NULL;
+ }
+ precvframe->len += sz;
+ return precvframe->rx_tail;
+}
+
+static inline u8 *recvframe_pull_tail(struct recv_frame *precvframe, int sz)
+{
+ /* rmv data from rx_tail (by yitsen) */
+
+ /* used for extract sz bytes from rx_end, update rx_end and return
+ * the updated rx_end to the caller */
+ /* after pulling, rx_end must be still larger than rx_data. */
+
+ if (precvframe == NULL)
+ return NULL;
+ precvframe->rx_tail -= sz;
+ if (precvframe->rx_tail < precvframe->rx_data) {
+ precvframe->rx_tail += sz;
+ return NULL;
+ }
+ precvframe->len -= sz;
+ return precvframe->rx_tail;
+}
+
+static inline s32 translate_percentage_to_dbm(u32 sig_stren_index)
+{
+ s32 power; /* in dBm. */
+
+ /* Translate to dBm (x=0.5y-95). */
+ power = (s32)((sig_stren_index + 1) >> 1);
+ power -= 95;
+
+ return power;
+}
+
+
+struct sta_info;
+
+void _rtw_init_sta_recv_priv(struct sta_recv_priv *psta_recvpriv);
+
+void mgt_dispatcher(struct adapter *padapter, struct recv_frame *precv_frame);
+
+#endif
diff --git a/drivers/staging/rtl8188eu/include/rtw_rf.h b/drivers/staging/rtl8188eu/include/rtw_rf.h
new file mode 100644
index 000000000..2df88370d
--- /dev/null
+++ b/drivers/staging/rtl8188eu/include/rtw_rf.h
@@ -0,0 +1,146 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2011 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ *
+ ******************************************************************************/
+#ifndef __RTW_RF_H_
+#define __RTW_RF_H_
+
+#include <rtw_cmd.h>
+
+#define OFDM_PHY 1
+#define MIXED_PHY 2
+#define CCK_PHY 3
+
+#define NumRates (13)
+
+/* slot time for 11g */
+#define SHORT_SLOT_TIME 9
+#define NON_SHORT_SLOT_TIME 20
+
+#define RTL8711_RF_MAX_SENS 6
+#define RTL8711_RF_DEF_SENS 4
+
+/* We now define the following channels as the max channels in each
+ * channel plan. */
+/* 2G, total 14 chnls */
+/* {1,2,3,4,5,6,7,8,9,10,11,12,13,14} */
+#define MAX_CHANNEL_NUM_2G 14
+#define MAX_CHANNEL_NUM 14 /* 2.4 GHz only */
+
+#define NUM_REGULATORYS 1
+
+/* Country codes */
+#define USA 0x555320
+#define EUROPE 0x1 /* temp, should be provided later */
+#define JAPAN 0x2 /* temp, should be provided later */
+
+struct regulatory_class {
+ u32 starting_freq; /* MHz, */
+ u8 channel_set[MAX_CHANNEL_NUM];
+ u8 channel_cck_power[MAX_CHANNEL_NUM]; /* dbm */
+ u8 channel_ofdm_power[MAX_CHANNEL_NUM]; /* dbm */
+ u8 txpower_limit; /* dbm */
+ u8 channel_spacing; /* MHz */
+ u8 modem;
+};
+
+enum capability {
+ cESS = 0x0001,
+ cIBSS = 0x0002,
+ cPollable = 0x0004,
+ cPollReq = 0x0008,
+ cPrivacy = 0x0010,
+ cShortPreamble = 0x0020,
+ cPBCC = 0x0040,
+ cChannelAgility = 0x0080,
+ cSpectrumMgnt = 0x0100,
+ cQos = 0x0200, /* For HCCA, use with CF-Pollable
+ * and CF-PollReq */
+ cShortSlotTime = 0x0400,
+ cAPSD = 0x0800,
+ cRM = 0x1000, /* RRM (Radio Request Measurement) */
+ cDSSS_OFDM = 0x2000,
+ cDelayedBA = 0x4000,
+ cImmediateBA = 0x8000,
+};
+
+enum _REG_PREAMBLE_MODE {
+ PREAMBLE_LONG = 1,
+ PREAMBLE_AUTO = 2,
+ PREAMBLE_SHORT = 3,
+};
+
+enum _RTL8712_RF_MIMO_CONFIG_ {
+ RTL8712_RFCONFIG_1T = 0x10,
+ RTL8712_RFCONFIG_2T = 0x20,
+ RTL8712_RFCONFIG_1R = 0x01,
+ RTL8712_RFCONFIG_2R = 0x02,
+ RTL8712_RFCONFIG_1T1R = 0x11,
+ RTL8712_RFCONFIG_1T2R = 0x12,
+ RTL8712_RFCONFIG_TURBO = 0x92,
+ RTL8712_RFCONFIG_2T2R = 0x22
+};
+
+enum rf90_radio_path {
+ RF90_PATH_A = 0, /* Radio Path A */
+ RF90_PATH_B = 1, /* Radio Path B */
+ RF90_PATH_C = 2, /* Radio Path C */
+ RF90_PATH_D = 3 /* Radio Path D */
+};
+
+/* Bandwidth Offset */
+#define HAL_PRIME_CHNL_OFFSET_DONT_CARE 0
+#define HAL_PRIME_CHNL_OFFSET_LOWER 1
+#define HAL_PRIME_CHNL_OFFSET_UPPER 2
+
+/* Represent Channel Width in HT Capabilities */
+/* */
+enum ht_channel_width {
+ HT_CHANNEL_WIDTH_20 = 0,
+ HT_CHANNEL_WIDTH_40 = 1,
+ HT_CHANNEL_WIDTH_80 = 2,
+ HT_CHANNEL_WIDTH_160 = 3,
+ HT_CHANNEL_WIDTH_10 = 4,
+};
+
+/* */
+/* Represent Extension Channel Offset in HT Capabilities */
+/* This is available only in 40Mhz mode. */
+/* */
+enum ht_extchnl_offset {
+ HT_EXTCHNL_OFFSET_NO_EXT = 0,
+ HT_EXTCHNL_OFFSET_UPPER = 1,
+ HT_EXTCHNL_OFFSET_NO_DEF = 2,
+ HT_EXTCHNL_OFFSET_LOWER = 3,
+};
+
+/* 2007/11/15 MH Define different RF type. */
+enum rt_rf_type_def {
+ RF_1T2R = 0,
+ RF_2T4R = 1,
+ RF_2T2R = 2,
+ RF_1T1R = 3,
+ RF_2T2R_GREEN = 4,
+ RF_819X_MAX_TYPE = 5,
+};
+
+u32 rtw_ch2freq(u32 ch);
+u32 rtw_freq2ch(u32 freq);
+
+
+#endif /* _RTL8711_RF_H_ */
diff --git a/drivers/staging/rtl8188eu/include/rtw_security.h b/drivers/staging/rtl8188eu/include/rtw_security.h
new file mode 100644
index 000000000..e9723a72a
--- /dev/null
+++ b/drivers/staging/rtl8188eu/include/rtw_security.h
@@ -0,0 +1,356 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2011 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ *
+ ******************************************************************************/
+#ifndef __RTW_SECURITY_H_
+#define __RTW_SECURITY_H_
+
+#include <osdep_service.h>
+#include <drv_types.h>
+
+#define _NO_PRIVACY_ 0x0
+#define _WEP40_ 0x1
+#define _TKIP_ 0x2
+#define _TKIP_WTMIC_ 0x3
+#define _AES_ 0x4
+#define _WEP104_ 0x5
+#define _WEP_WPA_MIXED_ 0x07 /* WEP + WPA */
+#define _SMS4_ 0x06
+
+#define is_wep_enc(alg) (((alg) == _WEP40_) || ((alg) == _WEP104_))
+
+#define _WPA_IE_ID_ 0xdd
+#define _WPA2_IE_ID_ 0x30
+
+#define SHA256_MAC_LEN 32
+#define AES_BLOCK_SIZE 16
+#define AES_PRIV_SIZE (4 * 44)
+
+enum {
+ ENCRYP_PROTOCOL_OPENSYS, /* open system */
+ ENCRYP_PROTOCOL_WEP, /* WEP */
+ ENCRYP_PROTOCOL_WPA, /* WPA */
+ ENCRYP_PROTOCOL_WPA2, /* WPA2 */
+ ENCRYP_PROTOCOL_WAPI, /* WAPI: Not support in this version */
+ ENCRYP_PROTOCOL_MAX
+};
+
+
+#ifndef Ndis802_11AuthModeWPA2
+#define Ndis802_11AuthModeWPA2 (Ndis802_11AuthModeWPANone + 1)
+#endif
+
+#ifndef Ndis802_11AuthModeWPA2PSK
+#define Ndis802_11AuthModeWPA2PSK (Ndis802_11AuthModeWPANone + 2)
+#endif
+
+union pn48 {
+ u64 val;
+
+#ifdef __LITTLE_ENDIAN
+ struct {
+ u8 TSC0;
+ u8 TSC1;
+ u8 TSC2;
+ u8 TSC3;
+ u8 TSC4;
+ u8 TSC5;
+ u8 TSC6;
+ u8 TSC7;
+ } _byte_;
+
+#elif defined(__BIG_ENDIAN)
+
+ struct {
+ u8 TSC7;
+ u8 TSC6;
+ u8 TSC5;
+ u8 TSC4;
+ u8 TSC3;
+ u8 TSC2;
+ u8 TSC1;
+ u8 TSC0;
+ } _byte_;
+#endif
+};
+
+union Keytype {
+ u8 skey[16];
+ u32 lkey[4];
+};
+
+struct rt_pmkid_list {
+ u8 bUsed;
+ u8 Bssid[6];
+ u8 PMKID[16];
+ u8 SsidBuf[33];
+ u8 *ssid_octet;
+ u16 ssid_length;
+};
+
+struct security_priv {
+ u32 dot11AuthAlgrthm; /* 802.11 auth, could be open,
+ * shared, 8021x and authswitch */
+ u32 dot11PrivacyAlgrthm; /* This specify the privacy for
+ * shared auth. algorithm. */
+ /* WEP */
+ u32 dot11PrivacyKeyIndex; /* this is only valid for legendary
+ * wep, 0~3 for key id.(tx key index) */
+ union Keytype dot11DefKey[4]; /* this is only valid for def. key */
+ u32 dot11DefKeylen[4];
+ u32 dot118021XGrpPrivacy; /* This specify the privacy algthm.
+ * used for Grp key */
+ u32 dot118021XGrpKeyid; /* key id used for Grp Key
+ * ( tx key index) */
+ union Keytype dot118021XGrpKey[4]; /* 802.1x Group Key,
+ * for inx0 and inx1 */
+ union Keytype dot118021XGrptxmickey[4];
+ union Keytype dot118021XGrprxmickey[4];
+ union pn48 dot11Grptxpn; /* PN48 used for Grp Key xmit.*/
+ union pn48 dot11Grprxpn; /* PN48 used for Grp Key recv.*/
+#ifdef CONFIG_88EU_AP_MODE
+ /* extend security capabilities for AP_MODE */
+ unsigned int dot8021xalg;/* 0:disable, 1:psk, 2:802.1x */
+ unsigned int wpa_psk;/* 0:disable, bit(0): WPA, bit(1):WPA2 */
+ unsigned int wpa_group_cipher;
+ unsigned int wpa2_group_cipher;
+ unsigned int wpa_pairwise_cipher;
+ unsigned int wpa2_pairwise_cipher;
+#endif
+ u8 wps_ie[MAX_WPS_IE_LEN];/* added in assoc req */
+ int wps_ie_len;
+ u8 binstallGrpkey;
+ u8 busetkipkey;
+ u8 bcheck_grpkey;
+ u8 bgrpkey_handshake;
+ s32 sw_encrypt;/* from registry_priv */
+ s32 sw_decrypt;/* from registry_priv */
+ s32 hw_decrypted;/* if the rx packets is hw_decrypted==false,i
+ * it means the hw has not been ready. */
+
+ /* keeps the auth_type & enc_status from upper layer
+ * ioctl(wpa_supplicant or wzc) */
+ u32 ndisauthtype; /* NDIS_802_11_AUTHENTICATION_MODE */
+ u32 ndisencryptstatus; /* NDIS_802_11_ENCRYPTION_STATUS */
+ struct wlan_bssid_ex sec_bss; /* for joinbss (h2c buffer) usage */
+ struct ndis_802_11_wep ndiswep;
+ u8 assoc_info[600];
+ u8 szofcapability[256]; /* for wpa2 usage */
+ u8 oidassociation[512]; /* for wpa/wpa2 usage */
+ u8 authenticator_ie[256]; /* store ap security information element */
+ u8 supplicant_ie[256]; /* store sta security information element */
+
+ /* for tkip countermeasure */
+ u32 last_mic_err_time;
+ u8 btkip_countermeasure;
+ u8 btkip_wait_report;
+ u32 btkip_countermeasure_time;
+
+ /* */
+ /* For WPA2 Pre-Authentication. */
+ /* */
+ struct rt_pmkid_list PMKIDList[NUM_PMKID_CACHE];
+ u8 PMKIDIndex;
+ u8 bWepDefaultKeyIdxSet;
+};
+
+struct sha256_state {
+ u64 length;
+ u32 state[8], curlen;
+ u8 buf[64];
+};
+
+#define GET_ENCRY_ALGO(psecuritypriv, psta, encry_algo, bmcst) \
+do { \
+ switch (psecuritypriv->dot11AuthAlgrthm) { \
+ case dot11AuthAlgrthm_Open: \
+ case dot11AuthAlgrthm_Shared: \
+ case dot11AuthAlgrthm_Auto: \
+ encry_algo = (u8)psecuritypriv->dot11PrivacyAlgrthm; \
+ break; \
+ case dot11AuthAlgrthm_8021X: \
+ if (bmcst) \
+ encry_algo = (u8)psecuritypriv->dot118021XGrpPrivacy;\
+ else \
+ encry_algo = (u8)psta->dot118021XPrivacy; \
+ break; \
+ case dot11AuthAlgrthm_WAPI: \
+ encry_algo = (u8)psecuritypriv->dot11PrivacyAlgrthm; \
+ break; \
+ } \
+} while (0)
+
+#define SET_ICE_IV_LEN(iv_len, icv_len, encrypt) \
+do { \
+ switch (encrypt) { \
+ case _WEP40_: \
+ case _WEP104_: \
+ iv_len = 4; \
+ icv_len = 4; \
+ break; \
+ case _TKIP_: \
+ iv_len = 8; \
+ icv_len = 4; \
+ break; \
+ case _AES_: \
+ iv_len = 8; \
+ icv_len = 8; \
+ break; \
+ case _SMS4_: \
+ iv_len = 18; \
+ icv_len = 16; \
+ break; \
+ default: \
+ iv_len = 0; \
+ icv_len = 0; \
+ break; \
+ } \
+} while (0)
+
+
+#define GET_TKIP_PN(iv, dot11txpn) \
+do { \
+ dot11txpn._byte_.TSC0 = iv[2]; \
+ dot11txpn._byte_.TSC1 = iv[0]; \
+ dot11txpn._byte_.TSC2 = iv[4]; \
+ dot11txpn._byte_.TSC3 = iv[5]; \
+ dot11txpn._byte_.TSC4 = iv[6]; \
+ dot11txpn._byte_.TSC5 = iv[7]; \
+} while (0)
+
+
+#define ROL32(A, n) (((A) << (n)) | (((A)>>(32-(n))) & ((1UL << (n)) - 1)))
+#define ROR32(A, n) ROL32((A), 32-(n))
+
+struct mic_data {
+ u32 K0, K1; /* Key */
+ u32 L, R; /* Current state */
+ u32 M; /* Message accumulator (single word) */
+ u32 nBytesInM; /* # bytes in M */
+};
+
+extern const u32 Te0[256];
+extern const u32 Td0[256];
+extern const u32 Td1[256];
+extern const u32 Td2[256];
+extern const u32 Td3[256];
+extern const u32 Td4[256];
+extern const u32 rcon[10];
+extern const u8 Td4s[256];
+extern const u8 rcons[10];
+
+#define RCON(i) (rcons[(i)] << 24)
+
+static inline u32 rotr(u32 val, int bits)
+{
+ return (val >> bits) | (val << (32 - bits));
+}
+
+#define TE0(i) Te0[((i) >> 24) & 0xff]
+#define TE1(i) rotr(Te0[((i) >> 16) & 0xff], 8)
+#define TE2(i) rotr(Te0[((i) >> 8) & 0xff], 16)
+#define TE3(i) rotr(Te0[(i) & 0xff], 24)
+
+#define GETU32(pt) (((u32)(pt)[0] << 24) ^ ((u32)(pt)[1] << 16) ^ \
+ ((u32)(pt)[2] << 8) ^ ((u32)(pt)[3]))
+
+#define PUTU32(ct, st) { \
+(ct)[0] = (u8)((st) >> 24); (ct)[1] = (u8)((st) >> 16); \
+(ct)[2] = (u8)((st) >> 8); (ct)[3] = (u8)(st); }
+
+#define WPA_GET_BE32(a) ((((u32)(a)[0]) << 24) | (((u32)(a)[1]) << 16) | \
+ (((u32)(a)[2]) << 8) | ((u32)(a)[3]))
+
+#define WPA_PUT_LE16(a, val) \
+ do { \
+ (a)[1] = ((u16)(val)) >> 8; \
+ (a)[0] = ((u16)(val)) & 0xff; \
+ } while (0)
+
+#define WPA_PUT_BE32(a, val) \
+ do { \
+ (a)[0] = (u8)((((u32)(val)) >> 24) & 0xff); \
+ (a)[1] = (u8)((((u32)(val)) >> 16) & 0xff); \
+ (a)[2] = (u8)((((u32)(val)) >> 8) & 0xff); \
+ (a)[3] = (u8)(((u32)(val)) & 0xff); \
+ } while (0)
+
+#define WPA_PUT_BE64(a, val) \
+ do { \
+ (a)[0] = (u8)(((u64)(val)) >> 56); \
+ (a)[1] = (u8)(((u64)(val)) >> 48); \
+ (a)[2] = (u8)(((u64)(val)) >> 40); \
+ (a)[3] = (u8)(((u64)(val)) >> 32); \
+ (a)[4] = (u8)(((u64)(val)) >> 24); \
+ (a)[5] = (u8)(((u64)(val)) >> 16); \
+ (a)[6] = (u8)(((u64)(val)) >> 8); \
+ (a)[7] = (u8)(((u64)(val)) & 0xff); \
+ } while (0)
+
+/* ===== start - public domain SHA256 implementation ===== */
+
+/* This is based on SHA256 implementation in LibTomCrypt that was released into
+ * public domain by Tom St Denis. */
+
+/* the K array */
+static const unsigned long K[64] = {
+ 0x428a2f98UL, 0x71374491UL, 0xb5c0fbcfUL, 0xe9b5dba5UL, 0x3956c25bUL,
+ 0x59f111f1UL, 0x923f82a4UL, 0xab1c5ed5UL, 0xd807aa98UL, 0x12835b01UL,
+ 0x243185beUL, 0x550c7dc3UL, 0x72be5d74UL, 0x80deb1feUL, 0x9bdc06a7UL,
+ 0xc19bf174UL, 0xe49b69c1UL, 0xefbe4786UL, 0x0fc19dc6UL, 0x240ca1ccUL,
+ 0x2de92c6fUL, 0x4a7484aaUL, 0x5cb0a9dcUL, 0x76f988daUL, 0x983e5152UL,
+ 0xa831c66dUL, 0xb00327c8UL, 0xbf597fc7UL, 0xc6e00bf3UL, 0xd5a79147UL,
+ 0x06ca6351UL, 0x14292967UL, 0x27b70a85UL, 0x2e1b2138UL, 0x4d2c6dfcUL,
+ 0x53380d13UL, 0x650a7354UL, 0x766a0abbUL, 0x81c2c92eUL, 0x92722c85UL,
+ 0xa2bfe8a1UL, 0xa81a664bUL, 0xc24b8b70UL, 0xc76c51a3UL, 0xd192e819UL,
+ 0xd6990624UL, 0xf40e3585UL, 0x106aa070UL, 0x19a4c116UL, 0x1e376c08UL,
+ 0x2748774cUL, 0x34b0bcb5UL, 0x391c0cb3UL, 0x4ed8aa4aUL, 0x5b9cca4fUL,
+ 0x682e6ff3UL, 0x748f82eeUL, 0x78a5636fUL, 0x84c87814UL, 0x8cc70208UL,
+ 0x90befffaUL, 0xa4506cebUL, 0xbef9a3f7UL, 0xc67178f2UL
+};
+
+/* Various logical functions */
+#define RORc(x, y) \
+ (((((unsigned long)(x) & 0xFFFFFFFFUL) >> (unsigned long)((y)&31)) | \
+ ((unsigned long)(x) << (unsigned long)(32-((y)&31)))) & 0xFFFFFFFFUL)
+#define Ch(x, y , z) (z ^ (x & (y ^ z)))
+#define Maj(x, y, z) (((x | y) & z) | (x & y))
+#define S(x, n) RORc((x), (n))
+#define R(x, n) (((x)&0xFFFFFFFFUL)>>(n))
+#define Sigma0(x) (S(x, 2) ^ S(x, 13) ^ S(x, 22))
+#define Sigma1(x) (S(x, 6) ^ S(x, 11) ^ S(x, 25))
+#define Gamma0(x) (S(x, 7) ^ S(x, 18) ^ R(x, 3))
+#define Gamma1(x) (S(x, 17) ^ S(x, 19) ^ R(x, 10))
+#ifndef MIN
+#define MIN(x, y) (((x) < (y)) ? (x) : (y))
+#endif
+
+void rtw_secmicsetkey(struct mic_data *pmicdata, u8 *key);
+void rtw_secmicappendbyte(struct mic_data *pmicdata, u8 b);
+void rtw_secmicappend(struct mic_data *pmicdata, u8 *src, u32 nBytes);
+void rtw_secgetmic(struct mic_data *pmicdata, u8 *dst);
+void rtw_seccalctkipmic(u8 *key, u8 *header, u8 *data, u32 data_len,
+ u8 *Miccode, u8 priority);
+u32 rtw_aes_encrypt(struct adapter *padapter, u8 *pxmitframe);
+u32 rtw_tkip_encrypt(struct adapter *padapter, u8 *pxmitframe);
+void rtw_wep_encrypt(struct adapter *padapter, u8 *pxmitframe);
+u32 rtw_aes_decrypt(struct adapter *padapter, u8 *precvframe);
+u32 rtw_tkip_decrypt(struct adapter *padapter, u8 *precvframe);
+void rtw_wep_decrypt(struct adapter *padapter, u8 *precvframe);
+
+#endif /* __RTL871X_SECURITY_H_ */
diff --git a/drivers/staging/rtl8188eu/include/rtw_sreset.h b/drivers/staging/rtl8188eu/include/rtw_sreset.h
new file mode 100644
index 000000000..580e85051
--- /dev/null
+++ b/drivers/staging/rtl8188eu/include/rtw_sreset.h
@@ -0,0 +1,45 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2012 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ *
+ ******************************************************************************/
+#ifndef _RTW_SRESET_C_
+#define _RTW_SRESET_C_
+
+#include <osdep_service.h>
+#include <drv_types.h>
+
+struct sreset_priv {
+ u8 Wifi_Error_Status;
+};
+
+#include <rtl8188e_hal.h>
+
+#define WIFI_STATUS_SUCCESS 0
+#define USB_VEN_REQ_CMD_FAIL BIT0
+#define USB_READ_PORT_FAIL BIT1
+#define USB_WRITE_PORT_FAIL BIT2
+#define WIFI_MAC_TXDMA_ERROR BIT3
+#define WIFI_TX_HANG BIT4
+#define WIFI_RX_HANG BIT5
+#define WIFI_IF_NOT_EXIST BIT6
+
+void sreset_init_value(struct adapter *padapter);
+u8 sreset_get_wifi_status(struct adapter *padapter);
+void sreset_set_wifi_error_status(struct adapter *padapter, u32 status);
+
+#endif
diff --git a/drivers/staging/rtl8188eu/include/rtw_xmit.h b/drivers/staging/rtl8188eu/include/rtw_xmit.h
new file mode 100644
index 000000000..62f5db169
--- /dev/null
+++ b/drivers/staging/rtl8188eu/include/rtw_xmit.h
@@ -0,0 +1,379 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2011 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ *
+ ******************************************************************************/
+#ifndef _RTW_XMIT_H_
+#define _RTW_XMIT_H_
+
+#include <osdep_service.h>
+#include <drv_types.h>
+
+#define MAX_XMITBUF_SZ (20480) /* 20k */
+#define NR_XMITBUFF (4)
+
+#define XMITBUF_ALIGN_SZ 4
+
+/* xmit extension buff defination */
+#define MAX_XMIT_EXTBUF_SZ (1536)
+#define NR_XMIT_EXTBUFF (32)
+
+#define MAX_NUMBLKS (1)
+
+#define XMIT_VO_QUEUE (0)
+#define XMIT_VI_QUEUE (1)
+#define XMIT_BE_QUEUE (2)
+#define XMIT_BK_QUEUE (3)
+
+#define VO_QUEUE_INX 0
+#define VI_QUEUE_INX 1
+#define BE_QUEUE_INX 2
+#define BK_QUEUE_INX 3
+#define BCN_QUEUE_INX 4
+#define MGT_QUEUE_INX 5
+#define HIGH_QUEUE_INX 6
+#define TXCMD_QUEUE_INX 7
+
+#define HW_QUEUE_ENTRY 8
+
+#define WEP_IV(pattrib_iv, dot11txpn, keyidx)\
+do {\
+ pattrib_iv[0] = dot11txpn._byte_.TSC0;\
+ pattrib_iv[1] = dot11txpn._byte_.TSC1;\
+ pattrib_iv[2] = dot11txpn._byte_.TSC2;\
+ pattrib_iv[3] = ((keyidx & 0x3)<<6);\
+ dot11txpn.val = (dot11txpn.val == 0xffffff) ? 0 : (dot11txpn.val+1);\
+} while (0)
+
+
+#define TKIP_IV(pattrib_iv, dot11txpn, keyidx)\
+do {\
+ pattrib_iv[0] = dot11txpn._byte_.TSC1;\
+ pattrib_iv[1] = (dot11txpn._byte_.TSC1 | 0x20) & 0x7f;\
+ pattrib_iv[2] = dot11txpn._byte_.TSC0;\
+ pattrib_iv[3] = BIT(5) | ((keyidx & 0x3)<<6);\
+ pattrib_iv[4] = dot11txpn._byte_.TSC2;\
+ pattrib_iv[5] = dot11txpn._byte_.TSC3;\
+ pattrib_iv[6] = dot11txpn._byte_.TSC4;\
+ pattrib_iv[7] = dot11txpn._byte_.TSC5;\
+ dot11txpn.val = dot11txpn.val == 0xffffffffffffULL ? 0 : (dot11txpn.val+1);\
+} while (0)
+
+#define AES_IV(pattrib_iv, dot11txpn, keyidx)\
+do { \
+ pattrib_iv[0] = dot11txpn._byte_.TSC0; \
+ pattrib_iv[1] = dot11txpn._byte_.TSC1; \
+ pattrib_iv[2] = 0; \
+ pattrib_iv[3] = BIT(5) | ((keyidx & 0x3)<<6); \
+ pattrib_iv[4] = dot11txpn._byte_.TSC2; \
+ pattrib_iv[5] = dot11txpn._byte_.TSC3; \
+ pattrib_iv[6] = dot11txpn._byte_.TSC4; \
+ pattrib_iv[7] = dot11txpn._byte_.TSC5; \
+ dot11txpn.val = dot11txpn.val == 0xffffffffffffULL ? 0 : (dot11txpn.val+1);\
+} while (0)
+
+#define HWXMIT_ENTRY 4
+
+#define TXDESC_SIZE 32
+
+#define PACKET_OFFSET_SZ (8)
+#define TXDESC_OFFSET (TXDESC_SIZE + PACKET_OFFSET_SZ)
+
+struct tx_desc {
+ /* DWORD 0 */
+ __le32 txdw0;
+ __le32 txdw1;
+ __le32 txdw2;
+ __le32 txdw3;
+ __le32 txdw4;
+ __le32 txdw5;
+ __le32 txdw6;
+ __le32 txdw7;
+};
+
+struct hw_xmit {
+ struct __queue *sta_queue;
+ int accnt;
+};
+
+/* reduce size */
+struct pkt_attrib {
+ u8 type;
+ u8 subtype;
+ u8 bswenc;
+ u8 dhcp_pkt;
+ u16 ether_type;
+ u16 seqnum;
+ u16 pkt_hdrlen; /* the original 802.3 pkt header len */
+ u16 hdrlen; /* the WLAN Header Len */
+ u32 pktlen; /* the original 802.3 pkt raw_data len (not include
+ * ether_hdr data) */
+ u32 last_txcmdsz;
+ u8 nr_frags;
+ u8 encrypt; /* when 0 indicate no encrypt. when non-zero,
+ * indicate the encrypt algorith */
+ u8 iv_len;
+ u8 icv_len;
+ u8 iv[18];
+ u8 icv[16];
+ u8 priority;
+ u8 ack_policy;
+ u8 mac_id;
+ u8 vcs_mode; /* virtual carrier sense method */
+ u8 dst[ETH_ALEN];
+ u8 src[ETH_ALEN];
+ u8 ta[ETH_ALEN];
+ u8 ra[ETH_ALEN];
+ u8 key_idx;
+ u8 qos_en;
+ u8 ht_en;
+ u8 raid;/* rate adpative id */
+ u8 bwmode;
+ u8 ch_offset;/* PRIME_CHNL_OFFSET */
+ u8 sgi;/* short GI */
+ u8 ampdu_en;/* tx ampdu enable */
+ u8 mdata;/* more data bit */
+ u8 pctrl;/* per packet txdesc control enable */
+ u8 triggered;/* for ap mode handling Power Saving sta */
+ u8 qsel;
+ u8 eosp;
+ u8 rate;
+ u8 intel_proxim;
+ u8 retry_ctrl;
+ struct sta_info *psta;
+};
+
+#define WLANHDR_OFFSET 64
+
+#define NULL_FRAMETAG (0x0)
+#define DATA_FRAMETAG 0x01
+#define L2_FRAMETAG 0x02
+#define MGNT_FRAMETAG 0x03
+#define AMSDU_FRAMETAG 0x04
+
+#define EII_FRAMETAG 0x05
+#define IEEE8023_FRAMETAG 0x06
+
+#define MP_FRAMETAG 0x07
+
+#define TXAGG_FRAMETAG 0x08
+
+struct submit_ctx {
+ u32 submit_time; /* */
+ u32 timeout_ms; /* <0: not synchronous, 0: wait forever, >0: up to ms waiting */
+ int status; /* status for operation */
+ struct completion done;
+};
+
+enum {
+ RTW_SCTX_SUBMITTED = -1,
+ RTW_SCTX_DONE_SUCCESS = 0,
+ RTW_SCTX_DONE_UNKNOWN,
+ RTW_SCTX_DONE_TIMEOUT,
+ RTW_SCTX_DONE_BUF_ALLOC,
+ RTW_SCTX_DONE_BUF_FREE,
+ RTW_SCTX_DONE_WRITE_PORT_ERR,
+ RTW_SCTX_DONE_TX_DESC_NA,
+ RTW_SCTX_DONE_TX_DENY,
+ RTW_SCTX_DONE_CCX_PKT_FAIL,
+ RTW_SCTX_DONE_DRV_STOP,
+ RTW_SCTX_DONE_DEV_REMOVE,
+};
+
+void rtw_sctx_init(struct submit_ctx *sctx, int timeout_ms);
+int rtw_sctx_wait(struct submit_ctx *sctx);
+void rtw_sctx_done_err(struct submit_ctx **sctx, int status);
+void rtw_sctx_done(struct submit_ctx **sctx);
+
+struct xmit_buf {
+ struct list_head list;
+ struct adapter *padapter;
+ u8 *pallocated_buf;
+ u8 *pbuf;
+ void *priv_data;
+ u16 ext_tag; /* 0: Normal xmitbuf, 1: extension xmitbuf. */
+ u16 flags;
+ u32 alloc_sz;
+ u32 len;
+ struct submit_ctx *sctx;
+ u32 ff_hwaddr;
+ struct urb *pxmit_urb[8];
+ dma_addr_t dma_transfer_addr; /* (in) dma addr for transfer_buffer */
+ u8 bpending[8];
+ int last[8];
+};
+
+struct xmit_frame {
+ struct list_head list;
+ struct pkt_attrib attrib;
+ struct sk_buff *pkt;
+ int frame_tag;
+ struct adapter *padapter;
+ u8 *buf_addr;
+ struct xmit_buf *pxmitbuf;
+
+ u8 agg_num;
+ s8 pkt_offset;
+ u8 ack_report;
+};
+
+struct tx_servq {
+ struct list_head tx_pending;
+ struct __queue sta_pending;
+ int qcnt;
+};
+
+struct sta_xmit_priv {
+ spinlock_t lock;
+ int option;
+ int apsd_setting; /* When bit mask is on, the associated edca
+ * queue supports APSD. */
+ struct tx_servq be_q; /* priority == 0,3 */
+ struct tx_servq bk_q; /* priority == 1,2 */
+ struct tx_servq vi_q; /* priority == 4,5 */
+ struct tx_servq vo_q; /* priority == 6,7 */
+ struct list_head legacy_dz;
+ struct list_head apsd;
+ u16 txseq_tid[16];
+};
+
+struct hw_txqueue {
+ volatile int head;
+ volatile int tail;
+ volatile int free_sz; /* in units of 64 bytes */
+ volatile int free_cmdsz;
+ volatile int txsz[8];
+ uint ff_hwaddr;
+ uint cmd_hwaddr;
+ int ac_tag;
+};
+
+struct agg_pkt_info {
+ u16 offset;
+ u16 pkt_len;
+};
+
+struct xmit_priv {
+ spinlock_t lock;
+ struct semaphore xmit_sema;
+ struct semaphore terminate_xmitthread_sema;
+ struct __queue be_pending;
+ struct __queue bk_pending;
+ struct __queue vi_pending;
+ struct __queue vo_pending;
+ struct __queue bm_pending;
+ u8 *pallocated_frame_buf;
+ u8 *pxmit_frame_buf;
+ uint free_xmitframe_cnt;
+ struct __queue free_xmit_queue;
+ uint frag_len;
+ struct adapter *adapter;
+ u8 vcs_setting;
+ u8 vcs;
+ u8 vcs_type;
+ u64 tx_bytes;
+ u64 tx_pkts;
+ u64 tx_drop;
+ u64 last_tx_bytes;
+ u64 last_tx_pkts;
+ struct hw_xmit *hwxmits;
+ u8 hwxmit_entry;
+ u8 wmm_para_seq[4];/* sequence for wmm ac parameter strength
+ * from large to small. it's value is 0->vo,
+ * 1->vi, 2->be, 3->bk. */
+ struct semaphore tx_retevt;/* all tx return event; */
+ u8 txirp_cnt;/* */
+ struct tasklet_struct xmit_tasklet;
+ /* per AC pending irp */
+ int beq_cnt;
+ int bkq_cnt;
+ int viq_cnt;
+ int voq_cnt;
+ struct __queue free_xmitbuf_queue;
+ struct __queue pending_xmitbuf_queue;
+ u8 *pallocated_xmitbuf;
+ u8 *pxmitbuf;
+ uint free_xmitbuf_cnt;
+ struct __queue free_xmit_extbuf_queue;
+ u8 *pallocated_xmit_extbuf;
+ u8 *pxmit_extbuf;
+ uint free_xmit_extbuf_cnt;
+ u16 nqos_ssn;
+ int ack_tx;
+ struct mutex ack_tx_mutex;
+ struct submit_ctx ack_tx_ops;
+};
+
+struct xmit_buf *rtw_alloc_xmitbuf_ext(struct xmit_priv *pxmitpriv);
+s32 rtw_free_xmitbuf_ext(struct xmit_priv *pxmitpriv,
+ struct xmit_buf *pxmitbuf);
+struct xmit_buf *rtw_alloc_xmitbuf(struct xmit_priv *pxmitpriv);
+s32 rtw_free_xmitbuf(struct xmit_priv *pxmitpriv,
+ struct xmit_buf *pxmitbuf);
+void rtw_count_tx_stats(struct adapter *padapter,
+ struct xmit_frame *pxmitframe, int sz);
+void rtw_update_protection(struct adapter *padapter, u8 *ie, uint ie_len);
+s32 rtw_make_wlanhdr(struct adapter *padapter, u8 *hdr,
+ struct pkt_attrib *pattrib);
+s32 rtw_put_snap(u8 *data, u16 h_proto);
+
+struct xmit_frame *rtw_alloc_xmitframe(struct xmit_priv *pxmitpriv);
+s32 rtw_free_xmitframe(struct xmit_priv *pxmitpriv,
+ struct xmit_frame *pxmitframe);
+void rtw_free_xmitframe_queue(struct xmit_priv *pxmitpriv,
+ struct __queue *pframequeue);
+struct tx_servq *rtw_get_sta_pending(struct adapter *padapter,
+ struct sta_info *psta, int up, u8 *ac);
+s32 rtw_xmitframe_enqueue(struct adapter *padapter,
+ struct xmit_frame *pxmitframe);
+struct xmit_frame *rtw_dequeue_xframe(struct xmit_priv *pxmitpriv,
+ struct hw_xmit *phwxmit_i, int entry);
+
+s32 rtw_xmit_classifier(struct adapter *padapter,
+ struct xmit_frame *pxmitframe);
+u32 rtw_calculate_wlan_pkt_size_by_attribue(struct pkt_attrib *pattrib);
+#define rtw_wlan_pkt_size(f) rtw_calculate_wlan_pkt_size_by_attribue(&f->attrib)
+s32 rtw_xmitframe_coalesce(struct adapter *padapter, struct sk_buff *pkt,
+ struct xmit_frame *pxmitframe);
+s32 _rtw_init_hw_txqueue(struct hw_txqueue *phw_txqueue, u8 ac_tag);
+void _rtw_init_sta_xmit_priv(struct sta_xmit_priv *psta_xmitpriv);
+s32 rtw_txframes_pending(struct adapter *padapter);
+s32 rtw_txframes_sta_ac_pending(struct adapter *padapter,
+ struct pkt_attrib *pattrib);
+void rtw_init_hwxmits(struct hw_xmit *phwxmit, int entry);
+s32 _rtw_init_xmit_priv(struct xmit_priv *pxmitpriv, struct adapter *padapter);
+void _rtw_free_xmit_priv(struct xmit_priv *pxmitpriv);
+void rtw_alloc_hwxmits(struct adapter *padapter);
+void rtw_free_hwxmits(struct adapter *padapter);
+s32 rtw_xmit(struct adapter *padapter, struct sk_buff **pkt);
+
+#if defined(CONFIG_88EU_AP_MODE)
+int xmitframe_enqueue_for_sleeping_sta(struct adapter *padapter, struct xmit_frame *pxmitframe);
+void stop_sta_xmit(struct adapter *padapter, struct sta_info *psta);
+void wakeup_sta_to_xmit(struct adapter *padapter, struct sta_info *psta);
+void xmit_delivery_enabled_frames(struct adapter *padapter, struct sta_info *psta);
+#endif
+
+u8 qos_acm(u8 acm_mask, u8 priority);
+u32 rtw_get_ff_hwaddr(struct xmit_frame *pxmitframe);
+int rtw_ack_tx_wait(struct xmit_priv *pxmitpriv, u32 timeout_ms);
+void rtw_ack_tx_done(struct xmit_priv *pxmitpriv, int status);
+
+/* include after declaring struct xmit_buf, in order to avoid warning */
+#include <xmit_osdep.h>
+
+#endif /* _RTL871X_XMIT_H_ */
diff --git a/drivers/staging/rtl8188eu/include/sta_info.h b/drivers/staging/rtl8188eu/include/sta_info.h
new file mode 100644
index 000000000..961249053
--- /dev/null
+++ b/drivers/staging/rtl8188eu/include/sta_info.h
@@ -0,0 +1,369 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2011 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ *
+ ******************************************************************************/
+#ifndef __STA_INFO_H_
+#define __STA_INFO_H_
+
+#include <osdep_service.h>
+#include <drv_types.h>
+#include <wifi.h>
+
+#define IBSS_START_MAC_ID 2
+#define NUM_STA 32
+#define NUM_ACL 16
+
+/* if mode ==0, then the sta is allowed once the addr is hit. */
+/* if mode ==1, then the sta is rejected once the addr is non-hit. */
+struct rtw_wlan_acl_node {
+ struct list_head list;
+ u8 addr[ETH_ALEN];
+ u8 valid;
+};
+
+/* mode=0, disable */
+/* mode=1, accept unless in deny list */
+/* mode=2, deny unless in accept list */
+struct wlan_acl_pool {
+ int mode;
+ int num;
+ struct rtw_wlan_acl_node aclnode[NUM_ACL];
+ struct __queue acl_node_q;
+};
+
+struct rssi_sta {
+ s32 UndecoratedSmoothedPWDB;
+ s32 UndecoratedSmoothedCCK;
+ s32 UndecoratedSmoothedOFDM;
+ u64 PacketMap;
+ u8 ValidBit;
+};
+
+struct stainfo_stats {
+ u64 rx_mgnt_pkts;
+ u64 rx_beacon_pkts;
+ u64 rx_probereq_pkts;
+ u64 rx_probersp_pkts;
+ u64 rx_probersp_bm_pkts;
+ u64 rx_probersp_uo_pkts;
+ u64 rx_ctrl_pkts;
+ u64 rx_data_pkts;
+
+ u64 last_rx_mgnt_pkts;
+ u64 last_rx_beacon_pkts;
+ u64 last_rx_probereq_pkts;
+ u64 last_rx_probersp_pkts;
+ u64 last_rx_probersp_bm_pkts;
+ u64 last_rx_probersp_uo_pkts;
+ u64 last_rx_ctrl_pkts;
+ u64 last_rx_data_pkts;
+ u64 rx_bytes;
+ u64 rx_drops;
+ u64 tx_pkts;
+ u64 tx_bytes;
+ u64 tx_drops;
+};
+
+struct sta_info {
+ spinlock_t lock;
+ struct list_head list; /* free_sta_queue */
+ struct list_head hash_list; /* sta_hash */
+
+ struct sta_xmit_priv sta_xmitpriv;
+ struct sta_recv_priv sta_recvpriv;
+
+ struct __queue sleep_q;
+ unsigned int sleepq_len;
+
+ uint state;
+ uint aid;
+ uint mac_id;
+ uint qos_option;
+ u8 hwaddr[ETH_ALEN];
+
+ uint ieee8021x_blocked; /* 0: allowed, 1:blocked */
+ uint dot118021XPrivacy; /* aes, tkip... */
+ union Keytype dot11tkiptxmickey;
+ union Keytype dot11tkiprxmickey;
+ union Keytype dot118021x_UncstKey;
+ union pn48 dot11txpn; /* PN48 used for Unicast xmit. */
+ union pn48 dot11rxpn; /* PN48 used for Unicast recv. */
+ u8 bssrateset[16];
+ u32 bssratelen;
+ s32 rssi;
+ s32 signal_quality;
+
+ u8 cts2self;
+ u8 rtsen;
+
+ u8 raid;
+ u8 init_rate;
+ u32 ra_mask;
+ u8 wireless_mode; /* NETWORK_TYPE */
+ struct stainfo_stats sta_stats;
+
+ /* for A-MPDU TX, ADDBA timeout check */
+ struct timer_list addba_retry_timer;
+
+ /* for A-MPDU Rx reordering buffer control */
+ struct recv_reorder_ctrl recvreorder_ctrl[16];
+
+ /* for A-MPDU Tx */
+ /* unsigned char ampdu_txen_bitmap; */
+ u16 BA_starting_seqctrl[16];
+
+ struct ht_priv htpriv;
+
+ /* Notes: */
+ /* STA_Mode: */
+ /* curr_network(mlme_priv/security_priv/qos/ht) +
+ * sta_info: (STA & AP) CAP/INFO */
+ /* scan_q: AP CAP/INFO */
+
+ /* AP_Mode: */
+ /* curr_network(mlme_priv/security_priv/qos/ht) : AP CAP/INFO */
+ /* sta_info: (AP & STA) CAP/INFO */
+
+ struct list_head asoc_list;
+#ifdef CONFIG_88EU_AP_MODE
+ struct list_head auth_list;
+
+ unsigned int expire_to;
+ unsigned int auth_seq;
+ unsigned int authalg;
+ unsigned char chg_txt[128];
+
+ u16 capability;
+ int flags;
+
+ int dot8021xalg;/* 0:disable, 1:psk, 2:802.1x */
+ int wpa_psk;/* 0:disable, bit(0): WPA, bit(1):WPA2 */
+ int wpa_group_cipher;
+ int wpa2_group_cipher;
+ int wpa_pairwise_cipher;
+ int wpa2_pairwise_cipher;
+
+ u8 bpairwise_key_installed;
+
+ u8 wpa_ie[32];
+
+ u8 nonerp_set;
+ u8 no_short_slot_time_set;
+ u8 no_short_preamble_set;
+ u8 no_ht_gf_set;
+ u8 no_ht_set;
+ u8 ht_20mhz_set;
+
+ unsigned int tx_ra_bitmap;
+ u8 qos_info;
+
+ u8 max_sp_len;
+ u8 uapsd_bk;/* BIT(0): Delivery enabled, BIT(1): Trigger enabled */
+ u8 uapsd_be;
+ u8 uapsd_vi;
+ u8 uapsd_vo;
+
+ u8 has_legacy_ac;
+ unsigned int sleepq_ac_len;
+#endif /* CONFIG_88EU_AP_MODE */
+
+ u8 under_exist_checking;
+ u8 keep_alive_trycnt;
+
+ /* for DM */
+ struct rssi_sta rssi_stat;
+
+ /* ================ODM Relative Info======================= */
+ /* Please be careful, don't declare too much structure here.
+ * It will cost memory * STA support num. */
+ /* 2011/10/20 MH Add for ODM STA info. */
+ /* Driver Write */
+ u8 bValid; /* record the sta status link or not? */
+ u8 IOTPeer; /* Enum value. HT_IOT_PEER_E */
+ u8 rssi_level; /* for Refresh RA mask */
+ /* ODM Write */
+ /* 1 PHY_STATUS_INFO */
+ u8 RSSI_Path[4]; /* */
+ u8 RSSI_Ave;
+ u8 RXEVM[4];
+ u8 RXSNR[4];
+
+ /* ================ODM Relative Info======================= */
+ /* */
+
+ /* To store the sequence number of received management frame */
+ u16 RxMgmtFrameSeqNum;
+};
+
+#define sta_rx_pkts(sta) \
+ (sta->sta_stats.rx_mgnt_pkts \
+ + sta->sta_stats.rx_ctrl_pkts \
+ + sta->sta_stats.rx_data_pkts)
+
+#define sta_last_rx_pkts(sta) \
+ (sta->sta_stats.last_rx_mgnt_pkts \
+ + sta->sta_stats.last_rx_ctrl_pkts \
+ + sta->sta_stats.last_rx_data_pkts)
+
+#define sta_rx_data_pkts(sta) \
+ (sta->sta_stats.rx_data_pkts)
+
+#define sta_last_rx_data_pkts(sta) \
+ (sta->sta_stats.last_rx_data_pkts)
+
+#define sta_rx_mgnt_pkts(sta) \
+ (sta->sta_stats.rx_mgnt_pkts)
+
+#define sta_last_rx_mgnt_pkts(sta) \
+ (sta->sta_stats.last_rx_mgnt_pkts)
+
+#define sta_rx_beacon_pkts(sta) \
+ (sta->sta_stats.rx_beacon_pkts)
+
+#define sta_last_rx_beacon_pkts(sta) \
+ (sta->sta_stats.last_rx_beacon_pkts)
+
+#define sta_rx_probereq_pkts(sta) \
+ (sta->sta_stats.rx_probereq_pkts)
+
+#define sta_last_rx_probereq_pkts(sta) \
+ (sta->sta_stats.last_rx_probereq_pkts)
+
+#define sta_rx_probersp_pkts(sta) \
+ (sta->sta_stats.rx_probersp_pkts)
+
+#define sta_last_rx_probersp_pkts(sta) \
+ (sta->sta_stats.last_rx_probersp_pkts)
+
+#define sta_rx_probersp_bm_pkts(sta) \
+ (sta->sta_stats.rx_probersp_bm_pkts)
+
+#define sta_last_rx_probersp_bm_pkts(sta) \
+ (sta->sta_stats.last_rx_probersp_bm_pkts)
+
+#define sta_rx_probersp_uo_pkts(sta) \
+ (sta->sta_stats.rx_probersp_uo_pkts)
+
+#define sta_last_rx_probersp_uo_pkts(sta) \
+ (sta->sta_stats.last_rx_probersp_uo_pkts)
+
+#define sta_update_last_rx_pkts(sta) \
+do { \
+ sta->sta_stats.last_rx_mgnt_pkts = sta->sta_stats.rx_mgnt_pkts; \
+ sta->sta_stats.last_rx_beacon_pkts = sta->sta_stats.rx_beacon_pkts; \
+ sta->sta_stats.last_rx_probereq_pkts = sta->sta_stats.rx_probereq_pkts; \
+ sta->sta_stats.last_rx_probersp_pkts = sta->sta_stats.rx_probersp_pkts; \
+ sta->sta_stats.last_rx_probersp_bm_pkts = sta->sta_stats.rx_probersp_bm_pkts; \
+ sta->sta_stats.last_rx_probersp_uo_pkts = sta->sta_stats.rx_probersp_uo_pkts; \
+ sta->sta_stats.last_rx_ctrl_pkts = sta->sta_stats.rx_ctrl_pkts; \
+ sta->sta_stats.last_rx_data_pkts = sta->sta_stats.rx_data_pkts; \
+} while (0)
+
+#define STA_RX_PKTS_ARG(sta) \
+ sta->sta_stats.rx_mgnt_pkts \
+ , sta->sta_stats.rx_ctrl_pkts \
+ , sta->sta_stats.rx_data_pkts
+
+#define STA_LAST_RX_PKTS_ARG(sta) \
+ sta->sta_stats.last_rx_mgnt_pkts \
+ , sta->sta_stats.last_rx_ctrl_pkts \
+ , sta->sta_stats.last_rx_data_pkts
+
+#define STA_RX_PKTS_DIFF_ARG(sta) \
+ sta->sta_stats.rx_mgnt_pkts - sta->sta_stats.last_rx_mgnt_pkts \
+ , sta->sta_stats.rx_ctrl_pkts - sta->sta_stats.last_rx_ctrl_pkts \
+ , sta->sta_stats.rx_data_pkts - sta->sta_stats.last_rx_data_pkts
+
+#define STA_PKTS_FMT "(m:%llu, c:%llu, d:%llu)"
+
+struct sta_priv {
+ u8 *pallocated_stainfo_buf;
+ u8 *pstainfo_buf;
+ struct __queue free_sta_queue;
+
+ spinlock_t sta_hash_lock;
+ struct list_head sta_hash[NUM_STA];
+ int asoc_sta_count;
+ struct __queue sleep_q;
+ struct __queue wakeup_q;
+
+ struct adapter *padapter;
+
+ spinlock_t asoc_list_lock;
+ struct list_head asoc_list;
+
+#ifdef CONFIG_88EU_AP_MODE
+ struct list_head auth_list;
+ spinlock_t auth_list_lock;
+ u8 asoc_list_cnt;
+ u8 auth_list_cnt;
+
+ unsigned int auth_to; /* sec, time to expire in authenticating. */
+ unsigned int assoc_to; /* sec, time to expire before associating. */
+ unsigned int expire_to; /* sec , time to expire after associated. */
+
+ /* pointers to STA info; based on allocated AID or NULL if AID free
+ * AID is in the range 1-2007, so sta_aid[0] corresponders to AID 1
+ * and so on
+ */
+ struct sta_info *sta_aid[NUM_STA];
+
+ u16 sta_dz_bitmap;/* only support 15 stations, station aid bitmap
+ * for sleeping sta. */
+ u16 tim_bitmap; /* only support 15 stations, aid=0~15 mapping
+ * bit0~bit15 */
+
+ u16 max_num_sta;
+
+ struct wlan_acl_pool acl_list;
+#endif
+
+};
+
+static inline u32 wifi_mac_hash(u8 *mac)
+{
+ u32 x;
+
+ x = mac[0];
+ x = (x << 2) ^ mac[1];
+ x = (x << 2) ^ mac[2];
+ x = (x << 2) ^ mac[3];
+ x = (x << 2) ^ mac[4];
+ x = (x << 2) ^ mac[5];
+
+ x ^= x >> 8;
+ x = x & (NUM_STA - 1);
+ return x;
+}
+
+extern u32 _rtw_init_sta_priv(struct sta_priv *pstapriv);
+extern u32 _rtw_free_sta_priv(struct sta_priv *pstapriv);
+
+#define stainfo_offset_valid(offset) (offset < NUM_STA && offset >= 0)
+int rtw_stainfo_offset(struct sta_priv *stapriv, struct sta_info *sta);
+struct sta_info *rtw_get_stainfo_by_offset(struct sta_priv *stapriv, int off);
+
+extern struct sta_info *rtw_alloc_stainfo(struct sta_priv *stapriv, u8 *hwaddr);
+extern u32 rtw_free_stainfo(struct adapter *adapt, struct sta_info *psta);
+extern void rtw_free_all_stainfo(struct adapter *adapt);
+extern struct sta_info *rtw_get_stainfo(struct sta_priv *stapriv, u8 *hwaddr);
+extern u32 rtw_init_bcmc_stainfo(struct adapter *adapt);
+extern struct sta_info *rtw_get_bcmc_stainfo(struct adapter *padapter);
+extern u8 rtw_access_ctrl(struct adapter *padapter, u8 *mac_addr);
+
+#endif /* _STA_INFO_H_ */
diff --git a/drivers/staging/rtl8188eu/include/usb_hal.h b/drivers/staging/rtl8188eu/include/usb_hal.h
new file mode 100644
index 000000000..8a65995d5
--- /dev/null
+++ b/drivers/staging/rtl8188eu/include/usb_hal.h
@@ -0,0 +1,26 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2011 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ *
+ ******************************************************************************/
+#ifndef __USB_HAL_H__
+#define __USB_HAL_H__
+
+void rtl8188eu_set_hal_ops(struct adapter *padapter);
+#define hal_set_hal_ops rtl8188eu_set_hal_ops
+
+#endif /* __USB_HAL_H__ */
diff --git a/drivers/staging/rtl8188eu/include/usb_ops_linux.h b/drivers/staging/rtl8188eu/include/usb_ops_linux.h
new file mode 100644
index 000000000..4fdc536cb
--- /dev/null
+++ b/drivers/staging/rtl8188eu/include/usb_ops_linux.h
@@ -0,0 +1,86 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2011 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ *
+ ******************************************************************************/
+#ifndef __USB_OPS_LINUX_H__
+#define __USB_OPS_LINUX_H__
+
+#define VENDOR_CMD_MAX_DATA_LEN 254
+
+#define RTW_USB_CONTROL_MSG_TIMEOUT_TEST 10/* ms */
+#define RTW_USB_CONTROL_MSG_TIMEOUT 500/* ms */
+
+#define MAX_USBCTRL_VENDORREQ_TIMES 10
+
+#define RTW_USB_BULKOUT_TIME 5000/* ms */
+
+#define REALTEK_USB_VENQT_READ 0xC0
+#define REALTEK_USB_VENQT_WRITE 0x40
+
+#define ALIGNMENT_UNIT 16
+#define MAX_VENDOR_REQ_CMD_SIZE 254 /* 8188cu SIE Support */
+#define MAX_USB_IO_CTL_SIZE (MAX_VENDOR_REQ_CMD_SIZE + ALIGNMENT_UNIT)
+
+#define USB_HIGH_SPEED_BULK_SIZE 512
+#define USB_FULL_SPEED_BULK_SIZE 64
+
+#define _usbctrl_vendorreq_async_callback(urb, regs) \
+ _usbctrl_vendorreq_async_callback(urb)
+#define usb_bulkout_zero_complete(purb, regs) \
+ usb_bulkout_zero_complete(purb)
+#define usb_write_mem_complete(purb, regs) \
+ usb_write_mem_complete(purb)
+#define usb_write_port_complete(purb, regs) \
+ usb_write_port_complete(purb)
+#define usb_read_port_complete(purb, regs) \
+ usb_read_port_complete(purb)
+#define usb_read_interrupt_complete(purb, regs) \
+ usb_read_interrupt_complete(purb)
+
+static inline u8 rtw_usb_bulk_size_boundary(struct adapter *padapter,
+ int buf_len)
+{
+ u8 rst = true;
+ struct dvobj_priv *pdvobjpriv = adapter_to_dvobj(padapter);
+
+ if (pdvobjpriv->ishighspeed)
+ rst = (0 == (buf_len) % USB_HIGH_SPEED_BULK_SIZE) ?
+ true : false;
+ else
+ rst = (0 == (buf_len) % USB_FULL_SPEED_BULK_SIZE) ?
+ true : false;
+ return rst;
+}
+
+unsigned int ffaddr2pipehdl(struct dvobj_priv *pdvobj, u32 addr);
+
+u8 usb_read8(struct adapter *adapter, u32 addr);
+u16 usb_read16(struct adapter *adapter, u32 addr);
+u32 usb_read32(struct adapter *adapter, u32 addr);
+
+u32 usb_read_port(struct adapter *adapter, u32 addr, u32 cnt, u8 *pmem);
+void usb_read_port_cancel(struct adapter *adapter);
+
+int usb_write8(struct adapter *adapter, u32 addr, u8 val);
+int usb_write16(struct adapter *adapter, u32 addr, u16 val);
+int usb_write32(struct adapter *adapter, u32 addr, u32 val);
+
+u32 usb_write_port(struct adapter *adapter, u32 addr, u32 cnt, u8 *pmem);
+void usb_write_port_cancel(struct adapter *adapter);
+
+#endif
diff --git a/drivers/staging/rtl8188eu/include/wifi.h b/drivers/staging/rtl8188eu/include/wifi.h
new file mode 100644
index 000000000..a89275e0e
--- /dev/null
+++ b/drivers/staging/rtl8188eu/include/wifi.h
@@ -0,0 +1,1099 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2012 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ *
+ ******************************************************************************/
+#ifndef _WIFI_H_
+#define _WIFI_H_
+
+
+#ifdef BIT
+/* error "BIT define occurred earlier elsewhere!\n" */
+#undef BIT
+#endif
+#define BIT(x) (1 << (x))
+
+
+#define WLAN_ETHHDR_LEN 14
+#define WLAN_ETHADDR_LEN 6
+#define WLAN_IEEE_OUI_LEN 3
+#define WLAN_ADDR_LEN 6
+#define WLAN_CRC_LEN 4
+#define WLAN_BSSID_LEN 6
+#define WLAN_BSS_TS_LEN 8
+#define WLAN_HDR_A3_LEN 24
+#define WLAN_HDR_A4_LEN 30
+#define WLAN_HDR_A3_QOS_LEN 26
+#define WLAN_HDR_A4_QOS_LEN 32
+#define WLAN_SSID_MAXLEN 32
+#define WLAN_DATA_MAXLEN 2312
+
+#define WLAN_A3_PN_OFFSET 24
+#define WLAN_A4_PN_OFFSET 30
+
+#define WLAN_MIN_ETHFRM_LEN 60
+#define WLAN_MAX_ETHFRM_LEN 1514
+#define WLAN_ETHHDR_LEN 14
+
+#define P80211CAPTURE_VERSION 0x80211001
+
+/* This value is tested by WiFi 11n Test Plan 5.2.3. */
+/* This test verifies the WLAN NIC can update the NAV through sending
+ * the CTS with large duration. */
+#define WiFiNavUpperUs 30000 /* 30 ms */
+
+enum WIFI_FRAME_TYPE {
+ WIFI_MGT_TYPE = (0),
+ WIFI_CTRL_TYPE = (BIT(2)),
+ WIFI_DATA_TYPE = (BIT(3)),
+ WIFI_QOS_DATA_TYPE = (BIT(7)|BIT(3)), /* QoS Data */
+};
+
+enum WIFI_FRAME_SUBTYPE {
+ /* below is for mgt frame */
+ WIFI_ASSOCREQ = (0 | WIFI_MGT_TYPE),
+ WIFI_ASSOCRSP = (BIT(4) | WIFI_MGT_TYPE),
+ WIFI_REASSOCREQ = (BIT(5) | WIFI_MGT_TYPE),
+ WIFI_REASSOCRSP = (BIT(5) | BIT(4) | WIFI_MGT_TYPE),
+ WIFI_PROBEREQ = (BIT(6) | WIFI_MGT_TYPE),
+ WIFI_PROBERSP = (BIT(6) | BIT(4) | WIFI_MGT_TYPE),
+ WIFI_BEACON = (BIT(7) | WIFI_MGT_TYPE),
+ WIFI_ATIM = (BIT(7) | BIT(4) | WIFI_MGT_TYPE),
+ WIFI_DISASSOC = (BIT(7) | BIT(5) | WIFI_MGT_TYPE),
+ WIFI_AUTH = (BIT(7) | BIT(5) | BIT(4) | WIFI_MGT_TYPE),
+ WIFI_DEAUTH = (BIT(7) | BIT(6) | WIFI_MGT_TYPE),
+ WIFI_ACTION = (BIT(7) | BIT(6) | BIT(4) | WIFI_MGT_TYPE),
+
+ /* below is for control frame */
+ WIFI_PSPOLL = (BIT(7) | BIT(5) | WIFI_CTRL_TYPE),
+ WIFI_RTS = (BIT(7) | BIT(5) | BIT(4) | WIFI_CTRL_TYPE),
+ WIFI_CTS = (BIT(7) | BIT(6) | WIFI_CTRL_TYPE),
+ WIFI_ACK = (BIT(7) | BIT(6) | BIT(4) | WIFI_CTRL_TYPE),
+ WIFI_CFEND = (BIT(7) | BIT(6) | BIT(5) | WIFI_CTRL_TYPE),
+ WIFI_CFEND_CFACK = (BIT(7) | BIT(6) | BIT(5) | BIT(4) |
+ WIFI_CTRL_TYPE),
+
+ /* below is for data frame */
+ WIFI_DATA = (0 | WIFI_DATA_TYPE),
+ WIFI_DATA_CFACK = (BIT(4) | WIFI_DATA_TYPE),
+ WIFI_DATA_CFPOLL = (BIT(5) | WIFI_DATA_TYPE),
+ WIFI_DATA_CFACKPOLL = (BIT(5) | BIT(4) | WIFI_DATA_TYPE),
+ WIFI_DATA_NULL = (BIT(6) | WIFI_DATA_TYPE),
+ WIFI_CF_ACK = (BIT(6) | BIT(4) | WIFI_DATA_TYPE),
+ WIFI_CF_POLL = (BIT(6) | BIT(5) | WIFI_DATA_TYPE),
+ WIFI_CF_ACKPOLL = (BIT(6) | BIT(5) | BIT(4) | WIFI_DATA_TYPE),
+ WIFI_QOS_DATA_NULL = (BIT(6) | WIFI_QOS_DATA_TYPE),
+};
+
+enum WIFI_REASON_CODE {
+ _RSON_RESERVED_ = 0,
+ _RSON_UNSPECIFIED_ = 1,
+ _RSON_AUTH_NO_LONGER_VALID_ = 2,
+ _RSON_DEAUTH_STA_LEAVING_ = 3,
+ _RSON_INACTIVITY_ = 4,
+ _RSON_UNABLE_HANDLE_ = 5,
+ _RSON_CLS2_ = 6,
+ _RSON_CLS3_ = 7,
+ _RSON_DISAOC_STA_LEAVING_ = 8,
+ _RSON_ASOC_NOT_AUTH_ = 9,
+
+ /* WPA reason */
+ _RSON_INVALID_IE_ = 13,
+ _RSON_MIC_FAILURE_ = 14,
+ _RSON_4WAY_HNDSHK_TIMEOUT_ = 15,
+ _RSON_GROUP_KEY_UPDATE_TIMEOUT_ = 16,
+ _RSON_DIFF_IE_ = 17,
+ _RSON_MLTCST_CIPHER_NOT_VALID_ = 18,
+ _RSON_UNICST_CIPHER_NOT_VALID_ = 19,
+ _RSON_AKMP_NOT_VALID_ = 20,
+ _RSON_UNSUPPORT_RSNE_VER_ = 21,
+ _RSON_INVALID_RSNE_CAP_ = 22,
+ _RSON_IEEE_802DOT1X_AUTH_FAIL_ = 23,
+
+ /* belowing are Realtek definition */
+ _RSON_PMK_NOT_AVAILABLE_ = 24,
+ _RSON_TDLS_TEAR_TOOFAR_ = 25,
+ _RSON_TDLS_TEAR_UN_RSN_ = 26,
+};
+
+/* Reason codes (IEEE 802.11-2007, 7.3.1.7, Table 7-22)
+
+#define WLAN_REASON_UNSPECIFIED 1
+#define WLAN_REASON_PREV_AUTH_NOT_VALID 2
+#define WLAN_REASON_DEAUTH_LEAVING 3
+#define WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY 4
+#define WLAN_REASON_DISASSOC_AP_BUSY 5
+#define WLAN_REASON_CLASS2_FRAME_FROM_NONAUTH_STA 6
+#define WLAN_REASON_CLASS3_FRAME_FROM_NONASSOC_STA 7
+#define WLAN_REASON_DISASSOC_STA_HAS_LEFT 8
+#define WLAN_REASON_STA_REQ_ASSOC_WITHOUT_AUTH 9 */
+/* IEEE 802.11h */
+#define WLAN_REASON_PWR_CAPABILITY_NOT_VALID 10
+#define WLAN_REASON_SUPPORTED_CHANNEL_NOT_VALID 11
+
+/* IEEE 802.11i
+#define WLAN_REASON_INVALID_IE 13
+#define WLAN_REASON_MICHAEL_MIC_FAILURE 14
+#define WLAN_REASON_4WAY_HANDSHAKE_TIMEOUT 15
+#define WLAN_REASON_GROUP_KEY_UPDATE_TIMEOUT 16
+#define WLAN_REASON_IE_IN_4WAY_DIFFERS 17
+#define WLAN_REASON_GROUP_CIPHER_NOT_VALID 18
+#define WLAN_REASON_PAIRWISE_CIPHER_NOT_VALID 19
+#define WLAN_REASON_AKMP_NOT_VALID 20
+#define WLAN_REASON_UNSUPPORTED_RSN_IE_VERSION 21
+#define WLAN_REASON_INVALID_RSN_IE_CAPAB 22
+#define WLAN_REASON_IEEE_802_1X_AUTH_FAILED 23
+#define WLAN_REASON_CIPHER_SUITE_REJECTED 24 */
+
+enum WIFI_STATUS_CODE {
+ _STATS_SUCCESSFUL_ = 0,
+ _STATS_FAILURE_ = 1,
+ _STATS_CAP_FAIL_ = 10,
+ _STATS_NO_ASOC_ = 11,
+ _STATS_OTHER_ = 12,
+ _STATS_NO_SUPP_ALG_ = 13,
+ _STATS_OUT_OF_AUTH_SEQ_ = 14,
+ _STATS_CHALLENGE_FAIL_ = 15,
+ _STATS_AUTH_TIMEOUT_ = 16,
+ _STATS_UNABLE_HANDLE_STA_ = 17,
+ _STATS_RATE_FAIL_ = 18,
+};
+
+/* Status codes (IEEE 802.11-2007, 7.3.1.9, Table 7-23)
+#define WLAN_STATUS_SUCCESS 0
+#define WLAN_STATUS_UNSPECIFIED_FAILURE 1
+#define WLAN_STATUS_CAPS_UNSUPPORTED 10
+#define WLAN_STATUS_REASSOC_NO_ASSOC 11
+#define WLAN_STATUS_ASSOC_DENIED_UNSPEC 12
+#define WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG 13
+#define WLAN_STATUS_UNKNOWN_AUTH_TRANSACTION 14
+#define WLAN_STATUS_CHALLENGE_FAIL 15
+#define WLAN_STATUS_AUTH_TIMEOUT 16
+#define WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA 17
+#define WLAN_STATUS_ASSOC_DENIED_RATES 18 */
+
+/* entended */
+/* IEEE 802.11b */
+#define WLAN_STATUS_ASSOC_DENIED_NOSHORT 19
+#define WLAN_STATUS_ASSOC_DENIED_NOPBCC 20
+#define WLAN_STATUS_ASSOC_DENIED_NOAGILITY 21
+/* IEEE 802.11h */
+#define WLAN_STATUS_SPEC_MGMT_REQUIRED 22
+#define WLAN_STATUS_PWR_CAPABILITY_NOT_VALID 23
+#define WLAN_STATUS_SUPPORTED_CHANNEL_NOT_VALID 24
+/* IEEE 802.11g */
+#define WLAN_STATUS_ASSOC_DENIED_NO_SHORT_SLOT_TIME 25
+#define WLAN_STATUS_ASSOC_DENIED_NO_ER_PBCC 26
+#define WLAN_STATUS_ASSOC_DENIED_NO_DSSS_OFDM 27
+/* IEEE 802.11w */
+#define WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY 30
+#define WLAN_STATUS_ROBUST_MGMT_FRAME_POLICY_VIOLATION 31
+/* IEEE 802.11i */
+#define WLAN_STATUS_INVALID_IE 40
+#define WLAN_STATUS_GROUP_CIPHER_NOT_VALID 41
+#define WLAN_STATUS_PAIRWISE_CIPHER_NOT_VALID 42
+#define WLAN_STATUS_AKMP_NOT_VALID 43
+#define WLAN_STATUS_UNSUPPORTED_RSN_IE_VERSION 44
+#define WLAN_STATUS_INVALID_RSN_IE_CAPAB 45
+#define WLAN_STATUS_CIPHER_REJECTED_PER_POLICY 46
+#define WLAN_STATUS_TS_NOT_CREATED 47
+#define WLAN_STATUS_DIRECT_LINK_NOT_ALLOWED 48
+#define WLAN_STATUS_DEST_STA_NOT_PRESENT 49
+#define WLAN_STATUS_DEST_STA_NOT_QOS_STA 50
+#define WLAN_STATUS_ASSOC_DENIED_LISTEN_INT_TOO_LARGE 51
+/* IEEE 802.11r */
+#define WLAN_STATUS_INVALID_FT_ACTION_FRAME_COUNT 52
+#define WLAN_STATUS_INVALID_PMKID 53
+#define WLAN_STATUS_INVALID_MDIE 54
+#define WLAN_STATUS_INVALID_FTIE 55
+
+enum WIFI_REG_DOMAIN {
+ DOMAIN_FCC = 1,
+ DOMAIN_IC = 2,
+ DOMAIN_ETSI = 3,
+ DOMAIN_SPA = 4,
+ DOMAIN_FRANCE = 5,
+ DOMAIN_MKK = 6,
+ DOMAIN_ISRAEL = 7,
+ DOMAIN_MKK1 = 8,
+ DOMAIN_MKK2 = 9,
+ DOMAIN_MKK3 = 10,
+ DOMAIN_MAX
+};
+
+#define _TO_DS_ BIT(8)
+#define _FROM_DS_ BIT(9)
+#define _MORE_FRAG_ BIT(10)
+#define _RETRY_ BIT(11)
+#define _PWRMGT_ BIT(12)
+#define _MORE_DATA_ BIT(13)
+#define _PRIVACY_ BIT(14)
+#define _ORDER_ BIT(15)
+
+#define SetToDs(pbuf) \
+ *(__le16 *)(pbuf) |= cpu_to_le16(_TO_DS_)
+
+#define GetToDs(pbuf) (((*(__le16 *)(pbuf)) & cpu_to_le16(_TO_DS_)) != 0)
+
+#define ClearToDs(pbuf) \
+ *(__le16 *)(pbuf) &= (~cpu_to_le16(_TO_DS_))
+
+#define SetFrDs(pbuf) \
+ *(__le16 *)(pbuf) |= cpu_to_le16(_FROM_DS_)
+
+#define GetFrDs(pbuf) (((*(__le16 *)(pbuf)) & cpu_to_le16(_FROM_DS_)) != 0)
+
+#define ClearFrDs(pbuf) \
+ *(__le16 *)(pbuf) &= (~cpu_to_le16(_FROM_DS_))
+
+#define get_tofr_ds(pframe) ((GetToDs(pframe) << 1) | GetFrDs(pframe))
+
+
+#define SetMFrag(pbuf) \
+ *(__le16 *)(pbuf) |= cpu_to_le16(_MORE_FRAG_)
+
+#define GetMFrag(pbuf) (((*(__le16 *)(pbuf)) & cpu_to_le16(_MORE_FRAG_)) != 0)
+
+#define ClearMFrag(pbuf) \
+ *(__le16 *)(pbuf) &= (~cpu_to_le16(_MORE_FRAG_))
+
+#define SetRetry(pbuf) \
+ *(__le16 *)(pbuf) |= cpu_to_le16(_RETRY_)
+
+#define GetRetry(pbuf) (((*(__le16 *)(pbuf)) & cpu_to_le16(_RETRY_)) != 0)
+
+#define ClearRetry(pbuf) \
+ *(__le16 *)(pbuf) &= (~cpu_to_le16(_RETRY_))
+
+#define SetPwrMgt(pbuf) \
+ *(__le16 *)(pbuf) |= cpu_to_le16(_PWRMGT_)
+
+#define GetPwrMgt(pbuf) (((*(__le16 *)(pbuf)) & cpu_to_le16(_PWRMGT_)) != 0)
+
+#define ClearPwrMgt(pbuf) \
+ *(__le16 *)(pbuf) &= (~cpu_to_le16(_PWRMGT_))
+
+#define SetMData(pbuf) \
+ *(__le16 *)(pbuf) |= cpu_to_le16(_MORE_DATA_)
+
+#define GetMData(pbuf) (((*(__le16 *)(pbuf)) & cpu_to_le16(_MORE_DATA_)) != 0)
+
+#define ClearMData(pbuf) \
+ *(__le16 *)(pbuf) &= (~cpu_to_le16(_MORE_DATA_))
+
+#define SetPrivacy(pbuf) \
+ *(__le16 *)(pbuf) |= cpu_to_le16(_PRIVACY_)
+
+#define GetPrivacy(pbuf) \
+ (((*(__le16 *)(pbuf)) & cpu_to_le16(_PRIVACY_)) != 0)
+
+#define GetOrder(pbuf) \
+ (((*(__le16 *)(pbuf)) & cpu_to_le16(_ORDER_)) != 0)
+
+#define GetFrameType(pbuf) \
+ (le16_to_cpu(*(__le16 *)(pbuf)) & (BIT(3) | BIT(2)))
+
+#define GetFrameSubType(pbuf) (le16_to_cpu(*(__le16 *)(pbuf)) & (BIT(7) |\
+ BIT(6) | BIT(5) | BIT(4) | BIT(3) | BIT(2)))
+
+#define SetFrameSubType(pbuf, type) \
+ do { \
+ *(__le16 *)(pbuf) &= cpu_to_le16(~(BIT(7) | BIT(6) | \
+ BIT(5) | BIT(4) | BIT(3) | BIT(2))); \
+ *(__le16 *)(pbuf) |= cpu_to_le16(type); \
+ } while (0)
+
+#define GetSequence(pbuf) \
+ (le16_to_cpu(*(__le16 *)((size_t)(pbuf) + 22)) >> 4)
+
+#define GetFragNum(pbuf) \
+ (le16_to_cpu(*(__le16 *)((size_t)(pbuf) + 22)) & 0x0f)
+
+#define SetSeqNum(pbuf, num) \
+ do { \
+ *(__le16 *)((size_t)(pbuf) + 22) = \
+ ((*(__le16 *)((size_t)(pbuf) + 22)) & cpu_to_le16((unsigned short)0x000f)) | \
+ cpu_to_le16((unsigned short)(0xfff0 & (num << 4))); \
+ } while (0)
+
+#define SetDuration(pbuf, dur) \
+ *(__le16 *)((size_t)(pbuf) + 2) = cpu_to_le16(0xffff & (dur))
+
+
+#define SetPriority(pbuf, tid) \
+ *(__le16 *)(pbuf) |= cpu_to_le16(tid & 0xf)
+
+#define GetPriority(pbuf) ((le16_to_cpu(*(__le16 *)(pbuf))) & 0xf)
+
+#define SetEOSP(pbuf, eosp) \
+ *(__le16 *)(pbuf) |= cpu_to_le16((eosp & 1) << 4)
+
+#define SetAckpolicy(pbuf, ack) \
+ *(__le16 *)(pbuf) |= cpu_to_le16((ack & 3) << 5)
+
+#define GetAckpolicy(pbuf) (((le16_to_cpu(*(__le16 *)pbuf)) >> 5) & 0x3)
+
+#define GetAMsdu(pbuf) (((le16_to_cpu(*(__le16 *)pbuf)) >> 7) & 0x1)
+
+#define GetAid(pbuf) (le16_to_cpu(*(__le16 *)((size_t)(pbuf) + 2)) & 0x3fff)
+
+#define GetAddr1Ptr(pbuf) ((unsigned char *)((size_t)(pbuf) + 4))
+
+#define GetAddr2Ptr(pbuf) ((unsigned char *)((size_t)(pbuf) + 10))
+
+#define GetAddr3Ptr(pbuf) ((unsigned char *)((size_t)(pbuf) + 16))
+
+#define GetAddr4Ptr(pbuf) ((unsigned char *)((size_t)(pbuf) + 24))
+
+#define MacAddr_isBcst(addr) \
+ ( \
+ ((addr[0] == 0xff) && (addr[1] == 0xff) && \
+ (addr[2] == 0xff) && (addr[3] == 0xff) && \
+ (addr[4] == 0xff) && (addr[5] == 0xff)) ? true : false \
+)
+
+static inline int IS_MCAST(unsigned char *da)
+{
+ if ((*da) & 0x01)
+ return true;
+ else
+ return false;
+}
+
+static inline unsigned char *get_da(unsigned char *pframe)
+{
+ unsigned char *da;
+ unsigned int to_fr_ds = (GetToDs(pframe) << 1) | GetFrDs(pframe);
+
+ switch (to_fr_ds) {
+ case 0x00: /* ToDs=0, FromDs=0 */
+ da = GetAddr1Ptr(pframe);
+ break;
+ case 0x01: /* ToDs=0, FromDs=1 */
+ da = GetAddr1Ptr(pframe);
+ break;
+ case 0x02: /* ToDs=1, FromDs=0 */
+ da = GetAddr3Ptr(pframe);
+ break;
+ default: /* ToDs=1, FromDs=1 */
+ da = GetAddr3Ptr(pframe);
+ break;
+ }
+ return da;
+}
+
+static inline unsigned char *get_sa(unsigned char *pframe)
+{
+ unsigned char *sa;
+ unsigned int to_fr_ds = (GetToDs(pframe) << 1) | GetFrDs(pframe);
+
+ switch (to_fr_ds) {
+ case 0x00: /* ToDs=0, FromDs=0 */
+ sa = GetAddr2Ptr(pframe);
+ break;
+ case 0x01: /* ToDs=0, FromDs=1 */
+ sa = GetAddr3Ptr(pframe);
+ break;
+ case 0x02: /* ToDs=1, FromDs=0 */
+ sa = GetAddr2Ptr(pframe);
+ break;
+ default: /* ToDs=1, FromDs=1 */
+ sa = GetAddr4Ptr(pframe);
+ break;
+ }
+ return sa;
+}
+
+static inline unsigned char *get_hdr_bssid(unsigned char *pframe)
+{
+ unsigned char *sa;
+ unsigned int to_fr_ds = (GetToDs(pframe) << 1) | GetFrDs(pframe);
+
+ switch (to_fr_ds) {
+ case 0x00: /* ToDs=0, FromDs=0 */
+ sa = GetAddr3Ptr(pframe);
+ break;
+ case 0x01: /* ToDs=0, FromDs=1 */
+ sa = GetAddr2Ptr(pframe);
+ break;
+ case 0x02: /* ToDs=1, FromDs=0 */
+ sa = GetAddr1Ptr(pframe);
+ break;
+ case 0x03: /* ToDs=1, FromDs=1 */
+ sa = GetAddr1Ptr(pframe);
+ break;
+ default:
+ sa = NULL; /* */
+ break;
+ }
+ return sa;
+}
+
+static inline int IsFrameTypeCtrl(unsigned char *pframe)
+{
+ if (WIFI_CTRL_TYPE == GetFrameType(pframe))
+ return true;
+ else
+ return false;
+}
+/*-----------------------------------------------------------------------------
+ Below is for the security related definition
+------------------------------------------------------------------------------*/
+#define _RESERVED_FRAME_TYPE_ 0
+#define _SKB_FRAME_TYPE_ 2
+#define _PRE_ALLOCMEM_ 1
+#define _PRE_ALLOCHDR_ 3
+#define _PRE_ALLOCLLCHDR_ 4
+#define _PRE_ALLOCICVHDR_ 5
+#define _PRE_ALLOCMICHDR_ 6
+
+#define _SIFSTIME_ \
+ ((priv->pmib->dot11BssType.net_work_type & WIRELESS_11A) ? 16 : 10)
+#define _ACKCTSLNG_ 14 /* 14 bytes long, including crclng */
+#define _CRCLNG_ 4
+
+#define _ASOCREQ_IE_OFFSET_ 4 /* excluding wlan_hdr */
+#define _ASOCRSP_IE_OFFSET_ 6
+#define _REASOCREQ_IE_OFFSET_ 10
+#define _REASOCRSP_IE_OFFSET_ 6
+#define _PROBEREQ_IE_OFFSET_ 0
+#define _PROBERSP_IE_OFFSET_ 12
+#define _AUTH_IE_OFFSET_ 6
+#define _DEAUTH_IE_OFFSET_ 0
+#define _BEACON_IE_OFFSET_ 12
+#define _PUBLIC_ACTION_IE_OFFSET_ 8
+
+#define _FIXED_IE_LENGTH_ _BEACON_IE_OFFSET_
+
+#define _SSID_IE_ 0
+#define _SUPPORTEDRATES_IE_ 1
+#define _DSSET_IE_ 3
+#define _TIM_IE_ 5
+#define _IBSS_PARA_IE_ 6
+#define _COUNTRY_IE_ 7
+#define _CHLGETXT_IE_ 16
+#define _SUPPORTED_CH_IE_ 36
+#define _CH_SWTICH_ANNOUNCE_ 37 /* Secondary Channel Offset */
+#define _RSN_IE_2_ 48
+#define _SSN_IE_1_ 221
+#define _ERPINFO_IE_ 42
+#define _EXT_SUPPORTEDRATES_IE_ 50
+
+#define _HT_CAPABILITY_IE_ 45
+#define _FTIE_ 55
+#define _TIMEOUT_ITVL_IE_ 56
+#define _SRC_IE_ 59
+#define _HT_EXTRA_INFO_IE_ 61
+#define _HT_ADD_INFO_IE_ 61 /* _HT_EXTRA_INFO_IE_ */
+#define _WAPI_IE_ 68
+
+
+#define EID_BSSCoexistence 72 /* 20/40 BSS Coexistence */
+#define EID_BSSIntolerantChlReport 73
+#define _RIC_Descriptor_IE_ 75
+
+#define _LINK_ID_IE_ 101
+#define _CH_SWITCH_TIMING_ 104
+#define _PTI_BUFFER_STATUS_ 106
+#define _EXT_CAP_IE_ 127
+#define _VENDOR_SPECIFIC_IE_ 221
+
+#define _RESERVED47_ 47
+
+/* ---------------------------------------------------------------------------
+ Below is the fixed elements...
+-----------------------------------------------------------------------------*/
+#define _AUTH_ALGM_NUM_ 2
+#define _AUTH_SEQ_NUM_ 2
+#define _BEACON_ITERVAL_ 2
+#define _CAPABILITY_ 2
+#define _CURRENT_APADDR_ 6
+#define _LISTEN_INTERVAL_ 2
+#define _RSON_CODE_ 2
+#define _ASOC_ID_ 2
+#define _STATUS_CODE_ 2
+#define _TIMESTAMP_ 8
+
+#define AUTH_ODD_TO 0
+#define AUTH_EVEN_TO 1
+
+#define WLAN_ETHCONV_ENCAP 1
+#define WLAN_ETHCONV_RFC1042 2
+#define WLAN_ETHCONV_8021h 3
+
+#define cap_ESS BIT(0)
+#define cap_IBSS BIT(1)
+#define cap_CFPollable BIT(2)
+#define cap_CFRequest BIT(3)
+#define cap_Privacy BIT(4)
+#define cap_ShortPremble BIT(5)
+#define cap_PBCC BIT(6)
+#define cap_ChAgility BIT(7)
+#define cap_SpecMgmt BIT(8)
+#define cap_QoSi BIT(9)
+#define cap_ShortSlot BIT(10)
+
+/*-----------------------------------------------------------------------------
+ Below is the definition for 802.11i / 802.1x
+------------------------------------------------------------------------------*/
+#define _IEEE8021X_MGT_ 1 /* WPA */
+#define _IEEE8021X_PSK_ 2 /* WPA with pre-shared key */
+
+/*
+#define _NO_PRIVACY_ 0
+#define _WEP_40_PRIVACY_ 1
+#define _TKIP_PRIVACY_ 2
+#define _WRAP_PRIVACY_ 3
+#define _CCMP_PRIVACY_ 4
+#define _WEP_104_PRIVACY_ 5
+#define _WEP_WPA_MIXED_PRIVACY_ 6 WEP + WPA
+*/
+
+/*-----------------------------------------------------------------------------
+ Below is the definition for WMM
+------------------------------------------------------------------------------*/
+#define _WMM_IE_Length_ 7 /* for WMM STA */
+#define _WMM_Para_Element_Length_ 24
+
+
+/*-----------------------------------------------------------------------------
+ Below is the definition for 802.11n
+------------------------------------------------------------------------------*/
+
+#define SetOrderBit(pbuf) \
+ do { \
+ *(unsigned short *)(pbuf) |= cpu_to_le16(_ORDER_); \
+ } while (0)
+
+#define GetOrderBit(pbuf) \
+ (((*(unsigned short *)(pbuf)) & le16_to_cpu(_ORDER_)) != 0)
+
+
+/**
+ * struct rtw_ieee80211_bar - HT Block Ack Request
+ *
+ * This structure refers to "HT BlockAckReq" as
+ * described in 802.11n draft section 7.2.1.7.1
+ */
+struct rtw_ieee80211_bar {
+ unsigned short frame_control;
+ unsigned short duration;
+ unsigned char ra[6];
+ unsigned char ta[6];
+ unsigned short control;
+ unsigned short start_seq_num;
+} __packed;
+
+/* 802.11 BAR control masks */
+#define IEEE80211_BAR_CTRL_ACK_POLICY_NORMAL 0x0000
+#define IEEE80211_BAR_CTRL_CBMTID_COMPRESSED_BA 0x0004
+
+ /**
+ * struct rtw_ieee80211_ht_cap - HT capabilities
+ *
+ * This structure refers to "HT capabilities element" as
+ * described in 802.11n draft section 7.3.2.52
+ */
+
+struct rtw_ieee80211_ht_cap {
+ unsigned short cap_info;
+ unsigned char ampdu_params_info;
+ unsigned char supp_mcs_set[16];
+ unsigned short extended_ht_cap_info;
+ unsigned int tx_BF_cap_info;
+ unsigned char antenna_selection_info;
+} __packed;
+
+/**
+ * struct rtw_ieee80211_ht_cap - HT additional information
+ *
+ * This structure refers to "HT information element" as
+ * described in 802.11n draft section 7.3.2.53
+ */
+struct ieee80211_ht_addt_info {
+ unsigned char control_chan;
+ unsigned char ht_param;
+ unsigned short operation_mode;
+ unsigned short stbc_param;
+ unsigned char basic_set[16];
+} __packed;
+
+struct HT_caps_element {
+ union {
+ struct {
+ __le16 HT_caps_info;
+ unsigned char AMPDU_para;
+ unsigned char MCS_rate[16];
+ unsigned short HT_ext_caps;
+ unsigned int Beamforming_caps;
+ unsigned char ASEL_caps;
+ } HT_cap_element;
+ unsigned char HT_cap[26];
+ } u;
+} __packed;
+
+struct HT_info_element {
+ unsigned char primary_channel;
+ unsigned char infos[5];
+ unsigned char MCS_rate[16];
+} __packed;
+
+struct AC_param {
+ unsigned char ACI_AIFSN;
+ unsigned char CW;
+ __le16 TXOP_limit;
+} __packed;
+
+struct WMM_para_element {
+ unsigned char QoS_info;
+ unsigned char reserved;
+ struct AC_param ac_param[4];
+} __packed;
+
+struct ADDBA_request {
+ unsigned char dialog_token;
+ __le16 BA_para_set;
+ unsigned short BA_timeout_value;
+ unsigned short BA_starting_seqctrl;
+} __packed;
+
+enum ht_cap_ampdu_factor {
+ MAX_AMPDU_FACTOR_8K = 0,
+ MAX_AMPDU_FACTOR_16K = 1,
+ MAX_AMPDU_FACTOR_32K = 2,
+ MAX_AMPDU_FACTOR_64K = 3,
+};
+
+/* 802.11n HT capabilities masks */
+#define IEEE80211_HT_CAP_SUP_WIDTH 0x0002
+#define IEEE80211_HT_CAP_SM_PS 0x000C
+#define IEEE80211_HT_CAP_GRN_FLD 0x0010
+#define IEEE80211_HT_CAP_SGI_20 0x0020
+#define IEEE80211_HT_CAP_SGI_40 0x0040
+#define IEEE80211_HT_CAP_TX_STBC 0x0080
+#define IEEE80211_HT_CAP_RX_STBC 0x0300
+#define IEEE80211_HT_CAP_DELAY_BA 0x0400
+#define IEEE80211_HT_CAP_MAX_AMSDU 0x0800
+#define IEEE80211_HT_CAP_DSSSCCK40 0x1000
+/* 802.11n HT capability AMPDU settings */
+#define IEEE80211_HT_CAP_AMPDU_FACTOR 0x03
+#define IEEE80211_HT_CAP_AMPDU_DENSITY 0x1C
+/* 802.11n HT capability MSC set */
+#define IEEE80211_SUPP_MCS_SET_UEQM 4
+#define IEEE80211_HT_CAP_MAX_STREAMS 4
+#define IEEE80211_SUPP_MCS_SET_LEN 10
+/* maximum streams the spec allows */
+#define IEEE80211_HT_CAP_MCS_TX_DEFINED 0x01
+#define IEEE80211_HT_CAP_MCS_TX_RX_DIFF 0x02
+#define IEEE80211_HT_CAP_MCS_TX_STREAMS 0x0C
+#define IEEE80211_HT_CAP_MCS_TX_UEQM 0x10
+/* 802.11n HT IE masks */
+#define IEEE80211_HT_IE_CHA_SEC_OFFSET 0x03
+#define IEEE80211_HT_IE_CHA_SEC_NONE 0x00
+#define IEEE80211_HT_IE_CHA_SEC_ABOVE 0x01
+#define IEEE80211_HT_IE_CHA_SEC_BELOW 0x03
+#define IEEE80211_HT_IE_CHA_WIDTH 0x04
+#define IEEE80211_HT_IE_HT_PROTECTION 0x0003
+#define IEEE80211_HT_IE_NON_GF_STA_PRSNT 0x0004
+#define IEEE80211_HT_IE_NON_HT_STA_PRSNT 0x0010
+
+/* block-ack parameters */
+#define IEEE80211_ADDBA_PARAM_POLICY_MASK 0x0002
+#define IEEE80211_ADDBA_PARAM_TID_MASK 0x003C
+#define RTW_IEEE80211_ADDBA_PARAM_BUF_SIZE_MASK 0xFFC0
+#define IEEE80211_DELBA_PARAM_TID_MASK 0xF000
+#define IEEE80211_DELBA_PARAM_INITIATOR_MASK 0x0800
+
+/*
+ * A-PMDU buffer sizes
+ * According to IEEE802.11n spec size varies from 8K to 64K (in powers of 2)
+ */
+#define IEEE80211_MIN_AMPDU_BUF 0x8
+#define IEEE80211_MAX_AMPDU_BUF 0x40
+
+
+/* Spatial Multiplexing Power Save Modes */
+#define WLAN_HT_CAP_SM_PS_STATIC 0
+#define WLAN_HT_CAP_SM_PS_DYNAMIC 1
+#define WLAN_HT_CAP_SM_PS_INVALID 2
+#define WLAN_HT_CAP_SM_PS_DISABLED 3
+
+
+#define OP_MODE_PURE 0
+#define OP_MODE_MAY_BE_LEGACY_STAS 1
+#define OP_MODE_20MHZ_HT_STA_ASSOCED 2
+#define OP_MODE_MIXED 3
+
+#define HT_INFO_HT_PARAM_SECONDARY_CHNL_OFF_MASK ((u8)BIT(0) | BIT(1))
+#define HT_INFO_HT_PARAM_SECONDARY_CHNL_ABOVE ((u8)BIT(0))
+#define HT_INFO_HT_PARAM_SECONDARY_CHNL_BELOW ((u8)BIT(0) | BIT(1))
+#define HT_INFO_HT_PARAM_REC_TRANS_CHNL_WIDTH ((u8)BIT(2))
+#define HT_INFO_HT_PARAM_RIFS_MODE ((u8)BIT(3))
+#define HT_INFO_HT_PARAM_CTRL_ACCESS_ONLY ((u8)BIT(4))
+#define HT_INFO_HT_PARAM_SRV_INTERVAL_GRANULARITY ((u8)BIT(5))
+
+#define HT_INFO_OPERATION_MODE_OP_MODE_MASK \
+ ((u16)(0x0001 | 0x0002))
+#define HT_INFO_OPERATION_MODE_OP_MODE_OFFSET 0
+#define HT_INFO_OPERATION_MODE_NON_GF_DEVS_PRESENT ((u8)BIT(2))
+#define HT_INFO_OPERATION_MODE_TRANSMIT_BURST_LIMIT ((u8)BIT(3))
+#define HT_INFO_OPERATION_MODE_NON_HT_STA_PRESENT ((u8)BIT(4))
+
+#define HT_INFO_STBC_PARAM_DUAL_BEACON ((u16)BIT(6))
+#define HT_INFO_STBC_PARAM_DUAL_STBC_PROTECT ((u16)BIT(7))
+#define HT_INFO_STBC_PARAM_SECONDARY_BC ((u16)BIT(8))
+#define HT_INFO_STBC_PARAM_LSIG_TXOP_PROTECT_ALLOWED ((u16)BIT(9))
+#define HT_INFO_STBC_PARAM_PCO_ACTIVE ((u16)BIT(10))
+#define HT_INFO_STBC_PARAM_PCO_PHASE ((u16)BIT(11))
+
+/* ===============WPS Section=============== */
+/* For WPSv1.0 */
+#define WPSOUI 0x0050f204
+/* WPS attribute ID */
+#define WPS_ATTR_VER1 0x104A
+#define WPS_ATTR_SIMPLE_CONF_STATE 0x1044
+#define WPS_ATTR_RESP_TYPE 0x103B
+#define WPS_ATTR_UUID_E 0x1047
+#define WPS_ATTR_MANUFACTURER 0x1021
+#define WPS_ATTR_MODEL_NAME 0x1023
+#define WPS_ATTR_MODEL_NUMBER 0x1024
+#define WPS_ATTR_SERIAL_NUMBER 0x1042
+#define WPS_ATTR_PRIMARY_DEV_TYPE 0x1054
+#define WPS_ATTR_SEC_DEV_TYPE_LIST 0x1055
+#define WPS_ATTR_DEVICE_NAME 0x1011
+#define WPS_ATTR_CONF_METHOD 0x1008
+#define WPS_ATTR_RF_BANDS 0x103C
+#define WPS_ATTR_DEVICE_PWID 0x1012
+#define WPS_ATTR_REQUEST_TYPE 0x103A
+#define WPS_ATTR_ASSOCIATION_STATE 0x1002
+#define WPS_ATTR_CONFIG_ERROR 0x1009
+#define WPS_ATTR_VENDOR_EXT 0x1049
+#define WPS_ATTR_SELECTED_REGISTRAR 0x1041
+
+/* Value of WPS attribute "WPS_ATTR_DEVICE_NAME */
+#define WPS_MAX_DEVICE_NAME_LEN 32
+
+/* Value of WPS Request Type Attribute */
+#define WPS_REQ_TYPE_ENROLLEE_INFO_ONLY 0x00
+#define WPS_REQ_TYPE_ENROLLEE_OPEN_8021X 0x01
+#define WPS_REQ_TYPE_REGISTRAR 0x02
+#define WPS_REQ_TYPE_WLAN_MANAGER_REGISTRAR 0x03
+
+/* Value of WPS Response Type Attribute */
+#define WPS_RESPONSE_TYPE_INFO_ONLY 0x00
+#define WPS_RESPONSE_TYPE_8021X 0x01
+#define WPS_RESPONSE_TYPE_REGISTRAR 0x02
+#define WPS_RESPONSE_TYPE_AP 0x03
+
+/* Value of WPS WiFi Simple Configuration State Attribute */
+#define WPS_WSC_STATE_NOT_CONFIG 0x01
+#define WPS_WSC_STATE_CONFIG 0x02
+
+/* Value of WPS Version Attribute */
+#define WPS_VERSION_1 0x10
+
+/* Value of WPS Configuration Method Attribute */
+#define WPS_CONFIG_METHOD_FLASH 0x0001
+#define WPS_CONFIG_METHOD_ETHERNET 0x0002
+#define WPS_CONFIG_METHOD_LABEL 0x0004
+#define WPS_CONFIG_METHOD_DISPLAY 0x0008
+#define WPS_CONFIG_METHOD_E_NFC 0x0010
+#define WPS_CONFIG_METHOD_I_NFC 0x0020
+#define WPS_CONFIG_METHOD_NFC 0x0040
+#define WPS_CONFIG_METHOD_PBC 0x0080
+#define WPS_CONFIG_METHOD_KEYPAD 0x0100
+#define WPS_CONFIG_METHOD_VPBC 0x0280
+#define WPS_CONFIG_METHOD_PPBC 0x0480
+#define WPS_CONFIG_METHOD_VDISPLAY 0x2008
+#define WPS_CONFIG_METHOD_PDISPLAY 0x4008
+
+/* Value of Category ID of WPS Primary Device Type Attribute */
+#define WPS_PDT_CID_DISPLAYS 0x0007
+#define WPS_PDT_CID_MULIT_MEDIA 0x0008
+#define WPS_PDT_CID_RTK_WIDI WPS_PDT_CID_MULIT_MEDIA
+
+/* Value of Sub Category ID of WPS Primary Device Type Attribute */
+#define WPS_PDT_SCID_MEDIA_SERVER 0x0005
+#define WPS_PDT_SCID_RTK_DMP WPS_PDT_SCID_MEDIA_SERVER
+
+/* Value of Device Password ID */
+#define WPS_DPID_P 0x0000
+#define WPS_DPID_USER_SPEC 0x0001
+#define WPS_DPID_MACHINE_SPEC 0x0002
+#define WPS_DPID_REKEY 0x0003
+#define WPS_DPID_PBC 0x0004
+#define WPS_DPID_REGISTRAR_SPEC 0x0005
+
+/* Value of WPS RF Bands Attribute */
+#define WPS_RF_BANDS_2_4_GHZ 0x01
+#define WPS_RF_BANDS_5_GHZ 0x02
+
+/* Value of WPS Association State Attribute */
+#define WPS_ASSOC_STATE_NOT_ASSOCIATED 0x00
+#define WPS_ASSOC_STATE_CONNECTION_SUCCESS 0x01
+#define WPS_ASSOC_STATE_CONFIGURATION_FAILURE 0x02
+#define WPS_ASSOC_STATE_ASSOCIATION_FAILURE 0x03
+#define WPS_ASSOC_STATE_IP_FAILURE 0x04
+
+/* =====================P2P Section===================== */
+/* For P2P */
+#define P2POUI 0x506F9A09
+
+/* P2P Attribute ID */
+#define P2P_ATTR_STATUS 0x00
+#define P2P_ATTR_MINOR_REASON_CODE 0x01
+#define P2P_ATTR_CAPABILITY 0x02
+#define P2P_ATTR_DEVICE_ID 0x03
+#define P2P_ATTR_GO_INTENT 0x04
+#define P2P_ATTR_CONF_TIMEOUT 0x05
+#define P2P_ATTR_LISTEN_CH 0x06
+#define P2P_ATTR_GROUP_BSSID 0x07
+#define P2P_ATTR_EX_LISTEN_TIMING 0x08
+#define P2P_ATTR_INTENTED_IF_ADDR 0x09
+#define P2P_ATTR_MANAGEABILITY 0x0A
+#define P2P_ATTR_CH_LIST 0x0B
+#define P2P_ATTR_NOA 0x0C
+#define P2P_ATTR_DEVICE_INFO 0x0D
+#define P2P_ATTR_GROUP_INFO 0x0E
+#define P2P_ATTR_GROUP_ID 0x0F
+#define P2P_ATTR_INTERFACE 0x10
+#define P2P_ATTR_OPERATING_CH 0x11
+#define P2P_ATTR_INVITATION_FLAGS 0x12
+
+/* Value of Status Attribute */
+#define P2P_STATUS_SUCCESS 0x00
+#define P2P_STATUS_FAIL_INFO_UNAVAILABLE 0x01
+#define P2P_STATUS_FAIL_INCOMPATIBLE_PARAM 0x02
+#define P2P_STATUS_FAIL_LIMIT_REACHED 0x03
+#define P2P_STATUS_FAIL_INVALID_PARAM 0x04
+#define P2P_STATUS_FAIL_REQUEST_UNABLE 0x05
+#define P2P_STATUS_FAIL_PREVOUS_PROTO_ERR 0x06
+#define P2P_STATUS_FAIL_NO_COMMON_CH 0x07
+#define P2P_STATUS_FAIL_UNKNOWN_P2PGROUP 0x08
+#define P2P_STATUS_FAIL_BOTH_GOINTENT_15 0x09
+#define P2P_STATUS_FAIL_INCOMPATIBLE_PROVSION 0x0A
+#define P2P_STATUS_FAIL_USER_REJECT 0x0B
+
+/* Value of Invitation Flags Attribute */
+#define P2P_INVITATION_FLAGS_PERSISTENT BIT(0)
+
+#define DMP_P2P_DEVCAP_SUPPORT (P2P_DEVCAP_SERVICE_DISCOVERY | \
+ P2P_DEVCAP_CLIENT_DISCOVERABILITY | \
+ P2P_DEVCAP_CONCURRENT_OPERATION | \
+ P2P_DEVCAP_INVITATION_PROC)
+
+#define DMP_P2P_GRPCAP_SUPPORT (P2P_GRPCAP_INTRABSS)
+
+/* Value of Device Capability Bitmap */
+#define P2P_DEVCAP_SERVICE_DISCOVERY BIT(0)
+#define P2P_DEVCAP_CLIENT_DISCOVERABILITY BIT(1)
+#define P2P_DEVCAP_CONCURRENT_OPERATION BIT(2)
+#define P2P_DEVCAP_INFRA_MANAGED BIT(3)
+#define P2P_DEVCAP_DEVICE_LIMIT BIT(4)
+#define P2P_DEVCAP_INVITATION_PROC BIT(5)
+
+/* Value of Group Capability Bitmap */
+#define P2P_GRPCAP_GO BIT(0)
+#define P2P_GRPCAP_PERSISTENT_GROUP BIT(1)
+#define P2P_GRPCAP_GROUP_LIMIT BIT(2)
+#define P2P_GRPCAP_INTRABSS BIT(3)
+#define P2P_GRPCAP_CROSS_CONN BIT(4)
+#define P2P_GRPCAP_PERSISTENT_RECONN BIT(5)
+#define P2P_GRPCAP_GROUP_FORMATION BIT(6)
+
+/* P2P Public Action Frame (Management Frame) */
+#define P2P_PUB_ACTION_ACTION 0x09
+
+/* P2P Public Action Frame Type */
+#define P2P_GO_NEGO_REQ 0
+#define P2P_GO_NEGO_RESP 1
+#define P2P_GO_NEGO_CONF 2
+#define P2P_INVIT_REQ 3
+#define P2P_INVIT_RESP 4
+#define P2P_DEVDISC_REQ 5
+#define P2P_DEVDISC_RESP 6
+#define P2P_PROVISION_DISC_REQ 7
+#define P2P_PROVISION_DISC_RESP 8
+
+/* P2P Action Frame Type */
+#define P2P_NOTICE_OF_ABSENCE 0
+#define P2P_PRESENCE_REQUEST 1
+#define P2P_PRESENCE_RESPONSE 2
+#define P2P_GO_DISC_REQUEST 3
+
+
+#define P2P_MAX_PERSISTENT_GROUP_NUM 10
+
+#define P2P_PROVISIONING_SCAN_CNT 3
+
+#define P2P_WILDCARD_SSID_LEN 7
+
+/* default value, used when: (1)p2p disabled or (2)p2p enabled
+ * but only do 1 scan phase */
+#define P2P_FINDPHASE_EX_NONE 0
+/* used when p2p enabled and want to do 1 scan phase and
+ * P2P_FINDPHASE_EX_MAX-1 find phase */
+#define P2P_FINDPHASE_EX_FULL 1
+#define P2P_FINDPHASE_EX_SOCIAL_FIRST (P2P_FINDPHASE_EX_FULL+1)
+#define P2P_FINDPHASE_EX_MAX 4
+#define P2P_FINDPHASE_EX_SOCIAL_LAST P2P_FINDPHASE_EX_MAX
+
+/* 5 seconds timeout for sending the provision discovery request */
+#define P2P_PROVISION_TIMEOUT 5000
+/* 3 seconds timeout for sending the prov disc request concurrent mode */
+#define P2P_CONCURRENT_PROVISION_TIME 3000
+/* 5 seconds timeout for receiving the group negotiation response */
+#define P2P_GO_NEGO_TIMEOUT 5000
+/* 3 seconds timeout for sending the negotiation request under concurrent mode */
+#define P2P_CONCURRENT_GO_NEGO_TIME 3000
+/* 100ms */
+#define P2P_TX_PRESCAN_TIMEOUT 100
+/* 5 seconds timeout for sending the invitation request */
+#define P2P_INVITE_TIMEOUT 5000
+/* 3 seconds timeout for sending the invitation request under concurrent mode */
+#define P2P_CONCURRENT_INVITE_TIME 3000
+/* 25 seconds timeout to reset the scan channel (based on channel plan) */
+#define P2P_RESET_SCAN_CH 25000
+#define P2P_MAX_INTENT 15
+
+#define P2P_MAX_NOA_NUM 2
+
+/* WPS Configuration Method */
+#define WPS_CM_NONE 0x0000
+#define WPS_CM_LABEL 0x0004
+#define WPS_CM_DISPLYA 0x0008
+#define WPS_CM_EXTERNAL_NFC_TOKEN 0x0010
+#define WPS_CM_INTEGRATED_NFC_TOKEN 0x0020
+#define WPS_CM_NFC_INTERFACE 0x0040
+#define WPS_CM_PUSH_BUTTON 0x0080
+#define WPS_CM_KEYPAD 0x0100
+#define WPS_CM_SW_PUHS_BUTTON 0x0280
+#define WPS_CM_HW_PUHS_BUTTON 0x0480
+#define WPS_CM_SW_DISPLAY_P 0x2008
+#define WPS_CM_LCD_DISPLAY_P 0x4008
+
+enum P2P_ROLE {
+ P2P_ROLE_DISABLE = 0,
+ P2P_ROLE_DEVICE = 1,
+ P2P_ROLE_CLIENT = 2,
+ P2P_ROLE_GO = 3
+};
+
+enum P2P_STATE {
+ P2P_STATE_NONE = 0, /* P2P disable */
+ /* P2P had enabled and do nothing */
+ P2P_STATE_IDLE = 1,
+ P2P_STATE_LISTEN = 2, /* In pure listen state */
+ P2P_STATE_SCAN = 3, /* In scan phase */
+ /* In the listen state of find phase */
+ P2P_STATE_FIND_PHASE_LISTEN = 4,
+ /* In the search state of find phase */
+ P2P_STATE_FIND_PHASE_SEARCH = 5,
+ /* In P2P provisioning discovery */
+ P2P_STATE_TX_PROVISION_DIS_REQ = 6,
+ P2P_STATE_RX_PROVISION_DIS_RSP = 7,
+ P2P_STATE_RX_PROVISION_DIS_REQ = 8,
+ /* Doing the group owner negotiation handshake */
+ P2P_STATE_GONEGO_ING = 9,
+ /* finish the group negotiation handshake with success */
+ P2P_STATE_GONEGO_OK = 10,
+ /* finish the group negotiation handshake with failure */
+ P2P_STATE_GONEGO_FAIL = 11,
+ /* receiving the P2P Invitation request and match with the profile. */
+ P2P_STATE_RECV_INVITE_REQ_MATCH = 12,
+ /* Doing the P2P WPS */
+ P2P_STATE_PROVISIONING_ING = 13,
+ /* Finish the P2P WPS */
+ P2P_STATE_PROVISIONING_DONE = 14,
+ /* Transmit the P2P Invitation request */
+ P2P_STATE_TX_INVITE_REQ = 15,
+ /* Receiving the P2P Invitation response */
+ P2P_STATE_RX_INVITE_RESP_OK = 16,
+ /* receiving the P2P Invitation request and dismatch with the profile. */
+ P2P_STATE_RECV_INVITE_REQ_DISMATCH = 17,
+ /* receiving the P2P Invitation request and this wifi is GO. */
+ P2P_STATE_RECV_INVITE_REQ_GO = 18,
+ /* receiving the P2P Invitation request to join an existing P2P Group. */
+ P2P_STATE_RECV_INVITE_REQ_JOIN = 19,
+ /* receiving the P2P Invitation response with failure */
+ P2P_STATE_RX_INVITE_RESP_FAIL = 20,
+ /* receiving p2p negotiation response with information is not available */
+ P2P_STATE_RX_INFOR_NOREADY = 21,
+ /* sending p2p negotiation response with information is not available */
+ P2P_STATE_TX_INFOR_NOREADY = 22,
+};
+
+enum P2P_WPSINFO {
+ P2P_NO_WPSINFO = 0,
+ P2P_GOT_WPSINFO_PEER_DISPLAY_PIN = 1,
+ P2P_GOT_WPSINFO_SELF_DISPLAY_PIN = 2,
+ P2P_GOT_WPSINFO_PBC = 3,
+};
+
+#define P2P_PRIVATE_IOCTL_SET_LEN 64
+
+enum P2P_PROTO_WK_ID {
+ P2P_FIND_PHASE_WK = 0,
+ P2P_RESTORE_STATE_WK = 1,
+ P2P_PRE_TX_PROVDISC_PROCESS_WK = 2,
+ P2P_PRE_TX_NEGOREQ_PROCESS_WK = 3,
+ P2P_PRE_TX_INVITEREQ_PROCESS_WK = 4,
+ P2P_AP_P2P_CH_SWITCH_PROCESS_WK = 5,
+ P2P_RO_CH_WK = 6,
+};
+
+enum P2P_PS_STATE {
+ P2P_PS_DISABLE = 0,
+ P2P_PS_ENABLE = 1,
+ P2P_PS_SCAN = 2,
+ P2P_PS_SCAN_DONE = 3,
+ P2P_PS_ALLSTASLEEP = 4, /* for P2P GO */
+};
+
+enum P2P_PS_MODE {
+ P2P_PS_NONE = 0,
+ P2P_PS_CTWINDOW = 1,
+ P2P_PS_NOA = 2,
+ P2P_PS_MIX = 3, /* CTWindow and NoA */
+};
+
+/* =====================WFD Section===================== */
+/* For Wi-Fi Display */
+#define WFD_ATTR_DEVICE_INFO 0x00
+#define WFD_ATTR_ASSOC_BSSID 0x01
+#define WFD_ATTR_COUPLED_SINK_INFO 0x06
+#define WFD_ATTR_LOCAL_IP_ADDR 0x08
+#define WFD_ATTR_SESSION_INFO 0x09
+#define WFD_ATTR_ALTER_MAC 0x0a
+
+/* For WFD Device Information Attribute */
+#define WFD_DEVINFO_SOURCE 0x0000
+#define WFD_DEVINFO_PSINK 0x0001
+#define WFD_DEVINFO_SSINK 0x0002
+#define WFD_DEVINFO_DUAL 0x0003
+
+#define WFD_DEVINFO_SESSION_AVAIL 0x0010
+#define WFD_DEVINFO_WSD 0x0040
+#define WFD_DEVINFO_PC_TDLS 0x0080
+#define WFD_DEVINFO_HDCP_SUPPORT 0x0100
+
+#define IP_MCAST_MAC(mac) \
+ ((mac[0] == 0x01) && (mac[1] == 0x00) && (mac[2] == 0x5e))
+#define ICMPV6_MCAST_MAC(mac) \
+ ((mac[0] == 0x33) && (mac[1] == 0x33) && (mac[2] != 0xff))
+
+#endif /* _WIFI_H_ */
diff --git a/drivers/staging/rtl8188eu/include/wlan_bssdef.h b/drivers/staging/rtl8188eu/include/wlan_bssdef.h
new file mode 100644
index 000000000..85b99da49
--- /dev/null
+++ b/drivers/staging/rtl8188eu/include/wlan_bssdef.h
@@ -0,0 +1,333 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2011 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ *
+ ******************************************************************************/
+#ifndef __WLAN_BSSDEF_H__
+#define __WLAN_BSSDEF_H__
+
+
+#define MAX_IE_SZ 768
+
+#define NDIS_802_11_LENGTH_SSID 32
+#define NDIS_802_11_LENGTH_RATES 8
+#define NDIS_802_11_LENGTH_RATES_EX 16
+
+#define NDIS_802_11_RSSI long /* in dBm */
+
+struct ndis_802_11_ssid {
+ u32 SsidLength;
+ u8 Ssid[32];
+};
+
+enum NDIS_802_11_NETWORK_TYPE {
+ Ndis802_11FH,
+ Ndis802_11DS,
+ Ndis802_11OFDM5,
+ Ndis802_11OFDM24,
+ Ndis802_11NetworkTypeMax /* dummy upper bound */
+};
+
+struct ndis_802_11_config_fh {
+ u32 Length; /* Length of structure */
+ u32 HopPattern; /* As defined by 802.11, MSB set */
+ u32 HopSet; /* to one if non-802.11 */
+ u32 DwellTime; /* units are Kusec */
+};
+
+/*
+ * FW will only save the channel number in DSConfig.
+ * ODI Handler will convert the channel number to freq. number.
+ */
+struct ndis_802_11_config {
+ u32 Length; /* Length of structure */
+ u32 BeaconPeriod; /* units are Kusec */
+ u32 ATIMWindow; /* units are Kusec */
+ u32 DSConfig; /* Frequency, units are kHz */
+ struct ndis_802_11_config_fh FHConfig;
+};
+
+enum ndis_802_11_network_infra {
+ Ndis802_11IBSS,
+ Ndis802_11Infrastructure,
+ Ndis802_11AutoUnknown,
+ Ndis802_11InfrastructureMax, /* dummy upper bound */
+ Ndis802_11APMode
+};
+
+struct ndis_802_11_fixed_ie {
+ u8 Timestamp[8];
+ u16 BeaconInterval;
+ u16 Capabilities;
+};
+
+
+
+struct ndis_802_11_var_ie {
+ u8 ElementID;
+ u8 Length;
+ u8 data[1];
+};
+
+/*
+ * Length is the 4 bytes multiples of the sume of
+ * [ETH_ALEN] + 2 + sizeof (struct ndis_802_11_ssid) + sizeof (u32)
+ * + sizeof (NDIS_802_11_RSSI) + sizeof (enum NDIS_802_11_NETWORK_TYPE)
+ * + sizeof (struct ndis_802_11_config)
+ * + NDIS_802_11_LENGTH_RATES_EX + IELength
+ *
+ * Except the IELength, all other fields are fixed length.
+ * Therefore, we can define a macro to represent the partial sum. */
+
+enum ndis_802_11_auth_mode {
+ Ndis802_11AuthModeOpen,
+ Ndis802_11AuthModeShared,
+ Ndis802_11AuthModeAutoSwitch,
+ Ndis802_11AuthModeWPA,
+ Ndis802_11AuthModeWPAPSK,
+ Ndis802_11AuthModeWPANone,
+ Ndis802_11AuthModeWAPI,
+ Ndis802_11AuthModeMax /* Not a real mode, upper bound */
+};
+
+enum ndis_802_11_wep_status {
+ Ndis802_11WEPEnabled,
+ Ndis802_11Encryption1Enabled = Ndis802_11WEPEnabled,
+ Ndis802_11WEPDisabled,
+ Ndis802_11EncryptionDisabled = Ndis802_11WEPDisabled,
+ Ndis802_11WEPKeyAbsent,
+ Ndis802_11Encryption1KeyAbsent = Ndis802_11WEPKeyAbsent,
+ Ndis802_11WEPNotSupported,
+ Ndis802_11EncryptionNotSupported = Ndis802_11WEPNotSupported,
+ Ndis802_11Encryption2Enabled,
+ Ndis802_11Encryption2KeyAbsent,
+ Ndis802_11Encryption3Enabled,
+ Ndis802_11Encryption3KeyAbsent,
+ Ndis802_11_EncryptionWAPI
+};
+
+#define NDIS_802_11_AI_REQFI_CAPABILITIES 1
+#define NDIS_802_11_AI_REQFI_LISTENINTERVAL 2
+#define NDIS_802_11_AI_REQFI_CURRENTAPADDRESS 4
+
+#define NDIS_802_11_AI_RESFI_CAPABILITIES 1
+#define NDIS_802_11_AI_RESFI_STATUSCODE 2
+#define NDIS_802_11_AI_RESFI_ASSOCIATIONID 4
+
+struct ndis_802_11_ai_reqfi {
+ u16 Capabilities;
+ u16 ListenInterval;
+ unsigned char CurrentAPAddress[ETH_ALEN];
+};
+
+struct ndis_802_11_ai_resfi {
+ u16 Capabilities;
+ u16 StatusCode;
+ u16 AssociationId;
+};
+
+struct ndis_802_11_assoc_info {
+ u32 Length;
+ u16 AvailableRequestFixedIEs;
+ struct ndis_802_11_ai_reqfi RequestFixedIEs;
+ u32 RequestIELength;
+ u32 OffsetRequestIEs;
+ u16 AvailableResponseFixedIEs;
+ struct ndis_802_11_ai_resfi ResponseFixedIEs;
+ u32 ResponseIELength;
+ u32 OffsetResponseIEs;
+};
+
+enum ndis_802_11_reload_def {
+ Ndis802_11ReloadWEPKeys
+};
+
+struct ndis_802_11_remove_key {
+ u32 Length; /* Length */
+ u32 KeyIndex;
+ unsigned char BSSID[ETH_ALEN];
+};
+
+struct ndis_802_11_wep {
+ u32 Length; /* Length of this structure */
+ u32 KeyIndex; /* 0 is the per-client key,
+ * 1-N are the global keys */
+ u32 KeyLength; /* length of key in bytes */
+ u8 KeyMaterial[16];/* variable len depending on above field */
+};
+
+struct ndis_802_11_auth_req {
+ u32 Length; /* Length of structure */
+ unsigned char Bssid[ETH_ALEN];
+ u32 Flags;
+};
+
+enum ndis_802_11_status_type {
+ Ndis802_11StatusType_Authentication,
+ Ndis802_11StatusType_MediaStreamMode,
+ Ndis802_11StatusType_PMKID_CandidateList,
+ Ndis802_11StatusTypeMax /* not a real type, defined as
+ * an upper bound */
+};
+
+struct ndis_802_11_status_ind {
+ enum ndis_802_11_status_type StatusType;
+};
+
+/* mask for authentication/integrity fields */
+#define NDIS_802_11_AUTH_REQUEST_AUTH_FIELDS 0x0f
+#define NDIS_802_11_AUTH_REQUEST_REAUTH 0x01
+#define NDIS_802_11_AUTH_REQUEST_KEYUPDATE 0x02
+#define NDIS_802_11_AUTH_REQUEST_PAIRWISE_ERROR 0x06
+#define NDIS_802_11_AUTH_REQUEST_GROUP_ERROR 0x0E
+
+/* MIC check time, 60 seconds. */
+#define MIC_CHECK_TIME 60000000
+
+struct ndis_802_11_auth_evt {
+ struct ndis_802_11_status_ind Status;
+ struct ndis_802_11_auth_req Request[1];
+};
+
+struct ndis_802_11_test {
+ u32 Length;
+ u32 Type;
+ union {
+ struct ndis_802_11_auth_evt AuthenticationEvent;
+ NDIS_802_11_RSSI RssiTrigger;
+ } tt;
+};
+
+
+#ifndef Ndis802_11APMode
+#define Ndis802_11APMode (Ndis802_11InfrastructureMax+1)
+#endif
+
+struct wlan_phy_info {
+ u8 SignalStrength;/* in percentage) */
+ u8 SignalQuality;/* in percentage) */
+ u8 Optimum_antenna; /* for Antenna diversity */
+ u8 Reserved_0;
+};
+
+struct wlan_bcn_info {
+ /* these infor get from rtw_get_encrypt_info when
+ * * translate scan to UI */
+ u8 encryp_protocol;/* ENCRYP_PROTOCOL_E: OPEN/WEP/WPA/WPA2/WAPI */
+ int group_cipher; /* WPA/WPA2 group cipher */
+ int pairwise_cipher;/* WPA/WPA2/WEP pairwise cipher */
+ int is_8021x;
+
+ /* bwmode 20/40 and ch_offset UP/LOW */
+ unsigned short ht_cap_info;
+ unsigned char ht_info_infos_0;
+};
+
+/* temporally add #pragma pack for structure alignment issue of
+* struct wlan_bssid_ex and get_struct wlan_bssid_ex_sz()
+*/
+struct wlan_bssid_ex {
+ u32 Length;
+ unsigned char MacAddress[ETH_ALEN];
+ u8 Reserved[2];/* 0]: IS beacon frame */
+ struct ndis_802_11_ssid Ssid;
+ u32 Privacy;
+ NDIS_802_11_RSSI Rssi;/* in dBM,raw data ,get from PHY) */
+ enum NDIS_802_11_NETWORK_TYPE NetworkTypeInUse;
+ struct ndis_802_11_config Configuration;
+ enum ndis_802_11_network_infra InfrastructureMode;
+ unsigned char SupportedRates[NDIS_802_11_LENGTH_RATES_EX];
+ struct wlan_phy_info PhyInfo;
+ u32 IELength;
+ u8 IEs[MAX_IE_SZ]; /* timestamp, beacon interval, and
+ * capability information) */
+} __packed;
+
+static inline uint get_wlan_bssid_ex_sz(struct wlan_bssid_ex *bss)
+{
+ return sizeof(struct wlan_bssid_ex) - MAX_IE_SZ + bss->IELength;
+}
+
+struct wlan_network {
+ struct list_head list;
+ int network_type; /* refer to ieee80211.h for WIRELESS_11A/B/G */
+ int fixed; /* set fixed when not to be removed
+ * in site-surveying */
+ unsigned long last_scanned; /* timestamp for the network */
+ int aid; /* will only be valid when a BSS is joinned. */
+ int join_res;
+ struct wlan_bssid_ex network; /* must be the last item */
+ struct wlan_bcn_info BcnInfo;
+};
+
+enum VRTL_CARRIER_SENSE {
+ DISABLE_VCS,
+ ENABLE_VCS,
+ AUTO_VCS
+};
+
+enum VCS_TYPE {
+ NONE_VCS,
+ RTS_CTS,
+ CTS_TO_SELF
+};
+
+#define PWR_CAM 0
+#define PWR_MINPS 1
+#define PWR_MAXPS 2
+#define PWR_UAPSD 3
+#define PWR_VOIP 4
+
+enum UAPSD_MAX_SP {
+ NO_LIMIT,
+ TWO_MSDU,
+ FOUR_MSDU,
+ SIX_MSDU
+};
+
+#define NUM_PRE_AUTH_KEY 16
+#define NUM_PMKID_CACHE NUM_PRE_AUTH_KEY
+
+/*
+* WPA2
+*/
+
+struct pmkid_candidate {
+ unsigned char BSSID[ETH_ALEN];
+ u32 Flags;
+};
+
+struct ndis_802_11_pmkid_list {
+ u32 Version; /* Version of the structure */
+ u32 NumCandidates; /* No. of pmkid candidates */
+ struct pmkid_candidate CandidateList[1];
+};
+
+struct ndis_802_11_auth_encrypt {
+ enum ndis_802_11_auth_mode AuthModeSupported;
+ enum ndis_802_11_wep_status EncryptStatusSupported;
+};
+
+struct ndis_802_11_cap {
+ u32 Length;
+ u32 Version;
+ u32 NoOfPMKIDs;
+ u32 NoOfAuthEncryptPairsSupported;
+ struct ndis_802_11_auth_encrypt AuthenticationEncryptionSupported[1];
+};
+
+#endif /* ifndef WLAN_BSSDEF_H_ */
diff --git a/drivers/staging/rtl8188eu/include/xmit_osdep.h b/drivers/staging/rtl8188eu/include/xmit_osdep.h
new file mode 100644
index 000000000..13965f248
--- /dev/null
+++ b/drivers/staging/rtl8188eu/include/xmit_osdep.h
@@ -0,0 +1,61 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2011 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ *
+ ******************************************************************************/
+#ifndef __XMIT_OSDEP_H_
+#define __XMIT_OSDEP_H_
+
+#include <osdep_service.h>
+#include <drv_types.h>
+
+struct pkt_file {
+ struct sk_buff *pkt;
+ size_t pkt_len; /* the remainder length of the open_file */
+ unsigned char *cur_buffer;
+ u8 *buf_start;
+ u8 *cur_addr;
+ size_t buf_len;
+};
+
+#define NR_XMITFRAME 256
+
+struct xmit_priv;
+struct pkt_attrib;
+struct sta_xmit_priv;
+struct xmit_frame;
+struct xmit_buf;
+
+int rtw_xmit_entry(struct sk_buff *pkt, struct net_device *pnetdev);
+
+void rtw_os_xmit_schedule(struct adapter *padapter);
+
+int rtw_os_xmit_resource_alloc(struct adapter *padapter,
+ struct xmit_buf *pxmitbuf, u32 alloc_sz);
+void rtw_os_xmit_resource_free(struct adapter *padapter,
+ struct xmit_buf *pxmitbuf, u32 free_sz);
+
+uint rtw_remainder_len(struct pkt_file *pfile);
+void _rtw_open_pktfile(struct sk_buff *pkt, struct pkt_file *pfile);
+uint _rtw_pktfile_read(struct pkt_file *pfile, u8 *rmem, uint rlen);
+int rtw_endofpktfile(struct pkt_file *pfile);
+
+void rtw_os_pkt_complete(struct adapter *padapter, struct sk_buff *pkt);
+void rtw_os_xmit_complete(struct adapter *padapter,
+ struct xmit_frame *pxframe);
+
+#endif /* __XMIT_OSDEP_H_ */
diff --git a/drivers/staging/rtl8188eu/os_dep/ioctl_linux.c b/drivers/staging/rtl8188eu/os_dep/ioctl_linux.c
new file mode 100644
index 000000000..96c1c2d4a
--- /dev/null
+++ b/drivers/staging/rtl8188eu/os_dep/ioctl_linux.c
@@ -0,0 +1,3125 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2012 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ *
+ ******************************************************************************/
+#define _IOCTL_LINUX_C_
+
+#include <osdep_service.h>
+#include <drv_types.h>
+#include <wlan_bssdef.h>
+#include <rtw_debug.h>
+#include <wifi.h>
+#include <rtw_mlme.h>
+#include <rtw_mlme_ext.h>
+#include <rtw_ioctl.h>
+#include <rtw_ioctl_set.h>
+#include <rtl8188e_hal.h>
+
+#include <rtw_iol.h>
+#include <linux/vmalloc.h>
+#include <linux/etherdevice.h>
+
+#include "osdep_intf.h"
+
+#define RTL_IOCTL_WPA_SUPPLICANT (SIOCIWFIRSTPRIV + 30)
+
+#define SCAN_ITEM_SIZE 768
+#define MAX_CUSTOM_LEN 64
+#define RATE_COUNT 4
+
+/* combo scan */
+#define WEXT_CSCAN_AMOUNT 9
+#define WEXT_CSCAN_BUF_LEN 360
+#define WEXT_CSCAN_HEADER "CSCAN S\x01\x00\x00S\x00"
+#define WEXT_CSCAN_HEADER_SIZE 12
+#define WEXT_CSCAN_SSID_SECTION 'S'
+#define WEXT_CSCAN_CHANNEL_SECTION 'C'
+#define WEXT_CSCAN_NPROBE_SECTION 'N'
+#define WEXT_CSCAN_ACTV_DWELL_SECTION 'A'
+#define WEXT_CSCAN_PASV_DWELL_SECTION 'P'
+#define WEXT_CSCAN_HOME_DWELL_SECTION 'H'
+#define WEXT_CSCAN_TYPE_SECTION 'T'
+
+static u32 rtw_rates[] = {1000000, 2000000, 5500000, 11000000,
+ 6000000, 9000000, 12000000, 18000000, 24000000, 36000000,
+ 48000000, 54000000};
+
+static const char * const iw_operation_mode[] = {
+ "Auto", "Ad-Hoc", "Managed", "Master", "Repeater",
+ "Secondary", "Monitor"
+};
+
+void indicate_wx_scan_complete_event(struct adapter *padapter)
+{
+ union iwreq_data wrqu;
+
+ memset(&wrqu, 0, sizeof(union iwreq_data));
+ wireless_send_event(padapter->pnetdev, SIOCGIWSCAN, &wrqu, NULL);
+}
+
+void rtw_indicate_wx_assoc_event(struct adapter *padapter)
+{
+ union iwreq_data wrqu;
+ struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
+
+ memset(&wrqu, 0, sizeof(union iwreq_data));
+
+ wrqu.ap_addr.sa_family = ARPHRD_ETHER;
+
+ memcpy(wrqu.ap_addr.sa_data, pmlmepriv->cur_network.network.MacAddress, ETH_ALEN);
+
+ DBG_88E_LEVEL(_drv_always_, "assoc success\n");
+ wireless_send_event(padapter->pnetdev, SIOCGIWAP, &wrqu, NULL);
+}
+
+void rtw_indicate_wx_disassoc_event(struct adapter *padapter)
+{
+ union iwreq_data wrqu;
+
+ memset(&wrqu, 0, sizeof(union iwreq_data));
+
+ wrqu.ap_addr.sa_family = ARPHRD_ETHER;
+ eth_zero_addr(wrqu.ap_addr.sa_data);
+
+ DBG_88E_LEVEL(_drv_always_, "indicate disassoc\n");
+ wireless_send_event(padapter->pnetdev, SIOCGIWAP, &wrqu, NULL);
+}
+
+static char *translate_scan(struct adapter *padapter,
+ struct iw_request_info *info,
+ struct wlan_network *pnetwork,
+ char *start, char *stop)
+{
+ struct mlme_priv *pmlmepriv = &(padapter->mlmepriv);
+ struct iw_event iwe;
+ u16 cap;
+ __le16 le_tmp;
+ u32 ht_ielen = 0;
+ char custom[MAX_CUSTOM_LEN];
+ char *p;
+ u16 max_rate = 0, rate, ht_cap = false;
+ u32 i = 0;
+ u8 bw_40MHz = 0, short_GI = 0;
+ u16 mcs_rate = 0;
+ u8 ss, sq;
+
+ /* AP MAC address */
+ iwe.cmd = SIOCGIWAP;
+ iwe.u.ap_addr.sa_family = ARPHRD_ETHER;
+
+ memcpy(iwe.u.ap_addr.sa_data, pnetwork->network.MacAddress, ETH_ALEN);
+ start = iwe_stream_add_event(info, start, stop, &iwe, IW_EV_ADDR_LEN);
+
+ /* Add the ESSID */
+ iwe.cmd = SIOCGIWESSID;
+ iwe.u.data.flags = 1;
+ iwe.u.data.length = min_t(u16, pnetwork->network.Ssid.SsidLength, 32);
+ start = iwe_stream_add_point(info, start, stop, &iwe, pnetwork->network.Ssid.Ssid);
+
+ /* parsing HT_CAP_IE */
+ p = rtw_get_ie(&pnetwork->network.IEs[12], _HT_CAPABILITY_IE_, &ht_ielen, pnetwork->network.IELength-12);
+
+ if (p && ht_ielen > 0) {
+ struct rtw_ieee80211_ht_cap *pht_capie;
+ ht_cap = true;
+ pht_capie = (struct rtw_ieee80211_ht_cap *)(p+2);
+ memcpy(&mcs_rate, pht_capie->supp_mcs_set, 2);
+ bw_40MHz = (pht_capie->cap_info&IEEE80211_HT_CAP_SUP_WIDTH) ? 1 : 0;
+ short_GI = (pht_capie->cap_info&(IEEE80211_HT_CAP_SGI_20|IEEE80211_HT_CAP_SGI_40)) ? 1 : 0;
+ }
+
+ /* Add the protocol name */
+ iwe.cmd = SIOCGIWNAME;
+ if ((rtw_is_cckratesonly_included((u8 *)&pnetwork->network.SupportedRates))) {
+ if (ht_cap)
+ snprintf(iwe.u.name, IFNAMSIZ, "IEEE 802.11bn");
+ else
+ snprintf(iwe.u.name, IFNAMSIZ, "IEEE 802.11b");
+ } else if ((rtw_is_cckrates_included((u8 *)&pnetwork->network.SupportedRates))) {
+ if (ht_cap)
+ snprintf(iwe.u.name, IFNAMSIZ, "IEEE 802.11bgn");
+ else
+ snprintf(iwe.u.name, IFNAMSIZ, "IEEE 802.11bg");
+ } else {
+ if (pnetwork->network.Configuration.DSConfig > 14) {
+ if (ht_cap)
+ snprintf(iwe.u.name, IFNAMSIZ, "IEEE 802.11an");
+ else
+ snprintf(iwe.u.name, IFNAMSIZ, "IEEE 802.11a");
+ } else {
+ if (ht_cap)
+ snprintf(iwe.u.name, IFNAMSIZ, "IEEE 802.11gn");
+ else
+ snprintf(iwe.u.name, IFNAMSIZ, "IEEE 802.11g");
+ }
+ }
+
+ start = iwe_stream_add_event(info, start, stop, &iwe, IW_EV_CHAR_LEN);
+
+ /* Add mode */
+ iwe.cmd = SIOCGIWMODE;
+ memcpy(&le_tmp, rtw_get_capability_from_ie(pnetwork->network.IEs), 2);
+
+ cap = le16_to_cpu(le_tmp);
+
+ if (cap & (WLAN_CAPABILITY_IBSS | WLAN_CAPABILITY_BSS)) {
+ if (cap & WLAN_CAPABILITY_BSS)
+ iwe.u.mode = IW_MODE_MASTER;
+ else
+ iwe.u.mode = IW_MODE_ADHOC;
+
+ start = iwe_stream_add_event(info, start, stop, &iwe, IW_EV_UINT_LEN);
+ }
+
+ if (pnetwork->network.Configuration.DSConfig < 1)
+ pnetwork->network.Configuration.DSConfig = 1;
+
+ /* Add frequency/channel */
+ iwe.cmd = SIOCGIWFREQ;
+ iwe.u.freq.m = rtw_ch2freq(pnetwork->network.Configuration.DSConfig) * 100000;
+ iwe.u.freq.e = 1;
+ iwe.u.freq.i = pnetwork->network.Configuration.DSConfig;
+ start = iwe_stream_add_event(info, start, stop, &iwe, IW_EV_FREQ_LEN);
+
+ /* Add encryption capability */
+ iwe.cmd = SIOCGIWENCODE;
+ if (cap & WLAN_CAPABILITY_PRIVACY)
+ iwe.u.data.flags = IW_ENCODE_ENABLED | IW_ENCODE_NOKEY;
+ else
+ iwe.u.data.flags = IW_ENCODE_DISABLED;
+ iwe.u.data.length = 0;
+ start = iwe_stream_add_point(info, start, stop, &iwe, pnetwork->network.Ssid.Ssid);
+
+ /*Add basic and extended rates */
+ max_rate = 0;
+ p = custom;
+ p += snprintf(p, MAX_CUSTOM_LEN - (p - custom), " Rates (Mb/s): ");
+ while (pnetwork->network.SupportedRates[i] != 0) {
+ rate = pnetwork->network.SupportedRates[i]&0x7F;
+ if (rate > max_rate)
+ max_rate = rate;
+ p += snprintf(p, MAX_CUSTOM_LEN - (p - custom),
+ "%d%s ", rate >> 1, (rate & 1) ? ".5" : "");
+ i++;
+ }
+
+ if (ht_cap) {
+ if (mcs_rate&0x8000)/* MCS15 */
+ max_rate = (bw_40MHz) ? ((short_GI) ? 300 : 270) : ((short_GI) ? 144 : 130);
+ else if (mcs_rate&0x0080)/* MCS7 */
+ ;
+ else/* default MCS7 */
+ max_rate = (bw_40MHz) ? ((short_GI) ? 150 : 135) : ((short_GI) ? 72 : 65);
+
+ max_rate = max_rate*2;/* Mbps/2; */
+ }
+
+ iwe.cmd = SIOCGIWRATE;
+ iwe.u.bitrate.fixed = 0;
+ iwe.u.bitrate.disabled = 0;
+ iwe.u.bitrate.value = max_rate * 500000;
+ start = iwe_stream_add_event(info, start, stop, &iwe, IW_EV_PARAM_LEN);
+
+ /* parsing WPA/WPA2 IE */
+ {
+ u8 buf[MAX_WPA_IE_LEN];
+ u8 wpa_ie[255], rsn_ie[255];
+ u16 wpa_len = 0, rsn_len = 0;
+ u8 *p;
+
+ rtw_get_sec_ie(pnetwork->network.IEs, pnetwork->network.IELength, rsn_ie, &rsn_len, wpa_ie, &wpa_len);
+ RT_TRACE(_module_rtl871x_mlme_c_, _drv_info_, ("rtw_wx_get_scan: ssid =%s\n", pnetwork->network.Ssid.Ssid));
+ RT_TRACE(_module_rtl871x_mlme_c_, _drv_info_, ("rtw_wx_get_scan: wpa_len =%d rsn_len =%d\n", wpa_len, rsn_len));
+
+ if (wpa_len > 0) {
+ p = buf;
+ memset(buf, 0, MAX_WPA_IE_LEN);
+ p += sprintf(p, "wpa_ie=");
+ for (i = 0; i < wpa_len; i++)
+ p += sprintf(p, "%02x", wpa_ie[i]);
+
+ memset(&iwe, 0, sizeof(iwe));
+ iwe.cmd = IWEVCUSTOM;
+ iwe.u.data.length = strlen(buf);
+ start = iwe_stream_add_point(info, start, stop, &iwe, buf);
+
+ memset(&iwe, 0, sizeof(iwe));
+ iwe.cmd = IWEVGENIE;
+ iwe.u.data.length = wpa_len;
+ start = iwe_stream_add_point(info, start, stop, &iwe, wpa_ie);
+ }
+ if (rsn_len > 0) {
+ p = buf;
+ memset(buf, 0, MAX_WPA_IE_LEN);
+ p += sprintf(p, "rsn_ie=");
+ for (i = 0; i < rsn_len; i++)
+ p += sprintf(p, "%02x", rsn_ie[i]);
+ memset(&iwe, 0, sizeof(iwe));
+ iwe.cmd = IWEVCUSTOM;
+ iwe.u.data.length = strlen(buf);
+ start = iwe_stream_add_point(info, start, stop, &iwe, buf);
+
+ memset(&iwe, 0, sizeof(iwe));
+ iwe.cmd = IWEVGENIE;
+ iwe.u.data.length = rsn_len;
+ start = iwe_stream_add_point(info, start, stop, &iwe, rsn_ie);
+ }
+ }
+
+ {/* parsing WPS IE */
+ uint cnt = 0, total_ielen;
+ u8 *wpsie_ptr = NULL;
+ uint wps_ielen = 0;
+
+ u8 *ie_ptr = pnetwork->network.IEs + _FIXED_IE_LENGTH_;
+ total_ielen = pnetwork->network.IELength - _FIXED_IE_LENGTH_;
+
+ while (cnt < total_ielen) {
+ if (rtw_is_wps_ie(&ie_ptr[cnt], &wps_ielen) && (wps_ielen > 2)) {
+ wpsie_ptr = &ie_ptr[cnt];
+ iwe.cmd = IWEVGENIE;
+ iwe.u.data.length = (u16)wps_ielen;
+ start = iwe_stream_add_point(info, start, stop, &iwe, wpsie_ptr);
+ }
+ cnt += ie_ptr[cnt+1]+2; /* goto next */
+ }
+ }
+
+ /* Add quality statistics */
+ iwe.cmd = IWEVQUAL;
+ iwe.u.qual.updated = IW_QUAL_QUAL_UPDATED | IW_QUAL_LEVEL_UPDATED | IW_QUAL_NOISE_INVALID;
+
+ if (check_fwstate(pmlmepriv, _FW_LINKED) == true &&
+ is_same_network(&pmlmepriv->cur_network.network, &pnetwork->network)) {
+ ss = padapter->recvpriv.signal_strength;
+ sq = padapter->recvpriv.signal_qual;
+ } else {
+ ss = pnetwork->network.PhyInfo.SignalStrength;
+ sq = pnetwork->network.PhyInfo.SignalQuality;
+ }
+
+ iwe.u.qual.level = (u8)ss;
+ iwe.u.qual.qual = (u8)sq; /* signal quality */
+ iwe.u.qual.noise = 0; /* noise level */
+ start = iwe_stream_add_event(info, start, stop, &iwe, IW_EV_QUAL_LEN);
+ return start;
+}
+
+static int wpa_set_auth_algs(struct net_device *dev, u32 value)
+{
+ struct adapter *padapter = (struct adapter *)rtw_netdev_priv(dev);
+ int ret = 0;
+
+ if ((value & AUTH_ALG_SHARED_KEY) && (value & AUTH_ALG_OPEN_SYSTEM)) {
+ DBG_88E("wpa_set_auth_algs, AUTH_ALG_SHARED_KEY and AUTH_ALG_OPEN_SYSTEM [value:0x%x]\n", value);
+ padapter->securitypriv.ndisencryptstatus = Ndis802_11Encryption1Enabled;
+ padapter->securitypriv.ndisauthtype = Ndis802_11AuthModeAutoSwitch;
+ padapter->securitypriv.dot11AuthAlgrthm = dot11AuthAlgrthm_Auto;
+ } else if (value & AUTH_ALG_SHARED_KEY) {
+ DBG_88E("wpa_set_auth_algs, AUTH_ALG_SHARED_KEY [value:0x%x]\n", value);
+ padapter->securitypriv.ndisencryptstatus = Ndis802_11Encryption1Enabled;
+
+ padapter->securitypriv.ndisauthtype = Ndis802_11AuthModeShared;
+ padapter->securitypriv.dot11AuthAlgrthm = dot11AuthAlgrthm_Shared;
+ } else if (value & AUTH_ALG_OPEN_SYSTEM) {
+ DBG_88E("wpa_set_auth_algs, AUTH_ALG_OPEN_SYSTEM\n");
+ if (padapter->securitypriv.ndisauthtype < Ndis802_11AuthModeWPAPSK) {
+ padapter->securitypriv.ndisauthtype = Ndis802_11AuthModeOpen;
+ padapter->securitypriv.dot11AuthAlgrthm = dot11AuthAlgrthm_Open;
+ }
+ } else if (value & AUTH_ALG_LEAP) {
+ DBG_88E("wpa_set_auth_algs, AUTH_ALG_LEAP\n");
+ } else {
+ DBG_88E("wpa_set_auth_algs, error!\n");
+ ret = -EINVAL;
+ }
+ return ret;
+}
+
+static int wpa_set_encryption(struct net_device *dev, struct ieee_param *param, u32 param_len)
+{
+ int ret = 0;
+ u32 wep_key_idx, wep_key_len, wep_total_len;
+ struct ndis_802_11_wep *pwep = NULL;
+ struct adapter *padapter = (struct adapter *)rtw_netdev_priv(dev);
+ struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
+ struct security_priv *psecuritypriv = &padapter->securitypriv;
+
+ param->u.crypt.err = 0;
+ param->u.crypt.alg[IEEE_CRYPT_ALG_NAME_LEN - 1] = '\0';
+
+ if (param_len < (u32)((u8 *)param->u.crypt.key - (u8 *)param) + param->u.crypt.key_len) {
+ ret = -EINVAL;
+ goto exit;
+ }
+
+ if (param->sta_addr[0] == 0xff && param->sta_addr[1] == 0xff &&
+ param->sta_addr[2] == 0xff && param->sta_addr[3] == 0xff &&
+ param->sta_addr[4] == 0xff && param->sta_addr[5] == 0xff) {
+ if (param->u.crypt.idx >= WEP_KEYS) {
+ ret = -EINVAL;
+ goto exit;
+ }
+ } else {
+ ret = -EINVAL;
+ goto exit;
+ }
+
+ if (strcmp(param->u.crypt.alg, "WEP") == 0) {
+ RT_TRACE(_module_rtl871x_ioctl_os_c, _drv_err_, ("wpa_set_encryption, crypt.alg = WEP\n"));
+ DBG_88E("wpa_set_encryption, crypt.alg = WEP\n");
+
+ padapter->securitypriv.ndisencryptstatus = Ndis802_11Encryption1Enabled;
+ padapter->securitypriv.dot11PrivacyAlgrthm = _WEP40_;
+ padapter->securitypriv.dot118021XGrpPrivacy = _WEP40_;
+
+ wep_key_idx = param->u.crypt.idx;
+ wep_key_len = param->u.crypt.key_len;
+
+ RT_TRACE(_module_rtl871x_ioctl_os_c, _drv_info_, ("(1)wep_key_idx =%d\n", wep_key_idx));
+ DBG_88E("(1)wep_key_idx =%d\n", wep_key_idx);
+
+ if (wep_key_idx > WEP_KEYS)
+ return -EINVAL;
+
+ RT_TRACE(_module_rtl871x_ioctl_os_c, _drv_info_, ("(2)wep_key_idx =%d\n", wep_key_idx));
+
+ if (wep_key_len > 0) {
+ wep_key_len = wep_key_len <= 5 ? 5 : 13;
+ wep_total_len = wep_key_len + FIELD_OFFSET(struct ndis_802_11_wep, KeyMaterial);
+ pwep = (struct ndis_802_11_wep *)rtw_malloc(wep_total_len);
+ if (pwep == NULL) {
+ RT_TRACE(_module_rtl871x_ioctl_os_c, _drv_err_, (" wpa_set_encryption: pwep allocate fail !!!\n"));
+ goto exit;
+ }
+ memset(pwep, 0, wep_total_len);
+ pwep->KeyLength = wep_key_len;
+ pwep->Length = wep_total_len;
+ if (wep_key_len == 13) {
+ padapter->securitypriv.dot11PrivacyAlgrthm = _WEP104_;
+ padapter->securitypriv.dot118021XGrpPrivacy = _WEP104_;
+ }
+ } else {
+ ret = -EINVAL;
+ goto exit;
+ }
+ pwep->KeyIndex = wep_key_idx;
+ pwep->KeyIndex |= 0x80000000;
+ memcpy(pwep->KeyMaterial, param->u.crypt.key, pwep->KeyLength);
+ if (param->u.crypt.set_tx) {
+ DBG_88E("wep, set_tx = 1\n");
+ if (rtw_set_802_11_add_wep(padapter, pwep) == (u8)_FAIL)
+ ret = -EOPNOTSUPP;
+ } else {
+ DBG_88E("wep, set_tx = 0\n");
+ if (wep_key_idx >= WEP_KEYS) {
+ ret = -EOPNOTSUPP;
+ goto exit;
+ }
+ memcpy(&(psecuritypriv->dot11DefKey[wep_key_idx].skey[0]), pwep->KeyMaterial, pwep->KeyLength);
+ psecuritypriv->dot11DefKeylen[wep_key_idx] = pwep->KeyLength;
+ rtw_set_key(padapter, psecuritypriv, wep_key_idx, 0);
+ }
+ goto exit;
+ }
+
+ if (padapter->securitypriv.dot11AuthAlgrthm == dot11AuthAlgrthm_8021X) { /* 802_1x */
+ struct sta_info *psta, *pbcmc_sta;
+ struct sta_priv *pstapriv = &padapter->stapriv;
+
+ if (check_fwstate(pmlmepriv, WIFI_STATION_STATE | WIFI_MP_STATE)) { /* sta mode */
+ psta = rtw_get_stainfo(pstapriv, get_bssid(pmlmepriv));
+ if (psta == NULL) {
+ ;
+ } else {
+ if (strcmp(param->u.crypt.alg, "none") != 0)
+ psta->ieee8021x_blocked = false;
+
+ if ((padapter->securitypriv.ndisencryptstatus == Ndis802_11Encryption2Enabled) ||
+ (padapter->securitypriv.ndisencryptstatus == Ndis802_11Encryption3Enabled))
+ psta->dot118021XPrivacy = padapter->securitypriv.dot11PrivacyAlgrthm;
+
+ if (param->u.crypt.set_tx == 1) { /* pairwise key */
+ memcpy(psta->dot118021x_UncstKey.skey, param->u.crypt.key, (param->u.crypt.key_len > 16 ? 16 : param->u.crypt.key_len));
+
+ if (strcmp(param->u.crypt.alg, "TKIP") == 0) { /* set mic key */
+ memcpy(psta->dot11tkiptxmickey.skey, &(param->u.crypt.key[16]), 8);
+ memcpy(psta->dot11tkiprxmickey.skey, &(param->u.crypt.key[24]), 8);
+ padapter->securitypriv.busetkipkey = false;
+ }
+
+ DBG_88E(" ~~~~set sta key:unicastkey\n");
+
+ rtw_setstakey_cmd(padapter, (unsigned char *)psta, true);
+ } else { /* group key */
+ memcpy(padapter->securitypriv.dot118021XGrpKey[param->u.crypt.idx].skey, param->u.crypt.key, (param->u.crypt.key_len > 16 ? 16 : param->u.crypt.key_len));
+ memcpy(padapter->securitypriv.dot118021XGrptxmickey[param->u.crypt.idx].skey, &(param->u.crypt.key[16]), 8);
+ memcpy(padapter->securitypriv.dot118021XGrprxmickey[param->u.crypt.idx].skey, &(param->u.crypt.key[24]), 8);
+ padapter->securitypriv.binstallGrpkey = true;
+ DBG_88E(" ~~~~set sta key:groupkey\n");
+
+ padapter->securitypriv.dot118021XGrpKeyid = param->u.crypt.idx;
+
+ rtw_set_key(padapter, &padapter->securitypriv, param->u.crypt.idx, 1);
+ }
+ }
+ pbcmc_sta = rtw_get_bcmc_stainfo(padapter);
+ if (pbcmc_sta == NULL) {
+ ;
+ } else {
+ /* Jeff: don't disable ieee8021x_blocked while clearing key */
+ if (strcmp(param->u.crypt.alg, "none") != 0)
+ pbcmc_sta->ieee8021x_blocked = false;
+
+ if ((padapter->securitypriv.ndisencryptstatus == Ndis802_11Encryption2Enabled) ||
+ (padapter->securitypriv.ndisencryptstatus == Ndis802_11Encryption3Enabled))
+ pbcmc_sta->dot118021XPrivacy = padapter->securitypriv.dot11PrivacyAlgrthm;
+ }
+ }
+ }
+
+exit:
+
+ kfree(pwep);
+ return ret;
+}
+
+static int rtw_set_wpa_ie(struct adapter *padapter, char *pie, unsigned short ielen)
+{
+ u8 *buf = NULL;
+ int group_cipher = 0, pairwise_cipher = 0;
+ int ret = 0;
+
+ if ((ielen > MAX_WPA_IE_LEN) || (pie == NULL)) {
+ _clr_fwstate_(&padapter->mlmepriv, WIFI_UNDER_WPS);
+ if (pie == NULL)
+ return ret;
+ else
+ return -EINVAL;
+ }
+
+ if (ielen) {
+ buf = kmemdup(pie, ielen, GFP_KERNEL);
+ if (buf == NULL) {
+ ret = -ENOMEM;
+ goto exit;
+ }
+
+ /* dump */
+ {
+ int i;
+ DBG_88E("\n wpa_ie(length:%d):\n", ielen);
+ for (i = 0; i < ielen; i += 8)
+ DBG_88E("0x%.2x 0x%.2x 0x%.2x 0x%.2x 0x%.2x 0x%.2x 0x%.2x 0x%.2x\n", buf[i], buf[i+1], buf[i+2], buf[i+3], buf[i+4], buf[i+5], buf[i+6], buf[i+7]);
+ }
+
+ if (ielen < RSN_HEADER_LEN) {
+ RT_TRACE(_module_rtl871x_ioctl_os_c, _drv_err_, ("Ie len too short %d\n", ielen));
+ ret = -1;
+ goto exit;
+ }
+
+ if (rtw_parse_wpa_ie(buf, ielen, &group_cipher, &pairwise_cipher, NULL) == _SUCCESS) {
+ padapter->securitypriv.dot11AuthAlgrthm = dot11AuthAlgrthm_8021X;
+ padapter->securitypriv.ndisauthtype = Ndis802_11AuthModeWPAPSK;
+ memcpy(padapter->securitypriv.supplicant_ie, &buf[0], ielen);
+ }
+
+ if (rtw_parse_wpa2_ie(buf, ielen, &group_cipher, &pairwise_cipher, NULL) == _SUCCESS) {
+ padapter->securitypriv.dot11AuthAlgrthm = dot11AuthAlgrthm_8021X;
+ padapter->securitypriv.ndisauthtype = Ndis802_11AuthModeWPA2PSK;
+ memcpy(padapter->securitypriv.supplicant_ie, &buf[0], ielen);
+ }
+
+ switch (group_cipher) {
+ case WPA_CIPHER_NONE:
+ padapter->securitypriv.dot118021XGrpPrivacy = _NO_PRIVACY_;
+ padapter->securitypriv.ndisencryptstatus = Ndis802_11EncryptionDisabled;
+ break;
+ case WPA_CIPHER_WEP40:
+ padapter->securitypriv.dot118021XGrpPrivacy = _WEP40_;
+ padapter->securitypriv.ndisencryptstatus = Ndis802_11Encryption1Enabled;
+ break;
+ case WPA_CIPHER_TKIP:
+ padapter->securitypriv.dot118021XGrpPrivacy = _TKIP_;
+ padapter->securitypriv.ndisencryptstatus = Ndis802_11Encryption2Enabled;
+ break;
+ case WPA_CIPHER_CCMP:
+ padapter->securitypriv.dot118021XGrpPrivacy = _AES_;
+ padapter->securitypriv.ndisencryptstatus = Ndis802_11Encryption3Enabled;
+ break;
+ case WPA_CIPHER_WEP104:
+ padapter->securitypriv.dot118021XGrpPrivacy = _WEP104_;
+ padapter->securitypriv.ndisencryptstatus = Ndis802_11Encryption1Enabled;
+ break;
+ }
+
+ switch (pairwise_cipher) {
+ case WPA_CIPHER_NONE:
+ padapter->securitypriv.dot11PrivacyAlgrthm = _NO_PRIVACY_;
+ padapter->securitypriv.ndisencryptstatus = Ndis802_11EncryptionDisabled;
+ break;
+ case WPA_CIPHER_WEP40:
+ padapter->securitypriv.dot11PrivacyAlgrthm = _WEP40_;
+ padapter->securitypriv.ndisencryptstatus = Ndis802_11Encryption1Enabled;
+ break;
+ case WPA_CIPHER_TKIP:
+ padapter->securitypriv.dot11PrivacyAlgrthm = _TKIP_;
+ padapter->securitypriv.ndisencryptstatus = Ndis802_11Encryption2Enabled;
+ break;
+ case WPA_CIPHER_CCMP:
+ padapter->securitypriv.dot11PrivacyAlgrthm = _AES_;
+ padapter->securitypriv.ndisencryptstatus = Ndis802_11Encryption3Enabled;
+ break;
+ case WPA_CIPHER_WEP104:
+ padapter->securitypriv.dot11PrivacyAlgrthm = _WEP104_;
+ padapter->securitypriv.ndisencryptstatus = Ndis802_11Encryption1Enabled;
+ break;
+ }
+
+ _clr_fwstate_(&padapter->mlmepriv, WIFI_UNDER_WPS);
+ {/* set wps_ie */
+ u16 cnt = 0;
+ u8 eid, wps_oui[4] = {0x0, 0x50, 0xf2, 0x04};
+
+ while (cnt < ielen) {
+ eid = buf[cnt];
+ if ((eid == _VENDOR_SPECIFIC_IE_) && (!memcmp(&buf[cnt+2], wps_oui, 4))) {
+ DBG_88E("SET WPS_IE\n");
+
+ padapter->securitypriv.wps_ie_len = ((buf[cnt+1]+2) < (MAX_WPA_IE_LEN<<2)) ? (buf[cnt+1]+2) : (MAX_WPA_IE_LEN<<2);
+
+ memcpy(padapter->securitypriv.wps_ie, &buf[cnt], padapter->securitypriv.wps_ie_len);
+
+ set_fwstate(&padapter->mlmepriv, WIFI_UNDER_WPS);
+ cnt += buf[cnt+1]+2;
+ break;
+ } else {
+ cnt += buf[cnt+1]+2; /* goto next */
+ }
+ }
+ }
+ }
+
+ RT_TRACE(_module_rtl871x_ioctl_os_c, _drv_info_,
+ ("rtw_set_wpa_ie: pairwise_cipher = 0x%08x padapter->securitypriv.ndisencryptstatus =%d padapter->securitypriv.ndisauthtype =%d\n",
+ pairwise_cipher, padapter->securitypriv.ndisencryptstatus, padapter->securitypriv.ndisauthtype));
+exit:
+ kfree(buf);
+ return ret;
+}
+
+typedef unsigned char NDIS_802_11_RATES_EX[NDIS_802_11_LENGTH_RATES_EX];
+
+static int rtw_wx_get_name(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct adapter *padapter = (struct adapter *)rtw_netdev_priv(dev);
+ u32 ht_ielen = 0;
+ char *p;
+ u8 ht_cap = false;
+ struct mlme_priv *pmlmepriv = &(padapter->mlmepriv);
+ struct wlan_bssid_ex *pcur_bss = &pmlmepriv->cur_network.network;
+ NDIS_802_11_RATES_EX *prates = NULL;
+
+ RT_TRACE(_module_rtl871x_mlme_c_, _drv_info_, ("cmd_code =%x\n", info->cmd));
+
+ if (check_fwstate(pmlmepriv, _FW_LINKED|WIFI_ADHOC_MASTER_STATE) == true) {
+ /* parsing HT_CAP_IE */
+ p = rtw_get_ie(&pcur_bss->IEs[12], _HT_CAPABILITY_IE_, &ht_ielen, pcur_bss->IELength-12);
+ if (p && ht_ielen > 0)
+ ht_cap = true;
+
+ prates = &pcur_bss->SupportedRates;
+
+ if (rtw_is_cckratesonly_included((u8 *)prates) == true) {
+ if (ht_cap)
+ snprintf(wrqu->name, IFNAMSIZ, "IEEE 802.11bn");
+ else
+ snprintf(wrqu->name, IFNAMSIZ, "IEEE 802.11b");
+ } else if ((rtw_is_cckrates_included((u8 *)prates)) == true) {
+ if (ht_cap)
+ snprintf(wrqu->name, IFNAMSIZ, "IEEE 802.11bgn");
+ else
+ snprintf(wrqu->name, IFNAMSIZ, "IEEE 802.11bg");
+ } else {
+ if (pcur_bss->Configuration.DSConfig > 14) {
+ if (ht_cap)
+ snprintf(wrqu->name, IFNAMSIZ, "IEEE 802.11an");
+ else
+ snprintf(wrqu->name, IFNAMSIZ, "IEEE 802.11a");
+ } else {
+ if (ht_cap)
+ snprintf(wrqu->name, IFNAMSIZ, "IEEE 802.11gn");
+ else
+ snprintf(wrqu->name, IFNAMSIZ, "IEEE 802.11g");
+ }
+ }
+ } else {
+ snprintf(wrqu->name, IFNAMSIZ, "unassociated");
+ }
+ return 0;
+}
+
+static int rtw_wx_set_freq(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ RT_TRACE(_module_rtl871x_mlme_c_, _drv_notice_, ("+rtw_wx_set_freq\n"));
+ return 0;
+}
+
+static int rtw_wx_get_freq(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct adapter *padapter = (struct adapter *)rtw_netdev_priv(dev);
+ struct mlme_priv *pmlmepriv = &(padapter->mlmepriv);
+ struct wlan_bssid_ex *pcur_bss = &pmlmepriv->cur_network.network;
+
+ if (check_fwstate(pmlmepriv, _FW_LINKED)) {
+ /* wrqu->freq.m = ieee80211_wlan_frequencies[pcur_bss->Configuration.DSConfig-1] * 100000; */
+ wrqu->freq.m = rtw_ch2freq(pcur_bss->Configuration.DSConfig) * 100000;
+ wrqu->freq.e = 1;
+ wrqu->freq.i = pcur_bss->Configuration.DSConfig;
+ } else {
+ wrqu->freq.m = rtw_ch2freq(padapter->mlmeextpriv.cur_channel) * 100000;
+ wrqu->freq.e = 1;
+ wrqu->freq.i = padapter->mlmeextpriv.cur_channel;
+ }
+
+ return 0;
+}
+
+static int rtw_wx_set_mode(struct net_device *dev, struct iw_request_info *a,
+ union iwreq_data *wrqu, char *b)
+{
+ struct adapter *padapter = (struct adapter *)rtw_netdev_priv(dev);
+ enum ndis_802_11_network_infra networkType;
+ int ret = 0;
+
+ if (_FAIL == rtw_pwr_wakeup(padapter)) {
+ ret = -EPERM;
+ goto exit;
+ }
+
+ if (!padapter->hw_init_completed) {
+ ret = -EPERM;
+ goto exit;
+ }
+
+ switch (wrqu->mode) {
+ case IW_MODE_AUTO:
+ networkType = Ndis802_11AutoUnknown;
+ DBG_88E("set_mode = IW_MODE_AUTO\n");
+ break;
+ case IW_MODE_ADHOC:
+ networkType = Ndis802_11IBSS;
+ DBG_88E("set_mode = IW_MODE_ADHOC\n");
+ break;
+ case IW_MODE_MASTER:
+ networkType = Ndis802_11APMode;
+ DBG_88E("set_mode = IW_MODE_MASTER\n");
+ break;
+ case IW_MODE_INFRA:
+ networkType = Ndis802_11Infrastructure;
+ DBG_88E("set_mode = IW_MODE_INFRA\n");
+ break;
+ default:
+ ret = -EINVAL;
+ RT_TRACE(_module_rtl871x_ioctl_os_c, _drv_err_, ("\n Mode: %s is not supported\n", iw_operation_mode[wrqu->mode]));
+ goto exit;
+ }
+ if (rtw_set_802_11_infrastructure_mode(padapter, networkType) == false) {
+ ret = -EPERM;
+ goto exit;
+ }
+ rtw_setopmode_cmd(padapter, networkType);
+exit:
+ return ret;
+}
+
+static int rtw_wx_get_mode(struct net_device *dev, struct iw_request_info *a,
+ union iwreq_data *wrqu, char *b)
+{
+ struct adapter *padapter = (struct adapter *)rtw_netdev_priv(dev);
+ struct mlme_priv *pmlmepriv = &(padapter->mlmepriv);
+
+ RT_TRACE(_module_rtl871x_mlme_c_, _drv_info_, (" rtw_wx_get_mode\n"));
+
+ if (check_fwstate(pmlmepriv, WIFI_STATION_STATE))
+ wrqu->mode = IW_MODE_INFRA;
+ else if ((check_fwstate(pmlmepriv, WIFI_ADHOC_MASTER_STATE)) ||
+ (check_fwstate(pmlmepriv, WIFI_ADHOC_STATE)))
+ wrqu->mode = IW_MODE_ADHOC;
+ else if (check_fwstate(pmlmepriv, WIFI_AP_STATE))
+ wrqu->mode = IW_MODE_MASTER;
+ else
+ wrqu->mode = IW_MODE_AUTO;
+
+ return 0;
+}
+
+static int rtw_wx_set_pmkid(struct net_device *dev,
+ struct iw_request_info *a,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct adapter *padapter = (struct adapter *)rtw_netdev_priv(dev);
+ u8 j, blInserted = false;
+ int ret = false;
+ struct security_priv *psecuritypriv = &padapter->securitypriv;
+ struct iw_pmksa *pPMK = (struct iw_pmksa *)extra;
+ u8 strZeroMacAddress[ETH_ALEN] = {0x00};
+ u8 strIssueBssid[ETH_ALEN] = {0x00};
+
+ memcpy(strIssueBssid, pPMK->bssid.sa_data, ETH_ALEN);
+ if (pPMK->cmd == IW_PMKSA_ADD) {
+ DBG_88E("[rtw_wx_set_pmkid] IW_PMKSA_ADD!\n");
+ if (!memcmp(strIssueBssid, strZeroMacAddress, ETH_ALEN))
+ return ret;
+ else
+ ret = true;
+ blInserted = false;
+
+ /* overwrite PMKID */
+ for (j = 0; j < NUM_PMKID_CACHE; j++) {
+ if (!memcmp(psecuritypriv->PMKIDList[j].Bssid, strIssueBssid, ETH_ALEN)) {
+ /* BSSID is matched, the same AP => rewrite with new PMKID. */
+ DBG_88E("[rtw_wx_set_pmkid] BSSID exists in the PMKList.\n");
+ memcpy(psecuritypriv->PMKIDList[j].PMKID, pPMK->pmkid, IW_PMKID_LEN);
+ psecuritypriv->PMKIDList[j].bUsed = true;
+ psecuritypriv->PMKIDIndex = j+1;
+ blInserted = true;
+ break;
+ }
+ }
+
+ if (!blInserted) {
+ /* Find a new entry */
+ DBG_88E("[rtw_wx_set_pmkid] Use the new entry index = %d for this PMKID.\n",
+ psecuritypriv->PMKIDIndex);
+
+ memcpy(psecuritypriv->PMKIDList[psecuritypriv->PMKIDIndex].Bssid, strIssueBssid, ETH_ALEN);
+ memcpy(psecuritypriv->PMKIDList[psecuritypriv->PMKIDIndex].PMKID, pPMK->pmkid, IW_PMKID_LEN);
+
+ psecuritypriv->PMKIDList[psecuritypriv->PMKIDIndex].bUsed = true;
+ psecuritypriv->PMKIDIndex++;
+ if (psecuritypriv->PMKIDIndex == 16)
+ psecuritypriv->PMKIDIndex = 0;
+ }
+ } else if (pPMK->cmd == IW_PMKSA_REMOVE) {
+ DBG_88E("[rtw_wx_set_pmkid] IW_PMKSA_REMOVE!\n");
+ ret = true;
+ for (j = 0; j < NUM_PMKID_CACHE; j++) {
+ if (!memcmp(psecuritypriv->PMKIDList[j].Bssid, strIssueBssid, ETH_ALEN)) {
+ /* BSSID is matched, the same AP => Remove this PMKID information and reset it. */
+ eth_zero_addr(psecuritypriv->PMKIDList[j].Bssid);
+ psecuritypriv->PMKIDList[j].bUsed = false;
+ break;
+ }
+ }
+ } else if (pPMK->cmd == IW_PMKSA_FLUSH) {
+ DBG_88E("[rtw_wx_set_pmkid] IW_PMKSA_FLUSH!\n");
+ memset(&psecuritypriv->PMKIDList[0], 0x00, sizeof(struct rt_pmkid_list) * NUM_PMKID_CACHE);
+ psecuritypriv->PMKIDIndex = 0;
+ ret = true;
+ }
+ return ret;
+}
+
+static int rtw_wx_get_sens(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ wrqu->sens.value = 0;
+ wrqu->sens.fixed = 0; /* no auto select */
+ wrqu->sens.disabled = 1;
+ return 0;
+}
+
+static int rtw_wx_get_range(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct iw_range *range = (struct iw_range *)extra;
+ struct adapter *padapter = (struct adapter *)rtw_netdev_priv(dev);
+ struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv;
+
+ u16 val;
+ int i;
+
+ RT_TRACE(_module_rtl871x_mlme_c_, _drv_info_, ("rtw_wx_get_range. cmd_code =%x\n", info->cmd));
+
+ wrqu->data.length = sizeof(*range);
+ memset(range, 0, sizeof(*range));
+
+ /* Let's try to keep this struct in the same order as in
+ * linux/include/wireless.h
+ */
+
+ /* TODO: See what values we can set, and remove the ones we can't
+ * set, or fill them with some default data.
+ */
+
+ /* ~5 Mb/s real (802.11b) */
+ range->throughput = 5 * 1000 * 1000;
+
+ /* signal level threshold range */
+
+ /* percent values between 0 and 100. */
+ range->max_qual.qual = 100;
+ range->max_qual.level = 100;
+ range->max_qual.noise = 100;
+ range->max_qual.updated = 7; /* Updated all three */
+
+ range->avg_qual.qual = 92; /* > 8% missed beacons is 'bad' */
+ /* TODO: Find real 'good' to 'bad' threshol value for RSSI */
+ range->avg_qual.level = 178; /* -78 dBm */
+ range->avg_qual.noise = 0;
+ range->avg_qual.updated = 7; /* Updated all three */
+
+ range->num_bitrates = RATE_COUNT;
+
+ for (i = 0; i < RATE_COUNT && i < IW_MAX_BITRATES; i++)
+ range->bitrate[i] = rtw_rates[i];
+
+ range->min_frag = MIN_FRAG_THRESHOLD;
+ range->max_frag = MAX_FRAG_THRESHOLD;
+
+ range->pm_capa = 0;
+
+ range->we_version_compiled = WIRELESS_EXT;
+ range->we_version_source = 16;
+
+ for (i = 0, val = 0; i < MAX_CHANNEL_NUM; i++) {
+ /* Include only legal frequencies for some countries */
+ if (pmlmeext->channel_set[i].ChannelNum != 0) {
+ range->freq[val].i = pmlmeext->channel_set[i].ChannelNum;
+ range->freq[val].m = rtw_ch2freq(pmlmeext->channel_set[i].ChannelNum) * 100000;
+ range->freq[val].e = 1;
+ val++;
+ }
+
+ if (val == IW_MAX_FREQUENCIES)
+ break;
+ }
+
+ range->num_channels = val;
+ range->num_frequency = val;
+
+/* The following code will proivde the security capability to network manager. */
+/* If the driver doesn't provide this capability to network manager, */
+/* the WPA/WPA2 routers can't be chosen in the network manager. */
+
+/*
+#define IW_SCAN_CAPA_NONE 0x00
+#define IW_SCAN_CAPA_ESSID 0x01
+#define IW_SCAN_CAPA_BSSID 0x02
+#define IW_SCAN_CAPA_CHANNEL 0x04
+#define IW_SCAN_CAPA_MODE 0x08
+#define IW_SCAN_CAPA_RATE 0x10
+#define IW_SCAN_CAPA_TYPE 0x20
+#define IW_SCAN_CAPA_TIME 0x40
+*/
+
+ range->enc_capa = IW_ENC_CAPA_WPA | IW_ENC_CAPA_WPA2 |
+ IW_ENC_CAPA_CIPHER_TKIP | IW_ENC_CAPA_CIPHER_CCMP;
+
+ range->scan_capa = IW_SCAN_CAPA_ESSID | IW_SCAN_CAPA_TYPE |
+ IW_SCAN_CAPA_BSSID | IW_SCAN_CAPA_CHANNEL |
+ IW_SCAN_CAPA_MODE | IW_SCAN_CAPA_RATE;
+ return 0;
+}
+
+/* set bssid flow */
+/* s1. rtw_set_802_11_infrastructure_mode() */
+/* s2. rtw_set_802_11_authentication_mode() */
+/* s3. set_802_11_encryption_mode() */
+/* s4. rtw_set_802_11_bssid() */
+static int rtw_wx_set_wap(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *awrq,
+ char *extra)
+{
+ uint ret = 0;
+ struct adapter *padapter = (struct adapter *)rtw_netdev_priv(dev);
+ struct sockaddr *temp = (struct sockaddr *)awrq;
+ struct mlme_priv *pmlmepriv = &(padapter->mlmepriv);
+ struct list_head *phead;
+ u8 *dst_bssid, *src_bssid;
+ struct __queue *queue = &(pmlmepriv->scanned_queue);
+ struct wlan_network *pnetwork = NULL;
+ enum ndis_802_11_auth_mode authmode;
+
+ if (_FAIL == rtw_pwr_wakeup(padapter)) {
+ ret = -1;
+ goto exit;
+ }
+
+ if (!padapter->bup) {
+ ret = -1;
+ goto exit;
+ }
+
+ if (temp->sa_family != ARPHRD_ETHER) {
+ ret = -EINVAL;
+ goto exit;
+ }
+
+ authmode = padapter->securitypriv.ndisauthtype;
+ spin_lock_bh(&queue->lock);
+ phead = get_list_head(queue);
+ pmlmepriv->pscanned = phead->next;
+
+ while (phead != pmlmepriv->pscanned) {
+ pnetwork = container_of(pmlmepriv->pscanned, struct wlan_network, list);
+
+ pmlmepriv->pscanned = pmlmepriv->pscanned->next;
+
+ dst_bssid = pnetwork->network.MacAddress;
+
+ src_bssid = temp->sa_data;
+
+ if ((!memcmp(dst_bssid, src_bssid, ETH_ALEN))) {
+ if (!rtw_set_802_11_infrastructure_mode(padapter, pnetwork->network.InfrastructureMode)) {
+ ret = -1;
+ spin_unlock_bh(&queue->lock);
+ goto exit;
+ }
+
+ break;
+ }
+ }
+ spin_unlock_bh(&queue->lock);
+
+ rtw_set_802_11_authentication_mode(padapter, authmode);
+ /* set_802_11_encryption_mode(padapter, padapter->securitypriv.ndisencryptstatus); */
+ if (rtw_set_802_11_bssid(padapter, temp->sa_data) == false) {
+ ret = -1;
+ goto exit;
+ }
+
+exit:
+
+ return ret;
+}
+
+static int rtw_wx_get_wap(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct adapter *padapter = (struct adapter *)rtw_netdev_priv(dev);
+ struct mlme_priv *pmlmepriv = &(padapter->mlmepriv);
+ struct wlan_bssid_ex *pcur_bss = &pmlmepriv->cur_network.network;
+
+ wrqu->ap_addr.sa_family = ARPHRD_ETHER;
+
+ eth_zero_addr(wrqu->ap_addr.sa_data);
+
+ RT_TRACE(_module_rtl871x_mlme_c_, _drv_info_, ("rtw_wx_get_wap\n"));
+
+ if (((check_fwstate(pmlmepriv, _FW_LINKED)) == true) ||
+ ((check_fwstate(pmlmepriv, WIFI_ADHOC_MASTER_STATE)) == true) ||
+ ((check_fwstate(pmlmepriv, WIFI_AP_STATE)) == true))
+ memcpy(wrqu->ap_addr.sa_data, pcur_bss->MacAddress, ETH_ALEN);
+ else
+ eth_zero_addr(wrqu->ap_addr.sa_data);
+ return 0;
+}
+
+static int rtw_wx_set_mlme(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ int ret = 0;
+ u16 reason;
+ struct adapter *padapter = (struct adapter *)rtw_netdev_priv(dev);
+ struct iw_mlme *mlme = (struct iw_mlme *)extra;
+
+ if (mlme == NULL)
+ return -1;
+
+ DBG_88E("%s\n", __func__);
+
+ reason = mlme->reason_code;
+
+ DBG_88E("%s, cmd =%d, reason =%d\n", __func__, mlme->cmd, reason);
+
+ switch (mlme->cmd) {
+ case IW_MLME_DEAUTH:
+ if (!rtw_set_802_11_disassociate(padapter))
+ ret = -1;
+ break;
+ case IW_MLME_DISASSOC:
+ if (!rtw_set_802_11_disassociate(padapter))
+ ret = -1;
+ break;
+ default:
+ return -EOPNOTSUPP;
+ }
+ return ret;
+}
+
+static int rtw_wx_set_scan(struct net_device *dev, struct iw_request_info *a,
+ union iwreq_data *wrqu, char *extra)
+{
+ u8 _status = false;
+ int ret = 0;
+ struct adapter *padapter = (struct adapter *)rtw_netdev_priv(dev);
+ struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
+ struct ndis_802_11_ssid ssid[RTW_SSID_SCAN_AMOUNT];
+ RT_TRACE(_module_rtl871x_mlme_c_, _drv_info_, ("rtw_wx_set_scan\n"));
+
+ if (padapter->registrypriv.mp_mode == 1) {
+ if (check_fwstate(pmlmepriv, WIFI_MP_STATE)) {
+ ret = -1;
+ goto exit;
+ }
+ }
+ if (_FAIL == rtw_pwr_wakeup(padapter)) {
+ ret = -1;
+ goto exit;
+ }
+
+ if (padapter->bDriverStopped) {
+ DBG_88E("bDriverStopped =%d\n", padapter->bDriverStopped);
+ ret = -1;
+ goto exit;
+ }
+
+ if (!padapter->bup) {
+ ret = -1;
+ goto exit;
+ }
+
+ if (!padapter->hw_init_completed) {
+ ret = -1;
+ goto exit;
+ }
+
+ /* When Busy Traffic, driver do not site survey. So driver return success. */
+ /* wpa_supplicant will not issue SIOCSIWSCAN cmd again after scan timeout. */
+ /* modify by thomas 2011-02-22. */
+ if (pmlmepriv->LinkDetectInfo.bBusyTraffic) {
+ indicate_wx_scan_complete_event(padapter);
+ goto exit;
+ }
+
+ if (check_fwstate(pmlmepriv, _FW_UNDER_SURVEY|_FW_UNDER_LINKING)) {
+ indicate_wx_scan_complete_event(padapter);
+ goto exit;
+ }
+
+/* For the DMP WiFi Display project, the driver won't to scan because */
+/* the pmlmepriv->scan_interval is always equal to 3. */
+/* So, the wpa_supplicant won't find out the WPS SoftAP. */
+
+ memset(ssid, 0, sizeof(struct ndis_802_11_ssid)*RTW_SSID_SCAN_AMOUNT);
+
+ if (wrqu->data.length == sizeof(struct iw_scan_req)) {
+ struct iw_scan_req *req = (struct iw_scan_req *)extra;
+
+ if (wrqu->data.flags & IW_SCAN_THIS_ESSID) {
+ int len = min_t(int, req->essid_len,
+ IW_ESSID_MAX_SIZE);
+
+ memcpy(ssid[0].Ssid, req->essid, len);
+ ssid[0].SsidLength = len;
+
+ DBG_88E("IW_SCAN_THIS_ESSID, ssid =%s, len =%d\n", req->essid, req->essid_len);
+
+ spin_lock_bh(&pmlmepriv->lock);
+
+ _status = rtw_sitesurvey_cmd(padapter, ssid, 1, NULL, 0);
+
+ spin_unlock_bh(&pmlmepriv->lock);
+ } else if (req->scan_type == IW_SCAN_TYPE_PASSIVE) {
+ DBG_88E("rtw_wx_set_scan, req->scan_type == IW_SCAN_TYPE_PASSIVE\n");
+ }
+ } else {
+ if (wrqu->data.length >= WEXT_CSCAN_HEADER_SIZE &&
+ !memcmp(extra, WEXT_CSCAN_HEADER, WEXT_CSCAN_HEADER_SIZE)) {
+ int len = wrqu->data.length - WEXT_CSCAN_HEADER_SIZE;
+ char *pos = extra+WEXT_CSCAN_HEADER_SIZE;
+ char section;
+ char sec_len;
+ int ssid_index = 0;
+
+ while (len >= 1) {
+ section = *(pos++);
+ len -= 1;
+
+ switch (section) {
+ case WEXT_CSCAN_SSID_SECTION:
+ if (len < 1) {
+ len = 0;
+ break;
+ }
+ sec_len = *(pos++); len -= 1;
+ if (sec_len > 0 && sec_len <= len) {
+ ssid[ssid_index].SsidLength = sec_len;
+ memcpy(ssid[ssid_index].Ssid, pos, ssid[ssid_index].SsidLength);
+ ssid_index++;
+ }
+ pos += sec_len;
+ len -= sec_len;
+ break;
+ case WEXT_CSCAN_TYPE_SECTION:
+ case WEXT_CSCAN_CHANNEL_SECTION:
+ pos += 1;
+ len -= 1;
+ break;
+ case WEXT_CSCAN_PASV_DWELL_SECTION:
+ case WEXT_CSCAN_HOME_DWELL_SECTION:
+ case WEXT_CSCAN_ACTV_DWELL_SECTION:
+ pos += 2;
+ len -= 2;
+ break;
+ default:
+ len = 0; /* stop parsing */
+ }
+ }
+
+ /* it has still some scan parameter to parse, we only do this now... */
+ _status = rtw_set_802_11_bssid_list_scan(padapter, ssid, RTW_SSID_SCAN_AMOUNT);
+ } else {
+ _status = rtw_set_802_11_bssid_list_scan(padapter, NULL, 0);
+ }
+ }
+
+ if (!_status)
+ ret = -1;
+
+exit:
+
+ return ret;
+}
+
+static int rtw_wx_get_scan(struct net_device *dev, struct iw_request_info *a,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct list_head *plist, *phead;
+ struct adapter *padapter = (struct adapter *)rtw_netdev_priv(dev);
+ struct mlme_priv *pmlmepriv = &(padapter->mlmepriv);
+ struct __queue *queue = &(pmlmepriv->scanned_queue);
+ struct wlan_network *pnetwork = NULL;
+ char *ev = extra;
+ char *stop = ev + wrqu->data.length;
+ u32 ret = 0;
+ u32 cnt = 0;
+ u32 wait_for_surveydone;
+ int wait_status;
+ RT_TRACE(_module_rtl871x_mlme_c_, _drv_info_, ("rtw_wx_get_scan\n"));
+ RT_TRACE(_module_rtl871x_ioctl_os_c, _drv_info_, (" Start of Query SIOCGIWSCAN .\n"));
+
+ if (padapter->pwrctrlpriv.brfoffbyhw && padapter->bDriverStopped) {
+ ret = -EINVAL;
+ goto exit;
+ }
+
+ wait_for_surveydone = 100;
+
+ wait_status = _FW_UNDER_SURVEY | _FW_UNDER_LINKING;
+
+ while (check_fwstate(pmlmepriv, wait_status)) {
+ msleep(30);
+ cnt++;
+ if (cnt > wait_for_surveydone)
+ break;
+ }
+
+ spin_lock_bh(&(pmlmepriv->scanned_queue.lock));
+
+ phead = get_list_head(queue);
+ plist = phead->next;
+
+ while (phead != plist) {
+ if ((stop - ev) < SCAN_ITEM_SIZE) {
+ ret = -E2BIG;
+ break;
+ }
+
+ pnetwork = container_of(plist, struct wlan_network, list);
+
+ /* report network only if the current channel set contains the channel to which this network belongs */
+ if (rtw_ch_set_search_ch(padapter->mlmeextpriv.channel_set, pnetwork->network.Configuration.DSConfig) >= 0)
+ ev = translate_scan(padapter, a, pnetwork, ev, stop);
+
+ plist = plist->next;
+ }
+
+ spin_unlock_bh(&pmlmepriv->scanned_queue.lock);
+
+ wrqu->data.length = ev-extra;
+ wrqu->data.flags = 0;
+
+exit:
+ return ret;
+}
+
+/* set ssid flow */
+/* s1. rtw_set_802_11_infrastructure_mode() */
+/* s2. set_802_11_authenticaion_mode() */
+/* s3. set_802_11_encryption_mode() */
+/* s4. rtw_set_802_11_ssid() */
+static int rtw_wx_set_essid(struct net_device *dev,
+ struct iw_request_info *a,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct adapter *padapter = (struct adapter *)rtw_netdev_priv(dev);
+ struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
+ struct __queue *queue = &pmlmepriv->scanned_queue;
+ struct list_head *phead;
+ struct wlan_network *pnetwork = NULL;
+ enum ndis_802_11_auth_mode authmode;
+ struct ndis_802_11_ssid ndis_ssid;
+ u8 *dst_ssid, *src_ssid;
+
+ uint ret = 0, len;
+
+
+ RT_TRACE(_module_rtl871x_ioctl_os_c, _drv_info_,
+ ("+rtw_wx_set_essid: fw_state = 0x%08x\n", get_fwstate(pmlmepriv)));
+ if (_FAIL == rtw_pwr_wakeup(padapter)) {
+ ret = -1;
+ goto exit;
+ }
+
+ if (!padapter->bup) {
+ ret = -1;
+ goto exit;
+ }
+
+ if (wrqu->essid.length > IW_ESSID_MAX_SIZE) {
+ ret = -E2BIG;
+ goto exit;
+ }
+
+ if (check_fwstate(pmlmepriv, WIFI_AP_STATE)) {
+ ret = -1;
+ goto exit;
+ }
+
+ authmode = padapter->securitypriv.ndisauthtype;
+ DBG_88E("=>%s\n", __func__);
+ if (wrqu->essid.flags && wrqu->essid.length) {
+ len = (wrqu->essid.length < IW_ESSID_MAX_SIZE) ? wrqu->essid.length : IW_ESSID_MAX_SIZE;
+
+ if (wrqu->essid.length != 33)
+ DBG_88E("ssid =%s, len =%d\n", extra, wrqu->essid.length);
+
+ memset(&ndis_ssid, 0, sizeof(struct ndis_802_11_ssid));
+ ndis_ssid.SsidLength = len;
+ memcpy(ndis_ssid.Ssid, extra, len);
+ src_ssid = ndis_ssid.Ssid;
+
+ RT_TRACE(_module_rtl871x_ioctl_os_c, _drv_info_, ("rtw_wx_set_essid: ssid =[%s]\n", src_ssid));
+ spin_lock_bh(&queue->lock);
+ phead = get_list_head(queue);
+ pmlmepriv->pscanned = phead->next;
+
+ while (phead != pmlmepriv->pscanned) {
+ pnetwork = container_of(pmlmepriv->pscanned, struct wlan_network, list);
+
+ pmlmepriv->pscanned = pmlmepriv->pscanned->next;
+
+ dst_ssid = pnetwork->network.Ssid.Ssid;
+
+ RT_TRACE(_module_rtl871x_ioctl_os_c, _drv_info_,
+ ("rtw_wx_set_essid: dst_ssid =%s\n",
+ pnetwork->network.Ssid.Ssid));
+
+ if ((!memcmp(dst_ssid, src_ssid, ndis_ssid.SsidLength)) &&
+ (pnetwork->network.Ssid.SsidLength == ndis_ssid.SsidLength)) {
+ RT_TRACE(_module_rtl871x_ioctl_os_c, _drv_info_,
+ ("rtw_wx_set_essid: find match, set infra mode\n"));
+
+ if (check_fwstate(pmlmepriv, WIFI_ADHOC_STATE) == true) {
+ if (pnetwork->network.InfrastructureMode != pmlmepriv->cur_network.network.InfrastructureMode)
+ continue;
+ }
+
+ if (!rtw_set_802_11_infrastructure_mode(padapter, pnetwork->network.InfrastructureMode)) {
+ ret = -1;
+ spin_unlock_bh(&queue->lock);
+ goto exit;
+ }
+
+ break;
+ }
+ }
+ spin_unlock_bh(&queue->lock);
+ RT_TRACE(_module_rtl871x_ioctl_os_c, _drv_info_,
+ ("set ssid: set_802_11_auth. mode =%d\n", authmode));
+ rtw_set_802_11_authentication_mode(padapter, authmode);
+ if (rtw_set_802_11_ssid(padapter, &ndis_ssid) == false) {
+ ret = -1;
+ goto exit;
+ }
+ }
+
+exit:
+
+ DBG_88E("<=%s, ret %d\n", __func__, ret);
+
+
+ return ret;
+}
+
+static int rtw_wx_get_essid(struct net_device *dev,
+ struct iw_request_info *a,
+ union iwreq_data *wrqu, char *extra)
+{
+ u32 len, ret = 0;
+ struct adapter *padapter = (struct adapter *)rtw_netdev_priv(dev);
+ struct mlme_priv *pmlmepriv = &(padapter->mlmepriv);
+ struct wlan_bssid_ex *pcur_bss = &pmlmepriv->cur_network.network;
+
+ RT_TRACE(_module_rtl871x_mlme_c_, _drv_info_, ("rtw_wx_get_essid\n"));
+
+
+ if ((check_fwstate(pmlmepriv, _FW_LINKED)) ||
+ (check_fwstate(pmlmepriv, WIFI_ADHOC_MASTER_STATE))) {
+ len = pcur_bss->Ssid.SsidLength;
+
+ wrqu->essid.length = len;
+
+ memcpy(extra, pcur_bss->Ssid.Ssid, len);
+
+ wrqu->essid.flags = 1;
+ } else {
+ ret = -1;
+ goto exit;
+ }
+
+exit:
+
+
+ return ret;
+}
+
+static int rtw_wx_set_rate(struct net_device *dev,
+ struct iw_request_info *a,
+ union iwreq_data *wrqu, char *extra)
+{
+ int i;
+ u8 datarates[NumRates];
+ u32 target_rate = wrqu->bitrate.value;
+ u32 fixed = wrqu->bitrate.fixed;
+ u32 ratevalue = 0;
+ u8 mpdatarate[NumRates] = {11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 0xff};
+
+
+ RT_TRACE(_module_rtl871x_mlme_c_, _drv_info_, (" rtw_wx_set_rate\n"));
+ RT_TRACE(_module_rtl871x_ioctl_os_c, _drv_info_, ("target_rate = %d, fixed = %d\n", target_rate, fixed));
+
+ if (target_rate == -1) {
+ ratevalue = 11;
+ goto set_rate;
+ }
+ target_rate = target_rate/100000;
+
+ switch (target_rate) {
+ case 10:
+ ratevalue = 0;
+ break;
+ case 20:
+ ratevalue = 1;
+ break;
+ case 55:
+ ratevalue = 2;
+ break;
+ case 60:
+ ratevalue = 3;
+ break;
+ case 90:
+ ratevalue = 4;
+ break;
+ case 110:
+ ratevalue = 5;
+ break;
+ case 120:
+ ratevalue = 6;
+ break;
+ case 180:
+ ratevalue = 7;
+ break;
+ case 240:
+ ratevalue = 8;
+ break;
+ case 360:
+ ratevalue = 9;
+ break;
+ case 480:
+ ratevalue = 10;
+ break;
+ case 540:
+ ratevalue = 11;
+ break;
+ default:
+ ratevalue = 11;
+ break;
+ }
+
+set_rate:
+
+ for (i = 0; i < NumRates; i++) {
+ if (ratevalue == mpdatarate[i]) {
+ datarates[i] = mpdatarate[i];
+ if (fixed == 0)
+ break;
+ } else {
+ datarates[i] = 0xff;
+ }
+
+ RT_TRACE(_module_rtl871x_ioctl_os_c, _drv_info_, ("datarate_inx =%d\n", datarates[i]));
+ }
+
+ return 0;
+}
+
+static int rtw_wx_get_rate(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ u16 max_rate = 0;
+
+ max_rate = rtw_get_cur_max_rate((struct adapter *)rtw_netdev_priv(dev));
+
+ if (max_rate == 0)
+ return -EPERM;
+
+ wrqu->bitrate.fixed = 0; /* no auto select */
+ wrqu->bitrate.value = max_rate * 100000;
+
+ return 0;
+}
+
+static int rtw_wx_set_rts(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct adapter *padapter = (struct adapter *)rtw_netdev_priv(dev);
+
+
+ if (wrqu->rts.disabled) {
+ padapter->registrypriv.rts_thresh = 2347;
+ } else {
+ if (wrqu->rts.value < 0 ||
+ wrqu->rts.value > 2347)
+ return -EINVAL;
+
+ padapter->registrypriv.rts_thresh = wrqu->rts.value;
+ }
+
+ DBG_88E("%s, rts_thresh =%d\n", __func__, padapter->registrypriv.rts_thresh);
+
+
+ return 0;
+}
+
+static int rtw_wx_get_rts(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct adapter *padapter = (struct adapter *)rtw_netdev_priv(dev);
+
+
+ DBG_88E("%s, rts_thresh =%d\n", __func__, padapter->registrypriv.rts_thresh);
+
+ wrqu->rts.value = padapter->registrypriv.rts_thresh;
+ wrqu->rts.fixed = 0; /* no auto select */
+ /* wrqu->rts.disabled = (wrqu->rts.value == DEFAULT_RTS_THRESHOLD); */
+
+
+ return 0;
+}
+
+static int rtw_wx_set_frag(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct adapter *padapter = (struct adapter *)rtw_netdev_priv(dev);
+
+
+ if (wrqu->frag.disabled) {
+ padapter->xmitpriv.frag_len = MAX_FRAG_THRESHOLD;
+ } else {
+ if (wrqu->frag.value < MIN_FRAG_THRESHOLD ||
+ wrqu->frag.value > MAX_FRAG_THRESHOLD)
+ return -EINVAL;
+
+ padapter->xmitpriv.frag_len = wrqu->frag.value & ~0x1;
+ }
+
+ DBG_88E("%s, frag_len =%d\n", __func__, padapter->xmitpriv.frag_len);
+
+
+ return 0;
+}
+
+static int rtw_wx_get_frag(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct adapter *padapter = (struct adapter *)rtw_netdev_priv(dev);
+
+
+ DBG_88E("%s, frag_len =%d\n", __func__, padapter->xmitpriv.frag_len);
+
+ wrqu->frag.value = padapter->xmitpriv.frag_len;
+ wrqu->frag.fixed = 0; /* no auto select */
+
+
+ return 0;
+}
+
+static int rtw_wx_get_retry(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ wrqu->retry.value = 7;
+ wrqu->retry.fixed = 0; /* no auto select */
+ wrqu->retry.disabled = 1;
+
+ return 0;
+}
+
+static int rtw_wx_set_enc(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *keybuf)
+{
+ u32 key, ret = 0;
+ u32 keyindex_provided;
+ struct ndis_802_11_wep wep;
+ enum ndis_802_11_auth_mode authmode;
+
+ struct iw_point *erq = &(wrqu->encoding);
+ struct adapter *padapter = (struct adapter *)rtw_netdev_priv(dev);
+ struct pwrctrl_priv *pwrpriv = &padapter->pwrctrlpriv;
+ DBG_88E("+rtw_wx_set_enc, flags = 0x%x\n", erq->flags);
+
+ memset(&wep, 0, sizeof(struct ndis_802_11_wep));
+
+ key = erq->flags & IW_ENCODE_INDEX;
+
+
+ if (erq->flags & IW_ENCODE_DISABLED) {
+ DBG_88E("EncryptionDisabled\n");
+ padapter->securitypriv.ndisencryptstatus = Ndis802_11EncryptionDisabled;
+ padapter->securitypriv.dot11PrivacyAlgrthm = _NO_PRIVACY_;
+ padapter->securitypriv.dot118021XGrpPrivacy = _NO_PRIVACY_;
+ padapter->securitypriv.dot11AuthAlgrthm = dot11AuthAlgrthm_Open; /* open system */
+ authmode = Ndis802_11AuthModeOpen;
+ padapter->securitypriv.ndisauthtype = authmode;
+
+ goto exit;
+ }
+
+ if (key) {
+ if (key > WEP_KEYS)
+ return -EINVAL;
+ key--;
+ keyindex_provided = 1;
+ } else {
+ keyindex_provided = 0;
+ key = padapter->securitypriv.dot11PrivacyKeyIndex;
+ DBG_88E("rtw_wx_set_enc, key =%d\n", key);
+ }
+
+ /* set authentication mode */
+ if (erq->flags & IW_ENCODE_OPEN) {
+ DBG_88E("rtw_wx_set_enc():IW_ENCODE_OPEN\n");
+ padapter->securitypriv.ndisencryptstatus = Ndis802_11Encryption1Enabled;/* Ndis802_11EncryptionDisabled; */
+ padapter->securitypriv.dot11AuthAlgrthm = dot11AuthAlgrthm_Open;
+ padapter->securitypriv.dot11PrivacyAlgrthm = _NO_PRIVACY_;
+ padapter->securitypriv.dot118021XGrpPrivacy = _NO_PRIVACY_;
+ authmode = Ndis802_11AuthModeOpen;
+ padapter->securitypriv.ndisauthtype = authmode;
+ } else if (erq->flags & IW_ENCODE_RESTRICTED) {
+ DBG_88E("rtw_wx_set_enc():IW_ENCODE_RESTRICTED\n");
+ padapter->securitypriv.ndisencryptstatus = Ndis802_11Encryption1Enabled;
+ padapter->securitypriv.dot11AuthAlgrthm = dot11AuthAlgrthm_Shared;
+ padapter->securitypriv.dot11PrivacyAlgrthm = _WEP40_;
+ padapter->securitypriv.dot118021XGrpPrivacy = _WEP40_;
+ authmode = Ndis802_11AuthModeShared;
+ padapter->securitypriv.ndisauthtype = authmode;
+ } else {
+ DBG_88E("rtw_wx_set_enc():erq->flags = 0x%x\n", erq->flags);
+
+ padapter->securitypriv.ndisencryptstatus = Ndis802_11Encryption1Enabled;/* Ndis802_11EncryptionDisabled; */
+ padapter->securitypriv.dot11AuthAlgrthm = dot11AuthAlgrthm_Open; /* open system */
+ padapter->securitypriv.dot11PrivacyAlgrthm = _NO_PRIVACY_;
+ padapter->securitypriv.dot118021XGrpPrivacy = _NO_PRIVACY_;
+ authmode = Ndis802_11AuthModeOpen;
+ padapter->securitypriv.ndisauthtype = authmode;
+ }
+
+ wep.KeyIndex = key;
+ if (erq->length > 0) {
+ wep.KeyLength = erq->length <= 5 ? 5 : 13;
+
+ wep.Length = wep.KeyLength + FIELD_OFFSET(struct ndis_802_11_wep, KeyMaterial);
+ } else {
+ wep.KeyLength = 0;
+
+ if (keyindex_provided == 1) {
+ /* set key_id only, no given KeyMaterial(erq->length == 0). */
+ padapter->securitypriv.dot11PrivacyKeyIndex = key;
+
+ DBG_88E("(keyindex_provided == 1), keyid =%d, key_len =%d\n", key, padapter->securitypriv.dot11DefKeylen[key]);
+
+ switch (padapter->securitypriv.dot11DefKeylen[key]) {
+ case 5:
+ padapter->securitypriv.dot11PrivacyAlgrthm = _WEP40_;
+ break;
+ case 13:
+ padapter->securitypriv.dot11PrivacyAlgrthm = _WEP104_;
+ break;
+ default:
+ padapter->securitypriv.dot11PrivacyAlgrthm = _NO_PRIVACY_;
+ break;
+ }
+
+ goto exit;
+ }
+ }
+
+ wep.KeyIndex |= 0x80000000;
+
+ memcpy(wep.KeyMaterial, keybuf, wep.KeyLength);
+
+ if (rtw_set_802_11_add_wep(padapter, &wep) == false) {
+ if (rf_on == pwrpriv->rf_pwrstate)
+ ret = -EOPNOTSUPP;
+ goto exit;
+ }
+
+exit:
+
+
+ return ret;
+}
+
+static int rtw_wx_get_enc(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *keybuf)
+{
+ uint key, ret = 0;
+ struct adapter *padapter = (struct adapter *)rtw_netdev_priv(dev);
+ struct iw_point *erq = &(wrqu->encoding);
+ struct mlme_priv *pmlmepriv = &(padapter->mlmepriv);
+
+
+ if (check_fwstate(pmlmepriv, _FW_LINKED) != true) {
+ if (!check_fwstate(pmlmepriv, WIFI_ADHOC_MASTER_STATE)) {
+ erq->length = 0;
+ erq->flags |= IW_ENCODE_DISABLED;
+ return 0;
+ }
+ }
+
+ key = erq->flags & IW_ENCODE_INDEX;
+
+ if (key) {
+ if (key > WEP_KEYS)
+ return -EINVAL;
+ key--;
+ } else {
+ key = padapter->securitypriv.dot11PrivacyKeyIndex;
+ }
+
+ erq->flags = key + 1;
+
+ switch (padapter->securitypriv.ndisencryptstatus) {
+ case Ndis802_11EncryptionNotSupported:
+ case Ndis802_11EncryptionDisabled:
+ erq->length = 0;
+ erq->flags |= IW_ENCODE_DISABLED;
+ break;
+ case Ndis802_11Encryption1Enabled:
+ erq->length = padapter->securitypriv.dot11DefKeylen[key];
+ if (erq->length) {
+ memcpy(keybuf, padapter->securitypriv.dot11DefKey[key].skey, padapter->securitypriv.dot11DefKeylen[key]);
+
+ erq->flags |= IW_ENCODE_ENABLED;
+
+ if (padapter->securitypriv.ndisauthtype == Ndis802_11AuthModeOpen)
+ erq->flags |= IW_ENCODE_OPEN;
+ else if (padapter->securitypriv.ndisauthtype == Ndis802_11AuthModeShared)
+ erq->flags |= IW_ENCODE_RESTRICTED;
+ } else {
+ erq->length = 0;
+ erq->flags |= IW_ENCODE_DISABLED;
+ }
+ break;
+ case Ndis802_11Encryption2Enabled:
+ case Ndis802_11Encryption3Enabled:
+ erq->length = 16;
+ erq->flags |= (IW_ENCODE_ENABLED | IW_ENCODE_OPEN | IW_ENCODE_NOKEY);
+ break;
+ default:
+ erq->length = 0;
+ erq->flags |= IW_ENCODE_DISABLED;
+ break;
+ }
+
+ return ret;
+}
+
+static int rtw_wx_get_power(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ wrqu->power.value = 0;
+ wrqu->power.fixed = 0; /* no auto select */
+ wrqu->power.disabled = 1;
+
+ return 0;
+}
+
+static int rtw_wx_set_gen_ie(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct adapter *padapter = (struct adapter *)rtw_netdev_priv(dev);
+
+ return rtw_set_wpa_ie(padapter, extra, wrqu->data.length);
+}
+
+static int rtw_wx_set_auth(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct adapter *padapter = (struct adapter *)rtw_netdev_priv(dev);
+ struct iw_param *param = (struct iw_param *)&(wrqu->param);
+ int ret = 0;
+
+ switch (param->flags & IW_AUTH_INDEX) {
+ case IW_AUTH_WPA_VERSION:
+ break;
+ case IW_AUTH_CIPHER_PAIRWISE:
+
+ break;
+ case IW_AUTH_CIPHER_GROUP:
+
+ break;
+ case IW_AUTH_KEY_MGMT:
+ /*
+ * ??? does not use these parameters
+ */
+ break;
+ case IW_AUTH_TKIP_COUNTERMEASURES:
+ if (param->value) {
+ /* wpa_supplicant is enabling the tkip countermeasure. */
+ padapter->securitypriv.btkip_countermeasure = true;
+ } else {
+ /* wpa_supplicant is disabling the tkip countermeasure. */
+ padapter->securitypriv.btkip_countermeasure = false;
+ }
+ break;
+ case IW_AUTH_DROP_UNENCRYPTED:
+ /* HACK:
+ *
+ * wpa_supplicant calls set_wpa_enabled when the driver
+ * is loaded and unloaded, regardless of if WPA is being
+ * used. No other calls are made which can be used to
+ * determine if encryption will be used or not prior to
+ * association being expected. If encryption is not being
+ * used, drop_unencrypted is set to false, else true -- we
+ * can use this to determine if the CAP_PRIVACY_ON bit should
+ * be set.
+ */
+
+ if (padapter->securitypriv.ndisencryptstatus == Ndis802_11Encryption1Enabled)
+ break;/* it means init value, or using wep, ndisencryptstatus = Ndis802_11Encryption1Enabled, */
+ /* then it needn't reset it; */
+
+ if (param->value) {
+ padapter->securitypriv.ndisencryptstatus = Ndis802_11EncryptionDisabled;
+ padapter->securitypriv.dot11PrivacyAlgrthm = _NO_PRIVACY_;
+ padapter->securitypriv.dot118021XGrpPrivacy = _NO_PRIVACY_;
+ padapter->securitypriv.dot11AuthAlgrthm = dot11AuthAlgrthm_Open; /* open system */
+ padapter->securitypriv.ndisauthtype = Ndis802_11AuthModeOpen;
+ }
+
+ break;
+ case IW_AUTH_80211_AUTH_ALG:
+ /*
+ * It's the starting point of a link layer connection using wpa_supplicant
+ */
+ if (check_fwstate(&padapter->mlmepriv, _FW_LINKED)) {
+ LeaveAllPowerSaveMode(padapter);
+ rtw_disassoc_cmd(padapter, 500, false);
+ DBG_88E("%s...call rtw_indicate_disconnect\n ", __func__);
+ rtw_indicate_disconnect(padapter);
+ rtw_free_assoc_resources(padapter, 1);
+ }
+ ret = wpa_set_auth_algs(dev, (u32)param->value);
+ break;
+ case IW_AUTH_WPA_ENABLED:
+ break;
+ case IW_AUTH_RX_UNENCRYPTED_EAPOL:
+ break;
+ case IW_AUTH_PRIVACY_INVOKED:
+ break;
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ return ret;
+}
+
+static int rtw_wx_set_enc_ext(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ char *alg_name;
+ u32 param_len;
+ struct ieee_param *param = NULL;
+ struct iw_point *pencoding = &wrqu->encoding;
+ struct iw_encode_ext *pext = (struct iw_encode_ext *)extra;
+ int ret = 0;
+
+ param_len = sizeof(struct ieee_param) + pext->key_len;
+ param = (struct ieee_param *)rtw_malloc(param_len);
+ if (param == NULL)
+ return -1;
+
+ memset(param, 0, param_len);
+
+ param->cmd = IEEE_CMD_SET_ENCRYPTION;
+ memset(param->sta_addr, 0xff, ETH_ALEN);
+
+ switch (pext->alg) {
+ case IW_ENCODE_ALG_NONE:
+ /* todo: remove key */
+ /* remove = 1; */
+ alg_name = "none";
+ break;
+ case IW_ENCODE_ALG_WEP:
+ alg_name = "WEP";
+ break;
+ case IW_ENCODE_ALG_TKIP:
+ alg_name = "TKIP";
+ break;
+ case IW_ENCODE_ALG_CCMP:
+ alg_name = "CCMP";
+ break;
+ default:
+ ret = -1;
+ goto exit;
+ }
+
+ strncpy((char *)param->u.crypt.alg, alg_name, IEEE_CRYPT_ALG_NAME_LEN);
+
+ if (pext->ext_flags & IW_ENCODE_EXT_SET_TX_KEY)
+ param->u.crypt.set_tx = 1;
+
+ /* cliW: WEP does not have group key
+ * just not checking GROUP key setting
+ */
+ if ((pext->alg != IW_ENCODE_ALG_WEP) &&
+ (pext->ext_flags & IW_ENCODE_EXT_GROUP_KEY))
+ param->u.crypt.set_tx = 0;
+
+ param->u.crypt.idx = (pencoding->flags&0x00FF) - 1;
+
+ if (pext->ext_flags & IW_ENCODE_EXT_RX_SEQ_VALID)
+ memcpy(param->u.crypt.seq, pext->rx_seq, 8);
+
+ if (pext->key_len) {
+ param->u.crypt.key_len = pext->key_len;
+ memcpy(param->u.crypt.key, pext + 1, pext->key_len);
+ }
+
+ ret = wpa_set_encryption(dev, param, param_len);
+
+exit:
+ kfree(param);
+ return ret;
+}
+
+static int rtw_wx_get_nick(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ if (extra) {
+ wrqu->data.length = 14;
+ wrqu->data.flags = 1;
+ memcpy(extra, "<WIFI@REALTEK>", 14);
+ }
+
+ /* dump debug info here */
+ return 0;
+}
+
+static int dummy(struct net_device *dev, struct iw_request_info *a,
+ union iwreq_data *wrqu, char *b)
+{
+ return -1;
+}
+
+static int wpa_set_param(struct net_device *dev, u8 name, u32 value)
+{
+ uint ret = 0;
+ struct adapter *padapter = (struct adapter *)rtw_netdev_priv(dev);
+
+ switch (name) {
+ case IEEE_PARAM_WPA_ENABLED:
+ padapter->securitypriv.dot11AuthAlgrthm = dot11AuthAlgrthm_8021X; /* 802.1x */
+ switch ((value)&0xff) {
+ case 1: /* WPA */
+ padapter->securitypriv.ndisauthtype = Ndis802_11AuthModeWPAPSK; /* WPA_PSK */
+ padapter->securitypriv.ndisencryptstatus = Ndis802_11Encryption2Enabled;
+ break;
+ case 2: /* WPA2 */
+ padapter->securitypriv.ndisauthtype = Ndis802_11AuthModeWPA2PSK; /* WPA2_PSK */
+ padapter->securitypriv.ndisencryptstatus = Ndis802_11Encryption3Enabled;
+ break;
+ }
+ RT_TRACE(_module_rtl871x_ioctl_os_c, _drv_info_,
+ ("wpa_set_param:padapter->securitypriv.ndisauthtype =%d\n", padapter->securitypriv.ndisauthtype));
+ break;
+ case IEEE_PARAM_TKIP_COUNTERMEASURES:
+ break;
+ case IEEE_PARAM_DROP_UNENCRYPTED: {
+ /* HACK:
+ *
+ * wpa_supplicant calls set_wpa_enabled when the driver
+ * is loaded and unloaded, regardless of if WPA is being
+ * used. No other calls are made which can be used to
+ * determine if encryption will be used or not prior to
+ * association being expected. If encryption is not being
+ * used, drop_unencrypted is set to false, else true -- we
+ * can use this to determine if the CAP_PRIVACY_ON bit should
+ * be set.
+ */
+
+ break;
+ }
+ case IEEE_PARAM_PRIVACY_INVOKED:
+ break;
+
+ case IEEE_PARAM_AUTH_ALGS:
+ ret = wpa_set_auth_algs(dev, value);
+ break;
+ case IEEE_PARAM_IEEE_802_1X:
+ break;
+ case IEEE_PARAM_WPAX_SELECT:
+ break;
+ default:
+ ret = -EOPNOTSUPP;
+ break;
+ }
+ return ret;
+}
+
+static int wpa_mlme(struct net_device *dev, u32 command, u32 reason)
+{
+ int ret = 0;
+ struct adapter *padapter = (struct adapter *)rtw_netdev_priv(dev);
+
+ switch (command) {
+ case IEEE_MLME_STA_DEAUTH:
+ if (!rtw_set_802_11_disassociate(padapter))
+ ret = -1;
+ break;
+ case IEEE_MLME_STA_DISASSOC:
+ if (!rtw_set_802_11_disassociate(padapter))
+ ret = -1;
+ break;
+ default:
+ ret = -EOPNOTSUPP;
+ break;
+ }
+
+ return ret;
+}
+
+static int wpa_supplicant_ioctl(struct net_device *dev, struct iw_point *p)
+{
+ struct ieee_param *param;
+ uint ret = 0;
+
+ if (p->length < sizeof(struct ieee_param) || !p->pointer) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ param = (struct ieee_param *)rtw_malloc(p->length);
+ if (param == NULL) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ if (copy_from_user(param, p->pointer, p->length)) {
+ kfree(param);
+ ret = -EFAULT;
+ goto out;
+ }
+
+ switch (param->cmd) {
+ case IEEE_CMD_SET_WPA_PARAM:
+ ret = wpa_set_param(dev, param->u.wpa_param.name, param->u.wpa_param.value);
+ break;
+
+ case IEEE_CMD_SET_WPA_IE:
+ ret = rtw_set_wpa_ie((struct adapter *)rtw_netdev_priv(dev),
+ (char *)param->u.wpa_ie.data, (u16)param->u.wpa_ie.len);
+ break;
+
+ case IEEE_CMD_SET_ENCRYPTION:
+ ret = wpa_set_encryption(dev, param, p->length);
+ break;
+
+ case IEEE_CMD_MLME:
+ ret = wpa_mlme(dev, param->u.mlme.command, param->u.mlme.reason_code);
+ break;
+
+ default:
+ DBG_88E("Unknown WPA supplicant request: %d\n", param->cmd);
+ ret = -EOPNOTSUPP;
+ break;
+ }
+
+ if (ret == 0 && copy_to_user(p->pointer, param, p->length))
+ ret = -EFAULT;
+
+ kfree(param);
+
+out:
+
+ return ret;
+}
+
+#ifdef CONFIG_88EU_AP_MODE
+static u8 set_pairwise_key(struct adapter *padapter, struct sta_info *psta)
+{
+ struct cmd_obj *ph2c;
+ struct set_stakey_parm *psetstakey_para;
+ struct cmd_priv *pcmdpriv = &padapter->cmdpriv;
+ u8 res = _SUCCESS;
+
+ ph2c = kzalloc(sizeof(struct cmd_obj), GFP_KERNEL);
+ if (ph2c == NULL) {
+ res = _FAIL;
+ goto exit;
+ }
+
+ psetstakey_para = kzalloc(sizeof(struct set_stakey_parm), GFP_KERNEL);
+ if (psetstakey_para == NULL) {
+ kfree(ph2c);
+ res = _FAIL;
+ goto exit;
+ }
+
+ init_h2fwcmd_w_parm_no_rsp(ph2c, psetstakey_para, _SetStaKey_CMD_);
+
+ psetstakey_para->algorithm = (u8)psta->dot118021XPrivacy;
+
+ memcpy(psetstakey_para->addr, psta->hwaddr, ETH_ALEN);
+
+ memcpy(psetstakey_para->key, &psta->dot118021x_UncstKey, 16);
+
+ res = rtw_enqueue_cmd(pcmdpriv, ph2c);
+
+exit:
+
+ return res;
+}
+
+static int set_group_key(struct adapter *padapter, u8 *key, u8 alg, int keyid)
+{
+ u8 keylen;
+ struct cmd_obj *pcmd;
+ struct setkey_parm *psetkeyparm;
+ struct cmd_priv *pcmdpriv = &(padapter->cmdpriv);
+ int res = _SUCCESS;
+
+ DBG_88E("%s\n", __func__);
+
+ pcmd = kzalloc(sizeof(struct cmd_obj), GFP_KERNEL);
+ if (pcmd == NULL) {
+ res = _FAIL;
+ goto exit;
+ }
+ psetkeyparm = kzalloc(sizeof(struct setkey_parm), GFP_KERNEL);
+ if (psetkeyparm == NULL) {
+ kfree(pcmd);
+ res = _FAIL;
+ goto exit;
+ }
+
+ memset(psetkeyparm, 0, sizeof(struct setkey_parm));
+
+ psetkeyparm->keyid = (u8)keyid;
+
+ psetkeyparm->algorithm = alg;
+
+ psetkeyparm->set_tx = 1;
+
+ switch (alg) {
+ case _WEP40_:
+ keylen = 5;
+ break;
+ case _WEP104_:
+ keylen = 13;
+ break;
+ case _TKIP_:
+ case _TKIP_WTMIC_:
+ case _AES_:
+ default:
+ keylen = 16;
+ }
+
+ memcpy(&(psetkeyparm->key[0]), key, keylen);
+
+ pcmd->cmdcode = _SetKey_CMD_;
+ pcmd->parmbuf = (u8 *)psetkeyparm;
+ pcmd->cmdsz = (sizeof(struct setkey_parm));
+ pcmd->rsp = NULL;
+ pcmd->rspsz = 0;
+
+ INIT_LIST_HEAD(&pcmd->list);
+
+ res = rtw_enqueue_cmd(pcmdpriv, pcmd);
+
+exit:
+
+ return res;
+}
+
+static int set_wep_key(struct adapter *padapter, u8 *key, u8 keylen, int keyid)
+{
+ u8 alg;
+
+ switch (keylen) {
+ case 5:
+ alg = _WEP40_;
+ break;
+ case 13:
+ alg = _WEP104_;
+ break;
+ default:
+ alg = _NO_PRIVACY_;
+ }
+
+ return set_group_key(padapter, key, alg, keyid);
+}
+
+static int rtw_set_encryption(struct net_device *dev, struct ieee_param *param, u32 param_len)
+{
+ int ret = 0;
+ u32 wep_key_idx, wep_key_len, wep_total_len;
+ struct ndis_802_11_wep *pwep = NULL;
+ struct sta_info *psta = NULL, *pbcmc_sta = NULL;
+ struct adapter *padapter = (struct adapter *)rtw_netdev_priv(dev);
+ struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
+ struct security_priv *psecuritypriv = &(padapter->securitypriv);
+ struct sta_priv *pstapriv = &padapter->stapriv;
+
+ DBG_88E("%s\n", __func__);
+ param->u.crypt.err = 0;
+ param->u.crypt.alg[IEEE_CRYPT_ALG_NAME_LEN - 1] = '\0';
+ if (param_len != sizeof(struct ieee_param) + param->u.crypt.key_len) {
+ ret = -EINVAL;
+ goto exit;
+ }
+ if (param->sta_addr[0] == 0xff && param->sta_addr[1] == 0xff &&
+ param->sta_addr[2] == 0xff && param->sta_addr[3] == 0xff &&
+ param->sta_addr[4] == 0xff && param->sta_addr[5] == 0xff) {
+ if (param->u.crypt.idx >= WEP_KEYS) {
+ ret = -EINVAL;
+ goto exit;
+ }
+ } else {
+ psta = rtw_get_stainfo(pstapriv, param->sta_addr);
+ if (!psta) {
+ DBG_88E("rtw_set_encryption(), sta has already been removed or never been added\n");
+ goto exit;
+ }
+ }
+
+ if (strcmp(param->u.crypt.alg, "none") == 0 && (psta == NULL)) {
+ /* todo:clear default encryption keys */
+
+ DBG_88E("clear default encryption keys, keyid =%d\n", param->u.crypt.idx);
+ goto exit;
+ }
+ if (strcmp(param->u.crypt.alg, "WEP") == 0 && (psta == NULL)) {
+ DBG_88E("r871x_set_encryption, crypt.alg = WEP\n");
+ wep_key_idx = param->u.crypt.idx;
+ wep_key_len = param->u.crypt.key_len;
+ DBG_88E("r871x_set_encryption, wep_key_idx=%d, len=%d\n", wep_key_idx, wep_key_len);
+ if ((wep_key_idx >= WEP_KEYS) || (wep_key_len <= 0)) {
+ ret = -EINVAL;
+ goto exit;
+ }
+
+ if (wep_key_len > 0) {
+ wep_key_len = wep_key_len <= 5 ? 5 : 13;
+ wep_total_len = wep_key_len + FIELD_OFFSET(struct ndis_802_11_wep, KeyMaterial);
+ pwep = (struct ndis_802_11_wep *)rtw_malloc(wep_total_len);
+ if (pwep == NULL) {
+ DBG_88E(" r871x_set_encryption: pwep allocate fail !!!\n");
+ goto exit;
+ }
+
+ memset(pwep, 0, wep_total_len);
+
+ pwep->KeyLength = wep_key_len;
+ pwep->Length = wep_total_len;
+ }
+
+ pwep->KeyIndex = wep_key_idx;
+
+ memcpy(pwep->KeyMaterial, param->u.crypt.key, pwep->KeyLength);
+
+ if (param->u.crypt.set_tx) {
+ DBG_88E("wep, set_tx = 1\n");
+
+ psecuritypriv->ndisencryptstatus = Ndis802_11Encryption1Enabled;
+ psecuritypriv->dot11PrivacyAlgrthm = _WEP40_;
+ psecuritypriv->dot118021XGrpPrivacy = _WEP40_;
+
+ if (pwep->KeyLength == 13) {
+ psecuritypriv->dot11PrivacyAlgrthm = _WEP104_;
+ psecuritypriv->dot118021XGrpPrivacy = _WEP104_;
+ }
+
+ psecuritypriv->dot11PrivacyKeyIndex = wep_key_idx;
+
+ memcpy(&(psecuritypriv->dot11DefKey[wep_key_idx].skey[0]), pwep->KeyMaterial, pwep->KeyLength);
+
+ psecuritypriv->dot11DefKeylen[wep_key_idx] = pwep->KeyLength;
+
+ set_wep_key(padapter, pwep->KeyMaterial, pwep->KeyLength, wep_key_idx);
+ } else {
+ DBG_88E("wep, set_tx = 0\n");
+
+ /* don't update "psecuritypriv->dot11PrivacyAlgrthm" and */
+ /* psecuritypriv->dot11PrivacyKeyIndex = keyid", but can rtw_set_key to cam */
+
+ memcpy(&(psecuritypriv->dot11DefKey[wep_key_idx].skey[0]), pwep->KeyMaterial, pwep->KeyLength);
+
+ psecuritypriv->dot11DefKeylen[wep_key_idx] = pwep->KeyLength;
+
+ set_wep_key(padapter, pwep->KeyMaterial, pwep->KeyLength, wep_key_idx);
+ }
+
+ goto exit;
+ }
+
+ if (!psta && check_fwstate(pmlmepriv, WIFI_AP_STATE)) { /* group key */
+ if (param->u.crypt.set_tx == 1) {
+ if (strcmp(param->u.crypt.alg, "WEP") == 0) {
+ DBG_88E("%s, set group_key, WEP\n", __func__);
+
+ memcpy(psecuritypriv->dot118021XGrpKey[param->u.crypt.idx].skey,
+ param->u.crypt.key, (param->u.crypt.key_len > 16 ? 16 : param->u.crypt.key_len));
+
+ psecuritypriv->dot118021XGrpPrivacy = _WEP40_;
+ if (param->u.crypt.key_len == 13)
+ psecuritypriv->dot118021XGrpPrivacy = _WEP104_;
+ } else if (strcmp(param->u.crypt.alg, "TKIP") == 0) {
+ DBG_88E("%s, set group_key, TKIP\n", __func__);
+ psecuritypriv->dot118021XGrpPrivacy = _TKIP_;
+ memcpy(psecuritypriv->dot118021XGrpKey[param->u.crypt.idx].skey,
+ param->u.crypt.key, (param->u.crypt.key_len > 16 ? 16 : param->u.crypt.key_len));
+ /* set mic key */
+ memcpy(psecuritypriv->dot118021XGrptxmickey[param->u.crypt.idx].skey, &(param->u.crypt.key[16]), 8);
+ memcpy(psecuritypriv->dot118021XGrprxmickey[param->u.crypt.idx].skey, &(param->u.crypt.key[24]), 8);
+
+ psecuritypriv->busetkipkey = true;
+ } else if (strcmp(param->u.crypt.alg, "CCMP") == 0) {
+ DBG_88E("%s, set group_key, CCMP\n", __func__);
+ psecuritypriv->dot118021XGrpPrivacy = _AES_;
+ memcpy(psecuritypriv->dot118021XGrpKey[param->u.crypt.idx].skey,
+ param->u.crypt.key, (param->u.crypt.key_len > 16 ? 16 : param->u.crypt.key_len));
+ } else {
+ DBG_88E("%s, set group_key, none\n", __func__);
+ psecuritypriv->dot118021XGrpPrivacy = _NO_PRIVACY_;
+ }
+ psecuritypriv->dot118021XGrpKeyid = param->u.crypt.idx;
+ psecuritypriv->binstallGrpkey = true;
+ psecuritypriv->dot11PrivacyAlgrthm = psecuritypriv->dot118021XGrpPrivacy;/* */
+ set_group_key(padapter, param->u.crypt.key, psecuritypriv->dot118021XGrpPrivacy, param->u.crypt.idx);
+ pbcmc_sta = rtw_get_bcmc_stainfo(padapter);
+ if (pbcmc_sta) {
+ pbcmc_sta->ieee8021x_blocked = false;
+ pbcmc_sta->dot118021XPrivacy = psecuritypriv->dot118021XGrpPrivacy;/* rx will use bmc_sta's dot118021XPrivacy */
+ }
+ }
+ goto exit;
+ }
+
+ if (psecuritypriv->dot11AuthAlgrthm == dot11AuthAlgrthm_8021X && psta) { /* psk/802_1x */
+ if (check_fwstate(pmlmepriv, WIFI_AP_STATE)) {
+ if (param->u.crypt.set_tx == 1) {
+ memcpy(psta->dot118021x_UncstKey.skey, param->u.crypt.key, (param->u.crypt.key_len > 16 ? 16 : param->u.crypt.key_len));
+
+ if (strcmp(param->u.crypt.alg, "WEP") == 0) {
+ DBG_88E("%s, set pairwise key, WEP\n", __func__);
+
+ psta->dot118021XPrivacy = _WEP40_;
+ if (param->u.crypt.key_len == 13)
+ psta->dot118021XPrivacy = _WEP104_;
+ } else if (strcmp(param->u.crypt.alg, "TKIP") == 0) {
+ DBG_88E("%s, set pairwise key, TKIP\n", __func__);
+
+ psta->dot118021XPrivacy = _TKIP_;
+
+ /* set mic key */
+ memcpy(psta->dot11tkiptxmickey.skey, &(param->u.crypt.key[16]), 8);
+ memcpy(psta->dot11tkiprxmickey.skey, &(param->u.crypt.key[24]), 8);
+
+ psecuritypriv->busetkipkey = true;
+ } else if (strcmp(param->u.crypt.alg, "CCMP") == 0) {
+ DBG_88E("%s, set pairwise key, CCMP\n", __func__);
+
+ psta->dot118021XPrivacy = _AES_;
+ } else {
+ DBG_88E("%s, set pairwise key, none\n", __func__);
+
+ psta->dot118021XPrivacy = _NO_PRIVACY_;
+ }
+
+ set_pairwise_key(padapter, psta);
+
+ psta->ieee8021x_blocked = false;
+ } else { /* group key??? */
+ if (strcmp(param->u.crypt.alg, "WEP") == 0) {
+ memcpy(psecuritypriv->dot118021XGrpKey[param->u.crypt.idx].skey,
+ param->u.crypt.key, (param->u.crypt.key_len > 16 ? 16 : param->u.crypt.key_len));
+ psecuritypriv->dot118021XGrpPrivacy = _WEP40_;
+ if (param->u.crypt.key_len == 13)
+ psecuritypriv->dot118021XGrpPrivacy = _WEP104_;
+ } else if (strcmp(param->u.crypt.alg, "TKIP") == 0) {
+ psecuritypriv->dot118021XGrpPrivacy = _TKIP_;
+
+ memcpy(psecuritypriv->dot118021XGrpKey[param->u.crypt.idx].skey,
+ param->u.crypt.key, (param->u.crypt.key_len > 16 ? 16 : param->u.crypt.key_len));
+
+ /* set mic key */
+ memcpy(psecuritypriv->dot118021XGrptxmickey[param->u.crypt.idx].skey, &(param->u.crypt.key[16]), 8);
+ memcpy(psecuritypriv->dot118021XGrprxmickey[param->u.crypt.idx].skey, &(param->u.crypt.key[24]), 8);
+
+ psecuritypriv->busetkipkey = true;
+ } else if (strcmp(param->u.crypt.alg, "CCMP") == 0) {
+ psecuritypriv->dot118021XGrpPrivacy = _AES_;
+
+ memcpy(psecuritypriv->dot118021XGrpKey[param->u.crypt.idx].skey,
+ param->u.crypt.key, (param->u.crypt.key_len > 16 ? 16 : param->u.crypt.key_len));
+ } else {
+ psecuritypriv->dot118021XGrpPrivacy = _NO_PRIVACY_;
+ }
+
+ psecuritypriv->dot118021XGrpKeyid = param->u.crypt.idx;
+
+ psecuritypriv->binstallGrpkey = true;
+
+ psecuritypriv->dot11PrivacyAlgrthm = psecuritypriv->dot118021XGrpPrivacy;/* */
+
+ set_group_key(padapter, param->u.crypt.key, psecuritypriv->dot118021XGrpPrivacy, param->u.crypt.idx);
+
+ pbcmc_sta = rtw_get_bcmc_stainfo(padapter);
+ if (pbcmc_sta) {
+ pbcmc_sta->ieee8021x_blocked = false;
+ pbcmc_sta->dot118021XPrivacy = psecuritypriv->dot118021XGrpPrivacy;/* rx will use bmc_sta's dot118021XPrivacy */
+ }
+ }
+ }
+ }
+
+exit:
+
+ kfree(pwep);
+
+ return ret;
+}
+
+static int rtw_set_beacon(struct net_device *dev, struct ieee_param *param, int len)
+{
+ int ret = 0;
+ struct adapter *padapter = (struct adapter *)rtw_netdev_priv(dev);
+ struct mlme_priv *pmlmepriv = &(padapter->mlmepriv);
+ struct sta_priv *pstapriv = &padapter->stapriv;
+ unsigned char *pbuf = param->u.bcn_ie.buf;
+
+ DBG_88E("%s, len =%d\n", __func__, len);
+
+ if (check_fwstate(pmlmepriv, WIFI_AP_STATE) != true)
+ return -EINVAL;
+
+ memcpy(&pstapriv->max_num_sta, param->u.bcn_ie.reserved, 2);
+
+ if ((pstapriv->max_num_sta > NUM_STA) || (pstapriv->max_num_sta <= 0))
+ pstapriv->max_num_sta = NUM_STA;
+
+ if (rtw_check_beacon_data(padapter, pbuf, (len-12-2)) == _SUCCESS)/* 12 = param header, 2:no packed */
+ ret = 0;
+ else
+ ret = -EINVAL;
+
+ return ret;
+}
+
+static int rtw_hostapd_sta_flush(struct net_device *dev)
+{
+ int ret = 0;
+ struct adapter *padapter = (struct adapter *)rtw_netdev_priv(dev);
+
+ DBG_88E("%s\n", __func__);
+
+ flush_all_cam_entry(padapter); /* clear CAM */
+
+ ret = rtw_sta_flush(padapter);
+
+ return ret;
+}
+
+static int rtw_add_sta(struct net_device *dev, struct ieee_param *param)
+{
+ int ret = 0;
+ struct sta_info *psta = NULL;
+ struct adapter *padapter = (struct adapter *)rtw_netdev_priv(dev);
+ struct mlme_priv *pmlmepriv = &(padapter->mlmepriv);
+ struct sta_priv *pstapriv = &padapter->stapriv;
+
+ DBG_88E("rtw_add_sta(aid =%d) =%pM\n", param->u.add_sta.aid, (param->sta_addr));
+
+ if (!check_fwstate(pmlmepriv, (_FW_LINKED|WIFI_AP_STATE)))
+ return -EINVAL;
+
+ if (param->sta_addr[0] == 0xff && param->sta_addr[1] == 0xff &&
+ param->sta_addr[2] == 0xff && param->sta_addr[3] == 0xff &&
+ param->sta_addr[4] == 0xff && param->sta_addr[5] == 0xff)
+ return -EINVAL;
+
+ psta = rtw_get_stainfo(pstapriv, param->sta_addr);
+ if (psta) {
+ int flags = param->u.add_sta.flags;
+
+ psta->aid = param->u.add_sta.aid;/* aid = 1~2007 */
+
+ memcpy(psta->bssrateset, param->u.add_sta.tx_supp_rates, 16);
+
+ /* check wmm cap. */
+ if (WLAN_STA_WME&flags)
+ psta->qos_option = 1;
+ else
+ psta->qos_option = 0;
+
+ if (pmlmepriv->qospriv.qos_option == 0)
+ psta->qos_option = 0;
+
+ /* chec 802.11n ht cap. */
+ if (WLAN_STA_HT&flags) {
+ psta->htpriv.ht_option = true;
+ psta->qos_option = 1;
+ memcpy((void *)&psta->htpriv.ht_cap, (void *)&param->u.add_sta.ht_cap, sizeof(struct rtw_ieee80211_ht_cap));
+ } else {
+ psta->htpriv.ht_option = false;
+ }
+
+ if (pmlmepriv->htpriv.ht_option == false)
+ psta->htpriv.ht_option = false;
+
+ update_sta_info_apmode(padapter, psta);
+ } else {
+ ret = -ENOMEM;
+ }
+
+ return ret;
+}
+
+static int rtw_del_sta(struct net_device *dev, struct ieee_param *param)
+{
+ int ret = 0;
+ struct sta_info *psta = NULL;
+ struct adapter *padapter = (struct adapter *)rtw_netdev_priv(dev);
+ struct mlme_priv *pmlmepriv = &(padapter->mlmepriv);
+ struct sta_priv *pstapriv = &padapter->stapriv;
+ int updated = 0;
+
+ DBG_88E("rtw_del_sta =%pM\n", (param->sta_addr));
+
+ if (check_fwstate(pmlmepriv, (_FW_LINKED|WIFI_AP_STATE)) != true)
+ return -EINVAL;
+
+ if (param->sta_addr[0] == 0xff && param->sta_addr[1] == 0xff &&
+ param->sta_addr[2] == 0xff && param->sta_addr[3] == 0xff &&
+ param->sta_addr[4] == 0xff && param->sta_addr[5] == 0xff)
+ return -EINVAL;
+
+ psta = rtw_get_stainfo(pstapriv, param->sta_addr);
+ if (psta) {
+ spin_lock_bh(&pstapriv->asoc_list_lock);
+ if (!list_empty(&psta->asoc_list)) {
+ list_del_init(&psta->asoc_list);
+ pstapriv->asoc_list_cnt--;
+ updated = ap_free_sta(padapter, psta, true, WLAN_REASON_DEAUTH_LEAVING);
+ }
+ spin_unlock_bh(&pstapriv->asoc_list_lock);
+ associated_clients_update(padapter, updated);
+ psta = NULL;
+ } else {
+ DBG_88E("rtw_del_sta(), sta has already been removed or never been added\n");
+ }
+
+ return ret;
+}
+
+static int rtw_ioctl_get_sta_data(struct net_device *dev, struct ieee_param *param, int len)
+{
+ int ret = 0;
+ struct sta_info *psta = NULL;
+ struct adapter *padapter = (struct adapter *)rtw_netdev_priv(dev);
+ struct mlme_priv *pmlmepriv = &(padapter->mlmepriv);
+ struct sta_priv *pstapriv = &padapter->stapriv;
+ struct ieee_param_ex *param_ex = (struct ieee_param_ex *)param;
+ struct sta_data *psta_data = (struct sta_data *)param_ex->data;
+
+ DBG_88E("rtw_ioctl_get_sta_info, sta_addr: %pM\n", (param_ex->sta_addr));
+
+ if (check_fwstate(pmlmepriv, (_FW_LINKED|WIFI_AP_STATE)) != true)
+ return -EINVAL;
+
+ if (param_ex->sta_addr[0] == 0xff && param_ex->sta_addr[1] == 0xff &&
+ param_ex->sta_addr[2] == 0xff && param_ex->sta_addr[3] == 0xff &&
+ param_ex->sta_addr[4] == 0xff && param_ex->sta_addr[5] == 0xff)
+ return -EINVAL;
+
+ psta = rtw_get_stainfo(pstapriv, param_ex->sta_addr);
+ if (psta) {
+ psta_data->aid = (u16)psta->aid;
+ psta_data->capability = psta->capability;
+ psta_data->flags = psta->flags;
+
+/*
+ nonerp_set : BIT(0)
+ no_short_slot_time_set : BIT(1)
+ no_short_preamble_set : BIT(2)
+ no_ht_gf_set : BIT(3)
+ no_ht_set : BIT(4)
+ ht_20mhz_set : BIT(5)
+*/
+
+ psta_data->sta_set = ((psta->nonerp_set) |
+ (psta->no_short_slot_time_set << 1) |
+ (psta->no_short_preamble_set << 2) |
+ (psta->no_ht_gf_set << 3) |
+ (psta->no_ht_set << 4) |
+ (psta->ht_20mhz_set << 5));
+ psta_data->tx_supp_rates_len = psta->bssratelen;
+ memcpy(psta_data->tx_supp_rates, psta->bssrateset, psta->bssratelen);
+ memcpy(&psta_data->ht_cap, &psta->htpriv.ht_cap, sizeof(struct rtw_ieee80211_ht_cap));
+ psta_data->rx_pkts = psta->sta_stats.rx_data_pkts;
+ psta_data->rx_bytes = psta->sta_stats.rx_bytes;
+ psta_data->rx_drops = psta->sta_stats.rx_drops;
+ psta_data->tx_pkts = psta->sta_stats.tx_pkts;
+ psta_data->tx_bytes = psta->sta_stats.tx_bytes;
+ psta_data->tx_drops = psta->sta_stats.tx_drops;
+ } else {
+ ret = -1;
+ }
+
+ return ret;
+}
+
+static int rtw_get_sta_wpaie(struct net_device *dev, struct ieee_param *param)
+{
+ int ret = 0;
+ struct sta_info *psta = NULL;
+ struct adapter *padapter = (struct adapter *)rtw_netdev_priv(dev);
+ struct mlme_priv *pmlmepriv = &(padapter->mlmepriv);
+ struct sta_priv *pstapriv = &padapter->stapriv;
+
+ DBG_88E("rtw_get_sta_wpaie, sta_addr: %pM\n", (param->sta_addr));
+
+ if (check_fwstate(pmlmepriv, (_FW_LINKED|WIFI_AP_STATE)) != true)
+ return -EINVAL;
+
+ if (param->sta_addr[0] == 0xff && param->sta_addr[1] == 0xff &&
+ param->sta_addr[2] == 0xff && param->sta_addr[3] == 0xff &&
+ param->sta_addr[4] == 0xff && param->sta_addr[5] == 0xff)
+ return -EINVAL;
+
+ psta = rtw_get_stainfo(pstapriv, param->sta_addr);
+ if (psta) {
+ if ((psta->wpa_ie[0] == WLAN_EID_RSN) || (psta->wpa_ie[0] == WLAN_EID_GENERIC)) {
+ int wpa_ie_len;
+ int copy_len;
+
+ wpa_ie_len = psta->wpa_ie[1];
+ copy_len = ((wpa_ie_len+2) > sizeof(psta->wpa_ie)) ? (sizeof(psta->wpa_ie)) : (wpa_ie_len+2);
+ param->u.wpa_ie.len = copy_len;
+ memcpy(param->u.wpa_ie.reserved, psta->wpa_ie, copy_len);
+ } else {
+ DBG_88E("sta's wpa_ie is NONE\n");
+ }
+ } else {
+ ret = -1;
+ }
+
+ return ret;
+}
+
+static int rtw_set_wps_beacon(struct net_device *dev, struct ieee_param *param, int len)
+{
+ int ret = 0;
+ unsigned char wps_oui[4] = {0x0, 0x50, 0xf2, 0x04};
+ struct adapter *padapter = (struct adapter *)rtw_netdev_priv(dev);
+ struct mlme_priv *pmlmepriv = &(padapter->mlmepriv);
+ struct mlme_ext_priv *pmlmeext = &(padapter->mlmeextpriv);
+ int ie_len;
+
+ DBG_88E("%s, len =%d\n", __func__, len);
+
+ if (check_fwstate(pmlmepriv, WIFI_AP_STATE) != true)
+ return -EINVAL;
+
+ ie_len = len-12-2;/* 12 = param header, 2:no packed */
+
+ kfree(pmlmepriv->wps_beacon_ie);
+ pmlmepriv->wps_beacon_ie = NULL;
+
+ if (ie_len > 0) {
+ pmlmepriv->wps_beacon_ie = rtw_malloc(ie_len);
+ pmlmepriv->wps_beacon_ie_len = ie_len;
+ if (pmlmepriv->wps_beacon_ie == NULL) {
+ DBG_88E("%s()-%d: rtw_malloc() ERROR!\n", __func__, __LINE__);
+ return -EINVAL;
+ }
+
+ memcpy(pmlmepriv->wps_beacon_ie, param->u.bcn_ie.buf, ie_len);
+
+ update_beacon(padapter, _VENDOR_SPECIFIC_IE_, wps_oui, true);
+
+ pmlmeext->bstart_bss = true;
+ }
+
+ return ret;
+}
+
+static int rtw_set_wps_probe_resp(struct net_device *dev, struct ieee_param *param, int len)
+{
+ int ret = 0;
+ struct adapter *padapter = (struct adapter *)rtw_netdev_priv(dev);
+ struct mlme_priv *pmlmepriv = &(padapter->mlmepriv);
+ int ie_len;
+
+ DBG_88E("%s, len =%d\n", __func__, len);
+
+ if (check_fwstate(pmlmepriv, WIFI_AP_STATE) != true)
+ return -EINVAL;
+
+ ie_len = len-12-2;/* 12 = param header, 2:no packed */
+
+ kfree(pmlmepriv->wps_probe_resp_ie);
+ pmlmepriv->wps_probe_resp_ie = NULL;
+
+ if (ie_len > 0) {
+ pmlmepriv->wps_probe_resp_ie = rtw_malloc(ie_len);
+ pmlmepriv->wps_probe_resp_ie_len = ie_len;
+ if (pmlmepriv->wps_probe_resp_ie == NULL) {
+ DBG_88E("%s()-%d: rtw_malloc() ERROR!\n", __func__, __LINE__);
+ return -EINVAL;
+ }
+ memcpy(pmlmepriv->wps_probe_resp_ie, param->u.bcn_ie.buf, ie_len);
+ }
+
+ return ret;
+}
+
+static int rtw_set_wps_assoc_resp(struct net_device *dev, struct ieee_param *param, int len)
+{
+ int ret = 0;
+ struct adapter *padapter = (struct adapter *)rtw_netdev_priv(dev);
+ struct mlme_priv *pmlmepriv = &(padapter->mlmepriv);
+ int ie_len;
+
+ DBG_88E("%s, len =%d\n", __func__, len);
+
+ if (check_fwstate(pmlmepriv, WIFI_AP_STATE) != true)
+ return -EINVAL;
+
+ ie_len = len-12-2;/* 12 = param header, 2:no packed */
+
+ kfree(pmlmepriv->wps_assoc_resp_ie);
+ pmlmepriv->wps_assoc_resp_ie = NULL;
+
+ if (ie_len > 0) {
+ pmlmepriv->wps_assoc_resp_ie = rtw_malloc(ie_len);
+ pmlmepriv->wps_assoc_resp_ie_len = ie_len;
+ if (pmlmepriv->wps_assoc_resp_ie == NULL) {
+ DBG_88E("%s()-%d: rtw_malloc() ERROR!\n", __func__, __LINE__);
+ return -EINVAL;
+ }
+
+ memcpy(pmlmepriv->wps_assoc_resp_ie, param->u.bcn_ie.buf, ie_len);
+ }
+
+ return ret;
+}
+
+static int rtw_set_hidden_ssid(struct net_device *dev, struct ieee_param *param, int len)
+{
+ int ret = 0;
+ struct adapter *padapter = (struct adapter *)rtw_netdev_priv(dev);
+ struct mlme_priv *pmlmepriv = &(padapter->mlmepriv);
+ struct mlme_ext_priv *pmlmeext = &(padapter->mlmeextpriv);
+ struct mlme_ext_info *pmlmeinfo = &(pmlmeext->mlmext_info);
+
+ u8 value;
+
+ if (check_fwstate(pmlmepriv, WIFI_AP_STATE) != true)
+ return -EINVAL;
+
+ if (param->u.wpa_param.name != 0) /* dummy test... */
+ DBG_88E("%s name(%u) != 0\n", __func__, param->u.wpa_param.name);
+ value = param->u.wpa_param.value;
+
+ /* use the same definition of hostapd's ignore_broadcast_ssid */
+ if (value != 1 && value != 2)
+ value = 0;
+ DBG_88E("%s value(%u)\n", __func__, value);
+ pmlmeinfo->hidden_ssid_mode = value;
+ return ret;
+}
+
+static int rtw_ioctl_acl_remove_sta(struct net_device *dev, struct ieee_param *param, int len)
+{
+ int ret = 0;
+ struct adapter *padapter = (struct adapter *)rtw_netdev_priv(dev);
+ struct mlme_priv *pmlmepriv = &(padapter->mlmepriv);
+
+ if (check_fwstate(pmlmepriv, WIFI_AP_STATE) != true)
+ return -EINVAL;
+
+ if (param->sta_addr[0] == 0xff && param->sta_addr[1] == 0xff &&
+ param->sta_addr[2] == 0xff && param->sta_addr[3] == 0xff &&
+ param->sta_addr[4] == 0xff && param->sta_addr[5] == 0xff)
+ return -EINVAL;
+ ret = rtw_acl_remove_sta(padapter, param->sta_addr);
+ return ret;
+}
+
+static int rtw_ioctl_acl_add_sta(struct net_device *dev, struct ieee_param *param, int len)
+{
+ int ret = 0;
+ struct adapter *padapter = (struct adapter *)rtw_netdev_priv(dev);
+ struct mlme_priv *pmlmepriv = &(padapter->mlmepriv);
+
+ if (check_fwstate(pmlmepriv, WIFI_AP_STATE) != true)
+ return -EINVAL;
+
+ if (param->sta_addr[0] == 0xff && param->sta_addr[1] == 0xff &&
+ param->sta_addr[2] == 0xff && param->sta_addr[3] == 0xff &&
+ param->sta_addr[4] == 0xff && param->sta_addr[5] == 0xff)
+ return -EINVAL;
+ ret = rtw_acl_add_sta(padapter, param->sta_addr);
+ return ret;
+}
+
+static int rtw_ioctl_set_macaddr_acl(struct net_device *dev, struct ieee_param *param, int len)
+{
+ int ret = 0;
+ struct adapter *padapter = (struct adapter *)rtw_netdev_priv(dev);
+ struct mlme_priv *pmlmepriv = &(padapter->mlmepriv);
+
+ if (check_fwstate(pmlmepriv, WIFI_AP_STATE) != true)
+ return -EINVAL;
+
+ rtw_set_macaddr_acl(padapter, param->u.mlme.command);
+
+ return ret;
+}
+
+static int rtw_hostapd_ioctl(struct net_device *dev, struct iw_point *p)
+{
+ struct ieee_param *param;
+ int ret = 0;
+ struct adapter *padapter = (struct adapter *)rtw_netdev_priv(dev);
+
+ /*
+ * this function is expect to call in master mode, which allows no power saving
+ * so, we just check hw_init_completed
+ */
+
+ if (!padapter->hw_init_completed) {
+ ret = -EPERM;
+ goto out;
+ }
+
+ if (!p->pointer) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ param = (struct ieee_param *)rtw_malloc(p->length);
+ if (param == NULL) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ if (copy_from_user(param, p->pointer, p->length)) {
+ kfree(param);
+ ret = -EFAULT;
+ goto out;
+ }
+
+ switch (param->cmd) {
+ case RTL871X_HOSTAPD_FLUSH:
+ ret = rtw_hostapd_sta_flush(dev);
+ break;
+ case RTL871X_HOSTAPD_ADD_STA:
+ ret = rtw_add_sta(dev, param);
+ break;
+ case RTL871X_HOSTAPD_REMOVE_STA:
+ ret = rtw_del_sta(dev, param);
+ break;
+ case RTL871X_HOSTAPD_SET_BEACON:
+ ret = rtw_set_beacon(dev, param, p->length);
+ break;
+ case RTL871X_SET_ENCRYPTION:
+ ret = rtw_set_encryption(dev, param, p->length);
+ break;
+ case RTL871X_HOSTAPD_GET_WPAIE_STA:
+ ret = rtw_get_sta_wpaie(dev, param);
+ break;
+ case RTL871X_HOSTAPD_SET_WPS_BEACON:
+ ret = rtw_set_wps_beacon(dev, param, p->length);
+ break;
+ case RTL871X_HOSTAPD_SET_WPS_PROBE_RESP:
+ ret = rtw_set_wps_probe_resp(dev, param, p->length);
+ break;
+ case RTL871X_HOSTAPD_SET_WPS_ASSOC_RESP:
+ ret = rtw_set_wps_assoc_resp(dev, param, p->length);
+ break;
+ case RTL871X_HOSTAPD_SET_HIDDEN_SSID:
+ ret = rtw_set_hidden_ssid(dev, param, p->length);
+ break;
+ case RTL871X_HOSTAPD_GET_INFO_STA:
+ ret = rtw_ioctl_get_sta_data(dev, param, p->length);
+ break;
+ case RTL871X_HOSTAPD_SET_MACADDR_ACL:
+ ret = rtw_ioctl_set_macaddr_acl(dev, param, p->length);
+ break;
+ case RTL871X_HOSTAPD_ACL_ADD_STA:
+ ret = rtw_ioctl_acl_add_sta(dev, param, p->length);
+ break;
+ case RTL871X_HOSTAPD_ACL_REMOVE_STA:
+ ret = rtw_ioctl_acl_remove_sta(dev, param, p->length);
+ break;
+ default:
+ DBG_88E("Unknown hostapd request: %d\n", param->cmd);
+ ret = -EOPNOTSUPP;
+ break;
+ }
+
+ if (ret == 0 && copy_to_user(p->pointer, param, p->length))
+ ret = -EFAULT;
+ kfree(param);
+out:
+ return ret;
+}
+#endif
+
+#include <rtw_android.h>
+static int rtw_wx_set_priv(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *awrq,
+ char *extra)
+{
+ int ret = 0;
+ int len = 0;
+ char *ext;
+ struct adapter *padapter = (struct adapter *)rtw_netdev_priv(dev);
+ struct iw_point *dwrq = (struct iw_point *)awrq;
+
+ if (dwrq->length == 0)
+ return -EFAULT;
+
+ len = dwrq->length;
+ ext = vmalloc(len);
+ if (!ext)
+ return -ENOMEM;
+
+ if (copy_from_user(ext, dwrq->pointer, len)) {
+ vfree(ext);
+ return -EFAULT;
+ }
+
+ /* added for wps2.0 @20110524 */
+ if (dwrq->flags == 0x8766 && len > 8) {
+ u32 cp_sz;
+ struct mlme_priv *pmlmepriv = &(padapter->mlmepriv);
+ u8 *probereq_wpsie = ext;
+ int probereq_wpsie_len = len;
+ u8 wps_oui[4] = {0x0, 0x50, 0xf2, 0x04};
+
+ if ((_VENDOR_SPECIFIC_IE_ == probereq_wpsie[0]) &&
+ (!memcmp(&probereq_wpsie[2], wps_oui, 4))) {
+ cp_sz = probereq_wpsie_len > MAX_WPS_IE_LEN ? MAX_WPS_IE_LEN : probereq_wpsie_len;
+
+ pmlmepriv->wps_probe_req_ie_len = 0;
+ kfree(pmlmepriv->wps_probe_req_ie);
+ pmlmepriv->wps_probe_req_ie = NULL;
+
+ pmlmepriv->wps_probe_req_ie = rtw_malloc(cp_sz);
+ if (pmlmepriv->wps_probe_req_ie == NULL) {
+ pr_info("%s()-%d: rtw_malloc() ERROR!\n", __func__, __LINE__);
+ ret = -EINVAL;
+ goto FREE_EXT;
+ }
+ memcpy(pmlmepriv->wps_probe_req_ie, probereq_wpsie, cp_sz);
+ pmlmepriv->wps_probe_req_ie_len = cp_sz;
+ }
+ goto FREE_EXT;
+ }
+
+ if (len >= WEXT_CSCAN_HEADER_SIZE &&
+ !memcmp(ext, WEXT_CSCAN_HEADER, WEXT_CSCAN_HEADER_SIZE)) {
+ ret = rtw_wx_set_scan(dev, info, awrq, ext);
+ goto FREE_EXT;
+ }
+
+FREE_EXT:
+
+ vfree(ext);
+
+ return ret;
+}
+
+static iw_handler rtw_handlers[] = {
+ NULL, /* SIOCSIWCOMMIT */
+ rtw_wx_get_name, /* SIOCGIWNAME */
+ dummy, /* SIOCSIWNWID */
+ dummy, /* SIOCGIWNWID */
+ rtw_wx_set_freq, /* SIOCSIWFREQ */
+ rtw_wx_get_freq, /* SIOCGIWFREQ */
+ rtw_wx_set_mode, /* SIOCSIWMODE */
+ rtw_wx_get_mode, /* SIOCGIWMODE */
+ dummy, /* SIOCSIWSENS */
+ rtw_wx_get_sens, /* SIOCGIWSENS */
+ NULL, /* SIOCSIWRANGE */
+ rtw_wx_get_range, /* SIOCGIWRANGE */
+ rtw_wx_set_priv, /* SIOCSIWPRIV */
+ NULL, /* SIOCGIWPRIV */
+ NULL, /* SIOCSIWSTATS */
+ NULL, /* SIOCGIWSTATS */
+ dummy, /* SIOCSIWSPY */
+ dummy, /* SIOCGIWSPY */
+ NULL, /* SIOCGIWTHRSPY */
+ NULL, /* SIOCWIWTHRSPY */
+ rtw_wx_set_wap, /* SIOCSIWAP */
+ rtw_wx_get_wap, /* SIOCGIWAP */
+ rtw_wx_set_mlme, /* request MLME operation; uses struct iw_mlme */
+ dummy, /* SIOCGIWAPLIST -- depricated */
+ rtw_wx_set_scan, /* SIOCSIWSCAN */
+ rtw_wx_get_scan, /* SIOCGIWSCAN */
+ rtw_wx_set_essid, /* SIOCSIWESSID */
+ rtw_wx_get_essid, /* SIOCGIWESSID */
+ dummy, /* SIOCSIWNICKN */
+ rtw_wx_get_nick, /* SIOCGIWNICKN */
+ NULL, /* -- hole -- */
+ NULL, /* -- hole -- */
+ rtw_wx_set_rate, /* SIOCSIWRATE */
+ rtw_wx_get_rate, /* SIOCGIWRATE */
+ rtw_wx_set_rts, /* SIOCSIWRTS */
+ rtw_wx_get_rts, /* SIOCGIWRTS */
+ rtw_wx_set_frag, /* SIOCSIWFRAG */
+ rtw_wx_get_frag, /* SIOCGIWFRAG */
+ dummy, /* SIOCSIWTXPOW */
+ dummy, /* SIOCGIWTXPOW */
+ dummy, /* SIOCSIWRETRY */
+ rtw_wx_get_retry, /* SIOCGIWRETRY */
+ rtw_wx_set_enc, /* SIOCSIWENCODE */
+ rtw_wx_get_enc, /* SIOCGIWENCODE */
+ dummy, /* SIOCSIWPOWER */
+ rtw_wx_get_power, /* SIOCGIWPOWER */
+ NULL, /*---hole---*/
+ NULL, /*---hole---*/
+ rtw_wx_set_gen_ie, /* SIOCSIWGENIE */
+ NULL, /* SIOCGWGENIE */
+ rtw_wx_set_auth, /* SIOCSIWAUTH */
+ NULL, /* SIOCGIWAUTH */
+ rtw_wx_set_enc_ext, /* SIOCSIWENCODEEXT */
+ NULL, /* SIOCGIWENCODEEXT */
+ rtw_wx_set_pmkid, /* SIOCSIWPMKSA */
+ NULL, /*---hole---*/
+};
+
+static struct iw_statistics *rtw_get_wireless_stats(struct net_device *dev)
+{
+ struct adapter *padapter = (struct adapter *)rtw_netdev_priv(dev);
+ struct iw_statistics *piwstats = &padapter->iwstats;
+ int tmp_level = 0;
+ int tmp_qual = 0;
+ int tmp_noise = 0;
+
+ if (!check_fwstate(&padapter->mlmepriv, _FW_LINKED)) {
+ piwstats->qual.qual = 0;
+ piwstats->qual.level = 0;
+ piwstats->qual.noise = 0;
+ } else {
+ tmp_level = padapter->recvpriv.signal_strength;
+ tmp_qual = padapter->recvpriv.signal_qual;
+ tmp_noise = padapter->recvpriv.noise;
+
+ piwstats->qual.level = tmp_level;
+ piwstats->qual.qual = tmp_qual;
+ piwstats->qual.noise = tmp_noise;
+ }
+ piwstats->qual.updated = IW_QUAL_ALL_UPDATED;/* IW_QUAL_DBM; */
+ return &padapter->iwstats;
+}
+
+struct iw_handler_def rtw_handlers_def = {
+ .standard = rtw_handlers,
+ .num_standard = sizeof(rtw_handlers) / sizeof(iw_handler),
+ .get_wireless_stats = rtw_get_wireless_stats,
+};
+
+#include <rtw_android.h>
+int rtw_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
+{
+ struct iwreq *wrq = (struct iwreq *)rq;
+ int ret = 0;
+
+ switch (cmd) {
+ case RTL_IOCTL_WPA_SUPPLICANT:
+ ret = wpa_supplicant_ioctl(dev, &wrq->u.data);
+ break;
+#ifdef CONFIG_88EU_AP_MODE
+ case RTL_IOCTL_HOSTAPD:
+ ret = rtw_hostapd_ioctl(dev, &wrq->u.data);
+ break;
+#endif /* CONFIG_88EU_AP_MODE */
+ case (SIOCDEVPRIVATE+1):
+ ret = rtw_android_priv_cmd(dev, rq, cmd);
+ break;
+ default:
+ ret = -EOPNOTSUPP;
+ break;
+ }
+ return ret;
+}
diff --git a/drivers/staging/rtl8188eu/os_dep/mlme_linux.c b/drivers/staging/rtl8188eu/os_dep/mlme_linux.c
new file mode 100644
index 000000000..baff1e266
--- /dev/null
+++ b/drivers/staging/rtl8188eu/os_dep/mlme_linux.c
@@ -0,0 +1,200 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2011 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ *
+ ******************************************************************************/
+
+
+#define _MLME_OSDEP_C_
+
+#include <osdep_service.h>
+#include <drv_types.h>
+#include <mlme_osdep.h>
+
+void rtw_init_mlme_timer(struct adapter *padapter)
+{
+ struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
+
+ setup_timer(&pmlmepriv->assoc_timer, _rtw_join_timeout_handler,
+ (unsigned long)padapter);
+ setup_timer(&pmlmepriv->scan_to_timer, rtw_scan_timeout_handler,
+ (unsigned long)padapter);
+ setup_timer(&pmlmepriv->dynamic_chk_timer,
+ rtw_dynamic_check_timer_handlder, (unsigned long)padapter);
+}
+
+void rtw_os_indicate_connect(struct adapter *adapter)
+{
+ rtw_indicate_wx_assoc_event(adapter);
+ netif_carrier_on(adapter->pnetdev);
+ if (adapter->pid[2] != 0)
+ rtw_signal_process(adapter->pid[2], SIGALRM);
+}
+
+void rtw_os_indicate_scan_done(struct adapter *padapter, bool aborted)
+{
+ indicate_wx_scan_complete_event(padapter);
+}
+
+static struct rt_pmkid_list backup_pmkid[NUM_PMKID_CACHE];
+
+void rtw_reset_securitypriv(struct adapter *adapter)
+{
+ u8 backup_index = 0;
+ u8 backup_counter = 0x00;
+ u32 backup_time = 0;
+
+ if (adapter->securitypriv.dot11AuthAlgrthm == dot11AuthAlgrthm_8021X) {
+ /* 802.1x */
+ /* We have to backup the PMK information for WiFi PMK Caching test item. */
+ /* Backup the btkip_countermeasure information. */
+ /* When the countermeasure is trigger, the driver have to disconnect with AP for 60 seconds. */
+ memcpy(&backup_pmkid[0], &adapter->securitypriv.PMKIDList[0], sizeof(struct rt_pmkid_list) * NUM_PMKID_CACHE);
+ backup_index = adapter->securitypriv.PMKIDIndex;
+ backup_counter = adapter->securitypriv.btkip_countermeasure;
+ backup_time = adapter->securitypriv.btkip_countermeasure_time;
+ memset((unsigned char *)&adapter->securitypriv, 0, sizeof(struct security_priv));
+
+ /* Restore the PMK information to securitypriv structure for the following connection. */
+ memcpy(&adapter->securitypriv.PMKIDList[0],
+ &backup_pmkid[0],
+ sizeof(struct rt_pmkid_list) * NUM_PMKID_CACHE);
+ adapter->securitypriv.PMKIDIndex = backup_index;
+ adapter->securitypriv.btkip_countermeasure = backup_counter;
+ adapter->securitypriv.btkip_countermeasure_time = backup_time;
+ adapter->securitypriv.ndisauthtype = Ndis802_11AuthModeOpen;
+ adapter->securitypriv.ndisencryptstatus = Ndis802_11WEPDisabled;
+ } else {
+ /* reset values in securitypriv */
+ struct security_priv *psec_priv = &adapter->securitypriv;
+
+ psec_priv->dot11AuthAlgrthm = dot11AuthAlgrthm_Open; /* open system */
+ psec_priv->dot11PrivacyAlgrthm = _NO_PRIVACY_;
+ psec_priv->dot11PrivacyKeyIndex = 0;
+ psec_priv->dot118021XGrpPrivacy = _NO_PRIVACY_;
+ psec_priv->dot118021XGrpKeyid = 1;
+ psec_priv->ndisauthtype = Ndis802_11AuthModeOpen;
+ psec_priv->ndisencryptstatus = Ndis802_11WEPDisabled;
+ }
+}
+
+void rtw_os_indicate_disconnect(struct adapter *adapter)
+{
+ netif_carrier_off(adapter->pnetdev); /* Do it first for tx broadcast pkt after disconnection issue! */
+ rtw_indicate_wx_disassoc_event(adapter);
+ rtw_reset_securitypriv(adapter);
+}
+
+void rtw_report_sec_ie(struct adapter *adapter, u8 authmode, u8 *sec_ie)
+{
+ uint len;
+ u8 *buff, *p, i;
+ union iwreq_data wrqu;
+
+ RT_TRACE(_module_mlme_osdep_c_, _drv_info_,
+ ("+rtw_report_sec_ie, authmode=%d\n", authmode));
+ buff = NULL;
+ if (authmode == _WPA_IE_ID_) {
+ RT_TRACE(_module_mlme_osdep_c_, _drv_info_,
+ ("rtw_report_sec_ie, authmode=%d\n", authmode));
+ buff = rtw_malloc(IW_CUSTOM_MAX);
+ if (!buff)
+ return;
+ memset(buff, 0, IW_CUSTOM_MAX);
+ p = buff;
+ p += sprintf(p, "ASSOCINFO(ReqIEs =");
+ len = sec_ie[1]+2;
+ len = (len < IW_CUSTOM_MAX) ? len : IW_CUSTOM_MAX;
+ for (i = 0; i < len; i++)
+ p += sprintf(p, "%02x", sec_ie[i]);
+ p += sprintf(p, ")");
+ memset(&wrqu, 0, sizeof(wrqu));
+ wrqu.data.length = p-buff;
+ wrqu.data.length = (wrqu.data.length < IW_CUSTOM_MAX) ?
+ wrqu.data.length : IW_CUSTOM_MAX;
+ wireless_send_event(adapter->pnetdev, IWEVCUSTOM, &wrqu, buff);
+ kfree(buff);
+ }
+}
+
+void init_addba_retry_timer(struct adapter *padapter, struct sta_info *psta)
+{
+ setup_timer(&psta->addba_retry_timer, addba_timer_hdl,
+ (unsigned long)psta);
+}
+
+void init_mlme_ext_timer(struct adapter *padapter)
+{
+ struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv;
+
+ setup_timer(&pmlmeext->survey_timer, survey_timer_hdl,
+ (unsigned long)padapter);
+ setup_timer(&pmlmeext->link_timer, link_timer_hdl,
+ (unsigned long)padapter);
+}
+
+#ifdef CONFIG_88EU_AP_MODE
+
+void rtw_indicate_sta_assoc_event(struct adapter *padapter, struct sta_info *psta)
+{
+ union iwreq_data wrqu;
+ struct sta_priv *pstapriv = &padapter->stapriv;
+
+ if (psta == NULL)
+ return;
+
+ if (psta->aid > NUM_STA)
+ return;
+
+ if (pstapriv->sta_aid[psta->aid - 1] != psta)
+ return;
+
+
+ wrqu.addr.sa_family = ARPHRD_ETHER;
+
+ memcpy(wrqu.addr.sa_data, psta->hwaddr, ETH_ALEN);
+
+ DBG_88E("+rtw_indicate_sta_assoc_event\n");
+
+ wireless_send_event(padapter->pnetdev, IWEVREGISTERED, &wrqu, NULL);
+}
+
+void rtw_indicate_sta_disassoc_event(struct adapter *padapter, struct sta_info *psta)
+{
+ union iwreq_data wrqu;
+ struct sta_priv *pstapriv = &padapter->stapriv;
+
+ if (psta == NULL)
+ return;
+
+ if (psta->aid > NUM_STA)
+ return;
+
+ if (pstapriv->sta_aid[psta->aid - 1] != psta)
+ return;
+
+
+ wrqu.addr.sa_family = ARPHRD_ETHER;
+
+ memcpy(wrqu.addr.sa_data, psta->hwaddr, ETH_ALEN);
+
+ DBG_88E("+rtw_indicate_sta_disassoc_event\n");
+
+ wireless_send_event(padapter->pnetdev, IWEVEXPIRED, &wrqu, NULL);
+}
+
+#endif
diff --git a/drivers/staging/rtl8188eu/os_dep/os_intfs.c b/drivers/staging/rtl8188eu/os_dep/os_intfs.c
new file mode 100644
index 000000000..750c87b46
--- /dev/null
+++ b/drivers/staging/rtl8188eu/os_dep/os_intfs.c
@@ -0,0 +1,1163 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2011 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ *
+ ******************************************************************************/
+#define _OS_INTFS_C_
+
+#include <osdep_service.h>
+#include <osdep_intf.h>
+#include <drv_types.h>
+#include <xmit_osdep.h>
+#include <recv_osdep.h>
+#include <hal_intf.h>
+#include <rtw_ioctl.h>
+#include <rtl8188e_hal.h>
+
+#include <usb_hal.h>
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Realtek Wireless Lan Driver");
+MODULE_AUTHOR("Realtek Semiconductor Corp.");
+MODULE_VERSION(DRIVERVERSION);
+
+#define RTW_NOTCH_FILTER 0 /* 0:Disable, 1:Enable, */
+
+/* module param defaults */
+static int rtw_chip_version;
+static int rtw_rfintfs = HWPI;
+static int rtw_lbkmode;/* RTL8712_AIR_TRX; */
+static int rtw_network_mode = Ndis802_11IBSS;/* Ndis802_11Infrastructure; infra, ad-hoc, auto */
+static int rtw_channel = 1;/* ad-hoc support requirement */
+static int rtw_wireless_mode = WIRELESS_11BG_24N;
+static int rtw_vrtl_carrier_sense = AUTO_VCS;
+static int rtw_vcs_type = RTS_CTS;/* */
+static int rtw_rts_thresh = 2347;/* */
+static int rtw_frag_thresh = 2346;/* */
+static int rtw_preamble = PREAMBLE_LONG;/* long, short, auto */
+static int rtw_scan_mode = 1;/* active, passive */
+static int rtw_adhoc_tx_pwr = 1;
+static int rtw_soft_ap;
+static int rtw_power_mgnt = 1;
+static int rtw_ips_mode = IPS_NORMAL;
+
+static int rtw_smart_ps = 2;
+
+module_param(rtw_ips_mode, int, 0644);
+MODULE_PARM_DESC(rtw_ips_mode, "The default IPS mode");
+
+static int rtw_debug = 1;
+static int rtw_radio_enable = 1;
+static int rtw_long_retry_lmt = 7;
+static int rtw_short_retry_lmt = 7;
+static int rtw_busy_thresh = 40;
+static int rtw_ack_policy = NORMAL_ACK;
+
+static int rtw_software_encrypt;
+static int rtw_software_decrypt;
+
+static int rtw_acm_method;/* 0:By SW 1:By HW. */
+
+static int rtw_wmm_enable = 1;/* default is set to enable the wmm. */
+static int rtw_uapsd_enable;
+static int rtw_uapsd_max_sp = NO_LIMIT;
+static int rtw_uapsd_acbk_en;
+static int rtw_uapsd_acbe_en;
+static int rtw_uapsd_acvi_en;
+static int rtw_uapsd_acvo_en;
+
+static int rtw_ht_enable = 1;
+static int rtw_cbw40_enable = 3; /* 0 :disable, bit(0): enable 2.4g, bit(1): enable 5g */
+static int rtw_ampdu_enable = 1;/* for enable tx_ampdu */
+static int rtw_rx_stbc = 1;/* 0: disable, bit(0):enable 2.4g, bit(1):enable 5g, default is set to enable 2.4GHZ for IOT issue with bufflao's AP at 5GHZ */
+static int rtw_ampdu_amsdu;/* 0: disabled, 1:enabled, 2:auto */
+
+static int rtw_lowrate_two_xmit = 1;/* Use 2 path Tx to transmit MCS0~7 and legacy mode */
+
+static int rtw_rf_config = RF_819X_MAX_TYPE; /* auto */
+static int rtw_low_power;
+static int rtw_wifi_spec;
+static int rtw_channel_plan = RT_CHANNEL_DOMAIN_MAX;
+static int rtw_AcceptAddbaReq = true;/* 0:Reject AP's Add BA req, 1:Accept AP's Add BA req. */
+
+static int rtw_antdiv_cfg = 2; /* 0:OFF , 1:ON, 2:decide by Efuse config */
+static int rtw_antdiv_type; /* 0:decide by efuse 1: for 88EE, 1Tx and 1RxCG are diversity.(2 Ant with SPDT), 2: for 88EE, 1Tx and 2Rx are diversity.(2 Ant, Tx and RxCG are both on aux port, RxCS is on main port), 3: for 88EE, 1Tx and 1RxCG are fixed.(1Ant, Tx and RxCG are both on aux port) */
+
+static int rtw_enusbss;/* 0:disable, 1:enable */
+
+static int rtw_hwpdn_mode = 2;/* 0:disable, 1:enable, 2: by EFUSE config */
+
+static int rtw_hwpwrp_detect; /* HW power ping detect 0:disable , 1:enable */
+
+static int rtw_hw_wps_pbc = 1;
+
+int rtw_mc2u_disable;
+
+static int rtw_80211d;
+
+static char *ifname = "wlan%d";
+module_param(ifname, charp, 0644);
+MODULE_PARM_DESC(ifname, "The default name to allocate for first interface");
+
+static char *if2name = "wlan%d";
+module_param(if2name, charp, 0644);
+MODULE_PARM_DESC(if2name, "The default name to allocate for second interface");
+
+char *rtw_initmac; /* temp mac address if users want to use instead of the mac address in Efuse */
+
+module_param(rtw_initmac, charp, 0644);
+module_param(rtw_channel_plan, int, 0644);
+module_param(rtw_chip_version, int, 0644);
+module_param(rtw_rfintfs, int, 0644);
+module_param(rtw_lbkmode, int, 0644);
+module_param(rtw_network_mode, int, 0644);
+module_param(rtw_channel, int, 0644);
+module_param(rtw_wmm_enable, int, 0644);
+module_param(rtw_vrtl_carrier_sense, int, 0644);
+module_param(rtw_vcs_type, int, 0644);
+module_param(rtw_busy_thresh, int, 0644);
+module_param(rtw_ht_enable, int, 0644);
+module_param(rtw_cbw40_enable, int, 0644);
+module_param(rtw_ampdu_enable, int, 0644);
+module_param(rtw_rx_stbc, int, 0644);
+module_param(rtw_ampdu_amsdu, int, 0644);
+module_param(rtw_lowrate_two_xmit, int, 0644);
+module_param(rtw_rf_config, int, 0644);
+module_param(rtw_power_mgnt, int, 0644);
+module_param(rtw_smart_ps, int, 0644);
+module_param(rtw_low_power, int, 0644);
+module_param(rtw_wifi_spec, int, 0644);
+module_param(rtw_antdiv_cfg, int, 0644);
+module_param(rtw_antdiv_type, int, 0644);
+module_param(rtw_enusbss, int, 0644);
+module_param(rtw_hwpdn_mode, int, 0644);
+module_param(rtw_hwpwrp_detect, int, 0644);
+module_param(rtw_hw_wps_pbc, int, 0644);
+
+static uint rtw_max_roaming_times = 2;
+module_param(rtw_max_roaming_times, uint, 0644);
+MODULE_PARM_DESC(rtw_max_roaming_times, "The max roaming times to try");
+
+static int rtw_fw_iol = 1;/* 0:Disable, 1:enable, 2:by usb speed */
+module_param(rtw_fw_iol, int, 0644);
+MODULE_PARM_DESC(rtw_fw_iol, "FW IOL");
+
+module_param(rtw_mc2u_disable, int, 0644);
+
+module_param(rtw_80211d, int, 0644);
+MODULE_PARM_DESC(rtw_80211d, "Enable 802.11d mechanism");
+
+static uint rtw_notch_filter = RTW_NOTCH_FILTER;
+module_param(rtw_notch_filter, uint, 0644);
+MODULE_PARM_DESC(rtw_notch_filter, "0:Disable, 1:Enable, 2:Enable only for P2P");
+module_param_named(debug, rtw_debug, int, 0444);
+MODULE_PARM_DESC(debug, "Set debug level (1-9) (default 1)");
+
+/* dummy routines */
+void rtw_proc_remove_one(struct net_device *dev)
+{
+}
+
+void rtw_proc_init_one(struct net_device *dev)
+{
+}
+
+#if 0 /* TODO: Convert these to /sys */
+void rtw_proc_init_one(struct net_device *dev)
+{
+ struct proc_dir_entry *dir_dev = NULL;
+ struct proc_dir_entry *entry = NULL;
+ struct adapter *padapter = rtw_netdev_priv(dev);
+ u8 rf_type;
+
+ if (rtw_proc == NULL) {
+ memcpy(rtw_proc_name, DRV_NAME, sizeof(DRV_NAME));
+
+ rtw_proc = create_proc_entry(rtw_proc_name, S_IFDIR, init_net.proc_net);
+ if (rtw_proc == NULL) {
+ DBG_88E(KERN_ERR "Unable to create rtw_proc directory\n");
+ return;
+ }
+
+ entry = create_proc_read_entry("ver_info", S_IFREG | S_IRUGO, rtw_proc, proc_get_drv_version, dev);
+ if (!entry) {
+ pr_info("Unable to create_proc_read_entry!\n");
+ return;
+ }
+ }
+
+ if (padapter->dir_dev == NULL) {
+ padapter->dir_dev = create_proc_entry(dev->name,
+ S_IFDIR | S_IRUGO | S_IXUGO,
+ rtw_proc);
+ dir_dev = padapter->dir_dev;
+ if (dir_dev == NULL) {
+ if (rtw_proc_cnt == 0) {
+ if (rtw_proc) {
+ remove_proc_entry(rtw_proc_name, init_net.proc_net);
+ rtw_proc = NULL;
+ }
+ }
+
+ pr_info("Unable to create dir_dev directory\n");
+ return;
+ }
+ } else {
+ return;
+ }
+
+ rtw_proc_cnt++;
+
+ entry = create_proc_read_entry("write_reg", S_IFREG | S_IRUGO,
+ dir_dev, proc_get_write_reg, dev);
+ if (!entry) {
+ pr_info("Unable to create_proc_read_entry!\n");
+ return;
+ }
+ entry->write_proc = proc_set_write_reg;
+
+ entry = create_proc_read_entry("read_reg", S_IFREG | S_IRUGO,
+ dir_dev, proc_get_read_reg, dev);
+ if (!entry) {
+ pr_info("Unable to create_proc_read_entry!\n");
+ return;
+ }
+ entry->write_proc = proc_set_read_reg;
+
+
+ entry = create_proc_read_entry("fwstate", S_IFREG | S_IRUGO,
+ dir_dev, proc_get_fwstate, dev);
+ if (!entry) {
+ pr_info("Unable to create_proc_read_entry!\n");
+ return;
+ }
+
+ entry = create_proc_read_entry("sec_info", S_IFREG | S_IRUGO,
+ dir_dev, proc_get_sec_info, dev);
+ if (!entry) {
+ pr_info("Unable to create_proc_read_entry!\n");
+ return;
+ }
+
+ entry = create_proc_read_entry("mlmext_state", S_IFREG | S_IRUGO,
+ dir_dev, proc_get_mlmext_state, dev);
+ if (!entry) {
+ pr_info("Unable to create_proc_read_entry!\n");
+ return;
+ }
+
+ entry = create_proc_read_entry("qos_option", S_IFREG | S_IRUGO,
+ dir_dev, proc_get_qos_option, dev);
+ if (!entry) {
+ pr_info("Unable to create_proc_read_entry!\n");
+ return;
+ }
+
+ entry = create_proc_read_entry("ht_option", S_IFREG | S_IRUGO,
+ dir_dev, proc_get_ht_option, dev);
+ if (!entry) {
+ pr_info("Unable to create_proc_read_entry!\n");
+ return;
+ }
+
+ entry = create_proc_read_entry("rf_info", S_IFREG | S_IRUGO,
+ dir_dev, proc_get_rf_info, dev);
+ if (!entry) {
+ pr_info("Unable to create_proc_read_entry!\n");
+ return;
+ }
+
+ entry = create_proc_read_entry("ap_info", S_IFREG | S_IRUGO,
+ dir_dev, proc_get_ap_info, dev);
+ if (!entry) {
+ pr_info("Unable to create_proc_read_entry!\n");
+ return;
+ }
+
+ entry = create_proc_read_entry("adapter_state", S_IFREG | S_IRUGO,
+ dir_dev, proc_getstruct adapter_state, dev);
+ if (!entry) {
+ pr_info("Unable to create_proc_read_entry!\n");
+ return;
+ }
+
+ entry = create_proc_read_entry("trx_info", S_IFREG | S_IRUGO,
+ dir_dev, proc_get_trx_info, dev);
+ if (!entry) {
+ pr_info("Unable to create_proc_read_entry!\n");
+ return;
+ }
+
+ entry = create_proc_read_entry("mac_reg_dump1", S_IFREG | S_IRUGO,
+ dir_dev, proc_get_mac_reg_dump1, dev);
+ if (!entry) {
+ pr_info("Unable to create_proc_read_entry!\n");
+ return;
+ }
+
+ entry = create_proc_read_entry("mac_reg_dump2", S_IFREG | S_IRUGO,
+ dir_dev, proc_get_mac_reg_dump2, dev);
+ if (!entry) {
+ pr_info("Unable to create_proc_read_entry!\n");
+ return;
+ }
+
+ entry = create_proc_read_entry("mac_reg_dump3", S_IFREG | S_IRUGO,
+ dir_dev, proc_get_mac_reg_dump3, dev);
+ if (!entry) {
+ pr_info("Unable to create_proc_read_entry!\n");
+ return;
+ }
+
+ entry = create_proc_read_entry("bb_reg_dump1", S_IFREG | S_IRUGO,
+ dir_dev, proc_get_bb_reg_dump1, dev);
+ if (!entry) {
+ pr_info("Unable to create_proc_read_entry!\n");
+ return;
+ }
+
+ entry = create_proc_read_entry("bb_reg_dump2", S_IFREG | S_IRUGO,
+ dir_dev, proc_get_bb_reg_dump2, dev);
+ if (!entry) {
+ pr_info("Unable to create_proc_read_entry!\n");
+ return;
+ }
+
+ entry = create_proc_read_entry("bb_reg_dump3", S_IFREG | S_IRUGO,
+ dir_dev, proc_get_bb_reg_dump3, dev);
+ if (!entry) {
+ pr_info("Unable to create_proc_read_entry!\n");
+ return;
+ }
+
+ entry = create_proc_read_entry("rf_reg_dump1", S_IFREG | S_IRUGO,
+ dir_dev, proc_get_rf_reg_dump1, dev);
+ if (!entry) {
+ pr_info("Unable to create_proc_read_entry!\n");
+ return;
+ }
+
+ entry = create_proc_read_entry("rf_reg_dump2", S_IFREG | S_IRUGO,
+ dir_dev, proc_get_rf_reg_dump2, dev);
+ if (!entry) {
+ pr_info("Unable to create_proc_read_entry!\n");
+ return;
+ }
+
+ rtw_hal_get_hwreg(padapter, HW_VAR_RF_TYPE, (u8 *)(&rf_type));
+ if ((RF_1T2R == rf_type) || (RF_1T1R == rf_type)) {
+ entry = create_proc_read_entry("rf_reg_dump3", S_IFREG | S_IRUGO,
+ dir_dev, proc_get_rf_reg_dump3, dev);
+ if (!entry) {
+ pr_info("Unable to create_proc_read_entry!\n");
+ return;
+ }
+
+ entry = create_proc_read_entry("rf_reg_dump4", S_IFREG | S_IRUGO,
+ dir_dev, proc_get_rf_reg_dump4, dev);
+ if (!entry) {
+ pr_info("Unable to create_proc_read_entry!\n");
+ return;
+ }
+ }
+
+#ifdef CONFIG_88EU_AP_MODE
+
+ entry = create_proc_read_entry("all_sta_info", S_IFREG | S_IRUGO,
+ dir_dev, proc_get_all_sta_info, dev);
+ if (!entry) {
+ pr_info("Unable to create_proc_read_entry!\n");
+ return;
+ }
+#endif
+
+ entry = create_proc_read_entry("best_channel", S_IFREG | S_IRUGO,
+ dir_dev, proc_get_best_channel, dev);
+ if (!entry) {
+ pr_info("Unable to create_proc_read_entry!\n");
+ return;
+ }
+
+ entry = create_proc_read_entry("rx_signal", S_IFREG | S_IRUGO,
+ dir_dev, proc_get_rx_signal, dev);
+ if (!entry) {
+ pr_info("Unable to create_proc_read_entry!\n");
+ return;
+ }
+ entry->write_proc = proc_set_rx_signal;
+ entry = create_proc_read_entry("ht_enable", S_IFREG | S_IRUGO,
+ dir_dev, proc_get_ht_enable, dev);
+ if (!entry) {
+ pr_info("Unable to create_proc_read_entry!\n");
+ return;
+ }
+ entry->write_proc = proc_set_ht_enable;
+
+ entry = create_proc_read_entry("cbw40_enable", S_IFREG | S_IRUGO,
+ dir_dev, proc_get_cbw40_enable, dev);
+ if (!entry) {
+ pr_info("Unable to create_proc_read_entry!\n");
+ return;
+ }
+ entry->write_proc = proc_set_cbw40_enable;
+
+ entry = create_proc_read_entry("ampdu_enable", S_IFREG | S_IRUGO,
+ dir_dev, proc_get_ampdu_enable, dev);
+ if (!entry) {
+ pr_info("Unable to create_proc_read_entry!\n");
+ return;
+ }
+ entry->write_proc = proc_set_ampdu_enable;
+
+ entry = create_proc_read_entry("rx_stbc", S_IFREG | S_IRUGO,
+ dir_dev, proc_get_rx_stbc, dev);
+ if (!entry) {
+ pr_info("Unable to create_proc_read_entry!\n");
+ return;
+ }
+ entry->write_proc = proc_set_rx_stbc;
+
+ entry = create_proc_read_entry("path_rssi", S_IFREG | S_IRUGO,
+ dir_dev, proc_get_two_path_rssi, dev);
+ if (!entry) {
+ pr_info("Unable to create_proc_read_entry!\n");
+ return;
+ }
+ entry = create_proc_read_entry("rssi_disp", S_IFREG | S_IRUGO,
+ dir_dev, proc_get_rssi_disp, dev);
+ if (!entry) {
+ pr_info("Unable to create_proc_read_entry!\n");
+ return;
+ }
+ entry->write_proc = proc_set_rssi_disp;
+}
+
+void rtw_proc_remove_one(struct net_device *dev)
+{
+ struct proc_dir_entry *dir_dev = NULL;
+ struct adapter *padapter = rtw_netdev_priv(dev);
+ u8 rf_type;
+
+ dir_dev = padapter->dir_dev;
+ padapter->dir_dev = NULL;
+
+ if (dir_dev) {
+ remove_proc_entry("write_reg", dir_dev);
+ remove_proc_entry("read_reg", dir_dev);
+ remove_proc_entry("fwstate", dir_dev);
+ remove_proc_entry("sec_info", dir_dev);
+ remove_proc_entry("mlmext_state", dir_dev);
+ remove_proc_entry("qos_option", dir_dev);
+ remove_proc_entry("ht_option", dir_dev);
+ remove_proc_entry("rf_info", dir_dev);
+ remove_proc_entry("ap_info", dir_dev);
+ remove_proc_entry("adapter_state", dir_dev);
+ remove_proc_entry("trx_info", dir_dev);
+ remove_proc_entry("mac_reg_dump1", dir_dev);
+ remove_proc_entry("mac_reg_dump2", dir_dev);
+ remove_proc_entry("mac_reg_dump3", dir_dev);
+ remove_proc_entry("bb_reg_dump1", dir_dev);
+ remove_proc_entry("bb_reg_dump2", dir_dev);
+ remove_proc_entry("bb_reg_dump3", dir_dev);
+ remove_proc_entry("rf_reg_dump1", dir_dev);
+ remove_proc_entry("rf_reg_dump2", dir_dev);
+ rtw_hal_get_hwreg(padapter, HW_VAR_RF_TYPE, (u8 *)(&rf_type));
+ if ((RF_1T2R == rf_type) || (RF_1T1R == rf_type)) {
+ remove_proc_entry("rf_reg_dump3", dir_dev);
+ remove_proc_entry("rf_reg_dump4", dir_dev);
+ }
+#ifdef CONFIG_88EU_AP_MODE
+ remove_proc_entry("all_sta_info", dir_dev);
+#endif
+
+ remove_proc_entry("best_channel", dir_dev);
+ remove_proc_entry("rx_signal", dir_dev);
+ remove_proc_entry("cbw40_enable", dir_dev);
+ remove_proc_entry("ht_enable", dir_dev);
+ remove_proc_entry("ampdu_enable", dir_dev);
+ remove_proc_entry("rx_stbc", dir_dev);
+ remove_proc_entry("path_rssi", dir_dev);
+ remove_proc_entry("rssi_disp", dir_dev);
+ remove_proc_entry(dev->name, rtw_proc);
+ dir_dev = NULL;
+ } else {
+ return;
+ }
+ rtw_proc_cnt--;
+
+ if (rtw_proc_cnt == 0) {
+ if (rtw_proc) {
+ remove_proc_entry("ver_info", rtw_proc);
+
+ remove_proc_entry(rtw_proc_name, init_net.proc_net);
+ rtw_proc = NULL;
+ }
+ }
+}
+#endif
+
+static uint loadparam(struct adapter *padapter, struct net_device *pnetdev)
+{
+ struct registry_priv *registry_par = &padapter->registrypriv;
+
+
+ GlobalDebugLevel = rtw_debug;
+ registry_par->chip_version = (u8)rtw_chip_version;
+ registry_par->rfintfs = (u8)rtw_rfintfs;
+ registry_par->lbkmode = (u8)rtw_lbkmode;
+ registry_par->network_mode = (u8)rtw_network_mode;
+
+ memcpy(registry_par->ssid.Ssid, "ANY", 3);
+ registry_par->ssid.SsidLength = 3;
+
+ registry_par->channel = (u8)rtw_channel;
+ registry_par->wireless_mode = (u8)rtw_wireless_mode;
+ registry_par->vrtl_carrier_sense = (u8)rtw_vrtl_carrier_sense;
+ registry_par->vcs_type = (u8)rtw_vcs_type;
+ registry_par->rts_thresh = (u16)rtw_rts_thresh;
+ registry_par->frag_thresh = (u16)rtw_frag_thresh;
+ registry_par->preamble = (u8)rtw_preamble;
+ registry_par->scan_mode = (u8)rtw_scan_mode;
+ registry_par->adhoc_tx_pwr = (u8)rtw_adhoc_tx_pwr;
+ registry_par->soft_ap = (u8)rtw_soft_ap;
+ registry_par->smart_ps = (u8)rtw_smart_ps;
+ registry_par->power_mgnt = (u8)rtw_power_mgnt;
+ registry_par->ips_mode = (u8)rtw_ips_mode;
+ registry_par->radio_enable = (u8)rtw_radio_enable;
+ registry_par->long_retry_lmt = (u8)rtw_long_retry_lmt;
+ registry_par->short_retry_lmt = (u8)rtw_short_retry_lmt;
+ registry_par->busy_thresh = (u16)rtw_busy_thresh;
+ registry_par->ack_policy = (u8)rtw_ack_policy;
+ registry_par->mp_mode = 0;
+ registry_par->software_encrypt = (u8)rtw_software_encrypt;
+ registry_par->software_decrypt = (u8)rtw_software_decrypt;
+ registry_par->acm_method = (u8)rtw_acm_method;
+
+ /* UAPSD */
+ registry_par->wmm_enable = (u8)rtw_wmm_enable;
+ registry_par->uapsd_enable = (u8)rtw_uapsd_enable;
+ registry_par->uapsd_max_sp = (u8)rtw_uapsd_max_sp;
+ registry_par->uapsd_acbk_en = (u8)rtw_uapsd_acbk_en;
+ registry_par->uapsd_acbe_en = (u8)rtw_uapsd_acbe_en;
+ registry_par->uapsd_acvi_en = (u8)rtw_uapsd_acvi_en;
+ registry_par->uapsd_acvo_en = (u8)rtw_uapsd_acvo_en;
+
+ registry_par->ht_enable = (u8)rtw_ht_enable;
+ registry_par->cbw40_enable = (u8)rtw_cbw40_enable;
+ registry_par->ampdu_enable = (u8)rtw_ampdu_enable;
+ registry_par->rx_stbc = (u8)rtw_rx_stbc;
+ registry_par->ampdu_amsdu = (u8)rtw_ampdu_amsdu;
+ registry_par->lowrate_two_xmit = (u8)rtw_lowrate_two_xmit;
+ registry_par->rf_config = (u8)rtw_rf_config;
+ registry_par->low_power = (u8)rtw_low_power;
+ registry_par->wifi_spec = (u8)rtw_wifi_spec;
+ registry_par->channel_plan = (u8)rtw_channel_plan;
+ registry_par->bAcceptAddbaReq = (u8)rtw_AcceptAddbaReq;
+ registry_par->antdiv_cfg = (u8)rtw_antdiv_cfg;
+ registry_par->antdiv_type = (u8)rtw_antdiv_type;
+ registry_par->hwpdn_mode = (u8)rtw_hwpdn_mode;/* 0:disable, 1:enable, 2:by EFUSE config */
+ registry_par->hwpwrp_detect = (u8)rtw_hwpwrp_detect;/* 0:disable, 1:enable */
+ registry_par->hw_wps_pbc = (u8)rtw_hw_wps_pbc;
+
+ registry_par->max_roaming_times = (u8)rtw_max_roaming_times;
+
+ registry_par->fw_iol = rtw_fw_iol;
+
+ registry_par->enable80211d = (u8)rtw_80211d;
+ snprintf(registry_par->ifname, 16, "%s", ifname);
+ snprintf(registry_par->if2name, 16, "%s", if2name);
+ registry_par->notch_filter = (u8)rtw_notch_filter;
+ return _SUCCESS;
+}
+
+static int rtw_net_set_mac_address(struct net_device *pnetdev, void *p)
+{
+ struct adapter *padapter = (struct adapter *)rtw_netdev_priv(pnetdev);
+ struct sockaddr *addr = p;
+
+ if (!padapter->bup)
+ memcpy(padapter->eeprompriv.mac_addr, addr->sa_data, ETH_ALEN);
+
+ return 0;
+}
+
+static struct net_device_stats *rtw_net_get_stats(struct net_device *pnetdev)
+{
+ struct adapter *padapter = (struct adapter *)rtw_netdev_priv(pnetdev);
+ struct xmit_priv *pxmitpriv = &(padapter->xmitpriv);
+ struct recv_priv *precvpriv = &(padapter->recvpriv);
+
+ padapter->stats.tx_packets = pxmitpriv->tx_pkts;/* pxmitpriv->tx_pkts++; */
+ padapter->stats.rx_packets = precvpriv->rx_pkts;/* precvpriv->rx_pkts++; */
+ padapter->stats.tx_dropped = pxmitpriv->tx_drop;
+ padapter->stats.rx_dropped = precvpriv->rx_drop;
+ padapter->stats.tx_bytes = pxmitpriv->tx_bytes;
+ padapter->stats.rx_bytes = precvpriv->rx_bytes;
+ return &padapter->stats;
+}
+
+/*
+ * AC to queue mapping
+ *
+ * AC_VO -> queue 0
+ * AC_VI -> queue 1
+ * AC_BE -> queue 2
+ * AC_BK -> queue 3
+ */
+static const u16 rtw_1d_to_queue[8] = { 2, 3, 3, 2, 1, 1, 0, 0 };
+
+/* Given a data frame determine the 802.1p/1d tag to use. */
+static unsigned int rtw_classify8021d(struct sk_buff *skb)
+{
+ unsigned int dscp;
+
+ /* skb->priority values from 256->263 are magic values to
+ * directly indicate a specific 802.1d priority. This is used
+ * to allow 802.1d priority to be passed directly in from VLAN
+ * tags, etc.
+ */
+ if (skb->priority >= 256 && skb->priority <= 263)
+ return skb->priority - 256;
+
+ switch (skb->protocol) {
+ case htons(ETH_P_IP):
+ dscp = ip_hdr(skb)->tos & 0xfc;
+ break;
+ default:
+ return 0;
+ }
+
+ return dscp >> 5;
+}
+
+static u16 rtw_select_queue(struct net_device *dev, struct sk_buff *skb,
+ void *accel_priv, select_queue_fallback_t fallback)
+{
+ struct adapter *padapter = rtw_netdev_priv(dev);
+ struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
+
+ skb->priority = rtw_classify8021d(skb);
+
+ if (pmlmepriv->acm_mask != 0)
+ skb->priority = qos_acm(pmlmepriv->acm_mask, skb->priority);
+
+ return rtw_1d_to_queue[skb->priority];
+}
+
+u16 rtw_recv_select_queue(struct sk_buff *skb)
+{
+ struct iphdr *piphdr;
+ unsigned int dscp;
+ __be16 eth_type;
+ u32 priority;
+ u8 *pdata = skb->data;
+
+ memcpy(&eth_type, pdata+(ETH_ALEN<<1), 2);
+
+ switch (eth_type) {
+ case htons(ETH_P_IP):
+ piphdr = (struct iphdr *)(pdata+ETH_HLEN);
+ dscp = piphdr->tos & 0xfc;
+ priority = dscp >> 5;
+ break;
+ default:
+ priority = 0;
+ }
+
+ return rtw_1d_to_queue[priority];
+}
+
+static const struct net_device_ops rtw_netdev_ops = {
+ .ndo_open = netdev_open,
+ .ndo_stop = netdev_close,
+ .ndo_start_xmit = rtw_xmit_entry,
+ .ndo_select_queue = rtw_select_queue,
+ .ndo_set_mac_address = rtw_net_set_mac_address,
+ .ndo_get_stats = rtw_net_get_stats,
+ .ndo_do_ioctl = rtw_ioctl,
+};
+
+int rtw_init_netdev_name(struct net_device *pnetdev, const char *ifname)
+{
+ if (dev_alloc_name(pnetdev, ifname) < 0)
+ RT_TRACE(_module_os_intfs_c_, _drv_err_, ("dev_alloc_name, fail!\n"));
+
+ netif_carrier_off(pnetdev);
+ return 0;
+}
+
+static const struct device_type wlan_type = {
+ .name = "wlan",
+};
+
+struct net_device *rtw_init_netdev(struct adapter *old_padapter)
+{
+ struct adapter *padapter;
+ struct net_device *pnetdev = NULL;
+
+ RT_TRACE(_module_os_intfs_c_, _drv_info_, ("+init_net_dev\n"));
+
+ if (old_padapter != NULL)
+ pnetdev = rtw_alloc_etherdev_with_old_priv(sizeof(struct adapter), (void *)old_padapter);
+
+ if (!pnetdev)
+ return NULL;
+
+ pnetdev->dev.type = &wlan_type;
+ padapter = rtw_netdev_priv(pnetdev);
+ padapter->pnetdev = pnetdev;
+ DBG_88E("register rtw_netdev_ops to netdev_ops\n");
+ pnetdev->netdev_ops = &rtw_netdev_ops;
+ pnetdev->watchdog_timeo = HZ*3; /* 3 second timeout */
+ pnetdev->wireless_handlers = (struct iw_handler_def *)&rtw_handlers_def;
+
+ /* step 2. */
+ loadparam(padapter, pnetdev);
+
+ return pnetdev;
+}
+
+u32 rtw_start_drv_threads(struct adapter *padapter)
+{
+ u32 _status = _SUCCESS;
+
+ RT_TRACE(_module_os_intfs_c_, _drv_info_, ("+rtw_start_drv_threads\n"));
+
+ padapter->cmdThread = kthread_run(rtw_cmd_thread, padapter, "RTW_CMD_THREAD");
+ if (IS_ERR(padapter->cmdThread))
+ _status = _FAIL;
+ else
+ _rtw_down_sema(&padapter->cmdpriv.terminate_cmdthread_sema); /* wait for cmd_thread to run */
+
+ return _status;
+}
+
+void rtw_stop_drv_threads(struct adapter *padapter)
+{
+ RT_TRACE(_module_os_intfs_c_, _drv_info_, ("+rtw_stop_drv_threads\n"));
+
+ /* Below is to termindate rtw_cmd_thread & event_thread... */
+ up(&padapter->cmdpriv.cmd_queue_sema);
+ if (padapter->cmdThread)
+ _rtw_down_sema(&padapter->cmdpriv.terminate_cmdthread_sema);
+
+}
+
+static u8 rtw_init_default_value(struct adapter *padapter)
+{
+ struct registry_priv *pregistrypriv = &padapter->registrypriv;
+ struct xmit_priv *pxmitpriv = &padapter->xmitpriv;
+ struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
+ struct security_priv *psecuritypriv = &padapter->securitypriv;
+
+ /* xmit_priv */
+ pxmitpriv->vcs_setting = pregistrypriv->vrtl_carrier_sense;
+ pxmitpriv->vcs = pregistrypriv->vcs_type;
+ pxmitpriv->vcs_type = pregistrypriv->vcs_type;
+ pxmitpriv->frag_len = pregistrypriv->frag_thresh;
+
+ /* mlme_priv */
+ pmlmepriv->scan_interval = SCAN_INTERVAL;/* 30*2 sec = 60sec */
+ pmlmepriv->scan_mode = SCAN_ACTIVE;
+
+ /* ht_priv */
+ pmlmepriv->htpriv.ampdu_enable = false;/* set to disabled */
+
+ /* security_priv */
+ psecuritypriv->binstallGrpkey = _FAIL;
+ psecuritypriv->sw_encrypt = pregistrypriv->software_encrypt;
+ psecuritypriv->sw_decrypt = pregistrypriv->software_decrypt;
+ psecuritypriv->dot11AuthAlgrthm = dot11AuthAlgrthm_Open; /* open system */
+ psecuritypriv->dot11PrivacyAlgrthm = _NO_PRIVACY_;
+ psecuritypriv->dot11PrivacyKeyIndex = 0;
+ psecuritypriv->dot118021XGrpPrivacy = _NO_PRIVACY_;
+ psecuritypriv->dot118021XGrpKeyid = 1;
+ psecuritypriv->ndisauthtype = Ndis802_11AuthModeOpen;
+ psecuritypriv->ndisencryptstatus = Ndis802_11WEPDisabled;
+
+ /* registry_priv */
+ rtw_init_registrypriv_dev_network(padapter);
+ rtw_update_registrypriv_dev_network(padapter);
+
+ /* hal_priv */
+ rtw_hal_def_value_init(padapter);
+
+ /* misc. */
+ padapter->bReadPortCancel = false;
+ padapter->bWritePortCancel = false;
+ padapter->bRxRSSIDisplay = 0;
+ padapter->bNotifyChannelChange = 0;
+ return _SUCCESS;
+}
+
+u8 rtw_reset_drv_sw(struct adapter *padapter)
+{
+ struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
+ struct pwrctrl_priv *pwrctrlpriv = &padapter->pwrctrlpriv;
+
+ /* hal_priv */
+ rtw_hal_def_value_init(padapter);
+ padapter->bReadPortCancel = false;
+ padapter->bWritePortCancel = false;
+ padapter->bRxRSSIDisplay = 0;
+ pmlmepriv->scan_interval = SCAN_INTERVAL;/* 30*2 sec = 60sec */
+
+ padapter->xmitpriv.tx_pkts = 0;
+ padapter->recvpriv.rx_pkts = 0;
+
+ pmlmepriv->LinkDetectInfo.bBusyTraffic = false;
+
+ _clr_fwstate_(pmlmepriv, _FW_UNDER_SURVEY | _FW_UNDER_LINKING);
+ rtw_hal_sreset_init(padapter);
+ pwrctrlpriv->pwr_state_check_cnts = 0;
+
+ /* mlmeextpriv */
+ padapter->mlmeextpriv.sitesurvey_res.state = SCAN_DISABLE;
+
+ rtw_set_signal_stat_timer(&padapter->recvpriv);
+
+ return _SUCCESS;
+}
+
+u8 rtw_init_drv_sw(struct adapter *padapter)
+{
+ u8 ret8 = _SUCCESS;
+
+
+ RT_TRACE(_module_os_intfs_c_, _drv_info_, ("+rtw_init_drv_sw\n"));
+
+ if ((rtw_init_cmd_priv(&padapter->cmdpriv)) == _FAIL) {
+ RT_TRACE(_module_os_intfs_c_, _drv_err_, ("\n Can't init cmd_priv\n"));
+ ret8 = _FAIL;
+ goto exit;
+ }
+
+ padapter->cmdpriv.padapter = padapter;
+
+ if (rtw_init_mlme_priv(padapter) == _FAIL) {
+ RT_TRACE(_module_os_intfs_c_, _drv_err_, ("\n Can't init mlme_priv\n"));
+ ret8 = _FAIL;
+ goto exit;
+ }
+
+ if (init_mlme_ext_priv(padapter) == _FAIL) {
+ RT_TRACE(_module_os_intfs_c_, _drv_err_, ("\n Can't init mlme_ext_priv\n"));
+ ret8 = _FAIL;
+ goto exit;
+ }
+
+ if (_rtw_init_xmit_priv(&padapter->xmitpriv, padapter) == _FAIL) {
+ DBG_88E("Can't _rtw_init_xmit_priv\n");
+ ret8 = _FAIL;
+ goto exit;
+ }
+
+ if (_rtw_init_recv_priv(&padapter->recvpriv, padapter) == _FAIL) {
+ DBG_88E("Can't _rtw_init_recv_priv\n");
+ ret8 = _FAIL;
+ goto exit;
+ }
+
+ if (_rtw_init_sta_priv(&padapter->stapriv) == _FAIL) {
+ DBG_88E("Can't _rtw_init_sta_priv\n");
+ ret8 = _FAIL;
+ goto exit;
+ }
+
+ padapter->stapriv.padapter = padapter;
+
+ rtw_init_bcmc_stainfo(padapter);
+
+ rtw_init_pwrctrl_priv(padapter);
+
+ ret8 = rtw_init_default_value(padapter);
+
+ rtw_hal_dm_init(padapter);
+ rtw_hal_sw_led_init(padapter);
+
+ rtw_hal_sreset_init(padapter);
+
+ spin_lock_init(&padapter->br_ext_lock);
+
+exit:
+ RT_TRACE(_module_os_intfs_c_, _drv_info_, ("-rtw_init_drv_sw\n"));
+
+
+ return ret8;
+}
+
+void rtw_cancel_all_timer(struct adapter *padapter)
+{
+ RT_TRACE(_module_os_intfs_c_, _drv_info_, ("+rtw_cancel_all_timer\n"));
+
+ del_timer_sync(&padapter->mlmepriv.assoc_timer);
+ RT_TRACE(_module_os_intfs_c_, _drv_info_, ("rtw_cancel_all_timer:cancel association timer complete!\n"));
+
+ del_timer_sync(&padapter->mlmepriv.scan_to_timer);
+ RT_TRACE(_module_os_intfs_c_, _drv_info_, ("rtw_cancel_all_timer:cancel scan_to_timer!\n"));
+
+ del_timer_sync(&padapter->mlmepriv.dynamic_chk_timer);
+ RT_TRACE(_module_os_intfs_c_, _drv_info_, ("rtw_cancel_all_timer:cancel dynamic_chk_timer!\n"));
+
+ /* cancel sw led timer */
+ rtw_hal_sw_led_deinit(padapter);
+ RT_TRACE(_module_os_intfs_c_, _drv_info_, ("rtw_cancel_all_timer:cancel DeInitSwLeds!\n"));
+
+ del_timer_sync(&padapter->pwrctrlpriv.pwr_state_check_timer);
+
+ del_timer_sync(&padapter->recvpriv.signal_stat_timer);
+}
+
+u8 rtw_free_drv_sw(struct adapter *padapter)
+{
+ RT_TRACE(_module_os_intfs_c_, _drv_info_, ("==>rtw_free_drv_sw"));
+
+ free_mlme_ext_priv(&padapter->mlmeextpriv);
+
+ rtw_free_mlme_priv(&padapter->mlmepriv);
+ _rtw_free_xmit_priv(&padapter->xmitpriv);
+
+ _rtw_free_sta_priv(&padapter->stapriv); /* will free bcmc_stainfo here */
+
+ _rtw_free_recv_priv(&padapter->recvpriv);
+
+ rtw_hal_free_data(padapter);
+
+ RT_TRACE(_module_os_intfs_c_, _drv_info_, ("<== rtw_free_drv_sw\n"));
+
+ /* free the old_pnetdev */
+ if (padapter->rereg_nd_name_priv.old_pnetdev) {
+ free_netdev(padapter->rereg_nd_name_priv.old_pnetdev);
+ padapter->rereg_nd_name_priv.old_pnetdev = NULL;
+ }
+
+ mutex_destroy(&padapter->hw_init_mutex);
+
+ RT_TRACE(_module_os_intfs_c_, _drv_info_, ("-rtw_free_drv_sw\n"));
+
+ return _SUCCESS;
+}
+
+int _netdev_open(struct net_device *pnetdev)
+{
+ uint status;
+ struct adapter *padapter = (struct adapter *)rtw_netdev_priv(pnetdev);
+ struct pwrctrl_priv *pwrctrlpriv = &padapter->pwrctrlpriv;
+
+ RT_TRACE(_module_os_intfs_c_, _drv_info_, ("+88eu_drv - dev_open\n"));
+ DBG_88E("+88eu_drv - drv_open, bup =%d\n", padapter->bup);
+
+ if (pwrctrlpriv->ps_flag) {
+ padapter->net_closed = false;
+ goto netdev_open_normal_process;
+ }
+
+ if (!padapter->bup) {
+ padapter->bDriverStopped = false;
+ padapter->bSurpriseRemoved = false;
+
+ status = rtw_hal_init(padapter);
+ if (status == _FAIL) {
+ RT_TRACE(_module_os_intfs_c_, _drv_err_, ("rtl88eu_hal_init(): Can't init h/w!\n"));
+ goto netdev_open_error;
+ }
+
+ pr_info("MAC Address = %pM\n", pnetdev->dev_addr);
+
+ status = rtw_start_drv_threads(padapter);
+ if (status == _FAIL) {
+ pr_info("Initialize driver software resource Failed!\n");
+ goto netdev_open_error;
+ }
+
+ if (init_hw_mlme_ext(padapter) == _FAIL) {
+ pr_info("can't init mlme_ext_priv\n");
+ goto netdev_open_error;
+ }
+ if (padapter->intf_start)
+ padapter->intf_start(padapter);
+ rtw_proc_init_one(pnetdev);
+
+ rtw_led_control(padapter, LED_CTL_NO_LINK);
+
+ padapter->bup = true;
+ }
+ padapter->net_closed = false;
+
+ mod_timer(&padapter->mlmepriv.dynamic_chk_timer,
+ jiffies + msecs_to_jiffies(2000));
+
+ padapter->pwrctrlpriv.bips_processing = false;
+ rtw_set_pwr_state_check_timer(&padapter->pwrctrlpriv);
+
+ if (!rtw_netif_queue_stopped(pnetdev))
+ netif_tx_start_all_queues(pnetdev);
+ else
+ netif_tx_wake_all_queues(pnetdev);
+
+netdev_open_normal_process:
+ RT_TRACE(_module_os_intfs_c_, _drv_info_, ("-88eu_drv - dev_open\n"));
+ DBG_88E("-88eu_drv - drv_open, bup =%d\n", padapter->bup);
+ return 0;
+
+netdev_open_error:
+ padapter->bup = false;
+ netif_carrier_off(pnetdev);
+ netif_tx_stop_all_queues(pnetdev);
+ RT_TRACE(_module_os_intfs_c_, _drv_err_, ("-88eu_drv - dev_open, fail!\n"));
+ DBG_88E("-88eu_drv - drv_open fail, bup =%d\n", padapter->bup);
+ return -1;
+}
+
+int netdev_open(struct net_device *pnetdev)
+{
+ int ret;
+ struct adapter *padapter = (struct adapter *)rtw_netdev_priv(pnetdev);
+
+ _enter_critical_mutex(&padapter->hw_init_mutex, NULL);
+ ret = _netdev_open(pnetdev);
+ mutex_unlock(&padapter->hw_init_mutex);
+ return ret;
+}
+
+static int ips_netdrv_open(struct adapter *padapter)
+{
+ int status = _SUCCESS;
+ padapter->net_closed = false;
+ DBG_88E("===> %s.........\n", __func__);
+
+ padapter->bDriverStopped = false;
+ padapter->bSurpriseRemoved = false;
+
+ status = rtw_hal_init(padapter);
+ if (status == _FAIL) {
+ RT_TRACE(_module_os_intfs_c_, _drv_err_, ("ips_netdrv_open(): Can't init h/w!\n"));
+ goto netdev_open_error;
+ }
+
+ if (padapter->intf_start)
+ padapter->intf_start(padapter);
+
+ rtw_set_pwr_state_check_timer(&padapter->pwrctrlpriv);
+ mod_timer(&padapter->mlmepriv.dynamic_chk_timer,
+ jiffies + msecs_to_jiffies(5000));
+
+ return _SUCCESS;
+
+netdev_open_error:
+ DBG_88E("-ips_netdrv_open - drv_open failure, bup =%d\n", padapter->bup);
+
+ return _FAIL;
+}
+
+
+int rtw_ips_pwr_up(struct adapter *padapter)
+{
+ int result;
+ u32 start_time = jiffies;
+ DBG_88E("===> rtw_ips_pwr_up..............\n");
+ rtw_reset_drv_sw(padapter);
+
+ result = ips_netdrv_open(padapter);
+
+ rtw_led_control(padapter, LED_CTL_NO_LINK);
+
+ DBG_88E("<=== rtw_ips_pwr_up.............. in %dms\n", rtw_get_passing_time_ms(start_time));
+ return result;
+}
+
+void rtw_ips_pwr_down(struct adapter *padapter)
+{
+ u32 start_time = jiffies;
+ DBG_88E("===> rtw_ips_pwr_down...................\n");
+
+ padapter->net_closed = true;
+
+ rtw_led_control(padapter, LED_CTL_POWER_OFF);
+
+ rtw_ips_dev_unload(padapter);
+ DBG_88E("<=== rtw_ips_pwr_down..................... in %dms\n", rtw_get_passing_time_ms(start_time));
+}
+
+void rtw_ips_dev_unload(struct adapter *padapter)
+{
+ DBG_88E("====> %s...\n", __func__);
+
+ rtw_hal_set_hwreg(padapter, HW_VAR_FIFO_CLEARN_UP, NULL);
+
+ if (padapter->intf_stop)
+ padapter->intf_stop(padapter);
+
+ /* s5. */
+ if (!padapter->bSurpriseRemoved)
+ rtw_hal_deinit(padapter);
+}
+
+int pm_netdev_open(struct net_device *pnetdev, u8 bnormal)
+{
+ int status;
+
+ if (bnormal)
+ status = netdev_open(pnetdev);
+ else
+ status = (_SUCCESS == ips_netdrv_open((struct adapter *)rtw_netdev_priv(pnetdev))) ? (0) : (-1);
+ return status;
+}
+
+int netdev_close(struct net_device *pnetdev)
+{
+ struct adapter *padapter = (struct adapter *)rtw_netdev_priv(pnetdev);
+ struct hal_data_8188e *rtlhal = GET_HAL_DATA(padapter);
+
+ RT_TRACE(_module_os_intfs_c_, _drv_info_, ("+88eu_drv - drv_close\n"));
+
+ if (padapter->pwrctrlpriv.bInternalAutoSuspend) {
+ if (padapter->pwrctrlpriv.rf_pwrstate == rf_off)
+ padapter->pwrctrlpriv.ps_flag = true;
+ }
+ padapter->net_closed = true;
+
+ if (padapter->pwrctrlpriv.rf_pwrstate == rf_on) {
+ DBG_88E("(2)88eu_drv - drv_close, bup =%d, hw_init_completed =%d\n",
+ padapter->bup, padapter->hw_init_completed);
+
+ /* s1. */
+ if (pnetdev) {
+ if (!rtw_netif_queue_stopped(pnetdev))
+ netif_tx_stop_all_queues(pnetdev);
+ }
+
+ /* s2. */
+ LeaveAllPowerSaveMode(padapter);
+ rtw_disassoc_cmd(padapter, 500, false);
+ /* s2-2. indicate disconnect to os */
+ rtw_indicate_disconnect(padapter);
+ /* s2-3. */
+ rtw_free_assoc_resources(padapter, 1);
+ /* s2-4. */
+ rtw_free_network_queue(padapter, true);
+ /* Close LED */
+ rtw_led_control(padapter, LED_CTL_POWER_OFF);
+ }
+
+ kfree(rtlhal->pfirmware);
+ rtlhal->pfirmware = NULL;
+
+ RT_TRACE(_module_os_intfs_c_, _drv_info_, ("-88eu_drv - drv_close\n"));
+ DBG_88E("-88eu_drv - drv_close, bup =%d\n", padapter->bup);
+ return 0;
+}
diff --git a/drivers/staging/rtl8188eu/os_dep/osdep_service.c b/drivers/staging/rtl8188eu/os_dep/osdep_service.c
new file mode 100644
index 000000000..abcb3a858
--- /dev/null
+++ b/drivers/staging/rtl8188eu/os_dep/osdep_service.c
@@ -0,0 +1,165 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2012 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ *
+ ******************************************************************************/
+
+
+#define _OSDEP_SERVICE_C_
+
+#include <osdep_service.h>
+#include <osdep_intf.h>
+#include <drv_types.h>
+#include <recv_osdep.h>
+#include <linux/vmalloc.h>
+#include <rtw_ioctl_set.h>
+
+/*
+* Translate the OS dependent @param error_code to OS independent RTW_STATUS_CODE
+* @return: one of RTW_STATUS_CODE
+*/
+inline int RTW_STATUS_CODE(int error_code)
+{
+ if (error_code >= 0)
+ return _SUCCESS;
+ return _FAIL;
+}
+
+u8 *_rtw_malloc(u32 sz)
+{
+ u8 *pbuf = NULL;
+
+ pbuf = kmalloc(sz, in_interrupt() ? GFP_ATOMIC : GFP_KERNEL);
+ return pbuf;
+}
+
+void *rtw_malloc2d(int h, int w, int size)
+{
+ int j;
+
+ void **a = kzalloc(h*sizeof(void *) + h*w*size, GFP_KERNEL);
+ if (a == NULL) {
+ pr_info("%s: alloc memory fail!\n", __func__);
+ return NULL;
+ }
+
+ for (j = 0; j < h; j++)
+ a[j] = ((char *)(a+h)) + j*w*size;
+
+ return a;
+}
+
+u32 _rtw_down_sema(struct semaphore *sema)
+{
+ if (down_interruptible(sema))
+ return _FAIL;
+ else
+ return _SUCCESS;
+}
+
+void _rtw_init_queue(struct __queue *pqueue)
+{
+ INIT_LIST_HEAD(&(pqueue->queue));
+ spin_lock_init(&(pqueue->lock));
+}
+
+/* the input parameter start must be in jiffies */
+inline s32 rtw_get_passing_time_ms(u32 start)
+{
+ return jiffies_to_msecs(jiffies-start);
+}
+
+struct net_device *rtw_alloc_etherdev_with_old_priv(int sizeof_priv,
+ void *old_priv)
+{
+ struct net_device *pnetdev;
+ struct rtw_netdev_priv_indicator *pnpi;
+
+ pnetdev = alloc_etherdev_mq(sizeof(struct rtw_netdev_priv_indicator), 4);
+ if (!pnetdev)
+ goto RETURN;
+
+ pnpi = netdev_priv(pnetdev);
+ pnpi->priv = old_priv;
+ pnpi->sizeof_priv = sizeof_priv;
+
+RETURN:
+ return pnetdev;
+}
+
+void rtw_free_netdev(struct net_device *netdev)
+{
+ struct rtw_netdev_priv_indicator *pnpi;
+
+ if (!netdev)
+ goto RETURN;
+
+ pnpi = netdev_priv(netdev);
+
+ if (!pnpi->priv)
+ goto RETURN;
+
+ vfree(pnpi->priv);
+ free_netdev(netdev);
+
+RETURN:
+ return;
+}
+
+u64 rtw_modular64(u64 x, u64 y)
+{
+ return do_div(x, y);
+}
+
+void rtw_buf_free(u8 **buf, u32 *buf_len)
+{
+ *buf_len = 0;
+ kfree(*buf);
+ *buf = NULL;
+}
+
+void rtw_buf_update(u8 **buf, u32 *buf_len, u8 *src, u32 src_len)
+{
+ u32 ori_len = 0, dup_len = 0;
+ u8 *ori = NULL;
+ u8 *dup = NULL;
+
+ if (!buf || !buf_len)
+ return;
+
+ if (!src || !src_len)
+ goto keep_ori;
+
+ /* duplicate src */
+ dup = rtw_malloc(src_len);
+ if (dup) {
+ dup_len = src_len;
+ memcpy(dup, src, dup_len);
+ }
+
+keep_ori:
+ ori = *buf;
+ ori_len = *buf_len;
+
+ /* replace buf with dup */
+ *buf_len = 0;
+ *buf = dup;
+ *buf_len = dup_len;
+
+ /* free ori */
+ kfree(ori);
+}
diff --git a/drivers/staging/rtl8188eu/os_dep/recv_linux.c b/drivers/staging/rtl8188eu/os_dep/recv_linux.c
new file mode 100644
index 000000000..05701328d
--- /dev/null
+++ b/drivers/staging/rtl8188eu/os_dep/recv_linux.c
@@ -0,0 +1,200 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2011 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ *
+ ******************************************************************************/
+#define _RECV_OSDEP_C_
+
+#include <osdep_service.h>
+#include <drv_types.h>
+
+#include <wifi.h>
+#include <recv_osdep.h>
+
+#include <osdep_intf.h>
+#include <usb_ops_linux.h>
+
+/* alloc os related resource in struct recv_frame */
+int rtw_os_recv_resource_alloc(struct adapter *padapter,
+ struct recv_frame *precvframe)
+{
+ precvframe->pkt_newalloc = NULL;
+ precvframe->pkt = NULL;
+ return _SUCCESS;
+}
+
+/* alloc os related resource in struct recv_buf */
+int rtw_os_recvbuf_resource_alloc(struct adapter *padapter,
+ struct recv_buf *precvbuf)
+{
+ int res = _SUCCESS;
+
+ precvbuf->purb = usb_alloc_urb(0, GFP_KERNEL);
+ if (precvbuf->purb == NULL)
+ res = _FAIL;
+ precvbuf->pskb = NULL;
+ precvbuf->reuse = false;
+ return res;
+}
+
+void rtw_handle_tkip_mic_err(struct adapter *padapter, u8 bgroup)
+{
+ union iwreq_data wrqu;
+ struct iw_michaelmicfailure ev;
+ struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
+ struct security_priv *psecuritypriv = &padapter->securitypriv;
+ u32 cur_time = 0;
+
+ if (psecuritypriv->last_mic_err_time == 0) {
+ psecuritypriv->last_mic_err_time = jiffies;
+ } else {
+ cur_time = jiffies;
+
+ if (cur_time - psecuritypriv->last_mic_err_time < 60*HZ) {
+ psecuritypriv->btkip_countermeasure = true;
+ psecuritypriv->last_mic_err_time = 0;
+ psecuritypriv->btkip_countermeasure_time = cur_time;
+ } else {
+ psecuritypriv->last_mic_err_time = jiffies;
+ }
+ }
+
+ memset(&ev, 0x00, sizeof(ev));
+ if (bgroup)
+ ev.flags |= IW_MICFAILURE_GROUP;
+ else
+ ev.flags |= IW_MICFAILURE_PAIRWISE;
+
+ ev.src_addr.sa_family = ARPHRD_ETHER;
+ memcpy(ev.src_addr.sa_data, &pmlmepriv->assoc_bssid[0], ETH_ALEN);
+ memset(&wrqu, 0x00, sizeof(wrqu));
+ wrqu.data.length = sizeof(ev);
+ wireless_send_event(padapter->pnetdev, IWEVMICHAELMICFAILURE,
+ &wrqu, (char *)&ev);
+}
+
+int rtw_recv_indicatepkt(struct adapter *padapter,
+ struct recv_frame *precv_frame)
+{
+ struct recv_priv *precvpriv;
+ struct __queue *pfree_recv_queue;
+ struct sk_buff *skb;
+ struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
+
+
+ precvpriv = &(padapter->recvpriv);
+ pfree_recv_queue = &(precvpriv->free_recv_queue);
+
+ skb = precv_frame->pkt;
+ if (skb == NULL) {
+ RT_TRACE(_module_recv_osdep_c_, _drv_err_,
+ ("rtw_recv_indicatepkt():skb == NULL something wrong!!!!\n"));
+ goto _recv_indicatepkt_drop;
+ }
+
+ RT_TRACE(_module_recv_osdep_c_, _drv_info_,
+ ("rtw_recv_indicatepkt():skb != NULL !!!\n"));
+ RT_TRACE(_module_recv_osdep_c_, _drv_info_,
+ ("rtw_recv_indicatepkt():precv_frame->rx_head =%p precv_frame->hdr.rx_data =%p\n",
+ precv_frame->rx_head, precv_frame->rx_data));
+ RT_TRACE(_module_recv_osdep_c_, _drv_info_,
+ ("precv_frame->hdr.rx_tail =%p precv_frame->rx_end =%p precv_frame->hdr.len =%d\n",
+ precv_frame->rx_tail, precv_frame->rx_end,
+ precv_frame->len));
+
+ skb->data = precv_frame->rx_data;
+
+ skb_set_tail_pointer(skb, precv_frame->len);
+
+ skb->len = precv_frame->len;
+
+ RT_TRACE(_module_recv_osdep_c_, _drv_info_,
+ ("skb->head =%p skb->data =%p skb->tail =%p skb->end =%p skb->len =%d\n",
+ skb->head, skb->data, skb_tail_pointer(skb),
+ skb_end_pointer(skb), skb->len));
+
+ if (check_fwstate(pmlmepriv, WIFI_AP_STATE)) {
+ struct sk_buff *pskb2 = NULL;
+ struct sta_info *psta = NULL;
+ struct sta_priv *pstapriv = &padapter->stapriv;
+ struct rx_pkt_attrib *pattrib = &precv_frame->attrib;
+ int bmcast = IS_MCAST(pattrib->dst);
+
+ if (memcmp(pattrib->dst, myid(&padapter->eeprompriv),
+ ETH_ALEN)) {
+ if (bmcast) {
+ psta = rtw_get_bcmc_stainfo(padapter);
+ pskb2 = skb_clone(skb, GFP_ATOMIC);
+ } else {
+ psta = rtw_get_stainfo(pstapriv, pattrib->dst);
+ }
+
+ if (psta) {
+ struct net_device *pnetdev;
+
+ pnetdev = (struct net_device *)padapter->pnetdev;
+ skb->dev = pnetdev;
+ skb_set_queue_mapping(skb, rtw_recv_select_queue(skb));
+
+ rtw_xmit_entry(skb, pnetdev);
+
+ if (bmcast)
+ skb = pskb2;
+ else
+ goto _recv_indicatepkt_end;
+ }
+ }
+ }
+
+ rcu_read_lock();
+ rcu_dereference(padapter->pnetdev->rx_handler_data);
+ rcu_read_unlock();
+
+ skb->ip_summed = CHECKSUM_NONE;
+ skb->dev = padapter->pnetdev;
+ skb->protocol = eth_type_trans(skb, padapter->pnetdev);
+
+ netif_rx(skb);
+
+_recv_indicatepkt_end:
+
+ /* pointers to NULL before rtw_free_recvframe() */
+ precv_frame->pkt = NULL;
+
+ rtw_free_recvframe(precv_frame, pfree_recv_queue);
+
+ RT_TRACE(_module_recv_osdep_c_, _drv_info_,
+ ("\n rtw_recv_indicatepkt :after netif_rx!!!!\n"));
+
+
+ return _SUCCESS;
+
+_recv_indicatepkt_drop:
+
+ /* enqueue back to free_recv_queue */
+ rtw_free_recvframe(precv_frame, pfree_recv_queue);
+
+ return _FAIL;
+}
+
+void rtw_init_recv_timer(struct recv_reorder_ctrl *preorder_ctrl)
+{
+
+ setup_timer(&preorder_ctrl->reordering_ctrl_timer,
+ rtw_reordering_ctrl_timeout_handler,
+ (unsigned long)preorder_ctrl);
+}
diff --git a/drivers/staging/rtl8188eu/os_dep/rtw_android.c b/drivers/staging/rtl8188eu/os_dep/rtw_android.c
new file mode 100644
index 000000000..99ce07700
--- /dev/null
+++ b/drivers/staging/rtl8188eu/os_dep/rtw_android.c
@@ -0,0 +1,270 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2011 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ *
+ ******************************************************************************/
+
+#include <linux/module.h>
+#include <linux/netdevice.h>
+
+#include <rtw_android.h>
+#include <osdep_service.h>
+#include <rtw_debug.h>
+#include <rtw_ioctl_set.h>
+
+static const char *android_wifi_cmd_str[ANDROID_WIFI_CMD_MAX] = {
+ "START",
+ "STOP",
+ "SCAN-ACTIVE",
+ "SCAN-PASSIVE",
+ "RSSI",
+ "LINKSPEED",
+ "RXFILTER-START",
+ "RXFILTER-STOP",
+ "RXFILTER-ADD",
+ "RXFILTER-REMOVE",
+ "BTCOEXSCAN-START",
+ "BTCOEXSCAN-STOP",
+ "BTCOEXMODE",
+ "SETSUSPENDOPT",
+ "P2P_DEV_ADDR",
+ "SETFWPATH",
+ "SETBAND",
+ "GETBAND",
+ "COUNTRY",
+ "P2P_SET_NOA",
+ "P2P_GET_NOA",
+ "P2P_SET_PS",
+ "SET_AP_WPS_P2P_IE",
+ "MACADDR",
+ "BLOCK",
+ "WFD-ENABLE",
+ "WFD-DISABLE",
+ "WFD-SET-TCPPORT",
+ "WFD-SET-MAXTPUT",
+ "WFD-SET-DEVTYPE",
+};
+
+struct android_wifi_priv_cmd {
+ const char __user *buf;
+ int used_len;
+ int total_len;
+};
+
+/**
+ * Local (static) functions and variables
+ */
+
+/* Initialize g_wifi_on to 1 so dhd_bus_start will be called for the first
+ * time (only) in dhd_open, subsequential wifi on will be handled by
+ * wl_android_wifi_on
+ */
+static int g_wifi_on = true;
+
+int rtw_android_cmdstr_to_num(char *cmdstr)
+{
+ int cmd_num;
+ for (cmd_num = 0; cmd_num < ANDROID_WIFI_CMD_MAX; cmd_num++)
+ if (0 == strncasecmp(cmdstr , android_wifi_cmd_str[cmd_num],
+ strlen(android_wifi_cmd_str[cmd_num])))
+ break;
+ return cmd_num;
+}
+
+static int rtw_android_get_rssi(struct net_device *net, char *command,
+ int total_len)
+{
+ struct adapter *padapter = (struct adapter *)rtw_netdev_priv(net);
+ struct mlme_priv *pmlmepriv = &(padapter->mlmepriv);
+ struct wlan_network *pcur_network = &pmlmepriv->cur_network;
+ int bytes_written = 0;
+
+ if (check_fwstate(pmlmepriv, _FW_LINKED)) {
+ bytes_written += snprintf(&command[bytes_written], total_len,
+ "%s rssi %d",
+ pcur_network->network.Ssid.Ssid,
+ padapter->recvpriv.rssi);
+ }
+ return bytes_written;
+}
+
+static int rtw_android_get_link_speed(struct net_device *net, char *command,
+ int total_len)
+{
+ struct adapter *padapter = (struct adapter *)rtw_netdev_priv(net);
+ u16 link_speed;
+
+ link_speed = rtw_get_cur_max_rate(padapter) / 10;
+ return snprintf(command, total_len, "LinkSpeed %d",
+ link_speed);
+}
+
+static int rtw_android_get_macaddr(struct net_device *net, char *command,
+ int total_len)
+{
+ return snprintf(command, total_len, "Macaddr = %pM",
+ net->dev_addr);
+}
+
+static int android_set_cntry(struct net_device *net, char *command,
+ int total_len)
+{
+ struct adapter *adapter = (struct adapter *)rtw_netdev_priv(net);
+ char *country_code = command + strlen(android_wifi_cmd_str[ANDROID_WIFI_CMD_COUNTRY]) + 1;
+ int ret;
+
+ ret = rtw_set_country(adapter, country_code);
+ return (ret == _SUCCESS) ? 0 : -1;
+}
+
+static int android_get_p2p_addr(struct net_device *net, char *command,
+ int total_len)
+{
+ /* We use the same address as our HW MAC address */
+ memcpy(command, net->dev_addr, ETH_ALEN);
+ return ETH_ALEN;
+}
+
+static int rtw_android_set_block(struct net_device *net, char *command,
+ int total_len)
+{
+ return 0;
+}
+
+int rtw_android_priv_cmd(struct net_device *net, struct ifreq *ifr, int cmd)
+{
+ int ret = 0;
+ char *command;
+ int cmd_num;
+ int bytes_written = 0;
+ struct android_wifi_priv_cmd priv_cmd;
+
+ if (!ifr->ifr_data)
+ return -EINVAL;
+ if (copy_from_user(&priv_cmd, ifr->ifr_data, sizeof(priv_cmd)))
+ return -EFAULT;
+ if (priv_cmd.total_len < 1)
+ return -EINVAL;
+ command = memdup_user(priv_cmd.buf, priv_cmd.total_len);
+ if (IS_ERR(command))
+ return PTR_ERR(command);
+ command[priv_cmd.total_len - 1] = 0;
+ DBG_88E("%s: Android private cmd \"%s\" on %s\n",
+ __func__, command, ifr->ifr_name);
+ cmd_num = rtw_android_cmdstr_to_num(command);
+ switch (cmd_num) {
+ case ANDROID_WIFI_CMD_START:
+ goto response;
+ case ANDROID_WIFI_CMD_SETFWPATH:
+ goto response;
+ }
+ if (!g_wifi_on) {
+ DBG_88E("%s: Ignore private cmd \"%s\" - iface %s is down\n",
+ __func__, command, ifr->ifr_name);
+ ret = 0;
+ goto free;
+ }
+ switch (cmd_num) {
+ case ANDROID_WIFI_CMD_STOP:
+ break;
+ case ANDROID_WIFI_CMD_SCAN_ACTIVE:
+ break;
+ case ANDROID_WIFI_CMD_SCAN_PASSIVE:
+ break;
+ case ANDROID_WIFI_CMD_RSSI:
+ bytes_written = rtw_android_get_rssi(net, command,
+ priv_cmd.total_len);
+ break;
+ case ANDROID_WIFI_CMD_LINKSPEED:
+ bytes_written = rtw_android_get_link_speed(net, command,
+ priv_cmd.total_len);
+ break;
+ case ANDROID_WIFI_CMD_MACADDR:
+ bytes_written = rtw_android_get_macaddr(net, command,
+ priv_cmd.total_len);
+ break;
+ case ANDROID_WIFI_CMD_BLOCK:
+ bytes_written = rtw_android_set_block(net, command,
+ priv_cmd.total_len);
+ break;
+ case ANDROID_WIFI_CMD_RXFILTER_START:
+ break;
+ case ANDROID_WIFI_CMD_RXFILTER_STOP:
+ break;
+ case ANDROID_WIFI_CMD_RXFILTER_ADD:
+ break;
+ case ANDROID_WIFI_CMD_RXFILTER_REMOVE:
+ break;
+ case ANDROID_WIFI_CMD_BTCOEXSCAN_START:
+ /* TBD: BTCOEXSCAN-START */
+ break;
+ case ANDROID_WIFI_CMD_BTCOEXSCAN_STOP:
+ /* TBD: BTCOEXSCAN-STOP */
+ break;
+ case ANDROID_WIFI_CMD_BTCOEXMODE:
+ break;
+ case ANDROID_WIFI_CMD_SETSUSPENDOPT:
+ break;
+ case ANDROID_WIFI_CMD_SETBAND:
+ break;
+ case ANDROID_WIFI_CMD_GETBAND:
+ break;
+ case ANDROID_WIFI_CMD_COUNTRY:
+ bytes_written = android_set_cntry(net, command,
+ priv_cmd.total_len);
+ break;
+ case ANDROID_WIFI_CMD_P2P_DEV_ADDR:
+ bytes_written = android_get_p2p_addr(net, command,
+ priv_cmd.total_len);
+ break;
+ case ANDROID_WIFI_CMD_P2P_SET_NOA:
+ break;
+ case ANDROID_WIFI_CMD_P2P_GET_NOA:
+ break;
+ case ANDROID_WIFI_CMD_P2P_SET_PS:
+ break;
+ default:
+ DBG_88E("Unknown PRIVATE command %s - ignored\n", command);
+ snprintf(command, 3, "OK");
+ bytes_written = strlen("OK");
+ }
+
+response:
+ if (bytes_written >= 0) {
+ if ((bytes_written == 0) && (priv_cmd.total_len > 0))
+ command[0] = '\0';
+ if (bytes_written >= priv_cmd.total_len) {
+ DBG_88E("%s: bytes_written = %d\n", __func__,
+ bytes_written);
+ bytes_written = priv_cmd.total_len;
+ } else {
+ bytes_written++;
+ }
+ priv_cmd.used_len = bytes_written;
+ if (copy_to_user((char __user *)priv_cmd.buf, command,
+ bytes_written)) {
+ DBG_88E("%s: failed to copy data to user buffer\n",
+ __func__);
+ ret = -EFAULT;
+ }
+ } else {
+ ret = bytes_written;
+ }
+free:
+ kfree(command);
+ return ret;
+}
diff --git a/drivers/staging/rtl8188eu/os_dep/usb_intf.c b/drivers/staging/rtl8188eu/os_dep/usb_intf.c
new file mode 100644
index 000000000..ef3c73e38
--- /dev/null
+++ b/drivers/staging/rtl8188eu/os_dep/usb_intf.c
@@ -0,0 +1,569 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2011 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ *
+ ******************************************************************************/
+#define _HCI_INTF_C_
+
+#include <osdep_service.h>
+#include <drv_types.h>
+#include <recv_osdep.h>
+#include <xmit_osdep.h>
+#include <hal_intf.h>
+#include <linux/usb.h>
+#include <linux/vmalloc.h>
+#include <osdep_intf.h>
+
+#include <usb_ops_linux.h>
+#include <usb_hal.h>
+#include <rtw_ioctl.h>
+
+int ui_pid[3] = {0, 0, 0};
+
+#define USB_VENDER_ID_REALTEK 0x0bda
+
+/* DID_USB_v916_20130116 */
+static struct usb_device_id rtw_usb_id_tbl[] = {
+ /*=== Realtek demoboard ===*/
+ {USB_DEVICE(USB_VENDER_ID_REALTEK, 0x8179)}, /* 8188EUS */
+ {USB_DEVICE(USB_VENDER_ID_REALTEK, 0x0179)}, /* 8188ETV */
+ /*=== Customer ID ===*/
+ /****** 8188EUS ********/
+ {USB_DEVICE(0x056e, 0x4008)}, /* Elecom WDC-150SU2M */
+ {USB_DEVICE(0x07b8, 0x8179)}, /* Abocom - Abocom */
+ {USB_DEVICE(0x2001, 0x330F)}, /* DLink DWA-125 REV D1 */
+ {USB_DEVICE(0x2001, 0x3310)}, /* Dlink DWA-123 REV D1 */
+ {USB_DEVICE(0x2001, 0x3311)}, /* DLink GO-USB-N150 REV B1 */
+ {USB_DEVICE(0x0df6, 0x0076)}, /* Sitecom N150 v2 */
+ {} /* Terminating entry */
+};
+
+MODULE_DEVICE_TABLE(usb, rtw_usb_id_tbl);
+
+static struct dvobj_priv *usb_dvobj_init(struct usb_interface *usb_intf)
+{
+ int i;
+ int status = _FAIL;
+ struct dvobj_priv *pdvobjpriv;
+ struct usb_host_config *phost_conf;
+ struct usb_config_descriptor *pconf_desc;
+ struct usb_host_interface *phost_iface;
+ struct usb_interface_descriptor *piface_desc;
+ struct usb_endpoint_descriptor *pendp_desc;
+ struct usb_device *pusbd;
+
+
+ pdvobjpriv = kzalloc(sizeof(*pdvobjpriv), GFP_KERNEL);
+ if (pdvobjpriv == NULL)
+ goto exit;
+
+ pdvobjpriv->pusbintf = usb_intf;
+ pusbd = interface_to_usbdev(usb_intf);
+ pdvobjpriv->pusbdev = pusbd;
+ usb_set_intfdata(usb_intf, pdvobjpriv);
+
+ pdvobjpriv->RtNumInPipes = 0;
+ pdvobjpriv->RtNumOutPipes = 0;
+
+ phost_conf = pusbd->actconfig;
+ pconf_desc = &phost_conf->desc;
+
+ phost_iface = &usb_intf->altsetting[0];
+ piface_desc = &phost_iface->desc;
+
+ pdvobjpriv->NumInterfaces = pconf_desc->bNumInterfaces;
+ pdvobjpriv->InterfaceNumber = piface_desc->bInterfaceNumber;
+ pdvobjpriv->nr_endpoint = piface_desc->bNumEndpoints;
+
+ for (i = 0; i < pdvobjpriv->nr_endpoint; i++) {
+ int ep_num;
+ pendp_desc = &phost_iface->endpoint[i].desc;
+
+ ep_num = usb_endpoint_num(pendp_desc);
+
+ if (usb_endpoint_is_bulk_in(pendp_desc)) {
+ pdvobjpriv->RtInPipe[pdvobjpriv->RtNumInPipes] = ep_num;
+ pdvobjpriv->RtNumInPipes++;
+ } else if (usb_endpoint_is_int_in(pendp_desc)) {
+ pdvobjpriv->RtInPipe[pdvobjpriv->RtNumInPipes] = ep_num;
+ pdvobjpriv->RtNumInPipes++;
+ } else if (usb_endpoint_is_bulk_out(pendp_desc)) {
+ pdvobjpriv->RtOutPipe[pdvobjpriv->RtNumOutPipes] =
+ ep_num;
+ pdvobjpriv->RtNumOutPipes++;
+ }
+ pdvobjpriv->ep_num[i] = ep_num;
+ }
+
+ if (pusbd->speed == USB_SPEED_HIGH)
+ pdvobjpriv->ishighspeed = true;
+ else
+ pdvobjpriv->ishighspeed = false;
+
+ mutex_init(&pdvobjpriv->usb_vendor_req_mutex);
+ pdvobjpriv->usb_vendor_req_buf = kzalloc(MAX_USB_IO_CTL_SIZE, GFP_KERNEL);
+
+ if (!pdvobjpriv->usb_vendor_req_buf)
+ goto free_dvobj;
+
+ usb_get_dev(pusbd);
+
+ status = _SUCCESS;
+
+free_dvobj:
+ if (status != _SUCCESS && pdvobjpriv) {
+ usb_set_intfdata(usb_intf, NULL);
+ kfree(pdvobjpriv);
+ pdvobjpriv = NULL;
+ }
+exit:
+ return pdvobjpriv;
+}
+
+static void usb_dvobj_deinit(struct usb_interface *usb_intf)
+{
+ struct dvobj_priv *dvobj = usb_get_intfdata(usb_intf);
+
+
+ usb_set_intfdata(usb_intf, NULL);
+ if (dvobj) {
+ /* Modify condition for 92DU DMDP 2010.11.18, by Thomas */
+ if ((dvobj->NumInterfaces != 2 &&
+ dvobj->NumInterfaces != 3) ||
+ (dvobj->InterfaceNumber == 1)) {
+ if (interface_to_usbdev(usb_intf)->state !=
+ USB_STATE_NOTATTACHED) {
+ /* If we didn't unplug usb dongle and
+ * remove/insert module, driver fails
+ * on sitesurvey for the first time when
+ * device is up . Reset usb port for sitesurvey
+ * fail issue. */
+ DBG_88E("usb attached..., try to reset usb device\n");
+ usb_reset_device(interface_to_usbdev(usb_intf));
+ }
+ }
+
+ kfree(dvobj->usb_vendor_req_buf);
+ mutex_destroy(&dvobj->usb_vendor_req_mutex);
+ kfree(dvobj);
+ }
+
+ usb_put_dev(interface_to_usbdev(usb_intf));
+
+}
+
+static void usb_intf_start(struct adapter *padapter)
+{
+ RT_TRACE(_module_hci_intfs_c_, _drv_err_, ("+usb_intf_start\n"));
+
+ rtw_hal_inirp_init(padapter);
+
+ RT_TRACE(_module_hci_intfs_c_, _drv_err_, ("-usb_intf_start\n"));
+}
+
+static void usb_intf_stop(struct adapter *padapter)
+{
+ RT_TRACE(_module_hci_intfs_c_, _drv_err_, ("+usb_intf_stop\n"));
+
+ /* disable_hw_interrupt */
+ if (!padapter->bSurpriseRemoved) {
+ /* device still exists, so driver can do i/o operation */
+ /* TODO: */
+ RT_TRACE(_module_hci_intfs_c_, _drv_err_,
+ ("SurpriseRemoved == false\n"));
+ }
+
+ /* cancel in irp */
+ rtw_hal_inirp_deinit(padapter);
+
+ /* cancel out irp */
+ usb_write_port_cancel(padapter);
+
+ /* todo:cancel other irps */
+
+ RT_TRACE(_module_hci_intfs_c_, _drv_err_, ("-usb_intf_stop\n"));
+}
+
+static void rtw_dev_unload(struct adapter *padapter)
+{
+ RT_TRACE(_module_hci_intfs_c_, _drv_err_, ("+rtw_dev_unload\n"));
+
+ if (padapter->bup) {
+ DBG_88E("===> rtw_dev_unload\n");
+ padapter->bDriverStopped = true;
+ if (padapter->xmitpriv.ack_tx)
+ rtw_ack_tx_done(&padapter->xmitpriv, RTW_SCTX_DONE_DRV_STOP);
+ /* s3. */
+ if (padapter->intf_stop)
+ padapter->intf_stop(padapter);
+ /* s4. */
+ if (!padapter->pwrctrlpriv.bInternalAutoSuspend)
+ rtw_stop_drv_threads(padapter);
+
+ /* s5. */
+ if (!padapter->bSurpriseRemoved) {
+ rtw_hal_deinit(padapter);
+ padapter->bSurpriseRemoved = true;
+ }
+
+ padapter->bup = false;
+ } else {
+ RT_TRACE(_module_hci_intfs_c_, _drv_err_,
+ ("r871x_dev_unload():padapter->bup == false\n"));
+ }
+
+ DBG_88E("<=== rtw_dev_unload\n");
+
+ RT_TRACE(_module_hci_intfs_c_, _drv_err_, ("-rtw_dev_unload\n"));
+}
+
+static int rtw_suspend(struct usb_interface *pusb_intf, pm_message_t message)
+{
+ struct dvobj_priv *dvobj = usb_get_intfdata(pusb_intf);
+ struct adapter *padapter = dvobj->if1;
+ struct net_device *pnetdev = padapter->pnetdev;
+ struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
+ struct pwrctrl_priv *pwrpriv = &padapter->pwrctrlpriv;
+
+ int ret = 0;
+ u32 start_time = jiffies;
+
+
+ DBG_88E("==> %s (%s:%d)\n", __func__, current->comm, current->pid);
+
+ if ((!padapter->bup) || (padapter->bDriverStopped) ||
+ (padapter->bSurpriseRemoved)) {
+ DBG_88E("padapter->bup=%d bDriverStopped=%d bSurpriseRemoved = %d\n",
+ padapter->bup, padapter->bDriverStopped,
+ padapter->bSurpriseRemoved);
+ goto exit;
+ }
+
+ pwrpriv->bInSuspend = true;
+ rtw_cancel_all_timer(padapter);
+ LeaveAllPowerSaveMode(padapter);
+
+ _enter_pwrlock(&pwrpriv->lock);
+ /* s1. */
+ if (pnetdev) {
+ netif_carrier_off(pnetdev);
+ netif_tx_stop_all_queues(pnetdev);
+ }
+
+ /* s2. */
+ rtw_disassoc_cmd(padapter, 0, false);
+
+ if (check_fwstate(pmlmepriv, WIFI_STATION_STATE) &&
+ check_fwstate(pmlmepriv, _FW_LINKED)) {
+ DBG_88E("%s:%d %s(%pM), length:%d assoc_ssid.length:%d\n",
+ __func__, __LINE__,
+ pmlmepriv->cur_network.network.Ssid.Ssid,
+ pmlmepriv->cur_network.network.MacAddress,
+ pmlmepriv->cur_network.network.Ssid.SsidLength,
+ pmlmepriv->assoc_ssid.SsidLength);
+
+ pmlmepriv->to_roaming = 1;
+ }
+ /* s2-2. indicate disconnect to os */
+ rtw_indicate_disconnect(padapter);
+ /* s2-3. */
+ rtw_free_assoc_resources(padapter, 1);
+ /* s2-4. */
+ rtw_free_network_queue(padapter, true);
+
+ rtw_dev_unload(padapter);
+ _exit_pwrlock(&pwrpriv->lock);
+
+ if (check_fwstate(pmlmepriv, _FW_UNDER_SURVEY))
+ rtw_indicate_scan_done(padapter, 1);
+
+ if (check_fwstate(pmlmepriv, _FW_UNDER_LINKING))
+ rtw_indicate_disconnect(padapter);
+
+exit:
+ DBG_88E("<=== %s return %d.............. in %dms\n", __func__
+ , ret, rtw_get_passing_time_ms(start_time));
+
+ return ret;
+}
+
+static int rtw_resume_process(struct adapter *padapter)
+{
+ struct net_device *pnetdev;
+ struct pwrctrl_priv *pwrpriv = NULL;
+ int ret = -1;
+ u32 start_time = jiffies;
+
+ DBG_88E("==> %s (%s:%d)\n", __func__, current->comm, current->pid);
+
+ if (padapter) {
+ pnetdev = padapter->pnetdev;
+ pwrpriv = &padapter->pwrctrlpriv;
+ } else {
+ goto exit;
+ }
+
+ _enter_pwrlock(&pwrpriv->lock);
+ rtw_reset_drv_sw(padapter);
+ pwrpriv->bkeepfwalive = false;
+
+ DBG_88E("bkeepfwalive(%x)\n", pwrpriv->bkeepfwalive);
+ if (pm_netdev_open(pnetdev, true) != 0)
+ goto exit;
+
+ netif_device_attach(pnetdev);
+ netif_carrier_on(pnetdev);
+
+ _exit_pwrlock(&pwrpriv->lock);
+
+ if (padapter->pid[1] != 0) {
+ DBG_88E("pid[1]:%d\n", padapter->pid[1]);
+ rtw_signal_process(padapter->pid[1], SIGUSR2);
+ }
+
+ rtw_roaming(padapter, NULL);
+
+ ret = 0;
+exit:
+ if (pwrpriv)
+ pwrpriv->bInSuspend = false;
+ DBG_88E("<=== %s return %d.............. in %dms\n", __func__,
+ ret, rtw_get_passing_time_ms(start_time));
+
+
+ return ret;
+}
+
+static int rtw_resume(struct usb_interface *pusb_intf)
+{
+ struct dvobj_priv *dvobj = usb_get_intfdata(pusb_intf);
+ struct adapter *padapter = dvobj->if1;
+
+ return rtw_resume_process(padapter);
+}
+
+/*
+ * drv_init() - a device potentially for us
+ *
+ * notes: drv_init() is called when the bus driver has located
+ * a card for us to support.
+ * We accept the new device by returning 0.
+ */
+
+static struct adapter *rtw_usb_if1_init(struct dvobj_priv *dvobj,
+ struct usb_interface *pusb_intf, const struct usb_device_id *pdid)
+{
+ struct adapter *padapter = NULL;
+ struct net_device *pnetdev = NULL;
+ int status = _FAIL;
+
+ padapter = (struct adapter *)vzalloc(sizeof(*padapter));
+ if (padapter == NULL)
+ goto exit;
+ padapter->dvobj = dvobj;
+ dvobj->if1 = padapter;
+
+ padapter->bDriverStopped = true;
+ mutex_init(&padapter->hw_init_mutex);
+ padapter->chip_type = RTL8188E;
+
+ pnetdev = rtw_init_netdev(padapter);
+ if (pnetdev == NULL)
+ goto free_adapter;
+ SET_NETDEV_DEV(pnetdev, dvobj_to_dev(dvobj));
+ padapter = rtw_netdev_priv(pnetdev);
+
+ /* step 2. hook HalFunc, allocate HalData */
+ hal_set_hal_ops(padapter);
+
+ padapter->intf_start = &usb_intf_start;
+ padapter->intf_stop = &usb_intf_stop;
+
+ /* step read_chip_version */
+ rtw_hal_read_chip_version(padapter);
+
+ /* step usb endpoint mapping */
+ rtw_hal_chip_configure(padapter);
+
+ /* step read efuse/eeprom data and get mac_addr */
+ rtw_hal_read_chip_info(padapter);
+
+ /* step 5. */
+ if (rtw_init_drv_sw(padapter) == _FAIL) {
+ RT_TRACE(_module_hci_intfs_c_, _drv_err_,
+ ("Initialize driver software resource Failed!\n"));
+ goto free_hal_data;
+ }
+
+#ifdef CONFIG_PM
+ if (padapter->pwrctrlpriv.bSupportRemoteWakeup) {
+ dvobj->pusbdev->do_remote_wakeup = 1;
+ pusb_intf->needs_remote_wakeup = 1;
+ device_init_wakeup(&pusb_intf->dev, 1);
+ DBG_88E("\n padapter->pwrctrlpriv.bSupportRemoteWakeup~~~~~~\n");
+ DBG_88E("\n padapter->pwrctrlpriv.bSupportRemoteWakeup~~~[%d]~~~\n",
+ device_may_wakeup(&pusb_intf->dev));
+ }
+#endif
+
+ /* 2012-07-11 Move here to prevent the 8723AS-VAU BT auto
+ * suspend influence */
+ if (usb_autopm_get_interface(pusb_intf) < 0)
+ DBG_88E("can't get autopm:\n");
+
+ /* alloc dev name after read efuse. */
+ rtw_init_netdev_name(pnetdev, padapter->registrypriv.ifname);
+ rtw_macaddr_cfg(padapter->eeprompriv.mac_addr);
+ memcpy(pnetdev->dev_addr, padapter->eeprompriv.mac_addr, ETH_ALEN);
+ DBG_88E("MAC Address from pnetdev->dev_addr = %pM\n",
+ pnetdev->dev_addr);
+
+ /* step 6. Tell the network stack we exist */
+ if (register_netdev(pnetdev) != 0) {
+ RT_TRACE(_module_hci_intfs_c_, _drv_err_, ("register_netdev() failed\n"));
+ goto free_hal_data;
+ }
+
+ DBG_88E("bDriverStopped:%d, bSurpriseRemoved:%d, bup:%d, hw_init_completed:%d\n"
+ , padapter->bDriverStopped
+ , padapter->bSurpriseRemoved
+ , padapter->bup
+ , padapter->hw_init_completed
+ );
+
+ status = _SUCCESS;
+
+free_hal_data:
+ if (status != _SUCCESS)
+ kfree(padapter->HalData);
+free_adapter:
+ if (status != _SUCCESS) {
+ if (pnetdev)
+ rtw_free_netdev(pnetdev);
+ else if (padapter)
+ vfree(padapter);
+ padapter = NULL;
+ }
+exit:
+ return padapter;
+}
+
+static void rtw_usb_if1_deinit(struct adapter *if1)
+{
+ struct net_device *pnetdev = if1->pnetdev;
+ struct mlme_priv *pmlmepriv = &if1->mlmepriv;
+
+ if (check_fwstate(pmlmepriv, _FW_LINKED))
+ rtw_disassoc_cmd(if1, 0, false);
+
+#ifdef CONFIG_88EU_AP_MODE
+ free_mlme_ap_info(if1);
+#endif
+
+ if (pnetdev) {
+ /* will call netdev_close() */
+ unregister_netdev(pnetdev);
+ rtw_proc_remove_one(pnetdev);
+ }
+ rtw_cancel_all_timer(if1);
+
+ rtw_dev_unload(if1);
+ DBG_88E("+r871xu_dev_remove, hw_init_completed=%d\n",
+ if1->hw_init_completed);
+ rtw_free_drv_sw(if1);
+ if (pnetdev)
+ rtw_free_netdev(pnetdev);
+}
+
+static int rtw_drv_init(struct usb_interface *pusb_intf, const struct usb_device_id *pdid)
+{
+ struct adapter *if1 = NULL;
+ int status = _FAIL;
+ struct dvobj_priv *dvobj;
+
+ RT_TRACE(_module_hci_intfs_c_, _drv_err_, ("+rtw_drv_init\n"));
+
+ /* Initialize dvobj_priv */
+ dvobj = usb_dvobj_init(pusb_intf);
+ if (dvobj == NULL) {
+ RT_TRACE(_module_hci_intfs_c_, _drv_err_,
+ ("initialize device object priv Failed!\n"));
+ goto exit;
+ }
+
+ if1 = rtw_usb_if1_init(dvobj, pusb_intf, pdid);
+ if (if1 == NULL) {
+ DBG_88E("rtw_init_primarystruct adapter Failed!\n");
+ goto free_dvobj;
+ }
+
+ if (ui_pid[1] != 0) {
+ DBG_88E("ui_pid[1]:%d\n", ui_pid[1]);
+ rtw_signal_process(ui_pid[1], SIGUSR2);
+ }
+
+ RT_TRACE(_module_hci_intfs_c_, _drv_err_, ("-871x_drv - drv_init, success!\n"));
+
+ status = _SUCCESS;
+
+free_dvobj:
+ if (status != _SUCCESS)
+ usb_dvobj_deinit(pusb_intf);
+exit:
+ return status == _SUCCESS ? 0 : -ENODEV;
+}
+
+/*
+ * dev_remove() - our device is being removed
+*/
+/* rmmod module & unplug(SurpriseRemoved) will call r871xu_dev_remove() => how to recognize both */
+static void rtw_dev_remove(struct usb_interface *pusb_intf)
+{
+ struct dvobj_priv *dvobj = usb_get_intfdata(pusb_intf);
+ struct adapter *padapter = dvobj->if1;
+
+
+ DBG_88E("+rtw_dev_remove\n");
+ RT_TRACE(_module_hci_intfs_c_, _drv_err_, ("+dev_remove()\n"));
+
+ if (!pusb_intf->unregistering)
+ padapter->bSurpriseRemoved = true;
+
+ rtw_pm_set_ips(padapter, IPS_NONE);
+ rtw_pm_set_lps(padapter, PS_MODE_ACTIVE);
+
+ LeaveAllPowerSaveMode(padapter);
+
+ rtw_usb_if1_deinit(padapter);
+
+ usb_dvobj_deinit(pusb_intf);
+
+ RT_TRACE(_module_hci_intfs_c_, _drv_err_, ("-dev_remove()\n"));
+ DBG_88E("-r871xu_dev_remove, done\n");
+}
+
+static struct usb_driver rtl8188e_usb_drv = {
+ .name = "r8188eu",
+ .probe = rtw_drv_init,
+ .disconnect = rtw_dev_remove,
+ .id_table = rtw_usb_id_tbl,
+ .suspend = rtw_suspend,
+ .resume = rtw_resume,
+ .reset_resume = rtw_resume,
+};
+
+module_usb_driver(rtl8188e_usb_drv)
diff --git a/drivers/staging/rtl8188eu/os_dep/usb_ops_linux.c b/drivers/staging/rtl8188eu/os_dep/usb_ops_linux.c
new file mode 100644
index 000000000..7e599bc5b
--- /dev/null
+++ b/drivers/staging/rtl8188eu/os_dep/usb_ops_linux.c
@@ -0,0 +1,855 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2012 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ ******************************************************************************/
+#define _USB_OPS_LINUX_C_
+
+#include <drv_types.h>
+#include <recv_osdep.h>
+#include <rtw_sreset.h>
+
+static void interrupt_handler_8188eu(struct adapter *adapt, u16 pkt_len, u8 *pbuf)
+{
+ struct hal_data_8188e *haldata = GET_HAL_DATA(adapt);
+
+ if (pkt_len != INTERRUPT_MSG_FORMAT_LEN) {
+ DBG_88E("%s Invalid interrupt content length (%d)!\n", __func__, pkt_len);
+ return;
+ }
+
+ /* HISR */
+ memcpy(&(haldata->IntArray[0]), &(pbuf[USB_INTR_CONTENT_HISR_OFFSET]), 4);
+ memcpy(&(haldata->IntArray[1]), &(pbuf[USB_INTR_CONTENT_HISRE_OFFSET]), 4);
+
+ /* C2H Event */
+ if (pbuf[0] != 0)
+ memcpy(&(haldata->C2hArray[0]), &(pbuf[USB_INTR_CONTENT_C2H_OFFSET]), 16);
+}
+
+static int recvbuf2recvframe(struct adapter *adapt, struct sk_buff *pskb)
+{
+ u8 *pbuf;
+ u8 shift_sz = 0;
+ u16 pkt_cnt;
+ u32 pkt_offset, skb_len, alloc_sz;
+ s32 transfer_len;
+ struct recv_stat *prxstat;
+ struct phy_stat *pphy_status = NULL;
+ struct sk_buff *pkt_copy = NULL;
+ struct recv_frame *precvframe = NULL;
+ struct rx_pkt_attrib *pattrib = NULL;
+ struct hal_data_8188e *haldata = GET_HAL_DATA(adapt);
+ struct recv_priv *precvpriv = &adapt->recvpriv;
+ struct __queue *pfree_recv_queue = &precvpriv->free_recv_queue;
+
+ transfer_len = (s32)pskb->len;
+ pbuf = pskb->data;
+
+ prxstat = (struct recv_stat *)pbuf;
+ pkt_cnt = (le32_to_cpu(prxstat->rxdw2) >> 16) & 0xff;
+
+ do {
+ RT_TRACE(_module_rtl871x_recv_c_, _drv_info_,
+ ("recvbuf2recvframe: rxdesc=offsset 0:0x%08x, 4:0x%08x, 8:0x%08x, C:0x%08x\n",
+ prxstat->rxdw0, prxstat->rxdw1, prxstat->rxdw2, prxstat->rxdw4));
+
+ prxstat = (struct recv_stat *)pbuf;
+
+ precvframe = rtw_alloc_recvframe(pfree_recv_queue);
+ if (precvframe == NULL) {
+ RT_TRACE(_module_rtl871x_recv_c_, _drv_err_, ("recvbuf2recvframe: precvframe==NULL\n"));
+ DBG_88E("%s()-%d: rtw_alloc_recvframe() failed! RX Drop!\n", __func__, __LINE__);
+ goto _exit_recvbuf2recvframe;
+ }
+
+ INIT_LIST_HEAD(&precvframe->list);
+ precvframe->len = 0;
+
+ update_recvframe_attrib_88e(precvframe, prxstat);
+
+ pattrib = &precvframe->attrib;
+
+ if ((pattrib->crc_err) || (pattrib->icv_err)) {
+ DBG_88E("%s: RX Warning! crc_err=%d icv_err=%d, skip!\n", __func__, pattrib->crc_err, pattrib->icv_err);
+
+ rtw_free_recvframe(precvframe, pfree_recv_queue);
+ goto _exit_recvbuf2recvframe;
+ }
+
+ if ((pattrib->physt) && (pattrib->pkt_rpt_type == NORMAL_RX))
+ pphy_status = (struct phy_stat *)(pbuf + RXDESC_OFFSET);
+
+ pkt_offset = RXDESC_SIZE + pattrib->drvinfo_sz + pattrib->shift_sz + pattrib->pkt_len;
+
+ if ((pattrib->pkt_len <= 0) || (pkt_offset > transfer_len)) {
+ RT_TRACE(_module_rtl871x_recv_c_, _drv_info_, ("recvbuf2recvframe: pkt_len<=0\n"));
+ DBG_88E("%s()-%d: RX Warning!,pkt_len<=0 or pkt_offset> transfoer_len\n", __func__, __LINE__);
+ rtw_free_recvframe(precvframe, pfree_recv_queue);
+ goto _exit_recvbuf2recvframe;
+ }
+
+ /* Modified by Albert 20101213 */
+ /* For 8 bytes IP header alignment. */
+ if (pattrib->qos) /* Qos data, wireless lan header length is 26 */
+ shift_sz = 6;
+ else
+ shift_sz = 0;
+
+ skb_len = pattrib->pkt_len;
+
+ /* for first fragment packet, driver need allocate 1536+drvinfo_sz+RXDESC_SIZE to defrag packet. */
+ /* modify alloc_sz for recvive crc error packet by thomas 2011-06-02 */
+ if ((pattrib->mfrag == 1) && (pattrib->frag_num == 0)) {
+ if (skb_len <= 1650)
+ alloc_sz = 1664;
+ else
+ alloc_sz = skb_len + 14;
+ } else {
+ alloc_sz = skb_len;
+ /* 6 is for IP header 8 bytes alignment in QoS packet case. */
+ /* 8 is for skb->data 4 bytes alignment. */
+ alloc_sz += 14;
+ }
+
+ pkt_copy = netdev_alloc_skb(adapt->pnetdev, alloc_sz);
+ if (pkt_copy) {
+ pkt_copy->dev = adapt->pnetdev;
+ precvframe->pkt = pkt_copy;
+ precvframe->rx_head = pkt_copy->data;
+ precvframe->rx_end = pkt_copy->data + alloc_sz;
+ skb_reserve(pkt_copy, 8 - ((size_t)(pkt_copy->data) & 7));/* force pkt_copy->data at 8-byte alignment address */
+ skb_reserve(pkt_copy, shift_sz);/* force ip_hdr at 8-byte alignment address according to shift_sz. */
+ memcpy(pkt_copy->data, (pbuf + pattrib->drvinfo_sz + RXDESC_SIZE), skb_len);
+ precvframe->rx_tail = pkt_copy->data;
+ precvframe->rx_data = pkt_copy->data;
+ } else {
+ if ((pattrib->mfrag == 1) && (pattrib->frag_num == 0)) {
+ DBG_88E("recvbuf2recvframe: alloc_skb fail , drop frag frame\n");
+ rtw_free_recvframe(precvframe, pfree_recv_queue);
+ goto _exit_recvbuf2recvframe;
+ }
+ precvframe->pkt = skb_clone(pskb, GFP_ATOMIC);
+ if (precvframe->pkt) {
+ precvframe->rx_tail = pbuf + pattrib->drvinfo_sz + RXDESC_SIZE;
+ precvframe->rx_head = precvframe->rx_tail;
+ precvframe->rx_data = precvframe->rx_tail;
+ precvframe->rx_end = pbuf + pattrib->drvinfo_sz + RXDESC_SIZE + alloc_sz;
+ } else {
+ DBG_88E("recvbuf2recvframe: skb_clone fail\n");
+ rtw_free_recvframe(precvframe, pfree_recv_queue);
+ goto _exit_recvbuf2recvframe;
+ }
+ }
+
+ recvframe_put(precvframe, skb_len);
+
+ switch (haldata->UsbRxAggMode) {
+ case USB_RX_AGG_DMA:
+ case USB_RX_AGG_MIX:
+ pkt_offset = (u16)round_up(pkt_offset, 128);
+ break;
+ case USB_RX_AGG_USB:
+ pkt_offset = (u16)round_up(pkt_offset, 4);
+ break;
+ case USB_RX_AGG_DISABLE:
+ default:
+ break;
+ }
+ if (pattrib->pkt_rpt_type == NORMAL_RX) { /* Normal rx packet */
+ if (pattrib->physt)
+ update_recvframe_phyinfo_88e(precvframe, (struct phy_stat *)pphy_status);
+ if (rtw_recv_entry(precvframe) != _SUCCESS) {
+ RT_TRACE(_module_rtl871x_recv_c_, _drv_err_,
+ ("recvbuf2recvframe: rtw_recv_entry(precvframe) != _SUCCESS\n"));
+ }
+ } else {
+ /* enqueue recvframe to txrtp queue */
+ if (pattrib->pkt_rpt_type == TX_REPORT1) {
+ /* CCX-TXRPT ack for xmit mgmt frames. */
+ handle_txrpt_ccx_88e(adapt, precvframe->rx_data);
+ } else if (pattrib->pkt_rpt_type == TX_REPORT2) {
+ ODM_RA_TxRPT2Handle_8188E(
+ &haldata->odmpriv,
+ precvframe->rx_data,
+ pattrib->pkt_len,
+ pattrib->MacIDValidEntry[0],
+ pattrib->MacIDValidEntry[1]
+ );
+ } else if (pattrib->pkt_rpt_type == HIS_REPORT) {
+ interrupt_handler_8188eu(adapt, pattrib->pkt_len, precvframe->rx_data);
+ }
+ rtw_free_recvframe(precvframe, pfree_recv_queue);
+ }
+ pkt_cnt--;
+ transfer_len -= pkt_offset;
+ pbuf += pkt_offset;
+ precvframe = NULL;
+ pkt_copy = NULL;
+
+ if (transfer_len > 0 && pkt_cnt == 0)
+ pkt_cnt = (le32_to_cpu(prxstat->rxdw2)>>16) & 0xff;
+
+ } while ((transfer_len > 0) && (pkt_cnt > 0));
+
+_exit_recvbuf2recvframe:
+
+ return _SUCCESS;
+}
+
+unsigned int ffaddr2pipehdl(struct dvobj_priv *pdvobj, u32 addr)
+{
+ unsigned int pipe = 0, ep_num = 0;
+ struct usb_device *pusbd = pdvobj->pusbdev;
+
+ if (addr == RECV_BULK_IN_ADDR) {
+ pipe = usb_rcvbulkpipe(pusbd, pdvobj->RtInPipe[0]);
+ } else if (addr == RECV_INT_IN_ADDR) {
+ pipe = usb_rcvbulkpipe(pusbd, pdvobj->RtInPipe[1]);
+ } else if (addr < HW_QUEUE_ENTRY) {
+ ep_num = pdvobj->Queue2Pipe[addr];
+ pipe = usb_sndbulkpipe(pusbd, ep_num);
+ }
+
+ return pipe;
+}
+
+static int usbctrl_vendorreq(struct adapter *adapt, u8 request, u16 value, u16 index, void *pdata, u16 len, u8 requesttype)
+{
+ struct dvobj_priv *dvobjpriv = adapter_to_dvobj(adapt);
+ struct usb_device *udev = dvobjpriv->pusbdev;
+ unsigned int pipe;
+ int status = 0;
+ u8 reqtype;
+ u8 *pIo_buf;
+ int vendorreq_times = 0;
+
+ if ((adapt->bSurpriseRemoved) || (adapt->pwrctrlpriv.pnp_bstop_trx)) {
+ RT_TRACE(_module_hci_ops_os_c_, _drv_err_, ("usbctrl_vendorreq:(adapt->bSurpriseRemoved ||adapter->pwrctrlpriv.pnp_bstop_trx)!!!\n"));
+ status = -EPERM;
+ goto exit;
+ }
+
+ if (len > MAX_VENDOR_REQ_CMD_SIZE) {
+ DBG_88E("[%s] Buffer len error ,vendor request failed\n", __func__);
+ status = -EINVAL;
+ goto exit;
+ }
+
+ _enter_critical_mutex(&dvobjpriv->usb_vendor_req_mutex, NULL);
+
+ /* Acquire IO memory for vendorreq */
+ pIo_buf = dvobjpriv->usb_vendor_req_buf;
+
+ if (pIo_buf == NULL) {
+ DBG_88E("[%s] pIo_buf == NULL\n", __func__);
+ status = -ENOMEM;
+ goto release_mutex;
+ }
+
+ while (++vendorreq_times <= MAX_USBCTRL_VENDORREQ_TIMES) {
+ memset(pIo_buf, 0, len);
+
+ if (requesttype == 0x01) {
+ pipe = usb_rcvctrlpipe(udev, 0);/* read_in */
+ reqtype = REALTEK_USB_VENQT_READ;
+ } else {
+ pipe = usb_sndctrlpipe(udev, 0);/* write_out */
+ reqtype = REALTEK_USB_VENQT_WRITE;
+ memcpy(pIo_buf, pdata, len);
+ }
+
+ status = usb_control_msg(udev, pipe, request, reqtype, value, index, pIo_buf, len, RTW_USB_CONTROL_MSG_TIMEOUT);
+
+ if (status == len) { /* Success this control transfer. */
+ if (requesttype == 0x01)
+ memcpy(pdata, pIo_buf, len);
+ } else { /* error cases */
+ DBG_88E("reg 0x%x, usb %s %u fail, status:%d value=0x%x, vendorreq_times:%d\n",
+ value, (requesttype == 0x01) ? "read" : "write",
+ len, status, *(u32 *)pdata, vendorreq_times);
+
+ if (status < 0) {
+ if (status == (-ESHUTDOWN) || status == -ENODEV) {
+ adapt->bSurpriseRemoved = true;
+ } else {
+ struct hal_data_8188e *haldata = GET_HAL_DATA(adapt);
+ haldata->srestpriv.Wifi_Error_Status = USB_VEN_REQ_CMD_FAIL;
+ }
+ } else { /* status != len && status >= 0 */
+ if (status > 0) {
+ if (requesttype == 0x01) {
+ /* For Control read transfer, we have to copy the read data from pIo_buf to pdata. */
+ memcpy(pdata, pIo_buf, len);
+ }
+ }
+ }
+
+ }
+
+ /* firmware download is checksumed, don't retry */
+ if ((value >= FW_8188E_START_ADDRESS && value <= FW_8188E_END_ADDRESS) || status == len)
+ break;
+ }
+release_mutex:
+ mutex_unlock(&dvobjpriv->usb_vendor_req_mutex);
+exit:
+ return status;
+}
+
+u8 usb_read8(struct adapter *adapter, u32 addr)
+{
+ u8 request;
+ u8 requesttype;
+ u16 wvalue;
+ u16 index;
+ u16 len;
+ u8 data = 0;
+
+
+ request = 0x05;
+ requesttype = 0x01;/* read_in */
+ index = 0;/* n/a */
+
+ wvalue = (u16)(addr&0x0000ffff);
+ len = 1;
+
+ usbctrl_vendorreq(adapter, request, wvalue, index, &data, len, requesttype);
+
+
+ return data;
+
+}
+
+u16 usb_read16(struct adapter *adapter, u32 addr)
+{
+ u8 request;
+ u8 requesttype;
+ u16 wvalue;
+ u16 index;
+ u16 len;
+ __le32 data;
+
+ request = 0x05;
+ requesttype = 0x01;/* read_in */
+ index = 0;/* n/a */
+ wvalue = (u16)(addr&0x0000ffff);
+ len = 2;
+ usbctrl_vendorreq(adapter, request, wvalue, index, &data, len, requesttype);
+
+ return (u16)(le32_to_cpu(data)&0xffff);
+}
+
+u32 usb_read32(struct adapter *adapter, u32 addr)
+{
+ u8 request;
+ u8 requesttype;
+ u16 wvalue;
+ u16 index;
+ u16 len;
+ __le32 data;
+
+
+ request = 0x05;
+ requesttype = 0x01;/* read_in */
+ index = 0;/* n/a */
+
+ wvalue = (u16)(addr&0x0000ffff);
+ len = 4;
+
+ usbctrl_vendorreq(adapter, request, wvalue, index, &data, len, requesttype);
+
+
+ return le32_to_cpu(data);
+}
+
+static void usb_read_port_complete(struct urb *purb, struct pt_regs *regs)
+{
+ struct recv_buf *precvbuf = (struct recv_buf *)purb->context;
+ struct adapter *adapt = (struct adapter *)precvbuf->adapter;
+ struct recv_priv *precvpriv = &adapt->recvpriv;
+
+ RT_TRACE(_module_hci_ops_os_c_, _drv_err_, ("usb_read_port_complete!!!\n"));
+
+ precvpriv->rx_pending_cnt--;
+
+ if (adapt->bSurpriseRemoved || adapt->bDriverStopped || adapt->bReadPortCancel) {
+ RT_TRACE(_module_hci_ops_os_c_, _drv_err_,
+ ("usb_read_port_complete:bDriverStopped(%d) OR bSurpriseRemoved(%d)\n",
+ adapt->bDriverStopped, adapt->bSurpriseRemoved));
+
+ precvbuf->reuse = true;
+ DBG_88E("%s() RX Warning! bDriverStopped(%d) OR bSurpriseRemoved(%d) bReadPortCancel(%d)\n",
+ __func__, adapt->bDriverStopped,
+ adapt->bSurpriseRemoved, adapt->bReadPortCancel);
+ return;
+ }
+
+ if (purb->status == 0) { /* SUCCESS */
+ if ((purb->actual_length > MAX_RECVBUF_SZ) || (purb->actual_length < RXDESC_SIZE)) {
+ RT_TRACE(_module_hci_ops_os_c_, _drv_err_,
+ ("usb_read_port_complete: (purb->actual_length > MAX_RECVBUF_SZ) || (purb->actual_length < RXDESC_SIZE)\n"));
+ precvbuf->reuse = true;
+ usb_read_port(adapt, precvpriv->ff_hwaddr, 0, (unsigned char *)precvbuf);
+ DBG_88E("%s()-%d: RX Warning!\n", __func__, __LINE__);
+ } else {
+ skb_put(precvbuf->pskb, purb->actual_length);
+ skb_queue_tail(&precvpriv->rx_skb_queue, precvbuf->pskb);
+
+ if (skb_queue_len(&precvpriv->rx_skb_queue) <= 1)
+ tasklet_schedule(&precvpriv->recv_tasklet);
+
+ precvbuf->pskb = NULL;
+ precvbuf->reuse = false;
+ usb_read_port(adapt, precvpriv->ff_hwaddr, 0, (unsigned char *)precvbuf);
+ }
+ } else {
+ RT_TRACE(_module_hci_ops_os_c_, _drv_err_, ("usb_read_port_complete : purb->status(%d) != 0\n", purb->status));
+
+ DBG_88E("###=> usb_read_port_complete => urb status(%d)\n", purb->status);
+ skb_put(precvbuf->pskb, purb->actual_length);
+ precvbuf->pskb = NULL;
+
+ switch (purb->status) {
+ case -EINVAL:
+ case -EPIPE:
+ case -ENODEV:
+ case -ESHUTDOWN:
+ adapt->bSurpriseRemoved = true;
+ case -ENOENT:
+ adapt->bDriverStopped = true;
+ RT_TRACE(_module_hci_ops_os_c_, _drv_err_, ("usb_read_port_complete:bDriverStopped=true\n"));
+ break;
+ case -EPROTO:
+ case -EOVERFLOW:
+ {
+ struct hal_data_8188e *haldata = GET_HAL_DATA(adapt);
+ haldata->srestpriv.Wifi_Error_Status = USB_READ_PORT_FAIL;
+ }
+ precvbuf->reuse = true;
+ usb_read_port(adapt, precvpriv->ff_hwaddr, 0, (unsigned char *)precvbuf);
+ break;
+ case -EINPROGRESS:
+ DBG_88E("ERROR: URB IS IN PROGRESS!\n");
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+u32 usb_read_port(struct adapter *adapter, u32 addr, u32 cnt, u8 *rmem)
+{
+ struct urb *purb = NULL;
+ struct recv_buf *precvbuf = (struct recv_buf *)rmem;
+ struct dvobj_priv *pdvobj = adapter_to_dvobj(adapter);
+ struct recv_priv *precvpriv = &adapter->recvpriv;
+ struct usb_device *pusbd = pdvobj->pusbdev;
+ int err;
+ unsigned int pipe;
+ size_t tmpaddr = 0;
+ size_t alignment = 0;
+ u32 ret = _SUCCESS;
+
+
+ if (adapter->bDriverStopped || adapter->bSurpriseRemoved ||
+ adapter->pwrctrlpriv.pnp_bstop_trx) {
+ RT_TRACE(_module_hci_ops_os_c_, _drv_err_,
+ ("usb_read_port:(adapt->bDriverStopped ||adapt->bSurpriseRemoved ||adapter->pwrctrlpriv.pnp_bstop_trx)!!!\n"));
+ return _FAIL;
+ }
+
+ if (!precvbuf) {
+ RT_TRACE(_module_hci_ops_os_c_, _drv_err_,
+ ("usb_read_port:precvbuf==NULL\n"));
+ return _FAIL;
+ }
+
+ if ((!precvbuf->reuse) || (precvbuf->pskb == NULL)) {
+ precvbuf->pskb = skb_dequeue(&precvpriv->free_recv_skb_queue);
+ if (NULL != precvbuf->pskb)
+ precvbuf->reuse = true;
+ }
+
+ /* re-assign for linux based on skb */
+ if ((!precvbuf->reuse) || (precvbuf->pskb == NULL)) {
+ precvbuf->pskb = netdev_alloc_skb(adapter->pnetdev, MAX_RECVBUF_SZ + RECVBUFF_ALIGN_SZ);
+ if (precvbuf->pskb == NULL) {
+ RT_TRACE(_module_hci_ops_os_c_, _drv_err_, ("init_recvbuf(): alloc_skb fail!\n"));
+ DBG_88E("#### usb_read_port() alloc_skb fail!#####\n");
+ return _FAIL;
+ }
+
+ tmpaddr = (size_t)precvbuf->pskb->data;
+ alignment = tmpaddr & (RECVBUFF_ALIGN_SZ-1);
+ skb_reserve(precvbuf->pskb, (RECVBUFF_ALIGN_SZ - alignment));
+ } else { /* reuse skb */
+ precvbuf->reuse = false;
+ }
+
+ precvpriv->rx_pending_cnt++;
+
+ purb = precvbuf->purb;
+
+ /* translate DMA FIFO addr to pipehandle */
+ pipe = ffaddr2pipehdl(pdvobj, addr);
+
+ usb_fill_bulk_urb(purb, pusbd, pipe,
+ precvbuf->pskb->data,
+ MAX_RECVBUF_SZ,
+ usb_read_port_complete,
+ precvbuf);/* context is precvbuf */
+
+ err = usb_submit_urb(purb, GFP_ATOMIC);
+ if ((err) && (err != (-EPERM))) {
+ RT_TRACE(_module_hci_ops_os_c_, _drv_err_,
+ ("cannot submit rx in-token(err=0x%.8x), URB_STATUS =0x%.8x",
+ err, purb->status));
+ DBG_88E("cannot submit rx in-token(err = 0x%08x),urb_status = %d\n",
+ err, purb->status);
+ ret = _FAIL;
+ }
+
+ return ret;
+}
+
+void usb_read_port_cancel(struct adapter *padapter)
+{
+ int i;
+ struct recv_buf *precvbuf;
+
+ precvbuf = (struct recv_buf *)padapter->recvpriv.precv_buf;
+
+ DBG_88E("%s\n", __func__);
+
+ padapter->bReadPortCancel = true;
+
+ for (i = 0; i < NR_RECVBUFF; i++) {
+ precvbuf->reuse = true;
+ if (precvbuf->purb)
+ usb_kill_urb(precvbuf->purb);
+ precvbuf++;
+ }
+}
+
+int usb_write8(struct adapter *adapter, u32 addr, u8 val)
+{
+ u8 request;
+ u8 requesttype;
+ u16 wvalue;
+ u16 index;
+ u16 len;
+ u8 data;
+
+ request = 0x05;
+ requesttype = 0x00;/* write_out */
+ index = 0;/* n/a */
+ wvalue = (u16)(addr&0x0000ffff);
+ len = 1;
+ data = val;
+ return usbctrl_vendorreq(adapter, request, wvalue,
+ index, &data, len, requesttype);
+}
+
+int usb_write16(struct adapter *adapter, u32 addr, u16 val)
+{
+ u8 request;
+ u8 requesttype;
+ u16 wvalue;
+ u16 index;
+ u16 len;
+ __le32 data;
+
+
+ request = 0x05;
+ requesttype = 0x00;/* write_out */
+ index = 0;/* n/a */
+
+ wvalue = (u16)(addr&0x0000ffff);
+ len = 2;
+
+ data = cpu_to_le32(val & 0x0000ffff);
+
+ return usbctrl_vendorreq(adapter, request, wvalue,
+ index, &data, len, requesttype);
+
+
+}
+
+int usb_write32(struct adapter *adapter, u32 addr, u32 val)
+{
+ u8 request;
+ u8 requesttype;
+ u16 wvalue;
+ u16 index;
+ u16 len;
+ __le32 data;
+
+
+ request = 0x05;
+ requesttype = 0x00;/* write_out */
+ index = 0;/* n/a */
+
+ wvalue = (u16)(addr&0x0000ffff);
+ len = 4;
+ data = cpu_to_le32(val);
+
+ return usbctrl_vendorreq(adapter, request, wvalue,
+ index, &data, len, requesttype);
+
+
+}
+
+static void usb_write_port_complete(struct urb *purb, struct pt_regs *regs)
+{
+ struct xmit_buf *pxmitbuf = (struct xmit_buf *)purb->context;
+ struct adapter *padapter = pxmitbuf->padapter;
+ struct xmit_priv *pxmitpriv = &padapter->xmitpriv;
+
+ switch (pxmitbuf->flags) {
+ case VO_QUEUE_INX:
+ pxmitpriv->voq_cnt--;
+ break;
+ case VI_QUEUE_INX:
+ pxmitpriv->viq_cnt--;
+ break;
+ case BE_QUEUE_INX:
+ pxmitpriv->beq_cnt--;
+ break;
+ case BK_QUEUE_INX:
+ pxmitpriv->bkq_cnt--;
+ break;
+ case HIGH_QUEUE_INX:
+#ifdef CONFIG_88EU_AP_MODE
+ rtw_chk_hi_queue_cmd(padapter);
+#endif
+ break;
+ default:
+ break;
+ }
+
+ if (padapter->bSurpriseRemoved || padapter->bDriverStopped ||
+ padapter->bWritePortCancel) {
+ RT_TRACE(_module_hci_ops_os_c_, _drv_err_,
+ ("usb_write_port_complete:bDriverStopped(%d) OR bSurpriseRemoved(%d)",
+ padapter->bDriverStopped, padapter->bSurpriseRemoved));
+ DBG_88E("%s(): TX Warning! bDriverStopped(%d) OR bSurpriseRemoved(%d) bWritePortCancel(%d) pxmitbuf->ext_tag(%x)\n",
+ __func__, padapter->bDriverStopped,
+ padapter->bSurpriseRemoved, padapter->bReadPortCancel,
+ pxmitbuf->ext_tag);
+
+ goto check_completion;
+ }
+
+ if (purb->status) {
+ RT_TRACE(_module_hci_ops_os_c_, _drv_err_, ("usb_write_port_complete : purb->status(%d) != 0\n", purb->status));
+ DBG_88E("###=> urb_write_port_complete status(%d)\n", purb->status);
+ if ((purb->status == -EPIPE) || (purb->status == -EPROTO)) {
+ sreset_set_wifi_error_status(padapter, USB_WRITE_PORT_FAIL);
+ } else if (purb->status == -EINPROGRESS) {
+ RT_TRACE(_module_hci_ops_os_c_, _drv_err_, ("usb_write_port_complete: EINPROGESS\n"));
+ goto check_completion;
+ } else if (purb->status == -ENOENT) {
+ DBG_88E("%s: -ENOENT\n", __func__);
+ goto check_completion;
+ } else if (purb->status == -ECONNRESET) {
+ DBG_88E("%s: -ECONNRESET\n", __func__);
+ goto check_completion;
+ } else if (purb->status == -ESHUTDOWN) {
+ RT_TRACE(_module_hci_ops_os_c_, _drv_err_, ("usb_write_port_complete: ESHUTDOWN\n"));
+ padapter->bDriverStopped = true;
+ RT_TRACE(_module_hci_ops_os_c_, _drv_err_, ("usb_write_port_complete:bDriverStopped = true\n"));
+ goto check_completion;
+ } else {
+ padapter->bSurpriseRemoved = true;
+ DBG_88E("bSurpriseRemoved = true\n");
+ RT_TRACE(_module_hci_ops_os_c_, _drv_err_, ("usb_write_port_complete:bSurpriseRemoved = true\n"));
+
+ goto check_completion;
+ }
+ }
+
+check_completion:
+ rtw_sctx_done_err(&pxmitbuf->sctx,
+ purb->status ? RTW_SCTX_DONE_WRITE_PORT_ERR :
+ RTW_SCTX_DONE_SUCCESS);
+
+ rtw_free_xmitbuf(pxmitpriv, pxmitbuf);
+
+ tasklet_hi_schedule(&pxmitpriv->xmit_tasklet);
+}
+
+u32 usb_write_port(struct adapter *padapter, u32 addr, u32 cnt, u8 *wmem)
+{
+ unsigned long irqL;
+ unsigned int pipe;
+ int status;
+ u32 ret = _FAIL;
+ struct urb *purb = NULL;
+ struct dvobj_priv *pdvobj = adapter_to_dvobj(padapter);
+ struct xmit_priv *pxmitpriv = &padapter->xmitpriv;
+ struct xmit_buf *pxmitbuf = (struct xmit_buf *)wmem;
+ struct xmit_frame *pxmitframe = (struct xmit_frame *)pxmitbuf->priv_data;
+ struct usb_device *pusbd = pdvobj->pusbdev;
+
+
+ RT_TRACE(_module_hci_ops_os_c_, _drv_err_, ("+usb_write_port\n"));
+
+ if ((padapter->bDriverStopped) || (padapter->bSurpriseRemoved) ||
+ (padapter->pwrctrlpriv.pnp_bstop_trx)) {
+ RT_TRACE(_module_hci_ops_os_c_, _drv_err_,
+ ("usb_write_port:( padapter->bDriverStopped ||padapter->bSurpriseRemoved ||adapter->pwrctrlpriv.pnp_bstop_trx)!!!\n"));
+ rtw_sctx_done_err(&pxmitbuf->sctx, RTW_SCTX_DONE_TX_DENY);
+ goto exit;
+ }
+
+ spin_lock_irqsave(&pxmitpriv->lock, irqL);
+
+ switch (addr) {
+ case VO_QUEUE_INX:
+ pxmitpriv->voq_cnt++;
+ pxmitbuf->flags = VO_QUEUE_INX;
+ break;
+ case VI_QUEUE_INX:
+ pxmitpriv->viq_cnt++;
+ pxmitbuf->flags = VI_QUEUE_INX;
+ break;
+ case BE_QUEUE_INX:
+ pxmitpriv->beq_cnt++;
+ pxmitbuf->flags = BE_QUEUE_INX;
+ break;
+ case BK_QUEUE_INX:
+ pxmitpriv->bkq_cnt++;
+ pxmitbuf->flags = BK_QUEUE_INX;
+ break;
+ case HIGH_QUEUE_INX:
+ pxmitbuf->flags = HIGH_QUEUE_INX;
+ break;
+ default:
+ pxmitbuf->flags = MGT_QUEUE_INX;
+ break;
+ }
+
+ spin_unlock_irqrestore(&pxmitpriv->lock, irqL);
+
+ purb = pxmitbuf->pxmit_urb[0];
+
+ /* translate DMA FIFO addr to pipehandle */
+ pipe = ffaddr2pipehdl(pdvobj, addr);
+
+ usb_fill_bulk_urb(purb, pusbd, pipe,
+ pxmitframe->buf_addr, /* pxmitbuf->pbuf */
+ cnt,
+ usb_write_port_complete,
+ pxmitbuf);/* context is pxmitbuf */
+
+ status = usb_submit_urb(purb, GFP_ATOMIC);
+ if (status) {
+ rtw_sctx_done_err(&pxmitbuf->sctx, RTW_SCTX_DONE_WRITE_PORT_ERR);
+ DBG_88E("usb_write_port, status =%d\n", status);
+ RT_TRACE(_module_hci_ops_os_c_, _drv_err_, ("usb_write_port(): usb_submit_urb, status =%x\n", status));
+
+ switch (status) {
+ case -ENODEV:
+ padapter->bDriverStopped = true;
+ break;
+ default:
+ break;
+ }
+ goto exit;
+ }
+
+ ret = _SUCCESS;
+
+/* We add the URB_ZERO_PACKET flag to urb so that the host will send the zero packet automatically. */
+
+ RT_TRACE(_module_hci_ops_os_c_, _drv_err_, ("-usb_write_port\n"));
+
+exit:
+ if (ret != _SUCCESS)
+ rtw_free_xmitbuf(pxmitpriv, pxmitbuf);
+ return ret;
+}
+
+void usb_write_port_cancel(struct adapter *padapter)
+{
+ int i, j;
+ struct xmit_buf *pxmitbuf = (struct xmit_buf *)padapter->xmitpriv.pxmitbuf;
+
+ DBG_88E("%s\n", __func__);
+
+ padapter->bWritePortCancel = true;
+
+ for (i = 0; i < NR_XMITBUFF; i++) {
+ for (j = 0; j < 8; j++) {
+ if (pxmitbuf->pxmit_urb[j])
+ usb_kill_urb(pxmitbuf->pxmit_urb[j]);
+ }
+ pxmitbuf++;
+ }
+
+ pxmitbuf = (struct xmit_buf *)padapter->xmitpriv.pxmit_extbuf;
+ for (i = 0; i < NR_XMIT_EXTBUFF; i++) {
+ for (j = 0; j < 8; j++) {
+ if (pxmitbuf->pxmit_urb[j])
+ usb_kill_urb(pxmitbuf->pxmit_urb[j]);
+ }
+ pxmitbuf++;
+ }
+}
+
+void rtl8188eu_recv_tasklet(void *priv)
+{
+ struct sk_buff *pskb;
+ struct adapter *adapt = priv;
+ struct recv_priv *precvpriv = &adapt->recvpriv;
+
+ while (NULL != (pskb = skb_dequeue(&precvpriv->rx_skb_queue))) {
+ if ((adapt->bDriverStopped) || (adapt->bSurpriseRemoved)) {
+ DBG_88E("recv_tasklet => bDriverStopped or bSurpriseRemoved\n");
+ dev_kfree_skb_any(pskb);
+ break;
+ }
+ recvbuf2recvframe(adapt, pskb);
+ skb_reset_tail_pointer(pskb);
+ pskb->len = 0;
+ skb_queue_tail(&precvpriv->free_recv_skb_queue, pskb);
+ }
+}
+
+void rtl8188eu_xmit_tasklet(void *priv)
+{
+ int ret = false;
+ struct adapter *adapt = priv;
+ struct xmit_priv *pxmitpriv = &adapt->xmitpriv;
+
+ if (check_fwstate(&adapt->mlmepriv, _FW_UNDER_SURVEY))
+ return;
+
+ while (1) {
+ if ((adapt->bDriverStopped) ||
+ (adapt->bSurpriseRemoved) ||
+ (adapt->bWritePortCancel)) {
+ DBG_88E("xmit_tasklet => bDriverStopped or bSurpriseRemoved or bWritePortCancel\n");
+ break;
+ }
+
+ ret = rtl8188eu_xmitframe_complete(adapt, pxmitpriv, NULL);
+
+ if (!ret)
+ break;
+ }
+}
diff --git a/drivers/staging/rtl8188eu/os_dep/xmit_linux.c b/drivers/staging/rtl8188eu/os_dep/xmit_linux.c
new file mode 100644
index 000000000..5acf9a9dd
--- /dev/null
+++ b/drivers/staging/rtl8188eu/os_dep/xmit_linux.c
@@ -0,0 +1,260 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2012 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ *
+ ******************************************************************************/
+#define _XMIT_OSDEP_C_
+
+#include <osdep_service.h>
+#include <drv_types.h>
+
+#include <wifi.h>
+#include <mlme_osdep.h>
+#include <xmit_osdep.h>
+#include <osdep_intf.h>
+
+uint rtw_remainder_len(struct pkt_file *pfile)
+{
+ return pfile->buf_len - ((size_t)(pfile->cur_addr) -
+ (size_t)(pfile->buf_start));
+}
+
+void _rtw_open_pktfile(struct sk_buff *pktptr, struct pkt_file *pfile)
+{
+
+ pfile->pkt = pktptr;
+ pfile->cur_addr = pktptr->data;
+ pfile->buf_start = pktptr->data;
+ pfile->pkt_len = pktptr->len;
+ pfile->buf_len = pktptr->len;
+
+ pfile->cur_buffer = pfile->buf_start;
+
+}
+
+uint _rtw_pktfile_read(struct pkt_file *pfile, u8 *rmem, uint rlen)
+{
+ uint len = 0;
+
+
+ len = rtw_remainder_len(pfile);
+ len = (rlen > len) ? len : rlen;
+
+ if (rmem)
+ skb_copy_bits(pfile->pkt, pfile->buf_len-pfile->pkt_len, rmem, len);
+
+ pfile->cur_addr += len;
+ pfile->pkt_len -= len;
+
+
+ return len;
+}
+
+int rtw_endofpktfile(struct pkt_file *pfile)
+{
+ return pfile->pkt_len == 0;
+}
+
+int rtw_os_xmit_resource_alloc(struct adapter *padapter, struct xmit_buf *pxmitbuf, u32 alloc_sz)
+{
+ int i;
+
+ pxmitbuf->pallocated_buf = kzalloc(alloc_sz, GFP_KERNEL);
+ if (pxmitbuf->pallocated_buf == NULL)
+ return _FAIL;
+
+ pxmitbuf->pbuf = (u8 *)N_BYTE_ALIGMENT((size_t)(pxmitbuf->pallocated_buf), XMITBUF_ALIGN_SZ);
+ pxmitbuf->dma_transfer_addr = 0;
+
+ for (i = 0; i < 8; i++) {
+ pxmitbuf->pxmit_urb[i] = usb_alloc_urb(0, GFP_KERNEL);
+ if (pxmitbuf->pxmit_urb[i] == NULL) {
+ DBG_88E("pxmitbuf->pxmit_urb[i]==NULL");
+ return _FAIL;
+ }
+ }
+ return _SUCCESS;
+}
+
+void rtw_os_xmit_resource_free(struct adapter *padapter,
+ struct xmit_buf *pxmitbuf, u32 free_sz)
+{
+ int i;
+
+ for (i = 0; i < 8; i++)
+ usb_free_urb(pxmitbuf->pxmit_urb[i]);
+
+ kfree(pxmitbuf->pallocated_buf);
+}
+
+#define WMM_XMIT_THRESHOLD (NR_XMITFRAME*2/5)
+
+void rtw_os_pkt_complete(struct adapter *padapter, struct sk_buff *pkt)
+{
+ u16 queue;
+ struct xmit_priv *pxmitpriv = &padapter->xmitpriv;
+
+ queue = skb_get_queue_mapping(pkt);
+ if (padapter->registrypriv.wifi_spec) {
+ if (__netif_subqueue_stopped(padapter->pnetdev, queue) &&
+ (pxmitpriv->hwxmits[queue].accnt < WMM_XMIT_THRESHOLD))
+ netif_wake_subqueue(padapter->pnetdev, queue);
+ } else {
+ if (__netif_subqueue_stopped(padapter->pnetdev, queue))
+ netif_wake_subqueue(padapter->pnetdev, queue);
+ }
+
+ dev_kfree_skb_any(pkt);
+}
+
+void rtw_os_xmit_complete(struct adapter *padapter, struct xmit_frame *pxframe)
+{
+ if (pxframe->pkt)
+ rtw_os_pkt_complete(padapter, pxframe->pkt);
+ pxframe->pkt = NULL;
+}
+
+void rtw_os_xmit_schedule(struct adapter *padapter)
+{
+ struct xmit_priv *pxmitpriv;
+
+ if (!padapter)
+ return;
+
+ pxmitpriv = &padapter->xmitpriv;
+
+ spin_lock_bh(&pxmitpriv->lock);
+
+ if (rtw_txframes_pending(padapter))
+ tasklet_hi_schedule(&pxmitpriv->xmit_tasklet);
+
+ spin_unlock_bh(&pxmitpriv->lock);
+}
+
+static void rtw_check_xmit_resource(struct adapter *padapter, struct sk_buff *pkt)
+{
+ struct xmit_priv *pxmitpriv = &padapter->xmitpriv;
+ u16 queue;
+
+ queue = skb_get_queue_mapping(pkt);
+ if (padapter->registrypriv.wifi_spec) {
+ /* No free space for Tx, tx_worker is too slow */
+ if (pxmitpriv->hwxmits[queue].accnt > WMM_XMIT_THRESHOLD)
+ netif_stop_subqueue(padapter->pnetdev, queue);
+ } else {
+ if (pxmitpriv->free_xmitframe_cnt <= 4) {
+ if (!netif_tx_queue_stopped(netdev_get_tx_queue(padapter->pnetdev, queue)))
+ netif_stop_subqueue(padapter->pnetdev, queue);
+ }
+ }
+}
+
+static int rtw_mlcst2unicst(struct adapter *padapter, struct sk_buff *skb)
+{
+ struct sta_priv *pstapriv = &padapter->stapriv;
+ struct xmit_priv *pxmitpriv = &padapter->xmitpriv;
+ struct list_head *phead, *plist;
+ struct sk_buff *newskb;
+ struct sta_info *psta = NULL;
+ s32 res;
+
+ spin_lock_bh(&pstapriv->asoc_list_lock);
+ phead = &pstapriv->asoc_list;
+ plist = phead->next;
+
+ /* free sta asoc_queue */
+ while (phead != plist) {
+ psta = container_of(plist, struct sta_info, asoc_list);
+
+ plist = plist->next;
+
+ /* avoid come from STA1 and send back STA1 */
+ if (!memcmp(psta->hwaddr, &skb->data[6], 6))
+ continue;
+
+ newskb = skb_copy(skb, GFP_ATOMIC);
+
+ if (newskb) {
+ memcpy(newskb->data, psta->hwaddr, 6);
+ res = rtw_xmit(padapter, &newskb);
+ if (res < 0) {
+ DBG_88E("%s()-%d: rtw_xmit() return error!\n", __func__, __LINE__);
+ pxmitpriv->tx_drop++;
+ dev_kfree_skb_any(newskb);
+ } else {
+ pxmitpriv->tx_pkts++;
+ }
+ } else {
+ DBG_88E("%s-%d: skb_copy() failed!\n", __func__, __LINE__);
+ pxmitpriv->tx_drop++;
+
+ spin_unlock_bh(&pstapriv->asoc_list_lock);
+ return false; /* Caller shall tx this multicast frame via normal way. */
+ }
+ }
+
+ spin_unlock_bh(&pstapriv->asoc_list_lock);
+ dev_kfree_skb_any(skb);
+ return true;
+}
+
+
+int rtw_xmit_entry(struct sk_buff *pkt, struct net_device *pnetdev)
+{
+ struct adapter *padapter = (struct adapter *)rtw_netdev_priv(pnetdev);
+ struct xmit_priv *pxmitpriv = &padapter->xmitpriv;
+ struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
+ s32 res = 0;
+
+
+ RT_TRACE(_module_rtl871x_mlme_c_, _drv_info_, ("+xmit_enry\n"));
+
+ if (rtw_if_up(padapter) == false) {
+ RT_TRACE(_module_xmit_osdep_c_, _drv_err_, ("rtw_xmit_entry: rtw_if_up fail\n"));
+ goto drop_packet;
+ }
+
+ rtw_check_xmit_resource(padapter, pkt);
+
+ if (!rtw_mc2u_disable && check_fwstate(pmlmepriv, WIFI_AP_STATE) &&
+ (IP_MCAST_MAC(pkt->data) || ICMPV6_MCAST_MAC(pkt->data)) &&
+ (padapter->registrypriv.wifi_spec == 0)) {
+ if (pxmitpriv->free_xmitframe_cnt > (NR_XMITFRAME/4)) {
+ res = rtw_mlcst2unicst(padapter, pkt);
+ if (res)
+ goto exit;
+ }
+ }
+
+ res = rtw_xmit(padapter, &pkt);
+ if (res < 0)
+ goto drop_packet;
+
+ pxmitpriv->tx_pkts++;
+ RT_TRACE(_module_xmit_osdep_c_, _drv_info_, ("rtw_xmit_entry: tx_pkts=%d\n", (u32)pxmitpriv->tx_pkts));
+ goto exit;
+
+drop_packet:
+ pxmitpriv->tx_drop++;
+ dev_kfree_skb_any(pkt);
+ RT_TRACE(_module_xmit_osdep_c_, _drv_notice_, ("rtw_xmit_entry: drop, tx_drop=%d\n", (u32)pxmitpriv->tx_drop));
+
+exit:
+
+
+ return 0;
+}
diff --git a/drivers/staging/rtl8192e/Kconfig b/drivers/staging/rtl8192e/Kconfig
new file mode 100644
index 000000000..4602a47cd
--- /dev/null
+++ b/drivers/staging/rtl8192e/Kconfig
@@ -0,0 +1,47 @@
+config RTLLIB
+ tristate "Support for rtllib wireless devices"
+ depends on WLAN && m
+ default n
+ select LIB80211
+ ---help---
+ If you have a wireless card that uses rtllib, say
+ Y. Currently the only card is the rtl8192e.
+
+ If unsure, say N.
+
+if RTLLIB
+
+config RTLLIB_CRYPTO_CCMP
+ tristate "Support for rtllib CCMP crypto"
+ depends on RTLLIB
+ select CRYPTO_AES
+ default y
+ ---help---
+ CCMP crypto driver for rtllib.
+
+ If you enabled RTLLIB, you want this.
+
+config RTLLIB_CRYPTO_TKIP
+ tristate "Support for rtllib TKIP crypto"
+ depends on RTLLIB
+ select CRYPTO_ARC4
+ select CRYPTO_MICHAEL_MIC
+ default y
+ ---help---
+ TKIP crypto driver for rtllib.
+
+ If you enabled RTLLIB, you want this.
+
+config RTLLIB_CRYPTO_WEP
+ tristate "Support for rtllib WEP crypto"
+ select CRYPTO_ARC4
+ depends on RTLLIB
+ default y
+ ---help---
+ TKIP crypto driver for rtllib.
+
+ If you enabled RTLLIB, you want this.
+
+source "drivers/staging/rtl8192e/rtl8192e/Kconfig"
+
+endif
diff --git a/drivers/staging/rtl8192e/Makefile b/drivers/staging/rtl8192e/Makefile
new file mode 100644
index 000000000..cb18db74d
--- /dev/null
+++ b/drivers/staging/rtl8192e/Makefile
@@ -0,0 +1,21 @@
+rtllib-objs := \
+ dot11d.o \
+ rtllib_module.o \
+ rtllib_rx.o \
+ rtllib_tx.o \
+ rtllib_wx.o \
+ rtllib_softmac.o \
+ rtllib_softmac_wx.o \
+ rtl819x_BAProc.o \
+ rtl819x_HTProc.o \
+ rtl819x_TSProc.o
+
+obj-$(CONFIG_RTLLIB) += rtllib.o
+
+obj-$(CONFIG_RTLLIB_CRYPTO_CCMP) += rtllib_crypt_ccmp.o
+obj-$(CONFIG_RTLLIB_CRYPTO_TKIP) += rtllib_crypt_tkip.o
+obj-$(CONFIG_RTLLIB_CRYPTO_WEP) += rtllib_crypt_wep.o
+
+obj-$(CONFIG_RTL8192E) += rtl8192e/
+
+ccflags-y += -D__CHECK_ENDIAN__
diff --git a/drivers/staging/rtl8192e/TODO b/drivers/staging/rtl8192e/TODO
new file mode 100644
index 000000000..d51f159d1
--- /dev/null
+++ b/drivers/staging/rtl8192e/TODO
@@ -0,0 +1,2 @@
+* merge into drivers/net/wireless/rtllib/rtl8192e
+* clean up function naming
diff --git a/drivers/staging/rtl8192e/dot11d.c b/drivers/staging/rtl8192e/dot11d.c
new file mode 100644
index 000000000..ef9da863c
--- /dev/null
+++ b/drivers/staging/rtl8192e/dot11d.c
@@ -0,0 +1,216 @@
+/******************************************************************************
+ * Copyright(c) 2008 - 2010 Realtek Corporation. All rights reserved.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ * The full GNU General Public License is included in this distribution in the
+ * file called LICENSE.
+ *
+ * Contact Information:
+ * wlanfae <wlanfae@realtek.com>
+******************************************************************************/
+#include "dot11d.h"
+
+struct channel_list {
+ u8 Channel[32];
+ u8 Len;
+};
+
+static struct channel_list ChannelPlan[] = {
+ {{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 36, 40, 44, 48, 52, 56, 60, 64,
+ 149, 153, 157, 161, 165}, 24},
+ {{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}, 11},
+ {{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 36, 40, 44, 48, 52, 56,
+ 60, 64}, 21},
+ {{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13}, 13},
+ {{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13}, 13},
+ {{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 36, 40, 44, 48, 52,
+ 56, 60, 64}, 22},
+ {{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 36, 40, 44, 48, 52,
+ 56, 60, 64}, 22},
+ {{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13}, 13},
+ {{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 36, 40, 44, 48, 52,
+ 56, 60, 64}, 22},
+ {{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 36, 40, 44, 48, 52,
+ 56, 60, 64}, 22},
+ {{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14}, 14},
+ {{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13}, 13},
+ {{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 36, 40, 44, 48, 52,
+ 56, 60, 64}, 21}
+};
+
+void dot11d_init(struct rtllib_device *ieee)
+{
+ struct rt_dot11d_info *pDot11dInfo = GET_DOT11D_INFO(ieee);
+
+ pDot11dInfo->bEnabled = false;
+
+ pDot11dInfo->State = DOT11D_STATE_NONE;
+ pDot11dInfo->CountryIeLen = 0;
+ memset(pDot11dInfo->channel_map, 0, MAX_CHANNEL_NUMBER+1);
+ memset(pDot11dInfo->MaxTxPwrDbmList, 0xFF, MAX_CHANNEL_NUMBER+1);
+ RESET_CIE_WATCHDOG(ieee);
+
+}
+EXPORT_SYMBOL(dot11d_init);
+
+void Dot11d_Channelmap(u8 channel_plan, struct rtllib_device *ieee)
+{
+ int i, max_chan = 14, min_chan = 1;
+
+ ieee->bGlobalDomain = false;
+
+ if (ChannelPlan[channel_plan].Len != 0) {
+ memset(GET_DOT11D_INFO(ieee)->channel_map, 0,
+ sizeof(GET_DOT11D_INFO(ieee)->channel_map));
+ for (i = 0; i < ChannelPlan[channel_plan].Len; i++) {
+ if (ChannelPlan[channel_plan].Channel[i] < min_chan ||
+ ChannelPlan[channel_plan].Channel[i] > max_chan)
+ break;
+ GET_DOT11D_INFO(ieee)->channel_map[ChannelPlan
+ [channel_plan].Channel[i]] = 1;
+ }
+ }
+
+ switch (channel_plan) {
+ case COUNTRY_CODE_GLOBAL_DOMAIN:
+ ieee->bGlobalDomain = true;
+ for (i = 12; i <= 14; i++)
+ GET_DOT11D_INFO(ieee)->channel_map[i] = 2;
+ ieee->IbssStartChnl = 10;
+ ieee->ibss_maxjoin_chal = 11;
+ break;
+
+ case COUNTRY_CODE_WORLD_WIDE_13:
+ for (i = 12; i <= 13; i++)
+ GET_DOT11D_INFO(ieee)->channel_map[i] = 2;
+ ieee->IbssStartChnl = 10;
+ ieee->ibss_maxjoin_chal = 11;
+ break;
+
+ default:
+ ieee->IbssStartChnl = 1;
+ ieee->ibss_maxjoin_chal = 14;
+ break;
+ }
+}
+EXPORT_SYMBOL(Dot11d_Channelmap);
+
+
+void Dot11d_Reset(struct rtllib_device *ieee)
+{
+ struct rt_dot11d_info *pDot11dInfo = GET_DOT11D_INFO(ieee);
+ u32 i;
+
+ memset(pDot11dInfo->channel_map, 0, MAX_CHANNEL_NUMBER+1);
+ memset(pDot11dInfo->MaxTxPwrDbmList, 0xFF, MAX_CHANNEL_NUMBER+1);
+ for (i = 1; i <= 11; i++)
+ (pDot11dInfo->channel_map)[i] = 1;
+ for (i = 12; i <= 14; i++)
+ (pDot11dInfo->channel_map)[i] = 2;
+ pDot11dInfo->State = DOT11D_STATE_NONE;
+ pDot11dInfo->CountryIeLen = 0;
+ RESET_CIE_WATCHDOG(ieee);
+}
+
+void Dot11d_UpdateCountryIe(struct rtllib_device *dev, u8 *pTaddr,
+ u16 CoutryIeLen, u8 *pCoutryIe)
+{
+ struct rt_dot11d_info *pDot11dInfo = GET_DOT11D_INFO(dev);
+ u8 i, j, NumTriples, MaxChnlNum;
+ struct chnl_txpow_triple *pTriple;
+
+ memset(pDot11dInfo->channel_map, 0, MAX_CHANNEL_NUMBER+1);
+ memset(pDot11dInfo->MaxTxPwrDbmList, 0xFF, MAX_CHANNEL_NUMBER+1);
+ MaxChnlNum = 0;
+ NumTriples = (CoutryIeLen - 3) / 3;
+ pTriple = (struct chnl_txpow_triple *)(pCoutryIe + 3);
+ for (i = 0; i < NumTriples; i++) {
+ if (MaxChnlNum >= pTriple->FirstChnl) {
+ netdev_info(dev->dev, "Dot11d_UpdateCountryIe(): Invalid country IE, skip it........1\n");
+ return;
+ }
+ if (MAX_CHANNEL_NUMBER < (pTriple->FirstChnl +
+ pTriple->NumChnls)) {
+ netdev_info(dev->dev, "Dot11d_UpdateCountryIe(): Invalid country IE, skip it........2\n");
+ return;
+ }
+
+ for (j = 0; j < pTriple->NumChnls; j++) {
+ pDot11dInfo->channel_map[pTriple->FirstChnl + j] = 1;
+ pDot11dInfo->MaxTxPwrDbmList[pTriple->FirstChnl + j] =
+ pTriple->MaxTxPowerInDbm;
+ MaxChnlNum = pTriple->FirstChnl + j;
+ }
+
+ pTriple = (struct chnl_txpow_triple *)((u8 *)pTriple + 3);
+ }
+
+ UPDATE_CIE_SRC(dev, pTaddr);
+
+ pDot11dInfo->CountryIeLen = CoutryIeLen;
+ memcpy(pDot11dInfo->CountryIeBuf, pCoutryIe, CoutryIeLen);
+ pDot11dInfo->State = DOT11D_STATE_LEARNED;
+}
+
+u8 DOT11D_GetMaxTxPwrInDbm(struct rtllib_device *dev, u8 Channel)
+{
+ struct rt_dot11d_info *pDot11dInfo = GET_DOT11D_INFO(dev);
+ u8 MaxTxPwrInDbm = 255;
+
+ if (MAX_CHANNEL_NUMBER < Channel) {
+ netdev_info(dev->dev, "DOT11D_GetMaxTxPwrInDbm(): Invalid Channel\n");
+ return MaxTxPwrInDbm;
+ }
+ if (pDot11dInfo->channel_map[Channel])
+ MaxTxPwrInDbm = pDot11dInfo->MaxTxPwrDbmList[Channel];
+
+ return MaxTxPwrInDbm;
+}
+
+void DOT11D_ScanComplete(struct rtllib_device *dev)
+{
+ struct rt_dot11d_info *pDot11dInfo = GET_DOT11D_INFO(dev);
+
+ switch (pDot11dInfo->State) {
+ case DOT11D_STATE_LEARNED:
+ pDot11dInfo->State = DOT11D_STATE_DONE;
+ break;
+ case DOT11D_STATE_DONE:
+ Dot11d_Reset(dev);
+ break;
+ case DOT11D_STATE_NONE:
+ break;
+ }
+}
+
+int ToLegalChannel(struct rtllib_device *dev, u8 channel)
+{
+ struct rt_dot11d_info *pDot11dInfo = GET_DOT11D_INFO(dev);
+ u8 default_chn = 0;
+ u32 i;
+
+ for (i = 1; i <= MAX_CHANNEL_NUMBER; i++) {
+ if (pDot11dInfo->channel_map[i] > 0) {
+ default_chn = i;
+ break;
+ }
+ }
+
+ if (MAX_CHANNEL_NUMBER < channel) {
+ netdev_err(dev->dev, "%s(): Invalid Channel\n", __func__);
+ return default_chn;
+ }
+
+ if (pDot11dInfo->channel_map[channel] > 0)
+ return channel;
+
+ return default_chn;
+}
diff --git a/drivers/staging/rtl8192e/dot11d.h b/drivers/staging/rtl8192e/dot11d.h
new file mode 100644
index 000000000..aad339439
--- /dev/null
+++ b/drivers/staging/rtl8192e/dot11d.h
@@ -0,0 +1,103 @@
+/******************************************************************************
+ * Copyright(c) 2008 - 2010 Realtek Corporation. All rights reserved.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ * The full GNU General Public License is included in this distribution in the
+ * file called LICENSE.
+ *
+ * Contact Information:
+ * wlanfae <wlanfae@realtek.com>
+******************************************************************************/
+#ifndef __INC_DOT11D_H
+#define __INC_DOT11D_H
+
+#include "rtllib.h"
+
+
+
+struct chnl_txpow_triple {
+ u8 FirstChnl;
+ u8 NumChnls;
+ u8 MaxTxPowerInDbm;
+};
+
+enum dot11d_state {
+ DOT11D_STATE_NONE = 0,
+ DOT11D_STATE_LEARNED,
+ DOT11D_STATE_DONE,
+};
+
+/**
+ * struct rt_dot11d_info * @CountryIeLen: value greater than 0 if @CountryIeBuf contains
+ * valid country information element.
+ * @channel_map: holds channel values
+ * 0 - invalid,
+ * 1 - valid (active scan),
+ * 2 - valid (passive scan)
+ * @CountryIeSrcAddr - Source AP of the country IE
+ */
+
+struct rt_dot11d_info {
+
+ bool bEnabled;
+
+ u16 CountryIeLen;
+ u8 CountryIeBuf[MAX_IE_LEN];
+ u8 CountryIeSrcAddr[6];
+ u8 CountryIeWatchdog;
+
+ u8 channel_map[MAX_CHANNEL_NUMBER+1];
+ u8 MaxTxPwrDbmList[MAX_CHANNEL_NUMBER+1];
+
+ enum dot11d_state State;
+};
+
+static inline void cpMacAddr(unsigned char *des, unsigned char *src)
+{
+ memcpy(des, src, 6);
+}
+
+#define GET_DOT11D_INFO(__pIeeeDev) \
+ ((struct rt_dot11d_info *)((__pIeeeDev)->pDot11dInfo))
+
+#define IS_DOT11D_ENABLE(__pIeeeDev) \
+ (GET_DOT11D_INFO(__pIeeeDev)->bEnabled)
+#define IS_COUNTRY_IE_VALID(__pIeeeDev) \
+ (GET_DOT11D_INFO(__pIeeeDev)->CountryIeLen > 0)
+
+#define IS_EQUAL_CIE_SRC(__pIeeeDev, __pTa) \
+ ether_addr_equal_unaligned(GET_DOT11D_INFO(__pIeeeDev)->CountryIeSrcAddr, \
+ __pTa)
+#define UPDATE_CIE_SRC(__pIeeeDev, __pTa) \
+ cpMacAddr(GET_DOT11D_INFO(__pIeeeDev)->CountryIeSrcAddr, __pTa)
+
+#define CIE_WATCHDOG_TH 1
+#define GET_CIE_WATCHDOG(__pIeeeDev) \
+ (GET_DOT11D_INFO(__pIeeeDev)->CountryIeWatchdog)
+static inline void RESET_CIE_WATCHDOG(struct rtllib_device *__pIeeeDev)
+{
+ GET_CIE_WATCHDOG(__pIeeeDev) = 0;
+}
+#define UPDATE_CIE_WATCHDOG(__pIeeeDev) (++GET_CIE_WATCHDOG(__pIeeeDev))
+
+#define IS_DOT11D_STATE_DONE(__pIeeeDev) \
+ (GET_DOT11D_INFO(__pIeeeDev)->State == DOT11D_STATE_DONE)
+
+void dot11d_init(struct rtllib_device *dev);
+void Dot11d_Channelmap(u8 channel_plan, struct rtllib_device *ieee);
+void Dot11d_Reset(struct rtllib_device *dev);
+void Dot11d_UpdateCountryIe(struct rtllib_device *dev, u8 *pTaddr,
+ u16 CoutryIeLen, u8 *pCoutryIe);
+u8 DOT11D_GetMaxTxPwrInDbm(struct rtllib_device *dev, u8 Channel);
+void DOT11D_ScanComplete(struct rtllib_device *dev);
+int ToLegalChannel(struct rtllib_device *dev, u8 channel);
+
+#endif
diff --git a/drivers/staging/rtl8192e/license b/drivers/staging/rtl8192e/license
new file mode 100644
index 000000000..4bea9fa60
--- /dev/null
+++ b/drivers/staging/rtl8192e/license
@@ -0,0 +1,339 @@
+
+"This software program is licensed subject to the GNU General Public License
+(GPL). Version 2, June 1991, available at
+<http:
+
+GNU General Public License
+
+Version 2, June 1991
+
+Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+
+Everyone is permitted to copy and distribute verbatim copies of this license
+document, but changing it is not allowed.
+
+Preamble
+
+The licenses for most software are designed to take away your freedom to
+share and change it. By contrast, the GNU General Public License is intended
+to guarantee your freedom to share and change free software--to make sure
+the software is free for all its users. This General Public License applies
+to most of the Free Software Foundation's software and to any other program
+whose authors commit to using it. (Some other Free Software Foundation
+software is covered by the GNU Library General Public License instead.) You
+can apply it to your programs, too.
+
+When we speak of free software, we are referring to freedom, not price. Our
+General Public Licenses are designed to make sure that you have the freedom
+to distribute copies of free software (and charge for this service if you
+wish), that you receive source code or can get it if you want it, that you
+can change the software or use pieces of it in new free programs; and that
+you know you can do these things.
+
+To protect your rights, we need to make restrictions that forbid anyone to
+deny you these rights or to ask you to surrender the rights. These
+restrictions translate to certain responsibilities for you if you distribute
+copies of the software, or if you modify it.
+
+For example, if you distribute copies of such a program, whether gratis or
+for a fee, you must give the recipients all the rights that you have. You
+must make sure that they, too, receive or can get the source code. And you
+must show them these terms so they know their rights.
+
+We protect your rights with two steps: (1) copyright the software, and (2)
+offer you this license which gives you legal permission to copy, distribute
+and/or modify the software.
+
+Also, for each author's protection and ours, we want to make certain that
+everyone understands that there is no warranty for this free software. If
+the software is modified by someone else and passed on, we want its
+recipients to know that what they have is not the original, so that any
+problems introduced by others will not reflect on the original authors'
+reputations.
+
+Finally, any free program is threatened constantly by software patents. We
+wish to avoid the danger that redistributors of a free program will
+individually obtain patent licenses, in effect making the program
+proprietary. To prevent this, we have made it clear that any patent must be
+licensed for everyone's free use or not licensed at all.
+
+The precise terms and conditions for copying, distribution and modification
+follow.
+
+TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+0. This License applies to any program or other work which contains a notice
+ placed by the copyright holder saying it may be distributed under the
+ terms of this General Public License. The "Program", below, refers to any
+ such program or work, and a "work based on the Program" means either the
+ Program or any derivative work under copyright law: that is to say, a
+ work containing the Program or a portion of it, either verbatim or with
+ modifications and/or translated into another language. (Hereinafter,
+ translation is included without limitation in the term "modification".)
+ Each licensee is addressed as "you".
+
+ Activities other than copying, distribution and modification are not
+ covered by this License; they are outside its scope. The act of running
+ the Program is not restricted, and the output from the Program is covered
+ only if its contents constitute a work based on the Program (independent
+ of having been made by running the Program). Whether that is true depends
+ on what the Program does.
+
+1. You may copy and distribute verbatim copies of the Program's source code
+ as you receive it, in any medium, provided that you conspicuously and
+ appropriately publish on each copy an appropriate copyright notice and
+ disclaimer of warranty; keep intact all the notices that refer to this
+ License and to the absence of any warranty; and give any other recipients
+ of the Program a copy of this License along with the Program.
+
+ You may charge a fee for the physical act of transferring a copy, and you
+ may at your option offer warranty protection in exchange for a fee.
+
+2. You may modify your copy or copies of the Program or any portion of it,
+ thus forming a work based on the Program, and copy and distribute such
+ modifications or work under the terms of Section 1 above, provided that
+ you also meet all of these conditions:
+
+ * a) You must cause the modified files to carry prominent notices stating
+ that you changed the files and the date of any change.
+
+ * b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any part
+ thereof, to be licensed as a whole at no charge to all third parties
+ under the terms of this License.
+
+ * c) If the modified program normally reads commands interactively when
+ run, you must cause it, when started running for such interactive
+ use in the most ordinary way, to print or display an announcement
+ including an appropriate copyright notice and a notice that there is
+ no warranty (or else, saying that you provide a warranty) and that
+ users may redistribute the program under these conditions, and
+ telling the user how to view a copy of this License. (Exception: if
+ the Program itself is interactive but does not normally print such
+ an announcement, your work based on the Program is not required to
+ print an announcement.)
+
+ These requirements apply to the modified work as a whole. If identifiable
+ sections of that work are not derived from the Program, and can be
+ reasonably considered independent and separate works in themselves, then
+ this License, and its terms, do not apply to those sections when you
+ distribute them as separate works. But when you distribute the same
+ sections as part of a whole which is a work based on the Program, the
+ distribution of the whole must be on the terms of this License, whose
+ permissions for other licensees extend to the entire whole, and thus to
+ each and every part regardless of who wrote it.
+
+ Thus, it is not the intent of this section to claim rights or contest
+ your rights to work written entirely by you; rather, the intent is to
+ exercise the right to control the distribution of derivative or
+ collective works based on the Program.
+
+ In addition, mere aggregation of another work not based on the Program
+ with the Program (or with a work based on the Program) on a volume of a
+ storage or distribution medium does not bring the other work under the
+ scope of this License.
+
+3. You may copy and distribute the Program (or a work based on it, under
+ Section 2) in object code or executable form under the terms of Sections
+ 1 and 2 above provided that you also do one of the following:
+
+ * a) Accompany it with the complete corresponding machine-readable source
+ code, which must be distributed under the terms of Sections 1 and 2
+ above on a medium customarily used for software interchange; or,
+
+ * b) Accompany it with a written offer, valid for at least three years,
+ to give any third party, for a charge no more than your cost of
+ physically performing source distribution, a complete machine-
+ readable copy of the corresponding source code, to be distributed
+ under the terms of Sections 1 and 2 above on a medium customarily
+ used for software interchange; or,
+
+ * c) Accompany it with the information you received as to the offer to
+ distribute corresponding source code. (This alternative is allowed
+ only for noncommercial distribution and only if you received the
+ program in object code or executable form with such an offer, in
+ accord with Subsection b above.)
+
+ The source code for a work means the preferred form of the work for
+ making modifications to it. For an executable work, complete source code
+ means all the source code for all modules it contains, plus any
+ associated interface definition files, plus the scripts used to control
+ compilation and installation of the executable. However, as a special
+ exception, the source code distributed need not include anything that is
+ normally distributed (in either source or binary form) with the major
+ components (compiler, kernel, and so on) of the operating system on which
+ the executable runs, unless that component itself accompanies the
+ executable.
+
+ If distribution of executable or object code is made by offering access
+ to copy from a designated place, then offering equivalent access to copy
+ the source code from the same place counts as distribution of the source
+ code, even though third parties are not compelled to copy the source
+ along with the object code.
+
+4. You may not copy, modify, sublicense, or distribute the Program except as
+ expressly provided under this License. Any attempt otherwise to copy,
+ modify, sublicense or distribute the Program is void, and will
+ automatically terminate your rights under this License. However, parties
+ who have received copies, or rights, from you under this License will not
+ have their licenses terminated so long as such parties remain in full
+ compliance.
+
+5. You are not required to accept this License, since you have not signed
+ it. However, nothing else grants you permission to modify or distribute
+ the Program or its derivative works. These actions are prohibited by law
+ if you do not accept this License. Therefore, by modifying or
+ distributing the Program (or any work based on the Program), you
+ indicate your acceptance of this License to do so, and all its terms and
+ conditions for copying, distributing or modifying the Program or works
+ based on it.
+
+6. Each time you redistribute the Program (or any work based on the
+ Program), the recipient automatically receives a license from the
+ original licensor to copy, distribute or modify the Program subject to
+ these terms and conditions. You may not impose any further restrictions
+ on the recipients' exercise of the rights granted herein. You are not
+ responsible for enforcing compliance by third parties to this License.
+
+7. If, as a consequence of a court judgment or allegation of patent
+ infringement or for any other reason (not limited to patent issues),
+ conditions are imposed on you (whether by court order, agreement or
+ otherwise) that contradict the conditions of this License, they do not
+ excuse you from the conditions of this License. If you cannot distribute
+ so as to satisfy simultaneously your obligations under this License and
+ any other pertinent obligations, then as a consequence you may not
+ distribute the Program at all. For example, if a patent license would
+ not permit royalty-free redistribution of the Program by all those who
+ receive copies directly or indirectly through you, then the only way you
+ could satisfy both it and this License would be to refrain entirely from
+ distribution of the Program.
+
+ If any portion of this section is held invalid or unenforceable under any
+ particular circumstance, the balance of the section is intended to apply
+ and the section as a whole is intended to apply in other circumstances.
+
+ It is not the purpose of this section to induce you to infringe any
+ patents or other property right claims or to contest validity of any
+ such claims; this section has the sole purpose of protecting the
+ integrity of the free software distribution system, which is implemented
+ by public license practices. Many people have made generous contributions
+ to the wide range of software distributed through that system in
+ reliance on consistent application of that system; it is up to the
+ author/donor to decide if he or she is willing to distribute software
+ through any other system and a licensee cannot impose that choice.
+
+ This section is intended to make thoroughly clear what is believed to be
+ a consequence of the rest of this License.
+
+8. If the distribution and/or use of the Program is restricted in certain
+ countries either by patents or by copyrighted interfaces, the original
+ copyright holder who places the Program under this License may add an
+ explicit geographical distribution limitation excluding those countries,
+ so that distribution is permitted only in or among countries not thus
+ excluded. In such case, this License incorporates the limitation as if
+ written in the body of this License.
+
+9. The Free Software Foundation may publish revised and/or new versions of
+ the General Public License from time to time. Such new versions will be
+ similar in spirit to the present version, but may differ in detail to
+ address new problems or concerns.
+
+ Each version is given a distinguishing version number. If the Program
+ specifies a version number of this License which applies to it and "any
+ later version", you have the option of following the terms and
+ conditions either of that version or of any later version published by
+ the Free Software Foundation. If the Program does not specify a version
+ number of this License, you may choose any version ever published by the
+ Free Software Foundation.
+
+10. If you wish to incorporate parts of the Program into other free programs
+ whose distribution conditions are different, write to the author to ask
+ for permission. For software which is copyrighted by the Free Software
+ Foundation, write to the Free Software Foundation; we sometimes make
+ exceptions for this. Our decision will be guided by the two goals of
+ preserving the free status of all derivatives of our free software and
+ of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+ FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+ OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+ PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER
+ EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE
+ ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH
+ YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL
+ NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+ WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+ REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR
+ DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL
+ DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM
+ (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED
+ INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF
+ THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR
+ OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+
+END OF TERMS AND CONDITIONS
+
+How to Apply These Terms to Your New Programs
+
+If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it free
+software which everyone can redistribute and change under these terms.
+
+To do so, attach the following notices to the program. It is safest to
+attach them to the start of each source file to most effectively convey the
+exclusion of warranty; and each file should have at least the "copyright"
+line and a pointer to where the full notice is found.
+
+one line to give the program's name and an idea of what it does.
+Copyright (C) yyyy name of author
+
+This program is free software; you can redistribute it and/or modify it
+under the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 2 of the License, or (at your option)
+any later version.
+
+This program is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+more details.
+
+You should have received a copy of the GNU General Public License along with
+this program; if not, write to the Free Software Foundation, Inc., 59
+Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this when
+it starts in an interactive mode:
+
+Gnomovision version 69, Copyright (C) year name of author Gnomovision comes
+with ABSOLUTELY NO WARRANTY; for details type 'show w'. This is free
+software, and you are welcome to redistribute it under certain conditions;
+type 'show c' for details.
+
+The hypothetical commands 'show w' and 'show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may be
+called something other than 'show w' and 'show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+'Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+signature of Ty Coon, 1 April 1989
+Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Library General Public
+License instead of this License.
diff --git a/drivers/staging/rtl8192e/rtl8192e/Kconfig b/drivers/staging/rtl8192e/rtl8192e/Kconfig
new file mode 100644
index 000000000..282e293da
--- /dev/null
+++ b/drivers/staging/rtl8192e/rtl8192e/Kconfig
@@ -0,0 +1,9 @@
+config RTL8192E
+ tristate "RealTek RTL8192E Wireless LAN NIC driver"
+ depends on PCI && WLAN && RTLLIB
+ depends on m
+ select WIRELESS_EXT
+ select WEXT_PRIV
+ select CRYPTO
+ select FW_LOADER
+ ---help---
diff --git a/drivers/staging/rtl8192e/rtl8192e/Makefile b/drivers/staging/rtl8192e/rtl8192e/Makefile
new file mode 100644
index 000000000..a2c4fb4ba
--- /dev/null
+++ b/drivers/staging/rtl8192e/rtl8192e/Makefile
@@ -0,0 +1,20 @@
+r8192e_pci-objs := \
+ r8192E_dev.o \
+ r8192E_phy.o \
+ r8192E_firmware.o \
+ r8192E_cmdpkt.o \
+ r8192E_hwimg.o \
+ r8190P_rtl8256.o \
+ rtl_cam.o \
+ rtl_core.o \
+ rtl_dm.o \
+ rtl_eeprom.o \
+ rtl_ethtool.o \
+ rtl_pci.o \
+ rtl_pm.o \
+ rtl_ps.o \
+ rtl_wx.o \
+
+obj-$(CONFIG_RTL8192E) += r8192e_pci.o
+
+ccflags-y += -D__CHECK_ENDIAN__
diff --git a/drivers/staging/rtl8192e/rtl8192e/r8190P_def.h b/drivers/staging/rtl8192e/rtl8192e/r8190P_def.h
new file mode 100644
index 000000000..b7bb71fa9
--- /dev/null
+++ b/drivers/staging/rtl8192e/rtl8192e/r8190P_def.h
@@ -0,0 +1,410 @@
+/******************************************************************************
+ * Copyright(c) 2008 - 2010 Realtek Corporation. All rights reserved.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ * The full GNU General Public License is included in this distribution in the
+ * file called LICENSE.
+ *
+ * Contact Information:
+ * wlanfae <wlanfae@realtek.com>
+******************************************************************************/
+
+
+#ifndef R8190P_DEF_H
+#define R8190P_DEF_H
+
+#include <linux/types.h>
+
+#define MAX_SILENT_RESET_RX_SLOT_NUM 10
+
+#define RX_MPDU_QUEUE 0
+#define RX_CMD_QUEUE 1
+
+
+enum rtl819x_loopback {
+ RTL819X_NO_LOOPBACK = 0,
+ RTL819X_MAC_LOOPBACK = 1,
+ RTL819X_DMA_LOOPBACK = 2,
+ RTL819X_CCK_LOOPBACK = 3,
+};
+
+
+#define RESET_DELAY_8185 20
+
+#define RT_IBSS_INT_MASKS (IMR_BcnInt | IMR_BcnInt | IMR_TBDOK | IMR_TBDER)
+
+#define DESC90_RATE1M 0x00
+#define DESC90_RATE2M 0x01
+#define DESC90_RATE5_5M 0x02
+#define DESC90_RATE11M 0x03
+#define DESC90_RATE6M 0x04
+#define DESC90_RATE9M 0x05
+#define DESC90_RATE12M 0x06
+#define DESC90_RATE18M 0x07
+#define DESC90_RATE24M 0x08
+#define DESC90_RATE36M 0x09
+#define DESC90_RATE48M 0x0a
+#define DESC90_RATE54M 0x0b
+#define DESC90_RATEMCS0 0x00
+#define DESC90_RATEMCS1 0x01
+#define DESC90_RATEMCS2 0x02
+#define DESC90_RATEMCS3 0x03
+#define DESC90_RATEMCS4 0x04
+#define DESC90_RATEMCS5 0x05
+#define DESC90_RATEMCS6 0x06
+#define DESC90_RATEMCS7 0x07
+#define DESC90_RATEMCS8 0x08
+#define DESC90_RATEMCS9 0x09
+#define DESC90_RATEMCS10 0x0a
+#define DESC90_RATEMCS11 0x0b
+#define DESC90_RATEMCS12 0x0c
+#define DESC90_RATEMCS13 0x0d
+#define DESC90_RATEMCS14 0x0e
+#define DESC90_RATEMCS15 0x0f
+#define DESC90_RATEMCS32 0x20
+
+#define SHORT_SLOT_TIME 9
+#define NON_SHORT_SLOT_TIME 20
+
+
+#define MAX_LINES_HWCONFIG_TXT 1000
+#define MAX_BYTES_LINE_HWCONFIG_TXT 128
+
+#define SW_THREE_WIRE 0
+#define HW_THREE_WIRE 2
+
+#define BT_DEMO_BOARD 0
+#define BT_QA_BOARD 1
+#define BT_FPGA 2
+
+#define RX_SMOOTH 20
+
+#define QSLT_BK 0x1
+#define QSLT_BE 0x0
+#define QSLT_VI 0x4
+#define QSLT_VO 0x6
+#define QSLT_BEACON 0x10
+#define QSLT_HIGH 0x11
+#define QSLT_MGNT 0x12
+#define QSLT_CMD 0x13
+
+#define NUM_OF_FIRMWARE_QUEUE 10
+#define NUM_OF_PAGES_IN_FW 0x100
+#define NUM_OF_PAGE_IN_FW_QUEUE_BK 0x007
+#define NUM_OF_PAGE_IN_FW_QUEUE_BE 0x0aa
+#define NUM_OF_PAGE_IN_FW_QUEUE_VI 0x024
+#define NUM_OF_PAGE_IN_FW_QUEUE_VO 0x007
+#define NUM_OF_PAGE_IN_FW_QUEUE_HCCA 0
+#define NUM_OF_PAGE_IN_FW_QUEUE_CMD 0x2
+#define NUM_OF_PAGE_IN_FW_QUEUE_MGNT 0x10
+#define NUM_OF_PAGE_IN_FW_QUEUE_HIGH 0
+#define NUM_OF_PAGE_IN_FW_QUEUE_BCN 0x4
+#define NUM_OF_PAGE_IN_FW_QUEUE_PUB 0xd
+
+#define NUM_OF_PAGE_IN_FW_QUEUE_BK_DTM 0x026
+#define NUM_OF_PAGE_IN_FW_QUEUE_BE_DTM 0x048
+#define NUM_OF_PAGE_IN_FW_QUEUE_VI_DTM 0x048
+#define NUM_OF_PAGE_IN_FW_QUEUE_VO_DTM 0x026
+#define NUM_OF_PAGE_IN_FW_QUEUE_PUB_DTM 0x00
+
+#define APPLIED_RESERVED_QUEUE_IN_FW 0x80000000
+#define RSVD_FW_QUEUE_PAGE_BK_SHIFT 0x00
+#define RSVD_FW_QUEUE_PAGE_BE_SHIFT 0x08
+#define RSVD_FW_QUEUE_PAGE_VI_SHIFT 0x10
+#define RSVD_FW_QUEUE_PAGE_VO_SHIFT 0x18
+#define RSVD_FW_QUEUE_PAGE_MGNT_SHIFT 0x10
+#define RSVD_FW_QUEUE_PAGE_BCN_SHIFT 0x00
+#define RSVD_FW_QUEUE_PAGE_PUB_SHIFT 0x08
+
+#define HAL_PRIME_CHNL_OFFSET_DONT_CARE 0
+#define HAL_PRIME_CHNL_OFFSET_LOWER 1
+#define HAL_PRIME_CHNL_OFFSET_UPPER 2
+
+
+enum version_8190_loopback {
+ VERSION_8190_BD = 0x3,
+ VERSION_8190_BE
+};
+
+#define IC_VersionCut_C 0x2
+#define IC_VersionCut_D 0x3
+#define IC_VersionCut_E 0x4
+
+enum rf_optype {
+ RF_OP_By_SW_3wire = 0,
+ RF_OP_By_FW,
+ RF_OP_MAX
+};
+
+
+enum power_save_mode {
+ POWER_SAVE_MODE_ACTIVE,
+ POWER_SAVE_MODE_SAVE,
+};
+
+enum interface_select_8190pci {
+ INTF_SEL1_MINICARD = 0,
+ INTF_SEL0_PCIE = 1,
+ INTF_SEL2_RSV = 2,
+ INTF_SEL3_RSV = 3,
+};
+
+struct bb_reg_definition {
+ u32 rfintfs;
+ u32 rfintfi;
+ u32 rfintfo;
+ u32 rfintfe;
+ u32 rf3wireOffset;
+ u32 rfLSSI_Select;
+ u32 rfTxGainStage;
+ u32 rfHSSIPara1;
+ u32 rfHSSIPara2;
+ u32 rfSwitchControl;
+ u32 rfAGCControl1;
+ u32 rfAGCControl2;
+ u32 rfRxIQImbalance;
+ u32 rfRxAFE;
+ u32 rfTxIQImbalance;
+ u32 rfTxAFE;
+ u32 rfLSSIReadBack;
+ u32 rfLSSIReadBackPi;
+};
+
+struct tx_fwinfo {
+ u8 TxRate:7;
+ u8 CtsEnable:1;
+ u8 RtsRate:7;
+ u8 RtsEnable:1;
+ u8 TxHT:1;
+ u8 Short:1;
+ u8 TxBandwidth:1;
+ u8 TxSubCarrier:2;
+ u8 STBC:2;
+ u8 AllowAggregation:1;
+ u8 RtsHT:1;
+ u8 RtsShort:1;
+ u8 RtsBandwidth:1;
+ u8 RtsSubcarrier:2;
+ u8 RtsSTBC:2;
+ u8 EnableCPUDur:1;
+
+ u32 RxMF:2;
+ u32 RxAMD:3;
+ u32 Reserved1:3;
+ u32 TxAGCOffset:4;
+ u32 TxAGCSign:1;
+ u32 Tx_INFO_RSVD:6;
+ u32 PacketID:13;
+};
+
+struct tx_fwinfo_8190pci {
+ u8 TxRate:7;
+ u8 CtsEnable:1;
+ u8 RtsRate:7;
+ u8 RtsEnable:1;
+ u8 TxHT:1;
+ u8 Short:1;
+ u8 TxBandwidth:1;
+ u8 TxSubCarrier:2;
+ u8 STBC:2;
+ u8 AllowAggregation:1;
+ u8 RtsHT:1;
+ u8 RtsShort:1;
+ u8 RtsBandwidth:1;
+ u8 RtsSubcarrier:2;
+ u8 RtsSTBC:2;
+ u8 EnableCPUDur:1;
+
+ u32 RxMF:2;
+ u32 RxAMD:3;
+ u32 TxPerPktInfoFeedback:1;
+ u32 Reserved1:2;
+ u32 TxAGCOffset:4;
+ u32 TxAGCSign:1;
+ u32 RAW_TXD:1;
+ u32 Retry_Limit:4;
+ u32 Reserved2:1;
+ u32 PacketID:13;
+
+
+};
+
+
+#define TX_DESC_SIZE 32
+
+#define TX_DESC_CMD_SIZE 32
+
+
+#define TX_STATUS_DESC_SIZE 32
+
+#define TX_FWINFO_SIZE 8
+
+
+#define RX_DESC_SIZE 16
+
+#define RX_STATUS_DESC_SIZE 16
+
+#define RX_DRIVER_INFO_SIZE 8
+
+struct log_int_8190 {
+ u32 nIMR_COMDOK;
+ u32 nIMR_MGNTDOK;
+ u32 nIMR_HIGH;
+ u32 nIMR_VODOK;
+ u32 nIMR_VIDOK;
+ u32 nIMR_BEDOK;
+ u32 nIMR_BKDOK;
+ u32 nIMR_ROK;
+ u32 nIMR_RCOK;
+ u32 nIMR_TBDOK;
+ u32 nIMR_BDOK;
+ u32 nIMR_RXFOVW;
+};
+
+struct phy_ofdm_rx_status_rxsc_sgien_exintfflag {
+ u8 reserved:4;
+ u8 rxsc:2;
+ u8 sgi_en:1;
+ u8 ex_intf_flag:1;
+};
+
+struct phy_sts_ofdm_819xpci {
+ u8 trsw_gain_X[4];
+ u8 pwdb_all;
+ u8 cfosho_X[4];
+ u8 cfotail_X[4];
+ u8 rxevm_X[2];
+ u8 rxsnr_X[4];
+ u8 pdsnr_X[2];
+ u8 csi_current_X[2];
+ u8 csi_target_X[2];
+ u8 sigevm;
+ u8 max_ex_pwr;
+ u8 sgi_en;
+ u8 rxsc_sgien_exflg;
+};
+
+struct phy_sts_cck_819xpci {
+ u8 adc_pwdb_X[4];
+ u8 sq_rpt;
+ u8 cck_agc_rpt;
+};
+
+
+#define PHY_RSSI_SLID_WIN_MAX 100
+#define PHY_Beacon_RSSI_SLID_WIN_MAX 10
+
+struct tx_desc {
+ u16 PktSize;
+ u8 Offset;
+ u8 Reserved1:3;
+ u8 CmdInit:1;
+ u8 LastSeg:1;
+ u8 FirstSeg:1;
+ u8 LINIP:1;
+ u8 OWN:1;
+
+ u8 TxFWInfoSize;
+ u8 RATid:3;
+ u8 DISFB:1;
+ u8 USERATE:1;
+ u8 MOREFRAG:1;
+ u8 NoEnc:1;
+ u8 PIFS:1;
+ u8 QueueSelect:5;
+ u8 NoACM:1;
+ u8 Resv:2;
+ u8 SecCAMID:5;
+ u8 SecDescAssign:1;
+ u8 SecType:2;
+
+ u16 TxBufferSize;
+ u8 PktId:7;
+ u8 Resv1:1;
+ u8 Reserved2;
+
+ u32 TxBuffAddr;
+
+ u32 NextDescAddress;
+
+ u32 Reserved5;
+ u32 Reserved6;
+ u32 Reserved7;
+};
+
+
+struct tx_desc_cmd {
+ u16 PktSize;
+ u8 Reserved1;
+ u8 CmdType:3;
+ u8 CmdInit:1;
+ u8 LastSeg:1;
+ u8 FirstSeg:1;
+ u8 LINIP:1;
+ u8 OWN:1;
+
+ u16 ElementReport;
+ u16 Reserved2;
+
+ u16 TxBufferSize;
+ u16 Reserved3;
+
+ u32 TxBuffAddr;
+ u32 NextDescAddress;
+ u32 Reserved4;
+ u32 Reserved5;
+ u32 Reserved6;
+};
+
+struct rx_desc {
+ u16 Length:14;
+ u16 CRC32:1;
+ u16 ICV:1;
+ u8 RxDrvInfoSize;
+ u8 Shift:2;
+ u8 PHYStatus:1;
+ u8 SWDec:1;
+ u8 LastSeg:1;
+ u8 FirstSeg:1;
+ u8 EOR:1;
+ u8 OWN:1;
+
+ u32 Reserved2;
+
+ u32 Reserved3;
+
+ u32 BufferAddress;
+
+};
+
+
+struct rx_fwinfo {
+ u16 Reserved1:12;
+ u16 PartAggr:1;
+ u16 FirstAGGR:1;
+ u16 Reserved2:2;
+
+ u8 RxRate:7;
+ u8 RxHT:1;
+
+ u8 BW:1;
+ u8 SPLCP:1;
+ u8 Reserved3:2;
+ u8 PAM:1;
+ u8 Mcast:1;
+ u8 Bcast:1;
+ u8 Reserved4:1;
+
+ u32 TSFL;
+
+};
+
+#endif
diff --git a/drivers/staging/rtl8192e/rtl8192e/r8190P_rtl8256.c b/drivers/staging/rtl8192e/rtl8192e/r8190P_rtl8256.c
new file mode 100644
index 000000000..01d2201af
--- /dev/null
+++ b/drivers/staging/rtl8192e/rtl8192e/r8190P_rtl8256.c
@@ -0,0 +1,309 @@
+/******************************************************************************
+ * Copyright(c) 2008 - 2010 Realtek Corporation. All rights reserved.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ * The full GNU General Public License is included in this distribution in the
+ * file called LICENSE.
+ *
+ * Contact Information:
+ * wlanfae <wlanfae@realtek.com>
+******************************************************************************/
+
+#include "rtl_core.h"
+#include "r8192E_phyreg.h"
+#include "r8192E_phy.h"
+#include "r8190P_rtl8256.h"
+
+void PHY_SetRF8256Bandwidth(struct net_device *dev,
+ enum ht_channel_width Bandwidth)
+{
+ u8 eRFPath;
+ struct r8192_priv *priv = rtllib_priv(dev);
+
+ for (eRFPath = 0; eRFPath < priv->NumTotalRFPath; eRFPath++) {
+ if (!rtl8192_phy_CheckIsLegalRFPath(dev, eRFPath))
+ continue;
+
+ switch (Bandwidth) {
+ case HT_CHANNEL_WIDTH_20:
+ if (priv->card_8192_version == VERSION_8190_BD ||
+ priv->card_8192_version == VERSION_8190_BE) {
+ rtl8192_phy_SetRFReg(dev,
+ (enum rf90_radio_path)eRFPath,
+ 0x0b, bMask12Bits, 0x100);
+ rtl8192_phy_SetRFReg(dev,
+ (enum rf90_radio_path)eRFPath,
+ 0x2c, bMask12Bits, 0x3d7);
+ rtl8192_phy_SetRFReg(dev,
+ (enum rf90_radio_path)eRFPath,
+ 0x0e, bMask12Bits, 0x021);
+
+ } else {
+ RT_TRACE(COMP_ERR,
+ "PHY_SetRF8256Bandwidth(): unknown hardware version\n");
+ }
+
+ break;
+ case HT_CHANNEL_WIDTH_20_40:
+ if (priv->card_8192_version == VERSION_8190_BD ||
+ priv->card_8192_version == VERSION_8190_BE) {
+ rtl8192_phy_SetRFReg(dev,
+ (enum rf90_radio_path)eRFPath,
+ 0x0b, bMask12Bits, 0x300);
+ rtl8192_phy_SetRFReg(dev,
+ (enum rf90_radio_path)eRFPath,
+ 0x2c, bMask12Bits, 0x3ff);
+ rtl8192_phy_SetRFReg(dev,
+ (enum rf90_radio_path)eRFPath,
+ 0x0e, bMask12Bits, 0x0e1);
+
+ } else {
+ RT_TRACE(COMP_ERR,
+ "PHY_SetRF8256Bandwidth(): unknown hardware version\n");
+ }
+
+
+ break;
+ default:
+ RT_TRACE(COMP_ERR,
+ "PHY_SetRF8256Bandwidth(): unknown Bandwidth: %#X\n",
+ Bandwidth);
+ break;
+
+ }
+ }
+}
+
+bool PHY_RF8256_Config(struct net_device *dev)
+{
+ struct r8192_priv *priv = rtllib_priv(dev);
+
+ priv->NumTotalRFPath = RTL819X_TOTAL_RF_PATH;
+ return phy_RF8256_Config_ParaFile(dev);
+}
+
+bool phy_RF8256_Config_ParaFile(struct net_device *dev)
+{
+ u32 u4RegValue = 0;
+ u8 eRFPath;
+ bool rtStatus = true;
+ struct bb_reg_definition *pPhyReg;
+ struct r8192_priv *priv = rtllib_priv(dev);
+ u32 RegOffSetToBeCheck = 0x3;
+ u32 RegValueToBeCheck = 0x7f1;
+ u32 RF3_Final_Value = 0;
+ u8 ConstRetryTimes = 5, RetryTimes = 5;
+ u8 ret = 0;
+
+ for (eRFPath = (enum rf90_radio_path)RF90_PATH_A;
+ eRFPath < priv->NumTotalRFPath; eRFPath++) {
+ if (!rtl8192_phy_CheckIsLegalRFPath(dev, eRFPath))
+ continue;
+
+ pPhyReg = &priv->PHYRegDef[eRFPath];
+
+
+ switch (eRFPath) {
+ case RF90_PATH_A:
+ case RF90_PATH_C:
+ u4RegValue = rtl8192_QueryBBReg(dev, pPhyReg->rfintfs,
+ bRFSI_RFENV);
+ break;
+ case RF90_PATH_B:
+ case RF90_PATH_D:
+ u4RegValue = rtl8192_QueryBBReg(dev, pPhyReg->rfintfs,
+ bRFSI_RFENV<<16);
+ break;
+ }
+
+ rtl8192_setBBreg(dev, pPhyReg->rfintfe, bRFSI_RFENV<<16, 0x1);
+
+ rtl8192_setBBreg(dev, pPhyReg->rfintfo, bRFSI_RFENV, 0x1);
+
+ rtl8192_setBBreg(dev, pPhyReg->rfHSSIPara2,
+ b3WireAddressLength, 0x0);
+ rtl8192_setBBreg(dev, pPhyReg->rfHSSIPara2,
+ b3WireDataLength, 0x0);
+
+ rtl8192_phy_SetRFReg(dev, (enum rf90_radio_path) eRFPath, 0x0,
+ bMask12Bits, 0xbf);
+
+ rtStatus = rtl8192_phy_checkBBAndRF(dev, HW90_BLOCK_RF,
+ (enum rf90_radio_path)eRFPath);
+ if (!rtStatus) {
+ RT_TRACE(COMP_ERR,
+ "PHY_RF8256_Config():Check Radio[%d] Fail!!\n",
+ eRFPath);
+ goto phy_RF8256_Config_ParaFile_Fail;
+ }
+
+ RetryTimes = ConstRetryTimes;
+ RF3_Final_Value = 0;
+ switch (eRFPath) {
+ case RF90_PATH_A:
+ while (RF3_Final_Value != RegValueToBeCheck &&
+ RetryTimes != 0) {
+ ret = rtl8192_phy_ConfigRFWithHeaderFile(dev,
+ (enum rf90_radio_path)eRFPath);
+ RF3_Final_Value = rtl8192_phy_QueryRFReg(dev,
+ (enum rf90_radio_path)eRFPath,
+ RegOffSetToBeCheck,
+ bMask12Bits);
+ RT_TRACE(COMP_RF,
+ "RF %d %d register final value: %x\n",
+ eRFPath, RegOffSetToBeCheck,
+ RF3_Final_Value);
+ RetryTimes--;
+ }
+ break;
+ case RF90_PATH_B:
+ while (RF3_Final_Value != RegValueToBeCheck &&
+ RetryTimes != 0) {
+ ret = rtl8192_phy_ConfigRFWithHeaderFile(dev,
+ (enum rf90_radio_path)eRFPath);
+ RF3_Final_Value = rtl8192_phy_QueryRFReg(dev,
+ (enum rf90_radio_path)eRFPath,
+ RegOffSetToBeCheck,
+ bMask12Bits);
+ RT_TRACE(COMP_RF,
+ "RF %d %d register final value: %x\n",
+ eRFPath, RegOffSetToBeCheck,
+ RF3_Final_Value);
+ RetryTimes--;
+ }
+ break;
+ case RF90_PATH_C:
+ while (RF3_Final_Value != RegValueToBeCheck &&
+ RetryTimes != 0) {
+ ret = rtl8192_phy_ConfigRFWithHeaderFile(dev,
+ (enum rf90_radio_path)eRFPath);
+ RF3_Final_Value = rtl8192_phy_QueryRFReg(dev,
+ (enum rf90_radio_path)eRFPath,
+ RegOffSetToBeCheck,
+ bMask12Bits);
+ RT_TRACE(COMP_RF,
+ "RF %d %d register final value: %x\n",
+ eRFPath, RegOffSetToBeCheck,
+ RF3_Final_Value);
+ RetryTimes--;
+ }
+ break;
+ case RF90_PATH_D:
+ while (RF3_Final_Value != RegValueToBeCheck &&
+ RetryTimes != 0) {
+ ret = rtl8192_phy_ConfigRFWithHeaderFile(dev,
+ (enum rf90_radio_path)eRFPath);
+ RF3_Final_Value = rtl8192_phy_QueryRFReg(dev,
+ (enum rf90_radio_path)eRFPath,
+ RegOffSetToBeCheck, bMask12Bits);
+ RT_TRACE(COMP_RF,
+ "RF %d %d register final value: %x\n",
+ eRFPath, RegOffSetToBeCheck,
+ RF3_Final_Value);
+ RetryTimes--;
+ }
+ break;
+ }
+
+ switch (eRFPath) {
+ case RF90_PATH_A:
+ case RF90_PATH_C:
+ rtl8192_setBBreg(dev, pPhyReg->rfintfs, bRFSI_RFENV,
+ u4RegValue);
+ break;
+ case RF90_PATH_B:
+ case RF90_PATH_D:
+ rtl8192_setBBreg(dev, pPhyReg->rfintfs, bRFSI_RFENV<<16,
+ u4RegValue);
+ break;
+ }
+
+ if (ret) {
+ RT_TRACE(COMP_ERR,
+ "phy_RF8256_Config_ParaFile():Radio[%d] Fail!!",
+ eRFPath);
+ goto phy_RF8256_Config_ParaFile_Fail;
+ }
+
+ }
+
+ RT_TRACE(COMP_PHY, "PHY Initialization Success\n");
+ return true;
+
+phy_RF8256_Config_ParaFile_Fail:
+ RT_TRACE(COMP_ERR, "PHY Initialization failed\n");
+ return false;
+}
+
+void PHY_SetRF8256CCKTxPower(struct net_device *dev, u8 powerlevel)
+{
+ u32 TxAGC = 0;
+ struct r8192_priv *priv = rtllib_priv(dev);
+
+ TxAGC = powerlevel;
+ if (priv->bDynamicTxLowPower) {
+ if (priv->CustomerID == RT_CID_819x_Netcore)
+ TxAGC = 0x22;
+ else
+ TxAGC += priv->CckPwEnl;
+ }
+ if (TxAGC > 0x24)
+ TxAGC = 0x24;
+ rtl8192_setBBreg(dev, rTxAGC_CCK_Mcs32, bTxAGCRateCCK, TxAGC);
+}
+
+
+void PHY_SetRF8256OFDMTxPower(struct net_device *dev, u8 powerlevel)
+{
+ struct r8192_priv *priv = rtllib_priv(dev);
+ u32 writeVal, powerBase0, powerBase1, writeVal_tmp;
+ u8 index = 0;
+ u16 RegOffset[6] = {0xe00, 0xe04, 0xe10, 0xe14, 0xe18, 0xe1c};
+ u8 byte0, byte1, byte2, byte3;
+
+ powerBase0 = powerlevel + priv->LegacyHTTxPowerDiff;
+ powerBase0 = (powerBase0 << 24) | (powerBase0 << 16) |
+ (powerBase0 << 8) | powerBase0;
+ powerBase1 = powerlevel;
+ powerBase1 = (powerBase1 << 24) | (powerBase1 << 16) |
+ (powerBase1 << 8) | powerBase1;
+
+ for (index = 0; index < 6; index++) {
+ writeVal = (u32)(priv->MCSTxPowerLevelOriginalOffset[index] +
+ ((index < 2) ? powerBase0 : powerBase1));
+ byte0 = (u8)(writeVal & 0x7f);
+ byte1 = (u8)((writeVal & 0x7f00)>>8);
+ byte2 = (u8)((writeVal & 0x7f0000)>>16);
+ byte3 = (u8)((writeVal & 0x7f000000)>>24);
+ if (byte0 > 0x24)
+ byte0 = 0x24;
+ if (byte1 > 0x24)
+ byte1 = 0x24;
+ if (byte2 > 0x24)
+ byte2 = 0x24;
+ if (byte3 > 0x24)
+ byte3 = 0x24;
+
+ if (index == 3) {
+ writeVal_tmp = (byte3 << 24) | (byte2 << 16) |
+ (byte1 << 8) | byte0;
+ priv->Pwr_Track = writeVal_tmp;
+ }
+
+ if (priv->bDynamicTxHighPower)
+ writeVal = 0x03030303;
+ else
+ writeVal = (byte3 << 24) | (byte2 << 16) |
+ (byte1 << 8) | byte0;
+ rtl8192_setBBreg(dev, RegOffset[index], 0x7f7f7f7f, writeVal);
+ }
+
+}
diff --git a/drivers/staging/rtl8192e/rtl8192e/r8190P_rtl8256.h b/drivers/staging/rtl8192e/rtl8192e/r8190P_rtl8256.h
new file mode 100644
index 000000000..64e831d2f
--- /dev/null
+++ b/drivers/staging/rtl8192e/rtl8192e/r8190P_rtl8256.h
@@ -0,0 +1,31 @@
+/******************************************************************************
+ * Copyright(c) 2008 - 2010 Realtek Corporation. All rights reserved.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ * The full GNU General Public License is included in this distribution in the
+ * file called LICENSE.
+ *
+ * Contact Information:
+ * wlanfae <wlanfae@realtek.com>
+******************************************************************************/
+
+#ifndef RTL8225H
+#define RTL8225H
+
+#define RTL819X_TOTAL_RF_PATH 2
+extern void PHY_SetRF8256Bandwidth(struct net_device *dev,
+ enum ht_channel_width Bandwidth);
+extern bool PHY_RF8256_Config(struct net_device *dev);
+extern bool phy_RF8256_Config_ParaFile(struct net_device *dev);
+extern void PHY_SetRF8256CCKTxPower(struct net_device *dev, u8 powerlevel);
+extern void PHY_SetRF8256OFDMTxPower(struct net_device *dev, u8 powerlevel);
+
+#endif
diff --git a/drivers/staging/rtl8192e/rtl8192e/r8192E_cmdpkt.c b/drivers/staging/rtl8192e/rtl8192e/r8192E_cmdpkt.c
new file mode 100644
index 000000000..ecdd2e5c6
--- /dev/null
+++ b/drivers/staging/rtl8192e/rtl8192e/r8192E_cmdpkt.c
@@ -0,0 +1,380 @@
+/******************************************************************************
+ * Copyright(c) 2008 - 2010 Realtek Corporation. All rights reserved.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ * The full GNU General Public License is included in this distribution in the
+ * file called LICENSE.
+ *
+ * Contact Information:
+ * wlanfae <wlanfae@realtek.com>
+******************************************************************************/
+
+#include "rtl_core.h"
+#include "r8192E_hw.h"
+#include "r8192E_cmdpkt.h"
+
+bool cmpk_message_handle_tx(
+ struct net_device *dev,
+ u8 *code_virtual_address,
+ u32 packettype,
+ u32 buffer_len)
+{
+
+ bool rt_status = true;
+ struct r8192_priv *priv = rtllib_priv(dev);
+ u16 frag_threshold;
+ u16 frag_length = 0, frag_offset = 0;
+ struct rt_firmware *pfirmware = priv->pFirmware;
+ struct sk_buff *skb;
+ unsigned char *seg_ptr;
+ struct cb_desc *tcb_desc;
+ u8 bLastIniPkt;
+
+ struct tx_fwinfo_8190pci *pTxFwInfo = NULL;
+
+ RT_TRACE(COMP_CMDPKT, "%s(),buffer_len is %d\n", __func__, buffer_len);
+ firmware_init_param(dev);
+ frag_threshold = pfirmware->cmdpacket_frag_thresold;
+
+ do {
+ if ((buffer_len - frag_offset) > frag_threshold) {
+ frag_length = frag_threshold;
+ bLastIniPkt = 0;
+
+ } else {
+ frag_length = (u16)(buffer_len - frag_offset);
+ bLastIniPkt = 1;
+ }
+
+ skb = dev_alloc_skb(frag_length +
+ priv->rtllib->tx_headroom + 4);
+
+ if (skb == NULL) {
+ rt_status = false;
+ goto Failed;
+ }
+
+ memcpy((unsigned char *)(skb->cb), &dev, sizeof(dev));
+ tcb_desc = (struct cb_desc *)(skb->cb + MAX_DEV_ADDR_SIZE);
+ tcb_desc->queue_index = TXCMD_QUEUE;
+ tcb_desc->bCmdOrInit = DESC_PACKET_TYPE_NORMAL;
+ tcb_desc->bLastIniPkt = bLastIniPkt;
+ tcb_desc->pkt_size = frag_length;
+
+ seg_ptr = skb_put(skb, priv->rtllib->tx_headroom);
+ pTxFwInfo = (struct tx_fwinfo_8190pci *)seg_ptr;
+ memset(pTxFwInfo, 0, sizeof(struct tx_fwinfo_8190pci));
+ memset(pTxFwInfo, 0x12, 8);
+
+ seg_ptr = skb_put(skb, frag_length);
+ memcpy(seg_ptr, code_virtual_address, (u32)frag_length);
+
+ priv->rtllib->softmac_hard_start_xmit(skb, dev);
+
+ code_virtual_address += frag_length;
+ frag_offset += frag_length;
+
+ } while (frag_offset < buffer_len);
+
+ write_nic_byte(dev, TPPoll, TPPoll_CQ);
+Failed:
+ return rt_status;
+}
+
+static void
+cmpk_count_txstatistic(
+ struct net_device *dev,
+ struct cmpk_txfb *pstx_fb)
+{
+ struct r8192_priv *priv = rtllib_priv(dev);
+#ifdef ENABLE_PS
+ enum rt_rf_power_state rtState;
+
+ pAdapter->HalFunc.GetHwRegHandler(pAdapter, HW_VAR_RF_STATE,
+ (pu1Byte)(&rtState));
+
+ if (rtState == eRfOff)
+ return;
+#endif
+
+ if (pstx_fb->tok) {
+ priv->stats.txfeedbackok++;
+ priv->stats.txoktotal++;
+ priv->stats.txokbytestotal += pstx_fb->pkt_length;
+ priv->stats.txokinperiod++;
+
+ if (pstx_fb->pkt_type == PACKET_MULTICAST) {
+ priv->stats.txmulticast++;
+ priv->stats.txbytesmulticast += pstx_fb->pkt_length;
+ } else if (pstx_fb->pkt_type == PACKET_BROADCAST) {
+ priv->stats.txbroadcast++;
+ priv->stats.txbytesbroadcast += pstx_fb->pkt_length;
+ } else {
+ priv->stats.txunicast++;
+ priv->stats.txbytesunicast += pstx_fb->pkt_length;
+ }
+ } else {
+ priv->stats.txfeedbackfail++;
+ priv->stats.txerrtotal++;
+ priv->stats.txerrbytestotal += pstx_fb->pkt_length;
+
+ if (pstx_fb->pkt_type == PACKET_MULTICAST)
+ priv->stats.txerrmulticast++;
+ else if (pstx_fb->pkt_type == PACKET_BROADCAST)
+ priv->stats.txerrbroadcast++;
+ else
+ priv->stats.txerrunicast++;
+ }
+
+ priv->stats.txretrycount += pstx_fb->retry_cnt;
+ priv->stats.txfeedbackretry += pstx_fb->retry_cnt;
+}
+
+static void cmpk_handle_tx_feedback(struct net_device *dev, u8 *pmsg)
+{
+ struct r8192_priv *priv = rtllib_priv(dev);
+ struct cmpk_txfb rx_tx_fb;
+
+ priv->stats.txfeedback++;
+
+
+ memcpy((u8 *)&rx_tx_fb, pmsg, sizeof(struct cmpk_txfb));
+ cmpk_count_txstatistic(dev, &rx_tx_fb);
+}
+
+static void cmdpkt_beacontimerinterrupt_819xusb(struct net_device *dev)
+{
+ struct r8192_priv *priv = rtllib_priv(dev);
+
+ if ((priv->rtllib->current_network.mode == IEEE_A) ||
+ (priv->rtllib->current_network.mode == IEEE_N_5G) ||
+ ((priv->rtllib->current_network.mode == IEEE_N_24G) &&
+ (!priv->rtllib->pHTInfo->bCurSuppCCK)))
+ DMESG("send beacon frame tx rate is 6Mbpm\n");
+ else
+ DMESG("send beacon frame tx rate is 1Mbpm\n");
+}
+
+static void cmpk_handle_interrupt_status(struct net_device *dev, u8 *pmsg)
+{
+ struct cmpk_intr_sta rx_intr_status;
+ struct r8192_priv *priv = rtllib_priv(dev);
+
+ DMESG("---> cmpk_Handle_Interrupt_Status()\n");
+
+ rx_intr_status.length = pmsg[1];
+ if (rx_intr_status.length != (sizeof(struct cmpk_intr_sta) - 2)) {
+ DMESG("cmpk_Handle_Interrupt_Status: wrong length!\n");
+ return;
+ }
+
+ if (priv->rtllib->iw_mode == IW_MODE_ADHOC) {
+ rx_intr_status.interrupt_status = *((u32 *)(pmsg + 4));
+
+ DMESG("interrupt status = 0x%x\n",
+ rx_intr_status.interrupt_status);
+
+ if (rx_intr_status.interrupt_status & ISR_TxBcnOk) {
+ priv->rtllib->bibsscoordinator = true;
+ priv->stats.txbeaconokint++;
+ } else if (rx_intr_status.interrupt_status & ISR_TxBcnErr) {
+ priv->rtllib->bibsscoordinator = false;
+ priv->stats.txbeaconerr++;
+ }
+
+ if (rx_intr_status.interrupt_status & ISR_BcnTimerIntr)
+ cmdpkt_beacontimerinterrupt_819xusb(dev);
+ }
+
+ DMESG("<---- cmpk_handle_interrupt_status()\n");
+
+}
+
+static void cmpk_handle_query_config_rx(struct net_device *dev, u8 *pmsg)
+{
+ cmpk_query_cfg_t rx_query_cfg;
+
+
+ rx_query_cfg.cfg_action = (pmsg[4] & 0x80000000)>>31;
+ rx_query_cfg.cfg_type = (pmsg[4] & 0x60) >> 5;
+ rx_query_cfg.cfg_size = (pmsg[4] & 0x18) >> 3;
+ rx_query_cfg.cfg_page = (pmsg[6] & 0x0F) >> 0;
+ rx_query_cfg.cfg_offset = pmsg[7];
+ rx_query_cfg.value = (pmsg[8] << 24) | (pmsg[9] << 16) |
+ (pmsg[10] << 8) | (pmsg[11] << 0);
+ rx_query_cfg.mask = (pmsg[12] << 24) | (pmsg[13] << 16) |
+ (pmsg[14] << 8) | (pmsg[15] << 0);
+
+}
+
+static void cmpk_count_tx_status(struct net_device *dev,
+ struct cmpk_tx_status *pstx_status)
+{
+ struct r8192_priv *priv = rtllib_priv(dev);
+
+#ifdef ENABLE_PS
+
+ enum rt_rf_power_state rtstate;
+
+ pAdapter->HalFunc.GetHwRegHandler(pAdapter, HW_VAR_RF_STATE,
+ (pu1Byte)(&rtState));
+
+ if (rtState == eRfOff)
+ return;
+#endif
+
+ priv->stats.txfeedbackok += pstx_status->txok;
+ priv->stats.txoktotal += pstx_status->txok;
+
+ priv->stats.txfeedbackfail += pstx_status->txfail;
+ priv->stats.txerrtotal += pstx_status->txfail;
+
+ priv->stats.txretrycount += pstx_status->txretry;
+ priv->stats.txfeedbackretry += pstx_status->txretry;
+
+
+ priv->stats.txmulticast += pstx_status->txmcok;
+ priv->stats.txbroadcast += pstx_status->txbcok;
+ priv->stats.txunicast += pstx_status->txucok;
+
+ priv->stats.txerrmulticast += pstx_status->txmcfail;
+ priv->stats.txerrbroadcast += pstx_status->txbcfail;
+ priv->stats.txerrunicast += pstx_status->txucfail;
+
+ priv->stats.txbytesmulticast += pstx_status->txmclength;
+ priv->stats.txbytesbroadcast += pstx_status->txbclength;
+ priv->stats.txbytesunicast += pstx_status->txuclength;
+
+ priv->stats.last_packet_rate = pstx_status->rate;
+}
+
+static void cmpk_handle_tx_status(struct net_device *dev, u8 *pmsg)
+{
+ struct cmpk_tx_status rx_tx_sts;
+
+ memcpy((void *)&rx_tx_sts, (void *)pmsg, sizeof(struct cmpk_tx_status));
+ cmpk_count_tx_status(dev, &rx_tx_sts);
+}
+
+static void cmpk_handle_tx_rate_history(struct net_device *dev, u8 *pmsg)
+{
+ struct cmpk_tx_rahis *ptxrate;
+ u8 i, j;
+ u16 length = sizeof(struct cmpk_tx_rahis);
+ u32 *ptemp;
+ struct r8192_priv *priv = rtllib_priv(dev);
+
+#ifdef ENABLE_PS
+ pAdapter->HalFunc.GetHwRegHandler(pAdapter, HW_VAR_RF_STATE,
+ (pu1Byte)(&rtState));
+
+ if (rtState == eRfOff)
+ return;
+#endif
+
+ ptemp = (u32 *)pmsg;
+
+ for (i = 0; i < (length / 4); i++) {
+ u16 temp1, temp2;
+
+ temp1 = ptemp[i] & 0x0000FFFF;
+ temp2 = ptemp[i] >> 16;
+ ptemp[i] = (temp1 << 16) | temp2;
+ }
+
+ ptxrate = (struct cmpk_tx_rahis *)pmsg;
+
+ if (ptxrate == NULL)
+ return;
+
+ for (i = 0; i < 16; i++) {
+ if (i < 4)
+ priv->stats.txrate.cck[i] += ptxrate->cck[i];
+
+ if (i < 8)
+ priv->stats.txrate.ofdm[i] += ptxrate->ofdm[i];
+
+ for (j = 0; j < 4; j++)
+ priv->stats.txrate.ht_mcs[j][i] +=
+ ptxrate->ht_mcs[j][i];
+ }
+}
+
+u32 cmpk_message_handle_rx(struct net_device *dev,
+ struct rtllib_rx_stats *pstats)
+{
+ int total_length;
+ u8 cmd_length, exe_cnt = 0;
+ u8 element_id;
+ u8 *pcmd_buff;
+
+ RT_TRACE(COMP_CMDPKT, "---->cmpk_message_handle_rx()\n");
+
+ if (pstats == NULL)
+ return 0;
+
+ total_length = pstats->Length;
+
+ pcmd_buff = pstats->virtual_address;
+
+ element_id = pcmd_buff[0];
+
+ while (total_length > 0 || exe_cnt++ > 100) {
+ element_id = pcmd_buff[0];
+
+ switch (element_id) {
+ case RX_TX_FEEDBACK:
+ RT_TRACE(COMP_CMDPKT,
+ "---->cmpk_message_handle_rx():RX_TX_FEEDBACK\n");
+ cmpk_handle_tx_feedback(dev, pcmd_buff);
+ cmd_length = CMPK_RX_TX_FB_SIZE;
+ break;
+ case RX_INTERRUPT_STATUS:
+ RT_TRACE(COMP_CMDPKT,
+ "---->cmpk_message_handle_rx():RX_INTERRUPT_STATUS\n");
+ cmpk_handle_interrupt_status(dev, pcmd_buff);
+ cmd_length = sizeof(struct cmpk_intr_sta);
+ break;
+ case BOTH_QUERY_CONFIG:
+ RT_TRACE(COMP_CMDPKT,
+ "---->cmpk_message_handle_rx():BOTH_QUERY_CONFIG\n");
+ cmpk_handle_query_config_rx(dev, pcmd_buff);
+ cmd_length = CMPK_BOTH_QUERY_CONFIG_SIZE;
+ break;
+ case RX_TX_STATUS:
+ RT_TRACE(COMP_CMDPKT,
+ "---->cmpk_message_handle_rx():RX_TX_STATUS\n");
+ cmpk_handle_tx_status(dev, pcmd_buff);
+ cmd_length = CMPK_RX_TX_STS_SIZE;
+ break;
+ case RX_TX_PER_PKT_FEEDBACK:
+ RT_TRACE(COMP_CMDPKT,
+ "---->cmpk_message_handle_rx():RX_TX_PER_PKT_FEEDBACK\n");
+ cmd_length = CMPK_RX_TX_FB_SIZE;
+ break;
+ case RX_TX_RATE_HISTORY:
+ RT_TRACE(COMP_CMDPKT,
+ "---->cmpk_message_handle_rx():RX_TX_HISTORY\n");
+ cmpk_handle_tx_rate_history(dev, pcmd_buff);
+ cmd_length = CMPK_TX_RAHIS_SIZE;
+ break;
+ default:
+
+ RT_TRACE(COMP_CMDPKT,
+ "---->cmpk_message_handle_rx():unknown CMD Element\n");
+ return 1;
+ }
+
+ total_length -= cmd_length;
+ pcmd_buff += cmd_length;
+ }
+ return 1;
+}
diff --git a/drivers/staging/rtl8192e/rtl8192e/r8192E_cmdpkt.h b/drivers/staging/rtl8192e/rtl8192e/r8192E_cmdpkt.h
new file mode 100644
index 000000000..269368264
--- /dev/null
+++ b/drivers/staging/rtl8192e/rtl8192e/r8192E_cmdpkt.h
@@ -0,0 +1,159 @@
+/******************************************************************************
+ * Copyright(c) 2008 - 2010 Realtek Corporation. All rights reserved.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ * The full GNU General Public License is included in this distribution in the
+ * file called LICENSE.
+ *
+ * Contact Information:
+ * wlanfae <wlanfae@realtek.com>
+******************************************************************************/
+#ifndef R819XUSB_CMDPKT_H
+#define R819XUSB_CMDPKT_H
+#define CMPK_RX_TX_FB_SIZE sizeof(struct cmpk_txfb)
+#define CMPK_TX_SET_CONFIG_SIZE sizeof(struct cmpk_set_cfg)
+#define CMPK_BOTH_QUERY_CONFIG_SIZE sizeof(struct cmpk_set_cfg)
+#define CMPK_RX_TX_STS_SIZE sizeof(struct cmpk_tx_status)
+#define CMPK_RX_DBG_MSG_SIZE sizeof(struct cmpk_rx_dbginfo)
+#define CMPK_TX_RAHIS_SIZE sizeof(struct cmpk_tx_rahis)
+
+#define ISR_TxBcnOk BIT27
+#define ISR_TxBcnErr BIT26
+#define ISR_BcnTimerIntr BIT13
+
+
+struct cmpk_txfb {
+ u8 element_id;
+ u8 length;
+ u8 TID:4;
+ u8 fail_reason:3;
+ u8 tok:1;
+ u8 reserve1:4;
+ u8 pkt_type:2;
+ u8 bandwidth:1;
+ u8 qos_pkt:1;
+
+ u8 reserve2;
+ u8 retry_cnt;
+ u16 pkt_id;
+
+ u16 seq_num;
+ u8 s_rate;
+ u8 f_rate;
+
+ u8 s_rts_rate;
+ u8 f_rts_rate;
+ u16 pkt_length;
+
+ u16 reserve3;
+ u16 duration;
+};
+
+struct cmpk_intr_sta {
+ u8 element_id;
+ u8 length;
+ u16 reserve;
+ u32 interrupt_status;
+};
+
+
+struct cmpk_set_cfg {
+ u8 element_id;
+ u8 length;
+ u16 reserve1;
+ u8 cfg_reserve1:3;
+ u8 cfg_size:2;
+ u8 cfg_type:2;
+ u8 cfg_action:1;
+ u8 cfg_reserve2;
+ u8 cfg_page:4;
+ u8 cfg_reserve3:4;
+ u8 cfg_offset;
+ u32 value;
+ u32 mask;
+};
+
+#define cmpk_query_cfg_t struct cmpk_set_cfg
+
+struct cmpk_tx_status {
+ u16 reserve1;
+ u8 length;
+ u8 element_id;
+
+ u16 txfail;
+ u16 txok;
+
+ u16 txmcok;
+ u16 txretry;
+
+ u16 txucok;
+ u16 txbcok;
+
+ u16 txbcfail;
+ u16 txmcfail;
+
+ u16 reserve2;
+ u16 txucfail;
+
+ u32 txmclength;
+ u32 txbclength;
+ u32 txuclength;
+
+ u16 reserve3_23;
+ u8 reserve3_1;
+ u8 rate;
+} __packed;
+
+struct cmpk_rx_dbginfo {
+ u16 reserve1;
+ u8 length;
+ u8 element_id;
+
+
+};
+
+struct cmpk_tx_rahis {
+ u8 element_id;
+ u8 length;
+ u16 reserved1;
+
+ u16 cck[4];
+
+ u16 ofdm[8];
+
+
+
+
+
+ u16 ht_mcs[4][16];
+
+} __packed;
+
+enum cmpk_element {
+ RX_TX_FEEDBACK = 0,
+ RX_INTERRUPT_STATUS = 1,
+ TX_SET_CONFIG = 2,
+ BOTH_QUERY_CONFIG = 3,
+ RX_TX_STATUS = 4,
+ RX_DBGINFO_FEEDBACK = 5,
+ RX_TX_PER_PKT_FEEDBACK = 6,
+ RX_TX_RATE_HISTORY = 7,
+ RX_CMD_ELE_MAX
+};
+
+extern u32 cmpk_message_handle_rx(struct net_device *dev,
+ struct rtllib_rx_stats *pstats);
+extern bool cmpk_message_handle_tx(struct net_device *dev,
+ u8 *codevirtualaddress, u32 packettype,
+ u32 buffer_len);
+
+
+#endif
diff --git a/drivers/staging/rtl8192e/rtl8192e/r8192E_dev.c b/drivers/staging/rtl8192e/rtl8192e/r8192E_dev.c
new file mode 100644
index 000000000..286960243
--- /dev/null
+++ b/drivers/staging/rtl8192e/rtl8192e/r8192E_dev.c
@@ -0,0 +1,2416 @@
+/******************************************************************************
+ * Copyright(c) 2008 - 2010 Realtek Corporation. All rights reserved.
+ *
+ * Based on the r8180 driver, which is:
+ * Copyright 2004-2005 Andrea Merello <andrea.merello@gmail.com>, et al.
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ * The full GNU General Public License is included in this distribution in the
+ * file called LICENSE.
+ *
+ * Contact Information:
+ * wlanfae <wlanfae@realtek.com>
+******************************************************************************/
+#include "rtl_core.h"
+#include "r8192E_phy.h"
+#include "r8192E_phyreg.h"
+#include "r8190P_rtl8256.h"
+#include "r8192E_cmdpkt.h"
+#include "rtl_dm.h"
+#include "rtl_wx.h"
+
+static int WDCAPARA_ADD[] = {EDCAPARA_BE, EDCAPARA_BK, EDCAPARA_VI, EDCAPARA_VO};
+
+void rtl8192e_start_beacon(struct net_device *dev)
+{
+ struct r8192_priv *priv = (struct r8192_priv *)rtllib_priv(dev);
+ struct rtllib_network *net = &priv->rtllib->current_network;
+ u16 BcnTimeCfg = 0;
+ u16 BcnCW = 6;
+ u16 BcnIFS = 0xf;
+
+ DMESG("Enabling beacon TX");
+ rtl8192_irq_disable(dev);
+
+ write_nic_word(dev, ATIMWND, 2);
+
+ write_nic_word(dev, BCN_INTERVAL, net->beacon_interval);
+ write_nic_word(dev, BCN_DRV_EARLY_INT, 10);
+ write_nic_word(dev, BCN_DMATIME, 256);
+
+ write_nic_byte(dev, BCN_ERR_THRESH, 100);
+
+ BcnTimeCfg |= BcnCW<<BCN_TCFG_CW_SHIFT;
+ BcnTimeCfg |= BcnIFS<<BCN_TCFG_IFS;
+ write_nic_word(dev, BCN_TCFG, BcnTimeCfg);
+ rtl8192_irq_enable(dev);
+}
+
+static void rtl8192e_update_msr(struct net_device *dev)
+{
+ struct r8192_priv *priv = rtllib_priv(dev);
+ u8 msr;
+ enum led_ctl_mode LedAction = LED_CTL_NO_LINK;
+
+ msr = read_nic_byte(dev, MSR);
+ msr &= ~MSR_LINK_MASK;
+
+ switch (priv->rtllib->iw_mode) {
+ case IW_MODE_INFRA:
+ if (priv->rtllib->state == RTLLIB_LINKED)
+ msr |= (MSR_LINK_MANAGED << MSR_LINK_SHIFT);
+ else
+ msr |= (MSR_LINK_NONE << MSR_LINK_SHIFT);
+ LedAction = LED_CTL_LINK;
+ break;
+ case IW_MODE_ADHOC:
+ if (priv->rtllib->state == RTLLIB_LINKED)
+ msr |= (MSR_LINK_ADHOC << MSR_LINK_SHIFT);
+ else
+ msr |= (MSR_LINK_NONE << MSR_LINK_SHIFT);
+ break;
+ case IW_MODE_MASTER:
+ if (priv->rtllib->state == RTLLIB_LINKED)
+ msr |= (MSR_LINK_MASTER << MSR_LINK_SHIFT);
+ else
+ msr |= (MSR_LINK_NONE << MSR_LINK_SHIFT);
+ break;
+ default:
+ break;
+ }
+
+ write_nic_byte(dev, MSR, msr);
+ if (priv->rtllib->LedControlHandler)
+ priv->rtllib->LedControlHandler(dev, LedAction);
+}
+
+void rtl8192e_SetHwReg(struct net_device *dev, u8 variable, u8 *val)
+{
+ struct r8192_priv *priv = rtllib_priv(dev);
+
+ switch (variable) {
+ case HW_VAR_BSSID:
+ write_nic_dword(dev, BSSIDR, ((u32 *)(val))[0]);
+ write_nic_word(dev, BSSIDR+2, ((u16 *)(val+2))[0]);
+ break;
+
+ case HW_VAR_MEDIA_STATUS:
+ {
+ enum rt_op_mode OpMode = *((enum rt_op_mode *)(val));
+ enum led_ctl_mode LedAction = LED_CTL_NO_LINK;
+ u8 btMsr = read_nic_byte(dev, MSR);
+
+ btMsr &= 0xfc;
+
+ switch (OpMode) {
+ case RT_OP_MODE_INFRASTRUCTURE:
+ btMsr |= MSR_INFRA;
+ LedAction = LED_CTL_LINK;
+ break;
+
+ case RT_OP_MODE_IBSS:
+ btMsr |= MSR_ADHOC;
+ break;
+
+ case RT_OP_MODE_AP:
+ btMsr |= MSR_AP;
+ LedAction = LED_CTL_LINK;
+ break;
+
+ default:
+ btMsr |= MSR_NOLINK;
+ break;
+ }
+
+ write_nic_byte(dev, MSR, btMsr);
+
+ }
+ break;
+
+ case HW_VAR_CECHK_BSSID:
+ {
+ u32 RegRCR, Type;
+
+ Type = ((u8 *)(val))[0];
+ RegRCR = read_nic_dword(dev, RCR);
+ priv->ReceiveConfig = RegRCR;
+
+ if (Type == true)
+ RegRCR |= (RCR_CBSSID);
+ else if (Type == false)
+ RegRCR &= (~RCR_CBSSID);
+
+ write_nic_dword(dev, RCR, RegRCR);
+ priv->ReceiveConfig = RegRCR;
+
+ }
+ break;
+
+ case HW_VAR_SLOT_TIME:
+
+ priv->slot_time = val[0];
+ write_nic_byte(dev, SLOT_TIME, val[0]);
+
+ break;
+
+ case HW_VAR_ACK_PREAMBLE:
+ {
+ u32 regTmp;
+
+ priv->short_preamble = (bool)(*(u8 *)val);
+ regTmp = priv->basic_rate;
+ if (priv->short_preamble)
+ regTmp |= BRSR_AckShortPmb;
+ write_nic_dword(dev, RRSR, regTmp);
+ break;
+ }
+
+ case HW_VAR_CPU_RST:
+ write_nic_dword(dev, CPU_GEN, ((u32 *)(val))[0]);
+ break;
+
+ case HW_VAR_AC_PARAM:
+ {
+ u8 pAcParam = *((u8 *)val);
+ u32 eACI = pAcParam;
+ u8 u1bAIFS;
+ u32 u4bAcParam;
+ u8 mode = priv->rtllib->mode;
+ struct rtllib_qos_parameters *qos_parameters =
+ &priv->rtllib->current_network.qos_data.parameters;
+
+ u1bAIFS = qos_parameters->aifs[pAcParam] *
+ ((mode&(IEEE_G|IEEE_N_24G)) ? 9 : 20) + aSifsTime;
+
+ dm_init_edca_turbo(dev);
+
+ u4bAcParam = (((le16_to_cpu(
+ qos_parameters->tx_op_limit[pAcParam])) <<
+ AC_PARAM_TXOP_LIMIT_OFFSET) |
+ ((le16_to_cpu(qos_parameters->cw_max[pAcParam])) <<
+ AC_PARAM_ECW_MAX_OFFSET) |
+ ((le16_to_cpu(qos_parameters->cw_min[pAcParam])) <<
+ AC_PARAM_ECW_MIN_OFFSET) |
+ (((u32)u1bAIFS) << AC_PARAM_AIFS_OFFSET));
+
+ RT_TRACE(COMP_DBG, "%s():HW_VAR_AC_PARAM eACI:%x:%x\n",
+ __func__, eACI, u4bAcParam);
+ switch (eACI) {
+ case AC1_BK:
+ write_nic_dword(dev, EDCAPARA_BK, u4bAcParam);
+ break;
+
+ case AC0_BE:
+ write_nic_dword(dev, EDCAPARA_BE, u4bAcParam);
+ break;
+
+ case AC2_VI:
+ write_nic_dword(dev, EDCAPARA_VI, u4bAcParam);
+ break;
+
+ case AC3_VO:
+ write_nic_dword(dev, EDCAPARA_VO, u4bAcParam);
+ break;
+
+ default:
+ netdev_info(dev, "SetHwReg8185(): invalid ACI: %d !\n",
+ eACI);
+ break;
+ }
+ priv->rtllib->SetHwRegHandler(dev, HW_VAR_ACM_CTRL,
+ (u8 *)(&pAcParam));
+ break;
+ }
+
+ case HW_VAR_ACM_CTRL:
+ {
+ struct rtllib_qos_parameters *qos_parameters =
+ &priv->rtllib->current_network.qos_data.parameters;
+ u8 pAcParam = *((u8 *)val);
+ u32 eACI = pAcParam;
+ union aci_aifsn *pAciAifsn = (union aci_aifsn *) &
+ (qos_parameters->aifs[0]);
+ u8 acm = pAciAifsn->f.acm;
+ u8 AcmCtrl = read_nic_byte(dev, AcmHwCtrl);
+
+ RT_TRACE(COMP_DBG, "===========>%s():HW_VAR_ACM_CTRL:%x\n",
+ __func__, eACI);
+ AcmCtrl = AcmCtrl | ((priv->AcmMethod == 2) ? 0x0 : 0x1);
+
+ if (acm) {
+ switch (eACI) {
+ case AC0_BE:
+ AcmCtrl |= AcmHw_BeqEn;
+ break;
+
+ case AC2_VI:
+ AcmCtrl |= AcmHw_ViqEn;
+ break;
+
+ case AC3_VO:
+ AcmCtrl |= AcmHw_VoqEn;
+ break;
+
+ default:
+ RT_TRACE(COMP_QOS,
+ "SetHwReg8185(): [HW_VAR_ACM_CTRL] acm set failed: eACI is %d\n",
+ eACI);
+ break;
+ }
+ } else {
+ switch (eACI) {
+ case AC0_BE:
+ AcmCtrl &= (~AcmHw_BeqEn);
+ break;
+
+ case AC2_VI:
+ AcmCtrl &= (~AcmHw_ViqEn);
+ break;
+
+ case AC3_VO:
+ AcmCtrl &= (~AcmHw_BeqEn);
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ RT_TRACE(COMP_QOS,
+ "SetHwReg8190pci(): [HW_VAR_ACM_CTRL] Write 0x%X\n",
+ AcmCtrl);
+ write_nic_byte(dev, AcmHwCtrl, AcmCtrl);
+ break;
+ }
+
+ case HW_VAR_SIFS:
+ write_nic_byte(dev, SIFS, val[0]);
+ write_nic_byte(dev, SIFS+1, val[0]);
+ break;
+
+ case HW_VAR_RF_TIMING:
+ {
+ u8 Rf_Timing = *((u8 *)val);
+
+ write_nic_byte(dev, rFPGA0_RFTiming1, Rf_Timing);
+ break;
+ }
+
+ default:
+ break;
+ }
+
+}
+
+static void rtl8192_read_eeprom_info(struct net_device *dev)
+{
+ struct r8192_priv *priv = rtllib_priv(dev);
+
+ u8 tempval;
+ u8 ICVer8192, ICVer8256;
+ u16 i, usValue, IC_Version;
+ u16 EEPROMId;
+ u8 bMac_Tmp_Addr[6] = {0x00, 0xe0, 0x4c, 0x00, 0x00, 0x01};
+
+ RT_TRACE(COMP_INIT, "====> rtl8192_read_eeprom_info\n");
+
+ EEPROMId = eprom_read(dev, 0);
+ if (EEPROMId != RTL8190_EEPROM_ID) {
+ RT_TRACE(COMP_ERR, "EEPROM ID is invalid:%x, %x\n",
+ EEPROMId, RTL8190_EEPROM_ID);
+ priv->AutoloadFailFlag = true;
+ } else {
+ priv->AutoloadFailFlag = false;
+ }
+
+ if (!priv->AutoloadFailFlag) {
+ priv->eeprom_vid = eprom_read(dev, EEPROM_VID >> 1);
+ priv->eeprom_did = eprom_read(dev, EEPROM_DID >> 1);
+
+ usValue = eprom_read(dev, (u16)(EEPROM_Customer_ID>>1)) >> 8;
+ priv->eeprom_CustomerID = (u8)(usValue & 0xff);
+ usValue = eprom_read(dev, EEPROM_ICVersion_ChannelPlan>>1);
+ priv->eeprom_ChannelPlan = usValue&0xff;
+ IC_Version = (usValue & 0xff00)>>8;
+
+ ICVer8192 = (IC_Version&0xf);
+ ICVer8256 = (IC_Version & 0xf0)>>4;
+ RT_TRACE(COMP_INIT, "\nICVer8192 = 0x%x\n", ICVer8192);
+ RT_TRACE(COMP_INIT, "\nICVer8256 = 0x%x\n", ICVer8256);
+ if (ICVer8192 == 0x2) {
+ if (ICVer8256 == 0x5)
+ priv->card_8192_version = VERSION_8190_BE;
+ }
+ switch (priv->card_8192_version) {
+ case VERSION_8190_BD:
+ case VERSION_8190_BE:
+ break;
+ default:
+ priv->card_8192_version = VERSION_8190_BD;
+ break;
+ }
+ RT_TRACE(COMP_INIT, "\nIC Version = 0x%x\n",
+ priv->card_8192_version);
+ } else {
+ priv->card_8192_version = VERSION_8190_BD;
+ priv->eeprom_vid = 0;
+ priv->eeprom_did = 0;
+ priv->eeprom_CustomerID = 0;
+ priv->eeprom_ChannelPlan = 0;
+ RT_TRACE(COMP_INIT, "\nIC Version = 0x%x\n", 0xff);
+ }
+
+ RT_TRACE(COMP_INIT, "EEPROM VID = 0x%4x\n", priv->eeprom_vid);
+ RT_TRACE(COMP_INIT, "EEPROM DID = 0x%4x\n", priv->eeprom_did);
+ RT_TRACE(COMP_INIT, "EEPROM Customer ID: 0x%2x\n",
+ priv->eeprom_CustomerID);
+
+ if (!priv->AutoloadFailFlag) {
+ for (i = 0; i < 6; i += 2) {
+ usValue = eprom_read(dev,
+ (u16)((EEPROM_NODE_ADDRESS_BYTE_0 + i) >> 1));
+ *(u16 *)(&dev->dev_addr[i]) = usValue;
+ }
+ } else {
+ memcpy(dev->dev_addr, bMac_Tmp_Addr, 6);
+ }
+
+ RT_TRACE(COMP_INIT, "Permanent Address = %pM\n",
+ dev->dev_addr);
+
+ if (priv->card_8192_version > VERSION_8190_BD)
+ priv->bTXPowerDataReadFromEEPORM = true;
+ else
+ priv->bTXPowerDataReadFromEEPORM = false;
+
+ priv->rf_type = RTL819X_DEFAULT_RF_TYPE;
+
+ if (priv->card_8192_version > VERSION_8190_BD) {
+ if (!priv->AutoloadFailFlag) {
+ tempval = (eprom_read(dev, (EEPROM_RFInd_PowerDiff >>
+ 1))) & 0xff;
+ priv->EEPROMLegacyHTTxPowerDiff = tempval & 0xf;
+
+ if (tempval&0x80)
+ priv->rf_type = RF_1T2R;
+ else
+ priv->rf_type = RF_2T4R;
+ } else {
+ priv->EEPROMLegacyHTTxPowerDiff = 0x04;
+ }
+ RT_TRACE(COMP_INIT, "EEPROMLegacyHTTxPowerDiff = %d\n",
+ priv->EEPROMLegacyHTTxPowerDiff);
+
+ if (!priv->AutoloadFailFlag)
+ priv->EEPROMThermalMeter = (u8)(((eprom_read(dev,
+ (EEPROM_ThermalMeter>>1))) &
+ 0xff00)>>8);
+ else
+ priv->EEPROMThermalMeter = EEPROM_Default_ThermalMeter;
+ RT_TRACE(COMP_INIT, "ThermalMeter = %d\n",
+ priv->EEPROMThermalMeter);
+ priv->TSSI_13dBm = priv->EEPROMThermalMeter * 100;
+
+ if (priv->epromtype == EEPROM_93C46) {
+ if (!priv->AutoloadFailFlag) {
+ usValue = eprom_read(dev,
+ EEPROM_TxPwDiff_CrystalCap >> 1);
+ priv->EEPROMAntPwDiff = (usValue&0x0fff);
+ priv->EEPROMCrystalCap = (u8)((usValue & 0xf000)
+ >> 12);
+ } else {
+ priv->EEPROMAntPwDiff =
+ EEPROM_Default_AntTxPowerDiff;
+ priv->EEPROMCrystalCap =
+ EEPROM_Default_TxPwDiff_CrystalCap;
+ }
+ RT_TRACE(COMP_INIT, "EEPROMAntPwDiff = %d\n",
+ priv->EEPROMAntPwDiff);
+ RT_TRACE(COMP_INIT, "EEPROMCrystalCap = %d\n",
+ priv->EEPROMCrystalCap);
+
+ for (i = 0; i < 14; i += 2) {
+ if (!priv->AutoloadFailFlag)
+ usValue = eprom_read(dev,
+ (u16)((EEPROM_TxPwIndex_CCK +
+ i) >> 1));
+ else
+ usValue = EEPROM_Default_TxPower;
+ *((u16 *)(&priv->EEPROMTxPowerLevelCCK[i])) =
+ usValue;
+ RT_TRACE(COMP_INIT,
+ "CCK Tx Power Level, Index %d = 0x%02x\n",
+ i, priv->EEPROMTxPowerLevelCCK[i]);
+ RT_TRACE(COMP_INIT,
+ "CCK Tx Power Level, Index %d = 0x%02x\n",
+ i+1, priv->EEPROMTxPowerLevelCCK[i+1]);
+ }
+ for (i = 0; i < 14; i += 2) {
+ if (!priv->AutoloadFailFlag)
+ usValue = eprom_read(dev,
+ (u16)((EEPROM_TxPwIndex_OFDM_24G
+ + i) >> 1));
+ else
+ usValue = EEPROM_Default_TxPower;
+ *((u16 *)(&priv->EEPROMTxPowerLevelOFDM24G[i]))
+ = usValue;
+ RT_TRACE(COMP_INIT,
+ "OFDM 2.4G Tx Power Level, Index %d = 0x%02x\n",
+ i, priv->EEPROMTxPowerLevelOFDM24G[i]);
+ RT_TRACE(COMP_INIT,
+ "OFDM 2.4G Tx Power Level, Index %d = 0x%02x\n",
+ i + 1,
+ priv->EEPROMTxPowerLevelOFDM24G[i+1]);
+ }
+ }
+ if (priv->epromtype == EEPROM_93C46) {
+ for (i = 0; i < 14; i++) {
+ priv->TxPowerLevelCCK[i] =
+ priv->EEPROMTxPowerLevelCCK[i];
+ priv->TxPowerLevelOFDM24G[i] =
+ priv->EEPROMTxPowerLevelOFDM24G[i];
+ }
+ priv->LegacyHTTxPowerDiff =
+ priv->EEPROMLegacyHTTxPowerDiff;
+ priv->AntennaTxPwDiff[0] = (priv->EEPROMAntPwDiff &
+ 0xf);
+ priv->AntennaTxPwDiff[1] = (priv->EEPROMAntPwDiff &
+ 0xf0) >> 4;
+ priv->AntennaTxPwDiff[2] = (priv->EEPROMAntPwDiff &
+ 0xf00) >> 8;
+ priv->CrystalCap = priv->EEPROMCrystalCap;
+ priv->ThermalMeter[0] = (priv->EEPROMThermalMeter &
+ 0xf);
+ priv->ThermalMeter[1] = (priv->EEPROMThermalMeter &
+ 0xf0) >> 4;
+ } else if (priv->epromtype == EEPROM_93C56) {
+
+ for (i = 0; i < 3; i++) {
+ priv->TxPowerLevelCCK_A[i] =
+ priv->EEPROMRfACCKChnl1TxPwLevel[0];
+ priv->TxPowerLevelOFDM24G_A[i] =
+ priv->EEPROMRfAOfdmChnlTxPwLevel[0];
+ priv->TxPowerLevelCCK_C[i] =
+ priv->EEPROMRfCCCKChnl1TxPwLevel[0];
+ priv->TxPowerLevelOFDM24G_C[i] =
+ priv->EEPROMRfCOfdmChnlTxPwLevel[0];
+ }
+ for (i = 3; i < 9; i++) {
+ priv->TxPowerLevelCCK_A[i] =
+ priv->EEPROMRfACCKChnl1TxPwLevel[1];
+ priv->TxPowerLevelOFDM24G_A[i] =
+ priv->EEPROMRfAOfdmChnlTxPwLevel[1];
+ priv->TxPowerLevelCCK_C[i] =
+ priv->EEPROMRfCCCKChnl1TxPwLevel[1];
+ priv->TxPowerLevelOFDM24G_C[i] =
+ priv->EEPROMRfCOfdmChnlTxPwLevel[1];
+ }
+ for (i = 9; i < 14; i++) {
+ priv->TxPowerLevelCCK_A[i] =
+ priv->EEPROMRfACCKChnl1TxPwLevel[2];
+ priv->TxPowerLevelOFDM24G_A[i] =
+ priv->EEPROMRfAOfdmChnlTxPwLevel[2];
+ priv->TxPowerLevelCCK_C[i] =
+ priv->EEPROMRfCCCKChnl1TxPwLevel[2];
+ priv->TxPowerLevelOFDM24G_C[i] =
+ priv->EEPROMRfCOfdmChnlTxPwLevel[2];
+ }
+ for (i = 0; i < 14; i++)
+ RT_TRACE(COMP_INIT,
+ "priv->TxPowerLevelCCK_A[%d] = 0x%x\n",
+ i, priv->TxPowerLevelCCK_A[i]);
+ for (i = 0; i < 14; i++)
+ RT_TRACE(COMP_INIT,
+ "priv->TxPowerLevelOFDM24G_A[%d] = 0x%x\n",
+ i, priv->TxPowerLevelOFDM24G_A[i]);
+ for (i = 0; i < 14; i++)
+ RT_TRACE(COMP_INIT,
+ "priv->TxPowerLevelCCK_C[%d] = 0x%x\n",
+ i, priv->TxPowerLevelCCK_C[i]);
+ for (i = 0; i < 14; i++)
+ RT_TRACE(COMP_INIT,
+ "priv->TxPowerLevelOFDM24G_C[%d] = 0x%x\n",
+ i, priv->TxPowerLevelOFDM24G_C[i]);
+ priv->LegacyHTTxPowerDiff =
+ priv->EEPROMLegacyHTTxPowerDiff;
+ priv->AntennaTxPwDiff[0] = 0;
+ priv->AntennaTxPwDiff[1] = 0;
+ priv->AntennaTxPwDiff[2] = 0;
+ priv->CrystalCap = priv->EEPROMCrystalCap;
+ priv->ThermalMeter[0] = (priv->EEPROMThermalMeter &
+ 0xf);
+ priv->ThermalMeter[1] = (priv->EEPROMThermalMeter &
+ 0xf0) >> 4;
+ }
+ }
+
+ if (priv->rf_type == RF_1T2R) {
+ /* no matter what checkpatch says, the braces are needed */
+ RT_TRACE(COMP_INIT, "\n1T2R config\n");
+ } else if (priv->rf_type == RF_2T4R) {
+ RT_TRACE(COMP_INIT, "\n2T4R config\n");
+ }
+
+ init_rate_adaptive(dev);
+
+ priv->rf_chip = RF_8256;
+
+ if (priv->RegChannelPlan == 0xf)
+ priv->ChannelPlan = priv->eeprom_ChannelPlan;
+ else
+ priv->ChannelPlan = priv->RegChannelPlan;
+
+ if (priv->eeprom_vid == 0x1186 && priv->eeprom_did == 0x3304)
+ priv->CustomerID = RT_CID_DLINK;
+
+ switch (priv->eeprom_CustomerID) {
+ case EEPROM_CID_DEFAULT:
+ priv->CustomerID = RT_CID_DEFAULT;
+ break;
+ case EEPROM_CID_CAMEO:
+ priv->CustomerID = RT_CID_819x_CAMEO;
+ break;
+ case EEPROM_CID_RUNTOP:
+ priv->CustomerID = RT_CID_819x_RUNTOP;
+ break;
+ case EEPROM_CID_NetCore:
+ priv->CustomerID = RT_CID_819x_Netcore;
+ break;
+ case EEPROM_CID_TOSHIBA:
+ priv->CustomerID = RT_CID_TOSHIBA;
+ if (priv->eeprom_ChannelPlan&0x80)
+ priv->ChannelPlan = priv->eeprom_ChannelPlan&0x7f;
+ else
+ priv->ChannelPlan = 0x0;
+ RT_TRACE(COMP_INIT, "Toshiba ChannelPlan = 0x%x\n",
+ priv->ChannelPlan);
+ break;
+ case EEPROM_CID_Nettronix:
+ priv->ScanDelay = 100;
+ priv->CustomerID = RT_CID_Nettronix;
+ break;
+ case EEPROM_CID_Pronet:
+ priv->CustomerID = RT_CID_PRONET;
+ break;
+ case EEPROM_CID_DLINK:
+ priv->CustomerID = RT_CID_DLINK;
+ break;
+
+ case EEPROM_CID_WHQL:
+ break;
+ default:
+ break;
+ }
+
+ if (priv->ChannelPlan > CHANNEL_PLAN_LEN - 1)
+ priv->ChannelPlan = 0;
+ priv->ChannelPlan = COUNTRY_CODE_WORLD_WIDE_13;
+
+ if (priv->eeprom_vid == 0x1186 && priv->eeprom_did == 0x3304)
+ priv->rtllib->bSupportRemoteWakeUp = true;
+ else
+ priv->rtllib->bSupportRemoteWakeUp = false;
+
+ RT_TRACE(COMP_INIT, "RegChannelPlan(%d)\n", priv->RegChannelPlan);
+ RT_TRACE(COMP_INIT, "ChannelPlan = %d\n", priv->ChannelPlan);
+ RT_TRACE(COMP_TRACE, "<==== ReadAdapterInfo\n");
+}
+
+void rtl8192_get_eeprom_size(struct net_device *dev)
+{
+ u16 curCR;
+ struct r8192_priv *priv = rtllib_priv(dev);
+
+ RT_TRACE(COMP_INIT, "===========>%s()\n", __func__);
+ curCR = read_nic_dword(dev, EPROM_CMD);
+ RT_TRACE(COMP_INIT, "read from Reg Cmd9346CR(%x):%x\n", EPROM_CMD,
+ curCR);
+ priv->epromtype = (curCR & EPROM_CMD_9356SEL) ? EEPROM_93C56 :
+ EEPROM_93C46;
+ RT_TRACE(COMP_INIT, "<===========%s(), epromtype:%d\n", __func__,
+ priv->epromtype);
+ rtl8192_read_eeprom_info(dev);
+}
+
+static void rtl8192_hwconfig(struct net_device *dev)
+{
+ u32 regRATR = 0, regRRSR = 0;
+ u8 regBwOpMode = 0, regTmp = 0;
+ struct r8192_priv *priv = rtllib_priv(dev);
+
+ switch (priv->rtllib->mode) {
+ case WIRELESS_MODE_B:
+ regBwOpMode = BW_OPMODE_20MHZ;
+ regRATR = RATE_ALL_CCK;
+ regRRSR = RATE_ALL_CCK;
+ break;
+ case WIRELESS_MODE_A:
+ regBwOpMode = BW_OPMODE_5G | BW_OPMODE_20MHZ;
+ regRATR = RATE_ALL_OFDM_AG;
+ regRRSR = RATE_ALL_OFDM_AG;
+ break;
+ case WIRELESS_MODE_G:
+ regBwOpMode = BW_OPMODE_20MHZ;
+ regRATR = RATE_ALL_CCK | RATE_ALL_OFDM_AG;
+ regRRSR = RATE_ALL_CCK | RATE_ALL_OFDM_AG;
+ break;
+ case WIRELESS_MODE_AUTO:
+ case WIRELESS_MODE_N_24G:
+ regBwOpMode = BW_OPMODE_20MHZ;
+ regRATR = RATE_ALL_CCK | RATE_ALL_OFDM_AG |
+ RATE_ALL_OFDM_1SS | RATE_ALL_OFDM_2SS;
+ regRRSR = RATE_ALL_CCK | RATE_ALL_OFDM_AG;
+ break;
+ case WIRELESS_MODE_N_5G:
+ regBwOpMode = BW_OPMODE_5G;
+ regRATR = RATE_ALL_OFDM_AG | RATE_ALL_OFDM_1SS |
+ RATE_ALL_OFDM_2SS;
+ regRRSR = RATE_ALL_OFDM_AG;
+ break;
+ default:
+ regBwOpMode = BW_OPMODE_20MHZ;
+ regRATR = RATE_ALL_CCK | RATE_ALL_OFDM_AG;
+ regRRSR = RATE_ALL_CCK | RATE_ALL_OFDM_AG;
+ break;
+ }
+
+ write_nic_byte(dev, BW_OPMODE, regBwOpMode);
+ {
+ u32 ratr_value = 0;
+
+ ratr_value = regRATR;
+ if (priv->rf_type == RF_1T2R)
+ ratr_value &= ~(RATE_ALL_OFDM_2SS);
+ write_nic_dword(dev, RATR0, ratr_value);
+ write_nic_byte(dev, UFWP, 1);
+ }
+ regTmp = read_nic_byte(dev, 0x313);
+ regRRSR = ((regTmp) << 24) | (regRRSR & 0x00ffffff);
+ write_nic_dword(dev, RRSR, regRRSR);
+
+ write_nic_word(dev, RETRY_LIMIT,
+ priv->ShortRetryLimit << RETRY_LIMIT_SHORT_SHIFT |
+ priv->LongRetryLimit << RETRY_LIMIT_LONG_SHIFT);
+}
+
+bool rtl8192_adapter_start(struct net_device *dev)
+{
+ struct r8192_priv *priv = rtllib_priv(dev);
+ u32 ulRegRead;
+ bool rtStatus = true;
+ u8 tmpvalue;
+ u8 ICVersion, SwitchingRegulatorOutput;
+ bool bfirmwareok = true;
+ u32 tmpRegA, tmpRegC, TempCCk;
+ int i = 0;
+ u32 retry_times = 0;
+
+ RT_TRACE(COMP_INIT, "====>%s()\n", __func__);
+ priv->being_init_adapter = true;
+
+start:
+ rtl8192_pci_resetdescring(dev);
+ priv->Rf_Mode = RF_OP_By_SW_3wire;
+ if (priv->ResetProgress == RESET_TYPE_NORESET) {
+ write_nic_byte(dev, ANAPAR, 0x37);
+ mdelay(500);
+ }
+ priv->pFirmware->firmware_status = FW_STATUS_0_INIT;
+
+ if (priv->RegRfOff)
+ priv->rtllib->eRFPowerState = eRfOff;
+
+ ulRegRead = read_nic_dword(dev, CPU_GEN);
+ if (priv->pFirmware->firmware_status == FW_STATUS_0_INIT)
+ ulRegRead |= CPU_GEN_SYSTEM_RESET;
+ else if (priv->pFirmware->firmware_status == FW_STATUS_5_READY)
+ ulRegRead |= CPU_GEN_FIRMWARE_RESET;
+ else
+ RT_TRACE(COMP_ERR,
+ "ERROR in %s(): undefined firmware state(%d)\n",
+ __func__, priv->pFirmware->firmware_status);
+
+ write_nic_dword(dev, CPU_GEN, ulRegRead);
+
+ ICVersion = read_nic_byte(dev, IC_VERRSION);
+ if (ICVersion >= 0x4) {
+ SwitchingRegulatorOutput = read_nic_byte(dev, SWREGULATOR);
+ if (SwitchingRegulatorOutput != 0xb8) {
+ write_nic_byte(dev, SWREGULATOR, 0xa8);
+ mdelay(1);
+ write_nic_byte(dev, SWREGULATOR, 0xb8);
+ }
+ }
+ RT_TRACE(COMP_INIT, "BB Config Start!\n");
+ rtStatus = rtl8192_BBConfig(dev);
+ if (!rtStatus) {
+ RT_TRACE(COMP_ERR, "BB Config failed\n");
+ return rtStatus;
+ }
+ RT_TRACE(COMP_INIT, "BB Config Finished!\n");
+
+ priv->LoopbackMode = RTL819X_NO_LOOPBACK;
+ if (priv->ResetProgress == RESET_TYPE_NORESET) {
+ ulRegRead = read_nic_dword(dev, CPU_GEN);
+ if (priv->LoopbackMode == RTL819X_NO_LOOPBACK)
+ ulRegRead = ((ulRegRead & CPU_GEN_NO_LOOPBACK_MSK) |
+ CPU_GEN_NO_LOOPBACK_SET);
+ else if (priv->LoopbackMode == RTL819X_MAC_LOOPBACK)
+ ulRegRead |= CPU_CCK_LOOPBACK;
+ else
+ RT_TRACE(COMP_ERR,
+ "Serious error: wrong loopback mode setting\n");
+
+ write_nic_dword(dev, CPU_GEN, ulRegRead);
+
+ udelay(500);
+ }
+ rtl8192_hwconfig(dev);
+ write_nic_byte(dev, CMDR, CR_RE | CR_TE);
+
+ write_nic_byte(dev, PCIF, ((MXDMA2_NoLimit<<MXDMA2_RX_SHIFT) |
+ (MXDMA2_NoLimit<<MXDMA2_TX_SHIFT)));
+ write_nic_dword(dev, MAC0, ((u32 *)dev->dev_addr)[0]);
+ write_nic_word(dev, MAC4, ((u16 *)(dev->dev_addr + 4))[0]);
+ write_nic_dword(dev, RCR, priv->ReceiveConfig);
+
+ write_nic_dword(dev, RQPN1, NUM_OF_PAGE_IN_FW_QUEUE_BK <<
+ RSVD_FW_QUEUE_PAGE_BK_SHIFT |
+ NUM_OF_PAGE_IN_FW_QUEUE_BE <<
+ RSVD_FW_QUEUE_PAGE_BE_SHIFT |
+ NUM_OF_PAGE_IN_FW_QUEUE_VI <<
+ RSVD_FW_QUEUE_PAGE_VI_SHIFT |
+ NUM_OF_PAGE_IN_FW_QUEUE_VO <<
+ RSVD_FW_QUEUE_PAGE_VO_SHIFT);
+ write_nic_dword(dev, RQPN2, NUM_OF_PAGE_IN_FW_QUEUE_MGNT <<
+ RSVD_FW_QUEUE_PAGE_MGNT_SHIFT);
+ write_nic_dword(dev, RQPN3, APPLIED_RESERVED_QUEUE_IN_FW |
+ NUM_OF_PAGE_IN_FW_QUEUE_BCN <<
+ RSVD_FW_QUEUE_PAGE_BCN_SHIFT|
+ NUM_OF_PAGE_IN_FW_QUEUE_PUB <<
+ RSVD_FW_QUEUE_PAGE_PUB_SHIFT);
+
+ rtl8192_tx_enable(dev);
+ rtl8192_rx_enable(dev);
+ ulRegRead = (0xFFF00000 & read_nic_dword(dev, RRSR)) |
+ RATE_ALL_OFDM_AG | RATE_ALL_CCK;
+ write_nic_dword(dev, RRSR, ulRegRead);
+ write_nic_dword(dev, RATR0+4*7, (RATE_ALL_OFDM_AG | RATE_ALL_CCK));
+
+ write_nic_byte(dev, ACK_TIMEOUT, 0x30);
+
+ if (priv->ResetProgress == RESET_TYPE_NORESET)
+ rtl8192_SetWirelessMode(dev, priv->rtllib->mode);
+ CamResetAllEntry(dev);
+ {
+ u8 SECR_value = 0x0;
+
+ SECR_value |= SCR_TxEncEnable;
+ SECR_value |= SCR_RxDecEnable;
+ SECR_value |= SCR_NoSKMC;
+ write_nic_byte(dev, SECR, SECR_value);
+ }
+ write_nic_word(dev, ATIMWND, 2);
+ write_nic_word(dev, BCN_INTERVAL, 100);
+ {
+ int i;
+
+ for (i = 0; i < QOS_QUEUE_NUM; i++)
+ write_nic_dword(dev, WDCAPARA_ADD[i], 0x005e4332);
+ }
+ write_nic_byte(dev, 0xbe, 0xc0);
+
+ rtl8192_phy_configmac(dev);
+
+ if (priv->card_8192_version > (u8) VERSION_8190_BD) {
+ rtl8192_phy_getTxPower(dev);
+ rtl8192_phy_setTxPower(dev, priv->chan);
+ }
+
+ tmpvalue = read_nic_byte(dev, IC_VERRSION);
+ priv->IC_Cut = tmpvalue;
+ RT_TRACE(COMP_INIT, "priv->IC_Cut= 0x%x\n", priv->IC_Cut);
+ if (priv->IC_Cut >= IC_VersionCut_D) {
+ if (priv->IC_Cut == IC_VersionCut_D) {
+ /* no matter what checkpatch says, braces are needed */
+ RT_TRACE(COMP_INIT, "D-cut\n");
+ } else if (priv->IC_Cut == IC_VersionCut_E) {
+ RT_TRACE(COMP_INIT, "E-cut\n");
+ }
+ } else {
+ RT_TRACE(COMP_INIT, "Before C-cut\n");
+ }
+
+ RT_TRACE(COMP_INIT, "Load Firmware!\n");
+ bfirmwareok = init_firmware(dev);
+ if (!bfirmwareok) {
+ if (retry_times < 10) {
+ retry_times++;
+ goto start;
+ } else {
+ rtStatus = false;
+ goto end;
+ }
+ }
+ RT_TRACE(COMP_INIT, "Load Firmware finished!\n");
+ if (priv->ResetProgress == RESET_TYPE_NORESET) {
+ RT_TRACE(COMP_INIT, "RF Config Started!\n");
+ rtStatus = rtl8192_phy_RFConfig(dev);
+ if (!rtStatus) {
+ RT_TRACE(COMP_ERR, "RF Config failed\n");
+ return rtStatus;
+ }
+ RT_TRACE(COMP_INIT, "RF Config Finished!\n");
+ }
+ rtl8192_phy_updateInitGain(dev);
+
+ rtl8192_setBBreg(dev, rFPGA0_RFMOD, bCCKEn, 0x1);
+ rtl8192_setBBreg(dev, rFPGA0_RFMOD, bOFDMEn, 0x1);
+
+ write_nic_byte(dev, 0x87, 0x0);
+
+ if (priv->RegRfOff) {
+ RT_TRACE((COMP_INIT | COMP_RF | COMP_POWER),
+ "%s(): Turn off RF for RegRfOff ----------\n",
+ __func__);
+ MgntActSet_RF_State(dev, eRfOff, RF_CHANGE_BY_SW, true);
+ } else if (priv->rtllib->RfOffReason > RF_CHANGE_BY_PS) {
+ RT_TRACE((COMP_INIT|COMP_RF|COMP_POWER),
+ "%s(): Turn off RF for RfOffReason(%d) ----------\n",
+ __func__, priv->rtllib->RfOffReason);
+ MgntActSet_RF_State(dev, eRfOff, priv->rtllib->RfOffReason,
+ true);
+ } else if (priv->rtllib->RfOffReason >= RF_CHANGE_BY_IPS) {
+ RT_TRACE((COMP_INIT|COMP_RF|COMP_POWER),
+ "%s(): Turn off RF for RfOffReason(%d) ----------\n",
+ __func__, priv->rtllib->RfOffReason);
+ MgntActSet_RF_State(dev, eRfOff, priv->rtllib->RfOffReason,
+ true);
+ } else {
+ RT_TRACE((COMP_INIT|COMP_RF|COMP_POWER), "%s(): RF-ON\n",
+ __func__);
+ priv->rtllib->eRFPowerState = eRfOn;
+ priv->rtllib->RfOffReason = 0;
+ }
+
+ if (priv->rtllib->FwRWRF)
+ priv->Rf_Mode = RF_OP_By_FW;
+ else
+ priv->Rf_Mode = RF_OP_By_SW_3wire;
+
+ if (priv->ResetProgress == RESET_TYPE_NORESET) {
+ dm_initialize_txpower_tracking(dev);
+
+ if (priv->IC_Cut >= IC_VersionCut_D) {
+ tmpRegA = rtl8192_QueryBBReg(dev,
+ rOFDM0_XATxIQImbalance, bMaskDWord);
+ tmpRegC = rtl8192_QueryBBReg(dev,
+ rOFDM0_XCTxIQImbalance, bMaskDWord);
+ for (i = 0; i < TxBBGainTableLength; i++) {
+ if (tmpRegA ==
+ priv->txbbgain_table[i].txbbgain_value) {
+ priv->rfa_txpowertrackingindex = (u8)i;
+ priv->rfa_txpowertrackingindex_real =
+ (u8)i;
+ priv->rfa_txpowertracking_default =
+ priv->rfa_txpowertrackingindex;
+ break;
+ }
+ }
+
+ TempCCk = rtl8192_QueryBBReg(dev,
+ rCCK0_TxFilter1, bMaskByte2);
+
+ for (i = 0; i < CCKTxBBGainTableLength; i++) {
+ if (TempCCk == priv->cck_txbbgain_table[i].ccktxbb_valuearray[0]) {
+ priv->CCKPresentAttentuation_20Mdefault = (u8)i;
+ break;
+ }
+ }
+ priv->CCKPresentAttentuation_40Mdefault = 0;
+ priv->CCKPresentAttentuation_difference = 0;
+ priv->CCKPresentAttentuation =
+ priv->CCKPresentAttentuation_20Mdefault;
+ RT_TRACE(COMP_POWER_TRACKING,
+ "priv->rfa_txpowertrackingindex_initial = %d\n",
+ priv->rfa_txpowertrackingindex);
+ RT_TRACE(COMP_POWER_TRACKING,
+ "priv->rfa_txpowertrackingindex_real__initial = %d\n",
+ priv->rfa_txpowertrackingindex_real);
+ RT_TRACE(COMP_POWER_TRACKING,
+ "priv->CCKPresentAttentuation_difference_initial = %d\n",
+ priv->CCKPresentAttentuation_difference);
+ RT_TRACE(COMP_POWER_TRACKING,
+ "priv->CCKPresentAttentuation_initial = %d\n",
+ priv->CCKPresentAttentuation);
+ priv->btxpower_tracking = false;
+ }
+ }
+ rtl8192_irq_enable(dev);
+end:
+ priv->being_init_adapter = false;
+ return rtStatus;
+}
+
+static void rtl8192_net_update(struct net_device *dev)
+{
+
+ struct r8192_priv *priv = rtllib_priv(dev);
+ struct rtllib_network *net;
+ u16 BcnTimeCfg = 0, BcnCW = 6, BcnIFS = 0xf;
+ u16 rate_config = 0;
+
+ net = &priv->rtllib->current_network;
+ rtl8192_config_rate(dev, &rate_config);
+ priv->dot11CurrentPreambleMode = PREAMBLE_AUTO;
+ priv->basic_rate = rate_config &= 0x15f;
+ write_nic_dword(dev, BSSIDR, ((u32 *)net->bssid)[0]);
+ write_nic_word(dev, BSSIDR+4, ((u16 *)net->bssid)[2]);
+
+ if (priv->rtllib->iw_mode == IW_MODE_ADHOC) {
+ write_nic_word(dev, ATIMWND, 2);
+ write_nic_word(dev, BCN_DMATIME, 256);
+ write_nic_word(dev, BCN_INTERVAL, net->beacon_interval);
+ write_nic_word(dev, BCN_DRV_EARLY_INT, 10);
+ write_nic_byte(dev, BCN_ERR_THRESH, 100);
+
+ BcnTimeCfg |= (BcnCW<<BCN_TCFG_CW_SHIFT);
+ BcnTimeCfg |= BcnIFS<<BCN_TCFG_IFS;
+
+ write_nic_word(dev, BCN_TCFG, BcnTimeCfg);
+ }
+}
+
+void rtl8192_link_change(struct net_device *dev)
+{
+ struct r8192_priv *priv = rtllib_priv(dev);
+ struct rtllib_device *ieee = priv->rtllib;
+
+ if (!priv->up)
+ return;
+
+ if (ieee->state == RTLLIB_LINKED) {
+ rtl8192_net_update(dev);
+ priv->ops->update_ratr_table(dev);
+ if ((KEY_TYPE_WEP40 == ieee->pairwise_key_type) ||
+ (KEY_TYPE_WEP104 == ieee->pairwise_key_type))
+ EnableHWSecurityConfig8192(dev);
+ } else {
+ write_nic_byte(dev, 0x173, 0);
+ }
+ rtl8192e_update_msr(dev);
+
+ if (ieee->iw_mode == IW_MODE_INFRA || ieee->iw_mode == IW_MODE_ADHOC) {
+ u32 reg = 0;
+
+ reg = read_nic_dword(dev, RCR);
+ if (priv->rtllib->state == RTLLIB_LINKED) {
+ if (ieee->IntelPromiscuousModeInfo.bPromiscuousOn)
+ ;
+ else
+ priv->ReceiveConfig = reg |= RCR_CBSSID;
+ } else
+ priv->ReceiveConfig = reg &= ~RCR_CBSSID;
+
+ write_nic_dword(dev, RCR, reg);
+ }
+}
+
+void rtl8192_AllowAllDestAddr(struct net_device *dev,
+ bool bAllowAllDA, bool WriteIntoReg)
+{
+ struct r8192_priv *priv = rtllib_priv(dev);
+
+ if (bAllowAllDA)
+ priv->ReceiveConfig |= RCR_AAP;
+ else
+ priv->ReceiveConfig &= ~RCR_AAP;
+
+ if (WriteIntoReg)
+ write_nic_dword(dev, RCR, priv->ReceiveConfig);
+}
+
+static u8 MRateToHwRate8190Pci(u8 rate)
+{
+ u8 ret = DESC90_RATE1M;
+
+ switch (rate) {
+ case MGN_1M:
+ ret = DESC90_RATE1M;
+ break;
+ case MGN_2M:
+ ret = DESC90_RATE2M;
+ break;
+ case MGN_5_5M:
+ ret = DESC90_RATE5_5M;
+ break;
+ case MGN_11M:
+ ret = DESC90_RATE11M;
+ break;
+ case MGN_6M:
+ ret = DESC90_RATE6M;
+ break;
+ case MGN_9M:
+ ret = DESC90_RATE9M;
+ break;
+ case MGN_12M:
+ ret = DESC90_RATE12M;
+ break;
+ case MGN_18M:
+ ret = DESC90_RATE18M;
+ break;
+ case MGN_24M:
+ ret = DESC90_RATE24M;
+ break;
+ case MGN_36M:
+ ret = DESC90_RATE36M;
+ break;
+ case MGN_48M:
+ ret = DESC90_RATE48M;
+ break;
+ case MGN_54M:
+ ret = DESC90_RATE54M;
+ break;
+ case MGN_MCS0:
+ ret = DESC90_RATEMCS0;
+ break;
+ case MGN_MCS1:
+ ret = DESC90_RATEMCS1;
+ break;
+ case MGN_MCS2:
+ ret = DESC90_RATEMCS2;
+ break;
+ case MGN_MCS3:
+ ret = DESC90_RATEMCS3;
+ break;
+ case MGN_MCS4:
+ ret = DESC90_RATEMCS4;
+ break;
+ case MGN_MCS5:
+ ret = DESC90_RATEMCS5;
+ break;
+ case MGN_MCS6:
+ ret = DESC90_RATEMCS6;
+ break;
+ case MGN_MCS7:
+ ret = DESC90_RATEMCS7;
+ break;
+ case MGN_MCS8:
+ ret = DESC90_RATEMCS8;
+ break;
+ case MGN_MCS9:
+ ret = DESC90_RATEMCS9;
+ break;
+ case MGN_MCS10:
+ ret = DESC90_RATEMCS10;
+ break;
+ case MGN_MCS11:
+ ret = DESC90_RATEMCS11;
+ break;
+ case MGN_MCS12:
+ ret = DESC90_RATEMCS12;
+ break;
+ case MGN_MCS13:
+ ret = DESC90_RATEMCS13;
+ break;
+ case MGN_MCS14:
+ ret = DESC90_RATEMCS14;
+ break;
+ case MGN_MCS15:
+ ret = DESC90_RATEMCS15;
+ break;
+ case (0x80|0x20):
+ ret = DESC90_RATEMCS32;
+ break;
+ default:
+ break;
+ }
+ return ret;
+}
+
+static u8 rtl8192_MapHwQueueToFirmwareQueue(u8 QueueID, u8 priority)
+{
+ u8 QueueSelect = 0x0;
+
+ switch (QueueID) {
+ case BE_QUEUE:
+ QueueSelect = QSLT_BE;
+ break;
+
+ case BK_QUEUE:
+ QueueSelect = QSLT_BK;
+ break;
+
+ case VO_QUEUE:
+ QueueSelect = QSLT_VO;
+ break;
+
+ case VI_QUEUE:
+ QueueSelect = QSLT_VI;
+ break;
+ case MGNT_QUEUE:
+ QueueSelect = QSLT_MGNT;
+ break;
+ case BEACON_QUEUE:
+ QueueSelect = QSLT_BEACON;
+ break;
+ case TXCMD_QUEUE:
+ QueueSelect = QSLT_CMD;
+ break;
+ case HIGH_QUEUE:
+ QueueSelect = QSLT_HIGH;
+ break;
+ default:
+ RT_TRACE(COMP_ERR,
+ "TransmitTCB(): Impossible Queue Selection: %d\n",
+ QueueID);
+ break;
+ }
+ return QueueSelect;
+}
+
+void rtl8192_tx_fill_desc(struct net_device *dev, struct tx_desc *pdesc,
+ struct cb_desc *cb_desc, struct sk_buff *skb)
+{
+ struct r8192_priv *priv = rtllib_priv(dev);
+ dma_addr_t mapping = pci_map_single(priv->pdev, skb->data, skb->len,
+ PCI_DMA_TODEVICE);
+ struct tx_fwinfo_8190pci *pTxFwInfo = NULL;
+
+ pTxFwInfo = (struct tx_fwinfo_8190pci *)skb->data;
+ memset(pTxFwInfo, 0, sizeof(struct tx_fwinfo_8190pci));
+ pTxFwInfo->TxHT = (cb_desc->data_rate & 0x80) ? 1 : 0;
+ pTxFwInfo->TxRate = MRateToHwRate8190Pci((u8)cb_desc->data_rate);
+ pTxFwInfo->EnableCPUDur = cb_desc->bTxEnableFwCalcDur;
+ pTxFwInfo->Short = rtl8192_QueryIsShort(pTxFwInfo->TxHT,
+ pTxFwInfo->TxRate,
+ cb_desc);
+
+ if (pci_dma_mapping_error(priv->pdev, mapping))
+ RT_TRACE(COMP_ERR, "DMA Mapping error\n");
+ if (cb_desc->bAMPDUEnable) {
+ pTxFwInfo->AllowAggregation = 1;
+ pTxFwInfo->RxMF = cb_desc->ampdu_factor;
+ pTxFwInfo->RxAMD = cb_desc->ampdu_density;
+ } else {
+ pTxFwInfo->AllowAggregation = 0;
+ pTxFwInfo->RxMF = 0;
+ pTxFwInfo->RxAMD = 0;
+ }
+
+ pTxFwInfo->RtsEnable = (cb_desc->bRTSEnable) ? 1 : 0;
+ pTxFwInfo->CtsEnable = (cb_desc->bCTSEnable) ? 1 : 0;
+ pTxFwInfo->RtsSTBC = (cb_desc->bRTSSTBC) ? 1 : 0;
+ pTxFwInfo->RtsHT = (cb_desc->rts_rate&0x80) ? 1 : 0;
+ pTxFwInfo->RtsRate = MRateToHwRate8190Pci((u8)cb_desc->rts_rate);
+ pTxFwInfo->RtsBandwidth = 0;
+ pTxFwInfo->RtsSubcarrier = cb_desc->RTSSC;
+ pTxFwInfo->RtsShort = (pTxFwInfo->RtsHT == 0) ?
+ (cb_desc->bRTSUseShortPreamble ? 1 : 0) :
+ (cb_desc->bRTSUseShortGI ? 1 : 0);
+ if (priv->CurrentChannelBW == HT_CHANNEL_WIDTH_20_40) {
+ if (cb_desc->bPacketBW) {
+ pTxFwInfo->TxBandwidth = 1;
+ pTxFwInfo->TxSubCarrier = 0;
+ } else {
+ pTxFwInfo->TxBandwidth = 0;
+ pTxFwInfo->TxSubCarrier = priv->nCur40MhzPrimeSC;
+ }
+ } else {
+ pTxFwInfo->TxBandwidth = 0;
+ pTxFwInfo->TxSubCarrier = 0;
+ }
+
+ memset((u8 *)pdesc, 0, 12);
+ pdesc->LINIP = 0;
+ pdesc->CmdInit = 1;
+ pdesc->Offset = sizeof(struct tx_fwinfo_8190pci) + 8;
+ pdesc->PktSize = (u16)skb->len-sizeof(struct tx_fwinfo_8190pci);
+
+ pdesc->SecCAMID = 0;
+ pdesc->RATid = cb_desc->RATRIndex;
+
+
+ pdesc->NoEnc = 1;
+ pdesc->SecType = 0x0;
+ if (cb_desc->bHwSec) {
+ static u8 tmp;
+
+ if (!tmp) {
+ RT_TRACE(COMP_DBG, "==>================hw sec\n");
+ tmp = 1;
+ }
+ switch (priv->rtllib->pairwise_key_type) {
+ case KEY_TYPE_WEP40:
+ case KEY_TYPE_WEP104:
+ pdesc->SecType = 0x1;
+ pdesc->NoEnc = 0;
+ break;
+ case KEY_TYPE_TKIP:
+ pdesc->SecType = 0x2;
+ pdesc->NoEnc = 0;
+ break;
+ case KEY_TYPE_CCMP:
+ pdesc->SecType = 0x3;
+ pdesc->NoEnc = 0;
+ break;
+ case KEY_TYPE_NA:
+ pdesc->SecType = 0x0;
+ pdesc->NoEnc = 1;
+ break;
+ }
+ }
+
+ pdesc->PktId = 0x0;
+
+ pdesc->QueueSelect = rtl8192_MapHwQueueToFirmwareQueue(
+ cb_desc->queue_index,
+ cb_desc->priority);
+ pdesc->TxFWInfoSize = sizeof(struct tx_fwinfo_8190pci);
+
+ pdesc->DISFB = cb_desc->bTxDisableRateFallBack;
+ pdesc->USERATE = cb_desc->bTxUseDriverAssingedRate;
+
+ pdesc->FirstSeg = 1;
+ pdesc->LastSeg = 1;
+ pdesc->TxBufferSize = skb->len;
+
+ pdesc->TxBuffAddr = mapping;
+}
+
+void rtl8192_tx_fill_cmd_desc(struct net_device *dev,
+ struct tx_desc_cmd *entry,
+ struct cb_desc *cb_desc, struct sk_buff *skb)
+{
+ struct r8192_priv *priv = rtllib_priv(dev);
+ dma_addr_t mapping = pci_map_single(priv->pdev, skb->data, skb->len,
+ PCI_DMA_TODEVICE);
+
+ if (pci_dma_mapping_error(priv->pdev, mapping))
+ RT_TRACE(COMP_ERR, "DMA Mapping error\n");
+ memset(entry, 0, 12);
+ entry->LINIP = cb_desc->bLastIniPkt;
+ entry->FirstSeg = 1;
+ entry->LastSeg = 1;
+ if (cb_desc->bCmdOrInit == DESC_PACKET_TYPE_INIT) {
+ entry->CmdInit = DESC_PACKET_TYPE_INIT;
+ } else {
+ struct tx_desc *entry_tmp = (struct tx_desc *)entry;
+
+ entry_tmp->CmdInit = DESC_PACKET_TYPE_NORMAL;
+ entry_tmp->Offset = sizeof(struct tx_fwinfo_8190pci) + 8;
+ entry_tmp->PktSize = (u16)(cb_desc->pkt_size +
+ entry_tmp->Offset);
+ entry_tmp->QueueSelect = QSLT_CMD;
+ entry_tmp->TxFWInfoSize = 0x08;
+ entry_tmp->RATid = (u8)DESC_PACKET_TYPE_INIT;
+ }
+ entry->TxBufferSize = skb->len;
+ entry->TxBuffAddr = mapping;
+ entry->OWN = 1;
+}
+
+static u8 HwRateToMRate90(bool bIsHT, u8 rate)
+{
+ u8 ret_rate = 0x02;
+
+ if (!bIsHT) {
+ switch (rate) {
+ case DESC90_RATE1M:
+ ret_rate = MGN_1M;
+ break;
+ case DESC90_RATE2M:
+ ret_rate = MGN_2M;
+ break;
+ case DESC90_RATE5_5M:
+ ret_rate = MGN_5_5M;
+ break;
+ case DESC90_RATE11M:
+ ret_rate = MGN_11M;
+ break;
+ case DESC90_RATE6M:
+ ret_rate = MGN_6M;
+ break;
+ case DESC90_RATE9M:
+ ret_rate = MGN_9M;
+ break;
+ case DESC90_RATE12M:
+ ret_rate = MGN_12M;
+ break;
+ case DESC90_RATE18M:
+ ret_rate = MGN_18M;
+ break;
+ case DESC90_RATE24M:
+ ret_rate = MGN_24M;
+ break;
+ case DESC90_RATE36M:
+ ret_rate = MGN_36M;
+ break;
+ case DESC90_RATE48M:
+ ret_rate = MGN_48M;
+ break;
+ case DESC90_RATE54M:
+ ret_rate = MGN_54M;
+ break;
+
+ default:
+ RT_TRACE(COMP_RECV,
+ "HwRateToMRate90(): Non supportedRate [%x], bIsHT = %d!!!\n",
+ rate, bIsHT);
+ break;
+ }
+
+ } else {
+ switch (rate) {
+ case DESC90_RATEMCS0:
+ ret_rate = MGN_MCS0;
+ break;
+ case DESC90_RATEMCS1:
+ ret_rate = MGN_MCS1;
+ break;
+ case DESC90_RATEMCS2:
+ ret_rate = MGN_MCS2;
+ break;
+ case DESC90_RATEMCS3:
+ ret_rate = MGN_MCS3;
+ break;
+ case DESC90_RATEMCS4:
+ ret_rate = MGN_MCS4;
+ break;
+ case DESC90_RATEMCS5:
+ ret_rate = MGN_MCS5;
+ break;
+ case DESC90_RATEMCS6:
+ ret_rate = MGN_MCS6;
+ break;
+ case DESC90_RATEMCS7:
+ ret_rate = MGN_MCS7;
+ break;
+ case DESC90_RATEMCS8:
+ ret_rate = MGN_MCS8;
+ break;
+ case DESC90_RATEMCS9:
+ ret_rate = MGN_MCS9;
+ break;
+ case DESC90_RATEMCS10:
+ ret_rate = MGN_MCS10;
+ break;
+ case DESC90_RATEMCS11:
+ ret_rate = MGN_MCS11;
+ break;
+ case DESC90_RATEMCS12:
+ ret_rate = MGN_MCS12;
+ break;
+ case DESC90_RATEMCS13:
+ ret_rate = MGN_MCS13;
+ break;
+ case DESC90_RATEMCS14:
+ ret_rate = MGN_MCS14;
+ break;
+ case DESC90_RATEMCS15:
+ ret_rate = MGN_MCS15;
+ break;
+ case DESC90_RATEMCS32:
+ ret_rate = (0x80|0x20);
+ break;
+
+ default:
+ RT_TRACE(COMP_RECV,
+ "HwRateToMRate90(): Non supported Rate [%x], bIsHT = %d!!!\n",
+ rate, bIsHT);
+ break;
+ }
+ }
+
+ return ret_rate;
+}
+
+static long rtl8192_signal_scale_mapping(struct r8192_priv *priv, long currsig)
+{
+ long retsig;
+
+ if (currsig >= 61 && currsig <= 100)
+ retsig = 90 + ((currsig - 60) / 4);
+ else if (currsig >= 41 && currsig <= 60)
+ retsig = 78 + ((currsig - 40) / 2);
+ else if (currsig >= 31 && currsig <= 40)
+ retsig = 66 + (currsig - 30);
+ else if (currsig >= 21 && currsig <= 30)
+ retsig = 54 + (currsig - 20);
+ else if (currsig >= 5 && currsig <= 20)
+ retsig = 42 + (((currsig - 5) * 2) / 3);
+ else if (currsig == 4)
+ retsig = 36;
+ else if (currsig == 3)
+ retsig = 27;
+ else if (currsig == 2)
+ retsig = 18;
+ else if (currsig == 1)
+ retsig = 9;
+ else
+ retsig = currsig;
+
+ return retsig;
+}
+
+
+#define rx_hal_is_cck_rate(_pdrvinfo)\
+ ((_pdrvinfo->RxRate == DESC90_RATE1M ||\
+ _pdrvinfo->RxRate == DESC90_RATE2M ||\
+ _pdrvinfo->RxRate == DESC90_RATE5_5M ||\
+ _pdrvinfo->RxRate == DESC90_RATE11M) &&\
+ !_pdrvinfo->RxHT)
+
+static void rtl8192_query_rxphystatus(
+ struct r8192_priv *priv,
+ struct rtllib_rx_stats *pstats,
+ struct rx_desc *pdesc,
+ struct rx_fwinfo *pdrvinfo,
+ struct rtllib_rx_stats *precord_stats,
+ bool bpacket_match_bssid,
+ bool bpacket_toself,
+ bool bPacketBeacon,
+ bool bToSelfBA
+ )
+{
+ struct phy_sts_ofdm_819xpci *pofdm_buf;
+ struct phy_sts_cck_819xpci *pcck_buf;
+ struct phy_ofdm_rx_status_rxsc_sgien_exintfflag *prxsc;
+ u8 *prxpkt;
+ u8 i, max_spatial_stream, tmp_rxsnr, tmp_rxevm, rxsc_sgien_exflg;
+ char rx_pwr[4], rx_pwr_all = 0;
+ char rx_snrX, rx_evmX;
+ u8 evm, pwdb_all;
+ u32 RSSI, total_rssi = 0;
+ u8 is_cck_rate = 0;
+ u8 rf_rx_num = 0;
+ static u8 check_reg824;
+ static u32 reg824_bit9;
+
+ priv->stats.numqry_phystatus++;
+
+ is_cck_rate = rx_hal_is_cck_rate(pdrvinfo);
+ memset(precord_stats, 0, sizeof(struct rtllib_rx_stats));
+ pstats->bPacketMatchBSSID = precord_stats->bPacketMatchBSSID =
+ bpacket_match_bssid;
+ pstats->bPacketToSelf = precord_stats->bPacketToSelf = bpacket_toself;
+ pstats->bIsCCK = precord_stats->bIsCCK = is_cck_rate;
+ pstats->bPacketBeacon = precord_stats->bPacketBeacon = bPacketBeacon;
+ pstats->bToSelfBA = precord_stats->bToSelfBA = bToSelfBA;
+ if (check_reg824 == 0) {
+ reg824_bit9 = rtl8192_QueryBBReg(priv->rtllib->dev,
+ rFPGA0_XA_HSSIParameter2, 0x200);
+ check_reg824 = 1;
+ }
+
+
+ prxpkt = (u8 *)pdrvinfo;
+
+ prxpkt += sizeof(struct rx_fwinfo);
+
+ pcck_buf = (struct phy_sts_cck_819xpci *)prxpkt;
+ pofdm_buf = (struct phy_sts_ofdm_819xpci *)prxpkt;
+
+ pstats->RxMIMOSignalQuality[0] = -1;
+ pstats->RxMIMOSignalQuality[1] = -1;
+ precord_stats->RxMIMOSignalQuality[0] = -1;
+ precord_stats->RxMIMOSignalQuality[1] = -1;
+
+ if (is_cck_rate) {
+ u8 report;
+
+ priv->stats.numqry_phystatusCCK++;
+ if (!reg824_bit9) {
+ report = pcck_buf->cck_agc_rpt & 0xc0;
+ report >>= 6;
+ switch (report) {
+ case 0x3:
+ rx_pwr_all = -35 - (pcck_buf->cck_agc_rpt &
+ 0x3e);
+ break;
+ case 0x2:
+ rx_pwr_all = -23 - (pcck_buf->cck_agc_rpt &
+ 0x3e);
+ break;
+ case 0x1:
+ rx_pwr_all = -11 - (pcck_buf->cck_agc_rpt &
+ 0x3e);
+ break;
+ case 0x0:
+ rx_pwr_all = 8 - (pcck_buf->cck_agc_rpt & 0x3e);
+ break;
+ }
+ } else {
+ report = pcck_buf->cck_agc_rpt & 0x60;
+ report >>= 5;
+ switch (report) {
+ case 0x3:
+ rx_pwr_all = -35 -
+ ((pcck_buf->cck_agc_rpt &
+ 0x1f) << 1);
+ break;
+ case 0x2:
+ rx_pwr_all = -23 -
+ ((pcck_buf->cck_agc_rpt &
+ 0x1f) << 1);
+ break;
+ case 0x1:
+ rx_pwr_all = -11 -
+ ((pcck_buf->cck_agc_rpt &
+ 0x1f) << 1);
+ break;
+ case 0x0:
+ rx_pwr_all = -8 -
+ ((pcck_buf->cck_agc_rpt &
+ 0x1f) << 1);
+ break;
+ }
+ }
+
+ pwdb_all = rtl819x_query_rxpwrpercentage(rx_pwr_all);
+ pstats->RxPWDBAll = precord_stats->RxPWDBAll = pwdb_all;
+ pstats->RecvSignalPower = rx_pwr_all;
+
+ if (bpacket_match_bssid) {
+ u8 sq;
+
+ if (pstats->RxPWDBAll > 40) {
+ sq = 100;
+ } else {
+ sq = pcck_buf->sq_rpt;
+
+ if (pcck_buf->sq_rpt > 64)
+ sq = 0;
+ else if (pcck_buf->sq_rpt < 20)
+ sq = 100;
+ else
+ sq = ((64-sq) * 100) / 44;
+ }
+ pstats->SignalQuality = sq;
+ precord_stats->SignalQuality = sq;
+ pstats->RxMIMOSignalQuality[0] = sq;
+ precord_stats->RxMIMOSignalQuality[0] = sq;
+ pstats->RxMIMOSignalQuality[1] = -1;
+ precord_stats->RxMIMOSignalQuality[1] = -1;
+ }
+ } else {
+ priv->stats.numqry_phystatusHT++;
+ for (i = RF90_PATH_A; i < RF90_PATH_MAX; i++) {
+ if (priv->brfpath_rxenable[i])
+ rf_rx_num++;
+
+ rx_pwr[i] = ((pofdm_buf->trsw_gain_X[i] & 0x3F) *
+ 2) - 110;
+
+ tmp_rxsnr = pofdm_buf->rxsnr_X[i];
+ rx_snrX = (char)(tmp_rxsnr);
+ rx_snrX /= 2;
+ priv->stats.rxSNRdB[i] = (long)rx_snrX;
+
+ RSSI = rtl819x_query_rxpwrpercentage(rx_pwr[i]);
+ if (priv->brfpath_rxenable[i])
+ total_rssi += RSSI;
+
+ if (bpacket_match_bssid) {
+ pstats->RxMIMOSignalStrength[i] = (u8) RSSI;
+ precord_stats->RxMIMOSignalStrength[i] =
+ (u8) RSSI;
+ }
+ }
+
+
+ rx_pwr_all = (((pofdm_buf->pwdb_all) >> 1) & 0x7f) - 106;
+ pwdb_all = rtl819x_query_rxpwrpercentage(rx_pwr_all);
+
+ pstats->RxPWDBAll = precord_stats->RxPWDBAll = pwdb_all;
+ pstats->RxPower = precord_stats->RxPower = rx_pwr_all;
+ pstats->RecvSignalPower = rx_pwr_all;
+ if (pdrvinfo->RxHT && pdrvinfo->RxRate >= DESC90_RATEMCS8 &&
+ pdrvinfo->RxRate <= DESC90_RATEMCS15)
+ max_spatial_stream = 2;
+ else
+ max_spatial_stream = 1;
+
+ for (i = 0; i < max_spatial_stream; i++) {
+ tmp_rxevm = pofdm_buf->rxevm_X[i];
+ rx_evmX = (char)(tmp_rxevm);
+
+ rx_evmX /= 2;
+
+ evm = rtl819x_evm_dbtopercentage(rx_evmX);
+ if (bpacket_match_bssid) {
+ if (i == 0) {
+ pstats->SignalQuality = (u8)(evm &
+ 0xff);
+ precord_stats->SignalQuality = (u8)(evm
+ & 0xff);
+ }
+ pstats->RxMIMOSignalQuality[i] = (u8)(evm &
+ 0xff);
+ precord_stats->RxMIMOSignalQuality[i] = (u8)(evm
+ & 0xff);
+ }
+ }
+
+
+ rxsc_sgien_exflg = pofdm_buf->rxsc_sgien_exflg;
+ prxsc = (struct phy_ofdm_rx_status_rxsc_sgien_exintfflag *)
+ &rxsc_sgien_exflg;
+ if (pdrvinfo->BW)
+ priv->stats.received_bwtype[1+prxsc->rxsc]++;
+ else
+ priv->stats.received_bwtype[0]++;
+ }
+
+ if (is_cck_rate) {
+ pstats->SignalStrength = precord_stats->SignalStrength =
+ (u8)(rtl8192_signal_scale_mapping(priv,
+ (long)pwdb_all));
+
+ } else {
+ if (rf_rx_num != 0)
+ pstats->SignalStrength = precord_stats->SignalStrength =
+ (u8)(rtl8192_signal_scale_mapping(priv,
+ (long)(total_rssi /= rf_rx_num)));
+ }
+}
+
+static void rtl8192_process_phyinfo(struct r8192_priv *priv, u8 *buffer,
+ struct rtllib_rx_stats *prev_st,
+ struct rtllib_rx_stats *curr_st)
+{
+ bool bcheck = false;
+ u8 rfpath;
+ u32 ij, tmp_val;
+ static u32 slide_rssi_index, slide_rssi_statistics;
+ static u32 slide_evm_index, slide_evm_statistics;
+ static u32 last_rssi, last_evm;
+ static u32 slide_beacon_adc_pwdb_index;
+ static u32 slide_beacon_adc_pwdb_statistics;
+ static u32 last_beacon_adc_pwdb;
+ struct rtllib_hdr_3addr *hdr;
+ u16 sc;
+ unsigned int frag, seq;
+
+ hdr = (struct rtllib_hdr_3addr *)buffer;
+ sc = le16_to_cpu(hdr->seq_ctl);
+ frag = WLAN_GET_SEQ_FRAG(sc);
+ seq = WLAN_GET_SEQ_SEQ(sc);
+ curr_st->Seq_Num = seq;
+ if (!prev_st->bIsAMPDU)
+ bcheck = true;
+
+ if (slide_rssi_statistics++ >= PHY_RSSI_SLID_WIN_MAX) {
+ slide_rssi_statistics = PHY_RSSI_SLID_WIN_MAX;
+ last_rssi = priv->stats.slide_signal_strength[slide_rssi_index];
+ priv->stats.slide_rssi_total -= last_rssi;
+ }
+ priv->stats.slide_rssi_total += prev_st->SignalStrength;
+
+ priv->stats.slide_signal_strength[slide_rssi_index++] =
+ prev_st->SignalStrength;
+ if (slide_rssi_index >= PHY_RSSI_SLID_WIN_MAX)
+ slide_rssi_index = 0;
+
+ tmp_val = priv->stats.slide_rssi_total/slide_rssi_statistics;
+ priv->stats.signal_strength = rtl819x_translate_todbm(priv,
+ (u8)tmp_val);
+ curr_st->rssi = priv->stats.signal_strength;
+ if (!prev_st->bPacketMatchBSSID) {
+ if (!prev_st->bToSelfBA)
+ return;
+ }
+
+ if (!bcheck)
+ return;
+
+ rtl819x_process_cck_rxpathsel(priv, prev_st);
+
+ priv->stats.num_process_phyinfo++;
+ if (!prev_st->bIsCCK && prev_st->bPacketToSelf) {
+ for (rfpath = RF90_PATH_A; rfpath < RF90_PATH_C; rfpath++) {
+ if (!rtl8192_phy_CheckIsLegalRFPath(priv->rtllib->dev,
+ rfpath))
+ continue;
+ RT_TRACE(COMP_DBG,
+ "Jacken -> pPreviousstats->RxMIMOSignalStrength[rfpath] = %d\n",
+ prev_st->RxMIMOSignalStrength[rfpath]);
+ if (priv->stats.rx_rssi_percentage[rfpath] == 0) {
+ priv->stats.rx_rssi_percentage[rfpath] =
+ prev_st->RxMIMOSignalStrength[rfpath];
+ }
+ if (prev_st->RxMIMOSignalStrength[rfpath] >
+ priv->stats.rx_rssi_percentage[rfpath]) {
+ priv->stats.rx_rssi_percentage[rfpath] =
+ ((priv->stats.rx_rssi_percentage[rfpath]
+ * (RX_SMOOTH - 1)) +
+ (prev_st->RxMIMOSignalStrength
+ [rfpath])) / (RX_SMOOTH);
+ priv->stats.rx_rssi_percentage[rfpath] =
+ priv->stats.rx_rssi_percentage[rfpath]
+ + 1;
+ } else {
+ priv->stats.rx_rssi_percentage[rfpath] =
+ ((priv->stats.rx_rssi_percentage[rfpath] *
+ (RX_SMOOTH-1)) +
+ (prev_st->RxMIMOSignalStrength[rfpath])) /
+ (RX_SMOOTH);
+ }
+ RT_TRACE(COMP_DBG,
+ "Jacken -> priv->RxStats.RxRSSIPercentage[rfPath] = %d\n",
+ priv->stats.rx_rssi_percentage[rfpath]);
+ }
+ }
+
+
+ if (prev_st->bPacketBeacon) {
+ if (slide_beacon_adc_pwdb_statistics++ >=
+ PHY_Beacon_RSSI_SLID_WIN_MAX) {
+ slide_beacon_adc_pwdb_statistics =
+ PHY_Beacon_RSSI_SLID_WIN_MAX;
+ last_beacon_adc_pwdb = priv->stats.Slide_Beacon_pwdb
+ [slide_beacon_adc_pwdb_index];
+ priv->stats.Slide_Beacon_Total -= last_beacon_adc_pwdb;
+ }
+ priv->stats.Slide_Beacon_Total += prev_st->RxPWDBAll;
+ priv->stats.Slide_Beacon_pwdb[slide_beacon_adc_pwdb_index] =
+ prev_st->RxPWDBAll;
+ slide_beacon_adc_pwdb_index++;
+ if (slide_beacon_adc_pwdb_index >= PHY_Beacon_RSSI_SLID_WIN_MAX)
+ slide_beacon_adc_pwdb_index = 0;
+ prev_st->RxPWDBAll = priv->stats.Slide_Beacon_Total /
+ slide_beacon_adc_pwdb_statistics;
+ if (prev_st->RxPWDBAll >= 3)
+ prev_st->RxPWDBAll -= 3;
+ }
+
+ RT_TRACE(COMP_RXDESC, "Smooth %s PWDB = %d\n",
+ prev_st->bIsCCK ? "CCK" : "OFDM",
+ prev_st->RxPWDBAll);
+
+ if (prev_st->bPacketToSelf || prev_st->bPacketBeacon ||
+ prev_st->bToSelfBA) {
+ if (priv->undecorated_smoothed_pwdb < 0)
+ priv->undecorated_smoothed_pwdb = prev_st->RxPWDBAll;
+ if (prev_st->RxPWDBAll > (u32)priv->undecorated_smoothed_pwdb) {
+ priv->undecorated_smoothed_pwdb =
+ (((priv->undecorated_smoothed_pwdb) *
+ (RX_SMOOTH-1)) +
+ (prev_st->RxPWDBAll)) / (RX_SMOOTH);
+ priv->undecorated_smoothed_pwdb =
+ priv->undecorated_smoothed_pwdb + 1;
+ } else {
+ priv->undecorated_smoothed_pwdb =
+ (((priv->undecorated_smoothed_pwdb) *
+ (RX_SMOOTH-1)) +
+ (prev_st->RxPWDBAll)) / (RX_SMOOTH);
+ }
+ rtl819x_update_rxsignalstatistics8190pci(priv, prev_st);
+ }
+
+ if (prev_st->SignalQuality != 0) {
+ if (prev_st->bPacketToSelf || prev_st->bPacketBeacon ||
+ prev_st->bToSelfBA) {
+ if (slide_evm_statistics++ >= PHY_RSSI_SLID_WIN_MAX) {
+ slide_evm_statistics = PHY_RSSI_SLID_WIN_MAX;
+ last_evm =
+ priv->stats.slide_evm[slide_evm_index];
+ priv->stats.slide_evm_total -= last_evm;
+ }
+
+ priv->stats.slide_evm_total += prev_st->SignalQuality;
+
+ priv->stats.slide_evm[slide_evm_index++] =
+ prev_st->SignalQuality;
+ if (slide_evm_index >= PHY_RSSI_SLID_WIN_MAX)
+ slide_evm_index = 0;
+
+ tmp_val = priv->stats.slide_evm_total /
+ slide_evm_statistics;
+ priv->stats.signal_quality = tmp_val;
+ priv->stats.last_signal_strength_inpercent = tmp_val;
+ }
+
+ if (prev_st->bPacketToSelf ||
+ prev_st->bPacketBeacon ||
+ prev_st->bToSelfBA) {
+ for (ij = 0; ij < 2; ij++) {
+ if (prev_st->RxMIMOSignalQuality[ij] != -1) {
+ if (priv->stats.rx_evm_percentage[ij] == 0)
+ priv->stats.rx_evm_percentage[ij] =
+ prev_st->RxMIMOSignalQuality[ij];
+ priv->stats.rx_evm_percentage[ij] =
+ ((priv->stats.rx_evm_percentage[ij] *
+ (RX_SMOOTH - 1)) +
+ (prev_st->RxMIMOSignalQuality[ij])) /
+ (RX_SMOOTH);
+ }
+ }
+ }
+ }
+}
+
+static void rtl8192_TranslateRxSignalStuff(struct net_device *dev,
+ struct sk_buff *skb,
+ struct rtllib_rx_stats *pstats,
+ struct rx_desc *pdesc,
+ struct rx_fwinfo *pdrvinfo)
+{
+ struct r8192_priv *priv = (struct r8192_priv *)rtllib_priv(dev);
+ bool bpacket_match_bssid, bpacket_toself;
+ bool bPacketBeacon = false;
+ struct rtllib_hdr_3addr *hdr;
+ bool bToSelfBA = false;
+ static struct rtllib_rx_stats previous_stats;
+ u16 fc, type;
+ u8 *tmp_buf;
+ u8 *praddr;
+
+ tmp_buf = skb->data + pstats->RxDrvInfoSize + pstats->RxBufShift;
+
+ hdr = (struct rtllib_hdr_3addr *)tmp_buf;
+ fc = le16_to_cpu(hdr->frame_ctl);
+ type = WLAN_FC_GET_TYPE(fc);
+ praddr = hdr->addr1;
+
+ bpacket_match_bssid =
+ ((RTLLIB_FTYPE_CTL != type) &&
+ ether_addr_equal(priv->rtllib->current_network.bssid,
+ (fc & RTLLIB_FCTL_TODS) ? hdr->addr1 :
+ (fc & RTLLIB_FCTL_FROMDS) ? hdr->addr2 :
+ hdr->addr3) &&
+ (!pstats->bHwError) && (!pstats->bCRC) && (!pstats->bICV));
+ bpacket_toself = bpacket_match_bssid && /* check this */
+ ether_addr_equal(praddr, priv->rtllib->dev->dev_addr);
+ if (WLAN_FC_GET_FRAMETYPE(fc) == RTLLIB_STYPE_BEACON)
+ bPacketBeacon = true;
+ if (bpacket_match_bssid)
+ priv->stats.numpacket_matchbssid++;
+ if (bpacket_toself)
+ priv->stats.numpacket_toself++;
+ rtl8192_process_phyinfo(priv, tmp_buf, &previous_stats, pstats);
+ rtl8192_query_rxphystatus(priv, pstats, pdesc, pdrvinfo,
+ &previous_stats, bpacket_match_bssid,
+ bpacket_toself, bPacketBeacon, bToSelfBA);
+ rtl8192_record_rxdesc_forlateruse(pstats, &previous_stats);
+}
+
+static void rtl8192_UpdateReceivedRateHistogramStatistics(
+ struct net_device *dev,
+ struct rtllib_rx_stats *pstats)
+{
+ struct r8192_priv *priv = (struct r8192_priv *)rtllib_priv(dev);
+ u32 rcvType = 1;
+ u32 rateIndex;
+ u32 preamble_guardinterval;
+
+ if (pstats->bCRC)
+ rcvType = 2;
+ else if (pstats->bICV)
+ rcvType = 3;
+
+ if (pstats->bShortPreamble)
+ preamble_guardinterval = 1;
+ else
+ preamble_guardinterval = 0;
+
+ switch (pstats->rate) {
+ case MGN_1M:
+ rateIndex = 0;
+ break;
+ case MGN_2M:
+ rateIndex = 1;
+ break;
+ case MGN_5_5M:
+ rateIndex = 2;
+ break;
+ case MGN_11M:
+ rateIndex = 3;
+ break;
+ case MGN_6M:
+ rateIndex = 4;
+ break;
+ case MGN_9M:
+ rateIndex = 5;
+ break;
+ case MGN_12M:
+ rateIndex = 6;
+ break;
+ case MGN_18M:
+ rateIndex = 7;
+ break;
+ case MGN_24M:
+ rateIndex = 8;
+ break;
+ case MGN_36M:
+ rateIndex = 9;
+ break;
+ case MGN_48M:
+ rateIndex = 10;
+ break;
+ case MGN_54M:
+ rateIndex = 11;
+ break;
+ case MGN_MCS0:
+ rateIndex = 12;
+ break;
+ case MGN_MCS1:
+ rateIndex = 13;
+ break;
+ case MGN_MCS2:
+ rateIndex = 14;
+ break;
+ case MGN_MCS3:
+ rateIndex = 15;
+ break;
+ case MGN_MCS4:
+ rateIndex = 16;
+ break;
+ case MGN_MCS5:
+ rateIndex = 17;
+ break;
+ case MGN_MCS6:
+ rateIndex = 18;
+ break;
+ case MGN_MCS7:
+ rateIndex = 19;
+ break;
+ case MGN_MCS8:
+ rateIndex = 20;
+ break;
+ case MGN_MCS9:
+ rateIndex = 21;
+ break;
+ case MGN_MCS10:
+ rateIndex = 22;
+ break;
+ case MGN_MCS11:
+ rateIndex = 23;
+ break;
+ case MGN_MCS12:
+ rateIndex = 24;
+ break;
+ case MGN_MCS13:
+ rateIndex = 25;
+ break;
+ case MGN_MCS14:
+ rateIndex = 26;
+ break;
+ case MGN_MCS15:
+ rateIndex = 27;
+ break;
+ default:
+ rateIndex = 28;
+ break;
+ }
+ priv->stats.received_preamble_GI[preamble_guardinterval][rateIndex]++;
+ priv->stats.received_rate_histogram[0][rateIndex]++;
+ priv->stats.received_rate_histogram[rcvType][rateIndex]++;
+}
+
+bool rtl8192_rx_query_status_desc(struct net_device *dev,
+ struct rtllib_rx_stats *stats,
+ struct rx_desc *pdesc,
+ struct sk_buff *skb)
+{
+ struct r8192_priv *priv = rtllib_priv(dev);
+ struct rx_fwinfo *pDrvInfo = NULL;
+
+ stats->bICV = pdesc->ICV;
+ stats->bCRC = pdesc->CRC32;
+ stats->bHwError = pdesc->CRC32 | pdesc->ICV;
+
+ stats->Length = pdesc->Length;
+ if (stats->Length < 24)
+ stats->bHwError |= 1;
+
+ if (stats->bHwError) {
+ stats->bShift = false;
+
+ if (pdesc->CRC32) {
+ if (pdesc->Length < 500)
+ priv->stats.rxcrcerrmin++;
+ else if (pdesc->Length > 1000)
+ priv->stats.rxcrcerrmax++;
+ else
+ priv->stats.rxcrcerrmid++;
+ }
+ return false;
+ }
+
+ stats->RxDrvInfoSize = pdesc->RxDrvInfoSize;
+ stats->RxBufShift = ((pdesc->Shift)&0x03);
+ stats->Decrypted = !pdesc->SWDec;
+
+ pDrvInfo = (struct rx_fwinfo *)(skb->data + stats->RxBufShift);
+
+ stats->rate = HwRateToMRate90((bool)pDrvInfo->RxHT,
+ (u8)pDrvInfo->RxRate);
+ stats->bShortPreamble = pDrvInfo->SPLCP;
+
+ rtl8192_UpdateReceivedRateHistogramStatistics(dev, stats);
+
+ stats->bIsAMPDU = (pDrvInfo->PartAggr == 1);
+ stats->bFirstMPDU = (pDrvInfo->PartAggr == 1) &&
+ (pDrvInfo->FirstAGGR == 1);
+
+ stats->TimeStampLow = pDrvInfo->TSFL;
+ stats->TimeStampHigh = read_nic_dword(dev, TSFR+4);
+
+ rtl819x_UpdateRxPktTimeStamp(dev, stats);
+
+ if ((stats->RxBufShift + stats->RxDrvInfoSize) > 0)
+ stats->bShift = 1;
+
+ stats->RxIs40MHzPacket = pDrvInfo->BW;
+
+ rtl8192_TranslateRxSignalStuff(dev, skb, stats, pdesc,
+ pDrvInfo);
+
+ if (pDrvInfo->FirstAGGR == 1 || pDrvInfo->PartAggr == 1)
+ RT_TRACE(COMP_RXDESC,
+ "pDrvInfo->FirstAGGR = %d, pDrvInfo->PartAggr = %d\n",
+ pDrvInfo->FirstAGGR, pDrvInfo->PartAggr);
+ skb_trim(skb, skb->len - 4/*sCrcLng*/);
+
+
+ stats->packetlength = stats->Length-4;
+ stats->fraglength = stats->packetlength;
+ stats->fragoffset = 0;
+ stats->ntotalfrag = 1;
+ return true;
+}
+
+void rtl8192_halt_adapter(struct net_device *dev, bool reset)
+{
+ struct r8192_priv *priv = rtllib_priv(dev);
+ int i;
+ u8 OpMode;
+ u8 u1bTmp;
+ u32 ulRegRead;
+
+ OpMode = RT_OP_MODE_NO_LINK;
+ priv->rtllib->SetHwRegHandler(dev, HW_VAR_MEDIA_STATUS, &OpMode);
+
+ if (!priv->rtllib->bSupportRemoteWakeUp) {
+ u1bTmp = 0x0;
+ write_nic_byte(dev, CMDR, u1bTmp);
+ }
+
+ mdelay(20);
+
+ if (!reset) {
+ mdelay(150);
+
+ priv->bHwRfOffAction = 2;
+
+ if (!priv->rtllib->bSupportRemoteWakeUp) {
+ PHY_SetRtl8192eRfOff(dev);
+ ulRegRead = read_nic_dword(dev, CPU_GEN);
+ ulRegRead |= CPU_GEN_SYSTEM_RESET;
+ write_nic_dword(dev, CPU_GEN, ulRegRead);
+ } else {
+ write_nic_dword(dev, WFCRC0, 0xffffffff);
+ write_nic_dword(dev, WFCRC1, 0xffffffff);
+ write_nic_dword(dev, WFCRC2, 0xffffffff);
+
+
+ write_nic_byte(dev, PMR, 0x5);
+ write_nic_byte(dev, MacBlkCtrl, 0xa);
+ }
+ }
+
+ for (i = 0; i < MAX_QUEUE_SIZE; i++)
+ skb_queue_purge(&priv->rtllib->skb_waitQ[i]);
+ for (i = 0; i < MAX_QUEUE_SIZE; i++)
+ skb_queue_purge(&priv->rtllib->skb_aggQ[i]);
+
+ skb_queue_purge(&priv->skb_queue);
+}
+
+void rtl8192_update_ratr_table(struct net_device *dev)
+{
+ struct r8192_priv *priv = rtllib_priv(dev);
+ struct rtllib_device *ieee = priv->rtllib;
+ u8 *pMcsRate = ieee->dot11HTOperationalRateSet;
+ u32 ratr_value = 0;
+ u16 rate_config = 0;
+ u8 rate_index = 0;
+
+ rtl8192_config_rate(dev, &rate_config);
+ ratr_value = rate_config | *pMcsRate << 12;
+ switch (ieee->mode) {
+ case IEEE_A:
+ ratr_value &= 0x00000FF0;
+ break;
+ case IEEE_B:
+ ratr_value &= 0x0000000F;
+ break;
+ case IEEE_G:
+ case IEEE_G|IEEE_B:
+ ratr_value &= 0x00000FF7;
+ break;
+ case IEEE_N_24G:
+ case IEEE_N_5G:
+ if (ieee->pHTInfo->PeerMimoPs == 0) {
+ ratr_value &= 0x0007F007;
+ } else {
+ if (priv->rf_type == RF_1T2R)
+ ratr_value &= 0x000FF007;
+ else
+ ratr_value &= 0x0F81F007;
+ }
+ break;
+ default:
+ break;
+ }
+ ratr_value &= 0x0FFFFFFF;
+ if (ieee->pHTInfo->bCurTxBW40MHz &&
+ ieee->pHTInfo->bCurShortGI40MHz)
+ ratr_value |= 0x80000000;
+ else if (!ieee->pHTInfo->bCurTxBW40MHz &&
+ ieee->pHTInfo->bCurShortGI20MHz)
+ ratr_value |= 0x80000000;
+ write_nic_dword(dev, RATR0+rate_index*4, ratr_value);
+ write_nic_byte(dev, UFWP, 1);
+}
+
+void
+rtl8192_InitializeVariables(struct net_device *dev)
+{
+ struct r8192_priv *priv = rtllib_priv(dev);
+
+ strcpy(priv->nick, "rtl8192E");
+
+ priv->rtllib->softmac_features = IEEE_SOFTMAC_SCAN |
+ IEEE_SOFTMAC_ASSOCIATE | IEEE_SOFTMAC_PROBERQ |
+ IEEE_SOFTMAC_PROBERS | IEEE_SOFTMAC_TX_QUEUE;
+
+ priv->rtllib->tx_headroom = sizeof(struct tx_fwinfo_8190pci);
+
+ priv->ShortRetryLimit = 0x30;
+ priv->LongRetryLimit = 0x30;
+
+ priv->EarlyRxThreshold = 7;
+ priv->pwrGroupCnt = 0;
+
+ priv->bIgnoreSilentReset = false;
+ priv->enable_gpio0 = 0;
+
+ priv->TransmitConfig = 0;
+
+ priv->ReceiveConfig = RCR_ADD3 |
+ RCR_AMF | RCR_ADF |
+ RCR_AICV |
+ RCR_AB | RCR_AM | RCR_APM |
+ RCR_AAP | ((u32)7<<RCR_MXDMA_OFFSET) |
+ ((u32)7 << RCR_FIFO_OFFSET) | RCR_ONLYERLPKT;
+
+ priv->irq_mask[0] = (u32)(IMR_ROK | IMR_VODOK | IMR_VIDOK |
+ IMR_BEDOK | IMR_BKDOK | IMR_HCCADOK |
+ IMR_MGNTDOK | IMR_COMDOK | IMR_HIGHDOK |
+ IMR_BDOK | IMR_RXCMDOK | IMR_TIMEOUT0 |
+ IMR_RDU | IMR_RXFOVW | IMR_TXFOVW |
+ IMR_BcnInt | IMR_TBDOK | IMR_TBDER);
+
+
+ priv->MidHighPwrTHR_L1 = 0x3B;
+ priv->MidHighPwrTHR_L2 = 0x40;
+ priv->PwrDomainProtect = false;
+
+ priv->bfirst_after_down = false;
+}
+
+void rtl8192_EnableInterrupt(struct net_device *dev)
+{
+ struct r8192_priv *priv = (struct r8192_priv *)rtllib_priv(dev);
+
+ priv->irq_enabled = 1;
+
+ write_nic_dword(dev, INTA_MASK, priv->irq_mask[0]);
+
+}
+
+void rtl8192_DisableInterrupt(struct net_device *dev)
+{
+ struct r8192_priv *priv = (struct r8192_priv *)rtllib_priv(dev);
+
+ write_nic_dword(dev, INTA_MASK, 0);
+
+ priv->irq_enabled = 0;
+}
+
+void rtl8192_ClearInterrupt(struct net_device *dev)
+{
+ u32 tmp = 0;
+
+ tmp = read_nic_dword(dev, ISR);
+ write_nic_dword(dev, ISR, tmp);
+}
+
+
+void rtl8192_enable_rx(struct net_device *dev)
+{
+ struct r8192_priv *priv = (struct r8192_priv *)rtllib_priv(dev);
+
+ write_nic_dword(dev, RDQDA, priv->rx_ring_dma[RX_MPDU_QUEUE]);
+}
+
+static const u32 TX_DESC_BASE[] = {
+ BKQDA, BEQDA, VIQDA, VOQDA, HCCAQDA, CQDA, MQDA, HQDA, BQDA
+};
+
+void rtl8192_enable_tx(struct net_device *dev)
+{
+ struct r8192_priv *priv = (struct r8192_priv *)rtllib_priv(dev);
+ u32 i;
+
+ for (i = 0; i < MAX_TX_QUEUE_COUNT; i++)
+ write_nic_dword(dev, TX_DESC_BASE[i], priv->tx_ring[i].dma);
+}
+
+
+void rtl8192_interrupt_recognized(struct net_device *dev, u32 *p_inta,
+ u32 *p_intb)
+{
+ *p_inta = read_nic_dword(dev, ISR);
+ write_nic_dword(dev, ISR, *p_inta);
+}
+
+bool rtl8192_HalRxCheckStuck(struct net_device *dev)
+{
+ struct r8192_priv *priv = rtllib_priv(dev);
+ u16 RegRxCounter = read_nic_word(dev, 0x130);
+ bool bStuck = false;
+ static u8 rx_chk_cnt;
+ u32 SlotIndex = 0, TotalRxStuckCount = 0;
+ u8 i;
+ u8 SilentResetRxSoltNum = 4;
+
+ RT_TRACE(COMP_RESET, "%s(): RegRxCounter is %d, RxCounter is %d\n",
+ __func__, RegRxCounter, priv->RxCounter);
+
+ rx_chk_cnt++;
+ if (priv->undecorated_smoothed_pwdb >= (RateAdaptiveTH_High+5)) {
+ rx_chk_cnt = 0;
+ } else if ((priv->undecorated_smoothed_pwdb < (RateAdaptiveTH_High + 5))
+ && (((priv->CurrentChannelBW != HT_CHANNEL_WIDTH_20) &&
+ (priv->undecorated_smoothed_pwdb >= RateAdaptiveTH_Low_40M))
+ || ((priv->CurrentChannelBW == HT_CHANNEL_WIDTH_20) &&
+ (priv->undecorated_smoothed_pwdb >= RateAdaptiveTH_Low_20M)))) {
+ if (rx_chk_cnt < 2)
+ return bStuck;
+ rx_chk_cnt = 0;
+ } else if ((((priv->CurrentChannelBW != HT_CHANNEL_WIDTH_20) &&
+ (priv->undecorated_smoothed_pwdb < RateAdaptiveTH_Low_40M)) ||
+ ((priv->CurrentChannelBW == HT_CHANNEL_WIDTH_20) &&
+ (priv->undecorated_smoothed_pwdb < RateAdaptiveTH_Low_20M))) &&
+ priv->undecorated_smoothed_pwdb >= VeryLowRSSI) {
+ if (rx_chk_cnt < 4)
+ return bStuck;
+ rx_chk_cnt = 0;
+ } else {
+ if (rx_chk_cnt < 8)
+ return bStuck;
+ rx_chk_cnt = 0;
+ }
+
+
+ SlotIndex = (priv->SilentResetRxSlotIndex++)%SilentResetRxSoltNum;
+
+ if (priv->RxCounter == RegRxCounter) {
+ priv->SilentResetRxStuckEvent[SlotIndex] = 1;
+
+ for (i = 0; i < SilentResetRxSoltNum; i++)
+ TotalRxStuckCount += priv->SilentResetRxStuckEvent[i];
+
+ if (TotalRxStuckCount == SilentResetRxSoltNum) {
+ bStuck = true;
+ for (i = 0; i < SilentResetRxSoltNum; i++)
+ TotalRxStuckCount +=
+ priv->SilentResetRxStuckEvent[i];
+ }
+
+
+ } else {
+ priv->SilentResetRxStuckEvent[SlotIndex] = 0;
+ }
+
+ priv->RxCounter = RegRxCounter;
+
+ return bStuck;
+}
+
+bool rtl8192_HalTxCheckStuck(struct net_device *dev)
+{
+ struct r8192_priv *priv = rtllib_priv(dev);
+ bool bStuck = false;
+ u16 RegTxCounter = read_nic_word(dev, 0x128);
+
+ RT_TRACE(COMP_RESET, "%s():RegTxCounter is %d,TxCounter is %d\n",
+ __func__, RegTxCounter, priv->TxCounter);
+
+ if (priv->TxCounter == RegTxCounter)
+ bStuck = true;
+
+ priv->TxCounter = RegTxCounter;
+
+ return bStuck;
+}
+
+bool rtl8192_GetNmodeSupportBySecCfg(struct net_device *dev)
+{
+ struct r8192_priv *priv = rtllib_priv(dev);
+ struct rtllib_device *ieee = priv->rtllib;
+
+ if (ieee->rtllib_ap_sec_type &&
+ (ieee->rtllib_ap_sec_type(priv->rtllib)&(SEC_ALG_WEP |
+ SEC_ALG_TKIP))) {
+ return false;
+ } else {
+ return true;
+ }
+}
+
+bool rtl8192_GetHalfNmodeSupportByAPs(struct net_device *dev)
+{
+ bool Reval;
+ struct r8192_priv *priv = rtllib_priv(dev);
+ struct rtllib_device *ieee = priv->rtllib;
+
+ if (ieee->bHalfWirelessN24GMode == true)
+ Reval = true;
+ else
+ Reval = false;
+
+ return Reval;
+}
+
+u8 rtl8192_QueryIsShort(u8 TxHT, u8 TxRate, struct cb_desc *tcb_desc)
+{
+ u8 tmp_Short;
+
+ tmp_Short = (TxHT == 1) ? ((tcb_desc->bUseShortGI) ? 1 : 0) :
+ ((tcb_desc->bUseShortPreamble) ? 1 : 0);
+ if (TxHT == 1 && TxRate != DESC90_RATEMCS15)
+ tmp_Short = 0;
+
+ return tmp_Short;
+}
+
+void ActUpdateChannelAccessSetting(struct net_device *dev,
+ enum wireless_mode WirelessMode,
+ struct channel_access_setting *ChnlAccessSetting)
+{
+}
diff --git a/drivers/staging/rtl8192e/rtl8192e/r8192E_dev.h b/drivers/staging/rtl8192e/rtl8192e/r8192E_dev.h
new file mode 100644
index 000000000..dbe0e1c87
--- /dev/null
+++ b/drivers/staging/rtl8192e/rtl8192e/r8192E_dev.h
@@ -0,0 +1,62 @@
+/******************************************************************************
+ * Copyright(c) 2008 - 2010 Realtek Corporation. All rights reserved.
+ *
+ * Based on the r8180 driver, which is:
+ * Copyright 2004-2005 Andrea Merello <andrea.merello@gmail.com>, et al.
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ * The full GNU General Public License is included in this distribution in the
+ * file called LICENSE.
+ *
+ * Contact Information:
+ * wlanfae <wlanfae@realtek.com>
+******************************************************************************/
+#ifndef _RTL8192E_H
+#define _RTL8192E_H
+
+#include "r8190P_def.h"
+
+u8 rtl8192_QueryIsShort(u8 TxHT, u8 TxRate, struct cb_desc *tcb_desc);
+bool rtl8192_GetHalfNmodeSupportByAPs(struct net_device *dev);
+bool rtl8192_GetNmodeSupportBySecCfg(struct net_device *dev);
+bool rtl8192_HalTxCheckStuck(struct net_device *dev);
+bool rtl8192_HalRxCheckStuck(struct net_device *dev);
+void rtl8192_interrupt_recognized(struct net_device *dev, u32 *p_inta,
+ u32 *p_intb);
+void rtl8192_enable_rx(struct net_device *dev);
+void rtl8192_enable_tx(struct net_device *dev);
+void rtl8192_EnableInterrupt(struct net_device *dev);
+void rtl8192_DisableInterrupt(struct net_device *dev);
+void rtl8192_ClearInterrupt(struct net_device *dev);
+void rtl8192_InitializeVariables(struct net_device *dev);
+void rtl8192e_start_beacon(struct net_device *dev);
+void rtl8192e_SetHwReg(struct net_device *dev, u8 variable, u8 *val);
+void rtl8192_get_eeprom_size(struct net_device *dev);
+bool rtl8192_adapter_start(struct net_device *dev);
+void rtl8192_link_change(struct net_device *dev);
+void rtl8192_AllowAllDestAddr(struct net_device *dev, bool bAllowAllDA,
+ bool WriteIntoReg);
+void rtl8192_tx_fill_desc(struct net_device *dev, struct tx_desc *pdesc,
+ struct cb_desc *cb_desc,
+ struct sk_buff *skb);
+void rtl8192_tx_fill_cmd_desc(struct net_device *dev,
+ struct tx_desc_cmd *entry,
+ struct cb_desc *cb_desc, struct sk_buff *skb);
+bool rtl8192_rx_query_status_desc(struct net_device *dev,
+ struct rtllib_rx_stats *stats,
+ struct rx_desc *pdesc,
+ struct sk_buff *skb);
+void rtl8192_halt_adapter(struct net_device *dev, bool reset);
+void rtl8192_update_ratr_table(struct net_device *dev);
+#endif
diff --git a/drivers/staging/rtl8192e/rtl8192e/r8192E_firmware.c b/drivers/staging/rtl8192e/rtl8192e/r8192E_firmware.c
new file mode 100644
index 000000000..fd6574e67
--- /dev/null
+++ b/drivers/staging/rtl8192e/rtl8192e/r8192E_firmware.c
@@ -0,0 +1,319 @@
+/******************************************************************************
+ * Copyright(c) 2008 - 2010 Realtek Corporation. All rights reserved.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ * The full GNU General Public License is included in this distribution in the
+ * file called LICENSE.
+ *
+ * Contact Information:
+ * wlanfae <wlanfae@realtek.com>
+******************************************************************************/
+
+#include "rtl_core.h"
+#include "r8192E_hw.h"
+#include "r8192E_hwimg.h"
+#include "r8192E_firmware.h"
+#include <linux/firmware.h>
+
+void firmware_init_param(struct net_device *dev)
+{
+ struct r8192_priv *priv = rtllib_priv(dev);
+ struct rt_firmware *pfirmware = priv->pFirmware;
+
+ pfirmware->cmdpacket_frag_thresold = GET_COMMAND_PACKET_FRAG_THRESHOLD(
+ MAX_TRANSMIT_BUFFER_SIZE);
+}
+
+static bool fw_download_code(struct net_device *dev, u8 *code_virtual_address,
+ u32 buffer_len)
+{
+ struct r8192_priv *priv = rtllib_priv(dev);
+ u16 frag_threshold;
+ u16 frag_length, frag_offset = 0;
+ int i;
+
+ struct rt_firmware *pfirmware = priv->pFirmware;
+ struct sk_buff *skb;
+ unsigned char *seg_ptr;
+ struct cb_desc *tcb_desc;
+ u8 bLastIniPkt;
+
+ firmware_init_param(dev);
+ frag_threshold = pfirmware->cmdpacket_frag_thresold;
+ do {
+ if ((buffer_len - frag_offset) > frag_threshold) {
+ frag_length = frag_threshold;
+ bLastIniPkt = 0;
+
+ } else {
+ frag_length = buffer_len - frag_offset;
+ bLastIniPkt = 1;
+
+ }
+
+ skb = dev_alloc_skb(frag_length + 4);
+ memcpy((unsigned char *)(skb->cb), &dev, sizeof(dev));
+ tcb_desc = (struct cb_desc *)(skb->cb + MAX_DEV_ADDR_SIZE);
+ tcb_desc->queue_index = TXCMD_QUEUE;
+ tcb_desc->bCmdOrInit = DESC_PACKET_TYPE_INIT;
+ tcb_desc->bLastIniPkt = bLastIniPkt;
+
+ seg_ptr = skb->data;
+ for (i = 0; i < frag_length; i += 4) {
+ *seg_ptr++ = ((i+0) < frag_length) ?
+ code_virtual_address[i+3] : 0;
+ *seg_ptr++ = ((i+1) < frag_length) ?
+ code_virtual_address[i+2] : 0;
+ *seg_ptr++ = ((i+2) < frag_length) ?
+ code_virtual_address[i+1] : 0;
+ *seg_ptr++ = ((i+3) < frag_length) ?
+ code_virtual_address[i+0] : 0;
+ }
+ tcb_desc->txbuf_size = (u16)i;
+ skb_put(skb, i);
+
+ if (!priv->rtllib->check_nic_enough_desc(dev, tcb_desc->queue_index) ||
+ (!skb_queue_empty(&priv->rtllib->skb_waitQ[tcb_desc->queue_index])) ||
+ (priv->rtllib->queue_stop)) {
+ RT_TRACE(COMP_FIRMWARE,
+ "===================> tx full!\n");
+ skb_queue_tail(&priv->rtllib->skb_waitQ
+ [tcb_desc->queue_index], skb);
+ } else {
+ priv->rtllib->softmac_hard_start_xmit(skb, dev);
+ }
+
+ code_virtual_address += frag_length;
+ frag_offset += frag_length;
+
+ } while (frag_offset < buffer_len);
+
+ write_nic_byte(dev, TPPoll, TPPoll_CQ);
+
+ return true;
+}
+
+static bool CPUcheck_maincodeok_turnonCPU(struct net_device *dev)
+{
+ bool rt_status = true;
+ u32 CPU_status = 0;
+ unsigned long timeout;
+
+ timeout = jiffies + msecs_to_jiffies(200);
+ while (time_before(jiffies, timeout)) {
+ CPU_status = read_nic_dword(dev, CPU_GEN);
+ if (CPU_status & CPU_GEN_PUT_CODE_OK)
+ break;
+ mdelay(2);
+ }
+
+ if (!(CPU_status&CPU_GEN_PUT_CODE_OK)) {
+ RT_TRACE(COMP_ERR, "Download Firmware: Put code fail!\n");
+ goto CPUCheckMainCodeOKAndTurnOnCPU_Fail;
+ } else {
+ RT_TRACE(COMP_FIRMWARE, "Download Firmware: Put code ok!\n");
+ }
+
+ CPU_status = read_nic_dword(dev, CPU_GEN);
+ write_nic_byte(dev, CPU_GEN,
+ (u8)((CPU_status|CPU_GEN_PWR_STB_CPU)&0xff));
+ mdelay(1);
+
+ timeout = jiffies + msecs_to_jiffies(200);
+ while (time_before(jiffies, timeout)) {
+ CPU_status = read_nic_dword(dev, CPU_GEN);
+ if (CPU_status&CPU_GEN_BOOT_RDY)
+ break;
+ mdelay(2);
+ }
+
+ if (!(CPU_status&CPU_GEN_BOOT_RDY))
+ goto CPUCheckMainCodeOKAndTurnOnCPU_Fail;
+ else
+ RT_TRACE(COMP_FIRMWARE, "Download Firmware: Boot ready!\n");
+
+ return rt_status;
+
+CPUCheckMainCodeOKAndTurnOnCPU_Fail:
+ RT_TRACE(COMP_ERR, "ERR in %s()\n", __func__);
+ rt_status = false;
+ return rt_status;
+}
+
+static bool CPUcheck_firmware_ready(struct net_device *dev)
+{
+
+ bool rt_status = true;
+ u32 CPU_status = 0;
+ unsigned long timeout;
+
+ timeout = jiffies + msecs_to_jiffies(20);
+ while (time_before(jiffies, timeout)) {
+ CPU_status = read_nic_dword(dev, CPU_GEN);
+ if (CPU_status&CPU_GEN_FIRM_RDY)
+ break;
+ mdelay(2);
+ }
+
+ if (!(CPU_status&CPU_GEN_FIRM_RDY))
+ goto CPUCheckFirmwareReady_Fail;
+ else
+ RT_TRACE(COMP_FIRMWARE, "Download Firmware: Firmware ready!\n");
+
+ return rt_status;
+
+CPUCheckFirmwareReady_Fail:
+ RT_TRACE(COMP_ERR, "ERR in %s()\n", __func__);
+ rt_status = false;
+ return rt_status;
+
+}
+
+static bool firmware_check_ready(struct net_device *dev,
+ u8 load_fw_status)
+{
+ struct r8192_priv *priv = rtllib_priv(dev);
+ struct rt_firmware *pfirmware = priv->pFirmware;
+ bool rt_status = true;
+
+ switch (load_fw_status) {
+ case FW_INIT_STEP0_BOOT:
+ pfirmware->firmware_status = FW_STATUS_1_MOVE_BOOT_CODE;
+ break;
+
+ case FW_INIT_STEP1_MAIN:
+ pfirmware->firmware_status = FW_STATUS_2_MOVE_MAIN_CODE;
+
+ rt_status = CPUcheck_maincodeok_turnonCPU(dev);
+ if (rt_status)
+ pfirmware->firmware_status = FW_STATUS_3_TURNON_CPU;
+ else
+ RT_TRACE(COMP_FIRMWARE,
+ "CPUcheck_maincodeok_turnonCPU fail!\n");
+
+ break;
+
+ case FW_INIT_STEP2_DATA:
+ pfirmware->firmware_status = FW_STATUS_4_MOVE_DATA_CODE;
+ mdelay(1);
+
+ rt_status = CPUcheck_firmware_ready(dev);
+ if (rt_status)
+ pfirmware->firmware_status = FW_STATUS_5_READY;
+ else
+ RT_TRACE(COMP_FIRMWARE,
+ "CPUcheck_firmware_ready fail(%d)!\n",
+ rt_status);
+
+ break;
+ default:
+ rt_status = false;
+ RT_TRACE(COMP_FIRMWARE, "Unknown firmware status");
+ break;
+ }
+
+ return rt_status;
+}
+
+bool init_firmware(struct net_device *dev)
+{
+ struct r8192_priv *priv = rtllib_priv(dev);
+ bool rt_status = true;
+
+ u32 file_length = 0;
+ u8 *mapped_file = NULL;
+ u8 init_step = 0;
+ enum opt_rst_type rst_opt = OPT_SYSTEM_RESET;
+ enum firmware_init_step starting_state = FW_INIT_STEP0_BOOT;
+
+ struct rt_firmware *pfirmware = priv->pFirmware;
+
+ RT_TRACE(COMP_FIRMWARE, " PlatformInitFirmware()==>\n");
+
+ if (pfirmware->firmware_status == FW_STATUS_0_INIT) {
+ rst_opt = OPT_SYSTEM_RESET;
+ starting_state = FW_INIT_STEP0_BOOT;
+
+ } else if (pfirmware->firmware_status == FW_STATUS_5_READY) {
+ rst_opt = OPT_FIRMWARE_RESET;
+ starting_state = FW_INIT_STEP2_DATA;
+ } else {
+ RT_TRACE(COMP_FIRMWARE,
+ "PlatformInitFirmware: undefined firmware state\n");
+ }
+
+ for (init_step = starting_state; init_step <= FW_INIT_STEP2_DATA;
+ init_step++) {
+ if (rst_opt == OPT_SYSTEM_RESET) {
+ if (pfirmware->firmware_buf_size[init_step] == 0) {
+ const char *fw_name[3] = {
+ RTL8192E_BOOT_IMG_FW,
+ RTL8192E_MAIN_IMG_FW,
+ RTL8192E_DATA_IMG_FW
+ };
+ const struct firmware *fw_entry;
+ int rc;
+
+ rc = reject_firmware(&fw_entry,
+ fw_name[init_step],
+ &priv->pdev->dev);
+ if (rc < 0) {
+ RT_TRACE(COMP_FIRMWARE,
+ "request firmware fail!\n");
+ goto download_firmware_fail;
+ }
+ if (fw_entry->size >
+ sizeof(pfirmware->firmware_buf[init_step])) {
+ RT_TRACE(COMP_FIRMWARE,
+ "img file size exceed the container struct buffer fail!\n");
+ goto download_firmware_fail;
+ }
+
+ if (init_step != FW_INIT_STEP1_MAIN) {
+ memcpy(pfirmware->firmware_buf[init_step],
+ fw_entry->data, fw_entry->size);
+ pfirmware->firmware_buf_size[init_step] =
+ fw_entry->size;
+
+ } else {
+ memset(pfirmware->firmware_buf[init_step],
+ 0, 128);
+ memcpy(&pfirmware->firmware_buf[init_step][128],
+ fw_entry->data, fw_entry->size);
+ pfirmware->firmware_buf_size[init_step] =
+ fw_entry->size + 128;
+ }
+
+ if (rst_opt == OPT_SYSTEM_RESET)
+ release_firmware(fw_entry);
+ }
+ }
+
+ mapped_file = pfirmware->firmware_buf[init_step];
+ file_length = pfirmware->firmware_buf_size[init_step];
+
+ rt_status = fw_download_code(dev, mapped_file, file_length);
+ if (!rt_status)
+ goto download_firmware_fail;
+
+ if (!firmware_check_ready(dev, init_step))
+ goto download_firmware_fail;
+ }
+
+ RT_TRACE(COMP_FIRMWARE, "Firmware Download Success\n");
+ return rt_status;
+
+download_firmware_fail:
+ RT_TRACE(COMP_ERR, "ERR in %s()\n", __func__);
+ rt_status = false;
+ return rt_status;
+
+}
diff --git a/drivers/staging/rtl8192e/rtl8192e/r8192E_firmware.h b/drivers/staging/rtl8192e/rtl8192e/r8192E_firmware.h
new file mode 100644
index 000000000..8ffb23433
--- /dev/null
+++ b/drivers/staging/rtl8192e/rtl8192e/r8192E_firmware.h
@@ -0,0 +1,72 @@
+/******************************************************************************
+ * Copyright(c) 2008 - 2010 Realtek Corporation. All rights reserved.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ * The full GNU General Public License is included in this distribution in the
+ * file called LICENSE.
+ *
+ * Contact Information:
+ * wlanfae <wlanfae@realtek.com>
+******************************************************************************/
+#ifndef __INC_FIRMWARE_H
+#define __INC_FIRMWARE_H
+
+#define RTL8190_CPU_START_OFFSET 0x80
+
+#define GET_COMMAND_PACKET_FRAG_THRESHOLD(v) (4*(v/4) - 8)
+
+#define RTL8192E_BOOT_IMG_FW "/*(DEBLOBBED)*/"
+#define RTL8192E_MAIN_IMG_FW "/*(DEBLOBBED)*/"
+#define RTL8192E_DATA_IMG_FW "/*(DEBLOBBED)*/"
+
+enum firmware_init_step {
+ FW_INIT_STEP0_BOOT = 0,
+ FW_INIT_STEP1_MAIN = 1,
+ FW_INIT_STEP2_DATA = 2,
+};
+
+enum opt_rst_type {
+ OPT_SYSTEM_RESET = 0,
+ OPT_FIRMWARE_RESET = 1,
+};
+
+enum desc_packet_type {
+ DESC_PACKET_TYPE_INIT = 0,
+ DESC_PACKET_TYPE_NORMAL = 1,
+};
+
+enum firmware_status {
+ FW_STATUS_0_INIT = 0,
+ FW_STATUS_1_MOVE_BOOT_CODE = 1,
+ FW_STATUS_2_MOVE_MAIN_CODE = 2,
+ FW_STATUS_3_TURNON_CPU = 3,
+ FW_STATUS_4_MOVE_DATA_CODE = 4,
+ FW_STATUS_5_READY = 5,
+};
+
+struct fw_seg_container {
+ u16 seg_size;
+ u8 *seg_ptr;
+};
+
+struct rt_firmware {
+ enum firmware_status firmware_status;
+ u16 cmdpacket_frag_thresold;
+#define RTL8190_MAX_FIRMWARE_CODE_SIZE 64000
+#define MAX_FW_INIT_STEP 3
+ u8 firmware_buf[MAX_FW_INIT_STEP][RTL8190_MAX_FIRMWARE_CODE_SIZE];
+ u16 firmware_buf_size[MAX_FW_INIT_STEP];
+};
+
+bool init_firmware(struct net_device *dev);
+extern void firmware_init_param(struct net_device *dev);
+
+#endif
diff --git a/drivers/staging/rtl8192e/rtl8192e/r8192E_hw.h b/drivers/staging/rtl8192e/rtl8192e/r8192E_hw.h
new file mode 100644
index 000000000..43c3fb859
--- /dev/null
+++ b/drivers/staging/rtl8192e/rtl8192e/r8192E_hw.h
@@ -0,0 +1,453 @@
+/******************************************************************************
+ * Copyright(c) 2008 - 2010 Realtek Corporation. All rights reserved.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ * The full GNU General Public License is included in this distribution in the
+ * file called LICENSE.
+ *
+ * Contact Information:
+ * wlanfae <wlanfae@realtek.com>
+******************************************************************************/
+
+
+#ifndef R8180_HW
+#define R8180_HW
+
+enum baseband_config {
+ BaseBand_Config_PHY_REG = 0,
+ BaseBand_Config_AGC_TAB = 1,
+};
+
+#define RTL8187_REQT_READ 0xc0
+#define RTL8187_REQT_WRITE 0x40
+#define RTL8187_REQ_GET_REGS 0x05
+#define RTL8187_REQ_SET_REGS 0x05
+
+#define MAX_TX_URB 5
+#define MAX_RX_URB 16
+#define RX_URB_SIZE 9100
+
+#define BB_ANTATTEN_CHAN14 0x0c
+#define BB_ANTENNA_B 0x40
+
+#define BB_HOST_BANG (1<<30)
+#define BB_HOST_BANG_EN (1<<2)
+#define BB_HOST_BANG_CLK (1<<1)
+#define BB_HOST_BANG_RW (1<<3)
+#define BB_HOST_BANG_DATA 1
+
+#define RTL8190_EEPROM_ID 0x8129
+#define EEPROM_VID 0x02
+#define EEPROM_DID 0x04
+#define EEPROM_NODE_ADDRESS_BYTE_0 0x0C
+
+#define EEPROM_TxPowerDiff 0x1F
+
+
+#define EEPROM_PwDiff 0x21
+#define EEPROM_CrystalCap 0x22
+
+
+
+#define EEPROM_TxPwIndex_CCK_V1 0x29
+#define EEPROM_TxPwIndex_OFDM_24G_V1 0x2C
+#define EEPROM_TxPwIndex_Ver 0x27
+
+#define EEPROM_Default_TxPowerDiff 0x0
+#define EEPROM_Default_ThermalMeter 0x77
+#define EEPROM_Default_AntTxPowerDiff 0x0
+#define EEPROM_Default_TxPwDiff_CrystalCap 0x5
+#define EEPROM_Default_PwDiff 0x4
+#define EEPROM_Default_CrystalCap 0x5
+#define EEPROM_Default_TxPower 0x1010
+#define EEPROM_ICVersion_ChannelPlan 0x7C
+#define EEPROM_Customer_ID 0x7B
+#define EEPROM_RFInd_PowerDiff 0x28
+#define EEPROM_ThermalMeter 0x29
+#define EEPROM_TxPwDiff_CrystalCap 0x2A
+#define EEPROM_TxPwIndex_CCK 0x2C
+#define EEPROM_TxPwIndex_OFDM_24G 0x3A
+#define EEPROM_Default_TxPowerLevel 0x10
+#define EEPROM_IC_VER 0x7d
+#define EEPROM_CRC 0x7e
+
+#define EEPROM_CID_DEFAULT 0x0
+#define EEPROM_CID_CAMEO 0x1
+#define EEPROM_CID_RUNTOP 0x2
+#define EEPROM_CID_Senao 0x3
+#define EEPROM_CID_TOSHIBA 0x4
+#define EEPROM_CID_NetCore 0x5
+#define EEPROM_CID_Nettronix 0x6
+#define EEPROM_CID_Pronet 0x7
+#define EEPROM_CID_DLINK 0x8
+#define EEPROM_CID_WHQL 0xFE
+enum _RTL8192Pci_HW {
+ MAC0 = 0x000,
+ MAC1 = 0x001,
+ MAC2 = 0x002,
+ MAC3 = 0x003,
+ MAC4 = 0x004,
+ MAC5 = 0x005,
+ PCIF = 0x009,
+#define MXDMA2_16bytes 0x000
+#define MXDMA2_32bytes 0x001
+#define MXDMA2_64bytes 0x010
+#define MXDMA2_128bytes 0x011
+#define MXDMA2_256bytes 0x100
+#define MXDMA2_512bytes 0x101
+#define MXDMA2_1024bytes 0x110
+#define MXDMA2_NoLimit 0x7
+
+#define MULRW_SHIFT 3
+#define MXDMA2_RX_SHIFT 4
+#define MXDMA2_TX_SHIFT 0
+ PMR = 0x00c,
+ EPROM_CMD = 0x00e,
+#define EPROM_CMD_RESERVED_MASK BIT5
+#define EPROM_CMD_9356SEL BIT4
+#define EPROM_CMD_OPERATING_MODE_SHIFT 6
+#define EPROM_CMD_OPERATING_MODE_MASK ((1<<7)|(1<<6))
+#define EPROM_CMD_CONFIG 0x3
+#define EPROM_CMD_NORMAL 0
+#define EPROM_CMD_LOAD 1
+#define EPROM_CMD_PROGRAM 2
+#define EPROM_CS_SHIFT 3
+#define EPROM_CK_SHIFT 2
+#define EPROM_W_SHIFT 1
+#define EPROM_R_SHIFT 0
+
+ AFR = 0x010,
+#define AFR_CardBEn (1<<0)
+#define AFR_CLKRUN_SEL (1<<1)
+#define AFR_FuncRegEn (1<<2)
+
+ ANAPAR = 0x17,
+#define BB_GLOBAL_RESET_BIT 0x1
+ BB_GLOBAL_RESET = 0x020,
+ BSSIDR = 0x02E,
+ CMDR = 0x037,
+#define CR_RST 0x10
+#define CR_RE 0x08
+#define CR_TE 0x04
+#define CR_MulRW 0x01
+ SIFS = 0x03E,
+ TCR = 0x040,
+ RCR = 0x044,
+#define RCR_FILTER_MASK (BIT0 | BIT1 | BIT2 | BIT3 | BIT5 | BIT12 | \
+ BIT18 | BIT19 | BIT20 | BIT21 | BIT22 | BIT23)
+#define RCR_ONLYERLPKT BIT31
+#define RCR_ENCS2 BIT30
+#define RCR_ENCS1 BIT29
+#define RCR_ENMBID BIT27
+#define RCR_ACKTXBW (BIT24|BIT25)
+#define RCR_CBSSID BIT23
+#define RCR_APWRMGT BIT22
+#define RCR_ADD3 BIT21
+#define RCR_AMF BIT20
+#define RCR_ACF BIT19
+#define RCR_ADF BIT18
+#define RCR_RXFTH BIT13
+#define RCR_AICV BIT12
+#define RCR_ACRC32 BIT5
+#define RCR_AB BIT3
+#define RCR_AM BIT2
+#define RCR_APM BIT1
+#define RCR_AAP BIT0
+#define RCR_MXDMA_OFFSET 8
+#define RCR_FIFO_OFFSET 13
+ SLOT_TIME = 0x049,
+ ACK_TIMEOUT = 0x04c,
+ PIFS_TIME = 0x04d,
+ USTIME = 0x04e,
+ EDCAPARA_BE = 0x050,
+ EDCAPARA_BK = 0x054,
+ EDCAPARA_VO = 0x058,
+ EDCAPARA_VI = 0x05C,
+#define AC_PARAM_TXOP_LIMIT_OFFSET 16
+#define AC_PARAM_ECW_MAX_OFFSET 12
+#define AC_PARAM_ECW_MIN_OFFSET 8
+#define AC_PARAM_AIFS_OFFSET 0
+ RFPC = 0x05F,
+ CWRR = 0x060,
+ BCN_TCFG = 0x062,
+#define BCN_TCFG_CW_SHIFT 8
+#define BCN_TCFG_IFS 0
+ BCN_INTERVAL = 0x070,
+ ATIMWND = 0x072,
+ BCN_DRV_EARLY_INT = 0x074,
+#define BCN_DRV_EARLY_INT_SWBCN_SHIFT 8
+#define BCN_DRV_EARLY_INT_TIME_SHIFT 0
+ BCN_DMATIME = 0x076,
+ BCN_ERR_THRESH = 0x078,
+ RWCAM = 0x0A0,
+#define CAM_CM_SecCAMPolling BIT31
+#define CAM_CM_SecCAMClr BIT30
+#define CAM_CM_SecCAMWE BIT16
+#define CAM_VALID BIT15
+#define CAM_NOTVALID 0x0000
+#define CAM_USEDK BIT5
+
+#define CAM_NONE 0x0
+#define CAM_WEP40 0x01
+#define CAM_TKIP 0x02
+#define CAM_AES 0x04
+#define CAM_WEP104 0x05
+
+#define TOTAL_CAM_ENTRY 32
+
+#define CAM_CONFIG_USEDK true
+#define CAM_CONFIG_NO_USEDK false
+#define CAM_WRITE BIT16
+#define CAM_READ 0x00000000
+#define CAM_POLLINIG BIT31
+#define SCR_UseDK 0x01
+ WCAMI = 0x0A4,
+ RCAMO = 0x0A8,
+ SECR = 0x0B0,
+#define SCR_TxUseDK BIT0
+#define SCR_RxUseDK BIT1
+#define SCR_TxEncEnable BIT2
+#define SCR_RxDecEnable BIT3
+#define SCR_SKByA2 BIT4
+#define SCR_NoSKMC BIT5
+ SWREGULATOR = 0x0BD,
+ INTA_MASK = 0x0f4,
+#define IMR8190_DISABLED 0x0
+#define IMR_ATIMEND BIT28
+#define IMR_TBDOK BIT27
+#define IMR_TBDER BIT26
+#define IMR_TXFOVW BIT15
+#define IMR_TIMEOUT0 BIT14
+#define IMR_BcnInt BIT13
+#define IMR_RXFOVW BIT12
+#define IMR_RDU BIT11
+#define IMR_RXCMDOK BIT10
+#define IMR_BDOK BIT9
+#define IMR_HIGHDOK BIT8
+#define IMR_COMDOK BIT7
+#define IMR_MGNTDOK BIT6
+#define IMR_HCCADOK BIT5
+#define IMR_BKDOK BIT4
+#define IMR_BEDOK BIT3
+#define IMR_VIDOK BIT2
+#define IMR_VODOK BIT1
+#define IMR_ROK BIT0
+ ISR = 0x0f8,
+ TPPoll = 0x0fd,
+#define TPPoll_BKQ BIT0
+#define TPPoll_BEQ BIT1
+#define TPPoll_VIQ BIT2
+#define TPPoll_VOQ BIT3
+#define TPPoll_BQ BIT4
+#define TPPoll_CQ BIT5
+#define TPPoll_MQ BIT6
+#define TPPoll_HQ BIT7
+#define TPPoll_HCCAQ BIT8
+#define TPPoll_StopBK BIT9
+#define TPPoll_StopBE BIT10
+#define TPPoll_StopVI BIT11
+#define TPPoll_StopVO BIT12
+#define TPPoll_StopMgt BIT13
+#define TPPoll_StopHigh BIT14
+#define TPPoll_StopHCCA BIT15
+#define TPPoll_SHIFT 8
+
+ PSR = 0x0ff,
+#define PSR_GEN 0x0
+#define PSR_CPU 0x1
+ CPU_GEN = 0x100,
+ BB_RESET = 0x101,
+#define CPU_CCK_LOOPBACK 0x00030000
+#define CPU_GEN_SYSTEM_RESET 0x00000001
+#define CPU_GEN_FIRMWARE_RESET 0x00000008
+#define CPU_GEN_BOOT_RDY 0x00000010
+#define CPU_GEN_FIRM_RDY 0x00000020
+#define CPU_GEN_PUT_CODE_OK 0x00000080
+#define CPU_GEN_BB_RST 0x00000100
+#define CPU_GEN_PWR_STB_CPU 0x00000004
+#define CPU_GEN_NO_LOOPBACK_MSK 0xFFF8FFFF
+#define CPU_GEN_NO_LOOPBACK_SET 0x00080000
+#define CPU_GEN_GPIO_UART 0x00007000
+
+ LED1Cfg = 0x154,
+ LED0Cfg = 0x155,
+
+ AcmAvg = 0x170,
+ AcmHwCtrl = 0x171,
+#define AcmHw_HwEn BIT0
+#define AcmHw_BeqEn BIT1
+#define AcmHw_ViqEn BIT2
+#define AcmHw_VoqEn BIT3
+#define AcmHw_BeqStatus BIT4
+#define AcmHw_ViqStatus BIT5
+#define AcmHw_VoqStatus BIT6
+ AcmFwCtrl = 0x172,
+#define AcmFw_BeqStatus BIT0
+#define AcmFw_ViqStatus BIT1
+#define AcmFw_VoqStatus BIT2
+ VOAdmTime = 0x174,
+ VIAdmTime = 0x178,
+ BEAdmTime = 0x17C,
+ RQPN1 = 0x180,
+ RQPN2 = 0x184,
+ RQPN3 = 0x188,
+ QPRR = 0x1E0,
+ QPNR = 0x1F0,
+ BQDA = 0x200,
+ HQDA = 0x204,
+ CQDA = 0x208,
+ MQDA = 0x20C,
+ HCCAQDA = 0x210,
+ VOQDA = 0x214,
+ VIQDA = 0x218,
+ BEQDA = 0x21C,
+ BKQDA = 0x220,
+ RCQDA = 0x224,
+ RDQDA = 0x228,
+
+ MAR0 = 0x240,
+ MAR4 = 0x244,
+
+ CCX_PERIOD = 0x250,
+ CLM_RESULT = 0x251,
+ NHM_PERIOD = 0x252,
+
+ NHM_THRESHOLD0 = 0x253,
+ NHM_THRESHOLD1 = 0x254,
+ NHM_THRESHOLD2 = 0x255,
+ NHM_THRESHOLD3 = 0x256,
+ NHM_THRESHOLD4 = 0x257,
+ NHM_THRESHOLD5 = 0x258,
+ NHM_THRESHOLD6 = 0x259,
+
+ MCTRL = 0x25A,
+
+ NHM_RPI_COUNTER0 = 0x264,
+ NHM_RPI_COUNTER1 = 0x265,
+ NHM_RPI_COUNTER2 = 0x266,
+ NHM_RPI_COUNTER3 = 0x267,
+ NHM_RPI_COUNTER4 = 0x268,
+ NHM_RPI_COUNTER5 = 0x269,
+ NHM_RPI_COUNTER6 = 0x26A,
+ NHM_RPI_COUNTER7 = 0x26B,
+ WFCRC0 = 0x2f0,
+ WFCRC1 = 0x2f4,
+ WFCRC2 = 0x2f8,
+
+ BW_OPMODE = 0x300,
+#define BW_OPMODE_11J BIT0
+#define BW_OPMODE_5G BIT1
+#define BW_OPMODE_20MHZ BIT2
+ IC_VERRSION = 0x301,
+ MSR = 0x303,
+#define MSR_LINK_MASK ((1<<0)|(1<<1))
+#define MSR_LINK_MANAGED 2
+#define MSR_LINK_NONE 0
+#define MSR_LINK_SHIFT 0
+#define MSR_LINK_ADHOC 1
+#define MSR_LINK_MASTER 3
+#define MSR_LINK_ENEDCA (1<<4)
+
+#define MSR_NOLINK 0x00
+#define MSR_ADHOC 0x01
+#define MSR_INFRA 0x02
+#define MSR_AP 0x03
+
+ RETRY_LIMIT = 0x304,
+#define RETRY_LIMIT_SHORT_SHIFT 8
+#define RETRY_LIMIT_LONG_SHIFT 0
+ TSFR = 0x308,
+ RRSR = 0x310,
+#define RRSR_RSC_OFFSET 21
+#define RRSR_SHORT_OFFSET 23
+#define RRSR_RSC_DUPLICATE 0x600000
+#define RRSR_RSC_UPSUBCHNL 0x400000
+#define RRSR_RSC_LOWSUBCHNL 0x200000
+#define RRSR_SHORT 0x800000
+#define RRSR_1M BIT0
+#define RRSR_2M BIT1
+#define RRSR_5_5M BIT2
+#define RRSR_11M BIT3
+#define RRSR_6M BIT4
+#define RRSR_9M BIT5
+#define RRSR_12M BIT6
+#define RRSR_18M BIT7
+#define RRSR_24M BIT8
+#define RRSR_36M BIT9
+#define RRSR_48M BIT10
+#define RRSR_54M BIT11
+#define RRSR_MCS0 BIT12
+#define RRSR_MCS1 BIT13
+#define RRSR_MCS2 BIT14
+#define RRSR_MCS3 BIT15
+#define RRSR_MCS4 BIT16
+#define RRSR_MCS5 BIT17
+#define RRSR_MCS6 BIT18
+#define RRSR_MCS7 BIT19
+#define BRSR_AckShortPmb BIT23
+ UFWP = 0x318,
+ RATR0 = 0x320,
+#define RATR_1M 0x00000001
+#define RATR_2M 0x00000002
+#define RATR_55M 0x00000004
+#define RATR_11M 0x00000008
+#define RATR_6M 0x00000010
+#define RATR_9M 0x00000020
+#define RATR_12M 0x00000040
+#define RATR_18M 0x00000080
+#define RATR_24M 0x00000100
+#define RATR_36M 0x00000200
+#define RATR_48M 0x00000400
+#define RATR_54M 0x00000800
+#define RATR_MCS0 0x00001000
+#define RATR_MCS1 0x00002000
+#define RATR_MCS2 0x00004000
+#define RATR_MCS3 0x00008000
+#define RATR_MCS4 0x00010000
+#define RATR_MCS5 0x00020000
+#define RATR_MCS6 0x00040000
+#define RATR_MCS7 0x00080000
+#define RATR_MCS8 0x00100000
+#define RATR_MCS9 0x00200000
+#define RATR_MCS10 0x00400000
+#define RATR_MCS11 0x00800000
+#define RATR_MCS12 0x01000000
+#define RATR_MCS13 0x02000000
+#define RATR_MCS14 0x04000000
+#define RATR_MCS15 0x08000000
+#define RATE_ALL_CCK (RATR_1M | RATR_2M | RATR_55M | RATR_11M)
+#define RATE_ALL_OFDM_AG (RATR_6M | RATR_9M | RATR_12M | RATR_18M | \
+ RATR_24M | RATR_36M | RATR_48M | RATR_54M)
+#define RATE_ALL_OFDM_1SS (RATR_MCS0 | RATR_MCS1 | RATR_MCS2 | \
+ RATR_MCS3 | RATR_MCS4 | RATR_MCS5 | \
+ RATR_MCS6 | RATR_MCS7)
+#define RATE_ALL_OFDM_2SS (RATR_MCS8 | RATR_MCS9 | RATR_MCS10 | \
+ RATR_MCS11 | RATR_MCS12 | RATR_MCS13 | \
+ RATR_MCS14|RATR_MCS15)
+
+
+ DRIVER_RSSI = 0x32c,
+ MCS_TXAGC = 0x340,
+ CCK_TXAGC = 0x348,
+ MacBlkCtrl = 0x403,
+
+}
+;
+
+#define GPI 0x108
+#define GPO 0x109
+#define GPE 0x10a
+
+#define HWSET_MAX_SIZE_92S 128
+
+#define ANAPAR_FOR_8192PciE 0x17
+
+#endif
diff --git a/drivers/staging/rtl8192e/rtl8192e/r8192E_hwimg.c b/drivers/staging/rtl8192e/rtl8192e/r8192E_hwimg.c
new file mode 100644
index 000000000..6767b5965
--- /dev/null
+++ b/drivers/staging/rtl8192e/rtl8192e/r8192E_hwimg.c
@@ -0,0 +1,565 @@
+/******************************************************************************
+ * Copyright(c) 2008 - 2010 Realtek Corporation. All rights reserved.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ * The full GNU General Public License is included in this distribution in the
+ * file called LICENSE.
+ *
+ * Contact Information:
+ * wlanfae <wlanfae@realtek.com>
+******************************************************************************/
+/*Created on 2008/11/18, 3: 7*/
+
+#include "r8192E_hwimg.h"
+
+u32 Rtl8192PciEPHY_REGArray[PHY_REGArrayLengthPciE] = {0x0,};
+
+u32 Rtl8192PciEPHY_REG_1T2RArray[PHY_REG_1T2RArrayLengthPciE] = {
+ 0x800, 0x00000000,
+ 0x804, 0x00000001,
+ 0x808, 0x0000fc00,
+ 0x80c, 0x0000001c,
+ 0x810, 0x801010aa,
+ 0x814, 0x008514d0,
+ 0x818, 0x00000040,
+ 0x81c, 0x00000000,
+ 0x820, 0x00000004,
+ 0x824, 0x00690000,
+ 0x828, 0x00000004,
+ 0x82c, 0x00e90000,
+ 0x830, 0x00000004,
+ 0x834, 0x00690000,
+ 0x838, 0x00000004,
+ 0x83c, 0x00e90000,
+ 0x840, 0x00000000,
+ 0x844, 0x00000000,
+ 0x848, 0x00000000,
+ 0x84c, 0x00000000,
+ 0x850, 0x00000000,
+ 0x854, 0x00000000,
+ 0x858, 0x65a965a9,
+ 0x85c, 0x65a965a9,
+ 0x860, 0x001f0010,
+ 0x864, 0x007f0010,
+ 0x868, 0x001f0010,
+ 0x86c, 0x007f0010,
+ 0x870, 0x0f100f70,
+ 0x874, 0x0f100f70,
+ 0x878, 0x00000000,
+ 0x87c, 0x00000000,
+ 0x880, 0x6870e36c,
+ 0x884, 0xe3573600,
+ 0x888, 0x4260c340,
+ 0x88c, 0x0000ff00,
+ 0x890, 0x00000000,
+ 0x894, 0xfffffffe,
+ 0x898, 0x4c42382f,
+ 0x89c, 0x00656056,
+ 0x8b0, 0x00000000,
+ 0x8e0, 0x00000000,
+ 0x8e4, 0x00000000,
+ 0x900, 0x00000000,
+ 0x904, 0x00000023,
+ 0x908, 0x00000000,
+ 0x90c, 0x31121311,
+ 0xa00, 0x00d0c7d8,
+ 0xa04, 0x811f0008,
+ 0xa08, 0x80cd8300,
+ 0xa0c, 0x2e62740f,
+ 0xa10, 0x95009b78,
+ 0xa14, 0x11145008,
+ 0xa18, 0x00881117,
+ 0xa1c, 0x89140fa0,
+ 0xa20, 0x1a1b0000,
+ 0xa24, 0x090e1317,
+ 0xa28, 0x00000204,
+ 0xa2c, 0x00000000,
+ 0xc00, 0x00000040,
+ 0xc04, 0x00005433,
+ 0xc08, 0x000000e4,
+ 0xc0c, 0x6c6c6c6c,
+ 0xc10, 0x08800000,
+ 0xc14, 0x40000100,
+ 0xc18, 0x08000000,
+ 0xc1c, 0x40000100,
+ 0xc20, 0x08000000,
+ 0xc24, 0x40000100,
+ 0xc28, 0x08000000,
+ 0xc2c, 0x40000100,
+ 0xc30, 0x6de9ac44,
+ 0xc34, 0x465c52cd,
+ 0xc38, 0x497f5994,
+ 0xc3c, 0x0a969764,
+ 0xc40, 0x1f7c403f,
+ 0xc44, 0x000100b7,
+ 0xc48, 0xec020000,
+ 0xc4c, 0x00000300,
+ 0xc50, 0x69543420,
+ 0xc54, 0x433c0094,
+ 0xc58, 0x69543420,
+ 0xc5c, 0x433c0094,
+ 0xc60, 0x69543420,
+ 0xc64, 0x433c0094,
+ 0xc68, 0x69543420,
+ 0xc6c, 0x433c0094,
+ 0xc70, 0x2c7f000d,
+ 0xc74, 0x0186175b,
+ 0xc78, 0x0000001f,
+ 0xc7c, 0x00b91612,
+ 0xc80, 0x40000100,
+ 0xc84, 0x20000000,
+ 0xc88, 0x40000100,
+ 0xc8c, 0x20200000,
+ 0xc90, 0x40000100,
+ 0xc94, 0x00000000,
+ 0xc98, 0x40000100,
+ 0xc9c, 0x00000000,
+ 0xca0, 0x00492492,
+ 0xca4, 0x00000000,
+ 0xca8, 0x00000000,
+ 0xcac, 0x00000000,
+ 0xcb0, 0x00000000,
+ 0xcb4, 0x00000000,
+ 0xcb8, 0x00000000,
+ 0xcbc, 0x00492492,
+ 0xcc0, 0x00000000,
+ 0xcc4, 0x00000000,
+ 0xcc8, 0x00000000,
+ 0xccc, 0x00000000,
+ 0xcd0, 0x00000000,
+ 0xcd4, 0x00000000,
+ 0xcd8, 0x64b22427,
+ 0xcdc, 0x00766932,
+ 0xce0, 0x00222222,
+ 0xd00, 0x00000750,
+ 0xd04, 0x00000403,
+ 0xd08, 0x0000907f,
+ 0xd0c, 0x00000001,
+ 0xd10, 0xa0633333,
+ 0xd14, 0x33333c63,
+ 0xd18, 0x6a8f5b6b,
+ 0xd1c, 0x00000000,
+ 0xd20, 0x00000000,
+ 0xd24, 0x00000000,
+ 0xd28, 0x00000000,
+ 0xd2c, 0xcc979975,
+ 0xd30, 0x00000000,
+ 0xd34, 0x00000000,
+ 0xd38, 0x00000000,
+ 0xd3c, 0x00027293,
+ 0xd40, 0x00000000,
+ 0xd44, 0x00000000,
+ 0xd48, 0x00000000,
+ 0xd4c, 0x00000000,
+ 0xd50, 0x6437140a,
+ 0xd54, 0x024dbd02,
+ 0xd58, 0x00000000,
+ 0xd5c, 0x04032064,
+ 0xe00, 0x161a1a1a,
+ 0xe04, 0x12121416,
+ 0xe08, 0x00001800,
+ 0xe0c, 0x00000000,
+ 0xe10, 0x161a1a1a,
+ 0xe14, 0x12121416,
+ 0xe18, 0x161a1a1a,
+ 0xe1c, 0x12121416,
+};
+
+u32 Rtl8192PciERadioA_Array[RadioA_ArrayLengthPciE] = {
+ 0x019, 0x00000003,
+ 0x000, 0x000000bf,
+ 0x001, 0x00000ee0,
+ 0x002, 0x0000004c,
+ 0x003, 0x000007f1,
+ 0x004, 0x00000975,
+ 0x005, 0x00000c58,
+ 0x006, 0x00000ae6,
+ 0x007, 0x000000ca,
+ 0x008, 0x00000e1c,
+ 0x009, 0x000007f0,
+ 0x00a, 0x000009d0,
+ 0x00b, 0x000001ba,
+ 0x00c, 0x00000240,
+ 0x00e, 0x00000020,
+ 0x00f, 0x00000990,
+ 0x012, 0x00000806,
+ 0x014, 0x000005ab,
+ 0x015, 0x00000f80,
+ 0x016, 0x00000020,
+ 0x017, 0x00000597,
+ 0x018, 0x0000050a,
+ 0x01a, 0x00000f80,
+ 0x01b, 0x00000f5e,
+ 0x01c, 0x00000008,
+ 0x01d, 0x00000607,
+ 0x01e, 0x000006cc,
+ 0x01f, 0x00000000,
+ 0x020, 0x000001a5,
+ 0x01f, 0x00000001,
+ 0x020, 0x00000165,
+ 0x01f, 0x00000002,
+ 0x020, 0x000000c6,
+ 0x01f, 0x00000003,
+ 0x020, 0x00000086,
+ 0x01f, 0x00000004,
+ 0x020, 0x00000046,
+ 0x01f, 0x00000005,
+ 0x020, 0x000001e6,
+ 0x01f, 0x00000006,
+ 0x020, 0x000001a6,
+ 0x01f, 0x00000007,
+ 0x020, 0x00000166,
+ 0x01f, 0x00000008,
+ 0x020, 0x000000c7,
+ 0x01f, 0x00000009,
+ 0x020, 0x00000087,
+ 0x01f, 0x0000000a,
+ 0x020, 0x000000f7,
+ 0x01f, 0x0000000b,
+ 0x020, 0x000000d7,
+ 0x01f, 0x0000000c,
+ 0x020, 0x000000b7,
+ 0x01f, 0x0000000d,
+ 0x020, 0x00000097,
+ 0x01f, 0x0000000e,
+ 0x020, 0x00000077,
+ 0x01f, 0x0000000f,
+ 0x020, 0x00000057,
+ 0x01f, 0x00000010,
+ 0x020, 0x00000037,
+ 0x01f, 0x00000011,
+ 0x020, 0x000000fb,
+ 0x01f, 0x00000012,
+ 0x020, 0x000000db,
+ 0x01f, 0x00000013,
+ 0x020, 0x000000bb,
+ 0x01f, 0x00000014,
+ 0x020, 0x000000ff,
+ 0x01f, 0x00000015,
+ 0x020, 0x000000e3,
+ 0x01f, 0x00000016,
+ 0x020, 0x000000c3,
+ 0x01f, 0x00000017,
+ 0x020, 0x000000a3,
+ 0x01f, 0x00000018,
+ 0x020, 0x00000083,
+ 0x01f, 0x00000019,
+ 0x020, 0x00000063,
+ 0x01f, 0x0000001a,
+ 0x020, 0x00000043,
+ 0x01f, 0x0000001b,
+ 0x020, 0x00000023,
+ 0x01f, 0x0000001c,
+ 0x020, 0x00000003,
+ 0x01f, 0x0000001d,
+ 0x020, 0x000001e3,
+ 0x01f, 0x0000001e,
+ 0x020, 0x000001c3,
+ 0x01f, 0x0000001f,
+ 0x020, 0x000001a3,
+ 0x01f, 0x00000020,
+ 0x020, 0x00000183,
+ 0x01f, 0x00000021,
+ 0x020, 0x00000163,
+ 0x01f, 0x00000022,
+ 0x020, 0x00000143,
+ 0x01f, 0x00000023,
+ 0x020, 0x00000123,
+ 0x01f, 0x00000024,
+ 0x020, 0x00000103,
+ 0x023, 0x00000203,
+ 0x024, 0x00000100,
+ 0x00b, 0x000001ba,
+ 0x02c, 0x000003d7,
+ 0x02d, 0x00000ff0,
+ 0x000, 0x00000037,
+ 0x004, 0x00000160,
+ 0x007, 0x00000080,
+ 0x002, 0x0000088d,
+ 0x0fe, 0x00000000,
+ 0x0fe, 0x00000000,
+ 0x016, 0x00000200,
+ 0x016, 0x00000380,
+ 0x016, 0x00000020,
+ 0x016, 0x000001a0,
+ 0x000, 0x000000bf,
+ 0x00d, 0x0000001f,
+ 0x00d, 0x00000c9f,
+ 0x002, 0x0000004d,
+ 0x000, 0x00000cbf,
+ 0x004, 0x00000975,
+ 0x007, 0x00000700,
+};
+
+u32 Rtl8192PciERadioB_Array[RadioB_ArrayLengthPciE] = {
+ 0x019, 0x00000003,
+ 0x000, 0x000000bf,
+ 0x001, 0x000006e0,
+ 0x002, 0x0000004c,
+ 0x003, 0x000007f1,
+ 0x004, 0x00000975,
+ 0x005, 0x00000c58,
+ 0x006, 0x00000ae6,
+ 0x007, 0x000000ca,
+ 0x008, 0x00000e1c,
+ 0x000, 0x000000b7,
+ 0x00a, 0x00000850,
+ 0x000, 0x000000bf,
+ 0x00b, 0x000001ba,
+ 0x00c, 0x00000240,
+ 0x00e, 0x00000020,
+ 0x015, 0x00000f80,
+ 0x016, 0x00000020,
+ 0x017, 0x00000597,
+ 0x018, 0x0000050a,
+ 0x01a, 0x00000e00,
+ 0x01b, 0x00000f5e,
+ 0x01d, 0x00000607,
+ 0x01e, 0x000006cc,
+ 0x00b, 0x000001ba,
+ 0x023, 0x00000203,
+ 0x024, 0x00000100,
+ 0x000, 0x00000037,
+ 0x004, 0x00000160,
+ 0x016, 0x00000200,
+ 0x016, 0x00000380,
+ 0x016, 0x00000020,
+ 0x016, 0x000001a0,
+ 0x00d, 0x00000ccc,
+ 0x000, 0x000000bf,
+ 0x002, 0x0000004d,
+ 0x000, 0x00000cbf,
+ 0x004, 0x00000975,
+ 0x007, 0x00000700,
+};
+
+u32 Rtl8192PciERadioC_Array[RadioC_ArrayLengthPciE] = {
+ 0x0, };
+
+u32 Rtl8192PciERadioD_Array[RadioD_ArrayLengthPciE] = {
+ 0x0, };
+
+u32 Rtl8192PciEMACPHY_Array[] = {
+ 0x03c, 0xffff0000, 0x00000f0f,
+ 0x340, 0xffffffff, 0x161a1a1a,
+ 0x344, 0xffffffff, 0x12121416,
+ 0x348, 0x0000ffff, 0x00001818,
+ 0x12c, 0xffffffff, 0x04000802,
+ 0x318, 0x00000fff, 0x00000100,
+};
+
+u32 Rtl8192PciEMACPHY_Array_PG[] = {
+ 0x03c, 0xffff0000, 0x00000f0f,
+ 0xe00, 0xffffffff, 0x06090909,
+ 0xe04, 0xffffffff, 0x00030306,
+ 0xe08, 0x0000ff00, 0x00000000,
+ 0xe10, 0xffffffff, 0x0a0c0d0f,
+ 0xe14, 0xffffffff, 0x06070809,
+ 0xe18, 0xffffffff, 0x0a0c0d0f,
+ 0xe1c, 0xffffffff, 0x06070809,
+ 0x12c, 0xffffffff, 0x04000802,
+ 0x318, 0x00000fff, 0x00000800,
+};
+
+u32 Rtl8192PciEAGCTAB_Array[AGCTAB_ArrayLengthPciE] = {
+ 0xc78, 0x7d000001,
+ 0xc78, 0x7d010001,
+ 0xc78, 0x7d020001,
+ 0xc78, 0x7d030001,
+ 0xc78, 0x7d040001,
+ 0xc78, 0x7d050001,
+ 0xc78, 0x7c060001,
+ 0xc78, 0x7b070001,
+ 0xc78, 0x7a080001,
+ 0xc78, 0x79090001,
+ 0xc78, 0x780a0001,
+ 0xc78, 0x770b0001,
+ 0xc78, 0x760c0001,
+ 0xc78, 0x750d0001,
+ 0xc78, 0x740e0001,
+ 0xc78, 0x730f0001,
+ 0xc78, 0x72100001,
+ 0xc78, 0x71110001,
+ 0xc78, 0x70120001,
+ 0xc78, 0x6f130001,
+ 0xc78, 0x6e140001,
+ 0xc78, 0x6d150001,
+ 0xc78, 0x6c160001,
+ 0xc78, 0x6b170001,
+ 0xc78, 0x6a180001,
+ 0xc78, 0x69190001,
+ 0xc78, 0x681a0001,
+ 0xc78, 0x671b0001,
+ 0xc78, 0x661c0001,
+ 0xc78, 0x651d0001,
+ 0xc78, 0x641e0001,
+ 0xc78, 0x491f0001,
+ 0xc78, 0x48200001,
+ 0xc78, 0x47210001,
+ 0xc78, 0x46220001,
+ 0xc78, 0x45230001,
+ 0xc78, 0x44240001,
+ 0xc78, 0x43250001,
+ 0xc78, 0x28260001,
+ 0xc78, 0x27270001,
+ 0xc78, 0x26280001,
+ 0xc78, 0x25290001,
+ 0xc78, 0x242a0001,
+ 0xc78, 0x232b0001,
+ 0xc78, 0x222c0001,
+ 0xc78, 0x212d0001,
+ 0xc78, 0x202e0001,
+ 0xc78, 0x0a2f0001,
+ 0xc78, 0x08300001,
+ 0xc78, 0x06310001,
+ 0xc78, 0x05320001,
+ 0xc78, 0x04330001,
+ 0xc78, 0x03340001,
+ 0xc78, 0x02350001,
+ 0xc78, 0x01360001,
+ 0xc78, 0x00370001,
+ 0xc78, 0x00380001,
+ 0xc78, 0x00390001,
+ 0xc78, 0x003a0001,
+ 0xc78, 0x003b0001,
+ 0xc78, 0x003c0001,
+ 0xc78, 0x003d0001,
+ 0xc78, 0x003e0001,
+ 0xc78, 0x003f0001,
+ 0xc78, 0x7d400001,
+ 0xc78, 0x7d410001,
+ 0xc78, 0x7d420001,
+ 0xc78, 0x7d430001,
+ 0xc78, 0x7d440001,
+ 0xc78, 0x7d450001,
+ 0xc78, 0x7c460001,
+ 0xc78, 0x7b470001,
+ 0xc78, 0x7a480001,
+ 0xc78, 0x79490001,
+ 0xc78, 0x784a0001,
+ 0xc78, 0x774b0001,
+ 0xc78, 0x764c0001,
+ 0xc78, 0x754d0001,
+ 0xc78, 0x744e0001,
+ 0xc78, 0x734f0001,
+ 0xc78, 0x72500001,
+ 0xc78, 0x71510001,
+ 0xc78, 0x70520001,
+ 0xc78, 0x6f530001,
+ 0xc78, 0x6e540001,
+ 0xc78, 0x6d550001,
+ 0xc78, 0x6c560001,
+ 0xc78, 0x6b570001,
+ 0xc78, 0x6a580001,
+ 0xc78, 0x69590001,
+ 0xc78, 0x685a0001,
+ 0xc78, 0x675b0001,
+ 0xc78, 0x665c0001,
+ 0xc78, 0x655d0001,
+ 0xc78, 0x645e0001,
+ 0xc78, 0x495f0001,
+ 0xc78, 0x48600001,
+ 0xc78, 0x47610001,
+ 0xc78, 0x46620001,
+ 0xc78, 0x45630001,
+ 0xc78, 0x44640001,
+ 0xc78, 0x43650001,
+ 0xc78, 0x28660001,
+ 0xc78, 0x27670001,
+ 0xc78, 0x26680001,
+ 0xc78, 0x25690001,
+ 0xc78, 0x246a0001,
+ 0xc78, 0x236b0001,
+ 0xc78, 0x226c0001,
+ 0xc78, 0x216d0001,
+ 0xc78, 0x206e0001,
+ 0xc78, 0x0a6f0001,
+ 0xc78, 0x08700001,
+ 0xc78, 0x06710001,
+ 0xc78, 0x05720001,
+ 0xc78, 0x04730001,
+ 0xc78, 0x03740001,
+ 0xc78, 0x02750001,
+ 0xc78, 0x01760001,
+ 0xc78, 0x00770001,
+ 0xc78, 0x00780001,
+ 0xc78, 0x00790001,
+ 0xc78, 0x007a0001,
+ 0xc78, 0x007b0001,
+ 0xc78, 0x007c0001,
+ 0xc78, 0x007d0001,
+ 0xc78, 0x007e0001,
+ 0xc78, 0x007f0001,
+ 0xc78, 0x2e00001e,
+ 0xc78, 0x2e01001e,
+ 0xc78, 0x2e02001e,
+ 0xc78, 0x2e03001e,
+ 0xc78, 0x2e04001e,
+ 0xc78, 0x2e05001e,
+ 0xc78, 0x3006001e,
+ 0xc78, 0x3407001e,
+ 0xc78, 0x3908001e,
+ 0xc78, 0x3c09001e,
+ 0xc78, 0x3f0a001e,
+ 0xc78, 0x420b001e,
+ 0xc78, 0x440c001e,
+ 0xc78, 0x450d001e,
+ 0xc78, 0x460e001e,
+ 0xc78, 0x460f001e,
+ 0xc78, 0x4710001e,
+ 0xc78, 0x4811001e,
+ 0xc78, 0x4912001e,
+ 0xc78, 0x4a13001e,
+ 0xc78, 0x4b14001e,
+ 0xc78, 0x4b15001e,
+ 0xc78, 0x4c16001e,
+ 0xc78, 0x4d17001e,
+ 0xc78, 0x4e18001e,
+ 0xc78, 0x4f19001e,
+ 0xc78, 0x4f1a001e,
+ 0xc78, 0x501b001e,
+ 0xc78, 0x511c001e,
+ 0xc78, 0x521d001e,
+ 0xc78, 0x521e001e,
+ 0xc78, 0x531f001e,
+ 0xc78, 0x5320001e,
+ 0xc78, 0x5421001e,
+ 0xc78, 0x5522001e,
+ 0xc78, 0x5523001e,
+ 0xc78, 0x5624001e,
+ 0xc78, 0x5725001e,
+ 0xc78, 0x5726001e,
+ 0xc78, 0x5827001e,
+ 0xc78, 0x5828001e,
+ 0xc78, 0x5929001e,
+ 0xc78, 0x592a001e,
+ 0xc78, 0x5a2b001e,
+ 0xc78, 0x5b2c001e,
+ 0xc78, 0x5c2d001e,
+ 0xc78, 0x5c2e001e,
+ 0xc78, 0x5d2f001e,
+ 0xc78, 0x5e30001e,
+ 0xc78, 0x5f31001e,
+ 0xc78, 0x6032001e,
+ 0xc78, 0x6033001e,
+ 0xc78, 0x6134001e,
+ 0xc78, 0x6235001e,
+ 0xc78, 0x6336001e,
+ 0xc78, 0x6437001e,
+ 0xc78, 0x6438001e,
+ 0xc78, 0x6539001e,
+ 0xc78, 0x663a001e,
+ 0xc78, 0x673b001e,
+ 0xc78, 0x673c001e,
+ 0xc78, 0x683d001e,
+ 0xc78, 0x693e001e,
+ 0xc78, 0x6a3f001e,
+};
diff --git a/drivers/staging/rtl8192e/rtl8192e/r8192E_hwimg.h b/drivers/staging/rtl8192e/rtl8192e/r8192E_hwimg.h
new file mode 100644
index 000000000..d804876dd
--- /dev/null
+++ b/drivers/staging/rtl8192e/rtl8192e/r8192E_hwimg.h
@@ -0,0 +1,51 @@
+/******************************************************************************
+ * Copyright(c) 2008 - 2010 Realtek Corporation. All rights reserved.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ * The full GNU General Public License is included in this distribution in the
+ * file called LICENSE.
+ *
+ * Contact Information:
+ * wlanfae <wlanfae@realtek.com>
+******************************************************************************/
+#ifndef __INC_HAL8192PciE_FW_IMG_H
+#define __INC_HAL8192PciE_FW_IMG_H
+
+/*Created on 2008/11/18, 3: 7*/
+
+#include <linux/types.h>
+
+#define BootArrayLengthPciE 344
+extern u8 Rtl8192PciEFwBootArray[BootArrayLengthPciE];
+#define MainArrayLengthPciE 43012
+extern u8 Rtl8192PciEFwMainArray[MainArrayLengthPciE];
+#define DataArrayLengthPciE 848
+extern u8 Rtl8192PciEFwDataArray[DataArrayLengthPciE];
+#define PHY_REGArrayLengthPciE 1
+extern u32 Rtl8192PciEPHY_REGArray[PHY_REGArrayLengthPciE];
+#define PHY_REG_1T2RArrayLengthPciE 296
+extern u32 Rtl8192PciEPHY_REG_1T2RArray[PHY_REG_1T2RArrayLengthPciE];
+#define RadioA_ArrayLengthPciE 246
+extern u32 Rtl8192PciERadioA_Array[RadioA_ArrayLengthPciE];
+#define RadioB_ArrayLengthPciE 78
+extern u32 Rtl8192PciERadioB_Array[RadioB_ArrayLengthPciE];
+#define RadioC_ArrayLengthPciE 2
+extern u32 Rtl8192PciERadioC_Array[RadioC_ArrayLengthPciE];
+#define RadioD_ArrayLengthPciE 2
+extern u32 Rtl8192PciERadioD_Array[RadioD_ArrayLengthPciE];
+#define MACPHY_ArrayLengthPciE 18
+extern u32 Rtl8192PciEMACPHY_Array[MACPHY_ArrayLengthPciE];
+#define MACPHY_Array_PGLengthPciE 30
+extern u32 Rtl8192PciEMACPHY_Array_PG[MACPHY_Array_PGLengthPciE];
+#define AGCTAB_ArrayLengthPciE 384
+extern u32 Rtl8192PciEAGCTAB_Array[AGCTAB_ArrayLengthPciE];
+
+#endif
diff --git a/drivers/staging/rtl8192e/rtl8192e/r8192E_phy.c b/drivers/staging/rtl8192e/rtl8192e/r8192E_phy.c
new file mode 100644
index 000000000..4664a4fd1
--- /dev/null
+++ b/drivers/staging/rtl8192e/rtl8192e/r8192E_phy.c
@@ -0,0 +1,1636 @@
+/******************************************************************************
+ * Copyright(c) 2008 - 2010 Realtek Corporation. All rights reserved.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ * The full GNU General Public License is included in this distribution in the
+ * file called LICENSE.
+ *
+ * Contact Information:
+ * wlanfae <wlanfae@realtek.com>
+******************************************************************************/
+
+#include "rtl_core.h"
+#include "r8192E_hw.h"
+#include "r8192E_phyreg.h"
+#include "r8190P_rtl8256.h"
+#include "r8192E_phy.h"
+#include "rtl_dm.h"
+
+#include "r8192E_hwimg.h"
+
+static u32 RF_CHANNEL_TABLE_ZEBRA[] = {
+ 0,
+ 0x085c,
+ 0x08dc,
+ 0x095c,
+ 0x09dc,
+ 0x0a5c,
+ 0x0adc,
+ 0x0b5c,
+ 0x0bdc,
+ 0x0c5c,
+ 0x0cdc,
+ 0x0d5c,
+ 0x0ddc,
+ 0x0e5c,
+ 0x0f72,
+};
+
+/*************************Define local function prototype**********************/
+
+static u32 phy_FwRFSerialRead(struct net_device *dev,
+ enum rf90_radio_path eRFPath,
+ u32 Offset);
+static void phy_FwRFSerialWrite(struct net_device *dev,
+ enum rf90_radio_path eRFPath,
+ u32 Offset, u32 Data);
+
+static u32 rtl8192_CalculateBitShift(u32 dwBitMask)
+{
+ u32 i;
+
+ for (i = 0; i <= 31; i++) {
+ if (((dwBitMask >> i) & 0x1) == 1)
+ break;
+ }
+ return i;
+}
+
+u8 rtl8192_phy_CheckIsLegalRFPath(struct net_device *dev, u32 eRFPath)
+{
+ u8 ret = 1;
+ struct r8192_priv *priv = rtllib_priv(dev);
+
+ if (priv->rf_type == RF_2T4R)
+ ret = 0;
+ else if (priv->rf_type == RF_1T2R) {
+ if (eRFPath == RF90_PATH_A || eRFPath == RF90_PATH_B)
+ ret = 1;
+ else if (eRFPath == RF90_PATH_C || eRFPath == RF90_PATH_D)
+ ret = 0;
+ }
+ return ret;
+}
+
+void rtl8192_setBBreg(struct net_device *dev, u32 dwRegAddr, u32 dwBitMask,
+ u32 dwData)
+{
+
+ u32 OriginalValue, BitShift, NewValue;
+
+ if (dwBitMask != bMaskDWord) {
+ OriginalValue = read_nic_dword(dev, dwRegAddr);
+ BitShift = rtl8192_CalculateBitShift(dwBitMask);
+ NewValue = (((OriginalValue) & (~dwBitMask)) |
+ (dwData << BitShift));
+ write_nic_dword(dev, dwRegAddr, NewValue);
+ } else
+ write_nic_dword(dev, dwRegAddr, dwData);
+}
+
+u32 rtl8192_QueryBBReg(struct net_device *dev, u32 dwRegAddr, u32 dwBitMask)
+{
+ u32 Ret = 0, OriginalValue, BitShift;
+
+ OriginalValue = read_nic_dword(dev, dwRegAddr);
+ BitShift = rtl8192_CalculateBitShift(dwBitMask);
+ Ret = (OriginalValue & dwBitMask) >> BitShift;
+
+ return Ret;
+}
+static u32 rtl8192_phy_RFSerialRead(struct net_device *dev,
+ enum rf90_radio_path eRFPath, u32 Offset)
+{
+ struct r8192_priv *priv = rtllib_priv(dev);
+ u32 ret = 0;
+ u32 NewOffset = 0;
+ struct bb_reg_definition *pPhyReg = &priv->PHYRegDef[eRFPath];
+
+ Offset &= 0x3f;
+
+ if (priv->rf_chip == RF_8256) {
+ rtl8192_setBBreg(dev, rFPGA0_AnalogParameter4, 0xf00, 0x0);
+ if (Offset >= 31) {
+ priv->RfReg0Value[eRFPath] |= 0x140;
+ rtl8192_setBBreg(dev, pPhyReg->rf3wireOffset,
+ bMaskDWord,
+ (priv->RfReg0Value[eRFPath]<<16));
+ NewOffset = Offset - 30;
+ } else if (Offset >= 16) {
+ priv->RfReg0Value[eRFPath] |= 0x100;
+ priv->RfReg0Value[eRFPath] &= (~0x40);
+ rtl8192_setBBreg(dev, pPhyReg->rf3wireOffset,
+ bMaskDWord,
+ (priv->RfReg0Value[eRFPath]<<16));
+
+ NewOffset = Offset - 15;
+ } else
+ NewOffset = Offset;
+ } else {
+ RT_TRACE((COMP_PHY|COMP_ERR),
+ "check RF type here, need to be 8256\n");
+ NewOffset = Offset;
+ }
+ rtl8192_setBBreg(dev, pPhyReg->rfHSSIPara2, bLSSIReadAddress,
+ NewOffset);
+ rtl8192_setBBreg(dev, pPhyReg->rfHSSIPara2, bLSSIReadEdge, 0x0);
+ rtl8192_setBBreg(dev, pPhyReg->rfHSSIPara2, bLSSIReadEdge, 0x1);
+
+ mdelay(1);
+
+ ret = rtl8192_QueryBBReg(dev, pPhyReg->rfLSSIReadBack,
+ bLSSIReadBackData);
+
+ if (priv->rf_chip == RF_8256) {
+ priv->RfReg0Value[eRFPath] &= 0xebf;
+
+ rtl8192_setBBreg(dev, pPhyReg->rf3wireOffset, bMaskDWord,
+ (priv->RfReg0Value[eRFPath] << 16));
+
+ rtl8192_setBBreg(dev, rFPGA0_AnalogParameter4, 0x300, 0x3);
+ }
+
+
+ return ret;
+
+}
+
+static void rtl8192_phy_RFSerialWrite(struct net_device *dev,
+ enum rf90_radio_path eRFPath, u32 Offset,
+ u32 Data)
+{
+ struct r8192_priv *priv = rtllib_priv(dev);
+ u32 DataAndAddr = 0, NewOffset = 0;
+ struct bb_reg_definition *pPhyReg = &priv->PHYRegDef[eRFPath];
+
+ Offset &= 0x3f;
+ if (priv->rf_chip == RF_8256) {
+ rtl8192_setBBreg(dev, rFPGA0_AnalogParameter4, 0xf00, 0x0);
+
+ if (Offset >= 31) {
+ priv->RfReg0Value[eRFPath] |= 0x140;
+ rtl8192_setBBreg(dev, pPhyReg->rf3wireOffset,
+ bMaskDWord,
+ (priv->RfReg0Value[eRFPath] << 16));
+ NewOffset = Offset - 30;
+ } else if (Offset >= 16) {
+ priv->RfReg0Value[eRFPath] |= 0x100;
+ priv->RfReg0Value[eRFPath] &= (~0x40);
+ rtl8192_setBBreg(dev, pPhyReg->rf3wireOffset,
+ bMaskDWord,
+ (priv->RfReg0Value[eRFPath] << 16));
+ NewOffset = Offset - 15;
+ } else
+ NewOffset = Offset;
+ } else {
+ RT_TRACE((COMP_PHY|COMP_ERR),
+ "check RF type here, need to be 8256\n");
+ NewOffset = Offset;
+ }
+
+ DataAndAddr = (Data<<16) | (NewOffset&0x3f);
+
+ rtl8192_setBBreg(dev, pPhyReg->rf3wireOffset, bMaskDWord, DataAndAddr);
+
+ if (Offset == 0x0)
+ priv->RfReg0Value[eRFPath] = Data;
+
+ if (priv->rf_chip == RF_8256) {
+ if (Offset != 0) {
+ priv->RfReg0Value[eRFPath] &= 0xebf;
+ rtl8192_setBBreg(
+ dev,
+ pPhyReg->rf3wireOffset,
+ bMaskDWord,
+ (priv->RfReg0Value[eRFPath] << 16));
+ }
+ rtl8192_setBBreg(dev, rFPGA0_AnalogParameter4, 0x300, 0x3);
+ }
+}
+
+void rtl8192_phy_SetRFReg(struct net_device *dev, enum rf90_radio_path eRFPath,
+ u32 RegAddr, u32 BitMask, u32 Data)
+{
+ struct r8192_priv *priv = rtllib_priv(dev);
+ u32 Original_Value, BitShift, New_Value;
+
+ if (!rtl8192_phy_CheckIsLegalRFPath(dev, eRFPath))
+ return;
+ if (priv->rtllib->eRFPowerState != eRfOn && !priv->being_init_adapter)
+ return;
+
+ RT_TRACE(COMP_PHY, "FW RF CTRL is not ready now\n");
+ if (priv->Rf_Mode == RF_OP_By_FW) {
+ if (BitMask != bMask12Bits) {
+ Original_Value = phy_FwRFSerialRead(dev, eRFPath,
+ RegAddr);
+ BitShift = rtl8192_CalculateBitShift(BitMask);
+ New_Value = (((Original_Value) & (~BitMask)) |
+ (Data << BitShift));
+
+ phy_FwRFSerialWrite(dev, eRFPath, RegAddr, New_Value);
+ } else
+ phy_FwRFSerialWrite(dev, eRFPath, RegAddr, Data);
+ udelay(200);
+
+ } else {
+ if (BitMask != bMask12Bits) {
+ Original_Value = rtl8192_phy_RFSerialRead(dev, eRFPath,
+ RegAddr);
+ BitShift = rtl8192_CalculateBitShift(BitMask);
+ New_Value = (((Original_Value) & (~BitMask)) |
+ (Data << BitShift));
+
+ rtl8192_phy_RFSerialWrite(dev, eRFPath, RegAddr,
+ New_Value);
+ } else
+ rtl8192_phy_RFSerialWrite(dev, eRFPath, RegAddr, Data);
+ }
+}
+
+u32 rtl8192_phy_QueryRFReg(struct net_device *dev, enum rf90_radio_path eRFPath,
+ u32 RegAddr, u32 BitMask)
+{
+ u32 Original_Value, Readback_Value, BitShift;
+ struct r8192_priv *priv = rtllib_priv(dev);
+
+ if (!rtl8192_phy_CheckIsLegalRFPath(dev, eRFPath))
+ return 0;
+ if (priv->rtllib->eRFPowerState != eRfOn && !priv->being_init_adapter)
+ return 0;
+ down(&priv->rf_sem);
+ if (priv->Rf_Mode == RF_OP_By_FW) {
+ Original_Value = phy_FwRFSerialRead(dev, eRFPath, RegAddr);
+ udelay(200);
+ } else {
+ Original_Value = rtl8192_phy_RFSerialRead(dev, eRFPath,
+ RegAddr);
+ }
+ BitShift = rtl8192_CalculateBitShift(BitMask);
+ Readback_Value = (Original_Value & BitMask) >> BitShift;
+ up(&priv->rf_sem);
+ return Readback_Value;
+}
+
+static u32 phy_FwRFSerialRead(struct net_device *dev,
+ enum rf90_radio_path eRFPath, u32 Offset)
+{
+ u32 Data = 0;
+ u8 time = 0;
+
+ Data |= ((Offset & 0xFF) << 12);
+ Data |= ((eRFPath & 0x3) << 20);
+ Data |= 0x80000000;
+ while (read_nic_dword(dev, QPNR)&0x80000000) {
+ if (time++ < 100)
+ udelay(10);
+ else
+ break;
+ }
+ write_nic_dword(dev, QPNR, Data);
+ while (read_nic_dword(dev, QPNR) & 0x80000000) {
+ if (time++ < 100)
+ udelay(10);
+ else
+ return 0;
+ }
+ return read_nic_dword(dev, RF_DATA);
+
+}
+
+static void phy_FwRFSerialWrite(struct net_device *dev,
+ enum rf90_radio_path eRFPath,
+ u32 Offset, u32 Data)
+{
+ u8 time = 0;
+
+ Data |= ((Offset & 0xFF) << 12);
+ Data |= ((eRFPath & 0x3) << 20);
+ Data |= 0x400000;
+ Data |= 0x80000000;
+
+ while (read_nic_dword(dev, QPNR) & 0x80000000) {
+ if (time++ < 100)
+ udelay(10);
+ else
+ break;
+ }
+ write_nic_dword(dev, QPNR, Data);
+
+}
+
+
+void rtl8192_phy_configmac(struct net_device *dev)
+{
+ u32 dwArrayLen = 0, i = 0;
+ u32 *pdwArray = NULL;
+ struct r8192_priv *priv = rtllib_priv(dev);
+
+ if (priv->bTXPowerDataReadFromEEPORM) {
+ RT_TRACE(COMP_PHY, "Rtl819XMACPHY_Array_PG\n");
+ dwArrayLen = MACPHY_Array_PGLength;
+ pdwArray = Rtl819XMACPHY_Array_PG;
+
+ } else {
+ RT_TRACE(COMP_PHY, "Read rtl819XMACPHY_Array\n");
+ dwArrayLen = MACPHY_ArrayLength;
+ pdwArray = Rtl819XMACPHY_Array;
+ }
+ for (i = 0; i < dwArrayLen; i += 3) {
+ RT_TRACE(COMP_DBG,
+ "The Rtl8190MACPHY_Array[0] is %x Rtl8190MACPHY_Array[1] is %x Rtl8190MACPHY_Array[2] is %x\n",
+ pdwArray[i], pdwArray[i+1], pdwArray[i+2]);
+ if (pdwArray[i] == 0x318)
+ pdwArray[i+2] = 0x00000800;
+ rtl8192_setBBreg(dev, pdwArray[i], pdwArray[i+1],
+ pdwArray[i+2]);
+ }
+ return;
+
+}
+
+void rtl8192_phyConfigBB(struct net_device *dev, u8 ConfigType)
+{
+ int i;
+ u32 *Rtl819XPHY_REGArray_Table = NULL;
+ u32 *Rtl819XAGCTAB_Array_Table = NULL;
+ u16 AGCTAB_ArrayLen, PHY_REGArrayLen = 0;
+ struct r8192_priv *priv = rtllib_priv(dev);
+
+ AGCTAB_ArrayLen = AGCTAB_ArrayLength;
+ Rtl819XAGCTAB_Array_Table = Rtl819XAGCTAB_Array;
+ if (priv->rf_type == RF_2T4R) {
+ PHY_REGArrayLen = PHY_REGArrayLength;
+ Rtl819XPHY_REGArray_Table = Rtl819XPHY_REGArray;
+ } else if (priv->rf_type == RF_1T2R) {
+ PHY_REGArrayLen = PHY_REG_1T2RArrayLength;
+ Rtl819XPHY_REGArray_Table = Rtl819XPHY_REG_1T2RArray;
+ }
+
+ if (ConfigType == BaseBand_Config_PHY_REG) {
+ for (i = 0; i < PHY_REGArrayLen; i += 2) {
+ rtl8192_setBBreg(dev, Rtl819XPHY_REGArray_Table[i],
+ bMaskDWord,
+ Rtl819XPHY_REGArray_Table[i+1]);
+ RT_TRACE(COMP_DBG,
+ "i: %x, The Rtl819xUsbPHY_REGArray[0] is %x Rtl819xUsbPHY_REGArray[1] is %x\n",
+ i, Rtl819XPHY_REGArray_Table[i],
+ Rtl819XPHY_REGArray_Table[i+1]);
+ }
+ } else if (ConfigType == BaseBand_Config_AGC_TAB) {
+ for (i = 0; i < AGCTAB_ArrayLen; i += 2) {
+ rtl8192_setBBreg(dev, Rtl819XAGCTAB_Array_Table[i],
+ bMaskDWord,
+ Rtl819XAGCTAB_Array_Table[i+1]);
+ RT_TRACE(COMP_DBG,
+ "i:%x, The rtl819XAGCTAB_Array[0] is %x rtl819XAGCTAB_Array[1] is %x\n",
+ i, Rtl819XAGCTAB_Array_Table[i],
+ Rtl819XAGCTAB_Array_Table[i+1]);
+ }
+ }
+}
+
+static void rtl8192_InitBBRFRegDef(struct net_device *dev)
+{
+ struct r8192_priv *priv = rtllib_priv(dev);
+
+ priv->PHYRegDef[RF90_PATH_A].rfintfs = rFPGA0_XAB_RFInterfaceSW;
+ priv->PHYRegDef[RF90_PATH_B].rfintfs = rFPGA0_XAB_RFInterfaceSW;
+ priv->PHYRegDef[RF90_PATH_C].rfintfs = rFPGA0_XCD_RFInterfaceSW;
+ priv->PHYRegDef[RF90_PATH_D].rfintfs = rFPGA0_XCD_RFInterfaceSW;
+
+ priv->PHYRegDef[RF90_PATH_A].rfintfi = rFPGA0_XAB_RFInterfaceRB;
+ priv->PHYRegDef[RF90_PATH_B].rfintfi = rFPGA0_XAB_RFInterfaceRB;
+ priv->PHYRegDef[RF90_PATH_C].rfintfi = rFPGA0_XCD_RFInterfaceRB;
+ priv->PHYRegDef[RF90_PATH_D].rfintfi = rFPGA0_XCD_RFInterfaceRB;
+
+ priv->PHYRegDef[RF90_PATH_A].rfintfo = rFPGA0_XA_RFInterfaceOE;
+ priv->PHYRegDef[RF90_PATH_B].rfintfo = rFPGA0_XB_RFInterfaceOE;
+ priv->PHYRegDef[RF90_PATH_C].rfintfo = rFPGA0_XC_RFInterfaceOE;
+ priv->PHYRegDef[RF90_PATH_D].rfintfo = rFPGA0_XD_RFInterfaceOE;
+
+ priv->PHYRegDef[RF90_PATH_A].rfintfe = rFPGA0_XA_RFInterfaceOE;
+ priv->PHYRegDef[RF90_PATH_B].rfintfe = rFPGA0_XB_RFInterfaceOE;
+ priv->PHYRegDef[RF90_PATH_C].rfintfe = rFPGA0_XC_RFInterfaceOE;
+ priv->PHYRegDef[RF90_PATH_D].rfintfe = rFPGA0_XD_RFInterfaceOE;
+
+ priv->PHYRegDef[RF90_PATH_A].rf3wireOffset = rFPGA0_XA_LSSIParameter;
+ priv->PHYRegDef[RF90_PATH_B].rf3wireOffset = rFPGA0_XB_LSSIParameter;
+ priv->PHYRegDef[RF90_PATH_C].rf3wireOffset = rFPGA0_XC_LSSIParameter;
+ priv->PHYRegDef[RF90_PATH_D].rf3wireOffset = rFPGA0_XD_LSSIParameter;
+
+ priv->PHYRegDef[RF90_PATH_A].rfLSSI_Select = rFPGA0_XAB_RFParameter;
+ priv->PHYRegDef[RF90_PATH_B].rfLSSI_Select = rFPGA0_XAB_RFParameter;
+ priv->PHYRegDef[RF90_PATH_C].rfLSSI_Select = rFPGA0_XCD_RFParameter;
+ priv->PHYRegDef[RF90_PATH_D].rfLSSI_Select = rFPGA0_XCD_RFParameter;
+
+ priv->PHYRegDef[RF90_PATH_A].rfTxGainStage = rFPGA0_TxGainStage;
+ priv->PHYRegDef[RF90_PATH_B].rfTxGainStage = rFPGA0_TxGainStage;
+ priv->PHYRegDef[RF90_PATH_C].rfTxGainStage = rFPGA0_TxGainStage;
+ priv->PHYRegDef[RF90_PATH_D].rfTxGainStage = rFPGA0_TxGainStage;
+
+ priv->PHYRegDef[RF90_PATH_A].rfHSSIPara1 = rFPGA0_XA_HSSIParameter1;
+ priv->PHYRegDef[RF90_PATH_B].rfHSSIPara1 = rFPGA0_XB_HSSIParameter1;
+ priv->PHYRegDef[RF90_PATH_C].rfHSSIPara1 = rFPGA0_XC_HSSIParameter1;
+ priv->PHYRegDef[RF90_PATH_D].rfHSSIPara1 = rFPGA0_XD_HSSIParameter1;
+
+ priv->PHYRegDef[RF90_PATH_A].rfHSSIPara2 = rFPGA0_XA_HSSIParameter2;
+ priv->PHYRegDef[RF90_PATH_B].rfHSSIPara2 = rFPGA0_XB_HSSIParameter2;
+ priv->PHYRegDef[RF90_PATH_C].rfHSSIPara2 = rFPGA0_XC_HSSIParameter2;
+ priv->PHYRegDef[RF90_PATH_D].rfHSSIPara2 = rFPGA0_XD_HSSIParameter2;
+
+ priv->PHYRegDef[RF90_PATH_A].rfSwitchControl = rFPGA0_XAB_SwitchControl;
+ priv->PHYRegDef[RF90_PATH_B].rfSwitchControl = rFPGA0_XAB_SwitchControl;
+ priv->PHYRegDef[RF90_PATH_C].rfSwitchControl = rFPGA0_XCD_SwitchControl;
+ priv->PHYRegDef[RF90_PATH_D].rfSwitchControl = rFPGA0_XCD_SwitchControl;
+
+ priv->PHYRegDef[RF90_PATH_A].rfAGCControl1 = rOFDM0_XAAGCCore1;
+ priv->PHYRegDef[RF90_PATH_B].rfAGCControl1 = rOFDM0_XBAGCCore1;
+ priv->PHYRegDef[RF90_PATH_C].rfAGCControl1 = rOFDM0_XCAGCCore1;
+ priv->PHYRegDef[RF90_PATH_D].rfAGCControl1 = rOFDM0_XDAGCCore1;
+
+ priv->PHYRegDef[RF90_PATH_A].rfAGCControl2 = rOFDM0_XAAGCCore2;
+ priv->PHYRegDef[RF90_PATH_B].rfAGCControl2 = rOFDM0_XBAGCCore2;
+ priv->PHYRegDef[RF90_PATH_C].rfAGCControl2 = rOFDM0_XCAGCCore2;
+ priv->PHYRegDef[RF90_PATH_D].rfAGCControl2 = rOFDM0_XDAGCCore2;
+
+ priv->PHYRegDef[RF90_PATH_A].rfRxIQImbalance = rOFDM0_XARxIQImbalance;
+ priv->PHYRegDef[RF90_PATH_B].rfRxIQImbalance = rOFDM0_XBRxIQImbalance;
+ priv->PHYRegDef[RF90_PATH_C].rfRxIQImbalance = rOFDM0_XCRxIQImbalance;
+ priv->PHYRegDef[RF90_PATH_D].rfRxIQImbalance = rOFDM0_XDRxIQImbalance;
+
+ priv->PHYRegDef[RF90_PATH_A].rfRxAFE = rOFDM0_XARxAFE;
+ priv->PHYRegDef[RF90_PATH_B].rfRxAFE = rOFDM0_XBRxAFE;
+ priv->PHYRegDef[RF90_PATH_C].rfRxAFE = rOFDM0_XCRxAFE;
+ priv->PHYRegDef[RF90_PATH_D].rfRxAFE = rOFDM0_XDRxAFE;
+
+ priv->PHYRegDef[RF90_PATH_A].rfTxIQImbalance = rOFDM0_XATxIQImbalance;
+ priv->PHYRegDef[RF90_PATH_B].rfTxIQImbalance = rOFDM0_XBTxIQImbalance;
+ priv->PHYRegDef[RF90_PATH_C].rfTxIQImbalance = rOFDM0_XCTxIQImbalance;
+ priv->PHYRegDef[RF90_PATH_D].rfTxIQImbalance = rOFDM0_XDTxIQImbalance;
+
+ priv->PHYRegDef[RF90_PATH_A].rfTxAFE = rOFDM0_XATxAFE;
+ priv->PHYRegDef[RF90_PATH_B].rfTxAFE = rOFDM0_XBTxAFE;
+ priv->PHYRegDef[RF90_PATH_C].rfTxAFE = rOFDM0_XCTxAFE;
+ priv->PHYRegDef[RF90_PATH_D].rfTxAFE = rOFDM0_XDTxAFE;
+
+ priv->PHYRegDef[RF90_PATH_A].rfLSSIReadBack = rFPGA0_XA_LSSIReadBack;
+ priv->PHYRegDef[RF90_PATH_B].rfLSSIReadBack = rFPGA0_XB_LSSIReadBack;
+ priv->PHYRegDef[RF90_PATH_C].rfLSSIReadBack = rFPGA0_XC_LSSIReadBack;
+ priv->PHYRegDef[RF90_PATH_D].rfLSSIReadBack = rFPGA0_XD_LSSIReadBack;
+
+}
+
+bool rtl8192_phy_checkBBAndRF(struct net_device *dev,
+ enum hw90_block CheckBlock,
+ enum rf90_radio_path eRFPath)
+{
+ bool ret = true;
+ u32 i, CheckTimes = 4, dwRegRead = 0;
+ u32 WriteAddr[4];
+ u32 WriteData[] = {0xfffff027, 0xaa55a02f, 0x00000027, 0x55aa502f};
+
+ WriteAddr[HW90_BLOCK_MAC] = 0x100;
+ WriteAddr[HW90_BLOCK_PHY0] = 0x900;
+ WriteAddr[HW90_BLOCK_PHY1] = 0x800;
+ WriteAddr[HW90_BLOCK_RF] = 0x3;
+ RT_TRACE(COMP_PHY, "=======>%s(), CheckBlock:%d\n", __func__,
+ CheckBlock);
+ for (i = 0; i < CheckTimes; i++) {
+ switch (CheckBlock) {
+ case HW90_BLOCK_MAC:
+ RT_TRACE(COMP_ERR,
+ "PHY_CheckBBRFOK(): Never Write 0x100 here!");
+ break;
+
+ case HW90_BLOCK_PHY0:
+ case HW90_BLOCK_PHY1:
+ write_nic_dword(dev, WriteAddr[CheckBlock],
+ WriteData[i]);
+ dwRegRead = read_nic_dword(dev, WriteAddr[CheckBlock]);
+ break;
+
+ case HW90_BLOCK_RF:
+ WriteData[i] &= 0xfff;
+ rtl8192_phy_SetRFReg(dev, eRFPath,
+ WriteAddr[HW90_BLOCK_RF],
+ bMask12Bits, WriteData[i]);
+ mdelay(10);
+ dwRegRead = rtl8192_phy_QueryRFReg(dev, eRFPath,
+ WriteAddr[HW90_BLOCK_RF],
+ bMaskDWord);
+ mdelay(10);
+ break;
+
+ default:
+ ret = false;
+ break;
+ }
+
+
+ if (dwRegRead != WriteData[i]) {
+ RT_TRACE(COMP_ERR,
+ "====>error=====dwRegRead: %x, WriteData: %x\n",
+ dwRegRead, WriteData[i]);
+ ret = false;
+ break;
+ }
+ }
+
+ return ret;
+}
+
+static bool rtl8192_BB_Config_ParaFile(struct net_device *dev)
+{
+ struct r8192_priv *priv = rtllib_priv(dev);
+ bool rtStatus = true;
+ u8 bRegValue = 0, eCheckItem = 0;
+ u32 dwRegValue = 0;
+
+ bRegValue = read_nic_byte(dev, BB_GLOBAL_RESET);
+ write_nic_byte(dev, BB_GLOBAL_RESET, (bRegValue|BB_GLOBAL_RESET_BIT));
+
+ dwRegValue = read_nic_dword(dev, CPU_GEN);
+ write_nic_dword(dev, CPU_GEN, (dwRegValue&(~CPU_GEN_BB_RST)));
+
+ for (eCheckItem = (enum hw90_block)HW90_BLOCK_PHY0;
+ eCheckItem <= HW90_BLOCK_PHY1; eCheckItem++) {
+ rtStatus = rtl8192_phy_checkBBAndRF(dev,
+ (enum hw90_block)eCheckItem,
+ (enum rf90_radio_path)0);
+ if (!rtStatus) {
+ RT_TRACE((COMP_ERR | COMP_PHY),
+ "PHY_RF8256_Config():Check PHY%d Fail!!\n",
+ eCheckItem-1);
+ return rtStatus;
+ }
+ }
+ rtl8192_setBBreg(dev, rFPGA0_RFMOD, bCCKEn|bOFDMEn, 0x0);
+ rtl8192_phyConfigBB(dev, BaseBand_Config_PHY_REG);
+
+ dwRegValue = read_nic_dword(dev, CPU_GEN);
+ write_nic_dword(dev, CPU_GEN, (dwRegValue|CPU_GEN_BB_RST));
+
+ rtl8192_phyConfigBB(dev, BaseBand_Config_AGC_TAB);
+
+ if (priv->IC_Cut > VERSION_8190_BD) {
+ if (priv->rf_type == RF_2T4R)
+ dwRegValue = (priv->AntennaTxPwDiff[2]<<8 |
+ priv->AntennaTxPwDiff[1]<<4 |
+ priv->AntennaTxPwDiff[0]);
+ else
+ dwRegValue = 0x0;
+ rtl8192_setBBreg(dev, rFPGA0_TxGainStage,
+ (bXBTxAGC|bXCTxAGC|bXDTxAGC), dwRegValue);
+
+
+ dwRegValue = priv->CrystalCap;
+ rtl8192_setBBreg(dev, rFPGA0_AnalogParameter1, bXtalCap92x,
+ dwRegValue);
+ }
+
+ return rtStatus;
+}
+bool rtl8192_BBConfig(struct net_device *dev)
+{
+ rtl8192_InitBBRFRegDef(dev);
+ return rtl8192_BB_Config_ParaFile(dev);
+}
+
+void rtl8192_phy_getTxPower(struct net_device *dev)
+{
+ struct r8192_priv *priv = rtllib_priv(dev);
+
+ priv->MCSTxPowerLevelOriginalOffset[0] =
+ read_nic_dword(dev, rTxAGC_Rate18_06);
+ priv->MCSTxPowerLevelOriginalOffset[1] =
+ read_nic_dword(dev, rTxAGC_Rate54_24);
+ priv->MCSTxPowerLevelOriginalOffset[2] =
+ read_nic_dword(dev, rTxAGC_Mcs03_Mcs00);
+ priv->MCSTxPowerLevelOriginalOffset[3] =
+ read_nic_dword(dev, rTxAGC_Mcs07_Mcs04);
+ priv->MCSTxPowerLevelOriginalOffset[4] =
+ read_nic_dword(dev, rTxAGC_Mcs11_Mcs08);
+ priv->MCSTxPowerLevelOriginalOffset[5] =
+ read_nic_dword(dev, rTxAGC_Mcs15_Mcs12);
+
+ priv->DefaultInitialGain[0] = read_nic_byte(dev, rOFDM0_XAAGCCore1);
+ priv->DefaultInitialGain[1] = read_nic_byte(dev, rOFDM0_XBAGCCore1);
+ priv->DefaultInitialGain[2] = read_nic_byte(dev, rOFDM0_XCAGCCore1);
+ priv->DefaultInitialGain[3] = read_nic_byte(dev, rOFDM0_XDAGCCore1);
+ RT_TRACE(COMP_INIT,
+ "Default initial gain (c50=0x%x, c58=0x%x, c60=0x%x, c68=0x%x)\n",
+ priv->DefaultInitialGain[0], priv->DefaultInitialGain[1],
+ priv->DefaultInitialGain[2], priv->DefaultInitialGain[3]);
+
+ priv->framesync = read_nic_byte(dev, rOFDM0_RxDetector3);
+ priv->framesyncC34 = read_nic_dword(dev, rOFDM0_RxDetector2);
+ RT_TRACE(COMP_INIT, "Default framesync (0x%x) = 0x%x\n",
+ rOFDM0_RxDetector3, priv->framesync);
+ priv->SifsTime = read_nic_word(dev, SIFS);
+}
+
+void rtl8192_phy_setTxPower(struct net_device *dev, u8 channel)
+{
+ struct r8192_priv *priv = rtllib_priv(dev);
+ u8 powerlevel = 0, powerlevelOFDM24G = 0;
+ char ant_pwr_diff;
+ u32 u4RegValue;
+
+ if (priv->epromtype == EEPROM_93C46) {
+ powerlevel = priv->TxPowerLevelCCK[channel-1];
+ powerlevelOFDM24G = priv->TxPowerLevelOFDM24G[channel-1];
+ } else if (priv->epromtype == EEPROM_93C56) {
+ if (priv->rf_type == RF_1T2R) {
+ powerlevel = priv->TxPowerLevelCCK_C[channel-1];
+ powerlevelOFDM24G = priv->TxPowerLevelOFDM24G_C[channel-1];
+ } else if (priv->rf_type == RF_2T4R) {
+ powerlevel = priv->TxPowerLevelCCK_A[channel-1];
+ powerlevelOFDM24G = priv->TxPowerLevelOFDM24G_A[channel-1];
+
+ ant_pwr_diff = priv->TxPowerLevelOFDM24G_C[channel-1]
+ - priv->TxPowerLevelOFDM24G_A[channel-1];
+
+ priv->RF_C_TxPwDiff = ant_pwr_diff;
+
+ ant_pwr_diff &= 0xf;
+
+ priv->AntennaTxPwDiff[2] = 0;
+ priv->AntennaTxPwDiff[1] = (u8)(ant_pwr_diff);
+ priv->AntennaTxPwDiff[0] = 0;
+
+ u4RegValue = (priv->AntennaTxPwDiff[2]<<8 |
+ priv->AntennaTxPwDiff[1]<<4 |
+ priv->AntennaTxPwDiff[0]);
+
+ rtl8192_setBBreg(dev, rFPGA0_TxGainStage,
+ (bXBTxAGC|bXCTxAGC|bXDTxAGC), u4RegValue);
+ }
+ }
+ switch (priv->rf_chip) {
+ case RF_8225:
+ break;
+ case RF_8256:
+ PHY_SetRF8256CCKTxPower(dev, powerlevel);
+ PHY_SetRF8256OFDMTxPower(dev, powerlevelOFDM24G);
+ break;
+ case RF_8258:
+ break;
+ default:
+ RT_TRACE(COMP_ERR, "unknown rf chip in function %s()\n",
+ __func__);
+ break;
+ }
+}
+
+bool rtl8192_phy_RFConfig(struct net_device *dev)
+{
+ struct r8192_priv *priv = rtllib_priv(dev);
+ bool rtStatus = true;
+
+ switch (priv->rf_chip) {
+ case RF_8225:
+ break;
+ case RF_8256:
+ rtStatus = PHY_RF8256_Config(dev);
+ break;
+
+ case RF_8258:
+ break;
+ case RF_PSEUDO_11N:
+ break;
+
+ default:
+ RT_TRACE(COMP_ERR, "error chip id\n");
+ break;
+ }
+ return rtStatus;
+}
+
+void rtl8192_phy_updateInitGain(struct net_device *dev)
+{
+}
+
+u8 rtl8192_phy_ConfigRFWithHeaderFile(struct net_device *dev,
+ enum rf90_radio_path eRFPath)
+{
+
+ int i;
+
+ switch (eRFPath) {
+ case RF90_PATH_A:
+ for (i = 0; i < RadioA_ArrayLength; i += 2) {
+ if (Rtl819XRadioA_Array[i] == 0xfe) {
+ msleep(100);
+ continue;
+ }
+ rtl8192_phy_SetRFReg(dev, eRFPath,
+ Rtl819XRadioA_Array[i],
+ bMask12Bits,
+ Rtl819XRadioA_Array[i+1]);
+
+ }
+ break;
+ case RF90_PATH_B:
+ for (i = 0; i < RadioB_ArrayLength; i += 2) {
+ if (Rtl819XRadioB_Array[i] == 0xfe) {
+ msleep(100);
+ continue;
+ }
+ rtl8192_phy_SetRFReg(dev, eRFPath,
+ Rtl819XRadioB_Array[i],
+ bMask12Bits,
+ Rtl819XRadioB_Array[i+1]);
+
+ }
+ break;
+ case RF90_PATH_C:
+ for (i = 0; i < RadioC_ArrayLength; i += 2) {
+ if (Rtl819XRadioC_Array[i] == 0xfe) {
+ msleep(100);
+ continue;
+ }
+ rtl8192_phy_SetRFReg(dev, eRFPath,
+ Rtl819XRadioC_Array[i],
+ bMask12Bits,
+ Rtl819XRadioC_Array[i+1]);
+
+ }
+ break;
+ case RF90_PATH_D:
+ for (i = 0; i < RadioD_ArrayLength; i += 2) {
+ if (Rtl819XRadioD_Array[i] == 0xfe) {
+ msleep(100);
+ continue;
+ }
+ rtl8192_phy_SetRFReg(dev, eRFPath,
+ Rtl819XRadioD_Array[i], bMask12Bits,
+ Rtl819XRadioD_Array[i+1]);
+
+ }
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+
+}
+static void rtl8192_SetTxPowerLevel(struct net_device *dev, u8 channel)
+{
+ struct r8192_priv *priv = rtllib_priv(dev);
+ u8 powerlevel = priv->TxPowerLevelCCK[channel-1];
+ u8 powerlevelOFDM24G = priv->TxPowerLevelOFDM24G[channel-1];
+
+ switch (priv->rf_chip) {
+ case RF_8225:
+ break;
+
+ case RF_8256:
+ PHY_SetRF8256CCKTxPower(dev, powerlevel);
+ PHY_SetRF8256OFDMTxPower(dev, powerlevelOFDM24G);
+ break;
+
+ case RF_8258:
+ break;
+ default:
+ RT_TRACE(COMP_ERR,
+ "unknown rf chip ID in rtl8192_SetTxPowerLevel()\n");
+ break;
+ }
+}
+
+static u8 rtl8192_phy_SetSwChnlCmdArray(struct sw_chnl_cmd *CmdTable,
+ u32 CmdTableIdx, u32 CmdTableSz,
+ enum sw_chnl_cmd_id CmdID,
+ u32 Para1, u32 Para2, u32 msDelay)
+{
+ struct sw_chnl_cmd *pCmd;
+
+ if (CmdTable == NULL) {
+ RT_TRACE(COMP_ERR,
+ "phy_SetSwChnlCmdArray(): CmdTable cannot be NULL.\n");
+ return false;
+ }
+ if (CmdTableIdx >= CmdTableSz) {
+ RT_TRACE(COMP_ERR,
+ "phy_SetSwChnlCmdArray(): Access invalid index, please check size of the table, CmdTableIdx:%d, CmdTableSz:%d\n",
+ CmdTableIdx, CmdTableSz);
+ return false;
+ }
+
+ pCmd = CmdTable + CmdTableIdx;
+ pCmd->CmdID = CmdID;
+ pCmd->Para1 = Para1;
+ pCmd->Para2 = Para2;
+ pCmd->msDelay = msDelay;
+
+ return true;
+}
+
+static u8 rtl8192_phy_SwChnlStepByStep(struct net_device *dev, u8 channel,
+ u8 *stage, u8 *step, u32 *delay)
+{
+ struct r8192_priv *priv = rtllib_priv(dev);
+ struct rtllib_device *ieee = priv->rtllib;
+ u32 PreCommonCmdCnt;
+ u32 PostCommonCmdCnt;
+ u32 RfDependCmdCnt;
+ struct sw_chnl_cmd *CurrentCmd = NULL;
+ u8 eRFPath;
+
+ RT_TRACE(COMP_TRACE, "====>%s()====stage:%d, step:%d, channel:%d\n",
+ __func__, *stage, *step, channel);
+
+ if (!rtllib_legal_channel(priv->rtllib, channel)) {
+ RT_TRACE(COMP_ERR, "=============>set to illegal channel:%d\n",
+ channel);
+ return true;
+ }
+
+ {
+ PreCommonCmdCnt = 0;
+ rtl8192_phy_SetSwChnlCmdArray(ieee->PreCommonCmd,
+ PreCommonCmdCnt++,
+ MAX_PRECMD_CNT, CmdID_SetTxPowerLevel,
+ 0, 0, 0);
+ rtl8192_phy_SetSwChnlCmdArray(ieee->PreCommonCmd,
+ PreCommonCmdCnt++,
+ MAX_PRECMD_CNT, CmdID_End, 0, 0, 0);
+
+ PostCommonCmdCnt = 0;
+
+ rtl8192_phy_SetSwChnlCmdArray(ieee->PostCommonCmd,
+ PostCommonCmdCnt++,
+ MAX_POSTCMD_CNT, CmdID_End, 0, 0, 0);
+
+ RfDependCmdCnt = 0;
+ switch (priv->rf_chip) {
+ case RF_8225:
+ if (!(channel >= 1 && channel <= 14)) {
+ RT_TRACE(COMP_ERR,
+ "illegal channel for Zebra 8225: %d\n",
+ channel);
+ return false;
+ }
+ rtl8192_phy_SetSwChnlCmdArray(ieee->RfDependCmd,
+ RfDependCmdCnt++, MAX_RFDEPENDCMD_CNT,
+ CmdID_RF_WriteReg, rZebra1_Channel,
+ RF_CHANNEL_TABLE_ZEBRA[channel], 10);
+ rtl8192_phy_SetSwChnlCmdArray(ieee->RfDependCmd,
+ RfDependCmdCnt++, MAX_RFDEPENDCMD_CNT,
+ CmdID_End, 0, 0, 0);
+ break;
+
+ case RF_8256:
+ if (!(channel >= 1 && channel <= 14)) {
+ RT_TRACE(COMP_ERR,
+ "illegal channel for Zebra 8256: %d\n",
+ channel);
+ return false;
+ }
+ rtl8192_phy_SetSwChnlCmdArray(ieee->RfDependCmd,
+ RfDependCmdCnt++, MAX_RFDEPENDCMD_CNT,
+ CmdID_RF_WriteReg, rZebra1_Channel, channel,
+ 10);
+ rtl8192_phy_SetSwChnlCmdArray(ieee->RfDependCmd,
+
+ RfDependCmdCnt++,
+ MAX_RFDEPENDCMD_CNT,
+ CmdID_End, 0, 0, 0);
+ break;
+
+ case RF_8258:
+ break;
+
+ default:
+ RT_TRACE(COMP_ERR, "Unknown RFChipID: %d\n",
+ priv->rf_chip);
+ return false;
+ }
+
+
+ do {
+ switch (*stage) {
+ case 0:
+ CurrentCmd = &ieee->PreCommonCmd[*step];
+ break;
+ case 1:
+ CurrentCmd = &ieee->RfDependCmd[*step];
+ break;
+ case 2:
+ CurrentCmd = &ieee->PostCommonCmd[*step];
+ break;
+ }
+
+ if (CurrentCmd && CurrentCmd->CmdID == CmdID_End) {
+ if ((*stage) == 2)
+ return true;
+ (*stage)++;
+ (*step) = 0;
+ continue;
+ }
+
+ if (!CurrentCmd)
+ continue;
+ switch (CurrentCmd->CmdID) {
+ case CmdID_SetTxPowerLevel:
+ if (priv->IC_Cut > (u8)VERSION_8190_BD)
+ rtl8192_SetTxPowerLevel(dev, channel);
+ break;
+ case CmdID_WritePortUlong:
+ write_nic_dword(dev, CurrentCmd->Para1,
+ CurrentCmd->Para2);
+ break;
+ case CmdID_WritePortUshort:
+ write_nic_word(dev, CurrentCmd->Para1,
+ (u16)CurrentCmd->Para2);
+ break;
+ case CmdID_WritePortUchar:
+ write_nic_byte(dev, CurrentCmd->Para1,
+ (u8)CurrentCmd->Para2);
+ break;
+ case CmdID_RF_WriteReg:
+ for (eRFPath = 0; eRFPath <
+ priv->NumTotalRFPath; eRFPath++)
+ rtl8192_phy_SetRFReg(dev,
+ (enum rf90_radio_path)eRFPath,
+ CurrentCmd->Para1, bMask12Bits,
+ CurrentCmd->Para2<<7);
+ break;
+ default:
+ break;
+ }
+
+ break;
+ } while (true);
+ } /*for (Number of RF paths)*/
+
+ (*delay) = CurrentCmd->msDelay;
+ (*step)++;
+ return false;
+}
+
+static void rtl8192_phy_FinishSwChnlNow(struct net_device *dev, u8 channel)
+{
+ struct r8192_priv *priv = rtllib_priv(dev);
+ u32 delay = 0;
+
+ while (!rtl8192_phy_SwChnlStepByStep(dev, channel, &priv->SwChnlStage,
+ &priv->SwChnlStep, &delay)) {
+ if (delay > 0)
+ msleep(delay);
+ if (!priv->up)
+ break;
+ }
+}
+void rtl8192_SwChnl_WorkItem(struct net_device *dev)
+{
+
+ struct r8192_priv *priv = rtllib_priv(dev);
+
+ RT_TRACE(COMP_TRACE, "==> SwChnlCallback819xUsbWorkItem()\n");
+
+ RT_TRACE(COMP_TRACE, "=====>--%s(), set chan:%d, priv:%p\n", __func__,
+ priv->chan, priv);
+
+ rtl8192_phy_FinishSwChnlNow(dev , priv->chan);
+
+ RT_TRACE(COMP_TRACE, "<== SwChnlCallback819xUsbWorkItem()\n");
+}
+
+u8 rtl8192_phy_SwChnl(struct net_device *dev, u8 channel)
+{
+ struct r8192_priv *priv = rtllib_priv(dev);
+
+ RT_TRACE(COMP_PHY, "=====>%s()\n", __func__);
+ if (!priv->up) {
+ RT_TRACE(COMP_ERR, "%s(): ERR !! driver is not up\n", __func__);
+ return false;
+ }
+ if (priv->SwChnlInProgress)
+ return false;
+
+
+ switch (priv->rtllib->mode) {
+ case WIRELESS_MODE_A:
+ case WIRELESS_MODE_N_5G:
+ if (channel <= 14) {
+ RT_TRACE(COMP_ERR, "WIRELESS_MODE_A but channel<=14");
+ return false;
+ }
+ break;
+ case WIRELESS_MODE_B:
+ if (channel > 14) {
+ RT_TRACE(COMP_ERR, "WIRELESS_MODE_B but channel>14");
+ return false;
+ }
+ break;
+ case WIRELESS_MODE_G:
+ case WIRELESS_MODE_N_24G:
+ if (channel > 14) {
+ RT_TRACE(COMP_ERR, "WIRELESS_MODE_G but channel>14");
+ return false;
+ }
+ break;
+ }
+
+ priv->SwChnlInProgress = true;
+ if (channel == 0)
+ channel = 1;
+
+ priv->chan = channel;
+
+ priv->SwChnlStage = 0;
+ priv->SwChnlStep = 0;
+
+ if (priv->up)
+ rtl8192_SwChnl_WorkItem(dev);
+ priv->SwChnlInProgress = false;
+ return true;
+}
+
+static void CCK_Tx_Power_Track_BW_Switch_TSSI(struct net_device *dev)
+{
+ struct r8192_priv *priv = rtllib_priv(dev);
+
+ switch (priv->CurrentChannelBW) {
+ case HT_CHANNEL_WIDTH_20:
+ priv->CCKPresentAttentuation =
+ priv->CCKPresentAttentuation_20Mdefault +
+ priv->CCKPresentAttentuation_difference;
+
+ if (priv->CCKPresentAttentuation >
+ (CCKTxBBGainTableLength-1))
+ priv->CCKPresentAttentuation =
+ CCKTxBBGainTableLength-1;
+ if (priv->CCKPresentAttentuation < 0)
+ priv->CCKPresentAttentuation = 0;
+
+ RT_TRACE(COMP_POWER_TRACKING,
+ "20M, priv->CCKPresentAttentuation = %d\n",
+ priv->CCKPresentAttentuation);
+
+ if (priv->rtllib->current_network.channel == 14 &&
+ !priv->bcck_in_ch14) {
+ priv->bcck_in_ch14 = true;
+ dm_cck_txpower_adjust(dev, priv->bcck_in_ch14);
+ } else if (priv->rtllib->current_network.channel !=
+ 14 && priv->bcck_in_ch14) {
+ priv->bcck_in_ch14 = false;
+ dm_cck_txpower_adjust(dev, priv->bcck_in_ch14);
+ } else {
+ dm_cck_txpower_adjust(dev, priv->bcck_in_ch14);
+ }
+ break;
+
+ case HT_CHANNEL_WIDTH_20_40:
+ priv->CCKPresentAttentuation =
+ priv->CCKPresentAttentuation_40Mdefault +
+ priv->CCKPresentAttentuation_difference;
+
+ RT_TRACE(COMP_POWER_TRACKING,
+ "40M, priv->CCKPresentAttentuation = %d\n",
+ priv->CCKPresentAttentuation);
+ if (priv->CCKPresentAttentuation >
+ (CCKTxBBGainTableLength - 1))
+ priv->CCKPresentAttentuation =
+ CCKTxBBGainTableLength-1;
+ if (priv->CCKPresentAttentuation < 0)
+ priv->CCKPresentAttentuation = 0;
+
+ if (priv->rtllib->current_network.channel == 14 &&
+ !priv->bcck_in_ch14) {
+ priv->bcck_in_ch14 = true;
+ dm_cck_txpower_adjust(dev, priv->bcck_in_ch14);
+ } else if (priv->rtllib->current_network.channel != 14
+ && priv->bcck_in_ch14) {
+ priv->bcck_in_ch14 = false;
+ dm_cck_txpower_adjust(dev, priv->bcck_in_ch14);
+ } else {
+ dm_cck_txpower_adjust(dev, priv->bcck_in_ch14);
+ }
+ break;
+ }
+}
+
+static void CCK_Tx_Power_Track_BW_Switch_ThermalMeter(struct net_device *dev)
+{
+ struct r8192_priv *priv = rtllib_priv(dev);
+
+ if (priv->rtllib->current_network.channel == 14 &&
+ !priv->bcck_in_ch14)
+ priv->bcck_in_ch14 = true;
+ else if (priv->rtllib->current_network.channel != 14 &&
+ priv->bcck_in_ch14)
+ priv->bcck_in_ch14 = false;
+
+ switch (priv->CurrentChannelBW) {
+ case HT_CHANNEL_WIDTH_20:
+ if (priv->Record_CCK_20Mindex == 0)
+ priv->Record_CCK_20Mindex = 6;
+ priv->CCK_index = priv->Record_CCK_20Mindex;
+ RT_TRACE(COMP_POWER_TRACKING,
+ "20MHz, CCK_Tx_Power_Track_BW_Switch_ThermalMeter(),CCK_index = %d\n",
+ priv->CCK_index);
+ break;
+
+ case HT_CHANNEL_WIDTH_20_40:
+ priv->CCK_index = priv->Record_CCK_40Mindex;
+ RT_TRACE(COMP_POWER_TRACKING,
+ "40MHz, CCK_Tx_Power_Track_BW_Switch_ThermalMeter(), CCK_index = %d\n",
+ priv->CCK_index);
+ break;
+ }
+ dm_cck_txpower_adjust(dev, priv->bcck_in_ch14);
+}
+
+static void CCK_Tx_Power_Track_BW_Switch(struct net_device *dev)
+{
+ struct r8192_priv *priv = rtllib_priv(dev);
+
+ if (priv->IC_Cut >= IC_VersionCut_D)
+ CCK_Tx_Power_Track_BW_Switch_TSSI(dev);
+ else
+ CCK_Tx_Power_Track_BW_Switch_ThermalMeter(dev);
+}
+
+void rtl8192_SetBWModeWorkItem(struct net_device *dev)
+{
+
+ struct r8192_priv *priv = rtllib_priv(dev);
+ u8 regBwOpMode;
+
+ RT_TRACE(COMP_SWBW,
+ "==>rtl8192_SetBWModeWorkItem() Switch to %s bandwidth\n",
+ priv->CurrentChannelBW == HT_CHANNEL_WIDTH_20 ?
+ "20MHz" : "40MHz");
+
+
+ if (priv->rf_chip == RF_PSEUDO_11N) {
+ priv->SetBWModeInProgress = false;
+ return;
+ }
+ if (!priv->up) {
+ RT_TRACE(COMP_ERR, "%s(): ERR!! driver is not up\n", __func__);
+ return;
+ }
+ regBwOpMode = read_nic_byte(dev, BW_OPMODE);
+
+ switch (priv->CurrentChannelBW) {
+ case HT_CHANNEL_WIDTH_20:
+ regBwOpMode |= BW_OPMODE_20MHZ;
+ write_nic_byte(dev, BW_OPMODE, regBwOpMode);
+ break;
+
+ case HT_CHANNEL_WIDTH_20_40:
+ regBwOpMode &= ~BW_OPMODE_20MHZ;
+ write_nic_byte(dev, BW_OPMODE, regBwOpMode);
+ break;
+
+ default:
+ RT_TRACE(COMP_ERR,
+ "SetChannelBandwidth819xUsb(): unknown Bandwidth: %#X\n",
+ priv->CurrentChannelBW);
+ break;
+ }
+
+ switch (priv->CurrentChannelBW) {
+ case HT_CHANNEL_WIDTH_20:
+ rtl8192_setBBreg(dev, rFPGA0_RFMOD, bRFMOD, 0x0);
+ rtl8192_setBBreg(dev, rFPGA1_RFMOD, bRFMOD, 0x0);
+
+ if (!priv->btxpower_tracking) {
+ write_nic_dword(dev, rCCK0_TxFilter1, 0x1a1b0000);
+ write_nic_dword(dev, rCCK0_TxFilter2, 0x090e1317);
+ write_nic_dword(dev, rCCK0_DebugPort, 0x00000204);
+ } else {
+ CCK_Tx_Power_Track_BW_Switch(dev);
+ }
+
+ rtl8192_setBBreg(dev, rFPGA0_AnalogParameter1, 0x00100000, 1);
+
+ break;
+ case HT_CHANNEL_WIDTH_20_40:
+ rtl8192_setBBreg(dev, rFPGA0_RFMOD, bRFMOD, 0x1);
+ rtl8192_setBBreg(dev, rFPGA1_RFMOD, bRFMOD, 0x1);
+
+ if (!priv->btxpower_tracking) {
+ write_nic_dword(dev, rCCK0_TxFilter1, 0x35360000);
+ write_nic_dword(dev, rCCK0_TxFilter2, 0x121c252e);
+ write_nic_dword(dev, rCCK0_DebugPort, 0x00000409);
+ } else {
+ CCK_Tx_Power_Track_BW_Switch(dev);
+ }
+
+ rtl8192_setBBreg(dev, rCCK0_System, bCCKSideBand,
+ (priv->nCur40MhzPrimeSC>>1));
+ rtl8192_setBBreg(dev, rOFDM1_LSTF, 0xC00,
+ priv->nCur40MhzPrimeSC);
+
+ rtl8192_setBBreg(dev, rFPGA0_AnalogParameter1, 0x00100000, 0);
+ break;
+ default:
+ RT_TRACE(COMP_ERR,
+ "SetChannelBandwidth819xUsb(): unknown Bandwidth: %#X\n",
+ priv->CurrentChannelBW);
+ break;
+
+ }
+
+ switch (priv->rf_chip) {
+ case RF_8225:
+ break;
+
+ case RF_8256:
+ PHY_SetRF8256Bandwidth(dev, priv->CurrentChannelBW);
+ break;
+
+ case RF_8258:
+ break;
+
+ case RF_PSEUDO_11N:
+ break;
+
+ default:
+ RT_TRACE(COMP_ERR, "Unknown RFChipID: %d\n", priv->rf_chip);
+ break;
+ }
+
+ atomic_dec(&(priv->rtllib->atm_swbw));
+ priv->SetBWModeInProgress = false;
+
+ RT_TRACE(COMP_SWBW, "<==SetBWMode819xUsb()");
+}
+
+void rtl8192_SetBWMode(struct net_device *dev, enum ht_channel_width Bandwidth,
+ enum ht_extchnl_offset Offset)
+{
+ struct r8192_priv *priv = rtllib_priv(dev);
+
+
+ if (priv->SetBWModeInProgress)
+ return;
+
+ atomic_inc(&(priv->rtllib->atm_swbw));
+ priv->SetBWModeInProgress = true;
+
+ priv->CurrentChannelBW = Bandwidth;
+
+ if (Offset == HT_EXTCHNL_OFFSET_LOWER)
+ priv->nCur40MhzPrimeSC = HAL_PRIME_CHNL_OFFSET_UPPER;
+ else if (Offset == HT_EXTCHNL_OFFSET_UPPER)
+ priv->nCur40MhzPrimeSC = HAL_PRIME_CHNL_OFFSET_LOWER;
+ else
+ priv->nCur40MhzPrimeSC = HAL_PRIME_CHNL_OFFSET_DONT_CARE;
+
+ rtl8192_SetBWModeWorkItem(dev);
+
+}
+
+void InitialGain819xPci(struct net_device *dev, u8 Operation)
+{
+#define SCAN_RX_INITIAL_GAIN 0x17
+#define POWER_DETECTION_TH 0x08
+ struct r8192_priv *priv = rtllib_priv(dev);
+ u32 BitMask;
+ u8 initial_gain;
+
+ if (priv->up) {
+ switch (Operation) {
+ case IG_Backup:
+ RT_TRACE(COMP_SCAN,
+ "IG_Backup, backup the initial gain.\n");
+ initial_gain = SCAN_RX_INITIAL_GAIN;
+ BitMask = bMaskByte0;
+ if (dm_digtable.dig_algorithm ==
+ DIG_ALGO_BY_FALSE_ALARM)
+ rtl8192_setBBreg(dev, UFWP, bMaskByte1, 0x8);
+ priv->initgain_backup.xaagccore1 =
+ (u8)rtl8192_QueryBBReg(dev, rOFDM0_XAAGCCore1,
+ BitMask);
+ priv->initgain_backup.xbagccore1 =
+ (u8)rtl8192_QueryBBReg(dev, rOFDM0_XBAGCCore1,
+ BitMask);
+ priv->initgain_backup.xcagccore1 =
+ (u8)rtl8192_QueryBBReg(dev, rOFDM0_XCAGCCore1,
+ BitMask);
+ priv->initgain_backup.xdagccore1 =
+ (u8)rtl8192_QueryBBReg(dev, rOFDM0_XDAGCCore1,
+ BitMask);
+ BitMask = bMaskByte2;
+ priv->initgain_backup.cca = (u8)rtl8192_QueryBBReg(dev,
+ rCCK0_CCA, BitMask);
+
+ RT_TRACE(COMP_SCAN,
+ "Scan InitialGainBackup 0xc50 is %x\n",
+ priv->initgain_backup.xaagccore1);
+ RT_TRACE(COMP_SCAN,
+ "Scan InitialGainBackup 0xc58 is %x\n",
+ priv->initgain_backup.xbagccore1);
+ RT_TRACE(COMP_SCAN,
+ "Scan InitialGainBackup 0xc60 is %x\n",
+ priv->initgain_backup.xcagccore1);
+ RT_TRACE(COMP_SCAN,
+ "Scan InitialGainBackup 0xc68 is %x\n",
+ priv->initgain_backup.xdagccore1);
+ RT_TRACE(COMP_SCAN,
+ "Scan InitialGainBackup 0xa0a is %x\n",
+ priv->initgain_backup.cca);
+
+ RT_TRACE(COMP_SCAN, "Write scan initial gain = 0x%x\n",
+ initial_gain);
+ write_nic_byte(dev, rOFDM0_XAAGCCore1, initial_gain);
+ write_nic_byte(dev, rOFDM0_XBAGCCore1, initial_gain);
+ write_nic_byte(dev, rOFDM0_XCAGCCore1, initial_gain);
+ write_nic_byte(dev, rOFDM0_XDAGCCore1, initial_gain);
+ RT_TRACE(COMP_SCAN, "Write scan 0xa0a = 0x%x\n",
+ POWER_DETECTION_TH);
+ write_nic_byte(dev, 0xa0a, POWER_DETECTION_TH);
+ break;
+ case IG_Restore:
+ RT_TRACE(COMP_SCAN,
+ "IG_Restore, restore the initial gain.\n");
+ BitMask = 0x7f;
+ if (dm_digtable.dig_algorithm ==
+ DIG_ALGO_BY_FALSE_ALARM)
+ rtl8192_setBBreg(dev, UFWP, bMaskByte1, 0x8);
+
+ rtl8192_setBBreg(dev, rOFDM0_XAAGCCore1, BitMask,
+ (u32)priv->initgain_backup.xaagccore1);
+ rtl8192_setBBreg(dev, rOFDM0_XBAGCCore1, BitMask,
+ (u32)priv->initgain_backup.xbagccore1);
+ rtl8192_setBBreg(dev, rOFDM0_XCAGCCore1, BitMask,
+ (u32)priv->initgain_backup.xcagccore1);
+ rtl8192_setBBreg(dev, rOFDM0_XDAGCCore1, BitMask,
+ (u32)priv->initgain_backup.xdagccore1);
+ BitMask = bMaskByte2;
+ rtl8192_setBBreg(dev, rCCK0_CCA, BitMask,
+ (u32)priv->initgain_backup.cca);
+
+ RT_TRACE(COMP_SCAN,
+ "Scan BBInitialGainRestore 0xc50 is %x\n",
+ priv->initgain_backup.xaagccore1);
+ RT_TRACE(COMP_SCAN,
+ "Scan BBInitialGainRestore 0xc58 is %x\n",
+ priv->initgain_backup.xbagccore1);
+ RT_TRACE(COMP_SCAN,
+ "Scan BBInitialGainRestore 0xc60 is %x\n",
+ priv->initgain_backup.xcagccore1);
+ RT_TRACE(COMP_SCAN,
+ "Scan BBInitialGainRestore 0xc68 is %x\n",
+ priv->initgain_backup.xdagccore1);
+ RT_TRACE(COMP_SCAN,
+ "Scan BBInitialGainRestore 0xa0a is %x\n",
+ priv->initgain_backup.cca);
+
+ rtl8192_phy_setTxPower(dev,
+ priv->rtllib->current_network.channel);
+
+ if (dm_digtable.dig_algorithm ==
+ DIG_ALGO_BY_FALSE_ALARM)
+ rtl8192_setBBreg(dev, UFWP, bMaskByte1, 0x1);
+ break;
+ default:
+ RT_TRACE(COMP_SCAN, "Unknown IG Operation.\n");
+ break;
+ }
+ }
+}
+
+void PHY_SetRtl8192eRfOff(struct net_device *dev)
+{
+
+ rtl8192_setBBreg(dev, rFPGA0_XA_RFInterfaceOE, BIT4, 0x0);
+ rtl8192_setBBreg(dev, rFPGA0_AnalogParameter4, 0x300, 0x0);
+ rtl8192_setBBreg(dev, rFPGA0_AnalogParameter1, 0x18, 0x0);
+ rtl8192_setBBreg(dev, rOFDM0_TRxPathEnable, 0xf, 0x0);
+ rtl8192_setBBreg(dev, rOFDM1_TRxPathEnable, 0xf, 0x0);
+ rtl8192_setBBreg(dev, rFPGA0_AnalogParameter1, 0x60, 0x0);
+ rtl8192_setBBreg(dev, rFPGA0_AnalogParameter1, 0x4, 0x0);
+ write_nic_byte(dev, ANAPAR_FOR_8192PciE, 0x07);
+
+}
+
+static bool SetRFPowerState8190(struct net_device *dev,
+ enum rt_rf_power_state eRFPowerState)
+{
+ struct r8192_priv *priv = rtllib_priv(dev);
+ struct rt_pwr_save_ctrl *pPSC = (struct rt_pwr_save_ctrl *)
+ (&(priv->rtllib->PowerSaveControl));
+ bool bResult = true;
+ u8 i = 0, QueueID = 0;
+ struct rtl8192_tx_ring *ring = NULL;
+
+ if (priv->SetRFPowerStateInProgress)
+ return false;
+ RT_TRACE(COMP_PS, "===========> SetRFPowerState8190()!\n");
+ priv->SetRFPowerStateInProgress = true;
+
+ switch (priv->rf_chip) {
+ case RF_8256:
+ switch (eRFPowerState) {
+ case eRfOn:
+ RT_TRACE(COMP_PS, "SetRFPowerState8190() eRfOn!\n");
+ if ((priv->rtllib->eRFPowerState == eRfOff) &&
+ RT_IN_PS_LEVEL(pPSC, RT_RF_OFF_LEVL_HALT_NIC)) {
+ bool rtstatus = true;
+ u32 InitilizeCount = 3;
+
+ do {
+ InitilizeCount--;
+ priv->RegRfOff = false;
+ rtstatus = NicIFEnableNIC(dev);
+ } while (!rtstatus && (InitilizeCount > 0));
+
+ if (!rtstatus) {
+ RT_TRACE(COMP_ERR,
+ "%s():Initialize Adapter fail,return\n",
+ __func__);
+ priv->SetRFPowerStateInProgress = false;
+ return false;
+ }
+
+ RT_CLEAR_PS_LEVEL(pPSC,
+ RT_RF_OFF_LEVL_HALT_NIC);
+ } else {
+ write_nic_byte(dev, ANAPAR, 0x37);
+ mdelay(1);
+ rtl8192_setBBreg(dev, rFPGA0_AnalogParameter1,
+ 0x4, 0x1);
+ priv->bHwRfOffAction = 0;
+
+ rtl8192_setBBreg(dev, rFPGA0_XA_RFInterfaceOE,
+ BIT4, 0x1);
+ rtl8192_setBBreg(dev, rFPGA0_AnalogParameter4,
+ 0x300, 0x3);
+ rtl8192_setBBreg(dev, rFPGA0_AnalogParameter1,
+ 0x18, 0x3);
+ rtl8192_setBBreg(dev, rOFDM0_TRxPathEnable, 0x3,
+ 0x3);
+ rtl8192_setBBreg(dev, rOFDM1_TRxPathEnable, 0x3,
+ 0x3);
+ rtl8192_setBBreg(dev, rFPGA0_AnalogParameter1,
+ 0x60, 0x3);
+
+ }
+
+ break;
+
+ case eRfSleep:
+ if (priv->rtllib->eRFPowerState == eRfOff)
+ break;
+
+
+ for (QueueID = 0, i = 0; QueueID < MAX_TX_QUEUE; ) {
+ ring = &priv->tx_ring[QueueID];
+
+ if (skb_queue_len(&ring->queue) == 0) {
+ QueueID++;
+ continue;
+ } else {
+ RT_TRACE((COMP_POWER|COMP_RF),
+ "eRf Off/Sleep: %d times TcbBusyQueue[%d] !=0 before doze!\n",
+ (i+1), QueueID);
+ udelay(10);
+ i++;
+ }
+
+ if (i >= MAX_DOZE_WAITING_TIMES_9x) {
+ RT_TRACE(COMP_POWER,
+ "\n\n\n TimeOut!! SetRFPowerState8190(): eRfOff: %d times TcbBusyQueue[%d] != 0 !!!\n",
+ MAX_DOZE_WAITING_TIMES_9x,
+ QueueID);
+ break;
+ }
+ }
+ PHY_SetRtl8192eRfOff(dev);
+ break;
+
+ case eRfOff:
+ RT_TRACE(COMP_PS,
+ "SetRFPowerState8190() eRfOff/Sleep !\n");
+
+ for (QueueID = 0, i = 0; QueueID < MAX_TX_QUEUE; ) {
+ ring = &priv->tx_ring[QueueID];
+
+ if (skb_queue_len(&ring->queue) == 0) {
+ QueueID++;
+ continue;
+ } else {
+ RT_TRACE(COMP_POWER,
+ "eRf Off/Sleep: %d times TcbBusyQueue[%d] !=0 before doze!\n",
+ (i+1), QueueID);
+ udelay(10);
+ i++;
+ }
+
+ if (i >= MAX_DOZE_WAITING_TIMES_9x) {
+ RT_TRACE(COMP_POWER,
+ "\n\n\n SetZebra: RFPowerState8185B(): eRfOff: %d times TcbBusyQueue[%d] != 0 !!!\n",
+ MAX_DOZE_WAITING_TIMES_9x,
+ QueueID);
+ break;
+ }
+ }
+
+ if (pPSC->RegRfPsLevel & RT_RF_OFF_LEVL_HALT_NIC &&
+ !RT_IN_PS_LEVEL(pPSC, RT_RF_OFF_LEVL_HALT_NIC)) {
+ NicIFDisableNIC(dev);
+ RT_SET_PS_LEVEL(pPSC, RT_RF_OFF_LEVL_HALT_NIC);
+ } else if (!(pPSC->RegRfPsLevel &
+ RT_RF_OFF_LEVL_HALT_NIC)) {
+ PHY_SetRtl8192eRfOff(dev);
+ }
+
+ break;
+
+ default:
+ bResult = false;
+ RT_TRACE(COMP_ERR,
+ "SetRFPowerState8190(): unknown state to set: 0x%X!!!\n",
+ eRFPowerState);
+ break;
+ }
+
+ break;
+
+ default:
+ RT_TRACE(COMP_ERR, "SetRFPowerState8190(): Unknown RF type\n");
+ break;
+ }
+
+ if (bResult) {
+ priv->rtllib->eRFPowerState = eRFPowerState;
+
+ switch (priv->rf_chip) {
+ case RF_8256:
+ break;
+
+ default:
+ RT_TRACE(COMP_ERR,
+ "SetRFPowerState8190(): Unknown RF type\n");
+ break;
+ }
+ }
+
+ priv->SetRFPowerStateInProgress = false;
+ RT_TRACE(COMP_PS, "<=========== SetRFPowerState8190() bResult = %d!\n",
+ bResult);
+ return bResult;
+}
+
+bool SetRFPowerState(struct net_device *dev,
+ enum rt_rf_power_state eRFPowerState)
+{
+ struct r8192_priv *priv = rtllib_priv(dev);
+
+ bool bResult = false;
+
+ RT_TRACE(COMP_PS, "---------> SetRFPowerState(): eRFPowerState(%d)\n",
+ eRFPowerState);
+ if (eRFPowerState == priv->rtllib->eRFPowerState &&
+ priv->bHwRfOffAction == 0) {
+ RT_TRACE(COMP_PS,
+ "<--------- SetRFPowerState(): discard the request for eRFPowerState(%d) is the same.\n",
+ eRFPowerState);
+ return bResult;
+ }
+
+ bResult = SetRFPowerState8190(dev, eRFPowerState);
+
+ RT_TRACE(COMP_PS, "<--------- SetRFPowerState(): bResult(%d)\n",
+ bResult);
+
+ return bResult;
+}
+
+void PHY_ScanOperationBackup8192(struct net_device *dev, u8 Operation)
+{
+ struct r8192_priv *priv = rtllib_priv(dev);
+
+ if (priv->up) {
+ switch (Operation) {
+ case SCAN_OPT_BACKUP:
+ priv->rtllib->InitialGainHandler(dev, IG_Backup);
+ break;
+
+ case SCAN_OPT_RESTORE:
+ priv->rtllib->InitialGainHandler(dev, IG_Restore);
+ break;
+
+ default:
+ RT_TRACE(COMP_SCAN, "Unknown Scan Backup Operation.\n");
+ break;
+ }
+ }
+
+}
diff --git a/drivers/staging/rtl8192e/rtl8192e/r8192E_phy.h b/drivers/staging/rtl8192e/rtl8192e/r8192E_phy.h
new file mode 100644
index 000000000..7318f8857
--- /dev/null
+++ b/drivers/staging/rtl8192e/rtl8192e/r8192E_phy.h
@@ -0,0 +1,120 @@
+/******************************************************************************
+ * Copyright(c) 2008 - 2010 Realtek Corporation. All rights reserved.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ * The full GNU General Public License is included in this distribution in the
+ * file called LICENSE.
+ *
+ * Contact Information:
+ * wlanfae <wlanfae@realtek.com>
+******************************************************************************/
+#ifndef _R819XU_PHY_H
+#define _R819XU_PHY_H
+
+#define MAX_DOZE_WAITING_TIMES_9x 64
+
+#define AGCTAB_ArrayLength AGCTAB_ArrayLengthPciE
+#define MACPHY_ArrayLength MACPHY_ArrayLengthPciE
+#define RadioA_ArrayLength RadioA_ArrayLengthPciE
+#define RadioB_ArrayLength RadioB_ArrayLengthPciE
+#define MACPHY_Array_PGLength MACPHY_Array_PGLengthPciE
+#define RadioC_ArrayLength RadioC_ArrayLengthPciE
+#define RadioD_ArrayLength RadioD_ArrayLengthPciE
+#define PHY_REGArrayLength PHY_REGArrayLengthPciE
+#define PHY_REG_1T2RArrayLength PHY_REG_1T2RArrayLengthPciE
+
+#define Rtl819XMACPHY_Array_PG Rtl8192PciEMACPHY_Array_PG
+#define Rtl819XMACPHY_Array Rtl8192PciEMACPHY_Array
+#define Rtl819XRadioA_Array Rtl8192PciERadioA_Array
+#define Rtl819XRadioB_Array Rtl8192PciERadioB_Array
+#define Rtl819XRadioC_Array Rtl8192PciERadioC_Array
+#define Rtl819XRadioD_Array Rtl8192PciERadioD_Array
+#define Rtl819XAGCTAB_Array Rtl8192PciEAGCTAB_Array
+#define Rtl819XPHY_REGArray Rtl8192PciEPHY_REGArray
+#define Rtl819XPHY_REG_1T2RArray Rtl8192PciEPHY_REG_1T2RArray
+
+extern u32 rtl819XMACPHY_Array_PG[];
+extern u32 rtl819XPHY_REG_1T2RArray[];
+extern u32 rtl819XAGCTAB_Array[];
+extern u32 rtl819XRadioA_Array[];
+extern u32 rtl819XRadioB_Array[];
+extern u32 rtl819XRadioC_Array[];
+extern u32 rtl819XRadioD_Array[];
+
+enum hw90_block {
+ HW90_BLOCK_MAC = 0,
+ HW90_BLOCK_PHY0 = 1,
+ HW90_BLOCK_PHY1 = 2,
+ HW90_BLOCK_RF = 3,
+ HW90_BLOCK_MAXIMUM = 4,
+};
+
+enum rf90_radio_path {
+ RF90_PATH_A = 0,
+ RF90_PATH_B = 1,
+ RF90_PATH_C = 2,
+ RF90_PATH_D = 3,
+ RF90_PATH_MAX
+};
+
+#define bMaskByte0 0xff
+#define bMaskByte1 0xff00
+#define bMaskByte2 0xff0000
+#define bMaskByte3 0xff000000
+#define bMaskHWord 0xffff0000
+#define bMaskLWord 0x0000ffff
+#define bMaskDWord 0xffffffff
+
+extern u8 rtl8192_phy_CheckIsLegalRFPath(struct net_device *dev,
+ u32 eRFPath);
+extern void rtl8192_setBBreg(struct net_device *dev, u32 dwRegAddr,
+ u32 dwBitMask, u32 dwData);
+extern u32 rtl8192_QueryBBReg(struct net_device *dev, u32 dwRegAddr,
+ u32 dwBitMask);
+extern void rtl8192_phy_SetRFReg(struct net_device *dev,
+ enum rf90_radio_path eRFPath,
+ u32 RegAddr, u32 BitMask, u32 Data);
+extern u32 rtl8192_phy_QueryRFReg(struct net_device *dev,
+ enum rf90_radio_path eRFPath,
+ u32 RegAddr, u32 BitMask);
+extern void rtl8192_phy_configmac(struct net_device *dev);
+extern void rtl8192_phyConfigBB(struct net_device *dev, u8 ConfigType);
+extern bool rtl8192_phy_checkBBAndRF(struct net_device *dev,
+ enum hw90_block CheckBlock,
+ enum rf90_radio_path eRFPath);
+extern bool rtl8192_BBConfig(struct net_device *dev);
+extern void rtl8192_phy_getTxPower(struct net_device *dev);
+extern void rtl8192_phy_setTxPower(struct net_device *dev, u8 channel);
+extern bool rtl8192_phy_RFConfig(struct net_device *dev);
+extern void rtl8192_phy_updateInitGain(struct net_device *dev);
+extern u8 rtl8192_phy_ConfigRFWithHeaderFile(struct net_device *dev,
+ enum rf90_radio_path eRFPath);
+
+extern u8 rtl8192_phy_SwChnl(struct net_device *dev, u8 channel);
+extern void rtl8192_SetBWMode(struct net_device *dev,
+ enum ht_channel_width Bandwidth,
+ enum ht_extchnl_offset Offset);
+extern void rtl8192_SwChnl_WorkItem(struct net_device *dev);
+extern void rtl8192_SetBWModeWorkItem(struct net_device *dev);
+extern void InitialGain819xPci(struct net_device *dev, u8 Operation);
+
+extern void PHY_SetRtl8192eRfOff(struct net_device *dev);
+
+bool
+SetRFPowerState(
+ struct net_device *dev,
+ enum rt_rf_power_state eRFPowerState
+ );
+#define PHY_SetRFPowerState SetRFPowerState
+
+extern void PHY_ScanOperationBackup8192(struct net_device *dev, u8 Operation);
+
+#endif
diff --git a/drivers/staging/rtl8192e/rtl8192e/r8192E_phyreg.h b/drivers/staging/rtl8192e/rtl8192e/r8192E_phyreg.h
new file mode 100644
index 000000000..7899dd538
--- /dev/null
+++ b/drivers/staging/rtl8192e/rtl8192e/r8192E_phyreg.h
@@ -0,0 +1,852 @@
+/******************************************************************************
+ * Copyright(c) 2008 - 2010 Realtek Corporation. All rights reserved.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ * The full GNU General Public License is included in this distribution in the
+ * file called LICENSE.
+ *
+ * Contact Information:
+ * wlanfae <wlanfae@realtek.com>
+******************************************************************************/
+#ifndef _R819XU_PHYREG_H
+#define _R819XU_PHYREG_H
+
+
+#define RF_DATA 0x1d4
+
+#define rPMAC_Reset 0x100
+#define rPMAC_TxStart 0x104
+#define rPMAC_TxLegacySIG 0x108
+#define rPMAC_TxHTSIG1 0x10c
+#define rPMAC_TxHTSIG2 0x110
+#define rPMAC_PHYDebug 0x114
+#define rPMAC_TxPacketNum 0x118
+#define rPMAC_TxIdle 0x11c
+#define rPMAC_TxMACHeader0 0x120
+#define rPMAC_TxMACHeader1 0x124
+#define rPMAC_TxMACHeader2 0x128
+#define rPMAC_TxMACHeader3 0x12c
+#define rPMAC_TxMACHeader4 0x130
+#define rPMAC_TxMACHeader5 0x134
+#define rPMAC_TxDataType 0x138
+#define rPMAC_TxRandomSeed 0x13c
+#define rPMAC_CCKPLCPPreamble 0x140
+#define rPMAC_CCKPLCPHeader 0x144
+#define rPMAC_CCKCRC16 0x148
+#define rPMAC_OFDMRxCRC32OK 0x170
+#define rPMAC_OFDMRxCRC32Er 0x174
+#define rPMAC_OFDMRxParityEr 0x178
+#define rPMAC_OFDMRxCRC8Er 0x17c
+#define rPMAC_CCKCRxRC16Er 0x180
+#define rPMAC_CCKCRxRC32Er 0x184
+#define rPMAC_CCKCRxRC32OK 0x188
+#define rPMAC_TxStatus 0x18c
+
+#define MCS_TXAGC 0x340
+#define CCK_TXAGC 0x348
+
+/*---------------------0x400~0x4ff----------------------*/
+#define MacBlkCtrl 0x403
+
+#define rFPGA0_RFMOD 0x800
+#define rFPGA0_TxInfo 0x804
+#define rFPGA0_PSDFunction 0x808
+#define rFPGA0_TxGainStage 0x80c
+#define rFPGA0_RFTiming1 0x810
+#define rFPGA0_RFTiming2 0x814
+#define rFPGA0_XA_HSSIParameter1 0x820
+#define rFPGA0_XA_HSSIParameter2 0x824
+#define rFPGA0_XB_HSSIParameter1 0x828
+#define rFPGA0_XB_HSSIParameter2 0x82c
+#define rFPGA0_XC_HSSIParameter1 0x830
+#define rFPGA0_XC_HSSIParameter2 0x834
+#define rFPGA0_XD_HSSIParameter1 0x838
+#define rFPGA0_XD_HSSIParameter2 0x83c
+#define rFPGA0_XA_LSSIParameter 0x840
+#define rFPGA0_XB_LSSIParameter 0x844
+#define rFPGA0_XC_LSSIParameter 0x848
+#define rFPGA0_XD_LSSIParameter 0x84c
+#define rFPGA0_RFWakeUpParameter 0x850
+#define rFPGA0_RFSleepUpParameter 0x854
+#define rFPGA0_XAB_SwitchControl 0x858
+#define rFPGA0_XCD_SwitchControl 0x85c
+#define rFPGA0_XA_RFInterfaceOE 0x860
+#define rFPGA0_XB_RFInterfaceOE 0x864
+#define rFPGA0_XC_RFInterfaceOE 0x868
+#define rFPGA0_XD_RFInterfaceOE 0x86c
+#define rFPGA0_XAB_RFInterfaceSW 0x870
+#define rFPGA0_XCD_RFInterfaceSW 0x874
+#define rFPGA0_XAB_RFParameter 0x878
+#define rFPGA0_XCD_RFParameter 0x87c
+#define rFPGA0_AnalogParameter1 0x880
+#define rFPGA0_AnalogParameter2 0x884
+#define rFPGA0_AnalogParameter3 0x888
+#define rFPGA0_AnalogParameter4 0x88c
+#define rFPGA0_XA_LSSIReadBack 0x8a0
+#define rFPGA0_XB_LSSIReadBack 0x8a4
+#define rFPGA0_XC_LSSIReadBack 0x8a8
+#define rFPGA0_XD_LSSIReadBack 0x8ac
+#define rFPGA0_PSDReport 0x8b4
+#define rFPGA0_XAB_RFInterfaceRB 0x8e0
+#define rFPGA0_XCD_RFInterfaceRB 0x8e4
+
+#define rFPGA1_RFMOD 0x900
+#define rFPGA1_TxBlock 0x904
+#define rFPGA1_DebugSelect 0x908
+#define rFPGA1_TxInfo 0x90c
+
+#define rCCK0_System 0xa00
+#define rCCK0_AFESetting 0xa04
+#define rCCK0_CCA 0xa08
+#define rCCK0_RxAGC1 0xa0c
+#define rCCK0_RxAGC2 0xa10
+#define rCCK0_RxHP 0xa14
+#define rCCK0_DSPParameter1 0xa18
+#define rCCK0_DSPParameter2 0xa1c
+#define rCCK0_TxFilter1 0xa20
+#define rCCK0_TxFilter2 0xa24
+#define rCCK0_DebugPort 0xa28
+#define rCCK0_FalseAlarmReport 0xa2c
+#define rCCK0_TRSSIReport 0xa50
+#define rCCK0_RxReport 0xa54
+#define rCCK0_FACounterLower 0xa5c
+#define rCCK0_FACounterUpper 0xa58
+
+#define rOFDM0_LSTF 0xc00
+#define rOFDM0_TRxPathEnable 0xc04
+#define rOFDM0_TRMuxPar 0xc08
+#define rOFDM0_TRSWIsolation 0xc0c
+#define rOFDM0_XARxAFE 0xc10
+#define rOFDM0_XARxIQImbalance 0xc14
+#define rOFDM0_XBRxAFE 0xc18
+#define rOFDM0_XBRxIQImbalance 0xc1c
+#define rOFDM0_XCRxAFE 0xc20
+#define rOFDM0_XCRxIQImbalance 0xc24
+#define rOFDM0_XDRxAFE 0xc28
+#define rOFDM0_XDRxIQImbalance 0xc2c
+#define rOFDM0_RxDetector1 0xc30
+#define rOFDM0_RxDetector2 0xc34
+#define rOFDM0_RxDetector3 0xc38
+#define rOFDM0_RxDetector4 0xc3c
+#define rOFDM0_RxDSP 0xc40
+#define rOFDM0_CFOandDAGC 0xc44
+#define rOFDM0_CCADropThreshold 0xc48
+#define rOFDM0_ECCAThreshold 0xc4c
+#define rOFDM0_XAAGCCore1 0xc50
+#define rOFDM0_XAAGCCore2 0xc54
+#define rOFDM0_XBAGCCore1 0xc58
+#define rOFDM0_XBAGCCore2 0xc5c
+#define rOFDM0_XCAGCCore1 0xc60
+#define rOFDM0_XCAGCCore2 0xc64
+#define rOFDM0_XDAGCCore1 0xc68
+#define rOFDM0_XDAGCCore2 0xc6c
+#define rOFDM0_AGCParameter1 0xc70
+#define rOFDM0_AGCParameter2 0xc74
+#define rOFDM0_AGCRSSITable 0xc78
+#define rOFDM0_HTSTFAGC 0xc7c
+#define rOFDM0_XATxIQImbalance 0xc80
+#define rOFDM0_XATxAFE 0xc84
+#define rOFDM0_XBTxIQImbalance 0xc88
+#define rOFDM0_XBTxAFE 0xc8c
+#define rOFDM0_XCTxIQImbalance 0xc90
+#define rOFDM0_XCTxAFE 0xc94
+#define rOFDM0_XDTxIQImbalance 0xc98
+#define rOFDM0_XDTxAFE 0xc9c
+#define rOFDM0_RxHPParameter 0xce0
+#define rOFDM0_TxPseudoNoiseWgt 0xce4
+#define rOFDM0_FrameSync 0xcf0
+#define rOFDM0_DFSReport 0xcf4
+#define rOFDM0_TxCoeff1 0xca4
+#define rOFDM0_TxCoeff2 0xca8
+#define rOFDM0_TxCoeff3 0xcac
+#define rOFDM0_TxCoeff4 0xcb0
+#define rOFDM0_TxCoeff5 0xcb4
+#define rOFDM0_TxCoeff6 0xcb8
+
+
+#define rOFDM1_LSTF 0xd00
+#define rOFDM1_TRxPathEnable 0xd04
+#define rOFDM1_CFO 0xd08
+#define rOFDM1_CSI1 0xd10
+#define rOFDM1_SBD 0xd14
+#define rOFDM1_CSI2 0xd18
+#define rOFDM1_CFOTracking 0xd2c
+#define rOFDM1_TRxMesaure1 0xd34
+#define rOFDM1_IntfDet 0xd3c
+#define rOFDM1_PseudoNoiseStateAB 0xd50
+#define rOFDM1_PseudoNoiseStateCD 0xd54
+#define rOFDM1_RxPseudoNoiseWgt 0xd58
+#define rOFDM_PHYCounter1 0xda0
+#define rOFDM_PHYCounter2 0xda4
+#define rOFDM_PHYCounter3 0xda8
+#define rOFDM_ShortCFOAB 0xdac
+#define rOFDM_ShortCFOCD 0xdb0
+#define rOFDM_LongCFOAB 0xdb4
+#define rOFDM_LongCFOCD 0xdb8
+#define rOFDM_TailCFOAB 0xdbc
+#define rOFDM_TailCFOCD 0xdc0
+#define rOFDM_PWMeasure1 0xdc4
+#define rOFDM_PWMeasure2 0xdc8
+#define rOFDM_BWReport 0xdcc
+#define rOFDM_AGCReport 0xdd0
+#define rOFDM_RxSNR 0xdd4
+#define rOFDM_RxEVMCSI 0xdd8
+#define rOFDM_SIGReport 0xddc
+
+#define rTxAGC_Rate18_06 0xe00
+#define rTxAGC_Rate54_24 0xe04
+#define rTxAGC_CCK_Mcs32 0xe08
+#define rTxAGC_Mcs03_Mcs00 0xe10
+#define rTxAGC_Mcs07_Mcs04 0xe14
+#define rTxAGC_Mcs11_Mcs08 0xe18
+#define rTxAGC_Mcs15_Mcs12 0xe1c
+
+
+#define rZebra1_HSSIEnable 0x0
+#define rZebra1_TRxEnable1 0x1
+#define rZebra1_TRxEnable2 0x2
+#define rZebra1_AGC 0x4
+#define rZebra1_ChargePump 0x5
+#define rZebra1_Channel 0x7
+#define rZebra1_TxGain 0x8
+#define rZebra1_TxLPF 0x9
+#define rZebra1_RxLPF 0xb
+#define rZebra1_RxHPFCorner 0xc
+
+#define rGlobalCtrl 0
+#define rRTL8256_TxLPF 19
+#define rRTL8256_RxLPF 11
+
+#define rRTL8258_TxLPF 0x11
+#define rRTL8258_RxLPF 0x13
+#define rRTL8258_RSSILPF 0xa
+
+#define bBBResetB 0x100
+#define bGlobalResetB 0x200
+#define bOFDMTxStart 0x4
+#define bCCKTxStart 0x8
+#define bCRC32Debug 0x100
+#define bPMACLoopback 0x10
+#define bTxLSIG 0xffffff
+#define bOFDMTxRate 0xf
+#define bOFDMTxReserved 0x10
+#define bOFDMTxLength 0x1ffe0
+#define bOFDMTxParity 0x20000
+#define bTxHTSIG1 0xffffff
+#define bTxHTMCSRate 0x7f
+#define bTxHTBW 0x80
+#define bTxHTLength 0xffff00
+#define bTxHTSIG2 0xffffff
+#define bTxHTSmoothing 0x1
+#define bTxHTSounding 0x2
+#define bTxHTReserved 0x4
+#define bTxHTAggreation 0x8
+#define bTxHTSTBC 0x30
+#define bTxHTAdvanceCoding 0x40
+#define bTxHTShortGI 0x80
+#define bTxHTNumberHT_LTF 0x300
+#define bTxHTCRC8 0x3fc00
+#define bCounterReset 0x10000
+#define bNumOfOFDMTx 0xffff
+#define bNumOfCCKTx 0xffff0000
+#define bTxIdleInterval 0xffff
+#define bOFDMService 0xffff0000
+#define bTxMACHeader 0xffffffff
+#define bTxDataInit 0xff
+#define bTxHTMode 0x100
+#define bTxDataType 0x30000
+#define bTxRandomSeed 0xffffffff
+#define bCCKTxPreamble 0x1
+#define bCCKTxSFD 0xffff0000
+#define bCCKTxSIG 0xff
+#define bCCKTxService 0xff00
+#define bCCKLengthExt 0x8000
+#define bCCKTxLength 0xffff0000
+#define bCCKTxCRC16 0xffff
+#define bCCKTxStatus 0x1
+#define bOFDMTxStatus 0x2
+
+#define bRFMOD 0x1
+#define bJapanMode 0x2
+#define bCCKTxSC 0x30
+#define bCCKEn 0x1000000
+#define bOFDMEn 0x2000000
+#define bOFDMRxADCPhase 0x10000
+#define bOFDMTxDACPhase 0x40000
+#define bXATxAGC 0x3f
+#define bXBTxAGC 0xf00
+#define bXCTxAGC 0xf000
+#define bXDTxAGC 0xf0000
+#define bPAStart 0xf0000000
+#define bTRStart 0x00f00000
+#define bRFStart 0x0000f000
+#define bBBStart 0x000000f0
+#define bBBCCKStart 0x0000000f
+#define bPAEnd 0xf
+#define bTREnd 0x0f000000
+#define bRFEnd 0x000f0000
+#define bCCAMask 0x000000f0
+#define bR2RCCAMask 0x00000f00
+#define bHSSI_R2TDelay 0xf8000000
+#define bHSSI_T2RDelay 0xf80000
+#define bContTxHSSI 0x400
+#define bIGFromCCK 0x200
+#define bAGCAddress 0x3f
+#define bRxHPTx 0x7000
+#define bRxHPT2R 0x38000
+#define bRxHPCCKIni 0xc0000
+#define bAGCTxCode 0xc00000
+#define bAGCRxCode 0x300000
+#define b3WireDataLength 0x800
+#define b3WireAddressLength 0x400
+#define b3WireRFPowerDown 0x1
+#define b5GPAPEPolarity 0x40000000
+#define b2GPAPEPolarity 0x80000000
+#define bRFSW_TxDefaultAnt 0x3
+#define bRFSW_TxOptionAnt 0x30
+#define bRFSW_RxDefaultAnt 0x300
+#define bRFSW_RxOptionAnt 0x3000
+#define bRFSI_3WireData 0x1
+#define bRFSI_3WireClock 0x2
+#define bRFSI_3WireLoad 0x4
+#define bRFSI_3WireRW 0x8
+#define bRFSI_3Wire 0xf
+#define bRFSI_RFENV 0x10
+#define bRFSI_TRSW 0x20
+#define bRFSI_TRSWB 0x40
+#define bRFSI_ANTSW 0x100
+#define bRFSI_ANTSWB 0x200
+#define bRFSI_PAPE 0x400
+#define bRFSI_PAPE5G 0x800
+#define bBandSelect 0x1
+#define bHTSIG2_GI 0x80
+#define bHTSIG2_Smoothing 0x01
+#define bHTSIG2_Sounding 0x02
+#define bHTSIG2_Aggreaton 0x08
+#define bHTSIG2_STBC 0x30
+#define bHTSIG2_AdvCoding 0x40
+#define bHTSIG2_NumOfHTLTF 0x300
+#define bHTSIG2_CRC8 0x3fc
+#define bHTSIG1_MCS 0x7f
+#define bHTSIG1_BandWidth 0x80
+#define bHTSIG1_HTLength 0xffff
+#define bLSIG_Rate 0xf
+#define bLSIG_Reserved 0x10
+#define bLSIG_Length 0x1fffe
+#define bLSIG_Parity 0x20
+#define bCCKRxPhase 0x4
+#define bLSSIReadAddress 0x3f000000
+#define bLSSIReadEdge 0x80000000
+#define bLSSIReadBackData 0xfff
+#define bLSSIReadOKFlag 0x1000
+#define bCCKSampleRate 0x8
+
+#define bRegulator0Standby 0x1
+#define bRegulatorPLLStandby 0x2
+#define bRegulator1Standby 0x4
+#define bPLLPowerUp 0x8
+#define bDPLLPowerUp 0x10
+#define bDA10PowerUp 0x20
+#define bAD7PowerUp 0x200
+#define bDA6PowerUp 0x2000
+#define bXtalPowerUp 0x4000
+#define b40MDClkPowerUP 0x8000
+#define bDA6DebugMode 0x20000
+#define bDA6Swing 0x380000
+#define bADClkPhase 0x4000000
+#define b80MClkDelay 0x18000000
+#define bAFEWatchDogEnable 0x20000000
+#define bXtalCap 0x0f000000
+#define bXtalCap01 0xc0000000
+#define bXtalCap23 0x3
+#define bXtalCap92x 0x0f000000
+#define bIntDifClkEnable 0x400
+#define bExtSigClkEnable 0x800
+#define bBandgapMbiasPowerUp 0x10000
+#define bAD11SHGain 0xc0000
+#define bAD11InputRange 0x700000
+#define bAD11OPCurrent 0x3800000
+#define bIPathLoopback 0x4000000
+#define bQPathLoopback 0x8000000
+#define bAFELoopback 0x10000000
+#define bDA10Swing 0x7e0
+#define bDA10Reverse 0x800
+#define bDAClkSource 0x1000
+#define bAD7InputRange 0x6000
+#define bAD7Gain 0x38000
+#define bAD7OutputCMMode 0x40000
+#define bAD7InputCMMode 0x380000
+#define bAD7Current 0xc00000
+#define bRegulatorAdjust 0x7000000
+#define bAD11PowerUpAtTx 0x1
+#define bDA10PSAtTx 0x10
+#define bAD11PowerUpAtRx 0x100
+#define bDA10PSAtRx 0x1000
+
+#define bCCKRxAGCFormat 0x200
+
+#define bPSDFFTSamplepPoint 0xc000
+#define bPSDAverageNum 0x3000
+#define bIQPathControl 0xc00
+#define bPSDFreq 0x3ff
+#define bPSDAntennaPath 0x30
+#define bPSDIQSwitch 0x40
+#define bPSDRxTrigger 0x400000
+#define bPSDTxTrigger 0x80000000
+#define bPSDSineToneScale 0x7f000000
+#define bPSDReport 0xffff
+
+#define bOFDMTxSC 0x30000000
+#define bCCKTxOn 0x1
+#define bOFDMTxOn 0x2
+#define bDebugPage 0xfff
+#define bDebugItem 0xff
+#define bAntL 0x10
+#define bAntNonHT 0x100
+#define bAntHT1 0x1000
+#define bAntHT2 0x10000
+#define bAntHT1S1 0x100000
+#define bAntNonHTS1 0x1000000
+
+#define bCCKBBMode 0x3
+#define bCCKTxPowerSaving 0x80
+#define bCCKRxPowerSaving 0x40
+#define bCCKSideBand 0x10
+#define bCCKScramble 0x8
+#define bCCKAntDiversity 0x8000
+#define bCCKCarrierRecovery 0x4000
+#define bCCKTxRate 0x3000
+#define bCCKDCCancel 0x0800
+#define bCCKISICancel 0x0400
+#define bCCKMatchFilter 0x0200
+#define bCCKEqualizer 0x0100
+#define bCCKPreambleDetect 0x800000
+#define bCCKFastFalseCCA 0x400000
+#define bCCKChEstStart 0x300000
+#define bCCKCCACount 0x080000
+#define bCCKcs_lim 0x070000
+#define bCCKBistMode 0x80000000
+#define bCCKCCAMask 0x40000000
+#define bCCKTxDACPhase 0x4
+#define bCCKRxADCPhase 0x20000000
+#define bCCKr_cp_mode0 0x0100
+#define bCCKTxDCOffset 0xf0
+#define bCCKRxDCOffset 0xf
+#define bCCKCCAMode 0xc000
+#define bCCKFalseCS_lim 0x3f00
+#define bCCKCS_ratio 0xc00000
+#define bCCKCorgBit_sel 0x300000
+#define bCCKPD_lim 0x0f0000
+#define bCCKNewCCA 0x80000000
+#define bCCKRxHPofIG 0x8000
+#define bCCKRxIG 0x7f00
+#define bCCKLNAPolarity 0x800000
+#define bCCKRx1stGain 0x7f0000
+#define bCCKRFExtend 0x20000000
+#define bCCKRxAGCSatLevel 0x1f000000
+#define bCCKRxAGCSatCount 0xe0
+#define bCCKRxRFSettle 0x1f
+#define bCCKFixedRxAGC 0x8000
+#define bCCKAntennaPolarity 0x2000
+#define bCCKTxFilterType 0x0c00
+#define bCCKRxAGCReportType 0x0300
+#define bCCKRxDAGCEn 0x80000000
+#define bCCKRxDAGCPeriod 0x20000000
+#define bCCKRxDAGCSatLevel 0x1f000000
+#define bCCKTimingRecovery 0x800000
+#define bCCKTxC0 0x3f0000
+#define bCCKTxC1 0x3f000000
+#define bCCKTxC2 0x3f
+#define bCCKTxC3 0x3f00
+#define bCCKTxC4 0x3f0000
+#define bCCKTxC5 0x3f000000
+#define bCCKTxC6 0x3f
+#define bCCKTxC7 0x3f00
+#define bCCKDebugPort 0xff0000
+#define bCCKDACDebug 0x0f000000
+#define bCCKFalseAlarmEnable 0x8000
+#define bCCKFalseAlarmRead 0x4000
+#define bCCKTRSSI 0x7f
+#define bCCKRxAGCReport 0xfe
+#define bCCKRxReport_AntSel 0x80000000
+#define bCCKRxReport_MFOff 0x40000000
+#define bCCKRxRxReport_SQLoss 0x20000000
+#define bCCKRxReport_Pktloss 0x10000000
+#define bCCKRxReport_Lockedbit 0x08000000
+#define bCCKRxReport_RateError 0x04000000
+#define bCCKRxReport_RxRate 0x03000000
+#define bCCKRxFACounterLower 0xff
+#define bCCKRxFACounterUpper 0xff000000
+#define bCCKRxHPAGCStart 0xe000
+#define bCCKRxHPAGCFinal 0x1c00
+
+#define bCCKRxFalseAlarmEnable 0x8000
+#define bCCKFACounterFreeze 0x4000
+
+#define bCCKTxPathSel 0x10000000
+#define bCCKDefaultRxPath 0xc000000
+#define bCCKOptionRxPath 0x3000000
+
+#define bNumOfSTF 0x3
+#define bShift_L 0xc0
+#define bGI_TH 0xc
+#define bRxPathA 0x1
+#define bRxPathB 0x2
+#define bRxPathC 0x4
+#define bRxPathD 0x8
+#define bTxPathA 0x1
+#define bTxPathB 0x2
+#define bTxPathC 0x4
+#define bTxPathD 0x8
+#define bTRSSIFreq 0x200
+#define bADCBackoff 0x3000
+#define bDFIRBackoff 0xc000
+#define bTRSSILatchPhase 0x10000
+#define bRxIDCOffset 0xff
+#define bRxQDCOffset 0xff00
+#define bRxDFIRMode 0x1800000
+#define bRxDCNFType 0xe000000
+#define bRXIQImb_A 0x3ff
+#define bRXIQImb_B 0xfc00
+#define bRXIQImb_C 0x3f0000
+#define bRXIQImb_D 0xffc00000
+#define bDC_dc_Notch 0x60000
+#define bRxNBINotch 0x1f000000
+#define bPD_TH 0xf
+#define bPD_TH_Opt2 0xc000
+#define bPWED_TH 0x700
+#define bIfMF_Win_L 0x800
+#define bPD_Option 0x1000
+#define bMF_Win_L 0xe000
+#define bBW_Search_L 0x30000
+#define bwin_enh_L 0xc0000
+#define bBW_TH 0x700000
+#define bED_TH2 0x3800000
+#define bBW_option 0x4000000
+#define bRatio_TH 0x18000000
+#define bWindow_L 0xe0000000
+#define bSBD_Option 0x1
+#define bFrame_TH 0x1c
+#define bFS_Option 0x60
+#define bDC_Slope_check 0x80
+#define bFGuard_Counter_DC_L 0xe00
+#define bFrame_Weight_Short 0x7000
+#define bSub_Tune 0xe00000
+#define bFrame_DC_Length 0xe000000
+#define bSBD_start_offset 0x30000000
+#define bFrame_TH_2 0x7
+#define bFrame_GI2_TH 0x38
+#define bGI2_Sync_en 0x40
+#define bSarch_Short_Early 0x300
+#define bSarch_Short_Late 0xc00
+#define bSarch_GI2_Late 0x70000
+#define bCFOAntSum 0x1
+#define bCFOAcc 0x2
+#define bCFOStartOffset 0xc
+#define bCFOLookBack 0x70
+#define bCFOSumWeight 0x80
+#define bDAGCEnable 0x10000
+#define bTXIQImb_A 0x3ff
+#define bTXIQImb_B 0xfc00
+#define bTXIQImb_C 0x3f0000
+#define bTXIQImb_D 0xffc00000
+#define bTxIDCOffset 0xff
+#define bTxQDCOffset 0xff00
+#define bTxDFIRMode 0x10000
+#define bTxPesudoNoiseOn 0x4000000
+#define bTxPesudoNoise_A 0xff
+#define bTxPesudoNoise_B 0xff00
+#define bTxPesudoNoise_C 0xff0000
+#define bTxPesudoNoise_D 0xff000000
+#define bCCADropOption 0x20000
+#define bCCADropThres 0xfff00000
+#define bEDCCA_H 0xf
+#define bEDCCA_L 0xf0
+#define bLambda_ED 0x300
+#define bRxInitialGain 0x7f
+#define bRxAntDivEn 0x80
+#define bRxAGCAddressForLNA 0x7f00
+#define bRxHighPowerFlow 0x8000
+#define bRxAGCFreezeThres 0xc0000
+#define bRxFreezeStep_AGC1 0x300000
+#define bRxFreezeStep_AGC2 0xc00000
+#define bRxFreezeStep_AGC3 0x3000000
+#define bRxFreezeStep_AGC0 0xc000000
+#define bRxRssi_Cmp_En 0x10000000
+#define bRxQuickAGCEn 0x20000000
+#define bRxAGCFreezeThresMode 0x40000000
+#define bRxOverFlowCheckType 0x80000000
+#define bRxAGCShift 0x7f
+#define bTRSW_Tri_Only 0x80
+#define bPowerThres 0x300
+#define bRxAGCEn 0x1
+#define bRxAGCTogetherEn 0x2
+#define bRxAGCMin 0x4
+#define bRxHP_Ini 0x7
+#define bRxHP_TRLNA 0x70
+#define bRxHP_RSSI 0x700
+#define bRxHP_BBP1 0x7000
+#define bRxHP_BBP2 0x70000
+#define bRxHP_BBP3 0x700000
+#define bRSSI_H 0x7f0000
+#define bRSSI_Gen 0x7f000000
+#define bRxSettle_TRSW 0x7
+#define bRxSettle_LNA 0x38
+#define bRxSettle_RSSI 0x1c0
+#define bRxSettle_BBP 0xe00
+#define bRxSettle_RxHP 0x7000
+#define bRxSettle_AntSW_RSSI 0x38000
+#define bRxSettle_AntSW 0xc0000
+#define bRxProcessTime_DAGC 0x300000
+#define bRxSettle_HSSI 0x400000
+#define bRxProcessTime_BBPPW 0x800000
+#define bRxAntennaPowerShift 0x3000000
+#define bRSSITableSelect 0xc000000
+#define bRxHP_Final 0x7000000
+#define bRxHTSettle_BBP 0x7
+#define bRxHTSettle_HSSI 0x8
+#define bRxHTSettle_RxHP 0x70
+#define bRxHTSettle_BBPPW 0x80
+#define bRxHTSettle_Idle 0x300
+#define bRxHTSettle_Reserved 0x1c00
+#define bRxHTRxHPEn 0x8000
+#define bRxHTAGCFreezeThres 0x30000
+#define bRxHTAGCTogetherEn 0x40000
+#define bRxHTAGCMin 0x80000
+#define bRxHTAGCEn 0x100000
+#define bRxHTDAGCEn 0x200000
+#define bRxHTRxHP_BBP 0x1c00000
+#define bRxHTRxHP_Final 0xe0000000
+#define bRxPWRatioTH 0x3
+#define bRxPWRatioEn 0x4
+#define bRxMFHold 0x3800
+#define bRxPD_Delay_TH1 0x38
+#define bRxPD_Delay_TH2 0x1c0
+#define bRxPD_DC_COUNT_MAX 0x600
+#define bRxPD_Delay_TH 0x8000
+#define bRxProcess_Delay 0xf0000
+#define bRxSearchrange_GI2_Early 0x700000
+#define bRxFrame_Guard_Counter_L 0x3800000
+#define bRxSGI_Guard_L 0xc000000
+#define bRxSGI_Search_L 0x30000000
+#define bRxSGI_TH 0xc0000000
+#define bDFSCnt0 0xff
+#define bDFSCnt1 0xff00
+#define bDFSFlag 0xf0000
+
+#define bMFWeightSum 0x300000
+#define bMinIdxTH 0x7f000000
+
+#define bDAFormat 0x40000
+
+#define bTxChEmuEnable 0x01000000
+
+#define bTRSWIsolation_A 0x7f
+#define bTRSWIsolation_B 0x7f00
+#define bTRSWIsolation_C 0x7f0000
+#define bTRSWIsolation_D 0x7f000000
+
+#define bExtLNAGain 0x7c00
+
+#define bSTBCEn 0x4
+#define bAntennaMapping 0x10
+#define bNss 0x20
+#define bCFOAntSumD 0x200
+#define bPHYCounterReset 0x8000000
+#define bCFOReportGet 0x4000000
+#define bOFDMContinueTx 0x10000000
+#define bOFDMSingleCarrier 0x20000000
+#define bOFDMSingleTone 0x40000000
+#define bHTDetect 0x100
+#define bCFOEn 0x10000
+#define bCFOValue 0xfff00000
+#define bSigTone_Re 0x3f
+#define bSigTone_Im 0x7f00
+#define bCounter_CCA 0xffff
+#define bCounter_ParityFail 0xffff0000
+#define bCounter_RateIllegal 0xffff
+#define bCounter_CRC8Fail 0xffff0000
+#define bCounter_MCSNoSupport 0xffff
+#define bCounter_FastSync 0xffff
+#define bShortCFO 0xfff
+#define bShortCFOTLength 12
+#define bShortCFOFLength 11
+#define bLongCFO 0x7ff
+#define bLongCFOTLength 11
+#define bLongCFOFLength 11
+#define bTailCFO 0x1fff
+#define bTailCFOTLength 13
+#define bTailCFOFLength 12
+
+#define bmax_en_pwdB 0xffff
+#define bCC_power_dB 0xffff0000
+#define bnoise_pwdB 0xffff
+#define bPowerMeasTLength 10
+#define bPowerMeasFLength 3
+#define bRx_HT_BW 0x1
+#define bRxSC 0x6
+#define bRx_HT 0x8
+
+#define bNB_intf_det_on 0x1
+#define bIntf_win_len_cfg 0x30
+#define bNB_Intf_TH_cfg 0x1c0
+
+#define bRFGain 0x3f
+#define bTableSel 0x40
+#define bTRSW 0x80
+
+#define bRxSNR_A 0xff
+#define bRxSNR_B 0xff00
+#define bRxSNR_C 0xff0000
+#define bRxSNR_D 0xff000000
+#define bSNREVMTLength 8
+#define bSNREVMFLength 1
+
+#define bCSI1st 0xff
+#define bCSI2nd 0xff00
+#define bRxEVM1st 0xff0000
+#define bRxEVM2nd 0xff000000
+
+#define bSIGEVM 0xff
+#define bPWDB 0xff00
+#define bSGIEN 0x10000
+
+#define bSFactorQAM1 0xf
+#define bSFactorQAM2 0xf0
+#define bSFactorQAM3 0xf00
+#define bSFactorQAM4 0xf000
+#define bSFactorQAM5 0xf0000
+#define bSFactorQAM6 0xf0000
+#define bSFactorQAM7 0xf00000
+#define bSFactorQAM8 0xf000000
+#define bSFactorQAM9 0xf0000000
+#define bCSIScheme 0x100000
+
+#define bNoiseLvlTopSet 0x3
+#define bChSmooth 0x4
+#define bChSmoothCfg1 0x38
+#define bChSmoothCfg2 0x1c0
+#define bChSmoothCfg3 0xe00
+#define bChSmoothCfg4 0x7000
+#define bMRCMode 0x800000
+#define bTHEVMCfg 0x7000000
+
+#define bLoopFitType 0x1
+#define bUpdCFO 0x40
+#define bUpdCFOOffData 0x80
+#define bAdvUpdCFO 0x100
+#define bAdvTimeCtrl 0x800
+#define bUpdClko 0x1000
+#define bFC 0x6000
+#define bTrackingMode 0x8000
+#define bPhCmpEnable 0x10000
+#define bUpdClkoLTF 0x20000
+#define bComChCFO 0x40000
+#define bCSIEstiMode 0x80000
+#define bAdvUpdEqz 0x100000
+#define bUChCfg 0x7000000
+#define bUpdEqz 0x8000000
+
+#define bTxAGCRate18_06 0x7f7f7f7f
+#define bTxAGCRate54_24 0x7f7f7f7f
+#define bTxAGCRateMCS32 0x7f
+#define bTxAGCRateCCK 0x7f00
+#define bTxAGCRateMCS3_MCS0 0x7f7f7f7f
+#define bTxAGCRateMCS7_MCS4 0x7f7f7f7f
+#define bTxAGCRateMCS11_MCS8 0x7f7f7f7f
+#define bTxAGCRateMCS15_MCS12 0x7f7f7f7f
+
+
+#define bRxPesudoNoiseOn 0x20000000
+#define bRxPesudoNoise_A 0xff
+#define bRxPesudoNoise_B 0xff00
+#define bRxPesudoNoise_C 0xff0000
+#define bRxPesudoNoise_D 0xff000000
+#define bPesudoNoiseState_A 0xffff
+#define bPesudoNoiseState_B 0xffff0000
+#define bPesudoNoiseState_C 0xffff
+#define bPesudoNoiseState_D 0xffff0000
+
+#define bZebra1_HSSIEnable 0x8
+#define bZebra1_TRxControl 0xc00
+#define bZebra1_TRxGainSetting 0x07f
+#define bZebra1_RxCorner 0xc00
+#define bZebra1_TxChargePump 0x38
+#define bZebra1_RxChargePump 0x7
+#define bZebra1_ChannelNum 0xf80
+#define bZebra1_TxLPFBW 0x400
+#define bZebra1_RxLPFBW 0x600
+
+#define bRTL8256RegModeCtrl1 0x100
+#define bRTL8256RegModeCtrl0 0x40
+#define bRTL8256_TxLPFBW 0x18
+#define bRTL8256_RxLPFBW 0x600
+
+#define bRTL8258_TxLPFBW 0xc
+#define bRTL8258_RxLPFBW 0xc00
+#define bRTL8258_RSSILPFBW 0xc0
+
+#define bByte0 0x1
+#define bByte1 0x2
+#define bByte2 0x4
+#define bByte3 0x8
+#define bWord0 0x3
+#define bWord1 0xc
+#define bDWord 0xf
+
+#define bMaskByte0 0xff
+#define bMaskByte1 0xff00
+#define bMaskByte2 0xff0000
+#define bMaskByte3 0xff000000
+#define bMaskHWord 0xffff0000
+#define bMaskLWord 0x0000ffff
+#define bMaskDWord 0xffffffff
+
+#define bMask12Bits 0xfff
+
+#define bEnable 0x1
+#define bDisable 0x0
+
+#define LeftAntenna 0x0
+#define RightAntenna 0x1
+
+#define tCheckTxStatus 500
+#define tUpdateRxCounter 100
+
+#define rateCCK 0
+#define rateOFDM 1
+#define rateHT 2
+
+#define bPMAC_End 0x1ff
+#define bFPGAPHY0_End 0x8ff
+#define bFPGAPHY1_End 0x9ff
+#define bCCKPHY0_End 0xaff
+#define bOFDMPHY0_End 0xcff
+#define bOFDMPHY1_End 0xdff
+
+
+#define bPMACControl 0x0
+#define bWMACControl 0x1
+#define bWNICControl 0x2
+
+#define PathA 0x0
+#define PathB 0x1
+#define PathC 0x2
+#define PathD 0x3
+
+#define rRTL8256RxMixerPole 0xb
+#define bZebraRxMixerPole 0x6
+#define rRTL8256TxBBOPBias 0x9
+#define bRTL8256TxBBOPBias 0x400
+#define rRTL8256TxBBBW 19
+#define bRTL8256TxBBBW 0x18
+
+#endif
diff --git a/drivers/staging/rtl8192e/rtl8192e/r819xE_phyreg.h b/drivers/staging/rtl8192e/rtl8192e/r819xE_phyreg.h
new file mode 100644
index 000000000..03eee3d05
--- /dev/null
+++ b/drivers/staging/rtl8192e/rtl8192e/r819xE_phyreg.h
@@ -0,0 +1,908 @@
+#ifndef _R819XU_PHYREG_H
+#define _R819XU_PHYREG_H
+
+
+#define RF_DATA 0x1d4 // FW will write RF data in the register.
+
+//Register //duplicate register due to connection: RF_Mode, TRxRN, NumOf L-STF
+//page 1
+#define rPMAC_Reset 0x100
+#define rPMAC_TxStart 0x104
+#define rPMAC_TxLegacySIG 0x108
+#define rPMAC_TxHTSIG1 0x10c
+#define rPMAC_TxHTSIG2 0x110
+#define rPMAC_PHYDebug 0x114
+#define rPMAC_TxPacketNum 0x118
+#define rPMAC_TxIdle 0x11c
+#define rPMAC_TxMACHeader0 0x120
+#define rPMAC_TxMACHeader1 0x124
+#define rPMAC_TxMACHeader2 0x128
+#define rPMAC_TxMACHeader3 0x12c
+#define rPMAC_TxMACHeader4 0x130
+#define rPMAC_TxMACHeader5 0x134
+#define rPMAC_TxDataType 0x138
+#define rPMAC_TxRandomSeed 0x13c
+#define rPMAC_CCKPLCPPreamble 0x140
+#define rPMAC_CCKPLCPHeader 0x144
+#define rPMAC_CCKCRC16 0x148
+#define rPMAC_OFDMRxCRC32OK 0x170
+#define rPMAC_OFDMRxCRC32Er 0x174
+#define rPMAC_OFDMRxParityEr 0x178
+#define rPMAC_OFDMRxCRC8Er 0x17c
+#define rPMAC_CCKCRxRC16Er 0x180
+#define rPMAC_CCKCRxRC32Er 0x184
+#define rPMAC_CCKCRxRC32OK 0x188
+#define rPMAC_TxStatus 0x18c
+
+//90P
+#define MCS_TXAGC 0x340 // MCS AGC
+#define CCK_TXAGC 0x348 // CCK AGC
+
+#define MacBlkCtrl 0x403 // Mac block on/off control register
+
+//page8
+#define rFPGA0_RFMOD 0x800 //RF mode & CCK TxSC
+#define rFPGA0_TxInfo 0x804
+#define rFPGA0_PSDFunction 0x808
+#define rFPGA0_TxGainStage 0x80c
+#define rFPGA0_RFTiming1 0x810
+#define rFPGA0_RFTiming2 0x814
+//#define rFPGA0_XC_RFTiming 0x818
+//#define rFPGA0_XD_RFTiming 0x81c
+#define rFPGA0_XA_HSSIParameter1 0x820
+#define rFPGA0_XA_HSSIParameter2 0x824
+#define rFPGA0_XB_HSSIParameter1 0x828
+#define rFPGA0_XB_HSSIParameter2 0x82c
+#define rFPGA0_XC_HSSIParameter1 0x830
+#define rFPGA0_XC_HSSIParameter2 0x834
+#define rFPGA0_XD_HSSIParameter1 0x838
+#define rFPGA0_XD_HSSIParameter2 0x83c
+#define rFPGA0_XA_LSSIParameter 0x840
+#define rFPGA0_XB_LSSIParameter 0x844
+#define rFPGA0_XC_LSSIParameter 0x848
+#define rFPGA0_XD_LSSIParameter 0x84c
+#define rFPGA0_RFWakeUpParameter 0x850
+#define rFPGA0_RFSleepUpParameter 0x854
+#define rFPGA0_XAB_SwitchControl 0x858
+#define rFPGA0_XCD_SwitchControl 0x85c
+#define rFPGA0_XA_RFInterfaceOE 0x860
+#define rFPGA0_XB_RFInterfaceOE 0x864
+#define rFPGA0_XC_RFInterfaceOE 0x868
+#define rFPGA0_XD_RFInterfaceOE 0x86c
+#define rFPGA0_XAB_RFInterfaceSW 0x870
+#define rFPGA0_XCD_RFInterfaceSW 0x874
+#define rFPGA0_XAB_RFParameter 0x878
+#define rFPGA0_XCD_RFParameter 0x87c
+#define rFPGA0_AnalogParameter1 0x880
+#define rFPGA0_AnalogParameter2 0x884
+#define rFPGA0_AnalogParameter3 0x888
+#define rFPGA0_AnalogParameter4 0x88c
+#define rFPGA0_XA_LSSIReadBack 0x8a0
+#define rFPGA0_XB_LSSIReadBack 0x8a4
+#define rFPGA0_XC_LSSIReadBack 0x8a8
+#define rFPGA0_XD_LSSIReadBack 0x8ac
+#define rFPGA0_PSDReport 0x8b4
+#define rFPGA0_XAB_RFInterfaceRB 0x8e0
+#define rFPGA0_XCD_RFInterfaceRB 0x8e4
+
+/* Page 9 - RF mode & OFDM TxSC */
+#define rFPGA1_RFMOD 0x900
+#define rFPGA1_TxBlock 0x904
+#define rFPGA1_DebugSelect 0x908
+#define rFPGA1_TxInfo 0x90c
+
+/* Page a */
+#define rCCK0_System 0xa00
+#define rCCK0_AFESetting 0xa04
+#define rCCK0_CCA 0xa08
+/* AGC default value, saturation level */
+#define rCCK0_RxAGC1 0xa0c
+/* AGC & DAGC */
+#define rCCK0_RxAGC2 0xa10
+#define rCCK0_RxHP 0xa14
+/* Timing recovery & channel estimation threshold */
+#define rCCK0_DSPParameter1 0xa18
+/* SQ threshold */
+#define rCCK0_DSPParameter2 0xa1c
+#define rCCK0_TxFilter1 0xa20
+#define rCCK0_TxFilter2 0xa24
+/* Debug port and TX filter 3 */
+#define rCCK0_DebugPort 0xa28
+#define rCCK0_FalseAlarmReport 0xa2c
+#define rCCK0_TRSSIReport 0xa50
+#define rCCK0_RxReport 0xa54
+#define rCCK0_FACounterLower 0xa5c
+#define rCCK0_FACounterUpper 0xa58
+
+/* Page c */
+#define rOFDM0_LSTF 0xc00
+#define rOFDM0_TRxPathEnable 0xc04
+#define rOFDM0_TRMuxPar 0xc08
+#define rOFDM0_TRSWIsolation 0xc0c
+/* RxIQ DC offset, Rx digital filter, DC notch filter */
+#define rOFDM0_XARxAFE 0xc10
+/* RxIQ imblance matrix */
+#define rOFDM0_XARxIQImbalance 0xc14
+#define rOFDM0_XBRxAFE 0xc18
+#define rOFDM0_XBRxIQImbalance 0xc1c
+#define rOFDM0_XCRxAFE 0xc20
+#define rOFDM0_XCRxIQImbalance 0xc24
+#define rOFDM0_XDRxAFE 0xc28
+#define rOFDM0_XDRxIQImbalance 0xc2c
+/* PD, BW & SBD */
+#define rOFDM0_RxDetector1 0xc30
+/* SBD */
+#define rOFDM0_RxDetector2 0xc34
+/* Frame Sync */
+#define rOFDM0_RxDetector3 0xc38
+/* PD, SBD, Frame Sync & Short-GI */
+#define rOFDM0_RxDetector4 0xc3c
+/* Rx Sync Path */
+#define rOFDM0_RxDSP 0xc40
+/* CFO & DAGC */
+#define rOFDM0_CFOandDAGC 0xc44
+/* CCA Drop threshold */
+#define rOFDM0_CCADropThreshold 0xc48
+/* Energy CCA */
+#define rOFDM0_ECCAThreshold 0xc4c
+#define rOFDM0_XAAGCCore1 0xc50
+#define rOFDM0_XAAGCCore2 0xc54
+#define rOFDM0_XBAGCCore1 0xc58
+#define rOFDM0_XBAGCCore2 0xc5c
+#define rOFDM0_XCAGCCore1 0xc60
+#define rOFDM0_XCAGCCore2 0xc64
+#define rOFDM0_XDAGCCore1 0xc68
+#define rOFDM0_XDAGCCore2 0xc6c
+#define rOFDM0_AGCParameter1 0xc70
+#define rOFDM0_AGCParameter2 0xc74
+#define rOFDM0_AGCRSSITable 0xc78
+#define rOFDM0_HTSTFAGC 0xc7c
+#define rOFDM0_XATxIQImbalance 0xc80
+#define rOFDM0_XATxAFE 0xc84
+#define rOFDM0_XBTxIQImbalance 0xc88
+#define rOFDM0_XBTxAFE 0xc8c
+#define rOFDM0_XCTxIQImbalance 0xc90
+#define rOFDM0_XCTxAFE 0xc94
+#define rOFDM0_XDTxIQImbalance 0xc98
+#define rOFDM0_XDTxAFE 0xc9c
+#define rOFDM0_RxHPParameter 0xce0
+#define rOFDM0_TxPseudoNoiseWgt 0xce4
+#define rOFDM0_FrameSync 0xcf0
+#define rOFDM0_DFSReport 0xcf4
+#define rOFDM0_TxCoeff1 0xca4
+#define rOFDM0_TxCoeff2 0xca8
+#define rOFDM0_TxCoeff3 0xcac
+#define rOFDM0_TxCoeff4 0xcb0
+#define rOFDM0_TxCoeff5 0xcb4
+#define rOFDM0_TxCoeff6 0xcb8
+
+
+/* Page d */
+#define rOFDM1_LSTF 0xd00
+#define rOFDM1_TRxPathEnable 0xd04
+#define rOFDM1_CFO 0xd08
+#define rOFDM1_CSI1 0xd10
+#define rOFDM1_SBD 0xd14
+#define rOFDM1_CSI2 0xd18
+#define rOFDM1_CFOTracking 0xd2c
+#define rOFDM1_TRxMesaure1 0xd34
+#define rOFDM1_IntfDet 0xd3c
+#define rOFDM1_PseudoNoiseStateAB 0xd50
+#define rOFDM1_PseudoNoiseStateCD 0xd54
+#define rOFDM1_RxPseudoNoiseWgt 0xd58
+/* cca, parity fail */
+#define rOFDM_PHYCounter1 0xda0
+/* rate illegal, crc8 fail */
+#define rOFDM_PHYCounter2 0xda4
+/* MCS not supported */
+#define rOFDM_PHYCounter3 0xda8
+#define rOFDM_ShortCFOAB 0xdac
+#define rOFDM_ShortCFOCD 0xdb0
+#define rOFDM_LongCFOAB 0xdb4
+#define rOFDM_LongCFOCD 0xdb8
+#define rOFDM_TailCFOAB 0xdbc
+#define rOFDM_TailCFOCD 0xdc0
+#define rOFDM_PWMeasure1 0xdc4
+#define rOFDM_PWMeasure2 0xdc8
+#define rOFDM_BWReport 0xdcc
+#define rOFDM_AGCReport 0xdd0
+#define rOFDM_RxSNR 0xdd4
+#define rOFDM_RxEVMCSI 0xdd8
+#define rOFDM_SIGReport 0xddc
+
+/* Page e */
+#define rTxAGC_Rate18_06 0xe00
+#define rTxAGC_Rate54_24 0xe04
+#define rTxAGC_CCK_Mcs32 0xe08
+#define rTxAGC_Mcs03_Mcs00 0xe10
+#define rTxAGC_Mcs07_Mcs04 0xe14
+#define rTxAGC_Mcs11_Mcs08 0xe18
+#define rTxAGC_Mcs15_Mcs12 0xe1c
+
+
+/* RF Zebra 1 */
+#define rZebra1_HSSIEnable 0x0
+#define rZebra1_TRxEnable1 0x1
+#define rZebra1_TRxEnable2 0x2
+#define rZebra1_AGC 0x4
+#define rZebra1_ChargePump 0x5
+#define rZebra1_Channel 0x7
+#define rZebra1_TxGain 0x8
+#define rZebra1_TxLPF 0x9
+#define rZebra1_RxLPF 0xb
+#define rZebra1_RxHPFCorner 0xc
+
+/* Zebra 4 */
+#define rGlobalCtrl 0
+#define rRTL8256_TxLPF 19
+#define rRTL8256_RxLPF 11
+
+/* RTL8258 */
+#define rRTL8258_TxLPF 0x11
+#define rRTL8258_RxLPF 0x13
+#define rRTL8258_RSSILPF 0xa
+
+/* Bit Mask */
+/* Page 1 */
+#define bBBResetB 0x100
+#define bGlobalResetB 0x200
+#define bOFDMTxStart 0x4
+#define bCCKTxStart 0x8
+#define bCRC32Debug 0x100
+#define bPMACLoopback 0x10
+#define bTxLSIG 0xffffff
+#define bOFDMTxRate 0xf
+#define bOFDMTxReserved 0x10
+#define bOFDMTxLength 0x1ffe0
+#define bOFDMTxParity 0x20000
+#define bTxHTSIG1 0xffffff
+#define bTxHTMCSRate 0x7f
+#define bTxHTBW 0x80
+#define bTxHTLength 0xffff00
+#define bTxHTSIG2 0xffffff
+#define bTxHTSmoothing 0x1
+#define bTxHTSounding 0x2
+#define bTxHTReserved 0x4
+#define bTxHTAggreation 0x8
+#define bTxHTSTBC 0x30
+#define bTxHTAdvanceCoding 0x40
+#define bTxHTShortGI 0x80
+#define bTxHTNumberHT_LTF 0x300
+#define bTxHTCRC8 0x3fc00
+#define bCounterReset 0x10000
+#define bNumOfOFDMTx 0xffff
+#define bNumOfCCKTx 0xffff0000
+#define bTxIdleInterval 0xffff
+#define bOFDMService 0xffff0000
+#define bTxMACHeader 0xffffffff
+#define bTxDataInit 0xff
+#define bTxHTMode 0x100
+#define bTxDataType 0x30000
+#define bTxRandomSeed 0xffffffff
+#define bCCKTxPreamble 0x1
+#define bCCKTxSFD 0xffff0000
+#define bCCKTxSIG 0xff
+#define bCCKTxService 0xff00
+#define bCCKLengthExt 0x8000
+#define bCCKTxLength 0xffff0000
+#define bCCKTxCRC16 0xffff
+#define bCCKTxStatus 0x1
+#define bOFDMTxStatus 0x2
+
+/* Page 8 */
+#define bRFMOD 0x1
+#define bJapanMode 0x2
+#define bCCKTxSC 0x30
+#define bCCKEn 0x1000000
+#define bOFDMEn 0x2000000
+#define bOFDMRxADCPhase 0x10000
+#define bOFDMTxDACPhase 0x40000
+#define bXATxAGC 0x3f
+#define bXBTxAGC 0xf00
+#define bXCTxAGC 0xf000
+#define bXDTxAGC 0xf0000
+#define bPAStart 0xf0000000
+#define bTRStart 0x00f00000
+#define bRFStart 0x0000f000
+#define bBBStart 0x000000f0
+#define bBBCCKStart 0x0000000f
+/* Reg x814 */
+#define bPAEnd 0xf
+#define bTREnd 0x0f000000
+#define bRFEnd 0x000f0000
+/* T2R */
+#define bCCAMask 0x000000f0
+#define bR2RCCAMask 0x00000f00
+#define bHSSI_R2TDelay 0xf8000000
+#define bHSSI_T2RDelay 0xf80000
+/* Channel gain at continue TX. */
+#define bContTxHSSI 0x400
+#define bIGFromCCK 0x200
+#define bAGCAddress 0x3f
+#define bRxHPTx 0x7000
+#define bRxHPT2R 0x38000
+#define bRxHPCCKIni 0xc0000
+#define bAGCTxCode 0xc00000
+#define bAGCRxCode 0x300000
+#define b3WireDataLength 0x800
+#define b3WireAddressLength 0x400
+#define b3WireRFPowerDown 0x1
+/*#define bHWSISelect 0x8 */
+#define b5GPAPEPolarity 0x40000000
+#define b2GPAPEPolarity 0x80000000
+#define bRFSW_TxDefaultAnt 0x3
+#define bRFSW_TxOptionAnt 0x30
+#define bRFSW_RxDefaultAnt 0x300
+#define bRFSW_RxOptionAnt 0x3000
+#define bRFSI_3WireData 0x1
+#define bRFSI_3WireClock 0x2
+#define bRFSI_3WireLoad 0x4
+#define bRFSI_3WireRW 0x8
+/* 3-wire total control */
+#define bRFSI_3Wire 0xf
+#define bRFSI_RFENV 0x10
+#define bRFSI_TRSW 0x20
+#define bRFSI_TRSWB 0x40
+#define bRFSI_ANTSW 0x100
+#define bRFSI_ANTSWB 0x200
+#define bRFSI_PAPE 0x400
+#define bRFSI_PAPE5G 0x800
+#define bBandSelect 0x1
+#define bHTSIG2_GI 0x80
+#define bHTSIG2_Smoothing 0x01
+#define bHTSIG2_Sounding 0x02
+#define bHTSIG2_Aggreaton 0x08
+#define bHTSIG2_STBC 0x30
+#define bHTSIG2_AdvCoding 0x40
+#define bHTSIG2_NumOfHTLTF 0x300
+#define bHTSIG2_CRC8 0x3fc
+#define bHTSIG1_MCS 0x7f
+#define bHTSIG1_BandWidth 0x80
+#define bHTSIG1_HTLength 0xffff
+#define bLSIG_Rate 0xf
+#define bLSIG_Reserved 0x10
+#define bLSIG_Length 0x1fffe
+#define bLSIG_Parity 0x20
+#define bCCKRxPhase 0x4
+/* LSSI "read" address */
+#define bLSSIReadAddress 0x3f000000
+/* LSSI "read" edge signal */
+#define bLSSIReadEdge 0x80000000
+#define bLSSIReadBackData 0xfff
+#define bLSSIReadOKFlag 0x1000
+/* 0: 44 MHz, 1: 88MHz */
+#define bCCKSampleRate 0x8
+
+#define bRegulator0Standby 0x1
+#define bRegulatorPLLStandby 0x2
+#define bRegulator1Standby 0x4
+#define bPLLPowerUp 0x8
+#define bDPLLPowerUp 0x10
+#define bDA10PowerUp 0x20
+#define bAD7PowerUp 0x200
+#define bDA6PowerUp 0x2000
+#define bXtalPowerUp 0x4000
+#define b40MDClkPowerUP 0x8000
+#define bDA6DebugMode 0x20000
+#define bDA6Swing 0x380000
+#define bADClkPhase 0x4000000
+#define b80MClkDelay 0x18000000
+#define bAFEWatchDogEnable 0x20000000
+#define bXtalCap 0x0f000000
+#define bXtalCap01 0xc0000000
+#define bXtalCap23 0x3
+#define bXtalCap92x 0x0f000000
+#define bIntDifClkEnable 0x400
+#define bExtSigClkEnable 0x800
+#define bBandgapMbiasPowerUp 0x10000
+#define bAD11SHGain 0xc0000
+#define bAD11InputRange 0x700000
+#define bAD11OPCurrent 0x3800000
+#define bIPathLoopback 0x4000000
+#define bQPathLoopback 0x8000000
+#define bAFELoopback 0x10000000
+#define bDA10Swing 0x7e0
+#define bDA10Reverse 0x800
+#define bDAClkSource 0x1000
+#define bAD7InputRange 0x6000
+#define bAD7Gain 0x38000
+#define bAD7OutputCMMode 0x40000
+#define bAD7InputCMMode 0x380000
+#define bAD7Current 0xc00000
+#define bRegulatorAdjust 0x7000000
+#define bAD11PowerUpAtTx 0x1
+#define bDA10PSAtTx 0x10
+#define bAD11PowerUpAtRx 0x100
+#define bDA10PSAtRx 0x1000
+
+#define bCCKRxAGCFormat 0x200
+
+#define bPSDFFTSamplepPoint 0xc000
+#define bPSDAverageNum 0x3000
+#define bIQPathControl 0xc00
+#define bPSDFreq 0x3ff
+#define bPSDAntennaPath 0x30
+#define bPSDIQSwitch 0x40
+#define bPSDRxTrigger 0x400000
+#define bPSDTxTrigger 0x80000000
+#define bPSDSineToneScale 0x7f000000
+#define bPSDReport 0xffff
+
+/* Page 8 */
+#define bOFDMTxSC 0x30000000
+#define bCCKTxOn 0x1
+#define bOFDMTxOn 0x2
+/* Reset debug page and also HWord, LWord */
+#define bDebugPage 0xfff
+/* Reset debug page and LWord */
+#define bDebugItem 0xff
+#define bAntL 0x10
+#define bAntNonHT 0x100
+#define bAntHT1 0x1000
+#define bAntHT2 0x10000
+#define bAntHT1S1 0x100000
+#define bAntNonHTS1 0x1000000
+
+/* Page a */
+#define bCCKBBMode 0x3
+#define bCCKTxPowerSaving 0x80
+#define bCCKRxPowerSaving 0x40
+#define bCCKSideBand 0x10
+#define bCCKScramble 0x8
+#define bCCKAntDiversity 0x8000
+#define bCCKCarrierRecovery 0x4000
+#define bCCKTxRate 0x3000
+#define bCCKDCCancel 0x0800
+#define bCCKISICancel 0x0400
+#define bCCKMatchFilter 0x0200
+#define bCCKEqualizer 0x0100
+#define bCCKPreambleDetect 0x800000
+#define bCCKFastFalseCCA 0x400000
+#define bCCKChEstStart 0x300000
+#define bCCKCCACount 0x080000
+#define bCCKcs_lim 0x070000
+#define bCCKBistMode 0x80000000
+#define bCCKCCAMask 0x40000000
+#define bCCKTxDACPhase 0x4
+/* r_rx_clk */
+#define bCCKRxADCPhase 0x20000000
+#define bCCKr_cp_mode0 0x0100
+#define bCCKTxDCOffset 0xf0
+#define bCCKRxDCOffset 0xf
+#define bCCKCCAMode 0xc000
+#define bCCKFalseCS_lim 0x3f00
+#define bCCKCS_ratio 0xc00000
+#define bCCKCorgBit_sel 0x300000
+#define bCCKPD_lim 0x0f0000
+#define bCCKNewCCA 0x80000000
+#define bCCKRxHPofIG 0x8000
+#define bCCKRxIG 0x7f00
+#define bCCKLNAPolarity 0x800000
+#define bCCKRx1stGain 0x7f0000
+/* CCK Rx Initial gain polarity */
+#define bCCKRFExtend 0x20000000
+#define bCCKRxAGCSatLevel 0x1f000000
+#define bCCKRxAGCSatCount 0xe0
+/* AGCSAmp_dly */
+#define bCCKRxRFSettle 0x1f
+#define bCCKFixedRxAGC 0x8000
+/*#define bCCKRxAGCFormat 0x4000 remove to HSSI register 0x824 */
+#define bCCKAntennaPolarity 0x2000
+#define bCCKTxFilterType 0x0c00
+#define bCCKRxAGCReportType 0x0300
+#define bCCKRxDAGCEn 0x80000000
+#define bCCKRxDAGCPeriod 0x20000000
+#define bCCKRxDAGCSatLevel 0x1f000000
+#define bCCKTimingRecovery 0x800000
+#define bCCKTxC0 0x3f0000
+#define bCCKTxC1 0x3f000000
+#define bCCKTxC2 0x3f
+#define bCCKTxC3 0x3f00
+#define bCCKTxC4 0x3f0000
+#define bCCKTxC5 0x3f000000
+#define bCCKTxC6 0x3f
+#define bCCKTxC7 0x3f00
+#define bCCKDebugPort 0xff0000
+#define bCCKDACDebug 0x0f000000
+#define bCCKFalseAlarmEnable 0x8000
+#define bCCKFalseAlarmRead 0x4000
+#define bCCKTRSSI 0x7f
+#define bCCKRxAGCReport 0xfe
+#define bCCKRxReport_AntSel 0x80000000
+#define bCCKRxReport_MFOff 0x40000000
+#define bCCKRxRxReport_SQLoss 0x20000000
+#define bCCKRxReport_Pktloss 0x10000000
+#define bCCKRxReport_Lockedbit 0x08000000
+#define bCCKRxReport_RateError 0x04000000
+#define bCCKRxReport_RxRate 0x03000000
+#define bCCKRxFACounterLower 0xff
+#define bCCKRxFACounterUpper 0xff000000
+#define bCCKRxHPAGCStart 0xe000
+#define bCCKRxHPAGCFinal 0x1c00
+
+#define bCCKRxFalseAlarmEnable 0x8000
+#define bCCKFACounterFreeze 0x4000
+
+#define bCCKTxPathSel 0x10000000
+#define bCCKDefaultRxPath 0xc000000
+#define bCCKOptionRxPath 0x3000000
+
+/* Page c */
+#define bNumOfSTF 0x3
+#define bShift_L 0xc0
+#define bGI_TH 0xc
+#define bRxPathA 0x1
+#define bRxPathB 0x2
+#define bRxPathC 0x4
+#define bRxPathD 0x8
+#define bTxPathA 0x1
+#define bTxPathB 0x2
+#define bTxPathC 0x4
+#define bTxPathD 0x8
+#define bTRSSIFreq 0x200
+#define bADCBackoff 0x3000
+#define bDFIRBackoff 0xc000
+#define bTRSSILatchPhase 0x10000
+#define bRxIDCOffset 0xff
+#define bRxQDCOffset 0xff00
+#define bRxDFIRMode 0x1800000
+#define bRxDCNFType 0xe000000
+#define bRXIQImb_A 0x3ff
+#define bRXIQImb_B 0xfc00
+#define bRXIQImb_C 0x3f0000
+#define bRXIQImb_D 0xffc00000
+#define bDC_dc_Notch 0x60000
+#define bRxNBINotch 0x1f000000
+#define bPD_TH 0xf
+#define bPD_TH_Opt2 0xc000
+#define bPWED_TH 0x700
+#define bIfMF_Win_L 0x800
+#define bPD_Option 0x1000
+#define bMF_Win_L 0xe000
+#define bBW_Search_L 0x30000
+#define bwin_enh_L 0xc0000
+#define bBW_TH 0x700000
+#define bED_TH2 0x3800000
+#define bBW_option 0x4000000
+#define bRatio_TH 0x18000000
+#define bWindow_L 0xe0000000
+#define bSBD_Option 0x1
+#define bFrame_TH 0x1c
+#define bFS_Option 0x60
+#define bDC_Slope_check 0x80
+#define bFGuard_Counter_DC_L 0xe00
+#define bFrame_Weight_Short 0x7000
+#define bSub_Tune 0xe00000
+#define bFrame_DC_Length 0xe000000
+#define bSBD_start_offset 0x30000000
+#define bFrame_TH_2 0x7
+#define bFrame_GI2_TH 0x38
+#define bGI2_Sync_en 0x40
+#define bSarch_Short_Early 0x300
+#define bSarch_Short_Late 0xc00
+#define bSarch_GI2_Late 0x70000
+#define bCFOAntSum 0x1
+#define bCFOAcc 0x2
+#define bCFOStartOffset 0xc
+#define bCFOLookBack 0x70
+#define bCFOSumWeight 0x80
+#define bDAGCEnable 0x10000
+#define bTXIQImb_A 0x3ff
+#define bTXIQImb_B 0xfc00
+#define bTXIQImb_C 0x3f0000
+#define bTXIQImb_D 0xffc00000
+#define bTxIDCOffset 0xff
+#define bTxQDCOffset 0xff00
+#define bTxDFIRMode 0x10000
+#define bTxPesudoNoiseOn 0x4000000
+#define bTxPesudoNoise_A 0xff
+#define bTxPesudoNoise_B 0xff00
+#define bTxPesudoNoise_C 0xff0000
+#define bTxPesudoNoise_D 0xff000000
+#define bCCADropOption 0x20000
+#define bCCADropThres 0xfff00000
+#define bEDCCA_H 0xf
+#define bEDCCA_L 0xf0
+#define bLambda_ED 0x300
+#define bRxInitialGain 0x7f
+#define bRxAntDivEn 0x80
+#define bRxAGCAddressForLNA 0x7f00
+#define bRxHighPowerFlow 0x8000
+#define bRxAGCFreezeThres 0xc0000
+#define bRxFreezeStep_AGC1 0x300000
+#define bRxFreezeStep_AGC2 0xc00000
+#define bRxFreezeStep_AGC3 0x3000000
+#define bRxFreezeStep_AGC0 0xc000000
+#define bRxRssi_Cmp_En 0x10000000
+#define bRxQuickAGCEn 0x20000000
+#define bRxAGCFreezeThresMode 0x40000000
+#define bRxOverFlowCheckType 0x80000000
+#define bRxAGCShift 0x7f
+#define bTRSW_Tri_Only 0x80
+#define bPowerThres 0x300
+#define bRxAGCEn 0x1
+#define bRxAGCTogetherEn 0x2
+#define bRxAGCMin 0x4
+#define bRxHP_Ini 0x7
+#define bRxHP_TRLNA 0x70
+#define bRxHP_RSSI 0x700
+#define bRxHP_BBP1 0x7000
+#define bRxHP_BBP2 0x70000
+#define bRxHP_BBP3 0x700000
+/* The threshold for high power */
+#define bRSSI_H 0x7f0000
+/* The threshold for ant diversity */
+#define bRSSI_Gen 0x7f000000
+#define bRxSettle_TRSW 0x7
+#define bRxSettle_LNA 0x38
+#define bRxSettle_RSSI 0x1c0
+#define bRxSettle_BBP 0xe00
+#define bRxSettle_RxHP 0x7000
+#define bRxSettle_AntSW_RSSI 0x38000
+#define bRxSettle_AntSW 0xc0000
+#define bRxProcessTime_DAGC 0x300000
+#define bRxSettle_HSSI 0x400000
+#define bRxProcessTime_BBPPW 0x800000
+#define bRxAntennaPowerShift 0x3000000
+#define bRSSITableSelect 0xc000000
+#define bRxHP_Final 0x7000000
+#define bRxHTSettle_BBP 0x7
+#define bRxHTSettle_HSSI 0x8
+#define bRxHTSettle_RxHP 0x70
+#define bRxHTSettle_BBPPW 0x80
+#define bRxHTSettle_Idle 0x300
+#define bRxHTSettle_Reserved 0x1c00
+#define bRxHTRxHPEn 0x8000
+#define bRxHTAGCFreezeThres 0x30000
+#define bRxHTAGCTogetherEn 0x40000
+#define bRxHTAGCMin 0x80000
+#define bRxHTAGCEn 0x100000
+#define bRxHTDAGCEn 0x200000
+#define bRxHTRxHP_BBP 0x1c00000
+#define bRxHTRxHP_Final 0xe0000000
+#define bRxPWRatioTH 0x3
+#define bRxPWRatioEn 0x4
+#define bRxMFHold 0x3800
+#define bRxPD_Delay_TH1 0x38
+#define bRxPD_Delay_TH2 0x1c0
+#define bRxPD_DC_COUNT_MAX 0x600
+/*#define bRxMF_Hold 0x3800*/
+#define bRxPD_Delay_TH 0x8000
+#define bRxProcess_Delay 0xf0000
+#define bRxSearchrange_GI2_Early 0x700000
+#define bRxFrame_Guard_Counter_L 0x3800000
+#define bRxSGI_Guard_L 0xc000000
+#define bRxSGI_Search_L 0x30000000
+#define bRxSGI_TH 0xc0000000
+#define bDFSCnt0 0xff
+#define bDFSCnt1 0xff00
+#define bDFSFlag 0xf0000
+
+#define bMFWeightSum 0x300000
+#define bMinIdxTH 0x7f000000
+
+#define bDAFormat 0x40000
+
+#define bTxChEmuEnable 0x01000000
+
+#define bTRSWIsolation_A 0x7f
+#define bTRSWIsolation_B 0x7f00
+#define bTRSWIsolation_C 0x7f0000
+#define bTRSWIsolation_D 0x7f000000
+
+#define bExtLNAGain 0x7c00
+
+/* Page d */
+#define bSTBCEn 0x4
+#define bAntennaMapping 0x10
+#define bNss 0x20
+#define bCFOAntSumD 0x200
+#define bPHYCounterReset 0x8000000
+#define bCFOReportGet 0x4000000
+#define bOFDMContinueTx 0x10000000
+#define bOFDMSingleCarrier 0x20000000
+#define bOFDMSingleTone 0x40000000
+/*#define bRxPath1 0x01
+#define bRxPath2 0x02
+#define bRxPath3 0x04
+#define bRxPath4 0x08
+#define bTxPath1 0x10
+#define bTxPath2 0x20*/
+#define bHTDetect 0x100
+#define bCFOEn 0x10000
+#define bCFOValue 0xfff00000
+#define bSigTone_Re 0x3f
+#define bSigTone_Im 0x7f00
+#define bCounter_CCA 0xffff
+#define bCounter_ParityFail 0xffff0000
+#define bCounter_RateIllegal 0xffff
+#define bCounter_CRC8Fail 0xffff0000
+#define bCounter_MCSNoSupport 0xffff
+#define bCounter_FastSync 0xffff
+#define bShortCFO 0xfff
+/* total */
+#define bShortCFOTLength 12
+/* fraction */
+#define bShortCFOFLength 11
+#define bLongCFO 0x7ff
+#define bLongCFOTLength 11
+#define bLongCFOFLength 11
+#define bTailCFO 0x1fff
+#define bTailCFOTLength 13
+#define bTailCFOFLength 12
+
+#define bmax_en_pwdB 0xffff
+#define bCC_power_dB 0xffff0000
+#define bnoise_pwdB 0xffff
+#define bPowerMeasTLength 10
+#define bPowerMeasFLength 3
+#define bRx_HT_BW 0x1
+#define bRxSC 0x6
+#define bRx_HT 0x8
+
+#define bNB_intf_det_on 0x1
+#define bIntf_win_len_cfg 0x30
+#define bNB_Intf_TH_cfg 0x1c0
+
+#define bRFGain 0x3f
+#define bTableSel 0x40
+#define bTRSW 0x80
+
+#define bRxSNR_A 0xff
+#define bRxSNR_B 0xff00
+#define bRxSNR_C 0xff0000
+#define bRxSNR_D 0xff000000
+#define bSNREVMTLength 8
+#define bSNREVMFLength 1
+
+#define bCSI1st 0xff
+#define bCSI2nd 0xff00
+#define bRxEVM1st 0xff0000
+#define bRxEVM2nd 0xff000000
+
+#define bSIGEVM 0xff
+#define bPWDB 0xff00
+#define bSGIEN 0x10000
+
+#define bSFactorQAM1 0xf
+#define bSFactorQAM2 0xf0
+#define bSFactorQAM3 0xf00
+#define bSFactorQAM4 0xf000
+#define bSFactorQAM5 0xf0000
+#define bSFactorQAM6 0xf0000
+#define bSFactorQAM7 0xf00000
+#define bSFactorQAM8 0xf000000
+#define bSFactorQAM9 0xf0000000
+#define bCSIScheme 0x100000
+
+#define bNoiseLvlTopSet 0x3
+#define bChSmooth 0x4
+#define bChSmoothCfg1 0x38
+#define bChSmoothCfg2 0x1c0
+#define bChSmoothCfg3 0xe00
+#define bChSmoothCfg4 0x7000
+#define bMRCMode 0x800000
+#define bTHEVMCfg 0x7000000
+
+#define bLoopFitType 0x1
+#define bUpdCFO 0x40
+#define bUpdCFOOffData 0x80
+#define bAdvUpdCFO 0x100
+#define bAdvTimeCtrl 0x800
+#define bUpdClko 0x1000
+#define bFC 0x6000
+#define bTrackingMode 0x8000
+#define bPhCmpEnable 0x10000
+#define bUpdClkoLTF 0x20000
+#define bComChCFO 0x40000
+#define bCSIEstiMode 0x80000
+#define bAdvUpdEqz 0x100000
+#define bUChCfg 0x7000000
+#define bUpdEqz 0x8000000
+
+/* Page e */
+#define bTxAGCRate18_06 0x7f7f7f7f
+#define bTxAGCRate54_24 0x7f7f7f7f
+#define bTxAGCRateMCS32 0x7f
+#define bTxAGCRateCCK 0x7f00
+#define bTxAGCRateMCS3_MCS0 0x7f7f7f7f
+#define bTxAGCRateMCS7_MCS4 0x7f7f7f7f
+#define bTxAGCRateMCS11_MCS8 0x7f7f7f7f
+#define bTxAGCRateMCS15_MCS12 0x7f7f7f7f
+
+
+/* Rx Pseduo noise */
+#define bRxPesudoNoiseOn 0x20000000
+#define bRxPesudoNoise_A 0xff
+#define bRxPesudoNoise_B 0xff00
+#define bRxPesudoNoise_C 0xff0000
+#define bRxPesudoNoise_D 0xff000000
+#define bPesudoNoiseState_A 0xffff
+#define bPesudoNoiseState_B 0xffff0000
+#define bPesudoNoiseState_C 0xffff
+#define bPesudoNoiseState_D 0xffff0000
+
+/* RF Zebra 1 */
+#define bZebra1_HSSIEnable 0x8
+#define bZebra1_TRxControl 0xc00
+#define bZebra1_TRxGainSetting 0x07f
+#define bZebra1_RxCorner 0xc00
+#define bZebra1_TxChargePump 0x38
+#define bZebra1_RxChargePump 0x7
+#define bZebra1_ChannelNum 0xf80
+#define bZebra1_TxLPFBW 0x400
+#define bZebra1_RxLPFBW 0x600
+
+/* Zebra4 */
+#define bRTL8256RegModeCtrl1 0x100
+#define bRTL8256RegModeCtrl0 0x40
+#define bRTL8256_TxLPFBW 0x18
+#define bRTL8256_RxLPFBW 0x600
+
+//RTL8258
+#define bRTL8258_TxLPFBW 0xc
+#define bRTL8258_RxLPFBW 0xc00
+#define bRTL8258_RSSILPFBW 0xc0
+
+/* byte enable for sb_write */
+#define bByte0 0x1
+#define bByte1 0x2
+#define bByte2 0x4
+#define bByte3 0x8
+#define bWord0 0x3
+#define bWord1 0xc
+#define bDWord 0xf
+
+/* for PutRegsetting & GetRegSetting BitMask */
+#define bMaskByte0 0xff
+#define bMaskByte1 0xff00
+#define bMaskByte2 0xff0000
+#define bMaskByte3 0xff000000
+#define bMaskHWord 0xffff0000
+#define bMaskLWord 0x0000ffff
+#define bMaskDWord 0xffffffff
+
+/* for PutRFRegsetting & GetRFRegSetting BitMask */
+#define bMask12Bits 0xfff
+
+#define bEnable 0x1
+#define bDisable 0x0
+
+#define LeftAntenna 0x0
+#define RightAntenna 0x1
+
+/* 500 ms */
+#define tCheckTxStatus 500
+/* 100 ms */
+#define tUpdateRxCounter 100
+
+#define rateCCK 0
+#define rateOFDM 1
+#define rateHT 2
+
+/* define Register-End */
+#define bPMAC_End 0x1ff
+#define bFPGAPHY0_End 0x8ff
+#define bFPGAPHY1_End 0x9ff
+#define bCCKPHY0_End 0xaff
+#define bOFDMPHY0_End 0xcff
+#define bOFDMPHY1_End 0xdff
+
+#define bPMACControl 0x0
+#define bWMACControl 0x1
+#define bWNICControl 0x2
+
+#define PathA 0x0
+#define PathB 0x1
+#define PathC 0x2
+#define PathD 0x3
+
+#define rRTL8256RxMixerPole 0xb
+#define bZebraRxMixerPole 0x6
+#define rRTL8256TxBBOPBias 0x9
+#define bRTL8256TxBBOPBias 0x400
+#define rRTL8256TxBBBW 19
+#define bRTL8256TxBBBW 0x18
+
+
+#endif /* __INC_HAL8190PCIPHYREG_H */
diff --git a/drivers/staging/rtl8192e/rtl8192e/rtl_cam.c b/drivers/staging/rtl8192e/rtl8192e/rtl_cam.c
new file mode 100644
index 000000000..41b025e25
--- /dev/null
+++ b/drivers/staging/rtl8192e/rtl8192e/rtl_cam.c
@@ -0,0 +1,277 @@
+/******************************************************************************
+ * Copyright(c) 2008 - 2010 Realtek Corporation. All rights reserved.
+ *
+ * Based on the r8180 driver, which is:
+ * Copyright 2004-2005 Andrea Merello <andrea.merello@gmail.com>, et al.
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ * The full GNU General Public License is included in this distribution in the
+ * file called LICENSE.
+ *
+ * Contact Information:
+ * wlanfae <wlanfae@realtek.com>
+******************************************************************************/
+#include "rtl_core.h"
+#include "r8192E_phy.h"
+#include "r8192E_phyreg.h"
+#include "r8190P_rtl8256.h" /* RTL8225 Radio frontend */
+#include "r8192E_cmdpkt.h"
+
+void CamResetAllEntry(struct net_device *dev)
+{
+ u32 ulcommand = 0;
+
+ ulcommand |= BIT31|BIT30;
+ write_nic_dword(dev, RWCAM, ulcommand);
+}
+
+void write_cam(struct net_device *dev, u8 addr, u32 data)
+{
+ write_nic_dword(dev, WCAMI, data);
+ write_nic_dword(dev, RWCAM, BIT31|BIT16|(addr&0xff));
+}
+
+u32 read_cam(struct net_device *dev, u8 addr)
+{
+ write_nic_dword(dev, RWCAM, 0x80000000|(addr&0xff));
+ return read_nic_dword(dev, 0xa8);
+}
+
+void EnableHWSecurityConfig8192(struct net_device *dev)
+{
+ u8 SECR_value = 0x0;
+ struct r8192_priv *priv = (struct r8192_priv *)rtllib_priv(dev);
+ struct rtllib_device *ieee = priv->rtllib;
+
+ SECR_value = SCR_TxEncEnable | SCR_RxDecEnable;
+ if (((KEY_TYPE_WEP40 == ieee->pairwise_key_type) ||
+ (KEY_TYPE_WEP104 == ieee->pairwise_key_type)) &&
+ (priv->rtllib->auth_mode != 2)) {
+ SECR_value |= SCR_RxUseDK;
+ SECR_value |= SCR_TxUseDK;
+ } else if ((ieee->iw_mode == IW_MODE_ADHOC) &&
+ (ieee->pairwise_key_type & (KEY_TYPE_CCMP |
+ KEY_TYPE_TKIP))) {
+ SECR_value |= SCR_RxUseDK;
+ SECR_value |= SCR_TxUseDK;
+ }
+
+
+ ieee->hwsec_active = 1;
+ if ((ieee->pHTInfo->IOTAction&HT_IOT_ACT_PURE_N_MODE) || !hwwep) {
+ ieee->hwsec_active = 0;
+ SECR_value &= ~SCR_RxDecEnable;
+ }
+
+ RT_TRACE(COMP_SEC, "%s:, hwsec:%d, pairwise_key:%d, SECR_value:%x\n",
+ __func__, ieee->hwsec_active, ieee->pairwise_key_type,
+ SECR_value);
+ write_nic_byte(dev, SECR, SECR_value);
+}
+
+void set_swcam(struct net_device *dev, u8 EntryNo, u8 KeyIndex, u16 KeyType,
+ u8 *MacAddr, u8 DefaultKey, u32 *KeyContent, u8 is_mesh)
+{
+ struct r8192_priv *priv = rtllib_priv(dev);
+ struct rtllib_device *ieee = priv->rtllib;
+
+ RT_TRACE(COMP_DBG,
+ "===========>%s():EntryNo is %d,KeyIndex is %d,KeyType is %d,is_mesh is %d\n",
+ __func__, EntryNo, KeyIndex, KeyType, is_mesh);
+ if (!is_mesh) {
+ ieee->swcamtable[EntryNo].bused = true;
+ ieee->swcamtable[EntryNo].key_index = KeyIndex;
+ ieee->swcamtable[EntryNo].key_type = KeyType;
+ memcpy(ieee->swcamtable[EntryNo].macaddr, MacAddr, 6);
+ ieee->swcamtable[EntryNo].useDK = DefaultKey;
+ memcpy(ieee->swcamtable[EntryNo].key_buf, (u8 *)KeyContent, 16);
+ }
+}
+
+void setKey(struct net_device *dev, u8 EntryNo, u8 KeyIndex, u16 KeyType,
+ u8 *MacAddr, u8 DefaultKey, u32 *KeyContent)
+{
+ u32 TargetCommand = 0;
+ u32 TargetContent = 0;
+ u16 usConfig = 0;
+ u8 i;
+ struct r8192_priv *priv = (struct r8192_priv *)rtllib_priv(dev);
+ enum rt_rf_power_state rtState;
+
+ rtState = priv->rtllib->eRFPowerState;
+ if (priv->rtllib->PowerSaveControl.bInactivePs) {
+ if (rtState == eRfOff) {
+ if (priv->rtllib->RfOffReason > RF_CHANGE_BY_IPS) {
+ RT_TRACE(COMP_ERR, "%s(): RF is OFF.\n",
+ __func__);
+ return;
+ }
+ down(&priv->rtllib->ips_sem);
+ IPSLeave(dev);
+ up(&priv->rtllib->ips_sem);
+ }
+ }
+ priv->rtllib->is_set_key = true;
+ if (EntryNo >= TOTAL_CAM_ENTRY)
+ RT_TRACE(COMP_ERR, "cam entry exceeds in setKey()\n");
+
+ RT_TRACE(COMP_SEC,
+ "====>to setKey(), dev:%p, EntryNo:%d, KeyIndex:%d,KeyType:%d, MacAddr %pM\n",
+ dev, EntryNo, KeyIndex, KeyType, MacAddr);
+
+ if (DefaultKey)
+ usConfig |= BIT15 | (KeyType<<2);
+ else
+ usConfig |= BIT15 | (KeyType<<2) | KeyIndex;
+
+
+ for (i = 0; i < CAM_CONTENT_COUNT; i++) {
+ TargetCommand = i + CAM_CONTENT_COUNT * EntryNo;
+ TargetCommand |= BIT31|BIT16;
+
+ if (i == 0) {
+ TargetContent = (u32)(*(MacAddr+0)) << 16 |
+ (u32)(*(MacAddr+1)) << 24 |
+ (u32)usConfig;
+
+ write_nic_dword(dev, WCAMI, TargetContent);
+ write_nic_dword(dev, RWCAM, TargetCommand);
+ } else if (i == 1) {
+ TargetContent = (u32)(*(MacAddr+2)) |
+ (u32)(*(MacAddr+3)) << 8 |
+ (u32)(*(MacAddr+4)) << 16 |
+ (u32)(*(MacAddr+5)) << 24;
+ write_nic_dword(dev, WCAMI, TargetContent);
+ write_nic_dword(dev, RWCAM, TargetCommand);
+ } else {
+ if (KeyContent != NULL) {
+ write_nic_dword(dev, WCAMI,
+ (u32)(*(KeyContent+i-2)));
+ write_nic_dword(dev, RWCAM, TargetCommand);
+ udelay(100);
+ }
+ }
+ }
+ RT_TRACE(COMP_SEC, "=========>after set key, usconfig:%x\n", usConfig);
+}
+
+void CamRestoreAllEntry(struct net_device *dev)
+{
+ u8 EntryId = 0;
+ struct r8192_priv *priv = rtllib_priv(dev);
+ u8 *MacAddr = priv->rtllib->current_network.bssid;
+
+ static u8 CAM_CONST_ADDR[4][6] = {
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x01},
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x02},
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x03}
+ };
+ static u8 CAM_CONST_BROAD[] = {
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
+ };
+
+ RT_TRACE(COMP_SEC, "CamRestoreAllEntry:\n");
+
+
+ if ((priv->rtllib->pairwise_key_type == KEY_TYPE_WEP40) ||
+ (priv->rtllib->pairwise_key_type == KEY_TYPE_WEP104)) {
+
+ for (EntryId = 0; EntryId < 4; EntryId++) {
+ MacAddr = CAM_CONST_ADDR[EntryId];
+ if (priv->rtllib->swcamtable[EntryId].bused) {
+ setKey(dev, EntryId, EntryId,
+ priv->rtllib->pairwise_key_type, MacAddr,
+ 0, (u32 *)(&priv->rtllib->swcamtable
+ [EntryId].key_buf[0]));
+ }
+ }
+
+ } else if (priv->rtllib->pairwise_key_type == KEY_TYPE_TKIP) {
+ if (priv->rtllib->iw_mode == IW_MODE_ADHOC) {
+ setKey(dev, 4, 0, priv->rtllib->pairwise_key_type,
+ (u8 *)dev->dev_addr, 0,
+ (u32 *)(&priv->rtllib->swcamtable[4].key_buf[0]));
+ } else {
+ setKey(dev, 4, 0, priv->rtllib->pairwise_key_type,
+ MacAddr, 0,
+ (u32 *)(&priv->rtllib->swcamtable[4].key_buf[0]));
+ }
+
+ } else if (priv->rtllib->pairwise_key_type == KEY_TYPE_CCMP) {
+ if (priv->rtllib->iw_mode == IW_MODE_ADHOC) {
+ setKey(dev, 4, 0,
+ priv->rtllib->pairwise_key_type,
+ (u8 *)dev->dev_addr, 0,
+ (u32 *)(&priv->rtllib->swcamtable[4].
+ key_buf[0]));
+ } else {
+ setKey(dev, 4, 0,
+ priv->rtllib->pairwise_key_type, MacAddr,
+ 0, (u32 *)(&priv->rtllib->swcamtable[4].
+ key_buf[0]));
+ }
+ }
+
+ if (priv->rtllib->group_key_type == KEY_TYPE_TKIP) {
+ MacAddr = CAM_CONST_BROAD;
+ for (EntryId = 1; EntryId < 4; EntryId++) {
+ if (priv->rtllib->swcamtable[EntryId].bused) {
+ setKey(dev, EntryId, EntryId,
+ priv->rtllib->group_key_type,
+ MacAddr, 0,
+ (u32 *)(&priv->rtllib->swcamtable[EntryId].key_buf[0])
+ );
+ }
+ }
+ if (priv->rtllib->iw_mode == IW_MODE_ADHOC) {
+ if (priv->rtllib->swcamtable[0].bused) {
+ setKey(dev, 0, 0,
+ priv->rtllib->group_key_type,
+ CAM_CONST_ADDR[0], 0,
+ (u32 *)(&priv->rtllib->swcamtable[0].key_buf[0])
+ );
+ } else {
+ RT_TRACE(COMP_ERR,
+ "===>%s():ERR!! ADHOC TKIP ,but 0 entry is have no data\n",
+ __func__);
+ return;
+ }
+ }
+ } else if (priv->rtllib->group_key_type == KEY_TYPE_CCMP) {
+ MacAddr = CAM_CONST_BROAD;
+ for (EntryId = 1; EntryId < 4; EntryId++) {
+ if (priv->rtllib->swcamtable[EntryId].bused) {
+ setKey(dev, EntryId, EntryId,
+ priv->rtllib->group_key_type,
+ MacAddr, 0,
+ (u32 *)(&priv->rtllib->swcamtable[EntryId].key_buf[0]));
+ }
+ }
+
+ if (priv->rtllib->iw_mode == IW_MODE_ADHOC) {
+ if (priv->rtllib->swcamtable[0].bused) {
+ setKey(dev, 0, 0,
+ priv->rtllib->group_key_type,
+ CAM_CONST_ADDR[0], 0,
+ (u32 *)(&priv->rtllib->swcamtable[0].key_buf[0]));
+ } else {
+ RT_TRACE(COMP_ERR,
+ "===>%s():ERR!! ADHOC CCMP ,but 0 entry is have no data\n",
+ __func__);
+ return;
+ }
+ }
+ }
+}
diff --git a/drivers/staging/rtl8192e/rtl8192e/rtl_cam.h b/drivers/staging/rtl8192e/rtl8192e/rtl_cam.h
new file mode 100644
index 000000000..3c4c0e61c
--- /dev/null
+++ b/drivers/staging/rtl8192e/rtl8192e/rtl_cam.h
@@ -0,0 +1,44 @@
+/******************************************************************************
+ * Copyright(c) 2008 - 2010 Realtek Corporation. All rights reserved.
+ *
+ * Based on the r8180 driver, which is:
+ * Copyright 2004-2005 Andrea Merello <andrea.merello@gmail.com>, et al.
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ * The full GNU General Public License is included in this distribution in the
+ * file called LICENSE.
+ *
+ * Contact Information:
+ * wlanfae <wlanfae@realtek.com>
+******************************************************************************/
+#ifndef _RTL_CAM_H
+#define _RTL_CAM_H
+
+#include <linux/types.h>
+struct net_device;
+
+void CamResetAllEntry(struct net_device *dev);
+void EnableHWSecurityConfig8192(struct net_device *dev);
+void setKey(struct net_device *dev, u8 EntryNo, u8 KeyIndex, u16 KeyType,
+ u8 *MacAddr, u8 DefaultKey, u32 *KeyContent);
+void set_swcam(struct net_device *dev, u8 EntryNo, u8 KeyIndex, u16 KeyType,
+ u8 *MacAddr, u8 DefaultKey, u32 *KeyContent, u8 is_mesh);
+void CamPrintDbgReg(struct net_device *dev);
+
+u32 read_cam(struct net_device *dev, u8 addr);
+void write_cam(struct net_device *dev, u8 addr, u32 data);
+
+void CamRestoreAllEntry(struct net_device *dev);
+
+#endif
diff --git a/drivers/staging/rtl8192e/rtl8192e/rtl_core.c b/drivers/staging/rtl8192e/rtl8192e/rtl_core.c
new file mode 100644
index 000000000..23ec2d0b7
--- /dev/null
+++ b/drivers/staging/rtl8192e/rtl8192e/rtl_core.c
@@ -0,0 +1,3120 @@
+/******************************************************************************
+ * Copyright(c) 2008 - 2010 Realtek Corporation. All rights reserved.
+ *
+ * Based on the r8180 driver, which is:
+ * Copyright 2004-2005 Andrea Merello <andrea.merello@gmail.com>, et al.
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ * The full GNU General Public License is included in this distribution in the
+ * file called LICENSE.
+ *
+ * Contact Information:
+ * wlanfae <wlanfae@realtek.com>
+******************************************************************************/
+#undef RX_DONT_PASS_UL
+#undef DEBUG_EPROM
+#undef DEBUG_RX_VERBOSE
+#undef DUMMY_RX
+#undef DEBUG_ZERO_RX
+#undef DEBUG_RX_SKB
+#undef DEBUG_TX_FRAG
+#undef DEBUG_RX_FRAG
+#undef DEBUG_TX_FILLDESC
+#undef DEBUG_TX
+#undef DEBUG_IRQ
+#undef DEBUG_RX
+#undef DEBUG_RXALLOC
+#undef DEBUG_REGISTERS
+#undef DEBUG_RING
+#undef DEBUG_IRQ_TASKLET
+#undef DEBUG_TX_ALLOC
+#undef DEBUG_TX_DESC
+
+#include <linux/uaccess.h>
+#include <linux/pci.h>
+#include <linux/vmalloc.h>
+#include "rtl_core.h"
+#include "r8192E_phy.h"
+#include "r8192E_phyreg.h"
+#include "r8190P_rtl8256.h"
+#include "r8192E_cmdpkt.h"
+
+#include "rtl_wx.h"
+#include "rtl_dm.h"
+
+#include "rtl_pm.h"
+
+int hwwep = 1;
+static int channels = 0x3fff;
+static char *ifname = "wlan%d";
+
+
+static struct rtl819x_ops rtl819xp_ops = {
+ .nic_type = NIC_8192E,
+ .get_eeprom_size = rtl8192_get_eeprom_size,
+ .init_adapter_variable = rtl8192_InitializeVariables,
+ .initialize_adapter = rtl8192_adapter_start,
+ .link_change = rtl8192_link_change,
+ .tx_fill_descriptor = rtl8192_tx_fill_desc,
+ .tx_fill_cmd_descriptor = rtl8192_tx_fill_cmd_desc,
+ .rx_query_status_descriptor = rtl8192_rx_query_status_desc,
+ .rx_command_packet_handler = NULL,
+ .stop_adapter = rtl8192_halt_adapter,
+ .update_ratr_table = rtl8192_update_ratr_table,
+ .irq_enable = rtl8192_EnableInterrupt,
+ .irq_disable = rtl8192_DisableInterrupt,
+ .irq_clear = rtl8192_ClearInterrupt,
+ .rx_enable = rtl8192_enable_rx,
+ .tx_enable = rtl8192_enable_tx,
+ .interrupt_recognized = rtl8192_interrupt_recognized,
+ .TxCheckStuckHandler = rtl8192_HalTxCheckStuck,
+ .RxCheckStuckHandler = rtl8192_HalRxCheckStuck,
+};
+
+static struct pci_device_id rtl8192_pci_id_tbl[] = {
+ {RTL_PCI_DEVICE(0x10ec, 0x8192, rtl819xp_ops)},
+ {RTL_PCI_DEVICE(0x07aa, 0x0044, rtl819xp_ops)},
+ {RTL_PCI_DEVICE(0x07aa, 0x0047, rtl819xp_ops)},
+ {}
+};
+
+MODULE_DEVICE_TABLE(pci, rtl8192_pci_id_tbl);
+
+static int rtl8192_pci_probe(struct pci_dev *pdev,
+ const struct pci_device_id *id);
+static void rtl8192_pci_disconnect(struct pci_dev *pdev);
+static irqreturn_t rtl8192_interrupt(int irq, void *netdev);
+
+static struct pci_driver rtl8192_pci_driver = {
+ .name = DRV_NAME, /* Driver name */
+ .id_table = rtl8192_pci_id_tbl, /* PCI_ID table */
+ .probe = rtl8192_pci_probe, /* probe fn */
+ .remove = rtl8192_pci_disconnect, /* remove fn */
+ .suspend = rtl8192E_suspend, /* PM suspend fn */
+ .resume = rtl8192E_resume, /* PM resume fn */
+};
+
+/****************************************************************************
+ -----------------------------IO STUFF-------------------------
+*****************************************************************************/
+static bool PlatformIOCheckPageLegalAndGetRegMask(u32 u4bPage, u8 *pu1bPageMask)
+{
+ bool bReturn = false;
+
+ *pu1bPageMask = 0xfe;
+
+ switch (u4bPage) {
+ case 1: case 2: case 3: case 4:
+ case 8: case 9: case 10: case 12: case 13:
+ bReturn = true;
+ *pu1bPageMask = 0xf0;
+ break;
+
+ default:
+ bReturn = false;
+ break;
+ }
+
+ return bReturn;
+}
+
+void write_nic_io_byte(struct net_device *dev, int x, u8 y)
+{
+ u32 u4bPage = x >> 8;
+ u8 u1PageMask = 0;
+ bool bIsLegalPage = false;
+
+ if (u4bPage == 0) {
+ outb(y&0xff, dev->base_addr + x);
+
+ } else {
+ bIsLegalPage = PlatformIOCheckPageLegalAndGetRegMask(u4bPage,
+ &u1PageMask);
+ if (bIsLegalPage) {
+ u8 u1bPsr = read_nic_io_byte(dev, PSR);
+
+ write_nic_io_byte(dev, PSR, ((u1bPsr & u1PageMask) |
+ (u8)u4bPage));
+ write_nic_io_byte(dev, (x & 0xff), y);
+ write_nic_io_byte(dev, PSR, (u1bPsr & u1PageMask));
+ }
+ }
+}
+
+void write_nic_io_word(struct net_device *dev, int x, u16 y)
+{
+ u32 u4bPage = x >> 8;
+ u8 u1PageMask = 0;
+ bool bIsLegalPage = false;
+
+ if (u4bPage == 0) {
+ outw(y, dev->base_addr + x);
+ } else {
+ bIsLegalPage = PlatformIOCheckPageLegalAndGetRegMask(u4bPage,
+ &u1PageMask);
+ if (bIsLegalPage) {
+ u8 u1bPsr = read_nic_io_byte(dev, PSR);
+
+ write_nic_io_byte(dev, PSR, ((u1bPsr & u1PageMask) |
+ (u8)u4bPage));
+ write_nic_io_word(dev, (x & 0xff), y);
+ write_nic_io_byte(dev, PSR, (u1bPsr & u1PageMask));
+
+ }
+ }
+}
+
+void write_nic_io_dword(struct net_device *dev, int x, u32 y)
+{
+ u32 u4bPage = x >> 8;
+ u8 u1PageMask = 0;
+ bool bIsLegalPage = false;
+
+ if (u4bPage == 0) {
+ outl(y, dev->base_addr + x);
+ } else {
+ bIsLegalPage = PlatformIOCheckPageLegalAndGetRegMask(u4bPage,
+ &u1PageMask);
+ if (bIsLegalPage) {
+ u8 u1bPsr = read_nic_io_byte(dev, PSR);
+
+ write_nic_io_byte(dev, PSR, ((u1bPsr & u1PageMask) |
+ (u8)u4bPage));
+ write_nic_io_dword(dev, (x & 0xff), y);
+ write_nic_io_byte(dev, PSR, (u1bPsr & u1PageMask));
+ }
+ }
+}
+
+u8 read_nic_io_byte(struct net_device *dev, int x)
+{
+ u32 u4bPage = x >> 8;
+ u8 u1PageMask = 0;
+ bool bIsLegalPage = false;
+ u8 Data = 0;
+
+ if (u4bPage == 0)
+ return 0xff&inb(dev->base_addr + x);
+
+ bIsLegalPage = PlatformIOCheckPageLegalAndGetRegMask(u4bPage,
+ &u1PageMask);
+ if (bIsLegalPage) {
+ u8 u1bPsr = read_nic_io_byte(dev, PSR);
+
+ write_nic_io_byte(dev, PSR, ((u1bPsr & u1PageMask) |
+ (u8)u4bPage));
+ Data = read_nic_io_byte(dev, (x & 0xff));
+ write_nic_io_byte(dev, PSR, (u1bPsr & u1PageMask));
+ }
+
+ return Data;
+}
+
+u16 read_nic_io_word(struct net_device *dev, int x)
+{
+ u32 u4bPage = x >> 8;
+ u8 u1PageMask = 0;
+ bool bIsLegalPage = false;
+ u16 Data = 0;
+
+ if (u4bPage == 0)
+ return inw(dev->base_addr + x);
+ bIsLegalPage = PlatformIOCheckPageLegalAndGetRegMask(u4bPage,
+ &u1PageMask);
+ if (bIsLegalPage) {
+ u8 u1bPsr = read_nic_io_byte(dev, PSR);
+
+ write_nic_io_byte(dev, PSR, ((u1bPsr & u1PageMask) |
+ (u8)u4bPage));
+ Data = read_nic_io_word(dev, (x & 0xff));
+ write_nic_io_byte(dev, PSR, (u1bPsr & u1PageMask));
+ }
+
+ return Data;
+}
+
+u32 read_nic_io_dword(struct net_device *dev, int x)
+{
+ u32 u4bPage = x >> 8;
+ u8 u1PageMask = 0;
+ bool bIsLegalPage = false;
+ u32 Data = 0;
+
+ if (u4bPage == 0)
+ return inl(dev->base_addr + x);
+ bIsLegalPage = PlatformIOCheckPageLegalAndGetRegMask(u4bPage,
+ &u1PageMask);
+ if (bIsLegalPage) {
+ u8 u1bPsr = read_nic_io_byte(dev, PSR);
+
+ write_nic_io_byte(dev, PSR, ((u1bPsr & u1PageMask) |
+ (u8)u4bPage));
+ Data = read_nic_io_dword(dev, (x & 0xff));
+ write_nic_io_byte(dev, PSR, (u1bPsr & u1PageMask));
+ }
+
+ return Data;
+}
+
+u8 read_nic_byte(struct net_device *dev, int x)
+{
+ return 0xff & readb((u8 __iomem *)dev->mem_start + x);
+}
+
+u32 read_nic_dword(struct net_device *dev, int x)
+{
+ return readl((u8 __iomem *)dev->mem_start + x);
+}
+
+u16 read_nic_word(struct net_device *dev, int x)
+{
+ return readw((u8 __iomem *)dev->mem_start + x);
+}
+
+void write_nic_byte(struct net_device *dev, int x, u8 y)
+{
+ writeb(y, (u8 __iomem *)dev->mem_start + x);
+
+ udelay(20);
+}
+
+void write_nic_dword(struct net_device *dev, int x, u32 y)
+{
+ writel(y, (u8 __iomem *)dev->mem_start + x);
+
+ udelay(20);
+}
+
+void write_nic_word(struct net_device *dev, int x, u16 y)
+{
+ writew(y, (u8 __iomem *)dev->mem_start + x);
+
+ udelay(20);
+}
+
+/****************************************************************************
+ -----------------------------GENERAL FUNCTION-------------------------
+*****************************************************************************/
+bool MgntActSet_RF_State(struct net_device *dev,
+ enum rt_rf_power_state StateToSet,
+ RT_RF_CHANGE_SOURCE ChangeSource,
+ bool ProtectOrNot)
+{
+ struct r8192_priv *priv = rtllib_priv(dev);
+ struct rtllib_device *ieee = priv->rtllib;
+ bool bActionAllowed = false;
+ bool bConnectBySSID = false;
+ enum rt_rf_power_state rtState;
+ u16 RFWaitCounter = 0;
+ unsigned long flag;
+
+ RT_TRACE((COMP_PS | COMP_RF),
+ "===>MgntActSet_RF_State(): StateToSet(%d)\n", StateToSet);
+
+ ProtectOrNot = false;
+
+
+ if (!ProtectOrNot) {
+ while (true) {
+ spin_lock_irqsave(&priv->rf_ps_lock, flag);
+ if (priv->RFChangeInProgress) {
+ spin_unlock_irqrestore(&priv->rf_ps_lock, flag);
+ RT_TRACE((COMP_PS | COMP_RF),
+ "MgntActSet_RF_State(): RF Change in progress! Wait to set..StateToSet(%d).\n",
+ StateToSet);
+
+ while (priv->RFChangeInProgress) {
+ RFWaitCounter++;
+ RT_TRACE((COMP_PS | COMP_RF),
+ "MgntActSet_RF_State(): Wait 1 ms (%d times)...\n",
+ RFWaitCounter);
+ mdelay(1);
+
+ if (RFWaitCounter > 100) {
+ RT_TRACE(COMP_ERR,
+ "MgntActSet_RF_State(): Wait too logn to set RF\n");
+ return false;
+ }
+ }
+ } else {
+ priv->RFChangeInProgress = true;
+ spin_unlock_irqrestore(&priv->rf_ps_lock, flag);
+ break;
+ }
+ }
+ }
+
+ rtState = priv->rtllib->eRFPowerState;
+
+ switch (StateToSet) {
+ case eRfOn:
+ priv->rtllib->RfOffReason &= (~ChangeSource);
+
+ if ((ChangeSource == RF_CHANGE_BY_HW) && priv->bHwRadioOff)
+ priv->bHwRadioOff = false;
+
+ if (!priv->rtllib->RfOffReason) {
+ priv->rtllib->RfOffReason = 0;
+ bActionAllowed = true;
+
+
+ if (rtState == eRfOff &&
+ ChangeSource >= RF_CHANGE_BY_HW)
+ bConnectBySSID = true;
+ } else {
+ RT_TRACE((COMP_PS | COMP_RF),
+ "MgntActSet_RF_State - eRfon reject pMgntInfo->RfOffReason= 0x%x, ChangeSource=0x%X\n",
+ priv->rtllib->RfOffReason, ChangeSource);
+ }
+
+ break;
+
+ case eRfOff:
+
+ if ((priv->rtllib->iw_mode == IW_MODE_INFRA) ||
+ (priv->rtllib->iw_mode == IW_MODE_ADHOC)) {
+ if ((priv->rtllib->RfOffReason > RF_CHANGE_BY_IPS) ||
+ (ChangeSource > RF_CHANGE_BY_IPS)) {
+ if (ieee->state == RTLLIB_LINKED)
+ priv->blinked_ingpio = true;
+ else
+ priv->blinked_ingpio = false;
+ rtllib_MgntDisconnect(priv->rtllib,
+ disas_lv_ss);
+ }
+ }
+ if ((ChangeSource == RF_CHANGE_BY_HW) && !priv->bHwRadioOff)
+ priv->bHwRadioOff = true;
+ priv->rtllib->RfOffReason |= ChangeSource;
+ bActionAllowed = true;
+ break;
+
+ case eRfSleep:
+ priv->rtllib->RfOffReason |= ChangeSource;
+ bActionAllowed = true;
+ break;
+
+ default:
+ break;
+ }
+
+ if (bActionAllowed) {
+ RT_TRACE((COMP_PS | COMP_RF),
+ "MgntActSet_RF_State(): Action is allowed.... StateToSet(%d), RfOffReason(%#X)\n",
+ StateToSet, priv->rtllib->RfOffReason);
+ PHY_SetRFPowerState(dev, StateToSet);
+ if (StateToSet == eRfOn) {
+
+ if (bConnectBySSID && priv->blinked_ingpio) {
+ queue_delayed_work_rsl(ieee->wq,
+ &ieee->associate_procedure_wq, 0);
+ priv->blinked_ingpio = false;
+ }
+ }
+ } else {
+ RT_TRACE((COMP_PS | COMP_RF),
+ "MgntActSet_RF_State(): Action is rejected.... StateToSet(%d), ChangeSource(%#X), RfOffReason(%#X)\n",
+ StateToSet, ChangeSource, priv->rtllib->RfOffReason);
+ }
+
+ if (!ProtectOrNot) {
+ spin_lock_irqsave(&priv->rf_ps_lock, flag);
+ priv->RFChangeInProgress = false;
+ spin_unlock_irqrestore(&priv->rf_ps_lock, flag);
+ }
+
+ RT_TRACE((COMP_PS | COMP_RF), "<===MgntActSet_RF_State()\n");
+ return bActionAllowed;
+}
+
+
+static short rtl8192_get_nic_desc_num(struct net_device *dev, int prio)
+{
+ struct r8192_priv *priv = rtllib_priv(dev);
+ struct rtl8192_tx_ring *ring = &priv->tx_ring[prio];
+
+ /* For now, we reserved two free descriptor as a safety boundary
+ * between the tail and the head
+ */
+ if ((prio == MGNT_QUEUE) && (skb_queue_len(&ring->queue) > 10))
+ RT_TRACE(COMP_DBG,
+ "-----[%d]---------ring->idx=%d queue_len=%d---------\n",
+ prio, ring->idx, skb_queue_len(&ring->queue));
+ return skb_queue_len(&ring->queue);
+}
+
+static short rtl8192_check_nic_enough_desc(struct net_device *dev, int prio)
+{
+ struct r8192_priv *priv = rtllib_priv(dev);
+ struct rtl8192_tx_ring *ring = &priv->tx_ring[prio];
+
+ if (ring->entries - skb_queue_len(&ring->queue) >= 2)
+ return 1;
+ return 0;
+}
+
+void rtl8192_tx_timeout(struct net_device *dev)
+{
+ struct r8192_priv *priv = rtllib_priv(dev);
+
+ schedule_work(&priv->reset_wq);
+ netdev_info(dev, "TXTIMEOUT");
+}
+
+void rtl8192_irq_enable(struct net_device *dev)
+{
+ struct r8192_priv *priv = (struct r8192_priv *)rtllib_priv(dev);
+
+ priv->irq_enabled = 1;
+
+ priv->ops->irq_enable(dev);
+}
+
+void rtl8192_irq_disable(struct net_device *dev)
+{
+ struct r8192_priv *priv = (struct r8192_priv *)rtllib_priv(dev);
+
+ priv->ops->irq_disable(dev);
+
+ priv->irq_enabled = 0;
+}
+
+void rtl8192_set_chan(struct net_device *dev, short ch)
+{
+ struct r8192_priv *priv = (struct r8192_priv *)rtllib_priv(dev);
+
+ RT_TRACE(COMP_CH, "=====>%s()====ch:%d\n", __func__, ch);
+ if (priv->chan_forced)
+ return;
+
+ priv->chan = ch;
+
+ if (priv->rf_set_chan)
+ priv->rf_set_chan(dev, priv->chan);
+}
+
+void rtl8192_update_cap(struct net_device *dev, u16 cap)
+{
+ struct r8192_priv *priv = rtllib_priv(dev);
+ struct rtllib_network *net = &priv->rtllib->current_network;
+ bool ShortPreamble;
+
+ if (cap & WLAN_CAPABILITY_SHORT_PREAMBLE) {
+ if (priv->dot11CurrentPreambleMode != PREAMBLE_SHORT) {
+ ShortPreamble = true;
+ priv->dot11CurrentPreambleMode = PREAMBLE_SHORT;
+ RT_TRACE(COMP_DBG,
+ "%s(): WLAN_CAPABILITY_SHORT_PREAMBLE\n",
+ __func__);
+ priv->rtllib->SetHwRegHandler(dev, HW_VAR_ACK_PREAMBLE,
+ (unsigned char *)&ShortPreamble);
+ }
+ } else {
+ if (priv->dot11CurrentPreambleMode != PREAMBLE_LONG) {
+ ShortPreamble = false;
+ priv->dot11CurrentPreambleMode = PREAMBLE_LONG;
+ RT_TRACE(COMP_DBG,
+ "%s(): WLAN_CAPABILITY_LONG_PREAMBLE\n",
+ __func__);
+ priv->rtllib->SetHwRegHandler(dev, HW_VAR_ACK_PREAMBLE,
+ (unsigned char *)&ShortPreamble);
+ }
+ }
+
+ if (net->mode & (IEEE_G|IEEE_N_24G)) {
+ u8 slot_time_val;
+ u8 CurSlotTime = priv->slot_time;
+
+ if ((cap & WLAN_CAPABILITY_SHORT_SLOT_TIME) &&
+ (!priv->rtllib->pHTInfo->bCurrentRT2RTLongSlotTime)) {
+ if (CurSlotTime != SHORT_SLOT_TIME) {
+ slot_time_val = SHORT_SLOT_TIME;
+ priv->rtllib->SetHwRegHandler(dev,
+ HW_VAR_SLOT_TIME, &slot_time_val);
+ }
+ } else {
+ if (CurSlotTime != NON_SHORT_SLOT_TIME) {
+ slot_time_val = NON_SHORT_SLOT_TIME;
+ priv->rtllib->SetHwRegHandler(dev,
+ HW_VAR_SLOT_TIME, &slot_time_val);
+ }
+ }
+ }
+}
+
+static struct rtllib_qos_parameters def_qos_parameters = {
+ {cpu_to_le16(3), cpu_to_le16(3), cpu_to_le16(3), cpu_to_le16(3)},
+ {cpu_to_le16(7), cpu_to_le16(7), cpu_to_le16(7), cpu_to_le16(7)},
+ {2, 2, 2, 2},
+ {0, 0, 0, 0},
+ {0, 0, 0, 0}
+};
+
+static void rtl8192_update_beacon(void *data)
+{
+ struct r8192_priv *priv = container_of_work_rsl(data, struct r8192_priv,
+ update_beacon_wq.work);
+ struct net_device *dev = priv->rtllib->dev;
+ struct rtllib_device *ieee = priv->rtllib;
+ struct rtllib_network *net = &ieee->current_network;
+
+ if (ieee->pHTInfo->bCurrentHTSupport)
+ HT_update_self_and_peer_setting(ieee, net);
+ ieee->pHTInfo->bCurrentRT2RTLongSlotTime =
+ net->bssht.bdRT2RTLongSlotTime;
+ ieee->pHTInfo->RT2RT_HT_Mode = net->bssht.RT2RT_HT_Mode;
+ rtl8192_update_cap(dev, net->capability);
+}
+
+static void rtl8192_qos_activate(void *data)
+{
+ struct r8192_priv *priv = container_of_work_rsl(data, struct r8192_priv,
+ qos_activate);
+ struct net_device *dev = priv->rtllib->dev;
+ int i;
+
+ mutex_lock(&priv->mutex);
+ if (priv->rtllib->state != RTLLIB_LINKED)
+ goto success;
+ RT_TRACE(COMP_QOS,
+ "qos active process with associate response received\n");
+
+ for (i = 0; i < QOS_QUEUE_NUM; i++)
+ priv->rtllib->SetHwRegHandler(dev, HW_VAR_AC_PARAM, (u8 *)(&i));
+
+
+success:
+ mutex_unlock(&priv->mutex);
+}
+
+static int rtl8192_qos_handle_probe_response(struct r8192_priv *priv,
+ int active_network,
+ struct rtllib_network *network)
+{
+ int ret = 0;
+ u32 size = sizeof(struct rtllib_qos_parameters);
+
+ if (priv->rtllib->state != RTLLIB_LINKED)
+ return ret;
+
+ if (priv->rtllib->iw_mode != IW_MODE_INFRA)
+ return ret;
+
+ if (network->flags & NETWORK_HAS_QOS_MASK) {
+ if (active_network &&
+ (network->flags & NETWORK_HAS_QOS_PARAMETERS))
+ network->qos_data.active = network->qos_data.supported;
+
+ if ((network->qos_data.active == 1) && (active_network == 1) &&
+ (network->flags & NETWORK_HAS_QOS_PARAMETERS) &&
+ (network->qos_data.old_param_count !=
+ network->qos_data.param_count)) {
+ network->qos_data.old_param_count =
+ network->qos_data.param_count;
+ priv->rtllib->wmm_acm = network->qos_data.wmm_acm;
+ queue_work_rsl(priv->priv_wq, &priv->qos_activate);
+ RT_TRACE(COMP_QOS,
+ "QoS parameters change call qos_activate\n");
+ }
+ } else {
+ memcpy(&priv->rtllib->current_network.qos_data.parameters,
+ &def_qos_parameters, size);
+
+ if ((network->qos_data.active == 1) && (active_network == 1)) {
+ queue_work_rsl(priv->priv_wq, &priv->qos_activate);
+ RT_TRACE(COMP_QOS,
+ "QoS was disabled call qos_activate\n");
+ }
+ network->qos_data.active = 0;
+ network->qos_data.supported = 0;
+ }
+
+ return 0;
+}
+
+static int rtl8192_handle_beacon(struct net_device *dev,
+ struct rtllib_beacon *beacon,
+ struct rtllib_network *network)
+{
+ struct r8192_priv *priv = rtllib_priv(dev);
+
+ rtl8192_qos_handle_probe_response(priv, 1, network);
+
+ queue_delayed_work_rsl(priv->priv_wq, &priv->update_beacon_wq, 0);
+ return 0;
+
+}
+
+static int rtl8192_qos_association_resp(struct r8192_priv *priv,
+ struct rtllib_network *network)
+{
+ unsigned long flags;
+ u32 size = sizeof(struct rtllib_qos_parameters);
+ int set_qos_param = 0;
+
+ if ((priv == NULL) || (network == NULL))
+ return 0;
+
+ if (priv->rtllib->state != RTLLIB_LINKED)
+ return 0;
+
+ if (priv->rtllib->iw_mode != IW_MODE_INFRA)
+ return 0;
+
+ spin_lock_irqsave(&priv->rtllib->lock, flags);
+ if (network->flags & NETWORK_HAS_QOS_PARAMETERS) {
+ memcpy(&priv->rtllib->current_network.qos_data.parameters,
+ &network->qos_data.parameters,
+ sizeof(struct rtllib_qos_parameters));
+ priv->rtllib->current_network.qos_data.active = 1;
+ priv->rtllib->wmm_acm = network->qos_data.wmm_acm;
+ set_qos_param = 1;
+ priv->rtllib->current_network.qos_data.old_param_count =
+ priv->rtllib->current_network.qos_data.param_count;
+ priv->rtllib->current_network.qos_data.param_count =
+ network->qos_data.param_count;
+ } else {
+ memcpy(&priv->rtllib->current_network.qos_data.parameters,
+ &def_qos_parameters, size);
+ priv->rtllib->current_network.qos_data.active = 0;
+ priv->rtllib->current_network.qos_data.supported = 0;
+ set_qos_param = 1;
+ }
+
+ spin_unlock_irqrestore(&priv->rtllib->lock, flags);
+
+ RT_TRACE(COMP_QOS, "%s: network->flags = %d,%d\n", __func__,
+ network->flags, priv->rtllib->current_network.qos_data.active);
+ if (set_qos_param == 1) {
+ dm_init_edca_turbo(priv->rtllib->dev);
+ queue_work_rsl(priv->priv_wq, &priv->qos_activate);
+ }
+ return 0;
+}
+
+static int rtl8192_handle_assoc_response(struct net_device *dev,
+ struct rtllib_assoc_response_frame *resp,
+ struct rtllib_network *network)
+{
+ struct r8192_priv *priv = rtllib_priv(dev);
+
+ rtl8192_qos_association_resp(priv, network);
+ return 0;
+}
+
+static void rtl8192_prepare_beacon(struct r8192_priv *priv)
+{
+ struct net_device *dev = priv->rtllib->dev;
+ struct sk_buff *pskb = NULL, *pnewskb = NULL;
+ struct cb_desc *tcb_desc = NULL;
+ struct rtl8192_tx_ring *ring = NULL;
+ struct tx_desc *pdesc = NULL;
+
+ ring = &priv->tx_ring[BEACON_QUEUE];
+ pskb = __skb_dequeue(&ring->queue);
+ kfree_skb(pskb);
+
+ pnewskb = rtllib_get_beacon(priv->rtllib);
+ if (!pnewskb)
+ return;
+
+ tcb_desc = (struct cb_desc *)(pnewskb->cb + 8);
+ tcb_desc->queue_index = BEACON_QUEUE;
+ tcb_desc->data_rate = 2;
+ tcb_desc->RATRIndex = 7;
+ tcb_desc->bTxDisableRateFallBack = 1;
+ tcb_desc->bTxUseDriverAssingedRate = 1;
+ skb_push(pnewskb, priv->rtllib->tx_headroom);
+
+ pdesc = &ring->desc[0];
+ priv->ops->tx_fill_descriptor(dev, pdesc, tcb_desc, pnewskb);
+ __skb_queue_tail(&ring->queue, pnewskb);
+ pdesc->OWN = 1;
+}
+
+static void rtl8192_stop_beacon(struct net_device *dev)
+{
+}
+
+void rtl8192_config_rate(struct net_device *dev, u16 *rate_config)
+{
+ struct r8192_priv *priv = rtllib_priv(dev);
+ struct rtllib_network *net;
+ u8 i = 0, basic_rate = 0;
+
+ net = &priv->rtllib->current_network;
+
+ for (i = 0; i < net->rates_len; i++) {
+ basic_rate = net->rates[i] & 0x7f;
+ switch (basic_rate) {
+ case MGN_1M:
+ *rate_config |= RRSR_1M;
+ break;
+ case MGN_2M:
+ *rate_config |= RRSR_2M;
+ break;
+ case MGN_5_5M:
+ *rate_config |= RRSR_5_5M;
+ break;
+ case MGN_11M:
+ *rate_config |= RRSR_11M;
+ break;
+ case MGN_6M:
+ *rate_config |= RRSR_6M;
+ break;
+ case MGN_9M:
+ *rate_config |= RRSR_9M;
+ break;
+ case MGN_12M:
+ *rate_config |= RRSR_12M;
+ break;
+ case MGN_18M:
+ *rate_config |= RRSR_18M;
+ break;
+ case MGN_24M:
+ *rate_config |= RRSR_24M;
+ break;
+ case MGN_36M:
+ *rate_config |= RRSR_36M;
+ break;
+ case MGN_48M:
+ *rate_config |= RRSR_48M;
+ break;
+ case MGN_54M:
+ *rate_config |= RRSR_54M;
+ break;
+ }
+ }
+
+ for (i = 0; i < net->rates_ex_len; i++) {
+ basic_rate = net->rates_ex[i] & 0x7f;
+ switch (basic_rate) {
+ case MGN_1M:
+ *rate_config |= RRSR_1M;
+ break;
+ case MGN_2M:
+ *rate_config |= RRSR_2M;
+ break;
+ case MGN_5_5M:
+ *rate_config |= RRSR_5_5M;
+ break;
+ case MGN_11M:
+ *rate_config |= RRSR_11M;
+ break;
+ case MGN_6M:
+ *rate_config |= RRSR_6M;
+ break;
+ case MGN_9M:
+ *rate_config |= RRSR_9M;
+ break;
+ case MGN_12M:
+ *rate_config |= RRSR_12M;
+ break;
+ case MGN_18M:
+ *rate_config |= RRSR_18M;
+ break;
+ case MGN_24M:
+ *rate_config |= RRSR_24M;
+ break;
+ case MGN_36M:
+ *rate_config |= RRSR_36M;
+ break;
+ case MGN_48M:
+ *rate_config |= RRSR_48M;
+ break;
+ case MGN_54M:
+ *rate_config |= RRSR_54M;
+ break;
+ }
+ }
+}
+
+static void rtl8192_refresh_supportrate(struct r8192_priv *priv)
+{
+ struct rtllib_device *ieee = priv->rtllib;
+
+ if (ieee->mode == WIRELESS_MODE_N_24G ||
+ ieee->mode == WIRELESS_MODE_N_5G) {
+ memcpy(ieee->Regdot11HTOperationalRateSet,
+ ieee->RegHTSuppRateSet, 16);
+ memcpy(ieee->Regdot11TxHTOperationalRateSet,
+ ieee->RegHTSuppRateSet, 16);
+
+ } else {
+ memset(ieee->Regdot11HTOperationalRateSet, 0, 16);
+ }
+}
+
+static u8 rtl8192_getSupportedWireleeMode(struct net_device *dev)
+{
+ struct r8192_priv *priv = rtllib_priv(dev);
+ u8 ret = 0;
+
+ switch (priv->rf_chip) {
+ case RF_8225:
+ case RF_8256:
+ case RF_6052:
+ case RF_PSEUDO_11N:
+ ret = (WIRELESS_MODE_N_24G|WIRELESS_MODE_G | WIRELESS_MODE_B);
+ break;
+ case RF_8258:
+ ret = (WIRELESS_MODE_A | WIRELESS_MODE_N_5G);
+ break;
+ default:
+ ret = WIRELESS_MODE_B;
+ break;
+ }
+ return ret;
+}
+
+void rtl8192_SetWirelessMode(struct net_device *dev, u8 wireless_mode)
+{
+ struct r8192_priv *priv = rtllib_priv(dev);
+ u8 bSupportMode = rtl8192_getSupportedWireleeMode(dev);
+
+ if ((wireless_mode == WIRELESS_MODE_AUTO) ||
+ ((wireless_mode & bSupportMode) == 0)) {
+ if (bSupportMode & WIRELESS_MODE_N_24G) {
+ wireless_mode = WIRELESS_MODE_N_24G;
+ } else if (bSupportMode & WIRELESS_MODE_N_5G) {
+ wireless_mode = WIRELESS_MODE_N_5G;
+ } else if ((bSupportMode & WIRELESS_MODE_A)) {
+ wireless_mode = WIRELESS_MODE_A;
+ } else if ((bSupportMode & WIRELESS_MODE_G)) {
+ wireless_mode = WIRELESS_MODE_G;
+ } else if ((bSupportMode & WIRELESS_MODE_B)) {
+ wireless_mode = WIRELESS_MODE_B;
+ } else {
+ RT_TRACE(COMP_ERR,
+ "%s(), No valid wireless mode supported (%x)!!!\n",
+ __func__, bSupportMode);
+ wireless_mode = WIRELESS_MODE_B;
+ }
+ }
+
+ if ((wireless_mode & (WIRELESS_MODE_B | WIRELESS_MODE_G)) ==
+ (WIRELESS_MODE_G | WIRELESS_MODE_B))
+ wireless_mode = WIRELESS_MODE_G;
+
+ priv->rtllib->mode = wireless_mode;
+
+ ActUpdateChannelAccessSetting(dev, wireless_mode,
+ &priv->ChannelAccessSetting);
+
+ if ((wireless_mode == WIRELESS_MODE_N_24G) ||
+ (wireless_mode == WIRELESS_MODE_N_5G)) {
+ priv->rtllib->pHTInfo->bEnableHT = 1;
+ RT_TRACE(COMP_DBG, "%s(), wireless_mode:%x, bEnableHT = 1\n",
+ __func__, wireless_mode);
+ } else {
+ priv->rtllib->pHTInfo->bEnableHT = 0;
+ RT_TRACE(COMP_DBG, "%s(), wireless_mode:%x, bEnableHT = 0\n",
+ __func__, wireless_mode);
+ }
+
+ RT_TRACE(COMP_INIT, "Current Wireless Mode is %x\n", wireless_mode);
+ rtl8192_refresh_supportrate(priv);
+}
+
+static int _rtl8192_sta_up(struct net_device *dev, bool is_silent_reset)
+{
+ struct r8192_priv *priv = rtllib_priv(dev);
+ struct rt_pwr_save_ctrl *pPSC = (struct rt_pwr_save_ctrl *)
+ (&(priv->rtllib->PowerSaveControl));
+ bool init_status = true;
+
+ priv->bDriverIsGoingToUnload = false;
+ priv->bdisable_nic = false;
+
+ priv->up = 1;
+ priv->rtllib->ieee_up = 1;
+
+ priv->up_first_time = 0;
+ RT_TRACE(COMP_INIT, "Bringing up iface");
+ priv->bfirst_init = true;
+ init_status = priv->ops->initialize_adapter(dev);
+ if (!init_status) {
+ RT_TRACE(COMP_ERR, "ERR!!! %s(): initialization is failed!\n",
+ __func__);
+ priv->bfirst_init = false;
+ return -1;
+ }
+
+ RT_TRACE(COMP_INIT, "start adapter finished\n");
+ RT_CLEAR_PS_LEVEL(pPSC, RT_RF_OFF_LEVL_HALT_NIC);
+ priv->bfirst_init = false;
+
+ if (priv->polling_timer_on == 0)
+ check_rfctrl_gpio_timer((unsigned long)dev);
+
+ if (priv->rtllib->state != RTLLIB_LINKED)
+ rtllib_softmac_start_protocol(priv->rtllib, 0);
+ rtllib_reset_queue(priv->rtllib);
+ watch_dog_timer_callback((unsigned long) dev);
+
+ if (!netif_queue_stopped(dev))
+ netif_start_queue(dev);
+ else
+ netif_wake_queue(dev);
+
+ return 0;
+}
+
+static int rtl8192_sta_down(struct net_device *dev, bool shutdownrf)
+{
+ struct r8192_priv *priv = rtllib_priv(dev);
+ unsigned long flags = 0;
+ u8 RFInProgressTimeOut = 0;
+
+ if (priv->up == 0)
+ return -1;
+
+ if (priv->rtllib->rtllib_ips_leave != NULL)
+ priv->rtllib->rtllib_ips_leave(dev);
+
+ if (priv->rtllib->state == RTLLIB_LINKED)
+ LeisurePSLeave(dev);
+
+ priv->bDriverIsGoingToUnload = true;
+ priv->up = 0;
+ priv->rtllib->ieee_up = 0;
+ priv->bfirst_after_down = true;
+ RT_TRACE(COMP_DOWN, "==========>%s()\n", __func__);
+ if (!netif_queue_stopped(dev))
+ netif_stop_queue(dev);
+
+ priv->rtllib->wpa_ie_len = 0;
+ kfree(priv->rtllib->wpa_ie);
+ priv->rtllib->wpa_ie = NULL;
+ CamResetAllEntry(dev);
+ memset(priv->rtllib->swcamtable, 0, sizeof(struct sw_cam_table) * 32);
+ rtl8192_irq_disable(dev);
+
+ del_timer_sync(&priv->watch_dog_timer);
+ rtl8192_cancel_deferred_work(priv);
+ cancel_delayed_work(&priv->rtllib->hw_wakeup_wq);
+
+ rtllib_softmac_stop_protocol(priv->rtllib, 0, true);
+ spin_lock_irqsave(&priv->rf_ps_lock, flags);
+ while (priv->RFChangeInProgress) {
+ spin_unlock_irqrestore(&priv->rf_ps_lock, flags);
+ if (RFInProgressTimeOut > 100) {
+ spin_lock_irqsave(&priv->rf_ps_lock, flags);
+ break;
+ }
+ RT_TRACE(COMP_DBG,
+ "===>%s():RF is in progress, need to wait until rf change is done.\n",
+ __func__);
+ mdelay(1);
+ RFInProgressTimeOut++;
+ spin_lock_irqsave(&priv->rf_ps_lock, flags);
+ }
+ priv->RFChangeInProgress = true;
+ spin_unlock_irqrestore(&priv->rf_ps_lock, flags);
+ priv->ops->stop_adapter(dev, false);
+ spin_lock_irqsave(&priv->rf_ps_lock, flags);
+ priv->RFChangeInProgress = false;
+ spin_unlock_irqrestore(&priv->rf_ps_lock, flags);
+ udelay(100);
+ memset(&priv->rtllib->current_network, 0,
+ offsetof(struct rtllib_network, list));
+ RT_TRACE(COMP_DOWN, "<==========%s()\n", __func__);
+
+ return 0;
+}
+
+static void rtl8192_init_priv_handler(struct net_device *dev)
+{
+ struct r8192_priv *priv = rtllib_priv(dev);
+
+ priv->rtllib->softmac_hard_start_xmit = rtl8192_hard_start_xmit;
+ priv->rtllib->set_chan = rtl8192_set_chan;
+ priv->rtllib->link_change = priv->ops->link_change;
+ priv->rtllib->softmac_data_hard_start_xmit = rtl8192_hard_data_xmit;
+ priv->rtllib->data_hard_stop = rtl8192_data_hard_stop;
+ priv->rtllib->data_hard_resume = rtl8192_data_hard_resume;
+ priv->rtllib->check_nic_enough_desc = rtl8192_check_nic_enough_desc;
+ priv->rtllib->get_nic_desc_num = rtl8192_get_nic_desc_num;
+ priv->rtllib->handle_assoc_response = rtl8192_handle_assoc_response;
+ priv->rtllib->handle_beacon = rtl8192_handle_beacon;
+ priv->rtllib->SetWirelessMode = rtl8192_SetWirelessMode;
+ priv->rtllib->LeisurePSLeave = LeisurePSLeave;
+ priv->rtllib->SetBWModeHandler = rtl8192_SetBWMode;
+ priv->rf_set_chan = rtl8192_phy_SwChnl;
+
+ priv->rtllib->start_send_beacons = rtl8192e_start_beacon;
+ priv->rtllib->stop_send_beacons = rtl8192_stop_beacon;
+
+ priv->rtllib->sta_wake_up = rtl8192_hw_wakeup;
+ priv->rtllib->enter_sleep_state = rtl8192_hw_to_sleep;
+ priv->rtllib->ps_is_queue_empty = rtl8192_is_tx_queue_empty;
+
+ priv->rtllib->GetNmodeSupportBySecCfg = rtl8192_GetNmodeSupportBySecCfg;
+ priv->rtllib->GetHalfNmodeSupportByAPsHandler =
+ rtl8192_GetHalfNmodeSupportByAPs;
+
+ priv->rtllib->SetHwRegHandler = rtl8192e_SetHwReg;
+ priv->rtllib->AllowAllDestAddrHandler = rtl8192_AllowAllDestAddr;
+ priv->rtllib->SetFwCmdHandler = NULL;
+ priv->rtllib->InitialGainHandler = InitialGain819xPci;
+ priv->rtllib->rtllib_ips_leave_wq = rtllib_ips_leave_wq;
+ priv->rtllib->rtllib_ips_leave = rtllib_ips_leave;
+
+ priv->rtllib->LedControlHandler = NULL;
+ priv->rtllib->UpdateBeaconInterruptHandler = NULL;
+
+ priv->rtllib->ScanOperationBackupHandler = PHY_ScanOperationBackup8192;
+
+ priv->rtllib->rtllib_rfkill_poll = NULL;
+}
+
+static void rtl8192_init_priv_constant(struct net_device *dev)
+{
+ struct r8192_priv *priv = rtllib_priv(dev);
+ struct rt_pwr_save_ctrl *pPSC = (struct rt_pwr_save_ctrl *)
+ &(priv->rtllib->PowerSaveControl);
+
+ pPSC->RegMaxLPSAwakeIntvl = 5;
+
+ priv->RegPciASPM = 2;
+
+ priv->RegDevicePciASPMSetting = 0x03;
+
+ priv->RegHostPciASPMSetting = 0x02;
+
+ priv->RegHwSwRfOffD3 = 2;
+
+ priv->RegSupportPciASPM = 2;
+}
+
+
+static void rtl8192_init_priv_variable(struct net_device *dev)
+{
+ struct r8192_priv *priv = rtllib_priv(dev);
+ u8 i;
+
+ priv->AcmMethod = eAcmWay2_SW;
+ priv->dot11CurrentPreambleMode = PREAMBLE_AUTO;
+ priv->rtllib->hwscan_sem_up = 1;
+ priv->rtllib->status = 0;
+ priv->H2CTxCmdSeq = 0;
+ priv->bDisableFrameBursting = false;
+ priv->bDMInitialGainEnable = true;
+ priv->polling_timer_on = 0;
+ priv->up_first_time = 1;
+ priv->blinked_ingpio = false;
+ priv->bDriverIsGoingToUnload = false;
+ priv->being_init_adapter = false;
+ priv->initialized_at_probe = false;
+ priv->sw_radio_on = true;
+ priv->bdisable_nic = false;
+ priv->bfirst_init = false;
+ priv->txringcount = 64;
+ priv->rxbuffersize = 9100;
+ priv->rxringcount = MAX_RX_COUNT;
+ priv->irq_enabled = 0;
+ priv->chan = 1;
+ priv->RegWirelessMode = WIRELESS_MODE_AUTO;
+ priv->RegChannelPlan = 0xf;
+ priv->nrxAMPDU_size = 0;
+ priv->nrxAMPDU_aggr_num = 0;
+ priv->last_rxdesc_tsf_high = 0;
+ priv->last_rxdesc_tsf_low = 0;
+ priv->rtllib->mode = WIRELESS_MODE_AUTO;
+ priv->rtllib->iw_mode = IW_MODE_INFRA;
+ priv->rtllib->bNetPromiscuousMode = false;
+ priv->rtllib->IntelPromiscuousModeInfo.bPromiscuousOn = false;
+ priv->rtllib->IntelPromiscuousModeInfo.bFilterSourceStationFrame =
+ false;
+ priv->rtllib->ieee_up = 0;
+ priv->retry_rts = DEFAULT_RETRY_RTS;
+ priv->retry_data = DEFAULT_RETRY_DATA;
+ priv->rtllib->rts = DEFAULT_RTS_THRESHOLD;
+ priv->rtllib->rate = 110;
+ priv->rtllib->short_slot = 1;
+ priv->promisc = (dev->flags & IFF_PROMISC) ? 1 : 0;
+ priv->bcck_in_ch14 = false;
+ priv->bfsync_processing = false;
+ priv->CCKPresentAttentuation = 0;
+ priv->rfa_txpowertrackingindex = 0;
+ priv->rfc_txpowertrackingindex = 0;
+ priv->CckPwEnl = 6;
+ priv->ScanDelay = 50;
+ priv->ResetProgress = RESET_TYPE_NORESET;
+ priv->bForcedSilentReset = false;
+ priv->bDisableNormalResetCheck = false;
+ priv->force_reset = false;
+ memset(priv->rtllib->swcamtable, 0, sizeof(struct sw_cam_table) * 32);
+
+ memset(&priv->InterruptLog, 0, sizeof(struct log_int_8190));
+ priv->RxCounter = 0;
+ priv->rtllib->wx_set_enc = 0;
+ priv->bHwRadioOff = false;
+ priv->RegRfOff = false;
+ priv->isRFOff = false;
+ priv->bInPowerSaveMode = false;
+ priv->rtllib->RfOffReason = 0;
+ priv->RFChangeInProgress = false;
+ priv->bHwRfOffAction = 0;
+ priv->SetRFPowerStateInProgress = false;
+ priv->rtllib->PowerSaveControl.bInactivePs = true;
+ priv->rtllib->PowerSaveControl.bIPSModeBackup = false;
+ priv->rtllib->PowerSaveControl.bLeisurePs = true;
+ priv->rtllib->PowerSaveControl.bFwCtrlLPS = false;
+ priv->rtllib->LPSDelayCnt = 0;
+ priv->rtllib->sta_sleep = LPS_IS_WAKE;
+ priv->rtllib->eRFPowerState = eRfOn;
+
+ priv->txpower_checkcnt = 0;
+ priv->thermal_readback_index = 0;
+ priv->txpower_tracking_callback_cnt = 0;
+ priv->ccktxpower_adjustcnt_ch14 = 0;
+ priv->ccktxpower_adjustcnt_not_ch14 = 0;
+
+ priv->rtllib->current_network.beacon_interval = DEFAULT_BEACONINTERVAL;
+ priv->rtllib->iw_mode = IW_MODE_INFRA;
+ priv->rtllib->active_scan = 1;
+ priv->rtllib->be_scan_inprogress = false;
+ priv->rtllib->modulation = RTLLIB_CCK_MODULATION |
+ RTLLIB_OFDM_MODULATION;
+ priv->rtllib->host_encrypt = 1;
+ priv->rtllib->host_decrypt = 1;
+
+ priv->rtllib->dot11PowerSaveMode = eActive;
+ priv->rtllib->fts = DEFAULT_FRAG_THRESHOLD;
+ priv->rtllib->MaxMssDensity = 0;
+ priv->rtllib->MinSpaceCfg = 0;
+
+ priv->card_type = PCI;
+
+ priv->AcmControl = 0;
+ priv->pFirmware = vzalloc(sizeof(struct rt_firmware));
+ if (!priv->pFirmware)
+ netdev_err(dev,
+ "rtl8192e: Unable to allocate space for firmware\n");
+
+ skb_queue_head_init(&priv->rx_queue);
+ skb_queue_head_init(&priv->skb_queue);
+
+ for (i = 0; i < MAX_QUEUE_SIZE; i++)
+ skb_queue_head_init(&priv->rtllib->skb_waitQ[i]);
+ for (i = 0; i < MAX_QUEUE_SIZE; i++)
+ skb_queue_head_init(&priv->rtllib->skb_aggQ[i]);
+}
+
+static void rtl8192_init_priv_lock(struct r8192_priv *priv)
+{
+ spin_lock_init(&priv->fw_scan_lock);
+ spin_lock_init(&priv->tx_lock);
+ spin_lock_init(&priv->irq_lock);
+ spin_lock_init(&priv->irq_th_lock);
+ spin_lock_init(&priv->rf_ps_lock);
+ spin_lock_init(&priv->ps_lock);
+ spin_lock_init(&priv->rf_lock);
+ spin_lock_init(&priv->rt_h2c_lock);
+ sema_init(&priv->wx_sem, 1);
+ sema_init(&priv->rf_sem, 1);
+ mutex_init(&priv->mutex);
+}
+
+static void rtl8192_init_priv_task(struct net_device *dev)
+{
+ struct r8192_priv *priv = rtllib_priv(dev);
+
+ priv->priv_wq = create_workqueue(DRV_NAME);
+ INIT_WORK_RSL(&priv->reset_wq, (void *)rtl8192_restart, dev);
+ INIT_WORK_RSL(&priv->rtllib->ips_leave_wq, (void *)IPSLeave_wq, dev);
+ INIT_DELAYED_WORK_RSL(&priv->watch_dog_wq,
+ (void *)rtl819x_watchdog_wqcallback, dev);
+ INIT_DELAYED_WORK_RSL(&priv->txpower_tracking_wq,
+ (void *)dm_txpower_trackingcallback, dev);
+ INIT_DELAYED_WORK_RSL(&priv->rfpath_check_wq,
+ (void *)dm_rf_pathcheck_workitemcallback, dev);
+ INIT_DELAYED_WORK_RSL(&priv->update_beacon_wq,
+ (void *)rtl8192_update_beacon, dev);
+ INIT_WORK_RSL(&priv->qos_activate, (void *)rtl8192_qos_activate, dev);
+ INIT_DELAYED_WORK_RSL(&priv->rtllib->hw_wakeup_wq,
+ (void *) rtl8192_hw_wakeup_wq, dev);
+ INIT_DELAYED_WORK_RSL(&priv->rtllib->hw_sleep_wq,
+ (void *) rtl8192_hw_sleep_wq, dev);
+ tasklet_init(&priv->irq_rx_tasklet,
+ (void(*)(unsigned long))rtl8192_irq_rx_tasklet,
+ (unsigned long)priv);
+ tasklet_init(&priv->irq_tx_tasklet,
+ (void(*)(unsigned long))rtl8192_irq_tx_tasklet,
+ (unsigned long)priv);
+ tasklet_init(&priv->irq_prepare_beacon_tasklet,
+ (void(*)(unsigned long))rtl8192_prepare_beacon,
+ (unsigned long)priv);
+}
+
+static short rtl8192_get_channel_map(struct net_device *dev)
+{
+ int i;
+
+ struct r8192_priv *priv = rtllib_priv(dev);
+
+ if ((priv->rf_chip != RF_8225) && (priv->rf_chip != RF_8256)
+ && (priv->rf_chip != RF_6052)) {
+ RT_TRACE(COMP_ERR,
+ "%s: unknown rf chip, can't set channel map\n",
+ __func__);
+ return -1;
+ }
+
+ if (priv->ChannelPlan >= COUNTRY_CODE_MAX) {
+ netdev_info(dev,
+ "rtl819x_init:Error channel plan! Set to default.\n");
+ priv->ChannelPlan = COUNTRY_CODE_FCC;
+ }
+ RT_TRACE(COMP_INIT, "Channel plan is %d\n", priv->ChannelPlan);
+ dot11d_init(priv->rtllib);
+ Dot11d_Channelmap(priv->ChannelPlan, priv->rtllib);
+ for (i = 1; i <= 11; i++)
+ (priv->rtllib->active_channel_map)[i] = 1;
+ (priv->rtllib->active_channel_map)[12] = 2;
+ (priv->rtllib->active_channel_map)[13] = 2;
+
+ return 0;
+}
+
+static short rtl8192_init(struct net_device *dev)
+{
+ struct r8192_priv *priv = rtllib_priv(dev);
+
+ memset(&(priv->stats), 0, sizeof(struct rt_stats));
+
+ rtl8192_init_priv_handler(dev);
+ rtl8192_init_priv_constant(dev);
+ rtl8192_init_priv_variable(dev);
+ rtl8192_init_priv_lock(priv);
+ rtl8192_init_priv_task(dev);
+ priv->ops->get_eeprom_size(dev);
+ priv->ops->init_adapter_variable(dev);
+ rtl8192_get_channel_map(dev);
+
+ init_hal_dm(dev);
+
+ init_timer(&priv->watch_dog_timer);
+ setup_timer(&priv->watch_dog_timer,
+ watch_dog_timer_callback,
+ (unsigned long) dev);
+
+ init_timer(&priv->gpio_polling_timer);
+ setup_timer(&priv->gpio_polling_timer,
+ check_rfctrl_gpio_timer,
+ (unsigned long)dev);
+
+ rtl8192_irq_disable(dev);
+ if (request_irq(dev->irq, rtl8192_interrupt, IRQF_SHARED,
+ dev->name, dev)) {
+ netdev_err(dev, "Error allocating IRQ %d", dev->irq);
+ return -1;
+ }
+
+ priv->irq = dev->irq;
+ RT_TRACE(COMP_INIT, "IRQ %d\n", dev->irq);
+
+ if (rtl8192_pci_initdescring(dev) != 0) {
+ netdev_err(dev, "Endopoints initialization failed");
+ free_irq(dev->irq, dev);
+ return -1;
+ }
+
+ return 0;
+}
+
+/***************************************************************************
+ -------------------------------WATCHDOG STUFF---------------------------
+***************************************************************************/
+short rtl8192_is_tx_queue_empty(struct net_device *dev)
+{
+ int i = 0;
+ struct r8192_priv *priv = rtllib_priv(dev);
+
+ for (i = 0; i <= MGNT_QUEUE; i++) {
+ if ((i == TXCMD_QUEUE) || (i == HCCA_QUEUE))
+ continue;
+ if (skb_queue_len(&(&priv->tx_ring[i])->queue) > 0) {
+ netdev_info(dev, "===>tx queue is not empty:%d, %d\n",
+ i, skb_queue_len(&(&priv->tx_ring[i])->queue));
+ return 0;
+ }
+ }
+ return 1;
+}
+
+static enum reset_type rtl819x_TxCheckStuck(struct net_device *dev)
+{
+ struct r8192_priv *priv = rtllib_priv(dev);
+ u8 QueueID;
+ u8 ResetThreshold = NIC_SEND_HANG_THRESHOLD_POWERSAVE;
+ bool bCheckFwTxCnt = false;
+ struct rtl8192_tx_ring *ring = NULL;
+ struct sk_buff *skb = NULL;
+ struct cb_desc *tcb_desc = NULL;
+ unsigned long flags = 0;
+
+ switch (priv->rtllib->ps) {
+ case RTLLIB_PS_DISABLED:
+ ResetThreshold = NIC_SEND_HANG_THRESHOLD_NORMAL;
+ break;
+ case (RTLLIB_PS_MBCAST|RTLLIB_PS_UNICAST):
+ ResetThreshold = NIC_SEND_HANG_THRESHOLD_POWERSAVE;
+ break;
+ default:
+ ResetThreshold = NIC_SEND_HANG_THRESHOLD_POWERSAVE;
+ break;
+ }
+ spin_lock_irqsave(&priv->irq_th_lock, flags);
+ for (QueueID = 0; QueueID < MAX_TX_QUEUE; QueueID++) {
+ if (QueueID == TXCMD_QUEUE)
+ continue;
+
+ if (QueueID == BEACON_QUEUE)
+ continue;
+
+ ring = &priv->tx_ring[QueueID];
+
+ if (skb_queue_len(&ring->queue) == 0) {
+ continue;
+ } else {
+ skb = (&ring->queue)->next;
+ tcb_desc = (struct cb_desc *)(skb->cb +
+ MAX_DEV_ADDR_SIZE);
+ tcb_desc->nStuckCount++;
+ bCheckFwTxCnt = true;
+ if (tcb_desc->nStuckCount > 1)
+ netdev_info(dev,
+ "%s: QueueID=%d tcb_desc->nStuckCount=%d\n",
+ __func__, QueueID,
+ tcb_desc->nStuckCount);
+ }
+ }
+ spin_unlock_irqrestore(&priv->irq_th_lock, flags);
+
+ if (bCheckFwTxCnt) {
+ if (priv->ops->TxCheckStuckHandler(dev)) {
+ RT_TRACE(COMP_RESET,
+ "TxCheckStuck(): Fw indicates no Tx condition!\n");
+ return RESET_TYPE_SILENT;
+ }
+ }
+
+ return RESET_TYPE_NORESET;
+}
+
+static enum reset_type rtl819x_RxCheckStuck(struct net_device *dev)
+{
+ struct r8192_priv *priv = rtllib_priv(dev);
+
+ if (priv->ops->RxCheckStuckHandler(dev)) {
+ RT_TRACE(COMP_RESET, "RxStuck Condition\n");
+ return RESET_TYPE_SILENT;
+ }
+
+ return RESET_TYPE_NORESET;
+}
+
+static enum reset_type rtl819x_ifcheck_resetornot(struct net_device *dev)
+{
+ struct r8192_priv *priv = rtllib_priv(dev);
+ enum reset_type TxResetType = RESET_TYPE_NORESET;
+ enum reset_type RxResetType = RESET_TYPE_NORESET;
+ enum rt_rf_power_state rfState;
+
+ rfState = priv->rtllib->eRFPowerState;
+
+ if (rfState == eRfOn)
+ TxResetType = rtl819x_TxCheckStuck(dev);
+
+ if (rfState == eRfOn &&
+ (priv->rtllib->iw_mode == IW_MODE_INFRA) &&
+ (priv->rtllib->state == RTLLIB_LINKED))
+ RxResetType = rtl819x_RxCheckStuck(dev);
+
+ if (TxResetType == RESET_TYPE_NORMAL ||
+ RxResetType == RESET_TYPE_NORMAL) {
+ netdev_info(dev, "%s(): TxResetType is %d, RxResetType is %d\n",
+ __func__, TxResetType, RxResetType);
+ return RESET_TYPE_NORMAL;
+ } else if (TxResetType == RESET_TYPE_SILENT ||
+ RxResetType == RESET_TYPE_SILENT) {
+ netdev_info(dev, "%s(): TxResetType is %d, RxResetType is %d\n",
+ __func__, TxResetType, RxResetType);
+ return RESET_TYPE_SILENT;
+ } else {
+ return RESET_TYPE_NORESET;
+ }
+
+}
+
+static void rtl819x_silentreset_mesh_bk(struct net_device *dev, u8 IsPortal)
+{
+}
+
+static void rtl819x_ifsilentreset(struct net_device *dev)
+{
+ struct r8192_priv *priv = rtllib_priv(dev);
+ u8 reset_times = 0;
+ int reset_status = 0;
+ struct rtllib_device *ieee = priv->rtllib;
+ unsigned long flag;
+
+ u8 IsPortal = 0;
+
+
+ if (priv->ResetProgress == RESET_TYPE_NORESET) {
+
+ RT_TRACE(COMP_RESET, "=========>Reset progress!!\n");
+
+ priv->ResetProgress = RESET_TYPE_SILENT;
+
+ spin_lock_irqsave(&priv->rf_ps_lock, flag);
+ if (priv->RFChangeInProgress) {
+ spin_unlock_irqrestore(&priv->rf_ps_lock, flag);
+ goto END;
+ }
+ priv->RFChangeInProgress = true;
+ priv->bResetInProgress = true;
+ spin_unlock_irqrestore(&priv->rf_ps_lock, flag);
+
+RESET_START:
+
+ down(&priv->wx_sem);
+
+ if (priv->rtllib->state == RTLLIB_LINKED)
+ LeisurePSLeave(dev);
+
+ if (priv->up) {
+ RT_TRACE(COMP_ERR,
+ "%s():the driver is not up! return\n",
+ __func__);
+ up(&priv->wx_sem);
+ return;
+ }
+ priv->up = 0;
+
+ RT_TRACE(COMP_RESET, "%s():======>start to down the driver\n",
+ __func__);
+ mdelay(1000);
+ RT_TRACE(COMP_RESET,
+ "%s():111111111111111111111111======>start to down the driver\n",
+ __func__);
+
+ if (!netif_queue_stopped(dev))
+ netif_stop_queue(dev);
+
+ rtl8192_irq_disable(dev);
+ del_timer_sync(&priv->watch_dog_timer);
+ rtl8192_cancel_deferred_work(priv);
+ deinit_hal_dm(dev);
+ rtllib_stop_scan_syncro(ieee);
+
+ if (ieee->state == RTLLIB_LINKED) {
+ SEM_DOWN_IEEE_WX(&ieee->wx_sem);
+ netdev_info(dev, "ieee->state is RTLLIB_LINKED\n");
+ rtllib_stop_send_beacons(priv->rtllib);
+ del_timer_sync(&ieee->associate_timer);
+ cancel_delayed_work(&ieee->associate_retry_wq);
+ rtllib_stop_scan(ieee);
+ netif_carrier_off(dev);
+ SEM_UP_IEEE_WX(&ieee->wx_sem);
+ } else {
+ netdev_info(dev, "ieee->state is NOT LINKED\n");
+ rtllib_softmac_stop_protocol(priv->rtllib, 0 , true);
+ }
+
+ dm_backup_dynamic_mechanism_state(dev);
+
+ up(&priv->wx_sem);
+ RT_TRACE(COMP_RESET,
+ "%s():<==========down process is finished\n",
+ __func__);
+
+ RT_TRACE(COMP_RESET, "%s():<===========up process start\n",
+ __func__);
+ reset_status = _rtl8192_up(dev, true);
+
+ RT_TRACE(COMP_RESET,
+ "%s():<===========up process is finished\n", __func__);
+ if (reset_status == -1) {
+ if (reset_times < 3) {
+ reset_times++;
+ goto RESET_START;
+ } else {
+ RT_TRACE(COMP_ERR,
+ " ERR!!! %s(): Reset Failed!!\n",
+ __func__);
+ }
+ }
+
+ ieee->is_silent_reset = 1;
+
+ spin_lock_irqsave(&priv->rf_ps_lock, flag);
+ priv->RFChangeInProgress = false;
+ spin_unlock_irqrestore(&priv->rf_ps_lock, flag);
+
+ EnableHWSecurityConfig8192(dev);
+
+ if (ieee->state == RTLLIB_LINKED && ieee->iw_mode ==
+ IW_MODE_INFRA) {
+ ieee->set_chan(ieee->dev,
+ ieee->current_network.channel);
+
+ queue_work_rsl(ieee->wq, &ieee->associate_complete_wq);
+
+ } else if (ieee->state == RTLLIB_LINKED && ieee->iw_mode ==
+ IW_MODE_ADHOC) {
+ ieee->set_chan(ieee->dev,
+ ieee->current_network.channel);
+ ieee->link_change(ieee->dev);
+
+ notify_wx_assoc_event(ieee);
+
+ rtllib_start_send_beacons(ieee);
+
+ if (ieee->data_hard_resume)
+ ieee->data_hard_resume(ieee->dev);
+ netif_carrier_on(ieee->dev);
+ } else if (ieee->iw_mode == IW_MODE_MESH) {
+ rtl819x_silentreset_mesh_bk(dev, IsPortal);
+ }
+
+ CamRestoreAllEntry(dev);
+ dm_restore_dynamic_mechanism_state(dev);
+END:
+ priv->ResetProgress = RESET_TYPE_NORESET;
+ priv->reset_count++;
+
+ priv->bForcedSilentReset = false;
+ priv->bResetInProgress = false;
+
+ write_nic_byte(dev, UFWP, 1);
+ RT_TRACE(COMP_RESET, "Reset finished!! ====>[%d]\n",
+ priv->reset_count);
+ }
+}
+
+static void rtl819x_update_rxcounts(struct r8192_priv *priv, u32 *TotalRxBcnNum,
+ u32 *TotalRxDataNum)
+{
+ u16 SlotIndex;
+ u8 i;
+
+ *TotalRxBcnNum = 0;
+ *TotalRxDataNum = 0;
+
+ SlotIndex = (priv->rtllib->LinkDetectInfo.SlotIndex++) %
+ (priv->rtllib->LinkDetectInfo.SlotNum);
+ priv->rtllib->LinkDetectInfo.RxBcnNum[SlotIndex] =
+ priv->rtllib->LinkDetectInfo.NumRecvBcnInPeriod;
+ priv->rtllib->LinkDetectInfo.RxDataNum[SlotIndex] =
+ priv->rtllib->LinkDetectInfo.NumRecvDataInPeriod;
+ for (i = 0; i < priv->rtllib->LinkDetectInfo.SlotNum; i++) {
+ *TotalRxBcnNum += priv->rtllib->LinkDetectInfo.RxBcnNum[i];
+ *TotalRxDataNum += priv->rtllib->LinkDetectInfo.RxDataNum[i];
+ }
+}
+
+
+void rtl819x_watchdog_wqcallback(void *data)
+{
+ struct r8192_priv *priv = container_of_dwork_rsl(data,
+ struct r8192_priv, watch_dog_wq);
+ struct net_device *dev = priv->rtllib->dev;
+ struct rtllib_device *ieee = priv->rtllib;
+ enum reset_type ResetType = RESET_TYPE_NORESET;
+ static u8 check_reset_cnt;
+ unsigned long flags;
+ struct rt_pwr_save_ctrl *pPSC = (struct rt_pwr_save_ctrl *)
+ (&(priv->rtllib->PowerSaveControl));
+ bool bBusyTraffic = false;
+ bool bHigherBusyTraffic = false;
+ bool bHigherBusyRxTraffic = false;
+ bool bEnterPS = false;
+
+ if (!priv->up || priv->bHwRadioOff)
+ return;
+
+ if (priv->rtllib->state >= RTLLIB_LINKED) {
+ if (priv->rtllib->CntAfterLink < 2)
+ priv->rtllib->CntAfterLink++;
+ } else {
+ priv->rtllib->CntAfterLink = 0;
+ }
+
+ hal_dm_watchdog(dev);
+
+ if (rtllib_act_scanning(priv->rtllib, false) == false) {
+ if ((ieee->iw_mode == IW_MODE_INFRA) && (ieee->state ==
+ RTLLIB_NOLINK) &&
+ (ieee->eRFPowerState == eRfOn) && !ieee->is_set_key &&
+ (!ieee->proto_stoppping) && !ieee->wx_set_enc) {
+ if ((ieee->PowerSaveControl.ReturnPoint ==
+ IPS_CALLBACK_NONE) &&
+ (!ieee->bNetPromiscuousMode)) {
+ RT_TRACE(COMP_PS,
+ "====================>haha: IPSEnter()\n");
+ IPSEnter(dev);
+ }
+ }
+ }
+ if ((ieee->state == RTLLIB_LINKED) && (ieee->iw_mode ==
+ IW_MODE_INFRA) && (!ieee->bNetPromiscuousMode)) {
+ if (ieee->LinkDetectInfo.NumRxOkInPeriod > 100 ||
+ ieee->LinkDetectInfo.NumTxOkInPeriod > 100)
+ bBusyTraffic = true;
+
+
+ if (ieee->LinkDetectInfo.NumRxOkInPeriod > 4000 ||
+ ieee->LinkDetectInfo.NumTxOkInPeriod > 4000) {
+ bHigherBusyTraffic = true;
+ if (ieee->LinkDetectInfo.NumRxOkInPeriod > 5000)
+ bHigherBusyRxTraffic = true;
+ else
+ bHigherBusyRxTraffic = false;
+ }
+
+ if (((ieee->LinkDetectInfo.NumRxUnicastOkInPeriod +
+ ieee->LinkDetectInfo.NumTxOkInPeriod) > 8) ||
+ (ieee->LinkDetectInfo.NumRxUnicastOkInPeriod > 2))
+ bEnterPS = false;
+ else
+ bEnterPS = true;
+
+ if (ieee->current_network.beacon_interval < 95)
+ bEnterPS = false;
+
+ if (bEnterPS)
+ LeisurePSEnter(dev);
+ else
+ LeisurePSLeave(dev);
+
+ } else {
+ RT_TRACE(COMP_LPS, "====>no link LPS leave\n");
+ LeisurePSLeave(dev);
+ }
+
+ ieee->LinkDetectInfo.NumRxOkInPeriod = 0;
+ ieee->LinkDetectInfo.NumTxOkInPeriod = 0;
+ ieee->LinkDetectInfo.NumRxUnicastOkInPeriod = 0;
+ ieee->LinkDetectInfo.bBusyTraffic = bBusyTraffic;
+
+ ieee->LinkDetectInfo.bHigherBusyTraffic = bHigherBusyTraffic;
+ ieee->LinkDetectInfo.bHigherBusyRxTraffic = bHigherBusyRxTraffic;
+
+ if (ieee->state == RTLLIB_LINKED && ieee->iw_mode == IW_MODE_INFRA) {
+ u32 TotalRxBcnNum = 0;
+ u32 TotalRxDataNum = 0;
+
+ rtl819x_update_rxcounts(priv, &TotalRxBcnNum, &TotalRxDataNum);
+
+ if ((TotalRxBcnNum+TotalRxDataNum) == 0)
+ priv->check_roaming_cnt++;
+ else
+ priv->check_roaming_cnt = 0;
+
+
+ if (priv->check_roaming_cnt > 0) {
+ if (ieee->eRFPowerState == eRfOff)
+ RT_TRACE(COMP_ERR, "========>%s()\n", __func__);
+
+ netdev_info(dev,
+ "===>%s(): AP is power off, chan:%d, connect another one\n",
+ __func__, priv->chan);
+
+ ieee->state = RTLLIB_ASSOCIATING;
+
+ RemovePeerTS(priv->rtllib,
+ priv->rtllib->current_network.bssid);
+ ieee->is_roaming = true;
+ ieee->is_set_key = false;
+ ieee->link_change(dev);
+ if (ieee->LedControlHandler)
+ ieee->LedControlHandler(ieee->dev,
+ LED_CTL_START_TO_LINK);
+
+ notify_wx_assoc_event(ieee);
+
+ if (!(ieee->rtllib_ap_sec_type(ieee) &
+ (SEC_ALG_CCMP|SEC_ALG_TKIP)))
+ queue_delayed_work_rsl(ieee->wq,
+ &ieee->associate_procedure_wq, 0);
+
+ priv->check_roaming_cnt = 0;
+ }
+ ieee->LinkDetectInfo.NumRecvBcnInPeriod = 0;
+ ieee->LinkDetectInfo.NumRecvDataInPeriod = 0;
+
+ }
+
+ spin_lock_irqsave(&priv->tx_lock, flags);
+ if ((check_reset_cnt++ >= 3) && (!ieee->is_roaming) &&
+ (!priv->RFChangeInProgress) && (!pPSC->bSwRfProcessing)) {
+ ResetType = rtl819x_ifcheck_resetornot(dev);
+ check_reset_cnt = 3;
+ }
+ spin_unlock_irqrestore(&priv->tx_lock, flags);
+
+ if (!priv->bDisableNormalResetCheck && ResetType == RESET_TYPE_NORMAL) {
+ priv->ResetProgress = RESET_TYPE_NORMAL;
+ RT_TRACE(COMP_RESET, "%s(): NOMAL RESET\n", __func__);
+ return;
+ }
+
+ if (((priv->force_reset) || (!priv->bDisableNormalResetCheck &&
+ ResetType == RESET_TYPE_SILENT)))
+ rtl819x_ifsilentreset(dev);
+ priv->force_reset = false;
+ priv->bForcedSilentReset = false;
+ priv->bResetInProgress = false;
+ RT_TRACE(COMP_TRACE, " <==RtUsbCheckForHangWorkItemCallback()\n");
+}
+
+void watch_dog_timer_callback(unsigned long data)
+{
+ struct r8192_priv *priv = rtllib_priv((struct net_device *)data);
+
+ queue_delayed_work_rsl(priv->priv_wq, &priv->watch_dog_wq, 0);
+ mod_timer(&priv->watch_dog_timer, jiffies +
+ msecs_to_jiffies(RTLLIB_WATCH_DOG_TIME));
+}
+
+/****************************************************************************
+ ---------------------------- NIC TX/RX STUFF---------------------------
+*****************************************************************************/
+void rtl8192_rx_enable(struct net_device *dev)
+{
+ struct r8192_priv *priv = (struct r8192_priv *)rtllib_priv(dev);
+
+ priv->ops->rx_enable(dev);
+}
+
+void rtl8192_tx_enable(struct net_device *dev)
+{
+ struct r8192_priv *priv = (struct r8192_priv *)rtllib_priv(dev);
+
+ priv->ops->tx_enable(dev);
+
+ rtllib_reset_queue(priv->rtllib);
+}
+
+
+static void rtl8192_free_rx_ring(struct net_device *dev)
+{
+ struct r8192_priv *priv = rtllib_priv(dev);
+ int i, rx_queue_idx;
+
+ for (rx_queue_idx = 0; rx_queue_idx < MAX_RX_QUEUE;
+ rx_queue_idx++) {
+ for (i = 0; i < priv->rxringcount; i++) {
+ struct sk_buff *skb = priv->rx_buf[rx_queue_idx][i];
+
+ if (!skb)
+ continue;
+
+ pci_unmap_single(priv->pdev,
+ *((dma_addr_t *)skb->cb),
+ priv->rxbuffersize, PCI_DMA_FROMDEVICE);
+ kfree_skb(skb);
+ }
+
+ pci_free_consistent(priv->pdev,
+ sizeof(*priv->rx_ring[rx_queue_idx]) *
+ priv->rxringcount,
+ priv->rx_ring[rx_queue_idx],
+ priv->rx_ring_dma[rx_queue_idx]);
+ priv->rx_ring[rx_queue_idx] = NULL;
+ }
+}
+
+static void rtl8192_free_tx_ring(struct net_device *dev, unsigned int prio)
+{
+ struct r8192_priv *priv = rtllib_priv(dev);
+ struct rtl8192_tx_ring *ring = &priv->tx_ring[prio];
+
+ while (skb_queue_len(&ring->queue)) {
+ struct tx_desc *entry = &ring->desc[ring->idx];
+ struct sk_buff *skb = __skb_dequeue(&ring->queue);
+
+ pci_unmap_single(priv->pdev, entry->TxBuffAddr,
+ skb->len, PCI_DMA_TODEVICE);
+ kfree_skb(skb);
+ ring->idx = (ring->idx + 1) % ring->entries;
+ }
+
+ pci_free_consistent(priv->pdev, sizeof(*ring->desc)*ring->entries,
+ ring->desc, ring->dma);
+ ring->desc = NULL;
+}
+
+void rtl8192_data_hard_stop(struct net_device *dev)
+{
+}
+
+
+void rtl8192_data_hard_resume(struct net_device *dev)
+{
+}
+
+void rtl8192_hard_data_xmit(struct sk_buff *skb, struct net_device *dev,
+ int rate)
+{
+ struct r8192_priv *priv = (struct r8192_priv *)rtllib_priv(dev);
+ int ret;
+ struct cb_desc *tcb_desc = (struct cb_desc *)(skb->cb +
+ MAX_DEV_ADDR_SIZE);
+ u8 queue_index = tcb_desc->queue_index;
+
+ if ((priv->rtllib->eRFPowerState == eRfOff) || !priv->up ||
+ priv->bResetInProgress) {
+ kfree_skb(skb);
+ return;
+ }
+
+ assert(queue_index != TXCMD_QUEUE);
+
+
+ memcpy((unsigned char *)(skb->cb), &dev, sizeof(dev));
+ skb_push(skb, priv->rtllib->tx_headroom);
+ ret = rtl8192_tx(dev, skb);
+ if (ret != 0)
+ kfree_skb(skb);
+
+ if (queue_index != MGNT_QUEUE) {
+ priv->rtllib->stats.tx_bytes += (skb->len -
+ priv->rtllib->tx_headroom);
+ priv->rtllib->stats.tx_packets++;
+ }
+}
+
+int rtl8192_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+ struct r8192_priv *priv = (struct r8192_priv *)rtllib_priv(dev);
+ int ret;
+ struct cb_desc *tcb_desc = (struct cb_desc *)(skb->cb +
+ MAX_DEV_ADDR_SIZE);
+ u8 queue_index = tcb_desc->queue_index;
+
+ if (queue_index != TXCMD_QUEUE) {
+ if ((priv->rtllib->eRFPowerState == eRfOff) ||
+ !priv->up || priv->bResetInProgress) {
+ kfree_skb(skb);
+ return 0;
+ }
+ }
+
+ memcpy((unsigned char *)(skb->cb), &dev, sizeof(dev));
+ if (queue_index == TXCMD_QUEUE) {
+ rtl8192_tx_cmd(dev, skb);
+ return 0;
+ }
+
+ tcb_desc->RATRIndex = 7;
+ tcb_desc->bTxDisableRateFallBack = 1;
+ tcb_desc->bTxUseDriverAssingedRate = 1;
+ tcb_desc->bTxEnableFwCalcDur = 1;
+ skb_push(skb, priv->rtllib->tx_headroom);
+ ret = rtl8192_tx(dev, skb);
+ if (ret != 0)
+ kfree_skb(skb);
+ return ret;
+}
+
+static void rtl8192_tx_isr(struct net_device *dev, int prio)
+{
+ struct r8192_priv *priv = (struct r8192_priv *)rtllib_priv(dev);
+
+ struct rtl8192_tx_ring *ring = &priv->tx_ring[prio];
+
+ while (skb_queue_len(&ring->queue)) {
+ struct tx_desc *entry = &ring->desc[ring->idx];
+ struct sk_buff *skb;
+
+ if (prio != BEACON_QUEUE) {
+ if (entry->OWN)
+ return;
+ ring->idx = (ring->idx + 1) % ring->entries;
+ }
+
+ skb = __skb_dequeue(&ring->queue);
+ pci_unmap_single(priv->pdev, entry->TxBuffAddr,
+ skb->len, PCI_DMA_TODEVICE);
+
+ kfree_skb(skb);
+ }
+ if (prio != BEACON_QUEUE)
+ tasklet_schedule(&priv->irq_tx_tasklet);
+}
+
+void rtl8192_tx_cmd(struct net_device *dev, struct sk_buff *skb)
+{
+ struct r8192_priv *priv = rtllib_priv(dev);
+ struct rtl8192_tx_ring *ring;
+ struct tx_desc_cmd *entry;
+ unsigned int idx;
+ struct cb_desc *tcb_desc;
+ unsigned long flags;
+
+ spin_lock_irqsave(&priv->irq_th_lock, flags);
+ ring = &priv->tx_ring[TXCMD_QUEUE];
+
+ idx = (ring->idx + skb_queue_len(&ring->queue)) % ring->entries;
+ entry = (struct tx_desc_cmd *) &ring->desc[idx];
+
+ tcb_desc = (struct cb_desc *)(skb->cb + MAX_DEV_ADDR_SIZE);
+
+ priv->ops->tx_fill_cmd_descriptor(dev, entry, tcb_desc, skb);
+
+ __skb_queue_tail(&ring->queue, skb);
+ spin_unlock_irqrestore(&priv->irq_th_lock, flags);
+}
+
+short rtl8192_tx(struct net_device *dev, struct sk_buff *skb)
+{
+ struct r8192_priv *priv = rtllib_priv(dev);
+ struct rtl8192_tx_ring *ring;
+ unsigned long flags;
+ struct cb_desc *tcb_desc = (struct cb_desc *)(skb->cb +
+ MAX_DEV_ADDR_SIZE);
+ struct tx_desc *pdesc = NULL;
+ struct rtllib_hdr_1addr *header = NULL;
+ u16 fc = 0, type = 0, stype = 0;
+ bool multi_addr = false, broad_addr = false, uni_addr = false;
+ u8 *pda_addr = NULL;
+ int idx;
+ u32 fwinfo_size = 0;
+
+ if (priv->bdisable_nic) {
+ RT_TRACE(COMP_ERR,
+ "%s: ERR!! Nic is disabled! Can't tx packet len=%d qidx=%d!!!\n",
+ __func__, skb->len, tcb_desc->queue_index);
+ return skb->len;
+ }
+
+ priv->rtllib->bAwakePktSent = true;
+
+ fwinfo_size = sizeof(struct tx_fwinfo_8190pci);
+
+ header = (struct rtllib_hdr_1addr *)(((u8 *)skb->data) + fwinfo_size);
+ fc = le16_to_cpu(header->frame_ctl);
+ type = WLAN_FC_GET_TYPE(fc);
+ stype = WLAN_FC_GET_STYPE(fc);
+ pda_addr = header->addr1;
+
+ if (is_broadcast_ether_addr(pda_addr))
+ broad_addr = true;
+ else if (is_multicast_ether_addr(pda_addr))
+ multi_addr = true;
+ else
+ uni_addr = true;
+
+ if (uni_addr)
+ priv->stats.txbytesunicast += skb->len - fwinfo_size;
+ else if (multi_addr)
+ priv->stats.txbytesmulticast += skb->len - fwinfo_size;
+ else
+ priv->stats.txbytesbroadcast += skb->len - fwinfo_size;
+
+ spin_lock_irqsave(&priv->irq_th_lock, flags);
+ ring = &priv->tx_ring[tcb_desc->queue_index];
+ if (tcb_desc->queue_index != BEACON_QUEUE)
+ idx = (ring->idx + skb_queue_len(&ring->queue)) % ring->entries;
+ else
+ idx = 0;
+
+ pdesc = &ring->desc[idx];
+ if ((pdesc->OWN == 1) && (tcb_desc->queue_index != BEACON_QUEUE)) {
+ RT_TRACE(COMP_ERR,
+ "No more TX desc@%d, ring->idx = %d, idx = %d, skblen = 0x%x queuelen=%d",
+ tcb_desc->queue_index, ring->idx, idx, skb->len,
+ skb_queue_len(&ring->queue));
+ spin_unlock_irqrestore(&priv->irq_th_lock, flags);
+ return skb->len;
+ }
+
+ if (type == RTLLIB_FTYPE_DATA) {
+ if (priv->rtllib->LedControlHandler)
+ priv->rtllib->LedControlHandler(dev, LED_CTL_TX);
+ }
+ priv->ops->tx_fill_descriptor(dev, pdesc, tcb_desc, skb);
+ __skb_queue_tail(&ring->queue, skb);
+ pdesc->OWN = 1;
+ spin_unlock_irqrestore(&priv->irq_th_lock, flags);
+ dev->trans_start = jiffies;
+
+ write_nic_word(dev, TPPoll, 0x01 << tcb_desc->queue_index);
+ return 0;
+}
+
+static short rtl8192_alloc_rx_desc_ring(struct net_device *dev)
+{
+ struct r8192_priv *priv = rtllib_priv(dev);
+ struct rx_desc *entry = NULL;
+ int i, rx_queue_idx;
+
+ for (rx_queue_idx = 0; rx_queue_idx < MAX_RX_QUEUE; rx_queue_idx++) {
+ priv->rx_ring[rx_queue_idx] =
+ pci_zalloc_consistent(priv->pdev,
+ sizeof(*priv->rx_ring[rx_queue_idx]) * priv->rxringcount,
+ &priv->rx_ring_dma[rx_queue_idx]);
+ if (!priv->rx_ring[rx_queue_idx] ||
+ (unsigned long)priv->rx_ring[rx_queue_idx] & 0xFF) {
+ RT_TRACE(COMP_ERR, "Cannot allocate RX ring\n");
+ return -ENOMEM;
+ }
+
+ priv->rx_idx[rx_queue_idx] = 0;
+
+ for (i = 0; i < priv->rxringcount; i++) {
+ struct sk_buff *skb = dev_alloc_skb(priv->rxbuffersize);
+ dma_addr_t *mapping;
+
+ entry = &priv->rx_ring[rx_queue_idx][i];
+ if (!skb)
+ return 0;
+ skb->dev = dev;
+ priv->rx_buf[rx_queue_idx][i] = skb;
+ mapping = (dma_addr_t *)skb->cb;
+ *mapping = pci_map_single(priv->pdev,
+ skb_tail_pointer_rsl(skb),
+ priv->rxbuffersize,
+ PCI_DMA_FROMDEVICE);
+ if (pci_dma_mapping_error(priv->pdev, *mapping)) {
+ dev_kfree_skb_any(skb);
+ return -1;
+ }
+ entry->BufferAddress = *mapping;
+
+ entry->Length = priv->rxbuffersize;
+ entry->OWN = 1;
+ }
+
+ if(entry)
+ entry->EOR = 1;
+ }
+ return 0;
+}
+
+static int rtl8192_alloc_tx_desc_ring(struct net_device *dev,
+ unsigned int prio, unsigned int entries)
+{
+ struct r8192_priv *priv = (struct r8192_priv *)rtllib_priv(dev);
+ struct tx_desc *ring;
+ dma_addr_t dma;
+ int i;
+
+ ring = pci_zalloc_consistent(priv->pdev, sizeof(*ring) * entries, &dma);
+ if (!ring || (unsigned long)ring & 0xFF) {
+ RT_TRACE(COMP_ERR, "Cannot allocate TX ring (prio = %d)\n",
+ prio);
+ return -ENOMEM;
+ }
+
+ priv->tx_ring[prio].desc = ring;
+ priv->tx_ring[prio].dma = dma;
+ priv->tx_ring[prio].idx = 0;
+ priv->tx_ring[prio].entries = entries;
+ skb_queue_head_init(&priv->tx_ring[prio].queue);
+
+ for (i = 0; i < entries; i++)
+ ring[i].NextDescAddress =
+ (u32)dma + ((i + 1) % entries) *
+ sizeof(*ring);
+
+ return 0;
+}
+
+
+short rtl8192_pci_initdescring(struct net_device *dev)
+{
+ u32 ret;
+ int i;
+ struct r8192_priv *priv = rtllib_priv(dev);
+
+ ret = rtl8192_alloc_rx_desc_ring(dev);
+ if (ret)
+ return ret;
+
+ for (i = 0; i < MAX_TX_QUEUE_COUNT; i++) {
+ ret = rtl8192_alloc_tx_desc_ring(dev, i, priv->txringcount);
+ if (ret)
+ goto err_free_rings;
+ }
+
+ return 0;
+
+err_free_rings:
+ rtl8192_free_rx_ring(dev);
+ for (i = 0; i < MAX_TX_QUEUE_COUNT; i++)
+ if (priv->tx_ring[i].desc)
+ rtl8192_free_tx_ring(dev, i);
+ return 1;
+}
+
+void rtl8192_pci_resetdescring(struct net_device *dev)
+{
+ struct r8192_priv *priv = rtllib_priv(dev);
+ int i, rx_queue_idx;
+ unsigned long flags = 0;
+
+ for (rx_queue_idx = 0; rx_queue_idx < MAX_RX_QUEUE; rx_queue_idx++) {
+ if (priv->rx_ring[rx_queue_idx]) {
+ struct rx_desc *entry = NULL;
+
+ for (i = 0; i < priv->rxringcount; i++) {
+ entry = &priv->rx_ring[rx_queue_idx][i];
+ entry->OWN = 1;
+ }
+ priv->rx_idx[rx_queue_idx] = 0;
+ }
+ }
+
+ spin_lock_irqsave(&priv->irq_th_lock, flags);
+ for (i = 0; i < MAX_TX_QUEUE_COUNT; i++) {
+ if (priv->tx_ring[i].desc) {
+ struct rtl8192_tx_ring *ring = &priv->tx_ring[i];
+
+ while (skb_queue_len(&ring->queue)) {
+ struct tx_desc *entry = &ring->desc[ring->idx];
+ struct sk_buff *skb =
+ __skb_dequeue(&ring->queue);
+
+ pci_unmap_single(priv->pdev,
+ entry->TxBuffAddr,
+ skb->len, PCI_DMA_TODEVICE);
+ kfree_skb(skb);
+ ring->idx = (ring->idx + 1) % ring->entries;
+ }
+ ring->idx = 0;
+ }
+ }
+ spin_unlock_irqrestore(&priv->irq_th_lock, flags);
+}
+
+void rtl819x_UpdateRxPktTimeStamp(struct net_device *dev,
+ struct rtllib_rx_stats *stats)
+{
+ struct r8192_priv *priv = (struct r8192_priv *)rtllib_priv(dev);
+
+ if (stats->bIsAMPDU && !stats->bFirstMPDU)
+ stats->mac_time = priv->LastRxDescTSF;
+ else
+ priv->LastRxDescTSF = stats->mac_time;
+}
+
+long rtl819x_translate_todbm(struct r8192_priv *priv, u8 signal_strength_index)
+{
+ long signal_power;
+
+ signal_power = (long)((signal_strength_index + 1) >> 1);
+ signal_power -= 95;
+
+ return signal_power;
+}
+
+
+void
+rtl819x_update_rxsignalstatistics8190pci(
+ struct r8192_priv *priv,
+ struct rtllib_rx_stats *pprevious_stats
+ )
+{
+ int weighting = 0;
+
+
+ if (priv->stats.recv_signal_power == 0)
+ priv->stats.recv_signal_power =
+ pprevious_stats->RecvSignalPower;
+
+ if (pprevious_stats->RecvSignalPower > priv->stats.recv_signal_power)
+ weighting = 5;
+ else if (pprevious_stats->RecvSignalPower <
+ priv->stats.recv_signal_power)
+ weighting = (-5);
+ priv->stats.recv_signal_power = (priv->stats.recv_signal_power * 5 +
+ pprevious_stats->RecvSignalPower +
+ weighting) / 6;
+}
+
+void rtl819x_process_cck_rxpathsel(struct r8192_priv *priv,
+ struct rtllib_rx_stats *pprevious_stats)
+{
+}
+
+
+u8 rtl819x_query_rxpwrpercentage(char antpower)
+{
+ if ((antpower <= -100) || (antpower >= 20))
+ return 0;
+ else if (antpower >= 0)
+ return 100;
+ else
+ return 100 + antpower;
+
+} /* QueryRxPwrPercentage */
+
+u8
+rtl819x_evm_dbtopercentage(
+ char value
+ )
+{
+ char ret_val;
+
+ ret_val = value;
+
+ if (ret_val >= 0)
+ ret_val = 0;
+ if (ret_val <= -33)
+ ret_val = -33;
+ ret_val = 0 - ret_val;
+ ret_val *= 3;
+ if (ret_val == 99)
+ ret_val = 100;
+ return ret_val;
+}
+
+void
+rtl8192_record_rxdesc_forlateruse(
+ struct rtllib_rx_stats *psrc_stats,
+ struct rtllib_rx_stats *ptarget_stats
+)
+{
+ ptarget_stats->bIsAMPDU = psrc_stats->bIsAMPDU;
+ ptarget_stats->bFirstMPDU = psrc_stats->bFirstMPDU;
+}
+
+
+
+static void rtl8192_rx_normal(struct net_device *dev)
+{
+ struct r8192_priv *priv = (struct r8192_priv *)rtllib_priv(dev);
+ struct rtllib_hdr_1addr *rtllib_hdr = NULL;
+ bool unicast_packet = false;
+ bool bLedBlinking = true;
+ u16 fc = 0, type = 0;
+ u32 skb_len = 0;
+ int rx_queue_idx = RX_MPDU_QUEUE;
+
+ struct rtllib_rx_stats stats = {
+ .signal = 0,
+ .noise = -98,
+ .rate = 0,
+ .freq = RTLLIB_24GHZ_BAND,
+ };
+ unsigned int count = priv->rxringcount;
+
+ stats.nic_type = NIC_8192E;
+
+ while (count--) {
+ struct rx_desc *pdesc = &priv->rx_ring[rx_queue_idx]
+ [priv->rx_idx[rx_queue_idx]];
+ struct sk_buff *skb = priv->rx_buf[rx_queue_idx]
+ [priv->rx_idx[rx_queue_idx]];
+ struct sk_buff *new_skb;
+
+ if (pdesc->OWN)
+ return;
+ if (!priv->ops->rx_query_status_descriptor(dev, &stats,
+ pdesc, skb))
+ goto done;
+ new_skb = dev_alloc_skb(priv->rxbuffersize);
+ /* if allocation of new skb failed - drop current packet
+ * and reuse skb
+ */
+ if (unlikely(!new_skb))
+ goto done;
+
+ pci_unmap_single(priv->pdev,
+ *((dma_addr_t *)skb->cb),
+ priv->rxbuffersize,
+ PCI_DMA_FROMDEVICE);
+
+ skb_put(skb, pdesc->Length);
+ skb_reserve(skb, stats.RxDrvInfoSize +
+ stats.RxBufShift);
+ skb_trim(skb, skb->len - 4/*sCrcLng*/);
+ rtllib_hdr = (struct rtllib_hdr_1addr *)skb->data;
+ if (!is_multicast_ether_addr(rtllib_hdr->addr1)) {
+ /* unicast packet */
+ unicast_packet = true;
+ }
+ fc = le16_to_cpu(rtllib_hdr->frame_ctl);
+ type = WLAN_FC_GET_TYPE(fc);
+ if (type == RTLLIB_FTYPE_MGMT)
+ bLedBlinking = false;
+
+ if (bLedBlinking)
+ if (priv->rtllib->LedControlHandler)
+ priv->rtllib->LedControlHandler(dev,
+ LED_CTL_RX);
+
+ if (stats.bCRC) {
+ if (type != RTLLIB_FTYPE_MGMT)
+ priv->stats.rxdatacrcerr++;
+ else
+ priv->stats.rxmgmtcrcerr++;
+ }
+
+ skb_len = skb->len;
+
+ if (!rtllib_rx(priv->rtllib, skb, &stats)) {
+ dev_kfree_skb_any(skb);
+ } else {
+ priv->stats.rxok++;
+ if (unicast_packet)
+ priv->stats.rxbytesunicast += skb_len;
+ }
+
+ skb = new_skb;
+ skb->dev = dev;
+
+ priv->rx_buf[rx_queue_idx][priv->rx_idx[rx_queue_idx]] =
+ skb;
+ *((dma_addr_t *) skb->cb) = pci_map_single(priv->pdev,
+ skb_tail_pointer_rsl(skb),
+ priv->rxbuffersize,
+ PCI_DMA_FROMDEVICE);
+ if (pci_dma_mapping_error(priv->pdev,
+ *((dma_addr_t *)skb->cb))) {
+ dev_kfree_skb_any(skb);
+ return;
+ }
+done:
+ pdesc->BufferAddress = *((dma_addr_t *)skb->cb);
+ pdesc->OWN = 1;
+ pdesc->Length = priv->rxbuffersize;
+ if (priv->rx_idx[rx_queue_idx] == priv->rxringcount-1)
+ pdesc->EOR = 1;
+ priv->rx_idx[rx_queue_idx] = (priv->rx_idx[rx_queue_idx] + 1) %
+ priv->rxringcount;
+ }
+
+}
+
+static void rtl8192_rx_cmd(struct net_device *dev)
+{
+}
+
+
+static void rtl8192_tx_resume(struct net_device *dev)
+{
+ struct r8192_priv *priv = (struct r8192_priv *)rtllib_priv(dev);
+ struct rtllib_device *ieee = priv->rtllib;
+ struct sk_buff *skb;
+ int queue_index;
+
+ for (queue_index = BK_QUEUE;
+ queue_index < MAX_QUEUE_SIZE; queue_index++) {
+ while ((!skb_queue_empty(&ieee->skb_waitQ[queue_index])) &&
+ (priv->rtllib->check_nic_enough_desc(dev, queue_index) > 0)) {
+ skb = skb_dequeue(&ieee->skb_waitQ[queue_index]);
+ ieee->softmac_data_hard_start_xmit(skb, dev, 0);
+ }
+ }
+}
+
+void rtl8192_irq_tx_tasklet(struct r8192_priv *priv)
+{
+ rtl8192_tx_resume(priv->rtllib->dev);
+}
+
+void rtl8192_irq_rx_tasklet(struct r8192_priv *priv)
+{
+ rtl8192_rx_normal(priv->rtllib->dev);
+
+ if (MAX_RX_QUEUE > 1)
+ rtl8192_rx_cmd(priv->rtllib->dev);
+
+ write_nic_dword(priv->rtllib->dev, INTA_MASK,
+ read_nic_dword(priv->rtllib->dev, INTA_MASK) | IMR_RDU);
+}
+
+/****************************************************************************
+ ---------------------------- NIC START/CLOSE STUFF---------------------------
+*****************************************************************************/
+void rtl8192_cancel_deferred_work(struct r8192_priv *priv)
+{
+ cancel_delayed_work(&priv->watch_dog_wq);
+ cancel_delayed_work(&priv->update_beacon_wq);
+ cancel_delayed_work(&priv->rtllib->hw_sleep_wq);
+ cancel_work_sync(&priv->reset_wq);
+ cancel_work_sync(&priv->qos_activate);
+}
+
+int _rtl8192_up(struct net_device *dev, bool is_silent_reset)
+{
+ if (_rtl8192_sta_up(dev, is_silent_reset) == -1)
+ return -1;
+ return 0;
+}
+
+
+static int rtl8192_open(struct net_device *dev)
+{
+ struct r8192_priv *priv = rtllib_priv(dev);
+ int ret;
+
+ down(&priv->wx_sem);
+ ret = rtl8192_up(dev);
+ up(&priv->wx_sem);
+ return ret;
+
+}
+
+
+int rtl8192_up(struct net_device *dev)
+{
+ struct r8192_priv *priv = rtllib_priv(dev);
+
+ if (priv->up == 1)
+ return -1;
+ return _rtl8192_up(dev, false);
+}
+
+
+static int rtl8192_close(struct net_device *dev)
+{
+ struct r8192_priv *priv = rtllib_priv(dev);
+ int ret;
+
+ if ((rtllib_act_scanning(priv->rtllib, false)) &&
+ !(priv->rtllib->softmac_features & IEEE_SOFTMAC_SCAN)) {
+ rtllib_stop_scan(priv->rtllib);
+ }
+
+ down(&priv->wx_sem);
+
+ ret = rtl8192_down(dev, true);
+
+ up(&priv->wx_sem);
+
+ return ret;
+
+}
+
+int rtl8192_down(struct net_device *dev, bool shutdownrf)
+{
+ if (rtl8192_sta_down(dev, shutdownrf) == -1)
+ return -1;
+
+ return 0;
+}
+
+void rtl8192_commit(struct net_device *dev)
+{
+ struct r8192_priv *priv = rtllib_priv(dev);
+
+ if (priv->up == 0)
+ return;
+ rtllib_softmac_stop_protocol(priv->rtllib, 0 , true);
+ rtl8192_irq_disable(dev);
+ priv->ops->stop_adapter(dev, true);
+ _rtl8192_up(dev, false);
+}
+
+void rtl8192_restart(void *data)
+{
+ struct r8192_priv *priv = container_of_work_rsl(data, struct r8192_priv,
+ reset_wq);
+ struct net_device *dev = priv->rtllib->dev;
+
+ down(&priv->wx_sem);
+
+ rtl8192_commit(dev);
+
+ up(&priv->wx_sem);
+}
+
+static void r8192_set_multicast(struct net_device *dev)
+{
+ struct r8192_priv *priv = rtllib_priv(dev);
+ short promisc;
+
+ promisc = (dev->flags & IFF_PROMISC) ? 1 : 0;
+ priv->promisc = promisc;
+
+}
+
+
+static int r8192_set_mac_adr(struct net_device *dev, void *mac)
+{
+ struct r8192_priv *priv = rtllib_priv(dev);
+ struct sockaddr *addr = mac;
+
+ down(&priv->wx_sem);
+
+ ether_addr_copy(dev->dev_addr, addr->sa_data);
+
+ schedule_work(&priv->reset_wq);
+ up(&priv->wx_sem);
+
+ return 0;
+}
+
+/* based on ipw2200 driver */
+static int rtl8192_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
+{
+ struct r8192_priv *priv = (struct r8192_priv *)rtllib_priv(dev);
+ struct iwreq *wrq = (struct iwreq *)rq;
+ int ret = -1;
+ struct rtllib_device *ieee = priv->rtllib;
+ u32 key[4];
+ u8 broadcast_addr[6] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
+ u8 zero_addr[6] = {0};
+ struct iw_point *p = &wrq->u.data;
+ struct ieee_param *ipw = NULL;
+
+ down(&priv->wx_sem);
+
+ switch (cmd) {
+ case RTL_IOCTL_WPA_SUPPLICANT:
+ if (p->length < sizeof(struct ieee_param) || !p->pointer) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ ipw = memdup_user(p->pointer, p->length);
+ if (IS_ERR(ipw)) {
+ ret = PTR_ERR(ipw);
+ goto out;
+ }
+
+ if (ipw->cmd == IEEE_CMD_SET_ENCRYPTION) {
+ if (ipw->u.crypt.set_tx) {
+ if (strcmp(ipw->u.crypt.alg, "CCMP") == 0)
+ ieee->pairwise_key_type = KEY_TYPE_CCMP;
+ else if (strcmp(ipw->u.crypt.alg, "TKIP") == 0)
+ ieee->pairwise_key_type = KEY_TYPE_TKIP;
+ else if (strcmp(ipw->u.crypt.alg, "WEP") == 0) {
+ if (ipw->u.crypt.key_len == 13)
+ ieee->pairwise_key_type =
+ KEY_TYPE_WEP104;
+ else if (ipw->u.crypt.key_len == 5)
+ ieee->pairwise_key_type =
+ KEY_TYPE_WEP40;
+ } else {
+ ieee->pairwise_key_type = KEY_TYPE_NA;
+ }
+
+ if (ieee->pairwise_key_type) {
+ if (memcmp(ieee->ap_mac_addr, zero_addr,
+ 6) == 0)
+ ieee->iw_mode = IW_MODE_ADHOC;
+ memcpy((u8 *)key, ipw->u.crypt.key, 16);
+ EnableHWSecurityConfig8192(dev);
+ set_swcam(dev, 4, ipw->u.crypt.idx,
+ ieee->pairwise_key_type,
+ (u8 *)ieee->ap_mac_addr,
+ 0, key, 0);
+ setKey(dev, 4, ipw->u.crypt.idx,
+ ieee->pairwise_key_type,
+ (u8 *)ieee->ap_mac_addr, 0, key);
+ if (ieee->iw_mode == IW_MODE_ADHOC) {
+ set_swcam(dev, ipw->u.crypt.idx,
+ ipw->u.crypt.idx,
+ ieee->pairwise_key_type,
+ (u8 *)ieee->ap_mac_addr,
+ 0, key, 0);
+ setKey(dev, ipw->u.crypt.idx,
+ ipw->u.crypt.idx,
+ ieee->pairwise_key_type,
+ (u8 *)ieee->ap_mac_addr,
+ 0, key);
+ }
+ }
+ if ((ieee->pairwise_key_type == KEY_TYPE_CCMP)
+ && ieee->pHTInfo->bCurrentHTSupport) {
+ write_nic_byte(dev, 0x173, 1);
+ }
+
+ } else {
+ memcpy((u8 *)key, ipw->u.crypt.key, 16);
+ if (strcmp(ipw->u.crypt.alg, "CCMP") == 0)
+ ieee->group_key_type = KEY_TYPE_CCMP;
+ else if (strcmp(ipw->u.crypt.alg, "TKIP") == 0)
+ ieee->group_key_type = KEY_TYPE_TKIP;
+ else if (strcmp(ipw->u.crypt.alg, "WEP") == 0) {
+ if (ipw->u.crypt.key_len == 13)
+ ieee->group_key_type =
+ KEY_TYPE_WEP104;
+ else if (ipw->u.crypt.key_len == 5)
+ ieee->group_key_type =
+ KEY_TYPE_WEP40;
+ } else
+ ieee->group_key_type = KEY_TYPE_NA;
+
+ if (ieee->group_key_type) {
+ set_swcam(dev, ipw->u.crypt.idx,
+ ipw->u.crypt.idx,
+ ieee->group_key_type,
+ broadcast_addr, 0, key, 0);
+ setKey(dev, ipw->u.crypt.idx,
+ ipw->u.crypt.idx,
+ ieee->group_key_type,
+ broadcast_addr, 0, key);
+ }
+ }
+ }
+
+ ret = rtllib_wpa_supplicant_ioctl(priv->rtllib, &wrq->u.data,
+ 0);
+ kfree(ipw);
+ break;
+ default:
+ ret = -EOPNOTSUPP;
+ break;
+ }
+
+out:
+ up(&priv->wx_sem);
+
+ return ret;
+}
+
+
+static irqreturn_t rtl8192_interrupt(int irq, void *netdev)
+{
+ struct net_device *dev = (struct net_device *) netdev;
+ struct r8192_priv *priv = (struct r8192_priv *)rtllib_priv(dev);
+ unsigned long flags;
+ u32 inta;
+ u32 intb;
+
+ intb = 0;
+
+ if (priv->irq_enabled == 0)
+ goto done;
+
+ spin_lock_irqsave(&priv->irq_th_lock, flags);
+
+ priv->ops->interrupt_recognized(dev, &inta, &intb);
+ priv->stats.shints++;
+
+ if (!inta) {
+ spin_unlock_irqrestore(&priv->irq_th_lock, flags);
+ goto done;
+ }
+
+ if (inta == 0xffff) {
+ spin_unlock_irqrestore(&priv->irq_th_lock, flags);
+ goto done;
+ }
+
+ priv->stats.ints++;
+
+ if (!netif_running(dev)) {
+ spin_unlock_irqrestore(&priv->irq_th_lock, flags);
+ goto done;
+ }
+
+ if (inta & IMR_TBDOK) {
+ RT_TRACE(COMP_INTR, "beacon ok interrupt!\n");
+ priv->stats.txbeaconokint++;
+ }
+
+ if (inta & IMR_TBDER) {
+ RT_TRACE(COMP_INTR, "beacon ok interrupt!\n");
+ priv->stats.txbeaconerr++;
+ }
+
+ if (inta & IMR_BDOK)
+ RT_TRACE(COMP_INTR, "beacon interrupt!\n");
+
+ if (inta & IMR_MGNTDOK) {
+ RT_TRACE(COMP_INTR, "Manage ok interrupt!\n");
+ priv->stats.txmanageokint++;
+ rtl8192_tx_isr(dev, MGNT_QUEUE);
+ spin_unlock_irqrestore(&priv->irq_th_lock, flags);
+ if (priv->rtllib->ack_tx_to_ieee) {
+ if (rtl8192_is_tx_queue_empty(dev)) {
+ priv->rtllib->ack_tx_to_ieee = 0;
+ rtllib_ps_tx_ack(priv->rtllib, 1);
+ }
+ }
+ spin_lock_irqsave(&priv->irq_th_lock, flags);
+ }
+
+ if (inta & IMR_COMDOK) {
+ priv->stats.txcmdpktokint++;
+ rtl8192_tx_isr(dev, TXCMD_QUEUE);
+ }
+
+ if (inta & IMR_HIGHDOK)
+ rtl8192_tx_isr(dev, HIGH_QUEUE);
+
+ if (inta & IMR_ROK) {
+ priv->stats.rxint++;
+ priv->InterruptLog.nIMR_ROK++;
+ tasklet_schedule(&priv->irq_rx_tasklet);
+ }
+
+ if (inta & IMR_BcnInt) {
+ RT_TRACE(COMP_INTR, "prepare beacon for interrupt!\n");
+ tasklet_schedule(&priv->irq_prepare_beacon_tasklet);
+ }
+
+ if (inta & IMR_RDU) {
+ RT_TRACE(COMP_INTR, "rx descriptor unavailable!\n");
+ priv->stats.rxrdu++;
+ write_nic_dword(dev, INTA_MASK,
+ read_nic_dword(dev, INTA_MASK) & ~IMR_RDU);
+ tasklet_schedule(&priv->irq_rx_tasklet);
+ }
+
+ if (inta & IMR_RXFOVW) {
+ RT_TRACE(COMP_INTR, "rx overflow !\n");
+ priv->stats.rxoverflow++;
+ tasklet_schedule(&priv->irq_rx_tasklet);
+ }
+
+ if (inta & IMR_TXFOVW)
+ priv->stats.txoverflow++;
+
+ if (inta & IMR_BKDOK) {
+ RT_TRACE(COMP_INTR, "BK Tx OK interrupt!\n");
+ priv->stats.txbkokint++;
+ priv->rtllib->LinkDetectInfo.NumTxOkInPeriod++;
+ rtl8192_tx_isr(dev, BK_QUEUE);
+ }
+
+ if (inta & IMR_BEDOK) {
+ RT_TRACE(COMP_INTR, "BE TX OK interrupt!\n");
+ priv->stats.txbeokint++;
+ priv->rtllib->LinkDetectInfo.NumTxOkInPeriod++;
+ rtl8192_tx_isr(dev, BE_QUEUE);
+ }
+
+ if (inta & IMR_VIDOK) {
+ RT_TRACE(COMP_INTR, "VI TX OK interrupt!\n");
+ priv->stats.txviokint++;
+ priv->rtllib->LinkDetectInfo.NumTxOkInPeriod++;
+ rtl8192_tx_isr(dev, VI_QUEUE);
+ }
+
+ if (inta & IMR_VODOK) {
+ priv->stats.txvookint++;
+ RT_TRACE(COMP_INTR, "Vo TX OK interrupt!\n");
+ priv->rtllib->LinkDetectInfo.NumTxOkInPeriod++;
+ rtl8192_tx_isr(dev, VO_QUEUE);
+ }
+
+ spin_unlock_irqrestore(&priv->irq_th_lock, flags);
+
+done:
+
+ return IRQ_HANDLED;
+}
+
+
+
+/****************************************************************************
+ ---------------------------- PCI_STUFF---------------------------
+*****************************************************************************/
+static const struct net_device_ops rtl8192_netdev_ops = {
+ .ndo_open = rtl8192_open,
+ .ndo_stop = rtl8192_close,
+ .ndo_tx_timeout = rtl8192_tx_timeout,
+ .ndo_do_ioctl = rtl8192_ioctl,
+ .ndo_set_rx_mode = r8192_set_multicast,
+ .ndo_set_mac_address = r8192_set_mac_adr,
+ .ndo_validate_addr = eth_validate_addr,
+ .ndo_change_mtu = eth_change_mtu,
+ .ndo_start_xmit = rtllib_xmit,
+};
+
+static int rtl8192_pci_probe(struct pci_dev *pdev,
+ const struct pci_device_id *id)
+{
+ unsigned long ioaddr = 0;
+ struct net_device *dev = NULL;
+ struct r8192_priv *priv = NULL;
+ struct rtl819x_ops *ops = (struct rtl819x_ops *)(id->driver_data);
+ unsigned long pmem_start, pmem_len, pmem_flags;
+ int err = -ENOMEM;
+ bool bdma64 = false;
+ u8 revision_id;
+
+ RT_TRACE(COMP_INIT, "Configuring chip resources");
+
+ if (pci_enable_device(pdev)) {
+ RT_TRACE(COMP_ERR, "Failed to enable PCI device");
+ return -EIO;
+ }
+
+ pci_set_master(pdev);
+
+ if (!pci_set_dma_mask(pdev, DMA_BIT_MASK(32))) {
+ if (pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32))) {
+ dev_info(&pdev->dev,
+ "Unable to obtain 32bit DMA for consistent allocations\n");
+ goto err_pci_disable;
+ }
+ }
+ dev = alloc_rtllib(sizeof(struct r8192_priv));
+ if (!dev)
+ goto err_pci_disable;
+
+ err = -ENODEV;
+ if (bdma64)
+ dev->features |= NETIF_F_HIGHDMA;
+
+ pci_set_drvdata(pdev, dev);
+ SET_NETDEV_DEV(dev, &pdev->dev);
+ priv = rtllib_priv(dev);
+ priv->rtllib = (struct rtllib_device *)netdev_priv_rsl(dev);
+ priv->pdev = pdev;
+ priv->rtllib->pdev = pdev;
+ if ((pdev->subsystem_vendor == PCI_VENDOR_ID_DLINK) &&
+ (pdev->subsystem_device == 0x3304))
+ priv->rtllib->bSupportRemoteWakeUp = 1;
+ else
+ priv->rtllib->bSupportRemoteWakeUp = 0;
+
+ pmem_start = pci_resource_start(pdev, 1);
+ pmem_len = pci_resource_len(pdev, 1);
+ pmem_flags = pci_resource_flags(pdev, 1);
+
+ if (!(pmem_flags & IORESOURCE_MEM)) {
+ RT_TRACE(COMP_ERR, "region #1 not a MMIO resource, aborting");
+ goto err_rel_rtllib;
+ }
+
+ dev_info(&pdev->dev, "Memory mapped space start: 0x%08lx\n",
+ pmem_start);
+ if (!request_mem_region(pmem_start, pmem_len, DRV_NAME)) {
+ RT_TRACE(COMP_ERR, "request_mem_region failed!");
+ goto err_rel_rtllib;
+ }
+
+
+ ioaddr = (unsigned long)ioremap_nocache(pmem_start, pmem_len);
+ if (ioaddr == (unsigned long)NULL) {
+ RT_TRACE(COMP_ERR, "ioremap failed!");
+ goto err_rel_mem;
+ }
+
+ dev->mem_start = ioaddr;
+ dev->mem_end = ioaddr + pci_resource_len(pdev, 0);
+
+ pci_read_config_byte(pdev, 0x08, &revision_id);
+ /* If the revisionid is 0x10, the device uses rtl8192se. */
+ if (pdev->device == 0x8192 && revision_id == 0x10)
+ goto err_rel_mem;
+
+ priv->ops = ops;
+
+ if (rtl8192_pci_findadapter(pdev, dev) == false)
+ goto err_rel_mem;
+
+ dev->irq = pdev->irq;
+ priv->irq = 0;
+
+ dev->netdev_ops = &rtl8192_netdev_ops;
+
+ dev->wireless_handlers = &r8192_wx_handlers_def;
+ dev->ethtool_ops = &rtl819x_ethtool_ops;
+
+ dev->type = ARPHRD_ETHER;
+ dev->watchdog_timeo = HZ * 3;
+
+ if (dev_alloc_name(dev, ifname) < 0) {
+ RT_TRACE(COMP_INIT,
+ "Oops: devname already taken! Trying wlan%%d...\n");
+ dev_alloc_name(dev, ifname);
+ }
+
+ RT_TRACE(COMP_INIT, "Driver probe completed1\n");
+ if (rtl8192_init(dev) != 0) {
+ RT_TRACE(COMP_ERR, "Initialization failed");
+ goto err_free_irq;
+ }
+
+ netif_carrier_off(dev);
+ netif_stop_queue(dev);
+
+ if (register_netdev(dev))
+ goto err_free_irq;
+ RT_TRACE(COMP_INIT, "dev name: %s\n", dev->name);
+
+ if (priv->polling_timer_on == 0)
+ check_rfctrl_gpio_timer((unsigned long)dev);
+
+ RT_TRACE(COMP_INIT, "Driver probe completed\n");
+ return 0;
+
+err_free_irq:
+ free_irq(dev->irq, dev);
+ priv->irq = 0;
+err_rel_mem:
+ release_mem_region(pmem_start, pmem_len);
+err_rel_rtllib:
+ free_rtllib(dev);
+
+ DMESG("wlan driver load failed\n");
+err_pci_disable:
+ pci_disable_device(pdev);
+ return err;
+}
+
+static void rtl8192_pci_disconnect(struct pci_dev *pdev)
+{
+ struct net_device *dev = pci_get_drvdata(pdev);
+ struct r8192_priv *priv;
+ u32 i;
+
+ if (dev) {
+ unregister_netdev(dev);
+
+ priv = rtllib_priv(dev);
+
+ del_timer_sync(&priv->gpio_polling_timer);
+ cancel_delayed_work(&priv->gpio_change_rf_wq);
+ priv->polling_timer_on = 0;
+ rtl8192_down(dev, true);
+ deinit_hal_dm(dev);
+ if (priv->pFirmware) {
+ vfree(priv->pFirmware);
+ priv->pFirmware = NULL;
+ }
+ destroy_workqueue(priv->priv_wq);
+ rtl8192_free_rx_ring(dev);
+ for (i = 0; i < MAX_TX_QUEUE_COUNT; i++)
+ rtl8192_free_tx_ring(dev, i);
+
+ if (priv->irq) {
+ dev_info(&pdev->dev, "Freeing irq %d\n", dev->irq);
+ free_irq(dev->irq, dev);
+ priv->irq = 0;
+ }
+ free_rtllib(dev);
+
+ kfree(priv->scan_cmd);
+
+ if (dev->mem_start != 0) {
+ iounmap((void __iomem *)dev->mem_start);
+ release_mem_region(pci_resource_start(pdev, 1),
+ pci_resource_len(pdev, 1));
+ }
+ } else {
+ priv = rtllib_priv(dev);
+ }
+
+ pci_disable_device(pdev);
+ RT_TRACE(COMP_DOWN, "wlan driver removed\n");
+}
+
+bool NicIFEnableNIC(struct net_device *dev)
+{
+ bool init_status = true;
+ struct r8192_priv *priv = rtllib_priv(dev);
+ struct rt_pwr_save_ctrl *pPSC = (struct rt_pwr_save_ctrl *)
+ (&(priv->rtllib->PowerSaveControl));
+
+ if (!priv->up) {
+ RT_TRACE(COMP_ERR, "ERR!!! %s(): Driver is already down!\n",
+ __func__);
+ priv->bdisable_nic = false;
+ return false;
+ }
+
+ RT_TRACE(COMP_PS, "===========>%s()\n", __func__);
+ priv->bfirst_init = true;
+ init_status = priv->ops->initialize_adapter(dev);
+ if (!init_status) {
+ RT_TRACE(COMP_ERR, "ERR!!! %s(): initialization is failed!\n",
+ __func__);
+ priv->bdisable_nic = false;
+ return false;
+ }
+ RT_TRACE(COMP_INIT, "start adapter finished\n");
+ RT_CLEAR_PS_LEVEL(pPSC, RT_RF_OFF_LEVL_HALT_NIC);
+ priv->bfirst_init = false;
+
+ rtl8192_irq_enable(dev);
+ priv->bdisable_nic = false;
+ RT_TRACE(COMP_PS, "<===========%s()\n", __func__);
+ return init_status;
+}
+bool NicIFDisableNIC(struct net_device *dev)
+{
+ struct r8192_priv *priv = rtllib_priv(dev);
+ u8 tmp_state = 0;
+
+ RT_TRACE(COMP_PS, "=========>%s()\n", __func__);
+ priv->bdisable_nic = true;
+ tmp_state = priv->rtllib->state;
+ rtllib_softmac_stop_protocol(priv->rtllib, 0, false);
+ priv->rtllib->state = tmp_state;
+ rtl8192_cancel_deferred_work(priv);
+ rtl8192_irq_disable(dev);
+
+ priv->ops->stop_adapter(dev, false);
+ RT_TRACE(COMP_PS, "<=========%s()\n", __func__);
+
+ return true;
+}
+
+static int __init rtl8192_pci_module_init(void)
+{
+ pr_info("\nLinux kernel driver for RTL8192E WLAN cards\n");
+ pr_info("Copyright (c) 2007-2008, Realsil Wlan Driver\n");
+
+ if (0 != pci_register_driver(&rtl8192_pci_driver)) {
+ DMESG("No device found");
+ /*pci_unregister_driver (&rtl8192_pci_driver);*/
+ return -ENODEV;
+ }
+ return 0;
+}
+
+static void __exit rtl8192_pci_module_exit(void)
+{
+ pci_unregister_driver(&rtl8192_pci_driver);
+
+ RT_TRACE(COMP_DOWN, "Exiting");
+}
+
+void check_rfctrl_gpio_timer(unsigned long data)
+{
+ struct r8192_priv *priv = rtllib_priv((struct net_device *)data);
+
+ priv->polling_timer_on = 1;
+
+ queue_delayed_work_rsl(priv->priv_wq, &priv->gpio_change_rf_wq, 0);
+
+ mod_timer(&priv->gpio_polling_timer, jiffies +
+ msecs_to_jiffies(RTLLIB_WATCH_DOG_TIME));
+}
+
+/***************************************************************************
+ ------------------- module init / exit stubs ----------------
+****************************************************************************/
+module_init(rtl8192_pci_module_init);
+module_exit(rtl8192_pci_module_exit);
+
+MODULE_DESCRIPTION("Linux driver for Realtek RTL819x WiFi cards");
+MODULE_AUTHOR(DRV_COPYRIGHT " " DRV_AUTHOR);
+MODULE_VERSION(DRV_VERSION);
+MODULE_LICENSE("GPL");
+/*(DEBLOBBED)*/
+
+module_param(ifname, charp, S_IRUGO|S_IWUSR);
+module_param(hwwep, int, S_IRUGO|S_IWUSR);
+module_param(channels, int, S_IRUGO|S_IWUSR);
+
+MODULE_PARM_DESC(ifname, " Net interface name, wlan%d=default");
+MODULE_PARM_DESC(hwwep, " Try to use hardware WEP support(default use hw. set 0 to use software security)");
+MODULE_PARM_DESC(channels, " Channel bitmask for specific locales. NYI");
diff --git a/drivers/staging/rtl8192e/rtl8192e/rtl_core.h b/drivers/staging/rtl8192e/rtl8192e/rtl_core.h
new file mode 100644
index 000000000..d365af6eb
--- /dev/null
+++ b/drivers/staging/rtl8192e/rtl8192e/rtl_core.h
@@ -0,0 +1,1076 @@
+/******************************************************************************
+ * Copyright(c) 2008 - 2010 Realtek Corporation. All rights reserved.
+ *
+ * Based on the r8180 driver, which is:
+ * Copyright 2004-2005 Andrea Merello <andrea.merello@gmail.com>, et al.
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ * The full GNU General Public License is included in this distribution in the
+ * file called LICENSE.
+ *
+ * Contact Information:
+ * wlanfae <wlanfae@realtek.com>
+******************************************************************************/
+
+#ifndef _RTL_CORE_H
+#define _RTL_CORE_H
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/ioport.h>
+#include <linux/sched.h>
+#include <linux/types.h>
+#include <linux/interrupt.h>
+#include <linux/slab.h>
+#include <linux/netdevice.h>
+#include <linux/pci.h>
+#include <linux/etherdevice.h>
+#include <linux/delay.h>
+#include <linux/rtnetlink.h>
+#include <linux/wireless.h>
+#include <linux/timer.h>
+#include <linux/proc_fs.h>
+#include <linux/if_arp.h>
+#include <linux/random.h>
+#include <linux/io.h>
+
+/* Need this defined before including local include files */
+#define DRV_NAME "rtl819xE"
+
+#include "../rtllib.h"
+
+#include "../dot11d.h"
+
+#include "r8192E_firmware.h"
+#include "r8192E_hw.h"
+
+#include "r8190P_def.h"
+#include "r8192E_dev.h"
+
+#include "rtl_eeprom.h"
+#include "rtl_ps.h"
+#include "rtl_pci.h"
+#include "rtl_cam.h"
+
+#define DRV_COPYRIGHT \
+ "Copyright(c) 2008 - 2010 Realsil Semiconductor Corporation"
+#define DRV_AUTHOR "<wlanfae@realtek.com>"
+#define DRV_VERSION "0014.0401.2010"
+
+#define IS_HARDWARE_TYPE_819xP(_priv) \
+ ((((struct r8192_priv *)rtllib_priv(dev))->card_8192 == NIC_8190P) || \
+ (((struct r8192_priv *)rtllib_priv(dev))->card_8192 == NIC_8192E))
+#define IS_HARDWARE_TYPE_8192SE(_priv) \
+ (((struct r8192_priv *)rtllib_priv(dev))->card_8192 == NIC_8192SE)
+#define IS_HARDWARE_TYPE_8192CE(_priv) \
+ (((struct r8192_priv *)rtllib_priv(dev))->card_8192 == NIC_8192CE)
+#define IS_HARDWARE_TYPE_8192CU(_priv) \
+ (((struct r8192_priv *)rtllib_priv(dev))->card_8192 == NIC_8192CU)
+#define IS_HARDWARE_TYPE_8192DE(_priv) \
+ (((struct r8192_priv *)rtllib_priv(dev))->card_8192 == NIC_8192DE)
+#define IS_HARDWARE_TYPE_8192DU(_priv) \
+ (((struct r8192_priv *)rtllib_priv(dev))->card_8192 == NIC_8192DU)
+
+#define RTL_PCI_DEVICE(vend, dev, cfg) \
+ .vendor = (vend), .device = (dev), \
+ .subvendor = PCI_ANY_ID, .subdevice = PCI_ANY_ID , \
+ .driver_data = (kernel_ulong_t)&(cfg)
+
+#define RTL_MAX_SCAN_SIZE 128
+
+#define RTL_RATE_MAX 30
+
+#define TOTAL_CAM_ENTRY 32
+#define CAM_CONTENT_COUNT 8
+
+#ifndef BIT
+#define BIT(_i) (1<<(_i))
+#endif
+
+#define IS_ADAPTER_SENDS_BEACON(dev) 0
+
+#define HAL_MEMORY_MAPPED_IO_RANGE_8190PCI 0x1000
+#define HAL_HW_PCI_REVISION_ID_8190PCI 0x00
+#define HAL_MEMORY_MAPPED_IO_RANGE_8192PCIE 0x4000
+#define HAL_HW_PCI_REVISION_ID_8192PCIE 0x01
+#define HAL_MEMORY_MAPPED_IO_RANGE_8192SE 0x4000
+#define HAL_HW_PCI_REVISION_ID_8192SE 0x10
+#define HAL_HW_PCI_REVISION_ID_8192CE 0x1
+#define HAL_MEMORY_MAPPED_IO_RANGE_8192CE 0x4000
+#define HAL_HW_PCI_REVISION_ID_8192DE 0x0
+#define HAL_MEMORY_MAPPED_IO_RANGE_8192DE 0x4000
+
+#define HAL_HW_PCI_8180_DEVICE_ID 0x8180
+#define HAL_HW_PCI_8185_DEVICE_ID 0x8185
+#define HAL_HW_PCI_8188_DEVICE_ID 0x8188
+#define HAL_HW_PCI_8198_DEVICE_ID 0x8198
+#define HAL_HW_PCI_8190_DEVICE_ID 0x8190
+#define HAL_HW_PCI_8192_DEVICE_ID 0x8192
+#define HAL_HW_PCI_8192SE_DEVICE_ID 0x8192
+#define HAL_HW_PCI_8174_DEVICE_ID 0x8174
+#define HAL_HW_PCI_8173_DEVICE_ID 0x8173
+#define HAL_HW_PCI_8172_DEVICE_ID 0x8172
+#define HAL_HW_PCI_8171_DEVICE_ID 0x8171
+#define HAL_HW_PCI_0045_DEVICE_ID 0x0045
+#define HAL_HW_PCI_0046_DEVICE_ID 0x0046
+#define HAL_HW_PCI_0044_DEVICE_ID 0x0044
+#define HAL_HW_PCI_0047_DEVICE_ID 0x0047
+#define HAL_HW_PCI_700F_DEVICE_ID 0x700F
+#define HAL_HW_PCI_701F_DEVICE_ID 0x701F
+#define HAL_HW_PCI_DLINK_DEVICE_ID 0x3304
+#define HAL_HW_PCI_8192CET_DEVICE_ID 0x8191
+#define HAL_HW_PCI_8192CE_DEVICE_ID 0x8178
+#define HAL_HW_PCI_8191CE_DEVICE_ID 0x8177
+#define HAL_HW_PCI_8188CE_DEVICE_ID 0x8176
+#define HAL_HW_PCI_8192CU_DEVICE_ID 0x8191
+#define HAL_HW_PCI_8192DE_DEVICE_ID 0x092D
+#define HAL_HW_PCI_8192DU_DEVICE_ID 0x092D
+
+#define RTL819X_DEFAULT_RF_TYPE RF_1T2R
+
+#define RTLLIB_WATCH_DOG_TIME 2000
+
+#define MAX_DEV_ADDR_SIZE 8 /*support till 64 bit bus width OS*/
+#define MAX_FIRMWARE_INFORMATION_SIZE 32
+#define MAX_802_11_HEADER_LENGTH (40 + MAX_FIRMWARE_INFORMATION_SIZE)
+#define ENCRYPTION_MAX_OVERHEAD 128
+#define MAX_FRAGMENT_COUNT 8
+#define MAX_TRANSMIT_BUFFER_SIZE \
+ (1600 + (MAX_802_11_HEADER_LENGTH + ENCRYPTION_MAX_OVERHEAD) * \
+ MAX_FRAGMENT_COUNT)
+
+#define scrclng 4
+
+#define DEFAULT_FRAG_THRESHOLD 2342U
+#define MIN_FRAG_THRESHOLD 256U
+#define DEFAULT_BEACONINTERVAL 0x64U
+
+#define DEFAULT_SSID ""
+#define DEFAULT_RETRY_RTS 7
+#define DEFAULT_RETRY_DATA 7
+#define PRISM_HDR_SIZE 64
+
+#define PHY_RSSI_SLID_WIN_MAX 100
+
+#define RTL_IOCTL_WPA_SUPPLICANT (SIOCIWFIRSTPRIV + 30)
+
+#define TxBBGainTableLength 37
+#define CCKTxBBGainTableLength 23
+
+#define CHANNEL_PLAN_LEN 10
+#define sCrcLng 4
+
+#define NIC_SEND_HANG_THRESHOLD_NORMAL 4
+#define NIC_SEND_HANG_THRESHOLD_POWERSAVE 8
+
+#define MAX_TX_QUEUE 9
+
+#define MAX_RX_QUEUE 1
+
+#define MAX_RX_COUNT 64
+#define MAX_TX_QUEUE_COUNT 9
+
+extern int hwwep;
+
+enum RTL819x_PHY_PARAM {
+ RTL819X_PHY_MACPHY_REG = 0,
+ RTL819X_PHY_MACPHY_REG_PG = 1,
+ RTL8188C_PHY_MACREG = 2,
+ RTL8192C_PHY_MACREG = 3,
+ RTL819X_PHY_REG = 4,
+ RTL819X_PHY_REG_1T2R = 5,
+ RTL819X_PHY_REG_to1T1R = 6,
+ RTL819X_PHY_REG_to1T2R = 7,
+ RTL819X_PHY_REG_to2T2R = 8,
+ RTL819X_PHY_REG_PG = 9,
+ RTL819X_AGC_TAB = 10,
+ RTL819X_PHY_RADIO_A = 11,
+ RTL819X_PHY_RADIO_A_1T = 12,
+ RTL819X_PHY_RADIO_A_2T = 13,
+ RTL819X_PHY_RADIO_B = 14,
+ RTL819X_PHY_RADIO_B_GM = 15,
+ RTL819X_PHY_RADIO_C = 16,
+ RTL819X_PHY_RADIO_D = 17,
+ RTL819X_EEPROM_MAP = 18,
+ RTL819X_EFUSE_MAP = 19,
+};
+
+enum nic_t {
+ NIC_UNKNOWN = 0,
+ NIC_8192E = 1,
+ NIC_8190P = 2,
+ NIC_8192SE = 4,
+ NIC_8192CE = 5,
+ NIC_8192CU = 6,
+ NIC_8192DE = 7,
+ NIC_8192DU = 8,
+};
+
+enum rt_eeprom_type {
+ EEPROM_93C46,
+ EEPROM_93C56,
+ EEPROM_BOOT_EFUSE,
+};
+
+enum dcmg_txcmd_op {
+ TXCMD_TXRA_HISTORY_CTRL = 0xFF900000,
+ TXCMD_RESET_TX_PKT_BUFF = 0xFF900001,
+ TXCMD_RESET_RX_PKT_BUFF = 0xFF900002,
+ TXCMD_SET_TX_DURATION = 0xFF900003,
+ TXCMD_SET_RX_RSSI = 0xFF900004,
+ TXCMD_SET_TX_PWR_TRACKING = 0xFF900005,
+ TXCMD_XXXX_CTRL,
+};
+
+enum rt_rf_type_819xu {
+ RF_TYPE_MIN = 0,
+ RF_8225,
+ RF_8256,
+ RF_8258,
+ RF_6052 = 4,
+ RF_PSEUDO_11N = 5,
+};
+
+enum rf_step {
+ RF_STEP_INIT = 0,
+ RF_STEP_NORMAL,
+ RF_STEP_MAX
+};
+
+enum rt_status {
+ RT_STATUS_SUCCESS,
+ RT_STATUS_FAILURE,
+ RT_STATUS_PENDING,
+ RT_STATUS_RESOURCE
+};
+
+enum rt_customer_id {
+ RT_CID_DEFAULT = 0,
+ RT_CID_8187_ALPHA0 = 1,
+ RT_CID_8187_SERCOMM_PS = 2,
+ RT_CID_8187_HW_LED = 3,
+ RT_CID_8187_NETGEAR = 4,
+ RT_CID_WHQL = 5,
+ RT_CID_819x_CAMEO = 6,
+ RT_CID_819x_RUNTOP = 7,
+ RT_CID_819x_Senao = 8,
+ RT_CID_TOSHIBA = 9,
+ RT_CID_819x_Netcore = 10,
+ RT_CID_Nettronix = 11,
+ RT_CID_DLINK = 12,
+ RT_CID_PRONET = 13,
+ RT_CID_COREGA = 14,
+ RT_CID_819x_ALPHA = 15,
+ RT_CID_819x_Sitecom = 16,
+ RT_CID_CCX = 17,
+ RT_CID_819x_Lenovo = 18,
+ RT_CID_819x_QMI = 19,
+ RT_CID_819x_Edimax_Belkin = 20,
+ RT_CID_819x_Sercomm_Belkin = 21,
+ RT_CID_819x_CAMEO1 = 22,
+ RT_CID_819x_MSI = 23,
+ RT_CID_819x_Acer = 24,
+ RT_CID_819x_HP = 27,
+ RT_CID_819x_CLEVO = 28,
+ RT_CID_819x_Arcadyan_Belkin = 29,
+ RT_CID_819x_SAMSUNG = 30,
+ RT_CID_819x_WNC_COREGA = 31,
+};
+
+enum reset_type {
+ RESET_TYPE_NORESET = 0x00,
+ RESET_TYPE_NORMAL = 0x01,
+ RESET_TYPE_SILENT = 0x02
+};
+
+enum ic_inferiority_8192s {
+ IC_INFERIORITY_A = 0,
+ IC_INFERIORITY_B = 1,
+};
+
+enum pci_bridge_vendor {
+ PCI_BRIDGE_VENDOR_INTEL = 0x0,
+ PCI_BRIDGE_VENDOR_ATI,
+ PCI_BRIDGE_VENDOR_AMD,
+ PCI_BRIDGE_VENDOR_SIS ,
+ PCI_BRIDGE_VENDOR_UNKNOWN,
+ PCI_BRIDGE_VENDOR_MAX ,
+};
+
+struct buffer {
+ struct buffer *next;
+ u32 *buf;
+ dma_addr_t dma;
+
+};
+
+struct rtl_reg_debug {
+ unsigned int cmd;
+ struct {
+ unsigned char type;
+ unsigned char addr;
+ unsigned char page;
+ unsigned char length;
+ } head;
+ unsigned char buf[0xff];
+};
+
+struct rt_tx_rahis {
+ u32 cck[4];
+ u32 ofdm[8];
+ u32 ht_mcs[4][16];
+};
+
+struct rt_smooth_data_4rf {
+ char elements[4][100];
+ u32 index;
+ u32 TotalNum;
+ u32 TotalVal[4];
+};
+
+struct rt_stats {
+ unsigned long txrdu;
+ unsigned long rxrdu;
+ unsigned long rxok;
+ unsigned long rxframgment;
+ unsigned long rxurberr;
+ unsigned long rxstaterr;
+ unsigned long rxdatacrcerr;
+ unsigned long rxmgmtcrcerr;
+ unsigned long rxcrcerrmin;
+ unsigned long rxcrcerrmid;
+ unsigned long rxcrcerrmax;
+ unsigned long received_rate_histogram[4][32];
+ unsigned long received_preamble_GI[2][32];
+ unsigned long rx_AMPDUsize_histogram[5];
+ unsigned long rx_AMPDUnum_histogram[5];
+ unsigned long numpacket_matchbssid;
+ unsigned long numpacket_toself;
+ unsigned long num_process_phyinfo;
+ unsigned long numqry_phystatus;
+ unsigned long numqry_phystatusCCK;
+ unsigned long numqry_phystatusHT;
+ unsigned long received_bwtype[5];
+ unsigned long txnperr;
+ unsigned long txnpdrop;
+ unsigned long txresumed;
+ unsigned long rxoverflow;
+ unsigned long rxint;
+ unsigned long txnpokint;
+ unsigned long ints;
+ unsigned long shints;
+ unsigned long txoverflow;
+ unsigned long txlpokint;
+ unsigned long txlpdrop;
+ unsigned long txlperr;
+ unsigned long txbeokint;
+ unsigned long txbedrop;
+ unsigned long txbeerr;
+ unsigned long txbkokint;
+ unsigned long txbkdrop;
+ unsigned long txbkerr;
+ unsigned long txviokint;
+ unsigned long txvidrop;
+ unsigned long txvierr;
+ unsigned long txvookint;
+ unsigned long txvodrop;
+ unsigned long txvoerr;
+ unsigned long txbeaconokint;
+ unsigned long txbeacondrop;
+ unsigned long txbeaconerr;
+ unsigned long txmanageokint;
+ unsigned long txmanagedrop;
+ unsigned long txmanageerr;
+ unsigned long txcmdpktokint;
+ unsigned long txdatapkt;
+ unsigned long txfeedback;
+ unsigned long txfeedbackok;
+ unsigned long txoktotal;
+ unsigned long txokbytestotal;
+ unsigned long txokinperiod;
+ unsigned long txmulticast;
+ unsigned long txbytesmulticast;
+ unsigned long txbroadcast;
+ unsigned long txbytesbroadcast;
+ unsigned long txunicast;
+ unsigned long txbytesunicast;
+ unsigned long rxbytesunicast;
+ unsigned long txfeedbackfail;
+ unsigned long txerrtotal;
+ unsigned long txerrbytestotal;
+ unsigned long txerrmulticast;
+ unsigned long txerrbroadcast;
+ unsigned long txerrunicast;
+ unsigned long txretrycount;
+ unsigned long txfeedbackretry;
+ u8 last_packet_rate;
+ unsigned long slide_signal_strength[100];
+ unsigned long slide_evm[100];
+ unsigned long slide_rssi_total;
+ unsigned long slide_evm_total;
+ long signal_strength;
+ long signal_quality;
+ long last_signal_strength_inpercent;
+ long recv_signal_power;
+ u8 rx_rssi_percentage[4];
+ u8 rx_evm_percentage[2];
+ long rxSNRdB[4];
+ struct rt_tx_rahis txrate;
+ u32 Slide_Beacon_pwdb[100];
+ u32 Slide_Beacon_Total;
+ struct rt_smooth_data_4rf cck_adc_pwdb;
+ u32 CurrentShowTxate;
+};
+
+struct channel_access_setting {
+ u16 SIFS_Timer;
+ u16 DIFS_Timer;
+ u16 SlotTimeTimer;
+ u16 EIFS_Timer;
+ u16 CWminIndex;
+ u16 CWmaxIndex;
+};
+
+enum two_port_status {
+ TWO_PORT_STATUS__DEFAULT_ONLY,
+ TWO_PORT_STATUS__EXTENSION_ONLY,
+ TWO_PORT_STATUS__EXTENSION_FOLLOW_DEFAULT,
+ TWO_PORT_STATUS__DEFAULT_G_EXTENSION_N20,
+ TWO_PORT_STATUS__ADHOC,
+ TWO_PORT_STATUS__WITHOUT_ANY_ASSOCIATE
+};
+
+struct txbbgain_struct {
+ long txbb_iq_amplifygain;
+ u32 txbbgain_value;
+};
+
+struct ccktxbbgain {
+ u8 ccktxbb_valuearray[8];
+};
+
+struct init_gain {
+ u8 xaagccore1;
+ u8 xbagccore1;
+ u8 xcagccore1;
+ u8 xdagccore1;
+ u8 cca;
+
+};
+
+struct tx_ring {
+ u32 *desc;
+ u8 nStuckCount;
+ struct tx_ring *next;
+} __packed;
+
+struct rtl8192_tx_ring {
+ struct tx_desc *desc;
+ dma_addr_t dma;
+ unsigned int idx;
+ unsigned int entries;
+ struct sk_buff_head queue;
+};
+
+
+
+struct rtl819x_ops {
+ enum nic_t nic_type;
+ void (*get_eeprom_size)(struct net_device *dev);
+ void (*init_adapter_variable)(struct net_device *dev);
+ void (*init_before_adapter_start)(struct net_device *dev);
+ bool (*initialize_adapter)(struct net_device *dev);
+ void (*link_change)(struct net_device *dev);
+ void (*tx_fill_descriptor)(struct net_device *dev,
+ struct tx_desc *tx_desc,
+ struct cb_desc *cb_desc,
+ struct sk_buff *skb);
+ void (*tx_fill_cmd_descriptor)(struct net_device *dev,
+ struct tx_desc_cmd *entry,
+ struct cb_desc *cb_desc,
+ struct sk_buff *skb);
+ bool (*rx_query_status_descriptor)(struct net_device *dev,
+ struct rtllib_rx_stats *stats,
+ struct rx_desc *pdesc,
+ struct sk_buff *skb);
+ bool (*rx_command_packet_handler)(struct net_device *dev,
+ struct sk_buff *skb,
+ struct rx_desc *pdesc);
+ void (*stop_adapter)(struct net_device *dev, bool reset);
+ void (*update_ratr_table)(struct net_device *dev);
+ void (*irq_enable)(struct net_device *dev);
+ void (*irq_disable)(struct net_device *dev);
+ void (*irq_clear)(struct net_device *dev);
+ void (*rx_enable)(struct net_device *dev);
+ void (*tx_enable)(struct net_device *dev);
+ void (*interrupt_recognized)(struct net_device *dev,
+ u32 *p_inta, u32 *p_intb);
+ bool (*TxCheckStuckHandler)(struct net_device *dev);
+ bool (*RxCheckStuckHandler)(struct net_device *dev);
+};
+
+struct r8192_priv {
+ struct pci_dev *pdev;
+ struct pci_dev *bridge_pdev;
+
+ bool bfirst_init;
+ bool bfirst_after_down;
+ bool initialized_at_probe;
+ bool being_init_adapter;
+ bool bDriverIsGoingToUnload;
+
+ int irq;
+ short irq_enabled;
+
+ short up;
+ short up_first_time;
+ struct delayed_work update_beacon_wq;
+ struct delayed_work watch_dog_wq;
+ struct delayed_work txpower_tracking_wq;
+ struct delayed_work rfpath_check_wq;
+ struct delayed_work gpio_change_rf_wq;
+ struct delayed_work initialgain_operate_wq;
+ struct delayed_work check_hw_scan_wq;
+ struct delayed_work hw_scan_simu_wq;
+ struct delayed_work start_hw_scan_wq;
+
+ struct workqueue_struct *priv_wq;
+
+ struct channel_access_setting ChannelAccessSetting;
+
+ struct mp_adapter NdisAdapter;
+
+ struct rtl819x_ops *ops;
+ struct rtllib_device *rtllib;
+
+ struct work_struct reset_wq;
+
+ struct log_int_8190 InterruptLog;
+
+ enum rt_customer_id CustomerID;
+
+
+ enum rt_rf_type_819xu rf_chip;
+ enum ic_inferiority_8192s IC_Class;
+ enum ht_channel_width CurrentChannelBW;
+ struct bb_reg_definition PHYRegDef[4];
+ struct rate_adaptive rate_adaptive;
+
+ struct ccktxbbgain cck_txbbgain_table[CCKTxBBGainTableLength];
+ struct ccktxbbgain cck_txbbgain_ch14_table[CCKTxBBGainTableLength];
+
+ struct txbbgain_struct txbbgain_table[TxBBGainTableLength];
+
+ enum acm_method AcmMethod;
+
+ struct rt_firmware *pFirmware;
+ enum rtl819x_loopback LoopbackMode;
+
+ struct timer_list watch_dog_timer;
+ struct timer_list fsync_timer;
+ struct timer_list gpio_polling_timer;
+
+ spinlock_t fw_scan_lock;
+ spinlock_t irq_lock;
+ spinlock_t irq_th_lock;
+ spinlock_t tx_lock;
+ spinlock_t rf_ps_lock;
+ spinlock_t rw_lock;
+ spinlock_t rt_h2c_lock;
+ spinlock_t rf_lock;
+ spinlock_t ps_lock;
+
+ struct sk_buff_head rx_queue;
+ struct sk_buff_head skb_queue;
+
+ struct tasklet_struct irq_rx_tasklet;
+ struct tasklet_struct irq_tx_tasklet;
+ struct tasklet_struct irq_prepare_beacon_tasklet;
+
+ struct semaphore wx_sem;
+ struct semaphore rf_sem;
+ struct mutex mutex;
+
+ struct rt_stats stats;
+ struct iw_statistics wstats;
+ struct proc_dir_entry *dir_dev;
+
+ short (*rf_set_sens)(struct net_device *dev, short sens);
+ u8 (*rf_set_chan)(struct net_device *dev, u8 ch);
+ void (*rf_close)(struct net_device *dev);
+ void (*rf_init)(struct net_device *dev);
+
+ struct rx_desc *rx_ring[MAX_RX_QUEUE];
+ struct sk_buff *rx_buf[MAX_RX_QUEUE][MAX_RX_COUNT];
+ dma_addr_t rx_ring_dma[MAX_RX_QUEUE];
+ unsigned int rx_idx[MAX_RX_QUEUE];
+ int rxringcount;
+ u16 rxbuffersize;
+
+ u64 LastRxDescTSF;
+
+ u16 EarlyRxThreshold;
+ u32 ReceiveConfig;
+ u8 AcmControl;
+ u8 RFProgType;
+ u8 retry_data;
+ u8 retry_rts;
+ u16 rts;
+
+ struct rtl8192_tx_ring tx_ring[MAX_TX_QUEUE_COUNT];
+ int txringcount;
+ int txbuffsize;
+ int txfwbuffersize;
+ atomic_t tx_pending[0x10];
+
+ u16 ShortRetryLimit;
+ u16 LongRetryLimit;
+ u32 TransmitConfig;
+ u8 RegCWinMin;
+ u8 keepAliveLevel;
+
+ bool sw_radio_on;
+ bool bHwRadioOff;
+ bool pwrdown;
+ bool blinked_ingpio;
+ u8 polling_timer_on;
+
+ /**********************************************************/
+
+ enum card_type {
+ PCI, MINIPCI,
+ CARDBUS, USB
+ } card_type;
+
+ struct work_struct qos_activate;
+
+ u8 bIbssCoordinator;
+
+ short promisc;
+ short crcmon;
+
+ int txbeaconcount;
+
+ short chan;
+ short sens;
+ short max_sens;
+ u32 rx_prevlen;
+
+ u8 ScanDelay;
+ bool ps_force;
+
+ u32 irq_mask[2];
+
+ u8 Rf_Mode;
+ enum nic_t card_8192;
+ u8 card_8192_version;
+
+ short enable_gpio0;
+
+ u8 rf_type;
+ u8 IC_Cut;
+ char nick[IW_ESSID_MAX_SIZE + 1];
+
+ u8 RegBcnCtrlVal;
+ bool bHwAntDiv;
+
+ bool bTKIPinNmodeFromReg;
+ bool bWEPinNmodeFromReg;
+
+ bool bLedOpenDrain;
+
+ u8 check_roaming_cnt;
+
+ bool bIgnoreSilentReset;
+ u32 SilentResetRxSoltNum;
+ u32 SilentResetRxSlotIndex;
+ u32 SilentResetRxStuckEvent[MAX_SILENT_RESET_RX_SLOT_NUM];
+
+ void *scan_cmd;
+ u8 hwscan_bw_40;
+
+ u16 nrxAMPDU_size;
+ u8 nrxAMPDU_aggr_num;
+
+ u32 last_rxdesc_tsf_high;
+ u32 last_rxdesc_tsf_low;
+
+ u16 basic_rate;
+ u8 short_preamble;
+ u8 dot11CurrentPreambleMode;
+ u8 slot_time;
+ u16 SifsTime;
+
+ u8 RegWirelessMode;
+
+ u8 firmware_version;
+ u16 FirmwareSubVersion;
+ u16 rf_pathmap;
+ bool AutoloadFailFlag;
+
+ u8 RegPciASPM;
+ u8 RegAMDPciASPM;
+ u8 RegHwSwRfOffD3;
+ u8 RegSupportPciASPM;
+ bool bSupportASPM;
+
+ u32 RfRegChnlVal[2];
+
+ u8 ShowRateMode;
+ u8 RATRTableBitmap;
+
+ u8 EfuseMap[2][HWSET_MAX_SIZE_92S];
+ u16 EfuseUsedBytes;
+ u8 EfuseUsedPercentage;
+
+ short epromtype;
+ u16 eeprom_vid;
+ u16 eeprom_did;
+ u16 eeprom_svid;
+ u16 eeprom_smid;
+ u8 eeprom_CustomerID;
+ u16 eeprom_ChannelPlan;
+ u8 eeprom_version;
+
+ u8 EEPROMRegulatory;
+ u8 EEPROMPwrGroup[2][3];
+ u8 EEPROMOptional;
+
+ u8 EEPROMTxPowerLevelCCK[14];
+ u8 EEPROMTxPowerLevelOFDM24G[14];
+ u8 EEPROMTxPowerLevelOFDM5G[24];
+ u8 EEPROMRfACCKChnl1TxPwLevel[3];
+ u8 EEPROMRfAOfdmChnlTxPwLevel[3];
+ u8 EEPROMRfCCCKChnl1TxPwLevel[3];
+ u8 EEPROMRfCOfdmChnlTxPwLevel[3];
+ u16 EEPROMTxPowerDiff;
+ u16 EEPROMAntPwDiff;
+ u8 EEPROMThermalMeter;
+ u8 EEPROMPwDiff;
+ u8 EEPROMCrystalCap;
+
+ u8 EEPROMBluetoothCoexist;
+ u8 EEPROMBluetoothType;
+ u8 EEPROMBluetoothAntNum;
+ u8 EEPROMBluetoothAntIsolation;
+ u8 EEPROMBluetoothRadioShared;
+
+
+ u8 EEPROMSupportWoWLAN;
+ u8 EEPROMBoardType;
+ u8 EEPROM_Def_Ver;
+ u8 EEPROMHT2T_TxPwr[6];
+ u8 EEPROMTSSI_A;
+ u8 EEPROMTSSI_B;
+ u8 EEPROMTxPowerLevelCCK_V1[3];
+ u8 EEPROMLegacyHTTxPowerDiff;
+
+ u8 BluetoothCoexist;
+
+ u8 CrystalCap;
+ u8 ThermalMeter[2];
+
+ u16 FwCmdIOMap;
+ u32 FwCmdIOParam;
+
+ u8 SwChnlInProgress;
+ u8 SwChnlStage;
+ u8 SwChnlStep;
+ u8 SetBWModeInProgress;
+
+ u8 nCur40MhzPrimeSC;
+
+ u32 RfReg0Value[4];
+ u8 NumTotalRFPath;
+ bool brfpath_rxenable[4];
+
+ bool bTXPowerDataReadFromEEPORM;
+
+ u16 RegChannelPlan;
+ u16 ChannelPlan;
+ bool bChnlPlanFromHW;
+
+ bool RegRfOff;
+ bool isRFOff;
+ bool bInPowerSaveMode;
+ u8 bHwRfOffAction;
+
+ bool aspm_clkreq_enable;
+ u32 pci_bridge_vendor;
+ u8 RegHostPciASPMSetting;
+ u8 RegDevicePciASPMSetting;
+
+ bool RFChangeInProgress;
+ bool SetRFPowerStateInProgress;
+ bool bdisable_nic;
+
+ u8 pwrGroupCnt;
+
+ u8 ThermalValue_LCK;
+ u8 ThermalValue_IQK;
+ bool bRfPiEnable;
+
+ u32 APKoutput[2][2];
+ bool bAPKdone;
+
+ long RegE94;
+ long RegE9C;
+ long RegEB4;
+ long RegEBC;
+
+ u32 RegC04;
+ u32 Reg874;
+ u32 RegC08;
+ u32 ADDA_backup[16];
+ u32 IQK_MAC_backup[3];
+
+ bool SetFwCmdInProgress;
+ u8 CurrentFwCmdIO;
+
+ u8 rssi_level;
+
+ bool bInformFWDriverControlDM;
+ u8 PwrGroupHT20[2][14];
+ u8 PwrGroupHT40[2][14];
+
+ u8 ThermalValue;
+ long EntryMinUndecoratedSmoothedPWDB;
+ long EntryMaxUndecoratedSmoothedPWDB;
+ u8 DynamicTxHighPowerLvl;
+ u8 LastDTPLvl;
+ u32 CurrentRATR0;
+ struct false_alarm_stats FalseAlmCnt;
+
+ u8 DMFlag;
+ u8 DM_Type;
+
+ u8 CckPwEnl;
+ u16 TSSI_13dBm;
+ u32 Pwr_Track;
+ u8 CCKPresentAttentuation_20Mdefault;
+ u8 CCKPresentAttentuation_40Mdefault;
+ char CCKPresentAttentuation_difference;
+ char CCKPresentAttentuation;
+ u8 bCckHighPower;
+ long undecorated_smoothed_pwdb;
+ long undecorated_smoothed_cck_adc_pwdb[4];
+
+ u32 MCSTxPowerLevelOriginalOffset[6];
+ u32 CCKTxPowerLevelOriginalOffset;
+ u8 TxPowerLevelCCK[14];
+ u8 TxPowerLevelCCK_A[14];
+ u8 TxPowerLevelCCK_C[14];
+ u8 TxPowerLevelOFDM24G[14];
+ u8 TxPowerLevelOFDM5G[14];
+ u8 TxPowerLevelOFDM24G_A[14];
+ u8 TxPowerLevelOFDM24G_C[14];
+ u8 LegacyHTTxPowerDiff;
+ u8 TxPowerDiff;
+ s8 RF_C_TxPwDiff;
+ s8 RF_B_TxPwDiff;
+ u8 RfTxPwrLevelCck[2][14];
+ u8 RfTxPwrLevelOfdm1T[2][14];
+ u8 RfTxPwrLevelOfdm2T[2][14];
+ u8 AntennaTxPwDiff[3];
+ u8 TxPwrHt20Diff[2][14];
+ u8 TxPwrLegacyHtDiff[2][14];
+ u8 TxPwrSafetyFlag;
+ u8 HT2T_TxPwr_A[14];
+ u8 HT2T_TxPwr_B[14];
+ u8 CurrentCckTxPwrIdx;
+ u8 CurrentOfdm24GTxPwrIdx;
+
+ bool bdynamic_txpower;
+ bool bDynamicTxHighPower;
+ bool bDynamicTxLowPower;
+ bool bLastDTPFlag_High;
+ bool bLastDTPFlag_Low;
+
+ bool bstore_last_dtpflag;
+ bool bstart_txctrl_bydtp;
+
+ u8 rfa_txpowertrackingindex;
+ u8 rfa_txpowertrackingindex_real;
+ u8 rfa_txpowertracking_default;
+ u8 rfc_txpowertrackingindex;
+ u8 rfc_txpowertrackingindex_real;
+ u8 rfc_txpowertracking_default;
+ bool btxpower_tracking;
+ bool bcck_in_ch14;
+
+ u8 TxPowerTrackControl;
+ u8 txpower_count;
+ bool btxpower_trackingInit;
+
+ u8 OFDM_index[2];
+ u8 CCK_index;
+
+ u8 Record_CCK_20Mindex;
+ u8 Record_CCK_40Mindex;
+
+ struct init_gain initgain_backup;
+ u8 DefaultInitialGain[4];
+ bool bis_any_nonbepkts;
+ bool bcurrent_turbo_EDCA;
+ bool bis_cur_rdlstate;
+
+ bool bCCKinCH14;
+
+ u8 MidHighPwrTHR_L1;
+ u8 MidHighPwrTHR_L2;
+
+ bool bfsync_processing;
+ u32 rate_record;
+ u32 rateCountDiffRecord;
+ u32 ContinueDiffCount;
+ bool bswitch_fsync;
+ u8 framesync;
+ u32 framesyncC34;
+ u8 framesyncMonitor;
+
+ bool bDMInitialGainEnable;
+ bool MutualAuthenticationFail;
+
+ bool bDisableFrameBursting;
+
+ u32 reset_count;
+ bool bpbc_pressed;
+
+ u32 txpower_checkcnt;
+ u32 txpower_tracking_callback_cnt;
+ u8 thermal_read_val[40];
+ u8 thermal_readback_index;
+ u32 ccktxpower_adjustcnt_not_ch14;
+ u32 ccktxpower_adjustcnt_ch14;
+
+ enum reset_type ResetProgress;
+ bool bForcedSilentReset;
+ bool bDisableNormalResetCheck;
+ u16 TxCounter;
+ u16 RxCounter;
+ int IrpPendingCount;
+ bool bResetInProgress;
+ bool force_reset;
+ bool force_lps;
+ u8 InitialGainOperateType;
+
+ bool chan_forced;
+ bool bSingleCarrier;
+ bool RegBoard;
+ bool bCckContTx;
+ bool bOfdmContTx;
+ bool bStartContTx;
+ u8 RegPaModel;
+ u8 btMpCckTxPower;
+ u8 btMpOfdmTxPower;
+
+ u32 MptActType;
+ u32 MptIoOffset;
+ u32 MptIoValue;
+ u32 MptRfPath;
+
+ u32 MptBandWidth;
+ u32 MptRateIndex;
+ u8 MptChannelToSw;
+ u32 MptRCR;
+
+ u8 PwrDomainProtect;
+ u8 H2CTxCmdSeq;
+
+
+};
+
+extern const struct ethtool_ops rtl819x_ethtool_ops;
+
+void rtl8192_tx_cmd(struct net_device *dev, struct sk_buff *skb);
+short rtl8192_tx(struct net_device *dev, struct sk_buff *skb);
+
+u8 read_nic_io_byte(struct net_device *dev, int x);
+u32 read_nic_io_dword(struct net_device *dev, int x);
+u16 read_nic_io_word(struct net_device *dev, int x);
+void write_nic_io_byte(struct net_device *dev, int x, u8 y);
+void write_nic_io_word(struct net_device *dev, int x, u16 y);
+void write_nic_io_dword(struct net_device *dev, int x, u32 y);
+
+u8 read_nic_byte(struct net_device *dev, int x);
+u32 read_nic_dword(struct net_device *dev, int x);
+u16 read_nic_word(struct net_device *dev, int x);
+void write_nic_byte(struct net_device *dev, int x, u8 y);
+void write_nic_word(struct net_device *dev, int x, u16 y);
+void write_nic_dword(struct net_device *dev, int x, u32 y);
+
+void force_pci_posting(struct net_device *dev);
+
+void rtl8192_rx_enable(struct net_device *);
+void rtl8192_tx_enable(struct net_device *);
+
+int rtl8192_hard_start_xmit(struct sk_buff *skb, struct net_device *dev);
+void rtl8192_hard_data_xmit(struct sk_buff *skb, struct net_device *dev,
+ int rate);
+void rtl8192_data_hard_stop(struct net_device *dev);
+void rtl8192_data_hard_resume(struct net_device *dev);
+void rtl8192_restart(void *data);
+void rtl819x_watchdog_wqcallback(void *data);
+void rtl8192_hw_sleep_wq(void *data);
+void watch_dog_timer_callback(unsigned long data);
+void rtl8192_irq_rx_tasklet(struct r8192_priv *priv);
+void rtl8192_irq_tx_tasklet(struct r8192_priv *priv);
+int rtl8192_down(struct net_device *dev, bool shutdownrf);
+int rtl8192_up(struct net_device *dev);
+void rtl8192_commit(struct net_device *dev);
+void rtl8192_set_chan(struct net_device *dev, short ch);
+
+void check_rfctrl_gpio_timer(unsigned long data);
+
+void rtl8192_hw_wakeup_wq(void *data);
+short rtl8192_pci_initdescring(struct net_device *dev);
+
+void rtl8192_cancel_deferred_work(struct r8192_priv *priv);
+
+int _rtl8192_up(struct net_device *dev, bool is_silent_reset);
+
+short rtl8192_is_tx_queue_empty(struct net_device *dev);
+void rtl8192_irq_disable(struct net_device *dev);
+
+void rtl8192_tx_timeout(struct net_device *dev);
+void rtl8192_pci_resetdescring(struct net_device *dev);
+void rtl8192_SetWirelessMode(struct net_device *dev, u8 wireless_mode);
+void rtl8192_irq_enable(struct net_device *dev);
+void rtl8192_config_rate(struct net_device *dev, u16 *rate_config);
+void rtl8192_update_cap(struct net_device *dev, u16 cap);
+void rtl8192_irq_disable(struct net_device *dev);
+
+void rtl819x_UpdateRxPktTimeStamp(struct net_device *dev,
+ struct rtllib_rx_stats *stats);
+long rtl819x_translate_todbm(struct r8192_priv *priv, u8 signal_strength_index);
+void rtl819x_update_rxsignalstatistics8190pci(struct r8192_priv *priv,
+ struct rtllib_rx_stats *pprevious_stats);
+u8 rtl819x_evm_dbtopercentage(char value);
+void rtl819x_process_cck_rxpathsel(struct r8192_priv *priv,
+ struct rtllib_rx_stats *pprevious_stats);
+u8 rtl819x_query_rxpwrpercentage(char antpower);
+void rtl8192_record_rxdesc_forlateruse(struct rtllib_rx_stats *psrc_stats,
+ struct rtllib_rx_stats *ptarget_stats);
+bool NicIFEnableNIC(struct net_device *dev);
+bool NicIFDisableNIC(struct net_device *dev);
+
+bool MgntActSet_RF_State(struct net_device *dev,
+ enum rt_rf_power_state StateToSet,
+ RT_RF_CHANGE_SOURCE ChangeSource,
+ bool ProtectOrNot);
+void ActUpdateChannelAccessSetting(struct net_device *dev,
+ enum wireless_mode WirelessMode,
+ struct channel_access_setting *ChnlAccessSetting);
+
+#endif
diff --git a/drivers/staging/rtl8192e/rtl8192e/rtl_crypto.h b/drivers/staging/rtl8192e/rtl8192e/rtl_crypto.h
new file mode 100644
index 000000000..ee57c0f4f
--- /dev/null
+++ b/drivers/staging/rtl8192e/rtl8192e/rtl_crypto.h
@@ -0,0 +1,382 @@
+/*
+ * Scatterlist Cryptographic API.
+ *
+ * Copyright (c) 2002 James Morris <jmorris@intercode.com.au>
+ * Copyright (c) 2002 David S. Miller (davem@redhat.com)
+ *
+ * Portions derived from Cryptoapi, by Alexander Kjeldaas <astor@fast.no>
+ * and Nettle, by Niels Mé°ˆler.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ */
+#ifndef _LINUX_CRYPTO_H
+#define _LINUX_CRYPTO_H
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/list.h>
+#include <linux/string.h>
+#include <asm/page.h>
+#include <linux/errno.h>
+
+#define crypto_register_alg crypto_register_alg_rsl
+#define crypto_unregister_alg crypto_unregister_alg_rsl
+#define crypto_alloc_tfm crypto_alloc_tfm_rsl
+#define crypto_free_tfm crypto_free_tfm_rsl
+#define crypto_alg_available crypto_alg_available_rsl
+
+/*
+ * Algorithm masks and types.
+ */
+#define CRYPTO_ALG_TYPE_MASK 0x000000ff
+#define CRYPTO_ALG_TYPE_CIPHER 0x00000001
+#define CRYPTO_ALG_TYPE_DIGEST 0x00000002
+#define CRYPTO_ALG_TYPE_COMPRESS 0x00000004
+
+/*
+ * Transform masks and values (for crt_flags).
+ */
+#define CRYPTO_TFM_MODE_MASK 0x000000ff
+#define CRYPTO_TFM_REQ_MASK 0x000fff00
+#define CRYPTO_TFM_RES_MASK 0xfff00000
+
+#define CRYPTO_TFM_MODE_ECB 0x00000001
+#define CRYPTO_TFM_MODE_CBC 0x00000002
+#define CRYPTO_TFM_MODE_CFB 0x00000004
+#define CRYPTO_TFM_MODE_CTR 0x00000008
+
+#define CRYPTO_TFM_REQ_WEAK_KEY 0x00000100
+#define CRYPTO_TFM_RES_WEAK_KEY 0x00100000
+#define CRYPTO_TFM_RES_BAD_KEY_LEN 0x00200000
+#define CRYPTO_TFM_RES_BAD_KEY_SCHED 0x00400000
+#define CRYPTO_TFM_RES_BAD_BLOCK_LEN 0x00800000
+#define CRYPTO_TFM_RES_BAD_FLAGS 0x01000000
+
+/*
+ * Miscellaneous stuff.
+ */
+#define CRYPTO_UNSPEC 0
+#define CRYPTO_MAX_ALG_NAME 64
+
+struct scatterlist;
+
+/*
+ * Algorithms: modular crypto algorithm implementations, managed
+ * via crypto_register_alg() and crypto_unregister_alg().
+ */
+struct cipher_alg {
+ unsigned int cia_min_keysize;
+ unsigned int cia_max_keysize;
+ int (*cia_setkey)(void *ctx, const u8 *key,
+ unsigned int keylen, u32 *flags);
+ void (*cia_encrypt)(void *ctx, u8 *dst, const u8 *src);
+ void (*cia_decrypt)(void *ctx, u8 *dst, const u8 *src);
+};
+
+struct digest_alg {
+ unsigned int dia_digestsize;
+ void (*dia_init)(void *ctx);
+ void (*dia_update)(void *ctx, const u8 *data, unsigned int len);
+ void (*dia_final)(void *ctx, u8 *out);
+ int (*dia_setkey)(void *ctx, const u8 *key,
+ unsigned int keylen, u32 *flags);
+};
+
+struct compress_alg {
+ int (*coa_init)(void *ctx);
+ void (*coa_exit)(void *ctx);
+ int (*coa_compress)(void *ctx, const u8 *src, unsigned int slen,
+ u8 *dst, unsigned int *dlen);
+ int (*coa_decompress)(void *ctx, const u8 *src, unsigned int slen,
+ u8 *dst, unsigned int *dlen);
+};
+
+#define cra_cipher cra_u.cipher
+#define cra_digest cra_u.digest
+#define cra_compress cra_u.compress
+
+struct crypto_alg {
+ struct list_head cra_list;
+ u32 cra_flags;
+ unsigned int cra_blocksize;
+ unsigned int cra_ctxsize;
+ const char cra_name[CRYPTO_MAX_ALG_NAME];
+
+ union {
+ struct cipher_alg cipher;
+ struct digest_alg digest;
+ struct compress_alg compress;
+ } cra_u;
+
+ struct module *cra_module;
+};
+
+/*
+ * Algorithm registration interface.
+ */
+int crypto_register_alg(struct crypto_alg *alg);
+int crypto_unregister_alg(struct crypto_alg *alg);
+
+/*
+ * Algorithm query interface.
+ */
+int crypto_alg_available(const char *name, u32 flags);
+
+/*
+ * Transforms: user-instantiated objects which encapsulate algorithms
+ * and core processing logic. Managed via crypto_alloc_tfm() and
+ * crypto_free_tfm(), as well as the various helpers below.
+ */
+struct crypto_tfm;
+
+struct cipher_tfm {
+ void *cit_iv;
+ unsigned int cit_ivsize;
+ u32 cit_mode;
+ int (*cit_setkey)(struct crypto_tfm *tfm,
+ const u8 *key, unsigned int keylen);
+ int (*cit_encrypt)(struct crypto_tfm *tfm,
+ struct scatterlist *dst,
+ struct scatterlist *src,
+ unsigned int nbytes);
+ int (*cit_encrypt_iv)(struct crypto_tfm *tfm,
+ struct scatterlist *dst,
+ struct scatterlist *src,
+ unsigned int nbytes, u8 *iv);
+ int (*cit_decrypt)(struct crypto_tfm *tfm,
+ struct scatterlist *dst,
+ struct scatterlist *src,
+ unsigned int nbytes);
+ int (*cit_decrypt_iv)(struct crypto_tfm *tfm,
+ struct scatterlist *dst,
+ struct scatterlist *src,
+ unsigned int nbytes, u8 *iv);
+ void (*cit_xor_block)(u8 *dst, const u8 *src);
+};
+
+struct digest_tfm {
+ void (*dit_init)(struct crypto_tfm *tfm);
+ void (*dit_update)(struct crypto_tfm *tfm,
+ struct scatterlist *sg, unsigned int nsg);
+ void (*dit_final)(struct crypto_tfm *tfm, u8 *out);
+ void (*dit_digest)(struct crypto_tfm *tfm, struct scatterlist *sg,
+ unsigned int nsg, u8 *out);
+ int (*dit_setkey)(struct crypto_tfm *tfm,
+ const u8 *key, unsigned int keylen);
+};
+
+struct compress_tfm {
+ int (*cot_compress)(struct crypto_tfm *tfm,
+ const u8 *src, unsigned int slen,
+ u8 *dst, unsigned int *dlen);
+ int (*cot_decompress)(struct crypto_tfm *tfm,
+ const u8 *src, unsigned int slen,
+ u8 *dst, unsigned int *dlen);
+};
+
+#define crt_cipher crt_u.cipher
+#define crt_digest crt_u.digest
+#define crt_compress crt_u.compress
+
+struct crypto_tfm {
+
+ u32 crt_flags;
+
+ union {
+ struct cipher_tfm cipher;
+ struct digest_tfm digest;
+ struct compress_tfm compress;
+ } crt_u;
+
+ struct crypto_alg *__crt_alg;
+};
+
+/*
+ * Transform user interface.
+ */
+
+/*
+ * crypto_alloc_tfm() will first attempt to locate an already loaded algorithm.
+ * If that fails and the kernel supports dynamically loadable modules, it
+ * will then attempt to load a module of the same name or alias. A refcount
+ * is grabbed on the algorithm which is then associated with the new transform.
+ *
+ * crypto_free_tfm() frees up the transform and any associated resources,
+ * then drops the refcount on the associated algorithm.
+ */
+struct crypto_tfm *crypto_alloc_tfm(const char *alg_name, u32 tfm_flags);
+void crypto_free_tfm(struct crypto_tfm *tfm);
+
+/*
+ * Transform helpers which query the underlying algorithm.
+ */
+static inline const char *crypto_tfm_alg_name(struct crypto_tfm *tfm)
+{
+ return tfm->__crt_alg->cra_name;
+}
+
+static inline const char *crypto_tfm_alg_modname(struct crypto_tfm *tfm)
+{
+ struct crypto_alg *alg = tfm->__crt_alg;
+
+ if (alg->cra_module)
+ return alg->cra_module->name;
+ else
+ return NULL;
+}
+
+static inline u32 crypto_tfm_alg_type(struct crypto_tfm *tfm)
+{
+ return tfm->__crt_alg->cra_flags & CRYPTO_ALG_TYPE_MASK;
+}
+
+static inline unsigned int crypto_tfm_alg_min_keysize(struct crypto_tfm *tfm)
+{
+ BUG_ON(crypto_tfm_alg_type(tfm) != CRYPTO_ALG_TYPE_CIPHER);
+ return tfm->__crt_alg->cra_cipher.cia_min_keysize;
+}
+
+static inline unsigned int crypto_tfm_alg_max_keysize(struct crypto_tfm *tfm)
+{
+ BUG_ON(crypto_tfm_alg_type(tfm) != CRYPTO_ALG_TYPE_CIPHER);
+ return tfm->__crt_alg->cra_cipher.cia_max_keysize;
+}
+
+static inline unsigned int crypto_tfm_alg_ivsize(struct crypto_tfm *tfm)
+{
+ BUG_ON(crypto_tfm_alg_type(tfm) != CRYPTO_ALG_TYPE_CIPHER);
+ return tfm->crt_cipher.cit_ivsize;
+}
+
+static inline unsigned int crypto_tfm_alg_blocksize(struct crypto_tfm *tfm)
+{
+ return tfm->__crt_alg->cra_blocksize;
+}
+
+static inline unsigned int crypto_tfm_alg_digestsize(struct crypto_tfm *tfm)
+{
+ BUG_ON(crypto_tfm_alg_type(tfm) != CRYPTO_ALG_TYPE_DIGEST);
+ return tfm->__crt_alg->cra_digest.dia_digestsize;
+}
+
+/*
+ * API wrappers.
+ */
+static inline void crypto_digest_init(struct crypto_tfm *tfm)
+{
+ BUG_ON(crypto_tfm_alg_type(tfm) != CRYPTO_ALG_TYPE_DIGEST);
+ tfm->crt_digest.dit_init(tfm);
+}
+
+static inline void crypto_digest_update(struct crypto_tfm *tfm,
+ struct scatterlist *sg,
+ unsigned int nsg)
+{
+ BUG_ON(crypto_tfm_alg_type(tfm) != CRYPTO_ALG_TYPE_DIGEST);
+ tfm->crt_digest.dit_update(tfm, sg, nsg);
+}
+
+static inline void crypto_digest_final(struct crypto_tfm *tfm, u8 *out)
+{
+ BUG_ON(crypto_tfm_alg_type(tfm) != CRYPTO_ALG_TYPE_DIGEST);
+ tfm->crt_digest.dit_final(tfm, out);
+}
+
+static inline void crypto_digest_digest(struct crypto_tfm *tfm,
+ struct scatterlist *sg,
+ unsigned int nsg, u8 *out)
+{
+ BUG_ON(crypto_tfm_alg_type(tfm) != CRYPTO_ALG_TYPE_DIGEST);
+ tfm->crt_digest.dit_digest(tfm, sg, nsg, out);
+}
+
+static inline int crypto_digest_setkey(struct crypto_tfm *tfm,
+ const u8 *key, unsigned int keylen)
+{
+ BUG_ON(crypto_tfm_alg_type(tfm) != CRYPTO_ALG_TYPE_DIGEST);
+ if (tfm->crt_digest.dit_setkey == NULL)
+ return -ENOSYS;
+ return tfm->crt_digest.dit_setkey(tfm, key, keylen);
+}
+
+static inline int crypto_cipher_setkey(struct crypto_tfm *tfm,
+ const u8 *key, unsigned int keylen)
+{
+ BUG_ON(crypto_tfm_alg_type(tfm) != CRYPTO_ALG_TYPE_CIPHER);
+ return tfm->crt_cipher.cit_setkey(tfm, key, keylen);
+}
+
+static inline int crypto_cipher_encrypt(struct crypto_tfm *tfm,
+ struct scatterlist *dst,
+ struct scatterlist *src,
+ unsigned int nbytes)
+{
+ BUG_ON(crypto_tfm_alg_type(tfm) != CRYPTO_ALG_TYPE_CIPHER);
+ return tfm->crt_cipher.cit_encrypt(tfm, dst, src, nbytes);
+}
+
+static inline int crypto_cipher_encrypt_iv(struct crypto_tfm *tfm,
+ struct scatterlist *dst,
+ struct scatterlist *src,
+ unsigned int nbytes, u8 *iv)
+{
+ BUG_ON(crypto_tfm_alg_type(tfm) != CRYPTO_ALG_TYPE_CIPHER);
+ BUG_ON(tfm->crt_cipher.cit_mode == CRYPTO_TFM_MODE_ECB);
+ return tfm->crt_cipher.cit_encrypt_iv(tfm, dst, src, nbytes, iv);
+}
+
+static inline int crypto_cipher_decrypt(struct crypto_tfm *tfm,
+ struct scatterlist *dst,
+ struct scatterlist *src,
+ unsigned int nbytes)
+{
+ BUG_ON(crypto_tfm_alg_type(tfm) != CRYPTO_ALG_TYPE_CIPHER);
+ return tfm->crt_cipher.cit_decrypt(tfm, dst, src, nbytes);
+}
+
+static inline int crypto_cipher_decrypt_iv(struct crypto_tfm *tfm,
+ struct scatterlist *dst,
+ struct scatterlist *src,
+ unsigned int nbytes, u8 *iv)
+{
+ BUG_ON(crypto_tfm_alg_type(tfm) != CRYPTO_ALG_TYPE_CIPHER);
+ BUG_ON(tfm->crt_cipher.cit_mode == CRYPTO_TFM_MODE_ECB);
+ return tfm->crt_cipher.cit_decrypt_iv(tfm, dst, src, nbytes, iv);
+}
+
+static inline void crypto_cipher_set_iv(struct crypto_tfm *tfm,
+ const u8 *src, unsigned int len)
+{
+ BUG_ON(crypto_tfm_alg_type(tfm) != CRYPTO_ALG_TYPE_CIPHER);
+ memcpy(tfm->crt_cipher.cit_iv, src, len);
+}
+
+static inline void crypto_cipher_get_iv(struct crypto_tfm *tfm,
+ u8 *dst, unsigned int len)
+{
+ BUG_ON(crypto_tfm_alg_type(tfm) != CRYPTO_ALG_TYPE_CIPHER);
+ memcpy(dst, tfm->crt_cipher.cit_iv, len);
+}
+
+static inline int crypto_comp_compress(struct crypto_tfm *tfm,
+ const u8 *src, unsigned int slen,
+ u8 *dst, unsigned int *dlen)
+{
+ BUG_ON(crypto_tfm_alg_type(tfm) != CRYPTO_ALG_TYPE_COMPRESS);
+ return tfm->crt_compress.cot_compress(tfm, src, slen, dst, dlen);
+}
+
+static inline int crypto_comp_decompress(struct crypto_tfm *tfm,
+ const u8 *src, unsigned int slen,
+ u8 *dst, unsigned int *dlen)
+{
+ BUG_ON(crypto_tfm_alg_type(tfm) != CRYPTO_ALG_TYPE_COMPRESS);
+ return tfm->crt_compress.cot_decompress(tfm, src, slen, dst, dlen);
+}
+
+#endif /* _LINUX_CRYPTO_H */
diff --git a/drivers/staging/rtl8192e/rtl8192e/rtl_dm.c b/drivers/staging/rtl8192e/rtl8192e/rtl_dm.c
new file mode 100644
index 000000000..df4bbcf38
--- /dev/null
+++ b/drivers/staging/rtl8192e/rtl8192e/rtl_dm.c
@@ -0,0 +1,2995 @@
+/******************************************************************************
+ * Copyright(c) 2008 - 2010 Realtek Corporation. All rights reserved.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ * The full GNU General Public License is included in this distribution in the
+ * file called LICENSE.
+ *
+ * Contact Information:
+ * wlanfae <wlanfae@realtek.com>
+******************************************************************************/
+#include "rtl_core.h"
+#include "rtl_dm.h"
+#include "r8192E_hw.h"
+#include "r8192E_phy.h"
+#include "r8192E_phyreg.h"
+#include "r8190P_rtl8256.h"
+#include "r8192E_cmdpkt.h"
+
+/*---------------------------Define Local Constant---------------------------*/
+static u32 edca_setting_DL[HT_IOT_PEER_MAX] = {
+ 0x5e4322,
+ 0x5e4322,
+ 0x5ea44f,
+ 0x5e4322,
+ 0x604322,
+ 0xa44f,
+ 0x5e4322,
+ 0x5e4332
+};
+
+static u32 edca_setting_DL_GMode[HT_IOT_PEER_MAX] = {
+ 0x5e4322,
+ 0x5e4322,
+ 0x5e4322,
+ 0x5e4322,
+ 0x604322,
+ 0xa44f,
+ 0x5e4322,
+ 0x5e4322
+};
+
+static u32 edca_setting_UL[HT_IOT_PEER_MAX] = {
+ 0x5e4322,
+ 0xa44f,
+ 0x5ea44f,
+ 0x5e4322,
+ 0x604322,
+ 0x5e4322,
+ 0x5e4322,
+ 0x5e4332
+};
+
+#define RTK_UL_EDCA 0xa44f
+#define RTK_DL_EDCA 0x5e4322
+/*---------------------------Define Local Constant---------------------------*/
+
+
+/*------------------------Define global variable-----------------------------*/
+struct dig_t dm_digtable;
+u8 dm_shadow[16][256] = {
+ {0}
+};
+
+struct drx_path_sel DM_RxPathSelTable;
+/*------------------------Define global variable-----------------------------*/
+
+
+/*------------------------Define local variable------------------------------*/
+/*------------------------Define local variable------------------------------*/
+
+
+
+/*---------------------Define local function prototype-----------------------*/
+static void dm_check_rate_adaptive(struct net_device *dev);
+
+static void dm_init_bandwidth_autoswitch(struct net_device *dev);
+static void dm_bandwidth_autoswitch(struct net_device *dev);
+
+
+static void dm_check_txpower_tracking(struct net_device *dev);
+
+
+
+
+
+static void dm_bb_initialgain_restore(struct net_device *dev);
+
+
+static void dm_bb_initialgain_backup(struct net_device *dev);
+
+static void dm_dig_init(struct net_device *dev);
+static void dm_ctrl_initgain_byrssi(struct net_device *dev);
+static void dm_ctrl_initgain_byrssi_highpwr(struct net_device *dev);
+static void dm_ctrl_initgain_byrssi_by_driverrssi(struct net_device *dev);
+static void dm_ctrl_initgain_byrssi_by_fwfalse_alarm(struct net_device *dev);
+static void dm_initial_gain(struct net_device *dev);
+static void dm_pd_th(struct net_device *dev);
+static void dm_cs_ratio(struct net_device *dev);
+
+static void dm_init_ctstoself(struct net_device *dev);
+static void dm_Init_WA_Broadcom_IOT(struct net_device *dev);
+
+static void dm_check_edca_turbo(struct net_device *dev);
+
+static void dm_check_pbc_gpio(struct net_device *dev);
+
+
+static void dm_check_rx_path_selection(struct net_device *dev);
+static void dm_init_rxpath_selection(struct net_device *dev);
+static void dm_rxpath_sel_byrssi(struct net_device *dev);
+
+
+static void dm_init_fsync(struct net_device *dev);
+static void dm_deInit_fsync(struct net_device *dev);
+
+static void dm_check_txrateandretrycount(struct net_device *dev);
+static void dm_check_ac_dc_power(struct net_device *dev);
+
+/*---------------------Define local function prototype-----------------------*/
+
+static void dm_init_dynamic_txpower(struct net_device *dev);
+static void dm_dynamic_txpower(struct net_device *dev);
+
+
+static void dm_send_rssi_tofw(struct net_device *dev);
+static void dm_ctstoself(struct net_device *dev);
+/*---------------------------Define function prototype------------------------*/
+
+void init_hal_dm(struct net_device *dev)
+{
+ struct r8192_priv *priv = rtllib_priv(dev);
+
+ priv->DM_Type = DM_Type_ByDriver;
+
+ priv->undecorated_smoothed_pwdb = -1;
+
+ dm_init_dynamic_txpower(dev);
+
+ init_rate_adaptive(dev);
+
+ dm_dig_init(dev);
+ dm_init_edca_turbo(dev);
+ dm_init_bandwidth_autoswitch(dev);
+ dm_init_fsync(dev);
+ dm_init_rxpath_selection(dev);
+ dm_init_ctstoself(dev);
+ if (IS_HARDWARE_TYPE_8192SE(dev))
+ dm_Init_WA_Broadcom_IOT(dev);
+
+ INIT_DELAYED_WORK_RSL(&priv->gpio_change_rf_wq, (void *)dm_CheckRfCtrlGPIO, dev);
+}
+
+void deinit_hal_dm(struct net_device *dev)
+{
+
+ dm_deInit_fsync(dev);
+
+}
+
+void hal_dm_watchdog(struct net_device *dev)
+{
+ struct r8192_priv *priv = rtllib_priv(dev);
+
+ if (priv->being_init_adapter)
+ return;
+
+ dm_check_ac_dc_power(dev);
+
+ dm_check_pbc_gpio(dev);
+ dm_check_txrateandretrycount(dev);
+ dm_check_edca_turbo(dev);
+
+ dm_check_rate_adaptive(dev);
+ dm_dynamic_txpower(dev);
+ dm_check_txpower_tracking(dev);
+
+ dm_ctrl_initgain_byrssi(dev);
+ dm_bandwidth_autoswitch(dev);
+
+ dm_check_rx_path_selection(dev);
+ dm_check_fsync(dev);
+
+ dm_send_rssi_tofw(dev);
+ dm_ctstoself(dev);
+}
+
+static void dm_check_ac_dc_power(struct net_device *dev)
+{
+ struct r8192_priv *priv = rtllib_priv(dev);
+ static char *ac_dc_check_script_path = "/etc/acpi/wireless-rtl-ac-dc-power.sh";
+ char *argv[] = {ac_dc_check_script_path, DRV_NAME, NULL};
+ static char *envp[] = {"HOME=/",
+ "TERM=linux",
+ "PATH=/usr/bin:/bin",
+ NULL};
+
+ if (priv->ResetProgress == RESET_TYPE_SILENT) {
+ RT_TRACE((COMP_INIT | COMP_POWER | COMP_RF),
+ "GPIOChangeRFWorkItemCallBack(): Silent Reset!!!!!!!\n");
+ return;
+ }
+
+ if (priv->rtllib->state != RTLLIB_LINKED)
+ return;
+ call_usermodehelper(ac_dc_check_script_path, argv, envp, UMH_WAIT_PROC);
+
+ return;
+};
+
+
+void init_rate_adaptive(struct net_device *dev)
+{
+
+ struct r8192_priv *priv = rtllib_priv(dev);
+ struct rate_adaptive *pra = (struct rate_adaptive *)&priv->rate_adaptive;
+
+ pra->ratr_state = DM_RATR_STA_MAX;
+ pra->high2low_rssi_thresh_for_ra = RateAdaptiveTH_High;
+ pra->low2high_rssi_thresh_for_ra20M = RateAdaptiveTH_Low_20M+5;
+ pra->low2high_rssi_thresh_for_ra40M = RateAdaptiveTH_Low_40M+5;
+
+ pra->high_rssi_thresh_for_ra = RateAdaptiveTH_High+5;
+ pra->low_rssi_thresh_for_ra20M = RateAdaptiveTH_Low_20M;
+ pra->low_rssi_thresh_for_ra40M = RateAdaptiveTH_Low_40M;
+
+ if (priv->CustomerID == RT_CID_819x_Netcore)
+ pra->ping_rssi_enable = 1;
+ else
+ pra->ping_rssi_enable = 0;
+ pra->ping_rssi_thresh_for_ra = 15;
+
+
+ if (priv->rf_type == RF_2T4R) {
+ pra->upper_rssi_threshold_ratr = 0x8f0f0000;
+ pra->middle_rssi_threshold_ratr = 0x8f0ff000;
+ pra->low_rssi_threshold_ratr = 0x8f0ff001;
+ pra->low_rssi_threshold_ratr_40M = 0x8f0ff005;
+ pra->low_rssi_threshold_ratr_20M = 0x8f0ff001;
+ pra->ping_rssi_ratr = 0x0000000d;
+ } else if (priv->rf_type == RF_1T2R) {
+ pra->upper_rssi_threshold_ratr = 0x000fc000;
+ pra->middle_rssi_threshold_ratr = 0x000ff000;
+ pra->low_rssi_threshold_ratr = 0x000ff001;
+ pra->low_rssi_threshold_ratr_40M = 0x000ff005;
+ pra->low_rssi_threshold_ratr_20M = 0x000ff001;
+ pra->ping_rssi_ratr = 0x0000000d;
+ }
+
+}
+
+
+static void dm_check_rate_adaptive(struct net_device *dev)
+{
+ struct r8192_priv *priv = rtllib_priv(dev);
+ struct rt_hi_throughput *pHTInfo = priv->rtllib->pHTInfo;
+ struct rate_adaptive *pra = (struct rate_adaptive *)&priv->rate_adaptive;
+ u32 currentRATR, targetRATR = 0;
+ u32 LowRSSIThreshForRA = 0, HighRSSIThreshForRA = 0;
+ bool bshort_gi_enabled = false;
+ static u8 ping_rssi_state;
+
+ if (!priv->up) {
+ RT_TRACE(COMP_RATE, "<---- dm_check_rate_adaptive(): driver is going to unload\n");
+ return;
+ }
+
+ if (pra->rate_adaptive_disabled)
+ return;
+
+ if (!(priv->rtllib->mode == WIRELESS_MODE_N_24G ||
+ priv->rtllib->mode == WIRELESS_MODE_N_5G))
+ return;
+
+ if (priv->rtllib->state == RTLLIB_LINKED) {
+
+ bshort_gi_enabled = (pHTInfo->bCurTxBW40MHz && pHTInfo->bCurShortGI40MHz) ||
+ (!pHTInfo->bCurTxBW40MHz && pHTInfo->bCurShortGI20MHz);
+
+
+ pra->upper_rssi_threshold_ratr =
+ (pra->upper_rssi_threshold_ratr & (~BIT31)) | ((bshort_gi_enabled) ? BIT31 : 0);
+
+ pra->middle_rssi_threshold_ratr =
+ (pra->middle_rssi_threshold_ratr & (~BIT31)) | ((bshort_gi_enabled) ? BIT31 : 0);
+
+ if (priv->CurrentChannelBW != HT_CHANNEL_WIDTH_20) {
+ pra->low_rssi_threshold_ratr =
+ (pra->low_rssi_threshold_ratr_40M & (~BIT31)) | ((bshort_gi_enabled) ? BIT31 : 0);
+ } else {
+ pra->low_rssi_threshold_ratr =
+ (pra->low_rssi_threshold_ratr_20M & (~BIT31)) | ((bshort_gi_enabled) ? BIT31 : 0);
+ }
+ pra->ping_rssi_ratr =
+ (pra->ping_rssi_ratr & (~BIT31)) | ((bshort_gi_enabled) ? BIT31 : 0);
+
+ if (pra->ratr_state == DM_RATR_STA_HIGH) {
+ HighRSSIThreshForRA = pra->high2low_rssi_thresh_for_ra;
+ LowRSSIThreshForRA = (priv->CurrentChannelBW != HT_CHANNEL_WIDTH_20) ?
+ (pra->low_rssi_thresh_for_ra40M) : (pra->low_rssi_thresh_for_ra20M);
+ } else if (pra->ratr_state == DM_RATR_STA_LOW) {
+ HighRSSIThreshForRA = pra->high_rssi_thresh_for_ra;
+ LowRSSIThreshForRA = (priv->CurrentChannelBW != HT_CHANNEL_WIDTH_20) ?
+ (pra->low2high_rssi_thresh_for_ra40M) : (pra->low2high_rssi_thresh_for_ra20M);
+ } else {
+ HighRSSIThreshForRA = pra->high_rssi_thresh_for_ra;
+ LowRSSIThreshForRA = (priv->CurrentChannelBW != HT_CHANNEL_WIDTH_20) ?
+ (pra->low_rssi_thresh_for_ra40M) : (pra->low_rssi_thresh_for_ra20M);
+ }
+
+ if (priv->undecorated_smoothed_pwdb >= (long)HighRSSIThreshForRA) {
+ pra->ratr_state = DM_RATR_STA_HIGH;
+ targetRATR = pra->upper_rssi_threshold_ratr;
+ } else if (priv->undecorated_smoothed_pwdb >= (long)LowRSSIThreshForRA) {
+ pra->ratr_state = DM_RATR_STA_MIDDLE;
+ targetRATR = pra->middle_rssi_threshold_ratr;
+ } else {
+ pra->ratr_state = DM_RATR_STA_LOW;
+ targetRATR = pra->low_rssi_threshold_ratr;
+ }
+
+ if (pra->ping_rssi_enable) {
+ if (priv->undecorated_smoothed_pwdb < (long)(pra->ping_rssi_thresh_for_ra+5)) {
+ if ((priv->undecorated_smoothed_pwdb < (long)pra->ping_rssi_thresh_for_ra) ||
+ ping_rssi_state) {
+ pra->ratr_state = DM_RATR_STA_LOW;
+ targetRATR = pra->ping_rssi_ratr;
+ ping_rssi_state = 1;
+ }
+ } else {
+ ping_rssi_state = 0;
+ }
+ }
+
+ if (priv->rtllib->GetHalfNmodeSupportByAPsHandler(dev))
+ targetRATR &= 0xf00fffff;
+
+ currentRATR = read_nic_dword(dev, RATR0);
+ if (targetRATR != currentRATR) {
+ u32 ratr_value;
+
+ ratr_value = targetRATR;
+ RT_TRACE(COMP_RATE,
+ "currentRATR = %x, targetRATR = %x\n",
+ currentRATR, targetRATR);
+ if (priv->rf_type == RF_1T2R)
+ ratr_value &= ~(RATE_ALL_OFDM_2SS);
+ write_nic_dword(dev, RATR0, ratr_value);
+ write_nic_byte(dev, UFWP, 1);
+
+ pra->last_ratr = targetRATR;
+ }
+
+ } else {
+ pra->ratr_state = DM_RATR_STA_MAX;
+ }
+}
+
+static void dm_init_bandwidth_autoswitch(struct net_device *dev)
+{
+ struct r8192_priv *priv = rtllib_priv(dev);
+
+ priv->rtllib->bandwidth_auto_switch.threshold_20Mhzto40Mhz = BW_AUTO_SWITCH_LOW_HIGH;
+ priv->rtllib->bandwidth_auto_switch.threshold_40Mhzto20Mhz = BW_AUTO_SWITCH_HIGH_LOW;
+ priv->rtllib->bandwidth_auto_switch.bforced_tx20Mhz = false;
+ priv->rtllib->bandwidth_auto_switch.bautoswitch_enable = false;
+}
+
+static void dm_bandwidth_autoswitch(struct net_device *dev)
+{
+ struct r8192_priv *priv = rtllib_priv(dev);
+
+ if (priv->CurrentChannelBW == HT_CHANNEL_WIDTH_20 ||
+ !priv->rtllib->bandwidth_auto_switch.bautoswitch_enable)
+ return;
+ if (priv->rtllib->bandwidth_auto_switch.bforced_tx20Mhz == false) {
+ if (priv->undecorated_smoothed_pwdb <=
+ priv->rtllib->bandwidth_auto_switch.threshold_40Mhzto20Mhz)
+ priv->rtllib->bandwidth_auto_switch.bforced_tx20Mhz = true;
+ } else {
+ if (priv->undecorated_smoothed_pwdb >=
+ priv->rtllib->bandwidth_auto_switch.threshold_20Mhzto40Mhz)
+ priv->rtllib->bandwidth_auto_switch.bforced_tx20Mhz = false;
+ }
+}
+
+static u32 OFDMSwingTable[OFDM_Table_Length] = {
+ 0x7f8001fe,
+ 0x71c001c7,
+ 0x65400195,
+ 0x5a400169,
+ 0x50800142,
+ 0x47c0011f,
+ 0x40000100,
+ 0x390000e4,
+ 0x32c000cb,
+ 0x2d4000b5,
+ 0x288000a2,
+ 0x24000090,
+ 0x20000080,
+ 0x1c800072,
+ 0x19800066,
+ 0x26c0005b,
+ 0x24400051,
+ 0x12000048,
+ 0x10000040
+};
+
+static u8 CCKSwingTable_Ch1_Ch13[CCK_Table_length][8] = {
+ {0x36, 0x35, 0x2e, 0x25, 0x1c, 0x12, 0x09, 0x04},
+ {0x30, 0x2f, 0x29, 0x21, 0x19, 0x10, 0x08, 0x03},
+ {0x2b, 0x2a, 0x25, 0x1e, 0x16, 0x0e, 0x07, 0x03},
+ {0x26, 0x25, 0x21, 0x1b, 0x14, 0x0d, 0x06, 0x03},
+ {0x22, 0x21, 0x1d, 0x18, 0x11, 0x0b, 0x06, 0x02},
+ {0x1f, 0x1e, 0x1a, 0x15, 0x10, 0x0a, 0x05, 0x02},
+ {0x1b, 0x1a, 0x17, 0x13, 0x0e, 0x09, 0x04, 0x02},
+ {0x18, 0x17, 0x15, 0x11, 0x0c, 0x08, 0x04, 0x02},
+ {0x16, 0x15, 0x12, 0x0f, 0x0b, 0x07, 0x04, 0x01},
+ {0x13, 0x13, 0x10, 0x0d, 0x0a, 0x06, 0x03, 0x01},
+ {0x11, 0x11, 0x0f, 0x0c, 0x09, 0x06, 0x03, 0x01},
+ {0x0f, 0x0f, 0x0d, 0x0b, 0x08, 0x05, 0x03, 0x01}
+};
+
+static u8 CCKSwingTable_Ch14[CCK_Table_length][8] = {
+ {0x36, 0x35, 0x2e, 0x1b, 0x00, 0x00, 0x00, 0x00},
+ {0x30, 0x2f, 0x29, 0x18, 0x00, 0x00, 0x00, 0x00},
+ {0x2b, 0x2a, 0x25, 0x15, 0x00, 0x00, 0x00, 0x00},
+ {0x26, 0x25, 0x21, 0x13, 0x00, 0x00, 0x00, 0x00},
+ {0x22, 0x21, 0x1d, 0x11, 0x00, 0x00, 0x00, 0x00},
+ {0x1f, 0x1e, 0x1a, 0x0f, 0x00, 0x00, 0x00, 0x00},
+ {0x1b, 0x1a, 0x17, 0x0e, 0x00, 0x00, 0x00, 0x00},
+ {0x18, 0x17, 0x15, 0x0c, 0x00, 0x00, 0x00, 0x00},
+ {0x16, 0x15, 0x12, 0x0b, 0x00, 0x00, 0x00, 0x00},
+ {0x13, 0x13, 0x10, 0x0a, 0x00, 0x00, 0x00, 0x00},
+ {0x11, 0x11, 0x0f, 0x09, 0x00, 0x00, 0x00, 0x00},
+ {0x0f, 0x0f, 0x0d, 0x08, 0x00, 0x00, 0x00, 0x00}
+};
+
+#define Pw_Track_Flag 0x11d
+#define Tssi_Mea_Value 0x13c
+#define Tssi_Report_Value1 0x134
+#define Tssi_Report_Value2 0x13e
+#define FW_Busy_Flag 0x13f
+
+static void dm_TXPowerTrackingCallback_TSSI(struct net_device *dev)
+{
+ struct r8192_priv *priv = rtllib_priv(dev);
+ bool bHighpowerstate, viviflag = false;
+ struct dcmd_txcmd tx_cmd;
+ u8 powerlevelOFDM24G;
+ int i = 0, j = 0, k = 0;
+ u8 RF_Type, tmp_report[5] = {0, 0, 0, 0, 0};
+ u32 Value;
+ u8 Pwr_Flag;
+ u16 Avg_TSSI_Meas, TSSI_13dBm, Avg_TSSI_Meas_from_driver = 0;
+ u32 delta = 0;
+
+ RT_TRACE(COMP_POWER_TRACKING, "%s()\n", __func__);
+ write_nic_byte(dev, Pw_Track_Flag, 0);
+ write_nic_byte(dev, FW_Busy_Flag, 0);
+ priv->rtllib->bdynamic_txpower_enable = false;
+ bHighpowerstate = priv->bDynamicTxHighPower;
+
+ powerlevelOFDM24G = (u8)(priv->Pwr_Track>>24);
+ RF_Type = priv->rf_type;
+ Value = (RF_Type<<8) | powerlevelOFDM24G;
+
+ RT_TRACE(COMP_POWER_TRACKING, "powerlevelOFDM24G = %x\n",
+ powerlevelOFDM24G);
+
+
+ for (j = 0; j <= 30; j++) {
+
+ tx_cmd.Op = TXCMD_SET_TX_PWR_TRACKING;
+ tx_cmd.Length = 4;
+ tx_cmd.Value = Value;
+ cmpk_message_handle_tx(dev, (u8 *)&tx_cmd,
+ DESC_PACKET_TYPE_INIT,
+ sizeof(struct dcmd_txcmd));
+ mdelay(1);
+ for (i = 0; i <= 30; i++) {
+ Pwr_Flag = read_nic_byte(dev, Pw_Track_Flag);
+
+ if (Pwr_Flag == 0) {
+ mdelay(1);
+
+ if (priv->bResetInProgress) {
+ RT_TRACE(COMP_POWER_TRACKING,
+ "we are in silent reset progress, so return\n");
+ write_nic_byte(dev, Pw_Track_Flag, 0);
+ write_nic_byte(dev, FW_Busy_Flag, 0);
+ return;
+ }
+ if (priv->rtllib->eRFPowerState != eRfOn) {
+ RT_TRACE(COMP_POWER_TRACKING,
+ "we are in power save, so return\n");
+ write_nic_byte(dev, Pw_Track_Flag, 0);
+ write_nic_byte(dev, FW_Busy_Flag, 0);
+ return;
+ }
+
+ continue;
+ }
+
+ Avg_TSSI_Meas = read_nic_word(dev, Tssi_Mea_Value);
+
+ if (Avg_TSSI_Meas == 0) {
+ write_nic_byte(dev, Pw_Track_Flag, 0);
+ write_nic_byte(dev, FW_Busy_Flag, 0);
+ return;
+ }
+
+ for (k = 0; k < 5; k++) {
+ if (k != 4)
+ tmp_report[k] = read_nic_byte(dev,
+ Tssi_Report_Value1+k);
+ else
+ tmp_report[k] = read_nic_byte(dev,
+ Tssi_Report_Value2);
+
+ RT_TRACE(COMP_POWER_TRACKING,
+ "TSSI_report_value = %d\n",
+ tmp_report[k]);
+
+ if (tmp_report[k] <= 20) {
+ viviflag = true;
+ break;
+ }
+ }
+
+ if (viviflag) {
+ write_nic_byte(dev, Pw_Track_Flag, 0);
+ viviflag = false;
+ RT_TRACE(COMP_POWER_TRACKING, "we filted this data\n");
+ for (k = 0; k < 5; k++)
+ tmp_report[k] = 0;
+ break;
+ }
+
+ for (k = 0; k < 5; k++)
+ Avg_TSSI_Meas_from_driver += tmp_report[k];
+
+ Avg_TSSI_Meas_from_driver = Avg_TSSI_Meas_from_driver*100/5;
+ RT_TRACE(COMP_POWER_TRACKING,
+ "Avg_TSSI_Meas_from_driver = %d\n",
+ Avg_TSSI_Meas_from_driver);
+ TSSI_13dBm = priv->TSSI_13dBm;
+ RT_TRACE(COMP_POWER_TRACKING, "TSSI_13dBm = %d\n", TSSI_13dBm);
+
+ if (Avg_TSSI_Meas_from_driver > TSSI_13dBm)
+ delta = Avg_TSSI_Meas_from_driver - TSSI_13dBm;
+ else
+ delta = TSSI_13dBm - Avg_TSSI_Meas_from_driver;
+
+ if (delta <= E_FOR_TX_POWER_TRACK) {
+ priv->rtllib->bdynamic_txpower_enable = true;
+ write_nic_byte(dev, Pw_Track_Flag, 0);
+ write_nic_byte(dev, FW_Busy_Flag, 0);
+ RT_TRACE(COMP_POWER_TRACKING,
+ "tx power track is done\n");
+ RT_TRACE(COMP_POWER_TRACKING,
+ "priv->rfa_txpowertrackingindex = %d\n",
+ priv->rfa_txpowertrackingindex);
+ RT_TRACE(COMP_POWER_TRACKING,
+ "priv->rfa_txpowertrackingindex_real = %d\n",
+ priv->rfa_txpowertrackingindex_real);
+ RT_TRACE(COMP_POWER_TRACKING,
+ "priv->CCKPresentAttentuation_difference = %d\n",
+ priv->CCKPresentAttentuation_difference);
+ RT_TRACE(COMP_POWER_TRACKING,
+ "priv->CCKPresentAttentuation = %d\n",
+ priv->CCKPresentAttentuation);
+ return;
+ }
+ if (Avg_TSSI_Meas_from_driver < TSSI_13dBm - E_FOR_TX_POWER_TRACK) {
+ if (RF_Type == RF_2T4R) {
+
+ if ((priv->rfa_txpowertrackingindex > 0) &&
+ (priv->rfc_txpowertrackingindex > 0)) {
+ priv->rfa_txpowertrackingindex--;
+ if (priv->rfa_txpowertrackingindex_real > 4) {
+ priv->rfa_txpowertrackingindex_real--;
+ rtl8192_setBBreg(dev,
+ rOFDM0_XATxIQImbalance,
+ bMaskDWord,
+ priv->txbbgain_table[priv->rfa_txpowertrackingindex_real].txbbgain_value);
+ }
+
+ priv->rfc_txpowertrackingindex--;
+ if (priv->rfc_txpowertrackingindex_real > 4) {
+ priv->rfc_txpowertrackingindex_real--;
+ rtl8192_setBBreg(dev,
+ rOFDM0_XCTxIQImbalance,
+ bMaskDWord,
+ priv->txbbgain_table[priv->rfc_txpowertrackingindex_real].txbbgain_value);
+ }
+ } else {
+ rtl8192_setBBreg(dev, rOFDM0_XATxIQImbalance,
+ bMaskDWord,
+ priv->txbbgain_table[4].txbbgain_value);
+ rtl8192_setBBreg(dev,
+ rOFDM0_XCTxIQImbalance,
+ bMaskDWord, priv->txbbgain_table[4].txbbgain_value);
+ }
+ } else {
+ if (priv->rfa_txpowertrackingindex > 0) {
+ priv->rfa_txpowertrackingindex--;
+ if (priv->rfa_txpowertrackingindex_real > 4) {
+ priv->rfa_txpowertrackingindex_real--;
+ rtl8192_setBBreg(dev,
+ rOFDM0_XATxIQImbalance,
+ bMaskDWord,
+ priv->txbbgain_table[priv->rfa_txpowertrackingindex_real].txbbgain_value);
+ }
+ } else
+ rtl8192_setBBreg(dev, rOFDM0_XATxIQImbalance,
+ bMaskDWord, priv->txbbgain_table[4].txbbgain_value);
+
+ }
+ } else {
+ if (RF_Type == RF_2T4R) {
+ if ((priv->rfa_txpowertrackingindex <
+ TxBBGainTableLength - 1) &&
+ (priv->rfc_txpowertrackingindex <
+ TxBBGainTableLength - 1)) {
+ priv->rfa_txpowertrackingindex++;
+ priv->rfa_txpowertrackingindex_real++;
+ rtl8192_setBBreg(dev,
+ rOFDM0_XATxIQImbalance,
+ bMaskDWord,
+ priv->txbbgain_table
+ [priv->rfa_txpowertrackingindex_real].txbbgain_value);
+ priv->rfc_txpowertrackingindex++;
+ priv->rfc_txpowertrackingindex_real++;
+ rtl8192_setBBreg(dev,
+ rOFDM0_XCTxIQImbalance,
+ bMaskDWord,
+ priv->txbbgain_table[priv->rfc_txpowertrackingindex_real].txbbgain_value);
+ } else {
+ rtl8192_setBBreg(dev,
+ rOFDM0_XATxIQImbalance,
+ bMaskDWord,
+ priv->txbbgain_table[TxBBGainTableLength - 1].txbbgain_value);
+ rtl8192_setBBreg(dev,
+ rOFDM0_XCTxIQImbalance,
+ bMaskDWord, priv->txbbgain_table[TxBBGainTableLength - 1].txbbgain_value);
+ }
+ } else {
+ if (priv->rfa_txpowertrackingindex < (TxBBGainTableLength - 1)) {
+ priv->rfa_txpowertrackingindex++;
+ priv->rfa_txpowertrackingindex_real++;
+ rtl8192_setBBreg(dev, rOFDM0_XATxIQImbalance,
+ bMaskDWord,
+ priv->txbbgain_table[priv->rfa_txpowertrackingindex_real].txbbgain_value);
+ } else
+ rtl8192_setBBreg(dev, rOFDM0_XATxIQImbalance,
+ bMaskDWord,
+ priv->txbbgain_table[TxBBGainTableLength - 1].txbbgain_value);
+ }
+ }
+ if (RF_Type == RF_2T4R) {
+ priv->CCKPresentAttentuation_difference
+ = priv->rfa_txpowertrackingindex - priv->rfa_txpowertracking_default;
+ } else {
+ priv->CCKPresentAttentuation_difference
+ = priv->rfa_txpowertrackingindex_real - priv->rfa_txpowertracking_default;
+ }
+
+ if (priv->CurrentChannelBW == HT_CHANNEL_WIDTH_20)
+ priv->CCKPresentAttentuation =
+ priv->CCKPresentAttentuation_20Mdefault +
+ priv->CCKPresentAttentuation_difference;
+ else
+ priv->CCKPresentAttentuation =
+ priv->CCKPresentAttentuation_40Mdefault +
+ priv->CCKPresentAttentuation_difference;
+
+ if (priv->CCKPresentAttentuation > (CCKTxBBGainTableLength-1))
+ priv->CCKPresentAttentuation = CCKTxBBGainTableLength-1;
+ if (priv->CCKPresentAttentuation < 0)
+ priv->CCKPresentAttentuation = 0;
+
+ if (priv->CCKPresentAttentuation > -1 &&
+ priv->CCKPresentAttentuation < CCKTxBBGainTableLength) {
+ if (priv->rtllib->current_network.channel == 14 &&
+ !priv->bcck_in_ch14) {
+ priv->bcck_in_ch14 = true;
+ dm_cck_txpower_adjust(dev, priv->bcck_in_ch14);
+ } else if (priv->rtllib->current_network.channel != 14 && priv->bcck_in_ch14) {
+ priv->bcck_in_ch14 = false;
+ dm_cck_txpower_adjust(dev, priv->bcck_in_ch14);
+ } else
+ dm_cck_txpower_adjust(dev, priv->bcck_in_ch14);
+ }
+ RT_TRACE(COMP_POWER_TRACKING,
+ "priv->rfa_txpowertrackingindex = %d\n",
+ priv->rfa_txpowertrackingindex);
+ RT_TRACE(COMP_POWER_TRACKING,
+ "priv->rfa_txpowertrackingindex_real = %d\n",
+ priv->rfa_txpowertrackingindex_real);
+ RT_TRACE(COMP_POWER_TRACKING,
+ "priv->CCKPresentAttentuation_difference = %d\n",
+ priv->CCKPresentAttentuation_difference);
+ RT_TRACE(COMP_POWER_TRACKING,
+ "priv->CCKPresentAttentuation = %d\n",
+ priv->CCKPresentAttentuation);
+
+ if (priv->CCKPresentAttentuation_difference <= -12 || priv->CCKPresentAttentuation_difference >= 24) {
+ priv->rtllib->bdynamic_txpower_enable = true;
+ write_nic_byte(dev, Pw_Track_Flag, 0);
+ write_nic_byte(dev, FW_Busy_Flag, 0);
+ RT_TRACE(COMP_POWER_TRACKING, "tx power track--->limited\n");
+ return;
+ }
+
+ write_nic_byte(dev, Pw_Track_Flag, 0);
+ Avg_TSSI_Meas_from_driver = 0;
+ for (k = 0; k < 5; k++)
+ tmp_report[k] = 0;
+ break;
+ }
+ write_nic_byte(dev, FW_Busy_Flag, 0);
+ }
+ priv->rtllib->bdynamic_txpower_enable = true;
+ write_nic_byte(dev, Pw_Track_Flag, 0);
+}
+
+static void dm_TXPowerTrackingCallback_ThermalMeter(struct net_device *dev)
+{
+#define ThermalMeterVal 9
+ struct r8192_priv *priv = rtllib_priv(dev);
+ u32 tmpRegA, TempCCk;
+ u8 tmpOFDMindex, tmpCCKindex, tmpCCK20Mindex, tmpCCK40Mindex, tmpval;
+ int i = 0, CCKSwingNeedUpdate = 0;
+
+ if (!priv->btxpower_trackingInit) {
+ tmpRegA = rtl8192_QueryBBReg(dev, rOFDM0_XATxIQImbalance, bMaskDWord);
+ for (i = 0; i < OFDM_Table_Length; i++) {
+ if (tmpRegA == OFDMSwingTable[i]) {
+ priv->OFDM_index[0] = (u8)i;
+ RT_TRACE(COMP_POWER_TRACKING, "Initial reg0x%x = 0x%x, OFDM_index = 0x%x\n",
+ rOFDM0_XATxIQImbalance, tmpRegA, priv->OFDM_index[0]);
+ }
+ }
+
+ TempCCk = rtl8192_QueryBBReg(dev, rCCK0_TxFilter1, bMaskByte2);
+ for (i = 0; i < CCK_Table_length; i++) {
+ if (TempCCk == (u32)CCKSwingTable_Ch1_Ch13[i][0]) {
+ priv->CCK_index = (u8) i;
+ RT_TRACE(COMP_POWER_TRACKING,
+ "Initial reg0x%x = 0x%x, CCK_index = 0x%x\n",
+ rCCK0_TxFilter1, TempCCk,
+ priv->CCK_index);
+ break;
+ }
+ }
+ priv->btxpower_trackingInit = true;
+ return;
+ }
+
+ tmpRegA = rtl8192_phy_QueryRFReg(dev, RF90_PATH_A, 0x12, 0x078);
+ RT_TRACE(COMP_POWER_TRACKING, "Readback ThermalMeterA = %d\n", tmpRegA);
+ if (tmpRegA < 3 || tmpRegA > 13)
+ return;
+ if (tmpRegA >= 12)
+ tmpRegA = 12;
+ RT_TRACE(COMP_POWER_TRACKING, "Valid ThermalMeterA = %d\n", tmpRegA);
+ priv->ThermalMeter[0] = ThermalMeterVal;
+ priv->ThermalMeter[1] = ThermalMeterVal;
+
+ if (priv->ThermalMeter[0] >= (u8)tmpRegA) {
+ tmpOFDMindex = tmpCCK20Mindex = 6+(priv->ThermalMeter[0] -
+ (u8)tmpRegA);
+ tmpCCK40Mindex = tmpCCK20Mindex - 6;
+ if (tmpOFDMindex >= OFDM_Table_Length)
+ tmpOFDMindex = OFDM_Table_Length-1;
+ if (tmpCCK20Mindex >= CCK_Table_length)
+ tmpCCK20Mindex = CCK_Table_length-1;
+ if (tmpCCK40Mindex >= CCK_Table_length)
+ tmpCCK40Mindex = CCK_Table_length-1;
+ } else {
+ tmpval = ((u8)tmpRegA - priv->ThermalMeter[0]);
+ if (tmpval >= 6)
+ tmpOFDMindex = tmpCCK20Mindex = 0;
+ else
+ tmpOFDMindex = tmpCCK20Mindex = 6 - tmpval;
+ tmpCCK40Mindex = 0;
+ }
+ if (priv->CurrentChannelBW != HT_CHANNEL_WIDTH_20)
+ tmpCCKindex = tmpCCK40Mindex;
+ else
+ tmpCCKindex = tmpCCK20Mindex;
+
+ priv->Record_CCK_20Mindex = tmpCCK20Mindex;
+ priv->Record_CCK_40Mindex = tmpCCK40Mindex;
+ RT_TRACE(COMP_POWER_TRACKING,
+ "Record_CCK_20Mindex / Record_CCK_40Mindex = %d / %d.\n",
+ priv->Record_CCK_20Mindex, priv->Record_CCK_40Mindex);
+
+ if (priv->rtllib->current_network.channel == 14 &&
+ !priv->bcck_in_ch14) {
+ priv->bcck_in_ch14 = true;
+ CCKSwingNeedUpdate = 1;
+ } else if (priv->rtllib->current_network.channel != 14 &&
+ priv->bcck_in_ch14) {
+ priv->bcck_in_ch14 = false;
+ CCKSwingNeedUpdate = 1;
+ }
+
+ if (priv->CCK_index != tmpCCKindex) {
+ priv->CCK_index = tmpCCKindex;
+ CCKSwingNeedUpdate = 1;
+ }
+
+ if (CCKSwingNeedUpdate)
+ dm_cck_txpower_adjust(dev, priv->bcck_in_ch14);
+ if (priv->OFDM_index[0] != tmpOFDMindex) {
+ priv->OFDM_index[0] = tmpOFDMindex;
+ rtl8192_setBBreg(dev, rOFDM0_XATxIQImbalance, bMaskDWord,
+ OFDMSwingTable[priv->OFDM_index[0]]);
+ RT_TRACE(COMP_POWER_TRACKING, "Update OFDMSwing[%d] = 0x%x\n",
+ priv->OFDM_index[0],
+ OFDMSwingTable[priv->OFDM_index[0]]);
+ }
+ priv->txpower_count = 0;
+}
+
+void dm_txpower_trackingcallback(void *data)
+{
+ struct r8192_priv *priv = container_of_dwork_rsl(data,
+ struct r8192_priv, txpower_tracking_wq);
+ struct net_device *dev = priv->rtllib->dev;
+
+ if (priv->IC_Cut >= IC_VersionCut_D)
+ dm_TXPowerTrackingCallback_TSSI(dev);
+ else
+ dm_TXPowerTrackingCallback_ThermalMeter(dev);
+}
+
+static void dm_InitializeTXPowerTracking_TSSI(struct net_device *dev)
+{
+
+ struct r8192_priv *priv = rtllib_priv(dev);
+
+ priv->txbbgain_table[0].txbb_iq_amplifygain = 12;
+ priv->txbbgain_table[0].txbbgain_value = 0x7f8001fe;
+ priv->txbbgain_table[1].txbb_iq_amplifygain = 11;
+ priv->txbbgain_table[1].txbbgain_value = 0x788001e2;
+ priv->txbbgain_table[2].txbb_iq_amplifygain = 10;
+ priv->txbbgain_table[2].txbbgain_value = 0x71c001c7;
+ priv->txbbgain_table[3].txbb_iq_amplifygain = 9;
+ priv->txbbgain_table[3].txbbgain_value = 0x6b8001ae;
+ priv->txbbgain_table[4].txbb_iq_amplifygain = 8;
+ priv->txbbgain_table[4].txbbgain_value = 0x65400195;
+ priv->txbbgain_table[5].txbb_iq_amplifygain = 7;
+ priv->txbbgain_table[5].txbbgain_value = 0x5fc0017f;
+ priv->txbbgain_table[6].txbb_iq_amplifygain = 6;
+ priv->txbbgain_table[6].txbbgain_value = 0x5a400169;
+ priv->txbbgain_table[7].txbb_iq_amplifygain = 5;
+ priv->txbbgain_table[7].txbbgain_value = 0x55400155;
+ priv->txbbgain_table[8].txbb_iq_amplifygain = 4;
+ priv->txbbgain_table[8].txbbgain_value = 0x50800142;
+ priv->txbbgain_table[9].txbb_iq_amplifygain = 3;
+ priv->txbbgain_table[9].txbbgain_value = 0x4c000130;
+ priv->txbbgain_table[10].txbb_iq_amplifygain = 2;
+ priv->txbbgain_table[10].txbbgain_value = 0x47c0011f;
+ priv->txbbgain_table[11].txbb_iq_amplifygain = 1;
+ priv->txbbgain_table[11].txbbgain_value = 0x43c0010f;
+ priv->txbbgain_table[12].txbb_iq_amplifygain = 0;
+ priv->txbbgain_table[12].txbbgain_value = 0x40000100;
+ priv->txbbgain_table[13].txbb_iq_amplifygain = -1;
+ priv->txbbgain_table[13].txbbgain_value = 0x3c8000f2;
+ priv->txbbgain_table[14].txbb_iq_amplifygain = -2;
+ priv->txbbgain_table[14].txbbgain_value = 0x390000e4;
+ priv->txbbgain_table[15].txbb_iq_amplifygain = -3;
+ priv->txbbgain_table[15].txbbgain_value = 0x35c000d7;
+ priv->txbbgain_table[16].txbb_iq_amplifygain = -4;
+ priv->txbbgain_table[16].txbbgain_value = 0x32c000cb;
+ priv->txbbgain_table[17].txbb_iq_amplifygain = -5;
+ priv->txbbgain_table[17].txbbgain_value = 0x300000c0;
+ priv->txbbgain_table[18].txbb_iq_amplifygain = -6;
+ priv->txbbgain_table[18].txbbgain_value = 0x2d4000b5;
+ priv->txbbgain_table[19].txbb_iq_amplifygain = -7;
+ priv->txbbgain_table[19].txbbgain_value = 0x2ac000ab;
+ priv->txbbgain_table[20].txbb_iq_amplifygain = -8;
+ priv->txbbgain_table[20].txbbgain_value = 0x288000a2;
+ priv->txbbgain_table[21].txbb_iq_amplifygain = -9;
+ priv->txbbgain_table[21].txbbgain_value = 0x26000098;
+ priv->txbbgain_table[22].txbb_iq_amplifygain = -10;
+ priv->txbbgain_table[22].txbbgain_value = 0x24000090;
+ priv->txbbgain_table[23].txbb_iq_amplifygain = -11;
+ priv->txbbgain_table[23].txbbgain_value = 0x22000088;
+ priv->txbbgain_table[24].txbb_iq_amplifygain = -12;
+ priv->txbbgain_table[24].txbbgain_value = 0x20000080;
+ priv->txbbgain_table[25].txbb_iq_amplifygain = -13;
+ priv->txbbgain_table[25].txbbgain_value = 0x1a00006c;
+ priv->txbbgain_table[26].txbb_iq_amplifygain = -14;
+ priv->txbbgain_table[26].txbbgain_value = 0x1c800072;
+ priv->txbbgain_table[27].txbb_iq_amplifygain = -15;
+ priv->txbbgain_table[27].txbbgain_value = 0x18000060;
+ priv->txbbgain_table[28].txbb_iq_amplifygain = -16;
+ priv->txbbgain_table[28].txbbgain_value = 0x19800066;
+ priv->txbbgain_table[29].txbb_iq_amplifygain = -17;
+ priv->txbbgain_table[29].txbbgain_value = 0x15800056;
+ priv->txbbgain_table[30].txbb_iq_amplifygain = -18;
+ priv->txbbgain_table[30].txbbgain_value = 0x26c0005b;
+ priv->txbbgain_table[31].txbb_iq_amplifygain = -19;
+ priv->txbbgain_table[31].txbbgain_value = 0x14400051;
+ priv->txbbgain_table[32].txbb_iq_amplifygain = -20;
+ priv->txbbgain_table[32].txbbgain_value = 0x24400051;
+ priv->txbbgain_table[33].txbb_iq_amplifygain = -21;
+ priv->txbbgain_table[33].txbbgain_value = 0x1300004c;
+ priv->txbbgain_table[34].txbb_iq_amplifygain = -22;
+ priv->txbbgain_table[34].txbbgain_value = 0x12000048;
+ priv->txbbgain_table[35].txbb_iq_amplifygain = -23;
+ priv->txbbgain_table[35].txbbgain_value = 0x11000044;
+ priv->txbbgain_table[36].txbb_iq_amplifygain = -24;
+ priv->txbbgain_table[36].txbbgain_value = 0x10000040;
+
+ priv->cck_txbbgain_table[0].ccktxbb_valuearray[0] = 0x36;
+ priv->cck_txbbgain_table[0].ccktxbb_valuearray[1] = 0x35;
+ priv->cck_txbbgain_table[0].ccktxbb_valuearray[2] = 0x2e;
+ priv->cck_txbbgain_table[0].ccktxbb_valuearray[3] = 0x25;
+ priv->cck_txbbgain_table[0].ccktxbb_valuearray[4] = 0x1c;
+ priv->cck_txbbgain_table[0].ccktxbb_valuearray[5] = 0x12;
+ priv->cck_txbbgain_table[0].ccktxbb_valuearray[6] = 0x09;
+ priv->cck_txbbgain_table[0].ccktxbb_valuearray[7] = 0x04;
+
+ priv->cck_txbbgain_table[1].ccktxbb_valuearray[0] = 0x33;
+ priv->cck_txbbgain_table[1].ccktxbb_valuearray[1] = 0x32;
+ priv->cck_txbbgain_table[1].ccktxbb_valuearray[2] = 0x2b;
+ priv->cck_txbbgain_table[1].ccktxbb_valuearray[3] = 0x23;
+ priv->cck_txbbgain_table[1].ccktxbb_valuearray[4] = 0x1a;
+ priv->cck_txbbgain_table[1].ccktxbb_valuearray[5] = 0x11;
+ priv->cck_txbbgain_table[1].ccktxbb_valuearray[6] = 0x08;
+ priv->cck_txbbgain_table[1].ccktxbb_valuearray[7] = 0x04;
+
+ priv->cck_txbbgain_table[2].ccktxbb_valuearray[0] = 0x30;
+ priv->cck_txbbgain_table[2].ccktxbb_valuearray[1] = 0x2f;
+ priv->cck_txbbgain_table[2].ccktxbb_valuearray[2] = 0x29;
+ priv->cck_txbbgain_table[2].ccktxbb_valuearray[3] = 0x21;
+ priv->cck_txbbgain_table[2].ccktxbb_valuearray[4] = 0x19;
+ priv->cck_txbbgain_table[2].ccktxbb_valuearray[5] = 0x10;
+ priv->cck_txbbgain_table[2].ccktxbb_valuearray[6] = 0x08;
+ priv->cck_txbbgain_table[2].ccktxbb_valuearray[7] = 0x03;
+
+ priv->cck_txbbgain_table[3].ccktxbb_valuearray[0] = 0x2d;
+ priv->cck_txbbgain_table[3].ccktxbb_valuearray[1] = 0x2d;
+ priv->cck_txbbgain_table[3].ccktxbb_valuearray[2] = 0x27;
+ priv->cck_txbbgain_table[3].ccktxbb_valuearray[3] = 0x1f;
+ priv->cck_txbbgain_table[3].ccktxbb_valuearray[4] = 0x18;
+ priv->cck_txbbgain_table[3].ccktxbb_valuearray[5] = 0x0f;
+ priv->cck_txbbgain_table[3].ccktxbb_valuearray[6] = 0x08;
+ priv->cck_txbbgain_table[3].ccktxbb_valuearray[7] = 0x03;
+
+ priv->cck_txbbgain_table[4].ccktxbb_valuearray[0] = 0x2b;
+ priv->cck_txbbgain_table[4].ccktxbb_valuearray[1] = 0x2a;
+ priv->cck_txbbgain_table[4].ccktxbb_valuearray[2] = 0x25;
+ priv->cck_txbbgain_table[4].ccktxbb_valuearray[3] = 0x1e;
+ priv->cck_txbbgain_table[4].ccktxbb_valuearray[4] = 0x16;
+ priv->cck_txbbgain_table[4].ccktxbb_valuearray[5] = 0x0e;
+ priv->cck_txbbgain_table[4].ccktxbb_valuearray[6] = 0x07;
+ priv->cck_txbbgain_table[4].ccktxbb_valuearray[7] = 0x03;
+
+ priv->cck_txbbgain_table[5].ccktxbb_valuearray[0] = 0x28;
+ priv->cck_txbbgain_table[5].ccktxbb_valuearray[1] = 0x28;
+ priv->cck_txbbgain_table[5].ccktxbb_valuearray[2] = 0x22;
+ priv->cck_txbbgain_table[5].ccktxbb_valuearray[3] = 0x1c;
+ priv->cck_txbbgain_table[5].ccktxbb_valuearray[4] = 0x15;
+ priv->cck_txbbgain_table[5].ccktxbb_valuearray[5] = 0x0d;
+ priv->cck_txbbgain_table[5].ccktxbb_valuearray[6] = 0x07;
+ priv->cck_txbbgain_table[5].ccktxbb_valuearray[7] = 0x03;
+
+ priv->cck_txbbgain_table[6].ccktxbb_valuearray[0] = 0x26;
+ priv->cck_txbbgain_table[6].ccktxbb_valuearray[1] = 0x25;
+ priv->cck_txbbgain_table[6].ccktxbb_valuearray[2] = 0x21;
+ priv->cck_txbbgain_table[6].ccktxbb_valuearray[3] = 0x1b;
+ priv->cck_txbbgain_table[6].ccktxbb_valuearray[4] = 0x14;
+ priv->cck_txbbgain_table[6].ccktxbb_valuearray[5] = 0x0d;
+ priv->cck_txbbgain_table[6].ccktxbb_valuearray[6] = 0x06;
+ priv->cck_txbbgain_table[6].ccktxbb_valuearray[7] = 0x03;
+
+ priv->cck_txbbgain_table[7].ccktxbb_valuearray[0] = 0x24;
+ priv->cck_txbbgain_table[7].ccktxbb_valuearray[1] = 0x23;
+ priv->cck_txbbgain_table[7].ccktxbb_valuearray[2] = 0x1f;
+ priv->cck_txbbgain_table[7].ccktxbb_valuearray[3] = 0x19;
+ priv->cck_txbbgain_table[7].ccktxbb_valuearray[4] = 0x13;
+ priv->cck_txbbgain_table[7].ccktxbb_valuearray[5] = 0x0c;
+ priv->cck_txbbgain_table[7].ccktxbb_valuearray[6] = 0x06;
+ priv->cck_txbbgain_table[7].ccktxbb_valuearray[7] = 0x03;
+
+ priv->cck_txbbgain_table[8].ccktxbb_valuearray[0] = 0x22;
+ priv->cck_txbbgain_table[8].ccktxbb_valuearray[1] = 0x21;
+ priv->cck_txbbgain_table[8].ccktxbb_valuearray[2] = 0x1d;
+ priv->cck_txbbgain_table[8].ccktxbb_valuearray[3] = 0x18;
+ priv->cck_txbbgain_table[8].ccktxbb_valuearray[4] = 0x11;
+ priv->cck_txbbgain_table[8].ccktxbb_valuearray[5] = 0x0b;
+ priv->cck_txbbgain_table[8].ccktxbb_valuearray[6] = 0x06;
+ priv->cck_txbbgain_table[8].ccktxbb_valuearray[7] = 0x02;
+
+ priv->cck_txbbgain_table[9].ccktxbb_valuearray[0] = 0x20;
+ priv->cck_txbbgain_table[9].ccktxbb_valuearray[1] = 0x20;
+ priv->cck_txbbgain_table[9].ccktxbb_valuearray[2] = 0x1b;
+ priv->cck_txbbgain_table[9].ccktxbb_valuearray[3] = 0x16;
+ priv->cck_txbbgain_table[9].ccktxbb_valuearray[4] = 0x11;
+ priv->cck_txbbgain_table[9].ccktxbb_valuearray[5] = 0x08;
+ priv->cck_txbbgain_table[9].ccktxbb_valuearray[6] = 0x05;
+ priv->cck_txbbgain_table[9].ccktxbb_valuearray[7] = 0x02;
+
+ priv->cck_txbbgain_table[10].ccktxbb_valuearray[0] = 0x1f;
+ priv->cck_txbbgain_table[10].ccktxbb_valuearray[1] = 0x1e;
+ priv->cck_txbbgain_table[10].ccktxbb_valuearray[2] = 0x1a;
+ priv->cck_txbbgain_table[10].ccktxbb_valuearray[3] = 0x15;
+ priv->cck_txbbgain_table[10].ccktxbb_valuearray[4] = 0x10;
+ priv->cck_txbbgain_table[10].ccktxbb_valuearray[5] = 0x0a;
+ priv->cck_txbbgain_table[10].ccktxbb_valuearray[6] = 0x05;
+ priv->cck_txbbgain_table[10].ccktxbb_valuearray[7] = 0x02;
+
+ priv->cck_txbbgain_table[11].ccktxbb_valuearray[0] = 0x1d;
+ priv->cck_txbbgain_table[11].ccktxbb_valuearray[1] = 0x1c;
+ priv->cck_txbbgain_table[11].ccktxbb_valuearray[2] = 0x18;
+ priv->cck_txbbgain_table[11].ccktxbb_valuearray[3] = 0x14;
+ priv->cck_txbbgain_table[11].ccktxbb_valuearray[4] = 0x0f;
+ priv->cck_txbbgain_table[11].ccktxbb_valuearray[5] = 0x0a;
+ priv->cck_txbbgain_table[11].ccktxbb_valuearray[6] = 0x05;
+ priv->cck_txbbgain_table[11].ccktxbb_valuearray[7] = 0x02;
+
+ priv->cck_txbbgain_table[12].ccktxbb_valuearray[0] = 0x1b;
+ priv->cck_txbbgain_table[12].ccktxbb_valuearray[1] = 0x1a;
+ priv->cck_txbbgain_table[12].ccktxbb_valuearray[2] = 0x17;
+ priv->cck_txbbgain_table[12].ccktxbb_valuearray[3] = 0x13;
+ priv->cck_txbbgain_table[12].ccktxbb_valuearray[4] = 0x0e;
+ priv->cck_txbbgain_table[12].ccktxbb_valuearray[5] = 0x09;
+ priv->cck_txbbgain_table[12].ccktxbb_valuearray[6] = 0x04;
+ priv->cck_txbbgain_table[12].ccktxbb_valuearray[7] = 0x02;
+
+ priv->cck_txbbgain_table[13].ccktxbb_valuearray[0] = 0x1a;
+ priv->cck_txbbgain_table[13].ccktxbb_valuearray[1] = 0x19;
+ priv->cck_txbbgain_table[13].ccktxbb_valuearray[2] = 0x16;
+ priv->cck_txbbgain_table[13].ccktxbb_valuearray[3] = 0x12;
+ priv->cck_txbbgain_table[13].ccktxbb_valuearray[4] = 0x0d;
+ priv->cck_txbbgain_table[13].ccktxbb_valuearray[5] = 0x09;
+ priv->cck_txbbgain_table[13].ccktxbb_valuearray[6] = 0x04;
+ priv->cck_txbbgain_table[13].ccktxbb_valuearray[7] = 0x02;
+
+ priv->cck_txbbgain_table[14].ccktxbb_valuearray[0] = 0x18;
+ priv->cck_txbbgain_table[14].ccktxbb_valuearray[1] = 0x17;
+ priv->cck_txbbgain_table[14].ccktxbb_valuearray[2] = 0x15;
+ priv->cck_txbbgain_table[14].ccktxbb_valuearray[3] = 0x11;
+ priv->cck_txbbgain_table[14].ccktxbb_valuearray[4] = 0x0c;
+ priv->cck_txbbgain_table[14].ccktxbb_valuearray[5] = 0x08;
+ priv->cck_txbbgain_table[14].ccktxbb_valuearray[6] = 0x04;
+ priv->cck_txbbgain_table[14].ccktxbb_valuearray[7] = 0x02;
+
+ priv->cck_txbbgain_table[15].ccktxbb_valuearray[0] = 0x17;
+ priv->cck_txbbgain_table[15].ccktxbb_valuearray[1] = 0x16;
+ priv->cck_txbbgain_table[15].ccktxbb_valuearray[2] = 0x13;
+ priv->cck_txbbgain_table[15].ccktxbb_valuearray[3] = 0x10;
+ priv->cck_txbbgain_table[15].ccktxbb_valuearray[4] = 0x0c;
+ priv->cck_txbbgain_table[15].ccktxbb_valuearray[5] = 0x08;
+ priv->cck_txbbgain_table[15].ccktxbb_valuearray[6] = 0x04;
+ priv->cck_txbbgain_table[15].ccktxbb_valuearray[7] = 0x02;
+
+ priv->cck_txbbgain_table[16].ccktxbb_valuearray[0] = 0x16;
+ priv->cck_txbbgain_table[16].ccktxbb_valuearray[1] = 0x15;
+ priv->cck_txbbgain_table[16].ccktxbb_valuearray[2] = 0x12;
+ priv->cck_txbbgain_table[16].ccktxbb_valuearray[3] = 0x0f;
+ priv->cck_txbbgain_table[16].ccktxbb_valuearray[4] = 0x0b;
+ priv->cck_txbbgain_table[16].ccktxbb_valuearray[5] = 0x07;
+ priv->cck_txbbgain_table[16].ccktxbb_valuearray[6] = 0x04;
+ priv->cck_txbbgain_table[16].ccktxbb_valuearray[7] = 0x01;
+
+ priv->cck_txbbgain_table[17].ccktxbb_valuearray[0] = 0x14;
+ priv->cck_txbbgain_table[17].ccktxbb_valuearray[1] = 0x14;
+ priv->cck_txbbgain_table[17].ccktxbb_valuearray[2] = 0x11;
+ priv->cck_txbbgain_table[17].ccktxbb_valuearray[3] = 0x0e;
+ priv->cck_txbbgain_table[17].ccktxbb_valuearray[4] = 0x0b;
+ priv->cck_txbbgain_table[17].ccktxbb_valuearray[5] = 0x07;
+ priv->cck_txbbgain_table[17].ccktxbb_valuearray[6] = 0x03;
+ priv->cck_txbbgain_table[17].ccktxbb_valuearray[7] = 0x02;
+
+ priv->cck_txbbgain_table[18].ccktxbb_valuearray[0] = 0x13;
+ priv->cck_txbbgain_table[18].ccktxbb_valuearray[1] = 0x13;
+ priv->cck_txbbgain_table[18].ccktxbb_valuearray[2] = 0x10;
+ priv->cck_txbbgain_table[18].ccktxbb_valuearray[3] = 0x0d;
+ priv->cck_txbbgain_table[18].ccktxbb_valuearray[4] = 0x0a;
+ priv->cck_txbbgain_table[18].ccktxbb_valuearray[5] = 0x06;
+ priv->cck_txbbgain_table[18].ccktxbb_valuearray[6] = 0x03;
+ priv->cck_txbbgain_table[18].ccktxbb_valuearray[7] = 0x01;
+
+ priv->cck_txbbgain_table[19].ccktxbb_valuearray[0] = 0x12;
+ priv->cck_txbbgain_table[19].ccktxbb_valuearray[1] = 0x12;
+ priv->cck_txbbgain_table[19].ccktxbb_valuearray[2] = 0x0f;
+ priv->cck_txbbgain_table[19].ccktxbb_valuearray[3] = 0x0c;
+ priv->cck_txbbgain_table[19].ccktxbb_valuearray[4] = 0x09;
+ priv->cck_txbbgain_table[19].ccktxbb_valuearray[5] = 0x06;
+ priv->cck_txbbgain_table[19].ccktxbb_valuearray[6] = 0x03;
+ priv->cck_txbbgain_table[19].ccktxbb_valuearray[7] = 0x01;
+
+ priv->cck_txbbgain_table[20].ccktxbb_valuearray[0] = 0x11;
+ priv->cck_txbbgain_table[20].ccktxbb_valuearray[1] = 0x11;
+ priv->cck_txbbgain_table[20].ccktxbb_valuearray[2] = 0x0f;
+ priv->cck_txbbgain_table[20].ccktxbb_valuearray[3] = 0x0c;
+ priv->cck_txbbgain_table[20].ccktxbb_valuearray[4] = 0x09;
+ priv->cck_txbbgain_table[20].ccktxbb_valuearray[5] = 0x06;
+ priv->cck_txbbgain_table[20].ccktxbb_valuearray[6] = 0x03;
+ priv->cck_txbbgain_table[20].ccktxbb_valuearray[7] = 0x01;
+
+ priv->cck_txbbgain_table[21].ccktxbb_valuearray[0] = 0x10;
+ priv->cck_txbbgain_table[21].ccktxbb_valuearray[1] = 0x10;
+ priv->cck_txbbgain_table[21].ccktxbb_valuearray[2] = 0x0e;
+ priv->cck_txbbgain_table[21].ccktxbb_valuearray[3] = 0x0b;
+ priv->cck_txbbgain_table[21].ccktxbb_valuearray[4] = 0x08;
+ priv->cck_txbbgain_table[21].ccktxbb_valuearray[5] = 0x05;
+ priv->cck_txbbgain_table[21].ccktxbb_valuearray[6] = 0x03;
+ priv->cck_txbbgain_table[21].ccktxbb_valuearray[7] = 0x01;
+
+ priv->cck_txbbgain_table[22].ccktxbb_valuearray[0] = 0x0f;
+ priv->cck_txbbgain_table[22].ccktxbb_valuearray[1] = 0x0f;
+ priv->cck_txbbgain_table[22].ccktxbb_valuearray[2] = 0x0d;
+ priv->cck_txbbgain_table[22].ccktxbb_valuearray[3] = 0x0b;
+ priv->cck_txbbgain_table[22].ccktxbb_valuearray[4] = 0x08;
+ priv->cck_txbbgain_table[22].ccktxbb_valuearray[5] = 0x05;
+ priv->cck_txbbgain_table[22].ccktxbb_valuearray[6] = 0x03;
+ priv->cck_txbbgain_table[22].ccktxbb_valuearray[7] = 0x01;
+
+ priv->cck_txbbgain_ch14_table[0].ccktxbb_valuearray[0] = 0x36;
+ priv->cck_txbbgain_ch14_table[0].ccktxbb_valuearray[1] = 0x35;
+ priv->cck_txbbgain_ch14_table[0].ccktxbb_valuearray[2] = 0x2e;
+ priv->cck_txbbgain_ch14_table[0].ccktxbb_valuearray[3] = 0x1b;
+ priv->cck_txbbgain_ch14_table[0].ccktxbb_valuearray[4] = 0x00;
+ priv->cck_txbbgain_ch14_table[0].ccktxbb_valuearray[5] = 0x00;
+ priv->cck_txbbgain_ch14_table[0].ccktxbb_valuearray[6] = 0x00;
+ priv->cck_txbbgain_ch14_table[0].ccktxbb_valuearray[7] = 0x00;
+
+ priv->cck_txbbgain_ch14_table[1].ccktxbb_valuearray[0] = 0x33;
+ priv->cck_txbbgain_ch14_table[1].ccktxbb_valuearray[1] = 0x32;
+ priv->cck_txbbgain_ch14_table[1].ccktxbb_valuearray[2] = 0x2b;
+ priv->cck_txbbgain_ch14_table[1].ccktxbb_valuearray[3] = 0x19;
+ priv->cck_txbbgain_ch14_table[1].ccktxbb_valuearray[4] = 0x00;
+ priv->cck_txbbgain_ch14_table[1].ccktxbb_valuearray[5] = 0x00;
+ priv->cck_txbbgain_ch14_table[1].ccktxbb_valuearray[6] = 0x00;
+ priv->cck_txbbgain_ch14_table[1].ccktxbb_valuearray[7] = 0x00;
+
+ priv->cck_txbbgain_ch14_table[2].ccktxbb_valuearray[0] = 0x30;
+ priv->cck_txbbgain_ch14_table[2].ccktxbb_valuearray[1] = 0x2f;
+ priv->cck_txbbgain_ch14_table[2].ccktxbb_valuearray[2] = 0x29;
+ priv->cck_txbbgain_ch14_table[2].ccktxbb_valuearray[3] = 0x18;
+ priv->cck_txbbgain_ch14_table[2].ccktxbb_valuearray[4] = 0x00;
+ priv->cck_txbbgain_ch14_table[2].ccktxbb_valuearray[5] = 0x00;
+ priv->cck_txbbgain_ch14_table[2].ccktxbb_valuearray[6] = 0x00;
+ priv->cck_txbbgain_ch14_table[2].ccktxbb_valuearray[7] = 0x00;
+
+ priv->cck_txbbgain_ch14_table[3].ccktxbb_valuearray[0] = 0x2d;
+ priv->cck_txbbgain_ch14_table[3].ccktxbb_valuearray[1] = 0x2d;
+ priv->cck_txbbgain_ch14_table[3].ccktxbb_valuearray[2] = 0x27;
+ priv->cck_txbbgain_ch14_table[3].ccktxbb_valuearray[3] = 0x17;
+ priv->cck_txbbgain_ch14_table[3].ccktxbb_valuearray[4] = 0x00;
+ priv->cck_txbbgain_ch14_table[3].ccktxbb_valuearray[5] = 0x00;
+ priv->cck_txbbgain_ch14_table[3].ccktxbb_valuearray[6] = 0x00;
+ priv->cck_txbbgain_ch14_table[3].ccktxbb_valuearray[7] = 0x00;
+
+ priv->cck_txbbgain_ch14_table[4].ccktxbb_valuearray[0] = 0x2b;
+ priv->cck_txbbgain_ch14_table[4].ccktxbb_valuearray[1] = 0x2a;
+ priv->cck_txbbgain_ch14_table[4].ccktxbb_valuearray[2] = 0x25;
+ priv->cck_txbbgain_ch14_table[4].ccktxbb_valuearray[3] = 0x15;
+ priv->cck_txbbgain_ch14_table[4].ccktxbb_valuearray[4] = 0x00;
+ priv->cck_txbbgain_ch14_table[4].ccktxbb_valuearray[5] = 0x00;
+ priv->cck_txbbgain_ch14_table[4].ccktxbb_valuearray[6] = 0x00;
+ priv->cck_txbbgain_ch14_table[4].ccktxbb_valuearray[7] = 0x00;
+
+ priv->cck_txbbgain_ch14_table[5].ccktxbb_valuearray[0] = 0x28;
+ priv->cck_txbbgain_ch14_table[5].ccktxbb_valuearray[1] = 0x28;
+ priv->cck_txbbgain_ch14_table[5].ccktxbb_valuearray[2] = 0x22;
+ priv->cck_txbbgain_ch14_table[5].ccktxbb_valuearray[3] = 0x14;
+ priv->cck_txbbgain_ch14_table[5].ccktxbb_valuearray[4] = 0x00;
+ priv->cck_txbbgain_ch14_table[5].ccktxbb_valuearray[5] = 0x00;
+ priv->cck_txbbgain_ch14_table[5].ccktxbb_valuearray[6] = 0x00;
+ priv->cck_txbbgain_ch14_table[5].ccktxbb_valuearray[7] = 0x00;
+
+ priv->cck_txbbgain_ch14_table[6].ccktxbb_valuearray[0] = 0x26;
+ priv->cck_txbbgain_ch14_table[6].ccktxbb_valuearray[1] = 0x25;
+ priv->cck_txbbgain_ch14_table[6].ccktxbb_valuearray[2] = 0x21;
+ priv->cck_txbbgain_ch14_table[6].ccktxbb_valuearray[3] = 0x13;
+ priv->cck_txbbgain_ch14_table[6].ccktxbb_valuearray[4] = 0x00;
+ priv->cck_txbbgain_ch14_table[6].ccktxbb_valuearray[5] = 0x00;
+ priv->cck_txbbgain_ch14_table[6].ccktxbb_valuearray[6] = 0x00;
+ priv->cck_txbbgain_ch14_table[6].ccktxbb_valuearray[7] = 0x00;
+
+ priv->cck_txbbgain_ch14_table[7].ccktxbb_valuearray[0] = 0x24;
+ priv->cck_txbbgain_ch14_table[7].ccktxbb_valuearray[1] = 0x23;
+ priv->cck_txbbgain_ch14_table[7].ccktxbb_valuearray[2] = 0x1f;
+ priv->cck_txbbgain_ch14_table[7].ccktxbb_valuearray[3] = 0x12;
+ priv->cck_txbbgain_ch14_table[7].ccktxbb_valuearray[4] = 0x00;
+ priv->cck_txbbgain_ch14_table[7].ccktxbb_valuearray[5] = 0x00;
+ priv->cck_txbbgain_ch14_table[7].ccktxbb_valuearray[6] = 0x00;
+ priv->cck_txbbgain_ch14_table[7].ccktxbb_valuearray[7] = 0x00;
+
+ priv->cck_txbbgain_ch14_table[8].ccktxbb_valuearray[0] = 0x22;
+ priv->cck_txbbgain_ch14_table[8].ccktxbb_valuearray[1] = 0x21;
+ priv->cck_txbbgain_ch14_table[8].ccktxbb_valuearray[2] = 0x1d;
+ priv->cck_txbbgain_ch14_table[8].ccktxbb_valuearray[3] = 0x11;
+ priv->cck_txbbgain_ch14_table[8].ccktxbb_valuearray[4] = 0x00;
+ priv->cck_txbbgain_ch14_table[8].ccktxbb_valuearray[5] = 0x00;
+ priv->cck_txbbgain_ch14_table[8].ccktxbb_valuearray[6] = 0x00;
+ priv->cck_txbbgain_ch14_table[8].ccktxbb_valuearray[7] = 0x00;
+
+ priv->cck_txbbgain_ch14_table[9].ccktxbb_valuearray[0] = 0x20;
+ priv->cck_txbbgain_ch14_table[9].ccktxbb_valuearray[1] = 0x20;
+ priv->cck_txbbgain_ch14_table[9].ccktxbb_valuearray[2] = 0x1b;
+ priv->cck_txbbgain_ch14_table[9].ccktxbb_valuearray[3] = 0x10;
+ priv->cck_txbbgain_ch14_table[9].ccktxbb_valuearray[4] = 0x00;
+ priv->cck_txbbgain_ch14_table[9].ccktxbb_valuearray[5] = 0x00;
+ priv->cck_txbbgain_ch14_table[9].ccktxbb_valuearray[6] = 0x00;
+ priv->cck_txbbgain_ch14_table[9].ccktxbb_valuearray[7] = 0x00;
+
+ priv->cck_txbbgain_ch14_table[10].ccktxbb_valuearray[0] = 0x1f;
+ priv->cck_txbbgain_ch14_table[10].ccktxbb_valuearray[1] = 0x1e;
+ priv->cck_txbbgain_ch14_table[10].ccktxbb_valuearray[2] = 0x1a;
+ priv->cck_txbbgain_ch14_table[10].ccktxbb_valuearray[3] = 0x0f;
+ priv->cck_txbbgain_ch14_table[10].ccktxbb_valuearray[4] = 0x00;
+ priv->cck_txbbgain_ch14_table[10].ccktxbb_valuearray[5] = 0x00;
+ priv->cck_txbbgain_ch14_table[10].ccktxbb_valuearray[6] = 0x00;
+ priv->cck_txbbgain_ch14_table[10].ccktxbb_valuearray[7] = 0x00;
+
+ priv->cck_txbbgain_ch14_table[11].ccktxbb_valuearray[0] = 0x1d;
+ priv->cck_txbbgain_ch14_table[11].ccktxbb_valuearray[1] = 0x1c;
+ priv->cck_txbbgain_ch14_table[11].ccktxbb_valuearray[2] = 0x18;
+ priv->cck_txbbgain_ch14_table[11].ccktxbb_valuearray[3] = 0x0e;
+ priv->cck_txbbgain_ch14_table[11].ccktxbb_valuearray[4] = 0x00;
+ priv->cck_txbbgain_ch14_table[11].ccktxbb_valuearray[5] = 0x00;
+ priv->cck_txbbgain_ch14_table[11].ccktxbb_valuearray[6] = 0x00;
+ priv->cck_txbbgain_ch14_table[11].ccktxbb_valuearray[7] = 0x00;
+
+ priv->cck_txbbgain_ch14_table[12].ccktxbb_valuearray[0] = 0x1b;
+ priv->cck_txbbgain_ch14_table[12].ccktxbb_valuearray[1] = 0x1a;
+ priv->cck_txbbgain_ch14_table[12].ccktxbb_valuearray[2] = 0x17;
+ priv->cck_txbbgain_ch14_table[12].ccktxbb_valuearray[3] = 0x0e;
+ priv->cck_txbbgain_ch14_table[12].ccktxbb_valuearray[4] = 0x00;
+ priv->cck_txbbgain_ch14_table[12].ccktxbb_valuearray[5] = 0x00;
+ priv->cck_txbbgain_ch14_table[12].ccktxbb_valuearray[6] = 0x00;
+ priv->cck_txbbgain_ch14_table[12].ccktxbb_valuearray[7] = 0x00;
+
+ priv->cck_txbbgain_ch14_table[13].ccktxbb_valuearray[0] = 0x1a;
+ priv->cck_txbbgain_ch14_table[13].ccktxbb_valuearray[1] = 0x19;
+ priv->cck_txbbgain_ch14_table[13].ccktxbb_valuearray[2] = 0x16;
+ priv->cck_txbbgain_ch14_table[13].ccktxbb_valuearray[3] = 0x0d;
+ priv->cck_txbbgain_ch14_table[13].ccktxbb_valuearray[4] = 0x00;
+ priv->cck_txbbgain_ch14_table[13].ccktxbb_valuearray[5] = 0x00;
+ priv->cck_txbbgain_ch14_table[13].ccktxbb_valuearray[6] = 0x00;
+ priv->cck_txbbgain_ch14_table[13].ccktxbb_valuearray[7] = 0x00;
+
+ priv->cck_txbbgain_ch14_table[14].ccktxbb_valuearray[0] = 0x18;
+ priv->cck_txbbgain_ch14_table[14].ccktxbb_valuearray[1] = 0x17;
+ priv->cck_txbbgain_ch14_table[14].ccktxbb_valuearray[2] = 0x15;
+ priv->cck_txbbgain_ch14_table[14].ccktxbb_valuearray[3] = 0x0c;
+ priv->cck_txbbgain_ch14_table[14].ccktxbb_valuearray[4] = 0x00;
+ priv->cck_txbbgain_ch14_table[14].ccktxbb_valuearray[5] = 0x00;
+ priv->cck_txbbgain_ch14_table[14].ccktxbb_valuearray[6] = 0x00;
+ priv->cck_txbbgain_ch14_table[14].ccktxbb_valuearray[7] = 0x00;
+
+ priv->cck_txbbgain_ch14_table[15].ccktxbb_valuearray[0] = 0x17;
+ priv->cck_txbbgain_ch14_table[15].ccktxbb_valuearray[1] = 0x16;
+ priv->cck_txbbgain_ch14_table[15].ccktxbb_valuearray[2] = 0x13;
+ priv->cck_txbbgain_ch14_table[15].ccktxbb_valuearray[3] = 0x0b;
+ priv->cck_txbbgain_ch14_table[15].ccktxbb_valuearray[4] = 0x00;
+ priv->cck_txbbgain_ch14_table[15].ccktxbb_valuearray[5] = 0x00;
+ priv->cck_txbbgain_ch14_table[15].ccktxbb_valuearray[6] = 0x00;
+ priv->cck_txbbgain_ch14_table[15].ccktxbb_valuearray[7] = 0x00;
+
+ priv->cck_txbbgain_ch14_table[16].ccktxbb_valuearray[0] = 0x16;
+ priv->cck_txbbgain_ch14_table[16].ccktxbb_valuearray[1] = 0x15;
+ priv->cck_txbbgain_ch14_table[16].ccktxbb_valuearray[2] = 0x12;
+ priv->cck_txbbgain_ch14_table[16].ccktxbb_valuearray[3] = 0x0b;
+ priv->cck_txbbgain_ch14_table[16].ccktxbb_valuearray[4] = 0x00;
+ priv->cck_txbbgain_ch14_table[16].ccktxbb_valuearray[5] = 0x00;
+ priv->cck_txbbgain_ch14_table[16].ccktxbb_valuearray[6] = 0x00;
+ priv->cck_txbbgain_ch14_table[16].ccktxbb_valuearray[7] = 0x00;
+
+ priv->cck_txbbgain_ch14_table[17].ccktxbb_valuearray[0] = 0x14;
+ priv->cck_txbbgain_ch14_table[17].ccktxbb_valuearray[1] = 0x14;
+ priv->cck_txbbgain_ch14_table[17].ccktxbb_valuearray[2] = 0x11;
+ priv->cck_txbbgain_ch14_table[17].ccktxbb_valuearray[3] = 0x0a;
+ priv->cck_txbbgain_ch14_table[17].ccktxbb_valuearray[4] = 0x00;
+ priv->cck_txbbgain_ch14_table[17].ccktxbb_valuearray[5] = 0x00;
+ priv->cck_txbbgain_ch14_table[17].ccktxbb_valuearray[6] = 0x00;
+ priv->cck_txbbgain_ch14_table[17].ccktxbb_valuearray[7] = 0x00;
+
+ priv->cck_txbbgain_ch14_table[18].ccktxbb_valuearray[0] = 0x13;
+ priv->cck_txbbgain_ch14_table[18].ccktxbb_valuearray[1] = 0x13;
+ priv->cck_txbbgain_ch14_table[18].ccktxbb_valuearray[2] = 0x10;
+ priv->cck_txbbgain_ch14_table[18].ccktxbb_valuearray[3] = 0x0a;
+ priv->cck_txbbgain_ch14_table[18].ccktxbb_valuearray[4] = 0x00;
+ priv->cck_txbbgain_ch14_table[18].ccktxbb_valuearray[5] = 0x00;
+ priv->cck_txbbgain_ch14_table[18].ccktxbb_valuearray[6] = 0x00;
+ priv->cck_txbbgain_ch14_table[18].ccktxbb_valuearray[7] = 0x00;
+
+ priv->cck_txbbgain_ch14_table[19].ccktxbb_valuearray[0] = 0x12;
+ priv->cck_txbbgain_ch14_table[19].ccktxbb_valuearray[1] = 0x12;
+ priv->cck_txbbgain_ch14_table[19].ccktxbb_valuearray[2] = 0x0f;
+ priv->cck_txbbgain_ch14_table[19].ccktxbb_valuearray[3] = 0x09;
+ priv->cck_txbbgain_ch14_table[19].ccktxbb_valuearray[4] = 0x00;
+ priv->cck_txbbgain_ch14_table[19].ccktxbb_valuearray[5] = 0x00;
+ priv->cck_txbbgain_ch14_table[19].ccktxbb_valuearray[6] = 0x00;
+ priv->cck_txbbgain_ch14_table[19].ccktxbb_valuearray[7] = 0x00;
+
+ priv->cck_txbbgain_ch14_table[20].ccktxbb_valuearray[0] = 0x11;
+ priv->cck_txbbgain_ch14_table[20].ccktxbb_valuearray[1] = 0x11;
+ priv->cck_txbbgain_ch14_table[20].ccktxbb_valuearray[2] = 0x0f;
+ priv->cck_txbbgain_ch14_table[20].ccktxbb_valuearray[3] = 0x09;
+ priv->cck_txbbgain_ch14_table[20].ccktxbb_valuearray[4] = 0x00;
+ priv->cck_txbbgain_ch14_table[20].ccktxbb_valuearray[5] = 0x00;
+ priv->cck_txbbgain_ch14_table[20].ccktxbb_valuearray[6] = 0x00;
+ priv->cck_txbbgain_ch14_table[20].ccktxbb_valuearray[7] = 0x00;
+
+ priv->cck_txbbgain_ch14_table[21].ccktxbb_valuearray[0] = 0x10;
+ priv->cck_txbbgain_ch14_table[21].ccktxbb_valuearray[1] = 0x10;
+ priv->cck_txbbgain_ch14_table[21].ccktxbb_valuearray[2] = 0x0e;
+ priv->cck_txbbgain_ch14_table[21].ccktxbb_valuearray[3] = 0x08;
+ priv->cck_txbbgain_ch14_table[21].ccktxbb_valuearray[4] = 0x00;
+ priv->cck_txbbgain_ch14_table[21].ccktxbb_valuearray[5] = 0x00;
+ priv->cck_txbbgain_ch14_table[21].ccktxbb_valuearray[6] = 0x00;
+ priv->cck_txbbgain_ch14_table[21].ccktxbb_valuearray[7] = 0x00;
+
+ priv->cck_txbbgain_ch14_table[22].ccktxbb_valuearray[0] = 0x0f;
+ priv->cck_txbbgain_ch14_table[22].ccktxbb_valuearray[1] = 0x0f;
+ priv->cck_txbbgain_ch14_table[22].ccktxbb_valuearray[2] = 0x0d;
+ priv->cck_txbbgain_ch14_table[22].ccktxbb_valuearray[3] = 0x08;
+ priv->cck_txbbgain_ch14_table[22].ccktxbb_valuearray[4] = 0x00;
+ priv->cck_txbbgain_ch14_table[22].ccktxbb_valuearray[5] = 0x00;
+ priv->cck_txbbgain_ch14_table[22].ccktxbb_valuearray[6] = 0x00;
+ priv->cck_txbbgain_ch14_table[22].ccktxbb_valuearray[7] = 0x00;
+
+ priv->btxpower_tracking = true;
+ priv->txpower_count = 0;
+ priv->btxpower_trackingInit = false;
+
+}
+
+static void dm_InitializeTXPowerTracking_ThermalMeter(struct net_device *dev)
+{
+ struct r8192_priv *priv = rtllib_priv(dev);
+
+
+ if (priv->rtllib->FwRWRF)
+ priv->btxpower_tracking = true;
+ else
+ priv->btxpower_tracking = false;
+ priv->txpower_count = 0;
+ priv->btxpower_trackingInit = false;
+ RT_TRACE(COMP_POWER_TRACKING, "pMgntInfo->bTXPowerTracking = %d\n",
+ priv->btxpower_tracking);
+}
+
+void dm_initialize_txpower_tracking(struct net_device *dev)
+{
+ struct r8192_priv *priv = rtllib_priv(dev);
+
+ if (priv->IC_Cut >= IC_VersionCut_D)
+ dm_InitializeTXPowerTracking_TSSI(dev);
+ else
+ dm_InitializeTXPowerTracking_ThermalMeter(dev);
+}
+
+static void dm_CheckTXPowerTracking_TSSI(struct net_device *dev)
+{
+ struct r8192_priv *priv = rtllib_priv(dev);
+ static u32 tx_power_track_counter;
+
+ RT_TRACE(COMP_POWER_TRACKING, "%s()\n", __func__);
+ if (read_nic_byte(dev, 0x11e) == 1)
+ return;
+ if (!priv->btxpower_tracking)
+ return;
+ tx_power_track_counter++;
+
+
+ if (tx_power_track_counter >= 180) {
+ queue_delayed_work_rsl(priv->priv_wq, &priv->txpower_tracking_wq, 0);
+ tx_power_track_counter = 0;
+ }
+
+}
+static void dm_CheckTXPowerTracking_ThermalMeter(struct net_device *dev)
+{
+ struct r8192_priv *priv = rtllib_priv(dev);
+ static u8 TM_Trigger;
+ u8 TxPowerCheckCnt = 0;
+
+ if (IS_HARDWARE_TYPE_8192SE(dev))
+ TxPowerCheckCnt = 5;
+ else
+ TxPowerCheckCnt = 2;
+ if (!priv->btxpower_tracking)
+ return;
+
+ if (priv->txpower_count <= TxPowerCheckCnt) {
+ priv->txpower_count++;
+ return;
+ }
+
+ if (!TM_Trigger) {
+ {
+ rtl8192_phy_SetRFReg(dev, RF90_PATH_A, 0x02, bMask12Bits, 0x4d);
+ rtl8192_phy_SetRFReg(dev, RF90_PATH_A, 0x02, bMask12Bits, 0x4f);
+ rtl8192_phy_SetRFReg(dev, RF90_PATH_A, 0x02, bMask12Bits, 0x4d);
+ rtl8192_phy_SetRFReg(dev, RF90_PATH_A, 0x02, bMask12Bits, 0x4f);
+ }
+ TM_Trigger = 1;
+ return;
+ }
+ netdev_info(dev, "===============>Schedule TxPowerTrackingWorkItem\n");
+ queue_delayed_work_rsl(priv->priv_wq, &priv->txpower_tracking_wq, 0);
+ TM_Trigger = 0;
+
+}
+
+static void dm_check_txpower_tracking(struct net_device *dev)
+{
+ struct r8192_priv *priv = rtllib_priv(dev);
+
+ if (priv->IC_Cut >= IC_VersionCut_D)
+ dm_CheckTXPowerTracking_TSSI(dev);
+ else
+ dm_CheckTXPowerTracking_ThermalMeter(dev);
+}
+
+static void dm_CCKTxPowerAdjust_TSSI(struct net_device *dev, bool bInCH14)
+{
+ u32 TempVal;
+ struct r8192_priv *priv = rtllib_priv(dev);
+
+ TempVal = 0;
+ if (!bInCH14) {
+ TempVal = (u32)(priv->cck_txbbgain_table[(u8)(priv->CCKPresentAttentuation)].ccktxbb_valuearray[0] +
+ (priv->cck_txbbgain_table[(u8)(priv->CCKPresentAttentuation)].ccktxbb_valuearray[1]<<8));
+
+ rtl8192_setBBreg(dev, rCCK0_TxFilter1, bMaskHWord, TempVal);
+ TempVal = (u32)(priv->cck_txbbgain_table[(u8)(priv->CCKPresentAttentuation)].ccktxbb_valuearray[2] +
+ (priv->cck_txbbgain_table[(u8)(priv->CCKPresentAttentuation)].ccktxbb_valuearray[3]<<8) +
+ (priv->cck_txbbgain_table[(u8)(priv->CCKPresentAttentuation)].ccktxbb_valuearray[4]<<16)+
+ (priv->cck_txbbgain_table[(u8)(priv->CCKPresentAttentuation)].ccktxbb_valuearray[5]<<24));
+ rtl8192_setBBreg(dev, rCCK0_TxFilter2, bMaskDWord, TempVal);
+ TempVal = (u32)(priv->cck_txbbgain_table[(u8)(priv->CCKPresentAttentuation)].ccktxbb_valuearray[6] +
+ (priv->cck_txbbgain_table[(u8)(priv->CCKPresentAttentuation)].ccktxbb_valuearray[7]<<8));
+
+ rtl8192_setBBreg(dev, rCCK0_DebugPort, bMaskLWord, TempVal);
+ } else {
+ TempVal = (u32)(priv->cck_txbbgain_ch14_table[(u8)(priv->CCKPresentAttentuation)].ccktxbb_valuearray[0] +
+ (priv->cck_txbbgain_ch14_table[(u8)(priv->CCKPresentAttentuation)].ccktxbb_valuearray[1]<<8));
+
+ rtl8192_setBBreg(dev, rCCK0_TxFilter1, bMaskHWord, TempVal);
+ TempVal = (u32)(priv->cck_txbbgain_ch14_table[(u8)(priv->CCKPresentAttentuation)].ccktxbb_valuearray[2] +
+ (priv->cck_txbbgain_ch14_table[(u8)(priv->CCKPresentAttentuation)].ccktxbb_valuearray[3]<<8) +
+ (priv->cck_txbbgain_ch14_table[(u8)(priv->CCKPresentAttentuation)].ccktxbb_valuearray[4]<<16)+
+ (priv->cck_txbbgain_ch14_table[(u8)(priv->CCKPresentAttentuation)].ccktxbb_valuearray[5]<<24));
+ rtl8192_setBBreg(dev, rCCK0_TxFilter2, bMaskDWord, TempVal);
+ TempVal = (u32)(priv->cck_txbbgain_ch14_table[(u8)(priv->CCKPresentAttentuation)].ccktxbb_valuearray[6] +
+ (priv->cck_txbbgain_ch14_table[(u8)(priv->CCKPresentAttentuation)].ccktxbb_valuearray[7]<<8));
+
+ rtl8192_setBBreg(dev, rCCK0_DebugPort, bMaskLWord, TempVal);
+ }
+
+
+}
+
+static void dm_CCKTxPowerAdjust_ThermalMeter(struct net_device *dev, bool bInCH14)
+{
+ u32 TempVal;
+ struct r8192_priv *priv = rtllib_priv(dev);
+
+ TempVal = 0;
+ if (!bInCH14) {
+ TempVal = CCKSwingTable_Ch1_Ch13[priv->CCK_index][0] +
+ (CCKSwingTable_Ch1_Ch13[priv->CCK_index][1]<<8);
+ rtl8192_setBBreg(dev, rCCK0_TxFilter1, bMaskHWord, TempVal);
+ RT_TRACE(COMP_POWER_TRACKING, "CCK not chnl 14, reg 0x%x = 0x%x\n",
+ rCCK0_TxFilter1, TempVal);
+ TempVal = CCKSwingTable_Ch1_Ch13[priv->CCK_index][2] +
+ (CCKSwingTable_Ch1_Ch13[priv->CCK_index][3]<<8) +
+ (CCKSwingTable_Ch1_Ch13[priv->CCK_index][4]<<16)+
+ (CCKSwingTable_Ch1_Ch13[priv->CCK_index][5]<<24);
+ rtl8192_setBBreg(dev, rCCK0_TxFilter2, bMaskDWord, TempVal);
+ RT_TRACE(COMP_POWER_TRACKING, "CCK not chnl 14, reg 0x%x = 0x%x\n",
+ rCCK0_TxFilter2, TempVal);
+ TempVal = CCKSwingTable_Ch1_Ch13[priv->CCK_index][6] +
+ (CCKSwingTable_Ch1_Ch13[priv->CCK_index][7]<<8);
+
+ rtl8192_setBBreg(dev, rCCK0_DebugPort, bMaskLWord, TempVal);
+ RT_TRACE(COMP_POWER_TRACKING, "CCK not chnl 14, reg 0x%x = 0x%x\n",
+ rCCK0_DebugPort, TempVal);
+ } else {
+ TempVal = CCKSwingTable_Ch14[priv->CCK_index][0] +
+ (CCKSwingTable_Ch14[priv->CCK_index][1]<<8);
+
+ rtl8192_setBBreg(dev, rCCK0_TxFilter1, bMaskHWord, TempVal);
+ RT_TRACE(COMP_POWER_TRACKING, "CCK chnl 14, reg 0x%x = 0x%x\n",
+ rCCK0_TxFilter1, TempVal);
+ TempVal = CCKSwingTable_Ch14[priv->CCK_index][2] +
+ (CCKSwingTable_Ch14[priv->CCK_index][3]<<8) +
+ (CCKSwingTable_Ch14[priv->CCK_index][4]<<16)+
+ (CCKSwingTable_Ch14[priv->CCK_index][5]<<24);
+ rtl8192_setBBreg(dev, rCCK0_TxFilter2, bMaskDWord, TempVal);
+ RT_TRACE(COMP_POWER_TRACKING, "CCK chnl 14, reg 0x%x = 0x%x\n",
+ rCCK0_TxFilter2, TempVal);
+ TempVal = CCKSwingTable_Ch14[priv->CCK_index][6] +
+ (CCKSwingTable_Ch14[priv->CCK_index][7]<<8);
+
+ rtl8192_setBBreg(dev, rCCK0_DebugPort, bMaskLWord, TempVal);
+ RT_TRACE(COMP_POWER_TRACKING, "CCK chnl 14, reg 0x%x = 0x%x\n",
+ rCCK0_DebugPort, TempVal);
+ }
+ }
+
+void dm_cck_txpower_adjust(struct net_device *dev, bool binch14)
+{
+ struct r8192_priv *priv = rtllib_priv(dev);
+
+ if (priv->IC_Cut >= IC_VersionCut_D)
+ dm_CCKTxPowerAdjust_TSSI(dev, binch14);
+ else
+ dm_CCKTxPowerAdjust_ThermalMeter(dev, binch14);
+}
+
+static void dm_txpower_reset_recovery(struct net_device *dev)
+{
+ struct r8192_priv *priv = rtllib_priv(dev);
+
+ RT_TRACE(COMP_POWER_TRACKING, "Start Reset Recovery ==>\n");
+ rtl8192_setBBreg(dev, rOFDM0_XATxIQImbalance, bMaskDWord,
+ priv->txbbgain_table[priv->rfa_txpowertrackingindex].txbbgain_value);
+ RT_TRACE(COMP_POWER_TRACKING, "Reset Recovery: Fill in 0xc80 is %08x\n",
+ priv->txbbgain_table[priv->rfa_txpowertrackingindex].txbbgain_value);
+ RT_TRACE(COMP_POWER_TRACKING, "Reset Recovery: Fill in RFA_txPowerTrackingIndex is %x\n",
+ priv->rfa_txpowertrackingindex);
+ RT_TRACE(COMP_POWER_TRACKING, "Reset Recovery : RF A I/Q Amplify Gain is %ld\n",
+ priv->txbbgain_table[priv->rfa_txpowertrackingindex].txbb_iq_amplifygain);
+ RT_TRACE(COMP_POWER_TRACKING, "Reset Recovery: CCK Attenuation is %d dB\n",
+ priv->CCKPresentAttentuation);
+ dm_cck_txpower_adjust(dev, priv->bcck_in_ch14);
+
+ rtl8192_setBBreg(dev, rOFDM0_XCTxIQImbalance, bMaskDWord,
+ priv->txbbgain_table[priv->rfc_txpowertrackingindex].txbbgain_value);
+ RT_TRACE(COMP_POWER_TRACKING, "Reset Recovery: Fill in 0xc90 is %08x\n",
+ priv->txbbgain_table[priv->rfc_txpowertrackingindex].txbbgain_value);
+ RT_TRACE(COMP_POWER_TRACKING, "Reset Recovery: Fill in RFC_txPowerTrackingIndex is %x\n",
+ priv->rfc_txpowertrackingindex);
+ RT_TRACE(COMP_POWER_TRACKING, "Reset Recovery : RF C I/Q Amplify Gain is %ld\n",
+ priv->txbbgain_table[priv->rfc_txpowertrackingindex].txbb_iq_amplifygain);
+
+}
+
+void dm_restore_dynamic_mechanism_state(struct net_device *dev)
+{
+ struct r8192_priv *priv = rtllib_priv(dev);
+ u32 reg_ratr = priv->rate_adaptive.last_ratr;
+ u32 ratr_value;
+
+ if (!priv->up) {
+ RT_TRACE(COMP_RATE, "<---- dm_restore_dynamic_mechanism_state(): driver is going to unload\n");
+ return;
+ }
+
+ if (priv->rate_adaptive.rate_adaptive_disabled)
+ return;
+ if (!(priv->rtllib->mode == WIRELESS_MODE_N_24G ||
+ priv->rtllib->mode == WIRELESS_MODE_N_5G))
+ return;
+ ratr_value = reg_ratr;
+ if (priv->rf_type == RF_1T2R)
+ ratr_value &= ~(RATE_ALL_OFDM_2SS);
+ write_nic_dword(dev, RATR0, ratr_value);
+ write_nic_byte(dev, UFWP, 1);
+ if (priv->btxpower_trackingInit && priv->btxpower_tracking)
+ dm_txpower_reset_recovery(dev);
+
+ dm_bb_initialgain_restore(dev);
+
+}
+
+static void dm_bb_initialgain_restore(struct net_device *dev)
+{
+ struct r8192_priv *priv = rtllib_priv(dev);
+ u32 bit_mask = 0x7f;
+
+ if (dm_digtable.dig_algorithm == DIG_ALGO_BY_RSSI)
+ return;
+
+ rtl8192_setBBreg(dev, UFWP, bMaskByte1, 0x8);
+ rtl8192_setBBreg(dev, rOFDM0_XAAGCCore1, bit_mask, (u32)priv->initgain_backup.xaagccore1);
+ rtl8192_setBBreg(dev, rOFDM0_XBAGCCore1, bit_mask, (u32)priv->initgain_backup.xbagccore1);
+ rtl8192_setBBreg(dev, rOFDM0_XCAGCCore1, bit_mask, (u32)priv->initgain_backup.xcagccore1);
+ rtl8192_setBBreg(dev, rOFDM0_XDAGCCore1, bit_mask, (u32)priv->initgain_backup.xdagccore1);
+ bit_mask = bMaskByte2;
+ rtl8192_setBBreg(dev, rCCK0_CCA, bit_mask, (u32)priv->initgain_backup.cca);
+
+ RT_TRACE(COMP_DIG, "dm_BBInitialGainRestore 0xc50 is %x\n", priv->initgain_backup.xaagccore1);
+ RT_TRACE(COMP_DIG, "dm_BBInitialGainRestore 0xc58 is %x\n", priv->initgain_backup.xbagccore1);
+ RT_TRACE(COMP_DIG, "dm_BBInitialGainRestore 0xc60 is %x\n", priv->initgain_backup.xcagccore1);
+ RT_TRACE(COMP_DIG, "dm_BBInitialGainRestore 0xc68 is %x\n", priv->initgain_backup.xdagccore1);
+ RT_TRACE(COMP_DIG, "dm_BBInitialGainRestore 0xa0a is %x\n", priv->initgain_backup.cca);
+ rtl8192_setBBreg(dev, UFWP, bMaskByte1, 0x1);
+
+}
+
+
+void dm_backup_dynamic_mechanism_state(struct net_device *dev)
+{
+ struct r8192_priv *priv = rtllib_priv(dev);
+
+ priv->bswitch_fsync = false;
+ priv->bfsync_processing = false;
+ dm_bb_initialgain_backup(dev);
+
+}
+
+
+static void dm_bb_initialgain_backup(struct net_device *dev)
+{
+ struct r8192_priv *priv = rtllib_priv(dev);
+ u32 bit_mask = bMaskByte0;
+
+ if (dm_digtable.dig_algorithm == DIG_ALGO_BY_RSSI)
+ return;
+
+ rtl8192_setBBreg(dev, UFWP, bMaskByte1, 0x8);
+ priv->initgain_backup.xaagccore1 = (u8)rtl8192_QueryBBReg(dev, rOFDM0_XAAGCCore1, bit_mask);
+ priv->initgain_backup.xbagccore1 = (u8)rtl8192_QueryBBReg(dev, rOFDM0_XBAGCCore1, bit_mask);
+ priv->initgain_backup.xcagccore1 = (u8)rtl8192_QueryBBReg(dev, rOFDM0_XCAGCCore1, bit_mask);
+ priv->initgain_backup.xdagccore1 = (u8)rtl8192_QueryBBReg(dev, rOFDM0_XDAGCCore1, bit_mask);
+ bit_mask = bMaskByte2;
+ priv->initgain_backup.cca = (u8)rtl8192_QueryBBReg(dev, rCCK0_CCA, bit_mask);
+
+ RT_TRACE(COMP_DIG, "BBInitialGainBackup 0xc50 is %x\n", priv->initgain_backup.xaagccore1);
+ RT_TRACE(COMP_DIG, "BBInitialGainBackup 0xc58 is %x\n", priv->initgain_backup.xbagccore1);
+ RT_TRACE(COMP_DIG, "BBInitialGainBackup 0xc60 is %x\n", priv->initgain_backup.xcagccore1);
+ RT_TRACE(COMP_DIG, "BBInitialGainBackup 0xc68 is %x\n", priv->initgain_backup.xdagccore1);
+ RT_TRACE(COMP_DIG, "BBInitialGainBackup 0xa0a is %x\n", priv->initgain_backup.cca);
+
+}
+
+void dm_change_dynamic_initgain_thresh(struct net_device *dev,
+ u32 dm_type, u32 dm_value)
+{
+ if (dm_type == DIG_TYPE_THRESH_HIGH) {
+ dm_digtable.rssi_high_thresh = dm_value;
+ } else if (dm_type == DIG_TYPE_THRESH_LOW) {
+ dm_digtable.rssi_low_thresh = dm_value;
+ } else if (dm_type == DIG_TYPE_THRESH_HIGHPWR_HIGH) {
+ dm_digtable.rssi_high_power_highthresh = dm_value;
+ } else if (dm_type == DIG_TYPE_THRESH_HIGHPWR_LOW) {
+ dm_digtable.rssi_high_power_lowthresh = dm_value;
+ } else if (dm_type == DIG_TYPE_ENABLE) {
+ dm_digtable.dig_state = DM_STA_DIG_MAX;
+ dm_digtable.dig_enable_flag = true;
+ } else if (dm_type == DIG_TYPE_DISABLE) {
+ dm_digtable.dig_state = DM_STA_DIG_MAX;
+ dm_digtable.dig_enable_flag = false;
+ } else if (dm_type == DIG_TYPE_DBG_MODE) {
+ if (dm_value >= DM_DBG_MAX)
+ dm_value = DM_DBG_OFF;
+ dm_digtable.dbg_mode = (u8)dm_value;
+ } else if (dm_type == DIG_TYPE_RSSI) {
+ if (dm_value > 100)
+ dm_value = 30;
+ dm_digtable.rssi_val = (long)dm_value;
+ } else if (dm_type == DIG_TYPE_ALGORITHM) {
+ if (dm_value >= DIG_ALGO_MAX)
+ dm_value = DIG_ALGO_BY_FALSE_ALARM;
+ if (dm_digtable.dig_algorithm != (u8)dm_value)
+ dm_digtable.dig_algorithm_switch = 1;
+ dm_digtable.dig_algorithm = (u8)dm_value;
+ } else if (dm_type == DIG_TYPE_BACKOFF) {
+ if (dm_value > 30)
+ dm_value = 30;
+ dm_digtable.backoff_val = (u8)dm_value;
+ } else if (dm_type == DIG_TYPE_RX_GAIN_MIN) {
+ if (dm_value == 0)
+ dm_value = 0x1;
+ dm_digtable.rx_gain_range_min = (u8)dm_value;
+ } else if (dm_type == DIG_TYPE_RX_GAIN_MAX) {
+ if (dm_value > 0x50)
+ dm_value = 0x50;
+ dm_digtable.rx_gain_range_max = (u8)dm_value;
+ }
+}
+
+static void dm_dig_init(struct net_device *dev)
+{
+ struct r8192_priv *priv = rtllib_priv(dev);
+
+ dm_digtable.dig_enable_flag = true;
+ dm_digtable.Backoff_Enable_Flag = true;
+
+ dm_digtable.dig_algorithm = DIG_ALGO_BY_RSSI;
+
+ dm_digtable.Dig_TwoPort_Algorithm = DIG_TWO_PORT_ALGO_RSSI;
+ dm_digtable.Dig_Ext_Port_Stage = DIG_EXT_PORT_STAGE_MAX;
+ dm_digtable.dbg_mode = DM_DBG_OFF;
+ dm_digtable.dig_algorithm_switch = 0;
+
+ dm_digtable.dig_state = DM_STA_DIG_MAX;
+ dm_digtable.dig_highpwr_state = DM_STA_DIG_MAX;
+ dm_digtable.CurSTAConnectState = dm_digtable.PreSTAConnectState = DIG_STA_DISCONNECT;
+ dm_digtable.CurAPConnectState = dm_digtable.PreAPConnectState = DIG_AP_DISCONNECT;
+ dm_digtable.initialgain_lowerbound_state = false;
+
+ dm_digtable.rssi_low_thresh = DM_DIG_THRESH_LOW;
+ dm_digtable.rssi_high_thresh = DM_DIG_THRESH_HIGH;
+
+ dm_digtable.FALowThresh = DM_FALSEALARM_THRESH_LOW;
+ dm_digtable.FAHighThresh = DM_FALSEALARM_THRESH_HIGH;
+
+ dm_digtable.rssi_high_power_lowthresh = DM_DIG_HIGH_PWR_THRESH_LOW;
+ dm_digtable.rssi_high_power_highthresh = DM_DIG_HIGH_PWR_THRESH_HIGH;
+
+ dm_digtable.rssi_val = 50;
+ dm_digtable.backoff_val = DM_DIG_BACKOFF;
+ dm_digtable.rx_gain_range_max = DM_DIG_MAX;
+ if (priv->CustomerID == RT_CID_819x_Netcore)
+ dm_digtable.rx_gain_range_min = DM_DIG_MIN_Netcore;
+ else
+ dm_digtable.rx_gain_range_min = DM_DIG_MIN;
+
+ dm_digtable.BackoffVal_range_max = DM_DIG_BACKOFF_MAX;
+ dm_digtable.BackoffVal_range_min = DM_DIG_BACKOFF_MIN;
+}
+
+static void dm_ctrl_initgain_byrssi(struct net_device *dev)
+{
+
+ if (dm_digtable.dig_enable_flag == false)
+ return;
+
+ if (dm_digtable.dig_algorithm == DIG_ALGO_BY_FALSE_ALARM)
+ dm_ctrl_initgain_byrssi_by_fwfalse_alarm(dev);
+ else if (dm_digtable.dig_algorithm == DIG_ALGO_BY_RSSI)
+ dm_ctrl_initgain_byrssi_by_driverrssi(dev);
+ else
+ return;
+}
+
+/*-----------------------------------------------------------------------------
+ * Function: dm_CtrlInitGainBeforeConnectByRssiAndFalseAlarm()
+ *
+ * Overview: Driver monitor RSSI and False Alarm to change initial gain.
+ Only change initial gain during link in progress.
+ *
+ * Input: IN PADAPTER pAdapter
+ *
+ * Output: NONE
+ *
+ * Return: NONE
+ *
+ * Revised History:
+ * When Who Remark
+ * 03/04/2009 hpfan Create Version 0.
+ *
+ *---------------------------------------------------------------------------*/
+
+static void dm_ctrl_initgain_byrssi_by_driverrssi(
+ struct net_device *dev)
+{
+ struct r8192_priv *priv = rtllib_priv(dev);
+ u8 i;
+ static u8 fw_dig;
+
+ if (dm_digtable.dig_enable_flag == false)
+ return;
+
+ if (dm_digtable.dig_algorithm_switch)
+ fw_dig = 0;
+ if (fw_dig <= 3) {
+ for (i = 0; i < 3; i++)
+ rtl8192_setBBreg(dev, UFWP, bMaskByte1, 0x8);
+ fw_dig++;
+ dm_digtable.dig_state = DM_STA_DIG_OFF;
+ }
+
+ if (priv->rtllib->state == RTLLIB_LINKED)
+ dm_digtable.CurSTAConnectState = DIG_STA_CONNECT;
+ else
+ dm_digtable.CurSTAConnectState = DIG_STA_DISCONNECT;
+
+
+ if (dm_digtable.dbg_mode == DM_DBG_OFF)
+ dm_digtable.rssi_val = priv->undecorated_smoothed_pwdb;
+ dm_initial_gain(dev);
+ dm_pd_th(dev);
+ dm_cs_ratio(dev);
+ if (dm_digtable.dig_algorithm_switch)
+ dm_digtable.dig_algorithm_switch = 0;
+ dm_digtable.PreSTAConnectState = dm_digtable.CurSTAConnectState;
+
+}
+
+static void dm_ctrl_initgain_byrssi_by_fwfalse_alarm(
+ struct net_device *dev)
+{
+ struct r8192_priv *priv = rtllib_priv(dev);
+ static u32 reset_cnt;
+ u8 i;
+
+ if (dm_digtable.dig_enable_flag == false)
+ return;
+
+ if (dm_digtable.dig_algorithm_switch) {
+ dm_digtable.dig_state = DM_STA_DIG_MAX;
+ for (i = 0; i < 3; i++)
+ rtl8192_setBBreg(dev, UFWP, bMaskByte1, 0x1);
+ dm_digtable.dig_algorithm_switch = 0;
+ }
+
+ if (priv->rtllib->state != RTLLIB_LINKED)
+ return;
+
+ if ((priv->undecorated_smoothed_pwdb > dm_digtable.rssi_low_thresh) &&
+ (priv->undecorated_smoothed_pwdb < dm_digtable.rssi_high_thresh))
+ return;
+ if (priv->undecorated_smoothed_pwdb <= dm_digtable.rssi_low_thresh) {
+ if (dm_digtable.dig_state == DM_STA_DIG_OFF &&
+ (priv->reset_count == reset_cnt))
+ return;
+ reset_cnt = priv->reset_count;
+
+ dm_digtable.dig_highpwr_state = DM_STA_DIG_MAX;
+ dm_digtable.dig_state = DM_STA_DIG_OFF;
+
+ rtl8192_setBBreg(dev, UFWP, bMaskByte1, 0x8);
+
+ write_nic_byte(dev, rOFDM0_XAAGCCore1, 0x17);
+ write_nic_byte(dev, rOFDM0_XBAGCCore1, 0x17);
+ write_nic_byte(dev, rOFDM0_XCAGCCore1, 0x17);
+ write_nic_byte(dev, rOFDM0_XDAGCCore1, 0x17);
+
+ if (priv->CurrentChannelBW != HT_CHANNEL_WIDTH_20)
+ write_nic_byte(dev, (rOFDM0_XATxAFE+3), 0x00);
+ else
+ write_nic_byte(dev, rOFDM0_RxDetector1, 0x42);
+
+ write_nic_byte(dev, 0xa0a, 0x08);
+
+ return;
+ }
+
+ if (priv->undecorated_smoothed_pwdb >= dm_digtable.rssi_high_thresh) {
+ u8 reset_flag = 0;
+
+ if (dm_digtable.dig_state == DM_STA_DIG_ON &&
+ (priv->reset_count == reset_cnt)) {
+ dm_ctrl_initgain_byrssi_highpwr(dev);
+ return;
+ }
+ if (priv->reset_count != reset_cnt)
+ reset_flag = 1;
+
+ reset_cnt = priv->reset_count;
+
+ dm_digtable.dig_state = DM_STA_DIG_ON;
+
+ if (reset_flag == 1) {
+ write_nic_byte(dev, rOFDM0_XAAGCCore1, 0x2c);
+ write_nic_byte(dev, rOFDM0_XBAGCCore1, 0x2c);
+ write_nic_byte(dev, rOFDM0_XCAGCCore1, 0x2c);
+ write_nic_byte(dev, rOFDM0_XDAGCCore1, 0x2c);
+ } else {
+ write_nic_byte(dev, rOFDM0_XAAGCCore1, 0x20);
+ write_nic_byte(dev, rOFDM0_XBAGCCore1, 0x20);
+ write_nic_byte(dev, rOFDM0_XCAGCCore1, 0x20);
+ write_nic_byte(dev, rOFDM0_XDAGCCore1, 0x20);
+ }
+
+ if (priv->CurrentChannelBW != HT_CHANNEL_WIDTH_20)
+ write_nic_byte(dev, (rOFDM0_XATxAFE+3), 0x20);
+ else
+ write_nic_byte(dev, rOFDM0_RxDetector1, 0x44);
+
+ write_nic_byte(dev, 0xa0a, 0xcd);
+
+ rtl8192_setBBreg(dev, UFWP, bMaskByte1, 0x1);
+ }
+ dm_ctrl_initgain_byrssi_highpwr(dev);
+}
+
+
+static void dm_ctrl_initgain_byrssi_highpwr(struct net_device *dev)
+{
+ struct r8192_priv *priv = rtllib_priv(dev);
+ static u32 reset_cnt_highpwr;
+
+ if ((priv->undecorated_smoothed_pwdb > dm_digtable.rssi_high_power_lowthresh) &&
+ (priv->undecorated_smoothed_pwdb < dm_digtable.rssi_high_power_highthresh))
+ return;
+
+ if (priv->undecorated_smoothed_pwdb >= dm_digtable.rssi_high_power_highthresh) {
+ if (dm_digtable.dig_highpwr_state == DM_STA_DIG_ON &&
+ (priv->reset_count == reset_cnt_highpwr))
+ return;
+ dm_digtable.dig_highpwr_state = DM_STA_DIG_ON;
+
+ if (priv->CurrentChannelBW != HT_CHANNEL_WIDTH_20)
+ write_nic_byte(dev, (rOFDM0_XATxAFE+3), 0x10);
+ else
+ write_nic_byte(dev, rOFDM0_RxDetector1, 0x43);
+ } else {
+ if (dm_digtable.dig_highpwr_state == DM_STA_DIG_OFF &&
+ (priv->reset_count == reset_cnt_highpwr))
+ return;
+ dm_digtable.dig_highpwr_state = DM_STA_DIG_OFF;
+
+ if (priv->undecorated_smoothed_pwdb < dm_digtable.rssi_high_power_lowthresh &&
+ priv->undecorated_smoothed_pwdb >= dm_digtable.rssi_high_thresh) {
+ if (priv->CurrentChannelBW != HT_CHANNEL_WIDTH_20)
+ write_nic_byte(dev, (rOFDM0_XATxAFE+3), 0x20);
+ else
+ write_nic_byte(dev, rOFDM0_RxDetector1, 0x44);
+ }
+ }
+ reset_cnt_highpwr = priv->reset_count;
+}
+
+static void dm_initial_gain(struct net_device *dev)
+{
+ struct r8192_priv *priv = rtllib_priv(dev);
+ u8 initial_gain = 0;
+ static u8 initialized, force_write;
+ static u32 reset_cnt;
+
+ if (dm_digtable.dig_algorithm_switch) {
+ initialized = 0;
+ reset_cnt = 0;
+ }
+
+ if (rtllib_act_scanning(priv->rtllib, true) == true) {
+ force_write = 1;
+ return;
+ }
+
+ if (dm_digtable.PreSTAConnectState == dm_digtable.CurSTAConnectState) {
+ if (dm_digtable.CurSTAConnectState == DIG_STA_CONNECT) {
+ if ((dm_digtable.rssi_val+10-dm_digtable.backoff_val) > dm_digtable.rx_gain_range_max)
+ dm_digtable.cur_ig_value = dm_digtable.rx_gain_range_max;
+ else if ((dm_digtable.rssi_val+10-dm_digtable.backoff_val) < dm_digtable.rx_gain_range_min)
+ dm_digtable.cur_ig_value = dm_digtable.rx_gain_range_min;
+ else
+ dm_digtable.cur_ig_value = dm_digtable.rssi_val+10-dm_digtable.backoff_val;
+ } else {
+ if (dm_digtable.cur_ig_value == 0)
+ dm_digtable.cur_ig_value = priv->DefaultInitialGain[0];
+ else
+ dm_digtable.cur_ig_value = dm_digtable.pre_ig_value;
+ }
+ } else {
+ dm_digtable.cur_ig_value = priv->DefaultInitialGain[0];
+ dm_digtable.pre_ig_value = 0;
+ }
+
+ if (priv->reset_count != reset_cnt) {
+ force_write = 1;
+ reset_cnt = priv->reset_count;
+ }
+
+ if (dm_digtable.pre_ig_value != read_nic_byte(dev, rOFDM0_XAAGCCore1))
+ force_write = 1;
+
+ if ((dm_digtable.pre_ig_value != dm_digtable.cur_ig_value)
+ || !initialized || force_write) {
+ initial_gain = (u8)dm_digtable.cur_ig_value;
+ write_nic_byte(dev, rOFDM0_XAAGCCore1, initial_gain);
+ write_nic_byte(dev, rOFDM0_XBAGCCore1, initial_gain);
+ write_nic_byte(dev, rOFDM0_XCAGCCore1, initial_gain);
+ write_nic_byte(dev, rOFDM0_XDAGCCore1, initial_gain);
+ dm_digtable.pre_ig_value = dm_digtable.cur_ig_value;
+ initialized = 1;
+ force_write = 0;
+ }
+}
+
+static void dm_pd_th(struct net_device *dev)
+{
+ struct r8192_priv *priv = rtllib_priv(dev);
+ static u8 initialized, force_write;
+ static u32 reset_cnt;
+
+ if (dm_digtable.dig_algorithm_switch) {
+ initialized = 0;
+ reset_cnt = 0;
+ }
+
+ if (dm_digtable.PreSTAConnectState == dm_digtable.CurSTAConnectState) {
+ if (dm_digtable.CurSTAConnectState == DIG_STA_CONNECT) {
+ if (dm_digtable.rssi_val >= dm_digtable.rssi_high_power_highthresh)
+ dm_digtable.curpd_thstate = DIG_PD_AT_HIGH_POWER;
+ else if (dm_digtable.rssi_val <= dm_digtable.rssi_low_thresh)
+ dm_digtable.curpd_thstate = DIG_PD_AT_LOW_POWER;
+ else if ((dm_digtable.rssi_val >= dm_digtable.rssi_high_thresh) &&
+ (dm_digtable.rssi_val < dm_digtable.rssi_high_power_lowthresh))
+ dm_digtable.curpd_thstate = DIG_PD_AT_NORMAL_POWER;
+ else
+ dm_digtable.curpd_thstate = dm_digtable.prepd_thstate;
+ } else {
+ dm_digtable.curpd_thstate = DIG_PD_AT_LOW_POWER;
+ }
+ } else {
+ dm_digtable.curpd_thstate = DIG_PD_AT_LOW_POWER;
+ }
+
+ if (priv->reset_count != reset_cnt) {
+ force_write = 1;
+ reset_cnt = priv->reset_count;
+ }
+
+ if ((dm_digtable.prepd_thstate != dm_digtable.curpd_thstate) ||
+ (initialized <= 3) || force_write) {
+ if (dm_digtable.curpd_thstate == DIG_PD_AT_LOW_POWER) {
+ if (priv->CurrentChannelBW != HT_CHANNEL_WIDTH_20)
+ write_nic_byte(dev, (rOFDM0_XATxAFE+3), 0x00);
+ else
+ write_nic_byte(dev, rOFDM0_RxDetector1, 0x42);
+ } else if (dm_digtable.curpd_thstate == DIG_PD_AT_NORMAL_POWER) {
+ if (priv->CurrentChannelBW != HT_CHANNEL_WIDTH_20)
+ write_nic_byte(dev, (rOFDM0_XATxAFE+3), 0x20);
+ else
+ write_nic_byte(dev, rOFDM0_RxDetector1, 0x44);
+ } else if (dm_digtable.curpd_thstate == DIG_PD_AT_HIGH_POWER) {
+ if (priv->CurrentChannelBW != HT_CHANNEL_WIDTH_20)
+ write_nic_byte(dev, (rOFDM0_XATxAFE+3), 0x10);
+ else
+ write_nic_byte(dev, rOFDM0_RxDetector1, 0x43);
+ }
+ dm_digtable.prepd_thstate = dm_digtable.curpd_thstate;
+ if (initialized <= 3)
+ initialized++;
+ force_write = 0;
+ }
+}
+
+static void dm_cs_ratio(struct net_device *dev)
+{
+ struct r8192_priv *priv = rtllib_priv(dev);
+ static u8 initialized, force_write;
+ static u32 reset_cnt;
+
+ if (dm_digtable.dig_algorithm_switch) {
+ initialized = 0;
+ reset_cnt = 0;
+ }
+
+ if (dm_digtable.PreSTAConnectState == dm_digtable.CurSTAConnectState) {
+ if (dm_digtable.CurSTAConnectState == DIG_STA_CONNECT) {
+ if (dm_digtable.rssi_val <= dm_digtable.rssi_low_thresh)
+ dm_digtable.curcs_ratio_state = DIG_CS_RATIO_LOWER;
+ else if (dm_digtable.rssi_val >= dm_digtable.rssi_high_thresh)
+ dm_digtable.curcs_ratio_state = DIG_CS_RATIO_HIGHER;
+ else
+ dm_digtable.curcs_ratio_state = dm_digtable.precs_ratio_state;
+ } else {
+ dm_digtable.curcs_ratio_state = DIG_CS_RATIO_LOWER;
+ }
+ } else {
+ dm_digtable.curcs_ratio_state = DIG_CS_RATIO_LOWER;
+ }
+
+ if (priv->reset_count != reset_cnt) {
+ force_write = 1;
+ reset_cnt = priv->reset_count;
+ }
+
+
+ if ((dm_digtable.precs_ratio_state != dm_digtable.curcs_ratio_state) ||
+ !initialized || force_write) {
+ if (dm_digtable.curcs_ratio_state == DIG_CS_RATIO_LOWER)
+ write_nic_byte(dev, 0xa0a, 0x08);
+ else if (dm_digtable.curcs_ratio_state == DIG_CS_RATIO_HIGHER)
+ write_nic_byte(dev, 0xa0a, 0xcd);
+ dm_digtable.precs_ratio_state = dm_digtable.curcs_ratio_state;
+ initialized = 1;
+ force_write = 0;
+ }
+}
+
+void dm_init_edca_turbo(struct net_device *dev)
+{
+ struct r8192_priv *priv = rtllib_priv(dev);
+
+ priv->bcurrent_turbo_EDCA = false;
+ priv->rtllib->bis_any_nonbepkts = false;
+ priv->bis_cur_rdlstate = false;
+}
+
+static void dm_check_edca_turbo(struct net_device *dev)
+{
+ struct r8192_priv *priv = rtllib_priv(dev);
+ struct rt_hi_throughput *pHTInfo = priv->rtllib->pHTInfo;
+
+ static unsigned long lastTxOkCnt;
+ static unsigned long lastRxOkCnt;
+ unsigned long curTxOkCnt = 0;
+ unsigned long curRxOkCnt = 0;
+
+ if (priv->rtllib->iw_mode == IW_MODE_ADHOC)
+ goto dm_CheckEdcaTurbo_EXIT;
+ if (priv->rtllib->state != RTLLIB_LINKED)
+ goto dm_CheckEdcaTurbo_EXIT;
+ if (priv->rtllib->pHTInfo->IOTAction & HT_IOT_ACT_DISABLE_EDCA_TURBO)
+ goto dm_CheckEdcaTurbo_EXIT;
+
+ {
+ u8 *peername[11] = {
+ "unknown", "realtek_90", "realtek_92se", "broadcom",
+ "ralink", "atheros", "cisco", "marvell", "92u_softap",
+ "self_softap"
+ };
+ static int wb_tmp;
+
+ if (wb_tmp == 0) {
+ netdev_info(dev,
+ "%s():iot peer is %s, bssid: %pM\n",
+ __func__, peername[pHTInfo->IOTPeer],
+ priv->rtllib->current_network.bssid);
+ wb_tmp = 1;
+ }
+ }
+ if (!priv->rtllib->bis_any_nonbepkts) {
+ curTxOkCnt = priv->stats.txbytesunicast - lastTxOkCnt;
+ curRxOkCnt = priv->stats.rxbytesunicast - lastRxOkCnt;
+ if (pHTInfo->IOTAction & HT_IOT_ACT_EDCA_BIAS_ON_RX) {
+ if (curTxOkCnt > 4*curRxOkCnt) {
+ if (priv->bis_cur_rdlstate ||
+ !priv->bcurrent_turbo_EDCA) {
+ write_nic_dword(dev, EDCAPARA_BE,
+ edca_setting_UL[pHTInfo->IOTPeer]);
+ priv->bis_cur_rdlstate = false;
+ }
+ } else {
+ if (!priv->bis_cur_rdlstate ||
+ !priv->bcurrent_turbo_EDCA) {
+ if (priv->rtllib->mode == WIRELESS_MODE_G)
+ write_nic_dword(dev, EDCAPARA_BE,
+ edca_setting_DL_GMode[pHTInfo->IOTPeer]);
+ else
+ write_nic_dword(dev, EDCAPARA_BE,
+ edca_setting_DL[pHTInfo->IOTPeer]);
+ priv->bis_cur_rdlstate = true;
+ }
+ }
+ priv->bcurrent_turbo_EDCA = true;
+ } else {
+ if (curRxOkCnt > 4*curTxOkCnt) {
+ if (!priv->bis_cur_rdlstate || !priv->bcurrent_turbo_EDCA) {
+ if (priv->rtllib->mode == WIRELESS_MODE_G)
+ write_nic_dword(dev, EDCAPARA_BE,
+ edca_setting_DL_GMode[pHTInfo->IOTPeer]);
+ else
+ write_nic_dword(dev, EDCAPARA_BE,
+ edca_setting_DL[pHTInfo->IOTPeer]);
+ priv->bis_cur_rdlstate = true;
+ }
+ } else {
+ if (priv->bis_cur_rdlstate ||
+ !priv->bcurrent_turbo_EDCA) {
+ write_nic_dword(dev, EDCAPARA_BE,
+ edca_setting_UL[pHTInfo->IOTPeer]);
+ priv->bis_cur_rdlstate = false;
+ }
+
+ }
+
+ priv->bcurrent_turbo_EDCA = true;
+ }
+ } else {
+ if (priv->bcurrent_turbo_EDCA) {
+ u8 tmp = AC0_BE;
+
+ priv->rtllib->SetHwRegHandler(dev, HW_VAR_AC_PARAM, (u8 *)(&tmp));
+ priv->bcurrent_turbo_EDCA = false;
+ }
+ }
+
+
+dm_CheckEdcaTurbo_EXIT:
+ priv->rtllib->bis_any_nonbepkts = false;
+ lastTxOkCnt = priv->stats.txbytesunicast;
+ lastRxOkCnt = priv->stats.rxbytesunicast;
+}
+
+static void dm_init_ctstoself(struct net_device *dev)
+{
+ struct r8192_priv *priv = rtllib_priv((struct net_device *)dev);
+
+ priv->rtllib->bCTSToSelfEnable = true;
+ priv->rtllib->CTSToSelfTH = CTSToSelfTHVal;
+}
+
+static void dm_ctstoself(struct net_device *dev)
+{
+ struct r8192_priv *priv = rtllib_priv((struct net_device *)dev);
+ struct rt_hi_throughput *pHTInfo = priv->rtllib->pHTInfo;
+ static unsigned long lastTxOkCnt;
+ static unsigned long lastRxOkCnt;
+ unsigned long curTxOkCnt = 0;
+ unsigned long curRxOkCnt = 0;
+
+ if (priv->rtllib->bCTSToSelfEnable != true) {
+ pHTInfo->IOTAction &= ~HT_IOT_ACT_FORCED_CTS2SELF;
+ return;
+ }
+ if (pHTInfo->IOTPeer == HT_IOT_PEER_BROADCOM) {
+ curTxOkCnt = priv->stats.txbytesunicast - lastTxOkCnt;
+ curRxOkCnt = priv->stats.rxbytesunicast - lastRxOkCnt;
+ if (curRxOkCnt > 4*curTxOkCnt)
+ pHTInfo->IOTAction &= ~HT_IOT_ACT_FORCED_CTS2SELF;
+ else
+ pHTInfo->IOTAction |= HT_IOT_ACT_FORCED_CTS2SELF;
+
+ lastTxOkCnt = priv->stats.txbytesunicast;
+ lastRxOkCnt = priv->stats.rxbytesunicast;
+ }
+}
+
+
+static void dm_Init_WA_Broadcom_IOT(struct net_device *dev)
+{
+ struct r8192_priv *priv = rtllib_priv((struct net_device *)dev);
+ struct rt_hi_throughput *pHTInfo = priv->rtllib->pHTInfo;
+
+ pHTInfo->bWAIotBroadcom = false;
+ pHTInfo->WAIotTH = WAIotTHVal;
+}
+
+static void dm_check_pbc_gpio(struct net_device *dev)
+{
+}
+
+void dm_CheckRfCtrlGPIO(void *data)
+{
+ struct r8192_priv *priv = container_of_dwork_rsl(data,
+ struct r8192_priv, gpio_change_rf_wq);
+ struct net_device *dev = priv->rtllib->dev;
+ u8 tmp1byte;
+ enum rt_rf_power_state eRfPowerStateToSet;
+ bool bActuallySet = false;
+ char *argv[3];
+ static char *RadioPowerPath = "/etc/acpi/events/RadioPower.sh";
+ static char *envp[] = {"HOME=/", "TERM=linux", "PATH=/usr/bin:/bin", NULL};
+
+ bActuallySet = false;
+
+ if ((priv->up_first_time == 1) || (priv->being_init_adapter))
+ return;
+
+ if (priv->bfirst_after_down) {
+ priv->bfirst_after_down = true;
+ return;
+ }
+
+ tmp1byte = read_nic_byte(dev, GPI);
+
+ eRfPowerStateToSet = (tmp1byte&BIT1) ? eRfOn : eRfOff;
+
+ if (priv->bHwRadioOff && (eRfPowerStateToSet == eRfOn)) {
+ RT_TRACE(COMP_RF, "gpiochangeRF - HW Radio ON\n");
+ netdev_info(dev, "gpiochangeRF - HW Radio ON\n");
+ priv->bHwRadioOff = false;
+ bActuallySet = true;
+ } else if (!priv->bHwRadioOff && (eRfPowerStateToSet == eRfOff)) {
+ RT_TRACE(COMP_RF, "gpiochangeRF - HW Radio OFF\n");
+ netdev_info(dev, "gpiochangeRF - HW Radio OFF\n");
+ priv->bHwRadioOff = true;
+ bActuallySet = true;
+ }
+
+ if (bActuallySet) {
+ mdelay(1000);
+ priv->bHwRfOffAction = 1;
+ MgntActSet_RF_State(dev, eRfPowerStateToSet, RF_CHANGE_BY_HW, true);
+ if (priv->bHwRadioOff)
+ argv[1] = "RFOFF";
+ else
+ argv[1] = "RFON";
+
+ argv[0] = RadioPowerPath;
+ argv[2] = NULL;
+ call_usermodehelper(RadioPowerPath, argv, envp, UMH_WAIT_PROC);
+ }
+}
+
+void dm_rf_pathcheck_workitemcallback(void *data)
+{
+ struct r8192_priv *priv = container_of_dwork_rsl(data,
+ struct r8192_priv,
+ rfpath_check_wq);
+ struct net_device *dev = priv->rtllib->dev;
+ u8 rfpath = 0, i;
+
+ rfpath = read_nic_byte(dev, 0xc04);
+
+ for (i = 0; i < RF90_PATH_MAX; i++) {
+ if (rfpath & (0x01<<i))
+ priv->brfpath_rxenable[i] = true;
+ else
+ priv->brfpath_rxenable[i] = false;
+ }
+ if (!DM_RxPathSelTable.Enable)
+ return;
+
+ dm_rxpath_sel_byrssi(dev);
+}
+
+static void dm_init_rxpath_selection(struct net_device *dev)
+{
+ u8 i;
+ struct r8192_priv *priv = rtllib_priv(dev);
+
+ DM_RxPathSelTable.Enable = 1;
+ DM_RxPathSelTable.SS_TH_low = RxPathSelection_SS_TH_low;
+ DM_RxPathSelTable.diff_TH = RxPathSelection_diff_TH;
+ if (priv->CustomerID == RT_CID_819x_Netcore)
+ DM_RxPathSelTable.cck_method = CCK_Rx_Version_2;
+ else
+ DM_RxPathSelTable.cck_method = CCK_Rx_Version_1;
+ DM_RxPathSelTable.DbgMode = DM_DBG_OFF;
+ DM_RxPathSelTable.disabledRF = 0;
+ for (i = 0; i < 4; i++) {
+ DM_RxPathSelTable.rf_rssi[i] = 50;
+ DM_RxPathSelTable.cck_pwdb_sta[i] = -64;
+ DM_RxPathSelTable.rf_enable_rssi_th[i] = 100;
+ }
+}
+
+#define PWDB_IN_RANGE ((cur_cck_pwdb < tmp_cck_max_pwdb) && \
+ (cur_cck_pwdb > tmp_cck_sec_pwdb))
+
+static void dm_rxpath_sel_byrssi(struct net_device *dev)
+{
+ struct r8192_priv *priv = rtllib_priv(dev);
+ u8 i, max_rssi_index = 0, min_rssi_index = 0;
+ u8 sec_rssi_index = 0, rf_num = 0;
+ u8 tmp_max_rssi = 0, tmp_min_rssi = 0, tmp_sec_rssi = 0;
+ u8 cck_default_Rx = 0x2;
+ u8 cck_optional_Rx = 0x3;
+ long tmp_cck_max_pwdb = 0, tmp_cck_min_pwdb = 0, tmp_cck_sec_pwdb = 0;
+ u8 cck_rx_ver2_max_index = 0, cck_rx_ver2_min_index = 0;
+ u8 cck_rx_ver2_sec_index = 0;
+ u8 cur_rf_rssi;
+ long cur_cck_pwdb;
+ static u8 disabled_rf_cnt, cck_Rx_Path_initialized;
+ u8 update_cck_rx_path;
+
+ if (priv->rf_type != RF_2T4R)
+ return;
+
+ if (!cck_Rx_Path_initialized) {
+ DM_RxPathSelTable.cck_Rx_path = (read_nic_byte(dev, 0xa07)&0xf);
+ cck_Rx_Path_initialized = 1;
+ }
+
+ DM_RxPathSelTable.disabledRF = 0xf;
+ DM_RxPathSelTable.disabledRF &= ~(read_nic_byte(dev, 0xc04));
+
+ if (priv->rtllib->mode == WIRELESS_MODE_B)
+ DM_RxPathSelTable.cck_method = CCK_Rx_Version_2;
+
+ for (i = 0; i < RF90_PATH_MAX; i++) {
+ if (!DM_RxPathSelTable.DbgMode)
+ DM_RxPathSelTable.rf_rssi[i] = priv->stats.rx_rssi_percentage[i];
+
+ if (priv->brfpath_rxenable[i]) {
+ rf_num++;
+ cur_rf_rssi = DM_RxPathSelTable.rf_rssi[i];
+
+ if (rf_num == 1) {
+ max_rssi_index = min_rssi_index = sec_rssi_index = i;
+ tmp_max_rssi = tmp_min_rssi = tmp_sec_rssi = cur_rf_rssi;
+ } else if (rf_num == 2) {
+ if (cur_rf_rssi >= tmp_max_rssi) {
+ tmp_max_rssi = cur_rf_rssi;
+ max_rssi_index = i;
+ } else {
+ tmp_sec_rssi = tmp_min_rssi = cur_rf_rssi;
+ sec_rssi_index = min_rssi_index = i;
+ }
+ } else {
+ if (cur_rf_rssi > tmp_max_rssi) {
+ tmp_sec_rssi = tmp_max_rssi;
+ sec_rssi_index = max_rssi_index;
+ tmp_max_rssi = cur_rf_rssi;
+ max_rssi_index = i;
+ } else if (cur_rf_rssi == tmp_max_rssi) {
+ tmp_sec_rssi = cur_rf_rssi;
+ sec_rssi_index = i;
+ } else if ((cur_rf_rssi < tmp_max_rssi) &&
+ (cur_rf_rssi > tmp_sec_rssi)) {
+ tmp_sec_rssi = cur_rf_rssi;
+ sec_rssi_index = i;
+ } else if (cur_rf_rssi == tmp_sec_rssi) {
+ if (tmp_sec_rssi == tmp_min_rssi) {
+ tmp_sec_rssi = cur_rf_rssi;
+ sec_rssi_index = i;
+ }
+ } else if ((cur_rf_rssi < tmp_sec_rssi) &&
+ (cur_rf_rssi > tmp_min_rssi)) {
+ ;
+ } else if (cur_rf_rssi == tmp_min_rssi) {
+ if (tmp_sec_rssi == tmp_min_rssi) {
+ tmp_min_rssi = cur_rf_rssi;
+ min_rssi_index = i;
+ }
+ } else if (cur_rf_rssi < tmp_min_rssi) {
+ tmp_min_rssi = cur_rf_rssi;
+ min_rssi_index = i;
+ }
+ }
+ }
+ }
+
+ rf_num = 0;
+ if (DM_RxPathSelTable.cck_method == CCK_Rx_Version_2) {
+ for (i = 0; i < RF90_PATH_MAX; i++) {
+ if (priv->brfpath_rxenable[i]) {
+ rf_num++;
+ cur_cck_pwdb =
+ DM_RxPathSelTable.cck_pwdb_sta[i];
+
+ if (rf_num == 1) {
+ cck_rx_ver2_max_index = i;
+ cck_rx_ver2_min_index = i;
+ cck_rx_ver2_sec_index = i;
+ tmp_cck_max_pwdb = cur_cck_pwdb;
+ tmp_cck_min_pwdb = cur_cck_pwdb;
+ tmp_cck_sec_pwdb = cur_cck_pwdb;
+ } else if (rf_num == 2) {
+ if (cur_cck_pwdb >= tmp_cck_max_pwdb) {
+ tmp_cck_max_pwdb = cur_cck_pwdb;
+ cck_rx_ver2_max_index = i;
+ } else {
+ tmp_cck_sec_pwdb = cur_cck_pwdb;
+ tmp_cck_min_pwdb = cur_cck_pwdb;
+ cck_rx_ver2_sec_index = i;
+ cck_rx_ver2_min_index = i;
+ }
+ } else {
+ if (cur_cck_pwdb > tmp_cck_max_pwdb) {
+ tmp_cck_sec_pwdb =
+ tmp_cck_max_pwdb;
+ cck_rx_ver2_sec_index =
+ cck_rx_ver2_max_index;
+ tmp_cck_max_pwdb = cur_cck_pwdb;
+ cck_rx_ver2_max_index = i;
+ } else if (cur_cck_pwdb ==
+ tmp_cck_max_pwdb) {
+ tmp_cck_sec_pwdb = cur_cck_pwdb;
+ cck_rx_ver2_sec_index = i;
+ } else if (PWDB_IN_RANGE) {
+ tmp_cck_sec_pwdb = cur_cck_pwdb;
+ cck_rx_ver2_sec_index = i;
+ } else if (cur_cck_pwdb ==
+ tmp_cck_sec_pwdb) {
+ if (tmp_cck_sec_pwdb ==
+ tmp_cck_min_pwdb) {
+ tmp_cck_sec_pwdb =
+ cur_cck_pwdb;
+ cck_rx_ver2_sec_index =
+ i;
+ }
+ } else if ((cur_cck_pwdb < tmp_cck_sec_pwdb) &&
+ (cur_cck_pwdb > tmp_cck_min_pwdb)) {
+ ;
+ } else if (cur_cck_pwdb == tmp_cck_min_pwdb) {
+ if (tmp_cck_sec_pwdb == tmp_cck_min_pwdb) {
+ tmp_cck_min_pwdb = cur_cck_pwdb;
+ cck_rx_ver2_min_index = i;
+ }
+ } else if (cur_cck_pwdb < tmp_cck_min_pwdb) {
+ tmp_cck_min_pwdb = cur_cck_pwdb;
+ cck_rx_ver2_min_index = i;
+ }
+ }
+
+ }
+ }
+ }
+
+ update_cck_rx_path = 0;
+ if (DM_RxPathSelTable.cck_method == CCK_Rx_Version_2) {
+ cck_default_Rx = cck_rx_ver2_max_index;
+ cck_optional_Rx = cck_rx_ver2_sec_index;
+ if (tmp_cck_max_pwdb != -64)
+ update_cck_rx_path = 1;
+ }
+
+ if (tmp_min_rssi < DM_RxPathSelTable.SS_TH_low && disabled_rf_cnt < 2) {
+ if ((tmp_max_rssi - tmp_min_rssi) >=
+ DM_RxPathSelTable.diff_TH) {
+ DM_RxPathSelTable.rf_enable_rssi_th[min_rssi_index] =
+ tmp_max_rssi+5;
+ rtl8192_setBBreg(dev, rOFDM0_TRxPathEnable,
+ 0x1<<min_rssi_index, 0x0);
+ rtl8192_setBBreg(dev, rOFDM1_TRxPathEnable,
+ 0x1<<min_rssi_index, 0x0);
+ disabled_rf_cnt++;
+ }
+ if (DM_RxPathSelTable.cck_method == CCK_Rx_Version_1) {
+ cck_default_Rx = max_rssi_index;
+ cck_optional_Rx = sec_rssi_index;
+ if (tmp_max_rssi)
+ update_cck_rx_path = 1;
+ }
+ }
+
+ if (update_cck_rx_path) {
+ DM_RxPathSelTable.cck_Rx_path = (cck_default_Rx<<2) |
+ (cck_optional_Rx);
+ rtl8192_setBBreg(dev, rCCK0_AFESetting, 0x0f000000,
+ DM_RxPathSelTable.cck_Rx_path);
+ }
+
+ if (DM_RxPathSelTable.disabledRF) {
+ for (i = 0; i < 4; i++) {
+ if ((DM_RxPathSelTable.disabledRF>>i) & 0x1) {
+ if (tmp_max_rssi >=
+ DM_RxPathSelTable.rf_enable_rssi_th[i]) {
+ rtl8192_setBBreg(dev,
+ rOFDM0_TRxPathEnable, 0x1 << i,
+ 0x1);
+ rtl8192_setBBreg(dev,
+ rOFDM1_TRxPathEnable,
+ 0x1 << i, 0x1);
+ DM_RxPathSelTable.rf_enable_rssi_th[i]
+ = 100;
+ disabled_rf_cnt--;
+ }
+ }
+ }
+ }
+}
+
+static void dm_check_rx_path_selection(struct net_device *dev)
+{
+ struct r8192_priv *priv = rtllib_priv(dev);
+
+ queue_delayed_work_rsl(priv->priv_wq, &priv->rfpath_check_wq, 0);
+}
+
+
+static void dm_init_fsync(struct net_device *dev)
+{
+ struct r8192_priv *priv = rtllib_priv(dev);
+
+ priv->rtllib->fsync_time_interval = 500;
+ priv->rtllib->fsync_rate_bitmap = 0x0f000800;
+ priv->rtllib->fsync_rssi_threshold = 30;
+ priv->rtllib->bfsync_enable = false;
+ priv->rtllib->fsync_multiple_timeinterval = 3;
+ priv->rtllib->fsync_firstdiff_ratethreshold = 100;
+ priv->rtllib->fsync_seconddiff_ratethreshold = 200;
+ priv->rtllib->fsync_state = Default_Fsync;
+ priv->framesyncMonitor = 1;
+
+ init_timer(&priv->fsync_timer);
+ setup_timer(&priv->fsync_timer, dm_fsync_timer_callback,
+ (unsigned long) dev);
+}
+
+
+static void dm_deInit_fsync(struct net_device *dev)
+{
+ struct r8192_priv *priv = rtllib_priv(dev);
+
+ del_timer_sync(&priv->fsync_timer);
+}
+
+void dm_fsync_timer_callback(unsigned long data)
+{
+ struct net_device *dev = (struct net_device *)data;
+ struct r8192_priv *priv = rtllib_priv((struct net_device *)data);
+ u32 rate_index, rate_count = 0, rate_count_diff = 0;
+ bool bSwitchFromCountDiff = false;
+ bool bDoubleTimeInterval = false;
+
+ if (priv->rtllib->state == RTLLIB_LINKED &&
+ priv->rtllib->bfsync_enable &&
+ (priv->rtllib->pHTInfo->IOTAction & HT_IOT_ACT_CDD_FSYNC)) {
+ u32 rate_bitmap;
+
+ for (rate_index = 0; rate_index <= 27; rate_index++) {
+ rate_bitmap = 1 << rate_index;
+ if (priv->rtllib->fsync_rate_bitmap & rate_bitmap)
+ rate_count +=
+ priv->stats.received_rate_histogram[1]
+ [rate_index];
+ }
+
+ if (rate_count < priv->rate_record)
+ rate_count_diff = 0xffffffff - rate_count +
+ priv->rate_record;
+ else
+ rate_count_diff = rate_count - priv->rate_record;
+ if (rate_count_diff < priv->rateCountDiffRecord) {
+
+ u32 DiffNum = priv->rateCountDiffRecord -
+ rate_count_diff;
+ if (DiffNum >=
+ priv->rtllib->fsync_seconddiff_ratethreshold)
+ priv->ContinueDiffCount++;
+ else
+ priv->ContinueDiffCount = 0;
+
+ if (priv->ContinueDiffCount >= 2) {
+ bSwitchFromCountDiff = true;
+ priv->ContinueDiffCount = 0;
+ }
+ } else {
+ priv->ContinueDiffCount = 0;
+ }
+
+ if (rate_count_diff <=
+ priv->rtllib->fsync_firstdiff_ratethreshold) {
+ bSwitchFromCountDiff = true;
+ priv->ContinueDiffCount = 0;
+ }
+ priv->rate_record = rate_count;
+ priv->rateCountDiffRecord = rate_count_diff;
+ RT_TRACE(COMP_HALDM,
+ "rateRecord %d rateCount %d, rateCountdiff %d bSwitchFsync %d\n",
+ priv->rate_record, rate_count, rate_count_diff,
+ priv->bswitch_fsync);
+ if (priv->undecorated_smoothed_pwdb >
+ priv->rtllib->fsync_rssi_threshold &&
+ bSwitchFromCountDiff) {
+ bDoubleTimeInterval = true;
+ priv->bswitch_fsync = !priv->bswitch_fsync;
+ if (priv->bswitch_fsync) {
+ write_nic_byte(dev, 0xC36, 0x1c);
+ write_nic_byte(dev, 0xC3e, 0x90);
+ } else {
+ write_nic_byte(dev, 0xC36, 0x5c);
+ write_nic_byte(dev, 0xC3e, 0x96);
+ }
+ } else if (priv->undecorated_smoothed_pwdb <=
+ priv->rtllib->fsync_rssi_threshold) {
+ if (priv->bswitch_fsync) {
+ priv->bswitch_fsync = false;
+ write_nic_byte(dev, 0xC36, 0x5c);
+ write_nic_byte(dev, 0xC3e, 0x96);
+ }
+ }
+ if (bDoubleTimeInterval) {
+ if (timer_pending(&priv->fsync_timer))
+ del_timer_sync(&priv->fsync_timer);
+ priv->fsync_timer.expires = jiffies +
+ msecs_to_jiffies(priv->rtllib->fsync_time_interval *
+ priv->rtllib->fsync_multiple_timeinterval);
+ add_timer(&priv->fsync_timer);
+ } else {
+ if (timer_pending(&priv->fsync_timer))
+ del_timer_sync(&priv->fsync_timer);
+ priv->fsync_timer.expires = jiffies +
+ msecs_to_jiffies(priv->rtllib->fsync_time_interval);
+ add_timer(&priv->fsync_timer);
+ }
+ } else {
+ if (priv->bswitch_fsync) {
+ priv->bswitch_fsync = false;
+ write_nic_byte(dev, 0xC36, 0x5c);
+ write_nic_byte(dev, 0xC3e, 0x96);
+ }
+ priv->ContinueDiffCount = 0;
+ write_nic_dword(dev, rOFDM0_RxDetector2, 0x465c52cd);
+ }
+ RT_TRACE(COMP_HALDM, "ContinueDiffCount %d\n", priv->ContinueDiffCount);
+ RT_TRACE(COMP_HALDM,
+ "rateRecord %d rateCount %d, rateCountdiff %d bSwitchFsync %d\n",
+ priv->rate_record, rate_count, rate_count_diff,
+ priv->bswitch_fsync);
+}
+
+static void dm_StartHWFsync(struct net_device *dev)
+{
+ u8 rf_timing = 0x77;
+ struct r8192_priv *priv = rtllib_priv(dev);
+
+ RT_TRACE(COMP_HALDM, "%s\n", __func__);
+ write_nic_dword(dev, rOFDM0_RxDetector2, 0x465c12cf);
+ priv->rtllib->SetHwRegHandler(dev, HW_VAR_RF_TIMING,
+ (u8 *)(&rf_timing));
+ write_nic_byte(dev, 0xc3b, 0x41);
+}
+
+static void dm_EndHWFsync(struct net_device *dev)
+{
+ u8 rf_timing = 0xaa;
+ struct r8192_priv *priv = rtllib_priv(dev);
+
+ RT_TRACE(COMP_HALDM, "%s\n", __func__);
+ write_nic_dword(dev, rOFDM0_RxDetector2, 0x465c52cd);
+ priv->rtllib->SetHwRegHandler(dev, HW_VAR_RF_TIMING, (u8 *)
+ (&rf_timing));
+ write_nic_byte(dev, 0xc3b, 0x49);
+}
+
+static void dm_EndSWFsync(struct net_device *dev)
+{
+ struct r8192_priv *priv = rtllib_priv(dev);
+
+ RT_TRACE(COMP_HALDM, "%s\n", __func__);
+ del_timer_sync(&(priv->fsync_timer));
+
+ if (priv->bswitch_fsync) {
+ priv->bswitch_fsync = false;
+
+ write_nic_byte(dev, 0xC36, 0x5c);
+
+ write_nic_byte(dev, 0xC3e, 0x96);
+ }
+
+ priv->ContinueDiffCount = 0;
+ write_nic_dword(dev, rOFDM0_RxDetector2, 0x465c52cd);
+}
+
+static void dm_StartSWFsync(struct net_device *dev)
+{
+ struct r8192_priv *priv = rtllib_priv(dev);
+ u32 rateIndex;
+ u32 rateBitmap;
+
+ RT_TRACE(COMP_HALDM, "%s\n", __func__);
+ priv->rate_record = 0;
+ priv->ContinueDiffCount = 0;
+ priv->rateCountDiffRecord = 0;
+ priv->bswitch_fsync = false;
+
+ if (priv->rtllib->mode == WIRELESS_MODE_N_24G) {
+ priv->rtllib->fsync_firstdiff_ratethreshold = 600;
+ priv->rtllib->fsync_seconddiff_ratethreshold = 0xffff;
+ } else {
+ priv->rtllib->fsync_firstdiff_ratethreshold = 200;
+ priv->rtllib->fsync_seconddiff_ratethreshold = 200;
+ }
+ for (rateIndex = 0; rateIndex <= 27; rateIndex++) {
+ rateBitmap = 1 << rateIndex;
+ if (priv->rtllib->fsync_rate_bitmap & rateBitmap)
+ priv->rate_record +=
+ priv->stats.received_rate_histogram[1]
+ [rateIndex];
+ }
+ if (timer_pending(&priv->fsync_timer))
+ del_timer_sync(&priv->fsync_timer);
+ priv->fsync_timer.expires = jiffies +
+ msecs_to_jiffies(priv->rtllib->fsync_time_interval);
+ add_timer(&priv->fsync_timer);
+
+ write_nic_dword(dev, rOFDM0_RxDetector2, 0x465c12cd);
+
+}
+
+void dm_check_fsync(struct net_device *dev)
+{
+#define RegC38_Default 0
+#define RegC38_NonFsync_Other_AP 1
+#define RegC38_Fsync_AP_BCM 2
+ struct r8192_priv *priv = rtllib_priv(dev);
+ static u8 reg_c38_State = RegC38_Default;
+ static u32 reset_cnt;
+
+ RT_TRACE(COMP_HALDM,
+ "RSSI %d TimeInterval %d MultipleTimeInterval %d\n",
+ priv->rtllib->fsync_rssi_threshold,
+ priv->rtllib->fsync_time_interval,
+ priv->rtllib->fsync_multiple_timeinterval);
+ RT_TRACE(COMP_HALDM,
+ "RateBitmap 0x%x FirstDiffRateThreshold %d SecondDiffRateThreshold %d\n",
+ priv->rtllib->fsync_rate_bitmap,
+ priv->rtllib->fsync_firstdiff_ratethreshold,
+ priv->rtllib->fsync_seconddiff_ratethreshold);
+
+ if (priv->rtllib->state == RTLLIB_LINKED &&
+ priv->rtllib->pHTInfo->IOTPeer == HT_IOT_PEER_BROADCOM) {
+ if (priv->rtllib->bfsync_enable == 0) {
+ switch (priv->rtllib->fsync_state) {
+ case Default_Fsync:
+ dm_StartHWFsync(dev);
+ priv->rtllib->fsync_state = HW_Fsync;
+ break;
+ case SW_Fsync:
+ dm_EndSWFsync(dev);
+ dm_StartHWFsync(dev);
+ priv->rtllib->fsync_state = HW_Fsync;
+ break;
+ case HW_Fsync:
+ default:
+ break;
+ }
+ } else {
+ switch (priv->rtllib->fsync_state) {
+ case Default_Fsync:
+ dm_StartSWFsync(dev);
+ priv->rtllib->fsync_state = SW_Fsync;
+ break;
+ case HW_Fsync:
+ dm_EndHWFsync(dev);
+ dm_StartSWFsync(dev);
+ priv->rtllib->fsync_state = SW_Fsync;
+ break;
+ case SW_Fsync:
+ default:
+ break;
+
+ }
+ }
+ if (priv->framesyncMonitor) {
+ if (reg_c38_State != RegC38_Fsync_AP_BCM) {
+ write_nic_byte(dev, rOFDM0_RxDetector3, 0x95);
+
+ reg_c38_State = RegC38_Fsync_AP_BCM;
+ }
+ }
+ } else {
+ switch (priv->rtllib->fsync_state) {
+ case HW_Fsync:
+ dm_EndHWFsync(dev);
+ priv->rtllib->fsync_state = Default_Fsync;
+ break;
+ case SW_Fsync:
+ dm_EndSWFsync(dev);
+ priv->rtllib->fsync_state = Default_Fsync;
+ break;
+ case Default_Fsync:
+ default:
+ break;
+ }
+
+ if (priv->framesyncMonitor) {
+ if (priv->rtllib->state == RTLLIB_LINKED) {
+ if (priv->undecorated_smoothed_pwdb <=
+ RegC38_TH) {
+ if (reg_c38_State !=
+ RegC38_NonFsync_Other_AP) {
+ write_nic_byte(dev,
+ rOFDM0_RxDetector3,
+ 0x90);
+
+ reg_c38_State =
+ RegC38_NonFsync_Other_AP;
+ }
+ } else if (priv->undecorated_smoothed_pwdb >=
+ (RegC38_TH+5)) {
+ if (reg_c38_State) {
+ write_nic_byte(dev,
+ rOFDM0_RxDetector3,
+ priv->framesync);
+ reg_c38_State = RegC38_Default;
+ }
+ }
+ } else {
+ if (reg_c38_State) {
+ write_nic_byte(dev, rOFDM0_RxDetector3,
+ priv->framesync);
+ reg_c38_State = RegC38_Default;
+ }
+ }
+ }
+ }
+ if (priv->framesyncMonitor) {
+ if (priv->reset_count != reset_cnt) {
+ write_nic_byte(dev, rOFDM0_RxDetector3,
+ priv->framesync);
+ reg_c38_State = RegC38_Default;
+ reset_cnt = priv->reset_count;
+ }
+ } else {
+ if (reg_c38_State) {
+ write_nic_byte(dev, rOFDM0_RxDetector3,
+ priv->framesync);
+ reg_c38_State = RegC38_Default;
+ }
+ }
+}
+
+void dm_shadow_init(struct net_device *dev)
+{
+ u8 page;
+ u16 offset;
+
+ for (page = 0; page < 5; page++)
+ for (offset = 0; offset < 256; offset++)
+ dm_shadow[page][offset] = read_nic_byte(dev,
+ offset+page * 256);
+
+ for (page = 8; page < 11; page++)
+ for (offset = 0; offset < 256; offset++)
+ dm_shadow[page][offset] = read_nic_byte(dev,
+ offset+page * 256);
+
+ for (page = 12; page < 15; page++)
+ for (offset = 0; offset < 256; offset++)
+ dm_shadow[page][offset] = read_nic_byte(dev,
+ offset+page*256);
+
+}
+
+/*---------------------------Define function prototype------------------------*/
+static void dm_init_dynamic_txpower(struct net_device *dev)
+{
+ struct r8192_priv *priv = rtllib_priv(dev);
+
+ priv->rtllib->bdynamic_txpower_enable = true;
+ priv->bLastDTPFlag_High = false;
+ priv->bLastDTPFlag_Low = false;
+ priv->bDynamicTxHighPower = false;
+ priv->bDynamicTxLowPower = false;
+}
+
+static void dm_dynamic_txpower(struct net_device *dev)
+{
+ struct r8192_priv *priv = rtllib_priv(dev);
+ unsigned int txhipower_threshhold = 0;
+ unsigned int txlowpower_threshold = 0;
+
+ if (priv->rtllib->bdynamic_txpower_enable != true) {
+ priv->bDynamicTxHighPower = false;
+ priv->bDynamicTxLowPower = false;
+ return;
+ }
+ if ((priv->rtllib->pHTInfo->IOTPeer == HT_IOT_PEER_ATHEROS) &&
+ (priv->rtllib->mode == IEEE_G)) {
+ txhipower_threshhold = TX_POWER_ATHEROAP_THRESH_HIGH;
+ txlowpower_threshold = TX_POWER_ATHEROAP_THRESH_LOW;
+ } else {
+ txhipower_threshhold = TX_POWER_NEAR_FIELD_THRESH_HIGH;
+ txlowpower_threshold = TX_POWER_NEAR_FIELD_THRESH_LOW;
+ }
+
+ RT_TRACE(COMP_TXAGC, "priv->undecorated_smoothed_pwdb = %ld\n",
+ priv->undecorated_smoothed_pwdb);
+
+ if (priv->rtllib->state == RTLLIB_LINKED) {
+ if (priv->undecorated_smoothed_pwdb >= txhipower_threshhold) {
+ priv->bDynamicTxHighPower = true;
+ priv->bDynamicTxLowPower = false;
+ } else {
+ if (priv->undecorated_smoothed_pwdb <
+ txlowpower_threshold && priv->bDynamicTxHighPower)
+ priv->bDynamicTxHighPower = false;
+ if (priv->undecorated_smoothed_pwdb < 35)
+ priv->bDynamicTxLowPower = true;
+ else if (priv->undecorated_smoothed_pwdb >= 40)
+ priv->bDynamicTxLowPower = false;
+ }
+ } else {
+ priv->bDynamicTxHighPower = false;
+ priv->bDynamicTxLowPower = false;
+ }
+
+ if ((priv->bDynamicTxHighPower != priv->bLastDTPFlag_High) ||
+ (priv->bDynamicTxLowPower != priv->bLastDTPFlag_Low)) {
+ RT_TRACE(COMP_TXAGC, "SetTxPowerLevel8190() channel = %d\n",
+ priv->rtllib->current_network.channel);
+
+ rtl8192_phy_setTxPower(dev,
+ priv->rtllib->current_network.channel);
+ }
+ priv->bLastDTPFlag_High = priv->bDynamicTxHighPower;
+ priv->bLastDTPFlag_Low = priv->bDynamicTxLowPower;
+
+}
+
+static void dm_check_txrateandretrycount(struct net_device *dev)
+{
+ struct r8192_priv *priv = rtllib_priv(dev);
+ struct rtllib_device *ieee = priv->rtllib;
+
+ ieee->softmac_stats.CurrentShowTxate = read_nic_byte(dev,
+ Current_Tx_Rate_Reg);
+
+ ieee->softmac_stats.last_packet_rate = read_nic_byte(dev,
+ Initial_Tx_Rate_Reg);
+
+ ieee->softmac_stats.txretrycount = read_nic_dword(dev,
+ Tx_Retry_Count_Reg);
+}
+
+static void dm_send_rssi_tofw(struct net_device *dev)
+{
+ struct r8192_priv *priv = rtllib_priv(dev);
+
+ write_nic_byte(dev, DRIVER_RSSI, (u8)priv->undecorated_smoothed_pwdb);
+}
diff --git a/drivers/staging/rtl8192e/rtl8192e/rtl_dm.h b/drivers/staging/rtl8192e/rtl8192e/rtl_dm.h
new file mode 100644
index 000000000..3f02e11cf
--- /dev/null
+++ b/drivers/staging/rtl8192e/rtl8192e/rtl_dm.h
@@ -0,0 +1,316 @@
+/******************************************************************************
+ * Copyright(c) 2008 - 2010 Realtek Corporation. All rights reserved.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ * The full GNU General Public License is included in this distribution in the
+ * file called LICENSE.
+ *
+ * Contact Information:
+ * wlanfae <wlanfae@realtek.com>
+******************************************************************************/
+#ifndef __R8192UDM_H__
+#define __R8192UDM_H__
+
+
+/*--------------------------Define Parameters-------------------------------*/
+#define OFDM_Table_Length 19
+#define CCK_Table_length 12
+
+#define DM_DIG_THRESH_HIGH 40
+#define DM_DIG_THRESH_LOW 35
+
+#define DM_FALSEALARM_THRESH_LOW 40
+#define DM_FALSEALARM_THRESH_HIGH 1000
+
+#define DM_DIG_HIGH_PWR_THRESH_HIGH 75
+#define DM_DIG_HIGH_PWR_THRESH_LOW 70
+
+#define BW_AUTO_SWITCH_HIGH_LOW 25
+#define BW_AUTO_SWITCH_LOW_HIGH 30
+
+#define DM_check_fsync_time_interval 500
+
+
+#define DM_DIG_BACKOFF 12
+#define DM_DIG_MAX 0x36
+#define DM_DIG_MIN 0x1c
+#define DM_DIG_MIN_Netcore 0x12
+
+#define DM_DIG_BACKOFF_MAX 12
+#define DM_DIG_BACKOFF_MIN -4
+
+#define RxPathSelection_SS_TH_low 30
+#define RxPathSelection_diff_TH 18
+
+#define RateAdaptiveTH_High 50
+#define RateAdaptiveTH_Low_20M 30
+#define RateAdaptiveTH_Low_40M 10
+#define VeryLowRSSI 15
+
+#define CTSToSelfTHVal 35
+
+#define WAIotTHVal 25
+
+#define E_FOR_TX_POWER_TRACK 300
+#define TX_POWER_NEAR_FIELD_THRESH_HIGH 68
+#define TX_POWER_NEAR_FIELD_THRESH_LOW 62
+#define TX_POWER_ATHEROAP_THRESH_HIGH 78
+#define TX_POWER_ATHEROAP_THRESH_LOW 72
+
+#define Current_Tx_Rate_Reg 0x1e0
+#define Initial_Tx_Rate_Reg 0x1e1
+#define Tx_Retry_Count_Reg 0x1ac
+#define RegC38_TH 20
+
+#define TX_POWER_NEAR_FIELD_THRESH_LVL2 74
+#define TX_POWER_NEAR_FIELD_THRESH_LVL1 67
+
+#define TxHighPwrLevel_Normal 0
+#define TxHighPwrLevel_Level1 1
+#define TxHighPwrLevel_Level2 2
+
+#define DM_Type_ByFW 0
+#define DM_Type_ByDriver 1
+
+/*--------------------------Define Parameters-------------------------------*/
+
+
+/*------------------------------Define structure----------------------------*/
+struct dig_t {
+ u8 dig_enable_flag;
+ u8 dig_algorithm;
+ u8 Dig_TwoPort_Algorithm;
+ u8 Dig_Ext_Port_Stage;
+ u8 dbg_mode;
+ u8 dig_algorithm_switch;
+
+ long rssi_low_thresh;
+ long rssi_high_thresh;
+
+ u32 FALowThresh;
+ u32 FAHighThresh;
+
+ long rssi_high_power_lowthresh;
+ long rssi_high_power_highthresh;
+
+ u8 dig_state;
+ u8 dig_highpwr_state;
+ u8 CurSTAConnectState;
+ u8 PreSTAConnectState;
+ u8 CurAPConnectState;
+ u8 PreAPConnectState;
+
+ u8 curpd_thstate;
+ u8 prepd_thstate;
+ u8 curcs_ratio_state;
+ u8 precs_ratio_state;
+
+ u32 pre_ig_value;
+ u32 cur_ig_value;
+
+ u8 Backoff_Enable_Flag;
+ u8 backoff_val;
+ char BackoffVal_range_max;
+ char BackoffVal_range_min;
+ u8 rx_gain_range_max;
+ u8 rx_gain_range_min;
+ bool initialgain_lowerbound_state;
+
+ long rssi_val;
+};
+
+enum dm_dig_sta {
+ DM_STA_DIG_OFF = 0,
+ DM_STA_DIG_ON,
+ DM_STA_DIG_MAX
+};
+
+
+enum dm_ratr_sta {
+ DM_RATR_STA_HIGH = 0,
+ DM_RATR_STA_MIDDLE = 1,
+ DM_RATR_STA_LOW = 2,
+ DM_RATR_STA_MAX
+};
+
+enum dm_dig_op_sta {
+ DIG_TYPE_THRESH_HIGH = 0,
+ DIG_TYPE_THRESH_LOW = 1,
+ DIG_TYPE_THRESH_HIGHPWR_HIGH = 2,
+ DIG_TYPE_THRESH_HIGHPWR_LOW = 3,
+ DIG_TYPE_DBG_MODE = 4,
+ DIG_TYPE_RSSI = 5,
+ DIG_TYPE_ALGORITHM = 6,
+ DIG_TYPE_BACKOFF = 7,
+ DIG_TYPE_PWDB_FACTOR = 8,
+ DIG_TYPE_RX_GAIN_MIN = 9,
+ DIG_TYPE_RX_GAIN_MAX = 10,
+ DIG_TYPE_ENABLE = 20,
+ DIG_TYPE_DISABLE = 30,
+ DIG_OP_TYPE_MAX
+};
+
+enum dm_dig_alg {
+ DIG_ALGO_BY_FALSE_ALARM = 0,
+ DIG_ALGO_BY_RSSI = 1,
+ DIG_ALGO_BEFORE_CONNECT_BY_RSSI_AND_ALARM = 2,
+ DIG_ALGO_BY_TOW_PORT = 3,
+ DIG_ALGO_MAX
+};
+
+enum dm_dig_two_port_alg {
+ DIG_TWO_PORT_ALGO_RSSI = 0,
+ DIG_TWO_PORT_ALGO_FALSE_ALARM = 1,
+};
+
+
+enum dm_dig_ext_port_alg {
+ DIG_EXT_PORT_STAGE_0 = 0,
+ DIG_EXT_PORT_STAGE_1 = 1,
+ DIG_EXT_PORT_STAGE_2 = 2,
+ DIG_EXT_PORT_STAGE_3 = 3,
+ DIG_EXT_PORT_STAGE_MAX = 4,
+};
+
+enum dm_dig_dbg {
+ DIG_DBG_OFF = 0,
+ DIG_DBG_ON = 1,
+ DIG_DBG_MAX
+};
+
+enum dm_dig_connect {
+ DIG_STA_DISCONNECT = 0,
+ DIG_STA_CONNECT = 1,
+ DIG_STA_BEFORE_CONNECT = 2,
+ DIG_AP_DISCONNECT = 3,
+ DIG_AP_CONNECT = 4,
+ DIG_AP_ADD_STATION = 5,
+ DIG_CONNECT_MAX
+};
+
+enum dm_dig_pd_th {
+ DIG_PD_AT_LOW_POWER = 0,
+ DIG_PD_AT_NORMAL_POWER = 1,
+ DIG_PD_AT_HIGH_POWER = 2,
+ DIG_PD_MAX
+};
+
+enum dm_dig_cs_ratio {
+ DIG_CS_RATIO_LOWER = 0,
+ DIG_CS_RATIO_HIGHER = 1,
+ DIG_CS_MAX
+};
+
+struct drx_path_sel {
+ u8 Enable;
+ u8 DbgMode;
+ u8 cck_method;
+ u8 cck_Rx_path;
+
+ u8 SS_TH_low;
+ u8 diff_TH;
+ u8 disabledRF;
+ u8 reserved;
+
+ u8 rf_rssi[4];
+ u8 rf_enable_rssi_th[4];
+ long cck_pwdb_sta[4];
+};
+
+enum dm_cck_rx_path_method {
+ CCK_Rx_Version_1 = 0,
+ CCK_Rx_Version_2 = 1,
+ CCK_Rx_Version_MAX
+};
+
+
+enum dm_dbg {
+ DM_DBG_OFF = 0,
+ DM_DBG_ON = 1,
+ DM_DBG_MAX
+};
+
+struct dcmd_txcmd {
+ u32 Op;
+ u32 Length;
+ u32 Value;
+};
+/*------------------------------Define structure----------------------------*/
+
+
+/*------------------------Export global variable----------------------------*/
+extern struct dig_t dm_digtable;
+extern u8 dm_shadow[16][256];
+extern struct drx_path_sel DM_RxPathSelTable;
+
+extern u8 test_flag;
+/*------------------------Export global variable----------------------------*/
+
+
+/*--------------------------Exported Function prototype---------------------*/
+/*--------------------------Exported Function prototype---------------------*/
+extern void init_hal_dm(struct net_device *dev);
+extern void deinit_hal_dm(struct net_device *dev);
+
+extern void hal_dm_watchdog(struct net_device *dev);
+
+
+extern void init_rate_adaptive(struct net_device *dev);
+extern void dm_txpower_trackingcallback(void *data);
+
+extern void dm_cck_txpower_adjust(struct net_device *dev, bool binch14);
+
+extern void dm_restore_dynamic_mechanism_state(struct net_device *dev);
+extern void dm_backup_dynamic_mechanism_state(struct net_device *dev);
+extern void dm_change_dynamic_initgain_thresh(struct net_device *dev,
+ u32 dm_type,
+ u32 dm_value);
+extern void DM_ChangeFsyncSetting(struct net_device *dev,
+ s32 DM_Type,
+ s32 DM_Value);
+extern void dm_force_tx_fw_info(struct net_device *dev,
+ u32 force_type,
+ u32 force_value);
+extern void dm_init_edca_turbo(struct net_device *dev);
+extern void dm_rf_operation_test_callback(unsigned long data);
+extern void dm_rf_pathcheck_workitemcallback(void *data);
+extern void dm_fsync_timer_callback(unsigned long data);
+extern void dm_check_fsync(struct net_device *dev);
+extern void dm_shadow_init(struct net_device *dev);
+extern void dm_initialize_txpower_tracking(struct net_device *dev);
+extern void dm_CheckRfCtrlGPIO(void *data);
+extern void dm_InitRateAdaptiveMask(struct net_device *dev);
+extern void init_hal_dm(struct net_device *dev);
+extern void deinit_hal_dm(struct net_device *dev);
+extern void hal_dm_watchdog(struct net_device *dev);
+extern void init_rate_adaptive(struct net_device *dev);
+extern void dm_txpower_trackingcallback(void *data);
+extern void dm_restore_dynamic_mechanism_state(struct net_device *dev);
+extern void dm_backup_dynamic_mechanism_state(struct net_device *dev);
+extern void dm_change_dynamic_initgain_thresh(struct net_device *dev,
+ u32 dm_type,
+ u32 dm_value);
+extern void DM_ChangeFsyncSetting(struct net_device *dev,
+ s32 DM_Type,
+ s32 DM_Value);
+extern void dm_force_tx_fw_info(struct net_device *dev,
+ u32 force_type,
+ u32 force_value);
+extern void dm_init_edca_turbo(struct net_device *dev);
+extern void dm_rf_operation_test_callback(unsigned long data);
+extern void dm_rf_pathcheck_workitemcallback(void *data);
+extern void dm_fsync_timer_callback(unsigned long data);
+extern void dm_check_fsync(struct net_device *dev);
+extern void dm_shadow_init(struct net_device *dev);
+extern void dm_initialize_txpower_tracking(struct net_device *dev);
+extern void dm_CheckRfCtrlGPIO(void *data);
+
+#endif /*__R8192UDM_H__ */
diff --git a/drivers/staging/rtl8192e/rtl8192e/rtl_eeprom.c b/drivers/staging/rtl8192e/rtl8192e/rtl_eeprom.c
new file mode 100644
index 000000000..a6778e085
--- /dev/null
+++ b/drivers/staging/rtl8192e/rtl8192e/rtl_eeprom.c
@@ -0,0 +1,139 @@
+/******************************************************************************
+ * Copyright(c) 2008 - 2010 Realtek Corporation. All rights reserved.
+ *
+ * Based on the r8180 driver, which is:
+ * Copyright 2004-2005 Andrea Merello <andrea.merello@gmail.com>, et al.
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ * The full GNU General Public License is included in this distribution in the
+ * file called LICENSE.
+ *
+ * Contact Information:
+ * wlanfae <wlanfae@realtek.com>
+******************************************************************************/
+#include "rtl_core.h"
+#include "rtl_eeprom.h"
+
+static void eprom_cs(struct net_device *dev, short bit)
+{
+ if (bit)
+ write_nic_byte(dev, EPROM_CMD,
+ (1 << EPROM_CS_SHIFT) |
+ read_nic_byte(dev, EPROM_CMD));
+ else
+ write_nic_byte(dev, EPROM_CMD, read_nic_byte(dev, EPROM_CMD)
+ & ~(1<<EPROM_CS_SHIFT));
+
+ udelay(EPROM_DELAY);
+}
+
+
+static void eprom_ck_cycle(struct net_device *dev)
+{
+ write_nic_byte(dev, EPROM_CMD,
+ (1<<EPROM_CK_SHIFT) | read_nic_byte(dev, EPROM_CMD));
+ udelay(EPROM_DELAY);
+ write_nic_byte(dev, EPROM_CMD,
+ read_nic_byte(dev, EPROM_CMD) & ~(1<<EPROM_CK_SHIFT));
+ udelay(EPROM_DELAY);
+}
+
+
+static void eprom_w(struct net_device *dev, short bit)
+{
+ if (bit)
+ write_nic_byte(dev, EPROM_CMD, (1<<EPROM_W_SHIFT) |
+ read_nic_byte(dev, EPROM_CMD));
+ else
+ write_nic_byte(dev, EPROM_CMD, read_nic_byte(dev, EPROM_CMD)
+ & ~(1<<EPROM_W_SHIFT));
+
+ udelay(EPROM_DELAY);
+}
+
+
+static short eprom_r(struct net_device *dev)
+{
+ short bit;
+
+ bit = (read_nic_byte(dev, EPROM_CMD) & (1<<EPROM_R_SHIFT));
+ udelay(EPROM_DELAY);
+
+ if (bit)
+ return 1;
+ return 0;
+}
+
+static void eprom_send_bits_string(struct net_device *dev, short b[], int len)
+{
+ int i;
+
+ for (i = 0; i < len; i++) {
+ eprom_w(dev, b[i]);
+ eprom_ck_cycle(dev);
+ }
+}
+
+u32 eprom_read(struct net_device *dev, u32 addr)
+{
+ struct r8192_priv *priv = rtllib_priv(dev);
+ short read_cmd[] = {1, 1, 0};
+ short addr_str[8];
+ int i;
+ int addr_len;
+ u32 ret;
+
+ ret = 0;
+ write_nic_byte(dev, EPROM_CMD,
+ (EPROM_CMD_PROGRAM << EPROM_CMD_OPERATING_MODE_SHIFT));
+ udelay(EPROM_DELAY);
+
+ if (priv->epromtype == EEPROM_93C56) {
+ addr_str[7] = addr & 1;
+ addr_str[6] = addr & (1<<1);
+ addr_str[5] = addr & (1<<2);
+ addr_str[4] = addr & (1<<3);
+ addr_str[3] = addr & (1<<4);
+ addr_str[2] = addr & (1<<5);
+ addr_str[1] = addr & (1<<6);
+ addr_str[0] = addr & (1<<7);
+ addr_len = 8;
+ } else {
+ addr_str[5] = addr & 1;
+ addr_str[4] = addr & (1<<1);
+ addr_str[3] = addr & (1<<2);
+ addr_str[2] = addr & (1<<3);
+ addr_str[1] = addr & (1<<4);
+ addr_str[0] = addr & (1<<5);
+ addr_len = 6;
+ }
+ eprom_cs(dev, 1);
+ eprom_ck_cycle(dev);
+ eprom_send_bits_string(dev, read_cmd, 3);
+ eprom_send_bits_string(dev, addr_str, addr_len);
+
+ eprom_w(dev, 0);
+
+ for (i = 0; i < 16; i++) {
+ eprom_ck_cycle(dev);
+ ret |= (eprom_r(dev)<<(15-i));
+ }
+
+ eprom_cs(dev, 0);
+ eprom_ck_cycle(dev);
+
+ write_nic_byte(dev, EPROM_CMD,
+ (EPROM_CMD_NORMAL<<EPROM_CMD_OPERATING_MODE_SHIFT));
+ return ret;
+}
diff --git a/drivers/staging/rtl8192e/rtl8192e/rtl_eeprom.h b/drivers/staging/rtl8192e/rtl8192e/rtl_eeprom.h
new file mode 100644
index 000000000..adea2b4c7
--- /dev/null
+++ b/drivers/staging/rtl8192e/rtl8192e/rtl_eeprom.h
@@ -0,0 +1,29 @@
+/******************************************************************************
+ * Copyright(c) 2008 - 2010 Realtek Corporation. All rights reserved.
+ *
+ * Based on the r8180 driver, which is:
+ * Copyright 2004-2005 Andrea Merello <andrea.merello@gmail.com>, et al.
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ * The full GNU General Public License is included in this distribution in the
+ * file called LICENSE.
+ *
+ * Contact Information:
+ * wlanfae <wlanfae@realtek.com>
+******************************************************************************/
+
+
+#define EPROM_DELAY 10
+
+u32 eprom_read(struct net_device *dev, u32 addr);
diff --git a/drivers/staging/rtl8192e/rtl8192e/rtl_ethtool.c b/drivers/staging/rtl8192e/rtl8192e/rtl_ethtool.c
new file mode 100644
index 000000000..529ea54d1
--- /dev/null
+++ b/drivers/staging/rtl8192e/rtl8192e/rtl_ethtool.c
@@ -0,0 +1,53 @@
+/******************************************************************************
+ * Copyright(c) 2008 - 2010 Realtek Corporation. All rights reserved.
+ *
+ * Based on the r8180 driver, which is:
+ * Copyright 2004-2005 Andrea Merello <andrea.merello@gmail.com>, et al.
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ * The full GNU General Public License is included in this distribution in the
+ * file called LICENSE.
+ *
+ * Contact Information:
+ * wlanfae <wlanfae@realtek.com>
+ *****************************************************************************
+ */
+#include <linux/netdevice.h>
+#include <linux/ethtool.h>
+#include <linux/delay.h>
+
+#include "rtl_core.h"
+
+static void rtl819x_ethtool_get_drvinfo(struct net_device *dev,
+ struct ethtool_drvinfo *info)
+{
+ struct r8192_priv *priv = rtllib_priv(dev);
+
+ strlcpy(info->driver, DRV_NAME, sizeof(info->driver));
+ strlcpy(info->version, DRV_VERSION, sizeof(info->version));
+ strlcpy(info->bus_info, pci_name(priv->pdev), sizeof(info->bus_info));
+}
+
+static u32 rtl819x_ethtool_get_link(struct net_device *dev)
+{
+ struct r8192_priv *priv = rtllib_priv(dev);
+
+ return ((priv->rtllib->state == RTLLIB_LINKED) ||
+ (priv->rtllib->state == RTLLIB_LINKED_SCANNING));
+}
+
+const struct ethtool_ops rtl819x_ethtool_ops = {
+ .get_drvinfo = rtl819x_ethtool_get_drvinfo,
+ .get_link = rtl819x_ethtool_get_link,
+};
diff --git a/drivers/staging/rtl8192e/rtl8192e/rtl_pci.c b/drivers/staging/rtl8192e/rtl8192e/rtl_pci.c
new file mode 100644
index 000000000..51f53be2d
--- /dev/null
+++ b/drivers/staging/rtl8192e/rtl8192e/rtl_pci.c
@@ -0,0 +1,100 @@
+/******************************************************************************
+ * Copyright(c) 2008 - 2010 Realtek Corporation. All rights reserved.
+ *
+ * Based on the r8180 driver, which is:
+ * Copyright 2004-2005 Andrea Merello <andrea.merello@gmail.com>, et al.
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ * The full GNU General Public License is included in this distribution in the
+ * file called LICENSE.
+ *
+ * Contact Information:
+ * wlanfae <wlanfae@realtek.com>
+ *****************************************************************************/
+#include "rtl_pci.h"
+#include "rtl_core.h"
+
+static void rtl8192_parse_pci_configuration(struct pci_dev *pdev,
+ struct net_device *dev)
+{
+ struct r8192_priv *priv = (struct r8192_priv *)rtllib_priv(dev);
+
+ u8 tmp;
+ u16 LinkCtrlReg;
+
+ pcie_capability_read_word(priv->pdev, PCI_EXP_LNKCTL, &LinkCtrlReg);
+ priv->NdisAdapter.LinkCtrlReg = (u8)LinkCtrlReg;
+
+ RT_TRACE(COMP_INIT, "Link Control Register =%x\n",
+ priv->NdisAdapter.LinkCtrlReg);
+
+ pci_read_config_byte(pdev, 0x98, &tmp);
+ tmp |= BIT4;
+ pci_write_config_byte(pdev, 0x98, tmp);
+
+ tmp = 0x17;
+ pci_write_config_byte(pdev, 0x70f, tmp);
+}
+
+bool rtl8192_pci_findadapter(struct pci_dev *pdev, struct net_device *dev)
+{
+ struct r8192_priv *priv = (struct r8192_priv *)rtllib_priv(dev);
+ u16 VenderID;
+ u16 DeviceID;
+ u8 RevisionID;
+ u16 IrqLine;
+
+ VenderID = pdev->vendor;
+ DeviceID = pdev->device;
+ RevisionID = pdev->revision;
+ pci_read_config_word(pdev, 0x3C, &IrqLine);
+
+ priv->card_8192 = priv->ops->nic_type;
+
+ if (DeviceID == 0x8172) {
+ switch (RevisionID) {
+ case HAL_HW_PCI_REVISION_ID_8192PCIE:
+ dev_info(&pdev->dev,
+ "Adapter(8192 PCI-E) is found - DeviceID=%x\n",
+ DeviceID);
+ priv->card_8192 = NIC_8192E;
+ break;
+ case HAL_HW_PCI_REVISION_ID_8192SE:
+ dev_info(&pdev->dev,
+ "Adapter(8192SE) is found - DeviceID=%x\n",
+ DeviceID);
+ priv->card_8192 = NIC_8192SE;
+ break;
+ default:
+ dev_info(&pdev->dev,
+ "UNKNOWN nic type(%4x:%4x)\n",
+ pdev->vendor, pdev->device);
+ priv->card_8192 = NIC_UNKNOWN;
+ return false;
+ }
+ }
+
+ if (priv->ops->nic_type != priv->card_8192) {
+ dev_info(&pdev->dev,
+ "Detect info(%x) and hardware info(%x) not match!\n",
+ priv->ops->nic_type, priv->card_8192);
+ dev_info(&pdev->dev,
+ "Please select proper driver before install!!!!\n");
+ return false;
+ }
+
+ rtl8192_parse_pci_configuration(pdev, dev);
+
+ return true;
+}
diff --git a/drivers/staging/rtl8192e/rtl8192e/rtl_pci.h b/drivers/staging/rtl8192e/rtl8192e/rtl_pci.h
new file mode 100644
index 000000000..4b94653c5
--- /dev/null
+++ b/drivers/staging/rtl8192e/rtl8192e/rtl_pci.h
@@ -0,0 +1,51 @@
+/******************************************************************************
+ * Copyright(c) 2008 - 2010 Realtek Corporation. All rights reserved.
+ *
+ * Based on the r8180 driver, which is:
+ * Copyright 2004-2005 Andrea Merello <andrea.merello@gmail.com>, et al.
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ * The full GNU General Public License is included in this distribution in the
+ * file called LICENSE.
+ *
+ * Contact Information:
+ * wlanfae <wlanfae@realtek.com>
+ ******************************************************************************/
+#ifndef _RTL_PCI_H
+#define _RTL_PCI_H
+
+#include <linux/types.h>
+#include <linux/pci.h>
+
+struct mp_adapter {
+ u8 LinkCtrlReg;
+
+ u8 BusNumber;
+ u8 DevNumber;
+ u8 FuncNumber;
+
+ u8 PciBridgeBusNum;
+ u8 PciBridgeDevNum;
+ u8 PciBridgeFuncNum;
+ u8 PciBridgeVendor;
+ u16 PciBridgeVendorId;
+ u16 PciBridgeDeviceId;
+ u8 PciBridgePCIeHdrOffset;
+ u8 PciBridgeLinkCtrlReg;
+};
+
+struct net_device;
+bool rtl8192_pci_findadapter(struct pci_dev *pdev, struct net_device *dev);
+
+#endif
diff --git a/drivers/staging/rtl8192e/rtl8192e/rtl_pm.c b/drivers/staging/rtl8192e/rtl8192e/rtl_pm.c
new file mode 100644
index 000000000..ca6ecfc82
--- /dev/null
+++ b/drivers/staging/rtl8192e/rtl8192e/rtl_pm.c
@@ -0,0 +1,119 @@
+/******************************************************************************
+ * Copyright(c) 2008 - 2010 Realtek Corporation. All rights reserved.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ * The full GNU General Public License is included in this distribution in the
+ * file called LICENSE.
+ *
+ * Contact Information:
+ * wlanfae <wlanfae@realtek.com>
+******************************************************************************/
+
+#include "rtl_core.h"
+#include "r8192E_hw.h"
+#include "r8190P_rtl8256.h"
+#include "rtl_pm.h"
+
+
+int rtl8192E_suspend(struct pci_dev *pdev, pm_message_t state)
+{
+ struct net_device *dev = pci_get_drvdata(pdev);
+ struct r8192_priv *priv = rtllib_priv(dev);
+ u32 ulRegRead;
+
+ netdev_info(dev, "============> r8192E suspend call.\n");
+ del_timer_sync(&priv->gpio_polling_timer);
+ cancel_delayed_work(&priv->gpio_change_rf_wq);
+ priv->polling_timer_on = 0;
+
+ if (!netif_running(dev)) {
+ netdev_info(dev,
+ "RTL819XE:UI is open out of suspend function\n");
+ goto out_pci_suspend;
+ }
+
+ if (dev->netdev_ops->ndo_stop)
+ dev->netdev_ops->ndo_stop(dev);
+ netif_device_detach(dev);
+
+ if (!priv->rtllib->bSupportRemoteWakeUp) {
+ MgntActSet_RF_State(dev, eRfOff, RF_CHANGE_BY_INIT, true);
+ ulRegRead = read_nic_dword(dev, CPU_GEN);
+ ulRegRead |= CPU_GEN_SYSTEM_RESET;
+ write_nic_dword(dev, CPU_GEN, ulRegRead);
+ } else {
+ write_nic_dword(dev, WFCRC0, 0xffffffff);
+ write_nic_dword(dev, WFCRC1, 0xffffffff);
+ write_nic_dword(dev, WFCRC2, 0xffffffff);
+ write_nic_byte(dev, PMR, 0x5);
+ write_nic_byte(dev, MacBlkCtrl, 0xa);
+ }
+out_pci_suspend:
+ netdev_info(dev, "r8192E support WOL call??????????????????????\n");
+ if (priv->rtllib->bSupportRemoteWakeUp)
+ RT_TRACE(COMP_POWER,
+ "r8192E support WOL call!!!!!!!!!!!!!!!!!!.\n");
+ pci_save_state(pdev);
+ pci_disable_device(pdev);
+ pci_enable_wake(pdev, pci_choose_state(pdev, state),
+ priv->rtllib->bSupportRemoteWakeUp ? 1 : 0);
+ pci_set_power_state(pdev, pci_choose_state(pdev, state));
+
+ mdelay(20);
+
+ return 0;
+}
+
+int rtl8192E_resume(struct pci_dev *pdev)
+{
+ struct net_device *dev = pci_get_drvdata(pdev);
+ struct r8192_priv *priv = rtllib_priv(dev);
+ int err;
+ u32 val;
+
+ netdev_info(dev, "================>r8192E resume call.\n");
+
+ pci_set_power_state(pdev, PCI_D0);
+
+ err = pci_enable_device(pdev);
+ if (err) {
+ netdev_err(dev, "pci_enable_device failed on resume\n");
+ return err;
+ }
+ pci_restore_state(pdev);
+
+ pci_read_config_dword(pdev, 0x40, &val);
+ if ((val & 0x0000ff00) != 0)
+ pci_write_config_dword(pdev, 0x40, val & 0xffff00ff);
+
+ pci_enable_wake(pdev, PCI_D0, 0);
+
+ if (priv->polling_timer_on == 0)
+ check_rfctrl_gpio_timer((unsigned long)dev);
+
+ if (!netif_running(dev)) {
+ netdev_info(dev,
+ "RTL819XE:UI is open out of resume function\n");
+ goto out;
+ }
+
+ netif_device_attach(dev);
+ if (dev->netdev_ops->ndo_open)
+ dev->netdev_ops->ndo_open(dev);
+
+ if (!priv->rtllib->bSupportRemoteWakeUp)
+ MgntActSet_RF_State(dev, eRfOn, RF_CHANGE_BY_INIT, true);
+
+out:
+ RT_TRACE(COMP_POWER, "<================r8192E resume call.\n");
+ return 0;
+}
+
diff --git a/drivers/staging/rtl8192e/rtl8192e/rtl_pm.h b/drivers/staging/rtl8192e/rtl8192e/rtl_pm.h
new file mode 100644
index 000000000..7bfe44817
--- /dev/null
+++ b/drivers/staging/rtl8192e/rtl8192e/rtl_pm.h
@@ -0,0 +1,29 @@
+/******************************************************************************
+ * Copyright(c) 2008 - 2010 Realtek Corporation. All rights reserved.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ * The full GNU General Public License is included in this distribution in the
+ * file called LICENSE.
+ *
+ * Contact Information:
+ * wlanfae <wlanfae@realtek.com>
+******************************************************************************/
+
+#ifndef R8192E_PM_H
+#define R8192E_PM_H
+
+#include <linux/types.h>
+#include <linux/pci.h>
+
+int rtl8192E_suspend(struct pci_dev *dev, pm_message_t state);
+int rtl8192E_resume(struct pci_dev *dev);
+
+#endif
diff --git a/drivers/staging/rtl8192e/rtl8192e/rtl_ps.c b/drivers/staging/rtl8192e/rtl8192e/rtl_ps.c
new file mode 100644
index 000000000..0bbffec0c
--- /dev/null
+++ b/drivers/staging/rtl8192e/rtl8192e/rtl_ps.c
@@ -0,0 +1,316 @@
+/******************************************************************************
+ * Copyright(c) 2008 - 2010 Realtek Corporation. All rights reserved.
+ *
+ * Based on the r8180 driver, which is:
+ * Copyright 2004-2005 Andrea Merello <andrea.merello@gmail.com>, et al.
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ * The full GNU General Public License is included in this distribution in the
+ * file called LICENSE.
+ *
+ * Contact Information:
+ * wlanfae <wlanfae@realtek.com>
+ *****************************************************************************/
+#include "rtl_ps.h"
+#include "rtl_core.h"
+#include "r8192E_phy.h"
+#include "r8192E_phyreg.h"
+#include "r8190P_rtl8256.h" /* RTL8225 Radio frontend */
+#include "r8192E_cmdpkt.h"
+
+static void rtl8192_hw_sleep_down(struct net_device *dev)
+{
+ struct r8192_priv *priv = rtllib_priv(dev);
+ unsigned long flags = 0;
+
+ spin_lock_irqsave(&priv->rf_ps_lock, flags);
+ if (priv->RFChangeInProgress) {
+ spin_unlock_irqrestore(&priv->rf_ps_lock, flags);
+ RT_TRACE(COMP_DBG,
+ "rtl8192_hw_sleep_down(): RF Change in progress!\n");
+ return;
+ }
+ spin_unlock_irqrestore(&priv->rf_ps_lock, flags);
+ RT_TRACE(COMP_DBG, "%s()============>come to sleep down\n", __func__);
+
+ MgntActSet_RF_State(dev, eRfSleep, RF_CHANGE_BY_PS, false);
+}
+
+void rtl8192_hw_sleep_wq(void *data)
+{
+ struct rtllib_device *ieee = container_of_dwork_rsl(data,
+ struct rtllib_device, hw_sleep_wq);
+ struct net_device *dev = ieee->dev;
+
+ rtl8192_hw_sleep_down(dev);
+}
+
+void rtl8192_hw_wakeup(struct net_device *dev)
+{
+ struct r8192_priv *priv = rtllib_priv(dev);
+ unsigned long flags = 0;
+
+ spin_lock_irqsave(&priv->rf_ps_lock, flags);
+ if (priv->RFChangeInProgress) {
+ spin_unlock_irqrestore(&priv->rf_ps_lock, flags);
+ RT_TRACE(COMP_DBG,
+ "rtl8192_hw_wakeup(): RF Change in progress!\n");
+ queue_delayed_work_rsl(priv->rtllib->wq,
+ &priv->rtllib->hw_wakeup_wq,
+ msecs_to_jiffies(10));
+ return;
+ }
+ spin_unlock_irqrestore(&priv->rf_ps_lock, flags);
+ RT_TRACE(COMP_PS, "%s()============>come to wake up\n", __func__);
+ MgntActSet_RF_State(dev, eRfOn, RF_CHANGE_BY_PS, false);
+}
+
+void rtl8192_hw_wakeup_wq(void *data)
+{
+ struct rtllib_device *ieee = container_of_dwork_rsl(data,
+ struct rtllib_device, hw_wakeup_wq);
+ struct net_device *dev = ieee->dev;
+
+ rtl8192_hw_wakeup(dev);
+}
+
+#define MIN_SLEEP_TIME 50
+#define MAX_SLEEP_TIME 10000
+void rtl8192_hw_to_sleep(struct net_device *dev, u64 time)
+{
+ struct r8192_priv *priv = rtllib_priv(dev);
+
+ u32 tmp;
+ unsigned long flags;
+
+ spin_lock_irqsave(&priv->ps_lock, flags);
+
+ time -= msecs_to_jiffies(8 + 16 + 7);
+
+ if ((time - jiffies) <= msecs_to_jiffies(MIN_SLEEP_TIME)) {
+ spin_unlock_irqrestore(&priv->ps_lock, flags);
+ netdev_info(dev, "too short to sleep::%lld < %ld\n",
+ time - jiffies, msecs_to_jiffies(MIN_SLEEP_TIME));
+ return;
+ }
+
+ if ((time - jiffies) > msecs_to_jiffies(MAX_SLEEP_TIME)) {
+ netdev_info(dev, "========>too long to sleep:%lld > %ld\n",
+ time - jiffies, msecs_to_jiffies(MAX_SLEEP_TIME));
+ spin_unlock_irqrestore(&priv->ps_lock, flags);
+ return;
+ }
+ tmp = time - jiffies;
+ queue_delayed_work_rsl(priv->rtllib->wq,
+ &priv->rtllib->hw_wakeup_wq, tmp);
+ queue_delayed_work_rsl(priv->rtllib->wq,
+ (void *)&priv->rtllib->hw_sleep_wq, 0);
+ spin_unlock_irqrestore(&priv->ps_lock, flags);
+}
+
+static void InactivePsWorkItemCallback(struct net_device *dev)
+{
+ struct r8192_priv *priv = rtllib_priv(dev);
+ struct rt_pwr_save_ctrl *pPSC = (struct rt_pwr_save_ctrl *)
+ &(priv->rtllib->PowerSaveControl);
+
+ RT_TRACE(COMP_PS, "InactivePsWorkItemCallback() --------->\n");
+ pPSC->bSwRfProcessing = true;
+
+ RT_TRACE(COMP_PS, "InactivePsWorkItemCallback(): Set RF to %s.\n",
+ pPSC->eInactivePowerState == eRfOff ? "OFF" : "ON");
+ MgntActSet_RF_State(dev, pPSC->eInactivePowerState, RF_CHANGE_BY_IPS,
+ false);
+
+ pPSC->bSwRfProcessing = false;
+ RT_TRACE(COMP_PS, "InactivePsWorkItemCallback() <---------\n");
+}
+
+void IPSEnter(struct net_device *dev)
+{
+ struct r8192_priv *priv = rtllib_priv(dev);
+ struct rt_pwr_save_ctrl *pPSC = (struct rt_pwr_save_ctrl *)
+ &(priv->rtllib->PowerSaveControl);
+ enum rt_rf_power_state rtState;
+
+ if (pPSC->bInactivePs) {
+ rtState = priv->rtllib->eRFPowerState;
+ if (rtState == eRfOn && !pPSC->bSwRfProcessing &&
+ (priv->rtllib->state != RTLLIB_LINKED) &&
+ (priv->rtllib->iw_mode != IW_MODE_MASTER)) {
+ RT_TRACE(COMP_PS, "IPSEnter(): Turn off RF.\n");
+ pPSC->eInactivePowerState = eRfOff;
+ priv->isRFOff = true;
+ priv->bInPowerSaveMode = true;
+ InactivePsWorkItemCallback(dev);
+ }
+ }
+}
+
+void IPSLeave(struct net_device *dev)
+{
+ struct r8192_priv *priv = rtllib_priv(dev);
+ struct rt_pwr_save_ctrl *pPSC = (struct rt_pwr_save_ctrl *)
+ &(priv->rtllib->PowerSaveControl);
+ enum rt_rf_power_state rtState;
+
+ if (pPSC->bInactivePs) {
+ rtState = priv->rtllib->eRFPowerState;
+ if (rtState != eRfOn && !pPSC->bSwRfProcessing &&
+ priv->rtllib->RfOffReason <= RF_CHANGE_BY_IPS) {
+ RT_TRACE(COMP_PS, "IPSLeave(): Turn on RF.\n");
+ pPSC->eInactivePowerState = eRfOn;
+ priv->bInPowerSaveMode = false;
+ InactivePsWorkItemCallback(dev);
+ }
+ }
+}
+
+void IPSLeave_wq(void *data)
+{
+ struct rtllib_device *ieee = container_of_work_rsl(data,
+ struct rtllib_device, ips_leave_wq);
+ struct net_device *dev = ieee->dev;
+ struct r8192_priv *priv = (struct r8192_priv *)rtllib_priv(dev);
+
+ down(&priv->rtllib->ips_sem);
+ IPSLeave(dev);
+ up(&priv->rtllib->ips_sem);
+}
+
+void rtllib_ips_leave_wq(struct net_device *dev)
+{
+ struct r8192_priv *priv = (struct r8192_priv *)rtllib_priv(dev);
+ enum rt_rf_power_state rtState;
+
+ rtState = priv->rtllib->eRFPowerState;
+
+ if (priv->rtllib->PowerSaveControl.bInactivePs) {
+ if (rtState == eRfOff) {
+ if (priv->rtllib->RfOffReason > RF_CHANGE_BY_IPS) {
+ RT_TRACE(COMP_ERR, "%s(): RF is OFF.\n",
+ __func__);
+ return;
+ }
+ netdev_info(dev, "=========>%s(): IPSLeave\n",
+ __func__);
+ queue_work_rsl(priv->rtllib->wq,
+ &priv->rtllib->ips_leave_wq);
+ }
+ }
+}
+
+void rtllib_ips_leave(struct net_device *dev)
+{
+ struct r8192_priv *priv = (struct r8192_priv *)rtllib_priv(dev);
+
+ down(&priv->rtllib->ips_sem);
+ IPSLeave(dev);
+ up(&priv->rtllib->ips_sem);
+}
+
+static bool MgntActSet_802_11_PowerSaveMode(struct net_device *dev,
+ u8 rtPsMode)
+{
+ struct r8192_priv *priv = rtllib_priv(dev);
+
+ if (priv->rtllib->iw_mode == IW_MODE_ADHOC)
+ return false;
+
+ RT_TRACE(COMP_LPS, "%s(): set ieee->ps = %x\n", __func__, rtPsMode);
+ if (!priv->ps_force)
+ priv->rtllib->ps = rtPsMode;
+ if (priv->rtllib->sta_sleep != LPS_IS_WAKE &&
+ rtPsMode == RTLLIB_PS_DISABLED) {
+ unsigned long flags;
+
+ rtl8192_hw_wakeup(dev);
+ priv->rtllib->sta_sleep = LPS_IS_WAKE;
+
+ spin_lock_irqsave(&(priv->rtllib->mgmt_tx_lock), flags);
+ RT_TRACE(COMP_DBG,
+ "LPS leave: notify AP we are awaked ++++++++++ SendNullFunctionData\n");
+ rtllib_sta_ps_send_null_frame(priv->rtllib, 0);
+ spin_unlock_irqrestore(&(priv->rtllib->mgmt_tx_lock), flags);
+ }
+
+ return true;
+}
+
+void LeisurePSEnter(struct net_device *dev)
+{
+ struct r8192_priv *priv = rtllib_priv(dev);
+ struct rt_pwr_save_ctrl *pPSC = (struct rt_pwr_save_ctrl *)
+ &(priv->rtllib->PowerSaveControl);
+
+ RT_TRACE(COMP_PS, "LeisurePSEnter()...\n");
+ RT_TRACE(COMP_PS,
+ "pPSC->bLeisurePs = %d, ieee->ps = %d,pPSC->LpsIdleCount is %d,RT_CHECK_FOR_HANG_PERIOD is %d\n",
+ pPSC->bLeisurePs, priv->rtllib->ps, pPSC->LpsIdleCount,
+ RT_CHECK_FOR_HANG_PERIOD);
+
+ if (!((priv->rtllib->iw_mode == IW_MODE_INFRA) &&
+ (priv->rtllib->state == RTLLIB_LINKED))
+ || (priv->rtllib->iw_mode == IW_MODE_ADHOC) ||
+ (priv->rtllib->iw_mode == IW_MODE_MASTER))
+ return;
+
+ if (pPSC->bLeisurePs) {
+ if (pPSC->LpsIdleCount >= RT_CHECK_FOR_HANG_PERIOD) {
+
+ if (priv->rtllib->ps == RTLLIB_PS_DISABLED) {
+
+ RT_TRACE(COMP_LPS,
+ "LeisurePSEnter(): Enter 802.11 power save mode...\n");
+
+ if (!pPSC->bFwCtrlLPS) {
+ if (priv->rtllib->SetFwCmdHandler)
+ priv->rtllib->SetFwCmdHandler(
+ dev, FW_CMD_LPS_ENTER);
+ }
+ MgntActSet_802_11_PowerSaveMode(dev,
+ RTLLIB_PS_MBCAST |
+ RTLLIB_PS_UNICAST);
+ }
+ } else
+ pPSC->LpsIdleCount++;
+ }
+}
+
+void LeisurePSLeave(struct net_device *dev)
+{
+ struct r8192_priv *priv = rtllib_priv(dev);
+ struct rt_pwr_save_ctrl *pPSC = (struct rt_pwr_save_ctrl *)
+ &(priv->rtllib->PowerSaveControl);
+
+
+ RT_TRACE(COMP_PS, "LeisurePSLeave()...\n");
+ RT_TRACE(COMP_PS, "pPSC->bLeisurePs = %d, ieee->ps = %d\n",
+ pPSC->bLeisurePs, priv->rtllib->ps);
+
+ if (pPSC->bLeisurePs) {
+ if (priv->rtllib->ps != RTLLIB_PS_DISABLED) {
+ RT_TRACE(COMP_LPS,
+ "LeisurePSLeave(): Busy Traffic , Leave 802.11 power save..\n");
+ MgntActSet_802_11_PowerSaveMode(dev,
+ RTLLIB_PS_DISABLED);
+
+ if (!pPSC->bFwCtrlLPS) {
+ if (priv->rtllib->SetFwCmdHandler)
+ priv->rtllib->SetFwCmdHandler(dev,
+ FW_CMD_LPS_LEAVE);
+ }
+ }
+ }
+}
diff --git a/drivers/staging/rtl8192e/rtl8192e/rtl_ps.h b/drivers/staging/rtl8192e/rtl8192e/rtl_ps.h
new file mode 100644
index 000000000..962f2e5b8
--- /dev/null
+++ b/drivers/staging/rtl8192e/rtl8192e/rtl_ps.h
@@ -0,0 +1,47 @@
+/******************************************************************************
+ * Copyright(c) 2008 - 2010 Realtek Corporation. All rights reserved.
+ *
+ * Based on the r8180 driver, which is:
+ * Copyright 2004-2005 Andrea Merello <andrea.merello@gmail.com>, et al.
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ * The full GNU General Public License is included in this distribution in the
+ * file called LICENSE.
+ *
+ * Contact Information:
+ * wlanfae <wlanfae@realtek.com>
+ ******************************************************************************/
+#ifndef _RTL_PS_H
+#define _RTL_PS_H
+
+#include <linux/types.h>
+
+struct net_device;
+
+#define RT_CHECK_FOR_HANG_PERIOD 2
+#define INIT_DEFAULT_CHAN 1
+
+void rtl8192_hw_wakeup(struct net_device *dev);
+void rtl8192_hw_to_sleep(struct net_device *dev, u64 time);
+void rtllib_ips_leave_wq(struct net_device *dev);
+void rtllib_ips_leave(struct net_device *dev);
+void IPSLeave_wq(void *data);
+
+void IPSEnter(struct net_device *dev);
+void IPSLeave(struct net_device *dev);
+
+void LeisurePSEnter(struct net_device *dev);
+void LeisurePSLeave(struct net_device *dev);
+
+#endif
diff --git a/drivers/staging/rtl8192e/rtl8192e/rtl_wx.c b/drivers/staging/rtl8192e/rtl8192e/rtl_wx.c
new file mode 100644
index 000000000..8d6a109e0
--- /dev/null
+++ b/drivers/staging/rtl8192e/rtl8192e/rtl_wx.c
@@ -0,0 +1,1341 @@
+/******************************************************************************
+ * Copyright(c) 2008 - 2010 Realtek Corporation. All rights reserved.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ * The full GNU General Public License is included in this distribution in the
+ * file called LICENSE.
+ *
+ * Contact Information:
+ * wlanfae <wlanfae@realtek.com>
+******************************************************************************/
+
+#include <linux/string.h>
+#include "rtl_core.h"
+#include "rtl_wx.h"
+
+#define RATE_COUNT 12
+static u32 rtl8192_rates[] = {
+ 1000000, 2000000, 5500000, 11000000, 6000000, 9000000, 12000000,
+ 18000000, 24000000, 36000000, 48000000, 54000000
+};
+
+#ifndef ENETDOWN
+#define ENETDOWN 1
+#endif
+
+static int r8192_wx_get_freq(struct net_device *dev,
+ struct iw_request_info *a,
+ union iwreq_data *wrqu, char *b)
+{
+ struct r8192_priv *priv = rtllib_priv(dev);
+
+ return rtllib_wx_get_freq(priv->rtllib, a, wrqu, b);
+}
+
+
+static int r8192_wx_get_mode(struct net_device *dev, struct iw_request_info *a,
+ union iwreq_data *wrqu, char *b)
+{
+ struct r8192_priv *priv = rtllib_priv(dev);
+
+ return rtllib_wx_get_mode(priv->rtllib, a, wrqu, b);
+}
+
+static int r8192_wx_get_rate(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct r8192_priv *priv = rtllib_priv(dev);
+
+ return rtllib_wx_get_rate(priv->rtllib, info, wrqu, extra);
+}
+
+
+
+static int r8192_wx_set_rate(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ int ret;
+ struct r8192_priv *priv = rtllib_priv(dev);
+
+ if (priv->bHwRadioOff)
+ return 0;
+
+ down(&priv->wx_sem);
+
+ ret = rtllib_wx_set_rate(priv->rtllib, info, wrqu, extra);
+
+ up(&priv->wx_sem);
+
+ return ret;
+}
+
+
+static int r8192_wx_set_rts(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ int ret;
+ struct r8192_priv *priv = rtllib_priv(dev);
+
+ if (priv->bHwRadioOff)
+ return 0;
+
+ down(&priv->wx_sem);
+
+ ret = rtllib_wx_set_rts(priv->rtllib, info, wrqu, extra);
+
+ up(&priv->wx_sem);
+
+ return ret;
+}
+
+static int r8192_wx_get_rts(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct r8192_priv *priv = rtllib_priv(dev);
+
+ return rtllib_wx_get_rts(priv->rtllib, info, wrqu, extra);
+}
+
+static int r8192_wx_set_power(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ int ret;
+ struct r8192_priv *priv = rtllib_priv(dev);
+
+ if (priv->bHwRadioOff) {
+ RT_TRACE(COMP_ERR,
+ "%s():Hw is Radio Off, we can't set Power,return\n",
+ __func__);
+ return 0;
+ }
+ down(&priv->wx_sem);
+
+ ret = rtllib_wx_set_power(priv->rtllib, info, wrqu, extra);
+
+ up(&priv->wx_sem);
+
+ return ret;
+}
+
+static int r8192_wx_get_power(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct r8192_priv *priv = rtllib_priv(dev);
+
+ return rtllib_wx_get_power(priv->rtllib, info, wrqu, extra);
+}
+
+static int r8192_wx_set_rawtx(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct r8192_priv *priv = rtllib_priv(dev);
+ int ret;
+
+ if (priv->bHwRadioOff)
+ return 0;
+
+ down(&priv->wx_sem);
+
+ ret = rtllib_wx_set_rawtx(priv->rtllib, info, wrqu, extra);
+
+ up(&priv->wx_sem);
+
+ return ret;
+
+}
+
+static int r8192_wx_force_reset(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct r8192_priv *priv = rtllib_priv(dev);
+
+ down(&priv->wx_sem);
+
+ RT_TRACE(COMP_DBG, "%s(): force reset ! extra is %d\n",
+ __func__, *extra);
+ priv->force_reset = *extra;
+ up(&priv->wx_sem);
+ return 0;
+
+}
+
+static int r8192_wx_force_mic_error(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct r8192_priv *priv = rtllib_priv(dev);
+ struct rtllib_device *ieee = priv->rtllib;
+
+ down(&priv->wx_sem);
+
+ RT_TRACE(COMP_DBG, "%s(): force mic error !\n", __func__);
+ ieee->force_mic_error = true;
+ up(&priv->wx_sem);
+ return 0;
+
+}
+
+#define MAX_ADHOC_PEER_NUM 64
+struct adhoc_peer_entry {
+ unsigned char MacAddr[ETH_ALEN];
+ unsigned char WirelessMode;
+ unsigned char bCurTxBW40MHz;
+};
+struct adhoc_peers_info {
+ struct adhoc_peer_entry Entry[MAX_ADHOC_PEER_NUM];
+ unsigned char num;
+};
+
+static int r8192_wx_get_adhoc_peers(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ return 0;
+}
+
+
+static int r8191se_wx_get_firm_version(struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_param *wrqu, char *extra)
+{
+ return 0;
+}
+
+static int r8192_wx_adapter_power_status(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct r8192_priv *priv = rtllib_priv(dev);
+ struct rt_pwr_save_ctrl *pPSC = (struct rt_pwr_save_ctrl *)
+ (&(priv->rtllib->PowerSaveControl));
+ struct rtllib_device *ieee = priv->rtllib;
+
+ down(&priv->wx_sem);
+
+ RT_TRACE(COMP_POWER, "%s(): %s\n", __func__, (*extra == 6) ?
+ "DC power" : "AC power");
+ if (*extra || priv->force_lps) {
+ priv->ps_force = false;
+ pPSC->bLeisurePs = true;
+ } else {
+ if (priv->rtllib->state == RTLLIB_LINKED)
+ LeisurePSLeave(dev);
+
+ priv->ps_force = true;
+ pPSC->bLeisurePs = false;
+ ieee->ps = *extra;
+ }
+
+ up(&priv->wx_sem);
+
+ return 0;
+}
+
+static int r8192se_wx_set_radio(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct r8192_priv *priv = rtllib_priv(dev);
+
+ down(&priv->wx_sem);
+
+ netdev_info(dev, "%s(): set radio ! extra is %d\n", __func__, *extra);
+ if ((*extra != 0) && (*extra != 1)) {
+ RT_TRACE(COMP_ERR,
+ "%s(): set radio an err value,must 0(radio off) or 1(radio on)\n",
+ __func__);
+ up(&priv->wx_sem);
+ return -1;
+ }
+ priv->sw_radio_on = *extra;
+ up(&priv->wx_sem);
+ return 0;
+
+}
+
+static int r8192se_wx_set_lps_awake_interval(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct r8192_priv *priv = rtllib_priv(dev);
+ struct rt_pwr_save_ctrl *pPSC = (struct rt_pwr_save_ctrl *)
+ (&(priv->rtllib->PowerSaveControl));
+
+ down(&priv->wx_sem);
+
+ netdev_info(dev, "%s(): set lps awake interval ! extra is %d\n",
+ __func__, *extra);
+
+ pPSC->RegMaxLPSAwakeIntvl = *extra;
+ up(&priv->wx_sem);
+ return 0;
+}
+
+static int r8192se_wx_set_force_lps(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct r8192_priv *priv = rtllib_priv(dev);
+
+ down(&priv->wx_sem);
+
+ netdev_info(dev,
+ "%s(): force LPS ! extra is %d (1 is open 0 is close)\n",
+ __func__, *extra);
+ priv->force_lps = *extra;
+ up(&priv->wx_sem);
+ return 0;
+
+}
+
+static int r8192_wx_set_debugflag(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct r8192_priv *priv = rtllib_priv(dev);
+ u8 c = *extra;
+
+ if (priv->bHwRadioOff)
+ return 0;
+
+ netdev_info(dev, "=====>%s(), *extra:%x, debugflag:%x\n", __func__,
+ *extra, rt_global_debug_component);
+ if (c > 0)
+ rt_global_debug_component |= (1<<c);
+ else
+ rt_global_debug_component &= BIT31;
+ return 0;
+}
+
+static int r8192_wx_set_mode(struct net_device *dev, struct iw_request_info *a,
+ union iwreq_data *wrqu, char *b)
+{
+ struct r8192_priv *priv = rtllib_priv(dev);
+ struct rtllib_device *ieee = netdev_priv_rsl(dev);
+
+ enum rt_rf_power_state rtState;
+ int ret;
+
+ if (priv->bHwRadioOff)
+ return 0;
+ rtState = priv->rtllib->eRFPowerState;
+ down(&priv->wx_sem);
+ if (wrqu->mode == IW_MODE_ADHOC || wrqu->mode == IW_MODE_MONITOR ||
+ ieee->bNetPromiscuousMode) {
+ if (priv->rtllib->PowerSaveControl.bInactivePs) {
+ if (rtState == eRfOff) {
+ if (priv->rtllib->RfOffReason >
+ RF_CHANGE_BY_IPS) {
+ RT_TRACE(COMP_ERR, "%s(): RF is OFF.\n",
+ __func__);
+ up(&priv->wx_sem);
+ return -1;
+ }
+ netdev_info(dev, "=========>%s(): IPSLeave\n",
+ __func__);
+ down(&priv->rtllib->ips_sem);
+ IPSLeave(dev);
+ up(&priv->rtllib->ips_sem);
+ }
+ }
+ }
+ ret = rtllib_wx_set_mode(priv->rtllib, a, wrqu, b);
+
+ up(&priv->wx_sem);
+ return ret;
+}
+
+struct iw_range_with_scan_capa {
+ /* Informative stuff (to choose between different interface) */
+ __u32 throughput; /* To give an idea... */
+ /* In theory this value should be the maximum benchmarked
+ * TCP/IP throughput, because with most of these devices the
+ * bit rate is meaningless (overhead an co) to estimate how
+ * fast the connection will go and pick the fastest one.
+ * I suggest people to play with Netperf or any benchmark...
+ */
+
+ /* NWID (or domain id) */
+ __u32 min_nwid; /* Minimal NWID we are able to set */
+ __u32 max_nwid; /* Maximal NWID we are able to set */
+
+ /* Old Frequency (backward compat - moved lower ) */
+ __u16 old_num_channels;
+ __u8 old_num_frequency;
+
+ /* Scan capabilities */
+ __u8 scan_capa;
+};
+
+static int rtl8192_wx_get_range(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct iw_range *range = (struct iw_range *)extra;
+ struct r8192_priv *priv = rtllib_priv(dev);
+ u16 val;
+ int i;
+
+ wrqu->data.length = sizeof(*range);
+ memset(range, 0, sizeof(*range));
+
+ /* ~130 Mb/s real (802.11n) */
+ range->throughput = 130 * 1000 * 1000;
+
+ if (priv->rf_set_sens != NULL) {
+ /* signal level threshold range */
+ range->sensitivity = priv->max_sens;
+ }
+
+ range->max_qual.qual = 100;
+ range->max_qual.level = 0;
+ range->max_qual.noise = 0;
+ range->max_qual.updated = 7; /* Updated all three */
+
+ range->avg_qual.qual = 70; /* > 8% missed beacons is 'bad' */
+ range->avg_qual.level = 0;
+ range->avg_qual.noise = 0;
+ range->avg_qual.updated = 7; /* Updated all three */
+
+ range->num_bitrates = min(RATE_COUNT, IW_MAX_BITRATES);
+
+ for (i = 0; i < range->num_bitrates; i++)
+ range->bitrate[i] = rtl8192_rates[i];
+
+ range->max_rts = DEFAULT_RTS_THRESHOLD;
+ range->min_frag = MIN_FRAG_THRESHOLD;
+ range->max_frag = MAX_FRAG_THRESHOLD;
+
+ range->min_pmp = 0;
+ range->max_pmp = 5000000;
+ range->min_pmt = 0;
+ range->max_pmt = 65535*1000;
+ range->pmp_flags = IW_POWER_PERIOD;
+ range->pmt_flags = IW_POWER_TIMEOUT;
+ range->pm_capa = IW_POWER_PERIOD | IW_POWER_TIMEOUT | IW_POWER_ALL_R;
+ range->we_version_compiled = WIRELESS_EXT;
+ range->we_version_source = 18;
+
+ for (i = 0, val = 0; i < 14; i++) {
+ if ((priv->rtllib->active_channel_map)[i+1]) {
+ range->freq[val].i = i + 1;
+ range->freq[val].m = rtllib_wlan_frequencies[i] *
+ 100000;
+ range->freq[val].e = 1;
+ val++;
+ }
+
+ if (val == IW_MAX_FREQUENCIES)
+ break;
+ }
+ range->num_frequency = val;
+ range->num_channels = val;
+ range->enc_capa = IW_ENC_CAPA_WPA|IW_ENC_CAPA_WPA2|
+ IW_ENC_CAPA_CIPHER_TKIP|IW_ENC_CAPA_CIPHER_CCMP;
+ range->scan_capa = IW_SCAN_CAPA_ESSID | IW_SCAN_CAPA_TYPE;
+
+ /* Event capability (kernel + driver) */
+
+ return 0;
+}
+
+static int r8192_wx_set_scan(struct net_device *dev, struct iw_request_info *a,
+ union iwreq_data *wrqu, char *b)
+{
+ struct r8192_priv *priv = rtllib_priv(dev);
+ struct rtllib_device *ieee = priv->rtllib;
+ enum rt_rf_power_state rtState;
+ int ret;
+
+ if (!(ieee->softmac_features & IEEE_SOFTMAC_SCAN)) {
+ if ((ieee->state >= RTLLIB_ASSOCIATING) &&
+ (ieee->state <= RTLLIB_ASSOCIATING_AUTHENTICATED))
+ return 0;
+ if ((priv->rtllib->state == RTLLIB_LINKED) &&
+ (priv->rtllib->CntAfterLink < 2))
+ return 0;
+ }
+
+ if (priv->bHwRadioOff) {
+ netdev_info(dev, "================>%s(): hwradio off\n",
+ __func__);
+ return 0;
+ }
+ rtState = priv->rtllib->eRFPowerState;
+ if (!priv->up)
+ return -ENETDOWN;
+ if (priv->rtllib->LinkDetectInfo.bBusyTraffic == true)
+ return -EAGAIN;
+
+ if (wrqu->data.flags & IW_SCAN_THIS_ESSID) {
+ struct iw_scan_req *req = (struct iw_scan_req *)b;
+
+ if (req->essid_len) {
+ ieee->current_network.ssid_len = req->essid_len;
+ memcpy(ieee->current_network.ssid, req->essid,
+ req->essid_len);
+ }
+ }
+
+ down(&priv->wx_sem);
+
+ priv->rtllib->FirstIe_InScan = true;
+
+ if (priv->rtllib->state != RTLLIB_LINKED) {
+ if (priv->rtllib->PowerSaveControl.bInactivePs) {
+ if (rtState == eRfOff) {
+ if (priv->rtllib->RfOffReason >
+ RF_CHANGE_BY_IPS) {
+ RT_TRACE(COMP_ERR,
+ "%s(): RF is OFF.\n",
+ __func__);
+ up(&priv->wx_sem);
+ return -1;
+ }
+ RT_TRACE(COMP_PS, "=========>%s(): IPSLeave\n",
+ __func__);
+ down(&priv->rtllib->ips_sem);
+ IPSLeave(dev);
+ up(&priv->rtllib->ips_sem);
+ }
+ }
+ rtllib_stop_scan(priv->rtllib);
+ if (priv->rtllib->LedControlHandler)
+ priv->rtllib->LedControlHandler(dev,
+ LED_CTL_SITE_SURVEY);
+
+ if (priv->rtllib->eRFPowerState != eRfOff) {
+ priv->rtllib->actscanning = true;
+
+ if (ieee->ScanOperationBackupHandler)
+ ieee->ScanOperationBackupHandler(ieee->dev,
+ SCAN_OPT_BACKUP);
+
+ rtllib_start_scan_syncro(priv->rtllib, 0);
+
+ if (ieee->ScanOperationBackupHandler)
+ ieee->ScanOperationBackupHandler(ieee->dev,
+ SCAN_OPT_RESTORE);
+ }
+ ret = 0;
+ } else {
+ priv->rtllib->actscanning = true;
+ ret = rtllib_wx_set_scan(priv->rtllib, a, wrqu, b);
+ }
+
+ up(&priv->wx_sem);
+ return ret;
+}
+
+
+static int r8192_wx_get_scan(struct net_device *dev, struct iw_request_info *a,
+ union iwreq_data *wrqu, char *b)
+{
+
+ int ret;
+ struct r8192_priv *priv = rtllib_priv(dev);
+
+ if (!priv->up)
+ return -ENETDOWN;
+
+ if (priv->bHwRadioOff)
+ return 0;
+
+
+ down(&priv->wx_sem);
+
+ ret = rtllib_wx_get_scan(priv->rtllib, a, wrqu, b);
+
+ up(&priv->wx_sem);
+
+ return ret;
+}
+
+static int r8192_wx_set_essid(struct net_device *dev,
+ struct iw_request_info *a,
+ union iwreq_data *wrqu, char *b)
+{
+ struct r8192_priv *priv = rtllib_priv(dev);
+ int ret;
+
+ if (priv->bHwRadioOff) {
+ netdev_info(dev,
+ "=========>%s():hw radio off,or Rf state is eRfOff, return\n",
+ __func__);
+ return 0;
+ }
+ down(&priv->wx_sem);
+ ret = rtllib_wx_set_essid(priv->rtllib, a, wrqu, b);
+
+ up(&priv->wx_sem);
+
+ return ret;
+}
+
+static int r8192_wx_get_essid(struct net_device *dev,
+ struct iw_request_info *a,
+ union iwreq_data *wrqu, char *b)
+{
+ int ret;
+ struct r8192_priv *priv = rtllib_priv(dev);
+
+ down(&priv->wx_sem);
+
+ ret = rtllib_wx_get_essid(priv->rtllib, a, wrqu, b);
+
+ up(&priv->wx_sem);
+
+ return ret;
+}
+
+static int r8192_wx_set_nick(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct r8192_priv *priv = rtllib_priv(dev);
+
+ if (wrqu->data.length > IW_ESSID_MAX_SIZE)
+ return -E2BIG;
+ down(&priv->wx_sem);
+ wrqu->data.length = min_t(size_t, wrqu->data.length, sizeof(priv->nick));
+ memset(priv->nick, 0, sizeof(priv->nick));
+ memcpy(priv->nick, extra, wrqu->data.length);
+ up(&priv->wx_sem);
+ return 0;
+
+}
+
+static int r8192_wx_get_nick(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct r8192_priv *priv = rtllib_priv(dev);
+
+ down(&priv->wx_sem);
+ wrqu->data.length = strlen(priv->nick);
+ memcpy(extra, priv->nick, wrqu->data.length);
+ wrqu->data.flags = 1; /* active */
+ up(&priv->wx_sem);
+ return 0;
+}
+
+static int r8192_wx_set_freq(struct net_device *dev, struct iw_request_info *a,
+ union iwreq_data *wrqu, char *b)
+{
+ int ret;
+ struct r8192_priv *priv = rtllib_priv(dev);
+
+ if (priv->bHwRadioOff)
+ return 0;
+
+ down(&priv->wx_sem);
+
+ ret = rtllib_wx_set_freq(priv->rtllib, a, wrqu, b);
+
+ up(&priv->wx_sem);
+ return ret;
+}
+
+static int r8192_wx_get_name(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct r8192_priv *priv = rtllib_priv(dev);
+
+ return rtllib_wx_get_name(priv->rtllib, info, wrqu, extra);
+}
+
+
+static int r8192_wx_set_frag(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct r8192_priv *priv = rtllib_priv(dev);
+
+ if (priv->bHwRadioOff)
+ return 0;
+
+ if (wrqu->frag.disabled)
+ priv->rtllib->fts = DEFAULT_FRAG_THRESHOLD;
+ else {
+ if (wrqu->frag.value < MIN_FRAG_THRESHOLD ||
+ wrqu->frag.value > MAX_FRAG_THRESHOLD)
+ return -EINVAL;
+
+ priv->rtllib->fts = wrqu->frag.value & ~0x1;
+ }
+
+ return 0;
+}
+
+
+static int r8192_wx_get_frag(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct r8192_priv *priv = rtllib_priv(dev);
+
+ wrqu->frag.value = priv->rtllib->fts;
+ wrqu->frag.fixed = 0; /* no auto select */
+ wrqu->frag.disabled = (wrqu->frag.value == DEFAULT_FRAG_THRESHOLD);
+
+ return 0;
+}
+
+
+static int r8192_wx_set_wap(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *awrq,
+ char *extra)
+{
+ int ret;
+ struct r8192_priv *priv = rtllib_priv(dev);
+
+ if (priv->bHwRadioOff)
+ return 0;
+
+ down(&priv->wx_sem);
+
+ ret = rtllib_wx_set_wap(priv->rtllib, info, awrq, extra);
+
+ up(&priv->wx_sem);
+
+ return ret;
+
+}
+
+
+static int r8192_wx_get_wap(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct r8192_priv *priv = rtllib_priv(dev);
+
+ return rtllib_wx_get_wap(priv->rtllib, info, wrqu, extra);
+}
+
+
+static int r8192_wx_get_enc(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *key)
+{
+ struct r8192_priv *priv = rtllib_priv(dev);
+
+ return rtllib_wx_get_encode(priv->rtllib, info, wrqu, key);
+}
+
+static int r8192_wx_set_enc(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *key)
+{
+ struct r8192_priv *priv = rtllib_priv(dev);
+ int ret;
+
+ struct rtllib_device *ieee = priv->rtllib;
+ u32 hwkey[4] = {0, 0, 0, 0};
+ u8 mask = 0xff;
+ u32 key_idx = 0;
+ u8 zero_addr[4][6] = {{0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x01},
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x02},
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x03} };
+ int i;
+
+ if (priv->bHwRadioOff)
+ return 0;
+
+ if (!priv->up)
+ return -ENETDOWN;
+
+ priv->rtllib->wx_set_enc = 1;
+ down(&priv->rtllib->ips_sem);
+ IPSLeave(dev);
+ up(&priv->rtllib->ips_sem);
+ down(&priv->wx_sem);
+
+ RT_TRACE(COMP_SEC, "Setting SW wep key");
+ ret = rtllib_wx_set_encode(priv->rtllib, info, wrqu, key);
+ up(&priv->wx_sem);
+
+
+ if (wrqu->encoding.flags & IW_ENCODE_DISABLED) {
+ ieee->pairwise_key_type = ieee->group_key_type = KEY_TYPE_NA;
+ CamResetAllEntry(dev);
+ memset(priv->rtllib->swcamtable, 0,
+ sizeof(struct sw_cam_table) * 32);
+ goto end_hw_sec;
+ }
+ if (wrqu->encoding.length != 0) {
+
+ for (i = 0; i < 4; i++) {
+ hwkey[i] |= key[4*i+0]&mask;
+ if (i == 1 && (4 * i + 1) == wrqu->encoding.length)
+ mask = 0x00;
+ if (i == 3 && (4 * i + 1) == wrqu->encoding.length)
+ mask = 0x00;
+ hwkey[i] |= (key[4 * i + 1] & mask) << 8;
+ hwkey[i] |= (key[4 * i + 2] & mask) << 16;
+ hwkey[i] |= (key[4 * i + 3] & mask) << 24;
+ }
+
+ #define CONF_WEP40 0x4
+ #define CONF_WEP104 0x14
+
+ switch (wrqu->encoding.flags & IW_ENCODE_INDEX) {
+ case 0:
+ key_idx = ieee->crypt_info.tx_keyidx;
+ break;
+ case 1:
+ key_idx = 0;
+ break;
+ case 2:
+ key_idx = 1;
+ break;
+ case 3:
+ key_idx = 2;
+ break;
+ case 4:
+ key_idx = 3;
+ break;
+ default:
+ break;
+ }
+ if (wrqu->encoding.length == 0x5) {
+ ieee->pairwise_key_type = KEY_TYPE_WEP40;
+ EnableHWSecurityConfig8192(dev);
+ }
+
+ else if (wrqu->encoding.length == 0xd) {
+ ieee->pairwise_key_type = KEY_TYPE_WEP104;
+ EnableHWSecurityConfig8192(dev);
+ setKey(dev, key_idx, key_idx, KEY_TYPE_WEP104,
+ zero_addr[key_idx], 0, hwkey);
+ set_swcam(dev, key_idx, key_idx, KEY_TYPE_WEP104,
+ zero_addr[key_idx], 0, hwkey, 0);
+ } else {
+ netdev_info(dev,
+ "wrong type in WEP, not WEP40 and WEP104\n");
+ }
+ }
+
+end_hw_sec:
+ priv->rtllib->wx_set_enc = 0;
+ return ret;
+}
+
+static int r8192_wx_set_scan_type(struct net_device *dev,
+ struct iw_request_info *aa,
+ union iwreq_data *wrqu, char *p)
+{
+ struct r8192_priv *priv = rtllib_priv(dev);
+ int *parms = (int *)p;
+ int mode = parms[0];
+
+ if (priv->bHwRadioOff)
+ return 0;
+
+ priv->rtllib->active_scan = mode;
+
+ return 1;
+}
+
+
+
+#define R8192_MAX_RETRY 255
+static int r8192_wx_set_retry(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct r8192_priv *priv = rtllib_priv(dev);
+ int err = 0;
+
+ if (priv->bHwRadioOff)
+ return 0;
+
+ down(&priv->wx_sem);
+
+ if (wrqu->retry.flags & IW_RETRY_LIFETIME ||
+ wrqu->retry.disabled) {
+ err = -EINVAL;
+ goto exit;
+ }
+ if (!(wrqu->retry.flags & IW_RETRY_LIMIT)) {
+ err = -EINVAL;
+ goto exit;
+ }
+
+ if (wrqu->retry.value > R8192_MAX_RETRY) {
+ err = -EINVAL;
+ goto exit;
+ }
+ if (wrqu->retry.flags & IW_RETRY_MAX) {
+ priv->retry_rts = wrqu->retry.value;
+ DMESG("Setting retry for RTS/CTS data to %d",
+ wrqu->retry.value);
+
+ } else {
+ priv->retry_data = wrqu->retry.value;
+ DMESG("Setting retry for non RTS/CTS data to %d",
+ wrqu->retry.value);
+ }
+
+
+ rtl8192_commit(dev);
+exit:
+ up(&priv->wx_sem);
+
+ return err;
+}
+
+static int r8192_wx_get_retry(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct r8192_priv *priv = rtllib_priv(dev);
+
+
+ wrqu->retry.disabled = 0; /* can't be disabled */
+
+ if ((wrqu->retry.flags & IW_RETRY_TYPE) ==
+ IW_RETRY_LIFETIME)
+ return -EINVAL;
+
+ if (wrqu->retry.flags & IW_RETRY_MAX) {
+ wrqu->retry.flags = IW_RETRY_LIMIT | IW_RETRY_MAX;
+ wrqu->retry.value = priv->retry_rts;
+ } else {
+ wrqu->retry.flags = IW_RETRY_LIMIT | IW_RETRY_MIN;
+ wrqu->retry.value = priv->retry_data;
+ }
+ return 0;
+}
+
+static int r8192_wx_get_sens(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct r8192_priv *priv = rtllib_priv(dev);
+
+ if (priv->rf_set_sens == NULL)
+ return -1; /* we have not this support for this radio */
+ wrqu->sens.value = priv->sens;
+ return 0;
+}
+
+
+static int r8192_wx_set_sens(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+
+ struct r8192_priv *priv = rtllib_priv(dev);
+
+ short err = 0;
+
+ if (priv->bHwRadioOff)
+ return 0;
+
+ down(&priv->wx_sem);
+ if (priv->rf_set_sens == NULL) {
+ err = -1; /* we have not this support for this radio */
+ goto exit;
+ }
+ if (priv->rf_set_sens(dev, wrqu->sens.value) == 0)
+ priv->sens = wrqu->sens.value;
+ else
+ err = -EINVAL;
+
+exit:
+ up(&priv->wx_sem);
+
+ return err;
+}
+
+static int r8192_wx_set_enc_ext(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ int ret = 0;
+ struct r8192_priv *priv = rtllib_priv(dev);
+ struct rtllib_device *ieee = priv->rtllib;
+
+ if (priv->bHwRadioOff)
+ return 0;
+
+ down(&priv->wx_sem);
+
+ priv->rtllib->wx_set_enc = 1;
+ down(&priv->rtllib->ips_sem);
+ IPSLeave(dev);
+ up(&priv->rtllib->ips_sem);
+
+ ret = rtllib_wx_set_encode_ext(ieee, info, wrqu, extra);
+ {
+ u8 broadcast_addr[6] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
+ u8 zero[6] = {0};
+ u32 key[4] = {0};
+ struct iw_encode_ext *ext = (struct iw_encode_ext *)extra;
+ struct iw_point *encoding = &wrqu->encoding;
+ u8 idx = 0, alg = 0, group = 0;
+
+ if ((encoding->flags & IW_ENCODE_DISABLED) ||
+ ext->alg == IW_ENCODE_ALG_NONE) {
+ ieee->pairwise_key_type = ieee->group_key_type
+ = KEY_TYPE_NA;
+ CamResetAllEntry(dev);
+ memset(priv->rtllib->swcamtable, 0,
+ sizeof(struct sw_cam_table) * 32);
+ goto end_hw_sec;
+ }
+ alg = (ext->alg == IW_ENCODE_ALG_CCMP) ? KEY_TYPE_CCMP :
+ ext->alg;
+ idx = encoding->flags & IW_ENCODE_INDEX;
+ if (idx)
+ idx--;
+ group = ext->ext_flags & IW_ENCODE_EXT_GROUP_KEY;
+
+ if ((!group) || (IW_MODE_ADHOC == ieee->iw_mode) ||
+ (alg == KEY_TYPE_WEP40)) {
+ if ((ext->key_len == 13) && (alg == KEY_TYPE_WEP40))
+ alg = KEY_TYPE_WEP104;
+ ieee->pairwise_key_type = alg;
+ EnableHWSecurityConfig8192(dev);
+ }
+ memcpy((u8 *)key, ext->key, 16);
+
+ if ((alg & KEY_TYPE_WEP40) && (ieee->auth_mode != 2)) {
+ if (ext->key_len == 13)
+ ieee->pairwise_key_type = alg = KEY_TYPE_WEP104;
+ setKey(dev, idx, idx, alg, zero, 0, key);
+ set_swcam(dev, idx, idx, alg, zero, 0, key, 0);
+ } else if (group) {
+ ieee->group_key_type = alg;
+ setKey(dev, idx, idx, alg, broadcast_addr, 0, key);
+ set_swcam(dev, idx, idx, alg, broadcast_addr, 0,
+ key, 0);
+ } else {
+ if ((ieee->pairwise_key_type == KEY_TYPE_CCMP) &&
+ ieee->pHTInfo->bCurrentHTSupport)
+ write_nic_byte(dev, 0x173, 1);
+ setKey(dev, 4, idx, alg, (u8 *)ieee->ap_mac_addr,
+ 0, key);
+ set_swcam(dev, 4, idx, alg, (u8 *)ieee->ap_mac_addr,
+ 0, key, 0);
+ }
+
+
+ }
+
+end_hw_sec:
+ priv->rtllib->wx_set_enc = 0;
+ up(&priv->wx_sem);
+ return ret;
+
+}
+static int r8192_wx_set_auth(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *data, char *extra)
+{
+ int ret = 0;
+
+ struct r8192_priv *priv = rtllib_priv(dev);
+
+ if (priv->bHwRadioOff)
+ return 0;
+
+ down(&priv->wx_sem);
+ ret = rtllib_wx_set_auth(priv->rtllib, info, &(data->param), extra);
+ up(&priv->wx_sem);
+ return ret;
+}
+
+static int r8192_wx_set_mlme(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+
+ int ret = 0;
+
+ struct r8192_priv *priv = rtllib_priv(dev);
+
+ if (priv->bHwRadioOff)
+ return 0;
+
+ down(&priv->wx_sem);
+ ret = rtllib_wx_set_mlme(priv->rtllib, info, wrqu, extra);
+ up(&priv->wx_sem);
+ return ret;
+}
+
+static int r8192_wx_set_gen_ie(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *data, char *extra)
+{
+ int ret = 0;
+
+ struct r8192_priv *priv = rtllib_priv(dev);
+
+ if (priv->bHwRadioOff)
+ return 0;
+
+ down(&priv->wx_sem);
+ ret = rtllib_wx_set_gen_ie(priv->rtllib, extra, data->data.length);
+ up(&priv->wx_sem);
+ return ret;
+}
+
+static int r8192_wx_get_gen_ie(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *data, char *extra)
+{
+ int ret = 0;
+ struct r8192_priv *priv = rtllib_priv(dev);
+ struct rtllib_device *ieee = priv->rtllib;
+
+ if (ieee->wpa_ie_len == 0 || ieee->wpa_ie == NULL) {
+ data->data.length = 0;
+ return 0;
+ }
+
+ if (data->data.length < ieee->wpa_ie_len)
+ return -E2BIG;
+
+ data->data.length = ieee->wpa_ie_len;
+ memcpy(extra, ieee->wpa_ie, ieee->wpa_ie_len);
+ return ret;
+}
+
+#define OID_RT_INTEL_PROMISCUOUS_MODE 0xFF0101F6
+
+static int r8192_wx_set_PromiscuousMode(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct r8192_priv *priv = rtllib_priv(dev);
+ struct rtllib_device *ieee = priv->rtllib;
+
+ u32 info_buf[3];
+
+ u32 oid;
+ u32 bPromiscuousOn;
+ u32 bFilterSourceStationFrame;
+
+ if (copy_from_user(info_buf, wrqu->data.pointer, sizeof(info_buf)))
+ return -EFAULT;
+
+ oid = info_buf[0];
+ bPromiscuousOn = info_buf[1];
+ bFilterSourceStationFrame = info_buf[2];
+
+ if (OID_RT_INTEL_PROMISCUOUS_MODE == oid) {
+ ieee->IntelPromiscuousModeInfo.bPromiscuousOn =
+ (bPromiscuousOn) ? (true) : (false);
+ ieee->IntelPromiscuousModeInfo.bFilterSourceStationFrame =
+ (bFilterSourceStationFrame) ? (true) : (false);
+ (bPromiscuousOn) ?
+ (rtllib_EnableIntelPromiscuousMode(dev, false)) :
+ (rtllib_DisableIntelPromiscuousMode(dev, false));
+
+ netdev_info(dev,
+ "=======>%s(), on = %d, filter src sta = %d\n",
+ __func__, bPromiscuousOn,
+ bFilterSourceStationFrame);
+ } else {
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static int r8192_wx_get_PromiscuousMode(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct r8192_priv *priv = rtllib_priv(dev);
+ struct rtllib_device *ieee = priv->rtllib;
+
+ down(&priv->wx_sem);
+
+ snprintf(extra, 45, "PromiscuousMode:%d, FilterSrcSTAFrame:%d",
+ ieee->IntelPromiscuousModeInfo.bPromiscuousOn,
+ ieee->IntelPromiscuousModeInfo.bFilterSourceStationFrame);
+ wrqu->data.length = strlen(extra) + 1;
+
+ up(&priv->wx_sem);
+
+ return 0;
+}
+
+
+#define IW_IOCTL(x) [(x)-SIOCSIWCOMMIT]
+static iw_handler r8192_wx_handlers[] = {
+ IW_IOCTL(SIOCGIWNAME) = r8192_wx_get_name,
+ IW_IOCTL(SIOCSIWFREQ) = r8192_wx_set_freq,
+ IW_IOCTL(SIOCGIWFREQ) = r8192_wx_get_freq,
+ IW_IOCTL(SIOCSIWMODE) = r8192_wx_set_mode,
+ IW_IOCTL(SIOCGIWMODE) = r8192_wx_get_mode,
+ IW_IOCTL(SIOCSIWSENS) = r8192_wx_set_sens,
+ IW_IOCTL(SIOCGIWSENS) = r8192_wx_get_sens,
+ IW_IOCTL(SIOCGIWRANGE) = rtl8192_wx_get_range,
+ IW_IOCTL(SIOCSIWAP) = r8192_wx_set_wap,
+ IW_IOCTL(SIOCGIWAP) = r8192_wx_get_wap,
+ IW_IOCTL(SIOCSIWSCAN) = r8192_wx_set_scan,
+ IW_IOCTL(SIOCGIWSCAN) = r8192_wx_get_scan,
+ IW_IOCTL(SIOCSIWESSID) = r8192_wx_set_essid,
+ IW_IOCTL(SIOCGIWESSID) = r8192_wx_get_essid,
+ IW_IOCTL(SIOCSIWNICKN) = r8192_wx_set_nick,
+ IW_IOCTL(SIOCGIWNICKN) = r8192_wx_get_nick,
+ IW_IOCTL(SIOCSIWRATE) = r8192_wx_set_rate,
+ IW_IOCTL(SIOCGIWRATE) = r8192_wx_get_rate,
+ IW_IOCTL(SIOCSIWRTS) = r8192_wx_set_rts,
+ IW_IOCTL(SIOCGIWRTS) = r8192_wx_get_rts,
+ IW_IOCTL(SIOCSIWFRAG) = r8192_wx_set_frag,
+ IW_IOCTL(SIOCGIWFRAG) = r8192_wx_get_frag,
+ IW_IOCTL(SIOCSIWRETRY) = r8192_wx_set_retry,
+ IW_IOCTL(SIOCGIWRETRY) = r8192_wx_get_retry,
+ IW_IOCTL(SIOCSIWENCODE) = r8192_wx_set_enc,
+ IW_IOCTL(SIOCGIWENCODE) = r8192_wx_get_enc,
+ IW_IOCTL(SIOCSIWPOWER) = r8192_wx_set_power,
+ IW_IOCTL(SIOCGIWPOWER) = r8192_wx_get_power,
+ IW_IOCTL(SIOCSIWGENIE) = r8192_wx_set_gen_ie,
+ IW_IOCTL(SIOCGIWGENIE) = r8192_wx_get_gen_ie,
+ IW_IOCTL(SIOCSIWMLME) = r8192_wx_set_mlme,
+ IW_IOCTL(SIOCSIWAUTH) = r8192_wx_set_auth,
+ IW_IOCTL(SIOCSIWENCODEEXT) = r8192_wx_set_enc_ext,
+};
+
+/* the following rule need to be following,
+ * Odd : get (world access),
+ * even : set (root access)
+ */
+static const struct iw_priv_args r8192_private_args[] = {
+ {
+ SIOCIWFIRSTPRIV + 0x0,
+ IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "set_debugflag"
+ }, {
+ SIOCIWFIRSTPRIV + 0x1,
+ IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "activescan"
+ }, {
+ SIOCIWFIRSTPRIV + 0x2,
+ IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "rawtx"
+ }, {
+ SIOCIWFIRSTPRIV + 0x3,
+ IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "forcereset"
+ }, {
+ SIOCIWFIRSTPRIV + 0x4,
+ IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "force_mic_error"
+ }, {
+ SIOCIWFIRSTPRIV + 0x5,
+ IW_PRIV_TYPE_NONE, IW_PRIV_TYPE_INT|IW_PRIV_SIZE_FIXED|1,
+ "firm_ver"
+ }, {
+ SIOCIWFIRSTPRIV + 0x6,
+ IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED|1, IW_PRIV_TYPE_NONE,
+ "set_power"
+ }, {
+ SIOCIWFIRSTPRIV + 0x9,
+ IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED|1, IW_PRIV_TYPE_NONE,
+ "radio"
+ }, {
+ SIOCIWFIRSTPRIV + 0xa,
+ IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED|1, IW_PRIV_TYPE_NONE,
+ "lps_interv"
+ }, {
+ SIOCIWFIRSTPRIV + 0xb,
+ IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED|1, IW_PRIV_TYPE_NONE,
+ "lps_force"
+ }, {
+ SIOCIWFIRSTPRIV + 0xc,
+ 0, IW_PRIV_TYPE_CHAR|2047, "adhoc_peer_list"
+ }, {
+ SIOCIWFIRSTPRIV + 0x16,
+ IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 3, 0, "setpromisc"
+ }, {
+ SIOCIWFIRSTPRIV + 0x17,
+ 0, IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_FIXED | 45, "getpromisc"
+ }
+
+};
+
+static iw_handler r8192_private_handler[] = {
+ (iw_handler)r8192_wx_set_debugflag, /*SIOCIWSECONDPRIV*/
+ (iw_handler)r8192_wx_set_scan_type,
+ (iw_handler)r8192_wx_set_rawtx,
+ (iw_handler)r8192_wx_force_reset,
+ (iw_handler)r8192_wx_force_mic_error,
+ (iw_handler)r8191se_wx_get_firm_version,
+ (iw_handler)r8192_wx_adapter_power_status,
+ (iw_handler)NULL,
+ (iw_handler)NULL,
+ (iw_handler)r8192se_wx_set_radio,
+ (iw_handler)r8192se_wx_set_lps_awake_interval,
+ (iw_handler)r8192se_wx_set_force_lps,
+ (iw_handler)r8192_wx_get_adhoc_peers,
+ (iw_handler)NULL,
+ (iw_handler)NULL,
+ (iw_handler)NULL,
+ (iw_handler)NULL,
+ (iw_handler)NULL,
+ (iw_handler)NULL,
+ (iw_handler)NULL,
+ (iw_handler)NULL,
+ (iw_handler)NULL,
+ (iw_handler)r8192_wx_set_PromiscuousMode,
+ (iw_handler)r8192_wx_get_PromiscuousMode,
+};
+
+static struct iw_statistics *r8192_get_wireless_stats(struct net_device *dev)
+{
+ struct r8192_priv *priv = rtllib_priv(dev);
+ struct rtllib_device *ieee = priv->rtllib;
+ struct iw_statistics *wstats = &priv->wstats;
+ int tmp_level = 0;
+ int tmp_qual = 0;
+ int tmp_noise = 0;
+
+ if (ieee->state < RTLLIB_LINKED) {
+ wstats->qual.qual = 10;
+ wstats->qual.level = 0;
+ wstats->qual.noise = -100;
+ wstats->qual.updated = IW_QUAL_ALL_UPDATED | IW_QUAL_DBM;
+ return wstats;
+ }
+
+ tmp_level = (&ieee->current_network)->stats.rssi;
+ tmp_qual = (&ieee->current_network)->stats.signal;
+ tmp_noise = (&ieee->current_network)->stats.noise;
+
+ wstats->qual.level = tmp_level;
+ wstats->qual.qual = tmp_qual;
+ wstats->qual.noise = tmp_noise;
+ wstats->qual.updated = IW_QUAL_ALL_UPDATED | IW_QUAL_DBM;
+ return wstats;
+}
+
+const struct iw_handler_def r8192_wx_handlers_def = {
+ .standard = r8192_wx_handlers,
+ .num_standard = ARRAY_SIZE(r8192_wx_handlers),
+ .private = r8192_private_handler,
+ .num_private = ARRAY_SIZE(r8192_private_handler),
+ .num_private_args = sizeof(r8192_private_args) /
+ sizeof(struct iw_priv_args),
+ .get_wireless_stats = r8192_get_wireless_stats,
+ .private_args = (struct iw_priv_args *)r8192_private_args,
+};
diff --git a/drivers/staging/rtl8192e/rtl8192e/rtl_wx.h b/drivers/staging/rtl8192e/rtl8192e/rtl_wx.h
new file mode 100644
index 000000000..58398517f
--- /dev/null
+++ b/drivers/staging/rtl8192e/rtl8192e/rtl_wx.h
@@ -0,0 +1,30 @@
+/******************************************************************************
+ * Copyright(c) 2008 - 2010 Realtek Corporation. All rights reserved.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ * The full GNU General Public License is included in this distribution in the
+ * file called LICENSE.
+ *
+ * Contact Information:
+ * wlanfae <wlanfae@realtek.com>
+******************************************************************************/
+
+#ifndef R819x_WX_H
+#define R819x_WX_H
+
+struct net_device;
+struct iw_handler_def;
+struct iw_statistics;
+
+extern const struct iw_handler_def r8192_wx_handlers_def;
+u16 rtl8192_11n_user_show_rates(struct net_device *dev);
+
+#endif
diff --git a/drivers/staging/rtl8192e/rtl819x_BA.h b/drivers/staging/rtl8192e/rtl819x_BA.h
new file mode 100644
index 000000000..613e14c12
--- /dev/null
+++ b/drivers/staging/rtl8192e/rtl819x_BA.h
@@ -0,0 +1,77 @@
+/******************************************************************************
+ * Copyright(c) 2008 - 2010 Realtek Corporation. All rights reserved.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ * The full GNU General Public License is included in this distribution in the
+ * file called LICENSE.
+ *
+ * Contact Information:
+ * wlanfae <wlanfae@realtek.com>
+******************************************************************************/
+#ifndef _BATYPE_H_
+#define _BATYPE_H_
+
+#define TOTAL_TXBA_NUM 16
+#define TOTAL_RXBA_NUM 16
+
+#define BA_SETUP_TIMEOUT 200
+#define BA_INACT_TIMEOUT 60000
+
+#define BA_POLICY_DELAYED 0
+#define BA_POLICY_IMMEDIATE 1
+
+#define ADDBA_STATUS_SUCCESS 0
+#define ADDBA_STATUS_REFUSED 37
+#define ADDBA_STATUS_INVALID_PARAM 38
+
+#define DELBA_REASON_QSTA_LEAVING 36
+#define DELBA_REASON_END_BA 37
+#define DELBA_REASON_UNKNOWN_BA 38
+#define DELBA_REASON_TIMEOUT 39
+union sequence_control {
+ u16 ShortData;
+ struct {
+ u16 FragNum:4;
+ u16 SeqNum:12;
+ } field;
+};
+
+union ba_param_set {
+ u8 charData[2];
+ u16 shortData;
+ struct {
+ u16 AMSDU_Support:1;
+ u16 BAPolicy:1;
+ u16 TID:4;
+ u16 BufferSize:10;
+ } field;
+};
+
+union delba_param_set {
+ u8 charData[2];
+ u16 shortData;
+ struct {
+ u16 Reserved:11;
+ u16 Initiator:1;
+ u16 TID:4;
+ } field;
+};
+
+struct ba_record {
+ struct timer_list Timer;
+ u8 bValid;
+ u8 DialogToken;
+ union ba_param_set BaParamSet;
+ u16 BaTimeoutValue;
+ union sequence_control BaStartSeqCtrl;
+};
+
+#endif
diff --git a/drivers/staging/rtl8192e/rtl819x_BAProc.c b/drivers/staging/rtl8192e/rtl819x_BAProc.c
new file mode 100644
index 000000000..26258ea8d
--- /dev/null
+++ b/drivers/staging/rtl8192e/rtl819x_BAProc.c
@@ -0,0 +1,571 @@
+/******************************************************************************
+ * Copyright(c) 2008 - 2010 Realtek Corporation. All rights reserved.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ * The full GNU General Public License is included in this distribution in the
+ * file called LICENSE.
+ *
+ * Contact Information:
+ * wlanfae <wlanfae@realtek.com>
+******************************************************************************/
+#include <asm/byteorder.h>
+#include <asm/unaligned.h>
+#include "rtllib.h"
+#include "rtl819x_BA.h"
+
+static void ActivateBAEntry(struct rtllib_device *ieee, struct ba_record *pBA,
+ u16 Time)
+{
+ pBA->bValid = true;
+ if (Time != 0)
+ mod_timer(&pBA->Timer, jiffies + msecs_to_jiffies(Time));
+}
+
+static void DeActivateBAEntry(struct rtllib_device *ieee, struct ba_record *pBA)
+{
+ pBA->bValid = false;
+ del_timer_sync(&pBA->Timer);
+}
+
+static u8 TxTsDeleteBA(struct rtllib_device *ieee, struct tx_ts_record *pTxTs)
+{
+ struct ba_record *pAdmittedBa = &pTxTs->TxAdmittedBARecord;
+ struct ba_record *pPendingBa = &pTxTs->TxPendingBARecord;
+ u8 bSendDELBA = false;
+
+ if (pPendingBa->bValid) {
+ DeActivateBAEntry(ieee, pPendingBa);
+ bSendDELBA = true;
+ }
+
+ if (pAdmittedBa->bValid) {
+ DeActivateBAEntry(ieee, pAdmittedBa);
+ bSendDELBA = true;
+ }
+ return bSendDELBA;
+}
+
+static u8 RxTsDeleteBA(struct rtllib_device *ieee, struct rx_ts_record *pRxTs)
+{
+ struct ba_record *pBa = &pRxTs->RxAdmittedBARecord;
+ u8 bSendDELBA = false;
+
+ if (pBa->bValid) {
+ DeActivateBAEntry(ieee, pBa);
+ bSendDELBA = true;
+ }
+
+ return bSendDELBA;
+}
+
+void ResetBaEntry(struct ba_record *pBA)
+{
+ pBA->bValid = false;
+ pBA->BaParamSet.shortData = 0;
+ pBA->BaTimeoutValue = 0;
+ pBA->DialogToken = 0;
+ pBA->BaStartSeqCtrl.ShortData = 0;
+}
+static struct sk_buff *rtllib_ADDBA(struct rtllib_device *ieee, u8 *Dst,
+ struct ba_record *pBA,
+ u16 StatusCode, u8 type)
+{
+ struct sk_buff *skb = NULL;
+ struct rtllib_hdr_3addr *BAReq = NULL;
+ u8 *tag = NULL;
+ u16 len = ieee->tx_headroom + 9;
+
+ RTLLIB_DEBUG(RTLLIB_DL_TRACE | RTLLIB_DL_BA,
+ "========>%s(), frame(%d) sentd to: %pM, ieee->dev:%p\n",
+ __func__, type, Dst, ieee->dev);
+ if (pBA == NULL) {
+ RTLLIB_DEBUG(RTLLIB_DL_ERR, "pBA is NULL\n");
+ return NULL;
+ }
+ skb = dev_alloc_skb(len + sizeof(struct rtllib_hdr_3addr));
+ if (skb == NULL) {
+ RTLLIB_DEBUG(RTLLIB_DL_ERR, "can't alloc skb for ADDBA_REQ\n");
+ return NULL;
+ }
+
+ memset(skb->data, 0, sizeof(struct rtllib_hdr_3addr));
+
+ skb_reserve(skb, ieee->tx_headroom);
+
+ BAReq = (struct rtllib_hdr_3addr *)skb_put(skb,
+ sizeof(struct rtllib_hdr_3addr));
+
+ memcpy(BAReq->addr1, Dst, ETH_ALEN);
+ memcpy(BAReq->addr2, ieee->dev->dev_addr, ETH_ALEN);
+
+ memcpy(BAReq->addr3, ieee->current_network.bssid, ETH_ALEN);
+ BAReq->frame_ctl = cpu_to_le16(RTLLIB_STYPE_MANAGE_ACT);
+
+ tag = (u8 *)skb_put(skb, 9);
+ *tag++ = ACT_CAT_BA;
+ *tag++ = type;
+ *tag++ = pBA->DialogToken;
+
+ if (ACT_ADDBARSP == type) {
+ RT_TRACE(COMP_DBG, "====>to send ADDBARSP\n");
+
+ put_unaligned_le16(StatusCode, tag);
+ tag += 2;
+ }
+
+ put_unaligned_le16(pBA->BaParamSet.shortData, tag);
+ tag += 2;
+
+ put_unaligned_le16(pBA->BaTimeoutValue, tag);
+ tag += 2;
+
+ if (ACT_ADDBAREQ == type) {
+ memcpy(tag, (u8 *)&(pBA->BaStartSeqCtrl), 2);
+ tag += 2;
+ }
+
+ RTLLIB_DEBUG_DATA(RTLLIB_DL_DATA|RTLLIB_DL_BA, skb->data, skb->len);
+ return skb;
+}
+
+static struct sk_buff *rtllib_DELBA(struct rtllib_device *ieee, u8 *dst,
+ struct ba_record *pBA,
+ enum tr_select TxRxSelect, u16 ReasonCode)
+{
+ union delba_param_set DelbaParamSet;
+ struct sk_buff *skb = NULL;
+ struct rtllib_hdr_3addr *Delba = NULL;
+ u8 *tag = NULL;
+ u16 len = 6 + ieee->tx_headroom;
+
+ if (net_ratelimit())
+ RTLLIB_DEBUG(RTLLIB_DL_TRACE | RTLLIB_DL_BA,
+ "========>%s(), ReasonCode(%d) sentd to: %pM\n",
+ __func__, ReasonCode, dst);
+
+ memset(&DelbaParamSet, 0, 2);
+
+ DelbaParamSet.field.Initiator = (TxRxSelect == TX_DIR) ? 1 : 0;
+ DelbaParamSet.field.TID = pBA->BaParamSet.field.TID;
+
+ skb = dev_alloc_skb(len + sizeof(struct rtllib_hdr_3addr));
+ if (skb == NULL) {
+ RTLLIB_DEBUG(RTLLIB_DL_ERR, "can't alloc skb for ADDBA_REQ\n");
+ return NULL;
+ }
+
+ skb_reserve(skb, ieee->tx_headroom);
+
+ Delba = (struct rtllib_hdr_3addr *) skb_put(skb,
+ sizeof(struct rtllib_hdr_3addr));
+
+ memcpy(Delba->addr1, dst, ETH_ALEN);
+ memcpy(Delba->addr2, ieee->dev->dev_addr, ETH_ALEN);
+ memcpy(Delba->addr3, ieee->current_network.bssid, ETH_ALEN);
+ Delba->frame_ctl = cpu_to_le16(RTLLIB_STYPE_MANAGE_ACT);
+
+ tag = (u8 *)skb_put(skb, 6);
+
+ *tag++ = ACT_CAT_BA;
+ *tag++ = ACT_DELBA;
+
+
+ put_unaligned_le16(DelbaParamSet.shortData, tag);
+ tag += 2;
+
+ put_unaligned_le16(ReasonCode, tag);
+ tag += 2;
+
+ RTLLIB_DEBUG_DATA(RTLLIB_DL_DATA|RTLLIB_DL_BA, skb->data, skb->len);
+ if (net_ratelimit())
+ RTLLIB_DEBUG(RTLLIB_DL_TRACE | RTLLIB_DL_BA, "<=====%s()\n",
+ __func__);
+ return skb;
+}
+
+static void rtllib_send_ADDBAReq(struct rtllib_device *ieee, u8 *dst,
+ struct ba_record *pBA)
+{
+ struct sk_buff *skb = NULL;
+
+ skb = rtllib_ADDBA(ieee, dst, pBA, 0, ACT_ADDBAREQ);
+
+ if (skb) {
+ RT_TRACE(COMP_DBG, "====>to send ADDBAREQ!!!!!\n");
+ softmac_mgmt_xmit(skb, ieee);
+ } else {
+ RTLLIB_DEBUG(RTLLIB_DL_ERR,
+ "alloc skb error in function %s()\n", __func__);
+ }
+}
+
+static void rtllib_send_ADDBARsp(struct rtllib_device *ieee, u8 *dst,
+ struct ba_record *pBA, u16 StatusCode)
+{
+ struct sk_buff *skb = NULL;
+
+ skb = rtllib_ADDBA(ieee, dst, pBA, StatusCode, ACT_ADDBARSP);
+ if (skb)
+ softmac_mgmt_xmit(skb, ieee);
+ else
+ RTLLIB_DEBUG(RTLLIB_DL_ERR,
+ "alloc skb error in function %s()\n", __func__);
+}
+
+static void rtllib_send_DELBA(struct rtllib_device *ieee, u8 *dst,
+ struct ba_record *pBA, enum tr_select TxRxSelect,
+ u16 ReasonCode)
+{
+ struct sk_buff *skb = NULL;
+
+ skb = rtllib_DELBA(ieee, dst, pBA, TxRxSelect, ReasonCode);
+ if (skb)
+ softmac_mgmt_xmit(skb, ieee);
+ else
+ RTLLIB_DEBUG(RTLLIB_DL_ERR,
+ "alloc skb error in function %s()\n", __func__);
+}
+
+int rtllib_rx_ADDBAReq(struct rtllib_device *ieee, struct sk_buff *skb)
+{
+ struct rtllib_hdr_3addr *req = NULL;
+ u16 rc = 0;
+ u8 *dst = NULL, *pDialogToken = NULL, *tag = NULL;
+ struct ba_record *pBA = NULL;
+ union ba_param_set *pBaParamSet = NULL;
+ u16 *pBaTimeoutVal = NULL;
+ union sequence_control *pBaStartSeqCtrl = NULL;
+ struct rx_ts_record *pTS = NULL;
+
+ if (skb->len < sizeof(struct rtllib_hdr_3addr) + 9) {
+ RTLLIB_DEBUG(RTLLIB_DL_ERR,
+ " Invalid skb len in BAREQ(%d / %d)\n",
+ (int)skb->len,
+ (int)(sizeof(struct rtllib_hdr_3addr) + 9));
+ return -1;
+ }
+
+ RTLLIB_DEBUG_DATA(RTLLIB_DL_DATA|RTLLIB_DL_BA, skb->data, skb->len);
+
+ req = (struct rtllib_hdr_3addr *) skb->data;
+ tag = (u8 *)req;
+ dst = (u8 *)(&req->addr2[0]);
+ tag += sizeof(struct rtllib_hdr_3addr);
+ pDialogToken = tag + 2;
+ pBaParamSet = (union ba_param_set *)(tag + 3);
+ pBaTimeoutVal = (u16 *)(tag + 5);
+ pBaStartSeqCtrl = (union sequence_control *)(req + 7);
+
+ RT_TRACE(COMP_DBG, "====>rx ADDBAREQ from : %pM\n", dst);
+ if (ieee->current_network.qos_data.active == 0 ||
+ (ieee->pHTInfo->bCurrentHTSupport == false) ||
+ (ieee->pHTInfo->IOTAction & HT_IOT_ACT_REJECT_ADDBA_REQ)) {
+ rc = ADDBA_STATUS_REFUSED;
+ RTLLIB_DEBUG(RTLLIB_DL_ERR,
+ "Failed to reply on ADDBA_REQ as some capability is not ready(%d, %d)\n",
+ ieee->current_network.qos_data.active,
+ ieee->pHTInfo->bCurrentHTSupport);
+ goto OnADDBAReq_Fail;
+ }
+ if (!GetTs(ieee, (struct ts_common_info **)(&pTS), dst,
+ (u8)(pBaParamSet->field.TID), RX_DIR, true)) {
+ rc = ADDBA_STATUS_REFUSED;
+ RTLLIB_DEBUG(RTLLIB_DL_ERR, "can't get TS in %s()\n", __func__);
+ goto OnADDBAReq_Fail;
+ }
+ pBA = &pTS->RxAdmittedBARecord;
+
+ if (pBaParamSet->field.BAPolicy == BA_POLICY_DELAYED) {
+ rc = ADDBA_STATUS_INVALID_PARAM;
+ RTLLIB_DEBUG(RTLLIB_DL_ERR,
+ "BA Policy is not correct in %s()\n", __func__);
+ goto OnADDBAReq_Fail;
+ }
+
+ rtllib_FlushRxTsPendingPkts(ieee, pTS);
+
+ DeActivateBAEntry(ieee, pBA);
+ pBA->DialogToken = *pDialogToken;
+ pBA->BaParamSet = *pBaParamSet;
+ pBA->BaTimeoutValue = *pBaTimeoutVal;
+ pBA->BaStartSeqCtrl = *pBaStartSeqCtrl;
+
+ if (ieee->GetHalfNmodeSupportByAPsHandler(ieee->dev) ||
+ (ieee->pHTInfo->IOTAction & HT_IOT_ACT_ALLOW_PEER_AGG_ONE_PKT))
+ pBA->BaParamSet.field.BufferSize = 1;
+ else
+ pBA->BaParamSet.field.BufferSize = 32;
+
+ ActivateBAEntry(ieee, pBA, 0);
+ rtllib_send_ADDBARsp(ieee, dst, pBA, ADDBA_STATUS_SUCCESS);
+
+ return 0;
+
+OnADDBAReq_Fail:
+ {
+ struct ba_record BA;
+
+ BA.BaParamSet = *pBaParamSet;
+ BA.BaTimeoutValue = *pBaTimeoutVal;
+ BA.DialogToken = *pDialogToken;
+ BA.BaParamSet.field.BAPolicy = BA_POLICY_IMMEDIATE;
+ rtllib_send_ADDBARsp(ieee, dst, &BA, rc);
+ return 0;
+ }
+}
+
+int rtllib_rx_ADDBARsp(struct rtllib_device *ieee, struct sk_buff *skb)
+{
+ struct rtllib_hdr_3addr *rsp = NULL;
+ struct ba_record *pPendingBA, *pAdmittedBA;
+ struct tx_ts_record *pTS = NULL;
+ u8 *dst = NULL, *pDialogToken = NULL, *tag = NULL;
+ u16 *pStatusCode = NULL, *pBaTimeoutVal = NULL;
+ union ba_param_set *pBaParamSet = NULL;
+ u16 ReasonCode;
+
+ if (skb->len < sizeof(struct rtllib_hdr_3addr) + 9) {
+ RTLLIB_DEBUG(RTLLIB_DL_ERR,
+ "Invalid skb len in BARSP(%d / %d)\n",
+ (int)skb->len,
+ (int)(sizeof(struct rtllib_hdr_3addr) + 9));
+ return -1;
+ }
+ rsp = (struct rtllib_hdr_3addr *)skb->data;
+ tag = (u8 *)rsp;
+ dst = (u8 *)(&rsp->addr2[0]);
+ tag += sizeof(struct rtllib_hdr_3addr);
+ pDialogToken = tag + 2;
+ pStatusCode = (u16 *)(tag + 3);
+ pBaParamSet = (union ba_param_set *)(tag + 5);
+ pBaTimeoutVal = (u16 *)(tag + 7);
+
+ RT_TRACE(COMP_DBG, "====>rx ADDBARSP from : %pM\n", dst);
+ if (ieee->current_network.qos_data.active == 0 ||
+ ieee->pHTInfo->bCurrentHTSupport == false ||
+ ieee->pHTInfo->bCurrentAMPDUEnable == false) {
+ RTLLIB_DEBUG(RTLLIB_DL_ERR,
+ "reject to ADDBA_RSP as some capability is not ready(%d, %d, %d)\n",
+ ieee->current_network.qos_data.active,
+ ieee->pHTInfo->bCurrentHTSupport,
+ ieee->pHTInfo->bCurrentAMPDUEnable);
+ ReasonCode = DELBA_REASON_UNKNOWN_BA;
+ goto OnADDBARsp_Reject;
+ }
+
+
+ if (!GetTs(ieee, (struct ts_common_info **)(&pTS), dst,
+ (u8)(pBaParamSet->field.TID), TX_DIR, false)) {
+ RTLLIB_DEBUG(RTLLIB_DL_ERR, "can't get TS in %s()\n", __func__);
+ ReasonCode = DELBA_REASON_UNKNOWN_BA;
+ goto OnADDBARsp_Reject;
+ }
+
+ pTS->bAddBaReqInProgress = false;
+ pPendingBA = &pTS->TxPendingBARecord;
+ pAdmittedBA = &pTS->TxAdmittedBARecord;
+
+
+ if (pAdmittedBA->bValid == true) {
+ RTLLIB_DEBUG(RTLLIB_DL_BA,
+ "OnADDBARsp(): Recv ADDBA Rsp. Drop because already admit it!\n");
+ return -1;
+ } else if ((pPendingBA->bValid == false) ||
+ (*pDialogToken != pPendingBA->DialogToken)) {
+ RTLLIB_DEBUG(RTLLIB_DL_ERR,
+ "OnADDBARsp(): Recv ADDBA Rsp. BA invalid, DELBA!\n");
+ ReasonCode = DELBA_REASON_UNKNOWN_BA;
+ goto OnADDBARsp_Reject;
+ } else {
+ RTLLIB_DEBUG(RTLLIB_DL_BA,
+ "OnADDBARsp(): Recv ADDBA Rsp. BA is admitted! Status code:%X\n",
+ *pStatusCode);
+ DeActivateBAEntry(ieee, pPendingBA);
+ }
+
+
+ if (*pStatusCode == ADDBA_STATUS_SUCCESS) {
+ if (pBaParamSet->field.BAPolicy == BA_POLICY_DELAYED) {
+ pTS->bAddBaReqDelayed = true;
+ DeActivateBAEntry(ieee, pAdmittedBA);
+ ReasonCode = DELBA_REASON_END_BA;
+ goto OnADDBARsp_Reject;
+ }
+
+
+ pAdmittedBA->DialogToken = *pDialogToken;
+ pAdmittedBA->BaTimeoutValue = *pBaTimeoutVal;
+ pAdmittedBA->BaStartSeqCtrl = pPendingBA->BaStartSeqCtrl;
+ pAdmittedBA->BaParamSet = *pBaParamSet;
+ DeActivateBAEntry(ieee, pAdmittedBA);
+ ActivateBAEntry(ieee, pAdmittedBA, *pBaTimeoutVal);
+ } else {
+ pTS->bAddBaReqDelayed = true;
+ pTS->bDisable_AddBa = true;
+ ReasonCode = DELBA_REASON_END_BA;
+ goto OnADDBARsp_Reject;
+ }
+
+ return 0;
+
+OnADDBARsp_Reject:
+ {
+ struct ba_record BA;
+
+ BA.BaParamSet = *pBaParamSet;
+ rtllib_send_DELBA(ieee, dst, &BA, TX_DIR, ReasonCode);
+ return 0;
+ }
+}
+
+int rtllib_rx_DELBA(struct rtllib_device *ieee, struct sk_buff *skb)
+{
+ struct rtllib_hdr_3addr *delba = NULL;
+ union delba_param_set *pDelBaParamSet = NULL;
+ u16 *pReasonCode = NULL;
+ u8 *dst = NULL;
+
+ if (skb->len < sizeof(struct rtllib_hdr_3addr) + 6) {
+ RTLLIB_DEBUG(RTLLIB_DL_ERR,
+ "Invalid skb len in DELBA(%d / %d)\n",
+ (int)skb->len,
+ (int)(sizeof(struct rtllib_hdr_3addr) + 6));
+ return -1;
+ }
+
+ if (ieee->current_network.qos_data.active == 0 ||
+ ieee->pHTInfo->bCurrentHTSupport == false) {
+ RTLLIB_DEBUG(RTLLIB_DL_ERR,
+ "received DELBA while QOS or HT is not supported(%d, %d)\n",
+ ieee->current_network. qos_data.active,
+ ieee->pHTInfo->bCurrentHTSupport);
+ return -1;
+ }
+
+ RTLLIB_DEBUG_DATA(RTLLIB_DL_DATA|RTLLIB_DL_BA, skb->data, skb->len);
+ delba = (struct rtllib_hdr_3addr *)skb->data;
+ dst = (u8 *)(&delba->addr2[0]);
+ delba += sizeof(struct rtllib_hdr_3addr);
+ pDelBaParamSet = (union delba_param_set *)(delba+2);
+ pReasonCode = (u16 *)(delba+4);
+
+ if (pDelBaParamSet->field.Initiator == 1) {
+ struct rx_ts_record *pRxTs;
+
+ if (!GetTs(ieee, (struct ts_common_info **)&pRxTs, dst,
+ (u8)pDelBaParamSet->field.TID, RX_DIR, false)) {
+ RTLLIB_DEBUG(RTLLIB_DL_ERR,
+ "can't get TS for RXTS in %s().dst: %pM TID:%d\n",
+ __func__, dst,
+ (u8)pDelBaParamSet->field.TID);
+ return -1;
+ }
+
+ RxTsDeleteBA(ieee, pRxTs);
+ } else {
+ struct tx_ts_record *pTxTs;
+
+ if (!GetTs(ieee, (struct ts_common_info **)&pTxTs, dst,
+ (u8)pDelBaParamSet->field.TID, TX_DIR, false)) {
+ RTLLIB_DEBUG(RTLLIB_DL_ERR,
+ "can't get TS for TXTS in %s()\n",
+ __func__);
+ return -1;
+ }
+
+ pTxTs->bUsingBa = false;
+ pTxTs->bAddBaReqInProgress = false;
+ pTxTs->bAddBaReqDelayed = false;
+ del_timer_sync(&pTxTs->TsAddBaTimer);
+ TxTsDeleteBA(ieee, pTxTs);
+ }
+ return 0;
+}
+
+void TsInitAddBA(struct rtllib_device *ieee, struct tx_ts_record *pTS,
+ u8 Policy, u8 bOverwritePending)
+{
+ struct ba_record *pBA = &pTS->TxPendingBARecord;
+
+ if (pBA->bValid == true && bOverwritePending == false)
+ return;
+
+ DeActivateBAEntry(ieee, pBA);
+
+ pBA->DialogToken++;
+ pBA->BaParamSet.field.AMSDU_Support = 0;
+ pBA->BaParamSet.field.BAPolicy = Policy;
+ pBA->BaParamSet.field.TID =
+ pTS->TsCommonInfo.TSpec.f.TSInfo.field.ucTSID;
+ pBA->BaParamSet.field.BufferSize = 32;
+ pBA->BaTimeoutValue = 0;
+ pBA->BaStartSeqCtrl.field.SeqNum = (pTS->TxCurSeq + 3) % 4096;
+
+ ActivateBAEntry(ieee, pBA, BA_SETUP_TIMEOUT);
+
+ rtllib_send_ADDBAReq(ieee, pTS->TsCommonInfo.Addr, pBA);
+}
+
+void TsInitDelBA(struct rtllib_device *ieee,
+ struct ts_common_info *pTsCommonInfo,
+ enum tr_select TxRxSelect)
+{
+ if (TxRxSelect == TX_DIR) {
+ struct tx_ts_record *pTxTs =
+ (struct tx_ts_record *)pTsCommonInfo;
+
+ if (TxTsDeleteBA(ieee, pTxTs))
+ rtllib_send_DELBA(ieee, pTsCommonInfo->Addr,
+ (pTxTs->TxAdmittedBARecord.bValid) ?
+ (&pTxTs->TxAdmittedBARecord) :
+ (&pTxTs->TxPendingBARecord),
+ TxRxSelect, DELBA_REASON_END_BA);
+ } else if (TxRxSelect == RX_DIR) {
+ struct rx_ts_record *pRxTs =
+ (struct rx_ts_record *)pTsCommonInfo;
+ if (RxTsDeleteBA(ieee, pRxTs))
+ rtllib_send_DELBA(ieee, pTsCommonInfo->Addr,
+ &pRxTs->RxAdmittedBARecord,
+ TxRxSelect, DELBA_REASON_END_BA);
+ }
+}
+
+void BaSetupTimeOut(unsigned long data)
+{
+ struct tx_ts_record *pTxTs = (struct tx_ts_record *)data;
+
+ pTxTs->bAddBaReqInProgress = false;
+ pTxTs->bAddBaReqDelayed = true;
+ pTxTs->TxPendingBARecord.bValid = false;
+}
+
+void TxBaInactTimeout(unsigned long data)
+{
+ struct tx_ts_record *pTxTs = (struct tx_ts_record *)data;
+ struct rtllib_device *ieee = container_of(pTxTs, struct rtllib_device,
+ TxTsRecord[pTxTs->num]);
+ TxTsDeleteBA(ieee, pTxTs);
+ rtllib_send_DELBA(ieee, pTxTs->TsCommonInfo.Addr,
+ &pTxTs->TxAdmittedBARecord, TX_DIR,
+ DELBA_REASON_TIMEOUT);
+}
+
+void RxBaInactTimeout(unsigned long data)
+{
+ struct rx_ts_record *pRxTs = (struct rx_ts_record *)data;
+ struct rtllib_device *ieee = container_of(pRxTs, struct rtllib_device,
+ RxTsRecord[pRxTs->num]);
+
+ RxTsDeleteBA(ieee, pRxTs);
+ rtllib_send_DELBA(ieee, pRxTs->TsCommonInfo.Addr,
+ &pRxTs->RxAdmittedBARecord, RX_DIR,
+ DELBA_REASON_TIMEOUT);
+}
diff --git a/drivers/staging/rtl8192e/rtl819x_HT.h b/drivers/staging/rtl8192e/rtl819x_HT.h
new file mode 100644
index 000000000..f7076d7dd
--- /dev/null
+++ b/drivers/staging/rtl8192e/rtl819x_HT.h
@@ -0,0 +1,428 @@
+/******************************************************************************
+ * Copyright(c) 2008 - 2010 Realtek Corporation. All rights reserved.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ * The full GNU General Public License is included in this distribution in the
+ * file called LICENSE.
+ *
+ * Contact Information:
+ * wlanfae <wlanfae@realtek.com>
+******************************************************************************/
+#ifndef _RTL819XU_HTTYPE_H_
+#define _RTL819XU_HTTYPE_H_
+
+
+#define HT_OPMODE_NO_PROTECT 0
+#define HT_OPMODE_OPTIONAL 1
+#define HT_OPMODE_40MHZ_PROTECT 2
+#define HT_OPMODE_MIXED 3
+
+#define MIMO_PS_STATIC 0
+#define MIMO_PS_DYNAMIC 1
+#define MIMO_PS_NOLIMIT 3
+
+
+
+#define sHTCLng 4
+
+
+#define HT_SUPPORTED_MCS_1SS_BITMAP 0x000000ff
+#define HT_SUPPORTED_MCS_2SS_BITMAP 0x0000ff00
+#define HT_SUPPORTED_MCS_1SS_2SS_BITMAP \
+ (HT_MCS_1SS_BITMAP | HT_MCS_1SS_2SS_BITMAP)
+
+enum ht_mcs_rate {
+ HT_MCS0 = 0x00000001,
+ HT_MCS1 = 0x00000002,
+ HT_MCS2 = 0x00000004,
+ HT_MCS3 = 0x00000008,
+ HT_MCS4 = 0x00000010,
+ HT_MCS5 = 0x00000020,
+ HT_MCS6 = 0x00000040,
+ HT_MCS7 = 0x00000080,
+ HT_MCS8 = 0x00000100,
+ HT_MCS9 = 0x00000200,
+ HT_MCS10 = 0x00000400,
+ HT_MCS11 = 0x00000800,
+ HT_MCS12 = 0x00001000,
+ HT_MCS13 = 0x00002000,
+ HT_MCS14 = 0x00004000,
+ HT_MCS15 = 0x00008000,
+};
+
+enum ht_channel_width {
+ HT_CHANNEL_WIDTH_20 = 0,
+ HT_CHANNEL_WIDTH_20_40 = 1,
+};
+
+enum ht_extchnl_offset {
+ HT_EXTCHNL_OFFSET_NO_EXT = 0,
+ HT_EXTCHNL_OFFSET_UPPER = 1,
+ HT_EXTCHNL_OFFSET_NO_DEF = 2,
+ HT_EXTCHNL_OFFSET_LOWER = 3,
+};
+
+enum chnl_op {
+ CHNLOP_NONE = 0,
+ CHNLOP_SCAN = 1,
+ CHNLOP_SWBW = 2,
+ CHNLOP_SWCHNL = 3,
+};
+
+enum ht_action {
+ ACT_RECOMMAND_WIDTH = 0,
+ ACT_MIMO_PWR_SAVE = 1,
+ ACT_PSMP = 2,
+ ACT_SET_PCO_PHASE = 3,
+ ACT_MIMO_CHL_MEASURE = 4,
+ ACT_RECIPROCITY_CORRECT = 5,
+ ACT_MIMO_CSI_MATRICS = 6,
+ ACT_MIMO_NOCOMPR_STEER = 7,
+ ACT_MIMO_COMPR_STEER = 8,
+ ACT_ANTENNA_SELECT = 9,
+};
+
+
+enum ht_bw40_sc {
+ SC_MODE_DUPLICATE = 0,
+ SC_MODE_LOWER = 1,
+ SC_MODE_UPPER = 2,
+ SC_MODE_FULL40MHZ = 3,
+};
+
+struct ht_capab_ele {
+
+ u8 AdvCoding:1;
+ u8 ChlWidth:1;
+ u8 MimoPwrSave:2;
+ u8 GreenField:1;
+ u8 ShortGI20Mhz:1;
+ u8 ShortGI40Mhz:1;
+ u8 TxSTBC:1;
+ u8 RxSTBC:2;
+ u8 DelayBA:1;
+ u8 MaxAMSDUSize:1;
+ u8 DssCCk:1;
+ u8 PSMP:1;
+ u8 Rsvd1:1;
+ u8 LSigTxopProtect:1;
+
+ u8 MaxRxAMPDUFactor:2;
+ u8 MPDUDensity:3;
+ u8 Rsvd2:3;
+
+ u8 MCS[16];
+
+
+ u16 ExtHTCapInfo;
+
+ u8 TxBFCap[4];
+
+ u8 ASCap;
+
+} __packed;
+
+
+struct ht_info_ele {
+ u8 ControlChl;
+
+ u8 ExtChlOffset:2;
+ u8 RecommemdedTxWidth:1;
+ u8 RIFS:1;
+ u8 PSMPAccessOnly:1;
+ u8 SrvIntGranularity:3;
+
+ u8 OptMode:2;
+ u8 NonGFDevPresent:1;
+ u8 Revd1:5;
+ u8 Revd2:8;
+
+ u8 Rsvd3:6;
+ u8 DualBeacon:1;
+ u8 DualCTSProtect:1;
+
+ u8 SecondaryBeacon:1;
+ u8 LSigTxopProtectFull:1;
+ u8 PcoActive:1;
+ u8 PcoPhase:1;
+ u8 Rsvd4:4;
+
+ u8 BasicMSC[16];
+} __packed;
+
+struct mimops_ctrl {
+ u8 MimoPsEnable:1;
+ u8 MimoPsMode:1;
+ u8 Reserved:6;
+};
+
+enum ht_spec_ver {
+ HT_SPEC_VER_IEEE = 0,
+ HT_SPEC_VER_EWC = 1,
+};
+
+enum ht_aggre_mode {
+ HT_AGG_AUTO = 0,
+ HT_AGG_FORCE_ENABLE = 1,
+ HT_AGG_FORCE_DISABLE = 2,
+};
+
+
+struct rt_hi_throughput {
+ u8 bEnableHT;
+ u8 bCurrentHTSupport;
+
+ u8 bRegBW40MHz;
+ u8 bCurBW40MHz;
+
+ u8 bRegShortGI40MHz;
+ u8 bCurShortGI40MHz;
+
+ u8 bRegShortGI20MHz;
+ u8 bCurShortGI20MHz;
+
+ u8 bRegSuppCCK;
+ u8 bCurSuppCCK;
+
+ enum ht_spec_ver ePeerHTSpecVer;
+
+
+ struct ht_capab_ele SelfHTCap;
+ struct ht_info_ele SelfHTInfo;
+
+ u8 PeerHTCapBuf[32];
+ u8 PeerHTInfoBuf[32];
+
+
+ u8 bAMSDU_Support;
+ u16 nAMSDU_MaxSize;
+ u8 bCurrent_AMSDU_Support;
+ u16 nCurrent_AMSDU_MaxSize;
+
+ u8 bAMPDUEnable;
+ u8 bCurrentAMPDUEnable;
+ u8 AMPDU_Factor;
+ u8 CurrentAMPDUFactor;
+ u8 MPDU_Density;
+ u8 CurrentMPDUDensity;
+
+ enum ht_aggre_mode ForcedAMPDUMode;
+ u8 ForcedAMPDUFactor;
+ u8 ForcedMPDUDensity;
+
+ enum ht_aggre_mode ForcedAMSDUMode;
+ u16 ForcedAMSDUMaxSize;
+
+ u8 bForcedShortGI;
+
+ u8 CurrentOpMode;
+
+ u8 SelfMimoPs;
+ u8 PeerMimoPs;
+
+ enum ht_extchnl_offset CurSTAExtChnlOffset;
+ u8 bCurTxBW40MHz;
+ u8 PeerBandwidth;
+
+ u8 bSwBwInProgress;
+ enum chnl_op ChnlOp;
+ u8 SwBwStep;
+
+ u8 bRegRT2RTAggregation;
+ u8 RT2RT_HT_Mode;
+ u8 bCurrentRT2RTAggregation;
+ u8 bCurrentRT2RTLongSlotTime;
+ u8 szRT2RTAggBuffer[10];
+
+ u8 bRegRxReorderEnable;
+ u8 bCurRxReorderEnable;
+ u8 RxReorderWinSize;
+ u8 RxReorderPendingTime;
+ u16 RxReorderDropCounter;
+
+ u8 bIsPeerBcm;
+
+ u8 IOTPeer;
+ u32 IOTAction;
+ u8 IOTRaFunc;
+
+ u8 bWAIotBroadcom;
+ u8 WAIotTH;
+
+ u8 bAcceptAddbaReq;
+} __packed;
+
+
+
+struct rt_htinfo_sta_entry {
+ u8 bEnableHT;
+
+ u8 bSupportCck;
+
+ u16 AMSDU_MaxSize;
+
+ u8 AMPDU_Factor;
+ u8 MPDU_Density;
+
+ u8 HTHighestOperaRate;
+
+ u8 bBw40MHz;
+
+ u8 bCurTxBW40MHz;
+
+ u8 bCurShortGI20MHz;
+
+ u8 bCurShortGI40MHz;
+
+ u8 MimoPs;
+
+ u8 McsRateSet[16];
+
+ u8 bCurRxReorderEnable;
+
+ u16 nAMSDU_MaxSize;
+
+};
+
+
+
+
+
+
+struct bss_ht {
+
+ u8 bdSupportHT;
+
+ u8 bdHTCapBuf[32];
+ u16 bdHTCapLen;
+ u8 bdHTInfoBuf[32];
+ u16 bdHTInfoLen;
+
+ enum ht_spec_ver bdHTSpecVer;
+ enum ht_channel_width bdBandWidth;
+
+ u8 bdRT2RTAggregation;
+ u8 bdRT2RTLongSlotTime;
+ u8 RT2RT_HT_Mode;
+ u8 bdHT1R;
+};
+
+struct mimo_rssi {
+ u32 EnableAntenna;
+ u32 AntennaA;
+ u32 AntennaB;
+ u32 AntennaC;
+ u32 AntennaD;
+ u32 Average;
+};
+
+struct mimo_evm {
+ u32 EVM1;
+ u32 EVM2;
+};
+
+struct false_alarm_stats {
+ u32 Cnt_Parity_Fail;
+ u32 Cnt_Rate_Illegal;
+ u32 Cnt_Crc8_fail;
+ u32 Cnt_Mcs_fail;
+ u32 Cnt_Ofdm_fail;
+ u32 Cnt_Cck_fail;
+ u32 Cnt_all;
+};
+
+
+extern u8 MCS_FILTER_ALL[16];
+extern u8 MCS_FILTER_1SS[16];
+
+#define RATE_ADPT_1SS_MASK 0xFF
+#define RATE_ADPT_2SS_MASK 0xF0
+#define RATE_ADPT_MCS32_MASK 0x01
+
+#define IS_11N_MCS_RATE(rate) (rate&0x80)
+
+enum ht_aggre_size {
+ HT_AGG_SIZE_8K = 0,
+ HT_AGG_SIZE_16K = 1,
+ HT_AGG_SIZE_32K = 2,
+ HT_AGG_SIZE_64K = 3,
+};
+
+enum ht_iot_peer {
+ HT_IOT_PEER_UNKNOWN = 0,
+ HT_IOT_PEER_REALTEK = 1,
+ HT_IOT_PEER_REALTEK_92SE = 2,
+ HT_IOT_PEER_BROADCOM = 3,
+ HT_IOT_PEER_RALINK = 4,
+ HT_IOT_PEER_ATHEROS = 5,
+ HT_IOT_PEER_CISCO = 6,
+ HT_IOT_PEER_MARVELL = 7,
+ HT_IOT_PEER_92U_SOFTAP = 8,
+ HT_IOT_PEER_SELF_SOFTAP = 9,
+ HT_IOT_PEER_AIRGO = 10,
+ HT_IOT_PEER_MAX = 11,
+};
+
+enum ht_iot_peer_subtype {
+ HT_IOT_PEER_ATHEROS_DIR635 = 0,
+};
+
+enum ht_iot_action {
+ HT_IOT_ACT_TX_USE_AMSDU_4K = 0x00000001,
+ HT_IOT_ACT_TX_USE_AMSDU_8K = 0x00000002,
+ HT_IOT_ACT_DISABLE_MCS14 = 0x00000004,
+ HT_IOT_ACT_DISABLE_MCS15 = 0x00000008,
+ HT_IOT_ACT_DISABLE_ALL_2SS = 0x00000010,
+ HT_IOT_ACT_DISABLE_EDCA_TURBO = 0x00000020,
+ HT_IOT_ACT_MGNT_USE_CCK_6M = 0x00000040,
+ HT_IOT_ACT_CDD_FSYNC = 0x00000080,
+ HT_IOT_ACT_PURE_N_MODE = 0x00000100,
+ HT_IOT_ACT_FORCED_CTS2SELF = 0x00000200,
+ HT_IOT_ACT_FORCED_RTS = 0x00000400,
+ HT_IOT_ACT_AMSDU_ENABLE = 0x00000800,
+ HT_IOT_ACT_REJECT_ADDBA_REQ = 0x00001000,
+ HT_IOT_ACT_ALLOW_PEER_AGG_ONE_PKT = 0x00002000,
+ HT_IOT_ACT_EDCA_BIAS_ON_RX = 0x00004000,
+
+ HT_IOT_ACT_HYBRID_AGGREGATION = 0x00010000,
+ HT_IOT_ACT_DISABLE_SHORT_GI = 0x00020000,
+ HT_IOT_ACT_DISABLE_HIGH_POWER = 0x00040000,
+ HT_IOT_ACT_DISABLE_TX_40_MHZ = 0x00080000,
+ HT_IOT_ACT_TX_NO_AGGREGATION = 0x00100000,
+ HT_IOT_ACT_DISABLE_TX_2SS = 0x00200000,
+
+ HT_IOT_ACT_MID_HIGHPOWER = 0x00400000,
+ HT_IOT_ACT_NULL_DATA_POWER_SAVING = 0x00800000,
+
+ HT_IOT_ACT_DISABLE_CCK_RATE = 0x01000000,
+ HT_IOT_ACT_FORCED_ENABLE_BE_TXOP = 0x02000000,
+ HT_IOT_ACT_WA_IOT_Broadcom = 0x04000000,
+
+ HT_IOT_ACT_DISABLE_RX_40MHZ_SHORT_GI = 0x08000000,
+
+};
+
+enum ht_iot_rafunc {
+ HT_IOT_RAFUNC_DISABLE_ALL = 0x00,
+ HT_IOT_RAFUNC_PEER_1R = 0x01,
+ HT_IOT_RAFUNC_TX_AMSDU = 0x02,
+};
+
+enum rt_ht_capability {
+ RT_HT_CAP_USE_TURBO_AGGR = 0x01,
+ RT_HT_CAP_USE_LONG_PREAMBLE = 0x02,
+ RT_HT_CAP_USE_AMPDU = 0x04,
+ RT_HT_CAP_USE_WOW = 0x8,
+ RT_HT_CAP_USE_SOFTAP = 0x10,
+ RT_HT_CAP_USE_92SE = 0x20,
+};
+
+#endif
diff --git a/drivers/staging/rtl8192e/rtl819x_HTProc.c b/drivers/staging/rtl8192e/rtl819x_HTProc.c
new file mode 100644
index 000000000..7f103114d
--- /dev/null
+++ b/drivers/staging/rtl8192e/rtl819x_HTProc.c
@@ -0,0 +1,919 @@
+/******************************************************************************
+ * Copyright(c) 2008 - 2010 Realtek Corporation. All rights reserved.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ * The full GNU General Public License is included in this distribution in the
+ * file called LICENSE.
+ *
+ * Contact Information:
+ * wlanfae <wlanfae@realtek.com>
+******************************************************************************/
+#include "rtllib.h"
+#include "rtl819x_HT.h"
+u8 MCS_FILTER_ALL[16] = {
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+u8 MCS_FILTER_1SS[16] = {
+ 0xff, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}
+;
+
+u16 MCS_DATA_RATE[2][2][77] = {
+ {{13, 26, 39, 52, 78, 104, 117, 130, 26, 52, 78, 104, 156, 208, 234,
+ 260, 39, 78, 117, 234, 312, 351, 390, 52, 104, 156, 208, 312, 416,
+ 468, 520, 0, 78, 104, 130, 117, 156, 195, 104, 130, 130, 156, 182,
+ 182, 208, 156, 195, 195, 234, 273, 273, 312, 130, 156, 181, 156,
+ 181, 208, 234, 208, 234, 260, 260, 286, 195, 234, 273, 234, 273,
+ 312, 351, 312, 351, 390, 390, 429},
+ {14, 29, 43, 58, 87, 116, 130, 144, 29, 58, 87, 116, 173, 231, 260, 289,
+ 43, 87, 130, 173, 260, 347, 390, 433, 58, 116, 173, 231, 347, 462, 520,
+ 578, 0, 87, 116, 144, 130, 173, 217, 116, 144, 144, 173, 202, 202, 231,
+ 173, 217, 217, 260, 303, 303, 347, 144, 173, 202, 173, 202, 231, 260,
+ 231, 260, 289, 289, 318, 217, 260, 303, 260, 303, 347, 390, 347, 390,
+ 433, 433, 477} },
+ {{27, 54, 81, 108, 162, 216, 243, 270, 54, 108, 162, 216, 324, 432, 486,
+ 540, 81, 162, 243, 324, 486, 648, 729, 810, 108, 216, 324, 432, 648,
+ 864, 972, 1080, 12, 162, 216, 270, 243, 324, 405, 216, 270, 270, 324,
+ 378, 378, 432, 324, 405, 405, 486, 567, 567, 648, 270, 324, 378, 324,
+ 378, 432, 486, 432, 486, 540, 540, 594, 405, 486, 567, 486, 567, 648,
+ 729, 648, 729, 810, 810, 891},
+ {30, 60, 90, 120, 180, 240, 270, 300, 60, 120, 180, 240, 360, 480, 540,
+ 600, 90, 180, 270, 360, 540, 720, 810, 900, 120, 240, 360, 480, 720,
+ 960, 1080, 1200, 13, 180, 240, 300, 270, 360, 450, 240, 300, 300, 360,
+ 420, 420, 480, 360, 450, 450, 540, 630, 630, 720, 300, 360, 420, 360,
+ 420, 480, 540, 480, 540, 600, 600, 660, 450, 540, 630, 540, 630, 720,
+ 810, 720, 810, 900, 900, 990} }
+};
+
+static u8 UNKNOWN_BORADCOM[3] = {0x00, 0x14, 0xbf};
+
+static u8 LINKSYSWRT330_LINKSYSWRT300_BROADCOM[3] = {0x00, 0x1a, 0x70};
+
+static u8 LINKSYSWRT350_LINKSYSWRT150_BROADCOM[3] = {0x00, 0x1d, 0x7e};
+
+static u8 BELKINF5D8233V1_RALINK[3] = {0x00, 0x17, 0x3f};
+
+static u8 BELKINF5D82334V3_RALINK[3] = {0x00, 0x1c, 0xdf};
+
+static u8 PCI_RALINK[3] = {0x00, 0x90, 0xcc};
+
+static u8 EDIMAX_RALINK[3] = {0x00, 0x0e, 0x2e};
+
+static u8 AIRLINK_RALINK[3] = {0x00, 0x18, 0x02};
+
+static u8 DLINK_ATHEROS_1[3] = {0x00, 0x1c, 0xf0};
+
+static u8 DLINK_ATHEROS_2[3] = {0x00, 0x21, 0x91};
+
+static u8 CISCO_BROADCOM[3] = {0x00, 0x17, 0x94};
+
+static u8 LINKSYS_MARVELL_4400N[3] = {0x00, 0x14, 0xa4};
+
+void HTUpdateDefaultSetting(struct rtllib_device *ieee)
+{
+ struct rt_hi_throughput *pHTInfo = ieee->pHTInfo;
+
+ pHTInfo->bAcceptAddbaReq = 1;
+
+ pHTInfo->bRegShortGI20MHz = 1;
+ pHTInfo->bRegShortGI40MHz = 1;
+
+ pHTInfo->bRegBW40MHz = 1;
+
+ if (pHTInfo->bRegBW40MHz)
+ pHTInfo->bRegSuppCCK = 1;
+ else
+ pHTInfo->bRegSuppCCK = true;
+
+ pHTInfo->nAMSDU_MaxSize = 7935UL;
+ pHTInfo->bAMSDU_Support = 0;
+
+ pHTInfo->bAMPDUEnable = 1;
+ pHTInfo->AMPDU_Factor = 2;
+ pHTInfo->MPDU_Density = 0;
+
+ pHTInfo->SelfMimoPs = 3;
+ if (pHTInfo->SelfMimoPs == 2)
+ pHTInfo->SelfMimoPs = 3;
+ ieee->bTxDisableRateFallBack = 0;
+ ieee->bTxUseDriverAssingedRate = 0;
+
+ ieee->bTxEnableFwCalcDur = 1;
+
+ pHTInfo->bRegRT2RTAggregation = 1;
+
+ pHTInfo->bRegRxReorderEnable = 1;
+ pHTInfo->RxReorderWinSize = 64;
+ pHTInfo->RxReorderPendingTime = 30;
+}
+
+u16 HTMcsToDataRate(struct rtllib_device *ieee, u8 nMcsRate)
+{
+ struct rt_hi_throughput *pHTInfo = ieee->pHTInfo;
+
+ u8 is40MHz = (pHTInfo->bCurBW40MHz) ? 1 : 0;
+ u8 isShortGI = (pHTInfo->bCurBW40MHz) ?
+ ((pHTInfo->bCurShortGI40MHz) ? 1 : 0) :
+ ((pHTInfo->bCurShortGI20MHz) ? 1 : 0);
+ return MCS_DATA_RATE[is40MHz][isShortGI][(nMcsRate & 0x7f)];
+}
+
+u16 TxCountToDataRate(struct rtllib_device *ieee, u8 nDataRate)
+{
+ u16 CCKOFDMRate[12] = {0x02, 0x04, 0x0b, 0x16, 0x0c, 0x12, 0x18,
+ 0x24, 0x30, 0x48, 0x60, 0x6c};
+ u8 is40MHz = 0;
+ u8 isShortGI = 0;
+
+ if (nDataRate < 12)
+ return CCKOFDMRate[nDataRate];
+ if (nDataRate >= 0x10 && nDataRate <= 0x1f) {
+ is40MHz = 0;
+ isShortGI = 0;
+ } else if (nDataRate >= 0x20 && nDataRate <= 0x2f) {
+ is40MHz = 1;
+ isShortGI = 0;
+ } else if (nDataRate >= 0x30 && nDataRate <= 0x3f) {
+ is40MHz = 0;
+ isShortGI = 1;
+ } else if (nDataRate >= 0x40 && nDataRate <= 0x4f) {
+ is40MHz = 1;
+ isShortGI = 1;
+ }
+ return MCS_DATA_RATE[is40MHz][isShortGI][nDataRate&0xf];
+}
+
+bool IsHTHalfNmodeAPs(struct rtllib_device *ieee)
+{
+ bool retValue = false;
+ struct rtllib_network *net = &ieee->current_network;
+
+ if ((memcmp(net->bssid, BELKINF5D8233V1_RALINK, 3) == 0) ||
+ (memcmp(net->bssid, BELKINF5D82334V3_RALINK, 3) == 0) ||
+ (memcmp(net->bssid, PCI_RALINK, 3) == 0) ||
+ (memcmp(net->bssid, EDIMAX_RALINK, 3) == 0) ||
+ (memcmp(net->bssid, AIRLINK_RALINK, 3) == 0) ||
+ (net->ralink_cap_exist))
+ retValue = true;
+ else if (!memcmp(net->bssid, UNKNOWN_BORADCOM, 3) ||
+ !memcmp(net->bssid, LINKSYSWRT330_LINKSYSWRT300_BROADCOM, 3) ||
+ !memcmp(net->bssid, LINKSYSWRT350_LINKSYSWRT150_BROADCOM, 3) ||
+ (net->broadcom_cap_exist))
+ retValue = true;
+ else if (net->bssht.bdRT2RTAggregation)
+ retValue = true;
+ else
+ retValue = false;
+
+ return retValue;
+}
+
+static void HTIOTPeerDetermine(struct rtllib_device *ieee)
+{
+ struct rt_hi_throughput *pHTInfo = ieee->pHTInfo;
+ struct rtllib_network *net = &ieee->current_network;
+
+ if (net->bssht.bdRT2RTAggregation) {
+ pHTInfo->IOTPeer = HT_IOT_PEER_REALTEK;
+ if (net->bssht.RT2RT_HT_Mode & RT_HT_CAP_USE_92SE)
+ pHTInfo->IOTPeer = HT_IOT_PEER_REALTEK_92SE;
+ if (net->bssht.RT2RT_HT_Mode & RT_HT_CAP_USE_SOFTAP)
+ pHTInfo->IOTPeer = HT_IOT_PEER_92U_SOFTAP;
+ } else if (net->broadcom_cap_exist)
+ pHTInfo->IOTPeer = HT_IOT_PEER_BROADCOM;
+ else if (!memcmp(net->bssid, UNKNOWN_BORADCOM, 3) ||
+ !memcmp(net->bssid, LINKSYSWRT330_LINKSYSWRT300_BROADCOM, 3) ||
+ !memcmp(net->bssid, LINKSYSWRT350_LINKSYSWRT150_BROADCOM, 3))
+ pHTInfo->IOTPeer = HT_IOT_PEER_BROADCOM;
+ else if ((memcmp(net->bssid, BELKINF5D8233V1_RALINK, 3) == 0) ||
+ (memcmp(net->bssid, BELKINF5D82334V3_RALINK, 3) == 0) ||
+ (memcmp(net->bssid, PCI_RALINK, 3) == 0) ||
+ (memcmp(net->bssid, EDIMAX_RALINK, 3) == 0) ||
+ (memcmp(net->bssid, AIRLINK_RALINK, 3) == 0) ||
+ net->ralink_cap_exist)
+ pHTInfo->IOTPeer = HT_IOT_PEER_RALINK;
+ else if ((net->atheros_cap_exist) ||
+ (memcmp(net->bssid, DLINK_ATHEROS_1, 3) == 0) ||
+ (memcmp(net->bssid, DLINK_ATHEROS_2, 3) == 0))
+ pHTInfo->IOTPeer = HT_IOT_PEER_ATHEROS;
+ else if ((memcmp(net->bssid, CISCO_BROADCOM, 3) == 0) ||
+ net->cisco_cap_exist)
+ pHTInfo->IOTPeer = HT_IOT_PEER_CISCO;
+ else if ((memcmp(net->bssid, LINKSYS_MARVELL_4400N, 3) == 0) ||
+ net->marvell_cap_exist)
+ pHTInfo->IOTPeer = HT_IOT_PEER_MARVELL;
+ else if (net->airgo_cap_exist)
+ pHTInfo->IOTPeer = HT_IOT_PEER_AIRGO;
+ else
+ pHTInfo->IOTPeer = HT_IOT_PEER_UNKNOWN;
+
+ RTLLIB_DEBUG(RTLLIB_DL_IOT, "Joseph debug!! IOTPEER: %x\n",
+ pHTInfo->IOTPeer);
+}
+
+static u8 HTIOTActIsDisableMCS14(struct rtllib_device *ieee, u8 *PeerMacAddr)
+{
+ return 0;
+}
+
+
+static bool HTIOTActIsDisableMCS15(struct rtllib_device *ieee)
+{
+ return false;
+}
+
+static bool HTIOTActIsDisableMCSTwoSpatialStream(struct rtllib_device *ieee)
+{
+ return false;
+}
+
+static u8 HTIOTActIsDisableEDCATurbo(struct rtllib_device *ieee, u8 *PeerMacAddr)
+{
+ return false;
+}
+
+static u8 HTIOTActIsMgntUseCCK6M(struct rtllib_device *ieee,
+ struct rtllib_network *network)
+{
+ u8 retValue = 0;
+
+
+ if (ieee->pHTInfo->IOTPeer == HT_IOT_PEER_BROADCOM)
+ retValue = 1;
+
+ return retValue;
+}
+
+static u8 HTIOTActIsCCDFsync(struct rtllib_device *ieee)
+{
+ u8 retValue = 0;
+
+ if (ieee->pHTInfo->IOTPeer == HT_IOT_PEER_BROADCOM)
+ retValue = 1;
+ return retValue;
+}
+
+static void HTIOTActDetermineRaFunc(struct rtllib_device *ieee, bool bPeerRx2ss)
+{
+ struct rt_hi_throughput *pHTInfo = ieee->pHTInfo;
+
+ pHTInfo->IOTRaFunc &= HT_IOT_RAFUNC_DISABLE_ALL;
+
+ if (pHTInfo->IOTPeer == HT_IOT_PEER_RALINK && !bPeerRx2ss)
+ pHTInfo->IOTRaFunc |= HT_IOT_RAFUNC_PEER_1R;
+
+ if (pHTInfo->IOTAction & HT_IOT_ACT_AMSDU_ENABLE)
+ pHTInfo->IOTRaFunc |= HT_IOT_RAFUNC_TX_AMSDU;
+
+}
+
+void HTResetIOTSetting(struct rt_hi_throughput *pHTInfo)
+{
+ pHTInfo->IOTAction = 0;
+ pHTInfo->IOTPeer = HT_IOT_PEER_UNKNOWN;
+ pHTInfo->IOTRaFunc = 0;
+}
+
+void HTConstructCapabilityElement(struct rtllib_device *ieee, u8 *posHTCap,
+ u8 *len, u8 IsEncrypt, bool bAssoc)
+{
+ struct rt_hi_throughput *pHT = ieee->pHTInfo;
+ struct ht_capab_ele *pCapELE = NULL;
+
+ if ((posHTCap == NULL) || (pHT == NULL)) {
+ RTLLIB_DEBUG(RTLLIB_DL_ERR,
+ "posHTCap or pHTInfo can't be null in HTConstructCapabilityElement()\n");
+ return;
+ }
+ memset(posHTCap, 0, *len);
+
+ if ((bAssoc) && (pHT->ePeerHTSpecVer == HT_SPEC_VER_EWC)) {
+ u8 EWC11NHTCap[] = {0x00, 0x90, 0x4c, 0x33};
+
+ memcpy(posHTCap, EWC11NHTCap, sizeof(EWC11NHTCap));
+ pCapELE = (struct ht_capab_ele *)&(posHTCap[4]);
+ *len = 30 + 2;
+ } else {
+ pCapELE = (struct ht_capab_ele *)posHTCap;
+ *len = 26 + 2;
+ }
+
+ pCapELE->AdvCoding = 0;
+ if (ieee->GetHalfNmodeSupportByAPsHandler(ieee->dev))
+ pCapELE->ChlWidth = 0;
+ else
+ pCapELE->ChlWidth = (pHT->bRegBW40MHz ? 1 : 0);
+
+ pCapELE->MimoPwrSave = pHT->SelfMimoPs;
+ pCapELE->GreenField = 0;
+ pCapELE->ShortGI20Mhz = 1;
+ pCapELE->ShortGI40Mhz = 1;
+
+ pCapELE->TxSTBC = 1;
+ pCapELE->RxSTBC = 0;
+ pCapELE->DelayBA = 0;
+ pCapELE->MaxAMSDUSize = (MAX_RECEIVE_BUFFER_SIZE >= 7935) ? 1 : 0;
+ pCapELE->DssCCk = ((pHT->bRegBW40MHz) ? (pHT->bRegSuppCCK ? 1 : 0) : 0);
+ pCapELE->PSMP = 0;
+ pCapELE->LSigTxopProtect = 0;
+
+
+ RTLLIB_DEBUG(RTLLIB_DL_HT,
+ "TX HT cap/info ele BW=%d MaxAMSDUSize:%d DssCCk:%d\n",
+ pCapELE->ChlWidth, pCapELE->MaxAMSDUSize, pCapELE->DssCCk);
+
+ if (IsEncrypt) {
+ pCapELE->MPDUDensity = 7;
+ pCapELE->MaxRxAMPDUFactor = 2;
+ } else {
+ pCapELE->MaxRxAMPDUFactor = 3;
+ pCapELE->MPDUDensity = 0;
+ }
+
+ memcpy(pCapELE->MCS, ieee->Regdot11HTOperationalRateSet, 16);
+ memset(&pCapELE->ExtHTCapInfo, 0, 2);
+ memset(pCapELE->TxBFCap, 0, 4);
+
+ pCapELE->ASCap = 0;
+
+ if (bAssoc) {
+ if (pHT->IOTAction & HT_IOT_ACT_DISABLE_MCS15)
+ pCapELE->MCS[1] &= 0x7f;
+
+ if (pHT->IOTAction & HT_IOT_ACT_DISABLE_MCS14)
+ pCapELE->MCS[1] &= 0xbf;
+
+ if (pHT->IOTAction & HT_IOT_ACT_DISABLE_ALL_2SS)
+ pCapELE->MCS[1] &= 0x00;
+
+ if (pHT->IOTAction & HT_IOT_ACT_DISABLE_RX_40MHZ_SHORT_GI)
+ pCapELE->ShortGI40Mhz = 0;
+
+ if (ieee->GetHalfNmodeSupportByAPsHandler(ieee->dev)) {
+ pCapELE->ChlWidth = 0;
+ pCapELE->MCS[1] = 0;
+ }
+ }
+}
+
+void HTConstructInfoElement(struct rtllib_device *ieee, u8 *posHTInfo,
+ u8 *len, u8 IsEncrypt)
+{
+ struct rt_hi_throughput *pHT = ieee->pHTInfo;
+ struct ht_info_ele *pHTInfoEle = (struct ht_info_ele *)posHTInfo;
+
+ if ((posHTInfo == NULL) || (pHTInfoEle == NULL)) {
+ RTLLIB_DEBUG(RTLLIB_DL_ERR,
+ "posHTInfo or pHTInfoEle can't be null in HTConstructInfoElement()\n");
+ return;
+ }
+
+ memset(posHTInfo, 0, *len);
+ if ((ieee->iw_mode == IW_MODE_ADHOC) ||
+ (ieee->iw_mode == IW_MODE_MASTER)) {
+ pHTInfoEle->ControlChl = ieee->current_network.channel;
+ pHTInfoEle->ExtChlOffset = ((pHT->bRegBW40MHz == false) ?
+ HT_EXTCHNL_OFFSET_NO_EXT :
+ (ieee->current_network.channel <= 6)
+ ? HT_EXTCHNL_OFFSET_UPPER :
+ HT_EXTCHNL_OFFSET_LOWER);
+ pHTInfoEle->RecommemdedTxWidth = pHT->bRegBW40MHz;
+ pHTInfoEle->RIFS = 0;
+ pHTInfoEle->PSMPAccessOnly = 0;
+ pHTInfoEle->SrvIntGranularity = 0;
+ pHTInfoEle->OptMode = pHT->CurrentOpMode;
+ pHTInfoEle->NonGFDevPresent = 0;
+ pHTInfoEle->DualBeacon = 0;
+ pHTInfoEle->SecondaryBeacon = 0;
+ pHTInfoEle->LSigTxopProtectFull = 0;
+ pHTInfoEle->PcoActive = 0;
+ pHTInfoEle->PcoPhase = 0;
+
+ memset(pHTInfoEle->BasicMSC, 0, 16);
+
+
+ *len = 22 + 2;
+
+ } else {
+ *len = 0;
+ }
+}
+
+void HTConstructRT2RTAggElement(struct rtllib_device *ieee, u8 *posRT2RTAgg,
+ u8 *len)
+{
+ if (posRT2RTAgg == NULL) {
+ RTLLIB_DEBUG(RTLLIB_DL_ERR,
+ "posRT2RTAgg can't be null in HTConstructRT2RTAggElement()\n");
+ return;
+ }
+ memset(posRT2RTAgg, 0, *len);
+ *posRT2RTAgg++ = 0x00;
+ *posRT2RTAgg++ = 0xe0;
+ *posRT2RTAgg++ = 0x4c;
+ *posRT2RTAgg++ = 0x02;
+ *posRT2RTAgg++ = 0x01;
+
+ *posRT2RTAgg = 0x30;
+
+ if (ieee->bSupportRemoteWakeUp)
+ *posRT2RTAgg |= RT_HT_CAP_USE_WOW;
+
+ *len = 6 + 2;
+}
+
+static u8 HT_PickMCSRate(struct rtllib_device *ieee, u8 *pOperateMCS)
+{
+ u8 i;
+
+ if (pOperateMCS == NULL) {
+ RTLLIB_DEBUG(RTLLIB_DL_ERR,
+ "pOperateMCS can't be null in HT_PickMCSRate()\n");
+ return false;
+ }
+
+ switch (ieee->mode) {
+ case IEEE_A:
+ case IEEE_B:
+ case IEEE_G:
+ for (i = 0; i <= 15; i++)
+ pOperateMCS[i] = 0;
+ break;
+ case IEEE_N_24G:
+ case IEEE_N_5G:
+ pOperateMCS[0] &= RATE_ADPT_1SS_MASK;
+ pOperateMCS[1] &= RATE_ADPT_2SS_MASK;
+ pOperateMCS[3] &= RATE_ADPT_MCS32_MASK;
+ break;
+ default:
+ break;
+
+ }
+
+ return true;
+}
+
+u8 HTGetHighestMCSRate(struct rtllib_device *ieee, u8 *pMCSRateSet,
+ u8 *pMCSFilter)
+{
+ u8 i, j;
+ u8 bitMap;
+ u8 mcsRate = 0;
+ u8 availableMcsRate[16];
+
+ if (pMCSRateSet == NULL || pMCSFilter == NULL) {
+ RTLLIB_DEBUG(RTLLIB_DL_ERR,
+ "pMCSRateSet or pMCSFilter can't be null in HTGetHighestMCSRate()\n");
+ return false;
+ }
+ for (i = 0; i < 16; i++)
+ availableMcsRate[i] = pMCSRateSet[i] & pMCSFilter[i];
+
+ for (i = 0; i < 16; i++) {
+ if (availableMcsRate[i] != 0)
+ break;
+ }
+ if (i == 16)
+ return false;
+
+ for (i = 0; i < 16; i++) {
+ if (availableMcsRate[i] != 0) {
+ bitMap = availableMcsRate[i];
+ for (j = 0; j < 8; j++) {
+ if ((bitMap%2) != 0) {
+ if (HTMcsToDataRate(ieee, (8*i+j)) >
+ HTMcsToDataRate(ieee, mcsRate))
+ mcsRate = (8*i+j);
+ }
+ bitMap >>= 1;
+ }
+ }
+ }
+ return mcsRate | 0x80;
+}
+
+u8 HTFilterMCSRate(struct rtllib_device *ieee, u8 *pSupportMCS, u8 *pOperateMCS)
+{
+
+ u8 i;
+
+ for (i = 0; i <= 15; i++)
+ pOperateMCS[i] = ieee->Regdot11TxHTOperationalRateSet[i] &
+ pSupportMCS[i];
+
+ HT_PickMCSRate(ieee, pOperateMCS);
+
+ if (ieee->GetHalfNmodeSupportByAPsHandler(ieee->dev))
+ pOperateMCS[1] = 0;
+
+ for (i = 2; i <= 15; i++)
+ pOperateMCS[i] = 0;
+
+ return true;
+}
+
+void HTSetConnectBwMode(struct rtllib_device *ieee,
+ enum ht_channel_width Bandwidth,
+ enum ht_extchnl_offset Offset);
+
+void HTOnAssocRsp(struct rtllib_device *ieee)
+{
+ struct rt_hi_throughput *pHTInfo = ieee->pHTInfo;
+ struct ht_capab_ele *pPeerHTCap = NULL;
+ struct ht_info_ele *pPeerHTInfo = NULL;
+ u16 nMaxAMSDUSize = 0;
+ u8 *pMcsFilter = NULL;
+
+ static u8 EWC11NHTCap[] = {0x00, 0x90, 0x4c, 0x33};
+ static u8 EWC11NHTInfo[] = {0x00, 0x90, 0x4c, 0x34};
+
+ if (pHTInfo->bCurrentHTSupport == false) {
+ RTLLIB_DEBUG(RTLLIB_DL_ERR,
+ "<=== HTOnAssocRsp(): HT_DISABLE\n");
+ return;
+ }
+ RTLLIB_DEBUG(RTLLIB_DL_HT, "===> HTOnAssocRsp_wq(): HT_ENABLE\n");
+
+ if (!memcmp(pHTInfo->PeerHTCapBuf, EWC11NHTCap, sizeof(EWC11NHTCap)))
+ pPeerHTCap = (struct ht_capab_ele *)(&pHTInfo->PeerHTCapBuf[4]);
+ else
+ pPeerHTCap = (struct ht_capab_ele *)(pHTInfo->PeerHTCapBuf);
+
+ if (!memcmp(pHTInfo->PeerHTInfoBuf, EWC11NHTInfo, sizeof(EWC11NHTInfo)))
+ pPeerHTInfo = (struct ht_info_ele *)
+ (&pHTInfo->PeerHTInfoBuf[4]);
+ else
+ pPeerHTInfo = (struct ht_info_ele *)(pHTInfo->PeerHTInfoBuf);
+
+ RTLLIB_DEBUG_DATA(RTLLIB_DL_DATA | RTLLIB_DL_HT, pPeerHTCap,
+ sizeof(struct ht_capab_ele));
+ HTSetConnectBwMode(ieee, (enum ht_channel_width)(pPeerHTCap->ChlWidth),
+ (enum ht_extchnl_offset)(pPeerHTInfo->ExtChlOffset));
+ pHTInfo->bCurTxBW40MHz = ((pPeerHTInfo->RecommemdedTxWidth == 1) ?
+ true : false);
+
+ pHTInfo->bCurShortGI20MHz = ((pHTInfo->bRegShortGI20MHz) ?
+ ((pPeerHTCap->ShortGI20Mhz == 1) ?
+ true : false) : false);
+ pHTInfo->bCurShortGI40MHz = ((pHTInfo->bRegShortGI40MHz) ?
+ ((pPeerHTCap->ShortGI40Mhz == 1) ?
+ true : false) : false);
+
+ pHTInfo->bCurSuppCCK = ((pHTInfo->bRegSuppCCK) ?
+ ((pPeerHTCap->DssCCk == 1) ? true :
+ false) : false);
+
+
+ pHTInfo->bCurrent_AMSDU_Support = pHTInfo->bAMSDU_Support;
+
+ nMaxAMSDUSize = (pPeerHTCap->MaxAMSDUSize == 0) ? 3839 : 7935;
+
+ if (pHTInfo->nAMSDU_MaxSize > nMaxAMSDUSize)
+ pHTInfo->nCurrent_AMSDU_MaxSize = nMaxAMSDUSize;
+ else
+ pHTInfo->nCurrent_AMSDU_MaxSize = pHTInfo->nAMSDU_MaxSize;
+
+ pHTInfo->bCurrentAMPDUEnable = pHTInfo->bAMPDUEnable;
+ if (ieee->rtllib_ap_sec_type &&
+ (ieee->rtllib_ap_sec_type(ieee)&(SEC_ALG_WEP|SEC_ALG_TKIP))) {
+ if ((pHTInfo->IOTPeer == HT_IOT_PEER_ATHEROS) ||
+ (pHTInfo->IOTPeer == HT_IOT_PEER_UNKNOWN))
+ pHTInfo->bCurrentAMPDUEnable = false;
+ }
+
+ if (!pHTInfo->bRegRT2RTAggregation) {
+ if (pHTInfo->AMPDU_Factor > pPeerHTCap->MaxRxAMPDUFactor)
+ pHTInfo->CurrentAMPDUFactor =
+ pPeerHTCap->MaxRxAMPDUFactor;
+ else
+ pHTInfo->CurrentAMPDUFactor = pHTInfo->AMPDU_Factor;
+
+ } else {
+ if (ieee->current_network.bssht.bdRT2RTAggregation) {
+ if (ieee->pairwise_key_type != KEY_TYPE_NA)
+ pHTInfo->CurrentAMPDUFactor =
+ pPeerHTCap->MaxRxAMPDUFactor;
+ else
+ pHTInfo->CurrentAMPDUFactor = HT_AGG_SIZE_64K;
+ } else {
+ if (pPeerHTCap->MaxRxAMPDUFactor < HT_AGG_SIZE_32K)
+ pHTInfo->CurrentAMPDUFactor =
+ pPeerHTCap->MaxRxAMPDUFactor;
+ else
+ pHTInfo->CurrentAMPDUFactor = HT_AGG_SIZE_32K;
+ }
+ }
+ if (pHTInfo->MPDU_Density > pPeerHTCap->MPDUDensity)
+ pHTInfo->CurrentMPDUDensity = pHTInfo->MPDU_Density;
+ else
+ pHTInfo->CurrentMPDUDensity = pPeerHTCap->MPDUDensity;
+ if (pHTInfo->IOTAction & HT_IOT_ACT_TX_USE_AMSDU_8K) {
+ pHTInfo->bCurrentAMPDUEnable = false;
+ pHTInfo->ForcedAMSDUMode = HT_AGG_FORCE_ENABLE;
+ pHTInfo->ForcedAMSDUMaxSize = 7935;
+ }
+ pHTInfo->bCurRxReorderEnable = pHTInfo->bRegRxReorderEnable;
+
+ if (pPeerHTCap->MCS[0] == 0)
+ pPeerHTCap->MCS[0] = 0xff;
+
+ HTIOTActDetermineRaFunc(ieee, ((pPeerHTCap->MCS[1]) != 0));
+
+ HTFilterMCSRate(ieee, pPeerHTCap->MCS, ieee->dot11HTOperationalRateSet);
+
+ pHTInfo->PeerMimoPs = pPeerHTCap->MimoPwrSave;
+ if (pHTInfo->PeerMimoPs == MIMO_PS_STATIC)
+ pMcsFilter = MCS_FILTER_1SS;
+ else
+ pMcsFilter = MCS_FILTER_ALL;
+ ieee->HTHighestOperaRate = HTGetHighestMCSRate(ieee,
+ ieee->dot11HTOperationalRateSet, pMcsFilter);
+ ieee->HTCurrentOperaRate = ieee->HTHighestOperaRate;
+
+ pHTInfo->CurrentOpMode = pPeerHTInfo->OptMode;
+}
+
+void HTInitializeHTInfo(struct rtllib_device *ieee)
+{
+ struct rt_hi_throughput *pHTInfo = ieee->pHTInfo;
+
+ RTLLIB_DEBUG(RTLLIB_DL_HT, "===========>%s()\n", __func__);
+ pHTInfo->bCurrentHTSupport = false;
+
+ pHTInfo->bCurBW40MHz = false;
+ pHTInfo->bCurTxBW40MHz = false;
+
+ pHTInfo->bCurShortGI20MHz = false;
+ pHTInfo->bCurShortGI40MHz = false;
+ pHTInfo->bForcedShortGI = false;
+
+ pHTInfo->bCurSuppCCK = true;
+
+ pHTInfo->bCurrent_AMSDU_Support = false;
+ pHTInfo->nCurrent_AMSDU_MaxSize = pHTInfo->nAMSDU_MaxSize;
+ pHTInfo->CurrentMPDUDensity = pHTInfo->MPDU_Density;
+ pHTInfo->CurrentAMPDUFactor = pHTInfo->AMPDU_Factor;
+
+ memset((void *)(&(pHTInfo->SelfHTCap)), 0,
+ sizeof(pHTInfo->SelfHTCap));
+ memset((void *)(&(pHTInfo->SelfHTInfo)), 0,
+ sizeof(pHTInfo->SelfHTInfo));
+ memset((void *)(&(pHTInfo->PeerHTCapBuf)), 0,
+ sizeof(pHTInfo->PeerHTCapBuf));
+ memset((void *)(&(pHTInfo->PeerHTInfoBuf)), 0,
+ sizeof(pHTInfo->PeerHTInfoBuf));
+
+ pHTInfo->bSwBwInProgress = false;
+ pHTInfo->ChnlOp = CHNLOP_NONE;
+
+ pHTInfo->ePeerHTSpecVer = HT_SPEC_VER_IEEE;
+
+ pHTInfo->bCurrentRT2RTAggregation = false;
+ pHTInfo->bCurrentRT2RTLongSlotTime = false;
+ pHTInfo->RT2RT_HT_Mode = (enum rt_ht_capability)0;
+
+ pHTInfo->IOTPeer = 0;
+ pHTInfo->IOTAction = 0;
+ pHTInfo->IOTRaFunc = 0;
+
+ {
+ u8 *RegHTSuppRateSets = &(ieee->RegHTSuppRateSet[0]);
+
+ RegHTSuppRateSets[0] = 0xFF;
+ RegHTSuppRateSets[1] = 0xFF;
+ RegHTSuppRateSets[4] = 0x01;
+ }
+}
+
+void HTInitializeBssDesc(struct bss_ht *pBssHT)
+{
+
+ pBssHT->bdSupportHT = false;
+ memset(pBssHT->bdHTCapBuf, 0, sizeof(pBssHT->bdHTCapBuf));
+ pBssHT->bdHTCapLen = 0;
+ memset(pBssHT->bdHTInfoBuf, 0, sizeof(pBssHT->bdHTInfoBuf));
+ pBssHT->bdHTInfoLen = 0;
+
+ pBssHT->bdHTSpecVer = HT_SPEC_VER_IEEE;
+
+ pBssHT->bdRT2RTAggregation = false;
+ pBssHT->bdRT2RTLongSlotTime = false;
+ pBssHT->RT2RT_HT_Mode = (enum rt_ht_capability)0;
+}
+
+void HTResetSelfAndSavePeerSetting(struct rtllib_device *ieee,
+ struct rtllib_network *pNetwork)
+{
+ struct rt_hi_throughput *pHTInfo = ieee->pHTInfo;
+ u8 bIOTAction = 0;
+
+ RTLLIB_DEBUG(RTLLIB_DL_HT, "==============>%s()\n", __func__);
+ /* unmark bEnableHT flag here is the same reason why unmarked in
+ * function rtllib_softmac_new_net. WB 2008.09.10
+ */
+ if (pNetwork->bssht.bdSupportHT) {
+ pHTInfo->bCurrentHTSupport = true;
+ pHTInfo->ePeerHTSpecVer = pNetwork->bssht.bdHTSpecVer;
+
+ if (pNetwork->bssht.bdHTCapLen > 0 &&
+ pNetwork->bssht.bdHTCapLen <= sizeof(pHTInfo->PeerHTCapBuf))
+ memcpy(pHTInfo->PeerHTCapBuf,
+ pNetwork->bssht.bdHTCapBuf,
+ pNetwork->bssht.bdHTCapLen);
+
+ if (pNetwork->bssht.bdHTInfoLen > 0 &&
+ pNetwork->bssht.bdHTInfoLen <=
+ sizeof(pHTInfo->PeerHTInfoBuf))
+ memcpy(pHTInfo->PeerHTInfoBuf,
+ pNetwork->bssht.bdHTInfoBuf,
+ pNetwork->bssht.bdHTInfoLen);
+
+ if (pHTInfo->bRegRT2RTAggregation) {
+ pHTInfo->bCurrentRT2RTAggregation =
+ pNetwork->bssht.bdRT2RTAggregation;
+ pHTInfo->bCurrentRT2RTLongSlotTime =
+ pNetwork->bssht.bdRT2RTLongSlotTime;
+ pHTInfo->RT2RT_HT_Mode = pNetwork->bssht.RT2RT_HT_Mode;
+ } else {
+ pHTInfo->bCurrentRT2RTAggregation = false;
+ pHTInfo->bCurrentRT2RTLongSlotTime = false;
+ pHTInfo->RT2RT_HT_Mode = (enum rt_ht_capability)0;
+ }
+
+ HTIOTPeerDetermine(ieee);
+
+ pHTInfo->IOTAction = 0;
+ bIOTAction = HTIOTActIsDisableMCS14(ieee, pNetwork->bssid);
+ if (bIOTAction)
+ pHTInfo->IOTAction |= HT_IOT_ACT_DISABLE_MCS14;
+
+ bIOTAction = HTIOTActIsDisableMCS15(ieee);
+ if (bIOTAction)
+ pHTInfo->IOTAction |= HT_IOT_ACT_DISABLE_MCS15;
+
+ bIOTAction = HTIOTActIsDisableMCSTwoSpatialStream(ieee);
+ if (bIOTAction)
+ pHTInfo->IOTAction |= HT_IOT_ACT_DISABLE_ALL_2SS;
+
+
+ bIOTAction = HTIOTActIsDisableEDCATurbo(ieee, pNetwork->bssid);
+ if (bIOTAction)
+ pHTInfo->IOTAction |= HT_IOT_ACT_DISABLE_EDCA_TURBO;
+
+ bIOTAction = HTIOTActIsMgntUseCCK6M(ieee, pNetwork);
+ if (bIOTAction)
+ pHTInfo->IOTAction |= HT_IOT_ACT_MGNT_USE_CCK_6M;
+ bIOTAction = HTIOTActIsCCDFsync(ieee);
+ if (bIOTAction)
+ pHTInfo->IOTAction |= HT_IOT_ACT_CDD_FSYNC;
+ } else {
+ pHTInfo->bCurrentHTSupport = false;
+ pHTInfo->bCurrentRT2RTAggregation = false;
+ pHTInfo->bCurrentRT2RTLongSlotTime = false;
+ pHTInfo->RT2RT_HT_Mode = (enum rt_ht_capability)0;
+
+ pHTInfo->IOTAction = 0;
+ pHTInfo->IOTRaFunc = 0;
+ }
+}
+
+void HT_update_self_and_peer_setting(struct rtllib_device *ieee,
+ struct rtllib_network *pNetwork)
+{
+ struct rt_hi_throughput *pHTInfo = ieee->pHTInfo;
+ struct ht_info_ele *pPeerHTInfo =
+ (struct ht_info_ele *)pNetwork->bssht.bdHTInfoBuf;
+
+ if (pHTInfo->bCurrentHTSupport) {
+ if (pNetwork->bssht.bdHTInfoLen != 0)
+ pHTInfo->CurrentOpMode = pPeerHTInfo->OptMode;
+ }
+}
+EXPORT_SYMBOL(HT_update_self_and_peer_setting);
+
+void HTUseDefaultSetting(struct rtllib_device *ieee)
+{
+ struct rt_hi_throughput *pHTInfo = ieee->pHTInfo;
+
+ if (pHTInfo->bEnableHT) {
+ pHTInfo->bCurrentHTSupport = true;
+ pHTInfo->bCurSuppCCK = pHTInfo->bRegSuppCCK;
+
+ pHTInfo->bCurBW40MHz = pHTInfo->bRegBW40MHz;
+ pHTInfo->bCurShortGI20MHz = pHTInfo->bRegShortGI20MHz;
+
+ pHTInfo->bCurShortGI40MHz = pHTInfo->bRegShortGI40MHz;
+
+ if (ieee->iw_mode == IW_MODE_ADHOC)
+ ieee->current_network.qos_data.active =
+ ieee->current_network.qos_data.supported;
+ pHTInfo->bCurrent_AMSDU_Support = pHTInfo->bAMSDU_Support;
+ pHTInfo->nCurrent_AMSDU_MaxSize = pHTInfo->nAMSDU_MaxSize;
+
+ pHTInfo->bCurrentAMPDUEnable = pHTInfo->bAMPDUEnable;
+ pHTInfo->CurrentAMPDUFactor = pHTInfo->AMPDU_Factor;
+
+ pHTInfo->CurrentMPDUDensity = pHTInfo->CurrentMPDUDensity;
+
+ HTFilterMCSRate(ieee, ieee->Regdot11TxHTOperationalRateSet,
+ ieee->dot11HTOperationalRateSet);
+ ieee->HTHighestOperaRate = HTGetHighestMCSRate(ieee,
+ ieee->dot11HTOperationalRateSet,
+ MCS_FILTER_ALL);
+ ieee->HTCurrentOperaRate = ieee->HTHighestOperaRate;
+
+ } else {
+ pHTInfo->bCurrentHTSupport = false;
+ }
+}
+
+u8 HTCCheck(struct rtllib_device *ieee, u8 *pFrame)
+{
+ if (ieee->pHTInfo->bCurrentHTSupport) {
+ if ((IsQoSDataFrame(pFrame) && Frame_Order(pFrame)) == 1) {
+ RTLLIB_DEBUG(RTLLIB_DL_HT,
+ "HT CONTROL FILED EXIST!!\n");
+ return true;
+ }
+ }
+ return false;
+}
+
+static void HTSetConnectBwModeCallback(struct rtllib_device *ieee)
+{
+ struct rt_hi_throughput *pHTInfo = ieee->pHTInfo;
+
+ RTLLIB_DEBUG(RTLLIB_DL_HT, "======>%s()\n", __func__);
+ if (pHTInfo->bCurBW40MHz) {
+ if (pHTInfo->CurSTAExtChnlOffset == HT_EXTCHNL_OFFSET_UPPER)
+ ieee->set_chan(ieee->dev,
+ ieee->current_network.channel + 2);
+ else if (pHTInfo->CurSTAExtChnlOffset ==
+ HT_EXTCHNL_OFFSET_LOWER)
+ ieee->set_chan(ieee->dev,
+ ieee->current_network.channel - 2);
+ else
+ ieee->set_chan(ieee->dev,
+ ieee->current_network.channel);
+
+ ieee->SetBWModeHandler(ieee->dev, HT_CHANNEL_WIDTH_20_40,
+ pHTInfo->CurSTAExtChnlOffset);
+ } else {
+ ieee->set_chan(ieee->dev, ieee->current_network.channel);
+ ieee->SetBWModeHandler(ieee->dev, HT_CHANNEL_WIDTH_20,
+ HT_EXTCHNL_OFFSET_NO_EXT);
+ }
+
+ pHTInfo->bSwBwInProgress = false;
+}
+
+void HTSetConnectBwMode(struct rtllib_device *ieee,
+ enum ht_channel_width Bandwidth,
+ enum ht_extchnl_offset Offset)
+{
+ struct rt_hi_throughput *pHTInfo = ieee->pHTInfo;
+
+ if (pHTInfo->bRegBW40MHz == false)
+ return;
+
+ if (ieee->GetHalfNmodeSupportByAPsHandler(ieee->dev))
+ Bandwidth = HT_CHANNEL_WIDTH_20;
+
+ if (pHTInfo->bSwBwInProgress) {
+ pr_info("%s: bSwBwInProgress!!\n", __func__);
+ return;
+ }
+ if (Bandwidth == HT_CHANNEL_WIDTH_20_40) {
+ if (ieee->current_network.channel < 2 &&
+ Offset == HT_EXTCHNL_OFFSET_LOWER)
+ Offset = HT_EXTCHNL_OFFSET_NO_EXT;
+ if (Offset == HT_EXTCHNL_OFFSET_UPPER ||
+ Offset == HT_EXTCHNL_OFFSET_LOWER) {
+ pHTInfo->bCurBW40MHz = true;
+ pHTInfo->CurSTAExtChnlOffset = Offset;
+ } else {
+ pHTInfo->bCurBW40MHz = false;
+ pHTInfo->CurSTAExtChnlOffset = HT_EXTCHNL_OFFSET_NO_EXT;
+ }
+ } else {
+ pHTInfo->bCurBW40MHz = false;
+ pHTInfo->CurSTAExtChnlOffset = HT_EXTCHNL_OFFSET_NO_EXT;
+ }
+
+ pr_info("%s():pHTInfo->bCurBW40MHz:%x\n", __func__,
+ pHTInfo->bCurBW40MHz);
+
+ pHTInfo->bSwBwInProgress = true;
+
+ HTSetConnectBwModeCallback(ieee);
+}
diff --git a/drivers/staging/rtl8192e/rtl819x_Qos.h b/drivers/staging/rtl8192e/rtl819x_Qos.h
new file mode 100644
index 000000000..55ef7ec33
--- /dev/null
+++ b/drivers/staging/rtl8192e/rtl819x_Qos.h
@@ -0,0 +1,389 @@
+/******************************************************************************
+ * Copyright(c) 2008 - 2010 Realtek Corporation. All rights reserved.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ * The full GNU General Public License is included in this distribution in the
+ * file called LICENSE.
+ *
+ * Contact Information:
+ * wlanfae <wlanfae@realtek.com>
+******************************************************************************/
+#ifndef __INC_QOS_TYPE_H
+#define __INC_QOS_TYPE_H
+
+#define BIT0 0x00000001
+#define BIT1 0x00000002
+#define BIT2 0x00000004
+#define BIT3 0x00000008
+#define BIT4 0x00000010
+#define BIT5 0x00000020
+#define BIT6 0x00000040
+#define BIT7 0x00000080
+#define BIT8 0x00000100
+#define BIT9 0x00000200
+#define BIT10 0x00000400
+#define BIT11 0x00000800
+#define BIT12 0x00001000
+#define BIT13 0x00002000
+#define BIT14 0x00004000
+#define BIT15 0x00008000
+#define BIT16 0x00010000
+#define BIT17 0x00020000
+#define BIT18 0x00040000
+#define BIT19 0x00080000
+#define BIT20 0x00100000
+#define BIT21 0x00200000
+#define BIT22 0x00400000
+#define BIT23 0x00800000
+#define BIT24 0x01000000
+#define BIT25 0x02000000
+#define BIT26 0x04000000
+#define BIT27 0x08000000
+#define BIT28 0x10000000
+#define BIT29 0x20000000
+#define BIT30 0x40000000
+#define BIT31 0x80000000
+
+union qos_tsinfo {
+ u8 charData[3];
+ struct {
+ u8 ucTrafficType:1;
+ u8 ucTSID:4;
+ u8 ucDirection:2;
+ u8 ucAccessPolicy:2;
+ u8 ucAggregation:1;
+ u8 ucPSB:1;
+ u8 ucUP:3;
+ u8 ucTSInfoAckPolicy:2;
+ u8 ucSchedule:1;
+ u8 ucReserved:7;
+ } field;
+};
+
+union tspec_body {
+ u8 charData[55];
+
+ struct {
+ union qos_tsinfo TSInfo;
+ u16 NominalMSDUsize;
+ u16 MaxMSDUsize;
+ u32 MinServiceItv;
+ u32 MaxServiceItv;
+ u32 InactivityItv;
+ u32 SuspenItv;
+ u32 ServiceStartTime;
+ u32 MinDataRate;
+ u32 MeanDataRate;
+ u32 PeakDataRate;
+ u32 MaxBurstSize;
+ u32 DelayBound;
+ u32 MinPhyRate;
+ u16 SurplusBandwidthAllowance;
+ u16 MediumTime;
+ } f;
+};
+
+struct wmm_tspec {
+ u8 ID;
+ u8 Length;
+ u8 OUI[3];
+ u8 OUI_Type;
+ u8 OUI_SubType;
+ u8 Version;
+ union tspec_body Body;
+};
+
+struct octet_string {
+ u8 *Octet;
+ u16 Length;
+};
+
+#define MAX_WMMELE_LENGTH 64
+
+#define QOS_MODE u32
+
+#define QOS_DISABLE 0
+#define QOS_WMM 1
+#define QOS_WMMSA 2
+#define QOS_EDCA 4
+#define QOS_HCCA 8
+#define QOS_WMM_UAPSD 16
+
+#define WMM_PARAM_ELE_BODY_LEN 18
+
+#define MAX_STA_TS_COUNT 16
+#define MAX_AP_TS_COUNT 32
+#define QOS_TSTREAM_KEY_SIZE 13
+
+#define WMM_ACTION_CATEGORY_CODE 17
+#define WMM_PARAM_ELE_BODY_LEN 18
+
+#define MAX_TSPEC_TSID 15
+#define SESSION_REJECT_TSID 0xfe
+#define DEFAULT_TSID 0xff
+
+#define ADDTS_TIME_SLOT 100
+
+#define ACM_TIMEOUT 1000
+#define SESSION_REJECT_TIMEOUT 60000
+
+enum ack_policy {
+ eAckPlc0_ACK = 0x00,
+ eAckPlc1_NoACK = 0x01,
+};
+
+
+#define SET_WMM_QOS_INFO_FIELD(_pStart, _val) \
+ WriteEF1Byte(_pStart, _val)
+
+#define GET_WMM_QOS_INFO_FIELD_PARAMETERSET_COUNT(_pStart) \
+ LE_BITS_TO_1BYTE(_pStart, 0, 4)
+#define SET_WMM_QOS_INFO_FIELD_PARAMETERSET_COUNT(_pStart, _val) \
+ SET_BITS_TO_LE_1BYTE(_pStart, 0, 4, _val)
+
+#define GET_WMM_QOS_INFO_FIELD_AP_UAPSD(_pStart) \
+ LE_BITS_TO_1BYTE(_pStart, 7, 1)
+#define SET_WMM_QOS_INFO_FIELD_AP_UAPSD(_pStart, _val) \
+ SET_BITS_TO_LE_1BYTE(_pStart, 7, 1, _val)
+
+#define GET_WMM_QOS_INFO_FIELD_STA_AC_VO_UAPSD(_pStart) \
+ LE_BITS_TO_1BYTE(_pStart, 0, 1)
+#define SET_WMM_QOS_INFO_FIELD_STA_AC_VO_UAPSD(_pStart, _val) \
+ SET_BITS_TO_LE_1BYTE(_pStart, 0, 1, _val)
+
+#define GET_WMM_QOS_INFO_FIELD_STA_AC_VI_UAPSD(_pStart) \
+ LE_BITS_TO_1BYTE(_pStart, 1, 1)
+#define SET_WMM_QOS_INFO_FIELD_STA_AC_VI_UAPSD(_pStart, _val) \
+ SET_BITS_TO_LE_1BYTE(_pStart, 1, 1, _val)
+
+#define GET_WMM_QOS_INFO_FIELD_STA_AC_BE_UAPSD(_pStart) \
+ LE_BITS_TO_1BYTE(_pStart, 2, 1)
+#define SET_WMM_QOS_INFO_FIELD_STA_AC_BE_UAPSD(_pStart, _val) \
+ SET_BITS_TO_LE_1BYTE(_pStart, 2, 1, _val)
+
+#define GET_WMM_QOS_INFO_FIELD_STA_AC_BK_UAPSD(_pStart) \
+ LE_BITS_TO_1BYTE(_pStart, 3, 1)
+#define SET_WMM_QOS_INFO_FIELD_STA_AC_BK_UAPSD(_pStart, _val) \
+ SET_BITS_TO_LE_1BYTE(_pStart, 3, 1, _val)
+
+#define GET_WMM_QOS_INFO_FIELD_STA_MAX_SP_LEN(_pStart) \
+ LE_BITS_TO_1BYTE(_pStart, 5, 2)
+#define SET_WMM_QOS_INFO_FIELD_STA_MAX_SP_LEN(_pStart, _val) \
+ SET_BITS_TO_LE_1BYTE(_pStart, 5, 2, _val)
+
+enum qos_ie_source {
+ QOSIE_SRC_ADDTSREQ,
+ QOSIE_SRC_ADDTSRSP,
+ QOSIE_SRC_REASOCREQ,
+ QOSIE_SRC_REASOCRSP,
+ QOSIE_SRC_DELTS,
+};
+
+
+#define AC_CODING u32
+
+#define AC0_BE 0
+#define AC1_BK 1
+#define AC2_VI 2
+#define AC3_VO 3
+#define AC_MAX 4
+
+
+#define AC_PARAM_SIZE 4
+
+#define WMM_PARAM_ELEMENT_SIZE (8+(4*AC_PARAM_SIZE))
+
+enum qos_ele_subtype {
+ QOSELE_TYPE_INFO = 0x00,
+ QOSELE_TYPE_PARAM = 0x01,
+};
+
+
+enum direction_value {
+ DIR_UP = 0,
+ DIR_DOWN = 1,
+ DIR_DIRECT = 2,
+ DIR_BI_DIR = 3,
+};
+
+enum acm_method {
+ eAcmWay0_SwAndHw = 0,
+ eAcmWay1_HW = 1,
+ eAcmWay2_SW = 2,
+};
+
+
+struct acm {
+ u64 UsedTime;
+ u64 MediumTime;
+ u8 HwAcmCtl;
+};
+
+
+
+#define AC_UAPSD u8
+
+#define GET_VO_UAPSD(_apsd) ((_apsd) & BIT0)
+#define SET_VO_UAPSD(_apsd) ((_apsd) |= BIT0)
+
+#define GET_VI_UAPSD(_apsd) ((_apsd) & BIT1)
+#define SET_VI_UAPSD(_apsd) ((_apsd) |= BIT1)
+
+#define GET_BK_UAPSD(_apsd) ((_apsd) & BIT2)
+#define SET_BK_UAPSD(_apsd) ((_apsd) |= BIT2)
+
+#define GET_BE_UAPSD(_apsd) ((_apsd) & BIT3)
+#define SET_BE_UAPSD(_apsd) ((_apsd) |= BIT3)
+
+union qos_tclas {
+
+ struct _TYPE_GENERAL {
+ u8 Priority;
+ u8 ClassifierType;
+ u8 Mask;
+ } TYPE_GENERAL;
+
+ struct _TYPE0_ETH {
+ u8 Priority;
+ u8 ClassifierType;
+ u8 Mask;
+ u8 SrcAddr[6];
+ u8 DstAddr[6];
+ u16 Type;
+ } TYPE0_ETH;
+
+ struct _TYPE1_IPV4 {
+ u8 Priority;
+ u8 ClassifierType;
+ u8 Mask;
+ u8 Version;
+ u8 SrcIP[4];
+ u8 DstIP[4];
+ u16 SrcPort;
+ u16 DstPort;
+ u8 DSCP;
+ u8 Protocol;
+ u8 Reserved;
+ } TYPE1_IPV4;
+
+ struct _TYPE1_IPV6 {
+ u8 Priority;
+ u8 ClassifierType;
+ u8 Mask;
+ u8 Version;
+ u8 SrcIP[16];
+ u8 DstIP[16];
+ u16 SrcPort;
+ u16 DstPort;
+ u8 FlowLabel[3];
+ } TYPE1_IPV6;
+
+ struct _TYPE2_8021Q {
+ u8 Priority;
+ u8 ClassifierType;
+ u8 Mask;
+ u16 TagType;
+ } TYPE2_8021Q;
+};
+
+struct qos_tstream {
+
+ bool bUsed;
+ u16 MsduLifetime;
+ bool bEstablishing;
+ u8 TimeSlotCount;
+ u8 DialogToken;
+ struct wmm_tspec TSpec;
+ struct wmm_tspec OutStandingTSpec;
+ u8 NominalPhyRate;
+};
+
+struct sta_qos {
+ u8 WMMIEBuf[MAX_WMMELE_LENGTH];
+ u8 *WMMIE;
+
+ QOS_MODE QosCapability;
+ QOS_MODE CurrentQosMode;
+
+ AC_UAPSD b4ac_Uapsd;
+ AC_UAPSD Curr4acUapsd;
+ u8 bInServicePeriod;
+ u8 MaxSPLength;
+ int NumBcnBeforeTrigger;
+
+ u8 *pWMMInfoEle;
+ u8 WMMParamEle[WMM_PARAM_ELEMENT_SIZE];
+
+ struct acm acm[4];
+ enum acm_method AcmMethod;
+
+ struct qos_tstream StaTsArray[MAX_STA_TS_COUNT];
+ u8 DialogToken;
+ struct wmm_tspec TSpec;
+
+ u8 QBssWirelessMode;
+
+ bool bNoAck;
+
+ bool bEnableRxImmBA;
+
+};
+
+#define QBSS_LOAD_SIZE 5
+
+struct bss_qos {
+ QOS_MODE bdQoSMode;
+ u8 bdWMMIEBuf[MAX_WMMELE_LENGTH];
+ struct octet_string bdWMMIE;
+
+ enum qos_ele_subtype EleSubType;
+
+ u8 *pWMMInfoEle;
+ u8 *pWMMParamEle;
+
+ u8 QBssLoad[QBSS_LOAD_SIZE];
+ bool bQBssLoadValid;
+};
+
+#define IsACValid(ac) ((ac >= 0 && ac <= 7) ? true : false)
+
+
+union aci_aifsn {
+ u8 charData;
+
+ struct {
+ u8 AIFSN:4;
+ u8 acm:1;
+ u8 ACI:2;
+ u8 Reserved:1;
+ } f;
+};
+
+union ecw {
+ u8 charData;
+ struct {
+ u8 ECWmin:4;
+ u8 ECWmax:4;
+ } f;
+};
+
+union ac_param {
+ u32 longData;
+ u8 charData[4];
+
+ struct {
+ union aci_aifsn AciAifsn;
+ union ecw Ecw;
+ u16 TXOPLimit;
+ } f;
+};
+
+#endif
diff --git a/drivers/staging/rtl8192e/rtl819x_TS.h b/drivers/staging/rtl8192e/rtl819x_TS.h
new file mode 100644
index 000000000..8601b1ad2
--- /dev/null
+++ b/drivers/staging/rtl8192e/rtl819x_TS.h
@@ -0,0 +1,73 @@
+/******************************************************************************
+ * Copyright(c) 2008 - 2010 Realtek Corporation. All rights reserved.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ * The full GNU General Public License is included in this distribution in the
+ * file called LICENSE.
+ *
+ * Contact Information:
+ * wlanfae <wlanfae@realtek.com>
+******************************************************************************/
+#ifndef _TSTYPE_H_
+#define _TSTYPE_H_
+#include "rtl819x_Qos.h"
+#define TS_SETUP_TIMEOUT 60
+#define TS_INACT_TIMEOUT 60
+#define TS_ADDBA_DELAY 60
+
+#define TOTAL_TS_NUM 16
+#define TCLAS_NUM 4
+
+enum tr_select {
+ TX_DIR = 0,
+ RX_DIR = 1,
+};
+
+struct ts_common_info {
+ struct list_head List;
+ struct timer_list SetupTimer;
+ struct timer_list InactTimer;
+ u8 Addr[6];
+ union tspec_body TSpec;
+ union qos_tclas TClass[TCLAS_NUM];
+ u8 TClasProc;
+ u8 TClasNum;
+};
+
+struct tx_ts_record {
+ struct ts_common_info TsCommonInfo;
+ u16 TxCurSeq;
+ struct ba_record TxPendingBARecord;
+ struct ba_record TxAdmittedBARecord;
+ u8 bAddBaReqInProgress;
+ u8 bAddBaReqDelayed;
+ u8 bUsingBa;
+ u8 bDisable_AddBa;
+ struct timer_list TsAddBaTimer;
+ u8 num;
+};
+
+struct rx_ts_record {
+ struct ts_common_info TsCommonInfo;
+ u16 RxIndicateSeq;
+ u16 RxTimeoutIndicateSeq;
+ struct list_head RxPendingPktList;
+ struct timer_list RxPktPendingTimer;
+ struct ba_record RxAdmittedBARecord;
+ u16 RxLastSeqNum;
+ u8 RxLastFragNum;
+ u8 num;
+};
+
+void _setup_timer(struct timer_list *, void *, unsigned long);
+
+
+#endif
diff --git a/drivers/staging/rtl8192e/rtl819x_TSProc.c b/drivers/staging/rtl8192e/rtl819x_TSProc.c
new file mode 100644
index 000000000..7d77d0562
--- /dev/null
+++ b/drivers/staging/rtl8192e/rtl819x_TSProc.c
@@ -0,0 +1,554 @@
+/******************************************************************************
+ * Copyright(c) 2008 - 2010 Realtek Corporation. All rights reserved.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ * The full GNU General Public License is included in this distribution in the
+ * file called LICENSE.
+ *
+ * Contact Information:
+ * wlanfae <wlanfae@realtek.com>
+******************************************************************************/
+#include "rtllib.h"
+#include <linux/etherdevice.h>
+#include "rtl819x_TS.h"
+
+static void TsSetupTimeOut(unsigned long data)
+{
+}
+
+static void TsInactTimeout(unsigned long data)
+{
+}
+
+static void RxPktPendingTimeout(unsigned long data)
+{
+ struct rx_ts_record *pRxTs = (struct rx_ts_record *)data;
+ struct rtllib_device *ieee = container_of(pRxTs, struct rtllib_device,
+ RxTsRecord[pRxTs->num]);
+
+ struct rx_reorder_entry *pReorderEntry = NULL;
+
+ unsigned long flags = 0;
+ u8 index = 0;
+ bool bPktInBuf = false;
+
+ spin_lock_irqsave(&(ieee->reorder_spinlock), flags);
+ if (pRxTs->RxTimeoutIndicateSeq != 0xffff) {
+ while (!list_empty(&pRxTs->RxPendingPktList)) {
+ pReorderEntry = (struct rx_reorder_entry *)
+ list_entry(pRxTs->RxPendingPktList.prev,
+ struct rx_reorder_entry, List);
+ if (index == 0)
+ pRxTs->RxIndicateSeq = pReorderEntry->SeqNum;
+
+ if (SN_LESS(pReorderEntry->SeqNum, pRxTs->RxIndicateSeq) ||
+ SN_EQUAL(pReorderEntry->SeqNum, pRxTs->RxIndicateSeq)) {
+ list_del_init(&pReorderEntry->List);
+
+ if (SN_EQUAL(pReorderEntry->SeqNum,
+ pRxTs->RxIndicateSeq))
+ pRxTs->RxIndicateSeq =
+ (pRxTs->RxIndicateSeq + 1) % 4096;
+
+ RTLLIB_DEBUG(RTLLIB_DL_REORDER,
+ "%s(): Indicate SeqNum: %d\n",
+ __func__, pReorderEntry->SeqNum);
+ ieee->stats_IndicateArray[index] =
+ pReorderEntry->prxb;
+ index++;
+
+ list_add_tail(&pReorderEntry->List,
+ &ieee->RxReorder_Unused_List);
+ } else {
+ bPktInBuf = true;
+ break;
+ }
+ }
+ }
+
+ if (index > 0) {
+ pRxTs->RxTimeoutIndicateSeq = 0xffff;
+
+ if (index > REORDER_WIN_SIZE) {
+ RTLLIB_DEBUG(RTLLIB_DL_ERR,
+ "RxReorderIndicatePacket(): Rx Reorder struct buffer full!!\n");
+ spin_unlock_irqrestore(&(ieee->reorder_spinlock),
+ flags);
+ return;
+ }
+ rtllib_indicate_packets(ieee, ieee->stats_IndicateArray, index);
+ bPktInBuf = false;
+ }
+
+ if (bPktInBuf && (pRxTs->RxTimeoutIndicateSeq == 0xffff)) {
+ pRxTs->RxTimeoutIndicateSeq = pRxTs->RxIndicateSeq;
+ mod_timer(&pRxTs->RxPktPendingTimer, jiffies +
+ msecs_to_jiffies(ieee->pHTInfo->RxReorderPendingTime));
+ }
+ spin_unlock_irqrestore(&(ieee->reorder_spinlock), flags);
+}
+
+static void TsAddBaProcess(unsigned long data)
+{
+ struct tx_ts_record *pTxTs = (struct tx_ts_record *)data;
+ u8 num = pTxTs->num;
+ struct rtllib_device *ieee = container_of(pTxTs, struct rtllib_device,
+ TxTsRecord[num]);
+
+ TsInitAddBA(ieee, pTxTs, BA_POLICY_IMMEDIATE, false);
+ RTLLIB_DEBUG(RTLLIB_DL_BA,
+ "TsAddBaProcess(): ADDBA Req is started!!\n");
+}
+
+static void ResetTsCommonInfo(struct ts_common_info *pTsCommonInfo)
+{
+ memset(pTsCommonInfo->Addr, 0, 6);
+ memset(&pTsCommonInfo->TSpec, 0, sizeof(union tspec_body));
+ memset(&pTsCommonInfo->TClass, 0, sizeof(union qos_tclas)*TCLAS_NUM);
+ pTsCommonInfo->TClasProc = 0;
+ pTsCommonInfo->TClasNum = 0;
+}
+
+static void ResetTxTsEntry(struct tx_ts_record *pTS)
+{
+ ResetTsCommonInfo(&pTS->TsCommonInfo);
+ pTS->TxCurSeq = 0;
+ pTS->bAddBaReqInProgress = false;
+ pTS->bAddBaReqDelayed = false;
+ pTS->bUsingBa = false;
+ pTS->bDisable_AddBa = false;
+ ResetBaEntry(&pTS->TxAdmittedBARecord);
+ ResetBaEntry(&pTS->TxPendingBARecord);
+}
+
+static void ResetRxTsEntry(struct rx_ts_record *pTS)
+{
+ ResetTsCommonInfo(&pTS->TsCommonInfo);
+ pTS->RxIndicateSeq = 0xffff;
+ pTS->RxTimeoutIndicateSeq = 0xffff;
+ ResetBaEntry(&pTS->RxAdmittedBARecord);
+}
+
+void TSInitialize(struct rtllib_device *ieee)
+{
+ struct tx_ts_record *pTxTS = ieee->TxTsRecord;
+ struct rx_ts_record *pRxTS = ieee->RxTsRecord;
+ struct rx_reorder_entry *pRxReorderEntry = ieee->RxReorderEntry;
+ u8 count = 0;
+
+ RTLLIB_DEBUG(RTLLIB_DL_TS, "==========>%s()\n", __func__);
+ INIT_LIST_HEAD(&ieee->Tx_TS_Admit_List);
+ INIT_LIST_HEAD(&ieee->Tx_TS_Pending_List);
+ INIT_LIST_HEAD(&ieee->Tx_TS_Unused_List);
+
+ for (count = 0; count < TOTAL_TS_NUM; count++) {
+ pTxTS->num = count;
+ _setup_timer(&pTxTS->TsCommonInfo.SetupTimer,
+ TsSetupTimeOut,
+ (unsigned long) pTxTS);
+
+ _setup_timer(&pTxTS->TsCommonInfo.InactTimer,
+ TsInactTimeout,
+ (unsigned long) pTxTS);
+
+ _setup_timer(&pTxTS->TsAddBaTimer,
+ TsAddBaProcess,
+ (unsigned long) pTxTS);
+
+ _setup_timer(&pTxTS->TxPendingBARecord.Timer,
+ BaSetupTimeOut,
+ (unsigned long) pTxTS);
+ _setup_timer(&pTxTS->TxAdmittedBARecord.Timer,
+ TxBaInactTimeout,
+ (unsigned long) pTxTS);
+
+ ResetTxTsEntry(pTxTS);
+ list_add_tail(&pTxTS->TsCommonInfo.List,
+ &ieee->Tx_TS_Unused_List);
+ pTxTS++;
+ }
+
+ INIT_LIST_HEAD(&ieee->Rx_TS_Admit_List);
+ INIT_LIST_HEAD(&ieee->Rx_TS_Pending_List);
+ INIT_LIST_HEAD(&ieee->Rx_TS_Unused_List);
+ for (count = 0; count < TOTAL_TS_NUM; count++) {
+ pRxTS->num = count;
+ INIT_LIST_HEAD(&pRxTS->RxPendingPktList);
+
+ _setup_timer(&pRxTS->TsCommonInfo.SetupTimer,
+ TsSetupTimeOut,
+ (unsigned long) pRxTS);
+
+ _setup_timer(&pRxTS->TsCommonInfo.InactTimer,
+ TsInactTimeout,
+ (unsigned long) pRxTS);
+
+ _setup_timer(&pRxTS->RxAdmittedBARecord.Timer,
+ RxBaInactTimeout,
+ (unsigned long) pRxTS);
+
+ _setup_timer(&pRxTS->RxPktPendingTimer,
+ RxPktPendingTimeout,
+ (unsigned long) pRxTS);
+
+ ResetRxTsEntry(pRxTS);
+ list_add_tail(&pRxTS->TsCommonInfo.List,
+ &ieee->Rx_TS_Unused_List);
+ pRxTS++;
+ }
+ INIT_LIST_HEAD(&ieee->RxReorder_Unused_List);
+ for (count = 0; count < REORDER_ENTRY_NUM; count++) {
+ list_add_tail(&pRxReorderEntry->List,
+ &ieee->RxReorder_Unused_List);
+ if (count == (REORDER_ENTRY_NUM-1))
+ break;
+ pRxReorderEntry = &ieee->RxReorderEntry[count+1];
+ }
+
+}
+
+static void AdmitTS(struct rtllib_device *ieee,
+ struct ts_common_info *pTsCommonInfo, u32 InactTime)
+{
+ del_timer_sync(&pTsCommonInfo->SetupTimer);
+ del_timer_sync(&pTsCommonInfo->InactTimer);
+
+ if (InactTime != 0)
+ mod_timer(&pTsCommonInfo->InactTimer, jiffies +
+ msecs_to_jiffies(InactTime));
+}
+
+static struct ts_common_info *SearchAdmitTRStream(struct rtllib_device *ieee,
+ u8 *Addr, u8 TID,
+ enum tr_select TxRxSelect)
+{
+ u8 dir;
+ bool search_dir[4] = {0};
+ struct list_head *psearch_list;
+ struct ts_common_info *pRet = NULL;
+
+ if (ieee->iw_mode == IW_MODE_MASTER) {
+ if (TxRxSelect == TX_DIR) {
+ search_dir[DIR_DOWN] = true;
+ search_dir[DIR_BI_DIR] = true;
+ } else {
+ search_dir[DIR_UP] = true;
+ search_dir[DIR_BI_DIR] = true;
+ }
+ } else if (ieee->iw_mode == IW_MODE_ADHOC) {
+ if (TxRxSelect == TX_DIR)
+ search_dir[DIR_UP] = true;
+ else
+ search_dir[DIR_DOWN] = true;
+ } else {
+ if (TxRxSelect == TX_DIR) {
+ search_dir[DIR_UP] = true;
+ search_dir[DIR_BI_DIR] = true;
+ search_dir[DIR_DIRECT] = true;
+ } else {
+ search_dir[DIR_DOWN] = true;
+ search_dir[DIR_BI_DIR] = true;
+ search_dir[DIR_DIRECT] = true;
+ }
+ }
+
+ if (TxRxSelect == TX_DIR)
+ psearch_list = &ieee->Tx_TS_Admit_List;
+ else
+ psearch_list = &ieee->Rx_TS_Admit_List;
+
+ for (dir = 0; dir <= DIR_BI_DIR; dir++) {
+ if (!search_dir[dir])
+ continue;
+ list_for_each_entry(pRet, psearch_list, List) {
+ if (memcmp(pRet->Addr, Addr, 6) == 0)
+ if (pRet->TSpec.f.TSInfo.field.ucTSID == TID)
+ if (pRet->TSpec.f.TSInfo.field.ucDirection == dir)
+ break;
+
+ }
+ if (&pRet->List != psearch_list)
+ break;
+ }
+
+ if (pRet && &pRet->List != psearch_list)
+ return pRet;
+ return NULL;
+}
+
+static void MakeTSEntry(struct ts_common_info *pTsCommonInfo, u8 *Addr,
+ union tspec_body *pTSPEC, union qos_tclas *pTCLAS,
+ u8 TCLAS_Num, u8 TCLAS_Proc)
+{
+ u8 count;
+
+ if (pTsCommonInfo == NULL)
+ return;
+
+ memcpy(pTsCommonInfo->Addr, Addr, 6);
+
+ if (pTSPEC != NULL)
+ memcpy((u8 *)(&(pTsCommonInfo->TSpec)), (u8 *)pTSPEC,
+ sizeof(union tspec_body));
+
+ for (count = 0; count < TCLAS_Num; count++)
+ memcpy((u8 *)(&(pTsCommonInfo->TClass[count])),
+ (u8 *)pTCLAS, sizeof(union qos_tclas));
+
+ pTsCommonInfo->TClasProc = TCLAS_Proc;
+ pTsCommonInfo->TClasNum = TCLAS_Num;
+}
+
+bool GetTs(struct rtllib_device *ieee, struct ts_common_info **ppTS,
+ u8 *Addr, u8 TID, enum tr_select TxRxSelect, bool bAddNewTs)
+{
+ u8 UP = 0;
+ union tspec_body TSpec;
+ union qos_tsinfo *pTSInfo = &TSpec.f.TSInfo;
+ struct list_head *pUnusedList;
+ struct list_head *pAddmitList;
+ enum direction_value Dir;
+
+ if (is_multicast_ether_addr(Addr)) {
+ RTLLIB_DEBUG(RTLLIB_DL_ERR,
+ "ERR! get TS for Broadcast or Multicast\n");
+ return false;
+ }
+ if (ieee->current_network.qos_data.supported == 0) {
+ UP = 0;
+ } else {
+ if (!IsACValid(TID)) {
+ RTLLIB_DEBUG(RTLLIB_DL_ERR,
+ "ERR! in %s(), TID(%d) is not valid\n",
+ __func__, TID);
+ return false;
+ }
+
+ switch (TID) {
+ case 0:
+ case 3:
+ UP = 0;
+ break;
+ case 1:
+ case 2:
+ UP = 2;
+ break;
+ case 4:
+ case 5:
+ UP = 5;
+ break;
+ case 6:
+ case 7:
+ UP = 7;
+ break;
+ }
+ }
+
+ *ppTS = SearchAdmitTRStream(ieee, Addr, UP, TxRxSelect);
+ if (*ppTS != NULL)
+ return true;
+
+ if (!bAddNewTs) {
+ RTLLIB_DEBUG(RTLLIB_DL_TS,
+ "add new TS failed(tid:%d)\n", UP);
+ return false;
+ }
+
+ pUnusedList = (TxRxSelect == TX_DIR) ?
+ (&ieee->Tx_TS_Unused_List) :
+ (&ieee->Rx_TS_Unused_List);
+
+ pAddmitList = (TxRxSelect == TX_DIR) ?
+ (&ieee->Tx_TS_Admit_List) :
+ (&ieee->Rx_TS_Admit_List);
+
+ Dir = (ieee->iw_mode == IW_MODE_MASTER) ?
+ ((TxRxSelect == TX_DIR) ? DIR_DOWN : DIR_UP) :
+ ((TxRxSelect == TX_DIR) ? DIR_UP : DIR_DOWN);
+
+ RTLLIB_DEBUG(RTLLIB_DL_TS, "to add Ts\n");
+ if (!list_empty(pUnusedList)) {
+ (*ppTS) = list_entry(pUnusedList->next,
+ struct ts_common_info, List);
+ list_del_init(&(*ppTS)->List);
+ if (TxRxSelect == TX_DIR) {
+ struct tx_ts_record *tmp =
+ container_of(*ppTS,
+ struct tx_ts_record,
+ TsCommonInfo);
+ ResetTxTsEntry(tmp);
+ } else {
+ struct rx_ts_record *tmp =
+ container_of(*ppTS,
+ struct rx_ts_record,
+ TsCommonInfo);
+ ResetRxTsEntry(tmp);
+ }
+
+ RTLLIB_DEBUG(RTLLIB_DL_TS,
+ "to init current TS, UP:%d, Dir:%d, addr: %pM ppTs=%p\n",
+ UP, Dir, Addr, *ppTS);
+ pTSInfo->field.ucTrafficType = 0;
+ pTSInfo->field.ucTSID = UP;
+ pTSInfo->field.ucDirection = Dir;
+ pTSInfo->field.ucAccessPolicy = 1;
+ pTSInfo->field.ucAggregation = 0;
+ pTSInfo->field.ucPSB = 0;
+ pTSInfo->field.ucUP = UP;
+ pTSInfo->field.ucTSInfoAckPolicy = 0;
+ pTSInfo->field.ucSchedule = 0;
+
+ MakeTSEntry(*ppTS, Addr, &TSpec, NULL, 0, 0);
+ AdmitTS(ieee, *ppTS, 0);
+ list_add_tail(&((*ppTS)->List), pAddmitList);
+
+ return true;
+ }
+
+ RTLLIB_DEBUG(RTLLIB_DL_ERR,
+ "ERR!!in function %s() There is not enough dir=%d(0=up down=1) TS record to be used!!",
+ __func__, Dir);
+ return false;
+}
+
+static void RemoveTsEntry(struct rtllib_device *ieee, struct ts_common_info *pTs,
+ enum tr_select TxRxSelect)
+{
+ del_timer_sync(&pTs->SetupTimer);
+ del_timer_sync(&pTs->InactTimer);
+ TsInitDelBA(ieee, pTs, TxRxSelect);
+
+ if (TxRxSelect == RX_DIR) {
+ struct rx_reorder_entry *pRxReorderEntry;
+ struct rx_ts_record *pRxTS = (struct rx_ts_record *)pTs;
+
+ if (timer_pending(&pRxTS->RxPktPendingTimer))
+ del_timer_sync(&pRxTS->RxPktPendingTimer);
+
+ while (!list_empty(&pRxTS->RxPendingPktList)) {
+ pRxReorderEntry = (struct rx_reorder_entry *)
+ list_entry(pRxTS->RxPendingPktList.prev,
+ struct rx_reorder_entry, List);
+ RTLLIB_DEBUG(RTLLIB_DL_REORDER,
+ "%s(): Delete SeqNum %d!\n", __func__,
+ pRxReorderEntry->SeqNum);
+ list_del_init(&pRxReorderEntry->List);
+ {
+ int i = 0;
+ struct rtllib_rxb *prxb = pRxReorderEntry->prxb;
+
+ if (unlikely(!prxb))
+ return;
+ for (i = 0; i < prxb->nr_subframes; i++)
+ dev_kfree_skb(prxb->subframes[i]);
+ kfree(prxb);
+ prxb = NULL;
+ }
+ list_add_tail(&pRxReorderEntry->List,
+ &ieee->RxReorder_Unused_List);
+ }
+ } else {
+ struct tx_ts_record *pTxTS = (struct tx_ts_record *)pTs;
+
+ del_timer_sync(&pTxTS->TsAddBaTimer);
+ }
+}
+
+void RemovePeerTS(struct rtllib_device *ieee, u8 *Addr)
+{
+ struct ts_common_info *pTS, *pTmpTS;
+
+ netdev_info(ieee->dev, "===========>RemovePeerTS, %pM\n", Addr);
+
+ list_for_each_entry_safe(pTS, pTmpTS, &ieee->Tx_TS_Pending_List, List) {
+ if (memcmp(pTS->Addr, Addr, 6) == 0) {
+ RemoveTsEntry(ieee, pTS, TX_DIR);
+ list_del_init(&pTS->List);
+ list_add_tail(&pTS->List, &ieee->Tx_TS_Unused_List);
+ }
+ }
+
+ list_for_each_entry_safe(pTS, pTmpTS, &ieee->Tx_TS_Admit_List, List) {
+ if (memcmp(pTS->Addr, Addr, 6) == 0) {
+ netdev_info(ieee->dev,
+ "====>remove Tx_TS_admin_list\n");
+ RemoveTsEntry(ieee, pTS, TX_DIR);
+ list_del_init(&pTS->List);
+ list_add_tail(&pTS->List, &ieee->Tx_TS_Unused_List);
+ }
+ }
+
+ list_for_each_entry_safe(pTS, pTmpTS, &ieee->Rx_TS_Pending_List, List) {
+ if (memcmp(pTS->Addr, Addr, 6) == 0) {
+ RemoveTsEntry(ieee, pTS, RX_DIR);
+ list_del_init(&pTS->List);
+ list_add_tail(&pTS->List, &ieee->Rx_TS_Unused_List);
+ }
+ }
+
+ list_for_each_entry_safe(pTS, pTmpTS, &ieee->Rx_TS_Admit_List, List) {
+ if (memcmp(pTS->Addr, Addr, 6) == 0) {
+ RemoveTsEntry(ieee, pTS, RX_DIR);
+ list_del_init(&pTS->List);
+ list_add_tail(&pTS->List, &ieee->Rx_TS_Unused_List);
+ }
+ }
+}
+EXPORT_SYMBOL(RemovePeerTS);
+
+void RemoveAllTS(struct rtllib_device *ieee)
+{
+ struct ts_common_info *pTS, *pTmpTS;
+
+ list_for_each_entry_safe(pTS, pTmpTS, &ieee->Tx_TS_Pending_List, List) {
+ RemoveTsEntry(ieee, pTS, TX_DIR);
+ list_del_init(&pTS->List);
+ list_add_tail(&pTS->List, &ieee->Tx_TS_Unused_List);
+ }
+
+ list_for_each_entry_safe(pTS, pTmpTS, &ieee->Tx_TS_Admit_List, List) {
+ RemoveTsEntry(ieee, pTS, TX_DIR);
+ list_del_init(&pTS->List);
+ list_add_tail(&pTS->List, &ieee->Tx_TS_Unused_List);
+ }
+
+ list_for_each_entry_safe(pTS, pTmpTS, &ieee->Rx_TS_Pending_List, List) {
+ RemoveTsEntry(ieee, pTS, RX_DIR);
+ list_del_init(&pTS->List);
+ list_add_tail(&pTS->List, &ieee->Rx_TS_Unused_List);
+ }
+
+ list_for_each_entry_safe(pTS, pTmpTS, &ieee->Rx_TS_Admit_List, List) {
+ RemoveTsEntry(ieee, pTS, RX_DIR);
+ list_del_init(&pTS->List);
+ list_add_tail(&pTS->List, &ieee->Rx_TS_Unused_List);
+ }
+}
+
+void TsStartAddBaProcess(struct rtllib_device *ieee, struct tx_ts_record *pTxTS)
+{
+ if (pTxTS->bAddBaReqInProgress == false) {
+ pTxTS->bAddBaReqInProgress = true;
+
+ if (pTxTS->bAddBaReqDelayed) {
+ RTLLIB_DEBUG(RTLLIB_DL_BA,
+ "TsStartAddBaProcess(): Delayed Start ADDBA after 60 sec!!\n");
+ mod_timer(&pTxTS->TsAddBaTimer, jiffies +
+ msecs_to_jiffies(TS_ADDBA_DELAY));
+ } else {
+ RTLLIB_DEBUG(RTLLIB_DL_BA,
+ "TsStartAddBaProcess(): Immediately Start ADDBA now!!\n");
+ mod_timer(&pTxTS->TsAddBaTimer, jiffies+10);
+ }
+ } else
+ RTLLIB_DEBUG(RTLLIB_DL_BA, "%s()==>BA timer is already added\n",
+ __func__);
+}
diff --git a/drivers/staging/rtl8192e/rtllib.h b/drivers/staging/rtl8192e/rtllib.h
new file mode 100644
index 000000000..3c8b708df
--- /dev/null
+++ b/drivers/staging/rtl8192e/rtllib.h
@@ -0,0 +1,2975 @@
+/*
+ * Merged with mainline rtllib.h in Aug 2004. Original ieee802_11
+ * remains copyright by the original authors
+ *
+ * Portions of the merged code are based on Host AP (software wireless
+ * LAN access point) driver for Intersil Prism2/2.5/3.
+ *
+ * Copyright (c) 2001-2002, SSH Communications Security Corp and Jouni Malinen
+ * <jkmaline@cc.hut.fi>
+ * Copyright (c) 2002-2003, Jouni Malinen <jkmaline@cc.hut.fi>
+ *
+ * Adaption to a generic IEEE 802.11 stack by James Ketrenos
+ * <jketreno@linux.intel.com>
+ * Copyright (c) 2004, Intel Corporation
+ *
+ * Modified for Realtek's wi-fi cards by Andrea Merello
+ * <andrea.merello@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation. See README and COPYING for
+ * more details.
+ */
+#ifndef RTLLIB_H
+#define RTLLIB_H
+#include <linux/if_ether.h> /* ETH_ALEN */
+#include <linux/kernel.h> /* ARRAY_SIZE */
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/jiffies.h>
+#include <linux/timer.h>
+#include <linux/sched.h>
+#include <linux/semaphore.h>
+
+#include <linux/delay.h>
+#include <linux/wireless.h>
+
+#include "rtllib_debug.h"
+#include "rtl819x_HT.h"
+#include "rtl819x_BA.h"
+#include "rtl819x_TS.h"
+
+#include <linux/netdevice.h>
+#include <linux/if_arp.h> /* ARPHRD_ETHER */
+#include <net/lib80211.h>
+
+#define MAX_PRECMD_CNT 16
+#define MAX_RFDEPENDCMD_CNT 16
+#define MAX_POSTCMD_CNT 16
+
+#ifndef WIRELESS_SPY
+#define WIRELESS_SPY
+#endif
+#include <net/iw_handler.h>
+
+#ifndef IW_MODE_MONITOR
+#define IW_MODE_MONITOR 6
+#endif
+
+#ifndef IWEVCUSTOM
+#define IWEVCUSTOM 0x8c02
+#endif
+
+#ifndef IW_CUSTOM_MAX
+/* Max number of char in custom event - use multiple of them if needed */
+#define IW_CUSTOM_MAX 256 /* In bytes */
+#endif
+
+#define skb_tail_pointer_rsl(skb) skb_tail_pointer(skb)
+
+#define queue_delayed_work_rsl(x, y, z) queue_delayed_work(x, y, z)
+#define INIT_DELAYED_WORK_RSL(x, y, z) INIT_DELAYED_WORK(x, y)
+
+#define queue_work_rsl(x, y) queue_work(x, y)
+#define INIT_WORK_RSL(x, y, z) INIT_WORK(x, y)
+
+#define container_of_work_rsl(x, y, z) container_of(x, y, z)
+#define container_of_dwork_rsl(x, y, z) \
+ container_of(container_of(x, struct delayed_work, work), y, z)
+
+#define iwe_stream_add_event_rsl(info, start, stop, iwe, len) \
+ iwe_stream_add_event(info, start, stop, iwe, len)
+
+#define iwe_stream_add_point_rsl(info, start, stop, iwe, p) \
+ iwe_stream_add_point(info, start, stop, iwe, p)
+
+#define usb_alloc_urb_rsl(x, y) usb_alloc_urb(x, y)
+#define usb_submit_urb_rsl(x, y) usb_submit_urb(x, y)
+
+static inline void *netdev_priv_rsl(struct net_device *dev)
+{
+ return netdev_priv(dev);
+}
+
+#define KEY_TYPE_NA 0x0
+#define KEY_TYPE_WEP40 0x1
+#define KEY_TYPE_TKIP 0x2
+#define KEY_TYPE_CCMP 0x4
+#define KEY_TYPE_WEP104 0x5
+/* added for rtl819x tx procedure */
+#define MAX_QUEUE_SIZE 0x10
+
+#define BK_QUEUE 0
+#define BE_QUEUE 1
+#define VI_QUEUE 2
+#define VO_QUEUE 3
+#define HCCA_QUEUE 4
+#define TXCMD_QUEUE 5
+#define MGNT_QUEUE 6
+#define HIGH_QUEUE 7
+#define BEACON_QUEUE 8
+
+#define LOW_QUEUE BE_QUEUE
+#define NORMAL_QUEUE MGNT_QUEUE
+
+#ifndef IW_MODE_MESH
+#define IW_MODE_MESH 7
+#endif
+#define AMSDU_SUBHEADER_LEN 14
+#define SWRF_TIMEOUT 50
+
+#define IE_CISCO_FLAG_POSITION 0x08
+#define SUPPORT_CKIP_MIC 0x08
+#define SUPPORT_CKIP_PK 0x10
+#define RT_RF_OFF_LEVL_ASPM BIT0
+#define RT_RF_OFF_LEVL_CLK_REQ BIT1
+#define RT_RF_OFF_LEVL_PCI_D3 BIT2
+#define RT_RF_OFF_LEVL_HALT_NIC BIT3
+#define RT_RF_OFF_LEVL_FREE_FW BIT4
+#define RT_RF_OFF_LEVL_FW_32K BIT5
+#define RT_RF_PS_LEVEL_ALWAYS_ASPM BIT6
+#define RT_RF_LPS_DISALBE_2R BIT30
+#define RT_RF_LPS_LEVEL_ASPM BIT31
+#define RT_IN_PS_LEVEL(pPSC, _PS_FLAG) \
+ ((pPSC->CurPsLevel & _PS_FLAG) ? true : false)
+#define RT_CLEAR_PS_LEVEL(pPSC, _PS_FLAG) \
+ (pPSC->CurPsLevel &= (~(_PS_FLAG)))
+#define RT_SET_PS_LEVEL(pPSC, _PS_FLAG) (pPSC->CurPsLevel |= _PS_FLAG)
+
+/* defined for skb cb field */
+/* At most 28 byte */
+struct cb_desc {
+ /* Tx Desc Related flags (8-9) */
+ u8 bLastIniPkt:1;
+ u8 bCmdOrInit:1;
+ u8 bFirstSeg:1;
+ u8 bLastSeg:1;
+ u8 bEncrypt:1;
+ u8 bTxDisableRateFallBack:1;
+ u8 bTxUseDriverAssingedRate:1;
+ u8 bHwSec:1;
+
+ u8 nStuckCount;
+
+ /* Tx Firmware Related flags (10-11)*/
+ u8 bCTSEnable:1;
+ u8 bRTSEnable:1;
+ u8 bUseShortGI:1;
+ u8 bUseShortPreamble:1;
+ u8 bTxEnableFwCalcDur:1;
+ u8 bAMPDUEnable:1;
+ u8 bRTSSTBC:1;
+ u8 RTSSC:1;
+
+ u8 bRTSBW:1;
+ u8 bPacketBW:1;
+ u8 bRTSUseShortPreamble:1;
+ u8 bRTSUseShortGI:1;
+ u8 bMulticast:1;
+ u8 bBroadcast:1;
+ u8 drv_agg_enable:1;
+ u8 reserved2:1;
+
+ /* Tx Desc related element(12-19) */
+ u8 rata_index;
+ u8 queue_index;
+ u16 txbuf_size;
+ u8 RATRIndex;
+ u8 bAMSDU:1;
+ u8 bFromAggrQ:1;
+ u8 reserved6:6;
+ u8 macId;
+ u8 priority;
+
+ /* Tx firmware related element(20-27) */
+ u8 data_rate;
+ u8 rts_rate;
+ u8 ampdu_factor;
+ u8 ampdu_density;
+ u8 DrvAggrNum;
+ u8 bdhcp;
+ u16 pkt_size;
+ u8 bIsSpecialDataFrame;
+
+ u8 bBTTxPacket;
+ u8 bIsBTProbRsp;
+};
+
+enum sw_chnl_cmd_id {
+ CmdID_End,
+ CmdID_SetTxPowerLevel,
+ CmdID_BBRegWrite10,
+ CmdID_WritePortUlong,
+ CmdID_WritePortUshort,
+ CmdID_WritePortUchar,
+ CmdID_RF_WriteReg,
+};
+
+struct sw_chnl_cmd {
+ enum sw_chnl_cmd_id CmdID;
+ u32 Para1;
+ u32 Para2;
+ u32 msDelay;
+} __packed;
+
+/*--------------------------Define -------------------------------------------*/
+#define MGN_1M 0x02
+#define MGN_2M 0x04
+#define MGN_5_5M 0x0b
+#define MGN_11M 0x16
+
+#define MGN_6M 0x0c
+#define MGN_9M 0x12
+#define MGN_12M 0x18
+#define MGN_18M 0x24
+#define MGN_24M 0x30
+#define MGN_36M 0x48
+#define MGN_48M 0x60
+#define MGN_54M 0x6c
+
+#define MGN_MCS0 0x80
+#define MGN_MCS1 0x81
+#define MGN_MCS2 0x82
+#define MGN_MCS3 0x83
+#define MGN_MCS4 0x84
+#define MGN_MCS5 0x85
+#define MGN_MCS6 0x86
+#define MGN_MCS7 0x87
+#define MGN_MCS8 0x88
+#define MGN_MCS9 0x89
+#define MGN_MCS10 0x8a
+#define MGN_MCS11 0x8b
+#define MGN_MCS12 0x8c
+#define MGN_MCS13 0x8d
+#define MGN_MCS14 0x8e
+#define MGN_MCS15 0x8f
+#define MGN_MCS0_SG 0x90
+#define MGN_MCS1_SG 0x91
+#define MGN_MCS2_SG 0x92
+#define MGN_MCS3_SG 0x93
+#define MGN_MCS4_SG 0x94
+#define MGN_MCS5_SG 0x95
+#define MGN_MCS6_SG 0x96
+#define MGN_MCS7_SG 0x97
+#define MGN_MCS8_SG 0x98
+#define MGN_MCS9_SG 0x99
+#define MGN_MCS10_SG 0x9a
+#define MGN_MCS11_SG 0x9b
+#define MGN_MCS12_SG 0x9c
+#define MGN_MCS13_SG 0x9d
+#define MGN_MCS14_SG 0x9e
+#define MGN_MCS15_SG 0x9f
+
+
+enum _ReasonCode {
+ unspec_reason = 0x1,
+ auth_not_valid = 0x2,
+ deauth_lv_ss = 0x3,
+ inactivity = 0x4,
+ ap_overload = 0x5,
+ class2_err = 0x6,
+ class3_err = 0x7,
+ disas_lv_ss = 0x8,
+ asoc_not_auth = 0x9,
+
+ mic_failure = 0xe,
+
+ invalid_IE = 0x0d,
+ four_way_tmout = 0x0f,
+ two_way_tmout = 0x10,
+ IE_dismatch = 0x11,
+ invalid_Gcipher = 0x12,
+ invalid_Pcipher = 0x13,
+ invalid_AKMP = 0x14,
+ unsup_RSNIEver = 0x15,
+ invalid_RSNIE = 0x16,
+ auth_802_1x_fail = 0x17,
+ ciper_reject = 0x18,
+
+ QoS_unspec = 0x20,
+ QAP_bandwidth = 0x21,
+ poor_condition = 0x22,
+ no_facility = 0x23,
+ req_declined = 0x25,
+ invalid_param = 0x26,
+ req_not_honored = 0x27,
+ TS_not_created = 0x2F,
+ DL_not_allowed = 0x30,
+ dest_not_exist = 0x31,
+ dest_not_QSTA = 0x32,
+};
+
+enum hal_def_variable {
+ HAL_DEF_TPC_ENABLE,
+ HAL_DEF_INIT_GAIN,
+ HAL_DEF_PROT_IMP_MODE,
+ HAL_DEF_HIGH_POWER_MECHANISM,
+ HAL_DEF_RATE_ADAPTIVE_MECHANISM,
+ HAL_DEF_ANTENNA_DIVERSITY_MECHANISM,
+ HAL_DEF_LED,
+ HAL_DEF_CW_MAX_MIN,
+
+ HAL_DEF_WOWLAN,
+ HAL_DEF_ENDPOINTS,
+ HAL_DEF_MIN_TX_POWER_DBM,
+ HAL_DEF_MAX_TX_POWER_DBM,
+ HW_DEF_EFUSE_REPG_SECTION1_FLAG,
+ HW_DEF_EFUSE_REPG_DATA,
+ HW_DEF_GPIO,
+ HAL_DEF_PCI_SUPPORT_ASPM,
+ HAL_DEF_THERMAL_VALUE,
+ HAL_DEF_USB_IN_TOKEN_REV,
+};
+
+enum hw_variables {
+ HW_VAR_ETHER_ADDR,
+ HW_VAR_MULTICAST_REG,
+ HW_VAR_BASIC_RATE,
+ HW_VAR_BSSID,
+ HW_VAR_MEDIA_STATUS,
+ HW_VAR_SECURITY_CONF,
+ HW_VAR_BEACON_INTERVAL,
+ HW_VAR_ATIM_WINDOW,
+ HW_VAR_LISTEN_INTERVAL,
+ HW_VAR_CS_COUNTER,
+ HW_VAR_DEFAULTKEY0,
+ HW_VAR_DEFAULTKEY1,
+ HW_VAR_DEFAULTKEY2,
+ HW_VAR_DEFAULTKEY3,
+ HW_VAR_SIFS,
+ HW_VAR_DIFS,
+ HW_VAR_EIFS,
+ HW_VAR_SLOT_TIME,
+ HW_VAR_ACK_PREAMBLE,
+ HW_VAR_CW_CONFIG,
+ HW_VAR_CW_VALUES,
+ HW_VAR_RATE_FALLBACK_CONTROL,
+ HW_VAR_CONTENTION_WINDOW,
+ HW_VAR_RETRY_COUNT,
+ HW_VAR_TR_SWITCH,
+ HW_VAR_COMMAND,
+ HW_VAR_WPA_CONFIG,
+ HW_VAR_AMPDU_MIN_SPACE,
+ HW_VAR_SHORTGI_DENSITY,
+ HW_VAR_AMPDU_FACTOR,
+ HW_VAR_MCS_RATE_AVAILABLE,
+ HW_VAR_AC_PARAM,
+ HW_VAR_ACM_CTRL,
+ HW_VAR_DIS_Req_Qsize,
+ HW_VAR_CCX_CHNL_LOAD,
+ HW_VAR_CCX_NOISE_HISTOGRAM,
+ HW_VAR_CCX_CLM_NHM,
+ HW_VAR_TxOPLimit,
+ HW_VAR_TURBO_MODE,
+ HW_VAR_RF_STATE,
+ HW_VAR_RF_OFF_BY_HW,
+ HW_VAR_BUS_SPEED,
+ HW_VAR_SET_DEV_POWER,
+
+ HW_VAR_RCR,
+ HW_VAR_RATR_0,
+ HW_VAR_RRSR,
+ HW_VAR_CPU_RST,
+ HW_VAR_CECHK_BSSID,
+ HW_VAR_LBK_MODE,
+ HW_VAR_AES_11N_FIX,
+ HW_VAR_USB_RX_AGGR,
+ HW_VAR_USER_CONTROL_TURBO_MODE,
+ HW_VAR_RETRY_LIMIT,
+ HW_VAR_INIT_TX_RATE,
+ HW_VAR_TX_RATE_REG,
+ HW_VAR_EFUSE_USAGE,
+ HW_VAR_EFUSE_BYTES,
+ HW_VAR_AUTOLOAD_STATUS,
+ HW_VAR_RF_2R_DISABLE,
+ HW_VAR_SET_RPWM,
+ HW_VAR_H2C_FW_PWRMODE,
+ HW_VAR_H2C_FW_JOINBSSRPT,
+ HW_VAR_1X1_RECV_COMBINE,
+ HW_VAR_STOP_SEND_BEACON,
+ HW_VAR_TSF_TIMER,
+ HW_VAR_IO_CMD,
+
+ HW_VAR_RF_RECOVERY,
+ HW_VAR_H2C_FW_UPDATE_GTK,
+ HW_VAR_WF_MASK,
+ HW_VAR_WF_CRC,
+ HW_VAR_WF_IS_MAC_ADDR,
+ HW_VAR_H2C_FW_OFFLOAD,
+ HW_VAR_RESET_WFCRC,
+
+ HW_VAR_HANDLE_FW_C2H,
+ HW_VAR_DL_FW_RSVD_PAGE,
+ HW_VAR_AID,
+ HW_VAR_HW_SEQ_ENABLE,
+ HW_VAR_CORRECT_TSF,
+ HW_VAR_BCN_VALID,
+ HW_VAR_FWLPS_RF_ON,
+ HW_VAR_DUAL_TSF_RST,
+ HW_VAR_SWITCH_EPHY_WoWLAN,
+ HW_VAR_INT_MIGRATION,
+ HW_VAR_INT_AC,
+ HW_VAR_RF_TIMING,
+};
+
+enum rt_op_mode {
+ RT_OP_MODE_AP,
+ RT_OP_MODE_INFRASTRUCTURE,
+ RT_OP_MODE_IBSS,
+ RT_OP_MODE_NO_LINK,
+};
+
+
+#define aSifsTime \
+ (((priv->rtllib->current_network.mode == IEEE_A) \
+ || (priv->rtllib->current_network.mode == IEEE_N_24G) \
+ || (priv->rtllib->current_network.mode == IEEE_N_5G)) ? 16 : 10)
+
+#define MGMT_QUEUE_NUM 5
+
+#define IEEE_CMD_SET_WPA_PARAM 1
+#define IEEE_CMD_SET_WPA_IE 2
+#define IEEE_CMD_SET_ENCRYPTION 3
+#define IEEE_CMD_MLME 4
+
+#define IEEE_PARAM_WPA_ENABLED 1
+#define IEEE_PARAM_TKIP_COUNTERMEASURES 2
+#define IEEE_PARAM_DROP_UNENCRYPTED 3
+#define IEEE_PARAM_PRIVACY_INVOKED 4
+#define IEEE_PARAM_AUTH_ALGS 5
+#define IEEE_PARAM_IEEE_802_1X 6
+#define IEEE_PARAM_WPAX_SELECT 7
+#define IEEE_PROTO_WPA 1
+#define IEEE_PROTO_RSN 2
+#define IEEE_WPAX_USEGROUP 0
+#define IEEE_WPAX_WEP40 1
+#define IEEE_WPAX_TKIP 2
+#define IEEE_WPAX_WRAP 3
+#define IEEE_WPAX_CCMP 4
+#define IEEE_WPAX_WEP104 5
+
+#define IEEE_KEY_MGMT_IEEE8021X 1
+#define IEEE_KEY_MGMT_PSK 2
+
+#define IEEE_MLME_STA_DEAUTH 1
+#define IEEE_MLME_STA_DISASSOC 2
+
+
+#define IEEE_CRYPT_ERR_UNKNOWN_ALG 2
+#define IEEE_CRYPT_ERR_UNKNOWN_ADDR 3
+#define IEEE_CRYPT_ERR_CRYPT_INIT_FAILED 4
+#define IEEE_CRYPT_ERR_KEY_SET_FAILED 5
+#define IEEE_CRYPT_ERR_TX_KEY_SET_FAILED 6
+#define IEEE_CRYPT_ERR_CARD_CONF_FAILED 7
+#define IEEE_CRYPT_ALG_NAME_LEN 16
+
+#define MAX_IE_LEN 0xff
+
+struct ieee_param {
+ u32 cmd;
+ u8 sta_addr[ETH_ALEN];
+ union {
+ struct {
+ u8 name;
+ u32 value;
+ } wpa_param;
+ struct {
+ u32 len;
+ u8 reserved[32];
+ u8 data[0];
+ } wpa_ie;
+ struct {
+ int command;
+ int reason_code;
+ } mlme;
+ struct {
+ u8 alg[IEEE_CRYPT_ALG_NAME_LEN];
+ u8 set_tx;
+ u32 err;
+ u8 idx;
+ u8 seq[8]; /* sequence counter (set: RX, get: TX) */
+ u16 key_len;
+ u8 key[0];
+ } crypt;
+ } u;
+};
+
+
+#if WIRELESS_EXT < 17
+#define IW_QUAL_QUAL_INVALID 0x10
+#define IW_QUAL_LEVEL_INVALID 0x20
+#define IW_QUAL_NOISE_INVALID 0x40
+#define IW_QUAL_QUAL_UPDATED 0x1
+#define IW_QUAL_LEVEL_UPDATED 0x2
+#define IW_QUAL_NOISE_UPDATED 0x4
+#endif
+
+#define msleep_interruptible_rsl msleep_interruptible
+
+#define RTLLIB_DATA_LEN 2304
+/* Maximum size for the MA-UNITDATA primitive, 802.11 standard section
+ * 6.2.1.1.2.
+ *
+ * The figure in section 7.1.2 suggests a body size of up to 2312
+ * bytes is allowed, which is a bit confusing, I suspect this
+ * represents the 2304 bytes of real data, plus a possible 8 bytes of
+ * WEP IV and ICV. (this interpretation suggested by Ramiro Barreiro)
+ */
+#define RTLLIB_1ADDR_LEN 10
+#define RTLLIB_2ADDR_LEN 16
+#define RTLLIB_3ADDR_LEN 24
+#define RTLLIB_4ADDR_LEN 30
+#define RTLLIB_FCS_LEN 4
+#define RTLLIB_HLEN (RTLLIB_4ADDR_LEN)
+#define RTLLIB_FRAME_LEN (RTLLIB_DATA_LEN + RTLLIB_HLEN)
+#define RTLLIB_MGMT_HDR_LEN 24
+#define RTLLIB_DATA_HDR3_LEN 24
+#define RTLLIB_DATA_HDR4_LEN 30
+
+#define RTLLIB_SKBBUFFER_SIZE 2500
+
+#define MIN_FRAG_THRESHOLD 256U
+#define MAX_FRAG_THRESHOLD 2346U
+#define MAX_HT_DATA_FRAG_THRESHOLD 0x2000
+
+#define HT_AMSDU_SIZE_4K 3839
+#define HT_AMSDU_SIZE_8K 7935
+
+/* Frame control field constants */
+#define RTLLIB_FCTL_VERS 0x0003
+#define RTLLIB_FCTL_FTYPE 0x000c
+#define RTLLIB_FCTL_STYPE 0x00f0
+#define RTLLIB_FCTL_FRAMETYPE 0x00fc
+#define RTLLIB_FCTL_TODS 0x0100
+#define RTLLIB_FCTL_FROMDS 0x0200
+#define RTLLIB_FCTL_DSTODS 0x0300
+#define RTLLIB_FCTL_MOREFRAGS 0x0400
+#define RTLLIB_FCTL_RETRY 0x0800
+#define RTLLIB_FCTL_PM 0x1000
+#define RTLLIB_FCTL_MOREDATA 0x2000
+#define RTLLIB_FCTL_WEP 0x4000
+#define RTLLIB_FCTL_ORDER 0x8000
+
+#define RTLLIB_FTYPE_MGMT 0x0000
+#define RTLLIB_FTYPE_CTL 0x0004
+#define RTLLIB_FTYPE_DATA 0x0008
+
+/* management */
+#define RTLLIB_STYPE_ASSOC_REQ 0x0000
+#define RTLLIB_STYPE_ASSOC_RESP 0x0010
+#define RTLLIB_STYPE_REASSOC_REQ 0x0020
+#define RTLLIB_STYPE_REASSOC_RESP 0x0030
+#define RTLLIB_STYPE_PROBE_REQ 0x0040
+#define RTLLIB_STYPE_PROBE_RESP 0x0050
+#define RTLLIB_STYPE_BEACON 0x0080
+#define RTLLIB_STYPE_ATIM 0x0090
+#define RTLLIB_STYPE_DISASSOC 0x00A0
+#define RTLLIB_STYPE_AUTH 0x00B0
+#define RTLLIB_STYPE_DEAUTH 0x00C0
+#define RTLLIB_STYPE_MANAGE_ACT 0x00D0
+
+/* control */
+#define RTLLIB_STYPE_PSPOLL 0x00A0
+#define RTLLIB_STYPE_RTS 0x00B0
+#define RTLLIB_STYPE_CTS 0x00C0
+#define RTLLIB_STYPE_ACK 0x00D0
+#define RTLLIB_STYPE_CFEND 0x00E0
+#define RTLLIB_STYPE_CFENDACK 0x00F0
+#define RTLLIB_STYPE_BLOCKACK 0x0094
+
+/* data */
+#define RTLLIB_STYPE_DATA 0x0000
+#define RTLLIB_STYPE_DATA_CFACK 0x0010
+#define RTLLIB_STYPE_DATA_CFPOLL 0x0020
+#define RTLLIB_STYPE_DATA_CFACKPOLL 0x0030
+#define RTLLIB_STYPE_NULLFUNC 0x0040
+#define RTLLIB_STYPE_CFACK 0x0050
+#define RTLLIB_STYPE_CFPOLL 0x0060
+#define RTLLIB_STYPE_CFACKPOLL 0x0070
+#define RTLLIB_STYPE_QOS_DATA 0x0080
+#define RTLLIB_STYPE_QOS_NULL 0x00C0
+
+#define RTLLIB_SCTL_FRAG 0x000F
+#define RTLLIB_SCTL_SEQ 0xFFF0
+
+/* QOS control */
+#define RTLLIB_QCTL_TID 0x000F
+
+#define FC_QOS_BIT BIT7
+#define IsDataFrame(pdu) (((pdu[0] & 0x0C) == 0x08) ? true : false)
+#define IsLegacyDataFrame(pdu) (IsDataFrame(pdu) && (!(pdu[0]&FC_QOS_BIT)))
+#define IsQoSDataFrame(pframe) \
+ ((*(u16 *)pframe&(RTLLIB_STYPE_QOS_DATA|RTLLIB_FTYPE_DATA)) == \
+ (RTLLIB_STYPE_QOS_DATA|RTLLIB_FTYPE_DATA))
+#define Frame_Order(pframe) (*(u16 *)pframe&RTLLIB_FCTL_ORDER)
+#define SN_LESS(a, b) (((a-b)&0x800) != 0)
+#define SN_EQUAL(a, b) (a == b)
+#define MAX_DEV_ADDR_SIZE 8
+
+enum act_category {
+ ACT_CAT_QOS = 1,
+ ACT_CAT_DLS = 2,
+ ACT_CAT_BA = 3,
+ ACT_CAT_HT = 7,
+ ACT_CAT_WMM = 17,
+};
+
+enum ts_action {
+ ACT_ADDTSREQ = 0,
+ ACT_ADDTSRSP = 1,
+ ACT_DELTS = 2,
+ ACT_SCHEDULE = 3,
+};
+
+enum ba_action {
+ ACT_ADDBAREQ = 0,
+ ACT_ADDBARSP = 1,
+ ACT_DELBA = 2,
+};
+
+enum init_gain_op_type {
+ IG_Backup = 0,
+ IG_Restore,
+ IG_Max
+};
+
+enum led_ctl_mode {
+ LED_CTL_POWER_ON = 1,
+ LED_CTL_LINK = 2,
+ LED_CTL_NO_LINK = 3,
+ LED_CTL_TX = 4,
+ LED_CTL_RX = 5,
+ LED_CTL_SITE_SURVEY = 6,
+ LED_CTL_POWER_OFF = 7,
+ LED_CTL_START_TO_LINK = 8,
+ LED_CTL_START_WPS = 9,
+ LED_CTL_STOP_WPS = 10,
+ LED_CTL_START_WPS_BOTTON = 11,
+ LED_CTL_STOP_WPS_FAIL = 12,
+ LED_CTL_STOP_WPS_FAIL_OVERLAP = 13,
+};
+
+enum rt_rf_type_def {
+ RF_1T2R = 0,
+ RF_2T4R,
+ RF_2T2R,
+ RF_1T1R,
+ RF_2T2R_GREEN,
+ RF_819X_MAX_TYPE
+};
+
+enum wireless_mode {
+ WIRELESS_MODE_UNKNOWN = 0x00,
+ WIRELESS_MODE_A = 0x01,
+ WIRELESS_MODE_B = 0x02,
+ WIRELESS_MODE_G = 0x04,
+ WIRELESS_MODE_AUTO = 0x08,
+ WIRELESS_MODE_N_24G = 0x10,
+ WIRELESS_MODE_N_5G = 0x20
+};
+
+enum wireless_network_type {
+ WIRELESS_11B = 1,
+ WIRELESS_11G = 2,
+ WIRELESS_11A = 4,
+ WIRELESS_11N = 8
+};
+
+#define OUI_SUBTYPE_WMM_INFO 0
+#define OUI_SUBTYPE_WMM_PARAM 1
+#define OUI_SUBTYPE_QOS_CAPABI 5
+
+/* debug macros */
+extern u32 rtllib_debug_level;
+#define RTLLIB_DEBUG(level, fmt, args...) \
+do { \
+ if (rtllib_debug_level & (level)) \
+ printk(KERN_DEBUG "rtllib: " fmt, ## args); \
+} while (0)
+
+#define RTLLIB_DEBUG_DATA(level, data, datalen) \
+ do { \
+ if ((rtllib_debug_level & (level)) == (level)) { \
+ printk(KERN_DEBUG "rtllib: %s()\n", __func__); \
+ print_hex_dump_bytes(KERN_DEBUG, DUMP_PREFIX_NONE, \
+ data, datalen); \
+ } \
+ } while (0)
+
+/* To use the debug system;
+ *
+ * If you are defining a new debug classification, simply add it to the #define
+ * list here in the form of:
+ *
+ * #define RTLLIB_DL_xxxx VALUE
+ *
+ * shifting value to the left one bit from the previous entry. xxxx should be
+ * the name of the classification (for example, WEP)
+ *
+ * You then need to either add a RTLLIB_xxxx_DEBUG() macro definition for your
+ * classification, or use RTLLIB_DEBUG(RTLLIB_DL_xxxx, ...) whenever you want
+ * to send output to that classification.
+ *
+ * To add your debug level to the list of levels seen when you perform
+ *
+ * % cat /proc/net/ipw/debug_level
+ *
+ * you simply need to add your entry to the ipw_debug_levels array.
+ */
+
+#define RTLLIB_DL_INFO (1<<0)
+#define RTLLIB_DL_WX (1<<1)
+#define RTLLIB_DL_SCAN (1<<2)
+#define RTLLIB_DL_STATE (1<<3)
+#define RTLLIB_DL_MGMT (1<<4)
+#define RTLLIB_DL_FRAG (1<<5)
+#define RTLLIB_DL_EAP (1<<6)
+#define RTLLIB_DL_DROP (1<<7)
+
+#define RTLLIB_DL_TX (1<<8)
+#define RTLLIB_DL_RX (1<<9)
+
+#define RTLLIB_DL_HT (1<<10)
+#define RTLLIB_DL_BA (1<<11)
+#define RTLLIB_DL_TS (1<<12)
+#define RTLLIB_DL_QOS (1<<13)
+#define RTLLIB_DL_REORDER (1<<14)
+#define RTLLIB_DL_IOT (1<<15)
+#define RTLLIB_DL_IPS (1<<16)
+#define RTLLIB_DL_TRACE (1<<29)
+#define RTLLIB_DL_DATA (1<<30)
+#define RTLLIB_DL_ERR (1<<31)
+#define RTLLIB_ERROR(f, a...) pr_err("rtllib: " f, ## a)
+#define RTLLIB_WARNING(f, a...) pr_warn("rtllib: " f, ## a)
+#define RTLLIB_DEBUG_INFO(f, a...) RTLLIB_DEBUG(RTLLIB_DL_INFO, f, ## a)
+
+#define RTLLIB_DEBUG_WX(f, a...) RTLLIB_DEBUG(RTLLIB_DL_WX, f, ## a)
+#define RTLLIB_DEBUG_SCAN(f, a...) RTLLIB_DEBUG(RTLLIB_DL_SCAN, f, ## a)
+#define RTLLIB_DEBUG_STATE(f, a...) RTLLIB_DEBUG(RTLLIB_DL_STATE, f, ## a)
+#define RTLLIB_DEBUG_MGMT(f, a...) RTLLIB_DEBUG(RTLLIB_DL_MGMT, f, ## a)
+#define RTLLIB_DEBUG_FRAG(f, a...) RTLLIB_DEBUG(RTLLIB_DL_FRAG, f, ## a)
+#define RTLLIB_DEBUG_EAP(f, a...) RTLLIB_DEBUG(RTLLIB_DL_EAP, f, ## a)
+#define RTLLIB_DEBUG_DROP(f, a...) RTLLIB_DEBUG(RTLLIB_DL_DROP, f, ## a)
+#define RTLLIB_DEBUG_TX(f, a...) RTLLIB_DEBUG(RTLLIB_DL_TX, f, ## a)
+#define RTLLIB_DEBUG_RX(f, a...) RTLLIB_DEBUG(RTLLIB_DL_RX, f, ## a)
+#define RTLLIB_DEBUG_QOS(f, a...) RTLLIB_DEBUG(RTLLIB_DL_QOS, f, ## a)
+
+#ifndef ETH_P_PAE
+#define ETH_P_PAE 0x888E /* Port Access Entity (IEEE 802.1X) */
+#define ETH_P_IP 0x0800 /* Internet Protocol packet */
+#define ETH_P_ARP 0x0806 /* Address Resolution packet */
+#endif /* ETH_P_PAE */
+
+#define ETH_P_PREAUTH 0x88C7 /* IEEE 802.11i pre-authentication */
+
+#ifndef ETH_P_80211_RAW
+#define ETH_P_80211_RAW (ETH_P_ECONET + 1)
+#endif
+
+/* IEEE 802.11 defines */
+
+#define P80211_OUI_LEN 3
+
+struct rtllib_snap_hdr {
+
+ u8 dsap; /* always 0xAA */
+ u8 ssap; /* always 0xAA */
+ u8 ctrl; /* always 0x03 */
+ u8 oui[P80211_OUI_LEN]; /* organizational universal id */
+
+} __packed;
+
+enum _REG_PREAMBLE_MODE {
+ PREAMBLE_LONG = 1,
+ PREAMBLE_AUTO = 2,
+ PREAMBLE_SHORT = 3,
+};
+
+#define SNAP_SIZE sizeof(struct rtllib_snap_hdr)
+
+#define WLAN_FC_GET_VERS(fc) ((fc) & RTLLIB_FCTL_VERS)
+#define WLAN_FC_GET_TYPE(fc) ((fc) & RTLLIB_FCTL_FTYPE)
+#define WLAN_FC_GET_STYPE(fc) ((fc) & RTLLIB_FCTL_STYPE)
+#define WLAN_FC_MORE_DATA(fc) ((fc) & RTLLIB_FCTL_MOREDATA)
+
+#define WLAN_FC_GET_FRAMETYPE(fc) ((fc) & RTLLIB_FCTL_FRAMETYPE)
+#define WLAN_GET_SEQ_FRAG(seq) ((seq) & RTLLIB_SCTL_FRAG)
+#define WLAN_GET_SEQ_SEQ(seq) (((seq) & RTLLIB_SCTL_SEQ) >> 4)
+
+/* Authentication algorithms */
+#define WLAN_AUTH_OPEN 0
+#define WLAN_AUTH_SHARED_KEY 1
+#define WLAN_AUTH_LEAP 128
+
+#define WLAN_AUTH_CHALLENGE_LEN 128
+
+#define WLAN_CAPABILITY_ESS (1<<0)
+#define WLAN_CAPABILITY_IBSS (1<<1)
+#define WLAN_CAPABILITY_CF_POLLABLE (1<<2)
+#define WLAN_CAPABILITY_CF_POLL_REQUEST (1<<3)
+#define WLAN_CAPABILITY_PRIVACY (1<<4)
+#define WLAN_CAPABILITY_SHORT_PREAMBLE (1<<5)
+#define WLAN_CAPABILITY_PBCC (1<<6)
+#define WLAN_CAPABILITY_CHANNEL_AGILITY (1<<7)
+#define WLAN_CAPABILITY_SPECTRUM_MGMT (1<<8)
+#define WLAN_CAPABILITY_QOS (1<<9)
+#define WLAN_CAPABILITY_SHORT_SLOT_TIME (1<<10)
+#define WLAN_CAPABILITY_DSSS_OFDM (1<<13)
+
+/* 802.11g ERP information element */
+#define WLAN_ERP_NON_ERP_PRESENT (1<<0)
+#define WLAN_ERP_USE_PROTECTION (1<<1)
+#define WLAN_ERP_BARKER_PREAMBLE (1<<2)
+
+#define RTLLIB_STATMASK_SIGNAL (1<<0)
+#define RTLLIB_STATMASK_RSSI (1<<1)
+#define RTLLIB_STATMASK_NOISE (1<<2)
+#define RTLLIB_STATMASK_RATE (1<<3)
+#define RTLLIB_STATMASK_WEMASK 0x7
+
+#define RTLLIB_CCK_MODULATION (1<<0)
+#define RTLLIB_OFDM_MODULATION (1<<1)
+
+#define RTLLIB_24GHZ_BAND (1<<0)
+#define RTLLIB_52GHZ_BAND (1<<1)
+
+#define RTLLIB_CCK_RATE_LEN 4
+#define RTLLIB_CCK_RATE_1MB 0x02
+#define RTLLIB_CCK_RATE_2MB 0x04
+#define RTLLIB_CCK_RATE_5MB 0x0B
+#define RTLLIB_CCK_RATE_11MB 0x16
+#define RTLLIB_OFDM_RATE_LEN 8
+#define RTLLIB_OFDM_RATE_6MB 0x0C
+#define RTLLIB_OFDM_RATE_9MB 0x12
+#define RTLLIB_OFDM_RATE_12MB 0x18
+#define RTLLIB_OFDM_RATE_18MB 0x24
+#define RTLLIB_OFDM_RATE_24MB 0x30
+#define RTLLIB_OFDM_RATE_36MB 0x48
+#define RTLLIB_OFDM_RATE_48MB 0x60
+#define RTLLIB_OFDM_RATE_54MB 0x6C
+#define RTLLIB_BASIC_RATE_MASK 0x80
+
+#define RTLLIB_CCK_RATE_1MB_MASK (1<<0)
+#define RTLLIB_CCK_RATE_2MB_MASK (1<<1)
+#define RTLLIB_CCK_RATE_5MB_MASK (1<<2)
+#define RTLLIB_CCK_RATE_11MB_MASK (1<<3)
+#define RTLLIB_OFDM_RATE_6MB_MASK (1<<4)
+#define RTLLIB_OFDM_RATE_9MB_MASK (1<<5)
+#define RTLLIB_OFDM_RATE_12MB_MASK (1<<6)
+#define RTLLIB_OFDM_RATE_18MB_MASK (1<<7)
+#define RTLLIB_OFDM_RATE_24MB_MASK (1<<8)
+#define RTLLIB_OFDM_RATE_36MB_MASK (1<<9)
+#define RTLLIB_OFDM_RATE_48MB_MASK (1<<10)
+#define RTLLIB_OFDM_RATE_54MB_MASK (1<<11)
+
+#define RTLLIB_CCK_RATES_MASK 0x0000000F
+#define RTLLIB_CCK_BASIC_RATES_MASK (RTLLIB_CCK_RATE_1MB_MASK | \
+ RTLLIB_CCK_RATE_2MB_MASK)
+#define RTLLIB_CCK_DEFAULT_RATES_MASK (RTLLIB_CCK_BASIC_RATES_MASK | \
+ RTLLIB_CCK_RATE_5MB_MASK | \
+ RTLLIB_CCK_RATE_11MB_MASK)
+
+#define RTLLIB_OFDM_RATES_MASK 0x00000FF0
+#define RTLLIB_OFDM_BASIC_RATES_MASK (RTLLIB_OFDM_RATE_6MB_MASK | \
+ RTLLIB_OFDM_RATE_12MB_MASK | \
+ RTLLIB_OFDM_RATE_24MB_MASK)
+#define RTLLIB_OFDM_DEFAULT_RATES_MASK (RTLLIB_OFDM_BASIC_RATES_MASK | \
+ RTLLIB_OFDM_RATE_9MB_MASK | \
+ RTLLIB_OFDM_RATE_18MB_MASK | \
+ RTLLIB_OFDM_RATE_36MB_MASK | \
+ RTLLIB_OFDM_RATE_48MB_MASK | \
+ RTLLIB_OFDM_RATE_54MB_MASK)
+#define RTLLIB_DEFAULT_RATES_MASK (RTLLIB_OFDM_DEFAULT_RATES_MASK | \
+ RTLLIB_CCK_DEFAULT_RATES_MASK)
+
+#define RTLLIB_NUM_OFDM_RATES 8
+#define RTLLIB_NUM_CCK_RATES 4
+#define RTLLIB_OFDM_SHIFT_MASK_A 4
+
+
+/* this is stolen and modified from the madwifi driver*/
+#define RTLLIB_FC0_TYPE_MASK 0x0c
+#define RTLLIB_FC0_TYPE_DATA 0x08
+#define RTLLIB_FC0_SUBTYPE_MASK 0xB0
+#define RTLLIB_FC0_SUBTYPE_QOS 0x80
+
+#define RTLLIB_QOS_HAS_SEQ(fc) \
+ (((fc) & (RTLLIB_FC0_TYPE_MASK | RTLLIB_FC0_SUBTYPE_MASK)) == \
+ (RTLLIB_FC0_TYPE_DATA | RTLLIB_FC0_SUBTYPE_QOS))
+
+/* this is stolen from ipw2200 driver */
+#define IEEE_IBSS_MAC_HASH_SIZE 31
+struct ieee_ibss_seq {
+ u8 mac[ETH_ALEN];
+ u16 seq_num[17];
+ u16 frag_num[17];
+ unsigned long packet_time[17];
+ struct list_head list;
+};
+
+/* NOTE: This data is for statistical purposes; not all hardware provides this
+ * information for frames received. Not setting these will not cause
+ * any adverse affects. */
+struct rtllib_rx_stats {
+ u64 mac_time;
+ s8 rssi;
+ u8 signal;
+ u8 noise;
+ u16 rate; /* in 100 kbps */
+ u8 received_channel;
+ u8 control;
+ u8 mask;
+ u8 freq;
+ u16 len;
+ u64 tsf;
+ u32 beacon_time;
+ u8 nic_type;
+ u16 Length;
+ u8 SignalQuality;
+ s32 RecvSignalPower;
+ s8 RxPower;
+ u8 SignalStrength;
+ u16 bHwError:1;
+ u16 bCRC:1;
+ u16 bICV:1;
+ u16 bShortPreamble:1;
+ u16 Antenna:1;
+ u16 Decrypted:1;
+ u16 Wakeup:1;
+ u16 Reserved0:1;
+ u8 AGC;
+ u32 TimeStampLow;
+ u32 TimeStampHigh;
+ bool bShift;
+ bool bIsQosData;
+ u8 UserPriority;
+
+ u8 RxDrvInfoSize;
+ u8 RxBufShift;
+ bool bIsAMPDU;
+ bool bFirstMPDU;
+ bool bContainHTC;
+ bool RxIs40MHzPacket;
+ u32 RxPWDBAll;
+ u8 RxMIMOSignalStrength[4];
+ s8 RxMIMOSignalQuality[2];
+ bool bPacketMatchBSSID;
+ bool bIsCCK;
+ bool bPacketToSelf;
+ u8 *virtual_address;
+ u16 packetlength;
+ u16 fraglength;
+ u16 fragoffset;
+ u16 ntotalfrag;
+ bool bisrxaggrsubframe;
+ bool bPacketBeacon;
+ bool bToSelfBA;
+ char cck_adc_pwdb[4];
+ u16 Seq_Num;
+ u8 nTotalAggPkt;
+};
+
+/* IEEE 802.11 requires that STA supports concurrent reception of at least
+ * three fragmented frames. This define can be increased to support more
+ * concurrent frames, but it should be noted that each entry can consume about
+ * 2 kB of RAM and increasing cache size will slow down frame reassembly.
+ */
+#define RTLLIB_FRAG_CACHE_LEN 4
+
+struct rtllib_frag_entry {
+ unsigned long first_frag_time;
+ unsigned int seq;
+ unsigned int last_frag;
+ struct sk_buff *skb;
+ u8 src_addr[ETH_ALEN];
+ u8 dst_addr[ETH_ALEN];
+};
+
+struct rtllib_stats {
+ unsigned int tx_unicast_frames;
+ unsigned int tx_multicast_frames;
+ unsigned int tx_fragments;
+ unsigned int tx_unicast_octets;
+ unsigned int tx_multicast_octets;
+ unsigned int tx_deferred_transmissions;
+ unsigned int tx_single_retry_frames;
+ unsigned int tx_multiple_retry_frames;
+ unsigned int tx_retry_limit_exceeded;
+ unsigned int tx_discards;
+ unsigned int rx_unicast_frames;
+ unsigned int rx_multicast_frames;
+ unsigned int rx_fragments;
+ unsigned int rx_unicast_octets;
+ unsigned int rx_multicast_octets;
+ unsigned int rx_fcs_errors;
+ unsigned int rx_discards_no_buffer;
+ unsigned int tx_discards_wrong_sa;
+ unsigned int rx_discards_undecryptable;
+ unsigned int rx_message_in_msg_fragments;
+ unsigned int rx_message_in_bad_msg_fragments;
+};
+
+struct rtllib_device;
+
+#define SEC_KEY_1 (1<<0)
+#define SEC_KEY_2 (1<<1)
+#define SEC_KEY_3 (1<<2)
+#define SEC_KEY_4 (1<<3)
+#define SEC_ACTIVE_KEY (1<<4)
+#define SEC_AUTH_MODE (1<<5)
+#define SEC_UNICAST_GROUP (1<<6)
+#define SEC_LEVEL (1<<7)
+#define SEC_ENABLED (1<<8)
+#define SEC_ENCRYPT (1<<9)
+
+#define SEC_LEVEL_0 0 /* None */
+#define SEC_LEVEL_1 1 /* WEP 40 and 104 bit */
+#define SEC_LEVEL_2 2 /* Level 1 + TKIP */
+#define SEC_LEVEL_2_CKIP 3 /* Level 1 + CKIP */
+#define SEC_LEVEL_3 4 /* Level 2 + CCMP */
+
+#define SEC_ALG_NONE 0
+#define SEC_ALG_WEP 1
+#define SEC_ALG_TKIP 2
+#define SEC_ALG_CCMP 4
+
+#define WEP_KEY_LEN 13
+#define SCM_KEY_LEN 32
+#define SCM_TEMPORAL_KEY_LENGTH 16
+
+struct rtllib_security {
+ u16 active_key:2,
+ enabled:1,
+ auth_mode:2,
+ auth_algo:4,
+ unicast_uses_group:1,
+ encrypt:1;
+ u8 key_sizes[NUM_WEP_KEYS];
+ u8 keys[NUM_WEP_KEYS][SCM_KEY_LEN];
+ u8 level;
+ u16 flags;
+} __packed;
+
+
+/* 802.11 data frame from AP
+ * ,-------------------------------------------------------------------.
+ * Bytes | 2 | 2 | 6 | 6 | 6 | 2 | 0..2312 | 4 |
+ * |------|------|---------|---------|---------|------|---------|------|
+ * Desc. | ctrl | dura | DA/RA | TA | SA | Sequ | frame | fcs |
+ * | | tion | (BSSID) | | | ence | data | |
+ * `-------------------------------------------------------------------'
+ * Total: 28-2340 bytes
+ */
+
+/* Management Frame Information Element Types */
+enum rtllib_mfie {
+ MFIE_TYPE_SSID = 0,
+ MFIE_TYPE_RATES = 1,
+ MFIE_TYPE_FH_SET = 2,
+ MFIE_TYPE_DS_SET = 3,
+ MFIE_TYPE_CF_SET = 4,
+ MFIE_TYPE_TIM = 5,
+ MFIE_TYPE_IBSS_SET = 6,
+ MFIE_TYPE_COUNTRY = 7,
+ MFIE_TYPE_HOP_PARAMS = 8,
+ MFIE_TYPE_HOP_TABLE = 9,
+ MFIE_TYPE_REQUEST = 10,
+ MFIE_TYPE_CHALLENGE = 16,
+ MFIE_TYPE_POWER_CONSTRAINT = 32,
+ MFIE_TYPE_POWER_CAPABILITY = 33,
+ MFIE_TYPE_TPC_REQUEST = 34,
+ MFIE_TYPE_TPC_REPORT = 35,
+ MFIE_TYPE_SUPP_CHANNELS = 36,
+ MFIE_TYPE_CSA = 37,
+ MFIE_TYPE_MEASURE_REQUEST = 38,
+ MFIE_TYPE_MEASURE_REPORT = 39,
+ MFIE_TYPE_QUIET = 40,
+ MFIE_TYPE_IBSS_DFS = 41,
+ MFIE_TYPE_ERP = 42,
+ MFIE_TYPE_HT_CAP = 45,
+ MFIE_TYPE_RSN = 48,
+ MFIE_TYPE_RATES_EX = 50,
+ MFIE_TYPE_HT_INFO = 61,
+ MFIE_TYPE_AIRONET = 133,
+ MFIE_TYPE_GENERIC = 221,
+ MFIE_TYPE_QOS_PARAMETER = 222,
+};
+
+/* Minimal header; can be used for passing 802.11 frames with sufficient
+ * information to determine what type of underlying data type is actually
+ * stored in the data.
+ */
+struct rtllib_pspoll_hdr {
+ __le16 frame_ctl;
+ __le16 aid;
+ u8 bssid[ETH_ALEN];
+ u8 ta[ETH_ALEN];
+} __packed;
+
+struct rtllib_hdr {
+ __le16 frame_ctl;
+ __le16 duration_id;
+ u8 payload[0];
+} __packed;
+
+struct rtllib_hdr_1addr {
+ __le16 frame_ctl;
+ __le16 duration_id;
+ u8 addr1[ETH_ALEN];
+ u8 payload[0];
+} __packed;
+
+struct rtllib_hdr_2addr {
+ __le16 frame_ctl;
+ __le16 duration_id;
+ u8 addr1[ETH_ALEN];
+ u8 addr2[ETH_ALEN];
+ u8 payload[0];
+} __packed;
+
+struct rtllib_hdr_3addr {
+ __le16 frame_ctl;
+ __le16 duration_id;
+ u8 addr1[ETH_ALEN];
+ u8 addr2[ETH_ALEN];
+ u8 addr3[ETH_ALEN];
+ __le16 seq_ctl;
+ u8 payload[0];
+} __packed;
+
+struct rtllib_hdr_4addr {
+ __le16 frame_ctl;
+ __le16 duration_id;
+ u8 addr1[ETH_ALEN];
+ u8 addr2[ETH_ALEN];
+ u8 addr3[ETH_ALEN];
+ __le16 seq_ctl;
+ u8 addr4[ETH_ALEN];
+ u8 payload[0];
+} __packed;
+
+struct rtllib_hdr_3addrqos {
+ __le16 frame_ctl;
+ __le16 duration_id;
+ u8 addr1[ETH_ALEN];
+ u8 addr2[ETH_ALEN];
+ u8 addr3[ETH_ALEN];
+ __le16 seq_ctl;
+ __le16 qos_ctl;
+ u8 payload[0];
+} __packed;
+
+struct rtllib_hdr_4addrqos {
+ __le16 frame_ctl;
+ __le16 duration_id;
+ u8 addr1[ETH_ALEN];
+ u8 addr2[ETH_ALEN];
+ u8 addr3[ETH_ALEN];
+ __le16 seq_ctl;
+ u8 addr4[ETH_ALEN];
+ __le16 qos_ctl;
+ u8 payload[0];
+} __packed;
+
+struct rtllib_info_element {
+ u8 id;
+ u8 len;
+ u8 data[0];
+} __packed;
+
+struct rtllib_authentication {
+ struct rtllib_hdr_3addr header;
+ __le16 algorithm;
+ __le16 transaction;
+ __le16 status;
+ /*challenge*/
+ struct rtllib_info_element info_element[0];
+} __packed;
+
+struct rtllib_disauth {
+ struct rtllib_hdr_3addr header;
+ __le16 reason;
+} __packed;
+
+struct rtllib_disassoc {
+ struct rtllib_hdr_3addr header;
+ __le16 reason;
+} __packed;
+
+struct rtllib_probe_request {
+ struct rtllib_hdr_3addr header;
+ /* SSID, supported rates */
+ struct rtllib_info_element info_element[0];
+} __packed;
+
+struct rtllib_probe_response {
+ struct rtllib_hdr_3addr header;
+ u32 time_stamp[2];
+ __le16 beacon_interval;
+ __le16 capability;
+ /* SSID, supported rates, FH params, DS params,
+ * CF params, IBSS params, TIM (if beacon), RSN
+ */
+ struct rtllib_info_element info_element[0];
+} __packed;
+
+/* Alias beacon for probe_response */
+#define rtllib_beacon rtllib_probe_response
+
+struct rtllib_assoc_request_frame {
+ struct rtllib_hdr_3addr header;
+ __le16 capability;
+ __le16 listen_interval;
+ /* SSID, supported rates, RSN */
+ struct rtllib_info_element info_element[0];
+} __packed;
+
+struct rtllib_reassoc_request_frame {
+ struct rtllib_hdr_3addr header;
+ __le16 capability;
+ __le16 listen_interval;
+ u8 current_ap[ETH_ALEN];
+ /* SSID, supported rates, RSN */
+ struct rtllib_info_element info_element[0];
+} __packed;
+
+struct rtllib_assoc_response_frame {
+ struct rtllib_hdr_3addr header;
+ __le16 capability;
+ __le16 status;
+ __le16 aid;
+ struct rtllib_info_element info_element[0]; /* supported rates */
+} __packed;
+
+struct rtllib_txb {
+ u8 nr_frags;
+ u8 encrypted;
+ u8 queue_index;
+ u8 rts_included;
+ u16 reserved;
+ __le16 frag_size;
+ __le16 payload_size;
+ struct sk_buff *fragments[0];
+};
+
+#define MAX_TX_AGG_COUNT 16
+struct rtllib_drv_agg_txb {
+ u8 nr_drv_agg_frames;
+ struct sk_buff *tx_agg_frames[MAX_TX_AGG_COUNT];
+} __packed;
+
+#define MAX_SUBFRAME_COUNT 64
+struct rtllib_rxb {
+ u8 nr_subframes;
+ struct sk_buff *subframes[MAX_SUBFRAME_COUNT];
+ u8 dst[ETH_ALEN];
+ u8 src[ETH_ALEN];
+} __packed;
+
+union frameqos {
+ u16 shortdata;
+ u8 chardata[2];
+ struct {
+ u16 tid:4;
+ u16 eosp:1;
+ u16 ack_policy:2;
+ u16 reserved:1;
+ u16 txop:8;
+ } field;
+};
+
+/* SWEEP TABLE ENTRIES NUMBER*/
+#define MAX_SWEEP_TAB_ENTRIES 42
+#define MAX_SWEEP_TAB_ENTRIES_PER_PACKET 7
+/* MAX_RATES_LENGTH needs to be 12. The spec says 8, and many APs
+ * only use 8, and then use extended rates for the remaining supported
+ * rates. Other APs, however, stick all of their supported rates on the
+ * main rates information element...
+ */
+#define MAX_RATES_LENGTH ((u8)12)
+#define MAX_RATES_EX_LENGTH ((u8)16)
+#define MAX_NETWORK_COUNT 96
+
+#define MAX_CHANNEL_NUMBER 161
+#define RTLLIB_SOFTMAC_SCAN_TIME 100
+#define RTLLIB_SOFTMAC_ASSOC_RETRY_TIME (HZ * 2)
+
+#define CRC_LENGTH 4U
+
+#define MAX_WPA_IE_LEN 64
+#define MAX_WZC_IE_LEN 256
+
+#define NETWORK_EMPTY_ESSID (1<<0)
+#define NETWORK_HAS_OFDM (1<<1)
+#define NETWORK_HAS_CCK (1<<2)
+
+/* QoS structure */
+#define NETWORK_HAS_QOS_PARAMETERS (1<<3)
+#define NETWORK_HAS_QOS_INFORMATION (1<<4)
+#define NETWORK_HAS_QOS_MASK (NETWORK_HAS_QOS_PARAMETERS | \
+ NETWORK_HAS_QOS_INFORMATION)
+/* 802.11h */
+#define NETWORK_HAS_POWER_CONSTRAINT (1<<5)
+#define NETWORK_HAS_CSA (1<<6)
+#define NETWORK_HAS_QUIET (1<<7)
+#define NETWORK_HAS_IBSS_DFS (1<<8)
+#define NETWORK_HAS_TPC_REPORT (1<<9)
+
+#define NETWORK_HAS_ERP_VALUE (1<<10)
+
+#define QOS_QUEUE_NUM 4
+#define QOS_OUI_LEN 3
+#define QOS_OUI_TYPE 2
+#define QOS_ELEMENT_ID 221
+#define QOS_OUI_INFO_SUB_TYPE 0
+#define QOS_OUI_PARAM_SUB_TYPE 1
+#define QOS_VERSION_1 1
+#define QOS_AIFSN_MIN_VALUE 2
+
+struct rtllib_qos_information_element {
+ u8 elementID;
+ u8 length;
+ u8 qui[QOS_OUI_LEN];
+ u8 qui_type;
+ u8 qui_subtype;
+ u8 version;
+ u8 ac_info;
+} __packed;
+
+struct rtllib_qos_ac_parameter {
+ u8 aci_aifsn;
+ u8 ecw_min_max;
+ __le16 tx_op_limit;
+} __packed;
+
+struct rtllib_qos_parameter_info {
+ struct rtllib_qos_information_element info_element;
+ u8 reserved;
+ struct rtllib_qos_ac_parameter ac_params_record[QOS_QUEUE_NUM];
+} __packed;
+
+struct rtllib_qos_parameters {
+ __le16 cw_min[QOS_QUEUE_NUM];
+ __le16 cw_max[QOS_QUEUE_NUM];
+ u8 aifs[QOS_QUEUE_NUM];
+ u8 flag[QOS_QUEUE_NUM];
+ __le16 tx_op_limit[QOS_QUEUE_NUM];
+} __packed;
+
+struct rtllib_qos_data {
+ struct rtllib_qos_parameters parameters;
+ unsigned int wmm_acm;
+ int active;
+ int supported;
+ u8 param_count;
+ u8 old_param_count;
+};
+
+struct rtllib_tim_parameters {
+ u8 tim_count;
+ u8 tim_period;
+} __packed;
+
+struct rtllib_wmm_ac_param {
+ u8 ac_aci_acm_aifsn;
+ u8 ac_ecwmin_ecwmax;
+ u16 ac_txop_limit;
+};
+
+struct rtllib_wmm_ts_info {
+ u8 ac_dir_tid;
+ u8 ac_up_psb;
+ u8 reserved;
+} __packed;
+
+struct rtllib_wmm_tspec_elem {
+ struct rtllib_wmm_ts_info ts_info;
+ u16 norm_msdu_size;
+ u16 max_msdu_size;
+ u32 min_serv_inter;
+ u32 max_serv_inter;
+ u32 inact_inter;
+ u32 suspen_inter;
+ u32 serv_start_time;
+ u32 min_data_rate;
+ u32 mean_data_rate;
+ u32 peak_data_rate;
+ u32 max_burst_size;
+ u32 delay_bound;
+ u32 min_phy_rate;
+ u16 surp_band_allow;
+ u16 medium_time;
+} __packed;
+
+enum eap_type {
+ EAP_PACKET = 0,
+ EAPOL_START,
+ EAPOL_LOGOFF,
+ EAPOL_KEY,
+ EAPOL_ENCAP_ASF_ALERT
+};
+
+static const char * const eap_types[] = {
+ [EAP_PACKET] = "EAP-Packet",
+ [EAPOL_START] = "EAPOL-Start",
+ [EAPOL_LOGOFF] = "EAPOL-Logoff",
+ [EAPOL_KEY] = "EAPOL-Key",
+ [EAPOL_ENCAP_ASF_ALERT] = "EAPOL-Encap-ASF-Alert"
+};
+
+static inline const char *eap_get_type(int type)
+{
+ return ((u32)type >= ARRAY_SIZE(eap_types)) ? "Unknown" :
+ eap_types[type];
+}
+static inline u8 Frame_QoSTID(u8 *buf)
+{
+ struct rtllib_hdr_3addr *hdr;
+ u16 fc;
+
+ hdr = (struct rtllib_hdr_3addr *)buf;
+ fc = le16_to_cpu(hdr->frame_ctl);
+ return (u8)((union frameqos *)(buf + (((fc & RTLLIB_FCTL_TODS) &&
+ (fc & RTLLIB_FCTL_FROMDS)) ? 30 : 24)))->field.tid;
+}
+
+
+struct eapol {
+ u8 snap[6];
+ u16 ethertype;
+ u8 version;
+ u8 type;
+ u16 length;
+} __packed;
+
+struct rtllib_softmac_stats {
+ unsigned int rx_ass_ok;
+ unsigned int rx_ass_err;
+ unsigned int rx_probe_rq;
+ unsigned int tx_probe_rs;
+ unsigned int tx_beacons;
+ unsigned int rx_auth_rq;
+ unsigned int rx_auth_rs_ok;
+ unsigned int rx_auth_rs_err;
+ unsigned int tx_auth_rq;
+ unsigned int no_auth_rs;
+ unsigned int no_ass_rs;
+ unsigned int tx_ass_rq;
+ unsigned int rx_ass_rq;
+ unsigned int tx_probe_rq;
+ unsigned int reassoc;
+ unsigned int swtxstop;
+ unsigned int swtxawake;
+ unsigned char CurrentShowTxate;
+ unsigned char last_packet_rate;
+ unsigned int txretrycount;
+};
+
+#define BEACON_PROBE_SSID_ID_POSITION 12
+
+struct rtllib_info_element_hdr {
+ u8 id;
+ u8 len;
+} __packed;
+
+/* These are the data types that can make up management packets
+ *
+ * u16 auth_algorithm;
+ * u16 auth_sequence;
+ * u16 beacon_interval;
+ * u16 capability;
+ * u8 current_ap[ETH_ALEN];
+ * u16 listen_interval;
+ * struct {
+ * u16 association_id:14, reserved:2;
+ * } __packed;
+ * u32 time_stamp[2];
+ * u16 reason;
+ * u16 status;
+ */
+
+#define RTLLIB_DEFAULT_TX_ESSID "Penguin"
+#define RTLLIB_DEFAULT_BASIC_RATE 2
+
+enum {WMM_all_frame, WMM_two_frame, WMM_four_frame, WMM_six_frame};
+#define MAX_SP_Len (WMM_all_frame << 4)
+#define RTLLIB_QOS_TID 0x0f
+#define QOS_CTL_NOTCONTAIN_ACK (0x01 << 5)
+
+#define RTLLIB_DTIM_MBCAST 4
+#define RTLLIB_DTIM_UCAST 2
+#define RTLLIB_DTIM_VALID 1
+#define RTLLIB_DTIM_INVALID 0
+
+#define RTLLIB_PS_DISABLED 0
+#define RTLLIB_PS_UNICAST RTLLIB_DTIM_UCAST
+#define RTLLIB_PS_MBCAST RTLLIB_DTIM_MBCAST
+
+#define WME_AC_BK 0x00
+#define WME_AC_BE 0x01
+#define WME_AC_VI 0x02
+#define WME_AC_VO 0x03
+#define WME_ACI_MASK 0x03
+#define WME_AIFSN_MASK 0x03
+#define WME_AC_PRAM_LEN 16
+
+#define MAX_RECEIVE_BUFFER_SIZE 9100
+
+#define UP2AC(up) ( \
+ ((up) < 1) ? WME_AC_BE : \
+ ((up) < 3) ? WME_AC_BK : \
+ ((up) < 4) ? WME_AC_BE : \
+ ((up) < 6) ? WME_AC_VI : \
+ WME_AC_VO)
+
+#define AC2UP(_ac) ( \
+ ((_ac) == WME_AC_VO) ? 6 : \
+ ((_ac) == WME_AC_VI) ? 5 : \
+ ((_ac) == WME_AC_BK) ? 1 : \
+ 0)
+
+#define ETHER_ADDR_LEN 6 /* length of an Ethernet address */
+#define ETHERNET_HEADER_SIZE 14 /* length of two Ethernet address
+ * plus ether type*/
+
+struct ether_header {
+ u8 ether_dhost[ETHER_ADDR_LEN];
+ u8 ether_shost[ETHER_ADDR_LEN];
+ u16 ether_type;
+} __packed;
+
+#ifndef ETHERTYPE_PAE
+#define ETHERTYPE_PAE 0x888e /* EAPOL PAE/802.1x */
+#endif
+#ifndef ETHERTYPE_IP
+#define ETHERTYPE_IP 0x0800 /* IP protocol */
+#endif
+
+
+enum erp_t {
+ ERP_NonERPpresent = 0x01,
+ ERP_UseProtection = 0x02,
+ ERP_BarkerPreambleMode = 0x04,
+};
+
+struct rtllib_network {
+ /* These entries are used to identify a unique network */
+ u8 bssid[ETH_ALEN];
+ u8 channel;
+ /* Ensure null-terminated for any debug msgs */
+ u8 ssid[IW_ESSID_MAX_SIZE + 1];
+ u8 ssid_len;
+ u8 hidden_ssid[IW_ESSID_MAX_SIZE + 1];
+ u8 hidden_ssid_len;
+ struct rtllib_qos_data qos_data;
+
+ bool bWithAironetIE;
+ bool bCkipSupported;
+ bool bCcxRmEnable;
+ u16 CcxRmState[2];
+ bool bMBssidValid;
+ u8 MBssidMask;
+ u8 MBssid[6];
+ bool bWithCcxVerNum;
+ u8 BssCcxVerNumber;
+ /* These are network statistics */
+ struct rtllib_rx_stats stats;
+ u16 capability;
+ u8 rates[MAX_RATES_LENGTH];
+ u8 rates_len;
+ u8 rates_ex[MAX_RATES_EX_LENGTH];
+ u8 rates_ex_len;
+ unsigned long last_scanned;
+ u8 mode;
+ u32 flags;
+ u32 last_associate;
+ u32 time_stamp[2];
+ u16 beacon_interval;
+ u16 listen_interval;
+ u16 atim_window;
+ u8 erp_value;
+ u8 wpa_ie[MAX_WPA_IE_LEN];
+ size_t wpa_ie_len;
+ u8 rsn_ie[MAX_WPA_IE_LEN];
+ size_t rsn_ie_len;
+ u8 wzc_ie[MAX_WZC_IE_LEN];
+ size_t wzc_ie_len;
+
+ struct rtllib_tim_parameters tim;
+ u8 dtim_period;
+ u8 dtim_data;
+ u64 last_dtim_sta_time;
+
+ u8 wmm_info;
+ struct rtllib_wmm_ac_param wmm_param[4];
+ u8 Turbo_Enable;
+ u16 CountryIeLen;
+ u8 CountryIeBuf[MAX_IE_LEN];
+ struct bss_ht bssht;
+ bool broadcom_cap_exist;
+ bool realtek_cap_exit;
+ bool marvell_cap_exist;
+ bool ralink_cap_exist;
+ bool atheros_cap_exist;
+ bool cisco_cap_exist;
+ bool airgo_cap_exist;
+ bool unknown_cap_exist;
+ bool berp_info_valid;
+ bool buseprotection;
+ bool bIsNetgear854T;
+ u8 SignalStrength;
+ u8 RSSI;
+ struct list_head list;
+};
+
+enum rtllib_state {
+
+ /* the card is not linked at all */
+ RTLLIB_NOLINK = 0,
+
+ /* RTLLIB_ASSOCIATING* are for BSS client mode
+ * the driver shall not perform RX filtering unless
+ * the state is LINKED.
+ * The driver shall just check for the state LINKED and
+ * defaults to NOLINK for ALL the other states (including
+ * LINKED_SCANNING)
+ */
+
+ /* the association procedure will start (wq scheduling)*/
+ RTLLIB_ASSOCIATING,
+ RTLLIB_ASSOCIATING_RETRY,
+
+ /* the association procedure is sending AUTH request*/
+ RTLLIB_ASSOCIATING_AUTHENTICATING,
+
+ /* the association procedure has successfully authenticated
+ * and is sending association request
+ */
+ RTLLIB_ASSOCIATING_AUTHENTICATED,
+
+ /* the link is ok. the card associated to a BSS or linked
+ * to a ibss cell or acting as an AP and creating the bss
+ */
+ RTLLIB_LINKED,
+
+ /* same as LINKED, but the driver shall apply RX filter
+ * rules as we are in NO_LINK mode. As the card is still
+ * logically linked, but it is doing a syncro site survey
+ * then it will be back to LINKED state.
+ */
+ RTLLIB_LINKED_SCANNING,
+};
+
+#define DEFAULT_MAX_SCAN_AGE (15 * HZ)
+#define DEFAULT_FTS 2346
+
+#define CFG_RTLLIB_RESERVE_FCS (1<<0)
+#define CFG_RTLLIB_COMPUTE_FCS (1<<1)
+#define CFG_RTLLIB_RTS (1<<2)
+
+#define RTLLIB_24GHZ_MIN_CHANNEL 1
+#define RTLLIB_24GHZ_MAX_CHANNEL 14
+#define RTLLIB_24GHZ_CHANNELS (RTLLIB_24GHZ_MAX_CHANNEL - \
+ RTLLIB_24GHZ_MIN_CHANNEL + 1)
+
+#define RTLLIB_52GHZ_MIN_CHANNEL 34
+#define RTLLIB_52GHZ_MAX_CHANNEL 165
+#define RTLLIB_52GHZ_CHANNELS (RTLLIB_52GHZ_MAX_CHANNEL - \
+ RTLLIB_52GHZ_MIN_CHANNEL + 1)
+struct tx_pending {
+ int frag;
+ struct rtllib_txb *txb;
+};
+
+struct bandwidth_autoswitch {
+ long threshold_20Mhzto40Mhz;
+ long threshold_40Mhzto20Mhz;
+ bool bforced_tx20Mhz;
+ bool bautoswitch_enable;
+};
+
+
+
+#define REORDER_WIN_SIZE 128
+#define REORDER_ENTRY_NUM 128
+struct rx_reorder_entry {
+ struct list_head List;
+ u16 SeqNum;
+ struct rtllib_rxb *prxb;
+};
+enum fsync_state {
+ Default_Fsync,
+ HW_Fsync,
+ SW_Fsync
+};
+
+enum rt_ps_mode {
+ eActive,
+ eMaxPs,
+ eFastPs,
+ eAutoPs,
+};
+
+enum ips_callback_function {
+ IPS_CALLBACK_NONE = 0,
+ IPS_CALLBACK_MGNT_LINK_REQUEST = 1,
+ IPS_CALLBACK_JOIN_REQUEST = 2,
+};
+
+enum rt_join_action {
+ RT_JOIN_INFRA = 1,
+ RT_JOIN_IBSS = 2,
+ RT_START_IBSS = 3,
+ RT_NO_ACTION = 4,
+};
+
+struct ibss_parms {
+ u16 atimWin;
+};
+#define MAX_NUM_RATES 264
+
+enum rt_rf_power_state {
+ eRfOn,
+ eRfSleep,
+ eRfOff
+};
+
+#define MAX_SUPPORT_WOL_PATTERN_NUM 8
+
+#define MAX_WOL_BIT_MASK_SIZE 16
+#define MAX_WOL_PATTERN_SIZE 128
+
+enum wol_pattern_type {
+ eNetBIOS = 0,
+ eIPv4IPv6ARP,
+ eIPv4IPv6TCPSYN,
+ eMACIDOnly,
+ eNoDefined,
+};
+
+struct rt_pm_wol_info {
+ u32 PatternId;
+ u32 Mask[4];
+ u16 CrcRemainder;
+ u8 WFMIndex;
+ enum wol_pattern_type PatternType;
+};
+
+struct rt_pwr_save_ctrl {
+
+ bool bInactivePs;
+ bool bIPSModeBackup;
+ bool bHaltAdapterClkRQ;
+ bool bSwRfProcessing;
+ enum rt_rf_power_state eInactivePowerState;
+ struct work_struct InactivePsWorkItem;
+ struct timer_list InactivePsTimer;
+
+ enum ips_callback_function ReturnPoint;
+
+ bool bTmpBssDesc;
+ enum rt_join_action tmpJoinAction;
+ struct rtllib_network tmpBssDesc;
+
+ bool bTmpScanOnly;
+ bool bTmpActiveScan;
+ bool bTmpFilterHiddenAP;
+ bool bTmpUpdateParms;
+ u8 tmpSsidBuf[33];
+ struct octet_string tmpSsid2Scan;
+ bool bTmpSsid2Scan;
+ u8 tmpNetworkType;
+ u8 tmpChannelNumber;
+ u16 tmpBcnPeriod;
+ u8 tmpDtimPeriod;
+ u16 tmpmCap;
+ struct octet_string tmpSuppRateSet;
+ u8 tmpSuppRateBuf[MAX_NUM_RATES];
+ bool bTmpSuppRate;
+ struct ibss_parms tmpIbpm;
+ bool bTmpIbpm;
+
+ bool bLeisurePs;
+ u32 PowerProfile;
+ u8 LpsIdleCount;
+ u8 RegMaxLPSAwakeIntvl;
+ u8 LPSAwakeIntvl;
+
+ u32 CurPsLevel;
+ u32 RegRfPsLevel;
+
+ bool bFwCtrlLPS;
+ u8 FWCtrlPSMode;
+
+ bool LinkReqInIPSRFOffPgs;
+ bool BufConnectinfoBefore;
+
+
+ bool bGpioRfSw;
+
+ u8 RegAMDPciASPM;
+
+ u8 oWLANMode;
+ struct rt_pm_wol_info PmWoLPatternInfo[MAX_SUPPORT_WOL_PATTERN_NUM];
+
+};
+
+#define RT_RF_CHANGE_SOURCE u32
+
+#define RF_CHANGE_BY_SW BIT31
+#define RF_CHANGE_BY_HW BIT30
+#define RF_CHANGE_BY_PS BIT29
+#define RF_CHANGE_BY_IPS BIT28
+#define RF_CHANGE_BY_INIT 0
+
+enum country_code_type {
+ COUNTRY_CODE_FCC = 0,
+ COUNTRY_CODE_IC = 1,
+ COUNTRY_CODE_ETSI = 2,
+ COUNTRY_CODE_SPAIN = 3,
+ COUNTRY_CODE_FRANCE = 4,
+ COUNTRY_CODE_MKK = 5,
+ COUNTRY_CODE_MKK1 = 6,
+ COUNTRY_CODE_ISRAEL = 7,
+ COUNTRY_CODE_TELEC = 8,
+ COUNTRY_CODE_MIC = 9,
+ COUNTRY_CODE_GLOBAL_DOMAIN = 10,
+ COUNTRY_CODE_WORLD_WIDE_13 = 11,
+ COUNTRY_CODE_TELEC_NETGEAR = 12,
+ COUNTRY_CODE_MAX
+};
+
+enum scan_op_backup_opt {
+ SCAN_OPT_BACKUP = 0,
+ SCAN_OPT_RESTORE,
+ SCAN_OPT_MAX
+};
+
+enum fw_cmd_io_type {
+ FW_CMD_DIG_ENABLE = 0,
+ FW_CMD_DIG_DISABLE = 1,
+ FW_CMD_DIG_HALT = 2,
+ FW_CMD_DIG_RESUME = 3,
+ FW_CMD_HIGH_PWR_ENABLE = 4,
+ FW_CMD_HIGH_PWR_DISABLE = 5,
+ FW_CMD_RA_RESET = 6,
+ FW_CMD_RA_ACTIVE = 7,
+ FW_CMD_RA_REFRESH_N = 8,
+ FW_CMD_RA_REFRESH_BG = 9,
+ FW_CMD_RA_INIT = 10,
+ FW_CMD_IQK_ENABLE = 11,
+ FW_CMD_TXPWR_TRACK_ENABLE = 12,
+ FW_CMD_TXPWR_TRACK_DISABLE = 13,
+ FW_CMD_TXPWR_TRACK_THERMAL = 14,
+ FW_CMD_PAUSE_DM_BY_SCAN = 15,
+ FW_CMD_RESUME_DM_BY_SCAN = 16,
+ FW_CMD_RA_REFRESH_N_COMB = 17,
+ FW_CMD_RA_REFRESH_BG_COMB = 18,
+ FW_CMD_ANTENNA_SW_ENABLE = 19,
+ FW_CMD_ANTENNA_SW_DISABLE = 20,
+ FW_CMD_TX_FEEDBACK_CCX_ENABLE = 21,
+ FW_CMD_LPS_ENTER = 22,
+ FW_CMD_LPS_LEAVE = 23,
+ FW_CMD_DIG_MODE_SS = 24,
+ FW_CMD_DIG_MODE_FA = 25,
+ FW_CMD_ADD_A2_ENTRY = 26,
+ FW_CMD_CTRL_DM_BY_DRIVER = 27,
+ FW_CMD_CTRL_DM_BY_DRIVER_NEW = 28,
+ FW_CMD_PAPE_CONTROL = 29,
+ FW_CMD_CHAN_SET = 30,
+};
+
+#define RT_MAX_LD_SLOT_NUM 10
+struct rt_link_detect {
+
+ u32 NumRecvBcnInPeriod;
+ u32 NumRecvDataInPeriod;
+
+ u32 RxBcnNum[RT_MAX_LD_SLOT_NUM];
+ u32 RxDataNum[RT_MAX_LD_SLOT_NUM];
+ u16 SlotNum;
+ u16 SlotIndex;
+
+ u32 NumTxOkInPeriod;
+ u32 NumRxOkInPeriod;
+ u32 NumRxUnicastOkInPeriod;
+ bool bBusyTraffic;
+ bool bHigherBusyTraffic;
+ bool bHigherBusyRxTraffic;
+ u8 IdleCount;
+ u32 NumTxUnicastOkInPeriod;
+ u32 LastNumTxUnicast;
+ u32 LastNumRxUnicast;
+};
+
+struct sw_cam_table {
+
+ u8 macaddr[6];
+ bool bused;
+ u8 key_buf[16];
+ u16 key_type;
+ u8 useDK;
+ u8 key_index;
+
+};
+#define TOTAL_CAM_ENTRY 32
+struct rate_adaptive {
+ u8 rate_adaptive_disabled;
+ u8 ratr_state;
+ u16 reserve;
+
+ u32 high_rssi_thresh_for_ra;
+ u32 high2low_rssi_thresh_for_ra;
+ u8 low2high_rssi_thresh_for_ra40M;
+ u32 low_rssi_thresh_for_ra40M;
+ u8 low2high_rssi_thresh_for_ra20M;
+ u32 low_rssi_thresh_for_ra20M;
+ u32 upper_rssi_threshold_ratr;
+ u32 middle_rssi_threshold_ratr;
+ u32 low_rssi_threshold_ratr;
+ u32 low_rssi_threshold_ratr_40M;
+ u32 low_rssi_threshold_ratr_20M;
+ u8 ping_rssi_enable;
+ u32 ping_rssi_ratr;
+ u32 ping_rssi_thresh_for_ra;
+ u32 last_ratr;
+ u8 PreRATRState;
+
+};
+enum ratr_table_mode_8192s {
+ RATR_INX_WIRELESS_NGB = 0,
+ RATR_INX_WIRELESS_NG = 1,
+ RATR_INX_WIRELESS_NB = 2,
+ RATR_INX_WIRELESS_N = 3,
+ RATR_INX_WIRELESS_GB = 4,
+ RATR_INX_WIRELESS_G = 5,
+ RATR_INX_WIRELESS_B = 6,
+ RATR_INX_WIRELESS_MC = 7,
+ RATR_INX_WIRELESS_A = 8,
+};
+
+#define NUM_PMKID_CACHE 16
+struct rt_pmkid_list {
+ u8 bUsed;
+ u8 Bssid[6];
+ u8 PMKID[16];
+ u8 SsidBuf[33];
+ u8 *ssid_octet;
+ u16 ssid_length;
+};
+
+struct rt_intel_promisc_mode {
+ bool bPromiscuousOn;
+ bool bFilterSourceStationFrame;
+};
+
+
+/*************** DRIVER STATUS *****/
+#define STATUS_SCANNING 0
+#define STATUS_SCAN_HW 1
+#define STATUS_SCAN_ABORTING 2
+#define STATUS_SETTING_CHAN 3
+/*************** DRIVER STATUS *****/
+
+enum {
+ NO_USE = 0,
+ USED = 1,
+ HW_SEC = 2,
+ SW_SEC = 3,
+};
+
+enum {
+ LPS_IS_WAKE = 0,
+ LPS_IS_SLEEP = 1,
+ LPS_WAIT_NULL_DATA_SEND = 2,
+};
+
+struct rtllib_device {
+ struct pci_dev *pdev;
+ struct net_device *dev;
+ struct rtllib_security sec;
+
+ bool disable_mgnt_queue;
+
+ unsigned long status;
+ short hwscan_ch_bk;
+ enum ht_extchnl_offset chan_offset_bk;
+ enum ht_channel_width bandwidth_bk;
+ u8 hwscan_sem_up;
+ u8 CntAfterLink;
+
+ enum rt_op_mode OpMode;
+
+ u8 VersionID;
+ /* The last AssocReq/Resp IEs */
+ u8 *assocreq_ies, *assocresp_ies;
+ size_t assocreq_ies_len, assocresp_ies_len;
+
+ bool b_customer_lenovo_id;
+ bool bForcedShowRxRate;
+ bool bForcedShowRateStill;
+ u8 SystemQueryDataRateCount;
+ bool bForcedBgMode;
+ bool bUseRAMask;
+ bool b1x1RecvCombine;
+ u8 RF_Type;
+ bool b1SSSupport;
+
+ u8 hwsec_active;
+ bool is_silent_reset;
+ bool force_mic_error;
+ bool is_roaming;
+ bool ieee_up;
+ bool cannot_notify;
+ bool bSupportRemoteWakeUp;
+ enum rt_ps_mode dot11PowerSaveMode;
+ bool actscanning;
+ bool FirstIe_InScan;
+ bool be_scan_inprogress;
+ bool beinretry;
+ enum rt_rf_power_state eRFPowerState;
+ RT_RF_CHANGE_SOURCE RfOffReason;
+ bool is_set_key;
+ bool wx_set_enc;
+ struct rt_hi_throughput *pHTInfo;
+ spinlock_t bw_spinlock;
+
+ spinlock_t reorder_spinlock;
+ u8 Regdot11HTOperationalRateSet[16];
+ u8 Regdot11TxHTOperationalRateSet[16];
+ u8 dot11HTOperationalRateSet[16];
+ u8 RegHTSuppRateSet[16];
+ u8 HTCurrentOperaRate;
+ u8 HTHighestOperaRate;
+ u8 MinSpaceCfg;
+ u8 MaxMssDensity;
+ u8 bTxDisableRateFallBack;
+ u8 bTxUseDriverAssingedRate;
+ u8 bTxEnableFwCalcDur;
+ atomic_t atm_chnlop;
+ atomic_t atm_swbw;
+
+ struct list_head Tx_TS_Admit_List;
+ struct list_head Tx_TS_Pending_List;
+ struct list_head Tx_TS_Unused_List;
+ struct tx_ts_record TxTsRecord[TOTAL_TS_NUM];
+ struct list_head Rx_TS_Admit_List;
+ struct list_head Rx_TS_Pending_List;
+ struct list_head Rx_TS_Unused_List;
+ struct rx_ts_record RxTsRecord[TOTAL_TS_NUM];
+ struct rx_reorder_entry RxReorderEntry[128];
+ struct list_head RxReorder_Unused_List;
+ u8 ForcedPriority;
+
+
+ /* Bookkeeping structures */
+ struct net_device_stats stats;
+ struct rtllib_stats ieee_stats;
+ struct rtllib_softmac_stats softmac_stats;
+
+ /* Probe / Beacon management */
+ struct list_head network_free_list;
+ struct list_head network_list;
+ struct rtllib_network *networks;
+ int scans;
+ int scan_age;
+
+ int iw_mode; /* operating mode (IW_MODE_*) */
+ bool bNetPromiscuousMode;
+ struct rt_intel_promisc_mode IntelPromiscuousModeInfo;
+
+ struct iw_spy_data spy_data;
+
+ spinlock_t lock;
+ spinlock_t wpax_suitlist_lock;
+
+ int tx_headroom; /* Set to size of any additional room needed at front
+ * of allocated Tx SKBs
+ */
+ u32 config;
+
+ /* WEP and other encryption related settings at the device level */
+ int open_wep; /* Set to 1 to allow unencrypted frames */
+ int auth_mode;
+ int reset_on_keychange; /* Set to 1 if the HW needs to be reset on
+ * WEP key changes
+ */
+
+ /* If the host performs {en,de}cryption, then set to 1 */
+ int host_encrypt;
+ int host_encrypt_msdu;
+ int host_decrypt;
+ /* host performs multicast decryption */
+ int host_mc_decrypt;
+
+ /* host should strip IV and ICV from protected frames */
+ /* meaningful only when hardware decryption is being used */
+ int host_strip_iv_icv;
+
+ int host_open_frag;
+ int host_build_iv;
+ int ieee802_1x; /* is IEEE 802.1X used */
+
+ /* WPA data */
+ bool bHalfNMode;
+ bool bHalfWirelessN24GMode;
+ int wpa_enabled;
+ int drop_unencrypted;
+ int tkip_countermeasures;
+ int privacy_invoked;
+ size_t wpa_ie_len;
+ u8 *wpa_ie;
+ size_t wps_ie_len;
+ u8 *wps_ie;
+ u8 ap_mac_addr[6];
+ u16 pairwise_key_type;
+ u16 group_key_type;
+
+ struct lib80211_crypt_info crypt_info;
+
+ struct sw_cam_table swcamtable[TOTAL_CAM_ENTRY];
+ int bcrx_sta_key; /* use individual keys to override default keys even
+ * with RX of broad/multicast frames */
+
+ struct rt_pmkid_list PMKIDList[NUM_PMKID_CACHE];
+
+ /* Fragmentation structures */
+ struct rtllib_frag_entry frag_cache[17][RTLLIB_FRAG_CACHE_LEN];
+ unsigned int frag_next_idx[17];
+ u16 fts; /* Fragmentation Threshold */
+#define DEFAULT_RTS_THRESHOLD 2346U
+#define MIN_RTS_THRESHOLD 1
+#define MAX_RTS_THRESHOLD 2346U
+ u16 rts; /* RTS threshold */
+
+ /* Association info */
+ u8 bssid[ETH_ALEN];
+
+ /* This stores infos for the current network.
+ * Either the network we are associated in INFRASTRUCTURE
+ * or the network that we are creating in MASTER mode.
+ * ad-hoc is a mixture ;-).
+ * Note that in infrastructure mode, even when not associated,
+ * fields bssid and essid may be valid (if wpa_set and essid_set
+ * are true) as thy carry the value set by the user via iwconfig
+ */
+ struct rtllib_network current_network;
+
+ enum rtllib_state state;
+
+ int short_slot;
+ int reg_mode;
+ int mode; /* A, B, G */
+ int modulation; /* CCK, OFDM */
+ int freq_band; /* 2.4Ghz, 5.2Ghz, Mixed */
+ int abg_true; /* ABG flag */
+
+ /* used for forcing the ibss workqueue to terminate
+ * without wait for the syncro scan to terminate
+ */
+ short sync_scan_hurryup;
+ u16 scan_watch_dog;
+ int perfect_rssi;
+ int worst_rssi;
+
+ u16 prev_seq_ctl; /* used to drop duplicate frames */
+
+ /* map of allowed channels. 0 is dummy */
+ void *pDot11dInfo;
+ bool bGlobalDomain;
+ u8 active_channel_map[MAX_CHANNEL_NUMBER+1];
+
+ u8 IbssStartChnl;
+ u8 ibss_maxjoin_chal;
+
+ int rate; /* current rate */
+ int basic_rate;
+ u32 currentRate;
+
+ short active_scan;
+
+ /* this contains flags for selectively enable softmac support */
+ u16 softmac_features;
+
+ /* if the sequence control field is not filled by HW */
+ u16 seq_ctrl[5];
+
+ /* association procedure transaction sequence number */
+ u16 associate_seq;
+
+ /* AID for RTXed association responses */
+ u16 assoc_id;
+
+ /* power save mode related*/
+ u8 ack_tx_to_ieee;
+ short ps;
+ short sta_sleep;
+ int ps_timeout;
+ int ps_period;
+ struct tasklet_struct ps_task;
+ u64 ps_time;
+ bool polling;
+
+ short raw_tx;
+ /* used if IEEE_SOFTMAC_TX_QUEUE is set */
+ short queue_stop;
+ short scanning_continue;
+ short proto_started;
+ short proto_stoppping;
+
+ struct semaphore wx_sem;
+ struct semaphore scan_sem;
+ struct semaphore ips_sem;
+
+ spinlock_t mgmt_tx_lock;
+ spinlock_t beacon_lock;
+
+ short beacon_txing;
+
+ short wap_set;
+ short ssid_set;
+
+ /* set on initialization */
+ u8 qos_support;
+ unsigned int wmm_acm;
+
+ /* for discarding duplicated packets in IBSS */
+ struct list_head ibss_mac_hash[IEEE_IBSS_MAC_HASH_SIZE];
+
+ /* for discarding duplicated packets in BSS */
+ u16 last_rxseq_num[17]; /* rx seq previous per-tid */
+ u16 last_rxfrag_num[17];/* tx frag previous per-tid */
+ unsigned long last_packet_time[17];
+
+ /* for PS mode */
+ unsigned long last_rx_ps_time;
+ bool bAwakePktSent;
+ u8 LPSDelayCnt;
+
+ /* used if IEEE_SOFTMAC_SINGLE_QUEUE is set */
+ struct sk_buff *mgmt_queue_ring[MGMT_QUEUE_NUM];
+ int mgmt_queue_head;
+ int mgmt_queue_tail;
+#define RTLLIB_QUEUE_LIMIT 128
+ u8 AsocRetryCount;
+ unsigned int hw_header;
+ struct sk_buff_head skb_waitQ[MAX_QUEUE_SIZE];
+ struct sk_buff_head skb_aggQ[MAX_QUEUE_SIZE];
+ struct sk_buff_head skb_drv_aggQ[MAX_QUEUE_SIZE];
+ u32 sta_edca_param[4];
+ bool aggregation;
+ bool enable_rx_imm_BA;
+ bool bibsscoordinator;
+
+ bool bdynamic_txpower_enable;
+
+ bool bCTSToSelfEnable;
+ u8 CTSToSelfTH;
+
+ u32 fsync_time_interval;
+ u32 fsync_rate_bitmap;
+ u8 fsync_rssi_threshold;
+ bool bfsync_enable;
+
+ u8 fsync_multiple_timeinterval;
+ u32 fsync_firstdiff_ratethreshold;
+ u32 fsync_seconddiff_ratethreshold;
+ enum fsync_state fsync_state;
+ bool bis_any_nonbepkts;
+ struct bandwidth_autoswitch bandwidth_auto_switch;
+ bool FwRWRF;
+
+ struct rt_link_detect LinkDetectInfo;
+ bool bIsAggregateFrame;
+ struct rt_pwr_save_ctrl PowerSaveControl;
+ u8 amsdu_in_process;
+
+ /* used if IEEE_SOFTMAC_TX_QUEUE is set */
+ struct tx_pending tx_pending;
+
+ /* used if IEEE_SOFTMAC_ASSOCIATE is set */
+ struct timer_list associate_timer;
+
+ /* used if IEEE_SOFTMAC_BEACONS is set */
+ struct timer_list beacon_timer;
+ u8 need_sw_enc;
+ struct work_struct associate_complete_wq;
+ struct work_struct ips_leave_wq;
+ struct delayed_work associate_procedure_wq;
+ struct delayed_work softmac_scan_wq;
+ struct delayed_work softmac_hint11d_wq;
+ struct delayed_work associate_retry_wq;
+ struct delayed_work start_ibss_wq;
+ struct delayed_work hw_wakeup_wq;
+ struct delayed_work hw_sleep_wq;
+ struct delayed_work link_change_wq;
+ struct work_struct wx_sync_scan_wq;
+
+ struct workqueue_struct *wq;
+ union {
+ struct rtllib_rxb *RfdArray[REORDER_WIN_SIZE];
+ struct rtllib_rxb *stats_IndicateArray[REORDER_WIN_SIZE];
+ struct rtllib_rxb *prxbIndicateArray[REORDER_WIN_SIZE];
+ struct {
+ struct sw_chnl_cmd PreCommonCmd[MAX_PRECMD_CNT];
+ struct sw_chnl_cmd PostCommonCmd[MAX_POSTCMD_CNT];
+ struct sw_chnl_cmd RfDependCmd[MAX_RFDEPENDCMD_CNT];
+ };
+ };
+
+ /* Callback functions */
+ void (*set_security)(struct net_device *dev,
+ struct rtllib_security *sec);
+
+ /* Used to TX data frame by using txb structs.
+ * this is not used if in the softmac_features
+ * is set the flag IEEE_SOFTMAC_TX_QUEUE
+ */
+ int (*hard_start_xmit)(struct rtllib_txb *txb,
+ struct net_device *dev);
+
+ int (*reset_port)(struct net_device *dev);
+ int (*is_queue_full)(struct net_device *dev, int pri);
+
+ int (*handle_management)(struct net_device *dev,
+ struct rtllib_network *network, u16 type);
+ int (*is_qos_active)(struct net_device *dev, struct sk_buff *skb);
+
+ /* Softmac-generated frames (management) are TXed via this
+ * callback if the flag IEEE_SOFTMAC_SINGLE_QUEUE is
+ * not set. As some cards may have different HW queues that
+ * one might want to use for data and management frames
+ * the option to have two callbacks might be useful.
+ * This function can't sleep.
+ */
+ int (*softmac_hard_start_xmit)(struct sk_buff *skb,
+ struct net_device *dev);
+
+ /* used instead of hard_start_xmit (not softmac_hard_start_xmit)
+ * if the IEEE_SOFTMAC_TX_QUEUE feature is used to TX data
+ * frames. If the option IEEE_SOFTMAC_SINGLE_QUEUE is also set
+ * then also management frames are sent via this callback.
+ * This function can't sleep.
+ */
+ void (*softmac_data_hard_start_xmit)(struct sk_buff *skb,
+ struct net_device *dev, int rate);
+
+ /* stops the HW queue for DATA frames. Useful to avoid
+ * waste time to TX data frame when we are reassociating
+ * This function can sleep.
+ */
+ void (*data_hard_stop)(struct net_device *dev);
+
+ /* OK this is complementing to data_poll_hard_stop */
+ void (*data_hard_resume)(struct net_device *dev);
+
+ /* ask to the driver to retune the radio.
+ * This function can sleep. the driver should ensure
+ * the radio has been switched before return.
+ */
+ void (*set_chan)(struct net_device *dev, short ch);
+
+ /* These are not used if the ieee stack takes care of
+ * scanning (IEEE_SOFTMAC_SCAN feature set).
+ * In this case only the set_chan is used.
+ *
+ * The syncro version is similar to the start_scan but
+ * does not return until all channels has been scanned.
+ * this is called in user context and should sleep,
+ * it is called in a work_queue when switching to ad-hoc mode
+ * or in behalf of iwlist scan when the card is associated
+ * and root user ask for a scan.
+ * the function stop_scan should stop both the syncro and
+ * background scanning and can sleep.
+ * The function start_scan should initiate the background
+ * scanning and can't sleep.
+ */
+ void (*scan_syncro)(struct net_device *dev);
+ void (*start_scan)(struct net_device *dev);
+ void (*stop_scan)(struct net_device *dev);
+
+ void (*rtllib_start_hw_scan)(struct net_device *dev);
+ void (*rtllib_stop_hw_scan)(struct net_device *dev);
+
+ /* indicate the driver that the link state is changed
+ * for example it may indicate the card is associated now.
+ * Driver might be interested in this to apply RX filter
+ * rules or simply light the LINK led
+ */
+ void (*link_change)(struct net_device *dev);
+
+ /* these two function indicates to the HW when to start
+ * and stop to send beacons. This is used when the
+ * IEEE_SOFTMAC_BEACONS is not set. For now the
+ * stop_send_bacons is NOT guaranteed to be called only
+ * after start_send_beacons.
+ */
+ void (*start_send_beacons)(struct net_device *dev);
+ void (*stop_send_beacons)(struct net_device *dev);
+
+ /* power save mode related */
+ void (*sta_wake_up)(struct net_device *dev);
+ void (*enter_sleep_state)(struct net_device *dev, u64 time);
+ short (*ps_is_queue_empty)(struct net_device *dev);
+ int (*handle_beacon)(struct net_device *dev,
+ struct rtllib_beacon *beacon,
+ struct rtllib_network *network);
+ int (*handle_assoc_response)(struct net_device *dev,
+ struct rtllib_assoc_response_frame *resp,
+ struct rtllib_network *network);
+
+
+ /* check whether Tx hw resource available */
+ short (*check_nic_enough_desc)(struct net_device *dev, int queue_index);
+ short (*get_nic_desc_num)(struct net_device *dev, int queue_index);
+ void (*SetBWModeHandler)(struct net_device *dev,
+ enum ht_channel_width Bandwidth,
+ enum ht_extchnl_offset Offset);
+ bool (*GetNmodeSupportBySecCfg)(struct net_device *dev);
+ void (*SetWirelessMode)(struct net_device *dev, u8 wireless_mode);
+ bool (*GetHalfNmodeSupportByAPsHandler)(struct net_device *dev);
+ u8 (*rtllib_ap_sec_type)(struct rtllib_device *ieee);
+ void (*HalUsbRxAggrHandler)(struct net_device *dev, bool Value);
+ void (*InitialGainHandler)(struct net_device *dev, u8 Operation);
+ bool (*SetFwCmdHandler)(struct net_device *dev,
+ enum fw_cmd_io_type FwCmdIO);
+ void (*UpdateHalRAMaskHandler)(struct net_device *dev, bool bMulticast,
+ u8 macId, u8 MimoPs, u8 WirelessMode,
+ u8 bCurTxBW40MHz, u8 rssi_level);
+ void (*UpdateBeaconInterruptHandler)(struct net_device *dev,
+ bool start);
+ void (*UpdateInterruptMaskHandler)(struct net_device *dev, u32 AddMSR,
+ u32 RemoveMSR);
+ u16 (*rtl_11n_user_show_rates)(struct net_device *dev);
+ void (*ScanOperationBackupHandler)(struct net_device *dev,
+ u8 Operation);
+ void (*LedControlHandler)(struct net_device *dev,
+ enum led_ctl_mode LedAction);
+ void (*SetHwRegHandler)(struct net_device *dev, u8 variable, u8 *val);
+ void (*GetHwRegHandler)(struct net_device *dev, u8 variable, u8 *val);
+
+ void (*AllowAllDestAddrHandler)(struct net_device *dev,
+ bool bAllowAllDA, bool WriteIntoReg);
+
+ void (*rtllib_ips_leave_wq)(struct net_device *dev);
+ void (*rtllib_ips_leave)(struct net_device *dev);
+ void (*LeisurePSLeave)(struct net_device *dev);
+ void (*rtllib_rfkill_poll)(struct net_device *dev);
+
+ /* This must be the last item so that it points to the data
+ * allocated beyond this structure by alloc_rtllib
+ */
+ u8 priv[0];
+};
+
+#define IEEE_A (1<<0)
+#define IEEE_B (1<<1)
+#define IEEE_G (1<<2)
+#define IEEE_N_24G (1<<4)
+#define IEEE_N_5G (1<<5)
+#define IEEE_MODE_MASK (IEEE_A|IEEE_B|IEEE_G)
+
+/* Generate a 802.11 header */
+
+/* Uses the channel change callback directly
+ * instead of [start/stop] scan callbacks
+ */
+#define IEEE_SOFTMAC_SCAN (1<<2)
+
+/* Perform authentication and association handshake */
+#define IEEE_SOFTMAC_ASSOCIATE (1<<3)
+
+/* Generate probe requests */
+#define IEEE_SOFTMAC_PROBERQ (1<<4)
+
+/* Generate response to probe requests */
+#define IEEE_SOFTMAC_PROBERS (1<<5)
+
+/* The ieee802.11 stack will manage the netif queue
+ * wake/stop for the driver, taking care of 802.11
+ * fragmentation. See softmac.c for details.
+ */
+#define IEEE_SOFTMAC_TX_QUEUE (1<<7)
+
+/* Uses only the softmac_data_hard_start_xmit
+ * even for TX management frames.
+ */
+#define IEEE_SOFTMAC_SINGLE_QUEUE (1<<8)
+
+/* Generate beacons. The stack will enqueue beacons
+ * to the card
+ */
+#define IEEE_SOFTMAC_BEACONS (1<<6)
+
+
+static inline void *rtllib_priv(struct net_device *dev)
+{
+ return ((struct rtllib_device *)netdev_priv(dev))->priv;
+}
+
+static inline int rtllib_is_empty_essid(const char *essid, int essid_len)
+{
+ /* Single white space is for Linksys APs */
+ if (essid_len == 1 && essid[0] == ' ')
+ return 1;
+
+ /* Otherwise, if the entire essid is 0, we assume it is hidden */
+ while (essid_len) {
+ essid_len--;
+ if (essid[essid_len] != '\0')
+ return 0;
+ }
+
+ return 1;
+}
+
+static inline int rtllib_is_valid_mode(struct rtllib_device *ieee, int mode)
+{
+ /* It is possible for both access points and our device to support
+ * combinations of modes, so as long as there is one valid combination
+ * of ap/device supported modes, then return success
+ */
+ if ((mode & IEEE_A) &&
+ (ieee->modulation & RTLLIB_OFDM_MODULATION) &&
+ (ieee->freq_band & RTLLIB_52GHZ_BAND))
+ return 1;
+
+ if ((mode & IEEE_G) &&
+ (ieee->modulation & RTLLIB_OFDM_MODULATION) &&
+ (ieee->freq_band & RTLLIB_24GHZ_BAND))
+ return 1;
+
+ if ((mode & IEEE_B) &&
+ (ieee->modulation & RTLLIB_CCK_MODULATION) &&
+ (ieee->freq_band & RTLLIB_24GHZ_BAND))
+ return 1;
+
+ return 0;
+}
+
+static inline int rtllib_get_hdrlen(u16 fc)
+{
+ int hdrlen = RTLLIB_3ADDR_LEN;
+
+ switch (WLAN_FC_GET_TYPE(fc)) {
+ case RTLLIB_FTYPE_DATA:
+ if ((fc & RTLLIB_FCTL_FROMDS) && (fc & RTLLIB_FCTL_TODS))
+ hdrlen = RTLLIB_4ADDR_LEN; /* Addr4 */
+ if (RTLLIB_QOS_HAS_SEQ(fc))
+ hdrlen += 2; /* QOS ctrl*/
+ break;
+ case RTLLIB_FTYPE_CTL:
+ switch (WLAN_FC_GET_STYPE(fc)) {
+ case RTLLIB_STYPE_CTS:
+ case RTLLIB_STYPE_ACK:
+ hdrlen = RTLLIB_1ADDR_LEN;
+ break;
+ default:
+ hdrlen = RTLLIB_2ADDR_LEN;
+ break;
+ }
+ break;
+ }
+
+ return hdrlen;
+}
+
+static inline u8 *rtllib_get_payload(struct rtllib_hdr *hdr)
+{
+ switch (rtllib_get_hdrlen(le16_to_cpu(hdr->frame_ctl))) {
+ case RTLLIB_1ADDR_LEN:
+ return ((struct rtllib_hdr_1addr *)hdr)->payload;
+ case RTLLIB_2ADDR_LEN:
+ return ((struct rtllib_hdr_2addr *)hdr)->payload;
+ case RTLLIB_3ADDR_LEN:
+ return ((struct rtllib_hdr_3addr *)hdr)->payload;
+ case RTLLIB_4ADDR_LEN:
+ return ((struct rtllib_hdr_4addr *)hdr)->payload;
+ }
+ return NULL;
+}
+
+static inline int rtllib_is_ofdm_rate(u8 rate)
+{
+ switch (rate & ~RTLLIB_BASIC_RATE_MASK) {
+ case RTLLIB_OFDM_RATE_6MB:
+ case RTLLIB_OFDM_RATE_9MB:
+ case RTLLIB_OFDM_RATE_12MB:
+ case RTLLIB_OFDM_RATE_18MB:
+ case RTLLIB_OFDM_RATE_24MB:
+ case RTLLIB_OFDM_RATE_36MB:
+ case RTLLIB_OFDM_RATE_48MB:
+ case RTLLIB_OFDM_RATE_54MB:
+ return 1;
+ }
+ return 0;
+}
+
+static inline int rtllib_is_cck_rate(u8 rate)
+{
+ switch (rate & ~RTLLIB_BASIC_RATE_MASK) {
+ case RTLLIB_CCK_RATE_1MB:
+ case RTLLIB_CCK_RATE_2MB:
+ case RTLLIB_CCK_RATE_5MB:
+ case RTLLIB_CCK_RATE_11MB:
+ return 1;
+ }
+ return 0;
+}
+
+
+/* rtllib.c */
+extern void free_rtllib(struct net_device *dev);
+extern struct net_device *alloc_rtllib(int sizeof_priv);
+
+extern int rtllib_set_encryption(struct rtllib_device *ieee);
+
+/* rtllib_tx.c */
+
+extern int rtllib_encrypt_fragment(
+ struct rtllib_device *ieee,
+ struct sk_buff *frag,
+ int hdr_len);
+
+extern int rtllib_xmit(struct sk_buff *skb, struct net_device *dev);
+extern int rtllib_xmit_inter(struct sk_buff *skb, struct net_device *dev);
+extern void rtllib_txb_free(struct rtllib_txb *);
+
+/* rtllib_rx.c */
+extern int rtllib_rx(struct rtllib_device *ieee, struct sk_buff *skb,
+ struct rtllib_rx_stats *rx_stats);
+extern void rtllib_rx_mgt(struct rtllib_device *ieee,
+ struct sk_buff *skb,
+ struct rtllib_rx_stats *stats);
+extern void rtllib_rx_probe_rq(struct rtllib_device *ieee,
+ struct sk_buff *skb);
+extern int rtllib_legal_channel(struct rtllib_device *rtllib, u8 channel);
+
+/* rtllib_wx.c */
+extern int rtllib_wx_get_scan(struct rtllib_device *ieee,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *key);
+extern int rtllib_wx_set_encode(struct rtllib_device *ieee,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *key);
+extern int rtllib_wx_get_encode(struct rtllib_device *ieee,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *key);
+#if WIRELESS_EXT >= 18
+extern int rtllib_wx_get_encode_ext(struct rtllib_device *ieee,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra);
+extern int rtllib_wx_set_encode_ext(struct rtllib_device *ieee,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra);
+#endif
+extern int rtllib_wx_set_auth(struct rtllib_device *ieee,
+ struct iw_request_info *info,
+ struct iw_param *data, char *extra);
+extern int rtllib_wx_set_mlme(struct rtllib_device *ieee,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra);
+extern int rtllib_wx_set_gen_ie(struct rtllib_device *ieee, u8 *ie, size_t len);
+
+/* rtllib_softmac.c */
+extern short rtllib_is_54g(struct rtllib_network *net);
+extern short rtllib_is_shortslot(const struct rtllib_network *net);
+extern int rtllib_rx_frame_softmac(struct rtllib_device *ieee,
+ struct sk_buff *skb,
+ struct rtllib_rx_stats *rx_stats, u16 type,
+ u16 stype);
+extern void rtllib_softmac_new_net(struct rtllib_device *ieee,
+ struct rtllib_network *net);
+
+void SendDisassociation(struct rtllib_device *ieee, bool deauth, u16 asRsn);
+extern void rtllib_softmac_xmit(struct rtllib_txb *txb,
+ struct rtllib_device *ieee);
+
+extern void rtllib_stop_send_beacons(struct rtllib_device *ieee);
+extern void notify_wx_assoc_event(struct rtllib_device *ieee);
+extern void rtllib_softmac_check_all_nets(struct rtllib_device *ieee);
+extern void rtllib_start_bss(struct rtllib_device *ieee);
+extern void rtllib_start_master_bss(struct rtllib_device *ieee);
+extern void rtllib_start_ibss(struct rtllib_device *ieee);
+extern void rtllib_softmac_init(struct rtllib_device *ieee);
+extern void rtllib_softmac_free(struct rtllib_device *ieee);
+extern void rtllib_associate_abort(struct rtllib_device *ieee);
+extern void rtllib_disassociate(struct rtllib_device *ieee);
+extern void rtllib_stop_scan(struct rtllib_device *ieee);
+extern bool rtllib_act_scanning(struct rtllib_device *ieee, bool sync_scan);
+extern void rtllib_stop_scan_syncro(struct rtllib_device *ieee);
+extern void rtllib_start_scan_syncro(struct rtllib_device *ieee, u8 is_mesh);
+extern void rtllib_sta_ps_send_null_frame(struct rtllib_device *ieee,
+ short pwr);
+extern void rtllib_sta_wakeup(struct rtllib_device *ieee, short nl);
+extern void rtllib_sta_ps_send_pspoll_frame(struct rtllib_device *ieee);
+extern void rtllib_check_all_nets(struct rtllib_device *ieee);
+extern void rtllib_start_protocol(struct rtllib_device *ieee);
+extern void rtllib_stop_protocol(struct rtllib_device *ieee, u8 shutdown);
+
+extern void rtllib_EnableNetMonitorMode(struct net_device *dev,
+ bool bInitState);
+extern void rtllib_DisableNetMonitorMode(struct net_device *dev,
+ bool bInitState);
+extern void rtllib_EnableIntelPromiscuousMode(struct net_device *dev,
+ bool bInitState);
+extern void rtllib_DisableIntelPromiscuousMode(struct net_device *dev,
+ bool bInitState);
+extern void rtllib_send_probe_requests(struct rtllib_device *ieee, u8 is_mesh);
+
+extern void rtllib_softmac_stop_protocol(struct rtllib_device *ieee,
+ u8 mesh_flag, u8 shutdown);
+extern void rtllib_softmac_start_protocol(struct rtllib_device *ieee,
+ u8 mesh_flag);
+
+extern void rtllib_reset_queue(struct rtllib_device *ieee);
+extern void rtllib_wake_queue(struct rtllib_device *ieee);
+extern void rtllib_stop_queue(struct rtllib_device *ieee);
+extern void rtllib_wake_all_queues(struct rtllib_device *ieee);
+extern void rtllib_stop_all_queues(struct rtllib_device *ieee);
+extern struct sk_buff *rtllib_get_beacon(struct rtllib_device *ieee);
+extern void rtllib_start_send_beacons(struct rtllib_device *ieee);
+extern void rtllib_stop_send_beacons(struct rtllib_device *ieee);
+extern int rtllib_wpa_supplicant_ioctl(struct rtllib_device *ieee,
+ struct iw_point *p, u8 is_mesh);
+
+extern void notify_wx_assoc_event(struct rtllib_device *ieee);
+extern void rtllib_ps_tx_ack(struct rtllib_device *ieee, short success);
+
+extern void softmac_mgmt_xmit(struct sk_buff *skb,
+ struct rtllib_device *ieee);
+extern u16 rtllib_query_seqnum(struct rtllib_device *ieee,
+ struct sk_buff *skb, u8 *dst);
+extern u8 rtllib_ap_sec_type(struct rtllib_device *ieee);
+
+/* rtllib_crypt_ccmp&tkip&wep.c */
+extern void rtllib_tkip_null(void);
+extern void rtllib_wep_null(void);
+extern void rtllib_ccmp_null(void);
+
+/* rtllib_softmac_wx.c */
+
+extern int rtllib_wx_get_wap(struct rtllib_device *ieee,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *ext);
+
+extern int rtllib_wx_set_wap(struct rtllib_device *ieee,
+ struct iw_request_info *info,
+ union iwreq_data *awrq,
+ char *extra);
+
+extern int rtllib_wx_get_essid(struct rtllib_device *ieee,
+ struct iw_request_info *a,
+ union iwreq_data *wrqu, char *b);
+
+extern int rtllib_wx_set_rate(struct rtllib_device *ieee,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra);
+
+extern int rtllib_wx_get_rate(struct rtllib_device *ieee,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra);
+
+extern int rtllib_wx_set_mode(struct rtllib_device *ieee,
+ struct iw_request_info *a,
+ union iwreq_data *wrqu, char *b);
+
+extern int rtllib_wx_set_scan(struct rtllib_device *ieee,
+ struct iw_request_info *a,
+ union iwreq_data *wrqu, char *b);
+
+extern int rtllib_wx_set_essid(struct rtllib_device *ieee,
+ struct iw_request_info *a,
+ union iwreq_data *wrqu, char *extra);
+
+extern int rtllib_wx_get_mode(struct rtllib_device *ieee,
+ struct iw_request_info *a,
+ union iwreq_data *wrqu, char *b);
+
+extern int rtllib_wx_set_freq(struct rtllib_device *ieee,
+ struct iw_request_info *a,
+ union iwreq_data *wrqu, char *b);
+
+extern int rtllib_wx_get_freq(struct rtllib_device *ieee,
+ struct iw_request_info *a,
+ union iwreq_data *wrqu, char *b);
+extern void rtllib_wx_sync_scan_wq(void *data);
+
+extern int rtllib_wx_set_rawtx(struct rtllib_device *ieee,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra);
+
+extern int rtllib_wx_get_name(struct rtllib_device *ieee,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra);
+
+extern int rtllib_wx_set_power(struct rtllib_device *ieee,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra);
+
+extern int rtllib_wx_get_power(struct rtllib_device *ieee,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra);
+
+extern int rtllib_wx_set_rts(struct rtllib_device *ieee,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra);
+
+extern int rtllib_wx_get_rts(struct rtllib_device *ieee,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra);
+#define MAX_RECEIVE_BUFFER_SIZE 9100
+
+void HTSetConnectBwMode(struct rtllib_device *ieee,
+ enum ht_channel_width Bandwidth,
+ enum ht_extchnl_offset Offset);
+extern void HTUpdateDefaultSetting(struct rtllib_device *ieee);
+extern void HTConstructCapabilityElement(struct rtllib_device *ieee,
+ u8 *posHTCap, u8 *len,
+ u8 isEncrypt, bool bAssoc);
+extern void HTConstructInfoElement(struct rtllib_device *ieee,
+ u8 *posHTInfo, u8 *len, u8 isEncrypt);
+extern void HTConstructRT2RTAggElement(struct rtllib_device *ieee,
+ u8 *posRT2RTAgg, u8 *len);
+extern void HTOnAssocRsp(struct rtllib_device *ieee);
+extern void HTInitializeHTInfo(struct rtllib_device *ieee);
+extern void HTInitializeBssDesc(struct bss_ht *pBssHT);
+extern void HTResetSelfAndSavePeerSetting(struct rtllib_device *ieee,
+ struct rtllib_network *pNetwork);
+extern void HT_update_self_and_peer_setting(struct rtllib_device *ieee,
+ struct rtllib_network *pNetwork);
+extern u8 HTGetHighestMCSRate(struct rtllib_device *ieee, u8 *pMCSRateSet,
+ u8 *pMCSFilter);
+extern u8 MCS_FILTER_ALL[];
+extern u16 MCS_DATA_RATE[2][2][77];
+extern u8 HTCCheck(struct rtllib_device *ieee, u8 *pFrame);
+extern void HTResetIOTSetting(struct rt_hi_throughput *pHTInfo);
+extern bool IsHTHalfNmodeAPs(struct rtllib_device *ieee);
+extern u16 HTMcsToDataRate(struct rtllib_device *ieee, u8 nMcsRate);
+extern u16 TxCountToDataRate(struct rtllib_device *ieee, u8 nDataRate);
+extern int rtllib_rx_ADDBAReq(struct rtllib_device *ieee, struct sk_buff *skb);
+extern int rtllib_rx_ADDBARsp(struct rtllib_device *ieee, struct sk_buff *skb);
+extern int rtllib_rx_DELBA(struct rtllib_device *ieee, struct sk_buff *skb);
+extern void TsInitAddBA(struct rtllib_device *ieee, struct tx_ts_record *pTS,
+ u8 Policy, u8 bOverwritePending);
+extern void TsInitDelBA(struct rtllib_device *ieee,
+ struct ts_common_info *pTsCommonInfo,
+ enum tr_select TxRxSelect);
+extern void BaSetupTimeOut(unsigned long data);
+extern void TxBaInactTimeout(unsigned long data);
+extern void RxBaInactTimeout(unsigned long data);
+extern void ResetBaEntry(struct ba_record *pBA);
+extern bool GetTs(
+ struct rtllib_device *ieee,
+ struct ts_common_info **ppTS,
+ u8 *Addr,
+ u8 TID,
+ enum tr_select TxRxSelect,
+ bool bAddNewTs
+);
+extern void TSInitialize(struct rtllib_device *ieee);
+extern void TsStartAddBaProcess(struct rtllib_device *ieee,
+ struct tx_ts_record *pTxTS);
+extern void RemovePeerTS(struct rtllib_device *ieee, u8 *Addr);
+extern void RemoveAllTS(struct rtllib_device *ieee);
+void rtllib_softmac_scan_syncro(struct rtllib_device *ieee, u8 is_mesh);
+
+extern const long rtllib_wlan_frequencies[];
+
+static inline void rtllib_increment_scans(struct rtllib_device *ieee)
+{
+ ieee->scans++;
+}
+
+static inline int rtllib_get_scans(struct rtllib_device *ieee)
+{
+ return ieee->scans;
+}
+
+static inline const char *escape_essid(const char *essid, u8 essid_len)
+{
+ static char escaped[IW_ESSID_MAX_SIZE * 2 + 1];
+
+ if (rtllib_is_empty_essid(essid, essid_len)) {
+ memcpy(escaped, "<hidden>", sizeof("<hidden>"));
+ return escaped;
+ }
+
+ snprintf(escaped, sizeof(escaped), "%*pEn", essid_len, essid);
+ return escaped;
+}
+
+#define CONVERT_RATE(_ieee, _MGN_RATE) \
+ ((_MGN_RATE < MGN_MCS0) ? (_MGN_RATE) : \
+ (HTMcsToDataRate(_ieee, (u8)_MGN_RATE)))
+
+/* fun with the built-in rtllib stack... */
+bool rtllib_MgntDisconnect(struct rtllib_device *rtllib, u8 asRsn);
+
+
+/* For the function is more related to hardware setting, it's better to use the
+ * ieee handler to refer to it.
+ */
+extern void rtllib_update_active_chan_map(struct rtllib_device *ieee);
+extern void rtllib_FlushRxTsPendingPkts(struct rtllib_device *ieee,
+ struct rx_ts_record *pTS);
+extern int rtllib_data_xmit(struct sk_buff *skb, struct net_device *dev);
+extern int rtllib_parse_info_param(struct rtllib_device *ieee,
+ struct rtllib_info_element *info_element,
+ u16 length,
+ struct rtllib_network *network,
+ struct rtllib_rx_stats *stats);
+
+void rtllib_indicate_packets(struct rtllib_device *ieee,
+ struct rtllib_rxb **prxbIndicateArray, u8 index);
+extern u8 HTFilterMCSRate(struct rtllib_device *ieee, u8 *pSupportMCS,
+ u8 *pOperateMCS);
+extern void HTUseDefaultSetting(struct rtllib_device *ieee);
+#define RT_ASOC_RETRY_LIMIT 5
+u8 MgntQuery_TxRateExcludeCCKRates(struct rtllib_device *ieee);
+extern void rtllib_TURBO_Info(struct rtllib_device *ieee, u8 **tag_p);
+#ifndef ENABLE_LOCK_DEBUG
+#define SPIN_LOCK_IEEE(plock) spin_lock_irqsave((plock), flags)
+#define SPIN_UNLOCK_IEEE(plock) spin_unlock_irqrestore((plock), flags)
+#define SPIN_LOCK_IEEE_REORDER(plock) spin_lock_irqsave((plock), flags)
+#define SPIN_UNLOCK_IEEE_REORDER(plock) spin_unlock_irqrestore((plock), flags)
+#define SPIN_LOCK_IEEE_WPAX(plock) spin_lock_irqsave((plock), flags)
+#define SPIN_UNLOCK_IEEE_WPAX(plock) spin_unlock_irqrestore((plock), flags)
+#define SPIN_LOCK_IEEE_MGNTTX(plock) spin_lock_irqsave((plock), flags)
+#define SPIN_UNLOCK_IEEE_MGNTTX(plock) spin_unlock_irqrestore((plock), flags)
+#define SPIN_LOCK_IEEE_BCN(plock) spin_lock_irqsave((plock), flags)
+#define SPIN_UNLOCK_IEEE_BCN(plock) spin_unlock_irqrestore((plock), flags)
+#define SPIN_LOCK_MSH_STAINFO(plock) spin_lock_irqsave((plock), flags)
+#define SPIN_UNLOCK_MSH_STAINFO(plock) spin_unlock_irqrestore((plock), flags)
+#define SPIN_LOCK_MSH_PREQ(plock) spin_lock_irqsave((plock), flags)
+#define SPIN_UNLOCK_MSH_PREQ(plock) spin_unlock_irqrestore((plock), flags)
+#define SPIN_LOCK_MSH_QUEUE(plock) spin_lock_irqsave((plock), flags)
+#define SPIN_UNLOCK_MSH_QUEUE(plock) spin_unlock_irqrestore((plock), flags)
+#define SPIN_LOCK_PRIV_RFPS(plock) spin_lock_irqsave((plock), flags)
+#define SPIN_UNLOCK_PRIV_RFPS(plock) spin_unlock_irqrestore((plock), flags)
+#define SPIN_LOCK_PRIV_IRQTH(plock) spin_lock_irqsave((plock), flags)
+#define SPIN_UNLOCK_PRIV_IRQTH(plock) spin_unlock_irqrestore((plock), flags)
+#define SPIN_LOCK_PRIV_TX(plock) spin_lock_irqsave((plock), flags)
+#define SPIN_UNLOCK_PRIV_TX(plock) spin_unlock_irqrestore((plock), flags)
+#define SPIN_LOCK_PRIV_D3(plock) spin_lock_irqsave((plock), flags)
+#define SPIN_UNLOCK_PRIV_D3(plock) spin_unlock_irqrestore((plock), flags)
+#define SPIN_LOCK_PRIV_RF(plock) spin_lock_irqsave((plock), flags)
+#define SPIN_UNLOCK_PRIV_RF(plock) spin_unlock_irqrestore((plock), flags)
+#define SPIN_LOCK_PRIV_PS(plock) spin_lock_irqsave((plock), flags)
+#define SPIN_UNLOCK_PRIV_PS(plock) spin_unlock_irqrestore((plock), flags)
+#define SEM_DOWN_IEEE_WX(psem) down(psem)
+#define SEM_UP_IEEE_WX(psem) up(psem)
+#define SEM_DOWN_IEEE_SCAN(psem) down(psem)
+#define SEM_UP_IEEE_SCAN(psem) up(psem)
+#define SEM_DOWN_IEEE_IPS(psem) down(psem)
+#define SEM_UP_IEEE_IPS(psem) up(psem)
+#define SEM_DOWN_PRIV_WX(psem) down(psem)
+#define SEM_UP_PRIV_WX(psem) up(psem)
+#define SEM_DOWN_PRIV_RF(psem) down(psem)
+#define SEM_UP_PRIV_RF(psem) up(psem)
+#define MUTEX_LOCK_PRIV(pmutex) mutex_lock(pmutex)
+#define MUTEX_UNLOCK_PRIV(pmutex) mutex_unlock(pmutex)
+#endif
+
+#endif /* RTLLIB_H */
diff --git a/drivers/staging/rtl8192e/rtllib_crypt.c b/drivers/staging/rtl8192e/rtllib_crypt.c
new file mode 100644
index 000000000..1e6ae9bea
--- /dev/null
+++ b/drivers/staging/rtl8192e/rtllib_crypt.c
@@ -0,0 +1,254 @@
+/*
+ * Host AP crypto routines
+ *
+ * Copyright (c) 2002-2003, Jouni Malinen <jkmaline@cc.hut.fi>
+ * Portions Copyright (C) 2004, Intel Corporation <jketreno@linux.intel.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation. See README and COPYING for
+ * more details.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+
+#include "rtllib.h"
+
+struct rtllib_crypto_alg {
+ struct list_head list;
+ struct lib80211_crypto_ops *ops;
+};
+
+
+struct rtllib_crypto {
+ struct list_head algs;
+ spinlock_t lock;
+};
+
+static struct rtllib_crypto *hcrypt;
+
+void rtllib_crypt_deinit_entries(struct lib80211_crypt_info *info,
+ int force)
+{
+ struct list_head *ptr, *n;
+ struct lib80211_crypt_data *entry;
+
+ for (ptr = info->crypt_deinit_list.next, n = ptr->next;
+ ptr != &info->crypt_deinit_list; ptr = n, n = ptr->next) {
+ entry = list_entry(ptr, struct lib80211_crypt_data, list);
+
+ if (atomic_read(&entry->refcnt) != 0 && !force)
+ continue;
+
+ list_del(ptr);
+
+ if (entry->ops)
+ entry->ops->deinit(entry->priv);
+ kfree(entry);
+ }
+}
+EXPORT_SYMBOL(rtllib_crypt_deinit_entries);
+
+void rtllib_crypt_deinit_handler(unsigned long data)
+{
+ struct lib80211_crypt_info *info = (struct lib80211_crypt_info *)data;
+ unsigned long flags;
+
+ spin_lock_irqsave(info->lock, flags);
+ rtllib_crypt_deinit_entries(info, 0);
+ if (!list_empty(&info->crypt_deinit_list)) {
+ printk(KERN_DEBUG
+ "%s: entries remaining in delayed crypt deletion list\n",
+ info->name);
+ info->crypt_deinit_timer.expires = jiffies + HZ;
+ add_timer(&info->crypt_deinit_timer);
+ }
+ spin_unlock_irqrestore(info->lock, flags);
+
+}
+EXPORT_SYMBOL(rtllib_crypt_deinit_handler);
+
+void rtllib_crypt_delayed_deinit(struct lib80211_crypt_info *info,
+ struct lib80211_crypt_data **crypt)
+{
+ struct lib80211_crypt_data *tmp;
+ unsigned long flags;
+
+ if (*crypt == NULL)
+ return;
+
+ tmp = *crypt;
+ *crypt = NULL;
+
+ /* must not run ops->deinit() while there may be pending encrypt or
+ * decrypt operations. Use a list of delayed deinits to avoid needing
+ * locking.
+ */
+
+ spin_lock_irqsave(info->lock, flags);
+ list_add(&tmp->list, &info->crypt_deinit_list);
+ if (!timer_pending(&info->crypt_deinit_timer)) {
+ info->crypt_deinit_timer.expires = jiffies + HZ;
+ add_timer(&info->crypt_deinit_timer);
+ }
+ spin_unlock_irqrestore(info->lock, flags);
+}
+EXPORT_SYMBOL(rtllib_crypt_delayed_deinit);
+
+int rtllib_register_crypto_ops(struct lib80211_crypto_ops *ops)
+{
+ unsigned long flags;
+ struct rtllib_crypto_alg *alg;
+
+ if (hcrypt == NULL)
+ return -1;
+
+ alg = kzalloc(sizeof(*alg), GFP_KERNEL);
+ if (alg == NULL)
+ return -ENOMEM;
+
+ alg->ops = ops;
+
+ spin_lock_irqsave(&hcrypt->lock, flags);
+ list_add(&alg->list, &hcrypt->algs);
+ spin_unlock_irqrestore(&hcrypt->lock, flags);
+
+ printk(KERN_DEBUG "rtllib_crypt: registered algorithm '%s'\n",
+ ops->name);
+
+ return 0;
+}
+EXPORT_SYMBOL(rtllib_register_crypto_ops);
+
+int rtllib_unregister_crypto_ops(struct lib80211_crypto_ops *ops)
+{
+ unsigned long flags;
+ struct list_head *ptr;
+ struct rtllib_crypto_alg *del_alg = NULL;
+
+ if (hcrypt == NULL)
+ return -1;
+
+ spin_lock_irqsave(&hcrypt->lock, flags);
+ for (ptr = hcrypt->algs.next; ptr != &hcrypt->algs; ptr = ptr->next) {
+ struct rtllib_crypto_alg *alg =
+ (struct rtllib_crypto_alg *) ptr;
+ if (alg->ops == ops) {
+ list_del(&alg->list);
+ del_alg = alg;
+ break;
+ }
+ }
+ spin_unlock_irqrestore(&hcrypt->lock, flags);
+
+ if (del_alg) {
+ printk(KERN_DEBUG "rtllib_crypt: unregistered algorithm '%s'\n",
+ ops->name);
+ kfree(del_alg);
+ }
+
+ return del_alg ? 0 : -1;
+}
+EXPORT_SYMBOL(rtllib_unregister_crypto_ops);
+
+
+struct lib80211_crypto_ops *rtllib_get_crypto_ops(const char *name)
+{
+ unsigned long flags;
+ struct list_head *ptr;
+ struct rtllib_crypto_alg *found_alg = NULL;
+
+ if (hcrypt == NULL)
+ return NULL;
+
+ spin_lock_irqsave(&hcrypt->lock, flags);
+ for (ptr = hcrypt->algs.next; ptr != &hcrypt->algs; ptr = ptr->next) {
+ struct rtllib_crypto_alg *alg =
+ (struct rtllib_crypto_alg *) ptr;
+ if (strcmp(alg->ops->name, name) == 0) {
+ found_alg = alg;
+ break;
+ }
+ }
+ spin_unlock_irqrestore(&hcrypt->lock, flags);
+
+ if (found_alg)
+ return found_alg->ops;
+ else
+ return NULL;
+}
+EXPORT_SYMBOL(rtllib_get_crypto_ops);
+
+
+static void *rtllib_crypt_null_init(int keyidx) { return (void *) 1; }
+static void rtllib_crypt_null_deinit(void *priv) {}
+
+static struct lib80211_crypto_ops rtllib_crypt_null = {
+ .name = "NULL",
+ .init = rtllib_crypt_null_init,
+ .deinit = rtllib_crypt_null_deinit,
+ .encrypt_mpdu = NULL,
+ .decrypt_mpdu = NULL,
+ .encrypt_msdu = NULL,
+ .decrypt_msdu = NULL,
+ .set_key = NULL,
+ .get_key = NULL,
+ .extra_mpdu_prefix_len = 0,
+ .extra_mpdu_postfix_len = 0,
+ .extra_msdu_prefix_len = 0,
+ .extra_msdu_postfix_len = 0,
+ .owner = THIS_MODULE,
+};
+
+
+int __init rtllib_crypto_init(void)
+{
+ int ret = -ENOMEM;
+
+ hcrypt = kzalloc(sizeof(*hcrypt), GFP_KERNEL);
+ if (!hcrypt)
+ goto out;
+
+ INIT_LIST_HEAD(&hcrypt->algs);
+ spin_lock_init(&hcrypt->lock);
+
+ ret = lib80211_register_crypto_ops(&rtllib_crypt_null);
+ if (ret < 0) {
+ kfree(hcrypt);
+ hcrypt = NULL;
+ }
+out:
+ return ret;
+}
+
+
+void __exit rtllib_crypto_deinit(void)
+{
+ struct list_head *ptr, *n;
+
+ if (hcrypt == NULL)
+ return;
+
+ for (ptr = hcrypt->algs.next, n = ptr->next; ptr != &hcrypt->algs;
+ ptr = n, n = ptr->next) {
+ struct rtllib_crypto_alg *alg =
+ (struct rtllib_crypto_alg *) ptr;
+ list_del(ptr);
+ printk(KERN_DEBUG
+ "rtllib_crypt: unregistered algorithm '%s' (deinit)\n",
+ alg->ops->name);
+ kfree(alg);
+ }
+
+ kfree(hcrypt);
+}
+
+module_init(rtllib_crypto_init);
+module_exit(rtllib_crypto_deinit);
+
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/rtl8192e/rtllib_crypt.h b/drivers/staging/rtl8192e/rtllib_crypt.h
new file mode 100644
index 000000000..b8cf59f39
--- /dev/null
+++ b/drivers/staging/rtl8192e/rtllib_crypt.h
@@ -0,0 +1,34 @@
+/*
+ * Original code based on Host AP (software wireless LAN access point) driver
+ * for Intersil Prism2/2.5/3.
+ *
+ * Copyright (c) 2001-2002, SSH Communications Security Corp and Jouni Malinen
+ * <jkmaline@cc.hut.fi>
+ * Copyright (c) 2002-2003, Jouni Malinen <jkmaline@cc.hut.fi>
+ *
+ * Adaption to a generic IEEE 802.11 stack by James Ketrenos
+ * <jketreno@linux.intel.com>
+ *
+ * Copyright (c) 2004, Intel Corporation
+ *
+ * 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. See README and COPYING for
+ * more details.
+ */
+
+/* This file defines the interface to the rtllib crypto module.
+ */
+#ifndef RTLLIB_CRYPT_H
+#define RTLLIB_CRYPT_H
+
+#include <linux/skbuff.h>
+
+int rtllib_register_crypto_ops(struct lib80211_crypto_ops *ops);
+int rtllib_unregister_crypto_ops(struct lib80211_crypto_ops *ops);
+struct lib80211_crypto_ops *rtllib_get_crypto_ops(const char *name);
+void rtllib_crypt_deinit_entries(struct lib80211_crypt_info *info, int force);
+void rtllib_crypt_deinit_handler(unsigned long data);
+void rtllib_crypt_delayed_deinit(struct lib80211_crypt_info *info,
+ struct lib80211_crypt_data **crypt);
+#endif
diff --git a/drivers/staging/rtl8192e/rtllib_crypt_ccmp.c b/drivers/staging/rtl8192e/rtllib_crypt_ccmp.c
new file mode 100644
index 000000000..7d486e888
--- /dev/null
+++ b/drivers/staging/rtl8192e/rtllib_crypt_ccmp.c
@@ -0,0 +1,457 @@
+/*
+ * Host AP crypt: host-based CCMP encryption implementation for Host AP driver
+ *
+ * Copyright (c) 2003-2004, Jouni Malinen <jkmaline@cc.hut.fi>
+ *
+ * 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. See README and COPYING for
+ * more details.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/random.h>
+#include <linux/skbuff.h>
+#include <linux/netdevice.h>
+#include <linux/if_ether.h>
+#include <linux/if_arp.h>
+#include <linux/string.h>
+#include <linux/wireless.h>
+#include "rtllib.h"
+
+#include <linux/crypto.h>
+
+#include <linux/scatterlist.h>
+
+#define AES_BLOCK_LEN 16
+#define CCMP_HDR_LEN 8
+#define CCMP_MIC_LEN 8
+#define CCMP_TK_LEN 16
+#define CCMP_PN_LEN 6
+
+struct rtllib_ccmp_data {
+ u8 key[CCMP_TK_LEN];
+ int key_set;
+
+ u8 tx_pn[CCMP_PN_LEN];
+ u8 rx_pn[CCMP_PN_LEN];
+
+ u32 dot11RSNAStatsCCMPFormatErrors;
+ u32 dot11RSNAStatsCCMPReplays;
+ u32 dot11RSNAStatsCCMPDecryptErrors;
+
+ int key_idx;
+
+ struct crypto_tfm *tfm;
+
+ /* scratch buffers for virt_to_page() (crypto API) */
+ u8 tx_b0[AES_BLOCK_LEN], tx_b[AES_BLOCK_LEN],
+ tx_e[AES_BLOCK_LEN], tx_s0[AES_BLOCK_LEN];
+ u8 rx_b0[AES_BLOCK_LEN], rx_b[AES_BLOCK_LEN], rx_a[AES_BLOCK_LEN];
+};
+
+static void rtllib_ccmp_aes_encrypt(struct crypto_tfm *tfm,
+ const u8 pt[16], u8 ct[16])
+{
+ crypto_cipher_encrypt_one((void *)tfm, ct, pt);
+}
+
+static void *rtllib_ccmp_init(int key_idx)
+{
+ struct rtllib_ccmp_data *priv;
+
+ priv = kzalloc(sizeof(*priv), GFP_ATOMIC);
+ if (priv == NULL)
+ goto fail;
+ priv->key_idx = key_idx;
+
+ priv->tfm = (void *)crypto_alloc_cipher("aes", 0, CRYPTO_ALG_ASYNC);
+ if (IS_ERR(priv->tfm)) {
+ pr_debug("rtllib_crypt_ccmp: could not allocate crypto API aes\n");
+ priv->tfm = NULL;
+ goto fail;
+ }
+ return priv;
+
+fail:
+ if (priv) {
+ if (priv->tfm)
+ crypto_free_cipher((void *)priv->tfm);
+ kfree(priv);
+ }
+
+ return NULL;
+}
+
+
+static void rtllib_ccmp_deinit(void *priv)
+{
+ struct rtllib_ccmp_data *_priv = priv;
+
+ if (_priv && _priv->tfm)
+ crypto_free_cipher((void *)_priv->tfm);
+ kfree(priv);
+}
+
+
+static inline void xor_block(u8 *b, u8 *a, size_t len)
+{
+ int i;
+
+ for (i = 0; i < len; i++)
+ b[i] ^= a[i];
+}
+
+
+
+static void ccmp_init_blocks(struct crypto_tfm *tfm,
+ struct rtllib_hdr_4addr *hdr,
+ u8 *pn, size_t dlen, u8 *b0, u8 *auth,
+ u8 *s0)
+{
+ u8 *pos, qc = 0;
+ size_t aad_len;
+ u16 fc;
+ int a4_included, qc_included;
+ u8 aad[2 * AES_BLOCK_LEN];
+
+ fc = le16_to_cpu(hdr->frame_ctl);
+ a4_included = ((fc & (RTLLIB_FCTL_TODS | RTLLIB_FCTL_FROMDS)) ==
+ (RTLLIB_FCTL_TODS | RTLLIB_FCTL_FROMDS));
+
+ qc_included = ((WLAN_FC_GET_TYPE(fc) == RTLLIB_FTYPE_DATA) &&
+ (WLAN_FC_GET_STYPE(fc) & 0x80));
+ aad_len = 22;
+ if (a4_included)
+ aad_len += 6;
+ if (qc_included) {
+ pos = (u8 *) &hdr->addr4;
+ if (a4_included)
+ pos += 6;
+ qc = *pos & 0x0f;
+ aad_len += 2;
+ }
+ /* CCM Initial Block:
+ * Flag (Include authentication header, M=3 (8-octet MIC),
+ * L=1 (2-octet Dlen))
+ * Nonce: 0x00 | A2 | PN
+ * Dlen
+ */
+ b0[0] = 0x59;
+ b0[1] = qc;
+ memcpy(b0 + 2, hdr->addr2, ETH_ALEN);
+ memcpy(b0 + 8, pn, CCMP_PN_LEN);
+ b0[14] = (dlen >> 8) & 0xff;
+ b0[15] = dlen & 0xff;
+
+ /* AAD:
+ * FC with bits 4..6 and 11..13 masked to zero; 14 is always one
+ * A1 | A2 | A3
+ * SC with bits 4..15 (seq#) masked to zero
+ * A4 (if present)
+ * QC (if present)
+ */
+ pos = (u8 *) hdr;
+ aad[0] = 0; /* aad_len >> 8 */
+ aad[1] = aad_len & 0xff;
+ aad[2] = pos[0] & 0x8f;
+ aad[3] = pos[1] & 0xc7;
+ memcpy(aad + 4, hdr->addr1, 3 * ETH_ALEN);
+ pos = (u8 *) &hdr->seq_ctl;
+ aad[22] = pos[0] & 0x0f;
+ aad[23] = 0; /* all bits masked */
+ memset(aad + 24, 0, 8);
+ if (a4_included)
+ memcpy(aad + 24, hdr->addr4, ETH_ALEN);
+ if (qc_included) {
+ aad[a4_included ? 30 : 24] = qc;
+ /* rest of QC masked */
+ }
+
+ /* Start with the first block and AAD */
+ rtllib_ccmp_aes_encrypt(tfm, b0, auth);
+ xor_block(auth, aad, AES_BLOCK_LEN);
+ rtllib_ccmp_aes_encrypt(tfm, auth, auth);
+ xor_block(auth, &aad[AES_BLOCK_LEN], AES_BLOCK_LEN);
+ rtllib_ccmp_aes_encrypt(tfm, auth, auth);
+ b0[0] &= 0x07;
+ b0[14] = b0[15] = 0;
+ rtllib_ccmp_aes_encrypt(tfm, b0, s0);
+}
+
+
+
+static int rtllib_ccmp_encrypt(struct sk_buff *skb, int hdr_len, void *priv)
+{
+ struct rtllib_ccmp_data *key = priv;
+ int data_len, i;
+ u8 *pos;
+ struct rtllib_hdr_4addr *hdr;
+ struct cb_desc *tcb_desc = (struct cb_desc *)(skb->cb +
+ MAX_DEV_ADDR_SIZE);
+ if (skb_headroom(skb) < CCMP_HDR_LEN ||
+ skb_tailroom(skb) < CCMP_MIC_LEN ||
+ skb->len < hdr_len)
+ return -1;
+
+ data_len = skb->len - hdr_len;
+ pos = skb_push(skb, CCMP_HDR_LEN);
+ memmove(pos, pos + CCMP_HDR_LEN, hdr_len);
+ pos += hdr_len;
+
+ i = CCMP_PN_LEN - 1;
+ while (i >= 0) {
+ key->tx_pn[i]++;
+ if (key->tx_pn[i] != 0)
+ break;
+ i--;
+ }
+
+ *pos++ = key->tx_pn[5];
+ *pos++ = key->tx_pn[4];
+ *pos++ = 0;
+ *pos++ = (key->key_idx << 6) | (1 << 5) /* Ext IV included */;
+ *pos++ = key->tx_pn[3];
+ *pos++ = key->tx_pn[2];
+ *pos++ = key->tx_pn[1];
+ *pos++ = key->tx_pn[0];
+
+
+ hdr = (struct rtllib_hdr_4addr *) skb->data;
+ if (!tcb_desc->bHwSec) {
+ int blocks, last, len;
+ u8 *mic;
+ u8 *b0 = key->tx_b0;
+ u8 *b = key->tx_b;
+ u8 *e = key->tx_e;
+ u8 *s0 = key->tx_s0;
+
+ mic = skb_put(skb, CCMP_MIC_LEN);
+
+ ccmp_init_blocks(key->tfm, hdr, key->tx_pn, data_len,
+ b0, b, s0);
+
+ blocks = (data_len + AES_BLOCK_LEN - 1) / AES_BLOCK_LEN;
+ last = data_len % AES_BLOCK_LEN;
+
+ for (i = 1; i <= blocks; i++) {
+ len = (i == blocks && last) ? last : AES_BLOCK_LEN;
+ /* Authentication */
+ xor_block(b, pos, len);
+ rtllib_ccmp_aes_encrypt(key->tfm, b, b);
+ /* Encryption, with counter */
+ b0[14] = (i >> 8) & 0xff;
+ b0[15] = i & 0xff;
+ rtllib_ccmp_aes_encrypt(key->tfm, b0, e);
+ xor_block(pos, e, len);
+ pos += len;
+ }
+
+ for (i = 0; i < CCMP_MIC_LEN; i++)
+ mic[i] = b[i] ^ s0[i];
+ }
+ return 0;
+}
+
+
+static int rtllib_ccmp_decrypt(struct sk_buff *skb, int hdr_len, void *priv)
+{
+ struct rtllib_ccmp_data *key = priv;
+ u8 keyidx, *pos;
+ struct rtllib_hdr_4addr *hdr;
+ struct cb_desc *tcb_desc = (struct cb_desc *)(skb->cb +
+ MAX_DEV_ADDR_SIZE);
+ u8 pn[6];
+
+ if (skb->len < hdr_len + CCMP_HDR_LEN + CCMP_MIC_LEN) {
+ key->dot11RSNAStatsCCMPFormatErrors++;
+ return -1;
+ }
+
+ hdr = (struct rtllib_hdr_4addr *) skb->data;
+ pos = skb->data + hdr_len;
+ keyidx = pos[3];
+ if (!(keyidx & (1 << 5))) {
+ if (net_ratelimit()) {
+ pr_debug("CCMP: received packet without ExtIV flag from %pM\n",
+ hdr->addr2);
+ }
+ key->dot11RSNAStatsCCMPFormatErrors++;
+ return -2;
+ }
+ keyidx >>= 6;
+ if (key->key_idx != keyidx) {
+ pr_debug("CCMP: RX tkey->key_idx=%d frame keyidx=%d priv=%p\n",
+ key->key_idx, keyidx, priv);
+ return -6;
+ }
+ if (!key->key_set) {
+ if (net_ratelimit()) {
+ pr_debug("CCMP: received packet from %pM with keyid=%d that does not have a configured key\n",
+ hdr->addr2, keyidx);
+ }
+ return -3;
+ }
+
+ pn[0] = pos[7];
+ pn[1] = pos[6];
+ pn[2] = pos[5];
+ pn[3] = pos[4];
+ pn[4] = pos[1];
+ pn[5] = pos[0];
+ pos += 8;
+ if (memcmp(pn, key->rx_pn, CCMP_PN_LEN) <= 0) {
+ key->dot11RSNAStatsCCMPReplays++;
+ return -4;
+ }
+ if (!tcb_desc->bHwSec) {
+ size_t data_len = skb->len - hdr_len - CCMP_HDR_LEN -
+ CCMP_MIC_LEN;
+ u8 *mic = skb->data + skb->len - CCMP_MIC_LEN;
+ u8 *b0 = key->rx_b0;
+ u8 *b = key->rx_b;
+ u8 *a = key->rx_a;
+ int i, blocks, last, len;
+
+
+ ccmp_init_blocks(key->tfm, hdr, pn, data_len, b0, a, b);
+ xor_block(mic, b, CCMP_MIC_LEN);
+
+ blocks = (data_len + AES_BLOCK_LEN - 1) / AES_BLOCK_LEN;
+ last = data_len % AES_BLOCK_LEN;
+
+ for (i = 1; i <= blocks; i++) {
+ len = (i == blocks && last) ? last : AES_BLOCK_LEN;
+ /* Decrypt, with counter */
+ b0[14] = (i >> 8) & 0xff;
+ b0[15] = i & 0xff;
+ rtllib_ccmp_aes_encrypt(key->tfm, b0, b);
+ xor_block(pos, b, len);
+ /* Authentication */
+ xor_block(a, pos, len);
+ rtllib_ccmp_aes_encrypt(key->tfm, a, a);
+ pos += len;
+ }
+
+ if (memcmp(mic, a, CCMP_MIC_LEN) != 0) {
+ if (net_ratelimit()) {
+ pr_debug("CCMP: decrypt failed: STA= %pM\n",
+ hdr->addr2);
+ }
+ key->dot11RSNAStatsCCMPDecryptErrors++;
+ return -5;
+ }
+
+ memcpy(key->rx_pn, pn, CCMP_PN_LEN);
+ }
+ /* Remove hdr and MIC */
+ memmove(skb->data + CCMP_HDR_LEN, skb->data, hdr_len);
+ skb_pull(skb, CCMP_HDR_LEN);
+ skb_trim(skb, skb->len - CCMP_MIC_LEN);
+
+ return keyidx;
+}
+
+
+static int rtllib_ccmp_set_key(void *key, int len, u8 *seq, void *priv)
+{
+ struct rtllib_ccmp_data *data = priv;
+ int keyidx;
+ struct crypto_tfm *tfm = data->tfm;
+
+ keyidx = data->key_idx;
+ memset(data, 0, sizeof(*data));
+ data->key_idx = keyidx;
+ data->tfm = tfm;
+ if (len == CCMP_TK_LEN) {
+ memcpy(data->key, key, CCMP_TK_LEN);
+ data->key_set = 1;
+ if (seq) {
+ data->rx_pn[0] = seq[5];
+ data->rx_pn[1] = seq[4];
+ data->rx_pn[2] = seq[3];
+ data->rx_pn[3] = seq[2];
+ data->rx_pn[4] = seq[1];
+ data->rx_pn[5] = seq[0];
+ }
+ crypto_cipher_setkey((void *)data->tfm, data->key, CCMP_TK_LEN);
+ } else if (len == 0)
+ data->key_set = 0;
+ else
+ return -1;
+
+ return 0;
+}
+
+
+static int rtllib_ccmp_get_key(void *key, int len, u8 *seq, void *priv)
+{
+ struct rtllib_ccmp_data *data = priv;
+
+ if (len < CCMP_TK_LEN)
+ return -1;
+
+ if (!data->key_set)
+ return 0;
+ memcpy(key, data->key, CCMP_TK_LEN);
+
+ if (seq) {
+ seq[0] = data->tx_pn[5];
+ seq[1] = data->tx_pn[4];
+ seq[2] = data->tx_pn[3];
+ seq[3] = data->tx_pn[2];
+ seq[4] = data->tx_pn[1];
+ seq[5] = data->tx_pn[0];
+ }
+
+ return CCMP_TK_LEN;
+}
+
+
+static void rtllib_ccmp_print_stats(struct seq_file *m, void *priv)
+{
+ struct rtllib_ccmp_data *ccmp = priv;
+
+ seq_printf(m,
+ "key[%d] alg=CCMP key_set=%d tx_pn=%pM rx_pn=%pM format_errors=%d replays=%d decrypt_errors=%d\n",
+ ccmp->key_idx, ccmp->key_set,
+ ccmp->tx_pn, ccmp->rx_pn,
+ ccmp->dot11RSNAStatsCCMPFormatErrors,
+ ccmp->dot11RSNAStatsCCMPReplays,
+ ccmp->dot11RSNAStatsCCMPDecryptErrors);
+}
+
+static struct lib80211_crypto_ops rtllib_crypt_ccmp = {
+ .name = "R-CCMP",
+ .init = rtllib_ccmp_init,
+ .deinit = rtllib_ccmp_deinit,
+ .encrypt_mpdu = rtllib_ccmp_encrypt,
+ .decrypt_mpdu = rtllib_ccmp_decrypt,
+ .encrypt_msdu = NULL,
+ .decrypt_msdu = NULL,
+ .set_key = rtllib_ccmp_set_key,
+ .get_key = rtllib_ccmp_get_key,
+ .print_stats = rtllib_ccmp_print_stats,
+ .extra_mpdu_prefix_len = CCMP_HDR_LEN,
+ .extra_mpdu_postfix_len = CCMP_MIC_LEN,
+ .owner = THIS_MODULE,
+};
+
+
+static int __init rtllib_crypto_ccmp_init(void)
+{
+ return lib80211_register_crypto_ops(&rtllib_crypt_ccmp);
+}
+
+
+static void __exit rtllib_crypto_ccmp_exit(void)
+{
+ lib80211_unregister_crypto_ops(&rtllib_crypt_ccmp);
+}
+
+module_init(rtllib_crypto_ccmp_init);
+module_exit(rtllib_crypto_ccmp_exit);
+
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/rtl8192e/rtllib_crypt_tkip.c b/drivers/staging/rtl8192e/rtllib_crypt_tkip.c
new file mode 100644
index 000000000..656b4b359
--- /dev/null
+++ b/drivers/staging/rtl8192e/rtllib_crypt_tkip.c
@@ -0,0 +1,779 @@
+/*
+ * Host AP crypt: host-based TKIP encryption implementation for Host AP driver
+ *
+ * Copyright (c) 2003-2004, Jouni Malinen <jkmaline@cc.hut.fi>
+ *
+ * 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. See README and COPYING for
+ * more details.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/random.h>
+#include <linux/skbuff.h>
+#include <linux/netdevice.h>
+#include <linux/if_ether.h>
+#include <linux/if_arp.h>
+#include <linux/string.h>
+#include <linux/crypto.h>
+#include <linux/scatterlist.h>
+#include <linux/crc32.h>
+
+#include "rtllib.h"
+
+struct rtllib_tkip_data {
+#define TKIP_KEY_LEN 32
+ u8 key[TKIP_KEY_LEN];
+ int key_set;
+
+ u32 tx_iv32;
+ u16 tx_iv16;
+ u16 tx_ttak[5];
+ int tx_phase1_done;
+
+ u32 rx_iv32;
+ u16 rx_iv16;
+ bool initialized;
+ u16 rx_ttak[5];
+ int rx_phase1_done;
+ u32 rx_iv32_new;
+ u16 rx_iv16_new;
+
+ u32 dot11RSNAStatsTKIPReplays;
+ u32 dot11RSNAStatsTKIPICVErrors;
+ u32 dot11RSNAStatsTKIPLocalMICFailures;
+
+ int key_idx;
+ struct crypto_blkcipher *rx_tfm_arc4;
+ struct crypto_hash *rx_tfm_michael;
+ struct crypto_blkcipher *tx_tfm_arc4;
+ struct crypto_hash *tx_tfm_michael;
+ /* scratch buffers for virt_to_page() (crypto API) */
+ u8 rx_hdr[16], tx_hdr[16];
+};
+
+static void *rtllib_tkip_init(int key_idx)
+{
+ struct rtllib_tkip_data *priv;
+
+ priv = kzalloc(sizeof(*priv), GFP_ATOMIC);
+ if (priv == NULL)
+ goto fail;
+ priv->key_idx = key_idx;
+ priv->tx_tfm_arc4 = crypto_alloc_blkcipher("ecb(arc4)", 0,
+ CRYPTO_ALG_ASYNC);
+ if (IS_ERR(priv->tx_tfm_arc4)) {
+ printk(KERN_DEBUG
+ "rtllib_crypt_tkip: could not allocate crypto API arc4\n");
+ priv->tx_tfm_arc4 = NULL;
+ goto fail;
+ }
+
+ priv->tx_tfm_michael = crypto_alloc_hash("michael_mic", 0,
+ CRYPTO_ALG_ASYNC);
+ if (IS_ERR(priv->tx_tfm_michael)) {
+ printk(KERN_DEBUG
+ "rtllib_crypt_tkip: could not allocate crypto API michael_mic\n");
+ priv->tx_tfm_michael = NULL;
+ goto fail;
+ }
+
+ priv->rx_tfm_arc4 = crypto_alloc_blkcipher("ecb(arc4)", 0,
+ CRYPTO_ALG_ASYNC);
+ if (IS_ERR(priv->rx_tfm_arc4)) {
+ printk(KERN_DEBUG
+ "rtllib_crypt_tkip: could not allocate crypto API arc4\n");
+ priv->rx_tfm_arc4 = NULL;
+ goto fail;
+ }
+
+ priv->rx_tfm_michael = crypto_alloc_hash("michael_mic", 0,
+ CRYPTO_ALG_ASYNC);
+ if (IS_ERR(priv->rx_tfm_michael)) {
+ printk(KERN_DEBUG
+ "rtllib_crypt_tkip: could not allocate crypto API michael_mic\n");
+ priv->rx_tfm_michael = NULL;
+ goto fail;
+ }
+ return priv;
+
+fail:
+ if (priv) {
+ if (priv->tx_tfm_michael)
+ crypto_free_hash(priv->tx_tfm_michael);
+ if (priv->tx_tfm_arc4)
+ crypto_free_blkcipher(priv->tx_tfm_arc4);
+ if (priv->rx_tfm_michael)
+ crypto_free_hash(priv->rx_tfm_michael);
+ if (priv->rx_tfm_arc4)
+ crypto_free_blkcipher(priv->rx_tfm_arc4);
+ kfree(priv);
+ }
+
+ return NULL;
+}
+
+
+static void rtllib_tkip_deinit(void *priv)
+{
+ struct rtllib_tkip_data *_priv = priv;
+
+ if (_priv) {
+ if (_priv->tx_tfm_michael)
+ crypto_free_hash(_priv->tx_tfm_michael);
+ if (_priv->tx_tfm_arc4)
+ crypto_free_blkcipher(_priv->tx_tfm_arc4);
+ if (_priv->rx_tfm_michael)
+ crypto_free_hash(_priv->rx_tfm_michael);
+ if (_priv->rx_tfm_arc4)
+ crypto_free_blkcipher(_priv->rx_tfm_arc4);
+ }
+ kfree(priv);
+}
+
+
+static inline u16 RotR1(u16 val)
+{
+ return (val >> 1) | (val << 15);
+}
+
+
+static inline u8 Lo8(u16 val)
+{
+ return val & 0xff;
+}
+
+
+static inline u8 Hi8(u16 val)
+{
+ return val >> 8;
+}
+
+
+static inline u16 Lo16(u32 val)
+{
+ return val & 0xffff;
+}
+
+
+static inline u16 Hi16(u32 val)
+{
+ return val >> 16;
+}
+
+
+static inline u16 Mk16(u8 hi, u8 lo)
+{
+ return lo | (((u16) hi) << 8);
+}
+
+
+static inline u16 Mk16_le(u16 *v)
+{
+ return *v;
+}
+
+
+static const u16 Sbox[256] = {
+ 0xC6A5, 0xF884, 0xEE99, 0xF68D, 0xFF0D, 0xD6BD, 0xDEB1, 0x9154,
+ 0x6050, 0x0203, 0xCEA9, 0x567D, 0xE719, 0xB562, 0x4DE6, 0xEC9A,
+ 0x8F45, 0x1F9D, 0x8940, 0xFA87, 0xEF15, 0xB2EB, 0x8EC9, 0xFB0B,
+ 0x41EC, 0xB367, 0x5FFD, 0x45EA, 0x23BF, 0x53F7, 0xE496, 0x9B5B,
+ 0x75C2, 0xE11C, 0x3DAE, 0x4C6A, 0x6C5A, 0x7E41, 0xF502, 0x834F,
+ 0x685C, 0x51F4, 0xD134, 0xF908, 0xE293, 0xAB73, 0x6253, 0x2A3F,
+ 0x080C, 0x9552, 0x4665, 0x9D5E, 0x3028, 0x37A1, 0x0A0F, 0x2FB5,
+ 0x0E09, 0x2436, 0x1B9B, 0xDF3D, 0xCD26, 0x4E69, 0x7FCD, 0xEA9F,
+ 0x121B, 0x1D9E, 0x5874, 0x342E, 0x362D, 0xDCB2, 0xB4EE, 0x5BFB,
+ 0xA4F6, 0x764D, 0xB761, 0x7DCE, 0x527B, 0xDD3E, 0x5E71, 0x1397,
+ 0xA6F5, 0xB968, 0x0000, 0xC12C, 0x4060, 0xE31F, 0x79C8, 0xB6ED,
+ 0xD4BE, 0x8D46, 0x67D9, 0x724B, 0x94DE, 0x98D4, 0xB0E8, 0x854A,
+ 0xBB6B, 0xC52A, 0x4FE5, 0xED16, 0x86C5, 0x9AD7, 0x6655, 0x1194,
+ 0x8ACF, 0xE910, 0x0406, 0xFE81, 0xA0F0, 0x7844, 0x25BA, 0x4BE3,
+ 0xA2F3, 0x5DFE, 0x80C0, 0x058A, 0x3FAD, 0x21BC, 0x7048, 0xF104,
+ 0x63DF, 0x77C1, 0xAF75, 0x4263, 0x2030, 0xE51A, 0xFD0E, 0xBF6D,
+ 0x814C, 0x1814, 0x2635, 0xC32F, 0xBEE1, 0x35A2, 0x88CC, 0x2E39,
+ 0x9357, 0x55F2, 0xFC82, 0x7A47, 0xC8AC, 0xBAE7, 0x322B, 0xE695,
+ 0xC0A0, 0x1998, 0x9ED1, 0xA37F, 0x4466, 0x547E, 0x3BAB, 0x0B83,
+ 0x8CCA, 0xC729, 0x6BD3, 0x283C, 0xA779, 0xBCE2, 0x161D, 0xAD76,
+ 0xDB3B, 0x6456, 0x744E, 0x141E, 0x92DB, 0x0C0A, 0x486C, 0xB8E4,
+ 0x9F5D, 0xBD6E, 0x43EF, 0xC4A6, 0x39A8, 0x31A4, 0xD337, 0xF28B,
+ 0xD532, 0x8B43, 0x6E59, 0xDAB7, 0x018C, 0xB164, 0x9CD2, 0x49E0,
+ 0xD8B4, 0xACFA, 0xF307, 0xCF25, 0xCAAF, 0xF48E, 0x47E9, 0x1018,
+ 0x6FD5, 0xF088, 0x4A6F, 0x5C72, 0x3824, 0x57F1, 0x73C7, 0x9751,
+ 0xCB23, 0xA17C, 0xE89C, 0x3E21, 0x96DD, 0x61DC, 0x0D86, 0x0F85,
+ 0xE090, 0x7C42, 0x71C4, 0xCCAA, 0x90D8, 0x0605, 0xF701, 0x1C12,
+ 0xC2A3, 0x6A5F, 0xAEF9, 0x69D0, 0x1791, 0x9958, 0x3A27, 0x27B9,
+ 0xD938, 0xEB13, 0x2BB3, 0x2233, 0xD2BB, 0xA970, 0x0789, 0x33A7,
+ 0x2DB6, 0x3C22, 0x1592, 0xC920, 0x8749, 0xAAFF, 0x5078, 0xA57A,
+ 0x038F, 0x59F8, 0x0980, 0x1A17, 0x65DA, 0xD731, 0x84C6, 0xD0B8,
+ 0x82C3, 0x29B0, 0x5A77, 0x1E11, 0x7BCB, 0xA8FC, 0x6DD6, 0x2C3A,
+};
+
+
+static inline u16 _S_(u16 v)
+{
+ u16 t = Sbox[Hi8(v)];
+ return Sbox[Lo8(v)] ^ ((t << 8) | (t >> 8));
+}
+
+
+#define PHASE1_LOOP_COUNT 8
+
+
+static void tkip_mixing_phase1(u16 *TTAK, const u8 *TK, const u8 *TA, u32 IV32)
+{
+ int i, j;
+
+ /* Initialize the 80-bit TTAK from TSC (IV32) and TA[0..5] */
+ TTAK[0] = Lo16(IV32);
+ TTAK[1] = Hi16(IV32);
+ TTAK[2] = Mk16(TA[1], TA[0]);
+ TTAK[3] = Mk16(TA[3], TA[2]);
+ TTAK[4] = Mk16(TA[5], TA[4]);
+
+ for (i = 0; i < PHASE1_LOOP_COUNT; i++) {
+ j = 2 * (i & 1);
+ TTAK[0] += _S_(TTAK[4] ^ Mk16(TK[1 + j], TK[0 + j]));
+ TTAK[1] += _S_(TTAK[0] ^ Mk16(TK[5 + j], TK[4 + j]));
+ TTAK[2] += _S_(TTAK[1] ^ Mk16(TK[9 + j], TK[8 + j]));
+ TTAK[3] += _S_(TTAK[2] ^ Mk16(TK[13 + j], TK[12 + j]));
+ TTAK[4] += _S_(TTAK[3] ^ Mk16(TK[1 + j], TK[0 + j])) + i;
+ }
+}
+
+
+static void tkip_mixing_phase2(u8 *WEPSeed, const u8 *TK, const u16 *TTAK,
+ u16 IV16)
+{
+ /* Make temporary area overlap WEP seed so that the final copy can be
+ * avoided on little endian hosts.
+ */
+ u16 *PPK = (u16 *) &WEPSeed[4];
+
+ /* Step 1 - make copy of TTAK and bring in TSC */
+ PPK[0] = TTAK[0];
+ PPK[1] = TTAK[1];
+ PPK[2] = TTAK[2];
+ PPK[3] = TTAK[3];
+ PPK[4] = TTAK[4];
+ PPK[5] = TTAK[4] + IV16;
+
+ /* Step 2 - 96-bit bijective mixing using S-box */
+ PPK[0] += _S_(PPK[5] ^ Mk16_le((u16 *) &TK[0]));
+ PPK[1] += _S_(PPK[0] ^ Mk16_le((u16 *) &TK[2]));
+ PPK[2] += _S_(PPK[1] ^ Mk16_le((u16 *) &TK[4]));
+ PPK[3] += _S_(PPK[2] ^ Mk16_le((u16 *) &TK[6]));
+ PPK[4] += _S_(PPK[3] ^ Mk16_le((u16 *) &TK[8]));
+ PPK[5] += _S_(PPK[4] ^ Mk16_le((u16 *) &TK[10]));
+
+ PPK[0] += RotR1(PPK[5] ^ Mk16_le((u16 *) &TK[12]));
+ PPK[1] += RotR1(PPK[0] ^ Mk16_le((u16 *) &TK[14]));
+ PPK[2] += RotR1(PPK[1]);
+ PPK[3] += RotR1(PPK[2]);
+ PPK[4] += RotR1(PPK[3]);
+ PPK[5] += RotR1(PPK[4]);
+
+ /* Step 3 - bring in last of TK bits, assign 24-bit WEP IV value
+ * WEPSeed[0..2] is transmitted as WEP IV
+ */
+ WEPSeed[0] = Hi8(IV16);
+ WEPSeed[1] = (Hi8(IV16) | 0x20) & 0x7F;
+ WEPSeed[2] = Lo8(IV16);
+ WEPSeed[3] = Lo8((PPK[5] ^ Mk16_le((u16 *) &TK[0])) >> 1);
+
+#ifdef __BIG_ENDIAN
+ {
+ int i;
+
+ for (i = 0; i < 6; i++)
+ PPK[i] = (PPK[i] << 8) | (PPK[i] >> 8);
+ }
+#endif
+}
+
+
+static int rtllib_tkip_encrypt(struct sk_buff *skb, int hdr_len, void *priv)
+{
+ struct rtllib_tkip_data *tkey = priv;
+ int len;
+ u8 *pos;
+ struct rtllib_hdr_4addr *hdr;
+ struct cb_desc *tcb_desc = (struct cb_desc *)(skb->cb +
+ MAX_DEV_ADDR_SIZE);
+ struct blkcipher_desc desc = {.tfm = tkey->tx_tfm_arc4};
+ int ret = 0;
+ u8 rc4key[16], *icv;
+ u32 crc;
+ struct scatterlist sg;
+
+ if (skb_headroom(skb) < 8 || skb_tailroom(skb) < 4 ||
+ skb->len < hdr_len)
+ return -1;
+
+ hdr = (struct rtllib_hdr_4addr *) skb->data;
+
+ if (!tcb_desc->bHwSec) {
+ if (!tkey->tx_phase1_done) {
+ tkip_mixing_phase1(tkey->tx_ttak, tkey->key, hdr->addr2,
+ tkey->tx_iv32);
+ tkey->tx_phase1_done = 1;
+ }
+ tkip_mixing_phase2(rc4key, tkey->key, tkey->tx_ttak,
+ tkey->tx_iv16);
+ } else
+ tkey->tx_phase1_done = 1;
+
+
+ len = skb->len - hdr_len;
+ pos = skb_push(skb, 8);
+ memmove(pos, pos + 8, hdr_len);
+ pos += hdr_len;
+
+ if (tcb_desc->bHwSec) {
+ *pos++ = Hi8(tkey->tx_iv16);
+ *pos++ = (Hi8(tkey->tx_iv16) | 0x20) & 0x7F;
+ *pos++ = Lo8(tkey->tx_iv16);
+ } else {
+ *pos++ = rc4key[0];
+ *pos++ = rc4key[1];
+ *pos++ = rc4key[2];
+ }
+
+ *pos++ = (tkey->key_idx << 6) | (1 << 5) /* Ext IV included */;
+ *pos++ = tkey->tx_iv32 & 0xff;
+ *pos++ = (tkey->tx_iv32 >> 8) & 0xff;
+ *pos++ = (tkey->tx_iv32 >> 16) & 0xff;
+ *pos++ = (tkey->tx_iv32 >> 24) & 0xff;
+
+ if (!tcb_desc->bHwSec) {
+ icv = skb_put(skb, 4);
+ crc = ~crc32_le(~0, pos, len);
+ icv[0] = crc;
+ icv[1] = crc >> 8;
+ icv[2] = crc >> 16;
+ icv[3] = crc >> 24;
+
+ sg_init_one(&sg, pos, len+4);
+
+
+ crypto_blkcipher_setkey(tkey->tx_tfm_arc4, rc4key, 16);
+ ret = crypto_blkcipher_encrypt(&desc, &sg, &sg, len + 4);
+ }
+
+ tkey->tx_iv16++;
+ if (tkey->tx_iv16 == 0) {
+ tkey->tx_phase1_done = 0;
+ tkey->tx_iv32++;
+ }
+
+ if (!tcb_desc->bHwSec)
+ return ret;
+ else
+ return 0;
+
+
+}
+
+static int rtllib_tkip_decrypt(struct sk_buff *skb, int hdr_len, void *priv)
+{
+ struct rtllib_tkip_data *tkey = priv;
+ u8 keyidx, *pos;
+ u32 iv32;
+ u16 iv16;
+ struct rtllib_hdr_4addr *hdr;
+ struct cb_desc *tcb_desc = (struct cb_desc *)(skb->cb +
+ MAX_DEV_ADDR_SIZE);
+ struct blkcipher_desc desc = {.tfm = tkey->rx_tfm_arc4};
+ u8 rc4key[16];
+ u8 icv[4];
+ u32 crc;
+ struct scatterlist sg;
+ int plen;
+
+ if (skb->len < hdr_len + 8 + 4)
+ return -1;
+
+ hdr = (struct rtllib_hdr_4addr *) skb->data;
+ pos = skb->data + hdr_len;
+ keyidx = pos[3];
+ if (!(keyidx & (1 << 5))) {
+ if (net_ratelimit()) {
+ printk(KERN_DEBUG
+ "TKIP: received packet without ExtIV flag from %pM\n",
+ hdr->addr2);
+ }
+ return -2;
+ }
+ keyidx >>= 6;
+ if (tkey->key_idx != keyidx) {
+ printk(KERN_DEBUG
+ "TKIP: RX tkey->key_idx=%d frame keyidx=%d priv=%p\n",
+ tkey->key_idx, keyidx, priv);
+ return -6;
+ }
+ if (!tkey->key_set) {
+ if (net_ratelimit()) {
+ printk(KERN_DEBUG
+ "TKIP: received packet from %pM with keyid=%d that does not have a configured key\n",
+ hdr->addr2, keyidx);
+ }
+ return -3;
+ }
+ iv16 = (pos[0] << 8) | pos[2];
+ iv32 = pos[4] | (pos[5] << 8) | (pos[6] << 16) | (pos[7] << 24);
+ pos += 8;
+
+ if (!tcb_desc->bHwSec || (skb->cb[0] == 1)) {
+ if ((iv32 < tkey->rx_iv32 ||
+ (iv32 == tkey->rx_iv32 && iv16 <= tkey->rx_iv16)) &&
+ tkey->initialized) {
+ if (net_ratelimit()) {
+ printk(KERN_DEBUG
+ "TKIP: replay detected: STA= %pM previous TSC %08x%04x received TSC %08x%04x\n",
+ hdr->addr2, tkey->rx_iv32, tkey->rx_iv16,
+ iv32, iv16);
+ }
+ tkey->dot11RSNAStatsTKIPReplays++;
+ return -4;
+ }
+ tkey->initialized = true;
+
+ if (iv32 != tkey->rx_iv32 || !tkey->rx_phase1_done) {
+ tkip_mixing_phase1(tkey->rx_ttak, tkey->key,
+ hdr->addr2, iv32);
+ tkey->rx_phase1_done = 1;
+ }
+ tkip_mixing_phase2(rc4key, tkey->key, tkey->rx_ttak, iv16);
+
+ plen = skb->len - hdr_len - 12;
+
+ sg_init_one(&sg, pos, plen+4);
+
+ crypto_blkcipher_setkey(tkey->rx_tfm_arc4, rc4key, 16);
+ if (crypto_blkcipher_decrypt(&desc, &sg, &sg, plen + 4)) {
+ if (net_ratelimit()) {
+ printk(KERN_DEBUG
+ ": TKIP: failed to decrypt received packet from %pM\n",
+ hdr->addr2);
+ }
+ return -7;
+ }
+
+ crc = ~crc32_le(~0, pos, plen);
+ icv[0] = crc;
+ icv[1] = crc >> 8;
+ icv[2] = crc >> 16;
+ icv[3] = crc >> 24;
+
+ if (memcmp(icv, pos + plen, 4) != 0) {
+ if (iv32 != tkey->rx_iv32) {
+ /* Previously cached Phase1 result was already
+ * lost, so it needs to be recalculated for the
+ * next packet.
+ */
+ tkey->rx_phase1_done = 0;
+ }
+ if (net_ratelimit()) {
+ printk(KERN_DEBUG
+ "TKIP: ICV error detected: STA= %pM\n",
+ hdr->addr2);
+ }
+ tkey->dot11RSNAStatsTKIPICVErrors++;
+ return -5;
+ }
+
+ }
+
+ /* Update real counters only after Michael MIC verification has
+ * completed
+ */
+ tkey->rx_iv32_new = iv32;
+ tkey->rx_iv16_new = iv16;
+
+ /* Remove IV and ICV */
+ memmove(skb->data + 8, skb->data, hdr_len);
+ skb_pull(skb, 8);
+ skb_trim(skb, skb->len - 4);
+
+ return keyidx;
+}
+
+
+static int michael_mic(struct crypto_hash *tfm_michael, u8 *key, u8 *hdr,
+ u8 *data, size_t data_len, u8 *mic)
+{
+ struct hash_desc desc;
+ struct scatterlist sg[2];
+
+ if (tfm_michael == NULL) {
+ pr_warn("michael_mic: tfm_michael == NULL\n");
+ return -1;
+ }
+ sg_init_table(sg, 2);
+ sg_set_buf(&sg[0], hdr, 16);
+ sg_set_buf(&sg[1], data, data_len);
+
+ if (crypto_hash_setkey(tfm_michael, key, 8))
+ return -1;
+
+ desc.tfm = tfm_michael;
+ desc.flags = 0;
+ return crypto_hash_digest(&desc, sg, data_len + 16, mic);
+}
+
+static void michael_mic_hdr(struct sk_buff *skb, u8 *hdr)
+{
+ struct rtllib_hdr_4addr *hdr11;
+
+ hdr11 = (struct rtllib_hdr_4addr *) skb->data;
+ switch (le16_to_cpu(hdr11->frame_ctl) &
+ (RTLLIB_FCTL_FROMDS | RTLLIB_FCTL_TODS)) {
+ case RTLLIB_FCTL_TODS:
+ memcpy(hdr, hdr11->addr3, ETH_ALEN); /* DA */
+ memcpy(hdr + ETH_ALEN, hdr11->addr2, ETH_ALEN); /* SA */
+ break;
+ case RTLLIB_FCTL_FROMDS:
+ memcpy(hdr, hdr11->addr1, ETH_ALEN); /* DA */
+ memcpy(hdr + ETH_ALEN, hdr11->addr3, ETH_ALEN); /* SA */
+ break;
+ case RTLLIB_FCTL_FROMDS | RTLLIB_FCTL_TODS:
+ memcpy(hdr, hdr11->addr3, ETH_ALEN); /* DA */
+ memcpy(hdr + ETH_ALEN, hdr11->addr4, ETH_ALEN); /* SA */
+ break;
+ case 0:
+ memcpy(hdr, hdr11->addr1, ETH_ALEN); /* DA */
+ memcpy(hdr + ETH_ALEN, hdr11->addr2, ETH_ALEN); /* SA */
+ break;
+ }
+
+ hdr[12] = 0; /* priority */
+
+ hdr[13] = hdr[14] = hdr[15] = 0; /* reserved */
+}
+
+
+static int rtllib_michael_mic_add(struct sk_buff *skb, int hdr_len, void *priv)
+{
+ struct rtllib_tkip_data *tkey = priv;
+ u8 *pos;
+ struct rtllib_hdr_4addr *hdr;
+
+ hdr = (struct rtllib_hdr_4addr *) skb->data;
+
+ if (skb_tailroom(skb) < 8 || skb->len < hdr_len) {
+ printk(KERN_DEBUG
+ "Invalid packet for Michael MIC add (tailroom=%d hdr_len=%d skb->len=%d)\n",
+ skb_tailroom(skb), hdr_len, skb->len);
+ return -1;
+ }
+
+ michael_mic_hdr(skb, tkey->tx_hdr);
+
+ if (RTLLIB_QOS_HAS_SEQ(le16_to_cpu(hdr->frame_ctl)))
+ tkey->tx_hdr[12] = *(skb->data + hdr_len - 2) & 0x07;
+ pos = skb_put(skb, 8);
+ if (michael_mic(tkey->tx_tfm_michael, &tkey->key[16], tkey->tx_hdr,
+ skb->data + hdr_len, skb->len - 8 - hdr_len, pos))
+ return -1;
+
+ return 0;
+}
+
+
+static void rtllib_michael_mic_failure(struct net_device *dev,
+ struct rtllib_hdr_4addr *hdr,
+ int keyidx)
+{
+ union iwreq_data wrqu;
+ struct iw_michaelmicfailure ev;
+
+ /* TODO: needed parameters: count, keyid, key type, TSC */
+ memset(&ev, 0, sizeof(ev));
+ ev.flags = keyidx & IW_MICFAILURE_KEY_ID;
+ if (hdr->addr1[0] & 0x01)
+ ev.flags |= IW_MICFAILURE_GROUP;
+ else
+ ev.flags |= IW_MICFAILURE_PAIRWISE;
+ ev.src_addr.sa_family = ARPHRD_ETHER;
+ memcpy(ev.src_addr.sa_data, hdr->addr2, ETH_ALEN);
+ memset(&wrqu, 0, sizeof(wrqu));
+ wrqu.data.length = sizeof(ev);
+ wireless_send_event(dev, IWEVMICHAELMICFAILURE, &wrqu, (char *) &ev);
+}
+
+static int rtllib_michael_mic_verify(struct sk_buff *skb, int keyidx,
+ int hdr_len, void *priv)
+{
+ struct rtllib_tkip_data *tkey = priv;
+ u8 mic[8];
+ struct rtllib_hdr_4addr *hdr;
+
+ hdr = (struct rtllib_hdr_4addr *) skb->data;
+
+ if (!tkey->key_set)
+ return -1;
+
+ michael_mic_hdr(skb, tkey->rx_hdr);
+ if (RTLLIB_QOS_HAS_SEQ(le16_to_cpu(hdr->frame_ctl)))
+ tkey->rx_hdr[12] = *(skb->data + hdr_len - 2) & 0x07;
+
+ if (michael_mic(tkey->rx_tfm_michael, &tkey->key[24], tkey->rx_hdr,
+ skb->data + hdr_len, skb->len - 8 - hdr_len, mic))
+ return -1;
+
+ if (memcmp(mic, skb->data + skb->len - 8, 8) != 0) {
+ struct rtllib_hdr_4addr *hdr;
+
+ hdr = (struct rtllib_hdr_4addr *) skb->data;
+ printk(KERN_DEBUG
+ "%s: Michael MIC verification failed for MSDU from %pM keyidx=%d\n",
+ skb->dev ? skb->dev->name : "N/A", hdr->addr2,
+ keyidx);
+ printk(KERN_DEBUG "%d\n",
+ memcmp(mic, skb->data + skb->len - 8, 8) != 0);
+ if (skb->dev) {
+ pr_info("skb->dev != NULL\n");
+ rtllib_michael_mic_failure(skb->dev, hdr, keyidx);
+ }
+ tkey->dot11RSNAStatsTKIPLocalMICFailures++;
+ return -1;
+ }
+
+ /* Update TSC counters for RX now that the packet verification has
+ * completed.
+ */
+ tkey->rx_iv32 = tkey->rx_iv32_new;
+ tkey->rx_iv16 = tkey->rx_iv16_new;
+
+ skb_trim(skb, skb->len - 8);
+
+ return 0;
+}
+
+
+static int rtllib_tkip_set_key(void *key, int len, u8 *seq, void *priv)
+{
+ struct rtllib_tkip_data *tkey = priv;
+ int keyidx;
+ struct crypto_hash *tfm = tkey->tx_tfm_michael;
+ struct crypto_blkcipher *tfm2 = tkey->tx_tfm_arc4;
+ struct crypto_hash *tfm3 = tkey->rx_tfm_michael;
+ struct crypto_blkcipher *tfm4 = tkey->rx_tfm_arc4;
+
+ keyidx = tkey->key_idx;
+ memset(tkey, 0, sizeof(*tkey));
+ tkey->key_idx = keyidx;
+ tkey->tx_tfm_michael = tfm;
+ tkey->tx_tfm_arc4 = tfm2;
+ tkey->rx_tfm_michael = tfm3;
+ tkey->rx_tfm_arc4 = tfm4;
+
+ if (len == TKIP_KEY_LEN) {
+ memcpy(tkey->key, key, TKIP_KEY_LEN);
+ tkey->key_set = 1;
+ tkey->tx_iv16 = 1; /* TSC is initialized to 1 */
+ if (seq) {
+ tkey->rx_iv32 = (seq[5] << 24) | (seq[4] << 16) |
+ (seq[3] << 8) | seq[2];
+ tkey->rx_iv16 = (seq[1] << 8) | seq[0];
+ }
+ } else if (len == 0)
+ tkey->key_set = 0;
+ else
+ return -1;
+
+ return 0;
+}
+
+
+static int rtllib_tkip_get_key(void *key, int len, u8 *seq, void *priv)
+{
+ struct rtllib_tkip_data *tkey = priv;
+
+ if (len < TKIP_KEY_LEN)
+ return -1;
+
+ if (!tkey->key_set)
+ return 0;
+ memcpy(key, tkey->key, TKIP_KEY_LEN);
+
+ if (seq) {
+ /* Return the sequence number of the last transmitted frame. */
+ u16 iv16 = tkey->tx_iv16;
+ u32 iv32 = tkey->tx_iv32;
+
+ if (iv16 == 0)
+ iv32--;
+ iv16--;
+ seq[0] = tkey->tx_iv16;
+ seq[1] = tkey->tx_iv16 >> 8;
+ seq[2] = tkey->tx_iv32;
+ seq[3] = tkey->tx_iv32 >> 8;
+ seq[4] = tkey->tx_iv32 >> 16;
+ seq[5] = tkey->tx_iv32 >> 24;
+ }
+
+ return TKIP_KEY_LEN;
+}
+
+
+static void rtllib_tkip_print_stats(struct seq_file *m, void *priv)
+{
+ struct rtllib_tkip_data *tkip = priv;
+
+ seq_printf(m,
+ "key[%d] alg=TKIP key_set=%d tx_pn=%02x%02x%02x%02x%02x%02x rx_pn=%02x%02x%02x%02x%02x%02x replays=%d icv_errors=%d local_mic_failures=%d\n",
+ tkip->key_idx, tkip->key_set,
+ (tkip->tx_iv32 >> 24) & 0xff,
+ (tkip->tx_iv32 >> 16) & 0xff,
+ (tkip->tx_iv32 >> 8) & 0xff,
+ tkip->tx_iv32 & 0xff,
+ (tkip->tx_iv16 >> 8) & 0xff,
+ tkip->tx_iv16 & 0xff,
+ (tkip->rx_iv32 >> 24) & 0xff,
+ (tkip->rx_iv32 >> 16) & 0xff,
+ (tkip->rx_iv32 >> 8) & 0xff,
+ tkip->rx_iv32 & 0xff,
+ (tkip->rx_iv16 >> 8) & 0xff,
+ tkip->rx_iv16 & 0xff,
+ tkip->dot11RSNAStatsTKIPReplays,
+ tkip->dot11RSNAStatsTKIPICVErrors,
+ tkip->dot11RSNAStatsTKIPLocalMICFailures);
+}
+
+static struct lib80211_crypto_ops rtllib_crypt_tkip = {
+ .name = "R-TKIP",
+ .init = rtllib_tkip_init,
+ .deinit = rtllib_tkip_deinit,
+ .encrypt_mpdu = rtllib_tkip_encrypt,
+ .decrypt_mpdu = rtllib_tkip_decrypt,
+ .encrypt_msdu = rtllib_michael_mic_add,
+ .decrypt_msdu = rtllib_michael_mic_verify,
+ .set_key = rtllib_tkip_set_key,
+ .get_key = rtllib_tkip_get_key,
+ .print_stats = rtllib_tkip_print_stats,
+ .extra_mpdu_prefix_len = 4 + 4, /* IV + ExtIV */
+ .extra_mpdu_postfix_len = 4, /* ICV */
+ .extra_msdu_postfix_len = 8, /* MIC */
+ .owner = THIS_MODULE,
+};
+
+
+static int __init rtllib_crypto_tkip_init(void)
+{
+ return lib80211_register_crypto_ops(&rtllib_crypt_tkip);
+}
+
+
+static void __exit rtllib_crypto_tkip_exit(void)
+{
+ lib80211_unregister_crypto_ops(&rtllib_crypt_tkip);
+}
+
+module_init(rtllib_crypto_tkip_init);
+module_exit(rtllib_crypto_tkip_exit);
+
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/rtl8192e/rtllib_crypt_wep.c b/drivers/staging/rtl8192e/rtllib_crypt_wep.c
new file mode 100644
index 000000000..21d7eee4c
--- /dev/null
+++ b/drivers/staging/rtl8192e/rtllib_crypt_wep.c
@@ -0,0 +1,289 @@
+/*
+ * Host AP crypt: host-based WEP encryption implementation for Host AP driver
+ *
+ * Copyright (c) 2002-2004, Jouni Malinen <jkmaline@cc.hut.fi>
+ *
+ * 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. See README and COPYING for
+ * more details.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/random.h>
+#include <linux/skbuff.h>
+#include <linux/string.h>
+#include "rtllib.h"
+
+#include <linux/crypto.h>
+
+#include <linux/scatterlist.h>
+#include <linux/crc32.h>
+
+struct prism2_wep_data {
+ u32 iv;
+#define WEP_KEY_LEN 13
+ u8 key[WEP_KEY_LEN + 1];
+ u8 key_len;
+ u8 key_idx;
+ struct crypto_blkcipher *tx_tfm;
+ struct crypto_blkcipher *rx_tfm;
+};
+
+
+static void *prism2_wep_init(int keyidx)
+{
+ struct prism2_wep_data *priv;
+
+ priv = kzalloc(sizeof(*priv), GFP_ATOMIC);
+ if (priv == NULL)
+ goto fail;
+ priv->key_idx = keyidx;
+
+ priv->tx_tfm = crypto_alloc_blkcipher("ecb(arc4)", 0, CRYPTO_ALG_ASYNC);
+ if (IS_ERR(priv->tx_tfm)) {
+ pr_debug("rtllib_crypt_wep: could not allocate crypto API arc4\n");
+ priv->tx_tfm = NULL;
+ goto fail;
+ }
+ priv->rx_tfm = crypto_alloc_blkcipher("ecb(arc4)", 0, CRYPTO_ALG_ASYNC);
+ if (IS_ERR(priv->rx_tfm)) {
+ pr_debug("rtllib_crypt_wep: could not allocate crypto API arc4\n");
+ priv->rx_tfm = NULL;
+ goto fail;
+ }
+
+ /* start WEP IV from a random value */
+ get_random_bytes(&priv->iv, 4);
+
+ return priv;
+
+fail:
+ if (priv) {
+ if (priv->tx_tfm)
+ crypto_free_blkcipher(priv->tx_tfm);
+ if (priv->rx_tfm)
+ crypto_free_blkcipher(priv->rx_tfm);
+ kfree(priv);
+ }
+ return NULL;
+}
+
+
+static void prism2_wep_deinit(void *priv)
+{
+ struct prism2_wep_data *_priv = priv;
+
+ if (_priv) {
+ if (_priv->tx_tfm)
+ crypto_free_blkcipher(_priv->tx_tfm);
+ if (_priv->rx_tfm)
+ crypto_free_blkcipher(_priv->rx_tfm);
+ }
+ kfree(priv);
+}
+
+/* Perform WEP encryption on given skb that has at least 4 bytes of headroom
+ * for IV and 4 bytes of tailroom for ICV. Both IV and ICV will be transmitted,
+ * so the payload length increases with 8 bytes.
+ *
+ * WEP frame payload: IV + TX key idx, RC4(data), ICV = RC4(CRC32(data))
+ */
+static int prism2_wep_encrypt(struct sk_buff *skb, int hdr_len, void *priv)
+{
+ struct prism2_wep_data *wep = priv;
+ u32 klen, len;
+ u8 key[WEP_KEY_LEN + 3];
+ u8 *pos;
+ struct cb_desc *tcb_desc = (struct cb_desc *)(skb->cb +
+ MAX_DEV_ADDR_SIZE);
+ struct blkcipher_desc desc = {.tfm = wep->tx_tfm};
+ u32 crc;
+ u8 *icv;
+ struct scatterlist sg;
+
+ if (skb_headroom(skb) < 4 || skb_tailroom(skb) < 4 ||
+ skb->len < hdr_len){
+ pr_err("Error!!! headroom=%d tailroom=%d skblen=%d hdr_len=%d\n",
+ skb_headroom(skb), skb_tailroom(skb), skb->len, hdr_len);
+ return -1;
+ }
+ len = skb->len - hdr_len;
+ pos = skb_push(skb, 4);
+ memmove(pos, pos + 4, hdr_len);
+ pos += hdr_len;
+
+ klen = 3 + wep->key_len;
+
+ wep->iv++;
+
+ /* Fluhrer, Mantin, and Shamir have reported weaknesses in the key
+ * scheduling algorithm of RC4. At least IVs (KeyByte + 3, 0xff, N)
+ * can be used to speedup attacks, so avoid using them.
+ */
+ if ((wep->iv & 0xff00) == 0xff00) {
+ u8 B = (wep->iv >> 16) & 0xff;
+
+ if (B >= 3 && B < klen)
+ wep->iv += 0x0100;
+ }
+
+ /* Prepend 24-bit IV to RC4 key and TX frame */
+ *pos++ = key[0] = (wep->iv >> 16) & 0xff;
+ *pos++ = key[1] = (wep->iv >> 8) & 0xff;
+ *pos++ = key[2] = wep->iv & 0xff;
+ *pos++ = wep->key_idx << 6;
+
+ /* Copy rest of the WEP key (the secret part) */
+ memcpy(key + 3, wep->key, wep->key_len);
+
+ if (!tcb_desc->bHwSec) {
+
+ /* Append little-endian CRC32 and encrypt it to produce ICV */
+ crc = ~crc32_le(~0, pos, len);
+ icv = skb_put(skb, 4);
+ icv[0] = crc;
+ icv[1] = crc >> 8;
+ icv[2] = crc >> 16;
+ icv[3] = crc >> 24;
+
+ sg_init_one(&sg, pos, len+4);
+ crypto_blkcipher_setkey(wep->tx_tfm, key, klen);
+ return crypto_blkcipher_encrypt(&desc, &sg, &sg, len + 4);
+ }
+
+ return 0;
+}
+
+
+/* Perform WEP decryption on given struct buffer. Buffer includes whole WEP
+ * part of the frame: IV (4 bytes), encrypted payload (including SNAP header),
+ * ICV (4 bytes). len includes both IV and ICV.
+ *
+ * Returns 0 if frame was decrypted successfully and ICV was correct and -1 on
+ * failure. If frame is OK, IV and ICV will be removed.
+ */
+static int prism2_wep_decrypt(struct sk_buff *skb, int hdr_len, void *priv)
+{
+ struct prism2_wep_data *wep = priv;
+ u32 klen, plen;
+ u8 key[WEP_KEY_LEN + 3];
+ u8 keyidx, *pos;
+ struct cb_desc *tcb_desc = (struct cb_desc *)(skb->cb +
+ MAX_DEV_ADDR_SIZE);
+ struct blkcipher_desc desc = {.tfm = wep->rx_tfm};
+ u32 crc;
+ u8 icv[4];
+ struct scatterlist sg;
+
+ if (skb->len < hdr_len + 8)
+ return -1;
+
+ pos = skb->data + hdr_len;
+ key[0] = *pos++;
+ key[1] = *pos++;
+ key[2] = *pos++;
+ keyidx = *pos++ >> 6;
+ if (keyidx != wep->key_idx)
+ return -1;
+
+ klen = 3 + wep->key_len;
+
+ /* Copy rest of the WEP key (the secret part) */
+ memcpy(key + 3, wep->key, wep->key_len);
+
+ /* Apply RC4 to data and compute CRC32 over decrypted data */
+ plen = skb->len - hdr_len - 8;
+
+ if (!tcb_desc->bHwSec) {
+ sg_init_one(&sg, pos, plen+4);
+ crypto_blkcipher_setkey(wep->rx_tfm, key, klen);
+ if (crypto_blkcipher_decrypt(&desc, &sg, &sg, plen + 4))
+ return -7;
+ crc = ~crc32_le(~0, pos, plen);
+ icv[0] = crc;
+ icv[1] = crc >> 8;
+ icv[2] = crc >> 16;
+ icv[3] = crc >> 24;
+ if (memcmp(icv, pos + plen, 4) != 0) {
+ /* ICV mismatch - drop frame */
+ return -2;
+ }
+ }
+ /* Remove IV and ICV */
+ memmove(skb->data + 4, skb->data, hdr_len);
+ skb_pull(skb, 4);
+ skb_trim(skb, skb->len - 4);
+
+ return 0;
+}
+
+
+static int prism2_wep_set_key(void *key, int len, u8 *seq, void *priv)
+{
+ struct prism2_wep_data *wep = priv;
+
+ if (len < 0 || len > WEP_KEY_LEN)
+ return -1;
+
+ memcpy(wep->key, key, len);
+ wep->key_len = len;
+
+ return 0;
+}
+
+
+static int prism2_wep_get_key(void *key, int len, u8 *seq, void *priv)
+{
+ struct prism2_wep_data *wep = priv;
+
+ if (len < wep->key_len)
+ return -1;
+
+ memcpy(key, wep->key, wep->key_len);
+
+ return wep->key_len;
+}
+
+
+static void prism2_wep_print_stats(struct seq_file *m, void *priv)
+{
+ struct prism2_wep_data *wep = priv;
+
+ seq_printf(m, "key[%d] alg=WEP len=%d\n", wep->key_idx, wep->key_len);
+}
+
+static struct lib80211_crypto_ops rtllib_crypt_wep = {
+ .name = "R-WEP",
+ .init = prism2_wep_init,
+ .deinit = prism2_wep_deinit,
+ .encrypt_mpdu = prism2_wep_encrypt,
+ .decrypt_mpdu = prism2_wep_decrypt,
+ .encrypt_msdu = NULL,
+ .decrypt_msdu = NULL,
+ .set_key = prism2_wep_set_key,
+ .get_key = prism2_wep_get_key,
+ .print_stats = prism2_wep_print_stats,
+ .extra_mpdu_prefix_len = 4, /* IV */
+ .extra_mpdu_postfix_len = 4, /* ICV */
+ .owner = THIS_MODULE,
+};
+
+
+static int __init rtllib_crypto_wep_init(void)
+{
+ return lib80211_register_crypto_ops(&rtllib_crypt_wep);
+}
+
+
+static void __exit rtllib_crypto_wep_exit(void)
+{
+ lib80211_unregister_crypto_ops(&rtllib_crypt_wep);
+}
+
+module_init(rtllib_crypto_wep_init);
+module_exit(rtllib_crypto_wep_exit);
+
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/rtl8192e/rtllib_debug.h b/drivers/staging/rtl8192e/rtllib_debug.h
new file mode 100644
index 000000000..119729d31
--- /dev/null
+++ b/drivers/staging/rtl8192e/rtllib_debug.h
@@ -0,0 +1,88 @@
+/******************************************************************************
+ * Copyright(c) 2008 - 2010 Realtek Corporation. All rights reserved.
+ *
+ * Based on the r8180 driver, which is:
+ * Copyright 2004-2005 Andrea Merello <andrea.merello@gmail.com>, et al.
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ * The full GNU General Public License is included in this distribution in the
+ * file called LICENSE.
+ *
+ * Contact Information:
+ * wlanfae <wlanfae@realtek.com>
+******************************************************************************/
+#ifndef _RTL_DEBUG_H
+#define _RTL_DEBUG_H
+
+/* Allow files to override DRV_NAME */
+#ifndef DRV_NAME
+#define DRV_NAME "rtllib_92e"
+#endif
+
+#define DMESG(x, a...)
+
+extern u32 rt_global_debug_component;
+
+/* These are the defines for rt_global_debug_component */
+enum RTL_DEBUG {
+ COMP_TRACE = (1 << 0),
+ COMP_DBG = (1 << 1),
+ COMP_INIT = (1 << 2),
+ COMP_RECV = (1 << 3),
+ COMP_SEND = (1 << 4),
+ COMP_CMD = (1 << 5),
+ COMP_POWER = (1 << 6),
+ COMP_EPROM = (1 << 7),
+ COMP_SWBW = (1 << 8),
+ COMP_SEC = (1 << 9),
+ COMP_LPS = (1 << 10),
+ COMP_QOS = (1 << 11),
+ COMP_RATE = (1 << 12),
+ COMP_RXDESC = (1 << 13),
+ COMP_PHY = (1 << 14),
+ COMP_DIG = (1 << 15),
+ COMP_TXAGC = (1 << 16),
+ COMP_HALDM = (1 << 17),
+ COMP_POWER_TRACKING = (1 << 18),
+ COMP_CH = (1 << 19),
+ COMP_RF = (1 << 20),
+ COMP_FIRMWARE = (1 << 21),
+ COMP_HT = (1 << 22),
+ COMP_RESET = (1 << 23),
+ COMP_CMDPKT = (1 << 24),
+ COMP_SCAN = (1 << 25),
+ COMP_PS = (1 << 26),
+ COMP_DOWN = (1 << 27),
+ COMP_INTR = (1 << 28),
+ COMP_LED = (1 << 29),
+ COMP_MLME = (1 << 30),
+ COMP_ERR = (1 << 31)
+};
+
+#define RT_TRACE(component, x, args...) \
+do { \
+ if (rt_global_debug_component & component) \
+ printk(KERN_DEBUG DRV_NAME ":" x "\n" , \
+ ##args);\
+} while (0)
+
+#define assert(expr) \
+do { \
+ if (!(expr)) { \
+ pr_info("Assertion failed! %s,%s,%s,line=%d\n", \
+ #expr, __FILE__, __func__, __LINE__); \
+ } \
+} while (0)
+
+#endif
diff --git a/drivers/staging/rtl8192e/rtllib_module.c b/drivers/staging/rtl8192e/rtllib_module.c
new file mode 100644
index 000000000..32cc8df9d
--- /dev/null
+++ b/drivers/staging/rtl8192e/rtllib_module.c
@@ -0,0 +1,268 @@
+/*******************************************************************************
+
+ Copyright(c) 2004 Intel Corporation. All rights reserved.
+
+ Portions of this file are based on the WEP enablement code provided by the
+ Host AP project hostap-drivers v0.1.3
+ Copyright (c) 2001-2002, SSH Communications Security Corp and Jouni Malinen
+ <jkmaline@cc.hut.fi>
+ Copyright (c) 2002-2003, Jouni Malinen <jkmaline@cc.hut.fi>
+
+ This program is free software; you can redistribute it and/or modify it
+ under the terms of version 2 of the GNU General Public License as
+ published by the Free Software Foundation.
+
+ 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.
+
+ The full GNU General Public License is included in this distribution in the
+ file called LICENSE.
+
+ Contact Information:
+ James P. Ketrenos <ipw2100-admin@linux.intel.com>
+ Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+
+*******************************************************************************/
+
+#include <linux/compiler.h>
+#include <linux/errno.h>
+#include <linux/if_arp.h>
+#include <linux/in6.h>
+#include <linux/in.h>
+#include <linux/ip.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/netdevice.h>
+#include <linux/pci.h>
+#include <linux/proc_fs.h>
+#include <linux/skbuff.h>
+#include <linux/slab.h>
+#include <linux/tcp.h>
+#include <linux/types.h>
+#include <linux/wireless.h>
+#include <linux/etherdevice.h>
+#include <linux/uaccess.h>
+#include <net/arp.h>
+
+#include "rtllib.h"
+
+
+u32 rt_global_debug_component = COMP_ERR;
+EXPORT_SYMBOL(rt_global_debug_component);
+
+
+void _setup_timer(struct timer_list *ptimer, void *fun, unsigned long data)
+{
+ ptimer->function = fun;
+ ptimer->data = data;
+ init_timer(ptimer);
+}
+
+static inline int rtllib_networks_allocate(struct rtllib_device *ieee)
+{
+ if (ieee->networks)
+ return 0;
+
+ ieee->networks = kzalloc(
+ MAX_NETWORK_COUNT * sizeof(struct rtllib_network),
+ GFP_KERNEL);
+ if (!ieee->networks)
+ return -ENOMEM;
+
+ return 0;
+}
+
+static inline void rtllib_networks_free(struct rtllib_device *ieee)
+{
+ if (!ieee->networks)
+ return;
+ kfree(ieee->networks);
+ ieee->networks = NULL;
+}
+
+static inline void rtllib_networks_initialize(struct rtllib_device *ieee)
+{
+ int i;
+
+ INIT_LIST_HEAD(&ieee->network_free_list);
+ INIT_LIST_HEAD(&ieee->network_list);
+ for (i = 0; i < MAX_NETWORK_COUNT; i++)
+ list_add_tail(&ieee->networks[i].list,
+ &ieee->network_free_list);
+}
+
+struct net_device *alloc_rtllib(int sizeof_priv)
+{
+ struct rtllib_device *ieee = NULL;
+ struct net_device *dev;
+ int i, err;
+
+ RTLLIB_DEBUG_INFO("Initializing...\n");
+
+ dev = alloc_etherdev(sizeof(struct rtllib_device) + sizeof_priv);
+ if (!dev) {
+ RTLLIB_ERROR("Unable to network device.\n");
+ return NULL;
+ }
+ ieee = (struct rtllib_device *)netdev_priv_rsl(dev);
+ memset(ieee, 0, sizeof(struct rtllib_device)+sizeof_priv);
+ ieee->dev = dev;
+
+ err = rtllib_networks_allocate(ieee);
+ if (err) {
+ RTLLIB_ERROR("Unable to allocate beacon storage: %d\n",
+ err);
+ goto failed;
+ }
+ rtllib_networks_initialize(ieee);
+
+
+ /* Default fragmentation threshold is maximum payload size */
+ ieee->fts = DEFAULT_FTS;
+ ieee->scan_age = DEFAULT_MAX_SCAN_AGE;
+ ieee->open_wep = 1;
+
+ /* Default to enabling full open WEP with host based encrypt/decrypt */
+ ieee->host_encrypt = 1;
+ ieee->host_decrypt = 1;
+ ieee->ieee802_1x = 1; /* Default to supporting 802.1x */
+
+ ieee->rtllib_ap_sec_type = rtllib_ap_sec_type;
+
+ spin_lock_init(&ieee->lock);
+ spin_lock_init(&ieee->wpax_suitlist_lock);
+ spin_lock_init(&ieee->bw_spinlock);
+ spin_lock_init(&ieee->reorder_spinlock);
+ atomic_set(&(ieee->atm_chnlop), 0);
+ atomic_set(&(ieee->atm_swbw), 0);
+
+ /* SAM FIXME */
+ lib80211_crypt_info_init(&ieee->crypt_info, "RTLLIB", &ieee->lock);
+
+ ieee->bHalfNMode = false;
+ ieee->wpa_enabled = 0;
+ ieee->tkip_countermeasures = 0;
+ ieee->drop_unencrypted = 0;
+ ieee->privacy_invoked = 0;
+ ieee->ieee802_1x = 1;
+ ieee->raw_tx = 0;
+ ieee->hwsec_active = 0;
+
+ memset(ieee->swcamtable, 0, sizeof(struct sw_cam_table) * 32);
+ rtllib_softmac_init(ieee);
+
+ ieee->pHTInfo = kzalloc(sizeof(struct rt_hi_throughput), GFP_KERNEL);
+ if (ieee->pHTInfo == NULL)
+ return NULL;
+
+ HTUpdateDefaultSetting(ieee);
+ HTInitializeHTInfo(ieee);
+ TSInitialize(ieee);
+ for (i = 0; i < IEEE_IBSS_MAC_HASH_SIZE; i++)
+ INIT_LIST_HEAD(&ieee->ibss_mac_hash[i]);
+
+ for (i = 0; i < 17; i++) {
+ ieee->last_rxseq_num[i] = -1;
+ ieee->last_rxfrag_num[i] = -1;
+ ieee->last_packet_time[i] = 0;
+ }
+
+ return dev;
+
+ failed:
+ free_netdev(dev);
+ return NULL;
+}
+EXPORT_SYMBOL(alloc_rtllib);
+
+void free_rtllib(struct net_device *dev)
+{
+ struct rtllib_device *ieee = (struct rtllib_device *)
+ netdev_priv_rsl(dev);
+
+ kfree(ieee->pHTInfo);
+ ieee->pHTInfo = NULL;
+ rtllib_softmac_free(ieee);
+
+ lib80211_crypt_info_free(&ieee->crypt_info);
+
+ rtllib_networks_free(ieee);
+ free_netdev(dev);
+}
+EXPORT_SYMBOL(free_rtllib);
+
+u32 rtllib_debug_level;
+static int debug = RTLLIB_DL_ERR;
+static struct proc_dir_entry *rtllib_proc;
+
+static int show_debug_level(struct seq_file *m, void *v)
+{
+ seq_printf(m, "0x%08X\n", rtllib_debug_level);
+
+ return 0;
+}
+
+static ssize_t write_debug_level(struct file *file, const char __user *buffer,
+ size_t count, loff_t *ppos)
+{
+ unsigned long val;
+ int err = kstrtoul_from_user(buffer, count, 0, &val);
+
+ if (err)
+ return err;
+ rtllib_debug_level = val;
+ return count;
+}
+
+static int open_debug_level(struct inode *inode, struct file *file)
+{
+ return single_open(file, show_debug_level, NULL);
+}
+
+static const struct file_operations fops = {
+ .open = open_debug_level,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .write = write_debug_level,
+ .release = single_release,
+};
+
+static int __init rtllib_init(void)
+{
+ struct proc_dir_entry *e;
+
+ rtllib_debug_level = debug;
+ rtllib_proc = proc_mkdir(DRV_NAME, init_net.proc_net);
+ if (rtllib_proc == NULL) {
+ RTLLIB_ERROR("Unable to create " DRV_NAME
+ " proc directory\n");
+ return -EIO;
+ }
+ e = proc_create("debug_level", S_IRUGO | S_IWUSR, rtllib_proc, &fops);
+ if (!e) {
+ remove_proc_entry(DRV_NAME, init_net.proc_net);
+ rtllib_proc = NULL;
+ return -EIO;
+ }
+ return 0;
+}
+
+static void __exit rtllib_exit(void)
+{
+ if (rtllib_proc) {
+ remove_proc_entry("debug_level", rtllib_proc);
+ remove_proc_entry(DRV_NAME, init_net.proc_net);
+ rtllib_proc = NULL;
+ }
+}
+
+module_init(rtllib_init);
+module_exit(rtllib_exit);
+
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/rtl8192e/rtllib_rx.c b/drivers/staging/rtl8192e/rtllib_rx.c
new file mode 100644
index 000000000..fe3e7e127
--- /dev/null
+++ b/drivers/staging/rtl8192e/rtllib_rx.c
@@ -0,0 +1,2678 @@
+/*
+ * Original code based Host AP (software wireless LAN access point) driver
+ * for Intersil Prism2/2.5/3 - hostap.o module, common routines
+ *
+ * Copyright (c) 2001-2002, SSH Communications Security Corp and Jouni Malinen
+ * <jkmaline@cc.hut.fi>
+ * Copyright (c) 2002-2003, Jouni Malinen <jkmaline@cc.hut.fi>
+ * Copyright (c) 2004, Intel Corporation
+ *
+ * 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. See README and COPYING for
+ * more details.
+ ******************************************************************************
+
+ Few modifications for Realtek's Wi-Fi drivers by
+ Andrea Merello <andrea.merello@gmail.com>
+
+ A special thanks goes to Realtek for their support !
+
+******************************************************************************/
+
+
+#include <linux/compiler.h>
+#include <linux/errno.h>
+#include <linux/if_arp.h>
+#include <linux/in6.h>
+#include <linux/in.h>
+#include <linux/ip.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/netdevice.h>
+#include <linux/pci.h>
+#include <linux/proc_fs.h>
+#include <linux/skbuff.h>
+#include <linux/slab.h>
+#include <linux/tcp.h>
+#include <linux/types.h>
+#include <linux/wireless.h>
+#include <linux/etherdevice.h>
+#include <linux/uaccess.h>
+#include <linux/ctype.h>
+
+#include "rtllib.h"
+#include "dot11d.h"
+
+static inline void rtllib_monitor_rx(struct rtllib_device *ieee,
+ struct sk_buff *skb, struct rtllib_rx_stats *rx_status,
+ size_t hdr_length)
+{
+ skb->dev = ieee->dev;
+ skb_reset_mac_header(skb);
+ skb_pull(skb, hdr_length);
+ skb->pkt_type = PACKET_OTHERHOST;
+ skb->protocol = htons(ETH_P_80211_RAW);
+ memset(skb->cb, 0, sizeof(skb->cb));
+ netif_rx(skb);
+}
+
+/* Called only as a tasklet (software IRQ) */
+static struct rtllib_frag_entry *
+rtllib_frag_cache_find(struct rtllib_device *ieee, unsigned int seq,
+ unsigned int frag, u8 tid, u8 *src, u8 *dst)
+{
+ struct rtllib_frag_entry *entry;
+ int i;
+
+ for (i = 0; i < RTLLIB_FRAG_CACHE_LEN; i++) {
+ entry = &ieee->frag_cache[tid][i];
+ if (entry->skb != NULL &&
+ time_after(jiffies, entry->first_frag_time + 2 * HZ)) {
+ RTLLIB_DEBUG_FRAG(
+ "expiring fragment cache entry seq=%u last_frag=%u\n",
+ entry->seq, entry->last_frag);
+ dev_kfree_skb_any(entry->skb);
+ entry->skb = NULL;
+ }
+
+ if (entry->skb != NULL && entry->seq == seq &&
+ (entry->last_frag + 1 == frag || frag == -1) &&
+ memcmp(entry->src_addr, src, ETH_ALEN) == 0 &&
+ memcmp(entry->dst_addr, dst, ETH_ALEN) == 0)
+ return entry;
+ }
+
+ return NULL;
+}
+
+/* Called only as a tasklet (software IRQ) */
+static struct sk_buff *
+rtllib_frag_cache_get(struct rtllib_device *ieee,
+ struct rtllib_hdr_4addr *hdr)
+{
+ struct sk_buff *skb = NULL;
+ u16 fc = le16_to_cpu(hdr->frame_ctl);
+ u16 sc = le16_to_cpu(hdr->seq_ctl);
+ unsigned int frag = WLAN_GET_SEQ_FRAG(sc);
+ unsigned int seq = WLAN_GET_SEQ_SEQ(sc);
+ struct rtllib_frag_entry *entry;
+ struct rtllib_hdr_3addrqos *hdr_3addrqos;
+ struct rtllib_hdr_4addrqos *hdr_4addrqos;
+ u8 tid;
+
+ if (((fc & RTLLIB_FCTL_DSTODS) == RTLLIB_FCTL_DSTODS) && RTLLIB_QOS_HAS_SEQ(fc)) {
+ hdr_4addrqos = (struct rtllib_hdr_4addrqos *)hdr;
+ tid = le16_to_cpu(hdr_4addrqos->qos_ctl) & RTLLIB_QCTL_TID;
+ tid = UP2AC(tid);
+ tid++;
+ } else if (RTLLIB_QOS_HAS_SEQ(fc)) {
+ hdr_3addrqos = (struct rtllib_hdr_3addrqos *)hdr;
+ tid = le16_to_cpu(hdr_3addrqos->qos_ctl) & RTLLIB_QCTL_TID;
+ tid = UP2AC(tid);
+ tid++;
+ } else {
+ tid = 0;
+ }
+
+ if (frag == 0) {
+ /* Reserve enough space to fit maximum frame length */
+ skb = dev_alloc_skb(ieee->dev->mtu +
+ sizeof(struct rtllib_hdr_4addr) +
+ 8 /* LLC */ +
+ 2 /* alignment */ +
+ 8 /* WEP */ +
+ ETH_ALEN /* WDS */ +
+ (RTLLIB_QOS_HAS_SEQ(fc) ? 2 : 0) /* QOS Control */);
+ if (skb == NULL)
+ return NULL;
+
+ entry = &ieee->frag_cache[tid][ieee->frag_next_idx[tid]];
+ ieee->frag_next_idx[tid]++;
+ if (ieee->frag_next_idx[tid] >= RTLLIB_FRAG_CACHE_LEN)
+ ieee->frag_next_idx[tid] = 0;
+
+ if (entry->skb != NULL)
+ dev_kfree_skb_any(entry->skb);
+
+ entry->first_frag_time = jiffies;
+ entry->seq = seq;
+ entry->last_frag = frag;
+ entry->skb = skb;
+ memcpy(entry->src_addr, hdr->addr2, ETH_ALEN);
+ memcpy(entry->dst_addr, hdr->addr1, ETH_ALEN);
+ } else {
+ /* received a fragment of a frame for which the head fragment
+ * should have already been received
+ */
+ entry = rtllib_frag_cache_find(ieee, seq, frag, tid, hdr->addr2,
+ hdr->addr1);
+ if (entry != NULL) {
+ entry->last_frag = frag;
+ skb = entry->skb;
+ }
+ }
+
+ return skb;
+}
+
+
+/* Called only as a tasklet (software IRQ) */
+static int rtllib_frag_cache_invalidate(struct rtllib_device *ieee,
+ struct rtllib_hdr_4addr *hdr)
+{
+ u16 fc = le16_to_cpu(hdr->frame_ctl);
+ u16 sc = le16_to_cpu(hdr->seq_ctl);
+ unsigned int seq = WLAN_GET_SEQ_SEQ(sc);
+ struct rtllib_frag_entry *entry;
+ struct rtllib_hdr_3addrqos *hdr_3addrqos;
+ struct rtllib_hdr_4addrqos *hdr_4addrqos;
+ u8 tid;
+
+ if (((fc & RTLLIB_FCTL_DSTODS) == RTLLIB_FCTL_DSTODS) && RTLLIB_QOS_HAS_SEQ(fc)) {
+ hdr_4addrqos = (struct rtllib_hdr_4addrqos *)hdr;
+ tid = le16_to_cpu(hdr_4addrqos->qos_ctl) & RTLLIB_QCTL_TID;
+ tid = UP2AC(tid);
+ tid++;
+ } else if (RTLLIB_QOS_HAS_SEQ(fc)) {
+ hdr_3addrqos = (struct rtllib_hdr_3addrqos *)hdr;
+ tid = le16_to_cpu(hdr_3addrqos->qos_ctl) & RTLLIB_QCTL_TID;
+ tid = UP2AC(tid);
+ tid++;
+ } else {
+ tid = 0;
+ }
+
+ entry = rtllib_frag_cache_find(ieee, seq, -1, tid, hdr->addr2,
+ hdr->addr1);
+
+ if (entry == NULL) {
+ RTLLIB_DEBUG_FRAG(
+ "could not invalidate fragment cache entry (seq=%u)\n", seq);
+ return -1;
+ }
+
+ entry->skb = NULL;
+ return 0;
+}
+
+/* rtllib_rx_frame_mgtmt
+ *
+ * Responsible for handling management control frames
+ *
+ * Called by rtllib_rx
+ */
+static inline int
+rtllib_rx_frame_mgmt(struct rtllib_device *ieee, struct sk_buff *skb,
+ struct rtllib_rx_stats *rx_stats, u16 type,
+ u16 stype)
+{
+ /* On the struct stats definition there is written that
+ * this is not mandatory.... but seems that the probe
+ * response parser uses it
+ */
+ struct rtllib_hdr_3addr *hdr = (struct rtllib_hdr_3addr *)skb->data;
+
+ rx_stats->len = skb->len;
+ rtllib_rx_mgt(ieee, skb, rx_stats);
+ if ((memcmp(hdr->addr1, ieee->dev->dev_addr, ETH_ALEN))) {
+ dev_kfree_skb_any(skb);
+ return 0;
+ }
+ rtllib_rx_frame_softmac(ieee, skb, rx_stats, type, stype);
+
+ dev_kfree_skb_any(skb);
+
+ return 0;
+}
+
+/* See IEEE 802.1H for LLC/SNAP encapsulation/decapsulation
+ * Ethernet-II snap header (RFC1042 for most EtherTypes)
+ */
+static unsigned char rfc1042_header[] = {
+ 0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00
+};
+/* Bridge-Tunnel header (for EtherTypes ETH_P_AARP and ETH_P_IPX) */
+static unsigned char bridge_tunnel_header[] = {
+ 0xaa, 0xaa, 0x03, 0x00, 0x00, 0xf8
+};
+/* No encapsulation header if EtherType < 0x600 (=length) */
+
+/* Called by rtllib_rx_frame_decrypt */
+static int rtllib_is_eapol_frame(struct rtllib_device *ieee,
+ struct sk_buff *skb, size_t hdrlen)
+{
+ struct net_device *dev = ieee->dev;
+ u16 fc, ethertype;
+ struct rtllib_hdr_4addr *hdr;
+ u8 *pos;
+
+ if (skb->len < 24)
+ return 0;
+
+ hdr = (struct rtllib_hdr_4addr *) skb->data;
+ fc = le16_to_cpu(hdr->frame_ctl);
+
+ /* check that the frame is unicast frame to us */
+ if ((fc & (RTLLIB_FCTL_TODS | RTLLIB_FCTL_FROMDS)) ==
+ RTLLIB_FCTL_TODS &&
+ memcmp(hdr->addr1, dev->dev_addr, ETH_ALEN) == 0 &&
+ memcmp(hdr->addr3, dev->dev_addr, ETH_ALEN) == 0) {
+ /* ToDS frame with own addr BSSID and DA */
+ } else if ((fc & (RTLLIB_FCTL_TODS | RTLLIB_FCTL_FROMDS)) ==
+ RTLLIB_FCTL_FROMDS &&
+ memcmp(hdr->addr1, dev->dev_addr, ETH_ALEN) == 0) {
+ /* FromDS frame with own addr as DA */
+ } else
+ return 0;
+
+ if (skb->len < 24 + 8)
+ return 0;
+
+ /* check for port access entity Ethernet type */
+ pos = skb->data + hdrlen;
+ ethertype = (pos[6] << 8) | pos[7];
+ if (ethertype == ETH_P_PAE)
+ return 1;
+
+ return 0;
+}
+
+/* Called only as a tasklet (software IRQ), by rtllib_rx */
+static inline int
+rtllib_rx_frame_decrypt(struct rtllib_device *ieee, struct sk_buff *skb,
+ struct lib80211_crypt_data *crypt)
+{
+ struct rtllib_hdr_4addr *hdr;
+ int res, hdrlen;
+
+ if (crypt == NULL || crypt->ops->decrypt_mpdu == NULL)
+ return 0;
+
+ if (ieee->hwsec_active) {
+ struct cb_desc *tcb_desc = (struct cb_desc *)(skb->cb + MAX_DEV_ADDR_SIZE);
+
+ tcb_desc->bHwSec = 1;
+
+ if (ieee->need_sw_enc)
+ tcb_desc->bHwSec = 0;
+ }
+
+ hdr = (struct rtllib_hdr_4addr *) skb->data;
+ hdrlen = rtllib_get_hdrlen(le16_to_cpu(hdr->frame_ctl));
+
+ atomic_inc(&crypt->refcnt);
+ res = crypt->ops->decrypt_mpdu(skb, hdrlen, crypt->priv);
+ atomic_dec(&crypt->refcnt);
+ if (res < 0) {
+ RTLLIB_DEBUG_DROP(
+ "decryption failed (SA= %pM) res=%d\n", hdr->addr2, res);
+ if (res == -2)
+ RTLLIB_DEBUG_DROP("Decryption failed ICV mismatch (key %d)\n",
+ skb->data[hdrlen + 3] >> 6);
+ ieee->ieee_stats.rx_discards_undecryptable++;
+ return -1;
+ }
+
+ return res;
+}
+
+
+/* Called only as a tasklet (software IRQ), by rtllib_rx */
+static inline int
+rtllib_rx_frame_decrypt_msdu(struct rtllib_device *ieee, struct sk_buff *skb,
+ int keyidx, struct lib80211_crypt_data *crypt)
+{
+ struct rtllib_hdr_4addr *hdr;
+ int res, hdrlen;
+
+ if (crypt == NULL || crypt->ops->decrypt_msdu == NULL)
+ return 0;
+ if (ieee->hwsec_active) {
+ struct cb_desc *tcb_desc = (struct cb_desc *)(skb->cb + MAX_DEV_ADDR_SIZE);
+
+ tcb_desc->bHwSec = 1;
+
+ if (ieee->need_sw_enc)
+ tcb_desc->bHwSec = 0;
+ }
+
+ hdr = (struct rtllib_hdr_4addr *) skb->data;
+ hdrlen = rtllib_get_hdrlen(le16_to_cpu(hdr->frame_ctl));
+
+ atomic_inc(&crypt->refcnt);
+ res = crypt->ops->decrypt_msdu(skb, keyidx, hdrlen, crypt->priv);
+ atomic_dec(&crypt->refcnt);
+ if (res < 0) {
+ printk(KERN_DEBUG "%s: MSDU decryption/MIC verification failed (SA= %pM keyidx=%d)\n",
+ ieee->dev->name, hdr->addr2, keyidx);
+ return -1;
+ }
+
+ return 0;
+}
+
+
+/* this function is stolen from ipw2200 driver*/
+#define IEEE_PACKET_RETRY_TIME (5*HZ)
+static int is_duplicate_packet(struct rtllib_device *ieee,
+ struct rtllib_hdr_4addr *header)
+{
+ u16 fc = le16_to_cpu(header->frame_ctl);
+ u16 sc = le16_to_cpu(header->seq_ctl);
+ u16 seq = WLAN_GET_SEQ_SEQ(sc);
+ u16 frag = WLAN_GET_SEQ_FRAG(sc);
+ u16 *last_seq, *last_frag;
+ unsigned long *last_time;
+ struct rtllib_hdr_3addrqos *hdr_3addrqos;
+ struct rtllib_hdr_4addrqos *hdr_4addrqos;
+ u8 tid;
+
+ if (((fc & RTLLIB_FCTL_DSTODS) == RTLLIB_FCTL_DSTODS) && RTLLIB_QOS_HAS_SEQ(fc)) {
+ hdr_4addrqos = (struct rtllib_hdr_4addrqos *)header;
+ tid = le16_to_cpu(hdr_4addrqos->qos_ctl) & RTLLIB_QCTL_TID;
+ tid = UP2AC(tid);
+ tid++;
+ } else if (RTLLIB_QOS_HAS_SEQ(fc)) {
+ hdr_3addrqos = (struct rtllib_hdr_3addrqos *)header;
+ tid = le16_to_cpu(hdr_3addrqos->qos_ctl) & RTLLIB_QCTL_TID;
+ tid = UP2AC(tid);
+ tid++;
+ } else {
+ tid = 0;
+ }
+
+ switch (ieee->iw_mode) {
+ case IW_MODE_ADHOC:
+ {
+ struct list_head *p;
+ struct ieee_ibss_seq *entry = NULL;
+ u8 *mac = header->addr2;
+ int index = mac[5] % IEEE_IBSS_MAC_HASH_SIZE;
+
+ list_for_each(p, &ieee->ibss_mac_hash[index]) {
+ entry = list_entry(p, struct ieee_ibss_seq, list);
+ if (!memcmp(entry->mac, mac, ETH_ALEN))
+ break;
+ }
+ if (p == &ieee->ibss_mac_hash[index]) {
+ entry = kmalloc(sizeof(struct ieee_ibss_seq), GFP_ATOMIC);
+ if (!entry)
+ return 0;
+
+ memcpy(entry->mac, mac, ETH_ALEN);
+ entry->seq_num[tid] = seq;
+ entry->frag_num[tid] = frag;
+ entry->packet_time[tid] = jiffies;
+ list_add(&entry->list, &ieee->ibss_mac_hash[index]);
+ return 0;
+ }
+ last_seq = &entry->seq_num[tid];
+ last_frag = &entry->frag_num[tid];
+ last_time = &entry->packet_time[tid];
+ break;
+ }
+
+ case IW_MODE_INFRA:
+ last_seq = &ieee->last_rxseq_num[tid];
+ last_frag = &ieee->last_rxfrag_num[tid];
+ last_time = &ieee->last_packet_time[tid];
+ break;
+ default:
+ return 0;
+ }
+
+ if ((*last_seq == seq) &&
+ time_after(*last_time + IEEE_PACKET_RETRY_TIME, jiffies)) {
+ if (*last_frag == frag)
+ goto drop;
+ if (*last_frag + 1 != frag)
+ /* out-of-order fragment */
+ goto drop;
+ } else
+ *last_seq = seq;
+
+ *last_frag = frag;
+ *last_time = jiffies;
+ return 0;
+
+drop:
+
+ return 1;
+}
+
+static bool AddReorderEntry(struct rx_ts_record *pTS,
+ struct rx_reorder_entry *pReorderEntry)
+{
+ struct list_head *pList = &pTS->RxPendingPktList;
+
+ while (pList->next != &pTS->RxPendingPktList) {
+ if (SN_LESS(pReorderEntry->SeqNum, ((struct rx_reorder_entry *)
+ list_entry(pList->next, struct rx_reorder_entry,
+ List))->SeqNum))
+ pList = pList->next;
+ else if (SN_EQUAL(pReorderEntry->SeqNum,
+ ((struct rx_reorder_entry *)list_entry(pList->next,
+ struct rx_reorder_entry, List))->SeqNum))
+ return false;
+ else
+ break;
+ }
+ pReorderEntry->List.next = pList->next;
+ pReorderEntry->List.next->prev = &pReorderEntry->List;
+ pReorderEntry->List.prev = pList;
+ pList->next = &pReorderEntry->List;
+
+ return true;
+}
+
+void rtllib_indicate_packets(struct rtllib_device *ieee, struct rtllib_rxb **prxbIndicateArray, u8 index)
+{
+ struct net_device_stats *stats = &ieee->stats;
+ u8 i = 0 , j = 0;
+ u16 ethertype;
+
+ for (j = 0; j < index; j++) {
+ struct rtllib_rxb *prxb = prxbIndicateArray[j];
+
+ for (i = 0; i < prxb->nr_subframes; i++) {
+ struct sk_buff *sub_skb = prxb->subframes[i];
+
+ /* convert hdr + possible LLC headers into Ethernet header */
+ ethertype = (sub_skb->data[6] << 8) | sub_skb->data[7];
+ if (sub_skb->len >= 8 &&
+ ((memcmp(sub_skb->data, rfc1042_header, SNAP_SIZE) == 0 &&
+ ethertype != ETH_P_AARP && ethertype != ETH_P_IPX) ||
+ memcmp(sub_skb->data, bridge_tunnel_header, SNAP_SIZE) == 0)) {
+ /* remove RFC1042 or Bridge-Tunnel encapsulation
+ * and replace EtherType
+ */
+ skb_pull(sub_skb, SNAP_SIZE);
+ memcpy(skb_push(sub_skb, ETH_ALEN), prxb->src, ETH_ALEN);
+ memcpy(skb_push(sub_skb, ETH_ALEN), prxb->dst, ETH_ALEN);
+ } else {
+ u16 len;
+ /* Leave Ethernet header part of hdr and full payload */
+ len = sub_skb->len;
+ memcpy(skb_push(sub_skb, 2), &len, 2);
+ memcpy(skb_push(sub_skb, ETH_ALEN), prxb->src, ETH_ALEN);
+ memcpy(skb_push(sub_skb, ETH_ALEN), prxb->dst, ETH_ALEN);
+ }
+
+ /* Indicate the packets to upper layer */
+ if (sub_skb) {
+ stats->rx_packets++;
+ stats->rx_bytes += sub_skb->len;
+
+ memset(sub_skb->cb, 0, sizeof(sub_skb->cb));
+ sub_skb->protocol = eth_type_trans(sub_skb, ieee->dev);
+ sub_skb->dev = ieee->dev;
+ sub_skb->dev->stats.rx_packets++;
+ sub_skb->dev->stats.rx_bytes += sub_skb->len;
+ sub_skb->ip_summed = CHECKSUM_NONE; /* 802.11 crc not sufficient */
+ ieee->last_rx_ps_time = jiffies;
+ netif_rx(sub_skb);
+ }
+ }
+ kfree(prxb);
+ prxb = NULL;
+ }
+}
+
+void rtllib_FlushRxTsPendingPkts(struct rtllib_device *ieee, struct rx_ts_record *pTS)
+{
+ struct rx_reorder_entry *pRxReorderEntry;
+ u8 RfdCnt = 0;
+
+ del_timer_sync(&pTS->RxPktPendingTimer);
+ while (!list_empty(&pTS->RxPendingPktList)) {
+ if (RfdCnt >= REORDER_WIN_SIZE) {
+ netdev_info(ieee->dev,
+ "-------------->%s() error! RfdCnt >= REORDER_WIN_SIZE\n",
+ __func__);
+ break;
+ }
+
+ pRxReorderEntry = (struct rx_reorder_entry *)list_entry(pTS->RxPendingPktList.prev, struct rx_reorder_entry, List);
+ RTLLIB_DEBUG(RTLLIB_DL_REORDER, "%s(): Indicate SeqNum %d!\n", __func__, pRxReorderEntry->SeqNum);
+ list_del_init(&pRxReorderEntry->List);
+
+ ieee->RfdArray[RfdCnt] = pRxReorderEntry->prxb;
+
+ RfdCnt = RfdCnt + 1;
+ list_add_tail(&pRxReorderEntry->List, &ieee->RxReorder_Unused_List);
+ }
+ rtllib_indicate_packets(ieee, ieee->RfdArray, RfdCnt);
+
+ pTS->RxIndicateSeq = 0xffff;
+}
+
+static void RxReorderIndicatePacket(struct rtllib_device *ieee,
+ struct rtllib_rxb *prxb,
+ struct rx_ts_record *pTS, u16 SeqNum)
+{
+ struct rt_hi_throughput *pHTInfo = ieee->pHTInfo;
+ struct rx_reorder_entry *pReorderEntry = NULL;
+ u8 WinSize = pHTInfo->RxReorderWinSize;
+ u16 WinEnd = 0;
+ u8 index = 0;
+ bool bMatchWinStart = false, bPktInBuf = false;
+ unsigned long flags;
+
+ RTLLIB_DEBUG(RTLLIB_DL_REORDER, "%s(): Seq is %d, pTS->RxIndicateSeq is %d, WinSize is %d\n", __func__, SeqNum,
+ pTS->RxIndicateSeq, WinSize);
+
+ spin_lock_irqsave(&(ieee->reorder_spinlock), flags);
+
+ WinEnd = (pTS->RxIndicateSeq + WinSize - 1) % 4096;
+ /* Rx Reorder initialize condition.*/
+ if (pTS->RxIndicateSeq == 0xffff)
+ pTS->RxIndicateSeq = SeqNum;
+
+ /* Drop out the packet which SeqNum is smaller than WinStart */
+ if (SN_LESS(SeqNum, pTS->RxIndicateSeq)) {
+ RTLLIB_DEBUG(RTLLIB_DL_REORDER, "Packet Drop! IndicateSeq: %d, NewSeq: %d\n",
+ pTS->RxIndicateSeq, SeqNum);
+ pHTInfo->RxReorderDropCounter++;
+ {
+ int i;
+
+ for (i = 0; i < prxb->nr_subframes; i++)
+ dev_kfree_skb(prxb->subframes[i]);
+ kfree(prxb);
+ prxb = NULL;
+ }
+ spin_unlock_irqrestore(&(ieee->reorder_spinlock), flags);
+ return;
+ }
+
+ /* Sliding window manipulation. Conditions includes:
+ * 1. Incoming SeqNum is equal to WinStart =>Window shift 1
+ * 2. Incoming SeqNum is larger than the WinEnd => Window shift N
+ */
+ if (SN_EQUAL(SeqNum, pTS->RxIndicateSeq)) {
+ pTS->RxIndicateSeq = (pTS->RxIndicateSeq + 1) % 4096;
+ bMatchWinStart = true;
+ } else if (SN_LESS(WinEnd, SeqNum)) {
+ if (SeqNum >= (WinSize - 1))
+ pTS->RxIndicateSeq = SeqNum + 1 - WinSize;
+ else
+ pTS->RxIndicateSeq = 4095 - (WinSize - (SeqNum + 1)) + 1;
+ RTLLIB_DEBUG(RTLLIB_DL_REORDER, "Window Shift! IndicateSeq: %d, NewSeq: %d\n", pTS->RxIndicateSeq, SeqNum);
+ }
+
+ /* Indication process.
+ * After Packet dropping and Sliding Window shifting as above, we can
+ * now just indicate the packets with the SeqNum smaller than latest
+ * WinStart and struct buffer other packets.
+ *
+ * For Rx Reorder condition:
+ * 1. All packets with SeqNum smaller than WinStart => Indicate
+ * 2. All packets with SeqNum larger than or equal to
+ * WinStart => Buffer it.
+ */
+ if (bMatchWinStart) {
+ /* Current packet is going to be indicated.*/
+ RTLLIB_DEBUG(RTLLIB_DL_REORDER, "Packets indication!! IndicateSeq: %d, NewSeq: %d\n",
+ pTS->RxIndicateSeq, SeqNum);
+ ieee->prxbIndicateArray[0] = prxb;
+ index = 1;
+ } else {
+ /* Current packet is going to be inserted into pending list.*/
+ if (!list_empty(&ieee->RxReorder_Unused_List)) {
+ pReorderEntry = (struct rx_reorder_entry *)
+ list_entry(ieee->RxReorder_Unused_List.next,
+ struct rx_reorder_entry, List);
+ list_del_init(&pReorderEntry->List);
+
+ /* Make a reorder entry and insert into a the packet list.*/
+ pReorderEntry->SeqNum = SeqNum;
+ pReorderEntry->prxb = prxb;
+
+ if (!AddReorderEntry(pTS, pReorderEntry)) {
+ RTLLIB_DEBUG(RTLLIB_DL_REORDER,
+ "%s(): Duplicate packet is dropped!! IndicateSeq: %d, NewSeq: %d\n",
+ __func__, pTS->RxIndicateSeq,
+ SeqNum);
+ list_add_tail(&pReorderEntry->List,
+ &ieee->RxReorder_Unused_List); {
+ int i;
+
+ for (i = 0; i < prxb->nr_subframes; i++)
+ dev_kfree_skb(prxb->subframes[i]);
+ kfree(prxb);
+ prxb = NULL;
+ }
+ } else {
+ RTLLIB_DEBUG(RTLLIB_DL_REORDER,
+ "Pkt insert into struct buffer!! IndicateSeq: %d, NewSeq: %d\n",
+ pTS->RxIndicateSeq, SeqNum);
+ }
+ } else {
+ /* Packets are dropped if there are not enough reorder
+ * entries. This part should be modified!! We can just
+ * indicate all the packets in struct buffer and get
+ * reorder entries.
+ */
+ RTLLIB_DEBUG(RTLLIB_DL_ERR, "RxReorderIndicatePacket(): There is no reorder entry!! Packet is dropped!!\n");
+ {
+ int i;
+
+ for (i = 0; i < prxb->nr_subframes; i++)
+ dev_kfree_skb(prxb->subframes[i]);
+ kfree(prxb);
+ prxb = NULL;
+ }
+ }
+ }
+
+ /* Check if there is any packet need indicate.*/
+ while (!list_empty(&pTS->RxPendingPktList)) {
+ RTLLIB_DEBUG(RTLLIB_DL_REORDER, "%s(): start RREORDER indicate\n", __func__);
+
+ pReorderEntry = (struct rx_reorder_entry *)list_entry(pTS->RxPendingPktList.prev,
+ struct rx_reorder_entry, List);
+ if (SN_LESS(pReorderEntry->SeqNum, pTS->RxIndicateSeq) ||
+ SN_EQUAL(pReorderEntry->SeqNum, pTS->RxIndicateSeq)) {
+ /* This protect struct buffer from overflow. */
+ if (index >= REORDER_WIN_SIZE) {
+ RTLLIB_DEBUG(RTLLIB_DL_ERR, "RxReorderIndicatePacket(): Buffer overflow!!\n");
+ bPktInBuf = true;
+ break;
+ }
+
+ list_del_init(&pReorderEntry->List);
+
+ if (SN_EQUAL(pReorderEntry->SeqNum, pTS->RxIndicateSeq))
+ pTS->RxIndicateSeq = (pTS->RxIndicateSeq + 1) % 4096;
+
+ ieee->prxbIndicateArray[index] = pReorderEntry->prxb;
+ RTLLIB_DEBUG(RTLLIB_DL_REORDER, "%s(): Indicate SeqNum %d!\n", __func__, pReorderEntry->SeqNum);
+ index++;
+
+ list_add_tail(&pReorderEntry->List,
+ &ieee->RxReorder_Unused_List);
+ } else {
+ bPktInBuf = true;
+ break;
+ }
+ }
+
+ /* Handling pending timer. Set this timer to prevent from long time
+ * Rx buffering.
+ */
+ if (index > 0) {
+ if (timer_pending(&pTS->RxPktPendingTimer))
+ del_timer_sync(&pTS->RxPktPendingTimer);
+ pTS->RxTimeoutIndicateSeq = 0xffff;
+
+ if (index > REORDER_WIN_SIZE) {
+ RTLLIB_DEBUG(RTLLIB_DL_ERR, "RxReorderIndicatePacket(): Rx Reorder struct buffer full!!\n");
+ spin_unlock_irqrestore(&(ieee->reorder_spinlock),
+ flags);
+ return;
+ }
+ rtllib_indicate_packets(ieee, ieee->prxbIndicateArray, index);
+ bPktInBuf = false;
+ }
+
+ if (bPktInBuf && pTS->RxTimeoutIndicateSeq == 0xffff) {
+ RTLLIB_DEBUG(RTLLIB_DL_REORDER, "%s(): SET rx timeout timer\n",
+ __func__);
+ pTS->RxTimeoutIndicateSeq = pTS->RxIndicateSeq;
+ mod_timer(&pTS->RxPktPendingTimer, jiffies +
+ msecs_to_jiffies(pHTInfo->RxReorderPendingTime));
+ }
+ spin_unlock_irqrestore(&(ieee->reorder_spinlock), flags);
+}
+
+static u8 parse_subframe(struct rtllib_device *ieee, struct sk_buff *skb,
+ struct rtllib_rx_stats *rx_stats,
+ struct rtllib_rxb *rxb, u8 *src, u8 *dst)
+{
+ struct rtllib_hdr_3addr *hdr = (struct rtllib_hdr_3addr *)skb->data;
+ u16 fc = le16_to_cpu(hdr->frame_ctl);
+
+ u16 LLCOffset = sizeof(struct rtllib_hdr_3addr);
+ u16 ChkLength;
+ bool bIsAggregateFrame = false;
+ u16 nSubframe_Length;
+ u8 nPadding_Length = 0;
+ u16 SeqNum = 0;
+ struct sk_buff *sub_skb;
+ u8 *data_ptr;
+ /* just for debug purpose */
+ SeqNum = WLAN_GET_SEQ_SEQ(le16_to_cpu(hdr->seq_ctl));
+ if ((RTLLIB_QOS_HAS_SEQ(fc)) &&
+ (((union frameqos *)(skb->data + RTLLIB_3ADDR_LEN))->field.reserved))
+ bIsAggregateFrame = true;
+
+ if (RTLLIB_QOS_HAS_SEQ(fc))
+ LLCOffset += 2;
+ if (rx_stats->bContainHTC)
+ LLCOffset += sHTCLng;
+
+ ChkLength = LLCOffset;
+
+ if (skb->len <= ChkLength)
+ return 0;
+
+ skb_pull(skb, LLCOffset);
+ ieee->bIsAggregateFrame = bIsAggregateFrame;
+ if (!bIsAggregateFrame) {
+ rxb->nr_subframes = 1;
+
+ /* altered by clark 3/30/2010
+ * The struct buffer size of the skb indicated to upper layer
+ * must be less than 5000, or the defraged IP datagram
+ * in the IP layer will exceed "ipfrag_high_tresh" and be
+ * discarded. so there must not use the function
+ * "skb_copy" and "skb_clone" for "skb".
+ */
+
+ /* Allocate new skb for releasing to upper layer */
+ sub_skb = dev_alloc_skb(RTLLIB_SKBBUFFER_SIZE);
+ if (!sub_skb)
+ return 0;
+ skb_reserve(sub_skb, 12);
+ data_ptr = (u8 *)skb_put(sub_skb, skb->len);
+ memcpy(data_ptr, skb->data, skb->len);
+ sub_skb->dev = ieee->dev;
+
+ rxb->subframes[0] = sub_skb;
+
+ memcpy(rxb->src, src, ETH_ALEN);
+ memcpy(rxb->dst, dst, ETH_ALEN);
+ rxb->subframes[0]->dev = ieee->dev;
+ return 1;
+ }
+
+ rxb->nr_subframes = 0;
+ memcpy(rxb->src, src, ETH_ALEN);
+ memcpy(rxb->dst, dst, ETH_ALEN);
+ while (skb->len > ETHERNET_HEADER_SIZE) {
+ /* Offset 12 denote 2 mac address */
+ nSubframe_Length = *((u16 *)(skb->data + 12));
+ nSubframe_Length = (nSubframe_Length >> 8) +
+ (nSubframe_Length << 8);
+
+ if (skb->len < (ETHERNET_HEADER_SIZE + nSubframe_Length)) {
+ netdev_info(ieee->dev,
+ "%s: A-MSDU parse error!! pRfd->nTotalSubframe : %d\n",
+ __func__, rxb->nr_subframes);
+ netdev_info(ieee->dev,
+ "%s: A-MSDU parse error!! Subframe Length: %d\n",
+ __func__, nSubframe_Length);
+ netdev_info(ieee->dev,
+ "nRemain_Length is %d and nSubframe_Length is : %d\n",
+ skb->len, nSubframe_Length);
+ netdev_info(ieee->dev,
+ "The Packet SeqNum is %d\n",
+ SeqNum);
+ return 0;
+ }
+
+ /* move the data point to data content */
+ skb_pull(skb, ETHERNET_HEADER_SIZE);
+
+ /* altered by clark 3/30/2010
+ * The struct buffer size of the skb indicated to upper layer
+ * must be less than 5000, or the defraged IP datagram
+ * in the IP layer will exceed "ipfrag_high_tresh" and be
+ * discarded. so there must not use the function
+ * "skb_copy" and "skb_clone" for "skb".
+ */
+
+ /* Allocate new skb for releasing to upper layer */
+ sub_skb = dev_alloc_skb(nSubframe_Length + 12);
+ if (!sub_skb)
+ return 0;
+ skb_reserve(sub_skb, 12);
+ data_ptr = (u8 *)skb_put(sub_skb, nSubframe_Length);
+ memcpy(data_ptr, skb->data, nSubframe_Length);
+
+ sub_skb->dev = ieee->dev;
+ rxb->subframes[rxb->nr_subframes++] = sub_skb;
+ if (rxb->nr_subframes >= MAX_SUBFRAME_COUNT) {
+ RTLLIB_DEBUG_RX("ParseSubframe(): Too many Subframes! Packets dropped!\n");
+ break;
+ }
+ skb_pull(skb, nSubframe_Length);
+
+ if (skb->len != 0) {
+ nPadding_Length = 4 - ((nSubframe_Length +
+ ETHERNET_HEADER_SIZE) % 4);
+ if (nPadding_Length == 4)
+ nPadding_Length = 0;
+
+ if (skb->len < nPadding_Length)
+ return 0;
+
+ skb_pull(skb, nPadding_Length);
+ }
+ }
+
+ return rxb->nr_subframes;
+}
+
+
+static size_t rtllib_rx_get_hdrlen(struct rtllib_device *ieee,
+ struct sk_buff *skb,
+ struct rtllib_rx_stats *rx_stats)
+{
+ struct rtllib_hdr_4addr *hdr = (struct rtllib_hdr_4addr *)skb->data;
+ u16 fc = le16_to_cpu(hdr->frame_ctl);
+ size_t hdrlen = 0;
+
+ hdrlen = rtllib_get_hdrlen(fc);
+ if (HTCCheck(ieee, skb->data)) {
+ if (net_ratelimit())
+ netdev_info(ieee->dev, "%s: find HTCControl!\n",
+ __func__);
+ hdrlen += 4;
+ rx_stats->bContainHTC = true;
+ }
+
+ if (RTLLIB_QOS_HAS_SEQ(fc))
+ rx_stats->bIsQosData = true;
+
+ return hdrlen;
+}
+
+static int rtllib_rx_check_duplicate(struct rtllib_device *ieee,
+ struct sk_buff *skb, u8 multicast)
+{
+ struct rtllib_hdr_4addr *hdr = (struct rtllib_hdr_4addr *)skb->data;
+ u16 fc, sc;
+ u8 frag, type, stype;
+
+ fc = le16_to_cpu(hdr->frame_ctl);
+ type = WLAN_FC_GET_TYPE(fc);
+ stype = WLAN_FC_GET_STYPE(fc);
+ sc = le16_to_cpu(hdr->seq_ctl);
+ frag = WLAN_GET_SEQ_FRAG(sc);
+
+ if ((ieee->pHTInfo->bCurRxReorderEnable == false) ||
+ !ieee->current_network.qos_data.active ||
+ !IsDataFrame(skb->data) ||
+ IsLegacyDataFrame(skb->data)) {
+ if (!((type == RTLLIB_FTYPE_MGMT) && (stype == RTLLIB_STYPE_BEACON))) {
+ if (is_duplicate_packet(ieee, hdr))
+ return -1;
+ }
+ } else {
+ struct rx_ts_record *pRxTS = NULL;
+
+ if (GetTs(ieee, (struct ts_common_info **) &pRxTS, hdr->addr2,
+ (u8)Frame_QoSTID((u8 *)(skb->data)), RX_DIR, true)) {
+ if ((fc & (1<<11)) && (frag == pRxTS->RxLastFragNum) &&
+ (WLAN_GET_SEQ_SEQ(sc) == pRxTS->RxLastSeqNum))
+ return -1;
+ pRxTS->RxLastFragNum = frag;
+ pRxTS->RxLastSeqNum = WLAN_GET_SEQ_SEQ(sc);
+ } else {
+ RTLLIB_DEBUG(RTLLIB_DL_ERR, "ERR!!%s(): No TS!! Skip the check!!\n", __func__);
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+static void rtllib_rx_extract_addr(struct rtllib_device *ieee,
+ struct rtllib_hdr_4addr *hdr, u8 *dst,
+ u8 *src, u8 *bssid)
+{
+ u16 fc = le16_to_cpu(hdr->frame_ctl);
+
+ switch (fc & (RTLLIB_FCTL_FROMDS | RTLLIB_FCTL_TODS)) {
+ case RTLLIB_FCTL_FROMDS:
+ memcpy(dst, hdr->addr1, ETH_ALEN);
+ memcpy(src, hdr->addr3, ETH_ALEN);
+ memcpy(bssid, hdr->addr2, ETH_ALEN);
+ break;
+ case RTLLIB_FCTL_TODS:
+ memcpy(dst, hdr->addr3, ETH_ALEN);
+ memcpy(src, hdr->addr2, ETH_ALEN);
+ memcpy(bssid, hdr->addr1, ETH_ALEN);
+ break;
+ case RTLLIB_FCTL_FROMDS | RTLLIB_FCTL_TODS:
+ memcpy(dst, hdr->addr3, ETH_ALEN);
+ memcpy(src, hdr->addr4, ETH_ALEN);
+ memcpy(bssid, ieee->current_network.bssid, ETH_ALEN);
+ break;
+ case 0:
+ memcpy(dst, hdr->addr1, ETH_ALEN);
+ memcpy(src, hdr->addr2, ETH_ALEN);
+ memcpy(bssid, hdr->addr3, ETH_ALEN);
+ break;
+ }
+}
+
+static int rtllib_rx_data_filter(struct rtllib_device *ieee, u16 fc,
+ u8 *dst, u8 *src, u8 *bssid, u8 *addr2)
+{
+ u8 type, stype;
+
+ type = WLAN_FC_GET_TYPE(fc);
+ stype = WLAN_FC_GET_STYPE(fc);
+
+ /* Filter frames from different BSS */
+ if (((fc & RTLLIB_FCTL_DSTODS) != RTLLIB_FCTL_DSTODS) &&
+ !ether_addr_equal(ieee->current_network.bssid, bssid) &&
+ !is_zero_ether_addr(ieee->current_network.bssid)) {
+ return -1;
+ }
+
+ /* Filter packets sent by an STA that will be forwarded by AP */
+ if (ieee->IntelPromiscuousModeInfo.bPromiscuousOn &&
+ ieee->IntelPromiscuousModeInfo.bFilterSourceStationFrame) {
+ if ((fc & RTLLIB_FCTL_TODS) && !(fc & RTLLIB_FCTL_FROMDS) &&
+ !ether_addr_equal(dst, ieee->current_network.bssid) &&
+ ether_addr_equal(bssid, ieee->current_network.bssid)) {
+ return -1;
+ }
+ }
+
+ /* Nullfunc frames may have PS-bit set, so they must be passed to
+ * hostap_handle_sta_rx() before being dropped here.
+ */
+ if (!ieee->IntelPromiscuousModeInfo.bPromiscuousOn) {
+ if (stype != RTLLIB_STYPE_DATA &&
+ stype != RTLLIB_STYPE_DATA_CFACK &&
+ stype != RTLLIB_STYPE_DATA_CFPOLL &&
+ stype != RTLLIB_STYPE_DATA_CFACKPOLL &&
+ stype != RTLLIB_STYPE_QOS_DATA) {
+ if (stype != RTLLIB_STYPE_NULLFUNC)
+ RTLLIB_DEBUG_DROP(
+ "RX: dropped data frame with no data (type=0x%02x, subtype=0x%02x)\n",
+ type, stype);
+ return -1;
+ }
+ }
+
+ if (ieee->iw_mode != IW_MODE_MESH) {
+ /* packets from our adapter are dropped (echo) */
+ if (!memcmp(src, ieee->dev->dev_addr, ETH_ALEN))
+ return -1;
+
+ /* {broad,multi}cast packets to our BSS go through */
+ if (is_multicast_ether_addr(dst)) {
+ if (memcmp(bssid, ieee->current_network.bssid, ETH_ALEN))
+ return -1;
+ }
+ }
+ return 0;
+}
+
+static int rtllib_rx_get_crypt(struct rtllib_device *ieee, struct sk_buff *skb,
+ struct lib80211_crypt_data **crypt, size_t hdrlen)
+{
+ struct rtllib_hdr_4addr *hdr = (struct rtllib_hdr_4addr *)skb->data;
+ u16 fc = le16_to_cpu(hdr->frame_ctl);
+ int idx = 0;
+
+ if (ieee->host_decrypt) {
+ if (skb->len >= hdrlen + 3)
+ idx = skb->data[hdrlen + 3] >> 6;
+
+ *crypt = ieee->crypt_info.crypt[idx];
+ /* allow NULL decrypt to indicate an station specific override
+ * for default encryption
+ */
+ if (*crypt && ((*crypt)->ops == NULL ||
+ (*crypt)->ops->decrypt_mpdu == NULL))
+ *crypt = NULL;
+
+ if (!*crypt && (fc & RTLLIB_FCTL_WEP)) {
+ /* This seems to be triggered by some (multicast?)
+ * frames from other than current BSS, so just drop the
+ * frames silently instead of filling system log with
+ * these reports.
+ */
+ RTLLIB_DEBUG_DROP("Decryption failed (not set) (SA= %pM)\n",
+ hdr->addr2);
+ ieee->ieee_stats.rx_discards_undecryptable++;
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+static int rtllib_rx_decrypt(struct rtllib_device *ieee, struct sk_buff *skb,
+ struct rtllib_rx_stats *rx_stats,
+ struct lib80211_crypt_data *crypt, size_t hdrlen)
+{
+ struct rtllib_hdr_4addr *hdr;
+ int keyidx = 0;
+ u16 fc, sc;
+ u8 frag;
+
+ hdr = (struct rtllib_hdr_4addr *)skb->data;
+ fc = le16_to_cpu(hdr->frame_ctl);
+ sc = le16_to_cpu(hdr->seq_ctl);
+ frag = WLAN_GET_SEQ_FRAG(sc);
+
+ if ((!rx_stats->Decrypted))
+ ieee->need_sw_enc = 1;
+ else
+ ieee->need_sw_enc = 0;
+
+ keyidx = rtllib_rx_frame_decrypt(ieee, skb, crypt);
+ if (ieee->host_decrypt && (fc & RTLLIB_FCTL_WEP) && (keyidx < 0)) {
+ netdev_info(ieee->dev, "%s: decrypt frame error\n", __func__);
+ return -1;
+ }
+
+ hdr = (struct rtllib_hdr_4addr *) skb->data;
+ if ((frag != 0 || (fc & RTLLIB_FCTL_MOREFRAGS))) {
+ int flen;
+ struct sk_buff *frag_skb = rtllib_frag_cache_get(ieee, hdr);
+
+ RTLLIB_DEBUG_FRAG("Rx Fragment received (%u)\n", frag);
+
+ if (!frag_skb) {
+ RTLLIB_DEBUG(RTLLIB_DL_RX | RTLLIB_DL_FRAG,
+ "Rx cannot get skb from fragment cache (morefrag=%d seq=%u frag=%u)\n",
+ (fc & RTLLIB_FCTL_MOREFRAGS) != 0,
+ WLAN_GET_SEQ_SEQ(sc), frag);
+ return -1;
+ }
+ flen = skb->len;
+ if (frag != 0)
+ flen -= hdrlen;
+
+ if (frag_skb->tail + flen > frag_skb->end) {
+ netdev_warn(ieee->dev,
+ "%s: host decrypted and reassembled frame did not fit skb\n",
+ __func__);
+ rtllib_frag_cache_invalidate(ieee, hdr);
+ return -1;
+ }
+
+ 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);
+ } else {
+ /* append frame payload to the end of the fragment
+ * cache skb
+ */
+ memcpy(skb_put(frag_skb, flen), skb->data + hdrlen,
+ flen);
+ }
+ dev_kfree_skb_any(skb);
+ skb = NULL;
+
+ if (fc & RTLLIB_FCTL_MOREFRAGS) {
+ /* more fragments expected - leave the skb in fragment
+ * cache for now; it will be delivered to upper layers
+ * after all fragments have been received
+ */
+ return -2;
+ }
+
+ /* this was the last fragment and the frame will be
+ * delivered, so remove skb from fragment cache
+ */
+ skb = frag_skb;
+ hdr = (struct rtllib_hdr_4addr *) skb->data;
+ rtllib_frag_cache_invalidate(ieee, hdr);
+ }
+
+ /* skb: hdr + (possible reassembled) full MSDU payload; possibly still
+ * encrypted/authenticated
+ */
+ if (ieee->host_decrypt && (fc & RTLLIB_FCTL_WEP) &&
+ rtllib_rx_frame_decrypt_msdu(ieee, skb, keyidx, crypt)) {
+ netdev_info(ieee->dev, "%s: ==>decrypt msdu error\n", __func__);
+ return -1;
+ }
+
+ hdr = (struct rtllib_hdr_4addr *) skb->data;
+ if (crypt && !(fc & RTLLIB_FCTL_WEP) && !ieee->open_wep) {
+ if (/*ieee->ieee802_1x &&*/
+ rtllib_is_eapol_frame(ieee, skb, hdrlen)) {
+
+ /* pass unencrypted EAPOL frames even if encryption is
+ * configured
+ */
+ struct eapol *eap = (struct eapol *)(skb->data +
+ 24);
+ RTLLIB_DEBUG_EAP("RX: IEEE 802.1X EAPOL frame: %s\n",
+ eap_get_type(eap->type));
+ } else {
+ RTLLIB_DEBUG_DROP(
+ "encryption configured, but RX frame not encrypted (SA= %pM)\n",
+ hdr->addr2);
+ return -1;
+ }
+ }
+
+ if (crypt && !(fc & RTLLIB_FCTL_WEP) &&
+ rtllib_is_eapol_frame(ieee, skb, hdrlen)) {
+ struct eapol *eap = (struct eapol *)(skb->data +
+ 24);
+ RTLLIB_DEBUG_EAP("RX: IEEE 802.1X EAPOL frame: %s\n",
+ eap_get_type(eap->type));
+ }
+
+ if (crypt && !(fc & RTLLIB_FCTL_WEP) && !ieee->open_wep &&
+ !rtllib_is_eapol_frame(ieee, skb, hdrlen)) {
+ RTLLIB_DEBUG_DROP(
+ "dropped unencrypted RX data frame from %pM (drop_unencrypted=1)\n",
+ hdr->addr2);
+ return -1;
+ }
+
+ if (rtllib_is_eapol_frame(ieee, skb, hdrlen))
+ netdev_warn(ieee->dev, "RX: IEEE802.1X EAPOL frame!\n");
+
+ return 0;
+}
+
+static void rtllib_rx_check_leave_lps(struct rtllib_device *ieee, u8 unicast, u8 nr_subframes)
+{
+ if (unicast) {
+
+ if (ieee->state == RTLLIB_LINKED) {
+ if (((ieee->LinkDetectInfo.NumRxUnicastOkInPeriod +
+ ieee->LinkDetectInfo.NumTxOkInPeriod) > 8) ||
+ (ieee->LinkDetectInfo.NumRxUnicastOkInPeriod > 2)) {
+ if (ieee->LeisurePSLeave)
+ ieee->LeisurePSLeave(ieee->dev);
+ }
+ }
+ }
+ ieee->last_rx_ps_time = jiffies;
+}
+
+static void rtllib_rx_indicate_pkt_legacy(struct rtllib_device *ieee,
+ struct rtllib_rx_stats *rx_stats,
+ struct rtllib_rxb *rxb,
+ u8 *dst,
+ u8 *src)
+{
+ struct net_device *dev = ieee->dev;
+ u16 ethertype;
+ int i = 0;
+
+ if (rxb == NULL) {
+ netdev_info(dev, "%s: rxb is NULL!!\n", __func__);
+ return;
+ }
+
+ for (i = 0; i < rxb->nr_subframes; i++) {
+ struct sk_buff *sub_skb = rxb->subframes[i];
+
+ if (sub_skb) {
+ /* convert hdr + possible LLC headers into Ethernet header */
+ ethertype = (sub_skb->data[6] << 8) | sub_skb->data[7];
+ if (sub_skb->len >= 8 &&
+ ((memcmp(sub_skb->data, rfc1042_header, SNAP_SIZE) == 0 &&
+ ethertype != ETH_P_AARP && ethertype != ETH_P_IPX) ||
+ memcmp(sub_skb->data, bridge_tunnel_header, SNAP_SIZE) == 0)) {
+ /* remove RFC1042 or Bridge-Tunnel encapsulation and
+ * replace EtherType
+ */
+ skb_pull(sub_skb, SNAP_SIZE);
+ memcpy(skb_push(sub_skb, ETH_ALEN), src, ETH_ALEN);
+ memcpy(skb_push(sub_skb, ETH_ALEN), dst, ETH_ALEN);
+ } else {
+ u16 len;
+ /* Leave Ethernet header part of hdr and full payload */
+ len = sub_skb->len;
+ memcpy(skb_push(sub_skb, 2), &len, 2);
+ memcpy(skb_push(sub_skb, ETH_ALEN), src, ETH_ALEN);
+ memcpy(skb_push(sub_skb, ETH_ALEN), dst, ETH_ALEN);
+ }
+
+ ieee->stats.rx_packets++;
+ ieee->stats.rx_bytes += sub_skb->len;
+
+ if (is_multicast_ether_addr(dst))
+ ieee->stats.multicast++;
+
+ /* Indicate the packets to upper layer */
+ memset(sub_skb->cb, 0, sizeof(sub_skb->cb));
+ sub_skb->protocol = eth_type_trans(sub_skb, dev);
+ sub_skb->dev = dev;
+ sub_skb->dev->stats.rx_packets++;
+ sub_skb->dev->stats.rx_bytes += sub_skb->len;
+ sub_skb->ip_summed = CHECKSUM_NONE; /* 802.11 crc not sufficient */
+ netif_rx(sub_skb);
+ }
+ }
+ kfree(rxb);
+}
+
+static int rtllib_rx_InfraAdhoc(struct rtllib_device *ieee, struct sk_buff *skb,
+ struct rtllib_rx_stats *rx_stats)
+{
+ struct net_device *dev = ieee->dev;
+ struct rtllib_hdr_4addr *hdr = (struct rtllib_hdr_4addr *)skb->data;
+ struct lib80211_crypt_data *crypt = NULL;
+ struct rtllib_rxb *rxb = NULL;
+ struct rx_ts_record *pTS = NULL;
+ u16 fc, sc, SeqNum = 0;
+ u8 type, stype, multicast = 0, unicast = 0, nr_subframes = 0, TID = 0;
+ u8 dst[ETH_ALEN], src[ETH_ALEN], bssid[ETH_ALEN] = {0}, *payload;
+ size_t hdrlen = 0;
+ bool bToOtherSTA = false;
+ int ret = 0, i = 0;
+
+ hdr = (struct rtllib_hdr_4addr *)skb->data;
+ fc = le16_to_cpu(hdr->frame_ctl);
+ type = WLAN_FC_GET_TYPE(fc);
+ stype = WLAN_FC_GET_STYPE(fc);
+ sc = le16_to_cpu(hdr->seq_ctl);
+
+ /*Filter pkt not to me*/
+ multicast = is_multicast_ether_addr(hdr->addr1);
+ unicast = !multicast;
+ if (unicast && !ether_addr_equal(dev->dev_addr, hdr->addr1)) {
+ if (ieee->bNetPromiscuousMode)
+ bToOtherSTA = true;
+ else
+ goto rx_dropped;
+ }
+
+ /*Filter pkt has too small length */
+ hdrlen = rtllib_rx_get_hdrlen(ieee, skb, rx_stats);
+ if (skb->len < hdrlen) {
+ netdev_info(dev, "%s():ERR!!! skb->len is smaller than hdrlen\n",
+ __func__);
+ goto rx_dropped;
+ }
+
+ /* Filter Duplicate pkt */
+ ret = rtllib_rx_check_duplicate(ieee, skb, multicast);
+ if (ret < 0)
+ goto rx_dropped;
+
+ /* Filter CTRL Frame */
+ if (type == RTLLIB_FTYPE_CTL)
+ goto rx_dropped;
+
+ /* Filter MGNT Frame */
+ if (type == RTLLIB_FTYPE_MGMT) {
+ if (bToOtherSTA)
+ goto rx_dropped;
+ if (rtllib_rx_frame_mgmt(ieee, skb, rx_stats, type, stype))
+ goto rx_dropped;
+ else
+ goto rx_exit;
+ }
+
+ /* Filter WAPI DATA Frame */
+
+ /* Update statstics for AP roaming */
+ if (!bToOtherSTA) {
+ ieee->LinkDetectInfo.NumRecvDataInPeriod++;
+ ieee->LinkDetectInfo.NumRxOkInPeriod++;
+ }
+ dev->last_rx = jiffies;
+
+ /* Data frame - extract src/dst addresses */
+ rtllib_rx_extract_addr(ieee, hdr, dst, src, bssid);
+
+ /* Filter Data frames */
+ ret = rtllib_rx_data_filter(ieee, fc, dst, src, bssid, hdr->addr2);
+ if (ret < 0)
+ goto rx_dropped;
+
+ if (skb->len == hdrlen)
+ goto rx_dropped;
+
+ /* Send pspoll based on moredata */
+ if ((ieee->iw_mode == IW_MODE_INFRA) && (ieee->sta_sleep == LPS_IS_SLEEP)
+ && (ieee->polling) && (!bToOtherSTA)) {
+ if (WLAN_FC_MORE_DATA(fc)) {
+ /* more data bit is set, let's request a new frame from the AP */
+ rtllib_sta_ps_send_pspoll_frame(ieee);
+ } else {
+ ieee->polling = false;
+ }
+ }
+
+ /* Get crypt if encrypted */
+ ret = rtllib_rx_get_crypt(ieee, skb, &crypt, hdrlen);
+ if (ret == -1)
+ goto rx_dropped;
+
+ /* Decrypt data frame (including reassemble) */
+ ret = rtllib_rx_decrypt(ieee, skb, rx_stats, crypt, hdrlen);
+ if (ret == -1)
+ goto rx_dropped;
+ else if (ret == -2)
+ goto rx_exit;
+
+ /* Get TS for Rx Reorder */
+ hdr = (struct rtllib_hdr_4addr *) skb->data;
+ if (ieee->current_network.qos_data.active && IsQoSDataFrame(skb->data)
+ && !is_multicast_ether_addr(hdr->addr1)
+ && (!bToOtherSTA)) {
+ TID = Frame_QoSTID(skb->data);
+ SeqNum = WLAN_GET_SEQ_SEQ(sc);
+ GetTs(ieee, (struct ts_common_info **) &pTS, hdr->addr2, TID, RX_DIR, true);
+ if (TID != 0 && TID != 3)
+ ieee->bis_any_nonbepkts = true;
+ }
+
+ /* Parse rx data frame (For AMSDU) */
+ /* skb: hdr + (possible reassembled) full plaintext payload */
+ payload = skb->data + hdrlen;
+ rxb = kmalloc(sizeof(struct rtllib_rxb), GFP_ATOMIC);
+ if (rxb == NULL)
+ goto rx_dropped;
+
+ /* to parse amsdu packets */
+ /* qos data packets & reserved bit is 1 */
+ if (parse_subframe(ieee, skb, rx_stats, rxb, src, dst) == 0) {
+ /* only to free rxb, and not submit the packets to upper layer */
+ for (i = 0; i < rxb->nr_subframes; i++)
+ dev_kfree_skb(rxb->subframes[i]);
+ kfree(rxb);
+ rxb = NULL;
+ goto rx_dropped;
+ }
+
+ /* Update WAPI PN */
+
+ /* Check if leave LPS */
+ if (!bToOtherSTA) {
+ if (ieee->bIsAggregateFrame)
+ nr_subframes = rxb->nr_subframes;
+ else
+ nr_subframes = 1;
+ if (unicast)
+ ieee->LinkDetectInfo.NumRxUnicastOkInPeriod += nr_subframes;
+ rtllib_rx_check_leave_lps(ieee, unicast, nr_subframes);
+ }
+
+ /* Indicate packets to upper layer or Rx Reorder */
+ if (ieee->pHTInfo->bCurRxReorderEnable == false || pTS == NULL || bToOtherSTA)
+ rtllib_rx_indicate_pkt_legacy(ieee, rx_stats, rxb, dst, src);
+ else
+ RxReorderIndicatePacket(ieee, rxb, pTS, SeqNum);
+
+ dev_kfree_skb(skb);
+
+ rx_exit:
+ return 1;
+
+ rx_dropped:
+ ieee->stats.rx_dropped++;
+
+ /* Returning 0 indicates to caller that we have not handled the SKB--
+ * so it is still allocated and can be used again by underlying
+ * hardware as a DMA target
+ */
+ return 0;
+}
+
+static int rtllib_rx_Master(struct rtllib_device *ieee, struct sk_buff *skb,
+ struct rtllib_rx_stats *rx_stats)
+{
+ return 0;
+}
+
+static int rtllib_rx_Monitor(struct rtllib_device *ieee, struct sk_buff *skb,
+ struct rtllib_rx_stats *rx_stats)
+{
+ struct rtllib_hdr_4addr *hdr = (struct rtllib_hdr_4addr *)skb->data;
+ u16 fc = le16_to_cpu(hdr->frame_ctl);
+ size_t hdrlen = rtllib_get_hdrlen(fc);
+
+ if (skb->len < hdrlen) {
+ netdev_info(ieee->dev,
+ "%s():ERR!!! skb->len is smaller than hdrlen\n",
+ __func__);
+ return 0;
+ }
+
+ if (HTCCheck(ieee, skb->data)) {
+ if (net_ratelimit())
+ netdev_info(ieee->dev, "%s: Find HTCControl!\n",
+ __func__);
+ hdrlen += 4;
+ }
+
+ rtllib_monitor_rx(ieee, skb, rx_stats, hdrlen);
+ ieee->stats.rx_packets++;
+ ieee->stats.rx_bytes += skb->len;
+
+ return 1;
+}
+
+static int rtllib_rx_Mesh(struct rtllib_device *ieee, struct sk_buff *skb,
+ struct rtllib_rx_stats *rx_stats)
+{
+ return 0;
+}
+
+/* All received frames are sent to this function. @skb contains the frame in
+ * IEEE 802.11 format, i.e., in the format it was sent over air.
+ * This function is called only as a tasklet (software IRQ).
+ */
+int rtllib_rx(struct rtllib_device *ieee, struct sk_buff *skb,
+ struct rtllib_rx_stats *rx_stats)
+{
+ int ret = 0;
+
+ if ((NULL == ieee) || (NULL == skb) || (NULL == rx_stats)) {
+ pr_info("%s: Input parameters NULL!\n", __func__);
+ goto rx_dropped;
+ }
+ if (skb->len < 10) {
+ netdev_info(ieee->dev, "%s: SKB length < 10\n", __func__);
+ goto rx_dropped;
+ }
+
+ switch (ieee->iw_mode) {
+ case IW_MODE_ADHOC:
+ case IW_MODE_INFRA:
+ ret = rtllib_rx_InfraAdhoc(ieee, skb, rx_stats);
+ break;
+ case IW_MODE_MASTER:
+ case IW_MODE_REPEAT:
+ ret = rtllib_rx_Master(ieee, skb, rx_stats);
+ break;
+ case IW_MODE_MONITOR:
+ ret = rtllib_rx_Monitor(ieee, skb, rx_stats);
+ break;
+ case IW_MODE_MESH:
+ ret = rtllib_rx_Mesh(ieee, skb, rx_stats);
+ break;
+ default:
+ netdev_info(ieee->dev, "%s: ERR iw mode!!!\n", __func__);
+ break;
+ }
+
+ return ret;
+
+ rx_dropped:
+ if (ieee)
+ ieee->stats.rx_dropped++;
+ return 0;
+}
+EXPORT_SYMBOL(rtllib_rx);
+
+static u8 qos_oui[QOS_OUI_LEN] = { 0x00, 0x50, 0xF2 };
+
+/* Make ther structure we read from the beacon packet has the right values */
+static int rtllib_verify_qos_info(struct rtllib_qos_information_element
+ *info_element, int sub_type)
+{
+
+ if (info_element->qui_subtype != sub_type)
+ return -1;
+ if (memcmp(info_element->qui, qos_oui, QOS_OUI_LEN))
+ return -1;
+ if (info_element->qui_type != QOS_OUI_TYPE)
+ return -1;
+ if (info_element->version != QOS_VERSION_1)
+ return -1;
+
+ return 0;
+}
+
+
+/* Parse a QoS parameter element */
+static int rtllib_read_qos_param_element(struct rtllib_qos_parameter_info
+ *element_param, struct rtllib_info_element
+ *info_element)
+{
+ int ret = 0;
+ u16 size = sizeof(struct rtllib_qos_parameter_info) - 2;
+
+ if ((info_element == NULL) || (element_param == NULL))
+ return -1;
+
+ if (info_element->id == QOS_ELEMENT_ID && info_element->len == size) {
+ memcpy(element_param->info_element.qui, info_element->data,
+ info_element->len);
+ element_param->info_element.elementID = info_element->id;
+ element_param->info_element.length = info_element->len;
+ } else
+ ret = -1;
+ if (ret == 0)
+ ret = rtllib_verify_qos_info(&element_param->info_element,
+ QOS_OUI_PARAM_SUB_TYPE);
+ return ret;
+}
+
+/* Parse a QoS information element */
+static int rtllib_read_qos_info_element(struct
+ rtllib_qos_information_element
+ *element_info, struct rtllib_info_element
+ *info_element)
+{
+ int ret = 0;
+ u16 size = sizeof(struct rtllib_qos_information_element) - 2;
+
+ if (element_info == NULL)
+ return -1;
+ if (info_element == NULL)
+ return -1;
+
+ if ((info_element->id == QOS_ELEMENT_ID) && (info_element->len == size)) {
+ memcpy(element_info->qui, info_element->data,
+ info_element->len);
+ element_info->elementID = info_element->id;
+ element_info->length = info_element->len;
+ } else
+ ret = -1;
+
+ if (ret == 0)
+ ret = rtllib_verify_qos_info(element_info,
+ QOS_OUI_INFO_SUB_TYPE);
+ return ret;
+}
+
+
+/* Write QoS parameters from the ac parameters. */
+static int rtllib_qos_convert_ac_to_parameters(struct rtllib_qos_parameter_info *param_elm,
+ struct rtllib_qos_data *qos_data)
+{
+ struct rtllib_qos_ac_parameter *ac_params;
+ struct rtllib_qos_parameters *qos_param = &(qos_data->parameters);
+ int i;
+ u8 aci;
+ u8 acm;
+
+ qos_data->wmm_acm = 0;
+ for (i = 0; i < QOS_QUEUE_NUM; i++) {
+ ac_params = &(param_elm->ac_params_record[i]);
+
+ aci = (ac_params->aci_aifsn & 0x60) >> 5;
+ acm = (ac_params->aci_aifsn & 0x10) >> 4;
+
+ if (aci >= QOS_QUEUE_NUM)
+ continue;
+ switch (aci) {
+ case 1:
+ /* BIT(0) | BIT(3) */
+ if (acm)
+ qos_data->wmm_acm |= (0x01<<0)|(0x01<<3);
+ break;
+ case 2:
+ /* BIT(4) | BIT(5) */
+ if (acm)
+ qos_data->wmm_acm |= (0x01<<4)|(0x01<<5);
+ break;
+ case 3:
+ /* BIT(6) | BIT(7) */
+ if (acm)
+ qos_data->wmm_acm |= (0x01<<6)|(0x01<<7);
+ break;
+ case 0:
+ default:
+ /* BIT(1) | BIT(2) */
+ if (acm)
+ qos_data->wmm_acm |= (0x01<<1)|(0x01<<2);
+ break;
+ }
+
+ qos_param->aifs[aci] = (ac_params->aci_aifsn) & 0x0f;
+
+ /* WMM spec P.11: The minimum value for AIFSN shall be 2 */
+ qos_param->aifs[aci] = (qos_param->aifs[aci] < 2) ? 2 : qos_param->aifs[aci];
+
+ qos_param->cw_min[aci] = cpu_to_le16(ac_params->ecw_min_max & 0x0F);
+
+ qos_param->cw_max[aci] = cpu_to_le16((ac_params->ecw_min_max & 0xF0) >> 4);
+
+ qos_param->flag[aci] =
+ (ac_params->aci_aifsn & 0x10) ? 0x01 : 0x00;
+ qos_param->tx_op_limit[aci] = ac_params->tx_op_limit;
+ }
+ return 0;
+}
+
+/* we have a generic data element which it may contain QoS information or
+ * parameters element. check the information element length to decide
+ * which type to read
+ */
+static int rtllib_parse_qos_info_param_IE(struct rtllib_info_element
+ *info_element,
+ struct rtllib_network *network)
+{
+ int rc = 0;
+ struct rtllib_qos_information_element qos_info_element;
+
+ rc = rtllib_read_qos_info_element(&qos_info_element, info_element);
+
+ if (rc == 0) {
+ network->qos_data.param_count = qos_info_element.ac_info & 0x0F;
+ network->flags |= NETWORK_HAS_QOS_INFORMATION;
+ } else {
+ struct rtllib_qos_parameter_info param_element;
+
+ rc = rtllib_read_qos_param_element(&param_element,
+ info_element);
+ if (rc == 0) {
+ rtllib_qos_convert_ac_to_parameters(&param_element,
+ &(network->qos_data));
+ network->flags |= NETWORK_HAS_QOS_PARAMETERS;
+ network->qos_data.param_count =
+ param_element.info_element.ac_info & 0x0F;
+ }
+ }
+
+ if (rc == 0) {
+ RTLLIB_DEBUG_QOS("QoS is supported\n");
+ network->qos_data.supported = 1;
+ }
+ return rc;
+}
+
+#define MFIE_STRING(x) case MFIE_TYPE_ ##x: return #x
+
+static const char *get_info_element_string(u16 id)
+{
+ switch (id) {
+ MFIE_STRING(SSID);
+ MFIE_STRING(RATES);
+ MFIE_STRING(FH_SET);
+ MFIE_STRING(DS_SET);
+ MFIE_STRING(CF_SET);
+ MFIE_STRING(TIM);
+ MFIE_STRING(IBSS_SET);
+ MFIE_STRING(COUNTRY);
+ MFIE_STRING(HOP_PARAMS);
+ MFIE_STRING(HOP_TABLE);
+ MFIE_STRING(REQUEST);
+ MFIE_STRING(CHALLENGE);
+ MFIE_STRING(POWER_CONSTRAINT);
+ MFIE_STRING(POWER_CAPABILITY);
+ MFIE_STRING(TPC_REQUEST);
+ MFIE_STRING(TPC_REPORT);
+ MFIE_STRING(SUPP_CHANNELS);
+ MFIE_STRING(CSA);
+ MFIE_STRING(MEASURE_REQUEST);
+ MFIE_STRING(MEASURE_REPORT);
+ MFIE_STRING(QUIET);
+ MFIE_STRING(IBSS_DFS);
+ MFIE_STRING(RSN);
+ MFIE_STRING(RATES_EX);
+ MFIE_STRING(GENERIC);
+ MFIE_STRING(QOS_PARAMETER);
+ default:
+ return "UNKNOWN";
+ }
+}
+
+static inline void rtllib_extract_country_ie(
+ struct rtllib_device *ieee,
+ struct rtllib_info_element *info_element,
+ struct rtllib_network *network,
+ u8 *addr2)
+{
+ if (IS_DOT11D_ENABLE(ieee)) {
+ if (info_element->len != 0) {
+ memcpy(network->CountryIeBuf, info_element->data, info_element->len);
+ network->CountryIeLen = info_element->len;
+
+ if (!IS_COUNTRY_IE_VALID(ieee)) {
+ if (rtllib_act_scanning(ieee, false) && ieee->FirstIe_InScan)
+ netdev_info(ieee->dev,
+ "Received beacon ContryIE, SSID: <%s>\n",
+ network->ssid);
+ Dot11d_UpdateCountryIe(ieee, addr2, info_element->len, info_element->data);
+ }
+ }
+
+ if (IS_EQUAL_CIE_SRC(ieee, addr2))
+ UPDATE_CIE_WATCHDOG(ieee);
+ }
+
+}
+
+int rtllib_parse_info_param(struct rtllib_device *ieee,
+ struct rtllib_info_element *info_element,
+ u16 length,
+ struct rtllib_network *network,
+ struct rtllib_rx_stats *stats)
+{
+ u8 i;
+ short offset;
+ u16 tmp_htcap_len = 0;
+ u16 tmp_htinfo_len = 0;
+ u16 ht_realtek_agg_len = 0;
+ u8 ht_realtek_agg_buf[MAX_IE_LEN];
+ char rates_str[64];
+ char *p;
+
+ while (length >= sizeof(*info_element)) {
+ if (sizeof(*info_element) + info_element->len > length) {
+ RTLLIB_DEBUG_MGMT("Info elem: parse failed: info_element->len + 2 > left : info_element->len+2=%zd left=%d, id=%d.\n",
+ info_element->len +
+ sizeof(*info_element),
+ length, info_element->id);
+ /* We stop processing but don't return an error here
+ * because some misbehaviour APs break this rule. ie.
+ * Orinoco AP1000.
+ */
+ break;
+ }
+
+ switch (info_element->id) {
+ case MFIE_TYPE_SSID:
+ if (rtllib_is_empty_essid(info_element->data,
+ info_element->len)) {
+ network->flags |= NETWORK_EMPTY_ESSID;
+ break;
+ }
+
+ network->ssid_len = min(info_element->len,
+ (u8) IW_ESSID_MAX_SIZE);
+ memcpy(network->ssid, info_element->data, network->ssid_len);
+ if (network->ssid_len < IW_ESSID_MAX_SIZE)
+ memset(network->ssid + network->ssid_len, 0,
+ IW_ESSID_MAX_SIZE - network->ssid_len);
+
+ RTLLIB_DEBUG_MGMT("MFIE_TYPE_SSID: '%s' len=%d.\n",
+ network->ssid, network->ssid_len);
+ break;
+
+ case MFIE_TYPE_RATES:
+ p = rates_str;
+ network->rates_len = min(info_element->len,
+ MAX_RATES_LENGTH);
+ for (i = 0; i < network->rates_len; i++) {
+ network->rates[i] = info_element->data[i];
+ p += snprintf(p, sizeof(rates_str) -
+ (p - rates_str), "%02X ",
+ network->rates[i]);
+ if (rtllib_is_ofdm_rate
+ (info_element->data[i])) {
+ network->flags |= NETWORK_HAS_OFDM;
+ if (info_element->data[i] &
+ RTLLIB_BASIC_RATE_MASK)
+ network->flags &=
+ ~NETWORK_HAS_CCK;
+ }
+
+ if (rtllib_is_cck_rate
+ (info_element->data[i])) {
+ network->flags |= NETWORK_HAS_CCK;
+ }
+ }
+
+ RTLLIB_DEBUG_MGMT("MFIE_TYPE_RATES: '%s' (%d)\n",
+ rates_str, network->rates_len);
+ break;
+
+ case MFIE_TYPE_RATES_EX:
+ p = rates_str;
+ network->rates_ex_len = min(info_element->len,
+ MAX_RATES_EX_LENGTH);
+ for (i = 0; i < network->rates_ex_len; i++) {
+ network->rates_ex[i] = info_element->data[i];
+ p += snprintf(p, sizeof(rates_str) -
+ (p - rates_str), "%02X ",
+ network->rates_ex[i]);
+ if (rtllib_is_ofdm_rate
+ (info_element->data[i])) {
+ network->flags |= NETWORK_HAS_OFDM;
+ if (info_element->data[i] &
+ RTLLIB_BASIC_RATE_MASK)
+ network->flags &=
+ ~NETWORK_HAS_CCK;
+ }
+ }
+
+ RTLLIB_DEBUG_MGMT("MFIE_TYPE_RATES_EX: '%s' (%d)\n",
+ rates_str, network->rates_ex_len);
+ break;
+
+ case MFIE_TYPE_DS_SET:
+ RTLLIB_DEBUG_MGMT("MFIE_TYPE_DS_SET: %d\n",
+ info_element->data[0]);
+ network->channel = info_element->data[0];
+ break;
+
+ case MFIE_TYPE_FH_SET:
+ RTLLIB_DEBUG_MGMT("MFIE_TYPE_FH_SET: ignored\n");
+ break;
+
+ case MFIE_TYPE_CF_SET:
+ RTLLIB_DEBUG_MGMT("MFIE_TYPE_CF_SET: ignored\n");
+ break;
+
+ case MFIE_TYPE_TIM:
+ if (info_element->len < 4)
+ break;
+
+ network->tim.tim_count = info_element->data[0];
+ network->tim.tim_period = info_element->data[1];
+
+ network->dtim_period = info_element->data[1];
+ if (ieee->state != RTLLIB_LINKED)
+ break;
+ network->last_dtim_sta_time = jiffies;
+
+ network->dtim_data = RTLLIB_DTIM_VALID;
+
+
+ if (info_element->data[2] & 1)
+ network->dtim_data |= RTLLIB_DTIM_MBCAST;
+
+ offset = (info_element->data[2] >> 1)*2;
+
+
+ if (ieee->assoc_id < 8*offset ||
+ ieee->assoc_id > 8*(offset + info_element->len - 3))
+ break;
+
+ offset = (ieee->assoc_id / 8) - offset;
+ if (info_element->data[3 + offset] &
+ (1 << (ieee->assoc_id % 8)))
+ network->dtim_data |= RTLLIB_DTIM_UCAST;
+
+ network->listen_interval = network->dtim_period;
+ break;
+
+ case MFIE_TYPE_ERP:
+ network->erp_value = info_element->data[0];
+ network->flags |= NETWORK_HAS_ERP_VALUE;
+ RTLLIB_DEBUG_MGMT("MFIE_TYPE_ERP_SET: %d\n",
+ network->erp_value);
+ break;
+ case MFIE_TYPE_IBSS_SET:
+ network->atim_window = info_element->data[0];
+ RTLLIB_DEBUG_MGMT("MFIE_TYPE_IBSS_SET: %d\n",
+ network->atim_window);
+ break;
+
+ case MFIE_TYPE_CHALLENGE:
+ RTLLIB_DEBUG_MGMT("MFIE_TYPE_CHALLENGE: ignored\n");
+ break;
+
+ case MFIE_TYPE_GENERIC:
+ RTLLIB_DEBUG_MGMT("MFIE_TYPE_GENERIC: %d bytes\n",
+ info_element->len);
+ if (!rtllib_parse_qos_info_param_IE(info_element,
+ network))
+ break;
+ if (info_element->len >= 4 &&
+ info_element->data[0] == 0x00 &&
+ info_element->data[1] == 0x50 &&
+ info_element->data[2] == 0xf2 &&
+ info_element->data[3] == 0x01) {
+ network->wpa_ie_len = min(info_element->len + 2,
+ MAX_WPA_IE_LEN);
+ memcpy(network->wpa_ie, info_element,
+ network->wpa_ie_len);
+ break;
+ }
+ if (info_element->len == 7 &&
+ info_element->data[0] == 0x00 &&
+ info_element->data[1] == 0xe0 &&
+ info_element->data[2] == 0x4c &&
+ info_element->data[3] == 0x01 &&
+ info_element->data[4] == 0x02)
+ network->Turbo_Enable = 1;
+
+ if (tmp_htcap_len == 0) {
+ if (info_element->len >= 4 &&
+ info_element->data[0] == 0x00 &&
+ info_element->data[1] == 0x90 &&
+ info_element->data[2] == 0x4c &&
+ info_element->data[3] == 0x033) {
+
+ tmp_htcap_len = min_t(u8, info_element->len, MAX_IE_LEN);
+ if (tmp_htcap_len != 0) {
+ network->bssht.bdHTSpecVer = HT_SPEC_VER_EWC;
+ network->bssht.bdHTCapLen = tmp_htcap_len > sizeof(network->bssht.bdHTCapBuf) ?
+ sizeof(network->bssht.bdHTCapBuf) : tmp_htcap_len;
+ memcpy(network->bssht.bdHTCapBuf, info_element->data, network->bssht.bdHTCapLen);
+ }
+ }
+ if (tmp_htcap_len != 0) {
+ network->bssht.bdSupportHT = true;
+ network->bssht.bdHT1R = ((((struct ht_capab_ele *)(network->bssht.bdHTCapBuf))->MCS[1]) == 0);
+ } else {
+ network->bssht.bdSupportHT = false;
+ network->bssht.bdHT1R = false;
+ }
+ }
+
+
+ if (tmp_htinfo_len == 0) {
+ if (info_element->len >= 4 &&
+ info_element->data[0] == 0x00 &&
+ info_element->data[1] == 0x90 &&
+ info_element->data[2] == 0x4c &&
+ info_element->data[3] == 0x034) {
+ tmp_htinfo_len = min_t(u8, info_element->len, MAX_IE_LEN);
+ if (tmp_htinfo_len != 0) {
+ network->bssht.bdHTSpecVer = HT_SPEC_VER_EWC;
+ if (tmp_htinfo_len) {
+ network->bssht.bdHTInfoLen = tmp_htinfo_len > sizeof(network->bssht.bdHTInfoBuf) ?
+ sizeof(network->bssht.bdHTInfoBuf) : tmp_htinfo_len;
+ memcpy(network->bssht.bdHTInfoBuf, info_element->data, network->bssht.bdHTInfoLen);
+ }
+
+ }
+
+ }
+ }
+
+ if (ieee->aggregation) {
+ if (network->bssht.bdSupportHT) {
+ if (info_element->len >= 4 &&
+ info_element->data[0] == 0x00 &&
+ info_element->data[1] == 0xe0 &&
+ info_element->data[2] == 0x4c &&
+ info_element->data[3] == 0x02) {
+ ht_realtek_agg_len = min_t(u8, info_element->len, MAX_IE_LEN);
+ memcpy(ht_realtek_agg_buf, info_element->data, info_element->len);
+ }
+ if (ht_realtek_agg_len >= 5) {
+ network->realtek_cap_exit = true;
+ network->bssht.bdRT2RTAggregation = true;
+
+ if ((ht_realtek_agg_buf[4] == 1) && (ht_realtek_agg_buf[5] & 0x02))
+ network->bssht.bdRT2RTLongSlotTime = true;
+
+ if ((ht_realtek_agg_buf[4] == 1) && (ht_realtek_agg_buf[5] & RT_HT_CAP_USE_92SE))
+ network->bssht.RT2RT_HT_Mode |= RT_HT_CAP_USE_92SE;
+ }
+ }
+ if (ht_realtek_agg_len >= 5) {
+ if ((ht_realtek_agg_buf[5] & RT_HT_CAP_USE_SOFTAP))
+ network->bssht.RT2RT_HT_Mode |= RT_HT_CAP_USE_SOFTAP;
+ }
+ }
+
+ if ((info_element->len >= 3 &&
+ info_element->data[0] == 0x00 &&
+ info_element->data[1] == 0x05 &&
+ info_element->data[2] == 0xb5) ||
+ (info_element->len >= 3 &&
+ info_element->data[0] == 0x00 &&
+ info_element->data[1] == 0x0a &&
+ info_element->data[2] == 0xf7) ||
+ (info_element->len >= 3 &&
+ info_element->data[0] == 0x00 &&
+ info_element->data[1] == 0x10 &&
+ info_element->data[2] == 0x18)) {
+ network->broadcom_cap_exist = true;
+ }
+ if (info_element->len >= 3 &&
+ info_element->data[0] == 0x00 &&
+ info_element->data[1] == 0x0c &&
+ info_element->data[2] == 0x43)
+ network->ralink_cap_exist = true;
+ if ((info_element->len >= 3 &&
+ info_element->data[0] == 0x00 &&
+ info_element->data[1] == 0x03 &&
+ info_element->data[2] == 0x7f) ||
+ (info_element->len >= 3 &&
+ info_element->data[0] == 0x00 &&
+ info_element->data[1] == 0x13 &&
+ info_element->data[2] == 0x74))
+ network->atheros_cap_exist = true;
+
+ if ((info_element->len >= 3 &&
+ info_element->data[0] == 0x00 &&
+ info_element->data[1] == 0x50 &&
+ info_element->data[2] == 0x43))
+ network->marvell_cap_exist = true;
+ if (info_element->len >= 3 &&
+ info_element->data[0] == 0x00 &&
+ info_element->data[1] == 0x40 &&
+ info_element->data[2] == 0x96)
+ network->cisco_cap_exist = true;
+
+
+ if (info_element->len >= 3 &&
+ info_element->data[0] == 0x00 &&
+ info_element->data[1] == 0x0a &&
+ info_element->data[2] == 0xf5)
+ network->airgo_cap_exist = true;
+
+ if (info_element->len > 4 &&
+ info_element->data[0] == 0x00 &&
+ info_element->data[1] == 0x40 &&
+ info_element->data[2] == 0x96 &&
+ info_element->data[3] == 0x01) {
+ if (info_element->len == 6) {
+ memcpy(network->CcxRmState, &info_element[4], 2);
+ if (network->CcxRmState[0] != 0)
+ network->bCcxRmEnable = true;
+ else
+ network->bCcxRmEnable = false;
+ network->MBssidMask = network->CcxRmState[1] & 0x07;
+ if (network->MBssidMask != 0) {
+ network->bMBssidValid = true;
+ network->MBssidMask = 0xff << (network->MBssidMask);
+ memcpy(network->MBssid, network->bssid, ETH_ALEN);
+ network->MBssid[5] &= network->MBssidMask;
+ } else {
+ network->bMBssidValid = false;
+ }
+ } else {
+ network->bCcxRmEnable = false;
+ }
+ }
+ if (info_element->len > 4 &&
+ info_element->data[0] == 0x00 &&
+ info_element->data[1] == 0x40 &&
+ info_element->data[2] == 0x96 &&
+ info_element->data[3] == 0x03) {
+ if (info_element->len == 5) {
+ network->bWithCcxVerNum = true;
+ network->BssCcxVerNumber = info_element->data[4];
+ } else {
+ network->bWithCcxVerNum = false;
+ network->BssCcxVerNumber = 0;
+ }
+ }
+ if (info_element->len > 4 &&
+ info_element->data[0] == 0x00 &&
+ info_element->data[1] == 0x50 &&
+ info_element->data[2] == 0xf2 &&
+ info_element->data[3] == 0x04) {
+ RTLLIB_DEBUG_MGMT("MFIE_TYPE_WZC: %d bytes\n",
+ info_element->len);
+ network->wzc_ie_len = min(info_element->len+2,
+ MAX_WZC_IE_LEN);
+ memcpy(network->wzc_ie, info_element,
+ network->wzc_ie_len);
+ }
+ break;
+
+ case MFIE_TYPE_RSN:
+ RTLLIB_DEBUG_MGMT("MFIE_TYPE_RSN: %d bytes\n",
+ info_element->len);
+ network->rsn_ie_len = min(info_element->len + 2,
+ MAX_WPA_IE_LEN);
+ memcpy(network->rsn_ie, info_element,
+ network->rsn_ie_len);
+ break;
+
+ case MFIE_TYPE_HT_CAP:
+ RTLLIB_DEBUG_SCAN("MFIE_TYPE_HT_CAP: %d bytes\n",
+ info_element->len);
+ tmp_htcap_len = min_t(u8, info_element->len, MAX_IE_LEN);
+ if (tmp_htcap_len != 0) {
+ network->bssht.bdHTSpecVer = HT_SPEC_VER_EWC;
+ network->bssht.bdHTCapLen = tmp_htcap_len > sizeof(network->bssht.bdHTCapBuf) ?
+ sizeof(network->bssht.bdHTCapBuf) : tmp_htcap_len;
+ memcpy(network->bssht.bdHTCapBuf,
+ info_element->data,
+ network->bssht.bdHTCapLen);
+
+ network->bssht.bdSupportHT = true;
+ network->bssht.bdHT1R = ((((struct ht_capab_ele *)
+ network->bssht.bdHTCapBuf))->MCS[1]) == 0;
+
+ network->bssht.bdBandWidth = (enum ht_channel_width)
+ (((struct ht_capab_ele *)
+ (network->bssht.bdHTCapBuf))->ChlWidth);
+ } else {
+ network->bssht.bdSupportHT = false;
+ network->bssht.bdHT1R = false;
+ network->bssht.bdBandWidth = HT_CHANNEL_WIDTH_20;
+ }
+ break;
+
+
+ case MFIE_TYPE_HT_INFO:
+ RTLLIB_DEBUG_SCAN("MFIE_TYPE_HT_INFO: %d bytes\n",
+ info_element->len);
+ tmp_htinfo_len = min_t(u8, info_element->len, MAX_IE_LEN);
+ if (tmp_htinfo_len) {
+ network->bssht.bdHTSpecVer = HT_SPEC_VER_IEEE;
+ network->bssht.bdHTInfoLen = tmp_htinfo_len >
+ sizeof(network->bssht.bdHTInfoBuf) ?
+ sizeof(network->bssht.bdHTInfoBuf) :
+ tmp_htinfo_len;
+ memcpy(network->bssht.bdHTInfoBuf,
+ info_element->data,
+ network->bssht.bdHTInfoLen);
+ }
+ break;
+
+ case MFIE_TYPE_AIRONET:
+ RTLLIB_DEBUG_SCAN("MFIE_TYPE_AIRONET: %d bytes\n",
+ info_element->len);
+ if (info_element->len > IE_CISCO_FLAG_POSITION) {
+ network->bWithAironetIE = true;
+
+ if ((info_element->data[IE_CISCO_FLAG_POSITION]
+ & SUPPORT_CKIP_MIC) ||
+ (info_element->data[IE_CISCO_FLAG_POSITION]
+ & SUPPORT_CKIP_PK))
+ network->bCkipSupported = true;
+ else
+ network->bCkipSupported = false;
+ } else {
+ network->bWithAironetIE = false;
+ network->bCkipSupported = false;
+ }
+ break;
+ case MFIE_TYPE_QOS_PARAMETER:
+ netdev_err(ieee->dev,
+ "QoS Error need to parse QOS_PARAMETER IE\n");
+ break;
+
+ case MFIE_TYPE_COUNTRY:
+ RTLLIB_DEBUG_SCAN("MFIE_TYPE_COUNTRY: %d bytes\n",
+ info_element->len);
+ rtllib_extract_country_ie(ieee, info_element, network,
+ network->bssid);
+ break;
+/* TODO */
+ default:
+ RTLLIB_DEBUG_MGMT
+ ("Unsupported info element: %s (%d)\n",
+ get_info_element_string(info_element->id),
+ info_element->id);
+ break;
+ }
+
+ length -= sizeof(*info_element) + info_element->len;
+ info_element =
+ (struct rtllib_info_element *)&info_element->
+ data[info_element->len];
+ }
+
+ if (!network->atheros_cap_exist && !network->broadcom_cap_exist &&
+ !network->cisco_cap_exist && !network->ralink_cap_exist &&
+ !network->bssht.bdRT2RTAggregation)
+ network->unknown_cap_exist = true;
+ else
+ network->unknown_cap_exist = false;
+ return 0;
+}
+
+static long rtllib_translate_todbm(u8 signal_strength_index)
+{
+ long signal_power;
+
+ signal_power = (long)((signal_strength_index + 1) >> 1);
+ signal_power -= 95;
+
+ return signal_power;
+}
+
+static inline int rtllib_network_init(
+ struct rtllib_device *ieee,
+ struct rtllib_probe_response *beacon,
+ struct rtllib_network *network,
+ struct rtllib_rx_stats *stats)
+{
+ memset(&network->qos_data, 0, sizeof(struct rtllib_qos_data));
+
+ /* Pull out fixed field data */
+ memcpy(network->bssid, beacon->header.addr3, ETH_ALEN);
+ network->capability = le16_to_cpu(beacon->capability);
+ network->last_scanned = jiffies;
+ network->time_stamp[0] = beacon->time_stamp[0];
+ network->time_stamp[1] = beacon->time_stamp[1];
+ network->beacon_interval = le16_to_cpu(beacon->beacon_interval);
+ /* Where to pull this? beacon->listen_interval;*/
+ network->listen_interval = 0x0A;
+ network->rates_len = network->rates_ex_len = 0;
+ network->last_associate = 0;
+ network->ssid_len = 0;
+ network->hidden_ssid_len = 0;
+ memset(network->hidden_ssid, 0, sizeof(network->hidden_ssid));
+ network->flags = 0;
+ network->atim_window = 0;
+ network->erp_value = (network->capability & WLAN_CAPABILITY_IBSS) ?
+ 0x3 : 0x0;
+ network->berp_info_valid = false;
+ network->broadcom_cap_exist = false;
+ network->ralink_cap_exist = false;
+ network->atheros_cap_exist = false;
+ network->cisco_cap_exist = false;
+ network->unknown_cap_exist = false;
+ network->realtek_cap_exit = false;
+ network->marvell_cap_exist = false;
+ network->airgo_cap_exist = false;
+ network->Turbo_Enable = 0;
+ network->SignalStrength = stats->SignalStrength;
+ network->RSSI = stats->SignalStrength;
+ network->CountryIeLen = 0;
+ memset(network->CountryIeBuf, 0, MAX_IE_LEN);
+ HTInitializeBssDesc(&network->bssht);
+ if (stats->freq == RTLLIB_52GHZ_BAND) {
+ /* for A band (No DS info) */
+ network->channel = stats->received_channel;
+ } else
+ network->flags |= NETWORK_HAS_CCK;
+
+ network->wpa_ie_len = 0;
+ network->rsn_ie_len = 0;
+ network->wzc_ie_len = 0;
+
+ if (rtllib_parse_info_param(ieee,
+ beacon->info_element,
+ (stats->len - sizeof(*beacon)),
+ network,
+ stats))
+ return 1;
+
+ network->mode = 0;
+ if (stats->freq == RTLLIB_52GHZ_BAND)
+ network->mode = IEEE_A;
+ else {
+ if (network->flags & NETWORK_HAS_OFDM)
+ network->mode |= IEEE_G;
+ if (network->flags & NETWORK_HAS_CCK)
+ network->mode |= IEEE_B;
+ }
+
+ if (network->mode == 0) {
+ RTLLIB_DEBUG_SCAN("Filtered out '%s (%pM)' network.\n",
+ escape_essid(network->ssid,
+ network->ssid_len),
+ network->bssid);
+ return 1;
+ }
+
+ if (network->bssht.bdSupportHT) {
+ if (network->mode == IEEE_A)
+ network->mode = IEEE_N_5G;
+ else if (network->mode & (IEEE_G | IEEE_B))
+ network->mode = IEEE_N_24G;
+ }
+ if (rtllib_is_empty_essid(network->ssid, network->ssid_len))
+ network->flags |= NETWORK_EMPTY_ESSID;
+ stats->signal = 30 + (stats->SignalStrength * 70) / 100;
+ stats->noise = rtllib_translate_todbm((u8)(100-stats->signal)) - 25;
+
+ memcpy(&network->stats, stats, sizeof(network->stats));
+
+ return 0;
+}
+
+static inline int is_same_network(struct rtllib_network *src,
+ struct rtllib_network *dst, u8 ssidbroad)
+{
+ /* A network is only a duplicate if the channel, BSSID, ESSID
+ * and the capability field (in particular IBSS and BSS) all match.
+ * We treat all <hidden> with the same BSSID and channel
+ * as one network
+ */
+ return (((src->ssid_len == dst->ssid_len) || (!ssidbroad)) &&
+ (src->channel == dst->channel) &&
+ !memcmp(src->bssid, dst->bssid, ETH_ALEN) &&
+ (!memcmp(src->ssid, dst->ssid, src->ssid_len) ||
+ (!ssidbroad)) &&
+ ((src->capability & WLAN_CAPABILITY_IBSS) ==
+ (dst->capability & WLAN_CAPABILITY_IBSS)) &&
+ ((src->capability & WLAN_CAPABILITY_ESS) ==
+ (dst->capability & WLAN_CAPABILITY_ESS)));
+}
+
+
+static inline void update_network(struct rtllib_network *dst,
+ struct rtllib_network *src)
+{
+ int qos_active;
+ u8 old_param;
+
+ memcpy(&dst->stats, &src->stats, sizeof(struct rtllib_rx_stats));
+ dst->capability = src->capability;
+ memcpy(dst->rates, src->rates, src->rates_len);
+ dst->rates_len = src->rates_len;
+ memcpy(dst->rates_ex, src->rates_ex, src->rates_ex_len);
+ dst->rates_ex_len = src->rates_ex_len;
+ if (src->ssid_len > 0) {
+ if (dst->ssid_len == 0) {
+ memset(dst->hidden_ssid, 0, sizeof(dst->hidden_ssid));
+ dst->hidden_ssid_len = src->ssid_len;
+ memcpy(dst->hidden_ssid, src->ssid, src->ssid_len);
+ } else {
+ memset(dst->ssid, 0, dst->ssid_len);
+ dst->ssid_len = src->ssid_len;
+ memcpy(dst->ssid, src->ssid, src->ssid_len);
+ }
+ }
+ dst->mode = src->mode;
+ dst->flags = src->flags;
+ dst->time_stamp[0] = src->time_stamp[0];
+ dst->time_stamp[1] = src->time_stamp[1];
+ if (src->flags & NETWORK_HAS_ERP_VALUE) {
+ dst->erp_value = src->erp_value;
+ dst->berp_info_valid = src->berp_info_valid = true;
+ }
+ dst->beacon_interval = src->beacon_interval;
+ dst->listen_interval = src->listen_interval;
+ dst->atim_window = src->atim_window;
+ dst->dtim_period = src->dtim_period;
+ dst->dtim_data = src->dtim_data;
+ dst->last_dtim_sta_time = src->last_dtim_sta_time;
+ memcpy(&dst->tim, &src->tim, sizeof(struct rtllib_tim_parameters));
+
+ dst->bssht.bdSupportHT = src->bssht.bdSupportHT;
+ dst->bssht.bdRT2RTAggregation = src->bssht.bdRT2RTAggregation;
+ dst->bssht.bdHTCapLen = src->bssht.bdHTCapLen;
+ memcpy(dst->bssht.bdHTCapBuf, src->bssht.bdHTCapBuf,
+ src->bssht.bdHTCapLen);
+ dst->bssht.bdHTInfoLen = src->bssht.bdHTInfoLen;
+ memcpy(dst->bssht.bdHTInfoBuf, src->bssht.bdHTInfoBuf,
+ src->bssht.bdHTInfoLen);
+ dst->bssht.bdHTSpecVer = src->bssht.bdHTSpecVer;
+ dst->bssht.bdRT2RTLongSlotTime = src->bssht.bdRT2RTLongSlotTime;
+ dst->broadcom_cap_exist = src->broadcom_cap_exist;
+ dst->ralink_cap_exist = src->ralink_cap_exist;
+ dst->atheros_cap_exist = src->atheros_cap_exist;
+ dst->realtek_cap_exit = src->realtek_cap_exit;
+ dst->marvell_cap_exist = src->marvell_cap_exist;
+ dst->cisco_cap_exist = src->cisco_cap_exist;
+ dst->airgo_cap_exist = src->airgo_cap_exist;
+ dst->unknown_cap_exist = src->unknown_cap_exist;
+ memcpy(dst->wpa_ie, src->wpa_ie, src->wpa_ie_len);
+ dst->wpa_ie_len = src->wpa_ie_len;
+ memcpy(dst->rsn_ie, src->rsn_ie, src->rsn_ie_len);
+ dst->rsn_ie_len = src->rsn_ie_len;
+ memcpy(dst->wzc_ie, src->wzc_ie, src->wzc_ie_len);
+ dst->wzc_ie_len = src->wzc_ie_len;
+
+ dst->last_scanned = jiffies;
+ /* qos related parameters */
+ qos_active = dst->qos_data.active;
+ old_param = dst->qos_data.param_count;
+ dst->qos_data.supported = src->qos_data.supported;
+ if (dst->flags & NETWORK_HAS_QOS_PARAMETERS)
+ memcpy(&dst->qos_data, &src->qos_data,
+ sizeof(struct rtllib_qos_data));
+ if (dst->qos_data.supported == 1) {
+ if (dst->ssid_len)
+ RTLLIB_DEBUG_QOS
+ ("QoS the network %s is QoS supported\n",
+ dst->ssid);
+ else
+ RTLLIB_DEBUG_QOS
+ ("QoS the network is QoS supported\n");
+ }
+ dst->qos_data.active = qos_active;
+ dst->qos_data.old_param_count = old_param;
+
+ /* dst->last_associate is not overwritten */
+ dst->wmm_info = src->wmm_info;
+ if (src->wmm_param[0].ac_aci_acm_aifsn ||
+ src->wmm_param[1].ac_aci_acm_aifsn ||
+ src->wmm_param[2].ac_aci_acm_aifsn ||
+ src->wmm_param[3].ac_aci_acm_aifsn)
+ memcpy(dst->wmm_param, src->wmm_param, WME_AC_PRAM_LEN);
+
+ dst->SignalStrength = src->SignalStrength;
+ dst->RSSI = src->RSSI;
+ dst->Turbo_Enable = src->Turbo_Enable;
+
+ dst->CountryIeLen = src->CountryIeLen;
+ memcpy(dst->CountryIeBuf, src->CountryIeBuf, src->CountryIeLen);
+
+ dst->bWithAironetIE = src->bWithAironetIE;
+ dst->bCkipSupported = src->bCkipSupported;
+ memcpy(dst->CcxRmState, src->CcxRmState, 2);
+ dst->bCcxRmEnable = src->bCcxRmEnable;
+ dst->MBssidMask = src->MBssidMask;
+ dst->bMBssidValid = src->bMBssidValid;
+ memcpy(dst->MBssid, src->MBssid, 6);
+ dst->bWithCcxVerNum = src->bWithCcxVerNum;
+ dst->BssCcxVerNumber = src->BssCcxVerNumber;
+}
+
+static inline int is_beacon(__le16 fc)
+{
+ return (WLAN_FC_GET_STYPE(le16_to_cpu(fc)) == RTLLIB_STYPE_BEACON);
+}
+
+static int IsPassiveChannel(struct rtllib_device *rtllib, u8 channel)
+{
+ if (MAX_CHANNEL_NUMBER < channel) {
+ netdev_info(rtllib->dev, "%s(): Invalid Channel\n", __func__);
+ return 0;
+ }
+
+ if (rtllib->active_channel_map[channel] == 2)
+ return 1;
+
+ return 0;
+}
+
+int rtllib_legal_channel(struct rtllib_device *rtllib, u8 channel)
+{
+ if (MAX_CHANNEL_NUMBER < channel) {
+ netdev_info(rtllib->dev, "%s(): Invalid Channel\n", __func__);
+ return 0;
+ }
+ if (rtllib->active_channel_map[channel] > 0)
+ return 1;
+
+ return 0;
+}
+EXPORT_SYMBOL(rtllib_legal_channel);
+
+static inline void rtllib_process_probe_response(
+ struct rtllib_device *ieee,
+ struct rtllib_probe_response *beacon,
+ struct rtllib_rx_stats *stats)
+{
+ struct rtllib_network *target;
+ struct rtllib_network *oldest = NULL;
+ struct rtllib_info_element *info_element = &beacon->info_element[0];
+ unsigned long flags;
+ short renew;
+ struct rtllib_network *network = kzalloc(sizeof(struct rtllib_network),
+ GFP_ATOMIC);
+
+ if (!network)
+ return;
+
+ RTLLIB_DEBUG_SCAN(
+ "'%s' ( %pM ): %c%c%c%c %c%c%c%c-%c%c%c%c %c%c%c%c\n",
+ escape_essid(info_element->data, info_element->len),
+ beacon->header.addr3,
+ (le16_to_cpu(beacon->capability) & (1<<0xf)) ? '1' : '0',
+ (le16_to_cpu(beacon->capability) & (1<<0xe)) ? '1' : '0',
+ (le16_to_cpu(beacon->capability) & (1<<0xd)) ? '1' : '0',
+ (le16_to_cpu(beacon->capability) & (1<<0xc)) ? '1' : '0',
+ (le16_to_cpu(beacon->capability) & (1<<0xb)) ? '1' : '0',
+ (le16_to_cpu(beacon->capability) & (1<<0xa)) ? '1' : '0',
+ (le16_to_cpu(beacon->capability) & (1<<0x9)) ? '1' : '0',
+ (le16_to_cpu(beacon->capability) & (1<<0x8)) ? '1' : '0',
+ (le16_to_cpu(beacon->capability) & (1<<0x7)) ? '1' : '0',
+ (le16_to_cpu(beacon->capability) & (1<<0x6)) ? '1' : '0',
+ (le16_to_cpu(beacon->capability) & (1<<0x5)) ? '1' : '0',
+ (le16_to_cpu(beacon->capability) & (1<<0x4)) ? '1' : '0',
+ (le16_to_cpu(beacon->capability) & (1<<0x3)) ? '1' : '0',
+ (le16_to_cpu(beacon->capability) & (1<<0x2)) ? '1' : '0',
+ (le16_to_cpu(beacon->capability) & (1<<0x1)) ? '1' : '0',
+ (le16_to_cpu(beacon->capability) & (1<<0x0)) ? '1' : '0');
+
+ if (rtllib_network_init(ieee, beacon, network, stats)) {
+ RTLLIB_DEBUG_SCAN("Dropped '%s' ( %pM) via %s.\n",
+ escape_essid(info_element->data,
+ info_element->len),
+ beacon->header.addr3,
+ WLAN_FC_GET_STYPE(
+ le16_to_cpu(beacon->header.frame_ctl)) ==
+ RTLLIB_STYPE_PROBE_RESP ?
+ "PROBE RESPONSE" : "BEACON");
+ goto free_network;
+ }
+
+
+ if (!rtllib_legal_channel(ieee, network->channel))
+ goto free_network;
+
+ if (WLAN_FC_GET_STYPE(le16_to_cpu(beacon->header.frame_ctl)) ==
+ RTLLIB_STYPE_PROBE_RESP) {
+ if (IsPassiveChannel(ieee, network->channel)) {
+ netdev_info(ieee->dev,
+ "GetScanInfo(): For Global Domain, filter probe response at channel(%d).\n",
+ network->channel);
+ goto free_network;
+ }
+ }
+
+ /* The network parsed correctly -- so now we scan our known networks
+ * to see if we can find it in our list.
+ *
+ * NOTE: This search is definitely not optimized. Once its doing
+ * the "right thing" we'll optimize it for efficiency if
+ * necessary
+ */
+
+ /* Search for this entry in the list and update it if it is
+ * already there.
+ */
+
+ spin_lock_irqsave(&ieee->lock, flags);
+ if (is_same_network(&ieee->current_network, network,
+ (network->ssid_len ? 1 : 0))) {
+ update_network(&ieee->current_network, network);
+ if ((ieee->current_network.mode == IEEE_N_24G ||
+ ieee->current_network.mode == IEEE_G)
+ && ieee->current_network.berp_info_valid) {
+ if (ieee->current_network.erp_value & ERP_UseProtection)
+ ieee->current_network.buseprotection = true;
+ else
+ ieee->current_network.buseprotection = false;
+ }
+ if (is_beacon(beacon->header.frame_ctl)) {
+ if (ieee->state >= RTLLIB_LINKED)
+ ieee->LinkDetectInfo.NumRecvBcnInPeriod++;
+ }
+ }
+ list_for_each_entry(target, &ieee->network_list, list) {
+ if (is_same_network(target, network,
+ (target->ssid_len ? 1 : 0)))
+ break;
+ if ((oldest == NULL) ||
+ (target->last_scanned < oldest->last_scanned))
+ oldest = target;
+ }
+
+ /* If we didn't find a match, then get a new network slot to initialize
+ * with this beacon's information
+ */
+ if (&target->list == &ieee->network_list) {
+ if (list_empty(&ieee->network_free_list)) {
+ /* If there are no more slots, expire the oldest */
+ list_del(&oldest->list);
+ target = oldest;
+ RTLLIB_DEBUG_SCAN("Expired '%s' ( %pM) from network list.\n",
+ escape_essid(target->ssid,
+ target->ssid_len),
+ target->bssid);
+ } else {
+ /* Otherwise just pull from the free list */
+ target = list_entry(ieee->network_free_list.next,
+ struct rtllib_network, list);
+ list_del(ieee->network_free_list.next);
+ }
+
+
+ RTLLIB_DEBUG_SCAN("Adding '%s' ( %pM) via %s.\n",
+ escape_essid(network->ssid,
+ network->ssid_len), network->bssid,
+ WLAN_FC_GET_STYPE(
+ le16_to_cpu(beacon->header.frame_ctl)) ==
+ RTLLIB_STYPE_PROBE_RESP ?
+ "PROBE RESPONSE" : "BEACON");
+ memcpy(target, network, sizeof(*target));
+ list_add_tail(&target->list, &ieee->network_list);
+ if (ieee->softmac_features & IEEE_SOFTMAC_ASSOCIATE)
+ rtllib_softmac_new_net(ieee, network);
+ } else {
+ RTLLIB_DEBUG_SCAN("Updating '%s' ( %pM) via %s.\n",
+ escape_essid(target->ssid,
+ target->ssid_len), target->bssid,
+ WLAN_FC_GET_STYPE(
+ le16_to_cpu(beacon->header.frame_ctl)) ==
+ RTLLIB_STYPE_PROBE_RESP ?
+ "PROBE RESPONSE" : "BEACON");
+
+ /* we have an entry and we are going to update it. But this
+ * entry may be already expired. In this case we do the same
+ * as we found a new net and call the new_net handler
+ */
+ renew = !time_after(target->last_scanned + ieee->scan_age,
+ jiffies);
+ if ((!target->ssid_len) &&
+ (((network->ssid_len > 0) && (target->hidden_ssid_len == 0))
+ || ((ieee->current_network.ssid_len == network->ssid_len) &&
+ (strncmp(ieee->current_network.ssid, network->ssid,
+ network->ssid_len) == 0) &&
+ (ieee->state == RTLLIB_NOLINK))))
+ renew = 1;
+ update_network(target, network);
+ if (renew && (ieee->softmac_features & IEEE_SOFTMAC_ASSOCIATE))
+ rtllib_softmac_new_net(ieee, network);
+ }
+
+ spin_unlock_irqrestore(&ieee->lock, flags);
+ if (is_beacon(beacon->header.frame_ctl) &&
+ is_same_network(&ieee->current_network, network,
+ (network->ssid_len ? 1 : 0)) &&
+ (ieee->state == RTLLIB_LINKED)) {
+ if (ieee->handle_beacon != NULL)
+ ieee->handle_beacon(ieee->dev, beacon,
+ &ieee->current_network);
+ }
+free_network:
+ kfree(network);
+}
+
+void rtllib_rx_mgt(struct rtllib_device *ieee,
+ struct sk_buff *skb,
+ struct rtllib_rx_stats *stats)
+{
+ struct rtllib_hdr_4addr *header = (struct rtllib_hdr_4addr *)skb->data;
+
+ if ((WLAN_FC_GET_STYPE(le16_to_cpu(header->frame_ctl)) !=
+ RTLLIB_STYPE_PROBE_RESP) &&
+ (WLAN_FC_GET_STYPE(le16_to_cpu(header->frame_ctl)) !=
+ RTLLIB_STYPE_BEACON))
+ ieee->last_rx_ps_time = jiffies;
+
+ switch (WLAN_FC_GET_STYPE(le16_to_cpu(header->frame_ctl))) {
+
+ case RTLLIB_STYPE_BEACON:
+ RTLLIB_DEBUG_MGMT("received BEACON (%d)\n",
+ WLAN_FC_GET_STYPE(le16_to_cpu(header->frame_ctl)));
+ RTLLIB_DEBUG_SCAN("Beacon\n");
+ rtllib_process_probe_response(
+ ieee, (struct rtllib_probe_response *)header,
+ stats);
+
+ if (ieee->sta_sleep || (ieee->ps != RTLLIB_PS_DISABLED &&
+ ieee->iw_mode == IW_MODE_INFRA &&
+ ieee->state == RTLLIB_LINKED))
+ tasklet_schedule(&ieee->ps_task);
+
+ break;
+
+ case RTLLIB_STYPE_PROBE_RESP:
+ RTLLIB_DEBUG_MGMT("received PROBE RESPONSE (%d)\n",
+ WLAN_FC_GET_STYPE(le16_to_cpu(header->frame_ctl)));
+ RTLLIB_DEBUG_SCAN("Probe response\n");
+ rtllib_process_probe_response(ieee,
+ (struct rtllib_probe_response *)header, stats);
+ break;
+ case RTLLIB_STYPE_PROBE_REQ:
+ RTLLIB_DEBUG_MGMT("received PROBE RESQUEST (%d)\n",
+ WLAN_FC_GET_STYPE(
+ le16_to_cpu(header->frame_ctl)));
+ RTLLIB_DEBUG_SCAN("Probe request\n");
+ if ((ieee->softmac_features & IEEE_SOFTMAC_PROBERS) &&
+ ((ieee->iw_mode == IW_MODE_ADHOC ||
+ ieee->iw_mode == IW_MODE_MASTER) &&
+ ieee->state == RTLLIB_LINKED))
+ rtllib_rx_probe_rq(ieee, skb);
+ break;
+ }
+}
diff --git a/drivers/staging/rtl8192e/rtllib_softmac.c b/drivers/staging/rtl8192e/rtllib_softmac.c
new file mode 100644
index 000000000..23b7a4c3b
--- /dev/null
+++ b/drivers/staging/rtl8192e/rtllib_softmac.c
@@ -0,0 +1,3758 @@
+/* IEEE 802.11 SoftMAC layer
+ * Copyright (c) 2005 Andrea Merello <andrea.merello@gmail.com>
+ *
+ * Mostly extracted from the rtl8180-sa2400 driver for the
+ * in-kernel generic ieee802.11 stack.
+ *
+ * Few lines might be stolen from other part of the rtllib
+ * stack. Copyright who own it's copyright
+ *
+ * WPA code stolen from the ipw2200 driver.
+ * Copyright who own it's copyright.
+ *
+ * released under the GPL
+ */
+
+
+#include "rtllib.h"
+
+#include <linux/random.h>
+#include <linux/delay.h>
+#include <linux/uaccess.h>
+#include <linux/etherdevice.h>
+#include "dot11d.h"
+
+short rtllib_is_54g(struct rtllib_network *net)
+{
+ return (net->rates_ex_len > 0) || (net->rates_len > 4);
+}
+
+short rtllib_is_shortslot(const struct rtllib_network *net)
+{
+ return net->capability & WLAN_CAPABILITY_SHORT_SLOT_TIME;
+}
+
+/* returns the total length needed for placing the RATE MFIE
+ * tag and the EXTENDED RATE MFIE tag if needed.
+ * It encludes two bytes per tag for the tag itself and its len
+ */
+static unsigned int rtllib_MFIE_rate_len(struct rtllib_device *ieee)
+{
+ unsigned int rate_len = 0;
+
+ if (ieee->modulation & RTLLIB_CCK_MODULATION)
+ rate_len = RTLLIB_CCK_RATE_LEN + 2;
+
+ if (ieee->modulation & RTLLIB_OFDM_MODULATION)
+
+ rate_len += RTLLIB_OFDM_RATE_LEN + 2;
+
+ return rate_len;
+}
+
+/* place the MFIE rate, tag to the memory (double) pointed.
+ * Then it updates the pointer so that
+ * it points after the new MFIE tag added.
+ */
+static void rtllib_MFIE_Brate(struct rtllib_device *ieee, u8 **tag_p)
+{
+ u8 *tag = *tag_p;
+
+ if (ieee->modulation & RTLLIB_CCK_MODULATION) {
+ *tag++ = MFIE_TYPE_RATES;
+ *tag++ = 4;
+ *tag++ = RTLLIB_BASIC_RATE_MASK | RTLLIB_CCK_RATE_1MB;
+ *tag++ = RTLLIB_BASIC_RATE_MASK | RTLLIB_CCK_RATE_2MB;
+ *tag++ = RTLLIB_BASIC_RATE_MASK | RTLLIB_CCK_RATE_5MB;
+ *tag++ = RTLLIB_BASIC_RATE_MASK | RTLLIB_CCK_RATE_11MB;
+ }
+
+ /* We may add an option for custom rates that specific HW
+ * might support
+ */
+ *tag_p = tag;
+}
+
+static void rtllib_MFIE_Grate(struct rtllib_device *ieee, u8 **tag_p)
+{
+ u8 *tag = *tag_p;
+
+ if (ieee->modulation & RTLLIB_OFDM_MODULATION) {
+ *tag++ = MFIE_TYPE_RATES_EX;
+ *tag++ = 8;
+ *tag++ = RTLLIB_BASIC_RATE_MASK | RTLLIB_OFDM_RATE_6MB;
+ *tag++ = RTLLIB_BASIC_RATE_MASK | RTLLIB_OFDM_RATE_9MB;
+ *tag++ = RTLLIB_BASIC_RATE_MASK | RTLLIB_OFDM_RATE_12MB;
+ *tag++ = RTLLIB_BASIC_RATE_MASK | RTLLIB_OFDM_RATE_18MB;
+ *tag++ = RTLLIB_BASIC_RATE_MASK | RTLLIB_OFDM_RATE_24MB;
+ *tag++ = RTLLIB_BASIC_RATE_MASK | RTLLIB_OFDM_RATE_36MB;
+ *tag++ = RTLLIB_BASIC_RATE_MASK | RTLLIB_OFDM_RATE_48MB;
+ *tag++ = RTLLIB_BASIC_RATE_MASK | RTLLIB_OFDM_RATE_54MB;
+ }
+ /* We may add an option for custom rates that specific HW might
+ * support
+ */
+ *tag_p = tag;
+}
+
+static void rtllib_WMM_Info(struct rtllib_device *ieee, u8 **tag_p)
+{
+ u8 *tag = *tag_p;
+
+ *tag++ = MFIE_TYPE_GENERIC;
+ *tag++ = 7;
+ *tag++ = 0x00;
+ *tag++ = 0x50;
+ *tag++ = 0xf2;
+ *tag++ = 0x02;
+ *tag++ = 0x00;
+ *tag++ = 0x01;
+ *tag++ = MAX_SP_Len;
+ *tag_p = tag;
+}
+
+void rtllib_TURBO_Info(struct rtllib_device *ieee, u8 **tag_p)
+{
+ u8 *tag = *tag_p;
+
+ *tag++ = MFIE_TYPE_GENERIC;
+ *tag++ = 7;
+ *tag++ = 0x00;
+ *tag++ = 0xe0;
+ *tag++ = 0x4c;
+ *tag++ = 0x01;
+ *tag++ = 0x02;
+ *tag++ = 0x11;
+ *tag++ = 0x00;
+
+ *tag_p = tag;
+ netdev_alert(ieee->dev, "This is enable turbo mode IE process\n");
+}
+
+static void enqueue_mgmt(struct rtllib_device *ieee, struct sk_buff *skb)
+{
+ int nh;
+
+ nh = (ieee->mgmt_queue_head + 1) % MGMT_QUEUE_NUM;
+
+/* if the queue is full but we have newer frames then
+ * just overwrites the oldest.
+ *
+ * if (nh == ieee->mgmt_queue_tail)
+ * return -1;
+ */
+ ieee->mgmt_queue_head = nh;
+ ieee->mgmt_queue_ring[nh] = skb;
+
+}
+
+static struct sk_buff *dequeue_mgmt(struct rtllib_device *ieee)
+{
+ struct sk_buff *ret;
+
+ if (ieee->mgmt_queue_tail == ieee->mgmt_queue_head)
+ return NULL;
+
+ ret = ieee->mgmt_queue_ring[ieee->mgmt_queue_tail];
+
+ ieee->mgmt_queue_tail =
+ (ieee->mgmt_queue_tail+1) % MGMT_QUEUE_NUM;
+
+ return ret;
+}
+
+static void init_mgmt_queue(struct rtllib_device *ieee)
+{
+ ieee->mgmt_queue_tail = ieee->mgmt_queue_head = 0;
+}
+
+
+u8
+MgntQuery_TxRateExcludeCCKRates(struct rtllib_device *ieee)
+{
+ u16 i;
+ u8 QueryRate = 0;
+ u8 BasicRate;
+
+
+ for (i = 0; i < ieee->current_network.rates_len; i++) {
+ BasicRate = ieee->current_network.rates[i]&0x7F;
+ if (!rtllib_is_cck_rate(BasicRate)) {
+ if (QueryRate == 0) {
+ QueryRate = BasicRate;
+ } else {
+ if (BasicRate < QueryRate)
+ QueryRate = BasicRate;
+ }
+ }
+ }
+
+ if (QueryRate == 0) {
+ QueryRate = 12;
+ netdev_info(ieee->dev, "No BasicRate found!!\n");
+ }
+ return QueryRate;
+}
+
+static u8 MgntQuery_MgntFrameTxRate(struct rtllib_device *ieee)
+{
+ struct rt_hi_throughput *pHTInfo = ieee->pHTInfo;
+ u8 rate;
+
+ if (pHTInfo->IOTAction & HT_IOT_ACT_MGNT_USE_CCK_6M)
+ rate = 0x0c;
+ else
+ rate = ieee->basic_rate & 0x7f;
+
+ if (rate == 0) {
+ if (ieee->mode == IEEE_A ||
+ ieee->mode == IEEE_N_5G ||
+ (ieee->mode == IEEE_N_24G && !pHTInfo->bCurSuppCCK))
+ rate = 0x0c;
+ else
+ rate = 0x02;
+ }
+
+ return rate;
+}
+
+inline void softmac_mgmt_xmit(struct sk_buff *skb, struct rtllib_device *ieee)
+{
+ unsigned long flags;
+ short single = ieee->softmac_features & IEEE_SOFTMAC_SINGLE_QUEUE;
+ struct rtllib_hdr_3addr *header =
+ (struct rtllib_hdr_3addr *) skb->data;
+
+ struct cb_desc *tcb_desc = (struct cb_desc *)(skb->cb + 8);
+
+ spin_lock_irqsave(&ieee->lock, flags);
+
+ /* called with 2nd param 0, no mgmt lock required */
+ rtllib_sta_wakeup(ieee, 0);
+
+ if (le16_to_cpu(header->frame_ctl) == RTLLIB_STYPE_BEACON)
+ tcb_desc->queue_index = BEACON_QUEUE;
+ else
+ tcb_desc->queue_index = MGNT_QUEUE;
+
+ if (ieee->disable_mgnt_queue)
+ tcb_desc->queue_index = HIGH_QUEUE;
+
+ tcb_desc->data_rate = MgntQuery_MgntFrameTxRate(ieee);
+ tcb_desc->RATRIndex = 7;
+ tcb_desc->bTxDisableRateFallBack = 1;
+ tcb_desc->bTxUseDriverAssingedRate = 1;
+ if (single) {
+ if (ieee->queue_stop) {
+ enqueue_mgmt(ieee, skb);
+ } else {
+ header->seq_ctl = cpu_to_le16(ieee->seq_ctrl[0]<<4);
+
+ if (ieee->seq_ctrl[0] == 0xFFF)
+ ieee->seq_ctrl[0] = 0;
+ else
+ ieee->seq_ctrl[0]++;
+
+ /* avoid watchdog triggers */
+ ieee->softmac_data_hard_start_xmit(skb, ieee->dev,
+ ieee->basic_rate);
+ }
+
+ spin_unlock_irqrestore(&ieee->lock, flags);
+ } else {
+ spin_unlock_irqrestore(&ieee->lock, flags);
+ spin_lock_irqsave(&ieee->mgmt_tx_lock, flags);
+
+ header->seq_ctl = cpu_to_le16(ieee->seq_ctrl[0] << 4);
+
+ if (ieee->seq_ctrl[0] == 0xFFF)
+ ieee->seq_ctrl[0] = 0;
+ else
+ ieee->seq_ctrl[0]++;
+
+ /* check whether the managed packet queued greater than 5 */
+ if (!ieee->check_nic_enough_desc(ieee->dev, tcb_desc->queue_index) ||
+ (skb_queue_len(&ieee->skb_waitQ[tcb_desc->queue_index]) != 0) ||
+ (ieee->queue_stop)) {
+ /* insert the skb packet to the management queue
+ *
+ * as for the completion function, it does not need
+ * to check it any more.
+ */
+ netdev_info(ieee->dev,
+ "%s():insert to waitqueue, queue_index:%d!\n",
+ __func__, tcb_desc->queue_index);
+ skb_queue_tail(&ieee->skb_waitQ[tcb_desc->queue_index],
+ skb);
+ } else {
+ ieee->softmac_hard_start_xmit(skb, ieee->dev);
+ }
+ spin_unlock_irqrestore(&ieee->mgmt_tx_lock, flags);
+ }
+}
+
+inline void softmac_ps_mgmt_xmit(struct sk_buff *skb,
+ struct rtllib_device *ieee)
+{
+ short single = ieee->softmac_features & IEEE_SOFTMAC_SINGLE_QUEUE;
+ struct rtllib_hdr_3addr *header =
+ (struct rtllib_hdr_3addr *) skb->data;
+ u16 fc, type, stype;
+ struct cb_desc *tcb_desc = (struct cb_desc *)(skb->cb + 8);
+
+ fc = le16_to_cpu(header->frame_ctl);
+ type = WLAN_FC_GET_TYPE(fc);
+ stype = WLAN_FC_GET_STYPE(fc);
+
+
+ if (stype != RTLLIB_STYPE_PSPOLL)
+ tcb_desc->queue_index = MGNT_QUEUE;
+ else
+ tcb_desc->queue_index = HIGH_QUEUE;
+
+ if (ieee->disable_mgnt_queue)
+ tcb_desc->queue_index = HIGH_QUEUE;
+
+
+ tcb_desc->data_rate = MgntQuery_MgntFrameTxRate(ieee);
+ tcb_desc->RATRIndex = 7;
+ tcb_desc->bTxDisableRateFallBack = 1;
+ tcb_desc->bTxUseDriverAssingedRate = 1;
+ if (single) {
+ if (type != RTLLIB_FTYPE_CTL) {
+ header->seq_ctl = cpu_to_le16(ieee->seq_ctrl[0] << 4);
+
+ if (ieee->seq_ctrl[0] == 0xFFF)
+ ieee->seq_ctrl[0] = 0;
+ else
+ ieee->seq_ctrl[0]++;
+
+ }
+ /* avoid watchdog triggers */
+ ieee->softmac_data_hard_start_xmit(skb, ieee->dev,
+ ieee->basic_rate);
+
+ } else {
+ if (type != RTLLIB_FTYPE_CTL) {
+ header->seq_ctl = cpu_to_le16(ieee->seq_ctrl[0] << 4);
+
+ if (ieee->seq_ctrl[0] == 0xFFF)
+ ieee->seq_ctrl[0] = 0;
+ else
+ ieee->seq_ctrl[0]++;
+ }
+ ieee->softmac_hard_start_xmit(skb, ieee->dev);
+
+ }
+}
+
+static inline struct sk_buff *rtllib_probe_req(struct rtllib_device *ieee)
+{
+ unsigned int len, rate_len;
+ u8 *tag;
+ struct sk_buff *skb;
+ struct rtllib_probe_request *req;
+
+ len = ieee->current_network.ssid_len;
+
+ rate_len = rtllib_MFIE_rate_len(ieee);
+
+ skb = dev_alloc_skb(sizeof(struct rtllib_probe_request) +
+ 2 + len + rate_len + ieee->tx_headroom);
+
+ if (!skb)
+ return NULL;
+
+ skb_reserve(skb, ieee->tx_headroom);
+
+ req = (struct rtllib_probe_request *) skb_put(skb,
+ sizeof(struct rtllib_probe_request));
+ req->header.frame_ctl = cpu_to_le16(RTLLIB_STYPE_PROBE_REQ);
+ req->header.duration_id = 0;
+
+ memset(req->header.addr1, 0xff, ETH_ALEN);
+ memcpy(req->header.addr2, ieee->dev->dev_addr, ETH_ALEN);
+ memset(req->header.addr3, 0xff, ETH_ALEN);
+
+ tag = (u8 *) skb_put(skb, len + 2 + rate_len);
+
+ *tag++ = MFIE_TYPE_SSID;
+ *tag++ = len;
+ memcpy(tag, ieee->current_network.ssid, len);
+ tag += len;
+
+ rtllib_MFIE_Brate(ieee, &tag);
+ rtllib_MFIE_Grate(ieee, &tag);
+
+ return skb;
+}
+
+struct sk_buff *rtllib_get_beacon_(struct rtllib_device *ieee);
+
+static void rtllib_send_beacon(struct rtllib_device *ieee)
+{
+ struct sk_buff *skb;
+
+ if (!ieee->ieee_up)
+ return;
+ skb = rtllib_get_beacon_(ieee);
+
+ if (skb) {
+ softmac_mgmt_xmit(skb, ieee);
+ ieee->softmac_stats.tx_beacons++;
+ }
+
+ if (ieee->beacon_txing && ieee->ieee_up)
+ mod_timer(&ieee->beacon_timer, jiffies +
+ (msecs_to_jiffies(ieee->current_network.beacon_interval - 5)));
+}
+
+
+static void rtllib_send_beacon_cb(unsigned long _ieee)
+{
+ struct rtllib_device *ieee =
+ (struct rtllib_device *) _ieee;
+ unsigned long flags;
+
+ spin_lock_irqsave(&ieee->beacon_lock, flags);
+ rtllib_send_beacon(ieee);
+ spin_unlock_irqrestore(&ieee->beacon_lock, flags);
+}
+
+/* Enables network monitor mode, all rx packets will be received. */
+void rtllib_EnableNetMonitorMode(struct net_device *dev,
+ bool bInitState)
+{
+ struct rtllib_device *ieee = netdev_priv_rsl(dev);
+
+ netdev_info(dev, "========>Enter Monitor Mode\n");
+
+ ieee->AllowAllDestAddrHandler(dev, true, !bInitState);
+}
+
+
+/* Disables network monitor mode. Only packets destinated to
+ * us will be received.
+ */
+void rtllib_DisableNetMonitorMode(struct net_device *dev,
+ bool bInitState)
+{
+ struct rtllib_device *ieee = netdev_priv_rsl(dev);
+
+ netdev_info(dev, "========>Exit Monitor Mode\n");
+
+ ieee->AllowAllDestAddrHandler(dev, false, !bInitState);
+}
+
+
+/* Enables the specialized promiscuous mode required by Intel.
+ * In this mode, Intel intends to hear traffics from/to other STAs in the
+ * same BSS. Therefore we don't have to disable checking BSSID and we only need
+ * to allow all dest. BUT: if we enable checking BSSID then we can't recv
+ * packets from other STA.
+ */
+void rtllib_EnableIntelPromiscuousMode(struct net_device *dev,
+ bool bInitState)
+{
+ bool bFilterOutNonAssociatedBSSID = false;
+
+ struct rtllib_device *ieee = netdev_priv_rsl(dev);
+
+ netdev_info(dev, "========>Enter Intel Promiscuous Mode\n");
+
+ ieee->AllowAllDestAddrHandler(dev, true, !bInitState);
+ ieee->SetHwRegHandler(dev, HW_VAR_CECHK_BSSID,
+ (u8 *)&bFilterOutNonAssociatedBSSID);
+
+ ieee->bNetPromiscuousMode = true;
+}
+EXPORT_SYMBOL(rtllib_EnableIntelPromiscuousMode);
+
+
+/* Disables the specialized promiscuous mode required by Intel.
+ * See MgntEnableIntelPromiscuousMode for detail.
+ */
+void rtllib_DisableIntelPromiscuousMode(struct net_device *dev,
+ bool bInitState)
+{
+ bool bFilterOutNonAssociatedBSSID = true;
+
+ struct rtllib_device *ieee = netdev_priv_rsl(dev);
+
+ netdev_info(dev, "========>Exit Intel Promiscuous Mode\n");
+
+ ieee->AllowAllDestAddrHandler(dev, false, !bInitState);
+ ieee->SetHwRegHandler(dev, HW_VAR_CECHK_BSSID,
+ (u8 *)&bFilterOutNonAssociatedBSSID);
+
+ ieee->bNetPromiscuousMode = false;
+}
+EXPORT_SYMBOL(rtllib_DisableIntelPromiscuousMode);
+
+static void rtllib_send_probe(struct rtllib_device *ieee, u8 is_mesh)
+{
+ struct sk_buff *skb;
+
+ skb = rtllib_probe_req(ieee);
+ if (skb) {
+ softmac_mgmt_xmit(skb, ieee);
+ ieee->softmac_stats.tx_probe_rq++;
+ }
+}
+
+
+void rtllib_send_probe_requests(struct rtllib_device *ieee, u8 is_mesh)
+{
+ if (ieee->active_scan && (ieee->softmac_features &
+ IEEE_SOFTMAC_PROBERQ)) {
+ rtllib_send_probe(ieee, 0);
+ rtllib_send_probe(ieee, 0);
+ }
+}
+
+static void rtllib_softmac_hint11d_wq(void *data)
+{
+}
+
+void rtllib_update_active_chan_map(struct rtllib_device *ieee)
+{
+ memcpy(ieee->active_channel_map, GET_DOT11D_INFO(ieee)->channel_map,
+ MAX_CHANNEL_NUMBER+1);
+}
+
+/* this performs syncro scan blocking the caller until all channels
+ * in the allowed channel map has been checked.
+ */
+void rtllib_softmac_scan_syncro(struct rtllib_device *ieee, u8 is_mesh)
+{
+ union iwreq_data wrqu;
+ short ch = 0;
+
+ rtllib_update_active_chan_map(ieee);
+
+ ieee->be_scan_inprogress = true;
+
+ down(&ieee->scan_sem);
+
+ while (1) {
+ do {
+ ch++;
+ if (ch > MAX_CHANNEL_NUMBER)
+ goto out; /* scan completed */
+ } while (!ieee->active_channel_map[ch]);
+
+ /* this function can be called in two situations
+ * 1- We have switched to ad-hoc mode and we are
+ * performing a complete syncro scan before conclude
+ * there are no interesting cell and to create a
+ * new one. In this case the link state is
+ * RTLLIB_NOLINK until we found an interesting cell.
+ * If so the ieee8021_new_net, called by the RX path
+ * will set the state to RTLLIB_LINKED, so we stop
+ * scanning
+ * 2- We are linked and the root uses run iwlist scan.
+ * So we switch to RTLLIB_LINKED_SCANNING to remember
+ * that we are still logically linked (not interested in
+ * new network events, despite for updating the net list,
+ * but we are temporarly 'unlinked' as the driver shall
+ * not filter RX frames and the channel is changing.
+ * So the only situation in which are interested is to check
+ * if the state become LINKED because of the #1 situation
+ */
+
+ if (ieee->state == RTLLIB_LINKED)
+ goto out;
+ if (ieee->sync_scan_hurryup) {
+ netdev_info(ieee->dev,
+ "============>sync_scan_hurryup out\n");
+ goto out;
+ }
+
+ ieee->set_chan(ieee->dev, ch);
+ if (ieee->active_channel_map[ch] == 1)
+ rtllib_send_probe_requests(ieee, 0);
+
+ /* this prevent excessive time wait when we
+ * need to wait for a syncro scan to end..
+ */
+ msleep_interruptible_rsl(RTLLIB_SOFTMAC_SCAN_TIME);
+ }
+out:
+ ieee->actscanning = false;
+ ieee->sync_scan_hurryup = 0;
+
+ if (ieee->state >= RTLLIB_LINKED) {
+ if (IS_DOT11D_ENABLE(ieee))
+ DOT11D_ScanComplete(ieee);
+ }
+ up(&ieee->scan_sem);
+
+ ieee->be_scan_inprogress = false;
+
+ memset(&wrqu, 0, sizeof(wrqu));
+ wireless_send_event(ieee->dev, SIOCGIWSCAN, &wrqu, NULL);
+}
+
+static void rtllib_softmac_scan_wq(void *data)
+{
+ struct rtllib_device *ieee = container_of_dwork_rsl(data,
+ struct rtllib_device, softmac_scan_wq);
+ u8 last_channel = ieee->current_network.channel;
+
+ rtllib_update_active_chan_map(ieee);
+
+ if (!ieee->ieee_up)
+ return;
+ if (rtllib_act_scanning(ieee, true))
+ return;
+
+ down(&ieee->scan_sem);
+
+ if (ieee->eRFPowerState == eRfOff) {
+ netdev_info(ieee->dev,
+ "======>%s():rf state is eRfOff, return\n",
+ __func__);
+ goto out1;
+ }
+
+ do {
+ ieee->current_network.channel =
+ (ieee->current_network.channel + 1) %
+ MAX_CHANNEL_NUMBER;
+ if (ieee->scan_watch_dog++ > MAX_CHANNEL_NUMBER) {
+ if (!ieee->active_channel_map[ieee->current_network.channel])
+ ieee->current_network.channel = 6;
+ goto out; /* no good chans */
+ }
+ } while (!ieee->active_channel_map[ieee->current_network.channel]);
+
+ if (ieee->scanning_continue == 0)
+ goto out;
+
+ ieee->set_chan(ieee->dev, ieee->current_network.channel);
+
+ if (ieee->active_channel_map[ieee->current_network.channel] == 1)
+ rtllib_send_probe_requests(ieee, 0);
+
+ queue_delayed_work_rsl(ieee->wq, &ieee->softmac_scan_wq,
+ msecs_to_jiffies(RTLLIB_SOFTMAC_SCAN_TIME));
+
+ up(&ieee->scan_sem);
+ return;
+
+out:
+ if (IS_DOT11D_ENABLE(ieee))
+ DOT11D_ScanComplete(ieee);
+ ieee->current_network.channel = last_channel;
+
+out1:
+ ieee->actscanning = false;
+ ieee->scan_watch_dog = 0;
+ ieee->scanning_continue = 0;
+ up(&ieee->scan_sem);
+}
+
+
+
+static void rtllib_beacons_start(struct rtllib_device *ieee)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&ieee->beacon_lock, flags);
+
+ ieee->beacon_txing = 1;
+ rtllib_send_beacon(ieee);
+
+ spin_unlock_irqrestore(&ieee->beacon_lock, flags);
+}
+
+static void rtllib_beacons_stop(struct rtllib_device *ieee)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&ieee->beacon_lock, flags);
+
+ ieee->beacon_txing = 0;
+ del_timer_sync(&ieee->beacon_timer);
+
+ spin_unlock_irqrestore(&ieee->beacon_lock, flags);
+
+}
+
+
+void rtllib_stop_send_beacons(struct rtllib_device *ieee)
+{
+ if (ieee->stop_send_beacons)
+ ieee->stop_send_beacons(ieee->dev);
+ if (ieee->softmac_features & IEEE_SOFTMAC_BEACONS)
+ rtllib_beacons_stop(ieee);
+}
+EXPORT_SYMBOL(rtllib_stop_send_beacons);
+
+
+void rtllib_start_send_beacons(struct rtllib_device *ieee)
+{
+ if (ieee->start_send_beacons)
+ ieee->start_send_beacons(ieee->dev);
+ if (ieee->softmac_features & IEEE_SOFTMAC_BEACONS)
+ rtllib_beacons_start(ieee);
+}
+EXPORT_SYMBOL(rtllib_start_send_beacons);
+
+
+static void rtllib_softmac_stop_scan(struct rtllib_device *ieee)
+{
+ down(&ieee->scan_sem);
+ ieee->scan_watch_dog = 0;
+ if (ieee->scanning_continue == 1) {
+ ieee->scanning_continue = 0;
+ ieee->actscanning = false;
+
+ cancel_delayed_work(&ieee->softmac_scan_wq);
+ }
+
+ up(&ieee->scan_sem);
+}
+
+void rtllib_stop_scan(struct rtllib_device *ieee)
+{
+ if (ieee->softmac_features & IEEE_SOFTMAC_SCAN) {
+ rtllib_softmac_stop_scan(ieee);
+ } else {
+ if (ieee->rtllib_stop_hw_scan)
+ ieee->rtllib_stop_hw_scan(ieee->dev);
+ }
+}
+EXPORT_SYMBOL(rtllib_stop_scan);
+
+void rtllib_stop_scan_syncro(struct rtllib_device *ieee)
+{
+ if (ieee->softmac_features & IEEE_SOFTMAC_SCAN) {
+ ieee->sync_scan_hurryup = 1;
+ } else {
+ if (ieee->rtllib_stop_hw_scan)
+ ieee->rtllib_stop_hw_scan(ieee->dev);
+ }
+}
+EXPORT_SYMBOL(rtllib_stop_scan_syncro);
+
+bool rtllib_act_scanning(struct rtllib_device *ieee, bool sync_scan)
+{
+ if (ieee->softmac_features & IEEE_SOFTMAC_SCAN) {
+ if (sync_scan)
+ return ieee->be_scan_inprogress;
+ else
+ return ieee->actscanning || ieee->be_scan_inprogress;
+ } else {
+ return test_bit(STATUS_SCANNING, &ieee->status);
+ }
+}
+EXPORT_SYMBOL(rtllib_act_scanning);
+
+/* called with ieee->lock held */
+static void rtllib_start_scan(struct rtllib_device *ieee)
+{
+ RT_TRACE(COMP_DBG, "===>%s()\n", __func__);
+ if (ieee->rtllib_ips_leave_wq != NULL)
+ ieee->rtllib_ips_leave_wq(ieee->dev);
+
+ if (IS_DOT11D_ENABLE(ieee)) {
+ if (IS_COUNTRY_IE_VALID(ieee))
+ RESET_CIE_WATCHDOG(ieee);
+ }
+ if (ieee->softmac_features & IEEE_SOFTMAC_SCAN) {
+ if (ieee->scanning_continue == 0) {
+ ieee->actscanning = true;
+ ieee->scanning_continue = 1;
+ queue_delayed_work_rsl(ieee->wq,
+ &ieee->softmac_scan_wq, 0);
+ }
+ } else {
+ if (ieee->rtllib_start_hw_scan)
+ ieee->rtllib_start_hw_scan(ieee->dev);
+ }
+}
+
+/* called with wx_sem held */
+void rtllib_start_scan_syncro(struct rtllib_device *ieee, u8 is_mesh)
+{
+ if (IS_DOT11D_ENABLE(ieee)) {
+ if (IS_COUNTRY_IE_VALID(ieee))
+ RESET_CIE_WATCHDOG(ieee);
+ }
+ ieee->sync_scan_hurryup = 0;
+ if (ieee->softmac_features & IEEE_SOFTMAC_SCAN) {
+ rtllib_softmac_scan_syncro(ieee, is_mesh);
+ } else {
+ if (ieee->rtllib_start_hw_scan)
+ ieee->rtllib_start_hw_scan(ieee->dev);
+ }
+}
+EXPORT_SYMBOL(rtllib_start_scan_syncro);
+
+inline struct sk_buff *rtllib_authentication_req(struct rtllib_network *beacon,
+ struct rtllib_device *ieee, int challengelen, u8 *daddr)
+{
+ struct sk_buff *skb;
+ struct rtllib_authentication *auth;
+ int len = 0;
+
+ len = sizeof(struct rtllib_authentication) + challengelen +
+ ieee->tx_headroom + 4;
+ skb = dev_alloc_skb(len);
+
+ if (!skb)
+ return NULL;
+
+ skb_reserve(skb, ieee->tx_headroom);
+
+ auth = (struct rtllib_authentication *)
+ skb_put(skb, sizeof(struct rtllib_authentication));
+
+ auth->header.frame_ctl = cpu_to_le16(RTLLIB_STYPE_AUTH);
+ if (challengelen)
+ auth->header.frame_ctl |= cpu_to_le16(RTLLIB_FCTL_WEP);
+
+ auth->header.duration_id = cpu_to_le16(0x013a);
+ memcpy(auth->header.addr1, beacon->bssid, ETH_ALEN);
+ memcpy(auth->header.addr2, ieee->dev->dev_addr, ETH_ALEN);
+ memcpy(auth->header.addr3, beacon->bssid, ETH_ALEN);
+ if (ieee->auth_mode == 0)
+ auth->algorithm = WLAN_AUTH_OPEN;
+ else if (ieee->auth_mode == 1)
+ auth->algorithm = cpu_to_le16(WLAN_AUTH_SHARED_KEY);
+ else if (ieee->auth_mode == 2)
+ auth->algorithm = WLAN_AUTH_OPEN;
+ auth->transaction = cpu_to_le16(ieee->associate_seq);
+ ieee->associate_seq++;
+
+ auth->status = cpu_to_le16(WLAN_STATUS_SUCCESS);
+
+ return skb;
+}
+
+static struct sk_buff *rtllib_probe_resp(struct rtllib_device *ieee, u8 *dest)
+{
+ u8 *tag;
+ int beacon_size;
+ struct rtllib_probe_response *beacon_buf;
+ struct sk_buff *skb = NULL;
+ int encrypt;
+ int atim_len, erp_len;
+ struct lib80211_crypt_data *crypt;
+
+ char *ssid = ieee->current_network.ssid;
+ int ssid_len = ieee->current_network.ssid_len;
+ int rate_len = ieee->current_network.rates_len+2;
+ int rate_ex_len = ieee->current_network.rates_ex_len;
+ int wpa_ie_len = ieee->wpa_ie_len;
+ u8 erpinfo_content = 0;
+
+ u8 *tmp_ht_cap_buf = NULL;
+ u8 tmp_ht_cap_len = 0;
+ u8 *tmp_ht_info_buf = NULL;
+ u8 tmp_ht_info_len = 0;
+ struct rt_hi_throughput *pHTInfo = ieee->pHTInfo;
+ u8 *tmp_generic_ie_buf = NULL;
+ u8 tmp_generic_ie_len = 0;
+
+ if (rate_ex_len > 0)
+ rate_ex_len += 2;
+
+ if (ieee->current_network.capability & WLAN_CAPABILITY_IBSS)
+ atim_len = 4;
+ else
+ atim_len = 0;
+
+ if ((ieee->current_network.mode == IEEE_G) ||
+ (ieee->current_network.mode == IEEE_N_24G &&
+ ieee->pHTInfo->bCurSuppCCK)) {
+ erp_len = 3;
+ erpinfo_content = 0;
+ if (ieee->current_network.buseprotection)
+ erpinfo_content |= ERP_UseProtection;
+ } else
+ erp_len = 0;
+
+ crypt = ieee->crypt_info.crypt[ieee->crypt_info.tx_keyidx];
+ encrypt = ieee->host_encrypt && crypt && crypt->ops &&
+ ((0 == strcmp(crypt->ops->name, "R-WEP") || wpa_ie_len));
+ if (ieee->pHTInfo->bCurrentHTSupport) {
+ tmp_ht_cap_buf = (u8 *) &(ieee->pHTInfo->SelfHTCap);
+ tmp_ht_cap_len = sizeof(ieee->pHTInfo->SelfHTCap);
+ tmp_ht_info_buf = (u8 *) &(ieee->pHTInfo->SelfHTInfo);
+ tmp_ht_info_len = sizeof(ieee->pHTInfo->SelfHTInfo);
+ HTConstructCapabilityElement(ieee, tmp_ht_cap_buf,
+ &tmp_ht_cap_len, encrypt, false);
+ HTConstructInfoElement(ieee, tmp_ht_info_buf, &tmp_ht_info_len,
+ encrypt);
+
+ if (pHTInfo->bRegRT2RTAggregation) {
+ tmp_generic_ie_buf = ieee->pHTInfo->szRT2RTAggBuffer;
+ tmp_generic_ie_len =
+ sizeof(ieee->pHTInfo->szRT2RTAggBuffer);
+ HTConstructRT2RTAggElement(ieee, tmp_generic_ie_buf,
+ &tmp_generic_ie_len);
+ }
+ }
+
+ beacon_size = sizeof(struct rtllib_probe_response)+2+
+ ssid_len + 3 + rate_len + rate_ex_len + atim_len + erp_len
+ + wpa_ie_len + ieee->tx_headroom;
+ skb = dev_alloc_skb(beacon_size);
+ if (!skb)
+ return NULL;
+
+ skb_reserve(skb, ieee->tx_headroom);
+
+ beacon_buf = (struct rtllib_probe_response *) skb_put(skb,
+ (beacon_size - ieee->tx_headroom));
+ memcpy(beacon_buf->header.addr1, dest, ETH_ALEN);
+ memcpy(beacon_buf->header.addr2, ieee->dev->dev_addr, ETH_ALEN);
+ memcpy(beacon_buf->header.addr3, ieee->current_network.bssid, ETH_ALEN);
+
+ beacon_buf->header.duration_id = 0;
+ beacon_buf->beacon_interval =
+ cpu_to_le16(ieee->current_network.beacon_interval);
+ beacon_buf->capability =
+ cpu_to_le16(ieee->current_network.capability &
+ WLAN_CAPABILITY_IBSS);
+ beacon_buf->capability |=
+ cpu_to_le16(ieee->current_network.capability &
+ WLAN_CAPABILITY_SHORT_PREAMBLE);
+
+ if (ieee->short_slot && (ieee->current_network.capability &
+ WLAN_CAPABILITY_SHORT_SLOT_TIME))
+ beacon_buf->capability |=
+ cpu_to_le16(WLAN_CAPABILITY_SHORT_SLOT_TIME);
+
+ crypt = ieee->crypt_info.crypt[ieee->crypt_info.tx_keyidx];
+ if (encrypt)
+ beacon_buf->capability |= cpu_to_le16(WLAN_CAPABILITY_PRIVACY);
+
+
+ beacon_buf->header.frame_ctl = cpu_to_le16(RTLLIB_STYPE_PROBE_RESP);
+ beacon_buf->info_element[0].id = MFIE_TYPE_SSID;
+ beacon_buf->info_element[0].len = ssid_len;
+
+ tag = (u8 *) beacon_buf->info_element[0].data;
+
+ memcpy(tag, ssid, ssid_len);
+
+ tag += ssid_len;
+
+ *(tag++) = MFIE_TYPE_RATES;
+ *(tag++) = rate_len-2;
+ memcpy(tag, ieee->current_network.rates, rate_len-2);
+ tag += rate_len-2;
+
+ *(tag++) = MFIE_TYPE_DS_SET;
+ *(tag++) = 1;
+ *(tag++) = ieee->current_network.channel;
+
+ if (atim_len) {
+ u16 val16;
+ *(tag++) = MFIE_TYPE_IBSS_SET;
+ *(tag++) = 2;
+ val16 = ieee->current_network.atim_window;
+ memcpy((u8 *)tag, (u8 *)&val16, 2);
+ tag += 2;
+ }
+
+ if (erp_len) {
+ *(tag++) = MFIE_TYPE_ERP;
+ *(tag++) = 1;
+ *(tag++) = erpinfo_content;
+ }
+ if (rate_ex_len) {
+ *(tag++) = MFIE_TYPE_RATES_EX;
+ *(tag++) = rate_ex_len-2;
+ memcpy(tag, ieee->current_network.rates_ex, rate_ex_len-2);
+ tag += rate_ex_len-2;
+ }
+
+ if (wpa_ie_len) {
+ if (ieee->iw_mode == IW_MODE_ADHOC)
+ memcpy(&ieee->wpa_ie[14], &ieee->wpa_ie[8], 4);
+ memcpy(tag, ieee->wpa_ie, ieee->wpa_ie_len);
+ tag += ieee->wpa_ie_len;
+ }
+ return skb;
+}
+
+static struct sk_buff *rtllib_assoc_resp(struct rtllib_device *ieee, u8 *dest)
+{
+ struct sk_buff *skb;
+ u8 *tag;
+
+ struct lib80211_crypt_data *crypt;
+ struct rtllib_assoc_response_frame *assoc;
+ short encrypt;
+
+ unsigned int rate_len = rtllib_MFIE_rate_len(ieee);
+ int len = sizeof(struct rtllib_assoc_response_frame) + rate_len +
+ ieee->tx_headroom;
+
+ skb = dev_alloc_skb(len);
+
+ if (!skb)
+ return NULL;
+
+ skb_reserve(skb, ieee->tx_headroom);
+
+ assoc = (struct rtllib_assoc_response_frame *)
+ skb_put(skb, sizeof(struct rtllib_assoc_response_frame));
+
+ assoc->header.frame_ctl = cpu_to_le16(RTLLIB_STYPE_ASSOC_RESP);
+ memcpy(assoc->header.addr1, dest, ETH_ALEN);
+ memcpy(assoc->header.addr3, ieee->dev->dev_addr, ETH_ALEN);
+ memcpy(assoc->header.addr2, ieee->dev->dev_addr, ETH_ALEN);
+ assoc->capability = cpu_to_le16(ieee->iw_mode == IW_MODE_MASTER ?
+ WLAN_CAPABILITY_ESS : WLAN_CAPABILITY_IBSS);
+
+
+ if (ieee->short_slot)
+ assoc->capability |=
+ cpu_to_le16(WLAN_CAPABILITY_SHORT_SLOT_TIME);
+
+ if (ieee->host_encrypt)
+ crypt = ieee->crypt_info.crypt[ieee->crypt_info.tx_keyidx];
+ else
+ crypt = NULL;
+
+ encrypt = (crypt && crypt->ops);
+
+ if (encrypt)
+ assoc->capability |= cpu_to_le16(WLAN_CAPABILITY_PRIVACY);
+
+ assoc->status = 0;
+ assoc->aid = cpu_to_le16(ieee->assoc_id);
+ if (ieee->assoc_id == 0x2007)
+ ieee->assoc_id = 0;
+ else
+ ieee->assoc_id++;
+
+ tag = (u8 *) skb_put(skb, rate_len);
+ rtllib_MFIE_Brate(ieee, &tag);
+ rtllib_MFIE_Grate(ieee, &tag);
+
+ return skb;
+}
+
+static struct sk_buff *rtllib_auth_resp(struct rtllib_device *ieee, int status,
+ u8 *dest)
+{
+ struct sk_buff *skb = NULL;
+ struct rtllib_authentication *auth;
+ int len = ieee->tx_headroom + sizeof(struct rtllib_authentication) + 1;
+
+ skb = dev_alloc_skb(len);
+ if (!skb)
+ return NULL;
+
+ skb->len = sizeof(struct rtllib_authentication);
+
+ skb_reserve(skb, ieee->tx_headroom);
+
+ auth = (struct rtllib_authentication *)
+ skb_put(skb, sizeof(struct rtllib_authentication));
+
+ auth->status = cpu_to_le16(status);
+ auth->transaction = cpu_to_le16(2);
+ auth->algorithm = cpu_to_le16(WLAN_AUTH_OPEN);
+
+ memcpy(auth->header.addr3, ieee->dev->dev_addr, ETH_ALEN);
+ memcpy(auth->header.addr2, ieee->dev->dev_addr, ETH_ALEN);
+ memcpy(auth->header.addr1, dest, ETH_ALEN);
+ auth->header.frame_ctl = cpu_to_le16(RTLLIB_STYPE_AUTH);
+ return skb;
+
+
+}
+
+static struct sk_buff *rtllib_null_func(struct rtllib_device *ieee, short pwr)
+{
+ struct sk_buff *skb;
+ struct rtllib_hdr_3addr *hdr;
+
+ skb = dev_alloc_skb(sizeof(struct rtllib_hdr_3addr)+ieee->tx_headroom);
+ if (!skb)
+ return NULL;
+
+ skb_reserve(skb, ieee->tx_headroom);
+
+ hdr = (struct rtllib_hdr_3addr *)skb_put(skb,
+ sizeof(struct rtllib_hdr_3addr));
+
+ memcpy(hdr->addr1, ieee->current_network.bssid, ETH_ALEN);
+ memcpy(hdr->addr2, ieee->dev->dev_addr, ETH_ALEN);
+ memcpy(hdr->addr3, ieee->current_network.bssid, ETH_ALEN);
+
+ hdr->frame_ctl = cpu_to_le16(RTLLIB_FTYPE_DATA |
+ RTLLIB_STYPE_NULLFUNC | RTLLIB_FCTL_TODS |
+ (pwr ? RTLLIB_FCTL_PM : 0));
+
+ return skb;
+
+
+}
+
+static struct sk_buff *rtllib_pspoll_func(struct rtllib_device *ieee)
+{
+ struct sk_buff *skb;
+ struct rtllib_pspoll_hdr *hdr;
+
+ skb = dev_alloc_skb(sizeof(struct rtllib_pspoll_hdr)+ieee->tx_headroom);
+ if (!skb)
+ return NULL;
+
+ skb_reserve(skb, ieee->tx_headroom);
+
+ hdr = (struct rtllib_pspoll_hdr *)skb_put(skb,
+ sizeof(struct rtllib_pspoll_hdr));
+
+ memcpy(hdr->bssid, ieee->current_network.bssid, ETH_ALEN);
+ memcpy(hdr->ta, ieee->dev->dev_addr, ETH_ALEN);
+
+ hdr->aid = cpu_to_le16(ieee->assoc_id | 0xc000);
+ hdr->frame_ctl = cpu_to_le16(RTLLIB_FTYPE_CTL | RTLLIB_STYPE_PSPOLL |
+ RTLLIB_FCTL_PM);
+
+ return skb;
+
+}
+
+static void rtllib_resp_to_assoc_rq(struct rtllib_device *ieee, u8 *dest)
+{
+ struct sk_buff *buf = rtllib_assoc_resp(ieee, dest);
+
+ if (buf)
+ softmac_mgmt_xmit(buf, ieee);
+}
+
+
+static void rtllib_resp_to_auth(struct rtllib_device *ieee, int s, u8 *dest)
+{
+ struct sk_buff *buf = rtllib_auth_resp(ieee, s, dest);
+
+ if (buf)
+ softmac_mgmt_xmit(buf, ieee);
+}
+
+
+static void rtllib_resp_to_probe(struct rtllib_device *ieee, u8 *dest)
+{
+ struct sk_buff *buf = rtllib_probe_resp(ieee, dest);
+
+ if (buf)
+ softmac_mgmt_xmit(buf, ieee);
+}
+
+
+inline int SecIsInPMKIDList(struct rtllib_device *ieee, u8 *bssid)
+{
+ int i = 0;
+
+ do {
+ if ((ieee->PMKIDList[i].bUsed) &&
+ (memcmp(ieee->PMKIDList[i].Bssid, bssid, ETH_ALEN) == 0))
+ break;
+ i++;
+ } while (i < NUM_PMKID_CACHE);
+
+ if (i == NUM_PMKID_CACHE)
+ i = -1;
+ return i;
+}
+
+inline struct sk_buff *rtllib_association_req(struct rtllib_network *beacon,
+ struct rtllib_device *ieee)
+{
+ struct sk_buff *skb;
+ struct rtllib_assoc_request_frame *hdr;
+ u8 *tag, *ies;
+ int i;
+ u8 *ht_cap_buf = NULL;
+ u8 ht_cap_len = 0;
+ u8 *realtek_ie_buf = NULL;
+ u8 realtek_ie_len = 0;
+ int wpa_ie_len = ieee->wpa_ie_len;
+ int wps_ie_len = ieee->wps_ie_len;
+ unsigned int ckip_ie_len = 0;
+ unsigned int ccxrm_ie_len = 0;
+ unsigned int cxvernum_ie_len = 0;
+ struct lib80211_crypt_data *crypt;
+ int encrypt;
+ int PMKCacheIdx;
+
+ unsigned int rate_len = (beacon->rates_len ?
+ (beacon->rates_len + 2) : 0) +
+ (beacon->rates_ex_len ? (beacon->rates_ex_len) +
+ 2 : 0);
+
+ unsigned int wmm_info_len = beacon->qos_data.supported ? 9 : 0;
+ unsigned int turbo_info_len = beacon->Turbo_Enable ? 9 : 0;
+
+ int len = 0;
+
+ crypt = ieee->crypt_info.crypt[ieee->crypt_info.tx_keyidx];
+ if (crypt != NULL)
+ encrypt = ieee->host_encrypt && crypt && crypt->ops &&
+ ((0 == strcmp(crypt->ops->name, "R-WEP") ||
+ wpa_ie_len));
+ else
+ encrypt = 0;
+
+ if ((ieee->rtllib_ap_sec_type &&
+ (ieee->rtllib_ap_sec_type(ieee) & SEC_ALG_TKIP)) ||
+ ieee->bForcedBgMode) {
+ ieee->pHTInfo->bEnableHT = 0;
+ ieee->mode = WIRELESS_MODE_G;
+ }
+
+ if (ieee->pHTInfo->bCurrentHTSupport && ieee->pHTInfo->bEnableHT) {
+ ht_cap_buf = (u8 *)&(ieee->pHTInfo->SelfHTCap);
+ ht_cap_len = sizeof(ieee->pHTInfo->SelfHTCap);
+ HTConstructCapabilityElement(ieee, ht_cap_buf, &ht_cap_len,
+ encrypt, true);
+ if (ieee->pHTInfo->bCurrentRT2RTAggregation) {
+ realtek_ie_buf = ieee->pHTInfo->szRT2RTAggBuffer;
+ realtek_ie_len =
+ sizeof(ieee->pHTInfo->szRT2RTAggBuffer);
+ HTConstructRT2RTAggElement(ieee, realtek_ie_buf,
+ &realtek_ie_len);
+ }
+ }
+
+ if (beacon->bCkipSupported)
+ ckip_ie_len = 30+2;
+ if (beacon->bCcxRmEnable)
+ ccxrm_ie_len = 6+2;
+ if (beacon->BssCcxVerNumber >= 2)
+ cxvernum_ie_len = 5+2;
+
+ PMKCacheIdx = SecIsInPMKIDList(ieee, ieee->current_network.bssid);
+ if (PMKCacheIdx >= 0) {
+ wpa_ie_len += 18;
+ netdev_info(ieee->dev, "[PMK cache]: WPA2 IE length: %x\n",
+ wpa_ie_len);
+ }
+ len = sizeof(struct rtllib_assoc_request_frame) + 2
+ + beacon->ssid_len
+ + rate_len
+ + wpa_ie_len
+ + wps_ie_len
+ + wmm_info_len
+ + turbo_info_len
+ + ht_cap_len
+ + realtek_ie_len
+ + ckip_ie_len
+ + ccxrm_ie_len
+ + cxvernum_ie_len
+ + ieee->tx_headroom;
+
+ skb = dev_alloc_skb(len);
+
+ if (!skb)
+ return NULL;
+
+ skb_reserve(skb, ieee->tx_headroom);
+
+ hdr = (struct rtllib_assoc_request_frame *)
+ skb_put(skb, sizeof(struct rtllib_assoc_request_frame) + 2);
+
+
+ hdr->header.frame_ctl = RTLLIB_STYPE_ASSOC_REQ;
+ hdr->header.duration_id = cpu_to_le16(37);
+ memcpy(hdr->header.addr1, beacon->bssid, ETH_ALEN);
+ memcpy(hdr->header.addr2, ieee->dev->dev_addr, ETH_ALEN);
+ memcpy(hdr->header.addr3, beacon->bssid, ETH_ALEN);
+
+ memcpy(ieee->ap_mac_addr, beacon->bssid, ETH_ALEN);
+
+ hdr->capability = cpu_to_le16(WLAN_CAPABILITY_ESS);
+ if (beacon->capability & WLAN_CAPABILITY_PRIVACY)
+ hdr->capability |= cpu_to_le16(WLAN_CAPABILITY_PRIVACY);
+
+ if (beacon->capability & WLAN_CAPABILITY_SHORT_PREAMBLE)
+ hdr->capability |= cpu_to_le16(WLAN_CAPABILITY_SHORT_PREAMBLE);
+
+ if (ieee->short_slot &&
+ (beacon->capability&WLAN_CAPABILITY_SHORT_SLOT_TIME))
+ hdr->capability |= cpu_to_le16(WLAN_CAPABILITY_SHORT_SLOT_TIME);
+
+
+ hdr->listen_interval = cpu_to_le16(beacon->listen_interval);
+
+ hdr->info_element[0].id = MFIE_TYPE_SSID;
+
+ hdr->info_element[0].len = beacon->ssid_len;
+ tag = skb_put(skb, beacon->ssid_len);
+ memcpy(tag, beacon->ssid, beacon->ssid_len);
+
+ tag = skb_put(skb, rate_len);
+
+ if (beacon->rates_len) {
+ *tag++ = MFIE_TYPE_RATES;
+ *tag++ = beacon->rates_len;
+ for (i = 0; i < beacon->rates_len; i++)
+ *tag++ = beacon->rates[i];
+ }
+
+ if (beacon->rates_ex_len) {
+ *tag++ = MFIE_TYPE_RATES_EX;
+ *tag++ = beacon->rates_ex_len;
+ for (i = 0; i < beacon->rates_ex_len; i++)
+ *tag++ = beacon->rates_ex[i];
+ }
+
+ if (beacon->bCkipSupported) {
+ static const u8 AironetIeOui[] = {0x00, 0x01, 0x66};
+ u8 CcxAironetBuf[30];
+ struct octet_string osCcxAironetIE;
+
+ memset(CcxAironetBuf, 0, 30);
+ osCcxAironetIE.Octet = CcxAironetBuf;
+ osCcxAironetIE.Length = sizeof(CcxAironetBuf);
+ memcpy(osCcxAironetIE.Octet, AironetIeOui,
+ sizeof(AironetIeOui));
+
+ osCcxAironetIE.Octet[IE_CISCO_FLAG_POSITION] |=
+ (SUPPORT_CKIP_PK|SUPPORT_CKIP_MIC);
+ tag = skb_put(skb, ckip_ie_len);
+ *tag++ = MFIE_TYPE_AIRONET;
+ *tag++ = osCcxAironetIE.Length;
+ memcpy(tag, osCcxAironetIE.Octet, osCcxAironetIE.Length);
+ tag += osCcxAironetIE.Length;
+ }
+
+ if (beacon->bCcxRmEnable) {
+ static const u8 CcxRmCapBuf[] = {0x00, 0x40, 0x96, 0x01, 0x01,
+ 0x00};
+ struct octet_string osCcxRmCap;
+
+ osCcxRmCap.Octet = (u8 *) CcxRmCapBuf;
+ osCcxRmCap.Length = sizeof(CcxRmCapBuf);
+ tag = skb_put(skb, ccxrm_ie_len);
+ *tag++ = MFIE_TYPE_GENERIC;
+ *tag++ = osCcxRmCap.Length;
+ memcpy(tag, osCcxRmCap.Octet, osCcxRmCap.Length);
+ tag += osCcxRmCap.Length;
+ }
+
+ if (beacon->BssCcxVerNumber >= 2) {
+ u8 CcxVerNumBuf[] = {0x00, 0x40, 0x96, 0x03, 0x00};
+ struct octet_string osCcxVerNum;
+
+ CcxVerNumBuf[4] = beacon->BssCcxVerNumber;
+ osCcxVerNum.Octet = CcxVerNumBuf;
+ osCcxVerNum.Length = sizeof(CcxVerNumBuf);
+ tag = skb_put(skb, cxvernum_ie_len);
+ *tag++ = MFIE_TYPE_GENERIC;
+ *tag++ = osCcxVerNum.Length;
+ memcpy(tag, osCcxVerNum.Octet, osCcxVerNum.Length);
+ tag += osCcxVerNum.Length;
+ }
+ if (ieee->pHTInfo->bCurrentHTSupport && ieee->pHTInfo->bEnableHT) {
+ if (ieee->pHTInfo->ePeerHTSpecVer != HT_SPEC_VER_EWC) {
+ tag = skb_put(skb, ht_cap_len);
+ *tag++ = MFIE_TYPE_HT_CAP;
+ *tag++ = ht_cap_len - 2;
+ memcpy(tag, ht_cap_buf, ht_cap_len - 2);
+ tag += ht_cap_len - 2;
+ }
+ }
+
+ if (wpa_ie_len) {
+ tag = skb_put(skb, ieee->wpa_ie_len);
+ memcpy(tag, ieee->wpa_ie, ieee->wpa_ie_len);
+
+ if (PMKCacheIdx >= 0) {
+ tag = skb_put(skb, 18);
+ *tag = 1;
+ *(tag + 1) = 0;
+ memcpy((tag + 2), &ieee->PMKIDList[PMKCacheIdx].PMKID,
+ 16);
+ }
+ }
+ if (wmm_info_len) {
+ tag = skb_put(skb, wmm_info_len);
+ rtllib_WMM_Info(ieee, &tag);
+ }
+
+ if (wps_ie_len && ieee->wps_ie) {
+ tag = skb_put(skb, wps_ie_len);
+ memcpy(tag, ieee->wps_ie, wps_ie_len);
+ }
+
+ tag = skb_put(skb, turbo_info_len);
+ if (turbo_info_len)
+ rtllib_TURBO_Info(ieee, &tag);
+
+ if (ieee->pHTInfo->bCurrentHTSupport && ieee->pHTInfo->bEnableHT) {
+ if (ieee->pHTInfo->ePeerHTSpecVer == HT_SPEC_VER_EWC) {
+ tag = skb_put(skb, ht_cap_len);
+ *tag++ = MFIE_TYPE_GENERIC;
+ *tag++ = ht_cap_len - 2;
+ memcpy(tag, ht_cap_buf, ht_cap_len - 2);
+ tag += ht_cap_len - 2;
+ }
+
+ if (ieee->pHTInfo->bCurrentRT2RTAggregation) {
+ tag = skb_put(skb, realtek_ie_len);
+ *tag++ = MFIE_TYPE_GENERIC;
+ *tag++ = realtek_ie_len - 2;
+ memcpy(tag, realtek_ie_buf, realtek_ie_len - 2);
+ }
+ }
+
+ kfree(ieee->assocreq_ies);
+ ieee->assocreq_ies = NULL;
+ ies = &(hdr->info_element[0].id);
+ ieee->assocreq_ies_len = (skb->data + skb->len) - ies;
+ ieee->assocreq_ies = kmalloc(ieee->assocreq_ies_len, GFP_ATOMIC);
+ if (ieee->assocreq_ies)
+ memcpy(ieee->assocreq_ies, ies, ieee->assocreq_ies_len);
+ else {
+ netdev_info(ieee->dev,
+ "%s()Warning: can't alloc memory for assocreq_ies\n",
+ __func__);
+ ieee->assocreq_ies_len = 0;
+ }
+ return skb;
+}
+
+void rtllib_associate_abort(struct rtllib_device *ieee)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&ieee->lock, flags);
+
+ ieee->associate_seq++;
+
+ /* don't scan, and avoid to have the RX path possibily
+ * try again to associate. Even do not react to AUTH or
+ * ASSOC response. Just wait for the retry wq to be scheduled.
+ * Here we will check if there are good nets to associate
+ * with, so we retry or just get back to NO_LINK and scanning
+ */
+ if (ieee->state == RTLLIB_ASSOCIATING_AUTHENTICATING) {
+ RTLLIB_DEBUG_MGMT("Authentication failed\n");
+ ieee->softmac_stats.no_auth_rs++;
+ } else {
+ RTLLIB_DEBUG_MGMT("Association failed\n");
+ ieee->softmac_stats.no_ass_rs++;
+ }
+
+ ieee->state = RTLLIB_ASSOCIATING_RETRY;
+
+ queue_delayed_work_rsl(ieee->wq, &ieee->associate_retry_wq,
+ RTLLIB_SOFTMAC_ASSOC_RETRY_TIME);
+
+ spin_unlock_irqrestore(&ieee->lock, flags);
+}
+
+static void rtllib_associate_abort_cb(unsigned long dev)
+{
+ rtllib_associate_abort((struct rtllib_device *) dev);
+}
+
+static void rtllib_associate_step1(struct rtllib_device *ieee, u8 *daddr)
+{
+ struct rtllib_network *beacon = &ieee->current_network;
+ struct sk_buff *skb;
+
+ RTLLIB_DEBUG_MGMT("Stopping scan\n");
+
+ ieee->softmac_stats.tx_auth_rq++;
+
+ skb = rtllib_authentication_req(beacon, ieee, 0, daddr);
+
+ if (!skb)
+ rtllib_associate_abort(ieee);
+ else {
+ ieee->state = RTLLIB_ASSOCIATING_AUTHENTICATING;
+ RTLLIB_DEBUG_MGMT("Sending authentication request\n");
+ softmac_mgmt_xmit(skb, ieee);
+ if (!timer_pending(&ieee->associate_timer)) {
+ ieee->associate_timer.expires = jiffies + (HZ / 2);
+ add_timer(&ieee->associate_timer);
+ }
+ }
+}
+
+static void rtllib_auth_challenge(struct rtllib_device *ieee, u8 *challenge, int chlen)
+{
+ u8 *c;
+ struct sk_buff *skb;
+ struct rtllib_network *beacon = &ieee->current_network;
+
+ ieee->associate_seq++;
+ ieee->softmac_stats.tx_auth_rq++;
+
+ skb = rtllib_authentication_req(beacon, ieee, chlen + 2, beacon->bssid);
+
+ if (!skb)
+ rtllib_associate_abort(ieee);
+ else {
+ c = skb_put(skb, chlen+2);
+ *(c++) = MFIE_TYPE_CHALLENGE;
+ *(c++) = chlen;
+ memcpy(c, challenge, chlen);
+
+ RTLLIB_DEBUG_MGMT("Sending authentication challenge response\n");
+
+ rtllib_encrypt_fragment(ieee, skb,
+ sizeof(struct rtllib_hdr_3addr));
+
+ softmac_mgmt_xmit(skb, ieee);
+ mod_timer(&ieee->associate_timer, jiffies + (HZ/2));
+ }
+ kfree(challenge);
+}
+
+static void rtllib_associate_step2(struct rtllib_device *ieee)
+{
+ struct sk_buff *skb;
+ struct rtllib_network *beacon = &ieee->current_network;
+
+ del_timer_sync(&ieee->associate_timer);
+
+ RTLLIB_DEBUG_MGMT("Sending association request\n");
+
+ ieee->softmac_stats.tx_ass_rq++;
+ skb = rtllib_association_req(beacon, ieee);
+ if (!skb)
+ rtllib_associate_abort(ieee);
+ else {
+ softmac_mgmt_xmit(skb, ieee);
+ mod_timer(&ieee->associate_timer, jiffies + (HZ/2));
+ }
+}
+
+#define CANCELLED 2
+static void rtllib_associate_complete_wq(void *data)
+{
+ struct rtllib_device *ieee = (struct rtllib_device *)
+ container_of_work_rsl(data,
+ struct rtllib_device,
+ associate_complete_wq);
+ struct rt_pwr_save_ctrl *pPSC = (struct rt_pwr_save_ctrl *)
+ (&(ieee->PowerSaveControl));
+ netdev_info(ieee->dev, "Associated successfully\n");
+ if (!ieee->is_silent_reset) {
+ netdev_info(ieee->dev, "normal associate\n");
+ notify_wx_assoc_event(ieee);
+ }
+
+ netif_carrier_on(ieee->dev);
+ ieee->is_roaming = false;
+ if (rtllib_is_54g(&ieee->current_network) &&
+ (ieee->modulation & RTLLIB_OFDM_MODULATION)) {
+ ieee->rate = 108;
+ netdev_info(ieee->dev, "Using G rates:%d\n", ieee->rate);
+ } else {
+ ieee->rate = 22;
+ ieee->SetWirelessMode(ieee->dev, IEEE_B);
+ netdev_info(ieee->dev, "Using B rates:%d\n", ieee->rate);
+ }
+ if (ieee->pHTInfo->bCurrentHTSupport && ieee->pHTInfo->bEnableHT) {
+ netdev_info(ieee->dev, "Successfully associated, ht enabled\n");
+ HTOnAssocRsp(ieee);
+ } else {
+ netdev_info(ieee->dev,
+ "Successfully associated, ht not enabled(%d, %d)\n",
+ ieee->pHTInfo->bCurrentHTSupport,
+ ieee->pHTInfo->bEnableHT);
+ memset(ieee->dot11HTOperationalRateSet, 0, 16);
+ }
+ ieee->LinkDetectInfo.SlotNum = 2 * (1 +
+ ieee->current_network.beacon_interval /
+ 500);
+ if (ieee->LinkDetectInfo.NumRecvBcnInPeriod == 0 ||
+ ieee->LinkDetectInfo.NumRecvDataInPeriod == 0) {
+ ieee->LinkDetectInfo.NumRecvBcnInPeriod = 1;
+ ieee->LinkDetectInfo.NumRecvDataInPeriod = 1;
+ }
+ pPSC->LpsIdleCount = 0;
+ ieee->link_change(ieee->dev);
+
+ if (ieee->is_silent_reset) {
+ netdev_info(ieee->dev, "silent reset associate\n");
+ ieee->is_silent_reset = false;
+ }
+
+ if (ieee->data_hard_resume)
+ ieee->data_hard_resume(ieee->dev);
+
+}
+
+static void rtllib_sta_send_associnfo(struct rtllib_device *ieee)
+{
+}
+
+static void rtllib_associate_complete(struct rtllib_device *ieee)
+{
+ del_timer_sync(&ieee->associate_timer);
+
+ ieee->state = RTLLIB_LINKED;
+ rtllib_sta_send_associnfo(ieee);
+
+ queue_work_rsl(ieee->wq, &ieee->associate_complete_wq);
+}
+
+static void rtllib_associate_procedure_wq(void *data)
+{
+ struct rtllib_device *ieee = container_of_dwork_rsl(data,
+ struct rtllib_device,
+ associate_procedure_wq);
+ rtllib_stop_scan_syncro(ieee);
+ if (ieee->rtllib_ips_leave != NULL)
+ ieee->rtllib_ips_leave(ieee->dev);
+ down(&ieee->wx_sem);
+
+ if (ieee->data_hard_stop)
+ ieee->data_hard_stop(ieee->dev);
+
+ rtllib_stop_scan(ieee);
+ RT_TRACE(COMP_DBG, "===>%s(), chan:%d\n", __func__,
+ ieee->current_network.channel);
+ HTSetConnectBwMode(ieee, HT_CHANNEL_WIDTH_20, HT_EXTCHNL_OFFSET_NO_EXT);
+ if (ieee->eRFPowerState == eRfOff) {
+ RT_TRACE(COMP_DBG,
+ "=============>%s():Rf state is eRfOff, schedule ipsleave wq again,return\n",
+ __func__);
+ if (ieee->rtllib_ips_leave_wq != NULL)
+ ieee->rtllib_ips_leave_wq(ieee->dev);
+ up(&ieee->wx_sem);
+ return;
+ }
+ ieee->associate_seq = 1;
+
+ rtllib_associate_step1(ieee, ieee->current_network.bssid);
+
+ up(&ieee->wx_sem);
+}
+
+inline void rtllib_softmac_new_net(struct rtllib_device *ieee,
+ struct rtllib_network *net)
+{
+ u8 tmp_ssid[IW_ESSID_MAX_SIZE + 1];
+ int tmp_ssid_len = 0;
+
+ short apset, ssidset, ssidbroad, apmatch, ssidmatch;
+
+ /* we are interested in new new only if we are not associated
+ * and we are not associating / authenticating
+ */
+ if (ieee->state != RTLLIB_NOLINK)
+ return;
+
+ if ((ieee->iw_mode == IW_MODE_INFRA) && !(net->capability &
+ WLAN_CAPABILITY_ESS))
+ return;
+
+ if ((ieee->iw_mode == IW_MODE_ADHOC) && !(net->capability &
+ WLAN_CAPABILITY_IBSS))
+ return;
+
+ if ((ieee->iw_mode == IW_MODE_ADHOC) &&
+ (net->channel > ieee->ibss_maxjoin_chal))
+ return;
+ if (ieee->iw_mode == IW_MODE_INFRA || ieee->iw_mode == IW_MODE_ADHOC) {
+ /* if the user specified the AP MAC, we need also the essid
+ * This could be obtained by beacons or, if the network does not
+ * broadcast it, it can be put manually.
+ */
+ apset = ieee->wap_set;
+ ssidset = ieee->ssid_set;
+ ssidbroad = !(net->ssid_len == 0 || net->ssid[0] == '\0');
+ apmatch = (memcmp(ieee->current_network.bssid, net->bssid,
+ ETH_ALEN) == 0);
+ if (!ssidbroad) {
+ ssidmatch = (ieee->current_network.ssid_len ==
+ net->hidden_ssid_len) &&
+ (!strncmp(ieee->current_network.ssid,
+ net->hidden_ssid, net->hidden_ssid_len));
+ if (net->hidden_ssid_len > 0) {
+ strncpy(net->ssid, net->hidden_ssid,
+ net->hidden_ssid_len);
+ net->ssid_len = net->hidden_ssid_len;
+ ssidbroad = 1;
+ }
+ } else
+ ssidmatch =
+ (ieee->current_network.ssid_len == net->ssid_len) &&
+ (!strncmp(ieee->current_network.ssid, net->ssid,
+ net->ssid_len));
+
+ /* if the user set the AP check if match.
+ * if the network does not broadcast essid we check the
+ * user supplied ANY essid
+ * if the network does broadcast and the user does not set
+ * essid it is OK
+ * if the network does broadcast and the user did set essid
+ * check if essid match
+ * if the ap is not set, check that the user set the bssid
+ * and the network does broadcast and that those two bssid match
+ */
+ if ((apset && apmatch &&
+ ((ssidset && ssidbroad && ssidmatch) ||
+ (ssidbroad && !ssidset) || (!ssidbroad && ssidset))) ||
+ (!apset && ssidset && ssidbroad && ssidmatch) ||
+ (ieee->is_roaming && ssidset && ssidbroad && ssidmatch)) {
+ /* if the essid is hidden replace it with the
+ * essid provided by the user.
+ */
+ if (!ssidbroad) {
+ strncpy(tmp_ssid, ieee->current_network.ssid,
+ IW_ESSID_MAX_SIZE);
+ tmp_ssid_len = ieee->current_network.ssid_len;
+ }
+ memcpy(&ieee->current_network, net,
+ sizeof(struct rtllib_network));
+ if (!ssidbroad) {
+ strncpy(ieee->current_network.ssid, tmp_ssid,
+ IW_ESSID_MAX_SIZE);
+ ieee->current_network.ssid_len = tmp_ssid_len;
+ }
+ netdev_info(ieee->dev,
+ "Linking with %s,channel:%d, qos:%d, myHT:%d, networkHT:%d, mode:%x cur_net.flags:0x%x\n",
+ ieee->current_network.ssid,
+ ieee->current_network.channel,
+ ieee->current_network.qos_data.supported,
+ ieee->pHTInfo->bEnableHT,
+ ieee->current_network.bssht.bdSupportHT,
+ ieee->current_network.mode,
+ ieee->current_network.flags);
+
+ if ((rtllib_act_scanning(ieee, false)) &&
+ !(ieee->softmac_features & IEEE_SOFTMAC_SCAN))
+ rtllib_stop_scan_syncro(ieee);
+
+ ieee->hwscan_ch_bk = ieee->current_network.channel;
+ HTResetIOTSetting(ieee->pHTInfo);
+ ieee->wmm_acm = 0;
+ if (ieee->iw_mode == IW_MODE_INFRA) {
+ /* Join the network for the first time */
+ ieee->AsocRetryCount = 0;
+ if ((ieee->current_network.qos_data.supported == 1) &&
+ ieee->current_network.bssht.bdSupportHT)
+ HTResetSelfAndSavePeerSetting(ieee,
+ &(ieee->current_network));
+ else
+ ieee->pHTInfo->bCurrentHTSupport =
+ false;
+
+ ieee->state = RTLLIB_ASSOCIATING;
+ if (ieee->LedControlHandler != NULL)
+ ieee->LedControlHandler(ieee->dev,
+ LED_CTL_START_TO_LINK);
+ queue_delayed_work_rsl(ieee->wq,
+ &ieee->associate_procedure_wq, 0);
+ } else {
+ if (rtllib_is_54g(&ieee->current_network) &&
+ (ieee->modulation & RTLLIB_OFDM_MODULATION)) {
+ ieee->rate = 108;
+ ieee->SetWirelessMode(ieee->dev, IEEE_G);
+ netdev_info(ieee->dev, "Using G rates\n");
+ } else {
+ ieee->rate = 22;
+ ieee->SetWirelessMode(ieee->dev, IEEE_B);
+ netdev_info(ieee->dev, "Using B rates\n");
+ }
+ memset(ieee->dot11HTOperationalRateSet, 0, 16);
+ ieee->state = RTLLIB_LINKED;
+ }
+ }
+ }
+}
+
+void rtllib_softmac_check_all_nets(struct rtllib_device *ieee)
+{
+ unsigned long flags;
+ struct rtllib_network *target;
+
+ spin_lock_irqsave(&ieee->lock, flags);
+
+ list_for_each_entry(target, &ieee->network_list, list) {
+
+ /* if the state become different that NOLINK means
+ * we had found what we are searching for
+ */
+
+ if (ieee->state != RTLLIB_NOLINK)
+ break;
+
+ if (ieee->scan_age == 0 || time_after(target->last_scanned +
+ ieee->scan_age, jiffies))
+ rtllib_softmac_new_net(ieee, target);
+ }
+ spin_unlock_irqrestore(&ieee->lock, flags);
+}
+
+static inline u16 auth_parse(struct sk_buff *skb, u8 **challenge, int *chlen)
+{
+ struct rtllib_authentication *a;
+ u8 *t;
+
+ if (skb->len < (sizeof(struct rtllib_authentication) -
+ sizeof(struct rtllib_info_element))) {
+ RTLLIB_DEBUG_MGMT("invalid len in auth resp: %d\n", skb->len);
+ return 0xcafe;
+ }
+ *challenge = NULL;
+ a = (struct rtllib_authentication *) skb->data;
+ if (skb->len > (sizeof(struct rtllib_authentication) + 3)) {
+ t = skb->data + sizeof(struct rtllib_authentication);
+
+ if (*(t++) == MFIE_TYPE_CHALLENGE) {
+ *chlen = *(t++);
+ *challenge = kmemdup(t, *chlen, GFP_ATOMIC);
+ if (!*challenge)
+ return -ENOMEM;
+ }
+ }
+ return cpu_to_le16(a->status);
+}
+
+static int auth_rq_parse(struct sk_buff *skb, u8 *dest)
+{
+ struct rtllib_authentication *a;
+
+ if (skb->len < (sizeof(struct rtllib_authentication) -
+ sizeof(struct rtllib_info_element))) {
+ RTLLIB_DEBUG_MGMT("invalid len in auth request: %d\n",
+ skb->len);
+ return -1;
+ }
+ a = (struct rtllib_authentication *) skb->data;
+
+ memcpy(dest, a->header.addr2, ETH_ALEN);
+
+ if (le16_to_cpu(a->algorithm) != WLAN_AUTH_OPEN)
+ return WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG;
+
+ return WLAN_STATUS_SUCCESS;
+}
+
+static short probe_rq_parse(struct rtllib_device *ieee, struct sk_buff *skb,
+ u8 *src)
+{
+ u8 *tag;
+ u8 *skbend;
+ u8 *ssid = NULL;
+ u8 ssidlen = 0;
+ struct rtllib_hdr_3addr *header =
+ (struct rtllib_hdr_3addr *) skb->data;
+ bool bssid_match;
+
+ if (skb->len < sizeof(struct rtllib_hdr_3addr))
+ return -1; /* corrupted */
+
+ bssid_match =
+ (memcmp(header->addr3, ieee->current_network.bssid, ETH_ALEN) != 0) &&
+ (!is_broadcast_ether_addr(header->addr3));
+ if (bssid_match)
+ return -1;
+
+ memcpy(src, header->addr2, ETH_ALEN);
+
+ skbend = (u8 *)skb->data + skb->len;
+
+ tag = skb->data + sizeof(struct rtllib_hdr_3addr);
+
+ while (tag + 1 < skbend) {
+ if (*tag == 0) {
+ ssid = tag + 2;
+ ssidlen = *(tag + 1);
+ break;
+ }
+ tag++; /* point to the len field */
+ tag = tag + *(tag); /* point to the last data byte of the tag */
+ tag++; /* point to the next tag */
+ }
+
+ if (ssidlen == 0)
+ return 1;
+
+ if (!ssid)
+ return 1; /* ssid not found in tagged param */
+
+ return !strncmp(ssid, ieee->current_network.ssid, ssidlen);
+}
+
+static int assoc_rq_parse(struct sk_buff *skb, u8 *dest)
+{
+ struct rtllib_assoc_request_frame *a;
+
+ if (skb->len < (sizeof(struct rtllib_assoc_request_frame) -
+ sizeof(struct rtllib_info_element))) {
+
+ RTLLIB_DEBUG_MGMT("invalid len in auth request:%d\n", skb->len);
+ return -1;
+ }
+
+ a = (struct rtllib_assoc_request_frame *) skb->data;
+
+ memcpy(dest, a->header.addr2, ETH_ALEN);
+
+ return 0;
+}
+
+static inline u16 assoc_parse(struct rtllib_device *ieee, struct sk_buff *skb,
+ int *aid)
+{
+ struct rtllib_assoc_response_frame *response_head;
+ u16 status_code;
+
+ if (skb->len < sizeof(struct rtllib_assoc_response_frame)) {
+ RTLLIB_DEBUG_MGMT("invalid len in auth resp: %d\n", skb->len);
+ return 0xcafe;
+ }
+
+ response_head = (struct rtllib_assoc_response_frame *) skb->data;
+ *aid = le16_to_cpu(response_head->aid) & 0x3fff;
+
+ status_code = le16_to_cpu(response_head->status);
+ if ((status_code == WLAN_STATUS_ASSOC_DENIED_RATES ||
+ status_code == WLAN_STATUS_CAPS_UNSUPPORTED) &&
+ ((ieee->mode == IEEE_G) &&
+ (ieee->current_network.mode == IEEE_N_24G) &&
+ (ieee->AsocRetryCount++ < (RT_ASOC_RETRY_LIMIT-1)))) {
+ ieee->pHTInfo->IOTAction |= HT_IOT_ACT_PURE_N_MODE;
+ } else {
+ ieee->AsocRetryCount = 0;
+ }
+
+ return le16_to_cpu(response_head->status);
+}
+
+void rtllib_rx_probe_rq(struct rtllib_device *ieee, struct sk_buff *skb)
+{
+ u8 dest[ETH_ALEN];
+
+ ieee->softmac_stats.rx_probe_rq++;
+ if (probe_rq_parse(ieee, skb, dest) > 0) {
+ ieee->softmac_stats.tx_probe_rs++;
+ rtllib_resp_to_probe(ieee, dest);
+ }
+}
+
+static inline void rtllib_rx_auth_rq(struct rtllib_device *ieee,
+ struct sk_buff *skb)
+{
+ u8 dest[ETH_ALEN];
+ int status;
+
+ ieee->softmac_stats.rx_auth_rq++;
+
+ status = auth_rq_parse(skb, dest);
+ if (status != -1)
+ rtllib_resp_to_auth(ieee, status, dest);
+}
+
+static inline void rtllib_rx_assoc_rq(struct rtllib_device *ieee,
+ struct sk_buff *skb)
+{
+
+ u8 dest[ETH_ALEN];
+
+ ieee->softmac_stats.rx_ass_rq++;
+ if (assoc_rq_parse(skb, dest) != -1)
+ rtllib_resp_to_assoc_rq(ieee, dest);
+
+ netdev_info(ieee->dev, "New client associated: %pM\n", dest);
+}
+
+void rtllib_sta_ps_send_null_frame(struct rtllib_device *ieee, short pwr)
+{
+
+ struct sk_buff *buf = rtllib_null_func(ieee, pwr);
+
+ if (buf)
+ softmac_ps_mgmt_xmit(buf, ieee);
+}
+EXPORT_SYMBOL(rtllib_sta_ps_send_null_frame);
+
+void rtllib_sta_ps_send_pspoll_frame(struct rtllib_device *ieee)
+{
+ struct sk_buff *buf = rtllib_pspoll_func(ieee);
+
+ if (buf)
+ softmac_ps_mgmt_xmit(buf, ieee);
+}
+
+static short rtllib_sta_ps_sleep(struct rtllib_device *ieee, u64 *time)
+{
+ int timeout = ieee->ps_timeout;
+ u8 dtim;
+ struct rt_pwr_save_ctrl *pPSC = (struct rt_pwr_save_ctrl *)
+ (&(ieee->PowerSaveControl));
+
+ if (ieee->LPSDelayCnt) {
+ ieee->LPSDelayCnt--;
+ return 0;
+ }
+
+ dtim = ieee->current_network.dtim_data;
+ if (!(dtim & RTLLIB_DTIM_VALID))
+ return 0;
+ timeout = ieee->current_network.beacon_interval;
+ ieee->current_network.dtim_data = RTLLIB_DTIM_INVALID;
+ /* there's no need to nofity AP that I find you buffered
+ * with broadcast packet
+ */
+ if (dtim & (RTLLIB_DTIM_UCAST & ieee->ps))
+ return 2;
+
+ if (!time_after(jiffies,
+ ieee->dev->trans_start + msecs_to_jiffies(timeout)))
+ return 0;
+ if (!time_after(jiffies,
+ ieee->last_rx_ps_time + msecs_to_jiffies(timeout)))
+ return 0;
+ if ((ieee->softmac_features & IEEE_SOFTMAC_SINGLE_QUEUE) &&
+ (ieee->mgmt_queue_tail != ieee->mgmt_queue_head))
+ return 0;
+
+ if (time) {
+ if (ieee->bAwakePktSent) {
+ pPSC->LPSAwakeIntvl = 1;
+ } else {
+ u8 MaxPeriod = 1;
+
+ if (pPSC->LPSAwakeIntvl == 0)
+ pPSC->LPSAwakeIntvl = 1;
+ if (pPSC->RegMaxLPSAwakeIntvl == 0)
+ MaxPeriod = 1;
+ else if (pPSC->RegMaxLPSAwakeIntvl == 0xFF)
+ MaxPeriod = ieee->current_network.dtim_period;
+ else
+ MaxPeriod = pPSC->RegMaxLPSAwakeIntvl;
+ pPSC->LPSAwakeIntvl = (pPSC->LPSAwakeIntvl >=
+ MaxPeriod) ? MaxPeriod :
+ (pPSC->LPSAwakeIntvl + 1);
+ }
+ {
+ u8 LPSAwakeIntvl_tmp = 0;
+ u8 period = ieee->current_network.dtim_period;
+ u8 count = ieee->current_network.tim.tim_count;
+
+ if (count == 0) {
+ if (pPSC->LPSAwakeIntvl > period)
+ LPSAwakeIntvl_tmp = period +
+ (pPSC->LPSAwakeIntvl -
+ period) -
+ ((pPSC->LPSAwakeIntvl-period) %
+ period);
+ else
+ LPSAwakeIntvl_tmp = pPSC->LPSAwakeIntvl;
+
+ } else {
+ if (pPSC->LPSAwakeIntvl >
+ ieee->current_network.tim.tim_count)
+ LPSAwakeIntvl_tmp = count +
+ (pPSC->LPSAwakeIntvl - count) -
+ ((pPSC->LPSAwakeIntvl-count)%period);
+ else
+ LPSAwakeIntvl_tmp = pPSC->LPSAwakeIntvl;
+ }
+
+ *time = ieee->current_network.last_dtim_sta_time
+ + msecs_to_jiffies(ieee->current_network.beacon_interval *
+ LPSAwakeIntvl_tmp);
+ }
+ }
+
+ return 1;
+
+
+}
+
+static inline void rtllib_sta_ps(struct rtllib_device *ieee)
+{
+ u64 time;
+ short sleep;
+ unsigned long flags, flags2;
+
+ spin_lock_irqsave(&ieee->lock, flags);
+
+ if ((ieee->ps == RTLLIB_PS_DISABLED ||
+ ieee->iw_mode != IW_MODE_INFRA ||
+ ieee->state != RTLLIB_LINKED)) {
+ RT_TRACE(COMP_DBG,
+ "=====>%s(): no need to ps,wake up!! ieee->ps is %d, ieee->iw_mode is %d, ieee->state is %d\n",
+ __func__, ieee->ps, ieee->iw_mode, ieee->state);
+ spin_lock_irqsave(&ieee->mgmt_tx_lock, flags2);
+ rtllib_sta_wakeup(ieee, 1);
+
+ spin_unlock_irqrestore(&ieee->mgmt_tx_lock, flags2);
+ }
+ sleep = rtllib_sta_ps_sleep(ieee, &time);
+ /* 2 wake, 1 sleep, 0 do nothing */
+ if (sleep == 0)
+ goto out;
+ if (sleep == 1) {
+ if (ieee->sta_sleep == LPS_IS_SLEEP) {
+ ieee->enter_sleep_state(ieee->dev, time);
+ } else if (ieee->sta_sleep == LPS_IS_WAKE) {
+ spin_lock_irqsave(&ieee->mgmt_tx_lock, flags2);
+
+ if (ieee->ps_is_queue_empty(ieee->dev)) {
+ ieee->sta_sleep = LPS_WAIT_NULL_DATA_SEND;
+ ieee->ack_tx_to_ieee = 1;
+ rtllib_sta_ps_send_null_frame(ieee, 1);
+ ieee->ps_time = time;
+ }
+ spin_unlock_irqrestore(&ieee->mgmt_tx_lock, flags2);
+
+ }
+
+ ieee->bAwakePktSent = false;
+
+ } else if (sleep == 2) {
+ spin_lock_irqsave(&ieee->mgmt_tx_lock, flags2);
+
+ rtllib_sta_wakeup(ieee, 1);
+
+ spin_unlock_irqrestore(&ieee->mgmt_tx_lock, flags2);
+ }
+
+out:
+ spin_unlock_irqrestore(&ieee->lock, flags);
+
+}
+
+void rtllib_sta_wakeup(struct rtllib_device *ieee, short nl)
+{
+ if (ieee->sta_sleep == LPS_IS_WAKE) {
+ if (nl) {
+ if (ieee->pHTInfo->IOTAction &
+ HT_IOT_ACT_NULL_DATA_POWER_SAVING) {
+ ieee->ack_tx_to_ieee = 1;
+ rtllib_sta_ps_send_null_frame(ieee, 0);
+ } else {
+ ieee->ack_tx_to_ieee = 1;
+ rtllib_sta_ps_send_pspoll_frame(ieee);
+ }
+ }
+ return;
+
+ }
+
+ if (ieee->sta_sleep == LPS_IS_SLEEP)
+ ieee->sta_wake_up(ieee->dev);
+ if (nl) {
+ if (ieee->pHTInfo->IOTAction &
+ HT_IOT_ACT_NULL_DATA_POWER_SAVING) {
+ ieee->ack_tx_to_ieee = 1;
+ rtllib_sta_ps_send_null_frame(ieee, 0);
+ } else {
+ ieee->ack_tx_to_ieee = 1;
+ ieee->polling = true;
+ rtllib_sta_ps_send_pspoll_frame(ieee);
+ }
+
+ } else {
+ ieee->sta_sleep = LPS_IS_WAKE;
+ ieee->polling = false;
+ }
+}
+
+void rtllib_ps_tx_ack(struct rtllib_device *ieee, short success)
+{
+ unsigned long flags, flags2;
+
+ spin_lock_irqsave(&ieee->lock, flags);
+
+ if (ieee->sta_sleep == LPS_WAIT_NULL_DATA_SEND) {
+ /* Null frame with PS bit set */
+ if (success) {
+ ieee->sta_sleep = LPS_IS_SLEEP;
+ ieee->enter_sleep_state(ieee->dev, ieee->ps_time);
+ }
+ /* if the card report not success we can't be sure the AP
+ * has not RXed so we can't assume the AP believe us awake
+ */
+ } else {/* 21112005 - tx again null without PS bit if lost */
+
+ if ((ieee->sta_sleep == LPS_IS_WAKE) && !success) {
+ spin_lock_irqsave(&ieee->mgmt_tx_lock, flags2);
+ if (ieee->pHTInfo->IOTAction &
+ HT_IOT_ACT_NULL_DATA_POWER_SAVING)
+ rtllib_sta_ps_send_null_frame(ieee, 0);
+ else
+ rtllib_sta_ps_send_pspoll_frame(ieee);
+ spin_unlock_irqrestore(&ieee->mgmt_tx_lock, flags2);
+ }
+ }
+ spin_unlock_irqrestore(&ieee->lock, flags);
+}
+EXPORT_SYMBOL(rtllib_ps_tx_ack);
+
+static void rtllib_process_action(struct rtllib_device *ieee, struct sk_buff *skb)
+{
+ struct rtllib_hdr_3addr *header = (struct rtllib_hdr_3addr *) skb->data;
+ u8 *act = rtllib_get_payload((struct rtllib_hdr *)header);
+ u8 category = 0;
+
+ if (act == NULL) {
+ RTLLIB_DEBUG(RTLLIB_DL_ERR,
+ "error to get payload of action frame\n");
+ return;
+ }
+
+ category = *act;
+ act++;
+ switch (category) {
+ case ACT_CAT_BA:
+ switch (*act) {
+ case ACT_ADDBAREQ:
+ rtllib_rx_ADDBAReq(ieee, skb);
+ break;
+ case ACT_ADDBARSP:
+ rtllib_rx_ADDBARsp(ieee, skb);
+ break;
+ case ACT_DELBA:
+ rtllib_rx_DELBA(ieee, skb);
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+inline int rtllib_rx_assoc_resp(struct rtllib_device *ieee, struct sk_buff *skb,
+ struct rtllib_rx_stats *rx_stats)
+{
+ u16 errcode;
+ int aid;
+ u8 *ies;
+ struct rtllib_assoc_response_frame *assoc_resp;
+ struct rtllib_hdr_3addr *header = (struct rtllib_hdr_3addr *) skb->data;
+
+ RTLLIB_DEBUG_MGMT("received [RE]ASSOCIATION RESPONSE (%d)\n",
+ WLAN_FC_GET_STYPE(header->frame_ctl));
+
+ if ((ieee->softmac_features & IEEE_SOFTMAC_ASSOCIATE) &&
+ ieee->state == RTLLIB_ASSOCIATING_AUTHENTICATED &&
+ (ieee->iw_mode == IW_MODE_INFRA)) {
+ errcode = assoc_parse(ieee, skb, &aid);
+ if (0 == errcode) {
+ struct rtllib_network *network =
+ kzalloc(sizeof(struct rtllib_network),
+ GFP_ATOMIC);
+
+ if (!network)
+ return 1;
+ ieee->state = RTLLIB_LINKED;
+ ieee->assoc_id = aid;
+ ieee->softmac_stats.rx_ass_ok++;
+ /* station support qos */
+ /* Let the register setting default with Legacy station */
+ assoc_resp = (struct rtllib_assoc_response_frame *)skb->data;
+ if (ieee->current_network.qos_data.supported == 1) {
+ if (rtllib_parse_info_param(ieee, assoc_resp->info_element,
+ rx_stats->len - sizeof(*assoc_resp),
+ network, rx_stats)) {
+ kfree(network);
+ return 1;
+ }
+ memcpy(ieee->pHTInfo->PeerHTCapBuf,
+ network->bssht.bdHTCapBuf,
+ network->bssht.bdHTCapLen);
+ memcpy(ieee->pHTInfo->PeerHTInfoBuf,
+ network->bssht.bdHTInfoBuf,
+ network->bssht.bdHTInfoLen);
+ if (ieee->handle_assoc_response != NULL)
+ ieee->handle_assoc_response(ieee->dev,
+ (struct rtllib_assoc_response_frame *)header,
+ network);
+ }
+ kfree(network);
+
+ kfree(ieee->assocresp_ies);
+ ieee->assocresp_ies = NULL;
+ ies = &(assoc_resp->info_element[0].id);
+ ieee->assocresp_ies_len = (skb->data + skb->len) - ies;
+ ieee->assocresp_ies = kmalloc(ieee->assocresp_ies_len,
+ GFP_ATOMIC);
+ if (ieee->assocresp_ies)
+ memcpy(ieee->assocresp_ies, ies,
+ ieee->assocresp_ies_len);
+ else {
+ netdev_info(ieee->dev,
+ "%s()Warning: can't alloc memory for assocresp_ies\n",
+ __func__);
+ ieee->assocresp_ies_len = 0;
+ }
+ rtllib_associate_complete(ieee);
+ } else {
+ /* aid could not been allocated */
+ ieee->softmac_stats.rx_ass_err++;
+ netdev_info(ieee->dev,
+ "Association response status code 0x%x\n",
+ errcode);
+ RTLLIB_DEBUG_MGMT(
+ "Association response status code 0x%x\n",
+ errcode);
+ if (ieee->AsocRetryCount < RT_ASOC_RETRY_LIMIT)
+ queue_delayed_work_rsl(ieee->wq,
+ &ieee->associate_procedure_wq, 0);
+ else
+ rtllib_associate_abort(ieee);
+ }
+ }
+ return 0;
+}
+
+static void rtllib_rx_auth_resp(struct rtllib_device *ieee, struct sk_buff *skb)
+{
+ u16 errcode;
+ u8 *challenge;
+ int chlen = 0;
+ bool bSupportNmode = true, bHalfSupportNmode = false;
+
+ errcode = auth_parse(skb, &challenge, &chlen);
+
+ if (errcode) {
+ ieee->softmac_stats.rx_auth_rs_err++;
+ RTLLIB_DEBUG_MGMT("Authentication respose status code 0x%x",
+ errcode);
+
+ netdev_info(ieee->dev,
+ "Authentication respose status code 0x%x", errcode);
+ rtllib_associate_abort(ieee);
+ return;
+ }
+
+ if (ieee->open_wep || !challenge) {
+ ieee->state = RTLLIB_ASSOCIATING_AUTHENTICATED;
+ ieee->softmac_stats.rx_auth_rs_ok++;
+ if (!(ieee->pHTInfo->IOTAction & HT_IOT_ACT_PURE_N_MODE)) {
+ if (!ieee->GetNmodeSupportBySecCfg(ieee->dev)) {
+ if (IsHTHalfNmodeAPs(ieee)) {
+ bSupportNmode = true;
+ bHalfSupportNmode = true;
+ } else {
+ bSupportNmode = false;
+ bHalfSupportNmode = false;
+ }
+ }
+ }
+ /* Dummy wirless mode setting to avoid encryption issue */
+ if (bSupportNmode) {
+ ieee->SetWirelessMode(ieee->dev,
+ ieee->current_network.mode);
+ } else {
+ /*TODO*/
+ ieee->SetWirelessMode(ieee->dev, IEEE_G);
+ }
+
+ if ((ieee->current_network.mode == IEEE_N_24G) &&
+ bHalfSupportNmode) {
+ netdev_info(ieee->dev, "======>enter half N mode\n");
+ ieee->bHalfWirelessN24GMode = true;
+ } else {
+ ieee->bHalfWirelessN24GMode = false;
+ }
+ rtllib_associate_step2(ieee);
+ } else {
+ rtllib_auth_challenge(ieee, challenge, chlen);
+ }
+}
+
+inline int rtllib_rx_auth(struct rtllib_device *ieee, struct sk_buff *skb,
+ struct rtllib_rx_stats *rx_stats)
+{
+
+ if (ieee->softmac_features & IEEE_SOFTMAC_ASSOCIATE) {
+ if (ieee->state == RTLLIB_ASSOCIATING_AUTHENTICATING &&
+ (ieee->iw_mode == IW_MODE_INFRA)) {
+ RTLLIB_DEBUG_MGMT("Received authentication response");
+ rtllib_rx_auth_resp(ieee, skb);
+ } else if (ieee->iw_mode == IW_MODE_MASTER) {
+ rtllib_rx_auth_rq(ieee, skb);
+ }
+ }
+ return 0;
+}
+
+inline int rtllib_rx_deauth(struct rtllib_device *ieee, struct sk_buff *skb)
+{
+ struct rtllib_hdr_3addr *header = (struct rtllib_hdr_3addr *) skb->data;
+
+ if (memcmp(header->addr3, ieee->current_network.bssid, ETH_ALEN) != 0)
+ return 0;
+
+ /* FIXME for now repeat all the association procedure
+ * both for disassociation and deauthentication
+ */
+ if ((ieee->softmac_features & IEEE_SOFTMAC_ASSOCIATE) &&
+ ieee->state == RTLLIB_LINKED &&
+ (ieee->iw_mode == IW_MODE_INFRA)) {
+ netdev_info(ieee->dev,
+ "==========>received disassoc/deauth(%x) frame, reason code:%x\n",
+ WLAN_FC_GET_STYPE(header->frame_ctl),
+ ((struct rtllib_disassoc *)skb->data)->reason);
+ ieee->state = RTLLIB_ASSOCIATING;
+ ieee->softmac_stats.reassoc++;
+ ieee->is_roaming = true;
+ ieee->LinkDetectInfo.bBusyTraffic = false;
+ rtllib_disassociate(ieee);
+ RemovePeerTS(ieee, header->addr2);
+ if (ieee->LedControlHandler != NULL)
+ ieee->LedControlHandler(ieee->dev,
+ LED_CTL_START_TO_LINK);
+
+ if (!(ieee->rtllib_ap_sec_type(ieee) &
+ (SEC_ALG_CCMP|SEC_ALG_TKIP)))
+ queue_delayed_work_rsl(ieee->wq,
+ &ieee->associate_procedure_wq, 5);
+ }
+ return 0;
+}
+
+inline int rtllib_rx_frame_softmac(struct rtllib_device *ieee,
+ struct sk_buff *skb,
+ struct rtllib_rx_stats *rx_stats, u16 type,
+ u16 stype)
+{
+ struct rtllib_hdr_3addr *header = (struct rtllib_hdr_3addr *) skb->data;
+
+ if (!ieee->proto_started)
+ return 0;
+
+ switch (WLAN_FC_GET_STYPE(header->frame_ctl)) {
+ case RTLLIB_STYPE_ASSOC_RESP:
+ case RTLLIB_STYPE_REASSOC_RESP:
+ if (rtllib_rx_assoc_resp(ieee, skb, rx_stats) == 1)
+ return 1;
+ break;
+ case RTLLIB_STYPE_ASSOC_REQ:
+ case RTLLIB_STYPE_REASSOC_REQ:
+ if ((ieee->softmac_features & IEEE_SOFTMAC_ASSOCIATE) &&
+ ieee->iw_mode == IW_MODE_MASTER)
+ rtllib_rx_assoc_rq(ieee, skb);
+ break;
+ case RTLLIB_STYPE_AUTH:
+ rtllib_rx_auth(ieee, skb, rx_stats);
+ break;
+ case RTLLIB_STYPE_DISASSOC:
+ case RTLLIB_STYPE_DEAUTH:
+ rtllib_rx_deauth(ieee, skb);
+ break;
+ case RTLLIB_STYPE_MANAGE_ACT:
+ rtllib_process_action(ieee, skb);
+ break;
+ default:
+ return -1;
+ }
+ return 0;
+}
+
+/* following are for a simpler TX queue management.
+ * Instead of using netif_[stop/wake]_queue the driver
+ * will use these two functions (plus a reset one), that
+ * will internally use the kernel netif_* and takes
+ * care of the ieee802.11 fragmentation.
+ * So the driver receives a fragment per time and might
+ * call the stop function when it wants to not
+ * have enough room to TX an entire packet.
+ * This might be useful if each fragment needs it's own
+ * descriptor, thus just keep a total free memory > than
+ * the max fragmentation threshold is not enough.. If the
+ * ieee802.11 stack passed a TXB struct then you need
+ * to keep N free descriptors where
+ * N = MAX_PACKET_SIZE / MIN_FRAG_TRESHOLD
+ * In this way you need just one and the 802.11 stack
+ * will take care of buffering fragments and pass them to
+ * to the driver later, when it wakes the queue.
+ */
+void rtllib_softmac_xmit(struct rtllib_txb *txb, struct rtllib_device *ieee)
+{
+
+ unsigned int queue_index = txb->queue_index;
+ unsigned long flags;
+ int i;
+ struct cb_desc *tcb_desc = NULL;
+ unsigned long queue_len = 0;
+
+ spin_lock_irqsave(&ieee->lock, flags);
+
+ /* called with 2nd parm 0, no tx mgmt lock required */
+ rtllib_sta_wakeup(ieee, 0);
+
+ /* update the tx status */
+ tcb_desc = (struct cb_desc *)(txb->fragments[0]->cb +
+ MAX_DEV_ADDR_SIZE);
+ if (tcb_desc->bMulticast)
+ ieee->stats.multicast++;
+
+ /* if xmit available, just xmit it immediately, else just insert it to
+ * the wait queue
+ */
+ for (i = 0; i < txb->nr_frags; i++) {
+ queue_len = skb_queue_len(&ieee->skb_waitQ[queue_index]);
+ if ((queue_len != 0) ||
+ (!ieee->check_nic_enough_desc(ieee->dev, queue_index)) ||
+ (ieee->queue_stop)) {
+ /* insert the skb packet to the wait queue
+ * as for the completion function, it does not need
+ * to check it any more.
+ */
+ if (queue_len < 200)
+ skb_queue_tail(&ieee->skb_waitQ[queue_index],
+ txb->fragments[i]);
+ else
+ kfree_skb(txb->fragments[i]);
+ } else {
+ ieee->softmac_data_hard_start_xmit(
+ txb->fragments[i],
+ ieee->dev, ieee->rate);
+ }
+ }
+
+ rtllib_txb_free(txb);
+
+ spin_unlock_irqrestore(&ieee->lock, flags);
+
+}
+
+/* called with ieee->lock acquired */
+static void rtllib_resume_tx(struct rtllib_device *ieee)
+{
+ int i;
+
+ for (i = ieee->tx_pending.frag; i < ieee->tx_pending.txb->nr_frags;
+ i++) {
+
+ if (ieee->queue_stop) {
+ ieee->tx_pending.frag = i;
+ return;
+ }
+
+ ieee->softmac_data_hard_start_xmit(
+ ieee->tx_pending.txb->fragments[i],
+ ieee->dev, ieee->rate);
+ ieee->stats.tx_packets++;
+ }
+
+ rtllib_txb_free(ieee->tx_pending.txb);
+ ieee->tx_pending.txb = NULL;
+}
+
+
+void rtllib_reset_queue(struct rtllib_device *ieee)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&ieee->lock, flags);
+ init_mgmt_queue(ieee);
+ if (ieee->tx_pending.txb) {
+ rtllib_txb_free(ieee->tx_pending.txb);
+ ieee->tx_pending.txb = NULL;
+ }
+ ieee->queue_stop = 0;
+ spin_unlock_irqrestore(&ieee->lock, flags);
+
+}
+EXPORT_SYMBOL(rtllib_reset_queue);
+
+void rtllib_wake_queue(struct rtllib_device *ieee)
+{
+
+ unsigned long flags;
+ struct sk_buff *skb;
+ struct rtllib_hdr_3addr *header;
+
+ spin_lock_irqsave(&ieee->lock, flags);
+ if (!ieee->queue_stop)
+ goto exit;
+
+ ieee->queue_stop = 0;
+
+ if (ieee->softmac_features & IEEE_SOFTMAC_SINGLE_QUEUE) {
+ while (!ieee->queue_stop && (skb = dequeue_mgmt(ieee))) {
+
+ header = (struct rtllib_hdr_3addr *) skb->data;
+
+ header->seq_ctl = cpu_to_le16(ieee->seq_ctrl[0] << 4);
+
+ if (ieee->seq_ctrl[0] == 0xFFF)
+ ieee->seq_ctrl[0] = 0;
+ else
+ ieee->seq_ctrl[0]++;
+
+ ieee->softmac_data_hard_start_xmit(skb, ieee->dev,
+ ieee->basic_rate);
+ }
+ }
+ if (!ieee->queue_stop && ieee->tx_pending.txb)
+ rtllib_resume_tx(ieee);
+
+ if (!ieee->queue_stop && netif_queue_stopped(ieee->dev)) {
+ ieee->softmac_stats.swtxawake++;
+ netif_wake_queue(ieee->dev);
+ }
+
+exit:
+ spin_unlock_irqrestore(&ieee->lock, flags);
+}
+
+
+void rtllib_stop_queue(struct rtllib_device *ieee)
+{
+
+ if (!netif_queue_stopped(ieee->dev)) {
+ netif_stop_queue(ieee->dev);
+ ieee->softmac_stats.swtxstop++;
+ }
+ ieee->queue_stop = 1;
+
+}
+
+void rtllib_stop_all_queues(struct rtllib_device *ieee)
+{
+ unsigned int i;
+
+ for (i = 0; i < ieee->dev->num_tx_queues; i++)
+ netdev_get_tx_queue(ieee->dev, i)->trans_start = jiffies;
+
+ netif_tx_stop_all_queues(ieee->dev);
+}
+
+void rtllib_wake_all_queues(struct rtllib_device *ieee)
+{
+ netif_tx_wake_all_queues(ieee->dev);
+}
+
+inline void rtllib_randomize_cell(struct rtllib_device *ieee)
+{
+
+ random_ether_addr(ieee->current_network.bssid);
+}
+
+/* called in user context only */
+void rtllib_start_master_bss(struct rtllib_device *ieee)
+{
+ ieee->assoc_id = 1;
+
+ if (ieee->current_network.ssid_len == 0) {
+ strncpy(ieee->current_network.ssid,
+ RTLLIB_DEFAULT_TX_ESSID,
+ IW_ESSID_MAX_SIZE);
+
+ ieee->current_network.ssid_len =
+ strlen(RTLLIB_DEFAULT_TX_ESSID);
+ ieee->ssid_set = 1;
+ }
+
+ memcpy(ieee->current_network.bssid, ieee->dev->dev_addr, ETH_ALEN);
+
+ ieee->set_chan(ieee->dev, ieee->current_network.channel);
+ ieee->state = RTLLIB_LINKED;
+ ieee->link_change(ieee->dev);
+ notify_wx_assoc_event(ieee);
+
+ if (ieee->data_hard_resume)
+ ieee->data_hard_resume(ieee->dev);
+
+ netif_carrier_on(ieee->dev);
+}
+
+static void rtllib_start_monitor_mode(struct rtllib_device *ieee)
+{
+ /* reset hardware status */
+ if (ieee->raw_tx) {
+ if (ieee->data_hard_resume)
+ ieee->data_hard_resume(ieee->dev);
+
+ netif_carrier_on(ieee->dev);
+ }
+}
+
+static void rtllib_start_ibss_wq(void *data)
+{
+ struct rtllib_device *ieee = container_of_dwork_rsl(data,
+ struct rtllib_device, start_ibss_wq);
+ /* iwconfig mode ad-hoc will schedule this and return
+ * on the other hand this will block further iwconfig SET
+ * operations because of the wx_sem hold.
+ * Anyway some most set operations set a flag to speed-up
+ * (abort) this wq (when syncro scanning) before sleeping
+ * on the semaphore
+ */
+ if (!ieee->proto_started) {
+ netdev_info(ieee->dev, "==========oh driver down return\n");
+ return;
+ }
+ down(&ieee->wx_sem);
+
+ if (ieee->current_network.ssid_len == 0) {
+ strcpy(ieee->current_network.ssid, RTLLIB_DEFAULT_TX_ESSID);
+ ieee->current_network.ssid_len = strlen(RTLLIB_DEFAULT_TX_ESSID);
+ ieee->ssid_set = 1;
+ }
+
+ ieee->state = RTLLIB_NOLINK;
+ ieee->mode = IEEE_G;
+ /* check if we have this cell in our network list */
+ rtllib_softmac_check_all_nets(ieee);
+
+
+ /* if not then the state is not linked. Maybe the user switched to
+ * ad-hoc mode just after being in monitor mode, or just after
+ * being very few time in managed mode (so the card have had no
+ * time to scan all the chans..) or we have just run up the iface
+ * after setting ad-hoc mode. So we have to give another try..
+ * Here, in ibss mode, should be safe to do this without extra care
+ * (in bss mode we had to make sure no-one tried to associate when
+ * we had just checked the ieee->state and we was going to start the
+ * scan) because in ibss mode the rtllib_new_net function, when
+ * finds a good net, just set the ieee->state to RTLLIB_LINKED,
+ * so, at worst, we waste a bit of time to initiate an unneeded syncro
+ * scan, that will stop at the first round because it sees the state
+ * associated.
+ */
+ if (ieee->state == RTLLIB_NOLINK)
+ rtllib_start_scan_syncro(ieee, 0);
+
+ /* the network definitively is not here.. create a new cell */
+ if (ieee->state == RTLLIB_NOLINK) {
+ netdev_info(ieee->dev, "creating new IBSS cell\n");
+ ieee->current_network.channel = ieee->IbssStartChnl;
+ if (!ieee->wap_set)
+ rtllib_randomize_cell(ieee);
+
+ if (ieee->modulation & RTLLIB_CCK_MODULATION) {
+
+ ieee->current_network.rates_len = 4;
+
+ ieee->current_network.rates[0] =
+ RTLLIB_BASIC_RATE_MASK | RTLLIB_CCK_RATE_1MB;
+ ieee->current_network.rates[1] =
+ RTLLIB_BASIC_RATE_MASK | RTLLIB_CCK_RATE_2MB;
+ ieee->current_network.rates[2] =
+ RTLLIB_BASIC_RATE_MASK | RTLLIB_CCK_RATE_5MB;
+ ieee->current_network.rates[3] =
+ RTLLIB_BASIC_RATE_MASK | RTLLIB_CCK_RATE_11MB;
+
+ } else
+ ieee->current_network.rates_len = 0;
+
+ if (ieee->modulation & RTLLIB_OFDM_MODULATION) {
+ ieee->current_network.rates_ex_len = 8;
+
+ ieee->current_network.rates_ex[0] =
+ RTLLIB_OFDM_RATE_6MB;
+ ieee->current_network.rates_ex[1] =
+ RTLLIB_OFDM_RATE_9MB;
+ ieee->current_network.rates_ex[2] =
+ RTLLIB_OFDM_RATE_12MB;
+ ieee->current_network.rates_ex[3] =
+ RTLLIB_OFDM_RATE_18MB;
+ ieee->current_network.rates_ex[4] =
+ RTLLIB_OFDM_RATE_24MB;
+ ieee->current_network.rates_ex[5] =
+ RTLLIB_OFDM_RATE_36MB;
+ ieee->current_network.rates_ex[6] =
+ RTLLIB_OFDM_RATE_48MB;
+ ieee->current_network.rates_ex[7] =
+ RTLLIB_OFDM_RATE_54MB;
+
+ ieee->rate = 108;
+ } else {
+ ieee->current_network.rates_ex_len = 0;
+ ieee->rate = 22;
+ }
+
+ ieee->current_network.qos_data.supported = 0;
+ ieee->SetWirelessMode(ieee->dev, IEEE_G);
+ ieee->current_network.mode = ieee->mode;
+ ieee->current_network.atim_window = 0;
+ ieee->current_network.capability = WLAN_CAPABILITY_IBSS;
+ }
+
+ netdev_info(ieee->dev, "%s(): ieee->mode = %d\n", __func__, ieee->mode);
+ if ((ieee->mode == IEEE_N_24G) || (ieee->mode == IEEE_N_5G))
+ HTUseDefaultSetting(ieee);
+ else
+ ieee->pHTInfo->bCurrentHTSupport = false;
+
+ ieee->SetHwRegHandler(ieee->dev, HW_VAR_MEDIA_STATUS,
+ (u8 *)(&ieee->state));
+
+ ieee->state = RTLLIB_LINKED;
+ ieee->link_change(ieee->dev);
+
+ HTSetConnectBwMode(ieee, HT_CHANNEL_WIDTH_20, HT_EXTCHNL_OFFSET_NO_EXT);
+ if (ieee->LedControlHandler != NULL)
+ ieee->LedControlHandler(ieee->dev, LED_CTL_LINK);
+
+ rtllib_start_send_beacons(ieee);
+
+ notify_wx_assoc_event(ieee);
+
+ if (ieee->data_hard_resume)
+ ieee->data_hard_resume(ieee->dev);
+
+ netif_carrier_on(ieee->dev);
+
+ up(&ieee->wx_sem);
+}
+
+inline void rtllib_start_ibss(struct rtllib_device *ieee)
+{
+ queue_delayed_work_rsl(ieee->wq, &ieee->start_ibss_wq,
+ msecs_to_jiffies(150));
+}
+
+/* this is called only in user context, with wx_sem held */
+void rtllib_start_bss(struct rtllib_device *ieee)
+{
+ unsigned long flags;
+
+ if (IS_DOT11D_ENABLE(ieee) && !IS_COUNTRY_IE_VALID(ieee)) {
+ if (!ieee->bGlobalDomain)
+ return;
+ }
+ /* check if we have already found the net we
+ * are interested in (if any).
+ * if not (we are disassociated and we are not
+ * in associating / authenticating phase) start the background scanning.
+ */
+ rtllib_softmac_check_all_nets(ieee);
+
+ /* ensure no-one start an associating process (thus setting
+ * the ieee->state to rtllib_ASSOCIATING) while we
+ * have just checked it and we are going to enable scan.
+ * The rtllib_new_net function is always called with
+ * lock held (from both rtllib_softmac_check_all_nets and
+ * the rx path), so we cannot be in the middle of such function
+ */
+ spin_lock_irqsave(&ieee->lock, flags);
+
+ if (ieee->state == RTLLIB_NOLINK)
+ rtllib_start_scan(ieee);
+ spin_unlock_irqrestore(&ieee->lock, flags);
+}
+
+static void rtllib_link_change_wq(void *data)
+{
+ struct rtllib_device *ieee = container_of_dwork_rsl(data,
+ struct rtllib_device, link_change_wq);
+ ieee->link_change(ieee->dev);
+}
+/* called only in userspace context */
+void rtllib_disassociate(struct rtllib_device *ieee)
+{
+ netif_carrier_off(ieee->dev);
+ if (ieee->softmac_features & IEEE_SOFTMAC_TX_QUEUE)
+ rtllib_reset_queue(ieee);
+
+ if (ieee->data_hard_stop)
+ ieee->data_hard_stop(ieee->dev);
+ if (IS_DOT11D_ENABLE(ieee))
+ Dot11d_Reset(ieee);
+ ieee->state = RTLLIB_NOLINK;
+ ieee->is_set_key = false;
+ ieee->wap_set = 0;
+
+ queue_delayed_work_rsl(ieee->wq, &ieee->link_change_wq, 0);
+
+ notify_wx_assoc_event(ieee);
+}
+
+static void rtllib_associate_retry_wq(void *data)
+{
+ struct rtllib_device *ieee = container_of_dwork_rsl(data,
+ struct rtllib_device, associate_retry_wq);
+ unsigned long flags;
+
+ down(&ieee->wx_sem);
+ if (!ieee->proto_started)
+ goto exit;
+
+ if (ieee->state != RTLLIB_ASSOCIATING_RETRY)
+ goto exit;
+
+ /* until we do not set the state to RTLLIB_NOLINK
+ * there are no possibility to have someone else trying
+ * to start an association procedure (we get here with
+ * ieee->state = RTLLIB_ASSOCIATING).
+ * When we set the state to RTLLIB_NOLINK it is possible
+ * that the RX path run an attempt to associate, but
+ * both rtllib_softmac_check_all_nets and the
+ * RX path works with ieee->lock held so there are no
+ * problems. If we are still disassociated then start a scan.
+ * the lock here is necessary to ensure no one try to start
+ * an association procedure when we have just checked the
+ * state and we are going to start the scan.
+ */
+ ieee->beinretry = true;
+ ieee->state = RTLLIB_NOLINK;
+
+ rtllib_softmac_check_all_nets(ieee);
+
+ spin_lock_irqsave(&ieee->lock, flags);
+
+ if (ieee->state == RTLLIB_NOLINK)
+ rtllib_start_scan(ieee);
+ spin_unlock_irqrestore(&ieee->lock, flags);
+
+ ieee->beinretry = false;
+exit:
+ up(&ieee->wx_sem);
+}
+
+struct sk_buff *rtllib_get_beacon_(struct rtllib_device *ieee)
+{
+ u8 broadcast_addr[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
+
+ struct sk_buff *skb;
+ struct rtllib_probe_response *b;
+
+ skb = rtllib_probe_resp(ieee, broadcast_addr);
+
+ if (!skb)
+ return NULL;
+
+ b = (struct rtllib_probe_response *) skb->data;
+ b->header.frame_ctl = cpu_to_le16(RTLLIB_STYPE_BEACON);
+
+ return skb;
+
+}
+
+struct sk_buff *rtllib_get_beacon(struct rtllib_device *ieee)
+{
+ struct sk_buff *skb;
+ struct rtllib_probe_response *b;
+
+ skb = rtllib_get_beacon_(ieee);
+ if (!skb)
+ return NULL;
+
+ b = (struct rtllib_probe_response *) skb->data;
+ b->header.seq_ctl = cpu_to_le16(ieee->seq_ctrl[0] << 4);
+
+ if (ieee->seq_ctrl[0] == 0xFFF)
+ ieee->seq_ctrl[0] = 0;
+ else
+ ieee->seq_ctrl[0]++;
+
+ return skb;
+}
+EXPORT_SYMBOL(rtllib_get_beacon);
+
+void rtllib_softmac_stop_protocol(struct rtllib_device *ieee, u8 mesh_flag,
+ u8 shutdown)
+{
+ rtllib_stop_scan_syncro(ieee);
+ down(&ieee->wx_sem);
+ rtllib_stop_protocol(ieee, shutdown);
+ up(&ieee->wx_sem);
+}
+EXPORT_SYMBOL(rtllib_softmac_stop_protocol);
+
+
+void rtllib_stop_protocol(struct rtllib_device *ieee, u8 shutdown)
+{
+ if (!ieee->proto_started)
+ return;
+
+ if (shutdown) {
+ ieee->proto_started = 0;
+ ieee->proto_stoppping = 1;
+ if (ieee->rtllib_ips_leave != NULL)
+ ieee->rtllib_ips_leave(ieee->dev);
+ }
+
+ rtllib_stop_send_beacons(ieee);
+ del_timer_sync(&ieee->associate_timer);
+ cancel_delayed_work(&ieee->associate_retry_wq);
+ cancel_delayed_work(&ieee->start_ibss_wq);
+ cancel_delayed_work(&ieee->link_change_wq);
+ rtllib_stop_scan(ieee);
+
+ if (ieee->state <= RTLLIB_ASSOCIATING_AUTHENTICATED)
+ ieee->state = RTLLIB_NOLINK;
+
+ if (ieee->state == RTLLIB_LINKED) {
+ if (ieee->iw_mode == IW_MODE_INFRA)
+ SendDisassociation(ieee, 1, deauth_lv_ss);
+ rtllib_disassociate(ieee);
+ }
+
+ if (shutdown) {
+ RemoveAllTS(ieee);
+ ieee->proto_stoppping = 0;
+ }
+ kfree(ieee->assocreq_ies);
+ ieee->assocreq_ies = NULL;
+ ieee->assocreq_ies_len = 0;
+ kfree(ieee->assocresp_ies);
+ ieee->assocresp_ies = NULL;
+ ieee->assocresp_ies_len = 0;
+}
+
+void rtllib_softmac_start_protocol(struct rtllib_device *ieee, u8 mesh_flag)
+{
+ down(&ieee->wx_sem);
+ rtllib_start_protocol(ieee);
+ up(&ieee->wx_sem);
+}
+EXPORT_SYMBOL(rtllib_softmac_start_protocol);
+
+void rtllib_start_protocol(struct rtllib_device *ieee)
+{
+ short ch = 0;
+ int i = 0;
+
+ rtllib_update_active_chan_map(ieee);
+
+ if (ieee->proto_started)
+ return;
+
+ ieee->proto_started = 1;
+
+ if (ieee->current_network.channel == 0) {
+ do {
+ ch++;
+ if (ch > MAX_CHANNEL_NUMBER)
+ return; /* no channel found */
+ } while (!ieee->active_channel_map[ch]);
+ ieee->current_network.channel = ch;
+ }
+
+ if (ieee->current_network.beacon_interval == 0)
+ ieee->current_network.beacon_interval = 100;
+
+ for (i = 0; i < 17; i++) {
+ ieee->last_rxseq_num[i] = -1;
+ ieee->last_rxfrag_num[i] = -1;
+ ieee->last_packet_time[i] = 0;
+ }
+
+ if (ieee->UpdateBeaconInterruptHandler)
+ ieee->UpdateBeaconInterruptHandler(ieee->dev, false);
+
+ ieee->wmm_acm = 0;
+ /* if the user set the MAC of the ad-hoc cell and then
+ * switch to managed mode, shall we make sure that association
+ * attempts does not fail just because the user provide the essid
+ * and the nic is still checking for the AP MAC ??
+ */
+ if (ieee->iw_mode == IW_MODE_INFRA) {
+ rtllib_start_bss(ieee);
+ } else if (ieee->iw_mode == IW_MODE_ADHOC) {
+ if (ieee->UpdateBeaconInterruptHandler)
+ ieee->UpdateBeaconInterruptHandler(ieee->dev, true);
+
+ rtllib_start_ibss(ieee);
+
+ } else if (ieee->iw_mode == IW_MODE_MASTER) {
+ rtllib_start_master_bss(ieee);
+ } else if (ieee->iw_mode == IW_MODE_MONITOR) {
+ rtllib_start_monitor_mode(ieee);
+ }
+}
+
+void rtllib_softmac_init(struct rtllib_device *ieee)
+{
+ int i;
+
+ memset(&ieee->current_network, 0, sizeof(struct rtllib_network));
+
+ ieee->state = RTLLIB_NOLINK;
+ for (i = 0; i < 5; i++)
+ ieee->seq_ctrl[i] = 0;
+ ieee->pDot11dInfo = kzalloc(sizeof(struct rt_dot11d_info), GFP_ATOMIC);
+ if (!ieee->pDot11dInfo)
+ RTLLIB_DEBUG(RTLLIB_DL_ERR, "can't alloc memory for DOT11D\n");
+ ieee->LinkDetectInfo.SlotIndex = 0;
+ ieee->LinkDetectInfo.SlotNum = 2;
+ ieee->LinkDetectInfo.NumRecvBcnInPeriod = 0;
+ ieee->LinkDetectInfo.NumRecvDataInPeriod = 0;
+ ieee->LinkDetectInfo.NumTxOkInPeriod = 0;
+ ieee->LinkDetectInfo.NumRxOkInPeriod = 0;
+ ieee->LinkDetectInfo.NumRxUnicastOkInPeriod = 0;
+ ieee->bIsAggregateFrame = false;
+ ieee->assoc_id = 0;
+ ieee->queue_stop = 0;
+ ieee->scanning_continue = 0;
+ ieee->softmac_features = 0;
+ ieee->wap_set = 0;
+ ieee->ssid_set = 0;
+ ieee->proto_started = 0;
+ ieee->proto_stoppping = 0;
+ ieee->basic_rate = RTLLIB_DEFAULT_BASIC_RATE;
+ ieee->rate = 22;
+ ieee->ps = RTLLIB_PS_DISABLED;
+ ieee->sta_sleep = LPS_IS_WAKE;
+
+ ieee->Regdot11HTOperationalRateSet[0] = 0xff;
+ ieee->Regdot11HTOperationalRateSet[1] = 0xff;
+ ieee->Regdot11HTOperationalRateSet[4] = 0x01;
+
+ ieee->Regdot11TxHTOperationalRateSet[0] = 0xff;
+ ieee->Regdot11TxHTOperationalRateSet[1] = 0xff;
+ ieee->Regdot11TxHTOperationalRateSet[4] = 0x01;
+
+ ieee->FirstIe_InScan = false;
+ ieee->actscanning = false;
+ ieee->beinretry = false;
+ ieee->is_set_key = false;
+ init_mgmt_queue(ieee);
+
+ ieee->sta_edca_param[0] = 0x0000A403;
+ ieee->sta_edca_param[1] = 0x0000A427;
+ ieee->sta_edca_param[2] = 0x005E4342;
+ ieee->sta_edca_param[3] = 0x002F3262;
+ ieee->aggregation = true;
+ ieee->enable_rx_imm_BA = true;
+ ieee->tx_pending.txb = NULL;
+
+ _setup_timer(&ieee->associate_timer,
+ rtllib_associate_abort_cb,
+ (unsigned long) ieee);
+
+ _setup_timer(&ieee->beacon_timer,
+ rtllib_send_beacon_cb,
+ (unsigned long) ieee);
+
+
+ ieee->wq = create_workqueue(DRV_NAME);
+
+ INIT_DELAYED_WORK_RSL(&ieee->link_change_wq,
+ (void *)rtllib_link_change_wq, ieee);
+ INIT_DELAYED_WORK_RSL(&ieee->start_ibss_wq,
+ (void *)rtllib_start_ibss_wq, ieee);
+ INIT_WORK_RSL(&ieee->associate_complete_wq,
+ (void *)rtllib_associate_complete_wq, ieee);
+ INIT_DELAYED_WORK_RSL(&ieee->associate_procedure_wq,
+ (void *)rtllib_associate_procedure_wq, ieee);
+ INIT_DELAYED_WORK_RSL(&ieee->softmac_scan_wq,
+ (void *)rtllib_softmac_scan_wq, ieee);
+ INIT_DELAYED_WORK_RSL(&ieee->softmac_hint11d_wq,
+ (void *)rtllib_softmac_hint11d_wq, ieee);
+ INIT_DELAYED_WORK_RSL(&ieee->associate_retry_wq,
+ (void *)rtllib_associate_retry_wq, ieee);
+ INIT_WORK_RSL(&ieee->wx_sync_scan_wq, (void *)rtllib_wx_sync_scan_wq,
+ ieee);
+
+ sema_init(&ieee->wx_sem, 1);
+ sema_init(&ieee->scan_sem, 1);
+ sema_init(&ieee->ips_sem, 1);
+
+ spin_lock_init(&ieee->mgmt_tx_lock);
+ spin_lock_init(&ieee->beacon_lock);
+
+ tasklet_init(&ieee->ps_task,
+ (void(*)(unsigned long)) rtllib_sta_ps,
+ (unsigned long)ieee);
+
+}
+
+void rtllib_softmac_free(struct rtllib_device *ieee)
+{
+ down(&ieee->wx_sem);
+ kfree(ieee->pDot11dInfo);
+ ieee->pDot11dInfo = NULL;
+ del_timer_sync(&ieee->associate_timer);
+
+ cancel_delayed_work(&ieee->associate_retry_wq);
+ destroy_workqueue(ieee->wq);
+ up(&ieee->wx_sem);
+ tasklet_kill(&ieee->ps_task);
+}
+
+/********************************************************
+ * Start of WPA code. *
+ * this is stolen from the ipw2200 driver *
+ ********************************************************/
+
+
+static int rtllib_wpa_enable(struct rtllib_device *ieee, int value)
+{
+ /* This is called when wpa_supplicant loads and closes the driver
+ * interface.
+ */
+ netdev_info(ieee->dev, "%s WPA\n", value ? "enabling" : "disabling");
+ ieee->wpa_enabled = value;
+ memset(ieee->ap_mac_addr, 0, 6);
+ return 0;
+}
+
+
+static void rtllib_wpa_assoc_frame(struct rtllib_device *ieee, char *wpa_ie,
+ int wpa_ie_len)
+{
+ /* make sure WPA is enabled */
+ rtllib_wpa_enable(ieee, 1);
+
+ rtllib_disassociate(ieee);
+}
+
+
+static int rtllib_wpa_mlme(struct rtllib_device *ieee, int command, int reason)
+{
+
+ int ret = 0;
+
+ switch (command) {
+ case IEEE_MLME_STA_DEAUTH:
+ break;
+
+ case IEEE_MLME_STA_DISASSOC:
+ rtllib_disassociate(ieee);
+ break;
+
+ default:
+ netdev_info(ieee->dev, "Unknown MLME request: %d\n", command);
+ ret = -EOPNOTSUPP;
+ }
+
+ return ret;
+}
+
+
+static int rtllib_wpa_set_wpa_ie(struct rtllib_device *ieee,
+ struct ieee_param *param, int plen)
+{
+ u8 *buf;
+
+ if (param->u.wpa_ie.len > MAX_WPA_IE_LEN ||
+ (param->u.wpa_ie.len && param->u.wpa_ie.data == NULL))
+ return -EINVAL;
+
+ if (param->u.wpa_ie.len) {
+ buf = kmemdup(param->u.wpa_ie.data, param->u.wpa_ie.len,
+ GFP_KERNEL);
+ if (buf == NULL)
+ return -ENOMEM;
+
+ kfree(ieee->wpa_ie);
+ ieee->wpa_ie = buf;
+ ieee->wpa_ie_len = param->u.wpa_ie.len;
+ } else {
+ kfree(ieee->wpa_ie);
+ ieee->wpa_ie = NULL;
+ ieee->wpa_ie_len = 0;
+ }
+
+ rtllib_wpa_assoc_frame(ieee, ieee->wpa_ie, ieee->wpa_ie_len);
+ return 0;
+}
+
+#define AUTH_ALG_OPEN_SYSTEM 0x1
+#define AUTH_ALG_SHARED_KEY 0x2
+#define AUTH_ALG_LEAP 0x4
+static int rtllib_wpa_set_auth_algs(struct rtllib_device *ieee, int value)
+{
+
+ struct rtllib_security sec = {
+ .flags = SEC_AUTH_MODE,
+ };
+
+ if (value & AUTH_ALG_SHARED_KEY) {
+ sec.auth_mode = WLAN_AUTH_SHARED_KEY;
+ ieee->open_wep = 0;
+ ieee->auth_mode = 1;
+ } else if (value & AUTH_ALG_OPEN_SYSTEM) {
+ sec.auth_mode = WLAN_AUTH_OPEN;
+ ieee->open_wep = 1;
+ ieee->auth_mode = 0;
+ } else if (value & AUTH_ALG_LEAP) {
+ sec.auth_mode = WLAN_AUTH_LEAP >> 6;
+ ieee->open_wep = 1;
+ ieee->auth_mode = 2;
+ }
+
+
+ if (ieee->set_security)
+ ieee->set_security(ieee->dev, &sec);
+
+ return 0;
+}
+
+static int rtllib_wpa_set_param(struct rtllib_device *ieee, u8 name, u32 value)
+{
+ int ret = 0;
+ unsigned long flags;
+
+ switch (name) {
+ case IEEE_PARAM_WPA_ENABLED:
+ ret = rtllib_wpa_enable(ieee, value);
+ break;
+
+ case IEEE_PARAM_TKIP_COUNTERMEASURES:
+ ieee->tkip_countermeasures = value;
+ break;
+
+ case IEEE_PARAM_DROP_UNENCRYPTED:
+ {
+ /* HACK:
+ *
+ * wpa_supplicant calls set_wpa_enabled when the driver
+ * is loaded and unloaded, regardless of if WPA is being
+ * used. No other calls are made which can be used to
+ * determine if encryption will be used or not prior to
+ * association being expected. If encryption is not being
+ * used, drop_unencrypted is set to false, else true -- we
+ * can use this to determine if the CAP_PRIVACY_ON bit should
+ * be set.
+ */
+ struct rtllib_security sec = {
+ .flags = SEC_ENABLED,
+ .enabled = value,
+ };
+ ieee->drop_unencrypted = value;
+ /* We only change SEC_LEVEL for open mode. Others
+ * are set by ipw_wpa_set_encryption.
+ */
+ if (!value) {
+ sec.flags |= SEC_LEVEL;
+ sec.level = SEC_LEVEL_0;
+ } else {
+ sec.flags |= SEC_LEVEL;
+ sec.level = SEC_LEVEL_1;
+ }
+ if (ieee->set_security)
+ ieee->set_security(ieee->dev, &sec);
+ break;
+ }
+
+ case IEEE_PARAM_PRIVACY_INVOKED:
+ ieee->privacy_invoked = value;
+ break;
+
+ case IEEE_PARAM_AUTH_ALGS:
+ ret = rtllib_wpa_set_auth_algs(ieee, value);
+ break;
+
+ case IEEE_PARAM_IEEE_802_1X:
+ ieee->ieee802_1x = value;
+ break;
+ case IEEE_PARAM_WPAX_SELECT:
+ spin_lock_irqsave(&ieee->wpax_suitlist_lock, flags);
+ spin_unlock_irqrestore(&ieee->wpax_suitlist_lock, flags);
+ break;
+
+ default:
+ netdev_info(ieee->dev, "Unknown WPA param: %d\n", name);
+ ret = -EOPNOTSUPP;
+ }
+
+ return ret;
+}
+
+/* implementation borrowed from hostap driver */
+static int rtllib_wpa_set_encryption(struct rtllib_device *ieee,
+ struct ieee_param *param, int param_len,
+ u8 is_mesh)
+{
+ int ret = 0;
+ struct lib80211_crypto_ops *ops;
+ struct lib80211_crypt_data **crypt;
+
+ struct rtllib_security sec = {
+ .flags = 0,
+ };
+
+ param->u.crypt.err = 0;
+ param->u.crypt.alg[IEEE_CRYPT_ALG_NAME_LEN - 1] = '\0';
+
+ if (param_len !=
+ (int) ((char *) param->u.crypt.key - (char *) param) +
+ param->u.crypt.key_len) {
+ netdev_info(ieee->dev, "Len mismatch %d, %d\n", param_len,
+ param->u.crypt.key_len);
+ return -EINVAL;
+ }
+ if (is_broadcast_ether_addr(param->sta_addr)) {
+ if (param->u.crypt.idx >= NUM_WEP_KEYS)
+ return -EINVAL;
+ crypt = &ieee->crypt_info.crypt[param->u.crypt.idx];
+ } else {
+ return -EINVAL;
+ }
+
+ if (strcmp(param->u.crypt.alg, "none") == 0) {
+ if (crypt) {
+ sec.enabled = 0;
+ sec.level = SEC_LEVEL_0;
+ sec.flags |= SEC_ENABLED | SEC_LEVEL;
+ lib80211_crypt_delayed_deinit(&ieee->crypt_info, crypt);
+ }
+ goto done;
+ }
+ sec.enabled = 1;
+ sec.flags |= SEC_ENABLED;
+
+ /* IPW HW cannot build TKIP MIC, host decryption still needed. */
+ if (!(ieee->host_encrypt || ieee->host_decrypt) &&
+ strcmp(param->u.crypt.alg, "R-TKIP"))
+ goto skip_host_crypt;
+
+ ops = lib80211_get_crypto_ops(param->u.crypt.alg);
+ if (ops == NULL && strcmp(param->u.crypt.alg, "R-WEP") == 0) {
+ request_module("rtllib_crypt_wep");
+ ops = lib80211_get_crypto_ops(param->u.crypt.alg);
+ } else if (ops == NULL && strcmp(param->u.crypt.alg, "R-TKIP") == 0) {
+ request_module("rtllib_crypt_tkip");
+ ops = lib80211_get_crypto_ops(param->u.crypt.alg);
+ } else if (ops == NULL && strcmp(param->u.crypt.alg, "R-CCMP") == 0) {
+ request_module("rtllib_crypt_ccmp");
+ ops = lib80211_get_crypto_ops(param->u.crypt.alg);
+ }
+ if (ops == NULL) {
+ netdev_info(ieee->dev, "unknown crypto alg '%s'\n",
+ param->u.crypt.alg);
+ param->u.crypt.err = IEEE_CRYPT_ERR_UNKNOWN_ALG;
+ ret = -EINVAL;
+ goto done;
+ }
+ if (*crypt == NULL || (*crypt)->ops != ops) {
+ struct lib80211_crypt_data *new_crypt;
+
+ lib80211_crypt_delayed_deinit(&ieee->crypt_info, crypt);
+
+ new_crypt = kzalloc(sizeof(*new_crypt), GFP_KERNEL);
+ if (new_crypt == NULL) {
+ ret = -ENOMEM;
+ goto done;
+ }
+ new_crypt->ops = ops;
+ if (new_crypt->ops)
+ new_crypt->priv =
+ new_crypt->ops->init(param->u.crypt.idx);
+
+ if (new_crypt->priv == NULL) {
+ kfree(new_crypt);
+ param->u.crypt.err = IEEE_CRYPT_ERR_CRYPT_INIT_FAILED;
+ ret = -EINVAL;
+ goto done;
+ }
+
+ *crypt = new_crypt;
+ }
+
+ if (param->u.crypt.key_len > 0 && (*crypt)->ops->set_key &&
+ (*crypt)->ops->set_key(param->u.crypt.key,
+ param->u.crypt.key_len, param->u.crypt.seq,
+ (*crypt)->priv) < 0) {
+ netdev_info(ieee->dev, "key setting failed\n");
+ param->u.crypt.err = IEEE_CRYPT_ERR_KEY_SET_FAILED;
+ ret = -EINVAL;
+ goto done;
+ }
+
+ skip_host_crypt:
+ if (param->u.crypt.set_tx) {
+ ieee->crypt_info.tx_keyidx = param->u.crypt.idx;
+ sec.active_key = param->u.crypt.idx;
+ sec.flags |= SEC_ACTIVE_KEY;
+ } else
+ sec.flags &= ~SEC_ACTIVE_KEY;
+
+ if (param->u.crypt.alg != NULL) {
+ memcpy(sec.keys[param->u.crypt.idx],
+ param->u.crypt.key,
+ param->u.crypt.key_len);
+ sec.key_sizes[param->u.crypt.idx] = param->u.crypt.key_len;
+ sec.flags |= (1 << param->u.crypt.idx);
+
+ if (strcmp(param->u.crypt.alg, "R-WEP") == 0) {
+ sec.flags |= SEC_LEVEL;
+ sec.level = SEC_LEVEL_1;
+ } else if (strcmp(param->u.crypt.alg, "R-TKIP") == 0) {
+ sec.flags |= SEC_LEVEL;
+ sec.level = SEC_LEVEL_2;
+ } else if (strcmp(param->u.crypt.alg, "R-CCMP") == 0) {
+ sec.flags |= SEC_LEVEL;
+ sec.level = SEC_LEVEL_3;
+ }
+ }
+ done:
+ if (ieee->set_security)
+ ieee->set_security(ieee->dev, &sec);
+
+ /* Do not reset port if card is in Managed mode since resetting will
+ * generate new IEEE 802.11 authentication which may end up in looping
+ * with IEEE 802.1X. If your hardware requires a reset after WEP
+ * configuration (for example... Prism2), implement the reset_port in
+ * the callbacks structures used to initialize the 802.11 stack.
+ */
+ if (ieee->reset_on_keychange &&
+ ieee->iw_mode != IW_MODE_INFRA &&
+ ieee->reset_port &&
+ ieee->reset_port(ieee->dev)) {
+ netdev_info(ieee->dev, "reset_port failed\n");
+ param->u.crypt.err = IEEE_CRYPT_ERR_CARD_CONF_FAILED;
+ return -EINVAL;
+ }
+
+ return ret;
+}
+
+inline struct sk_buff *rtllib_disauth_skb(struct rtllib_network *beacon,
+ struct rtllib_device *ieee, u16 asRsn)
+{
+ struct sk_buff *skb;
+ struct rtllib_disauth *disauth;
+ int len = sizeof(struct rtllib_disauth) + ieee->tx_headroom;
+
+ skb = dev_alloc_skb(len);
+ if (!skb)
+ return NULL;
+
+ skb_reserve(skb, ieee->tx_headroom);
+
+ disauth = (struct rtllib_disauth *) skb_put(skb,
+ sizeof(struct rtllib_disauth));
+ disauth->header.frame_ctl = cpu_to_le16(RTLLIB_STYPE_DEAUTH);
+ disauth->header.duration_id = 0;
+
+ memcpy(disauth->header.addr1, beacon->bssid, ETH_ALEN);
+ memcpy(disauth->header.addr2, ieee->dev->dev_addr, ETH_ALEN);
+ memcpy(disauth->header.addr3, beacon->bssid, ETH_ALEN);
+
+ disauth->reason = cpu_to_le16(asRsn);
+ return skb;
+}
+
+inline struct sk_buff *rtllib_disassociate_skb(struct rtllib_network *beacon,
+ struct rtllib_device *ieee, u16 asRsn)
+{
+ struct sk_buff *skb;
+ struct rtllib_disassoc *disass;
+ int len = sizeof(struct rtllib_disassoc) + ieee->tx_headroom;
+
+ skb = dev_alloc_skb(len);
+
+ if (!skb)
+ return NULL;
+
+ skb_reserve(skb, ieee->tx_headroom);
+
+ disass = (struct rtllib_disassoc *) skb_put(skb,
+ sizeof(struct rtllib_disassoc));
+ disass->header.frame_ctl = cpu_to_le16(RTLLIB_STYPE_DISASSOC);
+ disass->header.duration_id = 0;
+
+ memcpy(disass->header.addr1, beacon->bssid, ETH_ALEN);
+ memcpy(disass->header.addr2, ieee->dev->dev_addr, ETH_ALEN);
+ memcpy(disass->header.addr3, beacon->bssid, ETH_ALEN);
+
+ disass->reason = cpu_to_le16(asRsn);
+ return skb;
+}
+
+void SendDisassociation(struct rtllib_device *ieee, bool deauth, u16 asRsn)
+{
+ struct rtllib_network *beacon = &ieee->current_network;
+ struct sk_buff *skb;
+
+ if (deauth)
+ skb = rtllib_disauth_skb(beacon, ieee, asRsn);
+ else
+ skb = rtllib_disassociate_skb(beacon, ieee, asRsn);
+
+ if (skb)
+ softmac_mgmt_xmit(skb, ieee);
+}
+
+u8 rtllib_ap_sec_type(struct rtllib_device *ieee)
+{
+ static u8 ccmp_ie[4] = {0x00, 0x50, 0xf2, 0x04};
+ static u8 ccmp_rsn_ie[4] = {0x00, 0x0f, 0xac, 0x04};
+ int wpa_ie_len = ieee->wpa_ie_len;
+ struct lib80211_crypt_data *crypt;
+ int encrypt;
+
+ crypt = ieee->crypt_info.crypt[ieee->crypt_info.tx_keyidx];
+ encrypt = (ieee->current_network.capability & WLAN_CAPABILITY_PRIVACY)
+ || (ieee->host_encrypt && crypt && crypt->ops &&
+ (0 == strcmp(crypt->ops->name, "R-WEP")));
+
+ /* simply judge */
+ if (encrypt && (wpa_ie_len == 0)) {
+ return SEC_ALG_WEP;
+ } else if ((wpa_ie_len != 0)) {
+ if (((ieee->wpa_ie[0] == 0xdd) &&
+ (!memcmp(&(ieee->wpa_ie[14]), ccmp_ie, 4))) ||
+ ((ieee->wpa_ie[0] == 0x30) &&
+ (!memcmp(&ieee->wpa_ie[10], ccmp_rsn_ie, 4))))
+ return SEC_ALG_CCMP;
+ else
+ return SEC_ALG_TKIP;
+ } else {
+ return SEC_ALG_NONE;
+ }
+}
+
+int rtllib_wpa_supplicant_ioctl(struct rtllib_device *ieee, struct iw_point *p,
+ u8 is_mesh)
+{
+ struct ieee_param *param;
+ int ret = 0;
+
+ down(&ieee->wx_sem);
+
+ if (p->length < sizeof(struct ieee_param) || !p->pointer) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ param = memdup_user(p->pointer, p->length);
+ if (IS_ERR(param)) {
+ ret = PTR_ERR(param);
+ goto out;
+ }
+
+ switch (param->cmd) {
+ case IEEE_CMD_SET_WPA_PARAM:
+ ret = rtllib_wpa_set_param(ieee, param->u.wpa_param.name,
+ param->u.wpa_param.value);
+ break;
+
+ case IEEE_CMD_SET_WPA_IE:
+ ret = rtllib_wpa_set_wpa_ie(ieee, param, p->length);
+ break;
+
+ case IEEE_CMD_SET_ENCRYPTION:
+ ret = rtllib_wpa_set_encryption(ieee, param, p->length, 0);
+ break;
+
+ case IEEE_CMD_MLME:
+ ret = rtllib_wpa_mlme(ieee, param->u.mlme.command,
+ param->u.mlme.reason_code);
+ break;
+
+ default:
+ netdev_info(ieee->dev, "Unknown WPA supplicant request: %d\n",
+ param->cmd);
+ ret = -EOPNOTSUPP;
+ break;
+ }
+
+ if (ret == 0 && copy_to_user(p->pointer, param, p->length))
+ ret = -EFAULT;
+
+ kfree(param);
+out:
+ up(&ieee->wx_sem);
+
+ return ret;
+}
+EXPORT_SYMBOL(rtllib_wpa_supplicant_ioctl);
+
+static void rtllib_MgntDisconnectIBSS(struct rtllib_device *rtllib)
+{
+ u8 OpMode;
+ u8 i;
+ bool bFilterOutNonAssociatedBSSID = false;
+
+ rtllib->state = RTLLIB_NOLINK;
+
+ for (i = 0; i < 6; i++)
+ rtllib->current_network.bssid[i] = 0x55;
+
+ rtllib->OpMode = RT_OP_MODE_NO_LINK;
+ rtllib->SetHwRegHandler(rtllib->dev, HW_VAR_BSSID,
+ rtllib->current_network.bssid);
+ OpMode = RT_OP_MODE_NO_LINK;
+ rtllib->SetHwRegHandler(rtllib->dev, HW_VAR_MEDIA_STATUS, &OpMode);
+ rtllib_stop_send_beacons(rtllib);
+
+ bFilterOutNonAssociatedBSSID = false;
+ rtllib->SetHwRegHandler(rtllib->dev, HW_VAR_CECHK_BSSID,
+ (u8 *)(&bFilterOutNonAssociatedBSSID));
+ notify_wx_assoc_event(rtllib);
+
+}
+
+static void rtllib_MlmeDisassociateRequest(struct rtllib_device *rtllib, u8 *asSta,
+ u8 asRsn)
+{
+ u8 i;
+ u8 OpMode;
+
+ RemovePeerTS(rtllib, asSta);
+
+ if (memcmp(rtllib->current_network.bssid, asSta, 6) == 0) {
+ rtllib->state = RTLLIB_NOLINK;
+
+ for (i = 0; i < 6; i++)
+ rtllib->current_network.bssid[i] = 0x22;
+ OpMode = RT_OP_MODE_NO_LINK;
+ rtllib->OpMode = RT_OP_MODE_NO_LINK;
+ rtllib->SetHwRegHandler(rtllib->dev, HW_VAR_MEDIA_STATUS,
+ (u8 *)(&OpMode));
+ rtllib_disassociate(rtllib);
+
+ rtllib->SetHwRegHandler(rtllib->dev, HW_VAR_BSSID,
+ rtllib->current_network.bssid);
+
+ }
+
+}
+
+static void
+rtllib_MgntDisconnectAP(
+ struct rtllib_device *rtllib,
+ u8 asRsn
+)
+{
+ bool bFilterOutNonAssociatedBSSID = false;
+
+ bFilterOutNonAssociatedBSSID = false;
+ rtllib->SetHwRegHandler(rtllib->dev, HW_VAR_CECHK_BSSID,
+ (u8 *)(&bFilterOutNonAssociatedBSSID));
+ rtllib_MlmeDisassociateRequest(rtllib, rtllib->current_network.bssid,
+ asRsn);
+
+ rtllib->state = RTLLIB_NOLINK;
+}
+
+bool rtllib_MgntDisconnect(struct rtllib_device *rtllib, u8 asRsn)
+{
+ if (rtllib->ps != RTLLIB_PS_DISABLED)
+ rtllib->sta_wake_up(rtllib->dev);
+
+ if (rtllib->state == RTLLIB_LINKED) {
+ if (rtllib->iw_mode == IW_MODE_ADHOC)
+ rtllib_MgntDisconnectIBSS(rtllib);
+ if (rtllib->iw_mode == IW_MODE_INFRA)
+ rtllib_MgntDisconnectAP(rtllib, asRsn);
+
+ }
+
+ return true;
+}
+EXPORT_SYMBOL(rtllib_MgntDisconnect);
+
+void notify_wx_assoc_event(struct rtllib_device *ieee)
+{
+ union iwreq_data wrqu;
+
+ if (ieee->cannot_notify)
+ return;
+
+ wrqu.ap_addr.sa_family = ARPHRD_ETHER;
+ if (ieee->state == RTLLIB_LINKED)
+ memcpy(wrqu.ap_addr.sa_data, ieee->current_network.bssid,
+ ETH_ALEN);
+ else {
+
+ netdev_info(ieee->dev, "%s(): Tell user space disconnected\n",
+ __func__);
+ eth_zero_addr(wrqu.ap_addr.sa_data);
+ }
+ wireless_send_event(ieee->dev, SIOCGIWAP, &wrqu, NULL);
+}
+EXPORT_SYMBOL(notify_wx_assoc_event);
diff --git a/drivers/staging/rtl8192e/rtllib_softmac_wx.c b/drivers/staging/rtl8192e/rtllib_softmac_wx.c
new file mode 100644
index 000000000..9715a793f
--- /dev/null
+++ b/drivers/staging/rtl8192e/rtllib_softmac_wx.c
@@ -0,0 +1,661 @@
+/* IEEE 802.11 SoftMAC layer
+ * Copyright (c) 2005 Andrea Merello <andrea.merello@gmail.com>
+ *
+ * Mostly extracted from the rtl8180-sa2400 driver for the
+ * in-kernel generic ieee802.11 stack.
+ *
+ * Some pieces of code might be stolen from ipw2100 driver
+ * copyright of who own it's copyright ;-)
+ *
+ * PS wx handler mostly stolen from hostap, copyright who
+ * own it's copyright ;-)
+ *
+ * released under the GPL
+ */
+
+
+#include <linux/etherdevice.h>
+
+#include "rtllib.h"
+#include "dot11d.h"
+/* FIXME: add A freqs */
+
+const long rtllib_wlan_frequencies[] = {
+ 2412, 2417, 2422, 2427,
+ 2432, 2437, 2442, 2447,
+ 2452, 2457, 2462, 2467,
+ 2472, 2484
+};
+EXPORT_SYMBOL(rtllib_wlan_frequencies);
+
+
+int rtllib_wx_set_freq(struct rtllib_device *ieee, struct iw_request_info *a,
+ union iwreq_data *wrqu, char *b)
+{
+ int ret;
+ struct iw_freq *fwrq = &wrqu->freq;
+
+ down(&ieee->wx_sem);
+
+ if (ieee->iw_mode == IW_MODE_INFRA) {
+ ret = 0;
+ goto out;
+ }
+
+ /* if setting by freq convert to channel */
+ if (fwrq->e == 1) {
+ if ((fwrq->m >= (int) 2.412e8 &&
+ fwrq->m <= (int) 2.487e8)) {
+ int f = fwrq->m / 100000;
+ int c = 0;
+
+ while ((c < 14) && (f != rtllib_wlan_frequencies[c]))
+ c++;
+
+ /* hack to fall through */
+ fwrq->e = 0;
+ fwrq->m = c + 1;
+ }
+ }
+
+ if (fwrq->e > 0 || fwrq->m > 14 || fwrq->m < 1) {
+ ret = -EOPNOTSUPP;
+ goto out;
+
+ } else { /* Set the channel */
+
+ if (ieee->active_channel_map[fwrq->m] != 1) {
+ ret = -EINVAL;
+ goto out;
+ }
+ ieee->current_network.channel = fwrq->m;
+ ieee->set_chan(ieee->dev, ieee->current_network.channel);
+
+ if (ieee->iw_mode == IW_MODE_ADHOC ||
+ ieee->iw_mode == IW_MODE_MASTER)
+ if (ieee->state == RTLLIB_LINKED) {
+ rtllib_stop_send_beacons(ieee);
+ rtllib_start_send_beacons(ieee);
+ }
+ }
+
+ ret = 0;
+out:
+ up(&ieee->wx_sem);
+ return ret;
+}
+EXPORT_SYMBOL(rtllib_wx_set_freq);
+
+
+int rtllib_wx_get_freq(struct rtllib_device *ieee,
+ struct iw_request_info *a,
+ union iwreq_data *wrqu, char *b)
+{
+ struct iw_freq *fwrq = &wrqu->freq;
+
+ if (ieee->current_network.channel == 0)
+ return -1;
+ fwrq->m = rtllib_wlan_frequencies[ieee->current_network.channel-1] *
+ 100000;
+ fwrq->e = 1;
+ return 0;
+}
+EXPORT_SYMBOL(rtllib_wx_get_freq);
+
+int rtllib_wx_get_wap(struct rtllib_device *ieee,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ unsigned long flags;
+
+ wrqu->ap_addr.sa_family = ARPHRD_ETHER;
+
+ if (ieee->iw_mode == IW_MODE_MONITOR)
+ return -1;
+
+ /* We want avoid to give to the user inconsistent infos*/
+ spin_lock_irqsave(&ieee->lock, flags);
+
+ if (ieee->state != RTLLIB_LINKED &&
+ ieee->state != RTLLIB_LINKED_SCANNING &&
+ ieee->wap_set == 0)
+
+ eth_zero_addr(wrqu->ap_addr.sa_data);
+ else
+ memcpy(wrqu->ap_addr.sa_data,
+ ieee->current_network.bssid, ETH_ALEN);
+
+ spin_unlock_irqrestore(&ieee->lock, flags);
+
+ return 0;
+}
+EXPORT_SYMBOL(rtllib_wx_get_wap);
+
+
+int rtllib_wx_set_wap(struct rtllib_device *ieee,
+ struct iw_request_info *info,
+ union iwreq_data *awrq,
+ char *extra)
+{
+
+ int ret = 0;
+ unsigned long flags;
+
+ short ifup = ieee->proto_started;
+ struct sockaddr *temp = (struct sockaddr *)awrq;
+
+ rtllib_stop_scan_syncro(ieee);
+
+ down(&ieee->wx_sem);
+ /* use ifconfig hw ether */
+ if (ieee->iw_mode == IW_MODE_MASTER) {
+ ret = -1;
+ goto out;
+ }
+
+ if (temp->sa_family != ARPHRD_ETHER) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ if (is_zero_ether_addr(temp->sa_data)) {
+ spin_lock_irqsave(&ieee->lock, flags);
+ memcpy(ieee->current_network.bssid, temp->sa_data, ETH_ALEN);
+ ieee->wap_set = 0;
+ spin_unlock_irqrestore(&ieee->lock, flags);
+ ret = -1;
+ goto out;
+ }
+
+
+ if (ifup)
+ rtllib_stop_protocol(ieee, true);
+
+ /* just to avoid to give inconsistent infos in the
+ * get wx method. not really needed otherwise
+ */
+ spin_lock_irqsave(&ieee->lock, flags);
+
+ ieee->cannot_notify = false;
+ memcpy(ieee->current_network.bssid, temp->sa_data, ETH_ALEN);
+ ieee->wap_set = !is_zero_ether_addr(temp->sa_data);
+
+ spin_unlock_irqrestore(&ieee->lock, flags);
+
+ if (ifup)
+ rtllib_start_protocol(ieee);
+out:
+ up(&ieee->wx_sem);
+ return ret;
+}
+EXPORT_SYMBOL(rtllib_wx_set_wap);
+
+int rtllib_wx_get_essid(struct rtllib_device *ieee, struct iw_request_info *a,
+ union iwreq_data *wrqu, char *b)
+{
+ int len, ret = 0;
+ unsigned long flags;
+
+ if (ieee->iw_mode == IW_MODE_MONITOR)
+ return -1;
+
+ /* We want avoid to give to the user inconsistent infos*/
+ spin_lock_irqsave(&ieee->lock, flags);
+
+ if (ieee->current_network.ssid[0] == '\0' ||
+ ieee->current_network.ssid_len == 0) {
+ ret = -1;
+ goto out;
+ }
+
+ if (ieee->state != RTLLIB_LINKED &&
+ ieee->state != RTLLIB_LINKED_SCANNING &&
+ ieee->ssid_set == 0) {
+ ret = -1;
+ goto out;
+ }
+ len = ieee->current_network.ssid_len;
+ wrqu->essid.length = len;
+ strncpy(b, ieee->current_network.ssid, len);
+ wrqu->essid.flags = 1;
+
+out:
+ spin_unlock_irqrestore(&ieee->lock, flags);
+
+ return ret;
+
+}
+EXPORT_SYMBOL(rtllib_wx_get_essid);
+
+int rtllib_wx_set_rate(struct rtllib_device *ieee,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+
+ u32 target_rate = wrqu->bitrate.value;
+
+ ieee->rate = target_rate/100000;
+ return 0;
+}
+EXPORT_SYMBOL(rtllib_wx_set_rate);
+
+int rtllib_wx_get_rate(struct rtllib_device *ieee,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ u32 tmp_rate = 0;
+
+ tmp_rate = TxCountToDataRate(ieee,
+ ieee->softmac_stats.CurrentShowTxate);
+ wrqu->bitrate.value = tmp_rate * 500000;
+
+ return 0;
+}
+EXPORT_SYMBOL(rtllib_wx_get_rate);
+
+
+int rtllib_wx_set_rts(struct rtllib_device *ieee,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ if (wrqu->rts.disabled || !wrqu->rts.fixed)
+ ieee->rts = DEFAULT_RTS_THRESHOLD;
+ else {
+ if (wrqu->rts.value < MIN_RTS_THRESHOLD ||
+ wrqu->rts.value > MAX_RTS_THRESHOLD)
+ return -EINVAL;
+ ieee->rts = wrqu->rts.value;
+ }
+ return 0;
+}
+EXPORT_SYMBOL(rtllib_wx_set_rts);
+
+int rtllib_wx_get_rts(struct rtllib_device *ieee,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ wrqu->rts.value = ieee->rts;
+ wrqu->rts.fixed = 0; /* no auto select */
+ wrqu->rts.disabled = (wrqu->rts.value == DEFAULT_RTS_THRESHOLD);
+ return 0;
+}
+EXPORT_SYMBOL(rtllib_wx_get_rts);
+
+int rtllib_wx_set_mode(struct rtllib_device *ieee, struct iw_request_info *a,
+ union iwreq_data *wrqu, char *b)
+{
+ int set_mode_status = 0;
+
+ rtllib_stop_scan_syncro(ieee);
+ down(&ieee->wx_sem);
+ switch (wrqu->mode) {
+ case IW_MODE_MONITOR:
+ case IW_MODE_ADHOC:
+ case IW_MODE_INFRA:
+ break;
+ case IW_MODE_AUTO:
+ wrqu->mode = IW_MODE_INFRA;
+ break;
+ default:
+ set_mode_status = -EINVAL;
+ goto out;
+ }
+
+ if (wrqu->mode == ieee->iw_mode)
+ goto out;
+
+ if (wrqu->mode == IW_MODE_MONITOR) {
+ ieee->dev->type = ARPHRD_IEEE80211;
+ rtllib_EnableNetMonitorMode(ieee->dev, false);
+ } else {
+ ieee->dev->type = ARPHRD_ETHER;
+ if (ieee->iw_mode == IW_MODE_MONITOR)
+ rtllib_DisableNetMonitorMode(ieee->dev, false);
+ }
+
+ if (!ieee->proto_started) {
+ ieee->iw_mode = wrqu->mode;
+ } else {
+ rtllib_stop_protocol(ieee, true);
+ ieee->iw_mode = wrqu->mode;
+ rtllib_start_protocol(ieee);
+ }
+
+out:
+ up(&ieee->wx_sem);
+ return set_mode_status;
+}
+EXPORT_SYMBOL(rtllib_wx_set_mode);
+
+void rtllib_wx_sync_scan_wq(void *data)
+{
+ struct rtllib_device *ieee = container_of_work_rsl(data,
+ struct rtllib_device, wx_sync_scan_wq);
+ short chan;
+ enum ht_extchnl_offset chan_offset = 0;
+ enum ht_channel_width bandwidth = 0;
+ int b40M = 0;
+
+ if (!(ieee->softmac_features & IEEE_SOFTMAC_SCAN)) {
+ rtllib_start_scan_syncro(ieee, 0);
+ goto out;
+ }
+
+ chan = ieee->current_network.channel;
+
+ if (ieee->LeisurePSLeave)
+ ieee->LeisurePSLeave(ieee->dev);
+ /* notify AP to be in PS mode */
+ rtllib_sta_ps_send_null_frame(ieee, 1);
+ rtllib_sta_ps_send_null_frame(ieee, 1);
+
+ rtllib_stop_all_queues(ieee);
+
+ if (ieee->data_hard_stop)
+ ieee->data_hard_stop(ieee->dev);
+ rtllib_stop_send_beacons(ieee);
+ ieee->state = RTLLIB_LINKED_SCANNING;
+ ieee->link_change(ieee->dev);
+ /* wait for ps packet to be kicked out successfully */
+ msleep(50);
+
+ if (ieee->ScanOperationBackupHandler)
+ ieee->ScanOperationBackupHandler(ieee->dev, SCAN_OPT_BACKUP);
+
+ if (ieee->pHTInfo->bCurrentHTSupport && ieee->pHTInfo->bEnableHT &&
+ ieee->pHTInfo->bCurBW40MHz) {
+ b40M = 1;
+ chan_offset = ieee->pHTInfo->CurSTAExtChnlOffset;
+ bandwidth = (enum ht_channel_width)ieee->pHTInfo->bCurBW40MHz;
+ RT_TRACE(COMP_DBG, "Scan in 40M, force to 20M first:%d, %d\n",
+ chan_offset, bandwidth);
+ ieee->SetBWModeHandler(ieee->dev, HT_CHANNEL_WIDTH_20,
+ HT_EXTCHNL_OFFSET_NO_EXT);
+ }
+
+ rtllib_start_scan_syncro(ieee, 0);
+
+ if (b40M) {
+ RT_TRACE(COMP_DBG, "Scan in 20M, back to 40M\n");
+ if (chan_offset == HT_EXTCHNL_OFFSET_UPPER)
+ ieee->set_chan(ieee->dev, chan + 2);
+ else if (chan_offset == HT_EXTCHNL_OFFSET_LOWER)
+ ieee->set_chan(ieee->dev, chan - 2);
+ else
+ ieee->set_chan(ieee->dev, chan);
+ ieee->SetBWModeHandler(ieee->dev, bandwidth, chan_offset);
+ } else {
+ ieee->set_chan(ieee->dev, chan);
+ }
+
+ if (ieee->ScanOperationBackupHandler)
+ ieee->ScanOperationBackupHandler(ieee->dev, SCAN_OPT_RESTORE);
+
+ ieee->state = RTLLIB_LINKED;
+ ieee->link_change(ieee->dev);
+
+ /* Notify AP that I wake up again */
+ rtllib_sta_ps_send_null_frame(ieee, 0);
+
+ if (ieee->LinkDetectInfo.NumRecvBcnInPeriod == 0 ||
+ ieee->LinkDetectInfo.NumRecvDataInPeriod == 0) {
+ ieee->LinkDetectInfo.NumRecvBcnInPeriod = 1;
+ ieee->LinkDetectInfo.NumRecvDataInPeriod = 1;
+ }
+
+ if (ieee->data_hard_resume)
+ ieee->data_hard_resume(ieee->dev);
+
+ if (ieee->iw_mode == IW_MODE_ADHOC || ieee->iw_mode == IW_MODE_MASTER)
+ rtllib_start_send_beacons(ieee);
+
+ rtllib_wake_all_queues(ieee);
+
+out:
+ up(&ieee->wx_sem);
+
+}
+
+int rtllib_wx_set_scan(struct rtllib_device *ieee, struct iw_request_info *a,
+ union iwreq_data *wrqu, char *b)
+{
+ int ret = 0;
+
+ down(&ieee->wx_sem);
+
+ if (ieee->iw_mode == IW_MODE_MONITOR || !(ieee->proto_started)) {
+ ret = -1;
+ goto out;
+ }
+
+ if (ieee->state == RTLLIB_LINKED) {
+ queue_work_rsl(ieee->wq, &ieee->wx_sync_scan_wq);
+ /* intentionally forget to up sem */
+ return 0;
+ }
+
+out:
+ up(&ieee->wx_sem);
+ return ret;
+}
+EXPORT_SYMBOL(rtllib_wx_set_scan);
+
+int rtllib_wx_set_essid(struct rtllib_device *ieee,
+ struct iw_request_info *a,
+ union iwreq_data *wrqu, char *extra)
+{
+
+ int ret = 0, len, i;
+ short proto_started;
+ unsigned long flags;
+
+ rtllib_stop_scan_syncro(ieee);
+ down(&ieee->wx_sem);
+
+ proto_started = ieee->proto_started;
+
+ len = (wrqu->essid.length < IW_ESSID_MAX_SIZE) ? wrqu->essid.length :
+ IW_ESSID_MAX_SIZE;
+
+ if (len > IW_ESSID_MAX_SIZE) {
+ ret = -E2BIG;
+ goto out;
+ }
+
+ if (ieee->iw_mode == IW_MODE_MONITOR) {
+ ret = -1;
+ goto out;
+ }
+
+ for (i = 0; i < len; i++) {
+ if (extra[i] < 0) {
+ ret = -1;
+ goto out;
+ }
+ }
+
+ if (proto_started)
+ rtllib_stop_protocol(ieee, true);
+
+
+ /* this is just to be sure that the GET wx callback
+ * has consistent infos. not needed otherwise
+ */
+ spin_lock_irqsave(&ieee->lock, flags);
+
+ if (wrqu->essid.flags && wrqu->essid.length) {
+ strncpy(ieee->current_network.ssid, extra, len);
+ ieee->current_network.ssid_len = len;
+ ieee->cannot_notify = false;
+ ieee->ssid_set = 1;
+ } else {
+ ieee->ssid_set = 0;
+ ieee->current_network.ssid[0] = '\0';
+ ieee->current_network.ssid_len = 0;
+ }
+ spin_unlock_irqrestore(&ieee->lock, flags);
+
+ if (proto_started)
+ rtllib_start_protocol(ieee);
+out:
+ up(&ieee->wx_sem);
+ return ret;
+}
+EXPORT_SYMBOL(rtllib_wx_set_essid);
+
+int rtllib_wx_get_mode(struct rtllib_device *ieee, struct iw_request_info *a,
+ union iwreq_data *wrqu, char *b)
+{
+ wrqu->mode = ieee->iw_mode;
+ return 0;
+}
+EXPORT_SYMBOL(rtllib_wx_get_mode);
+
+int rtllib_wx_set_rawtx(struct rtllib_device *ieee,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+
+ int *parms = (int *)extra;
+ int enable = (parms[0] > 0);
+ short prev = ieee->raw_tx;
+
+ down(&ieee->wx_sem);
+
+ if (enable)
+ ieee->raw_tx = 1;
+ else
+ ieee->raw_tx = 0;
+
+ netdev_info(ieee->dev, "raw TX is %s\n",
+ ieee->raw_tx ? "enabled" : "disabled");
+
+ if (ieee->iw_mode == IW_MODE_MONITOR) {
+ if (prev == 0 && ieee->raw_tx) {
+ if (ieee->data_hard_resume)
+ ieee->data_hard_resume(ieee->dev);
+
+ netif_carrier_on(ieee->dev);
+ }
+
+ if (prev && ieee->raw_tx == 1)
+ netif_carrier_off(ieee->dev);
+ }
+
+ up(&ieee->wx_sem);
+
+ return 0;
+}
+EXPORT_SYMBOL(rtllib_wx_set_rawtx);
+
+int rtllib_wx_get_name(struct rtllib_device *ieee,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ strcpy(wrqu->name, "802.11");
+
+ if (ieee->modulation & RTLLIB_CCK_MODULATION)
+ strcat(wrqu->name, "b");
+ if (ieee->modulation & RTLLIB_OFDM_MODULATION)
+ strcat(wrqu->name, "g");
+ if (ieee->mode & (IEEE_N_24G | IEEE_N_5G))
+ strcat(wrqu->name, "n");
+ return 0;
+}
+EXPORT_SYMBOL(rtllib_wx_get_name);
+
+
+/* this is mostly stolen from hostap */
+int rtllib_wx_set_power(struct rtllib_device *ieee,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ int ret = 0;
+
+ if ((!ieee->sta_wake_up) ||
+ (!ieee->enter_sleep_state) ||
+ (!ieee->ps_is_queue_empty)) {
+ RTLLIB_DEBUG(RTLLIB_DL_ERR,
+ "%s(): PS mode is tried to be use but driver missed a callback\n\n",
+ __func__);
+ return -1;
+ }
+
+ down(&ieee->wx_sem);
+
+ if (wrqu->power.disabled) {
+ RT_TRACE(COMP_DBG, "===>%s(): power disable\n", __func__);
+ ieee->ps = RTLLIB_PS_DISABLED;
+ goto exit;
+ }
+ if (wrqu->power.flags & IW_POWER_TIMEOUT) {
+ ieee->ps_timeout = wrqu->power.value / 1000;
+ RT_TRACE(COMP_DBG, "===>%s():ps_timeout is %d\n", __func__,
+ ieee->ps_timeout);
+ }
+
+ if (wrqu->power.flags & IW_POWER_PERIOD)
+ ieee->ps_period = wrqu->power.value / 1000;
+
+ switch (wrqu->power.flags & IW_POWER_MODE) {
+ case IW_POWER_UNICAST_R:
+ ieee->ps = RTLLIB_PS_UNICAST;
+ break;
+ case IW_POWER_MULTICAST_R:
+ ieee->ps = RTLLIB_PS_MBCAST;
+ break;
+ case IW_POWER_ALL_R:
+ ieee->ps = RTLLIB_PS_UNICAST | RTLLIB_PS_MBCAST;
+ break;
+
+ case IW_POWER_ON:
+ break;
+
+ default:
+ ret = -EINVAL;
+ goto exit;
+
+ }
+exit:
+ up(&ieee->wx_sem);
+ return ret;
+
+}
+EXPORT_SYMBOL(rtllib_wx_set_power);
+
+/* this is stolen from hostap */
+int rtllib_wx_get_power(struct rtllib_device *ieee,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ down(&ieee->wx_sem);
+
+ if (ieee->ps == RTLLIB_PS_DISABLED) {
+ wrqu->power.disabled = 1;
+ goto exit;
+ }
+
+ wrqu->power.disabled = 0;
+
+ if ((wrqu->power.flags & IW_POWER_TYPE) == IW_POWER_TIMEOUT) {
+ wrqu->power.flags = IW_POWER_TIMEOUT;
+ wrqu->power.value = ieee->ps_timeout * 1000;
+ } else {
+ wrqu->power.flags = IW_POWER_PERIOD;
+ wrqu->power.value = ieee->ps_period * 1000;
+ }
+
+ if ((ieee->ps & (RTLLIB_PS_MBCAST | RTLLIB_PS_UNICAST)) ==
+ (RTLLIB_PS_MBCAST | RTLLIB_PS_UNICAST))
+ wrqu->power.flags |= IW_POWER_ALL_R;
+ else if (ieee->ps & RTLLIB_PS_MBCAST)
+ wrqu->power.flags |= IW_POWER_MULTICAST_R;
+ else
+ wrqu->power.flags |= IW_POWER_UNICAST_R;
+
+exit:
+ up(&ieee->wx_sem);
+ return 0;
+
+}
+EXPORT_SYMBOL(rtllib_wx_get_power);
diff --git a/drivers/staging/rtl8192e/rtllib_tx.c b/drivers/staging/rtl8192e/rtllib_tx.c
new file mode 100644
index 000000000..3b159638b
--- /dev/null
+++ b/drivers/staging/rtl8192e/rtllib_tx.c
@@ -0,0 +1,988 @@
+/******************************************************************************
+
+ Copyright(c) 2003 - 2004 Intel Corporation. All rights reserved.
+
+ This program is free software; you can redistribute it and/or modify it
+ under the terms of version 2 of the GNU General Public License as
+ published by the Free Software Foundation.
+
+ 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.
+
+ The full GNU General Public License is included in this distribution in the
+ file called LICENSE.
+
+ Contact Information:
+ James P. Ketrenos <ipw2100-admin@linux.intel.com>
+ Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+
+******************************************************************************
+
+ Few modifications for Realtek's Wi-Fi drivers by
+ Andrea Merello <andrea.merello@gmail.com>
+
+ A special thanks goes to Realtek for their support !
+
+******************************************************************************/
+
+#include <linux/compiler.h>
+#include <linux/errno.h>
+#include <linux/if_arp.h>
+#include <linux/in6.h>
+#include <linux/in.h>
+#include <linux/ip.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/netdevice.h>
+#include <linux/pci.h>
+#include <linux/proc_fs.h>
+#include <linux/skbuff.h>
+#include <linux/slab.h>
+#include <linux/tcp.h>
+#include <linux/types.h>
+#include <linux/wireless.h>
+#include <linux/etherdevice.h>
+#include <linux/uaccess.h>
+#include <linux/if_vlan.h>
+
+#include "rtllib.h"
+
+/* 802.11 Data Frame
+ *
+ *
+ * 802.11 frame_control for data frames - 2 bytes
+ * ,-----------------------------------------------------------------------------------------.
+ * bits | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | a | b | c | d | e |
+ * |----|-----|-----|-----|-----|-----|-----|-----|-----|-----|-----|-----|-----|-----|------|
+ * val | 0 | 0 | 0 | 1 | x | 0 | 0 | 0 | 1 | 0 | x | x | x | x | x |
+ * |----|-----|-----|-----|-----|-----|-----|-----|-----|-----|-----|-----|-----|-----|------|
+ * desc | ^-ver-^ | ^type-^ | ^-----subtype-----^ | to |from |more |retry| pwr |more |wep |
+ * | | | x=0 data,x=1 data+ack | DS | DS |frag | | mgm |data | |
+ * '-----------------------------------------------------------------------------------------'
+ * /\
+ * |
+ * 802.11 Data Frame |
+ * ,--------- 'ctrl' expands to >-----------'
+ * |
+ * ,--'---,-------------------------------------------------------------.
+ * Bytes | 2 | 2 | 6 | 6 | 6 | 2 | 0..2312 | 4 |
+ * |------|------|---------|---------|---------|------|---------|------|
+ * Desc. | ctrl | dura | DA/RA | TA | SA | Sequ | Frame | fcs |
+ * | | tion | (BSSID) | | | ence | data | |
+ * `--------------------------------------------------| |------'
+ * Total: 28 non-data bytes `----.----'
+ * |
+ * .- 'Frame data' expands to <---------------------------'
+ * |
+ * V
+ * ,---------------------------------------------------.
+ * Bytes | 1 | 1 | 1 | 3 | 2 | 0-2304 |
+ * |------|------|---------|----------|------|---------|
+ * Desc. | SNAP | SNAP | Control |Eth Tunnel| Type | IP |
+ * | DSAP | SSAP | | | | Packet |
+ * | 0xAA | 0xAA |0x03 (UI)|0x00-00-F8| | |
+ * `-----------------------------------------| |
+ * Total: 8 non-data bytes `----.----'
+ * |
+ * .- 'IP Packet' expands, if WEP enabled, to <--'
+ * |
+ * V
+ * ,-----------------------.
+ * Bytes | 4 | 0-2296 | 4 |
+ * |-----|-----------|-----|
+ * Desc. | IV | Encrypted | ICV |
+ * | | IP Packet | |
+ * `-----------------------'
+ * Total: 8 non-data bytes
+ *
+ *
+ * 802.3 Ethernet Data Frame
+ *
+ * ,-----------------------------------------.
+ * Bytes | 6 | 6 | 2 | Variable | 4 |
+ * |-------|-------|------|-----------|------|
+ * Desc. | Dest. | Source| Type | IP Packet | fcs |
+ * | MAC | MAC | | | |
+ * `-----------------------------------------'
+ * Total: 18 non-data bytes
+ *
+ * In the event that fragmentation is required, the incoming payload is split into
+ * N parts of size ieee->fts. The first fragment contains the SNAP header and the
+ * remaining packets are just data.
+ *
+ * If encryption is enabled, each fragment payload size is reduced by enough space
+ * to add the prefix and postfix (IV and ICV totalling 8 bytes in the case of WEP)
+ * So if you have 1500 bytes of payload with ieee->fts set to 500 without
+ * encryption it will take 3 frames. With WEP it will take 4 frames as the
+ * payload of each frame is reduced to 492 bytes.
+ *
+ * SKB visualization
+ *
+ * ,- skb->data
+ * |
+ * | ETHERNET HEADER ,-<-- PAYLOAD
+ * | | 14 bytes from skb->data
+ * | 2 bytes for Type --> ,T. | (sizeof ethhdr)
+ * | | | |
+ * |,-Dest.--. ,--Src.---. | | |
+ * | 6 bytes| | 6 bytes | | | |
+ * v | | | | | |
+ * 0 | v 1 | v | v 2
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
+ * ^ | ^ | ^ |
+ * | | | | | |
+ * | | | | `T' <---- 2 bytes for Type
+ * | | | |
+ * | | '---SNAP--' <-------- 6 bytes for SNAP
+ * | |
+ * `-IV--' <-------------------- 4 bytes for IV (WEP)
+ *
+ * SNAP HEADER
+ *
+ */
+
+static u8 P802_1H_OUI[P80211_OUI_LEN] = { 0x00, 0x00, 0xf8 };
+static u8 RFC1042_OUI[P80211_OUI_LEN] = { 0x00, 0x00, 0x00 };
+
+inline int rtllib_put_snap(u8 *data, u16 h_proto)
+{
+ struct rtllib_snap_hdr *snap;
+ u8 *oui;
+
+ snap = (struct rtllib_snap_hdr *)data;
+ snap->dsap = 0xaa;
+ snap->ssap = 0xaa;
+ snap->ctrl = 0x03;
+
+ if (h_proto == 0x8137 || h_proto == 0x80f3)
+ oui = P802_1H_OUI;
+ else
+ oui = RFC1042_OUI;
+ snap->oui[0] = oui[0];
+ snap->oui[1] = oui[1];
+ snap->oui[2] = oui[2];
+
+ *(__be16 *)(data + SNAP_SIZE) = htons(h_proto);
+
+ return SNAP_SIZE + sizeof(u16);
+}
+
+int rtllib_encrypt_fragment(struct rtllib_device *ieee, struct sk_buff *frag,
+ int hdr_len)
+{
+ struct lib80211_crypt_data *crypt = NULL;
+ int res;
+
+ crypt = ieee->crypt_info.crypt[ieee->crypt_info.tx_keyidx];
+
+ if (!(crypt && crypt->ops)) {
+ netdev_info(ieee->dev, "=========>%s(), crypt is null\n",
+ __func__);
+ return -1;
+ }
+ /* To encrypt, frame format is:
+ * IV (4 bytes), clear payload (including SNAP), ICV (4 bytes)
+ */
+
+ /* Host-based IEEE 802.11 fragmentation for TX is not yet supported, so
+ * call both MSDU and MPDU encryption functions from here.
+ */
+ atomic_inc(&crypt->refcnt);
+ res = 0;
+ if (crypt->ops->encrypt_msdu)
+ res = crypt->ops->encrypt_msdu(frag, hdr_len, crypt->priv);
+ if (res == 0 && crypt->ops->encrypt_mpdu)
+ res = crypt->ops->encrypt_mpdu(frag, hdr_len, crypt->priv);
+
+ atomic_dec(&crypt->refcnt);
+ if (res < 0) {
+ netdev_info(ieee->dev, "%s: Encryption failed: len=%d.\n",
+ ieee->dev->name, frag->len);
+ ieee->ieee_stats.tx_discards++;
+ return -1;
+ }
+
+ return 0;
+}
+
+
+void rtllib_txb_free(struct rtllib_txb *txb)
+{
+ if (unlikely(!txb))
+ return;
+ kfree(txb);
+}
+
+static struct rtllib_txb *rtllib_alloc_txb(int nr_frags, int txb_size,
+ gfp_t gfp_mask)
+{
+ struct rtllib_txb *txb;
+ int i;
+
+ txb = kmalloc(sizeof(struct rtllib_txb) + (sizeof(u8 *) * nr_frags),
+ gfp_mask);
+ if (!txb)
+ return NULL;
+
+ memset(txb, 0, sizeof(struct rtllib_txb));
+ txb->nr_frags = nr_frags;
+ txb->frag_size = cpu_to_le16(txb_size);
+
+ for (i = 0; i < nr_frags; i++) {
+ txb->fragments[i] = dev_alloc_skb(txb_size);
+ if (unlikely(!txb->fragments[i])) {
+ i--;
+ break;
+ }
+ memset(txb->fragments[i]->cb, 0, sizeof(txb->fragments[i]->cb));
+ }
+ if (unlikely(i != nr_frags)) {
+ while (i >= 0)
+ dev_kfree_skb_any(txb->fragments[i--]);
+ kfree(txb);
+ return NULL;
+ }
+ return txb;
+}
+
+static int rtllib_classify(struct sk_buff *skb, u8 bIsAmsdu)
+{
+ struct ethhdr *eth;
+ struct iphdr *ip;
+
+ eth = (struct ethhdr *)skb->data;
+ if (eth->h_proto != htons(ETH_P_IP))
+ return 0;
+
+ RTLLIB_DEBUG_DATA(RTLLIB_DL_DATA, skb->data, skb->len);
+ ip = ip_hdr(skb);
+ switch (ip->tos & 0xfc) {
+ case 0x20:
+ return 2;
+ case 0x40:
+ return 1;
+ case 0x60:
+ return 3;
+ case 0x80:
+ return 4;
+ case 0xa0:
+ return 5;
+ case 0xc0:
+ return 6;
+ case 0xe0:
+ return 7;
+ default:
+ return 0;
+ }
+}
+
+static void rtllib_tx_query_agg_cap(struct rtllib_device *ieee,
+ struct sk_buff *skb,
+ struct cb_desc *tcb_desc)
+{
+ struct rt_hi_throughput *pHTInfo = ieee->pHTInfo;
+ struct tx_ts_record *pTxTs = NULL;
+ struct rtllib_hdr_1addr *hdr = (struct rtllib_hdr_1addr *)skb->data;
+
+ if (rtllib_act_scanning(ieee, false))
+ return;
+
+ if (!pHTInfo->bCurrentHTSupport || !pHTInfo->bEnableHT)
+ return;
+ if (!IsQoSDataFrame(skb->data))
+ return;
+ if (is_multicast_ether_addr(hdr->addr1))
+ return;
+
+ if (tcb_desc->bdhcp || ieee->CntAfterLink < 2)
+ return;
+
+ if (pHTInfo->IOTAction & HT_IOT_ACT_TX_NO_AGGREGATION)
+ return;
+
+ if (!ieee->GetNmodeSupportBySecCfg(ieee->dev))
+ return;
+ if (pHTInfo->bCurrentAMPDUEnable) {
+ if (!GetTs(ieee, (struct ts_common_info **)(&pTxTs), hdr->addr1,
+ skb->priority, TX_DIR, true)) {
+ netdev_info(ieee->dev, "%s: can't get TS\n", __func__);
+ return;
+ }
+ if (pTxTs->TxAdmittedBARecord.bValid == false) {
+ if (ieee->wpa_ie_len && (ieee->pairwise_key_type ==
+ KEY_TYPE_NA)) {
+ ;
+ } else if (tcb_desc->bdhcp == 1) {
+ ;
+ } else if (!pTxTs->bDisable_AddBa) {
+ TsStartAddBaProcess(ieee, pTxTs);
+ }
+ goto FORCED_AGG_SETTING;
+ } else if (pTxTs->bUsingBa == false) {
+ if (SN_LESS(pTxTs->TxAdmittedBARecord.BaStartSeqCtrl.field.SeqNum,
+ (pTxTs->TxCurSeq+1)%4096))
+ pTxTs->bUsingBa = true;
+ else
+ goto FORCED_AGG_SETTING;
+ }
+ if (ieee->iw_mode == IW_MODE_INFRA) {
+ tcb_desc->bAMPDUEnable = true;
+ tcb_desc->ampdu_factor = pHTInfo->CurrentAMPDUFactor;
+ tcb_desc->ampdu_density = pHTInfo->CurrentMPDUDensity;
+ }
+ }
+FORCED_AGG_SETTING:
+ switch (pHTInfo->ForcedAMPDUMode) {
+ case HT_AGG_AUTO:
+ break;
+
+ case HT_AGG_FORCE_ENABLE:
+ tcb_desc->bAMPDUEnable = true;
+ tcb_desc->ampdu_density = pHTInfo->ForcedMPDUDensity;
+ tcb_desc->ampdu_factor = pHTInfo->ForcedAMPDUFactor;
+ break;
+
+ case HT_AGG_FORCE_DISABLE:
+ tcb_desc->bAMPDUEnable = false;
+ tcb_desc->ampdu_density = 0;
+ tcb_desc->ampdu_factor = 0;
+ break;
+ }
+}
+
+static void rtllib_qurey_ShortPreambleMode(struct rtllib_device *ieee,
+ struct cb_desc *tcb_desc)
+{
+ tcb_desc->bUseShortPreamble = false;
+ if (tcb_desc->data_rate == 2)
+ return;
+ else if (ieee->current_network.capability &
+ WLAN_CAPABILITY_SHORT_PREAMBLE)
+ tcb_desc->bUseShortPreamble = true;
+}
+
+static void rtllib_query_HTCapShortGI(struct rtllib_device *ieee,
+ struct cb_desc *tcb_desc)
+{
+ struct rt_hi_throughput *pHTInfo = ieee->pHTInfo;
+
+ tcb_desc->bUseShortGI = false;
+
+ if (!pHTInfo->bCurrentHTSupport || !pHTInfo->bEnableHT)
+ return;
+
+ if (pHTInfo->bForcedShortGI) {
+ tcb_desc->bUseShortGI = true;
+ return;
+ }
+
+ if ((pHTInfo->bCurBW40MHz == true) && pHTInfo->bCurShortGI40MHz)
+ tcb_desc->bUseShortGI = true;
+ else if ((pHTInfo->bCurBW40MHz == false) && pHTInfo->bCurShortGI20MHz)
+ tcb_desc->bUseShortGI = true;
+}
+
+static void rtllib_query_BandwidthMode(struct rtllib_device *ieee,
+ struct cb_desc *tcb_desc)
+{
+ struct rt_hi_throughput *pHTInfo = ieee->pHTInfo;
+
+ tcb_desc->bPacketBW = false;
+
+ if (!pHTInfo->bCurrentHTSupport || !pHTInfo->bEnableHT)
+ return;
+
+ if (tcb_desc->bMulticast || tcb_desc->bBroadcast)
+ return;
+
+ if ((tcb_desc->data_rate & 0x80) == 0)
+ return;
+ if (pHTInfo->bCurBW40MHz && pHTInfo->bCurTxBW40MHz &&
+ !ieee->bandwidth_auto_switch.bforced_tx20Mhz)
+ tcb_desc->bPacketBW = true;
+}
+
+static void rtllib_query_protectionmode(struct rtllib_device *ieee,
+ struct cb_desc *tcb_desc,
+ struct sk_buff *skb)
+{
+ struct rt_hi_throughput *pHTInfo;
+
+ tcb_desc->bRTSSTBC = false;
+ tcb_desc->bRTSUseShortGI = false;
+ tcb_desc->bCTSEnable = false;
+ tcb_desc->RTSSC = 0;
+ tcb_desc->bRTSBW = false;
+
+ if (tcb_desc->bBroadcast || tcb_desc->bMulticast)
+ return;
+
+ if (is_broadcast_ether_addr(skb->data+16))
+ return;
+
+ if (ieee->mode < IEEE_N_24G) {
+ if (skb->len > ieee->rts) {
+ tcb_desc->bRTSEnable = true;
+ tcb_desc->rts_rate = MGN_24M;
+ } else if (ieee->current_network.buseprotection) {
+ tcb_desc->bRTSEnable = true;
+ tcb_desc->bCTSEnable = true;
+ tcb_desc->rts_rate = MGN_24M;
+ }
+ return;
+ }
+
+ pHTInfo = ieee->pHTInfo;
+
+ while (true) {
+ if (pHTInfo->IOTAction & HT_IOT_ACT_FORCED_CTS2SELF) {
+ tcb_desc->bCTSEnable = true;
+ tcb_desc->rts_rate = MGN_24M;
+ tcb_desc->bRTSEnable = true;
+ break;
+ } else if (pHTInfo->IOTAction & (HT_IOT_ACT_FORCED_RTS |
+ HT_IOT_ACT_PURE_N_MODE)) {
+ tcb_desc->bRTSEnable = true;
+ tcb_desc->rts_rate = MGN_24M;
+ break;
+ }
+ if (ieee->current_network.buseprotection) {
+ tcb_desc->bRTSEnable = true;
+ tcb_desc->bCTSEnable = true;
+ tcb_desc->rts_rate = MGN_24M;
+ break;
+ }
+ if (pHTInfo->bCurrentHTSupport && pHTInfo->bEnableHT) {
+ u8 HTOpMode = pHTInfo->CurrentOpMode;
+
+ if ((pHTInfo->bCurBW40MHz && (HTOpMode == 2 ||
+ HTOpMode == 3)) ||
+ (!pHTInfo->bCurBW40MHz && HTOpMode == 3)) {
+ tcb_desc->rts_rate = MGN_24M;
+ tcb_desc->bRTSEnable = true;
+ break;
+ }
+ }
+ if (skb->len > ieee->rts) {
+ tcb_desc->rts_rate = MGN_24M;
+ tcb_desc->bRTSEnable = true;
+ break;
+ }
+ if (tcb_desc->bAMPDUEnable) {
+ tcb_desc->rts_rate = MGN_24M;
+ tcb_desc->bRTSEnable = false;
+ break;
+ }
+ goto NO_PROTECTION;
+ }
+ if (ieee->current_network.capability & WLAN_CAPABILITY_SHORT_PREAMBLE)
+ tcb_desc->bUseShortPreamble = true;
+ if (ieee->iw_mode == IW_MODE_MASTER)
+ goto NO_PROTECTION;
+ return;
+NO_PROTECTION:
+ tcb_desc->bRTSEnable = false;
+ tcb_desc->bCTSEnable = false;
+ tcb_desc->rts_rate = 0;
+ tcb_desc->RTSSC = 0;
+ tcb_desc->bRTSBW = false;
+}
+
+
+static void rtllib_txrate_selectmode(struct rtllib_device *ieee,
+ struct cb_desc *tcb_desc)
+{
+ if (ieee->bTxDisableRateFallBack)
+ tcb_desc->bTxDisableRateFallBack = true;
+
+ if (ieee->bTxUseDriverAssingedRate)
+ tcb_desc->bTxUseDriverAssingedRate = true;
+ if (!tcb_desc->bTxDisableRateFallBack ||
+ !tcb_desc->bTxUseDriverAssingedRate) {
+ if (ieee->iw_mode == IW_MODE_INFRA ||
+ ieee->iw_mode == IW_MODE_ADHOC)
+ tcb_desc->RATRIndex = 0;
+ }
+}
+
+u16 rtllib_query_seqnum(struct rtllib_device *ieee, struct sk_buff *skb,
+ u8 *dst)
+{
+ u16 seqnum = 0;
+
+ if (is_multicast_ether_addr(dst))
+ return 0;
+ if (IsQoSDataFrame(skb->data)) {
+ struct tx_ts_record *pTS = NULL;
+
+ if (!GetTs(ieee, (struct ts_common_info **)(&pTS), dst,
+ skb->priority, TX_DIR, true))
+ return 0;
+ seqnum = pTS->TxCurSeq;
+ pTS->TxCurSeq = (pTS->TxCurSeq+1)%4096;
+ return seqnum;
+ }
+ return 0;
+}
+
+static int wme_downgrade_ac(struct sk_buff *skb)
+{
+ switch (skb->priority) {
+ case 6:
+ case 7:
+ skb->priority = 5; /* VO -> VI */
+ return 0;
+ case 4:
+ case 5:
+ skb->priority = 3; /* VI -> BE */
+ return 0;
+ case 0:
+ case 3:
+ skb->priority = 1; /* BE -> BK */
+ return 0;
+ default:
+ return -1;
+ }
+}
+
+static u8 rtllib_current_rate(struct rtllib_device *ieee)
+{
+ if (ieee->mode & IEEE_MODE_MASK)
+ return ieee->rate;
+
+ if (ieee->HTCurrentOperaRate)
+ return ieee->HTCurrentOperaRate;
+ else
+ return ieee->rate & 0x7F;
+}
+
+int rtllib_xmit_inter(struct sk_buff *skb, struct net_device *dev)
+{
+ struct rtllib_device *ieee = (struct rtllib_device *)
+ netdev_priv_rsl(dev);
+ struct rtllib_txb *txb = NULL;
+ struct rtllib_hdr_3addrqos *frag_hdr;
+ int i, bytes_per_frag, nr_frags, bytes_last_frag, frag_size;
+ unsigned long flags;
+ struct net_device_stats *stats = &ieee->stats;
+ int ether_type = 0, encrypt;
+ int bytes, fc, qos_ctl = 0, hdr_len;
+ struct sk_buff *skb_frag;
+ struct rtllib_hdr_3addrqos header = { /* Ensure zero initialized */
+ .duration_id = 0,
+ .seq_ctl = 0,
+ .qos_ctl = 0
+ };
+ u8 dest[ETH_ALEN], src[ETH_ALEN];
+ int qos_actived = ieee->current_network.qos_data.active;
+ struct lib80211_crypt_data *crypt = NULL;
+ struct cb_desc *tcb_desc;
+ u8 bIsMulticast = false;
+ u8 IsAmsdu = false;
+ bool bdhcp = false;
+
+ spin_lock_irqsave(&ieee->lock, flags);
+
+ /* If there is no driver handler to take the TXB, don't bother
+ * creating it...
+ */
+ if ((!ieee->hard_start_xmit && !(ieee->softmac_features &
+ IEEE_SOFTMAC_TX_QUEUE)) ||
+ ((!ieee->softmac_data_hard_start_xmit &&
+ (ieee->softmac_features & IEEE_SOFTMAC_TX_QUEUE)))) {
+ netdev_warn(ieee->dev, "No xmit handler.\n");
+ goto success;
+ }
+
+
+ if (likely(ieee->raw_tx == 0)) {
+ if (unlikely(skb->len < SNAP_SIZE + sizeof(u16))) {
+ netdev_warn(ieee->dev, "skb too small (%d).\n",
+ skb->len);
+ goto success;
+ }
+ /* Save source and destination addresses */
+ memcpy(dest, skb->data, ETH_ALEN);
+ memcpy(src, skb->data+ETH_ALEN, ETH_ALEN);
+
+ memset(skb->cb, 0, sizeof(skb->cb));
+ ether_type = ntohs(((struct ethhdr *)skb->data)->h_proto);
+
+ if (ieee->iw_mode == IW_MODE_MONITOR) {
+ txb = rtllib_alloc_txb(1, skb->len, GFP_ATOMIC);
+ if (unlikely(!txb)) {
+ netdev_warn(ieee->dev,
+ "Could not allocate TXB\n");
+ goto failed;
+ }
+
+ txb->encrypted = 0;
+ txb->payload_size = cpu_to_le16(skb->len);
+ memcpy(skb_put(txb->fragments[0], skb->len), skb->data,
+ skb->len);
+
+ goto success;
+ }
+
+ if (skb->len > 282) {
+ if (ETH_P_IP == ether_type) {
+ const struct iphdr *ip = (struct iphdr *)
+ ((u8 *)skb->data+14);
+ if (IPPROTO_UDP == ip->protocol) {
+ struct udphdr *udp;
+
+ udp = (struct udphdr *)((u8 *)ip +
+ (ip->ihl << 2));
+ if (((((u8 *)udp)[1] == 68) &&
+ (((u8 *)udp)[3] == 67)) ||
+ ((((u8 *)udp)[1] == 67) &&
+ (((u8 *)udp)[3] == 68))) {
+ bdhcp = true;
+ ieee->LPSDelayCnt = 200;
+ }
+ }
+ } else if (ETH_P_ARP == ether_type) {
+ netdev_info(ieee->dev,
+ "=================>DHCP Protocol start tx ARP pkt!!\n");
+ bdhcp = true;
+ ieee->LPSDelayCnt =
+ ieee->current_network.tim.tim_count;
+ }
+ }
+
+ skb->priority = rtllib_classify(skb, IsAmsdu);
+ crypt = ieee->crypt_info.crypt[ieee->crypt_info.tx_keyidx];
+ encrypt = !(ether_type == ETH_P_PAE && ieee->ieee802_1x) &&
+ ieee->host_encrypt && crypt && crypt->ops;
+ if (!encrypt && ieee->ieee802_1x &&
+ ieee->drop_unencrypted && ether_type != ETH_P_PAE) {
+ stats->tx_dropped++;
+ goto success;
+ }
+ if (crypt && !encrypt && ether_type == ETH_P_PAE) {
+ struct eapol *eap = (struct eapol *)(skb->data +
+ sizeof(struct ethhdr) - SNAP_SIZE -
+ sizeof(u16));
+ RTLLIB_DEBUG_EAP("TX: IEEE 802.11 EAPOL frame: %s\n",
+ eap_get_type(eap->type));
+ }
+
+ /* Advance the SKB to the start of the payload */
+ skb_pull(skb, sizeof(struct ethhdr));
+
+ /* Determine total amount of storage required for TXB packets */
+ bytes = skb->len + SNAP_SIZE + sizeof(u16);
+
+ if (encrypt)
+ fc = RTLLIB_FTYPE_DATA | RTLLIB_FCTL_WEP;
+ else
+ fc = RTLLIB_FTYPE_DATA;
+
+ if (qos_actived)
+ fc |= RTLLIB_STYPE_QOS_DATA;
+ else
+ fc |= RTLLIB_STYPE_DATA;
+
+ if (ieee->iw_mode == IW_MODE_INFRA) {
+ fc |= RTLLIB_FCTL_TODS;
+ /* To DS: Addr1 = BSSID, Addr2 = SA,
+ * Addr3 = DA
+ */
+ memcpy(&header.addr1, ieee->current_network.bssid,
+ ETH_ALEN);
+ memcpy(&header.addr2, &src, ETH_ALEN);
+ if (IsAmsdu)
+ memcpy(&header.addr3,
+ ieee->current_network.bssid, ETH_ALEN);
+ else
+ memcpy(&header.addr3, &dest, ETH_ALEN);
+ } else if (ieee->iw_mode == IW_MODE_ADHOC) {
+ /* not From/To DS: Addr1 = DA, Addr2 = SA,
+ * Addr3 = BSSID
+ */
+ memcpy(&header.addr1, dest, ETH_ALEN);
+ memcpy(&header.addr2, src, ETH_ALEN);
+ memcpy(&header.addr3, ieee->current_network.bssid,
+ ETH_ALEN);
+ }
+
+ bIsMulticast = is_multicast_ether_addr(header.addr1);
+
+ header.frame_ctl = cpu_to_le16(fc);
+
+ /* Determine fragmentation size based on destination (multicast
+ * and broadcast are not fragmented)
+ */
+ if (bIsMulticast) {
+ frag_size = MAX_FRAG_THRESHOLD;
+ qos_ctl |= QOS_CTL_NOTCONTAIN_ACK;
+ } else {
+ frag_size = ieee->fts;
+ qos_ctl = 0;
+ }
+
+ if (qos_actived) {
+ hdr_len = RTLLIB_3ADDR_LEN + 2;
+
+ /* in case we are a client verify acm is not set for this ac */
+ while (unlikely(ieee->wmm_acm & (0x01 << skb->priority))) {
+ netdev_info(ieee->dev, "skb->priority = %x\n",
+ skb->priority);
+ if (wme_downgrade_ac(skb))
+ break;
+ netdev_info(ieee->dev, "converted skb->priority = %x\n",
+ skb->priority);
+ }
+ qos_ctl |= skb->priority;
+ header.qos_ctl = cpu_to_le16(qos_ctl & RTLLIB_QOS_TID);
+ } else {
+ hdr_len = RTLLIB_3ADDR_LEN;
+ }
+ /* Determine amount of payload per fragment. Regardless of if
+ * this stack is providing the full 802.11 header, one will
+ * eventually be affixed to this fragment -- so we must account
+ * for it when determining the amount of payload space.
+ */
+ bytes_per_frag = frag_size - hdr_len;
+ if (ieee->config &
+ (CFG_RTLLIB_COMPUTE_FCS | CFG_RTLLIB_RESERVE_FCS))
+ bytes_per_frag -= RTLLIB_FCS_LEN;
+
+ /* Each fragment may need to have room for encrypting
+ * pre/postfix
+ */
+ if (encrypt) {
+ bytes_per_frag -= crypt->ops->extra_mpdu_prefix_len +
+ crypt->ops->extra_mpdu_postfix_len +
+ crypt->ops->extra_msdu_prefix_len +
+ crypt->ops->extra_msdu_postfix_len;
+ }
+ /* Number of fragments is the total bytes_per_frag /
+ * payload_per_fragment
+ */
+ nr_frags = bytes / bytes_per_frag;
+ bytes_last_frag = bytes % bytes_per_frag;
+ if (bytes_last_frag)
+ nr_frags++;
+ else
+ bytes_last_frag = bytes_per_frag;
+
+ /* When we allocate the TXB we allocate enough space for the
+ * reserve and full fragment bytes (bytes_per_frag doesn't
+ * include prefix, postfix, header, FCS, etc.)
+ */
+ txb = rtllib_alloc_txb(nr_frags, frag_size +
+ ieee->tx_headroom, GFP_ATOMIC);
+ if (unlikely(!txb)) {
+ netdev_warn(ieee->dev, "Could not allocate TXB\n");
+ goto failed;
+ }
+ txb->encrypted = encrypt;
+ txb->payload_size = cpu_to_le16(bytes);
+
+ if (qos_actived)
+ txb->queue_index = UP2AC(skb->priority);
+ else
+ txb->queue_index = WME_AC_BE;
+
+ for (i = 0; i < nr_frags; i++) {
+ skb_frag = txb->fragments[i];
+ tcb_desc = (struct cb_desc *)(skb_frag->cb +
+ MAX_DEV_ADDR_SIZE);
+ if (qos_actived) {
+ skb_frag->priority = skb->priority;
+ tcb_desc->queue_index = UP2AC(skb->priority);
+ } else {
+ skb_frag->priority = WME_AC_BE;
+ tcb_desc->queue_index = WME_AC_BE;
+ }
+ skb_reserve(skb_frag, ieee->tx_headroom);
+
+ if (encrypt) {
+ if (ieee->hwsec_active)
+ tcb_desc->bHwSec = 1;
+ else
+ tcb_desc->bHwSec = 0;
+ skb_reserve(skb_frag,
+ crypt->ops->extra_mpdu_prefix_len +
+ crypt->ops->extra_msdu_prefix_len);
+ } else {
+ tcb_desc->bHwSec = 0;
+ }
+ frag_hdr = (struct rtllib_hdr_3addrqos *)
+ skb_put(skb_frag, hdr_len);
+ memcpy(frag_hdr, &header, hdr_len);
+
+ /* If this is not the last fragment, then add the
+ * MOREFRAGS bit to the frame control
+ */
+ if (i != nr_frags - 1) {
+ frag_hdr->frame_ctl = cpu_to_le16(
+ fc | RTLLIB_FCTL_MOREFRAGS);
+ bytes = bytes_per_frag;
+
+ } else {
+ /* The last fragment has the remaining length */
+ bytes = bytes_last_frag;
+ }
+ if ((qos_actived) && (!bIsMulticast)) {
+ frag_hdr->seq_ctl =
+ cpu_to_le16(rtllib_query_seqnum(ieee, skb_frag,
+ header.addr1));
+ frag_hdr->seq_ctl =
+ cpu_to_le16(le16_to_cpu(frag_hdr->seq_ctl)<<4 | i);
+ } else {
+ frag_hdr->seq_ctl =
+ cpu_to_le16(ieee->seq_ctrl[0]<<4 | i);
+ }
+ /* Put a SNAP header on the first fragment */
+ if (i == 0) {
+ rtllib_put_snap(
+ skb_put(skb_frag, SNAP_SIZE +
+ sizeof(u16)), ether_type);
+ bytes -= SNAP_SIZE + sizeof(u16);
+ }
+
+ memcpy(skb_put(skb_frag, bytes), skb->data, bytes);
+
+ /* Advance the SKB... */
+ skb_pull(skb, bytes);
+
+ /* Encryption routine will move the header forward in
+ * order to insert the IV between the header and the
+ * payload
+ */
+ if (encrypt)
+ rtllib_encrypt_fragment(ieee, skb_frag,
+ hdr_len);
+ if (ieee->config &
+ (CFG_RTLLIB_COMPUTE_FCS | CFG_RTLLIB_RESERVE_FCS))
+ skb_put(skb_frag, 4);
+ }
+
+ if ((qos_actived) && (!bIsMulticast)) {
+ if (ieee->seq_ctrl[UP2AC(skb->priority) + 1] == 0xFFF)
+ ieee->seq_ctrl[UP2AC(skb->priority) + 1] = 0;
+ else
+ ieee->seq_ctrl[UP2AC(skb->priority) + 1]++;
+ } else {
+ if (ieee->seq_ctrl[0] == 0xFFF)
+ ieee->seq_ctrl[0] = 0;
+ else
+ ieee->seq_ctrl[0]++;
+ }
+ } else {
+ if (unlikely(skb->len < sizeof(struct rtllib_hdr_3addr))) {
+ netdev_warn(ieee->dev, "skb too small (%d).\n",
+ skb->len);
+ goto success;
+ }
+
+ txb = rtllib_alloc_txb(1, skb->len, GFP_ATOMIC);
+ if (!txb) {
+ netdev_warn(ieee->dev, "Could not allocate TXB\n");
+ goto failed;
+ }
+
+ txb->encrypted = 0;
+ txb->payload_size = cpu_to_le16(skb->len);
+ memcpy(skb_put(txb->fragments[0], skb->len), skb->data,
+ skb->len);
+ }
+
+ success:
+ if (txb) {
+ struct cb_desc *tcb_desc = (struct cb_desc *)
+ (txb->fragments[0]->cb + MAX_DEV_ADDR_SIZE);
+ tcb_desc->bTxEnableFwCalcDur = 1;
+ tcb_desc->priority = skb->priority;
+
+ if (ether_type == ETH_P_PAE) {
+ if (ieee->pHTInfo->IOTAction &
+ HT_IOT_ACT_WA_IOT_Broadcom) {
+ tcb_desc->data_rate =
+ MgntQuery_TxRateExcludeCCKRates(ieee);
+ tcb_desc->bTxDisableRateFallBack = false;
+ } else {
+ tcb_desc->data_rate = ieee->basic_rate;
+ tcb_desc->bTxDisableRateFallBack = 1;
+ }
+
+
+ tcb_desc->RATRIndex = 7;
+ tcb_desc->bTxUseDriverAssingedRate = 1;
+ } else {
+ if (is_multicast_ether_addr(header.addr1))
+ tcb_desc->bMulticast = 1;
+ if (is_broadcast_ether_addr(header.addr1))
+ tcb_desc->bBroadcast = 1;
+ rtllib_txrate_selectmode(ieee, tcb_desc);
+ if (tcb_desc->bMulticast || tcb_desc->bBroadcast)
+ tcb_desc->data_rate = ieee->basic_rate;
+ else
+ tcb_desc->data_rate = rtllib_current_rate(ieee);
+
+ if (bdhcp) {
+ if (ieee->pHTInfo->IOTAction &
+ HT_IOT_ACT_WA_IOT_Broadcom) {
+ tcb_desc->data_rate =
+ MgntQuery_TxRateExcludeCCKRates(ieee);
+ tcb_desc->bTxDisableRateFallBack = false;
+ } else {
+ tcb_desc->data_rate = MGN_1M;
+ tcb_desc->bTxDisableRateFallBack = 1;
+ }
+
+
+ tcb_desc->RATRIndex = 7;
+ tcb_desc->bTxUseDriverAssingedRate = 1;
+ tcb_desc->bdhcp = 1;
+ }
+
+ rtllib_qurey_ShortPreambleMode(ieee, tcb_desc);
+ rtllib_tx_query_agg_cap(ieee, txb->fragments[0],
+ tcb_desc);
+ rtllib_query_HTCapShortGI(ieee, tcb_desc);
+ rtllib_query_BandwidthMode(ieee, tcb_desc);
+ rtllib_query_protectionmode(ieee, tcb_desc,
+ txb->fragments[0]);
+ }
+ }
+ spin_unlock_irqrestore(&ieee->lock, flags);
+ dev_kfree_skb_any(skb);
+ if (txb) {
+ if (ieee->softmac_features & IEEE_SOFTMAC_TX_QUEUE) {
+ dev->stats.tx_packets++;
+ dev->stats.tx_bytes += le16_to_cpu(txb->payload_size);
+ rtllib_softmac_xmit(txb, ieee);
+ } else {
+ if ((*ieee->hard_start_xmit)(txb, dev) == 0) {
+ stats->tx_packets++;
+ stats->tx_bytes += le16_to_cpu(txb->payload_size);
+ return 0;
+ }
+ rtllib_txb_free(txb);
+ }
+ }
+
+ return 0;
+
+ failed:
+ spin_unlock_irqrestore(&ieee->lock, flags);
+ netif_stop_queue(dev);
+ stats->tx_errors++;
+ return 1;
+
+}
+int rtllib_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+ memset(skb->cb, 0, sizeof(skb->cb));
+ return rtllib_xmit_inter(skb, dev);
+}
+EXPORT_SYMBOL(rtllib_xmit);
diff --git a/drivers/staging/rtl8192e/rtllib_wx.c b/drivers/staging/rtl8192e/rtllib_wx.c
new file mode 100644
index 000000000..6234aae5b
--- /dev/null
+++ b/drivers/staging/rtl8192e/rtllib_wx.c
@@ -0,0 +1,881 @@
+/******************************************************************************
+
+ Copyright(c) 2004 Intel Corporation. All rights reserved.
+
+ Portions of this file are based on the WEP enablement code provided by the
+ Host AP project hostap-drivers v0.1.3
+ Copyright (c) 2001-2002, SSH Communications Security Corp and Jouni Malinen
+ <jkmaline@cc.hut.fi>
+ Copyright (c) 2002-2003, Jouni Malinen <jkmaline@cc.hut.fi>
+
+ This program is free software; you can redistribute it and/or modify it
+ under the terms of version 2 of the GNU General Public License as
+ published by the Free Software Foundation.
+
+ 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.
+
+ The full GNU General Public License is included in this distribution in the
+ file called LICENSE.
+
+ Contact Information:
+ James P. Ketrenos <ipw2100-admin@linux.intel.com>
+ Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+
+******************************************************************************/
+#include <linux/wireless.h>
+#include <linux/kmod.h>
+#include <linux/module.h>
+#include <linux/etherdevice.h>
+#include "rtllib.h"
+struct modes_unit {
+ char *mode_string;
+ int mode_size;
+};
+static struct modes_unit rtllib_modes[] = {
+ {"a", 1},
+ {"b", 1},
+ {"g", 1},
+ {"?", 1},
+ {"N-24G", 5},
+ {"N-5G", 4},
+};
+
+#define MAX_CUSTOM_LEN 64
+static inline char *rtl819x_translate_scan(struct rtllib_device *ieee,
+ char *start, char *stop,
+ struct rtllib_network *network,
+ struct iw_request_info *info)
+{
+ char custom[MAX_CUSTOM_LEN];
+ char proto_name[IFNAMSIZ];
+ char *pname = proto_name;
+ char *p;
+ struct iw_event iwe;
+ int i, j;
+ u16 max_rate, rate;
+ static u8 EWC11NHTCap[] = {0x00, 0x90, 0x4c, 0x33};
+
+ /* First entry *MUST* be the AP MAC address */
+ iwe.cmd = SIOCGIWAP;
+ iwe.u.ap_addr.sa_family = ARPHRD_ETHER;
+ ether_addr_copy(iwe.u.ap_addr.sa_data, network->bssid);
+ start = iwe_stream_add_event_rsl(info, start, stop,
+ &iwe, IW_EV_ADDR_LEN);
+ /* Remaining entries will be displayed in the order we provide them */
+
+ /* Add the ESSID */
+ iwe.cmd = SIOCGIWESSID;
+ iwe.u.data.flags = 1;
+ if (network->ssid_len > 0) {
+ iwe.u.data.length = min_t(u8, network->ssid_len, 32);
+ start = iwe_stream_add_point_rsl(info, start, stop, &iwe,
+ network->ssid);
+ } else if (network->hidden_ssid_len == 0) {
+ iwe.u.data.length = sizeof("<hidden>");
+ start = iwe_stream_add_point_rsl(info, start, stop,
+ &iwe, "<hidden>");
+ } else {
+ iwe.u.data.length = min_t(u8, network->hidden_ssid_len, 32);
+ start = iwe_stream_add_point_rsl(info, start, stop, &iwe,
+ network->hidden_ssid);
+ }
+ /* Add the protocol name */
+ iwe.cmd = SIOCGIWNAME;
+ for (i = 0; i < ARRAY_SIZE(rtllib_modes); i++) {
+ if (network->mode&(1<<i)) {
+ sprintf(pname, rtllib_modes[i].mode_string,
+ rtllib_modes[i].mode_size);
+ pname += rtllib_modes[i].mode_size;
+ }
+ }
+ *pname = '\0';
+ snprintf(iwe.u.name, IFNAMSIZ, "IEEE802.11%s", proto_name);
+ start = iwe_stream_add_event_rsl(info, start, stop,
+ &iwe, IW_EV_CHAR_LEN);
+ /* Add mode */
+ iwe.cmd = SIOCGIWMODE;
+ if (network->capability &
+ (WLAN_CAPABILITY_ESS | WLAN_CAPABILITY_IBSS)) {
+ if (network->capability & WLAN_CAPABILITY_ESS)
+ iwe.u.mode = IW_MODE_MASTER;
+ else
+ iwe.u.mode = IW_MODE_ADHOC;
+ start = iwe_stream_add_event_rsl(info, start, stop,
+ &iwe, IW_EV_UINT_LEN);
+ }
+
+ /* Add frequency/channel */
+ iwe.cmd = SIOCGIWFREQ;
+ iwe.u.freq.m = network->channel;
+ iwe.u.freq.e = 0;
+ iwe.u.freq.i = 0;
+ start = iwe_stream_add_event_rsl(info, start, stop, &iwe,
+ IW_EV_FREQ_LEN);
+
+ /* Add encryption capability */
+ iwe.cmd = SIOCGIWENCODE;
+ if (network->capability & WLAN_CAPABILITY_PRIVACY)
+ iwe.u.data.flags = IW_ENCODE_ENABLED | IW_ENCODE_NOKEY;
+ else
+ iwe.u.data.flags = IW_ENCODE_DISABLED;
+ iwe.u.data.length = 0;
+ start = iwe_stream_add_point_rsl(info, start, stop,
+ &iwe, network->ssid);
+ /* Add basic and extended rates */
+ max_rate = 0;
+ p = custom;
+ p += snprintf(p, MAX_CUSTOM_LEN - (p - custom), " Rates (Mb/s): ");
+ for (i = 0, j = 0; i < network->rates_len;) {
+ if (j < network->rates_ex_len &&
+ ((network->rates_ex[j] & 0x7F) <
+ (network->rates[i] & 0x7F)))
+ rate = network->rates_ex[j++] & 0x7F;
+ else
+ rate = network->rates[i++] & 0x7F;
+ if (rate > max_rate)
+ max_rate = rate;
+ p += snprintf(p, MAX_CUSTOM_LEN - (p - custom),
+ "%d%s ", rate >> 1, (rate & 1) ? ".5" : "");
+ }
+ for (; j < network->rates_ex_len; j++) {
+ rate = network->rates_ex[j] & 0x7F;
+ p += snprintf(p, MAX_CUSTOM_LEN - (p - custom),
+ "%d%s ", rate >> 1, (rate & 1) ? ".5" : "");
+ if (rate > max_rate)
+ max_rate = rate;
+ }
+
+ if (network->mode >= IEEE_N_24G) {
+ struct ht_capab_ele *ht_cap = NULL;
+ bool is40M = false, isShortGI = false;
+ u8 max_mcs = 0;
+
+ if (!memcmp(network->bssht.bdHTCapBuf, EWC11NHTCap, 4))
+ ht_cap = (struct ht_capab_ele *)
+ &network->bssht.bdHTCapBuf[4];
+ else
+ ht_cap = (struct ht_capab_ele *)
+ &network->bssht.bdHTCapBuf[0];
+ is40M = (ht_cap->ChlWidth) ? 1 : 0;
+ isShortGI = (ht_cap->ChlWidth) ?
+ ((ht_cap->ShortGI40Mhz) ? 1 : 0) :
+ ((ht_cap->ShortGI20Mhz) ? 1 : 0);
+
+ max_mcs = HTGetHighestMCSRate(ieee, ht_cap->MCS,
+ MCS_FILTER_ALL);
+ rate = MCS_DATA_RATE[is40M][isShortGI][max_mcs & 0x7f];
+ if (rate > max_rate)
+ max_rate = rate;
+ }
+ iwe.cmd = SIOCGIWRATE;
+ iwe.u.bitrate.fixed = iwe.u.bitrate.disabled = 0;
+ iwe.u.bitrate.value = max_rate * 500000;
+ start = iwe_stream_add_event_rsl(info, start, stop, &iwe,
+ IW_EV_PARAM_LEN);
+ iwe.cmd = IWEVCUSTOM;
+ iwe.u.data.length = p - custom;
+ if (iwe.u.data.length)
+ start = iwe_stream_add_point_rsl(info, start, stop,
+ &iwe, custom);
+ /* Add quality statistics */
+ /* TODO: Fix these values... */
+ iwe.cmd = IWEVQUAL;
+ iwe.u.qual.qual = network->stats.signal;
+ iwe.u.qual.level = network->stats.rssi;
+ iwe.u.qual.noise = network->stats.noise;
+ iwe.u.qual.updated = network->stats.mask & RTLLIB_STATMASK_WEMASK;
+ if (!(network->stats.mask & RTLLIB_STATMASK_RSSI))
+ iwe.u.qual.updated |= IW_QUAL_LEVEL_INVALID;
+ if (!(network->stats.mask & RTLLIB_STATMASK_NOISE))
+ iwe.u.qual.updated |= IW_QUAL_NOISE_INVALID;
+ if (!(network->stats.mask & RTLLIB_STATMASK_SIGNAL))
+ iwe.u.qual.updated |= IW_QUAL_QUAL_INVALID;
+ iwe.u.qual.updated = 7;
+ start = iwe_stream_add_event_rsl(info, start, stop, &iwe,
+ IW_EV_QUAL_LEN);
+
+ iwe.cmd = IWEVCUSTOM;
+ p = custom;
+ iwe.u.data.length = p - custom;
+ if (iwe.u.data.length)
+ start = iwe_stream_add_point_rsl(info, start, stop,
+ &iwe, custom);
+
+ memset(&iwe, 0, sizeof(iwe));
+ if (network->wpa_ie_len) {
+ char buf[MAX_WPA_IE_LEN];
+
+ memcpy(buf, network->wpa_ie, network->wpa_ie_len);
+ iwe.cmd = IWEVGENIE;
+ iwe.u.data.length = network->wpa_ie_len;
+ start = iwe_stream_add_point_rsl(info, start, stop, &iwe, buf);
+ }
+ memset(&iwe, 0, sizeof(iwe));
+ if (network->rsn_ie_len) {
+ char buf[MAX_WPA_IE_LEN];
+
+ memcpy(buf, network->rsn_ie, network->rsn_ie_len);
+ iwe.cmd = IWEVGENIE;
+ iwe.u.data.length = network->rsn_ie_len;
+ start = iwe_stream_add_point_rsl(info, start, stop, &iwe, buf);
+ }
+
+ /* add info for WZC */
+ memset(&iwe, 0, sizeof(iwe));
+ if (network->wzc_ie_len) {
+ char buf[MAX_WZC_IE_LEN];
+
+ memcpy(buf, network->wzc_ie, network->wzc_ie_len);
+ iwe.cmd = IWEVGENIE;
+ iwe.u.data.length = network->wzc_ie_len;
+ start = iwe_stream_add_point_rsl(info, start, stop, &iwe, buf);
+ }
+
+ /* Add EXTRA: Age to display seconds since last beacon/probe response
+ * for given network.
+ */
+ iwe.cmd = IWEVCUSTOM;
+ p = custom;
+ p += snprintf(p, MAX_CUSTOM_LEN - (p - custom),
+ " Last beacon: %lums ago",
+ (jiffies - network->last_scanned) / (HZ / 100));
+ iwe.u.data.length = p - custom;
+ if (iwe.u.data.length)
+ start = iwe_stream_add_point_rsl(info, start, stop,
+ &iwe, custom);
+
+ return start;
+}
+
+int rtllib_wx_get_scan(struct rtllib_device *ieee,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct rtllib_network *network;
+ unsigned long flags;
+
+ char *ev = extra;
+ char *stop = ev + wrqu->data.length;
+ int i = 0;
+ int err = 0;
+
+ RTLLIB_DEBUG_WX("Getting scan\n");
+ down(&ieee->wx_sem);
+ spin_lock_irqsave(&ieee->lock, flags);
+
+ list_for_each_entry(network, &ieee->network_list, list) {
+ i++;
+ if ((stop - ev) < 200) {
+ err = -E2BIG;
+ break;
+ }
+ if (ieee->scan_age == 0 ||
+ time_after(network->last_scanned + ieee->scan_age, jiffies))
+ ev = rtl819x_translate_scan(ieee, ev, stop, network,
+ info);
+ else
+ RTLLIB_DEBUG_SCAN("Not showing network '%s ( %pM)' due to age (%lums).\n",
+ escape_essid(network->ssid,
+ network->ssid_len),
+ network->bssid,
+ (jiffies - network->last_scanned) / (HZ / 100));
+ }
+
+ spin_unlock_irqrestore(&ieee->lock, flags);
+ up(&ieee->wx_sem);
+ wrqu->data.length = ev - extra;
+ wrqu->data.flags = 0;
+
+ RTLLIB_DEBUG_WX("exit: %d networks returned.\n", i);
+
+ return err;
+}
+EXPORT_SYMBOL(rtllib_wx_get_scan);
+
+int rtllib_wx_set_encode(struct rtllib_device *ieee,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *keybuf)
+{
+ struct iw_point *erq = &(wrqu->encoding);
+ struct net_device *dev = ieee->dev;
+ struct rtllib_security sec = {
+ .flags = 0
+ };
+ int i, key, key_provided, len;
+ struct lib80211_crypt_data **crypt;
+
+ RTLLIB_DEBUG_WX("SET_ENCODE\n");
+
+ key = erq->flags & IW_ENCODE_INDEX;
+ if (key) {
+ if (key > NUM_WEP_KEYS)
+ return -EINVAL;
+ key--;
+ key_provided = 1;
+ } else {
+ key_provided = 0;
+ key = ieee->crypt_info.tx_keyidx;
+ }
+
+ RTLLIB_DEBUG_WX("Key: %d [%s]\n", key, key_provided ?
+ "provided" : "default");
+ crypt = &ieee->crypt_info.crypt[key];
+ if (erq->flags & IW_ENCODE_DISABLED) {
+ if (key_provided && *crypt) {
+ RTLLIB_DEBUG_WX("Disabling encryption on key %d.\n",
+ key);
+ lib80211_crypt_delayed_deinit(&ieee->crypt_info, crypt);
+ } else
+ RTLLIB_DEBUG_WX("Disabling encryption.\n");
+
+ /* Check all the keys to see if any are still configured,
+ * and if no key index was provided, de-init them all
+ */
+ for (i = 0; i < NUM_WEP_KEYS; i++) {
+ if (ieee->crypt_info.crypt[i] != NULL) {
+ if (key_provided)
+ break;
+ lib80211_crypt_delayed_deinit(&ieee->crypt_info,
+ &ieee->crypt_info.crypt[i]);
+ }
+ }
+
+ if (i == NUM_WEP_KEYS) {
+ sec.enabled = 0;
+ sec.level = SEC_LEVEL_0;
+ sec.flags |= SEC_ENABLED | SEC_LEVEL;
+ }
+
+ goto done;
+ }
+
+
+
+ sec.enabled = 1;
+ sec.flags |= SEC_ENABLED;
+
+ if (*crypt != NULL && (*crypt)->ops != NULL &&
+ strcmp((*crypt)->ops->name, "R-WEP") != 0) {
+ /* changing to use WEP; deinit previously used algorithm
+ * on this key
+ */
+ lib80211_crypt_delayed_deinit(&ieee->crypt_info, crypt);
+ }
+
+ if (*crypt == NULL) {
+ struct lib80211_crypt_data *new_crypt;
+
+ /* take WEP into use */
+ new_crypt = kzalloc(sizeof(struct lib80211_crypt_data),
+ GFP_KERNEL);
+ if (new_crypt == NULL)
+ return -ENOMEM;
+ new_crypt->ops = lib80211_get_crypto_ops("R-WEP");
+ if (!new_crypt->ops) {
+ request_module("rtllib_crypt_wep");
+ new_crypt->ops = lib80211_get_crypto_ops("R-WEP");
+ }
+
+ if (new_crypt->ops)
+ new_crypt->priv = new_crypt->ops->init(key);
+
+ if (!new_crypt->ops || !new_crypt->priv) {
+ kfree(new_crypt);
+ new_crypt = NULL;
+
+ netdev_warn(dev,
+ "%s: could not initialize WEP: load module rtllib_crypt_wep\n",
+ dev->name);
+ return -EOPNOTSUPP;
+ }
+ *crypt = new_crypt;
+ }
+
+ /* If a new key was provided, set it up */
+ if (erq->length > 0) {
+ len = erq->length <= 5 ? 5 : 13;
+ memcpy(sec.keys[key], keybuf, erq->length);
+ if (len > erq->length)
+ memset(sec.keys[key] + erq->length, 0,
+ len - erq->length);
+ RTLLIB_DEBUG_WX("Setting key %d to '%s' (%d:%d bytes)\n",
+ key, escape_essid(sec.keys[key], len),
+ erq->length, len);
+ sec.key_sizes[key] = len;
+ (*crypt)->ops->set_key(sec.keys[key], len, NULL,
+ (*crypt)->priv);
+ sec.flags |= (1 << key);
+ /* This ensures a key will be activated if no key is
+ * explicitly set
+ */
+ if (key == sec.active_key)
+ sec.flags |= SEC_ACTIVE_KEY;
+ ieee->crypt_info.tx_keyidx = key;
+
+ } else {
+ len = (*crypt)->ops->get_key(sec.keys[key], WEP_KEY_LEN,
+ NULL, (*crypt)->priv);
+ if (len == 0) {
+ /* Set a default key of all 0 */
+ netdev_info(ieee->dev, "Setting key %d to all zero.\n",
+ key);
+
+ memset(sec.keys[key], 0, 13);
+ (*crypt)->ops->set_key(sec.keys[key], 13, NULL,
+ (*crypt)->priv);
+ sec.key_sizes[key] = 13;
+ sec.flags |= (1 << key);
+ }
+
+ /* No key data - just set the default TX key index */
+ if (key_provided) {
+ RTLLIB_DEBUG_WX("Setting key %d to default Tx key.\n",
+ key);
+ ieee->crypt_info.tx_keyidx = key;
+ sec.active_key = key;
+ sec.flags |= SEC_ACTIVE_KEY;
+ }
+ }
+ done:
+ ieee->open_wep = !(erq->flags & IW_ENCODE_RESTRICTED);
+ ieee->auth_mode = ieee->open_wep ? WLAN_AUTH_OPEN :
+ WLAN_AUTH_SHARED_KEY;
+ sec.auth_mode = ieee->open_wep ? WLAN_AUTH_OPEN : WLAN_AUTH_SHARED_KEY;
+ sec.flags |= SEC_AUTH_MODE;
+ RTLLIB_DEBUG_WX("Auth: %s\n", sec.auth_mode == WLAN_AUTH_OPEN ?
+ "OPEN" : "SHARED KEY");
+
+ /* For now we just support WEP, so only set that security level...
+ * TODO: When WPA is added this is one place that needs to change
+ */
+ sec.flags |= SEC_LEVEL;
+ sec.level = SEC_LEVEL_1; /* 40 and 104 bit WEP */
+
+ if (ieee->set_security)
+ ieee->set_security(dev, &sec);
+
+ /* Do not reset port if card is in Managed mode since resetting will
+ * generate new IEEE 802.11 authentication which may end up in looping
+ * with IEEE 802.1X. If your hardware requires a reset after WEP
+ * configuration (for example... Prism2), implement the reset_port in
+ * the callbacks structures used to initialize the 802.11 stack.
+ */
+ if (ieee->reset_on_keychange &&
+ ieee->iw_mode != IW_MODE_INFRA &&
+ ieee->reset_port && ieee->reset_port(dev)) {
+ netdev_dbg(dev, "%s: reset_port failed\n", dev->name);
+ return -EINVAL;
+ }
+ return 0;
+}
+EXPORT_SYMBOL(rtllib_wx_set_encode);
+
+int rtllib_wx_get_encode(struct rtllib_device *ieee,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *keybuf)
+{
+ struct iw_point *erq = &(wrqu->encoding);
+ int len, key;
+ struct lib80211_crypt_data *crypt;
+
+ RTLLIB_DEBUG_WX("GET_ENCODE\n");
+
+ if (ieee->iw_mode == IW_MODE_MONITOR)
+ return -1;
+
+ key = erq->flags & IW_ENCODE_INDEX;
+ if (key) {
+ if (key > NUM_WEP_KEYS)
+ return -EINVAL;
+ key--;
+ } else {
+ key = ieee->crypt_info.tx_keyidx;
+ }
+ crypt = ieee->crypt_info.crypt[key];
+
+ erq->flags = key + 1;
+
+ if (crypt == NULL || crypt->ops == NULL) {
+ erq->length = 0;
+ erq->flags |= IW_ENCODE_DISABLED;
+ return 0;
+ }
+ len = crypt->ops->get_key(keybuf, SCM_KEY_LEN, NULL, crypt->priv);
+ erq->length = (len >= 0 ? len : 0);
+
+ erq->flags |= IW_ENCODE_ENABLED;
+
+ if (ieee->open_wep)
+ erq->flags |= IW_ENCODE_OPEN;
+ else
+ erq->flags |= IW_ENCODE_RESTRICTED;
+
+ return 0;
+}
+EXPORT_SYMBOL(rtllib_wx_get_encode);
+
+int rtllib_wx_set_encode_ext(struct rtllib_device *ieee,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ int ret = 0;
+ struct net_device *dev = ieee->dev;
+ struct iw_point *encoding = &wrqu->encoding;
+ struct iw_encode_ext *ext = (struct iw_encode_ext *)extra;
+ int i, idx;
+ int group_key = 0;
+ const char *alg, *module;
+ struct lib80211_crypto_ops *ops;
+ struct lib80211_crypt_data **crypt;
+
+ struct rtllib_security sec = {
+ .flags = 0,
+ };
+ idx = encoding->flags & IW_ENCODE_INDEX;
+ if (idx) {
+ if (idx < 1 || idx > NUM_WEP_KEYS)
+ return -EINVAL;
+ idx--;
+ } else{
+ idx = ieee->crypt_info.tx_keyidx;
+ }
+ if (ext->ext_flags & IW_ENCODE_EXT_GROUP_KEY) {
+ crypt = &ieee->crypt_info.crypt[idx];
+ group_key = 1;
+ } else {
+ /* some Cisco APs use idx>0 for unicast in dynamic WEP */
+ if (idx != 0 && ext->alg != IW_ENCODE_ALG_WEP)
+ return -EINVAL;
+ if (ieee->iw_mode == IW_MODE_INFRA)
+ crypt = &ieee->crypt_info.crypt[idx];
+ else
+ return -EINVAL;
+ }
+
+ sec.flags |= SEC_ENABLED;
+ if ((encoding->flags & IW_ENCODE_DISABLED) ||
+ ext->alg == IW_ENCODE_ALG_NONE) {
+ if (*crypt)
+ lib80211_crypt_delayed_deinit(&ieee->crypt_info, crypt);
+
+ for (i = 0; i < NUM_WEP_KEYS; i++) {
+ if (ieee->crypt_info.crypt[i] != NULL)
+ break;
+ }
+ if (i == NUM_WEP_KEYS) {
+ sec.enabled = 0;
+ sec.level = SEC_LEVEL_0;
+ sec.flags |= SEC_LEVEL;
+ }
+ goto done;
+ }
+
+ sec.enabled = 1;
+ switch (ext->alg) {
+ case IW_ENCODE_ALG_WEP:
+ alg = "R-WEP";
+ module = "rtllib_crypt_wep";
+ break;
+ case IW_ENCODE_ALG_TKIP:
+ alg = "R-TKIP";
+ module = "rtllib_crypt_tkip";
+ break;
+ case IW_ENCODE_ALG_CCMP:
+ alg = "R-CCMP";
+ module = "rtllib_crypt_ccmp";
+ break;
+ default:
+ RTLLIB_DEBUG_WX("%s: unknown crypto alg %d\n",
+ dev->name, ext->alg);
+ ret = -EINVAL;
+ goto done;
+ }
+ netdev_info(dev, "alg name:%s\n", alg);
+
+ ops = lib80211_get_crypto_ops(alg);
+ if (ops == NULL) {
+ char tempbuf[100];
+
+ memset(tempbuf, 0x00, 100);
+ sprintf(tempbuf, "%s", module);
+ request_module("%s", tempbuf);
+ ops = lib80211_get_crypto_ops(alg);
+ }
+ if (ops == NULL) {
+ netdev_info(dev, "========>unknown crypto alg %d\n", ext->alg);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ if (*crypt == NULL || (*crypt)->ops != ops) {
+ struct lib80211_crypt_data *new_crypt;
+
+ lib80211_crypt_delayed_deinit(&ieee->crypt_info, crypt);
+
+ new_crypt = kzalloc(sizeof(*new_crypt), GFP_KERNEL);
+ if (new_crypt == NULL) {
+ ret = -ENOMEM;
+ goto done;
+ }
+ new_crypt->ops = ops;
+ if (new_crypt->ops)
+ new_crypt->priv = new_crypt->ops->init(idx);
+
+ if (new_crypt->priv == NULL) {
+ kfree(new_crypt);
+ ret = -EINVAL;
+ goto done;
+ }
+ *crypt = new_crypt;
+
+ }
+
+ if (ext->key_len > 0 && (*crypt)->ops->set_key &&
+ (*crypt)->ops->set_key(ext->key, ext->key_len, ext->rx_seq,
+ (*crypt)->priv) < 0) {
+ netdev_info(dev, "key setting failed\n");
+ ret = -EINVAL;
+ goto done;
+ }
+ if (ext->ext_flags & IW_ENCODE_EXT_SET_TX_KEY) {
+ ieee->crypt_info.tx_keyidx = idx;
+ sec.active_key = idx;
+ sec.flags |= SEC_ACTIVE_KEY;
+ }
+ if (ext->alg != IW_ENCODE_ALG_NONE) {
+ sec.key_sizes[idx] = ext->key_len;
+ sec.flags |= (1 << idx);
+ if (ext->alg == IW_ENCODE_ALG_WEP) {
+ sec.flags |= SEC_LEVEL;
+ sec.level = SEC_LEVEL_1;
+ } else if (ext->alg == IW_ENCODE_ALG_TKIP) {
+ sec.flags |= SEC_LEVEL;
+ sec.level = SEC_LEVEL_2;
+ } else if (ext->alg == IW_ENCODE_ALG_CCMP) {
+ sec.flags |= SEC_LEVEL;
+ sec.level = SEC_LEVEL_3;
+ }
+ /* Don't set sec level for group keys. */
+ if (group_key)
+ sec.flags &= ~SEC_LEVEL;
+ }
+done:
+ if (ieee->set_security)
+ ieee->set_security(ieee->dev, &sec);
+
+ if (ieee->reset_on_keychange &&
+ ieee->iw_mode != IW_MODE_INFRA &&
+ ieee->reset_port && ieee->reset_port(dev)) {
+ RTLLIB_DEBUG_WX("%s: reset_port failed\n", dev->name);
+ return -EINVAL;
+ }
+ return ret;
+}
+EXPORT_SYMBOL(rtllib_wx_set_encode_ext);
+
+int rtllib_wx_get_encode_ext(struct rtllib_device *ieee,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct iw_point *encoding = &wrqu->encoding;
+ struct iw_encode_ext *ext = (struct iw_encode_ext *)extra;
+ struct lib80211_crypt_data *crypt;
+ int idx, max_key_len;
+
+ max_key_len = encoding->length - sizeof(*ext);
+ if (max_key_len < 0)
+ return -EINVAL;
+
+ idx = encoding->flags & IW_ENCODE_INDEX;
+ if (idx) {
+ if (idx < 1 || idx > NUM_WEP_KEYS)
+ return -EINVAL;
+ idx--;
+ } else {
+ idx = ieee->crypt_info.tx_keyidx;
+ }
+ if (!(ext->ext_flags & IW_ENCODE_EXT_GROUP_KEY) &&
+ (ext->alg != IW_ENCODE_ALG_WEP))
+ if (idx != 0 || (ieee->iw_mode != IW_MODE_INFRA))
+ return -EINVAL;
+
+ crypt = ieee->crypt_info.crypt[idx];
+
+ encoding->flags = idx + 1;
+ memset(ext, 0, sizeof(*ext));
+
+ if (crypt == NULL || crypt->ops == NULL) {
+ ext->alg = IW_ENCODE_ALG_NONE;
+ ext->key_len = 0;
+ encoding->flags |= IW_ENCODE_DISABLED;
+ } else {
+ if (strcmp(crypt->ops->name, "R-WEP") == 0)
+ ext->alg = IW_ENCODE_ALG_WEP;
+ else if (strcmp(crypt->ops->name, "R-TKIP"))
+ ext->alg = IW_ENCODE_ALG_TKIP;
+ else if (strcmp(crypt->ops->name, "R-CCMP"))
+ ext->alg = IW_ENCODE_ALG_CCMP;
+ else
+ return -EINVAL;
+ ext->key_len = crypt->ops->get_key(ext->key, SCM_KEY_LEN,
+ NULL, crypt->priv);
+ encoding->flags |= IW_ENCODE_ENABLED;
+ if (ext->key_len &&
+ (ext->alg == IW_ENCODE_ALG_TKIP ||
+ ext->alg == IW_ENCODE_ALG_CCMP))
+ ext->ext_flags |= IW_ENCODE_EXT_TX_SEQ_VALID;
+
+ }
+
+ return 0;
+}
+
+int rtllib_wx_set_mlme(struct rtllib_device *ieee,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ u8 i = 0;
+ bool deauth = false;
+ struct iw_mlme *mlme = (struct iw_mlme *) extra;
+
+ if (ieee->state != RTLLIB_LINKED)
+ return -ENOLINK;
+
+ down(&ieee->wx_sem);
+
+ switch (mlme->cmd) {
+ case IW_MLME_DEAUTH:
+ deauth = true;
+ /* leave break out intentionly */
+
+ case IW_MLME_DISASSOC:
+ if (deauth)
+ netdev_info(ieee->dev, "disauth packet !\n");
+ else
+ netdev_info(ieee->dev, "dis associate packet!\n");
+
+ ieee->cannot_notify = true;
+
+ SendDisassociation(ieee, deauth, mlme->reason_code);
+ rtllib_disassociate(ieee);
+
+ ieee->wap_set = 0;
+ for (i = 0; i < 6; i++)
+ ieee->current_network.bssid[i] = 0x55;
+
+ ieee->ssid_set = 0;
+ ieee->current_network.ssid[0] = '\0';
+ ieee->current_network.ssid_len = 0;
+ break;
+ default:
+ up(&ieee->wx_sem);
+ return -EOPNOTSUPP;
+ }
+
+ up(&ieee->wx_sem);
+
+ return 0;
+}
+EXPORT_SYMBOL(rtllib_wx_set_mlme);
+
+int rtllib_wx_set_auth(struct rtllib_device *ieee,
+ struct iw_request_info *info,
+ struct iw_param *data, char *extra)
+{
+ switch (data->flags & IW_AUTH_INDEX) {
+ case IW_AUTH_WPA_VERSION:
+ break;
+ case IW_AUTH_CIPHER_PAIRWISE:
+ case IW_AUTH_CIPHER_GROUP:
+ case IW_AUTH_KEY_MGMT:
+ /* Host AP driver does not use these parameters and allows
+ * wpa_supplicant to control them internally.
+ */
+ break;
+ case IW_AUTH_TKIP_COUNTERMEASURES:
+ ieee->tkip_countermeasures = data->value;
+ break;
+ case IW_AUTH_DROP_UNENCRYPTED:
+ ieee->drop_unencrypted = data->value;
+ break;
+
+ case IW_AUTH_80211_AUTH_ALG:
+ if (data->value & IW_AUTH_ALG_SHARED_KEY) {
+ ieee->open_wep = 0;
+ ieee->auth_mode = 1;
+ } else if (data->value & IW_AUTH_ALG_OPEN_SYSTEM) {
+ ieee->open_wep = 1;
+ ieee->auth_mode = 0;
+ } else if (data->value & IW_AUTH_ALG_LEAP) {
+ ieee->open_wep = 1;
+ ieee->auth_mode = 2;
+ } else
+ return -EINVAL;
+ break;
+
+ case IW_AUTH_WPA_ENABLED:
+ ieee->wpa_enabled = (data->value) ? 1 : 0;
+ break;
+
+ case IW_AUTH_RX_UNENCRYPTED_EAPOL:
+ ieee->ieee802_1x = data->value;
+ break;
+ case IW_AUTH_PRIVACY_INVOKED:
+ ieee->privacy_invoked = data->value;
+ break;
+ default:
+ return -EOPNOTSUPP;
+ }
+ return 0;
+}
+EXPORT_SYMBOL(rtllib_wx_set_auth);
+
+int rtllib_wx_set_gen_ie(struct rtllib_device *ieee, u8 *ie, size_t len)
+{
+ u8 *buf;
+ u8 eid, wps_oui[4] = {0x0, 0x50, 0xf2, 0x04};
+
+ if (len > MAX_WPA_IE_LEN || (len && ie == NULL))
+ return -EINVAL;
+
+ if (len) {
+ eid = ie[0];
+ if ((eid == MFIE_TYPE_GENERIC) && (!memcmp(&ie[2],
+ wps_oui, 4))) {
+
+ ieee->wps_ie_len = (len < MAX_WZC_IE_LEN) ? (len) :
+ (MAX_WZC_IE_LEN);
+ buf = kmemdup(ie, ieee->wps_ie_len, GFP_KERNEL);
+ if (buf == NULL)
+ return -ENOMEM;
+ ieee->wps_ie = buf;
+ return 0;
+ }
+ }
+ ieee->wps_ie_len = 0;
+ kfree(ieee->wps_ie);
+ ieee->wps_ie = NULL;
+ if (len) {
+ if (len != ie[1]+2)
+ return -EINVAL;
+ buf = kmemdup(ie, len, GFP_KERNEL);
+ if (buf == NULL)
+ return -ENOMEM;
+ kfree(ieee->wpa_ie);
+ ieee->wpa_ie = buf;
+ ieee->wpa_ie_len = len;
+ } else {
+ kfree(ieee->wpa_ie);
+ ieee->wpa_ie = NULL;
+ ieee->wpa_ie_len = 0;
+ }
+ return 0;
+}
+EXPORT_SYMBOL(rtllib_wx_set_gen_ie);
diff --git a/drivers/staging/rtl8192u/Kconfig b/drivers/staging/rtl8192u/Kconfig
new file mode 100644
index 000000000..3ee9d0d00
--- /dev/null
+++ b/drivers/staging/rtl8192u/Kconfig
@@ -0,0 +1,8 @@
+config RTL8192U
+ tristate "RealTek RTL8192U Wireless LAN NIC driver"
+ depends on PCI && WLAN && USB
+ depends on m
+ select WIRELESS_EXT
+ select WEXT_PRIV
+ select CRYPTO
+ ---help---
diff --git a/drivers/staging/rtl8192u/Makefile b/drivers/staging/rtl8192u/Makefile
new file mode 100644
index 000000000..703c1505e
--- /dev/null
+++ b/drivers/staging/rtl8192u/Makefile
@@ -0,0 +1,29 @@
+NIC_SELECT = RTL8192U
+
+ccflags-y := -std=gnu89
+ccflags-y += -O2
+
+ccflags-y += -DCONFIG_FORCE_HARD_FLOAT=y
+ccflags-y += -DJACKSON_NEW_8187 -DJACKSON_NEW_RX
+ccflags-y += -DTHOMAS_BEACON -DTHOMAS_TASKLET -DTHOMAS_SKB -DTHOMAS_TURBO
+ccflags-y += -Idrivers/staging/rtl8192u/ieee80211
+
+r8192u_usb-y := r8192U_core.o r8180_93cx6.o r8192U_wx.o \
+ r8190_rtl8256.o r819xU_phy.o r819xU_firmware.o \
+ r819xU_cmdpkt.o r8192U_dm.o r819xU_firmware_img.o \
+ ieee80211/ieee80211_crypt.o \
+ ieee80211/ieee80211_crypt_tkip.o \
+ ieee80211/ieee80211_crypt_ccmp.o \
+ ieee80211/ieee80211_crypt_wep.o \
+ ieee80211/ieee80211_rx.o \
+ ieee80211/ieee80211_softmac.o \
+ ieee80211/ieee80211_tx.o \
+ ieee80211/ieee80211_wx.o \
+ ieee80211/ieee80211_module.o \
+ ieee80211/ieee80211_softmac_wx.o \
+ ieee80211/rtl819x_HTProc.o \
+ ieee80211/rtl819x_TSProc.o \
+ ieee80211/rtl819x_BAProc.o \
+ ieee80211/dot11d.o
+
+obj-$(CONFIG_RTL8192U) += r8192u_usb.o
diff --git a/drivers/staging/rtl8192u/authors b/drivers/staging/rtl8192u/authors
new file mode 100644
index 000000000..0fab11228
--- /dev/null
+++ b/drivers/staging/rtl8192u/authors
@@ -0,0 +1 @@
+Andrea Merello <andrea.merello@gmail.com>
diff --git a/drivers/staging/rtl8192u/changes b/drivers/staging/rtl8192u/changes
new file mode 100644
index 000000000..0485d6eec
--- /dev/null
+++ b/drivers/staging/rtl8192u/changes
@@ -0,0 +1,4 @@
+v 0.1
+
+First version.
+This is based on the rtl8180-sa2400 pre-0.22-CVS code..
diff --git a/drivers/staging/rtl8192u/copying b/drivers/staging/rtl8192u/copying
new file mode 100644
index 000000000..e90dfed1a
--- /dev/null
+++ b/drivers/staging/rtl8192u/copying
@@ -0,0 +1,340 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+ 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) year name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Library General
+Public License instead of this License.
diff --git a/drivers/staging/rtl8192u/ieee80211/Makefile b/drivers/staging/rtl8192u/ieee80211/Makefile
new file mode 100644
index 000000000..b5d0c2eb0
--- /dev/null
+++ b/drivers/staging/rtl8192u/ieee80211/Makefile
@@ -0,0 +1,27 @@
+NIC_SELECT = RTL8192U
+
+ccflags-y := -I$(TOPDIR)/drivers/net/wireless
+ccflags-y += -O2
+ccflags-y += -DJACKSON_NEW_8187 -DJACKSON_NEW_RX
+
+ieee80211-rsl-objs := ieee80211_rx.o \
+ ieee80211_softmac.o \
+ ieee80211_tx.o \
+ ieee80211_wx.o \
+ ieee80211_module.o \
+ ieee80211_softmac_wx.o\
+ rtl819x_HTProc.o\
+ rtl819x_TSProc.o\
+ rtl819x_BAProc.o\
+ dot11d.o
+
+ieee80211_crypt-rsl-objs := ieee80211_crypt.o
+ieee80211_crypt_tkip-rsl-objs := ieee80211_crypt_tkip.o
+ieee80211_crypt_ccmp-rsl-objs := ieee80211_crypt_ccmp.o
+ieee80211_crypt_wep-rsl-objs := ieee80211_crypt_wep.o
+
+obj-m +=ieee80211-rsl.o
+obj-m +=ieee80211_crypt-rsl.o
+obj-m +=ieee80211_crypt_wep-rsl.o
+obj-m +=ieee80211_crypt_tkip-rsl.o
+obj-m +=ieee80211_crypt_ccmp-rsl.o
diff --git a/drivers/staging/rtl8192u/ieee80211/dot11d.c b/drivers/staging/rtl8192u/ieee80211/dot11d.c
new file mode 100644
index 000000000..82d60380b
--- /dev/null
+++ b/drivers/staging/rtl8192u/ieee80211/dot11d.c
@@ -0,0 +1,175 @@
+/* Implement 802.11d. */
+
+#include "dot11d.h"
+
+void Dot11d_Init(struct ieee80211_device *ieee)
+{
+ PRT_DOT11D_INFO pDot11dInfo = GET_DOT11D_INFO(ieee);
+
+ pDot11dInfo->bEnabled = false;
+
+ pDot11dInfo->State = DOT11D_STATE_NONE;
+ pDot11dInfo->CountryIeLen = 0;
+ memset(pDot11dInfo->channel_map, 0, MAX_CHANNEL_NUMBER+1);
+ memset(pDot11dInfo->MaxTxPwrDbmList, 0xFF, MAX_CHANNEL_NUMBER+1);
+ RESET_CIE_WATCHDOG(ieee);
+
+ netdev_info(ieee->dev, "Dot11d_Init()\n");
+}
+EXPORT_SYMBOL(Dot11d_Init);
+
+/* Reset to the state as we are just entering a regulatory domain. */
+void Dot11d_Reset(struct ieee80211_device *ieee)
+{
+ u32 i;
+ PRT_DOT11D_INFO pDot11dInfo = GET_DOT11D_INFO(ieee);
+ /* Clear old channel map */
+ memset(pDot11dInfo->channel_map, 0, MAX_CHANNEL_NUMBER+1);
+ memset(pDot11dInfo->MaxTxPwrDbmList, 0xFF, MAX_CHANNEL_NUMBER+1);
+ /* Set new channel map */
+ for (i = 1; i <= 11; i++)
+ (pDot11dInfo->channel_map)[i] = 1;
+
+ for (i = 12; i <= 14; i++)
+ (pDot11dInfo->channel_map)[i] = 2;
+
+ pDot11dInfo->State = DOT11D_STATE_NONE;
+ pDot11dInfo->CountryIeLen = 0;
+ RESET_CIE_WATCHDOG(ieee);
+}
+EXPORT_SYMBOL(Dot11d_Reset);
+
+/*
+ * Update country IE from Beacon or Probe Resopnse and configure PHY for
+ * operation in the regulatory domain.
+ *
+ * TODO: Configure Tx power.
+ * Assumption:
+ * 1. IS_DOT11D_ENABLE() is TRUE.
+ * 2. Input IE is an valid one.
+ */
+void Dot11d_UpdateCountryIe(struct ieee80211_device *dev, u8 *pTaddr,
+ u16 CoutryIeLen, u8 *pCoutryIe)
+{
+ PRT_DOT11D_INFO pDot11dInfo = GET_DOT11D_INFO(dev);
+ u8 i, j, NumTriples, MaxChnlNum;
+ PCHNL_TXPOWER_TRIPLE pTriple;
+
+ memset(pDot11dInfo->channel_map, 0, MAX_CHANNEL_NUMBER+1);
+ memset(pDot11dInfo->MaxTxPwrDbmList, 0xFF, MAX_CHANNEL_NUMBER+1);
+ MaxChnlNum = 0;
+ NumTriples = (CoutryIeLen - 3) / 3; /* skip 3-byte country string. */
+ pTriple = (PCHNL_TXPOWER_TRIPLE)(pCoutryIe + 3);
+ for (i = 0; i < NumTriples; i++) {
+ if (MaxChnlNum >= pTriple->FirstChnl) {
+ /* It is not in a monotonically increasing order, so
+ * stop processing.
+ */
+ netdev_err(dev->dev, "Dot11d_UpdateCountryIe(): Invalid country IE, skip it........1\n");
+ return;
+ }
+ if (MAX_CHANNEL_NUMBER < (pTriple->FirstChnl + pTriple->NumChnls)) {
+ /* It is not a valid set of channel id, so stop
+ * processing.
+ */
+ netdev_err(dev->dev, "Dot11d_UpdateCountryIe(): Invalid country IE, skip it........2\n");
+ return;
+ }
+
+ for (j = 0; j < pTriple->NumChnls; j++) {
+ pDot11dInfo->channel_map[pTriple->FirstChnl + j] = 1;
+ pDot11dInfo->MaxTxPwrDbmList[pTriple->FirstChnl + j] = pTriple->MaxTxPowerInDbm;
+ MaxChnlNum = pTriple->FirstChnl + j;
+ }
+
+ pTriple = (PCHNL_TXPOWER_TRIPLE)((u8 *)pTriple + 3);
+ }
+ netdev_info(dev->dev, "Channel List:");
+ for (i = 1; i <= MAX_CHANNEL_NUMBER; i++)
+ if (pDot11dInfo->channel_map[i] > 0)
+ netdev_info(dev->dev, " %d", i);
+ netdev_info(dev->dev, "\n");
+
+ UPDATE_CIE_SRC(dev, pTaddr);
+
+ pDot11dInfo->CountryIeLen = CoutryIeLen;
+ memcpy(pDot11dInfo->CountryIeBuf, pCoutryIe, CoutryIeLen);
+ pDot11dInfo->State = DOT11D_STATE_LEARNED;
+}
+EXPORT_SYMBOL(Dot11d_UpdateCountryIe);
+
+u8 DOT11D_GetMaxTxPwrInDbm(struct ieee80211_device *dev, u8 Channel)
+{
+ PRT_DOT11D_INFO pDot11dInfo = GET_DOT11D_INFO(dev);
+ u8 MaxTxPwrInDbm = 255;
+
+ if (MAX_CHANNEL_NUMBER < Channel) {
+ netdev_err(dev->dev, "DOT11D_GetMaxTxPwrInDbm(): Invalid Channel\n");
+ return MaxTxPwrInDbm;
+ }
+ if (pDot11dInfo->channel_map[Channel])
+ MaxTxPwrInDbm = pDot11dInfo->MaxTxPwrDbmList[Channel];
+
+ return MaxTxPwrInDbm;
+}
+EXPORT_SYMBOL(DOT11D_GetMaxTxPwrInDbm);
+
+void DOT11D_ScanComplete(struct ieee80211_device *dev)
+{
+ PRT_DOT11D_INFO pDot11dInfo = GET_DOT11D_INFO(dev);
+
+ switch (pDot11dInfo->State) {
+ case DOT11D_STATE_LEARNED:
+ pDot11dInfo->State = DOT11D_STATE_DONE;
+ break;
+
+ case DOT11D_STATE_DONE:
+ if (GET_CIE_WATCHDOG(dev) == 0) {
+ /* Reset country IE if previous one is gone. */
+ Dot11d_Reset(dev);
+ }
+ break;
+ case DOT11D_STATE_NONE:
+ break;
+ }
+}
+EXPORT_SYMBOL(DOT11D_ScanComplete);
+
+int IsLegalChannel(struct ieee80211_device *dev, u8 channel)
+{
+ PRT_DOT11D_INFO pDot11dInfo = GET_DOT11D_INFO(dev);
+
+ if (MAX_CHANNEL_NUMBER < channel) {
+ netdev_err(dev->dev, "IsLegalChannel(): Invalid Channel\n");
+ return 0;
+ }
+ if (pDot11dInfo->channel_map[channel] > 0)
+ return 1;
+ return 0;
+}
+EXPORT_SYMBOL(IsLegalChannel);
+
+int ToLegalChannel(struct ieee80211_device *dev, u8 channel)
+{
+ PRT_DOT11D_INFO pDot11dInfo = GET_DOT11D_INFO(dev);
+ u8 default_chn = 0;
+ u32 i = 0;
+
+ for (i = 1; i <= MAX_CHANNEL_NUMBER; i++) {
+ if (pDot11dInfo->channel_map[i] > 0) {
+ default_chn = i;
+ break;
+ }
+ }
+
+ if (MAX_CHANNEL_NUMBER < channel) {
+ netdev_err(dev->dev, "IsLegalChannel(): Invalid Channel\n");
+ return default_chn;
+ }
+
+ if (pDot11dInfo->channel_map[channel] > 0)
+ return channel;
+
+ return default_chn;
+}
+EXPORT_SYMBOL(ToLegalChannel);
diff --git a/drivers/staging/rtl8192u/ieee80211/dot11d.h b/drivers/staging/rtl8192u/ieee80211/dot11d.h
new file mode 100644
index 000000000..8ae673b21
--- /dev/null
+++ b/drivers/staging/rtl8192u/ieee80211/dot11d.h
@@ -0,0 +1,100 @@
+#ifndef __INC_DOT11D_H
+#define __INC_DOT11D_H
+
+#include "ieee80211.h"
+
+
+typedef struct _CHNL_TXPOWER_TRIPLE {
+ u8 FirstChnl;
+ u8 NumChnls;
+ u8 MaxTxPowerInDbm;
+} CHNL_TXPOWER_TRIPLE, *PCHNL_TXPOWER_TRIPLE;
+
+typedef enum _DOT11D_STATE {
+ DOT11D_STATE_NONE = 0,
+ DOT11D_STATE_LEARNED,
+ DOT11D_STATE_DONE,
+} DOT11D_STATE;
+
+typedef struct _RT_DOT11D_INFO {
+ /* DECLARE_RT_OBJECT(RT_DOT11D_INFO); */
+
+ bool bEnabled; /* dot11MultiDomainCapabilityEnabled */
+
+ u16 CountryIeLen; /* > 0 if CountryIeBuf[] contains valid country information element. */
+ u8 CountryIeBuf[MAX_IE_LEN];
+ u8 CountryIeSrcAddr[6]; /* Source AP of the country IE. */
+ u8 CountryIeWatchdog;
+
+ u8 channel_map[MAX_CHANNEL_NUMBER+1]; /* !Value 0: Invalid, 1: Valid (active scan), 2: Valid (passive scan) */
+ u8 MaxTxPwrDbmList[MAX_CHANNEL_NUMBER+1];
+
+ DOT11D_STATE State;
+} RT_DOT11D_INFO, *PRT_DOT11D_INFO;
+#define eqMacAddr(a, b) (((a)[0] == (b)[0] && \
+ (a)[1] == (b)[1] && (a)[2] == (b)[2] && (a)[3] == (b)[3] && \
+ (a)[4] == (b)[4] && (a)[5] == (b)[5]) ? 1 : 0)
+#define cpMacAddr(des, src) ((des)[0] = (src)[0], \
+ (des)[1] = (src)[1], (des)[2] = (src)[2], \
+ (des)[3] = (src)[3], (des)[4] = (src)[4], \
+ (des)[5] = (src)[5])
+#define GET_DOT11D_INFO(__pIeeeDev) ((PRT_DOT11D_INFO)((__pIeeeDev)->pDot11dInfo))
+
+#define IS_DOT11D_ENABLE(__pIeeeDev) (GET_DOT11D_INFO(__pIeeeDev)->bEnabled)
+#define IS_COUNTRY_IE_VALID(__pIeeeDev) (GET_DOT11D_INFO(__pIeeeDev)->CountryIeLen > 0)
+
+#define IS_EQUAL_CIE_SRC(__pIeeeDev, __pTa) eqMacAddr(GET_DOT11D_INFO(__pIeeeDev)->CountryIeSrcAddr, __pTa)
+#define UPDATE_CIE_SRC(__pIeeeDev, __pTa) cpMacAddr(GET_DOT11D_INFO(__pIeeeDev)->CountryIeSrcAddr, __pTa)
+
+#define IS_COUNTRY_IE_CHANGED(__pIeeeDev, __Ie) \
+ (((__Ie).Length == 0 || (__Ie).Length != GET_DOT11D_INFO(__pIeeeDev)->CountryIeLen) ? \
+ FALSE : \
+ (!memcmp(GET_DOT11D_INFO(__pIeeeDev)->CountryIeBuf, (__Ie).Octet, (__Ie).Length)))
+
+#define CIE_WATCHDOG_TH 1
+#define GET_CIE_WATCHDOG(__pIeeeDev) (GET_DOT11D_INFO(__pIeeeDev)->CountryIeWatchdog)
+#define RESET_CIE_WATCHDOG(__pIeeeDev) (GET_CIE_WATCHDOG(__pIeeeDev) = 0)
+#define UPDATE_CIE_WATCHDOG(__pIeeeDev) (++GET_CIE_WATCHDOG(__pIeeeDev))
+
+#define IS_DOT11D_STATE_DONE(__pIeeeDev) (GET_DOT11D_INFO(__pIeeeDev)->State == DOT11D_STATE_DONE)
+
+
+void
+Dot11d_Init(
+ struct ieee80211_device *dev
+ );
+
+void
+Dot11d_Reset(
+ struct ieee80211_device *dev
+ );
+
+void
+Dot11d_UpdateCountryIe(
+ struct ieee80211_device *dev,
+ u8 *pTaddr,
+ u16 CoutryIeLen,
+ u8 *pCoutryIe
+ );
+
+u8
+DOT11D_GetMaxTxPwrInDbm(
+ struct ieee80211_device *dev,
+ u8 Channel
+ );
+
+void
+DOT11D_ScanComplete(
+ struct ieee80211_device *dev
+ );
+
+int IsLegalChannel(
+ struct ieee80211_device *dev,
+ u8 channel
+);
+
+int ToLegalChannel(
+ struct ieee80211_device *dev,
+ u8 channel
+);
+#endif /* #ifndef __INC_DOT11D_H */
diff --git a/drivers/staging/rtl8192u/ieee80211/ieee80211.h b/drivers/staging/rtl8192u/ieee80211/ieee80211.h
new file mode 100644
index 000000000..0f53c6a97
--- /dev/null
+++ b/drivers/staging/rtl8192u/ieee80211/ieee80211.h
@@ -0,0 +1,2574 @@
+/*
+ * Merged with mainline ieee80211.h in Aug 2004. Original ieee802_11
+ * remains copyright by the original authors
+ *
+ * Portions of the merged code are based on Host AP (software wireless
+ * LAN access point) driver for Intersil Prism2/2.5/3.
+ *
+ * Copyright (c) 2001-2002, SSH Communications Security Corp and Jouni Malinen
+ * <jkmaline@cc.hut.fi>
+ * Copyright (c) 2002-2003, Jouni Malinen <jkmaline@cc.hut.fi>
+ *
+ * Adaption to a generic IEEE 802.11 stack by James Ketrenos
+ * <jketreno@linux.intel.com>
+ * Copyright (c) 2004, Intel Corporation
+ *
+ * Modified for Realtek's wi-fi cards by Andrea Merello
+ * <andrea.merello@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation. See README and COPYING for
+ * more details.
+ */
+#ifndef IEEE80211_H
+#define IEEE80211_H
+#include <linux/if_ether.h> /* ETH_ALEN */
+#include <linux/kernel.h> /* ARRAY_SIZE */
+#include <linux/module.h>
+#include <linux/jiffies.h>
+#include <linux/timer.h>
+#include <linux/sched.h>
+#include <linux/semaphore.h>
+#include <linux/interrupt.h>
+
+#include <linux/delay.h>
+#include <linux/wireless.h>
+
+#include "rtl819x_HT.h"
+#include "rtl819x_BA.h"
+#include "rtl819x_TS.h"
+
+
+#ifndef IW_MODE_MONITOR
+#define IW_MODE_MONITOR 6
+#endif
+
+#ifndef IWEVCUSTOM
+#define IWEVCUSTOM 0x8c02
+#endif
+
+
+#ifndef container_of
+/**
+ * container_of - cast a member of a structure out to the containing structure
+ *
+ * @ptr: the pointer to the member.
+ * @type: the type of the container struct this is embedded in.
+ * @member: the name of the member within the struct.
+ *
+ */
+#define container_of(ptr, type, member) ({ \
+ const typeof( ((type *)0)->member ) *__mptr = (ptr); \
+ (type *)( (char *)__mptr - offsetof(type,member) );})
+#endif
+
+#define KEY_TYPE_NA 0x0
+#define KEY_TYPE_WEP40 0x1
+#define KEY_TYPE_TKIP 0x2
+#define KEY_TYPE_CCMP 0x4
+#define KEY_TYPE_WEP104 0x5
+
+/* added for rtl819x tx procedure */
+#define MAX_QUEUE_SIZE 0x10
+
+//
+// 8190 queue mapping
+//
+#define BK_QUEUE 0
+#define BE_QUEUE 1
+#define VI_QUEUE 2
+#define VO_QUEUE 3
+#define HCCA_QUEUE 4
+#define TXCMD_QUEUE 5
+#define MGNT_QUEUE 6
+#define HIGH_QUEUE 7
+#define BEACON_QUEUE 8
+
+#define LOW_QUEUE BE_QUEUE
+#define NORMAL_QUEUE MGNT_QUEUE
+
+//added by amy for ps
+#define SWRF_TIMEOUT 50
+
+//added by amy for LEAP related
+#define IE_CISCO_FLAG_POSITION 0x08 // Flag byte: byte 8, numbered from 0.
+#define SUPPORT_CKIP_MIC 0x08 // bit3
+#define SUPPORT_CKIP_PK 0x10 // bit4
+/* defined for skb cb field */
+/* At most 28 byte */
+typedef struct cb_desc {
+ /* Tx Desc Related flags (8-9) */
+ u8 bLastIniPkt:1;
+ u8 bCmdOrInit:1;
+ u8 bFirstSeg:1;
+ u8 bLastSeg:1;
+ u8 bEncrypt:1;
+ u8 bTxDisableRateFallBack:1;
+ u8 bTxUseDriverAssingedRate:1;
+ u8 bHwSec:1; //indicate whether use Hw security. WB
+
+ u8 reserved1;
+
+ /* Tx Firmware Relaged flags (10-11)*/
+ u8 bCTSEnable:1;
+ u8 bRTSEnable:1;
+ u8 bUseShortGI:1;
+ u8 bUseShortPreamble:1;
+ u8 bTxEnableFwCalcDur:1;
+ u8 bAMPDUEnable:1;
+ u8 bRTSSTBC:1;
+ u8 RTSSC:1;
+
+ u8 bRTSBW:1;
+ u8 bPacketBW:1;
+ u8 bRTSUseShortPreamble:1;
+ u8 bRTSUseShortGI:1;
+ u8 bMulticast:1;
+ u8 bBroadcast:1;
+ //u8 reserved2:2;
+ u8 drv_agg_enable:1;
+ u8 reserved2:1;
+
+ /* Tx Desc related element(12-19) */
+ u8 rata_index;
+ u8 queue_index;
+ //u8 reserved3;
+ //u8 reserved4;
+ u16 txbuf_size;
+ //u8 reserved5;
+ u8 RATRIndex;
+ u8 reserved6;
+ u8 reserved7;
+ u8 reserved8;
+
+ /* Tx firmware related element(20-27) */
+ u8 data_rate;
+ u8 rts_rate;
+ u8 ampdu_factor;
+ u8 ampdu_density;
+ //u8 reserved9;
+ //u8 reserved10;
+ //u8 reserved11;
+ u8 DrvAggrNum;
+ u16 pkt_size;
+ u8 reserved12;
+}cb_desc, *pcb_desc;
+
+/*--------------------------Define -------------------------------------------*/
+#define MGN_1M 0x02
+#define MGN_2M 0x04
+#define MGN_5_5M 0x0b
+#define MGN_11M 0x16
+
+#define MGN_6M 0x0c
+#define MGN_9M 0x12
+#define MGN_12M 0x18
+#define MGN_18M 0x24
+#define MGN_24M 0x30
+#define MGN_36M 0x48
+#define MGN_48M 0x60
+#define MGN_54M 0x6c
+
+#define MGN_MCS0 0x80
+#define MGN_MCS1 0x81
+#define MGN_MCS2 0x82
+#define MGN_MCS3 0x83
+#define MGN_MCS4 0x84
+#define MGN_MCS5 0x85
+#define MGN_MCS6 0x86
+#define MGN_MCS7 0x87
+#define MGN_MCS8 0x88
+#define MGN_MCS9 0x89
+#define MGN_MCS10 0x8a
+#define MGN_MCS11 0x8b
+#define MGN_MCS12 0x8c
+#define MGN_MCS13 0x8d
+#define MGN_MCS14 0x8e
+#define MGN_MCS15 0x8f
+
+//----------------------------------------------------------------------------
+// 802.11 Management frame Reason Code field
+//----------------------------------------------------------------------------
+enum _ReasonCode{
+ unspec_reason = 0x1,
+ auth_not_valid = 0x2,
+ deauth_lv_ss = 0x3,
+ inactivity = 0x4,
+ ap_overload = 0x5,
+ class2_err = 0x6,
+ class3_err = 0x7,
+ disas_lv_ss = 0x8,
+ asoc_not_auth = 0x9,
+
+ //----MIC_CHECK
+ mic_failure = 0xe,
+ //----END MIC_CHECK
+
+ // Reason code defined in 802.11i D10.0 p.28.
+ invalid_IE = 0x0d,
+ four_way_tmout = 0x0f,
+ two_way_tmout = 0x10,
+ IE_dismatch = 0x11,
+ invalid_Gcipher = 0x12,
+ invalid_Pcipher = 0x13,
+ invalid_AKMP = 0x14,
+ unsup_RSNIEver = 0x15,
+ invalid_RSNIE = 0x16,
+ auth_802_1x_fail= 0x17,
+ ciper_reject = 0x18,
+
+ // Reason code defined in 7.3.1.7, 802.1e D13.0, p.42. Added by Annie, 2005-11-15.
+ QoS_unspec = 0x20, // 32
+ QAP_bandwidth = 0x21, // 33
+ poor_condition = 0x22, // 34
+ no_facility = 0x23, // 35
+ // Where is 36???
+ req_declined = 0x25, // 37
+ invalid_param = 0x26, // 38
+ req_not_honored= 0x27, // 39
+ TS_not_created = 0x2F, // 47
+ DL_not_allowed = 0x30, // 48
+ dest_not_exist = 0x31, // 49
+ dest_not_QSTA = 0x32, // 50
+};
+
+
+
+#define aSifsTime ((priv->ieee80211->current_network.mode == IEEE_A || \
+ priv->ieee80211->current_network.mode == IEEE_N_24G || \
+ priv->ieee80211->current_network.mode == IEEE_N_5G) ? \
+ 16 : 10)
+
+#define MGMT_QUEUE_NUM 5
+
+#define IEEE_CMD_SET_WPA_PARAM 1
+#define IEEE_CMD_SET_WPA_IE 2
+#define IEEE_CMD_SET_ENCRYPTION 3
+#define IEEE_CMD_MLME 4
+
+#define IEEE_PARAM_WPA_ENABLED 1
+#define IEEE_PARAM_TKIP_COUNTERMEASURES 2
+#define IEEE_PARAM_DROP_UNENCRYPTED 3
+#define IEEE_PARAM_PRIVACY_INVOKED 4
+#define IEEE_PARAM_AUTH_ALGS 5
+#define IEEE_PARAM_IEEE_802_1X 6
+//It should consistent with the driver_XXX.c
+// David, 2006.9.26
+#define IEEE_PARAM_WPAX_SELECT 7
+//Added for notify the encryption type selection
+// David, 2006.9.26
+#define IEEE_PROTO_WPA 1
+#define IEEE_PROTO_RSN 2
+//Added for notify the encryption type selection
+// David, 2006.9.26
+#define IEEE_WPAX_USEGROUP 0
+#define IEEE_WPAX_WEP40 1
+#define IEEE_WPAX_TKIP 2
+#define IEEE_WPAX_WRAP 3
+#define IEEE_WPAX_CCMP 4
+#define IEEE_WPAX_WEP104 5
+
+#define IEEE_KEY_MGMT_IEEE8021X 1
+#define IEEE_KEY_MGMT_PSK 2
+
+#define IEEE_MLME_STA_DEAUTH 1
+#define IEEE_MLME_STA_DISASSOC 2
+
+
+#define IEEE_CRYPT_ERR_UNKNOWN_ALG 2
+#define IEEE_CRYPT_ERR_UNKNOWN_ADDR 3
+#define IEEE_CRYPT_ERR_CRYPT_INIT_FAILED 4
+#define IEEE_CRYPT_ERR_KEY_SET_FAILED 5
+#define IEEE_CRYPT_ERR_TX_KEY_SET_FAILED 6
+#define IEEE_CRYPT_ERR_CARD_CONF_FAILED 7
+
+
+#define IEEE_CRYPT_ALG_NAME_LEN 16
+
+#define MAX_IE_LEN 0xff
+
+// added for kernel conflict
+#define ieee80211_crypt_deinit_entries ieee80211_crypt_deinit_entries_rsl
+#define ieee80211_crypt_deinit_handler ieee80211_crypt_deinit_handler_rsl
+#define ieee80211_crypt_delayed_deinit ieee80211_crypt_delayed_deinit_rsl
+#define ieee80211_register_crypto_ops ieee80211_register_crypto_ops_rsl
+#define ieee80211_unregister_crypto_ops ieee80211_unregister_crypto_ops_rsl
+#define ieee80211_get_crypto_ops ieee80211_get_crypto_ops_rsl
+
+#define ieee80211_ccmp_null ieee80211_ccmp_null_rsl
+
+#define ieee80211_tkip_null ieee80211_tkip_null_rsl
+
+#define ieee80211_wep_null ieee80211_wep_null_rsl
+
+#define free_ieee80211 free_ieee80211_rsl
+#define alloc_ieee80211 alloc_ieee80211_rsl
+
+#define ieee80211_rx ieee80211_rx_rsl
+#define ieee80211_rx_mgt ieee80211_rx_mgt_rsl
+
+#define ieee80211_get_beacon ieee80211_get_beacon_rsl
+#define ieee80211_wake_queue ieee80211_wake_queue_rsl
+#define ieee80211_stop_queue ieee80211_stop_queue_rsl
+#define ieee80211_reset_queue ieee80211_reset_queue_rsl
+#define ieee80211_softmac_stop_protocol ieee80211_softmac_stop_protocol_rsl
+#define ieee80211_softmac_start_protocol ieee80211_softmac_start_protocol_rsl
+#define ieee80211_is_shortslot ieee80211_is_shortslot_rsl
+#define ieee80211_is_54g ieee80211_is_54g_rsl
+#define ieee80211_wpa_supplicant_ioctl ieee80211_wpa_supplicant_ioctl_rsl
+#define ieee80211_ps_tx_ack ieee80211_ps_tx_ack_rsl
+#define ieee80211_softmac_xmit ieee80211_softmac_xmit_rsl
+#define ieee80211_stop_send_beacons ieee80211_stop_send_beacons_rsl
+#define notify_wx_assoc_event notify_wx_assoc_event_rsl
+#define SendDisassociation SendDisassociation_rsl
+#define ieee80211_disassociate ieee80211_disassociate_rsl
+#define ieee80211_start_send_beacons ieee80211_start_send_beacons_rsl
+#define ieee80211_stop_scan ieee80211_stop_scan_rsl
+#define ieee80211_send_probe_requests ieee80211_send_probe_requests_rsl
+#define ieee80211_softmac_scan_syncro ieee80211_softmac_scan_syncro_rsl
+#define ieee80211_start_scan_syncro ieee80211_start_scan_syncro_rsl
+
+#define ieee80211_wx_get_essid ieee80211_wx_get_essid_rsl
+#define ieee80211_wx_set_essid ieee80211_wx_set_essid_rsl
+#define ieee80211_wx_set_rate ieee80211_wx_set_rate_rsl
+#define ieee80211_wx_get_rate ieee80211_wx_get_rate_rsl
+#define ieee80211_wx_set_wap ieee80211_wx_set_wap_rsl
+#define ieee80211_wx_get_wap ieee80211_wx_get_wap_rsl
+#define ieee80211_wx_set_mode ieee80211_wx_set_mode_rsl
+#define ieee80211_wx_get_mode ieee80211_wx_get_mode_rsl
+#define ieee80211_wx_set_scan ieee80211_wx_set_scan_rsl
+#define ieee80211_wx_get_freq ieee80211_wx_get_freq_rsl
+#define ieee80211_wx_set_freq ieee80211_wx_set_freq_rsl
+#define ieee80211_wx_set_rawtx ieee80211_wx_set_rawtx_rsl
+#define ieee80211_wx_get_name ieee80211_wx_get_name_rsl
+#define ieee80211_wx_set_power ieee80211_wx_set_power_rsl
+#define ieee80211_wx_get_power ieee80211_wx_get_power_rsl
+#define ieee80211_wlan_frequencies ieee80211_wlan_frequencies_rsl
+#define ieee80211_wx_set_rts ieee80211_wx_set_rts_rsl
+#define ieee80211_wx_get_rts ieee80211_wx_get_rts_rsl
+
+#define ieee80211_txb_free ieee80211_txb_free_rsl
+
+#define ieee80211_wx_set_gen_ie ieee80211_wx_set_gen_ie_rsl
+#define ieee80211_wx_get_scan ieee80211_wx_get_scan_rsl
+#define ieee80211_wx_set_encode ieee80211_wx_set_encode_rsl
+#define ieee80211_wx_get_encode ieee80211_wx_get_encode_rsl
+#define ieee80211_wx_set_mlme ieee80211_wx_set_mlme_rsl
+#define ieee80211_wx_set_auth ieee80211_wx_set_auth_rsl
+#define ieee80211_wx_set_encode_ext ieee80211_wx_set_encode_ext_rsl
+#define ieee80211_wx_get_encode_ext ieee80211_wx_get_encode_ext_rsl
+
+
+typedef struct ieee_param {
+ u32 cmd;
+ u8 sta_addr[ETH_ALEN];
+ union {
+ struct {
+ u8 name;
+ u32 value;
+ } wpa_param;
+ struct {
+ u32 len;
+ u8 reserved[32];
+ u8 data[0];
+ } wpa_ie;
+ struct{
+ int command;
+ int reason_code;
+ } mlme;
+ struct {
+ u8 alg[IEEE_CRYPT_ALG_NAME_LEN];
+ u8 set_tx;
+ u32 err;
+ u8 idx;
+ u8 seq[8]; /* sequence counter (set: RX, get: TX) */
+ u16 key_len;
+ u8 key[0];
+ } crypt;
+ } u;
+}ieee_param;
+
+
+// linux under 2.6.9 release may not support it, so modify it for common use
+#define MSECS(t) msecs_to_jiffies(t)
+#define msleep_interruptible_rsl msleep_interruptible
+
+#define IEEE80211_DATA_LEN 2304
+/* Maximum size for the MA-UNITDATA primitive, 802.11 standard section
+ 6.2.1.1.2.
+
+ The figure in section 7.1.2 suggests a body size of up to 2312
+ bytes is allowed, which is a bit confusing, I suspect this
+ represents the 2304 bytes of real data, plus a possible 8 bytes of
+ WEP IV and ICV. (this interpretation suggested by Ramiro Barreiro) */
+#define IEEE80211_1ADDR_LEN 10
+#define IEEE80211_2ADDR_LEN 16
+#define IEEE80211_3ADDR_LEN 24
+#define IEEE80211_4ADDR_LEN 30
+#define IEEE80211_FCS_LEN 4
+#define IEEE80211_HLEN (IEEE80211_4ADDR_LEN)
+#define IEEE80211_FRAME_LEN (IEEE80211_DATA_LEN + IEEE80211_HLEN)
+#define IEEE80211_MGMT_HDR_LEN 24
+#define IEEE80211_DATA_HDR3_LEN 24
+#define IEEE80211_DATA_HDR4_LEN 30
+
+#define MIN_FRAG_THRESHOLD 256U
+#define MAX_FRAG_THRESHOLD 2346U
+
+
+/* Frame control field constants */
+#define IEEE80211_FCTL_VERS 0x0003
+#define IEEE80211_FCTL_FTYPE 0x000c
+#define IEEE80211_FCTL_STYPE 0x00f0
+#define IEEE80211_FCTL_FRAMETYPE 0x00fc
+#define IEEE80211_FCTL_TODS 0x0100
+#define IEEE80211_FCTL_FROMDS 0x0200
+#define IEEE80211_FCTL_DSTODS 0x0300 //added by david
+#define IEEE80211_FCTL_MOREFRAGS 0x0400
+#define IEEE80211_FCTL_RETRY 0x0800
+#define IEEE80211_FCTL_PM 0x1000
+#define IEEE80211_FCTL_MOREDATA 0x2000
+#define IEEE80211_FCTL_WEP 0x4000
+#define IEEE80211_FCTL_ORDER 0x8000
+
+#define IEEE80211_FTYPE_MGMT 0x0000
+#define IEEE80211_FTYPE_CTL 0x0004
+#define IEEE80211_FTYPE_DATA 0x0008
+
+/* management */
+#define IEEE80211_STYPE_ASSOC_REQ 0x0000
+#define IEEE80211_STYPE_ASSOC_RESP 0x0010
+#define IEEE80211_STYPE_REASSOC_REQ 0x0020
+#define IEEE80211_STYPE_REASSOC_RESP 0x0030
+#define IEEE80211_STYPE_PROBE_REQ 0x0040
+#define IEEE80211_STYPE_PROBE_RESP 0x0050
+#define IEEE80211_STYPE_BEACON 0x0080
+#define IEEE80211_STYPE_ATIM 0x0090
+#define IEEE80211_STYPE_DISASSOC 0x00A0
+#define IEEE80211_STYPE_AUTH 0x00B0
+#define IEEE80211_STYPE_DEAUTH 0x00C0
+#define IEEE80211_STYPE_MANAGE_ACT 0x00D0
+
+/* control */
+#define IEEE80211_STYPE_PSPOLL 0x00A0
+#define IEEE80211_STYPE_RTS 0x00B0
+#define IEEE80211_STYPE_CTS 0x00C0
+#define IEEE80211_STYPE_ACK 0x00D0
+#define IEEE80211_STYPE_CFEND 0x00E0
+#define IEEE80211_STYPE_CFENDACK 0x00F0
+#define IEEE80211_STYPE_BLOCKACK 0x0094
+
+/* data */
+#define IEEE80211_STYPE_DATA 0x0000
+#define IEEE80211_STYPE_DATA_CFACK 0x0010
+#define IEEE80211_STYPE_DATA_CFPOLL 0x0020
+#define IEEE80211_STYPE_DATA_CFACKPOLL 0x0030
+#define IEEE80211_STYPE_NULLFUNC 0x0040
+#define IEEE80211_STYPE_CFACK 0x0050
+#define IEEE80211_STYPE_CFPOLL 0x0060
+#define IEEE80211_STYPE_CFACKPOLL 0x0070
+#define IEEE80211_STYPE_QOS_DATA 0x0080 //added for WMM 2006/8/2
+#define IEEE80211_STYPE_QOS_NULL 0x00C0
+
+#define IEEE80211_SCTL_FRAG 0x000F
+#define IEEE80211_SCTL_SEQ 0xFFF0
+
+/* QOS control */
+#define IEEE80211_QCTL_TID 0x000F
+
+#define FC_QOS_BIT BIT7
+#define IsDataFrame(pdu) ( ((pdu[0] & 0x0C)==0x08) ? true : false )
+#define IsLegacyDataFrame(pdu) (IsDataFrame(pdu) && (!(pdu[0]&FC_QOS_BIT)) )
+//added by wb. Is this right?
+#define IsQoSDataFrame(pframe) ((*(u16 *)pframe&(IEEE80211_STYPE_QOS_DATA|IEEE80211_FTYPE_DATA)) == (IEEE80211_STYPE_QOS_DATA|IEEE80211_FTYPE_DATA))
+#define Frame_Order(pframe) (*(u16 *)pframe&IEEE80211_FCTL_ORDER)
+#define SN_LESS(a, b) (((a-b)&0x800)!=0)
+#define SN_EQUAL(a, b) (a == b)
+#define MAX_DEV_ADDR_SIZE 8
+typedef enum _ACT_CATEGORY{
+ ACT_CAT_QOS = 1,
+ ACT_CAT_DLS = 2,
+ ACT_CAT_BA = 3,
+ ACT_CAT_HT = 7,
+ ACT_CAT_WMM = 17,
+} ACT_CATEGORY, *PACT_CATEGORY;
+
+typedef enum _TS_ACTION{
+ ACT_ADDTSREQ = 0,
+ ACT_ADDTSRSP = 1,
+ ACT_DELTS = 2,
+ ACT_SCHEDULE = 3,
+} TS_ACTION, *PTS_ACTION;
+
+typedef enum _BA_ACTION{
+ ACT_ADDBAREQ = 0,
+ ACT_ADDBARSP = 1,
+ ACT_DELBA = 2,
+} BA_ACTION, *PBA_ACTION;
+
+typedef enum _InitialGainOpType{
+ IG_Backup=0,
+ IG_Restore,
+ IG_Max
+}InitialGainOpType;
+
+/* debug macros */
+#define CONFIG_IEEE80211_DEBUG
+#ifdef CONFIG_IEEE80211_DEBUG
+extern u32 ieee80211_debug_level;
+#define IEEE80211_DEBUG(level, fmt, args...) \
+do { if (ieee80211_debug_level & (level)) \
+ printk(KERN_DEBUG "ieee80211: " fmt, ## args); } while (0)
+//wb added to debug out data buf
+//if you want print DATA buffer related BA, please set ieee80211_debug_level to DATA|BA
+#define IEEE80211_DEBUG_DATA(level, data, datalen) \
+ do{ if ((ieee80211_debug_level & (level)) == (level)) \
+ { \
+ int i; \
+ u8 *pdata = (u8 *) data; \
+ printk(KERN_DEBUG "ieee80211: %s()\n", __func__); \
+ for(i=0; i<(int)(datalen); i++) \
+ { \
+ printk("%2x ", pdata[i]); \
+ if ((i+1)%16 == 0) printk("\n"); \
+ } \
+ printk("\n"); \
+ } \
+ } while (0)
+#else
+#define IEEE80211_DEBUG(level, fmt, args...) do {} while (0)
+#define IEEE80211_DEBUG_DATA(level, data, datalen) do {} while(0)
+#endif /* CONFIG_IEEE80211_DEBUG */
+
+/* debug macros not dependent on CONFIG_IEEE80211_DEBUG */
+
+/*
+ * To use the debug system;
+ *
+ * If you are defining a new debug classification, simply add it to the #define
+ * list here in the form of:
+ *
+ * #define IEEE80211_DL_xxxx VALUE
+ *
+ * shifting value to the left one bit from the previous entry. xxxx should be
+ * the name of the classification (for example, WEP)
+ *
+ * You then need to either add a IEEE80211_xxxx_DEBUG() macro definition for your
+ * classification, or use IEEE80211_DEBUG(IEEE80211_DL_xxxx, ...) whenever you want
+ * to send output to that classification.
+ *
+ * To add your debug level to the list of levels seen when you perform
+ *
+ * % cat /proc/net/ipw/debug_level
+ *
+ * you simply need to add your entry to the ipw_debug_levels array.
+ *
+ * If you do not see debug_level in /proc/net/ipw then you do not have
+ * CONFIG_IEEE80211_DEBUG defined in your kernel configuration
+ *
+ */
+
+#define IEEE80211_DL_INFO (1<<0)
+#define IEEE80211_DL_WX (1<<1)
+#define IEEE80211_DL_SCAN (1<<2)
+#define IEEE80211_DL_STATE (1<<3)
+#define IEEE80211_DL_MGMT (1<<4)
+#define IEEE80211_DL_FRAG (1<<5)
+#define IEEE80211_DL_EAP (1<<6)
+#define IEEE80211_DL_DROP (1<<7)
+
+#define IEEE80211_DL_TX (1<<8)
+#define IEEE80211_DL_RX (1<<9)
+
+#define IEEE80211_DL_HT (1<<10) //HT
+#define IEEE80211_DL_BA (1<<11) //ba
+#define IEEE80211_DL_TS (1<<12) //TS
+#define IEEE80211_DL_QOS (1<<13)
+#define IEEE80211_DL_REORDER (1<<14)
+#define IEEE80211_DL_IOT (1<<15)
+#define IEEE80211_DL_IPS (1<<16)
+#define IEEE80211_DL_TRACE (1<<29) //trace function, need to user net_ratelimit() together in order not to print too much to the screen
+#define IEEE80211_DL_DATA (1<<30) //use this flag to control whether print data buf out.
+#define IEEE80211_DL_ERR (1<<31) //always open
+#define IEEE80211_ERROR(f, a...) printk(KERN_ERR "ieee80211: " f, ## a)
+#define IEEE80211_WARNING(f, a...) printk(KERN_WARNING "ieee80211: " f, ## a)
+#define IEEE80211_DEBUG_INFO(f, a...) IEEE80211_DEBUG(IEEE80211_DL_INFO, f, ## a)
+
+#define IEEE80211_DEBUG_WX(f, a...) IEEE80211_DEBUG(IEEE80211_DL_WX, f, ## a)
+#define IEEE80211_DEBUG_SCAN(f, a...) IEEE80211_DEBUG(IEEE80211_DL_SCAN, f, ## a)
+#define IEEE80211_DEBUG_STATE(f, a...) IEEE80211_DEBUG(IEEE80211_DL_STATE, f, ## a)
+#define IEEE80211_DEBUG_MGMT(f, a...) IEEE80211_DEBUG(IEEE80211_DL_MGMT, f, ## a)
+#define IEEE80211_DEBUG_FRAG(f, a...) IEEE80211_DEBUG(IEEE80211_DL_FRAG, f, ## a)
+#define IEEE80211_DEBUG_EAP(f, a...) IEEE80211_DEBUG(IEEE80211_DL_EAP, f, ## a)
+#define IEEE80211_DEBUG_DROP(f, a...) IEEE80211_DEBUG(IEEE80211_DL_DROP, f, ## a)
+#define IEEE80211_DEBUG_TX(f, a...) IEEE80211_DEBUG(IEEE80211_DL_TX, f, ## a)
+#define IEEE80211_DEBUG_RX(f, a...) IEEE80211_DEBUG(IEEE80211_DL_RX, f, ## a)
+#define IEEE80211_DEBUG_QOS(f, a...) IEEE80211_DEBUG(IEEE80211_DL_QOS, f, ## a)
+
+#include <linux/if_arp.h> /* ARPHRD_ETHER */
+
+#ifndef WIRELESS_SPY
+#define WIRELESS_SPY // enable iwspy support
+#endif
+#include <net/iw_handler.h> // new driver API
+
+#ifndef ETH_P_PAE
+#define ETH_P_PAE 0x888E /* Port Access Entity (IEEE 802.1X) */
+#endif /* ETH_P_PAE */
+
+#define ETH_P_PREAUTH 0x88C7 /* IEEE 802.11i pre-authentication */
+
+#ifndef ETH_P_80211_RAW
+#define ETH_P_80211_RAW (ETH_P_ECONET + 1)
+#endif
+
+/* IEEE 802.11 defines */
+
+#define P80211_OUI_LEN 3
+
+struct ieee80211_snap_hdr {
+
+ u8 dsap; /* always 0xAA */
+ u8 ssap; /* always 0xAA */
+ u8 ctrl; /* always 0x03 */
+ u8 oui[P80211_OUI_LEN]; /* organizational universal id */
+
+} __packed;
+
+#define SNAP_SIZE sizeof(struct ieee80211_snap_hdr)
+
+#define WLAN_FC_GET_VERS(fc) ((fc) & IEEE80211_FCTL_VERS)
+#define WLAN_FC_GET_TYPE(fc) ((fc) & IEEE80211_FCTL_FTYPE)
+#define WLAN_FC_GET_STYPE(fc) ((fc) & IEEE80211_FCTL_STYPE)
+
+#define WLAN_FC_GET_FRAMETYPE(fc) ((fc) & IEEE80211_FCTL_FRAMETYPE)
+#define WLAN_GET_SEQ_FRAG(seq) ((seq) & IEEE80211_SCTL_FRAG)
+#define WLAN_GET_SEQ_SEQ(seq) (((seq) & IEEE80211_SCTL_SEQ) >> 4)
+
+/* Authentication algorithms */
+#define WLAN_AUTH_OPEN 0
+#define WLAN_AUTH_SHARED_KEY 1
+#define WLAN_AUTH_LEAP 2
+
+#define WLAN_AUTH_CHALLENGE_LEN 128
+
+#define WLAN_CAPABILITY_BSS (1<<0)
+#define WLAN_CAPABILITY_IBSS (1<<1)
+#define WLAN_CAPABILITY_CF_POLLABLE (1<<2)
+#define WLAN_CAPABILITY_CF_POLL_REQUEST (1<<3)
+#define WLAN_CAPABILITY_PRIVACY (1<<4)
+#define WLAN_CAPABILITY_SHORT_PREAMBLE (1<<5)
+#define WLAN_CAPABILITY_PBCC (1<<6)
+#define WLAN_CAPABILITY_CHANNEL_AGILITY (1<<7)
+#define WLAN_CAPABILITY_SPECTRUM_MGMT (1<<8)
+#define WLAN_CAPABILITY_QOS (1<<9)
+#define WLAN_CAPABILITY_SHORT_SLOT (1<<10)
+#define WLAN_CAPABILITY_DSSS_OFDM (1<<13)
+
+/* 802.11g ERP information element */
+#define WLAN_ERP_NON_ERP_PRESENT (1<<0)
+#define WLAN_ERP_USE_PROTECTION (1<<1)
+#define WLAN_ERP_BARKER_PREAMBLE (1<<2)
+
+/* Status codes */
+enum ieee80211_statuscode {
+ WLAN_STATUS_SUCCESS = 0,
+ WLAN_STATUS_UNSPECIFIED_FAILURE = 1,
+ WLAN_STATUS_CAPS_UNSUPPORTED = 10,
+ WLAN_STATUS_REASSOC_NO_ASSOC = 11,
+ WLAN_STATUS_ASSOC_DENIED_UNSPEC = 12,
+ WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG = 13,
+ WLAN_STATUS_UNKNOWN_AUTH_TRANSACTION = 14,
+ WLAN_STATUS_CHALLENGE_FAIL = 15,
+ WLAN_STATUS_AUTH_TIMEOUT = 16,
+ WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA = 17,
+ WLAN_STATUS_ASSOC_DENIED_RATES = 18,
+ /* 802.11b */
+ WLAN_STATUS_ASSOC_DENIED_NOSHORTPREAMBLE = 19,
+ WLAN_STATUS_ASSOC_DENIED_NOPBCC = 20,
+ WLAN_STATUS_ASSOC_DENIED_NOAGILITY = 21,
+ /* 802.11h */
+ WLAN_STATUS_ASSOC_DENIED_NOSPECTRUM = 22,
+ WLAN_STATUS_ASSOC_REJECTED_BAD_POWER = 23,
+ WLAN_STATUS_ASSOC_REJECTED_BAD_SUPP_CHAN = 24,
+ /* 802.11g */
+ WLAN_STATUS_ASSOC_DENIED_NOSHORTTIME = 25,
+ WLAN_STATUS_ASSOC_DENIED_NODSSSOFDM = 26,
+ /* 802.11i */
+ WLAN_STATUS_INVALID_IE = 40,
+ WLAN_STATUS_INVALID_GROUP_CIPHER = 41,
+ WLAN_STATUS_INVALID_PAIRWISE_CIPHER = 42,
+ WLAN_STATUS_INVALID_AKMP = 43,
+ WLAN_STATUS_UNSUPP_RSN_VERSION = 44,
+ WLAN_STATUS_INVALID_RSN_IE_CAP = 45,
+ WLAN_STATUS_CIPHER_SUITE_REJECTED = 46,
+};
+
+/* Reason codes */
+enum ieee80211_reasoncode {
+ WLAN_REASON_UNSPECIFIED = 1,
+ WLAN_REASON_PREV_AUTH_NOT_VALID = 2,
+ WLAN_REASON_DEAUTH_LEAVING = 3,
+ WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY = 4,
+ WLAN_REASON_DISASSOC_AP_BUSY = 5,
+ WLAN_REASON_CLASS2_FRAME_FROM_NONAUTH_STA = 6,
+ WLAN_REASON_CLASS3_FRAME_FROM_NONASSOC_STA = 7,
+ WLAN_REASON_DISASSOC_STA_HAS_LEFT = 8,
+ WLAN_REASON_STA_REQ_ASSOC_WITHOUT_AUTH = 9,
+ /* 802.11h */
+ WLAN_REASON_DISASSOC_BAD_POWER = 10,
+ WLAN_REASON_DISASSOC_BAD_SUPP_CHAN = 11,
+ /* 802.11i */
+ WLAN_REASON_INVALID_IE = 13,
+ WLAN_REASON_MIC_FAILURE = 14,
+ WLAN_REASON_4WAY_HANDSHAKE_TIMEOUT = 15,
+ WLAN_REASON_GROUP_KEY_HANDSHAKE_TIMEOUT = 16,
+ WLAN_REASON_IE_DIFFERENT = 17,
+ WLAN_REASON_INVALID_GROUP_CIPHER = 18,
+ WLAN_REASON_INVALID_PAIRWISE_CIPHER = 19,
+ WLAN_REASON_INVALID_AKMP = 20,
+ WLAN_REASON_UNSUPP_RSN_VERSION = 21,
+ WLAN_REASON_INVALID_RSN_IE_CAP = 22,
+ WLAN_REASON_IEEE8021X_FAILED = 23,
+ WLAN_REASON_CIPHER_SUITE_REJECTED = 24,
+};
+
+#define IEEE80211_STATMASK_SIGNAL (1<<0)
+#define IEEE80211_STATMASK_RSSI (1<<1)
+#define IEEE80211_STATMASK_NOISE (1<<2)
+#define IEEE80211_STATMASK_RATE (1<<3)
+#define IEEE80211_STATMASK_WEMASK 0x7
+
+#define IEEE80211_CCK_MODULATION (1<<0)
+#define IEEE80211_OFDM_MODULATION (1<<1)
+
+#define IEEE80211_24GHZ_BAND (1<<0)
+#define IEEE80211_52GHZ_BAND (1<<1)
+
+#define IEEE80211_CCK_RATE_LEN 4
+#define IEEE80211_CCK_RATE_1MB 0x02
+#define IEEE80211_CCK_RATE_2MB 0x04
+#define IEEE80211_CCK_RATE_5MB 0x0B
+#define IEEE80211_CCK_RATE_11MB 0x16
+#define IEEE80211_OFDM_RATE_LEN 8
+#define IEEE80211_OFDM_RATE_6MB 0x0C
+#define IEEE80211_OFDM_RATE_9MB 0x12
+#define IEEE80211_OFDM_RATE_12MB 0x18
+#define IEEE80211_OFDM_RATE_18MB 0x24
+#define IEEE80211_OFDM_RATE_24MB 0x30
+#define IEEE80211_OFDM_RATE_36MB 0x48
+#define IEEE80211_OFDM_RATE_48MB 0x60
+#define IEEE80211_OFDM_RATE_54MB 0x6C
+#define IEEE80211_BASIC_RATE_MASK 0x80
+
+#define IEEE80211_CCK_RATE_1MB_MASK (1<<0)
+#define IEEE80211_CCK_RATE_2MB_MASK (1<<1)
+#define IEEE80211_CCK_RATE_5MB_MASK (1<<2)
+#define IEEE80211_CCK_RATE_11MB_MASK (1<<3)
+#define IEEE80211_OFDM_RATE_6MB_MASK (1<<4)
+#define IEEE80211_OFDM_RATE_9MB_MASK (1<<5)
+#define IEEE80211_OFDM_RATE_12MB_MASK (1<<6)
+#define IEEE80211_OFDM_RATE_18MB_MASK (1<<7)
+#define IEEE80211_OFDM_RATE_24MB_MASK (1<<8)
+#define IEEE80211_OFDM_RATE_36MB_MASK (1<<9)
+#define IEEE80211_OFDM_RATE_48MB_MASK (1<<10)
+#define IEEE80211_OFDM_RATE_54MB_MASK (1<<11)
+
+#define IEEE80211_CCK_RATES_MASK 0x0000000F
+#define IEEE80211_CCK_BASIC_RATES_MASK (IEEE80211_CCK_RATE_1MB_MASK | \
+ IEEE80211_CCK_RATE_2MB_MASK)
+#define IEEE80211_CCK_DEFAULT_RATES_MASK (IEEE80211_CCK_BASIC_RATES_MASK | \
+ IEEE80211_CCK_RATE_5MB_MASK | \
+ IEEE80211_CCK_RATE_11MB_MASK)
+
+#define IEEE80211_OFDM_RATES_MASK 0x00000FF0
+#define IEEE80211_OFDM_BASIC_RATES_MASK (IEEE80211_OFDM_RATE_6MB_MASK | \
+ IEEE80211_OFDM_RATE_12MB_MASK | \
+ IEEE80211_OFDM_RATE_24MB_MASK)
+#define IEEE80211_OFDM_DEFAULT_RATES_MASK (IEEE80211_OFDM_BASIC_RATES_MASK | \
+ IEEE80211_OFDM_RATE_9MB_MASK | \
+ IEEE80211_OFDM_RATE_18MB_MASK | \
+ IEEE80211_OFDM_RATE_36MB_MASK | \
+ IEEE80211_OFDM_RATE_48MB_MASK | \
+ IEEE80211_OFDM_RATE_54MB_MASK)
+#define IEEE80211_DEFAULT_RATES_MASK (IEEE80211_OFDM_DEFAULT_RATES_MASK | \
+ IEEE80211_CCK_DEFAULT_RATES_MASK)
+
+#define IEEE80211_NUM_OFDM_RATES 8
+#define IEEE80211_NUM_CCK_RATES 4
+#define IEEE80211_OFDM_SHIFT_MASK_A 4
+
+
+/* this is stolen and modified from the madwifi driver*/
+#define IEEE80211_FC0_TYPE_MASK 0x0c
+#define IEEE80211_FC0_TYPE_DATA 0x08
+#define IEEE80211_FC0_SUBTYPE_MASK 0xB0
+#define IEEE80211_FC0_SUBTYPE_QOS 0x80
+
+#define IEEE80211_QOS_HAS_SEQ(fc) \
+ (((fc) & (IEEE80211_FC0_TYPE_MASK | IEEE80211_FC0_SUBTYPE_MASK)) == \
+ (IEEE80211_FC0_TYPE_DATA | IEEE80211_FC0_SUBTYPE_QOS))
+
+/* this is stolen from ipw2200 driver */
+#define IEEE_IBSS_MAC_HASH_SIZE 31
+struct ieee_ibss_seq {
+ u8 mac[ETH_ALEN];
+ u16 seq_num[17];
+ u16 frag_num[17];
+ unsigned long packet_time[17];
+ struct list_head list;
+};
+
+/* NOTE: This data is for statistical purposes; not all hardware provides this
+ * information for frames received. Not setting these will not cause
+ * any adverse affects. */
+struct ieee80211_rx_stats {
+ u32 mac_time[2];
+ s8 rssi;
+ u8 signal;
+ u8 noise;
+ u16 rate; /* in 100 kbps */
+ u8 received_channel;
+ u8 control;
+ u8 mask;
+ u8 freq;
+ u16 len;
+ u64 tsf;
+ u32 beacon_time;
+ u8 nic_type;
+ u16 Length;
+ // u8 DataRate; // In 0.5 Mbps
+ u8 SignalQuality; // in 0-100 index.
+ s32 RecvSignalPower; // Real power in dBm for this packet, no beautification and aggregation.
+ s8 RxPower; // in dBm Translate from PWdB
+ u8 SignalStrength; // in 0-100 index.
+ u16 bHwError:1;
+ u16 bCRC:1;
+ u16 bICV:1;
+ u16 bShortPreamble:1;
+ u16 Antenna:1; //for rtl8185
+ u16 Decrypted:1; //for rtl8185, rtl8187
+ u16 Wakeup:1; //for rtl8185
+ u16 Reserved0:1; //for rtl8185
+ u8 AGC;
+ u32 TimeStampLow;
+ u32 TimeStampHigh;
+ bool bShift;
+ bool bIsQosData; // Added by Annie, 2005-12-22.
+ u8 UserPriority;
+
+ //1!!!!!!!!!!!!!!!!!!!!!!!!!!!
+ //1Attention Please!!!<11n or 8190 specific code should be put below this line>
+ //1!!!!!!!!!!!!!!!!!!!!!!!!!!!
+
+ u8 RxDrvInfoSize;
+ u8 RxBufShift;
+ bool bIsAMPDU;
+ bool bFirstMPDU;
+ bool bContainHTC;
+ bool RxIs40MHzPacket;
+ u32 RxPWDBAll;
+ u8 RxMIMOSignalStrength[4]; // in 0~100 index
+ s8 RxMIMOSignalQuality[2];
+ bool bPacketMatchBSSID;
+ bool bIsCCK;
+ bool bPacketToSelf;
+ //added by amy
+ u8 *virtual_address;
+ u16 packetlength; // Total packet length: Must equal to sum of all FragLength
+ u16 fraglength; // FragLength should equal to PacketLength in non-fragment case
+ u16 fragoffset; // Data offset for this fragment
+ u16 ntotalfrag;
+ bool bisrxaggrsubframe;
+ bool bPacketBeacon; //cosa add for rssi
+ bool bToSelfBA; //cosa add for rssi
+ char cck_adc_pwdb[4]; //cosa add for rx path selection
+ u16 Seq_Num;
+
+};
+
+/* IEEE 802.11 requires that STA supports concurrent reception of at least
+ * three fragmented frames. This define can be increased to support more
+ * concurrent frames, but it should be noted that each entry can consume about
+ * 2 kB of RAM and increasing cache size will slow down frame reassembly. */
+#define IEEE80211_FRAG_CACHE_LEN 4
+
+struct ieee80211_frag_entry {
+ unsigned long first_frag_time;
+ unsigned int seq;
+ unsigned int last_frag;
+ struct sk_buff *skb;
+ u8 src_addr[ETH_ALEN];
+ u8 dst_addr[ETH_ALEN];
+};
+
+struct ieee80211_stats {
+ unsigned int tx_unicast_frames;
+ unsigned int tx_multicast_frames;
+ unsigned int tx_fragments;
+ unsigned int tx_unicast_octets;
+ unsigned int tx_multicast_octets;
+ unsigned int tx_deferred_transmissions;
+ unsigned int tx_single_retry_frames;
+ unsigned int tx_multiple_retry_frames;
+ unsigned int tx_retry_limit_exceeded;
+ unsigned int tx_discards;
+ unsigned int rx_unicast_frames;
+ unsigned int rx_multicast_frames;
+ unsigned int rx_fragments;
+ unsigned int rx_unicast_octets;
+ unsigned int rx_multicast_octets;
+ unsigned int rx_fcs_errors;
+ unsigned int rx_discards_no_buffer;
+ unsigned int tx_discards_wrong_sa;
+ unsigned int rx_discards_undecryptable;
+ unsigned int rx_message_in_msg_fragments;
+ unsigned int rx_message_in_bad_msg_fragments;
+};
+
+struct ieee80211_device;
+
+#include "ieee80211_crypt.h"
+
+#define SEC_KEY_1 (1<<0)
+#define SEC_KEY_2 (1<<1)
+#define SEC_KEY_3 (1<<2)
+#define SEC_KEY_4 (1<<3)
+#define SEC_ACTIVE_KEY (1<<4)
+#define SEC_AUTH_MODE (1<<5)
+#define SEC_UNICAST_GROUP (1<<6)
+#define SEC_LEVEL (1<<7)
+#define SEC_ENABLED (1<<8)
+#define SEC_ENCRYPT (1<<9)
+
+#define SEC_LEVEL_0 0 /* None */
+#define SEC_LEVEL_1 1 /* WEP 40 and 104 bit */
+#define SEC_LEVEL_2 2 /* Level 1 + TKIP */
+#define SEC_LEVEL_2_CKIP 3 /* Level 1 + CKIP */
+#define SEC_LEVEL_3 4 /* Level 2 + CCMP */
+
+#define SEC_ALG_NONE 0
+#define SEC_ALG_WEP 1
+#define SEC_ALG_TKIP 2
+#define SEC_ALG_CCMP 3
+
+#define WEP_KEYS 4
+#define WEP_KEY_LEN 13
+#define SCM_KEY_LEN 32
+#define SCM_TEMPORAL_KEY_LENGTH 16
+
+struct ieee80211_security {
+ u16 active_key:2,
+ enabled:1,
+ auth_mode:2,
+ auth_algo:4,
+ unicast_uses_group:1,
+ encrypt:1;
+ u8 key_sizes[WEP_KEYS];
+ u8 keys[WEP_KEYS][SCM_KEY_LEN];
+ u8 level;
+ u16 flags;
+} __packed;
+
+
+/*
+ 802.11 data frame from AP
+ ,-------------------------------------------------------------------.
+Bytes | 2 | 2 | 6 | 6 | 6 | 2 | 0..2312 | 4 |
+ |------|------|---------|---------|---------|------|---------|------|
+Desc. | ctrl | dura | DA/RA | TA | SA | Sequ | frame | fcs |
+ | | tion | (BSSID) | | | ence | data | |
+ `-------------------------------------------------------------------'
+Total: 28-2340 bytes
+*/
+
+/* Management Frame Information Element Types */
+enum ieee80211_mfie {
+ MFIE_TYPE_SSID = 0,
+ MFIE_TYPE_RATES = 1,
+ MFIE_TYPE_FH_SET = 2,
+ MFIE_TYPE_DS_SET = 3,
+ MFIE_TYPE_CF_SET = 4,
+ MFIE_TYPE_TIM = 5,
+ MFIE_TYPE_IBSS_SET = 6,
+ MFIE_TYPE_COUNTRY = 7,
+ MFIE_TYPE_HOP_PARAMS = 8,
+ MFIE_TYPE_HOP_TABLE = 9,
+ MFIE_TYPE_REQUEST = 10,
+ MFIE_TYPE_CHALLENGE = 16,
+ MFIE_TYPE_POWER_CONSTRAINT = 32,
+ MFIE_TYPE_POWER_CAPABILITY = 33,
+ MFIE_TYPE_TPC_REQUEST = 34,
+ MFIE_TYPE_TPC_REPORT = 35,
+ MFIE_TYPE_SUPP_CHANNELS = 36,
+ MFIE_TYPE_CSA = 37,
+ MFIE_TYPE_MEASURE_REQUEST = 38,
+ MFIE_TYPE_MEASURE_REPORT = 39,
+ MFIE_TYPE_QUIET = 40,
+ MFIE_TYPE_IBSS_DFS = 41,
+ MFIE_TYPE_ERP = 42,
+ MFIE_TYPE_RSN = 48,
+ MFIE_TYPE_RATES_EX = 50,
+ MFIE_TYPE_HT_CAP= 45,
+ MFIE_TYPE_HT_INFO= 61,
+ MFIE_TYPE_AIRONET=133,
+ MFIE_TYPE_GENERIC = 221,
+ MFIE_TYPE_QOS_PARAMETER = 222,
+};
+
+/* Minimal header; can be used for passing 802.11 frames with sufficient
+ * information to determine what type of underlying data type is actually
+ * stored in the data. */
+struct ieee80211_hdr {
+ __le16 frame_ctl;
+ __le16 duration_id;
+ u8 payload[0];
+} __packed;
+
+struct ieee80211_hdr_1addr {
+ __le16 frame_ctl;
+ __le16 duration_id;
+ u8 addr1[ETH_ALEN];
+ u8 payload[0];
+} __packed;
+
+struct ieee80211_hdr_2addr {
+ __le16 frame_ctl;
+ __le16 duration_id;
+ u8 addr1[ETH_ALEN];
+ u8 addr2[ETH_ALEN];
+ u8 payload[0];
+} __packed;
+
+struct ieee80211_hdr_3addr {
+ __le16 frame_ctl;
+ __le16 duration_id;
+ u8 addr1[ETH_ALEN];
+ u8 addr2[ETH_ALEN];
+ u8 addr3[ETH_ALEN];
+ __le16 seq_ctl;
+ u8 payload[0];
+} __packed;
+
+struct ieee80211_hdr_4addr {
+ __le16 frame_ctl;
+ __le16 duration_id;
+ u8 addr1[ETH_ALEN];
+ u8 addr2[ETH_ALEN];
+ u8 addr3[ETH_ALEN];
+ __le16 seq_ctl;
+ u8 addr4[ETH_ALEN];
+ u8 payload[0];
+} __packed;
+
+struct ieee80211_hdr_3addrqos {
+ __le16 frame_ctl;
+ __le16 duration_id;
+ u8 addr1[ETH_ALEN];
+ u8 addr2[ETH_ALEN];
+ u8 addr3[ETH_ALEN];
+ __le16 seq_ctl;
+ u8 payload[0];
+ __le16 qos_ctl;
+} __packed;
+
+struct ieee80211_hdr_4addrqos {
+ __le16 frame_ctl;
+ __le16 duration_id;
+ u8 addr1[ETH_ALEN];
+ u8 addr2[ETH_ALEN];
+ u8 addr3[ETH_ALEN];
+ __le16 seq_ctl;
+ u8 addr4[ETH_ALEN];
+ u8 payload[0];
+ __le16 qos_ctl;
+} __packed;
+
+struct ieee80211_info_element {
+ u8 id;
+ u8 len;
+ u8 data[0];
+} __packed;
+
+struct ieee80211_authentication {
+ struct ieee80211_hdr_3addr header;
+ __le16 algorithm;
+ __le16 transaction;
+ __le16 status;
+ /*challenge*/
+ struct ieee80211_info_element info_element[0];
+} __packed;
+
+struct ieee80211_disassoc {
+ struct ieee80211_hdr_3addr header;
+ __le16 reason;
+} __packed;
+
+struct ieee80211_probe_request {
+ struct ieee80211_hdr_3addr header;
+ /* SSID, supported rates */
+ struct ieee80211_info_element info_element[0];
+} __packed;
+
+struct ieee80211_probe_response {
+ struct ieee80211_hdr_3addr header;
+ __le32 time_stamp[2];
+ __le16 beacon_interval;
+ __le16 capability;
+ /* SSID, supported rates, FH params, DS params,
+ * CF params, IBSS params, TIM (if beacon), RSN */
+ struct ieee80211_info_element info_element[0];
+} __packed;
+
+/* Alias beacon for probe_response */
+#define ieee80211_beacon ieee80211_probe_response
+
+struct ieee80211_assoc_request_frame {
+ struct ieee80211_hdr_3addr header;
+ __le16 capability;
+ __le16 listen_interval;
+ /* SSID, supported rates, RSN */
+ struct ieee80211_info_element info_element[0];
+} __packed;
+
+struct ieee80211_reassoc_request_frame {
+ struct ieee80211_hdr_3addr header;
+ __le16 capability;
+ __le16 listen_interval;
+ u8 current_ap[ETH_ALEN];
+ /* SSID, supported rates, RSN */
+ struct ieee80211_info_element info_element[0];
+} __packed;
+
+struct ieee80211_assoc_response_frame {
+ struct ieee80211_hdr_3addr header;
+ __le16 capability;
+ __le16 status;
+ __le16 aid;
+ struct ieee80211_info_element info_element[0]; /* supported rates */
+} __packed;
+
+struct ieee80211_txb {
+ u8 nr_frags;
+ u8 encrypted;
+ u8 queue_index;
+ u8 rts_included;
+ u16 reserved;
+ __le16 frag_size;
+ __le16 payload_size;
+ struct sk_buff *fragments[0];
+};
+
+#define MAX_TX_AGG_COUNT 16
+struct ieee80211_drv_agg_txb {
+ u8 nr_drv_agg_frames;
+ struct sk_buff *tx_agg_frames[MAX_TX_AGG_COUNT];
+} __packed;
+
+#define MAX_SUBFRAME_COUNT 64
+struct ieee80211_rxb {
+ u8 nr_subframes;
+ struct sk_buff *subframes[MAX_SUBFRAME_COUNT];
+ u8 dst[ETH_ALEN];
+ u8 src[ETH_ALEN];
+} __packed;
+
+typedef union _frameqos {
+ u16 shortdata;
+ u8 chardata[2];
+ struct {
+ u16 tid:4;
+ u16 eosp:1;
+ u16 ack_policy:2;
+ u16 reserved:1;
+ u16 txop:8;
+ }field;
+} frameqos, *pframeqos;
+
+/* SWEEP TABLE ENTRIES NUMBER*/
+#define MAX_SWEEP_TAB_ENTRIES 42
+#define MAX_SWEEP_TAB_ENTRIES_PER_PACKET 7
+/* MAX_RATES_LENGTH needs to be 12. The spec says 8, and many APs
+ * only use 8, and then use extended rates for the remaining supported
+ * rates. Other APs, however, stick all of their supported rates on the
+ * main rates information element... */
+#define MAX_RATES_LENGTH ((u8)12)
+#define MAX_RATES_EX_LENGTH ((u8)16)
+#define MAX_NETWORK_COUNT 128
+
+#define MAX_CHANNEL_NUMBER 161
+#define IEEE80211_SOFTMAC_SCAN_TIME 100
+//(HZ / 2)
+#define IEEE80211_SOFTMAC_ASSOC_RETRY_TIME (HZ * 2)
+
+#define CRC_LENGTH 4U
+
+#define MAX_WPA_IE_LEN 64
+
+#define NETWORK_EMPTY_ESSID (1<<0)
+#define NETWORK_HAS_OFDM (1<<1)
+#define NETWORK_HAS_CCK (1<<2)
+
+/* QoS structure */
+#define NETWORK_HAS_QOS_PARAMETERS (1<<3)
+#define NETWORK_HAS_QOS_INFORMATION (1<<4)
+#define NETWORK_HAS_QOS_MASK (NETWORK_HAS_QOS_PARAMETERS | \
+ NETWORK_HAS_QOS_INFORMATION)
+/* 802.11h */
+#define NETWORK_HAS_POWER_CONSTRAINT (1<<5)
+#define NETWORK_HAS_CSA (1<<6)
+#define NETWORK_HAS_QUIET (1<<7)
+#define NETWORK_HAS_IBSS_DFS (1<<8)
+#define NETWORK_HAS_TPC_REPORT (1<<9)
+
+#define NETWORK_HAS_ERP_VALUE (1<<10)
+
+#define QOS_QUEUE_NUM 4
+#define QOS_OUI_LEN 3
+#define QOS_OUI_TYPE 2
+#define QOS_ELEMENT_ID 221
+#define QOS_OUI_INFO_SUB_TYPE 0
+#define QOS_OUI_PARAM_SUB_TYPE 1
+#define QOS_VERSION_1 1
+#define QOS_AIFSN_MIN_VALUE 2
+struct ieee80211_qos_information_element {
+ u8 elementID;
+ u8 length;
+ u8 qui[QOS_OUI_LEN];
+ u8 qui_type;
+ u8 qui_subtype;
+ u8 version;
+ u8 ac_info;
+} __packed;
+
+struct ieee80211_qos_ac_parameter {
+ u8 aci_aifsn;
+ u8 ecw_min_max;
+ __le16 tx_op_limit;
+} __packed;
+
+struct ieee80211_qos_parameter_info {
+ struct ieee80211_qos_information_element info_element;
+ u8 reserved;
+ struct ieee80211_qos_ac_parameter ac_params_record[QOS_QUEUE_NUM];
+} __packed;
+
+struct ieee80211_qos_parameters {
+ __le16 cw_min[QOS_QUEUE_NUM];
+ __le16 cw_max[QOS_QUEUE_NUM];
+ u8 aifs[QOS_QUEUE_NUM];
+ u8 flag[QOS_QUEUE_NUM];
+ __le16 tx_op_limit[QOS_QUEUE_NUM];
+} __packed;
+
+struct ieee80211_qos_data {
+ struct ieee80211_qos_parameters parameters;
+ int active;
+ int supported;
+ u8 param_count;
+ u8 old_param_count;
+};
+
+struct ieee80211_tim_parameters {
+ u8 tim_count;
+ u8 tim_period;
+} __packed;
+
+//#else
+struct ieee80211_wmm_ac_param {
+ u8 ac_aci_acm_aifsn;
+ u8 ac_ecwmin_ecwmax;
+ u16 ac_txop_limit;
+};
+
+struct ieee80211_wmm_ts_info {
+ u8 ac_dir_tid;
+ u8 ac_up_psb;
+ u8 reserved;
+} __packed;
+
+struct ieee80211_wmm_tspec_elem {
+ struct ieee80211_wmm_ts_info ts_info;
+ u16 norm_msdu_size;
+ u16 max_msdu_size;
+ u32 min_serv_inter;
+ u32 max_serv_inter;
+ u32 inact_inter;
+ u32 suspen_inter;
+ u32 serv_start_time;
+ u32 min_data_rate;
+ u32 mean_data_rate;
+ u32 peak_data_rate;
+ u32 max_burst_size;
+ u32 delay_bound;
+ u32 min_phy_rate;
+ u16 surp_band_allow;
+ u16 medium_time;
+} __packed;
+enum eap_type {
+ EAP_PACKET = 0,
+ EAPOL_START,
+ EAPOL_LOGOFF,
+ EAPOL_KEY,
+ EAPOL_ENCAP_ASF_ALERT
+};
+
+static const char *eap_types[] = {
+ [EAP_PACKET] = "EAP-Packet",
+ [EAPOL_START] = "EAPOL-Start",
+ [EAPOL_LOGOFF] = "EAPOL-Logoff",
+ [EAPOL_KEY] = "EAPOL-Key",
+ [EAPOL_ENCAP_ASF_ALERT] = "EAPOL-Encap-ASF-Alert"
+};
+
+static inline const char *eap_get_type(int type)
+{
+ return ((u32)type >= ARRAY_SIZE(eap_types)) ? "Unknown" : eap_types[type];
+}
+//added by amy for reorder
+static inline u8 Frame_QoSTID(u8 *buf)
+{
+ struct ieee80211_hdr_3addr *hdr;
+ u16 fc;
+ hdr = (struct ieee80211_hdr_3addr *)buf;
+ fc = le16_to_cpu(hdr->frame_ctl);
+ return (u8)((frameqos *)(buf + (((fc & IEEE80211_FCTL_TODS)&&(fc & IEEE80211_FCTL_FROMDS))? 30 : 24)))->field.tid;
+}
+
+//added by amy for reorder
+
+struct eapol {
+ u8 snap[6];
+ u16 ethertype;
+ u8 version;
+ u8 type;
+ u16 length;
+} __packed;
+
+struct ieee80211_softmac_stats{
+ unsigned int rx_ass_ok;
+ unsigned int rx_ass_err;
+ unsigned int rx_probe_rq;
+ unsigned int tx_probe_rs;
+ unsigned int tx_beacons;
+ unsigned int rx_auth_rq;
+ unsigned int rx_auth_rs_ok;
+ unsigned int rx_auth_rs_err;
+ unsigned int tx_auth_rq;
+ unsigned int no_auth_rs;
+ unsigned int no_ass_rs;
+ unsigned int tx_ass_rq;
+ unsigned int rx_ass_rq;
+ unsigned int tx_probe_rq;
+ unsigned int reassoc;
+ unsigned int swtxstop;
+ unsigned int swtxawake;
+ unsigned char CurrentShowTxate;
+ unsigned char last_packet_rate;
+ unsigned int txretrycount;
+};
+
+#define BEACON_PROBE_SSID_ID_POSITION 12
+
+struct ieee80211_info_element_hdr {
+ u8 id;
+ u8 len;
+} __packed;
+
+/*
+ * These are the data types that can make up management packets
+ *
+ u16 auth_algorithm;
+ u16 auth_sequence;
+ u16 beacon_interval;
+ u16 capability;
+ u8 current_ap[ETH_ALEN];
+ u16 listen_interval;
+ struct {
+ u16 association_id:14, reserved:2;
+ } __packed;
+ u32 time_stamp[2];
+ u16 reason;
+ u16 status;
+*/
+
+#define IEEE80211_DEFAULT_TX_ESSID "Penguin"
+#define IEEE80211_DEFAULT_BASIC_RATE 2 //1Mbps
+
+enum {WMM_all_frame, WMM_two_frame, WMM_four_frame, WMM_six_frame};
+#define MAX_SP_Len (WMM_all_frame << 4)
+#define IEEE80211_QOS_TID 0x0f
+#define QOS_CTL_NOTCONTAIN_ACK (0x01 << 5)
+
+#define IEEE80211_DTIM_MBCAST 4
+#define IEEE80211_DTIM_UCAST 2
+#define IEEE80211_DTIM_VALID 1
+#define IEEE80211_DTIM_INVALID 0
+
+#define IEEE80211_PS_DISABLED 0
+#define IEEE80211_PS_UNICAST IEEE80211_DTIM_UCAST
+#define IEEE80211_PS_MBCAST IEEE80211_DTIM_MBCAST
+
+//added by David for QoS 2006/6/30
+//#define WMM_Hang_8187
+#ifdef WMM_Hang_8187
+#undef WMM_Hang_8187
+#endif
+
+#define WME_AC_BK 0x00
+#define WME_AC_BE 0x01
+#define WME_AC_VI 0x02
+#define WME_AC_VO 0x03
+#define WME_ACI_MASK 0x03
+#define WME_AIFSN_MASK 0x03
+#define WME_AC_PRAM_LEN 16
+
+#define MAX_RECEIVE_BUFFER_SIZE 9100
+
+//UP Mapping to AC, using in MgntQuery_SequenceNumber() and maybe for DSCP
+//#define UP2AC(up) ((up<3) ? ((up==0)?1:0) : (up>>1))
+#define UP2AC(up) ( \
+ ((up) < 1) ? WME_AC_BE : \
+ ((up) < 3) ? WME_AC_BK : \
+ ((up) < 4) ? WME_AC_BE : \
+ ((up) < 6) ? WME_AC_VI : \
+ WME_AC_VO)
+//AC Mapping to UP, using in Tx part for selecting the corresponding TX queue
+#define AC2UP(_ac) ( \
+ ((_ac) == WME_AC_VO) ? 6 : \
+ ((_ac) == WME_AC_VI) ? 5 : \
+ ((_ac) == WME_AC_BK) ? 1 : \
+ 0)
+
+#define ETHER_ADDR_LEN 6 /* length of an Ethernet address */
+#define ETHERNET_HEADER_SIZE 14 /* length of two Ethernet address plus ether type*/
+
+struct ether_header {
+ u8 ether_dhost[ETHER_ADDR_LEN];
+ u8 ether_shost[ETHER_ADDR_LEN];
+ u16 ether_type;
+} __packed;
+
+#ifndef ETHERTYPE_PAE
+#define ETHERTYPE_PAE 0x888e /* EAPOL PAE/802.1x */
+#endif
+#ifndef ETHERTYPE_IP
+#define ETHERTYPE_IP 0x0800 /* IP protocol */
+#endif
+
+typedef enum _erp_t{
+ ERP_NonERPpresent = 0x01,
+ ERP_UseProtection = 0x02,
+ ERP_BarkerPreambleMode = 0x04,
+} erp_t;
+
+
+struct ieee80211_network {
+ /* These entries are used to identify a unique network */
+ u8 bssid[ETH_ALEN];
+ u8 channel;
+ /* Ensure null-terminated for any debug msgs */
+ u8 ssid[IW_ESSID_MAX_SIZE + 1];
+ u8 ssid_len;
+ struct ieee80211_qos_data qos_data;
+
+ //added by amy for LEAP
+ bool bWithAironetIE;
+ bool bCkipSupported;
+ bool bCcxRmEnable;
+ u16 CcxRmState[2];
+ // CCXv4 S59, MBSSID.
+ bool bMBssidValid;
+ u8 MBssidMask;
+ u8 MBssid[6];
+ // CCX 2 S38, WLAN Device Version Number element. Annie, 2006-08-20.
+ bool bWithCcxVerNum;
+ u8 BssCcxVerNumber;
+ /* These are network statistics */
+ struct ieee80211_rx_stats stats;
+ u16 capability;
+ u8 rates[MAX_RATES_LENGTH];
+ u8 rates_len;
+ u8 rates_ex[MAX_RATES_EX_LENGTH];
+ u8 rates_ex_len;
+ unsigned long last_scanned;
+ u8 mode;
+ u32 flags;
+ u32 last_associate;
+ u32 time_stamp[2];
+ u16 beacon_interval;
+ u16 listen_interval;
+ u16 atim_window;
+ u8 erp_value;
+ u8 wpa_ie[MAX_WPA_IE_LEN];
+ size_t wpa_ie_len;
+ u8 rsn_ie[MAX_WPA_IE_LEN];
+ size_t rsn_ie_len;
+
+ struct ieee80211_tim_parameters tim;
+ u8 dtim_period;
+ u8 dtim_data;
+ u32 last_dtim_sta_time[2];
+
+ //appeded for QoS
+ u8 wmm_info;
+ struct ieee80211_wmm_ac_param wmm_param[4];
+ u8 QoS_Enable;
+#ifdef THOMAS_TURBO
+ u8 Turbo_Enable;//enable turbo mode, added by thomas
+#endif
+ u16 CountryIeLen;
+ u8 CountryIeBuf[MAX_IE_LEN];
+ // HT Related, by amy, 2008.04.29
+ BSS_HT bssht;
+ // Add to handle broadcom AP management frame CCK rate.
+ bool broadcom_cap_exist;
+ bool ralink_cap_exist;
+ bool atheros_cap_exist;
+ bool cisco_cap_exist;
+ bool unknown_cap_exist;
+// u8 berp_info;
+ bool berp_info_valid;
+ bool buseprotection;
+ //put at the end of the structure.
+ struct list_head list;
+};
+
+enum ieee80211_state {
+
+ /* the card is not linked at all */
+ IEEE80211_NOLINK = 0,
+
+ /* IEEE80211_ASSOCIATING* are for BSS client mode
+ * the driver shall not perform RX filtering unless
+ * the state is LINKED.
+ * The driver shall just check for the state LINKED and
+ * defaults to NOLINK for ALL the other states (including
+ * LINKED_SCANNING)
+ */
+
+ /* the association procedure will start (wq scheduling)*/
+ IEEE80211_ASSOCIATING,
+ IEEE80211_ASSOCIATING_RETRY,
+
+ /* the association procedure is sending AUTH request*/
+ IEEE80211_ASSOCIATING_AUTHENTICATING,
+
+ /* the association procedure has successfully authentcated
+ * and is sending association request
+ */
+ IEEE80211_ASSOCIATING_AUTHENTICATED,
+
+ /* the link is ok. the card associated to a BSS or linked
+ * to a ibss cell or acting as an AP and creating the bss
+ */
+ IEEE80211_LINKED,
+
+ /* same as LINKED, but the driver shall apply RX filter
+ * rules as we are in NO_LINK mode. As the card is still
+ * logically linked, but it is doing a syncro site survey
+ * then it will be back to LINKED state.
+ */
+ IEEE80211_LINKED_SCANNING,
+
+};
+
+#define DEFAULT_MAX_SCAN_AGE (15 * HZ)
+#define DEFAULT_FTS 2346
+
+#define CFG_IEEE80211_RESERVE_FCS (1<<0)
+#define CFG_IEEE80211_COMPUTE_FCS (1<<1)
+#define CFG_IEEE80211_RTS (1<<2)
+
+#define IEEE80211_24GHZ_MIN_CHANNEL 1
+#define IEEE80211_24GHZ_MAX_CHANNEL 14
+#define IEEE80211_24GHZ_CHANNELS (IEEE80211_24GHZ_MAX_CHANNEL - \
+ IEEE80211_24GHZ_MIN_CHANNEL + 1)
+
+#define IEEE80211_52GHZ_MIN_CHANNEL 34
+#define IEEE80211_52GHZ_MAX_CHANNEL 165
+#define IEEE80211_52GHZ_CHANNELS (IEEE80211_52GHZ_MAX_CHANNEL - \
+ IEEE80211_52GHZ_MIN_CHANNEL + 1)
+
+
+
+typedef struct tx_pending_t{
+ int frag;
+ struct ieee80211_txb *txb;
+}tx_pending_t;
+
+typedef struct _bandwidth_autoswitch {
+ long threshold_20Mhzto40Mhz;
+ long threshold_40Mhzto20Mhz;
+ bool bforced_tx20Mhz;
+ bool bautoswitch_enable;
+} bandwidth_autoswitch, *pbandwidth_autoswitch;
+
+
+//added by amy for order
+
+#define REORDER_WIN_SIZE 128
+#define REORDER_ENTRY_NUM 128
+typedef struct _RX_REORDER_ENTRY {
+ struct list_head List;
+ u16 SeqNum;
+ struct ieee80211_rxb *prxb;
+} RX_REORDER_ENTRY, *PRX_REORDER_ENTRY;
+//added by amy for order
+typedef enum _Fsync_State{
+ Default_Fsync,
+ HW_Fsync,
+ SW_Fsync
+}Fsync_State;
+
+// Power save mode configured.
+typedef enum _RT_PS_MODE
+{
+ eActive, // Active/Continuous access.
+ eMaxPs, // Max power save mode.
+ eFastPs // Fast power save mode.
+}RT_PS_MODE;
+
+typedef enum _IPS_CALLBACK_FUNCION
+{
+ IPS_CALLBACK_NONE = 0,
+ IPS_CALLBACK_MGNT_LINK_REQUEST = 1,
+ IPS_CALLBACK_JOIN_REQUEST = 2,
+}IPS_CALLBACK_FUNCION;
+
+typedef enum _RT_JOIN_ACTION{
+ RT_JOIN_INFRA = 1,
+ RT_JOIN_IBSS = 2,
+ RT_START_IBSS = 3,
+ RT_NO_ACTION = 4,
+}RT_JOIN_ACTION;
+
+typedef struct _IbssParms{
+ u16 atimWin;
+}IbssParms, *PIbssParms;
+#define MAX_NUM_RATES 264 // Max num of support rates element: 8, Max num of ext. support rate: 255. 061122, by rcnjko.
+
+// RF state.
+typedef enum _RT_RF_POWER_STATE {
+ eRfOn,
+ eRfSleep,
+ eRfOff
+}RT_RF_POWER_STATE;
+
+typedef struct _RT_POWER_SAVE_CONTROL {
+
+ //
+ // Inactive Power Save(IPS) : Disable RF when disconnected
+ //
+ bool bInactivePs;
+ bool bIPSModeBackup;
+ bool bSwRfProcessing;
+ RT_RF_POWER_STATE eInactivePowerState;
+ struct work_struct InactivePsWorkItem;
+ struct timer_list InactivePsTimer;
+
+ // Return point for join action
+ IPS_CALLBACK_FUNCION ReturnPoint;
+
+ // Recored Parameters for rescheduled JoinRequest
+ bool bTmpBssDesc;
+ RT_JOIN_ACTION tmpJoinAction;
+ struct ieee80211_network tmpBssDesc;
+
+ // Recored Parameters for rescheduled MgntLinkRequest
+ bool bTmpScanOnly;
+ bool bTmpActiveScan;
+ bool bTmpFilterHiddenAP;
+ bool bTmpUpdateParms;
+ u8 tmpSsidBuf[33];
+ OCTET_STRING tmpSsid2Scan;
+ bool bTmpSsid2Scan;
+ u8 tmpNetworkType;
+ u8 tmpChannelNumber;
+ u16 tmpBcnPeriod;
+ u8 tmpDtimPeriod;
+ u16 tmpmCap;
+ OCTET_STRING tmpSuppRateSet;
+ u8 tmpSuppRateBuf[MAX_NUM_RATES];
+ bool bTmpSuppRate;
+ IbssParms tmpIbpm;
+ bool bTmpIbpm;
+
+ //
+ // Leisre Poswer Save : Disable RF if connected but traffic is not busy
+ //
+ bool bLeisurePs;
+
+} RT_POWER_SAVE_CONTROL, *PRT_POWER_SAVE_CONTROL;
+
+typedef u32 RT_RF_CHANGE_SOURCE;
+#define RF_CHANGE_BY_SW BIT31
+#define RF_CHANGE_BY_HW BIT30
+#define RF_CHANGE_BY_PS BIT29
+#define RF_CHANGE_BY_IPS BIT28
+#define RF_CHANGE_BY_INIT 0 // Do not change the RFOff reason. Defined by Bruce, 2008-01-17.
+
+typedef enum
+{
+ COUNTRY_CODE_FCC = 0,
+ COUNTRY_CODE_IC = 1,
+ COUNTRY_CODE_ETSI = 2,
+ COUNTRY_CODE_SPAIN = 3,
+ COUNTRY_CODE_FRANCE = 4,
+ COUNTRY_CODE_MKK = 5,
+ COUNTRY_CODE_MKK1 = 6,
+ COUNTRY_CODE_ISRAEL = 7,
+ COUNTRY_CODE_TELEC,
+ COUNTRY_CODE_MIC,
+ COUNTRY_CODE_GLOBAL_DOMAIN
+}country_code_type_t;
+
+#define RT_MAX_LD_SLOT_NUM 10
+typedef struct _RT_LINK_DETECT_T{
+
+ u32 NumRecvBcnInPeriod;
+ u32 NumRecvDataInPeriod;
+
+ u32 RxBcnNum[RT_MAX_LD_SLOT_NUM]; // number of Rx beacon / CheckForHang_period to determine link status
+ u32 RxDataNum[RT_MAX_LD_SLOT_NUM]; // number of Rx data / CheckForHang_period to determine link status
+ u16 SlotNum; // number of CheckForHang period to determine link status
+ u16 SlotIndex;
+
+ u32 NumTxOkInPeriod;
+ u32 NumRxOkInPeriod;
+ bool bBusyTraffic;
+}RT_LINK_DETECT_T, *PRT_LINK_DETECT_T;
+
+
+struct ieee80211_device {
+ struct net_device *dev;
+ struct ieee80211_security sec;
+
+ //hw security related
+// u8 hwsec_support; //support?
+ u8 hwsec_active; //hw security active.
+ bool is_silent_reset;
+ bool ieee_up;
+ //added by amy
+ bool bSupportRemoteWakeUp;
+ RT_PS_MODE dot11PowerSaveMode; // Power save mode configured.
+ bool actscanning;
+ bool beinretry;
+ RT_RF_POWER_STATE eRFPowerState;
+ RT_RF_CHANGE_SOURCE RfOffReason;
+ bool is_set_key;
+ //11n spec related I wonder if These info structure need to be moved out of ieee80211_device
+
+ //11n HT below
+ PRT_HIGH_THROUGHPUT pHTInfo;
+ //struct timer_list SwBwTimer;
+// spinlock_t chnlop_spinlock;
+ spinlock_t bw_spinlock;
+
+ spinlock_t reorder_spinlock;
+ // for HT operation rate set. we use this one for HT data rate to separate different descriptors
+ //the way fill this is the same as in the IE
+ u8 Regdot11HTOperationalRateSet[16]; //use RATR format
+ u8 dot11HTOperationalRateSet[16]; //use RATR format
+ u8 RegHTSuppRateSet[16];
+ u8 HTCurrentOperaRate;
+ u8 HTHighestOperaRate;
+ //wb added for rate operation mode to firmware
+ u8 bTxDisableRateFallBack;
+ u8 bTxUseDriverAssingedRate;
+ atomic_t atm_chnlop;
+ atomic_t atm_swbw;
+// u8 HTHighestOperaRate;
+// u8 HTCurrentOperaRate;
+
+ // 802.11e and WMM Traffic Stream Info (TX)
+ struct list_head Tx_TS_Admit_List;
+ struct list_head Tx_TS_Pending_List;
+ struct list_head Tx_TS_Unused_List;
+ TX_TS_RECORD TxTsRecord[TOTAL_TS_NUM];
+ // 802.11e and WMM Traffic Stream Info (RX)
+ struct list_head Rx_TS_Admit_List;
+ struct list_head Rx_TS_Pending_List;
+ struct list_head Rx_TS_Unused_List;
+ RX_TS_RECORD RxTsRecord[TOTAL_TS_NUM];
+//#ifdef TO_DO_LIST
+ RX_REORDER_ENTRY RxReorderEntry[128];
+ struct list_head RxReorder_Unused_List;
+//#endif
+ // Qos related. Added by Annie, 2005-11-01.
+// PSTA_QOS pStaQos;
+ u8 ForcedPriority; // Force per-packet priority 1~7. (default: 0, not to force it.)
+
+
+ /* Bookkeeping structures */
+ struct net_device_stats stats;
+ struct ieee80211_stats ieee_stats;
+ struct ieee80211_softmac_stats softmac_stats;
+
+ /* Probe / Beacon management */
+ struct list_head network_free_list;
+ struct list_head network_list;
+ struct ieee80211_network *networks;
+ int scans;
+ int scan_age;
+
+ int iw_mode; /* operating mode (IW_MODE_*) */
+ struct iw_spy_data spy_data;
+
+ spinlock_t lock;
+ spinlock_t wpax_suitlist_lock;
+
+ int tx_headroom; /* Set to size of any additional room needed at front
+ * of allocated Tx SKBs */
+ u32 config;
+
+ /* WEP and other encryption related settings at the device level */
+ int open_wep; /* Set to 1 to allow unencrypted frames */
+ int auth_mode;
+ int reset_on_keychange; /* Set to 1 if the HW needs to be reset on
+ * WEP key changes */
+
+ /* If the host performs {en,de}cryption, then set to 1 */
+ int host_encrypt;
+ int host_encrypt_msdu;
+ int host_decrypt;
+ /* host performs multicast decryption */
+ int host_mc_decrypt;
+
+ /* host should strip IV and ICV from protected frames */
+ /* meaningful only when hardware decryption is being used */
+ int host_strip_iv_icv;
+
+ int host_open_frag;
+ int host_build_iv;
+ int ieee802_1x; /* is IEEE 802.1X used */
+
+ /* WPA data */
+ bool bHalfWirelessN24GMode;
+ int wpa_enabled;
+ int drop_unencrypted;
+ int tkip_countermeasures;
+ int privacy_invoked;
+ size_t wpa_ie_len;
+ u8 *wpa_ie;
+ u8 ap_mac_addr[6];
+ u16 pairwise_key_type;
+ u16 group_key_type;
+ struct list_head crypt_deinit_list;
+ struct ieee80211_crypt_data *crypt[WEP_KEYS];
+ int tx_keyidx; /* default TX key index (crypt[tx_keyidx]) */
+ struct timer_list crypt_deinit_timer;
+ int crypt_quiesced;
+
+ int bcrx_sta_key; /* use individual keys to override default keys even
+ * with RX of broad/multicast frames */
+
+ /* Fragmentation structures */
+ // each streaming contain a entry
+ struct ieee80211_frag_entry frag_cache[17][IEEE80211_FRAG_CACHE_LEN];
+ unsigned int frag_next_idx[17];
+ u16 fts; /* Fragmentation Threshold */
+#define DEFAULT_RTS_THRESHOLD 2346U
+#define MIN_RTS_THRESHOLD 1
+#define MAX_RTS_THRESHOLD 2346U
+ u16 rts; /* RTS threshold */
+
+ /* Association info */
+ u8 bssid[ETH_ALEN];
+
+ /* This stores infos for the current network.
+ * Either the network we are associated in INFRASTRUCTURE
+ * or the network that we are creating in MASTER mode.
+ * ad-hoc is a mixture ;-).
+ * Note that in infrastructure mode, even when not associated,
+ * fields bssid and essid may be valid (if wpa_set and essid_set
+ * are true) as thy carry the value set by the user via iwconfig
+ */
+ struct ieee80211_network current_network;
+
+ enum ieee80211_state state;
+
+ int short_slot;
+ int reg_mode;
+ int mode; /* A, B, G */
+ int modulation; /* CCK, OFDM */
+ int freq_band; /* 2.4Ghz, 5.2Ghz, Mixed */
+ int abg_true; /* ABG flag */
+
+ /* used for forcing the ibss workqueue to terminate
+ * without wait for the syncro scan to terminate
+ */
+ short sync_scan_hurryup;
+
+ int perfect_rssi;
+ int worst_rssi;
+
+ u16 prev_seq_ctl; /* used to drop duplicate frames */
+
+ /* map of allowed channels. 0 is dummy */
+ // FIXME: remember to default to a basic channel plan depending of the PHY type
+ void *pDot11dInfo;
+ bool bGlobalDomain;
+ int rate; /* current rate */
+ int basic_rate;
+ //FIXME: pleace callback, see if redundant with softmac_features
+ short active_scan;
+
+ /* this contains flags for selectively enable softmac support */
+ u16 softmac_features;
+
+ /* if the sequence control field is not filled by HW */
+ u16 seq_ctrl[5];
+
+ /* association procedure transaction sequence number */
+ u16 associate_seq;
+
+ /* AID for RTXed association responses */
+ u16 assoc_id;
+
+ /* power save mode related*/
+ short ps;
+ short sta_sleep;
+ int ps_timeout;
+ int ps_period;
+ struct tasklet_struct ps_task;
+ u32 ps_th;
+ u32 ps_tl;
+
+ short raw_tx;
+ /* used if IEEE_SOFTMAC_TX_QUEUE is set */
+ short queue_stop;
+ short scanning;
+ short proto_started;
+
+ struct semaphore wx_sem;
+ struct semaphore scan_sem;
+
+ spinlock_t mgmt_tx_lock;
+ spinlock_t beacon_lock;
+
+ short beacon_txing;
+
+ short wap_set;
+ short ssid_set;
+
+ u8 wpax_type_set; //{added by David, 2006.9.28}
+ u32 wpax_type_notify; //{added by David, 2006.9.26}
+
+ /* QoS related flag */
+ char init_wmmparam_flag;
+ /* set on initialization */
+ u8 qos_support;
+
+ /* for discarding duplicated packets in IBSS */
+ struct list_head ibss_mac_hash[IEEE_IBSS_MAC_HASH_SIZE];
+
+ /* for discarding duplicated packets in BSS */
+ u16 last_rxseq_num[17]; /* rx seq previous per-tid */
+ u16 last_rxfrag_num[17];/* tx frag previous per-tid */
+ unsigned long last_packet_time[17];
+
+ /* for PS mode */
+ unsigned long last_rx_ps_time;
+
+ /* used if IEEE_SOFTMAC_SINGLE_QUEUE is set */
+ struct sk_buff *mgmt_queue_ring[MGMT_QUEUE_NUM];
+ int mgmt_queue_head;
+ int mgmt_queue_tail;
+//{ added for rtl819x
+#define IEEE80211_QUEUE_LIMIT 128
+ u8 AsocRetryCount;
+ unsigned int hw_header;
+ struct sk_buff_head skb_waitQ[MAX_QUEUE_SIZE];
+ struct sk_buff_head skb_aggQ[MAX_QUEUE_SIZE];
+ struct sk_buff_head skb_drv_aggQ[MAX_QUEUE_SIZE];
+ u32 sta_edca_param[4];
+ bool aggregation;
+ // Enable/Disable Rx immediate BA capability.
+ bool enable_rx_imm_BA;
+ bool bibsscoordinator;
+
+ //+by amy for DM ,080515
+ //Dynamic Tx power for near/far range enable/Disable , by amy , 2008-05-15
+ bool bdynamic_txpower_enable;
+
+ bool bCTSToSelfEnable;
+ u8 CTSToSelfTH;
+
+ u32 fsync_time_interval;
+ u32 fsync_rate_bitmap;
+ u8 fsync_rssi_threshold;
+ bool bfsync_enable;
+
+ u8 fsync_multiple_timeinterval; // FsyncMultipleTimeInterval * FsyncTimeInterval
+ u32 fsync_firstdiff_ratethreshold; // low threshold
+ u32 fsync_seconddiff_ratethreshold; // decrease threshold
+ Fsync_State fsync_state;
+ bool bis_any_nonbepkts;
+ //20Mhz 40Mhz AutoSwitch Threshold
+ bandwidth_autoswitch bandwidth_auto_switch;
+ //for txpower tracking
+ bool FwRWRF;
+
+ //added by amy for AP roaming
+ RT_LINK_DETECT_T LinkDetectInfo;
+ //added by amy for ps
+ RT_POWER_SAVE_CONTROL PowerSaveControl;
+//}
+ /* used if IEEE_SOFTMAC_TX_QUEUE is set */
+ struct tx_pending_t tx_pending;
+
+ /* used if IEEE_SOFTMAC_ASSOCIATE is set */
+ struct timer_list associate_timer;
+
+ /* used if IEEE_SOFTMAC_BEACONS is set */
+ struct timer_list beacon_timer;
+ struct work_struct associate_complete_wq;
+ struct work_struct associate_procedure_wq;
+ struct delayed_work softmac_scan_wq;
+ struct delayed_work associate_retry_wq;
+ struct delayed_work start_ibss_wq;
+ struct work_struct wx_sync_scan_wq;
+ struct workqueue_struct *wq;
+ // Qos related. Added by Annie, 2005-11-01.
+ //STA_QOS StaQos;
+
+ //u32 STA_EDCA_PARAM[4];
+ //CHANNEL_ACCESS_SETTING ChannelAccessSetting;
+
+
+ /* Callback functions */
+ void (*set_security)(struct net_device *dev,
+ struct ieee80211_security *sec);
+
+ /* Used to TX data frame by using txb structs.
+ * this is not used if in the softmac_features
+ * is set the flag IEEE_SOFTMAC_TX_QUEUE
+ */
+ int (*hard_start_xmit)(struct ieee80211_txb *txb,
+ struct net_device *dev);
+
+ int (*reset_port)(struct net_device *dev);
+ int (*is_queue_full) (struct net_device *dev, int pri);
+
+ int (*handle_management) (struct net_device *dev,
+ struct ieee80211_network *network, u16 type);
+ int (*is_qos_active) (struct net_device *dev, struct sk_buff *skb);
+
+ /* Softmac-generated frames (management) are TXed via this
+ * callback if the flag IEEE_SOFTMAC_SINGLE_QUEUE is
+ * not set. As some cards may have different HW queues that
+ * one might want to use for data and management frames
+ * the option to have two callbacks might be useful.
+ * This function can't sleep.
+ */
+ int (*softmac_hard_start_xmit)(struct sk_buff *skb,
+ struct net_device *dev);
+
+ /* used instead of hard_start_xmit (not softmac_hard_start_xmit)
+ * if the IEEE_SOFTMAC_TX_QUEUE feature is used to TX data
+ * frames. I the option IEEE_SOFTMAC_SINGLE_QUEUE is also set
+ * then also management frames are sent via this callback.
+ * This function can't sleep.
+ */
+ void (*softmac_data_hard_start_xmit)(struct sk_buff *skb,
+ struct net_device *dev, int rate);
+
+ /* stops the HW queue for DATA frames. Useful to avoid
+ * waste time to TX data frame when we are reassociating
+ * This function can sleep.
+ */
+ void (*data_hard_stop)(struct net_device *dev);
+
+ /* OK this is complementar to data_poll_hard_stop */
+ void (*data_hard_resume)(struct net_device *dev);
+
+ /* ask to the driver to retune the radio .
+ * This function can sleep. the driver should ensure
+ * the radio has been swithced before return.
+ */
+ void (*set_chan)(struct net_device *dev, short ch);
+
+ /* These are not used if the ieee stack takes care of
+ * scanning (IEEE_SOFTMAC_SCAN feature set).
+ * In this case only the set_chan is used.
+ *
+ * The syncro version is similar to the start_scan but
+ * does not return until all channels has been scanned.
+ * this is called in user context and should sleep,
+ * it is called in a work_queue when swithcing to ad-hoc mode
+ * or in behalf of iwlist scan when the card is associated
+ * and root user ask for a scan.
+ * the function stop_scan should stop both the syncro and
+ * background scanning and can sleep.
+ * The function start_scan should initiate the background
+ * scanning and can't sleep.
+ */
+ void (*scan_syncro)(struct net_device *dev);
+ void (*start_scan)(struct net_device *dev);
+ void (*stop_scan)(struct net_device *dev);
+
+ /* indicate the driver that the link state is changed
+ * for example it may indicate the card is associated now.
+ * Driver might be interested in this to apply RX filter
+ * rules or simply light the LINK led
+ */
+ void (*link_change)(struct net_device *dev);
+
+ /* these two function indicates to the HW when to start
+ * and stop to send beacons. This is used when the
+ * IEEE_SOFTMAC_BEACONS is not set. For now the
+ * stop_send_bacons is NOT guaranteed to be called only
+ * after start_send_beacons.
+ */
+ void (*start_send_beacons) (struct net_device *dev,u16 tx_rate);
+ void (*stop_send_beacons) (struct net_device *dev);
+
+ /* power save mode related */
+ void (*sta_wake_up) (struct net_device *dev);
+ void (*ps_request_tx_ack) (struct net_device *dev);
+ void (*enter_sleep_state) (struct net_device *dev, u32 th, u32 tl);
+ short (*ps_is_queue_empty) (struct net_device *dev);
+ int (*handle_beacon) (struct net_device *dev, struct ieee80211_beacon *beacon, struct ieee80211_network *network);
+ int (*handle_assoc_response) (struct net_device *dev, struct ieee80211_assoc_response_frame *resp, struct ieee80211_network *network);
+
+
+ /* check whether Tx hw resource available */
+ short (*check_nic_enough_desc)(struct net_device *dev, int queue_index);
+ //added by wb for HT related
+// void (*SwChnlByTimerHandler)(struct net_device *dev, int channel);
+ void (*SetBWModeHandler)(struct net_device *dev, HT_CHANNEL_WIDTH Bandwidth, HT_EXTCHNL_OFFSET Offset);
+// void (*UpdateHalRATRTableHandler)(struct net_device* dev, u8* pMcsRate);
+ bool (*GetNmodeSupportBySecCfg)(struct net_device *dev);
+ void (*SetWirelessMode)(struct net_device *dev, u8 wireless_mode);
+ bool (*GetHalfNmodeSupportByAPsHandler)(struct net_device *dev);
+ void (*InitialGainHandler)(struct net_device *dev, u8 Operation);
+
+ /* This must be the last item so that it points to the data
+ * allocated beyond this structure by alloc_ieee80211 */
+ u8 priv[0];
+};
+
+#define IEEE_A (1<<0)
+#define IEEE_B (1<<1)
+#define IEEE_G (1<<2)
+#define IEEE_N_24G (1<<4)
+#define IEEE_N_5G (1<<5)
+#define IEEE_MODE_MASK (IEEE_A|IEEE_B|IEEE_G)
+
+/* Generate a 802.11 header */
+
+/* Uses the channel change callback directly
+ * instead of [start/stop] scan callbacks
+ */
+#define IEEE_SOFTMAC_SCAN (1<<2)
+
+/* Perform authentication and association handshake */
+#define IEEE_SOFTMAC_ASSOCIATE (1<<3)
+
+/* Generate probe requests */
+#define IEEE_SOFTMAC_PROBERQ (1<<4)
+
+/* Generate respones to probe requests */
+#define IEEE_SOFTMAC_PROBERS (1<<5)
+
+/* The ieee802.11 stack will manages the netif queue
+ * wake/stop for the driver, taking care of 802.11
+ * fragmentation. See softmac.c for details. */
+#define IEEE_SOFTMAC_TX_QUEUE (1<<7)
+
+/* Uses only the softmac_data_hard_start_xmit
+ * even for TX management frames.
+ */
+#define IEEE_SOFTMAC_SINGLE_QUEUE (1<<8)
+
+/* Generate beacons. The stack will enqueue beacons
+ * to the card
+ */
+#define IEEE_SOFTMAC_BEACONS (1<<6)
+
+static inline void *ieee80211_priv(struct net_device *dev)
+{
+ return ((struct ieee80211_device *)netdev_priv(dev))->priv;
+}
+
+static inline int ieee80211_is_empty_essid(const char *essid, int essid_len)
+{
+ /* Single white space is for Linksys APs */
+ if (essid_len == 1 && essid[0] == ' ')
+ return 1;
+
+ /* Otherwise, if the entire essid is 0, we assume it is hidden */
+ while (essid_len) {
+ essid_len--;
+ if (essid[essid_len] != '\0')
+ return 0;
+ }
+
+ return 1;
+}
+
+static inline int ieee80211_is_valid_mode(struct ieee80211_device *ieee, int mode)
+{
+ /*
+ * It is possible for both access points and our device to support
+ * combinations of modes, so as long as there is one valid combination
+ * of ap/device supported modes, then return success
+ *
+ */
+ if ((mode & IEEE_A) &&
+ (ieee->modulation & IEEE80211_OFDM_MODULATION) &&
+ (ieee->freq_band & IEEE80211_52GHZ_BAND))
+ return 1;
+
+ if ((mode & IEEE_G) &&
+ (ieee->modulation & IEEE80211_OFDM_MODULATION) &&
+ (ieee->freq_band & IEEE80211_24GHZ_BAND))
+ return 1;
+
+ if ((mode & IEEE_B) &&
+ (ieee->modulation & IEEE80211_CCK_MODULATION) &&
+ (ieee->freq_band & IEEE80211_24GHZ_BAND))
+ return 1;
+
+ return 0;
+}
+
+static inline int ieee80211_get_hdrlen(u16 fc)
+{
+ int hdrlen = IEEE80211_3ADDR_LEN;
+
+ switch (WLAN_FC_GET_TYPE(fc)) {
+ case IEEE80211_FTYPE_DATA:
+ if ((fc & IEEE80211_FCTL_FROMDS) && (fc & IEEE80211_FCTL_TODS))
+ hdrlen = IEEE80211_4ADDR_LEN; /* Addr4 */
+ if(IEEE80211_QOS_HAS_SEQ(fc))
+ hdrlen += 2; /* QOS ctrl*/
+ break;
+ case IEEE80211_FTYPE_CTL:
+ switch (WLAN_FC_GET_STYPE(fc)) {
+ case IEEE80211_STYPE_CTS:
+ case IEEE80211_STYPE_ACK:
+ hdrlen = IEEE80211_1ADDR_LEN;
+ break;
+ default:
+ hdrlen = IEEE80211_2ADDR_LEN;
+ break;
+ }
+ break;
+ }
+
+ return hdrlen;
+}
+
+static inline u8 *ieee80211_get_payload(struct ieee80211_hdr *hdr)
+{
+ switch (ieee80211_get_hdrlen(le16_to_cpu(hdr->frame_ctl))) {
+ case IEEE80211_1ADDR_LEN:
+ return ((struct ieee80211_hdr_1addr *)hdr)->payload;
+ case IEEE80211_2ADDR_LEN:
+ return ((struct ieee80211_hdr_2addr *)hdr)->payload;
+ case IEEE80211_3ADDR_LEN:
+ return ((struct ieee80211_hdr_3addr *)hdr)->payload;
+ case IEEE80211_4ADDR_LEN:
+ return ((struct ieee80211_hdr_4addr *)hdr)->payload;
+ }
+ return NULL;
+}
+
+static inline int ieee80211_is_ofdm_rate(u8 rate)
+{
+ switch (rate & ~IEEE80211_BASIC_RATE_MASK) {
+ case IEEE80211_OFDM_RATE_6MB:
+ case IEEE80211_OFDM_RATE_9MB:
+ case IEEE80211_OFDM_RATE_12MB:
+ case IEEE80211_OFDM_RATE_18MB:
+ case IEEE80211_OFDM_RATE_24MB:
+ case IEEE80211_OFDM_RATE_36MB:
+ case IEEE80211_OFDM_RATE_48MB:
+ case IEEE80211_OFDM_RATE_54MB:
+ return 1;
+ }
+ return 0;
+}
+
+static inline int ieee80211_is_cck_rate(u8 rate)
+{
+ switch (rate & ~IEEE80211_BASIC_RATE_MASK) {
+ case IEEE80211_CCK_RATE_1MB:
+ case IEEE80211_CCK_RATE_2MB:
+ case IEEE80211_CCK_RATE_5MB:
+ case IEEE80211_CCK_RATE_11MB:
+ return 1;
+ }
+ return 0;
+}
+
+
+/* ieee80211.c */
+extern void free_ieee80211(struct net_device *dev);
+extern struct net_device *alloc_ieee80211(int sizeof_priv);
+
+extern int ieee80211_set_encryption(struct ieee80211_device *ieee);
+
+/* ieee80211_tx.c */
+
+extern int ieee80211_encrypt_fragment(
+ struct ieee80211_device *ieee,
+ struct sk_buff *frag,
+ int hdr_len);
+
+extern int ieee80211_xmit(struct sk_buff *skb,
+ struct net_device *dev);
+extern void ieee80211_txb_free(struct ieee80211_txb *);
+
+
+/* ieee80211_rx.c */
+extern int ieee80211_rx(struct ieee80211_device *ieee, struct sk_buff *skb,
+ struct ieee80211_rx_stats *rx_stats);
+extern void ieee80211_rx_mgt(struct ieee80211_device *ieee,
+ struct ieee80211_hdr_4addr *header,
+ struct ieee80211_rx_stats *stats);
+
+/* ieee80211_wx.c */
+extern int ieee80211_wx_get_scan(struct ieee80211_device *ieee,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *key);
+extern int ieee80211_wx_set_encode(struct ieee80211_device *ieee,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *key);
+extern int ieee80211_wx_get_encode(struct ieee80211_device *ieee,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *key);
+extern int ieee80211_wx_get_encode_ext(struct ieee80211_device *ieee,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra);
+extern int ieee80211_wx_set_encode_ext(struct ieee80211_device *ieee,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra);
+extern int ieee80211_wx_set_auth(struct ieee80211_device *ieee,
+ struct iw_request_info *info,
+ struct iw_param *data, char *extra);
+extern int ieee80211_wx_set_mlme(struct ieee80211_device *ieee,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra);
+extern int ieee80211_wx_set_gen_ie(struct ieee80211_device *ieee, u8 *ie, size_t len);
+
+/* ieee80211_softmac.c */
+extern short ieee80211_is_54g(const struct ieee80211_network *net);
+extern short ieee80211_is_shortslot(const struct ieee80211_network *net);
+extern int ieee80211_rx_frame_softmac(struct ieee80211_device *ieee, struct sk_buff *skb,
+ struct ieee80211_rx_stats *rx_stats, u16 type,
+ u16 stype);
+extern void ieee80211_softmac_new_net(struct ieee80211_device *ieee, struct ieee80211_network *net);
+
+void SendDisassociation(struct ieee80211_device *ieee, u8 *asSta, u8 asRsn);
+extern void ieee80211_softmac_xmit(struct ieee80211_txb *txb, struct ieee80211_device *ieee);
+
+extern void ieee80211_stop_send_beacons(struct ieee80211_device *ieee);
+extern void notify_wx_assoc_event(struct ieee80211_device *ieee);
+extern void ieee80211_softmac_check_all_nets(struct ieee80211_device *ieee);
+extern void ieee80211_start_bss(struct ieee80211_device *ieee);
+extern void ieee80211_start_master_bss(struct ieee80211_device *ieee);
+extern void ieee80211_start_ibss(struct ieee80211_device *ieee);
+extern void ieee80211_softmac_init(struct ieee80211_device *ieee);
+extern void ieee80211_softmac_free(struct ieee80211_device *ieee);
+extern void ieee80211_associate_abort(struct ieee80211_device *ieee);
+extern void ieee80211_disassociate(struct ieee80211_device *ieee);
+extern void ieee80211_stop_scan(struct ieee80211_device *ieee);
+extern void ieee80211_start_scan_syncro(struct ieee80211_device *ieee);
+extern void ieee80211_check_all_nets(struct ieee80211_device *ieee);
+extern void ieee80211_start_protocol(struct ieee80211_device *ieee);
+extern void ieee80211_stop_protocol(struct ieee80211_device *ieee);
+extern void ieee80211_softmac_start_protocol(struct ieee80211_device *ieee);
+extern void ieee80211_softmac_stop_protocol(struct ieee80211_device *ieee);
+extern void ieee80211_reset_queue(struct ieee80211_device *ieee);
+extern void ieee80211_wake_queue(struct ieee80211_device *ieee);
+extern void ieee80211_stop_queue(struct ieee80211_device *ieee);
+extern struct sk_buff *ieee80211_get_beacon(struct ieee80211_device *ieee);
+extern void ieee80211_start_send_beacons(struct ieee80211_device *ieee);
+extern void ieee80211_stop_send_beacons(struct ieee80211_device *ieee);
+extern int ieee80211_wpa_supplicant_ioctl(struct ieee80211_device *ieee, struct iw_point *p);
+extern void notify_wx_assoc_event(struct ieee80211_device *ieee);
+extern void ieee80211_ps_tx_ack(struct ieee80211_device *ieee, short success);
+
+extern void softmac_mgmt_xmit(struct sk_buff *skb, struct ieee80211_device *ieee);
+
+/* ieee80211_crypt_ccmp&tkip&wep.c */
+extern void ieee80211_tkip_null(void);
+extern void ieee80211_wep_null(void);
+extern void ieee80211_ccmp_null(void);
+
+int ieee80211_crypto_init(void);
+void ieee80211_crypto_deinit(void);
+int ieee80211_crypto_tkip_init(void);
+void ieee80211_crypto_tkip_exit(void);
+int ieee80211_crypto_ccmp_init(void);
+void ieee80211_crypto_ccmp_exit(void);
+int ieee80211_crypto_wep_init(void);
+void ieee80211_crypto_wep_exit(void);
+
+/* ieee80211_softmac_wx.c */
+
+extern int ieee80211_wx_get_wap(struct ieee80211_device *ieee,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *ext);
+
+extern int ieee80211_wx_set_wap(struct ieee80211_device *ieee,
+ struct iw_request_info *info,
+ union iwreq_data *awrq,
+ char *extra);
+
+extern int ieee80211_wx_get_essid(struct ieee80211_device *ieee, struct iw_request_info *a,union iwreq_data *wrqu,char *b);
+
+extern int ieee80211_wx_set_rate(struct ieee80211_device *ieee,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra);
+
+extern int ieee80211_wx_get_rate(struct ieee80211_device *ieee,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra);
+
+extern int ieee80211_wx_set_mode(struct ieee80211_device *ieee, struct iw_request_info *a,
+ union iwreq_data *wrqu, char *b);
+
+extern int ieee80211_wx_set_scan(struct ieee80211_device *ieee, struct iw_request_info *a,
+ union iwreq_data *wrqu, char *b);
+
+extern int ieee80211_wx_set_essid(struct ieee80211_device *ieee,
+ struct iw_request_info *a,
+ union iwreq_data *wrqu, char *extra);
+
+extern int ieee80211_wx_get_mode(struct ieee80211_device *ieee, struct iw_request_info *a,
+ union iwreq_data *wrqu, char *b);
+
+extern int ieee80211_wx_set_freq(struct ieee80211_device *ieee, struct iw_request_info *a,
+ union iwreq_data *wrqu, char *b);
+
+extern int ieee80211_wx_get_freq(struct ieee80211_device *ieee, struct iw_request_info *a,
+ union iwreq_data *wrqu, char *b);
+
+/* ieee80211_module.c */
+extern int ieee80211_debug_init(void);
+extern void ieee80211_debug_exit(void);
+
+//extern void ieee80211_wx_sync_scan_wq(struct ieee80211_device *ieee);
+extern void ieee80211_wx_sync_scan_wq(struct work_struct *work);
+
+
+extern int ieee80211_wx_set_rawtx(struct ieee80211_device *ieee,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra);
+
+extern int ieee80211_wx_get_name(struct ieee80211_device *ieee,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra);
+
+extern int ieee80211_wx_set_power(struct ieee80211_device *ieee,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra);
+
+extern int ieee80211_wx_get_power(struct ieee80211_device *ieee,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra);
+
+extern int ieee80211_wx_set_rts(struct ieee80211_device *ieee,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra);
+
+extern int ieee80211_wx_get_rts(struct ieee80211_device *ieee,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra);
+//HT
+#define MAX_RECEIVE_BUFFER_SIZE 9100 //
+extern void HTDebugHTCapability(u8 *CapIE, u8 *TitleString );
+extern void HTDebugHTInfo(u8 *InfoIE, u8 *TitleString);
+
+void HTSetConnectBwMode(struct ieee80211_device *ieee, HT_CHANNEL_WIDTH Bandwidth, HT_EXTCHNL_OFFSET Offset);
+extern void HTUpdateDefaultSetting(struct ieee80211_device *ieee);
+extern void HTConstructCapabilityElement(struct ieee80211_device *ieee, u8 *posHTCap, u8 *len, u8 isEncrypt);
+extern void HTConstructInfoElement(struct ieee80211_device *ieee, u8 *posHTInfo, u8 *len, u8 isEncrypt);
+extern void HTConstructRT2RTAggElement(struct ieee80211_device *ieee, u8 *posRT2RTAgg, u8 *len);
+extern void HTOnAssocRsp(struct ieee80211_device *ieee);
+extern void HTInitializeHTInfo(struct ieee80211_device *ieee);
+extern void HTInitializeBssDesc(PBSS_HT pBssHT);
+extern void HTResetSelfAndSavePeerSetting(struct ieee80211_device *ieee, struct ieee80211_network *pNetwork);
+extern void HTUpdateSelfAndPeerSetting(struct ieee80211_device *ieee, struct ieee80211_network *pNetwork);
+extern u8 HTGetHighestMCSRate(struct ieee80211_device *ieee, u8 *pMCSRateSet, u8 *pMCSFilter);
+extern u8 MCS_FILTER_ALL[];
+extern u16 MCS_DATA_RATE[2][2][77] ;
+extern u8 HTCCheck(struct ieee80211_device *ieee, u8 *pFrame);
+//extern void HTSetConnectBwModeCallback(unsigned long data);
+extern void HTResetIOTSetting(PRT_HIGH_THROUGHPUT pHTInfo);
+extern bool IsHTHalfNmodeAPs(struct ieee80211_device *ieee);
+extern u16 HTHalfMcsToDataRate(struct ieee80211_device *ieee, u8 nMcsRate);
+extern u16 HTMcsToDataRate(struct ieee80211_device *ieee, u8 nMcsRate);
+extern u16 TxCountToDataRate(struct ieee80211_device *ieee, u8 nDataRate);
+//function in BAPROC.c
+extern int ieee80211_rx_ADDBAReq(struct ieee80211_device *ieee,
+ struct sk_buff *skb);
+extern int ieee80211_rx_ADDBARsp(struct ieee80211_device *ieee,
+ struct sk_buff *skb);
+extern int ieee80211_rx_DELBA(struct ieee80211_device *ieee,struct sk_buff *skb);
+extern void TsInitAddBA(struct ieee80211_device *ieee, PTX_TS_RECORD pTS,
+ u8 Policy, u8 bOverwritePending);
+extern void TsInitDelBA(struct ieee80211_device *ieee,
+ PTS_COMMON_INFO pTsCommonInfo, TR_SELECT TxRxSelect);
+extern void BaSetupTimeOut(unsigned long data);
+extern void TxBaInactTimeout(unsigned long data);
+extern void RxBaInactTimeout(unsigned long data);
+extern void ResetBaEntry(PBA_RECORD pBA);
+//function in TS.c
+extern bool GetTs(
+ struct ieee80211_device *ieee,
+ PTS_COMMON_INFO *ppTS,
+ u8 *Addr,
+ u8 TID,
+ TR_SELECT TxRxSelect, //Rx:1, Tx:0
+ bool bAddNewTs
+ );
+extern void TSInitialize(struct ieee80211_device *ieee);
+extern void TsStartAddBaProcess(struct ieee80211_device *ieee, PTX_TS_RECORD pTxTS);
+extern void RemovePeerTS(struct ieee80211_device *ieee, u8 *Addr);
+extern void RemoveAllTS(struct ieee80211_device *ieee);
+void ieee80211_softmac_scan_syncro(struct ieee80211_device *ieee);
+
+extern const long ieee80211_wlan_frequencies[];
+
+static inline void ieee80211_increment_scans(struct ieee80211_device *ieee)
+{
+ ieee->scans++;
+}
+
+static inline int ieee80211_get_scans(struct ieee80211_device *ieee)
+{
+ return ieee->scans;
+}
+
+static inline const char *escape_essid(const char *essid, u8 essid_len) {
+ static char escaped[IW_ESSID_MAX_SIZE * 2 + 1];
+
+ if (ieee80211_is_empty_essid(essid, essid_len)) {
+ memcpy(escaped, "<hidden>", sizeof("<hidden>"));
+ return escaped;
+ }
+
+ snprintf(escaped, sizeof(escaped), "%*pEn", essid_len, essid);
+ return escaped;
+}
+
+/* For the function is more related to hardware setting, it's better to use the
+ * ieee handler to refer to it.
+ */
+extern short check_nic_enough_desc(struct net_device *dev, int queue_index);
+extern int ieee80211_data_xmit(struct sk_buff *skb, struct net_device *dev);
+extern int ieee80211_parse_info_param(struct ieee80211_device *ieee,
+ struct ieee80211_info_element *info_element,
+ u16 length,
+ struct ieee80211_network *network,
+ struct ieee80211_rx_stats *stats);
+
+void ieee80211_indicate_packets(struct ieee80211_device *ieee, struct ieee80211_rxb **prxbIndicateArray,u8 index);
+#define RT_ASOC_RETRY_LIMIT 5
+#endif /* IEEE80211_H */
diff --git a/drivers/staging/rtl8192u/ieee80211/ieee80211_crypt.c b/drivers/staging/rtl8192u/ieee80211/ieee80211_crypt.c
new file mode 100644
index 000000000..3995620b3
--- /dev/null
+++ b/drivers/staging/rtl8192u/ieee80211/ieee80211_crypt.c
@@ -0,0 +1,240 @@
+/*
+ * Host AP crypto routines
+ *
+ * Copyright (c) 2002-2003, Jouni Malinen <jkmaline@cc.hut.fi>
+ * Portions Copyright (C) 2004, Intel Corporation <jketreno@linux.intel.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation. See README and COPYING for
+ * more details.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+
+#include "ieee80211.h"
+
+MODULE_AUTHOR("Jouni Malinen");
+MODULE_DESCRIPTION("HostAP crypto");
+MODULE_LICENSE("GPL");
+
+struct ieee80211_crypto_alg {
+ struct list_head list;
+ struct ieee80211_crypto_ops *ops;
+};
+
+
+struct ieee80211_crypto {
+ struct list_head algs;
+ spinlock_t lock;
+};
+
+static struct ieee80211_crypto *hcrypt;
+
+void ieee80211_crypt_deinit_entries(struct ieee80211_device *ieee,
+ int force)
+{
+ struct list_head *ptr, *n;
+ struct ieee80211_crypt_data *entry;
+
+ for (ptr = ieee->crypt_deinit_list.next, n = ptr->next;
+ ptr != &ieee->crypt_deinit_list; ptr = n, n = ptr->next) {
+ entry = list_entry(ptr, struct ieee80211_crypt_data, list);
+
+ if (atomic_read(&entry->refcnt) != 0 && !force)
+ continue;
+
+ list_del(ptr);
+
+ if (entry->ops)
+ entry->ops->deinit(entry->priv);
+ kfree(entry);
+ }
+}
+
+void ieee80211_crypt_deinit_handler(unsigned long data)
+{
+ struct ieee80211_device *ieee = (struct ieee80211_device *)data;
+ unsigned long flags;
+
+ spin_lock_irqsave(&ieee->lock, flags);
+ ieee80211_crypt_deinit_entries(ieee, 0);
+ if (!list_empty(&ieee->crypt_deinit_list)) {
+ netdev_dbg(ieee->dev, "%s: entries remaining in delayed crypt deletion list\n",
+ ieee->dev->name);
+ ieee->crypt_deinit_timer.expires = jiffies + HZ;
+ add_timer(&ieee->crypt_deinit_timer);
+ }
+ spin_unlock_irqrestore(&ieee->lock, flags);
+
+}
+
+void ieee80211_crypt_delayed_deinit(struct ieee80211_device *ieee,
+ struct ieee80211_crypt_data **crypt)
+{
+ struct ieee80211_crypt_data *tmp;
+ unsigned long flags;
+
+ if (*crypt == NULL)
+ return;
+
+ tmp = *crypt;
+ *crypt = NULL;
+
+ /* must not run ops->deinit() while there may be pending encrypt or
+ * decrypt operations. Use a list of delayed deinits to avoid needing
+ * locking. */
+
+ spin_lock_irqsave(&ieee->lock, flags);
+ list_add(&tmp->list, &ieee->crypt_deinit_list);
+ if (!timer_pending(&ieee->crypt_deinit_timer)) {
+ ieee->crypt_deinit_timer.expires = jiffies + HZ;
+ add_timer(&ieee->crypt_deinit_timer);
+ }
+ spin_unlock_irqrestore(&ieee->lock, flags);
+}
+
+int ieee80211_register_crypto_ops(struct ieee80211_crypto_ops *ops)
+{
+ unsigned long flags;
+ struct ieee80211_crypto_alg *alg;
+
+ if (hcrypt == NULL)
+ return -1;
+
+ alg = kzalloc(sizeof(*alg), GFP_KERNEL);
+ if (alg == NULL)
+ return -ENOMEM;
+
+ alg->ops = ops;
+
+ spin_lock_irqsave(&hcrypt->lock, flags);
+ list_add(&alg->list, &hcrypt->algs);
+ spin_unlock_irqrestore(&hcrypt->lock, flags);
+
+ pr_debug("ieee80211_crypt: registered algorithm '%s'\n",
+ ops->name);
+
+ return 0;
+}
+
+int ieee80211_unregister_crypto_ops(struct ieee80211_crypto_ops *ops)
+{
+ unsigned long flags;
+ struct list_head *ptr;
+ struct ieee80211_crypto_alg *del_alg = NULL;
+
+ if (hcrypt == NULL)
+ return -1;
+
+ spin_lock_irqsave(&hcrypt->lock, flags);
+ for (ptr = hcrypt->algs.next; ptr != &hcrypt->algs; ptr = ptr->next) {
+ struct ieee80211_crypto_alg *alg =
+ (struct ieee80211_crypto_alg *) ptr;
+ if (alg->ops == ops) {
+ list_del(&alg->list);
+ del_alg = alg;
+ break;
+ }
+ }
+ spin_unlock_irqrestore(&hcrypt->lock, flags);
+
+ if (del_alg) {
+ pr_debug("ieee80211_crypt: unregistered algorithm '%s'\n",
+ ops->name);
+ kfree(del_alg);
+ }
+
+ return del_alg ? 0 : -1;
+}
+
+
+struct ieee80211_crypto_ops *ieee80211_get_crypto_ops(const char *name)
+{
+ unsigned long flags;
+ struct list_head *ptr;
+ struct ieee80211_crypto_alg *found_alg = NULL;
+
+ if (hcrypt == NULL)
+ return NULL;
+
+ spin_lock_irqsave(&hcrypt->lock, flags);
+ for (ptr = hcrypt->algs.next; ptr != &hcrypt->algs; ptr = ptr->next) {
+ struct ieee80211_crypto_alg *alg =
+ (struct ieee80211_crypto_alg *) ptr;
+ if (strcmp(alg->ops->name, name) == 0) {
+ found_alg = alg;
+ break;
+ }
+ }
+ spin_unlock_irqrestore(&hcrypt->lock, flags);
+
+ if (found_alg)
+ return found_alg->ops;
+ else
+ return NULL;
+}
+
+
+static void *ieee80211_crypt_null_init(int keyidx) { return (void *) 1; }
+static void ieee80211_crypt_null_deinit(void *priv) {}
+
+static struct ieee80211_crypto_ops ieee80211_crypt_null = {
+ .name = "NULL",
+ .init = ieee80211_crypt_null_init,
+ .deinit = ieee80211_crypt_null_deinit,
+ .encrypt_mpdu = NULL,
+ .decrypt_mpdu = NULL,
+ .encrypt_msdu = NULL,
+ .decrypt_msdu = NULL,
+ .set_key = NULL,
+ .get_key = NULL,
+ .extra_prefix_len = 0,
+ .extra_postfix_len = 0,
+ .owner = THIS_MODULE,
+};
+
+int __init ieee80211_crypto_init(void)
+{
+ int ret = -ENOMEM;
+
+ hcrypt = kzalloc(sizeof(*hcrypt), GFP_KERNEL);
+ if (!hcrypt)
+ goto out;
+
+ INIT_LIST_HEAD(&hcrypt->algs);
+ spin_lock_init(&hcrypt->lock);
+
+ ret = ieee80211_register_crypto_ops(&ieee80211_crypt_null);
+ if (ret < 0) {
+ kfree(hcrypt);
+ hcrypt = NULL;
+ }
+out:
+ return ret;
+}
+
+void __exit ieee80211_crypto_deinit(void)
+{
+ struct list_head *ptr, *n;
+
+ if (hcrypt == NULL)
+ return;
+
+ for (ptr = hcrypt->algs.next, n = ptr->next; ptr != &hcrypt->algs;
+ ptr = n, n = ptr->next) {
+ struct ieee80211_crypto_alg *alg =
+ (struct ieee80211_crypto_alg *) ptr;
+ list_del(ptr);
+ pr_debug("ieee80211_crypt: unregistered algorithm '%s' (deinit)\n",
+ alg->ops->name);
+ kfree(alg);
+ }
+
+ kfree(hcrypt);
+}
diff --git a/drivers/staging/rtl8192u/ieee80211/ieee80211_crypt.h b/drivers/staging/rtl8192u/ieee80211/ieee80211_crypt.h
new file mode 100644
index 000000000..0b4ea4319
--- /dev/null
+++ b/drivers/staging/rtl8192u/ieee80211/ieee80211_crypt.h
@@ -0,0 +1,86 @@
+/*
+ * Original code based on Host AP (software wireless LAN access point) driver
+ * for Intersil Prism2/2.5/3.
+ *
+ * Copyright (c) 2001-2002, SSH Communications Security Corp and Jouni Malinen
+ * <jkmaline@cc.hut.fi>
+ * Copyright (c) 2002-2003, Jouni Malinen <jkmaline@cc.hut.fi>
+ *
+ * Adaption to a generic IEEE 802.11 stack by James Ketrenos
+ * <jketreno@linux.intel.com>
+ *
+ * Copyright (c) 2004, Intel Corporation
+ *
+ * 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. See README and COPYING for
+ * more details.
+ */
+
+/*
+ * This file defines the interface to the ieee80211 crypto module.
+ */
+#ifndef IEEE80211_CRYPT_H
+#define IEEE80211_CRYPT_H
+
+#include <linux/skbuff.h>
+
+struct ieee80211_crypto_ops {
+ const char *name;
+
+ /* init new crypto context (e.g., allocate private data space,
+ * select IV, etc.); returns NULL on failure or pointer to allocated
+ * private data on success */
+ void * (*init)(int keyidx);
+
+ /* deinitialize crypto context and free allocated private data */
+ void (*deinit)(void *priv);
+
+ /* encrypt/decrypt return < 0 on error or >= 0 on success. The return
+ * value from decrypt_mpdu is passed as the keyidx value for
+ * decrypt_msdu. skb must have enough head and tail room for the
+ * encryption; if not, error will be returned; these functions are
+ * called for all MPDUs (i.e., fragments).
+ */
+ int (*encrypt_mpdu)(struct sk_buff *skb, int hdr_len, void *priv);
+ int (*decrypt_mpdu)(struct sk_buff *skb, int hdr_len, void *priv);
+
+ /* These functions are called for full MSDUs, i.e. full frames.
+ * These can be NULL if full MSDU operations are not needed. */
+ int (*encrypt_msdu)(struct sk_buff *skb, int hdr_len, void *priv);
+ int (*decrypt_msdu)(struct sk_buff *skb, int keyidx, int hdr_len,
+ void *priv);
+
+ int (*set_key)(void *key, int len, u8 *seq, void *priv);
+ int (*get_key)(void *key, int len, u8 *seq, void *priv);
+
+ /* procfs handler for printing out key information and possible
+ * statistics */
+ char * (*print_stats)(char *p, void *priv);
+
+ /* maximum number of bytes added by encryption; encrypt buf is
+ * allocated with extra_prefix_len bytes, copy of in_buf, and
+ * extra_postfix_len; encrypt need not use all this space, but
+ * the result must start at the beginning of the buffer and correct
+ * length must be returned */
+ int extra_prefix_len, extra_postfix_len;
+
+ struct module *owner;
+};
+
+struct ieee80211_crypt_data {
+ struct list_head list; /* delayed deletion list */
+ struct ieee80211_crypto_ops *ops;
+ void *priv;
+ atomic_t refcnt;
+};
+
+int ieee80211_register_crypto_ops(struct ieee80211_crypto_ops *ops);
+int ieee80211_unregister_crypto_ops(struct ieee80211_crypto_ops *ops);
+struct ieee80211_crypto_ops *ieee80211_get_crypto_ops(const char *name);
+void ieee80211_crypt_deinit_entries(struct ieee80211_device *, int);
+void ieee80211_crypt_deinit_handler(unsigned long);
+void ieee80211_crypt_delayed_deinit(struct ieee80211_device *ieee,
+ struct ieee80211_crypt_data **crypt);
+
+#endif
diff --git a/drivers/staging/rtl8192u/ieee80211/ieee80211_crypt_ccmp.c b/drivers/staging/rtl8192u/ieee80211/ieee80211_crypt_ccmp.c
new file mode 100644
index 000000000..788704b80
--- /dev/null
+++ b/drivers/staging/rtl8192u/ieee80211/ieee80211_crypt_ccmp.c
@@ -0,0 +1,474 @@
+/*
+ * Host AP crypt: host-based CCMP encryption implementation for Host AP driver
+ *
+ * Copyright (c) 2003-2004, Jouni Malinen <jkmaline@cc.hut.fi>
+ *
+ * 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. See README and COPYING for
+ * more details.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/random.h>
+#include <linux/skbuff.h>
+#include <linux/netdevice.h>
+#include <linux/if_ether.h>
+#include <linux/if_arp.h>
+#include <linux/string.h>
+#include <linux/wireless.h>
+
+#include "ieee80211.h"
+
+#include <linux/crypto.h>
+ #include <linux/scatterlist.h>
+
+MODULE_AUTHOR("Jouni Malinen");
+MODULE_DESCRIPTION("Host AP crypt: CCMP");
+MODULE_LICENSE("GPL");
+
+#define AES_BLOCK_LEN 16
+#define CCMP_HDR_LEN 8
+#define CCMP_MIC_LEN 8
+#define CCMP_TK_LEN 16
+#define CCMP_PN_LEN 6
+
+struct ieee80211_ccmp_data {
+ u8 key[CCMP_TK_LEN];
+ int key_set;
+
+ u8 tx_pn[CCMP_PN_LEN];
+ u8 rx_pn[CCMP_PN_LEN];
+
+ u32 dot11RSNAStatsCCMPFormatErrors;
+ u32 dot11RSNAStatsCCMPReplays;
+ u32 dot11RSNAStatsCCMPDecryptErrors;
+
+ int key_idx;
+
+ struct crypto_tfm *tfm;
+
+ /* scratch buffers for virt_to_page() (crypto API) */
+ u8 tx_b0[AES_BLOCK_LEN], tx_b[AES_BLOCK_LEN],
+ tx_e[AES_BLOCK_LEN], tx_s0[AES_BLOCK_LEN];
+ u8 rx_b0[AES_BLOCK_LEN], rx_b[AES_BLOCK_LEN], rx_a[AES_BLOCK_LEN];
+};
+
+static void ieee80211_ccmp_aes_encrypt(struct crypto_tfm *tfm,
+ const u8 pt[16], u8 ct[16])
+{
+ crypto_cipher_encrypt_one((void *)tfm, ct, pt);
+}
+
+static void *ieee80211_ccmp_init(int key_idx)
+{
+ struct ieee80211_ccmp_data *priv;
+
+ priv = kzalloc(sizeof(*priv), GFP_ATOMIC);
+ if (priv == NULL)
+ goto fail;
+ priv->key_idx = key_idx;
+
+ priv->tfm = (void *)crypto_alloc_cipher("aes", 0, CRYPTO_ALG_ASYNC);
+ if (IS_ERR(priv->tfm)) {
+ printk(KERN_DEBUG "ieee80211_crypt_ccmp: could not allocate "
+ "crypto API aes\n");
+ priv->tfm = NULL;
+ goto fail;
+ }
+
+ return priv;
+
+fail:
+ if (priv) {
+ if (priv->tfm)
+ crypto_free_cipher((void *)priv->tfm);
+ kfree(priv);
+ }
+
+ return NULL;
+}
+
+
+static void ieee80211_ccmp_deinit(void *priv)
+{
+ struct ieee80211_ccmp_data *_priv = priv;
+
+ if (_priv && _priv->tfm)
+ crypto_free_cipher((void *)_priv->tfm);
+ kfree(priv);
+}
+
+
+static inline void xor_block(u8 *b, u8 *a, size_t len)
+{
+ int i;
+ for (i = 0; i < len; i++)
+ b[i] ^= a[i];
+}
+
+
+
+static void ccmp_init_blocks(struct crypto_tfm *tfm,
+ struct ieee80211_hdr_4addr *hdr,
+ u8 *pn, size_t dlen, u8 *b0, u8 *auth,
+ u8 *s0)
+{
+ u8 *pos, qc = 0;
+ size_t aad_len;
+ u16 fc;
+ int a4_included, qc_included;
+ u8 aad[2 * AES_BLOCK_LEN];
+
+ fc = le16_to_cpu(hdr->frame_ctl);
+ a4_included = ((fc & (IEEE80211_FCTL_TODS | IEEE80211_FCTL_FROMDS)) ==
+ (IEEE80211_FCTL_TODS | IEEE80211_FCTL_FROMDS));
+ /*
+ qc_included = ((WLAN_FC_GET_TYPE(fc) == IEEE80211_FTYPE_DATA) &&
+ (WLAN_FC_GET_STYPE(fc) & 0x08));
+ */
+ /* fixed by David :2006.9.6 */
+ qc_included = (WLAN_FC_GET_TYPE(fc) == IEEE80211_FTYPE_DATA) &&
+ (WLAN_FC_GET_STYPE(fc) & 0x80);
+ aad_len = 22;
+ if (a4_included)
+ aad_len += 6;
+ if (qc_included) {
+ pos = (u8 *) &hdr->addr4;
+ if (a4_included)
+ pos += 6;
+ qc = *pos & 0x0f;
+ aad_len += 2;
+ }
+ /* CCM Initial Block:
+ * Flag (Include authentication header, M=3 (8-octet MIC),
+ * L=1 (2-octet Dlen))
+ * Nonce: 0x00 | A2 | PN
+ * Dlen */
+ b0[0] = 0x59;
+ b0[1] = qc;
+ memcpy(b0 + 2, hdr->addr2, ETH_ALEN);
+ memcpy(b0 + 8, pn, CCMP_PN_LEN);
+ b0[14] = (dlen >> 8) & 0xff;
+ b0[15] = dlen & 0xff;
+
+ /* AAD:
+ * FC with bits 4..6 and 11..13 masked to zero; 14 is always one
+ * A1 | A2 | A3
+ * SC with bits 4..15 (seq#) masked to zero
+ * A4 (if present)
+ * QC (if present)
+ */
+ pos = (u8 *) hdr;
+ aad[0] = 0; /* aad_len >> 8 */
+ aad[1] = aad_len & 0xff;
+ aad[2] = pos[0] & 0x8f;
+ aad[3] = pos[1] & 0xc7;
+ memcpy(aad + 4, hdr->addr1, 3 * ETH_ALEN);
+ pos = (u8 *) &hdr->seq_ctl;
+ aad[22] = pos[0] & 0x0f;
+ aad[23] = 0; /* all bits masked */
+ memset(aad + 24, 0, 8);
+ if (a4_included)
+ memcpy(aad + 24, hdr->addr4, ETH_ALEN);
+ if (qc_included) {
+ aad[a4_included ? 30 : 24] = qc;
+ /* rest of QC masked */
+ }
+
+ /* Start with the first block and AAD */
+ ieee80211_ccmp_aes_encrypt(tfm, b0, auth);
+ xor_block(auth, aad, AES_BLOCK_LEN);
+ ieee80211_ccmp_aes_encrypt(tfm, auth, auth);
+ xor_block(auth, &aad[AES_BLOCK_LEN], AES_BLOCK_LEN);
+ ieee80211_ccmp_aes_encrypt(tfm, auth, auth);
+ b0[0] &= 0x07;
+ b0[14] = b0[15] = 0;
+ ieee80211_ccmp_aes_encrypt(tfm, b0, s0);
+}
+
+
+
+static int ieee80211_ccmp_encrypt(struct sk_buff *skb, int hdr_len, void *priv)
+{
+ struct ieee80211_ccmp_data *key = priv;
+ int data_len, i;
+ u8 *pos;
+ struct ieee80211_hdr_4addr *hdr;
+ cb_desc *tcb_desc = (cb_desc *)(skb->cb + MAX_DEV_ADDR_SIZE);
+
+ if (skb_headroom(skb) < CCMP_HDR_LEN ||
+ skb_tailroom(skb) < CCMP_MIC_LEN ||
+ skb->len < hdr_len)
+ return -1;
+
+ data_len = skb->len - hdr_len;
+ pos = skb_push(skb, CCMP_HDR_LEN);
+ memmove(pos, pos + CCMP_HDR_LEN, hdr_len);
+ pos += hdr_len;
+ /* mic = skb_put(skb, CCMP_MIC_LEN); */
+
+ i = CCMP_PN_LEN - 1;
+ while (i >= 0) {
+ key->tx_pn[i]++;
+ if (key->tx_pn[i] != 0)
+ break;
+ i--;
+ }
+
+ *pos++ = key->tx_pn[5];
+ *pos++ = key->tx_pn[4];
+ *pos++ = 0;
+ *pos++ = (key->key_idx << 6) | (1 << 5) /* Ext IV included */;
+ *pos++ = key->tx_pn[3];
+ *pos++ = key->tx_pn[2];
+ *pos++ = key->tx_pn[1];
+ *pos++ = key->tx_pn[0];
+
+
+ hdr = (struct ieee80211_hdr_4addr *) skb->data;
+ if (!tcb_desc->bHwSec)
+ {
+ int blocks, last, len;
+ u8 *mic;
+ u8 *b0 = key->tx_b0;
+ u8 *b = key->tx_b;
+ u8 *e = key->tx_e;
+ u8 *s0 = key->tx_s0;
+
+ /* mic is moved to here by john */
+ mic = skb_put(skb, CCMP_MIC_LEN);
+
+ ccmp_init_blocks(key->tfm, hdr, key->tx_pn, data_len, b0, b, s0);
+
+ blocks = (data_len + AES_BLOCK_LEN - 1) / AES_BLOCK_LEN;
+ last = data_len % AES_BLOCK_LEN;
+
+ for (i = 1; i <= blocks; i++) {
+ len = (i == blocks && last) ? last : AES_BLOCK_LEN;
+ /* Authentication */
+ xor_block(b, pos, len);
+ ieee80211_ccmp_aes_encrypt(key->tfm, b, b);
+ /* Encryption, with counter */
+ b0[14] = (i >> 8) & 0xff;
+ b0[15] = i & 0xff;
+ ieee80211_ccmp_aes_encrypt(key->tfm, b0, e);
+ xor_block(pos, e, len);
+ pos += len;
+ }
+
+ for (i = 0; i < CCMP_MIC_LEN; i++)
+ mic[i] = b[i] ^ s0[i];
+ }
+ return 0;
+}
+
+
+static int ieee80211_ccmp_decrypt(struct sk_buff *skb, int hdr_len, void *priv)
+{
+ struct ieee80211_ccmp_data *key = priv;
+ u8 keyidx, *pos;
+ struct ieee80211_hdr_4addr *hdr;
+ cb_desc *tcb_desc = (cb_desc *)(skb->cb + MAX_DEV_ADDR_SIZE);
+ u8 pn[6];
+
+ if (skb->len < hdr_len + CCMP_HDR_LEN + CCMP_MIC_LEN) {
+ key->dot11RSNAStatsCCMPFormatErrors++;
+ return -1;
+ }
+
+ hdr = (struct ieee80211_hdr_4addr *) skb->data;
+ pos = skb->data + hdr_len;
+ keyidx = pos[3];
+ if (!(keyidx & (1 << 5))) {
+ if (net_ratelimit()) {
+ printk(KERN_DEBUG "CCMP: received packet without ExtIV"
+ " flag from %pM\n", hdr->addr2);
+ }
+ key->dot11RSNAStatsCCMPFormatErrors++;
+ return -2;
+ }
+ keyidx >>= 6;
+ if (key->key_idx != keyidx) {
+ printk(KERN_DEBUG "CCMP: RX tkey->key_idx=%d frame "
+ "keyidx=%d priv=%p\n", key->key_idx, keyidx, priv);
+ return -6;
+ }
+ if (!key->key_set) {
+ if (net_ratelimit()) {
+ printk(KERN_DEBUG "CCMP: received packet from %pM"
+ " with keyid=%d that does not have a configured"
+ " key\n", hdr->addr2, keyidx);
+ }
+ return -3;
+ }
+
+ pn[0] = pos[7];
+ pn[1] = pos[6];
+ pn[2] = pos[5];
+ pn[3] = pos[4];
+ pn[4] = pos[1];
+ pn[5] = pos[0];
+ pos += 8;
+
+ if (memcmp(pn, key->rx_pn, CCMP_PN_LEN) <= 0) {
+ if (net_ratelimit()) {
+ printk(KERN_DEBUG "CCMP: replay detected: STA=%pM"
+ " previous PN %pm received PN %pm\n",
+ hdr->addr2, key->rx_pn, pn);
+ }
+ key->dot11RSNAStatsCCMPReplays++;
+ return -4;
+ }
+ if (!tcb_desc->bHwSec)
+ {
+ size_t data_len = skb->len - hdr_len - CCMP_HDR_LEN - CCMP_MIC_LEN;
+ u8 *mic = skb->data + skb->len - CCMP_MIC_LEN;
+ u8 *b0 = key->rx_b0;
+ u8 *b = key->rx_b;
+ u8 *a = key->rx_a;
+ int i, blocks, last, len;
+
+
+ ccmp_init_blocks(key->tfm, hdr, pn, data_len, b0, a, b);
+ xor_block(mic, b, CCMP_MIC_LEN);
+
+ blocks = (data_len + AES_BLOCK_LEN - 1) / AES_BLOCK_LEN;
+ last = data_len % AES_BLOCK_LEN;
+
+ for (i = 1; i <= blocks; i++) {
+ len = (i == blocks && last) ? last : AES_BLOCK_LEN;
+ /* Decrypt, with counter */
+ b0[14] = (i >> 8) & 0xff;
+ b0[15] = i & 0xff;
+ ieee80211_ccmp_aes_encrypt(key->tfm, b0, b);
+ xor_block(pos, b, len);
+ /* Authentication */
+ xor_block(a, pos, len);
+ ieee80211_ccmp_aes_encrypt(key->tfm, a, a);
+ pos += len;
+ }
+
+ if (memcmp(mic, a, CCMP_MIC_LEN) != 0) {
+ if (net_ratelimit()) {
+ printk(KERN_DEBUG "CCMP: decrypt failed: STA="
+ "%pM\n", hdr->addr2);
+ }
+ key->dot11RSNAStatsCCMPDecryptErrors++;
+ return -5;
+ }
+
+ memcpy(key->rx_pn, pn, CCMP_PN_LEN);
+ }
+ /* Remove hdr and MIC */
+ memmove(skb->data + CCMP_HDR_LEN, skb->data, hdr_len);
+ skb_pull(skb, CCMP_HDR_LEN);
+ skb_trim(skb, skb->len - CCMP_MIC_LEN);
+
+ return keyidx;
+}
+
+
+static int ieee80211_ccmp_set_key(void *key, int len, u8 *seq, void *priv)
+{
+ struct ieee80211_ccmp_data *data = priv;
+ int keyidx;
+ struct crypto_tfm *tfm = data->tfm;
+
+ keyidx = data->key_idx;
+ memset(data, 0, sizeof(*data));
+ data->key_idx = keyidx;
+ data->tfm = tfm;
+ if (len == CCMP_TK_LEN) {
+ memcpy(data->key, key, CCMP_TK_LEN);
+ data->key_set = 1;
+ if (seq) {
+ data->rx_pn[0] = seq[5];
+ data->rx_pn[1] = seq[4];
+ data->rx_pn[2] = seq[3];
+ data->rx_pn[3] = seq[2];
+ data->rx_pn[4] = seq[1];
+ data->rx_pn[5] = seq[0];
+ }
+ crypto_cipher_setkey((void *)data->tfm, data->key, CCMP_TK_LEN);
+ } else if (len == 0)
+ data->key_set = 0;
+ else
+ return -1;
+
+ return 0;
+}
+
+
+static int ieee80211_ccmp_get_key(void *key, int len, u8 *seq, void *priv)
+{
+ struct ieee80211_ccmp_data *data = priv;
+
+ if (len < CCMP_TK_LEN)
+ return -1;
+
+ if (!data->key_set)
+ return 0;
+ memcpy(key, data->key, CCMP_TK_LEN);
+
+ if (seq) {
+ seq[0] = data->tx_pn[5];
+ seq[1] = data->tx_pn[4];
+ seq[2] = data->tx_pn[3];
+ seq[3] = data->tx_pn[2];
+ seq[4] = data->tx_pn[1];
+ seq[5] = data->tx_pn[0];
+ }
+
+ return CCMP_TK_LEN;
+}
+
+
+static char *ieee80211_ccmp_print_stats(char *p, void *priv)
+{
+ struct ieee80211_ccmp_data *ccmp = priv;
+ p += sprintf(p, "key[%d] alg=CCMP key_set=%d "
+ "tx_pn=%pm rx_pn=%pm "
+ "format_errors=%d replays=%d decrypt_errors=%d\n",
+ ccmp->key_idx, ccmp->key_set,
+ ccmp->tx_pn, ccmp->rx_pn,
+ ccmp->dot11RSNAStatsCCMPFormatErrors,
+ ccmp->dot11RSNAStatsCCMPReplays,
+ ccmp->dot11RSNAStatsCCMPDecryptErrors);
+
+ return p;
+}
+
+void ieee80211_ccmp_null(void)
+{
+ /* printk("============>%s()\n", __func__); */
+ return;
+}
+
+static struct ieee80211_crypto_ops ieee80211_crypt_ccmp = {
+ .name = "CCMP",
+ .init = ieee80211_ccmp_init,
+ .deinit = ieee80211_ccmp_deinit,
+ .encrypt_mpdu = ieee80211_ccmp_encrypt,
+ .decrypt_mpdu = ieee80211_ccmp_decrypt,
+ .encrypt_msdu = NULL,
+ .decrypt_msdu = NULL,
+ .set_key = ieee80211_ccmp_set_key,
+ .get_key = ieee80211_ccmp_get_key,
+ .print_stats = ieee80211_ccmp_print_stats,
+ .extra_prefix_len = CCMP_HDR_LEN,
+ .extra_postfix_len = CCMP_MIC_LEN,
+ .owner = THIS_MODULE,
+};
+
+int __init ieee80211_crypto_ccmp_init(void)
+{
+ return ieee80211_register_crypto_ops(&ieee80211_crypt_ccmp);
+}
+
+void __exit ieee80211_crypto_ccmp_exit(void)
+{
+ ieee80211_unregister_crypto_ops(&ieee80211_crypt_ccmp);
+}
diff --git a/drivers/staging/rtl8192u/ieee80211/ieee80211_crypt_tkip.c b/drivers/staging/rtl8192u/ieee80211/ieee80211_crypt_tkip.c
new file mode 100644
index 000000000..e815c81b4
--- /dev/null
+++ b/drivers/staging/rtl8192u/ieee80211/ieee80211_crypt_tkip.c
@@ -0,0 +1,777 @@
+/*
+ * Host AP crypt: host-based TKIP encryption implementation for Host AP driver
+ *
+ * Copyright (c) 2003-2004, Jouni Malinen <jkmaline@cc.hut.fi>
+ *
+ * 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. See README and COPYING for
+ * more details.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/random.h>
+#include <linux/skbuff.h>
+#include <linux/netdevice.h>
+#include <linux/if_ether.h>
+#include <linux/if_arp.h>
+#include <linux/string.h>
+
+#include "ieee80211.h"
+
+#include <linux/crypto.h>
+ #include <linux/scatterlist.h>
+#include <linux/crc32.h>
+
+MODULE_AUTHOR("Jouni Malinen");
+MODULE_DESCRIPTION("Host AP crypt: TKIP");
+MODULE_LICENSE("GPL");
+
+struct ieee80211_tkip_data {
+#define TKIP_KEY_LEN 32
+ u8 key[TKIP_KEY_LEN];
+ int key_set;
+
+ u32 tx_iv32;
+ u16 tx_iv16;
+ u16 tx_ttak[5];
+ int tx_phase1_done;
+
+ u32 rx_iv32;
+ u16 rx_iv16;
+ u16 rx_ttak[5];
+ int rx_phase1_done;
+ u32 rx_iv32_new;
+ u16 rx_iv16_new;
+
+ u32 dot11RSNAStatsTKIPReplays;
+ u32 dot11RSNAStatsTKIPICVErrors;
+ u32 dot11RSNAStatsTKIPLocalMICFailures;
+
+ int key_idx;
+
+ struct crypto_blkcipher *rx_tfm_arc4;
+ struct crypto_hash *rx_tfm_michael;
+ struct crypto_blkcipher *tx_tfm_arc4;
+ struct crypto_hash *tx_tfm_michael;
+
+ /* scratch buffers for virt_to_page() (crypto API) */
+ u8 rx_hdr[16], tx_hdr[16];
+};
+
+static void *ieee80211_tkip_init(int key_idx)
+{
+ struct ieee80211_tkip_data *priv;
+
+ priv = kzalloc(sizeof(*priv), GFP_ATOMIC);
+ if (priv == NULL)
+ goto fail;
+ priv->key_idx = key_idx;
+
+ priv->tx_tfm_arc4 = crypto_alloc_blkcipher("ecb(arc4)", 0,
+ CRYPTO_ALG_ASYNC);
+ if (IS_ERR(priv->tx_tfm_arc4)) {
+ printk(KERN_DEBUG "ieee80211_crypt_tkip: could not allocate "
+ "crypto API arc4\n");
+ priv->tx_tfm_arc4 = NULL;
+ goto fail;
+ }
+
+ priv->tx_tfm_michael = crypto_alloc_hash("michael_mic", 0,
+ CRYPTO_ALG_ASYNC);
+ if (IS_ERR(priv->tx_tfm_michael)) {
+ printk(KERN_DEBUG "ieee80211_crypt_tkip: could not allocate "
+ "crypto API michael_mic\n");
+ priv->tx_tfm_michael = NULL;
+ goto fail;
+ }
+
+ priv->rx_tfm_arc4 = crypto_alloc_blkcipher("ecb(arc4)", 0,
+ CRYPTO_ALG_ASYNC);
+ if (IS_ERR(priv->rx_tfm_arc4)) {
+ printk(KERN_DEBUG "ieee80211_crypt_tkip: could not allocate "
+ "crypto API arc4\n");
+ priv->rx_tfm_arc4 = NULL;
+ goto fail;
+ }
+
+ priv->rx_tfm_michael = crypto_alloc_hash("michael_mic", 0,
+ CRYPTO_ALG_ASYNC);
+ if (IS_ERR(priv->rx_tfm_michael)) {
+ printk(KERN_DEBUG "ieee80211_crypt_tkip: could not allocate "
+ "crypto API michael_mic\n");
+ priv->rx_tfm_michael = NULL;
+ goto fail;
+ }
+
+ return priv;
+
+fail:
+ if (priv) {
+ if (priv->tx_tfm_michael)
+ crypto_free_hash(priv->tx_tfm_michael);
+ if (priv->tx_tfm_arc4)
+ crypto_free_blkcipher(priv->tx_tfm_arc4);
+ if (priv->rx_tfm_michael)
+ crypto_free_hash(priv->rx_tfm_michael);
+ if (priv->rx_tfm_arc4)
+ crypto_free_blkcipher(priv->rx_tfm_arc4);
+ kfree(priv);
+ }
+
+ return NULL;
+}
+
+
+static void ieee80211_tkip_deinit(void *priv)
+{
+ struct ieee80211_tkip_data *_priv = priv;
+
+ if (_priv) {
+ if (_priv->tx_tfm_michael)
+ crypto_free_hash(_priv->tx_tfm_michael);
+ if (_priv->tx_tfm_arc4)
+ crypto_free_blkcipher(_priv->tx_tfm_arc4);
+ if (_priv->rx_tfm_michael)
+ crypto_free_hash(_priv->rx_tfm_michael);
+ if (_priv->rx_tfm_arc4)
+ crypto_free_blkcipher(_priv->rx_tfm_arc4);
+ }
+ kfree(priv);
+}
+
+
+static inline u16 RotR1(u16 val)
+{
+ return (val >> 1) | (val << 15);
+}
+
+
+static inline u8 Lo8(u16 val)
+{
+ return val & 0xff;
+}
+
+
+static inline u8 Hi8(u16 val)
+{
+ return val >> 8;
+}
+
+
+static inline u16 Lo16(u32 val)
+{
+ return val & 0xffff;
+}
+
+
+static inline u16 Hi16(u32 val)
+{
+ return val >> 16;
+}
+
+
+static inline u16 Mk16(u8 hi, u8 lo)
+{
+ return lo | (((u16) hi) << 8);
+}
+
+
+static inline u16 Mk16_le(u16 *v)
+{
+ return le16_to_cpu(*v);
+}
+
+
+static const u16 Sbox[256] =
+{
+ 0xC6A5, 0xF884, 0xEE99, 0xF68D, 0xFF0D, 0xD6BD, 0xDEB1, 0x9154,
+ 0x6050, 0x0203, 0xCEA9, 0x567D, 0xE719, 0xB562, 0x4DE6, 0xEC9A,
+ 0x8F45, 0x1F9D, 0x8940, 0xFA87, 0xEF15, 0xB2EB, 0x8EC9, 0xFB0B,
+ 0x41EC, 0xB367, 0x5FFD, 0x45EA, 0x23BF, 0x53F7, 0xE496, 0x9B5B,
+ 0x75C2, 0xE11C, 0x3DAE, 0x4C6A, 0x6C5A, 0x7E41, 0xF502, 0x834F,
+ 0x685C, 0x51F4, 0xD134, 0xF908, 0xE293, 0xAB73, 0x6253, 0x2A3F,
+ 0x080C, 0x9552, 0x4665, 0x9D5E, 0x3028, 0x37A1, 0x0A0F, 0x2FB5,
+ 0x0E09, 0x2436, 0x1B9B, 0xDF3D, 0xCD26, 0x4E69, 0x7FCD, 0xEA9F,
+ 0x121B, 0x1D9E, 0x5874, 0x342E, 0x362D, 0xDCB2, 0xB4EE, 0x5BFB,
+ 0xA4F6, 0x764D, 0xB761, 0x7DCE, 0x527B, 0xDD3E, 0x5E71, 0x1397,
+ 0xA6F5, 0xB968, 0x0000, 0xC12C, 0x4060, 0xE31F, 0x79C8, 0xB6ED,
+ 0xD4BE, 0x8D46, 0x67D9, 0x724B, 0x94DE, 0x98D4, 0xB0E8, 0x854A,
+ 0xBB6B, 0xC52A, 0x4FE5, 0xED16, 0x86C5, 0x9AD7, 0x6655, 0x1194,
+ 0x8ACF, 0xE910, 0x0406, 0xFE81, 0xA0F0, 0x7844, 0x25BA, 0x4BE3,
+ 0xA2F3, 0x5DFE, 0x80C0, 0x058A, 0x3FAD, 0x21BC, 0x7048, 0xF104,
+ 0x63DF, 0x77C1, 0xAF75, 0x4263, 0x2030, 0xE51A, 0xFD0E, 0xBF6D,
+ 0x814C, 0x1814, 0x2635, 0xC32F, 0xBEE1, 0x35A2, 0x88CC, 0x2E39,
+ 0x9357, 0x55F2, 0xFC82, 0x7A47, 0xC8AC, 0xBAE7, 0x322B, 0xE695,
+ 0xC0A0, 0x1998, 0x9ED1, 0xA37F, 0x4466, 0x547E, 0x3BAB, 0x0B83,
+ 0x8CCA, 0xC729, 0x6BD3, 0x283C, 0xA779, 0xBCE2, 0x161D, 0xAD76,
+ 0xDB3B, 0x6456, 0x744E, 0x141E, 0x92DB, 0x0C0A, 0x486C, 0xB8E4,
+ 0x9F5D, 0xBD6E, 0x43EF, 0xC4A6, 0x39A8, 0x31A4, 0xD337, 0xF28B,
+ 0xD532, 0x8B43, 0x6E59, 0xDAB7, 0x018C, 0xB164, 0x9CD2, 0x49E0,
+ 0xD8B4, 0xACFA, 0xF307, 0xCF25, 0xCAAF, 0xF48E, 0x47E9, 0x1018,
+ 0x6FD5, 0xF088, 0x4A6F, 0x5C72, 0x3824, 0x57F1, 0x73C7, 0x9751,
+ 0xCB23, 0xA17C, 0xE89C, 0x3E21, 0x96DD, 0x61DC, 0x0D86, 0x0F85,
+ 0xE090, 0x7C42, 0x71C4, 0xCCAA, 0x90D8, 0x0605, 0xF701, 0x1C12,
+ 0xC2A3, 0x6A5F, 0xAEF9, 0x69D0, 0x1791, 0x9958, 0x3A27, 0x27B9,
+ 0xD938, 0xEB13, 0x2BB3, 0x2233, 0xD2BB, 0xA970, 0x0789, 0x33A7,
+ 0x2DB6, 0x3C22, 0x1592, 0xC920, 0x8749, 0xAAFF, 0x5078, 0xA57A,
+ 0x038F, 0x59F8, 0x0980, 0x1A17, 0x65DA, 0xD731, 0x84C6, 0xD0B8,
+ 0x82C3, 0x29B0, 0x5A77, 0x1E11, 0x7BCB, 0xA8FC, 0x6DD6, 0x2C3A,
+};
+
+
+static inline u16 _S_(u16 v)
+{
+ u16 t = Sbox[Hi8(v)];
+ return Sbox[Lo8(v)] ^ ((t << 8) | (t >> 8));
+}
+
+
+#define PHASE1_LOOP_COUNT 8
+
+
+static void tkip_mixing_phase1(u16 *TTAK, const u8 *TK, const u8 *TA, u32 IV32)
+{
+ int i, j;
+
+ /* Initialize the 80-bit TTAK from TSC (IV32) and TA[0..5] */
+ TTAK[0] = Lo16(IV32);
+ TTAK[1] = Hi16(IV32);
+ TTAK[2] = Mk16(TA[1], TA[0]);
+ TTAK[3] = Mk16(TA[3], TA[2]);
+ TTAK[4] = Mk16(TA[5], TA[4]);
+
+ for (i = 0; i < PHASE1_LOOP_COUNT; i++) {
+ j = 2 * (i & 1);
+ TTAK[0] += _S_(TTAK[4] ^ Mk16(TK[1 + j], TK[0 + j]));
+ TTAK[1] += _S_(TTAK[0] ^ Mk16(TK[5 + j], TK[4 + j]));
+ TTAK[2] += _S_(TTAK[1] ^ Mk16(TK[9 + j], TK[8 + j]));
+ TTAK[3] += _S_(TTAK[2] ^ Mk16(TK[13 + j], TK[12 + j]));
+ TTAK[4] += _S_(TTAK[3] ^ Mk16(TK[1 + j], TK[0 + j])) + i;
+ }
+}
+
+
+static void tkip_mixing_phase2(u8 *WEPSeed, const u8 *TK, const u16 *TTAK,
+ u16 IV16)
+{
+ /* Make temporary area overlap WEP seed so that the final copy can be
+ * avoided on little endian hosts. */
+ u16 *PPK = (u16 *) &WEPSeed[4];
+
+ /* Step 1 - make copy of TTAK and bring in TSC */
+ PPK[0] = TTAK[0];
+ PPK[1] = TTAK[1];
+ PPK[2] = TTAK[2];
+ PPK[3] = TTAK[3];
+ PPK[4] = TTAK[4];
+ PPK[5] = TTAK[4] + IV16;
+
+ /* Step 2 - 96-bit bijective mixing using S-box */
+ PPK[0] += _S_(PPK[5] ^ Mk16_le((u16 *) &TK[0]));
+ PPK[1] += _S_(PPK[0] ^ Mk16_le((u16 *) &TK[2]));
+ PPK[2] += _S_(PPK[1] ^ Mk16_le((u16 *) &TK[4]));
+ PPK[3] += _S_(PPK[2] ^ Mk16_le((u16 *) &TK[6]));
+ PPK[4] += _S_(PPK[3] ^ Mk16_le((u16 *) &TK[8]));
+ PPK[5] += _S_(PPK[4] ^ Mk16_le((u16 *) &TK[10]));
+
+ PPK[0] += RotR1(PPK[5] ^ Mk16_le((u16 *) &TK[12]));
+ PPK[1] += RotR1(PPK[0] ^ Mk16_le((u16 *) &TK[14]));
+ PPK[2] += RotR1(PPK[1]);
+ PPK[3] += RotR1(PPK[2]);
+ PPK[4] += RotR1(PPK[3]);
+ PPK[5] += RotR1(PPK[4]);
+
+ /* Step 3 - bring in last of TK bits, assign 24-bit WEP IV value
+ * WEPSeed[0..2] is transmitted as WEP IV */
+ WEPSeed[0] = Hi8(IV16);
+ WEPSeed[1] = (Hi8(IV16) | 0x20) & 0x7F;
+ WEPSeed[2] = Lo8(IV16);
+ WEPSeed[3] = Lo8((PPK[5] ^ Mk16_le((u16 *) &TK[0])) >> 1);
+
+#ifdef __BIG_ENDIAN
+ {
+ int i;
+ for (i = 0; i < 6; i++)
+ PPK[i] = (PPK[i] << 8) | (PPK[i] >> 8);
+ }
+#endif
+}
+
+
+static int ieee80211_tkip_encrypt(struct sk_buff *skb, int hdr_len, void *priv)
+{
+ struct ieee80211_tkip_data *tkey = priv;
+ int len;
+ u8 *pos;
+ struct ieee80211_hdr_4addr *hdr;
+ cb_desc *tcb_desc = (cb_desc *)(skb->cb + MAX_DEV_ADDR_SIZE);
+ struct blkcipher_desc desc = {.tfm = tkey->tx_tfm_arc4};
+ int ret = 0;
+ u8 rc4key[16], *icv;
+ u32 crc;
+ struct scatterlist sg;
+
+ if (skb_headroom(skb) < 8 || skb_tailroom(skb) < 4 ||
+ skb->len < hdr_len)
+ return -1;
+
+ hdr = (struct ieee80211_hdr_4addr *) skb->data;
+
+ if (!tcb_desc->bHwSec)
+ {
+ if (!tkey->tx_phase1_done) {
+ tkip_mixing_phase1(tkey->tx_ttak, tkey->key, hdr->addr2,
+ tkey->tx_iv32);
+ tkey->tx_phase1_done = 1;
+ }
+ tkip_mixing_phase2(rc4key, tkey->key, tkey->tx_ttak, tkey->tx_iv16);
+ }
+ else
+ tkey->tx_phase1_done = 1;
+
+
+ len = skb->len - hdr_len;
+ pos = skb_push(skb, 8);
+ memmove(pos, pos + 8, hdr_len);
+ pos += hdr_len;
+
+ if (tcb_desc->bHwSec)
+ {
+ *pos++ = Hi8(tkey->tx_iv16);
+ *pos++ = (Hi8(tkey->tx_iv16) | 0x20) & 0x7F;
+ *pos++ = Lo8(tkey->tx_iv16);
+ }
+ else
+ {
+ *pos++ = rc4key[0];
+ *pos++ = rc4key[1];
+ *pos++ = rc4key[2];
+ }
+
+ *pos++ = (tkey->key_idx << 6) | (1 << 5) /* Ext IV included */;
+ *pos++ = tkey->tx_iv32 & 0xff;
+ *pos++ = (tkey->tx_iv32 >> 8) & 0xff;
+ *pos++ = (tkey->tx_iv32 >> 16) & 0xff;
+ *pos++ = (tkey->tx_iv32 >> 24) & 0xff;
+
+ if (!tcb_desc->bHwSec)
+ {
+ icv = skb_put(skb, 4);
+ crc = ~crc32_le(~0, pos, len);
+ icv[0] = crc;
+ icv[1] = crc >> 8;
+ icv[2] = crc >> 16;
+ icv[3] = crc >> 24;
+ crypto_blkcipher_setkey(tkey->tx_tfm_arc4, rc4key, 16);
+ sg_init_one(&sg, pos, len+4);
+ ret= crypto_blkcipher_encrypt(&desc, &sg, &sg, len + 4);
+ }
+
+ tkey->tx_iv16++;
+ if (tkey->tx_iv16 == 0) {
+ tkey->tx_phase1_done = 0;
+ tkey->tx_iv32++;
+ }
+
+ if (!tcb_desc->bHwSec)
+ return ret;
+ else
+ return 0;
+
+
+}
+
+static int ieee80211_tkip_decrypt(struct sk_buff *skb, int hdr_len, void *priv)
+{
+ struct ieee80211_tkip_data *tkey = priv;
+ u8 keyidx, *pos;
+ u32 iv32;
+ u16 iv16;
+ struct ieee80211_hdr_4addr *hdr;
+ cb_desc *tcb_desc = (cb_desc *)(skb->cb + MAX_DEV_ADDR_SIZE);
+ struct blkcipher_desc desc = {.tfm = tkey->rx_tfm_arc4};
+ u8 rc4key[16];
+ u8 icv[4];
+ u32 crc;
+ struct scatterlist sg;
+ int plen;
+ if (skb->len < hdr_len + 8 + 4)
+ return -1;
+
+ hdr = (struct ieee80211_hdr_4addr *) skb->data;
+ pos = skb->data + hdr_len;
+ keyidx = pos[3];
+ if (!(keyidx & (1 << 5))) {
+ if (net_ratelimit()) {
+ printk(KERN_DEBUG "TKIP: received packet without ExtIV"
+ " flag from %pM\n", hdr->addr2);
+ }
+ return -2;
+ }
+ keyidx >>= 6;
+ if (tkey->key_idx != keyidx) {
+ printk(KERN_DEBUG "TKIP: RX tkey->key_idx=%d frame "
+ "keyidx=%d priv=%p\n", tkey->key_idx, keyidx, priv);
+ return -6;
+ }
+ if (!tkey->key_set) {
+ if (net_ratelimit()) {
+ printk(KERN_DEBUG "TKIP: received packet from %pM"
+ " with keyid=%d that does not have a configured"
+ " key\n", hdr->addr2, keyidx);
+ }
+ return -3;
+ }
+ iv16 = (pos[0] << 8) | pos[2];
+ iv32 = pos[4] | (pos[5] << 8) | (pos[6] << 16) | (pos[7] << 24);
+ pos += 8;
+
+ if (!tcb_desc->bHwSec)
+ {
+ if (iv32 < tkey->rx_iv32 ||
+ (iv32 == tkey->rx_iv32 && iv16 <= tkey->rx_iv16)) {
+ if (net_ratelimit()) {
+ printk(KERN_DEBUG "TKIP: replay detected: STA=%pM"
+ " previous TSC %08x%04x received TSC "
+ "%08x%04x\n", hdr->addr2,
+ tkey->rx_iv32, tkey->rx_iv16, iv32, iv16);
+ }
+ tkey->dot11RSNAStatsTKIPReplays++;
+ return -4;
+ }
+
+ if (iv32 != tkey->rx_iv32 || !tkey->rx_phase1_done) {
+ tkip_mixing_phase1(tkey->rx_ttak, tkey->key, hdr->addr2, iv32);
+ tkey->rx_phase1_done = 1;
+ }
+ tkip_mixing_phase2(rc4key, tkey->key, tkey->rx_ttak, iv16);
+
+ plen = skb->len - hdr_len - 12;
+
+ crypto_blkcipher_setkey(tkey->rx_tfm_arc4, rc4key, 16);
+ sg_init_one(&sg, pos, plen+4);
+
+ if (crypto_blkcipher_decrypt(&desc, &sg, &sg, plen + 4)) {
+ if (net_ratelimit()) {
+ printk(KERN_DEBUG ": TKIP: failed to decrypt "
+ "received packet from %pM\n",
+ hdr->addr2);
+ }
+ return -7;
+ }
+
+ crc = ~crc32_le(~0, pos, plen);
+ icv[0] = crc;
+ icv[1] = crc >> 8;
+ icv[2] = crc >> 16;
+ icv[3] = crc >> 24;
+
+ if (memcmp(icv, pos + plen, 4) != 0) {
+ if (iv32 != tkey->rx_iv32) {
+ /* Previously cached Phase1 result was already lost, so
+ * it needs to be recalculated for the next packet. */
+ tkey->rx_phase1_done = 0;
+ }
+ if (net_ratelimit()) {
+ printk(KERN_DEBUG "TKIP: ICV error detected: STA="
+ "%pM\n", hdr->addr2);
+ }
+ tkey->dot11RSNAStatsTKIPICVErrors++;
+ return -5;
+ }
+
+ }
+
+ /* Update real counters only after Michael MIC verification has
+ * completed */
+ tkey->rx_iv32_new = iv32;
+ tkey->rx_iv16_new = iv16;
+
+ /* Remove IV and ICV */
+ memmove(skb->data + 8, skb->data, hdr_len);
+ skb_pull(skb, 8);
+ skb_trim(skb, skb->len - 4);
+
+ return keyidx;
+}
+
+static int michael_mic(struct crypto_hash *tfm_michael, u8 *key, u8 *hdr,
+ u8 *data, size_t data_len, u8 *mic)
+{
+ struct hash_desc desc;
+ struct scatterlist sg[2];
+
+ if (tfm_michael == NULL) {
+ printk(KERN_WARNING "michael_mic: tfm_michael == NULL\n");
+ return -1;
+ }
+
+ sg_init_table(sg, 2);
+ sg_set_buf(&sg[0], hdr, 16);
+ sg_set_buf(&sg[1], data, data_len);
+
+ if (crypto_hash_setkey(tfm_michael, key, 8))
+ return -1;
+
+ desc.tfm = tfm_michael;
+ desc.flags = 0;
+ return crypto_hash_digest(&desc, sg, data_len + 16, mic);
+}
+
+static void michael_mic_hdr(struct sk_buff *skb, u8 *hdr)
+{
+ struct ieee80211_hdr_4addr *hdr11;
+
+ hdr11 = (struct ieee80211_hdr_4addr *) skb->data;
+ switch (le16_to_cpu(hdr11->frame_ctl) &
+ (IEEE80211_FCTL_FROMDS | IEEE80211_FCTL_TODS)) {
+ case IEEE80211_FCTL_TODS:
+ memcpy(hdr, hdr11->addr3, ETH_ALEN); /* DA */
+ memcpy(hdr + ETH_ALEN, hdr11->addr2, ETH_ALEN); /* SA */
+ break;
+ case IEEE80211_FCTL_FROMDS:
+ memcpy(hdr, hdr11->addr1, ETH_ALEN); /* DA */
+ memcpy(hdr + ETH_ALEN, hdr11->addr3, ETH_ALEN); /* SA */
+ break;
+ case IEEE80211_FCTL_FROMDS | IEEE80211_FCTL_TODS:
+ memcpy(hdr, hdr11->addr3, ETH_ALEN); /* DA */
+ memcpy(hdr + ETH_ALEN, hdr11->addr4, ETH_ALEN); /* SA */
+ break;
+ case 0:
+ memcpy(hdr, hdr11->addr1, ETH_ALEN); /* DA */
+ memcpy(hdr + ETH_ALEN, hdr11->addr2, ETH_ALEN); /* SA */
+ break;
+ }
+
+ hdr[12] = 0; /* priority */
+
+ hdr[13] = hdr[14] = hdr[15] = 0; /* reserved */
+}
+
+
+static int ieee80211_michael_mic_add(struct sk_buff *skb, int hdr_len, void *priv)
+{
+ struct ieee80211_tkip_data *tkey = priv;
+ u8 *pos;
+ struct ieee80211_hdr_4addr *hdr;
+
+ hdr = (struct ieee80211_hdr_4addr *) skb->data;
+
+ if (skb_tailroom(skb) < 8 || skb->len < hdr_len) {
+ printk(KERN_DEBUG "Invalid packet for Michael MIC add "
+ "(tailroom=%d hdr_len=%d skb->len=%d)\n",
+ skb_tailroom(skb), hdr_len, skb->len);
+ return -1;
+ }
+
+ michael_mic_hdr(skb, tkey->tx_hdr);
+
+ // { david, 2006.9.1
+ // fix the wpa process with wmm enabled.
+ if(IEEE80211_QOS_HAS_SEQ(le16_to_cpu(hdr->frame_ctl))) {
+ tkey->tx_hdr[12] = *(skb->data + hdr_len - 2) & 0x07;
+ }
+ // }
+ pos = skb_put(skb, 8);
+
+ if (michael_mic(tkey->tx_tfm_michael, &tkey->key[16], tkey->tx_hdr,
+ skb->data + hdr_len, skb->len - 8 - hdr_len, pos))
+ return -1;
+
+ return 0;
+}
+
+static void ieee80211_michael_mic_failure(struct net_device *dev,
+ struct ieee80211_hdr_4addr *hdr,
+ int keyidx)
+{
+ union iwreq_data wrqu;
+ struct iw_michaelmicfailure ev;
+
+ /* TODO: needed parameters: count, keyid, key type, TSC */
+ memset(&ev, 0, sizeof(ev));
+ ev.flags = keyidx & IW_MICFAILURE_KEY_ID;
+ if (hdr->addr1[0] & 0x01)
+ ev.flags |= IW_MICFAILURE_GROUP;
+ else
+ ev.flags |= IW_MICFAILURE_PAIRWISE;
+ ev.src_addr.sa_family = ARPHRD_ETHER;
+ memcpy(ev.src_addr.sa_data, hdr->addr2, ETH_ALEN);
+ memset(&wrqu, 0, sizeof(wrqu));
+ wrqu.data.length = sizeof(ev);
+ wireless_send_event(dev, IWEVMICHAELMICFAILURE, &wrqu, (char *) &ev);
+}
+
+static int ieee80211_michael_mic_verify(struct sk_buff *skb, int keyidx,
+ int hdr_len, void *priv)
+{
+ struct ieee80211_tkip_data *tkey = priv;
+ u8 mic[8];
+ struct ieee80211_hdr_4addr *hdr;
+
+ hdr = (struct ieee80211_hdr_4addr *) skb->data;
+
+ if (!tkey->key_set)
+ return -1;
+
+ michael_mic_hdr(skb, tkey->rx_hdr);
+ // { david, 2006.9.1
+ // fix the wpa process with wmm enabled.
+ if(IEEE80211_QOS_HAS_SEQ(le16_to_cpu(hdr->frame_ctl))) {
+ tkey->rx_hdr[12] = *(skb->data + hdr_len - 2) & 0x07;
+ }
+ // }
+
+ if (michael_mic(tkey->rx_tfm_michael, &tkey->key[24], tkey->rx_hdr,
+ skb->data + hdr_len, skb->len - 8 - hdr_len, mic))
+ return -1;
+ if (memcmp(mic, skb->data + skb->len - 8, 8) != 0) {
+ struct ieee80211_hdr_4addr *hdr;
+ hdr = (struct ieee80211_hdr_4addr *) skb->data;
+ printk(KERN_DEBUG "%s: Michael MIC verification failed for "
+ "MSDU from %pM keyidx=%d\n",
+ skb->dev ? skb->dev->name : "N/A", hdr->addr2,
+ keyidx);
+ if (skb->dev)
+ ieee80211_michael_mic_failure(skb->dev, hdr, keyidx);
+ tkey->dot11RSNAStatsTKIPLocalMICFailures++;
+ return -1;
+ }
+
+ /* Update TSC counters for RX now that the packet verification has
+ * completed. */
+ tkey->rx_iv32 = tkey->rx_iv32_new;
+ tkey->rx_iv16 = tkey->rx_iv16_new;
+
+ skb_trim(skb, skb->len - 8);
+
+ return 0;
+}
+
+
+static int ieee80211_tkip_set_key(void *key, int len, u8 *seq, void *priv)
+{
+ struct ieee80211_tkip_data *tkey = priv;
+ int keyidx;
+ struct crypto_hash *tfm = tkey->tx_tfm_michael;
+ struct crypto_blkcipher *tfm2 = tkey->tx_tfm_arc4;
+ struct crypto_hash *tfm3 = tkey->rx_tfm_michael;
+ struct crypto_blkcipher *tfm4 = tkey->rx_tfm_arc4;
+
+ keyidx = tkey->key_idx;
+ memset(tkey, 0, sizeof(*tkey));
+ tkey->key_idx = keyidx;
+ tkey->tx_tfm_michael = tfm;
+ tkey->tx_tfm_arc4 = tfm2;
+ tkey->rx_tfm_michael = tfm3;
+ tkey->rx_tfm_arc4 = tfm4;
+
+ if (len == TKIP_KEY_LEN) {
+ memcpy(tkey->key, key, TKIP_KEY_LEN);
+ tkey->key_set = 1;
+ tkey->tx_iv16 = 1; /* TSC is initialized to 1 */
+ if (seq) {
+ tkey->rx_iv32 = (seq[5] << 24) | (seq[4] << 16) |
+ (seq[3] << 8) | seq[2];
+ tkey->rx_iv16 = (seq[1] << 8) | seq[0];
+ }
+ } else if (len == 0)
+ tkey->key_set = 0;
+ else
+ return -1;
+
+ return 0;
+}
+
+
+static int ieee80211_tkip_get_key(void *key, int len, u8 *seq, void *priv)
+{
+ struct ieee80211_tkip_data *tkey = priv;
+
+ if (len < TKIP_KEY_LEN)
+ return -1;
+
+ if (!tkey->key_set)
+ return 0;
+ memcpy(key, tkey->key, TKIP_KEY_LEN);
+
+ if (seq) {
+ /* Return the sequence number of the last transmitted frame. */
+ u16 iv16 = tkey->tx_iv16;
+ u32 iv32 = tkey->tx_iv32;
+ if (iv16 == 0)
+ iv32--;
+ iv16--;
+ seq[0] = tkey->tx_iv16;
+ seq[1] = tkey->tx_iv16 >> 8;
+ seq[2] = tkey->tx_iv32;
+ seq[3] = tkey->tx_iv32 >> 8;
+ seq[4] = tkey->tx_iv32 >> 16;
+ seq[5] = tkey->tx_iv32 >> 24;
+ }
+
+ return TKIP_KEY_LEN;
+}
+
+
+static char *ieee80211_tkip_print_stats(char *p, void *priv)
+{
+ struct ieee80211_tkip_data *tkip = priv;
+ p += sprintf(p, "key[%d] alg=TKIP key_set=%d "
+ "tx_pn=%02x%02x%02x%02x%02x%02x "
+ "rx_pn=%02x%02x%02x%02x%02x%02x "
+ "replays=%d icv_errors=%d local_mic_failures=%d\n",
+ tkip->key_idx, tkip->key_set,
+ (tkip->tx_iv32 >> 24) & 0xff,
+ (tkip->tx_iv32 >> 16) & 0xff,
+ (tkip->tx_iv32 >> 8) & 0xff,
+ tkip->tx_iv32 & 0xff,
+ (tkip->tx_iv16 >> 8) & 0xff,
+ tkip->tx_iv16 & 0xff,
+ (tkip->rx_iv32 >> 24) & 0xff,
+ (tkip->rx_iv32 >> 16) & 0xff,
+ (tkip->rx_iv32 >> 8) & 0xff,
+ tkip->rx_iv32 & 0xff,
+ (tkip->rx_iv16 >> 8) & 0xff,
+ tkip->rx_iv16 & 0xff,
+ tkip->dot11RSNAStatsTKIPReplays,
+ tkip->dot11RSNAStatsTKIPICVErrors,
+ tkip->dot11RSNAStatsTKIPLocalMICFailures);
+ return p;
+}
+
+
+static struct ieee80211_crypto_ops ieee80211_crypt_tkip = {
+ .name = "TKIP",
+ .init = ieee80211_tkip_init,
+ .deinit = ieee80211_tkip_deinit,
+ .encrypt_mpdu = ieee80211_tkip_encrypt,
+ .decrypt_mpdu = ieee80211_tkip_decrypt,
+ .encrypt_msdu = ieee80211_michael_mic_add,
+ .decrypt_msdu = ieee80211_michael_mic_verify,
+ .set_key = ieee80211_tkip_set_key,
+ .get_key = ieee80211_tkip_get_key,
+ .print_stats = ieee80211_tkip_print_stats,
+ .extra_prefix_len = 4 + 4, /* IV + ExtIV */
+ .extra_postfix_len = 8 + 4, /* MIC + ICV */
+ .owner = THIS_MODULE,
+};
+
+int __init ieee80211_crypto_tkip_init(void)
+{
+ return ieee80211_register_crypto_ops(&ieee80211_crypt_tkip);
+}
+
+void __exit ieee80211_crypto_tkip_exit(void)
+{
+ ieee80211_unregister_crypto_ops(&ieee80211_crypt_tkip);
+}
+
+void ieee80211_tkip_null(void)
+{
+// printk("============>%s()\n", __func__);
+ return;
+}
diff --git a/drivers/staging/rtl8192u/ieee80211/ieee80211_crypt_wep.c b/drivers/staging/rtl8192u/ieee80211/ieee80211_crypt_wep.c
new file mode 100644
index 000000000..0a17f84bb
--- /dev/null
+++ b/drivers/staging/rtl8192u/ieee80211/ieee80211_crypt_wep.c
@@ -0,0 +1,298 @@
+/*
+ * Host AP crypt: host-based WEP encryption implementation for Host AP driver
+ *
+ * Copyright (c) 2002-2004, Jouni Malinen <jkmaline@cc.hut.fi>
+ *
+ * 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. See README and COPYING for
+ * more details.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/random.h>
+#include <linux/skbuff.h>
+#include <linux/string.h>
+
+#include "ieee80211.h"
+
+#include <linux/crypto.h>
+ #include <linux/scatterlist.h>
+#include <linux/crc32.h>
+
+MODULE_AUTHOR("Jouni Malinen");
+MODULE_DESCRIPTION("Host AP crypt: WEP");
+MODULE_LICENSE("GPL");
+
+struct prism2_wep_data {
+ u32 iv;
+#define WEP_KEY_LEN 13
+ u8 key[WEP_KEY_LEN + 1];
+ u8 key_len;
+ u8 key_idx;
+ struct crypto_blkcipher *tx_tfm;
+ struct crypto_blkcipher *rx_tfm;
+};
+
+
+static void *prism2_wep_init(int keyidx)
+{
+ struct prism2_wep_data *priv;
+
+ priv = kzalloc(sizeof(*priv), GFP_ATOMIC);
+ if (priv == NULL)
+ goto fail;
+ priv->key_idx = keyidx;
+
+ priv->tx_tfm = crypto_alloc_blkcipher("ecb(arc4)", 0, CRYPTO_ALG_ASYNC);
+ if (IS_ERR(priv->tx_tfm)) {
+ pr_debug("ieee80211_crypt_wep: could not allocate "
+ "crypto API arc4\n");
+ priv->tx_tfm = NULL;
+ goto fail;
+ }
+ priv->rx_tfm = crypto_alloc_blkcipher("ecb(arc4)", 0, CRYPTO_ALG_ASYNC);
+ if (IS_ERR(priv->rx_tfm)) {
+ pr_debug("ieee80211_crypt_wep: could not allocate "
+ "crypto API arc4\n");
+ priv->rx_tfm = NULL;
+ goto fail;
+ }
+
+ /* start WEP IV from a random value */
+ get_random_bytes(&priv->iv, 4);
+
+ return priv;
+
+fail:
+ if (priv) {
+ if (priv->tx_tfm)
+ crypto_free_blkcipher(priv->tx_tfm);
+ if (priv->rx_tfm)
+ crypto_free_blkcipher(priv->rx_tfm);
+ kfree(priv);
+ }
+
+ return NULL;
+}
+
+
+static void prism2_wep_deinit(void *priv)
+{
+ struct prism2_wep_data *_priv = priv;
+
+ if (_priv) {
+ if (_priv->tx_tfm)
+ crypto_free_blkcipher(_priv->tx_tfm);
+ if (_priv->rx_tfm)
+ crypto_free_blkcipher(_priv->rx_tfm);
+ }
+ kfree(priv);
+}
+
+/* Perform WEP encryption on given skb that has at least 4 bytes of headroom
+ * for IV and 4 bytes of tailroom for ICV. Both IV and ICV will be transmitted,
+ * so the payload length increases with 8 bytes.
+ *
+ * WEP frame payload: IV + TX key idx, RC4(data), ICV = RC4(CRC32(data))
+ */
+static int prism2_wep_encrypt(struct sk_buff *skb, int hdr_len, void *priv)
+{
+ struct prism2_wep_data *wep = priv;
+ u32 klen, len;
+ u8 key[WEP_KEY_LEN + 3];
+ u8 *pos;
+ cb_desc *tcb_desc = (cb_desc *)(skb->cb + MAX_DEV_ADDR_SIZE);
+ struct blkcipher_desc desc = {.tfm = wep->tx_tfm};
+ u32 crc;
+ u8 *icv;
+ struct scatterlist sg;
+
+ if (skb_headroom(skb) < 4 || skb_tailroom(skb) < 4 ||
+ skb->len < hdr_len)
+ return -1;
+
+ len = skb->len - hdr_len;
+ pos = skb_push(skb, 4);
+ memmove(pos, pos + 4, hdr_len);
+ pos += hdr_len;
+
+ klen = 3 + wep->key_len;
+
+ wep->iv++;
+
+ /* Fluhrer, Mantin, and Shamir have reported weaknesses in the key
+ * scheduling algorithm of RC4. At least IVs (KeyByte + 3, 0xff, N)
+ * can be used to speedup attacks, so avoid using them. */
+ if ((wep->iv & 0xff00) == 0xff00) {
+ u8 B = (wep->iv >> 16) & 0xff;
+
+ if (B >= 3 && B < klen)
+ wep->iv += 0x0100;
+ }
+
+ /* Prepend 24-bit IV to RC4 key and TX frame */
+ *pos++ = key[0] = (wep->iv >> 16) & 0xff;
+ *pos++ = key[1] = (wep->iv >> 8) & 0xff;
+ *pos++ = key[2] = wep->iv & 0xff;
+ *pos++ = wep->key_idx << 6;
+
+ /* Copy rest of the WEP key (the secret part) */
+ memcpy(key + 3, wep->key, wep->key_len);
+
+ if (!tcb_desc->bHwSec)
+ {
+
+ /* Append little-endian CRC32 and encrypt it to produce ICV */
+ crc = ~crc32_le(~0, pos, len);
+ icv = skb_put(skb, 4);
+ icv[0] = crc;
+ icv[1] = crc >> 8;
+ icv[2] = crc >> 16;
+ icv[3] = crc >> 24;
+
+ crypto_blkcipher_setkey(wep->tx_tfm, key, klen);
+ sg_init_one(&sg, pos, len+4);
+
+ return crypto_blkcipher_encrypt(&desc, &sg, &sg, len + 4);
+ }
+
+ return 0;
+}
+
+
+/* Perform WEP decryption on given buffer. Buffer includes whole WEP part of
+ * the frame: IV (4 bytes), encrypted payload (including SNAP header),
+ * ICV (4 bytes). len includes both IV and ICV.
+ *
+ * Returns 0 if frame was decrypted successfully and ICV was correct and -1 on
+ * failure. If frame is OK, IV and ICV will be removed.
+ */
+static int prism2_wep_decrypt(struct sk_buff *skb, int hdr_len, void *priv)
+{
+ struct prism2_wep_data *wep = priv;
+ u32 klen, plen;
+ u8 key[WEP_KEY_LEN + 3];
+ u8 keyidx, *pos;
+ cb_desc *tcb_desc = (cb_desc *)(skb->cb + MAX_DEV_ADDR_SIZE);
+ struct blkcipher_desc desc = {.tfm = wep->rx_tfm};
+ u32 crc;
+ u8 icv[4];
+ struct scatterlist sg;
+
+ if (skb->len < hdr_len + 8)
+ return -1;
+
+ pos = skb->data + hdr_len;
+ key[0] = *pos++;
+ key[1] = *pos++;
+ key[2] = *pos++;
+ keyidx = *pos++ >> 6;
+ if (keyidx != wep->key_idx)
+ return -1;
+
+ klen = 3 + wep->key_len;
+
+ /* Copy rest of the WEP key (the secret part) */
+ memcpy(key + 3, wep->key, wep->key_len);
+
+ /* Apply RC4 to data and compute CRC32 over decrypted data */
+ plen = skb->len - hdr_len - 8;
+
+ if (!tcb_desc->bHwSec)
+ {
+ crypto_blkcipher_setkey(wep->rx_tfm, key, klen);
+ sg_init_one(&sg, pos, plen+4);
+
+ if (crypto_blkcipher_decrypt(&desc, &sg, &sg, plen + 4))
+ return -7;
+
+ crc = ~crc32_le(~0, pos, plen);
+ icv[0] = crc;
+ icv[1] = crc >> 8;
+ icv[2] = crc >> 16;
+ icv[3] = crc >> 24;
+ if (memcmp(icv, pos + plen, 4) != 0) {
+ /* ICV mismatch - drop frame */
+ return -2;
+ }
+ }
+ /* Remove IV and ICV */
+ memmove(skb->data + 4, skb->data, hdr_len);
+ skb_pull(skb, 4);
+ skb_trim(skb, skb->len - 4);
+
+ return 0;
+}
+
+
+static int prism2_wep_set_key(void *key, int len, u8 *seq, void *priv)
+{
+ struct prism2_wep_data *wep = priv;
+
+ if (len < 0 || len > WEP_KEY_LEN)
+ return -1;
+
+ memcpy(wep->key, key, len);
+ wep->key_len = len;
+
+ return 0;
+}
+
+
+static int prism2_wep_get_key(void *key, int len, u8 *seq, void *priv)
+{
+ struct prism2_wep_data *wep = priv;
+
+ if (len < wep->key_len)
+ return -1;
+
+ memcpy(key, wep->key, wep->key_len);
+
+ return wep->key_len;
+}
+
+
+static char *prism2_wep_print_stats(char *p, void *priv)
+{
+ struct prism2_wep_data *wep = priv;
+
+ p += sprintf(p, "key[%d] alg=WEP len=%d\n",
+ wep->key_idx, wep->key_len);
+ return p;
+}
+
+
+static struct ieee80211_crypto_ops ieee80211_crypt_wep = {
+ .name = "WEP",
+ .init = prism2_wep_init,
+ .deinit = prism2_wep_deinit,
+ .encrypt_mpdu = prism2_wep_encrypt,
+ .decrypt_mpdu = prism2_wep_decrypt,
+ .encrypt_msdu = NULL,
+ .decrypt_msdu = NULL,
+ .set_key = prism2_wep_set_key,
+ .get_key = prism2_wep_get_key,
+ .print_stats = prism2_wep_print_stats,
+ .extra_prefix_len = 4, /* IV */
+ .extra_postfix_len = 4, /* ICV */
+ .owner = THIS_MODULE,
+};
+
+int __init ieee80211_crypto_wep_init(void)
+{
+ return ieee80211_register_crypto_ops(&ieee80211_crypt_wep);
+}
+
+void __exit ieee80211_crypto_wep_exit(void)
+{
+ ieee80211_unregister_crypto_ops(&ieee80211_crypt_wep);
+}
+
+void ieee80211_wep_null(void)
+{
+// printk("============>%s()\n", __func__);
+ return;
+}
diff --git a/drivers/staging/rtl8192u/ieee80211/ieee80211_module.c b/drivers/staging/rtl8192u/ieee80211/ieee80211_module.c
new file mode 100644
index 000000000..31233d895
--- /dev/null
+++ b/drivers/staging/rtl8192u/ieee80211/ieee80211_module.c
@@ -0,0 +1,309 @@
+/*******************************************************************************
+
+ Copyright(c) 2004 Intel Corporation. All rights reserved.
+
+ Portions of this file are based on the WEP enablement code provided by the
+ Host AP project hostap-drivers v0.1.3
+ Copyright (c) 2001-2002, SSH Communications Security Corp and Jouni Malinen
+ <jkmaline@cc.hut.fi>
+ Copyright (c) 2002-2003, Jouni Malinen <jkmaline@cc.hut.fi>
+
+ This program is free software; you can redistribute it and/or modify it
+ under the terms of version 2 of the GNU General Public License as
+ published by the Free Software Foundation.
+
+ 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.
+
+ The full GNU General Public License is included in this distribution in the
+ file called LICENSE.
+
+ Contact Information:
+ James P. Ketrenos <ipw2100-admin@linux.intel.com>
+ Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+
+*******************************************************************************/
+
+#include <linux/compiler.h>
+/* #include <linux/config.h> */
+#include <linux/errno.h>
+#include <linux/if_arp.h>
+#include <linux/in6.h>
+#include <linux/in.h>
+#include <linux/ip.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/netdevice.h>
+#include <linux/pci.h>
+#include <linux/proc_fs.h>
+#include <linux/skbuff.h>
+#include <linux/slab.h>
+#include <linux/tcp.h>
+#include <linux/types.h>
+#include <linux/wireless.h>
+#include <linux/etherdevice.h>
+#include <asm/uaccess.h>
+#include <net/arp.h>
+
+#include "ieee80211.h"
+
+MODULE_DESCRIPTION("802.11 data/management/control stack");
+MODULE_AUTHOR("Copyright (C) 2004 Intel Corporation <jketreno@linux.intel.com>");
+MODULE_LICENSE("GPL");
+
+#define DRV_NAME "ieee80211"
+
+static inline int ieee80211_networks_allocate(struct ieee80211_device *ieee)
+{
+ if (ieee->networks)
+ return 0;
+
+ ieee->networks = kcalloc(
+ MAX_NETWORK_COUNT, sizeof(struct ieee80211_network),
+ GFP_KERNEL);
+ if (!ieee->networks) {
+ printk(KERN_WARNING "%s: Out of memory allocating beacons\n",
+ ieee->dev->name);
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+static inline void ieee80211_networks_free(struct ieee80211_device *ieee)
+{
+ if (!ieee->networks)
+ return;
+ kfree(ieee->networks);
+ ieee->networks = NULL;
+}
+
+static inline void ieee80211_networks_initialize(struct ieee80211_device *ieee)
+{
+ int i;
+
+ INIT_LIST_HEAD(&ieee->network_free_list);
+ INIT_LIST_HEAD(&ieee->network_list);
+ for (i = 0; i < MAX_NETWORK_COUNT; i++)
+ list_add_tail(&ieee->networks[i].list, &ieee->network_free_list);
+}
+
+
+struct net_device *alloc_ieee80211(int sizeof_priv)
+{
+ struct ieee80211_device *ieee;
+ struct net_device *dev;
+ int i, err;
+
+ IEEE80211_DEBUG_INFO("Initializing...\n");
+
+ dev = alloc_etherdev(sizeof(struct ieee80211_device) + sizeof_priv);
+ if (!dev) {
+ IEEE80211_ERROR("Unable to network device.\n");
+ goto failed;
+ }
+
+ ieee = netdev_priv(dev);
+ memset(ieee, 0, sizeof(struct ieee80211_device)+sizeof_priv);
+ ieee->dev = dev;
+
+ err = ieee80211_networks_allocate(ieee);
+ if (err) {
+ IEEE80211_ERROR("Unable to allocate beacon storage: %d\n",
+ err);
+ goto failed;
+ }
+ ieee80211_networks_initialize(ieee);
+
+
+ /* Default fragmentation threshold is maximum payload size */
+ ieee->fts = DEFAULT_FTS;
+ ieee->scan_age = DEFAULT_MAX_SCAN_AGE;
+ ieee->open_wep = 1;
+
+ /* Default to enabling full open WEP with host based encrypt/decrypt */
+ ieee->host_encrypt = 1;
+ ieee->host_decrypt = 1;
+ ieee->ieee802_1x = 1; /* Default to supporting 802.1x */
+
+ INIT_LIST_HEAD(&ieee->crypt_deinit_list);
+ setup_timer(&ieee->crypt_deinit_timer,
+ ieee80211_crypt_deinit_handler, (unsigned long)ieee);
+
+ spin_lock_init(&ieee->lock);
+ spin_lock_init(&ieee->wpax_suitlist_lock);
+ spin_lock_init(&ieee->bw_spinlock);
+ spin_lock_init(&ieee->reorder_spinlock);
+ /* added by WB */
+ atomic_set(&(ieee->atm_chnlop), 0);
+ atomic_set(&(ieee->atm_swbw), 0);
+
+ ieee->wpax_type_set = 0;
+ ieee->wpa_enabled = 0;
+ ieee->tkip_countermeasures = 0;
+ ieee->drop_unencrypted = 0;
+ ieee->privacy_invoked = 0;
+ ieee->ieee802_1x = 1;
+ ieee->raw_tx = 0;
+ //ieee->hwsec_support = 1; //defalt support hw security. //use module_param instead.
+ ieee->hwsec_active = 0; /* disable hwsec, switch it on when necessary. */
+
+ ieee80211_softmac_init(ieee);
+
+ ieee->pHTInfo = kzalloc(sizeof(RT_HIGH_THROUGHPUT), GFP_KERNEL);
+ if (ieee->pHTInfo == NULL)
+ {
+ IEEE80211_DEBUG(IEEE80211_DL_ERR, "can't alloc memory for HTInfo\n");
+ goto failed;
+ }
+ HTUpdateDefaultSetting(ieee);
+ HTInitializeHTInfo(ieee); /* may move to other place. */
+ TSInitialize(ieee);
+
+ for (i = 0; i < IEEE_IBSS_MAC_HASH_SIZE; i++)
+ INIT_LIST_HEAD(&ieee->ibss_mac_hash[i]);
+
+ for (i = 0; i < 17; i++) {
+ ieee->last_rxseq_num[i] = -1;
+ ieee->last_rxfrag_num[i] = -1;
+ ieee->last_packet_time[i] = 0;
+ }
+
+/* These function were added to load crypte module autoly */
+ ieee80211_tkip_null();
+ ieee80211_wep_null();
+ ieee80211_ccmp_null();
+
+ return dev;
+
+ failed:
+ if (dev)
+ free_netdev(dev);
+
+ return NULL;
+}
+
+
+void free_ieee80211(struct net_device *dev)
+{
+ struct ieee80211_device *ieee = netdev_priv(dev);
+ int i;
+ /* struct list_head *p, *q; */
+// del_timer_sync(&ieee->SwBwTimer);
+ kfree(ieee->pHTInfo);
+ ieee->pHTInfo = NULL;
+ RemoveAllTS(ieee);
+ ieee80211_softmac_free(ieee);
+ del_timer_sync(&ieee->crypt_deinit_timer);
+ ieee80211_crypt_deinit_entries(ieee, 1);
+
+ for (i = 0; i < WEP_KEYS; i++) {
+ struct ieee80211_crypt_data *crypt = ieee->crypt[i];
+ if (crypt) {
+ if (crypt->ops)
+ crypt->ops->deinit(crypt->priv);
+ kfree(crypt);
+ ieee->crypt[i] = NULL;
+ }
+ }
+
+ ieee80211_networks_free(ieee);
+ free_netdev(dev);
+}
+
+#ifdef CONFIG_IEEE80211_DEBUG
+
+u32 ieee80211_debug_level;
+static int debug = \
+ // IEEE80211_DL_INFO |
+ // IEEE80211_DL_WX |
+ // IEEE80211_DL_SCAN |
+ // IEEE80211_DL_STATE |
+ // IEEE80211_DL_MGMT |
+ // IEEE80211_DL_FRAG |
+ // IEEE80211_DL_EAP |
+ // IEEE80211_DL_DROP |
+ // IEEE80211_DL_TX |
+ // IEEE80211_DL_RX |
+ //IEEE80211_DL_QOS |
+ // IEEE80211_DL_HT |
+ // IEEE80211_DL_TS |
+// IEEE80211_DL_BA |
+ // IEEE80211_DL_REORDER|
+// IEEE80211_DL_TRACE |
+ //IEEE80211_DL_DATA |
+ IEEE80211_DL_ERR /* awayls open this flags to show error out */
+ ;
+static struct proc_dir_entry *ieee80211_proc;
+
+static int show_debug_level(struct seq_file *m, void *v)
+{
+ seq_printf(m, "0x%08X\n", ieee80211_debug_level);
+
+ return 0;
+}
+
+static ssize_t write_debug_level(struct file *file, const char __user *buffer,
+ size_t count, loff_t *ppos)
+{
+ unsigned long val;
+ int err = kstrtoul_from_user(buffer, count, 0, &val);
+ if (err)
+ return err;
+ ieee80211_debug_level = val;
+ return count;
+}
+
+static int open_debug_level(struct inode *inode, struct file *file)
+{
+ return single_open(file, show_debug_level, NULL);
+}
+
+static const struct file_operations fops = {
+ .open = open_debug_level,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .write = write_debug_level,
+ .release = single_release,
+};
+
+int __init ieee80211_debug_init(void)
+{
+ struct proc_dir_entry *e;
+
+ ieee80211_debug_level = debug;
+
+ ieee80211_proc = proc_mkdir(DRV_NAME, init_net.proc_net);
+ if (ieee80211_proc == NULL) {
+ IEEE80211_ERROR("Unable to create " DRV_NAME
+ " proc directory\n");
+ return -EIO;
+ }
+ e = proc_create("debug_level", S_IRUGO | S_IWUSR,
+ ieee80211_proc, &fops);
+ if (!e) {
+ remove_proc_entry(DRV_NAME, init_net.proc_net);
+ ieee80211_proc = NULL;
+ return -EIO;
+ }
+ return 0;
+}
+
+void __exit ieee80211_debug_exit(void)
+{
+ if (ieee80211_proc) {
+ remove_proc_entry("debug_level", ieee80211_proc);
+ remove_proc_entry(DRV_NAME, init_net.proc_net);
+ ieee80211_proc = NULL;
+ }
+}
+
+module_param(debug, int, 0444);
+MODULE_PARM_DESC(debug, "debug output mask");
+#endif
diff --git a/drivers/staging/rtl8192u/ieee80211/ieee80211_rx.c b/drivers/staging/rtl8192u/ieee80211/ieee80211_rx.c
new file mode 100644
index 000000000..9fbb53d8c
--- /dev/null
+++ b/drivers/staging/rtl8192u/ieee80211/ieee80211_rx.c
@@ -0,0 +1,2638 @@
+/*
+ * Original code based Host AP (software wireless LAN access point) driver
+ * for Intersil Prism2/2.5/3 - hostap.o module, common routines
+ *
+ * Copyright (c) 2001-2002, SSH Communications Security Corp and Jouni Malinen
+ * <jkmaline@cc.hut.fi>
+ * Copyright (c) 2002-2003, Jouni Malinen <jkmaline@cc.hut.fi>
+ * Copyright (c) 2004, Intel Corporation
+ *
+ * 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. See README and COPYING for
+ * more details.
+ ******************************************************************************
+
+ Few modifications for Realtek's Wi-Fi drivers by
+ Andrea Merello <andrea.merello@gmail.com>
+
+ A special thanks goes to Realtek for their support !
+
+******************************************************************************/
+
+
+#include <linux/compiler.h>
+#include <linux/errno.h>
+#include <linux/if_arp.h>
+#include <linux/in6.h>
+#include <linux/in.h>
+#include <linux/ip.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/netdevice.h>
+#include <linux/pci.h>
+#include <linux/proc_fs.h>
+#include <linux/skbuff.h>
+#include <linux/slab.h>
+#include <linux/tcp.h>
+#include <linux/types.h>
+#include <linux/wireless.h>
+#include <linux/etherdevice.h>
+#include <linux/uaccess.h>
+#include <linux/ctype.h>
+
+#include "ieee80211.h"
+#include "dot11d.h"
+static inline void ieee80211_monitor_rx(struct ieee80211_device *ieee,
+ struct sk_buff *skb,
+ struct ieee80211_rx_stats *rx_stats)
+{
+ struct ieee80211_hdr_4addr *hdr = (struct ieee80211_hdr_4addr *)skb->data;
+ u16 fc = le16_to_cpu(hdr->frame_ctl);
+
+ skb->dev = ieee->dev;
+ skb_reset_mac_header(skb);
+
+ skb_pull(skb, ieee80211_get_hdrlen(fc));
+ skb->pkt_type = PACKET_OTHERHOST;
+ skb->protocol = htons(ETH_P_80211_RAW);
+ memset(skb->cb, 0, sizeof(skb->cb));
+ netif_rx(skb);
+}
+
+
+/* Called only as a tasklet (software IRQ) */
+static struct ieee80211_frag_entry *
+ieee80211_frag_cache_find(struct ieee80211_device *ieee, unsigned int seq,
+ unsigned int frag, u8 tid, u8 *src, u8 *dst)
+{
+ struct ieee80211_frag_entry *entry;
+ int i;
+
+ for (i = 0; i < IEEE80211_FRAG_CACHE_LEN; i++) {
+ entry = &ieee->frag_cache[tid][i];
+ if (entry->skb != NULL &&
+ time_after(jiffies, entry->first_frag_time + 2 * HZ)) {
+ IEEE80211_DEBUG_FRAG(
+ "expiring fragment cache entry "
+ "seq=%u last_frag=%u\n",
+ entry->seq, entry->last_frag);
+ dev_kfree_skb_any(entry->skb);
+ entry->skb = NULL;
+ }
+
+ if (entry->skb != NULL && entry->seq == seq &&
+ (entry->last_frag + 1 == frag || frag == -1) &&
+ memcmp(entry->src_addr, src, ETH_ALEN) == 0 &&
+ memcmp(entry->dst_addr, dst, ETH_ALEN) == 0)
+ return entry;
+ }
+
+ return NULL;
+}
+
+/* Called only as a tasklet (software IRQ) */
+static struct sk_buff *
+ieee80211_frag_cache_get(struct ieee80211_device *ieee,
+ struct ieee80211_hdr_4addr *hdr)
+{
+ struct sk_buff *skb = NULL;
+ u16 fc = le16_to_cpu(hdr->frame_ctl);
+ u16 sc = le16_to_cpu(hdr->seq_ctl);
+ unsigned int frag = WLAN_GET_SEQ_FRAG(sc);
+ unsigned int seq = WLAN_GET_SEQ_SEQ(sc);
+ struct ieee80211_frag_entry *entry;
+ struct ieee80211_hdr_3addrqos *hdr_3addrqos;
+ struct ieee80211_hdr_4addrqos *hdr_4addrqos;
+ u8 tid;
+
+ if (((fc & IEEE80211_FCTL_DSTODS) == IEEE80211_FCTL_DSTODS)&&IEEE80211_QOS_HAS_SEQ(fc)) {
+ hdr_4addrqos = (struct ieee80211_hdr_4addrqos *)hdr;
+ tid = le16_to_cpu(hdr_4addrqos->qos_ctl) & IEEE80211_QCTL_TID;
+ tid = UP2AC(tid);
+ tid ++;
+ } else if (IEEE80211_QOS_HAS_SEQ(fc)) {
+ hdr_3addrqos = (struct ieee80211_hdr_3addrqos *)hdr;
+ tid = le16_to_cpu(hdr_3addrqos->qos_ctl) & IEEE80211_QCTL_TID;
+ tid = UP2AC(tid);
+ tid ++;
+ } else {
+ tid = 0;
+ }
+
+ if (frag == 0) {
+ /* Reserve enough space to fit maximum frame length */
+ skb = dev_alloc_skb(ieee->dev->mtu +
+ sizeof(struct ieee80211_hdr_4addr) +
+ 8 /* LLC */ +
+ 2 /* alignment */ +
+ 8 /* WEP */ +
+ ETH_ALEN /* WDS */ +
+ (IEEE80211_QOS_HAS_SEQ(fc)?2:0) /* QOS Control */);
+ if (skb == NULL)
+ return NULL;
+
+ entry = &ieee->frag_cache[tid][ieee->frag_next_idx[tid]];
+ ieee->frag_next_idx[tid]++;
+ if (ieee->frag_next_idx[tid] >= IEEE80211_FRAG_CACHE_LEN)
+ ieee->frag_next_idx[tid] = 0;
+
+ if (entry->skb != NULL)
+ dev_kfree_skb_any(entry->skb);
+
+ entry->first_frag_time = jiffies;
+ entry->seq = seq;
+ entry->last_frag = frag;
+ entry->skb = skb;
+ memcpy(entry->src_addr, hdr->addr2, ETH_ALEN);
+ memcpy(entry->dst_addr, hdr->addr1, ETH_ALEN);
+ } else {
+ /* received a fragment of a frame for which the head fragment
+ * should have already been received */
+ entry = ieee80211_frag_cache_find(ieee, seq, frag, tid,hdr->addr2,
+ hdr->addr1);
+ if (entry != NULL) {
+ entry->last_frag = frag;
+ skb = entry->skb;
+ }
+ }
+
+ return skb;
+}
+
+
+/* Called only as a tasklet (software IRQ) */
+static int ieee80211_frag_cache_invalidate(struct ieee80211_device *ieee,
+ struct ieee80211_hdr_4addr *hdr)
+{
+ u16 fc = le16_to_cpu(hdr->frame_ctl);
+ u16 sc = le16_to_cpu(hdr->seq_ctl);
+ unsigned int seq = WLAN_GET_SEQ_SEQ(sc);
+ struct ieee80211_frag_entry *entry;
+ struct ieee80211_hdr_3addrqos *hdr_3addrqos;
+ struct ieee80211_hdr_4addrqos *hdr_4addrqos;
+ u8 tid;
+
+ if(((fc & IEEE80211_FCTL_DSTODS) == IEEE80211_FCTL_DSTODS)&&IEEE80211_QOS_HAS_SEQ(fc)) {
+ hdr_4addrqos = (struct ieee80211_hdr_4addrqos *)hdr;
+ tid = le16_to_cpu(hdr_4addrqos->qos_ctl) & IEEE80211_QCTL_TID;
+ tid = UP2AC(tid);
+ tid ++;
+ } else if (IEEE80211_QOS_HAS_SEQ(fc)) {
+ hdr_3addrqos = (struct ieee80211_hdr_3addrqos *)hdr;
+ tid = le16_to_cpu(hdr_3addrqos->qos_ctl) & IEEE80211_QCTL_TID;
+ tid = UP2AC(tid);
+ tid ++;
+ } else {
+ tid = 0;
+ }
+
+ entry = ieee80211_frag_cache_find(ieee, seq, -1, tid, hdr->addr2,
+ hdr->addr1);
+
+ if (entry == NULL) {
+ IEEE80211_DEBUG_FRAG(
+ "could not invalidate fragment cache "
+ "entry (seq=%u)\n", seq);
+ return -1;
+ }
+
+ entry->skb = NULL;
+ return 0;
+}
+
+
+
+/* ieee80211_rx_frame_mgtmt
+ *
+ * Responsible for handling management control frames
+ *
+ * Called by ieee80211_rx */
+static inline int
+ieee80211_rx_frame_mgmt(struct ieee80211_device *ieee, struct sk_buff *skb,
+ struct ieee80211_rx_stats *rx_stats, u16 type,
+ u16 stype)
+{
+ /* On the struct stats definition there is written that
+ * this is not mandatory.... but seems that the probe
+ * response parser uses it
+ */
+ struct ieee80211_hdr_3addr *hdr = (struct ieee80211_hdr_3addr *)skb->data;
+
+ rx_stats->len = skb->len;
+ ieee80211_rx_mgt(ieee,(struct ieee80211_hdr_4addr *)skb->data,rx_stats);
+ /* if ((ieee->state == IEEE80211_LINKED) && (memcmp(hdr->addr3, ieee->current_network.bssid, ETH_ALEN))) */
+ if ((memcmp(hdr->addr1, ieee->dev->dev_addr, ETH_ALEN)))/* use ADDR1 to perform address matching for Management frames */
+ {
+ dev_kfree_skb_any(skb);
+ return 0;
+ }
+
+ ieee80211_rx_frame_softmac(ieee, skb, rx_stats, type, stype);
+
+ dev_kfree_skb_any(skb);
+
+ return 0;
+
+ #ifdef NOT_YET
+ if (ieee->iw_mode == IW_MODE_MASTER) {
+ printk(KERN_DEBUG "%s: Master mode not yet supported.\n",
+ ieee->dev->name);
+ return 0;
+/*
+ hostap_update_sta_ps(ieee, (struct hostap_ieee80211_hdr_4addr *)
+ skb->data);*/
+ }
+
+ if (ieee->hostapd && type == IEEE80211_TYPE_MGMT) {
+ if (stype == WLAN_FC_STYPE_BEACON &&
+ ieee->iw_mode == IW_MODE_MASTER) {
+ struct sk_buff *skb2;
+ /* Process beacon frames also in kernel driver to
+ * update STA(AP) table statistics */
+ skb2 = skb_clone(skb, GFP_ATOMIC);
+ if (skb2)
+ hostap_rx(skb2->dev, skb2, rx_stats);
+ }
+
+ /* send management frames to the user space daemon for
+ * processing */
+ ieee->apdevstats.rx_packets++;
+ ieee->apdevstats.rx_bytes += skb->len;
+ prism2_rx_80211(ieee->apdev, skb, rx_stats, PRISM2_RX_MGMT);
+ return 0;
+ }
+
+ if (ieee->iw_mode == IW_MODE_MASTER) {
+ if (type != WLAN_FC_TYPE_MGMT && type != WLAN_FC_TYPE_CTRL) {
+ printk(KERN_DEBUG "%s: unknown management frame "
+ "(type=0x%02x, stype=0x%02x) dropped\n",
+ skb->dev->name, type, stype);
+ return -1;
+ }
+
+ hostap_rx(skb->dev, skb, rx_stats);
+ return 0;
+ }
+
+ printk(KERN_DEBUG "%s: hostap_rx_frame_mgmt: management frame "
+ "received in non-Host AP mode\n", skb->dev->name);
+ return -1;
+ #endif
+}
+
+
+
+/* See IEEE 802.1H for LLC/SNAP encapsulation/decapsulation */
+/* Ethernet-II snap header (RFC1042 for most EtherTypes) */
+static unsigned char rfc1042_header[] =
+{ 0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00 };
+/* Bridge-Tunnel header (for EtherTypes ETH_P_AARP and ETH_P_IPX) */
+static unsigned char bridge_tunnel_header[] =
+{ 0xaa, 0xaa, 0x03, 0x00, 0x00, 0xf8 };
+/* No encapsulation header if EtherType < 0x600 (=length) */
+
+/* Called by ieee80211_rx_frame_decrypt */
+static int ieee80211_is_eapol_frame(struct ieee80211_device *ieee,
+ struct sk_buff *skb, size_t hdrlen)
+{
+ struct net_device *dev = ieee->dev;
+ u16 fc, ethertype;
+ struct ieee80211_hdr_4addr *hdr;
+ u8 *pos;
+
+ if (skb->len < 24)
+ return 0;
+
+ hdr = (struct ieee80211_hdr_4addr *) skb->data;
+ fc = le16_to_cpu(hdr->frame_ctl);
+
+ /* check that the frame is unicast frame to us */
+ if ((fc & (IEEE80211_FCTL_TODS | IEEE80211_FCTL_FROMDS)) ==
+ IEEE80211_FCTL_TODS &&
+ memcmp(hdr->addr1, dev->dev_addr, ETH_ALEN) == 0 &&
+ memcmp(hdr->addr3, dev->dev_addr, ETH_ALEN) == 0) {
+ /* ToDS frame with own addr BSSID and DA */
+ } else if ((fc & (IEEE80211_FCTL_TODS | IEEE80211_FCTL_FROMDS)) ==
+ IEEE80211_FCTL_FROMDS &&
+ memcmp(hdr->addr1, dev->dev_addr, ETH_ALEN) == 0) {
+ /* FromDS frame with own addr as DA */
+ } else
+ return 0;
+
+ if (skb->len < 24 + 8)
+ return 0;
+
+ /* check for port access entity Ethernet type */
+// pos = skb->data + 24;
+ pos = skb->data + hdrlen;
+ ethertype = (pos[6] << 8) | pos[7];
+ if (ethertype == ETH_P_PAE)
+ return 1;
+
+ return 0;
+}
+
+/* Called only as a tasklet (software IRQ), by ieee80211_rx */
+static inline int
+ieee80211_rx_frame_decrypt(struct ieee80211_device *ieee, struct sk_buff *skb,
+ struct ieee80211_crypt_data *crypt)
+{
+ struct ieee80211_hdr_4addr *hdr;
+ int res, hdrlen;
+
+ if (crypt == NULL || crypt->ops->decrypt_mpdu == NULL)
+ return 0;
+ if (ieee->hwsec_active)
+ {
+ cb_desc *tcb_desc = (cb_desc *)(skb->cb+ MAX_DEV_ADDR_SIZE);
+ tcb_desc->bHwSec = 1;
+ }
+ hdr = (struct ieee80211_hdr_4addr *) skb->data;
+ hdrlen = ieee80211_get_hdrlen(le16_to_cpu(hdr->frame_ctl));
+
+ if (ieee->tkip_countermeasures &&
+ strcmp(crypt->ops->name, "TKIP") == 0) {
+ if (net_ratelimit()) {
+ printk(KERN_DEBUG "%s: TKIP countermeasures: dropped "
+ "received packet from %pM\n",
+ ieee->dev->name, hdr->addr2);
+ }
+ return -1;
+ }
+
+ atomic_inc(&crypt->refcnt);
+ res = crypt->ops->decrypt_mpdu(skb, hdrlen, crypt->priv);
+ atomic_dec(&crypt->refcnt);
+ if (res < 0) {
+ IEEE80211_DEBUG_DROP(
+ "decryption failed (SA=%pM"
+ ") res=%d\n", hdr->addr2, res);
+ if (res == -2)
+ IEEE80211_DEBUG_DROP("Decryption failed ICV "
+ "mismatch (key %d)\n",
+ skb->data[hdrlen + 3] >> 6);
+ ieee->ieee_stats.rx_discards_undecryptable++;
+ return -1;
+ }
+
+ return res;
+}
+
+
+/* Called only as a tasklet (software IRQ), by ieee80211_rx */
+static inline int
+ieee80211_rx_frame_decrypt_msdu(struct ieee80211_device *ieee, struct sk_buff *skb,
+ int keyidx, struct ieee80211_crypt_data *crypt)
+{
+ struct ieee80211_hdr_4addr *hdr;
+ int res, hdrlen;
+
+ if (crypt == NULL || crypt->ops->decrypt_msdu == NULL)
+ return 0;
+ if (ieee->hwsec_active)
+ {
+ cb_desc *tcb_desc = (cb_desc *)(skb->cb+ MAX_DEV_ADDR_SIZE);
+ tcb_desc->bHwSec = 1;
+ }
+
+ hdr = (struct ieee80211_hdr_4addr *) skb->data;
+ hdrlen = ieee80211_get_hdrlen(le16_to_cpu(hdr->frame_ctl));
+
+ atomic_inc(&crypt->refcnt);
+ res = crypt->ops->decrypt_msdu(skb, keyidx, hdrlen, crypt->priv);
+ atomic_dec(&crypt->refcnt);
+ if (res < 0) {
+ printk(KERN_DEBUG "%s: MSDU decryption/MIC verification failed"
+ " (SA=%pM keyidx=%d)\n",
+ ieee->dev->name, hdr->addr2, keyidx);
+ return -1;
+ }
+
+ return 0;
+}
+
+
+/* this function is stolen from ipw2200 driver*/
+#define IEEE_PACKET_RETRY_TIME (5*HZ)
+static int is_duplicate_packet(struct ieee80211_device *ieee,
+ struct ieee80211_hdr_4addr *header)
+{
+ u16 fc = le16_to_cpu(header->frame_ctl);
+ u16 sc = le16_to_cpu(header->seq_ctl);
+ u16 seq = WLAN_GET_SEQ_SEQ(sc);
+ u16 frag = WLAN_GET_SEQ_FRAG(sc);
+ u16 *last_seq, *last_frag;
+ unsigned long *last_time;
+ struct ieee80211_hdr_3addrqos *hdr_3addrqos;
+ struct ieee80211_hdr_4addrqos *hdr_4addrqos;
+ u8 tid;
+
+
+ //TO2DS and QoS
+ if(((fc & IEEE80211_FCTL_DSTODS) == IEEE80211_FCTL_DSTODS)&&IEEE80211_QOS_HAS_SEQ(fc)) {
+ hdr_4addrqos = (struct ieee80211_hdr_4addrqos *)header;
+ tid = le16_to_cpu(hdr_4addrqos->qos_ctl) & IEEE80211_QCTL_TID;
+ tid = UP2AC(tid);
+ tid ++;
+ } else if(IEEE80211_QOS_HAS_SEQ(fc)) { //QoS
+ hdr_3addrqos = (struct ieee80211_hdr_3addrqos *)header;
+ tid = le16_to_cpu(hdr_3addrqos->qos_ctl) & IEEE80211_QCTL_TID;
+ tid = UP2AC(tid);
+ tid ++;
+ } else { // no QoS
+ tid = 0;
+ }
+
+ switch (ieee->iw_mode) {
+ case IW_MODE_ADHOC:
+ {
+ struct list_head *p;
+ struct ieee_ibss_seq *entry = NULL;
+ u8 *mac = header->addr2;
+ int index = mac[5] % IEEE_IBSS_MAC_HASH_SIZE;
+
+ list_for_each(p, &ieee->ibss_mac_hash[index]) {
+ entry = list_entry(p, struct ieee_ibss_seq, list);
+ if (!memcmp(entry->mac, mac, ETH_ALEN))
+ break;
+ }
+ // if (memcmp(entry->mac, mac, ETH_ALEN)){
+ if (p == &ieee->ibss_mac_hash[index]) {
+ entry = kmalloc(sizeof(struct ieee_ibss_seq), GFP_ATOMIC);
+ if (!entry) {
+ printk(KERN_WARNING "Cannot malloc new mac entry\n");
+ return 0;
+ }
+ memcpy(entry->mac, mac, ETH_ALEN);
+ entry->seq_num[tid] = seq;
+ entry->frag_num[tid] = frag;
+ entry->packet_time[tid] = jiffies;
+ list_add(&entry->list, &ieee->ibss_mac_hash[index]);
+ return 0;
+ }
+ last_seq = &entry->seq_num[tid];
+ last_frag = &entry->frag_num[tid];
+ last_time = &entry->packet_time[tid];
+ break;
+ }
+
+ case IW_MODE_INFRA:
+ last_seq = &ieee->last_rxseq_num[tid];
+ last_frag = &ieee->last_rxfrag_num[tid];
+ last_time = &ieee->last_packet_time[tid];
+
+ break;
+ default:
+ return 0;
+ }
+
+// if(tid != 0) {
+// printk(KERN_WARNING ":)))))))))))%x %x %x, fc(%x)\n", tid, *last_seq, seq, header->frame_ctl);
+// }
+ if ((*last_seq == seq) &&
+ time_after(*last_time + IEEE_PACKET_RETRY_TIME, jiffies)) {
+ if (*last_frag == frag){
+ //printk(KERN_WARNING "[1] go drop!\n");
+ goto drop;
+
+ }
+ if (*last_frag + 1 != frag)
+ /* out-of-order fragment */
+ //printk(KERN_WARNING "[2] go drop!\n");
+ goto drop;
+ } else
+ *last_seq = seq;
+
+ *last_frag = frag;
+ *last_time = jiffies;
+ return 0;
+
+drop:
+// BUG_ON(!(fc & IEEE80211_FCTL_RETRY));
+// printk("DUP\n");
+
+ return 1;
+}
+
+static bool AddReorderEntry(PRX_TS_RECORD pTS, PRX_REORDER_ENTRY pReorderEntry)
+{
+ struct list_head *pList = &pTS->RxPendingPktList;
+ while(pList->next != &pTS->RxPendingPktList)
+ {
+ if( SN_LESS(pReorderEntry->SeqNum, ((PRX_REORDER_ENTRY)list_entry(pList->next,RX_REORDER_ENTRY,List))->SeqNum) )
+ {
+ pList = pList->next;
+ }
+ else if( SN_EQUAL(pReorderEntry->SeqNum, ((PRX_REORDER_ENTRY)list_entry(pList->next,RX_REORDER_ENTRY,List))->SeqNum) )
+ {
+ return false;
+ }
+ else
+ {
+ break;
+ }
+ }
+ pReorderEntry->List.next = pList->next;
+ pReorderEntry->List.next->prev = &pReorderEntry->List;
+ pReorderEntry->List.prev = pList;
+ pList->next = &pReorderEntry->List;
+
+ return true;
+}
+
+void ieee80211_indicate_packets(struct ieee80211_device *ieee, struct ieee80211_rxb **prxbIndicateArray,u8 index)
+{
+ u8 i = 0 , j=0;
+ u16 ethertype;
+// if(index > 1)
+// IEEE80211_DEBUG(IEEE80211_DL_REORDER,"%s(): hahahahhhh, We indicate packet from reorder list, index is %u\n",__func__,index);
+ for(j = 0; j<index; j++)
+ {
+//added by amy for reorder
+ struct ieee80211_rxb *prxb = prxbIndicateArray[j];
+ for(i = 0; i<prxb->nr_subframes; i++) {
+ struct sk_buff *sub_skb = prxb->subframes[i];
+
+ /* convert hdr + possible LLC headers into Ethernet header */
+ ethertype = (sub_skb->data[6] << 8) | sub_skb->data[7];
+ if (sub_skb->len >= 8 &&
+ ((memcmp(sub_skb->data, rfc1042_header, SNAP_SIZE) == 0 &&
+ ethertype != ETH_P_AARP && ethertype != ETH_P_IPX) ||
+ memcmp(sub_skb->data, bridge_tunnel_header, SNAP_SIZE) == 0)) {
+ /* remove RFC1042 or Bridge-Tunnel encapsulation and
+ * replace EtherType */
+ skb_pull(sub_skb, SNAP_SIZE);
+ memcpy(skb_push(sub_skb, ETH_ALEN), prxb->src, ETH_ALEN);
+ memcpy(skb_push(sub_skb, ETH_ALEN), prxb->dst, ETH_ALEN);
+ } else {
+ u16 len;
+ /* Leave Ethernet header part of hdr and full payload */
+ len = htons(sub_skb->len);
+ memcpy(skb_push(sub_skb, 2), &len, 2);
+ memcpy(skb_push(sub_skb, ETH_ALEN), prxb->src, ETH_ALEN);
+ memcpy(skb_push(sub_skb, ETH_ALEN), prxb->dst, ETH_ALEN);
+ }
+ //stats->rx_packets++;
+ //stats->rx_bytes += sub_skb->len;
+
+ /* Indicat the packets to upper layer */
+ if (sub_skb) {
+ //printk("0skb_len(%d)\n", skb->len);
+ sub_skb->protocol = eth_type_trans(sub_skb, ieee->dev);
+ memset(sub_skb->cb, 0, sizeof(sub_skb->cb));
+ sub_skb->dev = ieee->dev;
+ sub_skb->ip_summed = CHECKSUM_NONE; /* 802.11 crc not sufficient */
+ //skb->ip_summed = CHECKSUM_UNNECESSARY; /* 802.11 crc not sufficient */
+ ieee->last_rx_ps_time = jiffies;
+ //printk("1skb_len(%d)\n", skb->len);
+ netif_rx(sub_skb);
+ }
+ }
+ kfree(prxb);
+ prxb = NULL;
+ }
+}
+
+
+static void RxReorderIndicatePacket(struct ieee80211_device *ieee,
+ struct ieee80211_rxb *prxb,
+ PRX_TS_RECORD pTS, u16 SeqNum)
+{
+ PRT_HIGH_THROUGHPUT pHTInfo = ieee->pHTInfo;
+ PRX_REORDER_ENTRY pReorderEntry = NULL;
+ struct ieee80211_rxb *prxbIndicateArray[REORDER_WIN_SIZE];
+ u8 WinSize = pHTInfo->RxReorderWinSize;
+ u16 WinEnd = (pTS->RxIndicateSeq + WinSize -1)%4096;
+ u8 index = 0;
+ bool bMatchWinStart = false, bPktInBuf = false;
+ IEEE80211_DEBUG(IEEE80211_DL_REORDER,"%s(): Seq is %d,pTS->RxIndicateSeq is %d, WinSize is %d\n",__func__,SeqNum,pTS->RxIndicateSeq,WinSize);
+ /* Rx Reorder initialize condition.*/
+ if (pTS->RxIndicateSeq == 0xffff) {
+ pTS->RxIndicateSeq = SeqNum;
+ }
+
+ /* Drop out the packet which SeqNum is smaller than WinStart */
+ if (SN_LESS(SeqNum, pTS->RxIndicateSeq)) {
+ IEEE80211_DEBUG(IEEE80211_DL_REORDER,"Packet Drop! IndicateSeq: %d, NewSeq: %d\n",
+ pTS->RxIndicateSeq, SeqNum);
+ pHTInfo->RxReorderDropCounter++;
+ {
+ int i;
+ for(i =0; i < prxb->nr_subframes; i++) {
+ dev_kfree_skb(prxb->subframes[i]);
+ }
+ kfree(prxb);
+ prxb = NULL;
+ }
+ return;
+ }
+
+ /*
+ * Sliding window manipulation. Conditions includes:
+ * 1. Incoming SeqNum is equal to WinStart =>Window shift 1
+ * 2. Incoming SeqNum is larger than the WinEnd => Window shift N
+ */
+ if(SN_EQUAL(SeqNum, pTS->RxIndicateSeq)) {
+ pTS->RxIndicateSeq = (pTS->RxIndicateSeq + 1) % 4096;
+ bMatchWinStart = true;
+ } else if(SN_LESS(WinEnd, SeqNum)) {
+ if(SeqNum >= (WinSize - 1)) {
+ pTS->RxIndicateSeq = SeqNum + 1 -WinSize;
+ } else {
+ pTS->RxIndicateSeq = 4095 - (WinSize - (SeqNum +1)) + 1;
+ }
+ IEEE80211_DEBUG(IEEE80211_DL_REORDER, "Window Shift! IndicateSeq: %d, NewSeq: %d\n",pTS->RxIndicateSeq, SeqNum);
+ }
+
+ /*
+ * Indication process.
+ * After Packet dropping and Sliding Window shifting as above, we can now just indicate the packets
+ * with the SeqNum smaller than latest WinStart and buffer other packets.
+ */
+ /* For Rx Reorder condition:
+ * 1. All packets with SeqNum smaller than WinStart => Indicate
+ * 2. All packets with SeqNum larger than or equal to WinStart => Buffer it.
+ */
+ if(bMatchWinStart) {
+ /* Current packet is going to be indicated.*/
+ IEEE80211_DEBUG(IEEE80211_DL_REORDER, "Packets indication!! IndicateSeq: %d, NewSeq: %d\n",\
+ pTS->RxIndicateSeq, SeqNum);
+ prxbIndicateArray[0] = prxb;
+// printk("========================>%s(): SeqNum is %d\n",__func__,SeqNum);
+ index = 1;
+ } else {
+ /* Current packet is going to be inserted into pending list.*/
+ //IEEE80211_DEBUG(IEEE80211_DL_REORDER,"%s(): We RX no ordered packed, insert to ordered list\n",__func__);
+ if(!list_empty(&ieee->RxReorder_Unused_List)) {
+ pReorderEntry = (PRX_REORDER_ENTRY)list_entry(ieee->RxReorder_Unused_List.next,RX_REORDER_ENTRY,List);
+ list_del_init(&pReorderEntry->List);
+
+ /* Make a reorder entry and insert into a the packet list.*/
+ pReorderEntry->SeqNum = SeqNum;
+ pReorderEntry->prxb = prxb;
+ // IEEE80211_DEBUG(IEEE80211_DL_REORDER,"%s(): pREorderEntry->SeqNum is %d\n",__func__,pReorderEntry->SeqNum);
+
+ if(!AddReorderEntry(pTS, pReorderEntry)) {
+ IEEE80211_DEBUG(IEEE80211_DL_REORDER, "%s(): Duplicate packet is dropped!! IndicateSeq: %d, NewSeq: %d\n",
+ __func__, pTS->RxIndicateSeq, SeqNum);
+ list_add_tail(&pReorderEntry->List,&ieee->RxReorder_Unused_List);
+ {
+ int i;
+ for(i =0; i < prxb->nr_subframes; i++) {
+ dev_kfree_skb(prxb->subframes[i]);
+ }
+ kfree(prxb);
+ prxb = NULL;
+ }
+ } else {
+ IEEE80211_DEBUG(IEEE80211_DL_REORDER,
+ "Pkt insert into buffer!! IndicateSeq: %d, NewSeq: %d\n",pTS->RxIndicateSeq, SeqNum);
+ }
+ }
+ else {
+ /*
+ * Packets are dropped if there is not enough reorder entries.
+ * This part shall be modified!! We can just indicate all the
+ * packets in buffer and get reorder entries.
+ */
+ IEEE80211_DEBUG(IEEE80211_DL_ERR, "RxReorderIndicatePacket(): There is no reorder entry!! Packet is dropped!!\n");
+ {
+ int i;
+ for(i =0; i < prxb->nr_subframes; i++) {
+ dev_kfree_skb(prxb->subframes[i]);
+ }
+ kfree(prxb);
+ prxb = NULL;
+ }
+ }
+ }
+
+ /* Check if there is any packet need indicate.*/
+ while(!list_empty(&pTS->RxPendingPktList)) {
+ IEEE80211_DEBUG(IEEE80211_DL_REORDER,"%s(): start RREORDER indicate\n",__func__);
+ pReorderEntry = (PRX_REORDER_ENTRY)list_entry(pTS->RxPendingPktList.prev,RX_REORDER_ENTRY,List);
+ if (SN_LESS(pReorderEntry->SeqNum, pTS->RxIndicateSeq) ||
+ SN_EQUAL(pReorderEntry->SeqNum, pTS->RxIndicateSeq))
+ {
+ /* This protect buffer from overflow. */
+ if (index >= REORDER_WIN_SIZE) {
+ IEEE80211_DEBUG(IEEE80211_DL_ERR, "RxReorderIndicatePacket(): Buffer overflow!! \n");
+ bPktInBuf = true;
+ break;
+ }
+
+ list_del_init(&pReorderEntry->List);
+
+ if(SN_EQUAL(pReorderEntry->SeqNum, pTS->RxIndicateSeq))
+ pTS->RxIndicateSeq = (pTS->RxIndicateSeq + 1) % 4096;
+
+ IEEE80211_DEBUG(IEEE80211_DL_REORDER,"Packets indication!! IndicateSeq: %d, NewSeq: %d\n",pTS->RxIndicateSeq, SeqNum);
+ prxbIndicateArray[index] = pReorderEntry->prxb;
+ // printk("========================>%s(): pReorderEntry->SeqNum is %d\n",__func__,pReorderEntry->SeqNum);
+ index++;
+
+ list_add_tail(&pReorderEntry->List,&ieee->RxReorder_Unused_List);
+ } else {
+ bPktInBuf = true;
+ break;
+ }
+ }
+
+ /* Handling pending timer. Set this timer to prevent from long time Rx buffering.*/
+ if (index>0) {
+ // Cancel previous pending timer.
+ // del_timer_sync(&pTS->RxPktPendingTimer);
+ pTS->RxTimeoutIndicateSeq = 0xffff;
+
+ // Indicate packets
+ if(index>REORDER_WIN_SIZE){
+ IEEE80211_DEBUG(IEEE80211_DL_ERR, "RxReorderIndicatePacket(): Rx Reorer buffer full!! \n");
+ return;
+ }
+ ieee80211_indicate_packets(ieee, prxbIndicateArray, index);
+ }
+
+ if (bPktInBuf && pTS->RxTimeoutIndicateSeq==0xffff) {
+ // Set new pending timer.
+ IEEE80211_DEBUG(IEEE80211_DL_REORDER,"%s(): SET rx timeout timer\n", __func__);
+ pTS->RxTimeoutIndicateSeq = pTS->RxIndicateSeq;
+ if(timer_pending(&pTS->RxPktPendingTimer))
+ del_timer_sync(&pTS->RxPktPendingTimer);
+ pTS->RxPktPendingTimer.expires = jiffies + MSECS(pHTInfo->RxReorderPendingTime);
+ add_timer(&pTS->RxPktPendingTimer);
+ }
+}
+
+static u8 parse_subframe(struct sk_buff *skb,
+ struct ieee80211_rx_stats *rx_stats,
+ struct ieee80211_rxb *rxb, u8 *src, u8 *dst)
+{
+ struct ieee80211_hdr_3addr *hdr = (struct ieee80211_hdr_3addr *)skb->data;
+ u16 fc = le16_to_cpu(hdr->frame_ctl);
+
+ u16 LLCOffset= sizeof(struct ieee80211_hdr_3addr);
+ u16 ChkLength;
+ bool bIsAggregateFrame = false;
+ u16 nSubframe_Length;
+ u8 nPadding_Length = 0;
+ u16 SeqNum=0;
+
+ struct sk_buff *sub_skb;
+ u8 *data_ptr;
+ /* just for debug purpose */
+ SeqNum = WLAN_GET_SEQ_SEQ(le16_to_cpu(hdr->seq_ctl));
+
+ if ((IEEE80211_QOS_HAS_SEQ(fc))&&\
+ (((frameqos *)(skb->data + IEEE80211_3ADDR_LEN))->field.reserved)) {
+ bIsAggregateFrame = true;
+ }
+
+ if (IEEE80211_QOS_HAS_SEQ(fc)) {
+ LLCOffset += 2;
+ }
+
+ if (rx_stats->bContainHTC) {
+ LLCOffset += sHTCLng;
+ }
+ //printk("ChkLength = %d\n", LLCOffset);
+ // Null packet, don't indicate it to upper layer
+ ChkLength = LLCOffset;/* + (Frame_WEP(frame)!=0 ?Adapter->MgntInfo.SecurityInfo.EncryptionHeadOverhead:0);*/
+
+ if (skb->len <= ChkLength)
+ return 0;
+
+ skb_pull(skb, LLCOffset);
+
+ if(!bIsAggregateFrame) {
+ rxb->nr_subframes = 1;
+#ifdef JOHN_NOCPY
+ rxb->subframes[0] = skb;
+#else
+ rxb->subframes[0] = skb_copy(skb, GFP_ATOMIC);
+#endif
+
+ memcpy(rxb->src,src,ETH_ALEN);
+ memcpy(rxb->dst,dst,ETH_ALEN);
+ //IEEE80211_DEBUG_DATA(IEEE80211_DL_RX,skb->data,skb->len);
+ return 1;
+ } else {
+ rxb->nr_subframes = 0;
+ memcpy(rxb->src,src,ETH_ALEN);
+ memcpy(rxb->dst,dst,ETH_ALEN);
+ while(skb->len > ETHERNET_HEADER_SIZE) {
+ /* Offset 12 denote 2 mac address */
+ nSubframe_Length = *((u16 *)(skb->data + 12));
+ //==m==>change the length order
+ nSubframe_Length = (nSubframe_Length>>8) + (nSubframe_Length<<8);
+
+ if (skb->len<(ETHERNET_HEADER_SIZE + nSubframe_Length)) {
+ printk("%s: A-MSDU parse error!! pRfd->nTotalSubframe : %d\n",\
+ __func__, rxb->nr_subframes);
+ printk("%s: A-MSDU parse error!! Subframe Length: %d\n",__func__, nSubframe_Length);
+ printk("nRemain_Length is %d and nSubframe_Length is : %d\n",skb->len,nSubframe_Length);
+ printk("The Packet SeqNum is %d\n",SeqNum);
+ return 0;
+ }
+
+ /* move the data point to data content */
+ skb_pull(skb, ETHERNET_HEADER_SIZE);
+
+#ifdef JOHN_NOCPY
+ sub_skb = skb_clone(skb, GFP_ATOMIC);
+ sub_skb->len = nSubframe_Length;
+ sub_skb->tail = sub_skb->data + nSubframe_Length;
+#else
+ /* Allocate new skb for releasing to upper layer */
+ sub_skb = dev_alloc_skb(nSubframe_Length + 12);
+ if (!sub_skb)
+ return 0;
+ skb_reserve(sub_skb, 12);
+ data_ptr = (u8 *)skb_put(sub_skb, nSubframe_Length);
+ memcpy(data_ptr, skb->data, nSubframe_Length);
+#endif
+ rxb->subframes[rxb->nr_subframes++] = sub_skb;
+ if (rxb->nr_subframes >= MAX_SUBFRAME_COUNT) {
+ IEEE80211_DEBUG_RX("ParseSubframe(): Too many Subframes! Packets dropped!\n");
+ break;
+ }
+ skb_pull(skb, nSubframe_Length);
+
+ if (skb->len != 0) {
+ nPadding_Length = 4 - ((nSubframe_Length + ETHERNET_HEADER_SIZE) % 4);
+ if (nPadding_Length == 4) {
+ nPadding_Length = 0;
+ }
+
+ if (skb->len < nPadding_Length) {
+ return 0;
+ }
+
+ skb_pull(skb, nPadding_Length);
+ }
+ }
+#ifdef JOHN_NOCPY
+ dev_kfree_skb(skb);
+#endif
+ //{just for debug added by david
+ //printk("AMSDU::rxb->nr_subframes = %d\n",rxb->nr_subframes);
+ //}
+ return rxb->nr_subframes;
+ }
+}
+
+/* All received frames are sent to this function. @skb contains the frame in
+ * IEEE 802.11 format, i.e., in the format it was sent over air.
+ * This function is called only as a tasklet (software IRQ). */
+int ieee80211_rx(struct ieee80211_device *ieee, struct sk_buff *skb,
+ struct ieee80211_rx_stats *rx_stats)
+{
+ struct net_device *dev = ieee->dev;
+ struct ieee80211_hdr_4addr *hdr;
+ //struct ieee80211_hdr_3addrqos *hdr;
+
+ size_t hdrlen;
+ u16 fc, type, stype, sc;
+ struct net_device_stats *stats;
+ unsigned int frag;
+ u8 *payload;
+ u16 ethertype;
+ //added by amy for reorder
+ u8 TID = 0;
+ u16 SeqNum = 0;
+ PRX_TS_RECORD pTS = NULL;
+ //bool bIsAggregateFrame = false;
+ //added by amy for reorder
+#ifdef NOT_YET
+ struct net_device *wds = NULL;
+ struct sk_buff *skb2 = NULL;
+ struct net_device *wds = NULL;
+ int frame_authorized = 0;
+ int from_assoc_ap = 0;
+ void *sta = NULL;
+#endif
+// u16 qos_ctl = 0;
+ u8 dst[ETH_ALEN];
+ u8 src[ETH_ALEN];
+ u8 bssid[ETH_ALEN];
+ struct ieee80211_crypt_data *crypt = NULL;
+ int keyidx = 0;
+
+ int i;
+ struct ieee80211_rxb *rxb = NULL;
+ // cheat the the hdr type
+ hdr = (struct ieee80211_hdr_4addr *)skb->data;
+ stats = &ieee->stats;
+
+ if (skb->len < 10) {
+ printk(KERN_INFO "%s: SKB length < 10\n",
+ dev->name);
+ goto rx_dropped;
+ }
+
+ fc = le16_to_cpu(hdr->frame_ctl);
+ type = WLAN_FC_GET_TYPE(fc);
+ stype = WLAN_FC_GET_STYPE(fc);
+ sc = le16_to_cpu(hdr->seq_ctl);
+
+ frag = WLAN_GET_SEQ_FRAG(sc);
+ hdrlen = ieee80211_get_hdrlen(fc);
+
+ if (HTCCheck(ieee, skb->data))
+ {
+ if(net_ratelimit())
+ printk("find HTCControl\n");
+ hdrlen += 4;
+ rx_stats->bContainHTC = true;
+ }
+
+ //IEEE80211_DEBUG_DATA(IEEE80211_DL_DATA, skb->data, skb->len);
+#ifdef NOT_YET
+ /* Put this code here so that we avoid duplicating it in all
+ * Rx paths. - Jean II */
+#ifdef IW_WIRELESS_SPY /* defined in iw_handler.h */
+ /* If spy monitoring on */
+ if (iface->spy_data.spy_number > 0) {
+ struct iw_quality wstats;
+ wstats.level = rx_stats->rssi;
+ wstats.noise = rx_stats->noise;
+ wstats.updated = 6; /* No qual value */
+ /* Update spy records */
+ wireless_spy_update(dev, hdr->addr2, &wstats);
+ }
+#endif /* IW_WIRELESS_SPY */
+ hostap_update_rx_stats(local->ap, hdr, rx_stats);
+#endif
+
+ if (ieee->iw_mode == IW_MODE_MONITOR) {
+ ieee80211_monitor_rx(ieee, skb, rx_stats);
+ stats->rx_packets++;
+ stats->rx_bytes += skb->len;
+ return 1;
+ }
+
+ if (ieee->host_decrypt) {
+ int idx = 0;
+ if (skb->len >= hdrlen + 3)
+ idx = skb->data[hdrlen + 3] >> 6;
+ crypt = ieee->crypt[idx];
+#ifdef NOT_YET
+ sta = NULL;
+
+ /* Use station specific key to override default keys if the
+ * receiver address is a unicast address ("individual RA"). If
+ * bcrx_sta_key parameter is set, station specific key is used
+ * even with broad/multicast targets (this is against IEEE
+ * 802.11, but makes it easier to use different keys with
+ * stations that do not support WEP key mapping). */
+
+ if (!(hdr->addr1[0] & 0x01) || local->bcrx_sta_key)
+ (void) hostap_handle_sta_crypto(local, hdr, &crypt,
+ &sta);
+#endif
+
+ /* allow NULL decrypt to indicate an station specific override
+ * for default encryption */
+ if (crypt && (crypt->ops == NULL ||
+ crypt->ops->decrypt_mpdu == NULL))
+ crypt = NULL;
+
+ if (!crypt && (fc & IEEE80211_FCTL_WEP)) {
+ /* This seems to be triggered by some (multicast?)
+ * frames from other than current BSS, so just drop the
+ * frames silently instead of filling system log with
+ * these reports. */
+ IEEE80211_DEBUG_DROP("Decryption failed (not set)"
+ " (SA=%pM)\n",
+ hdr->addr2);
+ ieee->ieee_stats.rx_discards_undecryptable++;
+ goto rx_dropped;
+ }
+ }
+
+ if (skb->len < IEEE80211_DATA_HDR3_LEN)
+ goto rx_dropped;
+
+ // if QoS enabled, should check the sequence for each of the AC
+ if( (ieee->pHTInfo->bCurRxReorderEnable == false) || !ieee->current_network.qos_data.active|| !IsDataFrame(skb->data) || IsLegacyDataFrame(skb->data)){
+ if (is_duplicate_packet(ieee, hdr))
+ goto rx_dropped;
+
+ }
+ else
+ {
+ PRX_TS_RECORD pRxTS = NULL;
+ //IEEE80211_DEBUG(IEEE80211_DL_REORDER,"%s(): QOS ENABLE AND RECEIVE QOS DATA , we will get Ts, tid:%d\n",__func__, tid);
+ if(GetTs(
+ ieee,
+ (PTS_COMMON_INFO *) &pRxTS,
+ hdr->addr2,
+ (u8)Frame_QoSTID((u8 *)(skb->data)),
+ RX_DIR,
+ true))
+ {
+
+ // IEEE80211_DEBUG(IEEE80211_DL_REORDER,"%s(): pRxTS->RxLastFragNum is %d,frag is %d,pRxTS->RxLastSeqNum is %d,seq is %d\n",__func__,pRxTS->RxLastFragNum,frag,pRxTS->RxLastSeqNum,WLAN_GET_SEQ_SEQ(sc));
+ if ((fc & (1<<11)) &&
+ (frag == pRxTS->RxLastFragNum) &&
+ (WLAN_GET_SEQ_SEQ(sc) == pRxTS->RxLastSeqNum)) {
+ goto rx_dropped;
+ }
+ else
+ {
+ pRxTS->RxLastFragNum = frag;
+ pRxTS->RxLastSeqNum = WLAN_GET_SEQ_SEQ(sc);
+ }
+ }
+ else
+ {
+ IEEE80211_DEBUG(IEEE80211_DL_ERR, "%s(): No TS!! Skip the check!!\n",__func__);
+ goto rx_dropped;
+ }
+ }
+ if (type == IEEE80211_FTYPE_MGMT) {
+
+
+ //IEEE80211_DEBUG_DATA(IEEE80211_DL_DATA, skb->data, skb->len);
+ if (ieee80211_rx_frame_mgmt(ieee, skb, rx_stats, type, stype))
+ goto rx_dropped;
+ else
+ goto rx_exit;
+ }
+
+ /* Data frame - extract src/dst addresses */
+ switch (fc & (IEEE80211_FCTL_FROMDS | IEEE80211_FCTL_TODS)) {
+ case IEEE80211_FCTL_FROMDS:
+ memcpy(dst, hdr->addr1, ETH_ALEN);
+ memcpy(src, hdr->addr3, ETH_ALEN);
+ memcpy(bssid, hdr->addr2, ETH_ALEN);
+ break;
+ case IEEE80211_FCTL_TODS:
+ memcpy(dst, hdr->addr3, ETH_ALEN);
+ memcpy(src, hdr->addr2, ETH_ALEN);
+ memcpy(bssid, hdr->addr1, ETH_ALEN);
+ break;
+ case IEEE80211_FCTL_FROMDS | IEEE80211_FCTL_TODS:
+ if (skb->len < IEEE80211_DATA_HDR4_LEN)
+ goto rx_dropped;
+ memcpy(dst, hdr->addr3, ETH_ALEN);
+ memcpy(src, hdr->addr4, ETH_ALEN);
+ memcpy(bssid, ieee->current_network.bssid, ETH_ALEN);
+ break;
+ case 0:
+ memcpy(dst, hdr->addr1, ETH_ALEN);
+ memcpy(src, hdr->addr2, ETH_ALEN);
+ memcpy(bssid, hdr->addr3, ETH_ALEN);
+ break;
+ }
+
+#ifdef NOT_YET
+ if (hostap_rx_frame_wds(ieee, hdr, fc, &wds))
+ goto rx_dropped;
+ if (wds) {
+ skb->dev = dev = wds;
+ stats = hostap_get_stats(dev);
+ }
+
+ if (ieee->iw_mode == IW_MODE_MASTER && !wds &&
+ (fc & (IEEE80211_FCTL_TODS | IEEE80211_FCTL_FROMDS)) == IEEE80211_FCTL_FROMDS &&
+ ieee->stadev &&
+ memcmp(hdr->addr2, ieee->assoc_ap_addr, ETH_ALEN) == 0) {
+ /* Frame from BSSID of the AP for which we are a client */
+ skb->dev = dev = ieee->stadev;
+ stats = hostap_get_stats(dev);
+ from_assoc_ap = 1;
+ }
+#endif
+
+ dev->last_rx = jiffies;
+
+#ifdef NOT_YET
+ if ((ieee->iw_mode == IW_MODE_MASTER ||
+ ieee->iw_mode == IW_MODE_REPEAT) &&
+ !from_assoc_ap) {
+ switch (hostap_handle_sta_rx(ieee, dev, skb, rx_stats,
+ wds != NULL)) {
+ case AP_RX_CONTINUE_NOT_AUTHORIZED:
+ frame_authorized = 0;
+ break;
+ case AP_RX_CONTINUE:
+ frame_authorized = 1;
+ break;
+ case AP_RX_DROP:
+ goto rx_dropped;
+ case AP_RX_EXIT:
+ goto rx_exit;
+ }
+ }
+#endif
+ //IEEE80211_DEBUG_DATA(IEEE80211_DL_DATA, skb->data, skb->len);
+ /* Nullfunc frames may have PS-bit set, so they must be passed to
+ * hostap_handle_sta_rx() before being dropped here. */
+ if (stype != IEEE80211_STYPE_DATA &&
+ stype != IEEE80211_STYPE_DATA_CFACK &&
+ stype != IEEE80211_STYPE_DATA_CFPOLL &&
+ stype != IEEE80211_STYPE_DATA_CFACKPOLL&&
+ stype != IEEE80211_STYPE_QOS_DATA//add by David,2006.8.4
+ ) {
+ if (stype != IEEE80211_STYPE_NULLFUNC)
+ IEEE80211_DEBUG_DROP(
+ "RX: dropped data frame "
+ "with no data (type=0x%02x, "
+ "subtype=0x%02x, len=%d)\n",
+ type, stype, skb->len);
+ goto rx_dropped;
+ }
+ if (memcmp(bssid, ieee->current_network.bssid, ETH_ALEN))
+ goto rx_dropped;
+
+ /* skb: hdr + (possibly fragmented, possibly encrypted) payload */
+
+ if (ieee->host_decrypt && (fc & IEEE80211_FCTL_WEP) &&
+ (keyidx = ieee80211_rx_frame_decrypt(ieee, skb, crypt)) < 0)
+ {
+ printk("decrypt frame error\n");
+ goto rx_dropped;
+ }
+
+
+ hdr = (struct ieee80211_hdr_4addr *) skb->data;
+
+ /* skb: hdr + (possibly fragmented) plaintext payload */
+ // PR: FIXME: hostap has additional conditions in the "if" below:
+ // ieee->host_decrypt && (fc & IEEE80211_FCTL_WEP) &&
+ if ((frag != 0 || (fc & IEEE80211_FCTL_MOREFRAGS))) {
+ int flen;
+ struct sk_buff *frag_skb = ieee80211_frag_cache_get(ieee, hdr);
+ IEEE80211_DEBUG_FRAG("Rx Fragment received (%u)\n", frag);
+
+ if (!frag_skb) {
+ IEEE80211_DEBUG(IEEE80211_DL_RX | IEEE80211_DL_FRAG,
+ "Rx cannot get skb from fragment "
+ "cache (morefrag=%d seq=%u frag=%u)\n",
+ (fc & IEEE80211_FCTL_MOREFRAGS) != 0,
+ WLAN_GET_SEQ_SEQ(sc), frag);
+ goto rx_dropped;
+ }
+ flen = skb->len;
+ if (frag != 0)
+ flen -= hdrlen;
+
+ if (frag_skb->tail + flen > frag_skb->end) {
+ printk(KERN_WARNING "%s: host decrypted and "
+ "reassembled frame did not fit skb\n",
+ dev->name);
+ ieee80211_frag_cache_invalidate(ieee, hdr);
+ goto rx_dropped;
+ }
+
+ 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);
+ } else {
+ /* append frame payload to the end of the fragment
+ * cache skb */
+ memcpy(skb_put(frag_skb, flen), skb->data + hdrlen,
+ flen);
+ }
+ dev_kfree_skb_any(skb);
+ skb = NULL;
+
+ if (fc & IEEE80211_FCTL_MOREFRAGS) {
+ /* more fragments expected - leave the skb in fragment
+ * cache for now; it will be delivered to upper layers
+ * after all fragments have been received */
+ goto rx_exit;
+ }
+
+ /* this was the last fragment and the frame will be
+ * delivered, so remove skb from fragment cache */
+ skb = frag_skb;
+ hdr = (struct ieee80211_hdr_4addr *) skb->data;
+ ieee80211_frag_cache_invalidate(ieee, hdr);
+ }
+
+ /* skb: hdr + (possible reassembled) full MSDU payload; possibly still
+ * encrypted/authenticated */
+ if (ieee->host_decrypt && (fc & IEEE80211_FCTL_WEP) &&
+ ieee80211_rx_frame_decrypt_msdu(ieee, skb, keyidx, crypt))
+ {
+ printk("==>decrypt msdu error\n");
+ goto rx_dropped;
+ }
+
+ //added by amy for AP roaming
+ ieee->LinkDetectInfo.NumRecvDataInPeriod++;
+ ieee->LinkDetectInfo.NumRxOkInPeriod++;
+
+ hdr = (struct ieee80211_hdr_4addr *) skb->data;
+ if (crypt && !(fc & IEEE80211_FCTL_WEP) && !ieee->open_wep) {
+ if (/*ieee->ieee802_1x &&*/
+ ieee80211_is_eapol_frame(ieee, skb, hdrlen)) {
+
+#ifdef CONFIG_IEEE80211_DEBUG
+ /* pass unencrypted EAPOL frames even if encryption is
+ * configured */
+ struct eapol *eap = (struct eapol *)(skb->data +
+ 24);
+ IEEE80211_DEBUG_EAP("RX: IEEE 802.1X EAPOL frame: %s\n",
+ eap_get_type(eap->type));
+#endif
+ } else {
+ IEEE80211_DEBUG_DROP(
+ "encryption configured, but RX "
+ "frame not encrypted (SA=%pM)\n",
+ hdr->addr2);
+ goto rx_dropped;
+ }
+ }
+
+#ifdef CONFIG_IEEE80211_DEBUG
+ if (crypt && !(fc & IEEE80211_FCTL_WEP) &&
+ ieee80211_is_eapol_frame(ieee, skb, hdrlen)) {
+ struct eapol *eap = (struct eapol *)(skb->data +
+ 24);
+ IEEE80211_DEBUG_EAP("RX: IEEE 802.1X EAPOL frame: %s\n",
+ eap_get_type(eap->type));
+ }
+#endif
+
+ if (crypt && !(fc & IEEE80211_FCTL_WEP) && !ieee->open_wep &&
+ !ieee80211_is_eapol_frame(ieee, skb, hdrlen)) {
+ IEEE80211_DEBUG_DROP(
+ "dropped unencrypted RX data "
+ "frame from %pM"
+ " (drop_unencrypted=1)\n",
+ hdr->addr2);
+ goto rx_dropped;
+ }
+/*
+ if(ieee80211_is_eapol_frame(ieee, skb, hdrlen)) {
+ printk(KERN_WARNING "RX: IEEE802.1X EPAOL frame!\n");
+ }
+*/
+//added by amy for reorder
+ if (ieee->current_network.qos_data.active && IsQoSDataFrame(skb->data)
+ && !is_multicast_ether_addr(hdr->addr1))
+ {
+ TID = Frame_QoSTID(skb->data);
+ SeqNum = WLAN_GET_SEQ_SEQ(sc);
+ GetTs(ieee,(PTS_COMMON_INFO *) &pTS,hdr->addr2,TID,RX_DIR,true);
+ if (TID !=0 && TID !=3)
+ {
+ ieee->bis_any_nonbepkts = true;
+ }
+ }
+//added by amy for reorder
+ /* skb: hdr + (possible reassembled) full plaintext payload */
+ payload = skb->data + hdrlen;
+ //ethertype = (payload[6] << 8) | payload[7];
+ rxb = kmalloc(sizeof(struct ieee80211_rxb), GFP_ATOMIC);
+ if (rxb == NULL)
+ {
+ IEEE80211_DEBUG(IEEE80211_DL_ERR,"%s(): kmalloc rxb error\n",__func__);
+ goto rx_dropped;
+ }
+ /* to parse amsdu packets */
+ /* qos data packets & reserved bit is 1 */
+ if (parse_subframe(skb, rx_stats, rxb, src, dst) == 0) {
+ /* only to free rxb, and not submit the packets to upper layer */
+ for(i =0; i < rxb->nr_subframes; i++) {
+ dev_kfree_skb(rxb->subframes[i]);
+ }
+ kfree(rxb);
+ rxb = NULL;
+ goto rx_dropped;
+ }
+
+//added by amy for reorder
+ if(ieee->pHTInfo->bCurRxReorderEnable == false ||pTS == NULL){
+//added by amy for reorder
+ for(i = 0; i<rxb->nr_subframes; i++) {
+ struct sk_buff *sub_skb = rxb->subframes[i];
+
+ if (sub_skb) {
+ /* convert hdr + possible LLC headers into Ethernet header */
+ ethertype = (sub_skb->data[6] << 8) | sub_skb->data[7];
+ if (sub_skb->len >= 8 &&
+ ((memcmp(sub_skb->data, rfc1042_header, SNAP_SIZE) == 0 &&
+ ethertype != ETH_P_AARP && ethertype != ETH_P_IPX) ||
+ memcmp(sub_skb->data, bridge_tunnel_header, SNAP_SIZE) == 0)) {
+ /* remove RFC1042 or Bridge-Tunnel encapsulation and
+ * replace EtherType */
+ skb_pull(sub_skb, SNAP_SIZE);
+ memcpy(skb_push(sub_skb, ETH_ALEN), src, ETH_ALEN);
+ memcpy(skb_push(sub_skb, ETH_ALEN), dst, ETH_ALEN);
+ } else {
+ u16 len;
+ /* Leave Ethernet header part of hdr and full payload */
+ len = htons(sub_skb->len);
+ memcpy(skb_push(sub_skb, 2), &len, 2);
+ memcpy(skb_push(sub_skb, ETH_ALEN), src, ETH_ALEN);
+ memcpy(skb_push(sub_skb, ETH_ALEN), dst, ETH_ALEN);
+ }
+
+ stats->rx_packets++;
+ stats->rx_bytes += sub_skb->len;
+ if (is_multicast_ether_addr(dst)) {
+ stats->multicast++;
+ }
+
+ /* Indicat the packets to upper layer */
+ //printk("0skb_len(%d)\n", skb->len);
+ sub_skb->protocol = eth_type_trans(sub_skb, dev);
+ memset(sub_skb->cb, 0, sizeof(sub_skb->cb));
+ sub_skb->dev = dev;
+ sub_skb->ip_summed = CHECKSUM_NONE; /* 802.11 crc not sufficient */
+ //skb->ip_summed = CHECKSUM_UNNECESSARY; /* 802.11 crc not sufficient */
+ ieee->last_rx_ps_time = jiffies;
+ //printk("1skb_len(%d)\n", skb->len);
+ netif_rx(sub_skb);
+ }
+ }
+ kfree(rxb);
+ rxb = NULL;
+
+ }
+ else
+ {
+ IEEE80211_DEBUG(IEEE80211_DL_REORDER,"%s(): REORDER ENABLE AND PTS not NULL, and we will enter RxReorderIndicatePacket()\n",__func__);
+ RxReorderIndicatePacket(ieee, rxb, pTS, SeqNum);
+ }
+#ifndef JOHN_NOCPY
+ dev_kfree_skb(skb);
+#endif
+
+ rx_exit:
+#ifdef NOT_YET
+ if (sta)
+ hostap_handle_sta_release(sta);
+#endif
+ return 1;
+
+ rx_dropped:
+ kfree(rxb);
+ rxb = NULL;
+ stats->rx_dropped++;
+
+ /* Returning 0 indicates to caller that we have not handled the SKB--
+ * so it is still allocated and can be used again by underlying
+ * hardware as a DMA target */
+ return 0;
+}
+EXPORT_SYMBOL(ieee80211_rx);
+
+#define MGMT_FRAME_FIXED_PART_LENGTH 0x24
+
+static u8 qos_oui[QOS_OUI_LEN] = { 0x00, 0x50, 0xF2 };
+
+/*
+* Make the structure we read from the beacon packet to have
+* the right values
+*/
+static int ieee80211_verify_qos_info(struct ieee80211_qos_information_element
+ *info_element, int sub_type)
+{
+
+ if (info_element->qui_subtype != sub_type)
+ return -1;
+ if (memcmp(info_element->qui, qos_oui, QOS_OUI_LEN))
+ return -1;
+ if (info_element->qui_type != QOS_OUI_TYPE)
+ return -1;
+ if (info_element->version != QOS_VERSION_1)
+ return -1;
+
+ return 0;
+}
+
+
+/*
+ * Parse a QoS parameter element
+ */
+static int ieee80211_read_qos_param_element(struct ieee80211_qos_parameter_info
+ *element_param, struct ieee80211_info_element
+ *info_element)
+{
+ int ret = 0;
+ u16 size = sizeof(struct ieee80211_qos_parameter_info) - 2;
+
+ if ((info_element == NULL) || (element_param == NULL))
+ return -1;
+
+ if (info_element->id == QOS_ELEMENT_ID && info_element->len == size) {
+ memcpy(element_param->info_element.qui, info_element->data,
+ info_element->len);
+ element_param->info_element.elementID = info_element->id;
+ element_param->info_element.length = info_element->len;
+ } else
+ ret = -1;
+ if (ret == 0)
+ ret = ieee80211_verify_qos_info(&element_param->info_element,
+ QOS_OUI_PARAM_SUB_TYPE);
+ return ret;
+}
+
+/*
+ * Parse a QoS information element
+ */
+static int ieee80211_read_qos_info_element(struct
+ ieee80211_qos_information_element
+ *element_info, struct ieee80211_info_element
+ *info_element)
+{
+ int ret = 0;
+ u16 size = sizeof(struct ieee80211_qos_information_element) - 2;
+
+ if (element_info == NULL)
+ return -1;
+ if (info_element == NULL)
+ return -1;
+
+ if ((info_element->id == QOS_ELEMENT_ID) && (info_element->len == size)) {
+ memcpy(element_info->qui, info_element->data,
+ info_element->len);
+ element_info->elementID = info_element->id;
+ element_info->length = info_element->len;
+ } else
+ ret = -1;
+
+ if (ret == 0)
+ ret = ieee80211_verify_qos_info(element_info,
+ QOS_OUI_INFO_SUB_TYPE);
+ return ret;
+}
+
+
+/*
+ * Write QoS parameters from the ac parameters.
+ */
+static int ieee80211_qos_convert_ac_to_parameters(struct
+ ieee80211_qos_parameter_info
+ *param_elm, struct
+ ieee80211_qos_parameters
+ *qos_param)
+{
+ int i;
+ struct ieee80211_qos_ac_parameter *ac_params;
+ u8 aci;
+ //u8 cw_min;
+ //u8 cw_max;
+
+ for (i = 0; i < QOS_QUEUE_NUM; i++) {
+ ac_params = &(param_elm->ac_params_record[i]);
+
+ aci = (ac_params->aci_aifsn & 0x60) >> 5;
+
+ if(aci >= QOS_QUEUE_NUM)
+ continue;
+ qos_param->aifs[aci] = (ac_params->aci_aifsn) & 0x0f;
+
+ /* WMM spec P.11: The minimum value for AIFSN shall be 2 */
+ qos_param->aifs[aci] = (qos_param->aifs[aci] < 2) ? 2:qos_param->aifs[aci];
+
+ qos_param->cw_min[aci] = ac_params->ecw_min_max & 0x0F;
+
+ qos_param->cw_max[aci] = (ac_params->ecw_min_max & 0xF0) >> 4;
+
+ qos_param->flag[aci] =
+ (ac_params->aci_aifsn & 0x10) ? 0x01 : 0x00;
+ qos_param->tx_op_limit[aci] = le16_to_cpu(ac_params->tx_op_limit);
+ }
+ return 0;
+}
+
+/*
+ * we have a generic data element which it may contain QoS information or
+ * parameters element. check the information element length to decide
+ * which type to read
+ */
+static int ieee80211_parse_qos_info_param_IE(struct ieee80211_info_element
+ *info_element,
+ struct ieee80211_network *network)
+{
+ int rc = 0;
+ struct ieee80211_qos_parameters *qos_param = NULL;
+ struct ieee80211_qos_information_element qos_info_element;
+
+ rc = ieee80211_read_qos_info_element(&qos_info_element, info_element);
+
+ if (rc == 0) {
+ network->qos_data.param_count = qos_info_element.ac_info & 0x0F;
+ network->flags |= NETWORK_HAS_QOS_INFORMATION;
+ } else {
+ struct ieee80211_qos_parameter_info param_element;
+
+ rc = ieee80211_read_qos_param_element(&param_element,
+ info_element);
+ if (rc == 0) {
+ qos_param = &(network->qos_data.parameters);
+ ieee80211_qos_convert_ac_to_parameters(&param_element,
+ qos_param);
+ network->flags |= NETWORK_HAS_QOS_PARAMETERS;
+ network->qos_data.param_count =
+ param_element.info_element.ac_info & 0x0F;
+ }
+ }
+
+ if (rc == 0) {
+ IEEE80211_DEBUG_QOS("QoS is supported\n");
+ network->qos_data.supported = 1;
+ }
+ return rc;
+}
+
+#ifdef CONFIG_IEEE80211_DEBUG
+#define MFIE_STRING(x) case MFIE_TYPE_ ##x: return #x
+
+static const char *get_info_element_string(u16 id)
+{
+ switch (id) {
+ MFIE_STRING(SSID);
+ MFIE_STRING(RATES);
+ MFIE_STRING(FH_SET);
+ MFIE_STRING(DS_SET);
+ MFIE_STRING(CF_SET);
+ MFIE_STRING(TIM);
+ MFIE_STRING(IBSS_SET);
+ MFIE_STRING(COUNTRY);
+ MFIE_STRING(HOP_PARAMS);
+ MFIE_STRING(HOP_TABLE);
+ MFIE_STRING(REQUEST);
+ MFIE_STRING(CHALLENGE);
+ MFIE_STRING(POWER_CONSTRAINT);
+ MFIE_STRING(POWER_CAPABILITY);
+ MFIE_STRING(TPC_REQUEST);
+ MFIE_STRING(TPC_REPORT);
+ MFIE_STRING(SUPP_CHANNELS);
+ MFIE_STRING(CSA);
+ MFIE_STRING(MEASURE_REQUEST);
+ MFIE_STRING(MEASURE_REPORT);
+ MFIE_STRING(QUIET);
+ MFIE_STRING(IBSS_DFS);
+ // MFIE_STRING(ERP_INFO);
+ MFIE_STRING(RSN);
+ MFIE_STRING(RATES_EX);
+ MFIE_STRING(GENERIC);
+ MFIE_STRING(QOS_PARAMETER);
+ default:
+ return "UNKNOWN";
+ }
+}
+#endif
+
+static inline void ieee80211_extract_country_ie(
+ struct ieee80211_device *ieee,
+ struct ieee80211_info_element *info_element,
+ struct ieee80211_network *network,
+ u8 *addr2
+)
+{
+ if (IS_DOT11D_ENABLE(ieee))
+ {
+ if (info_element->len!= 0)
+ {
+ memcpy(network->CountryIeBuf, info_element->data, info_element->len);
+ network->CountryIeLen = info_element->len;
+
+ if (!IS_COUNTRY_IE_VALID(ieee))
+ {
+ Dot11d_UpdateCountryIe(ieee, addr2, info_element->len, info_element->data);
+ }
+ }
+
+ //
+ // 070305, rcnjko: I update country IE watch dog here because
+ // some AP (e.g. Cisco 1242) don't include country IE in their
+ // probe response frame.
+ //
+ if (IS_EQUAL_CIE_SRC(ieee, addr2) )
+ {
+ UPDATE_CIE_WATCHDOG(ieee);
+ }
+ }
+
+}
+
+int ieee80211_parse_info_param(struct ieee80211_device *ieee,
+ struct ieee80211_info_element *info_element,
+ u16 length,
+ struct ieee80211_network *network,
+ struct ieee80211_rx_stats *stats)
+{
+ u8 i;
+ short offset;
+ u16 tmp_htcap_len=0;
+ u16 tmp_htinfo_len=0;
+ u16 ht_realtek_agg_len=0;
+ u8 ht_realtek_agg_buf[MAX_IE_LEN];
+// u16 broadcom_len = 0;
+#ifdef CONFIG_IEEE80211_DEBUG
+ char rates_str[64];
+ char *p;
+#endif
+
+ while (length >= sizeof(*info_element)) {
+ if (sizeof(*info_element) + info_element->len > length) {
+ IEEE80211_DEBUG_MGMT("Info elem: parse failed: "
+ "info_element->len + 2 > left : "
+ "info_element->len+2=%zd left=%d, id=%d.\n",
+ info_element->len +
+ sizeof(*info_element),
+ length, info_element->id);
+ /* We stop processing but don't return an error here
+ * because some misbehaviour APs break this rule. ie.
+ * Orinoco AP1000. */
+ break;
+ }
+
+ switch (info_element->id) {
+ case MFIE_TYPE_SSID:
+ if (ieee80211_is_empty_essid(info_element->data,
+ info_element->len)) {
+ network->flags |= NETWORK_EMPTY_ESSID;
+ break;
+ }
+
+ network->ssid_len = min(info_element->len,
+ (u8) IW_ESSID_MAX_SIZE);
+ memcpy(network->ssid, info_element->data, network->ssid_len);
+ if (network->ssid_len < IW_ESSID_MAX_SIZE)
+ memset(network->ssid + network->ssid_len, 0,
+ IW_ESSID_MAX_SIZE - network->ssid_len);
+
+ IEEE80211_DEBUG_MGMT("MFIE_TYPE_SSID: '%s' len=%d.\n",
+ network->ssid, network->ssid_len);
+ break;
+
+ case MFIE_TYPE_RATES:
+#ifdef CONFIG_IEEE80211_DEBUG
+ p = rates_str;
+#endif
+ network->rates_len = min(info_element->len,
+ MAX_RATES_LENGTH);
+ for (i = 0; i < network->rates_len; i++) {
+ network->rates[i] = info_element->data[i];
+#ifdef CONFIG_IEEE80211_DEBUG
+ p += snprintf(p, sizeof(rates_str) -
+ (p - rates_str), "%02X ",
+ network->rates[i]);
+#endif
+ if (ieee80211_is_ofdm_rate
+ (info_element->data[i])) {
+ network->flags |= NETWORK_HAS_OFDM;
+ if (info_element->data[i] &
+ IEEE80211_BASIC_RATE_MASK)
+ network->flags &=
+ ~NETWORK_HAS_CCK;
+ }
+ }
+
+ IEEE80211_DEBUG_MGMT("MFIE_TYPE_RATES: '%s' (%d)\n",
+ rates_str, network->rates_len);
+ break;
+
+ case MFIE_TYPE_RATES_EX:
+#ifdef CONFIG_IEEE80211_DEBUG
+ p = rates_str;
+#endif
+ network->rates_ex_len = min(info_element->len,
+ MAX_RATES_EX_LENGTH);
+ for (i = 0; i < network->rates_ex_len; i++) {
+ network->rates_ex[i] = info_element->data[i];
+#ifdef CONFIG_IEEE80211_DEBUG
+ p += snprintf(p, sizeof(rates_str) -
+ (p - rates_str), "%02X ",
+ network->rates_ex[i]);
+#endif
+ if (ieee80211_is_ofdm_rate
+ (info_element->data[i])) {
+ network->flags |= NETWORK_HAS_OFDM;
+ if (info_element->data[i] &
+ IEEE80211_BASIC_RATE_MASK)
+ network->flags &=
+ ~NETWORK_HAS_CCK;
+ }
+ }
+
+ IEEE80211_DEBUG_MGMT("MFIE_TYPE_RATES_EX: '%s' (%d)\n",
+ rates_str, network->rates_ex_len);
+ break;
+
+ case MFIE_TYPE_DS_SET:
+ IEEE80211_DEBUG_MGMT("MFIE_TYPE_DS_SET: %d\n",
+ info_element->data[0]);
+ network->channel = info_element->data[0];
+ break;
+
+ case MFIE_TYPE_FH_SET:
+ IEEE80211_DEBUG_MGMT("MFIE_TYPE_FH_SET: ignored\n");
+ break;
+
+ case MFIE_TYPE_CF_SET:
+ IEEE80211_DEBUG_MGMT("MFIE_TYPE_CF_SET: ignored\n");
+ break;
+
+ case MFIE_TYPE_TIM:
+ if(info_element->len < 4)
+ break;
+
+ network->tim.tim_count = info_element->data[0];
+ network->tim.tim_period = info_element->data[1];
+
+ network->dtim_period = info_element->data[1];
+ if(ieee->state != IEEE80211_LINKED)
+ break;
+
+ network->last_dtim_sta_time[0] = stats->mac_time[0];
+ network->last_dtim_sta_time[1] = stats->mac_time[1];
+
+ network->dtim_data = IEEE80211_DTIM_VALID;
+
+ if(info_element->data[0] != 0)
+ break;
+
+ if(info_element->data[2] & 1)
+ network->dtim_data |= IEEE80211_DTIM_MBCAST;
+
+ offset = (info_element->data[2] >> 1)*2;
+
+ //printk("offset1:%x aid:%x\n",offset, ieee->assoc_id);
+
+ if(ieee->assoc_id < 8*offset ||
+ ieee->assoc_id > 8*(offset + info_element->len -3))
+
+ break;
+
+ offset = (ieee->assoc_id / 8) - offset;// + ((aid % 8)? 0 : 1) ;
+
+ if(info_element->data[3+offset] & (1<<(ieee->assoc_id%8)))
+ network->dtim_data |= IEEE80211_DTIM_UCAST;
+
+ //IEEE80211_DEBUG_MGMT("MFIE_TYPE_TIM: partially ignored\n");
+ break;
+
+ case MFIE_TYPE_ERP:
+ network->erp_value = info_element->data[0];
+ network->flags |= NETWORK_HAS_ERP_VALUE;
+ IEEE80211_DEBUG_MGMT("MFIE_TYPE_ERP_SET: %d\n",
+ network->erp_value);
+ break;
+ case MFIE_TYPE_IBSS_SET:
+ network->atim_window = info_element->data[0];
+ IEEE80211_DEBUG_MGMT("MFIE_TYPE_IBSS_SET: %d\n",
+ network->atim_window);
+ break;
+
+ case MFIE_TYPE_CHALLENGE:
+ IEEE80211_DEBUG_MGMT("MFIE_TYPE_CHALLENGE: ignored\n");
+ break;
+
+ case MFIE_TYPE_GENERIC:
+ IEEE80211_DEBUG_MGMT("MFIE_TYPE_GENERIC: %d bytes\n",
+ info_element->len);
+ if (!ieee80211_parse_qos_info_param_IE(info_element,
+ network))
+ break;
+
+ if (info_element->len >= 4 &&
+ info_element->data[0] == 0x00 &&
+ info_element->data[1] == 0x50 &&
+ info_element->data[2] == 0xf2 &&
+ info_element->data[3] == 0x01) {
+ network->wpa_ie_len = min(info_element->len + 2,
+ MAX_WPA_IE_LEN);
+ memcpy(network->wpa_ie, info_element,
+ network->wpa_ie_len);
+ break;
+ }
+
+#ifdef THOMAS_TURBO
+ if (info_element->len == 7 &&
+ info_element->data[0] == 0x00 &&
+ info_element->data[1] == 0xe0 &&
+ info_element->data[2] == 0x4c &&
+ info_element->data[3] == 0x01 &&
+ info_element->data[4] == 0x02) {
+ network->Turbo_Enable = 1;
+ }
+#endif
+
+ //for HTcap and HTinfo parameters
+ if(tmp_htcap_len == 0){
+ if(info_element->len >= 4 &&
+ info_element->data[0] == 0x00 &&
+ info_element->data[1] == 0x90 &&
+ info_element->data[2] == 0x4c &&
+ info_element->data[3] == 0x033){
+
+ tmp_htcap_len = min(info_element->len,(u8)MAX_IE_LEN);
+ if(tmp_htcap_len != 0){
+ network->bssht.bdHTSpecVer = HT_SPEC_VER_EWC;
+ network->bssht.bdHTCapLen = tmp_htcap_len > sizeof(network->bssht.bdHTCapBuf)?\
+ sizeof(network->bssht.bdHTCapBuf):tmp_htcap_len;
+ memcpy(network->bssht.bdHTCapBuf,info_element->data,network->bssht.bdHTCapLen);
+ }
+ }
+ if(tmp_htcap_len != 0)
+ network->bssht.bdSupportHT = true;
+ else
+ network->bssht.bdSupportHT = false;
+ }
+
+
+ if(tmp_htinfo_len == 0){
+ if(info_element->len >= 4 &&
+ info_element->data[0] == 0x00 &&
+ info_element->data[1] == 0x90 &&
+ info_element->data[2] == 0x4c &&
+ info_element->data[3] == 0x034){
+
+ tmp_htinfo_len = min(info_element->len,(u8)MAX_IE_LEN);
+ if(tmp_htinfo_len != 0){
+ network->bssht.bdHTSpecVer = HT_SPEC_VER_EWC;
+ if(tmp_htinfo_len){
+ network->bssht.bdHTInfoLen = tmp_htinfo_len > sizeof(network->bssht.bdHTInfoBuf)?\
+ sizeof(network->bssht.bdHTInfoBuf):tmp_htinfo_len;
+ memcpy(network->bssht.bdHTInfoBuf,info_element->data,network->bssht.bdHTInfoLen);
+ }
+
+ }
+
+ }
+ }
+
+ if(ieee->aggregation){
+ if(network->bssht.bdSupportHT){
+ if(info_element->len >= 4 &&
+ info_element->data[0] == 0x00 &&
+ info_element->data[1] == 0xe0 &&
+ info_element->data[2] == 0x4c &&
+ info_element->data[3] == 0x02){
+
+ ht_realtek_agg_len = min(info_element->len,(u8)MAX_IE_LEN);
+ memcpy(ht_realtek_agg_buf,info_element->data,info_element->len);
+
+ }
+ if(ht_realtek_agg_len >= 5){
+ network->bssht.bdRT2RTAggregation = true;
+
+ if((ht_realtek_agg_buf[4] == 1) && (ht_realtek_agg_buf[5] & 0x02))
+ network->bssht.bdRT2RTLongSlotTime = true;
+ }
+ }
+
+ }
+
+ //if(tmp_htcap_len !=0 || tmp_htinfo_len != 0)
+ {
+ if ((info_element->len >= 3 &&
+ info_element->data[0] == 0x00 &&
+ info_element->data[1] == 0x05 &&
+ info_element->data[2] == 0xb5) ||
+ (info_element->len >= 3 &&
+ info_element->data[0] == 0x00 &&
+ info_element->data[1] == 0x0a &&
+ info_element->data[2] == 0xf7) ||
+ (info_element->len >= 3 &&
+ info_element->data[0] == 0x00 &&
+ info_element->data[1] == 0x10 &&
+ info_element->data[2] == 0x18)){
+
+ network->broadcom_cap_exist = true;
+
+ }
+ }
+ if(info_element->len >= 3 &&
+ info_element->data[0] == 0x00 &&
+ info_element->data[1] == 0x0c &&
+ info_element->data[2] == 0x43)
+ {
+ network->ralink_cap_exist = true;
+ }
+ else
+ network->ralink_cap_exist = false;
+ //added by amy for atheros AP
+ if((info_element->len >= 3 &&
+ info_element->data[0] == 0x00 &&
+ info_element->data[1] == 0x03 &&
+ info_element->data[2] == 0x7f) ||
+ (info_element->len >= 3 &&
+ info_element->data[0] == 0x00 &&
+ info_element->data[1] == 0x13 &&
+ info_element->data[2] == 0x74))
+ {
+ printk("========>%s(): athros AP is exist\n",__func__);
+ network->atheros_cap_exist = true;
+ }
+ else
+ network->atheros_cap_exist = false;
+
+ if(info_element->len >= 3 &&
+ info_element->data[0] == 0x00 &&
+ info_element->data[1] == 0x40 &&
+ info_element->data[2] == 0x96)
+ {
+ network->cisco_cap_exist = true;
+ }
+ else
+ network->cisco_cap_exist = false;
+ //added by amy for LEAP of cisco
+ if (info_element->len > 4 &&
+ info_element->data[0] == 0x00 &&
+ info_element->data[1] == 0x40 &&
+ info_element->data[2] == 0x96 &&
+ info_element->data[3] == 0x01)
+ {
+ if(info_element->len == 6)
+ {
+ memcpy(network->CcxRmState, &info_element[4], 2);
+ if(network->CcxRmState[0] != 0)
+ {
+ network->bCcxRmEnable = true;
+ }
+ else
+ network->bCcxRmEnable = false;
+ //
+ // CCXv4 Table 59-1 MBSSID Masks.
+ //
+ network->MBssidMask = network->CcxRmState[1] & 0x07;
+ if(network->MBssidMask != 0)
+ {
+ network->bMBssidValid = true;
+ network->MBssidMask = 0xff << (network->MBssidMask);
+ cpMacAddr(network->MBssid, network->bssid);
+ network->MBssid[5] &= network->MBssidMask;
+ }
+ else
+ {
+ network->bMBssidValid = false;
+ }
+ }
+ else
+ {
+ network->bCcxRmEnable = false;
+ }
+ }
+ if (info_element->len > 4 &&
+ info_element->data[0] == 0x00 &&
+ info_element->data[1] == 0x40 &&
+ info_element->data[2] == 0x96 &&
+ info_element->data[3] == 0x03)
+ {
+ if(info_element->len == 5)
+ {
+ network->bWithCcxVerNum = true;
+ network->BssCcxVerNumber = info_element->data[4];
+ }
+ else
+ {
+ network->bWithCcxVerNum = false;
+ network->BssCcxVerNumber = 0;
+ }
+ }
+ break;
+
+ case MFIE_TYPE_RSN:
+ IEEE80211_DEBUG_MGMT("MFIE_TYPE_RSN: %d bytes\n",
+ info_element->len);
+ network->rsn_ie_len = min(info_element->len + 2,
+ MAX_WPA_IE_LEN);
+ memcpy(network->rsn_ie, info_element,
+ network->rsn_ie_len);
+ break;
+
+ //HT related element.
+ case MFIE_TYPE_HT_CAP:
+ IEEE80211_DEBUG_SCAN("MFIE_TYPE_HT_CAP: %d bytes\n",
+ info_element->len);
+ tmp_htcap_len = min(info_element->len,(u8)MAX_IE_LEN);
+ if(tmp_htcap_len != 0){
+ network->bssht.bdHTSpecVer = HT_SPEC_VER_EWC;
+ network->bssht.bdHTCapLen = tmp_htcap_len > sizeof(network->bssht.bdHTCapBuf)?\
+ sizeof(network->bssht.bdHTCapBuf):tmp_htcap_len;
+ memcpy(network->bssht.bdHTCapBuf,info_element->data,network->bssht.bdHTCapLen);
+
+ //If peer is HT, but not WMM, call QosSetLegacyWMMParamWithHT()
+ // windows driver will update WMM parameters each beacon received once connected
+ // Linux driver is a bit different.
+ network->bssht.bdSupportHT = true;
+ }
+ else
+ network->bssht.bdSupportHT = false;
+ break;
+
+
+ case MFIE_TYPE_HT_INFO:
+ IEEE80211_DEBUG_SCAN("MFIE_TYPE_HT_INFO: %d bytes\n",
+ info_element->len);
+ tmp_htinfo_len = min(info_element->len,(u8)MAX_IE_LEN);
+ if(tmp_htinfo_len){
+ network->bssht.bdHTSpecVer = HT_SPEC_VER_IEEE;
+ network->bssht.bdHTInfoLen = tmp_htinfo_len > sizeof(network->bssht.bdHTInfoBuf)?\
+ sizeof(network->bssht.bdHTInfoBuf):tmp_htinfo_len;
+ memcpy(network->bssht.bdHTInfoBuf,info_element->data,network->bssht.bdHTInfoLen);
+ }
+ break;
+
+ case MFIE_TYPE_AIRONET:
+ IEEE80211_DEBUG_SCAN("MFIE_TYPE_AIRONET: %d bytes\n",
+ info_element->len);
+ if(info_element->len >IE_CISCO_FLAG_POSITION)
+ {
+ network->bWithAironetIE = true;
+
+ // CCX 1 spec v1.13, A01.1 CKIP Negotiation (page23):
+ // "A Cisco access point advertises support for CKIP in beacon and probe response packets,
+ // by adding an Aironet element and setting one or both of the CKIP negotiation bits."
+ if( (info_element->data[IE_CISCO_FLAG_POSITION]&SUPPORT_CKIP_MIC) ||
+ (info_element->data[IE_CISCO_FLAG_POSITION]&SUPPORT_CKIP_PK) )
+ {
+ network->bCkipSupported = true;
+ }
+ else
+ {
+ network->bCkipSupported = false;
+ }
+ }
+ else
+ {
+ network->bWithAironetIE = false;
+ network->bCkipSupported = false;
+ }
+ break;
+ case MFIE_TYPE_QOS_PARAMETER:
+ printk(KERN_ERR
+ "QoS Error need to parse QOS_PARAMETER IE\n");
+ break;
+
+ case MFIE_TYPE_COUNTRY:
+ IEEE80211_DEBUG_SCAN("MFIE_TYPE_COUNTRY: %d bytes\n",
+ info_element->len);
+ //printk("=====>Receive <%s> Country IE\n",network->ssid);
+ ieee80211_extract_country_ie(ieee, info_element, network, network->bssid);//addr2 is same as addr3 when from an AP
+ break;
+/* TODO */
+ default:
+ IEEE80211_DEBUG_MGMT
+ ("Unsupported info element: %s (%d)\n",
+ get_info_element_string(info_element->id),
+ info_element->id);
+ break;
+ }
+
+ length -= sizeof(*info_element) + info_element->len;
+ info_element =
+ (struct ieee80211_info_element *)&info_element->
+ data[info_element->len];
+ }
+
+ if(!network->atheros_cap_exist && !network->broadcom_cap_exist &&
+ !network->cisco_cap_exist && !network->ralink_cap_exist && !network->bssht.bdRT2RTAggregation)
+ {
+ network->unknown_cap_exist = true;
+ }
+ else
+ {
+ network->unknown_cap_exist = false;
+ }
+ return 0;
+}
+
+static inline u8 ieee80211_SignalStrengthTranslate(
+ u8 CurrSS
+ )
+{
+ u8 RetSS;
+
+ // Step 1. Scale mapping.
+ if(CurrSS >= 71 && CurrSS <= 100)
+ {
+ RetSS = 90 + ((CurrSS - 70) / 3);
+ }
+ else if(CurrSS >= 41 && CurrSS <= 70)
+ {
+ RetSS = 78 + ((CurrSS - 40) / 3);
+ }
+ else if(CurrSS >= 31 && CurrSS <= 40)
+ {
+ RetSS = 66 + (CurrSS - 30);
+ }
+ else if(CurrSS >= 21 && CurrSS <= 30)
+ {
+ RetSS = 54 + (CurrSS - 20);
+ }
+ else if(CurrSS >= 5 && CurrSS <= 20)
+ {
+ RetSS = 42 + (((CurrSS - 5) * 2) / 3);
+ }
+ else if(CurrSS == 4)
+ {
+ RetSS = 36;
+ }
+ else if(CurrSS == 3)
+ {
+ RetSS = 27;
+ }
+ else if(CurrSS == 2)
+ {
+ RetSS = 18;
+ }
+ else if(CurrSS == 1)
+ {
+ RetSS = 9;
+ }
+ else
+ {
+ RetSS = CurrSS;
+ }
+ //RT_TRACE(COMP_DBG, DBG_LOUD, ("##### After Mapping: LastSS: %d, CurrSS: %d, RetSS: %d\n", LastSS, CurrSS, RetSS));
+
+ // Step 2. Smoothing.
+
+ //RT_TRACE(COMP_DBG, DBG_LOUD, ("$$$$$ After Smoothing: LastSS: %d, CurrSS: %d, RetSS: %d\n", LastSS, CurrSS, RetSS));
+
+ return RetSS;
+}
+
+/* 0-100 index */
+static long ieee80211_translate_todbm(u8 signal_strength_index)
+{
+ long signal_power; // in dBm.
+
+ // Translate to dBm (x=0.5y-95).
+ signal_power = (long)((signal_strength_index + 1) >> 1);
+ signal_power -= 95;
+
+ return signal_power;
+}
+
+static inline int ieee80211_network_init(
+ struct ieee80211_device *ieee,
+ struct ieee80211_probe_response *beacon,
+ struct ieee80211_network *network,
+ struct ieee80211_rx_stats *stats)
+{
+#ifdef CONFIG_IEEE80211_DEBUG
+ //char rates_str[64];
+ //char *p;
+#endif
+
+ network->qos_data.active = 0;
+ network->qos_data.supported = 0;
+ network->qos_data.param_count = 0;
+ network->qos_data.old_param_count = 0;
+
+ /* Pull out fixed field data */
+ memcpy(network->bssid, beacon->header.addr3, ETH_ALEN);
+ network->capability = le16_to_cpu(beacon->capability);
+ network->last_scanned = jiffies;
+ network->time_stamp[0] = le32_to_cpu(beacon->time_stamp[0]);
+ network->time_stamp[1] = le32_to_cpu(beacon->time_stamp[1]);
+ network->beacon_interval = le16_to_cpu(beacon->beacon_interval);
+ /* Where to pull this? beacon->listen_interval;*/
+ network->listen_interval = 0x0A;
+ network->rates_len = network->rates_ex_len = 0;
+ network->last_associate = 0;
+ network->ssid_len = 0;
+ network->flags = 0;
+ network->atim_window = 0;
+ network->erp_value = (network->capability & WLAN_CAPABILITY_IBSS) ?
+ 0x3 : 0x0;
+ network->berp_info_valid = false;
+ network->broadcom_cap_exist = false;
+ network->ralink_cap_exist = false;
+ network->atheros_cap_exist = false;
+ network->cisco_cap_exist = false;
+ network->unknown_cap_exist = false;
+#ifdef THOMAS_TURBO
+ network->Turbo_Enable = 0;
+#endif
+ network->CountryIeLen = 0;
+ memset(network->CountryIeBuf, 0, MAX_IE_LEN);
+//Initialize HT parameters
+ //ieee80211_ht_initialize(&network->bssht);
+ HTInitializeBssDesc(&network->bssht);
+ if (stats->freq == IEEE80211_52GHZ_BAND) {
+ /* for A band (No DS info) */
+ network->channel = stats->received_channel;
+ } else
+ network->flags |= NETWORK_HAS_CCK;
+
+ network->wpa_ie_len = 0;
+ network->rsn_ie_len = 0;
+
+ if (ieee80211_parse_info_param
+ (ieee,beacon->info_element, stats->len - sizeof(*beacon), network, stats))
+ return 1;
+
+ network->mode = 0;
+ if (stats->freq == IEEE80211_52GHZ_BAND)
+ network->mode = IEEE_A;
+ else {
+ if (network->flags & NETWORK_HAS_OFDM)
+ network->mode |= IEEE_G;
+ if (network->flags & NETWORK_HAS_CCK)
+ network->mode |= IEEE_B;
+ }
+
+ if (network->mode == 0) {
+ IEEE80211_DEBUG_SCAN("Filtered out '%s (%pM)' "
+ "network.\n",
+ escape_essid(network->ssid,
+ network->ssid_len),
+ network->bssid);
+ return 1;
+ }
+
+ if(network->bssht.bdSupportHT){
+ if(network->mode == IEEE_A)
+ network->mode = IEEE_N_5G;
+ else if(network->mode & (IEEE_G | IEEE_B))
+ network->mode = IEEE_N_24G;
+ }
+ if (ieee80211_is_empty_essid(network->ssid, network->ssid_len))
+ network->flags |= NETWORK_EMPTY_ESSID;
+
+ stats->signal = 30 + (stats->SignalStrength * 70) / 100;
+ //stats->signal = ieee80211_SignalStrengthTranslate(stats->signal);
+ stats->noise = ieee80211_translate_todbm((u8)(100-stats->signal)) -25;
+
+ memcpy(&network->stats, stats, sizeof(network->stats));
+
+ return 0;
+}
+
+static inline int is_same_network(struct ieee80211_network *src,
+ struct ieee80211_network *dst, struct ieee80211_device *ieee)
+{
+ /* A network is only a duplicate if the channel, BSSID, ESSID
+ * and the capability field (in particular IBSS and BSS) all match.
+ * We treat all <hidden> with the same BSSID and channel
+ * as one network */
+ return //((src->ssid_len == dst->ssid_len) &&
+ (((src->ssid_len == dst->ssid_len) || (ieee->iw_mode == IW_MODE_INFRA)) &&
+ (src->channel == dst->channel) &&
+ !memcmp(src->bssid, dst->bssid, ETH_ALEN) &&
+ //!memcmp(src->ssid, dst->ssid, src->ssid_len) &&
+ (!memcmp(src->ssid, dst->ssid, src->ssid_len) || (ieee->iw_mode == IW_MODE_INFRA)) &&
+ ((src->capability & WLAN_CAPABILITY_IBSS) ==
+ (dst->capability & WLAN_CAPABILITY_IBSS)) &&
+ ((src->capability & WLAN_CAPABILITY_BSS) ==
+ (dst->capability & WLAN_CAPABILITY_BSS)));
+}
+
+static inline void update_network(struct ieee80211_network *dst,
+ struct ieee80211_network *src)
+{
+ int qos_active;
+ u8 old_param;
+
+ memcpy(&dst->stats, &src->stats, sizeof(struct ieee80211_rx_stats));
+ dst->capability = src->capability;
+ memcpy(dst->rates, src->rates, src->rates_len);
+ dst->rates_len = src->rates_len;
+ memcpy(dst->rates_ex, src->rates_ex, src->rates_ex_len);
+ dst->rates_ex_len = src->rates_ex_len;
+ if (src->ssid_len > 0)
+ {
+ memset(dst->ssid, 0, dst->ssid_len);
+ dst->ssid_len = src->ssid_len;
+ memcpy(dst->ssid, src->ssid, src->ssid_len);
+ }
+ dst->mode = src->mode;
+ dst->flags = src->flags;
+ dst->time_stamp[0] = src->time_stamp[0];
+ dst->time_stamp[1] = src->time_stamp[1];
+ if (src->flags & NETWORK_HAS_ERP_VALUE)
+ {
+ dst->erp_value = src->erp_value;
+ dst->berp_info_valid = src->berp_info_valid = true;
+ }
+ dst->beacon_interval = src->beacon_interval;
+ dst->listen_interval = src->listen_interval;
+ dst->atim_window = src->atim_window;
+ dst->dtim_period = src->dtim_period;
+ dst->dtim_data = src->dtim_data;
+ dst->last_dtim_sta_time[0] = src->last_dtim_sta_time[0];
+ dst->last_dtim_sta_time[1] = src->last_dtim_sta_time[1];
+ memcpy(&dst->tim, &src->tim, sizeof(struct ieee80211_tim_parameters));
+
+ dst->bssht.bdSupportHT = src->bssht.bdSupportHT;
+ dst->bssht.bdRT2RTAggregation = src->bssht.bdRT2RTAggregation;
+ dst->bssht.bdHTCapLen= src->bssht.bdHTCapLen;
+ memcpy(dst->bssht.bdHTCapBuf,src->bssht.bdHTCapBuf,src->bssht.bdHTCapLen);
+ dst->bssht.bdHTInfoLen= src->bssht.bdHTInfoLen;
+ memcpy(dst->bssht.bdHTInfoBuf,src->bssht.bdHTInfoBuf,src->bssht.bdHTInfoLen);
+ dst->bssht.bdHTSpecVer = src->bssht.bdHTSpecVer;
+ dst->bssht.bdRT2RTLongSlotTime = src->bssht.bdRT2RTLongSlotTime;
+ dst->broadcom_cap_exist = src->broadcom_cap_exist;
+ dst->ralink_cap_exist = src->ralink_cap_exist;
+ dst->atheros_cap_exist = src->atheros_cap_exist;
+ dst->cisco_cap_exist = src->cisco_cap_exist;
+ dst->unknown_cap_exist = src->unknown_cap_exist;
+ memcpy(dst->wpa_ie, src->wpa_ie, src->wpa_ie_len);
+ dst->wpa_ie_len = src->wpa_ie_len;
+ memcpy(dst->rsn_ie, src->rsn_ie, src->rsn_ie_len);
+ dst->rsn_ie_len = src->rsn_ie_len;
+
+ dst->last_scanned = jiffies;
+ /* qos related parameters */
+ //qos_active = src->qos_data.active;
+ qos_active = dst->qos_data.active;
+ //old_param = dst->qos_data.old_param_count;
+ old_param = dst->qos_data.param_count;
+ if(dst->flags & NETWORK_HAS_QOS_MASK)
+ memcpy(&dst->qos_data, &src->qos_data,
+ sizeof(struct ieee80211_qos_data));
+ else {
+ dst->qos_data.supported = src->qos_data.supported;
+ dst->qos_data.param_count = src->qos_data.param_count;
+ }
+
+ if (dst->qos_data.supported == 1) {
+ dst->QoS_Enable = 1;
+ if(dst->ssid_len)
+ IEEE80211_DEBUG_QOS
+ ("QoS the network %s is QoS supported\n",
+ dst->ssid);
+ else
+ IEEE80211_DEBUG_QOS
+ ("QoS the network is QoS supported\n");
+ }
+ dst->qos_data.active = qos_active;
+ dst->qos_data.old_param_count = old_param;
+
+ /* dst->last_associate is not overwritten */
+ dst->wmm_info = src->wmm_info; //sure to exist in beacon or probe response frame.
+ if (src->wmm_param[0].ac_aci_acm_aifsn|| \
+ src->wmm_param[1].ac_aci_acm_aifsn|| \
+ src->wmm_param[2].ac_aci_acm_aifsn|| \
+ src->wmm_param[3].ac_aci_acm_aifsn) {
+ memcpy(dst->wmm_param, src->wmm_param, WME_AC_PRAM_LEN);
+ }
+ //dst->QoS_Enable = src->QoS_Enable;
+#ifdef THOMAS_TURBO
+ dst->Turbo_Enable = src->Turbo_Enable;
+#endif
+
+ dst->CountryIeLen = src->CountryIeLen;
+ memcpy(dst->CountryIeBuf, src->CountryIeBuf, src->CountryIeLen);
+
+ //added by amy for LEAP
+ dst->bWithAironetIE = src->bWithAironetIE;
+ dst->bCkipSupported = src->bCkipSupported;
+ memcpy(dst->CcxRmState, src->CcxRmState, 2);
+ dst->bCcxRmEnable = src->bCcxRmEnable;
+ dst->MBssidMask = src->MBssidMask;
+ dst->bMBssidValid = src->bMBssidValid;
+ memcpy(dst->MBssid, src->MBssid, 6);
+ dst->bWithCcxVerNum = src->bWithCcxVerNum;
+ dst->BssCcxVerNumber = src->BssCcxVerNumber;
+
+}
+
+static inline int is_beacon(__le16 fc)
+{
+ return (WLAN_FC_GET_STYPE(le16_to_cpu(fc)) == IEEE80211_STYPE_BEACON);
+}
+
+static inline void ieee80211_process_probe_response(
+ struct ieee80211_device *ieee,
+ struct ieee80211_probe_response *beacon,
+ struct ieee80211_rx_stats *stats)
+{
+ struct ieee80211_network network;
+ struct ieee80211_network *target;
+ struct ieee80211_network *oldest = NULL;
+#ifdef CONFIG_IEEE80211_DEBUG
+ struct ieee80211_info_element *info_element = &beacon->info_element[0];
+#endif
+ unsigned long flags;
+ short renew;
+ //u8 wmm_info;
+
+ memset(&network, 0, sizeof(struct ieee80211_network));
+ IEEE80211_DEBUG_SCAN(
+ "'%s' (%pM): %c%c%c%c %c%c%c%c-%c%c%c%c %c%c%c%c\n",
+ escape_essid(info_element->data, info_element->len),
+ beacon->header.addr3,
+ (beacon->capability & (1<<0xf)) ? '1' : '0',
+ (beacon->capability & (1<<0xe)) ? '1' : '0',
+ (beacon->capability & (1<<0xd)) ? '1' : '0',
+ (beacon->capability & (1<<0xc)) ? '1' : '0',
+ (beacon->capability & (1<<0xb)) ? '1' : '0',
+ (beacon->capability & (1<<0xa)) ? '1' : '0',
+ (beacon->capability & (1<<0x9)) ? '1' : '0',
+ (beacon->capability & (1<<0x8)) ? '1' : '0',
+ (beacon->capability & (1<<0x7)) ? '1' : '0',
+ (beacon->capability & (1<<0x6)) ? '1' : '0',
+ (beacon->capability & (1<<0x5)) ? '1' : '0',
+ (beacon->capability & (1<<0x4)) ? '1' : '0',
+ (beacon->capability & (1<<0x3)) ? '1' : '0',
+ (beacon->capability & (1<<0x2)) ? '1' : '0',
+ (beacon->capability & (1<<0x1)) ? '1' : '0',
+ (beacon->capability & (1<<0x0)) ? '1' : '0');
+
+ if (ieee80211_network_init(ieee, beacon, &network, stats)) {
+ IEEE80211_DEBUG_SCAN("Dropped '%s' (%pM) via %s.\n",
+ escape_essid(info_element->data,
+ info_element->len),
+ beacon->header.addr3,
+ WLAN_FC_GET_STYPE(beacon->header.frame_ctl) ==
+ IEEE80211_STYPE_PROBE_RESP ?
+ "PROBE RESPONSE" : "BEACON");
+ return;
+ }
+
+ // For Asus EeePc request,
+ // (1) if wireless adapter receive get any 802.11d country code in AP beacon,
+ // wireless adapter should follow the country code.
+ // (2) If there is no any country code in beacon,
+ // then wireless adapter should do active scan from ch1~11 and
+ // passive scan from ch12~14
+
+ if (!IsLegalChannel(ieee, network.channel))
+ return;
+ if (ieee->bGlobalDomain)
+ {
+ if (WLAN_FC_GET_STYPE(beacon->header.frame_ctl) == IEEE80211_STYPE_PROBE_RESP)
+ {
+ // Case 1: Country code
+ if(IS_COUNTRY_IE_VALID(ieee) )
+ {
+ if (!IsLegalChannel(ieee, network.channel)) {
+ printk("GetScanInfo(): For Country code, filter probe response at channel(%d).\n", network.channel);
+ return;
+ }
+ }
+ // Case 2: No any country code.
+ else
+ {
+ // Filter over channel ch12~14
+ if (network.channel > 11)
+ {
+ printk("GetScanInfo(): For Global Domain, filter probe response at channel(%d).\n", network.channel);
+ return;
+ }
+ }
+ }
+ else
+ {
+ // Case 1: Country code
+ if(IS_COUNTRY_IE_VALID(ieee) )
+ {
+ if (!IsLegalChannel(ieee, network.channel)) {
+ printk("GetScanInfo(): For Country code, filter beacon at channel(%d).\n",network.channel);
+ return;
+ }
+ }
+ // Case 2: No any country code.
+ else
+ {
+ // Filter over channel ch12~14
+ if (network.channel > 14)
+ {
+ printk("GetScanInfo(): For Global Domain, filter beacon at channel(%d).\n",network.channel);
+ return;
+ }
+ }
+ }
+ }
+
+ /* The network parsed correctly -- so now we scan our known networks
+ * to see if we can find it in our list.
+ *
+ * NOTE: This search is definitely not optimized. Once its doing
+ * the "right thing" we'll optimize it for efficiency if
+ * necessary */
+
+ /* Search for this entry in the list and update it if it is
+ * already there. */
+
+ spin_lock_irqsave(&ieee->lock, flags);
+
+ if (is_same_network(&ieee->current_network, &network, ieee)) {
+ update_network(&ieee->current_network, &network);
+ if ((ieee->current_network.mode == IEEE_N_24G || ieee->current_network.mode == IEEE_G)
+ && ieee->current_network.berp_info_valid){
+ if(ieee->current_network.erp_value& ERP_UseProtection)
+ ieee->current_network.buseprotection = true;
+ else
+ ieee->current_network.buseprotection = false;
+ }
+ if(is_beacon(beacon->header.frame_ctl))
+ {
+ if(ieee->state == IEEE80211_LINKED)
+ ieee->LinkDetectInfo.NumRecvBcnInPeriod++;
+ }
+ else //hidden AP
+ network.flags = (~NETWORK_EMPTY_ESSID & network.flags)|(NETWORK_EMPTY_ESSID & ieee->current_network.flags);
+ }
+
+ list_for_each_entry(target, &ieee->network_list, list) {
+ if (is_same_network(target, &network, ieee))
+ break;
+ if ((oldest == NULL) ||
+ (target->last_scanned < oldest->last_scanned))
+ oldest = target;
+ }
+
+ /* If we didn't find a match, then get a new network slot to initialize
+ * with this beacon's information */
+ if (&target->list == &ieee->network_list) {
+ if (list_empty(&ieee->network_free_list)) {
+ /* If there are no more slots, expire the oldest */
+ list_del(&oldest->list);
+ target = oldest;
+ IEEE80211_DEBUG_SCAN("Expired '%s' (%pM) from "
+ "network list.\n",
+ escape_essid(target->ssid,
+ target->ssid_len),
+ target->bssid);
+ } else {
+ /* Otherwise just pull from the free list */
+ target = list_entry(ieee->network_free_list.next,
+ struct ieee80211_network, list);
+ list_del(ieee->network_free_list.next);
+ }
+
+
+#ifdef CONFIG_IEEE80211_DEBUG
+ IEEE80211_DEBUG_SCAN("Adding '%s' (%pM) via %s.\n",
+ escape_essid(network.ssid,
+ network.ssid_len),
+ network.bssid,
+ WLAN_FC_GET_STYPE(beacon->header.frame_ctl) ==
+ IEEE80211_STYPE_PROBE_RESP ?
+ "PROBE RESPONSE" : "BEACON");
+#endif
+ memcpy(target, &network, sizeof(*target));
+ list_add_tail(&target->list, &ieee->network_list);
+ if(ieee->softmac_features & IEEE_SOFTMAC_ASSOCIATE)
+ ieee80211_softmac_new_net(ieee,&network);
+ } else {
+ IEEE80211_DEBUG_SCAN("Updating '%s' (%pM) via %s.\n",
+ escape_essid(target->ssid,
+ target->ssid_len),
+ target->bssid,
+ WLAN_FC_GET_STYPE(beacon->header.frame_ctl) ==
+ IEEE80211_STYPE_PROBE_RESP ?
+ "PROBE RESPONSE" : "BEACON");
+
+ /* we have an entry and we are going to update it. But this entry may
+ * be already expired. In this case we do the same as we found a new
+ * net and call the new_net handler
+ */
+ renew = !time_after(target->last_scanned + ieee->scan_age, jiffies);
+ //YJ,add,080819,for hidden ap
+ if(is_beacon(beacon->header.frame_ctl) == 0)
+ network.flags = (~NETWORK_EMPTY_ESSID & network.flags)|(NETWORK_EMPTY_ESSID & target->flags);
+ //if(strncmp(network.ssid, "linksys-c",9) == 0)
+ // printk("====>2 network.ssid=%s FLAG=%d target.ssid=%s FLAG=%d\n", network.ssid, network.flags, target->ssid, target->flags);
+ if(((network.flags & NETWORK_EMPTY_ESSID) == NETWORK_EMPTY_ESSID) \
+ && (((network.ssid_len > 0) && (strncmp(target->ssid, network.ssid, network.ssid_len)))\
+ ||((ieee->current_network.ssid_len == network.ssid_len)&&(strncmp(ieee->current_network.ssid, network.ssid, network.ssid_len) == 0)&&(ieee->state == IEEE80211_NOLINK))))
+ renew = 1;
+ //YJ,add,080819,for hidden ap,end
+
+ update_network(target, &network);
+ if(renew && (ieee->softmac_features & IEEE_SOFTMAC_ASSOCIATE))
+ ieee80211_softmac_new_net(ieee,&network);
+ }
+
+ spin_unlock_irqrestore(&ieee->lock, flags);
+ if (is_beacon(beacon->header.frame_ctl)&&is_same_network(&ieee->current_network, &network, ieee)&&\
+ (ieee->state == IEEE80211_LINKED)) {
+ if (ieee->handle_beacon != NULL) {
+ ieee->handle_beacon(ieee->dev,beacon,&ieee->current_network);
+ }
+ }
+}
+
+void ieee80211_rx_mgt(struct ieee80211_device *ieee,
+ struct ieee80211_hdr_4addr *header,
+ struct ieee80211_rx_stats *stats)
+{
+ switch (WLAN_FC_GET_STYPE(header->frame_ctl)) {
+
+ case IEEE80211_STYPE_BEACON:
+ IEEE80211_DEBUG_MGMT("received BEACON (%d)\n",
+ WLAN_FC_GET_STYPE(header->frame_ctl));
+ IEEE80211_DEBUG_SCAN("Beacon\n");
+ ieee80211_process_probe_response(
+ ieee, (struct ieee80211_probe_response *)header, stats);
+ break;
+
+ case IEEE80211_STYPE_PROBE_RESP:
+ IEEE80211_DEBUG_MGMT("received PROBE RESPONSE (%d)\n",
+ WLAN_FC_GET_STYPE(header->frame_ctl));
+ IEEE80211_DEBUG_SCAN("Probe response\n");
+ ieee80211_process_probe_response(
+ ieee, (struct ieee80211_probe_response *)header, stats);
+ break;
+
+ }
+}
+EXPORT_SYMBOL(ieee80211_rx_mgt);
diff --git a/drivers/staging/rtl8192u/ieee80211/ieee80211_softmac.c b/drivers/staging/rtl8192u/ieee80211/ieee80211_softmac.c
new file mode 100644
index 000000000..c2388812d
--- /dev/null
+++ b/drivers/staging/rtl8192u/ieee80211/ieee80211_softmac.c
@@ -0,0 +1,3217 @@
+/* IEEE 802.11 SoftMAC layer
+ * Copyright (c) 2005 Andrea Merello <andrea.merello@gmail.com>
+ *
+ * Mostly extracted from the rtl8180-sa2400 driver for the
+ * in-kernel generic ieee802.11 stack.
+ *
+ * Few lines might be stolen from other part of the ieee80211
+ * stack. Copyright who own it's copyright
+ *
+ * WPA code stolen from the ipw2200 driver.
+ * Copyright who own it's copyright.
+ *
+ * released under the GPL
+ */
+
+
+#include "ieee80211.h"
+
+#include <linux/random.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <asm/uaccess.h>
+#include <linux/etherdevice.h>
+
+#include "dot11d.h"
+
+short ieee80211_is_54g(const struct ieee80211_network *net)
+{
+ return (net->rates_ex_len > 0) || (net->rates_len > 4);
+}
+EXPORT_SYMBOL(ieee80211_is_54g);
+
+short ieee80211_is_shortslot(const struct ieee80211_network *net)
+{
+ return net->capability & WLAN_CAPABILITY_SHORT_SLOT;
+}
+EXPORT_SYMBOL(ieee80211_is_shortslot);
+
+/* returns the total length needed for pleacing the RATE MFIE
+ * tag and the EXTENDED RATE MFIE tag if needed.
+ * It encludes two bytes per tag for the tag itself and its len
+ */
+static unsigned int ieee80211_MFIE_rate_len(struct ieee80211_device *ieee)
+{
+ unsigned int rate_len = 0;
+
+ if (ieee->modulation & IEEE80211_CCK_MODULATION)
+ rate_len = IEEE80211_CCK_RATE_LEN + 2;
+
+ if (ieee->modulation & IEEE80211_OFDM_MODULATION)
+
+ rate_len += IEEE80211_OFDM_RATE_LEN + 2;
+
+ return rate_len;
+}
+
+/* pleace the MFIE rate, tag to the memory (double) poined.
+ * Then it updates the pointer so that
+ * it points after the new MFIE tag added.
+ */
+static void ieee80211_MFIE_Brate(struct ieee80211_device *ieee, u8 **tag_p)
+{
+ u8 *tag = *tag_p;
+
+ if (ieee->modulation & IEEE80211_CCK_MODULATION) {
+ *tag++ = MFIE_TYPE_RATES;
+ *tag++ = 4;
+ *tag++ = IEEE80211_BASIC_RATE_MASK | IEEE80211_CCK_RATE_1MB;
+ *tag++ = IEEE80211_BASIC_RATE_MASK | IEEE80211_CCK_RATE_2MB;
+ *tag++ = IEEE80211_BASIC_RATE_MASK | IEEE80211_CCK_RATE_5MB;
+ *tag++ = IEEE80211_BASIC_RATE_MASK | IEEE80211_CCK_RATE_11MB;
+ }
+
+ /* We may add an option for custom rates that specific HW might support */
+ *tag_p = tag;
+}
+
+static void ieee80211_MFIE_Grate(struct ieee80211_device *ieee, u8 **tag_p)
+{
+ u8 *tag = *tag_p;
+
+ if (ieee->modulation & IEEE80211_OFDM_MODULATION) {
+
+ *tag++ = MFIE_TYPE_RATES_EX;
+ *tag++ = 8;
+ *tag++ = IEEE80211_BASIC_RATE_MASK | IEEE80211_OFDM_RATE_6MB;
+ *tag++ = IEEE80211_BASIC_RATE_MASK | IEEE80211_OFDM_RATE_9MB;
+ *tag++ = IEEE80211_BASIC_RATE_MASK | IEEE80211_OFDM_RATE_12MB;
+ *tag++ = IEEE80211_BASIC_RATE_MASK | IEEE80211_OFDM_RATE_18MB;
+ *tag++ = IEEE80211_BASIC_RATE_MASK | IEEE80211_OFDM_RATE_24MB;
+ *tag++ = IEEE80211_BASIC_RATE_MASK | IEEE80211_OFDM_RATE_36MB;
+ *tag++ = IEEE80211_BASIC_RATE_MASK | IEEE80211_OFDM_RATE_48MB;
+ *tag++ = IEEE80211_BASIC_RATE_MASK | IEEE80211_OFDM_RATE_54MB;
+
+ }
+
+ /* We may add an option for custom rates that specific HW might support */
+ *tag_p = tag;
+}
+
+
+static void ieee80211_WMM_Info(struct ieee80211_device *ieee, u8 **tag_p)
+{
+ u8 *tag = *tag_p;
+
+ *tag++ = MFIE_TYPE_GENERIC; //0
+ *tag++ = 7;
+ *tag++ = 0x00;
+ *tag++ = 0x50;
+ *tag++ = 0xf2;
+ *tag++ = 0x02;//5
+ *tag++ = 0x00;
+ *tag++ = 0x01;
+#ifdef SUPPORT_USPD
+ if(ieee->current_network.wmm_info & 0x80) {
+ *tag++ = 0x0f|MAX_SP_Len;
+ } else {
+ *tag++ = MAX_SP_Len;
+ }
+#else
+ *tag++ = MAX_SP_Len;
+#endif
+ *tag_p = tag;
+}
+
+#ifdef THOMAS_TURBO
+static void ieee80211_TURBO_Info(struct ieee80211_device *ieee, u8 **tag_p)
+{
+ u8 *tag = *tag_p;
+
+ *tag++ = MFIE_TYPE_GENERIC; //0
+ *tag++ = 7;
+ *tag++ = 0x00;
+ *tag++ = 0xe0;
+ *tag++ = 0x4c;
+ *tag++ = 0x01;//5
+ *tag++ = 0x02;
+ *tag++ = 0x11;
+ *tag++ = 0x00;
+
+ *tag_p = tag;
+ printk(KERN_ALERT "This is enable turbo mode IE process\n");
+}
+#endif
+
+static void enqueue_mgmt(struct ieee80211_device *ieee, struct sk_buff *skb)
+{
+ int nh;
+ nh = (ieee->mgmt_queue_head +1) % MGMT_QUEUE_NUM;
+
+/*
+ * if the queue is full but we have newer frames then
+ * just overwrites the oldest.
+ *
+ * if (nh == ieee->mgmt_queue_tail)
+ * return -1;
+ */
+ ieee->mgmt_queue_head = nh;
+ ieee->mgmt_queue_ring[nh] = skb;
+
+ //return 0;
+}
+
+static struct sk_buff *dequeue_mgmt(struct ieee80211_device *ieee)
+{
+ struct sk_buff *ret;
+
+ if(ieee->mgmt_queue_tail == ieee->mgmt_queue_head)
+ return NULL;
+
+ ret = ieee->mgmt_queue_ring[ieee->mgmt_queue_tail];
+
+ ieee->mgmt_queue_tail =
+ (ieee->mgmt_queue_tail+1) % MGMT_QUEUE_NUM;
+
+ return ret;
+}
+
+static void init_mgmt_queue(struct ieee80211_device *ieee)
+{
+ ieee->mgmt_queue_tail = ieee->mgmt_queue_head = 0;
+}
+
+static u8 MgntQuery_MgntFrameTxRate(struct ieee80211_device *ieee)
+{
+ PRT_HIGH_THROUGHPUT pHTInfo = ieee->pHTInfo;
+ u8 rate;
+
+ // 2008/01/25 MH For broadcom, MGNT frame set as OFDM 6M.
+ if(pHTInfo->IOTAction & HT_IOT_ACT_MGNT_USE_CCK_6M)
+ rate = 0x0c;
+ else
+ rate = ieee->basic_rate & 0x7f;
+
+ if (rate == 0) {
+ // 2005.01.26, by rcnjko.
+ if(ieee->mode == IEEE_A||
+ ieee->mode== IEEE_N_5G||
+ (ieee->mode== IEEE_N_24G&&!pHTInfo->bCurSuppCCK))
+ rate = 0x0c;
+ else
+ rate = 0x02;
+ }
+
+ /*
+ // Data rate of ProbeReq is already decided. Annie, 2005-03-31
+ if( pMgntInfo->bScanInProgress || (pMgntInfo->bDualModeScanStep!=0) )
+ {
+ if(pMgntInfo->dot11CurrentWirelessMode==WIRELESS_MODE_A)
+ rate = 0x0c;
+ else
+ rate = 0x02;
+ }
+ */
+ return rate;
+}
+
+
+void ieee80211_sta_wakeup(struct ieee80211_device *ieee, short nl);
+
+inline void softmac_mgmt_xmit(struct sk_buff *skb, struct ieee80211_device *ieee)
+{
+ unsigned long flags;
+ short single = ieee->softmac_features & IEEE_SOFTMAC_SINGLE_QUEUE;
+ struct ieee80211_hdr_3addr *header=
+ (struct ieee80211_hdr_3addr *) skb->data;
+
+ cb_desc *tcb_desc = (cb_desc *)(skb->cb + 8);
+ spin_lock_irqsave(&ieee->lock, flags);
+
+ /* called with 2nd param 0, no mgmt lock required */
+ ieee80211_sta_wakeup(ieee, 0);
+
+ tcb_desc->queue_index = MGNT_QUEUE;
+ tcb_desc->data_rate = MgntQuery_MgntFrameTxRate(ieee);
+ tcb_desc->RATRIndex = 7;
+ tcb_desc->bTxDisableRateFallBack = 1;
+ tcb_desc->bTxUseDriverAssingedRate = 1;
+
+ if(single){
+ if(ieee->queue_stop){
+ enqueue_mgmt(ieee, skb);
+ }else{
+ header->seq_ctl = cpu_to_le16(ieee->seq_ctrl[0]<<4);
+
+ if (ieee->seq_ctrl[0] == 0xFFF)
+ ieee->seq_ctrl[0] = 0;
+ else
+ ieee->seq_ctrl[0]++;
+
+ /* avoid watchdog triggers */
+ ieee->dev->trans_start = jiffies;
+ ieee->softmac_data_hard_start_xmit(skb,ieee->dev,ieee->basic_rate);
+ //dev_kfree_skb_any(skb);//edit by thomas
+ }
+
+ spin_unlock_irqrestore(&ieee->lock, flags);
+ }else{
+ spin_unlock_irqrestore(&ieee->lock, flags);
+ spin_lock_irqsave(&ieee->mgmt_tx_lock, flags);
+
+ header->seq_ctl = cpu_to_le16(ieee->seq_ctrl[0] << 4);
+
+ if (ieee->seq_ctrl[0] == 0xFFF)
+ ieee->seq_ctrl[0] = 0;
+ else
+ ieee->seq_ctrl[0]++;
+
+ /* check whether the managed packet queued greater than 5 */
+ if(!ieee->check_nic_enough_desc(ieee->dev,tcb_desc->queue_index)||\
+ (skb_queue_len(&ieee->skb_waitQ[tcb_desc->queue_index]) != 0)||\
+ (ieee->queue_stop) ) {
+ /* insert the skb packet to the management queue */
+ /* as for the completion function, it does not need
+ * to check it any more.
+ * */
+ printk("%s():insert to waitqueue!\n",__func__);
+ skb_queue_tail(&ieee->skb_waitQ[tcb_desc->queue_index], skb);
+ } else {
+ //printk("TX packet!\n");
+ ieee->softmac_hard_start_xmit(skb, ieee->dev);
+ //dev_kfree_skb_any(skb);//edit by thomas
+ }
+ spin_unlock_irqrestore(&ieee->mgmt_tx_lock, flags);
+ }
+}
+
+inline void softmac_ps_mgmt_xmit(struct sk_buff *skb, struct ieee80211_device *ieee)
+{
+
+ short single = ieee->softmac_features & IEEE_SOFTMAC_SINGLE_QUEUE;
+ struct ieee80211_hdr_3addr *header =
+ (struct ieee80211_hdr_3addr *) skb->data;
+
+
+ if(single){
+
+ header->seq_ctl = cpu_to_le16(ieee->seq_ctrl[0] << 4);
+
+ if (ieee->seq_ctrl[0] == 0xFFF)
+ ieee->seq_ctrl[0] = 0;
+ else
+ ieee->seq_ctrl[0]++;
+
+ /* avoid watchdog triggers */
+ ieee->dev->trans_start = jiffies;
+ ieee->softmac_data_hard_start_xmit(skb,ieee->dev,ieee->basic_rate);
+
+ }else{
+
+ header->seq_ctl = cpu_to_le16(ieee->seq_ctrl[0] << 4);
+
+ if (ieee->seq_ctrl[0] == 0xFFF)
+ ieee->seq_ctrl[0] = 0;
+ else
+ ieee->seq_ctrl[0]++;
+
+ ieee->softmac_hard_start_xmit(skb, ieee->dev);
+
+ }
+ //dev_kfree_skb_any(skb);//edit by thomas
+}
+
+inline struct sk_buff *ieee80211_probe_req(struct ieee80211_device *ieee)
+{
+ unsigned int len, rate_len;
+ u8 *tag;
+ struct sk_buff *skb;
+ struct ieee80211_probe_request *req;
+
+ len = ieee->current_network.ssid_len;
+
+ rate_len = ieee80211_MFIE_rate_len(ieee);
+
+ skb = dev_alloc_skb(sizeof(struct ieee80211_probe_request) +
+ 2 + len + rate_len + ieee->tx_headroom);
+ if (!skb)
+ return NULL;
+
+ skb_reserve(skb, ieee->tx_headroom);
+
+ req = (struct ieee80211_probe_request *) skb_put(skb,sizeof(struct ieee80211_probe_request));
+ req->header.frame_ctl = cpu_to_le16(IEEE80211_STYPE_PROBE_REQ);
+ req->header.duration_id = 0; //FIXME: is this OK ?
+
+ memset(req->header.addr1, 0xff, ETH_ALEN);
+ memcpy(req->header.addr2, ieee->dev->dev_addr, ETH_ALEN);
+ memset(req->header.addr3, 0xff, ETH_ALEN);
+
+ tag = (u8 *) skb_put(skb,len+2+rate_len);
+
+ *tag++ = MFIE_TYPE_SSID;
+ *tag++ = len;
+ memcpy(tag, ieee->current_network.ssid, len);
+ tag += len;
+
+ ieee80211_MFIE_Brate(ieee,&tag);
+ ieee80211_MFIE_Grate(ieee,&tag);
+ return skb;
+}
+
+struct sk_buff *ieee80211_get_beacon_(struct ieee80211_device *ieee);
+
+static void ieee80211_send_beacon(struct ieee80211_device *ieee)
+{
+ struct sk_buff *skb;
+ if(!ieee->ieee_up)
+ return;
+ //unsigned long flags;
+ skb = ieee80211_get_beacon_(ieee);
+
+ if (skb) {
+ softmac_mgmt_xmit(skb, ieee);
+ ieee->softmac_stats.tx_beacons++;
+ //dev_kfree_skb_any(skb);//edit by thomas
+ }
+// ieee->beacon_timer.expires = jiffies +
+// (MSECS( ieee->current_network.beacon_interval -5));
+
+ //spin_lock_irqsave(&ieee->beacon_lock,flags);
+ if (ieee->beacon_txing && ieee->ieee_up) {
+// if(!timer_pending(&ieee->beacon_timer))
+// add_timer(&ieee->beacon_timer);
+ mod_timer(&ieee->beacon_timer,jiffies+(MSECS(ieee->current_network.beacon_interval-5)));
+ }
+ //spin_unlock_irqrestore(&ieee->beacon_lock,flags);
+}
+
+
+static void ieee80211_send_beacon_cb(unsigned long _ieee)
+{
+ struct ieee80211_device *ieee =
+ (struct ieee80211_device *) _ieee;
+ unsigned long flags;
+
+ spin_lock_irqsave(&ieee->beacon_lock, flags);
+ ieee80211_send_beacon(ieee);
+ spin_unlock_irqrestore(&ieee->beacon_lock, flags);
+}
+
+
+static void ieee80211_send_probe(struct ieee80211_device *ieee)
+{
+ struct sk_buff *skb;
+
+ skb = ieee80211_probe_req(ieee);
+ if (skb) {
+ softmac_mgmt_xmit(skb, ieee);
+ ieee->softmac_stats.tx_probe_rq++;
+ //dev_kfree_skb_any(skb);//edit by thomas
+ }
+}
+
+static void ieee80211_send_probe_requests(struct ieee80211_device *ieee)
+{
+ if (ieee->active_scan && (ieee->softmac_features & IEEE_SOFTMAC_PROBERQ)) {
+ ieee80211_send_probe(ieee);
+ ieee80211_send_probe(ieee);
+ }
+}
+EXPORT_SYMBOL(ieee80211_send_probe_requests);
+
+/* this performs syncro scan blocking the caller until all channels
+ * in the allowed channel map has been checked.
+ */
+void ieee80211_softmac_scan_syncro(struct ieee80211_device *ieee)
+{
+ short ch = 0;
+ u8 channel_map[MAX_CHANNEL_NUMBER+1];
+ memcpy(channel_map, GET_DOT11D_INFO(ieee)->channel_map, MAX_CHANNEL_NUMBER+1);
+ down(&ieee->scan_sem);
+
+ while(1)
+ {
+
+ do{
+ ch++;
+ if (ch > MAX_CHANNEL_NUMBER)
+ goto out; /* scan completed */
+ }while(!channel_map[ch]);
+
+ /* this function can be called in two situations
+ * 1- We have switched to ad-hoc mode and we are
+ * performing a complete syncro scan before conclude
+ * there are no interesting cell and to create a
+ * new one. In this case the link state is
+ * IEEE80211_NOLINK until we found an interesting cell.
+ * If so the ieee8021_new_net, called by the RX path
+ * will set the state to IEEE80211_LINKED, so we stop
+ * scanning
+ * 2- We are linked and the root uses run iwlist scan.
+ * So we switch to IEEE80211_LINKED_SCANNING to remember
+ * that we are still logically linked (not interested in
+ * new network events, despite for updating the net list,
+ * but we are temporarly 'unlinked' as the driver shall
+ * not filter RX frames and the channel is changing.
+ * So the only situation in witch are interested is to check
+ * if the state become LINKED because of the #1 situation
+ */
+
+ if (ieee->state == IEEE80211_LINKED)
+ goto out;
+ ieee->set_chan(ieee->dev, ch);
+ if(channel_map[ch] == 1)
+ ieee80211_send_probe_requests(ieee);
+
+ /* this prevent excessive time wait when we
+ * need to wait for a syncro scan to end..
+ */
+ if(ieee->state < IEEE80211_LINKED)
+ ;
+ else
+ if (ieee->sync_scan_hurryup)
+ goto out;
+
+
+ msleep_interruptible_rsl(IEEE80211_SOFTMAC_SCAN_TIME);
+
+ }
+out:
+ if(ieee->state < IEEE80211_LINKED){
+ ieee->actscanning = false;
+ up(&ieee->scan_sem);
+ }
+ else{
+ ieee->sync_scan_hurryup = 0;
+ if(IS_DOT11D_ENABLE(ieee))
+ DOT11D_ScanComplete(ieee);
+ up(&ieee->scan_sem);
+}
+}
+EXPORT_SYMBOL(ieee80211_softmac_scan_syncro);
+
+static void ieee80211_softmac_scan_wq(struct work_struct *work)
+{
+ struct delayed_work *dwork = container_of(work, struct delayed_work, work);
+ struct ieee80211_device *ieee = container_of(dwork, struct ieee80211_device, softmac_scan_wq);
+ static short watchdog;
+ u8 channel_map[MAX_CHANNEL_NUMBER+1];
+ memcpy(channel_map, GET_DOT11D_INFO(ieee)->channel_map, MAX_CHANNEL_NUMBER+1);
+ if(!ieee->ieee_up)
+ return;
+ down(&ieee->scan_sem);
+ do{
+ ieee->current_network.channel =
+ (ieee->current_network.channel + 1) % MAX_CHANNEL_NUMBER;
+ if (watchdog++ > MAX_CHANNEL_NUMBER)
+ {
+ //if current channel is not in channel map, set to default channel.
+ if (!channel_map[ieee->current_network.channel]) {
+ ieee->current_network.channel = 6;
+ goto out; /* no good chans */
+ }
+ }
+ }while(!channel_map[ieee->current_network.channel]);
+ if (ieee->scanning == 0 )
+ goto out;
+ ieee->set_chan(ieee->dev, ieee->current_network.channel);
+ if(channel_map[ieee->current_network.channel] == 1)
+ ieee80211_send_probe_requests(ieee);
+
+
+ queue_delayed_work(ieee->wq, &ieee->softmac_scan_wq, IEEE80211_SOFTMAC_SCAN_TIME);
+
+ up(&ieee->scan_sem);
+ return;
+out:
+ if(IS_DOT11D_ENABLE(ieee))
+ DOT11D_ScanComplete(ieee);
+ ieee->actscanning = false;
+ watchdog = 0;
+ ieee->scanning = 0;
+ up(&ieee->scan_sem);
+}
+
+
+
+static void ieee80211_beacons_start(struct ieee80211_device *ieee)
+{
+ unsigned long flags;
+ spin_lock_irqsave(&ieee->beacon_lock,flags);
+
+ ieee->beacon_txing = 1;
+ ieee80211_send_beacon(ieee);
+
+ spin_unlock_irqrestore(&ieee->beacon_lock, flags);
+}
+
+static void ieee80211_beacons_stop(struct ieee80211_device *ieee)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&ieee->beacon_lock, flags);
+
+ ieee->beacon_txing = 0;
+ del_timer_sync(&ieee->beacon_timer);
+
+ spin_unlock_irqrestore(&ieee->beacon_lock, flags);
+
+}
+
+
+void ieee80211_stop_send_beacons(struct ieee80211_device *ieee)
+{
+ if(ieee->stop_send_beacons)
+ ieee->stop_send_beacons(ieee->dev);
+ if (ieee->softmac_features & IEEE_SOFTMAC_BEACONS)
+ ieee80211_beacons_stop(ieee);
+}
+EXPORT_SYMBOL(ieee80211_stop_send_beacons);
+
+void ieee80211_start_send_beacons(struct ieee80211_device *ieee)
+{
+ if(ieee->start_send_beacons)
+ ieee->start_send_beacons(ieee->dev, ieee->basic_rate);
+ if(ieee->softmac_features & IEEE_SOFTMAC_BEACONS)
+ ieee80211_beacons_start(ieee);
+}
+EXPORT_SYMBOL(ieee80211_start_send_beacons);
+
+static void ieee80211_softmac_stop_scan(struct ieee80211_device *ieee)
+{
+// unsigned long flags;
+
+ //ieee->sync_scan_hurryup = 1;
+
+ down(&ieee->scan_sem);
+// spin_lock_irqsave(&ieee->lock, flags);
+
+ if (ieee->scanning == 1) {
+ ieee->scanning = 0;
+
+ cancel_delayed_work(&ieee->softmac_scan_wq);
+ }
+
+// spin_unlock_irqrestore(&ieee->lock, flags);
+ up(&ieee->scan_sem);
+}
+
+void ieee80211_stop_scan(struct ieee80211_device *ieee)
+{
+ if (ieee->softmac_features & IEEE_SOFTMAC_SCAN)
+ ieee80211_softmac_stop_scan(ieee);
+ else
+ ieee->stop_scan(ieee->dev);
+}
+EXPORT_SYMBOL(ieee80211_stop_scan);
+
+/* called with ieee->lock held */
+static void ieee80211_start_scan(struct ieee80211_device *ieee)
+{
+ if (IS_DOT11D_ENABLE(ieee) )
+ {
+ if (IS_COUNTRY_IE_VALID(ieee))
+ {
+ RESET_CIE_WATCHDOG(ieee);
+ }
+ }
+ if (ieee->softmac_features & IEEE_SOFTMAC_SCAN){
+ if (ieee->scanning == 0) {
+ ieee->scanning = 1;
+ queue_delayed_work(ieee->wq, &ieee->softmac_scan_wq, 0);
+ }
+ }else
+ ieee->start_scan(ieee->dev);
+
+}
+
+/* called with wx_sem held */
+void ieee80211_start_scan_syncro(struct ieee80211_device *ieee)
+{
+ if (IS_DOT11D_ENABLE(ieee) )
+ {
+ if (IS_COUNTRY_IE_VALID(ieee))
+ {
+ RESET_CIE_WATCHDOG(ieee);
+ }
+ }
+ ieee->sync_scan_hurryup = 0;
+ if (ieee->softmac_features & IEEE_SOFTMAC_SCAN)
+ ieee80211_softmac_scan_syncro(ieee);
+ else
+ ieee->scan_syncro(ieee->dev);
+
+}
+EXPORT_SYMBOL(ieee80211_start_scan_syncro);
+
+inline struct sk_buff *ieee80211_authentication_req(struct ieee80211_network *beacon,
+ struct ieee80211_device *ieee, int challengelen)
+{
+ struct sk_buff *skb;
+ struct ieee80211_authentication *auth;
+ int len = sizeof(struct ieee80211_authentication) + challengelen + ieee->tx_headroom;
+
+
+ skb = dev_alloc_skb(len);
+ if (!skb) return NULL;
+
+ skb_reserve(skb, ieee->tx_headroom);
+ auth = (struct ieee80211_authentication *)
+ skb_put(skb, sizeof(struct ieee80211_authentication));
+
+ auth->header.frame_ctl = IEEE80211_STYPE_AUTH;
+ if (challengelen) auth->header.frame_ctl |= IEEE80211_FCTL_WEP;
+
+ auth->header.duration_id = 0x013a; //FIXME
+
+ memcpy(auth->header.addr1, beacon->bssid, ETH_ALEN);
+ memcpy(auth->header.addr2, ieee->dev->dev_addr, ETH_ALEN);
+ memcpy(auth->header.addr3, beacon->bssid, ETH_ALEN);
+
+ //auth->algorithm = ieee->open_wep ? WLAN_AUTH_OPEN : WLAN_AUTH_SHARED_KEY;
+ if(ieee->auth_mode == 0)
+ auth->algorithm = WLAN_AUTH_OPEN;
+ else if(ieee->auth_mode == 1)
+ auth->algorithm = WLAN_AUTH_SHARED_KEY;
+ else if(ieee->auth_mode == 2)
+ auth->algorithm = WLAN_AUTH_OPEN;//0x80;
+ printk("=================>%s():auth->algorithm is %d\n",__func__,auth->algorithm);
+ auth->transaction = cpu_to_le16(ieee->associate_seq);
+ ieee->associate_seq++;
+
+ auth->status = cpu_to_le16(WLAN_STATUS_SUCCESS);
+
+ return skb;
+
+}
+
+
+static struct sk_buff *ieee80211_probe_resp(struct ieee80211_device *ieee, u8 *dest)
+{
+ u8 *tag;
+ int beacon_size;
+ struct ieee80211_probe_response *beacon_buf;
+ struct sk_buff *skb = NULL;
+ int encrypt;
+ int atim_len, erp_len;
+ struct ieee80211_crypt_data *crypt;
+
+ char *ssid = ieee->current_network.ssid;
+ int ssid_len = ieee->current_network.ssid_len;
+ int rate_len = ieee->current_network.rates_len+2;
+ int rate_ex_len = ieee->current_network.rates_ex_len;
+ int wpa_ie_len = ieee->wpa_ie_len;
+ u8 erpinfo_content = 0;
+
+ u8 *tmp_ht_cap_buf;
+ u8 tmp_ht_cap_len=0;
+ u8 *tmp_ht_info_buf;
+ u8 tmp_ht_info_len=0;
+ PRT_HIGH_THROUGHPUT pHTInfo = ieee->pHTInfo;
+ u8 *tmp_generic_ie_buf=NULL;
+ u8 tmp_generic_ie_len=0;
+
+ if(rate_ex_len > 0) rate_ex_len+=2;
+
+ if(ieee->current_network.capability & WLAN_CAPABILITY_IBSS)
+ atim_len = 4;
+ else
+ atim_len = 0;
+
+ if(ieee80211_is_54g(&ieee->current_network))
+ erp_len = 3;
+ else
+ erp_len = 0;
+
+
+ crypt = ieee->crypt[ieee->tx_keyidx];
+
+
+ encrypt = ieee->host_encrypt && crypt && crypt->ops &&
+ ((0 == strcmp(crypt->ops->name, "WEP") || wpa_ie_len));
+ //HT ralated element
+ tmp_ht_cap_buf =(u8 *) &(ieee->pHTInfo->SelfHTCap);
+ tmp_ht_cap_len = sizeof(ieee->pHTInfo->SelfHTCap);
+ tmp_ht_info_buf =(u8 *) &(ieee->pHTInfo->SelfHTInfo);
+ tmp_ht_info_len = sizeof(ieee->pHTInfo->SelfHTInfo);
+ HTConstructCapabilityElement(ieee, tmp_ht_cap_buf, &tmp_ht_cap_len,encrypt);
+ HTConstructInfoElement(ieee,tmp_ht_info_buf,&tmp_ht_info_len, encrypt);
+
+
+ if (pHTInfo->bRegRT2RTAggregation)
+ {
+ tmp_generic_ie_buf = ieee->pHTInfo->szRT2RTAggBuffer;
+ tmp_generic_ie_len = sizeof(ieee->pHTInfo->szRT2RTAggBuffer);
+ HTConstructRT2RTAggElement(ieee, tmp_generic_ie_buf, &tmp_generic_ie_len);
+ }
+// printk("===============>tmp_ht_cap_len is %d,tmp_ht_info_len is %d, tmp_generic_ie_len is %d\n",tmp_ht_cap_len,tmp_ht_info_len,tmp_generic_ie_len);
+ beacon_size = sizeof(struct ieee80211_probe_response)+2+
+ ssid_len
+ +3 //channel
+ +rate_len
+ +rate_ex_len
+ +atim_len
+ +erp_len
+ +wpa_ie_len
+ // +tmp_ht_cap_len
+ // +tmp_ht_info_len
+ // +tmp_generic_ie_len
+// +wmm_len+2
+ +ieee->tx_headroom;
+ skb = dev_alloc_skb(beacon_size);
+ if (!skb)
+ return NULL;
+ skb_reserve(skb, ieee->tx_headroom);
+ beacon_buf = (struct ieee80211_probe_response *) skb_put(skb, (beacon_size - ieee->tx_headroom));
+ memcpy (beacon_buf->header.addr1, dest,ETH_ALEN);
+ memcpy (beacon_buf->header.addr2, ieee->dev->dev_addr, ETH_ALEN);
+ memcpy (beacon_buf->header.addr3, ieee->current_network.bssid, ETH_ALEN);
+
+ beacon_buf->header.duration_id = 0; //FIXME
+ beacon_buf->beacon_interval =
+ cpu_to_le16(ieee->current_network.beacon_interval);
+ beacon_buf->capability =
+ cpu_to_le16(ieee->current_network.capability & WLAN_CAPABILITY_IBSS);
+ beacon_buf->capability |=
+ cpu_to_le16(ieee->current_network.capability & WLAN_CAPABILITY_SHORT_PREAMBLE); //add short preamble here
+
+ if(ieee->short_slot && (ieee->current_network.capability & WLAN_CAPABILITY_SHORT_SLOT))
+ beacon_buf->capability |= cpu_to_le16(WLAN_CAPABILITY_SHORT_SLOT);
+
+ crypt = ieee->crypt[ieee->tx_keyidx];
+ if (encrypt)
+ beacon_buf->capability |= cpu_to_le16(WLAN_CAPABILITY_PRIVACY);
+
+
+ beacon_buf->header.frame_ctl = cpu_to_le16(IEEE80211_STYPE_PROBE_RESP);
+ beacon_buf->info_element[0].id = MFIE_TYPE_SSID;
+ beacon_buf->info_element[0].len = ssid_len;
+
+ tag = (u8 *) beacon_buf->info_element[0].data;
+
+ memcpy(tag, ssid, ssid_len);
+
+ tag += ssid_len;
+
+ *(tag++) = MFIE_TYPE_RATES;
+ *(tag++) = rate_len-2;
+ memcpy(tag, ieee->current_network.rates, rate_len-2);
+ tag+=rate_len-2;
+
+ *(tag++) = MFIE_TYPE_DS_SET;
+ *(tag++) = 1;
+ *(tag++) = ieee->current_network.channel;
+
+ if (atim_len) {
+ *(tag++) = MFIE_TYPE_IBSS_SET;
+ *(tag++) = 2;
+
+ put_unaligned_le16(ieee->current_network.atim_window,
+ (u8 *)tag);
+ tag+=2;
+ }
+
+ if (erp_len) {
+ *(tag++) = MFIE_TYPE_ERP;
+ *(tag++) = 1;
+ *(tag++) = erpinfo_content;
+ }
+ if (rate_ex_len) {
+ *(tag++) = MFIE_TYPE_RATES_EX;
+ *(tag++) = rate_ex_len-2;
+ memcpy(tag, ieee->current_network.rates_ex, rate_ex_len-2);
+ tag+=rate_ex_len-2;
+ }
+
+ if (wpa_ie_len)
+ {
+ if (ieee->iw_mode == IW_MODE_ADHOC)
+ {//as Windows will set pairwise key same as the group key which is not allowed in Linux, so set this for IOT issue. WB 2008.07.07
+ memcpy(&ieee->wpa_ie[14], &ieee->wpa_ie[8], 4);
+ }
+ memcpy(tag, ieee->wpa_ie, ieee->wpa_ie_len);
+ tag += wpa_ie_len;
+ }
+
+ //skb->dev = ieee->dev;
+ return skb;
+}
+
+
+static struct sk_buff *ieee80211_assoc_resp(struct ieee80211_device *ieee,
+ u8 *dest)
+{
+ struct sk_buff *skb;
+ u8 *tag;
+
+ struct ieee80211_crypt_data *crypt;
+ struct ieee80211_assoc_response_frame *assoc;
+ short encrypt;
+
+ unsigned int rate_len = ieee80211_MFIE_rate_len(ieee);
+ int len = sizeof(struct ieee80211_assoc_response_frame) + rate_len + ieee->tx_headroom;
+
+ skb = dev_alloc_skb(len);
+
+ if (!skb)
+ return NULL;
+
+ skb_reserve(skb, ieee->tx_headroom);
+
+ assoc = (struct ieee80211_assoc_response_frame *)
+ skb_put(skb, sizeof(struct ieee80211_assoc_response_frame));
+
+ assoc->header.frame_ctl = cpu_to_le16(IEEE80211_STYPE_ASSOC_RESP);
+ memcpy(assoc->header.addr1, dest,ETH_ALEN);
+ memcpy(assoc->header.addr3, ieee->dev->dev_addr, ETH_ALEN);
+ memcpy(assoc->header.addr2, ieee->dev->dev_addr, ETH_ALEN);
+ assoc->capability = cpu_to_le16(ieee->iw_mode == IW_MODE_MASTER ?
+ WLAN_CAPABILITY_BSS : WLAN_CAPABILITY_IBSS);
+
+
+ if(ieee->short_slot)
+ assoc->capability |= cpu_to_le16(WLAN_CAPABILITY_SHORT_SLOT);
+
+ if (ieee->host_encrypt)
+ crypt = ieee->crypt[ieee->tx_keyidx];
+ else crypt = NULL;
+
+ encrypt = crypt && crypt->ops;
+
+ if (encrypt)
+ assoc->capability |= cpu_to_le16(WLAN_CAPABILITY_PRIVACY);
+
+ assoc->status = 0;
+ assoc->aid = cpu_to_le16(ieee->assoc_id);
+ if (ieee->assoc_id == 0x2007) ieee->assoc_id=0;
+ else ieee->assoc_id++;
+
+ tag = (u8 *) skb_put(skb, rate_len);
+
+ ieee80211_MFIE_Brate(ieee, &tag);
+ ieee80211_MFIE_Grate(ieee, &tag);
+
+ return skb;
+}
+
+static struct sk_buff *ieee80211_auth_resp(struct ieee80211_device *ieee,
+ int status, u8 *dest)
+{
+ struct sk_buff *skb;
+ struct ieee80211_authentication *auth;
+ int len = ieee->tx_headroom + sizeof(struct ieee80211_authentication)+1;
+
+ skb = dev_alloc_skb(len);
+
+ if (!skb)
+ return NULL;
+
+ skb->len = sizeof(struct ieee80211_authentication);
+
+ auth = (struct ieee80211_authentication *)skb->data;
+
+ auth->status = cpu_to_le16(status);
+ auth->transaction = cpu_to_le16(2);
+ auth->algorithm = cpu_to_le16(WLAN_AUTH_OPEN);
+
+ memcpy(auth->header.addr3, ieee->dev->dev_addr, ETH_ALEN);
+ memcpy(auth->header.addr2, ieee->dev->dev_addr, ETH_ALEN);
+ memcpy(auth->header.addr1, dest, ETH_ALEN);
+ auth->header.frame_ctl = cpu_to_le16(IEEE80211_STYPE_AUTH);
+ return skb;
+
+
+}
+
+static struct sk_buff *ieee80211_null_func(struct ieee80211_device *ieee,
+ short pwr)
+{
+ struct sk_buff *skb;
+ struct ieee80211_hdr_3addr *hdr;
+
+ skb = dev_alloc_skb(sizeof(struct ieee80211_hdr_3addr));
+
+ if (!skb)
+ return NULL;
+
+ hdr = (struct ieee80211_hdr_3addr *)skb_put(skb,sizeof(struct ieee80211_hdr_3addr));
+
+ memcpy(hdr->addr1, ieee->current_network.bssid, ETH_ALEN);
+ memcpy(hdr->addr2, ieee->dev->dev_addr, ETH_ALEN);
+ memcpy(hdr->addr3, ieee->current_network.bssid, ETH_ALEN);
+
+ hdr->frame_ctl = cpu_to_le16(IEEE80211_FTYPE_DATA |
+ IEEE80211_STYPE_NULLFUNC | IEEE80211_FCTL_TODS |
+ (pwr ? IEEE80211_FCTL_PM:0));
+
+ return skb;
+
+
+}
+
+
+static void ieee80211_resp_to_assoc_rq(struct ieee80211_device *ieee, u8 *dest)
+{
+ struct sk_buff *buf = ieee80211_assoc_resp(ieee, dest);
+
+ if (buf)
+ softmac_mgmt_xmit(buf, ieee);
+}
+
+
+static void ieee80211_resp_to_auth(struct ieee80211_device *ieee, int s,
+ u8 *dest)
+{
+ struct sk_buff *buf = ieee80211_auth_resp(ieee, s, dest);
+
+ if (buf)
+ softmac_mgmt_xmit(buf, ieee);
+}
+
+
+static void ieee80211_resp_to_probe(struct ieee80211_device *ieee, u8 *dest)
+{
+
+
+ struct sk_buff *buf = ieee80211_probe_resp(ieee, dest);
+ if (buf)
+ softmac_mgmt_xmit(buf, ieee);
+}
+
+
+inline struct sk_buff *ieee80211_association_req(struct ieee80211_network *beacon,struct ieee80211_device *ieee)
+{
+ struct sk_buff *skb;
+ //unsigned long flags;
+
+ struct ieee80211_assoc_request_frame *hdr;
+ u8 *tag;//,*rsn_ie;
+ //short info_addr = 0;
+ //int i;
+ //u16 suite_count = 0;
+ //u8 suit_select = 0;
+ //unsigned int wpa_len = beacon->wpa_ie_len;
+ //for HT
+ u8 *ht_cap_buf = NULL;
+ u8 ht_cap_len=0;
+ u8 *realtek_ie_buf=NULL;
+ u8 realtek_ie_len=0;
+ int wpa_ie_len= ieee->wpa_ie_len;
+ unsigned int ckip_ie_len=0;
+ unsigned int ccxrm_ie_len=0;
+ unsigned int cxvernum_ie_len=0;
+ struct ieee80211_crypt_data *crypt;
+ int encrypt;
+
+ unsigned int rate_len = ieee80211_MFIE_rate_len(ieee);
+ unsigned int wmm_info_len = beacon->qos_data.supported?9:0;
+#ifdef THOMAS_TURBO
+ unsigned int turbo_info_len = beacon->Turbo_Enable?9:0;
+#endif
+
+ int len = 0;
+
+ crypt = ieee->crypt[ieee->tx_keyidx];
+ encrypt = ieee->host_encrypt && crypt && crypt->ops && ((0 == strcmp(crypt->ops->name,"WEP") || wpa_ie_len));
+
+ //Include High Throuput capability && Realtek proprietary
+ if (ieee->pHTInfo->bCurrentHTSupport&&ieee->pHTInfo->bEnableHT)
+ {
+ ht_cap_buf = (u8 *)&(ieee->pHTInfo->SelfHTCap);
+ ht_cap_len = sizeof(ieee->pHTInfo->SelfHTCap);
+ HTConstructCapabilityElement(ieee, ht_cap_buf, &ht_cap_len, encrypt);
+ if (ieee->pHTInfo->bCurrentRT2RTAggregation)
+ {
+ realtek_ie_buf = ieee->pHTInfo->szRT2RTAggBuffer;
+ realtek_ie_len = sizeof( ieee->pHTInfo->szRT2RTAggBuffer);
+ HTConstructRT2RTAggElement(ieee, realtek_ie_buf, &realtek_ie_len);
+
+ }
+ }
+ if (ieee->qos_support) {
+ wmm_info_len = beacon->qos_data.supported?9:0;
+ }
+
+
+ if (beacon->bCkipSupported)
+ {
+ ckip_ie_len = 30+2;
+ }
+ if (beacon->bCcxRmEnable)
+ {
+ ccxrm_ie_len = 6+2;
+ }
+ if (beacon->BssCcxVerNumber >= 2)
+ cxvernum_ie_len = 5+2;
+
+#ifdef THOMAS_TURBO
+ len = sizeof(struct ieee80211_assoc_request_frame)+ 2
+ + beacon->ssid_len//essid tagged val
+ + rate_len//rates tagged val
+ + wpa_ie_len
+ + wmm_info_len
+ + turbo_info_len
+ + ht_cap_len
+ + realtek_ie_len
+ + ckip_ie_len
+ + ccxrm_ie_len
+ + cxvernum_ie_len
+ + ieee->tx_headroom;
+#else
+ len = sizeof(struct ieee80211_assoc_request_frame)+ 2
+ + beacon->ssid_len//essid tagged val
+ + rate_len//rates tagged val
+ + wpa_ie_len
+ + wmm_info_len
+ + ht_cap_len
+ + realtek_ie_len
+ + ckip_ie_len
+ + ccxrm_ie_len
+ + cxvernum_ie_len
+ + ieee->tx_headroom;
+#endif
+
+ skb = dev_alloc_skb(len);
+
+ if (!skb)
+ return NULL;
+
+ skb_reserve(skb, ieee->tx_headroom);
+
+ hdr = (struct ieee80211_assoc_request_frame *)
+ skb_put(skb, sizeof(struct ieee80211_assoc_request_frame)+2);
+
+
+ hdr->header.frame_ctl = IEEE80211_STYPE_ASSOC_REQ;
+ hdr->header.duration_id= 37; //FIXME
+ memcpy(hdr->header.addr1, beacon->bssid, ETH_ALEN);
+ memcpy(hdr->header.addr2, ieee->dev->dev_addr, ETH_ALEN);
+ memcpy(hdr->header.addr3, beacon->bssid, ETH_ALEN);
+
+ memcpy(ieee->ap_mac_addr, beacon->bssid, ETH_ALEN);//for HW security, John
+
+ hdr->capability = cpu_to_le16(WLAN_CAPABILITY_BSS);
+ if (beacon->capability & WLAN_CAPABILITY_PRIVACY )
+ hdr->capability |= cpu_to_le16(WLAN_CAPABILITY_PRIVACY);
+
+ if (beacon->capability & WLAN_CAPABILITY_SHORT_PREAMBLE)
+ hdr->capability |= cpu_to_le16(WLAN_CAPABILITY_SHORT_PREAMBLE); //add short_preamble here
+
+ if(ieee->short_slot)
+ hdr->capability |= cpu_to_le16(WLAN_CAPABILITY_SHORT_SLOT);
+ if (wmm_info_len) //QOS
+ hdr->capability |= cpu_to_le16(WLAN_CAPABILITY_QOS);
+
+ hdr->listen_interval = 0xa; //FIXME
+
+ hdr->info_element[0].id = MFIE_TYPE_SSID;
+
+ hdr->info_element[0].len = beacon->ssid_len;
+ tag = skb_put(skb, beacon->ssid_len);
+ memcpy(tag, beacon->ssid, beacon->ssid_len);
+
+ tag = skb_put(skb, rate_len);
+
+ ieee80211_MFIE_Brate(ieee, &tag);
+ ieee80211_MFIE_Grate(ieee, &tag);
+ // For CCX 1 S13, CKIP. Added by Annie, 2006-08-14.
+ if (beacon->bCkipSupported) {
+ static u8 AironetIeOui[] = {0x00, 0x01, 0x66}; // "4500-client"
+ u8 CcxAironetBuf[30];
+ OCTET_STRING osCcxAironetIE;
+
+ memset(CcxAironetBuf, 0, 30);
+ osCcxAironetIE.Octet = CcxAironetBuf;
+ osCcxAironetIE.Length = sizeof(CcxAironetBuf);
+ //
+ // Ref. CCX test plan v3.61, 3.2.3.1 step 13.
+ // We want to make the device type as "4500-client". 060926, by CCW.
+ //
+ memcpy(osCcxAironetIE.Octet, AironetIeOui, sizeof(AironetIeOui));
+
+ // CCX1 spec V1.13, A01.1 CKIP Negotiation (page23):
+ // "The CKIP negotiation is started with the associate request from the client to the access point,
+ // containing an Aironet element with both the MIC and KP bits set."
+ osCcxAironetIE.Octet[IE_CISCO_FLAG_POSITION] |= (SUPPORT_CKIP_PK|SUPPORT_CKIP_MIC) ;
+ tag = skb_put(skb, ckip_ie_len);
+ *tag++ = MFIE_TYPE_AIRONET;
+ *tag++ = osCcxAironetIE.Length;
+ memcpy(tag, osCcxAironetIE.Octet, osCcxAironetIE.Length);
+ tag += osCcxAironetIE.Length;
+ }
+
+ if (beacon->bCcxRmEnable)
+ {
+ static u8 CcxRmCapBuf[] = {0x00, 0x40, 0x96, 0x01, 0x01, 0x00};
+ OCTET_STRING osCcxRmCap;
+
+ osCcxRmCap.Octet = CcxRmCapBuf;
+ osCcxRmCap.Length = sizeof(CcxRmCapBuf);
+ tag = skb_put(skb, ccxrm_ie_len);
+ *tag++ = MFIE_TYPE_GENERIC;
+ *tag++ = osCcxRmCap.Length;
+ memcpy(tag, osCcxRmCap.Octet, osCcxRmCap.Length);
+ tag += osCcxRmCap.Length;
+ }
+
+ if (beacon->BssCcxVerNumber >= 2) {
+ u8 CcxVerNumBuf[] = {0x00, 0x40, 0x96, 0x03, 0x00};
+ OCTET_STRING osCcxVerNum;
+ CcxVerNumBuf[4] = beacon->BssCcxVerNumber;
+ osCcxVerNum.Octet = CcxVerNumBuf;
+ osCcxVerNum.Length = sizeof(CcxVerNumBuf);
+ tag = skb_put(skb, cxvernum_ie_len);
+ *tag++ = MFIE_TYPE_GENERIC;
+ *tag++ = osCcxVerNum.Length;
+ memcpy(tag, osCcxVerNum.Octet, osCcxVerNum.Length);
+ tag += osCcxVerNum.Length;
+ }
+ //HT cap element
+ if (ieee->pHTInfo->bCurrentHTSupport && ieee->pHTInfo->bEnableHT) {
+ if (ieee->pHTInfo->ePeerHTSpecVer != HT_SPEC_VER_EWC)
+ {
+ tag = skb_put(skb, ht_cap_len);
+ *tag++ = MFIE_TYPE_HT_CAP;
+ *tag++ = ht_cap_len - 2;
+ memcpy(tag, ht_cap_buf,ht_cap_len -2);
+ tag += ht_cap_len -2;
+ }
+ }
+
+
+ //choose what wpa_supplicant gives to associate.
+ tag = skb_put(skb, wpa_ie_len);
+ if (wpa_ie_len) {
+ memcpy(tag, ieee->wpa_ie, ieee->wpa_ie_len);
+ }
+
+ tag = skb_put(skb, wmm_info_len);
+ if (wmm_info_len) {
+ ieee80211_WMM_Info(ieee, &tag);
+ }
+#ifdef THOMAS_TURBO
+ tag = skb_put(skb, turbo_info_len);
+ if (turbo_info_len) {
+ ieee80211_TURBO_Info(ieee, &tag);
+ }
+#endif
+
+ if (ieee->pHTInfo->bCurrentHTSupport && ieee->pHTInfo->bEnableHT) {
+ if(ieee->pHTInfo->ePeerHTSpecVer == HT_SPEC_VER_EWC)
+ {
+ tag = skb_put(skb, ht_cap_len);
+ *tag++ = MFIE_TYPE_GENERIC;
+ *tag++ = ht_cap_len - 2;
+ memcpy(tag, ht_cap_buf, ht_cap_len - 2);
+ tag += ht_cap_len -2;
+ }
+
+ if (ieee->pHTInfo->bCurrentRT2RTAggregation) {
+ tag = skb_put(skb, realtek_ie_len);
+ *tag++ = MFIE_TYPE_GENERIC;
+ *tag++ = realtek_ie_len - 2;
+ memcpy(tag, realtek_ie_buf,realtek_ie_len -2 );
+ }
+ }
+// printk("<=====%s(), %p, %p\n", __func__, ieee->dev, ieee->dev->dev_addr);
+// IEEE80211_DEBUG_DATA(IEEE80211_DL_DATA, skb->data, skb->len);
+ return skb;
+}
+
+void ieee80211_associate_abort(struct ieee80211_device *ieee)
+{
+
+ unsigned long flags;
+ spin_lock_irqsave(&ieee->lock, flags);
+
+ ieee->associate_seq++;
+
+ /* don't scan, and avoid to have the RX path possibily
+ * try again to associate. Even do not react to AUTH or
+ * ASSOC response. Just wait for the retry wq to be scheduled.
+ * Here we will check if there are good nets to associate
+ * with, so we retry or just get back to NO_LINK and scanning
+ */
+ if (ieee->state == IEEE80211_ASSOCIATING_AUTHENTICATING){
+ IEEE80211_DEBUG_MGMT("Authentication failed\n");
+ ieee->softmac_stats.no_auth_rs++;
+ }else{
+ IEEE80211_DEBUG_MGMT("Association failed\n");
+ ieee->softmac_stats.no_ass_rs++;
+ }
+
+ ieee->state = IEEE80211_ASSOCIATING_RETRY;
+
+ queue_delayed_work(ieee->wq, &ieee->associate_retry_wq, \
+ IEEE80211_SOFTMAC_ASSOC_RETRY_TIME);
+
+ spin_unlock_irqrestore(&ieee->lock, flags);
+}
+
+static void ieee80211_associate_abort_cb(unsigned long dev)
+{
+ ieee80211_associate_abort((struct ieee80211_device *) dev);
+}
+
+
+static void ieee80211_associate_step1(struct ieee80211_device *ieee)
+{
+ struct ieee80211_network *beacon = &ieee->current_network;
+ struct sk_buff *skb;
+
+ IEEE80211_DEBUG_MGMT("Stopping scan\n");
+
+ ieee->softmac_stats.tx_auth_rq++;
+ skb=ieee80211_authentication_req(beacon, ieee, 0);
+
+ if (!skb)
+ ieee80211_associate_abort(ieee);
+ else{
+ ieee->state = IEEE80211_ASSOCIATING_AUTHENTICATING ;
+ IEEE80211_DEBUG_MGMT("Sending authentication request\n");
+ //printk(KERN_WARNING "Sending authentication request\n");
+ softmac_mgmt_xmit(skb, ieee);
+ //BUGON when you try to add_timer twice, using mod_timer may be better, john0709
+ if (!timer_pending(&ieee->associate_timer)) {
+ ieee->associate_timer.expires = jiffies + (HZ / 2);
+ add_timer(&ieee->associate_timer);
+ }
+ //dev_kfree_skb_any(skb);//edit by thomas
+ }
+}
+
+static void ieee80211_auth_challenge(struct ieee80211_device *ieee,
+ u8 *challenge,
+ int chlen)
+{
+ u8 *c;
+ struct sk_buff *skb;
+ struct ieee80211_network *beacon = &ieee->current_network;
+// int hlen = sizeof(struct ieee80211_authentication);
+
+ ieee->associate_seq++;
+ ieee->softmac_stats.tx_auth_rq++;
+
+ skb = ieee80211_authentication_req(beacon, ieee, chlen+2);
+ if (!skb)
+ ieee80211_associate_abort(ieee);
+ else{
+ c = skb_put(skb, chlen+2);
+ *(c++) = MFIE_TYPE_CHALLENGE;
+ *(c++) = chlen;
+ memcpy(c, challenge, chlen);
+
+ IEEE80211_DEBUG_MGMT("Sending authentication challenge response\n");
+
+ ieee80211_encrypt_fragment(ieee, skb, sizeof(struct ieee80211_hdr_3addr ));
+
+ softmac_mgmt_xmit(skb, ieee);
+ mod_timer(&ieee->associate_timer, jiffies + (HZ/2));
+ //dev_kfree_skb_any(skb);//edit by thomas
+ }
+ kfree(challenge);
+}
+
+static void ieee80211_associate_step2(struct ieee80211_device *ieee)
+{
+ struct sk_buff *skb;
+ struct ieee80211_network *beacon = &ieee->current_network;
+
+ del_timer_sync(&ieee->associate_timer);
+
+ IEEE80211_DEBUG_MGMT("Sending association request\n");
+
+ ieee->softmac_stats.tx_ass_rq++;
+ skb=ieee80211_association_req(beacon, ieee);
+ if (!skb)
+ ieee80211_associate_abort(ieee);
+ else{
+ softmac_mgmt_xmit(skb, ieee);
+ mod_timer(&ieee->associate_timer, jiffies + (HZ/2));
+ //dev_kfree_skb_any(skb);//edit by thomas
+ }
+}
+static void ieee80211_associate_complete_wq(struct work_struct *work)
+{
+ struct ieee80211_device *ieee = container_of(work, struct ieee80211_device, associate_complete_wq);
+ printk(KERN_INFO "Associated successfully\n");
+ if(ieee80211_is_54g(&ieee->current_network) &&
+ (ieee->modulation & IEEE80211_OFDM_MODULATION)){
+
+ ieee->rate = 108;
+ printk(KERN_INFO"Using G rates:%d\n", ieee->rate);
+ }else{
+ ieee->rate = 22;
+ printk(KERN_INFO"Using B rates:%d\n", ieee->rate);
+ }
+ if (ieee->pHTInfo->bCurrentHTSupport&&ieee->pHTInfo->bEnableHT)
+ {
+ printk("Successfully associated, ht enabled\n");
+ HTOnAssocRsp(ieee);
+ }
+ else
+ {
+ printk("Successfully associated, ht not enabled(%d, %d)\n", ieee->pHTInfo->bCurrentHTSupport, ieee->pHTInfo->bEnableHT);
+ memset(ieee->dot11HTOperationalRateSet, 0, 16);
+ //HTSetConnectBwMode(ieee, HT_CHANNEL_WIDTH_20, HT_EXTCHNL_OFFSET_NO_EXT);
+ }
+ ieee->LinkDetectInfo.SlotNum = 2 * (1 + ieee->current_network.beacon_interval/500);
+ // To prevent the immediately calling watch_dog after association.
+ if (ieee->LinkDetectInfo.NumRecvBcnInPeriod==0||ieee->LinkDetectInfo.NumRecvDataInPeriod==0 )
+ {
+ ieee->LinkDetectInfo.NumRecvBcnInPeriod = 1;
+ ieee->LinkDetectInfo.NumRecvDataInPeriod= 1;
+ }
+ ieee->link_change(ieee->dev);
+ if(ieee->is_silent_reset == 0){
+ printk("============>normal associate\n");
+ notify_wx_assoc_event(ieee);
+ }
+ else if(ieee->is_silent_reset == 1)
+ {
+ printk("==================>silent reset associate\n");
+ ieee->is_silent_reset = false;
+ }
+
+ if (ieee->data_hard_resume)
+ ieee->data_hard_resume(ieee->dev);
+ netif_carrier_on(ieee->dev);
+}
+
+static void ieee80211_associate_complete(struct ieee80211_device *ieee)
+{
+// int i;
+// struct net_device* dev = ieee->dev;
+ del_timer_sync(&ieee->associate_timer);
+
+ ieee->state = IEEE80211_LINKED;
+ //ieee->UpdateHalRATRTableHandler(dev, ieee->dot11HTOperationalRateSet);
+ queue_work(ieee->wq, &ieee->associate_complete_wq);
+}
+
+static void ieee80211_associate_procedure_wq(struct work_struct *work)
+{
+ struct ieee80211_device *ieee = container_of(work, struct ieee80211_device, associate_procedure_wq);
+ ieee->sync_scan_hurryup = 1;
+ down(&ieee->wx_sem);
+
+ if (ieee->data_hard_stop)
+ ieee->data_hard_stop(ieee->dev);
+
+ ieee80211_stop_scan(ieee);
+ printk("===>%s(), chan:%d\n", __func__, ieee->current_network.channel);
+ //ieee->set_chan(ieee->dev, ieee->current_network.channel);
+ HTSetConnectBwMode(ieee, HT_CHANNEL_WIDTH_20, HT_EXTCHNL_OFFSET_NO_EXT);
+
+ ieee->associate_seq = 1;
+ ieee80211_associate_step1(ieee);
+
+ up(&ieee->wx_sem);
+}
+
+inline void ieee80211_softmac_new_net(struct ieee80211_device *ieee, struct ieee80211_network *net)
+{
+ u8 tmp_ssid[IW_ESSID_MAX_SIZE+1];
+ int tmp_ssid_len = 0;
+
+ short apset, ssidset, ssidbroad, apmatch, ssidmatch;
+
+ /* we are interested in new new only if we are not associated
+ * and we are not associating / authenticating
+ */
+ if (ieee->state != IEEE80211_NOLINK)
+ return;
+
+ if ((ieee->iw_mode == IW_MODE_INFRA) && !(net->capability & WLAN_CAPABILITY_BSS))
+ return;
+
+ if ((ieee->iw_mode == IW_MODE_ADHOC) && !(net->capability & WLAN_CAPABILITY_IBSS))
+ return;
+
+
+ if (ieee->iw_mode == IW_MODE_INFRA || ieee->iw_mode == IW_MODE_ADHOC) {
+ /* if the user specified the AP MAC, we need also the essid
+ * This could be obtained by beacons or, if the network does not
+ * broadcast it, it can be put manually.
+ */
+ apset = ieee->wap_set;//(memcmp(ieee->current_network.bssid, zero,ETH_ALEN)!=0 );
+ ssidset = ieee->ssid_set;//ieee->current_network.ssid[0] != '\0';
+ ssidbroad = !(net->ssid_len == 0 || net->ssid[0]== '\0');
+ apmatch = (memcmp(ieee->current_network.bssid, net->bssid, ETH_ALEN)==0);
+ ssidmatch = (ieee->current_network.ssid_len == net->ssid_len)&&\
+ (!strncmp(ieee->current_network.ssid, net->ssid, net->ssid_len));
+
+
+ if ( /* if the user set the AP check if match.
+ * if the network does not broadcast essid we check the user supplyed ANY essid
+ * if the network does broadcast and the user does not set essid it is OK
+ * if the network does broadcast and the user did set essid chech if essid match
+ */
+ (apset && apmatch &&
+ ((ssidset && ssidbroad && ssidmatch) || (ssidbroad && !ssidset) || (!ssidbroad && ssidset)) ) ||
+ /* if the ap is not set, check that the user set the bssid
+ * and the network does broadcast and that those two bssid matches
+ */
+ (!apset && ssidset && ssidbroad && ssidmatch)
+ ){
+ /* if the essid is hidden replace it with the
+ * essid provided by the user.
+ */
+ if (!ssidbroad) {
+ strncpy(tmp_ssid, ieee->current_network.ssid, IW_ESSID_MAX_SIZE);
+ tmp_ssid_len = ieee->current_network.ssid_len;
+ }
+ memcpy(&ieee->current_network, net, sizeof(struct ieee80211_network));
+
+ if (!ssidbroad) {
+ strncpy(ieee->current_network.ssid, tmp_ssid, IW_ESSID_MAX_SIZE);
+ ieee->current_network.ssid_len = tmp_ssid_len;
+ }
+ printk(KERN_INFO"Linking with %s,channel:%d, qos:%d, myHT:%d, networkHT:%d\n",ieee->current_network.ssid,ieee->current_network.channel, ieee->current_network.qos_data.supported, ieee->pHTInfo->bEnableHT, ieee->current_network.bssht.bdSupportHT);
+
+ //ieee->pHTInfo->IOTAction = 0;
+ HTResetIOTSetting(ieee->pHTInfo);
+ if (ieee->iw_mode == IW_MODE_INFRA){
+ /* Join the network for the first time */
+ ieee->AsocRetryCount = 0;
+ //for HT by amy 080514
+ if((ieee->current_network.qos_data.supported == 1) &&
+ // (ieee->pHTInfo->bEnableHT && ieee->current_network.bssht.bdSupportHT))
+ ieee->current_network.bssht.bdSupportHT)
+/*WB, 2008.09.09:bCurrentHTSupport and bEnableHT two flags are going to put together to check whether we are in HT now, so needn't to check bEnableHT flags here. That's is to say we will set to HT support whenever joined AP has the ability to support HT. And whether we are in HT or not, please check bCurrentHTSupport&&bEnableHT now please.*/
+ {
+ // ieee->pHTInfo->bCurrentHTSupport = true;
+ HTResetSelfAndSavePeerSetting(ieee, &(ieee->current_network));
+ }
+ else
+ {
+ ieee->pHTInfo->bCurrentHTSupport = false;
+ }
+
+ ieee->state = IEEE80211_ASSOCIATING;
+ queue_work(ieee->wq, &ieee->associate_procedure_wq);
+ }else{
+ if(ieee80211_is_54g(&ieee->current_network) &&
+ (ieee->modulation & IEEE80211_OFDM_MODULATION)){
+ ieee->rate = 108;
+ ieee->SetWirelessMode(ieee->dev, IEEE_G);
+ printk(KERN_INFO"Using G rates\n");
+ }else{
+ ieee->rate = 22;
+ ieee->SetWirelessMode(ieee->dev, IEEE_B);
+ printk(KERN_INFO"Using B rates\n");
+ }
+ memset(ieee->dot11HTOperationalRateSet, 0, 16);
+ //HTSetConnectBwMode(ieee, HT_CHANNEL_WIDTH_20, HT_EXTCHNL_OFFSET_NO_EXT);
+ ieee->state = IEEE80211_LINKED;
+ }
+
+ }
+ }
+
+}
+
+void ieee80211_softmac_check_all_nets(struct ieee80211_device *ieee)
+{
+ unsigned long flags;
+ struct ieee80211_network *target;
+
+ spin_lock_irqsave(&ieee->lock, flags);
+
+ list_for_each_entry(target, &ieee->network_list, list) {
+
+ /* if the state become different that NOLINK means
+ * we had found what we are searching for
+ */
+
+ if (ieee->state != IEEE80211_NOLINK)
+ break;
+
+ if (ieee->scan_age == 0 || time_after(target->last_scanned + ieee->scan_age, jiffies))
+ ieee80211_softmac_new_net(ieee, target);
+ }
+
+ spin_unlock_irqrestore(&ieee->lock, flags);
+
+}
+
+
+static inline u16 auth_parse(struct sk_buff *skb, u8 **challenge, int *chlen)
+{
+ struct ieee80211_authentication *a;
+ u8 *t;
+ if (skb->len < (sizeof(struct ieee80211_authentication) - sizeof(struct ieee80211_info_element))) {
+ IEEE80211_DEBUG_MGMT("invalid len in auth resp: %d\n",skb->len);
+ return 0xcafe;
+ }
+ *challenge = NULL;
+ a = (struct ieee80211_authentication *) skb->data;
+ if (skb->len > (sizeof(struct ieee80211_authentication) + 3)) {
+ t = skb->data + sizeof(struct ieee80211_authentication);
+
+ if (*(t++) == MFIE_TYPE_CHALLENGE) {
+ *chlen = *(t++);
+ *challenge = kmemdup(t, *chlen, GFP_ATOMIC);
+ if (!*challenge)
+ return -ENOMEM;
+ }
+ }
+
+ return cpu_to_le16(a->status);
+
+}
+
+
+static int auth_rq_parse(struct sk_buff *skb, u8 *dest)
+{
+ struct ieee80211_authentication *a;
+
+ if (skb->len < (sizeof(struct ieee80211_authentication) - sizeof(struct ieee80211_info_element))) {
+ IEEE80211_DEBUG_MGMT("invalid len in auth request: %d\n",skb->len);
+ return -1;
+ }
+ a = (struct ieee80211_authentication *) skb->data;
+
+ memcpy(dest,a->header.addr2, ETH_ALEN);
+
+ if (le16_to_cpu(a->algorithm) != WLAN_AUTH_OPEN)
+ return WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG;
+
+ return WLAN_STATUS_SUCCESS;
+}
+
+static short probe_rq_parse(struct ieee80211_device *ieee, struct sk_buff *skb, u8 *src)
+{
+ u8 *tag;
+ u8 *skbend;
+ u8 *ssid=NULL;
+ u8 ssidlen = 0;
+
+ struct ieee80211_hdr_3addr *header =
+ (struct ieee80211_hdr_3addr *) skb->data;
+
+ if (skb->len < sizeof (struct ieee80211_hdr_3addr ))
+ return -1; /* corrupted */
+
+ memcpy(src,header->addr2, ETH_ALEN);
+
+ skbend = (u8 *)skb->data + skb->len;
+
+ tag = skb->data + sizeof (struct ieee80211_hdr_3addr );
+
+ while (tag+1 < skbend){
+ if (*tag == 0) {
+ ssid = tag+2;
+ ssidlen = *(tag+1);
+ break;
+ }
+ tag++; /* point to the len field */
+ tag = tag + *(tag); /* point to the last data byte of the tag */
+ tag++; /* point to the next tag */
+ }
+
+ //IEEE80211DMESG("Card MAC address is "MACSTR, MAC2STR(src));
+ if (ssidlen == 0) return 1;
+
+ if (!ssid) return 1; /* ssid not found in tagged param */
+ return (!strncmp(ssid, ieee->current_network.ssid, ssidlen));
+
+}
+
+static int assoc_rq_parse(struct sk_buff *skb, u8 *dest)
+{
+ struct ieee80211_assoc_request_frame *a;
+
+ if (skb->len < (sizeof(struct ieee80211_assoc_request_frame) -
+ sizeof(struct ieee80211_info_element))) {
+
+ IEEE80211_DEBUG_MGMT("invalid len in auth request:%d \n", skb->len);
+ return -1;
+ }
+
+ a = (struct ieee80211_assoc_request_frame *) skb->data;
+
+ memcpy(dest,a->header.addr2,ETH_ALEN);
+
+ return 0;
+}
+
+static inline u16 assoc_parse(struct ieee80211_device *ieee, struct sk_buff *skb, int *aid)
+{
+ struct ieee80211_assoc_response_frame *response_head;
+ u16 status_code;
+
+ if (skb->len < sizeof(struct ieee80211_assoc_response_frame)) {
+ IEEE80211_DEBUG_MGMT("invalid len in auth resp: %d\n", skb->len);
+ return 0xcafe;
+ }
+
+ response_head = (struct ieee80211_assoc_response_frame *) skb->data;
+ *aid = le16_to_cpu(response_head->aid) & 0x3fff;
+
+ status_code = le16_to_cpu(response_head->status);
+ if((status_code==WLAN_STATUS_ASSOC_DENIED_RATES || \
+ status_code==WLAN_STATUS_CAPS_UNSUPPORTED)&&
+ ((ieee->mode == IEEE_G) &&
+ (ieee->current_network.mode == IEEE_N_24G) &&
+ (ieee->AsocRetryCount++ < (RT_ASOC_RETRY_LIMIT-1)))) {
+ ieee->pHTInfo->IOTAction |= HT_IOT_ACT_PURE_N_MODE;
+ }else {
+ ieee->AsocRetryCount = 0;
+ }
+
+ return le16_to_cpu(response_head->status);
+}
+
+static inline void
+ieee80211_rx_probe_rq(struct ieee80211_device *ieee, struct sk_buff *skb)
+{
+ u8 dest[ETH_ALEN];
+
+ //IEEE80211DMESG("Rx probe");
+ ieee->softmac_stats.rx_probe_rq++;
+ //DMESG("Dest is "MACSTR, MAC2STR(dest));
+ if (probe_rq_parse(ieee, skb, dest)) {
+ //IEEE80211DMESG("Was for me!");
+ ieee->softmac_stats.tx_probe_rs++;
+ ieee80211_resp_to_probe(ieee, dest);
+ }
+}
+
+static inline void
+ieee80211_rx_auth_rq(struct ieee80211_device *ieee, struct sk_buff *skb)
+{
+ u8 dest[ETH_ALEN];
+ int status;
+ //IEEE80211DMESG("Rx probe");
+ ieee->softmac_stats.rx_auth_rq++;
+
+ status = auth_rq_parse(skb, dest);
+ if (status != -1) {
+ ieee80211_resp_to_auth(ieee, status, dest);
+ }
+ //DMESG("Dest is "MACSTR, MAC2STR(dest));
+
+}
+
+static inline void
+ieee80211_rx_assoc_rq(struct ieee80211_device *ieee, struct sk_buff *skb)
+{
+
+ u8 dest[ETH_ALEN];
+ //unsigned long flags;
+
+ ieee->softmac_stats.rx_ass_rq++;
+ if (assoc_rq_parse(skb, dest) != -1) {
+ ieee80211_resp_to_assoc_rq(ieee, dest);
+ }
+
+ printk(KERN_INFO"New client associated: %pM\n", dest);
+ //FIXME
+}
+
+static void ieee80211_sta_ps_send_null_frame(struct ieee80211_device *ieee,
+ short pwr)
+{
+
+ struct sk_buff *buf = ieee80211_null_func(ieee, pwr);
+
+ if (buf)
+ softmac_ps_mgmt_xmit(buf, ieee);
+
+}
+/* EXPORT_SYMBOL(ieee80211_sta_ps_send_null_frame); */
+
+static short ieee80211_sta_ps_sleep(struct ieee80211_device *ieee, u32 *time_h,
+ u32 *time_l)
+{
+ int timeout = ieee->ps_timeout;
+ u8 dtim;
+ /*if(ieee->ps == IEEE80211_PS_DISABLED ||
+ ieee->iw_mode != IW_MODE_INFRA ||
+ ieee->state != IEEE80211_LINKED)
+
+ return 0;
+ */
+ dtim = ieee->current_network.dtim_data;
+ //printk("DTIM\n");
+ if(!(dtim & IEEE80211_DTIM_VALID))
+ return 0;
+ timeout = ieee->current_network.beacon_interval; //should we use ps_timeout value or beacon_interval
+ //printk("VALID\n");
+ ieee->current_network.dtim_data = IEEE80211_DTIM_INVALID;
+
+ if(dtim & ((IEEE80211_DTIM_UCAST | IEEE80211_DTIM_MBCAST)& ieee->ps))
+ return 2;
+
+ if(!time_after(jiffies, ieee->dev->trans_start + MSECS(timeout)))
+ return 0;
+
+ if(!time_after(jiffies, ieee->last_rx_ps_time + MSECS(timeout)))
+ return 0;
+
+ if((ieee->softmac_features & IEEE_SOFTMAC_SINGLE_QUEUE ) &&
+ (ieee->mgmt_queue_tail != ieee->mgmt_queue_head))
+ return 0;
+
+ if (time_l) {
+ *time_l = ieee->current_network.last_dtim_sta_time[0]
+ + (ieee->current_network.beacon_interval
+ * ieee->current_network.dtim_period) * 1000;
+ }
+
+ if (time_h) {
+ *time_h = ieee->current_network.last_dtim_sta_time[1];
+ if(time_l && *time_l < ieee->current_network.last_dtim_sta_time[0])
+ *time_h += 1;
+ }
+
+ return 1;
+
+
+}
+
+static inline void ieee80211_sta_ps(struct ieee80211_device *ieee)
+{
+
+ u32 th, tl;
+ short sleep;
+
+ unsigned long flags, flags2;
+
+ spin_lock_irqsave(&ieee->lock, flags);
+
+ if ((ieee->ps == IEEE80211_PS_DISABLED ||
+ ieee->iw_mode != IW_MODE_INFRA ||
+ ieee->state != IEEE80211_LINKED)){
+
+ // #warning CHECK_LOCK_HERE
+ spin_lock_irqsave(&ieee->mgmt_tx_lock, flags2);
+
+ ieee80211_sta_wakeup(ieee, 1);
+
+ spin_unlock_irqrestore(&ieee->mgmt_tx_lock, flags2);
+ }
+
+ sleep = ieee80211_sta_ps_sleep(ieee,&th, &tl);
+ /* 2 wake, 1 sleep, 0 do nothing */
+ if(sleep == 0)
+ goto out;
+
+ if(sleep == 1){
+
+ if(ieee->sta_sleep == 1)
+ ieee->enter_sleep_state(ieee->dev, th, tl);
+
+ else if(ieee->sta_sleep == 0){
+ // printk("send null 1\n");
+ spin_lock_irqsave(&ieee->mgmt_tx_lock, flags2);
+
+ if(ieee->ps_is_queue_empty(ieee->dev)){
+
+
+ ieee->sta_sleep = 2;
+
+ ieee->ps_request_tx_ack(ieee->dev);
+
+ ieee80211_sta_ps_send_null_frame(ieee, 1);
+
+ ieee->ps_th = th;
+ ieee->ps_tl = tl;
+ }
+ spin_unlock_irqrestore(&ieee->mgmt_tx_lock, flags2);
+
+ }
+
+
+ }else if(sleep == 2){
+//#warning CHECK_LOCK_HERE
+ spin_lock_irqsave(&ieee->mgmt_tx_lock, flags2);
+
+ ieee80211_sta_wakeup(ieee, 1);
+
+ spin_unlock_irqrestore(&ieee->mgmt_tx_lock, flags2);
+ }
+
+out:
+ spin_unlock_irqrestore(&ieee->lock, flags);
+
+}
+
+void ieee80211_sta_wakeup(struct ieee80211_device *ieee, short nl)
+{
+ if (ieee->sta_sleep == 0) {
+ if (nl) {
+ printk("Warning: driver is probably failing to report TX ps error\n");
+ ieee->ps_request_tx_ack(ieee->dev);
+ ieee80211_sta_ps_send_null_frame(ieee, 0);
+ }
+ return;
+
+ }
+
+ if(ieee->sta_sleep == 1)
+ ieee->sta_wake_up(ieee->dev);
+
+ ieee->sta_sleep = 0;
+
+ if (nl) {
+ ieee->ps_request_tx_ack(ieee->dev);
+ ieee80211_sta_ps_send_null_frame(ieee, 0);
+ }
+}
+
+void ieee80211_ps_tx_ack(struct ieee80211_device *ieee, short success)
+{
+ unsigned long flags, flags2;
+
+ spin_lock_irqsave(&ieee->lock, flags);
+
+ if(ieee->sta_sleep == 2){
+ /* Null frame with PS bit set */
+ if (success) {
+ ieee->sta_sleep = 1;
+ ieee->enter_sleep_state(ieee->dev,ieee->ps_th,ieee->ps_tl);
+ }
+ /* if the card report not success we can't be sure the AP
+ * has not RXed so we can't assume the AP believe us awake
+ */
+ }
+ /* 21112005 - tx again null without PS bit if lost */
+ else {
+
+ if ((ieee->sta_sleep == 0) && !success) {
+ spin_lock_irqsave(&ieee->mgmt_tx_lock, flags2);
+ ieee80211_sta_ps_send_null_frame(ieee, 0);
+ spin_unlock_irqrestore(&ieee->mgmt_tx_lock, flags2);
+ }
+ }
+ spin_unlock_irqrestore(&ieee->lock, flags);
+}
+EXPORT_SYMBOL(ieee80211_ps_tx_ack);
+
+static void ieee80211_process_action(struct ieee80211_device *ieee,
+ struct sk_buff *skb)
+{
+ struct ieee80211_hdr *header = (struct ieee80211_hdr *)skb->data;
+ u8 *act = ieee80211_get_payload(header);
+ u8 tmp = 0;
+// IEEE80211_DEBUG_DATA(IEEE80211_DL_DATA|IEEE80211_DL_BA, skb->data, skb->len);
+ if (act == NULL)
+ {
+ IEEE80211_DEBUG(IEEE80211_DL_ERR, "error to get payload of action frame\n");
+ return;
+ }
+ tmp = *act;
+ act ++;
+ switch (tmp) {
+ case ACT_CAT_BA:
+ if (*act == ACT_ADDBAREQ)
+ ieee80211_rx_ADDBAReq(ieee, skb);
+ else if (*act == ACT_ADDBARSP)
+ ieee80211_rx_ADDBARsp(ieee, skb);
+ else if (*act == ACT_DELBA)
+ ieee80211_rx_DELBA(ieee, skb);
+ break;
+ default:
+ break;
+ }
+ return;
+
+}
+
+static void ieee80211_check_auth_response(struct ieee80211_device *ieee,
+ struct sk_buff *skb)
+{
+ /* default support N mode, disable halfNmode */
+ bool bSupportNmode = true, bHalfSupportNmode = false;
+ u16 errcode;
+ u8 *challenge;
+ int chlen = 0;
+ u32 iotAction;
+
+ errcode = auth_parse(skb, &challenge, &chlen);
+ if (!errcode) {
+ if (ieee->open_wep || !challenge) {
+ ieee->state = IEEE80211_ASSOCIATING_AUTHENTICATED;
+ ieee->softmac_stats.rx_auth_rs_ok++;
+ iotAction = ieee->pHTInfo->IOTAction;
+ if (!(iotAction & HT_IOT_ACT_PURE_N_MODE)) {
+ if (!ieee->GetNmodeSupportBySecCfg(ieee->dev)) {
+ /* WEP or TKIP encryption */
+ if (IsHTHalfNmodeAPs(ieee)) {
+ bSupportNmode = true;
+ bHalfSupportNmode = true;
+ } else {
+ bSupportNmode = false;
+ bHalfSupportNmode = false;
+ }
+ netdev_dbg(ieee->dev, "SEC(%d, %d)\n",
+ bSupportNmode,
+ bHalfSupportNmode);
+ }
+ }
+ /* Dummy wirless mode setting- avoid encryption issue */
+ if (bSupportNmode) {
+ /* N mode setting */
+ ieee->SetWirelessMode(ieee->dev,
+ ieee->current_network.mode);
+ } else {
+ /* b/g mode setting - TODO */
+ ieee->SetWirelessMode(ieee->dev, IEEE_G);
+ }
+
+ if (ieee->current_network.mode == IEEE_N_24G &&
+ bHalfSupportNmode == true) {
+ netdev_dbg(ieee->dev, "enter half N mode\n");
+ ieee->bHalfWirelessN24GMode = true;
+ } else
+ ieee->bHalfWirelessN24GMode = false;
+
+ ieee80211_associate_step2(ieee);
+ } else {
+ ieee80211_auth_challenge(ieee, challenge, chlen);
+ }
+ } else {
+ ieee->softmac_stats.rx_auth_rs_err++;
+ IEEE80211_DEBUG_MGMT("Auth response status code 0x%x", errcode);
+ ieee80211_associate_abort(ieee);
+ }
+}
+
+inline int
+ieee80211_rx_frame_softmac(struct ieee80211_device *ieee, struct sk_buff *skb,
+ struct ieee80211_rx_stats *rx_stats, u16 type,
+ u16 stype)
+{
+ struct ieee80211_hdr_3addr *header = (struct ieee80211_hdr_3addr *) skb->data;
+ u16 errcode;
+ int aid;
+ struct ieee80211_assoc_response_frame *assoc_resp;
+// struct ieee80211_info_element *info_element;
+
+ if(!ieee->proto_started)
+ return 0;
+
+ if(ieee->sta_sleep || (ieee->ps != IEEE80211_PS_DISABLED &&
+ ieee->iw_mode == IW_MODE_INFRA &&
+ ieee->state == IEEE80211_LINKED))
+
+ tasklet_schedule(&ieee->ps_task);
+
+ if(WLAN_FC_GET_STYPE(header->frame_ctl) != IEEE80211_STYPE_PROBE_RESP &&
+ WLAN_FC_GET_STYPE(header->frame_ctl) != IEEE80211_STYPE_BEACON)
+ ieee->last_rx_ps_time = jiffies;
+
+ switch (WLAN_FC_GET_STYPE(header->frame_ctl)) {
+
+ case IEEE80211_STYPE_ASSOC_RESP:
+ case IEEE80211_STYPE_REASSOC_RESP:
+
+ IEEE80211_DEBUG_MGMT("received [RE]ASSOCIATION RESPONSE (%d)\n",
+ WLAN_FC_GET_STYPE(header->frame_ctl));
+ if ((ieee->softmac_features & IEEE_SOFTMAC_ASSOCIATE) &&
+ ieee->state == IEEE80211_ASSOCIATING_AUTHENTICATED &&
+ ieee->iw_mode == IW_MODE_INFRA){
+ struct ieee80211_network network_resp;
+ struct ieee80211_network *network = &network_resp;
+
+ errcode = assoc_parse(ieee, skb, &aid);
+ if (!errcode) {
+ ieee->state=IEEE80211_LINKED;
+ ieee->assoc_id = aid;
+ ieee->softmac_stats.rx_ass_ok++;
+ /* station support qos */
+ /* Let the register setting defaultly with Legacy station */
+ if (ieee->qos_support) {
+ assoc_resp = (struct ieee80211_assoc_response_frame *)skb->data;
+ memset(network, 0, sizeof(*network));
+ if (ieee80211_parse_info_param(ieee,assoc_resp->info_element,\
+ rx_stats->len - sizeof(*assoc_resp),\
+ network,rx_stats)){
+ return 1;
+ }
+ else
+ { //filling the PeerHTCap. //maybe not necessary as we can get its info from current_network.
+ memcpy(ieee->pHTInfo->PeerHTCapBuf, network->bssht.bdHTCapBuf, network->bssht.bdHTCapLen);
+ memcpy(ieee->pHTInfo->PeerHTInfoBuf, network->bssht.bdHTInfoBuf, network->bssht.bdHTInfoLen);
+ }
+ if (ieee->handle_assoc_response != NULL)
+ ieee->handle_assoc_response(ieee->dev, (struct ieee80211_assoc_response_frame *)header, network);
+ }
+ ieee80211_associate_complete(ieee);
+ } else {
+ /* aid could not been allocated */
+ ieee->softmac_stats.rx_ass_err++;
+ printk(
+ "Association response status code 0x%x\n",
+ errcode);
+ IEEE80211_DEBUG_MGMT(
+ "Association response status code 0x%x\n",
+ errcode);
+ if(ieee->AsocRetryCount < RT_ASOC_RETRY_LIMIT) {
+ queue_work(ieee->wq, &ieee->associate_procedure_wq);
+ } else {
+ ieee80211_associate_abort(ieee);
+ }
+ }
+ }
+ break;
+
+ case IEEE80211_STYPE_ASSOC_REQ:
+ case IEEE80211_STYPE_REASSOC_REQ:
+
+ if ((ieee->softmac_features & IEEE_SOFTMAC_ASSOCIATE) &&
+ ieee->iw_mode == IW_MODE_MASTER)
+
+ ieee80211_rx_assoc_rq(ieee, skb);
+ break;
+
+ case IEEE80211_STYPE_AUTH:
+
+ if (ieee->softmac_features & IEEE_SOFTMAC_ASSOCIATE) {
+ if (ieee->state == IEEE80211_ASSOCIATING_AUTHENTICATING
+ && ieee->iw_mode == IW_MODE_INFRA) {
+
+ IEEE80211_DEBUG_MGMT("Received auth response");
+ ieee80211_check_auth_response(ieee, skb);
+ } else if (ieee->iw_mode == IW_MODE_MASTER) {
+ ieee80211_rx_auth_rq(ieee, skb);
+ }
+ }
+ break;
+
+ case IEEE80211_STYPE_PROBE_REQ:
+
+ if ((ieee->softmac_features & IEEE_SOFTMAC_PROBERS) &&
+ ((ieee->iw_mode == IW_MODE_ADHOC ||
+ ieee->iw_mode == IW_MODE_MASTER) &&
+ ieee->state == IEEE80211_LINKED)){
+ ieee80211_rx_probe_rq(ieee, skb);
+ }
+ break;
+
+ case IEEE80211_STYPE_DISASSOC:
+ case IEEE80211_STYPE_DEAUTH:
+ /* FIXME for now repeat all the association procedure
+ * both for disassociation and deauthentication
+ */
+ if ((ieee->softmac_features & IEEE_SOFTMAC_ASSOCIATE) &&
+ ieee->state == IEEE80211_LINKED &&
+ ieee->iw_mode == IW_MODE_INFRA){
+
+ ieee->state = IEEE80211_ASSOCIATING;
+ ieee->softmac_stats.reassoc++;
+
+ notify_wx_assoc_event(ieee);
+ //HTSetConnectBwMode(ieee, HT_CHANNEL_WIDTH_20, HT_EXTCHNL_OFFSET_NO_EXT);
+ RemovePeerTS(ieee, header->addr2);
+ queue_work(ieee->wq, &ieee->associate_procedure_wq);
+ }
+ break;
+ case IEEE80211_STYPE_MANAGE_ACT:
+ ieee80211_process_action(ieee, skb);
+ break;
+ default:
+ return -1;
+ }
+
+ //dev_kfree_skb_any(skb);
+ return 0;
+}
+
+/* The following are for a simpler TX queue management.
+ * Instead of using netif_[stop/wake]_queue, the driver
+ * will use these two functions (plus a reset one) that
+ * will internally call the kernel netif_* and take care
+ * of the ieee802.11 fragmentation.
+ * So, the driver receives a fragment at a time and might
+ * call the stop function when it wants, without taking
+ * care to have enough room to TX an entire packet.
+ * This might be useful if each fragment needs its own
+ * descriptor. Thus, just keeping a total free memory > than
+ * the max fragmentation threshold is not enough. If the
+ * ieee802.11 stack passed a TXB struct, then you would need
+ * to keep N free descriptors where
+ * N = MAX_PACKET_SIZE / MIN_FRAG_THRESHOLD.
+ * In this way you need just one and the 802.11 stack
+ * will take care of buffering fragments and pass them to
+ * to the driver later, when it wakes the queue.
+ */
+void ieee80211_softmac_xmit(struct ieee80211_txb *txb, struct ieee80211_device *ieee)
+{
+
+ unsigned int queue_index = txb->queue_index;
+ unsigned long flags;
+ int i;
+ cb_desc *tcb_desc = NULL;
+
+ spin_lock_irqsave(&ieee->lock, flags);
+
+ /* called with 2nd parm 0, no tx mgmt lock required */
+ ieee80211_sta_wakeup(ieee, 0);
+
+ /* update the tx status */
+ ieee->stats.tx_bytes += txb->payload_size;
+ ieee->stats.tx_packets++;
+ tcb_desc = (cb_desc *)(txb->fragments[0]->cb + MAX_DEV_ADDR_SIZE);
+ if (tcb_desc->bMulticast) {
+ ieee->stats.multicast++;
+ }
+ /* if xmit available, just xmit it immediately, else just insert it to the wait queue */
+ for(i = 0; i < txb->nr_frags; i++) {
+#ifdef USB_TX_DRIVER_AGGREGATION_ENABLE
+ if ((skb_queue_len(&ieee->skb_drv_aggQ[queue_index]) != 0) ||
+#else
+ if ((skb_queue_len(&ieee->skb_waitQ[queue_index]) != 0) ||
+#endif
+ (!ieee->check_nic_enough_desc(ieee->dev,queue_index))||\
+ (ieee->queue_stop)) {
+ /* insert the skb packet to the wait queue */
+ /* as for the completion function, it does not need
+ * to check it any more.
+ * */
+ //printk("error:no descriptor left@queue_index %d\n", queue_index);
+ //ieee80211_stop_queue(ieee);
+#ifdef USB_TX_DRIVER_AGGREGATION_ENABLE
+ skb_queue_tail(&ieee->skb_drv_aggQ[queue_index], txb->fragments[i]);
+#else
+ skb_queue_tail(&ieee->skb_waitQ[queue_index], txb->fragments[i]);
+#endif
+ }else{
+ ieee->softmac_data_hard_start_xmit(
+ txb->fragments[i],
+ ieee->dev, ieee->rate);
+ //ieee->stats.tx_packets++;
+ //ieee->stats.tx_bytes += txb->fragments[i]->len;
+ //ieee->dev->trans_start = jiffies;
+ }
+ }
+ ieee80211_txb_free(txb);
+
+//exit:
+ spin_unlock_irqrestore(&ieee->lock, flags);
+
+}
+EXPORT_SYMBOL(ieee80211_softmac_xmit);
+
+/* called with ieee->lock acquired */
+static void ieee80211_resume_tx(struct ieee80211_device *ieee)
+{
+ int i;
+ for(i = ieee->tx_pending.frag; i < ieee->tx_pending.txb->nr_frags; i++) {
+
+ if (ieee->queue_stop){
+ ieee->tx_pending.frag = i;
+ return;
+ }else{
+
+ ieee->softmac_data_hard_start_xmit(
+ ieee->tx_pending.txb->fragments[i],
+ ieee->dev, ieee->rate);
+ //(i+1)<ieee->tx_pending.txb->nr_frags);
+ ieee->stats.tx_packets++;
+ ieee->dev->trans_start = jiffies;
+ }
+ }
+
+
+ ieee80211_txb_free(ieee->tx_pending.txb);
+ ieee->tx_pending.txb = NULL;
+}
+
+
+void ieee80211_reset_queue(struct ieee80211_device *ieee)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&ieee->lock, flags);
+ init_mgmt_queue(ieee);
+ if (ieee->tx_pending.txb) {
+ ieee80211_txb_free(ieee->tx_pending.txb);
+ ieee->tx_pending.txb = NULL;
+ }
+ ieee->queue_stop = 0;
+ spin_unlock_irqrestore(&ieee->lock, flags);
+
+}
+EXPORT_SYMBOL(ieee80211_reset_queue);
+
+void ieee80211_wake_queue(struct ieee80211_device *ieee)
+{
+
+ unsigned long flags;
+ struct sk_buff *skb;
+ struct ieee80211_hdr_3addr *header;
+
+ spin_lock_irqsave(&ieee->lock, flags);
+ if (! ieee->queue_stop) goto exit;
+
+ ieee->queue_stop = 0;
+
+ if (ieee->softmac_features & IEEE_SOFTMAC_SINGLE_QUEUE) {
+ while (!ieee->queue_stop && (skb = dequeue_mgmt(ieee))){
+
+ header = (struct ieee80211_hdr_3addr *) skb->data;
+
+ header->seq_ctl = cpu_to_le16(ieee->seq_ctrl[0] << 4);
+
+ if (ieee->seq_ctrl[0] == 0xFFF)
+ ieee->seq_ctrl[0] = 0;
+ else
+ ieee->seq_ctrl[0]++;
+
+ ieee->softmac_data_hard_start_xmit(skb,ieee->dev,ieee->basic_rate);
+ //dev_kfree_skb_any(skb);//edit by thomas
+ }
+ }
+ if (!ieee->queue_stop && ieee->tx_pending.txb)
+ ieee80211_resume_tx(ieee);
+
+ if (!ieee->queue_stop && netif_queue_stopped(ieee->dev)) {
+ ieee->softmac_stats.swtxawake++;
+ netif_wake_queue(ieee->dev);
+ }
+
+exit :
+ spin_unlock_irqrestore(&ieee->lock, flags);
+}
+EXPORT_SYMBOL(ieee80211_wake_queue);
+
+void ieee80211_stop_queue(struct ieee80211_device *ieee)
+{
+ //unsigned long flags;
+ //spin_lock_irqsave(&ieee->lock,flags);
+
+ if (!netif_queue_stopped(ieee->dev)) {
+ netif_stop_queue(ieee->dev);
+ ieee->softmac_stats.swtxstop++;
+ }
+ ieee->queue_stop = 1;
+ //spin_unlock_irqrestore(&ieee->lock,flags);
+
+}
+EXPORT_SYMBOL(ieee80211_stop_queue);
+
+inline void ieee80211_randomize_cell(struct ieee80211_device *ieee)
+{
+
+ random_ether_addr(ieee->current_network.bssid);
+}
+
+/* called in user context only */
+void ieee80211_start_master_bss(struct ieee80211_device *ieee)
+{
+ ieee->assoc_id = 1;
+
+ if (ieee->current_network.ssid_len == 0) {
+ strncpy(ieee->current_network.ssid,
+ IEEE80211_DEFAULT_TX_ESSID,
+ IW_ESSID_MAX_SIZE);
+
+ ieee->current_network.ssid_len = strlen(IEEE80211_DEFAULT_TX_ESSID);
+ ieee->ssid_set = 1;
+ }
+
+ memcpy(ieee->current_network.bssid, ieee->dev->dev_addr, ETH_ALEN);
+
+ ieee->set_chan(ieee->dev, ieee->current_network.channel);
+ ieee->state = IEEE80211_LINKED;
+ ieee->link_change(ieee->dev);
+ notify_wx_assoc_event(ieee);
+
+ if (ieee->data_hard_resume)
+ ieee->data_hard_resume(ieee->dev);
+
+ netif_carrier_on(ieee->dev);
+}
+
+static void ieee80211_start_monitor_mode(struct ieee80211_device *ieee)
+{
+ if (ieee->raw_tx) {
+
+ if (ieee->data_hard_resume)
+ ieee->data_hard_resume(ieee->dev);
+
+ netif_carrier_on(ieee->dev);
+ }
+}
+static void ieee80211_start_ibss_wq(struct work_struct *work)
+{
+
+ struct delayed_work *dwork = container_of(work, struct delayed_work, work);
+ struct ieee80211_device *ieee = container_of(dwork, struct ieee80211_device, start_ibss_wq);
+ /* iwconfig mode ad-hoc will schedule this and return
+ * on the other hand this will block further iwconfig SET
+ * operations because of the wx_sem hold.
+ * Anyway some most set operations set a flag to speed-up
+ * (abort) this wq (when syncro scanning) before sleeping
+ * on the semaphore
+ */
+ if (!ieee->proto_started) {
+ printk("==========oh driver down return\n");
+ return;
+ }
+ down(&ieee->wx_sem);
+
+ if (ieee->current_network.ssid_len == 0) {
+ strcpy(ieee->current_network.ssid, IEEE80211_DEFAULT_TX_ESSID);
+ ieee->current_network.ssid_len = strlen(IEEE80211_DEFAULT_TX_ESSID);
+ ieee->ssid_set = 1;
+ }
+
+ /* check if we have this cell in our network list */
+ ieee80211_softmac_check_all_nets(ieee);
+
+
+// if((IS_DOT11D_ENABLE(ieee)) && (ieee->state == IEEE80211_NOLINK))
+ if (ieee->state == IEEE80211_NOLINK)
+ ieee->current_network.channel = 6;
+ /* if not then the state is not linked. Maybe the user swithced to
+ * ad-hoc mode just after being in monitor mode, or just after
+ * being very few time in managed mode (so the card have had no
+ * time to scan all the chans..) or we have just run up the iface
+ * after setting ad-hoc mode. So we have to give another try..
+ * Here, in ibss mode, should be safe to do this without extra care
+ * (in bss mode we had to make sure no-one tryed to associate when
+ * we had just checked the ieee->state and we was going to start the
+ * scan) beacause in ibss mode the ieee80211_new_net function, when
+ * finds a good net, just set the ieee->state to IEEE80211_LINKED,
+ * so, at worst, we waste a bit of time to initiate an unneeded syncro
+ * scan, that will stop at the first round because it sees the state
+ * associated.
+ */
+ if (ieee->state == IEEE80211_NOLINK)
+ ieee80211_start_scan_syncro(ieee);
+
+ /* the network definitively is not here.. create a new cell */
+ if (ieee->state == IEEE80211_NOLINK) {
+ printk("creating new IBSS cell\n");
+ if(!ieee->wap_set)
+ ieee80211_randomize_cell(ieee);
+
+ if(ieee->modulation & IEEE80211_CCK_MODULATION){
+
+ ieee->current_network.rates_len = 4;
+
+ ieee->current_network.rates[0] = IEEE80211_BASIC_RATE_MASK | IEEE80211_CCK_RATE_1MB;
+ ieee->current_network.rates[1] = IEEE80211_BASIC_RATE_MASK | IEEE80211_CCK_RATE_2MB;
+ ieee->current_network.rates[2] = IEEE80211_BASIC_RATE_MASK | IEEE80211_CCK_RATE_5MB;
+ ieee->current_network.rates[3] = IEEE80211_BASIC_RATE_MASK | IEEE80211_CCK_RATE_11MB;
+
+ }else
+ ieee->current_network.rates_len = 0;
+
+ if(ieee->modulation & IEEE80211_OFDM_MODULATION){
+ ieee->current_network.rates_ex_len = 8;
+
+ ieee->current_network.rates_ex[0] = IEEE80211_BASIC_RATE_MASK | IEEE80211_OFDM_RATE_6MB;
+ ieee->current_network.rates_ex[1] = IEEE80211_BASIC_RATE_MASK | IEEE80211_OFDM_RATE_9MB;
+ ieee->current_network.rates_ex[2] = IEEE80211_BASIC_RATE_MASK | IEEE80211_OFDM_RATE_12MB;
+ ieee->current_network.rates_ex[3] = IEEE80211_BASIC_RATE_MASK | IEEE80211_OFDM_RATE_18MB;
+ ieee->current_network.rates_ex[4] = IEEE80211_BASIC_RATE_MASK | IEEE80211_OFDM_RATE_24MB;
+ ieee->current_network.rates_ex[5] = IEEE80211_BASIC_RATE_MASK | IEEE80211_OFDM_RATE_36MB;
+ ieee->current_network.rates_ex[6] = IEEE80211_BASIC_RATE_MASK | IEEE80211_OFDM_RATE_48MB;
+ ieee->current_network.rates_ex[7] = IEEE80211_BASIC_RATE_MASK | IEEE80211_OFDM_RATE_54MB;
+
+ ieee->rate = 108;
+ }else{
+ ieee->current_network.rates_ex_len = 0;
+ ieee->rate = 22;
+ }
+
+ // By default, WMM function will be disabled in IBSS mode
+ ieee->current_network.QoS_Enable = 0;
+ ieee->SetWirelessMode(ieee->dev, IEEE_G);
+ ieee->current_network.atim_window = 0;
+ ieee->current_network.capability = WLAN_CAPABILITY_IBSS;
+ if(ieee->short_slot)
+ ieee->current_network.capability |= WLAN_CAPABILITY_SHORT_SLOT;
+
+ }
+
+ ieee->state = IEEE80211_LINKED;
+
+ ieee->set_chan(ieee->dev, ieee->current_network.channel);
+ ieee->link_change(ieee->dev);
+
+ notify_wx_assoc_event(ieee);
+
+ ieee80211_start_send_beacons(ieee);
+
+ if (ieee->data_hard_resume)
+ ieee->data_hard_resume(ieee->dev);
+ netif_carrier_on(ieee->dev);
+
+ up(&ieee->wx_sem);
+}
+
+inline void ieee80211_start_ibss(struct ieee80211_device *ieee)
+{
+ queue_delayed_work(ieee->wq, &ieee->start_ibss_wq, 150);
+}
+
+/* this is called only in user context, with wx_sem held */
+void ieee80211_start_bss(struct ieee80211_device *ieee)
+{
+ unsigned long flags;
+ //
+ // Ref: 802.11d 11.1.3.3
+ // STA shall not start a BSS unless properly formed Beacon frame including a Country IE.
+ //
+ if (IS_DOT11D_ENABLE(ieee) && !IS_COUNTRY_IE_VALID(ieee))
+ {
+ if (! ieee->bGlobalDomain)
+ {
+ return;
+ }
+ }
+ /* check if we have already found the net we
+ * are interested in (if any).
+ * if not (we are disassociated and we are not
+ * in associating / authenticating phase) start the background scanning.
+ */
+ ieee80211_softmac_check_all_nets(ieee);
+
+ /* ensure no-one start an associating process (thus setting
+ * the ieee->state to ieee80211_ASSOCIATING) while we
+ * have just cheked it and we are going to enable scan.
+ * The ieee80211_new_net function is always called with
+ * lock held (from both ieee80211_softmac_check_all_nets and
+ * the rx path), so we cannot be in the middle of such function
+ */
+ spin_lock_irqsave(&ieee->lock, flags);
+
+ if (ieee->state == IEEE80211_NOLINK) {
+ ieee->actscanning = true;
+ ieee80211_start_scan(ieee);
+ }
+ spin_unlock_irqrestore(&ieee->lock, flags);
+}
+
+/* called only in userspace context */
+void ieee80211_disassociate(struct ieee80211_device *ieee)
+{
+
+
+ netif_carrier_off(ieee->dev);
+ if (ieee->softmac_features & IEEE_SOFTMAC_TX_QUEUE)
+ ieee80211_reset_queue(ieee);
+
+ if (ieee->data_hard_stop)
+ ieee->data_hard_stop(ieee->dev);
+ if(IS_DOT11D_ENABLE(ieee))
+ Dot11d_Reset(ieee);
+ ieee->state = IEEE80211_NOLINK;
+ ieee->is_set_key = false;
+ ieee->link_change(ieee->dev);
+ //HTSetConnectBwMode(ieee, HT_CHANNEL_WIDTH_20, HT_EXTCHNL_OFFSET_NO_EXT);
+ notify_wx_assoc_event(ieee);
+
+}
+EXPORT_SYMBOL(ieee80211_disassociate);
+
+static void ieee80211_associate_retry_wq(struct work_struct *work)
+{
+ struct delayed_work *dwork = container_of(work, struct delayed_work, work);
+ struct ieee80211_device *ieee = container_of(dwork, struct ieee80211_device, associate_retry_wq);
+ unsigned long flags;
+
+ down(&ieee->wx_sem);
+ if(!ieee->proto_started)
+ goto exit;
+
+ if(ieee->state != IEEE80211_ASSOCIATING_RETRY)
+ goto exit;
+
+ /* until we do not set the state to IEEE80211_NOLINK
+ * there are no possibility to have someone else trying
+ * to start an association procedure (we get here with
+ * ieee->state = IEEE80211_ASSOCIATING).
+ * When we set the state to IEEE80211_NOLINK it is possible
+ * that the RX path run an attempt to associate, but
+ * both ieee80211_softmac_check_all_nets and the
+ * RX path works with ieee->lock held so there are no
+ * problems. If we are still disassociated then start a scan.
+ * the lock here is necessary to ensure no one try to start
+ * an association procedure when we have just checked the
+ * state and we are going to start the scan.
+ */
+ ieee->state = IEEE80211_NOLINK;
+
+ ieee80211_softmac_check_all_nets(ieee);
+
+ spin_lock_irqsave(&ieee->lock, flags);
+
+ if(ieee->state == IEEE80211_NOLINK)
+ ieee80211_start_scan(ieee);
+
+ spin_unlock_irqrestore(&ieee->lock, flags);
+
+exit:
+ up(&ieee->wx_sem);
+}
+
+struct sk_buff *ieee80211_get_beacon_(struct ieee80211_device *ieee)
+{
+ u8 broadcast_addr[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
+
+ struct sk_buff *skb;
+ struct ieee80211_probe_response *b;
+
+ skb = ieee80211_probe_resp(ieee, broadcast_addr);
+
+ if (!skb)
+ return NULL;
+
+ b = (struct ieee80211_probe_response *) skb->data;
+ b->header.frame_ctl = cpu_to_le16(IEEE80211_STYPE_BEACON);
+
+ return skb;
+
+}
+
+struct sk_buff *ieee80211_get_beacon(struct ieee80211_device *ieee)
+{
+ struct sk_buff *skb;
+ struct ieee80211_probe_response *b;
+
+ skb = ieee80211_get_beacon_(ieee);
+ if(!skb)
+ return NULL;
+
+ b = (struct ieee80211_probe_response *) skb->data;
+ b->header.seq_ctl = cpu_to_le16(ieee->seq_ctrl[0] << 4);
+
+ if (ieee->seq_ctrl[0] == 0xFFF)
+ ieee->seq_ctrl[0] = 0;
+ else
+ ieee->seq_ctrl[0]++;
+
+ return skb;
+}
+EXPORT_SYMBOL(ieee80211_get_beacon);
+
+void ieee80211_softmac_stop_protocol(struct ieee80211_device *ieee)
+{
+ ieee->sync_scan_hurryup = 1;
+ down(&ieee->wx_sem);
+ ieee80211_stop_protocol(ieee);
+ up(&ieee->wx_sem);
+}
+EXPORT_SYMBOL(ieee80211_softmac_stop_protocol);
+
+void ieee80211_stop_protocol(struct ieee80211_device *ieee)
+{
+ if (!ieee->proto_started)
+ return;
+
+ ieee->proto_started = 0;
+
+ ieee80211_stop_send_beacons(ieee);
+ del_timer_sync(&ieee->associate_timer);
+ cancel_delayed_work(&ieee->associate_retry_wq);
+ cancel_delayed_work(&ieee->start_ibss_wq);
+ ieee80211_stop_scan(ieee);
+
+ ieee80211_disassociate(ieee);
+ RemoveAllTS(ieee); //added as we disconnect from the previous BSS, Remove all TS
+}
+
+void ieee80211_softmac_start_protocol(struct ieee80211_device *ieee)
+{
+ ieee->sync_scan_hurryup = 0;
+ down(&ieee->wx_sem);
+ ieee80211_start_protocol(ieee);
+ up(&ieee->wx_sem);
+}
+EXPORT_SYMBOL(ieee80211_softmac_start_protocol);
+
+void ieee80211_start_protocol(struct ieee80211_device *ieee)
+{
+ short ch = 0;
+ int i = 0;
+ if (ieee->proto_started)
+ return;
+
+ ieee->proto_started = 1;
+
+ if (ieee->current_network.channel == 0) {
+ do{
+ ch++;
+ if (ch > MAX_CHANNEL_NUMBER)
+ return; /* no channel found */
+ }while(!GET_DOT11D_INFO(ieee)->channel_map[ch]);
+ ieee->current_network.channel = ch;
+ }
+
+ if (ieee->current_network.beacon_interval == 0)
+ ieee->current_network.beacon_interval = 100;
+// printk("===>%s(), chan:%d\n", __func__, ieee->current_network.channel);
+// ieee->set_chan(ieee->dev,ieee->current_network.channel);
+
+ for(i = 0; i < 17; i++) {
+ ieee->last_rxseq_num[i] = -1;
+ ieee->last_rxfrag_num[i] = -1;
+ ieee->last_packet_time[i] = 0;
+ }
+
+ ieee->init_wmmparam_flag = 0;//reinitialize AC_xx_PARAM registers.
+
+
+ /* if the user set the MAC of the ad-hoc cell and then
+ * switch to managed mode, shall we make sure that association
+ * attempts does not fail just because the user provide the essid
+ * and the nic is still checking for the AP MAC ??
+ */
+ if (ieee->iw_mode == IW_MODE_INFRA)
+ ieee80211_start_bss(ieee);
+
+ else if (ieee->iw_mode == IW_MODE_ADHOC)
+ ieee80211_start_ibss(ieee);
+
+ else if (ieee->iw_mode == IW_MODE_MASTER)
+ ieee80211_start_master_bss(ieee);
+
+ else if(ieee->iw_mode == IW_MODE_MONITOR)
+ ieee80211_start_monitor_mode(ieee);
+}
+
+
+#define DRV_NAME "Ieee80211"
+void ieee80211_softmac_init(struct ieee80211_device *ieee)
+{
+ int i;
+ memset(&ieee->current_network, 0, sizeof(struct ieee80211_network));
+
+ ieee->state = IEEE80211_NOLINK;
+ ieee->sync_scan_hurryup = 0;
+ for(i = 0; i < 5; i++) {
+ ieee->seq_ctrl[i] = 0;
+ }
+ ieee->pDot11dInfo = kzalloc(sizeof(RT_DOT11D_INFO), GFP_ATOMIC);
+ if (!ieee->pDot11dInfo)
+ IEEE80211_DEBUG(IEEE80211_DL_ERR, "can't alloc memory for DOT11D\n");
+ //added for AP roaming
+ ieee->LinkDetectInfo.SlotNum = 2;
+ ieee->LinkDetectInfo.NumRecvBcnInPeriod=0;
+ ieee->LinkDetectInfo.NumRecvDataInPeriod=0;
+
+ ieee->assoc_id = 0;
+ ieee->queue_stop = 0;
+ ieee->scanning = 0;
+ ieee->softmac_features = 0; //so IEEE2100-like driver are happy
+ ieee->wap_set = 0;
+ ieee->ssid_set = 0;
+ ieee->proto_started = 0;
+ ieee->basic_rate = IEEE80211_DEFAULT_BASIC_RATE;
+ ieee->rate = 22;
+ ieee->ps = IEEE80211_PS_DISABLED;
+ ieee->sta_sleep = 0;
+ ieee->Regdot11HTOperationalRateSet[0]= 0xff;//support MCS 0~7
+ ieee->Regdot11HTOperationalRateSet[1]= 0xff;//support MCS 8~15
+ ieee->Regdot11HTOperationalRateSet[4]= 0x01;
+ //added by amy
+ ieee->actscanning = false;
+ ieee->beinretry = false;
+ ieee->is_set_key = false;
+ init_mgmt_queue(ieee);
+
+ ieee->sta_edca_param[0] = 0x0000A403;
+ ieee->sta_edca_param[1] = 0x0000A427;
+ ieee->sta_edca_param[2] = 0x005E4342;
+ ieee->sta_edca_param[3] = 0x002F3262;
+ ieee->aggregation = true;
+ ieee->enable_rx_imm_BA = true;
+ ieee->tx_pending.txb = NULL;
+
+ setup_timer(&ieee->associate_timer, ieee80211_associate_abort_cb,
+ (unsigned long)ieee);
+
+ setup_timer(&ieee->beacon_timer, ieee80211_send_beacon_cb,
+ (unsigned long)ieee);
+
+ ieee->wq = create_workqueue(DRV_NAME);
+
+ INIT_DELAYED_WORK(&ieee->start_ibss_wq, ieee80211_start_ibss_wq);
+ INIT_WORK(&ieee->associate_complete_wq, ieee80211_associate_complete_wq);
+ INIT_WORK(&ieee->associate_procedure_wq, ieee80211_associate_procedure_wq);
+ INIT_DELAYED_WORK(&ieee->softmac_scan_wq, ieee80211_softmac_scan_wq);
+ INIT_DELAYED_WORK(&ieee->associate_retry_wq, ieee80211_associate_retry_wq);
+ INIT_WORK(&ieee->wx_sync_scan_wq, ieee80211_wx_sync_scan_wq);
+
+
+ sema_init(&ieee->wx_sem, 1);
+ sema_init(&ieee->scan_sem, 1);
+
+ spin_lock_init(&ieee->mgmt_tx_lock);
+ spin_lock_init(&ieee->beacon_lock);
+
+ tasklet_init(&ieee->ps_task,
+ (void(*)(unsigned long)) ieee80211_sta_ps,
+ (unsigned long)ieee);
+
+}
+
+void ieee80211_softmac_free(struct ieee80211_device *ieee)
+{
+ down(&ieee->wx_sem);
+ kfree(ieee->pDot11dInfo);
+ ieee->pDot11dInfo = NULL;
+ del_timer_sync(&ieee->associate_timer);
+
+ cancel_delayed_work(&ieee->associate_retry_wq);
+ destroy_workqueue(ieee->wq);
+
+ up(&ieee->wx_sem);
+}
+
+/********************************************************
+ * Start of WPA code. *
+ * this is stolen from the ipw2200 driver *
+ ********************************************************/
+
+
+static int ieee80211_wpa_enable(struct ieee80211_device *ieee, int value)
+{
+ /* This is called when wpa_supplicant loads and closes the driver
+ * interface. */
+ printk("%s WPA\n",value ? "enabling" : "disabling");
+ ieee->wpa_enabled = value;
+ return 0;
+}
+
+
+static void ieee80211_wpa_assoc_frame(struct ieee80211_device *ieee,
+ char *wpa_ie, int wpa_ie_len)
+{
+ /* make sure WPA is enabled */
+ ieee80211_wpa_enable(ieee, 1);
+
+ ieee80211_disassociate(ieee);
+}
+
+
+static int ieee80211_wpa_mlme(struct ieee80211_device *ieee, int command, int reason)
+{
+
+ int ret = 0;
+
+ switch (command) {
+ case IEEE_MLME_STA_DEAUTH:
+ // silently ignore
+ break;
+
+ case IEEE_MLME_STA_DISASSOC:
+ ieee80211_disassociate(ieee);
+ break;
+
+ default:
+ printk("Unknown MLME request: %d\n", command);
+ ret = -EOPNOTSUPP;
+ }
+
+ return ret;
+}
+
+
+static int ieee80211_wpa_set_wpa_ie(struct ieee80211_device *ieee,
+ struct ieee_param *param, int plen)
+{
+ u8 *buf;
+
+ if (param->u.wpa_ie.len > MAX_WPA_IE_LEN ||
+ (param->u.wpa_ie.len && param->u.wpa_ie.data == NULL))
+ return -EINVAL;
+
+ if (param->u.wpa_ie.len) {
+ buf = kmemdup(param->u.wpa_ie.data, param->u.wpa_ie.len,
+ GFP_KERNEL);
+ if (buf == NULL)
+ return -ENOMEM;
+
+ kfree(ieee->wpa_ie);
+ ieee->wpa_ie = buf;
+ ieee->wpa_ie_len = param->u.wpa_ie.len;
+ } else {
+ kfree(ieee->wpa_ie);
+ ieee->wpa_ie = NULL;
+ ieee->wpa_ie_len = 0;
+ }
+
+ ieee80211_wpa_assoc_frame(ieee, ieee->wpa_ie, ieee->wpa_ie_len);
+ return 0;
+}
+
+#define AUTH_ALG_OPEN_SYSTEM 0x1
+#define AUTH_ALG_SHARED_KEY 0x2
+
+static int ieee80211_wpa_set_auth_algs(struct ieee80211_device *ieee, int value)
+{
+
+ struct ieee80211_security sec = {
+ .flags = SEC_AUTH_MODE,
+ };
+
+ if (value & AUTH_ALG_SHARED_KEY) {
+ sec.auth_mode = WLAN_AUTH_SHARED_KEY;
+ ieee->open_wep = 0;
+ ieee->auth_mode = 1;
+ } else if (value & AUTH_ALG_OPEN_SYSTEM){
+ sec.auth_mode = WLAN_AUTH_OPEN;
+ ieee->open_wep = 1;
+ ieee->auth_mode = 0;
+ }
+ else if (value & IW_AUTH_ALG_LEAP){
+ sec.auth_mode = WLAN_AUTH_LEAP;
+ ieee->open_wep = 1;
+ ieee->auth_mode = 2;
+ }
+
+
+ if (ieee->set_security)
+ ieee->set_security(ieee->dev, &sec);
+ //else
+ // ret = -EOPNOTSUPP;
+
+ return 0;
+}
+
+static int ieee80211_wpa_set_param(struct ieee80211_device *ieee, u8 name, u32 value)
+{
+ int ret=0;
+ unsigned long flags;
+
+ switch (name) {
+ case IEEE_PARAM_WPA_ENABLED:
+ ret = ieee80211_wpa_enable(ieee, value);
+ break;
+
+ case IEEE_PARAM_TKIP_COUNTERMEASURES:
+ ieee->tkip_countermeasures=value;
+ break;
+
+ case IEEE_PARAM_DROP_UNENCRYPTED: {
+ /* HACK:
+ *
+ * wpa_supplicant calls set_wpa_enabled when the driver
+ * is loaded and unloaded, regardless of if WPA is being
+ * used. No other calls are made which can be used to
+ * determine if encryption will be used or not prior to
+ * association being expected. If encryption is not being
+ * used, drop_unencrypted is set to false, else true -- we
+ * can use this to determine if the CAP_PRIVACY_ON bit should
+ * be set.
+ */
+ struct ieee80211_security sec = {
+ .flags = SEC_ENABLED,
+ .enabled = value,
+ };
+ ieee->drop_unencrypted = value;
+ /* We only change SEC_LEVEL for open mode. Others
+ * are set by ipw_wpa_set_encryption.
+ */
+ if (!value) {
+ sec.flags |= SEC_LEVEL;
+ sec.level = SEC_LEVEL_0;
+ }
+ else {
+ sec.flags |= SEC_LEVEL;
+ sec.level = SEC_LEVEL_1;
+ }
+ if (ieee->set_security)
+ ieee->set_security(ieee->dev, &sec);
+ break;
+ }
+
+ case IEEE_PARAM_PRIVACY_INVOKED:
+ ieee->privacy_invoked=value;
+ break;
+
+ case IEEE_PARAM_AUTH_ALGS:
+ ret = ieee80211_wpa_set_auth_algs(ieee, value);
+ break;
+
+ case IEEE_PARAM_IEEE_802_1X:
+ ieee->ieee802_1x=value;
+ break;
+ case IEEE_PARAM_WPAX_SELECT:
+ // added for WPA2 mixed mode
+ spin_lock_irqsave(&ieee->wpax_suitlist_lock, flags);
+ ieee->wpax_type_set = 1;
+ ieee->wpax_type_notify = value;
+ spin_unlock_irqrestore(&ieee->wpax_suitlist_lock, flags);
+ break;
+
+ default:
+ printk("Unknown WPA param: %d\n",name);
+ ret = -EOPNOTSUPP;
+ }
+
+ return ret;
+}
+
+/* implementation borrowed from hostap driver */
+
+static int ieee80211_wpa_set_encryption(struct ieee80211_device *ieee,
+ struct ieee_param *param, int param_len)
+{
+ int ret = 0;
+
+ struct ieee80211_crypto_ops *ops;
+ struct ieee80211_crypt_data **crypt;
+
+ struct ieee80211_security sec = {
+ .flags = 0,
+ };
+
+ param->u.crypt.err = 0;
+ param->u.crypt.alg[IEEE_CRYPT_ALG_NAME_LEN - 1] = '\0';
+
+ if (param_len !=
+ (int) ((char *) param->u.crypt.key - (char *) param) +
+ param->u.crypt.key_len) {
+ printk("Len mismatch %d, %d\n", param_len,
+ param->u.crypt.key_len);
+ return -EINVAL;
+ }
+ if (is_broadcast_ether_addr(param->sta_addr)) {
+ if (param->u.crypt.idx >= WEP_KEYS)
+ return -EINVAL;
+ crypt = &ieee->crypt[param->u.crypt.idx];
+ } else {
+ return -EINVAL;
+ }
+
+ if (strcmp(param->u.crypt.alg, "none") == 0) {
+ if (crypt) {
+ sec.enabled = 0;
+ // FIXME FIXME
+ //sec.encrypt = 0;
+ sec.level = SEC_LEVEL_0;
+ sec.flags |= SEC_ENABLED | SEC_LEVEL;
+ ieee80211_crypt_delayed_deinit(ieee, crypt);
+ }
+ goto done;
+ }
+ sec.enabled = 1;
+// FIXME FIXME
+// sec.encrypt = 1;
+ sec.flags |= SEC_ENABLED;
+
+ /* IPW HW cannot build TKIP MIC, host decryption still needed. */
+ if (!(ieee->host_encrypt || ieee->host_decrypt) &&
+ strcmp(param->u.crypt.alg, "TKIP"))
+ goto skip_host_crypt;
+
+ ops = ieee80211_get_crypto_ops(param->u.crypt.alg);
+ if (ops == NULL && strcmp(param->u.crypt.alg, "WEP") == 0) {
+ request_module("ieee80211_crypt_wep");
+ ops = ieee80211_get_crypto_ops(param->u.crypt.alg);
+ //set WEP40 first, it will be modified according to WEP104 or WEP40 at other place
+ } else if (ops == NULL && strcmp(param->u.crypt.alg, "TKIP") == 0) {
+ request_module("ieee80211_crypt_tkip");
+ ops = ieee80211_get_crypto_ops(param->u.crypt.alg);
+ } else if (ops == NULL && strcmp(param->u.crypt.alg, "CCMP") == 0) {
+ request_module("ieee80211_crypt_ccmp");
+ ops = ieee80211_get_crypto_ops(param->u.crypt.alg);
+ }
+ if (ops == NULL) {
+ printk("unknown crypto alg '%s'\n", param->u.crypt.alg);
+ param->u.crypt.err = IEEE_CRYPT_ERR_UNKNOWN_ALG;
+ ret = -EINVAL;
+ goto done;
+ }
+
+ if (*crypt == NULL || (*crypt)->ops != ops) {
+ struct ieee80211_crypt_data *new_crypt;
+
+ ieee80211_crypt_delayed_deinit(ieee, crypt);
+
+ new_crypt = kzalloc(sizeof(*new_crypt), GFP_KERNEL);
+ if (new_crypt == NULL) {
+ ret = -ENOMEM;
+ goto done;
+ }
+ new_crypt->ops = ops;
+ if (new_crypt->ops && try_module_get(new_crypt->ops->owner))
+ new_crypt->priv =
+ new_crypt->ops->init(param->u.crypt.idx);
+
+ if (new_crypt->priv == NULL) {
+ kfree(new_crypt);
+ param->u.crypt.err = IEEE_CRYPT_ERR_CRYPT_INIT_FAILED;
+ ret = -EINVAL;
+ goto done;
+ }
+
+ *crypt = new_crypt;
+ }
+
+ if (param->u.crypt.key_len > 0 && (*crypt)->ops->set_key &&
+ (*crypt)->ops->set_key(param->u.crypt.key,
+ param->u.crypt.key_len, param->u.crypt.seq,
+ (*crypt)->priv) < 0) {
+ printk("key setting failed\n");
+ param->u.crypt.err = IEEE_CRYPT_ERR_KEY_SET_FAILED;
+ ret = -EINVAL;
+ goto done;
+ }
+
+ skip_host_crypt:
+ if (param->u.crypt.set_tx) {
+ ieee->tx_keyidx = param->u.crypt.idx;
+ sec.active_key = param->u.crypt.idx;
+ sec.flags |= SEC_ACTIVE_KEY;
+ } else
+ sec.flags &= ~SEC_ACTIVE_KEY;
+
+ if (param->u.crypt.alg != NULL) {
+ memcpy(sec.keys[param->u.crypt.idx],
+ param->u.crypt.key,
+ param->u.crypt.key_len);
+ sec.key_sizes[param->u.crypt.idx] = param->u.crypt.key_len;
+ sec.flags |= (1 << param->u.crypt.idx);
+
+ if (strcmp(param->u.crypt.alg, "WEP") == 0) {
+ sec.flags |= SEC_LEVEL;
+ sec.level = SEC_LEVEL_1;
+ } else if (strcmp(param->u.crypt.alg, "TKIP") == 0) {
+ sec.flags |= SEC_LEVEL;
+ sec.level = SEC_LEVEL_2;
+ } else if (strcmp(param->u.crypt.alg, "CCMP") == 0) {
+ sec.flags |= SEC_LEVEL;
+ sec.level = SEC_LEVEL_3;
+ }
+ }
+ done:
+ if (ieee->set_security)
+ ieee->set_security(ieee->dev, &sec);
+
+ /* Do not reset port if card is in Managed mode since resetting will
+ * generate new IEEE 802.11 authentication which may end up in looping
+ * with IEEE 802.1X. If your hardware requires a reset after WEP
+ * configuration (for example... Prism2), implement the reset_port in
+ * the callbacks structures used to initialize the 802.11 stack. */
+ if (ieee->reset_on_keychange &&
+ ieee->iw_mode != IW_MODE_INFRA &&
+ ieee->reset_port &&
+ ieee->reset_port(ieee->dev)) {
+ printk("reset_port failed\n");
+ param->u.crypt.err = IEEE_CRYPT_ERR_CARD_CONF_FAILED;
+ return -EINVAL;
+ }
+
+ return ret;
+}
+
+inline struct sk_buff *ieee80211_disassociate_skb(
+ struct ieee80211_network *beacon,
+ struct ieee80211_device *ieee,
+ u8 asRsn)
+{
+ struct sk_buff *skb;
+ struct ieee80211_disassoc *disass;
+
+ skb = dev_alloc_skb(sizeof(struct ieee80211_disassoc));
+ if (!skb)
+ return NULL;
+
+ disass = (struct ieee80211_disassoc *) skb_put(skb,sizeof(struct ieee80211_disassoc));
+ disass->header.frame_ctl = cpu_to_le16(IEEE80211_STYPE_DISASSOC);
+ disass->header.duration_id = 0;
+
+ memcpy(disass->header.addr1, beacon->bssid, ETH_ALEN);
+ memcpy(disass->header.addr2, ieee->dev->dev_addr, ETH_ALEN);
+ memcpy(disass->header.addr3, beacon->bssid, ETH_ALEN);
+
+ disass->reason = asRsn;
+ return skb;
+}
+
+
+void
+SendDisassociation(
+ struct ieee80211_device *ieee,
+ u8 *asSta,
+ u8 asRsn
+)
+{
+ struct ieee80211_network *beacon = &ieee->current_network;
+ struct sk_buff *skb;
+ skb = ieee80211_disassociate_skb(beacon,ieee,asRsn);
+ if (skb) {
+ softmac_mgmt_xmit(skb, ieee);
+ //dev_kfree_skb_any(skb);//edit by thomas
+ }
+}
+EXPORT_SYMBOL(SendDisassociation);
+
+int ieee80211_wpa_supplicant_ioctl(struct ieee80211_device *ieee, struct iw_point *p)
+{
+ struct ieee_param *param;
+ int ret=0;
+
+ down(&ieee->wx_sem);
+ //IEEE_DEBUG_INFO("wpa_supplicant: len=%d\n", p->length);
+
+ if (p->length < sizeof(struct ieee_param) || !p->pointer) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ param = memdup_user(p->pointer, p->length);
+ if (IS_ERR(param)) {
+ ret = PTR_ERR(param);
+ goto out;
+ }
+
+ switch (param->cmd) {
+
+ case IEEE_CMD_SET_WPA_PARAM:
+ ret = ieee80211_wpa_set_param(ieee, param->u.wpa_param.name,
+ param->u.wpa_param.value);
+ break;
+
+ case IEEE_CMD_SET_WPA_IE:
+ ret = ieee80211_wpa_set_wpa_ie(ieee, param, p->length);
+ break;
+
+ case IEEE_CMD_SET_ENCRYPTION:
+ ret = ieee80211_wpa_set_encryption(ieee, param, p->length);
+ break;
+
+ case IEEE_CMD_MLME:
+ ret = ieee80211_wpa_mlme(ieee, param->u.mlme.command,
+ param->u.mlme.reason_code);
+ break;
+
+ default:
+ printk("Unknown WPA supplicant request: %d\n",param->cmd);
+ ret = -EOPNOTSUPP;
+ break;
+ }
+
+ if (ret == 0 && copy_to_user(p->pointer, param, p->length))
+ ret = -EFAULT;
+
+ kfree(param);
+out:
+ up(&ieee->wx_sem);
+
+ return ret;
+}
+EXPORT_SYMBOL(ieee80211_wpa_supplicant_ioctl);
+
+void notify_wx_assoc_event(struct ieee80211_device *ieee)
+{
+ union iwreq_data wrqu;
+ wrqu.ap_addr.sa_family = ARPHRD_ETHER;
+ if (ieee->state == IEEE80211_LINKED)
+ memcpy(wrqu.ap_addr.sa_data, ieee->current_network.bssid, ETH_ALEN);
+ else
+ eth_zero_addr(wrqu.ap_addr.sa_data);
+ wireless_send_event(ieee->dev, SIOCGIWAP, &wrqu, NULL);
+}
+EXPORT_SYMBOL(notify_wx_assoc_event);
diff --git a/drivers/staging/rtl8192u/ieee80211/ieee80211_softmac_wx.c b/drivers/staging/rtl8192u/ieee80211/ieee80211_softmac_wx.c
new file mode 100644
index 000000000..714fbcace
--- /dev/null
+++ b/drivers/staging/rtl8192u/ieee80211/ieee80211_softmac_wx.c
@@ -0,0 +1,605 @@
+/* IEEE 802.11 SoftMAC layer
+ * Copyright (c) 2005 Andrea Merello <andrea.merello@gmail.com>
+ *
+ * Mostly extracted from the rtl8180-sa2400 driver for the
+ * in-kernel generic ieee802.11 stack.
+ *
+ * Some pieces of code might be stolen from ipw2100 driver
+ * copyright of who own it's copyright ;-)
+ *
+ * PS wx handler mostly stolen from hostap, copyright who
+ * own it's copyright ;-)
+ *
+ * released under the GPL
+ */
+
+
+#include <linux/etherdevice.h>
+
+#include "ieee80211.h"
+#include "dot11d.h"
+/* FIXME: add A freqs */
+
+const long ieee80211_wlan_frequencies[] = {
+ 2412, 2417, 2422, 2427,
+ 2432, 2437, 2442, 2447,
+ 2452, 2457, 2462, 2467,
+ 2472, 2484
+};
+EXPORT_SYMBOL(ieee80211_wlan_frequencies);
+
+int ieee80211_wx_set_freq(struct ieee80211_device *ieee, struct iw_request_info *a,
+ union iwreq_data *wrqu, char *b)
+{
+ int ret;
+ struct iw_freq *fwrq = & wrqu->freq;
+
+ down(&ieee->wx_sem);
+
+ if (ieee->iw_mode == IW_MODE_INFRA) {
+ ret = -EOPNOTSUPP;
+ goto out;
+ }
+
+ /* if setting by freq convert to channel */
+ if (fwrq->e == 1) {
+ if ((fwrq->m >= (int) 2.412e8 &&
+ fwrq->m <= (int) 2.487e8)) {
+ int f = fwrq->m / 100000;
+ int c = 0;
+
+ while ((c < 14) && (f != ieee80211_wlan_frequencies[c]))
+ c++;
+
+ /* hack to fall through */
+ fwrq->e = 0;
+ fwrq->m = c + 1;
+ }
+ }
+
+ if (fwrq->e > 0 || fwrq->m > 14 || fwrq->m < 1 ){
+ ret = -EOPNOTSUPP;
+ goto out;
+
+ }else { /* Set the channel */
+
+ if (!(GET_DOT11D_INFO(ieee)->channel_map)[fwrq->m]) {
+ ret = -EINVAL;
+ goto out;
+ }
+ ieee->current_network.channel = fwrq->m;
+ ieee->set_chan(ieee->dev, ieee->current_network.channel);
+
+ if(ieee->iw_mode == IW_MODE_ADHOC || ieee->iw_mode == IW_MODE_MASTER)
+ if(ieee->state == IEEE80211_LINKED){
+
+ ieee80211_stop_send_beacons(ieee);
+ ieee80211_start_send_beacons(ieee);
+ }
+ }
+
+ ret = 0;
+out:
+ up(&ieee->wx_sem);
+ return ret;
+}
+EXPORT_SYMBOL(ieee80211_wx_set_freq);
+
+int ieee80211_wx_get_freq(struct ieee80211_device *ieee,
+ struct iw_request_info *a,
+ union iwreq_data *wrqu, char *b)
+{
+ struct iw_freq *fwrq = & wrqu->freq;
+
+ if (ieee->current_network.channel == 0)
+ return -1;
+ //NM 0.7.0 will not accept channel any more.
+ fwrq->m = ieee80211_wlan_frequencies[ieee->current_network.channel-1] * 100000;
+ fwrq->e = 1;
+// fwrq->m = ieee->current_network.channel;
+// fwrq->e = 0;
+
+ return 0;
+}
+EXPORT_SYMBOL(ieee80211_wx_get_freq);
+
+int ieee80211_wx_get_wap(struct ieee80211_device *ieee,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ unsigned long flags;
+
+ wrqu->ap_addr.sa_family = ARPHRD_ETHER;
+
+ if (ieee->iw_mode == IW_MODE_MONITOR)
+ return -1;
+
+ /* We want avoid to give to the user inconsistent infos*/
+ spin_lock_irqsave(&ieee->lock, flags);
+
+ if (ieee->state != IEEE80211_LINKED &&
+ ieee->state != IEEE80211_LINKED_SCANNING &&
+ ieee->wap_set == 0)
+
+ eth_zero_addr(wrqu->ap_addr.sa_data);
+ else
+ memcpy(wrqu->ap_addr.sa_data,
+ ieee->current_network.bssid, ETH_ALEN);
+
+ spin_unlock_irqrestore(&ieee->lock, flags);
+
+ return 0;
+}
+EXPORT_SYMBOL(ieee80211_wx_get_wap);
+
+int ieee80211_wx_set_wap(struct ieee80211_device *ieee,
+ struct iw_request_info *info,
+ union iwreq_data *awrq,
+ char *extra)
+{
+
+ int ret = 0;
+ unsigned long flags;
+
+ short ifup = ieee->proto_started;//dev->flags & IFF_UP;
+ struct sockaddr *temp = (struct sockaddr *)awrq;
+
+ ieee->sync_scan_hurryup = 1;
+
+ down(&ieee->wx_sem);
+ /* use ifconfig hw ether */
+ if (ieee->iw_mode == IW_MODE_MASTER) {
+ ret = -1;
+ goto out;
+ }
+
+ if (temp->sa_family != ARPHRD_ETHER) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ if (ifup)
+ ieee80211_stop_protocol(ieee);
+
+ /* just to avoid to give inconsistent infos in the
+ * get wx method. not really needed otherwise
+ */
+ spin_lock_irqsave(&ieee->lock, flags);
+
+ memcpy(ieee->current_network.bssid, temp->sa_data, ETH_ALEN);
+ ieee->wap_set = !is_zero_ether_addr(temp->sa_data);
+
+ spin_unlock_irqrestore(&ieee->lock, flags);
+
+ if (ifup)
+ ieee80211_start_protocol(ieee);
+out:
+ up(&ieee->wx_sem);
+ return ret;
+}
+EXPORT_SYMBOL(ieee80211_wx_set_wap);
+
+ int ieee80211_wx_get_essid(struct ieee80211_device *ieee, struct iw_request_info *a,union iwreq_data *wrqu,char *b)
+{
+ int len, ret = 0;
+ unsigned long flags;
+
+ if (ieee->iw_mode == IW_MODE_MONITOR)
+ return -1;
+
+ /* We want avoid to give to the user inconsistent infos*/
+ spin_lock_irqsave(&ieee->lock, flags);
+
+ if (ieee->current_network.ssid[0] == '\0' ||
+ ieee->current_network.ssid_len == 0){
+ ret = -1;
+ goto out;
+ }
+
+ if (ieee->state != IEEE80211_LINKED &&
+ ieee->state != IEEE80211_LINKED_SCANNING &&
+ ieee->ssid_set == 0){
+ ret = -1;
+ goto out;
+ }
+ len = ieee->current_network.ssid_len;
+ wrqu->essid.length = len;
+ strncpy(b, ieee->current_network.ssid, len);
+ wrqu->essid.flags = 1;
+
+out:
+ spin_unlock_irqrestore(&ieee->lock, flags);
+
+ return ret;
+
+}
+EXPORT_SYMBOL(ieee80211_wx_get_essid);
+
+int ieee80211_wx_set_rate(struct ieee80211_device *ieee,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+
+ u32 target_rate = wrqu->bitrate.value;
+
+ ieee->rate = target_rate/100000;
+ //FIXME: we might want to limit rate also in management protocols.
+ return 0;
+}
+EXPORT_SYMBOL(ieee80211_wx_set_rate);
+
+int ieee80211_wx_get_rate(struct ieee80211_device *ieee,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ u32 tmp_rate;
+ tmp_rate = TxCountToDataRate(ieee, ieee->softmac_stats.CurrentShowTxate);
+
+ wrqu->bitrate.value = tmp_rate * 500000;
+
+ return 0;
+}
+EXPORT_SYMBOL(ieee80211_wx_get_rate);
+
+int ieee80211_wx_set_rts(struct ieee80211_device *ieee,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ if (wrqu->rts.disabled || !wrqu->rts.fixed)
+ ieee->rts = DEFAULT_RTS_THRESHOLD;
+ else
+ {
+ if (wrqu->rts.value < MIN_RTS_THRESHOLD ||
+ wrqu->rts.value > MAX_RTS_THRESHOLD)
+ return -EINVAL;
+ ieee->rts = wrqu->rts.value;
+ }
+ return 0;
+}
+EXPORT_SYMBOL(ieee80211_wx_set_rts);
+
+int ieee80211_wx_get_rts(struct ieee80211_device *ieee,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ wrqu->rts.value = ieee->rts;
+ wrqu->rts.fixed = 0; /* no auto select */
+ wrqu->rts.disabled = (wrqu->rts.value == DEFAULT_RTS_THRESHOLD);
+ return 0;
+}
+EXPORT_SYMBOL(ieee80211_wx_get_rts);
+
+int ieee80211_wx_set_mode(struct ieee80211_device *ieee, struct iw_request_info *a,
+ union iwreq_data *wrqu, char *b)
+{
+
+ ieee->sync_scan_hurryup = 1;
+
+ down(&ieee->wx_sem);
+
+ if (wrqu->mode == ieee->iw_mode)
+ goto out;
+
+ if (wrqu->mode == IW_MODE_MONITOR){
+
+ ieee->dev->type = ARPHRD_IEEE80211;
+ }else{
+ ieee->dev->type = ARPHRD_ETHER;
+ }
+
+ if (!ieee->proto_started){
+ ieee->iw_mode = wrqu->mode;
+ }else{
+ ieee80211_stop_protocol(ieee);
+ ieee->iw_mode = wrqu->mode;
+ ieee80211_start_protocol(ieee);
+ }
+
+out:
+ up(&ieee->wx_sem);
+ return 0;
+}
+EXPORT_SYMBOL(ieee80211_wx_set_mode);
+
+void ieee80211_wx_sync_scan_wq(struct work_struct *work)
+{
+ struct ieee80211_device *ieee = container_of(work, struct ieee80211_device, wx_sync_scan_wq);
+ short chan;
+ HT_EXTCHNL_OFFSET chan_offset=0;
+ HT_CHANNEL_WIDTH bandwidth=0;
+ int b40M = 0;
+ static int count;
+ chan = ieee->current_network.channel;
+ netif_carrier_off(ieee->dev);
+
+ if (ieee->data_hard_stop)
+ ieee->data_hard_stop(ieee->dev);
+
+ ieee80211_stop_send_beacons(ieee);
+
+ ieee->state = IEEE80211_LINKED_SCANNING;
+ ieee->link_change(ieee->dev);
+ ieee->InitialGainHandler(ieee->dev, IG_Backup);
+ if (ieee->pHTInfo->bCurrentHTSupport && ieee->pHTInfo->bEnableHT && ieee->pHTInfo->bCurBW40MHz) {
+ b40M = 1;
+ chan_offset = ieee->pHTInfo->CurSTAExtChnlOffset;
+ bandwidth = (HT_CHANNEL_WIDTH)ieee->pHTInfo->bCurBW40MHz;
+ printk("Scan in 40M, force to 20M first:%d, %d\n", chan_offset, bandwidth);
+ ieee->SetBWModeHandler(ieee->dev, HT_CHANNEL_WIDTH_20, HT_EXTCHNL_OFFSET_NO_EXT);
+ }
+ ieee80211_start_scan_syncro(ieee);
+ if (b40M) {
+ printk("Scan in 20M, back to 40M\n");
+ if (chan_offset == HT_EXTCHNL_OFFSET_UPPER)
+ ieee->set_chan(ieee->dev, chan + 2);
+ else if (chan_offset == HT_EXTCHNL_OFFSET_LOWER)
+ ieee->set_chan(ieee->dev, chan - 2);
+ else
+ ieee->set_chan(ieee->dev, chan);
+ ieee->SetBWModeHandler(ieee->dev, bandwidth, chan_offset);
+ } else {
+ ieee->set_chan(ieee->dev, chan);
+ }
+
+ ieee->InitialGainHandler(ieee->dev, IG_Restore);
+ ieee->state = IEEE80211_LINKED;
+ ieee->link_change(ieee->dev);
+ // To prevent the immediately calling watch_dog after scan.
+ if (ieee->LinkDetectInfo.NumRecvBcnInPeriod==0||ieee->LinkDetectInfo.NumRecvDataInPeriod==0 )
+ {
+ ieee->LinkDetectInfo.NumRecvBcnInPeriod = 1;
+ ieee->LinkDetectInfo.NumRecvDataInPeriod= 1;
+ }
+ if (ieee->data_hard_resume)
+ ieee->data_hard_resume(ieee->dev);
+
+ if(ieee->iw_mode == IW_MODE_ADHOC || ieee->iw_mode == IW_MODE_MASTER)
+ ieee80211_start_send_beacons(ieee);
+
+ netif_carrier_on(ieee->dev);
+ count = 0;
+ up(&ieee->wx_sem);
+
+}
+
+int ieee80211_wx_set_scan(struct ieee80211_device *ieee, struct iw_request_info *a,
+ union iwreq_data *wrqu, char *b)
+{
+ int ret = 0;
+
+ down(&ieee->wx_sem);
+
+ if (ieee->iw_mode == IW_MODE_MONITOR || !(ieee->proto_started)) {
+ ret = -1;
+ goto out;
+ }
+
+ if (ieee->state == IEEE80211_LINKED) {
+ queue_work(ieee->wq, &ieee->wx_sync_scan_wq);
+ /* intentionally forget to up sem */
+ return 0;
+ }
+
+out:
+ up(&ieee->wx_sem);
+ return ret;
+}
+EXPORT_SYMBOL(ieee80211_wx_set_scan);
+
+int ieee80211_wx_set_essid(struct ieee80211_device *ieee,
+ struct iw_request_info *a,
+ union iwreq_data *wrqu, char *extra)
+{
+
+ int ret=0,len;
+ short proto_started;
+ unsigned long flags;
+
+ ieee->sync_scan_hurryup = 1;
+ down(&ieee->wx_sem);
+
+ proto_started = ieee->proto_started;
+
+ if (wrqu->essid.length > IW_ESSID_MAX_SIZE) {
+ ret= -E2BIG;
+ goto out;
+ }
+
+ if (ieee->iw_mode == IW_MODE_MONITOR) {
+ ret= -1;
+ goto out;
+ }
+
+ if(proto_started)
+ ieee80211_stop_protocol(ieee);
+
+
+ /* this is just to be sure that the GET wx callback
+ * has consisten infos. not needed otherwise
+ */
+ spin_lock_irqsave(&ieee->lock, flags);
+
+ if (wrqu->essid.flags && wrqu->essid.length) {
+ //first flush current network.ssid
+ len = ((wrqu->essid.length-1) < IW_ESSID_MAX_SIZE) ? (wrqu->essid.length-1) : IW_ESSID_MAX_SIZE;
+ strncpy(ieee->current_network.ssid, extra, len+1);
+ ieee->current_network.ssid_len = len+1;
+ ieee->ssid_set = 1;
+ }
+ else{
+ ieee->ssid_set = 0;
+ ieee->current_network.ssid[0] = '\0';
+ ieee->current_network.ssid_len = 0;
+ }
+ spin_unlock_irqrestore(&ieee->lock, flags);
+
+ if (proto_started)
+ ieee80211_start_protocol(ieee);
+out:
+ up(&ieee->wx_sem);
+ return ret;
+}
+EXPORT_SYMBOL(ieee80211_wx_set_essid);
+
+ int ieee80211_wx_get_mode(struct ieee80211_device *ieee, struct iw_request_info *a,
+ union iwreq_data *wrqu, char *b)
+{
+
+ wrqu->mode = ieee->iw_mode;
+ return 0;
+}
+EXPORT_SYMBOL(ieee80211_wx_get_mode);
+
+ int ieee80211_wx_set_rawtx(struct ieee80211_device *ieee,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+
+ int *parms = (int *)extra;
+ int enable = (parms[0] > 0);
+ short prev = ieee->raw_tx;
+
+ down(&ieee->wx_sem);
+
+ if(enable)
+ ieee->raw_tx = 1;
+ else
+ ieee->raw_tx = 0;
+
+ printk(KERN_INFO"raw TX is %s\n",
+ ieee->raw_tx ? "enabled" : "disabled");
+
+ if (ieee->iw_mode == IW_MODE_MONITOR)
+ {
+ if (prev == 0 && ieee->raw_tx) {
+ if (ieee->data_hard_resume)
+ ieee->data_hard_resume(ieee->dev);
+
+ netif_carrier_on(ieee->dev);
+ }
+
+ if(prev && ieee->raw_tx == 1)
+ netif_carrier_off(ieee->dev);
+ }
+
+ up(&ieee->wx_sem);
+
+ return 0;
+}
+EXPORT_SYMBOL(ieee80211_wx_set_rawtx);
+
+int ieee80211_wx_get_name(struct ieee80211_device *ieee,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ strlcpy(wrqu->name, "802.11", IFNAMSIZ);
+ if (ieee->modulation & IEEE80211_CCK_MODULATION) {
+ strlcat(wrqu->name, "b", IFNAMSIZ);
+ if (ieee->modulation & IEEE80211_OFDM_MODULATION)
+ strlcat(wrqu->name, "/g", IFNAMSIZ);
+ } else if (ieee->modulation & IEEE80211_OFDM_MODULATION) {
+ strlcat(wrqu->name, "g", IFNAMSIZ);
+ }
+
+ if (ieee->mode & (IEEE_N_24G | IEEE_N_5G))
+ strlcat(wrqu->name, "/n", IFNAMSIZ);
+
+ if ((ieee->state == IEEE80211_LINKED) ||
+ (ieee->state == IEEE80211_LINKED_SCANNING))
+ strlcat(wrqu->name, " linked", IFNAMSIZ);
+ else if (ieee->state != IEEE80211_NOLINK)
+ strlcat(wrqu->name, " link..", IFNAMSIZ);
+
+ return 0;
+}
+EXPORT_SYMBOL(ieee80211_wx_get_name);
+
+/* this is mostly stolen from hostap */
+int ieee80211_wx_set_power(struct ieee80211_device *ieee,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ int ret = 0;
+ down(&ieee->wx_sem);
+
+ if (wrqu->power.disabled) {
+ ieee->ps = IEEE80211_PS_DISABLED;
+ goto exit;
+ }
+ if (wrqu->power.flags & IW_POWER_TIMEOUT) {
+ //ieee->ps_period = wrqu->power.value / 1000;
+ ieee->ps_timeout = wrqu->power.value / 1000;
+ }
+
+ if (wrqu->power.flags & IW_POWER_PERIOD) {
+
+ //ieee->ps_timeout = wrqu->power.value / 1000;
+ ieee->ps_period = wrqu->power.value / 1000;
+ //wrq->value / 1024;
+
+ }
+ switch (wrqu->power.flags & IW_POWER_MODE) {
+ case IW_POWER_UNICAST_R:
+ ieee->ps = IEEE80211_PS_UNICAST;
+ break;
+ case IW_POWER_MULTICAST_R:
+ ieee->ps = IEEE80211_PS_MBCAST;
+ break;
+ case IW_POWER_ALL_R:
+ ieee->ps = IEEE80211_PS_UNICAST | IEEE80211_PS_MBCAST;
+ break;
+
+ case IW_POWER_ON:
+ // ieee->ps = IEEE80211_PS_DISABLED;
+ break;
+
+ default:
+ ret = -EINVAL;
+ goto exit;
+
+ }
+exit:
+ up(&ieee->wx_sem);
+ return ret;
+
+}
+EXPORT_SYMBOL(ieee80211_wx_set_power);
+
+/* this is stolen from hostap */
+int ieee80211_wx_get_power(struct ieee80211_device *ieee,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ down(&ieee->wx_sem);
+
+ if (ieee->ps == IEEE80211_PS_DISABLED) {
+ wrqu->power.disabled = 1;
+ goto exit;
+ }
+
+ wrqu->power.disabled = 0;
+
+ if ((wrqu->power.flags & IW_POWER_TYPE) == IW_POWER_TIMEOUT) {
+ wrqu->power.flags = IW_POWER_TIMEOUT;
+ wrqu->power.value = ieee->ps_timeout * 1000;
+ } else {
+// ret = -EOPNOTSUPP;
+// goto exit;
+ wrqu->power.flags = IW_POWER_PERIOD;
+ wrqu->power.value = ieee->ps_period * 1000;
+//ieee->current_network.dtim_period * ieee->current_network.beacon_interval * 1024;
+ }
+
+ if ((ieee->ps & (IEEE80211_PS_MBCAST | IEEE80211_PS_UNICAST)) == (IEEE80211_PS_MBCAST | IEEE80211_PS_UNICAST))
+ wrqu->power.flags |= IW_POWER_ALL_R;
+ else if (ieee->ps & IEEE80211_PS_MBCAST)
+ wrqu->power.flags |= IW_POWER_MULTICAST_R;
+ else
+ wrqu->power.flags |= IW_POWER_UNICAST_R;
+
+exit:
+ up(&ieee->wx_sem);
+ return 0;
+
+}
+EXPORT_SYMBOL(ieee80211_wx_get_power);
diff --git a/drivers/staging/rtl8192u/ieee80211/ieee80211_tx.c b/drivers/staging/rtl8192u/ieee80211/ieee80211_tx.c
new file mode 100644
index 000000000..9f68c652f
--- /dev/null
+++ b/drivers/staging/rtl8192u/ieee80211/ieee80211_tx.c
@@ -0,0 +1,914 @@
+/******************************************************************************
+
+ Copyright(c) 2003 - 2004 Intel Corporation. All rights reserved.
+
+ This program is free software; you can redistribute it and/or modify it
+ under the terms of version 2 of the GNU General Public License as
+ published by the Free Software Foundation.
+
+ 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.
+
+ The full GNU General Public License is included in this distribution in the
+ file called LICENSE.
+
+ Contact Information:
+ James P. Ketrenos <ipw2100-admin@linux.intel.com>
+ Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+
+******************************************************************************
+
+ Few modifications for Realtek's Wi-Fi drivers by
+ Andrea Merello <andrea.merello@gmail.com>
+
+ A special thanks goes to Realtek for their support !
+
+******************************************************************************/
+
+#include <linux/compiler.h>
+#include <linux/errno.h>
+#include <linux/if_arp.h>
+#include <linux/in6.h>
+#include <linux/in.h>
+#include <linux/ip.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/netdevice.h>
+#include <linux/pci.h>
+#include <linux/proc_fs.h>
+#include <linux/skbuff.h>
+#include <linux/slab.h>
+#include <linux/tcp.h>
+#include <linux/types.h>
+#include <linux/wireless.h>
+#include <linux/etherdevice.h>
+#include <asm/uaccess.h>
+#include <linux/if_vlan.h>
+
+#include "ieee80211.h"
+
+
+/*
+
+
+802.11 Data Frame
+
+
+802.11 frame_contorl for data frames - 2 bytes
+ ,-----------------------------------------------------------------------------------------.
+bits | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | a | b | c | d | e |
+ |----|-----|-----|-----|-----|-----|-----|-----|-----|-----|-----|-----|-----|-----|------|
+val | 0 | 0 | 0 | 1 | x | 0 | 0 | 0 | 1 | 0 | x | x | x | x | x |
+ |----|-----|-----|-----|-----|-----|-----|-----|-----|-----|-----|-----|-----|-----|------|
+desc | ^-ver-^ | ^type-^ | ^-----subtype-----^ | to |from |more |retry| pwr |more |wep |
+ | | | x=0 data,x=1 data+ack | DS | DS |frag | | mgm |data | |
+ '-----------------------------------------------------------------------------------------'
+ /\
+ |
+802.11 Data Frame |
+ ,--------- 'ctrl' expands to >-----------'
+ |
+ ,--'---,-------------------------------------------------------------.
+Bytes | 2 | 2 | 6 | 6 | 6 | 2 | 0..2312 | 4 |
+ |------|------|---------|---------|---------|------|---------|------|
+Desc. | ctrl | dura | DA/RA | TA | SA | Sequ | Frame | fcs |
+ | | tion | (BSSID) | | | ence | data | |
+ `--------------------------------------------------| |------'
+Total: 28 non-data bytes `----.----'
+ |
+ .- 'Frame data' expands to <---------------------------'
+ |
+ V
+ ,---------------------------------------------------.
+Bytes | 1 | 1 | 1 | 3 | 2 | 0-2304 |
+ |------|------|---------|----------|------|---------|
+Desc. | SNAP | SNAP | Control |Eth Tunnel| Type | IP |
+ | DSAP | SSAP | | | | Packet |
+ | 0xAA | 0xAA |0x03 (UI)|0x00-00-F8| | |
+ `-----------------------------------------| |
+Total: 8 non-data bytes `----.----'
+ |
+ .- 'IP Packet' expands, if WEP enabled, to <--'
+ |
+ V
+ ,-----------------------.
+Bytes | 4 | 0-2296 | 4 |
+ |-----|-----------|-----|
+Desc. | IV | Encrypted | ICV |
+ | | IP Packet | |
+ `-----------------------'
+Total: 8 non-data bytes
+
+
+802.3 Ethernet Data Frame
+
+ ,-----------------------------------------.
+Bytes | 6 | 6 | 2 | Variable | 4 |
+ |-------|-------|------|-----------|------|
+Desc. | Dest. | Source| Type | IP Packet | fcs |
+ | MAC | MAC | | | |
+ `-----------------------------------------'
+Total: 18 non-data bytes
+
+In the event that fragmentation is required, the incoming payload is split into
+N parts of size ieee->fts. The first fragment contains the SNAP header and the
+remaining packets are just data.
+
+If encryption is enabled, each fragment payload size is reduced by enough space
+to add the prefix and postfix (IV and ICV totalling 8 bytes in the case of WEP)
+So if you have 1500 bytes of payload with ieee->fts set to 500 without
+encryption it will take 3 frames. With WEP it will take 4 frames as the
+payload of each frame is reduced to 492 bytes.
+
+* SKB visualization
+*
+* ,- skb->data
+* |
+* | ETHERNET HEADER ,-<-- PAYLOAD
+* | | 14 bytes from skb->data
+* | 2 bytes for Type --> ,T. | (sizeof ethhdr)
+* | | | |
+* |,-Dest.--. ,--Src.---. | | |
+* | 6 bytes| | 6 bytes | | | |
+* v | | | | | |
+* 0 | v 1 | v | v 2
+* 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
+* ^ | ^ | ^ |
+* | | | | | |
+* | | | | `T' <---- 2 bytes for Type
+* | | | |
+* | | '---SNAP--' <-------- 6 bytes for SNAP
+* | |
+* `-IV--' <-------------------- 4 bytes for IV (WEP)
+*
+* SNAP HEADER
+*
+*/
+
+static u8 P802_1H_OUI[P80211_OUI_LEN] = { 0x00, 0x00, 0xf8 };
+static u8 RFC1042_OUI[P80211_OUI_LEN] = { 0x00, 0x00, 0x00 };
+
+static inline int ieee80211_put_snap(u8 *data, u16 h_proto)
+{
+ struct ieee80211_snap_hdr *snap;
+ u8 *oui;
+
+ snap = (struct ieee80211_snap_hdr *)data;
+ snap->dsap = 0xaa;
+ snap->ssap = 0xaa;
+ snap->ctrl = 0x03;
+
+ if (h_proto == 0x8137 || h_proto == 0x80f3)
+ oui = P802_1H_OUI;
+ else
+ oui = RFC1042_OUI;
+ snap->oui[0] = oui[0];
+ snap->oui[1] = oui[1];
+ snap->oui[2] = oui[2];
+
+ *(u16 *)(data + SNAP_SIZE) = htons(h_proto);
+
+ return SNAP_SIZE + sizeof(u16);
+}
+
+int ieee80211_encrypt_fragment(
+ struct ieee80211_device *ieee,
+ struct sk_buff *frag,
+ int hdr_len)
+{
+ struct ieee80211_crypt_data *crypt = ieee->crypt[ieee->tx_keyidx];
+ int res;
+
+ if (!(crypt && crypt->ops))
+ {
+ printk("=========>%s(), crypt is null\n", __func__);
+ return -1;
+ }
+
+ if (ieee->tkip_countermeasures &&
+ crypt && crypt->ops && strcmp(crypt->ops->name, "TKIP") == 0) {
+ if (net_ratelimit()) {
+ struct ieee80211_hdr_3addrqos *header;
+
+ header = (struct ieee80211_hdr_3addrqos *)frag->data;
+ printk(KERN_DEBUG "%s: TKIP countermeasures: dropped "
+ "TX packet to %pM\n",
+ ieee->dev->name, header->addr1);
+ }
+ return -1;
+ }
+
+ /* To encrypt, frame format is:
+ * IV (4 bytes), clear payload (including SNAP), ICV (4 bytes) */
+
+ // PR: FIXME: Copied from hostap. Check fragmentation/MSDU/MPDU encryption.
+ /* Host-based IEEE 802.11 fragmentation for TX is not yet supported, so
+ * call both MSDU and MPDU encryption functions from here. */
+ atomic_inc(&crypt->refcnt);
+ res = 0;
+ if (crypt->ops->encrypt_msdu)
+ res = crypt->ops->encrypt_msdu(frag, hdr_len, crypt->priv);
+ if (res == 0 && crypt->ops->encrypt_mpdu)
+ res = crypt->ops->encrypt_mpdu(frag, hdr_len, crypt->priv);
+
+ atomic_dec(&crypt->refcnt);
+ if (res < 0) {
+ printk(KERN_INFO "%s: Encryption failed: len=%d.\n",
+ ieee->dev->name, frag->len);
+ ieee->ieee_stats.tx_discards++;
+ return -1;
+ }
+
+ return 0;
+}
+
+
+void ieee80211_txb_free(struct ieee80211_txb *txb) {
+ //int i;
+ if (unlikely(!txb))
+ return;
+ kfree(txb);
+}
+EXPORT_SYMBOL(ieee80211_txb_free);
+
+static struct ieee80211_txb *ieee80211_alloc_txb(int nr_frags, int txb_size,
+ gfp_t gfp_mask)
+{
+ struct ieee80211_txb *txb;
+ int i;
+ txb = kmalloc(
+ sizeof(struct ieee80211_txb) + (sizeof(u8 *) * nr_frags),
+ gfp_mask);
+ if (!txb)
+ return NULL;
+
+ memset(txb, 0, sizeof(struct ieee80211_txb));
+ txb->nr_frags = nr_frags;
+ txb->frag_size = txb_size;
+
+ for (i = 0; i < nr_frags; i++) {
+ txb->fragments[i] = dev_alloc_skb(txb_size);
+ if (unlikely(!txb->fragments[i])) {
+ i--;
+ break;
+ }
+ memset(txb->fragments[i]->cb, 0, sizeof(txb->fragments[i]->cb));
+ }
+ if (unlikely(i != nr_frags)) {
+ while (i >= 0)
+ dev_kfree_skb_any(txb->fragments[i--]);
+ kfree(txb);
+ return NULL;
+ }
+ return txb;
+}
+
+// Classify the to-be send data packet
+// Need to acquire the sent queue index.
+static int
+ieee80211_classify(struct sk_buff *skb, struct ieee80211_network *network)
+{
+ struct ethhdr *eth;
+ struct iphdr *ip;
+ eth = (struct ethhdr *)skb->data;
+ if (eth->h_proto != htons(ETH_P_IP))
+ return 0;
+
+// IEEE80211_DEBUG_DATA(IEEE80211_DL_DATA, skb->data, skb->len);
+ ip = ip_hdr(skb);
+ switch (ip->tos & 0xfc) {
+ case 0x20:
+ return 2;
+ case 0x40:
+ return 1;
+ case 0x60:
+ return 3;
+ case 0x80:
+ return 4;
+ case 0xa0:
+ return 5;
+ case 0xc0:
+ return 6;
+ case 0xe0:
+ return 7;
+ default:
+ return 0;
+ }
+}
+
+#define SN_LESS(a, b) (((a-b)&0x800)!=0)
+static void ieee80211_tx_query_agg_cap(struct ieee80211_device *ieee,
+ struct sk_buff *skb, cb_desc *tcb_desc)
+{
+ PRT_HIGH_THROUGHPUT pHTInfo = ieee->pHTInfo;
+ PTX_TS_RECORD pTxTs = NULL;
+ struct ieee80211_hdr_1addr *hdr = (struct ieee80211_hdr_1addr *)skb->data;
+
+ if (!pHTInfo->bCurrentHTSupport||!pHTInfo->bEnableHT)
+ return;
+ if (!IsQoSDataFrame(skb->data))
+ return;
+
+ if (is_multicast_ether_addr(hdr->addr1))
+ return;
+ //check packet and mode later
+#ifdef TO_DO_LIST
+ if(pTcb->PacketLength >= 4096)
+ return;
+ // For RTL819X, if pairwisekey = wep/tkip, we don't aggrregation.
+ if(!Adapter->HalFunc.GetNmodeSupportBySecCfgHandler(Adapter))
+ return;
+#endif
+ if(!ieee->GetNmodeSupportBySecCfg(ieee->dev))
+ {
+ return;
+ }
+ if(pHTInfo->bCurrentAMPDUEnable)
+ {
+ if (!GetTs(ieee, (PTS_COMMON_INFO *)(&pTxTs), hdr->addr1, skb->priority, TX_DIR, true))
+ {
+ printk("===>can't get TS\n");
+ return;
+ }
+ if (pTxTs->TxAdmittedBARecord.bValid == false)
+ {
+ TsStartAddBaProcess(ieee, pTxTs);
+ goto FORCED_AGG_SETTING;
+ }
+ else if (pTxTs->bUsingBa == false)
+ {
+ if (SN_LESS(pTxTs->TxAdmittedBARecord.BaStartSeqCtrl.field.SeqNum, (pTxTs->TxCurSeq+1)%4096))
+ pTxTs->bUsingBa = true;
+ else
+ goto FORCED_AGG_SETTING;
+ }
+
+ if (ieee->iw_mode == IW_MODE_INFRA)
+ {
+ tcb_desc->bAMPDUEnable = true;
+ tcb_desc->ampdu_factor = pHTInfo->CurrentAMPDUFactor;
+ tcb_desc->ampdu_density = pHTInfo->CurrentMPDUDensity;
+ }
+ }
+FORCED_AGG_SETTING:
+ switch (pHTInfo->ForcedAMPDUMode )
+ {
+ case HT_AGG_AUTO:
+ break;
+
+ case HT_AGG_FORCE_ENABLE:
+ tcb_desc->bAMPDUEnable = true;
+ tcb_desc->ampdu_density = pHTInfo->ForcedMPDUDensity;
+ tcb_desc->ampdu_factor = pHTInfo->ForcedAMPDUFactor;
+ break;
+
+ case HT_AGG_FORCE_DISABLE:
+ tcb_desc->bAMPDUEnable = false;
+ tcb_desc->ampdu_density = 0;
+ tcb_desc->ampdu_factor = 0;
+ break;
+
+ }
+ return;
+}
+
+static void ieee80211_qurey_ShortPreambleMode(struct ieee80211_device *ieee,
+ cb_desc *tcb_desc)
+{
+ tcb_desc->bUseShortPreamble = false;
+ if (tcb_desc->data_rate == 2)
+ {//// 1M can only use Long Preamble. 11B spec
+ return;
+ }
+ else if (ieee->current_network.capability & WLAN_CAPABILITY_SHORT_PREAMBLE)
+ {
+ tcb_desc->bUseShortPreamble = true;
+ }
+ return;
+}
+static void
+ieee80211_query_HTCapShortGI(struct ieee80211_device *ieee, cb_desc *tcb_desc)
+{
+ PRT_HIGH_THROUGHPUT pHTInfo = ieee->pHTInfo;
+
+ tcb_desc->bUseShortGI = false;
+
+ if(!pHTInfo->bCurrentHTSupport||!pHTInfo->bEnableHT)
+ return;
+
+ if(pHTInfo->bForcedShortGI)
+ {
+ tcb_desc->bUseShortGI = true;
+ return;
+ }
+
+ if((pHTInfo->bCurBW40MHz==true) && pHTInfo->bCurShortGI40MHz)
+ tcb_desc->bUseShortGI = true;
+ else if((pHTInfo->bCurBW40MHz==false) && pHTInfo->bCurShortGI20MHz)
+ tcb_desc->bUseShortGI = true;
+}
+
+static void ieee80211_query_BandwidthMode(struct ieee80211_device *ieee,
+ cb_desc *tcb_desc)
+{
+ PRT_HIGH_THROUGHPUT pHTInfo = ieee->pHTInfo;
+
+ tcb_desc->bPacketBW = false;
+
+ if(!pHTInfo->bCurrentHTSupport||!pHTInfo->bEnableHT)
+ return;
+
+ if(tcb_desc->bMulticast || tcb_desc->bBroadcast)
+ return;
+
+ if((tcb_desc->data_rate & 0x80)==0) // If using legacy rate, it shall use 20MHz channel.
+ return;
+ //BandWidthAutoSwitch is for auto switch to 20 or 40 in long distance
+ if(pHTInfo->bCurBW40MHz && pHTInfo->bCurTxBW40MHz && !ieee->bandwidth_auto_switch.bforced_tx20Mhz)
+ tcb_desc->bPacketBW = true;
+ return;
+}
+
+static void ieee80211_query_protectionmode(struct ieee80211_device *ieee,
+ cb_desc *tcb_desc,
+ struct sk_buff *skb)
+{
+ // Common Settings
+ tcb_desc->bRTSSTBC = false;
+ tcb_desc->bRTSUseShortGI = false; // Since protection frames are always sent by legacy rate, ShortGI will never be used.
+ tcb_desc->bCTSEnable = false; // Most of protection using RTS/CTS
+ tcb_desc->RTSSC = 0; // 20MHz: Don't care; 40MHz: Duplicate.
+ tcb_desc->bRTSBW = false; // RTS frame bandwidth is always 20MHz
+
+ if(tcb_desc->bBroadcast || tcb_desc->bMulticast)//only unicast frame will use rts/cts
+ return;
+
+ if (is_broadcast_ether_addr(skb->data+16)) //check addr3 as infrastructure add3 is DA.
+ return;
+
+ if (ieee->mode < IEEE_N_24G) //b, g mode
+ {
+ // (1) RTS_Threshold is compared to the MPDU, not MSDU.
+ // (2) If there are more than one frag in this MSDU, only the first frag uses protection frame.
+ // Other fragments are protected by previous fragment.
+ // So we only need to check the length of first fragment.
+ if (skb->len > ieee->rts)
+ {
+ tcb_desc->bRTSEnable = true;
+ tcb_desc->rts_rate = MGN_24M;
+ }
+ else if (ieee->current_network.buseprotection)
+ {
+ // Use CTS-to-SELF in protection mode.
+ tcb_desc->bRTSEnable = true;
+ tcb_desc->bCTSEnable = true;
+ tcb_desc->rts_rate = MGN_24M;
+ }
+ //otherwise return;
+ return;
+ }
+ else
+ {// 11n High throughput case.
+ PRT_HIGH_THROUGHPUT pHTInfo = ieee->pHTInfo;
+ while (true)
+ {
+ //check ERP protection
+ if (ieee->current_network.buseprotection)
+ {// CTS-to-SELF
+ tcb_desc->bRTSEnable = true;
+ tcb_desc->bCTSEnable = true;
+ tcb_desc->rts_rate = MGN_24M;
+ break;
+ }
+ //check HT op mode
+ if(pHTInfo->bCurrentHTSupport && pHTInfo->bEnableHT)
+ {
+ u8 HTOpMode = pHTInfo->CurrentOpMode;
+ if((pHTInfo->bCurBW40MHz && (HTOpMode == 2 || HTOpMode == 3)) ||
+ (!pHTInfo->bCurBW40MHz && HTOpMode == 3) )
+ {
+ tcb_desc->rts_rate = MGN_24M; // Rate is 24Mbps.
+ tcb_desc->bRTSEnable = true;
+ break;
+ }
+ }
+ //check rts
+ if (skb->len > ieee->rts)
+ {
+ tcb_desc->rts_rate = MGN_24M; // Rate is 24Mbps.
+ tcb_desc->bRTSEnable = true;
+ break;
+ }
+ //to do list: check MIMO power save condition.
+ //check AMPDU aggregation for TXOP
+ if(tcb_desc->bAMPDUEnable)
+ {
+ tcb_desc->rts_rate = MGN_24M; // Rate is 24Mbps.
+ // According to 8190 design, firmware sends CF-End only if RTS/CTS is enabled. However, it degrads
+ // throughput around 10M, so we disable of this mechanism. 2007.08.03 by Emily
+ tcb_desc->bRTSEnable = false;
+ break;
+ }
+ //check IOT action
+ if(pHTInfo->IOTAction & HT_IOT_ACT_FORCED_CTS2SELF)
+ {
+ tcb_desc->bCTSEnable = true;
+ tcb_desc->rts_rate = MGN_24M;
+ tcb_desc->bRTSEnable = true;
+ break;
+ }
+ // Totally no protection case!!
+ goto NO_PROTECTION;
+ }
+ }
+ // For test , CTS replace with RTS
+ if (0) {
+ tcb_desc->bCTSEnable = true;
+ tcb_desc->rts_rate = MGN_24M;
+ tcb_desc->bRTSEnable = true;
+ }
+ if (ieee->current_network.capability & WLAN_CAPABILITY_SHORT_PREAMBLE)
+ tcb_desc->bUseShortPreamble = true;
+ if (ieee->mode == IW_MODE_MASTER)
+ goto NO_PROTECTION;
+ return;
+NO_PROTECTION:
+ tcb_desc->bRTSEnable = false;
+ tcb_desc->bCTSEnable = false;
+ tcb_desc->rts_rate = 0;
+ tcb_desc->RTSSC = 0;
+ tcb_desc->bRTSBW = false;
+}
+
+
+static void ieee80211_txrate_selectmode(struct ieee80211_device *ieee,
+ cb_desc *tcb_desc)
+{
+#ifdef TO_DO_LIST
+ if(!IsDataFrame(pFrame))
+ {
+ pTcb->bTxDisableRateFallBack = true;
+ pTcb->bTxUseDriverAssingedRate = true;
+ pTcb->RATRIndex = 7;
+ return;
+ }
+
+ if(pMgntInfo->ForcedDataRate!= 0)
+ {
+ pTcb->bTxDisableRateFallBack = true;
+ pTcb->bTxUseDriverAssingedRate = true;
+ return;
+ }
+#endif
+ if(ieee->bTxDisableRateFallBack)
+ tcb_desc->bTxDisableRateFallBack = true;
+
+ if(ieee->bTxUseDriverAssingedRate)
+ tcb_desc->bTxUseDriverAssingedRate = true;
+ if(!tcb_desc->bTxDisableRateFallBack || !tcb_desc->bTxUseDriverAssingedRate)
+ {
+ if (ieee->iw_mode == IW_MODE_INFRA || ieee->iw_mode == IW_MODE_ADHOC)
+ tcb_desc->RATRIndex = 0;
+ }
+}
+
+static void ieee80211_query_seqnum(struct ieee80211_device *ieee,
+ struct sk_buff *skb, u8 *dst)
+{
+ if (is_multicast_ether_addr(dst))
+ return;
+ if (IsQoSDataFrame(skb->data)) //we deal qos data only
+ {
+ PTX_TS_RECORD pTS = NULL;
+ if (!GetTs(ieee, (PTS_COMMON_INFO *)(&pTS), dst, skb->priority, TX_DIR, true))
+ {
+ return;
+ }
+ pTS->TxCurSeq = (pTS->TxCurSeq+1)%4096;
+ }
+}
+
+int ieee80211_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+ struct ieee80211_device *ieee = netdev_priv(dev);
+ struct ieee80211_txb *txb = NULL;
+ struct ieee80211_hdr_3addrqos *frag_hdr;
+ int i, bytes_per_frag, nr_frags, bytes_last_frag, frag_size;
+ unsigned long flags;
+ struct net_device_stats *stats = &ieee->stats;
+ int ether_type = 0, encrypt;
+ int bytes, fc, qos_ctl = 0, hdr_len;
+ struct sk_buff *skb_frag;
+ struct ieee80211_hdr_3addrqos header = { /* Ensure zero initialized */
+ .duration_id = 0,
+ .seq_ctl = 0,
+ .qos_ctl = 0
+ };
+ u8 dest[ETH_ALEN], src[ETH_ALEN];
+ int qos_actived = ieee->current_network.qos_data.active;
+
+ struct ieee80211_crypt_data *crypt;
+
+ cb_desc *tcb_desc;
+
+ spin_lock_irqsave(&ieee->lock, flags);
+
+ /* If there is no driver handler to take the TXB, dont' bother
+ * creating it... */
+ if ((!ieee->hard_start_xmit && !(ieee->softmac_features & IEEE_SOFTMAC_TX_QUEUE))||
+ ((!ieee->softmac_data_hard_start_xmit && (ieee->softmac_features & IEEE_SOFTMAC_TX_QUEUE)))) {
+ printk(KERN_WARNING "%s: No xmit handler.\n",
+ ieee->dev->name);
+ goto success;
+ }
+
+
+ if(likely(ieee->raw_tx == 0)){
+ if (unlikely(skb->len < SNAP_SIZE + sizeof(u16))) {
+ printk(KERN_WARNING "%s: skb too small (%d).\n",
+ ieee->dev->name, skb->len);
+ goto success;
+ }
+
+ memset(skb->cb, 0, sizeof(skb->cb));
+ ether_type = ntohs(((struct ethhdr *)skb->data)->h_proto);
+
+ crypt = ieee->crypt[ieee->tx_keyidx];
+
+ encrypt = !(ether_type == ETH_P_PAE && ieee->ieee802_1x) &&
+ ieee->host_encrypt && crypt && crypt->ops;
+
+ if (!encrypt && ieee->ieee802_1x &&
+ ieee->drop_unencrypted && ether_type != ETH_P_PAE) {
+ stats->tx_dropped++;
+ goto success;
+ }
+ #ifdef CONFIG_IEEE80211_DEBUG
+ if (crypt && !encrypt && ether_type == ETH_P_PAE) {
+ struct eapol *eap = (struct eapol *)(skb->data +
+ sizeof(struct ethhdr) - SNAP_SIZE - sizeof(u16));
+ IEEE80211_DEBUG_EAP("TX: IEEE 802.11 EAPOL frame: %s\n",
+ eap_get_type(eap->type));
+ }
+ #endif
+
+ /* Save source and destination addresses */
+ memcpy(&dest, skb->data, ETH_ALEN);
+ memcpy(&src, skb->data+ETH_ALEN, ETH_ALEN);
+
+ /* Advance the SKB to the start of the payload */
+ skb_pull(skb, sizeof(struct ethhdr));
+
+ /* Determine total amount of storage required for TXB packets */
+ bytes = skb->len + SNAP_SIZE + sizeof(u16);
+
+ if (encrypt)
+ fc = IEEE80211_FTYPE_DATA | IEEE80211_FCTL_WEP;
+ else
+
+ fc = IEEE80211_FTYPE_DATA;
+
+ //if(ieee->current_network.QoS_Enable)
+ if(qos_actived)
+ fc |= IEEE80211_STYPE_QOS_DATA;
+ else
+ fc |= IEEE80211_STYPE_DATA;
+
+ if (ieee->iw_mode == IW_MODE_INFRA) {
+ fc |= IEEE80211_FCTL_TODS;
+ /* To DS: Addr1 = BSSID, Addr2 = SA,
+ Addr3 = DA */
+ memcpy(&header.addr1, ieee->current_network.bssid, ETH_ALEN);
+ memcpy(&header.addr2, &src, ETH_ALEN);
+ memcpy(&header.addr3, &dest, ETH_ALEN);
+ } else if (ieee->iw_mode == IW_MODE_ADHOC) {
+ /* not From/To DS: Addr1 = DA, Addr2 = SA,
+ Addr3 = BSSID */
+ memcpy(&header.addr1, dest, ETH_ALEN);
+ memcpy(&header.addr2, src, ETH_ALEN);
+ memcpy(&header.addr3, ieee->current_network.bssid, ETH_ALEN);
+ }
+
+ header.frame_ctl = cpu_to_le16(fc);
+
+ /* Determine fragmentation size based on destination (multicast
+ * and broadcast are not fragmented) */
+ if (is_multicast_ether_addr(header.addr1)) {
+ frag_size = MAX_FRAG_THRESHOLD;
+ qos_ctl |= QOS_CTL_NOTCONTAIN_ACK;
+ }
+ else {
+ frag_size = ieee->fts;//default:392
+ qos_ctl = 0;
+ }
+
+ //if (ieee->current_network.QoS_Enable)
+ if(qos_actived)
+ {
+ hdr_len = IEEE80211_3ADDR_LEN + 2;
+
+ skb->priority = ieee80211_classify(skb, &ieee->current_network);
+ qos_ctl |= skb->priority; //set in the ieee80211_classify
+ header.qos_ctl = cpu_to_le16(qos_ctl & IEEE80211_QOS_TID);
+ } else {
+ hdr_len = IEEE80211_3ADDR_LEN;
+ }
+ /* Determine amount of payload per fragment. Regardless of if
+ * this stack is providing the full 802.11 header, one will
+ * eventually be affixed to this fragment -- so we must account for
+ * it when determining the amount of payload space. */
+ bytes_per_frag = frag_size - hdr_len;
+ if (ieee->config &
+ (CFG_IEEE80211_COMPUTE_FCS | CFG_IEEE80211_RESERVE_FCS))
+ bytes_per_frag -= IEEE80211_FCS_LEN;
+
+ /* Each fragment may need to have room for encryption pre/postfix */
+ if (encrypt)
+ bytes_per_frag -= crypt->ops->extra_prefix_len +
+ crypt->ops->extra_postfix_len;
+
+ /* Number of fragments is the total bytes_per_frag /
+ * payload_per_fragment */
+ nr_frags = bytes / bytes_per_frag;
+ bytes_last_frag = bytes % bytes_per_frag;
+ if (bytes_last_frag)
+ nr_frags++;
+ else
+ bytes_last_frag = bytes_per_frag;
+
+ /* When we allocate the TXB we allocate enough space for the reserve
+ * and full fragment bytes (bytes_per_frag doesn't include prefix,
+ * postfix, header, FCS, etc.) */
+ txb = ieee80211_alloc_txb(nr_frags, frag_size + ieee->tx_headroom, GFP_ATOMIC);
+ if (unlikely(!txb)) {
+ printk(KERN_WARNING "%s: Could not allocate TXB\n",
+ ieee->dev->name);
+ goto failed;
+ }
+ txb->encrypted = encrypt;
+ txb->payload_size = bytes;
+
+ //if (ieee->current_network.QoS_Enable)
+ if(qos_actived)
+ {
+ txb->queue_index = UP2AC(skb->priority);
+ } else {
+ txb->queue_index = WME_AC_BK;
+ }
+
+
+
+ for (i = 0; i < nr_frags; i++) {
+ skb_frag = txb->fragments[i];
+ tcb_desc = (cb_desc *)(skb_frag->cb + MAX_DEV_ADDR_SIZE);
+ if(qos_actived){
+ skb_frag->priority = skb->priority;//UP2AC(skb->priority);
+ tcb_desc->queue_index = UP2AC(skb->priority);
+ } else {
+ skb_frag->priority = WME_AC_BK;
+ tcb_desc->queue_index = WME_AC_BK;
+ }
+ skb_reserve(skb_frag, ieee->tx_headroom);
+
+ if (encrypt){
+ if (ieee->hwsec_active)
+ tcb_desc->bHwSec = 1;
+ else
+ tcb_desc->bHwSec = 0;
+ skb_reserve(skb_frag, crypt->ops->extra_prefix_len);
+ }
+ else
+ {
+ tcb_desc->bHwSec = 0;
+ }
+ frag_hdr = (struct ieee80211_hdr_3addrqos *)skb_put(skb_frag, hdr_len);
+ memcpy(frag_hdr, &header, hdr_len);
+
+ /* If this is not the last fragment, then add the MOREFRAGS
+ * bit to the frame control */
+ if (i != nr_frags - 1) {
+ frag_hdr->frame_ctl = cpu_to_le16(
+ fc | IEEE80211_FCTL_MOREFRAGS);
+ bytes = bytes_per_frag;
+
+ } else {
+ /* The last fragment takes the remaining length */
+ bytes = bytes_last_frag;
+ }
+ //if(ieee->current_network.QoS_Enable)
+ if(qos_actived)
+ {
+ // add 1 only indicate to corresponding seq number control 2006/7/12
+ frag_hdr->seq_ctl = cpu_to_le16(ieee->seq_ctrl[UP2AC(skb->priority)+1]<<4 | i);
+ } else {
+ frag_hdr->seq_ctl = cpu_to_le16(ieee->seq_ctrl[0]<<4 | i);
+ }
+
+ /* Put a SNAP header on the first fragment */
+ if (i == 0) {
+ ieee80211_put_snap(
+ skb_put(skb_frag, SNAP_SIZE + sizeof(u16)),
+ ether_type);
+ bytes -= SNAP_SIZE + sizeof(u16);
+ }
+
+ memcpy(skb_put(skb_frag, bytes), skb->data, bytes);
+
+ /* Advance the SKB... */
+ skb_pull(skb, bytes);
+
+ /* Encryption routine will move the header forward in order
+ * to insert the IV between the header and the payload */
+ if (encrypt)
+ ieee80211_encrypt_fragment(ieee, skb_frag, hdr_len);
+ if (ieee->config &
+ (CFG_IEEE80211_COMPUTE_FCS | CFG_IEEE80211_RESERVE_FCS))
+ skb_put(skb_frag, 4);
+ }
+
+ if(qos_actived)
+ {
+ if (ieee->seq_ctrl[UP2AC(skb->priority) + 1] == 0xFFF)
+ ieee->seq_ctrl[UP2AC(skb->priority) + 1] = 0;
+ else
+ ieee->seq_ctrl[UP2AC(skb->priority) + 1]++;
+ } else {
+ if (ieee->seq_ctrl[0] == 0xFFF)
+ ieee->seq_ctrl[0] = 0;
+ else
+ ieee->seq_ctrl[0]++;
+ }
+ }else{
+ if (unlikely(skb->len < sizeof(struct ieee80211_hdr_3addr))) {
+ printk(KERN_WARNING "%s: skb too small (%d).\n",
+ ieee->dev->name, skb->len);
+ goto success;
+ }
+
+ txb = ieee80211_alloc_txb(1, skb->len, GFP_ATOMIC);
+ if(!txb){
+ printk(KERN_WARNING "%s: Could not allocate TXB\n",
+ ieee->dev->name);
+ goto failed;
+ }
+
+ txb->encrypted = 0;
+ txb->payload_size = skb->len;
+ memcpy(skb_put(txb->fragments[0],skb->len), skb->data, skb->len);
+ }
+
+ success:
+//WB add to fill data tcb_desc here. only first fragment is considered, need to change, and you may remove to other place.
+ if (txb)
+ {
+ cb_desc *tcb_desc = (cb_desc *)(txb->fragments[0]->cb + MAX_DEV_ADDR_SIZE);
+ tcb_desc->bTxEnableFwCalcDur = 1;
+ if (is_multicast_ether_addr(header.addr1))
+ tcb_desc->bMulticast = 1;
+ if (is_broadcast_ether_addr(header.addr1))
+ tcb_desc->bBroadcast = 1;
+ ieee80211_txrate_selectmode(ieee, tcb_desc);
+ if (tcb_desc->bMulticast || tcb_desc->bBroadcast)
+ tcb_desc->data_rate = ieee->basic_rate;
+ else
+ //tcb_desc->data_rate = CURRENT_RATE(ieee->current_network.mode, ieee->rate, ieee->HTCurrentOperaRate);
+ tcb_desc->data_rate = CURRENT_RATE(ieee->mode, ieee->rate, ieee->HTCurrentOperaRate);
+ ieee80211_qurey_ShortPreambleMode(ieee, tcb_desc);
+ ieee80211_tx_query_agg_cap(ieee, txb->fragments[0], tcb_desc);
+ ieee80211_query_HTCapShortGI(ieee, tcb_desc);
+ ieee80211_query_BandwidthMode(ieee, tcb_desc);
+ ieee80211_query_protectionmode(ieee, tcb_desc, txb->fragments[0]);
+ ieee80211_query_seqnum(ieee, txb->fragments[0], header.addr1);
+// IEEE80211_DEBUG_DATA(IEEE80211_DL_DATA, txb->fragments[0]->data, txb->fragments[0]->len);
+ //IEEE80211_DEBUG_DATA(IEEE80211_DL_DATA, tcb_desc, sizeof(cb_desc));
+ }
+ spin_unlock_irqrestore(&ieee->lock, flags);
+ dev_kfree_skb_any(skb);
+ if (txb) {
+ if (ieee->softmac_features & IEEE_SOFTMAC_TX_QUEUE){
+ ieee80211_softmac_xmit(txb, ieee);
+ }else{
+ if ((*ieee->hard_start_xmit)(txb, dev) == 0) {
+ stats->tx_packets++;
+ stats->tx_bytes += txb->payload_size;
+ return 0;
+ }
+ ieee80211_txb_free(txb);
+ }
+ }
+
+ return 0;
+
+ failed:
+ spin_unlock_irqrestore(&ieee->lock, flags);
+ netif_stop_queue(dev);
+ stats->tx_errors++;
+ return 1;
+
+}
diff --git a/drivers/staging/rtl8192u/ieee80211/ieee80211_wx.c b/drivers/staging/rtl8192u/ieee80211/ieee80211_wx.c
new file mode 100644
index 000000000..ae1b3cf28
--- /dev/null
+++ b/drivers/staging/rtl8192u/ieee80211/ieee80211_wx.c
@@ -0,0 +1,852 @@
+/******************************************************************************
+
+ Copyright(c) 2004 Intel Corporation. All rights reserved.
+
+ Portions of this file are based on the WEP enablement code provided by the
+ Host AP project hostap-drivers v0.1.3
+ Copyright (c) 2001-2002, SSH Communications Security Corp and Jouni Malinen
+ <jkmaline@cc.hut.fi>
+ Copyright (c) 2002-2003, Jouni Malinen <jkmaline@cc.hut.fi>
+
+ This program is free software; you can redistribute it and/or modify it
+ under the terms of version 2 of the GNU General Public License as
+ published by the Free Software Foundation.
+
+ 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.
+
+ The full GNU General Public License is included in this distribution in the
+ file called LICENSE.
+
+ Contact Information:
+ James P. Ketrenos <ipw2100-admin@linux.intel.com>
+ Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+
+******************************************************************************/
+#include <linux/wireless.h>
+#include <linux/kmod.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+
+#include "ieee80211.h"
+struct modes_unit {
+ char *mode_string;
+ int mode_size;
+};
+static struct modes_unit ieee80211_modes[] = {
+ {"a", 1},
+ {"b", 1},
+ {"g", 1},
+ {"?", 1},
+ {"N-24G", 5},
+ {"N-5G", 4},
+};
+
+#define iwe_stream_add_event_rsl iwe_stream_add_event
+
+#define MAX_CUSTOM_LEN 64
+static inline char *rtl819x_translate_scan(struct ieee80211_device *ieee,
+ char *start, char *stop,
+ struct ieee80211_network *network,
+ struct iw_request_info *info)
+{
+ char custom[MAX_CUSTOM_LEN];
+ char proto_name[IFNAMSIZ];
+ char *pname = proto_name;
+ char *p;
+ struct iw_event iwe;
+ int i, j;
+ u16 max_rate, rate;
+ static u8 EWC11NHTCap[] = {0x00, 0x90, 0x4c, 0x33};
+
+ /* First entry *MUST* be the AP MAC address */
+ iwe.cmd = SIOCGIWAP;
+ iwe.u.ap_addr.sa_family = ARPHRD_ETHER;
+ memcpy(iwe.u.ap_addr.sa_data, network->bssid, ETH_ALEN);
+ start = iwe_stream_add_event_rsl(info, start, stop, &iwe, IW_EV_ADDR_LEN);
+ /* Remaining entries will be displayed in the order we provide them */
+
+ /* Add the ESSID */
+ iwe.cmd = SIOCGIWESSID;
+ iwe.u.data.flags = 1;
+// if (network->flags & NETWORK_EMPTY_ESSID) {
+ if (network->ssid_len == 0) {
+ iwe.u.data.length = sizeof("<hidden>");
+ start = iwe_stream_add_point(info, start, stop, &iwe, "<hidden>");
+ } else {
+ iwe.u.data.length = min(network->ssid_len, (u8)32);
+ start = iwe_stream_add_point(info, start, stop, &iwe, network->ssid);
+ }
+ /* Add the protocol name */
+ iwe.cmd = SIOCGIWNAME;
+ for(i=0; i<ARRAY_SIZE(ieee80211_modes); i++) {
+ if(network->mode&(1<<i)) {
+ sprintf(pname,ieee80211_modes[i].mode_string,ieee80211_modes[i].mode_size);
+ pname +=ieee80211_modes[i].mode_size;
+ }
+ }
+ *pname = '\0';
+ snprintf(iwe.u.name, IFNAMSIZ, "IEEE802.11%s", proto_name);
+ start = iwe_stream_add_event_rsl(info, start, stop, &iwe, IW_EV_CHAR_LEN);
+ /* Add mode */
+ iwe.cmd = SIOCGIWMODE;
+ if (network->capability &
+ (WLAN_CAPABILITY_BSS | WLAN_CAPABILITY_IBSS)) {
+ if (network->capability & WLAN_CAPABILITY_BSS)
+ iwe.u.mode = IW_MODE_MASTER;
+ else
+ iwe.u.mode = IW_MODE_ADHOC;
+ start = iwe_stream_add_event_rsl(info, start, stop, &iwe, IW_EV_UINT_LEN);
+ }
+
+ /* Add frequency/channel */
+ iwe.cmd = SIOCGIWFREQ;
+/* iwe.u.freq.m = ieee80211_frequency(network->channel, network->mode);
+ iwe.u.freq.e = 3; */
+ iwe.u.freq.m = network->channel;
+ iwe.u.freq.e = 0;
+ iwe.u.freq.i = 0;
+ start = iwe_stream_add_event_rsl(info, start, stop, &iwe, IW_EV_FREQ_LEN);
+ /* Add encryption capability */
+ iwe.cmd = SIOCGIWENCODE;
+ if (network->capability & WLAN_CAPABILITY_PRIVACY)
+ iwe.u.data.flags = IW_ENCODE_ENABLED | IW_ENCODE_NOKEY;
+ else
+ iwe.u.data.flags = IW_ENCODE_DISABLED;
+ iwe.u.data.length = 0;
+ start = iwe_stream_add_point(info, start, stop, &iwe, network->ssid);
+ /* Add basic and extended rates */
+ max_rate = 0;
+ p = custom;
+ p += snprintf(p, MAX_CUSTOM_LEN - (p - custom), " Rates (Mb/s): ");
+ for (i = 0, j = 0; i < network->rates_len; ) {
+ if (j < network->rates_ex_len &&
+ ((network->rates_ex[j] & 0x7F) <
+ (network->rates[i] & 0x7F)))
+ rate = network->rates_ex[j++] & 0x7F;
+ else
+ rate = network->rates[i++] & 0x7F;
+ if (rate > max_rate)
+ max_rate = rate;
+ p += snprintf(p, MAX_CUSTOM_LEN - (p - custom),
+ "%d%s ", rate >> 1, (rate & 1) ? ".5" : "");
+ }
+ for (; j < network->rates_ex_len; j++) {
+ rate = network->rates_ex[j] & 0x7F;
+ p += snprintf(p, MAX_CUSTOM_LEN - (p - custom),
+ "%d%s ", rate >> 1, (rate & 1) ? ".5" : "");
+ if (rate > max_rate)
+ max_rate = rate;
+ }
+
+ if (network->mode >= IEEE_N_24G)//add N rate here;
+ {
+ PHT_CAPABILITY_ELE ht_cap = NULL;
+ bool is40M = false, isShortGI = false;
+ u8 max_mcs = 0;
+ if (!memcmp(network->bssht.bdHTCapBuf, EWC11NHTCap, 4))
+ ht_cap = (PHT_CAPABILITY_ELE)&network->bssht.bdHTCapBuf[4];
+ else
+ ht_cap = (PHT_CAPABILITY_ELE)&network->bssht.bdHTCapBuf[0];
+ is40M = (ht_cap->ChlWidth)?1:0;
+ isShortGI = (ht_cap->ChlWidth)?
+ ((ht_cap->ShortGI40Mhz)?1:0):
+ ((ht_cap->ShortGI20Mhz)?1:0);
+
+ max_mcs = HTGetHighestMCSRate(ieee, ht_cap->MCS, MCS_FILTER_ALL);
+ rate = MCS_DATA_RATE[is40M][isShortGI][max_mcs&0x7f];
+ if (rate > max_rate)
+ max_rate = rate;
+ }
+ iwe.cmd = SIOCGIWRATE;
+ iwe.u.bitrate.fixed = iwe.u.bitrate.disabled = 0;
+ iwe.u.bitrate.value = max_rate * 500000;
+ start = iwe_stream_add_event_rsl(info, start, stop, &iwe,
+ IW_EV_PARAM_LEN);
+ iwe.cmd = IWEVCUSTOM;
+ iwe.u.data.length = p - custom;
+ if (iwe.u.data.length)
+ start = iwe_stream_add_point(info, start, stop, &iwe, custom);
+ /* Add quality statistics */
+ /* TODO: Fix these values... */
+ iwe.cmd = IWEVQUAL;
+ iwe.u.qual.qual = network->stats.signal;
+ iwe.u.qual.level = network->stats.rssi;
+ iwe.u.qual.noise = network->stats.noise;
+ iwe.u.qual.updated = network->stats.mask & IEEE80211_STATMASK_WEMASK;
+ if (!(network->stats.mask & IEEE80211_STATMASK_RSSI))
+ iwe.u.qual.updated |= IW_QUAL_LEVEL_INVALID;
+ if (!(network->stats.mask & IEEE80211_STATMASK_NOISE))
+ iwe.u.qual.updated |= IW_QUAL_NOISE_INVALID;
+ if (!(network->stats.mask & IEEE80211_STATMASK_SIGNAL))
+ iwe.u.qual.updated |= IW_QUAL_QUAL_INVALID;
+ iwe.u.qual.updated = 7;
+ start = iwe_stream_add_event_rsl(info, start, stop, &iwe, IW_EV_QUAL_LEN);
+ iwe.cmd = IWEVCUSTOM;
+ p = custom;
+
+ iwe.u.data.length = p - custom;
+ if (iwe.u.data.length)
+ start = iwe_stream_add_point(info, start, stop, &iwe, custom);
+
+ if (ieee->wpa_enabled && network->wpa_ie_len){
+ char buf[MAX_WPA_IE_LEN * 2 + 30];
+ // printk("WPA IE\n");
+ u8 *p = buf;
+ p += sprintf(p, "wpa_ie=");
+ for (i = 0; i < network->wpa_ie_len; i++) {
+ p += sprintf(p, "%02x", network->wpa_ie[i]);
+ }
+
+ memset(&iwe, 0, sizeof(iwe));
+ iwe.cmd = IWEVCUSTOM;
+ iwe.u.data.length = strlen(buf);
+ start = iwe_stream_add_point(info, start, stop, &iwe, buf);
+ }
+
+ if (ieee->wpa_enabled && network->rsn_ie_len){
+ char buf[MAX_WPA_IE_LEN * 2 + 30];
+
+ u8 *p = buf;
+ p += sprintf(p, "rsn_ie=");
+ for (i = 0; i < network->rsn_ie_len; i++) {
+ p += sprintf(p, "%02x", network->rsn_ie[i]);
+ }
+
+ memset(&iwe, 0, sizeof(iwe));
+ iwe.cmd = IWEVCUSTOM;
+ iwe.u.data.length = strlen(buf);
+ start = iwe_stream_add_point(info, start, stop, &iwe, buf);
+ }
+
+
+ /* Add EXTRA: Age to display seconds since last beacon/probe response
+ * for given network. */
+ iwe.cmd = IWEVCUSTOM;
+ p = custom;
+ p += snprintf(p, MAX_CUSTOM_LEN - (p - custom),
+ " Last beacon: %lums ago", (jiffies - network->last_scanned) / (HZ / 100));
+ iwe.u.data.length = p - custom;
+ if (iwe.u.data.length)
+ start = iwe_stream_add_point(info, start, stop, &iwe, custom);
+
+ return start;
+}
+
+int ieee80211_wx_get_scan(struct ieee80211_device *ieee,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct ieee80211_network *network;
+ unsigned long flags;
+
+ char *ev = extra;
+// char *stop = ev + IW_SCAN_MAX_DATA;
+ char *stop = ev + wrqu->data.length;//IW_SCAN_MAX_DATA;
+ //char *stop = ev + IW_SCAN_MAX_DATA;
+ int i = 0;
+ int err = 0;
+ IEEE80211_DEBUG_WX("Getting scan\n");
+ down(&ieee->wx_sem);
+ spin_lock_irqsave(&ieee->lock, flags);
+
+ list_for_each_entry(network, &ieee->network_list, list) {
+ i++;
+ if((stop-ev)<200)
+ {
+ err = -E2BIG;
+ break;
+ }
+ if (ieee->scan_age == 0 ||
+ time_after(network->last_scanned + ieee->scan_age, jiffies))
+ ev = rtl819x_translate_scan(ieee, ev, stop, network, info);
+ else
+ IEEE80211_DEBUG_SCAN(
+ "Not showing network '%s ("
+ "%pM)' due to age (%lums).\n",
+ escape_essid(network->ssid,
+ network->ssid_len),
+ network->bssid,
+ (jiffies - network->last_scanned) / (HZ / 100));
+ }
+
+ spin_unlock_irqrestore(&ieee->lock, flags);
+ up(&ieee->wx_sem);
+ wrqu->data.length = ev - extra;
+ wrqu->data.flags = 0;
+
+ IEEE80211_DEBUG_WX("exit: %d networks returned.\n", i);
+
+ return err;
+}
+EXPORT_SYMBOL(ieee80211_wx_get_scan);
+
+int ieee80211_wx_set_encode(struct ieee80211_device *ieee,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *keybuf)
+{
+ struct iw_point *erq = &(wrqu->encoding);
+ struct net_device *dev = ieee->dev;
+ struct ieee80211_security sec = {
+ .flags = 0
+ };
+ int i, key, key_provided, len;
+ struct ieee80211_crypt_data **crypt;
+
+ IEEE80211_DEBUG_WX("SET_ENCODE\n");
+
+ key = erq->flags & IW_ENCODE_INDEX;
+ if (key) {
+ if (key > WEP_KEYS)
+ return -EINVAL;
+ key--;
+ key_provided = 1;
+ } else {
+ key_provided = 0;
+ key = ieee->tx_keyidx;
+ }
+
+ IEEE80211_DEBUG_WX("Key: %d [%s]\n", key, key_provided ?
+ "provided" : "default");
+ crypt = &ieee->crypt[key];
+
+ if (erq->flags & IW_ENCODE_DISABLED) {
+ if (key_provided && *crypt) {
+ IEEE80211_DEBUG_WX("Disabling encryption on key %d.\n",
+ key);
+ ieee80211_crypt_delayed_deinit(ieee, crypt);
+ } else
+ IEEE80211_DEBUG_WX("Disabling encryption.\n");
+
+ /* Check all the keys to see if any are still configured,
+ * and if no key index was provided, de-init them all */
+ for (i = 0; i < WEP_KEYS; i++) {
+ if (ieee->crypt[i] != NULL) {
+ if (key_provided)
+ break;
+ ieee80211_crypt_delayed_deinit(
+ ieee, &ieee->crypt[i]);
+ }
+ }
+
+ if (i == WEP_KEYS) {
+ sec.enabled = 0;
+ sec.level = SEC_LEVEL_0;
+ sec.flags |= SEC_ENABLED | SEC_LEVEL;
+ }
+
+ goto done;
+ }
+
+
+
+ sec.enabled = 1;
+ sec.flags |= SEC_ENABLED;
+
+ if (*crypt != NULL && (*crypt)->ops != NULL &&
+ strcmp((*crypt)->ops->name, "WEP") != 0) {
+ /* changing to use WEP; deinit previously used algorithm
+ * on this key */
+ ieee80211_crypt_delayed_deinit(ieee, crypt);
+ }
+
+ if (*crypt == NULL) {
+ struct ieee80211_crypt_data *new_crypt;
+
+ /* take WEP into use */
+ new_crypt = kzalloc(sizeof(struct ieee80211_crypt_data),
+ GFP_KERNEL);
+ if (new_crypt == NULL)
+ return -ENOMEM;
+ new_crypt->ops = ieee80211_get_crypto_ops("WEP");
+ if (!new_crypt->ops) {
+ request_module("ieee80211_crypt_wep");
+ new_crypt->ops = ieee80211_get_crypto_ops("WEP");
+ }
+ if (new_crypt->ops && try_module_get(new_crypt->ops->owner))
+ new_crypt->priv = new_crypt->ops->init(key);
+
+ if (!new_crypt->ops || !new_crypt->priv) {
+ kfree(new_crypt);
+ new_crypt = NULL;
+
+ printk(KERN_WARNING "%s: could not initialize WEP: "
+ "load module ieee80211_crypt_wep\n",
+ dev->name);
+ return -EOPNOTSUPP;
+ }
+ *crypt = new_crypt;
+ }
+
+ /* If a new key was provided, set it up */
+ if (erq->length > 0) {
+ len = erq->length <= 5 ? 5 : 13;
+ memcpy(sec.keys[key], keybuf, erq->length);
+ if (len > erq->length)
+ memset(sec.keys[key] + erq->length, 0,
+ len - erq->length);
+ IEEE80211_DEBUG_WX("Setting key %d to '%s' (%d:%d bytes)\n",
+ key, escape_essid(sec.keys[key], len),
+ erq->length, len);
+ sec.key_sizes[key] = len;
+ (*crypt)->ops->set_key(sec.keys[key], len, NULL,
+ (*crypt)->priv);
+ sec.flags |= (1 << key);
+ /* This ensures a key will be activated if no key is
+ * explicitely set */
+ if (key == sec.active_key)
+ sec.flags |= SEC_ACTIVE_KEY;
+ ieee->tx_keyidx = key;
+
+ } else {
+ len = (*crypt)->ops->get_key(sec.keys[key], WEP_KEY_LEN,
+ NULL, (*crypt)->priv);
+ if (len == 0) {
+ /* Set a default key of all 0 */
+ printk("Setting key %d to all zero.\n",
+ key);
+
+ IEEE80211_DEBUG_WX("Setting key %d to all zero.\n",
+ key);
+ memset(sec.keys[key], 0, 13);
+ (*crypt)->ops->set_key(sec.keys[key], 13, NULL,
+ (*crypt)->priv);
+ sec.key_sizes[key] = 13;
+ sec.flags |= (1 << key);
+ }
+
+ /* No key data - just set the default TX key index */
+ if (key_provided) {
+ IEEE80211_DEBUG_WX(
+ "Setting key %d to default Tx key.\n", key);
+ ieee->tx_keyidx = key;
+ sec.active_key = key;
+ sec.flags |= SEC_ACTIVE_KEY;
+ }
+ }
+
+ done:
+ ieee->open_wep = !(erq->flags & IW_ENCODE_RESTRICTED);
+ ieee->auth_mode = ieee->open_wep ? WLAN_AUTH_OPEN : WLAN_AUTH_SHARED_KEY;
+ sec.auth_mode = ieee->open_wep ? WLAN_AUTH_OPEN : WLAN_AUTH_SHARED_KEY;
+ sec.flags |= SEC_AUTH_MODE;
+ IEEE80211_DEBUG_WX("Auth: %s\n", sec.auth_mode == WLAN_AUTH_OPEN ?
+ "OPEN" : "SHARED KEY");
+
+ /* For now we just support WEP, so only set that security level...
+ * TODO: When WPA is added this is one place that needs to change */
+ sec.flags |= SEC_LEVEL;
+ sec.level = SEC_LEVEL_1; /* 40 and 104 bit WEP */
+
+ if (ieee->set_security)
+ ieee->set_security(dev, &sec);
+
+ /* Do not reset port if card is in Managed mode since resetting will
+ * generate new IEEE 802.11 authentication which may end up in looping
+ * with IEEE 802.1X. If your hardware requires a reset after WEP
+ * configuration (for example... Prism2), implement the reset_port in
+ * the callbacks structures used to initialize the 802.11 stack. */
+ if (ieee->reset_on_keychange &&
+ ieee->iw_mode != IW_MODE_INFRA &&
+ ieee->reset_port && ieee->reset_port(dev)) {
+ printk(KERN_DEBUG "%s: reset_port failed\n", dev->name);
+ return -EINVAL;
+ }
+ return 0;
+}
+EXPORT_SYMBOL(ieee80211_wx_set_encode);
+
+int ieee80211_wx_get_encode(struct ieee80211_device *ieee,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *keybuf)
+{
+ struct iw_point *erq = &(wrqu->encoding);
+ int len, key;
+ struct ieee80211_crypt_data *crypt;
+
+ IEEE80211_DEBUG_WX("GET_ENCODE\n");
+
+ if(ieee->iw_mode == IW_MODE_MONITOR)
+ return -1;
+
+ key = erq->flags & IW_ENCODE_INDEX;
+ if (key) {
+ if (key > WEP_KEYS)
+ return -EINVAL;
+ key--;
+ } else
+ key = ieee->tx_keyidx;
+
+ crypt = ieee->crypt[key];
+ erq->flags = key + 1;
+
+ if (crypt == NULL || crypt->ops == NULL) {
+ erq->length = 0;
+ erq->flags |= IW_ENCODE_DISABLED;
+ return 0;
+ }
+ len = crypt->ops->get_key(keybuf, SCM_KEY_LEN, NULL, crypt->priv);
+ erq->length = (len >= 0 ? len : 0);
+
+ erq->flags |= IW_ENCODE_ENABLED;
+
+ if (ieee->open_wep)
+ erq->flags |= IW_ENCODE_OPEN;
+ else
+ erq->flags |= IW_ENCODE_RESTRICTED;
+
+ return 0;
+}
+EXPORT_SYMBOL(ieee80211_wx_get_encode);
+
+int ieee80211_wx_set_encode_ext(struct ieee80211_device *ieee,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ int ret = 0;
+ struct net_device *dev = ieee->dev;
+ struct iw_point *encoding = &wrqu->encoding;
+ struct iw_encode_ext *ext = (struct iw_encode_ext *)extra;
+ int i, idx;
+ int group_key = 0;
+ const char *alg, *module;
+ struct ieee80211_crypto_ops *ops;
+ struct ieee80211_crypt_data **crypt;
+
+ struct ieee80211_security sec = {
+ .flags = 0,
+ };
+ //printk("======>encoding flag:%x,ext flag:%x, ext alg:%d\n", encoding->flags,ext->ext_flags, ext->alg);
+ idx = encoding->flags & IW_ENCODE_INDEX;
+ if (idx) {
+ if (idx < 1 || idx > WEP_KEYS)
+ return -EINVAL;
+ idx--;
+ } else
+ idx = ieee->tx_keyidx;
+
+ if (ext->ext_flags & IW_ENCODE_EXT_GROUP_KEY) {
+
+ crypt = &ieee->crypt[idx];
+
+ group_key = 1;
+ } else {
+ /* some Cisco APs use idx>0 for unicast in dynamic WEP */
+ //printk("not group key, flags:%x, ext->alg:%d\n", ext->ext_flags, ext->alg);
+ if (idx != 0 && ext->alg != IW_ENCODE_ALG_WEP)
+ return -EINVAL;
+ if (ieee->iw_mode == IW_MODE_INFRA)
+
+ crypt = &ieee->crypt[idx];
+
+ else
+ return -EINVAL;
+ }
+
+ sec.flags |= SEC_ENABLED;// | SEC_ENCRYPT;
+ if ((encoding->flags & IW_ENCODE_DISABLED) ||
+ ext->alg == IW_ENCODE_ALG_NONE) {
+ if (*crypt)
+ ieee80211_crypt_delayed_deinit(ieee, crypt);
+
+ for (i = 0; i < WEP_KEYS; i++)
+
+ if (ieee->crypt[i] != NULL)
+
+ break;
+
+ if (i == WEP_KEYS) {
+ sec.enabled = 0;
+ // sec.encrypt = 0;
+ sec.level = SEC_LEVEL_0;
+ sec.flags |= SEC_LEVEL;
+ }
+ //printk("disabled: flag:%x\n", encoding->flags);
+ goto done;
+ }
+
+ sec.enabled = 1;
+ // sec.encrypt = 1;
+ switch (ext->alg) {
+ case IW_ENCODE_ALG_WEP:
+ alg = "WEP";
+ module = "ieee80211_crypt_wep";
+ break;
+ case IW_ENCODE_ALG_TKIP:
+ alg = "TKIP";
+ module = "ieee80211_crypt_tkip";
+ break;
+ case IW_ENCODE_ALG_CCMP:
+ alg = "CCMP";
+ module = "ieee80211_crypt_ccmp";
+ break;
+ default:
+ IEEE80211_DEBUG_WX("%s: unknown crypto alg %d\n",
+ dev->name, ext->alg);
+ ret = -EINVAL;
+ goto done;
+ }
+ printk("alg name:%s\n",alg);
+
+ ops = ieee80211_get_crypto_ops(alg);
+ if (ops == NULL) {
+ request_module(module);
+ ops = ieee80211_get_crypto_ops(alg);
+ }
+ if (ops == NULL) {
+ IEEE80211_DEBUG_WX("%s: unknown crypto alg %d\n",
+ dev->name, ext->alg);
+ printk("========>unknown crypto alg %d\n", ext->alg);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ if (*crypt == NULL || (*crypt)->ops != ops) {
+ struct ieee80211_crypt_data *new_crypt;
+
+ ieee80211_crypt_delayed_deinit(ieee, crypt);
+
+ new_crypt = kzalloc(sizeof(*new_crypt), GFP_KERNEL);
+ if (new_crypt == NULL) {
+ ret = -ENOMEM;
+ goto done;
+ }
+ new_crypt->ops = ops;
+ if (new_crypt->ops && try_module_get(new_crypt->ops->owner))
+ new_crypt->priv = new_crypt->ops->init(idx);
+ if (new_crypt->priv == NULL) {
+ kfree(new_crypt);
+ ret = -EINVAL;
+ goto done;
+ }
+ *crypt = new_crypt;
+
+ }
+
+ if (ext->key_len > 0 && (*crypt)->ops->set_key &&
+ (*crypt)->ops->set_key(ext->key, ext->key_len, ext->rx_seq,
+ (*crypt)->priv) < 0) {
+ IEEE80211_DEBUG_WX("%s: key setting failed\n", dev->name);
+ printk("key setting failed\n");
+ ret = -EINVAL;
+ goto done;
+ }
+ //skip_host_crypt:
+ //printk("skip_host_crypt:ext_flags:%x\n", ext->ext_flags);
+ if (ext->ext_flags & IW_ENCODE_EXT_SET_TX_KEY) {
+ ieee->tx_keyidx = idx;
+ sec.active_key = idx;
+ sec.flags |= SEC_ACTIVE_KEY;
+ }
+
+ if (ext->alg != IW_ENCODE_ALG_NONE) {
+ //memcpy(sec.keys[idx], ext->key, ext->key_len);
+ sec.key_sizes[idx] = ext->key_len;
+ sec.flags |= (1 << idx);
+ if (ext->alg == IW_ENCODE_ALG_WEP) {
+ // sec.encode_alg[idx] = SEC_ALG_WEP;
+ sec.flags |= SEC_LEVEL;
+ sec.level = SEC_LEVEL_1;
+ } else if (ext->alg == IW_ENCODE_ALG_TKIP) {
+ // sec.encode_alg[idx] = SEC_ALG_TKIP;
+ sec.flags |= SEC_LEVEL;
+ sec.level = SEC_LEVEL_2;
+ } else if (ext->alg == IW_ENCODE_ALG_CCMP) {
+ // sec.encode_alg[idx] = SEC_ALG_CCMP;
+ sec.flags |= SEC_LEVEL;
+ sec.level = SEC_LEVEL_3;
+ }
+ /* Don't set sec level for group keys. */
+ if (group_key)
+ sec.flags &= ~SEC_LEVEL;
+ }
+done:
+ if (ieee->set_security)
+ ieee->set_security(ieee->dev, &sec);
+
+ if (ieee->reset_on_keychange &&
+ ieee->iw_mode != IW_MODE_INFRA &&
+ ieee->reset_port && ieee->reset_port(dev)) {
+ IEEE80211_DEBUG_WX("%s: reset_port failed\n", dev->name);
+ return -EINVAL;
+ }
+ return ret;
+}
+EXPORT_SYMBOL(ieee80211_wx_set_encode_ext);
+
+int ieee80211_wx_get_encode_ext(struct ieee80211_device *ieee,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct iw_point *encoding = &wrqu->encoding;
+ struct iw_encode_ext *ext = (struct iw_encode_ext *)extra;
+ struct ieee80211_crypt_data *crypt;
+ int idx, max_key_len;
+
+ max_key_len = encoding->length - sizeof(*ext);
+ if (max_key_len < 0)
+ return -EINVAL;
+
+ idx = encoding->flags & IW_ENCODE_INDEX;
+ if (idx) {
+ if (idx < 1 || idx > WEP_KEYS)
+ return -EINVAL;
+ idx--;
+ } else
+ idx = ieee->tx_keyidx;
+
+ if (!(ext->ext_flags & IW_ENCODE_EXT_GROUP_KEY) &&
+ ext->alg != IW_ENCODE_ALG_WEP)
+ if (idx != 0 || ieee->iw_mode != IW_MODE_INFRA)
+ return -EINVAL;
+
+ crypt = ieee->crypt[idx];
+ encoding->flags = idx + 1;
+ memset(ext, 0, sizeof(*ext));
+
+ if (crypt == NULL || crypt->ops == NULL ) {
+ ext->alg = IW_ENCODE_ALG_NONE;
+ ext->key_len = 0;
+ encoding->flags |= IW_ENCODE_DISABLED;
+ } else {
+ if (strcmp(crypt->ops->name, "WEP") == 0 )
+ ext->alg = IW_ENCODE_ALG_WEP;
+ else if (strcmp(crypt->ops->name, "TKIP"))
+ ext->alg = IW_ENCODE_ALG_TKIP;
+ else if (strcmp(crypt->ops->name, "CCMP"))
+ ext->alg = IW_ENCODE_ALG_CCMP;
+ else
+ return -EINVAL;
+ ext->key_len = crypt->ops->get_key(ext->key, SCM_KEY_LEN, NULL, crypt->priv);
+ encoding->flags |= IW_ENCODE_ENABLED;
+ if (ext->key_len &&
+ (ext->alg == IW_ENCODE_ALG_TKIP ||
+ ext->alg == IW_ENCODE_ALG_CCMP))
+ ext->ext_flags |= IW_ENCODE_EXT_TX_SEQ_VALID;
+
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL(ieee80211_wx_get_encode_ext);
+
+int ieee80211_wx_set_mlme(struct ieee80211_device *ieee,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct iw_mlme *mlme = (struct iw_mlme *) extra;
+ switch (mlme->cmd) {
+ case IW_MLME_DEAUTH:
+ case IW_MLME_DISASSOC:
+ ieee80211_disassociate(ieee);
+ break;
+ default:
+ return -EOPNOTSUPP;
+ }
+ return 0;
+}
+EXPORT_SYMBOL(ieee80211_wx_set_mlme);
+
+int ieee80211_wx_set_auth(struct ieee80211_device *ieee,
+ struct iw_request_info *info,
+ struct iw_param *data, char *extra)
+{
+ switch (data->flags & IW_AUTH_INDEX) {
+ case IW_AUTH_WPA_VERSION:
+ /*need to support wpa2 here*/
+ //printk("wpa version:%x\n", data->value);
+ break;
+ case IW_AUTH_CIPHER_PAIRWISE:
+ case IW_AUTH_CIPHER_GROUP:
+ case IW_AUTH_KEY_MGMT:
+ /*
+ * * Host AP driver does not use these parameters and allows
+ * * wpa_supplicant to control them internally.
+ * */
+ break;
+ case IW_AUTH_TKIP_COUNTERMEASURES:
+ ieee->tkip_countermeasures = data->value;
+ break;
+ case IW_AUTH_DROP_UNENCRYPTED:
+ ieee->drop_unencrypted = data->value;
+ break;
+
+ case IW_AUTH_80211_AUTH_ALG:
+ //printk("======>%s():data->value is %d\n",__func__,data->value);
+ // ieee->open_wep = (data->value&IW_AUTH_ALG_OPEN_SYSTEM)?1:0;
+ if(data->value & IW_AUTH_ALG_SHARED_KEY){
+ ieee->open_wep = 0;
+ ieee->auth_mode = 1;
+ }
+ else if(data->value & IW_AUTH_ALG_OPEN_SYSTEM){
+ ieee->open_wep = 1;
+ ieee->auth_mode = 0;
+ }
+ else if(data->value & IW_AUTH_ALG_LEAP){
+ ieee->open_wep = 1;
+ ieee->auth_mode = 2;
+ //printk("hahahaa:LEAP\n");
+ }
+ else
+ return -EINVAL;
+ //printk("open_wep:%d\n", ieee->open_wep);
+ break;
+
+ case IW_AUTH_WPA_ENABLED:
+ ieee->wpa_enabled = (data->value)?1:0;
+ //printk("enalbe wpa:%d\n", ieee->wpa_enabled);
+ break;
+
+ case IW_AUTH_RX_UNENCRYPTED_EAPOL:
+ ieee->ieee802_1x = data->value;
+ break;
+ case IW_AUTH_PRIVACY_INVOKED:
+ ieee->privacy_invoked = data->value;
+ break;
+ default:
+ return -EOPNOTSUPP;
+ }
+ return 0;
+}
+EXPORT_SYMBOL(ieee80211_wx_set_auth);
+
+int ieee80211_wx_set_gen_ie(struct ieee80211_device *ieee, u8 *ie, size_t len)
+{
+ u8 *buf;
+
+ if (len>MAX_WPA_IE_LEN || (len && ie == NULL))
+ {
+ // printk("return error out, len:%d\n", len);
+ return -EINVAL;
+ }
+
+
+ if (len)
+ {
+ if (len != ie[1]+2)
+ {
+ printk("len:%zu, ie:%d\n", len, ie[1]);
+ return -EINVAL;
+ }
+ buf = kmemdup(ie, len, GFP_KERNEL);
+ if (buf == NULL)
+ return -ENOMEM;
+ kfree(ieee->wpa_ie);
+ ieee->wpa_ie = buf;
+ ieee->wpa_ie_len = len;
+ }
+ else{
+ kfree(ieee->wpa_ie);
+ ieee->wpa_ie = NULL;
+ ieee->wpa_ie_len = 0;
+ }
+ return 0;
+
+}
+EXPORT_SYMBOL(ieee80211_wx_set_gen_ie);
diff --git a/drivers/staging/rtl8192u/ieee80211/rtl819x_BA.h b/drivers/staging/rtl8192u/ieee80211/rtl819x_BA.h
new file mode 100644
index 000000000..2c398ca9a
--- /dev/null
+++ b/drivers/staging/rtl8192u/ieee80211/rtl819x_BA.h
@@ -0,0 +1,67 @@
+#ifndef _BATYPE_H_
+#define _BATYPE_H_
+
+#define TOTAL_TXBA_NUM 16
+#define TOTAL_RXBA_NUM 16
+
+#define BA_SETUP_TIMEOUT 200
+#define BA_INACT_TIMEOUT 60000
+
+#define BA_POLICY_DELAYED 0
+#define BA_POLICY_IMMEDIATE 1
+
+#define ADDBA_STATUS_SUCCESS 0
+#define ADDBA_STATUS_REFUSED 37
+#define ADDBA_STATUS_INVALID_PARAM 38
+
+#define DELBA_REASON_QSTA_LEAVING 36
+#define DELBA_REASON_END_BA 37
+#define DELBA_REASON_UNKNOWN_BA 38
+#define DELBA_REASON_TIMEOUT 39
+/* whether need define BA Action frames here?
+struct ieee80211_ADDBA_Req{
+ struct ieee80211_header_data header;
+ u8 category;
+ u8
+} __attribute__ ((packed));
+*/
+//Is this need?I put here just to make it easier to define structure BA_RECORD //WB
+typedef union _SEQUENCE_CONTROL{
+ u16 ShortData;
+ struct {
+ u16 FragNum:4;
+ u16 SeqNum:12;
+ }field;
+}SEQUENCE_CONTROL, *PSEQUENCE_CONTROL;
+
+typedef union _BA_PARAM_SET {
+ u8 charData[2];
+ u16 shortData;
+ struct {
+ u16 AMSDU_Support:1;
+ u16 BAPolicy:1;
+ u16 TID:4;
+ u16 BufferSize:10;
+ } field;
+} BA_PARAM_SET, *PBA_PARAM_SET;
+
+typedef union _DELBA_PARAM_SET {
+ u8 charData[2];
+ u16 shortData;
+ struct {
+ u16 Reserved:11;
+ u16 Initiator:1;
+ u16 TID:4;
+ } field;
+} DELBA_PARAM_SET, *PDELBA_PARAM_SET;
+
+typedef struct _BA_RECORD {
+ struct timer_list Timer;
+ u8 bValid;
+ u8 DialogToken;
+ BA_PARAM_SET BaParamSet;
+ u16 BaTimeoutValue;
+ SEQUENCE_CONTROL BaStartSeqCtrl;
+} BA_RECORD, *PBA_RECORD;
+
+#endif //end _BATYPE_H_
diff --git a/drivers/staging/rtl8192u/ieee80211/rtl819x_BAProc.c b/drivers/staging/rtl8192u/ieee80211/rtl819x_BAProc.c
new file mode 100644
index 000000000..618d2cbc0
--- /dev/null
+++ b/drivers/staging/rtl8192u/ieee80211/rtl819x_BAProc.c
@@ -0,0 +1,745 @@
+/********************************************************************************************************************************
+ * This file is created to process BA Action Frame. According to 802.11 spec, there are 3 BA action types at all. And as BA is
+ * related to TS, this part need some structure defined in QOS side code. Also TX RX is going to be resturctured, so how to send
+ * ADDBAREQ ADDBARSP and DELBA packet is still on consideration. Temporarily use MANAGE QUEUE instead of Normal Queue.
+ * WB 2008-05-27
+ * *****************************************************************************************************************************/
+#include <asm/byteorder.h>
+#include <asm/unaligned.h>
+#include "ieee80211.h"
+#include "rtl819x_BA.h"
+
+/********************************************************************************************************************
+ *function: Activate BA entry. And if Time is nozero, start timer.
+ * input: PBA_RECORD pBA //BA entry to be enabled
+ * u16 Time //indicate time delay.
+ * output: none
+********************************************************************************************************************/
+static void ActivateBAEntry(struct ieee80211_device *ieee, PBA_RECORD pBA, u16 Time)
+{
+ pBA->bValid = true;
+ if(Time != 0)
+ mod_timer(&pBA->Timer, jiffies + MSECS(Time));
+}
+
+/********************************************************************************************************************
+ *function: deactivate BA entry, including its timer.
+ * input: PBA_RECORD pBA //BA entry to be disabled
+ * output: none
+********************************************************************************************************************/
+static void DeActivateBAEntry(struct ieee80211_device *ieee, PBA_RECORD pBA)
+{
+ pBA->bValid = false;
+ del_timer_sync(&pBA->Timer);
+}
+/********************************************************************************************************************
+ *function: deactivete BA entry in Tx Ts, and send DELBA.
+ * input:
+ * PTX_TS_RECORD pTxTs //Tx Ts which is to deactivate BA entry.
+ * output: none
+ * notice: As PTX_TS_RECORD structure will be defined in QOS, so wait to be merged. //FIXME
+********************************************************************************************************************/
+static u8 TxTsDeleteBA(struct ieee80211_device *ieee, PTX_TS_RECORD pTxTs)
+{
+ PBA_RECORD pAdmittedBa = &pTxTs->TxAdmittedBARecord; //These two BA entries must exist in TS structure
+ PBA_RECORD pPendingBa = &pTxTs->TxPendingBARecord;
+ u8 bSendDELBA = false;
+
+ // Delete pending BA
+ if (pPendingBa->bValid)
+ {
+ DeActivateBAEntry(ieee, pPendingBa);
+ bSendDELBA = true;
+ }
+
+ // Delete admitted BA
+ if (pAdmittedBa->bValid)
+ {
+ DeActivateBAEntry(ieee, pAdmittedBa);
+ bSendDELBA = true;
+ }
+
+ return bSendDELBA;
+}
+
+/********************************************************************************************************************
+ *function: deactivete BA entry in Tx Ts, and send DELBA.
+ * input:
+ * PRX_TS_RECORD pRxTs //Rx Ts which is to deactivate BA entry.
+ * output: none
+ * notice: As PRX_TS_RECORD structure will be defined in QOS, so wait to be merged. //FIXME, same with above
+********************************************************************************************************************/
+static u8 RxTsDeleteBA(struct ieee80211_device *ieee, PRX_TS_RECORD pRxTs)
+{
+ PBA_RECORD pBa = &pRxTs->RxAdmittedBARecord;
+ u8 bSendDELBA = false;
+
+ if (pBa->bValid)
+ {
+ DeActivateBAEntry(ieee, pBa);
+ bSendDELBA = true;
+ }
+
+ return bSendDELBA;
+}
+
+/********************************************************************************************************************
+ *function: reset BA entry
+ * input:
+ * PBA_RECORD pBA //entry to be reset
+ * output: none
+********************************************************************************************************************/
+void ResetBaEntry(PBA_RECORD pBA)
+{
+ pBA->bValid = false;
+ pBA->BaParamSet.shortData = 0;
+ pBA->BaTimeoutValue = 0;
+ pBA->DialogToken = 0;
+ pBA->BaStartSeqCtrl.ShortData = 0;
+}
+//These functions need porting here or not?
+/*******************************************************************************************************************************
+ *function: construct ADDBAREQ and ADDBARSP frame here together.
+ * input: u8* Dst //ADDBA frame's destination
+ * PBA_RECORD pBA //BA_RECORD entry which stores the necessary information for BA.
+ * u16 StatusCode //status code in RSP and I will use it to indicate whether it's RSP or REQ(will I?)
+ * u8 type //indicate whether it's RSP(ACT_ADDBARSP) ow REQ(ACT_ADDBAREQ)
+ * output: none
+ * return: sk_buff* skb //return constructed skb to xmit
+*******************************************************************************************************************************/
+static struct sk_buff *ieee80211_ADDBA(struct ieee80211_device *ieee, u8 *Dst, PBA_RECORD pBA, u16 StatusCode, u8 type)
+{
+ struct sk_buff *skb = NULL;
+ struct ieee80211_hdr_3addr *BAReq = NULL;
+ u8 *tag = NULL;
+ u16 len = ieee->tx_headroom + 9;
+ //category(1) + action field(1) + Dialog Token(1) + BA Parameter Set(2) + BA Timeout Value(2) + BA Start SeqCtrl(2)(or StatusCode(2))
+ IEEE80211_DEBUG(IEEE80211_DL_TRACE | IEEE80211_DL_BA, "========>%s(), frame(%d) sentd to:%pM, ieee->dev:%p\n", __func__, type, Dst, ieee->dev);
+ if (pBA == NULL)
+ {
+ IEEE80211_DEBUG(IEEE80211_DL_ERR, "pBA is NULL\n");
+ return NULL;
+ }
+ skb = dev_alloc_skb(len + sizeof( struct ieee80211_hdr_3addr)); //need to add something others? FIXME
+ if (skb == NULL)
+ {
+ IEEE80211_DEBUG(IEEE80211_DL_ERR, "can't alloc skb for ADDBA_REQ\n");
+ return NULL;
+ }
+
+ memset(skb->data, 0, sizeof( struct ieee80211_hdr_3addr)); //I wonder whether it's necessary. Apparently kernel will not do it when alloc a skb.
+ skb_reserve(skb, ieee->tx_headroom);
+
+ BAReq = ( struct ieee80211_hdr_3addr *) skb_put(skb,sizeof( struct ieee80211_hdr_3addr));
+
+ memcpy(BAReq->addr1, Dst, ETH_ALEN);
+ memcpy(BAReq->addr2, ieee->dev->dev_addr, ETH_ALEN);
+
+ memcpy(BAReq->addr3, ieee->current_network.bssid, ETH_ALEN);
+
+ BAReq->frame_ctl = cpu_to_le16(IEEE80211_STYPE_MANAGE_ACT); //action frame
+
+ //tag += sizeof( struct ieee80211_hdr_3addr); //move to action field
+ tag = (u8 *)skb_put(skb, 9);
+ *tag ++= ACT_CAT_BA;
+ *tag ++= type;
+ // Dialog Token
+ *tag ++= pBA->DialogToken;
+
+ if (ACT_ADDBARSP == type)
+ {
+ // Status Code
+ printk("=====>to send ADDBARSP\n");
+
+ put_unaligned_le16(StatusCode, tag);
+ tag += 2;
+ }
+ // BA Parameter Set
+
+ put_unaligned_le16(pBA->BaParamSet.shortData, tag);
+ tag += 2;
+ // BA Timeout Value
+
+ put_unaligned_le16(pBA->BaTimeoutValue, tag);
+ tag += 2;
+
+ if (ACT_ADDBAREQ == type)
+ {
+ // BA Start SeqCtrl
+ memcpy(tag, (u8 *)&(pBA->BaStartSeqCtrl), 2);
+ tag += 2;
+ }
+
+ IEEE80211_DEBUG_DATA(IEEE80211_DL_DATA|IEEE80211_DL_BA, skb->data, skb->len);
+ return skb;
+ //return NULL;
+}
+
+
+/********************************************************************************************************************
+ *function: construct DELBA frame
+ * input: u8* dst //DELBA frame's destination
+ * PBA_RECORD pBA //BA_RECORD entry which stores the necessary information for BA
+ * TR_SELECT TxRxSelect //TX RX direction
+ * u16 ReasonCode //status code.
+ * output: none
+ * return: sk_buff* skb //return constructed skb to xmit
+********************************************************************************************************************/
+static struct sk_buff *ieee80211_DELBA(
+ struct ieee80211_device *ieee,
+ u8 *dst,
+ PBA_RECORD pBA,
+ TR_SELECT TxRxSelect,
+ u16 ReasonCode
+ )
+{
+ DELBA_PARAM_SET DelbaParamSet;
+ struct sk_buff *skb = NULL;
+ struct ieee80211_hdr_3addr *Delba = NULL;
+ u8 *tag = NULL;
+ //len = head len + DELBA Parameter Set(2) + Reason Code(2)
+ u16 len = 6 + ieee->tx_headroom;
+
+ if (net_ratelimit())
+ IEEE80211_DEBUG(IEEE80211_DL_TRACE | IEEE80211_DL_BA, "========>%s(), ReasonCode(%d) sentd to:%pM\n", __func__, ReasonCode, dst);
+
+ memset(&DelbaParamSet, 0, 2);
+
+ DelbaParamSet.field.Initiator = (TxRxSelect==TX_DIR)?1:0;
+ DelbaParamSet.field.TID = pBA->BaParamSet.field.TID;
+
+ skb = dev_alloc_skb(len + sizeof( struct ieee80211_hdr_3addr)); //need to add something others? FIXME
+ if (skb == NULL)
+ {
+ IEEE80211_DEBUG(IEEE80211_DL_ERR, "can't alloc skb for ADDBA_REQ\n");
+ return NULL;
+ }
+// memset(skb->data, 0, len+sizeof( struct ieee80211_hdr_3addr));
+ skb_reserve(skb, ieee->tx_headroom);
+
+ Delba = ( struct ieee80211_hdr_3addr *) skb_put(skb,sizeof( struct ieee80211_hdr_3addr));
+
+ memcpy(Delba->addr1, dst, ETH_ALEN);
+ memcpy(Delba->addr2, ieee->dev->dev_addr, ETH_ALEN);
+ memcpy(Delba->addr3, ieee->current_network.bssid, ETH_ALEN);
+ Delba->frame_ctl = cpu_to_le16(IEEE80211_STYPE_MANAGE_ACT); //action frame
+
+ tag = (u8 *)skb_put(skb, 6);
+
+ *tag ++= ACT_CAT_BA;
+ *tag ++= ACT_DELBA;
+
+ // DELBA Parameter Set
+
+ put_unaligned_le16(DelbaParamSet.shortData, tag);
+ tag += 2;
+ // Reason Code
+
+ put_unaligned_le16(ReasonCode, tag);
+ tag += 2;
+
+ IEEE80211_DEBUG_DATA(IEEE80211_DL_DATA|IEEE80211_DL_BA, skb->data, skb->len);
+ if (net_ratelimit())
+ IEEE80211_DEBUG(IEEE80211_DL_TRACE | IEEE80211_DL_BA, "<=====%s()\n", __func__);
+ return skb;
+}
+
+/********************************************************************************************************************
+ *function: send ADDBAReq frame out
+ * input: u8* dst //ADDBAReq frame's destination
+ * PBA_RECORD pBA //BA_RECORD entry which stores the necessary information for BA
+ * output: none
+ * notice: If any possible, please hide pBA in ieee. And temporarily use Manage Queue as softmac_mgmt_xmit() usually does
+********************************************************************************************************************/
+static void ieee80211_send_ADDBAReq(struct ieee80211_device *ieee,
+ u8 *dst, PBA_RECORD pBA)
+{
+ struct sk_buff *skb = NULL;
+ skb = ieee80211_ADDBA(ieee, dst, pBA, 0, ACT_ADDBAREQ); //construct ACT_ADDBAREQ frames so set statuscode zero.
+
+ if (skb)
+ {
+ softmac_mgmt_xmit(skb, ieee);
+ //add statistic needed here.
+ //and skb will be freed in softmac_mgmt_xmit(), so omit all dev_kfree_skb_any() outside softmac_mgmt_xmit()
+ //WB
+ }
+ else
+ {
+ IEEE80211_DEBUG(IEEE80211_DL_ERR, "alloc skb error in function %s()\n", __func__);
+ }
+ return;
+}
+
+/********************************************************************************************************************
+ *function: send ADDBARSP frame out
+ * input: u8* dst //DELBA frame's destination
+ * PBA_RECORD pBA //BA_RECORD entry which stores the necessary information for BA
+ * u16 StatusCode //RSP StatusCode
+ * output: none
+ * notice: If any possible, please hide pBA in ieee. And temporarily use Manage Queue as softmac_mgmt_xmit() usually does
+********************************************************************************************************************/
+static void ieee80211_send_ADDBARsp(struct ieee80211_device *ieee, u8 *dst,
+ PBA_RECORD pBA, u16 StatusCode)
+{
+ struct sk_buff *skb = NULL;
+ skb = ieee80211_ADDBA(ieee, dst, pBA, StatusCode, ACT_ADDBARSP); //construct ACT_ADDBARSP frames
+ if (skb)
+ {
+ softmac_mgmt_xmit(skb, ieee);
+ //same above
+ }
+ else
+ {
+ IEEE80211_DEBUG(IEEE80211_DL_ERR, "alloc skb error in function %s()\n", __func__);
+ }
+
+ return;
+
+}
+/********************************************************************************************************************
+ *function: send ADDBARSP frame out
+ * input: u8* dst //DELBA frame's destination
+ * PBA_RECORD pBA //BA_RECORD entry which stores the necessary information for BA
+ * TR_SELECT TxRxSelect //TX or RX
+ * u16 ReasonCode //DEL ReasonCode
+ * output: none
+ * notice: If any possible, please hide pBA in ieee. And temporarily use Manage Queue as softmac_mgmt_xmit() usually does
+********************************************************************************************************************/
+
+static void ieee80211_send_DELBA(struct ieee80211_device *ieee, u8 *dst,
+ PBA_RECORD pBA, TR_SELECT TxRxSelect,
+ u16 ReasonCode)
+{
+ struct sk_buff *skb = NULL;
+ skb = ieee80211_DELBA(ieee, dst, pBA, TxRxSelect, ReasonCode); //construct ACT_ADDBARSP frames
+ if (skb)
+ {
+ softmac_mgmt_xmit(skb, ieee);
+ //same above
+ }
+ else
+ {
+ IEEE80211_DEBUG(IEEE80211_DL_ERR, "alloc skb error in function %s()\n", __func__);
+ }
+ return ;
+}
+
+/********************************************************************************************************************
+ *function: RX ADDBAReq
+ * input: struct sk_buff * skb //incoming ADDBAReq skb.
+ * return: 0(pass), other(fail)
+ * notice: As this function need support of QOS, I comment some code out. And when qos is ready, this code need to be support.
+********************************************************************************************************************/
+int ieee80211_rx_ADDBAReq(struct ieee80211_device *ieee, struct sk_buff *skb)
+{
+ struct ieee80211_hdr_3addr *req = NULL;
+ u16 rc = 0;
+ u8 *dst = NULL, *pDialogToken = NULL, *tag = NULL;
+ PBA_RECORD pBA = NULL;
+ PBA_PARAM_SET pBaParamSet = NULL;
+ u16 *pBaTimeoutVal = NULL;
+ PSEQUENCE_CONTROL pBaStartSeqCtrl = NULL;
+ PRX_TS_RECORD pTS = NULL;
+
+ if (skb->len < sizeof(struct ieee80211_hdr_3addr) + 9) {
+ IEEE80211_DEBUG(IEEE80211_DL_ERR,
+ " Invalid skb len in BAREQ(%d / %zu)\n",
+ skb->len,
+ (sizeof(struct ieee80211_hdr_3addr) + 9));
+ return -1;
+ }
+
+ IEEE80211_DEBUG_DATA(IEEE80211_DL_DATA|IEEE80211_DL_BA, skb->data, skb->len);
+
+ req = (struct ieee80211_hdr_3addr *) skb->data;
+ tag = (u8 *)req;
+ dst = (u8 *)(&req->addr2[0]);
+ tag += sizeof(struct ieee80211_hdr_3addr);
+ pDialogToken = tag + 2; //category+action
+ pBaParamSet = (PBA_PARAM_SET)(tag + 3); //+DialogToken
+ pBaTimeoutVal = (u16 *)(tag + 5);
+ pBaStartSeqCtrl = (PSEQUENCE_CONTROL)(req + 7);
+
+ printk("====================>rx ADDBAREQ from :%pM\n", dst);
+//some other capability is not ready now.
+ if ((ieee->current_network.qos_data.active == 0) ||
+ (ieee->pHTInfo->bCurrentHTSupport == false)) //||
+ // (ieee->pStaQos->bEnableRxImmBA == false) )
+ {
+ rc = ADDBA_STATUS_REFUSED;
+ IEEE80211_DEBUG(IEEE80211_DL_ERR, "Failed to reply on ADDBA_REQ as some capability is not ready(%d, %d)\n", ieee->current_network.qos_data.active, ieee->pHTInfo->bCurrentHTSupport);
+ goto OnADDBAReq_Fail;
+ }
+ // Search for related traffic stream.
+ // If there is no matched TS, reject the ADDBA request.
+ if (!GetTs(
+ ieee,
+ (PTS_COMMON_INFO *)(&pTS),
+ dst,
+ (u8)(pBaParamSet->field.TID),
+ RX_DIR,
+ true) )
+ {
+ rc = ADDBA_STATUS_REFUSED;
+ IEEE80211_DEBUG(IEEE80211_DL_ERR, "can't get TS in %s()\n", __func__);
+ goto OnADDBAReq_Fail;
+ }
+ pBA = &pTS->RxAdmittedBARecord;
+ // To Determine the ADDBA Req content
+ // We can do much more check here, including BufferSize, AMSDU_Support, Policy, StartSeqCtrl...
+ // I want to check StartSeqCtrl to make sure when we start aggregation!!!
+ //
+ if (pBaParamSet->field.BAPolicy == BA_POLICY_DELAYED)
+ {
+ rc = ADDBA_STATUS_INVALID_PARAM;
+ IEEE80211_DEBUG(IEEE80211_DL_ERR, "BA Policy is not correct in %s()\n", __func__);
+ goto OnADDBAReq_Fail;
+ }
+ // Admit the ADDBA Request
+ //
+ DeActivateBAEntry(ieee, pBA);
+ pBA->DialogToken = *pDialogToken;
+ pBA->BaParamSet = *pBaParamSet;
+ pBA->BaTimeoutValue = *pBaTimeoutVal;
+ pBA->BaStartSeqCtrl = *pBaStartSeqCtrl;
+ //for half N mode we only aggregate 1 frame
+ if (ieee->GetHalfNmodeSupportByAPsHandler(ieee->dev))
+ pBA->BaParamSet.field.BufferSize = 1;
+ else
+ pBA->BaParamSet.field.BufferSize = 32;
+ ActivateBAEntry(ieee, pBA, pBA->BaTimeoutValue);
+ ieee80211_send_ADDBARsp(ieee, dst, pBA, ADDBA_STATUS_SUCCESS);
+
+ // End of procedure.
+ return 0;
+
+OnADDBAReq_Fail:
+ {
+ BA_RECORD BA;
+ BA.BaParamSet = *pBaParamSet;
+ BA.BaTimeoutValue = *pBaTimeoutVal;
+ BA.DialogToken = *pDialogToken;
+ BA.BaParamSet.field.BAPolicy = BA_POLICY_IMMEDIATE;
+ ieee80211_send_ADDBARsp(ieee, dst, &BA, rc);
+ return 0; //we send RSP out.
+ }
+
+}
+
+/********************************************************************************************************************
+ *function: RX ADDBARSP
+ * input: struct sk_buff * skb //incoming ADDBAReq skb.
+ * return: 0(pass), other(fail)
+ * notice: As this function need support of QOS, I comment some code out. And when qos is ready, this code need to be support.
+********************************************************************************************************************/
+int ieee80211_rx_ADDBARsp(struct ieee80211_device *ieee, struct sk_buff *skb)
+{
+ struct ieee80211_hdr_3addr *rsp = NULL;
+ PBA_RECORD pPendingBA, pAdmittedBA;
+ PTX_TS_RECORD pTS = NULL;
+ u8 *dst = NULL, *pDialogToken = NULL, *tag = NULL;
+ u16 *pStatusCode = NULL, *pBaTimeoutVal = NULL;
+ PBA_PARAM_SET pBaParamSet = NULL;
+ u16 ReasonCode;
+
+ if (skb->len < sizeof(struct ieee80211_hdr_3addr) + 9) {
+ IEEE80211_DEBUG(IEEE80211_DL_ERR,
+ " Invalid skb len in BARSP(%d / %zu)\n",
+ skb->len,
+ (sizeof(struct ieee80211_hdr_3addr) + 9));
+ return -1;
+ }
+ rsp = (struct ieee80211_hdr_3addr *)skb->data;
+ tag = (u8 *)rsp;
+ dst = (u8 *)(&rsp->addr2[0]);
+ tag += sizeof(struct ieee80211_hdr_3addr);
+ pDialogToken = tag + 2;
+ pStatusCode = (u16 *)(tag + 3);
+ pBaParamSet = (PBA_PARAM_SET)(tag + 5);
+ pBaTimeoutVal = (u16 *)(tag + 7);
+
+ // Check the capability
+ // Since we can always receive A-MPDU, we just check if it is under HT mode.
+ if (ieee->current_network.qos_data.active == 0 ||
+ ieee->pHTInfo->bCurrentHTSupport == false ||
+ ieee->pHTInfo->bCurrentAMPDUEnable == false) {
+ IEEE80211_DEBUG(IEEE80211_DL_ERR, "reject to ADDBA_RSP as some capability is not ready(%d, %d, %d)\n",ieee->current_network.qos_data.active, ieee->pHTInfo->bCurrentHTSupport, ieee->pHTInfo->bCurrentAMPDUEnable);
+ ReasonCode = DELBA_REASON_UNKNOWN_BA;
+ goto OnADDBARsp_Reject;
+ }
+
+
+ //
+ // Search for related TS.
+ // If there is no TS found, we wil reject ADDBA Rsp by sending DELBA frame.
+ //
+ if (!GetTs(
+ ieee,
+ (PTS_COMMON_INFO *)(&pTS),
+ dst,
+ (u8)(pBaParamSet->field.TID),
+ TX_DIR,
+ false) )
+ {
+ IEEE80211_DEBUG(IEEE80211_DL_ERR, "can't get TS in %s()\n", __func__);
+ ReasonCode = DELBA_REASON_UNKNOWN_BA;
+ goto OnADDBARsp_Reject;
+ }
+
+ pTS->bAddBaReqInProgress = false;
+ pPendingBA = &pTS->TxPendingBARecord;
+ pAdmittedBA = &pTS->TxAdmittedBARecord;
+
+
+ //
+ // Check if related BA is waiting for setup.
+ // If not, reject by sending DELBA frame.
+ //
+ if((pAdmittedBA->bValid==true))
+ {
+ // Since BA is already setup, we ignore all other ADDBA Response.
+ IEEE80211_DEBUG(IEEE80211_DL_BA, "OnADDBARsp(): Recv ADDBA Rsp. Drop because already admit it! \n");
+ return -1;
+ }
+ else if((pPendingBA->bValid == false) ||(*pDialogToken != pPendingBA->DialogToken))
+ {
+ IEEE80211_DEBUG(IEEE80211_DL_ERR, "OnADDBARsp(): Recv ADDBA Rsp. BA invalid, DELBA! \n");
+ ReasonCode = DELBA_REASON_UNKNOWN_BA;
+ goto OnADDBARsp_Reject;
+ }
+ else
+ {
+ IEEE80211_DEBUG(IEEE80211_DL_BA, "OnADDBARsp(): Recv ADDBA Rsp. BA is admitted! Status code:%X\n", *pStatusCode);
+ DeActivateBAEntry(ieee, pPendingBA);
+ }
+
+
+ if(*pStatusCode == ADDBA_STATUS_SUCCESS)
+ {
+ //
+ // Determine ADDBA Rsp content here.
+ // We can compare the value of BA parameter set that Peer returned and Self sent.
+ // If it is OK, then admitted. Or we can send DELBA to cancel BA mechanism.
+ //
+ if (pBaParamSet->field.BAPolicy == BA_POLICY_DELAYED)
+ {
+ // Since this is a kind of ADDBA failed, we delay next ADDBA process.
+ pTS->bAddBaReqDelayed = true;
+ DeActivateBAEntry(ieee, pAdmittedBA);
+ ReasonCode = DELBA_REASON_END_BA;
+ goto OnADDBARsp_Reject;
+ }
+
+
+ //
+ // Admitted condition
+ //
+ pAdmittedBA->DialogToken = *pDialogToken;
+ pAdmittedBA->BaTimeoutValue = *pBaTimeoutVal;
+ pAdmittedBA->BaStartSeqCtrl = pPendingBA->BaStartSeqCtrl;
+ pAdmittedBA->BaParamSet = *pBaParamSet;
+ DeActivateBAEntry(ieee, pAdmittedBA);
+ ActivateBAEntry(ieee, pAdmittedBA, *pBaTimeoutVal);
+ }
+ else
+ {
+ // Delay next ADDBA process.
+ pTS->bAddBaReqDelayed = true;
+ }
+
+ // End of procedure
+ return 0;
+
+OnADDBARsp_Reject:
+ {
+ BA_RECORD BA;
+ BA.BaParamSet = *pBaParamSet;
+ ieee80211_send_DELBA(ieee, dst, &BA, TX_DIR, ReasonCode);
+ return 0;
+ }
+
+}
+
+/********************************************************************************************************************
+ *function: RX DELBA
+ * input: struct sk_buff * skb //incoming ADDBAReq skb.
+ * return: 0(pass), other(fail)
+ * notice: As this function need support of QOS, I comment some code out. And when qos is ready, this code need to be support.
+********************************************************************************************************************/
+int ieee80211_rx_DELBA(struct ieee80211_device *ieee, struct sk_buff *skb)
+{
+ struct ieee80211_hdr_3addr *delba = NULL;
+ PDELBA_PARAM_SET pDelBaParamSet = NULL;
+ u16 *pReasonCode = NULL;
+ u8 *dst = NULL;
+
+ if (skb->len < sizeof(struct ieee80211_hdr_3addr) + 6) {
+ IEEE80211_DEBUG(IEEE80211_DL_ERR,
+ " Invalid skb len in DELBA(%d / %zu)\n",
+ skb->len,
+ (sizeof(struct ieee80211_hdr_3addr) + 6));
+ return -1;
+ }
+
+ if (ieee->current_network.qos_data.active == 0 ||
+ ieee->pHTInfo->bCurrentHTSupport == false )
+ {
+ IEEE80211_DEBUG(IEEE80211_DL_ERR, "received DELBA while QOS or HT is not supported(%d, %d)\n",ieee->current_network.qos_data.active, ieee->pHTInfo->bCurrentHTSupport);
+ return -1;
+ }
+
+ IEEE80211_DEBUG_DATA(IEEE80211_DL_DATA|IEEE80211_DL_BA, skb->data, skb->len);
+ delba = (struct ieee80211_hdr_3addr *)skb->data;
+ dst = (u8 *)(&delba->addr2[0]);
+ delba += sizeof(struct ieee80211_hdr_3addr);
+ pDelBaParamSet = (PDELBA_PARAM_SET)(delba+2);
+ pReasonCode = (u16 *)(delba+4);
+
+ if(pDelBaParamSet->field.Initiator == 1)
+ {
+ PRX_TS_RECORD pRxTs;
+
+ if (!GetTs(
+ ieee,
+ (PTS_COMMON_INFO *)&pRxTs,
+ dst,
+ (u8)pDelBaParamSet->field.TID,
+ RX_DIR,
+ false) )
+ {
+ IEEE80211_DEBUG(IEEE80211_DL_ERR, "can't get TS for RXTS in %s()\n", __func__);
+ return -1;
+ }
+
+ RxTsDeleteBA(ieee, pRxTs);
+ }
+ else
+ {
+ PTX_TS_RECORD pTxTs;
+
+ if (!GetTs(
+ ieee,
+ (PTS_COMMON_INFO *)&pTxTs,
+ dst,
+ (u8)pDelBaParamSet->field.TID,
+ TX_DIR,
+ false) )
+ {
+ IEEE80211_DEBUG(IEEE80211_DL_ERR, "can't get TS for TXTS in %s()\n", __func__);
+ return -1;
+ }
+
+ pTxTs->bUsingBa = false;
+ pTxTs->bAddBaReqInProgress = false;
+ pTxTs->bAddBaReqDelayed = false;
+ del_timer_sync(&pTxTs->TsAddBaTimer);
+ //PlatformCancelTimer(Adapter, &pTxTs->TsAddBaTimer);
+ TxTsDeleteBA(ieee, pTxTs);
+ }
+ return 0;
+}
+
+//
+// ADDBA initiate. This can only be called by TX side.
+//
+void
+TsInitAddBA(
+ struct ieee80211_device *ieee,
+ PTX_TS_RECORD pTS,
+ u8 Policy,
+ u8 bOverwritePending
+ )
+{
+ PBA_RECORD pBA = &pTS->TxPendingBARecord;
+
+ if(pBA->bValid==true && bOverwritePending==false)
+ return;
+
+ // Set parameters to "Pending" variable set
+ DeActivateBAEntry(ieee, pBA);
+
+ pBA->DialogToken++; // DialogToken: Only keep the latest dialog token
+ pBA->BaParamSet.field.AMSDU_Support = 0; // Do not support A-MSDU with A-MPDU now!!
+ pBA->BaParamSet.field.BAPolicy = Policy; // Policy: Delayed or Immediate
+ pBA->BaParamSet.field.TID = pTS->TsCommonInfo.TSpec.f.TSInfo.field.ucTSID; // TID
+ // BufferSize: This need to be set according to A-MPDU vector
+ pBA->BaParamSet.field.BufferSize = 32; // BufferSize: This need to be set according to A-MPDU vector
+ pBA->BaTimeoutValue = 0; // Timeout value: Set 0 to disable Timer
+ pBA->BaStartSeqCtrl.field.SeqNum = (pTS->TxCurSeq + 3) % 4096; // Block Ack will start after 3 packets later.
+
+ ActivateBAEntry(ieee, pBA, BA_SETUP_TIMEOUT);
+
+ ieee80211_send_ADDBAReq(ieee, pTS->TsCommonInfo.Addr, pBA);
+}
+
+void
+TsInitDelBA( struct ieee80211_device *ieee, PTS_COMMON_INFO pTsCommonInfo, TR_SELECT TxRxSelect)
+{
+
+ if(TxRxSelect == TX_DIR)
+ {
+ PTX_TS_RECORD pTxTs = (PTX_TS_RECORD)pTsCommonInfo;
+
+ if(TxTsDeleteBA(ieee, pTxTs))
+ ieee80211_send_DELBA(
+ ieee,
+ pTsCommonInfo->Addr,
+ (pTxTs->TxAdmittedBARecord.bValid)?(&pTxTs->TxAdmittedBARecord):(&pTxTs->TxPendingBARecord),
+ TxRxSelect,
+ DELBA_REASON_END_BA);
+ }
+ else if(TxRxSelect == RX_DIR)
+ {
+ PRX_TS_RECORD pRxTs = (PRX_TS_RECORD)pTsCommonInfo;
+ if(RxTsDeleteBA(ieee, pRxTs))
+ ieee80211_send_DELBA(
+ ieee,
+ pTsCommonInfo->Addr,
+ &pRxTs->RxAdmittedBARecord,
+ TxRxSelect,
+ DELBA_REASON_END_BA );
+ }
+}
+/********************************************************************************************************************
+ *function: BA setup timer
+ * input: unsigned long data //acturally we send TX_TS_RECORD or RX_TS_RECORD to these timer
+ * return: NULL
+ * notice:
+********************************************************************************************************************/
+void BaSetupTimeOut(unsigned long data)
+{
+ PTX_TS_RECORD pTxTs = (PTX_TS_RECORD)data;
+
+ pTxTs->bAddBaReqInProgress = false;
+ pTxTs->bAddBaReqDelayed = true;
+ pTxTs->TxPendingBARecord.bValid = false;
+}
+
+void TxBaInactTimeout(unsigned long data)
+{
+ PTX_TS_RECORD pTxTs = (PTX_TS_RECORD)data;
+ struct ieee80211_device *ieee = container_of(pTxTs, struct ieee80211_device, TxTsRecord[pTxTs->num]);
+ TxTsDeleteBA(ieee, pTxTs);
+ ieee80211_send_DELBA(
+ ieee,
+ pTxTs->TsCommonInfo.Addr,
+ &pTxTs->TxAdmittedBARecord,
+ TX_DIR,
+ DELBA_REASON_TIMEOUT);
+}
+
+void RxBaInactTimeout(unsigned long data)
+{
+ PRX_TS_RECORD pRxTs = (PRX_TS_RECORD)data;
+ struct ieee80211_device *ieee = container_of(pRxTs, struct ieee80211_device, RxTsRecord[pRxTs->num]);
+
+ RxTsDeleteBA(ieee, pRxTs);
+ ieee80211_send_DELBA(
+ ieee,
+ pRxTs->TsCommonInfo.Addr,
+ &pRxTs->RxAdmittedBARecord,
+ RX_DIR,
+ DELBA_REASON_TIMEOUT);
+ return ;
+}
diff --git a/drivers/staging/rtl8192u/ieee80211/rtl819x_HT.h b/drivers/staging/rtl8192u/ieee80211/rtl819x_HT.h
new file mode 100644
index 000000000..c3aabbaac
--- /dev/null
+++ b/drivers/staging/rtl8192u/ieee80211/rtl819x_HT.h
@@ -0,0 +1,480 @@
+#ifndef _RTL819XU_HTTYPE_H_
+#define _RTL819XU_HTTYPE_H_
+
+//------------------------------------------------------------
+// The HT Capability element is present in beacons, association request,
+// reassociation request and probe response frames
+//------------------------------------------------------------
+
+//
+// Operation mode value
+//
+#define HT_OPMODE_NO_PROTECT 0
+#define HT_OPMODE_OPTIONAL 1
+#define HT_OPMODE_40MHZ_PROTECT 2
+#define HT_OPMODE_MIXED 3
+
+//
+// MIMO Power Save Settings
+//
+#define MIMO_PS_STATIC 0
+#define MIMO_PS_DYNAMIC 1
+#define MIMO_PS_NOLIMIT 3
+
+
+//
+// There should be 128 bits to cover all of the MCS rates. However, since
+// 8190 does not support too much rates, one integer is quite enough.
+//
+
+#define sHTCLng 4
+
+
+#define HT_SUPPORTED_MCS_1SS_BITMAP 0x000000ff
+#define HT_SUPPORTED_MCS_2SS_BITMAP 0x0000ff00
+#define HT_SUPPORTED_MCS_1SS_2SS_BITMAP HT_MCS_1SS_BITMAP|HT_MCS_1SS_2SS_BITMAP
+
+
+typedef enum _HT_MCS_RATE{
+ HT_MCS0 = 0x00000001,
+ HT_MCS1 = 0x00000002,
+ HT_MCS2 = 0x00000004,
+ HT_MCS3 = 0x00000008,
+ HT_MCS4 = 0x00000010,
+ HT_MCS5 = 0x00000020,
+ HT_MCS6 = 0x00000040,
+ HT_MCS7 = 0x00000080,
+ HT_MCS8 = 0x00000100,
+ HT_MCS9 = 0x00000200,
+ HT_MCS10 = 0x00000400,
+ HT_MCS11 = 0x00000800,
+ HT_MCS12 = 0x00001000,
+ HT_MCS13 = 0x00002000,
+ HT_MCS14 = 0x00004000,
+ HT_MCS15 = 0x00008000,
+ // Do not define MCS32 here although 8190 support MCS32
+} HT_MCS_RATE, *PHT_MCS_RATE;
+
+//
+// Represent Channel Width in HT Capabilities
+//
+typedef enum _HT_CHANNEL_WIDTH{
+ HT_CHANNEL_WIDTH_20 = 0,
+ HT_CHANNEL_WIDTH_20_40 = 1,
+}HT_CHANNEL_WIDTH, *PHT_CHANNEL_WIDTH;
+
+//
+// Represent Extension Channel Offset in HT Capabilities
+// This is available only in 40Mhz mode.
+//
+typedef enum _HT_EXTCHNL_OFFSET{
+ HT_EXTCHNL_OFFSET_NO_EXT = 0,
+ HT_EXTCHNL_OFFSET_UPPER = 1,
+ HT_EXTCHNL_OFFSET_NO_DEF = 2,
+ HT_EXTCHNL_OFFSET_LOWER = 3,
+}HT_EXTCHNL_OFFSET, *PHT_EXTCHNL_OFFSET;
+
+typedef enum _CHNLOP{
+ CHNLOP_NONE = 0, // No Action now
+ CHNLOP_SCAN = 1, // Scan in progress
+ CHNLOP_SWBW = 2, // Bandwidth switching in progress
+ CHNLOP_SWCHNL = 3, // Software Channel switching in progress
+} CHNLOP, *PCHNLOP;
+
+// Determine if the Channel Operation is in progress
+#define CHHLOP_IN_PROGRESS(_pHTInfo) \
+ ((_pHTInfo)->ChnlOp > CHNLOP_NONE) ? TRUE : FALSE
+
+/*
+typedef union _HT_CAPABILITY{
+ u16 ShortData;
+ u8 CharData[2];
+ struct
+ {
+ u16 AdvCoding:1;
+ u16 ChlWidth:1;
+ u16 MimoPwrSave:2;
+ u16 GreenField:1;
+ u16 ShortGI20Mhz:1;
+ u16 ShortGI40Mhz:1;
+ u16 STBC:1;
+ u16 BeamForm:1;
+ u16 DelayBA:1;
+ u16 MaxAMSDUSize:1;
+ u16 DssCCk:1;
+ u16 PSMP:1;
+ u16 Rsvd:3;
+ }Field;
+}HT_CAPABILITY, *PHT_CAPABILITY;
+
+typedef union _HT_CAPABILITY_MACPARA{
+ u8 ShortData;
+ u8 CharData[1];
+ struct
+ {
+ u8 MaxRxAMPDU:2;
+ u8 MPDUDensity:2;
+ u8 Rsvd:4;
+ }Field;
+}HT_CAPABILITY_MACPARA, *PHT_CAPABILITY_MACPARA;
+*/
+
+typedef enum _HT_ACTION{
+ ACT_RECOMMAND_WIDTH = 0,
+ ACT_MIMO_PWR_SAVE = 1,
+ ACT_PSMP = 2,
+ ACT_SET_PCO_PHASE = 3,
+ ACT_MIMO_CHL_MEASURE = 4,
+ ACT_RECIPROCITY_CORRECT = 5,
+ ACT_MIMO_CSI_MATRICS = 6,
+ ACT_MIMO_NOCOMPR_STEER = 7,
+ ACT_MIMO_COMPR_STEER = 8,
+ ACT_ANTENNA_SELECT = 9,
+} HT_ACTION, *PHT_ACTION;
+
+
+/* 2007/06/07 MH Define sub-carrier mode for 40MHZ. */
+typedef enum _HT_Bandwidth_40MHZ_Sub_Carrier{
+ SC_MODE_DUPLICATE = 0,
+ SC_MODE_LOWER = 1,
+ SC_MODE_UPPER = 2,
+ SC_MODE_FULL40MHZ = 3,
+}HT_BW40_SC_E;
+
+typedef struct _HT_CAPABILITY_ELE{
+
+ //HT capability info
+ u8 AdvCoding:1;
+ u8 ChlWidth:1;
+ u8 MimoPwrSave:2;
+ u8 GreenField:1;
+ u8 ShortGI20Mhz:1;
+ u8 ShortGI40Mhz:1;
+ u8 TxSTBC:1;
+ u8 RxSTBC:2;
+ u8 DelayBA:1;
+ u8 MaxAMSDUSize:1;
+ u8 DssCCk:1;
+ u8 PSMP:1;
+ u8 Rsvd1:1;
+ u8 LSigTxopProtect:1;
+
+ //MAC HT parameters info
+ u8 MaxRxAMPDUFactor:2;
+ u8 MPDUDensity:3;
+ u8 Rsvd2:3;
+
+ //Supported MCS set
+ u8 MCS[16];
+
+
+ //Extended HT Capability Info
+ u16 ExtHTCapInfo;
+
+ //TXBF Capabilities
+ u8 TxBFCap[4];
+
+ //Antenna Selection Capabilities
+ u8 ASCap;
+
+} __attribute__ ((packed)) HT_CAPABILITY_ELE, *PHT_CAPABILITY_ELE;
+
+//------------------------------------------------------------
+// The HT Information element is present in beacons
+// Only AP is required to include this element
+//------------------------------------------------------------
+
+typedef struct _HT_INFORMATION_ELE{
+ u8 ControlChl;
+
+ u8 ExtChlOffset:2;
+ u8 RecommemdedTxWidth:1;
+ u8 RIFS:1;
+ u8 PSMPAccessOnly:1;
+ u8 SrvIntGranularity:3;
+
+ u8 OptMode:2;
+ u8 NonGFDevPresent:1;
+ u8 Revd1:5;
+ u8 Revd2:8;
+
+ u8 Rsvd3:6;
+ u8 DualBeacon:1;
+ u8 DualCTSProtect:1;
+
+ u8 SecondaryBeacon:1;
+ u8 LSigTxopProtectFull:1;
+ u8 PcoActive:1;
+ u8 PcoPhase:1;
+ u8 Rsvd4:4;
+
+ u8 BasicMSC[16];
+} __attribute__ ((packed)) HT_INFORMATION_ELE, *PHT_INFORMATION_ELE;
+
+//
+// MIMO Power Save control field.
+// This is appear in MIMO Power Save Action Frame
+//
+typedef struct _MIMOPS_CTRL{
+ u8 MimoPsEnable:1;
+ u8 MimoPsMode:1;
+ u8 Reserved:6;
+} MIMOPS_CTRL, *PMIMOPS_CTRL;
+
+typedef enum _HT_SPEC_VER{
+ HT_SPEC_VER_IEEE = 0,
+ HT_SPEC_VER_EWC = 1,
+}HT_SPEC_VER, *PHT_SPEC_VER;
+
+typedef enum _HT_AGGRE_MODE_E{
+ HT_AGG_AUTO = 0,
+ HT_AGG_FORCE_ENABLE = 1,
+ HT_AGG_FORCE_DISABLE = 2,
+}HT_AGGRE_MODE_E, *PHT_AGGRE_MODE_E;
+
+//------------------------------------------------------------
+// The Data structure is used to keep HT related variables when card is
+// configured as non-AP STA mode. **Note** Current_xxx should be set
+// to default value in HTInitializeHTInfo()
+//------------------------------------------------------------
+
+typedef struct _RT_HIGH_THROUGHPUT{
+ u8 bEnableHT;
+ u8 bCurrentHTSupport;
+
+ u8 bRegBW40MHz; // Tx 40MHz channel capability
+ u8 bCurBW40MHz; // Tx 40MHz channel capability
+
+ u8 bRegShortGI40MHz; // Tx Short GI for 40Mhz
+ u8 bCurShortGI40MHz; // Tx Short GI for 40MHz
+
+ u8 bRegShortGI20MHz; // Tx Short GI for 20MHz
+ u8 bCurShortGI20MHz; // Tx Short GI for 20MHz
+
+ u8 bRegSuppCCK; // Tx CCK rate capability
+ u8 bCurSuppCCK; // Tx CCK rate capability
+
+ // 802.11n spec version for "peer"
+ HT_SPEC_VER ePeerHTSpecVer;
+
+
+ // HT related information for "Self"
+ HT_CAPABILITY_ELE SelfHTCap; // This is HT cap element sent to peer STA, which also indicate HT Rx capabilities.
+ HT_INFORMATION_ELE SelfHTInfo; // This is HT info element sent to peer STA, which also indicate HT Rx capabilities.
+
+ // HT related information for "Peer"
+ u8 PeerHTCapBuf[32];
+ u8 PeerHTInfoBuf[32];
+
+
+ // A-MSDU related
+ u8 bAMSDU_Support; // This indicates Tx A-MSDU capability
+ u16 nAMSDU_MaxSize; // This indicates Tx A-MSDU capability
+ u8 bCurrent_AMSDU_Support; // This indicates Tx A-MSDU capability
+ u16 nCurrent_AMSDU_MaxSize; // This indicates Tx A-MSDU capability
+
+
+ // AMPDU related <2006.08.10 Emily>
+ u8 bAMPDUEnable; // This indicate Tx A-MPDU capability
+ u8 bCurrentAMPDUEnable; // This indicate Tx A-MPDU capability
+ u8 AMPDU_Factor; // This indicate Tx A-MPDU capability
+ u8 CurrentAMPDUFactor; // This indicate Tx A-MPDU capability
+ u8 MPDU_Density; // This indicate Tx A-MPDU capability
+ u8 CurrentMPDUDensity; // This indicate Tx A-MPDU capability
+
+ // Forced A-MPDU enable
+ HT_AGGRE_MODE_E ForcedAMPDUMode;
+ u8 ForcedAMPDUFactor;
+ u8 ForcedMPDUDensity;
+
+ // Forced A-MSDU enable
+ HT_AGGRE_MODE_E ForcedAMSDUMode;
+ u16 ForcedAMSDUMaxSize;
+
+ u8 bForcedShortGI;
+
+ u8 CurrentOpMode;
+
+ // MIMO PS related
+ u8 SelfMimoPs;
+ u8 PeerMimoPs;
+
+ // 40MHz Channel Offset settings.
+ HT_EXTCHNL_OFFSET CurSTAExtChnlOffset;
+ u8 bCurTxBW40MHz; // If we use 40 MHz to Tx
+ u8 PeerBandwidth;
+
+ // For Bandwidth Switching
+ u8 bSwBwInProgress;
+ CHNLOP ChnlOp; // software switching channel in progress. By Bruce, 2008-02-15.
+ u8 SwBwStep;
+ //struct timer_list SwBwTimer; //moved to ieee80211_device. as timer_list need include some header file here.
+
+ // For Realtek proprietary A-MPDU factor for aggregation
+ u8 bRegRT2RTAggregation;
+ u8 bCurrentRT2RTAggregation;
+ u8 bCurrentRT2RTLongSlotTime;
+ u8 szRT2RTAggBuffer[10];
+
+ // Rx Reorder control
+ u8 bRegRxReorderEnable;
+ u8 bCurRxReorderEnable;
+ u8 RxReorderWinSize;
+ u8 RxReorderPendingTime;
+ u16 RxReorderDropCounter;
+
+#ifdef USB_TX_DRIVER_AGGREGATION_ENABLE
+ u8 UsbTxAggrNum;
+#endif
+#ifdef USB_RX_AGGREGATION_SUPPORT
+ u8 UsbRxFwAggrEn;
+ u8 UsbRxFwAggrPageNum;
+ u8 UsbRxFwAggrPacketNum;
+ u8 UsbRxFwAggrTimeout;
+#endif
+
+ // Add for Broadcom(Linksys) IOT. Joseph
+ u8 bIsPeerBcm;
+
+ // For IOT issue.
+ u8 IOTPeer;
+ u32 IOTAction;
+} __attribute__ ((packed)) RT_HIGH_THROUGHPUT, *PRT_HIGH_THROUGHPUT;
+
+
+//------------------------------------------------------------
+// The Data structure is used to keep HT related variable for "each Sta"
+// when card is configured as "AP mode"
+//------------------------------------------------------------
+
+typedef struct _RT_HTINFO_STA_ENTRY{
+ u8 bEnableHT;
+
+ u8 bSupportCck;
+
+ u16 AMSDU_MaxSize;
+
+ u8 AMPDU_Factor;
+ u8 MPDU_Density;
+
+ u8 HTHighestOperaRate;
+
+ u8 bBw40MHz;
+
+ u8 MimoPs;
+
+ u8 McsRateSet[16];
+
+
+}RT_HTINFO_STA_ENTRY, *PRT_HTINFO_STA_ENTRY;
+
+
+
+
+
+//------------------------------------------------------------
+// The Data structure is used to keep HT related variable for "each AP"
+// when card is configured as "STA mode"
+//------------------------------------------------------------
+
+typedef struct _BSS_HT{
+
+ u8 bdSupportHT;
+
+ // HT related elements
+ u8 bdHTCapBuf[32];
+ u16 bdHTCapLen;
+ u8 bdHTInfoBuf[32];
+ u16 bdHTInfoLen;
+
+ HT_SPEC_VER bdHTSpecVer;
+ //HT_CAPABILITY_ELE bdHTCapEle;
+ //HT_INFORMATION_ELE bdHTInfoEle;
+
+ u8 bdRT2RTAggregation;
+ u8 bdRT2RTLongSlotTime;
+} __attribute__ ((packed)) BSS_HT, *PBSS_HT;
+
+typedef struct _MIMO_RSSI{
+ u32 EnableAntenna;
+ u32 AntennaA;
+ u32 AntennaB;
+ u32 AntennaC;
+ u32 AntennaD;
+ u32 Average;
+}MIMO_RSSI, *PMIMO_RSSI;
+
+typedef struct _MIMO_EVM{
+ u32 EVM1;
+ u32 EVM2;
+}MIMO_EVM, *PMIMO_EVM;
+
+typedef struct _FALSE_ALARM_STATISTICS{
+ u32 Cnt_Parity_Fail;
+ u32 Cnt_Rate_Illegal;
+ u32 Cnt_Crc8_fail;
+ u32 Cnt_all;
+}FALSE_ALARM_STATISTICS, *PFALSE_ALARM_STATISTICS;
+
+
+extern u8 MCS_FILTER_ALL[16];
+extern u8 MCS_FILTER_1SS[16];
+
+/* 2007/07/11 MH Modify the macro. Becaus STA may link with a N-AP. If we set
+ STA in A/B/G mode and AP is still in N mode. The macro will be wrong. We have
+ to add a macro to judge wireless mode. */
+#define PICK_RATE(_nLegacyRate, _nMcsRate) \
+ (_nMcsRate==0)?(_nLegacyRate&0x7f):(_nMcsRate)
+/* 2007/07/12 MH We only define legacy and HT wireless mode now. */
+#define LEGACY_WIRELESS_MODE IEEE_MODE_MASK
+
+#define CURRENT_RATE(WirelessMode, LegacyRate, HTRate) \
+ ((WirelessMode & (LEGACY_WIRELESS_MODE))!=0)?\
+ (LegacyRate):\
+ (PICK_RATE(LegacyRate, HTRate))
+
+
+
+// MCS Bw 40 {1~7, 12~15,32}
+#define RATE_ADPT_1SS_MASK 0xFF
+#define RATE_ADPT_2SS_MASK 0xF0 //Skip MCS8~11 because mcs7 > mcs6, 9, 10, 11. 2007.01.16 by Emily
+#define RATE_ADPT_MCS32_MASK 0x01
+
+#define IS_11N_MCS_RATE(rate) (rate&0x80)
+
+typedef enum _HT_AGGRE_SIZE{
+ HT_AGG_SIZE_8K = 0,
+ HT_AGG_SIZE_16K = 1,
+ HT_AGG_SIZE_32K = 2,
+ HT_AGG_SIZE_64K = 3,
+}HT_AGGRE_SIZE_E, *PHT_AGGRE_SIZE_E;
+
+/* Indicate different AP vendor for IOT issue */
+typedef enum _HT_IOT_PEER
+{
+ HT_IOT_PEER_UNKNOWN = 0,
+ HT_IOT_PEER_REALTEK = 1,
+ HT_IOT_PEER_BROADCOM = 2,
+ HT_IOT_PEER_RALINK = 3,
+ HT_IOT_PEER_ATHEROS = 4,
+ HT_IOT_PEER_CISCO= 5,
+ HT_IOT_PEER_MAX = 6
+}HT_IOT_PEER_E, *PHTIOT_PEER_E;
+
+//
+// IOT Action for different AP
+//
+typedef enum _HT_IOT_ACTION{
+ HT_IOT_ACT_TX_USE_AMSDU_4K = 0x00000001,
+ HT_IOT_ACT_TX_USE_AMSDU_8K = 0x00000002,
+ HT_IOT_ACT_DISABLE_MCS14 = 0x00000004,
+ HT_IOT_ACT_DISABLE_MCS15 = 0x00000008,
+ HT_IOT_ACT_DISABLE_ALL_2SS = 0x00000010,
+ HT_IOT_ACT_DISABLE_EDCA_TURBO = 0x00000020,
+ HT_IOT_ACT_MGNT_USE_CCK_6M = 0x00000040,
+ HT_IOT_ACT_CDD_FSYNC = 0x00000080,
+ HT_IOT_ACT_PURE_N_MODE = 0x00000100,
+ HT_IOT_ACT_FORCED_CTS2SELF = 0x00000200,
+}HT_IOT_ACTION_E, *PHT_IOT_ACTION_E;
+
+#endif //_RTL819XU_HTTYPE_H_
diff --git a/drivers/staging/rtl8192u/ieee80211/rtl819x_HTProc.c b/drivers/staging/rtl8192u/ieee80211/rtl819x_HTProc.c
new file mode 100644
index 000000000..c2588f806
--- /dev/null
+++ b/drivers/staging/rtl8192u/ieee80211/rtl819x_HTProc.c
@@ -0,0 +1,1410 @@
+
+//As this function is mainly ported from Windows driver, so leave the name little changed. If any confusion caused, tell me. Created by WB. 2008.05.08
+#include "ieee80211.h"
+#include "rtl819x_HT.h"
+u8 MCS_FILTER_ALL[16] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+
+u8 MCS_FILTER_1SS[16] = {0xff, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+
+u16 MCS_DATA_RATE[2][2][77] =
+ { { {13, 26, 39, 52, 78, 104, 117, 130, 26, 52, 78 ,104, 156, 208, 234, 260,
+ 39, 78, 117, 234, 312, 351, 390, 52, 104, 156, 208, 312, 416, 468, 520,
+ 0, 78, 104, 130, 117, 156, 195, 104, 130, 130, 156, 182, 182, 208, 156, 195,
+ 195, 234, 273, 273, 312, 130, 156, 181, 156, 181, 208, 234, 208, 234, 260, 260,
+ 286, 195, 234, 273, 234, 273, 312, 351, 312, 351, 390, 390, 429}, // Long GI, 20MHz
+ {14, 29, 43, 58, 87, 116, 130, 144, 29, 58, 87, 116, 173, 231, 260, 289,
+ 43, 87, 130, 173, 260, 347, 390, 433, 58, 116, 173, 231, 347, 462, 520, 578,
+ 0, 87, 116, 144, 130, 173, 217, 116, 144, 144, 173, 202, 202, 231, 173, 217,
+ 217, 260, 303, 303, 347, 144, 173, 202, 173, 202, 231, 260, 231, 260, 289, 289,
+ 318, 217, 260, 303, 260, 303, 347, 390, 347, 390, 433, 433, 477} }, // Short GI, 20MHz
+ { {27, 54, 81, 108, 162, 216, 243, 270, 54, 108, 162, 216, 324, 432, 486, 540,
+ 81, 162, 243, 324, 486, 648, 729, 810, 108, 216, 324, 432, 648, 864, 972, 1080,
+ 12, 162, 216, 270, 243, 324, 405, 216, 270, 270, 324, 378, 378, 432, 324, 405,
+ 405, 486, 567, 567, 648, 270, 324, 378, 324, 378, 432, 486, 432, 486, 540, 540,
+ 594, 405, 486, 567, 486, 567, 648, 729, 648, 729, 810, 810, 891}, // Long GI, 40MHz
+ {30, 60, 90, 120, 180, 240, 270, 300, 60, 120, 180, 240, 360, 480, 540, 600,
+ 90, 180, 270, 360, 540, 720, 810, 900, 120, 240, 360, 480, 720, 960, 1080, 1200,
+ 13, 180, 240, 300, 270, 360, 450, 240, 300, 300, 360, 420, 420, 480, 360, 450,
+ 450, 540, 630, 630, 720, 300, 360, 420, 360, 420, 480, 540, 480, 540, 600, 600,
+ 660, 450, 540, 630, 540, 630, 720, 810, 720, 810, 900, 900, 990} } // Short GI, 40MHz
+ };
+
+static u8 UNKNOWN_BORADCOM[3] = {0x00, 0x14, 0xbf};
+static u8 LINKSYSWRT330_LINKSYSWRT300_BROADCOM[3] = {0x00, 0x1a, 0x70};
+static u8 LINKSYSWRT350_LINKSYSWRT150_BROADCOM[3] = {0x00, 0x1d, 0x7e};
+static u8 NETGEAR834Bv2_BROADCOM[3] = {0x00, 0x1b, 0x2f};
+static u8 BELKINF5D8233V1_RALINK[3] = {0x00, 0x17, 0x3f}; //cosa 03202008
+static u8 BELKINF5D82334V3_RALINK[3] = {0x00, 0x1c, 0xdf};
+static u8 PCI_RALINK[3] = {0x00, 0x90, 0xcc};
+static u8 EDIMAX_RALINK[3] = {0x00, 0x0e, 0x2e};
+static u8 AIRLINK_RALINK[3] = {0x00, 0x18, 0x02};
+//static u8 DLINK_ATHEROS[3] = {0x00, 0x1c, 0xf0};
+static u8 CISCO_BROADCOM[3] = {0x00, 0x17, 0x94};
+
+// 2008/04/01 MH For Cisco G mode RX TP We need to change FW duration. Should we put the
+// code in other place??
+//static u8 WIFI_CISCO_G_AP[3] = {0x00, 0x40, 0x96};
+/********************************************************************************************************************
+ *function: This function update default settings in pHTInfo structure
+ * input: PRT_HIGH_THROUGHPUT pHTInfo
+ * output: none
+ * return: none
+ * notice: These value need be modified if any changes.
+ * *****************************************************************************************************************/
+void HTUpdateDefaultSetting(struct ieee80211_device *ieee)
+{
+ PRT_HIGH_THROUGHPUT pHTInfo = ieee->pHTInfo;
+ //const typeof( ((struct ieee80211_device *)0)->pHTInfo ) *__mptr = &pHTInfo;
+
+ //printk("pHTinfo:%p, &pHTinfo:%p, mptr:%p, offsetof:%x\n", pHTInfo, &pHTInfo, __mptr, offsetof(struct ieee80211_device, pHTInfo));
+ //printk("===>ieee:%p,\n", ieee);
+ // ShortGI support
+ pHTInfo->bRegShortGI20MHz= 1;
+ pHTInfo->bRegShortGI40MHz= 1;
+
+ // 40MHz channel support
+ pHTInfo->bRegBW40MHz = 1;
+
+ // CCK rate support in 40MHz channel
+ if(pHTInfo->bRegBW40MHz)
+ pHTInfo->bRegSuppCCK = 1;
+ else
+ pHTInfo->bRegSuppCCK = true;
+
+ // AMSDU related
+ pHTInfo->nAMSDU_MaxSize = 7935UL;
+ pHTInfo->bAMSDU_Support = 0;
+
+ // AMPDU related
+ pHTInfo->bAMPDUEnable = 1;
+ pHTInfo->AMPDU_Factor = 2; //// 0: 2n13(8K), 1:2n14(16K), 2:2n15(32K), 3:2n16(64k)
+ pHTInfo->MPDU_Density = 0;// 0: No restriction, 1: 1/8usec, 2: 1/4usec, 3: 1/2usec, 4: 1usec, 5: 2usec, 6: 4usec, 7:8usec
+
+ // MIMO Power Save
+ pHTInfo->SelfMimoPs = 3;// 0: Static Mimo Ps, 1: Dynamic Mimo Ps, 3: No Limitation, 2: Reserved(Set to 3 automatically.)
+ if(pHTInfo->SelfMimoPs == 2)
+ pHTInfo->SelfMimoPs = 3;
+ // 8190 only. Assign rate operation mode to firmware
+ ieee->bTxDisableRateFallBack = 0;
+ ieee->bTxUseDriverAssingedRate = 0;
+
+#ifdef TO_DO_LIST
+ // 8190 only. Assign duration operation mode to firmware
+ pMgntInfo->bTxEnableFwCalcDur = (BOOLEAN)pNdisCommon->bRegTxEnableFwCalcDur;
+#endif
+ // 8190 only, Realtek proprietary aggregation mode
+ // Set MPDUDensity=2, 1: Set MPDUDensity=2(32k) for Realtek AP and set MPDUDensity=0(8k) for others
+ pHTInfo->bRegRT2RTAggregation = 1;//0: Set MPDUDensity=2, 1: Set MPDUDensity=2(32k) for Realtek AP and set MPDUDensity=0(8k) for others
+
+ // For Rx Reorder Control
+ pHTInfo->bRegRxReorderEnable = 1;
+ pHTInfo->RxReorderWinSize = 64;
+ pHTInfo->RxReorderPendingTime = 30;
+
+#ifdef USB_TX_DRIVER_AGGREGATION_ENABLE
+ pHTInfo->UsbTxAggrNum = 4;
+#endif
+#ifdef USB_RX_AGGREGATION_SUPPORT
+ pHTInfo->UsbRxFwAggrEn = 1;
+ pHTInfo->UsbRxFwAggrPageNum = 24;
+ pHTInfo->UsbRxFwAggrPacketNum = 8;
+ pHTInfo->UsbRxFwAggrTimeout = 16; ////usb rx FW aggregation timeout threshold.It's in units of 64us
+#endif
+
+
+}
+/********************************************************************************************************************
+ *function: This function print out each field on HT capability IE mainly from (Beacon/ProbeRsp/AssocReq)
+ * input: u8* CapIE //Capability IE to be printed out
+ * u8* TitleString //mainly print out caller function
+ * output: none
+ * return: none
+ * notice: Driver should not print out this message by default.
+ * *****************************************************************************************************************/
+void HTDebugHTCapability(u8 *CapIE, u8 *TitleString )
+{
+
+ static u8 EWC11NHTCap[] = {0x00, 0x90, 0x4c, 0x33}; // For 11n EWC definition, 2007.07.17, by Emily
+ PHT_CAPABILITY_ELE pCapELE;
+
+ if(!memcmp(CapIE, EWC11NHTCap, sizeof(EWC11NHTCap)))
+ {
+ //EWC IE
+ IEEE80211_DEBUG(IEEE80211_DL_HT, "EWC IE in %s()\n", __func__);
+ pCapELE = (PHT_CAPABILITY_ELE)(&CapIE[4]);
+ }else
+ pCapELE = (PHT_CAPABILITY_ELE)(&CapIE[0]);
+
+ IEEE80211_DEBUG(IEEE80211_DL_HT, "<Log HT Capability>. Called by %s\n", TitleString );
+
+ IEEE80211_DEBUG(IEEE80211_DL_HT, "\tSupported Channel Width = %s\n", (pCapELE->ChlWidth)?"20MHz": "20/40MHz");
+ IEEE80211_DEBUG(IEEE80211_DL_HT, "\tSupport Short GI for 20M = %s\n", (pCapELE->ShortGI20Mhz)?"YES": "NO");
+ IEEE80211_DEBUG(IEEE80211_DL_HT, "\tSupport Short GI for 40M = %s\n", (pCapELE->ShortGI40Mhz)?"YES": "NO");
+ IEEE80211_DEBUG(IEEE80211_DL_HT, "\tSupport TX STBC = %s\n", (pCapELE->TxSTBC)?"YES": "NO");
+ IEEE80211_DEBUG(IEEE80211_DL_HT, "\tMax AMSDU Size = %s\n", (pCapELE->MaxAMSDUSize)?"3839": "7935");
+ IEEE80211_DEBUG(IEEE80211_DL_HT, "\tSupport CCK in 20/40 mode = %s\n", (pCapELE->DssCCk)?"YES": "NO");
+ IEEE80211_DEBUG(IEEE80211_DL_HT, "\tMax AMPDU Factor = %d\n", pCapELE->MaxRxAMPDUFactor);
+ IEEE80211_DEBUG(IEEE80211_DL_HT, "\tMPDU Density = %d\n", pCapELE->MPDUDensity);
+ IEEE80211_DEBUG(IEEE80211_DL_HT, "\tMCS Rate Set = [%x][%x][%x][%x][%x]\n", pCapELE->MCS[0],\
+ pCapELE->MCS[1], pCapELE->MCS[2], pCapELE->MCS[3], pCapELE->MCS[4]);
+ return;
+
+}
+/********************************************************************************************************************
+ *function: This function print out each field on HT Information IE mainly from (Beacon/ProbeRsp)
+ * input: u8* InfoIE //Capability IE to be printed out
+ * u8* TitleString //mainly print out caller function
+ * output: none
+ * return: none
+ * notice: Driver should not print out this message by default.
+ * *****************************************************************************************************************/
+void HTDebugHTInfo(u8 *InfoIE, u8 *TitleString)
+{
+
+ static u8 EWC11NHTInfo[] = {0x00, 0x90, 0x4c, 0x34}; // For 11n EWC definition, 2007.07.17, by Emily
+ PHT_INFORMATION_ELE pHTInfoEle;
+
+ if(!memcmp(InfoIE, EWC11NHTInfo, sizeof(EWC11NHTInfo)))
+ {
+ // Not EWC IE
+ IEEE80211_DEBUG(IEEE80211_DL_HT, "EWC IE in %s()\n", __func__);
+ pHTInfoEle = (PHT_INFORMATION_ELE)(&InfoIE[4]);
+ }else
+ pHTInfoEle = (PHT_INFORMATION_ELE)(&InfoIE[0]);
+
+
+ IEEE80211_DEBUG(IEEE80211_DL_HT, "<Log HT Information Element>. Called by %s\n", TitleString);
+
+ IEEE80211_DEBUG(IEEE80211_DL_HT, "\tPrimary channel = %d\n", pHTInfoEle->ControlChl);
+ IEEE80211_DEBUG(IEEE80211_DL_HT, "\tSenondary channel =");
+ switch (pHTInfoEle->ExtChlOffset)
+ {
+ case 0:
+ IEEE80211_DEBUG(IEEE80211_DL_HT, "Not Present\n");
+ break;
+ case 1:
+ IEEE80211_DEBUG(IEEE80211_DL_HT, "Upper channel\n");
+ break;
+ case 2:
+ IEEE80211_DEBUG(IEEE80211_DL_HT, "Reserved. Eooro!!!\n");
+ break;
+ case 3:
+ IEEE80211_DEBUG(IEEE80211_DL_HT, "Lower Channel\n");
+ break;
+ }
+ IEEE80211_DEBUG(IEEE80211_DL_HT, "\tRecommended channel width = %s\n", (pHTInfoEle->RecommemdedTxWidth)?"20Mhz": "40Mhz");
+
+ IEEE80211_DEBUG(IEEE80211_DL_HT, "\tOperation mode for protection = ");
+ switch (pHTInfoEle->OptMode)
+ {
+ case 0:
+ IEEE80211_DEBUG(IEEE80211_DL_HT, "No Protection\n");
+ break;
+ case 1:
+ IEEE80211_DEBUG(IEEE80211_DL_HT, "HT non-member protection mode\n");
+ break;
+ case 2:
+ IEEE80211_DEBUG(IEEE80211_DL_HT, "Suggest to open protection\n");
+ break;
+ case 3:
+ IEEE80211_DEBUG(IEEE80211_DL_HT, "HT mixed mode\n");
+ break;
+ }
+
+ IEEE80211_DEBUG(IEEE80211_DL_HT, "\tBasic MCS Rate Set = [%x][%x][%x][%x][%x]\n", pHTInfoEle->BasicMSC[0],\
+ pHTInfoEle->BasicMSC[1], pHTInfoEle->BasicMSC[2], pHTInfoEle->BasicMSC[3], pHTInfoEle->BasicMSC[4]);
+ return;
+}
+
+/*
+* Return: true if station in half n mode and AP supports 40 bw
+*/
+static bool IsHTHalfNmode40Bandwidth(struct ieee80211_device *ieee)
+{
+ bool retValue = false;
+ PRT_HIGH_THROUGHPUT pHTInfo = ieee->pHTInfo;
+
+ if(pHTInfo->bCurrentHTSupport == false ) // wireless is n mode
+ retValue = false;
+ else if(pHTInfo->bRegBW40MHz == false) // station supports 40 bw
+ retValue = false;
+ else if(!ieee->GetHalfNmodeSupportByAPsHandler(ieee->dev)) // station in half n mode
+ retValue = false;
+ else if(((PHT_CAPABILITY_ELE)(pHTInfo->PeerHTCapBuf))->ChlWidth) // ap support 40 bw
+ retValue = true;
+ else
+ retValue = false;
+
+ return retValue;
+}
+
+static bool IsHTHalfNmodeSGI(struct ieee80211_device *ieee, bool is40MHz)
+{
+ bool retValue = false;
+ PRT_HIGH_THROUGHPUT pHTInfo = ieee->pHTInfo;
+
+ if(pHTInfo->bCurrentHTSupport == false ) // wireless is n mode
+ retValue = false;
+ else if(!ieee->GetHalfNmodeSupportByAPsHandler(ieee->dev)) // station in half n mode
+ retValue = false;
+ else if(is40MHz) // ap support 40 bw
+ {
+ if(((PHT_CAPABILITY_ELE)(pHTInfo->PeerHTCapBuf))->ShortGI40Mhz) // ap support 40 bw short GI
+ retValue = true;
+ else
+ retValue = false;
+ }
+ else
+ {
+ if(((PHT_CAPABILITY_ELE)(pHTInfo->PeerHTCapBuf))->ShortGI20Mhz) // ap support 40 bw short GI
+ retValue = true;
+ else
+ retValue = false;
+ }
+
+ return retValue;
+}
+
+u16 HTHalfMcsToDataRate(struct ieee80211_device *ieee, u8 nMcsRate)
+{
+
+ u8 is40MHz;
+ u8 isShortGI;
+
+ is40MHz = (IsHTHalfNmode40Bandwidth(ieee))?1:0;
+ isShortGI = (IsHTHalfNmodeSGI(ieee, is40MHz))? 1:0;
+
+ return MCS_DATA_RATE[is40MHz][isShortGI][(nMcsRate&0x7f)];
+}
+
+
+u16 HTMcsToDataRate(struct ieee80211_device *ieee, u8 nMcsRate)
+{
+ PRT_HIGH_THROUGHPUT pHTInfo = ieee->pHTInfo;
+
+ u8 is40MHz = (pHTInfo->bCurBW40MHz)?1:0;
+ u8 isShortGI = (pHTInfo->bCurBW40MHz)?
+ ((pHTInfo->bCurShortGI40MHz)?1:0):
+ ((pHTInfo->bCurShortGI20MHz)?1:0);
+ return MCS_DATA_RATE[is40MHz][isShortGI][(nMcsRate&0x7f)];
+}
+
+/********************************************************************************************************************
+ *function: This function returns current datarate.
+ * input: struct ieee80211_device* ieee
+ * u8 nDataRate
+ * output: none
+ * return: tx rate
+ * notice: quite unsure about how to use this function //wb
+ * *****************************************************************************************************************/
+u16 TxCountToDataRate(struct ieee80211_device *ieee, u8 nDataRate)
+{
+ //PRT_HIGH_THROUGHPUT pHTInfo = ieee->pHTInfo;
+ u16 CCKOFDMRate[12] = {0x02 , 0x04 , 0x0b , 0x16 , 0x0c , 0x12 , 0x18 , 0x24 , 0x30 , 0x48 , 0x60 , 0x6c};
+ u8 is40MHz = 0;
+ u8 isShortGI = 0;
+
+ if(nDataRate < 12)
+ {
+ return CCKOFDMRate[nDataRate];
+ }
+ else
+ {
+ if (nDataRate >= 0x10 && nDataRate <= 0x1f)//if(nDataRate > 11 && nDataRate < 28 )
+ {
+ is40MHz = 0;
+ isShortGI = 0;
+
+ // nDataRate = nDataRate - 12;
+ }
+ else if(nDataRate >=0x20 && nDataRate <= 0x2f ) //(27, 44)
+ {
+ is40MHz = 1;
+ isShortGI = 0;
+
+ //nDataRate = nDataRate - 28;
+ }
+ else if(nDataRate >= 0x30 && nDataRate <= 0x3f ) //(43, 60)
+ {
+ is40MHz = 0;
+ isShortGI = 1;
+
+ //nDataRate = nDataRate - 44;
+ }
+ else if(nDataRate >= 0x40 && nDataRate <= 0x4f ) //(59, 76)
+ {
+ is40MHz = 1;
+ isShortGI = 1;
+
+ //nDataRate = nDataRate - 60;
+ }
+ return MCS_DATA_RATE[is40MHz][isShortGI][nDataRate&0xf];
+ }
+}
+
+
+
+bool IsHTHalfNmodeAPs(struct ieee80211_device *ieee)
+{
+ bool retValue = false;
+ struct ieee80211_network *net = &ieee->current_network;
+ if((memcmp(net->bssid, BELKINF5D8233V1_RALINK, 3)==0) ||
+ (memcmp(net->bssid, BELKINF5D82334V3_RALINK, 3)==0) ||
+ (memcmp(net->bssid, PCI_RALINK, 3)==0) ||
+ (memcmp(net->bssid, EDIMAX_RALINK, 3)==0) ||
+ (memcmp(net->bssid, AIRLINK_RALINK, 3)==0) ||
+ (net->ralink_cap_exist))
+ retValue = true;
+ else if((memcmp(net->bssid, UNKNOWN_BORADCOM, 3)==0) ||
+ (memcmp(net->bssid, LINKSYSWRT330_LINKSYSWRT300_BROADCOM, 3)==0)||
+ (memcmp(net->bssid, LINKSYSWRT350_LINKSYSWRT150_BROADCOM, 3)==0)||
+ (memcmp(net->bssid, NETGEAR834Bv2_BROADCOM, 3)==0) ||
+ (net->broadcom_cap_exist))
+ retValue = true;
+ else if(net->bssht.bdRT2RTAggregation)
+ retValue = true;
+ else
+ retValue = false;
+
+ return retValue;
+}
+
+/********************************************************************************************************************
+ *function: This function returns peer IOT.
+ * input: struct ieee80211_device* ieee
+ * output: none
+ * return:
+ * notice:
+ * *****************************************************************************************************************/
+static void HTIOTPeerDetermine(struct ieee80211_device *ieee)
+{
+ PRT_HIGH_THROUGHPUT pHTInfo = ieee->pHTInfo;
+ struct ieee80211_network *net = &ieee->current_network;
+ if(net->bssht.bdRT2RTAggregation)
+ pHTInfo->IOTPeer = HT_IOT_PEER_REALTEK;
+ else if(net->broadcom_cap_exist)
+ pHTInfo->IOTPeer = HT_IOT_PEER_BROADCOM;
+ else if((memcmp(net->bssid, UNKNOWN_BORADCOM, 3)==0) ||
+ (memcmp(net->bssid, LINKSYSWRT330_LINKSYSWRT300_BROADCOM, 3)==0)||
+ (memcmp(net->bssid, LINKSYSWRT350_LINKSYSWRT150_BROADCOM, 3)==0)||
+ (memcmp(net->bssid, NETGEAR834Bv2_BROADCOM, 3)==0) )
+ pHTInfo->IOTPeer = HT_IOT_PEER_BROADCOM;
+ else if((memcmp(net->bssid, BELKINF5D8233V1_RALINK, 3)==0) ||
+ (memcmp(net->bssid, BELKINF5D82334V3_RALINK, 3)==0) ||
+ (memcmp(net->bssid, PCI_RALINK, 3)==0) ||
+ (memcmp(net->bssid, EDIMAX_RALINK, 3)==0) ||
+ (memcmp(net->bssid, AIRLINK_RALINK, 3)==0) ||
+ net->ralink_cap_exist)
+ pHTInfo->IOTPeer = HT_IOT_PEER_RALINK;
+ else if(net->atheros_cap_exist)
+ pHTInfo->IOTPeer = HT_IOT_PEER_ATHEROS;
+ else if(memcmp(net->bssid, CISCO_BROADCOM, 3)==0)
+ pHTInfo->IOTPeer = HT_IOT_PEER_CISCO;
+ else
+ pHTInfo->IOTPeer = HT_IOT_PEER_UNKNOWN;
+
+ IEEE80211_DEBUG(IEEE80211_DL_IOT, "Joseph debug!! IOTPEER: %x\n", pHTInfo->IOTPeer);
+}
+/********************************************************************************************************************
+ *function: Check whether driver should declare received rate up to MCS13 only since some chipset is not good
+ * at receiving MCS14~15 frame from some AP.
+ * input: struct ieee80211_device* ieee
+ * u8 * PeerMacAddr
+ * output: none
+ * return: return 1 if driver should declare MCS13 only(otherwise return 0)
+ * *****************************************************************************************************************/
+static u8 HTIOTActIsDisableMCS14(struct ieee80211_device *ieee, u8 *PeerMacAddr)
+{
+ return 0;
+ }
+
+
+/**
+* Function: HTIOTActIsDisableMCS15
+*
+* Overview: Check whether driver should declare capability of receiving MCS15
+*
+* Input:
+* PADAPTER Adapter,
+*
+* Output: None
+* Return: true if driver should disable MCS15
+* 2008.04.15 Emily
+*/
+static bool HTIOTActIsDisableMCS15(struct ieee80211_device *ieee)
+{
+ bool retValue = false;
+
+#ifdef TODO
+ // Apply for 819u only
+#if (HAL_CODE_BASE==RTL8192)
+
+#if (DEV_BUS_TYPE == USB_INTERFACE)
+ // Alway disable MCS15 by Jerry Chang's request.by Emily, 2008.04.15
+ retValue = true;
+#elif (DEV_BUS_TYPE == PCI_INTERFACE)
+ // Enable MCS15 if the peer is Cisco AP. by Emily, 2008.05.12
+// if(pBssDesc->bCiscoCapExist)
+// retValue = false;
+// else
+ retValue = false;
+#endif
+#endif
+#endif
+ // Jerry Chang suggest that 8190 1x2 does not need to disable MCS15
+
+ return retValue;
+}
+
+/**
+* Function: HTIOTActIsDisableMCSTwoSpatialStream
+*
+* Overview: Check whether driver should declare capability of receiving All 2 ss packets
+*
+* Input:
+* PADAPTER Adapter,
+*
+* Output: None
+* Return: true if driver should disable all two spatial stream packet
+* 2008.04.21 Emily
+*/
+static bool HTIOTActIsDisableMCSTwoSpatialStream(struct ieee80211_device *ieee,
+ u8 *PeerMacAddr)
+{
+#ifdef TODO
+ // Apply for 819u only
+#endif
+ return false;
+}
+
+/********************************************************************************************************************
+ *function: Check whether driver should disable EDCA turbo mode
+ * input: struct ieee80211_device* ieee
+ * u8* PeerMacAddr
+ * output: none
+ * return: return 1 if driver should disable EDCA turbo mode(otherwise return 0)
+ * *****************************************************************************************************************/
+static u8 HTIOTActIsDisableEDCATurbo(struct ieee80211_device *ieee,
+ u8 *PeerMacAddr)
+{ /* default enable EDCA Turbo mode. */
+ return false;
+}
+
+/********************************************************************************************************************
+ *function: Check whether we need to use OFDM to sned MGNT frame for broadcom AP
+ * input: struct ieee80211_network *network //current network we live
+ * output: none
+ * return: return 1 if true
+ * *****************************************************************************************************************/
+static u8 HTIOTActIsMgntUseCCK6M(struct ieee80211_network *network)
+{
+ u8 retValue = 0;
+
+ // 2008/01/25 MH Judeg if we need to use OFDM to sned MGNT frame for broadcom AP.
+ // 2008/01/28 MH We must prevent that we select null bssid to link.
+
+ if (network->broadcom_cap_exist)
+ {
+ retValue = 1;
+ }
+
+ return retValue;
+}
+
+static u8 HTIOTActIsCCDFsync(u8 *PeerMacAddr)
+{
+ u8 retValue = 0;
+ if( (memcmp(PeerMacAddr, UNKNOWN_BORADCOM, 3)==0) ||
+ (memcmp(PeerMacAddr, LINKSYSWRT330_LINKSYSWRT300_BROADCOM, 3)==0) ||
+ (memcmp(PeerMacAddr, LINKSYSWRT350_LINKSYSWRT150_BROADCOM, 3) ==0))
+ {
+ retValue = 1;
+ }
+
+ return retValue;
+}
+
+void HTResetIOTSetting(
+ PRT_HIGH_THROUGHPUT pHTInfo
+)
+{
+ pHTInfo->IOTAction = 0;
+ pHTInfo->IOTPeer = HT_IOT_PEER_UNKNOWN;
+}
+
+
+/********************************************************************************************************************
+ *function: Construct Capablility Element in Beacon... if HTEnable is turned on
+ * input: struct ieee80211_device* ieee
+ * u8* posHTCap //pointer to store Capability Ele
+ * u8* len //store length of CE
+ * u8 IsEncrypt //whether encrypt, needed further
+ * output: none
+ * return: none
+ * notice: posHTCap can't be null and should be initialized before.
+ * *****************************************************************************************************************/
+void HTConstructCapabilityElement(struct ieee80211_device *ieee, u8 *posHTCap, u8 *len, u8 IsEncrypt)
+{
+ PRT_HIGH_THROUGHPUT pHT = ieee->pHTInfo;
+ PHT_CAPABILITY_ELE pCapELE = NULL;
+ //u8 bIsDeclareMCS13;
+
+ if ((posHTCap == NULL) || (pHT == NULL))
+ {
+ IEEE80211_DEBUG(IEEE80211_DL_ERR, "posHTCap or pHTInfo can't be null in HTConstructCapabilityElement()\n");
+ return;
+ }
+ memset(posHTCap, 0, *len);
+ if(pHT->ePeerHTSpecVer == HT_SPEC_VER_EWC)
+ {
+ u8 EWC11NHTCap[] = {0x00, 0x90, 0x4c, 0x33}; // For 11n EWC definition, 2007.07.17, by Emily
+ memcpy(posHTCap, EWC11NHTCap, sizeof(EWC11NHTCap));
+ pCapELE = (PHT_CAPABILITY_ELE)&(posHTCap[4]);
+ }else
+ {
+ pCapELE = (PHT_CAPABILITY_ELE)posHTCap;
+ }
+
+
+ //HT capability info
+ pCapELE->AdvCoding = 0; // This feature is not supported now!!
+ if(ieee->GetHalfNmodeSupportByAPsHandler(ieee->dev))
+ {
+ pCapELE->ChlWidth = 0;
+ }
+ else
+ {
+ pCapELE->ChlWidth = (pHT->bRegBW40MHz?1:0);
+ }
+
+// pCapELE->ChlWidth = (pHT->bRegBW40MHz?1:0);
+ pCapELE->MimoPwrSave = pHT->SelfMimoPs;
+ pCapELE->GreenField = 0; // This feature is not supported now!!
+ pCapELE->ShortGI20Mhz = 1; // We can receive Short GI!!
+ pCapELE->ShortGI40Mhz = 1; // We can receive Short GI!!
+ //DbgPrint("TX HT cap/info ele BW=%d SG20=%d SG40=%d\n\r",
+ //pCapELE->ChlWidth, pCapELE->ShortGI20Mhz, pCapELE->ShortGI40Mhz);
+ pCapELE->TxSTBC = 1;
+ pCapELE->RxSTBC = 0;
+ pCapELE->DelayBA = 0; // Do not support now!!
+ pCapELE->MaxAMSDUSize = (MAX_RECEIVE_BUFFER_SIZE>=7935)?1:0;
+ pCapELE->DssCCk = ((pHT->bRegBW40MHz)?(pHT->bRegSuppCCK?1:0):0);
+ pCapELE->PSMP = 0; // Do not support now!!
+ pCapELE->LSigTxopProtect = 0; // Do not support now!!
+
+
+ //MAC HT parameters info
+ // TODO: Nedd to take care of this part
+ IEEE80211_DEBUG(IEEE80211_DL_HT, "TX HT cap/info ele BW=%d MaxAMSDUSize:%d DssCCk:%d\n", pCapELE->ChlWidth, pCapELE->MaxAMSDUSize, pCapELE->DssCCk);
+
+ if (IsEncrypt) {
+ pCapELE->MPDUDensity = 7; // 8us
+ pCapELE->MaxRxAMPDUFactor = 2; // 2 is for 32 K and 3 is 64K
+ }
+ else
+ {
+ pCapELE->MaxRxAMPDUFactor = 3; // 2 is for 32 K and 3 is 64K
+ pCapELE->MPDUDensity = 0; // no density
+ }
+
+ //Supported MCS set
+ memcpy(pCapELE->MCS, ieee->Regdot11HTOperationalRateSet, 16);
+ if(pHT->IOTAction & HT_IOT_ACT_DISABLE_MCS15)
+ pCapELE->MCS[1] &= 0x7f;
+
+ if(pHT->IOTAction & HT_IOT_ACT_DISABLE_MCS14)
+ pCapELE->MCS[1] &= 0xbf;
+
+ if(pHT->IOTAction & HT_IOT_ACT_DISABLE_ALL_2SS)
+ pCapELE->MCS[1] &= 0x00;
+
+ // 2008.06.12
+ // For RTL819X, if pairwisekey = wep/tkip, ap is ralink, we support only MCS0~7.
+ if (ieee->GetHalfNmodeSupportByAPsHandler(ieee->dev))
+ {
+ int i;
+ for(i = 1; i< 16; i++)
+ pCapELE->MCS[i] = 0;
+ }
+
+ //Extended HT Capability Info
+ memset(&pCapELE->ExtHTCapInfo, 0, 2);
+
+
+ //TXBF Capabilities
+ memset(pCapELE->TxBFCap, 0, 4);
+
+ //Antenna Selection Capabilities
+ pCapELE->ASCap = 0;
+//add 2 to give space for element ID and len when construct frames
+ if(pHT->ePeerHTSpecVer == HT_SPEC_VER_EWC)
+ *len = 30 + 2;
+ else
+ *len = 26 + 2;
+
+
+
+// IEEE80211_DEBUG_DATA(IEEE80211_DL_DATA | IEEE80211_DL_HT, posHTCap, *len -2);
+
+ //Print each field in detail. Driver should not print out this message by default
+// HTDebugHTCapability(posHTCap, (u8*)"HTConstructCapability()");
+ return;
+
+}
+/********************************************************************************************************************
+ *function: Construct Information Element in Beacon... if HTEnable is turned on
+ * input: struct ieee80211_device* ieee
+ * u8* posHTCap //pointer to store Information Ele
+ * u8* len //store len of
+ * u8 IsEncrypt //whether encrypt, needed further
+ * output: none
+ * return: none
+ * notice: posHTCap can't be null and be initialized before. only AP and IBSS sta should do this
+ * *****************************************************************************************************************/
+void HTConstructInfoElement(struct ieee80211_device *ieee, u8 *posHTInfo, u8 *len, u8 IsEncrypt)
+{
+ PRT_HIGH_THROUGHPUT pHT = ieee->pHTInfo;
+ PHT_INFORMATION_ELE pHTInfoEle = (PHT_INFORMATION_ELE)posHTInfo;
+ if ((posHTInfo == NULL) || (pHTInfoEle == NULL))
+ {
+ IEEE80211_DEBUG(IEEE80211_DL_ERR, "posHTInfo or pHTInfoEle can't be null in HTConstructInfoElement()\n");
+ return;
+ }
+
+ memset(posHTInfo, 0, *len);
+ if ( (ieee->iw_mode == IW_MODE_ADHOC) || (ieee->iw_mode == IW_MODE_MASTER)) //ap mode is not currently supported
+ {
+ pHTInfoEle->ControlChl = ieee->current_network.channel;
+ pHTInfoEle->ExtChlOffset = ((pHT->bRegBW40MHz == false)?HT_EXTCHNL_OFFSET_NO_EXT:
+ (ieee->current_network.channel<=6)?
+ HT_EXTCHNL_OFFSET_UPPER:HT_EXTCHNL_OFFSET_LOWER);
+ pHTInfoEle->RecommemdedTxWidth = pHT->bRegBW40MHz;
+ pHTInfoEle->RIFS = 0;
+ pHTInfoEle->PSMPAccessOnly = 0;
+ pHTInfoEle->SrvIntGranularity = 0;
+ pHTInfoEle->OptMode = pHT->CurrentOpMode;
+ pHTInfoEle->NonGFDevPresent = 0;
+ pHTInfoEle->DualBeacon = 0;
+ pHTInfoEle->SecondaryBeacon = 0;
+ pHTInfoEle->LSigTxopProtectFull = 0;
+ pHTInfoEle->PcoActive = 0;
+ pHTInfoEle->PcoPhase = 0;
+
+ memset(pHTInfoEle->BasicMSC, 0, 16);
+
+
+ *len = 22 + 2; //same above
+
+ }
+ else
+ {
+ //STA should not generate High Throughput Information Element
+ *len = 0;
+ }
+ //IEEE80211_DEBUG_DATA(IEEE80211_DL_DATA | IEEE80211_DL_HT, posHTInfo, *len - 2);
+ //HTDebugHTInfo(posHTInfo, "HTConstructInforElement");
+ return;
+}
+
+/*
+ * According to experiment, Realtek AP to STA (based on rtl8190) may achieve best performance
+ * if both STA and AP set limitation of aggregation size to 32K, that is, set AMPDU density to 2
+ * (Ref: IEEE 11n specification). However, if Realtek STA associates to other AP, STA should set
+ * limitation of aggregation size to 8K, otherwise, performance of traffic stream from STA to AP
+ * will be much less than the traffic stream from AP to STA if both of the stream runs concurrently
+ * at the same time.
+ *
+ * Frame Format
+ * Element ID Length OUI Type1 Reserved
+ * 1 byte 1 byte 3 bytes 1 byte 1 byte
+ *
+ * OUI = 0x00, 0xe0, 0x4c,
+ * Type = 0x02
+ * Reserved = 0x00
+ *
+ * 2007.8.21 by Emily
+*/
+/********************************************************************************************************************
+ *function: Construct Information Element in Beacon... in RT2RT condition
+ * input: struct ieee80211_device* ieee
+ * u8* posRT2RTAgg //pointer to store Information Ele
+ * u8* len //store len
+ * output: none
+ * return: none
+ * notice:
+ * *****************************************************************************************************************/
+void HTConstructRT2RTAggElement(struct ieee80211_device *ieee, u8 *posRT2RTAgg, u8 *len)
+{
+ if (posRT2RTAgg == NULL) {
+ IEEE80211_DEBUG(IEEE80211_DL_ERR, "posRT2RTAgg can't be null in HTConstructRT2RTAggElement()\n");
+ return;
+ }
+ memset(posRT2RTAgg, 0, *len);
+ *posRT2RTAgg++ = 0x00;
+ *posRT2RTAgg++ = 0xe0;
+ *posRT2RTAgg++ = 0x4c;
+ *posRT2RTAgg++ = 0x02;
+ *posRT2RTAgg++ = 0x01;
+ *posRT2RTAgg = 0x10;//*posRT2RTAgg = 0x02;
+
+ if (ieee->bSupportRemoteWakeUp) {
+ *posRT2RTAgg |= 0x08;//RT_HT_CAP_USE_WOW;
+ }
+
+ *len = 6 + 2;
+ return;
+#ifdef TODO
+#if (HAL_CODE_BASE == RTL8192 && DEV_BUS_TYPE == USB_INTERFACE)
+ /*
+ //Emily. If it is required to Ask Realtek AP to send AMPDU during AES mode, enable this
+ section of code.
+ if(IS_UNDER_11N_AES_MODE(Adapter))
+ {
+ posRT2RTAgg->Octet[5] |=RT_HT_CAP_USE_AMPDU;
+ }else
+ {
+ posRT2RTAgg->Octet[5] &= 0xfb;
+ }
+ */
+
+#else
+ // Do Nothing
+#endif
+
+ posRT2RTAgg->Length = 6;
+#endif
+
+
+
+
+}
+
+
+/********************************************************************************************************************
+ *function: Pick the right Rate Adaptive table to use
+ * input: struct ieee80211_device* ieee
+ * u8* pOperateMCS //A pointer to MCS rate bitmap
+ * return: always we return true
+ * notice:
+ * *****************************************************************************************************************/
+static u8 HT_PickMCSRate(struct ieee80211_device *ieee, u8 *pOperateMCS)
+{
+ u8 i;
+ if (pOperateMCS == NULL)
+ {
+ IEEE80211_DEBUG(IEEE80211_DL_ERR, "pOperateMCS can't be null in HT_PickMCSRate()\n");
+ return false;
+ }
+
+ switch (ieee->mode)
+ {
+ case IEEE_A:
+ case IEEE_B:
+ case IEEE_G:
+ //legacy rate routine handled at selectedrate
+
+ //no MCS rate
+ for(i=0;i<=15;i++){
+ pOperateMCS[i] = 0;
+ }
+ break;
+
+ case IEEE_N_24G: //assume CCK rate ok
+ case IEEE_N_5G:
+ // Legacy part we only use 6, 5.5,2,1 for N_24G and 6 for N_5G.
+ // Legacy part shall be handled at SelectRateSet().
+
+ //HT part
+ // TODO: may be different if we have different number of antenna
+ pOperateMCS[0] &=RATE_ADPT_1SS_MASK; //support MCS 0~7
+ pOperateMCS[1] &=RATE_ADPT_2SS_MASK;
+ pOperateMCS[3] &=RATE_ADPT_MCS32_MASK;
+ break;
+
+ //should never reach here
+ default:
+
+ break;
+
+ }
+
+ return true;
+}
+
+/*
+* Description:
+* This function will get the highest speed rate in input MCS set.
+*
+* /param Adapter Pionter to Adapter entity
+* pMCSRateSet Pointer to MCS rate bitmap
+* pMCSFilter Pointer to MCS rate filter
+*
+* /return Highest MCS rate included in pMCSRateSet and filtered by pMCSFilter.
+*
+*/
+/********************************************************************************************************************
+ *function: This function will get the highest speed rate in input MCS set.
+ * input: struct ieee80211_device* ieee
+ * u8* pMCSRateSet //Pointer to MCS rate bitmap
+ * u8* pMCSFilter //Pointer to MCS rate filter
+ * return: Highest MCS rate included in pMCSRateSet and filtered by pMCSFilter
+ * notice:
+ * *****************************************************************************************************************/
+u8 HTGetHighestMCSRate(struct ieee80211_device *ieee, u8 *pMCSRateSet, u8 *pMCSFilter)
+{
+ u8 i, j;
+ u8 bitMap;
+ u8 mcsRate = 0;
+ u8 availableMcsRate[16];
+ if (pMCSRateSet == NULL || pMCSFilter == NULL)
+ {
+ IEEE80211_DEBUG(IEEE80211_DL_ERR, "pMCSRateSet or pMCSFilter can't be null in HTGetHighestMCSRate()\n");
+ return false;
+ }
+ for(i=0; i<16; i++)
+ availableMcsRate[i] = pMCSRateSet[i] & pMCSFilter[i];
+
+ for(i = 0; i < 16; i++)
+ {
+ if(availableMcsRate[i] != 0)
+ break;
+ }
+ if(i == 16)
+ return false;
+
+ for(i = 0; i < 16; i++)
+ {
+ if (availableMcsRate[i] != 0)
+ {
+ bitMap = availableMcsRate[i];
+ for(j = 0; j < 8; j++)
+ {
+ if ((bitMap%2) != 0)
+ {
+ if(HTMcsToDataRate(ieee, (8*i+j)) > HTMcsToDataRate(ieee, mcsRate))
+ mcsRate = (8*i+j);
+ }
+ bitMap >>= 1;
+ }
+ }
+ }
+ return (mcsRate|0x80);
+}
+
+
+
+/*
+**
+**1.Filter our operation rate set with AP's rate set
+**2.shall reference channel bandwidth, STBC, Antenna number
+**3.generate rate adative table for firmware
+**David 20060906
+**
+** \pHTSupportedCap: the connected STA's supported rate Capability element
+*/
+static u8 HTFilterMCSRate(struct ieee80211_device *ieee, u8 *pSupportMCS,
+ u8 *pOperateMCS)
+{
+
+ u8 i=0;
+
+ // filter out operational rate set not supported by AP, the length of it is 16
+ for(i=0;i<=15;i++){
+ pOperateMCS[i] = ieee->Regdot11HTOperationalRateSet[i]&pSupportMCS[i];
+ }
+
+
+ // TODO: adjust our operational rate set according to our channel bandwidth, STBC and Antenna number
+
+ // TODO: fill suggested rate adaptive rate index and give firmware info using Tx command packet
+ // we also shall suggested the first start rate set according to our singal strength
+ HT_PickMCSRate(ieee, pOperateMCS);
+
+ // For RTL819X, if pairwisekey = wep/tkip, we support only MCS0~7.
+ if(ieee->GetHalfNmodeSupportByAPsHandler(ieee->dev))
+ pOperateMCS[1] = 0;
+
+ //
+ // For RTL819X, we support only MCS0~15.
+ // And also, we do not know how to use MCS32 now.
+ //
+ for(i=2; i<=15; i++)
+ pOperateMCS[i] = 0;
+
+ return true;
+}
+void HTSetConnectBwMode(struct ieee80211_device *ieee, HT_CHANNEL_WIDTH Bandwidth, HT_EXTCHNL_OFFSET Offset);
+void HTOnAssocRsp(struct ieee80211_device *ieee)
+{
+ PRT_HIGH_THROUGHPUT pHTInfo = ieee->pHTInfo;
+ PHT_CAPABILITY_ELE pPeerHTCap = NULL;
+ PHT_INFORMATION_ELE pPeerHTInfo = NULL;
+ u16 nMaxAMSDUSize = 0;
+ u8 *pMcsFilter = NULL;
+
+ static u8 EWC11NHTCap[] = {0x00, 0x90, 0x4c, 0x33}; // For 11n EWC definition, 2007.07.17, by Emily
+ static u8 EWC11NHTInfo[] = {0x00, 0x90, 0x4c, 0x34}; // For 11n EWC definition, 2007.07.17, by Emily
+
+ if (pHTInfo->bCurrentHTSupport == false) {
+ IEEE80211_DEBUG(IEEE80211_DL_ERR, "<=== HTOnAssocRsp(): HT_DISABLE\n");
+ return;
+ }
+ IEEE80211_DEBUG(IEEE80211_DL_HT, "===> HTOnAssocRsp_wq(): HT_ENABLE\n");
+// IEEE80211_DEBUG_DATA(IEEE80211_DL_DATA, pHTInfo->PeerHTCapBuf, sizeof(HT_CAPABILITY_ELE));
+// IEEE80211_DEBUG_DATA(IEEE80211_DL_DATA, pHTInfo->PeerHTInfoBuf, sizeof(HT_INFORMATION_ELE));
+
+// HTDebugHTCapability(pHTInfo->PeerHTCapBuf,"HTOnAssocRsp_wq");
+// HTDebugHTInfo(pHTInfo->PeerHTInfoBuf,"HTOnAssocRsp_wq");
+ //
+ if(!memcmp(pHTInfo->PeerHTCapBuf,EWC11NHTCap, sizeof(EWC11NHTCap)))
+ pPeerHTCap = (PHT_CAPABILITY_ELE)(&pHTInfo->PeerHTCapBuf[4]);
+ else
+ pPeerHTCap = (PHT_CAPABILITY_ELE)(pHTInfo->PeerHTCapBuf);
+
+ if(!memcmp(pHTInfo->PeerHTInfoBuf, EWC11NHTInfo, sizeof(EWC11NHTInfo)))
+ pPeerHTInfo = (PHT_INFORMATION_ELE)(&pHTInfo->PeerHTInfoBuf[4]);
+ else
+ pPeerHTInfo = (PHT_INFORMATION_ELE)(pHTInfo->PeerHTInfoBuf);
+
+
+ ////////////////////////////////////////////////////////
+ // Configurations:
+ ////////////////////////////////////////////////////////
+ IEEE80211_DEBUG_DATA(IEEE80211_DL_DATA|IEEE80211_DL_HT, pPeerHTCap, sizeof(HT_CAPABILITY_ELE));
+// IEEE80211_DEBUG_DATA(IEEE80211_DL_DATA|IEEE80211_DL_HT, pPeerHTInfo, sizeof(HT_INFORMATION_ELE));
+ // Config Supported Channel Width setting
+ //
+ HTSetConnectBwMode(ieee, (HT_CHANNEL_WIDTH)(pPeerHTCap->ChlWidth), (HT_EXTCHNL_OFFSET)(pPeerHTInfo->ExtChlOffset));
+
+// if(pHTInfo->bCurBW40MHz == true)
+ pHTInfo->bCurTxBW40MHz = ((pPeerHTInfo->RecommemdedTxWidth == 1)?true:false);
+
+ //
+ // Update short GI/ long GI setting
+ //
+ // TODO:
+ pHTInfo->bCurShortGI20MHz=
+ ((pHTInfo->bRegShortGI20MHz)?((pPeerHTCap->ShortGI20Mhz==1)?true:false):false);
+ pHTInfo->bCurShortGI40MHz=
+ ((pHTInfo->bRegShortGI40MHz)?((pPeerHTCap->ShortGI40Mhz==1)?true:false):false);
+
+ //
+ // Config TX STBC setting
+ //
+ // TODO:
+
+ //
+ // Config DSSS/CCK mode in 40MHz mode
+ //
+ // TODO:
+ pHTInfo->bCurSuppCCK =
+ ((pHTInfo->bRegSuppCCK)?((pPeerHTCap->DssCCk==1)?true:false):false);
+
+
+ //
+ // Config and configure A-MSDU setting
+ //
+ pHTInfo->bCurrent_AMSDU_Support = pHTInfo->bAMSDU_Support;
+
+ nMaxAMSDUSize = (pPeerHTCap->MaxAMSDUSize==0)?3839:7935;
+
+ if(pHTInfo->nAMSDU_MaxSize > nMaxAMSDUSize )
+ pHTInfo->nCurrent_AMSDU_MaxSize = nMaxAMSDUSize;
+ else
+ pHTInfo->nCurrent_AMSDU_MaxSize = pHTInfo->nAMSDU_MaxSize;
+
+
+ //
+ // Config A-MPDU setting
+ //
+ pHTInfo->bCurrentAMPDUEnable = pHTInfo->bAMPDUEnable;
+
+ // <1> Decide AMPDU Factor
+
+ // By Emily
+ if(!pHTInfo->bRegRT2RTAggregation)
+ {
+ // Decide AMPDU Factor according to protocol handshake
+ if(pHTInfo->AMPDU_Factor > pPeerHTCap->MaxRxAMPDUFactor)
+ pHTInfo->CurrentAMPDUFactor = pPeerHTCap->MaxRxAMPDUFactor;
+ else
+ pHTInfo->CurrentAMPDUFactor = pHTInfo->AMPDU_Factor;
+
+ }else
+ {
+ // Set MPDU density to 2 to Realtek AP, and set it to 0 for others
+ // Replace MPDU factor declared in original association response frame format. 2007.08.20 by Emily
+ if (ieee->current_network.bssht.bdRT2RTAggregation)
+ {
+ if (ieee->pairwise_key_type != KEY_TYPE_NA)
+ // Realtek may set 32k in security mode and 64k for others
+ pHTInfo->CurrentAMPDUFactor = pPeerHTCap->MaxRxAMPDUFactor;
+ else
+ pHTInfo->CurrentAMPDUFactor = HT_AGG_SIZE_64K;
+ }else
+ {
+ if(pPeerHTCap->MaxRxAMPDUFactor < HT_AGG_SIZE_32K)
+ pHTInfo->CurrentAMPDUFactor = pPeerHTCap->MaxRxAMPDUFactor;
+ else
+ pHTInfo->CurrentAMPDUFactor = HT_AGG_SIZE_32K;
+ }
+ }
+
+ // <2> Set AMPDU Minimum MPDU Start Spacing
+ // 802.11n 3.0 section 9.7d.3
+ if(pHTInfo->MPDU_Density > pPeerHTCap->MPDUDensity)
+ pHTInfo->CurrentMPDUDensity = pHTInfo->MPDU_Density;
+ else
+ pHTInfo->CurrentMPDUDensity = pPeerHTCap->MPDUDensity;
+ if(ieee->pairwise_key_type != KEY_TYPE_NA )
+ pHTInfo->CurrentMPDUDensity = 7; // 8us
+ // Force TX AMSDU
+
+ // Lanhsin: mark for tmp to avoid deauth by ap from s3
+ //if(memcmp(pMgntInfo->Bssid, NETGEAR834Bv2_BROADCOM, 3)==0)
+ if (0)
+ {
+
+ pHTInfo->bCurrentAMPDUEnable = false;
+ pHTInfo->ForcedAMSDUMode = HT_AGG_FORCE_ENABLE;
+ pHTInfo->ForcedAMSDUMaxSize = 7935;
+
+ pHTInfo->IOTAction |= HT_IOT_ACT_TX_USE_AMSDU_8K;
+ }
+
+ // Rx Reorder Setting
+ pHTInfo->bCurRxReorderEnable = pHTInfo->bRegRxReorderEnable;
+
+ //
+ // Filter out unsupported HT rate for this AP
+ // Update RATR table
+ // This is only for 8190 ,8192 or later product which using firmware to handle rate adaptive mechanism.
+ //
+
+ // Handle Ralink AP bad MCS rate set condition. Joseph.
+ // This fix the bug of Ralink AP. This may be removed in the future.
+ if(pPeerHTCap->MCS[0] == 0)
+ pPeerHTCap->MCS[0] = 0xff;
+
+ HTFilterMCSRate(ieee, pPeerHTCap->MCS, ieee->dot11HTOperationalRateSet);
+
+ //
+ // Config MIMO Power Save setting
+ //
+ pHTInfo->PeerMimoPs = pPeerHTCap->MimoPwrSave;
+ if(pHTInfo->PeerMimoPs == MIMO_PS_STATIC)
+ pMcsFilter = MCS_FILTER_1SS;
+ else
+ pMcsFilter = MCS_FILTER_ALL;
+ //WB add for MCS8 bug
+// pMcsFilter = MCS_FILTER_1SS;
+ ieee->HTHighestOperaRate = HTGetHighestMCSRate(ieee, ieee->dot11HTOperationalRateSet, pMcsFilter);
+ ieee->HTCurrentOperaRate = ieee->HTHighestOperaRate;
+
+ //
+ // Config current operation mode.
+ //
+ pHTInfo->CurrentOpMode = pPeerHTInfo->OptMode;
+
+
+
+}
+
+void HTSetConnectBwModeCallback(struct ieee80211_device *ieee);
+/********************************************************************************************************************
+ *function: initialize HT info(struct PRT_HIGH_THROUGHPUT)
+ * input: struct ieee80211_device* ieee
+ * output: none
+ * return: none
+ * notice: This function is called when * (1) MPInitialization Phase * (2) Receiving of Deauthentication from AP
+********************************************************************************************************************/
+// TODO: Should this funciton be called when receiving of Disassociation?
+void HTInitializeHTInfo(struct ieee80211_device *ieee)
+{
+ PRT_HIGH_THROUGHPUT pHTInfo = ieee->pHTInfo;
+
+ //
+ // These parameters will be reset when receiving deauthentication packet
+ //
+ IEEE80211_DEBUG(IEEE80211_DL_HT, "===========>%s()\n", __func__);
+ pHTInfo->bCurrentHTSupport = false;
+
+ // 40MHz channel support
+ pHTInfo->bCurBW40MHz = false;
+ pHTInfo->bCurTxBW40MHz = false;
+
+ // Short GI support
+ pHTInfo->bCurShortGI20MHz = false;
+ pHTInfo->bCurShortGI40MHz = false;
+ pHTInfo->bForcedShortGI = false;
+
+ // CCK rate support
+ // This flag is set to true to support CCK rate by default.
+ // It will be affected by "pHTInfo->bRegSuppCCK" and AP capabilities only when associate to
+ // 11N BSS.
+ pHTInfo->bCurSuppCCK = true;
+
+ // AMSDU related
+ pHTInfo->bCurrent_AMSDU_Support = false;
+ pHTInfo->nCurrent_AMSDU_MaxSize = pHTInfo->nAMSDU_MaxSize;
+
+ // AMPUD related
+ pHTInfo->CurrentMPDUDensity = pHTInfo->MPDU_Density;
+ pHTInfo->CurrentAMPDUFactor = pHTInfo->AMPDU_Factor;
+
+
+
+ // Initialize all of the parameters related to 11n
+ memset((void *)(&(pHTInfo->SelfHTCap)), 0, sizeof(pHTInfo->SelfHTCap));
+ memset((void *)(&(pHTInfo->SelfHTInfo)), 0, sizeof(pHTInfo->SelfHTInfo));
+ memset((void *)(&(pHTInfo->PeerHTCapBuf)), 0, sizeof(pHTInfo->PeerHTCapBuf));
+ memset((void *)(&(pHTInfo->PeerHTInfoBuf)), 0, sizeof(pHTInfo->PeerHTInfoBuf));
+
+ pHTInfo->bSwBwInProgress = false;
+ pHTInfo->ChnlOp = CHNLOP_NONE;
+
+ // Set default IEEE spec for Draft N
+ pHTInfo->ePeerHTSpecVer = HT_SPEC_VER_IEEE;
+
+ // Realtek proprietary aggregation mode
+ pHTInfo->bCurrentRT2RTAggregation = false;
+ pHTInfo->bCurrentRT2RTLongSlotTime = false;
+ pHTInfo->IOTPeer = 0;
+ pHTInfo->IOTAction = 0;
+
+ //MCS rate initialized here
+ {
+ u8 *RegHTSuppRateSets = &(ieee->RegHTSuppRateSet[0]);
+ RegHTSuppRateSets[0] = 0xFF; //support MCS 0~7
+ RegHTSuppRateSets[1] = 0xFF; //support MCS 8~15
+ RegHTSuppRateSets[4] = 0x01; //support MCS 32
+ }
+}
+/********************************************************************************************************************
+ *function: initialize Bss HT structure(struct PBSS_HT)
+ * input: PBSS_HT pBssHT //to be initialized
+ * output: none
+ * return: none
+ * notice: This function is called when initialize network structure
+********************************************************************************************************************/
+void HTInitializeBssDesc(PBSS_HT pBssHT)
+{
+
+ pBssHT->bdSupportHT = false;
+ memset(pBssHT->bdHTCapBuf, 0, sizeof(pBssHT->bdHTCapBuf));
+ pBssHT->bdHTCapLen = 0;
+ memset(pBssHT->bdHTInfoBuf, 0, sizeof(pBssHT->bdHTInfoBuf));
+ pBssHT->bdHTInfoLen = 0;
+
+ pBssHT->bdHTSpecVer= HT_SPEC_VER_IEEE;
+
+ pBssHT->bdRT2RTAggregation = false;
+ pBssHT->bdRT2RTLongSlotTime = false;
+}
+/********************************************************************************************************************
+ *function: initialize Bss HT structure(struct PBSS_HT)
+ * input: struct ieee80211_device *ieee
+ * struct ieee80211_network *pNetwork //usually current network we are live in
+ * output: none
+ * return: none
+ * notice: This function should ONLY be called before association
+********************************************************************************************************************/
+void HTResetSelfAndSavePeerSetting(struct ieee80211_device *ieee, struct ieee80211_network *pNetwork)
+{
+ PRT_HIGH_THROUGHPUT pHTInfo = ieee->pHTInfo;
+// u16 nMaxAMSDUSize;
+// PHT_CAPABILITY_ELE pPeerHTCap = (PHT_CAPABILITY_ELE)pNetwork->bssht.bdHTCapBuf;
+// PHT_INFORMATION_ELE pPeerHTInfo = (PHT_INFORMATION_ELE)pNetwork->bssht.bdHTInfoBuf;
+// u8* pMcsFilter;
+ u8 bIOTAction = 0;
+
+ //
+ // Save Peer Setting before Association
+ //
+ IEEE80211_DEBUG(IEEE80211_DL_HT, "==============>%s()\n", __func__);
+ /*unmark bEnableHT flag here is the same reason why unmarked in function ieee80211_softmac_new_net. WB 2008.09.10*/
+// if( pHTInfo->bEnableHT && pNetwork->bssht.bdSupportHT)
+ if (pNetwork->bssht.bdSupportHT)
+ {
+ pHTInfo->bCurrentHTSupport = true;
+ pHTInfo->ePeerHTSpecVer = pNetwork->bssht.bdHTSpecVer;
+
+ // Save HTCap and HTInfo information Element
+ if(pNetwork->bssht.bdHTCapLen > 0 && pNetwork->bssht.bdHTCapLen <= sizeof(pHTInfo->PeerHTCapBuf))
+ memcpy(pHTInfo->PeerHTCapBuf, pNetwork->bssht.bdHTCapBuf, pNetwork->bssht.bdHTCapLen);
+
+ if(pNetwork->bssht.bdHTInfoLen > 0 && pNetwork->bssht.bdHTInfoLen <= sizeof(pHTInfo->PeerHTInfoBuf))
+ memcpy(pHTInfo->PeerHTInfoBuf, pNetwork->bssht.bdHTInfoBuf, pNetwork->bssht.bdHTInfoLen);
+
+ // Check whether RT to RT aggregation mode is enabled
+ if(pHTInfo->bRegRT2RTAggregation)
+ {
+ pHTInfo->bCurrentRT2RTAggregation = pNetwork->bssht.bdRT2RTAggregation;
+ pHTInfo->bCurrentRT2RTLongSlotTime = pNetwork->bssht.bdRT2RTLongSlotTime;
+ }
+ else
+ {
+ pHTInfo->bCurrentRT2RTAggregation = false;
+ pHTInfo->bCurrentRT2RTLongSlotTime = false;
+ }
+
+ // Determine the IOT Peer Vendor.
+ HTIOTPeerDetermine(ieee);
+
+ // Decide IOT Action
+ // Must be called after the parameter of pHTInfo->bCurrentRT2RTAggregation is decided
+ pHTInfo->IOTAction = 0;
+ bIOTAction = HTIOTActIsDisableMCS14(ieee, pNetwork->bssid);
+ if(bIOTAction)
+ pHTInfo->IOTAction |= HT_IOT_ACT_DISABLE_MCS14;
+
+ bIOTAction = HTIOTActIsDisableMCS15(ieee);
+ if(bIOTAction)
+ pHTInfo->IOTAction |= HT_IOT_ACT_DISABLE_MCS15;
+
+ bIOTAction = HTIOTActIsDisableMCSTwoSpatialStream(ieee, pNetwork->bssid);
+ if(bIOTAction)
+ pHTInfo->IOTAction |= HT_IOT_ACT_DISABLE_ALL_2SS;
+
+
+ bIOTAction = HTIOTActIsDisableEDCATurbo(ieee, pNetwork->bssid);
+ if(bIOTAction)
+ pHTInfo->IOTAction |= HT_IOT_ACT_DISABLE_EDCA_TURBO;
+
+ bIOTAction = HTIOTActIsMgntUseCCK6M(pNetwork);
+ if(bIOTAction)
+ pHTInfo->IOTAction |= HT_IOT_ACT_MGNT_USE_CCK_6M;
+
+ bIOTAction = HTIOTActIsCCDFsync(pNetwork->bssid);
+ if(bIOTAction)
+ pHTInfo->IOTAction |= HT_IOT_ACT_CDD_FSYNC;
+
+
+ }
+ else
+ {
+ pHTInfo->bCurrentHTSupport = false;
+ pHTInfo->bCurrentRT2RTAggregation = false;
+ pHTInfo->bCurrentRT2RTLongSlotTime = false;
+
+ pHTInfo->IOTAction = 0;
+ }
+
+}
+
+void HTUpdateSelfAndPeerSetting(struct ieee80211_device *ieee, struct ieee80211_network *pNetwork)
+{
+ PRT_HIGH_THROUGHPUT pHTInfo = ieee->pHTInfo;
+// PHT_CAPABILITY_ELE pPeerHTCap = (PHT_CAPABILITY_ELE)pNetwork->bssht.bdHTCapBuf;
+ PHT_INFORMATION_ELE pPeerHTInfo = (PHT_INFORMATION_ELE)pNetwork->bssht.bdHTInfoBuf;
+
+ if (pHTInfo->bCurrentHTSupport)
+ {
+ //
+ // Config current operation mode.
+ //
+ if(pNetwork->bssht.bdHTInfoLen != 0)
+ pHTInfo->CurrentOpMode = pPeerHTInfo->OptMode;
+
+ //
+ // <TODO: Config according to OBSS non-HT STA present!!>
+ //
+ }
+}
+EXPORT_SYMBOL(HTUpdateSelfAndPeerSetting);
+
+/********************************************************************************************************************
+ *function: check whether HT control field exists
+ * input: struct ieee80211_device *ieee
+ * u8* pFrame //coming skb->data
+ * output: none
+ * return: return true if HT control field exists(false otherwise)
+ * notice:
+********************************************************************************************************************/
+u8 HTCCheck(struct ieee80211_device *ieee, u8 *pFrame)
+{
+ if (ieee->pHTInfo->bCurrentHTSupport)
+ {
+ if ((IsQoSDataFrame(pFrame) && Frame_Order(pFrame)) == 1) {
+ IEEE80211_DEBUG(IEEE80211_DL_HT, "HT CONTROL FILED EXIST!!\n");
+ return true;
+ }
+ }
+ return false;
+}
+
+//
+// This function set bandwidth mode in protocol layer.
+//
+void HTSetConnectBwMode(struct ieee80211_device *ieee, HT_CHANNEL_WIDTH Bandwidth, HT_EXTCHNL_OFFSET Offset)
+{
+ PRT_HIGH_THROUGHPUT pHTInfo = ieee->pHTInfo;
+// u32 flags = 0;
+
+ if(pHTInfo->bRegBW40MHz == false)
+ return;
+
+
+
+ // To reduce dummy operation
+// if((pHTInfo->bCurBW40MHz==false && Bandwidth==HT_CHANNEL_WIDTH_20) ||
+// (pHTInfo->bCurBW40MHz==true && Bandwidth==HT_CHANNEL_WIDTH_20_40 && Offset==pHTInfo->CurSTAExtChnlOffset))
+// return;
+
+// spin_lock_irqsave(&(ieee->bw_spinlock), flags);
+ if (pHTInfo->bSwBwInProgress) {
+// spin_unlock_irqrestore(&(ieee->bw_spinlock), flags);
+ return;
+ }
+ //if in half N mode, set to 20M bandwidth please 09.08.2008 WB.
+ if(Bandwidth==HT_CHANNEL_WIDTH_20_40 && (!ieee->GetHalfNmodeSupportByAPsHandler(ieee->dev)))
+ {
+ // Handle Illegal extension channel offset!!
+ if(ieee->current_network.channel<2 && Offset==HT_EXTCHNL_OFFSET_LOWER)
+ Offset = HT_EXTCHNL_OFFSET_NO_EXT;
+ if(Offset==HT_EXTCHNL_OFFSET_UPPER || Offset==HT_EXTCHNL_OFFSET_LOWER) {
+ pHTInfo->bCurBW40MHz = true;
+ pHTInfo->CurSTAExtChnlOffset = Offset;
+ } else {
+ pHTInfo->bCurBW40MHz = false;
+ pHTInfo->CurSTAExtChnlOffset = HT_EXTCHNL_OFFSET_NO_EXT;
+ }
+ } else {
+ pHTInfo->bCurBW40MHz = false;
+ pHTInfo->CurSTAExtChnlOffset = HT_EXTCHNL_OFFSET_NO_EXT;
+ }
+
+ pHTInfo->bSwBwInProgress = true;
+
+ // TODO: 2007.7.13 by Emily Wait 2000ms in order to guarantee that switching
+ // bandwidth is executed after scan is finished. It is a temporal solution
+ // because software should ganrantee the last operation of switching bandwidth
+ // is executed properlly.
+ HTSetConnectBwModeCallback(ieee);
+
+// spin_unlock_irqrestore(&(ieee->bw_spinlock), flags);
+}
+
+void HTSetConnectBwModeCallback(struct ieee80211_device *ieee)
+{
+ PRT_HIGH_THROUGHPUT pHTInfo = ieee->pHTInfo;
+
+ IEEE80211_DEBUG(IEEE80211_DL_HT, "======>%s()\n", __func__);
+
+ if(pHTInfo->bCurBW40MHz)
+ {
+ if(pHTInfo->CurSTAExtChnlOffset==HT_EXTCHNL_OFFSET_UPPER)
+ ieee->set_chan(ieee->dev, ieee->current_network.channel+2);
+ else if(pHTInfo->CurSTAExtChnlOffset==HT_EXTCHNL_OFFSET_LOWER)
+ ieee->set_chan(ieee->dev, ieee->current_network.channel-2);
+ else
+ ieee->set_chan(ieee->dev, ieee->current_network.channel);
+
+ ieee->SetBWModeHandler(ieee->dev, HT_CHANNEL_WIDTH_20_40, pHTInfo->CurSTAExtChnlOffset);
+ } else {
+ ieee->set_chan(ieee->dev, ieee->current_network.channel);
+ ieee->SetBWModeHandler(ieee->dev, HT_CHANNEL_WIDTH_20, HT_EXTCHNL_OFFSET_NO_EXT);
+ }
+
+ pHTInfo->bSwBwInProgress = false;
+}
diff --git a/drivers/staging/rtl8192u/ieee80211/rtl819x_Qos.h b/drivers/staging/rtl8192u/ieee80211/rtl819x_Qos.h
new file mode 100644
index 000000000..9fbce912a
--- /dev/null
+++ b/drivers/staging/rtl8192u/ieee80211/rtl819x_Qos.h
@@ -0,0 +1,565 @@
+#ifndef __INC_QOS_TYPE_H
+#define __INC_QOS_TYPE_H
+
+#define BIT0 0x00000001
+#define BIT1 0x00000002
+#define BIT2 0x00000004
+#define BIT3 0x00000008
+#define BIT4 0x00000010
+#define BIT5 0x00000020
+#define BIT6 0x00000040
+#define BIT7 0x00000080
+#define BIT8 0x00000100
+#define BIT9 0x00000200
+#define BIT10 0x00000400
+#define BIT11 0x00000800
+#define BIT12 0x00001000
+#define BIT13 0x00002000
+#define BIT14 0x00004000
+#define BIT15 0x00008000
+#define BIT16 0x00010000
+#define BIT17 0x00020000
+#define BIT18 0x00040000
+#define BIT19 0x00080000
+#define BIT20 0x00100000
+#define BIT21 0x00200000
+#define BIT22 0x00400000
+#define BIT23 0x00800000
+#define BIT24 0x01000000
+#define BIT25 0x02000000
+#define BIT26 0x04000000
+#define BIT27 0x08000000
+#define BIT28 0x10000000
+#define BIT29 0x20000000
+#define BIT30 0x40000000
+#define BIT31 0x80000000
+
+#define MAX_WMMELE_LENGTH 64
+
+//
+// QoS mode.
+// enum 0, 1, 2, 4: since we can use the OR(|) operation.
+//
+// QOS_MODE is redefined for enum can't be ++, | under C++ compiler, 2006.05.17, by rcnjko.
+//typedef enum _QOS_MODE{
+// QOS_DISABLE = 0,
+// QOS_WMM = 1,
+// QOS_EDCA = 2,
+// QOS_HCCA = 4,
+//}QOS_MODE,*PQOS_MODE;
+//
+typedef u32 QOS_MODE, *PQOS_MODE;
+#define QOS_DISABLE 0
+#define QOS_WMM 1
+#define QOS_WMMSA 2
+#define QOS_EDCA 4
+#define QOS_HCCA 8
+#define QOS_WMM_UAPSD 16 //WMM Power Save, 2006-06-14 Isaiah
+
+#define AC_PARAM_SIZE 4
+#define WMM_PARAM_ELE_BODY_LEN 18
+
+//
+// QoS ACK Policy Field Values
+// Ref: WMM spec 2.1.6: QoS Control Field, p.10.
+//
+typedef enum _ACK_POLICY{
+ eAckPlc0_ACK = 0x00,
+ eAckPlc1_NoACK = 0x01,
+} ACK_POLICY, *PACK_POLICY;
+
+#define WMM_PARAM_ELEMENT_SIZE (8+(4*AC_PARAM_SIZE))
+
+//
+// QoS Control Field
+// Ref:
+// 1. WMM spec 2.1.6: QoS Control Field, p.9.
+// 2. 802.11e/D13.0 7.1.3.5, p.26.
+//
+typedef union _QOS_CTRL_FIELD{
+ u8 charData[2];
+ u16 shortData;
+
+ // WMM spec
+ struct {
+ u8 UP:3;
+ u8 usRsvd1:1;
+ u8 EOSP:1;
+ u8 AckPolicy:2;
+ u8 usRsvd2:1;
+ u8 ucRsvdByte;
+ }WMM;
+
+ // 802.11e: QoS data type frame sent by non-AP QSTAs.
+ struct {
+ u8 TID:4;
+ u8 bIsQsize:1;// 0: BIT[8:15] is TXOP Duration Requested, 1: BIT[8:15] is Queue Size.
+ u8 AckPolicy:2;
+ u8 usRsvd:1;
+ u8 TxopOrQsize; // (BIT4=0)TXOP Duration Requested or (BIT4=1)Queue Size.
+ }BySta;
+
+ // 802.11e: QoS data, QoS Null, and QoS Data+CF-Ack frames sent by HC.
+ struct {
+ u8 TID:4;
+ u8 EOSP:1;
+ u8 AckPolicy:2;
+ u8 usRsvd:1;
+ u8 PSBufState; // QAP PS Buffer State.
+ }ByHc_Data;
+
+ // 802.11e: QoS (+) CF-Poll frames sent by HC.
+ struct {
+ u8 TID:4;
+ u8 EOSP:1;
+ u8 AckPolicy:2;
+ u8 usRsvd:1;
+ u8 TxopLimit; // TXOP Limit.
+ }ByHc_CFP;
+
+}QOS_CTRL_FIELD, *PQOS_CTRL_FIELD;
+
+
+//
+// QoS Info Field
+// Ref:
+// 1. WMM spec 2.2.1: WME Information Element, p.11.
+// 2. 8185 QoS code: QOS_INFO [def. in QoS_mp.h]
+//
+typedef union _QOS_INFO_FIELD{
+ u8 charData;
+
+ struct {
+ u8 ucParameterSetCount:4;
+ u8 ucReserved:4;
+ }WMM;
+
+ struct {
+ //Ref WMM_Specification_1-1.pdf, 2006-06-13 Isaiah
+ u8 ucAC_VO_UAPSD:1;
+ u8 ucAC_VI_UAPSD:1;
+ u8 ucAC_BE_UAPSD:1;
+ u8 ucAC_BK_UAPSD:1;
+ u8 ucReserved1:1;
+ u8 ucMaxSPLen:2;
+ u8 ucReserved2:1;
+
+ }ByWmmPsSta;
+
+ struct {
+ //Ref WMM_Specification_1-1.pdf, 2006-06-13 Isaiah
+ u8 ucParameterSetCount:4;
+ u8 ucReserved:3;
+ u8 ucApUapsd:1;
+ }ByWmmPsAp;
+
+ struct {
+ u8 ucAC3_UAPSD:1;
+ u8 ucAC2_UAPSD:1;
+ u8 ucAC1_UAPSD:1;
+ u8 ucAC0_UAPSD:1;
+ u8 ucQAck:1;
+ u8 ucMaxSPLen:2;
+ u8 ucMoreDataAck:1;
+ } By11eSta;
+
+ struct {
+ u8 ucParameterSetCount:4;
+ u8 ucQAck:1;
+ u8 ucQueueReq:1;
+ u8 ucTXOPReq:1;
+ u8 ucReserved:1;
+ } By11eAp;
+
+ struct {
+ u8 ucReserved1:4;
+ u8 ucQAck:1;
+ u8 ucReserved2:2;
+ u8 ucMoreDataAck:1;
+ } ByWmmsaSta;
+
+ struct {
+ u8 ucReserved1:4;
+ u8 ucQAck:1;
+ u8 ucQueueReq:1;
+ u8 ucTXOPReq:1;
+ u8 ucReserved2:1;
+ } ByWmmsaAp;
+
+ struct {
+ u8 ucAC3_UAPSD:1;
+ u8 ucAC2_UAPSD:1;
+ u8 ucAC1_UAPSD:1;
+ u8 ucAC0_UAPSD:1;
+ u8 ucQAck:1;
+ u8 ucMaxSPLen:2;
+ u8 ucMoreDataAck:1;
+ } ByAllSta;
+
+ struct {
+ u8 ucParameterSetCount:4;
+ u8 ucQAck:1;
+ u8 ucQueueReq:1;
+ u8 ucTXOPReq:1;
+ u8 ucApUapsd:1;
+ } ByAllAp;
+
+}QOS_INFO_FIELD, *PQOS_INFO_FIELD;
+
+//
+// ACI to AC coding.
+// Ref: WMM spec 2.2.2: WME Parameter Element, p.13.
+//
+// AC_CODING is redefined for enum can't be ++, | under C++ compiler, 2006.05.17, by rcnjko.
+//typedef enum _AC_CODING{
+// AC0_BE = 0, // ACI: 0x00 // Best Effort
+// AC1_BK = 1, // ACI: 0x01 // Background
+// AC2_VI = 2, // ACI: 0x10 // Video
+// AC3_VO = 3, // ACI: 0x11 // Voice
+// AC_MAX = 4, // Max: define total number; Should not to be used as a real enum.
+//}AC_CODING,*PAC_CODING;
+//
+typedef u32 AC_CODING;
+#define AC0_BE 0 // ACI: 0x00 // Best Effort
+#define AC1_BK 1 // ACI: 0x01 // Background
+#define AC2_VI 2 // ACI: 0x10 // Video
+#define AC3_VO 3 // ACI: 0x11 // Voice
+#define AC_MAX 4 // Max: define total number; Should not to be used as a real enum.
+
+//
+// ACI/AIFSN Field.
+// Ref: WMM spec 2.2.2: WME Parameter Element, p.12.
+//
+typedef union _ACI_AIFSN{
+ u8 charData;
+
+ struct {
+ u8 AIFSN:4;
+ u8 ACM:1;
+ u8 ACI:2;
+ u8 Reserved:1;
+ }f; // Field
+}ACI_AIFSN, *PACI_AIFSN;
+
+//
+// ECWmin/ECWmax field.
+// Ref: WMM spec 2.2.2: WME Parameter Element, p.13.
+//
+typedef union _ECW{
+ u8 charData;
+ struct {
+ u8 ECWmin:4;
+ u8 ECWmax:4;
+ }f; // Field
+}ECW, *PECW;
+
+//
+// AC Parameters Record Format.
+// Ref: WMM spec 2.2.2: WME Parameter Element, p.12.
+//
+typedef union _AC_PARAM{
+ u32 longData;
+ u8 charData[4];
+
+ struct {
+ ACI_AIFSN AciAifsn;
+ ECW Ecw;
+ u16 TXOPLimit;
+ }f; // Field
+}AC_PARAM, *PAC_PARAM;
+
+
+
+//
+// QoS element subtype
+//
+typedef enum _QOS_ELE_SUBTYPE{
+ QOSELE_TYPE_INFO = 0x00, // 0x00: Information element
+ QOSELE_TYPE_PARAM = 0x01, // 0x01: parameter element
+} QOS_ELE_SUBTYPE, *PQOS_ELE_SUBTYPE;
+
+
+//
+// Direction Field Values.
+// Ref: WMM spec 2.2.11: WME TSPEC Element, p.18.
+//
+typedef enum _DIRECTION_VALUE{
+ DIR_UP = 0, // 0x00 // UpLink
+ DIR_DOWN = 1, // 0x01 // DownLink
+ DIR_DIRECT = 2, // 0x10 // DirectLink
+ DIR_BI_DIR = 3, // 0x11 // Bi-Direction
+} DIRECTION_VALUE, *PDIRECTION_VALUE;
+
+
+//
+// TS Info field in WMM TSPEC Element.
+// Ref:
+// 1. WMM spec 2.2.11: WME TSPEC Element, p.18.
+// 2. 8185 QoS code: QOS_TSINFO [def. in QoS_mp.h]
+//
+typedef union _QOS_TSINFO{
+ u8 charData[3];
+ struct {
+ u8 ucTrafficType:1; //WMM is reserved
+ u8 ucTSID:4;
+ u8 ucDirection:2;
+ u8 ucAccessPolicy:2; //WMM: bit8=0, bit7=1
+ u8 ucAggregation:1; //WMM is reserved
+ u8 ucPSB:1; //WMMSA is APSD
+ u8 ucUP:3;
+ u8 ucTSInfoAckPolicy:2; //WMM is reserved
+ u8 ucSchedule:1; //WMM is reserved
+ u8 ucReserved:7;
+ }field;
+}QOS_TSINFO, *PQOS_TSINFO;
+
+//
+// WMM TSPEC Body.
+// Ref: WMM spec 2.2.11: WME TSPEC Element, p.16.
+//
+typedef union _TSPEC_BODY{
+ u8 charData[55];
+
+ struct {
+ QOS_TSINFO TSInfo; //u8 TSInfo[3];
+ u16 NominalMSDUsize;
+ u16 MaxMSDUsize;
+ u32 MinServiceItv;
+ u32 MaxServiceItv;
+ u32 InactivityItv;
+ u32 SuspenItv;
+ u32 ServiceStartTime;
+ u32 MinDataRate;
+ u32 MeanDataRate;
+ u32 PeakDataRate;
+ u32 MaxBurstSize;
+ u32 DelayBound;
+ u32 MinPhyRate;
+ u16 SurplusBandwidthAllowance;
+ u16 MediumTime;
+ } f; // Field
+}TSPEC_BODY, *PTSPEC_BODY;
+
+
+//
+// WMM TSPEC Element.
+// Ref: WMM spec 2.2.11: WME TSPEC Element, p.16.
+//
+typedef struct _WMM_TSPEC{
+ u8 ID;
+ u8 Length;
+ u8 OUI[3];
+ u8 OUI_Type;
+ u8 OUI_SubType;
+ u8 Version;
+ TSPEC_BODY Body;
+} WMM_TSPEC, *PWMM_TSPEC;
+
+//
+// ACM implementation method.
+// Annie, 2005-12-13.
+//
+typedef enum _ACM_METHOD{
+ eAcmWay0_SwAndHw = 0, // By SW and HW.
+ eAcmWay1_HW = 1, // By HW.
+ eAcmWay2_SW = 2, // By SW.
+} ACM_METHOD, *PACM_METHOD;
+
+
+typedef struct _ACM{
+// u8 RegEnableACM;
+ u64 UsedTime;
+ u64 MediumTime;
+ u8 HwAcmCtl; // TRUE: UsedTime exceed => Do NOT USE this AC. It wll be written to ACM_CONTROL(0xBF BIT 0/1/2 in 8185B).
+}ACM, *PACM;
+
+typedef u8 AC_UAPSD, *PAC_UAPSD;
+
+#define GET_VO_UAPSD(_apsd) ((_apsd) & BIT0)
+#define SET_VO_UAPSD(_apsd) ((_apsd) |= BIT0)
+
+#define GET_VI_UAPSD(_apsd) ((_apsd) & BIT1)
+#define SET_VI_UAPSD(_apsd) ((_apsd) |= BIT1)
+
+#define GET_BK_UAPSD(_apsd) ((_apsd) & BIT2)
+#define SET_BK_UAPSD(_apsd) ((_apsd) |= BIT2)
+
+#define GET_BE_UAPSD(_apsd) ((_apsd) & BIT3)
+#define SET_BE_UAPSD(_apsd) ((_apsd) |= BIT3)
+
+
+//typedef struct _TCLASS{
+// TODO
+//} TCLASS, *PTCLASS;
+typedef union _QOS_TCLAS{
+
+ struct _TYPE_GENERAL{
+ u8 Priority;
+ u8 ClassifierType;
+ u8 Mask;
+ } TYPE_GENERAL;
+
+ struct _TYPE0_ETH{
+ u8 Priority;
+ u8 ClassifierType;
+ u8 Mask;
+ u8 SrcAddr[6];
+ u8 DstAddr[6];
+ u16 Type;
+ } TYPE0_ETH;
+
+ struct _TYPE1_IPV4{
+ u8 Priority;
+ u8 ClassifierType;
+ u8 Mask;
+ u8 Version;
+ u8 SrcIP[4];
+ u8 DstIP[4];
+ u16 SrcPort;
+ u16 DstPort;
+ u8 DSCP;
+ u8 Protocol;
+ u8 Reserved;
+ } TYPE1_IPV4;
+
+ struct _TYPE1_IPV6{
+ u8 Priority;
+ u8 ClassifierType;
+ u8 Mask;
+ u8 Version;
+ u8 SrcIP[16];
+ u8 DstIP[16];
+ u16 SrcPort;
+ u16 DstPort;
+ u8 FlowLabel[3];
+ } TYPE1_IPV6;
+
+ struct _TYPE2_8021Q{
+ u8 Priority;
+ u8 ClassifierType;
+ u8 Mask;
+ u16 TagType;
+ } TYPE2_8021Q;
+} QOS_TCLAS, *PQOS_TCLAS;
+
+//typedef struct _WMM_TSTREAM{
+//
+//- TSPEC
+//- AC (which to mapping)
+//} WMM_TSTREAM, *PWMM_TSTREAM;
+typedef struct _QOS_TSTREAM{
+ u8 AC;
+ WMM_TSPEC TSpec;
+ QOS_TCLAS TClass;
+} QOS_TSTREAM, *PQOS_TSTREAM;
+
+//typedef struct _U_APSD{
+//- TriggerEnable [4]
+//- MaxSPLength
+//- HighestAcBuffered
+//} U_APSD, *PU_APSD;
+
+//joseph TODO:
+// UAPSD function should be implemented by 2 data structure
+// "Qos control field" and "Qos info field"
+//typedef struct _QOS_UAPSD{
+// u8 bTriggerEnable[4];
+// u8 MaxSPLength;
+// u8 HighestBufAC;
+//} QOS_UAPSD, *PQOS_APSD;
+
+//----------------------------------------------------------------------------
+// 802.11 Management frame Status Code field
+//----------------------------------------------------------------------------
+typedef struct _OCTET_STRING{
+ u8 *Octet;
+ u16 Length;
+}OCTET_STRING, *POCTET_STRING;
+
+//
+// STA QoS data.
+// Ref: DOT11_QOS in 8185 code. [def. in QoS_mp.h]
+//
+typedef struct _STA_QOS{
+ //DECLARE_RT_OBJECT(STA_QOS);
+ u8 WMMIEBuf[MAX_WMMELE_LENGTH];
+ u8 *WMMIE;
+
+ // Part 1. Self QoS Mode.
+ QOS_MODE QosCapability; //QoS Capability, 2006-06-14 Isaiah
+ QOS_MODE CurrentQosMode;
+
+ // For WMM Power Save Mode :
+ // ACs are trigger/delivery enabled or legacy power save enabled. 2006-06-13 Isaiah
+ AC_UAPSD b4ac_Uapsd; //VoUapsd(bit0), ViUapsd(bit1), BkUapsd(bit2), BeUapsd(bit3),
+ AC_UAPSD Curr4acUapsd;
+ u8 bInServicePeriod;
+ u8 MaxSPLength;
+ int NumBcnBeforeTrigger;
+
+ // Part 2. EDCA Parameter (perAC)
+ u8 *pWMMInfoEle;
+ u8 WMMParamEle[WMM_PARAM_ELEMENT_SIZE];
+ u8 WMMPELength;
+
+ // <Bruce_Note>
+ //2 ToDo: remove the Qos Info Field and replace it by the above WMM Info element.
+ // By Bruce, 2008-01-30.
+ // Part 2. EDCA Parameter (perAC)
+ QOS_INFO_FIELD QosInfoField_STA; // Maintained by STA
+ QOS_INFO_FIELD QosInfoField_AP; // Retrieved from AP
+
+ AC_PARAM CurAcParameters[4];
+
+ // Part 3. ACM
+ ACM acm[4];
+ ACM_METHOD AcmMethod;
+
+ // Part 4. Per TID (Part 5: TCLASS will be described by TStream)
+ QOS_TSTREAM TStream[16];
+ WMM_TSPEC TSpec;
+
+ u32 QBssWirelessMode;
+
+ // No Ack Setting
+ u8 bNoAck;
+
+ // Enable/Disable Rx immediate BA capability.
+ u8 bEnableRxImmBA;
+
+}STA_QOS, *PSTA_QOS;
+
+//
+// BSS QOS data.
+// Ref: BssDscr in 8185 code. [def. in BssDscr.h]
+//
+typedef struct _BSS_QOS{
+ QOS_MODE bdQoSMode;
+
+ u8 bdWMMIEBuf[MAX_WMMELE_LENGTH];
+ u8 *bdWMMIE;
+
+ QOS_ELE_SUBTYPE EleSubType;
+
+ u8 *pWMMInfoEle;
+ u8 *pWMMParamEle;
+
+ QOS_INFO_FIELD QosInfoField;
+ AC_PARAM AcParameter[4];
+}BSS_QOS, *PBSS_QOS;
+
+
+//
+// Ref: sQoSCtlLng and QoSCtl definition in 8185 QoS code.
+//#define QoSCtl (( (Adapter->bRegQoS) && (Adapter->dot11QoS.QoSMode &(QOS_EDCA|QOS_HCCA)) ) ?sQoSCtlLng:0)
+//
+#define sQoSCtlLng 2
+#define QOS_CTRL_LEN(_QosMode) ((_QosMode > QOS_DISABLE)? sQoSCtlLng : 0)
+
+
+//Added by joseph
+//UP Mapping to AC, using in MgntQuery_SequenceNumber() and maybe for DSCP
+//#define UP2AC(up) ((up<3)?((up==0)?1:0):(up>>1))
+#define IsACValid(ac) ((ac<=7 )?true:false )
+
+#endif // #ifndef __INC_QOS_TYPE_H
diff --git a/drivers/staging/rtl8192u/ieee80211/rtl819x_TS.h b/drivers/staging/rtl8192u/ieee80211/rtl819x_TS.h
new file mode 100644
index 000000000..873969c9f
--- /dev/null
+++ b/drivers/staging/rtl8192u/ieee80211/rtl819x_TS.h
@@ -0,0 +1,55 @@
+#ifndef _TSTYPE_H_
+#define _TSTYPE_H_
+#include "rtl819x_Qos.h"
+#define TS_SETUP_TIMEOUT 60 /* In millisecond */
+#define TS_INACT_TIMEOUT 60
+#define TS_ADDBA_DELAY 60
+
+#define TOTAL_TS_NUM 16
+#define TCLAS_NUM 4
+
+/* This define the Tx/Rx directions */
+typedef enum _TR_SELECT {
+ TX_DIR = 0,
+ RX_DIR = 1,
+} TR_SELECT, *PTR_SELECT;
+
+typedef struct _TS_COMMON_INFO{
+ struct list_head List;
+ struct timer_list SetupTimer;
+ struct timer_list InactTimer;
+ u8 Addr[6];
+ TSPEC_BODY TSpec;
+ QOS_TCLAS TClass[TCLAS_NUM];
+ u8 TClasProc;
+ u8 TClasNum;
+} TS_COMMON_INFO, *PTS_COMMON_INFO;
+
+typedef struct _TX_TS_RECORD{
+ TS_COMMON_INFO TsCommonInfo;
+ u16 TxCurSeq;
+ BA_RECORD TxPendingBARecord; /* For BA Originator */
+ BA_RECORD TxAdmittedBARecord; /* For BA Originator */
+/* QOS_DL_RECORD DLRecord; */
+ u8 bAddBaReqInProgress;
+ u8 bAddBaReqDelayed;
+ u8 bUsingBa;
+ struct timer_list TsAddBaTimer;
+ u8 num;
+} TX_TS_RECORD, *PTX_TS_RECORD;
+
+typedef struct _RX_TS_RECORD {
+ TS_COMMON_INFO TsCommonInfo;
+ u16 RxIndicateSeq;
+ u16 RxTimeoutIndicateSeq;
+ struct list_head RxPendingPktList;
+ struct timer_list RxPktPendingTimer;
+ BA_RECORD RxAdmittedBARecord; /* For BA Recipient */
+ u16 RxLastSeqNum;
+ u8 RxLastFragNum;
+ u8 num;
+/* QOS_DL_RECORD DLRecord; */
+} RX_TS_RECORD, *PRX_TS_RECORD;
+
+
+#endif
diff --git a/drivers/staging/rtl8192u/ieee80211/rtl819x_TSProc.c b/drivers/staging/rtl8192u/ieee80211/rtl819x_TSProc.c
new file mode 100644
index 000000000..ea92fdebe
--- /dev/null
+++ b/drivers/staging/rtl8192u/ieee80211/rtl819x_TSProc.c
@@ -0,0 +1,603 @@
+#include "ieee80211.h"
+#include <linux/etherdevice.h>
+#include <linux/slab.h>
+#include "rtl819x_TS.h"
+
+static void TsSetupTimeOut(unsigned long data)
+{
+ // Not implement yet
+ // This is used for WMMSA and ACM , that would send ADDTSReq frame.
+}
+
+static void TsInactTimeout(unsigned long data)
+{
+ // Not implement yet
+ // This is used for WMMSA and ACM.
+ // This function would be call when TS is no Tx/Rx for some period of time.
+}
+
+/********************************************************************************************************************
+ *function: I still not understand this function, so wait for further implementation
+ * input: unsigned long data //acturally we send TX_TS_RECORD or RX_TS_RECORD to these timer
+ * return: NULL
+ * notice:
+********************************************************************************************************************/
+static void RxPktPendingTimeout(unsigned long data)
+{
+ PRX_TS_RECORD pRxTs = (PRX_TS_RECORD)data;
+ struct ieee80211_device *ieee = container_of(pRxTs, struct ieee80211_device, RxTsRecord[pRxTs->num]);
+
+ PRX_REORDER_ENTRY pReorderEntry = NULL;
+
+ //u32 flags = 0;
+ unsigned long flags = 0;
+ struct ieee80211_rxb *stats_IndicateArray[REORDER_WIN_SIZE];
+ u8 index = 0;
+ bool bPktInBuf = false;
+
+
+ spin_lock_irqsave(&(ieee->reorder_spinlock), flags);
+ //PlatformAcquireSpinLock(Adapter, RT_RX_SPINLOCK);
+ IEEE80211_DEBUG(IEEE80211_DL_REORDER,"==================>%s()\n",__func__);
+ if(pRxTs->RxTimeoutIndicateSeq != 0xffff)
+ {
+ // Indicate the pending packets sequentially according to SeqNum until meet the gap.
+ while(!list_empty(&pRxTs->RxPendingPktList))
+ {
+ pReorderEntry = (PRX_REORDER_ENTRY)list_entry(pRxTs->RxPendingPktList.prev,RX_REORDER_ENTRY,List);
+ if(index == 0)
+ pRxTs->RxIndicateSeq = pReorderEntry->SeqNum;
+
+ if( SN_LESS(pReorderEntry->SeqNum, pRxTs->RxIndicateSeq) ||
+ SN_EQUAL(pReorderEntry->SeqNum, pRxTs->RxIndicateSeq) )
+ {
+ list_del_init(&pReorderEntry->List);
+
+ if(SN_EQUAL(pReorderEntry->SeqNum, pRxTs->RxIndicateSeq))
+ pRxTs->RxIndicateSeq = (pRxTs->RxIndicateSeq + 1) % 4096;
+
+ IEEE80211_DEBUG(IEEE80211_DL_REORDER,"RxPktPendingTimeout(): IndicateSeq: %d\n", pReorderEntry->SeqNum);
+ stats_IndicateArray[index] = pReorderEntry->prxb;
+ index++;
+
+ list_add_tail(&pReorderEntry->List, &ieee->RxReorder_Unused_List);
+ }
+ else
+ {
+ bPktInBuf = true;
+ break;
+ }
+ }
+ }
+
+ if(index>0)
+ {
+ // Set RxTimeoutIndicateSeq to 0xffff to indicate no pending packets in buffer now.
+ pRxTs->RxTimeoutIndicateSeq = 0xffff;
+
+ // Indicate packets
+ if(index > REORDER_WIN_SIZE){
+ IEEE80211_DEBUG(IEEE80211_DL_ERR, "RxReorderIndicatePacket(): Rx Reorer buffer full!! \n");
+ spin_unlock_irqrestore(&(ieee->reorder_spinlock), flags);
+ return;
+ }
+ ieee80211_indicate_packets(ieee, stats_IndicateArray, index);
+ }
+
+ if(bPktInBuf && (pRxTs->RxTimeoutIndicateSeq==0xffff))
+ {
+ pRxTs->RxTimeoutIndicateSeq = pRxTs->RxIndicateSeq;
+ mod_timer(&pRxTs->RxPktPendingTimer, jiffies + MSECS(ieee->pHTInfo->RxReorderPendingTime));
+ }
+ spin_unlock_irqrestore(&(ieee->reorder_spinlock), flags);
+ //PlatformReleaseSpinLock(Adapter, RT_RX_SPINLOCK);
+}
+
+/********************************************************************************************************************
+ *function: Add BA timer function
+ * input: unsigned long data //acturally we send TX_TS_RECORD or RX_TS_RECORD to these timer
+ * return: NULL
+ * notice:
+********************************************************************************************************************/
+static void TsAddBaProcess(unsigned long data)
+{
+ PTX_TS_RECORD pTxTs = (PTX_TS_RECORD)data;
+ u8 num = pTxTs->num;
+ struct ieee80211_device *ieee = container_of(pTxTs, struct ieee80211_device, TxTsRecord[num]);
+
+ TsInitAddBA(ieee, pTxTs, BA_POLICY_IMMEDIATE, false);
+ IEEE80211_DEBUG(IEEE80211_DL_BA, "TsAddBaProcess(): ADDBA Req is started!! \n");
+}
+
+
+static void ResetTsCommonInfo(PTS_COMMON_INFO pTsCommonInfo)
+{
+ memset(pTsCommonInfo->Addr, 0, 6);
+ memset(&pTsCommonInfo->TSpec, 0, sizeof(TSPEC_BODY));
+ memset(&pTsCommonInfo->TClass, 0, sizeof(QOS_TCLAS)*TCLAS_NUM);
+ pTsCommonInfo->TClasProc = 0;
+ pTsCommonInfo->TClasNum = 0;
+}
+
+static void ResetTxTsEntry(PTX_TS_RECORD pTS)
+{
+ ResetTsCommonInfo(&pTS->TsCommonInfo);
+ pTS->TxCurSeq = 0;
+ pTS->bAddBaReqInProgress = false;
+ pTS->bAddBaReqDelayed = false;
+ pTS->bUsingBa = false;
+ ResetBaEntry(&pTS->TxAdmittedBARecord); //For BA Originator
+ ResetBaEntry(&pTS->TxPendingBARecord);
+}
+
+static void ResetRxTsEntry(PRX_TS_RECORD pTS)
+{
+ ResetTsCommonInfo(&pTS->TsCommonInfo);
+ pTS->RxIndicateSeq = 0xffff; // This indicate the RxIndicateSeq is not used now!!
+ pTS->RxTimeoutIndicateSeq = 0xffff; // This indicate the RxTimeoutIndicateSeq is not used now!!
+ ResetBaEntry(&pTS->RxAdmittedBARecord); // For BA Recipient
+}
+
+void TSInitialize(struct ieee80211_device *ieee)
+{
+ PTX_TS_RECORD pTxTS = ieee->TxTsRecord;
+ PRX_TS_RECORD pRxTS = ieee->RxTsRecord;
+ PRX_REORDER_ENTRY pRxReorderEntry = ieee->RxReorderEntry;
+ u8 count = 0;
+ IEEE80211_DEBUG(IEEE80211_DL_TS, "==========>%s()\n", __func__);
+ // Initialize Tx TS related info.
+ INIT_LIST_HEAD(&ieee->Tx_TS_Admit_List);
+ INIT_LIST_HEAD(&ieee->Tx_TS_Pending_List);
+ INIT_LIST_HEAD(&ieee->Tx_TS_Unused_List);
+
+ for(count = 0; count < TOTAL_TS_NUM; count++)
+ {
+ //
+ pTxTS->num = count;
+ // The timers for the operation of Traffic Stream and Block Ack.
+ // DLS related timer will be add here in the future!!
+ setup_timer(&pTxTS->TsCommonInfo.SetupTimer, TsSetupTimeOut,
+ (unsigned long)pTxTS);
+ setup_timer(&pTxTS->TsCommonInfo.InactTimer, TsInactTimeout,
+ (unsigned long)pTxTS);
+ setup_timer(&pTxTS->TsAddBaTimer, TsAddBaProcess,
+ (unsigned long)pTxTS);
+ setup_timer(&pTxTS->TxPendingBARecord.Timer, BaSetupTimeOut,
+ (unsigned long)pTxTS);
+ setup_timer(&pTxTS->TxAdmittedBARecord.Timer,
+ TxBaInactTimeout, (unsigned long)pTxTS);
+ ResetTxTsEntry(pTxTS);
+ list_add_tail(&pTxTS->TsCommonInfo.List, &ieee->Tx_TS_Unused_List);
+ pTxTS++;
+ }
+
+ // Initialize Rx TS related info.
+ INIT_LIST_HEAD(&ieee->Rx_TS_Admit_List);
+ INIT_LIST_HEAD(&ieee->Rx_TS_Pending_List);
+ INIT_LIST_HEAD(&ieee->Rx_TS_Unused_List);
+ for(count = 0; count < TOTAL_TS_NUM; count++)
+ {
+ pRxTS->num = count;
+ INIT_LIST_HEAD(&pRxTS->RxPendingPktList);
+ setup_timer(&pRxTS->TsCommonInfo.SetupTimer, TsSetupTimeOut,
+ (unsigned long)pRxTS);
+ setup_timer(&pRxTS->TsCommonInfo.InactTimer, TsInactTimeout,
+ (unsigned long)pRxTS);
+ setup_timer(&pRxTS->RxAdmittedBARecord.Timer,
+ RxBaInactTimeout, (unsigned long)pRxTS);
+ setup_timer(&pRxTS->RxPktPendingTimer, RxPktPendingTimeout,
+ (unsigned long)pRxTS);
+ ResetRxTsEntry(pRxTS);
+ list_add_tail(&pRxTS->TsCommonInfo.List, &ieee->Rx_TS_Unused_List);
+ pRxTS++;
+ }
+ // Initialize unused Rx Reorder List.
+ INIT_LIST_HEAD(&ieee->RxReorder_Unused_List);
+//#ifdef TO_DO_LIST
+ for(count = 0; count < REORDER_ENTRY_NUM; count++)
+ {
+ list_add_tail( &pRxReorderEntry->List,&ieee->RxReorder_Unused_List);
+ if(count == (REORDER_ENTRY_NUM-1))
+ break;
+ pRxReorderEntry = &ieee->RxReorderEntry[count+1];
+ }
+//#endif
+
+}
+
+static void AdmitTS(struct ieee80211_device *ieee,
+ PTS_COMMON_INFO pTsCommonInfo, u32 InactTime)
+{
+ del_timer_sync(&pTsCommonInfo->SetupTimer);
+ del_timer_sync(&pTsCommonInfo->InactTimer);
+
+ if(InactTime!=0)
+ mod_timer(&pTsCommonInfo->InactTimer, jiffies + MSECS(InactTime));
+}
+
+
+static PTS_COMMON_INFO SearchAdmitTRStream(struct ieee80211_device *ieee,
+ u8 *Addr, u8 TID,
+ TR_SELECT TxRxSelect)
+{
+ //DIRECTION_VALUE dir;
+ u8 dir;
+ bool search_dir[4] = {0};
+ struct list_head *psearch_list; //FIXME
+ PTS_COMMON_INFO pRet = NULL;
+ if(ieee->iw_mode == IW_MODE_MASTER) //ap mode
+ {
+ if(TxRxSelect == TX_DIR)
+ {
+ search_dir[DIR_DOWN] = true;
+ search_dir[DIR_BI_DIR]= true;
+ }
+ else
+ {
+ search_dir[DIR_UP] = true;
+ search_dir[DIR_BI_DIR]= true;
+ }
+ }
+ else if(ieee->iw_mode == IW_MODE_ADHOC)
+ {
+ if(TxRxSelect == TX_DIR)
+ search_dir[DIR_UP] = true;
+ else
+ search_dir[DIR_DOWN] = true;
+ }
+ else
+ {
+ if(TxRxSelect == TX_DIR)
+ {
+ search_dir[DIR_UP] = true;
+ search_dir[DIR_BI_DIR]= true;
+ search_dir[DIR_DIRECT]= true;
+ }
+ else
+ {
+ search_dir[DIR_DOWN] = true;
+ search_dir[DIR_BI_DIR]= true;
+ search_dir[DIR_DIRECT]= true;
+ }
+ }
+
+ if(TxRxSelect == TX_DIR)
+ psearch_list = &ieee->Tx_TS_Admit_List;
+ else
+ psearch_list = &ieee->Rx_TS_Admit_List;
+
+ //for(dir = DIR_UP; dir <= DIR_BI_DIR; dir++)
+ for(dir = 0; dir <= DIR_BI_DIR; dir++)
+ {
+ if (!search_dir[dir])
+ continue;
+ list_for_each_entry(pRet, psearch_list, List){
+ // IEEE80211_DEBUG(IEEE80211_DL_TS, "ADD:%pM, TID:%d, dir:%d\n", pRet->Addr, pRet->TSpec.f.TSInfo.field.ucTSID, pRet->TSpec.f.TSInfo.field.ucDirection);
+ if (memcmp(pRet->Addr, Addr, 6) == 0)
+ if (pRet->TSpec.f.TSInfo.field.ucTSID == TID)
+ if(pRet->TSpec.f.TSInfo.field.ucDirection == dir)
+ {
+ // printk("Bingo! got it\n");
+ break;
+ }
+
+ }
+ if(&pRet->List != psearch_list)
+ break;
+ }
+
+ if(&pRet->List != psearch_list){
+ return pRet ;
+ }
+ else
+ return NULL;
+}
+
+static void MakeTSEntry(PTS_COMMON_INFO pTsCommonInfo, u8 *Addr,
+ PTSPEC_BODY pTSPEC, PQOS_TCLAS pTCLAS, u8 TCLAS_Num,
+ u8 TCLAS_Proc)
+{
+ u8 count;
+
+ if(pTsCommonInfo == NULL)
+ return;
+
+ memcpy(pTsCommonInfo->Addr, Addr, 6);
+
+ if(pTSPEC != NULL)
+ memcpy((u8 *)(&(pTsCommonInfo->TSpec)), (u8 *)pTSPEC, sizeof(TSPEC_BODY));
+
+ for(count = 0; count < TCLAS_Num; count++)
+ memcpy((u8 *)(&(pTsCommonInfo->TClass[count])), (u8 *)pTCLAS, sizeof(QOS_TCLAS));
+
+ pTsCommonInfo->TClasProc = TCLAS_Proc;
+ pTsCommonInfo->TClasNum = TCLAS_Num;
+}
+
+
+bool GetTs(
+ struct ieee80211_device *ieee,
+ PTS_COMMON_INFO *ppTS,
+ u8 *Addr,
+ u8 TID,
+ TR_SELECT TxRxSelect, //Rx:1, Tx:0
+ bool bAddNewTs
+ )
+{
+ u8 UP = 0;
+ //
+ // We do not build any TS for Broadcast or Multicast stream.
+ // So reject these kinds of search here.
+ //
+ if (is_multicast_ether_addr(Addr))
+ {
+ IEEE80211_DEBUG(IEEE80211_DL_ERR, "get TS for Broadcast or Multicast\n");
+ return false;
+ }
+
+ if (ieee->current_network.qos_data.supported == 0)
+ UP = 0;
+ else
+ {
+ // In WMM case: we use 4 TID only
+ if (!IsACValid(TID))
+ {
+ IEEE80211_DEBUG(IEEE80211_DL_ERR, " in %s(), TID(%d) is not valid\n", __func__, TID);
+ return false;
+ }
+
+ switch (TID)
+ {
+ case 0:
+ case 3:
+ UP = 0;
+ break;
+
+ case 1:
+ case 2:
+ UP = 2;
+ break;
+
+ case 4:
+ case 5:
+ UP = 5;
+ break;
+
+ case 6:
+ case 7:
+ UP = 7;
+ break;
+ }
+ }
+
+ *ppTS = SearchAdmitTRStream(
+ ieee,
+ Addr,
+ UP,
+ TxRxSelect);
+ if(*ppTS != NULL)
+ {
+ return true;
+ }
+ else
+ {
+ if (!bAddNewTs) {
+ IEEE80211_DEBUG(IEEE80211_DL_TS, "add new TS failed(tid:%d)\n", UP);
+ return false;
+ }
+ else
+ {
+ //
+ // Create a new Traffic stream for current Tx/Rx
+ // This is for EDCA and WMM to add a new TS.
+ // For HCCA or WMMSA, TS cannot be addmit without negotiation.
+ //
+ TSPEC_BODY TSpec;
+ PQOS_TSINFO pTSInfo = &TSpec.f.TSInfo;
+ struct list_head *pUnusedList =
+ (TxRxSelect == TX_DIR)?
+ (&ieee->Tx_TS_Unused_List):
+ (&ieee->Rx_TS_Unused_List);
+
+ struct list_head *pAddmitList =
+ (TxRxSelect == TX_DIR)?
+ (&ieee->Tx_TS_Admit_List):
+ (&ieee->Rx_TS_Admit_List);
+
+ DIRECTION_VALUE Dir = (ieee->iw_mode == IW_MODE_MASTER)?
+ ((TxRxSelect==TX_DIR)?DIR_DOWN:DIR_UP):
+ ((TxRxSelect==TX_DIR)?DIR_UP:DIR_DOWN);
+ IEEE80211_DEBUG(IEEE80211_DL_TS, "to add Ts\n");
+ if(!list_empty(pUnusedList))
+ {
+ (*ppTS) = list_entry(pUnusedList->next, TS_COMMON_INFO, List);
+ list_del_init(&(*ppTS)->List);
+ if(TxRxSelect==TX_DIR)
+ {
+ PTX_TS_RECORD tmp = container_of(*ppTS, TX_TS_RECORD, TsCommonInfo);
+ ResetTxTsEntry(tmp);
+ }
+ else{
+ PRX_TS_RECORD tmp = container_of(*ppTS, RX_TS_RECORD, TsCommonInfo);
+ ResetRxTsEntry(tmp);
+ }
+
+ IEEE80211_DEBUG(IEEE80211_DL_TS, "to init current TS, UP:%d, Dir:%d, addr:%pM\n", UP, Dir, Addr);
+ // Prepare TS Info releated field
+ pTSInfo->field.ucTrafficType = 0; // Traffic type: WMM is reserved in this field
+ pTSInfo->field.ucTSID = UP; // TSID
+ pTSInfo->field.ucDirection = Dir; // Direction: if there is DirectLink, this need additional consideration.
+ pTSInfo->field.ucAccessPolicy = 1; // Access policy
+ pTSInfo->field.ucAggregation = 0; // Aggregation
+ pTSInfo->field.ucPSB = 0; // Aggregation
+ pTSInfo->field.ucUP = UP; // User priority
+ pTSInfo->field.ucTSInfoAckPolicy = 0; // Ack policy
+ pTSInfo->field.ucSchedule = 0; // Schedule
+
+ MakeTSEntry(*ppTS, Addr, &TSpec, NULL, 0, 0);
+ AdmitTS(ieee, *ppTS, 0);
+ list_add_tail(&((*ppTS)->List), pAddmitList);
+ // if there is DirectLink, we need to do additional operation here!!
+
+ return true;
+ }
+ else
+ {
+ IEEE80211_DEBUG(IEEE80211_DL_ERR, "in function %s() There is not enough TS record to be used!!", __func__);
+ return false;
+ }
+ }
+ }
+}
+
+static void RemoveTsEntry(struct ieee80211_device *ieee, PTS_COMMON_INFO pTs,
+ TR_SELECT TxRxSelect)
+{
+ //u32 flags = 0;
+ unsigned long flags = 0;
+ del_timer_sync(&pTs->SetupTimer);
+ del_timer_sync(&pTs->InactTimer);
+ TsInitDelBA(ieee, pTs, TxRxSelect);
+
+ if(TxRxSelect == RX_DIR)
+ {
+//#ifdef TO_DO_LIST
+ PRX_REORDER_ENTRY pRxReorderEntry;
+ PRX_TS_RECORD pRxTS = (PRX_TS_RECORD)pTs;
+ if(timer_pending(&pRxTS->RxPktPendingTimer))
+ del_timer_sync(&pRxTS->RxPktPendingTimer);
+
+ while(!list_empty(&pRxTS->RxPendingPktList))
+ {
+ // PlatformAcquireSpinLock(Adapter, RT_RX_SPINLOCK);
+ spin_lock_irqsave(&(ieee->reorder_spinlock), flags);
+ //pRxReorderEntry = list_entry(&pRxTS->RxPendingPktList.prev,RX_REORDER_ENTRY,List);
+ pRxReorderEntry = (PRX_REORDER_ENTRY)list_entry(pRxTS->RxPendingPktList.prev,RX_REORDER_ENTRY,List);
+ list_del_init(&pRxReorderEntry->List);
+ {
+ int i = 0;
+ struct ieee80211_rxb *prxb = pRxReorderEntry->prxb;
+ if (unlikely(!prxb))
+ {
+ spin_unlock_irqrestore(&(ieee->reorder_spinlock), flags);
+ return;
+ }
+ for(i =0; i < prxb->nr_subframes; i++) {
+ dev_kfree_skb(prxb->subframes[i]);
+ }
+ kfree(prxb);
+ prxb = NULL;
+ }
+ list_add_tail(&pRxReorderEntry->List,&ieee->RxReorder_Unused_List);
+ //PlatformReleaseSpinLock(Adapter, RT_RX_SPINLOCK);
+ spin_unlock_irqrestore(&(ieee->reorder_spinlock), flags);
+ }
+
+//#endif
+ }
+ else
+ {
+ PTX_TS_RECORD pTxTS = (PTX_TS_RECORD)pTs;
+ del_timer_sync(&pTxTS->TsAddBaTimer);
+ }
+}
+
+void RemovePeerTS(struct ieee80211_device *ieee, u8 *Addr)
+{
+ PTS_COMMON_INFO pTS, pTmpTS;
+
+ printk("===========>RemovePeerTS,%pM\n", Addr);
+ list_for_each_entry_safe(pTS, pTmpTS, &ieee->Tx_TS_Pending_List, List)
+ {
+ if (memcmp(pTS->Addr, Addr, 6) == 0)
+ {
+ RemoveTsEntry(ieee, pTS, TX_DIR);
+ list_del_init(&pTS->List);
+ list_add_tail(&pTS->List, &ieee->Tx_TS_Unused_List);
+ }
+ }
+
+ list_for_each_entry_safe(pTS, pTmpTS, &ieee->Tx_TS_Admit_List, List)
+ {
+ if (memcmp(pTS->Addr, Addr, 6) == 0)
+ {
+ printk("====>remove Tx_TS_admin_list\n");
+ RemoveTsEntry(ieee, pTS, TX_DIR);
+ list_del_init(&pTS->List);
+ list_add_tail(&pTS->List, &ieee->Tx_TS_Unused_List);
+ }
+ }
+
+ list_for_each_entry_safe(pTS, pTmpTS, &ieee->Rx_TS_Pending_List, List)
+ {
+ if (memcmp(pTS->Addr, Addr, 6) == 0)
+ {
+ RemoveTsEntry(ieee, pTS, RX_DIR);
+ list_del_init(&pTS->List);
+ list_add_tail(&pTS->List, &ieee->Rx_TS_Unused_List);
+ }
+ }
+
+ list_for_each_entry_safe(pTS, pTmpTS, &ieee->Rx_TS_Admit_List, List)
+ {
+ if (memcmp(pTS->Addr, Addr, 6) == 0)
+ {
+ RemoveTsEntry(ieee, pTS, RX_DIR);
+ list_del_init(&pTS->List);
+ list_add_tail(&pTS->List, &ieee->Rx_TS_Unused_List);
+ }
+ }
+}
+
+void RemoveAllTS(struct ieee80211_device *ieee)
+{
+ PTS_COMMON_INFO pTS, pTmpTS;
+
+ list_for_each_entry_safe(pTS, pTmpTS, &ieee->Tx_TS_Pending_List, List)
+ {
+ RemoveTsEntry(ieee, pTS, TX_DIR);
+ list_del_init(&pTS->List);
+ list_add_tail(&pTS->List, &ieee->Tx_TS_Unused_List);
+ }
+
+ list_for_each_entry_safe(pTS, pTmpTS, &ieee->Tx_TS_Admit_List, List)
+ {
+ RemoveTsEntry(ieee, pTS, TX_DIR);
+ list_del_init(&pTS->List);
+ list_add_tail(&pTS->List, &ieee->Tx_TS_Unused_List);
+ }
+
+ list_for_each_entry_safe(pTS, pTmpTS, &ieee->Rx_TS_Pending_List, List)
+ {
+ RemoveTsEntry(ieee, pTS, RX_DIR);
+ list_del_init(&pTS->List);
+ list_add_tail(&pTS->List, &ieee->Rx_TS_Unused_List);
+ }
+
+ list_for_each_entry_safe(pTS, pTmpTS, &ieee->Rx_TS_Admit_List, List)
+ {
+ RemoveTsEntry(ieee, pTS, RX_DIR);
+ list_del_init(&pTS->List);
+ list_add_tail(&pTS->List, &ieee->Rx_TS_Unused_List);
+ }
+}
+
+void TsStartAddBaProcess(struct ieee80211_device *ieee, PTX_TS_RECORD pTxTS)
+{
+ if(pTxTS->bAddBaReqInProgress == false)
+ {
+ pTxTS->bAddBaReqInProgress = true;
+ if(pTxTS->bAddBaReqDelayed)
+ {
+ IEEE80211_DEBUG(IEEE80211_DL_BA, "TsStartAddBaProcess(): Delayed Start ADDBA after 60 sec!!\n");
+ mod_timer(&pTxTS->TsAddBaTimer, jiffies + MSECS(TS_ADDBA_DELAY));
+ }
+ else
+ {
+ IEEE80211_DEBUG(IEEE80211_DL_BA,"TsStartAddBaProcess(): Immediately Start ADDBA now!!\n");
+ mod_timer(&pTxTS->TsAddBaTimer, jiffies+10); //set 10 ticks
+ }
+ }
+ else
+ IEEE80211_DEBUG(IEEE80211_DL_ERR, "%s()==>BA timer is already added\n", __func__);
+}
diff --git a/drivers/staging/rtl8192u/r8180_93cx6.c b/drivers/staging/rtl8192u/r8180_93cx6.c
new file mode 100644
index 000000000..97d9b3f49
--- /dev/null
+++ b/drivers/staging/rtl8192u/r8180_93cx6.c
@@ -0,0 +1,157 @@
+/*
+ This files contains card eeprom (93c46 or 93c56) programming routines,
+ memory is addressed by 16 bits words.
+
+ This is part of rtl8180 OpenSource driver.
+ Copyright (C) Andrea Merello 2004 <andrea.merello@gmail.com>
+ Released under the terms of GPL (General Public Licence)
+
+ Parts of this driver are based on the GPL part of the
+ official realtek driver.
+
+ Parts of this driver are based on the rtl8180 driver skeleton
+ from Patric Schenke & Andres Salomon.
+
+ Parts of this driver are based on the Intel Pro Wireless 2100 GPL driver.
+
+ We want to thank the Authors of those projects and the Ndiswrapper
+ project Authors.
+*/
+
+#include "r8180_93cx6.h"
+
+static void eprom_cs(struct net_device *dev, short bit)
+{
+ u8 cmdreg;
+
+ read_nic_byte_E(dev, EPROM_CMD, &cmdreg);
+ if (bit)
+ /* enable EPROM */
+ write_nic_byte_E(dev, EPROM_CMD, cmdreg | EPROM_CS_BIT);
+ else
+ /* disable EPROM */
+ write_nic_byte_E(dev, EPROM_CMD, cmdreg & ~EPROM_CS_BIT);
+
+ force_pci_posting(dev);
+ udelay(EPROM_DELAY);
+}
+
+
+static void eprom_ck_cycle(struct net_device *dev)
+{
+ u8 cmdreg;
+
+ read_nic_byte_E(dev, EPROM_CMD, &cmdreg);
+ write_nic_byte_E(dev, EPROM_CMD, cmdreg | EPROM_CK_BIT);
+ force_pci_posting(dev);
+ udelay(EPROM_DELAY);
+
+ read_nic_byte_E(dev, EPROM_CMD, &cmdreg);
+ write_nic_byte_E(dev, EPROM_CMD, cmdreg & ~EPROM_CK_BIT);
+ force_pci_posting(dev);
+ udelay(EPROM_DELAY);
+}
+
+
+static void eprom_w(struct net_device *dev, short bit)
+{
+ u8 cmdreg;
+
+ read_nic_byte_E(dev, EPROM_CMD, &cmdreg);
+ if (bit)
+ write_nic_byte_E(dev, EPROM_CMD, cmdreg | EPROM_W_BIT);
+ else
+ write_nic_byte_E(dev, EPROM_CMD, cmdreg & ~EPROM_W_BIT);
+
+ force_pci_posting(dev);
+ udelay(EPROM_DELAY);
+}
+
+
+static short eprom_r(struct net_device *dev)
+{
+ u8 bit;
+
+ read_nic_byte_E(dev, EPROM_CMD, &bit);
+ udelay(EPROM_DELAY);
+
+ if (bit & EPROM_R_BIT)
+ return 1;
+
+ return 0;
+}
+
+
+static void eprom_send_bits_string(struct net_device *dev, short b[], int len)
+{
+ int i;
+
+ for (i = 0; i < len; i++) {
+ eprom_w(dev, b[i]);
+ eprom_ck_cycle(dev);
+ }
+}
+
+
+u32 eprom_read(struct net_device *dev, u32 addr)
+{
+ struct r8192_priv *priv = ieee80211_priv(dev);
+ short read_cmd[] = {1, 1, 0};
+ short addr_str[8];
+ int i;
+ int addr_len;
+ u32 ret;
+
+ ret = 0;
+ /* enable EPROM programming */
+ write_nic_byte_E(dev, EPROM_CMD,
+ (EPROM_CMD_PROGRAM<<EPROM_CMD_OPERATING_MODE_SHIFT));
+ force_pci_posting(dev);
+ udelay(EPROM_DELAY);
+
+ if (priv->epromtype == EPROM_93c56) {
+ addr_str[7] = addr & 1;
+ addr_str[6] = addr & (1<<1);
+ addr_str[5] = addr & (1<<2);
+ addr_str[4] = addr & (1<<3);
+ addr_str[3] = addr & (1<<4);
+ addr_str[2] = addr & (1<<5);
+ addr_str[1] = addr & (1<<6);
+ addr_str[0] = addr & (1<<7);
+ addr_len = 8;
+ } else {
+ addr_str[5] = addr & 1;
+ addr_str[4] = addr & (1<<1);
+ addr_str[3] = addr & (1<<2);
+ addr_str[2] = addr & (1<<3);
+ addr_str[1] = addr & (1<<4);
+ addr_str[0] = addr & (1<<5);
+ addr_len = 6;
+ }
+ eprom_cs(dev, 1);
+ eprom_ck_cycle(dev);
+ eprom_send_bits_string(dev, read_cmd, 3);
+ eprom_send_bits_string(dev, addr_str, addr_len);
+
+ /*
+ * keep chip pin D to low state while reading.
+ * I'm unsure if it is necessary, but anyway shouldn't hurt
+ */
+ eprom_w(dev, 0);
+
+ for (i = 0; i < 16; i++) {
+ /* eeprom needs a clk cycle between writing opcode&adr
+ * and reading data. (eeprom outs a dummy 0)
+ */
+ eprom_ck_cycle(dev);
+ ret |= (eprom_r(dev)<<(15-i));
+ }
+
+ eprom_cs(dev, 0);
+ eprom_ck_cycle(dev);
+
+ /* disable EPROM programming */
+ write_nic_byte_E(dev, EPROM_CMD,
+ (EPROM_CMD_NORMAL<<EPROM_CMD_OPERATING_MODE_SHIFT));
+ return ret;
+}
diff --git a/drivers/staging/rtl8192u/r8180_93cx6.h b/drivers/staging/rtl8192u/r8180_93cx6.h
new file mode 100644
index 000000000..b840348eb
--- /dev/null
+++ b/drivers/staging/rtl8192u/r8180_93cx6.h
@@ -0,0 +1,43 @@
+/*
+ This is part of rtl8187 OpenSource driver
+ Copyright (C) Andrea Merello 2004-2005 <andrea.merello@gmail.com>
+ Released under the terms of GPL (General Public Licence)
+
+ Parts of this driver are based on the GPL part of the
+ official realtek driver
+ Parts of this driver are based on the rtl8180 driver skeleton
+ from Patric Schenke & Andres Salomon
+ Parts of this driver are based on the Intel Pro Wireless 2100 GPL driver
+
+ We want to thank the Authors of such projects and the Ndiswrapper
+ project Authors.
+*/
+
+/*This files contains card eeprom (93c46 or 93c56) programming routines*/
+/*memory is addressed by WORDS*/
+
+#include "r8192U.h"
+#include "r8192U_hw.h"
+
+#define EPROM_DELAY 10
+
+#define EPROM_ANAPARAM_ADDRLWORD 0xd
+#define EPROM_ANAPARAM_ADDRHWORD 0xe
+
+#define EPROM_RFCHIPID 0x6
+#define EPROM_TXPW_BASE 0x05
+#define EPROM_RFCHIPID_RTL8225U 5
+#define EPROM_RF_PARAM 0x4
+#define EPROM_CONFIG2 0xc
+
+#define EPROM_VERSION 0x1E
+#define MAC_ADR 0x7
+
+#define CIS 0x18
+
+#define EPROM_TXPW0 0x16
+#define EPROM_TXPW2 0x1b
+#define EPROM_TXPW1 0x3d
+
+
+u32 eprom_read(struct net_device *dev, u32 addr); /* reads a 16 bits word */
diff --git a/drivers/staging/rtl8192u/r8190_rtl8256.c b/drivers/staging/rtl8192u/r8190_rtl8256.c
new file mode 100644
index 000000000..e00032947
--- /dev/null
+++ b/drivers/staging/rtl8192u/r8190_rtl8256.c
@@ -0,0 +1,290 @@
+/*
+ This is part of the rtl8192 driver
+ released under the GPL (See file COPYING for details).
+
+ This files contains programming code for the rtl8256
+ radio frontend.
+
+ *Many* thanks to Realtek Corp. for their great support!
+
+*/
+
+#include "r8192U.h"
+#include "r8192U_hw.h"
+#include "r819xU_phyreg.h"
+#include "r819xU_phy.h"
+#include "r8190_rtl8256.h"
+
+/*--------------------------------------------------------------------------
+ * Overview: set RF band width (20M or 40M)
+ * Input: struct net_device* dev
+ * WIRELESS_BANDWIDTH_E Bandwidth //20M or 40M
+ * Output: NONE
+ * Return: NONE
+ * Note: 8226 support both 20M and 40 MHz
+ *---------------------------------------------------------------------------*/
+void PHY_SetRF8256Bandwidth(struct net_device *dev, HT_CHANNEL_WIDTH Bandwidth)
+{
+ u8 eRFPath;
+ struct r8192_priv *priv = ieee80211_priv(dev);
+
+ /* for(eRFPath = RF90_PATH_A; eRFPath <pHalData->NumTotalRFPath;
+ * eRFPath++)
+ */
+ for (eRFPath = 0; eRFPath < RF90_PATH_MAX; eRFPath++) {
+ if (!rtl8192_phy_CheckIsLegalRFPath(dev, eRFPath))
+ continue;
+
+ switch (Bandwidth) {
+ case HT_CHANNEL_WIDTH_20:
+ if (priv->card_8192_version == VERSION_819xU_A
+ || priv->card_8192_version
+ == VERSION_819xU_B) { /* 8256 D-cut, E-cut, xiong: consider it later! */
+ rtl8192_phy_SetRFReg(dev,
+ (RF90_RADIO_PATH_E)eRFPath,
+ 0x0b, bMask12Bits, 0x100); /* phy para:1ba */
+ rtl8192_phy_SetRFReg(dev,
+ (RF90_RADIO_PATH_E)eRFPath,
+ 0x2c, bMask12Bits, 0x3d7);
+ rtl8192_phy_SetRFReg(dev,
+ (RF90_RADIO_PATH_E)eRFPath,
+ 0x0e, bMask12Bits, 0x021);
+ rtl8192_phy_SetRFReg(dev,
+ (RF90_RADIO_PATH_E)eRFPath,
+ 0x14, bMask12Bits, 0x5ab);
+ } else {
+ RT_TRACE(COMP_ERR, "PHY_SetRF8256Bandwidth(): unknown hardware version\n");
+ }
+ break;
+ case HT_CHANNEL_WIDTH_20_40:
+ if (priv->card_8192_version == VERSION_819xU_A || priv->card_8192_version == VERSION_819xU_B) { /* 8256 D-cut, E-cut, xiong: consider it later! */
+ rtl8192_phy_SetRFReg(dev, (RF90_RADIO_PATH_E)eRFPath, 0x0b, bMask12Bits, 0x300); /* phy para:3ba */
+ rtl8192_phy_SetRFReg(dev, (RF90_RADIO_PATH_E)eRFPath, 0x2c, bMask12Bits, 0x3df);
+ rtl8192_phy_SetRFReg(dev, (RF90_RADIO_PATH_E)eRFPath, 0x0e, bMask12Bits, 0x0a1);
+
+ if (priv->chan == 3 || priv->chan == 9)
+ /* I need to set priv->chan whenever current channel changes */
+ rtl8192_phy_SetRFReg(dev, (RF90_RADIO_PATH_E)eRFPath, 0x14, bMask12Bits, 0x59b);
+ else
+ rtl8192_phy_SetRFReg(dev, (RF90_RADIO_PATH_E)eRFPath, 0x14, bMask12Bits, 0x5ab);
+ } else {
+ RT_TRACE(COMP_ERR, "PHY_SetRF8256Bandwidth(): unknown hardware version\n");
+ }
+ break;
+ default:
+ RT_TRACE(COMP_ERR, "PHY_SetRF8256Bandwidth(): unknown Bandwidth: %#X\n", Bandwidth);
+ break;
+
+ }
+ }
+}
+/*--------------------------------------------------------------------------
+ * Overview: Interface to config 8256
+ * Input: struct net_device* dev
+ * Output: NONE
+ * Return: NONE
+ *---------------------------------------------------------------------------*/
+void PHY_RF8256_Config(struct net_device *dev)
+{
+ struct r8192_priv *priv = ieee80211_priv(dev);
+ /* Initialize general global value
+ *
+ * TODO: Extend RF_PATH_C and RF_PATH_D in the future
+ */
+ priv->NumTotalRFPath = RTL819X_TOTAL_RF_PATH;
+ /* Config BB and RF */
+ phy_RF8256_Config_ParaFile(dev);
+}
+/*--------------------------------------------------------------------------
+ * Overview: Interface to config 8256
+ * Input: struct net_device* dev
+ * Output: NONE
+ * Return: NONE
+ *---------------------------------------------------------------------------*/
+void phy_RF8256_Config_ParaFile(struct net_device *dev)
+{
+ u32 u4RegValue = 0;
+ u8 eRFPath;
+ BB_REGISTER_DEFINITION_T *pPhyReg;
+ struct r8192_priv *priv = ieee80211_priv(dev);
+ u32 RegOffSetToBeCheck = 0x3;
+ u32 RegValueToBeCheck = 0x7f1;
+ u32 RF3_Final_Value = 0;
+ u8 ConstRetryTimes = 5, RetryTimes = 5;
+ u8 ret = 0;
+ /* Initialize RF */
+ for (eRFPath = (RF90_RADIO_PATH_E)RF90_PATH_A; eRFPath < priv->NumTotalRFPath; eRFPath++) {
+ if (!rtl8192_phy_CheckIsLegalRFPath(dev, eRFPath))
+ continue;
+
+ pPhyReg = &priv->PHYRegDef[eRFPath];
+
+ /* Joseph test for shorten RF config
+ * pHalData->RfReg0Value[eRFPath] = rtl8192_phy_QueryRFReg(dev, (RF90_RADIO_PATH_E)eRFPath, rGlobalCtrl, bMaskDWord);
+ * ----Store original RFENV control type
+ */
+ switch (eRFPath) {
+ case RF90_PATH_A:
+ case RF90_PATH_C:
+ u4RegValue = rtl8192_QueryBBReg(dev, pPhyReg->rfintfs, bRFSI_RFENV);
+ break;
+ case RF90_PATH_B:
+ case RF90_PATH_D:
+ u4RegValue = rtl8192_QueryBBReg(dev, pPhyReg->rfintfs, bRFSI_RFENV<<16);
+ break;
+ }
+
+ /*----Set RF_ENV enable----*/
+ rtl8192_setBBreg(dev, pPhyReg->rfintfe, bRFSI_RFENV<<16, 0x1);
+
+ /*----Set RF_ENV output high----*/
+ rtl8192_setBBreg(dev, pPhyReg->rfintfo, bRFSI_RFENV, 0x1);
+
+ /* Set bit number of Address and Data for RF register */
+ rtl8192_setBBreg(dev, pPhyReg->rfHSSIPara2, b3WireAddressLength, 0x0); /* Set 0 to 4 bits for Z-serial and set 1 to 6 bits for 8258 */
+ rtl8192_setBBreg(dev, pPhyReg->rfHSSIPara2, b3WireDataLength, 0x0); /* Set 0 to 12 bits for Z-serial and 8258, and set 1 to 14 bits for ??? */
+
+ rtl8192_phy_SetRFReg(dev, (RF90_RADIO_PATH_E) eRFPath, 0x0, bMask12Bits, 0xbf);
+
+ /* Check RF block (for FPGA platform only)----
+ * TODO: this function should be removed on ASIC , Emily 2007.2.2
+ */
+ if (rtl8192_phy_checkBBAndRF(dev, HW90_BLOCK_RF, (RF90_RADIO_PATH_E)eRFPath)) {
+ RT_TRACE(COMP_ERR, "PHY_RF8256_Config():Check Radio[%d] Fail!!\n", eRFPath);
+ goto phy_RF8256_Config_ParaFile_Fail;
+ }
+
+ RetryTimes = ConstRetryTimes;
+ RF3_Final_Value = 0;
+ /*----Initialize RF fom connfiguration file----*/
+ switch (eRFPath) {
+ case RF90_PATH_A:
+ while (RF3_Final_Value != RegValueToBeCheck && RetryTimes != 0) {
+ ret = rtl8192_phy_ConfigRFWithHeaderFile(dev, (RF90_RADIO_PATH_E)eRFPath);
+ RF3_Final_Value = rtl8192_phy_QueryRFReg(dev, (RF90_RADIO_PATH_E)eRFPath, RegOffSetToBeCheck, bMask12Bits);
+ RT_TRACE(COMP_RF, "RF %d %d register final value: %x\n", eRFPath, RegOffSetToBeCheck, RF3_Final_Value);
+ RetryTimes--;
+ }
+ break;
+ case RF90_PATH_B:
+ while (RF3_Final_Value != RegValueToBeCheck && RetryTimes != 0) {
+ ret = rtl8192_phy_ConfigRFWithHeaderFile(dev, (RF90_RADIO_PATH_E)eRFPath);
+ RF3_Final_Value = rtl8192_phy_QueryRFReg(dev, (RF90_RADIO_PATH_E)eRFPath, RegOffSetToBeCheck, bMask12Bits);
+ RT_TRACE(COMP_RF, "RF %d %d register final value: %x\n", eRFPath, RegOffSetToBeCheck, RF3_Final_Value);
+ RetryTimes--;
+ }
+ break;
+ case RF90_PATH_C:
+ while (RF3_Final_Value != RegValueToBeCheck && RetryTimes != 0) {
+ ret = rtl8192_phy_ConfigRFWithHeaderFile(dev, (RF90_RADIO_PATH_E)eRFPath);
+ RF3_Final_Value = rtl8192_phy_QueryRFReg(dev, (RF90_RADIO_PATH_E)eRFPath, RegOffSetToBeCheck, bMask12Bits);
+ RT_TRACE(COMP_RF, "RF %d %d register final value: %x\n", eRFPath, RegOffSetToBeCheck, RF3_Final_Value);
+ RetryTimes--;
+ }
+ break;
+ case RF90_PATH_D:
+ while (RF3_Final_Value != RegValueToBeCheck && RetryTimes != 0) {
+ ret = rtl8192_phy_ConfigRFWithHeaderFile(dev, (RF90_RADIO_PATH_E)eRFPath);
+ RF3_Final_Value = rtl8192_phy_QueryRFReg(dev, (RF90_RADIO_PATH_E)eRFPath, RegOffSetToBeCheck, bMask12Bits);
+ RT_TRACE(COMP_RF, "RF %d %d register final value: %x\n", eRFPath, RegOffSetToBeCheck, RF3_Final_Value);
+ RetryTimes--;
+ }
+ break;
+ }
+
+ /*----Restore RFENV control type----*/;
+ switch (eRFPath) {
+ case RF90_PATH_A:
+ case RF90_PATH_C:
+ rtl8192_setBBreg(dev, pPhyReg->rfintfs, bRFSI_RFENV, u4RegValue);
+ break;
+ case RF90_PATH_B:
+ case RF90_PATH_D:
+ rtl8192_setBBreg(dev, pPhyReg->rfintfs, bRFSI_RFENV<<16, u4RegValue);
+ break;
+ }
+
+ if (ret) {
+ RT_TRACE(COMP_ERR, "phy_RF8256_Config_ParaFile():Radio[%d] Fail!!", eRFPath);
+ goto phy_RF8256_Config_ParaFile_Fail;
+ }
+
+ }
+
+ RT_TRACE(COMP_PHY, "PHY Initialization Success\n");
+ return;
+
+phy_RF8256_Config_ParaFile_Fail:
+ RT_TRACE(COMP_ERR, "PHY Initialization failed\n");
+}
+
+
+void PHY_SetRF8256CCKTxPower(struct net_device *dev, u8 powerlevel)
+{
+ u32 TxAGC = 0;
+ struct r8192_priv *priv = ieee80211_priv(dev);
+ TxAGC = powerlevel;
+
+ if (priv->bDynamicTxLowPower) {
+ if (priv->CustomerID == RT_CID_819x_Netcore)
+ TxAGC = 0x22;
+ else
+ TxAGC += priv->CckPwEnl;
+ }
+
+ if (TxAGC > 0x24)
+ TxAGC = 0x24;
+ rtl8192_setBBreg(dev, rTxAGC_CCK_Mcs32, bTxAGCRateCCK, TxAGC);
+}
+
+
+void PHY_SetRF8256OFDMTxPower(struct net_device *dev, u8 powerlevel)
+{
+ struct r8192_priv *priv = ieee80211_priv(dev);
+ /* Joseph TxPower for 8192 testing */
+ u32 writeVal, powerBase0, powerBase1, writeVal_tmp;
+ u8 index = 0;
+ u16 RegOffset[6] = {0xe00, 0xe04, 0xe10, 0xe14, 0xe18, 0xe1c};
+ u8 byte0, byte1, byte2, byte3;
+
+ powerBase0 = powerlevel + priv->TxPowerDiff; /* OFDM rates */
+ powerBase0 = (powerBase0<<24) | (powerBase0<<16) | (powerBase0<<8) | powerBase0;
+ powerBase1 = powerlevel; /* MCS rates */
+ powerBase1 = (powerBase1<<24) | (powerBase1<<16) | (powerBase1<<8) | powerBase1;
+
+ for (index = 0; index < 6; index++) {
+ writeVal = priv->MCSTxPowerLevelOriginalOffset[index] + ((index < 2)?powerBase0:powerBase1);
+ byte0 = (u8)(writeVal & 0x7f);
+ byte1 = (u8)((writeVal & 0x7f00)>>8);
+ byte2 = (u8)((writeVal & 0x7f0000)>>16);
+ byte3 = (u8)((writeVal & 0x7f000000)>>24);
+
+ if (byte0 > 0x24)
+ /* Max power index = 0x24 */
+ byte0 = 0x24;
+ if (byte1 > 0x24)
+ byte1 = 0x24;
+ if (byte2 > 0x24)
+ byte2 = 0x24;
+ if (byte3 > 0x24)
+ byte3 = 0x24;
+
+ /* for tx power track */
+ if (index == 3) {
+ writeVal_tmp = (byte3<<24) | (byte2<<16) | (byte1<<8) | byte0;
+ priv->Pwr_Track = writeVal_tmp;
+ }
+
+ if (priv->bDynamicTxHighPower) {
+ /*Add by Jacken 2008/03/06
+ *Emily, 20080613. Set low tx power for both MCS and legacy OFDM
+ */
+ writeVal = 0x03030303;
+ } else {
+ writeVal = (byte3<<24) | (byte2<<16) | (byte1<<8) | byte0;
+ }
+ rtl8192_setBBreg(dev, RegOffset[index], 0x7f7f7f7f, writeVal);
+ }
+ return;
+
+}
diff --git a/drivers/staging/rtl8192u/r8190_rtl8256.h b/drivers/staging/rtl8192u/r8190_rtl8256.h
new file mode 100644
index 000000000..6e5662f79
--- /dev/null
+++ b/drivers/staging/rtl8192u/r8190_rtl8256.h
@@ -0,0 +1,24 @@
+/*
+ This is part of the rtl8180-sa2400 driver
+ released under the GPL (See file COPYING for details).
+ Copyright (c) 2005 Andrea Merello <andrea.merello@gmail.com>
+
+ This files contains programming code for the rtl8256
+ radio frontend.
+
+ *Many* thanks to Realtek Corp. for their great support!
+
+*/
+
+#ifndef RTL8225H
+#define RTL8225H
+
+#define RTL819X_TOTAL_RF_PATH 2 /* for 8192U */
+extern void PHY_SetRF8256Bandwidth(struct net_device *dev,
+ HT_CHANNEL_WIDTH Bandwidth);
+extern void PHY_RF8256_Config(struct net_device *dev);
+extern void phy_RF8256_Config_ParaFile(struct net_device *dev);
+extern void PHY_SetRF8256CCKTxPower(struct net_device *dev, u8 powerlevel);
+extern void PHY_SetRF8256OFDMTxPower(struct net_device *dev, u8 powerlevel);
+
+#endif
diff --git a/drivers/staging/rtl8192u/r8192U.h b/drivers/staging/rtl8192u/r8192U.h
new file mode 100644
index 000000000..6c2e438c9
--- /dev/null
+++ b/drivers/staging/rtl8192u/r8192U.h
@@ -0,0 +1,1196 @@
+/*
+ * This is part of rtl8187 OpenSource driver.
+ * Copyright (C) Andrea Merello 2004-2005 <andrea.merello@gmail.com>
+ * Released under the terms of GPL (General Public Licence)
+ *
+ * Parts of this driver are based on the GPL part of the
+ * official realtek driver
+ *
+ * Parts of this driver are based on the rtl8192 driver skeleton
+ * from Patric Schenke & Andres Salomon
+ *
+ * Parts of this driver are based on the Intel Pro Wireless 2100 GPL driver
+ *
+ * We want to thank the Authors of those projects and the Ndiswrapper
+ * project Authors.
+ */
+
+#ifndef R819xU_H
+#define R819xU_H
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/ioport.h>
+#include <linux/sched.h>
+#include <linux/types.h>
+#include <linux/slab.h>
+#include <linux/netdevice.h>
+#include <linux/usb.h>
+#include <linux/etherdevice.h>
+#include <linux/delay.h>
+#include <linux/rtnetlink.h>
+#include <linux/wireless.h>
+#include <linux/timer.h>
+#include <linux/proc_fs.h>
+#include <linux/if_arp.h>
+#include <linux/random.h>
+#include <asm/io.h>
+#include "ieee80211/ieee80211.h"
+
+#define RTL8192U
+#define RTL819xU_MODULE_NAME "rtl819xU"
+/* HW security */
+#define MAX_KEY_LEN 61
+#define KEY_BUF_SIZE 5
+
+#define BIT0 0x00000001
+#define BIT1 0x00000002
+#define BIT2 0x00000004
+#define BIT3 0x00000008
+#define BIT4 0x00000010
+#define BIT5 0x00000020
+#define BIT6 0x00000040
+#define BIT7 0x00000080
+#define BIT8 0x00000100
+#define BIT9 0x00000200
+#define BIT10 0x00000400
+#define BIT11 0x00000800
+#define BIT12 0x00001000
+#define BIT13 0x00002000
+#define BIT14 0x00004000
+#define BIT15 0x00008000
+#define BIT16 0x00010000
+#define BIT17 0x00020000
+#define BIT18 0x00040000
+#define BIT19 0x00080000
+#define BIT20 0x00100000
+#define BIT21 0x00200000
+#define BIT22 0x00400000
+#define BIT23 0x00800000
+#define BIT24 0x01000000
+#define BIT25 0x02000000
+#define BIT26 0x04000000
+#define BIT27 0x08000000
+#define BIT28 0x10000000
+#define BIT29 0x20000000
+#define BIT30 0x40000000
+#define BIT31 0x80000000
+
+#define Rx_Smooth_Factor 20
+#define DMESG(x, a...)
+#define DMESGW(x, a...)
+#define DMESGE(x, a...)
+extern u32 rt_global_debug_component;
+#define RT_TRACE(component, x, args...) \
+ do { \
+ if (rt_global_debug_component & component) \
+ pr_debug("RTL8192U: " x "\n", ##args); \
+ } while (0)
+
+#define COMP_TRACE BIT0 /* Function call tracing. */
+#define COMP_DBG BIT1
+#define COMP_INIT BIT2 /* Driver initialization/halt/reset. */
+
+
+#define COMP_RECV BIT3 /* Receive data path. */
+#define COMP_SEND BIT4 /* Send data path. */
+#define COMP_IO BIT5
+/* 802.11 Power Save mode or System/Device Power state. */
+#define COMP_POWER BIT6
+/* 802.11 link related: join/start BSS, leave BSS. */
+#define COMP_EPROM BIT7
+#define COMP_SWBW BIT8 /* Bandwidth switch. */
+#define COMP_POWER_TRACKING BIT9 /* 8190 TX Power Tracking */
+#define COMP_TURBO BIT10 /* Turbo Mode */
+#define COMP_QOS BIT11
+#define COMP_RATE BIT12 /* Rate Adaptive mechanism */
+#define COMP_RM BIT13 /* Radio Measurement */
+#define COMP_DIG BIT14
+#define COMP_PHY BIT15
+#define COMP_CH BIT16 /* Channel setting debug */
+#define COMP_TXAGC BIT17 /* Tx power */
+#define COMP_HIPWR BIT18 /* High Power Mechanism */
+#define COMP_HALDM BIT19 /* HW Dynamic Mechanism */
+#define COMP_SEC BIT20 /* Event handling */
+#define COMP_LED BIT21
+#define COMP_RF BIT22
+#define COMP_RXDESC BIT23 /* Rx desc information for SD3 debug */
+
+/* 11n or 8190 specific code */
+
+#define COMP_FIRMWARE BIT24 /* Firmware downloading */
+#define COMP_HT BIT25 /* 802.11n HT related information */
+#define COMP_AMSDU BIT26 /* A-MSDU Debugging */
+#define COMP_SCAN BIT27
+#define COMP_DOWN BIT29 /* rm driver module */
+#define COMP_RESET BIT30 /* Silent reset */
+#define COMP_ERR BIT31 /* Error out, always on */
+
+#define RTL819x_DEBUG
+#ifdef RTL819x_DEBUG
+#define RTL8192U_ASSERT(expr) \
+ do { \
+ if (!(expr)) { \
+ pr_debug("Assertion failed! %s, %s, %s, line = %d\n", \
+ #expr, __FILE__, __func__, __LINE__); \
+ } \
+ } while (0)
+/*
+ * Debug out data buf.
+ * If you want to print DATA buffer related BA,
+ * please set ieee80211_debug_level to DATA|BA
+ */
+#define RT_DEBUG_DATA(level, data, datalen) \
+ do { \
+ if ((rt_global_debug_component & (level)) == (level)) { \
+ int i; \
+ u8 *pdata = (u8 *) data; \
+ pr_debug("RTL8192U: %s()\n", __func__); \
+ for (i = 0; i < (int)(datalen); i++) { \
+ printk("%2x ", pdata[i]); \
+ if ((i+1)%16 == 0) \
+ printk("\n"); \
+ } \
+ printk("\n"); \
+ } \
+ } while (0)
+#else
+#define RTL8192U_ASSERT(expr) do {} while (0)
+#define RT_DEBUG_DATA(level, data, datalen) do {} while (0)
+#endif /* RTL8169_DEBUG */
+
+
+/* Queue Select Value in TxDesc */
+#define QSLT_BK 0x1
+#define QSLT_BE 0x0
+#define QSLT_VI 0x4
+#define QSLT_VO 0x6
+#define QSLT_BEACON 0x10
+#define QSLT_HIGH 0x11
+#define QSLT_MGNT 0x12
+#define QSLT_CMD 0x13
+
+#define DESC90_RATE1M 0x00
+#define DESC90_RATE2M 0x01
+#define DESC90_RATE5_5M 0x02
+#define DESC90_RATE11M 0x03
+#define DESC90_RATE6M 0x04
+#define DESC90_RATE9M 0x05
+#define DESC90_RATE12M 0x06
+#define DESC90_RATE18M 0x07
+#define DESC90_RATE24M 0x08
+#define DESC90_RATE36M 0x09
+#define DESC90_RATE48M 0x0a
+#define DESC90_RATE54M 0x0b
+#define DESC90_RATEMCS0 0x00
+#define DESC90_RATEMCS1 0x01
+#define DESC90_RATEMCS2 0x02
+#define DESC90_RATEMCS3 0x03
+#define DESC90_RATEMCS4 0x04
+#define DESC90_RATEMCS5 0x05
+#define DESC90_RATEMCS6 0x06
+#define DESC90_RATEMCS7 0x07
+#define DESC90_RATEMCS8 0x08
+#define DESC90_RATEMCS9 0x09
+#define DESC90_RATEMCS10 0x0a
+#define DESC90_RATEMCS11 0x0b
+#define DESC90_RATEMCS12 0x0c
+#define DESC90_RATEMCS13 0x0d
+#define DESC90_RATEMCS14 0x0e
+#define DESC90_RATEMCS15 0x0f
+#define DESC90_RATEMCS32 0x20
+
+#define RTL819X_DEFAULT_RF_TYPE RF_1T2R
+
+#define IEEE80211_WATCH_DOG_TIME 2000
+#define PHY_Beacon_RSSI_SLID_WIN_MAX 10
+/* For Tx Power Tracking */
+#define OFDM_Table_Length 19
+#define CCK_Table_length 12
+
+/* For rtl819x */
+typedef struct _tx_desc_819x_usb {
+ /* DWORD 0 */
+ u16 PktSize;
+ u8 Offset;
+ u8 Reserved0:3;
+ u8 CmdInit:1;
+ u8 LastSeg:1;
+ u8 FirstSeg:1;
+ u8 LINIP:1;
+ u8 OWN:1;
+
+ /* DWORD 1 */
+ u8 TxFWInfoSize;
+ u8 RATid:3;
+ u8 DISFB:1;
+ u8 USERATE:1;
+ u8 MOREFRAG:1;
+ u8 NoEnc:1;
+ u8 PIFS:1;
+ u8 QueueSelect:5;
+ u8 NoACM:1;
+ u8 Reserved1:2;
+ u8 SecCAMID:5;
+ u8 SecDescAssign:1;
+ u8 SecType:2;
+
+ /* DWORD 2 */
+ u16 TxBufferSize;
+ u8 ResvForPaddingLen:7;
+ u8 Reserved3:1;
+ u8 Reserved4;
+
+ /* DWORD 3, 4, 5 */
+ u32 Reserved5;
+ u32 Reserved6;
+ u32 Reserved7;
+} tx_desc_819x_usb, *ptx_desc_819x_usb;
+
+#ifdef USB_TX_DRIVER_AGGREGATION_ENABLE
+typedef struct _tx_desc_819x_usb_aggr_subframe {
+ /* DWORD 0 */
+ u16 PktSize;
+ u8 Offset;
+ u8 TxFWInfoSize;
+
+ /* DWORD 1 */
+ u8 RATid:3;
+ u8 DISFB:1;
+ u8 USERATE:1;
+ u8 MOREFRAG:1;
+ u8 NoEnc:1;
+ u8 PIFS:1;
+ u8 QueueSelect:5;
+ u8 NoACM:1;
+ u8 Reserved1:2;
+ u8 SecCAMID:5;
+ u8 SecDescAssign:1;
+ u8 SecType:2;
+ u8 PacketID:7;
+ u8 OWN:1;
+} tx_desc_819x_usb_aggr_subframe, *ptx_desc_819x_usb_aggr_subframe;
+#endif
+
+
+
+typedef struct _tx_desc_cmd_819x_usb {
+ /* DWORD 0 */
+ u16 Reserved0;
+ u8 Reserved1;
+ u8 Reserved2:3;
+ u8 CmdInit:1;
+ u8 LastSeg:1;
+ u8 FirstSeg:1;
+ u8 LINIP:1;
+ u8 OWN:1;
+
+ /* DOWRD 1 */
+ u8 TxFWInfoSize;
+ u8 Reserved3;
+ u8 QueueSelect;
+ u8 Reserved4;
+
+ /* DOWRD 2 */
+ u16 TxBufferSize;
+ u16 Reserved5;
+
+ /* DWORD 3, 4, 5 */
+ u32 Reserved6;
+ u32 Reserved7;
+ u32 Reserved8;
+} tx_desc_cmd_819x_usb, *ptx_desc_cmd_819x_usb;
+
+
+typedef struct _tx_fwinfo_819x_usb {
+ /* DOWRD 0 */
+ u8 TxRate:7;
+ u8 CtsEnable:1;
+ u8 RtsRate:7;
+ u8 RtsEnable:1;
+ u8 TxHT:1;
+ u8 Short:1; /* Error out, always on */
+ u8 TxBandwidth:1; /* Used for HT MCS rate only */
+ u8 TxSubCarrier:2; /* Used for legacy OFDM rate only */
+ u8 STBC:2;
+ u8 AllowAggregation:1;
+ /* Interpret RtsRate field as high throughput data rate */
+ u8 RtsHT:1;
+ u8 RtsShort:1; /* Short PLCP for CCK or short GI for 11n MCS */
+ u8 RtsBandwidth:1; /* Used for HT MCS rate only */
+ u8 RtsSubcarrier:2;/* Used for legacy OFDM rate only */
+ u8 RtsSTBC:2;
+ /* Enable firmware to recalculate and assign packet duration */
+ u8 EnableCPUDur:1;
+
+ /* DWORD 1 */
+ u32 RxMF:2;
+ u32 RxAMD:3;
+ /* 1 indicate Tx info gathered by firmware and returned by Rx Cmd */
+ u32 TxPerPktInfoFeedback:1;
+ u32 Reserved1:2;
+ u32 TxAGCOffSet:4;
+ u32 TxAGCSign:1;
+ u32 Tx_INFO_RSVD:6;
+ u32 PacketID:13;
+} tx_fwinfo_819x_usb, *ptx_fwinfo_819x_usb;
+
+typedef struct rtl8192_rx_info {
+ struct urb *urb;
+ struct net_device *dev;
+ u8 out_pipe;
+} rtl8192_rx_info ;
+
+typedef struct rx_desc_819x_usb {
+ /* DOWRD 0 */
+ u16 Length:14;
+ u16 CRC32:1;
+ u16 ICV:1;
+ u8 RxDrvInfoSize;
+ u8 Shift:2;
+ u8 PHYStatus:1;
+ u8 SWDec:1;
+ u8 Reserved1:4;
+
+ /* DWORD 1 */
+ u32 Reserved2;
+} rx_desc_819x_usb, *prx_desc_819x_usb;
+
+#ifdef USB_RX_AGGREGATION_SUPPORT
+typedef struct _rx_desc_819x_usb_aggr_subframe {
+ /* DOWRD 0 */
+ u16 Length:14;
+ u16 CRC32:1;
+ u16 ICV:1;
+ u8 Offset;
+ u8 RxDrvInfoSize;
+ /* DOWRD 1 */
+ u8 Shift:2;
+ u8 PHYStatus:1;
+ u8 SWDec:1;
+ u8 Reserved1:4;
+ u8 Reserved2;
+ u16 Reserved3;
+} rx_desc_819x_usb_aggr_subframe, *prx_desc_819x_usb_aggr_subframe;
+#endif
+
+typedef struct rx_drvinfo_819x_usb {
+ /* DWORD 0 */
+ u16 Reserved1:12;
+ u16 PartAggr:1;
+ u16 FirstAGGR:1;
+ u16 Reserved2:2;
+
+ u8 RxRate:7;
+ u8 RxHT:1;
+
+ u8 BW:1;
+ u8 SPLCP:1;
+ u8 Reserved3:2;
+ u8 PAM:1;
+ u8 Mcast:1;
+ u8 Bcast:1;
+ u8 Reserved4:1;
+
+ /* DWORD 1 */
+ u32 TSFL;
+
+} rx_drvinfo_819x_usb, *prx_drvinfo_819x_usb;
+
+/* Support till 64 bit bus width OS */
+#define MAX_DEV_ADDR_SIZE 8
+/* For RTL8190 */
+#define MAX_FIRMWARE_INFORMATION_SIZE 32
+#define MAX_802_11_HEADER_LENGTH (40 + MAX_FIRMWARE_INFORMATION_SIZE)
+#define ENCRYPTION_MAX_OVERHEAD 128
+#define USB_HWDESC_HEADER_LEN sizeof(tx_desc_819x_usb)
+#define TX_PACKET_SHIFT_BYTES (USB_HWDESC_HEADER_LEN + sizeof(tx_fwinfo_819x_usb))
+#define MAX_FRAGMENT_COUNT 8
+#ifdef USB_TX_DRIVER_AGGREGATION_ENABLE
+#define MAX_TRANSMIT_BUFFER_SIZE 32000
+#else
+#define MAX_TRANSMIT_BUFFER_SIZE 8000
+#endif
+#ifdef USB_TX_DRIVER_AGGREGATION_ENABLE
+#define TX_PACKET_DRVAGGR_SUBFRAME_SHIFT_BYTES (sizeof(tx_desc_819x_usb_aggr_subframe) + sizeof(tx_fwinfo_819x_usb))
+#endif
+/* Octets for crc32 (FCS, ICV) */
+#define scrclng 4
+
+typedef enum rf_optype {
+ RF_OP_By_SW_3wire = 0,
+ RF_OP_By_FW,
+ RF_OP_MAX
+} rf_op_type;
+/* 8190 Loopback Mode definition */
+typedef enum _rtl819xUsb_loopback {
+ RTL819xU_NO_LOOPBACK = 0,
+ RTL819xU_MAC_LOOPBACK = 1,
+ RTL819xU_DMA_LOOPBACK = 2,
+ RTL819xU_CCK_LOOPBACK = 3,
+} rtl819xUsb_loopback_e;
+
+/* due to rtl8192 firmware */
+typedef enum _desc_packet_type_e {
+ DESC_PACKET_TYPE_INIT = 0,
+ DESC_PACKET_TYPE_NORMAL = 1,
+} desc_packet_type_e;
+
+typedef enum _firmware_status {
+ FW_STATUS_0_INIT = 0,
+ FW_STATUS_1_MOVE_BOOT_CODE = 1,
+ FW_STATUS_2_MOVE_MAIN_CODE = 2,
+ FW_STATUS_3_TURNON_CPU = 3,
+ FW_STATUS_4_MOVE_DATA_CODE = 4,
+ FW_STATUS_5_READY = 5,
+} firmware_status_e;
+
+typedef struct _rt_firmare_seg_container {
+ u16 seg_size;
+ u8 *seg_ptr;
+} fw_seg_container, *pfw_seg_container;
+typedef struct _rt_firmware {
+ firmware_status_e firmware_status;
+ u16 cmdpacket_frag_thresold;
+#define RTL8190_MAX_FIRMWARE_CODE_SIZE 64000
+ u8 firmware_buf[RTL8190_MAX_FIRMWARE_CODE_SIZE];
+ u16 firmware_buf_size;
+} rt_firmware, *prt_firmware;
+
+/* Add this to 9100 bytes to receive A-MSDU from RT-AP */
+#define MAX_RECEIVE_BUFFER_SIZE 9100
+
+typedef struct _rt_firmware_info_819xUsb {
+ u8 sz_info[16];
+} rt_firmware_info_819xUsb, *prt_firmware_info_819xUsb;
+
+/* Firmware Queue Layout */
+#define NUM_OF_FIRMWARE_QUEUE 10
+#define NUM_OF_PAGES_IN_FW 0x100
+
+#ifdef USE_ONE_PIPE
+#define NUM_OF_PAGE_IN_FW_QUEUE_BE 0x000
+#define NUM_OF_PAGE_IN_FW_QUEUE_BK 0x000
+#define NUM_OF_PAGE_IN_FW_QUEUE_VI 0x0ff
+#define NUM_OF_PAGE_IN_FW_QUEUE_VO 0x000
+#define NUM_OF_PAGE_IN_FW_QUEUE_HCCA 0
+#define NUM_OF_PAGE_IN_FW_QUEUE_CMD 0x0
+#define NUM_OF_PAGE_IN_FW_QUEUE_MGNT 0x00
+#define NUM_OF_PAGE_IN_FW_QUEUE_HIGH 0
+#define NUM_OF_PAGE_IN_FW_QUEUE_BCN 0x0
+#define NUM_OF_PAGE_IN_FW_QUEUE_PUB 0x00
+#else
+
+#define NUM_OF_PAGE_IN_FW_QUEUE_BE 0x020
+#define NUM_OF_PAGE_IN_FW_QUEUE_BK 0x020
+#define NUM_OF_PAGE_IN_FW_QUEUE_VI 0x040
+#define NUM_OF_PAGE_IN_FW_QUEUE_VO 0x040
+#define NUM_OF_PAGE_IN_FW_QUEUE_HCCA 0
+#define NUM_OF_PAGE_IN_FW_QUEUE_CMD 0x4
+#define NUM_OF_PAGE_IN_FW_QUEUE_MGNT 0x20
+#define NUM_OF_PAGE_IN_FW_QUEUE_HIGH 0
+#define NUM_OF_PAGE_IN_FW_QUEUE_BCN 0x4
+#define NUM_OF_PAGE_IN_FW_QUEUE_PUB 0x18
+
+#endif
+
+#define APPLIED_RESERVED_QUEUE_IN_FW 0x80000000
+#define RSVD_FW_QUEUE_PAGE_BK_SHIFT 0x00
+#define RSVD_FW_QUEUE_PAGE_BE_SHIFT 0x08
+#define RSVD_FW_QUEUE_PAGE_VI_SHIFT 0x10
+#define RSVD_FW_QUEUE_PAGE_VO_SHIFT 0x18
+#define RSVD_FW_QUEUE_PAGE_MGNT_SHIFT 0x10
+#define RSVD_FW_QUEUE_PAGE_CMD_SHIFT 0x08
+#define RSVD_FW_QUEUE_PAGE_BCN_SHIFT 0x00
+#define RSVD_FW_QUEUE_PAGE_PUB_SHIFT 0x08
+
+/*
+ * =================================================================
+ * =================================================================
+ */
+
+#define EPROM_93c46 0
+#define EPROM_93c56 1
+
+#define DEFAULT_FRAG_THRESHOLD 2342U
+#define MIN_FRAG_THRESHOLD 256U
+#define DEFAULT_BEACONINTERVAL 0x64U
+#define DEFAULT_BEACON_ESSID "Rtl819xU"
+
+#define DEFAULT_SSID ""
+#define DEFAULT_RETRY_RTS 7
+#define DEFAULT_RETRY_DATA 7
+#define PRISM_HDR_SIZE 64
+
+#define PHY_RSSI_SLID_WIN_MAX 100
+
+
+typedef enum _WIRELESS_MODE {
+ WIRELESS_MODE_UNKNOWN = 0x00,
+ WIRELESS_MODE_A = 0x01,
+ WIRELESS_MODE_B = 0x02,
+ WIRELESS_MODE_G = 0x04,
+ WIRELESS_MODE_AUTO = 0x08,
+ WIRELESS_MODE_N_24G = 0x10,
+ WIRELESS_MODE_N_5G = 0x20
+} WIRELESS_MODE;
+
+
+#define RTL_IOCTL_WPA_SUPPLICANT (SIOCIWFIRSTPRIV + 30)
+
+typedef struct buffer {
+ struct buffer *next;
+ u32 *buf;
+
+} buffer;
+
+typedef struct rtl_reg_debug {
+ unsigned int cmd;
+ struct {
+ unsigned char type;
+ unsigned char addr;
+ unsigned char page;
+ unsigned char length;
+ } head;
+ unsigned char buf[0xff];
+} rtl_reg_debug;
+
+
+
+
+
+
+typedef struct _rt_9x_tx_rate_history {
+ u32 cck[4];
+ u32 ofdm[8];
+ u32 ht_mcs[4][16];
+} rt_tx_rahis_t, *prt_tx_rahis_t;
+typedef struct _RT_SMOOTH_DATA_4RF {
+ char elements[4][100]; /* array to store values */
+ u32 index; /* index to current array to store */
+ u32 TotalNum; /* num of valid elements */
+ u32 TotalVal[4]; /* sum of valid elements */
+} RT_SMOOTH_DATA_4RF, *PRT_SMOOTH_DATA_4RF;
+
+/* This maybe changed for D-cut larger aggregation size */
+#define MAX_8192U_RX_SIZE 8192
+/* Stats seems messed up, clean it ASAP */
+typedef struct Stats {
+ unsigned long txrdu;
+ unsigned long rxok;
+ unsigned long rxframgment;
+ unsigned long rxurberr;
+ unsigned long rxstaterr;
+ /* 0: Total, 1: OK, 2: CRC, 3: ICV */
+ unsigned long received_rate_histogram[4][32];
+ /* 0: Long preamble/GI, 1: Short preamble/GI */
+ unsigned long received_preamble_GI[2][32];
+ /* level: (<4K), (4K~8K), (8K~16K), (16K~32K), (32K~64K) */
+ unsigned long rx_AMPDUsize_histogram[5];
+ /* level: (<5), (5~10), (10~20), (20~40), (>40) */
+ unsigned long rx_AMPDUnum_histogram[5];
+ unsigned long numpacket_matchbssid;
+ unsigned long numpacket_toself;
+ unsigned long num_process_phyinfo;
+ unsigned long numqry_phystatus;
+ unsigned long numqry_phystatusCCK;
+ unsigned long numqry_phystatusHT;
+ /* 0: 20M, 1: funn40M, 2: upper20M, 3: lower20M, 4: duplicate */
+ unsigned long received_bwtype[5];
+ unsigned long txnperr;
+ unsigned long txnpdrop;
+ unsigned long txresumed;
+ unsigned long txnpokint;
+ unsigned long txoverflow;
+ unsigned long txlpokint;
+ unsigned long txlpdrop;
+ unsigned long txlperr;
+ unsigned long txbeokint;
+ unsigned long txbedrop;
+ unsigned long txbeerr;
+ unsigned long txbkokint;
+ unsigned long txbkdrop;
+ unsigned long txbkerr;
+ unsigned long txviokint;
+ unsigned long txvidrop;
+ unsigned long txvierr;
+ unsigned long txvookint;
+ unsigned long txvodrop;
+ unsigned long txvoerr;
+ unsigned long txbeaconokint;
+ unsigned long txbeacondrop;
+ unsigned long txbeaconerr;
+ unsigned long txmanageokint;
+ unsigned long txmanagedrop;
+ unsigned long txmanageerr;
+ unsigned long txdatapkt;
+ unsigned long txfeedback;
+ unsigned long txfeedbackok;
+
+ unsigned long txoktotal;
+ unsigned long txokbytestotal;
+ unsigned long txokinperiod;
+ unsigned long txmulticast;
+ unsigned long txbytesmulticast;
+ unsigned long txbroadcast;
+ unsigned long txbytesbroadcast;
+ unsigned long txunicast;
+ unsigned long txbytesunicast;
+
+ unsigned long rxoktotal;
+ unsigned long rxbytesunicast;
+ unsigned long txfeedbackfail;
+ unsigned long txerrtotal;
+ unsigned long txerrbytestotal;
+ unsigned long txerrmulticast;
+ unsigned long txerrbroadcast;
+ unsigned long txerrunicast;
+ unsigned long txretrycount;
+ unsigned long txfeedbackretry;
+ u8 last_packet_rate;
+ unsigned long slide_signal_strength[100];
+ unsigned long slide_evm[100];
+ /* For recording sliding window's RSSI value */
+ unsigned long slide_rssi_total;
+ /* For recording sliding window's EVM value */
+ unsigned long slide_evm_total;
+ /* Transformed in dbm. Beautified signal strength for UI, not correct */
+ long signal_strength;
+ long signal_quality;
+ long last_signal_strength_inpercent;
+ /* Correct smoothed ss in dbm, only used in driver
+ * to report real power now */
+ long recv_signal_power;
+ u8 rx_rssi_percentage[4];
+ u8 rx_evm_percentage[2];
+ long rxSNRdB[4];
+ rt_tx_rahis_t txrate;
+ /* For beacon RSSI */
+ u32 Slide_Beacon_pwdb[100];
+ u32 Slide_Beacon_Total;
+ RT_SMOOTH_DATA_4RF cck_adc_pwdb;
+
+ u32 CurrentShowTxate;
+} Stats;
+
+
+/* Bandwidth Offset */
+#define HAL_PRIME_CHNL_OFFSET_DONT_CARE 0
+#define HAL_PRIME_CHNL_OFFSET_LOWER 1
+#define HAL_PRIME_CHNL_OFFSET_UPPER 2
+
+
+typedef struct ChnlAccessSetting {
+ u16 SIFS_Timer;
+ u16 DIFS_Timer;
+ u16 SlotTimeTimer;
+ u16 EIFS_Timer;
+ u16 CWminIndex;
+ u16 CWmaxIndex;
+} *PCHANNEL_ACCESS_SETTING, CHANNEL_ACCESS_SETTING;
+
+typedef struct _BB_REGISTER_DEFINITION {
+ /* set software control: 0x870~0x877 [8 bytes] */
+ u32 rfintfs;
+ /* readback data: 0x8e0~0x8e7 [8 bytes] */
+ u32 rfintfi;
+ /* output data: 0x860~0x86f [16 bytes] */
+ u32 rfintfo;
+ /* output enable: 0x860~0x86f [16 bytes] */
+ u32 rfintfe;
+ /* LSSI data: 0x840~0x84f [16 bytes] */
+ u32 rf3wireOffset;
+ /* BB Band Select: 0x878~0x87f [8 bytes] */
+ u32 rfLSSI_Select;
+ /* Tx gain stage: 0x80c~0x80f [4 bytes] */
+ u32 rfTxGainStage;
+ /* wire parameter control1: 0x820~0x823, 0x828~0x82b,
+ * 0x830~0x833, 0x838~0x83b [16 bytes] */
+ u32 rfHSSIPara1;
+ /* wire parameter control2: 0x824~0x827, 0x82c~0x82f,
+ * 0x834~0x837, 0x83c~0x83f [16 bytes] */
+ u32 rfHSSIPara2;
+ /* Tx Rx antenna control: 0x858~0x85f [16 bytes] */
+ u32 rfSwitchControl;
+ /* AGC parameter control1: 0xc50~0xc53, 0xc58~0xc5b,
+ * 0xc60~0xc63, 0xc68~0xc6b [16 bytes] */
+ u32 rfAGCControl1;
+ /* AGC parameter control2: 0xc54~0xc57, 0xc5c~0xc5f,
+ * 0xc64~0xc67, 0xc6c~0xc6f [16 bytes] */
+ u32 rfAGCControl2;
+ /* OFDM Rx IQ imbalance matrix: 0xc14~0xc17, 0xc1c~0xc1f,
+ * 0xc24~0xc27, 0xc2c~0xc2f [16 bytes] */
+ u32 rfRxIQImbalance;
+ /* Rx IQ DC offset and Rx digital filter, Rx DC notch filter:
+ * 0xc10~0xc13, 0xc18~0xc1b,
+ * 0xc20~0xc23, 0xc28~0xc2b [16 bytes] */
+ u32 rfRxAFE;
+ /* OFDM Tx IQ imbalance matrix: 0xc80~0xc83, 0xc88~0xc8b,
+ * 0xc90~0xc93, 0xc98~0xc9b [16 bytes] */
+ u32 rfTxIQImbalance;
+ /* Tx IQ DC Offset and Tx DFIR type:
+ * 0xc84~0xc87, 0xc8c~0xc8f,
+ * 0xc94~0xc97, 0xc9c~0xc9f [16 bytes] */
+ u32 rfTxAFE;
+ /* LSSI RF readback data: 0x8a0~0x8af [16 bytes] */
+ u32 rfLSSIReadBack;
+} BB_REGISTER_DEFINITION_T, *PBB_REGISTER_DEFINITION_T;
+
+typedef enum _RT_RF_TYPE_819xU {
+ RF_TYPE_MIN = 0,
+ RF_8225,
+ RF_8256,
+ RF_8258,
+ RF_PSEUDO_11N = 4,
+} RT_RF_TYPE_819xU, *PRT_RF_TYPE_819xU;
+
+typedef struct _rate_adaptive {
+ u8 rate_adaptive_disabled;
+ u8 ratr_state;
+ u16 reserve;
+
+ u32 high_rssi_thresh_for_ra;
+ u32 high2low_rssi_thresh_for_ra;
+ u8 low2high_rssi_thresh_for_ra40M;
+ u32 low_rssi_thresh_for_ra40M;
+ u8 low2high_rssi_thresh_for_ra20M;
+ u32 low_rssi_thresh_for_ra20M;
+ u32 upper_rssi_threshold_ratr;
+ u32 middle_rssi_threshold_ratr;
+ u32 low_rssi_threshold_ratr;
+ u32 low_rssi_threshold_ratr_40M;
+ u32 low_rssi_threshold_ratr_20M;
+ u8 ping_rssi_enable;
+ u32 ping_rssi_ratr;
+ u32 ping_rssi_thresh_for_ra;
+ u32 last_ratr;
+
+} rate_adaptive, *prate_adaptive;
+
+#define TxBBGainTableLength 37
+#define CCKTxBBGainTableLength 23
+
+typedef struct _txbbgain_struct {
+ long txbb_iq_amplifygain;
+ u32 txbbgain_value;
+} txbbgain_struct, *ptxbbgain_struct;
+
+typedef struct _ccktxbbgain_struct {
+ /* The value is from a22 to a29, one byte one time is much safer */
+ u8 ccktxbb_valuearray[8];
+} ccktxbbgain_struct, *pccktxbbgain_struct;
+
+
+typedef struct _init_gain {
+ u8 xaagccore1;
+ u8 xbagccore1;
+ u8 xcagccore1;
+ u8 xdagccore1;
+ u8 cca;
+
+} init_gain, *pinit_gain;
+
+typedef struct _phy_ofdm_rx_status_report_819xusb {
+ u8 trsw_gain_X[4];
+ u8 pwdb_all;
+ u8 cfosho_X[4];
+ u8 cfotail_X[4];
+ u8 rxevm_X[2];
+ u8 rxsnr_X[4];
+ u8 pdsnr_X[2];
+ u8 csi_current_X[2];
+ u8 csi_target_X[2];
+ u8 sigevm;
+ u8 max_ex_pwr;
+ u8 sgi_en;
+ u8 rxsc_sgien_exflg;
+} phy_sts_ofdm_819xusb_t;
+
+typedef struct _phy_cck_rx_status_report_819xusb {
+ /* For CCK rate descriptor. This is an unsigned 8:1 variable.
+ * LSB bit presend 0.5. And MSB 7 bts presend a signed value.
+ * Range from -64~+63.5. */
+ u8 adc_pwdb_X[4];
+ u8 sq_rpt;
+ u8 cck_agc_rpt;
+} phy_sts_cck_819xusb_t;
+
+
+typedef struct _phy_ofdm_rx_status_rxsc_sgien_exintfflag {
+ u8 reserved:4;
+ u8 rxsc:2;
+ u8 sgi_en:1;
+ u8 ex_intf_flag:1;
+} phy_ofdm_rx_status_rxsc_sgien_exintfflag;
+
+typedef enum _RT_CUSTOMER_ID {
+ RT_CID_DEFAULT = 0,
+ RT_CID_8187_ALPHA0 = 1,
+ RT_CID_8187_SERCOMM_PS = 2,
+ RT_CID_8187_HW_LED = 3,
+ RT_CID_8187_NETGEAR = 4,
+ RT_CID_WHQL = 5,
+ RT_CID_819x_CAMEO = 6,
+ RT_CID_819x_RUNTOP = 7,
+ RT_CID_819x_Senao = 8,
+ RT_CID_TOSHIBA = 9,
+ RT_CID_819x_Netcore = 10,
+ RT_CID_Nettronix = 11,
+ RT_CID_DLINK = 12,
+ RT_CID_PRONET = 13,
+} RT_CUSTOMER_ID, *PRT_CUSTOMER_ID;
+
+/*
+ * ==========================================================================
+ * LED customization.
+ * ==========================================================================
+ */
+
+typedef enum _LED_STRATEGY_8190 {
+ SW_LED_MODE0, /* SW control 1 LED via GPIO0. It is default option. */
+ SW_LED_MODE1, /* SW control for PCI Express */
+ SW_LED_MODE2, /* SW control for Cameo. */
+ SW_LED_MODE3, /* SW control for RunTop. */
+ SW_LED_MODE4, /* SW control for Netcore. */
+ /* HW control 2 LEDs, LED0 and LED1 (4 different control modes) */
+ HW_LED,
+} LED_STRATEGY_8190, *PLED_STRATEGY_8190;
+
+typedef enum _RESET_TYPE {
+ RESET_TYPE_NORESET = 0x00,
+ RESET_TYPE_NORMAL = 0x01,
+ RESET_TYPE_SILENT = 0x02
+} RESET_TYPE;
+
+/* The simple tx command OP code. */
+typedef enum _tag_TxCmd_Config_Index {
+ TXCMD_TXRA_HISTORY_CTRL = 0xFF900000,
+ TXCMD_RESET_TX_PKT_BUFF = 0xFF900001,
+ TXCMD_RESET_RX_PKT_BUFF = 0xFF900002,
+ TXCMD_SET_TX_DURATION = 0xFF900003,
+ TXCMD_SET_RX_RSSI = 0xFF900004,
+ TXCMD_SET_TX_PWR_TRACKING = 0xFF900005,
+ TXCMD_XXXX_CTRL,
+} DCMD_TXCMD_OP;
+
+typedef struct r8192_priv {
+ struct usb_device *udev;
+ /* For maintain info from eeprom */
+ short epromtype;
+ u16 eeprom_vid;
+ u16 eeprom_pid;
+ u8 eeprom_CustomerID;
+ u8 eeprom_ChannelPlan;
+ RT_CUSTOMER_ID CustomerID;
+ LED_STRATEGY_8190 LedStrategy;
+ u8 txqueue_to_outpipemap[9];
+ int irq;
+ struct ieee80211_device *ieee80211;
+
+ /* O: rtl8192, 1: rtl8185 V B/C, 2: rtl8185 V D */
+ short card_8192;
+ /* If TCR reports card V B/C, this discriminates */
+ u8 card_8192_version;
+ short enable_gpio0;
+ enum card_type {
+ PCI, MINIPCI, CARDBUS, USB
+ } card_type;
+ short hw_plcp_len;
+ short plcp_preamble_mode;
+
+ spinlock_t irq_lock;
+ spinlock_t tx_lock;
+ struct mutex mutex;
+
+ u16 irq_mask;
+ short chan;
+ short sens;
+ short max_sens;
+
+
+ short up;
+ /* If 1, allow bad crc frame, reception in monitor mode */
+ short crcmon;
+
+ struct semaphore wx_sem;
+ struct semaphore rf_sem; /* Used to lock rf write operation */
+
+ u8 rf_type; /* 0: 1T2R, 1: 2T4R */
+ RT_RF_TYPE_819xU rf_chip;
+
+ short (*rf_set_sens)(struct net_device *dev, short sens);
+ u8 (*rf_set_chan)(struct net_device *dev, u8 ch);
+ void (*rf_close)(struct net_device *dev);
+ void (*rf_init)(struct net_device *dev);
+ short promisc;
+ /* Stats */
+ struct Stats stats;
+ struct iw_statistics wstats;
+
+ /* RX stuff */
+ struct urb **rx_urb;
+ struct urb **rx_cmd_urb;
+#ifdef THOMAS_BEACON
+ u32 *oldaddr;
+#endif
+#ifdef THOMAS_TASKLET
+ atomic_t irt_counter; /* count for irq_rx_tasklet */
+#endif
+#ifdef JACKSON_NEW_RX
+ struct sk_buff **pp_rxskb;
+ int rx_inx;
+#endif
+
+ struct sk_buff_head rx_queue;
+ struct sk_buff_head skb_queue;
+ struct work_struct qos_activate;
+ short tx_urb_index;
+ atomic_t tx_pending[0x10]; /* UART_PRIORITY + 1 */
+
+
+ struct tasklet_struct irq_rx_tasklet;
+ struct urb *rxurb_task;
+
+ /* Tx Related variables */
+ u16 ShortRetryLimit;
+ u16 LongRetryLimit;
+ u32 TransmitConfig;
+ u8 RegCWinMin; /* For turbo mode CW adaptive */
+
+ u32 LastRxDescTSFHigh;
+ u32 LastRxDescTSFLow;
+
+
+ /* Rx Related variables */
+ u16 EarlyRxThreshold;
+ u32 ReceiveConfig;
+ u8 AcmControl;
+
+ u8 RFProgType;
+
+ u8 retry_data;
+ u8 retry_rts;
+ u16 rts;
+
+ struct ChnlAccessSetting ChannelAccessSetting;
+ struct work_struct reset_wq;
+
+/**********************************************************/
+ /* For rtl819xUsb */
+ u16 basic_rate;
+ u8 short_preamble;
+ u8 slot_time;
+ bool bDcut;
+ bool bCurrentRxAggrEnable;
+ u8 Rf_Mode; /* For Firmware RF -R/W switch */
+ prt_firmware pFirmware;
+ rtl819xUsb_loopback_e LoopbackMode;
+ u16 EEPROMTxPowerDiff;
+ u8 EEPROMThermalMeter;
+ u8 EEPROMPwDiff;
+ u8 EEPROMCrystalCap;
+ u8 EEPROM_Def_Ver;
+ u8 EEPROMTxPowerLevelCCK; /* CCK channel 1~14 */
+ u8 EEPROMTxPowerLevelCCK_V1[3];
+ u8 EEPROMTxPowerLevelOFDM24G[3]; /* OFDM 2.4G channel 1~14 */
+ u8 EEPROMTxPowerLevelOFDM5G[24]; /* OFDM 5G */
+
+ /* PHY related */
+ BB_REGISTER_DEFINITION_T PHYRegDef[4]; /* Radio A/B/C/D */
+ /* Read/write are allow for following hardware information variables */
+ u32 MCSTxPowerLevelOriginalOffset[6];
+ u32 CCKTxPowerLevelOriginalOffset;
+ u8 TxPowerLevelCCK[14]; /* CCK channel 1~14 */
+ u8 TxPowerLevelOFDM24G[14]; /* OFDM 2.4G channel 1~14 */
+ u8 TxPowerLevelOFDM5G[14]; /* OFDM 5G */
+ u32 Pwr_Track;
+ u8 TxPowerDiff;
+ u8 AntennaTxPwDiff[2]; /* Antenna gain offset, 0: B, 1: C, 2: D */
+ u8 CrystalCap;
+ u8 ThermalMeter[2]; /* index 0: RFIC0, index 1: RFIC1 */
+
+ u8 CckPwEnl;
+ /* Use to calculate PWBD */
+ u8 bCckHighPower;
+ long undecorated_smoothed_pwdb;
+
+ /* For set channel */
+ u8 SwChnlInProgress;
+ u8 SwChnlStage;
+ u8 SwChnlStep;
+ u8 SetBWModeInProgress;
+ HT_CHANNEL_WIDTH CurrentChannelBW;
+ u8 ChannelPlan;
+ /* 8190 40MHz mode */
+ /* Control channel sub-carrier */
+ u8 nCur40MhzPrimeSC;
+ /* Test for shorten RF configuration time.
+ * We save RF reg0 in this variable to reduce RF reading. */
+ u32 RfReg0Value[4];
+ u8 NumTotalRFPath;
+ bool brfpath_rxenable[4];
+ /* RF set related */
+ bool SetRFPowerStateInProgress;
+ struct timer_list watch_dog_timer;
+
+ /* For dynamic mechanism */
+ /* Tx Power Control for Near/Far Range */
+ bool bdynamic_txpower;
+ bool bDynamicTxHighPower;
+ bool bDynamicTxLowPower;
+ bool bLastDTPFlag_High;
+ bool bLastDTPFlag_Low;
+
+ bool bstore_last_dtpflag;
+ /* Define to discriminate on High power State or
+ * on sitesurvey to change Tx gain index */
+ bool bstart_txctrl_bydtp;
+ rate_adaptive rate_adaptive;
+ /* TX power tracking
+ * OPEN/CLOSE TX POWER TRACKING */
+ txbbgain_struct txbbgain_table[TxBBGainTableLength];
+ u8 txpower_count; /* For 6 sec do tracking again */
+ bool btxpower_trackingInit;
+ u8 OFDM_index;
+ u8 CCK_index;
+ /* CCK TX Power Tracking */
+ ccktxbbgain_struct cck_txbbgain_table[CCKTxBBGainTableLength];
+ ccktxbbgain_struct cck_txbbgain_ch14_table[CCKTxBBGainTableLength];
+ u8 rfa_txpowertrackingindex;
+ u8 rfa_txpowertrackingindex_real;
+ u8 rfa_txpowertracking_default;
+ u8 rfc_txpowertrackingindex;
+ u8 rfc_txpowertrackingindex_real;
+
+ s8 cck_present_attentuation;
+ u8 cck_present_attentuation_20Mdefault;
+ u8 cck_present_attentuation_40Mdefault;
+ char cck_present_attentuation_difference;
+ bool btxpower_tracking;
+ bool bcck_in_ch14;
+ bool btxpowerdata_readfromEEPORM;
+ u16 TSSI_13dBm;
+ init_gain initgain_backup;
+ u8 DefaultInitialGain[4];
+ /* For EDCA Turbo mode */
+ bool bis_any_nonbepkts;
+ bool bcurrent_turbo_EDCA;
+ bool bis_cur_rdlstate;
+ struct timer_list fsync_timer;
+ bool bfsync_processing; /* 500ms Fsync timer is active or not */
+ u32 rate_record;
+ u32 rateCountDiffRecord;
+ u32 ContinueDiffCount;
+ bool bswitch_fsync;
+
+ u8 framesync;
+ u32 framesyncC34;
+ u8 framesyncMonitor;
+ u16 nrxAMPDU_size;
+ u8 nrxAMPDU_aggr_num;
+
+ /* For gpio */
+ bool bHwRadioOff;
+
+ u32 reset_count;
+ bool bpbc_pressed;
+ u32 txpower_checkcnt;
+ u32 txpower_tracking_callback_cnt;
+ u8 thermal_read_val[40];
+ u8 thermal_readback_index;
+ u32 ccktxpower_adjustcnt_not_ch14;
+ u32 ccktxpower_adjustcnt_ch14;
+ u8 tx_fwinfo_force_subcarriermode;
+ u8 tx_fwinfo_force_subcarrierval;
+ /* For silent reset */
+ RESET_TYPE ResetProgress;
+ bool bForcedSilentReset;
+ bool bDisableNormalResetCheck;
+ u16 TxCounter;
+ u16 RxCounter;
+ int IrpPendingCount;
+ bool bResetInProgress;
+ bool force_reset;
+ u8 InitialGainOperateType;
+
+ u16 SifsTime;
+
+ /* Define work item */
+
+ struct delayed_work update_beacon_wq;
+ struct delayed_work watch_dog_wq;
+ struct delayed_work txpower_tracking_wq;
+ struct delayed_work rfpath_check_wq;
+ struct delayed_work gpio_change_rf_wq;
+ struct delayed_work initialgain_operate_wq;
+ struct workqueue_struct *priv_wq;
+} r8192_priv;
+
+/* For rtl8187B */
+typedef enum{
+ BULK_PRIORITY = 0x01,
+ LOW_PRIORITY,
+ NORM_PRIORITY,
+ VO_PRIORITY,
+ VI_PRIORITY,
+ BE_PRIORITY,
+ BK_PRIORITY,
+ RSVD2,
+ RSVD3,
+ BEACON_PRIORITY,
+ HIGH_PRIORITY,
+ MANAGE_PRIORITY,
+ RSVD4,
+ RSVD5,
+ UART_PRIORITY
+} priority_t;
+
+typedef enum {
+ NIC_8192U = 1,
+ NIC_8190P = 2,
+ NIC_8192E = 3,
+} nic_t;
+
+bool init_firmware(struct net_device *dev);
+short rtl819xU_tx_cmd(struct net_device *dev, struct sk_buff *skb);
+short rtl8192_tx(struct net_device *dev, struct sk_buff *skb);
+
+u32 read_cam(struct net_device *dev, u8 addr);
+void write_cam(struct net_device *dev, u8 addr, u32 data);
+
+int read_nic_byte(struct net_device *dev, int x, u8 *data);
+int read_nic_byte_E(struct net_device *dev, int x, u8 *data);
+int read_nic_dword(struct net_device *dev, int x, u32 *data);
+int read_nic_word(struct net_device *dev, int x, u16 *data);
+void write_nic_byte(struct net_device *dev, int x, u8 y);
+void write_nic_byte_E(struct net_device *dev, int x, u8 y);
+void write_nic_word(struct net_device *dev, int x, u16 y);
+void write_nic_dword(struct net_device *dev, int x, u32 y);
+void force_pci_posting(struct net_device *dev);
+
+void rtl8192_rtx_disable(struct net_device *);
+void rtl8192_rx_enable(struct net_device *);
+void rtl8192_tx_enable(struct net_device *);
+
+void rtl8192_disassociate(struct net_device *dev);
+void rtl8185_set_rf_pins_enable(struct net_device *dev, u32 a);
+
+void rtl8192_set_anaparam(struct net_device *dev, u32 a);
+void rtl8185_set_anaparam2(struct net_device *dev, u32 a);
+void rtl8192_update_msr(struct net_device *dev);
+int rtl8192_down(struct net_device *dev);
+int rtl8192_up(struct net_device *dev);
+void rtl8192_commit(struct net_device *dev);
+void rtl8192_set_chan(struct net_device *dev, short ch);
+void write_phy(struct net_device *dev, u8 adr, u8 data);
+void write_phy_cck(struct net_device *dev, u8 adr, u32 data);
+void write_phy_ofdm(struct net_device *dev, u8 adr, u32 data);
+void rtl8185_tx_antenna(struct net_device *dev, u8 ant);
+void rtl8192_set_rxconf(struct net_device *dev);
+extern void rtl819xusb_beacon_tx(struct net_device *dev, u16 tx_rate);
+
+void EnableHWSecurityConfig8192(struct net_device *dev);
+void setKey(struct net_device *dev, u8 EntryNo, u8 KeyIndex, u16 KeyType, u8 *MacAddr, u8 DefaultKey, u32 *KeyContent);
+
+
+#endif
diff --git a/drivers/staging/rtl8192u/r8192U_core.c b/drivers/staging/rtl8192u/r8192U_core.c
new file mode 100644
index 000000000..a4795afee
--- /dev/null
+++ b/drivers/staging/rtl8192u/r8192U_core.c
@@ -0,0 +1,4915 @@
+/******************************************************************************
+ * Copyright(c) 2008 - 2010 Realtek Corporation. All rights reserved.
+ * Linux device driver for RTL8192U
+ *
+ * Based on the r8187 driver, which is:
+ * Copyright 2004-2005 Andrea Merello <andrea.merello@gmail.com>, et al.
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ * The full GNU General Public License is included in this distribution in the
+ * file called LICENSE.
+ *
+ * Contact Information:
+ * Jerry chuang <wlanfae@realtek.com>
+ */
+
+#ifndef CONFIG_FORCE_HARD_FLOAT
+double __floatsidf(int i)
+{
+ return i;
+}
+
+unsigned int __fixunsdfsi(double d)
+{
+ return d;
+}
+
+double __adddf3(double a, double b)
+{
+ return a+b;
+}
+
+double __addsf3(float a, float b)
+{
+ return a+b;
+}
+
+double __subdf3(double a, double b)
+{
+ return a-b;
+}
+
+double __extendsfdf2(float a)
+{
+ return a;
+}
+#endif
+
+#define CONFIG_RTL8192_IO_MAP
+
+#include <asm/uaccess.h>
+#include "r8192U_hw.h"
+#include "r8192U.h"
+#include "r8190_rtl8256.h" /* RTL8225 Radio frontend */
+#include "r8180_93cx6.h" /* Card EEPROM */
+#include "r8192U_wx.h"
+#include "r819xU_phy.h" //added by WB 4.30.2008
+#include "r819xU_phyreg.h"
+#include "r819xU_cmdpkt.h"
+#include "r8192U_dm.h"
+#include <linux/usb.h>
+#include <linux/slab.h>
+#include <linux/proc_fs.h>
+#include <linux/seq_file.h>
+// FIXME: check if 2.6.7 is ok
+
+#include "dot11d.h"
+//set here to open your trace code. //WB
+u32 rt_global_debug_component = COMP_DOWN |
+ COMP_SEC |
+ COMP_ERR; //always open err flags on
+
+#define TOTAL_CAM_ENTRY 32
+#define CAM_CONTENT_COUNT 8
+
+static const struct usb_device_id rtl8192_usb_id_tbl[] = {
+ /* Realtek */
+ {USB_DEVICE(0x0bda, 0x8709)},
+ /* Corega */
+ {USB_DEVICE(0x07aa, 0x0043)},
+ /* Belkin */
+ {USB_DEVICE(0x050d, 0x805E)},
+ /* Sitecom */
+ {USB_DEVICE(0x0df6, 0x0031)},
+ /* EnGenius */
+ {USB_DEVICE(0x1740, 0x9201)},
+ /* Dlink */
+ {USB_DEVICE(0x2001, 0x3301)},
+ /* Zinwell */
+ {USB_DEVICE(0x5a57, 0x0290)},
+ /* LG */
+ {USB_DEVICE(0x043e, 0x7a01)},
+ {}
+};
+
+MODULE_LICENSE("GPL");
+MODULE_VERSION("V 1.1");
+MODULE_DEVICE_TABLE(usb, rtl8192_usb_id_tbl);
+MODULE_DESCRIPTION("Linux driver for Realtek RTL8192 USB WiFi cards");
+
+static char *ifname = "wlan%d";
+static int hwwep = 1; //default use hw. set 0 to use software security
+static int channels = 0x3fff;
+
+
+
+module_param(ifname, charp, S_IRUGO|S_IWUSR);
+module_param(hwwep, int, S_IRUGO|S_IWUSR);
+module_param(channels, int, S_IRUGO|S_IWUSR);
+
+MODULE_PARM_DESC(ifname, " Net interface name, wlan%d=default");
+MODULE_PARM_DESC(hwwep, " Try to use hardware security support. ");
+MODULE_PARM_DESC(channels, " Channel bitmask for specific locales. NYI");
+
+static int rtl8192_usb_probe(struct usb_interface *intf,
+ const struct usb_device_id *id);
+static void rtl8192_usb_disconnect(struct usb_interface *intf);
+
+
+static struct usb_driver rtl8192_usb_driver = {
+ .name = RTL819xU_MODULE_NAME, /* Driver name */
+ .id_table = rtl8192_usb_id_tbl, /* PCI_ID table */
+ .probe = rtl8192_usb_probe, /* probe fn */
+ .disconnect = rtl8192_usb_disconnect, /* remove fn */
+ .suspend = NULL, /* PM suspend fn */
+ .resume = NULL, /* PM resume fn */
+};
+
+
+struct CHANNEL_LIST {
+ u8 Channel[32];
+ u8 Len;
+};
+
+static struct CHANNEL_LIST ChannelPlan[] = {
+ {{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 36, 40, 44, 48, 52, 56, 60, 64, 149, 153, 157, 161, 165}, 24}, //FCC
+ {{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}, 11}, //IC
+ {{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 36, 40, 44, 48, 52, 56, 60, 64}, 21}, //ETSI
+ {{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13}, 13}, //Spain. Change to ETSI.
+ {{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13}, 13}, //France. Change to ETSI.
+ {{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 36, 40, 44, 48, 52, 56, 60, 64}, 22}, //MKK //MKK
+ {{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 36, 40, 44, 48, 52, 56, 60, 64}, 22},//MKK1
+ {{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13}, 13}, //Israel.
+ {{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 36, 40, 44, 48, 52, 56, 60, 64}, 22}, // For 11a , TELEC
+ {{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 36, 40, 44, 48, 52, 56, 60, 64}, 22}, //MIC
+ {{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14}, 14} //For Global Domain. 1-11:active scan, 12-14 passive scan. //+YJ, 080626
+};
+
+static void rtl819x_set_channel_map(u8 channel_plan, struct r8192_priv *priv)
+{
+ int i, max_chan = -1, min_chan = -1;
+ struct ieee80211_device *ieee = priv->ieee80211;
+ switch (channel_plan) {
+ case COUNTRY_CODE_FCC:
+ case COUNTRY_CODE_IC:
+ case COUNTRY_CODE_ETSI:
+ case COUNTRY_CODE_SPAIN:
+ case COUNTRY_CODE_FRANCE:
+ case COUNTRY_CODE_MKK:
+ case COUNTRY_CODE_MKK1:
+ case COUNTRY_CODE_ISRAEL:
+ case COUNTRY_CODE_TELEC:
+ case COUNTRY_CODE_MIC:
+ Dot11d_Init(ieee);
+ ieee->bGlobalDomain = false;
+ //actually 8225 & 8256 rf chips only support B,G,24N mode
+ if ((priv->rf_chip == RF_8225) || (priv->rf_chip == RF_8256)) {
+ min_chan = 1;
+ max_chan = 14;
+ } else {
+ RT_TRACE(COMP_ERR, "unknown rf chip, can't set channel map in function:%s()\n", __func__);
+ }
+ if (ChannelPlan[channel_plan].Len != 0) {
+ // Clear old channel map
+ memset(GET_DOT11D_INFO(ieee)->channel_map, 0, sizeof(GET_DOT11D_INFO(ieee)->channel_map));
+ // Set new channel map
+ for (i = 0; i < ChannelPlan[channel_plan].Len; i++) {
+ if (ChannelPlan[channel_plan].Channel[i] < min_chan || ChannelPlan[channel_plan].Channel[i] > max_chan)
+ break;
+ GET_DOT11D_INFO(ieee)->channel_map[ChannelPlan[channel_plan].Channel[i]] = 1;
+ }
+ }
+ break;
+
+ case COUNTRY_CODE_GLOBAL_DOMAIN:
+ GET_DOT11D_INFO(ieee)->bEnabled = 0;//this flag enabled to follow 11d country IE setting, otherwise, it shall follow global domain settings.
+ Dot11d_Reset(ieee);
+ ieee->bGlobalDomain = true;
+ break;
+
+ default:
+ break;
+ }
+}
+
+
+
+
+static void CamResetAllEntry(struct net_device *dev)
+{
+ u32 ulcommand = 0;
+ //2004/02/11 In static WEP, OID_ADD_KEY or OID_ADD_WEP are set before STA associate to AP.
+ // However, ResetKey is called on OID_802_11_INFRASTRUCTURE_MODE and MlmeAssociateRequest
+ // In this condition, Cam can not be reset because upper layer will not set this static key again.
+ ulcommand |= BIT31|BIT30;
+ write_nic_dword(dev, RWCAM, ulcommand);
+
+}
+
+
+void write_cam(struct net_device *dev, u8 addr, u32 data)
+{
+ write_nic_dword(dev, WCAMI, data);
+ write_nic_dword(dev, RWCAM, BIT31|BIT16|(addr&0xff));
+}
+
+u32 read_cam(struct net_device *dev, u8 addr)
+{
+ u32 data;
+
+ write_nic_dword(dev, RWCAM, 0x80000000|(addr&0xff));
+ read_nic_dword(dev, 0xa8, &data);
+ return data;
+}
+
+void write_nic_byte_E(struct net_device *dev, int indx, u8 data)
+{
+ int status;
+ struct r8192_priv *priv = (struct r8192_priv *)ieee80211_priv(dev);
+ struct usb_device *udev = priv->udev;
+
+ status = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
+ RTL8187_REQ_SET_REGS, RTL8187_REQT_WRITE,
+ indx|0xfe00, 0, &data, 1, HZ / 2);
+
+ if (status < 0)
+ netdev_err(dev, "write_nic_byte_E TimeOut! status: %d\n", status);
+}
+
+int read_nic_byte_E(struct net_device *dev, int indx, u8 *data)
+{
+ int status;
+ struct r8192_priv *priv = (struct r8192_priv *)ieee80211_priv(dev);
+ struct usb_device *udev = priv->udev;
+
+ status = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0),
+ RTL8187_REQ_GET_REGS, RTL8187_REQT_READ,
+ indx|0xfe00, 0, data, 1, HZ / 2);
+
+ if (status < 0) {
+ netdev_err(dev, "%s failure status: %d\n", __func__, status);
+ return status;
+ }
+
+ return 0;
+}
+//as 92U has extend page from 4 to 16, so modify functions below.
+void write_nic_byte(struct net_device *dev, int indx, u8 data)
+{
+ int status;
+
+ struct r8192_priv *priv = (struct r8192_priv *)ieee80211_priv(dev);
+ struct usb_device *udev = priv->udev;
+
+ status = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
+ RTL8187_REQ_SET_REGS, RTL8187_REQT_WRITE,
+ (indx&0xff)|0xff00, (indx>>8)&0x0f, &data, 1, HZ / 2);
+
+ if (status < 0)
+ netdev_err(dev, "write_nic_byte TimeOut! status: %d\n", status);
+
+
+}
+
+
+void write_nic_word(struct net_device *dev, int indx, u16 data)
+{
+
+ int status;
+
+ struct r8192_priv *priv = (struct r8192_priv *)ieee80211_priv(dev);
+ struct usb_device *udev = priv->udev;
+
+ status = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
+ RTL8187_REQ_SET_REGS, RTL8187_REQT_WRITE,
+ (indx&0xff)|0xff00, (indx>>8)&0x0f, &data, 2, HZ / 2);
+
+ if (status < 0)
+ netdev_err(dev, "write_nic_word TimeOut! status: %d\n", status);
+
+}
+
+
+void write_nic_dword(struct net_device *dev, int indx, u32 data)
+{
+
+ int status;
+
+ struct r8192_priv *priv = (struct r8192_priv *)ieee80211_priv(dev);
+ struct usb_device *udev = priv->udev;
+
+ status = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
+ RTL8187_REQ_SET_REGS, RTL8187_REQT_WRITE,
+ (indx&0xff)|0xff00, (indx>>8)&0x0f, &data, 4, HZ / 2);
+
+
+ if (status < 0)
+ netdev_err(dev, "write_nic_dword TimeOut! status: %d\n", status);
+
+}
+
+
+
+int read_nic_byte(struct net_device *dev, int indx, u8 *data)
+{
+ int status;
+ struct r8192_priv *priv = (struct r8192_priv *)ieee80211_priv(dev);
+ struct usb_device *udev = priv->udev;
+
+ status = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0),
+ RTL8187_REQ_GET_REGS, RTL8187_REQT_READ,
+ (indx&0xff)|0xff00, (indx>>8)&0x0f, data, 1, HZ / 2);
+
+ if (status < 0) {
+ netdev_err(dev, "%s failure status: %d\n", __func__, status);
+ return status;
+ }
+
+ return 0;
+}
+
+
+
+int read_nic_word(struct net_device *dev, int indx, u16 *data)
+{
+ int status;
+ struct r8192_priv *priv = (struct r8192_priv *)ieee80211_priv(dev);
+ struct usb_device *udev = priv->udev;
+
+ status = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0),
+ RTL8187_REQ_GET_REGS, RTL8187_REQT_READ,
+ (indx&0xff)|0xff00, (indx>>8)&0x0f,
+ data, 2, HZ / 2);
+
+ if (status < 0) {
+ netdev_err(dev, "%s failure status: %d\n", __func__, status);
+ return status;
+ }
+
+ return 0;
+}
+
+static int read_nic_word_E(struct net_device *dev, int indx, u16 *data)
+{
+ int status;
+ struct r8192_priv *priv = (struct r8192_priv *)ieee80211_priv(dev);
+ struct usb_device *udev = priv->udev;
+
+ status = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0),
+ RTL8187_REQ_GET_REGS, RTL8187_REQT_READ,
+ indx|0xfe00, 0, data, 2, HZ / 2);
+
+ if (status < 0) {
+ netdev_err(dev, "%s failure status: %d\n", __func__, status);
+ return status;
+ }
+
+ return 0;
+}
+
+int read_nic_dword(struct net_device *dev, int indx, u32 *data)
+{
+ int status;
+
+ struct r8192_priv *priv = (struct r8192_priv *)ieee80211_priv(dev);
+ struct usb_device *udev = priv->udev;
+
+ status = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0),
+ RTL8187_REQ_GET_REGS, RTL8187_REQT_READ,
+ (indx&0xff)|0xff00, (indx>>8)&0x0f,
+ data, 4, HZ / 2);
+
+ if (status < 0) {
+ netdev_err(dev, "%s failure status: %d\n", __func__, status);
+ return status;
+ }
+
+ return 0;
+}
+
+/* u8 read_phy_cck(struct net_device *dev, u8 adr); */
+/* u8 read_phy_ofdm(struct net_device *dev, u8 adr); */
+/* this might still called in what was the PHY rtl8185/rtl8192 common code
+ * plans are to possibility turn it again in one common code...
+ */
+inline void force_pci_posting(struct net_device *dev)
+{
+}
+
+static struct net_device_stats *rtl8192_stats(struct net_device *dev);
+static void rtl8192_restart(struct work_struct *work);
+static void watch_dog_timer_callback(unsigned long data);
+
+/****************************************************************************
+ * -----------------------------PROCFS STUFF-------------------------
+*****************************************************************************
+ */
+
+static struct proc_dir_entry *rtl8192_proc;
+
+static int proc_get_stats_ap(struct seq_file *m, void *v)
+{
+ struct net_device *dev = m->private;
+ struct r8192_priv *priv = (struct r8192_priv *)ieee80211_priv(dev);
+ struct ieee80211_device *ieee = priv->ieee80211;
+ struct ieee80211_network *target;
+
+ list_for_each_entry(target, &ieee->network_list, list) {
+ const char *wpa = "non_WPA";
+ if (target->wpa_ie_len > 0 || target->rsn_ie_len > 0)
+ wpa = "WPA";
+
+ seq_printf(m, "%s %s\n", target->ssid, wpa);
+ }
+
+ return 0;
+}
+
+static int proc_get_registers(struct seq_file *m, void *v)
+{
+ struct net_device *dev = m->private;
+ int i, n, max = 0xff;
+ u8 byte_rd;
+
+ seq_puts(m, "\n####################page 0##################\n ");
+
+ for (n = 0; n <= max;) {
+ seq_printf(m, "\nD: %2x > ", n);
+
+ for (i = 0; i < 16 && n <= max; i++, n++) {
+ read_nic_byte(dev, 0x000|n, &byte_rd);
+ seq_printf(m, "%2x ", byte_rd);
+ }
+ }
+
+ seq_puts(m, "\n####################page 1##################\n ");
+ for (n = 0; n <= max;) {
+ seq_printf(m, "\nD: %2x > ", n);
+
+ for (i = 0; i < 16 && n <= max; i++, n++) {
+ read_nic_byte(dev, 0x100|n, &byte_rd);
+ seq_printf(m, "%2x ", byte_rd);
+ }
+ }
+
+ seq_puts(m, "\n####################page 3##################\n ");
+ for (n = 0; n <= max;) {
+ seq_printf(m, "\nD: %2x > ", n);
+
+ for (i = 0; i < 16 && n <= max; i++, n++) {
+ read_nic_byte(dev, 0x300|n, &byte_rd);
+ seq_printf(m, "%2x ", byte_rd);
+ }
+ }
+
+ seq_putc(m, '\n');
+ return 0;
+}
+
+static int proc_get_stats_tx(struct seq_file *m, void *v)
+{
+ struct net_device *dev = m->private;
+ struct r8192_priv *priv = (struct r8192_priv *)ieee80211_priv(dev);
+
+ seq_printf(m,
+ "TX VI priority ok int: %lu\n"
+ "TX VI priority error int: %lu\n"
+ "TX VO priority ok int: %lu\n"
+ "TX VO priority error int: %lu\n"
+ "TX BE priority ok int: %lu\n"
+ "TX BE priority error int: %lu\n"
+ "TX BK priority ok int: %lu\n"
+ "TX BK priority error int: %lu\n"
+ "TX MANAGE priority ok int: %lu\n"
+ "TX MANAGE priority error int: %lu\n"
+ "TX BEACON priority ok int: %lu\n"
+ "TX BEACON priority error int: %lu\n"
+ "TX queue resume: %lu\n"
+ "TX queue stopped?: %d\n"
+ "TX fifo overflow: %lu\n"
+ "TX VI queue: %d\n"
+ "TX VO queue: %d\n"
+ "TX BE queue: %d\n"
+ "TX BK queue: %d\n"
+ "TX VI dropped: %lu\n"
+ "TX VO dropped: %lu\n"
+ "TX BE dropped: %lu\n"
+ "TX BK dropped: %lu\n"
+ "TX total data packets %lu\n",
+ priv->stats.txviokint,
+ priv->stats.txvierr,
+ priv->stats.txvookint,
+ priv->stats.txvoerr,
+ priv->stats.txbeokint,
+ priv->stats.txbeerr,
+ priv->stats.txbkokint,
+ priv->stats.txbkerr,
+ priv->stats.txmanageokint,
+ priv->stats.txmanageerr,
+ priv->stats.txbeaconokint,
+ priv->stats.txbeaconerr,
+ priv->stats.txresumed,
+ netif_queue_stopped(dev),
+ priv->stats.txoverflow,
+ atomic_read(&(priv->tx_pending[VI_PRIORITY])),
+ atomic_read(&(priv->tx_pending[VO_PRIORITY])),
+ atomic_read(&(priv->tx_pending[BE_PRIORITY])),
+ atomic_read(&(priv->tx_pending[BK_PRIORITY])),
+ priv->stats.txvidrop,
+ priv->stats.txvodrop,
+ priv->stats.txbedrop,
+ priv->stats.txbkdrop,
+ priv->stats.txdatapkt
+ );
+
+ return 0;
+}
+
+static int proc_get_stats_rx(struct seq_file *m, void *v)
+{
+ struct net_device *dev = m->private;
+ struct r8192_priv *priv = (struct r8192_priv *)ieee80211_priv(dev);
+
+ seq_printf(m,
+ "RX packets: %lu\n"
+ "RX urb status error: %lu\n"
+ "RX invalid urb error: %lu\n",
+ priv->stats.rxoktotal,
+ priv->stats.rxstaterr,
+ priv->stats.rxurberr);
+
+ return 0;
+}
+
+static void rtl8192_proc_module_init(void)
+{
+ RT_TRACE(COMP_INIT, "Initializing proc filesystem");
+ rtl8192_proc = proc_mkdir(RTL819xU_MODULE_NAME, init_net.proc_net);
+}
+
+/*
+ * seq_file wrappers for procfile show routines.
+ */
+static int rtl8192_proc_open(struct inode *inode, struct file *file)
+{
+ struct net_device *dev = proc_get_parent_data(inode);
+ int (*show)(struct seq_file *, void *) = PDE_DATA(inode);
+
+ return single_open(file, show, dev);
+}
+
+static const struct file_operations rtl8192_proc_fops = {
+ .open = rtl8192_proc_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+/*
+ * Table of proc files we need to create.
+ */
+struct rtl8192_proc_file {
+ char name[12];
+ int (*show)(struct seq_file *, void *);
+};
+
+static const struct rtl8192_proc_file rtl8192_proc_files[] = {
+ { "stats-rx", &proc_get_stats_rx },
+ { "stats-tx", &proc_get_stats_tx },
+ { "stats-ap", &proc_get_stats_ap },
+ { "registers", &proc_get_registers },
+ { "" }
+};
+
+static void rtl8192_proc_init_one(struct net_device *dev)
+{
+ const struct rtl8192_proc_file *f;
+ struct proc_dir_entry *dir;
+
+ if (rtl8192_proc) {
+ dir = proc_mkdir_data(dev->name, 0, rtl8192_proc, dev);
+ if (!dir) {
+ RT_TRACE(COMP_ERR, "Unable to initialize /proc/net/rtl8192/%s\n",
+ dev->name);
+ return;
+ }
+
+ for (f = rtl8192_proc_files; f->name[0]; f++) {
+ if (!proc_create_data(f->name, S_IFREG | S_IRUGO, dir,
+ &rtl8192_proc_fops, f->show)) {
+ RT_TRACE(COMP_ERR, "Unable to initialize "
+ "/proc/net/rtl8192/%s/%s\n",
+ dev->name, f->name);
+ return;
+ }
+ }
+ }
+}
+
+static void rtl8192_proc_remove_one(struct net_device *dev)
+{
+ remove_proc_subtree(dev->name, rtl8192_proc);
+}
+
+/****************************************************************************
+ -----------------------------MISC STUFF-------------------------
+*****************************************************************************/
+
+short check_nic_enough_desc(struct net_device *dev, int queue_index)
+{
+ struct r8192_priv *priv = ieee80211_priv(dev);
+ int used = atomic_read(&priv->tx_pending[queue_index]);
+
+ return (used < MAX_TX_URB);
+}
+
+static void tx_timeout(struct net_device *dev)
+{
+ struct r8192_priv *priv = ieee80211_priv(dev);
+
+ schedule_work(&priv->reset_wq);
+}
+
+void rtl8192_update_msr(struct net_device *dev)
+{
+ struct r8192_priv *priv = ieee80211_priv(dev);
+ u8 msr;
+
+ read_nic_byte(dev, MSR, &msr);
+ msr &= ~MSR_LINK_MASK;
+
+ /* do not change in link_state != WLAN_LINK_ASSOCIATED.
+ * msr must be updated if the state is ASSOCIATING.
+ * this is intentional and make sense for ad-hoc and
+ * master (see the create BSS/IBSS func)
+ */
+ if (priv->ieee80211->state == IEEE80211_LINKED) {
+
+ if (priv->ieee80211->iw_mode == IW_MODE_INFRA)
+ msr |= (MSR_LINK_MANAGED<<MSR_LINK_SHIFT);
+ else if (priv->ieee80211->iw_mode == IW_MODE_ADHOC)
+ msr |= (MSR_LINK_ADHOC<<MSR_LINK_SHIFT);
+ else if (priv->ieee80211->iw_mode == IW_MODE_MASTER)
+ msr |= (MSR_LINK_MASTER<<MSR_LINK_SHIFT);
+
+ } else {
+ msr |= (MSR_LINK_NONE<<MSR_LINK_SHIFT);
+ }
+
+ write_nic_byte(dev, MSR, msr);
+}
+
+void rtl8192_set_chan(struct net_device *dev, short ch)
+{
+ struct r8192_priv *priv = (struct r8192_priv *)ieee80211_priv(dev);
+ RT_TRACE(COMP_CH, "=====>%s()====ch:%d\n", __func__, ch);
+ priv->chan = ch;
+
+ /* this hack should avoid frame TX during channel setting*/
+
+ //need to implement rf set channel here WB
+
+ if (priv->rf_set_chan)
+ priv->rf_set_chan(dev, priv->chan);
+ mdelay(10);
+}
+
+static void rtl8192_rx_isr(struct urb *urb);
+
+static u32 get_rxpacket_shiftbytes_819xusb(struct ieee80211_rx_stats *pstats)
+{
+
+ return (sizeof(rx_desc_819x_usb) + pstats->RxDrvInfoSize
+ + pstats->RxBufShift);
+
+}
+static int rtl8192_rx_initiate(struct net_device *dev)
+{
+ struct r8192_priv *priv = (struct r8192_priv *)ieee80211_priv(dev);
+ struct urb *entry;
+ struct sk_buff *skb;
+ struct rtl8192_rx_info *info;
+
+ /* nomal packet rx procedure */
+ while (skb_queue_len(&priv->rx_queue) < MAX_RX_URB) {
+ skb = __dev_alloc_skb(RX_URB_SIZE, GFP_KERNEL);
+ if (!skb)
+ break;
+ entry = usb_alloc_urb(0, GFP_KERNEL);
+ if (!entry) {
+ kfree_skb(skb);
+ break;
+ }
+ usb_fill_bulk_urb(entry, priv->udev,
+ usb_rcvbulkpipe(priv->udev, 3), skb_tail_pointer(skb),
+ RX_URB_SIZE, rtl8192_rx_isr, skb);
+ info = (struct rtl8192_rx_info *) skb->cb;
+ info->urb = entry;
+ info->dev = dev;
+ info->out_pipe = 3; //denote rx normal packet queue
+ skb_queue_tail(&priv->rx_queue, skb);
+ usb_submit_urb(entry, GFP_KERNEL);
+ }
+
+ /* command packet rx procedure */
+ while (skb_queue_len(&priv->rx_queue) < MAX_RX_URB + 3) {
+ skb = __dev_alloc_skb(RX_URB_SIZE, GFP_KERNEL);
+ if (!skb)
+ break;
+ entry = usb_alloc_urb(0, GFP_KERNEL);
+ if (!entry) {
+ kfree_skb(skb);
+ break;
+ }
+ usb_fill_bulk_urb(entry, priv->udev,
+ usb_rcvbulkpipe(priv->udev, 9), skb_tail_pointer(skb),
+ RX_URB_SIZE, rtl8192_rx_isr, skb);
+ info = (struct rtl8192_rx_info *) skb->cb;
+ info->urb = entry;
+ info->dev = dev;
+ info->out_pipe = 9; //denote rx cmd packet queue
+ skb_queue_tail(&priv->rx_queue, skb);
+ usb_submit_urb(entry, GFP_KERNEL);
+ }
+
+ return 0;
+}
+
+void rtl8192_set_rxconf(struct net_device *dev)
+{
+ struct r8192_priv *priv = (struct r8192_priv *)ieee80211_priv(dev);
+ u32 rxconf;
+
+ read_nic_dword(dev, RCR, &rxconf);
+ rxconf = rxconf & ~MAC_FILTER_MASK;
+ rxconf = rxconf | RCR_AMF;
+ rxconf = rxconf | RCR_ADF;
+ rxconf = rxconf | RCR_AB;
+ rxconf = rxconf | RCR_AM;
+
+ if (dev->flags & IFF_PROMISC)
+ DMESG("NIC in promisc mode");
+
+ if (priv->ieee80211->iw_mode == IW_MODE_MONITOR ||
+ dev->flags & IFF_PROMISC) {
+ rxconf = rxconf | RCR_AAP;
+ } else {
+ rxconf = rxconf | RCR_APM;
+ rxconf = rxconf | RCR_CBSSID;
+ }
+
+
+ if (priv->ieee80211->iw_mode == IW_MODE_MONITOR) {
+ rxconf = rxconf | RCR_AICV;
+ rxconf = rxconf | RCR_APWRMGT;
+ }
+
+ if (priv->crcmon == 1 && priv->ieee80211->iw_mode == IW_MODE_MONITOR)
+ rxconf = rxconf | RCR_ACRC32;
+
+
+ rxconf = rxconf & ~RX_FIFO_THRESHOLD_MASK;
+ rxconf = rxconf | (RX_FIFO_THRESHOLD_NONE<<RX_FIFO_THRESHOLD_SHIFT);
+ rxconf = rxconf & ~MAX_RX_DMA_MASK;
+ rxconf = rxconf | ((u32)7<<RCR_MXDMA_OFFSET);
+
+ rxconf = rxconf | RCR_ONLYERLPKT;
+
+ write_nic_dword(dev, RCR, rxconf);
+}
+//wait to be removed
+void rtl8192_rx_enable(struct net_device *dev)
+{
+ rtl8192_rx_initiate(dev);
+}
+
+
+void rtl8192_tx_enable(struct net_device *dev)
+{
+}
+
+
+
+void rtl8192_rtx_disable(struct net_device *dev)
+{
+ u8 cmd;
+ struct r8192_priv *priv = ieee80211_priv(dev);
+ struct sk_buff *skb;
+ struct rtl8192_rx_info *info;
+
+ read_nic_byte(dev, CMDR, &cmd);
+ write_nic_byte(dev, CMDR, cmd & ~(CR_TE|CR_RE));
+ force_pci_posting(dev);
+ mdelay(10);
+
+ while ((skb = __skb_dequeue(&priv->rx_queue))) {
+ info = (struct rtl8192_rx_info *) skb->cb;
+ if (!info->urb)
+ continue;
+
+ usb_kill_urb(info->urb);
+ kfree_skb(skb);
+ }
+
+ if (skb_queue_len(&priv->skb_queue))
+ netdev_warn(dev, "skb_queue not empty\n");
+
+ skb_queue_purge(&priv->skb_queue);
+}
+
+inline u16 ieeerate2rtlrate(int rate)
+{
+ switch (rate) {
+ case 10:
+ return 0;
+ case 20:
+ return 1;
+ case 55:
+ return 2;
+ case 110:
+ return 3;
+ case 60:
+ return 4;
+ case 90:
+ return 5;
+ case 120:
+ return 6;
+ case 180:
+ return 7;
+ case 240:
+ return 8;
+ case 360:
+ return 9;
+ case 480:
+ return 10;
+ case 540:
+ return 11;
+ default:
+ return 3;
+
+ }
+}
+static u16 rtl_rate[] = {10, 20, 55, 110, 60, 90, 120, 180, 240, 360, 480, 540};
+inline u16 rtl8192_rate2rate(short rate)
+{
+ if (rate > 11)
+ return 0;
+ return rtl_rate[rate];
+}
+
+
+/* The prototype of rx_isr has changed since one version of Linux Kernel */
+static void rtl8192_rx_isr(struct urb *urb)
+{
+ struct sk_buff *skb = (struct sk_buff *) urb->context;
+ struct rtl8192_rx_info *info = (struct rtl8192_rx_info *)skb->cb;
+ struct net_device *dev = info->dev;
+ struct r8192_priv *priv = ieee80211_priv(dev);
+ int out_pipe = info->out_pipe;
+ int err;
+ if (!priv->up)
+ return;
+ if (unlikely(urb->status)) {
+ info->urb = NULL;
+ priv->stats.rxstaterr++;
+ priv->ieee80211->stats.rx_errors++;
+ usb_free_urb(urb);
+ return;
+ }
+ skb_unlink(skb, &priv->rx_queue);
+ skb_put(skb, urb->actual_length);
+
+ skb_queue_tail(&priv->skb_queue, skb);
+ tasklet_schedule(&priv->irq_rx_tasklet);
+
+ skb = dev_alloc_skb(RX_URB_SIZE);
+ if (unlikely(!skb)) {
+ usb_free_urb(urb);
+ netdev_err(dev, "%s(): can't alloc skb\n", __func__);
+ /* TODO check rx queue length and refill *somewhere* */
+ return;
+ }
+
+ usb_fill_bulk_urb(urb, priv->udev,
+ usb_rcvbulkpipe(priv->udev, out_pipe), skb_tail_pointer(skb),
+ RX_URB_SIZE, rtl8192_rx_isr, skb);
+
+ info = (struct rtl8192_rx_info *) skb->cb;
+ info->urb = urb;
+ info->dev = dev;
+ info->out_pipe = out_pipe;
+
+ urb->transfer_buffer = skb_tail_pointer(skb);
+ urb->context = skb;
+ skb_queue_tail(&priv->rx_queue, skb);
+ err = usb_submit_urb(urb, GFP_ATOMIC);
+ if (err && err != EPERM)
+ netdev_err(dev, "can not submit rxurb, err is %x, URB status is %x\n", err, urb->status);
+}
+
+static u32 rtl819xusb_rx_command_packet(struct net_device *dev,
+ struct ieee80211_rx_stats *pstats)
+{
+ u32 status;
+
+ status = cmpk_message_handle_rx(dev, pstats);
+ if (status)
+ DMESG("rxcommandpackethandle819xusb: It is a command packet\n");
+
+ return status;
+}
+
+
+static void rtl8192_data_hard_stop(struct net_device *dev)
+{
+ //FIXME !!
+}
+
+
+static void rtl8192_data_hard_resume(struct net_device *dev)
+{
+ // FIXME !!
+}
+
+/* this function TX data frames when the ieee80211 stack requires this.
+ * It checks also if we need to stop the ieee tx queue, eventually do it
+ */
+static void rtl8192_hard_data_xmit(struct sk_buff *skb, struct net_device *dev, int rate)
+{
+ struct r8192_priv *priv = (struct r8192_priv *)ieee80211_priv(dev);
+ int ret;
+ unsigned long flags;
+ cb_desc *tcb_desc = (cb_desc *)(skb->cb + MAX_DEV_ADDR_SIZE);
+ u8 queue_index = tcb_desc->queue_index;
+
+ /* shall not be referred by command packet */
+ RTL8192U_ASSERT(queue_index != TXCMD_QUEUE);
+
+ spin_lock_irqsave(&priv->tx_lock, flags);
+
+ memcpy((unsigned char *)(skb->cb), &dev, sizeof(dev));
+ tcb_desc->bTxEnableFwCalcDur = 1;
+ skb_push(skb, priv->ieee80211->tx_headroom);
+ ret = rtl8192_tx(dev, skb);
+
+ spin_unlock_irqrestore(&priv->tx_lock, flags);
+}
+
+/* This is a rough attempt to TX a frame
+ * This is called by the ieee 80211 stack to TX management frames.
+ * If the ring is full packet are dropped (for data frame the queue
+ * is stopped before this can happen).
+ */
+static int rtl8192_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+ struct r8192_priv *priv = (struct r8192_priv *)ieee80211_priv(dev);
+ int ret;
+ unsigned long flags;
+ cb_desc *tcb_desc = (cb_desc *)(skb->cb + MAX_DEV_ADDR_SIZE);
+ u8 queue_index = tcb_desc->queue_index;
+
+
+ spin_lock_irqsave(&priv->tx_lock, flags);
+
+ memcpy((unsigned char *)(skb->cb), &dev, sizeof(dev));
+ if (queue_index == TXCMD_QUEUE) {
+ skb_push(skb, USB_HWDESC_HEADER_LEN);
+ rtl819xU_tx_cmd(dev, skb);
+ ret = 1;
+ spin_unlock_irqrestore(&priv->tx_lock, flags);
+ return ret;
+ } else {
+ skb_push(skb, priv->ieee80211->tx_headroom);
+ ret = rtl8192_tx(dev, skb);
+ }
+
+ spin_unlock_irqrestore(&priv->tx_lock, flags);
+
+ return ret;
+}
+
+
+void rtl8192_try_wake_queue(struct net_device *dev, int pri);
+
+static void rtl8192_tx_isr(struct urb *tx_urb)
+{
+ struct sk_buff *skb = (struct sk_buff *)tx_urb->context;
+ struct net_device *dev = (struct net_device *)(skb->cb);
+ struct r8192_priv *priv = NULL;
+ cb_desc *tcb_desc = (cb_desc *)(skb->cb + MAX_DEV_ADDR_SIZE);
+ u8 queue_index = tcb_desc->queue_index;
+
+ priv = ieee80211_priv(dev);
+
+ if (tcb_desc->queue_index != TXCMD_QUEUE) {
+ if (tx_urb->status == 0) {
+ dev->trans_start = jiffies;
+ priv->stats.txoktotal++;
+ priv->ieee80211->LinkDetectInfo.NumTxOkInPeriod++;
+ priv->stats.txbytesunicast += (skb->len - priv->ieee80211->tx_headroom);
+ } else {
+ priv->ieee80211->stats.tx_errors++;
+ /* TODO */
+ }
+ }
+
+ /* free skb and tx_urb */
+ if (skb != NULL) {
+ dev_kfree_skb_any(skb);
+ usb_free_urb(tx_urb);
+ atomic_dec(&priv->tx_pending[queue_index]);
+ }
+
+ //
+ // Handle HW Beacon:
+ // We had transfer our beacon frame to host controller at this moment.
+ //
+ //
+ // Caution:
+ // Handling the wait queue of command packets.
+ // For Tx command packets, we must not do TCB fragment because it is not handled right now.
+ // We must cut the packets to match the size of TX_CMD_PKT before we send it.
+ //
+
+ /* Handle MPDU in wait queue. */
+ if (queue_index != BEACON_QUEUE) {
+ /* Don't send data frame during scanning.*/
+ if ((skb_queue_len(&priv->ieee80211->skb_waitQ[queue_index]) != 0) &&
+ (!(priv->ieee80211->queue_stop))) {
+ skb = skb_dequeue(&(priv->ieee80211->skb_waitQ[queue_index]));
+ if (skb)
+ priv->ieee80211->softmac_hard_start_xmit(skb, dev);
+
+ return; //modified by david to avoid further processing AMSDU
+ }
+ }
+
+}
+
+static void rtl8192_config_rate(struct net_device *dev, u16 *rate_config)
+{
+ struct r8192_priv *priv = ieee80211_priv(dev);
+ struct ieee80211_network *net;
+ u8 i = 0, basic_rate = 0;
+ net = &priv->ieee80211->current_network;
+
+ for (i = 0; i < net->rates_len; i++) {
+ basic_rate = net->rates[i]&0x7f;
+ switch (basic_rate) {
+ case MGN_1M:
+ *rate_config |= RRSR_1M;
+ break;
+ case MGN_2M:
+ *rate_config |= RRSR_2M;
+ break;
+ case MGN_5_5M:
+ *rate_config |= RRSR_5_5M;
+ break;
+ case MGN_11M:
+ *rate_config |= RRSR_11M;
+ break;
+ case MGN_6M:
+ *rate_config |= RRSR_6M;
+ break;
+ case MGN_9M:
+ *rate_config |= RRSR_9M;
+ break;
+ case MGN_12M:
+ *rate_config |= RRSR_12M;
+ break;
+ case MGN_18M:
+ *rate_config |= RRSR_18M;
+ break;
+ case MGN_24M:
+ *rate_config |= RRSR_24M;
+ break;
+ case MGN_36M:
+ *rate_config |= RRSR_36M;
+ break;
+ case MGN_48M:
+ *rate_config |= RRSR_48M;
+ break;
+ case MGN_54M:
+ *rate_config |= RRSR_54M;
+ break;
+ }
+ }
+ for (i = 0; i < net->rates_ex_len; i++) {
+ basic_rate = net->rates_ex[i]&0x7f;
+ switch (basic_rate) {
+ case MGN_1M:
+ *rate_config |= RRSR_1M;
+ break;
+ case MGN_2M:
+ *rate_config |= RRSR_2M;
+ break;
+ case MGN_5_5M:
+ *rate_config |= RRSR_5_5M;
+ break;
+ case MGN_11M:
+ *rate_config |= RRSR_11M;
+ break;
+ case MGN_6M:
+ *rate_config |= RRSR_6M;
+ break;
+ case MGN_9M:
+ *rate_config |= RRSR_9M;
+ break;
+ case MGN_12M:
+ *rate_config |= RRSR_12M;
+ break;
+ case MGN_18M:
+ *rate_config |= RRSR_18M;
+ break;
+ case MGN_24M:
+ *rate_config |= RRSR_24M;
+ break;
+ case MGN_36M:
+ *rate_config |= RRSR_36M;
+ break;
+ case MGN_48M:
+ *rate_config |= RRSR_48M;
+ break;
+ case MGN_54M:
+ *rate_config |= RRSR_54M;
+ break;
+ }
+ }
+}
+
+
+#define SHORT_SLOT_TIME 9
+#define NON_SHORT_SLOT_TIME 20
+
+static void rtl8192_update_cap(struct net_device *dev, u16 cap)
+{
+ u32 tmp = 0;
+ struct r8192_priv *priv = ieee80211_priv(dev);
+ struct ieee80211_network *net = &priv->ieee80211->current_network;
+ priv->short_preamble = cap & WLAN_CAPABILITY_SHORT_PREAMBLE;
+ tmp = priv->basic_rate;
+ if (priv->short_preamble)
+ tmp |= BRSR_AckShortPmb;
+ write_nic_dword(dev, RRSR, tmp);
+
+ if (net->mode & (IEEE_G|IEEE_N_24G)) {
+ u8 slot_time = 0;
+ if ((cap & WLAN_CAPABILITY_SHORT_SLOT) && (!priv->ieee80211->pHTInfo->bCurrentRT2RTLongSlotTime)) /* short slot time */
+ slot_time = SHORT_SLOT_TIME;
+ else //long slot time
+ slot_time = NON_SHORT_SLOT_TIME;
+ priv->slot_time = slot_time;
+ write_nic_byte(dev, SLOT_TIME, slot_time);
+ }
+
+}
+static void rtl8192_net_update(struct net_device *dev)
+{
+
+ struct r8192_priv *priv = ieee80211_priv(dev);
+ struct ieee80211_network *net;
+ u16 BcnTimeCfg = 0, BcnCW = 6, BcnIFS = 0xf;
+ u16 rate_config = 0;
+ net = &priv->ieee80211->current_network;
+
+ rtl8192_config_rate(dev, &rate_config);
+ priv->basic_rate = rate_config & 0x15f;
+
+ write_nic_dword(dev, BSSIDR, ((u32 *)net->bssid)[0]);
+ write_nic_word(dev, BSSIDR+4, ((u16 *)net->bssid)[2]);
+
+ rtl8192_update_msr(dev);
+ if (priv->ieee80211->iw_mode == IW_MODE_ADHOC) {
+ write_nic_word(dev, ATIMWND, 2);
+ write_nic_word(dev, BCN_DMATIME, 1023);
+ write_nic_word(dev, BCN_INTERVAL, net->beacon_interval);
+ write_nic_word(dev, BCN_DRV_EARLY_INT, 1);
+ write_nic_byte(dev, BCN_ERR_THRESH, 100);
+ BcnTimeCfg |= (BcnCW<<BCN_TCFG_CW_SHIFT);
+ // TODO: BcnIFS may required to be changed on ASIC
+ BcnTimeCfg |= BcnIFS<<BCN_TCFG_IFS;
+
+ write_nic_word(dev, BCN_TCFG, BcnTimeCfg);
+ }
+
+
+
+}
+
+//temporary hw beacon is not used any more.
+//open it when necessary
+void rtl819xusb_beacon_tx(struct net_device *dev, u16 tx_rate)
+{
+
+}
+inline u8 rtl8192_IsWirelessBMode(u16 rate)
+{
+ if (((rate <= 110) && (rate != 60) && (rate != 90)) || (rate == 220))
+ return 1;
+ else
+ return 0;
+}
+
+u16 N_DBPSOfRate(u16 DataRate);
+
+
+u16 N_DBPSOfRate(u16 DataRate)
+{
+ u16 N_DBPS = 24;
+
+ switch (DataRate) {
+ case 60:
+ N_DBPS = 24;
+ break;
+
+ case 90:
+ N_DBPS = 36;
+ break;
+
+ case 120:
+ N_DBPS = 48;
+ break;
+
+ case 180:
+ N_DBPS = 72;
+ break;
+
+ case 240:
+ N_DBPS = 96;
+ break;
+
+ case 360:
+ N_DBPS = 144;
+ break;
+
+ case 480:
+ N_DBPS = 192;
+ break;
+
+ case 540:
+ N_DBPS = 216;
+ break;
+
+ default:
+ break;
+ }
+
+ return N_DBPS;
+}
+
+short rtl819xU_tx_cmd(struct net_device *dev, struct sk_buff *skb)
+{
+ struct r8192_priv *priv = ieee80211_priv(dev);
+ int status;
+ struct urb *tx_urb;
+ unsigned int idx_pipe;
+ tx_desc_cmd_819x_usb *pdesc = (tx_desc_cmd_819x_usb *)skb->data;
+ cb_desc *tcb_desc = (cb_desc *)(skb->cb + MAX_DEV_ADDR_SIZE);
+ u8 queue_index = tcb_desc->queue_index;
+
+ atomic_inc(&priv->tx_pending[queue_index]);
+ tx_urb = usb_alloc_urb(0, GFP_ATOMIC);
+ if (!tx_urb) {
+ dev_kfree_skb(skb);
+ return -ENOMEM;
+ }
+
+ memset(pdesc, 0, USB_HWDESC_HEADER_LEN);
+ /* Tx descriptor ought to be set according to the skb->cb */
+ pdesc->FirstSeg = 1;//bFirstSeg;
+ pdesc->LastSeg = 1;//bLastSeg;
+ pdesc->CmdInit = tcb_desc->bCmdOrInit;
+ pdesc->TxBufferSize = tcb_desc->txbuf_size;
+ pdesc->OWN = 1;
+ pdesc->LINIP = tcb_desc->bLastIniPkt;
+
+ //----------------------------------------------------------------------------
+ // Fill up USB_OUT_CONTEXT.
+ //----------------------------------------------------------------------------
+ idx_pipe = 0x04;
+ usb_fill_bulk_urb(tx_urb, priv->udev, usb_sndbulkpipe(priv->udev, idx_pipe),
+ skb->data, skb->len, rtl8192_tx_isr, skb);
+
+ status = usb_submit_urb(tx_urb, GFP_ATOMIC);
+
+ if (!status) {
+ return 0;
+ } else {
+ DMESGE("Error TX CMD URB, error %d", status);
+ return -1;
+ }
+}
+
+/*
+ * Mapping Software/Hardware descriptor queue id to "Queue Select Field"
+ * in TxFwInfo data structure
+ * 2006.10.30 by Emily
+ *
+ * \param QUEUEID Software Queue
+*/
+static u8 MapHwQueueToFirmwareQueue(u8 QueueID)
+{
+ u8 QueueSelect = 0x0; //defualt set to
+
+ switch (QueueID) {
+ case BE_QUEUE:
+ QueueSelect = QSLT_BE;
+ break;
+
+ case BK_QUEUE:
+ QueueSelect = QSLT_BK;
+ break;
+
+ case VO_QUEUE:
+ QueueSelect = QSLT_VO;
+ break;
+
+ case VI_QUEUE:
+ QueueSelect = QSLT_VI;
+ break;
+ case MGNT_QUEUE:
+ QueueSelect = QSLT_MGNT;
+ break;
+
+ case BEACON_QUEUE:
+ QueueSelect = QSLT_BEACON;
+ break;
+
+ // TODO: 2006.10.30 mark other queue selection until we verify it is OK
+ // TODO: Remove Assertions
+ case TXCMD_QUEUE:
+ QueueSelect = QSLT_CMD;
+ break;
+ case HIGH_QUEUE:
+ QueueSelect = QSLT_HIGH;
+ break;
+
+ default:
+ RT_TRACE(COMP_ERR, "TransmitTCB(): Impossible Queue Selection: %d \n", QueueID);
+ break;
+ }
+ return QueueSelect;
+}
+
+static u8 MRateToHwRate8190Pci(u8 rate)
+{
+ u8 ret = DESC90_RATE1M;
+
+ switch (rate) {
+ case MGN_1M:
+ ret = DESC90_RATE1M;
+ break;
+ case MGN_2M:
+ ret = DESC90_RATE2M;
+ break;
+ case MGN_5_5M:
+ ret = DESC90_RATE5_5M;
+ break;
+ case MGN_11M:
+ ret = DESC90_RATE11M;
+ break;
+ case MGN_6M:
+ ret = DESC90_RATE6M;
+ break;
+ case MGN_9M:
+ ret = DESC90_RATE9M;
+ break;
+ case MGN_12M:
+ ret = DESC90_RATE12M;
+ break;
+ case MGN_18M:
+ ret = DESC90_RATE18M;
+ break;
+ case MGN_24M:
+ ret = DESC90_RATE24M;
+ break;
+ case MGN_36M:
+ ret = DESC90_RATE36M;
+ break;
+ case MGN_48M:
+ ret = DESC90_RATE48M;
+ break;
+ case MGN_54M:
+ ret = DESC90_RATE54M;
+ break;
+
+ /* HT rate since here */
+ case MGN_MCS0:
+ ret = DESC90_RATEMCS0;
+ break;
+ case MGN_MCS1:
+ ret = DESC90_RATEMCS1;
+ break;
+ case MGN_MCS2:
+ ret = DESC90_RATEMCS2;
+ break;
+ case MGN_MCS3:
+ ret = DESC90_RATEMCS3;
+ break;
+ case MGN_MCS4:
+ ret = DESC90_RATEMCS4;
+ break;
+ case MGN_MCS5:
+ ret = DESC90_RATEMCS5;
+ break;
+ case MGN_MCS6:
+ ret = DESC90_RATEMCS6;
+ break;
+ case MGN_MCS7:
+ ret = DESC90_RATEMCS7;
+ break;
+ case MGN_MCS8:
+ ret = DESC90_RATEMCS8;
+ break;
+ case MGN_MCS9:
+ ret = DESC90_RATEMCS9;
+ break;
+ case MGN_MCS10:
+ ret = DESC90_RATEMCS10;
+ break;
+ case MGN_MCS11:
+ ret = DESC90_RATEMCS11;
+ break;
+ case MGN_MCS12:
+ ret = DESC90_RATEMCS12;
+ break;
+ case MGN_MCS13:
+ ret = DESC90_RATEMCS13;
+ break;
+ case MGN_MCS14:
+ ret = DESC90_RATEMCS14;
+ break;
+ case MGN_MCS15:
+ ret = DESC90_RATEMCS15;
+ break;
+ case (0x80|0x20):
+ ret = DESC90_RATEMCS32;
+ break;
+
+ default:
+ break;
+ }
+ return ret;
+}
+
+
+static u8 QueryIsShort(u8 TxHT, u8 TxRate, cb_desc *tcb_desc)
+{
+ u8 tmp_Short;
+
+ tmp_Short = (TxHT == 1) ? ((tcb_desc->bUseShortGI) ? 1 : 0) : ((tcb_desc->bUseShortPreamble) ? 1 : 0);
+
+ if (TxHT == 1 && TxRate != DESC90_RATEMCS15)
+ tmp_Short = 0;
+
+ return tmp_Short;
+}
+
+static void tx_zero_isr(struct urb *tx_urb)
+{
+ return;
+}
+
+/*
+ * The tx procedure is just as following,
+ * skb->cb will contain all the following information,
+ * priority, morefrag, rate, &dev.
+ * */
+short rtl8192_tx(struct net_device *dev, struct sk_buff *skb)
+{
+ struct r8192_priv *priv = ieee80211_priv(dev);
+ cb_desc *tcb_desc = (cb_desc *)(skb->cb + MAX_DEV_ADDR_SIZE);
+ tx_desc_819x_usb *tx_desc = (tx_desc_819x_usb *)skb->data;
+ tx_fwinfo_819x_usb *tx_fwinfo = (tx_fwinfo_819x_usb *)(skb->data + USB_HWDESC_HEADER_LEN);
+ struct usb_device *udev = priv->udev;
+ int pend;
+ int status;
+ struct urb *tx_urb = NULL, *tx_urb_zero = NULL;
+ unsigned int idx_pipe;
+ pend = atomic_read(&priv->tx_pending[tcb_desc->queue_index]);
+ /* we are locked here so the two atomic_read and inc are executed
+ * without interleaves
+ * !!! For debug purpose
+ */
+ if (pend > MAX_TX_URB) {
+ netdev_dbg(dev, "To discard skb packet!\n");
+ dev_kfree_skb_any(skb);
+ return -1;
+ }
+
+ tx_urb = usb_alloc_urb(0, GFP_ATOMIC);
+ if (!tx_urb) {
+ dev_kfree_skb_any(skb);
+ return -ENOMEM;
+ }
+
+ /* Fill Tx firmware info */
+ memset(tx_fwinfo, 0, sizeof(tx_fwinfo_819x_usb));
+ /* DWORD 0 */
+ tx_fwinfo->TxHT = (tcb_desc->data_rate&0x80) ? 1 : 0;
+ tx_fwinfo->TxRate = MRateToHwRate8190Pci(tcb_desc->data_rate);
+ tx_fwinfo->EnableCPUDur = tcb_desc->bTxEnableFwCalcDur;
+ tx_fwinfo->Short = QueryIsShort(tx_fwinfo->TxHT, tx_fwinfo->TxRate, tcb_desc);
+ if (tcb_desc->bAMPDUEnable) { /* AMPDU enabled */
+ tx_fwinfo->AllowAggregation = 1;
+ /* DWORD 1 */
+ tx_fwinfo->RxMF = tcb_desc->ampdu_factor;
+ tx_fwinfo->RxAMD = tcb_desc->ampdu_density&0x07;//ampdudensity
+ } else {
+ tx_fwinfo->AllowAggregation = 0;
+ /* DWORD 1 */
+ tx_fwinfo->RxMF = 0;
+ tx_fwinfo->RxAMD = 0;
+ }
+
+ /* Protection mode related */
+ tx_fwinfo->RtsEnable = (tcb_desc->bRTSEnable) ? 1 : 0;
+ tx_fwinfo->CtsEnable = (tcb_desc->bCTSEnable) ? 1 : 0;
+ tx_fwinfo->RtsSTBC = (tcb_desc->bRTSSTBC) ? 1 : 0;
+ tx_fwinfo->RtsHT = (tcb_desc->rts_rate&0x80) ? 1 : 0;
+ tx_fwinfo->RtsRate = MRateToHwRate8190Pci((u8)tcb_desc->rts_rate);
+ tx_fwinfo->RtsSubcarrier = (tx_fwinfo->RtsHT == 0) ? (tcb_desc->RTSSC) : 0;
+ tx_fwinfo->RtsBandwidth = (tx_fwinfo->RtsHT == 1) ? ((tcb_desc->bRTSBW) ? 1 : 0) : 0;
+ tx_fwinfo->RtsShort = (tx_fwinfo->RtsHT == 0) ? (tcb_desc->bRTSUseShortPreamble ? 1 : 0) :
+ (tcb_desc->bRTSUseShortGI ? 1 : 0);
+
+ /* Set Bandwidth and sub-channel settings. */
+ if (priv->CurrentChannelBW == HT_CHANNEL_WIDTH_20_40) {
+ if (tcb_desc->bPacketBW) {
+ tx_fwinfo->TxBandwidth = 1;
+ tx_fwinfo->TxSubCarrier = 0; //By SD3's Jerry suggestion, use duplicated mode
+ } else {
+ tx_fwinfo->TxBandwidth = 0;
+ tx_fwinfo->TxSubCarrier = priv->nCur40MhzPrimeSC;
+ }
+ } else {
+ tx_fwinfo->TxBandwidth = 0;
+ tx_fwinfo->TxSubCarrier = 0;
+ }
+
+ /* Fill Tx descriptor */
+ memset(tx_desc, 0, sizeof(tx_desc_819x_usb));
+ /* DWORD 0 */
+ tx_desc->LINIP = 0;
+ tx_desc->CmdInit = 1;
+ tx_desc->Offset = sizeof(tx_fwinfo_819x_usb) + 8;
+ tx_desc->PktSize = (skb->len - TX_PACKET_SHIFT_BYTES) & 0xffff;
+
+ /*DWORD 1*/
+ tx_desc->SecCAMID = 0;
+ tx_desc->RATid = tcb_desc->RATRIndex;
+ tx_desc->NoEnc = 1;
+ tx_desc->SecType = 0x0;
+ if (tcb_desc->bHwSec) {
+ switch (priv->ieee80211->pairwise_key_type) {
+ case KEY_TYPE_WEP40:
+ case KEY_TYPE_WEP104:
+ tx_desc->SecType = 0x1;
+ tx_desc->NoEnc = 0;
+ break;
+ case KEY_TYPE_TKIP:
+ tx_desc->SecType = 0x2;
+ tx_desc->NoEnc = 0;
+ break;
+ case KEY_TYPE_CCMP:
+ tx_desc->SecType = 0x3;
+ tx_desc->NoEnc = 0;
+ break;
+ case KEY_TYPE_NA:
+ tx_desc->SecType = 0x0;
+ tx_desc->NoEnc = 1;
+ break;
+ }
+ }
+
+ tx_desc->QueueSelect = MapHwQueueToFirmwareQueue(tcb_desc->queue_index);
+ tx_desc->TxFWInfoSize = sizeof(tx_fwinfo_819x_usb);
+
+ tx_desc->DISFB = tcb_desc->bTxDisableRateFallBack;
+ tx_desc->USERATE = tcb_desc->bTxUseDriverAssingedRate;
+
+ /* Fill fields that are required to be initialized in all of the descriptors */
+ //DWORD 0
+ tx_desc->FirstSeg = 1;
+ tx_desc->LastSeg = 1;
+ tx_desc->OWN = 1;
+
+ /* DWORD 2 */
+ tx_desc->TxBufferSize = (u32)(skb->len - USB_HWDESC_HEADER_LEN);
+ idx_pipe = 0x5;
+
+ /* To submit bulk urb */
+ usb_fill_bulk_urb(tx_urb, udev,
+ usb_sndbulkpipe(udev, idx_pipe), skb->data,
+ skb->len, rtl8192_tx_isr, skb);
+
+ status = usb_submit_urb(tx_urb, GFP_ATOMIC);
+ if (!status) {
+ //we need to send 0 byte packet whenever 512N bytes/64N(HIGN SPEED/NORMAL SPEED) bytes packet has been transmitted. Otherwise, it will be halt to wait for another packet. WB. 2008.08.27
+ bool bSend0Byte = false;
+ u8 zero = 0;
+ if (udev->speed == USB_SPEED_HIGH) {
+ if (skb->len > 0 && skb->len % 512 == 0)
+ bSend0Byte = true;
+ } else {
+ if (skb->len > 0 && skb->len % 64 == 0)
+ bSend0Byte = true;
+ }
+ if (bSend0Byte) {
+ tx_urb_zero = usb_alloc_urb(0, GFP_ATOMIC);
+ if (!tx_urb_zero) {
+ RT_TRACE(COMP_ERR, "can't alloc urb for zero byte\n");
+ return -ENOMEM;
+ }
+ usb_fill_bulk_urb(tx_urb_zero, udev,
+ usb_sndbulkpipe(udev, idx_pipe), &zero,
+ 0, tx_zero_isr, dev);
+ status = usb_submit_urb(tx_urb_zero, GFP_ATOMIC);
+ if (status) {
+ RT_TRACE(COMP_ERR, "Error TX URB for zero byte %d, error %d", atomic_read(&priv->tx_pending[tcb_desc->queue_index]), status);
+ return -1;
+ }
+ }
+ dev->trans_start = jiffies;
+ atomic_inc(&priv->tx_pending[tcb_desc->queue_index]);
+ return 0;
+ } else {
+ RT_TRACE(COMP_ERR, "Error TX URB %d, error %d", atomic_read(&priv->tx_pending[tcb_desc->queue_index]),
+ status);
+ return -1;
+ }
+}
+
+static short rtl8192_usb_initendpoints(struct net_device *dev)
+{
+ struct r8192_priv *priv = ieee80211_priv(dev);
+
+ priv->rx_urb = kmalloc(sizeof(struct urb *) * (MAX_RX_URB+1),
+ GFP_KERNEL);
+ if (priv->rx_urb == NULL)
+ return -ENOMEM;
+
+#ifndef JACKSON_NEW_RX
+ for (i = 0; i < (MAX_RX_URB+1); i++) {
+
+ priv->rx_urb[i] = usb_alloc_urb(0, GFP_KERNEL);
+
+ priv->rx_urb[i]->transfer_buffer = kmalloc(RX_URB_SIZE, GFP_KERNEL);
+
+ priv->rx_urb[i]->transfer_buffer_length = RX_URB_SIZE;
+ }
+#endif
+
+#ifdef THOMAS_BEACON
+ {
+ long align = 0;
+ void *oldaddr, *newaddr;
+
+ priv->rx_urb[16] = usb_alloc_urb(0, GFP_KERNEL);
+ priv->oldaddr = kmalloc(16, GFP_KERNEL);
+ oldaddr = priv->oldaddr;
+ align = ((long)oldaddr) & 3;
+ if (align) {
+ newaddr = oldaddr + 4 - align;
+ priv->rx_urb[16]->transfer_buffer_length = 16 - 4 + align;
+ } else {
+ newaddr = oldaddr;
+ priv->rx_urb[16]->transfer_buffer_length = 16;
+ }
+ priv->rx_urb[16]->transfer_buffer = newaddr;
+ }
+#endif
+
+ memset(priv->rx_urb, 0, sizeof(struct urb *) * MAX_RX_URB);
+ priv->pp_rxskb = kcalloc(MAX_RX_URB, sizeof(struct sk_buff *),
+ GFP_KERNEL);
+ if (!priv->pp_rxskb) {
+ kfree(priv->rx_urb);
+
+ priv->pp_rxskb = NULL;
+ priv->rx_urb = NULL;
+
+ DMESGE("Endpoint Alloc Failure");
+ return -ENOMEM;
+ }
+
+ netdev_dbg(dev, "End of initendpoints\n");
+ return 0;
+
+}
+#ifdef THOMAS_BEACON
+static void rtl8192_usb_deleteendpoints(struct net_device *dev)
+{
+ int i;
+ struct r8192_priv *priv = ieee80211_priv(dev);
+
+ if (priv->rx_urb) {
+ for (i = 0; i < (MAX_RX_URB+1); i++) {
+ usb_kill_urb(priv->rx_urb[i]);
+ usb_free_urb(priv->rx_urb[i]);
+ }
+ kfree(priv->rx_urb);
+ priv->rx_urb = NULL;
+ }
+ kfree(priv->oldaddr);
+ priv->oldaddr = NULL;
+ if (priv->pp_rxskb) {
+ kfree(priv->pp_rxskb);
+ priv->pp_rxskb = NULL;
+ }
+}
+#else
+void rtl8192_usb_deleteendpoints(struct net_device *dev)
+{
+ int i;
+ struct r8192_priv *priv = ieee80211_priv(dev);
+
+#ifndef JACKSON_NEW_RX
+
+ if (priv->rx_urb) {
+ for (i = 0; i < (MAX_RX_URB+1); i++) {
+ usb_kill_urb(priv->rx_urb[i]);
+ kfree(priv->rx_urb[i]->transfer_buffer);
+ usb_free_urb(priv->rx_urb[i]);
+ }
+ kfree(priv->rx_urb);
+ priv->rx_urb = NULL;
+
+ }
+#else
+ kfree(priv->rx_urb);
+ priv->rx_urb = NULL;
+ kfree(priv->oldaddr);
+ priv->oldaddr = NULL;
+ if (priv->pp_rxskb) {
+ kfree(priv->pp_rxskb);
+ priv->pp_rxskb = 0;
+
+ }
+
+#endif
+}
+#endif
+
+static void rtl8192_update_ratr_table(struct net_device *dev);
+static void rtl8192_link_change(struct net_device *dev)
+{
+ struct r8192_priv *priv = ieee80211_priv(dev);
+ struct ieee80211_device *ieee = priv->ieee80211;
+ if (ieee->state == IEEE80211_LINKED) {
+ rtl8192_net_update(dev);
+ rtl8192_update_ratr_table(dev);
+ //add this as in pure N mode, wep encryption will use software way, but there is no chance to set this as wep will not set group key in wext. WB.2008.07.08
+ if ((KEY_TYPE_WEP40 == ieee->pairwise_key_type) || (KEY_TYPE_WEP104 == ieee->pairwise_key_type))
+ EnableHWSecurityConfig8192(dev);
+ }
+ /*update timing params*/
+ if (ieee->iw_mode == IW_MODE_INFRA || ieee->iw_mode == IW_MODE_ADHOC) {
+ u32 reg = 0;
+ read_nic_dword(dev, RCR, &reg);
+ if (priv->ieee80211->state == IEEE80211_LINKED)
+ priv->ReceiveConfig = reg |= RCR_CBSSID;
+ else
+ priv->ReceiveConfig = reg &= ~RCR_CBSSID;
+ write_nic_dword(dev, RCR, reg);
+ }
+}
+
+static struct ieee80211_qos_parameters def_qos_parameters = {
+ {cpu_to_le16(3), cpu_to_le16(3), cpu_to_le16(3), cpu_to_le16(3)},
+ {cpu_to_le16(7), cpu_to_le16(7), cpu_to_le16(7), cpu_to_le16(7)},
+ {2, 2, 2, 2},/* aifs */
+ {0, 0, 0, 0},/* flags */
+ {0, 0, 0, 0} /* tx_op_limit */
+};
+
+
+static void rtl8192_update_beacon(struct work_struct *work)
+{
+ struct r8192_priv *priv = container_of(work, struct r8192_priv, update_beacon_wq.work);
+ struct net_device *dev = priv->ieee80211->dev;
+ struct ieee80211_device *ieee = priv->ieee80211;
+ struct ieee80211_network *net = &ieee->current_network;
+
+ if (ieee->pHTInfo->bCurrentHTSupport)
+ HTUpdateSelfAndPeerSetting(ieee, net);
+ ieee->pHTInfo->bCurrentRT2RTLongSlotTime = net->bssht.bdRT2RTLongSlotTime;
+ rtl8192_update_cap(dev, net->capability);
+}
+/*
+* background support to run QoS activate functionality
+*/
+static int WDCAPARA_ADD[] = {EDCAPARA_BE, EDCAPARA_BK, EDCAPARA_VI, EDCAPARA_VO};
+static void rtl8192_qos_activate(struct work_struct *work)
+{
+ struct r8192_priv *priv = container_of(work, struct r8192_priv, qos_activate);
+ struct net_device *dev = priv->ieee80211->dev;
+ struct ieee80211_qos_parameters *qos_parameters = &priv->ieee80211->current_network.qos_data.parameters;
+ u8 mode = priv->ieee80211->current_network.mode;
+ u32 u1bAIFS;
+ u32 u4bAcParam;
+ u32 op_limit;
+ u32 cw_max;
+ u32 cw_min;
+ int i;
+
+ mutex_lock(&priv->mutex);
+ if (priv->ieee80211->state != IEEE80211_LINKED)
+ goto success;
+ RT_TRACE(COMP_QOS, "qos active process with associate response received\n");
+ /* It better set slot time at first */
+ /* For we just support b/g mode at present, let the slot time at 9/20 selection */
+ /* update the ac parameter to related registers */
+ for (i = 0; i < QOS_QUEUE_NUM; i++) {
+ //Mode G/A: slotTimeTimer = 9; Mode B: 20
+ u1bAIFS = qos_parameters->aifs[i] * ((mode&(IEEE_G|IEEE_N_24G)) ? 9 : 20) + aSifsTime;
+ u1bAIFS <<= AC_PARAM_AIFS_OFFSET;
+ op_limit = (u32)le16_to_cpu(qos_parameters->tx_op_limit[i]);
+ op_limit <<= AC_PARAM_TXOP_LIMIT_OFFSET;
+ cw_max = (u32)le16_to_cpu(qos_parameters->cw_max[i]);
+ cw_max <<= AC_PARAM_ECW_MAX_OFFSET;
+ cw_min = (u32)le16_to_cpu(qos_parameters->cw_min[i]);
+ cw_min <<= AC_PARAM_ECW_MIN_OFFSET;
+ u4bAcParam = op_limit | cw_max | cw_min | u1bAIFS;
+ write_nic_dword(dev, WDCAPARA_ADD[i], u4bAcParam);
+ }
+
+success:
+ mutex_unlock(&priv->mutex);
+}
+
+static int rtl8192_qos_handle_probe_response(struct r8192_priv *priv,
+ int active_network,
+ struct ieee80211_network *network)
+{
+ int ret = 0;
+ u32 size = sizeof(struct ieee80211_qos_parameters);
+
+ if (priv->ieee80211->state != IEEE80211_LINKED)
+ return ret;
+
+ if ((priv->ieee80211->iw_mode != IW_MODE_INFRA))
+ return ret;
+
+ if (network->flags & NETWORK_HAS_QOS_MASK) {
+ if (active_network &&
+ (network->flags & NETWORK_HAS_QOS_PARAMETERS))
+ network->qos_data.active = network->qos_data.supported;
+
+ if ((network->qos_data.active == 1) && (active_network == 1) &&
+ (network->flags & NETWORK_HAS_QOS_PARAMETERS) &&
+ (network->qos_data.old_param_count !=
+ network->qos_data.param_count)) {
+ network->qos_data.old_param_count =
+ network->qos_data.param_count;
+ queue_work(priv->priv_wq, &priv->qos_activate);
+ RT_TRACE(COMP_QOS, "QoS parameters change call "
+ "qos_activate\n");
+ }
+ } else {
+ memcpy(&priv->ieee80211->current_network.qos_data.parameters,
+ &def_qos_parameters, size);
+
+ if ((network->qos_data.active == 1) && (active_network == 1)) {
+ queue_work(priv->priv_wq, &priv->qos_activate);
+ RT_TRACE(COMP_QOS, "QoS was disabled call qos_activate \n");
+ }
+ network->qos_data.active = 0;
+ network->qos_data.supported = 0;
+ }
+
+ return 0;
+}
+
+/* handle and manage frame from beacon and probe response */
+static int rtl8192_handle_beacon(struct net_device *dev,
+ struct ieee80211_beacon *beacon,
+ struct ieee80211_network *network)
+{
+ struct r8192_priv *priv = ieee80211_priv(dev);
+
+ rtl8192_qos_handle_probe_response(priv, 1, network);
+ queue_delayed_work(priv->priv_wq, &priv->update_beacon_wq, 0);
+ return 0;
+
+}
+
+/*
+* handling the beaconing responses. if we get different QoS setting
+* off the network from the associated setting, adjust the QoS
+* setting
+*/
+static int rtl8192_qos_association_resp(struct r8192_priv *priv,
+ struct ieee80211_network *network)
+{
+ unsigned long flags;
+ u32 size = sizeof(struct ieee80211_qos_parameters);
+ int set_qos_param = 0;
+
+ if ((priv == NULL) || (network == NULL))
+ return 0;
+
+ if (priv->ieee80211->state != IEEE80211_LINKED)
+ return 0;
+
+ if ((priv->ieee80211->iw_mode != IW_MODE_INFRA))
+ return 0;
+
+ spin_lock_irqsave(&priv->ieee80211->lock, flags);
+ if (network->flags & NETWORK_HAS_QOS_PARAMETERS) {
+ memcpy(&priv->ieee80211->current_network.qos_data.parameters,
+ &network->qos_data.parameters,
+ sizeof(struct ieee80211_qos_parameters));
+ priv->ieee80211->current_network.qos_data.active = 1;
+ set_qos_param = 1;
+ /* update qos parameter for current network */
+ priv->ieee80211->current_network.qos_data.old_param_count =
+ priv->ieee80211->current_network.qos_data.param_count;
+ priv->ieee80211->current_network.qos_data.param_count =
+ network->qos_data.param_count;
+ } else {
+ memcpy(&priv->ieee80211->current_network.qos_data.parameters,
+ &def_qos_parameters, size);
+ priv->ieee80211->current_network.qos_data.active = 0;
+ priv->ieee80211->current_network.qos_data.supported = 0;
+ set_qos_param = 1;
+ }
+
+ spin_unlock_irqrestore(&priv->ieee80211->lock, flags);
+
+ RT_TRACE(COMP_QOS, "%s: network->flags = %d,%d\n", __func__, network->flags, priv->ieee80211->current_network.qos_data.active);
+ if (set_qos_param == 1)
+ queue_work(priv->priv_wq, &priv->qos_activate);
+
+
+ return 0;
+}
+
+
+static int rtl8192_handle_assoc_response(struct net_device *dev,
+ struct ieee80211_assoc_response_frame *resp,
+ struct ieee80211_network *network)
+{
+ struct r8192_priv *priv = ieee80211_priv(dev);
+ rtl8192_qos_association_resp(priv, network);
+ return 0;
+}
+
+
+static void rtl8192_update_ratr_table(struct net_device *dev)
+{
+ struct r8192_priv *priv = ieee80211_priv(dev);
+ struct ieee80211_device *ieee = priv->ieee80211;
+ u8 *pMcsRate = ieee->dot11HTOperationalRateSet;
+ u32 ratr_value = 0;
+ u8 rate_index = 0;
+ rtl8192_config_rate(dev, (u16 *)(&ratr_value));
+ ratr_value |= (*(u16 *)(pMcsRate)) << 12;
+ switch (ieee->mode) {
+ case IEEE_A:
+ ratr_value &= 0x00000FF0;
+ break;
+ case IEEE_B:
+ ratr_value &= 0x0000000F;
+ break;
+ case IEEE_G:
+ ratr_value &= 0x00000FF7;
+ break;
+ case IEEE_N_24G:
+ case IEEE_N_5G:
+ if (ieee->pHTInfo->PeerMimoPs == 0) { /* MIMO_PS_STATIC */
+ ratr_value &= 0x0007F007;
+ } else {
+ if (priv->rf_type == RF_1T2R)
+ ratr_value &= 0x000FF007;
+ else
+ ratr_value &= 0x0F81F007;
+ }
+ break;
+ default:
+ break;
+ }
+ ratr_value &= 0x0FFFFFFF;
+ if (ieee->pHTInfo->bCurTxBW40MHz && ieee->pHTInfo->bCurShortGI40MHz)
+ ratr_value |= 0x80000000;
+ else if (!ieee->pHTInfo->bCurTxBW40MHz && ieee->pHTInfo->bCurShortGI20MHz)
+ ratr_value |= 0x80000000;
+ write_nic_dword(dev, RATR0+rate_index*4, ratr_value);
+ write_nic_byte(dev, UFWP, 1);
+}
+
+static u8 ccmp_ie[4] = {0x00, 0x50, 0xf2, 0x04};
+static u8 ccmp_rsn_ie[4] = {0x00, 0x0f, 0xac, 0x04};
+static bool GetNmodeSupportBySecCfg8192(struct net_device *dev)
+{
+ struct r8192_priv *priv = ieee80211_priv(dev);
+ struct ieee80211_device *ieee = priv->ieee80211;
+ struct ieee80211_network *network = &ieee->current_network;
+ int wpa_ie_len = ieee->wpa_ie_len;
+ struct ieee80211_crypt_data *crypt;
+ int encrypt;
+
+ crypt = ieee->crypt[ieee->tx_keyidx];
+ //we use connecting AP's capability instead of only security config on our driver to distinguish whether it should use N mode or G mode
+ encrypt = (network->capability & WLAN_CAPABILITY_PRIVACY) || (ieee->host_encrypt && crypt && crypt->ops && (0 == strcmp(crypt->ops->name, "WEP")));
+
+ /* simply judge */
+ if (encrypt && (wpa_ie_len == 0)) {
+ /* wep encryption, no N mode setting */
+ return false;
+ } else if ((wpa_ie_len != 0)) {
+ /* parse pairwise key type */
+ if (((ieee->wpa_ie[0] == 0xdd) && (!memcmp(&(ieee->wpa_ie[14]), ccmp_ie, 4))) || ((ieee->wpa_ie[0] == 0x30) && (!memcmp(&ieee->wpa_ie[10], ccmp_rsn_ie, 4))))
+ return true;
+ else
+ return false;
+ } else {
+ return true;
+ }
+
+ return true;
+}
+
+static bool GetHalfNmodeSupportByAPs819xUsb(struct net_device *dev)
+{
+ bool Reval;
+ struct r8192_priv *priv = ieee80211_priv(dev);
+ struct ieee80211_device *ieee = priv->ieee80211;
+
+ if (ieee->bHalfWirelessN24GMode == true)
+ Reval = true;
+ else
+ Reval = false;
+
+ return Reval;
+}
+
+static void rtl8192_refresh_supportrate(struct r8192_priv *priv)
+{
+ struct ieee80211_device *ieee = priv->ieee80211;
+ //we do not consider set support rate for ABG mode, only HT MCS rate is set here.
+ if (ieee->mode == WIRELESS_MODE_N_24G || ieee->mode == WIRELESS_MODE_N_5G)
+ memcpy(ieee->Regdot11HTOperationalRateSet, ieee->RegHTSuppRateSet, 16);
+ else
+ memset(ieee->Regdot11HTOperationalRateSet, 0, 16);
+}
+
+static u8 rtl8192_getSupportedWireleeMode(struct net_device *dev)
+{
+ struct r8192_priv *priv = ieee80211_priv(dev);
+ u8 ret = 0;
+ switch (priv->rf_chip) {
+ case RF_8225:
+ case RF_8256:
+ case RF_PSEUDO_11N:
+ ret = WIRELESS_MODE_N_24G|WIRELESS_MODE_G|WIRELESS_MODE_B;
+ break;
+ case RF_8258:
+ ret = WIRELESS_MODE_A|WIRELESS_MODE_N_5G;
+ break;
+ default:
+ ret = WIRELESS_MODE_B;
+ break;
+ }
+ return ret;
+}
+static void rtl8192_SetWirelessMode(struct net_device *dev, u8 wireless_mode)
+{
+ struct r8192_priv *priv = ieee80211_priv(dev);
+ u8 bSupportMode = rtl8192_getSupportedWireleeMode(dev);
+
+ if ((wireless_mode == WIRELESS_MODE_AUTO) || ((wireless_mode&bSupportMode) == 0)) {
+ if (bSupportMode & WIRELESS_MODE_N_24G) {
+ wireless_mode = WIRELESS_MODE_N_24G;
+ } else if (bSupportMode & WIRELESS_MODE_N_5G) {
+ wireless_mode = WIRELESS_MODE_N_5G;
+ } else if ((bSupportMode & WIRELESS_MODE_A)) {
+ wireless_mode = WIRELESS_MODE_A;
+ } else if ((bSupportMode & WIRELESS_MODE_G)) {
+ wireless_mode = WIRELESS_MODE_G;
+ } else if ((bSupportMode & WIRELESS_MODE_B)) {
+ wireless_mode = WIRELESS_MODE_B;
+ } else {
+ RT_TRACE(COMP_ERR, "%s(), No valid wireless mode supported, SupportedWirelessMode(%x)!!!\n", __func__, bSupportMode);
+ wireless_mode = WIRELESS_MODE_B;
+ }
+ }
+#ifdef TO_DO_LIST //// TODO: this function doesn't work well at this time, we should wait for FPGA
+ ActUpdateChannelAccessSetting(pAdapter, pHalData->CurrentWirelessMode, &pAdapter->MgntInfo.Info8185.ChannelAccessSetting);
+#endif
+ priv->ieee80211->mode = wireless_mode;
+
+ if ((wireless_mode == WIRELESS_MODE_N_24G) || (wireless_mode == WIRELESS_MODE_N_5G))
+ priv->ieee80211->pHTInfo->bEnableHT = 1;
+ else
+ priv->ieee80211->pHTInfo->bEnableHT = 0;
+ RT_TRACE(COMP_INIT, "Current Wireless Mode is %x\n", wireless_mode);
+ rtl8192_refresh_supportrate(priv);
+
+}
+//init priv variables here. only non_zero value should be initialized here.
+static void rtl8192_init_priv_variable(struct net_device *dev)
+{
+ struct r8192_priv *priv = ieee80211_priv(dev);
+ u8 i;
+ priv->card_8192 = NIC_8192U;
+ priv->chan = 1; //set to channel 1
+ priv->ieee80211->mode = WIRELESS_MODE_AUTO; //SET AUTO
+ priv->ieee80211->iw_mode = IW_MODE_INFRA;
+ priv->ieee80211->ieee_up = 0;
+ priv->retry_rts = DEFAULT_RETRY_RTS;
+ priv->retry_data = DEFAULT_RETRY_DATA;
+ priv->ieee80211->rts = DEFAULT_RTS_THRESHOLD;
+ priv->ieee80211->rate = 110; //11 mbps
+ priv->ieee80211->short_slot = 1;
+ priv->promisc = (dev->flags & IFF_PROMISC) ? 1 : 0;
+ priv->CckPwEnl = 6;
+ //for silent reset
+ priv->IrpPendingCount = 1;
+ priv->ResetProgress = RESET_TYPE_NORESET;
+ priv->bForcedSilentReset = false;
+ priv->bDisableNormalResetCheck = false;
+ priv->force_reset = false;
+
+ priv->ieee80211->FwRWRF = 0; //we don't use FW read/write RF until stable firmware is available.
+ priv->ieee80211->current_network.beacon_interval = DEFAULT_BEACONINTERVAL;
+ priv->ieee80211->softmac_features = IEEE_SOFTMAC_SCAN |
+ IEEE_SOFTMAC_ASSOCIATE | IEEE_SOFTMAC_PROBERQ |
+ IEEE_SOFTMAC_PROBERS | IEEE_SOFTMAC_TX_QUEUE |
+ IEEE_SOFTMAC_BEACONS;//added by amy 080604
+
+ priv->ieee80211->active_scan = 1;
+ priv->ieee80211->modulation = IEEE80211_CCK_MODULATION | IEEE80211_OFDM_MODULATION;
+ priv->ieee80211->host_encrypt = 1;
+ priv->ieee80211->host_decrypt = 1;
+ priv->ieee80211->start_send_beacons = NULL; //-by amy 080604
+ priv->ieee80211->stop_send_beacons = NULL; //-by amy 080604
+ priv->ieee80211->softmac_hard_start_xmit = rtl8192_hard_start_xmit;
+ priv->ieee80211->set_chan = rtl8192_set_chan;
+ priv->ieee80211->link_change = rtl8192_link_change;
+ priv->ieee80211->softmac_data_hard_start_xmit = rtl8192_hard_data_xmit;
+ priv->ieee80211->data_hard_stop = rtl8192_data_hard_stop;
+ priv->ieee80211->data_hard_resume = rtl8192_data_hard_resume;
+ priv->ieee80211->init_wmmparam_flag = 0;
+ priv->ieee80211->fts = DEFAULT_FRAG_THRESHOLD;
+ priv->ieee80211->check_nic_enough_desc = check_nic_enough_desc;
+ priv->ieee80211->tx_headroom = TX_PACKET_SHIFT_BYTES;
+ priv->ieee80211->qos_support = 1;
+
+ //added by WB
+ priv->ieee80211->SetBWModeHandler = rtl8192_SetBWMode;
+ priv->ieee80211->handle_assoc_response = rtl8192_handle_assoc_response;
+ priv->ieee80211->handle_beacon = rtl8192_handle_beacon;
+ //added by david
+ priv->ieee80211->GetNmodeSupportBySecCfg = GetNmodeSupportBySecCfg8192;
+ priv->ieee80211->GetHalfNmodeSupportByAPsHandler = GetHalfNmodeSupportByAPs819xUsb;
+ priv->ieee80211->SetWirelessMode = rtl8192_SetWirelessMode;
+ //added by amy
+ priv->ieee80211->InitialGainHandler = InitialGain819xUsb;
+ priv->card_type = USB;
+#ifdef TO_DO_LIST
+ if (Adapter->bInHctTest) {
+ pHalData->ShortRetryLimit = 7;
+ pHalData->LongRetryLimit = 7;
+ }
+#endif
+ priv->ShortRetryLimit = 0x30;
+ priv->LongRetryLimit = 0x30;
+ priv->EarlyRxThreshold = 7;
+ priv->enable_gpio0 = 0;
+ priv->TransmitConfig =
+ (TCR_MXDMA_2048<<TCR_MXDMA_OFFSET)| // Max DMA Burst Size per Tx DMA Burst, 7: reserved.
+ (priv->ShortRetryLimit<<TCR_SRL_OFFSET)| // Short retry limit
+ (priv->LongRetryLimit<<TCR_LRL_OFFSET) | // Long retry limit
+ (false ? TCR_SAT : 0); // FALSE: HW provides PLCP length and LENGEXT, TRUE: SW provides them
+#ifdef TO_DO_LIST
+ if (Adapter->bInHctTest)
+ pHalData->ReceiveConfig = pHalData->CSMethod |
+ RCR_AMF | RCR_ADF | //accept management/data
+ //guangan200710
+ RCR_ACF | //accept control frame for SW AP needs PS-poll, 2005.07.07, by rcnjko.
+ RCR_AB | RCR_AM | RCR_APM | //accept BC/MC/UC
+ RCR_AICV | RCR_ACRC32 | //accept ICV/CRC error packet
+ ((u32)7<<RCR_MXDMA_OFFSET) | // Max DMA Burst Size per Rx DMA Burst, 7: unlimited.
+ (pHalData->EarlyRxThreshold<<RCR_FIFO_OFFSET) | // Rx FIFO Threshold, 7: No Rx threshold.
+ (pHalData->EarlyRxThreshold == 7 ? RCR_OnlyErlPkt : 0);
+ else
+
+#endif
+ priv->ReceiveConfig =
+ RCR_AMF | RCR_ADF | //accept management/data
+ RCR_ACF | //accept control frame for SW AP needs PS-poll, 2005.07.07, by rcnjko.
+ RCR_AB | RCR_AM | RCR_APM | //accept BC/MC/UC
+ ((u32)7<<RCR_MXDMA_OFFSET)| // Max DMA Burst Size per Rx DMA Burst, 7: unlimited.
+ (priv->EarlyRxThreshold<<RX_FIFO_THRESHOLD_SHIFT) | // Rx FIFO Threshold, 7: No Rx threshold.
+ (priv->EarlyRxThreshold == 7 ? RCR_ONLYERLPKT : 0);
+
+ priv->AcmControl = 0;
+ priv->pFirmware = kzalloc(sizeof(rt_firmware), GFP_KERNEL);
+
+ /* rx related queue */
+ skb_queue_head_init(&priv->rx_queue);
+ skb_queue_head_init(&priv->skb_queue);
+
+ /* Tx related queue */
+ for (i = 0; i < MAX_QUEUE_SIZE; i++)
+ skb_queue_head_init(&priv->ieee80211->skb_waitQ[i]);
+ for (i = 0; i < MAX_QUEUE_SIZE; i++)
+ skb_queue_head_init(&priv->ieee80211->skb_aggQ[i]);
+ for (i = 0; i < MAX_QUEUE_SIZE; i++)
+ skb_queue_head_init(&priv->ieee80211->skb_drv_aggQ[i]);
+ priv->rf_set_chan = rtl8192_phy_SwChnl;
+}
+
+//init lock here
+static void rtl8192_init_priv_lock(struct r8192_priv *priv)
+{
+ spin_lock_init(&priv->tx_lock);
+ spin_lock_init(&priv->irq_lock);//added by thomas
+ sema_init(&priv->wx_sem, 1);
+ sema_init(&priv->rf_sem, 1);
+ mutex_init(&priv->mutex);
+}
+
+static void rtl819x_watchdog_wqcallback(struct work_struct *work);
+
+static void rtl8192_irq_rx_tasklet(struct r8192_priv *priv);
+//init tasklet and wait_queue here. only 2.6 above kernel is considered
+#define DRV_NAME "wlan0"
+static void rtl8192_init_priv_task(struct net_device *dev)
+{
+ struct r8192_priv *priv = ieee80211_priv(dev);
+
+ priv->priv_wq = create_workqueue(DRV_NAME);
+
+ INIT_WORK(&priv->reset_wq, rtl8192_restart);
+
+ INIT_DELAYED_WORK(&priv->watch_dog_wq, rtl819x_watchdog_wqcallback);
+ INIT_DELAYED_WORK(&priv->txpower_tracking_wq, dm_txpower_trackingcallback);
+ INIT_DELAYED_WORK(&priv->rfpath_check_wq, dm_rf_pathcheck_workitemcallback);
+ INIT_DELAYED_WORK(&priv->update_beacon_wq, rtl8192_update_beacon);
+ INIT_DELAYED_WORK(&priv->initialgain_operate_wq, InitialGainOperateWorkItemCallBack);
+ INIT_WORK(&priv->qos_activate, rtl8192_qos_activate);
+
+ tasklet_init(&priv->irq_rx_tasklet,
+ (void(*)(unsigned long))rtl8192_irq_rx_tasklet,
+ (unsigned long)priv);
+}
+
+static void rtl8192_get_eeprom_size(struct net_device *dev)
+{
+ u16 curCR = 0;
+ struct r8192_priv *priv = ieee80211_priv(dev);
+ RT_TRACE(COMP_EPROM, "===========>%s()\n", __func__);
+ read_nic_word_E(dev, EPROM_CMD, &curCR);
+ RT_TRACE(COMP_EPROM, "read from Reg EPROM_CMD(%x):%x\n", EPROM_CMD, curCR);
+ //whether need I consider BIT5?
+ priv->epromtype = (curCR & Cmd9346CR_9356SEL) ? EPROM_93c56 : EPROM_93c46;
+ RT_TRACE(COMP_EPROM, "<===========%s(), epromtype:%d\n", __func__, priv->epromtype);
+}
+
+//used to swap endian. as ntohl & htonl are not necessary to swap endian, so use this instead.
+static inline u16 endian_swap(u16 *data)
+{
+ u16 tmp = *data;
+ *data = (tmp >> 8) | (tmp << 8);
+ return *data;
+}
+static void rtl8192_read_eeprom_info(struct net_device *dev)
+{
+ u16 wEPROM_ID = 0;
+ u8 bMac_Tmp_Addr[6] = {0x00, 0xe0, 0x4c, 0x00, 0x00, 0x02};
+ u8 bLoad_From_EEPOM = false;
+ struct r8192_priv *priv = ieee80211_priv(dev);
+ u16 tmpValue = 0;
+ int i;
+ RT_TRACE(COMP_EPROM, "===========>%s()\n", __func__);
+ wEPROM_ID = eprom_read(dev, 0); //first read EEPROM ID out;
+ RT_TRACE(COMP_EPROM, "EEPROM ID is 0x%x\n", wEPROM_ID);
+
+ if (wEPROM_ID != RTL8190_EEPROM_ID) {
+ RT_TRACE(COMP_ERR, "EEPROM ID is invalid(is 0x%x(should be 0x%x)\n", wEPROM_ID, RTL8190_EEPROM_ID);
+ } else {
+ bLoad_From_EEPOM = true;
+ }
+
+ if (bLoad_From_EEPOM) {
+ tmpValue = eprom_read(dev, EEPROM_VID>>1);
+ priv->eeprom_vid = endian_swap(&tmpValue);
+ priv->eeprom_pid = eprom_read(dev, EEPROM_PID>>1);
+ tmpValue = eprom_read(dev, EEPROM_ChannelPlan>>1);
+ priv->eeprom_ChannelPlan = (tmpValue & 0xff00)>>8;
+ priv->btxpowerdata_readfromEEPORM = true;
+ priv->eeprom_CustomerID = eprom_read(dev, (EEPROM_Customer_ID>>1)) >>8;
+ } else {
+ priv->eeprom_vid = 0;
+ priv->eeprom_pid = 0;
+ priv->card_8192_version = VERSION_819xU_B;
+ priv->eeprom_ChannelPlan = 0;
+ priv->eeprom_CustomerID = 0;
+ }
+ RT_TRACE(COMP_EPROM, "vid:0x%4x, pid:0x%4x, CustomID:0x%2x, ChanPlan:0x%x\n", priv->eeprom_vid, priv->eeprom_pid, priv->eeprom_CustomerID, priv->eeprom_ChannelPlan);
+ //set channelplan from eeprom
+ priv->ChannelPlan = priv->eeprom_ChannelPlan;
+ if (bLoad_From_EEPOM) {
+ int i;
+ for (i = 0; i < 6; i += 2) {
+ u16 tmp = 0;
+ tmp = eprom_read(dev, (u16)((EEPROM_NODE_ADDRESS_BYTE_0 + i)>>1));
+ *(u16 *)(&dev->dev_addr[i]) = tmp;
+ }
+ } else {
+ memcpy(dev->dev_addr, bMac_Tmp_Addr, 6);
+ //should I set IDR0 here?
+ }
+ RT_TRACE(COMP_EPROM, "MAC addr:%pM\n", dev->dev_addr);
+ priv->rf_type = RTL819X_DEFAULT_RF_TYPE; //default 1T2R
+ priv->rf_chip = RF_8256;
+
+ if (priv->card_8192_version == (u8)VERSION_819xU_A) {
+ //read Tx power gain offset of legacy OFDM to HT rate
+ if (bLoad_From_EEPOM)
+ priv->EEPROMTxPowerDiff = (eprom_read(dev, (EEPROM_TxPowerDiff>>1))&0xff00) >> 8;
+ else
+ priv->EEPROMTxPowerDiff = EEPROM_Default_TxPower;
+ RT_TRACE(COMP_EPROM, "TxPowerDiff:%d\n", priv->EEPROMTxPowerDiff);
+ //read ThermalMeter from EEPROM
+ if (bLoad_From_EEPOM)
+ priv->EEPROMThermalMeter = (u8)(eprom_read(dev, (EEPROM_ThermalMeter>>1))&0x00ff);
+ else
+ priv->EEPROMThermalMeter = EEPROM_Default_ThermalMeter;
+ RT_TRACE(COMP_EPROM, "ThermalMeter:%d\n", priv->EEPROMThermalMeter);
+ //vivi, for tx power track
+ priv->TSSI_13dBm = priv->EEPROMThermalMeter *100;
+ //read antenna tx power offset of B/C/D to A from EEPROM
+ if (bLoad_From_EEPOM)
+ priv->EEPROMPwDiff = (eprom_read(dev, (EEPROM_PwDiff>>1))&0x0f00)>>8;
+ else
+ priv->EEPROMPwDiff = EEPROM_Default_PwDiff;
+ RT_TRACE(COMP_EPROM, "TxPwDiff:%d\n", priv->EEPROMPwDiff);
+ // Read CrystalCap from EEPROM
+ if (bLoad_From_EEPOM)
+ priv->EEPROMCrystalCap = (eprom_read(dev, (EEPROM_CrystalCap>>1))&0x0f);
+ else
+ priv->EEPROMCrystalCap = EEPROM_Default_CrystalCap;
+ RT_TRACE(COMP_EPROM, "CrystalCap = %d\n", priv->EEPROMCrystalCap);
+ //get per-channel Tx power level
+ if (bLoad_From_EEPOM)
+ priv->EEPROM_Def_Ver = (eprom_read(dev, (EEPROM_TxPwIndex_Ver>>1))&0xff00)>>8;
+ else
+ priv->EEPROM_Def_Ver = 1;
+ RT_TRACE(COMP_EPROM, "EEPROM_DEF_VER:%d\n", priv->EEPROM_Def_Ver);
+ if (priv->EEPROM_Def_Ver == 0) { /* old eeprom definition */
+ int i;
+ if (bLoad_From_EEPOM)
+ priv->EEPROMTxPowerLevelCCK = (eprom_read(dev, (EEPROM_TxPwIndex_CCK>>1))&0xff) >> 8;
+ else
+ priv->EEPROMTxPowerLevelCCK = 0x10;
+ RT_TRACE(COMP_EPROM, "CCK Tx Power Levl: 0x%02x\n", priv->EEPROMTxPowerLevelCCK);
+ for (i = 0; i < 3; i++) {
+ if (bLoad_From_EEPOM) {
+ tmpValue = eprom_read(dev, (EEPROM_TxPwIndex_OFDM_24G+i)>>1);
+ if (((EEPROM_TxPwIndex_OFDM_24G+i) % 2) == 0)
+ tmpValue = tmpValue & 0x00ff;
+ else
+ tmpValue = (tmpValue & 0xff00) >> 8;
+ } else {
+ tmpValue = 0x10;
+ }
+ priv->EEPROMTxPowerLevelOFDM24G[i] = (u8) tmpValue;
+ RT_TRACE(COMP_EPROM, "OFDM 2.4G Tx Power Level, Index %d = 0x%02x\n", i, priv->EEPROMTxPowerLevelCCK);
+ }
+ } else if (priv->EEPROM_Def_Ver == 1) {
+ if (bLoad_From_EEPOM) {
+ tmpValue = eprom_read(dev,
+ EEPROM_TxPwIndex_CCK_V1 >> 1);
+ tmpValue = (tmpValue & 0xff00) >> 8;
+ } else {
+ tmpValue = 0x10;
+ }
+ priv->EEPROMTxPowerLevelCCK_V1[0] = (u8)tmpValue;
+
+ if (bLoad_From_EEPOM)
+ tmpValue = eprom_read(dev, (EEPROM_TxPwIndex_CCK_V1 + 2)>>1);
+ else
+ tmpValue = 0x1010;
+ *((u16 *)(&priv->EEPROMTxPowerLevelCCK_V1[1])) = tmpValue;
+ if (bLoad_From_EEPOM)
+ tmpValue = eprom_read(dev,
+ EEPROM_TxPwIndex_OFDM_24G_V1 >> 1);
+ else
+ tmpValue = 0x1010;
+ *((u16 *)(&priv->EEPROMTxPowerLevelOFDM24G[0])) = tmpValue;
+ if (bLoad_From_EEPOM)
+ tmpValue = eprom_read(dev, (EEPROM_TxPwIndex_OFDM_24G_V1+2)>>1);
+ else
+ tmpValue = 0x10;
+ priv->EEPROMTxPowerLevelOFDM24G[2] = (u8)tmpValue;
+ }//endif EEPROM_Def_Ver == 1
+
+ //update HAL variables
+ //
+ for (i = 0; i < 14; i++) {
+ if (i <= 3)
+ priv->TxPowerLevelOFDM24G[i] = priv->EEPROMTxPowerLevelOFDM24G[0];
+ else if (i >= 4 && i <= 9)
+ priv->TxPowerLevelOFDM24G[i] = priv->EEPROMTxPowerLevelOFDM24G[1];
+ else
+ priv->TxPowerLevelOFDM24G[i] = priv->EEPROMTxPowerLevelOFDM24G[2];
+ }
+
+ for (i = 0; i < 14; i++) {
+ if (priv->EEPROM_Def_Ver == 0) {
+ if (i <= 3)
+ priv->TxPowerLevelCCK[i] = priv->EEPROMTxPowerLevelOFDM24G[0] + (priv->EEPROMTxPowerLevelCCK - priv->EEPROMTxPowerLevelOFDM24G[1]);
+ else if (i >= 4 && i <= 9)
+ priv->TxPowerLevelCCK[i] = priv->EEPROMTxPowerLevelCCK;
+ else
+ priv->TxPowerLevelCCK[i] = priv->EEPROMTxPowerLevelOFDM24G[2] + (priv->EEPROMTxPowerLevelCCK - priv->EEPROMTxPowerLevelOFDM24G[1]);
+ } else if (priv->EEPROM_Def_Ver == 1) {
+ if (i <= 3)
+ priv->TxPowerLevelCCK[i] = priv->EEPROMTxPowerLevelCCK_V1[0];
+ else if (i >= 4 && i <= 9)
+ priv->TxPowerLevelCCK[i] = priv->EEPROMTxPowerLevelCCK_V1[1];
+ else
+ priv->TxPowerLevelCCK[i] = priv->EEPROMTxPowerLevelCCK_V1[2];
+ }
+ }
+ priv->TxPowerDiff = priv->EEPROMPwDiff;
+ // Antenna B gain offset to antenna A, bit0~3
+ priv->AntennaTxPwDiff[0] = (priv->EEPROMTxPowerDiff & 0xf);
+ // Antenna C gain offset to antenna A, bit4~7
+ priv->AntennaTxPwDiff[1] = (priv->EEPROMTxPowerDiff & 0xf0)>>4;
+ // CrystalCap, bit12~15
+ priv->CrystalCap = priv->EEPROMCrystalCap;
+ // ThermalMeter, bit0~3 for RFIC1, bit4~7 for RFIC2
+ // 92U does not enable TX power tracking.
+ priv->ThermalMeter[0] = priv->EEPROMThermalMeter;
+ }//end if VersionID == VERSION_819xU_A
+
+ //added by vivi, for dlink led, 20080416
+ switch (priv->eeprom_CustomerID) {
+ case EEPROM_CID_RUNTOP:
+ priv->CustomerID = RT_CID_819x_RUNTOP;
+ break;
+
+ case EEPROM_CID_DLINK:
+ priv->CustomerID = RT_CID_DLINK;
+ break;
+
+ default:
+ priv->CustomerID = RT_CID_DEFAULT;
+ break;
+
+ }
+
+ switch (priv->CustomerID) {
+ case RT_CID_819x_RUNTOP:
+ priv->LedStrategy = SW_LED_MODE2;
+ break;
+
+ case RT_CID_DLINK:
+ priv->LedStrategy = SW_LED_MODE4;
+ break;
+
+ default:
+ priv->LedStrategy = SW_LED_MODE0;
+ break;
+
+ }
+
+
+ if (priv->rf_type == RF_1T2R) {
+ RT_TRACE(COMP_EPROM, "\n1T2R config\n");
+ } else {
+ RT_TRACE(COMP_EPROM, "\n2T4R config\n");
+ }
+
+ // 2008/01/16 MH We can only know RF type in the function. So we have to init
+ // DIG RATR table again.
+ init_rate_adaptive(dev);
+ //we need init DIG RATR table here again.
+
+ RT_TRACE(COMP_EPROM, "<===========%s()\n", __func__);
+}
+
+static short rtl8192_get_channel_map(struct net_device *dev)
+{
+ struct r8192_priv *priv = ieee80211_priv(dev);
+ if (priv->ChannelPlan > COUNTRY_CODE_GLOBAL_DOMAIN) {
+ netdev_err(dev, "rtl8180_init: Error channel plan! Set to default.\n");
+ priv->ChannelPlan = 0;
+ }
+ RT_TRACE(COMP_INIT, "Channel plan is %d\n", priv->ChannelPlan);
+
+ rtl819x_set_channel_map(priv->ChannelPlan, priv);
+ return 0;
+}
+
+static short rtl8192_init(struct net_device *dev)
+{
+
+ struct r8192_priv *priv = ieee80211_priv(dev);
+
+ memset(&(priv->stats), 0, sizeof(struct Stats));
+ memset(priv->txqueue_to_outpipemap, 0, 9);
+#ifdef PIPE12
+ {
+ int i = 0;
+ u8 queuetopipe[] = {3, 2, 1, 0, 4, 8, 7, 6, 5};
+ memcpy(priv->txqueue_to_outpipemap, queuetopipe, 9);
+ }
+#else
+ {
+ u8 queuetopipe[] = {3, 2, 1, 0, 4, 4, 0, 4, 4};
+ memcpy(priv->txqueue_to_outpipemap, queuetopipe, 9);
+ }
+#endif
+ rtl8192_init_priv_variable(dev);
+ rtl8192_init_priv_lock(priv);
+ rtl8192_init_priv_task(dev);
+ rtl8192_get_eeprom_size(dev);
+ rtl8192_read_eeprom_info(dev);
+ rtl8192_get_channel_map(dev);
+ init_hal_dm(dev);
+ setup_timer(&priv->watch_dog_timer, watch_dog_timer_callback,
+ (unsigned long)dev);
+ if (rtl8192_usb_initendpoints(dev) != 0) {
+ DMESG("Endopoints initialization failed");
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+/******************************************************************************
+ *function: This function actually only set RRSR, RATR and BW_OPMODE registers
+ * not to do all the hw config as its name says
+ * input: net_device dev
+ * output: none
+ * return: none
+ * notice: This part need to modified according to the rate set we filtered
+ * ****************************************************************************/
+static void rtl8192_hwconfig(struct net_device *dev)
+{
+ u32 regRATR = 0, regRRSR = 0;
+ u8 regBwOpMode = 0, regTmp = 0;
+ struct r8192_priv *priv = ieee80211_priv(dev);
+ u32 ratr_value = 0;
+
+ // Set RRSR, RATR, and BW_OPMODE registers
+ //
+ switch (priv->ieee80211->mode) {
+ case WIRELESS_MODE_B:
+ regBwOpMode = BW_OPMODE_20MHZ;
+ regRATR = RATE_ALL_CCK;
+ regRRSR = RATE_ALL_CCK;
+ break;
+ case WIRELESS_MODE_A:
+ regBwOpMode = BW_OPMODE_5G |BW_OPMODE_20MHZ;
+ regRATR = RATE_ALL_OFDM_AG;
+ regRRSR = RATE_ALL_OFDM_AG;
+ break;
+ case WIRELESS_MODE_G:
+ regBwOpMode = BW_OPMODE_20MHZ;
+ regRATR = RATE_ALL_CCK | RATE_ALL_OFDM_AG;
+ regRRSR = RATE_ALL_CCK | RATE_ALL_OFDM_AG;
+ break;
+ case WIRELESS_MODE_AUTO:
+#ifdef TO_DO_LIST
+ if (Adapter->bInHctTest) {
+ regBwOpMode = BW_OPMODE_20MHZ;
+ regRATR = RATE_ALL_CCK | RATE_ALL_OFDM_AG;
+ regRRSR = RATE_ALL_CCK | RATE_ALL_OFDM_AG;
+ }
+ else
+#endif
+ {
+ regBwOpMode = BW_OPMODE_20MHZ;
+ regRATR = RATE_ALL_CCK | RATE_ALL_OFDM_AG | RATE_ALL_OFDM_1SS | RATE_ALL_OFDM_2SS;
+ regRRSR = RATE_ALL_CCK | RATE_ALL_OFDM_AG;
+ }
+ break;
+ case WIRELESS_MODE_N_24G:
+ // It support CCK rate by default.
+ // CCK rate will be filtered out only when associated AP does not support it.
+ regBwOpMode = BW_OPMODE_20MHZ;
+ regRATR = RATE_ALL_CCK | RATE_ALL_OFDM_AG | RATE_ALL_OFDM_1SS | RATE_ALL_OFDM_2SS;
+ regRRSR = RATE_ALL_CCK | RATE_ALL_OFDM_AG;
+ break;
+ case WIRELESS_MODE_N_5G:
+ regBwOpMode = BW_OPMODE_5G;
+ regRATR = RATE_ALL_OFDM_AG | RATE_ALL_OFDM_1SS | RATE_ALL_OFDM_2SS;
+ regRRSR = RATE_ALL_OFDM_AG;
+ break;
+ }
+
+ write_nic_byte(dev, BW_OPMODE, regBwOpMode);
+ ratr_value = regRATR;
+ if (priv->rf_type == RF_1T2R)
+ ratr_value &= ~(RATE_ALL_OFDM_2SS);
+ write_nic_dword(dev, RATR0, ratr_value);
+ write_nic_byte(dev, UFWP, 1);
+ read_nic_byte(dev, 0x313, &regTmp);
+ regRRSR = ((regTmp) << 24) | (regRRSR & 0x00ffffff);
+ write_nic_dword(dev, RRSR, regRRSR);
+
+ //
+ // Set Retry Limit here
+ //
+ write_nic_word(dev, RETRY_LIMIT,
+ priv->ShortRetryLimit << RETRY_LIMIT_SHORT_SHIFT |
+ priv->LongRetryLimit << RETRY_LIMIT_LONG_SHIFT);
+ // Set Contention Window here
+
+ // Set Tx AGC
+
+ // Set Tx Antenna including Feedback control
+
+ // Set Auto Rate fallback control
+
+
+}
+
+
+//InitializeAdapter and PhyCfg
+static bool rtl8192_adapter_start(struct net_device *dev)
+{
+ struct r8192_priv *priv = ieee80211_priv(dev);
+ u32 dwRegRead = 0;
+ bool init_status = true;
+ u8 SECR_value = 0x0;
+ u8 tmp;
+ RT_TRACE(COMP_INIT, "====>%s()\n", __func__);
+ priv->Rf_Mode = RF_OP_By_SW_3wire;
+ //for ASIC power on sequence
+ write_nic_byte_E(dev, 0x5f, 0x80);
+ mdelay(50);
+ write_nic_byte_E(dev, 0x5f, 0xf0);
+ write_nic_byte_E(dev, 0x5d, 0x00);
+ write_nic_byte_E(dev, 0x5e, 0x80);
+ write_nic_byte(dev, 0x17, 0x37);
+ mdelay(10);
+ priv->pFirmware->firmware_status = FW_STATUS_0_INIT;
+ //config CPUReset Register
+ //Firmware Reset or not?
+ read_nic_dword(dev, CPU_GEN, &dwRegRead);
+ if (priv->pFirmware->firmware_status == FW_STATUS_0_INIT)
+ dwRegRead |= CPU_GEN_SYSTEM_RESET; //do nothing here?
+ else if (priv->pFirmware->firmware_status == FW_STATUS_5_READY)
+ dwRegRead |= CPU_GEN_FIRMWARE_RESET;
+ else
+ RT_TRACE(COMP_ERR, "ERROR in %s(): undefined firmware state(%d)\n", __func__, priv->pFirmware->firmware_status);
+
+ write_nic_dword(dev, CPU_GEN, dwRegRead);
+ //config BB.
+ rtl8192_BBConfig(dev);
+
+ //Loopback mode or not
+ priv->LoopbackMode = RTL819xU_NO_LOOPBACK;
+
+ read_nic_dword(dev, CPU_GEN, &dwRegRead);
+ if (priv->LoopbackMode == RTL819xU_NO_LOOPBACK)
+ dwRegRead = (dwRegRead & CPU_GEN_NO_LOOPBACK_MSK) | CPU_GEN_NO_LOOPBACK_SET;
+ else if (priv->LoopbackMode == RTL819xU_MAC_LOOPBACK)
+ dwRegRead |= CPU_CCK_LOOPBACK;
+ else
+ RT_TRACE(COMP_ERR, "Serious error in %s(): wrong loopback mode setting(%d)\n", __func__, priv->LoopbackMode);
+
+ write_nic_dword(dev, CPU_GEN, dwRegRead);
+
+ //after reset cpu, we need wait for a seconds to write in register.
+ udelay(500);
+
+ //xiong add for new bitfile:usb suspend reset pin set to 1. //do we need?
+ read_nic_byte_E(dev, 0x5f, &tmp);
+ write_nic_byte_E(dev, 0x5f, tmp|0x20);
+
+ //Set Hardware
+ rtl8192_hwconfig(dev);
+
+ //turn on Tx/Rx
+ write_nic_byte(dev, CMDR, CR_RE|CR_TE);
+
+ //set IDR0 here
+ write_nic_dword(dev, MAC0, ((u32 *)dev->dev_addr)[0]);
+ write_nic_word(dev, MAC4, ((u16 *)(dev->dev_addr + 4))[0]);
+
+ //set RCR
+ write_nic_dword(dev, RCR, priv->ReceiveConfig);
+
+ //Initialize Number of Reserved Pages in Firmware Queue
+ write_nic_dword(dev, RQPN1, NUM_OF_PAGE_IN_FW_QUEUE_BK << RSVD_FW_QUEUE_PAGE_BK_SHIFT |
+ NUM_OF_PAGE_IN_FW_QUEUE_BE << RSVD_FW_QUEUE_PAGE_BE_SHIFT |
+ NUM_OF_PAGE_IN_FW_QUEUE_VI << RSVD_FW_QUEUE_PAGE_VI_SHIFT |
+ NUM_OF_PAGE_IN_FW_QUEUE_VO <<RSVD_FW_QUEUE_PAGE_VO_SHIFT);
+ write_nic_dword(dev, RQPN2, NUM_OF_PAGE_IN_FW_QUEUE_MGNT << RSVD_FW_QUEUE_PAGE_MGNT_SHIFT |
+ NUM_OF_PAGE_IN_FW_QUEUE_CMD << RSVD_FW_QUEUE_PAGE_CMD_SHIFT);
+ write_nic_dword(dev, RQPN3, APPLIED_RESERVED_QUEUE_IN_FW|
+ NUM_OF_PAGE_IN_FW_QUEUE_BCN<<RSVD_FW_QUEUE_PAGE_BCN_SHIFT);
+ write_nic_dword(dev, RATR0+4*7, (RATE_ALL_OFDM_AG | RATE_ALL_CCK));
+
+ //Set AckTimeout
+ // TODO: (it value is only for FPGA version). need to be changed!!2006.12.18, by Emily
+ write_nic_byte(dev, ACK_TIMEOUT, 0x30);
+
+ if (priv->ResetProgress == RESET_TYPE_NORESET)
+ rtl8192_SetWirelessMode(dev, priv->ieee80211->mode);
+ if (priv->ResetProgress == RESET_TYPE_NORESET) {
+ CamResetAllEntry(dev);
+ SECR_value |= SCR_TxEncEnable;
+ SECR_value |= SCR_RxDecEnable;
+ SECR_value |= SCR_NoSKMC;
+ write_nic_byte(dev, SECR, SECR_value);
+ }
+
+ //Beacon related
+ write_nic_word(dev, ATIMWND, 2);
+ write_nic_word(dev, BCN_INTERVAL, 100);
+
+#define DEFAULT_EDCA 0x005e4332
+ {
+ int i;
+ for (i = 0; i < QOS_QUEUE_NUM; i++)
+ write_nic_dword(dev, WDCAPARA_ADD[i], DEFAULT_EDCA);
+ }
+
+ rtl8192_phy_configmac(dev);
+
+ if (priv->card_8192_version == (u8) VERSION_819xU_A) {
+ rtl8192_phy_getTxPower(dev);
+ rtl8192_phy_setTxPower(dev, priv->chan);
+ }
+
+ //Firmware download
+ init_status = init_firmware(dev);
+ if (!init_status) {
+ RT_TRACE(COMP_ERR, "ERR!!! %s(): Firmware download is failed\n", __func__);
+ return init_status;
+ }
+ RT_TRACE(COMP_INIT, "%s():after firmware download\n", __func__);
+ //
+#ifdef TO_DO_LIST
+ if (Adapter->ResetProgress == RESET_TYPE_NORESET) {
+ if (pMgntInfo->RegRfOff == true) { /* User disable RF via registry. */
+ RT_TRACE((COMP_INIT|COMP_RF), DBG_LOUD, ("InitializeAdapter819xUsb(): Turn off RF for RegRfOff ----------\n"));
+ MgntActSet_RF_State(Adapter, eRfOff, RF_CHANGE_BY_SW);
+ // Those actions will be discard in MgntActSet_RF_State because of the same state
+ for (eRFPath = 0; eRFPath < pHalData->NumTotalRFPath; eRFPath++)
+ PHY_SetRFReg(Adapter, (RF90_RADIO_PATH_E)eRFPath, 0x4, 0xC00, 0x0);
+ } else if (pMgntInfo->RfOffReason > RF_CHANGE_BY_PS) { /* H/W or S/W RF OFF before sleep. */
+ RT_TRACE((COMP_INIT|COMP_RF), DBG_LOUD, ("InitializeAdapter819xUsb(): Turn off RF for RfOffReason(%d) ----------\n", pMgntInfo->RfOffReason));
+ MgntActSet_RF_State(Adapter, eRfOff, pMgntInfo->RfOffReason);
+ } else {
+ pHalData->eRFPowerState = eRfOn;
+ pMgntInfo->RfOffReason = 0;
+ RT_TRACE((COMP_INIT|COMP_RF), DBG_LOUD, ("InitializeAdapter819xUsb(): RF is on ----------\n"));
+ }
+ } else {
+ if (pHalData->eRFPowerState == eRfOff) {
+ MgntActSet_RF_State(Adapter, eRfOff, pMgntInfo->RfOffReason);
+ // Those actions will be discard in MgntActSet_RF_State because of the same state
+ for (eRFPath = 0; eRFPath < pHalData->NumTotalRFPath; eRFPath++)
+ PHY_SetRFReg(Adapter, (RF90_RADIO_PATH_E)eRFPath, 0x4, 0xC00, 0x0);
+ }
+ }
+#endif
+ //config RF.
+ if (priv->ResetProgress == RESET_TYPE_NORESET) {
+ rtl8192_phy_RFConfig(dev);
+ RT_TRACE(COMP_INIT, "%s():after phy RF config\n", __func__);
+ }
+
+
+ if (priv->ieee80211->FwRWRF)
+ // We can force firmware to do RF-R/W
+ priv->Rf_Mode = RF_OP_By_FW;
+ else
+ priv->Rf_Mode = RF_OP_By_SW_3wire;
+
+
+ rtl8192_phy_updateInitGain(dev);
+ /*--set CCK and OFDM Block "ON"--*/
+ rtl8192_setBBreg(dev, rFPGA0_RFMOD, bCCKEn, 0x1);
+ rtl8192_setBBreg(dev, rFPGA0_RFMOD, bOFDMEn, 0x1);
+
+ if (priv->ResetProgress == RESET_TYPE_NORESET) {
+ //if D or C cut
+ u8 tmpvalue;
+ read_nic_byte(dev, 0x301, &tmpvalue);
+ if (tmpvalue == 0x03) {
+ priv->bDcut = true;
+ RT_TRACE(COMP_POWER_TRACKING, "D-cut\n");
+ } else {
+ priv->bDcut = false;
+ RT_TRACE(COMP_POWER_TRACKING, "C-cut\n");
+ }
+ dm_initialize_txpower_tracking(dev);
+
+ if (priv->bDcut) {
+ u32 i, TempCCk;
+ u32 tmpRegA = rtl8192_QueryBBReg(dev, rOFDM0_XATxIQImbalance, bMaskDWord);
+ for (i = 0; i < TxBBGainTableLength; i++) {
+ if (tmpRegA == priv->txbbgain_table[i].txbbgain_value) {
+ priv->rfa_txpowertrackingindex = (u8)i;
+ priv->rfa_txpowertrackingindex_real = (u8)i;
+ priv->rfa_txpowertracking_default = priv->rfa_txpowertrackingindex;
+ break;
+ }
+ }
+
+ TempCCk = rtl8192_QueryBBReg(dev, rCCK0_TxFilter1, bMaskByte2);
+
+ for (i = 0; i < CCKTxBBGainTableLength; i++) {
+
+ if (TempCCk == priv->cck_txbbgain_table[i].ccktxbb_valuearray[0]) {
+ priv->cck_present_attentuation_20Mdefault = (u8) i;
+ break;
+ }
+ }
+ priv->cck_present_attentuation_40Mdefault = 0;
+ priv->cck_present_attentuation_difference = 0;
+ priv->cck_present_attentuation = priv->cck_present_attentuation_20Mdefault;
+
+ }
+ }
+ write_nic_byte(dev, 0x87, 0x0);
+
+
+ return init_status;
+}
+
+/* this configures registers for beacon tx and enables it via
+ * rtl8192_beacon_tx_enable(). rtl8192_beacon_tx_disable() might
+ * be used to stop beacon transmission
+ */
+/***************************************************************************
+ -------------------------------NET STUFF---------------------------
+***************************************************************************/
+
+static struct net_device_stats *rtl8192_stats(struct net_device *dev)
+{
+ struct r8192_priv *priv = ieee80211_priv(dev);
+
+ return &priv->ieee80211->stats;
+}
+
+static bool HalTxCheckStuck819xUsb(struct net_device *dev)
+{
+ struct r8192_priv *priv = ieee80211_priv(dev);
+ u16 RegTxCounter;
+ bool bStuck = false;
+ read_nic_word(dev, 0x128, &RegTxCounter);
+ RT_TRACE(COMP_RESET, "%s():RegTxCounter is %d,TxCounter is %d\n", __func__, RegTxCounter, priv->TxCounter);
+ if (priv->TxCounter == RegTxCounter)
+ bStuck = true;
+
+ priv->TxCounter = RegTxCounter;
+
+ return bStuck;
+}
+
+/*
+* <Assumption: RT_TX_SPINLOCK is acquired.>
+* First added: 2006.11.19 by emily
+*/
+static RESET_TYPE TxCheckStuck(struct net_device *dev)
+{
+ struct r8192_priv *priv = ieee80211_priv(dev);
+ u8 QueueID;
+ bool bCheckFwTxCnt = false;
+
+ //
+ // Decide such threshold according to current power save mode
+ //
+
+ for (QueueID = 0; QueueID <= BEACON_QUEUE; QueueID++) {
+ if (QueueID == TXCMD_QUEUE)
+ continue;
+ if ((skb_queue_len(&priv->ieee80211->skb_waitQ[QueueID]) == 0) && (skb_queue_len(&priv->ieee80211->skb_aggQ[QueueID]) == 0))
+ continue;
+
+ bCheckFwTxCnt = true;
+ }
+ if (bCheckFwTxCnt) {
+ if (HalTxCheckStuck819xUsb(dev)) {
+ RT_TRACE(COMP_RESET, "TxCheckStuck(): Fw indicates no Tx condition! \n");
+ return RESET_TYPE_SILENT;
+ }
+ }
+ return RESET_TYPE_NORESET;
+}
+
+static bool HalRxCheckStuck819xUsb(struct net_device *dev)
+{
+ u16 RegRxCounter;
+ struct r8192_priv *priv = ieee80211_priv(dev);
+ bool bStuck = false;
+ static u8 rx_chk_cnt;
+ read_nic_word(dev, 0x130, &RegRxCounter);
+ RT_TRACE(COMP_RESET, "%s(): RegRxCounter is %d,RxCounter is %d\n", __func__, RegRxCounter, priv->RxCounter);
+ // If rssi is small, we should check rx for long time because of bad rx.
+ // or maybe it will continuous silent reset every 2 seconds.
+ rx_chk_cnt++;
+ if (priv->undecorated_smoothed_pwdb >= (RateAdaptiveTH_High+5)) {
+ rx_chk_cnt = 0; //high rssi, check rx stuck right now.
+ } else if (priv->undecorated_smoothed_pwdb < (RateAdaptiveTH_High+5) &&
+ ((priv->CurrentChannelBW != HT_CHANNEL_WIDTH_20 && priv->undecorated_smoothed_pwdb >= RateAdaptiveTH_Low_40M) ||
+ (priv->CurrentChannelBW == HT_CHANNEL_WIDTH_20 && priv->undecorated_smoothed_pwdb >= RateAdaptiveTH_Low_20M))) {
+ if (rx_chk_cnt < 2)
+ return bStuck;
+ else
+ rx_chk_cnt = 0;
+ } else if (((priv->CurrentChannelBW != HT_CHANNEL_WIDTH_20 && priv->undecorated_smoothed_pwdb < RateAdaptiveTH_Low_40M) ||
+ (priv->CurrentChannelBW == HT_CHANNEL_WIDTH_20 && priv->undecorated_smoothed_pwdb < RateAdaptiveTH_Low_20M)) &&
+ priv->undecorated_smoothed_pwdb >= VeryLowRSSI) {
+ if (rx_chk_cnt < 4)
+ return bStuck;
+ else
+ rx_chk_cnt = 0;
+ } else {
+ if (rx_chk_cnt < 8)
+ return bStuck;
+ else
+ rx_chk_cnt = 0;
+ }
+
+ if (priv->RxCounter == RegRxCounter)
+ bStuck = true;
+
+ priv->RxCounter = RegRxCounter;
+
+ return bStuck;
+}
+
+static RESET_TYPE RxCheckStuck(struct net_device *dev)
+{
+ struct r8192_priv *priv = ieee80211_priv(dev);
+ bool bRxCheck = false;
+
+ if (priv->IrpPendingCount > 1)
+ bRxCheck = true;
+
+ if (bRxCheck) {
+ if (HalRxCheckStuck819xUsb(dev)) {
+ RT_TRACE(COMP_RESET, "RxStuck Condition\n");
+ return RESET_TYPE_SILENT;
+ }
+ }
+ return RESET_TYPE_NORESET;
+}
+
+
+/**
+* This function is called by Checkforhang to check whether we should ask OS to reset driver
+*
+* \param pAdapter The adapter context for this miniport
+*
+* Note:NIC with USB interface sholud not call this function because we cannot scan descriptor
+* to judge whether there is tx stuck.
+* Note: This function may be required to be rewrite for Vista OS.
+* <<<Assumption: Tx spinlock has been acquired >>>
+*
+* 8185 and 8185b does not implement this function. This is added by Emily at 2006.11.24
+*/
+static RESET_TYPE rtl819x_ifcheck_resetornot(struct net_device *dev)
+{
+ struct r8192_priv *priv = ieee80211_priv(dev);
+ RESET_TYPE TxResetType = RESET_TYPE_NORESET;
+ RESET_TYPE RxResetType = RESET_TYPE_NORESET;
+ RT_RF_POWER_STATE rfState;
+
+ rfState = priv->ieee80211->eRFPowerState;
+
+ TxResetType = TxCheckStuck(dev);
+ if (rfState != eRfOff ||
+ (priv->ieee80211->iw_mode != IW_MODE_ADHOC)) {
+ // If driver is in the status of firmware download failure , driver skips RF initialization and RF is
+ // in turned off state. Driver should check whether Rx stuck and do silent reset. And
+ // if driver is in firmware download failure status, driver should initialize RF in the following
+ // silent reset procedure Emily, 2008.01.21
+
+ // Driver should not check RX stuck in IBSS mode because it is required to
+ // set Check BSSID in order to send beacon, however, if check BSSID is
+ // set, STA cannot hear any packet at all. Emily, 2008.04.12
+ RxResetType = RxCheckStuck(dev);
+ }
+ if (TxResetType == RESET_TYPE_NORMAL || RxResetType == RESET_TYPE_NORMAL) {
+ return RESET_TYPE_NORMAL;
+ } else if (TxResetType == RESET_TYPE_SILENT || RxResetType == RESET_TYPE_SILENT) {
+ RT_TRACE(COMP_RESET, "%s():silent reset\n", __func__);
+ return RESET_TYPE_SILENT;
+ } else {
+ return RESET_TYPE_NORESET;
+ }
+
+}
+
+static void rtl8192_cancel_deferred_work(struct r8192_priv *priv);
+static int _rtl8192_up(struct net_device *dev);
+static int rtl8192_close(struct net_device *dev);
+
+
+
+static void CamRestoreAllEntry(struct net_device *dev)
+{
+ u8 EntryId = 0;
+ struct r8192_priv *priv = ieee80211_priv(dev);
+ u8 *MacAddr = priv->ieee80211->current_network.bssid;
+
+ static u8 CAM_CONST_ADDR[4][6] = {
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x01},
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x02},
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x03} };
+ static u8 CAM_CONST_BROAD[] = {
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
+
+ RT_TRACE(COMP_SEC, "CamRestoreAllEntry: \n");
+
+
+ if ((priv->ieee80211->pairwise_key_type == KEY_TYPE_WEP40) ||
+ (priv->ieee80211->pairwise_key_type == KEY_TYPE_WEP104)) {
+
+ for (EntryId = 0; EntryId < 4; EntryId++) {
+ MacAddr = CAM_CONST_ADDR[EntryId];
+ setKey(dev, EntryId, EntryId,
+ priv->ieee80211->pairwise_key_type,
+ MacAddr, 0, NULL);
+ }
+
+ } else if (priv->ieee80211->pairwise_key_type == KEY_TYPE_TKIP) {
+
+ if (priv->ieee80211->iw_mode == IW_MODE_ADHOC)
+ setKey(dev, 4, 0, priv->ieee80211->pairwise_key_type,
+ (u8 *)dev->dev_addr, 0, NULL);
+ else
+ setKey(dev, 4, 0, priv->ieee80211->pairwise_key_type,
+ MacAddr, 0, NULL);
+ } else if (priv->ieee80211->pairwise_key_type == KEY_TYPE_CCMP) {
+
+ if (priv->ieee80211->iw_mode == IW_MODE_ADHOC)
+ setKey(dev, 4, 0, priv->ieee80211->pairwise_key_type,
+ (u8 *)dev->dev_addr, 0, NULL);
+ else
+ setKey(dev, 4, 0, priv->ieee80211->pairwise_key_type,
+ MacAddr, 0, NULL);
+ }
+
+
+
+ if (priv->ieee80211->group_key_type == KEY_TYPE_TKIP) {
+ MacAddr = CAM_CONST_BROAD;
+ for (EntryId = 1; EntryId < 4; EntryId++) {
+ setKey(dev, EntryId, EntryId,
+ priv->ieee80211->group_key_type,
+ MacAddr, 0, NULL);
+ }
+ if (priv->ieee80211->iw_mode == IW_MODE_ADHOC)
+ setKey(dev, 0, 0, priv->ieee80211->group_key_type,
+ CAM_CONST_ADDR[0], 0, NULL);
+ } else if (priv->ieee80211->group_key_type == KEY_TYPE_CCMP) {
+ MacAddr = CAM_CONST_BROAD;
+ for (EntryId = 1; EntryId < 4; EntryId++) {
+ setKey(dev, EntryId, EntryId,
+ priv->ieee80211->group_key_type,
+ MacAddr, 0, NULL);
+ }
+
+ if (priv->ieee80211->iw_mode == IW_MODE_ADHOC)
+ setKey(dev, 0, 0, priv->ieee80211->group_key_type,
+ CAM_CONST_ADDR[0], 0, NULL);
+ }
+}
+//////////////////////////////////////////////////////////////
+// This function is used to fix Tx/Rx stop bug temporarily.
+// This function will do "system reset" to NIC when Tx or Rx is stuck.
+// The method checking Tx/Rx stuck of this function is supported by FW,
+// which reports Tx and Rx counter to register 0x128 and 0x130.
+//////////////////////////////////////////////////////////////
+static void rtl819x_ifsilentreset(struct net_device *dev)
+{
+ struct r8192_priv *priv = ieee80211_priv(dev);
+ u8 reset_times = 0;
+ int reset_status = 0;
+ struct ieee80211_device *ieee = priv->ieee80211;
+
+
+ // 2007.07.20. If we need to check CCK stop, please uncomment this line.
+ //bStuck = Adapter->HalFunc.CheckHWStopHandler(Adapter);
+
+ if (priv->ResetProgress == RESET_TYPE_NORESET) {
+RESET_START:
+
+ RT_TRACE(COMP_RESET, "=========>Reset progress!! \n");
+
+ // Set the variable for reset.
+ priv->ResetProgress = RESET_TYPE_SILENT;
+ down(&priv->wx_sem);
+ if (priv->up == 0) {
+ RT_TRACE(COMP_ERR, "%s():the driver is not up! return\n", __func__);
+ up(&priv->wx_sem);
+ return;
+ }
+ priv->up = 0;
+ RT_TRACE(COMP_RESET, "%s():======>start to down the driver\n", __func__);
+
+ rtl8192_rtx_disable(dev);
+ rtl8192_cancel_deferred_work(priv);
+ deinit_hal_dm(dev);
+ del_timer_sync(&priv->watch_dog_timer);
+
+ ieee->sync_scan_hurryup = 1;
+ if (ieee->state == IEEE80211_LINKED) {
+ down(&ieee->wx_sem);
+ netdev_dbg(dev, "ieee->state is IEEE80211_LINKED\n");
+ ieee80211_stop_send_beacons(priv->ieee80211);
+ del_timer_sync(&ieee->associate_timer);
+ cancel_delayed_work(&ieee->associate_retry_wq);
+ ieee80211_stop_scan(ieee);
+ netif_carrier_off(dev);
+ up(&ieee->wx_sem);
+ } else {
+ netdev_dbg(dev, "ieee->state is NOT LINKED\n");
+ ieee80211_softmac_stop_protocol(priv->ieee80211);
+ }
+ up(&priv->wx_sem);
+ RT_TRACE(COMP_RESET, "%s():<==========down process is finished\n", __func__);
+ RT_TRACE(COMP_RESET, "%s():===========>start up the driver\n", __func__);
+ reset_status = _rtl8192_up(dev);
+
+ RT_TRACE(COMP_RESET, "%s():<===========up process is finished\n", __func__);
+ if (reset_status == -EAGAIN) {
+ if (reset_times < 3) {
+ reset_times++;
+ goto RESET_START;
+ } else {
+ RT_TRACE(COMP_ERR, " ERR!!! %s(): Reset Failed!!\n", __func__);
+ }
+ }
+ ieee->is_silent_reset = 1;
+ EnableHWSecurityConfig8192(dev);
+ if (ieee->state == IEEE80211_LINKED && ieee->iw_mode == IW_MODE_INFRA) {
+ ieee->set_chan(ieee->dev, ieee->current_network.channel);
+
+ queue_work(ieee->wq, &ieee->associate_complete_wq);
+
+ } else if (ieee->state == IEEE80211_LINKED && ieee->iw_mode == IW_MODE_ADHOC) {
+ ieee->set_chan(ieee->dev, ieee->current_network.channel);
+ ieee->link_change(ieee->dev);
+
+ ieee80211_start_send_beacons(ieee);
+
+ if (ieee->data_hard_resume)
+ ieee->data_hard_resume(ieee->dev);
+ netif_carrier_on(ieee->dev);
+ }
+
+ CamRestoreAllEntry(dev);
+
+ priv->ResetProgress = RESET_TYPE_NORESET;
+ priv->reset_count++;
+
+ priv->bForcedSilentReset = false;
+ priv->bResetInProgress = false;
+
+ // For test --> force write UFWP.
+ write_nic_byte(dev, UFWP, 1);
+ RT_TRACE(COMP_RESET, "Reset finished!! ====>[%d]\n", priv->reset_count);
+ }
+}
+
+static void rtl819x_update_rxcounts(struct r8192_priv *priv, u32 *TotalRxBcnNum,
+ u32 *TotalRxDataNum)
+{
+ u16 SlotIndex;
+ u8 i;
+
+ *TotalRxBcnNum = 0;
+ *TotalRxDataNum = 0;
+
+ SlotIndex = (priv->ieee80211->LinkDetectInfo.SlotIndex++)%(priv->ieee80211->LinkDetectInfo.SlotNum);
+ priv->ieee80211->LinkDetectInfo.RxBcnNum[SlotIndex] = priv->ieee80211->LinkDetectInfo.NumRecvBcnInPeriod;
+ priv->ieee80211->LinkDetectInfo.RxDataNum[SlotIndex] = priv->ieee80211->LinkDetectInfo.NumRecvDataInPeriod;
+ for (i = 0; i < priv->ieee80211->LinkDetectInfo.SlotNum; i++) {
+ *TotalRxBcnNum += priv->ieee80211->LinkDetectInfo.RxBcnNum[i];
+ *TotalRxDataNum += priv->ieee80211->LinkDetectInfo.RxDataNum[i];
+ }
+}
+
+
+void rtl819x_watchdog_wqcallback(struct work_struct *work)
+{
+ struct delayed_work *dwork = container_of(work, struct delayed_work, work);
+ struct r8192_priv *priv = container_of(dwork, struct r8192_priv, watch_dog_wq);
+ struct net_device *dev = priv->ieee80211->dev;
+ struct ieee80211_device *ieee = priv->ieee80211;
+ RESET_TYPE ResetType = RESET_TYPE_NORESET;
+ static u8 check_reset_cnt;
+ bool bBusyTraffic = false;
+ u32 TotalRxBcnNum = 0;
+ u32 TotalRxDataNum = 0;
+
+ if (!priv->up)
+ return;
+ hal_dm_watchdog(dev);
+
+ //to get busy traffic condition
+ if (ieee->state == IEEE80211_LINKED) {
+ if (ieee->LinkDetectInfo.NumRxOkInPeriod > 666 ||
+ ieee->LinkDetectInfo.NumTxOkInPeriod > 666 ) {
+ bBusyTraffic = true;
+ }
+ ieee->LinkDetectInfo.NumRxOkInPeriod = 0;
+ ieee->LinkDetectInfo.NumTxOkInPeriod = 0;
+ ieee->LinkDetectInfo.bBusyTraffic = bBusyTraffic;
+ }
+ //added by amy for AP roaming
+ if (priv->ieee80211->state == IEEE80211_LINKED && priv->ieee80211->iw_mode == IW_MODE_INFRA) {
+
+ rtl819x_update_rxcounts(priv, &TotalRxBcnNum, &TotalRxDataNum);
+ if ((TotalRxBcnNum+TotalRxDataNum) == 0) {
+#ifdef TODO
+ if (rfState == eRfOff)
+ RT_TRACE(COMP_ERR, "========>%s()\n", __func__);
+#endif
+ netdev_dbg(dev, "===>%s(): AP is power off, connect another one\n", __func__);
+ priv->ieee80211->state = IEEE80211_ASSOCIATING;
+ notify_wx_assoc_event(priv->ieee80211);
+ RemovePeerTS(priv->ieee80211, priv->ieee80211->current_network.bssid);
+ priv->ieee80211->link_change(dev);
+ queue_work(priv->ieee80211->wq, &priv->ieee80211->associate_procedure_wq);
+
+ }
+ }
+ priv->ieee80211->LinkDetectInfo.NumRecvBcnInPeriod = 0;
+ priv->ieee80211->LinkDetectInfo.NumRecvDataInPeriod = 0;
+ //check if reset the driver
+ if (check_reset_cnt++ >= 3) {
+ ResetType = rtl819x_ifcheck_resetornot(dev);
+ check_reset_cnt = 3;
+ }
+ if ((priv->force_reset) || (priv->ResetProgress == RESET_TYPE_NORESET &&
+ (priv->bForcedSilentReset ||
+ (!priv->bDisableNormalResetCheck && ResetType == RESET_TYPE_SILENT)))) { /* This is control by OID set in Pomelo */
+ RT_TRACE(COMP_RESET, "%s():priv->force_reset is %d,priv->ResetProgress is %d, priv->bForcedSilentReset is %d,priv->bDisableNormalResetCheck is %d,ResetType is %d\n", __func__, priv->force_reset, priv->ResetProgress, priv->bForcedSilentReset, priv->bDisableNormalResetCheck, ResetType);
+ rtl819x_ifsilentreset(dev);
+ }
+ priv->force_reset = false;
+ priv->bForcedSilentReset = false;
+ priv->bResetInProgress = false;
+ RT_TRACE(COMP_TRACE, " <==RtUsbCheckForHangWorkItemCallback()\n");
+
+}
+
+void watch_dog_timer_callback(unsigned long data)
+{
+ struct r8192_priv *priv = ieee80211_priv((struct net_device *) data);
+ queue_delayed_work(priv->priv_wq, &priv->watch_dog_wq, 0);
+ mod_timer(&priv->watch_dog_timer, jiffies + MSECS(IEEE80211_WATCH_DOG_TIME));
+}
+int _rtl8192_up(struct net_device *dev)
+{
+ struct r8192_priv *priv = ieee80211_priv(dev);
+ int init_status = 0;
+ priv->up = 1;
+ priv->ieee80211->ieee_up = 1;
+ RT_TRACE(COMP_INIT, "Bringing up iface");
+ init_status = rtl8192_adapter_start(dev);
+ if (!init_status) {
+ RT_TRACE(COMP_ERR, "ERR!!! %s(): initialization failed!\n", __func__);
+ priv->up = priv->ieee80211->ieee_up = 0;
+ return -EAGAIN;
+ }
+ RT_TRACE(COMP_INIT, "start adapter finished\n");
+ rtl8192_rx_enable(dev);
+ if (priv->ieee80211->state != IEEE80211_LINKED)
+ ieee80211_softmac_start_protocol(priv->ieee80211);
+ ieee80211_reset_queue(priv->ieee80211);
+ watch_dog_timer_callback((unsigned long) dev);
+ if (!netif_queue_stopped(dev))
+ netif_start_queue(dev);
+ else
+ netif_wake_queue(dev);
+
+ return 0;
+}
+
+
+static int rtl8192_open(struct net_device *dev)
+{
+ struct r8192_priv *priv = ieee80211_priv(dev);
+ int ret;
+ down(&priv->wx_sem);
+ ret = rtl8192_up(dev);
+ up(&priv->wx_sem);
+ return ret;
+
+}
+
+
+int rtl8192_up(struct net_device *dev)
+{
+ struct r8192_priv *priv = ieee80211_priv(dev);
+
+ if (priv->up == 1)
+ return -1;
+
+ return _rtl8192_up(dev);
+}
+
+
+int rtl8192_close(struct net_device *dev)
+{
+ struct r8192_priv *priv = ieee80211_priv(dev);
+ int ret;
+
+ down(&priv->wx_sem);
+
+ ret = rtl8192_down(dev);
+
+ up(&priv->wx_sem);
+
+ return ret;
+
+}
+
+int rtl8192_down(struct net_device *dev)
+{
+ struct r8192_priv *priv = ieee80211_priv(dev);
+ int i;
+
+ if (priv->up == 0)
+ return -1;
+
+ priv->up = 0;
+ priv->ieee80211->ieee_up = 0;
+ RT_TRACE(COMP_DOWN, "==========>%s()\n", __func__);
+ /* FIXME */
+ if (!netif_queue_stopped(dev))
+ netif_stop_queue(dev);
+
+ rtl8192_rtx_disable(dev);
+
+ /* Tx related queue release */
+ for (i = 0; i < MAX_QUEUE_SIZE; i++)
+ skb_queue_purge(&priv->ieee80211->skb_waitQ[i]);
+ for (i = 0; i < MAX_QUEUE_SIZE; i++)
+ skb_queue_purge(&priv->ieee80211->skb_aggQ[i]);
+
+ for (i = 0; i < MAX_QUEUE_SIZE; i++)
+ skb_queue_purge(&priv->ieee80211->skb_drv_aggQ[i]);
+
+ //as cancel_delayed_work will del work->timer, so if work is not defined as struct delayed_work, it will corrupt
+ rtl8192_cancel_deferred_work(priv);
+ deinit_hal_dm(dev);
+ del_timer_sync(&priv->watch_dog_timer);
+
+
+ ieee80211_softmac_stop_protocol(priv->ieee80211);
+ memset(&priv->ieee80211->current_network, 0, offsetof(struct ieee80211_network, list));
+ RT_TRACE(COMP_DOWN, "<==========%s()\n", __func__);
+
+ return 0;
+}
+
+
+void rtl8192_commit(struct net_device *dev)
+{
+ struct r8192_priv *priv = ieee80211_priv(dev);
+ int reset_status = 0;
+ if (priv->up == 0)
+ return;
+ priv->up = 0;
+
+ rtl8192_cancel_deferred_work(priv);
+ del_timer_sync(&priv->watch_dog_timer);
+
+ ieee80211_softmac_stop_protocol(priv->ieee80211);
+
+ rtl8192_rtx_disable(dev);
+ reset_status = _rtl8192_up(dev);
+
+}
+
+void rtl8192_restart(struct work_struct *work)
+{
+ struct r8192_priv *priv = container_of(work, struct r8192_priv, reset_wq);
+ struct net_device *dev = priv->ieee80211->dev;
+
+ down(&priv->wx_sem);
+
+ rtl8192_commit(dev);
+
+ up(&priv->wx_sem);
+}
+
+static void r8192_set_multicast(struct net_device *dev)
+{
+ struct r8192_priv *priv = ieee80211_priv(dev);
+ short promisc;
+
+ /* FIXME FIXME */
+
+ promisc = (dev->flags & IFF_PROMISC) ? 1 : 0;
+
+ if (promisc != priv->promisc)
+
+ priv->promisc = promisc;
+}
+
+
+static int r8192_set_mac_adr(struct net_device *dev, void *mac)
+{
+ struct r8192_priv *priv = ieee80211_priv(dev);
+ struct sockaddr *addr = mac;
+
+ down(&priv->wx_sem);
+
+ memcpy(dev->dev_addr, addr->sa_data, ETH_ALEN);
+
+ schedule_work(&priv->reset_wq);
+ up(&priv->wx_sem);
+
+ return 0;
+}
+
+/* based on ipw2200 driver */
+static int rtl8192_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
+{
+ struct r8192_priv *priv = (struct r8192_priv *)ieee80211_priv(dev);
+ struct iwreq *wrq = (struct iwreq *)rq;
+ int ret = -1;
+ struct ieee80211_device *ieee = priv->ieee80211;
+ u32 key[4];
+ u8 broadcast_addr[6] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
+ struct iw_point *p = &wrq->u.data;
+ struct ieee_param *ipw = NULL;
+
+ down(&priv->wx_sem);
+
+
+ if (p->length < sizeof(struct ieee_param) || !p->pointer) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ ipw = memdup_user(p->pointer, p->length);
+ if (IS_ERR(ipw)) {
+ ret = PTR_ERR(ipw);
+ goto out;
+ }
+
+ switch (cmd) {
+ case RTL_IOCTL_WPA_SUPPLICANT:
+ //parse here for HW security
+ if (ipw->cmd == IEEE_CMD_SET_ENCRYPTION) {
+ if (ipw->u.crypt.set_tx) {
+ if (strcmp(ipw->u.crypt.alg, "CCMP") == 0) {
+ ieee->pairwise_key_type = KEY_TYPE_CCMP;
+ } else if (strcmp(ipw->u.crypt.alg, "TKIP") == 0) {
+ ieee->pairwise_key_type = KEY_TYPE_TKIP;
+ } else if (strcmp(ipw->u.crypt.alg, "WEP") == 0) {
+ if (ipw->u.crypt.key_len == 13)
+ ieee->pairwise_key_type = KEY_TYPE_WEP104;
+ else if (ipw->u.crypt.key_len == 5)
+ ieee->pairwise_key_type = KEY_TYPE_WEP40;
+ } else {
+ ieee->pairwise_key_type = KEY_TYPE_NA;
+ }
+
+ if (ieee->pairwise_key_type) {
+ memcpy((u8 *)key, ipw->u.crypt.key, 16);
+ EnableHWSecurityConfig8192(dev);
+ //we fill both index entry and 4th entry for pairwise key as in IPW interface, adhoc will only get here, so we need index entry for its default key serching!
+ //added by WB.
+ setKey(dev, 4, ipw->u.crypt.idx, ieee->pairwise_key_type, (u8 *)ieee->ap_mac_addr, 0, key);
+ if (ieee->auth_mode != 2)
+ setKey(dev, ipw->u.crypt.idx, ipw->u.crypt.idx, ieee->pairwise_key_type, (u8 *)ieee->ap_mac_addr, 0, key);
+ }
+ } else {
+ memcpy((u8 *)key, ipw->u.crypt.key, 16);
+ if (strcmp(ipw->u.crypt.alg, "CCMP") == 0) {
+ ieee->group_key_type = KEY_TYPE_CCMP;
+ } else if (strcmp(ipw->u.crypt.alg, "TKIP") == 0) {
+ ieee->group_key_type = KEY_TYPE_TKIP;
+ } else if (strcmp(ipw->u.crypt.alg, "WEP") == 0) {
+ if (ipw->u.crypt.key_len == 13)
+ ieee->group_key_type = KEY_TYPE_WEP104;
+ else if (ipw->u.crypt.key_len == 5)
+ ieee->group_key_type = KEY_TYPE_WEP40;
+ } else {
+ ieee->group_key_type = KEY_TYPE_NA;
+ }
+
+ if (ieee->group_key_type) {
+ setKey(dev, ipw->u.crypt.idx,
+ ipw->u.crypt.idx, //KeyIndex
+ ieee->group_key_type, //KeyType
+ broadcast_addr, //MacAddr
+ 0, //DefaultKey
+ key); //KeyContent
+ }
+ }
+ }
+ ret = ieee80211_wpa_supplicant_ioctl(priv->ieee80211, &wrq->u.data);
+ break;
+
+ default:
+ ret = -EOPNOTSUPP;
+ break;
+ }
+ kfree(ipw);
+ ipw = NULL;
+out:
+ up(&priv->wx_sem);
+ return ret;
+}
+
+static u8 HwRateToMRate90(bool bIsHT, u8 rate)
+{
+ u8 ret_rate = 0xff;
+
+ if (!bIsHT) {
+ switch (rate) {
+ case DESC90_RATE1M:
+ ret_rate = MGN_1M;
+ break;
+ case DESC90_RATE2M:
+ ret_rate = MGN_2M;
+ break;
+ case DESC90_RATE5_5M:
+ ret_rate = MGN_5_5M;
+ break;
+ case DESC90_RATE11M:
+ ret_rate = MGN_11M;
+ break;
+ case DESC90_RATE6M:
+ ret_rate = MGN_6M;
+ break;
+ case DESC90_RATE9M:
+ ret_rate = MGN_9M;
+ break;
+ case DESC90_RATE12M:
+ ret_rate = MGN_12M;
+ break;
+ case DESC90_RATE18M:
+ ret_rate = MGN_18M;
+ break;
+ case DESC90_RATE24M:
+ ret_rate = MGN_24M;
+ break;
+ case DESC90_RATE36M:
+ ret_rate = MGN_36M;
+ break;
+ case DESC90_RATE48M:
+ ret_rate = MGN_48M;
+ break;
+ case DESC90_RATE54M:
+ ret_rate = MGN_54M;
+ break;
+
+ default:
+ ret_rate = 0xff;
+ RT_TRACE(COMP_RECV, "HwRateToMRate90(): Non supported Rate [%x], bIsHT = %d!!!\n", rate, bIsHT);
+ break;
+ }
+
+ } else {
+ switch (rate) {
+ case DESC90_RATEMCS0:
+ ret_rate = MGN_MCS0;
+ break;
+ case DESC90_RATEMCS1:
+ ret_rate = MGN_MCS1;
+ break;
+ case DESC90_RATEMCS2:
+ ret_rate = MGN_MCS2;
+ break;
+ case DESC90_RATEMCS3:
+ ret_rate = MGN_MCS3;
+ break;
+ case DESC90_RATEMCS4:
+ ret_rate = MGN_MCS4;
+ break;
+ case DESC90_RATEMCS5:
+ ret_rate = MGN_MCS5;
+ break;
+ case DESC90_RATEMCS6:
+ ret_rate = MGN_MCS6;
+ break;
+ case DESC90_RATEMCS7:
+ ret_rate = MGN_MCS7;
+ break;
+ case DESC90_RATEMCS8:
+ ret_rate = MGN_MCS8;
+ break;
+ case DESC90_RATEMCS9:
+ ret_rate = MGN_MCS9;
+ break;
+ case DESC90_RATEMCS10:
+ ret_rate = MGN_MCS10;
+ break;
+ case DESC90_RATEMCS11:
+ ret_rate = MGN_MCS11;
+ break;
+ case DESC90_RATEMCS12:
+ ret_rate = MGN_MCS12;
+ break;
+ case DESC90_RATEMCS13:
+ ret_rate = MGN_MCS13;
+ break;
+ case DESC90_RATEMCS14:
+ ret_rate = MGN_MCS14;
+ break;
+ case DESC90_RATEMCS15:
+ ret_rate = MGN_MCS15;
+ break;
+ case DESC90_RATEMCS32:
+ ret_rate = 0x80|0x20;
+ break;
+
+ default:
+ ret_rate = 0xff;
+ RT_TRACE(COMP_RECV, "HwRateToMRate90(): Non supported Rate [%x], bIsHT = %d!!!\n", rate, bIsHT);
+ break;
+ }
+ }
+
+ return ret_rate;
+}
+
+/**
+ * Function: UpdateRxPktTimeStamp
+ * Overview: Record the TSF time stamp when receiving a packet
+ *
+ * Input:
+ * PADAPTER Adapter
+ * PRT_RFD pRfd,
+ *
+ * Output:
+ * PRT_RFD pRfd
+ * (pRfd->Status.TimeStampHigh is updated)
+ * (pRfd->Status.TimeStampLow is updated)
+ * Return:
+ * None
+ */
+static void UpdateRxPktTimeStamp8190(struct net_device *dev,
+ struct ieee80211_rx_stats *stats)
+{
+ struct r8192_priv *priv = (struct r8192_priv *)ieee80211_priv(dev);
+
+ if (stats->bIsAMPDU && !stats->bFirstMPDU) {
+ stats->mac_time[0] = priv->LastRxDescTSFLow;
+ stats->mac_time[1] = priv->LastRxDescTSFHigh;
+ } else {
+ priv->LastRxDescTSFLow = stats->mac_time[0];
+ priv->LastRxDescTSFHigh = stats->mac_time[1];
+ }
+}
+
+//by amy 080606
+
+static long rtl819x_translate_todbm(u8 signal_strength_index)// 0-100 index.
+{
+ long signal_power; // in dBm.
+
+ // Translate to dBm (x=0.5y-95).
+ signal_power = (long)((signal_strength_index + 1) >> 1);
+ signal_power -= 95;
+
+ return signal_power;
+}
+
+
+/* 2008/01/22 MH We can not declare RSSI/EVM total value of sliding window to
+ be a local static. Otherwise, it may increase when we return from S3/S4. The
+ value will be kept in memory or disk. Declare the value in the adaptor
+ and it will be reinitialized when returned from S3/S4. */
+static void rtl8192_process_phyinfo(struct r8192_priv *priv, u8 *buffer,
+ struct ieee80211_rx_stats *pprevious_stats,
+ struct ieee80211_rx_stats *pcurrent_stats)
+{
+ bool bcheck = false;
+ u8 rfpath;
+ u32 nspatial_stream, tmp_val;
+ static u32 slide_rssi_index, slide_rssi_statistics;
+ static u32 slide_evm_index, slide_evm_statistics;
+ static u32 last_rssi, last_evm;
+
+ static u32 slide_beacon_adc_pwdb_index, slide_beacon_adc_pwdb_statistics;
+ static u32 last_beacon_adc_pwdb;
+
+ struct ieee80211_hdr_3addr *hdr;
+ u16 sc;
+ unsigned int frag, seq;
+ hdr = (struct ieee80211_hdr_3addr *)buffer;
+ sc = le16_to_cpu(hdr->seq_ctl);
+ frag = WLAN_GET_SEQ_FRAG(sc);
+ seq = WLAN_GET_SEQ_SEQ(sc);
+ //cosa add 04292008 to record the sequence number
+ pcurrent_stats->Seq_Num = seq;
+ //
+ // Check whether we should take the previous packet into accounting
+ //
+ if (!pprevious_stats->bIsAMPDU) {
+ // if previous packet is not aggregated packet
+ bcheck = true;
+ }
+
+ if (slide_rssi_statistics++ >= PHY_RSSI_SLID_WIN_MAX) {
+ slide_rssi_statistics = PHY_RSSI_SLID_WIN_MAX;
+ last_rssi = priv->stats.slide_signal_strength[slide_rssi_index];
+ priv->stats.slide_rssi_total -= last_rssi;
+ }
+ priv->stats.slide_rssi_total += pprevious_stats->SignalStrength;
+
+ priv->stats.slide_signal_strength[slide_rssi_index++] = pprevious_stats->SignalStrength;
+ if (slide_rssi_index >= PHY_RSSI_SLID_WIN_MAX)
+ slide_rssi_index = 0;
+
+ // <1> Showed on UI for user, in dbm
+ tmp_val = priv->stats.slide_rssi_total/slide_rssi_statistics;
+ priv->stats.signal_strength = rtl819x_translate_todbm((u8)tmp_val);
+ pcurrent_stats->rssi = priv->stats.signal_strength;
+ //
+ // If the previous packet does not match the criteria, neglect it
+ //
+ if (!pprevious_stats->bPacketMatchBSSID) {
+ if (!pprevious_stats->bToSelfBA)
+ return;
+ }
+
+ if (!bcheck)
+ return;
+
+
+ //rtl8190_process_cck_rxpathsel(priv,pprevious_stats);//only rtl8190 supported
+
+ //
+ // Check RSSI
+ //
+ priv->stats.num_process_phyinfo++;
+
+ /* record the general signal strength to the sliding window. */
+
+
+ // <2> Showed on UI for engineering
+ // hardware does not provide rssi information for each rf path in CCK
+ if (!pprevious_stats->bIsCCK && (pprevious_stats->bPacketToSelf || pprevious_stats->bToSelfBA)) {
+ for (rfpath = RF90_PATH_A; rfpath < priv->NumTotalRFPath; rfpath++) {
+ if (!rtl8192_phy_CheckIsLegalRFPath(priv->ieee80211->dev, rfpath))
+ continue;
+
+ //Fixed by Jacken 2008-03-20
+ if (priv->stats.rx_rssi_percentage[rfpath] == 0)
+ priv->stats.rx_rssi_percentage[rfpath] = pprevious_stats->RxMIMOSignalStrength[rfpath];
+ if (pprevious_stats->RxMIMOSignalStrength[rfpath] > priv->stats.rx_rssi_percentage[rfpath]) {
+ priv->stats.rx_rssi_percentage[rfpath] =
+ ((priv->stats.rx_rssi_percentage[rfpath]*(Rx_Smooth_Factor-1)) +
+ (pprevious_stats->RxMIMOSignalStrength[rfpath])) /(Rx_Smooth_Factor);
+ priv->stats.rx_rssi_percentage[rfpath] = priv->stats.rx_rssi_percentage[rfpath] + 1;
+ } else {
+ priv->stats.rx_rssi_percentage[rfpath] =
+ ((priv->stats.rx_rssi_percentage[rfpath]*(Rx_Smooth_Factor-1)) +
+ (pprevious_stats->RxMIMOSignalStrength[rfpath])) /(Rx_Smooth_Factor);
+ }
+ RT_TRACE(COMP_DBG, "priv->stats.rx_rssi_percentage[rfPath] = %d \n", priv->stats.rx_rssi_percentage[rfpath]);
+ }
+ }
+
+
+ //
+ // Check PWDB.
+ //
+ RT_TRACE(COMP_RXDESC, "Smooth %s PWDB = %d\n",
+ pprevious_stats->bIsCCK ? "CCK" : "OFDM",
+ pprevious_stats->RxPWDBAll);
+
+ if (pprevious_stats->bPacketBeacon) {
+ /* record the beacon pwdb to the sliding window. */
+ if (slide_beacon_adc_pwdb_statistics++ >= PHY_Beacon_RSSI_SLID_WIN_MAX) {
+ slide_beacon_adc_pwdb_statistics = PHY_Beacon_RSSI_SLID_WIN_MAX;
+ last_beacon_adc_pwdb = priv->stats.Slide_Beacon_pwdb[slide_beacon_adc_pwdb_index];
+ priv->stats.Slide_Beacon_Total -= last_beacon_adc_pwdb;
+ }
+ priv->stats.Slide_Beacon_Total += pprevious_stats->RxPWDBAll;
+ priv->stats.Slide_Beacon_pwdb[slide_beacon_adc_pwdb_index] = pprevious_stats->RxPWDBAll;
+ slide_beacon_adc_pwdb_index++;
+ if (slide_beacon_adc_pwdb_index >= PHY_Beacon_RSSI_SLID_WIN_MAX)
+ slide_beacon_adc_pwdb_index = 0;
+ pprevious_stats->RxPWDBAll = priv->stats.Slide_Beacon_Total/slide_beacon_adc_pwdb_statistics;
+ if (pprevious_stats->RxPWDBAll >= 3)
+ pprevious_stats->RxPWDBAll -= 3;
+ }
+
+ RT_TRACE(COMP_RXDESC, "Smooth %s PWDB = %d\n",
+ pprevious_stats->bIsCCK ? "CCK" : "OFDM",
+ pprevious_stats->RxPWDBAll);
+
+
+ if (pprevious_stats->bPacketToSelf || pprevious_stats->bPacketBeacon || pprevious_stats->bToSelfBA) {
+ if (priv->undecorated_smoothed_pwdb < 0) /* initialize */
+ priv->undecorated_smoothed_pwdb = pprevious_stats->RxPWDBAll;
+ if (pprevious_stats->RxPWDBAll > (u32)priv->undecorated_smoothed_pwdb) {
+ priv->undecorated_smoothed_pwdb =
+ (((priv->undecorated_smoothed_pwdb)*(Rx_Smooth_Factor-1)) +
+ (pprevious_stats->RxPWDBAll)) /(Rx_Smooth_Factor);
+ priv->undecorated_smoothed_pwdb = priv->undecorated_smoothed_pwdb + 1;
+ } else {
+ priv->undecorated_smoothed_pwdb =
+ (((priv->undecorated_smoothed_pwdb)*(Rx_Smooth_Factor-1)) +
+ (pprevious_stats->RxPWDBAll)) /(Rx_Smooth_Factor);
+ }
+
+ }
+
+ //
+ // Check EVM
+ //
+ /* record the general EVM to the sliding window. */
+ if (pprevious_stats->SignalQuality) {
+ if (pprevious_stats->bPacketToSelf || pprevious_stats->bPacketBeacon || pprevious_stats->bToSelfBA) {
+ if (slide_evm_statistics++ >= PHY_RSSI_SLID_WIN_MAX) {
+ slide_evm_statistics = PHY_RSSI_SLID_WIN_MAX;
+ last_evm = priv->stats.slide_evm[slide_evm_index];
+ priv->stats.slide_evm_total -= last_evm;
+ }
+
+ priv->stats.slide_evm_total += pprevious_stats->SignalQuality;
+
+ priv->stats.slide_evm[slide_evm_index++] = pprevious_stats->SignalQuality;
+ if (slide_evm_index >= PHY_RSSI_SLID_WIN_MAX)
+ slide_evm_index = 0;
+
+ // <1> Showed on UI for user, in percentage.
+ tmp_val = priv->stats.slide_evm_total/slide_evm_statistics;
+ priv->stats.signal_quality = tmp_val;
+ //cosa add 10/11/2007, Showed on UI for user in Windows Vista, for Link quality.
+ priv->stats.last_signal_strength_inpercent = tmp_val;
+ }
+
+ // <2> Showed on UI for engineering
+ if (pprevious_stats->bPacketToSelf || pprevious_stats->bPacketBeacon || pprevious_stats->bToSelfBA) {
+ for (nspatial_stream = 0; nspatial_stream < 2; nspatial_stream++) { /* 2 spatial stream */
+ if (pprevious_stats->RxMIMOSignalQuality[nspatial_stream] != -1) {
+ if (priv->stats.rx_evm_percentage[nspatial_stream] == 0) /* initialize */
+ priv->stats.rx_evm_percentage[nspatial_stream] = pprevious_stats->RxMIMOSignalQuality[nspatial_stream];
+ priv->stats.rx_evm_percentage[nspatial_stream] =
+ ((priv->stats.rx_evm_percentage[nspatial_stream]* (Rx_Smooth_Factor-1)) +
+ (pprevious_stats->RxMIMOSignalQuality[nspatial_stream]* 1)) / (Rx_Smooth_Factor);
+ }
+ }
+ }
+ }
+
+
+}
+
+/*-----------------------------------------------------------------------------
+ * Function: rtl819x_query_rxpwrpercentage()
+ *
+ * Overview:
+ *
+ * Input: char antpower
+ *
+ * Output: NONE
+ *
+ * Return: 0-100 percentage
+ *
+ * Revised History:
+ * When Who Remark
+ * 05/26/2008 amy Create Version 0 porting from windows code.
+ *
+ *---------------------------------------------------------------------------*/
+static u8 rtl819x_query_rxpwrpercentage(char antpower)
+{
+ if ((antpower <= -100) || (antpower >= 20))
+ return 0;
+ else if (antpower >= 0)
+ return 100;
+ else
+ return 100 + antpower;
+
+} /* QueryRxPwrPercentage */
+
+static u8 rtl819x_evm_dbtopercentage(char value)
+{
+ char ret_val;
+
+ ret_val = value;
+
+ if (ret_val >= 0)
+ ret_val = 0;
+ if (ret_val <= -33)
+ ret_val = -33;
+ ret_val = 0 - ret_val;
+ ret_val *= 3;
+ if (ret_val == 99)
+ ret_val = 100;
+ return ret_val;
+}
+//
+// Description:
+// We want good-looking for signal strength/quality
+// 2007/7/19 01:09, by cosa.
+//
+static long rtl819x_signal_scale_mapping(long currsig)
+{
+ long retsig;
+
+ // Step 1. Scale mapping.
+ if (currsig >= 61 && currsig <= 100)
+ retsig = 90 + ((currsig - 60) / 4);
+ else if (currsig >= 41 && currsig <= 60)
+ retsig = 78 + ((currsig - 40) / 2);
+ else if (currsig >= 31 && currsig <= 40)
+ retsig = 66 + (currsig - 30);
+ else if (currsig >= 21 && currsig <= 30)
+ retsig = 54 + (currsig - 20);
+ else if (currsig >= 5 && currsig <= 20)
+ retsig = 42 + (((currsig - 5) * 2) / 3);
+ else if (currsig == 4)
+ retsig = 36;
+ else if (currsig == 3)
+ retsig = 27;
+ else if (currsig == 2)
+ retsig = 18;
+ else if (currsig == 1)
+ retsig = 9;
+ else
+ retsig = currsig;
+
+ return retsig;
+}
+
+static inline bool rx_hal_is_cck_rate(struct rx_drvinfo_819x_usb *pdrvinfo)
+{
+ if (pdrvinfo->RxHT)
+ return false;
+
+ switch (pdrvinfo->RxRate) {
+ case DESC90_RATE1M:
+ case DESC90_RATE2M:
+ case DESC90_RATE5_5M:
+ case DESC90_RATE11M:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static void rtl8192_query_rxphystatus(struct r8192_priv *priv,
+ struct ieee80211_rx_stats *pstats,
+ rx_drvinfo_819x_usb *pdrvinfo,
+ struct ieee80211_rx_stats *precord_stats,
+ bool bpacket_match_bssid,
+ bool bpacket_toself,
+ bool bPacketBeacon,
+ bool bToSelfBA)
+{
+ phy_sts_ofdm_819xusb_t *pofdm_buf;
+ phy_sts_cck_819xusb_t *pcck_buf;
+ phy_ofdm_rx_status_rxsc_sgien_exintfflag *prxsc;
+ u8 *prxpkt;
+ u8 i, max_spatial_stream, tmp_rxsnr, tmp_rxevm, rxsc_sgien_exflg;
+ char rx_pwr[4], rx_pwr_all = 0;
+ char rx_snrX, rx_evmX;
+ u8 evm, pwdb_all;
+ u32 RSSI, total_rssi = 0;
+ u8 is_cck_rate = 0;
+ u8 rf_rx_num = 0;
+ u8 sq;
+
+
+ priv->stats.numqry_phystatus++;
+
+ is_cck_rate = rx_hal_is_cck_rate(pdrvinfo);
+
+ // Record it for next packet processing
+ memset(precord_stats, 0, sizeof(struct ieee80211_rx_stats));
+ pstats->bPacketMatchBSSID = precord_stats->bPacketMatchBSSID = bpacket_match_bssid;
+ pstats->bPacketToSelf = precord_stats->bPacketToSelf = bpacket_toself;
+ pstats->bIsCCK = precord_stats->bIsCCK = is_cck_rate;
+ pstats->bPacketBeacon = precord_stats->bPacketBeacon = bPacketBeacon;
+ pstats->bToSelfBA = precord_stats->bToSelfBA = bToSelfBA;
+
+ prxpkt = (u8 *)pdrvinfo;
+
+ /* Move pointer to the 16th bytes. Phy status start address. */
+ prxpkt += sizeof(rx_drvinfo_819x_usb);
+
+ /* Initial the cck and ofdm buffer pointer */
+ pcck_buf = (phy_sts_cck_819xusb_t *)prxpkt;
+ pofdm_buf = (phy_sts_ofdm_819xusb_t *)prxpkt;
+
+ pstats->RxMIMOSignalQuality[0] = -1;
+ pstats->RxMIMOSignalQuality[1] = -1;
+ precord_stats->RxMIMOSignalQuality[0] = -1;
+ precord_stats->RxMIMOSignalQuality[1] = -1;
+
+ if (is_cck_rate) {
+ //
+ // (1)Hardware does not provide RSSI for CCK
+ //
+
+ //
+ // (2)PWDB, Average PWDB cacluated by hardware (for rate adaptive)
+ //
+ u8 report;
+
+ priv->stats.numqry_phystatusCCK++;
+
+ if (!priv->bCckHighPower) {
+ report = pcck_buf->cck_agc_rpt & 0xc0;
+ report >>= 6;
+ switch (report) {
+ //Fixed by Jacken from Bryant 2008-03-20
+ //Original value is -38 , -26 , -14 , -2
+ //Fixed value is -35 , -23 , -11 , 6
+ case 0x3:
+ rx_pwr_all = -35 - (pcck_buf->cck_agc_rpt & 0x3e);
+ break;
+ case 0x2:
+ rx_pwr_all = -23 - (pcck_buf->cck_agc_rpt & 0x3e);
+ break;
+ case 0x1:
+ rx_pwr_all = -11 - (pcck_buf->cck_agc_rpt & 0x3e);
+ break;
+ case 0x0:
+ rx_pwr_all = 6 - (pcck_buf->cck_agc_rpt & 0x3e);
+ break;
+ }
+ } else {
+ report = pcck_buf->cck_agc_rpt & 0x60;
+ report >>= 5;
+ switch (report) {
+ case 0x3:
+ rx_pwr_all = -35 - ((pcck_buf->cck_agc_rpt & 0x1f)<<1);
+ break;
+ case 0x2:
+ rx_pwr_all = -23 - ((pcck_buf->cck_agc_rpt & 0x1f)<<1);
+ break;
+ case 0x1:
+ rx_pwr_all = -11 - ((pcck_buf->cck_agc_rpt & 0x1f)<<1);
+ break;
+ case 0x0:
+ rx_pwr_all = 6 - ((pcck_buf->cck_agc_rpt & 0x1f)<<1);
+ break;
+ }
+ }
+
+ pwdb_all = rtl819x_query_rxpwrpercentage(rx_pwr_all);
+ pstats->RxPWDBAll = precord_stats->RxPWDBAll = pwdb_all;
+ pstats->RecvSignalPower = pwdb_all;
+
+ //
+ // (3) Get Signal Quality (EVM)
+ //
+
+ if (pstats->RxPWDBAll > 40) {
+ sq = 100;
+ } else {
+ sq = pcck_buf->sq_rpt;
+
+ if (pcck_buf->sq_rpt > 64)
+ sq = 0;
+ else if (pcck_buf->sq_rpt < 20)
+ sq = 100;
+ else
+ sq = ((64-sq) * 100) / 44;
+ }
+ pstats->SignalQuality = precord_stats->SignalQuality = sq;
+ pstats->RxMIMOSignalQuality[0] = precord_stats->RxMIMOSignalQuality[0] = sq;
+ pstats->RxMIMOSignalQuality[1] = precord_stats->RxMIMOSignalQuality[1] = -1;
+
+ } else {
+ priv->stats.numqry_phystatusHT++;
+ //
+ // (1)Get RSSI for HT rate
+ //
+ for (i = RF90_PATH_A; i < priv->NumTotalRFPath; i++) {
+ // 2008/01/30 MH we will judge RF RX path now.
+ if (priv->brfpath_rxenable[i])
+ rf_rx_num++;
+ else
+ continue;
+
+ if (!rtl8192_phy_CheckIsLegalRFPath(priv->ieee80211->dev, i))
+ continue;
+
+ //Fixed by Jacken from Bryant 2008-03-20
+ //Original value is 106
+ rx_pwr[i] = ((pofdm_buf->trsw_gain_X[i]&0x3F)*2) - 106;
+
+ //Get Rx snr value in DB
+ tmp_rxsnr = pofdm_buf->rxsnr_X[i];
+ rx_snrX = (char)(tmp_rxsnr);
+ rx_snrX /= 2;
+ priv->stats.rxSNRdB[i] = (long)rx_snrX;
+
+ /* Translate DBM to percentage. */
+ RSSI = rtl819x_query_rxpwrpercentage(rx_pwr[i]);
+ total_rssi += RSSI;
+
+ /* Record Signal Strength for next packet */
+ pstats->RxMIMOSignalStrength[i] = (u8) RSSI;
+ precord_stats->RxMIMOSignalStrength[i] = (u8) RSSI;
+ }
+
+
+ //
+ // (2)PWDB, Average PWDB cacluated by hardware (for rate adaptive)
+ //
+ //Fixed by Jacken from Bryant 2008-03-20
+ //Original value is 106
+ rx_pwr_all = (((pofdm_buf->pwdb_all) >> 1)& 0x7f) -106;
+ pwdb_all = rtl819x_query_rxpwrpercentage(rx_pwr_all);
+
+ pstats->RxPWDBAll = precord_stats->RxPWDBAll = pwdb_all;
+ pstats->RxPower = precord_stats->RxPower = rx_pwr_all;
+
+ //
+ // (3)EVM of HT rate
+ //
+ if (pdrvinfo->RxHT && pdrvinfo->RxRate >= DESC90_RATEMCS8 &&
+ pdrvinfo->RxRate <= DESC90_RATEMCS15)
+ max_spatial_stream = 2; //both spatial stream make sense
+ else
+ max_spatial_stream = 1; //only spatial stream 1 makes sense
+
+ for (i = 0; i < max_spatial_stream; i++) {
+ tmp_rxevm = pofdm_buf->rxevm_X[i];
+ rx_evmX = (char)(tmp_rxevm);
+
+ // Do not use shift operation like "rx_evmX >>= 1" because the compiler of free build environment
+ // will set the most significant bit to "zero" when doing shifting operation which may change a negative
+ // value to positive one, then the dbm value (which is supposed to be negative) is not correct anymore.
+ rx_evmX /= 2; //dbm
+
+ evm = rtl819x_evm_dbtopercentage(rx_evmX);
+ if (i == 0) /* Fill value in RFD, Get the first spatial stream only */
+ pstats->SignalQuality = precord_stats->SignalQuality = (u8)(evm & 0xff);
+ pstats->RxMIMOSignalQuality[i] = precord_stats->RxMIMOSignalQuality[i] = (u8)(evm & 0xff);
+ }
+
+
+ /* record rx statistics for debug */
+ rxsc_sgien_exflg = pofdm_buf->rxsc_sgien_exflg;
+ prxsc = (phy_ofdm_rx_status_rxsc_sgien_exintfflag *)&rxsc_sgien_exflg;
+ if (pdrvinfo->BW) /* 40M channel */
+ priv->stats.received_bwtype[1+prxsc->rxsc]++;
+ else //20M channel
+ priv->stats.received_bwtype[0]++;
+ }
+
+ //UI BSS List signal strength(in percentage), make it good looking, from 0~100.
+ //It is assigned to the BSS List in GetValueFromBeaconOrProbeRsp().
+ if (is_cck_rate) {
+ pstats->SignalStrength = precord_stats->SignalStrength = (u8)(rtl819x_signal_scale_mapping((long)pwdb_all));
+ } else {
+ // We can judge RX path number now.
+ if (rf_rx_num != 0)
+ pstats->SignalStrength = precord_stats->SignalStrength = (u8)(rtl819x_signal_scale_mapping((long)(total_rssi /= rf_rx_num)));
+ }
+} /* QueryRxPhyStatus8190Pci */
+
+static void rtl8192_record_rxdesc_forlateruse(struct ieee80211_rx_stats *psrc_stats,
+ struct ieee80211_rx_stats *ptarget_stats)
+{
+ ptarget_stats->bIsAMPDU = psrc_stats->bIsAMPDU;
+ ptarget_stats->bFirstMPDU = psrc_stats->bFirstMPDU;
+ ptarget_stats->Seq_Num = psrc_stats->Seq_Num;
+}
+
+
+static void TranslateRxSignalStuff819xUsb(struct sk_buff *skb,
+ struct ieee80211_rx_stats *pstats,
+ rx_drvinfo_819x_usb *pdrvinfo)
+{
+ // TODO: We must only check packet for current MAC address. Not finish
+ rtl8192_rx_info *info = (struct rtl8192_rx_info *)skb->cb;
+ struct net_device *dev = info->dev;
+ struct r8192_priv *priv = (struct r8192_priv *)ieee80211_priv(dev);
+ bool bpacket_match_bssid, bpacket_toself;
+ bool bPacketBeacon = false, bToSelfBA = false;
+ static struct ieee80211_rx_stats previous_stats;
+ struct ieee80211_hdr_3addr *hdr;//by amy
+ u16 fc, type;
+
+ // Get Signal Quality for only RX data queue (but not command queue)
+
+ u8 *tmp_buf;
+ u8 *praddr;
+
+ /* Get MAC frame start address. */
+ tmp_buf = (u8 *)skb->data;
+
+ hdr = (struct ieee80211_hdr_3addr *)tmp_buf;
+ fc = le16_to_cpu(hdr->frame_ctl);
+ type = WLAN_FC_GET_TYPE(fc);
+ praddr = hdr->addr1;
+
+ /* Check if the received packet is acceptable. */
+ bpacket_match_bssid = (IEEE80211_FTYPE_CTL != type) &&
+ (eqMacAddr(priv->ieee80211->current_network.bssid, (fc & IEEE80211_FCTL_TODS) ? hdr->addr1 : (fc & IEEE80211_FCTL_FROMDS) ? hdr->addr2 : hdr->addr3))
+ && (!pstats->bHwError) && (!pstats->bCRC) && (!pstats->bICV);
+ bpacket_toself = bpacket_match_bssid & (eqMacAddr(praddr, priv->ieee80211->dev->dev_addr));
+
+ if (WLAN_FC_GET_FRAMETYPE(fc) == IEEE80211_STYPE_BEACON)
+ bPacketBeacon = true;
+ if (WLAN_FC_GET_FRAMETYPE(fc) == IEEE80211_STYPE_BLOCKACK) {
+ if ((eqMacAddr(praddr, dev->dev_addr)))
+ bToSelfBA = true;
+ }
+
+
+
+ if (bpacket_match_bssid)
+ priv->stats.numpacket_matchbssid++;
+ if (bpacket_toself)
+ priv->stats.numpacket_toself++;
+ //
+ // Process PHY information for previous packet (RSSI/PWDB/EVM)
+ //
+ // Because phy information is contained in the last packet of AMPDU only, so driver
+ // should process phy information of previous packet
+ rtl8192_process_phyinfo(priv, tmp_buf, &previous_stats, pstats);
+ rtl8192_query_rxphystatus(priv, pstats, pdrvinfo, &previous_stats, bpacket_match_bssid, bpacket_toself, bPacketBeacon, bToSelfBA);
+ rtl8192_record_rxdesc_forlateruse(pstats, &previous_stats);
+
+}
+
+/**
+* Function: UpdateReceivedRateHistogramStatistics
+* Overview: Record the received data rate
+*
+* Input:
+* struct net_device *dev
+* struct ieee80211_rx_stats *stats
+*
+* Output:
+*
+* (priv->stats.ReceivedRateHistogram[] is updated)
+* Return:
+* None
+*/
+static void
+UpdateReceivedRateHistogramStatistics8190(struct net_device *dev,
+ struct ieee80211_rx_stats *stats)
+{
+ struct r8192_priv *priv = (struct r8192_priv *)ieee80211_priv(dev);
+ u32 rcvType = 1; //0: Total, 1:OK, 2:CRC, 3:ICV
+ u32 rateIndex;
+ u32 preamble_guardinterval; //1: short preamble/GI, 0: long preamble/GI
+
+
+ if (stats->bCRC)
+ rcvType = 2;
+ else if (stats->bICV)
+ rcvType = 3;
+
+ if (stats->bShortPreamble)
+ preamble_guardinterval = 1;// short
+ else
+ preamble_guardinterval = 0;// long
+
+ switch (stats->rate) {
+ /* CCK rate */
+ case MGN_1M:
+ rateIndex = 0;
+ break;
+ case MGN_2M:
+ rateIndex = 1;
+ break;
+ case MGN_5_5M:
+ rateIndex = 2;
+ break;
+ case MGN_11M:
+ rateIndex = 3;
+ break;
+ /* Legacy OFDM rate */
+ case MGN_6M:
+ rateIndex = 4;
+ break;
+ case MGN_9M:
+ rateIndex = 5;
+ break;
+ case MGN_12M:
+ rateIndex = 6;
+ break;
+ case MGN_18M:
+ rateIndex = 7;
+ break;
+ case MGN_24M:
+ rateIndex = 8;
+ break;
+ case MGN_36M:
+ rateIndex = 9;
+ break;
+ case MGN_48M:
+ rateIndex = 10;
+ break;
+ case MGN_54M:
+ rateIndex = 11;
+ break;
+ /* 11n High throughput rate */
+ case MGN_MCS0:
+ rateIndex = 12;
+ break;
+ case MGN_MCS1:
+ rateIndex = 13;
+ break;
+ case MGN_MCS2:
+ rateIndex = 14;
+ break;
+ case MGN_MCS3:
+ rateIndex = 15;
+ break;
+ case MGN_MCS4:
+ rateIndex = 16;
+ break;
+ case MGN_MCS5:
+ rateIndex = 17;
+ break;
+ case MGN_MCS6:
+ rateIndex = 18;
+ break;
+ case MGN_MCS7:
+ rateIndex = 19;
+ break;
+ case MGN_MCS8:
+ rateIndex = 20;
+ break;
+ case MGN_MCS9:
+ rateIndex = 21;
+ break;
+ case MGN_MCS10:
+ rateIndex = 22;
+ break;
+ case MGN_MCS11:
+ rateIndex = 23;
+ break;
+ case MGN_MCS12:
+ rateIndex = 24;
+ break;
+ case MGN_MCS13:
+ rateIndex = 25;
+ break;
+ case MGN_MCS14:
+ rateIndex = 26;
+ break;
+ case MGN_MCS15:
+ rateIndex = 27;
+ break;
+ default:
+ rateIndex = 28;
+ break;
+ }
+ priv->stats.received_preamble_GI[preamble_guardinterval][rateIndex]++;
+ priv->stats.received_rate_histogram[0][rateIndex]++; //total
+ priv->stats.received_rate_histogram[rcvType][rateIndex]++;
+}
+
+
+static void query_rxdesc_status(struct sk_buff *skb,
+ struct ieee80211_rx_stats *stats,
+ bool bIsRxAggrSubframe)
+{
+ rtl8192_rx_info *info = (struct rtl8192_rx_info *)skb->cb;
+ struct net_device *dev = info->dev;
+ struct r8192_priv *priv = (struct r8192_priv *)ieee80211_priv(dev);
+ rx_drvinfo_819x_usb *driver_info = NULL;
+
+ //
+ //Get Rx Descriptor Information
+ //
+ rx_desc_819x_usb *desc = (rx_desc_819x_usb *)skb->data;
+
+ stats->Length = desc->Length;
+ stats->RxDrvInfoSize = desc->RxDrvInfoSize;
+ stats->RxBufShift = 0;
+ stats->bICV = desc->ICV;
+ stats->bCRC = desc->CRC32;
+ stats->bHwError = stats->bCRC|stats->bICV;
+ /* RTL8190 set this bit to indicate that Hw does not decrypt packet */
+ stats->Decrypted = !desc->SWDec;
+
+ if ((priv->ieee80211->pHTInfo->bCurrentHTSupport == true) && (priv->ieee80211->pairwise_key_type == KEY_TYPE_CCMP))
+ stats->bHwError = false;
+ else
+ stats->bHwError = stats->bCRC|stats->bICV;
+
+ if (stats->Length < 24 || stats->Length > MAX_8192U_RX_SIZE)
+ stats->bHwError |= 1;
+ //
+ //Get Driver Info
+ //
+ // TODO: Need to verify it on FGPA platform
+ //Driver info are written to the RxBuffer following rx desc
+ if (stats->RxDrvInfoSize != 0) {
+ driver_info = (rx_drvinfo_819x_usb *)(skb->data + sizeof(rx_desc_819x_usb) +
+ stats->RxBufShift);
+ /* unit: 0.5M */
+ /* TODO */
+ if (!stats->bHwError) {
+ u8 ret_rate;
+ ret_rate = HwRateToMRate90(driver_info->RxHT, driver_info->RxRate);
+ if (ret_rate == 0xff) {
+ // Abnormal Case: Receive CRC OK packet with Rx descriptor indicating non supported rate.
+ // Special Error Handling here, 2008.05.16, by Emily
+
+ stats->bHwError = 1;
+ stats->rate = MGN_1M; //Set 1M rate by default
+ } else {
+ stats->rate = ret_rate;
+ }
+ } else {
+ stats->rate = 0x02;
+ }
+
+ stats->bShortPreamble = driver_info->SPLCP;
+
+
+ UpdateReceivedRateHistogramStatistics8190(dev, stats);
+
+ stats->bIsAMPDU = (driver_info->PartAggr == 1);
+ stats->bFirstMPDU = (driver_info->PartAggr == 1) && (driver_info->FirstAGGR == 1);
+ stats->TimeStampLow = driver_info->TSFL;
+ // xiong mask it, 070514
+
+ UpdateRxPktTimeStamp8190(dev, stats);
+
+ //
+ // Rx A-MPDU
+ //
+ if (driver_info->FirstAGGR == 1 || driver_info->PartAggr == 1)
+ RT_TRACE(COMP_RXDESC, "driver_info->FirstAGGR = %d, driver_info->PartAggr = %d\n",
+ driver_info->FirstAGGR, driver_info->PartAggr);
+
+ }
+
+ skb_pull(skb, sizeof(rx_desc_819x_usb));
+ //
+ // Get Total offset of MPDU Frame Body
+ //
+ if ((stats->RxBufShift + stats->RxDrvInfoSize) > 0) {
+ stats->bShift = 1;
+ skb_pull(skb, stats->RxBufShift + stats->RxDrvInfoSize);
+ }
+
+ if (driver_info) {
+ stats->RxIs40MHzPacket = driver_info->BW;
+ TranslateRxSignalStuff819xUsb(skb, stats, driver_info);
+ }
+}
+
+static void rtl8192_rx_nomal(struct sk_buff *skb)
+{
+ rtl8192_rx_info *info = (struct rtl8192_rx_info *)skb->cb;
+ struct net_device *dev = info->dev;
+ struct r8192_priv *priv = (struct r8192_priv *)ieee80211_priv(dev);
+ struct ieee80211_rx_stats stats = {
+ .signal = 0,
+ .noise = -98,
+ .rate = 0,
+ .freq = IEEE80211_24GHZ_BAND,
+ };
+ u32 rx_pkt_len = 0;
+ struct ieee80211_hdr_1addr *ieee80211_hdr = NULL;
+ bool unicast_packet = false;
+
+ /* 20 is for ps-poll */
+ if ((skb->len >= (20 + sizeof(rx_desc_819x_usb))) && (skb->len < RX_URB_SIZE)) {
+ /* first packet should not contain Rx aggregation header */
+ query_rxdesc_status(skb, &stats, false);
+ /* TODO */
+ /* hardware related info */
+ /* Process the MPDU received */
+ skb_trim(skb, skb->len - 4/*sCrcLng*/);
+
+ rx_pkt_len = skb->len;
+ ieee80211_hdr = (struct ieee80211_hdr_1addr *)skb->data;
+ unicast_packet = false;
+ if (is_broadcast_ether_addr(ieee80211_hdr->addr1)) {
+ //TODO
+ } else if (is_multicast_ether_addr(ieee80211_hdr->addr1)) {
+ //TODO
+ } else {
+ /* unicast packet */
+ unicast_packet = true;
+ }
+
+ if (!ieee80211_rx(priv->ieee80211, skb, &stats)) {
+ dev_kfree_skb_any(skb);
+ } else {
+ priv->stats.rxoktotal++;
+ if (unicast_packet)
+ priv->stats.rxbytesunicast += rx_pkt_len;
+ }
+ } else {
+ priv->stats.rxurberr++;
+ netdev_dbg(dev, "actual_length: %d\n", skb->len);
+ dev_kfree_skb_any(skb);
+ }
+
+}
+
+static void rtl819xusb_process_received_packet(struct net_device *dev,
+ struct ieee80211_rx_stats *pstats)
+{
+ u8 *frame;
+ u16 frame_len = 0;
+ struct r8192_priv *priv = ieee80211_priv(dev);
+
+ // Get shifted bytes of Starting address of 802.11 header. 2006.09.28, by Emily
+ //porting by amy 080508
+ pstats->virtual_address += get_rxpacket_shiftbytes_819xusb(pstats);
+ frame = pstats->virtual_address;
+ frame_len = pstats->packetlength;
+#ifdef TODO // by amy about HCT
+ if (!Adapter->bInHctTest)
+ CountRxErrStatistics(Adapter, pRfd);
+#endif
+#ifdef ENABLE_PS //by amy for adding ps function in future
+ RT_RF_POWER_STATE rtState;
+ // When RF is off, we should not count the packet for hw/sw synchronize
+ // reason, ie. there may be a duration while sw switch is changed and hw
+ // switch is being changed. 2006.12.04, by shien chang.
+ Adapter->HalFunc.GetHwRegHandler(Adapter, HW_VAR_RF_STATE, (u8 *)(&rtState));
+ if (rtState == eRfOff)
+ return;
+#endif
+ priv->stats.rxframgment++;
+
+#ifdef TODO
+ RmMonitorSignalStrength(Adapter, pRfd);
+#endif
+ /* 2007/01/16 MH Add RX command packet handle here. */
+ /* 2007/03/01 MH We have to release RFD and return if rx pkt is cmd pkt. */
+ if (rtl819xusb_rx_command_packet(dev, pstats))
+ return;
+
+#ifdef SW_CRC_CHECK
+ SwCrcCheck();
+#endif
+
+
+}
+
+static void query_rx_cmdpkt_desc_status(struct sk_buff *skb,
+ struct ieee80211_rx_stats *stats)
+{
+ rx_desc_819x_usb *desc = (rx_desc_819x_usb *)skb->data;
+
+ //
+ //Get Rx Descriptor Information
+ //
+ stats->virtual_address = (u8 *)skb->data;
+ stats->Length = desc->Length;
+ stats->RxDrvInfoSize = 0;
+ stats->RxBufShift = 0;
+ stats->packetlength = stats->Length-scrclng;
+ stats->fraglength = stats->packetlength;
+ stats->fragoffset = 0;
+ stats->ntotalfrag = 1;
+}
+
+
+static void rtl8192_rx_cmd(struct sk_buff *skb)
+{
+ struct rtl8192_rx_info *info = (struct rtl8192_rx_info *)skb->cb;
+ struct net_device *dev = info->dev;
+ /* TODO */
+ struct ieee80211_rx_stats stats = {
+ .signal = 0,
+ .noise = -98,
+ .rate = 0,
+ .freq = IEEE80211_24GHZ_BAND,
+ };
+
+ if ((skb->len >= (20 + sizeof(rx_desc_819x_usb))) && (skb->len < RX_URB_SIZE)) {
+
+ query_rx_cmdpkt_desc_status(skb, &stats);
+ // this is to be done by amy 080508 prfd->queue_id = 1;
+
+
+ //
+ // Process the command packet received.
+ //
+
+ rtl819xusb_process_received_packet(dev, &stats);
+
+ dev_kfree_skb_any(skb);
+ }
+}
+
+void rtl8192_irq_rx_tasklet(struct r8192_priv *priv)
+{
+ struct sk_buff *skb;
+ struct rtl8192_rx_info *info;
+
+ while (NULL != (skb = skb_dequeue(&priv->skb_queue))) {
+ info = (struct rtl8192_rx_info *)skb->cb;
+ switch (info->out_pipe) {
+ /* Nomal packet pipe */
+ case 3:
+ priv->IrpPendingCount--;
+ rtl8192_rx_nomal(skb);
+ break;
+
+ /* Command packet pipe */
+ case 9:
+ RT_TRACE(COMP_RECV, "command in-pipe index(%d)\n",
+ info->out_pipe);
+
+ rtl8192_rx_cmd(skb);
+ break;
+
+ default: /* should never get here! */
+ RT_TRACE(COMP_ERR, "Unknown in-pipe index(%d)\n",
+ info->out_pipe);
+ dev_kfree_skb(skb);
+ break;
+
+ }
+ }
+}
+
+static const struct net_device_ops rtl8192_netdev_ops = {
+ .ndo_open = rtl8192_open,
+ .ndo_stop = rtl8192_close,
+ .ndo_get_stats = rtl8192_stats,
+ .ndo_tx_timeout = tx_timeout,
+ .ndo_do_ioctl = rtl8192_ioctl,
+ .ndo_set_rx_mode = r8192_set_multicast,
+ .ndo_set_mac_address = r8192_set_mac_adr,
+ .ndo_validate_addr = eth_validate_addr,
+ .ndo_change_mtu = eth_change_mtu,
+ .ndo_start_xmit = ieee80211_xmit,
+};
+
+
+/****************************************************************************
+ ---------------------------- USB_STUFF---------------------------
+*****************************************************************************/
+
+static int rtl8192_usb_probe(struct usb_interface *intf,
+ const struct usb_device_id *id)
+{
+ struct net_device *dev = NULL;
+ struct r8192_priv *priv = NULL;
+ struct usb_device *udev = interface_to_usbdev(intf);
+ int ret;
+ RT_TRACE(COMP_INIT, "Oops: i'm coming\n");
+
+ dev = alloc_ieee80211(sizeof(struct r8192_priv));
+ if (dev == NULL)
+ return -ENOMEM;
+
+ usb_set_intfdata(intf, dev);
+ SET_NETDEV_DEV(dev, &intf->dev);
+ priv = ieee80211_priv(dev);
+ priv->ieee80211 = netdev_priv(dev);
+ priv->udev = udev;
+
+ dev->netdev_ops = &rtl8192_netdev_ops;
+
+ dev->wireless_handlers = (struct iw_handler_def *) &r8192_wx_handlers_def;
+
+ dev->type = ARPHRD_ETHER;
+
+ dev->watchdog_timeo = HZ*3; //modified by john, 0805
+
+ if (dev_alloc_name(dev, ifname) < 0) {
+ RT_TRACE(COMP_INIT, "Oops: devname already taken! Trying wlan%%d...\n");
+ ifname = "wlan%d";
+ dev_alloc_name(dev, ifname);
+ }
+
+ RT_TRACE(COMP_INIT, "Driver probe completed1\n");
+ if (rtl8192_init(dev) != 0) {
+ RT_TRACE(COMP_ERR, "Initialization failed");
+ ret = -ENODEV;
+ goto fail;
+ }
+ netif_carrier_off(dev);
+ netif_stop_queue(dev);
+
+ ret = register_netdev(dev);
+ if (ret)
+ goto fail2;
+
+ RT_TRACE(COMP_INIT, "dev name=======> %s\n", dev->name);
+ rtl8192_proc_init_one(dev);
+
+
+ RT_TRACE(COMP_INIT, "Driver probe completed\n");
+ return 0;
+
+fail2:
+ rtl8192_down(dev);
+ kfree(priv->pFirmware);
+ priv->pFirmware = NULL;
+ rtl8192_usb_deleteendpoints(dev);
+ destroy_workqueue(priv->priv_wq);
+ mdelay(10);
+fail:
+ free_ieee80211(dev);
+
+ RT_TRACE(COMP_ERR, "wlan driver load failed\n");
+ return ret;
+}
+
+//detach all the work and timer structure declared or inititialize in r8192U_init function.
+void rtl8192_cancel_deferred_work(struct r8192_priv *priv)
+{
+
+ cancel_work_sync(&priv->reset_wq);
+ cancel_delayed_work(&priv->watch_dog_wq);
+ cancel_delayed_work(&priv->update_beacon_wq);
+ cancel_work_sync(&priv->qos_activate);
+}
+
+
+static void rtl8192_usb_disconnect(struct usb_interface *intf)
+{
+ struct net_device *dev = usb_get_intfdata(intf);
+
+ struct r8192_priv *priv = ieee80211_priv(dev);
+ if (dev) {
+
+ unregister_netdev(dev);
+
+ RT_TRACE(COMP_DOWN, "=============>wlan driver to be removed\n");
+ rtl8192_proc_remove_one(dev);
+
+ rtl8192_down(dev);
+ kfree(priv->pFirmware);
+ priv->pFirmware = NULL;
+ rtl8192_usb_deleteendpoints(dev);
+ destroy_workqueue(priv->priv_wq);
+ mdelay(10);
+
+ }
+ free_ieee80211(dev);
+ RT_TRACE(COMP_DOWN, "wlan driver removed\n");
+}
+
+static int __init rtl8192_usb_module_init(void)
+{
+ int ret;
+
+#ifdef CONFIG_IEEE80211_DEBUG
+ ret = ieee80211_debug_init();
+ if (ret) {
+ pr_err("ieee80211_debug_init() failed %d\n", ret);
+ return ret;
+ }
+#endif
+ ret = ieee80211_crypto_init();
+ if (ret) {
+ pr_err("ieee80211_crypto_init() failed %d\n", ret);
+ return ret;
+ }
+
+ ret = ieee80211_crypto_tkip_init();
+ if (ret) {
+ pr_err("ieee80211_crypto_tkip_init() failed %d\n", ret);
+ return ret;
+ }
+
+ ret = ieee80211_crypto_ccmp_init();
+ if (ret) {
+ pr_err("ieee80211_crypto_ccmp_init() failed %d\n", ret);
+ return ret;
+ }
+
+ ret = ieee80211_crypto_wep_init();
+ if (ret) {
+ pr_err("ieee80211_crypto_wep_init() failed %d\n", ret);
+ return ret;
+ }
+
+ pr_info("\nLinux kernel driver for RTL8192 based WLAN cards\n");
+ pr_info("Copyright (c) 2007-2008, Realsil Wlan\n");
+ RT_TRACE(COMP_INIT, "Initializing module");
+ RT_TRACE(COMP_INIT, "Wireless extensions version %d", WIRELESS_EXT);
+ rtl8192_proc_module_init();
+ return usb_register(&rtl8192_usb_driver);
+}
+
+
+static void __exit rtl8192_usb_module_exit(void)
+{
+ usb_deregister(&rtl8192_usb_driver);
+
+ RT_TRACE(COMP_DOWN, "Exiting");
+}
+
+
+void rtl8192_try_wake_queue(struct net_device *dev, int pri)
+{
+ unsigned long flags;
+ short enough_desc;
+ struct r8192_priv *priv = (struct r8192_priv *)ieee80211_priv(dev);
+
+ spin_lock_irqsave(&priv->tx_lock, flags);
+ enough_desc = check_nic_enough_desc(dev, pri);
+ spin_unlock_irqrestore(&priv->tx_lock, flags);
+
+ if (enough_desc)
+ ieee80211_wake_queue(priv->ieee80211);
+}
+
+void EnableHWSecurityConfig8192(struct net_device *dev)
+{
+ u8 SECR_value = 0x0;
+ struct r8192_priv *priv = (struct r8192_priv *)ieee80211_priv(dev);
+ struct ieee80211_device *ieee = priv->ieee80211;
+ SECR_value = SCR_TxEncEnable | SCR_RxDecEnable;
+ if (((KEY_TYPE_WEP40 == ieee->pairwise_key_type) || (KEY_TYPE_WEP104 == ieee->pairwise_key_type)) && (priv->ieee80211->auth_mode != 2)) {
+ SECR_value |= SCR_RxUseDK;
+ SECR_value |= SCR_TxUseDK;
+ } else if ((ieee->iw_mode == IW_MODE_ADHOC) && (ieee->pairwise_key_type & (KEY_TYPE_CCMP | KEY_TYPE_TKIP))) {
+ SECR_value |= SCR_RxUseDK;
+ SECR_value |= SCR_TxUseDK;
+ }
+ //add HWSec active enable here.
+ //default using hwsec. when peer AP is in N mode only and pairwise_key_type is none_aes(which HT_IOT_ACT_PURE_N_MODE indicates it), use software security. when peer AP is in b,g,n mode mixed and pairwise_key_type is none_aes, use g mode hw security. WB on 2008.7.4
+
+ ieee->hwsec_active = 1;
+
+ if ((ieee->pHTInfo->IOTAction&HT_IOT_ACT_PURE_N_MODE) || !hwwep) { /* add hwsec_support flag to totol control hw_sec on/off */
+ ieee->hwsec_active = 0;
+ SECR_value &= ~SCR_RxDecEnable;
+ }
+ RT_TRACE(COMP_SEC, "%s:, hwsec:%d, pairwise_key:%d, SECR_value:%x\n", __func__,
+ ieee->hwsec_active, ieee->pairwise_key_type, SECR_value);
+ write_nic_byte(dev, SECR, SECR_value);
+}
+
+
+void setKey(struct net_device *dev, u8 EntryNo, u8 KeyIndex, u16 KeyType,
+ u8 *MacAddr, u8 DefaultKey, u32 *KeyContent)
+{
+ u32 TargetCommand = 0;
+ u32 TargetContent = 0;
+ u16 usConfig = 0;
+ u8 i;
+ if (EntryNo >= TOTAL_CAM_ENTRY)
+ RT_TRACE(COMP_ERR, "cam entry exceeds in setKey()\n");
+
+ RT_TRACE(COMP_SEC, "====>to setKey(), dev:%p, EntryNo:%d, KeyIndex:%d, KeyType:%d, MacAddr%pM\n", dev, EntryNo, KeyIndex, KeyType, MacAddr);
+
+ if (DefaultKey)
+ usConfig |= BIT15 | (KeyType<<2);
+ else
+ usConfig |= BIT15 | (KeyType<<2) | KeyIndex;
+
+
+ for (i = 0; i < CAM_CONTENT_COUNT; i++) {
+ TargetCommand = i+CAM_CONTENT_COUNT*EntryNo;
+ TargetCommand |= BIT31|BIT16;
+
+ if (i == 0) { /* MAC|Config */
+ TargetContent = (u32)(*(MacAddr+0)) << 16|
+ (u32)(*(MacAddr+1)) << 24|
+ (u32)usConfig;
+
+ write_nic_dword(dev, WCAMI, TargetContent);
+ write_nic_dword(dev, RWCAM, TargetCommand);
+ } else if (i == 1) { /* MAC */
+ TargetContent = (u32)(*(MacAddr+2)) |
+ (u32)(*(MacAddr+3)) << 8|
+ (u32)(*(MacAddr+4)) << 16|
+ (u32)(*(MacAddr+5)) << 24;
+ write_nic_dword(dev, WCAMI, TargetContent);
+ write_nic_dword(dev, RWCAM, TargetCommand);
+ } else {
+ //Key Material
+ if (KeyContent != NULL) {
+ write_nic_dword(dev, WCAMI, (u32)(*(KeyContent+i-2)));
+ write_nic_dword(dev, RWCAM, TargetCommand);
+ }
+ }
+ }
+
+}
+
+/***************************************************************************
+ ------------------- module init / exit stubs ----------------
+****************************************************************************/
+module_init(rtl8192_usb_module_init);
+module_exit(rtl8192_usb_module_exit);
diff --git a/drivers/staging/rtl8192u/r8192U_dm.c b/drivers/staging/rtl8192u/r8192U_dm.c
new file mode 100644
index 000000000..12dd19e11
--- /dev/null
+++ b/drivers/staging/rtl8192u/r8192U_dm.c
@@ -0,0 +1,3125 @@
+/*++
+Copyright-c Realtek Semiconductor Corp. All rights reserved.
+
+Module Name:
+ r8192U_dm.c
+
+Abstract:
+ HW dynamic mechanism.
+
+Major Change History:
+ When Who What
+ ---------- --------------- -------------------------------
+ 2008-05-14 amy create version 0 porting from windows code.
+
+--*/
+#include "r8192U.h"
+#include "r8192U_dm.h"
+#include "r8192U_hw.h"
+#include "r819xU_phy.h"
+#include "r819xU_phyreg.h"
+#include "r8190_rtl8256.h"
+#include "r819xU_cmdpkt.h"
+/*---------------------------Define Local Constant---------------------------*/
+/* Indicate different AP vendor for IOT issue. */
+static u32 edca_setting_DL[HT_IOT_PEER_MAX] = {
+ 0x5e4322, 0x5e4322, 0x5e4322, 0x604322, 0x00a44f, 0x5ea44f
+};
+static u32 edca_setting_UL[HT_IOT_PEER_MAX] = {
+ 0x5e4322, 0x00a44f, 0x5e4322, 0x604322, 0x5ea44f, 0x5ea44f
+};
+
+#define RTK_UL_EDCA 0xa44f
+#define RTK_DL_EDCA 0x5e4322
+/*---------------------------Define Local Constant---------------------------*/
+
+
+/*------------------------Define global variable-----------------------------*/
+/* Debug variable ? */
+struct dig dm_digtable;
+/* Store current software write register content for MAC PHY. */
+u8 dm_shadow[16][256] = { {0} };
+/* For Dynamic Rx Path Selection by Signal Strength */
+struct dynamic_rx_path_sel DM_RxPathSelTable;
+
+/*------------------------Define global variable-----------------------------*/
+
+
+/*------------------------Define local variable------------------------------*/
+/*------------------------Define local variable------------------------------*/
+
+
+/*--------------------Define export function prototype-----------------------*/
+extern void dm_check_fsync(struct net_device *dev);
+
+/*--------------------Define export function prototype-----------------------*/
+
+
+/*---------------------Define local function prototype-----------------------*/
+/* DM --> Rate Adaptive */
+static void dm_check_rate_adaptive(struct net_device *dev);
+
+/* DM --> Bandwidth switch */
+static void dm_init_bandwidth_autoswitch(struct net_device *dev);
+static void dm_bandwidth_autoswitch(struct net_device *dev);
+
+/* DM --> TX power control */
+/*static void dm_initialize_txpower_tracking(struct net_device *dev);*/
+
+static void dm_check_txpower_tracking(struct net_device *dev);
+
+/*static void dm_txpower_reset_recovery(struct net_device *dev);*/
+
+/* DM --> Dynamic Init Gain by RSSI */
+static void dm_dig_init(struct net_device *dev);
+static void dm_ctrl_initgain_byrssi(struct net_device *dev);
+static void dm_ctrl_initgain_byrssi_highpwr(struct net_device *dev);
+static void dm_ctrl_initgain_byrssi_by_driverrssi(struct net_device *dev);
+static void dm_ctrl_initgain_byrssi_by_fwfalse_alarm(struct net_device *dev);
+static void dm_initial_gain(struct net_device *dev);
+static void dm_pd_th(struct net_device *dev);
+static void dm_cs_ratio(struct net_device *dev);
+
+static void dm_init_ctstoself(struct net_device *dev);
+/* DM --> EDCA turbo mode control */
+static void dm_check_edca_turbo(struct net_device *dev);
+
+/*static void dm_gpio_change_rf(struct net_device *dev);*/
+/* DM --> Check PBC */
+static void dm_check_pbc_gpio(struct net_device *dev);
+
+/* DM --> Check current RX RF path state */
+static void dm_check_rx_path_selection(struct net_device *dev);
+static void dm_init_rxpath_selection(struct net_device *dev);
+static void dm_rxpath_sel_byrssi(struct net_device *dev);
+
+/* DM --> Fsync for broadcom ap */
+static void dm_init_fsync(struct net_device *dev);
+static void dm_deInit_fsync(struct net_device *dev);
+
+/* Added by vivi, 20080522 */
+static void dm_check_txrateandretrycount(struct net_device *dev);
+
+/*---------------------Define local function prototype-----------------------*/
+
+/*---------------------Define of Tx Power Control For Near/Far Range --------*/ /*Add by Jacken 2008/02/18 */
+static void dm_init_dynamic_txpower(struct net_device *dev);
+static void dm_dynamic_txpower(struct net_device *dev);
+
+/* DM --> For rate adaptive and DIG, we must send RSSI to firmware */
+static void dm_send_rssi_tofw(struct net_device *dev);
+static void dm_ctstoself(struct net_device *dev);
+/*---------------------------Define function prototype------------------------*/
+/*
+ * ================================================================================
+ * HW Dynamic mechanism interface.
+ * ================================================================================
+ *
+ *
+ * Description:
+ * Prepare SW resource for HW dynamic mechanism.
+ *
+ * Assumption:
+ * This function is only invoked at driver intialization once.
+ */
+void init_hal_dm(struct net_device *dev)
+{
+ struct r8192_priv *priv = ieee80211_priv(dev);
+
+ /* Undecorated Smoothed Signal Strength, it can utilized to dynamic mechanism. */
+ priv->undecorated_smoothed_pwdb = -1;
+
+ /* Initial TX Power Control for near/far range , add by amy 2008/05/15, porting from windows code. */
+ dm_init_dynamic_txpower(dev);
+ init_rate_adaptive(dev);
+ /*dm_initialize_txpower_tracking(dev);*/
+ dm_dig_init(dev);
+ dm_init_edca_turbo(dev);
+ dm_init_bandwidth_autoswitch(dev);
+ dm_init_fsync(dev);
+ dm_init_rxpath_selection(dev);
+ dm_init_ctstoself(dev);
+
+} /* InitHalDm */
+
+void deinit_hal_dm(struct net_device *dev)
+{
+ dm_deInit_fsync(dev);
+}
+
+#ifdef USB_RX_AGGREGATION_SUPPORT
+void dm_CheckRxAggregation(struct net_device *dev)
+{
+ struct r8192_priv *priv = ieee80211_priv((struct net_device *)dev);
+ PRT_HIGH_THROUGHPUT pHTInfo = priv->ieee80211->pHTInfo;
+ static unsigned long lastTxOkCnt;
+ static unsigned long lastRxOkCnt;
+ unsigned long curTxOkCnt = 0;
+ unsigned long curRxOkCnt = 0;
+
+/*
+ if (pHalData->bForcedUsbRxAggr) {
+ if (pHalData->ForcedUsbRxAggrInfo == 0) {
+ if (pHalData->bCurrentRxAggrEnable) {
+ Adapter->HalFunc.HalUsbRxAggrHandler(Adapter, FALSE);
+ }
+ } else {
+ if (!pHalData->bCurrentRxAggrEnable || (pHalData->ForcedUsbRxAggrInfo != pHalData->LastUsbRxAggrInfoSetting)) {
+ Adapter->HalFunc.HalUsbRxAggrHandler(Adapter, TRUE);
+ }
+ }
+ return;
+ }
+
+*/
+ curTxOkCnt = priv->stats.txbytesunicast - lastTxOkCnt;
+ curRxOkCnt = priv->stats.rxbytesunicast - lastRxOkCnt;
+
+ if ((curTxOkCnt + curRxOkCnt) < 15000000)
+ return;
+
+ if (curTxOkCnt > 4*curRxOkCnt) {
+ if (priv->bCurrentRxAggrEnable) {
+ write_nic_dword(dev, 0x1a8, 0);
+ priv->bCurrentRxAggrEnable = false;
+ }
+ } else {
+ if (!priv->bCurrentRxAggrEnable && !pHTInfo->bCurrentRT2RTAggregation) {
+ u32 ulValue;
+
+ ulValue = (pHTInfo->UsbRxFwAggrEn<<24) | (pHTInfo->UsbRxFwAggrPageNum<<16) |
+ (pHTInfo->UsbRxFwAggrPacketNum<<8) | (pHTInfo->UsbRxFwAggrTimeout);
+ /*
+ * If usb rx firmware aggregation is enabled,
+ * when anyone of three threshold conditions above is reached,
+ * firmware will send aggregated packet to driver.
+ */
+ write_nic_dword(dev, 0x1a8, ulValue);
+ priv->bCurrentRxAggrEnable = true;
+ }
+ }
+
+ lastTxOkCnt = priv->stats.txbytesunicast;
+ lastRxOkCnt = priv->stats.rxbytesunicast;
+} /* dm_CheckEdcaTurbo */
+#endif
+
+void hal_dm_watchdog(struct net_device *dev)
+{
+ /*struct r8192_priv *priv = ieee80211_priv(dev);*/
+
+ /*static u8 previous_bssid[6] ={0};*/
+
+ /*Add by amy 2008/05/15 ,porting from windows code.*/
+ dm_check_rate_adaptive(dev);
+ dm_dynamic_txpower(dev);
+ dm_check_txrateandretrycount(dev);
+ dm_check_txpower_tracking(dev);
+ dm_ctrl_initgain_byrssi(dev);
+ dm_check_edca_turbo(dev);
+ dm_bandwidth_autoswitch(dev);
+ dm_check_rx_path_selection(dev);
+ dm_check_fsync(dev);
+
+ /* Add by amy 2008-05-15 porting from windows code. */
+ dm_check_pbc_gpio(dev);
+ dm_send_rssi_tofw(dev);
+ dm_ctstoself(dev);
+#ifdef USB_RX_AGGREGATION_SUPPORT
+ dm_CheckRxAggregation(dev);
+#endif
+} /* HalDmWatchDog */
+
+/*
+ * Decide Rate Adaptive Set according to distance (signal strength)
+ * 01/11/2008 MHC Modify input arguments and RATR table level.
+ * 01/16/2008 MHC RF_Type is assigned in ReadAdapterInfo(). We must call
+ * the function after making sure RF_Type.
+ */
+void init_rate_adaptive(struct net_device *dev)
+{
+ struct r8192_priv *priv = ieee80211_priv(dev);
+ prate_adaptive pra = (prate_adaptive)&priv->rate_adaptive;
+
+ pra->ratr_state = DM_RATR_STA_MAX;
+ pra->high2low_rssi_thresh_for_ra = RateAdaptiveTH_High;
+ pra->low2high_rssi_thresh_for_ra20M = RateAdaptiveTH_Low_20M+5;
+ pra->low2high_rssi_thresh_for_ra40M = RateAdaptiveTH_Low_40M+5;
+
+ pra->high_rssi_thresh_for_ra = RateAdaptiveTH_High+5;
+ pra->low_rssi_thresh_for_ra20M = RateAdaptiveTH_Low_20M;
+ pra->low_rssi_thresh_for_ra40M = RateAdaptiveTH_Low_40M;
+
+ if (priv->CustomerID == RT_CID_819x_Netcore)
+ pra->ping_rssi_enable = 1;
+ else
+ pra->ping_rssi_enable = 0;
+ pra->ping_rssi_thresh_for_ra = 15;
+
+ if (priv->rf_type == RF_2T4R) {
+ /*
+ * 07/10/08 MH Modify for RA smooth scheme.
+ * 2008/01/11 MH Modify 2T RATR table for different RSSI. 080515 porting by amy from windows code.
+ */
+ pra->upper_rssi_threshold_ratr = 0x8f0f0000;
+ pra->middle_rssi_threshold_ratr = 0x8f0ff000;
+ pra->low_rssi_threshold_ratr = 0x8f0ff001;
+ pra->low_rssi_threshold_ratr_40M = 0x8f0ff005;
+ pra->low_rssi_threshold_ratr_20M = 0x8f0ff001;
+ pra->ping_rssi_ratr = 0x0000000d;/* cosa add for test */
+ } else if (priv->rf_type == RF_1T2R) {
+ pra->upper_rssi_threshold_ratr = 0x000f0000;
+ pra->middle_rssi_threshold_ratr = 0x000ff000;
+ pra->low_rssi_threshold_ratr = 0x000ff001;
+ pra->low_rssi_threshold_ratr_40M = 0x000ff005;
+ pra->low_rssi_threshold_ratr_20M = 0x000ff001;
+ pra->ping_rssi_ratr = 0x0000000d;/* cosa add for test */
+ }
+
+} /* InitRateAdaptive */
+
+/*-----------------------------------------------------------------------------
+ * Function: dm_check_rate_adaptive()
+ *
+ * Overview:
+ *
+ * Input: NONE
+ *
+ * Output: NONE
+ *
+ * Return: NONE
+ *
+ * Revised History:
+ * When Who Remark
+ * 05/26/08 amy Create version 0 porting from windows code.
+ *
+ *---------------------------------------------------------------------------*/
+static void dm_check_rate_adaptive(struct net_device *dev)
+{
+ struct r8192_priv *priv = ieee80211_priv(dev);
+ PRT_HIGH_THROUGHPUT pHTInfo = priv->ieee80211->pHTInfo;
+ prate_adaptive pra = (prate_adaptive)&priv->rate_adaptive;
+ u32 currentRATR, targetRATR = 0;
+ u32 LowRSSIThreshForRA = 0, HighRSSIThreshForRA = 0;
+ bool bshort_gi_enabled = false;
+ static u8 ping_rssi_state;
+
+ if (!priv->up) {
+ RT_TRACE(COMP_RATE, "<---- dm_check_rate_adaptive(): driver is going to unload\n");
+ return;
+ }
+
+ if (pra->rate_adaptive_disabled) /* this variable is set by ioctl. */
+ return;
+
+ /* TODO: Only 11n mode is implemented currently, */
+ if (!(priv->ieee80211->mode == WIRELESS_MODE_N_24G ||
+ priv->ieee80211->mode == WIRELESS_MODE_N_5G))
+ return;
+
+ if (priv->ieee80211->state == IEEE80211_LINKED) {
+ /*RT_TRACE(COMP_RATE, "dm_CheckRateAdaptive(): \t");*/
+
+ /* Check whether Short GI is enabled */
+ bshort_gi_enabled = (pHTInfo->bCurTxBW40MHz && pHTInfo->bCurShortGI40MHz) ||
+ (!pHTInfo->bCurTxBW40MHz && pHTInfo->bCurShortGI20MHz);
+
+ pra->upper_rssi_threshold_ratr =
+ (pra->upper_rssi_threshold_ratr & (~BIT31)) | ((bshort_gi_enabled) ? BIT31:0);
+
+ pra->middle_rssi_threshold_ratr =
+ (pra->middle_rssi_threshold_ratr & (~BIT31)) | ((bshort_gi_enabled) ? BIT31:0);
+
+ if (priv->CurrentChannelBW != HT_CHANNEL_WIDTH_20) {
+ pra->low_rssi_threshold_ratr =
+ (pra->low_rssi_threshold_ratr_40M & (~BIT31)) | ((bshort_gi_enabled) ? BIT31:0);
+ } else {
+ pra->low_rssi_threshold_ratr =
+ (pra->low_rssi_threshold_ratr_20M & (~BIT31)) | ((bshort_gi_enabled) ? BIT31:0);
+ }
+ /* cosa add for test */
+ pra->ping_rssi_ratr =
+ (pra->ping_rssi_ratr & (~BIT31)) | ((bshort_gi_enabled) ? BIT31:0);
+
+ /* 2007/10/08 MH We support RA smooth scheme now. When it is the first
+ time to link with AP. We will not change upper/lower threshold. If
+ STA stay in high or low level, we must change two different threshold
+ to prevent jumping frequently. */
+ if (pra->ratr_state == DM_RATR_STA_HIGH) {
+ HighRSSIThreshForRA = pra->high2low_rssi_thresh_for_ra;
+ LowRSSIThreshForRA = (priv->CurrentChannelBW != HT_CHANNEL_WIDTH_20) ?
+ (pra->low_rssi_thresh_for_ra40M):(pra->low_rssi_thresh_for_ra20M);
+ } else if (pra->ratr_state == DM_RATR_STA_LOW) {
+ HighRSSIThreshForRA = pra->high_rssi_thresh_for_ra;
+ LowRSSIThreshForRA = (priv->CurrentChannelBW != HT_CHANNEL_WIDTH_20) ?
+ (pra->low2high_rssi_thresh_for_ra40M):(pra->low2high_rssi_thresh_for_ra20M);
+ } else {
+ HighRSSIThreshForRA = pra->high_rssi_thresh_for_ra;
+ LowRSSIThreshForRA = (priv->CurrentChannelBW != HT_CHANNEL_WIDTH_20) ?
+ (pra->low_rssi_thresh_for_ra40M):(pra->low_rssi_thresh_for_ra20M);
+ }
+
+ /*DbgPrint("[DM] THresh H/L=%d/%d\n\r", RATR.HighRSSIThreshForRA, RATR.LowRSSIThreshForRA);*/
+ if (priv->undecorated_smoothed_pwdb >= (long)HighRSSIThreshForRA) {
+ /*DbgPrint("[DM] RSSI=%d STA=HIGH\n\r", pHalData->UndecoratedSmoothedPWDB);*/
+ pra->ratr_state = DM_RATR_STA_HIGH;
+ targetRATR = pra->upper_rssi_threshold_ratr;
+ } else if (priv->undecorated_smoothed_pwdb >= (long)LowRSSIThreshForRA) {
+ /*DbgPrint("[DM] RSSI=%d STA=Middle\n\r", pHalData->UndecoratedSmoothedPWDB);*/
+ pra->ratr_state = DM_RATR_STA_MIDDLE;
+ targetRATR = pra->middle_rssi_threshold_ratr;
+ } else {
+ /*DbgPrint("[DM] RSSI=%d STA=LOW\n\r", pHalData->UndecoratedSmoothedPWDB);*/
+ pra->ratr_state = DM_RATR_STA_LOW;
+ targetRATR = pra->low_rssi_threshold_ratr;
+ }
+
+ /* cosa add for test */
+ if (pra->ping_rssi_enable) {
+ /*pHalData->UndecoratedSmoothedPWDB = 19;*/
+ if (priv->undecorated_smoothed_pwdb < (long)(pra->ping_rssi_thresh_for_ra+5)) {
+ if ((priv->undecorated_smoothed_pwdb < (long)pra->ping_rssi_thresh_for_ra) ||
+ ping_rssi_state) {
+ /*DbgPrint("TestRSSI = %d, set RATR to 0x%x\n", pHalData->UndecoratedSmoothedPWDB, pRA->TestRSSIRATR);*/
+ pra->ratr_state = DM_RATR_STA_LOW;
+ targetRATR = pra->ping_rssi_ratr;
+ ping_rssi_state = 1;
+ }
+ /*else
+ DbgPrint("TestRSSI is between the range.\n");*/
+ } else {
+ /*DbgPrint("TestRSSI Recover to 0x%x\n", targetRATR);*/
+ ping_rssi_state = 0;
+ }
+ }
+
+ /*
+ * 2008.04.01
+ * For RTL819X, if pairwisekey = wep/tkip, we support only MCS0~7.
+ */
+ if (priv->ieee80211->GetHalfNmodeSupportByAPsHandler(dev))
+ targetRATR &= 0xf00fffff;
+
+ /* Check whether updating of RATR0 is required */
+ read_nic_dword(dev, RATR0, &currentRATR);
+ if (targetRATR != currentRATR) {
+ u32 ratr_value;
+
+ ratr_value = targetRATR;
+ RT_TRACE(COMP_RATE, "currentRATR = %x, targetRATR = %x\n", currentRATR, targetRATR);
+ if (priv->rf_type == RF_1T2R)
+ ratr_value &= ~(RATE_ALL_OFDM_2SS);
+ write_nic_dword(dev, RATR0, ratr_value);
+ write_nic_byte(dev, UFWP, 1);
+
+ pra->last_ratr = targetRATR;
+ }
+
+ } else {
+ pra->ratr_state = DM_RATR_STA_MAX;
+ }
+
+} /* dm_CheckRateAdaptive */
+
+static void dm_init_bandwidth_autoswitch(struct net_device *dev)
+{
+ struct r8192_priv *priv = ieee80211_priv(dev);
+
+ priv->ieee80211->bandwidth_auto_switch.threshold_20Mhzto40Mhz = BW_AUTO_SWITCH_LOW_HIGH;
+ priv->ieee80211->bandwidth_auto_switch.threshold_40Mhzto20Mhz = BW_AUTO_SWITCH_HIGH_LOW;
+ priv->ieee80211->bandwidth_auto_switch.bforced_tx20Mhz = false;
+ priv->ieee80211->bandwidth_auto_switch.bautoswitch_enable = false;
+
+} /* dm_init_bandwidth_autoswitch */
+
+static void dm_bandwidth_autoswitch(struct net_device *dev)
+{
+ struct r8192_priv *priv = ieee80211_priv(dev);
+
+ if (priv->CurrentChannelBW == HT_CHANNEL_WIDTH_20 || !priv->ieee80211->bandwidth_auto_switch.bautoswitch_enable)
+ return;
+ if (priv->ieee80211->bandwidth_auto_switch.bforced_tx20Mhz == false) { /* If send packets in 40 Mhz in 20/40 */
+ if (priv->undecorated_smoothed_pwdb <= priv->ieee80211->bandwidth_auto_switch.threshold_40Mhzto20Mhz)
+ priv->ieee80211->bandwidth_auto_switch.bforced_tx20Mhz = true;
+ } else { /* in force send packets in 20 Mhz in 20/40 */
+ if (priv->undecorated_smoothed_pwdb >= priv->ieee80211->bandwidth_auto_switch.threshold_20Mhzto40Mhz)
+ priv->ieee80211->bandwidth_auto_switch.bforced_tx20Mhz = false;
+ }
+} /* dm_BandwidthAutoSwitch */
+
+/* OFDM default at 0db, index=6. */
+static u32 OFDMSwingTable[OFDM_Table_Length] = {
+ 0x7f8001fe, /* 0, +6db */
+ 0x71c001c7, /* 1, +5db */
+ 0x65400195, /* 2, +4db */
+ 0x5a400169, /* 3, +3db */
+ 0x50800142, /* 4, +2db */
+ 0x47c0011f, /* 5, +1db */
+ 0x40000100, /* 6, +0db ===> default, upper for higher temperature, lower for low temperature */
+ 0x390000e4, /* 7, -1db */
+ 0x32c000cb, /* 8, -2db */
+ 0x2d4000b5, /* 9, -3db */
+ 0x288000a2, /* 10, -4db */
+ 0x24000090, /* 11, -5db */
+ 0x20000080, /* 12, -6db */
+ 0x1c800072, /* 13, -7db */
+ 0x19800066, /* 14, -8db */
+ 0x26c0005b, /* 15, -9db */
+ 0x24400051, /* 16, -10db */
+ 0x12000048, /* 17, -11db */
+ 0x10000040 /* 18, -12db */
+};
+
+static u8 CCKSwingTable_Ch1_Ch13[CCK_Table_length][8] = {
+ {0x36, 0x35, 0x2e, 0x25, 0x1c, 0x12, 0x09, 0x04}, /* 0, +0db ===> CCK40M default */
+ {0x30, 0x2f, 0x29, 0x21, 0x19, 0x10, 0x08, 0x03}, /* 1, -1db */
+ {0x2b, 0x2a, 0x25, 0x1e, 0x16, 0x0e, 0x07, 0x03}, /* 2, -2db */
+ {0x26, 0x25, 0x21, 0x1b, 0x14, 0x0d, 0x06, 0x03}, /* 3, -3db */
+ {0x22, 0x21, 0x1d, 0x18, 0x11, 0x0b, 0x06, 0x02}, /* 4, -4db */
+ {0x1f, 0x1e, 0x1a, 0x15, 0x10, 0x0a, 0x05, 0x02}, /* 5, -5db */
+ {0x1b, 0x1a, 0x17, 0x13, 0x0e, 0x09, 0x04, 0x02}, /* 6, -6db ===> CCK20M default */
+ {0x18, 0x17, 0x15, 0x11, 0x0c, 0x08, 0x04, 0x02}, /* 7, -7db */
+ {0x16, 0x15, 0x12, 0x0f, 0x0b, 0x07, 0x04, 0x01}, /* 8, -8db */
+ {0x13, 0x13, 0x10, 0x0d, 0x0a, 0x06, 0x03, 0x01}, /* 9, -9db */
+ {0x11, 0x11, 0x0f, 0x0c, 0x09, 0x06, 0x03, 0x01}, /* 10, -10db */
+ {0x0f, 0x0f, 0x0d, 0x0b, 0x08, 0x05, 0x03, 0x01} /* 11, -11db */
+};
+
+static u8 CCKSwingTable_Ch14[CCK_Table_length][8] = {
+ {0x36, 0x35, 0x2e, 0x1b, 0x00, 0x00, 0x00, 0x00}, /* 0, +0db ===> CCK40M default */
+ {0x30, 0x2f, 0x29, 0x18, 0x00, 0x00, 0x00, 0x00}, /* 1, -1db */
+ {0x2b, 0x2a, 0x25, 0x15, 0x00, 0x00, 0x00, 0x00}, /* 2, -2db */
+ {0x26, 0x25, 0x21, 0x13, 0x00, 0x00, 0x00, 0x00}, /* 3, -3db */
+ {0x22, 0x21, 0x1d, 0x11, 0x00, 0x00, 0x00, 0x00}, /* 4, -4db */
+ {0x1f, 0x1e, 0x1a, 0x0f, 0x00, 0x00, 0x00, 0x00}, /* 5, -5db */
+ {0x1b, 0x1a, 0x17, 0x0e, 0x00, 0x00, 0x00, 0x00}, /* 6, -6db ===> CCK20M default */
+ {0x18, 0x17, 0x15, 0x0c, 0x00, 0x00, 0x00, 0x00}, /* 7, -7db */
+ {0x16, 0x15, 0x12, 0x0b, 0x00, 0x00, 0x00, 0x00}, /* 8, -8db */
+ {0x13, 0x13, 0x10, 0x0a, 0x00, 0x00, 0x00, 0x00}, /* 9, -9db */
+ {0x11, 0x11, 0x0f, 0x09, 0x00, 0x00, 0x00, 0x00}, /* 10, -10db */
+ {0x0f, 0x0f, 0x0d, 0x08, 0x00, 0x00, 0x00, 0x00} /* 11, -11db */
+};
+
+static void dm_TXPowerTrackingCallback_TSSI(struct net_device *dev)
+{
+ struct r8192_priv *priv = ieee80211_priv(dev);
+ bool bHighpowerstate, viviflag = false;
+ DCMD_TXCMD_T tx_cmd;
+ u8 powerlevelOFDM24G;
+ int i = 0, j = 0, k = 0;
+ u8 RF_Type, tmp_report[5] = {0, 0, 0, 0, 0};
+ u32 Value;
+ u8 Pwr_Flag;
+ u16 Avg_TSSI_Meas, TSSI_13dBm, Avg_TSSI_Meas_from_driver = 0;
+ /*RT_STATUS rtStatus = RT_STATUS_SUCCESS;*/
+ bool rtStatus = true;
+ u32 delta = 0;
+
+ write_nic_byte(dev, 0x1ba, 0);
+
+ priv->ieee80211->bdynamic_txpower_enable = false;
+ bHighpowerstate = priv->bDynamicTxHighPower;
+
+ powerlevelOFDM24G = (u8)(priv->Pwr_Track>>24);
+ RF_Type = priv->rf_type;
+ Value = (RF_Type<<8) | powerlevelOFDM24G;
+
+ RT_TRACE(COMP_POWER_TRACKING, "powerlevelOFDM24G = %x\n", powerlevelOFDM24G);
+
+ for (j = 0; j <= 30; j++) { /* fill tx_cmd */
+ tx_cmd.Op = TXCMD_SET_TX_PWR_TRACKING;
+ tx_cmd.Length = 4;
+ tx_cmd.Value = Value;
+ rtStatus = SendTxCommandPacket(dev, &tx_cmd, 12);
+ if (rtStatus == RT_STATUS_FAILURE)
+ RT_TRACE(COMP_POWER_TRACKING, "Set configuration with tx cmd queue fail!\n");
+ mdelay(1);
+ /*DbgPrint("hi, vivi, strange\n");*/
+ for (i = 0; i <= 30; i++) {
+ read_nic_byte(dev, 0x1ba, &Pwr_Flag);
+
+ if (Pwr_Flag == 0) {
+ mdelay(1);
+ continue;
+ }
+ read_nic_word(dev, 0x13c, &Avg_TSSI_Meas);
+ if (Avg_TSSI_Meas == 0) {
+ write_nic_byte(dev, 0x1ba, 0);
+ break;
+ }
+
+ for (k = 0; k < 5; k++) {
+ if (k != 4)
+ read_nic_byte(dev, 0x134+k, &tmp_report[k]);
+ else
+ read_nic_byte(dev, 0x13e, &tmp_report[k]);
+ RT_TRACE(COMP_POWER_TRACKING, "TSSI_report_value = %d\n", tmp_report[k]);
+ }
+
+ /* check if the report value is right */
+ for (k = 0; k < 5; k++) {
+ if (tmp_report[k] <= 20) {
+ viviflag = true;
+ break;
+ }
+ }
+ if (viviflag == true) {
+ write_nic_byte(dev, 0x1ba, 0);
+ viviflag = false;
+ RT_TRACE(COMP_POWER_TRACKING, "we filtered the data\n");
+ for (k = 0; k < 5; k++)
+ tmp_report[k] = 0;
+ break;
+ }
+
+ for (k = 0; k < 5; k++)
+ Avg_TSSI_Meas_from_driver += tmp_report[k];
+
+ Avg_TSSI_Meas_from_driver = Avg_TSSI_Meas_from_driver*100/5;
+ RT_TRACE(COMP_POWER_TRACKING, "Avg_TSSI_Meas_from_driver = %d\n", Avg_TSSI_Meas_from_driver);
+ TSSI_13dBm = priv->TSSI_13dBm;
+ RT_TRACE(COMP_POWER_TRACKING, "TSSI_13dBm = %d\n", TSSI_13dBm);
+
+ /*if (abs(Avg_TSSI_Meas_from_driver - TSSI_13dBm) <= E_FOR_TX_POWER_TRACK)*/
+ /* For MacOS-compatible */
+ if (Avg_TSSI_Meas_from_driver > TSSI_13dBm)
+ delta = Avg_TSSI_Meas_from_driver - TSSI_13dBm;
+ else
+ delta = TSSI_13dBm - Avg_TSSI_Meas_from_driver;
+
+ if (delta <= E_FOR_TX_POWER_TRACK) {
+ priv->ieee80211->bdynamic_txpower_enable = true;
+ write_nic_byte(dev, 0x1ba, 0);
+ RT_TRACE(COMP_POWER_TRACKING, "tx power track is done\n");
+ RT_TRACE(COMP_POWER_TRACKING, "priv->rfa_txpowertrackingindex = %d\n", priv->rfa_txpowertrackingindex);
+ RT_TRACE(COMP_POWER_TRACKING, "priv->rfa_txpowertrackingindex_real = %d\n", priv->rfa_txpowertrackingindex_real);
+ RT_TRACE(COMP_POWER_TRACKING, "priv->cck_present_attentuation_difference = %d\n", priv->cck_present_attentuation_difference);
+ RT_TRACE(COMP_POWER_TRACKING, "priv->cck_present_attentuation = %d\n", priv->cck_present_attentuation);
+ return;
+ }
+ if (Avg_TSSI_Meas_from_driver < TSSI_13dBm - E_FOR_TX_POWER_TRACK) {
+ if (priv->rfa_txpowertrackingindex > 0) {
+ priv->rfa_txpowertrackingindex--;
+ if (priv->rfa_txpowertrackingindex_real > 4) {
+ priv->rfa_txpowertrackingindex_real--;
+ rtl8192_setBBreg(dev, rOFDM0_XATxIQImbalance, bMaskDWord, priv->txbbgain_table[priv->rfa_txpowertrackingindex_real].txbbgain_value);
+ }
+ }
+ } else {
+ if (priv->rfa_txpowertrackingindex < 36) {
+ priv->rfa_txpowertrackingindex++;
+ priv->rfa_txpowertrackingindex_real++;
+ rtl8192_setBBreg(dev, rOFDM0_XATxIQImbalance, bMaskDWord, priv->txbbgain_table[priv->rfa_txpowertrackingindex_real].txbbgain_value);
+
+ }
+ }
+ priv->cck_present_attentuation_difference
+ = priv->rfa_txpowertrackingindex - priv->rfa_txpowertracking_default;
+
+ if (priv->CurrentChannelBW == HT_CHANNEL_WIDTH_20)
+ priv->cck_present_attentuation
+ = priv->cck_present_attentuation_20Mdefault + priv->cck_present_attentuation_difference;
+ else
+ priv->cck_present_attentuation
+ = priv->cck_present_attentuation_40Mdefault + priv->cck_present_attentuation_difference;
+
+ if (priv->cck_present_attentuation > -1 && priv->cck_present_attentuation < 23) {
+ if (priv->ieee80211->current_network.channel == 14 && !priv->bcck_in_ch14) {
+ priv->bcck_in_ch14 = true;
+ dm_cck_txpower_adjust(dev, priv->bcck_in_ch14);
+ } else if (priv->ieee80211->current_network.channel != 14 && priv->bcck_in_ch14) {
+ priv->bcck_in_ch14 = false;
+ dm_cck_txpower_adjust(dev, priv->bcck_in_ch14);
+ } else
+ dm_cck_txpower_adjust(dev, priv->bcck_in_ch14);
+ }
+ RT_TRACE(COMP_POWER_TRACKING, "priv->rfa_txpowertrackingindex = %d\n", priv->rfa_txpowertrackingindex);
+ RT_TRACE(COMP_POWER_TRACKING, "priv->rfa_txpowertrackingindex_real = %d\n", priv->rfa_txpowertrackingindex_real);
+ RT_TRACE(COMP_POWER_TRACKING, "priv->cck_present_attentuation_difference = %d\n", priv->cck_present_attentuation_difference);
+ RT_TRACE(COMP_POWER_TRACKING, "priv->cck_present_attentuation = %d\n", priv->cck_present_attentuation);
+
+ if (priv->cck_present_attentuation_difference <= -12 || priv->cck_present_attentuation_difference >= 24) {
+ priv->ieee80211->bdynamic_txpower_enable = true;
+ write_nic_byte(dev, 0x1ba, 0);
+ RT_TRACE(COMP_POWER_TRACKING, "tx power track--->limited\n");
+ return;
+ }
+
+ write_nic_byte(dev, 0x1ba, 0);
+ Avg_TSSI_Meas_from_driver = 0;
+ for (k = 0; k < 5; k++)
+ tmp_report[k] = 0;
+ break;
+ }
+ }
+ priv->ieee80211->bdynamic_txpower_enable = true;
+ write_nic_byte(dev, 0x1ba, 0);
+}
+
+static void dm_TXPowerTrackingCallback_ThermalMeter(struct net_device *dev)
+{
+#define ThermalMeterVal 9
+ struct r8192_priv *priv = ieee80211_priv(dev);
+ u32 tmpRegA, TempCCk;
+ u8 tmpOFDMindex, tmpCCKindex, tmpCCK20Mindex, tmpCCK40Mindex, tmpval;
+ int i = 0, CCKSwingNeedUpdate = 0;
+
+ if (!priv->btxpower_trackingInit) {
+ /* Query OFDM default setting */
+ tmpRegA = rtl8192_QueryBBReg(dev, rOFDM0_XATxIQImbalance, bMaskDWord);
+ for (i = 0; i < OFDM_Table_Length; i++) { /* find the index */
+ if (tmpRegA == OFDMSwingTable[i]) {
+ priv->OFDM_index = (u8)i;
+ RT_TRACE(COMP_POWER_TRACKING, "Initial reg0x%x = 0x%x, OFDM_index=0x%x\n",
+ rOFDM0_XATxIQImbalance, tmpRegA, priv->OFDM_index);
+ }
+ }
+
+ /* Query CCK default setting From 0xa22 */
+ TempCCk = rtl8192_QueryBBReg(dev, rCCK0_TxFilter1, bMaskByte2);
+ for (i = 0; i < CCK_Table_length; i++) {
+ if (TempCCk == (u32)CCKSwingTable_Ch1_Ch13[i][0]) {
+ priv->CCK_index = (u8) i;
+ RT_TRACE(COMP_POWER_TRACKING, "Initial reg0x%x = 0x%x, CCK_index=0x%x\n",
+ rCCK0_TxFilter1, TempCCk, priv->CCK_index);
+ break;
+ }
+ }
+ priv->btxpower_trackingInit = true;
+ /*pHalData->TXPowercount = 0;*/
+ return;
+ }
+
+ /*
+ * ==========================
+ * this is only for test, should be masked
+ * ==========================
+ */
+
+ /* read and filter out unreasonable value */
+ tmpRegA = rtl8192_phy_QueryRFReg(dev, RF90_PATH_A, 0x12, 0x078); /* 0x12: RF Reg[10:7] */
+ RT_TRACE(COMP_POWER_TRACKING, "Readback ThermalMeterA = %d\n", tmpRegA);
+ if (tmpRegA < 3 || tmpRegA > 13)
+ return;
+ if (tmpRegA >= 12) /* if over 12, TP will be bad when high temperature */
+ tmpRegA = 12;
+ RT_TRACE(COMP_POWER_TRACKING, "Valid ThermalMeterA = %d\n", tmpRegA);
+ priv->ThermalMeter[0] = ThermalMeterVal; /* We use fixed value by Bryant's suggestion */
+ priv->ThermalMeter[1] = ThermalMeterVal; /* We use fixed value by Bryant's suggestion */
+
+ /* Get current RF-A temperature index */
+ if (priv->ThermalMeter[0] >= (u8)tmpRegA) { /* lower temperature */
+ tmpOFDMindex = tmpCCK20Mindex = 6+(priv->ThermalMeter[0]-(u8)tmpRegA);
+ tmpCCK40Mindex = tmpCCK20Mindex - 6;
+ if (tmpOFDMindex >= OFDM_Table_Length)
+ tmpOFDMindex = OFDM_Table_Length-1;
+ if (tmpCCK20Mindex >= CCK_Table_length)
+ tmpCCK20Mindex = CCK_Table_length-1;
+ if (tmpCCK40Mindex >= CCK_Table_length)
+ tmpCCK40Mindex = CCK_Table_length-1;
+ } else {
+ tmpval = (u8)tmpRegA - priv->ThermalMeter[0];
+
+ if (tmpval >= 6) /* higher temperature */
+ tmpOFDMindex = tmpCCK20Mindex = 0; /* max to +6dB */
+ else
+ tmpOFDMindex = tmpCCK20Mindex = 6 - tmpval;
+ tmpCCK40Mindex = 0;
+ }
+ /*DbgPrint("%ddb, tmpOFDMindex = %d, tmpCCK20Mindex = %d, tmpCCK40Mindex = %d",
+ ((u1Byte)tmpRegA - pHalData->ThermalMeter[0]),
+ tmpOFDMindex, tmpCCK20Mindex, tmpCCK40Mindex);*/
+ if (priv->CurrentChannelBW != HT_CHANNEL_WIDTH_20) /* 40M */
+ tmpCCKindex = tmpCCK40Mindex;
+ else
+ tmpCCKindex = tmpCCK20Mindex;
+
+ if (priv->ieee80211->current_network.channel == 14 && !priv->bcck_in_ch14) {
+ priv->bcck_in_ch14 = true;
+ CCKSwingNeedUpdate = 1;
+ } else if (priv->ieee80211->current_network.channel != 14 && priv->bcck_in_ch14) {
+ priv->bcck_in_ch14 = false;
+ CCKSwingNeedUpdate = 1;
+ }
+
+ if (priv->CCK_index != tmpCCKindex) {
+ priv->CCK_index = tmpCCKindex;
+ CCKSwingNeedUpdate = 1;
+ }
+
+ if (CCKSwingNeedUpdate) {
+ /*DbgPrint("Update CCK Swing, CCK_index = %d\n", pHalData->CCK_index);*/
+ dm_cck_txpower_adjust(dev, priv->bcck_in_ch14);
+ }
+ if (priv->OFDM_index != tmpOFDMindex) {
+ priv->OFDM_index = tmpOFDMindex;
+ rtl8192_setBBreg(dev, rOFDM0_XATxIQImbalance, bMaskDWord, OFDMSwingTable[priv->OFDM_index]);
+ RT_TRACE(COMP_POWER_TRACKING, "Update OFDMSwing[%d] = 0x%x\n",
+ priv->OFDM_index, OFDMSwingTable[priv->OFDM_index]);
+ }
+ priv->txpower_count = 0;
+}
+
+void dm_txpower_trackingcallback(struct work_struct *work)
+{
+ struct delayed_work *dwork = container_of(work, struct delayed_work, work);
+ struct r8192_priv *priv = container_of(dwork, struct r8192_priv, txpower_tracking_wq);
+ struct net_device *dev = priv->ieee80211->dev;
+
+ if (priv->bDcut == true)
+ dm_TXPowerTrackingCallback_TSSI(dev);
+ else
+ dm_TXPowerTrackingCallback_ThermalMeter(dev);
+}
+
+static void dm_InitializeTXPowerTracking_TSSI(struct net_device *dev)
+{
+ struct r8192_priv *priv = ieee80211_priv(dev);
+
+ /* Initial the Tx BB index and mapping value */
+ priv->txbbgain_table[0].txbb_iq_amplifygain = 12;
+ priv->txbbgain_table[0].txbbgain_value = 0x7f8001fe;
+ priv->txbbgain_table[1].txbb_iq_amplifygain = 11;
+ priv->txbbgain_table[1].txbbgain_value = 0x788001e2;
+ priv->txbbgain_table[2].txbb_iq_amplifygain = 10;
+ priv->txbbgain_table[2].txbbgain_value = 0x71c001c7;
+ priv->txbbgain_table[3].txbb_iq_amplifygain = 9;
+ priv->txbbgain_table[3].txbbgain_value = 0x6b8001ae;
+ priv->txbbgain_table[4].txbb_iq_amplifygain = 8;
+ priv->txbbgain_table[4].txbbgain_value = 0x65400195;
+ priv->txbbgain_table[5].txbb_iq_amplifygain = 7;
+ priv->txbbgain_table[5].txbbgain_value = 0x5fc0017f;
+ priv->txbbgain_table[6].txbb_iq_amplifygain = 6;
+ priv->txbbgain_table[6].txbbgain_value = 0x5a400169;
+ priv->txbbgain_table[7].txbb_iq_amplifygain = 5;
+ priv->txbbgain_table[7].txbbgain_value = 0x55400155;
+ priv->txbbgain_table[8].txbb_iq_amplifygain = 4;
+ priv->txbbgain_table[8].txbbgain_value = 0x50800142;
+ priv->txbbgain_table[9].txbb_iq_amplifygain = 3;
+ priv->txbbgain_table[9].txbbgain_value = 0x4c000130;
+ priv->txbbgain_table[10].txbb_iq_amplifygain = 2;
+ priv->txbbgain_table[10].txbbgain_value = 0x47c0011f;
+ priv->txbbgain_table[11].txbb_iq_amplifygain = 1;
+ priv->txbbgain_table[11].txbbgain_value = 0x43c0010f;
+ priv->txbbgain_table[12].txbb_iq_amplifygain = 0;
+ priv->txbbgain_table[12].txbbgain_value = 0x40000100;
+ priv->txbbgain_table[13].txbb_iq_amplifygain = -1;
+ priv->txbbgain_table[13].txbbgain_value = 0x3c8000f2;
+ priv->txbbgain_table[14].txbb_iq_amplifygain = -2;
+ priv->txbbgain_table[14].txbbgain_value = 0x390000e4;
+ priv->txbbgain_table[15].txbb_iq_amplifygain = -3;
+ priv->txbbgain_table[15].txbbgain_value = 0x35c000d7;
+ priv->txbbgain_table[16].txbb_iq_amplifygain = -4;
+ priv->txbbgain_table[16].txbbgain_value = 0x32c000cb;
+ priv->txbbgain_table[17].txbb_iq_amplifygain = -5;
+ priv->txbbgain_table[17].txbbgain_value = 0x300000c0;
+ priv->txbbgain_table[18].txbb_iq_amplifygain = -6;
+ priv->txbbgain_table[18].txbbgain_value = 0x2d4000b5;
+ priv->txbbgain_table[19].txbb_iq_amplifygain = -7;
+ priv->txbbgain_table[19].txbbgain_value = 0x2ac000ab;
+ priv->txbbgain_table[20].txbb_iq_amplifygain = -8;
+ priv->txbbgain_table[20].txbbgain_value = 0x288000a2;
+ priv->txbbgain_table[21].txbb_iq_amplifygain = -9;
+ priv->txbbgain_table[21].txbbgain_value = 0x26000098;
+ priv->txbbgain_table[22].txbb_iq_amplifygain = -10;
+ priv->txbbgain_table[22].txbbgain_value = 0x24000090;
+ priv->txbbgain_table[23].txbb_iq_amplifygain = -11;
+ priv->txbbgain_table[23].txbbgain_value = 0x22000088;
+ priv->txbbgain_table[24].txbb_iq_amplifygain = -12;
+ priv->txbbgain_table[24].txbbgain_value = 0x20000080;
+ priv->txbbgain_table[25].txbb_iq_amplifygain = -13;
+ priv->txbbgain_table[25].txbbgain_value = 0x1a00006c;
+ priv->txbbgain_table[26].txbb_iq_amplifygain = -14;
+ priv->txbbgain_table[26].txbbgain_value = 0x1c800072;
+ priv->txbbgain_table[27].txbb_iq_amplifygain = -15;
+ priv->txbbgain_table[27].txbbgain_value = 0x18000060;
+ priv->txbbgain_table[28].txbb_iq_amplifygain = -16;
+ priv->txbbgain_table[28].txbbgain_value = 0x19800066;
+ priv->txbbgain_table[29].txbb_iq_amplifygain = -17;
+ priv->txbbgain_table[29].txbbgain_value = 0x15800056;
+ priv->txbbgain_table[30].txbb_iq_amplifygain = -18;
+ priv->txbbgain_table[30].txbbgain_value = 0x26c0005b;
+ priv->txbbgain_table[31].txbb_iq_amplifygain = -19;
+ priv->txbbgain_table[31].txbbgain_value = 0x14400051;
+ priv->txbbgain_table[32].txbb_iq_amplifygain = -20;
+ priv->txbbgain_table[32].txbbgain_value = 0x24400051;
+ priv->txbbgain_table[33].txbb_iq_amplifygain = -21;
+ priv->txbbgain_table[33].txbbgain_value = 0x1300004c;
+ priv->txbbgain_table[34].txbb_iq_amplifygain = -22;
+ priv->txbbgain_table[34].txbbgain_value = 0x12000048;
+ priv->txbbgain_table[35].txbb_iq_amplifygain = -23;
+ priv->txbbgain_table[35].txbbgain_value = 0x11000044;
+ priv->txbbgain_table[36].txbb_iq_amplifygain = -24;
+ priv->txbbgain_table[36].txbbgain_value = 0x10000040;
+
+ /*
+ * ccktxbb_valuearray[0] is 0xA22 [1] is 0xA24 ...[7] is 0xA29
+ * This Table is for CH1~CH13
+ */
+ priv->cck_txbbgain_table[0].ccktxbb_valuearray[0] = 0x36;
+ priv->cck_txbbgain_table[0].ccktxbb_valuearray[1] = 0x35;
+ priv->cck_txbbgain_table[0].ccktxbb_valuearray[2] = 0x2e;
+ priv->cck_txbbgain_table[0].ccktxbb_valuearray[3] = 0x25;
+ priv->cck_txbbgain_table[0].ccktxbb_valuearray[4] = 0x1c;
+ priv->cck_txbbgain_table[0].ccktxbb_valuearray[5] = 0x12;
+ priv->cck_txbbgain_table[0].ccktxbb_valuearray[6] = 0x09;
+ priv->cck_txbbgain_table[0].ccktxbb_valuearray[7] = 0x04;
+
+ priv->cck_txbbgain_table[1].ccktxbb_valuearray[0] = 0x33;
+ priv->cck_txbbgain_table[1].ccktxbb_valuearray[1] = 0x32;
+ priv->cck_txbbgain_table[1].ccktxbb_valuearray[2] = 0x2b;
+ priv->cck_txbbgain_table[1].ccktxbb_valuearray[3] = 0x23;
+ priv->cck_txbbgain_table[1].ccktxbb_valuearray[4] = 0x1a;
+ priv->cck_txbbgain_table[1].ccktxbb_valuearray[5] = 0x11;
+ priv->cck_txbbgain_table[1].ccktxbb_valuearray[6] = 0x08;
+ priv->cck_txbbgain_table[1].ccktxbb_valuearray[7] = 0x04;
+
+ priv->cck_txbbgain_table[2].ccktxbb_valuearray[0] = 0x30;
+ priv->cck_txbbgain_table[2].ccktxbb_valuearray[1] = 0x2f;
+ priv->cck_txbbgain_table[2].ccktxbb_valuearray[2] = 0x29;
+ priv->cck_txbbgain_table[2].ccktxbb_valuearray[3] = 0x21;
+ priv->cck_txbbgain_table[2].ccktxbb_valuearray[4] = 0x19;
+ priv->cck_txbbgain_table[2].ccktxbb_valuearray[5] = 0x10;
+ priv->cck_txbbgain_table[2].ccktxbb_valuearray[6] = 0x08;
+ priv->cck_txbbgain_table[2].ccktxbb_valuearray[7] = 0x03;
+
+ priv->cck_txbbgain_table[3].ccktxbb_valuearray[0] = 0x2d;
+ priv->cck_txbbgain_table[3].ccktxbb_valuearray[1] = 0x2d;
+ priv->cck_txbbgain_table[3].ccktxbb_valuearray[2] = 0x27;
+ priv->cck_txbbgain_table[3].ccktxbb_valuearray[3] = 0x1f;
+ priv->cck_txbbgain_table[3].ccktxbb_valuearray[4] = 0x18;
+ priv->cck_txbbgain_table[3].ccktxbb_valuearray[5] = 0x0f;
+ priv->cck_txbbgain_table[3].ccktxbb_valuearray[6] = 0x08;
+ priv->cck_txbbgain_table[3].ccktxbb_valuearray[7] = 0x03;
+
+ priv->cck_txbbgain_table[4].ccktxbb_valuearray[0] = 0x2b;
+ priv->cck_txbbgain_table[4].ccktxbb_valuearray[1] = 0x2a;
+ priv->cck_txbbgain_table[4].ccktxbb_valuearray[2] = 0x25;
+ priv->cck_txbbgain_table[4].ccktxbb_valuearray[3] = 0x1e;
+ priv->cck_txbbgain_table[4].ccktxbb_valuearray[4] = 0x16;
+ priv->cck_txbbgain_table[4].ccktxbb_valuearray[5] = 0x0e;
+ priv->cck_txbbgain_table[4].ccktxbb_valuearray[6] = 0x07;
+ priv->cck_txbbgain_table[4].ccktxbb_valuearray[7] = 0x03;
+
+ priv->cck_txbbgain_table[5].ccktxbb_valuearray[0] = 0x28;
+ priv->cck_txbbgain_table[5].ccktxbb_valuearray[1] = 0x28;
+ priv->cck_txbbgain_table[5].ccktxbb_valuearray[2] = 0x22;
+ priv->cck_txbbgain_table[5].ccktxbb_valuearray[3] = 0x1c;
+ priv->cck_txbbgain_table[5].ccktxbb_valuearray[4] = 0x15;
+ priv->cck_txbbgain_table[5].ccktxbb_valuearray[5] = 0x0d;
+ priv->cck_txbbgain_table[5].ccktxbb_valuearray[6] = 0x07;
+ priv->cck_txbbgain_table[5].ccktxbb_valuearray[7] = 0x03;
+
+ priv->cck_txbbgain_table[6].ccktxbb_valuearray[0] = 0x26;
+ priv->cck_txbbgain_table[6].ccktxbb_valuearray[1] = 0x25;
+ priv->cck_txbbgain_table[6].ccktxbb_valuearray[2] = 0x21;
+ priv->cck_txbbgain_table[6].ccktxbb_valuearray[3] = 0x1b;
+ priv->cck_txbbgain_table[6].ccktxbb_valuearray[4] = 0x14;
+ priv->cck_txbbgain_table[6].ccktxbb_valuearray[5] = 0x0d;
+ priv->cck_txbbgain_table[6].ccktxbb_valuearray[6] = 0x06;
+ priv->cck_txbbgain_table[6].ccktxbb_valuearray[7] = 0x03;
+
+ priv->cck_txbbgain_table[7].ccktxbb_valuearray[0] = 0x24;
+ priv->cck_txbbgain_table[7].ccktxbb_valuearray[1] = 0x23;
+ priv->cck_txbbgain_table[7].ccktxbb_valuearray[2] = 0x1f;
+ priv->cck_txbbgain_table[7].ccktxbb_valuearray[3] = 0x19;
+ priv->cck_txbbgain_table[7].ccktxbb_valuearray[4] = 0x13;
+ priv->cck_txbbgain_table[7].ccktxbb_valuearray[5] = 0x0c;
+ priv->cck_txbbgain_table[7].ccktxbb_valuearray[6] = 0x06;
+ priv->cck_txbbgain_table[7].ccktxbb_valuearray[7] = 0x03;
+
+ priv->cck_txbbgain_table[8].ccktxbb_valuearray[0] = 0x22;
+ priv->cck_txbbgain_table[8].ccktxbb_valuearray[1] = 0x21;
+ priv->cck_txbbgain_table[8].ccktxbb_valuearray[2] = 0x1d;
+ priv->cck_txbbgain_table[8].ccktxbb_valuearray[3] = 0x18;
+ priv->cck_txbbgain_table[8].ccktxbb_valuearray[4] = 0x11;
+ priv->cck_txbbgain_table[8].ccktxbb_valuearray[5] = 0x0b;
+ priv->cck_txbbgain_table[8].ccktxbb_valuearray[6] = 0x06;
+ priv->cck_txbbgain_table[8].ccktxbb_valuearray[7] = 0x02;
+
+ priv->cck_txbbgain_table[9].ccktxbb_valuearray[0] = 0x20;
+ priv->cck_txbbgain_table[9].ccktxbb_valuearray[1] = 0x20;
+ priv->cck_txbbgain_table[9].ccktxbb_valuearray[2] = 0x1b;
+ priv->cck_txbbgain_table[9].ccktxbb_valuearray[3] = 0x16;
+ priv->cck_txbbgain_table[9].ccktxbb_valuearray[4] = 0x11;
+ priv->cck_txbbgain_table[9].ccktxbb_valuearray[5] = 0x08;
+ priv->cck_txbbgain_table[9].ccktxbb_valuearray[6] = 0x05;
+ priv->cck_txbbgain_table[9].ccktxbb_valuearray[7] = 0x02;
+
+ priv->cck_txbbgain_table[10].ccktxbb_valuearray[0] = 0x1f;
+ priv->cck_txbbgain_table[10].ccktxbb_valuearray[1] = 0x1e;
+ priv->cck_txbbgain_table[10].ccktxbb_valuearray[2] = 0x1a;
+ priv->cck_txbbgain_table[10].ccktxbb_valuearray[3] = 0x15;
+ priv->cck_txbbgain_table[10].ccktxbb_valuearray[4] = 0x10;
+ priv->cck_txbbgain_table[10].ccktxbb_valuearray[5] = 0x0a;
+ priv->cck_txbbgain_table[10].ccktxbb_valuearray[6] = 0x05;
+ priv->cck_txbbgain_table[10].ccktxbb_valuearray[7] = 0x02;
+
+ priv->cck_txbbgain_table[11].ccktxbb_valuearray[0] = 0x1d;
+ priv->cck_txbbgain_table[11].ccktxbb_valuearray[1] = 0x1c;
+ priv->cck_txbbgain_table[11].ccktxbb_valuearray[2] = 0x18;
+ priv->cck_txbbgain_table[11].ccktxbb_valuearray[3] = 0x14;
+ priv->cck_txbbgain_table[11].ccktxbb_valuearray[4] = 0x0f;
+ priv->cck_txbbgain_table[11].ccktxbb_valuearray[5] = 0x0a;
+ priv->cck_txbbgain_table[11].ccktxbb_valuearray[6] = 0x05;
+ priv->cck_txbbgain_table[11].ccktxbb_valuearray[7] = 0x02;
+
+ priv->cck_txbbgain_table[12].ccktxbb_valuearray[0] = 0x1b;
+ priv->cck_txbbgain_table[12].ccktxbb_valuearray[1] = 0x1a;
+ priv->cck_txbbgain_table[12].ccktxbb_valuearray[2] = 0x17;
+ priv->cck_txbbgain_table[12].ccktxbb_valuearray[3] = 0x13;
+ priv->cck_txbbgain_table[12].ccktxbb_valuearray[4] = 0x0e;
+ priv->cck_txbbgain_table[12].ccktxbb_valuearray[5] = 0x09;
+ priv->cck_txbbgain_table[12].ccktxbb_valuearray[6] = 0x04;
+ priv->cck_txbbgain_table[12].ccktxbb_valuearray[7] = 0x02;
+
+ priv->cck_txbbgain_table[13].ccktxbb_valuearray[0] = 0x1a;
+ priv->cck_txbbgain_table[13].ccktxbb_valuearray[1] = 0x19;
+ priv->cck_txbbgain_table[13].ccktxbb_valuearray[2] = 0x16;
+ priv->cck_txbbgain_table[13].ccktxbb_valuearray[3] = 0x12;
+ priv->cck_txbbgain_table[13].ccktxbb_valuearray[4] = 0x0d;
+ priv->cck_txbbgain_table[13].ccktxbb_valuearray[5] = 0x09;
+ priv->cck_txbbgain_table[13].ccktxbb_valuearray[6] = 0x04;
+ priv->cck_txbbgain_table[13].ccktxbb_valuearray[7] = 0x02;
+
+ priv->cck_txbbgain_table[14].ccktxbb_valuearray[0] = 0x18;
+ priv->cck_txbbgain_table[14].ccktxbb_valuearray[1] = 0x17;
+ priv->cck_txbbgain_table[14].ccktxbb_valuearray[2] = 0x15;
+ priv->cck_txbbgain_table[14].ccktxbb_valuearray[3] = 0x11;
+ priv->cck_txbbgain_table[14].ccktxbb_valuearray[4] = 0x0c;
+ priv->cck_txbbgain_table[14].ccktxbb_valuearray[5] = 0x08;
+ priv->cck_txbbgain_table[14].ccktxbb_valuearray[6] = 0x04;
+ priv->cck_txbbgain_table[14].ccktxbb_valuearray[7] = 0x02;
+
+ priv->cck_txbbgain_table[15].ccktxbb_valuearray[0] = 0x17;
+ priv->cck_txbbgain_table[15].ccktxbb_valuearray[1] = 0x16;
+ priv->cck_txbbgain_table[15].ccktxbb_valuearray[2] = 0x13;
+ priv->cck_txbbgain_table[15].ccktxbb_valuearray[3] = 0x10;
+ priv->cck_txbbgain_table[15].ccktxbb_valuearray[4] = 0x0c;
+ priv->cck_txbbgain_table[15].ccktxbb_valuearray[5] = 0x08;
+ priv->cck_txbbgain_table[15].ccktxbb_valuearray[6] = 0x04;
+ priv->cck_txbbgain_table[15].ccktxbb_valuearray[7] = 0x02;
+
+ priv->cck_txbbgain_table[16].ccktxbb_valuearray[0] = 0x16;
+ priv->cck_txbbgain_table[16].ccktxbb_valuearray[1] = 0x15;
+ priv->cck_txbbgain_table[16].ccktxbb_valuearray[2] = 0x12;
+ priv->cck_txbbgain_table[16].ccktxbb_valuearray[3] = 0x0f;
+ priv->cck_txbbgain_table[16].ccktxbb_valuearray[4] = 0x0b;
+ priv->cck_txbbgain_table[16].ccktxbb_valuearray[5] = 0x07;
+ priv->cck_txbbgain_table[16].ccktxbb_valuearray[6] = 0x04;
+ priv->cck_txbbgain_table[16].ccktxbb_valuearray[7] = 0x01;
+
+ priv->cck_txbbgain_table[17].ccktxbb_valuearray[0] = 0x14;
+ priv->cck_txbbgain_table[17].ccktxbb_valuearray[1] = 0x14;
+ priv->cck_txbbgain_table[17].ccktxbb_valuearray[2] = 0x11;
+ priv->cck_txbbgain_table[17].ccktxbb_valuearray[3] = 0x0e;
+ priv->cck_txbbgain_table[17].ccktxbb_valuearray[4] = 0x0b;
+ priv->cck_txbbgain_table[17].ccktxbb_valuearray[5] = 0x07;
+ priv->cck_txbbgain_table[17].ccktxbb_valuearray[6] = 0x03;
+ priv->cck_txbbgain_table[17].ccktxbb_valuearray[7] = 0x02;
+
+ priv->cck_txbbgain_table[18].ccktxbb_valuearray[0] = 0x13;
+ priv->cck_txbbgain_table[18].ccktxbb_valuearray[1] = 0x13;
+ priv->cck_txbbgain_table[18].ccktxbb_valuearray[2] = 0x10;
+ priv->cck_txbbgain_table[18].ccktxbb_valuearray[3] = 0x0d;
+ priv->cck_txbbgain_table[18].ccktxbb_valuearray[4] = 0x0a;
+ priv->cck_txbbgain_table[18].ccktxbb_valuearray[5] = 0x06;
+ priv->cck_txbbgain_table[18].ccktxbb_valuearray[6] = 0x03;
+ priv->cck_txbbgain_table[18].ccktxbb_valuearray[7] = 0x01;
+
+ priv->cck_txbbgain_table[19].ccktxbb_valuearray[0] = 0x12;
+ priv->cck_txbbgain_table[19].ccktxbb_valuearray[1] = 0x12;
+ priv->cck_txbbgain_table[19].ccktxbb_valuearray[2] = 0x0f;
+ priv->cck_txbbgain_table[19].ccktxbb_valuearray[3] = 0x0c;
+ priv->cck_txbbgain_table[19].ccktxbb_valuearray[4] = 0x09;
+ priv->cck_txbbgain_table[19].ccktxbb_valuearray[5] = 0x06;
+ priv->cck_txbbgain_table[19].ccktxbb_valuearray[6] = 0x03;
+ priv->cck_txbbgain_table[19].ccktxbb_valuearray[7] = 0x01;
+
+ priv->cck_txbbgain_table[20].ccktxbb_valuearray[0] = 0x11;
+ priv->cck_txbbgain_table[20].ccktxbb_valuearray[1] = 0x11;
+ priv->cck_txbbgain_table[20].ccktxbb_valuearray[2] = 0x0f;
+ priv->cck_txbbgain_table[20].ccktxbb_valuearray[3] = 0x0c;
+ priv->cck_txbbgain_table[20].ccktxbb_valuearray[4] = 0x09;
+ priv->cck_txbbgain_table[20].ccktxbb_valuearray[5] = 0x06;
+ priv->cck_txbbgain_table[20].ccktxbb_valuearray[6] = 0x03;
+ priv->cck_txbbgain_table[20].ccktxbb_valuearray[7] = 0x01;
+
+ priv->cck_txbbgain_table[21].ccktxbb_valuearray[0] = 0x10;
+ priv->cck_txbbgain_table[21].ccktxbb_valuearray[1] = 0x10;
+ priv->cck_txbbgain_table[21].ccktxbb_valuearray[2] = 0x0e;
+ priv->cck_txbbgain_table[21].ccktxbb_valuearray[3] = 0x0b;
+ priv->cck_txbbgain_table[21].ccktxbb_valuearray[4] = 0x08;
+ priv->cck_txbbgain_table[21].ccktxbb_valuearray[5] = 0x05;
+ priv->cck_txbbgain_table[21].ccktxbb_valuearray[6] = 0x03;
+ priv->cck_txbbgain_table[21].ccktxbb_valuearray[7] = 0x01;
+
+ priv->cck_txbbgain_table[22].ccktxbb_valuearray[0] = 0x0f;
+ priv->cck_txbbgain_table[22].ccktxbb_valuearray[1] = 0x0f;
+ priv->cck_txbbgain_table[22].ccktxbb_valuearray[2] = 0x0d;
+ priv->cck_txbbgain_table[22].ccktxbb_valuearray[3] = 0x0b;
+ priv->cck_txbbgain_table[22].ccktxbb_valuearray[4] = 0x08;
+ priv->cck_txbbgain_table[22].ccktxbb_valuearray[5] = 0x05;
+ priv->cck_txbbgain_table[22].ccktxbb_valuearray[6] = 0x03;
+ priv->cck_txbbgain_table[22].ccktxbb_valuearray[7] = 0x01;
+
+ /*
+ * ccktxbb_valuearray[0] is 0xA22 [1] is 0xA24 ...[7] is 0xA29
+ * This Table is for CH14
+ */
+ priv->cck_txbbgain_ch14_table[0].ccktxbb_valuearray[0] = 0x36;
+ priv->cck_txbbgain_ch14_table[0].ccktxbb_valuearray[1] = 0x35;
+ priv->cck_txbbgain_ch14_table[0].ccktxbb_valuearray[2] = 0x2e;
+ priv->cck_txbbgain_ch14_table[0].ccktxbb_valuearray[3] = 0x1b;
+ priv->cck_txbbgain_ch14_table[0].ccktxbb_valuearray[4] = 0x00;
+ priv->cck_txbbgain_ch14_table[0].ccktxbb_valuearray[5] = 0x00;
+ priv->cck_txbbgain_ch14_table[0].ccktxbb_valuearray[6] = 0x00;
+ priv->cck_txbbgain_ch14_table[0].ccktxbb_valuearray[7] = 0x00;
+
+ priv->cck_txbbgain_ch14_table[1].ccktxbb_valuearray[0] = 0x33;
+ priv->cck_txbbgain_ch14_table[1].ccktxbb_valuearray[1] = 0x32;
+ priv->cck_txbbgain_ch14_table[1].ccktxbb_valuearray[2] = 0x2b;
+ priv->cck_txbbgain_ch14_table[1].ccktxbb_valuearray[3] = 0x19;
+ priv->cck_txbbgain_ch14_table[1].ccktxbb_valuearray[4] = 0x00;
+ priv->cck_txbbgain_ch14_table[1].ccktxbb_valuearray[5] = 0x00;
+ priv->cck_txbbgain_ch14_table[1].ccktxbb_valuearray[6] = 0x00;
+ priv->cck_txbbgain_ch14_table[1].ccktxbb_valuearray[7] = 0x00;
+
+ priv->cck_txbbgain_ch14_table[2].ccktxbb_valuearray[0] = 0x30;
+ priv->cck_txbbgain_ch14_table[2].ccktxbb_valuearray[1] = 0x2f;
+ priv->cck_txbbgain_ch14_table[2].ccktxbb_valuearray[2] = 0x29;
+ priv->cck_txbbgain_ch14_table[2].ccktxbb_valuearray[3] = 0x18;
+ priv->cck_txbbgain_ch14_table[2].ccktxbb_valuearray[4] = 0x00;
+ priv->cck_txbbgain_ch14_table[2].ccktxbb_valuearray[5] = 0x00;
+ priv->cck_txbbgain_ch14_table[2].ccktxbb_valuearray[6] = 0x00;
+ priv->cck_txbbgain_ch14_table[2].ccktxbb_valuearray[7] = 0x00;
+
+ priv->cck_txbbgain_ch14_table[3].ccktxbb_valuearray[0] = 0x2d;
+ priv->cck_txbbgain_ch14_table[3].ccktxbb_valuearray[1] = 0x2d;
+ priv->cck_txbbgain_ch14_table[3].ccktxbb_valuearray[2] = 0x27;
+ priv->cck_txbbgain_ch14_table[3].ccktxbb_valuearray[3] = 0x17;
+ priv->cck_txbbgain_ch14_table[3].ccktxbb_valuearray[4] = 0x00;
+ priv->cck_txbbgain_ch14_table[3].ccktxbb_valuearray[5] = 0x00;
+ priv->cck_txbbgain_ch14_table[3].ccktxbb_valuearray[6] = 0x00;
+ priv->cck_txbbgain_ch14_table[3].ccktxbb_valuearray[7] = 0x00;
+
+ priv->cck_txbbgain_ch14_table[4].ccktxbb_valuearray[0] = 0x2b;
+ priv->cck_txbbgain_ch14_table[4].ccktxbb_valuearray[1] = 0x2a;
+ priv->cck_txbbgain_ch14_table[4].ccktxbb_valuearray[2] = 0x25;
+ priv->cck_txbbgain_ch14_table[4].ccktxbb_valuearray[3] = 0x15;
+ priv->cck_txbbgain_ch14_table[4].ccktxbb_valuearray[4] = 0x00;
+ priv->cck_txbbgain_ch14_table[4].ccktxbb_valuearray[5] = 0x00;
+ priv->cck_txbbgain_ch14_table[4].ccktxbb_valuearray[6] = 0x00;
+ priv->cck_txbbgain_ch14_table[4].ccktxbb_valuearray[7] = 0x00;
+
+ priv->cck_txbbgain_ch14_table[5].ccktxbb_valuearray[0] = 0x28;
+ priv->cck_txbbgain_ch14_table[5].ccktxbb_valuearray[1] = 0x28;
+ priv->cck_txbbgain_ch14_table[5].ccktxbb_valuearray[2] = 0x22;
+ priv->cck_txbbgain_ch14_table[5].ccktxbb_valuearray[3] = 0x14;
+ priv->cck_txbbgain_ch14_table[5].ccktxbb_valuearray[4] = 0x00;
+ priv->cck_txbbgain_ch14_table[5].ccktxbb_valuearray[5] = 0x00;
+ priv->cck_txbbgain_ch14_table[5].ccktxbb_valuearray[6] = 0x00;
+ priv->cck_txbbgain_ch14_table[5].ccktxbb_valuearray[7] = 0x00;
+
+ priv->cck_txbbgain_ch14_table[6].ccktxbb_valuearray[0] = 0x26;
+ priv->cck_txbbgain_ch14_table[6].ccktxbb_valuearray[1] = 0x25;
+ priv->cck_txbbgain_ch14_table[6].ccktxbb_valuearray[2] = 0x21;
+ priv->cck_txbbgain_ch14_table[6].ccktxbb_valuearray[3] = 0x13;
+ priv->cck_txbbgain_ch14_table[6].ccktxbb_valuearray[4] = 0x00;
+ priv->cck_txbbgain_ch14_table[6].ccktxbb_valuearray[5] = 0x00;
+ priv->cck_txbbgain_ch14_table[6].ccktxbb_valuearray[6] = 0x00;
+ priv->cck_txbbgain_ch14_table[6].ccktxbb_valuearray[7] = 0x00;
+
+ priv->cck_txbbgain_ch14_table[7].ccktxbb_valuearray[0] = 0x24;
+ priv->cck_txbbgain_ch14_table[7].ccktxbb_valuearray[1] = 0x23;
+ priv->cck_txbbgain_ch14_table[7].ccktxbb_valuearray[2] = 0x1f;
+ priv->cck_txbbgain_ch14_table[7].ccktxbb_valuearray[3] = 0x12;
+ priv->cck_txbbgain_ch14_table[7].ccktxbb_valuearray[4] = 0x00;
+ priv->cck_txbbgain_ch14_table[7].ccktxbb_valuearray[5] = 0x00;
+ priv->cck_txbbgain_ch14_table[7].ccktxbb_valuearray[6] = 0x00;
+ priv->cck_txbbgain_ch14_table[7].ccktxbb_valuearray[7] = 0x00;
+
+ priv->cck_txbbgain_ch14_table[8].ccktxbb_valuearray[0] = 0x22;
+ priv->cck_txbbgain_ch14_table[8].ccktxbb_valuearray[1] = 0x21;
+ priv->cck_txbbgain_ch14_table[8].ccktxbb_valuearray[2] = 0x1d;
+ priv->cck_txbbgain_ch14_table[8].ccktxbb_valuearray[3] = 0x11;
+ priv->cck_txbbgain_ch14_table[8].ccktxbb_valuearray[4] = 0x00;
+ priv->cck_txbbgain_ch14_table[8].ccktxbb_valuearray[5] = 0x00;
+ priv->cck_txbbgain_ch14_table[8].ccktxbb_valuearray[6] = 0x00;
+ priv->cck_txbbgain_ch14_table[8].ccktxbb_valuearray[7] = 0x00;
+
+ priv->cck_txbbgain_ch14_table[9].ccktxbb_valuearray[0] = 0x20;
+ priv->cck_txbbgain_ch14_table[9].ccktxbb_valuearray[1] = 0x20;
+ priv->cck_txbbgain_ch14_table[9].ccktxbb_valuearray[2] = 0x1b;
+ priv->cck_txbbgain_ch14_table[9].ccktxbb_valuearray[3] = 0x10;
+ priv->cck_txbbgain_ch14_table[9].ccktxbb_valuearray[4] = 0x00;
+ priv->cck_txbbgain_ch14_table[9].ccktxbb_valuearray[5] = 0x00;
+ priv->cck_txbbgain_ch14_table[9].ccktxbb_valuearray[6] = 0x00;
+ priv->cck_txbbgain_ch14_table[9].ccktxbb_valuearray[7] = 0x00;
+
+ priv->cck_txbbgain_ch14_table[10].ccktxbb_valuearray[0] = 0x1f;
+ priv->cck_txbbgain_ch14_table[10].ccktxbb_valuearray[1] = 0x1e;
+ priv->cck_txbbgain_ch14_table[10].ccktxbb_valuearray[2] = 0x1a;
+ priv->cck_txbbgain_ch14_table[10].ccktxbb_valuearray[3] = 0x0f;
+ priv->cck_txbbgain_ch14_table[10].ccktxbb_valuearray[4] = 0x00;
+ priv->cck_txbbgain_ch14_table[10].ccktxbb_valuearray[5] = 0x00;
+ priv->cck_txbbgain_ch14_table[10].ccktxbb_valuearray[6] = 0x00;
+ priv->cck_txbbgain_ch14_table[10].ccktxbb_valuearray[7] = 0x00;
+
+ priv->cck_txbbgain_ch14_table[11].ccktxbb_valuearray[0] = 0x1d;
+ priv->cck_txbbgain_ch14_table[11].ccktxbb_valuearray[1] = 0x1c;
+ priv->cck_txbbgain_ch14_table[11].ccktxbb_valuearray[2] = 0x18;
+ priv->cck_txbbgain_ch14_table[11].ccktxbb_valuearray[3] = 0x0e;
+ priv->cck_txbbgain_ch14_table[11].ccktxbb_valuearray[4] = 0x00;
+ priv->cck_txbbgain_ch14_table[11].ccktxbb_valuearray[5] = 0x00;
+ priv->cck_txbbgain_ch14_table[11].ccktxbb_valuearray[6] = 0x00;
+ priv->cck_txbbgain_ch14_table[11].ccktxbb_valuearray[7] = 0x00;
+
+ priv->cck_txbbgain_ch14_table[12].ccktxbb_valuearray[0] = 0x1b;
+ priv->cck_txbbgain_ch14_table[12].ccktxbb_valuearray[1] = 0x1a;
+ priv->cck_txbbgain_ch14_table[12].ccktxbb_valuearray[2] = 0x17;
+ priv->cck_txbbgain_ch14_table[12].ccktxbb_valuearray[3] = 0x0e;
+ priv->cck_txbbgain_ch14_table[12].ccktxbb_valuearray[4] = 0x00;
+ priv->cck_txbbgain_ch14_table[12].ccktxbb_valuearray[5] = 0x00;
+ priv->cck_txbbgain_ch14_table[12].ccktxbb_valuearray[6] = 0x00;
+ priv->cck_txbbgain_ch14_table[12].ccktxbb_valuearray[7] = 0x00;
+
+ priv->cck_txbbgain_ch14_table[13].ccktxbb_valuearray[0] = 0x1a;
+ priv->cck_txbbgain_ch14_table[13].ccktxbb_valuearray[1] = 0x19;
+ priv->cck_txbbgain_ch14_table[13].ccktxbb_valuearray[2] = 0x16;
+ priv->cck_txbbgain_ch14_table[13].ccktxbb_valuearray[3] = 0x0d;
+ priv->cck_txbbgain_ch14_table[13].ccktxbb_valuearray[4] = 0x00;
+ priv->cck_txbbgain_ch14_table[13].ccktxbb_valuearray[5] = 0x00;
+ priv->cck_txbbgain_ch14_table[13].ccktxbb_valuearray[6] = 0x00;
+ priv->cck_txbbgain_ch14_table[13].ccktxbb_valuearray[7] = 0x00;
+
+ priv->cck_txbbgain_ch14_table[14].ccktxbb_valuearray[0] = 0x18;
+ priv->cck_txbbgain_ch14_table[14].ccktxbb_valuearray[1] = 0x17;
+ priv->cck_txbbgain_ch14_table[14].ccktxbb_valuearray[2] = 0x15;
+ priv->cck_txbbgain_ch14_table[14].ccktxbb_valuearray[3] = 0x0c;
+ priv->cck_txbbgain_ch14_table[14].ccktxbb_valuearray[4] = 0x00;
+ priv->cck_txbbgain_ch14_table[14].ccktxbb_valuearray[5] = 0x00;
+ priv->cck_txbbgain_ch14_table[14].ccktxbb_valuearray[6] = 0x00;
+ priv->cck_txbbgain_ch14_table[14].ccktxbb_valuearray[7] = 0x00;
+
+ priv->cck_txbbgain_ch14_table[15].ccktxbb_valuearray[0] = 0x17;
+ priv->cck_txbbgain_ch14_table[15].ccktxbb_valuearray[1] = 0x16;
+ priv->cck_txbbgain_ch14_table[15].ccktxbb_valuearray[2] = 0x13;
+ priv->cck_txbbgain_ch14_table[15].ccktxbb_valuearray[3] = 0x0b;
+ priv->cck_txbbgain_ch14_table[15].ccktxbb_valuearray[4] = 0x00;
+ priv->cck_txbbgain_ch14_table[15].ccktxbb_valuearray[5] = 0x00;
+ priv->cck_txbbgain_ch14_table[15].ccktxbb_valuearray[6] = 0x00;
+ priv->cck_txbbgain_ch14_table[15].ccktxbb_valuearray[7] = 0x00;
+
+ priv->cck_txbbgain_ch14_table[16].ccktxbb_valuearray[0] = 0x16;
+ priv->cck_txbbgain_ch14_table[16].ccktxbb_valuearray[1] = 0x15;
+ priv->cck_txbbgain_ch14_table[16].ccktxbb_valuearray[2] = 0x12;
+ priv->cck_txbbgain_ch14_table[16].ccktxbb_valuearray[3] = 0x0b;
+ priv->cck_txbbgain_ch14_table[16].ccktxbb_valuearray[4] = 0x00;
+ priv->cck_txbbgain_ch14_table[16].ccktxbb_valuearray[5] = 0x00;
+ priv->cck_txbbgain_ch14_table[16].ccktxbb_valuearray[6] = 0x00;
+ priv->cck_txbbgain_ch14_table[16].ccktxbb_valuearray[7] = 0x00;
+
+ priv->cck_txbbgain_ch14_table[17].ccktxbb_valuearray[0] = 0x14;
+ priv->cck_txbbgain_ch14_table[17].ccktxbb_valuearray[1] = 0x14;
+ priv->cck_txbbgain_ch14_table[17].ccktxbb_valuearray[2] = 0x11;
+ priv->cck_txbbgain_ch14_table[17].ccktxbb_valuearray[3] = 0x0a;
+ priv->cck_txbbgain_ch14_table[17].ccktxbb_valuearray[4] = 0x00;
+ priv->cck_txbbgain_ch14_table[17].ccktxbb_valuearray[5] = 0x00;
+ priv->cck_txbbgain_ch14_table[17].ccktxbb_valuearray[6] = 0x00;
+ priv->cck_txbbgain_ch14_table[17].ccktxbb_valuearray[7] = 0x00;
+
+ priv->cck_txbbgain_ch14_table[18].ccktxbb_valuearray[0] = 0x13;
+ priv->cck_txbbgain_ch14_table[18].ccktxbb_valuearray[1] = 0x13;
+ priv->cck_txbbgain_ch14_table[18].ccktxbb_valuearray[2] = 0x10;
+ priv->cck_txbbgain_ch14_table[18].ccktxbb_valuearray[3] = 0x0a;
+ priv->cck_txbbgain_ch14_table[18].ccktxbb_valuearray[4] = 0x00;
+ priv->cck_txbbgain_ch14_table[18].ccktxbb_valuearray[5] = 0x00;
+ priv->cck_txbbgain_ch14_table[18].ccktxbb_valuearray[6] = 0x00;
+ priv->cck_txbbgain_ch14_table[18].ccktxbb_valuearray[7] = 0x00;
+
+ priv->cck_txbbgain_ch14_table[19].ccktxbb_valuearray[0] = 0x12;
+ priv->cck_txbbgain_ch14_table[19].ccktxbb_valuearray[1] = 0x12;
+ priv->cck_txbbgain_ch14_table[19].ccktxbb_valuearray[2] = 0x0f;
+ priv->cck_txbbgain_ch14_table[19].ccktxbb_valuearray[3] = 0x09;
+ priv->cck_txbbgain_ch14_table[19].ccktxbb_valuearray[4] = 0x00;
+ priv->cck_txbbgain_ch14_table[19].ccktxbb_valuearray[5] = 0x00;
+ priv->cck_txbbgain_ch14_table[19].ccktxbb_valuearray[6] = 0x00;
+ priv->cck_txbbgain_ch14_table[19].ccktxbb_valuearray[7] = 0x00;
+
+ priv->cck_txbbgain_ch14_table[20].ccktxbb_valuearray[0] = 0x11;
+ priv->cck_txbbgain_ch14_table[20].ccktxbb_valuearray[1] = 0x11;
+ priv->cck_txbbgain_ch14_table[20].ccktxbb_valuearray[2] = 0x0f;
+ priv->cck_txbbgain_ch14_table[20].ccktxbb_valuearray[3] = 0x09;
+ priv->cck_txbbgain_ch14_table[20].ccktxbb_valuearray[4] = 0x00;
+ priv->cck_txbbgain_ch14_table[20].ccktxbb_valuearray[5] = 0x00;
+ priv->cck_txbbgain_ch14_table[20].ccktxbb_valuearray[6] = 0x00;
+ priv->cck_txbbgain_ch14_table[20].ccktxbb_valuearray[7] = 0x00;
+
+ priv->cck_txbbgain_ch14_table[21].ccktxbb_valuearray[0] = 0x10;
+ priv->cck_txbbgain_ch14_table[21].ccktxbb_valuearray[1] = 0x10;
+ priv->cck_txbbgain_ch14_table[21].ccktxbb_valuearray[2] = 0x0e;
+ priv->cck_txbbgain_ch14_table[21].ccktxbb_valuearray[3] = 0x08;
+ priv->cck_txbbgain_ch14_table[21].ccktxbb_valuearray[4] = 0x00;
+ priv->cck_txbbgain_ch14_table[21].ccktxbb_valuearray[5] = 0x00;
+ priv->cck_txbbgain_ch14_table[21].ccktxbb_valuearray[6] = 0x00;
+ priv->cck_txbbgain_ch14_table[21].ccktxbb_valuearray[7] = 0x00;
+
+ priv->cck_txbbgain_ch14_table[22].ccktxbb_valuearray[0] = 0x0f;
+ priv->cck_txbbgain_ch14_table[22].ccktxbb_valuearray[1] = 0x0f;
+ priv->cck_txbbgain_ch14_table[22].ccktxbb_valuearray[2] = 0x0d;
+ priv->cck_txbbgain_ch14_table[22].ccktxbb_valuearray[3] = 0x08;
+ priv->cck_txbbgain_ch14_table[22].ccktxbb_valuearray[4] = 0x00;
+ priv->cck_txbbgain_ch14_table[22].ccktxbb_valuearray[5] = 0x00;
+ priv->cck_txbbgain_ch14_table[22].ccktxbb_valuearray[6] = 0x00;
+ priv->cck_txbbgain_ch14_table[22].ccktxbb_valuearray[7] = 0x00;
+
+ priv->btxpower_tracking = true;
+ priv->txpower_count = 0;
+ priv->btxpower_trackingInit = false;
+
+}
+
+static void dm_InitializeTXPowerTracking_ThermalMeter(struct net_device *dev)
+{
+ struct r8192_priv *priv = ieee80211_priv(dev);
+
+ /*
+ * Tx Power tracking by Thermal Meter requires Firmware R/W 3-wire. This mechanism
+ * can be enabled only when Firmware R/W 3-wire is enabled. Otherwise, frequent r/w
+ * 3-wire by driver causes RF to go into a wrong state.
+ */
+ if (priv->ieee80211->FwRWRF)
+ priv->btxpower_tracking = true;
+ else
+ priv->btxpower_tracking = false;
+ priv->txpower_count = 0;
+ priv->btxpower_trackingInit = false;
+}
+
+void dm_initialize_txpower_tracking(struct net_device *dev)
+{
+ struct r8192_priv *priv = ieee80211_priv(dev);
+
+ if (priv->bDcut == true)
+ dm_InitializeTXPowerTracking_TSSI(dev);
+ else
+ dm_InitializeTXPowerTracking_ThermalMeter(dev);
+} /* dm_InitializeTXPowerTracking */
+
+static void dm_CheckTXPowerTracking_TSSI(struct net_device *dev)
+{
+ struct r8192_priv *priv = ieee80211_priv(dev);
+ static u32 tx_power_track_counter;
+
+ if (!priv->btxpower_tracking)
+ return;
+ if ((tx_power_track_counter % 30 == 0) && (tx_power_track_counter != 0))
+ queue_delayed_work(priv->priv_wq, &priv->txpower_tracking_wq, 0);
+ tx_power_track_counter++;
+}
+
+static void dm_CheckTXPowerTracking_ThermalMeter(struct net_device *dev)
+{
+ struct r8192_priv *priv = ieee80211_priv(dev);
+ static u8 TM_Trigger;
+ /*DbgPrint("dm_CheckTXPowerTracking()\n");*/
+ if (!priv->btxpower_tracking)
+ return;
+ if (priv->txpower_count <= 2) {
+ priv->txpower_count++;
+ return;
+ }
+
+ if (!TM_Trigger) {
+ /*
+ * Attention!! You have to write all 12bits of data to RF, or it may cause RF to crash
+ * actually write reg0x02 bit1=0, then bit1=1.
+ * DbgPrint("Trigger ThermalMeter, write RF reg0x2 = 0x4d to 0x4f\n");
+ */
+ rtl8192_phy_SetRFReg(dev, RF90_PATH_A, 0x02, bMask12Bits, 0x4d);
+ rtl8192_phy_SetRFReg(dev, RF90_PATH_A, 0x02, bMask12Bits, 0x4f);
+ rtl8192_phy_SetRFReg(dev, RF90_PATH_A, 0x02, bMask12Bits, 0x4d);
+ rtl8192_phy_SetRFReg(dev, RF90_PATH_A, 0x02, bMask12Bits, 0x4f);
+ TM_Trigger = 1;
+ return;
+ }
+ /*DbgPrint("Schedule TxPowerTrackingWorkItem\n");*/
+ queue_delayed_work(priv->priv_wq, &priv->txpower_tracking_wq, 0);
+ TM_Trigger = 0;
+}
+
+static void dm_check_txpower_tracking(struct net_device *dev)
+{
+ struct r8192_priv *priv = ieee80211_priv(dev);
+ /*static u32 tx_power_track_counter = 0;*/
+
+#ifdef RTL8190P
+ dm_CheckTXPowerTracking_TSSI(dev);
+#else
+ if (priv->bDcut == true)
+ dm_CheckTXPowerTracking_TSSI(dev);
+ else
+ dm_CheckTXPowerTracking_ThermalMeter(dev);
+#endif
+
+} /* dm_CheckTXPowerTracking */
+
+static void dm_CCKTxPowerAdjust_TSSI(struct net_device *dev, bool bInCH14)
+{
+ u32 TempVal;
+ struct r8192_priv *priv = ieee80211_priv(dev);
+
+ /* Write 0xa22 0xa23 */
+ TempVal = 0;
+ if (!bInCH14) {
+ /* Write 0xa22 0xa23 */
+ TempVal = priv->cck_txbbgain_table[priv->cck_present_attentuation].ccktxbb_valuearray[0] +
+ (priv->cck_txbbgain_table[priv->cck_present_attentuation].ccktxbb_valuearray[1]<<8);
+
+ rtl8192_setBBreg(dev, rCCK0_TxFilter1, bMaskHWord, TempVal);
+ /* Write 0xa24 ~ 0xa27 */
+ TempVal = priv->cck_txbbgain_table[priv->cck_present_attentuation].ccktxbb_valuearray[2] +
+ (priv->cck_txbbgain_table[priv->cck_present_attentuation].ccktxbb_valuearray[3]<<8) +
+ (priv->cck_txbbgain_table[priv->cck_present_attentuation].ccktxbb_valuearray[4]<<16)+
+ (priv->cck_txbbgain_table[priv->cck_present_attentuation].ccktxbb_valuearray[5]<<24);
+ rtl8192_setBBreg(dev, rCCK0_TxFilter2, bMaskDWord, TempVal);
+ /* Write 0xa28 0xa29 */
+ TempVal = priv->cck_txbbgain_table[priv->cck_present_attentuation].ccktxbb_valuearray[6] +
+ (priv->cck_txbbgain_table[priv->cck_present_attentuation].ccktxbb_valuearray[7]<<8);
+
+ rtl8192_setBBreg(dev, rCCK0_DebugPort, bMaskLWord, TempVal);
+ } else {
+ TempVal = priv->cck_txbbgain_ch14_table[priv->cck_present_attentuation].ccktxbb_valuearray[0] +
+ (priv->cck_txbbgain_ch14_table[priv->cck_present_attentuation].ccktxbb_valuearray[1]<<8);
+
+ rtl8192_setBBreg(dev, rCCK0_TxFilter1, bMaskHWord, TempVal);
+ /* Write 0xa24 ~ 0xa27 */
+ TempVal = priv->cck_txbbgain_ch14_table[priv->cck_present_attentuation].ccktxbb_valuearray[2] +
+ (priv->cck_txbbgain_ch14_table[priv->cck_present_attentuation].ccktxbb_valuearray[3]<<8) +
+ (priv->cck_txbbgain_ch14_table[priv->cck_present_attentuation].ccktxbb_valuearray[4]<<16)+
+ (priv->cck_txbbgain_ch14_table[priv->cck_present_attentuation].ccktxbb_valuearray[5]<<24);
+ rtl8192_setBBreg(dev, rCCK0_TxFilter2, bMaskDWord, TempVal);
+ /* Write 0xa28 0xa29 */
+ TempVal = priv->cck_txbbgain_ch14_table[priv->cck_present_attentuation].ccktxbb_valuearray[6] +
+ (priv->cck_txbbgain_ch14_table[priv->cck_present_attentuation].ccktxbb_valuearray[7]<<8);
+
+ rtl8192_setBBreg(dev, rCCK0_DebugPort, bMaskLWord, TempVal);
+ }
+}
+
+static void dm_CCKTxPowerAdjust_ThermalMeter(struct net_device *dev, bool bInCH14)
+{
+ u32 TempVal;
+ struct r8192_priv *priv = ieee80211_priv(dev);
+
+ TempVal = 0;
+ if (!bInCH14) {
+ /* Write 0xa22 0xa23 */
+ TempVal = CCKSwingTable_Ch1_Ch13[priv->CCK_index][0] +
+ (CCKSwingTable_Ch1_Ch13[priv->CCK_index][1]<<8);
+ rtl8192_setBBreg(dev, rCCK0_TxFilter1, bMaskHWord, TempVal);
+ RT_TRACE(COMP_POWER_TRACKING, "CCK not chnl 14, reg 0x%x = 0x%x\n",
+ rCCK0_TxFilter1, TempVal);
+ /* Write 0xa24 ~ 0xa27 */
+ TempVal = CCKSwingTable_Ch1_Ch13[priv->CCK_index][2] +
+ (CCKSwingTable_Ch1_Ch13[priv->CCK_index][3]<<8) +
+ (CCKSwingTable_Ch1_Ch13[priv->CCK_index][4]<<16)+
+ (CCKSwingTable_Ch1_Ch13[priv->CCK_index][5]<<24);
+ rtl8192_setBBreg(dev, rCCK0_TxFilter2, bMaskDWord, TempVal);
+ RT_TRACE(COMP_POWER_TRACKING, "CCK not chnl 14, reg 0x%x = 0x%x\n",
+ rCCK0_TxFilter2, TempVal);
+ /* Write 0xa28 0xa29 */
+ TempVal = CCKSwingTable_Ch1_Ch13[priv->CCK_index][6] +
+ (CCKSwingTable_Ch1_Ch13[priv->CCK_index][7]<<8);
+
+ rtl8192_setBBreg(dev, rCCK0_DebugPort, bMaskLWord, TempVal);
+ RT_TRACE(COMP_POWER_TRACKING, "CCK not chnl 14, reg 0x%x = 0x%x\n",
+ rCCK0_DebugPort, TempVal);
+ } else {
+ /*priv->CCKTxPowerAdjustCntNotCh14++; cosa add for debug.*/
+ /* Write 0xa22 0xa23 */
+ TempVal = CCKSwingTable_Ch14[priv->CCK_index][0] +
+ (CCKSwingTable_Ch14[priv->CCK_index][1]<<8);
+
+ rtl8192_setBBreg(dev, rCCK0_TxFilter1, bMaskHWord, TempVal);
+ RT_TRACE(COMP_POWER_TRACKING, "CCK chnl 14, reg 0x%x = 0x%x\n",
+ rCCK0_TxFilter1, TempVal);
+ /* Write 0xa24 ~ 0xa27 */
+ TempVal = CCKSwingTable_Ch14[priv->CCK_index][2] +
+ (CCKSwingTable_Ch14[priv->CCK_index][3]<<8) +
+ (CCKSwingTable_Ch14[priv->CCK_index][4]<<16)+
+ (CCKSwingTable_Ch14[priv->CCK_index][5]<<24);
+ rtl8192_setBBreg(dev, rCCK0_TxFilter2, bMaskDWord, TempVal);
+ RT_TRACE(COMP_POWER_TRACKING, "CCK chnl 14, reg 0x%x = 0x%x\n",
+ rCCK0_TxFilter2, TempVal);
+ /* Write 0xa28 0xa29 */
+ TempVal = CCKSwingTable_Ch14[priv->CCK_index][6] +
+ (CCKSwingTable_Ch14[priv->CCK_index][7]<<8);
+
+ rtl8192_setBBreg(dev, rCCK0_DebugPort, bMaskLWord, TempVal);
+ RT_TRACE(COMP_POWER_TRACKING, "CCK chnl 14, reg 0x%x = 0x%x\n",
+ rCCK0_DebugPort, TempVal);
+ }
+}
+
+void dm_cck_txpower_adjust(struct net_device *dev, bool binch14)
+{ /* dm_CCKTxPowerAdjust */
+ struct r8192_priv *priv = ieee80211_priv(dev);
+
+ if (priv->bDcut == true)
+ dm_CCKTxPowerAdjust_TSSI(dev, binch14);
+ else
+ dm_CCKTxPowerAdjust_ThermalMeter(dev, binch14);
+}
+
+#ifndef RTL8192U
+static void dm_txpower_reset_recovery(
+ struct net_device *dev
+)
+{
+ struct r8192_priv *priv = ieee80211_priv(dev);
+
+ RT_TRACE(COMP_POWER_TRACKING, "Start Reset Recovery ==>\n");
+ rtl8192_setBBreg(dev, rOFDM0_XATxIQImbalance, bMaskDWord, priv->txbbgain_table[priv->rfa_txpowertrackingindex].txbbgain_value);
+ RT_TRACE(COMP_POWER_TRACKING, "Reset Recovery: Fill in 0xc80 is %08x\n", priv->txbbgain_table[priv->rfa_txpowertrackingindex].txbbgain_value);
+ RT_TRACE(COMP_POWER_TRACKING, "Reset Recovery: Fill in RFA_txPowerTrackingIndex is %x\n", priv->rfa_txpowertrackingindex);
+ RT_TRACE(COMP_POWER_TRACKING, "Reset Recovery : RF A I/Q Amplify Gain is %ld\n", priv->txbbgain_table[priv->rfa_txpowertrackingindex].txbb_iq_amplifygain);
+ RT_TRACE(COMP_POWER_TRACKING, "Reset Recovery: CCK Attenuation is %d dB\n", priv->cck_present_attentuation);
+ dm_cck_txpower_adjust(dev, priv->bcck_in_ch14);
+
+ rtl8192_setBBreg(dev, rOFDM0_XCTxIQImbalance, bMaskDWord, priv->txbbgain_table[priv->rfc_txpowertrackingindex].txbbgain_value);
+ RT_TRACE(COMP_POWER_TRACKING, "Reset Recovery: Fill in 0xc90 is %08x\n", priv->txbbgain_table[priv->rfc_txpowertrackingindex].txbbgain_value);
+ RT_TRACE(COMP_POWER_TRACKING, "Reset Recovery: Fill in RFC_txPowerTrackingIndex is %x\n", priv->rfc_txpowertrackingindex);
+ RT_TRACE(COMP_POWER_TRACKING, "Reset Recovery : RF C I/Q Amplify Gain is %ld\n", priv->txbbgain_table[priv->rfc_txpowertrackingindex].txbb_iq_amplifygain);
+
+} /* dm_TXPowerResetRecovery */
+
+void dm_restore_dynamic_mechanism_state(struct net_device *dev)
+{
+ struct r8192_priv *priv = ieee80211_priv(dev);
+ u32 reg_ratr = priv->rate_adaptive.last_ratr;
+
+ if (!priv->up) {
+ RT_TRACE(COMP_RATE, "<---- dm_restore_dynamic_mechanism_state(): driver is going to unload\n");
+ return;
+ }
+
+ /* Restore previous state for rate adaptive */
+ if (priv->rate_adaptive.rate_adaptive_disabled)
+ return;
+ /* TODO: Only 11n mode is implemented currently, */
+ if (!(priv->ieee80211->mode == WIRELESS_MODE_N_24G ||
+ priv->ieee80211->mode == WIRELESS_MODE_N_5G))
+ return;
+
+ {
+ /* 2007/11/15 MH Copy from 8190PCI. */
+ u32 ratr_value;
+
+ ratr_value = reg_ratr;
+ if (priv->rf_type == RF_1T2R) { /* 1T2R, Spatial Stream 2 should be disabled */
+ ratr_value &= ~(RATE_ALL_OFDM_2SS);
+ /*DbgPrint("HW_VAR_TATR_0 from 0x%x ==> 0x%x\n", ((pu4Byte)(val))[0], ratr_value);*/
+ }
+ /*DbgPrint("set HW_VAR_TATR_0 = 0x%x\n", ratr_value);*/
+ /*cosa PlatformEFIOWrite4Byte(Adapter, RATR0, ((pu4Byte)(val))[0]);*/
+ write_nic_dword(dev, RATR0, ratr_value);
+ write_nic_byte(dev, UFWP, 1);
+ }
+ /* Restore TX Power Tracking Index */
+ if (priv->btxpower_trackingInit && priv->btxpower_tracking)
+ dm_txpower_reset_recovery(dev);
+
+ /* Restore BB Initial Gain */
+ dm_bb_initialgain_restore(dev);
+
+} /* DM_RestoreDynamicMechanismState */
+
+static void dm_bb_initialgain_restore(struct net_device *dev)
+{
+ struct r8192_priv *priv = ieee80211_priv(dev);
+ u32 bit_mask = 0x7f; /* Bit0~ Bit6 */
+
+ if (dm_digtable.dig_algorithm == DIG_ALGO_BY_RSSI)
+ return;
+
+ /* Disable Initial Gain */
+ /*PHY_SetBBReg(Adapter, UFWP, bMaskLWord, 0x800);*/
+ rtl8192_setBBreg(dev, UFWP, bMaskByte1, 0x8); /* Only clear byte 1 and rewrite. */
+ rtl8192_setBBreg(dev, rOFDM0_XAAGCCore1, bit_mask, (u32)priv->initgain_backup.xaagccore1);
+ rtl8192_setBBreg(dev, rOFDM0_XBAGCCore1, bit_mask, (u32)priv->initgain_backup.xbagccore1);
+ rtl8192_setBBreg(dev, rOFDM0_XCAGCCore1, bit_mask, (u32)priv->initgain_backup.xcagccore1);
+ rtl8192_setBBreg(dev, rOFDM0_XDAGCCore1, bit_mask, (u32)priv->initgain_backup.xdagccore1);
+ bit_mask = bMaskByte2;
+ rtl8192_setBBreg(dev, rCCK0_CCA, bit_mask, (u32)priv->initgain_backup.cca);
+
+ RT_TRACE(COMP_DIG, "dm_BBInitialGainRestore 0xc50 is %x\n", priv->initgain_backup.xaagccore1);
+ RT_TRACE(COMP_DIG, "dm_BBInitialGainRestore 0xc58 is %x\n", priv->initgain_backup.xbagccore1);
+ RT_TRACE(COMP_DIG, "dm_BBInitialGainRestore 0xc60 is %x\n", priv->initgain_backup.xcagccore1);
+ RT_TRACE(COMP_DIG, "dm_BBInitialGainRestore 0xc68 is %x\n", priv->initgain_backup.xdagccore1);
+ RT_TRACE(COMP_DIG, "dm_BBInitialGainRestore 0xa0a is %x\n", priv->initgain_backup.cca);
+ /* Enable Initial Gain */
+ /*PHY_SetBBReg(Adapter, UFWP, bMaskLWord, 0x100);*/
+ rtl8192_setBBreg(dev, UFWP, bMaskByte1, 0x1); /* Only clear byte 1 and rewrite. */
+
+} /* dm_BBInitialGainRestore */
+
+void dm_backup_dynamic_mechanism_state(struct net_device *dev)
+{
+ struct r8192_priv *priv = ieee80211_priv(dev);
+
+ /* Fsync to avoid reset */
+ priv->bswitch_fsync = false;
+ priv->bfsync_processing = false;
+ /* Backup BB InitialGain */
+ dm_bb_initialgain_backup(dev);
+
+} /* DM_BackupDynamicMechanismState */
+
+static void dm_bb_initialgain_backup(struct net_device *dev)
+{
+ struct r8192_priv *priv = ieee80211_priv(dev);
+ u32 bit_mask = bMaskByte0; /* Bit0~ Bit6 */
+
+ if (dm_digtable.dig_algorithm == DIG_ALGO_BY_RSSI)
+ return;
+
+ /*PHY_SetBBReg(Adapter, UFWP, bMaskLWord, 0x800);*/
+ rtl8192_setBBreg(dev, UFWP, bMaskByte1, 0x8); /* Only clear byte 1 and rewrite. */
+ priv->initgain_backup.xaagccore1 = (u8)rtl8192_QueryBBReg(dev, rOFDM0_XAAGCCore1, bit_mask);
+ priv->initgain_backup.xbagccore1 = (u8)rtl8192_QueryBBReg(dev, rOFDM0_XBAGCCore1, bit_mask);
+ priv->initgain_backup.xcagccore1 = (u8)rtl8192_QueryBBReg(dev, rOFDM0_XCAGCCore1, bit_mask);
+ priv->initgain_backup.xdagccore1 = (u8)rtl8192_QueryBBReg(dev, rOFDM0_XDAGCCore1, bit_mask);
+ bit_mask = bMaskByte2;
+ priv->initgain_backup.cca = (u8)rtl8192_QueryBBReg(dev, rCCK0_CCA, bit_mask);
+
+ RT_TRACE(COMP_DIG, "BBInitialGainBackup 0xc50 is %x\n", priv->initgain_backup.xaagccore1);
+ RT_TRACE(COMP_DIG, "BBInitialGainBackup 0xc58 is %x\n", priv->initgain_backup.xbagccore1);
+ RT_TRACE(COMP_DIG, "BBInitialGainBackup 0xc60 is %x\n", priv->initgain_backup.xcagccore1);
+ RT_TRACE(COMP_DIG, "BBInitialGainBackup 0xc68 is %x\n", priv->initgain_backup.xdagccore1);
+ RT_TRACE(COMP_DIG, "BBInitialGainBackup 0xa0a is %x\n", priv->initgain_backup.cca);
+
+} /* dm_BBInitialGainBakcup */
+
+#endif
+/*-----------------------------------------------------------------------------
+ * Function: dm_change_dynamic_initgain_thresh()
+ *
+ * Overview:
+ *
+ * Input: NONE
+ *
+ * Output: NONE
+ *
+ * Return: NONE
+ *
+ * Revised History:
+ * When Who Remark
+ * 05/29/2008 amy Create Version 0 porting from windows code.
+ *
+ *---------------------------------------------------------------------------*/
+
+void dm_change_dynamic_initgain_thresh(struct net_device *dev, u32 dm_type,
+ u32 dm_value)
+{
+ if (dm_type == DIG_TYPE_THRESH_HIGH) {
+ dm_digtable.rssi_high_thresh = dm_value;
+ } else if (dm_type == DIG_TYPE_THRESH_LOW) {
+ dm_digtable.rssi_low_thresh = dm_value;
+ } else if (dm_type == DIG_TYPE_THRESH_HIGHPWR_HIGH) {
+ dm_digtable.rssi_high_power_highthresh = dm_value;
+ } else if (dm_type == DIG_TYPE_THRESH_HIGHPWR_LOW) {
+ dm_digtable.rssi_high_power_lowthresh = dm_value;
+ } else if (dm_type == DIG_TYPE_ENABLE) {
+ dm_digtable.dig_state = DM_STA_DIG_MAX;
+ dm_digtable.dig_enable_flag = true;
+ } else if (dm_type == DIG_TYPE_DISABLE) {
+ dm_digtable.dig_state = DM_STA_DIG_MAX;
+ dm_digtable.dig_enable_flag = false;
+ } else if (dm_type == DIG_TYPE_DBG_MODE) {
+ if (dm_value >= DM_DBG_MAX)
+ dm_value = DM_DBG_OFF;
+ dm_digtable.dbg_mode = (u8)dm_value;
+ } else if (dm_type == DIG_TYPE_RSSI) {
+ if (dm_value > 100)
+ dm_value = 30;
+ dm_digtable.rssi_val = (long)dm_value;
+ } else if (dm_type == DIG_TYPE_ALGORITHM) {
+ if (dm_value >= DIG_ALGO_MAX)
+ dm_value = DIG_ALGO_BY_FALSE_ALARM;
+ if (dm_digtable.dig_algorithm != (u8)dm_value)
+ dm_digtable.dig_algorithm_switch = 1;
+ dm_digtable.dig_algorithm = (u8)dm_value;
+ } else if (dm_type == DIG_TYPE_BACKOFF) {
+ if (dm_value > 30)
+ dm_value = 30;
+ dm_digtable.backoff_val = (u8)dm_value;
+ } else if (dm_type == DIG_TYPE_RX_GAIN_MIN) {
+ if (dm_value == 0)
+ dm_value = 0x1;
+ dm_digtable.rx_gain_range_min = (u8)dm_value;
+ } else if (dm_type == DIG_TYPE_RX_GAIN_MAX) {
+ if (dm_value > 0x50)
+ dm_value = 0x50;
+ dm_digtable.rx_gain_range_max = (u8)dm_value;
+ }
+} /* DM_ChangeDynamicInitGainThresh */
+
+/*-----------------------------------------------------------------------------
+ * Function: dm_dig_init()
+ *
+ * Overview: Set DIG scheme init value.
+ *
+ * Input: NONE
+ *
+ * Output: NONE
+ *
+ * Return: NONE
+ *
+ * Revised History:
+ * When Who Remark
+ * 05/15/2008 amy Create Version 0 porting from windows code.
+ *
+ *---------------------------------------------------------------------------*/
+static void dm_dig_init(struct net_device *dev)
+{
+ struct r8192_priv *priv = ieee80211_priv(dev);
+ /* 2007/10/05 MH Disable DIG scheme now. Not tested. */
+ dm_digtable.dig_enable_flag = true;
+ dm_digtable.dig_algorithm = DIG_ALGO_BY_RSSI;
+ dm_digtable.dbg_mode = DM_DBG_OFF; /* off=by real rssi value, on=by DM_DigTable.Rssi_val for new dig */
+ dm_digtable.dig_algorithm_switch = 0;
+
+ /* 2007/10/04 MH Define init gain threshold. */
+ dm_digtable.dig_state = DM_STA_DIG_MAX;
+ dm_digtable.dig_highpwr_state = DM_STA_DIG_MAX;
+ dm_digtable.initialgain_lowerbound_state = false;
+
+ dm_digtable.rssi_low_thresh = DM_DIG_THRESH_LOW;
+ dm_digtable.rssi_high_thresh = DM_DIG_THRESH_HIGH;
+
+ dm_digtable.rssi_high_power_lowthresh = DM_DIG_HIGH_PWR_THRESH_LOW;
+ dm_digtable.rssi_high_power_highthresh = DM_DIG_HIGH_PWR_THRESH_HIGH;
+
+ dm_digtable.rssi_val = 50; /* for new dig debug rssi value */
+ dm_digtable.backoff_val = DM_DIG_BACKOFF;
+ dm_digtable.rx_gain_range_max = DM_DIG_MAX;
+ if (priv->CustomerID == RT_CID_819x_Netcore)
+ dm_digtable.rx_gain_range_min = DM_DIG_MIN_Netcore;
+ else
+ dm_digtable.rx_gain_range_min = DM_DIG_MIN;
+
+} /* dm_dig_init */
+
+/*-----------------------------------------------------------------------------
+ * Function: dm_ctrl_initgain_byrssi()
+ *
+ * Overview: Driver must monitor RSSI and notify firmware to change initial
+ * gain according to different threshold. BB team provide the
+ * suggested solution.
+ *
+ * Input: struct net_device *dev
+ *
+ * Output: NONE
+ *
+ * Return: NONE
+ *
+ * Revised History:
+ * When Who Remark
+ * 05/27/2008 amy Create Version 0 porting from windows code.
+ *---------------------------------------------------------------------------*/
+static void dm_ctrl_initgain_byrssi(struct net_device *dev)
+{
+ if (dm_digtable.dig_enable_flag == false)
+ return;
+
+ if (dm_digtable.dig_algorithm == DIG_ALGO_BY_FALSE_ALARM)
+ dm_ctrl_initgain_byrssi_by_fwfalse_alarm(dev);
+ else if (dm_digtable.dig_algorithm == DIG_ALGO_BY_RSSI)
+ dm_ctrl_initgain_byrssi_by_driverrssi(dev);
+ /* ; */
+ else
+ return;
+}
+
+static void dm_ctrl_initgain_byrssi_by_driverrssi(
+ struct net_device *dev)
+{
+ struct r8192_priv *priv = ieee80211_priv(dev);
+ u8 i;
+ static u8 fw_dig;
+
+ if (dm_digtable.dig_enable_flag == false)
+ return;
+
+ /*DbgPrint("Dig by Sw Rssi\n");*/
+ if (dm_digtable.dig_algorithm_switch) /* if switched algorithm, we have to disable FW Dig. */
+ fw_dig = 0;
+
+ if (fw_dig <= 3) { /* execute several times to make sure the FW Dig is disabled */
+ /* FW DIG Off */
+ for (i = 0; i < 3; i++)
+ rtl8192_setBBreg(dev, UFWP, bMaskByte1, 0x8); /* Only clear byte 1 and rewrite. */
+ fw_dig++;
+ dm_digtable.dig_state = DM_STA_DIG_OFF; /* fw dig off. */
+ }
+
+ if (priv->ieee80211->state == IEEE80211_LINKED)
+ dm_digtable.cur_connect_state = DIG_CONNECT;
+ else
+ dm_digtable.cur_connect_state = DIG_DISCONNECT;
+
+ /*DbgPrint("DM_DigTable.PreConnectState = %d, DM_DigTable.CurConnectState = %d\n",
+ DM_DigTable.PreConnectState, DM_DigTable.CurConnectState);*/
+
+ if (dm_digtable.dbg_mode == DM_DBG_OFF)
+ dm_digtable.rssi_val = priv->undecorated_smoothed_pwdb;
+ /*DbgPrint("DM_DigTable.Rssi_val = %d\n", DM_DigTable.Rssi_val);*/
+ dm_initial_gain(dev);
+ dm_pd_th(dev);
+ dm_cs_ratio(dev);
+ if (dm_digtable.dig_algorithm_switch)
+ dm_digtable.dig_algorithm_switch = 0;
+ dm_digtable.pre_connect_state = dm_digtable.cur_connect_state;
+
+} /* dm_CtrlInitGainByRssi */
+
+static void dm_ctrl_initgain_byrssi_by_fwfalse_alarm(
+ struct net_device *dev)
+{
+ struct r8192_priv *priv = ieee80211_priv(dev);
+ static u32 reset_cnt;
+ u8 i;
+
+ if (dm_digtable.dig_enable_flag == false)
+ return;
+
+ if (dm_digtable.dig_algorithm_switch) {
+ dm_digtable.dig_state = DM_STA_DIG_MAX;
+ /* Fw DIG On. */
+ for (i = 0; i < 3; i++)
+ rtl8192_setBBreg(dev, UFWP, bMaskByte1, 0x1); /* Only clear byte 1 and rewrite.*/
+ dm_digtable.dig_algorithm_switch = 0;
+ }
+
+ if (priv->ieee80211->state != IEEE80211_LINKED)
+ return;
+
+ /* For smooth, we can not change DIG state. */
+ if ((priv->undecorated_smoothed_pwdb > dm_digtable.rssi_low_thresh) &&
+ (priv->undecorated_smoothed_pwdb < dm_digtable.rssi_high_thresh))
+ return;
+
+ /*DbgPrint("Dig by Fw False Alarm\n");*/
+ /*if (DM_DigTable.Dig_State == DM_STA_DIG_OFF)*/
+ /*DbgPrint("DIG Check\n\r RSSI=%d LOW=%d HIGH=%d STATE=%d",
+ pHalData->UndecoratedSmoothedPWDB, DM_DigTable.RssiLowThresh,
+ DM_DigTable.RssiHighThresh, DM_DigTable.Dig_State);*/
+ /* 1. When RSSI decrease, We have to judge if it is smaller than a threshold
+ and then execute the step below. */
+ if (priv->undecorated_smoothed_pwdb <= dm_digtable.rssi_low_thresh) {
+ /* 2008/02/05 MH When we execute silent reset, the DIG PHY parameters
+ will be reset to init value. We must prevent the condition. */
+ if (dm_digtable.dig_state == DM_STA_DIG_OFF &&
+ (priv->reset_count == reset_cnt)) {
+ return;
+ }
+ reset_cnt = priv->reset_count;
+
+ /* If DIG is off, DIG high power state must reset. */
+ dm_digtable.dig_highpwr_state = DM_STA_DIG_MAX;
+ dm_digtable.dig_state = DM_STA_DIG_OFF;
+
+ /* 1.1 DIG Off. */
+ rtl8192_setBBreg(dev, UFWP, bMaskByte1, 0x8); /* Only clear byte 1 and rewrite. */
+
+ /* 1.2 Set initial gain. */
+ write_nic_byte(dev, rOFDM0_XAAGCCore1, 0x17);
+ write_nic_byte(dev, rOFDM0_XBAGCCore1, 0x17);
+ write_nic_byte(dev, rOFDM0_XCAGCCore1, 0x17);
+ write_nic_byte(dev, rOFDM0_XDAGCCore1, 0x17);
+
+ /* 1.3 Lower PD_TH for OFDM. */
+ if (priv->CurrentChannelBW != HT_CHANNEL_WIDTH_20) {
+ /*
+ * 2008/01/11 MH 40MHZ 90/92 register are not the same.
+ * 2008/02/05 MH SD3-Jerry 92U/92E PD_TH are the same.
+ */
+ write_nic_byte(dev, (rOFDM0_XATxAFE+3), 0x00);
+ /*else if (priv->card_8192 == HARDWARE_TYPE_RTL8190P)
+ write_nic_byte(pAdapter, rOFDM0_RxDetector1, 0x40);
+ else if (pAdapter->HardwareType == HARDWARE_TYPE_RTL8192E)
+ else
+ PlatformEFIOWrite1Byte(pAdapter, rOFDM0_RxDetector1, 0x40);
+ */
+ } else
+ write_nic_byte(dev, rOFDM0_RxDetector1, 0x42);
+
+ /* 1.4 Lower CS ratio for CCK. */
+ write_nic_byte(dev, 0xa0a, 0x08);
+
+ /* 1.5 Higher EDCCA. */
+ /*PlatformEFIOWrite4Byte(pAdapter, rOFDM0_ECCAThreshold, 0x325);*/
+ return;
+
+ }
+
+ /* 2. When RSSI increase, We have to judge if it is larger than a threshold
+ and then execute the step below. */
+ if (priv->undecorated_smoothed_pwdb >= dm_digtable.rssi_high_thresh) {
+ u8 reset_flag = 0;
+
+ if (dm_digtable.dig_state == DM_STA_DIG_ON &&
+ (priv->reset_count == reset_cnt)) {
+ dm_ctrl_initgain_byrssi_highpwr(dev);
+ return;
+ }
+ if (priv->reset_count != reset_cnt)
+ reset_flag = 1;
+
+ reset_cnt = priv->reset_count;
+
+ dm_digtable.dig_state = DM_STA_DIG_ON;
+ /*DbgPrint("DIG ON\n\r");*/
+
+ /*
+ * 2.1 Set initial gain.
+ * 2008/02/26 MH SD3-Jerry suggest to prevent dirty environment.
+ */
+ if (reset_flag == 1) {
+ write_nic_byte(dev, rOFDM0_XAAGCCore1, 0x2c);
+ write_nic_byte(dev, rOFDM0_XBAGCCore1, 0x2c);
+ write_nic_byte(dev, rOFDM0_XCAGCCore1, 0x2c);
+ write_nic_byte(dev, rOFDM0_XDAGCCore1, 0x2c);
+ } else {
+ write_nic_byte(dev, rOFDM0_XAAGCCore1, 0x20);
+ write_nic_byte(dev, rOFDM0_XBAGCCore1, 0x20);
+ write_nic_byte(dev, rOFDM0_XCAGCCore1, 0x20);
+ write_nic_byte(dev, rOFDM0_XDAGCCore1, 0x20);
+ }
+
+ /* 2.2 Higher PD_TH for OFDM. */
+ if (priv->CurrentChannelBW != HT_CHANNEL_WIDTH_20) {
+ /*
+ * 2008/01/11 MH 40MHZ 90/92 register are not the same.
+ * 2008/02/05 MH SD3-Jerry 92U/92E PD_TH are the same.
+ */
+ write_nic_byte(dev, (rOFDM0_XATxAFE+3), 0x20);
+ /*
+ else if (priv->card_8192 == HARDWARE_TYPE_RTL8190P)
+ write_nic_byte(dev, rOFDM0_RxDetector1, 0x42);
+ else if (pAdapter->HardwareType == HARDWARE_TYPE_RTL8192E)
+ else
+ PlatformEFIOWrite1Byte(pAdapter, rOFDM0_RxDetector1, 0x42);
+ */
+ } else
+ write_nic_byte(dev, rOFDM0_RxDetector1, 0x44);
+
+ /* 2.3 Higher CS ratio for CCK. */
+ write_nic_byte(dev, 0xa0a, 0xcd);
+
+ /*
+ * 2.4 Lower EDCCA.
+ * 2008/01/11 MH 90/92 series are the same.
+ */
+ /*PlatformEFIOWrite4Byte(pAdapter, rOFDM0_ECCAThreshold, 0x346);*/
+
+ /* 2.5 DIG On. */
+ rtl8192_setBBreg(dev, UFWP, bMaskByte1, 0x1); /* Only clear byte 1 and rewrite. */
+
+ }
+
+ dm_ctrl_initgain_byrssi_highpwr(dev);
+
+} /* dm_CtrlInitGainByRssi */
+
+/*-----------------------------------------------------------------------------
+ * Function: dm_ctrl_initgain_byrssi_highpwr()
+ *
+ * Overview:
+ *
+ * Input: NONE
+ *
+ * Output: NONE
+ *
+ * Return: NONE
+ *
+ * Revised History:
+ * When Who Remark
+ * 05/28/2008 amy Create Version 0 porting from windows code.
+ *
+ *---------------------------------------------------------------------------*/
+static void dm_ctrl_initgain_byrssi_highpwr(
+ struct net_device *dev)
+{
+ struct r8192_priv *priv = ieee80211_priv(dev);
+ static u32 reset_cnt_highpwr;
+
+ /* For smooth, we can not change high power DIG state in the range. */
+ if ((priv->undecorated_smoothed_pwdb > dm_digtable.rssi_high_power_lowthresh) &&
+ (priv->undecorated_smoothed_pwdb < dm_digtable.rssi_high_power_highthresh))
+ return;
+
+ /*
+ * 3. When RSSI >75% or <70%, it is a high power issue. We have to judge if
+ * it is larger than a threshold and then execute the step below.
+ *
+ * 2008/02/05 MH SD3-Jerry Modify PD_TH for high power issue.
+ */
+ if (priv->undecorated_smoothed_pwdb >= dm_digtable.rssi_high_power_highthresh) {
+ if (dm_digtable.dig_highpwr_state == DM_STA_DIG_ON &&
+ (priv->reset_count == reset_cnt_highpwr))
+ return;
+ dm_digtable.dig_highpwr_state = DM_STA_DIG_ON;
+
+ /* 3.1 Higher PD_TH for OFDM for high power state. */
+ if (priv->CurrentChannelBW != HT_CHANNEL_WIDTH_20) {
+ write_nic_byte(dev, (rOFDM0_XATxAFE+3), 0x10);
+
+ /*else if (priv->card_8192 == HARDWARE_TYPE_RTL8190P)
+ write_nic_byte(dev, rOFDM0_RxDetector1, 0x41);
+ */
+
+ } else
+ write_nic_byte(dev, rOFDM0_RxDetector1, 0x43);
+ } else {
+ if (dm_digtable.dig_highpwr_state == DM_STA_DIG_OFF &&
+ (priv->reset_count == reset_cnt_highpwr))
+ return;
+ dm_digtable.dig_highpwr_state = DM_STA_DIG_OFF;
+
+ if (priv->undecorated_smoothed_pwdb < dm_digtable.rssi_high_power_lowthresh &&
+ priv->undecorated_smoothed_pwdb >= dm_digtable.rssi_high_thresh) {
+ /* 3.2 Recover PD_TH for OFDM for normal power region. */
+ if (priv->CurrentChannelBW != HT_CHANNEL_WIDTH_20) {
+ write_nic_byte(dev, (rOFDM0_XATxAFE+3), 0x20);
+ /*else if (priv->card_8192 == HARDWARE_TYPE_RTL8190P)
+ write_nic_byte(dev, rOFDM0_RxDetector1, 0x42);
+ */
+
+ } else
+ write_nic_byte(dev, rOFDM0_RxDetector1, 0x44);
+ }
+ }
+
+ reset_cnt_highpwr = priv->reset_count;
+
+} /* dm_CtrlInitGainByRssiHighPwr */
+
+static void dm_initial_gain(
+ struct net_device *dev)
+{
+ struct r8192_priv *priv = ieee80211_priv(dev);
+ u8 initial_gain = 0;
+ static u8 initialized, force_write;
+ static u32 reset_cnt;
+ u8 tmp;
+
+ if (dm_digtable.dig_algorithm_switch) {
+ initialized = 0;
+ reset_cnt = 0;
+ }
+
+ if (dm_digtable.pre_connect_state == dm_digtable.cur_connect_state) {
+ if (dm_digtable.cur_connect_state == DIG_CONNECT) {
+ if ((dm_digtable.rssi_val+10-dm_digtable.backoff_val) > dm_digtable.rx_gain_range_max)
+ dm_digtable.cur_ig_value = dm_digtable.rx_gain_range_max;
+ else if ((dm_digtable.rssi_val+10-dm_digtable.backoff_val) < dm_digtable.rx_gain_range_min)
+ dm_digtable.cur_ig_value = dm_digtable.rx_gain_range_min;
+ else
+ dm_digtable.cur_ig_value = dm_digtable.rssi_val+10-dm_digtable.backoff_val;
+ } else { /* current state is disconnected */
+ if (dm_digtable.cur_ig_value == 0)
+ dm_digtable.cur_ig_value = priv->DefaultInitialGain[0];
+ else
+ dm_digtable.cur_ig_value = dm_digtable.pre_ig_value;
+ }
+ } else { /* disconnected -> connected or connected -> disconnected */
+ dm_digtable.cur_ig_value = priv->DefaultInitialGain[0];
+ dm_digtable.pre_ig_value = 0;
+ }
+ /*DbgPrint("DM_DigTable.CurIGValue = 0x%x, DM_DigTable.PreIGValue = 0x%x\n", DM_DigTable.CurIGValue, DM_DigTable.PreIGValue);*/
+
+ /* if silent reset happened, we should rewrite the values back */
+ if (priv->reset_count != reset_cnt) {
+ force_write = 1;
+ reset_cnt = priv->reset_count;
+ }
+
+ read_nic_byte(dev, rOFDM0_XAAGCCore1, &tmp);
+ if (dm_digtable.pre_ig_value != tmp)
+ force_write = 1;
+
+ {
+ if ((dm_digtable.pre_ig_value != dm_digtable.cur_ig_value)
+ || !initialized || force_write) {
+ initial_gain = (u8)dm_digtable.cur_ig_value;
+ /*DbgPrint("Write initial gain = 0x%x\n", initial_gain);*/
+ /* Set initial gain. */
+ write_nic_byte(dev, rOFDM0_XAAGCCore1, initial_gain);
+ write_nic_byte(dev, rOFDM0_XBAGCCore1, initial_gain);
+ write_nic_byte(dev, rOFDM0_XCAGCCore1, initial_gain);
+ write_nic_byte(dev, rOFDM0_XDAGCCore1, initial_gain);
+ dm_digtable.pre_ig_value = dm_digtable.cur_ig_value;
+ initialized = 1;
+ force_write = 0;
+ }
+ }
+}
+
+static void dm_pd_th(
+ struct net_device *dev)
+{
+ struct r8192_priv *priv = ieee80211_priv(dev);
+ static u8 initialized, force_write;
+ static u32 reset_cnt;
+
+ if (dm_digtable.dig_algorithm_switch) {
+ initialized = 0;
+ reset_cnt = 0;
+ }
+
+ if (dm_digtable.pre_connect_state == dm_digtable.cur_connect_state) {
+ if (dm_digtable.cur_connect_state == DIG_CONNECT) {
+ if (dm_digtable.rssi_val >= dm_digtable.rssi_high_power_highthresh)
+ dm_digtable.curpd_thstate = DIG_PD_AT_HIGH_POWER;
+ else if (dm_digtable.rssi_val <= dm_digtable.rssi_low_thresh)
+ dm_digtable.curpd_thstate = DIG_PD_AT_LOW_POWER;
+ else if ((dm_digtable.rssi_val >= dm_digtable.rssi_high_thresh) &&
+ (dm_digtable.rssi_val < dm_digtable.rssi_high_power_lowthresh))
+ dm_digtable.curpd_thstate = DIG_PD_AT_NORMAL_POWER;
+ else
+ dm_digtable.curpd_thstate = dm_digtable.prepd_thstate;
+ } else {
+ dm_digtable.curpd_thstate = DIG_PD_AT_LOW_POWER;
+ }
+ } else { /* disconnected -> connected or connected -> disconnected */
+ dm_digtable.curpd_thstate = DIG_PD_AT_LOW_POWER;
+ }
+
+ /* if silent reset happened, we should rewrite the values back */
+ if (priv->reset_count != reset_cnt) {
+ force_write = 1;
+ reset_cnt = priv->reset_count;
+ }
+
+ {
+ if ((dm_digtable.prepd_thstate != dm_digtable.curpd_thstate) ||
+ (initialized <= 3) || force_write) {
+ /*DbgPrint("Write PD_TH state = %d\n", DM_DigTable.CurPD_THState);*/
+ if (dm_digtable.curpd_thstate == DIG_PD_AT_LOW_POWER) {
+ /* Lower PD_TH for OFDM. */
+ if (priv->CurrentChannelBW != HT_CHANNEL_WIDTH_20) {
+ /*
+ * 2008/01/11 MH 40MHZ 90/92 register are not the same.
+ * 2008/02/05 MH SD3-Jerry 92U/92E PD_TH are the same.
+ */
+ write_nic_byte(dev, (rOFDM0_XATxAFE+3), 0x00);
+ /*else if (priv->card_8192 == HARDWARE_TYPE_RTL8190P)
+ write_nic_byte(dev, rOFDM0_RxDetector1, 0x40);
+ */
+ } else
+ write_nic_byte(dev, rOFDM0_RxDetector1, 0x42);
+ } else if (dm_digtable.curpd_thstate == DIG_PD_AT_NORMAL_POWER) {
+ /* Higher PD_TH for OFDM. */
+ if (priv->CurrentChannelBW != HT_CHANNEL_WIDTH_20) {
+ /*
+ * 2008/01/11 MH 40MHZ 90/92 register are not the same.
+ * 2008/02/05 MH SD3-Jerry 92U/92E PD_TH are the same.
+ */
+ write_nic_byte(dev, (rOFDM0_XATxAFE+3), 0x20);
+ /*else if (priv->card_8192 == HARDWARE_TYPE_RTL8190P)
+ write_nic_byte(dev, rOFDM0_RxDetector1, 0x42);
+ */
+ } else
+ write_nic_byte(dev, rOFDM0_RxDetector1, 0x44);
+ } else if (dm_digtable.curpd_thstate == DIG_PD_AT_HIGH_POWER) {
+ /* Higher PD_TH for OFDM for high power state. */
+ if (priv->CurrentChannelBW != HT_CHANNEL_WIDTH_20) {
+ write_nic_byte(dev, (rOFDM0_XATxAFE+3), 0x10);
+ /*else if (priv->card_8192 == HARDWARE_TYPE_RTL8190P)
+ write_nic_byte(dev, rOFDM0_RxDetector1, 0x41);
+ */
+ } else
+ write_nic_byte(dev, rOFDM0_RxDetector1, 0x43);
+ }
+ dm_digtable.prepd_thstate = dm_digtable.curpd_thstate;
+ if (initialized <= 3)
+ initialized++;
+ force_write = 0;
+ }
+ }
+}
+
+static void dm_cs_ratio(
+ struct net_device *dev)
+{
+ struct r8192_priv *priv = ieee80211_priv(dev);
+ static u8 initialized, force_write;
+ static u32 reset_cnt;
+
+ if (dm_digtable.dig_algorithm_switch) {
+ initialized = 0;
+ reset_cnt = 0;
+ }
+
+ if (dm_digtable.pre_connect_state == dm_digtable.cur_connect_state) {
+ if (dm_digtable.cur_connect_state == DIG_CONNECT) {
+ if (dm_digtable.rssi_val <= dm_digtable.rssi_low_thresh)
+ dm_digtable.curcs_ratio_state = DIG_CS_RATIO_LOWER;
+ else if (dm_digtable.rssi_val >= dm_digtable.rssi_high_thresh)
+ dm_digtable.curcs_ratio_state = DIG_CS_RATIO_HIGHER;
+ else
+ dm_digtable.curcs_ratio_state = dm_digtable.precs_ratio_state;
+ } else {
+ dm_digtable.curcs_ratio_state = DIG_CS_RATIO_LOWER;
+ }
+ } else /* disconnected -> connected or connected -> disconnected */
+ dm_digtable.curcs_ratio_state = DIG_CS_RATIO_LOWER;
+
+ /* if silent reset happened, we should rewrite the values back */
+ if (priv->reset_count != reset_cnt) {
+ force_write = 1;
+ reset_cnt = priv->reset_count;
+ }
+
+ {
+ if ((dm_digtable.precs_ratio_state != dm_digtable.curcs_ratio_state) ||
+ !initialized || force_write) {
+ /*DbgPrint("Write CS_ratio state = %d\n", DM_DigTable.CurCS_ratioState);*/
+ if (dm_digtable.curcs_ratio_state == DIG_CS_RATIO_LOWER) {
+ /* Lower CS ratio for CCK. */
+ write_nic_byte(dev, 0xa0a, 0x08);
+ } else if (dm_digtable.curcs_ratio_state == DIG_CS_RATIO_HIGHER) {
+ /* Higher CS ratio for CCK. */
+ write_nic_byte(dev, 0xa0a, 0xcd);
+ }
+ dm_digtable.precs_ratio_state = dm_digtable.curcs_ratio_state;
+ initialized = 1;
+ force_write = 0;
+ }
+ }
+}
+
+void dm_init_edca_turbo(struct net_device *dev)
+{
+ struct r8192_priv *priv = ieee80211_priv(dev);
+
+ priv->bcurrent_turbo_EDCA = false;
+ priv->ieee80211->bis_any_nonbepkts = false;
+ priv->bis_cur_rdlstate = false;
+} /* dm_init_edca_turbo */
+
+static void dm_check_edca_turbo(
+ struct net_device *dev)
+{
+ struct r8192_priv *priv = ieee80211_priv(dev);
+ PRT_HIGH_THROUGHPUT pHTInfo = priv->ieee80211->pHTInfo;
+ /*PSTA_QOS pStaQos = pMgntInfo->pStaQos;*/
+
+ /* Keep past Tx/Rx packet count for RT-to-RT EDCA turbo. */
+ static unsigned long lastTxOkCnt;
+ static unsigned long lastRxOkCnt;
+ unsigned long curTxOkCnt = 0;
+ unsigned long curRxOkCnt = 0;
+
+ /*
+ * Do not be Turbo if it's under WiFi config and Qos Enabled, because the EDCA parameters
+ * should follow the settings from QAP. By Bruce, 2007-12-07.
+ */
+ if (priv->ieee80211->state != IEEE80211_LINKED)
+ goto dm_CheckEdcaTurbo_EXIT;
+ /* We do not turn on EDCA turbo mode for some AP that has IOT issue */
+ if (priv->ieee80211->pHTInfo->IOTAction & HT_IOT_ACT_DISABLE_EDCA_TURBO)
+ goto dm_CheckEdcaTurbo_EXIT;
+
+ /*printk("========>%s():bis_any_nonbepkts is %d\n", __func__, priv->bis_any_nonbepkts);*/
+ /* Check the status for current condition. */
+ if (!priv->ieee80211->bis_any_nonbepkts) {
+ curTxOkCnt = priv->stats.txbytesunicast - lastTxOkCnt;
+ curRxOkCnt = priv->stats.rxbytesunicast - lastRxOkCnt;
+ /* For RT-AP, we needs to turn it on when Rx>Tx */
+ if (curRxOkCnt > 4*curTxOkCnt) {
+ /*printk("%s():curRxOkCnt > 4*curTxOkCnt\n");*/
+ if (!priv->bis_cur_rdlstate || !priv->bcurrent_turbo_EDCA) {
+ write_nic_dword(dev, EDCAPARA_BE, edca_setting_DL[pHTInfo->IOTPeer]);
+ priv->bis_cur_rdlstate = true;
+ }
+ } else {
+ /*printk("%s():curRxOkCnt < 4*curTxOkCnt\n");*/
+ if (priv->bis_cur_rdlstate || !priv->bcurrent_turbo_EDCA) {
+ write_nic_dword(dev, EDCAPARA_BE, edca_setting_UL[pHTInfo->IOTPeer]);
+ priv->bis_cur_rdlstate = false;
+ }
+
+ }
+
+ priv->bcurrent_turbo_EDCA = true;
+ } else {
+ /*
+ * Turn Off EDCA turbo here.
+ * Restore original EDCA according to the declaration of AP.
+ */
+ if (priv->bcurrent_turbo_EDCA) {
+ {
+ u8 u1bAIFS;
+ u32 u4bAcParam;
+ struct ieee80211_qos_parameters *qos_parameters = &priv->ieee80211->current_network.qos_data.parameters;
+ u8 mode = priv->ieee80211->mode;
+
+ /* For Each time updating EDCA parameter, reset EDCA turbo mode status. */
+ dm_init_edca_turbo(dev);
+ u1bAIFS = qos_parameters->aifs[0] * ((mode&(IEEE_G|IEEE_N_24G)) ? 9 : 20) + aSifsTime;
+ u4bAcParam = (((u32)(qos_parameters->tx_op_limit[0])) << AC_PARAM_TXOP_LIMIT_OFFSET)|
+ (((u32)(qos_parameters->cw_max[0])) << AC_PARAM_ECW_MAX_OFFSET)|
+ (((u32)(qos_parameters->cw_min[0])) << AC_PARAM_ECW_MIN_OFFSET)|
+ ((u32)u1bAIFS << AC_PARAM_AIFS_OFFSET);
+ /*write_nic_dword(dev, WDCAPARA_ADD[i], u4bAcParam);*/
+ write_nic_dword(dev, EDCAPARA_BE, u4bAcParam);
+
+ /*
+ * Check ACM bit.
+ * If it is set, immediately set ACM control bit to downgrading AC for passing WMM testplan. Annie, 2005-12-13.
+ */
+ {
+ /* TODO: Modified this part and try to set acm control in only 1 IO processing!! */
+
+ PACI_AIFSN pAciAifsn = (PACI_AIFSN)&(qos_parameters->aifs[0]);
+ u8 AcmCtrl;
+
+ read_nic_byte(dev, AcmHwCtrl, &AcmCtrl);
+
+ if (pAciAifsn->f.ACM) { /* ACM bit is 1. */
+ AcmCtrl |= AcmHw_BeqEn;
+ } else { /* ACM bit is 0. */
+ AcmCtrl &= (~AcmHw_BeqEn);
+ }
+
+ RT_TRACE(COMP_QOS, "SetHwReg8190pci(): [HW_VAR_ACM_CTRL] Write 0x%X\n", AcmCtrl);
+ write_nic_byte(dev, AcmHwCtrl, AcmCtrl);
+ }
+ }
+ priv->bcurrent_turbo_EDCA = false;
+ }
+ }
+
+dm_CheckEdcaTurbo_EXIT:
+ /* Set variables for next time. */
+ priv->ieee80211->bis_any_nonbepkts = false;
+ lastTxOkCnt = priv->stats.txbytesunicast;
+ lastRxOkCnt = priv->stats.rxbytesunicast;
+} /* dm_CheckEdcaTurbo */
+
+static void dm_init_ctstoself(struct net_device *dev)
+{
+ struct r8192_priv *priv = ieee80211_priv((struct net_device *)dev);
+
+ priv->ieee80211->bCTSToSelfEnable = true;
+ priv->ieee80211->CTSToSelfTH = CTSToSelfTHVal;
+}
+
+static void dm_ctstoself(struct net_device *dev)
+{
+ struct r8192_priv *priv = ieee80211_priv((struct net_device *)dev);
+ PRT_HIGH_THROUGHPUT pHTInfo = priv->ieee80211->pHTInfo;
+ static unsigned long lastTxOkCnt;
+ static unsigned long lastRxOkCnt;
+ unsigned long curTxOkCnt = 0;
+ unsigned long curRxOkCnt = 0;
+
+ if (priv->ieee80211->bCTSToSelfEnable != true) {
+ pHTInfo->IOTAction &= ~HT_IOT_ACT_FORCED_CTS2SELF;
+ return;
+ }
+ /*
+ 1. Uplink
+ 2. Linksys350/Linksys300N
+ 3. <50 disable, >55 enable
+ */
+
+ if (pHTInfo->IOTPeer == HT_IOT_PEER_BROADCOM) {
+ curTxOkCnt = priv->stats.txbytesunicast - lastTxOkCnt;
+ curRxOkCnt = priv->stats.rxbytesunicast - lastRxOkCnt;
+ if (curRxOkCnt > 4*curTxOkCnt) { /* downlink, disable CTS to self */
+ pHTInfo->IOTAction &= ~HT_IOT_ACT_FORCED_CTS2SELF;
+ /*DbgPrint("dm_CTSToSelf() ==> CTS to self disabled -- downlink\n");*/
+ } else { /* uplink */
+ pHTInfo->IOTAction |= HT_IOT_ACT_FORCED_CTS2SELF;
+ }
+
+ lastTxOkCnt = priv->stats.txbytesunicast;
+ lastRxOkCnt = priv->stats.rxbytesunicast;
+ }
+}
+
+/*-----------------------------------------------------------------------------
+ * Function: dm_check_pbc_gpio()
+ *
+ * Overview: Check if PBC button is pressed.
+ *
+ * Input: NONE
+ *
+ * Output: NONE
+ *
+ * Return: NONE
+ *
+ * Revised History:
+ * When Who Remark
+ * 05/28/2008 amy Create Version 0 porting from windows code.
+ *
+ *---------------------------------------------------------------------------*/
+static void dm_check_pbc_gpio(struct net_device *dev)
+{
+ struct r8192_priv *priv = ieee80211_priv(dev);
+ u8 tmp1byte;
+
+ read_nic_byte(dev, GPI, &tmp1byte);
+ if (tmp1byte == 0xff)
+ return;
+
+ if (tmp1byte&BIT6 || tmp1byte&BIT0) {
+ /*
+ * Here we only set bPbcPressed to TRUE
+ * After trigger PBC, the variable will be set to FALSE
+ */
+ RT_TRACE(COMP_IO, "CheckPbcGPIO - PBC is pressed\n");
+ priv->bpbc_pressed = true;
+ }
+
+}
+
+/*-----------------------------------------------------------------------------
+ * Function: DM_RFPathCheckWorkItemCallBack()
+ *
+ * Overview: Check if Current RF RX path is enabled
+ *
+ * Input: NONE
+ *
+ * Output: NONE
+ *
+ * Return: NONE
+ *
+ * Revised History:
+ * When Who Remark
+ * 01/30/2008 MHC Create Version 0.
+ *
+ *---------------------------------------------------------------------------*/
+void dm_rf_pathcheck_workitemcallback(struct work_struct *work)
+{
+ struct delayed_work *dwork = container_of(work, struct delayed_work, work);
+ struct r8192_priv *priv = container_of(dwork, struct r8192_priv, rfpath_check_wq);
+ struct net_device *dev = priv->ieee80211->dev;
+ /*bool bactually_set = false;*/
+ u8 rfpath = 0, i;
+
+ /* 2008/01/30 MH After discussing with SD3 Jerry, 0xc04/0xd04 register will
+ always be the same. We only read 0xc04 now. */
+ read_nic_byte(dev, 0xc04, &rfpath);
+
+ /* Check Bit 0-3, it means if RF A-D is enabled. */
+ for (i = 0; i < RF90_PATH_MAX; i++) {
+ if (rfpath & (0x01<<i))
+ priv->brfpath_rxenable[i] = true;
+ else
+ priv->brfpath_rxenable[i] = false;
+ }
+ if (!DM_RxPathSelTable.Enable)
+ return;
+
+ dm_rxpath_sel_byrssi(dev);
+} /* DM_RFPathCheckWorkItemCallBack */
+
+static void dm_init_rxpath_selection(struct net_device *dev)
+{
+ u8 i;
+ struct r8192_priv *priv = ieee80211_priv(dev);
+
+ DM_RxPathSelTable.Enable = 1; /* default enabled */
+ DM_RxPathSelTable.SS_TH_low = RxPathSelection_SS_TH_low;
+ DM_RxPathSelTable.diff_TH = RxPathSelection_diff_TH;
+ if (priv->CustomerID == RT_CID_819x_Netcore)
+ DM_RxPathSelTable.cck_method = CCK_Rx_Version_2;
+ else
+ DM_RxPathSelTable.cck_method = CCK_Rx_Version_1;
+ DM_RxPathSelTable.DbgMode = DM_DBG_OFF;
+ DM_RxPathSelTable.disabledRF = 0;
+ for (i = 0; i < 4; i++) {
+ DM_RxPathSelTable.rf_rssi[i] = 50;
+ DM_RxPathSelTable.cck_pwdb_sta[i] = -64;
+ DM_RxPathSelTable.rf_enable_rssi_th[i] = 100;
+ }
+}
+
+static void dm_rxpath_sel_byrssi(struct net_device *dev)
+{
+ struct r8192_priv *priv = ieee80211_priv(dev);
+ u8 i, max_rssi_index = 0, min_rssi_index = 0, sec_rssi_index = 0, rf_num = 0;
+ u8 tmp_max_rssi = 0, tmp_min_rssi = 0, tmp_sec_rssi = 0;
+ u8 cck_default_Rx = 0x2; /* RF-C */
+ u8 cck_optional_Rx = 0x3; /* RF-D */
+ long tmp_cck_max_pwdb = 0, tmp_cck_min_pwdb = 0, tmp_cck_sec_pwdb = 0;
+ u8 cck_rx_ver2_max_index = 0, cck_rx_ver2_min_index = 0, cck_rx_ver2_sec_index = 0;
+ u8 cur_rf_rssi;
+ long cur_cck_pwdb;
+ static u8 disabled_rf_cnt, cck_Rx_Path_initialized;
+ u8 update_cck_rx_path;
+
+ if (priv->rf_type != RF_2T4R)
+ return;
+
+ if (!cck_Rx_Path_initialized) {
+ read_nic_byte(dev, 0xa07, &DM_RxPathSelTable.cck_Rx_path);
+ DM_RxPathSelTable.cck_Rx_path &= 0xf;
+ cck_Rx_Path_initialized = 1;
+ }
+
+ read_nic_byte(dev, 0xc04, &DM_RxPathSelTable.disabledRF);
+ DM_RxPathSelTable.disabledRF = ~DM_RxPathSelTable.disabledRF & 0xf;
+
+ if (priv->ieee80211->mode == WIRELESS_MODE_B) {
+ DM_RxPathSelTable.cck_method = CCK_Rx_Version_2; /* pure B mode, fixed cck version2 */
+ /*DbgPrint("Pure B mode, use cck rx version2\n");*/
+ }
+
+ /* decide max/sec/min rssi index */
+ for (i = 0; i < RF90_PATH_MAX; i++) {
+ if (!DM_RxPathSelTable.DbgMode)
+ DM_RxPathSelTable.rf_rssi[i] = priv->stats.rx_rssi_percentage[i];
+
+ if (priv->brfpath_rxenable[i]) {
+ rf_num++;
+ cur_rf_rssi = DM_RxPathSelTable.rf_rssi[i];
+
+ if (rf_num == 1) { /* find first enabled rf path and the rssi values */
+ /* initialize, set all rssi index to the same one */
+ max_rssi_index = min_rssi_index = sec_rssi_index = i;
+ tmp_max_rssi = tmp_min_rssi = tmp_sec_rssi = cur_rf_rssi;
+ } else if (rf_num == 2) { /* we pick up the max index first, and let sec and min to be the same one */
+ if (cur_rf_rssi >= tmp_max_rssi) {
+ tmp_max_rssi = cur_rf_rssi;
+ max_rssi_index = i;
+ } else {
+ tmp_sec_rssi = tmp_min_rssi = cur_rf_rssi;
+ sec_rssi_index = min_rssi_index = i;
+ }
+ } else {
+ if (cur_rf_rssi > tmp_max_rssi) {
+ tmp_sec_rssi = tmp_max_rssi;
+ sec_rssi_index = max_rssi_index;
+ tmp_max_rssi = cur_rf_rssi;
+ max_rssi_index = i;
+ } else if (cur_rf_rssi == tmp_max_rssi) { /* let sec and min point to the different index */
+ tmp_sec_rssi = cur_rf_rssi;
+ sec_rssi_index = i;
+ } else if ((cur_rf_rssi < tmp_max_rssi) && (cur_rf_rssi > tmp_sec_rssi)) {
+ tmp_sec_rssi = cur_rf_rssi;
+ sec_rssi_index = i;
+ } else if (cur_rf_rssi == tmp_sec_rssi) {
+ if (tmp_sec_rssi == tmp_min_rssi) {
+ /* let sec and min point to the different index */
+ tmp_sec_rssi = cur_rf_rssi;
+ sec_rssi_index = i;
+ } else {
+ /* This case we don't need to set any index */
+ }
+ } else if ((cur_rf_rssi < tmp_sec_rssi) && (cur_rf_rssi > tmp_min_rssi)) {
+ /* This case we don't need to set any index */
+ } else if (cur_rf_rssi == tmp_min_rssi) {
+ if (tmp_sec_rssi == tmp_min_rssi) {
+ /* let sec and min point to the different index */
+ tmp_min_rssi = cur_rf_rssi;
+ min_rssi_index = i;
+ } else {
+ /* This case we don't need to set any index */
+ }
+ } else if (cur_rf_rssi < tmp_min_rssi) {
+ tmp_min_rssi = cur_rf_rssi;
+ min_rssi_index = i;
+ }
+ }
+ }
+ }
+
+ rf_num = 0;
+ /* decide max/sec/min cck pwdb index */
+ if (DM_RxPathSelTable.cck_method == CCK_Rx_Version_2) {
+ for (i = 0; i < RF90_PATH_MAX; i++) {
+ if (priv->brfpath_rxenable[i]) {
+ rf_num++;
+ cur_cck_pwdb = DM_RxPathSelTable.cck_pwdb_sta[i];
+
+ if (rf_num == 1) { /* find first enabled rf path and the rssi values */
+ /* initialize, set all rssi index to the same one */
+ cck_rx_ver2_max_index = cck_rx_ver2_min_index = cck_rx_ver2_sec_index = i;
+ tmp_cck_max_pwdb = tmp_cck_min_pwdb = tmp_cck_sec_pwdb = cur_cck_pwdb;
+ } else if (rf_num == 2) { /* we pick up the max index first, and let sec and min to be the same one */
+ if (cur_cck_pwdb >= tmp_cck_max_pwdb) {
+ tmp_cck_max_pwdb = cur_cck_pwdb;
+ cck_rx_ver2_max_index = i;
+ } else {
+ tmp_cck_sec_pwdb = tmp_cck_min_pwdb = cur_cck_pwdb;
+ cck_rx_ver2_sec_index = cck_rx_ver2_min_index = i;
+ }
+ } else {
+ if (cur_cck_pwdb > tmp_cck_max_pwdb) {
+ tmp_cck_sec_pwdb = tmp_cck_max_pwdb;
+ cck_rx_ver2_sec_index = cck_rx_ver2_max_index;
+ tmp_cck_max_pwdb = cur_cck_pwdb;
+ cck_rx_ver2_max_index = i;
+ } else if (cur_cck_pwdb == tmp_cck_max_pwdb) {
+ /* let sec and min point to the different index */
+ tmp_cck_sec_pwdb = cur_cck_pwdb;
+ cck_rx_ver2_sec_index = i;
+ } else if ((cur_cck_pwdb < tmp_cck_max_pwdb) && (cur_cck_pwdb > tmp_cck_sec_pwdb)) {
+ tmp_cck_sec_pwdb = cur_cck_pwdb;
+ cck_rx_ver2_sec_index = i;
+ } else if (cur_cck_pwdb == tmp_cck_sec_pwdb && tmp_cck_sec_pwdb == tmp_cck_min_pwdb) {
+ /* let sec and min point to the different index */
+ tmp_cck_sec_pwdb = cur_cck_pwdb;
+ cck_rx_ver2_sec_index = i;
+ /* otherwise we don't need to set any index */
+ } else if ((cur_cck_pwdb < tmp_cck_sec_pwdb) && (cur_cck_pwdb > tmp_cck_min_pwdb)) {
+ /* This case we don't need to set any index */
+ } else if (cur_cck_pwdb == tmp_cck_min_pwdb && tmp_cck_sec_pwdb == tmp_cck_min_pwdb) {
+ /* let sec and min point to the different index */
+ tmp_cck_min_pwdb = cur_cck_pwdb;
+ cck_rx_ver2_min_index = i;
+ /* otherwise we don't need to set any index */
+ } else if (cur_cck_pwdb < tmp_cck_min_pwdb) {
+ tmp_cck_min_pwdb = cur_cck_pwdb;
+ cck_rx_ver2_min_index = i;
+ }
+ }
+
+ }
+ }
+ }
+
+ /*
+ * Set CCK Rx path
+ * reg0xA07[3:2]=cck default rx path, reg0xa07[1:0]=cck optional rx path.
+ */
+ update_cck_rx_path = 0;
+ if (DM_RxPathSelTable.cck_method == CCK_Rx_Version_2) {
+ cck_default_Rx = cck_rx_ver2_max_index;
+ cck_optional_Rx = cck_rx_ver2_sec_index;
+ if (tmp_cck_max_pwdb != -64)
+ update_cck_rx_path = 1;
+ }
+
+ if (tmp_min_rssi < DM_RxPathSelTable.SS_TH_low && disabled_rf_cnt < 2) {
+ if ((tmp_max_rssi - tmp_min_rssi) >= DM_RxPathSelTable.diff_TH) {
+ /* record the enabled rssi threshold */
+ DM_RxPathSelTable.rf_enable_rssi_th[min_rssi_index] = tmp_max_rssi+5;
+ /* disable the BB Rx path, OFDM */
+ rtl8192_setBBreg(dev, rOFDM0_TRxPathEnable, 0x1<<min_rssi_index, 0x0); /* 0xc04[3:0] */
+ rtl8192_setBBreg(dev, rOFDM1_TRxPathEnable, 0x1<<min_rssi_index, 0x0); /* 0xd04[3:0] */
+ disabled_rf_cnt++;
+ }
+ if (DM_RxPathSelTable.cck_method == CCK_Rx_Version_1) {
+ cck_default_Rx = max_rssi_index;
+ cck_optional_Rx = sec_rssi_index;
+ if (tmp_max_rssi)
+ update_cck_rx_path = 1;
+ }
+ }
+
+ if (update_cck_rx_path) {
+ DM_RxPathSelTable.cck_Rx_path = (cck_default_Rx<<2)|(cck_optional_Rx);
+ rtl8192_setBBreg(dev, rCCK0_AFESetting, 0x0f000000, DM_RxPathSelTable.cck_Rx_path);
+ }
+
+ if (DM_RxPathSelTable.disabledRF) {
+ for (i = 0; i < 4; i++) {
+ if ((DM_RxPathSelTable.disabledRF>>i) & 0x1) { /* disabled rf */
+ if (tmp_max_rssi >= DM_RxPathSelTable.rf_enable_rssi_th[i]) {
+ /* enable the BB Rx path */
+ /*DbgPrint("RF-%d is enabled.\n", 0x1<<i);*/
+ rtl8192_setBBreg(dev, rOFDM0_TRxPathEnable, 0x1<<i, 0x1); /* 0xc04[3:0] */
+ rtl8192_setBBreg(dev, rOFDM1_TRxPathEnable, 0x1<<i, 0x1); /* 0xd04[3:0] */
+ DM_RxPathSelTable.rf_enable_rssi_th[i] = 100;
+ disabled_rf_cnt--;
+ }
+ }
+ }
+ }
+}
+
+/*-----------------------------------------------------------------------------
+ * Function: dm_check_rx_path_selection()
+ *
+ * Overview: Call a workitem to check current RXRF path and Rx Path selection by RSSI.
+ *
+ * Input: NONE
+ *
+ * Output: NONE
+ *
+ * Return: NONE
+ *
+ * Revised History:
+ * When Who Remark
+ * 05/28/2008 amy Create Version 0 porting from windows code.
+ *
+ *---------------------------------------------------------------------------*/
+static void dm_check_rx_path_selection(struct net_device *dev)
+{
+ struct r8192_priv *priv = ieee80211_priv(dev);
+
+ queue_delayed_work(priv->priv_wq, &priv->rfpath_check_wq, 0);
+} /* dm_CheckRxRFPath */
+
+static void dm_init_fsync(struct net_device *dev)
+{
+ struct r8192_priv *priv = ieee80211_priv(dev);
+
+ priv->ieee80211->fsync_time_interval = 500;
+ priv->ieee80211->fsync_rate_bitmap = 0x0f000800;
+ priv->ieee80211->fsync_rssi_threshold = 30;
+ priv->ieee80211->bfsync_enable = false;
+ priv->ieee80211->fsync_multiple_timeinterval = 3;
+ priv->ieee80211->fsync_firstdiff_ratethreshold = 100;
+ priv->ieee80211->fsync_seconddiff_ratethreshold = 200;
+ priv->ieee80211->fsync_state = Default_Fsync;
+ priv->framesyncMonitor = 1; /* current default 0xc38 monitor on */
+ setup_timer(&priv->fsync_timer, dm_fsync_timer_callback,
+ (unsigned long)dev);
+}
+
+static void dm_deInit_fsync(struct net_device *dev)
+{
+ struct r8192_priv *priv = ieee80211_priv(dev);
+
+ del_timer_sync(&priv->fsync_timer);
+}
+
+void dm_fsync_timer_callback(unsigned long data)
+{
+ struct net_device *dev = (struct net_device *)data;
+ struct r8192_priv *priv = ieee80211_priv((struct net_device *)data);
+ u32 rate_index, rate_count = 0, rate_count_diff = 0;
+ bool bSwitchFromCountDiff = false;
+ bool bDoubleTimeInterval = false;
+
+ if (priv->ieee80211->state == IEEE80211_LINKED &&
+ priv->ieee80211->bfsync_enable &&
+ (priv->ieee80211->pHTInfo->IOTAction & HT_IOT_ACT_CDD_FSYNC)) {
+ /* Count rate 54, MCS [7], [12, 13, 14, 15] */
+ u32 rate_bitmap;
+
+ for (rate_index = 0; rate_index <= 27; rate_index++) {
+ rate_bitmap = 1 << rate_index;
+ if (priv->ieee80211->fsync_rate_bitmap & rate_bitmap)
+ rate_count += priv->stats.received_rate_histogram[1][rate_index];
+ }
+
+ if (rate_count < priv->rate_record)
+ rate_count_diff = 0xffffffff - rate_count + priv->rate_record;
+ else
+ rate_count_diff = rate_count - priv->rate_record;
+ if (rate_count_diff < priv->rateCountDiffRecord) {
+ u32 DiffNum = priv->rateCountDiffRecord - rate_count_diff;
+ /* Continue count */
+ if (DiffNum >= priv->ieee80211->fsync_seconddiff_ratethreshold)
+ priv->ContinueDiffCount++;
+ else
+ priv->ContinueDiffCount = 0;
+
+ /* Continue count over */
+ if (priv->ContinueDiffCount >= 2) {
+ bSwitchFromCountDiff = true;
+ priv->ContinueDiffCount = 0;
+ }
+ } else {
+ /* Stop the continued count */
+ priv->ContinueDiffCount = 0;
+ }
+
+ /* If Count diff <= FsyncRateCountThreshold */
+ if (rate_count_diff <= priv->ieee80211->fsync_firstdiff_ratethreshold) {
+ bSwitchFromCountDiff = true;
+ priv->ContinueDiffCount = 0;
+ }
+ priv->rate_record = rate_count;
+ priv->rateCountDiffRecord = rate_count_diff;
+ RT_TRACE(COMP_HALDM, "rateRecord %d rateCount %d, rateCountdiff %d bSwitchFsync %d\n", priv->rate_record, rate_count, rate_count_diff, priv->bswitch_fsync);
+ /* if we never receive those mcs rate and rssi > 30 % then switch fsyn */
+ if (priv->undecorated_smoothed_pwdb > priv->ieee80211->fsync_rssi_threshold && bSwitchFromCountDiff) {
+ bDoubleTimeInterval = true;
+ priv->bswitch_fsync = !priv->bswitch_fsync;
+ if (priv->bswitch_fsync) {
+ write_nic_byte(dev, 0xC36, 0x1c);
+ write_nic_byte(dev, 0xC3e, 0x90);
+ } else {
+ write_nic_byte(dev, 0xC36, 0x5c);
+ write_nic_byte(dev, 0xC3e, 0x96);
+ }
+ } else if (priv->undecorated_smoothed_pwdb <= priv->ieee80211->fsync_rssi_threshold) {
+ if (priv->bswitch_fsync) {
+ priv->bswitch_fsync = false;
+ write_nic_byte(dev, 0xC36, 0x5c);
+ write_nic_byte(dev, 0xC3e, 0x96);
+ }
+ }
+ if (bDoubleTimeInterval) {
+ if (timer_pending(&priv->fsync_timer))
+ del_timer_sync(&priv->fsync_timer);
+ priv->fsync_timer.expires = jiffies + MSECS(priv->ieee80211->fsync_time_interval*priv->ieee80211->fsync_multiple_timeinterval);
+ add_timer(&priv->fsync_timer);
+ } else {
+ if (timer_pending(&priv->fsync_timer))
+ del_timer_sync(&priv->fsync_timer);
+ priv->fsync_timer.expires = jiffies + MSECS(priv->ieee80211->fsync_time_interval);
+ add_timer(&priv->fsync_timer);
+ }
+ } else {
+ /* Let Register return to default value; */
+ if (priv->bswitch_fsync) {
+ priv->bswitch_fsync = false;
+ write_nic_byte(dev, 0xC36, 0x5c);
+ write_nic_byte(dev, 0xC3e, 0x96);
+ }
+ priv->ContinueDiffCount = 0;
+ write_nic_dword(dev, rOFDM0_RxDetector2, 0x465c52cd);
+ }
+ RT_TRACE(COMP_HALDM, "ContinueDiffCount %d\n", priv->ContinueDiffCount);
+ RT_TRACE(COMP_HALDM, "rateRecord %d rateCount %d, rateCountdiff %d bSwitchFsync %d\n", priv->rate_record, rate_count, rate_count_diff, priv->bswitch_fsync);
+}
+
+static void dm_StartHWFsync(struct net_device *dev)
+{
+ RT_TRACE(COMP_HALDM, "%s\n", __func__);
+ write_nic_dword(dev, rOFDM0_RxDetector2, 0x465c12cf);
+ write_nic_byte(dev, 0xc3b, 0x41);
+}
+
+static void dm_EndSWFsync(struct net_device *dev)
+{
+ struct r8192_priv *priv = ieee80211_priv(dev);
+
+ RT_TRACE(COMP_HALDM, "%s\n", __func__);
+ del_timer_sync(&(priv->fsync_timer));
+
+ /* Let Register return to default value; */
+ if (priv->bswitch_fsync) {
+ priv->bswitch_fsync = false;
+
+ write_nic_byte(dev, 0xC36, 0x5c);
+
+ write_nic_byte(dev, 0xC3e, 0x96);
+ }
+
+ priv->ContinueDiffCount = 0;
+ write_nic_dword(dev, rOFDM0_RxDetector2, 0x465c52cd);
+
+}
+
+static void dm_StartSWFsync(struct net_device *dev)
+{
+ struct r8192_priv *priv = ieee80211_priv(dev);
+ u32 rateIndex;
+ u32 rateBitmap;
+
+ RT_TRACE(COMP_HALDM, "%s\n", __func__);
+ /* Initial rate record to zero, start to record. */
+ priv->rate_record = 0;
+ /* Initialize continue diff count to zero, start to record. */
+ priv->ContinueDiffCount = 0;
+ priv->rateCountDiffRecord = 0;
+ priv->bswitch_fsync = false;
+
+ if (priv->ieee80211->mode == WIRELESS_MODE_N_24G) {
+ priv->ieee80211->fsync_firstdiff_ratethreshold = 600;
+ priv->ieee80211->fsync_seconddiff_ratethreshold = 0xffff;
+ } else {
+ priv->ieee80211->fsync_firstdiff_ratethreshold = 200;
+ priv->ieee80211->fsync_seconddiff_ratethreshold = 200;
+ }
+ for (rateIndex = 0; rateIndex <= 27; rateIndex++) {
+ rateBitmap = 1 << rateIndex;
+ if (priv->ieee80211->fsync_rate_bitmap & rateBitmap)
+ priv->rate_record += priv->stats.received_rate_histogram[1][rateIndex];
+ }
+ if (timer_pending(&priv->fsync_timer))
+ del_timer_sync(&priv->fsync_timer);
+ priv->fsync_timer.expires = jiffies + MSECS(priv->ieee80211->fsync_time_interval);
+ add_timer(&priv->fsync_timer);
+
+ write_nic_dword(dev, rOFDM0_RxDetector2, 0x465c12cd);
+
+}
+
+static void dm_EndHWFsync(struct net_device *dev)
+{
+ RT_TRACE(COMP_HALDM, "%s\n", __func__);
+ write_nic_dword(dev, rOFDM0_RxDetector2, 0x465c52cd);
+ write_nic_byte(dev, 0xc3b, 0x49);
+
+}
+
+void dm_check_fsync(struct net_device *dev)
+{
+#define RegC38_Default 0
+#define RegC38_NonFsync_Other_AP 1
+#define RegC38_Fsync_AP_BCM 2
+ struct r8192_priv *priv = ieee80211_priv(dev);
+ /*u32 framesyncC34;*/
+ static u8 reg_c38_State = RegC38_Default;
+ static u32 reset_cnt;
+
+ RT_TRACE(COMP_HALDM, "RSSI %d TimeInterval %d MultipleTimeInterval %d\n", priv->ieee80211->fsync_rssi_threshold, priv->ieee80211->fsync_time_interval, priv->ieee80211->fsync_multiple_timeinterval);
+ RT_TRACE(COMP_HALDM, "RateBitmap 0x%x FirstDiffRateThreshold %d SecondDiffRateThreshold %d\n", priv->ieee80211->fsync_rate_bitmap, priv->ieee80211->fsync_firstdiff_ratethreshold, priv->ieee80211->fsync_seconddiff_ratethreshold);
+
+ if (priv->ieee80211->state == IEEE80211_LINKED &&
+ (priv->ieee80211->pHTInfo->IOTAction & HT_IOT_ACT_CDD_FSYNC)) {
+ if (priv->ieee80211->bfsync_enable == 0) {
+ switch (priv->ieee80211->fsync_state) {
+ case Default_Fsync:
+ dm_StartHWFsync(dev);
+ priv->ieee80211->fsync_state = HW_Fsync;
+ break;
+ case SW_Fsync:
+ dm_EndSWFsync(dev);
+ dm_StartHWFsync(dev);
+ priv->ieee80211->fsync_state = HW_Fsync;
+ break;
+ case HW_Fsync:
+ default:
+ break;
+ }
+ } else {
+ switch (priv->ieee80211->fsync_state) {
+ case Default_Fsync:
+ dm_StartSWFsync(dev);
+ priv->ieee80211->fsync_state = SW_Fsync;
+ break;
+ case HW_Fsync:
+ dm_EndHWFsync(dev);
+ dm_StartSWFsync(dev);
+ priv->ieee80211->fsync_state = SW_Fsync;
+ break;
+ case SW_Fsync:
+ default:
+ break;
+ }
+ }
+ if (priv->framesyncMonitor) {
+ if (reg_c38_State != RegC38_Fsync_AP_BCM) {
+ /* For broadcom AP we write different default value */
+ write_nic_byte(dev, rOFDM0_RxDetector3, 0x95);
+
+ reg_c38_State = RegC38_Fsync_AP_BCM;
+ }
+ }
+ } else {
+ switch (priv->ieee80211->fsync_state) {
+ case HW_Fsync:
+ dm_EndHWFsync(dev);
+ priv->ieee80211->fsync_state = Default_Fsync;
+ break;
+ case SW_Fsync:
+ dm_EndSWFsync(dev);
+ priv->ieee80211->fsync_state = Default_Fsync;
+ break;
+ case Default_Fsync:
+ default:
+ break;
+ }
+
+ if (priv->framesyncMonitor) {
+ if (priv->ieee80211->state == IEEE80211_LINKED) {
+ if (priv->undecorated_smoothed_pwdb <= RegC38_TH) {
+ if (reg_c38_State != RegC38_NonFsync_Other_AP) {
+ write_nic_byte(dev, rOFDM0_RxDetector3, 0x90);
+
+ reg_c38_State = RegC38_NonFsync_Other_AP;
+ }
+ } else if (priv->undecorated_smoothed_pwdb >= (RegC38_TH+5)) {
+ if (reg_c38_State) {
+ write_nic_byte(dev, rOFDM0_RxDetector3, priv->framesync);
+ reg_c38_State = RegC38_Default;
+ /*DbgPrint("Fsync is idle, rssi>=40, write 0xc38 = 0x%x\n", pHalData->framesync);*/
+ }
+ }
+ } else {
+ if (reg_c38_State) {
+ write_nic_byte(dev, rOFDM0_RxDetector3, priv->framesync);
+ reg_c38_State = RegC38_Default;
+ /*DbgPrint("Fsync is idle, not connected, write 0xc38 = 0x%x\n", pHalData->framesync);*/
+ }
+ }
+ }
+ }
+ if (priv->framesyncMonitor) {
+ if (priv->reset_count != reset_cnt) { /* After silent reset, the reg_c38_State will be returned to default value */
+ write_nic_byte(dev, rOFDM0_RxDetector3, priv->framesync);
+ reg_c38_State = RegC38_Default;
+ reset_cnt = priv->reset_count;
+ /*DbgPrint("reg_c38_State = 0 for silent reset.\n");*/
+ }
+ } else {
+ if (reg_c38_State) {
+ write_nic_byte(dev, rOFDM0_RxDetector3, priv->framesync);
+ reg_c38_State = RegC38_Default;
+ /*DbgPrint("framesync no monitor, write 0xc38 = 0x%x\n", pHalData->framesync);*/
+ }
+ }
+}
+
+/*-----------------------------------------------------------------------------
+ * Function: dm_shadow_init()
+ *
+ * Overview: Store all NIC MAC/BB register content.
+ *
+ * Input: NONE
+ *
+ * Output: NONE
+ *
+ * Return: NONE
+ *
+ * Revised History:
+ * When Who Remark
+ * 05/29/2008 amy Create Version 0 porting from windows code.
+ *
+ *---------------------------------------------------------------------------*/
+void dm_shadow_init(struct net_device *dev)
+{
+ u8 page;
+ u16 offset;
+
+ for (page = 0; page < 5; page++)
+ for (offset = 0; offset < 256; offset++) {
+ read_nic_byte(dev, offset+page*256, &dm_shadow[page][offset]);
+ /*DbgPrint("P-%d/O-%02x=%02x\r\n", page, offset, DM_Shadow[page][offset]);*/
+ }
+
+ for (page = 8; page < 11; page++)
+ for (offset = 0; offset < 256; offset++)
+ read_nic_byte(dev, offset+page*256, &dm_shadow[page][offset]);
+
+ for (page = 12; page < 15; page++)
+ for (offset = 0; offset < 256; offset++)
+ read_nic_byte(dev, offset+page*256, &dm_shadow[page][offset]);
+
+} /* dm_shadow_init */
+
+/*---------------------------Define function prototype------------------------*/
+/*-----------------------------------------------------------------------------
+ * Function: DM_DynamicTxPower()
+ *
+ * Overview: Detect Signal strength to control TX Registry
+ Tx Power Control For Near/Far Range
+ *
+ * Input: NONE
+ *
+ * Output: NONE
+ *
+ * Return: NONE
+ *
+ * Revised History:
+ * When Who Remark
+ * 03/06/2008 Jacken Create Version 0.
+ *
+ *---------------------------------------------------------------------------*/
+static void dm_init_dynamic_txpower(struct net_device *dev)
+{
+ struct r8192_priv *priv = ieee80211_priv(dev);
+
+ /* Initial TX Power Control for near/far range , add by amy 2008/05/15, porting from windows code. */
+ priv->ieee80211->bdynamic_txpower_enable = true; /* Default to enable Tx Power Control */
+ priv->bLastDTPFlag_High = false;
+ priv->bLastDTPFlag_Low = false;
+ priv->bDynamicTxHighPower = false;
+ priv->bDynamicTxLowPower = false;
+}
+
+static void dm_dynamic_txpower(struct net_device *dev)
+{
+ struct r8192_priv *priv = ieee80211_priv(dev);
+ unsigned int txhipower_threshhold = 0;
+ unsigned int txlowpower_threshold = 0;
+
+ if (priv->ieee80211->bdynamic_txpower_enable != true) {
+ priv->bDynamicTxHighPower = false;
+ priv->bDynamicTxLowPower = false;
+ return;
+ }
+ /*printk("priv->ieee80211->current_network.unknown_cap_exist is %d , priv->ieee80211->current_network.broadcom_cap_exist is %d\n", priv->ieee80211->current_network.unknown_cap_exist, priv->ieee80211->current_network.broadcom_cap_exist);*/
+ if ((priv->ieee80211->current_network.atheros_cap_exist) && (priv->ieee80211->mode == IEEE_G)) {
+ txhipower_threshhold = TX_POWER_ATHEROAP_THRESH_HIGH;
+ txlowpower_threshold = TX_POWER_ATHEROAP_THRESH_LOW;
+ } else {
+ txhipower_threshhold = TX_POWER_NEAR_FIELD_THRESH_HIGH;
+ txlowpower_threshold = TX_POWER_NEAR_FIELD_THRESH_LOW;
+ }
+
+ /*printk("=======>%s(): txhipower_threshhold is %d, txlowpower_threshold is %d\n", __func__, txhipower_threshhold, txlowpower_threshold);*/
+ RT_TRACE(COMP_TXAGC, "priv->undecorated_smoothed_pwdb = %ld\n", priv->undecorated_smoothed_pwdb);
+
+ if (priv->ieee80211->state == IEEE80211_LINKED) {
+ if (priv->undecorated_smoothed_pwdb >= txhipower_threshhold) {
+ priv->bDynamicTxHighPower = true;
+ priv->bDynamicTxLowPower = false;
+ } else {
+ /* high power state check */
+ if (priv->undecorated_smoothed_pwdb < txlowpower_threshold && priv->bDynamicTxHighPower == true)
+ priv->bDynamicTxHighPower = false;
+
+ /* low power state check */
+ if (priv->undecorated_smoothed_pwdb < 35)
+ priv->bDynamicTxLowPower = true;
+ else if (priv->undecorated_smoothed_pwdb >= 40)
+ priv->bDynamicTxLowPower = false;
+ }
+ } else {
+ /*pHalData->bTXPowerCtrlforNearFarRange = !pHalData->bTXPowerCtrlforNearFarRange;*/
+ priv->bDynamicTxHighPower = false;
+ priv->bDynamicTxLowPower = false;
+ }
+
+ if ((priv->bDynamicTxHighPower != priv->bLastDTPFlag_High) ||
+ (priv->bDynamicTxLowPower != priv->bLastDTPFlag_Low)) {
+ RT_TRACE(COMP_TXAGC, "SetTxPowerLevel8190() channel = %d\n", priv->ieee80211->current_network.channel);
+
+#if defined(RTL8190P) || defined(RTL8192E)
+ SetTxPowerLevel8190(Adapter, pHalData->CurrentChannel);
+#endif
+
+ rtl8192_phy_setTxPower(dev, priv->ieee80211->current_network.channel);
+ /*pHalData->bStartTxCtrlByTPCNFR = FALSE; Clear th flag of Set TX Power from Sitesurvey*/
+ }
+ priv->bLastDTPFlag_High = priv->bDynamicTxHighPower;
+ priv->bLastDTPFlag_Low = priv->bDynamicTxLowPower;
+
+} /* dm_dynamic_txpower */
+
+/* added by vivi, for read tx rate and retrycount */
+static void dm_check_txrateandretrycount(struct net_device *dev)
+{
+ struct r8192_priv *priv = ieee80211_priv(dev);
+ struct ieee80211_device *ieee = priv->ieee80211;
+ /* for 11n tx rate */
+ /*priv->stats.CurrentShowTxate = read_nic_byte(dev, Current_Tx_Rate_Reg);*/
+ read_nic_byte(dev, Current_Tx_Rate_Reg, &ieee->softmac_stats.CurrentShowTxate);
+ /*printk("=============>tx_rate_reg:%x\n", ieee->softmac_stats.CurrentShowTxate);*/
+ /* for initial tx rate */
+ /*priv->stats.last_packet_rate = read_nic_byte(dev, Initial_Tx_Rate_Reg);*/
+ read_nic_byte(dev, Initial_Tx_Rate_Reg, &ieee->softmac_stats.last_packet_rate);
+ /* for tx tx retry count */
+ /*priv->stats.txretrycount = read_nic_dword(dev, Tx_Retry_Count_Reg);*/
+ read_nic_dword(dev, Tx_Retry_Count_Reg, &ieee->softmac_stats.txretrycount);
+}
+
+static void dm_send_rssi_tofw(struct net_device *dev)
+{
+ struct r8192_priv *priv = ieee80211_priv(dev);
+
+ /*
+ * If we test chariot, we should stop the TX command ?
+ * Because 92E will always silent reset when we send tx command. We use register
+ * 0x1e0(byte) to notify driver.
+ */
+ write_nic_byte(dev, DRIVER_RSSI, (u8)priv->undecorated_smoothed_pwdb);
+}
+
+/*---------------------------Define function prototype------------------------*/
diff --git a/drivers/staging/rtl8192u/r8192U_dm.h b/drivers/staging/rtl8192u/r8192U_dm.h
new file mode 100644
index 000000000..6cd32eb44
--- /dev/null
+++ b/drivers/staging/rtl8192u/r8192U_dm.h
@@ -0,0 +1,239 @@
+/*****************************************************************************
+ * Copyright(c) 2007, RealTEK Technology Inc. All Right Reserved.
+ *
+ * Module: Hal819xUsbDM.h (RTL8192 Header H File)
+ *
+ *
+ * Note: For dynamic control definition constant structure.
+ *
+ *
+ * Export:
+ *
+ * Abbrev:
+ *
+ * History:
+ * Data Who Remark
+ * 10/04/2007 MHC Create initial version.
+ *
+ *****************************************************************************/
+ /* Check to see if the file has been included already. */
+#ifndef __R8192UDM_H__
+#define __R8192UDM_H__
+
+
+/*--------------------------Define Parameters-------------------------------*/
+#define DM_DIG_THRESH_HIGH 40
+#define DM_DIG_THRESH_LOW 35
+
+#define DM_DIG_HIGH_PWR_THRESH_HIGH 75
+#define DM_DIG_HIGH_PWR_THRESH_LOW 70
+
+#define BW_AUTO_SWITCH_HIGH_LOW 25
+#define BW_AUTO_SWITCH_LOW_HIGH 30
+
+#define DM_check_fsync_time_interval 500
+
+
+#define DM_DIG_BACKOFF 12
+#define DM_DIG_MAX 0x36
+#define DM_DIG_MIN 0x1c
+#define DM_DIG_MIN_Netcore 0x12
+
+#define RxPathSelection_SS_TH_low 30
+#define RxPathSelection_diff_TH 18
+
+#define RateAdaptiveTH_High 50
+#define RateAdaptiveTH_Low_20M 30
+#define RateAdaptiveTH_Low_40M 10
+#define VeryLowRSSI 15
+#define CTSToSelfTHVal 30
+
+/* defined by vivi, for tx power track */
+#define E_FOR_TX_POWER_TRACK 300
+/* Dynamic Tx Power Control Threshold */
+#define TX_POWER_NEAR_FIELD_THRESH_HIGH 68
+#define TX_POWER_NEAR_FIELD_THRESH_LOW 62
+/* added by amy for atheros AP */
+#define TX_POWER_ATHEROAP_THRESH_HIGH 78
+#define TX_POWER_ATHEROAP_THRESH_LOW 72
+
+/* defined by vivi, for showing on UI */
+#define Current_Tx_Rate_Reg 0x1b8
+#define Initial_Tx_Rate_Reg 0x1b9
+#define Tx_Retry_Count_Reg 0x1ac
+#define RegC38_TH 20
+/*--------------------------Define Parameters-------------------------------*/
+
+
+/*------------------------------Define structure----------------------------*/
+/* 2007/10/04 MH Define upper and lower threshold of DIG enable or disable. */
+struct dig {
+ u8 dig_enable_flag;
+ u8 dig_algorithm;
+ u8 dbg_mode;
+ u8 dig_algorithm_switch;
+
+ long rssi_low_thresh;
+ long rssi_high_thresh;
+
+ long rssi_high_power_lowthresh;
+ long rssi_high_power_highthresh;
+
+ u8 dig_state;
+ u8 dig_highpwr_state;
+ u8 cur_connect_state;
+ u8 pre_connect_state;
+
+ u8 curpd_thstate;
+ u8 prepd_thstate;
+ u8 curcs_ratio_state;
+ u8 precs_ratio_state;
+
+ u32 pre_ig_value;
+ u32 cur_ig_value;
+
+ u8 backoff_val;
+ u8 rx_gain_range_max;
+ u8 rx_gain_range_min;
+ bool initialgain_lowerbound_state;
+
+ long rssi_val;
+};
+
+typedef enum tag_dynamic_init_gain_state_definition {
+ DM_STA_DIG_OFF = 0,
+ DM_STA_DIG_ON,
+ DM_STA_DIG_MAX
+} dm_dig_sta_e;
+
+
+/* 2007/10/08 MH Define RATR state. */
+typedef enum tag_dynamic_ratr_state_definition {
+ DM_RATR_STA_HIGH = 0,
+ DM_RATR_STA_MIDDLE = 1,
+ DM_RATR_STA_LOW = 2,
+ DM_RATR_STA_MAX
+} dm_ratr_sta_e;
+
+/* 2007/10/11 MH Define DIG operation type. */
+typedef enum tag_dynamic_init_gain_operation_type_definition {
+ DIG_TYPE_THRESH_HIGH = 0,
+ DIG_TYPE_THRESH_LOW = 1,
+ DIG_TYPE_THRESH_HIGHPWR_HIGH = 2,
+ DIG_TYPE_THRESH_HIGHPWR_LOW = 3,
+ DIG_TYPE_DBG_MODE = 4,
+ DIG_TYPE_RSSI = 5,
+ DIG_TYPE_ALGORITHM = 6,
+ DIG_TYPE_BACKOFF = 7,
+ DIG_TYPE_PWDB_FACTOR = 8,
+ DIG_TYPE_RX_GAIN_MIN = 9,
+ DIG_TYPE_RX_GAIN_MAX = 10,
+ DIG_TYPE_ENABLE = 20,
+ DIG_TYPE_DISABLE = 30,
+ DIG_OP_TYPE_MAX
+} dm_dig_op_e;
+
+typedef enum tag_dig_algorithm_definition {
+ DIG_ALGO_BY_FALSE_ALARM = 0,
+ DIG_ALGO_BY_RSSI = 1,
+ DIG_ALGO_MAX
+} dm_dig_alg_e;
+
+typedef enum tag_dig_dbgmode_definition {
+ DIG_DBG_OFF = 0,
+ DIG_DBG_ON = 1,
+ DIG_DBG_MAX
+} dm_dig_dbg_e;
+
+typedef enum tag_dig_connect_definition {
+ DIG_DISCONNECT = 0,
+ DIG_CONNECT = 1,
+ DIG_CONNECT_MAX
+} dm_dig_connect_e;
+
+typedef enum tag_dig_packetdetection_threshold_definition {
+ DIG_PD_AT_LOW_POWER = 0,
+ DIG_PD_AT_NORMAL_POWER = 1,
+ DIG_PD_AT_HIGH_POWER = 2,
+ DIG_PD_MAX
+} dm_dig_pd_th_e;
+
+typedef enum tag_dig_cck_cs_ratio_state_definition {
+ DIG_CS_RATIO_LOWER = 0,
+ DIG_CS_RATIO_HIGHER = 1,
+ DIG_CS_MAX
+} dm_dig_cs_ratio_e;
+struct dynamic_rx_path_sel {
+ u8 Enable;
+ u8 DbgMode;
+ u8 cck_method;
+ u8 cck_Rx_path;
+
+ u8 SS_TH_low;
+ u8 diff_TH;
+ u8 disabledRF;
+ u8 reserved;
+
+ u8 rf_rssi[4];
+ u8 rf_enable_rssi_th[4];
+ long cck_pwdb_sta[4];
+};
+
+typedef enum tag_CCK_Rx_Path_Method_Definition {
+ CCK_Rx_Version_1 = 0,
+ CCK_Rx_Version_2 = 1,
+ CCK_Rx_Version_MAX
+} DM_CCK_Rx_Path_Method;
+
+typedef enum tag_DM_DbgMode_Definition {
+ DM_DBG_OFF = 0,
+ DM_DBG_ON = 1,
+ DM_DBG_MAX
+} DM_DBG_E;
+
+typedef struct tag_Tx_Config_Cmd_Format {
+ u32 Op; /* Command packet type. */
+ u32 Length; /* Command packet length. */
+ u32 Value;
+} DCMD_TXCMD_T, *PDCMD_TXCMD_T;
+/*------------------------------Define structure----------------------------*/
+
+
+/*------------------------Export global variable----------------------------*/
+extern struct dig dm_digtable;
+extern u8 dm_shadow[16][256];
+extern struct dynamic_rx_path_sel DM_RxPathSelTable;
+/*------------------------Export global variable----------------------------*/
+
+
+/*------------------------Export Marco Definition---------------------------*/
+
+/*------------------------Export Marco Definition---------------------------*/
+
+
+/*--------------------------Exported Function prototype---------------------*/
+extern void init_hal_dm(struct net_device *dev);
+extern void deinit_hal_dm(struct net_device *dev);
+extern void hal_dm_watchdog(struct net_device *dev);
+extern void init_rate_adaptive(struct net_device *dev);
+extern void dm_txpower_trackingcallback(struct work_struct *work);
+extern void dm_restore_dynamic_mechanism_state(struct net_device *dev);
+extern void dm_backup_dynamic_mechanism_state(struct net_device *dev);
+extern void dm_change_dynamic_initgain_thresh(struct net_device *dev,
+ u32 dm_type, u32 dm_value);
+extern void dm_force_tx_fw_info(struct net_device *dev,
+ u32 force_type, u32 force_value);
+extern void dm_init_edca_turbo(struct net_device *dev);
+extern void dm_rf_operation_test_callback(unsigned long data);
+extern void dm_rf_pathcheck_workitemcallback(struct work_struct *work);
+extern void dm_fsync_timer_callback(unsigned long data);
+extern void dm_cck_txpower_adjust(struct net_device *dev, bool binch14);
+extern void dm_shadow_init(struct net_device *dev);
+extern void dm_initialize_txpower_tracking(struct net_device *dev);
+/*--------------------------Exported Function prototype---------------------*/
+
+
+#endif /*__R8192UDM_H__ */
+
+
+/* End of r8192U_dm.h */
diff --git a/drivers/staging/rtl8192u/r8192U_hw.h b/drivers/staging/rtl8192u/r8192U_hw.h
new file mode 100644
index 000000000..a93694ff0
--- /dev/null
+++ b/drivers/staging/rtl8192u/r8192U_hw.h
@@ -0,0 +1,412 @@
+/*
+ This is part of rtl8187 OpenSource driver.
+ Copyright (C) Andrea Merello 2004-2005 <andrea.merello@gmail.com>
+ Released under the terms of GPL (General Public Licence)
+
+ Parts of this driver are based on the GPL part of the
+ official Realtek driver.
+ Parts of this driver are based on the rtl8180 driver skeleton
+ from Patric Schenke & Andres Salomon.
+ Parts of this driver are based on the Intel Pro Wireless
+ 2100 GPL driver.
+
+ We want to thank the Authors of those projects
+ and the Ndiswrapper project Authors.
+*/
+
+/* Mariusz Matuszek added full registers definition with Realtek's name */
+
+/* this file contains register definitions for the rtl8187 MAC controller */
+#ifndef R8192_HW
+#define R8192_HW
+
+typedef enum _VERSION_819xU{
+ VERSION_819xU_A, // A-cut
+ VERSION_819xU_B, // B-cut
+ VERSION_819xU_C,// C-cut
+} VERSION_819xU, *PVERSION_819xU;
+//added for different RF type
+typedef enum _RT_RF_TYPE_DEF
+{
+ RF_1T2R = 0,
+ RF_2T4R,
+
+ RF_819X_MAX_TYPE
+}RT_RF_TYPE_DEF;
+
+
+typedef enum _BaseBand_Config_Type{
+ BaseBand_Config_PHY_REG = 0, //Radio Path A
+ BaseBand_Config_AGC_TAB = 1, //Radio Path B
+}BaseBand_Config_Type, *PBaseBand_Config_Type;
+#define RTL8187_REQT_READ 0xc0
+#define RTL8187_REQT_WRITE 0x40
+#define RTL8187_REQ_GET_REGS 0x05
+#define RTL8187_REQ_SET_REGS 0x05
+
+#define MAX_TX_URB 5
+#define MAX_RX_URB 16
+
+#define R8180_MAX_RETRY 255
+//#define MAX_RX_NORMAL_URB 3
+//#define MAX_RX_COMMAND_URB 2
+#define RX_URB_SIZE 9100
+
+#define BB_ANTATTEN_CHAN14 0x0c
+#define BB_ANTENNA_B 0x40
+
+#define BB_HOST_BANG (1<<30)
+#define BB_HOST_BANG_EN (1<<2)
+#define BB_HOST_BANG_CLK (1<<1)
+#define BB_HOST_BANG_RW (1<<3)
+#define BB_HOST_BANG_DATA 1
+
+//#if (RTL819X_FPGA_VER & RTL819X_FPGA_VIVI_070920)
+#define AFR 0x010
+#define AFR_CardBEn (1<<0)
+#define AFR_CLKRUN_SEL (1<<1)
+#define AFR_FuncRegEn (1<<2)
+#define RTL8190_EEPROM_ID 0x8129
+#define EEPROM_VID 0x02
+#define EEPROM_PID 0x04
+#define EEPROM_NODE_ADDRESS_BYTE_0 0x0C
+
+#define EEPROM_TxPowerDiff 0x1F
+#define EEPROM_ThermalMeter 0x20
+#define EEPROM_PwDiff 0x21 //0x21
+#define EEPROM_CrystalCap 0x22 //0x22
+
+#define EEPROM_TxPwIndex_CCK 0x23 //0x23
+#define EEPROM_TxPwIndex_OFDM_24G 0x24 //0x24~0x26
+#define EEPROM_TxPwIndex_CCK_V1 0x29 //0x29~0x2B
+#define EEPROM_TxPwIndex_OFDM_24G_V1 0x2C //0x2C~0x2E
+#define EEPROM_TxPwIndex_Ver 0x27 //0x27
+
+#define EEPROM_Default_TxPowerDiff 0x0
+#define EEPROM_Default_ThermalMeter 0x7
+#define EEPROM_Default_PwDiff 0x4
+#define EEPROM_Default_CrystalCap 0x5
+#define EEPROM_Default_TxPower 0x1010
+#define EEPROM_Customer_ID 0x7B //0x7B:CustomerID
+#define EEPROM_ChannelPlan 0x16 //0x7C
+#define EEPROM_IC_VER 0x7d //0x7D
+#define EEPROM_CRC 0x7e //0x7E~0x7F
+
+#define EEPROM_CID_DEFAULT 0x0
+#define EEPROM_CID_CAMEO 0x1
+#define EEPROM_CID_RUNTOP 0x2
+#define EEPROM_CID_Senao 0x3
+#define EEPROM_CID_TOSHIBA 0x4 // Toshiba setting, Merge by Jacken, 2008/01/31
+#define EEPROM_CID_NetCore 0x5
+#define EEPROM_CID_Nettronix 0x6
+#define EEPROM_CID_Pronet 0x7
+#define EEPROM_CID_DLINK 0x8
+
+#define AC_PARAM_TXOP_LIMIT_OFFSET 16
+#define AC_PARAM_ECW_MAX_OFFSET 12
+#define AC_PARAM_ECW_MIN_OFFSET 8
+#define AC_PARAM_AIFS_OFFSET 0
+
+//#endif
+enum _RTL8192Usb_HW {
+
+ PCIF = 0x009, // PCI Function Register 0x0009h~0x000bh
+#define BB_GLOBAL_RESET_BIT 0x1
+ BB_GLOBAL_RESET = 0x020, // BasebandGlobal Reset Register
+ BSSIDR = 0x02E, // BSSID Register
+ CMDR = 0x037, // Command register
+#define CR_RST 0x10
+#define CR_RE 0x08
+#define CR_TE 0x04
+#define CR_MulRW 0x01
+ SIFS = 0x03E, // SIFS register
+ TCR = 0x040, // Transmit Configuration Register
+
+#define TCR_MXDMA_2048 7
+#define TCR_LRL_OFFSET 0
+#define TCR_SRL_OFFSET 8
+#define TCR_MXDMA_OFFSET 21
+#define TCR_SAT BIT24 // Enable Rate depedent ack timeout timer
+ RCR = 0x044, // Receive Configuration Register
+#define MAC_FILTER_MASK ((1<<0) | (1<<1) | (1<<2) | (1<<3) | (1<<5) | \
+ (1<<12) | (1<<18) | (1<<19) | (1<<20) | (1<<21) | (1<<22) | (1<<23))
+#define RX_FIFO_THRESHOLD_MASK ((1<<13) | (1<<14) | (1<<15))
+#define RX_FIFO_THRESHOLD_SHIFT 13
+#define RX_FIFO_THRESHOLD_128 3
+#define RX_FIFO_THRESHOLD_256 4
+#define RX_FIFO_THRESHOLD_512 5
+#define RX_FIFO_THRESHOLD_1024 6
+#define RX_FIFO_THRESHOLD_NONE 7
+#define MAX_RX_DMA_MASK ((1<<8) | (1<<9) | (1<<10))
+#define RCR_MXDMA_OFFSET 8
+#define RCR_FIFO_OFFSET 13
+#define RCR_ONLYERLPKT BIT31 // Early Receiving based on Packet Size.
+#define RCR_ENCS2 BIT30 // Enable Carrier Sense Detection Method 2
+#define RCR_ENCS1 BIT29 // Enable Carrier Sense Detection Method 1
+#define RCR_ENMBID BIT27 // Enable Multiple BssId.
+#define RCR_ACKTXBW (BIT24|BIT25) // TXBW Setting of ACK frames
+#define RCR_CBSSID BIT23 // Accept BSSID match packet
+#define RCR_APWRMGT BIT22 // Accept power management packet
+#define RCR_ADD3 BIT21 // Accept address 3 match packet
+#define RCR_AMF BIT20 // Accept management type frame
+#define RCR_ACF BIT19 // Accept control type frame
+#define RCR_ADF BIT18 // Accept data type frame
+#define RCR_RXFTH BIT13 // Rx FIFO Threshold
+#define RCR_AICV BIT12 // Accept ICV error packet
+#define RCR_ACRC32 BIT5 // Accept CRC32 error packet
+#define RCR_AB BIT3 // Accept broadcast packet
+#define RCR_AM BIT2 // Accept multicast packet
+#define RCR_APM BIT1 // Accept physical match packet
+#define RCR_AAP BIT0 // Accept all unicast packet
+ SLOT_TIME = 0x049, // Slot Time Register
+ ACK_TIMEOUT = 0x04c, // Ack Timeout Register
+ PIFS_TIME = 0x04d, // PIFS time
+ USTIME = 0x04e, // Microsecond Tuning Register, Sets the microsecond time unit used by MAC clock.
+ EDCAPARA_BE = 0x050, // EDCA Parameter of AC BE
+ EDCAPARA_BK = 0x054, // EDCA Parameter of AC BK
+ EDCAPARA_VO = 0x058, // EDCA Parameter of AC VO
+ EDCAPARA_VI = 0x05C, // EDCA Parameter of AC VI
+ RFPC = 0x05F, // Rx FIFO Packet Count
+ CWRR = 0x060, // Contention Window Report Register
+ BCN_TCFG = 0x062, // Beacon Time Configuration
+#define BCN_TCFG_CW_SHIFT 8
+#define BCN_TCFG_IFS 0
+ BCN_INTERVAL = 0x070, // Beacon Interval (TU)
+ ATIMWND = 0x072, // ATIM Window Size (TU)
+ BCN_DRV_EARLY_INT = 0x074, // Driver Early Interrupt Time (TU). Time to send interrupt to notify to change beacon content before TBTT
+ BCN_DMATIME = 0x076, // Beacon DMA and ATIM interrupt time (US). Indicates the time before TBTT to perform beacon queue DMA
+ BCN_ERR_THRESH = 0x078, // Beacon Error Threshold
+ RWCAM = 0x0A0, //IN 8190 Data Sheet is called CAMcmd
+ WCAMI = 0x0A4, // Software write CAM input content
+ RCAMO = 0x0A8, // Software read/write CAM config
+ SECR = 0x0B0, //Security Configuration Register
+#define SCR_TxUseDK BIT0 //Force Tx Use Default Key
+#define SCR_RxUseDK BIT1 //Force Rx Use Default Key
+#define SCR_TxEncEnable BIT2 //Enable Tx Encryption
+#define SCR_RxDecEnable BIT3 //Enable Rx Decryption
+#define SCR_SKByA2 BIT4 //Search kEY BY A2
+#define SCR_NoSKMC BIT5 //No Key Search for Multicast
+#define SCR_UseDK 0x01
+#define SCR_TxSecEnable 0x02
+#define SCR_RxSecEnable 0x04
+ TPPoll = 0x0fd, // Transmit priority polling register
+ PSR = 0x0ff, // Page Select Register
+#define CPU_CCK_LOOPBACK 0x00030000
+#define CPU_GEN_SYSTEM_RESET 0x00000001
+#define CPU_GEN_FIRMWARE_RESET 0x00000008
+#define CPU_GEN_BOOT_RDY 0x00000010
+#define CPU_GEN_FIRM_RDY 0x00000020
+#define CPU_GEN_PUT_CODE_OK 0x00000080
+#define CPU_GEN_BB_RST 0x00000100
+#define CPU_GEN_PWR_STB_CPU 0x00000004
+#define CPU_GEN_NO_LOOPBACK_MSK 0xFFF8FFFF // Set bit18,17,16 to 0. Set bit19
+#define CPU_GEN_NO_LOOPBACK_SET 0x00080000 // Set BIT19 to 1
+
+//----------------------------------------------------------------------------
+// 8190 CPU General Register (offset 0x100, 4 byte)
+//----------------------------------------------------------------------------
+#define CPU_CCK_LOOPBACK 0x00030000
+#define CPU_GEN_SYSTEM_RESET 0x00000001
+#define CPU_GEN_FIRMWARE_RESET 0x00000008
+#define CPU_GEN_BOOT_RDY 0x00000010
+#define CPU_GEN_FIRM_RDY 0x00000020
+#define CPU_GEN_PUT_CODE_OK 0x00000080
+#define CPU_GEN_BB_RST 0x00000100
+#define CPU_GEN_PWR_STB_CPU 0x00000004
+#define CPU_GEN_NO_LOOPBACK_MSK 0xFFF8FFFF // Set bit18,17,16 to 0. Set bit19
+#define CPU_GEN_NO_LOOPBACK_SET 0x00080000 // Set BIT19 to 1
+ CPU_GEN = 0x100, // CPU Reset Register
+ LED1Cfg = 0x154,// LED1 Configuration Register
+ LED0Cfg = 0x155,// LED0 Configuration Register
+
+ AcmAvg = 0x170, // ACM Average Period Register
+ AcmHwCtrl = 0x171, // ACM Hardware Control Register
+//----------------------------------------------------------------------------
+////
+//// 8190 AcmHwCtrl bits (offset 0x171, 1 byte)
+////----------------------------------------------------------------------------
+//
+#define AcmHw_HwEn BIT0
+#define AcmHw_BeqEn BIT1
+#define AcmHw_ViqEn BIT2
+#define AcmHw_VoqEn BIT3
+#define AcmHw_BeqStatus BIT4
+#define AcmHw_ViqStatus BIT5
+#define AcmHw_VoqStatus BIT6
+
+ AcmFwCtrl = 0x172, // ACM Firmware Control Register
+ AES_11N_FIX = 0x173,
+ VOAdmTime = 0x174, // VO Queue Admitted Time Register
+ VIAdmTime = 0x178, // VI Queue Admitted Time Register
+ BEAdmTime = 0x17C, // BE Queue Admitted Time Register
+ RQPN1 = 0x180, // Reserved Queue Page Number , Vo Vi, Be, Bk
+ RQPN2 = 0x184, // Reserved Queue Page Number, HCCA, Cmd, Mgnt, High
+ RQPN3 = 0x188, // Reserved Queue Page Number, Bcn, Public,
+// QPRR = 0x1E0, // Queue Page Report per TID
+ QPNR = 0x1D0, //0x1F0, // Queue Packet Number report per TID
+ BQDA = 0x200, // Beacon Queue Descriptor Address
+ HQDA = 0x204, // High Priority Queue Descriptor Address
+ CQDA = 0x208, // Command Queue Descriptor Address
+ MQDA = 0x20C, // Management Queue Descriptor Address
+ HCCAQDA = 0x210, // HCCA Queue Descriptor Address
+ VOQDA = 0x214, // VO Queue Descriptor Address
+ VIQDA = 0x218, // VI Queue Descriptor Address
+ BEQDA = 0x21C, // BE Queue Descriptor Address
+ BKQDA = 0x220, // BK Queue Descriptor Address
+ RCQDA = 0x224, // Receive command Queue Descriptor Address
+ RDQDA = 0x228, // Receive Queue Descriptor Start Address
+
+ MAR0 = 0x240, // Multicast filter.
+ MAR4 = 0x244,
+
+ CCX_PERIOD = 0x250, // CCX Measurement Period Register, in unit of TU.
+ CLM_RESULT = 0x251, // CCA Busy fraction register.
+ NHM_PERIOD = 0x252, // NHM Measurement Period register, in unit of TU.
+
+ NHM_THRESHOLD0 = 0x253, // Noise Histogram Meashorement0.
+ NHM_THRESHOLD1 = 0x254, // Noise Histogram Meashorement1.
+ NHM_THRESHOLD2 = 0x255, // Noise Histogram Meashorement2.
+ NHM_THRESHOLD3 = 0x256, // Noise Histogram Meashorement3.
+ NHM_THRESHOLD4 = 0x257, // Noise Histogram Meashorement4.
+ NHM_THRESHOLD5 = 0x258, // Noise Histogram Meashorement5.
+ NHM_THRESHOLD6 = 0x259, // Noise Histogram Meashorement6
+
+ MCTRL = 0x25A, // Measurement Control
+
+ NHM_RPI_COUNTER0 = 0x264, // Noise Histogram RPI counter0, the fraction of signal strength < NHM_THRESHOLD0.
+ NHM_RPI_COUNTER1 = 0x265, // Noise Histogram RPI counter1, the fraction of signal strength in (NHM_THRESHOLD0, NHM_THRESHOLD1].
+ NHM_RPI_COUNTER2 = 0x266, // Noise Histogram RPI counter2, the fraction of signal strength in (NHM_THRESHOLD1, NHM_THRESHOLD2].
+ NHM_RPI_COUNTER3 = 0x267, // Noise Histogram RPI counter3, the fraction of signal strength in (NHM_THRESHOLD2, NHM_THRESHOLD3].
+ NHM_RPI_COUNTER4 = 0x268, // Noise Histogram RPI counter4, the fraction of signal strength in (NHM_THRESHOLD3, NHM_THRESHOLD4].
+ NHM_RPI_COUNTER5 = 0x269, // Noise Histogram RPI counter5, the fraction of signal strength in (NHM_THRESHOLD4, NHM_THRESHOLD5].
+ NHM_RPI_COUNTER6 = 0x26A, // Noise Histogram RPI counter6, the fraction of signal strength in (NHM_THRESHOLD5, NHM_THRESHOLD6].
+ NHM_RPI_COUNTER7 = 0x26B, // Noise Histogram RPI counter7, the fraction of signal strength in (NHM_THRESHOLD6, NHM_THRESHOLD7].
+#define BW_OPMODE_11J BIT0
+#define BW_OPMODE_5G BIT1
+#define BW_OPMODE_20MHZ BIT2
+ BW_OPMODE = 0x300, // Bandwidth operation mode
+ MSR = 0x303, // Media Status register
+#define MSR_LINK_MASK ((1<<0)|(1<<1))
+#define MSR_LINK_MANAGED 2
+#define MSR_LINK_NONE 0
+#define MSR_LINK_SHIFT 0
+#define MSR_LINK_ADHOC 1
+#define MSR_LINK_MASTER 3
+#define MSR_LINK_ENEDCA (1<<4)
+ RETRY_LIMIT = 0x304, // Retry Limit [15:8]-short, [7:0]-long
+#define RETRY_LIMIT_SHORT_SHIFT 8
+#define RETRY_LIMIT_LONG_SHIFT 0
+ TSFR = 0x308,
+ RRSR = 0x310, // Response Rate Set
+#define RRSR_RSC_OFFSET 21
+#define RRSR_SHORT_OFFSET 23
+#define RRSR_RSC_DUPLICATE 0x600000
+#define RRSR_RSC_LOWSUBCHNL 0x400000
+#define RRSR_RSC_UPSUBCHANL 0x200000
+#define RRSR_SHORT 0x800000
+#define RRSR_1M BIT0
+#define RRSR_2M BIT1
+#define RRSR_5_5M BIT2
+#define RRSR_11M BIT3
+#define RRSR_6M BIT4
+#define RRSR_9M BIT5
+#define RRSR_12M BIT6
+#define RRSR_18M BIT7
+#define RRSR_24M BIT8
+#define RRSR_36M BIT9
+#define RRSR_48M BIT10
+#define RRSR_54M BIT11
+#define RRSR_MCS0 BIT12
+#define RRSR_MCS1 BIT13
+#define RRSR_MCS2 BIT14
+#define RRSR_MCS3 BIT15
+#define RRSR_MCS4 BIT16
+#define RRSR_MCS5 BIT17
+#define RRSR_MCS6 BIT18
+#define RRSR_MCS7 BIT19
+#define BRSR_AckShortPmb BIT23 // CCK ACK: use Short Preamble or not.
+ RATR0 = 0x320, // Rate Adaptive Table register1
+ UFWP = 0x318,
+ DRIVER_RSSI = 0x32c, // Driver tell Firmware current RSSI
+//----------------------------------------------------------------------------
+// 8190 Rate Adaptive Table Register (offset 0x320, 4 byte)
+//----------------------------------------------------------------------------
+//CCK
+#define RATR_1M 0x00000001
+#define RATR_2M 0x00000002
+#define RATR_55M 0x00000004
+#define RATR_11M 0x00000008
+//OFDM
+#define RATR_6M 0x00000010
+#define RATR_9M 0x00000020
+#define RATR_12M 0x00000040
+#define RATR_18M 0x00000080
+#define RATR_24M 0x00000100
+#define RATR_36M 0x00000200
+#define RATR_48M 0x00000400
+#define RATR_54M 0x00000800
+//MCS 1 Spatial Stream
+#define RATR_MCS0 0x00001000
+#define RATR_MCS1 0x00002000
+#define RATR_MCS2 0x00004000
+#define RATR_MCS3 0x00008000
+#define RATR_MCS4 0x00010000
+#define RATR_MCS5 0x00020000
+#define RATR_MCS6 0x00040000
+#define RATR_MCS7 0x00080000
+//MCS 2 Spatial Stream
+#define RATR_MCS8 0x00100000
+#define RATR_MCS9 0x00200000
+#define RATR_MCS10 0x00400000
+#define RATR_MCS11 0x00800000
+#define RATR_MCS12 0x01000000
+#define RATR_MCS13 0x02000000
+#define RATR_MCS14 0x04000000
+#define RATR_MCS15 0x08000000
+// ALL CCK Rate
+#define RATE_ALL_CCK RATR_1M|RATR_2M|RATR_55M|RATR_11M
+#define RATE_ALL_OFDM_AG RATR_6M|RATR_9M|RATR_12M|RATR_18M|RATR_24M\
+ |RATR_36M|RATR_48M|RATR_54M
+#define RATE_ALL_OFDM_1SS RATR_MCS0|RATR_MCS1|RATR_MCS2|RATR_MCS3 | \
+ RATR_MCS4|RATR_MCS5|RATR_MCS6|RATR_MCS7
+#define RATE_ALL_OFDM_2SS RATR_MCS8|RATR_MCS9 |RATR_MCS10|RATR_MCS11| \
+ RATR_MCS12|RATR_MCS13|RATR_MCS14|RATR_MCS15
+
+ MCS_TXAGC = 0x340, // MCS AGC
+ CCK_TXAGC = 0x348, // CCK AGC
+// ISR = 0x350, // Interrupt Status Register
+// IMR = 0x354, // Interrupt Mask Register
+// IMR_POLL = 0x360,
+ MacBlkCtrl = 0x403, // Mac block on/off control register
+
+ EPROM_CMD = 0xfe58,
+#define Cmd9346CR_9356SEL (1<<4)
+#define EPROM_CMD_RESERVED_MASK (1<<5)
+#define EPROM_CMD_OPERATING_MODE_SHIFT 6
+#define EPROM_CMD_OPERATING_MODE_MASK ((1<<7)|(1<<6))
+#define EPROM_CMD_CONFIG 0x3
+#define EPROM_CMD_NORMAL 0
+#define EPROM_CMD_LOAD 1
+#define EPROM_CMD_PROGRAM 2
+#define EPROM_CS_BIT BIT(3)
+#define EPROM_CK_BIT BIT(2)
+#define EPROM_W_BIT BIT(1)
+#define EPROM_R_BIT BIT(0)
+
+ MAC0 = 0x000,
+ MAC1 = 0x001,
+ MAC2 = 0x002,
+ MAC3 = 0x003,
+ MAC4 = 0x004,
+ MAC5 = 0x005,
+
+};
+//----------------------------------------------------------------------------
+// 818xB AnaParm & AnaParm2 Register
+//----------------------------------------------------------------------------
+//#define ANAPARM_ASIC_ON 0x45090658
+//#define ANAPARM2_ASIC_ON 0x727f3f52
+#define GPI 0x108
+#define GPO 0x109
+#define GPE 0x10a
+#endif
diff --git a/drivers/staging/rtl8192u/r8192U_wx.c b/drivers/staging/rtl8192u/r8192U_wx.c
new file mode 100644
index 000000000..83597051a
--- /dev/null
+++ b/drivers/staging/rtl8192u/r8192U_wx.c
@@ -0,0 +1,995 @@
+/*
+ This file contains wireless extension handlers.
+
+ This is part of rtl8180 OpenSource driver.
+ Copyright (C) Andrea Merello 2004-2005 <andrea.merello@gmail.com>
+ Released under the terms of GPL (General Public Licence)
+
+ Parts of this driver are based on the GPL part
+ of the official realtek driver.
+
+ Parts of this driver are based on the rtl8180 driver skeleton
+ from Patric Schenke & Andres Salomon.
+
+ Parts of this driver are based on the Intel Pro Wireless 2100 GPL driver.
+
+ We want to thank the Authors of those projects and the Ndiswrapper
+ project Authors.
+*/
+
+#include <linux/string.h>
+#include "r8192U.h"
+#include "r8192U_hw.h"
+
+#include "dot11d.h"
+#include "r8192U_wx.h"
+
+#define RATE_COUNT 12
+static const u32 rtl8180_rates[] = {1000000, 2000000, 5500000, 11000000,
+ 6000000, 9000000, 12000000, 18000000, 24000000, 36000000, 48000000, 54000000};
+
+
+#ifndef ENETDOWN
+#define ENETDOWN 1
+#endif
+
+static int r8192_wx_get_freq(struct net_device *dev,
+ struct iw_request_info *a,
+ union iwreq_data *wrqu, char *b)
+{
+ struct r8192_priv *priv = ieee80211_priv(dev);
+
+ return ieee80211_wx_get_freq(priv->ieee80211, a, wrqu, b);
+}
+
+
+static int r8192_wx_get_mode(struct net_device *dev, struct iw_request_info *a,
+ union iwreq_data *wrqu, char *b)
+{
+ struct r8192_priv *priv = ieee80211_priv(dev);
+
+ return ieee80211_wx_get_mode(priv->ieee80211, a, wrqu, b);
+}
+
+
+
+static int r8192_wx_get_rate(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct r8192_priv *priv = ieee80211_priv(dev);
+
+ return ieee80211_wx_get_rate(priv->ieee80211, info, wrqu, extra);
+}
+
+
+
+static int r8192_wx_set_rate(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ int ret;
+ struct r8192_priv *priv = ieee80211_priv(dev);
+
+ down(&priv->wx_sem);
+
+ ret = ieee80211_wx_set_rate(priv->ieee80211, info, wrqu, extra);
+
+ up(&priv->wx_sem);
+
+ return ret;
+}
+
+
+static int r8192_wx_set_rts(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ int ret;
+ struct r8192_priv *priv = ieee80211_priv(dev);
+
+ down(&priv->wx_sem);
+
+ ret = ieee80211_wx_set_rts(priv->ieee80211, info, wrqu, extra);
+
+ up(&priv->wx_sem);
+
+ return ret;
+}
+
+static int r8192_wx_get_rts(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct r8192_priv *priv = ieee80211_priv(dev);
+
+ return ieee80211_wx_get_rts(priv->ieee80211, info, wrqu, extra);
+}
+
+static int r8192_wx_set_power(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ int ret;
+ struct r8192_priv *priv = ieee80211_priv(dev);
+
+ down(&priv->wx_sem);
+
+ ret = ieee80211_wx_set_power(priv->ieee80211, info, wrqu, extra);
+
+ up(&priv->wx_sem);
+
+ return ret;
+}
+
+static int r8192_wx_get_power(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct r8192_priv *priv = ieee80211_priv(dev);
+
+ return ieee80211_wx_get_power(priv->ieee80211, info, wrqu, extra);
+}
+
+static int r8192_wx_force_reset(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct r8192_priv *priv = ieee80211_priv(dev);
+
+ down(&priv->wx_sem);
+
+ netdev_dbg(dev, "%s(): force reset ! extra is %d\n", __func__, *extra);
+ priv->force_reset = *extra;
+ up(&priv->wx_sem);
+ return 0;
+
+}
+
+
+static int r8192_wx_set_rawtx(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct r8192_priv *priv = ieee80211_priv(dev);
+ int ret;
+
+ down(&priv->wx_sem);
+
+ ret = ieee80211_wx_set_rawtx(priv->ieee80211, info, wrqu, extra);
+
+ up(&priv->wx_sem);
+
+ return ret;
+
+}
+
+static int r8192_wx_set_crcmon(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct r8192_priv *priv = ieee80211_priv(dev);
+ int *parms = (int *)extra;
+ int enable = (parms[0] > 0);
+
+ down(&priv->wx_sem);
+
+ if (enable)
+ priv->crcmon = 1;
+ else
+ priv->crcmon = 0;
+
+ DMESG("bad CRC in monitor mode are %s",
+ priv->crcmon ? "accepted" : "rejected");
+
+ up(&priv->wx_sem);
+
+ return 0;
+}
+
+static int r8192_wx_set_mode(struct net_device *dev, struct iw_request_info *a,
+ union iwreq_data *wrqu, char *b)
+{
+ struct r8192_priv *priv = ieee80211_priv(dev);
+ int ret;
+
+ down(&priv->wx_sem);
+
+ ret = ieee80211_wx_set_mode(priv->ieee80211, a, wrqu, b);
+
+ rtl8192_set_rxconf(dev);
+
+ up(&priv->wx_sem);
+ return ret;
+}
+
+struct iw_range_with_scan_capa {
+ /* Informative stuff (to choose between different interface) */
+ __u32 throughput; /* To give an idea... */
+ /* In theory this value should be the maximum benchmarked
+ * TCP/IP throughput, because with most of these devices the
+ * bit rate is meaningless (overhead an co) to estimate how
+ * fast the connection will go and pick the fastest one.
+ * I suggest people to play with Netperf or any benchmark...
+ */
+
+ /* NWID (or domain id) */
+ __u32 min_nwid; /* Minimal NWID we are able to set */
+ __u32 max_nwid; /* Maximal NWID we are able to set */
+
+ /* Old Frequency (backward compat - moved lower ) */
+ __u16 old_num_channels;
+ __u8 old_num_frequency;
+
+ /* Scan capabilities */
+ __u8 scan_capa;
+};
+static int rtl8180_wx_get_range(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct iw_range *range = (struct iw_range *)extra;
+ struct iw_range_with_scan_capa *tmp = (struct iw_range_with_scan_capa *)range;
+ struct r8192_priv *priv = ieee80211_priv(dev);
+ u16 val;
+ int i;
+
+ wrqu->data.length = sizeof(*range);
+ memset(range, 0, sizeof(*range));
+
+ /* Let's try to keep this struct in the same order as in
+ * linux/include/wireless.h
+ */
+
+ /* TODO: See what values we can set, and remove the ones we can't
+ * set, or fill them with some default data.
+ */
+
+ /* ~5 Mb/s real (802.11b) */
+ range->throughput = 5 * 1000 * 1000;
+
+ /* TODO: Not used in 802.11b? */
+ /* range->min_nwid; */ /* Minimal NWID we are able to set */
+ /* TODO: Not used in 802.11b? */
+ /* range->max_nwid; */ /* Maximal NWID we are able to set */
+
+ /* Old Frequency (backward compat - moved lower ) */
+ /* range->old_num_channels; */
+ /* range->old_num_frequency; */
+ /* range->old_freq[6]; */ /* Filler to keep "version" at the same offset */
+ if (priv->rf_set_sens != NULL)
+ range->sensitivity = priv->max_sens; /* signal level threshold range */
+
+ range->max_qual.qual = 100;
+ /* TODO: Find real max RSSI and stick here */
+ range->max_qual.level = 0;
+ range->max_qual.noise = -98;
+ range->max_qual.updated = 7; /* Updated all three */
+
+ range->avg_qual.qual = 92; /* > 8% missed beacons is 'bad' */
+ /* TODO: Find real 'good' to 'bad' threshold value for RSSI */
+ range->avg_qual.level = 20 + -98;
+ range->avg_qual.noise = 0;
+ range->avg_qual.updated = 7; /* Updated all three */
+
+ range->num_bitrates = RATE_COUNT;
+
+ for (i = 0; i < RATE_COUNT && i < IW_MAX_BITRATES; i++)
+ range->bitrate[i] = rtl8180_rates[i];
+
+ range->min_frag = MIN_FRAG_THRESHOLD;
+ range->max_frag = MAX_FRAG_THRESHOLD;
+
+ range->min_pmp = 0;
+ range->max_pmp = 5000000;
+ range->min_pmt = 0;
+ range->max_pmt = 65535*1000;
+ range->pmp_flags = IW_POWER_PERIOD;
+ range->pmt_flags = IW_POWER_TIMEOUT;
+ range->pm_capa = IW_POWER_PERIOD | IW_POWER_TIMEOUT | IW_POWER_ALL_R;
+
+ range->we_version_compiled = WIRELESS_EXT;
+ range->we_version_source = 16;
+
+ /* range->retry_capa; */ /* What retry options are supported */
+ /* range->retry_flags; */ /* How to decode max/min retry limit */
+ /* range->r_time_flags; */ /* How to decode max/min retry life */
+ /* range->min_retry; */ /* Minimal number of retries */
+ /* range->max_retry; */ /* Maximal number of retries */
+ /* range->min_r_time; */ /* Minimal retry lifetime */
+ /* range->max_r_time; */ /* Maximal retry lifetime */
+
+
+ for (i = 0, val = 0; i < 14; i++) {
+
+ /* Include only legal frequencies for some countries */
+ if ((GET_DOT11D_INFO(priv->ieee80211)->channel_map)[i+1]) {
+ range->freq[val].i = i + 1;
+ range->freq[val].m = ieee80211_wlan_frequencies[i] * 100000;
+ range->freq[val].e = 1;
+ val++;
+ } else {
+ /* FIXME: do we need to set anything for channels */
+ /* we don't use ? */
+ }
+
+ if (val == IW_MAX_FREQUENCIES)
+ break;
+ }
+ range->num_frequency = val;
+ range->num_channels = val;
+ range->enc_capa = IW_ENC_CAPA_WPA|IW_ENC_CAPA_WPA2|
+ IW_ENC_CAPA_CIPHER_TKIP|IW_ENC_CAPA_CIPHER_CCMP;
+ tmp->scan_capa = 0x01;
+ return 0;
+}
+
+
+static int r8192_wx_set_scan(struct net_device *dev, struct iw_request_info *a,
+ union iwreq_data *wrqu, char *b)
+{
+ struct r8192_priv *priv = ieee80211_priv(dev);
+ struct ieee80211_device *ieee = priv->ieee80211;
+ int ret = 0;
+
+ if (!priv->up)
+ return -ENETDOWN;
+
+ if (priv->ieee80211->LinkDetectInfo.bBusyTraffic)
+ return -EAGAIN;
+ if (wrqu->data.flags & IW_SCAN_THIS_ESSID) {
+ struct iw_scan_req *req = (struct iw_scan_req *)b;
+
+ if (req->essid_len) {
+ ieee->current_network.ssid_len = req->essid_len;
+ memcpy(ieee->current_network.ssid, req->essid, req->essid_len);
+ }
+ }
+
+ down(&priv->wx_sem);
+ if (priv->ieee80211->state != IEEE80211_LINKED) {
+ priv->ieee80211->scanning = 0;
+ ieee80211_softmac_scan_syncro(priv->ieee80211);
+ ret = 0;
+ } else {
+ ret = ieee80211_wx_set_scan(priv->ieee80211, a, wrqu, b);
+ }
+ up(&priv->wx_sem);
+ return ret;
+}
+
+
+static int r8192_wx_get_scan(struct net_device *dev, struct iw_request_info *a,
+ union iwreq_data *wrqu, char *b)
+{
+
+ int ret;
+ struct r8192_priv *priv = ieee80211_priv(dev);
+
+ if (!priv->up)
+ return -ENETDOWN;
+
+ down(&priv->wx_sem);
+
+ ret = ieee80211_wx_get_scan(priv->ieee80211, a, wrqu, b);
+
+ up(&priv->wx_sem);
+
+ return ret;
+}
+
+static int r8192_wx_set_essid(struct net_device *dev,
+ struct iw_request_info *a,
+ union iwreq_data *wrqu, char *b)
+{
+ struct r8192_priv *priv = ieee80211_priv(dev);
+ int ret;
+
+ down(&priv->wx_sem);
+
+ ret = ieee80211_wx_set_essid(priv->ieee80211, a, wrqu, b);
+
+ up(&priv->wx_sem);
+
+ return ret;
+}
+
+
+
+
+static int r8192_wx_get_essid(struct net_device *dev,
+ struct iw_request_info *a,
+ union iwreq_data *wrqu, char *b)
+{
+ int ret;
+ struct r8192_priv *priv = ieee80211_priv(dev);
+
+ down(&priv->wx_sem);
+
+ ret = ieee80211_wx_get_essid(priv->ieee80211, a, wrqu, b);
+
+ up(&priv->wx_sem);
+
+ return ret;
+}
+
+
+static int r8192_wx_set_freq(struct net_device *dev, struct iw_request_info *a,
+ union iwreq_data *wrqu, char *b)
+{
+ int ret;
+ struct r8192_priv *priv = ieee80211_priv(dev);
+
+ down(&priv->wx_sem);
+
+ ret = ieee80211_wx_set_freq(priv->ieee80211, a, wrqu, b);
+
+ up(&priv->wx_sem);
+ return ret;
+}
+
+static int r8192_wx_get_name(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct r8192_priv *priv = ieee80211_priv(dev);
+
+ return ieee80211_wx_get_name(priv->ieee80211, info, wrqu, extra);
+}
+
+
+static int r8192_wx_set_frag(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct r8192_priv *priv = ieee80211_priv(dev);
+
+ if (wrqu->frag.disabled)
+ priv->ieee80211->fts = DEFAULT_FRAG_THRESHOLD;
+ else {
+ if (wrqu->frag.value < MIN_FRAG_THRESHOLD ||
+ wrqu->frag.value > MAX_FRAG_THRESHOLD)
+ return -EINVAL;
+
+ priv->ieee80211->fts = wrqu->frag.value & ~0x1;
+ }
+
+ return 0;
+}
+
+
+static int r8192_wx_get_frag(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct r8192_priv *priv = ieee80211_priv(dev);
+
+ wrqu->frag.value = priv->ieee80211->fts;
+ wrqu->frag.fixed = 0; /* no auto select */
+ wrqu->frag.disabled = (wrqu->frag.value == DEFAULT_FRAG_THRESHOLD);
+
+ return 0;
+}
+
+
+static int r8192_wx_set_wap(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *awrq,
+ char *extra)
+{
+
+ int ret;
+ struct r8192_priv *priv = ieee80211_priv(dev);
+ /* struct sockaddr *temp = (struct sockaddr *)awrq; */
+ down(&priv->wx_sem);
+
+ ret = ieee80211_wx_set_wap(priv->ieee80211, info, awrq, extra);
+
+ up(&priv->wx_sem);
+
+ return ret;
+
+}
+
+
+static int r8192_wx_get_wap(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct r8192_priv *priv = ieee80211_priv(dev);
+
+ return ieee80211_wx_get_wap(priv->ieee80211, info, wrqu, extra);
+}
+
+
+static int r8192_wx_get_enc(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *key)
+{
+ struct r8192_priv *priv = ieee80211_priv(dev);
+
+ return ieee80211_wx_get_encode(priv->ieee80211, info, wrqu, key);
+}
+
+static int r8192_wx_set_enc(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *key)
+{
+ struct r8192_priv *priv = ieee80211_priv(dev);
+ struct ieee80211_device *ieee = priv->ieee80211;
+ int ret;
+ u32 hwkey[4] = {0, 0, 0, 0};
+ u8 mask = 0xff;
+ u32 key_idx = 0;
+ u8 zero_addr[4][6] = { {0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x01},
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x02},
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x03} };
+ int i;
+
+ if (!priv->up)
+ return -ENETDOWN;
+
+ down(&priv->wx_sem);
+
+ RT_TRACE(COMP_SEC, "Setting SW wep key");
+ ret = ieee80211_wx_set_encode(priv->ieee80211, info, wrqu, key);
+
+ up(&priv->wx_sem);
+
+
+
+ /* sometimes, the length is zero while we do not type key value */
+ if (wrqu->encoding.length != 0) {
+
+ for (i = 0; i < 4; i++) {
+ hwkey[i] |= key[4*i+0]&mask;
+ if (i == 1 && (4*i+1) == wrqu->encoding.length)
+ mask = 0x00;
+ if (i == 3 && (4*i+1) == wrqu->encoding.length)
+ mask = 0x00;
+ hwkey[i] |= (key[4*i+1]&mask)<<8;
+ hwkey[i] |= (key[4*i+2]&mask)<<16;
+ hwkey[i] |= (key[4*i+3]&mask)<<24;
+ }
+
+ #define CONF_WEP40 0x4
+ #define CONF_WEP104 0x14
+
+ switch (wrqu->encoding.flags & IW_ENCODE_INDEX) {
+ case 0:
+ key_idx = ieee->tx_keyidx;
+ break;
+ case 1:
+ key_idx = 0;
+ break;
+ case 2:
+ key_idx = 1;
+ break;
+ case 3:
+ key_idx = 2;
+ break;
+ case 4:
+ key_idx = 3;
+ break;
+ default:
+ break;
+ }
+
+ if (wrqu->encoding.length == 0x5) {
+ ieee->pairwise_key_type = KEY_TYPE_WEP40;
+ EnableHWSecurityConfig8192(dev);
+
+ setKey(dev,
+ key_idx, /* EntryNo */
+ key_idx, /* KeyIndex */
+ KEY_TYPE_WEP40, /* KeyType */
+ zero_addr[key_idx],
+ 0, /* DefaultKey */
+ hwkey); /* KeyContent */
+
+ }
+
+ else if (wrqu->encoding.length == 0xd) {
+ ieee->pairwise_key_type = KEY_TYPE_WEP104;
+ EnableHWSecurityConfig8192(dev);
+
+ setKey(dev,
+ key_idx, /* EntryNo */
+ key_idx, /* KeyIndex */
+ KEY_TYPE_WEP104, /* KeyType */
+ zero_addr[key_idx],
+ 0, /* DefaultKey */
+ hwkey); /* KeyContent */
+
+ } else {
+ printk("wrong type in WEP, not WEP40 and WEP104\n");
+ }
+
+ }
+
+ return ret;
+}
+
+
+static int r8192_wx_set_scan_type(struct net_device *dev, struct iw_request_info *aa,
+ union iwreq_data *wrqu, char *p)
+{
+
+ struct r8192_priv *priv = ieee80211_priv(dev);
+ int *parms = (int *)p;
+ int mode = parms[0];
+
+ priv->ieee80211->active_scan = mode;
+
+ return 1;
+}
+
+
+
+static int r8192_wx_set_retry(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct r8192_priv *priv = ieee80211_priv(dev);
+ int err = 0;
+
+ down(&priv->wx_sem);
+
+ if (wrqu->retry.flags & IW_RETRY_LIFETIME ||
+ wrqu->retry.disabled){
+ err = -EINVAL;
+ goto exit;
+ }
+ if (!(wrqu->retry.flags & IW_RETRY_LIMIT)) {
+ err = -EINVAL;
+ goto exit;
+ }
+
+ if (wrqu->retry.value > R8180_MAX_RETRY) {
+ err = -EINVAL;
+ goto exit;
+ }
+ if (wrqu->retry.flags & IW_RETRY_MAX) {
+ priv->retry_rts = wrqu->retry.value;
+ DMESG("Setting retry for RTS/CTS data to %d", wrqu->retry.value);
+
+ } else {
+ priv->retry_data = wrqu->retry.value;
+ DMESG("Setting retry for non RTS/CTS data to %d", wrqu->retry.value);
+ }
+
+ /* FIXME !
+ * We might try to write directly the TX config register
+ * or to restart just the (R)TX process.
+ * I'm unsure if whole reset is really needed
+ */
+
+ rtl8192_commit(dev);
+exit:
+ up(&priv->wx_sem);
+
+ return err;
+}
+
+static int r8192_wx_get_retry(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct r8192_priv *priv = ieee80211_priv(dev);
+
+
+ wrqu->retry.disabled = 0; /* can't be disabled */
+
+ if ((wrqu->retry.flags & IW_RETRY_TYPE) ==
+ IW_RETRY_LIFETIME)
+ return -EINVAL;
+
+ if (wrqu->retry.flags & IW_RETRY_MAX) {
+ wrqu->retry.flags = IW_RETRY_LIMIT | IW_RETRY_MAX;
+ wrqu->retry.value = priv->retry_rts;
+ } else {
+ wrqu->retry.flags = IW_RETRY_LIMIT | IW_RETRY_MIN;
+ wrqu->retry.value = priv->retry_data;
+ }
+
+
+ return 0;
+}
+
+static int r8192_wx_get_sens(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct r8192_priv *priv = ieee80211_priv(dev);
+
+ if (priv->rf_set_sens == NULL)
+ return -1; /* we have not this support for this radio */
+ wrqu->sens.value = priv->sens;
+ return 0;
+}
+
+
+static int r8192_wx_set_sens(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+
+ struct r8192_priv *priv = ieee80211_priv(dev);
+ short err = 0;
+
+ down(&priv->wx_sem);
+ if (priv->rf_set_sens == NULL) {
+ err = -1; /* we have not this support for this radio */
+ goto exit;
+ }
+ if (priv->rf_set_sens(dev, wrqu->sens.value) == 0)
+ priv->sens = wrqu->sens.value;
+ else
+ err = -EINVAL;
+
+exit:
+ up(&priv->wx_sem);
+
+ return err;
+}
+
+/* hw security need to reorganized. */
+static int r8192_wx_set_enc_ext(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ int ret = 0;
+ struct r8192_priv *priv = ieee80211_priv(dev);
+ struct ieee80211_device *ieee = priv->ieee80211;
+
+
+ down(&priv->wx_sem);
+ ret = ieee80211_wx_set_encode_ext(priv->ieee80211, info, wrqu, extra);
+
+ {
+ u8 broadcast_addr[6] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
+ u8 zero[6] = {0};
+ u32 key[4] = {0};
+ struct iw_encode_ext *ext = (struct iw_encode_ext *)extra;
+ struct iw_point *encoding = &wrqu->encoding;
+ u8 idx = 0, alg = 0, group = 0;
+
+ if ((encoding->flags & IW_ENCODE_DISABLED) || ext->alg == IW_ENCODE_ALG_NONE)
+ /* none is not allowed to use hwsec WB 2008.07.01 */
+ goto end_hw_sec;
+
+ /* as IW_ENCODE_ALG_CCMP is defined to be 3 and KEY_TYPE_CCMP is defined to 4; */
+ alg = (ext->alg == IW_ENCODE_ALG_CCMP)?KEY_TYPE_CCMP:ext->alg;
+ idx = encoding->flags & IW_ENCODE_INDEX;
+ if (idx)
+ idx--;
+ group = ext->ext_flags & IW_ENCODE_EXT_GROUP_KEY;
+
+ if ((!group) || (IW_MODE_ADHOC == ieee->iw_mode) || (alg == KEY_TYPE_WEP40)) {
+ if ((ext->key_len == 13) && (alg == KEY_TYPE_WEP40))
+ alg = KEY_TYPE_WEP104;
+ ieee->pairwise_key_type = alg;
+ EnableHWSecurityConfig8192(dev);
+ }
+ memcpy((u8 *)key, ext->key, 16); /* we only get 16 bytes key.why? WB 2008.7.1 */
+
+ if ((alg & KEY_TYPE_WEP40) && (ieee->auth_mode != 2)) {
+
+ setKey(dev,
+ idx, /* EntryNao */
+ idx, /* KeyIndex */
+ alg, /* KeyType */
+ zero, /* MacAddr */
+ 0, /* DefaultKey */
+ key); /* KeyContent */
+ } else if (group) {
+ ieee->group_key_type = alg;
+ setKey(dev,
+ idx, /* EntryNo */
+ idx, /* KeyIndex */
+ alg, /* KeyType */
+ broadcast_addr, /* MacAddr */
+ 0, /* DefaultKey */
+ key); /* KeyContent */
+ } else { /* pairwise key */
+ setKey(dev,
+ 4, /* EntryNo */
+ idx, /* KeyIndex */
+ alg, /* KeyType */
+ (u8 *)ieee->ap_mac_addr,/* MacAddr */
+ 0, /* DefaultKey */
+ key); /* KeyContent */
+ }
+
+
+ }
+
+end_hw_sec:
+
+ up(&priv->wx_sem);
+ return ret;
+
+}
+static int r8192_wx_set_auth(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *data, char *extra)
+{
+ int ret = 0;
+ struct r8192_priv *priv = ieee80211_priv(dev);
+
+ down(&priv->wx_sem);
+ ret = ieee80211_wx_set_auth(priv->ieee80211, info, &(data->param), extra);
+ up(&priv->wx_sem);
+ return ret;
+}
+
+static int r8192_wx_set_mlme(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+
+ int ret = 0;
+ struct r8192_priv *priv = ieee80211_priv(dev);
+
+ down(&priv->wx_sem);
+ ret = ieee80211_wx_set_mlme(priv->ieee80211, info, wrqu, extra);
+
+ up(&priv->wx_sem);
+ return ret;
+}
+
+static int r8192_wx_set_gen_ie(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *data, char *extra)
+{
+ int ret = 0;
+ struct r8192_priv *priv = ieee80211_priv(dev);
+
+ down(&priv->wx_sem);
+ ret = ieee80211_wx_set_gen_ie(priv->ieee80211, extra, data->data.length);
+ up(&priv->wx_sem);
+ return ret;
+
+
+}
+
+static int dummy(struct net_device *dev, struct iw_request_info *a,
+ union iwreq_data *wrqu, char *b)
+{
+ return -1;
+}
+
+
+static iw_handler r8192_wx_handlers[] = {
+ NULL, /* SIOCSIWCOMMIT */
+ r8192_wx_get_name, /* SIOCGIWNAME */
+ dummy, /* SIOCSIWNWID */
+ dummy, /* SIOCGIWNWID */
+ r8192_wx_set_freq, /* SIOCSIWFREQ */
+ r8192_wx_get_freq, /* SIOCGIWFREQ */
+ r8192_wx_set_mode, /* SIOCSIWMODE */
+ r8192_wx_get_mode, /* SIOCGIWMODE */
+ r8192_wx_set_sens, /* SIOCSIWSENS */
+ r8192_wx_get_sens, /* SIOCGIWSENS */
+ NULL, /* SIOCSIWRANGE */
+ rtl8180_wx_get_range, /* SIOCGIWRANGE */
+ NULL, /* SIOCSIWPRIV */
+ NULL, /* SIOCGIWPRIV */
+ NULL, /* SIOCSIWSTATS */
+ NULL, /* SIOCGIWSTATS */
+ dummy, /* SIOCSIWSPY */
+ dummy, /* SIOCGIWSPY */
+ NULL, /* SIOCGIWTHRSPY */
+ NULL, /* SIOCWIWTHRSPY */
+ r8192_wx_set_wap, /* SIOCSIWAP */
+ r8192_wx_get_wap, /* SIOCGIWAP */
+ r8192_wx_set_mlme, /* MLME-- */
+ dummy, /* SIOCGIWAPLIST -- deprecated */
+ r8192_wx_set_scan, /* SIOCSIWSCAN */
+ r8192_wx_get_scan, /* SIOCGIWSCAN */
+ r8192_wx_set_essid, /* SIOCSIWESSID */
+ r8192_wx_get_essid, /* SIOCGIWESSID */
+ dummy, /* SIOCSIWNICKN */
+ dummy, /* SIOCGIWNICKN */
+ NULL, /* -- hole -- */
+ NULL, /* -- hole -- */
+ r8192_wx_set_rate, /* SIOCSIWRATE */
+ r8192_wx_get_rate, /* SIOCGIWRATE */
+ r8192_wx_set_rts, /* SIOCSIWRTS */
+ r8192_wx_get_rts, /* SIOCGIWRTS */
+ r8192_wx_set_frag, /* SIOCSIWFRAG */
+ r8192_wx_get_frag, /* SIOCGIWFRAG */
+ dummy, /* SIOCSIWTXPOW */
+ dummy, /* SIOCGIWTXPOW */
+ r8192_wx_set_retry, /* SIOCSIWRETRY */
+ r8192_wx_get_retry, /* SIOCGIWRETRY */
+ r8192_wx_set_enc, /* SIOCSIWENCODE */
+ r8192_wx_get_enc, /* SIOCGIWENCODE */
+ r8192_wx_set_power, /* SIOCSIWPOWER */
+ r8192_wx_get_power, /* SIOCGIWPOWER */
+ NULL, /*---hole---*/
+ NULL, /*---hole---*/
+ r8192_wx_set_gen_ie, /* NULL, */ /* SIOCSIWGENIE */
+ NULL, /* SIOCSIWGENIE */
+
+ r8192_wx_set_auth,/* NULL, */ /* SIOCSIWAUTH */
+ NULL,/* r8192_wx_get_auth, */ /* NULL, */ /* SIOCSIWAUTH */
+ r8192_wx_set_enc_ext, /* SIOCSIWENCODEEXT */
+ NULL,/* r8192_wx_get_enc_ext, *//* NULL, */ /* SIOCSIWENCODEEXT */
+ NULL, /* SIOCSIWPMKSA */
+ NULL, /*---hole---*/
+
+};
+
+
+static const struct iw_priv_args r8192_private_args[] = {
+
+ {
+ SIOCIWFIRSTPRIV + 0x0,
+ IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "badcrc"
+ },
+
+ {
+ SIOCIWFIRSTPRIV + 0x1,
+ IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "activescan"
+
+ },
+ {
+ SIOCIWFIRSTPRIV + 0x2,
+ IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "rawtx"
+ },
+ {
+ SIOCIWFIRSTPRIV + 0x3,
+ IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "forcereset"
+
+ }
+
+};
+
+
+static iw_handler r8192_private_handler[] = {
+ r8192_wx_set_crcmon,
+ r8192_wx_set_scan_type,
+ r8192_wx_set_rawtx,
+ r8192_wx_force_reset,
+};
+
+struct iw_statistics *r8192_get_wireless_stats(struct net_device *dev)
+{
+ struct r8192_priv *priv = ieee80211_priv(dev);
+ struct ieee80211_device *ieee = priv->ieee80211;
+ struct iw_statistics *wstats = &priv->wstats;
+ int tmp_level = 0;
+ int tmp_qual = 0;
+ int tmp_noise = 0;
+
+ if (ieee->state < IEEE80211_LINKED) {
+ wstats->qual.qual = 0;
+ wstats->qual.level = 0;
+ wstats->qual.noise = 0;
+ wstats->qual.updated = IW_QUAL_ALL_UPDATED | IW_QUAL_DBM;
+ return wstats;
+ }
+
+ tmp_level = (&ieee->current_network)->stats.rssi;
+ tmp_qual = (&ieee->current_network)->stats.signal;
+ tmp_noise = (&ieee->current_network)->stats.noise;
+
+ wstats->qual.level = tmp_level;
+ wstats->qual.qual = tmp_qual;
+ wstats->qual.noise = tmp_noise;
+ wstats->qual.updated = IW_QUAL_ALL_UPDATED | IW_QUAL_DBM;
+ return wstats;
+}
+
+
+struct iw_handler_def r8192_wx_handlers_def = {
+ .standard = r8192_wx_handlers,
+ .num_standard = ARRAY_SIZE(r8192_wx_handlers),
+ .private = r8192_private_handler,
+ .num_private = ARRAY_SIZE(r8192_private_handler),
+ .num_private_args = sizeof(r8192_private_args) / sizeof(struct iw_priv_args),
+ .get_wireless_stats = r8192_get_wireless_stats,
+ .private_args = (struct iw_priv_args *)r8192_private_args,
+};
diff --git a/drivers/staging/rtl8192u/r8192U_wx.h b/drivers/staging/rtl8192u/r8192U_wx.h
new file mode 100644
index 000000000..d6a2d9756
--- /dev/null
+++ b/drivers/staging/rtl8192u/r8192U_wx.h
@@ -0,0 +1,24 @@
+/*
+ * This is part of rtl8180 OpenSource driver - v 0.3
+ * Copyright (C) Andrea Merello 2004 <andrea.merello@gmail.com>
+ * Released under the terms of GPL (General Public Licence)
+ *
+ * Parts of this driver are based on the GPL part of the official realtek driver
+ * Parts of this driver are based on the rtl8180 driver skeleton from Patric
+ * Schenke & Andres Salomon
+ * Parts of this driver are based on the Intel Pro Wireless 2100 GPL driver
+ *
+ * We want to thank the Authors of such projects and the Ndiswrapper project
+ * Authors.
+ */
+
+/* this file (will) contains wireless extension handlers */
+
+#ifndef R8180_WX_H
+#define R8180_WX_H
+
+extern struct iw_handler_def r8192_wx_handlers_def;
+/* Enable the rtl819x_core.c to share this function, david 2008.9.22 */
+extern struct iw_statistics *r8192_get_wireless_stats(struct net_device *dev);
+
+#endif
diff --git a/drivers/staging/rtl8192u/r819xU_cmdpkt.c b/drivers/staging/rtl8192u/r819xU_cmdpkt.c
new file mode 100644
index 000000000..545f49ec9
--- /dev/null
+++ b/drivers/staging/rtl8192u/r819xU_cmdpkt.c
@@ -0,0 +1,571 @@
+/******************************************************************************
+ *
+ * (c) Copyright 2008, RealTEK Technologies Inc. All Rights Reserved.
+ *
+ * Module: r819xusb_cmdpkt.c
+ * (RTL8190 TX/RX command packet handler Source C File)
+ *
+ * Note: The module is responsible for handling TX and RX command packet.
+ * 1. TX : Send set and query configuration command packet.
+ * 2. RX : Receive tx feedback, beacon state, query configuration
+ * command packet.
+ *
+ * Function:
+ *
+ * Export:
+ *
+ * Abbrev:
+ *
+ * History:
+ *
+ * Date Who Remark
+ * 05/06/2008 amy Create initial version porting from
+ * windows driver.
+ *
+ ******************************************************************************/
+#include "r8192U.h"
+#include "r819xU_cmdpkt.h"
+
+rt_status SendTxCommandPacket(struct net_device *dev, void *pData, u32 DataLen)
+{
+ struct r8192_priv *priv = ieee80211_priv(dev);
+ struct sk_buff *skb;
+ cb_desc *tcb_desc;
+ unsigned char *ptr_buf;
+
+ /* Get TCB and local buffer from common pool.
+ (It is shared by CmdQ, MgntQ, and USB coalesce DataQ) */
+ skb = dev_alloc_skb(USB_HWDESC_HEADER_LEN + DataLen + 4);
+ if (!skb)
+ return RT_STATUS_FAILURE;
+ memcpy((unsigned char *)(skb->cb), &dev, sizeof(dev));
+ tcb_desc = (cb_desc *)(skb->cb + MAX_DEV_ADDR_SIZE);
+ tcb_desc->queue_index = TXCMD_QUEUE;
+ tcb_desc->bCmdOrInit = DESC_PACKET_TYPE_NORMAL;
+ tcb_desc->bLastIniPkt = 0;
+ skb_reserve(skb, USB_HWDESC_HEADER_LEN);
+ ptr_buf = skb_put(skb, DataLen);
+ memcpy(ptr_buf, pData, DataLen);
+ tcb_desc->txbuf_size = (u16)DataLen;
+
+ if (!priv->ieee80211->check_nic_enough_desc(dev, tcb_desc->queue_index) ||
+ (!skb_queue_empty(&priv->ieee80211->skb_waitQ[tcb_desc->queue_index])) ||
+ (priv->ieee80211->queue_stop)) {
+ RT_TRACE(COMP_FIRMWARE, "=== NULL packet ======> tx full!\n");
+ skb_queue_tail(&priv->ieee80211->skb_waitQ[tcb_desc->queue_index], skb);
+ } else {
+ priv->ieee80211->softmac_hard_start_xmit(skb, dev);
+ }
+
+ return RT_STATUS_SUCCESS;
+}
+
+/*-----------------------------------------------------------------------------
+ * Function: cmpk_counttxstatistic()
+ *
+ * Overview:
+ *
+ * Input: PADAPTER pAdapter
+ * CMPK_TXFB_T *psTx_FB
+ *
+ * Output: NONE
+ *
+ * Return: NONE
+ *
+ * Revised History:
+ * When Who Remark
+ * 05/12/2008 amy Create Version 0 porting from windows code.
+ *
+ *---------------------------------------------------------------------------*/
+static void cmpk_count_txstatistic(struct net_device *dev, cmpk_txfb_t *pstx_fb)
+{
+ struct r8192_priv *priv = ieee80211_priv(dev);
+#ifdef ENABLE_PS
+ RT_RF_POWER_STATE rtState;
+
+ pAdapter->HalFunc.GetHwRegHandler(pAdapter, HW_VAR_RF_STATE,
+ (pu1Byte)(&rtState));
+
+ /* When RF is off, we should not count the packet for hw/sw synchronize
+ reason, ie. there may be a duration while sw switch is changed and
+ hw switch is being changed. */
+ if (rtState == eRfOff)
+ return;
+#endif
+
+#ifdef TODO
+ if (pAdapter->bInHctTest)
+ return;
+#endif
+ /* We can not know the packet length and transmit type:
+ broadcast or uni or multicast. So the relative statistics
+ must be collected in tx feedback info. */
+ if (pstx_fb->tok) {
+ priv->stats.txfeedbackok++;
+ priv->stats.txoktotal++;
+ priv->stats.txokbytestotal += pstx_fb->pkt_length;
+ priv->stats.txokinperiod++;
+
+ /* We can not make sure broadcast/multicast or unicast mode. */
+ if (pstx_fb->pkt_type == PACKET_MULTICAST) {
+ priv->stats.txmulticast++;
+ priv->stats.txbytesmulticast += pstx_fb->pkt_length;
+ } else if (pstx_fb->pkt_type == PACKET_BROADCAST) {
+ priv->stats.txbroadcast++;
+ priv->stats.txbytesbroadcast += pstx_fb->pkt_length;
+ } else {
+ priv->stats.txunicast++;
+ priv->stats.txbytesunicast += pstx_fb->pkt_length;
+ }
+ } else {
+ priv->stats.txfeedbackfail++;
+ priv->stats.txerrtotal++;
+ priv->stats.txerrbytestotal += pstx_fb->pkt_length;
+
+ /* We can not make sure broadcast/multicast or unicast mode. */
+ if (pstx_fb->pkt_type == PACKET_MULTICAST)
+ priv->stats.txerrmulticast++;
+ else if (pstx_fb->pkt_type == PACKET_BROADCAST)
+ priv->stats.txerrbroadcast++;
+ else
+ priv->stats.txerrunicast++;
+ }
+
+ priv->stats.txretrycount += pstx_fb->retry_cnt;
+ priv->stats.txfeedbackretry += pstx_fb->retry_cnt;
+
+}
+
+
+
+/*-----------------------------------------------------------------------------
+ * Function: cmpk_handle_tx_feedback()
+ *
+ * Overview: The function is responsible for extract the message inside TX
+ * feedbck message from firmware. It will contain dedicated info in
+ * ws-06-0063-rtl8190-command-packet-specification.
+ * Please refer to chapter "TX Feedback Element".
+ * We have to read 20 bytes in the command packet.
+ *
+ * Input: struct net_device *dev
+ * u8 *pmsg - Msg Ptr of the command packet.
+ *
+ * Output: NONE
+ *
+ * Return: NONE
+ *
+ * Revised History:
+ * When Who Remark
+ * 05/08/2008 amy Create Version 0 porting from windows code.
+ *
+ *---------------------------------------------------------------------------*/
+static void cmpk_handle_tx_feedback(struct net_device *dev, u8 *pmsg)
+{
+ struct r8192_priv *priv = ieee80211_priv(dev);
+ cmpk_txfb_t rx_tx_fb;
+
+ priv->stats.txfeedback++;
+
+ /* 1. Extract TX feedback info from RFD to temp structure buffer. */
+ /* It seems that FW use big endian(MIPS) and DRV use little endian in
+ windows OS. So we have to read the content byte by byte or transfer
+ endian type before copy the message copy. */
+ /* Use pointer to transfer structure memory. */
+ memcpy((u8 *)&rx_tx_fb, pmsg, sizeof(cmpk_txfb_t));
+ /* 2. Use tx feedback info to count TX statistics. */
+ cmpk_count_txstatistic(dev, &rx_tx_fb);
+ /* Comment previous method for TX statistic function. */
+ /* Collect info TX feedback packet to fill TCB. */
+ /* We can not know the packet length and transmit type: broadcast or uni
+ or multicast. */
+
+}
+
+static void cmdpkt_beacontimerinterrupt_819xusb(struct net_device *dev)
+{
+ struct r8192_priv *priv = ieee80211_priv(dev);
+ u16 tx_rate;
+ /* 87B have to S/W beacon for DTM encryption_cmn. */
+ if (priv->ieee80211->current_network.mode == IEEE_A ||
+ priv->ieee80211->current_network.mode == IEEE_N_5G ||
+ (priv->ieee80211->current_network.mode == IEEE_N_24G &&
+ (!priv->ieee80211->pHTInfo->bCurSuppCCK))) {
+ tx_rate = 60;
+ DMESG("send beacon frame tx rate is 6Mbpm\n");
+ } else {
+ tx_rate = 10;
+ DMESG("send beacon frame tx rate is 1Mbpm\n");
+ }
+
+ rtl819xusb_beacon_tx(dev, tx_rate); /* HW Beacon */
+
+
+}
+
+
+
+
+/*-----------------------------------------------------------------------------
+ * Function: cmpk_handle_interrupt_status()
+ *
+ * Overview: The function is responsible for extract the message from
+ * firmware. It will contain dedicated info in
+ * ws-07-0063-v06-rtl819x-command-packet-specification-070315.doc.
+ * Please refer to chapter "Interrupt Status Element".
+ *
+ * Input: struct net_device *dev
+ * u8 *pmsg - Message Pointer of the command packet.
+ *
+ * Output: NONE
+ *
+ * Return: NONE
+ *
+ * Revised History:
+ * When Who Remark
+ * 05/12/2008 amy Add this for rtl8192 porting from windows code.
+ *
+ *---------------------------------------------------------------------------*/
+static void cmpk_handle_interrupt_status(struct net_device *dev, u8 *pmsg)
+{
+ cmpk_intr_sta_t rx_intr_status; /* */
+ struct r8192_priv *priv = ieee80211_priv(dev);
+
+ DMESG("---> cmpk_Handle_Interrupt_Status()\n");
+
+ /* 1. Extract TX feedback info from RFD to temp structure buffer. */
+ /* It seems that FW use big endian(MIPS) and DRV use little endian in
+ windows OS. So we have to read the content byte by byte or transfer
+ endian type before copy the message copy. */
+ rx_intr_status.length = pmsg[1];
+ if (rx_intr_status.length != (sizeof(cmpk_intr_sta_t) - 2)) {
+ DMESG("cmpk_Handle_Interrupt_Status: wrong length!\n");
+ return;
+ }
+
+
+ /* Statistics of beacon for ad-hoc mode. */
+ if (priv->ieee80211->iw_mode == IW_MODE_ADHOC) {
+ /* 2 maybe need endian transform? */
+ rx_intr_status.interrupt_status = *((u32 *)(pmsg + 4));
+
+ DMESG("interrupt status = 0x%x\n",
+ rx_intr_status.interrupt_status);
+
+ if (rx_intr_status.interrupt_status & ISR_TxBcnOk) {
+ priv->ieee80211->bibsscoordinator = true;
+ priv->stats.txbeaconokint++;
+ } else if (rx_intr_status.interrupt_status & ISR_TxBcnErr) {
+ priv->ieee80211->bibsscoordinator = false;
+ priv->stats.txbeaconerr++;
+ }
+
+ if (rx_intr_status.interrupt_status & ISR_BcnTimerIntr)
+ cmdpkt_beacontimerinterrupt_819xusb(dev);
+
+ }
+
+ /* Other informations in interrupt status we need? */
+
+
+ DMESG("<---- cmpk_handle_interrupt_status()\n");
+
+}
+
+
+/*-----------------------------------------------------------------------------
+ * Function: cmpk_handle_query_config_rx()
+ *
+ * Overview: The function is responsible for extract the message from
+ * firmware. It will contain dedicated info in
+ * ws-06-0063-rtl8190-command-packet-specification. Please
+ * refer to chapter "Beacon State Element".
+ *
+ * Input: u8 *pmsg - Message Pointer of the command packet.
+ *
+ * Output: NONE
+ *
+ * Return: NONE
+ *
+ * Revised History:
+ * When Who Remark
+ * 05/12/2008 amy Create Version 0 porting from windows code.
+ *
+ *---------------------------------------------------------------------------*/
+static void cmpk_handle_query_config_rx(struct net_device *dev, u8 *pmsg)
+{
+ cmpk_query_cfg_t rx_query_cfg;
+
+
+ /* 1. Extract TX feedback info from RFD to temp structure buffer. */
+ /* It seems that FW use big endian(MIPS) and DRV use little endian in
+ windows OS. So we have to read the content byte by byte or transfer
+ endian type before copy the message copy. */
+ rx_query_cfg.cfg_action = (pmsg[4] & 0x80000000) >> 31;
+ rx_query_cfg.cfg_type = (pmsg[4] & 0x60) >> 5;
+ rx_query_cfg.cfg_size = (pmsg[4] & 0x18) >> 3;
+ rx_query_cfg.cfg_page = (pmsg[6] & 0x0F) >> 0;
+ rx_query_cfg.cfg_offset = pmsg[7];
+ rx_query_cfg.value = (pmsg[8] << 24) | (pmsg[9] << 16) |
+ (pmsg[10] << 8) | (pmsg[11] << 0);
+ rx_query_cfg.mask = (pmsg[12] << 24) | (pmsg[13] << 16) |
+ (pmsg[14] << 8) | (pmsg[15] << 0);
+
+}
+
+
+/*-----------------------------------------------------------------------------
+ * Function: cmpk_count_tx_status()
+ *
+ * Overview: Count aggregated tx status from firmwar of one type rx command
+ * packet element id = RX_TX_STATUS.
+ *
+ * Input: NONE
+ *
+ * Output: NONE
+ *
+ * Return: NONE
+ *
+ * Revised History:
+ * When Who Remark
+ * 05/12/2008 amy Create Version 0 porting from windows code.
+ *
+ *---------------------------------------------------------------------------*/
+static void cmpk_count_tx_status(struct net_device *dev,
+ cmpk_tx_status_t *pstx_status)
+{
+ struct r8192_priv *priv = ieee80211_priv(dev);
+
+#ifdef ENABLE_PS
+
+ RT_RF_POWER_STATE rtstate;
+
+ pAdapter->HalFunc.GetHwRegHandler(pAdapter, HW_VAR_RF_STATE,
+ (pu1Byte)(&rtState));
+
+ /* When RF is off, we should not count the packet for hw/sw synchronize
+ reason, ie. there may be a duration while sw switch is changed and
+ hw switch is being changed. */
+ if (rtState == eRfOff)
+ return;
+#endif
+
+ priv->stats.txfeedbackok += pstx_status->txok;
+ priv->stats.txoktotal += pstx_status->txok;
+
+ priv->stats.txfeedbackfail += pstx_status->txfail;
+ priv->stats.txerrtotal += pstx_status->txfail;
+
+ priv->stats.txretrycount += pstx_status->txretry;
+ priv->stats.txfeedbackretry += pstx_status->txretry;
+
+
+ priv->stats.txmulticast += pstx_status->txmcok;
+ priv->stats.txbroadcast += pstx_status->txbcok;
+ priv->stats.txunicast += pstx_status->txucok;
+
+ priv->stats.txerrmulticast += pstx_status->txmcfail;
+ priv->stats.txerrbroadcast += pstx_status->txbcfail;
+ priv->stats.txerrunicast += pstx_status->txucfail;
+
+ priv->stats.txbytesmulticast += pstx_status->txmclength;
+ priv->stats.txbytesbroadcast += pstx_status->txbclength;
+ priv->stats.txbytesunicast += pstx_status->txuclength;
+
+ priv->stats.last_packet_rate = pstx_status->rate;
+}
+
+
+
+/*-----------------------------------------------------------------------------
+ * Function: cmpk_handle_tx_status()
+ *
+ * Overview: Firmware add a new tx feedback status to reduce rx command
+ * packet buffer operation load.
+ *
+ * Input: NONE
+ *
+ * Output: NONE
+ *
+ * Return: NONE
+ *
+ * Revised History:
+ * When Who Remark
+ * 05/12/2008 amy Create Version 0 porting from windows code.
+ *
+ *---------------------------------------------------------------------------*/
+static void cmpk_handle_tx_status(struct net_device *dev, u8 *pmsg)
+{
+ cmpk_tx_status_t rx_tx_sts;
+
+ memcpy((void *)&rx_tx_sts, (void *)pmsg, sizeof(cmpk_tx_status_t));
+ /* 2. Use tx feedback info to count TX statistics. */
+ cmpk_count_tx_status(dev, &rx_tx_sts);
+
+}
+
+
+/*-----------------------------------------------------------------------------
+ * Function: cmpk_handle_tx_rate_history()
+ *
+ * Overview: Firmware add a new tx rate history
+ *
+ * Input: NONE
+ *
+ * Output: NONE
+ *
+ * Return: NONE
+ *
+ * Revised History:
+ * When Who Remark
+ * 05/12/2008 amy Create Version 0 porting from windows code.
+ *
+ *---------------------------------------------------------------------------*/
+static void cmpk_handle_tx_rate_history(struct net_device *dev, u8 *pmsg)
+{
+ cmpk_tx_rahis_t *ptxrate;
+ u8 i, j;
+ u16 length = sizeof(cmpk_tx_rahis_t);
+ u32 *ptemp;
+ struct r8192_priv *priv = ieee80211_priv(dev);
+
+
+#ifdef ENABLE_PS
+ pAdapter->HalFunc.GetHwRegHandler(pAdapter, HW_VAR_RF_STATE,
+ (pu1Byte)(&rtState));
+
+ /* When RF is off, we should not count the packet for hw/sw synchronize
+ reason, ie. there may be a duration while sw switch is changed and
+ hw switch is being changed. */
+ if (rtState == eRfOff)
+ return;
+#endif
+
+ ptemp = (u32 *)pmsg;
+
+ /* Do endian transfer to word alignment(16 bits) for windows system.
+ You must do different endian transfer for linux and MAC OS */
+ for (i = 0; i < (length/4); i++) {
+ u16 temp1, temp2;
+
+ temp1 = ptemp[i] & 0x0000FFFF;
+ temp2 = ptemp[i] >> 16;
+ ptemp[i] = (temp1 << 16) | temp2;
+ }
+
+ ptxrate = (cmpk_tx_rahis_t *)pmsg;
+
+ if (ptxrate == NULL)
+ return;
+
+ for (i = 0; i < 16; i++) {
+ /* Collect CCK rate packet num */
+ if (i < 4)
+ priv->stats.txrate.cck[i] += ptxrate->cck[i];
+
+ /* Collect OFDM rate packet num */
+ if (i < 8)
+ priv->stats.txrate.ofdm[i] += ptxrate->ofdm[i];
+
+ for (j = 0; j < 4; j++)
+ priv->stats.txrate.ht_mcs[j][i] += ptxrate->ht_mcs[j][i];
+ }
+
+}
+
+
+/*-----------------------------------------------------------------------------
+ * Function: cmpk_message_handle_rx()
+ *
+ * Overview: In the function, we will capture different RX command packet
+ * info. Every RX command packet element has different message
+ * length and meaning in content. We only support three type of RX
+ * command packet now. Please refer to document
+ * ws-06-0063-rtl8190-command-packet-specification.
+ *
+ * Input: NONE
+ *
+ * Output: NONE
+ *
+ * Return: NONE
+ *
+ * Revised History:
+ * When Who Remark
+ * 05/06/2008 amy Create Version 0 porting from windows code.
+ *
+ *---------------------------------------------------------------------------*/
+u32 cmpk_message_handle_rx(struct net_device *dev,
+ struct ieee80211_rx_stats *pstats)
+{
+ int total_length;
+ u8 cmd_length, exe_cnt = 0;
+ u8 element_id;
+ u8 *pcmd_buff;
+
+ /* 0. Check inpt arguments. If is is a command queue message or
+ pointer is null. */
+ if (pstats == NULL)
+ return 0; /* This is not a command packet. */
+
+ /* 1. Read received command packet message length from RFD. */
+ total_length = pstats->Length;
+
+ /* 2. Read virtual address from RFD. */
+ pcmd_buff = pstats->virtual_address;
+
+ /* 3. Read command packet element id and length. */
+ element_id = pcmd_buff[0];
+
+ /* 4. Check every received command packet content according to different
+ element type. Because FW may aggregate RX command packet to
+ minimize transmit time between DRV and FW.*/
+ /* Add a counter to prevent the lock in the loop from being held too
+ long */
+ while (total_length > 0 && exe_cnt++ < 100) {
+ /* We support aggregation of different cmd in the same packet */
+ element_id = pcmd_buff[0];
+
+ switch (element_id) {
+ case RX_TX_FEEDBACK:
+ cmpk_handle_tx_feedback(dev, pcmd_buff);
+ cmd_length = CMPK_RX_TX_FB_SIZE;
+ break;
+
+ case RX_INTERRUPT_STATUS:
+ cmpk_handle_interrupt_status(dev, pcmd_buff);
+ cmd_length = sizeof(cmpk_intr_sta_t);
+ break;
+
+ case BOTH_QUERY_CONFIG:
+ cmpk_handle_query_config_rx(dev, pcmd_buff);
+ cmd_length = CMPK_BOTH_QUERY_CONFIG_SIZE;
+ break;
+
+ case RX_TX_STATUS:
+ cmpk_handle_tx_status(dev, pcmd_buff);
+ cmd_length = CMPK_RX_TX_STS_SIZE;
+ break;
+
+ case RX_TX_PER_PKT_FEEDBACK:
+ /* You must at lease add a switch case element here,
+ Otherwise, we will jump to default case. */
+ cmd_length = CMPK_RX_TX_FB_SIZE;
+ break;
+
+ case RX_TX_RATE_HISTORY:
+ cmpk_handle_tx_rate_history(dev, pcmd_buff);
+ cmd_length = CMPK_TX_RAHIS_SIZE;
+ break;
+
+ default:
+
+ RT_TRACE(COMP_ERR, "---->%s():unknown CMD Element\n",
+ __func__);
+ return 1; /* This is a command packet. */
+ }
+
+ total_length -= cmd_length;
+ pcmd_buff += cmd_length;
+ }
+ return 1; /* This is a command packet. */
+
+}
diff --git a/drivers/staging/rtl8192u/r819xU_cmdpkt.h b/drivers/staging/rtl8192u/r819xU_cmdpkt.h
new file mode 100644
index 000000000..52cd437ef
--- /dev/null
+++ b/drivers/staging/rtl8192u/r819xU_cmdpkt.h
@@ -0,0 +1,191 @@
+#ifndef R819XUSB_CMDPKT_H
+#define R819XUSB_CMDPKT_H
+/* Different command packet have dedicated message length and definition. */
+#define CMPK_RX_TX_FB_SIZE sizeof(cmpk_txfb_t) /* 20 */
+#define CMPK_TX_SET_CONFIG_SIZE sizeof(cmpk_set_cfg_t) /* 16 */
+#define CMPK_BOTH_QUERY_CONFIG_SIZE sizeof(cmpk_set_cfg_t) /* 16 */
+#define CMPK_RX_TX_STS_SIZE sizeof(cmpk_tx_status_t)
+#define CMPK_RX_DBG_MSG_SIZE sizeof(cmpk_rx_dbginfo_t)
+#define CMPK_TX_RAHIS_SIZE sizeof(cmpk_tx_rahis_t)
+
+/* 2008/05/08 amy For USB constant. */
+#define ISR_TxBcnOk BIT27 /* Transmit Beacon OK */
+#define ISR_TxBcnErr BIT26 /* Transmit Beacon Error */
+#define ISR_BcnTimerIntr BIT13 /* Beacon Timer Interrupt */
+
+
+/* Define element ID of command packet. */
+
+/*------------------------------Define structure----------------------------*/
+/* Define different command packet structure. */
+/* 1. RX side: TX feedback packet. */
+typedef struct tag_cmd_pkt_tx_feedback {
+ /* DWORD 0 */
+ u8 element_id; /* Command packet type. */
+ u8 length; /* Command packet length. */
+ /* Change tx feedback info field. */
+ /*------TX Feedback Info Field */
+ u8 TID:4;
+ u8 fail_reason:3;
+ u8 tok:1; /* Transmit ok. */
+ u8 reserve1:4;
+ u8 pkt_type:2;
+ u8 bandwidth:1;
+ u8 qos_pkt:1;
+
+ /* DWORD 1 */
+ u8 reserve2;
+ /*------TX Feedback Info Field */
+ u8 retry_cnt;
+ u16 pkt_id;
+
+ /* DWORD 3 */
+ u16 seq_num;
+ u8 s_rate; /* Start rate. */
+ u8 f_rate; /* Final rate. */
+
+ /* DWORD 4 */
+ u8 s_rts_rate;
+ u8 f_rts_rate;
+ u16 pkt_length;
+
+ /* DWORD 5 */
+ u16 reserve3;
+ u16 duration;
+} cmpk_txfb_t;
+
+/* 2. RX side: Interrupt status packet. It includes Beacon State,
+ * Beacon Timer Interrupt and other useful informations in MAC ISR Reg. */
+typedef struct tag_cmd_pkt_interrupt_status {
+ u8 element_id; /* Command packet type. */
+ u8 length; /* Command packet length. */
+ u16 reserve;
+ u32 interrupt_status; /* Interrupt Status. */
+} cmpk_intr_sta_t;
+
+
+/* 3. TX side: Set configuration packet. */
+typedef struct tag_cmd_pkt_set_configuration {
+ u8 element_id; /* Command packet type. */
+ u8 length; /* Command packet length. */
+ u16 reserve1;
+ /* Configuration info. */
+ u8 cfg_reserve1:3;
+ u8 cfg_size:2;
+ u8 cfg_type:2;
+ u8 cfg_action:1;
+ u8 cfg_reserve2;
+ u8 cfg_page:4;
+ u8 cfg_reserve3:4;
+ u8 cfg_offset;
+ u32 value;
+ u32 mask;
+} cmpk_set_cfg_t;
+
+/* 4. Both side : TX/RX query configuraton packet. The query structure is the
+ same as set configuration. */
+#define cmpk_query_cfg_t cmpk_set_cfg_t
+
+/* 5. Multi packet feedback status. */
+typedef struct tag_tx_stats_feedback {
+ /* For endian transfer --> Driver will not the same as
+ firmware structure. */
+ /* DW 0 */
+ u16 reserve1;
+ u8 length; /* Command packet length */
+ u8 element_id; /* Command packet type */
+
+ /* DW 1 */
+ u16 txfail; /* Tx fail count */
+ u16 txok; /* Tx ok count */
+
+ /* DW 2 */
+ u16 txmcok; /* Tx multicast */
+ u16 txretry; /* Tx retry count */
+
+ /* DW 3 */
+ u16 txucok; /* Tx unicast */
+ u16 txbcok; /* Tx broadcast */
+
+ /* DW 4 */
+ u16 txbcfail;
+ u16 txmcfail;
+
+ /* DW 5 */
+ u16 reserve2;
+ u16 txucfail;
+
+ /* DW 6-8 */
+ u32 txmclength;
+ u32 txbclength;
+ u32 txuclength;
+
+ /* DW 9 */
+ u16 reserve3_23;
+ u8 reserve3_1;
+ u8 rate;
+} __packed cmpk_tx_status_t;
+
+/* 6. Debug feedback message. */
+/* Define RX debug message */
+typedef struct tag_rx_debug_message_feedback {
+ /* For endian transfer --> for driver */
+ /* DW 0 */
+ u16 reserve1;
+ u8 length; /* Command packet length */
+ u8 element_id; /* Command packet type */
+
+ /* DW 1-?? */
+ /* Variable debug message. */
+
+} cmpk_rx_dbginfo_t;
+
+/* Define transmit rate history. For big endian format. */
+typedef struct tag_tx_rate_history {
+ /* For endian transfer --> for driver */
+ /* DW 0 */
+ u8 element_id; /* Command packet type */
+ u8 length; /* Command packet length */
+ u16 reserved1;
+
+ /* DW 1-2 CCK rate counter */
+ u16 cck[4];
+
+ /* DW 3-6 */
+ u16 ofdm[8];
+
+ /* DW 7-14 BW=0 SG=0
+ * DW 15-22 BW=1 SG=0
+ * DW 23-30 BW=0 SG=1
+ * DW 31-38 BW=1 SG=1
+ */
+ u16 ht_mcs[4][16];
+
+} __packed cmpk_tx_rahis_t;
+
+typedef enum tag_command_packet_directories {
+ RX_TX_FEEDBACK = 0,
+ RX_INTERRUPT_STATUS = 1,
+ TX_SET_CONFIG = 2,
+ BOTH_QUERY_CONFIG = 3,
+ RX_TX_STATUS = 4,
+ RX_DBGINFO_FEEDBACK = 5,
+ RX_TX_PER_PKT_FEEDBACK = 6,
+ RX_TX_RATE_HISTORY = 7,
+ RX_CMD_ELE_MAX
+} cmpk_element_e;
+
+typedef enum _rt_status {
+ RT_STATUS_SUCCESS,
+ RT_STATUS_FAILURE,
+ RT_STATUS_PENDING,
+ RT_STATUS_RESOURCE
+} rt_status, *prt_status;
+
+extern u32 cmpk_message_handle_rx(struct net_device *dev,
+ struct ieee80211_rx_stats *pstats);
+extern rt_status SendTxCommandPacket(struct net_device *dev,
+ void *pData, u32 DataLen);
+
+
+#endif
diff --git a/drivers/staging/rtl8192u/r819xU_firmware.c b/drivers/staging/rtl8192u/r819xU_firmware.c
new file mode 100644
index 000000000..87c3d7a88
--- /dev/null
+++ b/drivers/staging/rtl8192u/r819xU_firmware.c
@@ -0,0 +1,342 @@
+/**************************************************************************************************
+ * Procedure: Init boot code/firmware code/data session
+ *
+ * Description: This routine will initialize firmware. If any error occurs during the initialization
+ * process, the routine shall terminate immediately and return fail.
+ * NIC driver should call NdisOpenFile only from MiniportInitialize.
+ *
+ * Arguments: The pointer of the adapter
+
+ * Returns:
+ * NDIS_STATUS_FAILURE - the following initialization process should be terminated
+ * NDIS_STATUS_SUCCESS - if firmware initialization process success
+**************************************************************************************************/
+
+#include "r8192U.h"
+#include "r8192U_hw.h"
+#include "r819xU_firmware_img.h"
+#include "r819xU_firmware.h"
+#include <linux/firmware.h>
+
+static void firmware_init_param(struct net_device *dev)
+{
+ struct r8192_priv *priv = ieee80211_priv(dev);
+ rt_firmware *pfirmware = priv->pFirmware;
+
+ pfirmware->cmdpacket_frag_thresold = GET_COMMAND_PACKET_FRAG_THRESHOLD(MAX_TRANSMIT_BUFFER_SIZE);
+}
+
+/*
+ * segment the img and use the ptr and length to remember info on each segment
+ *
+ */
+static bool fw_download_code(struct net_device *dev, u8 *code_virtual_address,
+ u32 buffer_len)
+{
+ struct r8192_priv *priv = ieee80211_priv(dev);
+ bool rt_status = true;
+ u16 frag_threshold;
+ u16 frag_length, frag_offset = 0;
+ int i;
+
+ rt_firmware *pfirmware = priv->pFirmware;
+ struct sk_buff *skb;
+ unsigned char *seg_ptr;
+ cb_desc *tcb_desc;
+ u8 bLastIniPkt;
+ u8 index;
+
+ firmware_init_param(dev);
+ /* Fragmentation might be required */
+ frag_threshold = pfirmware->cmdpacket_frag_thresold;
+ do {
+ if ((buffer_len - frag_offset) > frag_threshold) {
+ frag_length = frag_threshold;
+ bLastIniPkt = 0;
+
+ } else {
+ frag_length = buffer_len - frag_offset;
+ bLastIniPkt = 1;
+
+ }
+
+ /* Allocate skb buffer to contain firmware info and tx descriptor info
+ * add 4 to avoid packet appending overflow.
+ * */
+ skb = dev_alloc_skb(USB_HWDESC_HEADER_LEN + frag_length + 4);
+ if (!skb)
+ return false;
+ memcpy((unsigned char *)(skb->cb),&dev,sizeof(dev));
+ tcb_desc = (cb_desc *)(skb->cb + MAX_DEV_ADDR_SIZE);
+ tcb_desc->queue_index = TXCMD_QUEUE;
+ tcb_desc->bCmdOrInit = DESC_PACKET_TYPE_INIT;
+ tcb_desc->bLastIniPkt = bLastIniPkt;
+
+ skb_reserve(skb, USB_HWDESC_HEADER_LEN);
+ seg_ptr = skb->data;
+ /*
+ * Transform from little endian to big endian
+ * and pending zero
+ */
+ for (i = 0; i < frag_length; i += 4) {
+ *seg_ptr++ = ((i+0) < frag_length)?code_virtual_address[i+3] : 0;
+ *seg_ptr++ = ((i+1) < frag_length)?code_virtual_address[i+2] : 0;
+ *seg_ptr++ = ((i+2) < frag_length)?code_virtual_address[i+1] : 0;
+ *seg_ptr++ = ((i+3) < frag_length)?code_virtual_address[i+0] : 0;
+ }
+ tcb_desc->txbuf_size = (u16)i;
+ skb_put(skb, i);
+
+ index = tcb_desc->queue_index;
+ if (!priv->ieee80211->check_nic_enough_desc(dev, index) ||
+ (!skb_queue_empty(&priv->ieee80211->skb_waitQ[index])) ||
+ (priv->ieee80211->queue_stop)) {
+ RT_TRACE(COMP_FIRMWARE,"=====================================================> tx full!\n");
+ skb_queue_tail(&priv->ieee80211->skb_waitQ[tcb_desc->queue_index], skb);
+ } else {
+ priv->ieee80211->softmac_hard_start_xmit(skb, dev);
+ }
+
+ code_virtual_address += frag_length;
+ frag_offset += frag_length;
+
+ } while (frag_offset < buffer_len);
+
+ return rt_status;
+
+}
+
+/*
+ * Procedure: Check whether main code is download OK. If OK, turn on CPU
+ *
+ * Description: CPU register locates in different page against general register.
+ * Switch to CPU register in the begin and switch back before return
+ *
+ *
+ * Arguments: The pointer of the adapter
+ *
+ * Returns:
+ * NDIS_STATUS_FAILURE - the following initialization process should
+ * be terminated
+ * NDIS_STATUS_SUCCESS - if firmware initialization process success
+ */
+static bool CPUcheck_maincodeok_turnonCPU(struct net_device *dev)
+{
+ bool rt_status = true;
+ int check_putcodeOK_time = 200000, check_bootOk_time = 200000;
+ u32 CPU_status = 0;
+
+ /* Check whether put code OK */
+ do {
+ read_nic_dword(dev, CPU_GEN, &CPU_status);
+
+ if (CPU_status&CPU_GEN_PUT_CODE_OK)
+ break;
+
+ } while (check_putcodeOK_time--);
+
+ if (!(CPU_status&CPU_GEN_PUT_CODE_OK)) {
+ RT_TRACE(COMP_ERR, "Download Firmware: Put code fail!\n");
+ goto CPUCheckMainCodeOKAndTurnOnCPU_Fail;
+ } else {
+ RT_TRACE(COMP_FIRMWARE, "Download Firmware: Put code ok!\n");
+ }
+
+ /* Turn On CPU */
+ read_nic_dword(dev, CPU_GEN, &CPU_status);
+ write_nic_byte(dev, CPU_GEN, (u8)((CPU_status|CPU_GEN_PWR_STB_CPU)&0xff));
+ mdelay(1000);
+
+ /* Check whether CPU boot OK */
+ do {
+ read_nic_dword(dev, CPU_GEN, &CPU_status);
+
+ if (CPU_status&CPU_GEN_BOOT_RDY)
+ break;
+ } while (check_bootOk_time--);
+
+ if (!(CPU_status&CPU_GEN_BOOT_RDY))
+ goto CPUCheckMainCodeOKAndTurnOnCPU_Fail;
+ else
+ RT_TRACE(COMP_FIRMWARE, "Download Firmware: Boot ready!\n");
+
+ return rt_status;
+
+CPUCheckMainCodeOKAndTurnOnCPU_Fail:
+ RT_TRACE(COMP_ERR, "ERR in %s()\n", __func__);
+ rt_status = false;
+ return rt_status;
+}
+
+static bool CPUcheck_firmware_ready(struct net_device *dev)
+{
+
+ bool rt_status = true;
+ int check_time = 200000;
+ u32 CPU_status = 0;
+
+ /* Check Firmware Ready */
+ do {
+ read_nic_dword(dev, CPU_GEN, &CPU_status);
+
+ if (CPU_status&CPU_GEN_FIRM_RDY)
+ break;
+
+ } while (check_time--);
+
+ if (!(CPU_status&CPU_GEN_FIRM_RDY))
+ goto CPUCheckFirmwareReady_Fail;
+ else
+ RT_TRACE(COMP_FIRMWARE, "Download Firmware: Firmware ready!\n");
+
+ return rt_status;
+
+CPUCheckFirmwareReady_Fail:
+ RT_TRACE(COMP_ERR, "ERR in %s()\n", __func__);
+ rt_status = false;
+ return rt_status;
+
+}
+
+bool init_firmware(struct net_device *dev)
+{
+ struct r8192_priv *priv = ieee80211_priv(dev);
+ bool rt_status = true;
+
+ u32 file_length = 0;
+ u8 *mapped_file = NULL;
+ u32 init_step = 0;
+ opt_rst_type_e rst_opt = OPT_SYSTEM_RESET;
+ firmware_init_step_e starting_state = FW_INIT_STEP0_BOOT;
+
+ rt_firmware *pfirmware = priv->pFirmware;
+ const struct firmware *fw_entry;
+ const char *fw_name[3] = { "/*(DEBLOBBED)*/",
+ "/*(DEBLOBBED)*/",
+ "/*(DEBLOBBED)*/"};
+ int rc;
+
+ RT_TRACE(COMP_FIRMWARE, " PlatformInitFirmware()==>\n");
+
+ if (pfirmware->firmware_status == FW_STATUS_0_INIT) {
+ /* it is called by reset */
+ rst_opt = OPT_SYSTEM_RESET;
+ starting_state = FW_INIT_STEP0_BOOT;
+ /* TODO: system reset */
+
+ } else if (pfirmware->firmware_status == FW_STATUS_5_READY) {
+ /* it is called by Initialize */
+ rst_opt = OPT_FIRMWARE_RESET;
+ starting_state = FW_INIT_STEP2_DATA;
+ } else {
+ RT_TRACE(COMP_FIRMWARE, "PlatformInitFirmware: undefined firmware state\n");
+ }
+
+ /*
+ * Download boot, main, and data image for System reset.
+ * Download data image for firmware reset
+ */
+ for (init_step = starting_state; init_step <= FW_INIT_STEP2_DATA; init_step++) {
+ /*
+ * Open image file, and map file to continuous memory if open file success.
+ * or read image file from array. Default load from IMG file
+ */
+ if (rst_opt == OPT_SYSTEM_RESET) {
+ rc = reject_firmware(&fw_entry, fw_name[init_step],&priv->udev->dev);
+ if (rc < 0) {
+ RT_TRACE(COMP_ERR, "request firmware fail!\n");
+ goto download_firmware_fail;
+ }
+
+ if (fw_entry->size > sizeof(pfirmware->firmware_buf)) {
+ RT_TRACE(COMP_ERR, "img file size exceed the container buffer fail!\n");
+ goto download_firmware_fail;
+ }
+
+ if (init_step != FW_INIT_STEP1_MAIN) {
+ memcpy(pfirmware->firmware_buf,fw_entry->data,fw_entry->size);
+ mapped_file = pfirmware->firmware_buf;
+ file_length = fw_entry->size;
+ } else {
+ memset(pfirmware->firmware_buf, 0, 128);
+ memcpy(&pfirmware->firmware_buf[128],fw_entry->data,fw_entry->size);
+ mapped_file = pfirmware->firmware_buf;
+ file_length = fw_entry->size + 128;
+ }
+ pfirmware->firmware_buf_size = file_length;
+ } else if (rst_opt == OPT_FIRMWARE_RESET) {
+ /* we only need to download data.img here */
+ mapped_file = pfirmware->firmware_buf;
+ file_length = pfirmware->firmware_buf_size;
+ }
+
+ /* Download image file */
+ /* The firmware download process is just as following,
+ * 1. that is each packet will be segmented and inserted to the wait queue.
+ * 2. each packet segment will be put in the skb_buff packet.
+ * 3. each skb_buff packet data content will already include the firmware info
+ * and Tx descriptor info
+ * */
+ rt_status = fw_download_code(dev, mapped_file, file_length);
+ if (rst_opt == OPT_SYSTEM_RESET)
+ release_firmware(fw_entry);
+
+ if (!rt_status)
+ goto download_firmware_fail;
+
+ switch (init_step) {
+ case FW_INIT_STEP0_BOOT:
+ /* Download boot
+ * initialize command descriptor.
+ * will set polling bit when firmware code is also configured
+ */
+ pfirmware->firmware_status = FW_STATUS_1_MOVE_BOOT_CODE;
+ /* mdelay(1000); */
+ /*
+ * To initialize IMEM, CPU move code from 0x80000080,
+ * hence, we send 0x80 byte packet
+ */
+ break;
+
+ case FW_INIT_STEP1_MAIN:
+ /* Download firmware code. Wait until Boot Ready and Turn on CPU */
+ pfirmware->firmware_status = FW_STATUS_2_MOVE_MAIN_CODE;
+
+ /* Check Put Code OK and Turn On CPU */
+ rt_status = CPUcheck_maincodeok_turnonCPU(dev);
+ if (!rt_status) {
+ RT_TRACE(COMP_ERR, "CPUcheck_maincodeok_turnonCPU fail!\n");
+ goto download_firmware_fail;
+ }
+
+ pfirmware->firmware_status = FW_STATUS_3_TURNON_CPU;
+ break;
+
+ case FW_INIT_STEP2_DATA:
+ /* download initial data code */
+ pfirmware->firmware_status = FW_STATUS_4_MOVE_DATA_CODE;
+ mdelay(1);
+
+ rt_status = CPUcheck_firmware_ready(dev);
+ if (!rt_status) {
+ RT_TRACE(COMP_ERR, "CPUcheck_firmware_ready fail(%d)!\n",rt_status);
+ goto download_firmware_fail;
+ }
+
+ /* wait until data code is initialized ready.*/
+ pfirmware->firmware_status = FW_STATUS_5_READY;
+ break;
+ }
+ }
+
+ RT_TRACE(COMP_FIRMWARE, "Firmware Download Success\n");
+ return rt_status;
+
+download_firmware_fail:
+ RT_TRACE(COMP_ERR, "ERR in %s()\n", __func__);
+ rt_status = false;
+ return rt_status;
+
+}
+
+/*(DEBLOBBED)*/
diff --git a/drivers/staging/rtl8192u/r819xU_firmware.h b/drivers/staging/rtl8192u/r819xU_firmware.h
new file mode 100644
index 000000000..cfa222350
--- /dev/null
+++ b/drivers/staging/rtl8192u/r819xU_firmware.h
@@ -0,0 +1,26 @@
+#ifndef __INC_FIRMWARE_H
+#define __INC_FIRMWARE_H
+
+#define RTL8190_CPU_START_OFFSET 0x80
+/* TODO: this definition is TBD */
+//#define USB_HWDESC_HEADER_LEN 0
+
+/* It should be double word alignment */
+//#if DEV_BUS_TYPE==PCI_INTERFACE
+//#define GET_COMMAND_PACKET_FRAG_THRESHOLD(v) 4*(v/4) - 8
+//#else
+#define GET_COMMAND_PACKET_FRAG_THRESHOLD(v) (4*(v/4) - 8 - USB_HWDESC_HEADER_LEN)
+//#endif
+
+typedef enum _firmware_init_step {
+ FW_INIT_STEP0_BOOT = 0,
+ FW_INIT_STEP1_MAIN = 1,
+ FW_INIT_STEP2_DATA = 2,
+} firmware_init_step_e;
+
+typedef enum _opt_rst_type {
+ OPT_SYSTEM_RESET = 0,
+ OPT_FIRMWARE_RESET = 1,
+} opt_rst_type_e;
+
+#endif
diff --git a/drivers/staging/rtl8192u/r819xU_firmware_img.c b/drivers/staging/rtl8192u/r819xU_firmware_img.c
new file mode 100644
index 000000000..4eb43cfe5
--- /dev/null
+++ b/drivers/staging/rtl8192u/r819xU_firmware_img.c
@@ -0,0 +1,548 @@
+/*Created on 2008/ 7/16, 5:31*/
+#include <linux/types.h>
+#include "r819xU_firmware_img.h"
+
+u32 Rtl8192UsbPHY_REGArray[] = {
+ 0x0, };
+
+u32 Rtl8192UsbPHY_REG_1T2RArray[] = {
+ 0x800, 0x00000000,
+ 0x804, 0x00000001,
+ 0x808, 0x0000fc00,
+ 0x80c, 0x0000001c,
+ 0x810, 0x801010aa,
+ 0x814, 0x008514d0,
+ 0x818, 0x00000040,
+ 0x81c, 0x00000000,
+ 0x820, 0x00000004,
+ 0x824, 0x00690000,
+ 0x828, 0x00000004,
+ 0x82c, 0x00e90000,
+ 0x830, 0x00000004,
+ 0x834, 0x00690000,
+ 0x838, 0x00000004,
+ 0x83c, 0x00e90000,
+ 0x840, 0x00000000,
+ 0x844, 0x00000000,
+ 0x848, 0x00000000,
+ 0x84c, 0x00000000,
+ 0x850, 0x00000000,
+ 0x854, 0x00000000,
+ 0x858, 0x65a965a9,
+ 0x85c, 0x65a965a9,
+ 0x860, 0x001f0010,
+ 0x864, 0x007f0010,
+ 0x868, 0x001f0010,
+ 0x86c, 0x007f0010,
+ 0x870, 0x0f100f70,
+ 0x874, 0x0f100f70,
+ 0x878, 0x00000000,
+ 0x87c, 0x00000000,
+ 0x880, 0x6870e36c,
+ 0x884, 0xe3573600,
+ 0x888, 0x4260c340,
+ 0x88c, 0x0000ff00,
+ 0x890, 0x00000000,
+ 0x894, 0xfffffffe,
+ 0x898, 0x4c42382f,
+ 0x89c, 0x00656056,
+ 0x8b0, 0x00000000,
+ 0x8e0, 0x00000000,
+ 0x8e4, 0x00000000,
+ 0x900, 0x00000000,
+ 0x904, 0x00000023,
+ 0x908, 0x00000000,
+ 0x90c, 0x31121311,
+ 0xa00, 0x00d0c7d8,
+ 0xa04, 0x811f0008,
+ 0xa08, 0x80cd8300,
+ 0xa0c, 0x2e62740f,
+ 0xa10, 0x95009b78,
+ 0xa14, 0x11145008,
+ 0xa18, 0x00881117,
+ 0xa1c, 0x89140fa0,
+ 0xa20, 0x1a1b0000,
+ 0xa24, 0x090e1317,
+ 0xa28, 0x00000204,
+ 0xa2c, 0x00000000,
+ 0xc00, 0x00000040,
+ 0xc04, 0x00005433,
+ 0xc08, 0x000000e4,
+ 0xc0c, 0x6c6c6c6c,
+ 0xc10, 0x08800000,
+ 0xc14, 0x40000100,
+ 0xc18, 0x08000000,
+ 0xc1c, 0x40000100,
+ 0xc20, 0x08000000,
+ 0xc24, 0x40000100,
+ 0xc28, 0x08000000,
+ 0xc2c, 0x40000100,
+ 0xc30, 0x6de9ac44,
+ 0xc34, 0x465c52cd,
+ 0xc38, 0x497f5994,
+ 0xc3c, 0x0a969764,
+ 0xc40, 0x1f7c403f,
+ 0xc44, 0x000100b7,
+ 0xc48, 0xec020000,
+ 0xc4c, 0x00000300,
+ 0xc50, 0x69543420,
+ 0xc54, 0x433c0094,
+ 0xc58, 0x69543420,
+ 0xc5c, 0x433c0094,
+ 0xc60, 0x69543420,
+ 0xc64, 0x433c0094,
+ 0xc68, 0x69543420,
+ 0xc6c, 0x433c0094,
+ 0xc70, 0x2c7f000d,
+ 0xc74, 0x0186175b,
+ 0xc78, 0x0000001f,
+ 0xc7c, 0x00b91612,
+ 0xc80, 0x40000100,
+ 0xc84, 0x20000000,
+ 0xc88, 0x40000100,
+ 0xc8c, 0x20200000,
+ 0xc90, 0x40000100,
+ 0xc94, 0x00000000,
+ 0xc98, 0x40000100,
+ 0xc9c, 0x00000000,
+ 0xca0, 0x00492492,
+ 0xca4, 0x00000000,
+ 0xca8, 0x00000000,
+ 0xcac, 0x00000000,
+ 0xcb0, 0x00000000,
+ 0xcb4, 0x00000000,
+ 0xcb8, 0x00000000,
+ 0xcbc, 0x00492492,
+ 0xcc0, 0x00000000,
+ 0xcc4, 0x00000000,
+ 0xcc8, 0x00000000,
+ 0xccc, 0x00000000,
+ 0xcd0, 0x00000000,
+ 0xcd4, 0x00000000,
+ 0xcd8, 0x64b22427,
+ 0xcdc, 0x00766932,
+ 0xce0, 0x00222222,
+ 0xd00, 0x00000750,
+ 0xd04, 0x00000403,
+ 0xd08, 0x0000907f,
+ 0xd0c, 0x00000001,
+ 0xd10, 0xa0633333,
+ 0xd14, 0x33333c63,
+ 0xd18, 0x6a8f5b6b,
+ 0xd1c, 0x00000000,
+ 0xd20, 0x00000000,
+ 0xd24, 0x00000000,
+ 0xd28, 0x00000000,
+ 0xd2c, 0xcc979975,
+ 0xd30, 0x00000000,
+ 0xd34, 0x00000000,
+ 0xd38, 0x00000000,
+ 0xd3c, 0x00027293,
+ 0xd40, 0x00000000,
+ 0xd44, 0x00000000,
+ 0xd48, 0x00000000,
+ 0xd4c, 0x00000000,
+ 0xd50, 0x6437140a,
+ 0xd54, 0x024dbd02,
+ 0xd58, 0x00000000,
+ 0xd5c, 0x04032064,
+ 0xe00, 0x161a1a1a,
+ 0xe04, 0x12121416,
+ 0xe08, 0x00001800,
+ 0xe0c, 0x00000000,
+ 0xe10, 0x161a1a1a,
+ 0xe14, 0x12121416,
+ 0xe18, 0x161a1a1a,
+ 0xe1c, 0x12121416,
+};
+
+u32 Rtl8192UsbRadioA_Array[] = {
+ 0x019, 0x00000003,
+ 0x000, 0x000000bf,
+ 0x001, 0x00000ee0,
+ 0x002, 0x0000004c,
+ 0x003, 0x000007f1,
+ 0x004, 0x00000975,
+ 0x005, 0x00000c58,
+ 0x006, 0x00000ae6,
+ 0x007, 0x000000ca,
+ 0x008, 0x00000e1c,
+ 0x009, 0x000007f0,
+ 0x00a, 0x000009d0,
+ 0x00b, 0x000001ba,
+ 0x00c, 0x00000240,
+ 0x00e, 0x00000020,
+ 0x00f, 0x00000990,
+ 0x012, 0x00000806,
+ 0x014, 0x000005ab,
+ 0x015, 0x00000f80,
+ 0x016, 0x00000020,
+ 0x017, 0x00000597,
+ 0x018, 0x0000050a,
+ 0x01a, 0x00000f80,
+ 0x01b, 0x00000f5e,
+ 0x01c, 0x00000008,
+ 0x01d, 0x00000607,
+ 0x01e, 0x000006cc,
+ 0x01f, 0x00000000,
+ 0x020, 0x000001a5,
+ 0x01f, 0x00000001,
+ 0x020, 0x00000165,
+ 0x01f, 0x00000002,
+ 0x020, 0x000000c6,
+ 0x01f, 0x00000003,
+ 0x020, 0x00000086,
+ 0x01f, 0x00000004,
+ 0x020, 0x00000046,
+ 0x01f, 0x00000005,
+ 0x020, 0x000001e6,
+ 0x01f, 0x00000006,
+ 0x020, 0x000001a6,
+ 0x01f, 0x00000007,
+ 0x020, 0x00000166,
+ 0x01f, 0x00000008,
+ 0x020, 0x000000c7,
+ 0x01f, 0x00000009,
+ 0x020, 0x00000087,
+ 0x01f, 0x0000000a,
+ 0x020, 0x000000f7,
+ 0x01f, 0x0000000b,
+ 0x020, 0x000000d7,
+ 0x01f, 0x0000000c,
+ 0x020, 0x000000b7,
+ 0x01f, 0x0000000d,
+ 0x020, 0x00000097,
+ 0x01f, 0x0000000e,
+ 0x020, 0x00000077,
+ 0x01f, 0x0000000f,
+ 0x020, 0x00000057,
+ 0x01f, 0x00000010,
+ 0x020, 0x00000037,
+ 0x01f, 0x00000011,
+ 0x020, 0x000000fb,
+ 0x01f, 0x00000012,
+ 0x020, 0x000000db,
+ 0x01f, 0x00000013,
+ 0x020, 0x000000bb,
+ 0x01f, 0x00000014,
+ 0x020, 0x000000ff,
+ 0x01f, 0x00000015,
+ 0x020, 0x000000e3,
+ 0x01f, 0x00000016,
+ 0x020, 0x000000c3,
+ 0x01f, 0x00000017,
+ 0x020, 0x000000a3,
+ 0x01f, 0x00000018,
+ 0x020, 0x00000083,
+ 0x01f, 0x00000019,
+ 0x020, 0x00000063,
+ 0x01f, 0x0000001a,
+ 0x020, 0x00000043,
+ 0x01f, 0x0000001b,
+ 0x020, 0x00000023,
+ 0x01f, 0x0000001c,
+ 0x020, 0x00000003,
+ 0x01f, 0x0000001d,
+ 0x020, 0x000001e3,
+ 0x01f, 0x0000001e,
+ 0x020, 0x000001c3,
+ 0x01f, 0x0000001f,
+ 0x020, 0x000001a3,
+ 0x01f, 0x00000020,
+ 0x020, 0x00000183,
+ 0x01f, 0x00000021,
+ 0x020, 0x00000163,
+ 0x01f, 0x00000022,
+ 0x020, 0x00000143,
+ 0x01f, 0x00000023,
+ 0x020, 0x00000123,
+ 0x01f, 0x00000024,
+ 0x020, 0x00000103,
+ 0x023, 0x00000203,
+ 0x024, 0x00000200,
+ 0x00b, 0x000001ba,
+ 0x02c, 0x000003d7,
+ 0x02d, 0x00000ff0,
+ 0x000, 0x00000037,
+ 0x004, 0x00000160,
+ 0x007, 0x00000080,
+ 0x002, 0x0000088d,
+ 0x0fe, 0x00000000,
+ 0x0fe, 0x00000000,
+ 0x016, 0x00000200,
+ 0x016, 0x00000380,
+ 0x016, 0x00000020,
+ 0x016, 0x000001a0,
+ 0x000, 0x000000bf,
+ 0x00d, 0x0000001f,
+ 0x00d, 0x00000c9f,
+ 0x002, 0x0000004d,
+ 0x000, 0x00000cbf,
+ 0x004, 0x00000975,
+ 0x007, 0x00000700,
+};
+
+u32 Rtl8192UsbRadioB_Array[] = {
+ 0x019, 0x00000003,
+ 0x000, 0x000000bf,
+ 0x001, 0x000006e0,
+ 0x002, 0x0000004c,
+ 0x003, 0x000007f1,
+ 0x004, 0x00000975,
+ 0x005, 0x00000c58,
+ 0x006, 0x00000ae6,
+ 0x007, 0x000000ca,
+ 0x008, 0x00000e1c,
+ 0x000, 0x000000b7,
+ 0x00a, 0x00000850,
+ 0x000, 0x000000bf,
+ 0x00b, 0x000001ba,
+ 0x00c, 0x00000240,
+ 0x00e, 0x00000020,
+ 0x015, 0x00000f80,
+ 0x016, 0x00000020,
+ 0x017, 0x00000597,
+ 0x018, 0x0000050a,
+ 0x01a, 0x00000e00,
+ 0x01b, 0x00000f5e,
+ 0x01d, 0x00000607,
+ 0x01e, 0x000006cc,
+ 0x00b, 0x000001ba,
+ 0x023, 0x00000203,
+ 0x024, 0x00000200,
+ 0x000, 0x00000037,
+ 0x004, 0x00000160,
+ 0x016, 0x00000200,
+ 0x016, 0x00000380,
+ 0x016, 0x00000020,
+ 0x016, 0x000001a0,
+ 0x00d, 0x00000ccc,
+ 0x000, 0x000000bf,
+ 0x002, 0x0000004d,
+ 0x000, 0x00000cbf,
+ 0x004, 0x00000975,
+ 0x007, 0x00000700,
+};
+
+u32 Rtl8192UsbRadioC_Array[] = {
+ 0x0, };
+
+u32 Rtl8192UsbRadioD_Array[] = {
+ 0x0, };
+
+u32 Rtl8192UsbMACPHY_Array[] = {
+ 0x03c, 0xffff0000, 0x00000f0f,
+ 0x340, 0xffffffff, 0x161a1a1a,
+ 0x344, 0xffffffff, 0x12121416,
+ 0x348, 0x0000ffff, 0x00001818,
+ 0x12c, 0xffffffff, 0x04000802,
+ 0x318, 0x00000fff, 0x00000100,
+};
+
+u32 Rtl8192UsbMACPHY_Array_PG[] = {
+ 0x03c, 0xffff0000, 0x00000f0f,
+ 0xe00, 0xffffffff, 0x06090909,
+ 0xe04, 0xffffffff, 0x00030306,
+ 0xe08, 0x0000ff00, 0x00000000,
+ 0xe10, 0xffffffff, 0x0a0c0d0f,
+ 0xe14, 0xffffffff, 0x06070809,
+ 0xe18, 0xffffffff, 0x0a0c0d0f,
+ 0xe1c, 0xffffffff, 0x06070809,
+ 0x12c, 0xffffffff, 0x04000802,
+ 0x318, 0x00000fff, 0x00000800,
+};
+
+u32 Rtl8192UsbAGCTAB_Array[] = {
+ 0xc78, 0x7d000001,
+ 0xc78, 0x7d010001,
+ 0xc78, 0x7d020001,
+ 0xc78, 0x7d030001,
+ 0xc78, 0x7d040001,
+ 0xc78, 0x7d050001,
+ 0xc78, 0x7c060001,
+ 0xc78, 0x7b070001,
+ 0xc78, 0x7a080001,
+ 0xc78, 0x79090001,
+ 0xc78, 0x780a0001,
+ 0xc78, 0x770b0001,
+ 0xc78, 0x760c0001,
+ 0xc78, 0x750d0001,
+ 0xc78, 0x740e0001,
+ 0xc78, 0x730f0001,
+ 0xc78, 0x72100001,
+ 0xc78, 0x71110001,
+ 0xc78, 0x70120001,
+ 0xc78, 0x6f130001,
+ 0xc78, 0x6e140001,
+ 0xc78, 0x6d150001,
+ 0xc78, 0x6c160001,
+ 0xc78, 0x6b170001,
+ 0xc78, 0x6a180001,
+ 0xc78, 0x69190001,
+ 0xc78, 0x681a0001,
+ 0xc78, 0x671b0001,
+ 0xc78, 0x661c0001,
+ 0xc78, 0x651d0001,
+ 0xc78, 0x641e0001,
+ 0xc78, 0x491f0001,
+ 0xc78, 0x48200001,
+ 0xc78, 0x47210001,
+ 0xc78, 0x46220001,
+ 0xc78, 0x45230001,
+ 0xc78, 0x44240001,
+ 0xc78, 0x43250001,
+ 0xc78, 0x28260001,
+ 0xc78, 0x27270001,
+ 0xc78, 0x26280001,
+ 0xc78, 0x25290001,
+ 0xc78, 0x242a0001,
+ 0xc78, 0x232b0001,
+ 0xc78, 0x222c0001,
+ 0xc78, 0x212d0001,
+ 0xc78, 0x202e0001,
+ 0xc78, 0x0a2f0001,
+ 0xc78, 0x08300001,
+ 0xc78, 0x06310001,
+ 0xc78, 0x05320001,
+ 0xc78, 0x04330001,
+ 0xc78, 0x03340001,
+ 0xc78, 0x02350001,
+ 0xc78, 0x01360001,
+ 0xc78, 0x00370001,
+ 0xc78, 0x00380001,
+ 0xc78, 0x00390001,
+ 0xc78, 0x003a0001,
+ 0xc78, 0x003b0001,
+ 0xc78, 0x003c0001,
+ 0xc78, 0x003d0001,
+ 0xc78, 0x003e0001,
+ 0xc78, 0x003f0001,
+ 0xc78, 0x7d400001,
+ 0xc78, 0x7d410001,
+ 0xc78, 0x7d420001,
+ 0xc78, 0x7d430001,
+ 0xc78, 0x7d440001,
+ 0xc78, 0x7d450001,
+ 0xc78, 0x7c460001,
+ 0xc78, 0x7b470001,
+ 0xc78, 0x7a480001,
+ 0xc78, 0x79490001,
+ 0xc78, 0x784a0001,
+ 0xc78, 0x774b0001,
+ 0xc78, 0x764c0001,
+ 0xc78, 0x754d0001,
+ 0xc78, 0x744e0001,
+ 0xc78, 0x734f0001,
+ 0xc78, 0x72500001,
+ 0xc78, 0x71510001,
+ 0xc78, 0x70520001,
+ 0xc78, 0x6f530001,
+ 0xc78, 0x6e540001,
+ 0xc78, 0x6d550001,
+ 0xc78, 0x6c560001,
+ 0xc78, 0x6b570001,
+ 0xc78, 0x6a580001,
+ 0xc78, 0x69590001,
+ 0xc78, 0x685a0001,
+ 0xc78, 0x675b0001,
+ 0xc78, 0x665c0001,
+ 0xc78, 0x655d0001,
+ 0xc78, 0x645e0001,
+ 0xc78, 0x495f0001,
+ 0xc78, 0x48600001,
+ 0xc78, 0x47610001,
+ 0xc78, 0x46620001,
+ 0xc78, 0x45630001,
+ 0xc78, 0x44640001,
+ 0xc78, 0x43650001,
+ 0xc78, 0x28660001,
+ 0xc78, 0x27670001,
+ 0xc78, 0x26680001,
+ 0xc78, 0x25690001,
+ 0xc78, 0x246a0001,
+ 0xc78, 0x236b0001,
+ 0xc78, 0x226c0001,
+ 0xc78, 0x216d0001,
+ 0xc78, 0x206e0001,
+ 0xc78, 0x0a6f0001,
+ 0xc78, 0x08700001,
+ 0xc78, 0x06710001,
+ 0xc78, 0x05720001,
+ 0xc78, 0x04730001,
+ 0xc78, 0x03740001,
+ 0xc78, 0x02750001,
+ 0xc78, 0x01760001,
+ 0xc78, 0x00770001,
+ 0xc78, 0x00780001,
+ 0xc78, 0x00790001,
+ 0xc78, 0x007a0001,
+ 0xc78, 0x007b0001,
+ 0xc78, 0x007c0001,
+ 0xc78, 0x007d0001,
+ 0xc78, 0x007e0001,
+ 0xc78, 0x007f0001,
+ 0xc78, 0x2e00001e,
+ 0xc78, 0x2e01001e,
+ 0xc78, 0x2e02001e,
+ 0xc78, 0x2e03001e,
+ 0xc78, 0x2e04001e,
+ 0xc78, 0x2e05001e,
+ 0xc78, 0x3006001e,
+ 0xc78, 0x3407001e,
+ 0xc78, 0x3908001e,
+ 0xc78, 0x3c09001e,
+ 0xc78, 0x3f0a001e,
+ 0xc78, 0x420b001e,
+ 0xc78, 0x440c001e,
+ 0xc78, 0x450d001e,
+ 0xc78, 0x460e001e,
+ 0xc78, 0x460f001e,
+ 0xc78, 0x4710001e,
+ 0xc78, 0x4811001e,
+ 0xc78, 0x4912001e,
+ 0xc78, 0x4a13001e,
+ 0xc78, 0x4b14001e,
+ 0xc78, 0x4b15001e,
+ 0xc78, 0x4c16001e,
+ 0xc78, 0x4d17001e,
+ 0xc78, 0x4e18001e,
+ 0xc78, 0x4f19001e,
+ 0xc78, 0x4f1a001e,
+ 0xc78, 0x501b001e,
+ 0xc78, 0x511c001e,
+ 0xc78, 0x521d001e,
+ 0xc78, 0x521e001e,
+ 0xc78, 0x531f001e,
+ 0xc78, 0x5320001e,
+ 0xc78, 0x5421001e,
+ 0xc78, 0x5522001e,
+ 0xc78, 0x5523001e,
+ 0xc78, 0x5624001e,
+ 0xc78, 0x5725001e,
+ 0xc78, 0x5726001e,
+ 0xc78, 0x5827001e,
+ 0xc78, 0x5828001e,
+ 0xc78, 0x5929001e,
+ 0xc78, 0x592a001e,
+ 0xc78, 0x5a2b001e,
+ 0xc78, 0x5b2c001e,
+ 0xc78, 0x5c2d001e,
+ 0xc78, 0x5c2e001e,
+ 0xc78, 0x5d2f001e,
+ 0xc78, 0x5e30001e,
+ 0xc78, 0x5f31001e,
+ 0xc78, 0x6032001e,
+ 0xc78, 0x6033001e,
+ 0xc78, 0x6134001e,
+ 0xc78, 0x6235001e,
+ 0xc78, 0x6336001e,
+ 0xc78, 0x6437001e,
+ 0xc78, 0x6438001e,
+ 0xc78, 0x6539001e,
+ 0xc78, 0x663a001e,
+ 0xc78, 0x673b001e,
+ 0xc78, 0x673c001e,
+ 0xc78, 0x683d001e,
+ 0xc78, 0x693e001e,
+ 0xc78, 0x6a3f001e,
+};
diff --git a/drivers/staging/rtl8192u/r819xU_firmware_img.h b/drivers/staging/rtl8192u/r819xU_firmware_img.h
new file mode 100644
index 000000000..18d0a6b5c
--- /dev/null
+++ b/drivers/staging/rtl8192u/r819xU_firmware_img.h
@@ -0,0 +1,28 @@
+#ifndef IMG_H
+#define IMG_H
+
+#define MACPHY_Array_PGLength 30
+#define PHY_REG_1T2RArrayLength 296
+#define AGCTAB_ArrayLength 384
+#define MACPHY_ArrayLength 18
+
+#define RadioA_ArrayLength 246
+#define RadioB_ArrayLength 78
+#define RadioC_ArrayLength 1
+#define RadioD_ArrayLength 1
+#define PHY_REGArrayLength 1
+
+
+extern u32 Rtl8192UsbPHY_REGArray[];
+extern u32 Rtl8192UsbPHY_REG_1T2RArray[];
+extern u32 Rtl8192UsbRadioA_Array[];
+extern u32 Rtl8192UsbRadioB_Array[];
+extern u32 Rtl8192UsbRadioC_Array[];
+extern u32 Rtl8192UsbRadioD_Array[];
+extern u32 Rtl8192UsbMACPHY_Array[];
+extern u32 Rtl8192UsbMACPHY_Array_PG[];
+extern u32 Rtl8192UsbAGCTAB_Array[];
+
+
+
+#endif
diff --git a/drivers/staging/rtl8192u/r819xU_phy.c b/drivers/staging/rtl8192u/r819xU_phy.c
new file mode 100644
index 000000000..e5dbaca9e
--- /dev/null
+++ b/drivers/staging/rtl8192u/r819xU_phy.c
@@ -0,0 +1,1795 @@
+#include "r8192U.h"
+#include "r8192U_hw.h"
+#include "r819xU_phy.h"
+#include "r819xU_phyreg.h"
+#include "r8190_rtl8256.h"
+#include "r8192U_dm.h"
+#include "r819xU_firmware_img.h"
+
+#include "dot11d.h"
+#include <linux/bitops.h>
+
+static u32 RF_CHANNEL_TABLE_ZEBRA[] = {
+ 0,
+ 0x085c, /* 2412 1 */
+ 0x08dc, /* 2417 2 */
+ 0x095c, /* 2422 3 */
+ 0x09dc, /* 2427 4 */
+ 0x0a5c, /* 2432 5 */
+ 0x0adc, /* 2437 6 */
+ 0x0b5c, /* 2442 7 */
+ 0x0bdc, /* 2447 8 */
+ 0x0c5c, /* 2452 9 */
+ 0x0cdc, /* 2457 10 */
+ 0x0d5c, /* 2462 11 */
+ 0x0ddc, /* 2467 12 */
+ 0x0e5c, /* 2472 13 */
+ 0x0f72, /* 2484 */
+};
+
+
+#define rtl819XPHY_REG_1T2RArray Rtl8192UsbPHY_REG_1T2RArray
+#define rtl819XMACPHY_Array_PG Rtl8192UsbMACPHY_Array_PG
+#define rtl819XMACPHY_Array Rtl8192UsbMACPHY_Array
+#define rtl819XRadioA_Array Rtl8192UsbRadioA_Array
+#define rtl819XRadioB_Array Rtl8192UsbRadioB_Array
+#define rtl819XRadioC_Array Rtl8192UsbRadioC_Array
+#define rtl819XRadioD_Array Rtl8192UsbRadioD_Array
+#define rtl819XAGCTAB_Array Rtl8192UsbAGCTAB_Array
+
+/******************************************************************************
+ * function: This function reads BB parameters from header file we generate,
+ * and does register read/write
+ * input: u32 bitmask //taget bit pos in the addr to be modified
+ * output: none
+ * return: u32 return the shift bit position of the mask
+ ******************************************************************************/
+static u32 rtl8192_CalculateBitShift(u32 bitmask)
+{
+ u32 i;
+
+ i = ffs(bitmask) - 1;
+ return i;
+}
+
+/******************************************************************************
+ * function: This function checks different RF type to execute legal judgement.
+ * If RF Path is illegal, we will return false.
+ * input: net_device *dev
+ * u32 eRFPath
+ * output: none
+ * return: 0(illegal, false), 1(legal, true)
+ *****************************************************************************/
+u8 rtl8192_phy_CheckIsLegalRFPath(struct net_device *dev, u32 eRFPath)
+{
+ u8 ret = 1;
+ struct r8192_priv *priv = ieee80211_priv(dev);
+
+ if (priv->rf_type == RF_2T4R) {
+ ret = 0;
+ } else if (priv->rf_type == RF_1T2R) {
+ if (eRFPath == RF90_PATH_A || eRFPath == RF90_PATH_B)
+ ret = 1;
+ else if (eRFPath == RF90_PATH_C || eRFPath == RF90_PATH_D)
+ ret = 0;
+ }
+ return ret;
+}
+
+/******************************************************************************
+ * function: This function sets specific bits to BB register
+ * input: net_device *dev
+ * u32 reg_addr //target addr to be modified
+ * u32 bitmask //taget bit pos to be modified
+ * u32 data //value to be write
+ * output: none
+ * return: none
+ * notice:
+ ******************************************************************************/
+void rtl8192_setBBreg(struct net_device *dev, u32 reg_addr, u32 bitmask,
+ u32 data)
+{
+
+ u32 reg, bitshift;
+
+ if (bitmask != bMaskDWord) {
+ read_nic_dword(dev, reg_addr, &reg);
+ bitshift = rtl8192_CalculateBitShift(bitmask);
+ reg &= ~bitmask;
+ reg |= data << bitshift;
+ write_nic_dword(dev, reg_addr, reg);
+ } else {
+ write_nic_dword(dev, reg_addr, data);
+ }
+}
+
+/******************************************************************************
+ * function: This function reads specific bits from BB register
+ * input: net_device *dev
+ * u32 reg_addr //target addr to be readback
+ * u32 bitmask //taget bit pos to be readback
+ * output: none
+ * return: u32 data //the readback register value
+ * notice:
+ ******************************************************************************/
+u32 rtl8192_QueryBBReg(struct net_device *dev, u32 reg_addr, u32 bitmask)
+{
+ u32 reg, bitshift;
+
+ read_nic_dword(dev, reg_addr, &reg);
+ bitshift = rtl8192_CalculateBitShift(bitmask);
+
+ return (reg & bitmask) >> bitshift;
+}
+
+static u32 phy_FwRFSerialRead(struct net_device *dev, RF90_RADIO_PATH_E eRFPath,
+ u32 offset);
+
+static void phy_FwRFSerialWrite(struct net_device *dev,
+ RF90_RADIO_PATH_E eRFPath, u32 offset,
+ u32 data);
+
+/******************************************************************************
+ * function: This function reads register from RF chip
+ * input: net_device *dev
+ * RF90_RADIO_PATH_E eRFPath //radio path of A/B/C/D
+ * u32 offset //target address to be read
+ * output: none
+ * return: u32 readback value
+ * notice: There are three types of serial operations:
+ * (1) Software serial write.
+ * (2)Hardware LSSI-Low Speed Serial Interface.
+ * (3)Hardware HSSI-High speed serial write.
+ * Driver here need to implement (1) and (2)
+ * ---need more spec for this information.
+ ******************************************************************************/
+static u32 rtl8192_phy_RFSerialRead(struct net_device *dev,
+ RF90_RADIO_PATH_E eRFPath, u32 offset)
+{
+ struct r8192_priv *priv = ieee80211_priv(dev);
+ u32 ret = 0;
+ u32 new_offset = 0;
+ BB_REGISTER_DEFINITION_T *pPhyReg = &priv->PHYRegDef[eRFPath];
+
+ rtl8192_setBBreg(dev, pPhyReg->rfLSSIReadBack, bLSSIReadBackData, 0);
+ /* Make sure RF register offset is correct */
+ offset &= 0x3f;
+
+ /* Switch page for 8256 RF IC */
+ if (priv->rf_chip == RF_8256) {
+ if (offset >= 31) {
+ priv->RfReg0Value[eRFPath] |= 0x140;
+ /* Switch to Reg_Mode2 for Reg 31-45 */
+ rtl8192_setBBreg(dev, pPhyReg->rf3wireOffset,
+ bMaskDWord,
+ priv->RfReg0Value[eRFPath]<<16);
+ /* Modify offset */
+ new_offset = offset - 30;
+ } else if (offset >= 16) {
+ priv->RfReg0Value[eRFPath] |= 0x100;
+ priv->RfReg0Value[eRFPath] &= (~0x40);
+ /* Switch to Reg_Mode1 for Reg16-30 */
+ rtl8192_setBBreg(dev, pPhyReg->rf3wireOffset,
+ bMaskDWord,
+ priv->RfReg0Value[eRFPath]<<16);
+
+ new_offset = offset - 15;
+ } else {
+ new_offset = offset;
+ }
+ } else {
+ RT_TRACE((COMP_PHY|COMP_ERR),
+ "check RF type here, need to be 8256\n");
+ new_offset = offset;
+ }
+ /* Put desired read addr to LSSI control Register */
+ rtl8192_setBBreg(dev, pPhyReg->rfHSSIPara2, bLSSIReadAddress,
+ new_offset);
+ /* Issue a posedge trigger */
+ rtl8192_setBBreg(dev, pPhyReg->rfHSSIPara2, bLSSIReadEdge, 0x0);
+ rtl8192_setBBreg(dev, pPhyReg->rfHSSIPara2, bLSSIReadEdge, 0x1);
+
+
+ /* TODO: we should not delay such a long time. Ask for help from SD3 */
+ usleep_range(1000, 1000);
+
+ ret = rtl8192_QueryBBReg(dev, pPhyReg->rfLSSIReadBack,
+ bLSSIReadBackData);
+
+
+ /* Switch back to Reg_Mode0 */
+ if (priv->rf_chip == RF_8256) {
+ priv->RfReg0Value[eRFPath] &= 0xebf;
+
+ rtl8192_setBBreg(dev, pPhyReg->rf3wireOffset, bMaskDWord,
+ priv->RfReg0Value[eRFPath] << 16);
+ }
+
+ return ret;
+}
+
+/******************************************************************************
+ * function: This function writes data to RF register
+ * input: net_device *dev
+ * RF90_RADIO_PATH_E eRFPath //radio path of A/B/C/D
+ * u32 offset //target address to be written
+ * u32 data //the new register data to be written
+ * output: none
+ * return: none
+ * notice: For RF8256 only.
+ * ===========================================================================
+ * Reg Mode RegCTL[1] RegCTL[0] Note
+ * (Reg00[12]) (Reg00[10])
+ * ===========================================================================
+ * Reg_Mode0 0 x Reg 0 ~ 15(0x0 ~ 0xf)
+ * ---------------------------------------------------------------------------
+ * Reg_Mode1 1 0 Reg 16 ~ 30(0x1 ~ 0xf)
+ * ---------------------------------------------------------------------------
+ * Reg_Mode2 1 1 Reg 31 ~ 45(0x1 ~ 0xf)
+ * ---------------------------------------------------------------------------
+ *****************************************************************************/
+static void rtl8192_phy_RFSerialWrite(struct net_device *dev,
+ RF90_RADIO_PATH_E eRFPath, u32 offset,
+ u32 data)
+{
+ struct r8192_priv *priv = ieee80211_priv(dev);
+ u32 DataAndAddr = 0, new_offset = 0;
+ BB_REGISTER_DEFINITION_T *pPhyReg = &priv->PHYRegDef[eRFPath];
+
+ offset &= 0x3f;
+ if (priv->rf_chip == RF_8256) {
+
+ if (offset >= 31) {
+ priv->RfReg0Value[eRFPath] |= 0x140;
+ rtl8192_setBBreg(dev, pPhyReg->rf3wireOffset,
+ bMaskDWord,
+ priv->RfReg0Value[eRFPath] << 16);
+ new_offset = offset - 30;
+ } else if (offset >= 16) {
+ priv->RfReg0Value[eRFPath] |= 0x100;
+ priv->RfReg0Value[eRFPath] &= (~0x40);
+ rtl8192_setBBreg(dev, pPhyReg->rf3wireOffset,
+ bMaskDWord,
+ priv->RfReg0Value[eRFPath]<<16);
+ new_offset = offset - 15;
+ } else {
+ new_offset = offset;
+ }
+ } else {
+ RT_TRACE((COMP_PHY|COMP_ERR),
+ "check RF type here, need to be 8256\n");
+ new_offset = offset;
+ }
+
+ /* Put write addr in [5:0] and write data in [31:16] */
+ DataAndAddr = (data<<16) | (new_offset&0x3f);
+
+ /* Write operation */
+ rtl8192_setBBreg(dev, pPhyReg->rf3wireOffset, bMaskDWord, DataAndAddr);
+
+
+ if (offset == 0x0)
+ priv->RfReg0Value[eRFPath] = data;
+
+ /* Switch back to Reg_Mode0 */
+ if (priv->rf_chip == RF_8256) {
+ if (offset != 0) {
+ priv->RfReg0Value[eRFPath] &= 0xebf;
+ rtl8192_setBBreg(dev, pPhyReg->rf3wireOffset,
+ bMaskDWord,
+ priv->RfReg0Value[eRFPath] << 16);
+ }
+ }
+}
+
+/******************************************************************************
+ * function: This function set specific bits to RF register
+ * input: net_device dev
+ * RF90_RADIO_PATH_E eRFPath //radio path of A/B/C/D
+ * u32 reg_addr //target addr to be modified
+ * u32 bitmask //taget bit pos to be modified
+ * u32 data //value to be written
+ * output: none
+ * return: none
+ * notice:
+ *****************************************************************************/
+void rtl8192_phy_SetRFReg(struct net_device *dev, RF90_RADIO_PATH_E eRFPath,
+ u32 reg_addr, u32 bitmask, u32 data)
+{
+ struct r8192_priv *priv = ieee80211_priv(dev);
+ u32 reg, bitshift;
+
+ if (!rtl8192_phy_CheckIsLegalRFPath(dev, eRFPath))
+ return;
+
+ if (priv->Rf_Mode == RF_OP_By_FW) {
+ if (bitmask != bMask12Bits) {
+ /* RF data is 12 bits only */
+ reg = phy_FwRFSerialRead(dev, eRFPath, reg_addr);
+ bitshift = rtl8192_CalculateBitShift(bitmask);
+ reg &= ~bitmask;
+ reg |= data << bitshift;
+
+ phy_FwRFSerialWrite(dev, eRFPath, reg_addr, reg);
+ } else {
+ phy_FwRFSerialWrite(dev, eRFPath, reg_addr, data);
+ }
+
+ udelay(200);
+
+ } else {
+ if (bitmask != bMask12Bits) {
+ /* RF data is 12 bits only */
+ reg = rtl8192_phy_RFSerialRead(dev, eRFPath, reg_addr);
+ bitshift = rtl8192_CalculateBitShift(bitmask);
+ reg &= ~bitmask;
+ reg |= data << bitshift;
+
+ rtl8192_phy_RFSerialWrite(dev, eRFPath, reg_addr, reg);
+ } else {
+ rtl8192_phy_RFSerialWrite(dev, eRFPath, reg_addr, data);
+ }
+ }
+}
+
+/******************************************************************************
+ * function: This function reads specific bits from RF register
+ * input: net_device *dev
+ * u32 reg_addr //target addr to be readback
+ * u32 bitmask //taget bit pos to be readback
+ * output: none
+ * return: u32 data //the readback register value
+ * notice:
+ *****************************************************************************/
+u32 rtl8192_phy_QueryRFReg(struct net_device *dev, RF90_RADIO_PATH_E eRFPath,
+ u32 reg_addr, u32 bitmask)
+{
+ u32 reg, bitshift;
+ struct r8192_priv *priv = ieee80211_priv(dev);
+
+
+ if (!rtl8192_phy_CheckIsLegalRFPath(dev, eRFPath))
+ return 0;
+ if (priv->Rf_Mode == RF_OP_By_FW) {
+ reg = phy_FwRFSerialRead(dev, eRFPath, reg_addr);
+ udelay(200);
+ } else {
+ reg = rtl8192_phy_RFSerialRead(dev, eRFPath, reg_addr);
+ }
+ bitshift = rtl8192_CalculateBitShift(bitmask);
+ reg = (reg & bitmask) >> bitshift;
+ return reg;
+
+}
+
+/******************************************************************************
+ * function: We support firmware to execute RF-R/W.
+ * input: net_device *dev
+ * RF90_RADIO_PATH_E eRFPath
+ * u32 offset
+ * output: none
+ * return: u32
+ * notice:
+ ****************************************************************************/
+static u32 phy_FwRFSerialRead(struct net_device *dev, RF90_RADIO_PATH_E eRFPath,
+ u32 offset)
+{
+ u32 reg = 0;
+ u32 data = 0;
+ u8 time = 0;
+ u32 tmp;
+
+ /* Firmware RF Write control.
+ * We can not execute the scheme in the initial step.
+ * Otherwise, RF-R/W will waste much time.
+ * This is only for site survey. */
+ /* 1. Read operation need not insert data. bit 0-11 */
+ /* 2. Write RF register address. bit 12-19 */
+ data |= ((offset&0xFF)<<12);
+ /* 3. Write RF path. bit 20-21 */
+ data |= ((eRFPath&0x3)<<20);
+ /* 4. Set RF read indicator. bit 22=0 */
+ /* 5. Trigger Fw to operate the command. bit 31 */
+ data |= 0x80000000;
+ /* 6. We can not execute read operation if bit 31 is 1. */
+ read_nic_dword(dev, QPNR, &tmp);
+ while (tmp & 0x80000000) {
+ /* If FW can not finish RF-R/W for more than ?? times.
+ We must reset FW. */
+ if (time++ < 100) {
+ udelay(10);
+ read_nic_dword(dev, QPNR, &tmp);
+ } else {
+ break;
+ }
+ }
+ /* 7. Execute read operation. */
+ write_nic_dword(dev, QPNR, data);
+ /* 8. Check if firmware send back RF content. */
+ read_nic_dword(dev, QPNR, &tmp);
+ while (tmp & 0x80000000) {
+ /* If FW can not finish RF-R/W for more than ?? times.
+ We must reset FW. */
+ if (time++ < 100) {
+ udelay(10);
+ read_nic_dword(dev, QPNR, &tmp);
+ } else {
+ return 0;
+ }
+ }
+ read_nic_dword(dev, RF_DATA, &reg);
+
+ return reg;
+}
+
+/******************************************************************************
+ * function: We support firmware to execute RF-R/W.
+ * input: net_device *dev
+ * RF90_RADIO_PATH_E eRFPath
+ * u32 offset
+ * u32 data
+ * output: none
+ * return: none
+ * notice:
+ ****************************************************************************/
+static void phy_FwRFSerialWrite(struct net_device *dev,
+ RF90_RADIO_PATH_E eRFPath, u32 offset, u32 data)
+{
+ u8 time = 0;
+ u32 tmp;
+
+ /* Firmware RF Write control.
+ * We can not execute the scheme in the initial step.
+ * Otherwise, RF-R/W will waste much time.
+ * This is only for site survey. */
+
+ /* 1. Set driver write bit and 12 bit data. bit 0-11 */
+ /* 2. Write RF register address. bit 12-19 */
+ data |= ((offset&0xFF)<<12);
+ /* 3. Write RF path. bit 20-21 */
+ data |= ((eRFPath&0x3)<<20);
+ /* 4. Set RF write indicator. bit 22=1 */
+ data |= 0x400000;
+ /* 5. Trigger Fw to operate the command. bit 31=1 */
+ data |= 0x80000000;
+
+ /* 6. Write operation. We can not write if bit 31 is 1. */
+ read_nic_dword(dev, QPNR, &tmp);
+ while (tmp & 0x80000000) {
+ /* If FW can not finish RF-R/W for more than ?? times.
+ We must reset FW. */
+ if (time++ < 100) {
+ udelay(10);
+ read_nic_dword(dev, QPNR, &tmp);
+ } else {
+ break;
+ }
+ }
+ /* 7. No matter check bit. We always force the write.
+ Because FW will not accept the command. */
+ write_nic_dword(dev, QPNR, data);
+ /* According to test, we must delay 20us to wait firmware
+ to finish RF write operation. */
+ /* We support delay in firmware side now. */
+}
+
+/******************************************************************************
+ * function: This function reads BB parameters from header file we generate,
+ * and do register read/write
+ * input: net_device *dev
+ * output: none
+ * return: none
+ * notice: BB parameters may change all the time, so please make
+ * sure it has been synced with the newest.
+ *****************************************************************************/
+void rtl8192_phy_configmac(struct net_device *dev)
+{
+ u32 dwArrayLen = 0, i;
+ u32 *pdwArray = NULL;
+ struct r8192_priv *priv = ieee80211_priv(dev);
+
+ if (priv->btxpowerdata_readfromEEPORM) {
+ RT_TRACE(COMP_PHY, "Rtl819XMACPHY_Array_PG\n");
+ dwArrayLen = MACPHY_Array_PGLength;
+ pdwArray = rtl819XMACPHY_Array_PG;
+
+ } else {
+ RT_TRACE(COMP_PHY, "Rtl819XMACPHY_Array\n");
+ dwArrayLen = MACPHY_ArrayLength;
+ pdwArray = rtl819XMACPHY_Array;
+ }
+ for (i = 0; i < dwArrayLen; i = i+3) {
+ if (pdwArray[i] == 0x318)
+ pdwArray[i+2] = 0x00000800;
+
+ RT_TRACE(COMP_DBG,
+ "Rtl8190MACPHY_Array[0]=%x Rtl8190MACPHY_Array[1]=%x Rtl8190MACPHY_Array[2]=%x\n",
+ pdwArray[i], pdwArray[i+1], pdwArray[i+2]);
+ rtl8192_setBBreg(dev, pdwArray[i], pdwArray[i+1],
+ pdwArray[i+2]);
+ }
+}
+
+/******************************************************************************
+ * function: This function does dirty work
+ * input: net_device *dev
+ * u8 ConfigType
+ * output: none
+ * return: none
+ * notice: BB parameters may change all the time, so please make
+ * sure it has been synced with the newest.
+ *****************************************************************************/
+void rtl8192_phyConfigBB(struct net_device *dev, u8 ConfigType)
+{
+ u32 i;
+
+#ifdef TO_DO_LIST
+ u32 *rtl8192PhyRegArrayTable = NULL, *rtl8192AgcTabArrayTable = NULL;
+
+ if (Adapter->bInHctTest) {
+ PHY_REGArrayLen = PHY_REGArrayLengthDTM;
+ AGCTAB_ArrayLen = AGCTAB_ArrayLengthDTM;
+ Rtl8190PHY_REGArray_Table = Rtl819XPHY_REGArrayDTM;
+ Rtl8190AGCTAB_Array_Table = Rtl819XAGCTAB_ArrayDTM;
+ }
+#endif
+ if (ConfigType == BaseBand_Config_PHY_REG) {
+ for (i = 0; i < PHY_REG_1T2RArrayLength; i += 2) {
+ rtl8192_setBBreg(dev, rtl819XPHY_REG_1T2RArray[i],
+ bMaskDWord,
+ rtl819XPHY_REG_1T2RArray[i+1]);
+ RT_TRACE(COMP_DBG,
+ "i: %x, Rtl819xUsbPHY_REGArray[0]=%x Rtl819xUsbPHY_REGArray[1]=%x\n",
+ i, rtl819XPHY_REG_1T2RArray[i],
+ rtl819XPHY_REG_1T2RArray[i+1]);
+ }
+ } else if (ConfigType == BaseBand_Config_AGC_TAB) {
+ for (i = 0; i < AGCTAB_ArrayLength; i += 2) {
+ rtl8192_setBBreg(dev, rtl819XAGCTAB_Array[i],
+ bMaskDWord, rtl819XAGCTAB_Array[i+1]);
+ RT_TRACE(COMP_DBG,
+ "i: %x, rtl819XAGCTAB_Array[0]=%x rtl819XAGCTAB_Array[1]=%x\n",
+ i, rtl819XAGCTAB_Array[i],
+ rtl819XAGCTAB_Array[i+1]);
+ }
+ }
+}
+
+/******************************************************************************
+ * function: This function initializes Register definition offset for
+ * Radio Path A/B/C/D
+ * input: net_device *dev
+ * output: none
+ * return: none
+ * notice: Initialization value here is constant and it should never
+ * be changed
+ *****************************************************************************/
+static void rtl8192_InitBBRFRegDef(struct net_device *dev)
+{
+ struct r8192_priv *priv = ieee80211_priv(dev);
+
+ /* RF Interface Software Control */
+ /* 16 LSBs if read 32-bit from 0x870 */
+ priv->PHYRegDef[RF90_PATH_A].rfintfs = rFPGA0_XAB_RFInterfaceSW;
+ /* 16 MSBs if read 32-bit from 0x870 (16-bit for 0x872) */
+ priv->PHYRegDef[RF90_PATH_B].rfintfs = rFPGA0_XAB_RFInterfaceSW;
+ /* 16 LSBs if read 32-bit from 0x874 */
+ priv->PHYRegDef[RF90_PATH_C].rfintfs = rFPGA0_XCD_RFInterfaceSW;
+ /* 16 MSBs if read 32-bit from 0x874 (16-bit for 0x876) */
+ priv->PHYRegDef[RF90_PATH_D].rfintfs = rFPGA0_XCD_RFInterfaceSW;
+
+ /* RF Interface Readback Value */
+ /* 16 LSBs if read 32-bit from 0x8E0 */
+ priv->PHYRegDef[RF90_PATH_A].rfintfi = rFPGA0_XAB_RFInterfaceRB;
+ /* 16 MSBs if read 32-bit from 0x8E0 (16-bit for 0x8E2) */
+ priv->PHYRegDef[RF90_PATH_B].rfintfi = rFPGA0_XAB_RFInterfaceRB;
+ /* 16 LSBs if read 32-bit from 0x8E4 */
+ priv->PHYRegDef[RF90_PATH_C].rfintfi = rFPGA0_XCD_RFInterfaceRB;
+ /* 16 MSBs if read 32-bit from 0x8E4 (16-bit for 0x8E6) */
+ priv->PHYRegDef[RF90_PATH_D].rfintfi = rFPGA0_XCD_RFInterfaceRB;
+
+ /* RF Interface Output (and Enable) */
+ /* 16 LSBs if read 32-bit from 0x860 */
+ priv->PHYRegDef[RF90_PATH_A].rfintfo = rFPGA0_XA_RFInterfaceOE;
+ /* 16 LSBs if read 32-bit from 0x864 */
+ priv->PHYRegDef[RF90_PATH_B].rfintfo = rFPGA0_XB_RFInterfaceOE;
+ /* 16 LSBs if read 32-bit from 0x868 */
+ priv->PHYRegDef[RF90_PATH_C].rfintfo = rFPGA0_XC_RFInterfaceOE;
+ /* 16 LSBs if read 32-bit from 0x86C */
+ priv->PHYRegDef[RF90_PATH_D].rfintfo = rFPGA0_XD_RFInterfaceOE;
+
+ /* RF Interface (Output and) Enable */
+ /* 16 MSBs if read 32-bit from 0x860 (16-bit for 0x862) */
+ priv->PHYRegDef[RF90_PATH_A].rfintfe = rFPGA0_XA_RFInterfaceOE;
+ /* 16 MSBs if read 32-bit from 0x864 (16-bit for 0x866) */
+ priv->PHYRegDef[RF90_PATH_B].rfintfe = rFPGA0_XB_RFInterfaceOE;
+ /* 16 MSBs if read 32-bit from 0x86A (16-bit for 0x86A) */
+ priv->PHYRegDef[RF90_PATH_C].rfintfe = rFPGA0_XC_RFInterfaceOE;
+ /* 16 MSBs if read 32-bit from 0x86C (16-bit for 0x86E) */
+ priv->PHYRegDef[RF90_PATH_D].rfintfe = rFPGA0_XD_RFInterfaceOE;
+
+ /* Addr of LSSI. Write RF register by driver */
+ priv->PHYRegDef[RF90_PATH_A].rf3wireOffset = rFPGA0_XA_LSSIParameter;
+ priv->PHYRegDef[RF90_PATH_B].rf3wireOffset = rFPGA0_XB_LSSIParameter;
+ priv->PHYRegDef[RF90_PATH_C].rf3wireOffset = rFPGA0_XC_LSSIParameter;
+ priv->PHYRegDef[RF90_PATH_D].rf3wireOffset = rFPGA0_XD_LSSIParameter;
+
+ /* RF parameter */
+ /* BB Band Select */
+ priv->PHYRegDef[RF90_PATH_A].rfLSSI_Select = rFPGA0_XAB_RFParameter;
+ priv->PHYRegDef[RF90_PATH_B].rfLSSI_Select = rFPGA0_XAB_RFParameter;
+ priv->PHYRegDef[RF90_PATH_C].rfLSSI_Select = rFPGA0_XCD_RFParameter;
+ priv->PHYRegDef[RF90_PATH_D].rfLSSI_Select = rFPGA0_XCD_RFParameter;
+
+ /* Tx AGC Gain Stage (same for all path. Should we remove this?) */
+ priv->PHYRegDef[RF90_PATH_A].rfTxGainStage = rFPGA0_TxGainStage;
+ priv->PHYRegDef[RF90_PATH_B].rfTxGainStage = rFPGA0_TxGainStage;
+ priv->PHYRegDef[RF90_PATH_C].rfTxGainStage = rFPGA0_TxGainStage;
+ priv->PHYRegDef[RF90_PATH_D].rfTxGainStage = rFPGA0_TxGainStage;
+
+ /* Tranceiver A~D HSSI Parameter-1 */
+ /* wire control parameter1 */
+ priv->PHYRegDef[RF90_PATH_A].rfHSSIPara1 = rFPGA0_XA_HSSIParameter1;
+ priv->PHYRegDef[RF90_PATH_B].rfHSSIPara1 = rFPGA0_XB_HSSIParameter1;
+ priv->PHYRegDef[RF90_PATH_C].rfHSSIPara1 = rFPGA0_XC_HSSIParameter1;
+ priv->PHYRegDef[RF90_PATH_D].rfHSSIPara1 = rFPGA0_XD_HSSIParameter1;
+
+ /* Tranceiver A~D HSSI Parameter-2 */
+ /* wire control parameter2 */
+ priv->PHYRegDef[RF90_PATH_A].rfHSSIPara2 = rFPGA0_XA_HSSIParameter2;
+ priv->PHYRegDef[RF90_PATH_B].rfHSSIPara2 = rFPGA0_XB_HSSIParameter2;
+ priv->PHYRegDef[RF90_PATH_C].rfHSSIPara2 = rFPGA0_XC_HSSIParameter2;
+ priv->PHYRegDef[RF90_PATH_D].rfHSSIPara2 = rFPGA0_XD_HSSIParameter2;
+
+ /* RF Switch Control */
+ /* TR/Ant switch control */
+ priv->PHYRegDef[RF90_PATH_A].rfSwitchControl = rFPGA0_XAB_SwitchControl;
+ priv->PHYRegDef[RF90_PATH_B].rfSwitchControl = rFPGA0_XAB_SwitchControl;
+ priv->PHYRegDef[RF90_PATH_C].rfSwitchControl = rFPGA0_XCD_SwitchControl;
+ priv->PHYRegDef[RF90_PATH_D].rfSwitchControl = rFPGA0_XCD_SwitchControl;
+
+ /* AGC control 1 */
+ priv->PHYRegDef[RF90_PATH_A].rfAGCControl1 = rOFDM0_XAAGCCore1;
+ priv->PHYRegDef[RF90_PATH_B].rfAGCControl1 = rOFDM0_XBAGCCore1;
+ priv->PHYRegDef[RF90_PATH_C].rfAGCControl1 = rOFDM0_XCAGCCore1;
+ priv->PHYRegDef[RF90_PATH_D].rfAGCControl1 = rOFDM0_XDAGCCore1;
+
+ /* AGC control 2 */
+ priv->PHYRegDef[RF90_PATH_A].rfAGCControl2 = rOFDM0_XAAGCCore2;
+ priv->PHYRegDef[RF90_PATH_B].rfAGCControl2 = rOFDM0_XBAGCCore2;
+ priv->PHYRegDef[RF90_PATH_C].rfAGCControl2 = rOFDM0_XCAGCCore2;
+ priv->PHYRegDef[RF90_PATH_D].rfAGCControl2 = rOFDM0_XDAGCCore2;
+
+ /* RX AFE control 1 */
+ priv->PHYRegDef[RF90_PATH_A].rfRxIQImbalance = rOFDM0_XARxIQImbalance;
+ priv->PHYRegDef[RF90_PATH_B].rfRxIQImbalance = rOFDM0_XBRxIQImbalance;
+ priv->PHYRegDef[RF90_PATH_C].rfRxIQImbalance = rOFDM0_XCRxIQImbalance;
+ priv->PHYRegDef[RF90_PATH_D].rfRxIQImbalance = rOFDM0_XDRxIQImbalance;
+
+ /* RX AFE control 1 */
+ priv->PHYRegDef[RF90_PATH_A].rfRxAFE = rOFDM0_XARxAFE;
+ priv->PHYRegDef[RF90_PATH_B].rfRxAFE = rOFDM0_XBRxAFE;
+ priv->PHYRegDef[RF90_PATH_C].rfRxAFE = rOFDM0_XCRxAFE;
+ priv->PHYRegDef[RF90_PATH_D].rfRxAFE = rOFDM0_XDRxAFE;
+
+ /* Tx AFE control 1 */
+ priv->PHYRegDef[RF90_PATH_A].rfTxIQImbalance = rOFDM0_XATxIQImbalance;
+ priv->PHYRegDef[RF90_PATH_B].rfTxIQImbalance = rOFDM0_XBTxIQImbalance;
+ priv->PHYRegDef[RF90_PATH_C].rfTxIQImbalance = rOFDM0_XCTxIQImbalance;
+ priv->PHYRegDef[RF90_PATH_D].rfTxIQImbalance = rOFDM0_XDTxIQImbalance;
+
+ /* Tx AFE control 2 */
+ priv->PHYRegDef[RF90_PATH_A].rfTxAFE = rOFDM0_XATxAFE;
+ priv->PHYRegDef[RF90_PATH_B].rfTxAFE = rOFDM0_XBTxAFE;
+ priv->PHYRegDef[RF90_PATH_C].rfTxAFE = rOFDM0_XCTxAFE;
+ priv->PHYRegDef[RF90_PATH_D].rfTxAFE = rOFDM0_XDTxAFE;
+
+ /* Tranceiver LSSI Readback */
+ priv->PHYRegDef[RF90_PATH_A].rfLSSIReadBack = rFPGA0_XA_LSSIReadBack;
+ priv->PHYRegDef[RF90_PATH_B].rfLSSIReadBack = rFPGA0_XB_LSSIReadBack;
+ priv->PHYRegDef[RF90_PATH_C].rfLSSIReadBack = rFPGA0_XC_LSSIReadBack;
+ priv->PHYRegDef[RF90_PATH_D].rfLSSIReadBack = rFPGA0_XD_LSSIReadBack;
+}
+
+/******************************************************************************
+ * function: This function is to write register and then readback to make
+ * sure whether BB and RF is OK
+ * input: net_device *dev
+ * HW90_BLOCK_E CheckBlock
+ * RF90_RADIO_PATH_E eRFPath //only used when checkblock is
+ * //HW90_BLOCK_RF
+ * output: none
+ * return: return whether BB and RF is ok (0:OK, 1:Fail)
+ * notice: This function may be removed in the ASIC
+ ******************************************************************************/
+u8 rtl8192_phy_checkBBAndRF(struct net_device *dev, HW90_BLOCK_E CheckBlock,
+ RF90_RADIO_PATH_E eRFPath)
+{
+ u8 ret = 0;
+ u32 i, CheckTimes = 4, reg = 0;
+ u32 WriteAddr[4];
+ u32 WriteData[] = {0xfffff027, 0xaa55a02f, 0x00000027, 0x55aa502f};
+
+ /* Initialize register address offset to be checked */
+ WriteAddr[HW90_BLOCK_MAC] = 0x100;
+ WriteAddr[HW90_BLOCK_PHY0] = 0x900;
+ WriteAddr[HW90_BLOCK_PHY1] = 0x800;
+ WriteAddr[HW90_BLOCK_RF] = 0x3;
+ RT_TRACE(COMP_PHY, "%s(), CheckBlock: %d\n", __func__, CheckBlock);
+ for (i = 0; i < CheckTimes; i++) {
+
+ /* Write data to register and readback */
+ switch (CheckBlock) {
+ case HW90_BLOCK_MAC:
+ RT_TRACE(COMP_ERR,
+ "PHY_CheckBBRFOK(): Never Write 0x100 here!\n");
+ break;
+
+ case HW90_BLOCK_PHY0:
+ case HW90_BLOCK_PHY1:
+ write_nic_dword(dev, WriteAddr[CheckBlock],
+ WriteData[i]);
+ read_nic_dword(dev, WriteAddr[CheckBlock], &reg);
+ break;
+
+ case HW90_BLOCK_RF:
+ WriteData[i] &= 0xfff;
+ rtl8192_phy_SetRFReg(dev, eRFPath,
+ WriteAddr[HW90_BLOCK_RF],
+ bMask12Bits, WriteData[i]);
+ /* TODO: we should not delay for such a long time.
+ Ask SD3 */
+ usleep_range(1000, 1000);
+ reg = rtl8192_phy_QueryRFReg(dev, eRFPath,
+ WriteAddr[HW90_BLOCK_RF],
+ bMask12Bits);
+ usleep_range(1000, 1000);
+ break;
+
+ default:
+ ret = 1;
+ break;
+ }
+
+
+ /* Check whether readback data is correct */
+ if (reg != WriteData[i]) {
+ RT_TRACE((COMP_PHY|COMP_ERR),
+ "error reg: %x, WriteData: %x\n",
+ reg, WriteData[i]);
+ ret = 1;
+ break;
+ }
+ }
+
+ return ret;
+}
+
+/******************************************************************************
+ * function: This function initializes BB&RF
+ * input: net_device *dev
+ * output: none
+ * return: none
+ * notice: Initialization value may change all the time, so please make
+ * sure it has been synced with the newest.
+ ******************************************************************************/
+static void rtl8192_BB_Config_ParaFile(struct net_device *dev)
+{
+ struct r8192_priv *priv = ieee80211_priv(dev);
+ u8 reg_u8 = 0, eCheckItem = 0, status = 0;
+ u32 reg_u32 = 0;
+
+ /**************************************
+ * <1> Initialize BaseBand
+ *************************************/
+
+ /* --set BB Global Reset-- */
+ read_nic_byte(dev, BB_GLOBAL_RESET, &reg_u8);
+ write_nic_byte(dev, BB_GLOBAL_RESET, (reg_u8|BB_GLOBAL_RESET_BIT));
+ mdelay(50);
+ /* ---set BB reset Active--- */
+ read_nic_dword(dev, CPU_GEN, &reg_u32);
+ write_nic_dword(dev, CPU_GEN, (reg_u32&(~CPU_GEN_BB_RST)));
+
+ /* ----Ckeck FPGAPHY0 and PHY1 board is OK---- */
+ /* TODO: this function should be removed on ASIC */
+ for (eCheckItem = (HW90_BLOCK_E)HW90_BLOCK_PHY0;
+ eCheckItem <= HW90_BLOCK_PHY1; eCheckItem++) {
+ /* don't care RF path */
+ status = rtl8192_phy_checkBBAndRF(dev, (HW90_BLOCK_E)eCheckItem,
+ (RF90_RADIO_PATH_E)0);
+ if (status != 0) {
+ RT_TRACE((COMP_ERR | COMP_PHY),
+ "PHY_RF8256_Config(): Check PHY%d Fail!!\n",
+ eCheckItem-1);
+ return;
+ }
+ }
+ /* ---- Set CCK and OFDM Block "OFF"---- */
+ rtl8192_setBBreg(dev, rFPGA0_RFMOD, bCCKEn|bOFDMEn, 0x0);
+ /* ----BB Register Initilazation---- */
+ /* ==m==>Set PHY REG From Header<==m== */
+ rtl8192_phyConfigBB(dev, BaseBand_Config_PHY_REG);
+
+ /* ----Set BB reset de-Active---- */
+ read_nic_dword(dev, CPU_GEN, &reg_u32);
+ write_nic_dword(dev, CPU_GEN, (reg_u32|CPU_GEN_BB_RST));
+
+ /* ----BB AGC table Initialization---- */
+ /* ==m==>Set PHY REG From Header<==m== */
+ rtl8192_phyConfigBB(dev, BaseBand_Config_AGC_TAB);
+
+ /* ----Enable XSTAL ---- */
+ write_nic_byte_E(dev, 0x5e, 0x00);
+ if (priv->card_8192_version == (u8)VERSION_819xU_A) {
+ /* Antenna gain offset from B/C/D to A */
+ reg_u32 = priv->AntennaTxPwDiff[1]<<4 |
+ priv->AntennaTxPwDiff[0];
+ rtl8192_setBBreg(dev, rFPGA0_TxGainStage, (bXBTxAGC|bXCTxAGC),
+ reg_u32);
+
+ /* XSTALLCap */
+ reg_u32 = priv->CrystalCap & 0xf;
+ rtl8192_setBBreg(dev, rFPGA0_AnalogParameter1, bXtalCap,
+ reg_u32);
+ }
+
+ /* Check if the CCK HighPower is turned ON.
+ This is used to calculate PWDB. */
+ priv->bCckHighPower = (u8)rtl8192_QueryBBReg(dev,
+ rFPGA0_XA_HSSIParameter2,
+ 0x200);
+}
+
+/******************************************************************************
+ * function: This function initializes BB&RF
+ * input: net_device *dev
+ * output: none
+ * return: none
+ * notice: Initialization value may change all the time, so please make
+ * sure it has been synced with the newest.
+ *****************************************************************************/
+void rtl8192_BBConfig(struct net_device *dev)
+{
+ rtl8192_InitBBRFRegDef(dev);
+ /* config BB&RF. As hardCode based initialization has not been well
+ * implemented, so use file first.
+ * FIXME: should implement it for hardcode? */
+ rtl8192_BB_Config_ParaFile(dev);
+}
+
+
+/******************************************************************************
+ * function: This function obtains the initialization value of Tx power Level
+ * offset
+ * input: net_device *dev
+ * output: none
+ * return: none
+ *****************************************************************************/
+void rtl8192_phy_getTxPower(struct net_device *dev)
+{
+ struct r8192_priv *priv = ieee80211_priv(dev);
+ u8 tmp;
+
+ read_nic_dword(dev, rTxAGC_Rate18_06,
+ &priv->MCSTxPowerLevelOriginalOffset[0]);
+ read_nic_dword(dev, rTxAGC_Rate54_24,
+ &priv->MCSTxPowerLevelOriginalOffset[1]);
+ read_nic_dword(dev, rTxAGC_Mcs03_Mcs00,
+ &priv->MCSTxPowerLevelOriginalOffset[2]);
+ read_nic_dword(dev, rTxAGC_Mcs07_Mcs04,
+ &priv->MCSTxPowerLevelOriginalOffset[3]);
+ read_nic_dword(dev, rTxAGC_Mcs11_Mcs08,
+ &priv->MCSTxPowerLevelOriginalOffset[4]);
+ read_nic_dword(dev, rTxAGC_Mcs15_Mcs12,
+ &priv->MCSTxPowerLevelOriginalOffset[5]);
+
+ /* Read rx initial gain */
+ read_nic_byte(dev, rOFDM0_XAAGCCore1, &priv->DefaultInitialGain[0]);
+ read_nic_byte(dev, rOFDM0_XBAGCCore1, &priv->DefaultInitialGain[1]);
+ read_nic_byte(dev, rOFDM0_XCAGCCore1, &priv->DefaultInitialGain[2]);
+ read_nic_byte(dev, rOFDM0_XDAGCCore1, &priv->DefaultInitialGain[3]);
+ RT_TRACE(COMP_INIT,
+ "Default initial gain (c50=0x%x, c58=0x%x, c60=0x%x, c68=0x%x)\n",
+ priv->DefaultInitialGain[0], priv->DefaultInitialGain[1],
+ priv->DefaultInitialGain[2], priv->DefaultInitialGain[3]);
+
+ /* Read framesync */
+ read_nic_byte(dev, rOFDM0_RxDetector3, &priv->framesync);
+ read_nic_byte(dev, rOFDM0_RxDetector2, &tmp);
+ priv->framesyncC34 = tmp;
+ RT_TRACE(COMP_INIT, "Default framesync (0x%x) = 0x%x\n",
+ rOFDM0_RxDetector3, priv->framesync);
+
+ /* Read SIFS (save the value read fome MACPHY_REG.txt) */
+ read_nic_word(dev, SIFS, &priv->SifsTime);
+}
+
+/******************************************************************************
+ * function: This function sets the initialization value of Tx power Level
+ * offset
+ * input: net_device *dev
+ * u8 channel
+ * output: none
+ * return: none
+ ******************************************************************************/
+void rtl8192_phy_setTxPower(struct net_device *dev, u8 channel)
+{
+ struct r8192_priv *priv = ieee80211_priv(dev);
+ u8 powerlevel = priv->TxPowerLevelCCK[channel-1];
+ u8 powerlevelOFDM24G = priv->TxPowerLevelOFDM24G[channel-1];
+
+ switch (priv->rf_chip) {
+ case RF_8256:
+ /* need further implement */
+ PHY_SetRF8256CCKTxPower(dev, powerlevel);
+ PHY_SetRF8256OFDMTxPower(dev, powerlevelOFDM24G);
+ break;
+ default:
+ RT_TRACE((COMP_PHY|COMP_ERR),
+ "error RF chipID(8225 or 8258) in function %s()\n",
+ __func__);
+ break;
+ }
+}
+
+/******************************************************************************
+ * function: This function checks Rf chip to do RF config
+ * input: net_device *dev
+ * output: none
+ * return: only 8256 is supported
+ ******************************************************************************/
+void rtl8192_phy_RFConfig(struct net_device *dev)
+{
+ struct r8192_priv *priv = ieee80211_priv(dev);
+
+ switch (priv->rf_chip) {
+ case RF_8256:
+ PHY_RF8256_Config(dev);
+ break;
+ default:
+ RT_TRACE(COMP_ERR, "error chip id\n");
+ break;
+ }
+}
+
+/******************************************************************************
+ * function: This function updates Initial gain
+ * input: net_device *dev
+ * output: none
+ * return: As Windows has not implemented this, wait for complement
+ ******************************************************************************/
+void rtl8192_phy_updateInitGain(struct net_device *dev)
+{
+}
+
+/******************************************************************************
+ * function: This function read RF parameters from general head file,
+ * and do RF 3-wire
+ * input: net_device *dev
+ * RF90_RADIO_PATH_E eRFPath
+ * output: none
+ * return: return code show if RF configuration is successful(0:pass, 1:fail)
+ * notice: Delay may be required for RF configuration
+ *****************************************************************************/
+u8 rtl8192_phy_ConfigRFWithHeaderFile(struct net_device *dev,
+ RF90_RADIO_PATH_E eRFPath)
+{
+
+ int i;
+
+ switch (eRFPath) {
+ case RF90_PATH_A:
+ for (i = 0; i < RadioA_ArrayLength; i = i+2) {
+
+ if (rtl819XRadioA_Array[i] == 0xfe) {
+ mdelay(100);
+ continue;
+ }
+ rtl8192_phy_SetRFReg(dev, eRFPath,
+ rtl819XRadioA_Array[i],
+ bMask12Bits,
+ rtl819XRadioA_Array[i+1]);
+ mdelay(1);
+
+ }
+ break;
+ case RF90_PATH_B:
+ for (i = 0; i < RadioB_ArrayLength; i = i+2) {
+
+ if (rtl819XRadioB_Array[i] == 0xfe) {
+ mdelay(100);
+ continue;
+ }
+ rtl8192_phy_SetRFReg(dev, eRFPath,
+ rtl819XRadioB_Array[i],
+ bMask12Bits,
+ rtl819XRadioB_Array[i+1]);
+ mdelay(1);
+
+ }
+ break;
+ case RF90_PATH_C:
+ for (i = 0; i < RadioC_ArrayLength; i = i+2) {
+
+ if (rtl819XRadioC_Array[i] == 0xfe) {
+ mdelay(100);
+ continue;
+ }
+ rtl8192_phy_SetRFReg(dev, eRFPath,
+ rtl819XRadioC_Array[i],
+ bMask12Bits,
+ rtl819XRadioC_Array[i+1]);
+ mdelay(1);
+
+ }
+ break;
+ case RF90_PATH_D:
+ for (i = 0; i < RadioD_ArrayLength; i = i+2) {
+
+ if (rtl819XRadioD_Array[i] == 0xfe) {
+ mdelay(100);
+ continue;
+ }
+ rtl8192_phy_SetRFReg(dev, eRFPath,
+ rtl819XRadioD_Array[i],
+ bMask12Bits,
+ rtl819XRadioD_Array[i+1]);
+ mdelay(1);
+
+ }
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+
+}
+
+/******************************************************************************
+ * function: This function sets Tx Power of the channel
+ * input: net_device *dev
+ * u8 channel
+ * output: none
+ * return: none
+ * notice:
+ ******************************************************************************/
+static void rtl8192_SetTxPowerLevel(struct net_device *dev, u8 channel)
+{
+ struct r8192_priv *priv = ieee80211_priv(dev);
+ u8 powerlevel = priv->TxPowerLevelCCK[channel-1];
+ u8 powerlevelOFDM24G = priv->TxPowerLevelOFDM24G[channel-1];
+
+ switch (priv->rf_chip) {
+ case RF_8225:
+#ifdef TO_DO_LIST
+ PHY_SetRF8225CckTxPower(Adapter, powerlevel);
+ PHY_SetRF8225OfdmTxPower(Adapter, powerlevelOFDM24G);
+#endif
+ break;
+
+ case RF_8256:
+ PHY_SetRF8256CCKTxPower(dev, powerlevel);
+ PHY_SetRF8256OFDMTxPower(dev, powerlevelOFDM24G);
+ break;
+
+ case RF_8258:
+ break;
+ default:
+ RT_TRACE(COMP_ERR, "unknown rf chip ID in %s()\n", __func__);
+ break;
+ }
+}
+
+/******************************************************************************
+ * function: This function sets RF state on or off
+ * input: net_device *dev
+ * RT_RF_POWER_STATE eRFPowerState //Power State to set
+ * output: none
+ * return: none
+ * notice:
+ *****************************************************************************/
+bool rtl8192_SetRFPowerState(struct net_device *dev,
+ RT_RF_POWER_STATE eRFPowerState)
+{
+ bool bResult = true;
+ struct r8192_priv *priv = ieee80211_priv(dev);
+
+ if (eRFPowerState == priv->ieee80211->eRFPowerState)
+ return false;
+
+ if (priv->SetRFPowerStateInProgress)
+ return false;
+
+ priv->SetRFPowerStateInProgress = true;
+
+ switch (priv->rf_chip) {
+ case RF_8256:
+ switch (eRFPowerState) {
+ case eRfOn:
+ /* RF-A, RF-B */
+ /* enable RF-Chip A/B - 0x860[4] */
+ rtl8192_setBBreg(dev, rFPGA0_XA_RFInterfaceOE, BIT4,
+ 0x1);
+ /* analog to digital on - 0x88c[9:8] */
+ rtl8192_setBBreg(dev, rFPGA0_AnalogParameter4, 0x300,
+ 0x3);
+ /* digital to analog on - 0x880[4:3] */
+ rtl8192_setBBreg(dev, rFPGA0_AnalogParameter1, 0x18,
+ 0x3);
+ /* rx antenna on - 0xc04[1:0] */
+ rtl8192_setBBreg(dev, rOFDM0_TRxPathEnable, 0x3, 0x3);
+ /* rx antenna on - 0xd04[1:0] */
+ rtl8192_setBBreg(dev, rOFDM1_TRxPathEnable, 0x3, 0x3);
+ /* analog to digital part2 on - 0x880[6:5] */
+ rtl8192_setBBreg(dev, rFPGA0_AnalogParameter1, 0x60,
+ 0x3);
+
+ break;
+
+ case eRfSleep:
+
+ break;
+
+ case eRfOff:
+ /* RF-A, RF-B */
+ /* disable RF-Chip A/B - 0x860[4] */
+ rtl8192_setBBreg(dev, rFPGA0_XA_RFInterfaceOE, BIT4,
+ 0x0);
+ /* analog to digital off, for power save */
+ rtl8192_setBBreg(dev, rFPGA0_AnalogParameter4, 0xf00,
+ 0x0); /* 0x88c[11:8] */
+ /* digital to analog off, for power save - 0x880[4:3] */
+ rtl8192_setBBreg(dev, rFPGA0_AnalogParameter1, 0x18,
+ 0x0);
+ /* rx antenna off - 0xc04[3:0] */
+ rtl8192_setBBreg(dev, rOFDM0_TRxPathEnable, 0xf, 0x0);
+ /* rx antenna off - 0xd04[3:0] */
+ rtl8192_setBBreg(dev, rOFDM1_TRxPathEnable, 0xf, 0x0);
+ /* analog to digital part2 off, for power save */
+ rtl8192_setBBreg(dev, rFPGA0_AnalogParameter1, 0x60,
+ 0x0); /* 0x880[6:5] */
+
+ break;
+
+ default:
+ bResult = false;
+ RT_TRACE(COMP_ERR, "%s(): unknown state to set: 0x%X\n",
+ __func__, eRFPowerState);
+ break;
+ }
+ break;
+ default:
+ RT_TRACE(COMP_ERR, "Not support rf_chip(%x)\n", priv->rf_chip);
+ break;
+ }
+#ifdef TO_DO_LIST
+ if (bResult) {
+ /* Update current RF state variable. */
+ pHalData->eRFPowerState = eRFPowerState;
+ switch (pHalData->RFChipID) {
+ case RF_8256:
+ switch (pHalData->eRFPowerState) {
+ case eRfOff:
+ /* If Rf off reason is from IPS,
+ LED should blink with no link */
+ if (pMgntInfo->RfOffReason == RF_CHANGE_BY_IPS)
+ Adapter->HalFunc.LedControlHandler(Adapter, LED_CTL_NO_LINK);
+ else
+ /* Turn off LED if RF is not ON. */
+ Adapter->HalFunc.LedControlHandler(Adapter, LED_CTL_POWER_OFF);
+ break;
+
+ case eRfOn:
+ /* Turn on RF we are still linked, which might
+ happen when we quickly turn off and on HW RF.
+ */
+ if (pMgntInfo->bMediaConnect)
+ Adapter->HalFunc.LedControlHandler(Adapter, LED_CTL_LINK);
+ else
+ /* Turn off LED if RF is not ON. */
+ Adapter->HalFunc.LedControlHandler(Adapter, LED_CTL_NO_LINK);
+ break;
+
+ default:
+ break;
+ }
+ break;
+
+ default:
+ RT_TRACE(COMP_RF, DBG_LOUD, "%s(): Unknown RF type\n",
+ __func__);
+ break;
+ }
+
+ }
+#endif
+ priv->SetRFPowerStateInProgress = false;
+
+ return bResult;
+}
+
+/******************************************************************************
+ * function: This function sets command table variable (struct SwChnlCmd).
+ * input: SwChnlCmd *CmdTable //table to be set
+ * u32 CmdTableIdx //variable index in table to be set
+ * u32 CmdTableSz //table size
+ * SwChnlCmdID CmdID //command ID to set
+ * u32 Para1
+ * u32 Para2
+ * u32 msDelay
+ * output:
+ * return: true if finished, false otherwise
+ * notice:
+ ******************************************************************************/
+static u8 rtl8192_phy_SetSwChnlCmdArray(SwChnlCmd *CmdTable, u32 CmdTableIdx,
+ u32 CmdTableSz, SwChnlCmdID CmdID,
+ u32 Para1, u32 Para2, u32 msDelay)
+{
+ SwChnlCmd *pCmd;
+
+ if (CmdTable == NULL) {
+ RT_TRACE(COMP_ERR, "%s(): CmdTable cannot be NULL\n", __func__);
+ return false;
+ }
+ if (CmdTableIdx >= CmdTableSz) {
+ RT_TRACE(COMP_ERR, "%s(): Access invalid index, please check size of the table, CmdTableIdx:%d, CmdTableSz:%d\n",
+ __func__, CmdTableIdx, CmdTableSz);
+ return false;
+ }
+
+ pCmd = CmdTable + CmdTableIdx;
+ pCmd->CmdID = CmdID;
+ pCmd->Para1 = Para1;
+ pCmd->Para2 = Para2;
+ pCmd->msDelay = msDelay;
+
+ return true;
+}
+
+/******************************************************************************
+ * function: This function sets channel step by step
+ * input: net_device *dev
+ * u8 channel
+ * u8 *stage //3 stages
+ * u8 *step
+ * u32 *delay //whether need to delay
+ * output: store new stage, step and delay for next step
+ * (combine with function above)
+ * return: true if finished, false otherwise
+ * notice: Wait for simpler function to replace it
+ *****************************************************************************/
+static u8 rtl8192_phy_SwChnlStepByStep(struct net_device *dev, u8 channel,
+ u8 *stage, u8 *step, u32 *delay)
+{
+ struct r8192_priv *priv = ieee80211_priv(dev);
+ SwChnlCmd PreCommonCmd[MAX_PRECMD_CNT];
+ u32 PreCommonCmdCnt;
+ SwChnlCmd PostCommonCmd[MAX_POSTCMD_CNT];
+ u32 PostCommonCmdCnt;
+ SwChnlCmd RfDependCmd[MAX_RFDEPENDCMD_CNT];
+ u32 RfDependCmdCnt;
+ SwChnlCmd *CurrentCmd = NULL;
+ u8 eRFPath;
+
+ RT_TRACE(COMP_CH, "%s() stage: %d, step: %d, channel: %d\n",
+ __func__, *stage, *step, channel);
+ if (!IsLegalChannel(priv->ieee80211, channel)) {
+ RT_TRACE(COMP_ERR, "set to illegal channel: %d\n", channel);
+ /* return true to tell upper caller function this channel
+ setting is finished! Or it will in while loop. */
+ return true;
+ }
+ /* FIXME: need to check whether channel is legal or not here */
+
+
+ /* <1> Fill up pre common command. */
+ PreCommonCmdCnt = 0;
+ rtl8192_phy_SetSwChnlCmdArray(PreCommonCmd, PreCommonCmdCnt++,
+ MAX_PRECMD_CNT, CmdID_SetTxPowerLevel,
+ 0, 0, 0);
+ rtl8192_phy_SetSwChnlCmdArray(PreCommonCmd, PreCommonCmdCnt++,
+ MAX_PRECMD_CNT, CmdID_End, 0, 0, 0);
+
+ /* <2> Fill up post common command. */
+ PostCommonCmdCnt = 0;
+
+ rtl8192_phy_SetSwChnlCmdArray(PostCommonCmd, PostCommonCmdCnt++,
+ MAX_POSTCMD_CNT, CmdID_End, 0, 0, 0);
+
+ /* <3> Fill up RF dependent command. */
+ RfDependCmdCnt = 0;
+ switch (priv->rf_chip) {
+ case RF_8225:
+ if (!(channel >= 1 && channel <= 14)) {
+ RT_TRACE(COMP_ERR,
+ "illegal channel for Zebra 8225: %d\n",
+ channel);
+ return true;
+ }
+ rtl8192_phy_SetSwChnlCmdArray(RfDependCmd, RfDependCmdCnt++,
+ MAX_RFDEPENDCMD_CNT,
+ CmdID_RF_WriteReg,
+ rZebra1_Channel,
+ RF_CHANNEL_TABLE_ZEBRA[channel],
+ 10);
+ rtl8192_phy_SetSwChnlCmdArray(RfDependCmd, RfDependCmdCnt++,
+ MAX_RFDEPENDCMD_CNT,
+ CmdID_End, 0, 0, 0);
+ break;
+
+ case RF_8256:
+ /* TEST!! This is not the table for 8256!! */
+ if (!(channel >= 1 && channel <= 14)) {
+ RT_TRACE(COMP_ERR,
+ "illegal channel for Zebra 8256: %d\n",
+ channel);
+ return true;
+ }
+ rtl8192_phy_SetSwChnlCmdArray(RfDependCmd, RfDependCmdCnt++,
+ MAX_RFDEPENDCMD_CNT,
+ CmdID_RF_WriteReg,
+ rZebra1_Channel, channel, 10);
+ rtl8192_phy_SetSwChnlCmdArray(RfDependCmd, RfDependCmdCnt++,
+ MAX_RFDEPENDCMD_CNT,
+ CmdID_End, 0, 0, 0);
+ break;
+
+ case RF_8258:
+ break;
+
+ default:
+ RT_TRACE(COMP_ERR, "Unknown RFChipID: %d\n", priv->rf_chip);
+ return true;
+ }
+
+
+ do {
+ switch (*stage) {
+ case 0:
+ CurrentCmd = &PreCommonCmd[*step];
+ break;
+ case 1:
+ CurrentCmd = &RfDependCmd[*step];
+ break;
+ case 2:
+ CurrentCmd = &PostCommonCmd[*step];
+ break;
+ }
+
+ if (CurrentCmd->CmdID == CmdID_End) {
+ if ((*stage) == 2) {
+ (*delay) = CurrentCmd->msDelay;
+ return true;
+ }
+ (*stage)++;
+ (*step) = 0;
+ continue;
+ }
+
+ switch (CurrentCmd->CmdID) {
+ case CmdID_SetTxPowerLevel:
+ if (priv->card_8192_version == (u8)VERSION_819xU_A)
+ /* consider it later! */
+ rtl8192_SetTxPowerLevel(dev, channel);
+ break;
+ case CmdID_WritePortUlong:
+ write_nic_dword(dev, CurrentCmd->Para1,
+ CurrentCmd->Para2);
+ break;
+ case CmdID_WritePortUshort:
+ write_nic_word(dev, CurrentCmd->Para1,
+ (u16)CurrentCmd->Para2);
+ break;
+ case CmdID_WritePortUchar:
+ write_nic_byte(dev, CurrentCmd->Para1,
+ (u8)CurrentCmd->Para2);
+ break;
+ case CmdID_RF_WriteReg:
+ for (eRFPath = 0; eRFPath < RF90_PATH_MAX; eRFPath++) {
+ rtl8192_phy_SetRFReg(dev,
+ (RF90_RADIO_PATH_E)eRFPath,
+ CurrentCmd->Para1,
+ bZebra1_ChannelNum,
+ CurrentCmd->Para2);
+ }
+ break;
+ default:
+ break;
+ }
+
+ break;
+ } while (true);
+
+ (*delay) = CurrentCmd->msDelay;
+ (*step)++;
+ return false;
+}
+
+/******************************************************************************
+ * function: This function does actually set channel work
+ * input: net_device *dev
+ * u8 channel
+ * output: none
+ * return: none
+ * notice: We should not call this function directly
+ *****************************************************************************/
+static void rtl8192_phy_FinishSwChnlNow(struct net_device *dev, u8 channel)
+{
+ struct r8192_priv *priv = ieee80211_priv(dev);
+ u32 delay = 0;
+
+ while (!rtl8192_phy_SwChnlStepByStep(dev, channel, &priv->SwChnlStage,
+ &priv->SwChnlStep, &delay)) {
+ if (!priv->up)
+ break;
+ }
+}
+
+/******************************************************************************
+ * function: Callback routine of the work item for switch channel.
+ * input: net_device *dev
+ *
+ * output: none
+ * return: none
+ *****************************************************************************/
+void rtl8192_SwChnl_WorkItem(struct net_device *dev)
+{
+
+ struct r8192_priv *priv = ieee80211_priv(dev);
+
+ RT_TRACE(COMP_CH, "==> SwChnlCallback819xUsbWorkItem(), chan:%d\n",
+ priv->chan);
+
+
+ rtl8192_phy_FinishSwChnlNow(dev, priv->chan);
+
+ RT_TRACE(COMP_CH, "<== SwChnlCallback819xUsbWorkItem()\n");
+}
+
+/******************************************************************************
+ * function: This function scheduled actual work item to set channel
+ * input: net_device *dev
+ * u8 channel //channel to set
+ * output: none
+ * return: return code show if workitem is scheduled (1:pass, 0:fail)
+ * notice: Delay may be required for RF configuration
+ ******************************************************************************/
+u8 rtl8192_phy_SwChnl(struct net_device *dev, u8 channel)
+{
+ struct r8192_priv *priv = ieee80211_priv(dev);
+
+ RT_TRACE(COMP_CH, "%s(), SwChnlInProgress: %d\n", __func__,
+ priv->SwChnlInProgress);
+ if (!priv->up)
+ return false;
+ if (priv->SwChnlInProgress)
+ return false;
+
+ /* -------------------------------------------- */
+ switch (priv->ieee80211->mode) {
+ case WIRELESS_MODE_A:
+ case WIRELESS_MODE_N_5G:
+ if (channel <= 14) {
+ RT_TRACE(COMP_ERR, "WIRELESS_MODE_A but channel<=14\n");
+ return false;
+ }
+ break;
+ case WIRELESS_MODE_B:
+ if (channel > 14) {
+ RT_TRACE(COMP_ERR, "WIRELESS_MODE_B but channel>14\n");
+ return false;
+ }
+ break;
+ case WIRELESS_MODE_G:
+ case WIRELESS_MODE_N_24G:
+ if (channel > 14) {
+ RT_TRACE(COMP_ERR, "WIRELESS_MODE_G but channel>14\n");
+ return false;
+ }
+ break;
+ }
+ /* -------------------------------------------- */
+
+ priv->SwChnlInProgress = true;
+ if (channel == 0)
+ channel = 1;
+
+ priv->chan = channel;
+
+ priv->SwChnlStage = 0;
+ priv->SwChnlStep = 0;
+ if (priv->up)
+ rtl8192_SwChnl_WorkItem(dev);
+
+ priv->SwChnlInProgress = false;
+ return true;
+}
+
+/******************************************************************************
+ * function: Callback routine of the work item for set bandwidth mode.
+ * input: net_device *dev
+ * output: none
+ * return: none
+ * notice: I doubt whether SetBWModeInProgress flag is necessary as we can
+ * test whether current work in the queue or not.//do I?
+ *****************************************************************************/
+void rtl8192_SetBWModeWorkItem(struct net_device *dev)
+{
+
+ struct r8192_priv *priv = ieee80211_priv(dev);
+ u8 regBwOpMode;
+
+ RT_TRACE(COMP_SWBW, "%s() Switch to %s bandwidth\n", __func__,
+ priv->CurrentChannelBW == HT_CHANNEL_WIDTH_20?"20MHz":"40MHz");
+
+
+ if (priv->rf_chip == RF_PSEUDO_11N) {
+ priv->SetBWModeInProgress = false;
+ return;
+ }
+
+ /* <1> Set MAC register */
+ read_nic_byte(dev, BW_OPMODE, &regBwOpMode);
+
+ switch (priv->CurrentChannelBW) {
+ case HT_CHANNEL_WIDTH_20:
+ regBwOpMode |= BW_OPMODE_20MHZ;
+ /* We have not verify whether this register works */
+ write_nic_byte(dev, BW_OPMODE, regBwOpMode);
+ break;
+
+ case HT_CHANNEL_WIDTH_20_40:
+ regBwOpMode &= ~BW_OPMODE_20MHZ;
+ /* We have not verify whether this register works */
+ write_nic_byte(dev, BW_OPMODE, regBwOpMode);
+ break;
+
+ default:
+ RT_TRACE(COMP_ERR,
+ "SetChannelBandwidth819xUsb(): unknown Bandwidth: %#X\n",
+ priv->CurrentChannelBW);
+ break;
+ }
+
+ /* <2> Set PHY related register */
+ switch (priv->CurrentChannelBW) {
+ case HT_CHANNEL_WIDTH_20:
+ rtl8192_setBBreg(dev, rFPGA0_RFMOD, bRFMOD, 0x0);
+ rtl8192_setBBreg(dev, rFPGA1_RFMOD, bRFMOD, 0x0);
+ rtl8192_setBBreg(dev, rFPGA0_AnalogParameter1,
+ 0x00100000, 1);
+
+ /* Correct the tx power for CCK rate in 20M. */
+ priv->cck_present_attentuation =
+ priv->cck_present_attentuation_20Mdefault +
+ priv->cck_present_attentuation_difference;
+
+ if (priv->cck_present_attentuation > 22)
+ priv->cck_present_attentuation = 22;
+ if (priv->cck_present_attentuation < 0)
+ priv->cck_present_attentuation = 0;
+ RT_TRACE(COMP_INIT,
+ "20M, pHalData->CCKPresentAttentuation = %d\n",
+ priv->cck_present_attentuation);
+
+ if (priv->chan == 14 && !priv->bcck_in_ch14) {
+ priv->bcck_in_ch14 = true;
+ dm_cck_txpower_adjust(dev, priv->bcck_in_ch14);
+ } else if (priv->chan != 14 && priv->bcck_in_ch14) {
+ priv->bcck_in_ch14 = false;
+ dm_cck_txpower_adjust(dev, priv->bcck_in_ch14);
+ } else {
+ dm_cck_txpower_adjust(dev, priv->bcck_in_ch14);
+ }
+
+ break;
+ case HT_CHANNEL_WIDTH_20_40:
+ rtl8192_setBBreg(dev, rFPGA0_RFMOD, bRFMOD, 0x1);
+ rtl8192_setBBreg(dev, rFPGA1_RFMOD, bRFMOD, 0x1);
+ rtl8192_setBBreg(dev, rCCK0_System, bCCKSideBand,
+ priv->nCur40MhzPrimeSC>>1);
+ rtl8192_setBBreg(dev, rFPGA0_AnalogParameter1, 0x00100000, 0);
+ rtl8192_setBBreg(dev, rOFDM1_LSTF, 0xC00,
+ priv->nCur40MhzPrimeSC);
+ priv->cck_present_attentuation =
+ priv->cck_present_attentuation_40Mdefault +
+ priv->cck_present_attentuation_difference;
+
+ if (priv->cck_present_attentuation > 22)
+ priv->cck_present_attentuation = 22;
+ if (priv->cck_present_attentuation < 0)
+ priv->cck_present_attentuation = 0;
+
+ RT_TRACE(COMP_INIT,
+ "40M, pHalData->CCKPresentAttentuation = %d\n",
+ priv->cck_present_attentuation);
+ if (priv->chan == 14 && !priv->bcck_in_ch14) {
+ priv->bcck_in_ch14 = true;
+ dm_cck_txpower_adjust(dev, priv->bcck_in_ch14);
+ } else if (priv->chan != 14 && priv->bcck_in_ch14) {
+ priv->bcck_in_ch14 = false;
+ dm_cck_txpower_adjust(dev, priv->bcck_in_ch14);
+ } else {
+ dm_cck_txpower_adjust(dev, priv->bcck_in_ch14);
+ }
+
+ break;
+ default:
+ RT_TRACE(COMP_ERR,
+ "SetChannelBandwidth819xUsb(): unknown Bandwidth: %#X\n",
+ priv->CurrentChannelBW);
+ break;
+
+ }
+ /* Skip over setting of J-mode in BB register here.
+ Default value is "None J mode". */
+
+ /* <3> Set RF related register */
+ switch (priv->rf_chip) {
+ case RF_8225:
+#ifdef TO_DO_LIST
+ PHY_SetRF8225Bandwidth(Adapter, pHalData->CurrentChannelBW);
+#endif
+ break;
+
+ case RF_8256:
+ PHY_SetRF8256Bandwidth(dev, priv->CurrentChannelBW);
+ break;
+
+ case RF_8258:
+ break;
+
+ case RF_PSEUDO_11N:
+ break;
+
+ default:
+ RT_TRACE(COMP_ERR, "Unknown RFChipID: %d\n", priv->rf_chip);
+ break;
+ }
+ priv->SetBWModeInProgress = false;
+
+ RT_TRACE(COMP_SWBW, "<==SetBWMode819xUsb(), %d\n",
+ atomic_read(&priv->ieee80211->atm_swbw));
+}
+
+/******************************************************************************
+ * function: This function schedules bandwidth switch work.
+ * input: struct net_deviceq *dev
+ * HT_CHANNEL_WIDTH bandwidth //20M or 40M
+ * HT_EXTCHNL_OFFSET offset //Upper, Lower, or Don't care
+ * output: none
+ * return: none
+ * notice: I doubt whether SetBWModeInProgress flag is necessary as we can
+ * test whether current work in the queue or not.//do I?
+ *****************************************************************************/
+void rtl8192_SetBWMode(struct net_device *dev, HT_CHANNEL_WIDTH bandwidth,
+ HT_EXTCHNL_OFFSET offset)
+{
+ struct r8192_priv *priv = ieee80211_priv(dev);
+
+ if (priv->SetBWModeInProgress)
+ return;
+ priv->SetBWModeInProgress = true;
+
+ priv->CurrentChannelBW = bandwidth;
+
+ if (offset == HT_EXTCHNL_OFFSET_LOWER)
+ priv->nCur40MhzPrimeSC = HAL_PRIME_CHNL_OFFSET_UPPER;
+ else if (offset == HT_EXTCHNL_OFFSET_UPPER)
+ priv->nCur40MhzPrimeSC = HAL_PRIME_CHNL_OFFSET_LOWER;
+ else
+ priv->nCur40MhzPrimeSC = HAL_PRIME_CHNL_OFFSET_DONT_CARE;
+
+ rtl8192_SetBWModeWorkItem(dev);
+
+}
+
+void InitialGain819xUsb(struct net_device *dev, u8 Operation)
+{
+ struct r8192_priv *priv = ieee80211_priv(dev);
+
+ priv->InitialGainOperateType = Operation;
+
+ if (priv->up)
+ queue_delayed_work(priv->priv_wq, &priv->initialgain_operate_wq, 0);
+}
+
+void InitialGainOperateWorkItemCallBack(struct work_struct *work)
+{
+ struct delayed_work *dwork = container_of(work, struct delayed_work,
+ work);
+ struct r8192_priv *priv = container_of(dwork, struct r8192_priv,
+ initialgain_operate_wq);
+ struct net_device *dev = priv->ieee80211->dev;
+#define SCAN_RX_INITIAL_GAIN 0x17
+#define POWER_DETECTION_TH 0x08
+ u32 bitmask;
+ u8 initial_gain;
+ u8 Operation;
+
+ Operation = priv->InitialGainOperateType;
+
+ switch (Operation) {
+ case IG_Backup:
+ RT_TRACE(COMP_SCAN, "IG_Backup, backup the initial gain.\n");
+ initial_gain = SCAN_RX_INITIAL_GAIN;
+ bitmask = bMaskByte0;
+ if (dm_digtable.dig_algorithm == DIG_ALGO_BY_FALSE_ALARM)
+ /* FW DIG OFF */
+ rtl8192_setBBreg(dev, UFWP, bMaskByte1, 0x8);
+ priv->initgain_backup.xaagccore1 =
+ (u8)rtl8192_QueryBBReg(dev, rOFDM0_XAAGCCore1, bitmask);
+ priv->initgain_backup.xbagccore1 =
+ (u8)rtl8192_QueryBBReg(dev, rOFDM0_XBAGCCore1, bitmask);
+ priv->initgain_backup.xcagccore1 =
+ (u8)rtl8192_QueryBBReg(dev, rOFDM0_XCAGCCore1, bitmask);
+ priv->initgain_backup.xdagccore1 =
+ (u8)rtl8192_QueryBBReg(dev, rOFDM0_XDAGCCore1, bitmask);
+ bitmask = bMaskByte2;
+ priv->initgain_backup.cca =
+ (u8)rtl8192_QueryBBReg(dev, rCCK0_CCA, bitmask);
+
+ RT_TRACE(COMP_SCAN, "Scan InitialGainBackup 0xc50 is %x\n",
+ priv->initgain_backup.xaagccore1);
+ RT_TRACE(COMP_SCAN, "Scan InitialGainBackup 0xc58 is %x\n",
+ priv->initgain_backup.xbagccore1);
+ RT_TRACE(COMP_SCAN, "Scan InitialGainBackup 0xc60 is %x\n",
+ priv->initgain_backup.xcagccore1);
+ RT_TRACE(COMP_SCAN, "Scan InitialGainBackup 0xc68 is %x\n",
+ priv->initgain_backup.xdagccore1);
+ RT_TRACE(COMP_SCAN, "Scan InitialGainBackup 0xa0a is %x\n",
+ priv->initgain_backup.cca);
+
+ RT_TRACE(COMP_SCAN, "Write scan initial gain = 0x%x\n",
+ initial_gain);
+ write_nic_byte(dev, rOFDM0_XAAGCCore1, initial_gain);
+ write_nic_byte(dev, rOFDM0_XBAGCCore1, initial_gain);
+ write_nic_byte(dev, rOFDM0_XCAGCCore1, initial_gain);
+ write_nic_byte(dev, rOFDM0_XDAGCCore1, initial_gain);
+ RT_TRACE(COMP_SCAN, "Write scan 0xa0a = 0x%x\n",
+ POWER_DETECTION_TH);
+ write_nic_byte(dev, 0xa0a, POWER_DETECTION_TH);
+ break;
+ case IG_Restore:
+ RT_TRACE(COMP_SCAN, "IG_Restore, restore the initial gain.\n");
+ bitmask = 0x7f; /* Bit0 ~ Bit6 */
+ if (dm_digtable.dig_algorithm == DIG_ALGO_BY_FALSE_ALARM)
+ /* FW DIG OFF */
+ rtl8192_setBBreg(dev, UFWP, bMaskByte1, 0x8);
+
+ rtl8192_setBBreg(dev, rOFDM0_XAAGCCore1, bitmask,
+ (u32)priv->initgain_backup.xaagccore1);
+ rtl8192_setBBreg(dev, rOFDM0_XBAGCCore1, bitmask,
+ (u32)priv->initgain_backup.xbagccore1);
+ rtl8192_setBBreg(dev, rOFDM0_XCAGCCore1, bitmask,
+ (u32)priv->initgain_backup.xcagccore1);
+ rtl8192_setBBreg(dev, rOFDM0_XDAGCCore1, bitmask,
+ (u32)priv->initgain_backup.xdagccore1);
+ bitmask = bMaskByte2;
+ rtl8192_setBBreg(dev, rCCK0_CCA, bitmask,
+ (u32)priv->initgain_backup.cca);
+
+ RT_TRACE(COMP_SCAN, "Scan BBInitialGainRestore 0xc50 is %x\n",
+ priv->initgain_backup.xaagccore1);
+ RT_TRACE(COMP_SCAN, "Scan BBInitialGainRestore 0xc58 is %x\n",
+ priv->initgain_backup.xbagccore1);
+ RT_TRACE(COMP_SCAN, "Scan BBInitialGainRestore 0xc60 is %x\n",
+ priv->initgain_backup.xcagccore1);
+ RT_TRACE(COMP_SCAN, "Scan BBInitialGainRestore 0xc68 is %x\n",
+ priv->initgain_backup.xdagccore1);
+ RT_TRACE(COMP_SCAN, "Scan BBInitialGainRestore 0xa0a is %x\n",
+ priv->initgain_backup.cca);
+
+ rtl8192_phy_setTxPower(dev, priv->ieee80211->current_network.channel);
+
+ if (dm_digtable.dig_algorithm == DIG_ALGO_BY_FALSE_ALARM)
+ /* FW DIG ON */
+ rtl8192_setBBreg(dev, UFWP, bMaskByte1, 0x1);
+ break;
+ default:
+ RT_TRACE(COMP_SCAN, "Unknown IG Operation.\n");
+ break;
+ }
+}
diff --git a/drivers/staging/rtl8192u/r819xU_phy.h b/drivers/staging/rtl8192u/r819xU_phy.h
new file mode 100644
index 000000000..66cbe3f9c
--- /dev/null
+++ b/drivers/staging/rtl8192u/r819xU_phy.h
@@ -0,0 +1,92 @@
+#ifndef _R819XU_PHY_H
+#define _R819XU_PHY_H
+
+/* Channel switch: The size of command tables for switch channel */
+#define MAX_PRECMD_CNT 16
+#define MAX_RFDEPENDCMD_CNT 16
+#define MAX_POSTCMD_CNT 16
+
+typedef enum _SwChnlCmdID {
+ CmdID_End,
+ CmdID_SetTxPowerLevel,
+ CmdID_BBRegWrite10,
+ CmdID_WritePortUlong,
+ CmdID_WritePortUshort,
+ CmdID_WritePortUchar,
+ CmdID_RF_WriteReg,
+} SwChnlCmdID;
+
+/* -----------------------Define structure---------------------- */
+/* 1. Switch channel related */
+typedef struct _SwChnlCmd {
+ SwChnlCmdID CmdID;
+ u32 Para1;
+ u32 Para2;
+ u32 msDelay;
+} __packed SwChnlCmd;
+
+extern u32 rtl819XMACPHY_Array_PG[];
+extern u32 rtl819XPHY_REG_1T2RArray[];
+extern u32 rtl819XAGCTAB_Array[];
+extern u32 rtl819XRadioA_Array[];
+extern u32 rtl819XRadioB_Array[];
+extern u32 rtl819XRadioC_Array[];
+extern u32 rtl819XRadioD_Array[];
+
+typedef enum _HW90_BLOCK {
+ HW90_BLOCK_MAC = 0,
+ HW90_BLOCK_PHY0 = 1,
+ HW90_BLOCK_PHY1 = 2,
+ HW90_BLOCK_RF = 3,
+ HW90_BLOCK_MAXIMUM = 4, /* Never use this */
+} HW90_BLOCK_E, *PHW90_BLOCK_E;
+
+typedef enum _RF90_RADIO_PATH {
+ RF90_PATH_A = 0, /* Radio Path A */
+ RF90_PATH_B = 1, /* Radio Path B */
+ RF90_PATH_C = 2, /* Radio Path C */
+ RF90_PATH_D = 3, /* Radio Path D */
+ RF90_PATH_MAX /* Max RF number 92 support */
+} RF90_RADIO_PATH_E, *PRF90_RADIO_PATH_E;
+
+#define bMaskByte0 0xff
+#define bMaskByte1 0xff00
+#define bMaskByte2 0xff0000
+#define bMaskByte3 0xff000000
+#define bMaskHWord 0xffff0000
+#define bMaskLWord 0x0000ffff
+#define bMaskDWord 0xffffffff
+
+extern u8 rtl8192_phy_CheckIsLegalRFPath(struct net_device *dev, u32 eRFPath);
+extern void rtl8192_setBBreg(struct net_device *dev, u32 reg_addr,
+ u32 bitmask, u32 data);
+extern u32 rtl8192_QueryBBReg(struct net_device *dev, u32 reg_addr,
+ u32 bitmask);
+extern void rtl8192_phy_SetRFReg(struct net_device *dev,
+ RF90_RADIO_PATH_E eRFPath, u32 reg_addr, u32 bitmask, u32 data);
+extern u32 rtl8192_phy_QueryRFReg(struct net_device *dev,
+ RF90_RADIO_PATH_E eRFPath, u32 reg_addr, u32 bitmask);
+extern void rtl8192_phy_configmac(struct net_device *dev);
+extern void rtl8192_phyConfigBB(struct net_device *dev, u8 ConfigType);
+extern u8 rtl8192_phy_checkBBAndRF(struct net_device *dev,
+ HW90_BLOCK_E CheckBlock, RF90_RADIO_PATH_E eRFPath);
+extern void rtl8192_BBConfig(struct net_device *dev);
+extern void rtl8192_phy_getTxPower(struct net_device *dev);
+extern void rtl8192_phy_setTxPower(struct net_device *dev, u8 channel);
+extern void rtl8192_phy_RFConfig(struct net_device *dev);
+extern void rtl8192_phy_updateInitGain(struct net_device *dev);
+extern u8 rtl8192_phy_ConfigRFWithHeaderFile(struct net_device *dev,
+ RF90_RADIO_PATH_E eRFPath);
+
+extern u8 rtl8192_phy_SwChnl(struct net_device *dev, u8 channel);
+extern void rtl8192_SetBWMode(struct net_device *dev,
+ HT_CHANNEL_WIDTH bandwidth, HT_EXTCHNL_OFFSET offset);
+extern void rtl8192_SwChnl_WorkItem(struct net_device *dev);
+void rtl8192_SetBWModeWorkItem(struct net_device *dev);
+extern bool rtl8192_SetRFPowerState(struct net_device *dev,
+ RT_RF_POWER_STATE eRFPowerState);
+extern void InitialGain819xUsb(struct net_device *dev, u8 Operation);
+
+extern void InitialGainOperateWorkItemCallBack(struct work_struct *work);
+
+#endif
diff --git a/drivers/staging/rtl8192u/r819xU_phyreg.h b/drivers/staging/rtl8192u/r819xU_phyreg.h
new file mode 100644
index 000000000..b855627e9
--- /dev/null
+++ b/drivers/staging/rtl8192u/r819xU_phyreg.h
@@ -0,0 +1,878 @@
+#ifndef _R819XU_PHYREG_H
+#define _R819XU_PHYREG_H
+
+
+#define RF_DATA 0x1d4 /* FW will write RF data in the register.*/
+
+/* Register duplicate register due to connection: RF_Mode, TRxRN, NumOf L-STF
+ * page 1
+ */
+#define rPMAC_Reset 0x100
+#define rPMAC_TxStart 0x104
+#define rPMAC_TxLegacySIG 0x108
+#define rPMAC_TxHTSIG1 0x10c
+#define rPMAC_TxHTSIG2 0x110
+#define rPMAC_PHYDebug 0x114
+#define rPMAC_TxPacketNum 0x118
+#define rPMAC_TxIdle 0x11c
+#define rPMAC_TxMACHeader0 0x120
+#define rPMAC_TxMACHeader1 0x124
+#define rPMAC_TxMACHeader2 0x128
+#define rPMAC_TxMACHeader3 0x12c
+#define rPMAC_TxMACHeader4 0x130
+#define rPMAC_TxMACHeader5 0x134
+#define rPMAC_TxDataType 0x138
+#define rPMAC_TxRandomSeed 0x13c
+#define rPMAC_CCKPLCPPreamble 0x140
+#define rPMAC_CCKPLCPHeader 0x144
+#define rPMAC_CCKCRC16 0x148
+#define rPMAC_OFDMRxCRC32OK 0x170
+#define rPMAC_OFDMRxCRC32Er 0x174
+#define rPMAC_OFDMRxParityEr 0x178
+#define rPMAC_OFDMRxCRC8Er 0x17c
+#define rPMAC_CCKCRxRC16Er 0x180
+#define rPMAC_CCKCRxRC32Er 0x184
+#define rPMAC_CCKCRxRC32OK 0x188
+#define rPMAC_TxStatus 0x18c
+
+/* page8 */
+#define rFPGA0_RFMOD 0x800 /* RF mode & CCK TxSC */
+#define rFPGA0_TxInfo 0x804
+#define rFPGA0_PSDFunction 0x808
+#define rFPGA0_TxGainStage 0x80c
+#define rFPGA0_RFTiming1 0x810
+#define rFPGA0_RFTiming2 0x814
+/* #define rFPGA0_XC_RFTiming 0x818
+ * #define rFPGA0_XD_RFTiming 0x81c
+ */
+#define rFPGA0_XA_HSSIParameter1 0x820
+#define rFPGA0_XA_HSSIParameter2 0x824
+#define rFPGA0_XB_HSSIParameter1 0x828
+#define rFPGA0_XB_HSSIParameter2 0x82c
+#define rFPGA0_XC_HSSIParameter1 0x830
+#define rFPGA0_XC_HSSIParameter2 0x834
+#define rFPGA0_XD_HSSIParameter1 0x838
+#define rFPGA0_XD_HSSIParameter2 0x83c
+#define rFPGA0_XA_LSSIParameter 0x840
+#define rFPGA0_XB_LSSIParameter 0x844
+#define rFPGA0_XC_LSSIParameter 0x848
+#define rFPGA0_XD_LSSIParameter 0x84c
+#define rFPGA0_RFWakeUpParameter 0x850
+#define rFPGA0_RFSleepUpParameter 0x854
+#define rFPGA0_XAB_SwitchControl 0x858
+#define rFPGA0_XCD_SwitchControl 0x85c
+#define rFPGA0_XA_RFInterfaceOE 0x860
+#define rFPGA0_XB_RFInterfaceOE 0x864
+#define rFPGA0_XC_RFInterfaceOE 0x868
+#define rFPGA0_XD_RFInterfaceOE 0x86c
+#define rFPGA0_XAB_RFInterfaceSW 0x870
+#define rFPGA0_XCD_RFInterfaceSW 0x874
+#define rFPGA0_XAB_RFParameter 0x878
+#define rFPGA0_XCD_RFParameter 0x87c
+#define rFPGA0_AnalogParameter1 0x880
+#define rFPGA0_AnalogParameter2 0x884
+#define rFPGA0_AnalogParameter3 0x888
+#define rFPGA0_AnalogParameter4 0x88c
+#define rFPGA0_XA_LSSIReadBack 0x8a0
+#define rFPGA0_XB_LSSIReadBack 0x8a4
+#define rFPGA0_XC_LSSIReadBack 0x8a8
+#define rFPGA0_XD_LSSIReadBack 0x8ac
+#define rFPGA0_PSDReport 0x8b4
+#define rFPGA0_XAB_RFInterfaceRB 0x8e0
+#define rFPGA0_XCD_RFInterfaceRB 0x8e4
+
+/* page 9 */
+#define rFPGA1_RFMOD 0x900 /* RF mode & OFDM TxSC */
+#define rFPGA1_TxBlock 0x904
+#define rFPGA1_DebugSelect 0x908
+#define rFPGA1_TxInfo 0x90c
+
+/* page a */
+#define rCCK0_System 0xa00
+#define rCCK0_AFESetting 0xa04
+#define rCCK0_CCA 0xa08
+#define rCCK0_RxAGC1 0xa0c /* AGC default value, saturation level */
+#define rCCK0_RxAGC2 0xa10 /* AGC & DAGC */
+#define rCCK0_RxHP 0xa14
+#define rCCK0_DSPParameter1 0xa18 /* Timing recovery & Channel estimation threshold */
+#define rCCK0_DSPParameter2 0xa1c /* SQ threshold */
+#define rCCK0_TxFilter1 0xa20
+#define rCCK0_TxFilter2 0xa24
+#define rCCK0_DebugPort 0xa28 /* debug port and Tx filter3 */
+#define rCCK0_FalseAlarmReport 0xa2c /* 0xa2d */
+#define rCCK0_TRSSIReport 0xa50
+#define rCCK0_RxReport 0xa54 /* 0xa57 */
+#define rCCK0_FACounterLower 0xa5c /* 0xa5b */
+#define rCCK0_FACounterUpper 0xa58 /* 0xa5c */
+
+/* page c */
+#define rOFDM0_LSTF 0xc00
+#define rOFDM0_TRxPathEnable 0xc04
+#define rOFDM0_TRMuxPar 0xc08
+#define rOFDM0_TRSWIsolation 0xc0c
+#define rOFDM0_XARxAFE 0xc10 /* RxIQ DC offset, Rx digital filter, DC notch filter */
+#define rOFDM0_XARxIQImbalance 0xc14 /* RxIQ imblance matrix */
+#define rOFDM0_XBRxAFE 0xc18
+#define rOFDM0_XBRxIQImbalance 0xc1c
+#define rOFDM0_XCRxAFE 0xc20
+#define rOFDM0_XCRxIQImbalance 0xc24
+#define rOFDM0_XDRxAFE 0xc28
+#define rOFDM0_XDRxIQImbalance 0xc2c
+#define rOFDM0_RxDetector1 0xc30 /* PD,BW & SBD */
+#define rOFDM0_RxDetector2 0xc34 /* SBD & Fame Sync.*/
+#define rOFDM0_RxDetector3 0xc38 /* Frame Sync.*/
+#define rOFDM0_RxDetector4 0xc3c /* PD, SBD, Frame Sync & Short-GI */
+#define rOFDM0_RxDSP 0xc40 /* Rx Sync Path */
+#define rOFDM0_CFOandDAGC 0xc44 /* CFO & DAGC */
+#define rOFDM0_CCADropThreshold 0xc48 /* CCA Drop threshold */
+#define rOFDM0_ECCAThreshold 0xc4c /* energy CCA */
+#define rOFDM0_XAAGCCore1 0xc50
+#define rOFDM0_XAAGCCore2 0xc54
+#define rOFDM0_XBAGCCore1 0xc58
+#define rOFDM0_XBAGCCore2 0xc5c
+#define rOFDM0_XCAGCCore1 0xc60
+#define rOFDM0_XCAGCCore2 0xc64
+#define rOFDM0_XDAGCCore1 0xc68
+#define rOFDM0_XDAGCCore2 0xc6c
+#define rOFDM0_AGCParameter1 0xc70
+#define rOFDM0_AGCParameter2 0xc74
+#define rOFDM0_AGCRSSITable 0xc78
+#define rOFDM0_HTSTFAGC 0xc7c
+#define rOFDM0_XATxIQImbalance 0xc80
+#define rOFDM0_XATxAFE 0xc84
+#define rOFDM0_XBTxIQImbalance 0xc88
+#define rOFDM0_XBTxAFE 0xc8c
+#define rOFDM0_XCTxIQImbalance 0xc90
+#define rOFDM0_XCTxAFE 0xc94
+#define rOFDM0_XDTxIQImbalance 0xc98
+#define rOFDM0_XDTxAFE 0xc9c
+#define rOFDM0_RxHPParameter 0xce0
+#define rOFDM0_TxPseudoNoiseWgt 0xce4
+#define rOFDM0_FrameSync 0xcf0
+#define rOFDM0_DFSReport 0xcf4
+#define rOFDM0_TxCoeff1 0xca4
+#define rOFDM0_TxCoeff2 0xca8
+#define rOFDM0_TxCoeff3 0xcac
+#define rOFDM0_TxCoeff4 0xcb0
+#define rOFDM0_TxCoeff5 0xcb4
+#define rOFDM0_TxCoeff6 0xcb8
+
+
+/* page d */
+#define rOFDM1_LSTF 0xd00
+#define rOFDM1_TRxPathEnable 0xd04
+#define rOFDM1_CFO 0xd08
+#define rOFDM1_CSI1 0xd10
+#define rOFDM1_SBD 0xd14
+#define rOFDM1_CSI2 0xd18
+#define rOFDM1_CFOTracking 0xd2c
+#define rOFDM1_TRxMesaure1 0xd34
+#define rOFDM1_IntfDet 0xd3c
+#define rOFDM1_PseudoNoiseStateAB 0xd50
+#define rOFDM1_PseudoNoiseStateCD 0xd54
+#define rOFDM1_RxPseudoNoiseWgt 0xd58
+#define rOFDM_PHYCounter1 0xda0 /* cca, parity fail */
+#define rOFDM_PHYCounter2 0xda4 /* rate illegal, crc8 fail */
+
+#define rOFDM_PHYCounter3 0xda8 /* MCS not support */
+#define rOFDM_ShortCFOAB 0xdac
+#define rOFDM_ShortCFOCD 0xdb0
+#define rOFDM_LongCFOAB 0xdb4
+#define rOFDM_LongCFOCD 0xdb8
+#define rOFDM_TailCFOAB 0xdbc
+#define rOFDM_TailCFOCD 0xdc0
+#define rOFDM_PWMeasure1 0xdc4
+#define rOFDM_PWMeasure2 0xdc8
+#define rOFDM_BWReport 0xdcc
+#define rOFDM_AGCReport 0xdd0
+#define rOFDM_RxSNR 0xdd4
+#define rOFDM_RxEVMCSI 0xdd8
+#define rOFDM_SIGReport 0xddc
+
+/* page e */
+#define rTxAGC_Rate18_06 0xe00
+#define rTxAGC_Rate54_24 0xe04
+#define rTxAGC_CCK_Mcs32 0xe08
+#define rTxAGC_Mcs03_Mcs00 0xe10
+#define rTxAGC_Mcs07_Mcs04 0xe14
+#define rTxAGC_Mcs11_Mcs08 0xe18
+#define rTxAGC_Mcs15_Mcs12 0xe1c
+
+
+/* RF
+ * Zebra1
+ */
+#define rZebra1_HSSIEnable 0x0
+#define rZebra1_TRxEnable1 0x1
+#define rZebra1_TRxEnable2 0x2
+#define rZebra1_AGC 0x4
+#define rZebra1_ChargePump 0x5
+#define rZebra1_Channel 0x7
+#define rZebra1_TxGain 0x8
+#define rZebra1_TxLPF 0x9
+#define rZebra1_RxLPF 0xb
+#define rZebra1_RxHPFCorner 0xc
+
+/* Zebra4 */
+#define rGlobalCtrl 0
+#define rRTL8256_TxLPF 19
+#define rRTL8256_RxLPF 11
+
+/* RTL8258 */
+#define rRTL8258_TxLPF 0x11
+#define rRTL8258_RxLPF 0x13
+#define rRTL8258_RSSILPF 0xa
+
+/* Bit Mask
+ * page-1
+ */
+#define bBBResetB 0x100
+#define bGlobalResetB 0x200
+#define bOFDMTxStart 0x4
+#define bCCKTxStart 0x8
+#define bCRC32Debug 0x100
+#define bPMACLoopback 0x10
+#define bTxLSIG 0xffffff
+#define bOFDMTxRate 0xf
+#define bOFDMTxReserved 0x10
+#define bOFDMTxLength 0x1ffe0
+#define bOFDMTxParity 0x20000
+#define bTxHTSIG1 0xffffff
+#define bTxHTMCSRate 0x7f
+#define bTxHTBW 0x80
+#define bTxHTLength 0xffff00
+#define bTxHTSIG2 0xffffff
+#define bTxHTSmoothing 0x1
+#define bTxHTSounding 0x2
+#define bTxHTReserved 0x4
+#define bTxHTAggreation 0x8
+#define bTxHTSTBC 0x30
+#define bTxHTAdvanceCoding 0x40
+#define bTxHTShortGI 0x80
+#define bTxHTNumberHT_LTF 0x300
+#define bTxHTCRC8 0x3fc00
+#define bCounterReset 0x10000
+#define bNumOfOFDMTx 0xffff
+#define bNumOfCCKTx 0xffff0000
+#define bTxIdleInterval 0xffff
+#define bOFDMService 0xffff0000
+#define bTxMACHeader 0xffffffff
+#define bTxDataInit 0xff
+#define bTxHTMode 0x100
+#define bTxDataType 0x30000
+#define bTxRandomSeed 0xffffffff
+#define bCCKTxPreamble 0x1
+#define bCCKTxSFD 0xffff0000
+#define bCCKTxSIG 0xff
+#define bCCKTxService 0xff00
+#define bCCKLengthExt 0x8000
+#define bCCKTxLength 0xffff0000
+#define bCCKTxCRC16 0xffff
+#define bCCKTxStatus 0x1
+#define bOFDMTxStatus 0x2
+
+/* page-8 */
+#define bRFMOD 0x1
+#define bJapanMode 0x2
+#define bCCKTxSC 0x30
+#define bCCKEn 0x1000000
+#define bOFDMEn 0x2000000
+#define bOFDMRxADCPhase 0x10000
+#define bOFDMTxDACPhase 0x40000
+#define bXATxAGC 0x3f
+#define bXBTxAGC 0xf00
+#define bXCTxAGC 0xf000
+#define bXDTxAGC 0xf0000
+#define bPAStart 0xf0000000
+#define bTRStart 0x00f00000
+#define bRFStart 0x0000f000
+#define bBBStart 0x000000f0
+#define bBBCCKStart 0x0000000f
+#define bPAEnd 0xf /* Reg0x814 */
+#define bTREnd 0x0f000000
+#define bRFEnd 0x000f0000
+#define bCCAMask 0x000000f0 /* T2R */
+#define bR2RCCAMask 0x00000f00
+#define bHSSI_R2TDelay 0xf8000000
+#define bHSSI_T2RDelay 0xf80000
+#define bContTxHSSI 0x400 /* chane gain at continue Tx */
+#define bIGFromCCK 0x200
+#define bAGCAddress 0x3f
+#define bRxHPTx 0x7000
+#define bRxHPT2R 0x38000
+#define bRxHPCCKIni 0xc0000
+#define bAGCTxCode 0xc00000
+#define bAGCRxCode 0x300000
+#define b3WireDataLength 0x800
+#define b3WireAddressLength 0x400
+#define b3WireRFPowerDown 0x1
+/* #define bHWSISelect 0x8 */
+#define b5GPAPEPolarity 0x40000000
+#define b2GPAPEPolarity 0x80000000
+#define bRFSW_TxDefaultAnt 0x3
+#define bRFSW_TxOptionAnt 0x30
+#define bRFSW_RxDefaultAnt 0x300
+#define bRFSW_RxOptionAnt 0x3000
+#define bRFSI_3WireData 0x1
+#define bRFSI_3WireClock 0x2
+#define bRFSI_3WireLoad 0x4
+#define bRFSI_3WireRW 0x8
+#define bRFSI_3Wire 0xf /* 3-wire total control */
+#define bRFSI_RFENV 0x10
+#define bRFSI_TRSW 0x20
+#define bRFSI_TRSWB 0x40
+#define bRFSI_ANTSW 0x100
+#define bRFSI_ANTSWB 0x200
+#define bRFSI_PAPE 0x400
+#define bRFSI_PAPE5G 0x800
+#define bBandSelect 0x1
+#define bHTSIG2_GI 0x80
+#define bHTSIG2_Smoothing 0x01
+#define bHTSIG2_Sounding 0x02
+#define bHTSIG2_Aggreaton 0x08
+#define bHTSIG2_STBC 0x30
+#define bHTSIG2_AdvCoding 0x40
+#define bHTSIG2_NumOfHTLTF 0x300
+#define bHTSIG2_CRC8 0x3fc
+#define bHTSIG1_MCS 0x7f
+#define bHTSIG1_BandWidth 0x80
+#define bHTSIG1_HTLength 0xffff
+#define bLSIG_Rate 0xf
+#define bLSIG_Reserved 0x10
+#define bLSIG_Length 0x1fffe
+#define bLSIG_Parity 0x20
+#define bCCKRxPhase 0x4
+#define bLSSIReadAddress 0x3f000000 /* LSSI "Read" Address */
+#define bLSSIReadEdge 0x80000000 /* LSSI "Read" edge signal */
+#define bLSSIReadBackData 0xfff
+#define bLSSIReadOKFlag 0x1000
+#define bCCKSampleRate 0x8 /* 0: 44MHz, 1:88MHz */
+#define bRegulator0Standby 0x1
+#define bRegulatorPLLStandby 0x2
+#define bRegulator1Standby 0x4
+#define bPLLPowerUp 0x8
+#define bDPLLPowerUp 0x10
+#define bDA10PowerUp 0x20
+#define bAD7PowerUp 0x200
+#define bDA6PowerUp 0x2000
+#define bXtalPowerUp 0x4000
+#define b40MDClkPowerUP 0x8000
+#define bDA6DebugMode 0x20000
+#define bDA6Swing 0x380000
+#define bADClkPhase 0x4000000
+#define b80MClkDelay 0x18000000
+#define bAFEWatchDogEnable 0x20000000
+#define bXtalCap 0x0f000000
+#define bIntDifClkEnable 0x400
+#define bExtSigClkEnable 0x800
+#define bBandgapMbiasPowerUp 0x10000
+#define bAD11SHGain 0xc0000
+#define bAD11InputRange 0x700000
+#define bAD11OPCurrent 0x3800000
+#define bIPathLoopback 0x4000000
+#define bQPathLoopback 0x8000000
+#define bAFELoopback 0x10000000
+#define bDA10Swing 0x7e0
+#define bDA10Reverse 0x800
+#define bDAClkSource 0x1000
+#define bAD7InputRange 0x6000
+#define bAD7Gain 0x38000
+#define bAD7OutputCMMode 0x40000
+#define bAD7InputCMMode 0x380000
+#define bAD7Current 0xc00000
+#define bRegulatorAdjust 0x7000000
+#define bAD11PowerUpAtTx 0x1
+#define bDA10PSAtTx 0x10
+#define bAD11PowerUpAtRx 0x100
+#define bDA10PSAtRx 0x1000
+
+#define bCCKRxAGCFormat 0x200
+
+#define bPSDFFTSamplepPoint 0xc000
+#define bPSDAverageNum 0x3000
+#define bIQPathControl 0xc00
+#define bPSDFreq 0x3ff
+#define bPSDAntennaPath 0x30
+#define bPSDIQSwitch 0x40
+#define bPSDRxTrigger 0x400000
+#define bPSDTxTrigger 0x80000000
+#define bPSDSineToneScale 0x7f000000
+#define bPSDReport 0xffff
+
+/* page-9 */
+#define bOFDMTxSC 0x30000000
+#define bCCKTxOn 0x1
+#define bOFDMTxOn 0x2
+#define bDebugPage 0xfff /* reset debug page and also HWord, LWord */
+#define bDebugItem 0xff /* reset debug page and LWord */
+#define bAntL 0x10
+#define bAntNonHT 0x100
+#define bAntHT1 0x1000
+#define bAntHT2 0x10000
+#define bAntHT1S1 0x100000
+#define bAntNonHTS1 0x1000000
+
+/* page-a */
+#define bCCKBBMode 0x3
+#define bCCKTxPowerSaving 0x80
+#define bCCKRxPowerSaving 0x40
+#define bCCKSideBand 0x10
+#define bCCKScramble 0x8
+#define bCCKAntDiversity 0x8000
+#define bCCKCarrierRecovery 0x4000
+#define bCCKTxRate 0x3000
+#define bCCKDCCancel 0x0800
+#define bCCKISICancel 0x0400
+#define bCCKMatchFilter 0x0200
+#define bCCKEqualizer 0x0100
+#define bCCKPreambleDetect 0x800000
+#define bCCKFastFalseCCA 0x400000
+#define bCCKChEstStart 0x300000
+#define bCCKCCACount 0x080000
+#define bCCKcs_lim 0x070000
+#define bCCKBistMode 0x80000000
+#define bCCKCCAMask 0x40000000
+#define bCCKTxDACPhase 0x4
+#define bCCKRxADCPhase 0x20000000 /* r_rx_clk */
+#define bCCKr_cp_mode0 0x0100
+#define bCCKTxDCOffset 0xf0
+#define bCCKRxDCOffset 0xf
+#define bCCKCCAMode 0xc000
+#define bCCKFalseCS_lim 0x3f00
+#define bCCKCS_ratio 0xc00000
+#define bCCKCorgBit_sel 0x300000
+#define bCCKPD_lim 0x0f0000
+#define bCCKNewCCA 0x80000000
+#define bCCKRxHPofIG 0x8000
+#define bCCKRxIG 0x7f00
+#define bCCKLNAPolarity 0x800000
+#define bCCKRx1stGain 0x7f0000
+#define bCCKRFExtend 0x20000000 /* CCK Rx initial gain polarity */
+#define bCCKRxAGCSatLevel 0x1f000000
+#define bCCKRxAGCSatCount 0xe0
+#define bCCKRxRFSettle 0x1f /* AGCsamp_dly */
+#define bCCKFixedRxAGC 0x8000
+/* #define bCCKRxAGCFormat 0x4000 */ /* remove to HSSI register 0x824 */
+#define bCCKAntennaPolarity 0x2000
+#define bCCKTxFilterType 0x0c00
+#define bCCKRxAGCReportType 0x0300
+#define bCCKRxDAGCEn 0x80000000
+#define bCCKRxDAGCPeriod 0x20000000
+#define bCCKRxDAGCSatLevel 0x1f000000
+#define bCCKTimingRecovery 0x800000
+#define bCCKTxC0 0x3f0000
+#define bCCKTxC1 0x3f000000
+#define bCCKTxC2 0x3f
+#define bCCKTxC3 0x3f00
+#define bCCKTxC4 0x3f0000
+#define bCCKTxC5 0x3f000000
+#define bCCKTxC6 0x3f
+#define bCCKTxC7 0x3f00
+#define bCCKDebugPort 0xff0000
+#define bCCKDACDebug 0x0f000000
+#define bCCKFalseAlarmEnable 0x8000
+#define bCCKFalseAlarmRead 0x4000
+#define bCCKTRSSI 0x7f
+#define bCCKRxAGCReport 0xfe
+#define bCCKRxReport_AntSel 0x80000000
+#define bCCKRxReport_MFOff 0x40000000
+#define bCCKRxRxReport_SQLoss 0x20000000
+#define bCCKRxReport_Pktloss 0x10000000
+#define bCCKRxReport_Lockedbit 0x08000000
+#define bCCKRxReport_RateError 0x04000000
+#define bCCKRxReport_RxRate 0x03000000
+#define bCCKRxFACounterLower 0xff
+#define bCCKRxFACounterUpper 0xff000000
+#define bCCKRxHPAGCStart 0xe000
+#define bCCKRxHPAGCFinal 0x1c00
+
+#define bCCKRxFalseAlarmEnable 0x8000
+#define bCCKFACounterFreeze 0x4000
+
+#define bCCKTxPathSel 0x10000000
+#define bCCKDefaultRxPath 0xc000000
+#define bCCKOptionRxPath 0x3000000
+
+/* page c */
+#define bNumOfSTF 0x3
+#define bShift_L 0xc0
+#define bGI_TH 0xc
+#define bRxPathA 0x1
+#define bRxPathB 0x2
+#define bRxPathC 0x4
+#define bRxPathD 0x8
+#define bTxPathA 0x1
+#define bTxPathB 0x2
+#define bTxPathC 0x4
+#define bTxPathD 0x8
+#define bTRSSIFreq 0x200
+#define bADCBackoff 0x3000
+#define bDFIRBackoff 0xc000
+#define bTRSSILatchPhase 0x10000
+#define bRxIDCOffset 0xff
+#define bRxQDCOffset 0xff00
+#define bRxDFIRMode 0x1800000
+#define bRxDCNFType 0xe000000
+#define bRXIQImb_A 0x3ff
+#define bRXIQImb_B 0xfc00
+#define bRXIQImb_C 0x3f0000
+#define bRXIQImb_D 0xffc00000
+#define bDC_dc_Notch 0x60000
+#define bRxNBINotch 0x1f000000
+#define bPD_TH 0xf
+#define bPD_TH_Opt2 0xc000
+#define bPWED_TH 0x700
+#define bIfMF_Win_L 0x800
+#define bPD_Option 0x1000
+#define bMF_Win_L 0xe000
+#define bBW_Search_L 0x30000
+#define bwin_enh_L 0xc0000
+#define bBW_TH 0x700000
+#define bED_TH2 0x3800000
+#define bBW_option 0x4000000
+#define bRatio_TH 0x18000000
+#define bWindow_L 0xe0000000
+#define bSBD_Option 0x1
+#define bFrame_TH 0x1c
+#define bFS_Option 0x60
+#define bDC_Slope_check 0x80
+#define bFGuard_Counter_DC_L 0xe00
+#define bFrame_Weight_Short 0x7000
+#define bSub_Tune 0xe00000
+#define bFrame_DC_Length 0xe000000
+#define bSBD_start_offset 0x30000000
+#define bFrame_TH_2 0x7
+#define bFrame_GI2_TH 0x38
+#define bGI2_Sync_en 0x40
+#define bSarch_Short_Early 0x300
+#define bSarch_Short_Late 0xc00
+#define bSarch_GI2_Late 0x70000
+#define bCFOAntSum 0x1
+#define bCFOAcc 0x2
+#define bCFOStartOffset 0xc
+#define bCFOLookBack 0x70
+#define bCFOSumWeight 0x80
+#define bDAGCEnable 0x10000
+#define bTXIQImb_A 0x3ff
+#define bTXIQImb_B 0xfc00
+#define bTXIQImb_C 0x3f0000
+#define bTXIQImb_D 0xffc00000
+#define bTxIDCOffset 0xff
+#define bTxQDCOffset 0xff00
+#define bTxDFIRMode 0x10000
+#define bTxPesudoNoiseOn 0x4000000
+#define bTxPesudoNoise_A 0xff
+#define bTxPesudoNoise_B 0xff00
+#define bTxPesudoNoise_C 0xff0000
+#define bTxPesudoNoise_D 0xff000000
+#define bCCADropOption 0x20000
+#define bCCADropThres 0xfff00000
+#define bEDCCA_H 0xf
+#define bEDCCA_L 0xf0
+#define bLambda_ED 0x300
+#define bRxInitialGain 0x7f
+#define bRxAntDivEn 0x80
+#define bRxAGCAddressForLNA 0x7f00
+#define bRxHighPowerFlow 0x8000
+#define bRxAGCFreezeThres 0xc0000
+#define bRxFreezeStep_AGC1 0x300000
+#define bRxFreezeStep_AGC2 0xc00000
+#define bRxFreezeStep_AGC3 0x3000000
+#define bRxFreezeStep_AGC0 0xc000000
+#define bRxRssi_Cmp_En 0x10000000
+#define bRxQuickAGCEn 0x20000000
+#define bRxAGCFreezeThresMode 0x40000000
+#define bRxOverFlowCheckType 0x80000000
+#define bRxAGCShift 0x7f
+#define bTRSW_Tri_Only 0x80
+#define bPowerThres 0x300
+#define bRxAGCEn 0x1
+#define bRxAGCTogetherEn 0x2
+#define bRxAGCMin 0x4
+#define bRxHP_Ini 0x7
+#define bRxHP_TRLNA 0x70
+#define bRxHP_RSSI 0x700
+#define bRxHP_BBP1 0x7000
+#define bRxHP_BBP2 0x70000
+#define bRxHP_BBP3 0x700000
+#define bRSSI_H 0x7f0000 /* the threshold for high power */
+#define bRSSI_Gen 0x7f000000 /* the threshold for ant diversity */
+#define bRxSettle_TRSW 0x7
+#define bRxSettle_LNA 0x38
+#define bRxSettle_RSSI 0x1c0
+#define bRxSettle_BBP 0xe00
+#define bRxSettle_RxHP 0x7000
+#define bRxSettle_AntSW_RSSI 0x38000
+#define bRxSettle_AntSW 0xc0000
+#define bRxProcessTime_DAGC 0x300000
+#define bRxSettle_HSSI 0x400000
+#define bRxProcessTime_BBPPW 0x800000
+#define bRxAntennaPowerShift 0x3000000
+#define bRSSITableSelect 0xc000000
+#define bRxHP_Final 0x7000000
+#define bRxHTSettle_BBP 0x7
+#define bRxHTSettle_HSSI 0x8
+#define bRxHTSettle_RxHP 0x70
+#define bRxHTSettle_BBPPW 0x80
+#define bRxHTSettle_Idle 0x300
+#define bRxHTSettle_Reserved 0x1c00
+#define bRxHTRxHPEn 0x8000
+#define bRxHTAGCFreezeThres 0x30000
+#define bRxHTAGCTogetherEn 0x40000
+#define bRxHTAGCMin 0x80000
+#define bRxHTAGCEn 0x100000
+#define bRxHTDAGCEn 0x200000
+#define bRxHTRxHP_BBP 0x1c00000
+#define bRxHTRxHP_Final 0xe0000000
+#define bRxPWRatioTH 0x3
+#define bRxPWRatioEn 0x4
+#define bRxMFHold 0x3800
+#define bRxPD_Delay_TH1 0x38
+#define bRxPD_Delay_TH2 0x1c0
+#define bRxPD_DC_COUNT_MAX 0x600
+/* #define bRxMF_Hold 0x3800 */
+#define bRxPD_Delay_TH 0x8000
+#define bRxProcess_Delay 0xf0000
+#define bRxSearchrange_GI2_Early 0x700000
+#define bRxFrame_Guard_Counter_L 0x3800000
+#define bRxSGI_Guard_L 0xc000000
+#define bRxSGI_Search_L 0x30000000
+#define bRxSGI_TH 0xc0000000
+#define bDFSCnt0 0xff
+#define bDFSCnt1 0xff00
+#define bDFSFlag 0xf0000
+
+#define bMFWeightSum 0x300000
+#define bMinIdxTH 0x7f000000
+
+#define bDAFormat 0x40000
+
+#define bTxChEmuEnable 0x01000000
+
+#define bTRSWIsolation_A 0x7f
+#define bTRSWIsolation_B 0x7f00
+#define bTRSWIsolation_C 0x7f0000
+#define bTRSWIsolation_D 0x7f000000
+
+#define bExtLNAGain 0x7c00
+
+/* page d */
+#define bSTBCEn 0x4
+#define bAntennaMapping 0x10
+#define bNss 0x20
+#define bCFOAntSumD 0x200
+#define bPHYCounterReset 0x8000000
+#define bCFOReportGet 0x4000000
+#define bOFDMContinueTx 0x10000000
+#define bOFDMSingleCarrier 0x20000000
+#define bOFDMSingleTone 0x40000000
+/* #define bRxPath1 0x01
+ * #define bRxPath2 0x02
+ * #define bRxPath3 0x04
+ * #define bRxPath4 0x08
+ * #define bTxPath1 0x10
+ * #define bTxPath2 0x20
+ */
+#define bHTDetect 0x100
+#define bCFOEn 0x10000
+#define bCFOValue 0xfff00000
+#define bSigTone_Re 0x3f
+#define bSigTone_Im 0x7f00
+#define bCounter_CCA 0xffff
+#define bCounter_ParityFail 0xffff0000
+#define bCounter_RateIllegal 0xffff
+#define bCounter_CRC8Fail 0xffff0000
+#define bCounter_MCSNoSupport 0xffff
+#define bCounter_FastSync 0xffff
+#define bShortCFO 0xfff
+#define bShortCFOTLength 12 /* total */
+#define bShortCFOFLength 11 /* fraction */
+#define bLongCFO 0x7ff
+#define bLongCFOTLength 11
+#define bLongCFOFLength 11
+#define bTailCFO 0x1fff
+#define bTailCFOTLength 13
+#define bTailCFOFLength 12
+
+#define bmax_en_pwdB 0xffff
+#define bCC_power_dB 0xffff0000
+#define bnoise_pwdB 0xffff
+#define bPowerMeasTLength 10
+#define bPowerMeasFLength 3
+#define bRx_HT_BW 0x1
+#define bRxSC 0x6
+#define bRx_HT 0x8
+
+#define bNB_intf_det_on 0x1
+#define bIntf_win_len_cfg 0x30
+#define bNB_Intf_TH_cfg 0x1c0
+
+#define bRFGain 0x3f
+#define bTableSel 0x40
+#define bTRSW 0x80
+
+#define bRxSNR_A 0xff
+#define bRxSNR_B 0xff00
+#define bRxSNR_C 0xff0000
+#define bRxSNR_D 0xff000000
+#define bSNREVMTLength 8
+#define bSNREVMFLength 1
+
+#define bCSI1st 0xff
+#define bCSI2nd 0xff00
+#define bRxEVM1st 0xff0000
+#define bRxEVM2nd 0xff000000
+
+#define bSIGEVM 0xff
+#define bPWDB 0xff00
+#define bSGIEN 0x10000
+
+#define bSFactorQAM1 0xf
+#define bSFactorQAM2 0xf0
+#define bSFactorQAM3 0xf00
+#define bSFactorQAM4 0xf000
+#define bSFactorQAM5 0xf0000
+#define bSFactorQAM6 0xf0000
+#define bSFactorQAM7 0xf00000
+#define bSFactorQAM8 0xf000000
+#define bSFactorQAM9 0xf0000000
+#define bCSIScheme 0x100000
+
+#define bNoiseLvlTopSet 0x3
+#define bChSmooth 0x4
+#define bChSmoothCfg1 0x38
+#define bChSmoothCfg2 0x1c0
+#define bChSmoothCfg3 0xe00
+#define bChSmoothCfg4 0x7000
+#define bMRCMode 0x800000
+#define bTHEVMCfg 0x7000000
+
+#define bLoopFitType 0x1
+#define bUpdCFO 0x40
+#define bUpdCFOOffData 0x80
+#define bAdvUpdCFO 0x100
+#define bAdvTimeCtrl 0x800
+#define bUpdClko 0x1000
+#define bFC 0x6000
+#define bTrackingMode 0x8000
+#define bPhCmpEnable 0x10000
+#define bUpdClkoLTF 0x20000
+#define bComChCFO 0x40000
+#define bCSIEstiMode 0x80000
+#define bAdvUpdEqz 0x100000
+#define bUChCfg 0x7000000
+#define bUpdEqz 0x8000000
+
+/* page e */
+#define bTxAGCRate18_06 0x7f7f7f7f
+#define bTxAGCRate54_24 0x7f7f7f7f
+#define bTxAGCRateMCS32 0x7f
+#define bTxAGCRateCCK 0x7f00
+#define bTxAGCRateMCS3_MCS0 0x7f7f7f7f
+#define bTxAGCRateMCS7_MCS4 0x7f7f7f7f
+#define bTxAGCRateMCS11_MCS8 0x7f7f7f7f
+#define bTxAGCRateMCS15_MCS12 0x7f7f7f7f
+
+
+/* Rx Pseduo noise */
+#define bRxPesudoNoiseOn 0x20000000
+#define bRxPesudoNoise_A 0xff
+#define bRxPesudoNoise_B 0xff00
+#define bRxPesudoNoise_C 0xff0000
+#define bRxPesudoNoise_D 0xff000000
+#define bPesudoNoiseState_A 0xffff
+#define bPesudoNoiseState_B 0xffff0000
+#define bPesudoNoiseState_C 0xffff
+#define bPesudoNoiseState_D 0xffff0000
+
+/* RF
+ * Zebra1
+ */
+#define bZebra1_HSSIEnable 0x8
+#define bZebra1_TRxControl 0xc00
+#define bZebra1_TRxGainSetting 0x07f
+#define bZebra1_RxCorner 0xc00
+#define bZebra1_TxChargePump 0x38
+#define bZebra1_RxChargePump 0x7
+#define bZebra1_ChannelNum 0xf80
+#define bZebra1_TxLPFBW 0x400
+#define bZebra1_RxLPFBW 0x600
+
+/* Zebra4 */
+#define bRTL8256RegModeCtrl1 0x100
+#define bRTL8256RegModeCtrl0 0x40
+#define bRTL8256_TxLPFBW 0x18
+#define bRTL8256_RxLPFBW 0x600
+
+/* RTL8258 */
+#define bRTL8258_TxLPFBW 0xc
+#define bRTL8258_RxLPFBW 0xc00
+#define bRTL8258_RSSILPFBW 0xc0
+
+/* byte endable for sb_write */
+#define bByte0 0x1
+#define bByte1 0x2
+#define bByte2 0x4
+#define bByte3 0x8
+#define bWord0 0x3
+#define bWord1 0xc
+#define bDWord 0xf
+
+/* for PutRegsetting & GetRegSetting BitMask */
+#define bMaskByte0 0xff
+#define bMaskByte1 0xff00
+#define bMaskByte2 0xff0000
+#define bMaskByte3 0xff000000
+#define bMaskHWord 0xffff0000
+#define bMaskLWord 0x0000ffff
+#define bMaskDWord 0xffffffff
+
+/* for PutRFRegsetting & GetRFRegSetting BitMask */
+#define bMask12Bits 0xfff
+
+#define bEnable 0x1
+#define bDisable 0x0
+
+#define LeftAntenna 0x0
+#define RightAntenna 0x1
+
+#define tCheckTxStatus 500 /* 500ms */
+#define tUpdateRxCounter 100 /* 100ms */
+
+#define rateCCK 0
+#define rateOFDM 1
+#define rateHT 2
+
+/* define Register-End */
+#define bPMAC_End 0x1ff
+#define bFPGAPHY0_End 0x8ff
+#define bFPGAPHY1_End 0x9ff
+#define bCCKPHY0_End 0xaff
+#define bOFDMPHY0_End 0xcff
+#define bOFDMPHY1_End 0xdff
+
+/* define max debug item in each debug page
+ * #define bMaxItem_FPGA_PHY0 0x9
+ * #define bMaxItem_FPGA_PHY1 0x3
+ * #define bMaxItem_PHY_11B 0x16
+ * #define bMaxItem_OFDM_PHY0 0x29
+ * #define bMaxItem_OFDM_PHY1 0x0
+ */
+
+#define bPMACControl 0x0
+#define bWMACControl 0x1
+#define bWNICControl 0x2
+
+#define PathA 0x0
+#define PathB 0x1
+#define PathC 0x2
+#define PathD 0x3
+
+#define rRTL8256RxMixerPole 0xb
+#define bZebraRxMixerPole 0x6
+#define rRTL8256TxBBOPBias 0x9
+#define bRTL8256TxBBOPBias 0x400
+#define rRTL8256TxBBBW 19
+#define bRTL8256TxBBBW 0x18
+
+#endif /* __INC_HAL8190PCIPHYREG_H */
diff --git a/drivers/staging/rtl8712/Kconfig b/drivers/staging/rtl8712/Kconfig
new file mode 100644
index 000000000..f160eee52
--- /dev/null
+++ b/drivers/staging/rtl8712/Kconfig
@@ -0,0 +1,17 @@
+config R8712U
+ tristate "RealTek RTL8712U (RTL8192SU) Wireless LAN NIC driver"
+ depends on WLAN && USB
+ select WIRELESS_EXT
+ select WEXT_PRIV
+ select FW_LOADER
+ ---help---
+ This option adds the Realtek RTL8712 USB device such as the D-Link DWA-130.
+ If built as a module, it will be called r8712u.
+
+config R8712_TX_AGGR
+ bool "Realtek RTL8712U Transmit Aggregation code"
+ depends on R8712U && BROKEN
+ ---help---
+ This option provides transmit aggregation for the Realtek RTL8712 USB device.
+
+
diff --git a/drivers/staging/rtl8712/Makefile b/drivers/staging/rtl8712/Makefile
new file mode 100644
index 000000000..6f8500c2d
--- /dev/null
+++ b/drivers/staging/rtl8712/Makefile
@@ -0,0 +1,34 @@
+r8712u-y := \
+ rtl871x_cmd.o \
+ rtl8712_cmd.o \
+ rtl871x_security.o \
+ rtl871x_eeprom.o \
+ rtl8712_efuse.o \
+ hal_init.o \
+ usb_halinit.o \
+ usb_ops.o \
+ usb_ops_linux.o \
+ rtl871x_io.o \
+ rtl8712_io.o \
+ rtl871x_ioctl_linux.o \
+ rtl871x_ioctl_rtl.o \
+ rtl871x_ioctl_set.o \
+ rtl8712_led.o \
+ rtl871x_mlme.o \
+ ieee80211.o \
+ rtl871x_mp_ioctl.o \
+ rtl871x_mp.o \
+ mlme_linux.o \
+ recv_linux.o \
+ xmit_linux.o \
+ usb_intf.o \
+ os_intfs.o \
+ rtl871x_pwrctrl.o \
+ rtl8712_recv.o \
+ rtl871x_recv.o \
+ rtl871x_sta_mgt.o \
+ rtl871x_xmit.o \
+ rtl8712_xmit.o
+
+obj-$(CONFIG_R8712U) := r8712u.o
+
diff --git a/drivers/staging/rtl8712/TODO b/drivers/staging/rtl8712/TODO
new file mode 100644
index 000000000..d8dfe5bfe
--- /dev/null
+++ b/drivers/staging/rtl8712/TODO
@@ -0,0 +1,13 @@
+TODO:
+- merge Realtek's bugfixes and new features into the driver
+- switch to use LIB80211
+- switch to use MAC80211
+- checkpatch.pl fixes - only a few remain
+
+Please send any patches to Greg Kroah-Hartman <greg@kroah.com>,
+Larry Finger <Larry.Finger@lwfinger.net> and
+Florian Schilhabel <florian.c.schilhabel@googlemail.com>.
+
+
+
+
diff --git a/drivers/staging/rtl8712/basic_types.h b/drivers/staging/rtl8712/basic_types.h
new file mode 100644
index 000000000..7561bed5d
--- /dev/null
+++ b/drivers/staging/rtl8712/basic_types.h
@@ -0,0 +1,48 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2010 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ * Modifications for inclusion into the Linux staging tree are
+ * Copyright(c) 2010 Larry Finger. All rights reserved.
+ *
+ * Contact information:
+ * WLAN FAE <wlanfae@realtek.com>
+ * Larry Finger <Larry.Finger@lwfinger.net>
+ *
+ ******************************************************************************/
+#ifndef __BASIC_TYPES_H__
+#define __BASIC_TYPES_H__
+
+#define SUCCESS 0
+#define FAIL (-1)
+
+#include <linux/types.h>
+
+#define SIZE_T __kernel_size_t
+#define sint signed int
+#define FIELD_OFFSET(s, field) ((addr_t)&((s *)(0))->field)
+
+/* Should we extend this to be host_addr_t and target_addr_t for case:
+ * host : x86_64
+ * target : mips64
+ */
+#define addr_t unsigned long
+
+#define MEM_ALIGNMENT_OFFSET (sizeof(SIZE_T))
+#define MEM_ALIGNMENT_PADDING (sizeof(SIZE_T) - 1)
+
+#endif /*__BASIC_TYPES_H__*/
+
diff --git a/drivers/staging/rtl8712/drv_types.h b/drivers/staging/rtl8712/drv_types.h
new file mode 100644
index 000000000..e62543d22
--- /dev/null
+++ b/drivers/staging/rtl8712/drv_types.h
@@ -0,0 +1,193 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2010 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ * Modifications for inclusion into the Linux staging tree are
+ * Copyright(c) 2010 Larry Finger. All rights reserved.
+ *
+ * Contact information:
+ * WLAN FAE <wlanfae@realtek.com>
+ * Larry Finger <Larry.Finger@lwfinger.net>
+ *
+ ******************************************************************************/
+/*---------------------------------------------------------------------
+
+ For type defines and data structure defines
+
+-----------------------------------------------------------------------*/
+#ifndef __DRV_TYPES_H__
+#define __DRV_TYPES_H__
+
+struct _adapter;
+
+#include "osdep_service.h"
+#include "wlan_bssdef.h"
+#include "rtl8712_spec.h"
+#include "rtl8712_hal.h"
+#include <linux/mutex.h>
+#include <linux/completion.h>
+
+enum _NIC_VERSION {
+ RTL8711_NIC,
+ RTL8712_NIC,
+ RTL8713_NIC,
+ RTL8716_NIC
+};
+
+struct _adapter;
+
+struct qos_priv {
+ /* bit mask option: u-apsd, s-apsd, ts, block ack... */
+ unsigned int qos_option;
+};
+
+#include "rtl871x_ht.h"
+#include "rtl871x_cmd.h"
+#include "rtl871x_xmit.h"
+#include "rtl871x_recv.h"
+#include "rtl871x_security.h"
+#include "rtl871x_pwrctrl.h"
+#include "rtl871x_io.h"
+#include "rtl871x_eeprom.h"
+#include "sta_info.h"
+#include "rtl871x_mlme.h"
+#include "rtl871x_mp.h"
+#include "rtl871x_debug.h"
+#include "rtl871x_rf.h"
+#include "rtl871x_event.h"
+#include "rtl871x_led.h"
+
+#define SPEC_DEV_ID_DISABLE_HT BIT(1)
+
+struct specific_device_id {
+ u32 flags;
+ u16 idVendor;
+ u16 idProduct;
+
+};
+
+struct registry_priv {
+ u8 chip_version;
+ u8 rfintfs;
+ u8 lbkmode;
+ u8 hci;
+ u8 network_mode; /*infra, ad-hoc, auto*/
+ struct ndis_802_11_ssid ssid;
+ u8 channel;/* ad-hoc support requirement */
+ u8 wireless_mode;/* A, B, G, auto */
+ u8 vrtl_carrier_sense; /*Enable, Disable, Auto*/
+ u8 vcs_type;/*RTS/CTS, CTS-to-self*/
+ u16 rts_thresh;
+ u16 frag_thresh;
+ u8 preamble;/*long, short, auto*/
+ u8 scan_mode;/*active, passive*/
+ u8 adhoc_tx_pwr;
+ u8 soft_ap;
+ u8 smart_ps;
+ u8 power_mgnt;
+ u8 radio_enable;
+ u8 long_retry_lmt;
+ u8 short_retry_lmt;
+ u16 busy_thresh;
+ u8 ack_policy;
+ u8 mp_mode;
+ u8 software_encrypt;
+ u8 software_decrypt;
+ /* UAPSD */
+ u8 wmm_enable;
+ u8 uapsd_enable;
+ u8 uapsd_max_sp;
+ u8 uapsd_acbk_en;
+ u8 uapsd_acbe_en;
+ u8 uapsd_acvi_en;
+ u8 uapsd_acvo_en;
+
+ struct wlan_bssid_ex dev_network;
+
+ u8 ht_enable;
+ u8 cbw40_enable;
+ u8 ampdu_enable;/*for tx*/
+ u8 rf_config;
+ u8 low_power;
+ u8 wifi_test;
+};
+
+struct dvobj_priv {
+ struct _adapter *padapter;
+ u32 nr_endpoint;
+ u8 ishighspeed;
+ uint (*inirp_init)(struct _adapter *adapter);
+ uint (*inirp_deinit)(struct _adapter *adapter);
+ struct usb_device *pusbdev;
+};
+
+/**
+ * struct _adapter - the main adapter structure for this device.
+ *
+ * bup: True indicates that the interface is up.
+ */
+struct _adapter {
+ struct dvobj_priv dvobjpriv;
+ struct mlme_priv mlmepriv;
+ struct cmd_priv cmdpriv;
+ struct evt_priv evtpriv;
+ struct io_queue *pio_queue;
+ struct xmit_priv xmitpriv;
+ struct recv_priv recvpriv;
+ struct sta_priv stapriv;
+ struct security_priv securitypriv;
+ struct registry_priv registrypriv;
+ struct wlan_acl_pool acl_list;
+ struct pwrctrl_priv pwrctrlpriv;
+ struct eeprom_priv eeprompriv;
+ struct hal_priv halpriv;
+ struct led_priv ledpriv;
+ struct mp_priv mppriv;
+ s32 bDriverStopped;
+ s32 bSurpriseRemoved;
+ u32 IsrContent;
+ u32 ImrContent;
+ u8 EepromAddressSize;
+ u8 hw_init_completed;
+ struct task_struct *cmdThread;
+ pid_t evtThread;
+ struct task_struct *xmitThread;
+ pid_t recvThread;
+ uint (*dvobj_init)(struct _adapter *adapter);
+ void (*dvobj_deinit)(struct _adapter *adapter);
+ struct net_device *pnetdev;
+ int bup;
+ struct net_device_stats stats;
+ struct iw_statistics iwstats;
+ int pid; /*process id from UI*/
+ struct work_struct wkFilterRxFF0;
+ u8 blnEnableRxFF0Filter;
+ spinlock_t lockRxFF0Filter;
+ const struct firmware *fw;
+ struct usb_interface *pusb_intf;
+ struct mutex mutex_start;
+ struct completion rtl8712_fw_ready;
+};
+
+static inline u8 *myid(struct eeprom_priv *peepriv)
+{
+ return peepriv->mac_addr;
+}
+
+u8 r8712_usb_hal_bus_init(struct _adapter *adapter);
+
+#endif /*__DRV_TYPES_H__*/
+
diff --git a/drivers/staging/rtl8712/ethernet.h b/drivers/staging/rtl8712/ethernet.h
new file mode 100644
index 000000000..fad173f40
--- /dev/null
+++ b/drivers/staging/rtl8712/ethernet.h
@@ -0,0 +1,33 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2010 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ * Modifications for inclusion into the Linux staging tree are
+ * Copyright(c) 2010 Larry Finger. All rights reserved.
+ *
+ * Contact information:
+ * WLAN FAE <wlanfae@realtek.com>
+ * Larry Finger <Larry.Finger@lwfinger.net>
+ *
+ ******************************************************************************/
+#ifndef __INC_ETHERNET_H
+#define __INC_ETHERNET_H
+
+#define ETHERNET_HEADER_SIZE 14 /*!< Ethernet Header Length*/
+#define LLC_HEADER_SIZE 6 /*!< LLC Header Length*/
+
+#endif /* #ifndef __INC_ETHERNET_H */
+
diff --git a/drivers/staging/rtl8712/hal_init.c b/drivers/staging/rtl8712/hal_init.c
new file mode 100644
index 000000000..59146cec8
--- /dev/null
+++ b/drivers/staging/rtl8712/hal_init.c
@@ -0,0 +1,396 @@
+/******************************************************************************
+ * hal_init.c
+ *
+ * Copyright(c) 2007 - 2010 Realtek Corporation. All rights reserved.
+ * Linux device driver for RTL8192SU
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ * Modifications for inclusion into the Linux staging tree are
+ * Copyright(c) 2010 Larry Finger. All rights reserved.
+ *
+ * Contact information:
+ * WLAN FAE <wlanfae@realtek.com>.
+ * Larry Finger <Larry.Finger@lwfinger.net>
+ *
+ ******************************************************************************/
+
+#define _HAL_INIT_C_
+
+#include <linux/usb.h>
+#include <linux/device.h>
+#include <linux/usb/ch9.h>
+#include <linux/firmware.h>
+#include <linux/module.h>
+
+#include "osdep_service.h"
+#include "drv_types.h"
+#include "usb_osintf.h"
+
+#define FWBUFF_ALIGN_SZ 512
+#define MAX_DUMP_FWSZ 49152 /*default = 49152 (48k)*/
+
+static void rtl871x_load_fw_cb(const struct firmware *firmware, void *context)
+{
+ struct _adapter *padapter = context;
+
+ complete(&padapter->rtl8712_fw_ready);
+ if (!firmware) {
+ struct usb_device *udev = padapter->dvobjpriv.pusbdev;
+ struct usb_interface *pusb_intf = padapter->pusb_intf;
+
+ dev_err(&udev->dev, "r8712u: Firmware request failed\n");
+ usb_put_dev(udev);
+ usb_set_intfdata(pusb_intf, NULL);
+ return;
+ }
+ padapter->fw = firmware;
+ /* firmware available - start netdev */
+ register_netdev(padapter->pnetdev);
+}
+
+static const char firmware_file[] = "/*(DEBLOBBED)*/";
+
+int rtl871x_load_fw(struct _adapter *padapter)
+{
+ struct device *dev = &padapter->dvobjpriv.pusbdev->dev;
+ int rc;
+
+ init_completion(&padapter->rtl8712_fw_ready);
+ dev_info(dev, "r8712u: Loading firmware from \"%s\"\n", firmware_file);
+ rc = reject_firmware_nowait(THIS_MODULE, 1, firmware_file, dev,
+ GFP_KERNEL, padapter, rtl871x_load_fw_cb);
+ if (rc)
+ dev_err(dev, "r8712u: Firmware request error %d\n", rc);
+ return rc;
+}
+/*(DEBLOBBED)*/
+
+static u32 rtl871x_open_fw(struct _adapter *padapter, const u8 **ppmappedfw)
+{
+ const struct firmware **praw = &padapter->fw;
+
+ if (padapter->fw->size > 200000) {
+ dev_err(&padapter->pnetdev->dev, "r8172u: Badfw->size of %d\n",
+ (int)padapter->fw->size);
+ return 0;
+ }
+ *ppmappedfw = (*praw)->data;
+ return (*praw)->size;
+}
+
+static void fill_fwpriv(struct _adapter *padapter, struct fw_priv *pfwpriv)
+{
+ struct dvobj_priv *pdvobj = &padapter->dvobjpriv;
+ struct registry_priv *pregpriv = &padapter->registrypriv;
+
+ memset(pfwpriv, 0, sizeof(struct fw_priv));
+ /* todo: check if needs endian conversion */
+ pfwpriv->hci_sel = RTL8712_HCI_TYPE_72USB;
+ pfwpriv->usb_ep_num = (u8)pdvobj->nr_endpoint;
+ pfwpriv->bw_40MHz_en = pregpriv->cbw40_enable;
+ switch (pregpriv->rf_config) {
+ case RTL8712_RF_1T1R:
+ pfwpriv->rf_config = RTL8712_RFC_1T1R;
+ break;
+ case RTL8712_RF_2T2R:
+ pfwpriv->rf_config = RTL8712_RFC_2T2R;
+ break;
+ case RTL8712_RF_1T2R:
+ default:
+ pfwpriv->rf_config = RTL8712_RFC_1T2R;
+ }
+ pfwpriv->mp_mode = (pregpriv->mp_mode == 1) ? 1 : 0;
+ pfwpriv->vcsType = pregpriv->vrtl_carrier_sense; /* 0:off 1:on 2:auto */
+ pfwpriv->vcsMode = pregpriv->vcs_type; /* 1:RTS/CTS 2:CTS to self */
+ /* default enable turboMode */
+ pfwpriv->turboMode = ((pregpriv->wifi_test == 1) ? 0 : 1);
+ pfwpriv->lowPowerMode = pregpriv->low_power;
+}
+
+static void update_fwhdr(struct fw_hdr *pfwhdr, const u8 *pmappedfw)
+{
+ pfwhdr->signature = le16_to_cpu(*(u16 *)pmappedfw);
+ pfwhdr->version = le16_to_cpu(*(u16 *)(pmappedfw+2));
+ /* define the size of boot loader */
+ pfwhdr->dmem_size = le32_to_cpu(*(uint *)(pmappedfw+4));
+ /* define the size of FW in IMEM */
+ pfwhdr->img_IMEM_size = le32_to_cpu(*(uint *)(pmappedfw+8));
+ /* define the size of FW in SRAM */
+ pfwhdr->img_SRAM_size = le32_to_cpu(*(uint *)(pmappedfw+12));
+ /* define the size of DMEM variable */
+ pfwhdr->fw_priv_sz = le32_to_cpu(*(uint *)(pmappedfw+16));
+}
+
+static u8 chk_fwhdr(struct fw_hdr *pfwhdr, u32 ulfilelength)
+{
+ u32 fwhdrsz, fw_sz;
+
+ /* check signature */
+ if ((pfwhdr->signature != 0x8712) && (pfwhdr->signature != 0x8192))
+ return _FAIL;
+ /* check fw_priv_sze & sizeof(struct fw_priv) */
+ if (pfwhdr->fw_priv_sz != sizeof(struct fw_priv))
+ return _FAIL;
+ /* check fw_sz & image_fw_sz */
+ fwhdrsz = FIELD_OFFSET(struct fw_hdr, fwpriv) + pfwhdr->fw_priv_sz;
+ fw_sz = fwhdrsz + pfwhdr->img_IMEM_size + pfwhdr->img_SRAM_size +
+ pfwhdr->dmem_size;
+ if (fw_sz != ulfilelength)
+ return _FAIL;
+ return _SUCCESS;
+}
+
+static u8 rtl8712_dl_fw(struct _adapter *padapter)
+{
+ sint i;
+ u8 tmp8, tmp8_a;
+ u16 tmp16;
+ u32 maxlen = 0; /* for compare usage */
+ uint dump_imem_sz, imem_sz, dump_emem_sz, emem_sz; /* max = 49152; */
+ struct fw_hdr fwhdr;
+ u32 ulfilelength; /* FW file size */
+ const u8 *pmappedfw = NULL;
+ u8 *ptmpchar = NULL, *ppayload, *ptr;
+ struct tx_desc *ptx_desc;
+ u32 txdscp_sz = sizeof(struct tx_desc);
+ u8 ret = _FAIL;
+
+ ulfilelength = rtl871x_open_fw(padapter, &pmappedfw);
+ if (pmappedfw && (ulfilelength > 0)) {
+ update_fwhdr(&fwhdr, pmappedfw);
+ if (chk_fwhdr(&fwhdr, ulfilelength) == _FAIL)
+ return ret;
+ fill_fwpriv(padapter, &fwhdr.fwpriv);
+ /* firmware check ok */
+ maxlen = (fwhdr.img_IMEM_size > fwhdr.img_SRAM_size) ?
+ fwhdr.img_IMEM_size : fwhdr.img_SRAM_size;
+ maxlen += txdscp_sz;
+ ptmpchar = kmalloc(maxlen + FWBUFF_ALIGN_SZ, GFP_ATOMIC);
+ if (ptmpchar == NULL)
+ return ret;
+
+ ptx_desc = (struct tx_desc *)(ptmpchar + FWBUFF_ALIGN_SZ -
+ ((addr_t)(ptmpchar) & (FWBUFF_ALIGN_SZ - 1)));
+ ppayload = (u8 *)(ptx_desc) + txdscp_sz;
+ ptr = (u8 *)pmappedfw + FIELD_OFFSET(struct fw_hdr, fwpriv) +
+ fwhdr.fw_priv_sz;
+ /* Download FirmWare */
+ /* 1. determine IMEM code size and Load IMEM Code Section */
+ imem_sz = fwhdr.img_IMEM_size;
+ do {
+ memset(ptx_desc, 0, TXDESC_SIZE);
+ if (imem_sz > MAX_DUMP_FWSZ/*49152*/)
+ dump_imem_sz = MAX_DUMP_FWSZ;
+ else {
+ dump_imem_sz = imem_sz;
+ ptx_desc->txdw0 |= cpu_to_le32(BIT(28));
+ }
+ ptx_desc->txdw0 |= cpu_to_le32(dump_imem_sz &
+ 0x0000ffff);
+ memcpy(ppayload, ptr, dump_imem_sz);
+ r8712_write_mem(padapter, RTL8712_DMA_VOQ,
+ dump_imem_sz + TXDESC_SIZE,
+ (u8 *)ptx_desc);
+ ptr += dump_imem_sz;
+ imem_sz -= dump_imem_sz;
+ } while (imem_sz > 0);
+ i = 10;
+ tmp16 = r8712_read16(padapter, TCR);
+ while (((tmp16 & _IMEM_CODE_DONE) == 0) && (i > 0)) {
+ udelay(10);
+ tmp16 = r8712_read16(padapter, TCR);
+ i--;
+ }
+ if (i == 0 || (tmp16 & _IMEM_CHK_RPT) == 0)
+ goto exit_fail;
+
+ /* 2.Download EMEM code size and Load EMEM Code Section */
+ emem_sz = fwhdr.img_SRAM_size;
+ do {
+ memset(ptx_desc, 0, TXDESC_SIZE);
+ if (emem_sz > MAX_DUMP_FWSZ) /* max=48k */
+ dump_emem_sz = MAX_DUMP_FWSZ;
+ else {
+ dump_emem_sz = emem_sz;
+ ptx_desc->txdw0 |= cpu_to_le32(BIT(28));
+ }
+ ptx_desc->txdw0 |= cpu_to_le32(dump_emem_sz &
+ 0x0000ffff);
+ memcpy(ppayload, ptr, dump_emem_sz);
+ r8712_write_mem(padapter, RTL8712_DMA_VOQ,
+ dump_emem_sz+TXDESC_SIZE, (u8 *)ptx_desc);
+ ptr += dump_emem_sz;
+ emem_sz -= dump_emem_sz;
+ } while (emem_sz > 0);
+ i = 5;
+ tmp16 = r8712_read16(padapter, TCR);
+ while (((tmp16 & _EMEM_CODE_DONE) == 0) && (i > 0)) {
+ udelay(10);
+ tmp16 = r8712_read16(padapter, TCR);
+ i--;
+ }
+ if (i == 0 || (tmp16 & _EMEM_CHK_RPT) == 0)
+ goto exit_fail;
+
+ /* 3.Enable CPU */
+ tmp8 = r8712_read8(padapter, SYS_CLKR);
+ r8712_write8(padapter, SYS_CLKR, tmp8|BIT(2));
+ tmp8_a = r8712_read8(padapter, SYS_CLKR);
+ if (tmp8_a != (tmp8|BIT(2)))
+ goto exit_fail;
+
+ tmp8 = r8712_read8(padapter, SYS_FUNC_EN + 1);
+ r8712_write8(padapter, SYS_FUNC_EN+1, tmp8|BIT(2));
+ tmp8_a = r8712_read8(padapter, SYS_FUNC_EN + 1);
+ if (tmp8_a != (tmp8|BIT(2)))
+ goto exit_fail;
+
+ r8712_read32(padapter, TCR);
+
+ /* 4.polling IMEM Ready */
+ i = 100;
+ tmp16 = r8712_read16(padapter, TCR);
+ while (((tmp16 & _IMEM_RDY) == 0) && (i > 0)) {
+ msleep(20);
+ tmp16 = r8712_read16(padapter, TCR);
+ i--;
+ }
+ if (i == 0) {
+ r8712_write16(padapter, 0x10250348, 0xc000);
+ r8712_write16(padapter, 0x10250348, 0xc001);
+ r8712_write16(padapter, 0x10250348, 0x2000);
+ r8712_write16(padapter, 0x10250348, 0x2001);
+ r8712_write16(padapter, 0x10250348, 0x2002);
+ r8712_write16(padapter, 0x10250348, 0x2003);
+ goto exit_fail;
+ }
+ /* 5.Download DMEM code size and Load EMEM Code Section */
+ memset(ptx_desc, 0, TXDESC_SIZE);
+ ptx_desc->txdw0 |= cpu_to_le32(fwhdr.fw_priv_sz&0x0000ffff);
+ ptx_desc->txdw0 |= cpu_to_le32(BIT(28));
+ memcpy(ppayload, &fwhdr.fwpriv, fwhdr.fw_priv_sz);
+ r8712_write_mem(padapter, RTL8712_DMA_VOQ,
+ fwhdr.fw_priv_sz + TXDESC_SIZE, (u8 *)ptx_desc);
+
+ /* polling dmem code done */
+ i = 100;
+ tmp16 = r8712_read16(padapter, TCR);
+ while (((tmp16 & _DMEM_CODE_DONE) == 0) && (i > 0)) {
+ msleep(20);
+ tmp16 = r8712_read16(padapter, TCR);
+ i--;
+ }
+ if (i == 0)
+ goto exit_fail;
+
+ tmp8 = r8712_read8(padapter, 0x1025000A);
+ if (tmp8 & BIT(4)) /* When boot from EEPROM,
+ & FW need more time to read EEPROM */
+ i = 60;
+ else /* boot from EFUSE */
+ i = 30;
+ tmp16 = r8712_read16(padapter, TCR);
+ while (((tmp16 & _FWRDY) == 0) && (i > 0)) {
+ msleep(100);
+ tmp16 = r8712_read16(padapter, TCR);
+ i--;
+ }
+ if (i == 0)
+ goto exit_fail;
+ } else
+ goto exit_fail;
+ ret = _SUCCESS;
+
+exit_fail:
+ kfree(ptmpchar);
+ return ret;
+}
+
+uint rtl8712_hal_init(struct _adapter *padapter)
+{
+ u32 val32;
+ int i;
+
+ /* r8712 firmware download */
+ if (rtl8712_dl_fw(padapter) != _SUCCESS)
+ return _FAIL;
+
+ netdev_info(padapter->pnetdev, "1 RCR=0x%x\n",
+ r8712_read32(padapter, RCR));
+ val32 = r8712_read32(padapter, RCR);
+ r8712_write32(padapter, RCR, (val32 | BIT(26))); /* Enable RX TCP
+ Checksum offload */
+ netdev_info(padapter->pnetdev, "2 RCR=0x%x\n",
+ r8712_read32(padapter, RCR));
+ val32 = r8712_read32(padapter, RCR);
+ r8712_write32(padapter, RCR, (val32|BIT(25))); /* Append PHY status */
+ val32 = 0;
+ val32 = r8712_read32(padapter, 0x10250040);
+ r8712_write32(padapter, 0x10250040, (val32&0x00FFFFFF));
+ /* for usb rx aggregation */
+ r8712_write8(padapter, 0x102500B5, r8712_read8(padapter, 0x102500B5) |
+ BIT(0)); /* page = 128bytes */
+ r8712_write8(padapter, 0x102500BD, r8712_read8(padapter, 0x102500BD) |
+ BIT(7)); /* enable usb rx aggregation */
+ r8712_write8(padapter, 0x102500D9, 1); /* TH=1 => means that invalidate
+ * usb rx aggregation */
+ r8712_write8(padapter, 0x1025FE5B, 0x04); /* 1.7ms/4 */
+ /* Fix the RX FIFO issue(USB error) */
+ r8712_write8(padapter, 0x1025fe5C, r8712_read8(padapter, 0x1025fe5C)
+ | BIT(7));
+ for (i = 0; i < 6; i++)
+ padapter->eeprompriv.mac_addr[i] = r8712_read8(padapter,
+ MACID + i);
+ return _SUCCESS;
+}
+
+uint rtl8712_hal_deinit(struct _adapter *padapter)
+{
+ r8712_write8(padapter, RF_CTRL, 0x00);
+ /* Turn off BB */
+ msleep(20);
+ /* Turn off MAC */
+ r8712_write8(padapter, SYS_CLKR+1, 0x38); /* Switch Control Path */
+ r8712_write8(padapter, SYS_FUNC_EN+1, 0x70);
+ r8712_write8(padapter, PMC_FSM, 0x06); /* Enable Loader Data Keep */
+ r8712_write8(padapter, SYS_ISO_CTRL, 0xF9); /* Isolation signals from
+ * CORE, PLL */
+ r8712_write8(padapter, SYS_ISO_CTRL+1, 0xe8); /* Enable EFUSE 1.2V */
+ r8712_write8(padapter, AFE_PLL_CTRL, 0x00); /* Disable AFE PLL. */
+ r8712_write8(padapter, LDOA15_CTRL, 0x54); /* Disable A15V */
+ r8712_write8(padapter, SYS_FUNC_EN+1, 0x50); /* Disable E-Fuse 1.2V */
+ r8712_write8(padapter, LDOV12D_CTRL, 0x24); /* Disable LDO12(for CE) */
+ r8712_write8(padapter, AFE_MISC, 0x30); /* Disable AFE BG&MB */
+ /* Option for Disable 1.6V LDO. */
+ r8712_write8(padapter, SPS0_CTRL, 0x56); /* Disable 1.6V LDO */
+ r8712_write8(padapter, SPS0_CTRL+1, 0x43); /* Set SW PFM */
+ return _SUCCESS;
+}
+
+uint rtl871x_hal_init(struct _adapter *padapter)
+{
+ padapter->hw_init_completed = false;
+ if (padapter->halpriv.hal_bus_init == NULL)
+ return _FAIL;
+ if (padapter->halpriv.hal_bus_init(padapter) != _SUCCESS)
+ return _FAIL;
+ if (rtl8712_hal_init(padapter) == _SUCCESS)
+ padapter->hw_init_completed = true;
+ else {
+ padapter->hw_init_completed = false;
+ return _FAIL;
+ }
+ return _SUCCESS;
+}
diff --git a/drivers/staging/rtl8712/ieee80211.c b/drivers/staging/rtl8712/ieee80211.c
new file mode 100644
index 000000000..57868085c
--- /dev/null
+++ b/drivers/staging/rtl8712/ieee80211.c
@@ -0,0 +1,418 @@
+/******************************************************************************
+ * ieee80211.c
+ *
+ * Copyright(c) 2007 - 2010 Realtek Corporation. All rights reserved.
+ * Linux device driver for RTL8192SU
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ * Modifications for inclusion into the Linux staging tree are
+ * Copyright(c) 2010 Larry Finger. All rights reserved.
+ *
+ * Contact information:
+ * WLAN FAE <wlanfae@realtek.com>.
+ * Larry Finger <Larry.Finger@lwfinger.net>
+ *
+ ******************************************************************************/
+
+#define _IEEE80211_C
+
+#include "drv_types.h"
+#include "ieee80211.h"
+#include "wifi.h"
+#include "osdep_service.h"
+#include "wlan_bssdef.h"
+
+static const u8 WPA_OUI_TYPE[] = {0x00, 0x50, 0xf2, 1};
+static const u8 WPA_CIPHER_SUITE_NONE[] = {0x00, 0x50, 0xf2, 0};
+static const u8 WPA_CIPHER_SUITE_WEP40[] = {0x00, 0x50, 0xf2, 1};
+static const u8 WPA_CIPHER_SUITE_TKIP[] = {0x00, 0x50, 0xf2, 2};
+static const u8 WPA_CIPHER_SUITE_CCMP[] = {0x00, 0x50, 0xf2, 4};
+static const u8 WPA_CIPHER_SUITE_WEP104[] = {0x00, 0x50, 0xf2, 5};
+
+static const u8 RSN_CIPHER_SUITE_NONE[] = {0x00, 0x0f, 0xac, 0};
+static const u8 RSN_CIPHER_SUITE_WEP40[] = {0x00, 0x0f, 0xac, 1};
+static const u8 RSN_CIPHER_SUITE_TKIP[] = {0x00, 0x0f, 0xac, 2};
+static const u8 RSN_CIPHER_SUITE_CCMP[] = {0x00, 0x0f, 0xac, 4};
+static const u8 RSN_CIPHER_SUITE_WEP104[] = {0x00, 0x0f, 0xac, 5};
+
+/*-----------------------------------------------------------
+ * for adhoc-master to generate ie and provide supported-rate to fw
+ *-----------------------------------------------------------
+ */
+
+static u8 WIFI_CCKRATES[] = {
+ (IEEE80211_CCK_RATE_1MB | IEEE80211_BASIC_RATE_MASK),
+ (IEEE80211_CCK_RATE_2MB | IEEE80211_BASIC_RATE_MASK),
+ (IEEE80211_CCK_RATE_5MB | IEEE80211_BASIC_RATE_MASK),
+ (IEEE80211_CCK_RATE_11MB | IEEE80211_BASIC_RATE_MASK)
+};
+
+static u8 WIFI_OFDMRATES[] = {
+ (IEEE80211_OFDM_RATE_6MB),
+ (IEEE80211_OFDM_RATE_9MB),
+ (IEEE80211_OFDM_RATE_12MB),
+ (IEEE80211_OFDM_RATE_18MB),
+ (IEEE80211_OFDM_RATE_24MB),
+ (IEEE80211_OFDM_RATE_36MB),
+ (IEEE80211_OFDM_RATE_48MB),
+ (IEEE80211_OFDM_RATE_54MB)
+};
+
+uint r8712_is_cckrates_included(u8 *rate)
+{
+ u32 i = 0;
+
+ while (rate[i] != 0) {
+ if ((((rate[i]) & 0x7f) == 2) || (((rate[i]) & 0x7f) == 4) ||
+ (((rate[i]) & 0x7f) == 11) || (((rate[i]) & 0x7f) == 22))
+ return true;
+ i++;
+ }
+ return false;
+}
+
+uint r8712_is_cckratesonly_included(u8 *rate)
+{
+ u32 i = 0;
+
+ while (rate[i] != 0) {
+ if ((((rate[i]) & 0x7f) != 2) && (((rate[i]) & 0x7f) != 4) &&
+ (((rate[i]) & 0x7f) != 11) && (((rate[i]) & 0x7f) != 22))
+ return false;
+ i++;
+ }
+ return true;
+}
+
+/* r8712_set_ie will update frame length */
+u8 *r8712_set_ie(u8 *pbuf, sint index, uint len, u8 *source, uint *frlen)
+{
+ *pbuf = (u8)index;
+ *(pbuf + 1) = (u8)len;
+ if (len > 0)
+ memcpy((void *)(pbuf + 2), (void *)source, len);
+ *frlen = *frlen + (len + 2);
+ return pbuf + len + 2;
+}
+
+/*----------------------------------------------------------------------------
+index: the information element id index, limit is the limit for search
+-----------------------------------------------------------------------------*/
+u8 *r8712_get_ie(u8 *pbuf, sint index, sint *len, sint limit)
+{
+ sint tmp, i;
+ u8 *p;
+
+ if (limit < 1)
+ return NULL;
+ p = pbuf;
+ i = 0;
+ *len = 0;
+ while (1) {
+ if (*p == index) {
+ *len = *(p + 1);
+ return p;
+ }
+ tmp = *(p + 1);
+ p += (tmp + 2);
+ i += (tmp + 2);
+ if (i >= limit)
+ break;
+ }
+ return NULL;
+}
+
+static void set_supported_rate(u8 *SupportedRates, uint mode)
+{
+ memset(SupportedRates, 0, NDIS_802_11_LENGTH_RATES_EX);
+ switch (mode) {
+ case WIRELESS_11B:
+ memcpy(SupportedRates, WIFI_CCKRATES,
+ IEEE80211_CCK_RATE_LEN);
+ break;
+ case WIRELESS_11G:
+ case WIRELESS_11A:
+ memcpy(SupportedRates, WIFI_OFDMRATES,
+ IEEE80211_NUM_OFDM_RATESLEN);
+ break;
+ case WIRELESS_11BG:
+ memcpy(SupportedRates, WIFI_CCKRATES, IEEE80211_CCK_RATE_LEN);
+ memcpy(SupportedRates + IEEE80211_CCK_RATE_LEN, WIFI_OFDMRATES,
+ IEEE80211_NUM_OFDM_RATESLEN);
+ break;
+ }
+}
+
+static uint r8712_get_rateset_len(u8 *rateset)
+{
+ uint i = 0;
+
+ while (1) {
+ if ((rateset[i]) == 0)
+ break;
+ if (i > 12)
+ break;
+ i++;
+ }
+ return i;
+}
+
+int r8712_generate_ie(struct registry_priv *pregistrypriv)
+{
+ int sz = 0, rateLen;
+ struct wlan_bssid_ex *pdev_network = &pregistrypriv->dev_network;
+ u8 *ie = pdev_network->IEs;
+
+ /*timestamp will be inserted by hardware*/
+ sz += 8;
+ ie += sz;
+ /*beacon interval : 2bytes*/
+ *(u16 *)ie = cpu_to_le16((u16)pdev_network->Configuration.BeaconPeriod);
+ sz += 2;
+ ie += 2;
+ /*capability info*/
+ *(u16 *)ie = 0;
+ *(u16 *)ie |= cpu_to_le16(cap_IBSS);
+ if (pregistrypriv->preamble == PREAMBLE_SHORT)
+ *(u16 *)ie |= cpu_to_le16(cap_ShortPremble);
+ if (pdev_network->Privacy)
+ *(u16 *)ie |= cpu_to_le16(cap_Privacy);
+ sz += 2;
+ ie += 2;
+ /*SSID*/
+ ie = r8712_set_ie(ie, _SSID_IE_, pdev_network->Ssid.SsidLength,
+ pdev_network->Ssid.Ssid, &sz);
+ /*supported rates*/
+ set_supported_rate(pdev_network->SupportedRates,
+ pregistrypriv->wireless_mode);
+ rateLen = r8712_get_rateset_len(pdev_network->SupportedRates);
+ if (rateLen > 8) {
+ ie = r8712_set_ie(ie, _SUPPORTEDRATES_IE_, 8,
+ pdev_network->SupportedRates, &sz);
+ ie = r8712_set_ie(ie, _EXT_SUPPORTEDRATES_IE_, (rateLen - 8),
+ (pdev_network->SupportedRates + 8), &sz);
+ } else
+ ie = r8712_set_ie(ie, _SUPPORTEDRATES_IE_,
+ rateLen, pdev_network->SupportedRates, &sz);
+ /*DS parameter set*/
+ ie = r8712_set_ie(ie, _DSSET_IE_, 1,
+ (u8 *)&(pdev_network->Configuration.DSConfig), &sz);
+ /*IBSS Parameter Set*/
+ ie = r8712_set_ie(ie, _IBSS_PARA_IE_, 2,
+ (u8 *)&(pdev_network->Configuration.ATIMWindow), &sz);
+ return sz;
+}
+
+unsigned char *r8712_get_wpa_ie(unsigned char *pie, int *wpa_ie_len, int limit)
+{
+ int len;
+ u16 val16;
+ unsigned char wpa_oui_type[] = {0x00, 0x50, 0xf2, 0x01};
+ u8 *pbuf = pie;
+
+ while (1) {
+ pbuf = r8712_get_ie(pbuf, _WPA_IE_ID_, &len, limit);
+ if (pbuf) {
+ /*check if oui matches...*/
+ if (memcmp((pbuf + 2), wpa_oui_type,
+ sizeof(wpa_oui_type)))
+ goto check_next_ie;
+ /*check version...*/
+ memcpy((u8 *)&val16, (pbuf + 6), sizeof(val16));
+ val16 = le16_to_cpu(val16);
+ if (val16 != 0x0001)
+ goto check_next_ie;
+ *wpa_ie_len = *(pbuf + 1);
+ return pbuf;
+ }
+ *wpa_ie_len = 0;
+ return NULL;
+check_next_ie:
+ limit = limit - (pbuf - pie) - 2 - len;
+ if (limit <= 0)
+ break;
+ pbuf += (2 + len);
+ }
+ *wpa_ie_len = 0;
+ return NULL;
+}
+
+unsigned char *r8712_get_wpa2_ie(unsigned char *pie, int *rsn_ie_len, int limit)
+{
+ return r8712_get_ie(pie, _WPA2_IE_ID_, rsn_ie_len, limit);
+}
+
+static int r8712_get_wpa_cipher_suite(u8 *s)
+{
+ if (!memcmp(s, (void *)WPA_CIPHER_SUITE_NONE, WPA_SELECTOR_LEN))
+ return WPA_CIPHER_NONE;
+ if (!memcmp(s, (void *)WPA_CIPHER_SUITE_WEP40, WPA_SELECTOR_LEN))
+ return WPA_CIPHER_WEP40;
+ if (!memcmp(s, (void *)WPA_CIPHER_SUITE_TKIP, WPA_SELECTOR_LEN))
+ return WPA_CIPHER_TKIP;
+ if (!memcmp(s, (void *)WPA_CIPHER_SUITE_CCMP, WPA_SELECTOR_LEN))
+ return WPA_CIPHER_CCMP;
+ if (!memcmp(s, (void *)WPA_CIPHER_SUITE_WEP104, WPA_SELECTOR_LEN))
+ return WPA_CIPHER_WEP104;
+ return 0;
+}
+
+static int r8712_get_wpa2_cipher_suite(u8 *s)
+{
+ if (!memcmp(s, (void *)RSN_CIPHER_SUITE_NONE, RSN_SELECTOR_LEN))
+ return WPA_CIPHER_NONE;
+ if (!memcmp(s, (void *)RSN_CIPHER_SUITE_WEP40, RSN_SELECTOR_LEN))
+ return WPA_CIPHER_WEP40;
+ if (!memcmp(s, (void *)RSN_CIPHER_SUITE_TKIP, RSN_SELECTOR_LEN))
+ return WPA_CIPHER_TKIP;
+ if (!memcmp(s, (void *)RSN_CIPHER_SUITE_CCMP, RSN_SELECTOR_LEN))
+ return WPA_CIPHER_CCMP;
+ if (!memcmp(s, (void *)RSN_CIPHER_SUITE_WEP104, RSN_SELECTOR_LEN))
+ return WPA_CIPHER_WEP104;
+ return 0;
+}
+
+int r8712_parse_wpa_ie(u8 *wpa_ie, int wpa_ie_len, int *group_cipher,
+ int *pairwise_cipher)
+{
+ int i;
+ int left, count;
+ u8 *pos;
+
+ if (wpa_ie_len <= 0) {
+ /* No WPA IE - fail silently */
+ return _FAIL;
+ }
+ if ((*wpa_ie != _WPA_IE_ID_) || (*(wpa_ie + 1) != (u8)(wpa_ie_len - 2))
+ || (memcmp(wpa_ie + 2, (void *)WPA_OUI_TYPE, WPA_SELECTOR_LEN)))
+ return _FAIL;
+ pos = wpa_ie;
+ pos += 8;
+ left = wpa_ie_len - 8;
+ /*group_cipher*/
+ if (left >= WPA_SELECTOR_LEN) {
+ *group_cipher = r8712_get_wpa_cipher_suite(pos);
+ pos += WPA_SELECTOR_LEN;
+ left -= WPA_SELECTOR_LEN;
+ } else if (left > 0)
+ return _FAIL;
+ /*pairwise_cipher*/
+ if (left >= 2) {
+ count = le16_to_cpu(*(u16 *)pos);
+ pos += 2;
+ left -= 2;
+ if (count == 0 || left < count * WPA_SELECTOR_LEN)
+ return _FAIL;
+ for (i = 0; i < count; i++) {
+ *pairwise_cipher |= r8712_get_wpa_cipher_suite(pos);
+ pos += WPA_SELECTOR_LEN;
+ left -= WPA_SELECTOR_LEN;
+ }
+ } else if (left == 1)
+ return _FAIL;
+ return _SUCCESS;
+}
+
+int r8712_parse_wpa2_ie(u8 *rsn_ie, int rsn_ie_len, int *group_cipher,
+ int *pairwise_cipher)
+{
+ int i;
+ int left, count;
+ u8 *pos;
+
+ if (rsn_ie_len <= 0) {
+ /* No RSN IE - fail silently */
+ return _FAIL;
+ }
+ if ((*rsn_ie != _WPA2_IE_ID_) || (*(rsn_ie+1) != (u8)(rsn_ie_len - 2)))
+ return _FAIL;
+ pos = rsn_ie;
+ pos += 4;
+ left = rsn_ie_len - 4;
+ /*group_cipher*/
+ if (left >= RSN_SELECTOR_LEN) {
+ *group_cipher = r8712_get_wpa2_cipher_suite(pos);
+ pos += RSN_SELECTOR_LEN;
+ left -= RSN_SELECTOR_LEN;
+ } else if (left > 0)
+ return _FAIL;
+ /*pairwise_cipher*/
+ if (left >= 2) {
+ count = le16_to_cpu(*(u16 *)pos);
+ pos += 2;
+ left -= 2;
+ if (count == 0 || left < count * RSN_SELECTOR_LEN)
+ return _FAIL;
+ for (i = 0; i < count; i++) {
+ *pairwise_cipher |= r8712_get_wpa2_cipher_suite(pos);
+ pos += RSN_SELECTOR_LEN;
+ left -= RSN_SELECTOR_LEN;
+ }
+ } else if (left == 1)
+ return _FAIL;
+ return _SUCCESS;
+}
+
+int r8712_get_sec_ie(u8 *in_ie, uint in_len, u8 *rsn_ie, u16 *rsn_len,
+ u8 *wpa_ie, u16 *wpa_len)
+{
+ u8 authmode;
+ u8 wpa_oui[4] = {0x0, 0x50, 0xf2, 0x01};
+ uint cnt;
+
+ /*Search required WPA or WPA2 IE and copy to sec_ie[ ]*/
+ cnt = (_TIMESTAMP_ + _BEACON_ITERVAL_ + _CAPABILITY_);
+ while (cnt < in_len) {
+ authmode = in_ie[cnt];
+ if ((authmode == _WPA_IE_ID_) &&
+ (!memcmp(&in_ie[cnt + 2], &wpa_oui[0], 4))) {
+ memcpy(wpa_ie, &in_ie[cnt], in_ie[cnt + 1] + 2);
+ *wpa_len = in_ie[cnt+1]+2;
+ cnt += in_ie[cnt + 1] + 2; /*get next */
+ } else {
+ if (authmode == _WPA2_IE_ID_) {
+ memcpy(rsn_ie, &in_ie[cnt],
+ in_ie[cnt + 1] + 2);
+ *rsn_len = in_ie[cnt+1] + 2;
+ cnt += in_ie[cnt+1] + 2; /*get next*/
+ } else
+ cnt += in_ie[cnt+1] + 2; /*get next*/
+ }
+ }
+ return *rsn_len + *wpa_len;
+}
+
+int r8712_get_wps_ie(u8 *in_ie, uint in_len, u8 *wps_ie, uint *wps_ielen)
+{
+ int match;
+ uint cnt;
+ u8 eid, wps_oui[4] = {0x0, 0x50, 0xf2, 0x04};
+
+ cnt = 12;
+ match = false;
+ while (cnt < in_len) {
+ eid = in_ie[cnt];
+ if ((eid == _WPA_IE_ID_) &&
+ (!memcmp(&in_ie[cnt+2], wps_oui, 4))) {
+ memcpy(wps_ie, &in_ie[cnt], in_ie[cnt+1]+2);
+ *wps_ielen = in_ie[cnt+1]+2;
+ cnt += in_ie[cnt+1]+2;
+ match = true;
+ break;
+ }
+ cnt += in_ie[cnt+1]+2; /* goto next */
+ }
+ return match;
+}
diff --git a/drivers/staging/rtl8712/ieee80211.h b/drivers/staging/rtl8712/ieee80211.h
new file mode 100644
index 000000000..8269be804
--- /dev/null
+++ b/drivers/staging/rtl8712/ieee80211.h
@@ -0,0 +1,796 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2010 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ * Modifications for inclusion into the Linux staging tree are
+ * Copyright(c) 2010 Larry Finger. All rights reserved.
+ *
+ * Contact information:
+ * WLAN FAE <wlanfae@realtek.com>
+ * Larry Finger <Larry.Finger@lwfinger.net>
+ *
+ ******************************************************************************/
+#ifndef __IEEE80211_H
+#define __IEEE80211_H
+
+#include "osdep_service.h"
+#include "drv_types.h"
+#include "wifi.h"
+#include <linux/compiler.h>
+#include <linux/wireless.h>
+
+#define MGMT_QUEUE_NUM 5
+#define ETH_ALEN 6
+#define IEEE_CMD_SET_WPA_PARAM 1
+#define IEEE_CMD_SET_WPA_IE 2
+#define IEEE_CMD_SET_ENCRYPTION 3
+#define IEEE_CMD_MLME 4
+
+#define IEEE_PARAM_WPA_ENABLED 1
+#define IEEE_PARAM_TKIP_COUNTERMEASURES 2
+#define IEEE_PARAM_DROP_UNENCRYPTED 3
+#define IEEE_PARAM_PRIVACY_INVOKED 4
+#define IEEE_PARAM_AUTH_ALGS 5
+#define IEEE_PARAM_IEEE_802_1X 6
+#define IEEE_PARAM_WPAX_SELECT 7
+
+#define AUTH_ALG_OPEN_SYSTEM 0x1
+#define AUTH_ALG_SHARED_KEY 0x2
+#define AUTH_ALG_LEAP 0x00000004
+
+#define IEEE_MLME_STA_DEAUTH 1
+#define IEEE_MLME_STA_DISASSOC 2
+
+#define IEEE_CRYPT_ERR_UNKNOWN_ALG 2
+#define IEEE_CRYPT_ERR_UNKNOWN_ADDR 3
+#define IEEE_CRYPT_ERR_CRYPT_INIT_FAILED 4
+#define IEEE_CRYPT_ERR_KEY_SET_FAILED 5
+#define IEEE_CRYPT_ERR_TX_KEY_SET_FAILED 6
+#define IEEE_CRYPT_ERR_CARD_CONF_FAILED 7
+
+
+#define IEEE_CRYPT_ALG_NAME_LEN 16
+
+#define WPA_CIPHER_NONE BIT(0)
+#define WPA_CIPHER_WEP40 BIT(1)
+#define WPA_CIPHER_WEP104 BIT(2)
+#define WPA_CIPHER_TKIP BIT(3)
+#define WPA_CIPHER_CCMP BIT(4)
+
+
+
+#define WPA_SELECTOR_LEN 4
+#define RSN_HEADER_LEN 4
+
+#define RSN_SELECTOR_LEN 4
+
+enum NETWORK_TYPE {
+ WIRELESS_INVALID = 0,
+ WIRELESS_11B = 1,
+ WIRELESS_11G = 2,
+ WIRELESS_11BG = (WIRELESS_11B | WIRELESS_11G),
+ WIRELESS_11A = 4,
+ WIRELESS_11N = 8,
+ WIRELESS_11GN = (WIRELESS_11G | WIRELESS_11N),
+ WIRELESS_11BGN = (WIRELESS_11B | WIRELESS_11G | WIRELESS_11N),
+};
+
+
+struct ieee_param {
+ u32 cmd;
+ u8 sta_addr[ETH_ALEN];
+ union {
+ struct {
+ u8 name;
+ u32 value;
+ } wpa_param;
+ struct {
+ u32 len;
+ u8 reserved[32];
+ u8 data[0];
+ } wpa_ie;
+ struct {
+ int command;
+ int reason_code;
+ } mlme;
+ struct {
+ u8 alg[IEEE_CRYPT_ALG_NAME_LEN];
+ u8 set_tx;
+ u32 err;
+ u8 idx;
+ u8 seq[8]; /* sequence counter (set: RX, get: TX) */
+ u16 key_len;
+ u8 key[0];
+ } crypt;
+ } u;
+};
+
+#define IEEE80211_DATA_LEN 2304
+/* Maximum size for the MA-UNITDATA primitive, 802.11 standard section
+ 6.2.1.1.2.
+
+ The figure in section 7.1.2 suggests a body size of up to 2312
+ bytes is allowed, which is a bit confusing, I suspect this
+ represents the 2304 bytes of real data, plus a possible 8 bytes of
+ WEP IV and ICV. (this interpretation suggested by Ramiro Barreiro) */
+
+#define IEEE80211_HLEN 30
+#define IEEE80211_FRAME_LEN (IEEE80211_DATA_LEN + IEEE80211_HLEN)
+
+/* this is stolen from ipw2200 driver */
+#define IEEE_IBSS_MAC_HASH_SIZE 31
+
+struct ieee_ibss_seq {
+ u8 mac[ETH_ALEN];
+ u16 seq_num;
+ u16 frag_num;
+ unsigned long packet_time;
+ struct list_head list;
+};
+
+struct ieee80211_hdr {
+ u16 frame_ctl;
+ u16 duration_id;
+ u8 addr1[ETH_ALEN];
+ u8 addr2[ETH_ALEN];
+ u8 addr3[ETH_ALEN];
+ u16 seq_ctl;
+ u8 addr4[ETH_ALEN];
+} __packed;
+
+struct ieee80211_hdr_3addr {
+ u16 frame_ctl;
+ u16 duration_id;
+ u8 addr1[ETH_ALEN];
+ u8 addr2[ETH_ALEN];
+ u8 addr3[ETH_ALEN];
+ u16 seq_ctl;
+} __packed;
+
+
+struct ieee80211_hdr_qos {
+ u16 frame_ctl;
+ u16 duration_id;
+ u8 addr1[ETH_ALEN];
+ u8 addr2[ETH_ALEN];
+ u8 addr3[ETH_ALEN];
+ u16 seq_ctl;
+ u8 addr4[ETH_ALEN];
+ u16 qc;
+} __packed;
+
+struct ieee80211_hdr_3addr_qos {
+ u16 frame_ctl;
+ u16 duration_id;
+ u8 addr1[ETH_ALEN];
+ u8 addr2[ETH_ALEN];
+ u8 addr3[ETH_ALEN];
+ u16 seq_ctl;
+ u16 qc;
+} __packed;
+
+struct eapol {
+ u8 snap[6];
+ u16 ethertype;
+ u8 version;
+ u8 type;
+ u16 length;
+} __packed;
+
+
+enum eap_type {
+ EAP_PACKET = 0,
+ EAPOL_START,
+ EAPOL_LOGOFF,
+ EAPOL_KEY,
+ EAPOL_ENCAP_ASF_ALERT
+};
+
+#define IEEE80211_3ADDR_LEN 24
+#define IEEE80211_4ADDR_LEN 30
+#define IEEE80211_FCS_LEN 4
+
+#define MIN_FRAG_THRESHOLD 256U
+#define MAX_FRAG_THRESHOLD 2346U
+
+/* Frame control field constants */
+#define IEEE80211_FCTL_VERS 0x0002
+#define IEEE80211_FCTL_FTYPE 0x000c
+#define IEEE80211_FCTL_STYPE 0x00f0
+#define IEEE80211_FCTL_TODS 0x0100
+#define IEEE80211_FCTL_FROMDS 0x0200
+#define IEEE80211_FCTL_MOREFRAGS 0x0400
+#define IEEE80211_FCTL_RETRY 0x0800
+#define IEEE80211_FCTL_PM 0x1000
+#define IEEE80211_FCTL_MOREDATA 0x2000
+#define IEEE80211_FCTL_WEP 0x4000
+#define IEEE80211_FCTL_ORDER 0x8000
+
+#define IEEE80211_FTYPE_MGMT 0x0000
+#define IEEE80211_FTYPE_CTL 0x0004
+#define IEEE80211_FTYPE_DATA 0x0008
+
+/* management */
+#define IEEE80211_STYPE_ASSOC_REQ 0x0000
+#define IEEE80211_STYPE_ASSOC_RESP 0x0010
+#define IEEE80211_STYPE_REASSOC_REQ 0x0020
+#define IEEE80211_STYPE_REASSOC_RESP 0x0030
+#define IEEE80211_STYPE_PROBE_REQ 0x0040
+#define IEEE80211_STYPE_PROBE_RESP 0x0050
+#define IEEE80211_STYPE_BEACON 0x0080
+#define IEEE80211_STYPE_ATIM 0x0090
+#define IEEE80211_STYPE_DISASSOC 0x00A0
+#define IEEE80211_STYPE_AUTH 0x00B0
+#define IEEE80211_STYPE_DEAUTH 0x00C0
+
+/* control */
+#define IEEE80211_STYPE_PSPOLL 0x00A0
+#define IEEE80211_STYPE_RTS 0x00B0
+#define IEEE80211_STYPE_CTS 0x00C0
+#define IEEE80211_STYPE_ACK 0x00D0
+#define IEEE80211_STYPE_CFEND 0x00E0
+#define IEEE80211_STYPE_CFENDACK 0x00F0
+
+/* data */
+#define IEEE80211_STYPE_DATA 0x0000
+#define IEEE80211_STYPE_DATA_CFACK 0x0010
+#define IEEE80211_STYPE_DATA_CFPOLL 0x0020
+#define IEEE80211_STYPE_DATA_CFACKPOLL 0x0030
+#define IEEE80211_STYPE_NULLFUNC 0x0040
+#define IEEE80211_STYPE_CFACK 0x0050
+#define IEEE80211_STYPE_CFPOLL 0x0060
+#define IEEE80211_STYPE_CFACKPOLL 0x0070
+#define IEEE80211_QOS_DATAGRP 0x0080
+#define IEEE80211_QoS_DATAGRP IEEE80211_QOS_DATAGRP
+
+#define IEEE80211_SCTL_FRAG 0x000F
+#define IEEE80211_SCTL_SEQ 0xFFF0
+
+/* QoS,QOS */
+#define NORMAL_ACK 0
+#define NO_ACK 1
+#define NON_EXPLICIT_ACK 2
+#define BLOCK_ACK 3
+
+#ifndef ETH_P_PAE
+#define ETH_P_PAE 0x888E /* Port Access Entity (IEEE 802.1X) */
+#endif /* ETH_P_PAE */
+
+#define ETH_P_PREAUTH 0x88C7 /* IEEE 802.11i pre-authentication */
+
+#define ETH_P_ECONET 0x0018
+
+#ifndef ETH_P_80211_RAW
+#define ETH_P_80211_RAW (ETH_P_ECONET + 1)
+#endif
+
+/* IEEE 802.11 defines */
+
+#define P80211_OUI_LEN 3
+
+struct ieee80211_snap_hdr {
+ u8 dsap; /* always 0xAA */
+ u8 ssap; /* always 0xAA */
+ u8 ctrl; /* always 0x03 */
+ u8 oui[P80211_OUI_LEN]; /* organizational universal id */
+} __packed;
+
+#define SNAP_SIZE sizeof(struct ieee80211_snap_hdr)
+
+#define WLAN_FC_GET_TYPE(fc) ((fc) & IEEE80211_FCTL_FTYPE)
+#define WLAN_FC_GET_STYPE(fc) ((fc) & IEEE80211_FCTL_STYPE)
+
+#define WLAN_QC_GET_TID(qc) ((qc) & 0x0f)
+
+#define WLAN_GET_SEQ_FRAG(seq) ((seq) & IEEE80211_SCTL_FRAG)
+#define WLAN_GET_SEQ_SEQ(seq) ((seq) & IEEE80211_SCTL_SEQ)
+
+/* Authentication algorithms */
+#define WLAN_AUTH_OPEN 0
+#define WLAN_AUTH_SHARED_KEY 1
+
+#define WLAN_AUTH_CHALLENGE_LEN 128
+
+#define WLAN_CAPABILITY_BSS (1<<0)
+#define WLAN_CAPABILITY_IBSS (1<<1)
+#define WLAN_CAPABILITY_CF_POLLABLE (1<<2)
+#define WLAN_CAPABILITY_CF_POLL_REQUEST (1<<3)
+#define WLAN_CAPABILITY_PRIVACY (1<<4)
+#define WLAN_CAPABILITY_SHORT_PREAMBLE (1<<5)
+#define WLAN_CAPABILITY_PBCC (1<<6)
+#define WLAN_CAPABILITY_CHANNEL_AGILITY (1<<7)
+#define WLAN_CAPABILITY_SHORT_SLOT (1<<10)
+
+/* Status codes */
+#define WLAN_STATUS_SUCCESS 0
+#define WLAN_STATUS_UNSPECIFIED_FAILURE 1
+#define WLAN_STATUS_CAPS_UNSUPPORTED 10
+#define WLAN_STATUS_REASSOC_NO_ASSOC 11
+#define WLAN_STATUS_ASSOC_DENIED_UNSPEC 12
+#define WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG 13
+#define WLAN_STATUS_UNKNOWN_AUTH_TRANSACTION 14
+#define WLAN_STATUS_CHALLENGE_FAIL 15
+#define WLAN_STATUS_AUTH_TIMEOUT 16
+#define WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA 17
+#define WLAN_STATUS_ASSOC_DENIED_RATES 18
+/* 802.11b */
+#define WLAN_STATUS_ASSOC_DENIED_NOSHORT 19
+#define WLAN_STATUS_ASSOC_DENIED_NOPBCC 20
+#define WLAN_STATUS_ASSOC_DENIED_NOAGILITY 21
+
+/* Reason codes */
+#define WLAN_REASON_UNSPECIFIED 1
+#define WLAN_REASON_PREV_AUTH_NOT_VALID 2
+#define WLAN_REASON_DEAUTH_LEAVING 3
+#define WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY 4
+#define WLAN_REASON_DISASSOC_AP_BUSY 5
+#define WLAN_REASON_CLASS2_FRAME_FROM_NONAUTH_STA 6
+#define WLAN_REASON_CLASS3_FRAME_FROM_NONASSOC_STA 7
+#define WLAN_REASON_DISASSOC_STA_HAS_LEFT 8
+#define WLAN_REASON_STA_REQ_ASSOC_WITHOUT_AUTH 9
+
+
+/* Information Element IDs */
+#define WLAN_EID_SSID 0
+#define WLAN_EID_SUPP_RATES 1
+#define WLAN_EID_FH_PARAMS 2
+#define WLAN_EID_DS_PARAMS 3
+#define WLAN_EID_CF_PARAMS 4
+#define WLAN_EID_TIM 5
+#define WLAN_EID_IBSS_PARAMS 6
+#define WLAN_EID_CHALLENGE 16
+#define WLAN_EID_RSN 48
+#define WLAN_EID_GENERIC 221
+
+#define IEEE80211_MGMT_HDR_LEN 24
+#define IEEE80211_DATA_HDR3_LEN 24
+#define IEEE80211_DATA_HDR4_LEN 30
+
+
+#define IEEE80211_STATMASK_SIGNAL (1<<0)
+#define IEEE80211_STATMASK_RSSI (1<<1)
+#define IEEE80211_STATMASK_NOISE (1<<2)
+#define IEEE80211_STATMASK_RATE (1<<3)
+#define IEEE80211_STATMASK_WEMASK 0x7
+
+
+#define IEEE80211_CCK_MODULATION (1<<0)
+#define IEEE80211_OFDM_MODULATION (1<<1)
+
+#define IEEE80211_24GHZ_BAND (1<<0)
+#define IEEE80211_52GHZ_BAND (1<<1)
+
+#define IEEE80211_CCK_RATE_LEN 4
+#define IEEE80211_NUM_OFDM_RATESLEN 8
+
+
+#define IEEE80211_CCK_RATE_1MB 0x02
+#define IEEE80211_CCK_RATE_2MB 0x04
+#define IEEE80211_CCK_RATE_5MB 0x0B
+#define IEEE80211_CCK_RATE_11MB 0x16
+#define IEEE80211_OFDM_RATE_LEN 8
+#define IEEE80211_OFDM_RATE_6MB 0x0C
+#define IEEE80211_OFDM_RATE_9MB 0x12
+#define IEEE80211_OFDM_RATE_12MB 0x18
+#define IEEE80211_OFDM_RATE_18MB 0x24
+#define IEEE80211_OFDM_RATE_24MB 0x30
+#define IEEE80211_OFDM_RATE_36MB 0x48
+#define IEEE80211_OFDM_RATE_48MB 0x60
+#define IEEE80211_OFDM_RATE_54MB 0x6C
+#define IEEE80211_BASIC_RATE_MASK 0x80
+
+#define IEEE80211_CCK_RATE_1MB_MASK (1<<0)
+#define IEEE80211_CCK_RATE_2MB_MASK (1<<1)
+#define IEEE80211_CCK_RATE_5MB_MASK (1<<2)
+#define IEEE80211_CCK_RATE_11MB_MASK (1<<3)
+#define IEEE80211_OFDM_RATE_6MB_MASK (1<<4)
+#define IEEE80211_OFDM_RATE_9MB_MASK (1<<5)
+#define IEEE80211_OFDM_RATE_12MB_MASK (1<<6)
+#define IEEE80211_OFDM_RATE_18MB_MASK (1<<7)
+#define IEEE80211_OFDM_RATE_24MB_MASK (1<<8)
+#define IEEE80211_OFDM_RATE_36MB_MASK (1<<9)
+#define IEEE80211_OFDM_RATE_48MB_MASK (1<<10)
+#define IEEE80211_OFDM_RATE_54MB_MASK (1<<11)
+
+#define IEEE80211_CCK_RATES_MASK 0x0000000F
+#define IEEE80211_CCK_BASIC_RATES_MASK (IEEE80211_CCK_RATE_1MB_MASK | \
+ IEEE80211_CCK_RATE_2MB_MASK)
+#define IEEE80211_CCK_DEFAULT_RATES_MASK (IEEE80211_CCK_BASIC_RATES_MASK | \
+ IEEE80211_CCK_RATE_5MB_MASK | \
+ IEEE80211_CCK_RATE_11MB_MASK)
+
+#define IEEE80211_OFDM_RATES_MASK 0x00000FF0
+#define IEEE80211_OFDM_BASIC_RATES_MASK (IEEE80211_OFDM_RATE_6MB_MASK | \
+ IEEE80211_OFDM_RATE_12MB_MASK | \
+ IEEE80211_OFDM_RATE_24MB_MASK)
+#define IEEE80211_OFDM_DEFAULT_RATES_MASK (IEEE80211_OFDM_BASIC_RATES_MASK | \
+ IEEE80211_OFDM_RATE_9MB_MASK | \
+ IEEE80211_OFDM_RATE_18MB_MASK | \
+ IEEE80211_OFDM_RATE_36MB_MASK | \
+ IEEE80211_OFDM_RATE_48MB_MASK | \
+ IEEE80211_OFDM_RATE_54MB_MASK)
+#define IEEE80211_DEFAULT_RATES_MASK (IEEE80211_OFDM_DEFAULT_RATES_MASK | \
+ IEEE80211_CCK_DEFAULT_RATES_MASK)
+
+#define IEEE80211_NUM_OFDM_RATES 8
+#define IEEE80211_NUM_CCK_RATES 4
+#define IEEE80211_OFDM_SHIFT_MASK_A 4
+
+
+
+
+/* NOTE: This data is for statistical purposes; not all hardware provides this
+ * information for frames received. Not setting these will not cause
+ * any adverse affects. */
+struct ieee80211_rx_stats {
+ s8 rssi;
+ u8 signal;
+ u8 noise;
+ u8 received_channel;
+ u16 rate; /* in 100 kbps */
+ u8 mask;
+ u8 freq;
+ u16 len;
+};
+
+/* IEEE 802.11 requires that STA supports concurrent reception of at least
+ * three fragmented frames. This define can be increased to support more
+ * concurrent frames, but it should be noted that each entry can consume about
+ * 2 kB of RAM and increasing cache size will slow down frame reassembly. */
+#define IEEE80211_FRAG_CACHE_LEN 4
+
+struct ieee80211_frag_entry {
+ u32 first_frag_time;
+ uint seq;
+ uint last_frag;
+ uint qos; /*jackson*/
+ uint tid; /*jackson*/
+ struct sk_buff *skb;
+ u8 src_addr[ETH_ALEN];
+ u8 dst_addr[ETH_ALEN];
+};
+
+struct ieee80211_stats {
+ uint tx_unicast_frames;
+ uint tx_multicast_frames;
+ uint tx_fragments;
+ uint tx_unicast_octets;
+ uint tx_multicast_octets;
+ uint tx_deferred_transmissions;
+ uint tx_single_retry_frames;
+ uint tx_multiple_retry_frames;
+ uint tx_retry_limit_exceeded;
+ uint tx_discards;
+ uint rx_unicast_frames;
+ uint rx_multicast_frames;
+ uint rx_fragments;
+ uint rx_unicast_octets;
+ uint rx_multicast_octets;
+ uint rx_fcs_errors;
+ uint rx_discards_no_buffer;
+ uint tx_discards_wrong_sa;
+ uint rx_discards_undecryptable;
+ uint rx_message_in_msg_fragments;
+ uint rx_message_in_bad_msg_fragments;
+};
+
+struct ieee80211_softmac_stats {
+ uint rx_ass_ok;
+ uint rx_ass_err;
+ uint rx_probe_rq;
+ uint tx_probe_rs;
+ uint tx_beacons;
+ uint rx_auth_rq;
+ uint rx_auth_rs_ok;
+ uint rx_auth_rs_err;
+ uint tx_auth_rq;
+ uint no_auth_rs;
+ uint no_ass_rs;
+ uint tx_ass_rq;
+ uint rx_ass_rq;
+ uint tx_probe_rq;
+ uint reassoc;
+ uint swtxstop;
+ uint swtxawake;
+};
+
+#define SEC_KEY_1 (1<<0)
+#define SEC_KEY_2 (1<<1)
+#define SEC_KEY_3 (1<<2)
+#define SEC_KEY_4 (1<<3)
+#define SEC_ACTIVE_KEY (1<<4)
+#define SEC_AUTH_MODE (1<<5)
+#define SEC_UNICAST_GROUP (1<<6)
+#define SEC_LEVEL (1<<7)
+#define SEC_ENABLED (1<<8)
+
+#define SEC_LEVEL_0 0 /* None */
+#define SEC_LEVEL_1 1 /* WEP 40 and 104 bit */
+#define SEC_LEVEL_2 2 /* Level 1 + TKIP */
+#define SEC_LEVEL_2_CKIP 3 /* Level 1 + CKIP */
+#define SEC_LEVEL_3 4 /* Level 2 + CCMP */
+
+#define WEP_KEYS 4
+#define WEP_KEY_LEN 13
+
+struct ieee80211_security {
+ u16 active_key:2,
+ enabled:1,
+ auth_mode:2,
+ auth_algo:4,
+ unicast_uses_group:1;
+ u8 key_sizes[WEP_KEYS];
+ u8 keys[WEP_KEYS][WEP_KEY_LEN];
+ u8 level;
+ u16 flags;
+} __packed;
+
+/*
+
+ 802.11 data frame from AP
+
+ ,-------------------------------------------------------------------.
+Bytes | 2 | 2 | 6 | 6 | 6 | 2 | 0..2312 | 4 |
+ |------|------|---------|---------|---------|------|---------|------|
+Desc. | ctrl | dura | DA/RA | TA | SA | Sequ | frame | fcs |
+ | | tion | (BSSID) | | | ence | data | |
+ `-------------------------------------------------------------------'
+
+Total: 28-2340 bytes
+
+*/
+
+struct ieee80211_header_data {
+ u16 frame_ctl;
+ u16 duration_id;
+ u8 addr1[6];
+ u8 addr2[6];
+ u8 addr3[6];
+ u16 seq_ctrl;
+};
+
+#define BEACON_PROBE_SSID_ID_POSITION 12
+
+/* Management Frame Information Element Types */
+#define MFIE_TYPE_SSID 0
+#define MFIE_TYPE_RATES 1
+#define MFIE_TYPE_FH_SET 2
+#define MFIE_TYPE_DS_SET 3
+#define MFIE_TYPE_CF_SET 4
+#define MFIE_TYPE_TIM 5
+#define MFIE_TYPE_IBSS_SET 6
+#define MFIE_TYPE_CHALLENGE 16
+#define MFIE_TYPE_ERP 42
+#define MFIE_TYPE_RSN 48
+#define MFIE_TYPE_RATES_EX 50
+#define MFIE_TYPE_GENERIC 221
+
+struct ieee80211_info_element_hdr {
+ u8 id;
+ u8 len;
+} __packed;
+
+struct ieee80211_info_element {
+ u8 id;
+ u8 len;
+ u8 data[0];
+} __packed;
+
+/*
+ * These are the data types that can make up management packets
+ *
+ u16 auth_algorithm;
+ u16 auth_sequence;
+ u16 beacon_interval;
+ u16 capability;
+ u8 current_ap[ETH_ALEN];
+ u16 listen_interval;
+ struct {
+ u16 association_id:14, reserved:2;
+ } __packed;
+ u32 time_stamp[2];
+ u16 reason;
+ u16 status;
+*/
+
+#define IEEE80211_DEFAULT_TX_ESSID "Penguin"
+#define IEEE80211_DEFAULT_BASIC_RATE 10
+
+struct ieee80211_authentication {
+ struct ieee80211_header_data header;
+ u16 algorithm;
+ u16 transaction;
+ u16 status;
+} __packed;
+
+struct ieee80211_probe_response {
+ struct ieee80211_header_data header;
+ u32 time_stamp[2];
+ u16 beacon_interval;
+ u16 capability;
+ struct ieee80211_info_element info_element;
+} __packed;
+
+struct ieee80211_probe_request {
+ struct ieee80211_header_data header;
+} __packed;
+
+struct ieee80211_assoc_request_frame {
+ struct ieee80211_hdr_3addr header;
+ u16 capability;
+ u16 listen_interval;
+ struct ieee80211_info_element_hdr info_element;
+} __packed;
+
+struct ieee80211_assoc_response_frame {
+ struct ieee80211_hdr_3addr header;
+ u16 capability;
+ u16 status;
+ u16 aid;
+} __packed;
+
+struct ieee80211_txb {
+ u8 nr_frags;
+ u8 encrypted;
+ u16 reserved;
+ u16 frag_size;
+ u16 payload_size;
+ struct sk_buff *fragments[0];
+};
+
+/* SWEEP TABLE ENTRIES NUMBER*/
+#define MAX_SWEEP_TAB_ENTRIES 42
+#define MAX_SWEEP_TAB_ENTRIES_PER_PACKET 7
+/* MAX_RATES_LENGTH needs to be 12. The spec says 8, and many APs
+ * only use 8, and then use extended rates for the remaining supported
+ * rates. Other APs, however, stick all of their supported rates on the
+ * main rates information element... */
+#define MAX_RATES_LENGTH ((u8)12)
+#define MAX_RATES_EX_LENGTH ((u8)16)
+#define MAX_NETWORK_COUNT 128
+#define MAX_CHANNEL_NUMBER 161
+#define IEEE80211_SOFTMAC_SCAN_TIME 400
+/*(HZ / 2)*/
+#define IEEE80211_SOFTMAC_ASSOC_RETRY_TIME (HZ * 2)
+
+#define CRC_LENGTH 4U
+
+#define MAX_WPA_IE_LEN 128
+
+#define NETWORK_EMPTY_ESSID (1<<0)
+#define NETWORK_HAS_OFDM (1<<1)
+#define NETWORK_HAS_CCK (1<<2)
+
+#define IEEE80211_DTIM_MBCAST 4
+#define IEEE80211_DTIM_UCAST 2
+#define IEEE80211_DTIM_VALID 1
+#define IEEE80211_DTIM_INVALID 0
+
+#define IEEE80211_PS_DISABLED 0
+#define IEEE80211_PS_UNICAST IEEE80211_DTIM_UCAST
+#define IEEE80211_PS_MBCAST IEEE80211_DTIM_MBCAST
+#define IW_ESSID_MAX_SIZE 32
+/*
+ * join_res:
+ * -1: authentication fail
+ * -2: association fail
+ * > 0: TID
+ */
+
+enum ieee80211_state {
+ /* the card is not linked at all */
+ IEEE80211_NOLINK = 0,
+ /* IEEE80211_ASSOCIATING* are for BSS client mode
+ * the driver shall not perform RX filtering unless
+ * the state is LINKED.
+ * The driver shall just check for the state LINKED and
+ * defaults to NOLINK for ALL the other states (including
+ * LINKED_SCANNING)
+ */
+ /* the association procedure will start (wq scheduling)*/
+ IEEE80211_ASSOCIATING,
+ IEEE80211_ASSOCIATING_RETRY,
+ /* the association procedure is sending AUTH request*/
+ IEEE80211_ASSOCIATING_AUTHENTICATING,
+ /* the association procedure has successfully authenticated
+ * and is sending association request
+ */
+ IEEE80211_ASSOCIATING_AUTHENTICATED,
+ /* the link is ok. the card associated to a BSS or linked
+ * to a ibss cell or acting as an AP and creating the bss
+ */
+ IEEE80211_LINKED,
+ /* same as LINKED, but the driver shall apply RX filter
+ * rules as we are in NO_LINK mode. As the card is still
+ * logically linked, but it is doing a syncro site survey
+ * then it will be back to LINKED state.
+ */
+ IEEE80211_LINKED_SCANNING,
+};
+
+#define DEFAULT_MAX_SCAN_AGE (15 * HZ)
+#define DEFAULT_FTS 2346
+
+#define CFG_IEEE80211_RESERVE_FCS (1<<0)
+#define CFG_IEEE80211_COMPUTE_FCS (1<<1)
+
+#define MAXTID 16
+
+#define IEEE_A (1<<0)
+#define IEEE_B (1<<1)
+#define IEEE_G (1<<2)
+#define IEEE_MODE_MASK (IEEE_A|IEEE_B|IEEE_G)
+
+static inline int ieee80211_is_empty_essid(const char *essid, int essid_len)
+{
+ /* Single white space is for Linksys APs */
+ if (essid_len == 1 && essid[0] == ' ')
+ return 1;
+ /* Otherwise, if the entire essid is 0, we assume it is hidden */
+ while (essid_len) {
+ essid_len--;
+ if (essid[essid_len] != '\0')
+ return 0;
+ }
+ return 1;
+}
+
+static inline int ieee80211_get_hdrlen(u16 fc)
+{
+ int hdrlen = 24;
+
+ switch (WLAN_FC_GET_TYPE(fc)) {
+ case IEEE80211_FTYPE_DATA:
+ if (fc & IEEE80211_QOS_DATAGRP)
+ hdrlen += 2;
+ if ((fc & IEEE80211_FCTL_FROMDS) && (fc & IEEE80211_FCTL_TODS))
+ hdrlen += 6; /* Addr4 */
+ break;
+ case IEEE80211_FTYPE_CTL:
+ switch (WLAN_FC_GET_STYPE(fc)) {
+ case IEEE80211_STYPE_CTS:
+ case IEEE80211_STYPE_ACK:
+ hdrlen = 10;
+ break;
+ default:
+ hdrlen = 16;
+ break;
+ }
+ break;
+ }
+ return hdrlen;
+}
+
+struct registry_priv;
+
+u8 *r8712_set_ie(u8 *pbuf, sint index, uint len, u8 *source, uint *frlen);
+u8 *r8712_get_ie(u8 *pbuf, sint index, sint *len, sint limit);
+unsigned char *r8712_get_wpa_ie(unsigned char *pie, int *rsn_ie_len, int limit);
+unsigned char *r8712_get_wpa2_ie(unsigned char *pie, int *rsn_ie_len,
+ int limit);
+int r8712_parse_wpa_ie(u8 *wpa_ie, int wpa_ie_len, int *group_cipher,
+ int *pairwise_cipher);
+int r8712_parse_wpa2_ie(u8 *wpa_ie, int wpa_ie_len, int *group_cipher,
+ int *pairwise_cipher);
+int r8712_get_sec_ie(u8 *in_ie, uint in_len, u8 *rsn_ie, u16 *rsn_len,
+ u8 *wpa_ie, u16 *wpa_len);
+int r8712_get_wps_ie(u8 *in_ie, uint in_len, u8 *wps_ie, uint *wps_ielen);
+int r8712_generate_ie(struct registry_priv *pregistrypriv);
+uint r8712_is_cckrates_included(u8 *rate);
+uint r8712_is_cckratesonly_included(u8 *rate);
+
+#endif /* IEEE80211_H */
+
diff --git a/drivers/staging/rtl8712/mlme_linux.c b/drivers/staging/rtl8712/mlme_linux.c
new file mode 100644
index 000000000..8c5a475f0
--- /dev/null
+++ b/drivers/staging/rtl8712/mlme_linux.c
@@ -0,0 +1,171 @@
+/******************************************************************************
+ * mlme_linux.c
+ *
+ * Copyright(c) 2007 - 2010 Realtek Corporation. All rights reserved.
+ * Linux device driver for RTL8192SU
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ * Modifications for inclusion into the Linux staging tree are
+ * Copyright(c) 2010 Larry Finger. All rights reserved.
+ *
+ * Contact information:
+ * WLAN FAE <wlanfae@realtek.com>.
+ * Larry Finger <Larry.Finger@lwfinger.net>
+ *
+ ******************************************************************************/
+
+#define _MLME_OSDEP_C_
+
+#include "osdep_service.h"
+#include "drv_types.h"
+#include "mlme_osdep.h"
+
+static void sitesurvey_ctrl_handler(unsigned long data)
+{
+ struct _adapter *adapter = (struct _adapter *)data;
+
+ _r8712_sitesurvey_ctrl_handler(adapter);
+ mod_timer(&adapter->mlmepriv.sitesurveyctrl.sitesurvey_ctrl_timer,
+ jiffies + msecs_to_jiffies(3000));
+}
+
+static void join_timeout_handler (unsigned long data)
+{
+ struct _adapter *adapter = (struct _adapter *)data;
+
+ _r8712_join_timeout_handler(adapter);
+}
+
+static void _scan_timeout_handler (unsigned long data)
+{
+ struct _adapter *adapter = (struct _adapter *)data;
+
+ r8712_scan_timeout_handler(adapter);
+}
+
+static void dhcp_timeout_handler (unsigned long data)
+{
+ struct _adapter *adapter = (struct _adapter *)data;
+
+ _r8712_dhcp_timeout_handler(adapter);
+}
+
+static void wdg_timeout_handler (unsigned long data)
+{
+ struct _adapter *adapter = (struct _adapter *)data;
+
+ _r8712_wdg_timeout_handler(adapter);
+
+ mod_timer(&adapter->mlmepriv.wdg_timer,
+ jiffies + msecs_to_jiffies(2000));
+}
+
+void r8712_init_mlme_timer(struct _adapter *padapter)
+{
+ struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
+
+ setup_timer(&pmlmepriv->assoc_timer, join_timeout_handler,
+ (unsigned long)padapter);
+ setup_timer(&pmlmepriv->sitesurveyctrl.sitesurvey_ctrl_timer,
+ sitesurvey_ctrl_handler,
+ (unsigned long)padapter);
+ setup_timer(&pmlmepriv->scan_to_timer, _scan_timeout_handler,
+ (unsigned long)padapter);
+ setup_timer(&pmlmepriv->dhcp_timer, dhcp_timeout_handler,
+ (unsigned long)padapter);
+ setup_timer(&pmlmepriv->wdg_timer, wdg_timeout_handler,
+ (unsigned long)padapter);
+}
+
+void r8712_os_indicate_connect(struct _adapter *adapter)
+{
+ r8712_indicate_wx_assoc_event(adapter);
+ netif_carrier_on(adapter->pnetdev);
+}
+
+static struct RT_PMKID_LIST backupPMKIDList[NUM_PMKID_CACHE];
+void r8712_os_indicate_disconnect(struct _adapter *adapter)
+{
+ u8 backupPMKIDIndex = 0;
+ u8 backupTKIPCountermeasure = 0x00;
+
+ r8712_indicate_wx_disassoc_event(adapter);
+ netif_carrier_off(adapter->pnetdev);
+ if (adapter->securitypriv.AuthAlgrthm == 2) { /*/802.1x*/
+ /* We have to backup the PMK information for WiFi PMK Caching
+ * test item. Backup the btkip_countermeasure information.
+ * When the countermeasure is trigger, the driver have to
+ * disconnect with AP for 60 seconds.
+ */
+
+ memcpy(&backupPMKIDList[0], &adapter->securitypriv.
+ PMKIDList[0], sizeof(struct RT_PMKID_LIST) *
+ NUM_PMKID_CACHE);
+ backupPMKIDIndex = adapter->securitypriv.PMKIDIndex;
+ backupTKIPCountermeasure = adapter->securitypriv.
+ btkip_countermeasure;
+ memset((unsigned char *)&adapter->securitypriv, 0,
+ sizeof(struct security_priv));
+ setup_timer(&adapter->securitypriv.tkip_timer,
+ r8712_use_tkipkey_handler,
+ (unsigned long)adapter);
+ /* Restore the PMK information to securitypriv structure
+ * for the following connection. */
+ memcpy(&adapter->securitypriv.PMKIDList[0],
+ &backupPMKIDList[0],
+ sizeof(struct RT_PMKID_LIST) * NUM_PMKID_CACHE);
+ adapter->securitypriv.PMKIDIndex = backupPMKIDIndex;
+ adapter->securitypriv.btkip_countermeasure =
+ backupTKIPCountermeasure;
+ } else { /*reset values in securitypriv*/
+ struct security_priv *psec_priv = &adapter->securitypriv;
+
+ psec_priv->AuthAlgrthm = 0; /*open system*/
+ psec_priv->PrivacyAlgrthm = _NO_PRIVACY_;
+ psec_priv->PrivacyKeyIndex = 0;
+ psec_priv->XGrpPrivacy = _NO_PRIVACY_;
+ psec_priv->XGrpKeyid = 1;
+ psec_priv->ndisauthtype = Ndis802_11AuthModeOpen;
+ psec_priv->ndisencryptstatus = Ndis802_11WEPDisabled;
+ psec_priv->wps_phase = false;
+ }
+}
+
+void r8712_report_sec_ie(struct _adapter *adapter, u8 authmode, u8 *sec_ie)
+{
+ uint len;
+ u8 *buff, *p, i;
+ union iwreq_data wrqu;
+
+ buff = NULL;
+ if (authmode == _WPA_IE_ID_) {
+ buff = kzalloc(IW_CUSTOM_MAX, GFP_ATOMIC);
+ if (buff == NULL)
+ return;
+ p = buff;
+ p += sprintf(p, "ASSOCINFO(ReqIEs=");
+ len = sec_ie[1] + 2;
+ len = (len < IW_CUSTOM_MAX) ? len : IW_CUSTOM_MAX - 1;
+ for (i = 0; i < len; i++)
+ p += sprintf(p, "%02x", sec_ie[i]);
+ p += sprintf(p, ")");
+ memset(&wrqu, 0, sizeof(wrqu));
+ wrqu.data.length = p-buff;
+ wrqu.data.length = (wrqu.data.length < IW_CUSTOM_MAX) ?
+ wrqu.data.length : IW_CUSTOM_MAX;
+ wireless_send_event(adapter->pnetdev, IWEVCUSTOM, &wrqu, buff);
+ kfree(buff);
+ }
+}
diff --git a/drivers/staging/rtl8712/mlme_osdep.h b/drivers/staging/rtl8712/mlme_osdep.h
new file mode 100644
index 000000000..a20fe81f9
--- /dev/null
+++ b/drivers/staging/rtl8712/mlme_osdep.h
@@ -0,0 +1,43 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2010 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ * Modifications for inclusion into the Linux staging tree are
+ * Copyright(c) 2010 Larry Finger. All rights reserved.
+ *
+ * Contact information:
+ * WLAN FAE <wlanfae@realtek.com>
+ * Larry Finger <Larry.Finger@lwfinger.net>
+ *
+ ******************************************************************************/
+#ifndef __MLME_OSDEP_H_
+#define __MLME_OSDEP_H_
+
+#include "osdep_service.h"
+#include "drv_types.h"
+
+void r8712_init_mlme_timer(struct _adapter *padapter);
+void r8712_os_indicate_disconnect(struct _adapter *adapter);
+void r8712_os_indicate_connect(struct _adapter *adapter);
+void r8712_report_sec_ie(struct _adapter *adapter, u8 authmode, u8 *sec_ie);
+int r8712_recv_indicatepkts_in_order(struct _adapter *adapter,
+ struct recv_reorder_ctrl *precvreorder_ctrl,
+ int bforced);
+void r8712_indicate_wx_assoc_event(struct _adapter *padapter);
+void r8712_indicate_wx_disassoc_event(struct _adapter *padapter);
+
+#endif /*_MLME_OSDEP_H_*/
+
diff --git a/drivers/staging/rtl8712/mp_custom_oid.h b/drivers/staging/rtl8712/mp_custom_oid.h
new file mode 100644
index 000000000..40510089b
--- /dev/null
+++ b/drivers/staging/rtl8712/mp_custom_oid.h
@@ -0,0 +1,299 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2010 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ * Modifications for inclusion into the Linux staging tree are
+ * Copyright(c) 2010 Larry Finger. All rights reserved.
+ *
+ * Contact information:
+ * WLAN FAE <wlanfae@realtek.com>
+ * Larry Finger <Larry.Finger@lwfinger.net>
+ *
+ ******************************************************************************/
+#ifndef __CUSTOM_OID_H
+#define __CUSTOM_OID_H
+
+/* 0xFF818000 - 0xFF81802F RTL8180 Mass Production Kit
+ * 0xFF818500 - 0xFF81850F RTL8185 Setup Utility
+ * 0xFF818580 - 0xFF81858F RTL8185 Phy Status Utility
+ *
+ * by Owen for Production Kit
+ * For Production Kit with Agilent Equipments
+ * in order to make our custom oids hopefully somewhat unique
+ * we will use 0xFF (indicating implementation specific OID)
+ * 81(first byte of non zero Realtek unique identifier)
+ * 80 (second byte of non zero Realtek unique identifier)
+ * XX (the custom OID number - providing 255 possible custom oids)
+ */
+#define OID_RT_PRO_RESET_DUT 0xFF818000
+#define OID_RT_PRO_SET_DATA_RATE 0xFF818001
+#define OID_RT_PRO_START_TEST 0xFF818002
+#define OID_RT_PRO_STOP_TEST 0xFF818003
+#define OID_RT_PRO_SET_PREAMBLE 0xFF818004
+#define OID_RT_PRO_SET_SCRAMBLER 0xFF818005
+#define OID_RT_PRO_SET_FILTER_BB 0xFF818006
+#define OID_RT_PRO_SET_MANUAL_DIVERSITY_BB 0xFF818007
+#define OID_RT_PRO_SET_CHANNEL_DIRECT_CALL 0xFF818008
+#define OID_RT_PRO_SET_SLEEP_MODE_DIRECT_CALL 0xFF818009
+#define OID_RT_PRO_SET_WAKE_MODE_DIRECT_CALL 0xFF81800A
+
+#define OID_RT_PRO_SET_TX_ANTENNA_BB 0xFF81800D
+#define OID_RT_PRO_SET_ANTENNA_BB 0xFF81800E
+#define OID_RT_PRO_SET_CR_SCRAMBLER 0xFF81800F
+#define OID_RT_PRO_SET_CR_NEW_FILTER 0xFF818010
+#define OID_RT_PRO_SET_TX_POWER_CONTROL 0xFF818011
+#define OID_RT_PRO_SET_CR_TX_CONFIG 0xFF818012
+#define OID_RT_PRO_GET_TX_POWER_CONTROL 0xFF818013
+#define OID_RT_PRO_GET_CR_SIGNAL_QUALITY 0xFF818014
+#define OID_RT_PRO_SET_CR_SETPOINT 0xFF818015
+#define OID_RT_PRO_SET_INTEGRATOR 0xFF818016
+#define OID_RT_PRO_SET_SIGNAL_QUALITY 0xFF818017
+#define OID_RT_PRO_GET_INTEGRATOR 0xFF818018
+#define OID_RT_PRO_GET_SIGNAL_QUALITY 0xFF818019
+#define OID_RT_PRO_QUERY_EEPROM_TYPE 0xFF81801A
+#define OID_RT_PRO_WRITE_MAC_ADDRESS 0xFF81801B
+#define OID_RT_PRO_READ_MAC_ADDRESS 0xFF81801C
+#define OID_RT_PRO_WRITE_CIS_DATA 0xFF81801D
+#define OID_RT_PRO_READ_CIS_DATA 0xFF81801E
+#define OID_RT_PRO_WRITE_POWER_CONTROL 0xFF81801F
+#define OID_RT_PRO_READ_POWER_CONTROL 0xFF818020
+#define OID_RT_PRO_WRITE_EEPROM 0xFF818021
+#define OID_RT_PRO_READ_EEPROM 0xFF818022
+#define OID_RT_PRO_RESET_TX_PACKET_SENT 0xFF818023
+#define OID_RT_PRO_QUERY_TX_PACKET_SENT 0xFF818024
+#define OID_RT_PRO_RESET_RX_PACKET_RECEIVED 0xFF818025
+#define OID_RT_PRO_QUERY_RX_PACKET_RECEIVED 0xFF818026
+#define OID_RT_PRO_QUERY_RX_PACKET_CRC32_ERROR 0xFF818027
+#define OID_RT_PRO_QUERY_CURRENT_ADDRESS 0xFF818028
+#define OID_RT_PRO_QUERY_PERMANENT_ADDRESS 0xFF818029
+#define OID_RT_PRO_SET_PHILIPS_RF_PARAMETERS 0xFF81802A
+#define OID_RT_PRO_RECEIVE_PACKET 0xFF81802C
+#define OID_RT_PRO_WRITE_EEPROM_BYTE 0xFF81802D
+#define OID_RT_PRO_READ_EEPROM_BYTE 0xFF81802E
+#define OID_RT_PRO_SET_MODULATION 0xFF81802F
+#define OID_RT_DRIVER_OPTION 0xFF818080
+#define OID_RT_RF_OFF 0xFF818081
+#define OID_RT_AUTH_STATUS 0xFF818082
+#define OID_RT_PRO_SET_CONTINUOUS_TX 0xFF81800B
+#define OID_RT_PRO_SET_SINGLE_CARRIER_TX 0xFF81800C
+#define OID_RT_PRO_SET_CARRIER_SUPPRESSION_TX 0xFF81802B
+#define OID_RT_PRO_SET_SINGLE_TONE_TX 0xFF818043
+#define OID_RT_UTILITY_FALSE_ALARM_COUNTERS 0xFF818580
+#define OID_RT_UTILITY_SELECT_DEBUG_MODE 0xFF818581
+#define OID_RT_UTILITY_SELECT_SUBCARRIER_NUMBER 0xFF818582
+#define OID_RT_UTILITY_GET_RSSI_STATUS 0xFF818583
+#define OID_RT_UTILITY_GET_FRAME_DETECTION_STATUS 0xFF818584
+#define OID_RT_UTILITY_GET_AGC_AND_FREQUENCY_OFFSET_ESTIMATION_STATUS \
+ 0xFF818585
+#define OID_RT_UTILITY_GET_CHANNEL_ESTIMATION_STATUS 0xFF818586
+#define OID_RT_WIRELESS_MODE 0xFF818500
+#define OID_RT_SUPPORTED_RATES 0xFF818501
+#define OID_RT_DESIRED_RATES 0xFF818502
+#define OID_RT_WIRELESS_MODE_STARTING_ADHOC 0xFF818503
+#define OID_RT_GET_CONNECT_STATE 0xFF030001
+#define OID_RT_RESCAN 0xFF030002
+#define OID_RT_SET_KEY_LENGTH 0xFF030003
+#define OID_RT_SET_DEFAULT_KEY_ID 0xFF030004
+#define OID_RT_SET_CHANNEL 0xFF010182
+#define OID_RT_SET_SNIFFER_MODE 0xFF010183
+#define OID_RT_GET_SIGNAL_QUALITY 0xFF010184
+#define OID_RT_GET_SMALL_PACKET_CRC 0xFF010185
+#define OID_RT_GET_MIDDLE_PACKET_CRC 0xFF010186
+#define OID_RT_GET_LARGE_PACKET_CRC 0xFF010187
+#define OID_RT_GET_TX_RETRY 0xFF010188
+#define OID_RT_GET_RX_RETRY 0xFF010189
+#define OID_RT_PRO_SET_FW_DIG_STATE 0xFF01018A
+#define OID_RT_PRO_SET_FW_RA_STATE 0xFF01018B
+#define OID_RT_GET_RX_TOTAL_PACKET 0xFF010190
+#define OID_RT_GET_TX_BEACON_OK 0xFF010191
+#define OID_RT_GET_TX_BEACON_ERR 0xFF010192
+#define OID_RT_GET_RX_ICV_ERR 0xFF010193
+#define OID_RT_SET_ENCRYPTION_ALGORITHM 0xFF010194
+#define OID_RT_SET_NO_AUTO_RESCAN 0xFF010195
+#define OID_RT_GET_PREAMBLE_MODE 0xFF010196
+#define OID_RT_GET_DRIVER_UP_DELTA_TIME 0xFF010197
+#define OID_RT_GET_AP_IP 0xFF010198
+#define OID_RT_GET_CHANNELPLAN 0xFF010199
+#define OID_RT_SET_PREAMBLE_MODE 0xFF01019A
+#define OID_RT_SET_BCN_INTVL 0xFF01019B
+#define OID_RT_GET_RF_VENDER 0xFF01019C
+#define OID_RT_DEDICATE_PROBE 0xFF01019D
+#define OID_RT_PRO_RX_FILTER_PATTERN 0xFF01019E
+#define OID_RT_GET_DCST_CURRENT_THRESHOLD 0xFF01019F
+#define OID_RT_GET_CCA_ERR 0xFF0101A0
+#define OID_RT_GET_CCA_UPGRADE_THRESHOLD 0xFF0101A1
+#define OID_RT_GET_CCA_FALLBACK_THRESHOLD 0xFF0101A2
+#define OID_RT_GET_CCA_UPGRADE_EVALUATE_TIMES 0xFF0101A3
+#define OID_RT_GET_CCA_FALLBACK_EVALUATE_TIMES 0xFF0101A4
+#define OID_RT_SET_RATE_ADAPTIVE 0xFF0101A5
+#define OID_RT_GET_DCST_EVALUATE_PERIOD 0xFF0101A5
+#define OID_RT_GET_DCST_TIME_UNIT_INDEX 0xFF0101A6
+#define OID_RT_GET_TOTAL_TX_BYTES 0xFF0101A7
+#define OID_RT_GET_TOTAL_RX_BYTES 0xFF0101A8
+#define OID_RT_CURRENT_TX_POWER_LEVEL 0xFF0101A9
+#define OID_RT_GET_ENC_KEY_MISMATCH_COUNT 0xFF0101AA
+#define OID_RT_GET_ENC_KEY_MATCH_COUNT 0xFF0101AB
+#define OID_RT_GET_CHANNEL 0xFF0101AC
+#define OID_RT_SET_CHANNELPLAN 0xFF0101AD
+#define OID_RT_GET_HARDWARE_RADIO_OFF 0xFF0101AE
+#define OID_RT_CHANNELPLAN_BY_COUNTRY 0xFF0101AF
+#define OID_RT_SCAN_AVAILABLE_BSSID 0xFF0101B0
+#define OID_RT_GET_HARDWARE_VERSION 0xFF0101B1
+#define OID_RT_GET_IS_ROAMING 0xFF0101B2
+#define OID_RT_GET_IS_PRIVACY 0xFF0101B3
+#define OID_RT_GET_KEY_MISMATCH 0xFF0101B4
+#define OID_RT_SET_RSSI_ROAM_TRAFFIC_TH 0xFF0101B5
+#define OID_RT_SET_RSSI_ROAM_SIGNAL_TH 0xFF0101B6
+#define OID_RT_RESET_LOG 0xFF0101B7
+#define OID_RT_GET_LOG 0xFF0101B8
+#define OID_RT_SET_INDICATE_HIDDEN_AP 0xFF0101B9
+#define OID_RT_GET_HEADER_FAIL 0xFF0101BA
+#define OID_RT_SUPPORTED_WIRELESS_MODE 0xFF0101BB
+#define OID_RT_GET_CHANNEL_LIST 0xFF0101BC
+#define OID_RT_GET_SCAN_IN_PROGRESS 0xFF0101BD
+#define OID_RT_GET_TX_INFO 0xFF0101BE
+#define OID_RT_RF_READ_WRITE_OFFSET 0xFF0101BF
+#define OID_RT_RF_READ_WRITE 0xFF0101C0
+#define OID_RT_FORCED_DATA_RATE 0xFF0101C1
+#define OID_RT_WIRELESS_MODE_FOR_SCAN_LIST 0xFF0101C2
+#define OID_RT_GET_BSS_WIRELESS_MODE 0xFF0101C3
+#define OID_RT_SCAN_WITH_MAGIC_PACKET 0xFF0101C4
+#define OID_RT_PRO_RX_FILTER 0xFF0111C0
+#define OID_CE_USB_WRITE_REGISTRY 0xFF0111C1
+#define OID_CE_USB_READ_REGISTRY 0xFF0111C2
+#define OID_RT_PRO_SET_INITIAL_GAIN 0xFF0111C3
+#define OID_RT_PRO_SET_BB_RF_STANDBY_MODE 0xFF0111C4
+#define OID_RT_PRO_SET_BB_RF_SHUTDOWN_MODE 0xFF0111C5
+#define OID_RT_PRO_SET_TX_CHARGE_PUMP 0xFF0111C6
+#define OID_RT_PRO_SET_RX_CHARGE_PUMP 0xFF0111C7
+#define OID_RT_PRO_RF_WRITE_REGISTRY 0xFF0111C8
+#define OID_RT_PRO_RF_READ_REGISTRY 0xFF0111C9
+#define OID_RT_PRO_QUERY_RF_TYPE 0xFF0111CA
+#define OID_RT_AP_GET_ASSOCIATED_STATION_LIST 0xFF010300
+#define OID_RT_AP_GET_CURRENT_TIME_STAMP 0xFF010301
+#define OID_RT_AP_SWITCH_INTO_AP_MODE 0xFF010302
+#define OID_RT_AP_SET_DTIM_PERIOD 0xFF010303
+#define OID_RT_AP_SUPPORTED 0xFF010304
+#define OID_RT_AP_SET_PASSPHRASE 0xFF010305
+#define OID_RT_PRO8187_WI_POLL 0xFF818780
+#define OID_RT_PRO_WRITE_BB_REG 0xFF818781
+#define OID_RT_PRO_READ_BB_REG 0xFF818782
+#define OID_RT_PRO_WRITE_RF_REG 0xFF818783
+#define OID_RT_PRO_READ_RF_REG 0xFF818784
+#define OID_RT_MH_VENDER_ID 0xFFEDC100
+#define OID_RT_PRO8711_JOIN_BSS 0xFF871100
+#define OID_RT_PRO_READ_REGISTER 0xFF871101
+#define OID_RT_PRO_WRITE_REGISTER 0xFF871102
+#define OID_RT_PRO_BURST_READ_REGISTER 0xFF871103
+#define OID_RT_PRO_BURST_WRITE_REGISTER 0xFF871104
+#define OID_RT_PRO_WRITE_TXCMD 0xFF871105
+#define OID_RT_PRO_READ16_EEPROM 0xFF871106
+#define OID_RT_PRO_WRITE16_EEPROM 0xFF871107
+#define OID_RT_PRO_H2C_SET_COMMAND 0xFF871108
+#define OID_RT_PRO_H2C_QUERY_RESULT 0xFF871109
+#define OID_RT_PRO8711_WI_POLL 0xFF87110A
+#define OID_RT_PRO8711_PKT_LOSS 0xFF87110B
+#define OID_RT_RD_ATTRIB_MEM 0xFF87110C
+#define OID_RT_WR_ATTRIB_MEM 0xFF87110D
+/*Method 2 for H2C/C2H*/
+#define OID_RT_PRO_H2C_CMD_MODE 0xFF871110
+#define OID_RT_PRO_H2C_CMD_RSP_MODE 0xFF871111
+#define OID_RT_PRO_H2C_CMD_EVENT_MODE 0xFF871112
+#define OID_RT_PRO_WAIT_C2H_EVENT 0xFF871113
+#define OID_RT_PRO_RW_ACCESS_PROTOCOL_TEST 0xFF871114
+#define OID_RT_PRO_SCSI_ACCESS_TEST 0xFF871115
+#define OID_RT_PRO_SCSI_TCPIPOFFLOAD_OUT 0xFF871116
+#define OID_RT_PRO_SCSI_TCPIPOFFLOAD_IN 0xFF871117
+#define OID_RT_RRO_RX_PKT_VIA_IOCTRL 0xFF871118
+#define OID_RT_RRO_RX_PKTARRAY_VIA_IOCTRL 0xFF871119
+#define OID_RT_RPO_SET_PWRMGT_TEST 0xFF87111A
+#define OID_RT_PRO_QRY_PWRMGT_TEST 0XFF87111B
+#define OID_RT_RPO_ASYNC_RWIO_TEST 0xFF87111C
+#define OID_RT_RPO_ASYNC_RWIO_POLL 0xFF87111D
+#define OID_RT_PRO_SET_RF_INTFS 0xFF87111E
+#define OID_RT_POLL_RX_STATUS 0xFF87111F
+#define OID_RT_PRO_CFG_DEBUG_MESSAGE 0xFF871120
+#define OID_RT_PRO_SET_DATA_RATE_EX 0xFF871121
+#define OID_RT_PRO_SET_BASIC_RATE 0xFF871122
+#define OID_RT_PRO_READ_TSSI 0xFF871123
+#define OID_RT_PRO_SET_POWER_TRACKING 0xFF871124
+#define OID_RT_PRO_QRY_PWRSTATE 0xFF871150
+#define OID_RT_PRO_SET_PWRSTATE 0xFF871151
+/*Method 2 , using workitem */
+#define OID_RT_SET_READ_REG 0xFF871181
+#define OID_RT_SET_WRITE_REG 0xFF871182
+#define OID_RT_SET_BURST_READ_REG 0xFF871183
+#define OID_RT_SET_BURST_WRITE_REG 0xFF871184
+#define OID_RT_SET_WRITE_TXCMD 0xFF871185
+#define OID_RT_SET_READ16_EEPROM 0xFF871186
+#define OID_RT_SET_WRITE16_EEPROM 0xFF871187
+#define OID_RT_QRY_POLL_WKITEM 0xFF871188
+
+/*For SDIO INTERFACE only*/
+#define OID_RT_PRO_SYNCPAGERW_SRAM 0xFF8711A0
+#define OID_RT_PRO_871X_DRV_EXT 0xFF8711A1
+
+/*For USB INTERFACE only*/
+#define OID_RT_PRO_USB_VENDOR_REQ 0xFF8711B0
+#define OID_RT_PRO_SCSI_AUTO_TEST 0xFF8711B1
+#define OID_RT_PRO_USB_MAC_AC_FIFO_WRITE 0xFF8711B2
+#define OID_RT_PRO_USB_MAC_RX_FIFO_READ 0xFF8711B3
+#define OID_RT_PRO_USB_MAC_RX_FIFO_POLLING 0xFF8711B4
+
+#define OID_RT_PRO_H2C_SET_RATE_TABLE 0xFF8711FB
+#define OID_RT_PRO_H2C_GET_RATE_TABLE 0xFF8711FC
+#define OID_RT_PRO_H2C_C2H_LBK_TEST 0xFF8711FE
+
+#define OID_RT_PRO_ENCRYPTION_CTRL 0xFF871200
+#define OID_RT_PRO_ADD_STA_INFO 0xFF871201
+#define OID_RT_PRO_DELE_STA_INFO 0xFF871202
+#define OID_RT_PRO_QUERY_DR_VARIABLE 0xFF871203
+
+#define OID_RT_PRO_RX_PACKET_TYPE 0xFF871204
+
+#define OID_RT_PRO_READ_EFUSE 0xFF871205
+#define OID_RT_PRO_WRITE_EFUSE 0xFF871206
+#define OID_RT_PRO_RW_EFUSE_PGPKT 0xFF871207
+#define OID_RT_GET_EFUSE_CURRENT_SIZE 0xFF871208
+
+#define OID_RT_SET_BANDWIDTH 0xFF871209
+#define OID_RT_SET_CRYSTAL_CAP 0xFF87120A
+
+#define OID_RT_SET_RX_PACKET_TYPE 0xFF87120B
+
+#define OID_RT_GET_EFUSE_MAX_SIZE 0xFF87120C
+
+#define OID_RT_PRO_SET_TX_AGC_OFFSET 0xFF87120D
+
+#define OID_RT_PRO_SET_PKT_TEST_MODE 0xFF87120E
+
+#define OID_RT_PRO_FOR_EVM_TEST_SETTING 0xFF87120F
+
+#define OID_RT_PRO_GET_THERMAL_METER 0xFF871210
+
+#define OID_RT_RESET_PHY_RX_PACKET_COUNT 0xFF871211
+#define OID_RT_GET_PHY_RX_PACKET_RECEIVED 0xFF871212
+#define OID_RT_GET_PHY_RX_PACKET_CRC32_ERROR 0xFF871213
+
+#define OID_RT_SET_POWER_DOWN 0xFF871214
+
+#define OID_RT_GET_POWER_MODE 0xFF871215
+
+#define OID_RT_PRO_EFUSE 0xFF871216
+#define OID_RT_PRO_EFUSE_MAP 0xFF871217
+
+#endif /*#ifndef __CUSTOM_OID_H */
+
diff --git a/drivers/staging/rtl8712/os_intfs.c b/drivers/staging/rtl8712/os_intfs.c
new file mode 100644
index 000000000..6e776e543
--- /dev/null
+++ b/drivers/staging/rtl8712/os_intfs.c
@@ -0,0 +1,478 @@
+/******************************************************************************
+ * os_intfs.c
+ *
+ * Copyright(c) 2007 - 2010 Realtek Corporation. All rights reserved.
+ * Linux device driver for RTL8192SU
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ * Modifications for inclusion into the Linux staging tree are
+ * Copyright(c) 2010 Larry Finger. All rights reserved.
+ *
+ * Contact information:
+ * WLAN FAE <wlanfae@realtek.com>.
+ * Larry Finger <Larry.Finger@lwfinger.net>
+ *
+ ******************************************************************************/
+
+#define _OS_INTFS_C_
+
+#include <linux/module.h>
+#include <linux/kthread.h>
+#include <linux/firmware.h>
+#include "osdep_service.h"
+#include "drv_types.h"
+#include "xmit_osdep.h"
+#include "recv_osdep.h"
+#include "rtl871x_ioctl.h"
+#include "usb_osintf.h"
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("rtl871x wireless lan driver");
+MODULE_AUTHOR("Larry Finger");
+
+static char ifname[IFNAMSIZ] = "wlan%d";
+
+/* module param defaults */
+static int chip_version = RTL8712_2ndCUT;
+static int rfintfs = HWPI;
+static int lbkmode = RTL8712_AIR_TRX;
+static int hci = RTL8712_USB;
+static int ampdu_enable = 1;/*for enable tx_ampdu*/
+
+/* The video_mode variable is for video mode.*/
+/* It may be specify when inserting module with video_mode=1 parameter.*/
+static int video_mode = 1; /* enable video mode*/
+
+/*Ndis802_11Infrastructure; infra, ad-hoc, auto*/
+static int network_mode = Ndis802_11IBSS;
+static int channel = 1;/*ad-hoc support requirement*/
+static int wireless_mode = WIRELESS_11BG;
+static int vrtl_carrier_sense = AUTO_VCS;
+static int vcs_type = RTS_CTS;
+static int frag_thresh = 2346;
+static int preamble = PREAMBLE_LONG;/*long, short, auto*/
+static int scan_mode = 1;/*active, passive*/
+static int adhoc_tx_pwr = 1;
+static int soft_ap;
+static int smart_ps = 1;
+static int power_mgnt = PS_MODE_ACTIVE;
+static int radio_enable = 1;
+static int long_retry_lmt = 7;
+static int short_retry_lmt = 7;
+static int busy_thresh = 40;
+static int ack_policy = NORMAL_ACK;
+static int mp_mode;
+static int software_encrypt;
+static int software_decrypt;
+
+static int wmm_enable;/* default is set to disable the wmm.*/
+static int uapsd_enable;
+static int uapsd_max_sp = NO_LIMIT;
+static int uapsd_acbk_en;
+static int uapsd_acbe_en;
+static int uapsd_acvi_en;
+static int uapsd_acvo_en;
+
+static int ht_enable = 1;
+static int cbw40_enable = 1;
+static int rf_config = RTL8712_RF_1T2R; /* 1T2R*/
+static int low_power;
+/* mac address to use instead of the one stored in Efuse */
+char *r8712_initmac;
+static char *initmac;
+/* if wifi_test = 1, driver will disable the turbo mode and pass it to
+ * firmware private.
+ */
+static int wifi_test;
+
+module_param_string(ifname, ifname, sizeof(ifname), S_IRUGO|S_IWUSR);
+module_param(wifi_test, int, 0644);
+module_param(initmac, charp, 0644);
+module_param(video_mode, int, 0644);
+module_param(chip_version, int, 0644);
+module_param(rfintfs, int, 0644);
+module_param(lbkmode, int, 0644);
+module_param(hci, int, 0644);
+module_param(network_mode, int, 0644);
+module_param(channel, int, 0644);
+module_param(mp_mode, int, 0644);
+module_param(wmm_enable, int, 0644);
+module_param(vrtl_carrier_sense, int, 0644);
+module_param(vcs_type, int, 0644);
+module_param(busy_thresh, int, 0644);
+module_param(ht_enable, int, 0644);
+module_param(cbw40_enable, int, 0644);
+module_param(ampdu_enable, int, 0644);
+module_param(rf_config, int, 0644);
+module_param(power_mgnt, int, 0644);
+module_param(low_power, int, 0644);
+
+MODULE_PARM_DESC(ifname, " Net interface name, wlan%d=default");
+MODULE_PARM_DESC(initmac, "MAC-Address, default: use FUSE");
+
+static uint loadparam(struct _adapter *padapter, struct net_device *pnetdev);
+static int netdev_open(struct net_device *pnetdev);
+static int netdev_close(struct net_device *pnetdev);
+
+static uint loadparam(struct _adapter *padapter, struct net_device *pnetdev)
+{
+ uint status = _SUCCESS;
+ struct registry_priv *registry_par = &padapter->registrypriv;
+
+ registry_par->chip_version = (u8)chip_version;
+ registry_par->rfintfs = (u8)rfintfs;
+ registry_par->lbkmode = (u8)lbkmode;
+ registry_par->hci = (u8)hci;
+ registry_par->network_mode = (u8)network_mode;
+ memcpy(registry_par->ssid.Ssid, "ANY", 3);
+ registry_par->ssid.SsidLength = 3;
+ registry_par->channel = (u8)channel;
+ registry_par->wireless_mode = (u8)wireless_mode;
+ registry_par->vrtl_carrier_sense = (u8)vrtl_carrier_sense;
+ registry_par->vcs_type = (u8)vcs_type;
+ registry_par->frag_thresh = (u16)frag_thresh;
+ registry_par->preamble = (u8)preamble;
+ registry_par->scan_mode = (u8)scan_mode;
+ registry_par->adhoc_tx_pwr = (u8)adhoc_tx_pwr;
+ registry_par->soft_ap = (u8)soft_ap;
+ registry_par->smart_ps = (u8)smart_ps;
+ registry_par->power_mgnt = (u8)power_mgnt;
+ registry_par->radio_enable = (u8)radio_enable;
+ registry_par->long_retry_lmt = (u8)long_retry_lmt;
+ registry_par->short_retry_lmt = (u8)short_retry_lmt;
+ registry_par->busy_thresh = (u16)busy_thresh;
+ registry_par->ack_policy = (u8)ack_policy;
+ registry_par->mp_mode = (u8)mp_mode;
+ registry_par->software_encrypt = (u8)software_encrypt;
+ registry_par->software_decrypt = (u8)software_decrypt;
+ /*UAPSD*/
+ registry_par->wmm_enable = (u8)wmm_enable;
+ registry_par->uapsd_enable = (u8)uapsd_enable;
+ registry_par->uapsd_max_sp = (u8)uapsd_max_sp;
+ registry_par->uapsd_acbk_en = (u8)uapsd_acbk_en;
+ registry_par->uapsd_acbe_en = (u8)uapsd_acbe_en;
+ registry_par->uapsd_acvi_en = (u8)uapsd_acvi_en;
+ registry_par->uapsd_acvo_en = (u8)uapsd_acvo_en;
+ registry_par->ht_enable = (u8)ht_enable;
+ registry_par->cbw40_enable = (u8)cbw40_enable;
+ registry_par->ampdu_enable = (u8)ampdu_enable;
+ registry_par->rf_config = (u8)rf_config;
+ registry_par->low_power = (u8)low_power;
+ registry_par->wifi_test = (u8) wifi_test;
+ r8712_initmac = initmac;
+ return status;
+}
+
+static int r871x_net_set_mac_address(struct net_device *pnetdev, void *p)
+{
+ struct _adapter *padapter = netdev_priv(pnetdev);
+ struct sockaddr *addr = p;
+
+ if (padapter->bup == false)
+ memcpy(pnetdev->dev_addr, addr->sa_data, ETH_ALEN);
+ return 0;
+}
+
+static struct net_device_stats *r871x_net_get_stats(struct net_device *pnetdev)
+{
+ struct _adapter *padapter = netdev_priv(pnetdev);
+ struct xmit_priv *pxmitpriv = &(padapter->xmitpriv);
+ struct recv_priv *precvpriv = &(padapter->recvpriv);
+
+ padapter->stats.tx_packets = pxmitpriv->tx_pkts;
+ padapter->stats.rx_packets = precvpriv->rx_pkts;
+ padapter->stats.tx_dropped = pxmitpriv->tx_drop;
+ padapter->stats.rx_dropped = precvpriv->rx_drop;
+ padapter->stats.tx_bytes = pxmitpriv->tx_bytes;
+ padapter->stats.rx_bytes = precvpriv->rx_bytes;
+ return &padapter->stats;
+}
+
+static const struct net_device_ops rtl8712_netdev_ops = {
+ .ndo_open = netdev_open,
+ .ndo_stop = netdev_close,
+ .ndo_start_xmit = r8712_xmit_entry,
+ .ndo_set_mac_address = r871x_net_set_mac_address,
+ .ndo_get_stats = r871x_net_get_stats,
+ .ndo_do_ioctl = r871x_ioctl,
+};
+
+struct net_device *r8712_init_netdev(void)
+{
+ struct _adapter *padapter;
+ struct net_device *pnetdev;
+
+ pnetdev = alloc_etherdev(sizeof(struct _adapter));
+ if (!pnetdev)
+ return NULL;
+ if (dev_alloc_name(pnetdev, ifname) < 0) {
+ strcpy(ifname, "wlan%d");
+ dev_alloc_name(pnetdev, ifname);
+ }
+ padapter = netdev_priv(pnetdev);
+ padapter->pnetdev = pnetdev;
+ pr_info("r8712u: register rtl8712_netdev_ops to netdev_ops\n");
+ pnetdev->netdev_ops = &rtl8712_netdev_ops;
+ pnetdev->watchdog_timeo = HZ; /* 1 second timeout */
+ pnetdev->wireless_handlers = (struct iw_handler_def *)
+ &r871x_handlers_def;
+ /*step 2.*/
+ loadparam(padapter, pnetdev);
+ netif_carrier_off(pnetdev);
+ padapter->pid = 0; /* Initial the PID value used for HW PBC.*/
+ return pnetdev;
+}
+
+static u32 start_drv_threads(struct _adapter *padapter)
+{
+ padapter->cmdThread = kthread_run(r8712_cmd_thread, padapter, "%s",
+ padapter->pnetdev->name);
+ if (IS_ERR(padapter->cmdThread))
+ return _FAIL;
+ return _SUCCESS;
+}
+
+void r8712_stop_drv_threads(struct _adapter *padapter)
+{
+ /*Below is to terminate r8712_cmd_thread & event_thread...*/
+ up(&padapter->cmdpriv.cmd_queue_sema);
+ if (padapter->cmdThread)
+ _down_sema(&padapter->cmdpriv.terminate_cmdthread_sema);
+ padapter->cmdpriv.cmd_seq = 1;
+}
+
+static void start_drv_timers(struct _adapter *padapter)
+{
+ mod_timer(&padapter->mlmepriv.sitesurveyctrl.sitesurvey_ctrl_timer,
+ jiffies + msecs_to_jiffies(5000));
+ mod_timer(&padapter->mlmepriv.wdg_timer,
+ jiffies + msecs_to_jiffies(2000));
+}
+
+void r8712_stop_drv_timers(struct _adapter *padapter)
+{
+ del_timer_sync(&padapter->mlmepriv.assoc_timer);
+ del_timer_sync(&padapter->securitypriv.tkip_timer);
+ del_timer_sync(&padapter->mlmepriv.scan_to_timer);
+ del_timer_sync(&padapter->mlmepriv.dhcp_timer);
+ del_timer_sync(&padapter->mlmepriv.wdg_timer);
+ del_timer_sync(&padapter->mlmepriv.sitesurveyctrl.sitesurvey_ctrl_timer);
+}
+
+static u8 init_default_value(struct _adapter *padapter)
+{
+ u8 ret = _SUCCESS;
+ struct registry_priv *pregistrypriv = &padapter->registrypriv;
+ struct xmit_priv *pxmitpriv = &padapter->xmitpriv;
+ struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
+ struct security_priv *psecuritypriv = &padapter->securitypriv;
+
+ /*xmit_priv*/
+ pxmitpriv->vcs_setting = pregistrypriv->vrtl_carrier_sense;
+ pxmitpriv->vcs = pregistrypriv->vcs_type;
+ pxmitpriv->vcs_type = pregistrypriv->vcs_type;
+ pxmitpriv->rts_thresh = pregistrypriv->rts_thresh;
+ pxmitpriv->frag_len = pregistrypriv->frag_thresh;
+ /* mlme_priv */
+ /* Maybe someday we should rename this variable to "active_mode"(Jeff)*/
+ pmlmepriv->passive_mode = 1; /* 1: active, 0: passive. */
+ /*ht_priv*/
+ {
+ int i;
+ struct ht_priv *phtpriv = &pmlmepriv->htpriv;
+
+ phtpriv->ampdu_enable = false;/*set to disabled*/
+ for (i = 0; i < 16; i++)
+ phtpriv->baddbareq_issued[i] = false;
+ }
+ /*security_priv*/
+ psecuritypriv->sw_encrypt = pregistrypriv->software_encrypt;
+ psecuritypriv->sw_decrypt = pregistrypriv->software_decrypt;
+ psecuritypriv->binstallGrpkey = _FAIL;
+ /*pwrctrl_priv*/
+ /*registry_priv*/
+ r8712_init_registrypriv_dev_network(padapter);
+ r8712_update_registrypriv_dev_network(padapter);
+ /*misc.*/
+ return ret;
+}
+
+u8 r8712_init_drv_sw(struct _adapter *padapter)
+{
+ if ((r8712_init_cmd_priv(&padapter->cmdpriv)) == _FAIL)
+ return _FAIL;
+ padapter->cmdpriv.padapter = padapter;
+ if ((r8712_init_evt_priv(&padapter->evtpriv)) == _FAIL)
+ return _FAIL;
+ if (r8712_init_mlme_priv(padapter) == _FAIL)
+ return _FAIL;
+ _r8712_init_xmit_priv(&padapter->xmitpriv, padapter);
+ _r8712_init_recv_priv(&padapter->recvpriv, padapter);
+ memset((unsigned char *)&padapter->securitypriv, 0,
+ sizeof(struct security_priv));
+ setup_timer(&padapter->securitypriv.tkip_timer,
+ r8712_use_tkipkey_handler, (unsigned long)padapter);
+ _r8712_init_sta_priv(&padapter->stapriv);
+ padapter->stapriv.padapter = padapter;
+ r8712_init_bcmc_stainfo(padapter);
+ r8712_init_pwrctrl_priv(padapter);
+ mp871xinit(padapter);
+ if (init_default_value(padapter) != _SUCCESS)
+ return _FAIL;
+ r8712_InitSwLeds(padapter);
+ return _SUCCESS;
+}
+
+u8 r8712_free_drv_sw(struct _adapter *padapter)
+{
+ struct net_device *pnetdev = (struct net_device *)padapter->pnetdev;
+
+ r8712_free_cmd_priv(&padapter->cmdpriv);
+ r8712_free_evt_priv(&padapter->evtpriv);
+ r8712_DeInitSwLeds(padapter);
+ r8712_free_mlme_priv(&padapter->mlmepriv);
+ r8712_free_io_queue(padapter);
+ _free_xmit_priv(&padapter->xmitpriv);
+ _r8712_free_sta_priv(&padapter->stapriv);
+ _r8712_free_recv_priv(&padapter->recvpriv);
+ mp871xdeinit(padapter);
+ if (pnetdev)
+ free_netdev(pnetdev);
+ return _SUCCESS;
+}
+
+
+static void enable_video_mode(struct _adapter *padapter, int cbw40_value)
+{
+ /* bit 8:
+ * 1 -> enable video mode to 96B AP
+ * 0 -> disable video mode to 96B AP
+ * bit 9:
+ * 1 -> enable 40MHz mode
+ * 0 -> disable 40MHz mode
+ * bit 10:
+ * 1 -> enable STBC
+ * 0 -> disable STBC
+ */
+ u32 intcmd = 0xf4000500; /* enable bit8, bit10*/
+
+ if (cbw40_value) {
+ /* if the driver supports the 40M bandwidth,
+ * we can enable the bit 9.*/
+ intcmd |= 0x200;
+ }
+ r8712_fw_cmd(padapter, intcmd);
+}
+
+/**
+ *
+ * This function intends to handle the activation of an interface
+ * i.e. when it is brought Up/Active from a Down state.
+ *
+ */
+static int netdev_open(struct net_device *pnetdev)
+{
+ struct _adapter *padapter = netdev_priv(pnetdev);
+
+ mutex_lock(&padapter->mutex_start);
+ if (padapter->bup == false) {
+ padapter->bDriverStopped = false;
+ padapter->bSurpriseRemoved = false;
+ padapter->bup = true;
+ if (rtl871x_hal_init(padapter) != _SUCCESS)
+ goto netdev_open_error;
+ if (r8712_initmac == NULL)
+ /* Use the mac address stored in the Efuse */
+ memcpy(pnetdev->dev_addr,
+ padapter->eeprompriv.mac_addr, ETH_ALEN);
+ else {
+ /* We have to inform f/w to use user-supplied MAC
+ * address.
+ */
+ msleep(200);
+ r8712_setMacAddr_cmd(padapter, (u8 *)pnetdev->dev_addr);
+ /*
+ * The "myid" function will get the wifi mac address
+ * from eeprompriv structure instead of netdev
+ * structure. So, we have to overwrite the mac_addr
+ * stored in the eeprompriv structure. In this case,
+ * the real mac address won't be used anymore. So that,
+ * the eeprompriv.mac_addr should store the mac which
+ * users specify.
+ */
+ memcpy(padapter->eeprompriv.mac_addr,
+ pnetdev->dev_addr, ETH_ALEN);
+ }
+ if (start_drv_threads(padapter) != _SUCCESS)
+ goto netdev_open_error;
+ if (padapter->dvobjpriv.inirp_init == NULL)
+ goto netdev_open_error;
+ else
+ padapter->dvobjpriv.inirp_init(padapter);
+ r8712_set_ps_mode(padapter, padapter->registrypriv.power_mgnt,
+ padapter->registrypriv.smart_ps);
+ }
+ if (!netif_queue_stopped(pnetdev))
+ netif_start_queue(pnetdev);
+ else
+ netif_wake_queue(pnetdev);
+
+ if (video_mode)
+ enable_video_mode(padapter, cbw40_enable);
+ /* start driver mlme relation timer */
+ start_drv_timers(padapter);
+ padapter->ledpriv.LedControlHandler(padapter, LED_CTL_NO_LINK);
+ mutex_unlock(&padapter->mutex_start);
+ return 0;
+netdev_open_error:
+ padapter->bup = false;
+ netif_carrier_off(pnetdev);
+ netif_stop_queue(pnetdev);
+ mutex_unlock(&padapter->mutex_start);
+ return -1;
+}
+
+/**
+ *
+ * This function intends to handle the shutdown of an interface
+ * i.e. when it is brought Down from an Up/Active state.
+ *
+ */
+static int netdev_close(struct net_device *pnetdev)
+{
+ struct _adapter *padapter = netdev_priv(pnetdev);
+
+ /* Close LED*/
+ padapter->ledpriv.LedControlHandler(padapter, LED_CTL_POWER_OFF);
+ msleep(200);
+
+ /*s1.*/
+ if (pnetdev) {
+ if (!netif_queue_stopped(pnetdev))
+ netif_stop_queue(pnetdev);
+ }
+ /*s2.*/
+ /*s2-1. issue disassoc_cmd to fw*/
+ r8712_disassoc_cmd(padapter);
+ /*s2-2. indicate disconnect to os*/
+ r8712_ind_disconnect(padapter);
+ /*s2-3.*/
+ r8712_free_assoc_resources(padapter);
+ /*s2-4.*/
+ r8712_free_network_queue(padapter);
+ return 0;
+}
+
+#include "mlme_osdep.h"
diff --git a/drivers/staging/rtl8712/osdep_intf.h b/drivers/staging/rtl8712/osdep_intf.h
new file mode 100644
index 000000000..aa0ec74af
--- /dev/null
+++ b/drivers/staging/rtl8712/osdep_intf.h
@@ -0,0 +1,44 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2010 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ * Modifications for inclusion into the Linux staging tree are
+ * Copyright(c) 2010 Larry Finger. All rights reserved.
+ *
+ * Contact information:
+ * WLAN FAE <wlanfae@realtek.com>
+ * Larry Finger <Larry.Finger@lwfinger.net>
+ *
+ ******************************************************************************/
+#ifndef __OSDEP_INTF_H_
+#define __OSDEP_INTF_H_
+
+#include "osdep_service.h"
+#include "drv_types.h"
+
+#define RND4(x) (((x >> 2) + (((x & 3) == 0) ? 0 : 1)) << 2)
+
+struct intf_priv {
+ u8 *intf_dev;
+ /* when in USB, IO is through interrupt in/out endpoints */
+ struct usb_device *udev;
+ struct urb *piorw_urb;
+ struct semaphore io_retevt;
+};
+
+int r871x_ioctl(struct net_device *dev, struct ifreq *rq, int cmd);
+
+#endif /*_OSDEP_INTF_H_*/
diff --git a/drivers/staging/rtl8712/osdep_service.h b/drivers/staging/rtl8712/osdep_service.h
new file mode 100644
index 000000000..0a7f58c59
--- /dev/null
+++ b/drivers/staging/rtl8712/osdep_service.h
@@ -0,0 +1,96 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2010 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ * Modifications for inclusion into the Linux staging tree are
+ * Copyright(c) 2010 Larry Finger. All rights reserved.
+ *
+ * Contact information:
+ * WLAN FAE <wlanfae@realtek.com>
+ * Larry Finger <Larry.Finger@lwfinger.net>
+ *
+ ******************************************************************************/
+#ifndef __OSDEP_SERVICE_H_
+#define __OSDEP_SERVICE_H_
+
+#define _SUCCESS 1
+#define _FAIL 0
+
+#include <linux/spinlock.h>
+
+#include <linux/interrupt.h>
+#include <linux/semaphore.h>
+#include <linux/sched.h>
+#include <linux/sem.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <net/iw_handler.h>
+#include <linux/proc_fs.h> /* Necessary because we use the proc fs */
+
+#include "basic_types.h"
+
+struct __queue {
+ struct list_head queue;
+ spinlock_t lock;
+};
+
+#define _pkt struct sk_buff
+#define _buffer unsigned char
+#define thread_exit() complete_and_exit(NULL, 0)
+
+#define _init_queue(pqueue) \
+ do { \
+ INIT_LIST_HEAD(&((pqueue)->queue)); \
+ spin_lock_init(&((pqueue)->lock)); \
+ } while (0)
+
+#define LIST_CONTAINOR(ptr, type, member) \
+ ((type *)((char *)(ptr)-(SIZE_T)(&((type *)0)->member)))
+
+#ifndef BIT
+ #define BIT(x) (1 << (x))
+#endif
+
+static inline u32 _down_sema(struct semaphore *sema)
+{
+ if (down_interruptible(sema))
+ return _FAIL;
+ return _SUCCESS;
+}
+
+static inline u32 end_of_queue_search(struct list_head *head,
+ struct list_head *plist)
+{
+ return (head == plist);
+}
+
+static inline void sleep_schedulable(int ms)
+{
+ u32 delta;
+
+ delta = msecs_to_jiffies(ms);/*(ms)*/
+ set_current_state(TASK_INTERRUPTIBLE);
+ schedule_timeout(delta);
+}
+
+static inline void flush_signals_thread(void)
+{
+ if (signal_pending(current))
+ flush_signals(current);
+}
+
+#endif
+
diff --git a/drivers/staging/rtl8712/recv_linux.c b/drivers/staging/rtl8712/recv_linux.c
new file mode 100644
index 000000000..799a0f9a5
--- /dev/null
+++ b/drivers/staging/rtl8712/recv_linux.c
@@ -0,0 +1,153 @@
+/******************************************************************************
+ * recv_linux.c
+ *
+ * Copyright(c) 2007 - 2010 Realtek Corporation. All rights reserved.
+ * Linux device driver for RTL8192SU
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ * Modifications for inclusion into the Linux staging tree are
+ * Copyright(c) 2010 Larry Finger. All rights reserved.
+ *
+ * Contact information:
+ * WLAN FAE <wlanfae@realtek.com>.
+ * Larry Finger <Larry.Finger@lwfinger.net>
+ *
+ ******************************************************************************/
+
+#define _RECV_OSDEP_C_
+
+#include <linux/usb.h>
+
+#include "osdep_service.h"
+#include "drv_types.h"
+#include "wifi.h"
+#include "recv_osdep.h"
+#include "osdep_intf.h"
+#include "ethernet.h"
+#include <linux/if_arp.h>
+#include "usb_ops.h"
+
+/*init os related resource in struct recv_priv*/
+/*alloc os related resource in union recv_frame*/
+int r8712_os_recv_resource_alloc(struct _adapter *padapter,
+ union recv_frame *precvframe)
+{
+ precvframe->u.hdr.pkt_newalloc = precvframe->u.hdr.pkt = NULL;
+ return _SUCCESS;
+}
+
+/*alloc os related resource in struct recv_buf*/
+int r8712_os_recvbuf_resource_alloc(struct _adapter *padapter,
+ struct recv_buf *precvbuf)
+{
+ int res = _SUCCESS;
+
+ precvbuf->irp_pending = false;
+ precvbuf->purb = usb_alloc_urb(0, GFP_KERNEL);
+ if (precvbuf->purb == NULL)
+ res = _FAIL;
+ precvbuf->pskb = NULL;
+ precvbuf->reuse = false;
+ precvbuf->pallocated_buf = NULL;
+ precvbuf->pbuf = NULL;
+ precvbuf->pdata = NULL;
+ precvbuf->phead = NULL;
+ precvbuf->ptail = NULL;
+ precvbuf->pend = NULL;
+ precvbuf->transfer_len = 0;
+ precvbuf->len = 0;
+ return res;
+}
+
+/*free os related resource in struct recv_buf*/
+int r8712_os_recvbuf_resource_free(struct _adapter *padapter,
+ struct recv_buf *precvbuf)
+{
+ if (precvbuf->pskb)
+ dev_kfree_skb_any(precvbuf->pskb);
+ if (precvbuf->purb) {
+ usb_kill_urb(precvbuf->purb);
+ usb_free_urb(precvbuf->purb);
+ }
+ return _SUCCESS;
+}
+
+void r8712_handle_tkip_mic_err(struct _adapter *padapter, u8 bgroup)
+{
+ union iwreq_data wrqu;
+ struct iw_michaelmicfailure ev;
+ struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
+
+ memset(&ev, 0x00, sizeof(ev));
+ if (bgroup)
+ ev.flags |= IW_MICFAILURE_GROUP;
+ else
+ ev.flags |= IW_MICFAILURE_PAIRWISE;
+ ev.src_addr.sa_family = ARPHRD_ETHER;
+ ether_addr_copy(ev.src_addr.sa_data, &pmlmepriv->assoc_bssid[0]);
+ memset(&wrqu, 0x00, sizeof(wrqu));
+ wrqu.data.length = sizeof(ev);
+ wireless_send_event(padapter->pnetdev, IWEVMICHAELMICFAILURE, &wrqu,
+ (char *)&ev);
+}
+
+void r8712_recv_indicatepkt(struct _adapter *padapter,
+ union recv_frame *precv_frame)
+{
+ struct recv_priv *precvpriv;
+ struct __queue *pfree_recv_queue;
+ _pkt *skb;
+ struct rx_pkt_attrib *pattrib = &precv_frame->u.hdr.attrib;
+
+ precvpriv = &(padapter->recvpriv);
+ pfree_recv_queue = &(precvpriv->free_recv_queue);
+ skb = precv_frame->u.hdr.pkt;
+ if (skb == NULL)
+ goto _recv_indicatepkt_drop;
+ skb->data = precv_frame->u.hdr.rx_data;
+ skb->len = precv_frame->u.hdr.len;
+ skb_set_tail_pointer(skb, skb->len);
+ if ((pattrib->tcpchk_valid == 1) && (pattrib->tcp_chkrpt == 1))
+ skb->ip_summed = CHECKSUM_UNNECESSARY;
+ else
+ skb->ip_summed = CHECKSUM_NONE;
+ skb->dev = padapter->pnetdev;
+ skb->protocol = eth_type_trans(skb, padapter->pnetdev);
+ netif_rx(skb);
+ precv_frame->u.hdr.pkt = NULL; /* pointers to NULL before
+ * r8712_free_recvframe() */
+ r8712_free_recvframe(precv_frame, pfree_recv_queue);
+ return;
+_recv_indicatepkt_drop:
+ /*enqueue back to free_recv_queue*/
+ if (precv_frame)
+ r8712_free_recvframe(precv_frame, pfree_recv_queue);
+ precvpriv->rx_drop++;
+}
+
+static void _r8712_reordering_ctrl_timeout_handler (unsigned long data)
+{
+ struct recv_reorder_ctrl *preorder_ctrl =
+ (struct recv_reorder_ctrl *)data;
+
+ r8712_reordering_ctrl_timeout_handler(preorder_ctrl);
+}
+
+void r8712_init_recv_timer(struct recv_reorder_ctrl *preorder_ctrl)
+{
+ setup_timer(&preorder_ctrl->reordering_ctrl_timer,
+ _r8712_reordering_ctrl_timeout_handler,
+ (unsigned long)preorder_ctrl);
+}
diff --git a/drivers/staging/rtl8712/recv_osdep.h b/drivers/staging/rtl8712/recv_osdep.h
new file mode 100644
index 000000000..1f4986e94
--- /dev/null
+++ b/drivers/staging/rtl8712/recv_osdep.h
@@ -0,0 +1,51 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2010 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ * Modifications for inclusion into the Linux staging tree are
+ * Copyright(c) 2010 Larry Finger. All rights reserved.
+ *
+ * Contact information:
+ * WLAN FAE <wlanfae@realtek.com>
+ * Larry Finger <Larry.Finger@lwfinger.net>
+ *
+ ******************************************************************************/
+#ifndef __RECV_OSDEP_H_
+#define __RECV_OSDEP_H_
+
+#include "osdep_service.h"
+#include "drv_types.h"
+#include <linux/skbuff.h>
+
+sint _r8712_init_recv_priv(struct recv_priv *precvpriv,
+ struct _adapter *padapter);
+void _r8712_free_recv_priv(struct recv_priv *precvpriv);
+s32 r8712_recv_entry(union recv_frame *precv_frame);
+void r8712_recv_indicatepkt(struct _adapter *adapter,
+ union recv_frame *precv_frame);
+void r8712_handle_tkip_mic_err(struct _adapter *padapter, u8 bgroup);
+int r8712_init_recv_priv(struct recv_priv *precvpriv,
+ struct _adapter *padapter);
+void r8712_free_recv_priv(struct recv_priv *precvpriv);
+int r8712_os_recv_resource_alloc(struct _adapter *padapter,
+ union recv_frame *precvframe);
+int r8712_os_recvbuf_resource_alloc(struct _adapter *padapter,
+ struct recv_buf *precvbuf);
+int r8712_os_recvbuf_resource_free(struct _adapter *padapter,
+ struct recv_buf *precvbuf);
+void r8712_init_recv_timer(struct recv_reorder_ctrl *preorder_ctrl);
+
+#endif
diff --git a/drivers/staging/rtl8712/rtl8712_bitdef.h b/drivers/staging/rtl8712/rtl8712_bitdef.h
new file mode 100644
index 000000000..bff57a8ee
--- /dev/null
+++ b/drivers/staging/rtl8712/rtl8712_bitdef.h
@@ -0,0 +1,40 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2010 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ *
+ ******************************************************************************/
+
+
+#ifndef __RTL8712_BITDEF_H__
+#define __RTL8712_BITDEF_H__
+
+#include "rtl8712_cmdctrl_bitdef.h"
+#include "rtl8712_syscfg_bitdef.h"
+#include "rtl8712_macsetting_bitdef.h"
+#include "rtl8712_timectrl_bitdef.h"
+#include "rtl8712_fifoctrl_bitdef.h"
+#include "rtl8712_ratectrl_bitdef.h"
+#include "rtl8712_edcasetting_bitdef.h"
+#include "rtl8712_wmac_bitdef.h"
+#include "rtl8712_security_bitdef.h"
+#include "rtl8712_powersave_bitdef.h"
+#include "rtl8712_gp_bitdef.h"
+#include "rtl8712_interrupt_bitdef.h"
+#include "rtl8712_debugctrl_bitdef.h"
+
+#endif /* __RTL8712_BITDEF_H__ */
+
diff --git a/drivers/staging/rtl8712/rtl8712_cmd.c b/drivers/staging/rtl8712/rtl8712_cmd.c
new file mode 100644
index 000000000..007f0a3ab
--- /dev/null
+++ b/drivers/staging/rtl8712/rtl8712_cmd.c
@@ -0,0 +1,469 @@
+/******************************************************************************
+ * rtl8712_cmd.c
+ *
+ * Copyright(c) 2007 - 2010 Realtek Corporation. All rights reserved.
+ * Linux device driver for RTL8192SU
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ * Modifications for inclusion into the Linux staging tree are
+ * Copyright(c) 2010 Larry Finger. All rights reserved.
+ *
+ * Contact information:
+ * WLAN FAE <wlanfae@realtek.com>.
+ * Larry Finger <Larry.Finger@lwfinger.net>
+ *
+ ******************************************************************************/
+
+#define _RTL8712_CMD_C_
+
+#include <linux/compiler.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/kref.h>
+#include <linux/netdevice.h>
+#include <linux/skbuff.h>
+#include <linux/usb.h>
+#include <linux/usb/ch9.h>
+#include <linux/circ_buf.h>
+#include <linux/uaccess.h>
+#include <asm/byteorder.h>
+#include <linux/atomic.h>
+#include <linux/semaphore.h>
+#include <linux/rtnetlink.h>
+
+#include "osdep_service.h"
+#include "drv_types.h"
+#include "recv_osdep.h"
+#include "mlme_osdep.h"
+#include "rtl871x_ioctl_set.h"
+
+static void check_hw_pbc(struct _adapter *padapter)
+{
+ u8 tmp1byte;
+
+ r8712_write8(padapter, MAC_PINMUX_CTRL, (GPIOMUX_EN | GPIOSEL_GPIO));
+ tmp1byte = r8712_read8(padapter, GPIO_IO_SEL);
+ tmp1byte &= ~(HAL_8192S_HW_GPIO_WPS_BIT);
+ r8712_write8(padapter, GPIO_IO_SEL, tmp1byte);
+ tmp1byte = r8712_read8(padapter, GPIO_CTRL);
+ if (tmp1byte == 0xff)
+ return;
+ if (tmp1byte&HAL_8192S_HW_GPIO_WPS_BIT) {
+ /* Here we only set bPbcPressed to true
+ * After trigger PBC, the variable will be set to false */
+ DBG_8712("CheckPbcGPIO - PBC is pressed !!!!\n");
+ /* 0 is the default value and it means the application monitors
+ * the HW PBC doesn't provide its pid to driver. */
+ if (padapter->pid == 0)
+ return;
+ kill_pid(find_vpid(padapter->pid), SIGUSR1, 1);
+ }
+}
+
+/* query rx phy status from fw.
+ * Adhoc mode: beacon.
+ * Infrastructure mode: beacon , data. */
+static void query_fw_rx_phy_status(struct _adapter *padapter)
+{
+ u32 val32 = 0;
+ int pollingcnts = 50;
+
+ if (check_fwstate(&padapter->mlmepriv, _FW_LINKED) == true) {
+ r8712_write32(padapter, IOCMD_CTRL_REG, 0xf4000001);
+ msleep(100);
+ /* Wait FW complete IO Cmd */
+ while ((r8712_read32(padapter, IOCMD_CTRL_REG)) &&
+ (pollingcnts > 0)) {
+ pollingcnts--;
+ msleep(20);
+ }
+ if (pollingcnts != 0)
+ val32 = r8712_read32(padapter, IOCMD_DATA_REG);
+ else /* time out */
+ val32 = 0;
+ val32 >>= 4;
+ padapter->recvpriv.fw_rssi =
+ (u8)r8712_signal_scale_mapping(val32);
+ }
+}
+
+/* check mlme, hw, phy, or dynamic algorithm status. */
+static void StatusWatchdogCallback(struct _adapter *padapter)
+{
+ check_hw_pbc(padapter);
+ query_fw_rx_phy_status(padapter);
+}
+
+static void r871x_internal_cmd_hdl(struct _adapter *padapter, u8 *pbuf)
+{
+ struct drvint_cmd_parm *pdrvcmd;
+
+ if (!pbuf)
+ return;
+ pdrvcmd = (struct drvint_cmd_parm *)pbuf;
+ switch (pdrvcmd->i_cid) {
+ case WDG_WK_CID:
+ StatusWatchdogCallback(padapter);
+ break;
+ default:
+ break;
+ }
+ kfree(pdrvcmd->pbuf);
+}
+
+static u8 read_macreg_hdl(struct _adapter *padapter, u8 *pbuf)
+{
+ void (*pcmd_callback)(struct _adapter *dev, struct cmd_obj *pcmd);
+ struct cmd_obj *pcmd = (struct cmd_obj *)pbuf;
+
+ /* invoke cmd->callback function */
+ pcmd_callback = cmd_callback[pcmd->cmdcode].callback;
+ if (pcmd_callback == NULL)
+ r8712_free_cmd_obj(pcmd);
+ else
+ pcmd_callback(padapter, pcmd);
+ return H2C_SUCCESS;
+}
+
+static u8 write_macreg_hdl(struct _adapter *padapter, u8 *pbuf)
+{
+ void (*pcmd_callback)(struct _adapter *dev, struct cmd_obj *pcmd);
+ struct cmd_obj *pcmd = (struct cmd_obj *)pbuf;
+
+ /* invoke cmd->callback function */
+ pcmd_callback = cmd_callback[pcmd->cmdcode].callback;
+ if (pcmd_callback == NULL)
+ r8712_free_cmd_obj(pcmd);
+ else
+ pcmd_callback(padapter, pcmd);
+ return H2C_SUCCESS;
+}
+
+static u8 read_bbreg_hdl(struct _adapter *padapter, u8 *pbuf)
+{
+ u32 val;
+ void (*pcmd_callback)(struct _adapter *dev, struct cmd_obj *pcmd);
+ struct cmd_obj *pcmd = (struct cmd_obj *)pbuf;
+
+ if (pcmd->rsp && pcmd->rspsz > 0)
+ memcpy(pcmd->rsp, (u8 *)&val, pcmd->rspsz);
+ pcmd_callback = cmd_callback[pcmd->cmdcode].callback;
+ if (pcmd_callback == NULL)
+ r8712_free_cmd_obj(pcmd);
+ else
+ pcmd_callback(padapter, pcmd);
+ return H2C_SUCCESS;
+}
+
+static u8 write_bbreg_hdl(struct _adapter *padapter, u8 *pbuf)
+{
+ void (*pcmd_callback)(struct _adapter *dev, struct cmd_obj *pcmd);
+ struct cmd_obj *pcmd = (struct cmd_obj *)pbuf;
+
+ pcmd_callback = cmd_callback[pcmd->cmdcode].callback;
+ if (pcmd_callback == NULL)
+ r8712_free_cmd_obj(pcmd);
+ else
+ pcmd_callback(padapter, pcmd);
+ return H2C_SUCCESS;
+}
+
+static u8 read_rfreg_hdl(struct _adapter *padapter, u8 *pbuf)
+{
+ u32 val;
+ void (*pcmd_callback)(struct _adapter *dev, struct cmd_obj *pcmd);
+ struct cmd_obj *pcmd = (struct cmd_obj *)pbuf;
+
+ if (pcmd->rsp && pcmd->rspsz > 0)
+ memcpy(pcmd->rsp, (u8 *)&val, pcmd->rspsz);
+ pcmd_callback = cmd_callback[pcmd->cmdcode].callback;
+ if (pcmd_callback == NULL)
+ r8712_free_cmd_obj(pcmd);
+ else
+ pcmd_callback(padapter, pcmd);
+ return H2C_SUCCESS;
+}
+
+static u8 write_rfreg_hdl(struct _adapter *padapter, u8 *pbuf)
+{
+ void (*pcmd_callback)(struct _adapter *dev, struct cmd_obj *pcmd);
+ struct cmd_obj *pcmd = (struct cmd_obj *)pbuf;
+
+ pcmd_callback = cmd_callback[pcmd->cmdcode].callback;
+ if (pcmd_callback == NULL)
+ r8712_free_cmd_obj(pcmd);
+ else
+ pcmd_callback(padapter, pcmd);
+ return H2C_SUCCESS;
+}
+
+static u8 sys_suspend_hdl(struct _adapter *padapter, u8 *pbuf)
+{
+ struct cmd_obj *pcmd = (struct cmd_obj *)pbuf;
+
+ r8712_free_cmd_obj(pcmd);
+ return H2C_SUCCESS;
+}
+
+static struct cmd_obj *cmd_hdl_filter(struct _adapter *padapter,
+ struct cmd_obj *pcmd)
+{
+ struct cmd_obj *pcmd_r;
+
+ if (pcmd == NULL)
+ return pcmd;
+ pcmd_r = NULL;
+
+ switch (pcmd->cmdcode) {
+ case GEN_CMD_CODE(_Read_MACREG):
+ read_macreg_hdl(padapter, (u8 *)pcmd);
+ pcmd_r = pcmd;
+ break;
+ case GEN_CMD_CODE(_Write_MACREG):
+ write_macreg_hdl(padapter, (u8 *)pcmd);
+ pcmd_r = pcmd;
+ break;
+ case GEN_CMD_CODE(_Read_BBREG):
+ read_bbreg_hdl(padapter, (u8 *)pcmd);
+ break;
+ case GEN_CMD_CODE(_Write_BBREG):
+ write_bbreg_hdl(padapter, (u8 *)pcmd);
+ break;
+ case GEN_CMD_CODE(_Read_RFREG):
+ read_rfreg_hdl(padapter, (u8 *)pcmd);
+ break;
+ case GEN_CMD_CODE(_Write_RFREG):
+ write_rfreg_hdl(padapter, (u8 *)pcmd);
+ break;
+ case GEN_CMD_CODE(_SetUsbSuspend):
+ sys_suspend_hdl(padapter, (u8 *)pcmd);
+ break;
+ case GEN_CMD_CODE(_JoinBss):
+ r8712_joinbss_reset(padapter);
+ /* Before set JoinBss_CMD to FW, driver must ensure FW is in
+ * PS_MODE_ACTIVE. Directly write rpwm to radio on and assign
+ * new pwr_mode to Driver, instead of use workitem to change
+ * state. */
+ if (padapter->pwrctrlpriv.pwr_mode > PS_MODE_ACTIVE) {
+ padapter->pwrctrlpriv.pwr_mode = PS_MODE_ACTIVE;
+ _enter_pwrlock(&(padapter->pwrctrlpriv.lock));
+ r8712_set_rpwm(padapter, PS_STATE_S4);
+ up(&(padapter->pwrctrlpriv.lock));
+ }
+ pcmd_r = pcmd;
+ break;
+ case _DRV_INT_CMD_:
+ r871x_internal_cmd_hdl(padapter, pcmd->parmbuf);
+ r8712_free_cmd_obj(pcmd);
+ pcmd_r = NULL;
+ break;
+ default:
+ pcmd_r = pcmd;
+ break;
+ }
+ return pcmd_r; /* if returning pcmd_r == NULL, pcmd must be free. */
+}
+
+static u8 check_cmd_fifo(struct _adapter *padapter, uint sz)
+{
+ return _SUCCESS;
+}
+
+u8 r8712_fw_cmd(struct _adapter *pAdapter, u32 cmd)
+{
+ int pollingcnts = 50;
+
+ r8712_write32(pAdapter, IOCMD_CTRL_REG, cmd);
+ msleep(100);
+ while ((0 != r8712_read32(pAdapter, IOCMD_CTRL_REG)) &&
+ (pollingcnts > 0)) {
+ pollingcnts--;
+ msleep(20);
+ }
+ if (pollingcnts == 0)
+ return false;
+ return true;
+}
+
+void r8712_fw_cmd_data(struct _adapter *pAdapter, u32 *value, u8 flag)
+{
+ if (flag == 0) /* set */
+ r8712_write32(pAdapter, IOCMD_DATA_REG, *value);
+ else /* query */
+ *value = r8712_read32(pAdapter, IOCMD_DATA_REG);
+}
+
+int r8712_cmd_thread(void *context)
+{
+ struct cmd_obj *pcmd;
+ unsigned int cmdsz, wr_sz, *pcmdbuf;
+ struct tx_desc *pdesc;
+ void (*pcmd_callback)(struct _adapter *dev, struct cmd_obj *pcmd);
+ struct _adapter *padapter = (struct _adapter *)context;
+ struct cmd_priv *pcmdpriv = &(padapter->cmdpriv);
+
+ allow_signal(SIGTERM);
+ while (1) {
+ if ((_down_sema(&(pcmdpriv->cmd_queue_sema))) == _FAIL)
+ break;
+ if ((padapter->bDriverStopped == true) ||
+ (padapter->bSurpriseRemoved == true))
+ break;
+ if (r8712_register_cmd_alive(padapter) != _SUCCESS)
+ continue;
+_next:
+ pcmd = r8712_dequeue_cmd(&(pcmdpriv->cmd_queue));
+ if (!(pcmd)) {
+ r8712_unregister_cmd_alive(padapter);
+ continue;
+ }
+ pcmdbuf = (unsigned int *)pcmdpriv->cmd_buf;
+ pdesc = (struct tx_desc *)pcmdbuf;
+ memset(pdesc, 0, TXDESC_SIZE);
+ pcmd = cmd_hdl_filter(padapter, pcmd);
+ if (pcmd) { /* if pcmd != NULL, cmd will be handled by f/w */
+ struct dvobj_priv *pdvobj = (struct dvobj_priv *)
+ &padapter->dvobjpriv;
+ u8 blnPending = 0;
+
+ pcmdpriv->cmd_issued_cnt++;
+ cmdsz = round_up(pcmd->cmdsz, 8);
+ wr_sz = TXDESC_SIZE + 8 + cmdsz;
+ pdesc->txdw0 |= cpu_to_le32((wr_sz-TXDESC_SIZE) &
+ 0x0000ffff);
+ if (pdvobj->ishighspeed) {
+ if ((wr_sz % 512) == 0)
+ blnPending = 1;
+ } else {
+ if ((wr_sz % 64) == 0)
+ blnPending = 1;
+ }
+ if (blnPending) /* 32 bytes for TX Desc - 8 offset */
+ pdesc->txdw0 |= cpu_to_le32(((TXDESC_SIZE +
+ OFFSET_SZ + 8) << OFFSET_SHT) &
+ 0x00ff0000);
+ else {
+ pdesc->txdw0 |= cpu_to_le32(((TXDESC_SIZE +
+ OFFSET_SZ) <<
+ OFFSET_SHT) &
+ 0x00ff0000);
+ }
+ pdesc->txdw0 |= cpu_to_le32(OWN | FSG | LSG);
+ pdesc->txdw1 |= cpu_to_le32((0x13 << QSEL_SHT) &
+ 0x00001f00);
+ pcmdbuf += (TXDESC_SIZE >> 2);
+ *pcmdbuf = cpu_to_le32((cmdsz & 0x0000ffff) |
+ (pcmd->cmdcode << 16) |
+ (pcmdpriv->cmd_seq << 24));
+ pcmdbuf += 2; /* 8 bytes alignment */
+ memcpy((u8 *)pcmdbuf, pcmd->parmbuf, pcmd->cmdsz);
+ while (check_cmd_fifo(padapter, wr_sz) == _FAIL) {
+ if ((padapter->bDriverStopped == true) ||
+ (padapter->bSurpriseRemoved == true))
+ break;
+ msleep(100);
+ continue;
+ }
+ if (blnPending)
+ wr_sz += 8; /* Append 8 bytes */
+ r8712_write_mem(padapter, RTL8712_DMA_H2CCMD, wr_sz,
+ (u8 *)pdesc);
+ pcmdpriv->cmd_seq++;
+ if (pcmd->cmdcode == GEN_CMD_CODE(_CreateBss)) {
+ pcmd->res = H2C_SUCCESS;
+ pcmd_callback = cmd_callback[pcmd->
+ cmdcode].callback;
+ if (pcmd_callback)
+ pcmd_callback(padapter, pcmd);
+ continue;
+ }
+ if (pcmd->cmdcode == GEN_CMD_CODE(_SetPwrMode)) {
+ if (padapter->pwrctrlpriv.bSleep) {
+ _enter_pwrlock(&(padapter->
+ pwrctrlpriv.lock));
+ r8712_set_rpwm(padapter, PS_STATE_S2);
+ up(&padapter->pwrctrlpriv.lock);
+ }
+ }
+ r8712_free_cmd_obj(pcmd);
+ if (list_empty(&pcmdpriv->cmd_queue.queue)) {
+ r8712_unregister_cmd_alive(padapter);
+ continue;
+ } else
+ goto _next;
+ } else
+ goto _next;
+ flush_signals_thread();
+ }
+ /* free all cmd_obj resources */
+ do {
+ pcmd = r8712_dequeue_cmd(&(pcmdpriv->cmd_queue));
+ if (pcmd == NULL)
+ break;
+ r8712_free_cmd_obj(pcmd);
+ } while (1);
+ up(&pcmdpriv->terminate_cmdthread_sema);
+ thread_exit();
+}
+
+void r8712_event_handle(struct _adapter *padapter, uint *peventbuf)
+{
+ u8 evt_code, evt_seq;
+ u16 evt_sz;
+ void (*event_callback)(struct _adapter *dev, u8 *pbuf);
+ struct evt_priv *pevt_priv = &(padapter->evtpriv);
+
+ if (peventbuf == NULL)
+ goto _abort_event_;
+ evt_sz = (u16)(le32_to_cpu(*peventbuf) & 0xffff);
+ evt_seq = (u8)((le32_to_cpu(*peventbuf) >> 24) & 0x7f);
+ evt_code = (u8)((le32_to_cpu(*peventbuf) >> 16) & 0xff);
+ /* checking event sequence... */
+ if ((evt_seq & 0x7f) != pevt_priv->event_seq) {
+ pevt_priv->event_seq = ((evt_seq + 1) & 0x7f);
+ goto _abort_event_;
+ }
+ /* checking if event code is valid */
+ if (evt_code >= MAX_C2HEVT) {
+ pevt_priv->event_seq = ((evt_seq+1) & 0x7f);
+ goto _abort_event_;
+ } else if ((evt_code == GEN_EVT_CODE(_Survey)) &&
+ (evt_sz > sizeof(struct wlan_bssid_ex))) {
+ pevt_priv->event_seq = ((evt_seq+1)&0x7f);
+ goto _abort_event_;
+ }
+ /* checking if event size match the event parm size */
+ if ((wlanevents[evt_code].parmsize) &&
+ (wlanevents[evt_code].parmsize != evt_sz)) {
+ pevt_priv->event_seq = ((evt_seq+1)&0x7f);
+ goto _abort_event_;
+ } else if ((evt_sz == 0) && (evt_code != GEN_EVT_CODE(_WPS_PBC))) {
+ pevt_priv->event_seq = ((evt_seq+1)&0x7f);
+ goto _abort_event_;
+ }
+ pevt_priv->event_seq++; /* update evt_seq */
+ if (pevt_priv->event_seq > 127)
+ pevt_priv->event_seq = 0;
+ /* move to event content, 8 bytes alignment */
+ peventbuf = peventbuf + 2;
+ event_callback = wlanevents[evt_code].event_callback;
+ if (event_callback)
+ event_callback(padapter, (u8 *)peventbuf);
+ pevt_priv->evt_done_cnt++;
+_abort_event_:
+ return;
+}
diff --git a/drivers/staging/rtl8712/rtl8712_cmd.h b/drivers/staging/rtl8712/rtl8712_cmd.h
new file mode 100644
index 000000000..67e9e910a
--- /dev/null
+++ b/drivers/staging/rtl8712/rtl8712_cmd.h
@@ -0,0 +1,244 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2010 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ * Modifications for inclusion into the Linux staging tree are
+ * Copyright(c) 2010 Larry Finger. All rights reserved.
+ *
+ * Contact information:
+ * WLAN FAE <wlanfae@realtek.com>
+ * Larry Finger <Larry.Finger@lwfinger.net>
+ *
+ ******************************************************************************/
+#ifndef __RTL8712_CMD_H_
+#define __RTL8712_CMD_H_
+
+#define CMD_HDR_SZ 8
+
+u8 r8712_fw_cmd(struct _adapter *pAdapter, u32 cmd);
+void r8712_fw_cmd_data(struct _adapter *pAdapter, u32 *value, u8 flag);
+
+struct cmd_hdr {
+ u32 cmd_dw0;
+ u32 cmd_dw1;
+};
+
+enum rtl8712_h2c_cmd {
+ GEN_CMD_CODE(_Read_MACREG), /*0*/
+ GEN_CMD_CODE(_Write_MACREG),
+ GEN_CMD_CODE(_Read_BBREG),
+ GEN_CMD_CODE(_Write_BBREG),
+ GEN_CMD_CODE(_Read_RFREG),
+ GEN_CMD_CODE(_Write_RFREG), /*5*/
+ GEN_CMD_CODE(_Read_EEPROM),
+ GEN_CMD_CODE(_Write_EEPROM),
+ GEN_CMD_CODE(_Read_EFUSE),
+ GEN_CMD_CODE(_Write_EFUSE),
+
+ GEN_CMD_CODE(_Read_CAM), /*10*/
+ GEN_CMD_CODE(_Write_CAM),
+ GEN_CMD_CODE(_setBCNITV),
+ GEN_CMD_CODE(_setMBIDCFG),
+ GEN_CMD_CODE(_JoinBss), /*14*/
+ GEN_CMD_CODE(_DisConnect), /*15*/
+ GEN_CMD_CODE(_CreateBss),
+ GEN_CMD_CODE(_SetOpMode),
+ GEN_CMD_CODE(_SiteSurvey), /*18*/
+ GEN_CMD_CODE(_SetAuth),
+
+ GEN_CMD_CODE(_SetKey), /*20*/
+ GEN_CMD_CODE(_SetStaKey),
+ GEN_CMD_CODE(_SetAssocSta),
+ GEN_CMD_CODE(_DelAssocSta),
+ GEN_CMD_CODE(_SetStaPwrState),
+ GEN_CMD_CODE(_SetBasicRate), /*25*/
+ GEN_CMD_CODE(_GetBasicRate),
+ GEN_CMD_CODE(_SetDataRate),
+ GEN_CMD_CODE(_GetDataRate),
+ GEN_CMD_CODE(_SetPhyInfo),
+
+ GEN_CMD_CODE(_GetPhyInfo), /*30*/
+ GEN_CMD_CODE(_SetPhy),
+ GEN_CMD_CODE(_GetPhy),
+ GEN_CMD_CODE(_readRssi),
+ GEN_CMD_CODE(_readGain),
+ GEN_CMD_CODE(_SetAtim), /*35*/
+ GEN_CMD_CODE(_SetPwrMode),
+ GEN_CMD_CODE(_JoinbssRpt),
+ GEN_CMD_CODE(_SetRaTable),
+ GEN_CMD_CODE(_GetRaTable),
+
+ GEN_CMD_CODE(_GetCCXReport), /*40*/
+ GEN_CMD_CODE(_GetDTMReport),
+ GEN_CMD_CODE(_GetTXRateStatistics),
+ GEN_CMD_CODE(_SetUsbSuspend),
+ GEN_CMD_CODE(_SetH2cLbk),
+ GEN_CMD_CODE(_AddBAReq), /*45*/
+
+ GEN_CMD_CODE(_SetChannel), /*46*/
+/* MP_OFFLOAD Start (47~54)*/
+ GEN_CMD_CODE(_SetTxPower),
+ GEN_CMD_CODE(_SwitchAntenna),
+ GEN_CMD_CODE(_SetCrystalCap),
+ GEN_CMD_CODE(_SetSingleCarrierTx), /*50*/
+ GEN_CMD_CODE(_SetSingleToneTx),
+ GEN_CMD_CODE(_SetCarrierSuppressionTx),
+ GEN_CMD_CODE(_SetContinuousTx),
+ GEN_CMD_CODE(_SwitchBandwidth), /*54*/
+/* MP_OFFLOAD End*/
+ GEN_CMD_CODE(_TX_Beacon), /*55*/
+ GEN_CMD_CODE(_SetPowerTracking),
+ GEN_CMD_CODE(_AMSDU_TO_AMPDU), /*57*/
+ GEN_CMD_CODE(_SetMacAddress), /*58*/
+
+ GEN_CMD_CODE(_DisconnectCtrl), /*59*/
+ GEN_CMD_CODE(_SetChannelPlan), /*60*/
+ GEN_CMD_CODE(_DisconnectCtrlEx), /*61*/
+
+ /* To do, modify these h2c cmd, add or delete */
+ GEN_CMD_CODE(_GetH2cLbk),
+
+ /* WPS extra IE */
+ GEN_CMD_CODE(_SetProbeReqExtraIE),
+ GEN_CMD_CODE(_SetAssocReqExtraIE),
+ GEN_CMD_CODE(_SetProbeRspExtraIE),
+ GEN_CMD_CODE(_SetAssocRspExtraIE),
+
+ /* the following is driver will do */
+ GEN_CMD_CODE(_GetCurDataRate),
+
+ GEN_CMD_CODE(_GetTxRetrycnt), /* to record times that Tx retry to
+ * transmit packet after association
+ */
+ GEN_CMD_CODE(_GetRxRetrycnt), /* to record total number of the
+ * received frame with ReTry bit set in
+ * the WLAN header
+ */
+
+ GEN_CMD_CODE(_GetBCNOKcnt),
+ GEN_CMD_CODE(_GetBCNERRcnt),
+ GEN_CMD_CODE(_GetCurTxPwrLevel),
+
+ GEN_CMD_CODE(_SetDIG),
+ GEN_CMD_CODE(_SetRA),
+ GEN_CMD_CODE(_SetPT),
+ GEN_CMD_CODE(_ReadTSSI),
+
+ MAX_H2CCMD
+};
+
+
+#define _GetBBReg_CMD_ _Read_BBREG_CMD_
+#define _SetBBReg_CMD_ _Write_BBREG_CMD_
+#define _GetRFReg_CMD_ _Read_RFREG_CMD_
+#define _SetRFReg_CMD_ _Write_RFREG_CMD_
+#define _DRV_INT_CMD_ (MAX_H2CCMD+1)
+#define _SetRFIntFs_CMD_ (MAX_H2CCMD+2)
+
+#ifdef _RTL8712_CMD_C_
+static struct _cmd_callback cmd_callback[] = {
+ {GEN_CMD_CODE(_Read_MACREG), NULL}, /*0*/
+ {GEN_CMD_CODE(_Write_MACREG), NULL},
+ {GEN_CMD_CODE(_Read_BBREG), &r8712_getbbrfreg_cmdrsp_callback},
+ {GEN_CMD_CODE(_Write_BBREG), NULL},
+ {GEN_CMD_CODE(_Read_RFREG), &r8712_getbbrfreg_cmdrsp_callback},
+ {GEN_CMD_CODE(_Write_RFREG), NULL}, /*5*/
+ {GEN_CMD_CODE(_Read_EEPROM), NULL},
+ {GEN_CMD_CODE(_Write_EEPROM), NULL},
+ {GEN_CMD_CODE(_Read_EFUSE), NULL},
+ {GEN_CMD_CODE(_Write_EFUSE), NULL},
+
+ {GEN_CMD_CODE(_Read_CAM), NULL}, /*10*/
+ {GEN_CMD_CODE(_Write_CAM), NULL},
+ {GEN_CMD_CODE(_setBCNITV), NULL},
+ {GEN_CMD_CODE(_setMBIDCFG), NULL},
+ {GEN_CMD_CODE(_JoinBss), &r8712_joinbss_cmd_callback}, /*14*/
+ {GEN_CMD_CODE(_DisConnect), &r8712_disassoc_cmd_callback}, /*15*/
+ {GEN_CMD_CODE(_CreateBss), &r8712_createbss_cmd_callback},
+ {GEN_CMD_CODE(_SetOpMode), NULL},
+ {GEN_CMD_CODE(_SiteSurvey), &r8712_survey_cmd_callback}, /*18*/
+ {GEN_CMD_CODE(_SetAuth), NULL},
+
+ {GEN_CMD_CODE(_SetKey), NULL}, /*20*/
+ {GEN_CMD_CODE(_SetStaKey), &r8712_setstaKey_cmdrsp_callback},
+ {GEN_CMD_CODE(_SetAssocSta), &r8712_setassocsta_cmdrsp_callback},
+ {GEN_CMD_CODE(_DelAssocSta), NULL},
+ {GEN_CMD_CODE(_SetStaPwrState), NULL},
+ {GEN_CMD_CODE(_SetBasicRate), NULL}, /*25*/
+ {GEN_CMD_CODE(_GetBasicRate), NULL},
+ {GEN_CMD_CODE(_SetDataRate), NULL},
+ {GEN_CMD_CODE(_GetDataRate), NULL},
+ {GEN_CMD_CODE(_SetPhyInfo), NULL},
+
+ {GEN_CMD_CODE(_GetPhyInfo), NULL}, /*30*/
+ {GEN_CMD_CODE(_SetPhy), NULL},
+ {GEN_CMD_CODE(_GetPhy), NULL},
+ {GEN_CMD_CODE(_readRssi), NULL},
+ {GEN_CMD_CODE(_readGain), NULL},
+ {GEN_CMD_CODE(_SetAtim), NULL}, /*35*/
+ {GEN_CMD_CODE(_SetPwrMode), NULL},
+ {GEN_CMD_CODE(_JoinbssRpt), NULL},
+ {GEN_CMD_CODE(_SetRaTable), NULL},
+ {GEN_CMD_CODE(_GetRaTable), NULL},
+
+ {GEN_CMD_CODE(_GetCCXReport), NULL}, /*40*/
+ {GEN_CMD_CODE(_GetDTMReport), NULL},
+ {GEN_CMD_CODE(_GetTXRateStatistics), NULL},
+ {GEN_CMD_CODE(_SetUsbSuspend), NULL},
+ {GEN_CMD_CODE(_SetH2cLbk), NULL},
+ {GEN_CMD_CODE(_AddBAReq), NULL}, /*45*/
+
+ {GEN_CMD_CODE(_SetChannel), NULL}, /*46*/
+/* MP_OFFLOAD Start (47~54)*/
+ {GEN_CMD_CODE(_SetTxPower), NULL},
+ {GEN_CMD_CODE(_SwitchAntenna), NULL},
+ {GEN_CMD_CODE(_SetCrystalCap), NULL},
+ {GEN_CMD_CODE(_SetSingleCarrierTx), NULL}, /*50*/
+ {GEN_CMD_CODE(_SetSingleToneTx), NULL},
+ {GEN_CMD_CODE(_SetCarrierSuppressionTx), NULL},
+ {GEN_CMD_CODE(_SetContinuousTx), NULL},
+ {GEN_CMD_CODE(_SwitchBandwidth), NULL}, /*54*/
+/* MP_OFFLOAD End*/
+ {GEN_CMD_CODE(_TX_Beacon), NULL}, /*55*/
+ {GEN_CMD_CODE(_SetPowerTracking), NULL},
+ {GEN_CMD_CODE(_AMSDU_TO_AMPDU), NULL}, /*57*/
+ {GEN_CMD_CODE(_SetMacAddress), NULL}, /*58*/
+
+ {GEN_CMD_CODE(_DisconnectCtrl), NULL}, /*59*/
+ {GEN_CMD_CODE(_SetChannelPlan), NULL}, /*60*/
+ {GEN_CMD_CODE(_DisconnectCtrlEx), NULL}, /*61*/
+
+ /* To do, modify these h2c cmd, add or delete */
+ {GEN_CMD_CODE(_GetH2cLbk), NULL},
+
+ {_SetProbeReqExtraIE_CMD_, NULL},
+ {_SetAssocReqExtraIE_CMD_, NULL},
+ {_SetProbeRspExtraIE_CMD_, NULL},
+ {_SetAssocRspExtraIE_CMD_, NULL},
+ {_GetCurDataRate_CMD_, NULL},
+ {_GetTxRetrycnt_CMD_, NULL},
+ {_GetRxRetrycnt_CMD_, NULL},
+ {_GetBCNOKcnt_CMD_, NULL},
+ {_GetBCNERRcnt_CMD_, NULL},
+ {_GetCurTxPwrLevel_CMD_, NULL},
+ {_SetDIG_CMD_, NULL},
+ {_SetRA_CMD_, NULL},
+ {_SetPT_CMD_, NULL},
+ {GEN_CMD_CODE(_ReadTSSI), &r8712_readtssi_cmdrsp_callback}
+};
+#endif
+
+#endif
diff --git a/drivers/staging/rtl8712/rtl8712_cmdctrl_bitdef.h b/drivers/staging/rtl8712/rtl8712_cmdctrl_bitdef.h
new file mode 100644
index 000000000..8dffe101b
--- /dev/null
+++ b/drivers/staging/rtl8712/rtl8712_cmdctrl_bitdef.h
@@ -0,0 +1,108 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2010 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ *
+ ******************************************************************************/
+#ifndef __RTL8712_CMDCTRL_BITDEF_H__
+#define __RTL8712_CMDCTRL_BITDEF_H__
+
+/*
+ * 2. Command Control Registers (Offset: 0x0040 - 0x004F)*/
+/*--------------------------------------------------------------------------*/
+/* 8192S (CMD) command register bits (Offset 0x40, 16 bits)*/
+/*--------------------------------------------------------------------------*/
+#define _APSDOFF_STATUS BIT(15)
+#define _APSDOFF BIT(14)
+#define _BBRSTn BIT(13) /*Enable OFDM/CCK*/
+#define _BB_GLB_RSTn BIT(12) /*Enable BB*/
+#define _SCHEDULE_EN BIT(10) /*Enable MAC scheduler*/
+#define _MACRXEN BIT(9)
+#define _MACTXEN BIT(8)
+#define _DDMA_EN BIT(7) /*FW off load function enable*/
+#define _FW2HW_EN BIT(6) /*MAC every module reset */
+#define _RXDMA_EN BIT(5)
+#define _TXDMA_EN BIT(4)
+#define _HCI_RXDMA_EN BIT(3)
+#define _HCI_TXDMA_EN BIT(2)
+
+/*TXPAUSE*/
+#define _STOPHCCA BIT(6)
+#define _STOPHIGH BIT(5)
+#define _STOPMGT BIT(4)
+#define _STOPVO BIT(3)
+#define _STOPVI BIT(2)
+#define _STOPBE BIT(1)
+#define _STOPBK BIT(0)
+
+/*TCR*/
+#define _DISCW BIT(20)
+#define _ICV BIT(19)
+#define _CFEND_FMT BIT(17)
+#define _CRC BIT(16)
+#define _FWRDY BIT(7)
+#define _BASECHG BIT(6)
+#define _IMEM_RDY BIT(5)
+#define _DMEM_CODE_DONE BIT(4)
+#define _EMEM_CHK_RPT BIT(3)
+#define _EMEM_CODE_DONE BIT(2)
+#define _IMEM_CHK_RPT BIT(1)
+#define _IMEM_CODE_DONE BIT(0)
+
+#define _TXDMA_INIT_VALUE (_IMEM_CHK_RPT|_EMEM_CHK_RPT)
+
+/*RCR*/
+#define _ENMBID BIT(27)
+#define _APP_PHYST_RXFF BIT(25)
+#define _APP_PHYST_STAFF BIT(24)
+#define _CBSSID BIT(23)
+#define _APWRMGT BIT(22)
+#define _ADD3 BIT(21)
+#define _AMF BIT(20)
+#define _ACF BIT(19)
+#define _ADF BIT(18)
+#define _APP_MIC BIT(17)
+#define _APP_ICV BIT(16)
+#define _RXFTH_MSK 0x0000E000
+#define _RXFTH_SHT 13
+#define _AICV BIT(12)
+#define _RXPKTLMT_MSK 0x00000FC0
+#define _RXPKTLMT_SHT 6
+#define _ACRC32 BIT(5)
+#define _AB BIT(3)
+#define _AM BIT(2)
+#define _APM BIT(1)
+#define _AAP BIT(0)
+
+/*MSR*/
+#define _NETTYPE_MSK 0x03
+#define _NETTYPE_SHT 0
+
+/*BT*/
+#define _BTMODE_MSK 0x06
+#define _BTMODE_SHT 1
+#define _ENBT BIT(0)
+
+/*MBIDCTRL*/
+#define _ENMBID_MODE BIT(15)
+#define _BCNNO_MSK 0x7000
+#define _BCNNO_SHT 12
+#define _BCNSPACE_MSK 0x0FFF
+#define _BCNSPACE_SHT 0
+
+
+#endif /* __RTL8712_CMDCTRL_BITDEF_H__*/
+
diff --git a/drivers/staging/rtl8712/rtl8712_cmdctrl_regdef.h b/drivers/staging/rtl8712/rtl8712_cmdctrl_regdef.h
new file mode 100644
index 000000000..9374f1c48
--- /dev/null
+++ b/drivers/staging/rtl8712/rtl8712_cmdctrl_regdef.h
@@ -0,0 +1,34 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2010 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ *
+ ******************************************************************************/
+#ifndef __RTL8712_CMDCTRL_REGDEF_H__
+#define __RTL8712_CMDCTRL_REGDEF_H__
+
+
+#define CR (RTL8712_CMDCTRL_ + 0x0000)
+#define TXPAUSE (RTL8712_CMDCTRL_ + 0x0002)
+#define TCR (RTL8712_CMDCTRL_ + 0x0004)
+#define RCR (RTL8712_CMDCTRL_ + 0x0008)
+#define MSR (RTL8712_CMDCTRL_ + 0x000C)
+#define SYSF_CFG (RTL8712_CMDCTRL_ + 0x000D)
+#define MBIDCTRL (RTL8712_CMDCTRL_ + 0x000E)
+
+
+#endif /* __RTL8712_CMDCTRL_REGDEF_H__ */
+
diff --git a/drivers/staging/rtl8712/rtl8712_debugctrl_bitdef.h b/drivers/staging/rtl8712/rtl8712_debugctrl_bitdef.h
new file mode 100644
index 000000000..8bd483795
--- /dev/null
+++ b/drivers/staging/rtl8712/rtl8712_debugctrl_bitdef.h
@@ -0,0 +1,55 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2010 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ *
+ ******************************************************************************/
+#ifndef __RTL8712_DEBUGCTRL_BITDEF_H__
+#define __RTL8712_DEBUGCTRL_BITDEF_H__
+
+/*BIST*/
+#define _BIST_RST BIT(0)
+
+/*LMS*/
+#define _LMS_MSK 0x03
+
+/*WDG_CTRL*/
+#define _OVSEL_MSK 0x0600
+#define _OVSEL_SHT 9
+#define _WDGCLR BIT(8)
+#define _WDGEN_MSK 0x00FF
+#define _WDGEN_SHT 0
+
+/*INTM*/
+#define _TXTIMER_MSK 0xF000
+#define _TXTIMER_SHT 12
+#define _TXNUM_MSK 0x0F00
+#define _TXNUM_SHT 8
+#define _RXTIMER_MSK 0x00F0
+#define _RXTIMER_SHT 4
+#define _RXNUM_MSK 0x000F
+#define _RXNUM_SHT 0
+
+/*FDLOCKTURN0*/
+/*FDLOCKTURN1*/
+#define _TURN1 BIT(0)
+
+/*FDLOCKFLAG0*/
+/*FDLOCKFLAG1*/
+#define _LOCKFLAG1_MSK 0x03
+
+
+#endif /* __RTL8712_DEBUGCTRL_BITDEF_H__ */
diff --git a/drivers/staging/rtl8712/rtl8712_debugctrl_regdef.h b/drivers/staging/rtl8712/rtl8712_debugctrl_regdef.h
new file mode 100644
index 000000000..43630bb06
--- /dev/null
+++ b/drivers/staging/rtl8712/rtl8712_debugctrl_regdef.h
@@ -0,0 +1,47 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2010 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ *
+ ******************************************************************************/
+#ifndef __RTL8712_DEBUGCTRL_REGDEF_H__
+#define __RTL8712_DEBUGCTRL_REGDEF_H__
+
+#define BIST (RTL8712_DEBUGCTRL_ + 0x00)
+#define DBS (RTL8712_DEBUGCTRL_ + 0x04)
+#define LMS (RTL8712_DEBUGCTRL_ + 0x05)
+#define CPUINST (RTL8712_DEBUGCTRL_ + 0x08)
+#define CPUCAUSE (RTL8712_DEBUGCTRL_ + 0x0C)
+#define LBUS_ERR_ADDR (RTL8712_DEBUGCTRL_ + 0x10)
+#define LBUS_ERR_CMD (RTL8712_DEBUGCTRL_ + 0x14)
+#define LBUS_ERR_DATA_L (RTL8712_DEBUGCTRL_ + 0x18)
+#define LBUS_ERR_DATA_H (RTL8712_DEBUGCTRL_ + 0x1C)
+#define LBUS_EXCEPTION_ADDR (RTL8712_DEBUGCTRL_ + 0x20)
+#define WDG_CTRL (RTL8712_DEBUGCTRL_ + 0x24)
+#define INTMTU (RTL8712_DEBUGCTRL_ + 0x28)
+#define INTM (RTL8712_DEBUGCTRL_ + 0x2A)
+#define FDLOCKTURN0 (RTL8712_DEBUGCTRL_ + 0x2C)
+#define FDLOCKTURN1 (RTL8712_DEBUGCTRL_ + 0x2D)
+#define FDLOCKFLAG0 (RTL8712_DEBUGCTRL_ + 0x2E)
+#define FDLOCKFLAG1 (RTL8712_DEBUGCTRL_ + 0x2F)
+#define TRXPKTBUF_DBG_DATA (RTL8712_DEBUGCTRL_ + 0x30)
+#define TRXPKTBUF_DBG_CTRL (RTL8712_DEBUGCTRL_ + 0x38)
+#define DPLL_MON (RTL8712_DEBUGCTRL_ + 0x3A)
+
+
+
+#endif /* __RTL8712_DEBUGCTRL_REGDEF_H__ */
+
diff --git a/drivers/staging/rtl8712/rtl8712_edcasetting_bitdef.h b/drivers/staging/rtl8712/rtl8712_edcasetting_bitdef.h
new file mode 100644
index 000000000..32dab81f1
--- /dev/null
+++ b/drivers/staging/rtl8712/rtl8712_edcasetting_bitdef.h
@@ -0,0 +1,77 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2010 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ * Modifications for inclusion into the Linux staging tree are
+ * Copyright(c) 2010 Larry Finger. All rights reserved.
+ *
+ * Contact information:
+ * WLAN FAE <wlanfae@realtek.com>
+ * Larry Finger <Larry.Finger@lwfinger.net>
+ *
+ ******************************************************************************/
+#ifndef __RTL8712_EDCASETTING_BITDEF_H__
+#define __RTL8712_EDCASETTING_BITDEF_H__
+
+/*EDCAPARAM*/
+#define _TXOPLIMIT_MSK 0xFFFF0000
+#define _TXOPLIMIT_SHT 16
+#define _ECWIN_MSK 0x0000FF00
+#define _ECWIN_SHT 8
+#define _AIFS_MSK 0x000000FF
+#define _AIFS_SHT 0
+
+/*BCNTCFG*/
+#define _BCNECW_MSK 0xFF00
+#define _BCNECW_SHT 8
+#define _BCNIFS_MSK 0x00FF
+#define _BCNIFS_SHT 0
+
+/*CWRR*/
+#define _CWRR_MSK 0x03FF
+
+/*ACMAVG*/
+#define _AVG_TIME_UP BIT(3)
+#define _AVGPERIOD_MSK 0x03
+
+/*ACMHWCTRL*/
+#define _VOQ_ACM_STATUS BIT(6)
+#define _VIQ_ACM_STATUS BIT(5)
+#define _BEQ_ACM_STATUS BIT(4)
+#define _VOQ_ACM_EN BIT(3)
+#define _VIQ_ACM_EN BIT(2)
+#define _BEQ_ACM_EN BIT(1)
+#define _ACMHWEN BIT(0)
+
+/*VO_ADMTIME*/
+#define _VO_ACM_RUT BIT(18)
+#define _VO_ADMTIME_MSK 0x0003FFF
+
+/*VI_ADMTIME*/
+#define _VI_ACM_RUT BIT(18)
+#define _VI_ADMTIME_MSK 0x0003FFF
+
+/*BE_ADMTIME*/
+#define _BE_ACM_RUT BIT(18)
+#define _BE_ADMTIME_MSK 0x0003FFF
+
+/*Retry limit reg*/
+#define _SRL_MSK 0xFF00
+#define _SRL_SHT 8
+#define _LRL_MSK 0x00FF
+#define _LRL_SHT 0
+
+#endif /* __RTL8712_EDCASETTING_BITDEF_H__*/
diff --git a/drivers/staging/rtl8712/rtl8712_edcasetting_regdef.h b/drivers/staging/rtl8712/rtl8712_edcasetting_regdef.h
new file mode 100644
index 000000000..d992cb8b1
--- /dev/null
+++ b/drivers/staging/rtl8712/rtl8712_edcasetting_regdef.h
@@ -0,0 +1,37 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2010 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ *
+ ******************************************************************************/
+#ifndef __RTL8712_EDCASETTING_REGDEF_H__
+#define __RTL8712_EDCASETTING_REGDEF_H__
+
+#define EDCA_VO_PARAM (RTL8712_EDCASETTING_ + 0x00)
+#define EDCA_VI_PARAM (RTL8712_EDCASETTING_ + 0x04)
+#define EDCA_BE_PARAM (RTL8712_EDCASETTING_ + 0x08)
+#define EDCA_BK_PARAM (RTL8712_EDCASETTING_ + 0x0C)
+#define BCNTCFG (RTL8712_EDCASETTING_ + 0x10)
+#define CWRR (RTL8712_EDCASETTING_ + 0x12)
+#define ACMAVG (RTL8712_EDCASETTING_ + 0x16)
+#define ACMHWCTRL (RTL8712_EDCASETTING_ + 0x17)
+#define VO_ADMTIME (RTL8712_EDCASETTING_ + 0x18)
+#define VI_ADMTIME (RTL8712_EDCASETTING_ + 0x1C)
+#define BE_ADMTIME (RTL8712_EDCASETTING_ + 0x20)
+#define RL (RTL8712_EDCASETTING_ + 0x24)
+
+#endif /* __RTL8712_EDCASETTING_REGDEF_H__ */
+
diff --git a/drivers/staging/rtl8712/rtl8712_efuse.c b/drivers/staging/rtl8712/rtl8712_efuse.c
new file mode 100644
index 000000000..d95716999
--- /dev/null
+++ b/drivers/staging/rtl8712/rtl8712_efuse.c
@@ -0,0 +1,574 @@
+/*
+ * rtl8712_efuse.c
+ *
+ * Copyright(c) 2007 - 2010 Realtek Corporation. All rights reserved.
+ * Linux device driver for RTL8192SU
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ * Modifications for inclusion into the Linux staging tree are
+ * Copyright(c) 2010 Larry Finger. All rights reserved.
+ *
+ * Contact information:
+ * WLAN FAE <wlanfae@realtek.com>.
+ * Larry Finger <Larry.Finger@lwfinger.net>
+ *
+ ******************************************************************************/
+
+#define _RTL8712_EFUSE_C_
+
+#include "osdep_service.h"
+#include "drv_types.h"
+#include "rtl8712_efuse.h"
+
+/* reserve 3 bytes for HW stop read */
+static int efuse_available_max_size = EFUSE_MAX_SIZE - 3 /*0x1FD*/;
+
+static void efuse_reg_ctrl(struct _adapter *padapter, u8 bPowerOn)
+{
+ u8 tmpu8 = 0;
+
+ if (true == bPowerOn) {
+ /* -----------------e-fuse pwr & clk reg ctrl ---------------
+ * Enable LDOE25 Macro Block
+ */
+ tmpu8 = r8712_read8(padapter, EFUSE_TEST + 3);
+ tmpu8 |= 0x80;
+ r8712_write8(padapter, EFUSE_TEST + 3, tmpu8);
+ msleep(20); /* for some platform , need some delay time */
+ /* Change Efuse Clock for write action to 40MHZ */
+ r8712_write8(padapter, EFUSE_CLK_CTRL, 0x03);
+ msleep(20); /* for some platform , need some delay time */
+ } else {
+ /* -----------------e-fuse pwr & clk reg ctrl -----------------
+ * Disable LDOE25 Macro Block
+ */
+ tmpu8 = r8712_read8(padapter, EFUSE_TEST + 3);
+ tmpu8 &= 0x7F;
+ r8712_write8(padapter, EFUSE_TEST + 3, tmpu8);
+ /* Change Efuse Clock for write action to 500K */
+ r8712_write8(padapter, EFUSE_CLK_CTRL, 0x02);
+ }
+}
+
+/*
+ * Before write E-Fuse, this function must be called.
+ */
+u8 r8712_efuse_reg_init(struct _adapter *padapter)
+{
+ return true;
+}
+
+void r8712_efuse_reg_uninit(struct _adapter *padapter)
+{
+ efuse_reg_ctrl(padapter, false);
+}
+
+static u8 efuse_one_byte_read(struct _adapter *padapter, u16 addr, u8 *data)
+{
+ u8 tmpidx = 0, bResult;
+
+ /* -----------------e-fuse reg ctrl --------------------------------- */
+ r8712_write8(padapter, EFUSE_CTRL+1, (u8)(addr&0xFF)); /* address */
+ r8712_write8(padapter, EFUSE_CTRL+2, ((u8)((addr>>8)&0x03)) |
+ (r8712_read8(padapter, EFUSE_CTRL+2)&0xFC));
+ r8712_write8(padapter, EFUSE_CTRL+3, 0x72); /* read cmd */
+ /* wait for complete */
+ while (!(0x80 & r8712_read8(padapter, EFUSE_CTRL+3)) && (tmpidx < 100))
+ tmpidx++;
+ if (tmpidx < 100) {
+ *data = r8712_read8(padapter, EFUSE_CTRL);
+ bResult = true;
+ } else {
+ *data = 0xff;
+ bResult = false;
+ }
+ return bResult;
+}
+
+static u8 efuse_one_byte_write(struct _adapter *padapter, u16 addr, u8 data)
+{
+ u8 tmpidx = 0, bResult;
+
+ /* -----------------e-fuse reg ctrl -------------------------------- */
+ r8712_write8(padapter, EFUSE_CTRL+1, (u8)(addr&0xFF)); /* address */
+ r8712_write8(padapter, EFUSE_CTRL+2, ((u8)((addr>>8)&0x03)) |
+ (r8712_read8(padapter, EFUSE_CTRL+2)&0xFC));
+ r8712_write8(padapter, EFUSE_CTRL, data); /* data */
+ r8712_write8(padapter, EFUSE_CTRL+3, 0xF2); /* write cmd */
+ /* wait for complete */
+ while ((0x80 & r8712_read8(padapter, EFUSE_CTRL+3)) && (tmpidx < 100))
+ tmpidx++;
+ if (tmpidx < 100)
+ bResult = true;
+ else
+ bResult = false;
+ return bResult;
+}
+
+static u8 efuse_one_byte_rw(struct _adapter *padapter, u8 bRead, u16 addr,
+ u8 *data)
+{
+ u8 tmpidx = 0, tmpv8 = 0, bResult;
+
+ /* -----------------e-fuse reg ctrl --------------------------------- */
+ r8712_write8(padapter, EFUSE_CTRL+1, (u8)(addr&0xFF)); /* address */
+ tmpv8 = ((u8)((addr >> 8) & 0x03)) |
+ (r8712_read8(padapter, EFUSE_CTRL + 2) & 0xFC);
+ r8712_write8(padapter, EFUSE_CTRL+2, tmpv8);
+ if (true == bRead) {
+ r8712_write8(padapter, EFUSE_CTRL+3, 0x72); /* read cmd */
+ while (!(0x80 & r8712_read8(padapter, EFUSE_CTRL+3)) &&
+ (tmpidx < 100))
+ tmpidx++;
+ if (tmpidx < 100) {
+ *data = r8712_read8(padapter, EFUSE_CTRL);
+ bResult = true;
+ } else {
+ *data = 0;
+ bResult = false;
+ }
+ } else {
+ r8712_write8(padapter, EFUSE_CTRL, *data); /* data */
+ r8712_write8(padapter, EFUSE_CTRL+3, 0xF2); /* write cmd */
+ while ((0x80 & r8712_read8(padapter, EFUSE_CTRL+3)) &&
+ (tmpidx < 100))
+ tmpidx++;
+ if (tmpidx < 100)
+ bResult = true;
+ else
+ bResult = false;
+ }
+ return bResult;
+}
+
+static u8 efuse_is_empty(struct _adapter *padapter, u8 *empty)
+{
+ u8 value, ret = true;
+
+ /* read one byte to check if E-Fuse is empty */
+ if (efuse_one_byte_rw(padapter, true, 0, &value) == true) {
+ if (0xFF == value)
+ *empty = true;
+ else
+ *empty = false;
+ } else
+ ret = false;
+ return ret;
+}
+
+void r8712_efuse_change_max_size(struct _adapter *padapter)
+{
+ u16 pre_pg_data_saddr = 0x1FB;
+ u16 i;
+ u16 pre_pg_data_size = 5;
+ u8 pre_pg_data[5];
+
+ for (i = 0; i < pre_pg_data_size; i++)
+ efuse_one_byte_read(padapter, pre_pg_data_saddr + i,
+ &pre_pg_data[i]);
+ if ((pre_pg_data[0] == 0x03) && (pre_pg_data[1] == 0x00) &&
+ (pre_pg_data[2] == 0x00) && (pre_pg_data[3] == 0x00) &&
+ (pre_pg_data[4] == 0x0C))
+ efuse_available_max_size -= pre_pg_data_size;
+}
+
+int r8712_efuse_get_max_size(struct _adapter *padapter)
+{
+ return efuse_available_max_size;
+}
+
+static u8 calculate_word_cnts(const u8 word_en)
+{
+ u8 word_cnts = 0;
+ u8 word_idx;
+
+ for (word_idx = 0; word_idx < PGPKG_MAX_WORDS; word_idx++)
+ if (!(word_en & BIT(word_idx)))
+ word_cnts++; /* 0 : write enable */
+ return word_cnts;
+}
+
+static void pgpacket_copy_data(const u8 word_en, const u8 *sourdata,
+ u8 *targetdata)
+{
+ u8 tmpindex = 0;
+ u8 word_idx, byte_idx;
+
+ for (word_idx = 0; word_idx < PGPKG_MAX_WORDS; word_idx++) {
+ if (!(word_en&BIT(word_idx))) {
+ byte_idx = word_idx * 2;
+ targetdata[byte_idx] = sourdata[tmpindex++];
+ targetdata[byte_idx + 1] = sourdata[tmpindex++];
+ }
+ }
+}
+
+u16 r8712_efuse_get_current_size(struct _adapter *padapter)
+{
+ int bContinual = true;
+ u16 efuse_addr = 0;
+ u8 hworden = 0;
+ u8 efuse_data, word_cnts = 0;
+
+ while (bContinual && efuse_one_byte_read(padapter, efuse_addr,
+ &efuse_data) && (efuse_addr < efuse_available_max_size)) {
+ if (efuse_data != 0xFF) {
+ hworden = efuse_data & 0x0F;
+ word_cnts = calculate_word_cnts(hworden);
+ /* read next header */
+ efuse_addr = efuse_addr + (word_cnts * 2) + 1;
+ } else
+ bContinual = false;
+ }
+ return efuse_addr;
+}
+
+u8 r8712_efuse_pg_packet_read(struct _adapter *padapter, u8 offset, u8 *data)
+{
+ u8 hoffset = 0, hworden = 0, word_cnts = 0;
+ u16 efuse_addr = 0;
+ u8 efuse_data;
+ u8 tmpidx = 0;
+ u8 tmpdata[PGPKT_DATA_SIZE];
+ u8 ret = true;
+
+ if (data == NULL)
+ return false;
+ if (offset > 0x0f)
+ return false;
+ memset(data, 0xFF, sizeof(u8)*PGPKT_DATA_SIZE);
+ while (efuse_addr < efuse_available_max_size) {
+ if (efuse_one_byte_read(padapter, efuse_addr, &efuse_data) ==
+ true) {
+ if (efuse_data == 0xFF)
+ break;
+ hoffset = (efuse_data >> 4) & 0x0F;
+ hworden = efuse_data & 0x0F;
+ word_cnts = calculate_word_cnts(hworden);
+ if (hoffset == offset) {
+ memset(tmpdata, 0xFF, PGPKT_DATA_SIZE);
+ for (tmpidx = 0; tmpidx < word_cnts * 2;
+ tmpidx++) {
+ if (efuse_one_byte_read(padapter,
+ efuse_addr+1+tmpidx, &efuse_data) ==
+ true) {
+ tmpdata[tmpidx] = efuse_data;
+ } else
+ ret = false;
+ }
+ pgpacket_copy_data(hworden, tmpdata, data);
+ }
+ efuse_addr += 1 + (word_cnts*2);
+ } else {
+ ret = false;
+ break;
+ }
+ }
+ return ret;
+}
+
+static u8 fix_header(struct _adapter *padapter, u8 header, u16 header_addr)
+{
+ struct PGPKT_STRUCT pkt;
+ u8 offset, word_en, value;
+ u16 addr;
+ int i;
+ u8 ret = true;
+
+ pkt.offset = GET_EFUSE_OFFSET(header);
+ pkt.word_en = GET_EFUSE_WORD_EN(header);
+ addr = header_addr + 1 + calculate_word_cnts(pkt.word_en) * 2;
+ if (addr > efuse_available_max_size)
+ return false;
+ /* retrieve original data */
+ addr = 0;
+ while (addr < header_addr) {
+ if (efuse_one_byte_read(padapter, addr++, &value) == false) {
+ ret = false;
+ break;
+ }
+ offset = GET_EFUSE_OFFSET(value);
+ word_en = GET_EFUSE_WORD_EN(value);
+ if (pkt.offset != offset) {
+ addr += calculate_word_cnts(word_en)*2;
+ continue;
+ }
+ for (i = 0; i < PGPKG_MAX_WORDS; i++) {
+ if (BIT(i) & word_en) {
+ if (BIT(i) & pkt.word_en) {
+ if (efuse_one_byte_read(
+ padapter, addr,
+ &value) == true)
+ pkt.data[i*2] = value;
+ else
+ return false;
+ if (efuse_one_byte_read(
+ padapter,
+ addr + 1,
+ &value) == true)
+ pkt.data[i*2 + 1] =
+ value;
+ else
+ return false;
+ }
+ addr += 2;
+ }
+ }
+ }
+ if (addr != header_addr)
+ return false;
+ addr++;
+ /* fill original data */
+ for (i = 0; i < PGPKG_MAX_WORDS; i++) {
+ if (BIT(i) & pkt.word_en) {
+ efuse_one_byte_write(padapter, addr, pkt.data[i*2]);
+ efuse_one_byte_write(padapter, addr+1,
+ pkt.data[i*2 + 1]);
+ /* additional check */
+ if (efuse_one_byte_read(padapter, addr, &value)
+ == false)
+ ret = false;
+ else if (pkt.data[i*2] != value) {
+ ret = false;
+ if (0xFF == value) /* write again */
+ efuse_one_byte_write(padapter, addr,
+ pkt.data[i * 2]);
+ }
+ if (efuse_one_byte_read(padapter, addr+1, &value) ==
+ false)
+ ret = false;
+ else if (pkt.data[i*2 + 1] != value) {
+ ret = false;
+ if (0xFF == value) /* write again */
+ efuse_one_byte_write(padapter, addr+1,
+ pkt.data[i*2 + 1]);
+ }
+ }
+ addr += 2;
+ }
+ return ret;
+}
+
+u8 r8712_efuse_pg_packet_write(struct _adapter *padapter, const u8 offset,
+ const u8 word_en, const u8 *data)
+{
+ u8 pg_header = 0;
+ u16 efuse_addr = 0, curr_size = 0;
+ u8 efuse_data, target_word_cnts = 0;
+ static int repeat_times;
+ int sub_repeat;
+ u8 bResult = true;
+
+ /* check if E-Fuse Clock Enable and E-Fuse Clock is 40M */
+ efuse_data = r8712_read8(padapter, EFUSE_CLK_CTRL);
+ if (efuse_data != 0x03)
+ return false;
+ pg_header = MAKE_EFUSE_HEADER(offset, word_en);
+ target_word_cnts = calculate_word_cnts(word_en);
+ repeat_times = 0;
+ efuse_addr = 0;
+ while (efuse_addr < efuse_available_max_size) {
+ curr_size = r8712_efuse_get_current_size(padapter);
+ if ((curr_size + 1 + target_word_cnts * 2) >
+ efuse_available_max_size)
+ return false; /*target_word_cnts + pg header(1 byte)*/
+ efuse_addr = curr_size; /* current size is also the last addr*/
+ efuse_one_byte_write(padapter, efuse_addr, pg_header); /*hdr*/
+ sub_repeat = 0;
+ /* check if what we read is what we write */
+ while (efuse_one_byte_read(padapter, efuse_addr,
+ &efuse_data) == false) {
+ if (++sub_repeat > _REPEAT_THRESHOLD_) {
+ bResult = false; /* continue to blind write */
+ break; /* continue to blind write */
+ }
+ }
+ if ((sub_repeat > _REPEAT_THRESHOLD_) ||
+ (pg_header == efuse_data)) {
+ /* write header ok OR can't check header(creep) */
+ u8 i;
+
+ /* go to next address */
+ efuse_addr++;
+ for (i = 0; i < target_word_cnts*2; i++) {
+ efuse_one_byte_write(padapter,
+ efuse_addr + i,
+ *(data + i));
+ if (efuse_one_byte_read(padapter,
+ efuse_addr + i, &efuse_data) == false)
+ bResult = false;
+ else if (*(data+i) != efuse_data) /* fail */
+ bResult = false;
+ }
+ break;
+ }
+ /* write header fail */
+ bResult = false;
+ if (0xFF == efuse_data)
+ return bResult; /* nothing damaged. */
+ /* call rescue procedure */
+ if (!fix_header(padapter, efuse_data, efuse_addr))
+ return false; /* rescue fail */
+
+ if (++repeat_times > _REPEAT_THRESHOLD_) /* fail */
+ break;
+ /* otherwise, take another risk... */
+ }
+ return bResult;
+}
+
+u8 r8712_efuse_access(struct _adapter *padapter, u8 bRead, u16 start_addr,
+ u16 cnts, u8 *data)
+{
+ int i;
+ u8 res = true;
+
+ if (start_addr > EFUSE_MAX_SIZE)
+ return false;
+ if ((bRead == false) && ((start_addr + cnts) >
+ efuse_available_max_size))
+ return false;
+ if ((false == bRead) && (r8712_efuse_reg_init(padapter) == false))
+ return false;
+ /* -----------------e-fuse one byte read / write ---------------------*/
+ for (i = 0; i < cnts; i++) {
+ if ((start_addr + i) > EFUSE_MAX_SIZE) {
+ res = false;
+ break;
+ }
+ res = efuse_one_byte_rw(padapter, bRead, start_addr + i,
+ data + i);
+ if ((false == bRead) && (false == res))
+ break;
+ }
+ if (false == bRead)
+ r8712_efuse_reg_uninit(padapter);
+ return res;
+}
+
+u8 r8712_efuse_map_read(struct _adapter *padapter, u16 addr, u16 cnts, u8 *data)
+{
+ u8 offset, ret = true;
+ u8 pktdata[PGPKT_DATA_SIZE];
+ int i, idx;
+
+ if ((addr + cnts) > EFUSE_MAP_MAX_SIZE)
+ return false;
+ if ((efuse_is_empty(padapter, &offset) == true) && (offset ==
+ true)) {
+ for (i = 0; i < cnts; i++)
+ data[i] = 0xFF;
+ return ret;
+ }
+ offset = (addr >> 3) & 0xF;
+ ret = r8712_efuse_pg_packet_read(padapter, offset, pktdata);
+ i = addr & 0x7; /* pktdata index */
+ idx = 0; /* data index */
+
+ do {
+ for (; i < PGPKT_DATA_SIZE; i++) {
+ data[idx++] = pktdata[i];
+ if (idx == cnts)
+ return ret;
+ }
+ offset++;
+ if (!r8712_efuse_pg_packet_read(padapter, offset, pktdata))
+ ret = false;
+ i = 0;
+ } while (1);
+ return ret;
+}
+
+u8 r8712_efuse_map_write(struct _adapter *padapter, u16 addr, u16 cnts,
+ u8 *data)
+{
+ u8 offset, word_en, empty;
+ u8 pktdata[PGPKT_DATA_SIZE], newdata[PGPKT_DATA_SIZE];
+ int i, j, idx;
+
+ if ((addr + cnts) > EFUSE_MAP_MAX_SIZE)
+ return false;
+ /* check if E-Fuse Clock Enable and E-Fuse Clock is 40M */
+ empty = r8712_read8(padapter, EFUSE_CLK_CTRL);
+ if (empty != 0x03)
+ return false;
+ if (efuse_is_empty(padapter, &empty) == true) {
+ if (true == empty)
+ memset(pktdata, 0xFF, PGPKT_DATA_SIZE);
+ } else
+ return false;
+ offset = (addr >> 3) & 0xF;
+ if (empty == false)
+ if (!r8712_efuse_pg_packet_read(padapter, offset, pktdata))
+ return false;
+ word_en = 0xF;
+ memset(newdata, 0xFF, PGPKT_DATA_SIZE);
+ i = addr & 0x7; /* pktdata index */
+ j = 0; /* newdata index */
+ idx = 0; /* data index */
+
+ if (i & 0x1) {
+ /* odd start */
+ if (data[idx] != pktdata[i]) {
+ word_en &= ~BIT(i >> 1);
+ newdata[j++] = pktdata[i - 1];
+ newdata[j++] = data[idx];
+ }
+ i++;
+ idx++;
+ }
+ do {
+ for (; i < PGPKT_DATA_SIZE; i += 2) {
+ if ((cnts - idx) == 1) {
+ if (data[idx] != pktdata[i]) {
+ word_en &= ~BIT(i >> 1);
+ newdata[j++] = data[idx];
+ newdata[j++] = pktdata[1 + 1];
+ }
+ idx++;
+ break;
+ }
+
+ if ((data[idx] != pktdata[i]) || (data[idx+1] !=
+ pktdata[i+1])) {
+ word_en &= ~BIT(i >> 1);
+ newdata[j++] = data[idx];
+ newdata[j++] = data[idx + 1];
+ }
+ idx += 2;
+
+ if (idx == cnts)
+ break;
+ }
+
+ if (word_en != 0xF)
+ if (r8712_efuse_pg_packet_write(padapter, offset,
+ word_en, newdata) == false)
+ return false;
+ if (idx == cnts)
+ break;
+ offset++;
+ if (empty == false)
+ if (!r8712_efuse_pg_packet_read(padapter, offset,
+ pktdata))
+ return false;
+ i = 0;
+ j = 0;
+ word_en = 0xF;
+ memset(newdata, 0xFF, PGPKT_DATA_SIZE);
+ } while (1);
+
+ return true;
+}
diff --git a/drivers/staging/rtl8712/rtl8712_efuse.h b/drivers/staging/rtl8712/rtl8712_efuse.h
new file mode 100644
index 000000000..6a64f91ad
--- /dev/null
+++ b/drivers/staging/rtl8712/rtl8712_efuse.h
@@ -0,0 +1,43 @@
+#ifndef __RTL8712_EFUSE_H__
+#define __RTL8712_EFUSE_H__
+
+#include "osdep_service.h"
+
+
+#define _REPEAT_THRESHOLD_ 3
+
+#define EFUSE_MAX_SIZE 512
+#define EFUSE_MAP_MAX_SIZE 128
+
+#define PGPKG_MAX_WORDS 4
+#define PGPKT_DATA_SIZE 8 /* PGPKG_MAX_WORDS*2; BYTES sizeof(u8)*8*/
+#define MAX_PGPKT_SIZE 9 /* 1 + PGPKT_DATA_SIZE; header + 2 * 4 words (BYTES)*/
+
+#define GET_EFUSE_OFFSET(header) ((header & 0xF0) >> 4)
+#define GET_EFUSE_WORD_EN(header) (header & 0x0F)
+#define MAKE_EFUSE_HEADER(offset, word_en) (((offset & 0x0F) << 4) | \
+ (word_en & 0x0F))
+/*--------------------------------------------------------------------------*/
+struct PGPKT_STRUCT {
+ u8 offset;
+ u8 word_en;
+ u8 data[PGPKT_DATA_SIZE];
+};
+/*--------------------------------------------------------------------------*/
+u8 r8712_efuse_reg_init(struct _adapter *padapter);
+void r8712_efuse_reg_uninit(struct _adapter *padapter);
+u16 r8712_efuse_get_current_size(struct _adapter *padapter);
+int r8712_efuse_get_max_size(struct _adapter *padapter);
+void r8712_efuse_change_max_size(struct _adapter *padapter);
+u8 r8712_efuse_pg_packet_read(struct _adapter *padapter,
+ u8 offset, u8 *data);
+u8 r8712_efuse_pg_packet_write(struct _adapter *padapter,
+ const u8 offset, const u8 word_en,
+ const u8 *data);
+u8 r8712_efuse_access(struct _adapter *padapter, u8 bRead,
+ u16 start_addr, u16 cnts, u8 *data);
+u8 r8712_efuse_map_read(struct _adapter *padapter, u16 addr,
+ u16 cnts, u8 *data);
+u8 r8712_efuse_map_write(struct _adapter *padapter, u16 addr,
+ u16 cnts, u8 *data);
+#endif
diff --git a/drivers/staging/rtl8712/rtl8712_event.h b/drivers/staging/rtl8712/rtl8712_event.h
new file mode 100644
index 000000000..29a4c23a0
--- /dev/null
+++ b/drivers/staging/rtl8712/rtl8712_event.h
@@ -0,0 +1,99 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2010 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ * Modifications for inclusion into the Linux staging tree are
+ * Copyright(c) 2010 Larry Finger. All rights reserved.
+ *
+ * Contact information:
+ * WLAN FAE <wlanfae@realtek.com>
+ * Larry Finger <Larry.Finger@lwfinger.net>
+ *
+ ******************************************************************************/
+#ifndef _RTL8712_EVENT_H_
+#define _RTL8712_EVENT_H_
+
+void r8712_event_handle(struct _adapter *padapter, uint *peventbuf);
+void r8712_got_addbareq_event_callback(struct _adapter *adapter, u8 *pbuf);
+
+enum rtl8712_c2h_event {
+ GEN_EVT_CODE(_Read_MACREG) = 0, /*0*/
+ GEN_EVT_CODE(_Read_BBREG),
+ GEN_EVT_CODE(_Read_RFREG),
+ GEN_EVT_CODE(_Read_EEPROM),
+ GEN_EVT_CODE(_Read_EFUSE),
+ GEN_EVT_CODE(_Read_CAM), /*5*/
+ GEN_EVT_CODE(_Get_BasicRate),
+ GEN_EVT_CODE(_Get_DataRate),
+ GEN_EVT_CODE(_Survey), /*8*/
+ GEN_EVT_CODE(_SurveyDone), /*9*/
+
+ GEN_EVT_CODE(_JoinBss), /*10*/
+ GEN_EVT_CODE(_AddSTA),
+ GEN_EVT_CODE(_DelSTA),
+ GEN_EVT_CODE(_AtimDone),
+ GEN_EVT_CODE(_TX_Report),
+ GEN_EVT_CODE(_CCX_Report), /*15*/
+ GEN_EVT_CODE(_DTM_Report),
+ GEN_EVT_CODE(_TX_Rate_Statistics),
+ GEN_EVT_CODE(_C2HLBK),
+ GEN_EVT_CODE(_FWDBG),
+ GEN_EVT_CODE(_C2HFEEDBACK), /*20*/
+ GEN_EVT_CODE(_ADDBA),
+ GEN_EVT_CODE(_C2HBCN),
+ GEN_EVT_CODE(_ReportPwrState), /*filen: only for PCIE, USB*/
+ GEN_EVT_CODE(_WPS_PBC), /*24*/
+ GEN_EVT_CODE(_ADDBAReq_Report), /*25*/
+ MAX_C2HEVT
+};
+
+
+#ifdef _RTL8712_CMD_C_
+
+static struct fwevent wlanevents[] = {
+ {0, NULL}, /*0*/
+ {0, NULL},
+ {0, NULL},
+ {0, NULL},
+ {0, NULL},
+ {0, NULL},
+ {0, NULL},
+ {0, NULL},
+ {0, &r8712_survey_event_callback}, /*8*/
+ {sizeof(struct surveydone_event),
+ &r8712_surveydone_event_callback}, /*9*/
+
+ {0, &r8712_joinbss_event_callback}, /*10*/
+ {sizeof(struct stassoc_event), &r8712_stassoc_event_callback},
+ {sizeof(struct stadel_event), &r8712_stadel_event_callback},
+ {0, &r8712_atimdone_event_callback},
+ {0, NULL},
+ {0, NULL}, /*15*/
+ {0, NULL},
+ {0, NULL},
+ {0, NULL},
+ {0, NULL}, /*fwdbg_event_callback},*/
+ {0, NULL}, /*20*/
+ {0, NULL},
+ {0, NULL},
+ {0, &r8712_cpwm_event_callback},
+ {0, &r8712_wpspbc_event_callback},
+ {0, &r8712_got_addbareq_event_callback},
+};
+
+#endif/*_RTL8712_CMD_C_*/
+
+#endif
diff --git a/drivers/staging/rtl8712/rtl8712_fifoctrl_bitdef.h b/drivers/staging/rtl8712/rtl8712_fifoctrl_bitdef.h
new file mode 100644
index 000000000..c564dc862
--- /dev/null
+++ b/drivers/staging/rtl8712/rtl8712_fifoctrl_bitdef.h
@@ -0,0 +1,145 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2010 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ *
+ ******************************************************************************/
+#ifndef __RTL8712_FIFOCTRL_BITDEF_H__
+#define __RTL8712_FIFOCTRL_BITDEF_H__
+
+/*PBP*/
+#define _PSTX_MSK 0xF0
+#define _PSTX_SHT 4
+#define _PSRX_MSK 0x0F
+#define _PSRX_SHT 0
+
+/*TXFF_STATUS*/
+#define _TXSTATUS_OVF BIT(15)
+
+/*RXFF_STATUS*/
+#define _STATUSFF1_OVF BIT(7)
+#define _STATUSFF1_EMPTY BIT(6)
+#define _STATUSFF0_OVF BIT(5)
+#define _STATUSFF0_EMPTY BIT(4)
+#define _RXFF1_OVF BIT(3)
+#define _RXFF1_EMPTY BIT(2)
+#define _RXFF0_OVF BIT(1)
+#define _RXFF0_EMPTY BIT(0)
+
+/*TXFF_EMPTY_TH*/
+#define _BKQ_EMPTY_TH_MSK 0x0F0000
+#define _BKQ_EMPTY_TH_SHT 16
+#define _BEQ_EMPTY_TH_MSK 0x00F000
+#define _BEQ_EMPTY_TH_SHT 12
+#define _VIQ_EMPTY_TH_MSK 0x000F00
+#define _VIQ_EMPTY_TH_SHT 8
+#define _VOQ_EMPTY_TH_MSK 0x0000F0
+#define _VOQ_EMPTY_TH_SHT 4
+#define _BMCQ_EMPTY_TH_MSK 0x00000F
+#define _BMCQ_EMPTY_TH_SHT 0
+
+/*SDIO_RX_BLKSZ*/
+#define _SDIO_RX_BLKSZ_MSK 0x07
+
+/*RXDMA_CTRL*/
+#define _C2HFF_POLL BIT(4)
+#define _RXPKT_POLL BIT(0)
+
+/*RXPKT_NUM*/
+#define _RXCMD_NUM_MSK 0xFF00
+#define _RXCMD_NUM_SHT 8
+#define _RXFF0_NUM_MSK 0x00FF
+#define _RXFF0_NUM_SHT 0
+
+/*FIFOPAGE2*/
+#define _PUB_AVAL_PG_MSK 0xFFFF0000
+#define _PUB_AVAL_PG_SHT 16
+#define _BCN_AVAL_PG_MSK 0x0000FFFF
+#define _BCN_AVAL_PG_SHT 0
+
+/*RX0PKTNUM*/
+#define _RXFF0_DEC_POLL BIT(15)
+#define _RXFF0_PKT_DEC_NUM_MSK 0x3F00
+#define _RXFF0_PKT_DEC_NUM_SHT 8
+#define _RXFF0_PKTNUM_RPT_MSK 0x00FF
+#define _RXFF0_PKTNUM_RPT_SHT 0
+
+/*RX1PKTNUM*/
+#define _RXFF1_DEC_POLL BIT(15)
+#define _RXFF1_PKT_DEC_NUM_MSK 0x3F00
+#define _RXFF1_PKT_DEC_NUM_SHT 8
+#define _RXFF1_PKTNUM_RPT_MSK 0x00FF
+#define _RXFF1_PKTNUM_RPT_SHT 0
+
+/*RXFLTMAP0*/
+#define _MGTFLT13EN BIT(13)
+#define _MGTFLT12EN BIT(12)
+#define _MGTFLT11EN BIT(11)
+#define _MGTFLT10EN BIT(10)
+#define _MGTFLT9EN BIT(9)
+#define _MGTFLT8EN BIT(8)
+#define _MGTFLT5EN BIT(5)
+#define _MGTFLT4EN BIT(4)
+#define _MGTFLT3EN BIT(3)
+#define _MGTFLT2EN BIT(2)
+#define _MGTFLT1EN BIT(1)
+#define _MGTFLT0EN BIT(0)
+
+/*RXFLTMAP1*/
+#define _CTRLFLT15EN BIT(15)
+#define _CTRLFLT14EN BIT(14)
+#define _CTRLFLT13EN BIT(13)
+#define _CTRLFLT12EN BIT(12)
+#define _CTRLFLT11EN BIT(11)
+#define _CTRLFLT10EN BIT(10)
+#define _CTRLFLT9EN BIT(9)
+#define _CTRLFLT8EN BIT(8)
+#define _CTRLFLT7EN BIT(7)
+#define _CTRLFLT6EN BIT(6)
+
+/*RXFLTMAP2*/
+#define _DATAFLT15EN BIT(15)
+#define _DATAFLT14EN BIT(14)
+#define _DATAFLT13EN BIT(13)
+#define _DATAFLT12EN BIT(12)
+#define _DATAFLT11EN BIT(11)
+#define _DATAFLT10EN BIT(10)
+#define _DATAFLT9EN BIT(9)
+#define _DATAFLT8EN BIT(8)
+#define _DATAFLT7EN BIT(7)
+#define _DATAFLT6EN BIT(6)
+#define _DATAFLT5EN BIT(5)
+#define _DATAFLT4EN BIT(4)
+#define _DATAFLT3EN BIT(3)
+#define _DATAFLT2EN BIT(2)
+#define _DATAFLT1EN BIT(1)
+#define _DATAFLT0EN BIT(0)
+
+/*RXFLTMAP3*/
+#define _MESHAFLT1EN BIT(1)
+#define _MESHAFLT0EN BIT(0)
+
+/*TXPKT_NUM_CTRL*/
+#define _TXPKTNUM_DEC BIT(8)
+#define _TXPKTNUM_MSK 0x00FF
+#define _TXPKTNUM_SHT 0
+
+/*TXFF_PG_NUM*/
+#define _TXFF_PG_NUM_MSK 0x0FFF
+
+
+#endif /* __RTL8712_FIFOCTRL_BITDEF_H__ */
+
diff --git a/drivers/staging/rtl8712/rtl8712_fifoctrl_regdef.h b/drivers/staging/rtl8712/rtl8712_fifoctrl_regdef.h
new file mode 100644
index 000000000..29b89c45c
--- /dev/null
+++ b/drivers/staging/rtl8712/rtl8712_fifoctrl_regdef.h
@@ -0,0 +1,76 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2010 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ *
+ ******************************************************************************/
+#ifndef __RTL8712_FIFOCTRL_REGDEF_H__
+#define __RTL8712_FIFOCTRL_REGDEF_H__
+
+#define RQPN (RTL8712_FIFOCTRL_ + 0x00)
+#define RXFF_BNDY (RTL8712_FIFOCTRL_ + 0x0C)
+#define RXRPT_BNDY (RTL8712_FIFOCTRL_ + 0x10)
+#define TXPKTBUF_PGBNDY (RTL8712_FIFOCTRL_ + 0x14)
+#define PBP (RTL8712_FIFOCTRL_ + 0x15)
+#define RX_DRVINFO_SZ (RTL8712_FIFOCTRL_ + 0x16)
+#define TXFF_STATUS (RTL8712_FIFOCTRL_ + 0x17)
+#define RXFF_STATUS (RTL8712_FIFOCTRL_ + 0x18)
+#define TXFF_EMPTY_TH (RTL8712_FIFOCTRL_ + 0x19)
+#define SDIO_RX_BLKSZ (RTL8712_FIFOCTRL_ + 0x1C)
+#define RXDMA_RXCTRL (RTL8712_FIFOCTRL_ + 0x1D)
+#define RXPKT_NUM (RTL8712_FIFOCTRL_ + 0x1E)
+#define RXPKT_NUM_C2H (RTL8712_FIFOCTRL_ + 0x1F)
+#define C2HCMD_UDT_SIZE (RTL8712_FIFOCTRL_ + 0x20)
+#define C2HCMD_UDT_ADDR (RTL8712_FIFOCTRL_ + 0x22)
+#define FIFOPAGE2 (RTL8712_FIFOCTRL_ + 0x24)
+#define FIFOPAGE1 (RTL8712_FIFOCTRL_ + 0x28)
+#define FW_RSVD_PG_CTRL (RTL8712_FIFOCTRL_ + 0x30)
+#define TXRPTFF_RDPTR (RTL8712_FIFOCTRL_ + 0x40)
+#define TXRPTFF_WTPTR (RTL8712_FIFOCTRL_ + 0x44)
+#define C2HFF_RDPTR (RTL8712_FIFOCTRL_ + 0x48)
+#define C2HFF_WTPTR (RTL8712_FIFOCTRL_ + 0x4C)
+#define RXFF0_RDPTR (RTL8712_FIFOCTRL_ + 0x50)
+#define RXFF0_WTPTR (RTL8712_FIFOCTRL_ + 0x54)
+#define RXFF1_RDPTR (RTL8712_FIFOCTRL_ + 0x58)
+#define RXFF1_WTPTR (RTL8712_FIFOCTRL_ + 0x5C)
+#define RXRPT0FF_RDPTR (RTL8712_FIFOCTRL_ + 0x60)
+#define RXRPT0FF_WTPTR (RTL8712_FIFOCTRL_ + 0x64)
+#define RXRPT1FF_RDPTR (RTL8712_FIFOCTRL_ + 0x68)
+#define RXRPT1FF_WTPTR (RTL8712_FIFOCTRL_ + 0x6C)
+#define RX0PKTNUM (RTL8712_FIFOCTRL_ + 0x72)
+#define RX1PKTNUM (RTL8712_FIFOCTRL_ + 0x74)
+#define RXFLTMAP0 (RTL8712_FIFOCTRL_ + 0x76)
+#define RXFLTMAP1 (RTL8712_FIFOCTRL_ + 0x78)
+#define RXFLTMAP2 (RTL8712_FIFOCTRL_ + 0x7A)
+#define RXFLTMAP3 (RTL8712_FIFOCTRL_ + 0x7c)
+#define TBDA (RTL8712_FIFOCTRL_ + 0x84)
+#define THPDA (RTL8712_FIFOCTRL_ + 0x88)
+#define TCDA (RTL8712_FIFOCTRL_ + 0x8C)
+#define TMDA (RTL8712_FIFOCTRL_ + 0x90)
+#define HDA (RTL8712_FIFOCTRL_ + 0x94)
+#define TVODA (RTL8712_FIFOCTRL_ + 0x98)
+#define TVIDA (RTL8712_FIFOCTRL_ + 0x9C)
+#define TBEDA (RTL8712_FIFOCTRL_ + 0xA0)
+#define TBKDA (RTL8712_FIFOCTRL_ + 0xA4)
+#define RCDA (RTL8712_FIFOCTRL_ + 0xA8)
+#define RDSA (RTL8712_FIFOCTRL_ + 0xAC)
+#define TXPKT_NUM_CTRL (RTL8712_FIFOCTRL_ + 0xB0)
+#define TXQ_PGADD (RTL8712_FIFOCTRL_ + 0xB3)
+#define TXFF_PG_NUM (RTL8712_FIFOCTRL_ + 0xB4)
+
+
+
+#endif /* __RTL8712_FIFOCTRL_REGDEF_H__ */
diff --git a/drivers/staging/rtl8712/rtl8712_gp_bitdef.h b/drivers/staging/rtl8712/rtl8712_gp_bitdef.h
new file mode 100644
index 000000000..138ea453d
--- /dev/null
+++ b/drivers/staging/rtl8712/rtl8712_gp_bitdef.h
@@ -0,0 +1,79 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2010 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ * Modifications for inclusion into the Linux staging tree are
+ * Copyright(c) 2010 Larry Finger. All rights reserved.
+ *
+ * Contact information:
+ * WLAN FAE <wlanfae@realtek.com>
+ * Larry Finger <Larry.Finger@lwfinger.net>
+ *
+ ******************************************************************************/
+#ifndef __RTL8712_GP_BITDEF_H__
+#define __RTL8712_GP_BITDEF_H__
+
+/*GPIO_CTRL*/
+#define _GPIO_MOD_MSK 0xFF000000
+#define _GPIO_MOD_SHT 24
+#define _GPIO_IO_SEL_MSK 0x00FF0000
+#define _GPIO_IO_SEL_SHT 16
+#define _GPIO_OUT_MSK 0x0000FF00
+#define _GPIO_OUT_SHT 8
+#define _GPIO_IN_MSK 0x000000FF
+#define _GPIO_IN_SHT 0
+
+/*SYS_PINMUX_CFG*/
+#define _GPIOSEL_MSK 0x0003
+#define _GPIOSEL_SHT 0
+
+/*LED_CFG*/
+#define _LED1SV BIT(7)
+#define _LED1CM_MSK 0x0070
+#define _LED1CM_SHT 4
+#define _LED0SV BIT(3)
+#define _LED0CM_MSK 0x0007
+#define _LED0CM_SHT 0
+
+/*PHY_REG*/
+#define _HST_RDRDY_SHT 0
+#define _HST_RDRDY_MSK 0xFF
+#define _HST_RDRDY BIT(_HST_RDRDY_SHT)
+#define _CPU_WTBUSY_SHT 1
+#define _CPU_WTBUSY_MSK 0xFF
+#define _CPU_WTBUSY BIT(_CPU_WTBUSY_SHT)
+
+/* 11. General Purpose Registers (Offset: 0x02E0 - 0x02FF)*/
+
+/* 8192S GPIO Config Setting (offset 0x2F1, 1 byte)*/
+
+/*----------------------------------------------------------------------------*/
+
+#define GPIOMUX_EN BIT(3) /* When this bit is set to "1",
+ * GPIO PINs will switch to MAC
+ * GPIO Function*/
+#define GPIOSEL_GPIO 0 /* UART or JTAG or pure GPIO*/
+#define GPIOSEL_PHYDBG 1 /* PHYDBG*/
+#define GPIOSEL_BT 2 /* BT_coex*/
+#define GPIOSEL_WLANDBG 3 /* WLANDBG*/
+#define GPIOSEL_GPIO_MASK (~(BIT(0)|BIT(1)))
+/* HW Radio OFF switch (GPIO BIT) */
+#define HAL_8192S_HW_GPIO_OFF_BIT BIT(3)
+#define HAL_8192S_HW_GPIO_OFF_MASK 0xF7
+#define HAL_8192S_HW_GPIO_WPS_BIT BIT(4)
+
+#endif /*__RTL8712_GP_BITDEF_H__*/
+
diff --git a/drivers/staging/rtl8712/rtl8712_gp_regdef.h b/drivers/staging/rtl8712/rtl8712_gp_regdef.h
new file mode 100644
index 000000000..8fc68f6a2
--- /dev/null
+++ b/drivers/staging/rtl8712/rtl8712_gp_regdef.h
@@ -0,0 +1,42 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2010 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ * Modifications for inclusion into the Linux staging tree are
+ * Copyright(c) 2010 Larry Finger. All rights reserved.
+ *
+ * Contact information:
+ * WLAN FAE <wlanfae@realtek.com>
+ * Larry Finger <Larry.Finger@lwfinger.net>
+ *
+ ******************************************************************************/
+#ifndef __RTL8712_GP_REGDEF_H__
+#define __RTL8712_GP_REGDEF_H__
+
+#define PSTIMER (RTL8712_GP_ + 0x00)
+#define TIMER1 (RTL8712_GP_ + 0x04)
+#define TIMER2 (RTL8712_GP_ + 0x08)
+#define GPIO_CTRL (RTL8712_GP_ + 0x0C)
+#define GPIO_IO_SEL (RTL8712_GP_ + 0x0E)
+#define GPIO_INTCTRL (RTL8712_GP_ + 0x10)
+#define MAC_PINMUX_CTRL (RTL8712_GP_ + 0x11)
+#define LEDCFG (RTL8712_GP_ + 0x12)
+#define PHY_REG_RPT (RTL8712_GP_ + 0x13)
+#define PHY_REG_DATA (RTL8712_GP_ + 0x14)
+
+
+#endif /*__RTL8712_GP_REGDEF_H__ */
+
diff --git a/drivers/staging/rtl8712/rtl8712_hal.h b/drivers/staging/rtl8712/rtl8712_hal.h
new file mode 100644
index 000000000..4c51fa373
--- /dev/null
+++ b/drivers/staging/rtl8712/rtl8712_hal.h
@@ -0,0 +1,150 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2010 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ * Modifications for inclusion into the Linux staging tree are
+ * Copyright(c) 2010 Larry Finger. All rights reserved.
+ *
+ * Contact information:
+ * WLAN FAE <wlanfae@realtek.com>
+ * Larry Finger <Larry.Finger@lwfinger.net>
+ *
+ ******************************************************************************/
+#ifndef __RTL8712_HAL_H__
+#define __RTL8712_HAL_H__
+
+enum _HW_VERSION {
+ RTL8712_FPGA,
+ RTL8712_1stCUT, /*A Cut (RTL8712_ASIC)*/
+ RTL8712_2ndCUT, /*B Cut*/
+ RTL8712_3rdCUT, /*C Cut*/
+};
+
+enum _LOOPBACK_TYPE {
+ RTL8712_AIR_TRX = 0,
+ RTL8712_MAC_LBK,
+ RTL8712_BB_LBK,
+ RTL8712_MAC_FW_LBK = 4,
+ RTL8712_BB_FW_LBK = 8,
+};
+
+enum RTL871X_HCI_TYPE {
+ RTL8712_SDIO,
+ RTL8712_USB,
+};
+
+enum RTL8712_RF_CONFIG {
+ RTL8712_RF_1T1R,
+ RTL8712_RF_1T2R,
+ RTL8712_RF_2T2R
+};
+
+enum _RTL8712_HCI_TYPE_ {
+ RTL8712_HCI_TYPE_PCIE = 0x01,
+ RTL8712_HCI_TYPE_AP_PCIE = 0x81,
+ RTL8712_HCI_TYPE_USB = 0x02,
+ RTL8712_HCI_TYPE_92USB = 0x02,
+ RTL8712_HCI_TYPE_AP_USB = 0x82,
+ RTL8712_HCI_TYPE_72USB = 0x12,
+ RTL8712_HCI_TYPE_SDIO = 0x04,
+ RTL8712_HCI_TYPE_72SDIO = 0x14
+};
+
+struct fw_priv { /*8-bytes alignment required*/
+ /*--- long word 0 ----*/
+ unsigned char signature_0; /*0x12: CE product, 0x92: IT product*/
+ unsigned char signature_1; /*0x87: CE product, 0x81: IT product*/
+ unsigned char hci_sel; /*0x81: PCI-AP, 01:PCIe, 02: 92S-U, 0x82: USB-AP,
+ * 0x12: 72S-U, 03:SDIO*/
+ unsigned char chip_version; /*the same value as register value*/
+ unsigned char customer_ID_0; /*customer ID low byte*/
+ unsigned char customer_ID_1; /*customer ID high byte*/
+ unsigned char rf_config; /*0x11: 1T1R, 0x12: 1T2R, 0x92: 1T2R turbo,
+ * 0x22: 2T2R*/
+ unsigned char usb_ep_num; /* 4: 4EP, 6: 6EP, 11: 11EP*/
+ /*--- long word 1 ----*/
+ unsigned char regulatory_class_0; /*regulatory class bit map 0*/
+ unsigned char regulatory_class_1; /*regulatory class bit map 1*/
+ unsigned char regulatory_class_2; /*regulatory class bit map 2*/
+ unsigned char regulatory_class_3; /*regulatory class bit map 3*/
+ unsigned char rfintfs; /* 0:SWSI, 1:HWSI, 2:HWPI*/
+ unsigned char def_nettype;
+ unsigned char turboMode;
+ unsigned char lowPowerMode;/* 0: normal mode, 1: low power mode*/
+ /*--- long word 2 ----*/
+ unsigned char lbk_mode; /*0x00: normal, 0x03: MACLBK, 0x01: PHYLBK*/
+ unsigned char mp_mode; /* 1: for MP use, 0: for normal driver */
+ unsigned char vcsType; /* 0:off 1:on 2:auto */
+ unsigned char vcsMode; /* 1:RTS/CTS 2:CTS to self */
+ unsigned char rsvd022;
+ unsigned char rsvd023;
+ unsigned char rsvd024;
+ unsigned char rsvd025;
+ /*--- long word 3 ----*/
+ unsigned char qos_en; /*1: QoS enable*/
+ unsigned char bw_40MHz_en; /*1: 40MHz BW enable*/
+ unsigned char AMSDU2AMPDU_en; /*1: 4181 convert AMSDU to AMPDU,
+ * 0: disable*/
+ unsigned char AMPDU_en; /*1: 11n AMPDU enable*/
+ unsigned char rate_control_offload; /*1: FW offloads,0: driver handles*/
+ unsigned char aggregation_offload; /*1: FW offloads,0: driver handles*/
+ unsigned char rsvd030;
+ unsigned char rsvd031;
+ /*--- long word 4 ----*/
+ unsigned char beacon_offload; /* 1. FW offloads, 0: driver handles*/
+ unsigned char MLME_offload; /* 2. FW offloads, 0: driver handles*/
+ unsigned char hwpc_offload; /* 3. FW offloads, 0: driver handles*/
+ unsigned char tcp_checksum_offload; /*4. FW offloads,0: driver handles*/
+ unsigned char tcp_offload; /* 5. FW offloads, 0: driver handles*/
+ unsigned char ps_control_offload; /* 6. FW offloads, 0: driver handles*/
+ unsigned char WWLAN_offload; /* 7. FW offloads, 0: driver handles*/
+ unsigned char rsvd040;
+ /*--- long word 5 ----*/
+ unsigned char tcp_tx_frame_len_L; /*tcp tx packet length low byte*/
+ unsigned char tcp_tx_frame_len_H; /*tcp tx packet length high byte*/
+ unsigned char tcp_rx_frame_len_L; /*tcp rx packet length low byte*/
+ unsigned char tcp_rx_frame_len_H; /*tcp rx packet length high byte*/
+ unsigned char rsvd050;
+ unsigned char rsvd051;
+ unsigned char rsvd052;
+ unsigned char rsvd053;
+};
+
+struct fw_hdr {/*8-byte alignment required*/
+ unsigned short signature;
+ unsigned short version; /*0x8000 ~ 0x8FFF for FPGA version,
+ *0x0000 ~ 0x7FFF for ASIC version,*/
+ unsigned int dmem_size; /*define the size of boot loader*/
+ unsigned int img_IMEM_size; /*define the size of FW in IMEM*/
+ unsigned int img_SRAM_size; /*define the size of FW in SRAM*/
+ unsigned int fw_priv_sz; /*define the size of DMEM variable*/
+ unsigned short efuse_addr;
+ unsigned short h2ccnd_resp_addr;
+ unsigned int SVNRevision;
+ unsigned int release_time; /*Mon:Day:Hr:Min*/
+ struct fw_priv fwpriv;
+};
+
+struct hal_priv {
+ /*Endpoint handles*/
+ struct net_device *pipehdls_r8712[10];
+ u8 (*hal_bus_init)(struct _adapter *adapter);
+};
+
+uint rtl8712_hal_init(struct _adapter *padapter);
+int rtl871x_load_fw(struct _adapter *padapter);
+
+#endif
diff --git a/drivers/staging/rtl8712/rtl8712_interrupt_bitdef.h b/drivers/staging/rtl8712/rtl8712_interrupt_bitdef.h
new file mode 100644
index 000000000..49598c314
--- /dev/null
+++ b/drivers/staging/rtl8712/rtl8712_interrupt_bitdef.h
@@ -0,0 +1,58 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2010 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ *
+ ******************************************************************************/
+#ifndef __RTL8712_INTERRUPT_BITDEF_H__
+#define __RTL8712_INTERRUPT_BITDEF_H__
+
+/*HIMR*/
+/*HISR*/
+#define _CPUERR BIT(29)
+#define _ATIMEND BIT(28)
+#define _TXBCNOK BIT(27)
+#define _TXBCNERR BIT(26)
+#define _BCNDMAINT4 BIT(25)
+#define _BCNDMAINT3 BIT(24)
+#define _BCNDMAINT2 BIT(23)
+#define _BCNDMAINT1 BIT(22)
+#define _BCNDOK4 BIT(21)
+#define _BCNDOK3 BIT(20)
+#define _BCNDOK2 BIT(19)
+#define _BCNDOK1 BIT(18)
+#define _TIMEOUT2 BIT(17)
+#define _TIMEOUT1 BIT(16)
+#define _TXFOVW BIT(15)
+#define _PSTIMEOUT BIT(14)
+#define _BCNDMAINT0 BIT(13)
+#define _FOVW BIT(12)
+#define _RDU BIT(11)
+#define _RXCMDOK BIT(10)
+#define _BCNDOK0 BIT(9)
+#define _HIGHDOK BIT(8)
+#define _COMDOK BIT(7)
+#define _MGTDOK BIT(6)
+#define _HCCADOK BIT(5)
+#define _BKDOK BIT(4)
+#define _BEDOK BIT(3)
+#define _VIDOK BIT(2)
+#define _VODOK BIT(1)
+#define _RXOK BIT(0)
+
+
+#endif /*__RTL8712_INTERRUPT_BITDEF_H__*/
+
diff --git a/drivers/staging/rtl8712/rtl8712_io.c b/drivers/staging/rtl8712/rtl8712_io.c
new file mode 100644
index 000000000..921fcffb3
--- /dev/null
+++ b/drivers/staging/rtl8712/rtl8712_io.c
@@ -0,0 +1,146 @@
+/******************************************************************************
+ * rtl8712_io.c
+ *
+ * Copyright(c) 2007 - 2010 Realtek Corporation. All rights reserved.
+ * Linux device driver for RTL8192SU
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ * Modifications for inclusion into the Linux staging tree are
+ * Copyright(c) 2010 Larry Finger. All rights reserved.
+ *
+ * Contact information:
+ * WLAN FAE <wlanfae@realtek.com>.
+ * Larry Finger <Larry.Finger@lwfinger.net>
+ *
+ ******************************************************************************/
+
+#define _RTL8712_IO_C_
+
+#include "osdep_service.h"
+#include "drv_types.h"
+#include "rtl871x_io.h"
+#include "osdep_intf.h"
+#include "usb_ops.h"
+
+u8 r8712_read8(struct _adapter *adapter, u32 addr)
+{
+ struct io_queue *pio_queue = (struct io_queue *)adapter->pio_queue;
+ struct intf_hdl *pintfhdl = &(pio_queue->intf);
+ u8 (*_read8)(struct intf_hdl *pintfhdl, u32 addr);
+
+ _read8 = pintfhdl->io_ops._read8;
+ return _read8(pintfhdl, addr);
+}
+
+u16 r8712_read16(struct _adapter *adapter, u32 addr)
+{
+ struct io_queue *pio_queue = (struct io_queue *)adapter->pio_queue;
+ struct intf_hdl *pintfhdl = &(pio_queue->intf);
+ u16 (*_read16)(struct intf_hdl *pintfhdl, u32 addr);
+
+ _read16 = pintfhdl->io_ops._read16;
+ return _read16(pintfhdl, addr);
+}
+
+u32 r8712_read32(struct _adapter *adapter, u32 addr)
+{
+ struct io_queue *pio_queue = (struct io_queue *)adapter->pio_queue;
+ struct intf_hdl *pintfhdl = &(pio_queue->intf);
+ u32 (*_read32)(struct intf_hdl *pintfhdl, u32 addr);
+
+ _read32 = pintfhdl->io_ops._read32;
+ return _read32(pintfhdl, addr);
+}
+
+void r8712_write8(struct _adapter *adapter, u32 addr, u8 val)
+{
+ struct io_queue *pio_queue = (struct io_queue *)adapter->pio_queue;
+ struct intf_hdl *pintfhdl = &(pio_queue->intf);
+ void (*_write8)(struct intf_hdl *pintfhdl, u32 addr, u8 val);
+
+ _write8 = pintfhdl->io_ops._write8;
+ _write8(pintfhdl, addr, val);
+}
+
+void r8712_write16(struct _adapter *adapter, u32 addr, u16 val)
+{
+ struct io_queue *pio_queue = (struct io_queue *)adapter->pio_queue;
+ struct intf_hdl *pintfhdl = &(pio_queue->intf);
+ void (*_write16)(struct intf_hdl *pintfhdl, u32 addr, u16 val);
+
+ _write16 = pintfhdl->io_ops._write16;
+ _write16(pintfhdl, addr, val);
+}
+
+void r8712_write32(struct _adapter *adapter, u32 addr, u32 val)
+{
+ struct io_queue *pio_queue = (struct io_queue *)adapter->pio_queue;
+ struct intf_hdl *pintfhdl = (struct intf_hdl *)(&(pio_queue->intf));
+
+ void (*_write32)(struct intf_hdl *pintfhdl, u32 addr, u32 val);
+
+ _write32 = pintfhdl->io_ops._write32;
+ _write32(pintfhdl, addr, val);
+}
+
+void r8712_read_mem(struct _adapter *adapter, u32 addr, u32 cnt, u8 *pmem)
+{
+ struct io_queue *pio_queue = (struct io_queue *)adapter->pio_queue;
+ struct intf_hdl *pintfhdl = &(pio_queue->intf);
+
+ void (*_read_mem)(struct intf_hdl *pintfhdl, u32 addr, u32 cnt,
+ u8 *pmem);
+ if ((adapter->bDriverStopped == true) ||
+ (adapter->bSurpriseRemoved == true))
+ return;
+ _read_mem = pintfhdl->io_ops._read_mem;
+ _read_mem(pintfhdl, addr, cnt, pmem);
+}
+
+void r8712_write_mem(struct _adapter *adapter, u32 addr, u32 cnt, u8 *pmem)
+{
+ struct io_queue *pio_queue = (struct io_queue *)adapter->pio_queue;
+ struct intf_hdl *pintfhdl = &(pio_queue->intf);
+ void (*_write_mem)(struct intf_hdl *pintfhdl, u32 addr, u32 cnt,
+ u8 *pmem);
+
+ _write_mem = pintfhdl->io_ops._write_mem;
+ _write_mem(pintfhdl, addr, cnt, pmem);
+}
+
+void r8712_read_port(struct _adapter *adapter, u32 addr, u32 cnt, u8 *pmem)
+{
+ struct io_queue *pio_queue = (struct io_queue *)adapter->pio_queue;
+ struct intf_hdl *pintfhdl = &(pio_queue->intf);
+
+ u32 (*_read_port)(struct intf_hdl *pintfhdl, u32 addr, u32 cnt,
+ u8 *pmem);
+ if ((adapter->bDriverStopped == true) ||
+ (adapter->bSurpriseRemoved == true))
+ return;
+ _read_port = pintfhdl->io_ops._read_port;
+ _read_port(pintfhdl, addr, cnt, pmem);
+}
+
+void r8712_write_port(struct _adapter *adapter, u32 addr, u32 cnt, u8 *pmem)
+{
+ struct io_queue *pio_queue = (struct io_queue *)adapter->pio_queue;
+ struct intf_hdl *pintfhdl = &(pio_queue->intf);
+
+ u32 (*_write_port)(struct intf_hdl *pintfhdl, u32 addr, u32 cnt,
+ u8 *pmem);
+ _write_port = pintfhdl->io_ops._write_port;
+ _write_port(pintfhdl, addr, cnt, pmem);
+}
diff --git a/drivers/staging/rtl8712/rtl8712_led.c b/drivers/staging/rtl8712/rtl8712_led.c
new file mode 100644
index 000000000..ada8d5daf
--- /dev/null
+++ b/drivers/staging/rtl8712/rtl8712_led.c
@@ -0,0 +1,1834 @@
+/******************************************************************************
+ * rtl8712_led.c
+ *
+ * Copyright(c) 2007 - 2010 Realtek Corporation. All rights reserved.
+ * Linux device driver for RTL8192SU
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ * Modifications for inclusion into the Linux staging tree are
+ * Copyright(c) 2010 Larry Finger. All rights reserved.
+ *
+ * Contact information:
+ * WLAN FAE <wlanfae@realtek.com>
+ * Larry Finger <Larry.Finger@lwfinger.net>
+ *
+ ******************************************************************************/
+
+#include "drv_types.h"
+
+/*===========================================================================
+ * Constant.
+ *===========================================================================
+
+ *
+ * Default LED behavior.
+ */
+#define LED_BLINK_NORMAL_INTERVAL 100
+#define LED_BLINK_SLOWLY_INTERVAL 200
+#define LED_BLINK_LONG_INTERVAL 400
+
+#define LED_BLINK_NO_LINK_INTERVAL_ALPHA 1000
+#define LED_BLINK_LINK_INTERVAL_ALPHA 500
+#define LED_BLINK_SCAN_INTERVAL_ALPHA 180
+#define LED_BLINK_FASTER_INTERVAL_ALPHA 50
+#define LED_BLINK_WPS_SUCESS_INTERVAL_ALPHA 5000
+
+/*===========================================================================
+ * LED object.
+ *===========================================================================
+ */
+enum _LED_STATE_871x {
+ LED_UNKNOWN = 0,
+ LED_ON = 1,
+ LED_OFF = 2,
+ LED_BLINK_NORMAL = 3,
+ LED_BLINK_SLOWLY = 4,
+ LED_POWER_ON_BLINK = 5,
+ LED_SCAN_BLINK = 6, /* LED is blinking during scanning period,
+ * the # of times to blink is depend on time
+ * for scanning. */
+ LED_NO_LINK_BLINK = 7, /* LED is blinking during no link state. */
+ LED_BLINK_StartToBlink = 8,/* Customized for Sercomm Printer
+ * Server case */
+ LED_BLINK_WPS = 9, /* LED is blinkg during WPS communication */
+ LED_TXRX_BLINK = 10,
+ LED_BLINK_WPS_STOP = 11, /*for ALPHA */
+ LED_BLINK_WPS_STOP_OVERLAP = 12, /*for BELKIN */
+};
+
+/*===========================================================================
+ * Prototype of protected function.
+ *===========================================================================
+ */
+static void BlinkTimerCallback(unsigned long data);
+
+static void BlinkWorkItemCallback(struct work_struct *work);
+/*===========================================================================
+ * LED_819xUsb routines.
+ *===========================================================================
+ *
+ *
+ *
+ * Description:
+ * Initialize an LED_871x object.
+ */
+static void InitLed871x(struct _adapter *padapter, struct LED_871x *pLed,
+ enum LED_PIN_871x LedPin)
+{
+ struct net_device *nic;
+
+ nic = padapter->pnetdev;
+ pLed->padapter = padapter;
+ pLed->LedPin = LedPin;
+ pLed->CurrLedState = LED_OFF;
+ pLed->bLedOn = false;
+ pLed->bLedBlinkInProgress = false;
+ pLed->BlinkTimes = 0;
+ pLed->BlinkingLedState = LED_UNKNOWN;
+ setup_timer(&pLed->BlinkTimer, BlinkTimerCallback,
+ (unsigned long)pLed);
+ INIT_WORK(&pLed->BlinkWorkItem, BlinkWorkItemCallback);
+}
+
+/*
+ * Description:
+ * DeInitialize an LED_871x object.
+ */
+static void DeInitLed871x(struct LED_871x *pLed)
+{
+ del_timer_sync(&pLed->BlinkTimer);
+ /* We should reset bLedBlinkInProgress if we cancel
+ * the LedControlTimer, */
+ pLed->bLedBlinkInProgress = false;
+}
+
+/*
+ * Description:
+ * Turn on LED according to LedPin specified.
+ */
+static void SwLedOn(struct _adapter *padapter, struct LED_871x *pLed)
+{
+ u8 LedCfg;
+
+ if ((padapter->bSurpriseRemoved == true) ||
+ (padapter->bDriverStopped == true))
+ return;
+ LedCfg = r8712_read8(padapter, LEDCFG);
+ switch (pLed->LedPin) {
+ case LED_PIN_GPIO0:
+ break;
+ case LED_PIN_LED0:
+ /* SW control led0 on.*/
+ r8712_write8(padapter, LEDCFG, LedCfg&0xf0);
+ break;
+ case LED_PIN_LED1:
+ /* SW control led1 on.*/
+ r8712_write8(padapter, LEDCFG, LedCfg&0x0f);
+ break;
+ default:
+ break;
+ }
+ pLed->bLedOn = true;
+}
+
+/*
+ * Description:
+ * Turn off LED according to LedPin specified.
+ */
+static void SwLedOff(struct _adapter *padapter, struct LED_871x *pLed)
+{
+ u8 LedCfg;
+
+ if ((padapter->bSurpriseRemoved == true) ||
+ (padapter->bDriverStopped == true))
+ return;
+ LedCfg = r8712_read8(padapter, LEDCFG);
+ switch (pLed->LedPin) {
+ case LED_PIN_GPIO0:
+ break;
+ case LED_PIN_LED0:
+ LedCfg &= 0xf0; /* Set to software control.*/
+ r8712_write8(padapter, LEDCFG, (LedCfg|BIT(3)));
+ break;
+ case LED_PIN_LED1:
+ LedCfg &= 0x0f; /* Set to software control.*/
+ r8712_write8(padapter, LEDCFG, (LedCfg|BIT(7)));
+ break;
+ default:
+ break;
+ }
+ pLed->bLedOn = false;
+}
+
+/*===========================================================================
+ * Interface to manipulate LED objects.
+ *===========================================================================
+ *
+ * Description:
+ * Initialize all LED_871x objects.
+ */
+void r8712_InitSwLeds(struct _adapter *padapter)
+{
+ struct led_priv *pledpriv = &(padapter->ledpriv);
+
+ pledpriv->LedControlHandler = LedControl871x;
+ InitLed871x(padapter, &(pledpriv->SwLed0), LED_PIN_LED0);
+ InitLed871x(padapter, &(pledpriv->SwLed1), LED_PIN_LED1);
+}
+
+/* Description:
+ * DeInitialize all LED_819xUsb objects.
+ */
+void r8712_DeInitSwLeds(struct _adapter *padapter)
+{
+ struct led_priv *ledpriv = &(padapter->ledpriv);
+
+ DeInitLed871x(&(ledpriv->SwLed0));
+ DeInitLed871x(&(ledpriv->SwLed1));
+}
+
+/* Description:
+ * Implementation of LED blinking behavior.
+ * It toggle off LED and schedule corresponding timer if necessary.
+ */
+static void SwLedBlink(struct LED_871x *pLed)
+{
+ struct _adapter *padapter = pLed->padapter;
+ struct mlme_priv *pmlmepriv = &(padapter->mlmepriv);
+ u8 bStopBlinking = false;
+
+ /* Change LED according to BlinkingLedState specified. */
+ if (pLed->BlinkingLedState == LED_ON)
+ SwLedOn(padapter, pLed);
+ else
+ SwLedOff(padapter, pLed);
+ /* Determine if we shall change LED state again. */
+ pLed->BlinkTimes--;
+ switch (pLed->CurrLedState) {
+ case LED_BLINK_NORMAL:
+ if (pLed->BlinkTimes == 0)
+ bStopBlinking = true;
+ break;
+ case LED_BLINK_StartToBlink:
+ if ((check_fwstate(pmlmepriv, _FW_LINKED) == true) &&
+ (pmlmepriv->fw_state & WIFI_STATION_STATE))
+ bStopBlinking = true;
+ if ((check_fwstate(pmlmepriv, _FW_LINKED) == true) &&
+ ((pmlmepriv->fw_state & WIFI_ADHOC_STATE) ||
+ (pmlmepriv->fw_state & WIFI_ADHOC_MASTER_STATE)))
+ bStopBlinking = true;
+ else if (pLed->BlinkTimes == 0)
+ bStopBlinking = true;
+ break;
+ case LED_BLINK_WPS:
+ if (pLed->BlinkTimes == 0)
+ bStopBlinking = true;
+ break;
+ default:
+ bStopBlinking = true;
+ break;
+ }
+ if (bStopBlinking) {
+ if ((check_fwstate(pmlmepriv, _FW_LINKED) == true) &&
+ (pLed->bLedOn == false))
+ SwLedOn(padapter, pLed);
+ else if ((check_fwstate(pmlmepriv, _FW_LINKED) ==
+ true) && pLed->bLedOn == true)
+ SwLedOff(padapter, pLed);
+ pLed->BlinkTimes = 0;
+ pLed->bLedBlinkInProgress = false;
+ } else {
+ /* Assign LED state to toggle. */
+ if (pLed->BlinkingLedState == LED_ON)
+ pLed->BlinkingLedState = LED_OFF;
+ else
+ pLed->BlinkingLedState = LED_ON;
+
+ /* Schedule a timer to toggle LED state. */
+ switch (pLed->CurrLedState) {
+ case LED_BLINK_NORMAL:
+ mod_timer(&pLed->BlinkTimer, jiffies +
+ msecs_to_jiffies(LED_BLINK_NORMAL_INTERVAL));
+ break;
+ case LED_BLINK_SLOWLY:
+ case LED_BLINK_StartToBlink:
+ mod_timer(&pLed->BlinkTimer, jiffies +
+ msecs_to_jiffies(LED_BLINK_SLOWLY_INTERVAL));
+ break;
+ case LED_BLINK_WPS:
+ mod_timer(&pLed->BlinkTimer, jiffies +
+ msecs_to_jiffies(LED_BLINK_LONG_INTERVAL));
+ break;
+ default:
+ mod_timer(&pLed->BlinkTimer, jiffies +
+ msecs_to_jiffies(LED_BLINK_SLOWLY_INTERVAL));
+ break;
+ }
+ }
+}
+
+static void SwLedBlink1(struct LED_871x *pLed)
+{
+ struct _adapter *padapter = pLed->padapter;
+ struct led_priv *ledpriv = &(padapter->ledpriv);
+ struct mlme_priv *pmlmepriv = &(padapter->mlmepriv);
+ struct eeprom_priv *peeprompriv = &(padapter->eeprompriv);
+ struct LED_871x *pLed1 = &(ledpriv->SwLed1);
+ u8 bStopBlinking = false;
+
+ if (peeprompriv->CustomerID == RT_CID_819x_CAMEO)
+ pLed = &(ledpriv->SwLed1);
+ /* Change LED according to BlinkingLedState specified. */
+ if (pLed->BlinkingLedState == LED_ON)
+ SwLedOn(padapter, pLed);
+ else
+ SwLedOff(padapter, pLed);
+ if (peeprompriv->CustomerID == RT_CID_DEFAULT) {
+ if (check_fwstate(pmlmepriv, _FW_LINKED) == true) {
+ if (!pLed1->bSWLedCtrl) {
+ SwLedOn(padapter, pLed1);
+ pLed1->bSWLedCtrl = true;
+ } else if (!pLed1->bLedOn)
+ SwLedOn(padapter, pLed1);
+ } else {
+ if (!pLed1->bSWLedCtrl) {
+ SwLedOff(padapter, pLed1);
+ pLed1->bSWLedCtrl = true;
+ } else if (pLed1->bLedOn)
+ SwLedOff(padapter, pLed1);
+ }
+ }
+ switch (pLed->CurrLedState) {
+ case LED_BLINK_SLOWLY:
+ if (pLed->bLedOn)
+ pLed->BlinkingLedState = LED_OFF;
+ else
+ pLed->BlinkingLedState = LED_ON;
+ mod_timer(&pLed->BlinkTimer, jiffies +
+ msecs_to_jiffies(LED_BLINK_NO_LINK_INTERVAL_ALPHA));
+ break;
+ case LED_BLINK_NORMAL:
+ if (pLed->bLedOn)
+ pLed->BlinkingLedState = LED_OFF;
+ else
+ pLed->BlinkingLedState = LED_ON;
+ mod_timer(&pLed->BlinkTimer, jiffies +
+ msecs_to_jiffies(LED_BLINK_LINK_INTERVAL_ALPHA));
+ break;
+ case LED_SCAN_BLINK:
+ pLed->BlinkTimes--;
+ if (pLed->BlinkTimes == 0)
+ bStopBlinking = true;
+ if (bStopBlinking) {
+ if (check_fwstate(pmlmepriv, _FW_LINKED) == true) {
+ pLed->bLedLinkBlinkInProgress = true;
+ pLed->CurrLedState = LED_BLINK_NORMAL;
+ if (pLed->bLedOn)
+ pLed->BlinkingLedState = LED_OFF;
+ else
+ pLed->BlinkingLedState = LED_ON;
+ mod_timer(&pLed->BlinkTimer, jiffies +
+ msecs_to_jiffies(LED_BLINK_LINK_INTERVAL_ALPHA));
+ } else if (!check_fwstate(pmlmepriv, _FW_LINKED)) {
+ pLed->bLedNoLinkBlinkInProgress = true;
+ pLed->CurrLedState = LED_BLINK_SLOWLY;
+ if (pLed->bLedOn)
+ pLed->BlinkingLedState = LED_OFF;
+ else
+ pLed->BlinkingLedState = LED_ON;
+ mod_timer(&pLed->BlinkTimer, jiffies +
+ msecs_to_jiffies(LED_BLINK_NO_LINK_INTERVAL_ALPHA));
+ }
+ pLed->bLedScanBlinkInProgress = false;
+ } else {
+ if (pLed->bLedOn)
+ pLed->BlinkingLedState = LED_OFF;
+ else
+ pLed->BlinkingLedState = LED_ON;
+ mod_timer(&pLed->BlinkTimer, jiffies +
+ msecs_to_jiffies(LED_BLINK_SCAN_INTERVAL_ALPHA));
+ }
+ break;
+ case LED_TXRX_BLINK:
+ pLed->BlinkTimes--;
+ if (pLed->BlinkTimes == 0)
+ bStopBlinking = true;
+ if (bStopBlinking) {
+ if (check_fwstate(pmlmepriv, _FW_LINKED) == true) {
+ pLed->bLedLinkBlinkInProgress = true;
+ pLed->CurrLedState = LED_BLINK_NORMAL;
+ if (pLed->bLedOn)
+ pLed->BlinkingLedState = LED_OFF;
+ else
+ pLed->BlinkingLedState = LED_ON;
+ mod_timer(&pLed->BlinkTimer, jiffies +
+ msecs_to_jiffies(LED_BLINK_LINK_INTERVAL_ALPHA));
+ } else if (!check_fwstate(pmlmepriv, _FW_LINKED)) {
+ pLed->bLedNoLinkBlinkInProgress = true;
+ pLed->CurrLedState = LED_BLINK_SLOWLY;
+ if (pLed->bLedOn)
+ pLed->BlinkingLedState = LED_OFF;
+ else
+ pLed->BlinkingLedState = LED_ON;
+ mod_timer(&pLed->BlinkTimer, jiffies +
+ msecs_to_jiffies(LED_BLINK_NO_LINK_INTERVAL_ALPHA));
+ }
+ pLed->BlinkTimes = 0;
+ pLed->bLedBlinkInProgress = false;
+ } else {
+ if (pLed->bLedOn)
+ pLed->BlinkingLedState = LED_OFF;
+ else
+ pLed->BlinkingLedState = LED_ON;
+ mod_timer(&pLed->BlinkTimer, jiffies +
+ msecs_to_jiffies(LED_BLINK_FASTER_INTERVAL_ALPHA));
+ }
+ break;
+ case LED_BLINK_WPS:
+ if (pLed->bLedOn)
+ pLed->BlinkingLedState = LED_OFF;
+ else
+ pLed->BlinkingLedState = LED_ON;
+ mod_timer(&pLed->BlinkTimer, jiffies +
+ msecs_to_jiffies(LED_BLINK_SCAN_INTERVAL_ALPHA));
+ break;
+ case LED_BLINK_WPS_STOP: /* WPS success */
+ if (pLed->BlinkingLedState == LED_ON) {
+ pLed->BlinkingLedState = LED_OFF;
+ mod_timer(&pLed->BlinkTimer, jiffies +
+ msecs_to_jiffies(LED_BLINK_WPS_SUCESS_INTERVAL_ALPHA));
+ bStopBlinking = false;
+ } else
+ bStopBlinking = true;
+ if (bStopBlinking) {
+ pLed->bLedLinkBlinkInProgress = true;
+ pLed->CurrLedState = LED_BLINK_NORMAL;
+ if (pLed->bLedOn)
+ pLed->BlinkingLedState = LED_OFF;
+ else
+ pLed->BlinkingLedState = LED_ON;
+ mod_timer(&pLed->BlinkTimer, jiffies +
+ msecs_to_jiffies(LED_BLINK_LINK_INTERVAL_ALPHA));
+ }
+ pLed->bLedWPSBlinkInProgress = false;
+ break;
+ default:
+ break;
+ }
+}
+
+static void SwLedBlink2(struct LED_871x *pLed)
+{
+ struct _adapter *padapter = pLed->padapter;
+ struct mlme_priv *pmlmepriv = &(padapter->mlmepriv);
+ u8 bStopBlinking = false;
+
+ /* Change LED according to BlinkingLedState specified. */
+ if (pLed->BlinkingLedState == LED_ON)
+ SwLedOn(padapter, pLed);
+ else
+ SwLedOff(padapter, pLed);
+ switch (pLed->CurrLedState) {
+ case LED_SCAN_BLINK:
+ pLed->BlinkTimes--;
+ if (pLed->BlinkTimes == 0)
+ bStopBlinking = true;
+ if (bStopBlinking) {
+ if (check_fwstate(pmlmepriv, _FW_LINKED) == true) {
+ pLed->CurrLedState = LED_ON;
+ pLed->BlinkingLedState = LED_ON;
+ SwLedOn(padapter, pLed);
+ } else if (!check_fwstate(pmlmepriv, _FW_LINKED)) {
+ pLed->CurrLedState = LED_OFF;
+ pLed->BlinkingLedState = LED_OFF;
+ SwLedOff(padapter, pLed);
+ }
+ pLed->bLedScanBlinkInProgress = false;
+ } else {
+ if (pLed->bLedOn)
+ pLed->BlinkingLedState = LED_OFF;
+ else
+ pLed->BlinkingLedState = LED_ON;
+ mod_timer(&pLed->BlinkTimer, jiffies +
+ msecs_to_jiffies(LED_BLINK_SCAN_INTERVAL_ALPHA));
+ }
+ break;
+ case LED_TXRX_BLINK:
+ pLed->BlinkTimes--;
+ if (pLed->BlinkTimes == 0)
+ bStopBlinking = true;
+ if (bStopBlinking) {
+ if (check_fwstate(pmlmepriv, _FW_LINKED) == true) {
+ pLed->CurrLedState = LED_ON;
+ pLed->BlinkingLedState = LED_ON;
+ SwLedOn(padapter, pLed);
+ } else if (!check_fwstate(pmlmepriv, _FW_LINKED)) {
+ pLed->CurrLedState = LED_OFF;
+ pLed->BlinkingLedState = LED_OFF;
+ SwLedOff(padapter, pLed);
+ }
+ pLed->bLedBlinkInProgress = false;
+ } else {
+ if (pLed->bLedOn)
+ pLed->BlinkingLedState = LED_OFF;
+ else
+ pLed->BlinkingLedState = LED_ON;
+ mod_timer(&pLed->BlinkTimer, jiffies +
+ msecs_to_jiffies(LED_BLINK_FASTER_INTERVAL_ALPHA));
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+static void SwLedBlink3(struct LED_871x *pLed)
+{
+ struct _adapter *padapter = pLed->padapter;
+ struct mlme_priv *pmlmepriv = &(padapter->mlmepriv);
+ u8 bStopBlinking = false;
+
+ /* Change LED according to BlinkingLedState specified. */
+ if (pLed->BlinkingLedState == LED_ON)
+ SwLedOn(padapter, pLed);
+ else
+ if (pLed->CurrLedState != LED_BLINK_WPS_STOP)
+ SwLedOff(padapter, pLed);
+ switch (pLed->CurrLedState) {
+ case LED_SCAN_BLINK:
+ pLed->BlinkTimes--;
+ if (pLed->BlinkTimes == 0)
+ bStopBlinking = true;
+ if (bStopBlinking) {
+ if (check_fwstate(pmlmepriv, _FW_LINKED) == true) {
+ pLed->CurrLedState = LED_ON;
+ pLed->BlinkingLedState = LED_ON;
+ if (!pLed->bLedOn)
+ SwLedOn(padapter, pLed);
+ } else if (!check_fwstate(pmlmepriv, _FW_LINKED)) {
+ pLed->CurrLedState = LED_OFF;
+ pLed->BlinkingLedState = LED_OFF;
+ if (pLed->bLedOn)
+ SwLedOff(padapter, pLed);
+ }
+ pLed->bLedScanBlinkInProgress = false;
+ } else {
+ if (pLed->bLedOn)
+ pLed->BlinkingLedState = LED_OFF;
+ else
+ pLed->BlinkingLedState = LED_ON;
+ mod_timer(&pLed->BlinkTimer, jiffies +
+ msecs_to_jiffies(LED_BLINK_SCAN_INTERVAL_ALPHA));
+ }
+ break;
+ case LED_TXRX_BLINK:
+ pLed->BlinkTimes--;
+ if (pLed->BlinkTimes == 0)
+ bStopBlinking = true;
+ if (bStopBlinking) {
+ if (check_fwstate(pmlmepriv, _FW_LINKED) == true) {
+ pLed->CurrLedState = LED_ON;
+ pLed->BlinkingLedState = LED_ON;
+ if (!pLed->bLedOn)
+ SwLedOn(padapter, pLed);
+ } else if (!check_fwstate(pmlmepriv, _FW_LINKED)) {
+ pLed->CurrLedState = LED_OFF;
+ pLed->BlinkingLedState = LED_OFF;
+ if (pLed->bLedOn)
+ SwLedOff(padapter, pLed);
+ }
+ pLed->bLedBlinkInProgress = false;
+ } else {
+ if (pLed->bLedOn)
+ pLed->BlinkingLedState = LED_OFF;
+ else
+ pLed->BlinkingLedState = LED_ON;
+ mod_timer(&pLed->BlinkTimer, jiffies +
+ msecs_to_jiffies(LED_BLINK_FASTER_INTERVAL_ALPHA));
+ }
+ break;
+ case LED_BLINK_WPS:
+ if (pLed->bLedOn)
+ pLed->BlinkingLedState = LED_OFF;
+ else
+ pLed->BlinkingLedState = LED_ON;
+ mod_timer(&pLed->BlinkTimer, jiffies +
+ msecs_to_jiffies(LED_BLINK_SCAN_INTERVAL_ALPHA));
+ break;
+ case LED_BLINK_WPS_STOP: /*WPS success*/
+ if (pLed->BlinkingLedState == LED_ON) {
+ pLed->BlinkingLedState = LED_OFF;
+ mod_timer(&pLed->BlinkTimer, jiffies +
+ msecs_to_jiffies(LED_BLINK_WPS_SUCESS_INTERVAL_ALPHA));
+ bStopBlinking = false;
+ } else
+ bStopBlinking = true;
+ if (bStopBlinking) {
+ pLed->CurrLedState = LED_ON;
+ pLed->BlinkingLedState = LED_ON;
+ SwLedOn(padapter, pLed);
+ pLed->bLedWPSBlinkInProgress = false;
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+static void SwLedBlink4(struct LED_871x *pLed)
+{
+ struct _adapter *padapter = pLed->padapter;
+ struct led_priv *ledpriv = &(padapter->ledpriv);
+ struct LED_871x *pLed1 = &(ledpriv->SwLed1);
+ u8 bStopBlinking = false;
+
+ /* Change LED according to BlinkingLedState specified. */
+ if (pLed->BlinkingLedState == LED_ON)
+ SwLedOn(padapter, pLed);
+ else
+ SwLedOff(padapter, pLed);
+ if (!pLed1->bLedWPSBlinkInProgress &&
+ pLed1->BlinkingLedState == LED_UNKNOWN) {
+ pLed1->BlinkingLedState = LED_OFF;
+ pLed1->CurrLedState = LED_OFF;
+ SwLedOff(padapter, pLed1);
+ }
+ switch (pLed->CurrLedState) {
+ case LED_BLINK_SLOWLY:
+ if (pLed->bLedOn)
+ pLed->BlinkingLedState = LED_OFF;
+ else
+ pLed->BlinkingLedState = LED_ON;
+ mod_timer(&pLed->BlinkTimer, jiffies +
+ msecs_to_jiffies(LED_BLINK_NO_LINK_INTERVAL_ALPHA));
+ break;
+ case LED_BLINK_StartToBlink:
+ if (pLed->bLedOn) {
+ pLed->BlinkingLedState = LED_OFF;
+ mod_timer(&pLed->BlinkTimer, jiffies +
+ msecs_to_jiffies(LED_BLINK_SLOWLY_INTERVAL));
+ } else {
+ pLed->BlinkingLedState = LED_ON;
+ mod_timer(&pLed->BlinkTimer, jiffies +
+ msecs_to_jiffies(LED_BLINK_NORMAL_INTERVAL));
+ }
+ break;
+ case LED_SCAN_BLINK:
+ pLed->BlinkTimes--;
+ if (pLed->BlinkTimes == 0)
+ bStopBlinking = true;
+ if (bStopBlinking) {
+ pLed->bLedNoLinkBlinkInProgress = true;
+ pLed->CurrLedState = LED_BLINK_SLOWLY;
+ if (pLed->bLedOn)
+ pLed->BlinkingLedState = LED_OFF;
+ else
+ pLed->BlinkingLedState = LED_ON;
+ mod_timer(&pLed->BlinkTimer, jiffies +
+ msecs_to_jiffies(LED_BLINK_NO_LINK_INTERVAL_ALPHA));
+ pLed->bLedScanBlinkInProgress = false;
+ } else {
+ if (pLed->bLedOn)
+ pLed->BlinkingLedState = LED_OFF;
+ else
+ pLed->BlinkingLedState = LED_ON;
+ mod_timer(&pLed->BlinkTimer, jiffies +
+ msecs_to_jiffies(LED_BLINK_SCAN_INTERVAL_ALPHA));
+ }
+ break;
+ case LED_TXRX_BLINK:
+ pLed->BlinkTimes--;
+ if (pLed->BlinkTimes == 0)
+ bStopBlinking = true;
+ if (bStopBlinking) {
+ pLed->bLedNoLinkBlinkInProgress = true;
+ pLed->CurrLedState = LED_BLINK_SLOWLY;
+ if (pLed->bLedOn)
+ pLed->BlinkingLedState = LED_OFF;
+ else
+ pLed->BlinkingLedState = LED_ON;
+ mod_timer(&pLed->BlinkTimer, jiffies +
+ msecs_to_jiffies(LED_BLINK_NO_LINK_INTERVAL_ALPHA));
+ pLed->bLedBlinkInProgress = false;
+ } else {
+ if (pLed->bLedOn)
+ pLed->BlinkingLedState = LED_OFF;
+ else
+ pLed->BlinkingLedState = LED_ON;
+ mod_timer(&pLed->BlinkTimer, jiffies +
+ msecs_to_jiffies(LED_BLINK_FASTER_INTERVAL_ALPHA));
+ }
+ break;
+ case LED_BLINK_WPS:
+ if (pLed->bLedOn) {
+ pLed->BlinkingLedState = LED_OFF;
+ mod_timer(&pLed->BlinkTimer, jiffies +
+ msecs_to_jiffies(LED_BLINK_SLOWLY_INTERVAL));
+ } else {
+ pLed->BlinkingLedState = LED_ON;
+ mod_timer(&pLed->BlinkTimer, jiffies +
+ msecs_to_jiffies(LED_BLINK_NORMAL_INTERVAL));
+ }
+ break;
+ case LED_BLINK_WPS_STOP: /*WPS authentication fail*/
+ if (pLed->bLedOn)
+ pLed->BlinkingLedState = LED_OFF;
+ else
+ pLed->BlinkingLedState = LED_ON;
+ mod_timer(&pLed->BlinkTimer, jiffies +
+ msecs_to_jiffies(LED_BLINK_NORMAL_INTERVAL));
+ break;
+ case LED_BLINK_WPS_STOP_OVERLAP: /*WPS session overlap */
+ pLed->BlinkTimes--;
+ if (pLed->BlinkTimes == 0) {
+ if (pLed->bLedOn)
+ pLed->BlinkTimes = 1;
+ else
+ bStopBlinking = true;
+ }
+ if (bStopBlinking) {
+ pLed->BlinkTimes = 10;
+ pLed->BlinkingLedState = LED_ON;
+ mod_timer(&pLed->BlinkTimer, jiffies +
+ msecs_to_jiffies(LED_BLINK_LINK_INTERVAL_ALPHA));
+ } else {
+ if (pLed->bLedOn)
+ pLed->BlinkingLedState = LED_OFF;
+ else
+ pLed->BlinkingLedState = LED_ON;
+ mod_timer(&pLed->BlinkTimer, jiffies +
+ msecs_to_jiffies(LED_BLINK_NORMAL_INTERVAL));
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+static void SwLedBlink5(struct LED_871x *pLed)
+{
+ struct _adapter *padapter = pLed->padapter;
+ u8 bStopBlinking = false;
+
+ /* Change LED according to BlinkingLedState specified. */
+ if (pLed->BlinkingLedState == LED_ON)
+ SwLedOn(padapter, pLed);
+ else
+ SwLedOff(padapter, pLed);
+ switch (pLed->CurrLedState) {
+ case LED_SCAN_BLINK:
+ pLed->BlinkTimes--;
+ if (pLed->BlinkTimes == 0)
+ bStopBlinking = true;
+ if (bStopBlinking) {
+ pLed->CurrLedState = LED_ON;
+ pLed->BlinkingLedState = LED_ON;
+ if (!pLed->bLedOn)
+ mod_timer(&pLed->BlinkTimer, jiffies +
+ msecs_to_jiffies(LED_BLINK_FASTER_INTERVAL_ALPHA));
+ pLed->bLedScanBlinkInProgress = false;
+ } else {
+ if (pLed->bLedOn)
+ pLed->BlinkingLedState = LED_OFF;
+ else
+ pLed->BlinkingLedState = LED_ON;
+ mod_timer(&pLed->BlinkTimer, jiffies +
+ msecs_to_jiffies(LED_BLINK_SCAN_INTERVAL_ALPHA));
+ }
+ break;
+ case LED_TXRX_BLINK:
+ pLed->BlinkTimes--;
+ if (pLed->BlinkTimes == 0)
+ bStopBlinking = true;
+ if (bStopBlinking) {
+ pLed->CurrLedState = LED_ON;
+ pLed->BlinkingLedState = LED_ON;
+ if (!pLed->bLedOn)
+ mod_timer(&pLed->BlinkTimer, jiffies +
+ msecs_to_jiffies(LED_BLINK_FASTER_INTERVAL_ALPHA));
+ pLed->bLedBlinkInProgress = false;
+ } else {
+ if (pLed->bLedOn)
+ pLed->BlinkingLedState = LED_OFF;
+ else
+ pLed->BlinkingLedState = LED_ON;
+ mod_timer(&pLed->BlinkTimer, jiffies +
+ msecs_to_jiffies(LED_BLINK_FASTER_INTERVAL_ALPHA));
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+static void SwLedBlink6(struct LED_871x *pLed)
+{
+ struct _adapter *padapter = pLed->padapter;
+ u8 bStopBlinking = false;
+
+ /* Change LED according to BlinkingLedState specified. */
+ if (pLed->BlinkingLedState == LED_ON)
+ SwLedOn(padapter, pLed);
+ else
+ SwLedOff(padapter, pLed);
+ switch (pLed->CurrLedState) {
+ case LED_TXRX_BLINK:
+ pLed->BlinkTimes--;
+ if (pLed->BlinkTimes == 0)
+ bStopBlinking = true;
+ if (bStopBlinking) {
+ pLed->CurrLedState = LED_ON;
+ pLed->BlinkingLedState = LED_ON;
+ if (!pLed->bLedOn)
+ SwLedOn(padapter, pLed);
+ pLed->bLedBlinkInProgress = false;
+ } else {
+ if (pLed->bLedOn)
+ pLed->BlinkingLedState = LED_OFF;
+ else
+ pLed->BlinkingLedState = LED_ON;
+ mod_timer(&pLed->BlinkTimer, jiffies +
+ msecs_to_jiffies(LED_BLINK_FASTER_INTERVAL_ALPHA));
+ }
+ break;
+ case LED_BLINK_WPS:
+ if (pLed->bLedOn)
+ pLed->BlinkingLedState = LED_OFF;
+ else
+ pLed->BlinkingLedState = LED_ON;
+ mod_timer(&pLed->BlinkTimer, jiffies +
+ msecs_to_jiffies(LED_BLINK_SCAN_INTERVAL_ALPHA));
+ break;
+
+ default:
+ break;
+ }
+}
+
+/* Description:
+ * Callback function of LED BlinkTimer,
+ * it just schedules to corresponding BlinkWorkItem.
+ */
+static void BlinkTimerCallback(unsigned long data)
+{
+ struct LED_871x *pLed = (struct LED_871x *)data;
+
+ /* This fixed the crash problem on Fedora 12 when trying to do the
+ * insmod;ifconfig up;rmmod commands. */
+ if ((pLed->padapter->bSurpriseRemoved == true) ||
+ (pLed->padapter->bDriverStopped == true))
+ return;
+ schedule_work(&pLed->BlinkWorkItem);
+}
+
+/* Description:
+ * Callback function of LED BlinkWorkItem.
+ * We dispatch actual LED blink action according to LedStrategy.
+ */
+static void BlinkWorkItemCallback(struct work_struct *work)
+{
+ struct LED_871x *pLed = container_of(work, struct LED_871x,
+ BlinkWorkItem);
+ struct led_priv *ledpriv = &(pLed->padapter->ledpriv);
+
+ switch (ledpriv->LedStrategy) {
+ case SW_LED_MODE0:
+ SwLedBlink(pLed);
+ break;
+ case SW_LED_MODE1:
+ SwLedBlink1(pLed);
+ break;
+ case SW_LED_MODE2:
+ SwLedBlink2(pLed);
+ break;
+ case SW_LED_MODE3:
+ SwLedBlink3(pLed);
+ break;
+ case SW_LED_MODE4:
+ SwLedBlink4(pLed);
+ break;
+ case SW_LED_MODE5:
+ SwLedBlink5(pLed);
+ break;
+ case SW_LED_MODE6:
+ SwLedBlink6(pLed);
+ break;
+ default:
+ SwLedBlink(pLed);
+ break;
+ }
+}
+
+/*============================================================================
+ * Default LED behavior.
+ *============================================================================
+ *
+ * Description:
+ * Implement each led action for SW_LED_MODE0.
+ * This is default strategy.
+ */
+
+static void SwLedControlMode1(struct _adapter *padapter,
+ enum LED_CTL_MODE LedAction)
+{
+ struct led_priv *ledpriv = &(padapter->ledpriv);
+ struct LED_871x *pLed = &(ledpriv->SwLed0);
+ struct mlme_priv *pmlmepriv = &(padapter->mlmepriv);
+ struct sitesurvey_ctrl *psitesurveyctrl = &(pmlmepriv->sitesurveyctrl);
+
+ if (padapter->eeprompriv.CustomerID == RT_CID_819x_CAMEO)
+ pLed = &(ledpriv->SwLed1);
+ switch (LedAction) {
+ case LED_CTL_START_TO_LINK:
+ case LED_CTL_NO_LINK:
+ if (pLed->bLedNoLinkBlinkInProgress == false) {
+ if (pLed->CurrLedState == LED_SCAN_BLINK ||
+ IS_LED_WPS_BLINKING(pLed))
+ return;
+ if (pLed->bLedLinkBlinkInProgress == true) {
+ del_timer(&pLed->BlinkTimer);
+ pLed->bLedLinkBlinkInProgress = false;
+ }
+ if (pLed->bLedBlinkInProgress == true) {
+ del_timer(&pLed->BlinkTimer);
+ pLed->bLedBlinkInProgress = false;
+ }
+ pLed->bLedNoLinkBlinkInProgress = true;
+ pLed->CurrLedState = LED_BLINK_SLOWLY;
+ if (pLed->bLedOn)
+ pLed->BlinkingLedState = LED_OFF;
+ else
+ pLed->BlinkingLedState = LED_ON;
+ mod_timer(&pLed->BlinkTimer, jiffies +
+ msecs_to_jiffies(LED_BLINK_NO_LINK_INTERVAL_ALPHA));
+ }
+ break;
+ case LED_CTL_LINK:
+ if (pLed->bLedLinkBlinkInProgress == false) {
+ if (pLed->CurrLedState == LED_SCAN_BLINK ||
+ IS_LED_WPS_BLINKING(pLed))
+ return;
+ if (pLed->bLedNoLinkBlinkInProgress == true) {
+ del_timer(&pLed->BlinkTimer);
+ pLed->bLedNoLinkBlinkInProgress = false;
+ }
+ if (pLed->bLedBlinkInProgress == true) {
+ del_timer(&pLed->BlinkTimer);
+ pLed->bLedBlinkInProgress = false;
+ }
+ pLed->bLedLinkBlinkInProgress = true;
+ pLed->CurrLedState = LED_BLINK_NORMAL;
+ if (pLed->bLedOn)
+ pLed->BlinkingLedState = LED_OFF;
+ else
+ pLed->BlinkingLedState = LED_ON;
+ mod_timer(&pLed->BlinkTimer, jiffies +
+ msecs_to_jiffies(LED_BLINK_LINK_INTERVAL_ALPHA));
+ }
+ break;
+ case LED_CTL_SITE_SURVEY:
+ if ((psitesurveyctrl->traffic_busy) &&
+ (check_fwstate(pmlmepriv, _FW_LINKED) == true))
+ ; /* dummy branch */
+ else if (pLed->bLedScanBlinkInProgress == false) {
+ if (IS_LED_WPS_BLINKING(pLed))
+ return;
+ if (pLed->bLedNoLinkBlinkInProgress == true) {
+ del_timer(&pLed->BlinkTimer);
+ pLed->bLedNoLinkBlinkInProgress = false;
+ }
+ if (pLed->bLedLinkBlinkInProgress == true) {
+ del_timer(&pLed->BlinkTimer);
+ pLed->bLedLinkBlinkInProgress = false;
+ }
+ if (pLed->bLedBlinkInProgress == true) {
+ del_timer(&pLed->BlinkTimer);
+ pLed->bLedBlinkInProgress = false;
+ }
+ pLed->bLedScanBlinkInProgress = true;
+ pLed->CurrLedState = LED_SCAN_BLINK;
+ pLed->BlinkTimes = 24;
+ if (pLed->bLedOn)
+ pLed->BlinkingLedState = LED_OFF;
+ else
+ pLed->BlinkingLedState = LED_ON;
+ mod_timer(&pLed->BlinkTimer, jiffies +
+ msecs_to_jiffies(LED_BLINK_SCAN_INTERVAL_ALPHA));
+ }
+ break;
+ case LED_CTL_TX:
+ case LED_CTL_RX:
+ if (pLed->bLedBlinkInProgress == false) {
+ if (pLed->CurrLedState == LED_SCAN_BLINK ||
+ IS_LED_WPS_BLINKING(pLed))
+ return;
+ if (pLed->bLedNoLinkBlinkInProgress == true) {
+ del_timer(&pLed->BlinkTimer);
+ pLed->bLedNoLinkBlinkInProgress = false;
+ }
+ if (pLed->bLedLinkBlinkInProgress == true) {
+ del_timer(&pLed->BlinkTimer);
+ pLed->bLedLinkBlinkInProgress = false;
+ }
+ pLed->bLedBlinkInProgress = true;
+ pLed->CurrLedState = LED_TXRX_BLINK;
+ pLed->BlinkTimes = 2;
+ if (pLed->bLedOn)
+ pLed->BlinkingLedState = LED_OFF;
+ else
+ pLed->BlinkingLedState = LED_ON;
+ mod_timer(&pLed->BlinkTimer, jiffies +
+ msecs_to_jiffies(LED_BLINK_FASTER_INTERVAL_ALPHA));
+ }
+ break;
+
+ case LED_CTL_START_WPS: /*wait until xinpin finish */
+ case LED_CTL_START_WPS_BOTTON:
+ if (pLed->bLedWPSBlinkInProgress == false) {
+ if (pLed->bLedNoLinkBlinkInProgress == true) {
+ del_timer(&pLed->BlinkTimer);
+ pLed->bLedNoLinkBlinkInProgress = false;
+ }
+ if (pLed->bLedLinkBlinkInProgress == true) {
+ del_timer(&pLed->BlinkTimer);
+ pLed->bLedLinkBlinkInProgress = false;
+ }
+ if (pLed->bLedBlinkInProgress == true) {
+ del_timer(&pLed->BlinkTimer);
+ pLed->bLedBlinkInProgress = false;
+ }
+ if (pLed->bLedScanBlinkInProgress == true) {
+ del_timer(&pLed->BlinkTimer);
+ pLed->bLedScanBlinkInProgress = false;
+ }
+ pLed->bLedWPSBlinkInProgress = true;
+ pLed->CurrLedState = LED_BLINK_WPS;
+ if (pLed->bLedOn)
+ pLed->BlinkingLedState = LED_OFF;
+ else
+ pLed->BlinkingLedState = LED_ON;
+ mod_timer(&pLed->BlinkTimer, jiffies +
+ msecs_to_jiffies(LED_BLINK_SCAN_INTERVAL_ALPHA));
+ }
+ break;
+ case LED_CTL_STOP_WPS:
+ if (pLed->bLedNoLinkBlinkInProgress == true) {
+ del_timer(&pLed->BlinkTimer);
+ pLed->bLedNoLinkBlinkInProgress = false;
+ }
+ if (pLed->bLedLinkBlinkInProgress == true) {
+ del_timer(&pLed->BlinkTimer);
+ pLed->bLedLinkBlinkInProgress = false;
+ }
+ if (pLed->bLedBlinkInProgress == true) {
+ del_timer(&pLed->BlinkTimer);
+ pLed->bLedBlinkInProgress = false;
+ }
+ if (pLed->bLedScanBlinkInProgress == true) {
+ del_timer(&pLed->BlinkTimer);
+ pLed->bLedScanBlinkInProgress = false;
+ }
+ if (pLed->bLedWPSBlinkInProgress)
+ del_timer(&pLed->BlinkTimer);
+ else
+ pLed->bLedWPSBlinkInProgress = true;
+ pLed->CurrLedState = LED_BLINK_WPS_STOP;
+ if (pLed->bLedOn) {
+ pLed->BlinkingLedState = LED_OFF;
+ mod_timer(&pLed->BlinkTimer, jiffies +
+ msecs_to_jiffies(LED_BLINK_WPS_SUCESS_INTERVAL_ALPHA));
+ } else {
+ pLed->BlinkingLedState = LED_ON;
+ mod_timer(&pLed->BlinkTimer,
+ jiffies + msecs_to_jiffies(0));
+ }
+ break;
+ case LED_CTL_STOP_WPS_FAIL:
+ if (pLed->bLedWPSBlinkInProgress) {
+ del_timer(&pLed->BlinkTimer);
+ pLed->bLedWPSBlinkInProgress = false;
+ }
+ pLed->bLedNoLinkBlinkInProgress = true;
+ pLed->CurrLedState = LED_BLINK_SLOWLY;
+ if (pLed->bLedOn)
+ pLed->BlinkingLedState = LED_OFF;
+ else
+ pLed->BlinkingLedState = LED_ON;
+ mod_timer(&pLed->BlinkTimer, jiffies +
+ msecs_to_jiffies(LED_BLINK_NO_LINK_INTERVAL_ALPHA));
+ break;
+ case LED_CTL_POWER_OFF:
+ pLed->CurrLedState = LED_OFF;
+ pLed->BlinkingLedState = LED_OFF;
+ if (pLed->bLedNoLinkBlinkInProgress) {
+ del_timer(&pLed->BlinkTimer);
+ pLed->bLedNoLinkBlinkInProgress = false;
+ }
+ if (pLed->bLedLinkBlinkInProgress) {
+ del_timer(&pLed->BlinkTimer);
+ pLed->bLedLinkBlinkInProgress = false;
+ }
+ if (pLed->bLedBlinkInProgress) {
+ del_timer(&pLed->BlinkTimer);
+ pLed->bLedBlinkInProgress = false;
+ }
+ if (pLed->bLedWPSBlinkInProgress) {
+ del_timer(&pLed->BlinkTimer);
+ pLed->bLedWPSBlinkInProgress = false;
+ }
+ if (pLed->bLedScanBlinkInProgress) {
+ del_timer(&pLed->BlinkTimer);
+ pLed->bLedScanBlinkInProgress = false;
+ }
+ mod_timer(&pLed->BlinkTimer,
+ jiffies + msecs_to_jiffies(0));
+ break;
+ default:
+ break;
+ }
+}
+
+static void SwLedControlMode2(struct _adapter *padapter,
+ enum LED_CTL_MODE LedAction)
+{
+ struct led_priv *ledpriv = &(padapter->ledpriv);
+ struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
+ struct LED_871x *pLed = &(ledpriv->SwLed0);
+
+ switch (LedAction) {
+ case LED_CTL_SITE_SURVEY:
+ if (pmlmepriv->sitesurveyctrl.traffic_busy)
+ ; /* dummy branch */
+ else if (pLed->bLedScanBlinkInProgress == false) {
+ if (IS_LED_WPS_BLINKING(pLed))
+ return;
+
+ if (pLed->bLedBlinkInProgress == true) {
+ del_timer(&pLed->BlinkTimer);
+ pLed->bLedBlinkInProgress = false;
+ }
+ pLed->bLedScanBlinkInProgress = true;
+ pLed->CurrLedState = LED_SCAN_BLINK;
+ pLed->BlinkTimes = 24;
+ if (pLed->bLedOn)
+ pLed->BlinkingLedState = LED_OFF;
+ else
+ pLed->BlinkingLedState = LED_ON;
+ mod_timer(&pLed->BlinkTimer, jiffies +
+ msecs_to_jiffies(LED_BLINK_SCAN_INTERVAL_ALPHA));
+ }
+ break;
+
+ case LED_CTL_TX:
+ case LED_CTL_RX:
+ if ((pLed->bLedBlinkInProgress == false) &&
+ (check_fwstate(pmlmepriv, _FW_LINKED) == true)) {
+ if (pLed->CurrLedState == LED_SCAN_BLINK ||
+ IS_LED_WPS_BLINKING(pLed))
+ return;
+ pLed->bLedBlinkInProgress = true;
+ pLed->CurrLedState = LED_TXRX_BLINK;
+ pLed->BlinkTimes = 2;
+ if (pLed->bLedOn)
+ pLed->BlinkingLedState = LED_OFF;
+ else
+ pLed->BlinkingLedState = LED_ON;
+ mod_timer(&pLed->BlinkTimer, jiffies +
+ msecs_to_jiffies(LED_BLINK_FASTER_INTERVAL_ALPHA));
+ }
+ break;
+
+ case LED_CTL_LINK:
+ pLed->CurrLedState = LED_ON;
+ pLed->BlinkingLedState = LED_ON;
+ if (pLed->bLedBlinkInProgress) {
+ del_timer(&pLed->BlinkTimer);
+ pLed->bLedBlinkInProgress = false;
+ }
+ if (pLed->bLedScanBlinkInProgress) {
+ del_timer(&pLed->BlinkTimer);
+ pLed->bLedScanBlinkInProgress = false;
+ }
+
+ mod_timer(&pLed->BlinkTimer,
+ jiffies + msecs_to_jiffies(0));
+ break;
+
+ case LED_CTL_START_WPS: /*wait until xinpin finish*/
+ case LED_CTL_START_WPS_BOTTON:
+ if (pLed->bLedWPSBlinkInProgress == false) {
+ if (pLed->bLedBlinkInProgress == true) {
+ del_timer(&pLed->BlinkTimer);
+ pLed->bLedBlinkInProgress = false;
+ }
+ if (pLed->bLedScanBlinkInProgress == true) {
+ del_timer(&pLed->BlinkTimer);
+ pLed->bLedScanBlinkInProgress = false;
+ }
+ pLed->bLedWPSBlinkInProgress = true;
+ pLed->CurrLedState = LED_ON;
+ pLed->BlinkingLedState = LED_ON;
+ mod_timer(&pLed->BlinkTimer,
+ jiffies + msecs_to_jiffies(0));
+ }
+ break;
+
+ case LED_CTL_STOP_WPS:
+ pLed->bLedWPSBlinkInProgress = false;
+ pLed->CurrLedState = LED_ON;
+ pLed->BlinkingLedState = LED_ON;
+ mod_timer(&pLed->BlinkTimer,
+ jiffies + msecs_to_jiffies(0));
+ break;
+
+ case LED_CTL_STOP_WPS_FAIL:
+ pLed->bLedWPSBlinkInProgress = false;
+ pLed->CurrLedState = LED_OFF;
+ pLed->BlinkingLedState = LED_OFF;
+ mod_timer(&pLed->BlinkTimer,
+ jiffies + msecs_to_jiffies(0));
+ break;
+
+ case LED_CTL_START_TO_LINK:
+ case LED_CTL_NO_LINK:
+ if (!IS_LED_BLINKING(pLed)) {
+ pLed->CurrLedState = LED_OFF;
+ pLed->BlinkingLedState = LED_OFF;
+ mod_timer(&pLed->BlinkTimer,
+ jiffies + msecs_to_jiffies(0));
+ }
+ break;
+ case LED_CTL_POWER_OFF:
+ pLed->CurrLedState = LED_OFF;
+ pLed->BlinkingLedState = LED_OFF;
+ if (pLed->bLedBlinkInProgress) {
+ del_timer(&pLed->BlinkTimer);
+ pLed->bLedBlinkInProgress = false;
+ }
+ if (pLed->bLedScanBlinkInProgress) {
+ del_timer(&pLed->BlinkTimer);
+ pLed->bLedScanBlinkInProgress = false;
+ }
+ if (pLed->bLedWPSBlinkInProgress) {
+ del_timer(&pLed->BlinkTimer);
+ pLed->bLedWPSBlinkInProgress = false;
+ }
+ mod_timer(&pLed->BlinkTimer,
+ jiffies + msecs_to_jiffies(0));
+ break;
+ default:
+ break;
+ }
+}
+
+static void SwLedControlMode3(struct _adapter *padapter,
+ enum LED_CTL_MODE LedAction)
+{
+ struct led_priv *ledpriv = &(padapter->ledpriv);
+ struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
+ struct LED_871x *pLed = &(ledpriv->SwLed0);
+
+ switch (LedAction) {
+ case LED_CTL_SITE_SURVEY:
+ if (pmlmepriv->sitesurveyctrl.traffic_busy)
+ ; /* dummy branch */
+ else if (pLed->bLedScanBlinkInProgress == false) {
+ if (IS_LED_WPS_BLINKING(pLed))
+ return;
+ if (pLed->bLedBlinkInProgress == true) {
+ del_timer(&pLed->BlinkTimer);
+ pLed->bLedBlinkInProgress = false;
+ }
+ pLed->bLedScanBlinkInProgress = true;
+ pLed->CurrLedState = LED_SCAN_BLINK;
+ pLed->BlinkTimes = 24;
+ if (pLed->bLedOn)
+ pLed->BlinkingLedState = LED_OFF;
+ else
+ pLed->BlinkingLedState = LED_ON;
+ mod_timer(&pLed->BlinkTimer, jiffies +
+ msecs_to_jiffies(LED_BLINK_SCAN_INTERVAL_ALPHA));
+ }
+ break;
+ case LED_CTL_TX:
+ case LED_CTL_RX:
+ if ((pLed->bLedBlinkInProgress == false) &&
+ (check_fwstate(pmlmepriv, _FW_LINKED) == true)) {
+ if (pLed->CurrLedState == LED_SCAN_BLINK ||
+ IS_LED_WPS_BLINKING(pLed))
+ return;
+ pLed->bLedBlinkInProgress = true;
+ pLed->CurrLedState = LED_TXRX_BLINK;
+ pLed->BlinkTimes = 2;
+ if (pLed->bLedOn)
+ pLed->BlinkingLedState = LED_OFF;
+ else
+ pLed->BlinkingLedState = LED_ON;
+ mod_timer(&pLed->BlinkTimer, jiffies +
+ msecs_to_jiffies(LED_BLINK_FASTER_INTERVAL_ALPHA));
+ }
+ break;
+ case LED_CTL_LINK:
+ if (IS_LED_WPS_BLINKING(pLed))
+ return;
+ pLed->CurrLedState = LED_ON;
+ pLed->BlinkingLedState = LED_ON;
+ if (pLed->bLedBlinkInProgress) {
+ del_timer(&pLed->BlinkTimer);
+ pLed->bLedBlinkInProgress = false;
+ }
+ if (pLed->bLedScanBlinkInProgress) {
+ del_timer(&pLed->BlinkTimer);
+ pLed->bLedScanBlinkInProgress = false;
+ }
+ mod_timer(&pLed->BlinkTimer,
+ jiffies + msecs_to_jiffies(0));
+ break;
+ case LED_CTL_START_WPS: /* wait until xinpin finish */
+ case LED_CTL_START_WPS_BOTTON:
+ if (pLed->bLedWPSBlinkInProgress == false) {
+ if (pLed->bLedBlinkInProgress == true) {
+ del_timer(&pLed->BlinkTimer);
+ pLed->bLedBlinkInProgress = false;
+ }
+ if (pLed->bLedScanBlinkInProgress == true) {
+ del_timer(&pLed->BlinkTimer);
+ pLed->bLedScanBlinkInProgress = false;
+ }
+ pLed->bLedWPSBlinkInProgress = true;
+ pLed->CurrLedState = LED_BLINK_WPS;
+ if (pLed->bLedOn)
+ pLed->BlinkingLedState = LED_OFF;
+ else
+ pLed->BlinkingLedState = LED_ON;
+ mod_timer(&pLed->BlinkTimer, jiffies +
+ msecs_to_jiffies(LED_BLINK_SCAN_INTERVAL_ALPHA));
+ }
+ break;
+ case LED_CTL_STOP_WPS:
+ if (pLed->bLedWPSBlinkInProgress) {
+ del_timer(&pLed->BlinkTimer);
+ pLed->bLedWPSBlinkInProgress = false;
+ } else
+ pLed->bLedWPSBlinkInProgress = true;
+ pLed->CurrLedState = LED_BLINK_WPS_STOP;
+ if (pLed->bLedOn) {
+ pLed->BlinkingLedState = LED_OFF;
+ mod_timer(&pLed->BlinkTimer, jiffies +
+ msecs_to_jiffies(LED_BLINK_WPS_SUCESS_INTERVAL_ALPHA));
+ } else {
+ pLed->BlinkingLedState = LED_ON;
+ mod_timer(&pLed->BlinkTimer,
+ jiffies + msecs_to_jiffies(0));
+ }
+ break;
+ case LED_CTL_STOP_WPS_FAIL:
+ if (pLed->bLedWPSBlinkInProgress) {
+ del_timer(&pLed->BlinkTimer);
+ pLed->bLedWPSBlinkInProgress = false;
+ }
+ pLed->CurrLedState = LED_OFF;
+ pLed->BlinkingLedState = LED_OFF;
+ mod_timer(&pLed->BlinkTimer,
+ jiffies + msecs_to_jiffies(0));
+ break;
+ case LED_CTL_START_TO_LINK:
+ case LED_CTL_NO_LINK:
+ if (!IS_LED_BLINKING(pLed)) {
+ pLed->CurrLedState = LED_OFF;
+ pLed->BlinkingLedState = LED_OFF;
+ mod_timer(&pLed->BlinkTimer,
+ jiffies + msecs_to_jiffies(0));
+ }
+ break;
+ case LED_CTL_POWER_OFF:
+ pLed->CurrLedState = LED_OFF;
+ pLed->BlinkingLedState = LED_OFF;
+ if (pLed->bLedBlinkInProgress) {
+ del_timer(&pLed->BlinkTimer);
+ pLed->bLedBlinkInProgress = false;
+ }
+ if (pLed->bLedScanBlinkInProgress) {
+ del_timer(&pLed->BlinkTimer);
+ pLed->bLedScanBlinkInProgress = false;
+ }
+ if (pLed->bLedWPSBlinkInProgress) {
+ del_timer(&pLed->BlinkTimer);
+ pLed->bLedWPSBlinkInProgress = false;
+ }
+ mod_timer(&pLed->BlinkTimer,
+ jiffies + msecs_to_jiffies(0));
+ break;
+ default:
+ break;
+ }
+}
+
+static void SwLedControlMode4(struct _adapter *padapter,
+ enum LED_CTL_MODE LedAction)
+{
+ struct led_priv *ledpriv = &(padapter->ledpriv);
+ struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
+ struct LED_871x *pLed = &(ledpriv->SwLed0);
+ struct LED_871x *pLed1 = &(ledpriv->SwLed1);
+
+ switch (LedAction) {
+ case LED_CTL_START_TO_LINK:
+ if (pLed1->bLedWPSBlinkInProgress) {
+ pLed1->bLedWPSBlinkInProgress = false;
+ del_timer(&pLed1->BlinkTimer);
+ pLed1->BlinkingLedState = LED_OFF;
+ pLed1->CurrLedState = LED_OFF;
+ if (pLed1->bLedOn)
+ mod_timer(&pLed->BlinkTimer,
+ jiffies + msecs_to_jiffies(0));
+ }
+ if (pLed->bLedStartToLinkBlinkInProgress == false) {
+ if (pLed->CurrLedState == LED_SCAN_BLINK ||
+ IS_LED_WPS_BLINKING(pLed))
+ return;
+ if (pLed->bLedBlinkInProgress == true) {
+ del_timer(&pLed->BlinkTimer);
+ pLed->bLedBlinkInProgress = false;
+ }
+ if (pLed->bLedNoLinkBlinkInProgress == true) {
+ del_timer(&pLed->BlinkTimer);
+ pLed->bLedNoLinkBlinkInProgress = false;
+ }
+ pLed->bLedStartToLinkBlinkInProgress = true;
+ pLed->CurrLedState = LED_BLINK_StartToBlink;
+ if (pLed->bLedOn) {
+ pLed->BlinkingLedState = LED_OFF;
+ mod_timer(&pLed->BlinkTimer, jiffies +
+ msecs_to_jiffies(LED_BLINK_SLOWLY_INTERVAL));
+ } else {
+ pLed->BlinkingLedState = LED_ON;
+ mod_timer(&pLed->BlinkTimer, jiffies +
+ msecs_to_jiffies(LED_BLINK_NORMAL_INTERVAL));
+ }
+ }
+ break;
+ case LED_CTL_LINK:
+ case LED_CTL_NO_LINK:
+ /*LED1 settings*/
+ if (LedAction == LED_CTL_LINK) {
+ if (pLed1->bLedWPSBlinkInProgress) {
+ pLed1->bLedWPSBlinkInProgress = false;
+ del_timer(&pLed1->BlinkTimer);
+ pLed1->BlinkingLedState = LED_OFF;
+ pLed1->CurrLedState = LED_OFF;
+ if (pLed1->bLedOn)
+ mod_timer(&pLed->BlinkTimer,
+ jiffies + msecs_to_jiffies(0));
+ }
+ }
+ if (pLed->bLedNoLinkBlinkInProgress == false) {
+ if (pLed->CurrLedState == LED_SCAN_BLINK ||
+ IS_LED_WPS_BLINKING(pLed))
+ return;
+ if (pLed->bLedBlinkInProgress == true) {
+ del_timer(&pLed->BlinkTimer);
+ pLed->bLedBlinkInProgress = false;
+ }
+ pLed->bLedNoLinkBlinkInProgress = true;
+ pLed->CurrLedState = LED_BLINK_SLOWLY;
+ if (pLed->bLedOn)
+ pLed->BlinkingLedState = LED_OFF;
+ else
+ pLed->BlinkingLedState = LED_ON;
+ mod_timer(&pLed->BlinkTimer, jiffies +
+ msecs_to_jiffies(LED_BLINK_NO_LINK_INTERVAL_ALPHA));
+ }
+ break;
+ case LED_CTL_SITE_SURVEY:
+ if ((pmlmepriv->sitesurveyctrl.traffic_busy) &&
+ (check_fwstate(pmlmepriv, _FW_LINKED) == true))
+ ;
+ else if (pLed->bLedScanBlinkInProgress == false) {
+ if (IS_LED_WPS_BLINKING(pLed))
+ return;
+ if (pLed->bLedNoLinkBlinkInProgress == true) {
+ del_timer(&pLed->BlinkTimer);
+ pLed->bLedNoLinkBlinkInProgress = false;
+ }
+ if (pLed->bLedBlinkInProgress == true) {
+ del_timer(&pLed->BlinkTimer);
+ pLed->bLedBlinkInProgress = false;
+ }
+ pLed->bLedScanBlinkInProgress = true;
+ pLed->CurrLedState = LED_SCAN_BLINK;
+ pLed->BlinkTimes = 24;
+ if (pLed->bLedOn)
+ pLed->BlinkingLedState = LED_OFF;
+ else
+ pLed->BlinkingLedState = LED_ON;
+ mod_timer(&pLed->BlinkTimer, jiffies +
+ msecs_to_jiffies(LED_BLINK_SCAN_INTERVAL_ALPHA));
+ }
+ break;
+ case LED_CTL_TX:
+ case LED_CTL_RX:
+ if (pLed->bLedBlinkInProgress == false) {
+ if (pLed->CurrLedState == LED_SCAN_BLINK ||
+ IS_LED_WPS_BLINKING(pLed))
+ return;
+ if (pLed->bLedNoLinkBlinkInProgress == true) {
+ del_timer(&pLed->BlinkTimer);
+ pLed->bLedNoLinkBlinkInProgress = false;
+ }
+ pLed->bLedBlinkInProgress = true;
+ pLed->CurrLedState = LED_TXRX_BLINK;
+ pLed->BlinkTimes = 2;
+ if (pLed->bLedOn)
+ pLed->BlinkingLedState = LED_OFF;
+ else
+ pLed->BlinkingLedState = LED_ON;
+ mod_timer(&pLed->BlinkTimer, jiffies +
+ msecs_to_jiffies(LED_BLINK_FASTER_INTERVAL_ALPHA));
+ }
+ break;
+ case LED_CTL_START_WPS: /*wait until xinpin finish*/
+ case LED_CTL_START_WPS_BOTTON:
+ if (pLed1->bLedWPSBlinkInProgress) {
+ pLed1->bLedWPSBlinkInProgress = false;
+ del_timer(&pLed1->BlinkTimer);
+ pLed1->BlinkingLedState = LED_OFF;
+ pLed1->CurrLedState = LED_OFF;
+ if (pLed1->bLedOn)
+ mod_timer(&pLed->BlinkTimer,
+ jiffies + msecs_to_jiffies(0));
+ }
+ if (pLed->bLedWPSBlinkInProgress == false) {
+ if (pLed->bLedNoLinkBlinkInProgress == true) {
+ del_timer(&pLed->BlinkTimer);
+ pLed->bLedNoLinkBlinkInProgress = false;
+ }
+ if (pLed->bLedBlinkInProgress == true) {
+ del_timer(&pLed->BlinkTimer);
+ pLed->bLedBlinkInProgress = false;
+ }
+ if (pLed->bLedScanBlinkInProgress == true) {
+ del_timer(&pLed->BlinkTimer);
+ pLed->bLedScanBlinkInProgress = false;
+ }
+ pLed->bLedWPSBlinkInProgress = true;
+ pLed->CurrLedState = LED_BLINK_WPS;
+ if (pLed->bLedOn) {
+ pLed->BlinkingLedState = LED_OFF;
+ mod_timer(&pLed->BlinkTimer, jiffies +
+ msecs_to_jiffies(LED_BLINK_SLOWLY_INTERVAL));
+ } else {
+ pLed->BlinkingLedState = LED_ON;
+ mod_timer(&pLed->BlinkTimer, jiffies +
+ msecs_to_jiffies(LED_BLINK_NORMAL_INTERVAL));
+ }
+ }
+ break;
+ case LED_CTL_STOP_WPS: /*WPS connect success*/
+ if (pLed->bLedWPSBlinkInProgress) {
+ del_timer(&pLed->BlinkTimer);
+ pLed->bLedWPSBlinkInProgress = false;
+ }
+ pLed->bLedNoLinkBlinkInProgress = true;
+ pLed->CurrLedState = LED_BLINK_SLOWLY;
+ if (pLed->bLedOn)
+ pLed->BlinkingLedState = LED_OFF;
+ else
+ pLed->BlinkingLedState = LED_ON;
+ mod_timer(&pLed->BlinkTimer, jiffies +
+ msecs_to_jiffies(LED_BLINK_NO_LINK_INTERVAL_ALPHA));
+ break;
+ case LED_CTL_STOP_WPS_FAIL: /*WPS authentication fail*/
+ if (pLed->bLedWPSBlinkInProgress) {
+ del_timer(&pLed->BlinkTimer);
+ pLed->bLedWPSBlinkInProgress = false;
+ }
+ pLed->bLedNoLinkBlinkInProgress = true;
+ pLed->CurrLedState = LED_BLINK_SLOWLY;
+ if (pLed->bLedOn)
+ pLed->BlinkingLedState = LED_OFF;
+ else
+ pLed->BlinkingLedState = LED_ON;
+ mod_timer(&pLed->BlinkTimer, jiffies +
+ msecs_to_jiffies(LED_BLINK_NO_LINK_INTERVAL_ALPHA));
+ /*LED1 settings*/
+ if (pLed1->bLedWPSBlinkInProgress)
+ del_timer(&pLed1->BlinkTimer);
+ else
+ pLed1->bLedWPSBlinkInProgress = true;
+ pLed1->CurrLedState = LED_BLINK_WPS_STOP;
+ if (pLed1->bLedOn)
+ pLed1->BlinkingLedState = LED_OFF;
+ else
+ pLed1->BlinkingLedState = LED_ON;
+ mod_timer(&pLed->BlinkTimer, jiffies +
+ msecs_to_jiffies(LED_BLINK_NORMAL_INTERVAL));
+ break;
+ case LED_CTL_STOP_WPS_FAIL_OVERLAP: /*WPS session overlap*/
+ if (pLed->bLedWPSBlinkInProgress) {
+ del_timer(&pLed->BlinkTimer);
+ pLed->bLedWPSBlinkInProgress = false;
+ }
+ pLed->bLedNoLinkBlinkInProgress = true;
+ pLed->CurrLedState = LED_BLINK_SLOWLY;
+ if (pLed->bLedOn)
+ pLed->BlinkingLedState = LED_OFF;
+ else
+ pLed->BlinkingLedState = LED_ON;
+ mod_timer(&pLed->BlinkTimer, jiffies +
+ msecs_to_jiffies(LED_BLINK_NO_LINK_INTERVAL_ALPHA));
+ /*LED1 settings*/
+ if (pLed1->bLedWPSBlinkInProgress)
+ del_timer(&pLed1->BlinkTimer);
+ else
+ pLed1->bLedWPSBlinkInProgress = true;
+ pLed1->CurrLedState = LED_BLINK_WPS_STOP_OVERLAP;
+ pLed1->BlinkTimes = 10;
+ if (pLed1->bLedOn)
+ pLed1->BlinkingLedState = LED_OFF;
+ else
+ pLed1->BlinkingLedState = LED_ON;
+ mod_timer(&pLed->BlinkTimer, jiffies +
+ msecs_to_jiffies(LED_BLINK_NORMAL_INTERVAL));
+ break;
+ case LED_CTL_POWER_OFF:
+ pLed->CurrLedState = LED_OFF;
+ pLed->BlinkingLedState = LED_OFF;
+ if (pLed->bLedNoLinkBlinkInProgress) {
+ del_timer(&pLed->BlinkTimer);
+ pLed->bLedNoLinkBlinkInProgress = false;
+ }
+ if (pLed->bLedLinkBlinkInProgress) {
+ del_timer(&pLed->BlinkTimer);
+ pLed->bLedLinkBlinkInProgress = false;
+ }
+ if (pLed->bLedBlinkInProgress) {
+ del_timer(&pLed->BlinkTimer);
+ pLed->bLedBlinkInProgress = false;
+ }
+ if (pLed->bLedWPSBlinkInProgress) {
+ del_timer(&pLed->BlinkTimer);
+ pLed->bLedWPSBlinkInProgress = false;
+ }
+ if (pLed->bLedScanBlinkInProgress) {
+ del_timer(&pLed->BlinkTimer);
+ pLed->bLedScanBlinkInProgress = false;
+ }
+ if (pLed->bLedStartToLinkBlinkInProgress) {
+ del_timer(&pLed->BlinkTimer);
+ pLed->bLedStartToLinkBlinkInProgress = false;
+ }
+ if (pLed1->bLedWPSBlinkInProgress) {
+ del_timer(&pLed1->BlinkTimer);
+ pLed1->bLedWPSBlinkInProgress = false;
+ }
+ pLed1->BlinkingLedState = LED_UNKNOWN;
+ SwLedOff(padapter, pLed);
+ SwLedOff(padapter, pLed1);
+ break;
+ default:
+ break;
+ }
+}
+
+static void SwLedControlMode5(struct _adapter *padapter,
+ enum LED_CTL_MODE LedAction)
+{
+ struct led_priv *ledpriv = &(padapter->ledpriv);
+ struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
+ struct LED_871x *pLed = &(ledpriv->SwLed0);
+
+ if (padapter->eeprompriv.CustomerID == RT_CID_819x_CAMEO)
+ pLed = &(ledpriv->SwLed1);
+
+ switch (LedAction) {
+ case LED_CTL_POWER_ON:
+ case LED_CTL_NO_LINK:
+ case LED_CTL_LINK: /* solid blue */
+ if (pLed->CurrLedState == LED_SCAN_BLINK)
+ return;
+ pLed->CurrLedState = LED_ON;
+ pLed->BlinkingLedState = LED_ON;
+ pLed->bLedBlinkInProgress = false;
+ mod_timer(&pLed->BlinkTimer,
+ jiffies + msecs_to_jiffies(0));
+ break;
+ case LED_CTL_SITE_SURVEY:
+ if ((pmlmepriv->sitesurveyctrl.traffic_busy) &&
+ (check_fwstate(pmlmepriv, _FW_LINKED) == true))
+ ; /* dummy branch */
+ else if (pLed->bLedScanBlinkInProgress == false) {
+ if (pLed->bLedBlinkInProgress == true) {
+ del_timer(&pLed->BlinkTimer);
+ pLed->bLedBlinkInProgress = false;
+ }
+ pLed->bLedScanBlinkInProgress = true;
+ pLed->CurrLedState = LED_SCAN_BLINK;
+ pLed->BlinkTimes = 24;
+ if (pLed->bLedOn)
+ pLed->BlinkingLedState = LED_OFF;
+ else
+ pLed->BlinkingLedState = LED_ON;
+ mod_timer(&pLed->BlinkTimer, jiffies +
+ msecs_to_jiffies(LED_BLINK_SCAN_INTERVAL_ALPHA));
+ }
+ break;
+ case LED_CTL_TX:
+ case LED_CTL_RX:
+ if (pLed->bLedBlinkInProgress == false) {
+ if (pLed->CurrLedState == LED_SCAN_BLINK)
+ return;
+ pLed->bLedBlinkInProgress = true;
+ pLed->CurrLedState = LED_TXRX_BLINK;
+ pLed->BlinkTimes = 2;
+ if (pLed->bLedOn)
+ pLed->BlinkingLedState = LED_OFF;
+ else
+ pLed->BlinkingLedState = LED_ON;
+ mod_timer(&pLed->BlinkTimer, jiffies +
+ msecs_to_jiffies(LED_BLINK_FASTER_INTERVAL_ALPHA));
+ }
+ break;
+ case LED_CTL_POWER_OFF:
+ pLed->CurrLedState = LED_OFF;
+ pLed->BlinkingLedState = LED_OFF;
+ if (pLed->bLedBlinkInProgress) {
+ del_timer(&pLed->BlinkTimer);
+ pLed->bLedBlinkInProgress = false;
+ }
+ SwLedOff(padapter, pLed);
+ break;
+ default:
+ break;
+ }
+}
+
+
+static void SwLedControlMode6(struct _adapter *padapter,
+ enum LED_CTL_MODE LedAction)
+{
+ struct led_priv *ledpriv = &(padapter->ledpriv);
+ struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
+ struct LED_871x *pLed = &(ledpriv->SwLed0);
+
+ switch (LedAction) {
+ case LED_CTL_POWER_ON:
+ case LED_CTL_NO_LINK:
+ case LED_CTL_LINK: /*solid blue*/
+ case LED_CTL_SITE_SURVEY:
+ if (IS_LED_WPS_BLINKING(pLed))
+ return;
+ pLed->CurrLedState = LED_ON;
+ pLed->BlinkingLedState = LED_ON;
+ pLed->bLedBlinkInProgress = false;
+ mod_timer(&(pLed->BlinkTimer), jiffies + msecs_to_jiffies(0));
+ break;
+ case LED_CTL_TX:
+ case LED_CTL_RX:
+ if (pLed->bLedBlinkInProgress == false &&
+ (check_fwstate(pmlmepriv, _FW_LINKED) == true)) {
+ if (IS_LED_WPS_BLINKING(pLed))
+ return;
+ pLed->bLedBlinkInProgress = true;
+ pLed->CurrLedState = LED_TXRX_BLINK;
+ pLed->BlinkTimes = 2;
+ if (pLed->bLedOn)
+ pLed->BlinkingLedState = LED_OFF;
+ else
+ pLed->BlinkingLedState = LED_ON;
+ mod_timer(&pLed->BlinkTimer, jiffies +
+ msecs_to_jiffies(LED_BLINK_FASTER_INTERVAL_ALPHA));
+ }
+ break;
+ case LED_CTL_START_WPS: /*wait until xinpin finish*/
+ case LED_CTL_START_WPS_BOTTON:
+ if (pLed->bLedWPSBlinkInProgress == false) {
+ if (pLed->bLedBlinkInProgress == true) {
+ del_timer(&pLed->BlinkTimer);
+ pLed->bLedBlinkInProgress = false;
+ }
+ pLed->bLedWPSBlinkInProgress = true;
+ pLed->CurrLedState = LED_BLINK_WPS;
+ if (pLed->bLedOn)
+ pLed->BlinkingLedState = LED_OFF;
+ else
+ pLed->BlinkingLedState = LED_ON;
+ mod_timer(&pLed->BlinkTimer, jiffies +
+ msecs_to_jiffies(LED_BLINK_SCAN_INTERVAL_ALPHA));
+ }
+ break;
+ case LED_CTL_STOP_WPS_FAIL:
+ case LED_CTL_STOP_WPS:
+ if (pLed->bLedWPSBlinkInProgress) {
+ del_timer(&pLed->BlinkTimer);
+ pLed->bLedWPSBlinkInProgress = false;
+ }
+ pLed->CurrLedState = LED_ON;
+ pLed->BlinkingLedState = LED_ON;
+ mod_timer(&pLed->BlinkTimer,
+ jiffies + msecs_to_jiffies(0));
+ break;
+ case LED_CTL_POWER_OFF:
+ pLed->CurrLedState = LED_OFF;
+ pLed->BlinkingLedState = LED_OFF;
+ if (pLed->bLedBlinkInProgress) {
+ del_timer(&pLed->BlinkTimer);
+ pLed->bLedBlinkInProgress = false;
+ }
+ if (pLed->bLedWPSBlinkInProgress) {
+ del_timer(&pLed->BlinkTimer);
+ pLed->bLedWPSBlinkInProgress = false;
+ }
+ SwLedOff(padapter, pLed);
+ break;
+ default:
+ break;
+ }
+}
+
+/* Description:
+ * Dispatch LED action according to pHalData->LedStrategy.
+ */
+void LedControl871x(struct _adapter *padapter, enum LED_CTL_MODE LedAction)
+{
+ struct led_priv *ledpriv = &(padapter->ledpriv);
+
+ if (ledpriv->bRegUseLed == false)
+ return;
+ switch (ledpriv->LedStrategy) {
+ case SW_LED_MODE0:
+ break;
+ case SW_LED_MODE1:
+ SwLedControlMode1(padapter, LedAction);
+ break;
+ case SW_LED_MODE2:
+ SwLedControlMode2(padapter, LedAction);
+ break;
+ case SW_LED_MODE3:
+ SwLedControlMode3(padapter, LedAction);
+ break;
+ case SW_LED_MODE4:
+ SwLedControlMode4(padapter, LedAction);
+ break;
+ case SW_LED_MODE5:
+ SwLedControlMode5(padapter, LedAction);
+ break;
+ case SW_LED_MODE6:
+ SwLedControlMode6(padapter, LedAction);
+ break;
+ default:
+ break;
+ }
+}
diff --git a/drivers/staging/rtl8712/rtl8712_macsetting_bitdef.h b/drivers/staging/rtl8712/rtl8712_macsetting_bitdef.h
new file mode 100644
index 000000000..28e0a7ebc
--- /dev/null
+++ b/drivers/staging/rtl8712/rtl8712_macsetting_bitdef.h
@@ -0,0 +1,47 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2010 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ *
+ ******************************************************************************/
+#ifndef __RTL8712_MACSETTING_BITDEF_H__
+#define __RTL8712_MACSETTING_BITDEF_H__
+
+
+/*MACID*/
+/*BSSID*/
+
+/*HWVID*/
+#define _HWVID_MSK 0x0F
+
+/*MAR*/
+/*MBIDCANCONTENT*/
+
+/*MBIDCANCFG*/
+#define _POOLING BIT(31)
+#define _WRITE_EN BIT(16)
+#define _CAM_ADDR_MSK 0x001F
+#define _CAM_ADDR_SHT 0
+
+/*BUILDTIME*/
+#define _BUILDTIME_MSK 0x3FFFFFFF
+
+/*BUILDUSER*/
+
+
+
+#endif /* __RTL8712_MACSETTING_BITDEF_H__*/
+
diff --git a/drivers/staging/rtl8712/rtl8712_macsetting_regdef.h b/drivers/staging/rtl8712/rtl8712_macsetting_regdef.h
new file mode 100644
index 000000000..ced0da933
--- /dev/null
+++ b/drivers/staging/rtl8712/rtl8712_macsetting_regdef.h
@@ -0,0 +1,35 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2010 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ *
+ ******************************************************************************/
+#ifndef __RTL8712_MACSETTING_REGDEF_H__
+#define __RTL8712_MACSETTING_REGDEF_H__
+
+#define MACID (RTL8712_MACIDSETTING_ + 0x0000)
+#define BSSIDR (RTL8712_MACIDSETTING_ + 0x0008)
+#define HWVID (RTL8712_MACIDSETTING_ + 0x000E)
+#define MAR (RTL8712_MACIDSETTING_ + 0x0010)
+#define MBIDCANCONTENT (RTL8712_MACIDSETTING_ + 0x0018)
+#define MBIDCANCFG (RTL8712_MACIDSETTING_ + 0x0020)
+#define BUILDTIME (RTL8712_MACIDSETTING_ + 0x0024)
+#define BUILDUSER (RTL8712_MACIDSETTING_ + 0x0028)
+
+
+
+#endif /*__RTL8712_MACSETTING_REGDEF_H__*/
+
diff --git a/drivers/staging/rtl8712/rtl8712_powersave_bitdef.h b/drivers/staging/rtl8712/rtl8712_powersave_bitdef.h
new file mode 100644
index 000000000..8fc689416
--- /dev/null
+++ b/drivers/staging/rtl8712/rtl8712_powersave_bitdef.h
@@ -0,0 +1,52 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2010 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ *
+ ******************************************************************************/
+#ifndef __RTL8712_POWERSAVE_BITDEF_H__
+#define __RTL8712_POWERSAVE_BITDEF_H__
+
+/*WOWCTRL*/
+#define _UWF BIT(3)
+#define _MAGIC BIT(2)
+#define _WOW_EN BIT(1)
+#define _PMEN BIT(0)
+
+/*PSSTATUS*/
+#define _PSSTATUS_SEL_MSK 0x0F
+
+/*PSSWITCH*/
+#define _PSSWITCH_ACT BIT(7)
+#define _PSSWITCH_SEL_MSK 0x0F
+#define _PSSWITCH_SEL_SHT 0
+
+/*LPNAV_CTRL*/
+#define _LPNAV_EN BIT(31)
+#define _LPNAV_EARLY_MSK 0x7FFF0000
+#define _LPNAV_EARLY_SHT 16
+#define _LPNAV_TH_MSK 0x0000FFFF
+#define _LPNAV_TH_SHT 0
+
+/*RPWM*/
+/*CPWM*/
+#define _TOGGLING BIT(7)
+#define _WWLAN BIT(3)
+#define _RPS_ST BIT(2)
+#define _WLAN_TRX BIT(1)
+#define _SYS_CLK BIT(0)
+
+#endif /* __RTL8712_POWERSAVE_BITDEF_H__*/
diff --git a/drivers/staging/rtl8712/rtl8712_powersave_regdef.h b/drivers/staging/rtl8712/rtl8712_powersave_regdef.h
new file mode 100644
index 000000000..4632ddd5d
--- /dev/null
+++ b/drivers/staging/rtl8712/rtl8712_powersave_regdef.h
@@ -0,0 +1,39 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2010 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ *
+ ******************************************************************************/
+#ifndef __RTL8712_POWERSAVE_REGDEF_H__
+#define __RTL8712_POWERSAVE_REGDEF_H__
+
+#define WOWCTRL (RTL8712_POWERSAVE_ + 0x00)
+#define PSSTATUS (RTL8712_POWERSAVE_ + 0x01)
+#define PSSWITCH (RTL8712_POWERSAVE_ + 0x02)
+#define MIMOPS_WAITPERIOD (RTL8712_POWERSAVE_ + 0x03)
+#define LPNAV_CTRL (RTL8712_POWERSAVE_ + 0x04)
+#define WFM0 (RTL8712_POWERSAVE_ + 0x10)
+#define WFM1 (RTL8712_POWERSAVE_ + 0x20)
+#define WFM2 (RTL8712_POWERSAVE_ + 0x30)
+#define WFM3 (RTL8712_POWERSAVE_ + 0x40)
+#define WFM4 (RTL8712_POWERSAVE_ + 0x50)
+#define WFM5 (RTL8712_POWERSAVE_ + 0x60)
+#define WFCRC (RTL8712_POWERSAVE_ + 0x70)
+#define RPWM (RTL8712_POWERSAVE_ + 0x7C)
+#define CPWM (RTL8712_POWERSAVE_ + 0x7D)
+
+#endif /* __RTL8712_POWERSAVE_REGDEF_H__ */
+
diff --git a/drivers/staging/rtl8712/rtl8712_ratectrl_bitdef.h b/drivers/staging/rtl8712/rtl8712_ratectrl_bitdef.h
new file mode 100644
index 000000000..6d3d6e852
--- /dev/null
+++ b/drivers/staging/rtl8712/rtl8712_ratectrl_bitdef.h
@@ -0,0 +1,49 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2010 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ *
+ ******************************************************************************/
+#ifndef __RTL8712_RATECTRL_BITDEF_H__
+#define __RTL8712_RATECTRL_BITDEF_H__
+
+/*INIRTSMCS_SEL*/
+#define _INIRTSMCS_SEL_MSK 0x3F
+
+/* RRSR*/
+#define _RRSR_SHORT BIT(23)
+#define _RRSR_RSC_MSK 0x600000
+#define _RRSR_RSC_SHT 21
+#define _RRSR_BITMAP_MSK 0x0FFFFF
+#define _RRSR_BITMAP_SHT 0
+
+/* AGGLEN_LMT_H*/
+#define _AGGLMT_MCS32_MSK 0xF0
+#define _AGGLMT_MCS32_SHT 4
+#define _AGGLMT_MCS15_SGI_MSK 0x0F
+#define _AGGLMT_MCS15_SGI_SHT 0
+
+/* DARFRC*/
+/* RARFRC*/
+/* MCS_TXAGC*/
+/* CCK_TXAGC*/
+#define _CCK_MSK 0xFF00
+#define _CCK_SHT 8
+#define _BARKER_MSK 0x00FF
+#define _BARKER_SHT 0
+
+#endif /* __RTL8712_RATECTRL_BITDEF_H__*/
+
diff --git a/drivers/staging/rtl8712/rtl8712_ratectrl_regdef.h b/drivers/staging/rtl8712/rtl8712_ratectrl_regdef.h
new file mode 100644
index 000000000..73dfc3610
--- /dev/null
+++ b/drivers/staging/rtl8712/rtl8712_ratectrl_regdef.h
@@ -0,0 +1,56 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2010 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ * Modifications for inclusion into the Linux staging tree are
+ * Copyright(c) 2010 Larry Finger. All rights reserved.
+ *
+ * Contact information:
+ * WLAN FAE <wlanfae@realtek.com>
+ * Larry Finger <Larry.Finger@lwfinger.net>
+ *
+ ******************************************************************************/
+#ifndef __RTL8712_RATECTRL_REGDEF_H__
+#define __RTL8712_RATECTRL_REGDEF_H__
+
+#define INIMCS_SEL (RTL8712_RATECTRL_ + 0x00)
+#define INIRTSMCS_SEL (RTL8712_RATECTRL_ + 0x20)
+#define RRSR (RTL8712_RATECTRL_ + 0x21)
+#define ARFR0 (RTL8712_RATECTRL_ + 0x24)
+#define ARFR1 (RTL8712_RATECTRL_ + 0x28)
+#define ARFR2 (RTL8712_RATECTRL_ + 0x2C)
+#define ARFR3 (RTL8712_RATECTRL_ + 0x30)
+#define ARFR4 (RTL8712_RATECTRL_ + 0x34)
+#define ARFR5 (RTL8712_RATECTRL_ + 0x38)
+#define ARFR6 (RTL8712_RATECTRL_ + 0x3C)
+#define ARFR7 (RTL8712_RATECTRL_ + 0x40)
+#define AGGLEN_LMT_H (RTL8712_RATECTRL_ + 0x47)
+#define AGGLEN_LMT_L (RTL8712_RATECTRL_ + 0x48)
+#define DARFRC (RTL8712_RATECTRL_ + 0x50)
+#define RARFRC (RTL8712_RATECTRL_ + 0x58)
+#define MCS_TXAGC0 (RTL8712_RATECTRL_ + 0x60)
+#define MCS_TXAGC1 (RTL8712_RATECTRL_ + 0x61)
+#define MCS_TXAGC2 (RTL8712_RATECTRL_ + 0x62)
+#define MCS_TXAGC3 (RTL8712_RATECTRL_ + 0x63)
+#define MCS_TXAGC4 (RTL8712_RATECTRL_ + 0x64)
+#define MCS_TXAGC5 (RTL8712_RATECTRL_ + 0x65)
+#define MCS_TXAGC6 (RTL8712_RATECTRL_ + 0x66)
+#define MCS_TXAGC7 (RTL8712_RATECTRL_ + 0x67)
+#define CCK_TXAGC (RTL8712_RATECTRL_ + 0x68)
+
+
+#endif /*__RTL8712_RATECTRL_REGDEF_H__*/
+
diff --git a/drivers/staging/rtl8712/rtl8712_recv.c b/drivers/staging/rtl8712/rtl8712_recv.c
new file mode 100644
index 000000000..fcb8c61b2
--- /dev/null
+++ b/drivers/staging/rtl8712/rtl8712_recv.c
@@ -0,0 +1,1118 @@
+/******************************************************************************
+ * rtl8712_recv.c
+ *
+ * Copyright(c) 2007 - 2010 Realtek Corporation. All rights reserved.
+ * Linux device driver for RTL8192SU
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ * Modifications for inclusion into the Linux staging tree are
+ * Copyright(c) 2010 Larry Finger. All rights reserved.
+ *
+ * Contact information:
+ * WLAN FAE <wlanfae@realtek.com>
+ * Larry Finger <Larry.Finger@lwfinger.net>
+ *
+ ******************************************************************************/
+
+#define _RTL8712_RECV_C_
+
+#include <linux/if_ether.h>
+#include <linux/ip.h>
+
+#include "osdep_service.h"
+#include "drv_types.h"
+#include "recv_osdep.h"
+#include "mlme_osdep.h"
+#include "ethernet.h"
+#include "usb_ops.h"
+#include "wifi.h"
+
+/* Bridge-Tunnel header (for EtherTypes ETH_P_AARP and ETH_P_IPX) */
+static u8 bridge_tunnel_header[] = {0xaa, 0xaa, 0x03, 0x00, 0x00, 0xf8};
+
+/* Ethernet-II snap header (RFC1042 for most EtherTypes) */
+static u8 rfc1042_header[] = {0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00};
+
+static void recv_tasklet(void *priv);
+
+int r8712_init_recv_priv(struct recv_priv *precvpriv, struct _adapter *padapter)
+{
+ int i;
+ struct recv_buf *precvbuf;
+ int res = _SUCCESS;
+ addr_t tmpaddr = 0;
+ int alignment = 0;
+ struct sk_buff *pskb = NULL;
+
+ /*init recv_buf*/
+ _init_queue(&precvpriv->free_recv_buf_queue);
+ precvpriv->pallocated_recv_buf = kzalloc(NR_RECVBUFF * sizeof(struct recv_buf) + 4,
+ GFP_ATOMIC);
+ if (precvpriv->pallocated_recv_buf == NULL)
+ return _FAIL;
+ precvpriv->precv_buf = precvpriv->pallocated_recv_buf + 4 -
+ ((addr_t) (precvpriv->pallocated_recv_buf) & 3);
+ precvbuf = (struct recv_buf *)precvpriv->precv_buf;
+ for (i = 0; i < NR_RECVBUFF; i++) {
+ INIT_LIST_HEAD(&precvbuf->list);
+ spin_lock_init(&precvbuf->recvbuf_lock);
+ res = r8712_os_recvbuf_resource_alloc(padapter, precvbuf);
+ if (res == _FAIL)
+ break;
+ precvbuf->ref_cnt = 0;
+ precvbuf->adapter = padapter;
+ list_add_tail(&precvbuf->list,
+ &(precvpriv->free_recv_buf_queue.queue));
+ precvbuf++;
+ }
+ precvpriv->free_recv_buf_queue_cnt = NR_RECVBUFF;
+ tasklet_init(&precvpriv->recv_tasklet,
+ (void(*)(unsigned long))recv_tasklet,
+ (unsigned long)padapter);
+ skb_queue_head_init(&precvpriv->rx_skb_queue);
+
+ skb_queue_head_init(&precvpriv->free_recv_skb_queue);
+ for (i = 0; i < NR_PREALLOC_RECV_SKB; i++) {
+ pskb = netdev_alloc_skb(padapter->pnetdev, MAX_RECVBUF_SZ +
+ RECVBUFF_ALIGN_SZ);
+ if (pskb) {
+ tmpaddr = (addr_t)pskb->data;
+ alignment = tmpaddr & (RECVBUFF_ALIGN_SZ-1);
+ skb_reserve(pskb, (RECVBUFF_ALIGN_SZ - alignment));
+ skb_queue_tail(&precvpriv->free_recv_skb_queue, pskb);
+ }
+ pskb = NULL;
+ }
+ return res;
+}
+
+void r8712_free_recv_priv(struct recv_priv *precvpriv)
+{
+ int i;
+ struct recv_buf *precvbuf;
+ struct _adapter *padapter = precvpriv->adapter;
+
+ precvbuf = (struct recv_buf *)precvpriv->precv_buf;
+ for (i = 0; i < NR_RECVBUFF; i++) {
+ r8712_os_recvbuf_resource_free(padapter, precvbuf);
+ precvbuf++;
+ }
+ kfree(precvpriv->pallocated_recv_buf);
+ skb_queue_purge(&precvpriv->rx_skb_queue);
+ if (skb_queue_len(&precvpriv->rx_skb_queue))
+ netdev_warn(padapter->pnetdev, "r8712u: rx_skb_queue not empty\n");
+ skb_queue_purge(&precvpriv->free_recv_skb_queue);
+ if (skb_queue_len(&precvpriv->free_recv_skb_queue))
+ netdev_warn(padapter->pnetdev, "r8712u: free_recv_skb_queue not empty %d\n",
+ skb_queue_len(&precvpriv->free_recv_skb_queue));
+}
+
+int r8712_init_recvbuf(struct _adapter *padapter, struct recv_buf *precvbuf)
+{
+ precvbuf->transfer_len = 0;
+ precvbuf->len = 0;
+ precvbuf->ref_cnt = 0;
+ if (precvbuf->pbuf) {
+ precvbuf->pdata = precvbuf->pbuf;
+ precvbuf->phead = precvbuf->pbuf;
+ precvbuf->ptail = precvbuf->pbuf;
+ precvbuf->pend = precvbuf->pdata + MAX_RECVBUF_SZ;
+ }
+ return _SUCCESS;
+}
+
+int r8712_free_recvframe(union recv_frame *precvframe,
+ struct __queue *pfree_recv_queue)
+{
+ unsigned long irqL;
+ struct _adapter *padapter = precvframe->u.hdr.adapter;
+ struct recv_priv *precvpriv = &padapter->recvpriv;
+
+ if (precvframe->u.hdr.pkt) {
+ dev_kfree_skb_any(precvframe->u.hdr.pkt);/*free skb by driver*/
+ precvframe->u.hdr.pkt = NULL;
+ }
+ spin_lock_irqsave(&pfree_recv_queue->lock, irqL);
+ list_del_init(&(precvframe->u.hdr.list));
+ list_add_tail(&(precvframe->u.hdr.list), &pfree_recv_queue->queue);
+ if (padapter != NULL) {
+ if (pfree_recv_queue == &precvpriv->free_recv_queue)
+ precvpriv->free_recvframe_cnt++;
+ }
+ spin_unlock_irqrestore(&pfree_recv_queue->lock, irqL);
+ return _SUCCESS;
+}
+
+static void update_recvframe_attrib_from_recvstat(struct rx_pkt_attrib *pattrib,
+ struct recv_stat *prxstat)
+{
+ u16 drvinfo_sz = 0;
+
+ drvinfo_sz = (le32_to_cpu(prxstat->rxdw0)&0x000f0000)>>16;
+ drvinfo_sz <<= 3;
+ /*TODO:
+ * Offset 0 */
+ pattrib->bdecrypted = ((le32_to_cpu(prxstat->rxdw0) & BIT(27)) >> 27)
+ ? 0 : 1;
+ pattrib->crc_err = (le32_to_cpu(prxstat->rxdw0) & BIT(14)) >> 14;
+ /*Offset 4*/
+ /*Offset 8*/
+ /*Offset 12*/
+ if (le32_to_cpu(prxstat->rxdw3) & BIT(13)) {
+ pattrib->tcpchk_valid = 1; /* valid */
+ if (le32_to_cpu(prxstat->rxdw3) & BIT(11))
+ pattrib->tcp_chkrpt = 1; /* correct */
+ else
+ pattrib->tcp_chkrpt = 0; /* incorrect */
+ if (le32_to_cpu(prxstat->rxdw3) & BIT(12))
+ pattrib->ip_chkrpt = 1; /* correct */
+ else
+ pattrib->ip_chkrpt = 0; /* incorrect */
+ } else
+ pattrib->tcpchk_valid = 0; /* invalid */
+ pattrib->mcs_rate = (u8)((le32_to_cpu(prxstat->rxdw3)) & 0x3f);
+ pattrib->htc = (u8)((le32_to_cpu(prxstat->rxdw3) >> 14) & 0x1);
+ /*Offset 16*/
+ /*Offset 20*/
+ /*phy_info*/
+}
+
+/*perform defrag*/
+static union recv_frame *recvframe_defrag(struct _adapter *adapter,
+ struct __queue *defrag_q)
+{
+ struct list_head *plist, *phead;
+ u8 wlanhdr_offset;
+ u8 curfragnum;
+ struct recv_frame_hdr *pfhdr, *pnfhdr;
+ union recv_frame *prframe, *pnextrframe;
+ struct __queue *pfree_recv_queue;
+
+ pfree_recv_queue = &adapter->recvpriv.free_recv_queue;
+ phead = &defrag_q->queue;
+ plist = phead->next;
+ prframe = LIST_CONTAINOR(plist, union recv_frame, u);
+ list_del_init(&prframe->u.list);
+ pfhdr = &prframe->u.hdr;
+ curfragnum = 0;
+ if (curfragnum != pfhdr->attrib.frag_num) {
+ /*the first fragment number must be 0
+ *free the whole queue*/
+ r8712_free_recvframe(prframe, pfree_recv_queue);
+ r8712_free_recvframe_queue(defrag_q, pfree_recv_queue);
+ return NULL;
+ }
+ curfragnum++;
+ plist = &defrag_q->queue;
+ plist = plist->next;
+ while (end_of_queue_search(phead, plist) == false) {
+ pnextrframe = LIST_CONTAINOR(plist, union recv_frame, u);
+ pnfhdr = &pnextrframe->u.hdr;
+ /*check the fragment sequence (2nd ~n fragment frame) */
+ if (curfragnum != pnfhdr->attrib.frag_num) {
+ /* the fragment number must increase (after decache)
+ * release the defrag_q & prframe */
+ r8712_free_recvframe(prframe, pfree_recv_queue);
+ r8712_free_recvframe_queue(defrag_q, pfree_recv_queue);
+ return NULL;
+ }
+ curfragnum++;
+ /* copy the 2nd~n fragment frame's payload to the first fragment
+ * get the 2nd~last fragment frame's payload */
+ wlanhdr_offset = pnfhdr->attrib.hdrlen + pnfhdr->attrib.iv_len;
+ recvframe_pull(pnextrframe, wlanhdr_offset);
+ /* append to first fragment frame's tail (if privacy frame,
+ * pull the ICV) */
+ recvframe_pull_tail(prframe, pfhdr->attrib.icv_len);
+ memcpy(pfhdr->rx_tail, pnfhdr->rx_data, pnfhdr->len);
+ recvframe_put(prframe, pnfhdr->len);
+ pfhdr->attrib.icv_len = pnfhdr->attrib.icv_len;
+ plist = plist->next;
+ }
+ /* free the defrag_q queue and return the prframe */
+ r8712_free_recvframe_queue(defrag_q, pfree_recv_queue);
+ return prframe;
+}
+
+/* check if need to defrag, if needed queue the frame to defrag_q */
+union recv_frame *r8712_recvframe_chk_defrag(struct _adapter *padapter,
+ union recv_frame *precv_frame)
+{
+ u8 ismfrag;
+ u8 fragnum;
+ u8 *psta_addr;
+ struct recv_frame_hdr *pfhdr;
+ struct sta_info *psta;
+ struct sta_priv *pstapriv;
+ struct list_head *phead;
+ union recv_frame *prtnframe = NULL;
+ struct __queue *pfree_recv_queue, *pdefrag_q;
+
+ pstapriv = &padapter->stapriv;
+ pfhdr = &precv_frame->u.hdr;
+ pfree_recv_queue = &padapter->recvpriv.free_recv_queue;
+ /* need to define struct of wlan header frame ctrl */
+ ismfrag = pfhdr->attrib.mfrag;
+ fragnum = pfhdr->attrib.frag_num;
+ psta_addr = pfhdr->attrib.ta;
+ psta = r8712_get_stainfo(pstapriv, psta_addr);
+ if (psta == NULL)
+ pdefrag_q = NULL;
+ else
+ pdefrag_q = &psta->sta_recvpriv.defrag_q;
+
+ if ((ismfrag == 0) && (fragnum == 0))
+ prtnframe = precv_frame;/*isn't a fragment frame*/
+ if (ismfrag == 1) {
+ /* 0~(n-1) fragment frame
+ * enqueue to defraf_g */
+ if (pdefrag_q != NULL) {
+ if (fragnum == 0) {
+ /*the first fragment*/
+ if (!list_empty(&pdefrag_q->queue)) {
+ /*free current defrag_q */
+ r8712_free_recvframe_queue(pdefrag_q,
+ pfree_recv_queue);
+ }
+ }
+ /* Then enqueue the 0~(n-1) fragment to the defrag_q */
+ phead = &pdefrag_q->queue;
+ list_add_tail(&pfhdr->list, phead);
+ prtnframe = NULL;
+ } else {
+ /* can't find this ta's defrag_queue, so free this
+ * recv_frame */
+ r8712_free_recvframe(precv_frame, pfree_recv_queue);
+ prtnframe = NULL;
+ }
+
+ }
+ if ((ismfrag == 0) && (fragnum != 0)) {
+ /* the last fragment frame
+ * enqueue the last fragment */
+ if (pdefrag_q != NULL) {
+ phead = &pdefrag_q->queue;
+ list_add_tail(&pfhdr->list, phead);
+ /*call recvframe_defrag to defrag*/
+ precv_frame = recvframe_defrag(padapter, pdefrag_q);
+ prtnframe = precv_frame;
+ } else {
+ /* can't find this ta's defrag_queue, so free this
+ * recv_frame */
+ r8712_free_recvframe(precv_frame, pfree_recv_queue);
+ prtnframe = NULL;
+ }
+ }
+ if ((prtnframe != NULL) && (prtnframe->u.hdr.attrib.privacy)) {
+ /* after defrag we must check tkip mic code */
+ if (r8712_recvframe_chkmic(padapter, prtnframe) == _FAIL) {
+ r8712_free_recvframe(prtnframe, pfree_recv_queue);
+ prtnframe = NULL;
+ }
+ }
+ return prtnframe;
+}
+
+static int amsdu_to_msdu(struct _adapter *padapter, union recv_frame *prframe)
+{
+ int a_len, padding_len;
+ u16 eth_type, nSubframe_Length;
+ u8 nr_subframes, i;
+ unsigned char *data_ptr, *pdata;
+ struct rx_pkt_attrib *pattrib;
+ _pkt *sub_skb, *subframes[MAX_SUBFRAME_COUNT];
+ struct recv_priv *precvpriv = &padapter->recvpriv;
+ struct __queue *pfree_recv_queue = &(precvpriv->free_recv_queue);
+
+ nr_subframes = 0;
+ pattrib = &prframe->u.hdr.attrib;
+ recvframe_pull(prframe, prframe->u.hdr.attrib.hdrlen);
+ if (prframe->u.hdr.attrib.iv_len > 0)
+ recvframe_pull(prframe, prframe->u.hdr.attrib.iv_len);
+ a_len = prframe->u.hdr.len;
+ pdata = prframe->u.hdr.rx_data;
+ while (a_len > ETH_HLEN) {
+ /* Offset 12 denote 2 mac address */
+ nSubframe_Length = *((u16 *)(pdata + 12));
+ /*==m==>change the length order*/
+ nSubframe_Length = (nSubframe_Length >> 8) +
+ (nSubframe_Length << 8);
+ if (a_len < (ETHERNET_HEADER_SIZE + nSubframe_Length)) {
+ netdev_warn(padapter->pnetdev, "r8712u: nRemain_Length is %d and nSubframe_Length is: %d\n",
+ a_len, nSubframe_Length);
+ goto exit;
+ }
+ /* move the data point to data content */
+ pdata += ETH_HLEN;
+ a_len -= ETH_HLEN;
+ /* Allocate new skb for releasing to upper layer */
+ sub_skb = dev_alloc_skb(nSubframe_Length + 12);
+ if (!sub_skb)
+ break;
+ skb_reserve(sub_skb, 12);
+ data_ptr = (u8 *)skb_put(sub_skb, nSubframe_Length);
+ memcpy(data_ptr, pdata, nSubframe_Length);
+ subframes[nr_subframes++] = sub_skb;
+ if (nr_subframes >= MAX_SUBFRAME_COUNT) {
+ netdev_warn(padapter->pnetdev, "r8712u: ParseSubframe(): Too many Subframes! Packets dropped!\n");
+ break;
+ }
+ pdata += nSubframe_Length;
+ a_len -= nSubframe_Length;
+ if (a_len != 0) {
+ padding_len = 4 - ((nSubframe_Length + ETH_HLEN) & 3);
+ if (padding_len == 4)
+ padding_len = 0;
+ if (a_len < padding_len)
+ goto exit;
+ pdata += padding_len;
+ a_len -= padding_len;
+ }
+ }
+ for (i = 0; i < nr_subframes; i++) {
+ sub_skb = subframes[i];
+ /* convert hdr + possible LLC headers into Ethernet header */
+ eth_type = (sub_skb->data[6] << 8) | sub_skb->data[7];
+ if (sub_skb->len >= 8 &&
+ ((!memcmp(sub_skb->data, rfc1042_header, SNAP_SIZE) &&
+ eth_type != ETH_P_AARP && eth_type != ETH_P_IPX) ||
+ !memcmp(sub_skb->data, bridge_tunnel_header, SNAP_SIZE))) {
+ /* remove RFC1042 or Bridge-Tunnel encapsulation and
+ * replace EtherType */
+ skb_pull(sub_skb, SNAP_SIZE);
+ memcpy(skb_push(sub_skb, ETH_ALEN), pattrib->src,
+ ETH_ALEN);
+ memcpy(skb_push(sub_skb, ETH_ALEN), pattrib->dst,
+ ETH_ALEN);
+ } else {
+ u16 len;
+ /* Leave Ethernet header part of hdr and full payload */
+ len = htons(sub_skb->len);
+ memcpy(skb_push(sub_skb, 2), &len, 2);
+ memcpy(skb_push(sub_skb, ETH_ALEN), pattrib->src,
+ ETH_ALEN);
+ memcpy(skb_push(sub_skb, ETH_ALEN), pattrib->dst,
+ ETH_ALEN);
+ }
+ /* Indicate the packets to upper layer */
+ if (sub_skb) {
+ sub_skb->protocol =
+ eth_type_trans(sub_skb, padapter->pnetdev);
+ sub_skb->dev = padapter->pnetdev;
+ if ((pattrib->tcpchk_valid == 1) &&
+ (pattrib->tcp_chkrpt == 1)) {
+ sub_skb->ip_summed = CHECKSUM_UNNECESSARY;
+ } else
+ sub_skb->ip_summed = CHECKSUM_NONE;
+ netif_rx(sub_skb);
+ }
+ }
+exit:
+ prframe->u.hdr.len = 0;
+ r8712_free_recvframe(prframe, pfree_recv_queue);
+ return _SUCCESS;
+}
+
+void r8712_rxcmd_event_hdl(struct _adapter *padapter, void *prxcmdbuf)
+{
+ uint voffset;
+ u8 *poffset;
+ u16 cmd_len, drvinfo_sz;
+ struct recv_stat *prxstat;
+
+ poffset = (u8 *)prxcmdbuf;
+ voffset = *(uint *)poffset;
+ prxstat = (struct recv_stat *)prxcmdbuf;
+ drvinfo_sz = (le32_to_cpu(prxstat->rxdw0) & 0x000f0000) >> 16;
+ drvinfo_sz <<= 3;
+ poffset += RXDESC_SIZE + drvinfo_sz;
+ do {
+ voffset = *(uint *)poffset;
+ cmd_len = (u16)(le32_to_cpu(voffset) & 0xffff);
+ r8712_event_handle(padapter, (uint *)poffset);
+ poffset += (cmd_len + 8);/*8 bytes alignment*/
+ } while (le32_to_cpu(voffset) & BIT(31));
+
+}
+
+static int check_indicate_seq(struct recv_reorder_ctrl *preorder_ctrl,
+ u16 seq_num)
+{
+ u8 wsize = preorder_ctrl->wsize_b;
+ u16 wend = (preorder_ctrl->indicate_seq + wsize - 1) % 4096;
+
+ /* Rx Reorder initialize condition.*/
+ if (preorder_ctrl->indicate_seq == 0xffff)
+ preorder_ctrl->indicate_seq = seq_num;
+ /* Drop out the packet which SeqNum is smaller than WinStart */
+ if (SN_LESS(seq_num, preorder_ctrl->indicate_seq))
+ return false;
+ /*
+ * Sliding window manipulation. Conditions includes:
+ * 1. Incoming SeqNum is equal to WinStart =>Window shift 1
+ * 2. Incoming SeqNum is larger than the WinEnd => Window shift N
+ */
+ if (SN_EQUAL(seq_num, preorder_ctrl->indicate_seq))
+ preorder_ctrl->indicate_seq = (preorder_ctrl->indicate_seq +
+ 1) % 4096;
+ else if (SN_LESS(wend, seq_num)) {
+ if (seq_num >= (wsize - 1))
+ preorder_ctrl->indicate_seq = seq_num + 1 - wsize;
+ else
+ preorder_ctrl->indicate_seq = 4095 - (wsize -
+ (seq_num + 1)) + 1;
+ }
+ return true;
+}
+
+static int enqueue_reorder_recvframe(struct recv_reorder_ctrl *preorder_ctrl,
+ union recv_frame *prframe)
+{
+ struct list_head *phead, *plist;
+ union recv_frame *pnextrframe;
+ struct rx_pkt_attrib *pnextattrib;
+ struct __queue *ppending_recvframe_queue =
+ &preorder_ctrl->pending_recvframe_queue;
+ struct rx_pkt_attrib *pattrib = &prframe->u.hdr.attrib;
+
+ phead = &ppending_recvframe_queue->queue;
+ plist = phead->next;
+ while (end_of_queue_search(phead, plist) == false) {
+ pnextrframe = LIST_CONTAINOR(plist, union recv_frame, u);
+ pnextattrib = &pnextrframe->u.hdr.attrib;
+ if (SN_LESS(pnextattrib->seq_num, pattrib->seq_num))
+ plist = plist->next;
+ else if (SN_EQUAL(pnextattrib->seq_num, pattrib->seq_num))
+ return false;
+ else
+ break;
+ }
+ list_del_init(&(prframe->u.hdr.list));
+ list_add_tail(&(prframe->u.hdr.list), plist);
+ return true;
+}
+
+int r8712_recv_indicatepkts_in_order(struct _adapter *padapter,
+ struct recv_reorder_ctrl *preorder_ctrl,
+ int bforced)
+{
+ struct list_head *phead, *plist;
+ union recv_frame *prframe;
+ struct rx_pkt_attrib *pattrib;
+ int bPktInBuf = false;
+ struct recv_priv *precvpriv = &padapter->recvpriv;
+ struct __queue *ppending_recvframe_queue =
+ &preorder_ctrl->pending_recvframe_queue;
+
+ phead = &ppending_recvframe_queue->queue;
+ plist = phead->next;
+ /* Handling some condition for forced indicate case.*/
+ if (bforced == true) {
+ if (list_empty(phead))
+ return true;
+
+ prframe = LIST_CONTAINOR(plist, union recv_frame, u);
+ pattrib = &prframe->u.hdr.attrib;
+ preorder_ctrl->indicate_seq = pattrib->seq_num;
+ }
+ /* Prepare indication list and indication.
+ * Check if there is any packet need indicate. */
+ while (!list_empty(phead)) {
+ prframe = LIST_CONTAINOR(plist, union recv_frame, u);
+ pattrib = &prframe->u.hdr.attrib;
+ if (!SN_LESS(preorder_ctrl->indicate_seq, pattrib->seq_num)) {
+ plist = plist->next;
+ list_del_init(&(prframe->u.hdr.list));
+ if (SN_EQUAL(preorder_ctrl->indicate_seq,
+ pattrib->seq_num))
+ preorder_ctrl->indicate_seq =
+ (preorder_ctrl->indicate_seq + 1) % 4096;
+ /*indicate this recv_frame*/
+ if (!pattrib->amsdu) {
+ if ((padapter->bDriverStopped == false) &&
+ (padapter->bSurpriseRemoved == false)) {
+ /* indicate this recv_frame */
+ r8712_recv_indicatepkt(padapter,
+ prframe);
+ }
+ } else if (pattrib->amsdu == 1) {
+ if (amsdu_to_msdu(padapter, prframe) !=
+ _SUCCESS)
+ r8712_free_recvframe(prframe,
+ &precvpriv->free_recv_queue);
+ }
+ /* Update local variables. */
+ bPktInBuf = false;
+ } else {
+ bPktInBuf = true;
+ break;
+ }
+ }
+ return bPktInBuf;
+}
+
+static int recv_indicatepkt_reorder(struct _adapter *padapter,
+ union recv_frame *prframe)
+{
+ unsigned long irql;
+ struct rx_pkt_attrib *pattrib = &prframe->u.hdr.attrib;
+ struct recv_reorder_ctrl *preorder_ctrl = prframe->u.hdr.preorder_ctrl;
+ struct __queue *ppending_recvframe_queue =
+ &preorder_ctrl->pending_recvframe_queue;
+
+ if (!pattrib->amsdu) {
+ /* s1. */
+ r8712_wlanhdr_to_ethhdr(prframe);
+ if (pattrib->qos != 1) {
+ if ((padapter->bDriverStopped == false) &&
+ (padapter->bSurpriseRemoved == false)) {
+ r8712_recv_indicatepkt(padapter, prframe);
+ return _SUCCESS;
+ } else
+ return _FAIL;
+ }
+ }
+ spin_lock_irqsave(&ppending_recvframe_queue->lock, irql);
+ /*s2. check if winstart_b(indicate_seq) needs to be updated*/
+ if (!check_indicate_seq(preorder_ctrl, pattrib->seq_num))
+ goto _err_exit;
+ /*s3. Insert all packet into Reorder Queue to maintain its ordering.*/
+ if (!enqueue_reorder_recvframe(preorder_ctrl, prframe))
+ goto _err_exit;
+ /*s4.
+ * Indication process.
+ * After Packet dropping and Sliding Window shifting as above, we can
+ * now just indicate the packets with the SeqNum smaller than latest
+ * WinStart and buffer other packets.
+ *
+ * For Rx Reorder condition:
+ * 1. All packets with SeqNum smaller than WinStart => Indicate
+ * 2. All packets with SeqNum larger than or equal to
+ * WinStart => Buffer it.
+ */
+ if (r8712_recv_indicatepkts_in_order(padapter, preorder_ctrl, false) ==
+ true) {
+ mod_timer(&preorder_ctrl->reordering_ctrl_timer,
+ jiffies + msecs_to_jiffies(REORDER_WAIT_TIME));
+ spin_unlock_irqrestore(&ppending_recvframe_queue->lock, irql);
+ } else {
+ spin_unlock_irqrestore(&ppending_recvframe_queue->lock, irql);
+ del_timer(&preorder_ctrl->reordering_ctrl_timer);
+ }
+ return _SUCCESS;
+_err_exit:
+ spin_unlock_irqrestore(&ppending_recvframe_queue->lock, irql);
+ return _FAIL;
+}
+
+void r8712_reordering_ctrl_timeout_handler(void *pcontext)
+{
+ unsigned long irql;
+ struct recv_reorder_ctrl *preorder_ctrl =
+ (struct recv_reorder_ctrl *)pcontext;
+ struct _adapter *padapter = preorder_ctrl->padapter;
+ struct __queue *ppending_recvframe_queue =
+ &preorder_ctrl->pending_recvframe_queue;
+
+ if (padapter->bDriverStopped || padapter->bSurpriseRemoved)
+ return;
+ spin_lock_irqsave(&ppending_recvframe_queue->lock, irql);
+ r8712_recv_indicatepkts_in_order(padapter, preorder_ctrl, true);
+ spin_unlock_irqrestore(&ppending_recvframe_queue->lock, irql);
+}
+
+static int r8712_process_recv_indicatepkts(struct _adapter *padapter,
+ union recv_frame *prframe)
+{
+ int retval = _SUCCESS;
+ struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
+ struct ht_priv *phtpriv = &pmlmepriv->htpriv;
+
+ if (phtpriv->ht_option == 1) { /*B/G/N Mode*/
+ if (recv_indicatepkt_reorder(padapter, prframe) != _SUCCESS) {
+ /* including perform A-MPDU Rx Ordering Buffer Control*/
+ if ((padapter->bDriverStopped == false) &&
+ (padapter->bSurpriseRemoved == false))
+ return _FAIL;
+ }
+ } else { /*B/G mode*/
+ retval = r8712_wlanhdr_to_ethhdr(prframe);
+ if (retval != _SUCCESS)
+ return retval;
+ if ((padapter->bDriverStopped == false) &&
+ (padapter->bSurpriseRemoved == false)) {
+ /* indicate this recv_frame */
+ r8712_recv_indicatepkt(padapter, prframe);
+ } else
+ return _FAIL;
+ }
+ return retval;
+}
+
+static u8 query_rx_pwr_percentage(s8 antpower)
+{
+ if ((antpower <= -100) || (antpower >= 20))
+ return 0;
+ else if (antpower >= 0)
+ return 100;
+ else
+ return 100 + antpower;
+}
+
+static u8 evm_db2percentage(s8 value)
+{
+ /*
+ * -33dB~0dB to 0%~99%
+ */
+ s8 ret_val;
+
+ ret_val = value;
+ if (ret_val >= 0)
+ ret_val = 0;
+ if (ret_val <= -33)
+ ret_val = -33;
+ ret_val = -ret_val;
+ ret_val *= 3;
+ if (ret_val == 99)
+ ret_val = 100;
+ return ret_val;
+}
+
+s32 r8712_signal_scale_mapping(s32 cur_sig)
+{
+ s32 ret_sig;
+
+ if (cur_sig >= 51 && cur_sig <= 100)
+ ret_sig = 100;
+ else if (cur_sig >= 41 && cur_sig <= 50)
+ ret_sig = 80 + ((cur_sig - 40) * 2);
+ else if (cur_sig >= 31 && cur_sig <= 40)
+ ret_sig = 66 + (cur_sig - 30);
+ else if (cur_sig >= 21 && cur_sig <= 30)
+ ret_sig = 54 + (cur_sig - 20);
+ else if (cur_sig >= 10 && cur_sig <= 20)
+ ret_sig = 42 + (((cur_sig - 10) * 2) / 3);
+ else if (cur_sig >= 5 && cur_sig <= 9)
+ ret_sig = 22 + (((cur_sig - 5) * 3) / 2);
+ else if (cur_sig >= 1 && cur_sig <= 4)
+ ret_sig = 6 + (((cur_sig - 1) * 3) / 2);
+ else
+ ret_sig = cur_sig;
+ return ret_sig;
+}
+
+static s32 translate2dbm(struct _adapter *padapter, u8 signal_strength_idx)
+{
+ s32 signal_power; /* in dBm.*/
+ /* Translate to dBm (x=0.5y-95).*/
+ signal_power = (s32)((signal_strength_idx + 1) >> 1);
+ signal_power -= 95;
+ return signal_power;
+}
+
+static void query_rx_phy_status(struct _adapter *padapter,
+ union recv_frame *prframe)
+{
+ u8 i, max_spatial_stream, evm;
+ struct recv_stat *prxstat = (struct recv_stat *)prframe->u.hdr.rx_head;
+ struct phy_stat *pphy_stat = (struct phy_stat *)(prxstat + 1);
+ u8 *pphy_head = (u8 *)(prxstat + 1);
+ s8 rx_pwr[4], rx_pwr_all;
+ u8 pwdb_all;
+ u32 rssi, total_rssi = 0;
+ u8 bcck_rate = 0, rf_rx_num = 0, cck_highpwr = 0;
+ struct phy_cck_rx_status *pcck_buf;
+ u8 sq;
+
+ /* Record it for next packet processing*/
+ bcck_rate = (prframe->u.hdr.attrib.mcs_rate <= 3 ? 1 : 0);
+ if (bcck_rate) {
+ u8 report;
+
+ /* CCK Driver info Structure is not the same as OFDM packet.*/
+ pcck_buf = (struct phy_cck_rx_status *)pphy_stat;
+ /* (1)Hardware does not provide RSSI for CCK
+ * (2)PWDB, Average PWDB cacluated by hardware
+ * (for rate adaptive)
+ */
+ if (!cck_highpwr) {
+ report = pcck_buf->cck_agc_rpt & 0xc0;
+ report >>= 6;
+ switch (report) {
+ /* Modify the RF RNA gain value to -40, -20,
+ * -2, 14 by Jenyu's suggestion
+ * Note: different RF with the different
+ * RNA gain. */
+ case 0x3:
+ rx_pwr_all = -40 - (pcck_buf->cck_agc_rpt &
+ 0x3e);
+ break;
+ case 0x2:
+ rx_pwr_all = -20 - (pcck_buf->cck_agc_rpt &
+ 0x3e);
+ break;
+ case 0x1:
+ rx_pwr_all = -2 - (pcck_buf->cck_agc_rpt &
+ 0x3e);
+ break;
+ case 0x0:
+ rx_pwr_all = 14 - (pcck_buf->cck_agc_rpt &
+ 0x3e);
+ break;
+ }
+ } else {
+ report = ((u8)(le32_to_cpu(pphy_stat->phydw1) >> 8)) &
+ 0x60;
+ report >>= 5;
+ switch (report) {
+ case 0x3:
+ rx_pwr_all = -40 - ((pcck_buf->cck_agc_rpt &
+ 0x1f) << 1);
+ break;
+ case 0x2:
+ rx_pwr_all = -20 - ((pcck_buf->cck_agc_rpt &
+ 0x1f) << 1);
+ break;
+ case 0x1:
+ rx_pwr_all = -2 - ((pcck_buf->cck_agc_rpt &
+ 0x1f) << 1);
+ break;
+ case 0x0:
+ rx_pwr_all = 14 - ((pcck_buf->cck_agc_rpt &
+ 0x1f) << 1);
+ break;
+ }
+ }
+ pwdb_all = query_rx_pwr_percentage(rx_pwr_all);
+ /* CCK gain is smaller than OFDM/MCS gain,*/
+ /* so we add gain diff by experiences, the val is 6 */
+ pwdb_all += 6;
+ if (pwdb_all > 100)
+ pwdb_all = 100;
+ /* modify the offset to make the same gain index with OFDM.*/
+ if (pwdb_all > 34 && pwdb_all <= 42)
+ pwdb_all -= 2;
+ else if (pwdb_all > 26 && pwdb_all <= 34)
+ pwdb_all -= 6;
+ else if (pwdb_all > 14 && pwdb_all <= 26)
+ pwdb_all -= 8;
+ else if (pwdb_all > 4 && pwdb_all <= 14)
+ pwdb_all -= 4;
+ /*
+ * (3) Get Signal Quality (EVM)
+ */
+ if (pwdb_all > 40)
+ sq = 100;
+ else {
+ sq = pcck_buf->sq_rpt;
+ if (pcck_buf->sq_rpt > 64)
+ sq = 0;
+ else if (pcck_buf->sq_rpt < 20)
+ sq = 100;
+ else
+ sq = ((64-sq) * 100) / 44;
+ }
+ prframe->u.hdr.attrib.signal_qual = sq;
+ prframe->u.hdr.attrib.rx_mimo_signal_qual[0] = sq;
+ prframe->u.hdr.attrib.rx_mimo_signal_qual[1] = -1;
+ } else {
+ /* (1)Get RSSI for HT rate */
+ for (i = 0; i < ((padapter->registrypriv.rf_config) &
+ 0x0f); i++) {
+ rf_rx_num++;
+ rx_pwr[i] = ((pphy_head[PHY_STAT_GAIN_TRSW_SHT + i]
+ & 0x3F) * 2) - 110;
+ /* Translate DBM to percentage. */
+ rssi = query_rx_pwr_percentage(rx_pwr[i]);
+ total_rssi += rssi;
+ }
+ /* (2)PWDB, Average PWDB cacluated by hardware (for
+ * rate adaptive) */
+ rx_pwr_all = (((pphy_head[PHY_STAT_PWDB_ALL_SHT]) >> 1) & 0x7f)
+ - 106;
+ pwdb_all = query_rx_pwr_percentage(rx_pwr_all);
+
+ {
+ /* (3)EVM of HT rate */
+ if (prframe->u.hdr.attrib.htc &&
+ prframe->u.hdr.attrib.mcs_rate >= 20 &&
+ prframe->u.hdr.attrib.mcs_rate <= 27) {
+ /* both spatial stream make sense */
+ max_spatial_stream = 2;
+ } else {
+ /* only spatial stream 1 makes sense */
+ max_spatial_stream = 1;
+ }
+ for (i = 0; i < max_spatial_stream; i++) {
+ evm = evm_db2percentage((pphy_head
+ [PHY_STAT_RXEVM_SHT + i]));/*dbm*/
+ prframe->u.hdr.attrib.signal_qual =
+ (u8)(evm & 0xff);
+ prframe->u.hdr.attrib.rx_mimo_signal_qual[i] =
+ (u8)(evm & 0xff);
+ }
+ }
+ }
+ /* UI BSS List signal strength(in percentage), make it good looking,
+ * from 0~100. It is assigned to the BSS List in
+ * GetValueFromBeaconOrProbeRsp(). */
+ if (bcck_rate)
+ prframe->u.hdr.attrib.signal_strength =
+ (u8)r8712_signal_scale_mapping(pwdb_all);
+ else {
+ if (rf_rx_num != 0)
+ prframe->u.hdr.attrib.signal_strength =
+ (u8)(r8712_signal_scale_mapping(total_rssi /=
+ rf_rx_num));
+ }
+}
+
+static void process_link_qual(struct _adapter *padapter,
+ union recv_frame *prframe)
+{
+ u32 last_evm = 0, tmpVal;
+ struct rx_pkt_attrib *pattrib;
+
+ if (prframe == NULL || padapter == NULL)
+ return;
+ pattrib = &prframe->u.hdr.attrib;
+ if (pattrib->signal_qual != 0) {
+ /*
+ * 1. Record the general EVM to the sliding window.
+ */
+ if (padapter->recvpriv.signal_qual_data.total_num++ >=
+ PHY_LINKQUALITY_SLID_WIN_MAX) {
+ padapter->recvpriv.signal_qual_data.total_num =
+ PHY_LINKQUALITY_SLID_WIN_MAX;
+ last_evm = padapter->recvpriv.signal_qual_data.elements
+ [padapter->recvpriv.signal_qual_data.index];
+ padapter->recvpriv.signal_qual_data.total_val -=
+ last_evm;
+ }
+ padapter->recvpriv.signal_qual_data.total_val +=
+ pattrib->signal_qual;
+ padapter->recvpriv.signal_qual_data.elements[padapter->
+ recvpriv.signal_qual_data.index++] =
+ pattrib->signal_qual;
+ if (padapter->recvpriv.signal_qual_data.index >=
+ PHY_LINKQUALITY_SLID_WIN_MAX)
+ padapter->recvpriv.signal_qual_data.index = 0;
+
+ /* <1> Showed on UI for user, in percentage. */
+ tmpVal = padapter->recvpriv.signal_qual_data.total_val /
+ padapter->recvpriv.signal_qual_data.total_num;
+ padapter->recvpriv.signal = (u8)tmpVal;
+ }
+}
+
+static void process_rssi(struct _adapter *padapter, union recv_frame *prframe)
+{
+ u32 last_rssi, tmp_val;
+ struct rx_pkt_attrib *pattrib = &prframe->u.hdr.attrib;
+
+ if (padapter->recvpriv.signal_strength_data.total_num++ >=
+ PHY_RSSI_SLID_WIN_MAX) {
+ padapter->recvpriv.signal_strength_data.total_num =
+ PHY_RSSI_SLID_WIN_MAX;
+ last_rssi = padapter->recvpriv.signal_strength_data.elements
+ [padapter->recvpriv.signal_strength_data.index];
+ padapter->recvpriv.signal_strength_data.total_val -= last_rssi;
+ }
+ padapter->recvpriv.signal_strength_data.total_val +=
+ pattrib->signal_strength;
+ padapter->recvpriv.signal_strength_data.elements[padapter->recvpriv.
+ signal_strength_data.index++] =
+ pattrib->signal_strength;
+ if (padapter->recvpriv.signal_strength_data.index >=
+ PHY_RSSI_SLID_WIN_MAX)
+ padapter->recvpriv.signal_strength_data.index = 0;
+ tmp_val = padapter->recvpriv.signal_strength_data.total_val /
+ padapter->recvpriv.signal_strength_data.total_num;
+ padapter->recvpriv.rssi = (s8)translate2dbm(padapter, (u8)tmp_val);
+}
+
+static void process_phy_info(struct _adapter *padapter,
+ union recv_frame *prframe)
+{
+ query_rx_phy_status(padapter, prframe);
+ process_rssi(padapter, prframe);
+ process_link_qual(padapter, prframe);
+}
+
+int recv_func(struct _adapter *padapter, void *pcontext)
+{
+ struct rx_pkt_attrib *pattrib;
+ union recv_frame *prframe, *orig_prframe;
+ int retval = _SUCCESS;
+ struct __queue *pfree_recv_queue = &padapter->recvpriv.free_recv_queue;
+ struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
+
+ prframe = (union recv_frame *)pcontext;
+ orig_prframe = prframe;
+ pattrib = &prframe->u.hdr.attrib;
+ if (check_fwstate(pmlmepriv, WIFI_MP_STATE) == true) {
+ if (pattrib->crc_err == 1)
+ padapter->mppriv.rx_crcerrpktcount++;
+ else
+ padapter->mppriv.rx_pktcount++;
+ if (check_fwstate(pmlmepriv, WIFI_MP_LPBK_STATE) == false) {
+ /* free this recv_frame */
+ r8712_free_recvframe(orig_prframe, pfree_recv_queue);
+ goto _exit_recv_func;
+ }
+ }
+ /* check the frame crtl field and decache */
+ retval = r8712_validate_recv_frame(padapter, prframe);
+ if (retval != _SUCCESS) {
+ /* free this recv_frame */
+ r8712_free_recvframe(orig_prframe, pfree_recv_queue);
+ goto _exit_recv_func;
+ }
+ process_phy_info(padapter, prframe);
+ prframe = r8712_decryptor(padapter, prframe);
+ if (prframe == NULL) {
+ retval = _FAIL;
+ goto _exit_recv_func;
+ }
+ prframe = r8712_recvframe_chk_defrag(padapter, prframe);
+ if (prframe == NULL)
+ goto _exit_recv_func;
+ prframe = r8712_portctrl(padapter, prframe);
+ if (prframe == NULL) {
+ retval = _FAIL;
+ goto _exit_recv_func;
+ }
+ retval = r8712_process_recv_indicatepkts(padapter, prframe);
+ if (retval != _SUCCESS) {
+ r8712_free_recvframe(orig_prframe, pfree_recv_queue);
+ goto _exit_recv_func;
+ }
+_exit_recv_func:
+ return retval;
+}
+
+static int recvbuf2recvframe(struct _adapter *padapter, struct sk_buff *pskb)
+{
+ u8 *pbuf, shift_sz = 0;
+ u8 frag, mf;
+ uint pkt_len;
+ u32 transfer_len;
+ struct recv_stat *prxstat;
+ u16 pkt_cnt, drvinfo_sz, pkt_offset, tmp_len, alloc_sz;
+ struct __queue *pfree_recv_queue;
+ _pkt *pkt_copy = NULL;
+ union recv_frame *precvframe = NULL;
+ struct recv_priv *precvpriv = &padapter->recvpriv;
+
+ pfree_recv_queue = &(precvpriv->free_recv_queue);
+ pbuf = pskb->data;
+ prxstat = (struct recv_stat *)pbuf;
+ pkt_cnt = (le32_to_cpu(prxstat->rxdw2)>>16)&0xff;
+ pkt_len = le32_to_cpu(prxstat->rxdw0)&0x00003fff;
+ transfer_len = pskb->len;
+ /* Test throughput with Netgear 3700 (No security) with Chariot 3T3R
+ * pairs. The packet count will be a big number so that the containing
+ * packet will effect the Rx reordering. */
+ if (transfer_len < pkt_len) {
+ /* In this case, it means the MAX_RECVBUF_SZ is too small to
+ * get the data from 8712u. */
+ return _FAIL;
+ }
+ do {
+ prxstat = (struct recv_stat *)pbuf;
+ pkt_len = le32_to_cpu(prxstat->rxdw0)&0x00003fff;
+ /* more fragment bit */
+ mf = (le32_to_cpu(prxstat->rxdw1) >> 27) & 0x1;
+ /* ragmentation number */
+ frag = (le32_to_cpu(prxstat->rxdw2) >> 12) & 0xf;
+ /* uint 2^3 = 8 bytes */
+ drvinfo_sz = (le32_to_cpu(prxstat->rxdw0) & 0x000f0000) >> 16;
+ drvinfo_sz <<= 3;
+ if (pkt_len <= 0)
+ goto _exit_recvbuf2recvframe;
+ /* Qos data, wireless lan header length is 26 */
+ if ((le32_to_cpu(prxstat->rxdw0) >> 23) & 0x01)
+ shift_sz = 2;
+ precvframe = r8712_alloc_recvframe(pfree_recv_queue);
+ if (precvframe == NULL)
+ goto _exit_recvbuf2recvframe;
+ INIT_LIST_HEAD(&precvframe->u.hdr.list);
+ precvframe->u.hdr.precvbuf = NULL; /*can't access the precvbuf*/
+ precvframe->u.hdr.len = 0;
+ tmp_len = pkt_len + drvinfo_sz + RXDESC_SIZE;
+ pkt_offset = (u16)round_up(tmp_len, 128);
+ /* for first fragment packet, driver need allocate 1536 +
+ * drvinfo_sz + RXDESC_SIZE to defrag packet. */
+ if ((mf == 1) && (frag == 0))
+ /*1658+6=1664, 1664 is 128 alignment.*/
+ alloc_sz = max_t(u16, tmp_len, 1658);
+ else
+ alloc_sz = tmp_len;
+ /* 2 is for IP header 4 bytes alignment in QoS packet case.
+ * 4 is for skb->data 4 bytes alignment. */
+ alloc_sz += 6;
+ pkt_copy = netdev_alloc_skb(padapter->pnetdev, alloc_sz);
+ if (pkt_copy) {
+ precvframe->u.hdr.pkt = pkt_copy;
+ skb_reserve(pkt_copy, 4 - ((addr_t)(pkt_copy->data)
+ % 4));
+ skb_reserve(pkt_copy, shift_sz);
+ memcpy(pkt_copy->data, pbuf, tmp_len);
+ precvframe->u.hdr.rx_head = precvframe->u.hdr.rx_data =
+ precvframe->u.hdr.rx_tail = pkt_copy->data;
+ precvframe->u.hdr.rx_end = pkt_copy->data + alloc_sz;
+ } else {
+ precvframe->u.hdr.pkt = skb_clone(pskb, GFP_ATOMIC);
+ if (!precvframe->u.hdr.pkt)
+ return _FAIL;
+ precvframe->u.hdr.rx_head = pbuf;
+ precvframe->u.hdr.rx_data = pbuf;
+ precvframe->u.hdr.rx_tail = pbuf;
+ precvframe->u.hdr.rx_end = pbuf + alloc_sz;
+ }
+ recvframe_put(precvframe, tmp_len);
+ recvframe_pull(precvframe, drvinfo_sz + RXDESC_SIZE);
+ /* because the endian issue, driver avoid reference to the
+ * rxstat after calling update_recvframe_attrib_from_recvstat();
+ */
+ update_recvframe_attrib_from_recvstat(&precvframe->u.hdr.attrib,
+ prxstat);
+ r8712_recv_entry(precvframe);
+ transfer_len -= pkt_offset;
+ pbuf += pkt_offset;
+ pkt_cnt--;
+ precvframe = NULL;
+ pkt_copy = NULL;
+ } while ((transfer_len > 0) && pkt_cnt > 0);
+_exit_recvbuf2recvframe:
+ return _SUCCESS;
+}
+
+static void recv_tasklet(void *priv)
+{
+ struct sk_buff *pskb;
+ struct _adapter *padapter = (struct _adapter *)priv;
+ struct recv_priv *precvpriv = &padapter->recvpriv;
+
+ while (NULL != (pskb = skb_dequeue(&precvpriv->rx_skb_queue))) {
+ recvbuf2recvframe(padapter, pskb);
+ skb_reset_tail_pointer(pskb);
+ pskb->len = 0;
+ if (!skb_cloned(pskb))
+ skb_queue_tail(&precvpriv->free_recv_skb_queue, pskb);
+ else
+ consume_skb(pskb);
+ }
+}
diff --git a/drivers/staging/rtl8712/rtl8712_recv.h b/drivers/staging/rtl8712/rtl8712_recv.h
new file mode 100644
index 000000000..fd9e3fc4c
--- /dev/null
+++ b/drivers/staging/rtl8712/rtl8712_recv.h
@@ -0,0 +1,157 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2010 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ * Modifications for inclusion into the Linux staging tree are
+ * Copyright(c) 2010 Larry Finger. All rights reserved.
+ *
+ * Contact information:
+ * WLAN FAE <wlanfae@realtek.com>
+ * Larry Finger <Larry.Finger@lwfinger.net>
+ *
+ ******************************************************************************/
+#ifndef _RTL8712_RECV_H_
+#define _RTL8712_RECV_H_
+
+#include "osdep_service.h"
+#include "drv_types.h"
+
+/* Realtek's v2.6.6 reduced this to 4. However, under heavy network and CPU
+ * loads, even 8 receive buffers might not be enough; cutting it to 4 seemed
+ * unwise.
+ */
+#define NR_RECVBUFF (8)
+
+#define NR_PREALLOC_RECV_SKB (8)
+#define RXDESC_SIZE 24
+#define RXDESC_OFFSET RXDESC_SIZE
+#define RECV_BLK_SZ 512
+#define RECV_BLK_CNT 16
+#define RECV_BLK_TH RECV_BLK_CNT
+#define MAX_RECVBUF_SZ 9100
+#define RECVBUFF_ALIGN_SZ 512
+#define RSVD_ROOM_SZ (0)
+/*These definition is used for Rx packet reordering.*/
+#define SN_LESS(a, b) (((a-b) & 0x800) != 0)
+#define SN_EQUAL(a, b) (a == b)
+#define REORDER_WAIT_TIME 30 /* (ms)*/
+
+struct recv_stat {
+ unsigned int rxdw0;
+ unsigned int rxdw1;
+ unsigned int rxdw2;
+ unsigned int rxdw3;
+ unsigned int rxdw4;
+ unsigned int rxdw5;
+};
+
+struct phy_cck_rx_status {
+ /* For CCK rate descriptor. This is a unsigned 8:1 variable.
+ * LSB bit present 0.5. And MSB 7 bts present a signed value.
+ * Range from -64~+63.5. */
+ u8 adc_pwdb_X[4];
+ u8 sq_rpt;
+ u8 cck_agc_rpt;
+};
+
+struct phy_stat {
+ unsigned int phydw0;
+ unsigned int phydw1;
+ unsigned int phydw2;
+ unsigned int phydw3;
+ unsigned int phydw4;
+ unsigned int phydw5;
+ unsigned int phydw6;
+ unsigned int phydw7;
+};
+#define PHY_STAT_GAIN_TRSW_SHT 0
+#define PHY_STAT_PWDB_ALL_SHT 4
+#define PHY_STAT_CFOSHO_SHT 5
+#define PHY_STAT_CCK_AGC_RPT_SHT 5
+#define PHY_STAT_CFOTAIL_SHT 9
+#define PHY_STAT_RXEVM_SHT 13
+#define PHY_STAT_RXSNR_SHT 15
+#define PHY_STAT_PDSNR_SHT 19
+#define PHY_STAT_CSI_CURRENT_SHT 21
+#define PHY_STAT_CSI_TARGET_SHT 23
+#define PHY_STAT_SIGEVM_SHT 25
+#define PHY_STAT_MAX_EX_PWR_SHT 26
+
+union recvstat {
+ struct recv_stat recv_stat;
+ unsigned int value[RXDESC_SIZE>>2];
+};
+
+
+struct recv_buf {
+ struct list_head list;
+ spinlock_t recvbuf_lock;
+ u32 ref_cnt;
+ struct _adapter *adapter;
+ struct urb *purb;
+ _pkt *pskb;
+ u8 reuse;
+ u8 irp_pending;
+ u32 transfer_len;
+ uint len;
+ u8 *phead;
+ u8 *pdata;
+ u8 *ptail;
+ u8 *pend;
+ u8 *pbuf;
+ u8 *pallocated_buf;
+};
+
+/*
+ head ----->
+ data ----->
+ payload
+ tail ----->
+ end ----->
+ len = (unsigned int )(tail - data);
+*/
+struct recv_frame_hdr {
+ struct list_head list;
+ _pkt *pkt;
+ _pkt *pkt_newalloc;
+ struct _adapter *adapter;
+ u8 fragcnt;
+ struct rx_pkt_attrib attrib;
+ uint len;
+ u8 *rx_head;
+ u8 *rx_data;
+ u8 *rx_tail;
+ u8 *rx_end;
+ void *precvbuf;
+ struct sta_info *psta;
+ /*for A-MPDU Rx reordering buffer control*/
+ struct recv_reorder_ctrl *preorder_ctrl;
+};
+
+union recv_frame {
+ union {
+ struct list_head list;
+ struct recv_frame_hdr hdr;
+ } u;
+};
+
+int r8712_init_recvbuf(struct _adapter *padapter, struct recv_buf *precvbuf);
+void r8712_rxcmd_event_hdl(struct _adapter *padapter, void *prxcmdbuf);
+s32 r8712_signal_scale_mapping(s32 cur_sig);
+void r8712_reordering_ctrl_timeout_handler(void *pcontext);
+
+#endif
+
diff --git a/drivers/staging/rtl8712/rtl8712_regdef.h b/drivers/staging/rtl8712/rtl8712_regdef.h
new file mode 100644
index 000000000..e7bca55b5
--- /dev/null
+++ b/drivers/staging/rtl8712/rtl8712_regdef.h
@@ -0,0 +1,44 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2010 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ * Modifications for inclusion into the Linux staging tree are
+ * Copyright(c) 2010 Larry Finger. All rights reserved.
+ *
+ * Contact information:
+ * WLAN FAE <wlanfae@realtek.com>
+ * Larry Finger <Larry.Finger@lwfinger.net>
+ *
+ ******************************************************************************/
+#ifndef __RTL8712_REGDEF_H__
+#define __RTL8712_REGDEF_H__
+
+#include "rtl8712_syscfg_regdef.h"
+#include "rtl8712_cmdctrl_regdef.h"
+#include "rtl8712_macsetting_regdef.h"
+#include "rtl8712_timectrl_regdef.h"
+#include "rtl8712_fifoctrl_regdef.h"
+#include "rtl8712_ratectrl_regdef.h"
+#include "rtl8712_edcasetting_regdef.h"
+#include "rtl8712_wmac_regdef.h"
+#include "rtl8712_powersave_regdef.h"
+#include "rtl8712_gp_regdef.h"
+#include "rtl8712_debugctrl_regdef.h"
+
+#define HIMR (RTL8712_INTERRUPT_ + 0x08)
+
+#endif /* __RTL8712_REGDEF_H__*/
+
diff --git a/drivers/staging/rtl8712/rtl8712_security_bitdef.h b/drivers/staging/rtl8712/rtl8712_security_bitdef.h
new file mode 100644
index 000000000..05dafa0c3
--- /dev/null
+++ b/drivers/staging/rtl8712/rtl8712_security_bitdef.h
@@ -0,0 +1,48 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2010 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ *
+ ******************************************************************************/
+#ifndef __RTL8712_SECURITY_BITDEF_H__
+#define __RTL8712_SECURITY_BITDEF_H__
+
+/*CAMCMD*/
+#define _SECCAM_POLLING BIT(31)
+#define _SECCAM_CLR BIT(30)
+#define _SECCAM_WE BIT(16)
+#define _SECCAM_ADR_MSK 0x000000FF
+#define _SECCAM_ADR_SHT 0
+
+/*CAMDBG*/
+#define _SECCAM_INFO BIT(31)
+#define _SEC_KEYFOUND BIT(30)
+#define _SEC_CONFIG_MSK 0x3F000000
+#define _SEC_CONFIG_SHT 24
+#define _SEC_KEYCONTENT_MSK 0x00FFFFFF
+#define _SEC_KEYCONTENT_SHT 0
+
+/*SECCFG*/
+#define _NOSKMC BIT(5)
+#define _SKBYA2 BIT(4)
+#define _RXDEC BIT(3)
+#define _TXENC BIT(2)
+#define _RXUSEDK BIT(1)
+#define _TXUSEDK BIT(0)
+
+
+#endif /*__RTL8712_SECURITY_BITDEF_H__*/
+
diff --git a/drivers/staging/rtl8712/rtl8712_spec.h b/drivers/staging/rtl8712/rtl8712_spec.h
new file mode 100644
index 000000000..af11b44b1
--- /dev/null
+++ b/drivers/staging/rtl8712/rtl8712_spec.h
@@ -0,0 +1,135 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2010 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ * Modifications for inclusion into the Linux staging tree are
+ * Copyright(c) 2010 Larry Finger. All rights reserved.
+ *
+ * Contact information:
+ * WLAN FAE <wlanfae@realtek.com>
+ * Larry Finger <Larry.Finger@lwfinger.net>
+ *
+ ******************************************************************************/
+#ifndef __RTL8712_SPEC_H__
+#define __RTL8712_SPEC_H__
+
+#define RTL8712_IOBASE_TXPKT 0x10200000 /*IOBASE_TXPKT*/
+#define RTL8712_IOBASE_RXPKT 0x10210000 /*IOBASE_RXPKT*/
+#define RTL8712_IOBASE_RXCMD 0x10220000 /*IOBASE_RXCMD*/
+#define RTL8712_IOBASE_TXSTATUS 0x10230000 /*IOBASE_TXSTATUS*/
+#define RTL8712_IOBASE_RXSTATUS 0x10240000 /*IOBASE_RXSTATUS*/
+#define RTL8712_IOBASE_IOREG 0x10250000 /*IOBASE_IOREG ADDR*/
+#define RTL8712_IOBASE_SCHEDULER 0x10260000 /*IOBASE_SCHEDULE*/
+
+#define RTL8712_IOBASE_TRXDMA 0x10270000 /*IOBASE_TRXDMA*/
+#define RTL8712_IOBASE_TXLLT 0x10280000 /*IOBASE_TXLLT*/
+#define RTL8712_IOBASE_WMAC 0x10290000 /*IOBASE_WMAC*/
+#define RTL8712_IOBASE_FW2HW 0x102A0000 /*IOBASE_FW2HW*/
+#define RTL8712_IOBASE_ACCESS_PHYREG 0x102B0000 /*IOBASE_ACCESS_PHYREG*/
+
+#define RTL8712_IOBASE_FF 0x10300000 /*IOBASE_FIFO 0x1031000~0x103AFFFF*/
+
+
+/*IOREG Offset for 8712*/
+#define RTL8712_SYSCFG_ RTL8712_IOBASE_IOREG
+#define RTL8712_CMDCTRL_ (RTL8712_IOBASE_IOREG + 0x40)
+#define RTL8712_MACIDSETTING_ (RTL8712_IOBASE_IOREG + 0x50)
+#define RTL8712_TIMECTRL_ (RTL8712_IOBASE_IOREG + 0x80)
+#define RTL8712_FIFOCTRL_ (RTL8712_IOBASE_IOREG + 0xA0)
+#define RTL8712_RATECTRL_ (RTL8712_IOBASE_IOREG + 0x160)
+#define RTL8712_EDCASETTING_ (RTL8712_IOBASE_IOREG + 0x1D0)
+#define RTL8712_WMAC_ (RTL8712_IOBASE_IOREG + 0x200)
+#define RTL8712_SECURITY_ (RTL8712_IOBASE_IOREG + 0x240)
+#define RTL8712_POWERSAVE_ (RTL8712_IOBASE_IOREG + 0x260)
+#define RTL8712_GP_ (RTL8712_IOBASE_IOREG + 0x2E0)
+#define RTL8712_INTERRUPT_ (RTL8712_IOBASE_IOREG + 0x300)
+#define RTL8712_DEBUGCTRL_ (RTL8712_IOBASE_IOREG + 0x310)
+#define RTL8712_OFFLOAD_ (RTL8712_IOBASE_IOREG + 0x2D0)
+
+
+/*FIFO for 8712*/
+#define RTL8712_DMA_BCNQ (RTL8712_IOBASE_FF + 0x10000)
+#define RTL8712_DMA_MGTQ (RTL8712_IOBASE_FF + 0x20000)
+#define RTL8712_DMA_BMCQ (RTL8712_IOBASE_FF + 0x30000)
+#define RTL8712_DMA_VOQ (RTL8712_IOBASE_FF + 0x40000)
+#define RTL8712_DMA_VIQ (RTL8712_IOBASE_FF + 0x50000)
+#define RTL8712_DMA_BEQ (RTL8712_IOBASE_FF + 0x60000)
+#define RTL8712_DMA_BKQ (RTL8712_IOBASE_FF + 0x70000)
+#define RTL8712_DMA_RX0FF (RTL8712_IOBASE_FF + 0x80000)
+#define RTL8712_DMA_H2CCMD (RTL8712_IOBASE_FF + 0x90000)
+#define RTL8712_DMA_C2HCMD (RTL8712_IOBASE_FF + 0xA0000)
+
+
+/*------------------------------*/
+
+/*BIT 16 15*/
+#define DID_SDIO_LOCAL 0 /* 0 0*/
+#define DID_WLAN_IOREG 1 /* 0 1*/
+#define DID_WLAN_FIFO 3 /* 1 1*/
+#define DID_UNDEFINE (-1)
+
+#define CMD_ADDR_MAPPING_SHIFT 2 /*SDIO CMD ADDR MAPPING,
+ *shift 2 bit for match
+ * offset[14:2]*/
+
+/*Offset for SDIO LOCAL*/
+#define OFFSET_SDIO_LOCAL 0x0FFF
+
+/*Offset for WLAN IOREG*/
+#define OFFSET_WLAN_IOREG 0x0FFF
+
+/*Offset for WLAN FIFO*/
+#define OFFSET_TX_BCNQ 0x0300
+#define OFFSET_TX_HIQ 0x0310
+#define OFFSET_TX_CMDQ 0x0320
+#define OFFSET_TX_MGTQ 0x0330
+#define OFFSET_TX_HCCAQ 0x0340
+#define OFFSET_TX_VOQ 0x0350
+#define OFFSET_TX_VIQ 0x0360
+#define OFFSET_TX_BEQ 0x0370
+#define OFFSET_TX_BKQ 0x0380
+#define OFFSET_RX_RX0FFQ 0x0390
+#define OFFSET_RX_C2HFFQ 0x03A0
+
+#define BK_QID_01 1
+#define BK_QID_02 2
+#define BE_QID_01 0
+#define BE_QID_02 3
+#define VI_QID_01 4
+#define VI_QID_02 5
+#define VO_QID_01 6
+#define VO_QID_02 7
+#define HCCA_QID_01 8
+#define HCCA_QID_02 9
+#define HCCA_QID_03 10
+#define HCCA_QID_04 11
+#define HCCA_QID_05 12
+#define HCCA_QID_06 13
+#define HCCA_QID_07 14
+#define HCCA_QID_08 15
+#define HI_QID 17
+#define CMD_QID 19
+#define MGT_QID 18
+#define BCN_QID 16
+
+#include "rtl8712_regdef.h"
+
+#include "rtl8712_bitdef.h"
+
+#include "basic_types.h"
+
+#endif /* __RTL8712_SPEC_H__ */
+
diff --git a/drivers/staging/rtl8712/rtl8712_syscfg_bitdef.h b/drivers/staging/rtl8712/rtl8712_syscfg_bitdef.h
new file mode 100644
index 000000000..eed09c872
--- /dev/null
+++ b/drivers/staging/rtl8712/rtl8712_syscfg_bitdef.h
@@ -0,0 +1,170 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2010 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ * Modifications for inclusion into the Linux staging tree are
+ * Copyright(c) 2010 Larry Finger. All rights reserved.
+ *
+ * Contact information:
+ * WLAN FAE <wlanfae@realtek.com>
+ * Larry Finger <Larry.Finger@lwfinger.net>
+ *
+ ******************************************************************************/
+#ifndef __RTL8712_SYSCFG_BITDEF_H__
+#define __RTL8712_SYSCFG_BITDEF_H__
+
+/*SYS_PWR_CTRL*/
+/*SRCTRL0*/
+/*SRCTRL1*/
+/*SYS_CLKR*/
+
+/*SYS_IOS_CTRL*/
+#define iso_LDR2RP_SHT 8 /* EE Loader to Retention Path*/
+#define iso_LDR2RP BIT(iso_LDR2RP_SHT) /* 1:isolation, 0:attach*/
+
+/*SYS_CTRL*/
+#define FEN_DIO_SDIO_SHT 0
+#define FEN_DIO_SDIO BIT(FEN_DIO_SDIO_SHT)
+#define FEN_SDIO_SHT 1
+#define FEN_SDIO BIT(FEN_SDIO_SHT)
+#define FEN_USBA_SHT 2
+#define FEN_USBA BIT(FEN_USBA_SHT)
+#define FEN_UPLL_SHT 3
+#define FEN_UPLL BIT(FEN_UPLL_SHT)
+#define FEN_USBD_SHT 4
+#define FEN_USBD BIT(FEN_USBD_SHT)
+#define FEN_DIO_PCIE_SHT 5
+#define FEN_DIO_PCIE BIT(FEN_DIO_PCIE_SHT)
+#define FEN_PCIEA_SHT 6
+#define FEN_PCIEA BIT(FEN_PCIEA_SHT)
+#define FEN_PPLL_SHT 7
+#define FEN_PPLL BIT(FEN_PPLL_SHT)
+#define FEN_PCIED_SHT 8
+#define FEN_PCIED BIT(FEN_PCIED_SHT)
+#define FEN_CPUEN_SHT 10
+#define FEN_CPUEN BIT(FEN_CPUEN_SHT)
+#define FEN_DCORE_SHT 11
+#define FEN_DCORE BIT(FEN_DCORE_SHT)
+#define FEN_ELDR_SHT 12
+#define FEN_ELDR BIT(FEN_ELDR_SHT)
+#define PWC_DV2LDR_SHT 13
+#define PWC_DV2LDR BIT(PWC_DV2LDR_SHT) /* Loader Power Enable*/
+
+/*=== SYS_CLKR ===*/
+#define SYS_CLKSEL_SHT 0
+#define SYS_CLKSEL BIT(SYS_CLKSEL_SHT) /* System Clock 80MHz*/
+#define PS_CLKSEL_SHT 1
+#define PS_CLKSEL BIT(PS_CLKSEL_SHT) /*System power save
+ * clock select.*/
+#define CPU_CLKSEL_SHT 2
+#define CPU_CLKSEL BIT(CPU_CLKSEL_SHT) /* System Clock select,
+ * 1: AFE source,
+ * 0: System clock(L-Bus)*/
+#define INT32K_EN_SHT 3
+#define INT32K_EN BIT(INT32K_EN_SHT)
+#define MACSLP_SHT 4
+#define MACSLP BIT(MACSLP_SHT)
+#define MAC_CLK_EN_SHT 11
+#define MAC_CLK_EN BIT(MAC_CLK_EN_SHT) /* MAC Clock Enable.*/
+#define SYS_CLK_EN_SHT 12
+#define SYS_CLK_EN BIT(SYS_CLK_EN_SHT)
+#define RING_CLK_EN_SHT 13
+#define RING_CLK_EN BIT(RING_CLK_EN_SHT)
+#define SWHW_SEL_SHT 14
+#define SWHW_SEL BIT(SWHW_SEL_SHT) /* Load done,
+ * control path switch.*/
+#define FWHW_SEL_SHT 15
+#define FWHW_SEL BIT(FWHW_SEL_SHT) /* Sleep exit,
+ * control path switch.*/
+
+/*9346CR*/
+#define _VPDIDX_MSK 0xFF00
+#define _VPDIDX_SHT 8
+#define _EEM_MSK 0x00C0
+#define _EEM_SHT 6
+#define _EEM0 BIT(6)
+#define _EEM1 BIT(7)
+#define _EEPROM_EN BIT(5)
+#define _9356SEL BIT(4)
+#define _EECS BIT(3)
+#define _EESK BIT(2)
+#define _EEDI BIT(1)
+#define _EEDO BIT(0)
+
+/*AFE_MISC*/
+#define AFE_MISC_USB_MBEN_SHT 7
+#define AFE_MISC_USB_MBEN BIT(AFE_MISC_USB_MBEN_SHT)
+#define AFE_MISC_USB_BGEN_SHT 6
+#define AFE_MISC_USB_BGEN BIT(AFE_MISC_USB_BGEN_SHT)
+#define AFE_MISC_LD12_VDAJ_SHT 4
+#define AFE_MISC_LD12_VDAJ_MSK 0X0030
+#define AFE_MISC_LD12_VDAJ BIT(AFE_MISC_LD12_VDAJ_SHT)
+#define AFE_MISC_I32_EN_SHT 3
+#define AFE_MISC_I32_EN BIT(AFE_MISC_I32_EN_SHT)
+#define AFE_MISC_E32_EN_SHT 2
+#define AFE_MISC_E32_EN BIT(AFE_MISC_E32_EN_SHT)
+#define AFE_MISC_MBEN_SHT 1
+#define AFE_MISC_MBEN BIT(AFE_MISC_MBEN_SHT)/* Enable AFE Macro
+ * Block's Mbias.*/
+#define AFE_MISC_BGEN_SHT 0
+#define AFE_MISC_BGEN BIT(AFE_MISC_BGEN_SHT)/* Enable AFE Macro
+ * Block's Bandgap.*/
+
+
+/*--------------------------------------------------------------------------*/
+/* SPS1_CTRL bits (Offset 0x18-1E, 56bits)*/
+/*--------------------------------------------------------------------------*/
+#define SPS1_SWEN BIT(1) /* Enable vsps18 SW Macro Block.*/
+#define SPS1_LDEN BIT(0) /* Enable VSPS12 LDO Macro block.*/
+
+
+/*----------------------------------------------------------------------------*/
+/* LDOA15_CTRL bits (Offset 0x20, 8bits)*/
+/*----------------------------------------------------------------------------*/
+#define LDA15_EN BIT(0) /* Enable LDOA15 Macro Block*/
+
+
+/*----------------------------------------------------------------------------*/
+/* 8192S LDOV12D_CTRL bit (Offset 0x21, 8bits)*/
+/*----------------------------------------------------------------------------*/
+#define LDV12_EN BIT(0) /* Enable LDOVD12 Macro Block*/
+#define LDV12_SDBY BIT(1) /* LDOVD12 standby mode*/
+
+/*CLK_PS_CTRL*/
+#define _CLK_GATE_EN BIT(0)
+
+
+/* EFUSE_CTRL*/
+#define EF_FLAG BIT(31) /* Access Flag, Write:1;
+ * Read:0*/
+#define EF_PGPD 0x70000000 /* E-fuse Program time*/
+#define EF_RDT 0x0F000000 /* E-fuse read time: in the
+ * unit of cycle time*/
+#define EF_PDN_EN BIT(19) /* EFuse Power down enable*/
+#define ALD_EN BIT(18) /* Autoload Enable*/
+#define EF_ADDR 0x0003FF00 /* Access Address*/
+#define EF_DATA 0x000000FF /* Access Data*/
+
+/* EFUSE_TEST*/
+#define LDOE25_EN BIT(31) /* Enable LDOE25 Macro Block*/
+
+/* EFUSE_CLK_CTRL*/
+#define EFUSE_CLK_EN BIT(1) /* E-Fuse Clock Enable*/
+#define EFUSE_CLK_SEL BIT(0) /* E-Fuse Clock Select,
+ * 0:500K, 1:40M*/
+
+#endif /*__RTL8712_SYSCFG_BITDEF_H__*/
+
diff --git a/drivers/staging/rtl8712/rtl8712_syscfg_regdef.h b/drivers/staging/rtl8712/rtl8712_syscfg_regdef.h
new file mode 100644
index 000000000..767dfdf8d
--- /dev/null
+++ b/drivers/staging/rtl8712/rtl8712_syscfg_regdef.h
@@ -0,0 +1,56 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2010 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ * Modifications for inclusion into the Linux staging tree are
+ * Copyright(c) 2010 Larry Finger. All rights reserved.
+ *
+ * Contact information:
+ * WLAN FAE <wlanfae@realtek.com>
+ * Larry Finger <Larry.Finger@lwfinger.net>
+ *
+ ******************************************************************************/
+#ifndef __RTL8712_SYSCFG_REGDEF_H__
+#define __RTL8712_SYSCFG_REGDEF_H__
+
+
+#define SYS_ISO_CTRL (RTL8712_SYSCFG_ + 0x0000)
+#define SYS_FUNC_EN (RTL8712_SYSCFG_ + 0x0002)
+#define PMC_FSM (RTL8712_SYSCFG_ + 0x0004)
+#define SYS_CLKR (RTL8712_SYSCFG_ + 0x0008)
+#define EE_9346CR (RTL8712_SYSCFG_ + 0x000A)
+#define EE_VPD (RTL8712_SYSCFG_ + 0x000C)
+#define AFE_MISC (RTL8712_SYSCFG_ + 0x0010)
+#define SPS0_CTRL (RTL8712_SYSCFG_ + 0x0011)
+#define SPS1_CTRL (RTL8712_SYSCFG_ + 0x0018)
+#define RF_CTRL (RTL8712_SYSCFG_ + 0x001F)
+#define LDOA15_CTRL (RTL8712_SYSCFG_ + 0x0020)
+#define LDOV12D_CTRL (RTL8712_SYSCFG_ + 0x0021)
+#define LDOHCI12_CTRL (RTL8712_SYSCFG_ + 0x0022)
+#define LDO_USB_CTRL (RTL8712_SYSCFG_ + 0x0023)
+#define LPLDO_CTRL (RTL8712_SYSCFG_ + 0x0024)
+#define AFE_XTAL_CTRL (RTL8712_SYSCFG_ + 0x0026)
+#define AFE_PLL_CTRL (RTL8712_SYSCFG_ + 0x0028)
+#define EFUSE_CTRL (RTL8712_SYSCFG_ + 0x0030)
+#define EFUSE_TEST (RTL8712_SYSCFG_ + 0x0034)
+#define PWR_DATA (RTL8712_SYSCFG_ + 0x0038)
+#define DPS_TIMER (RTL8712_SYSCFG_ + 0x003C)
+#define RCLK_MON (RTL8712_SYSCFG_ + 0x003E)
+#define EFUSE_CLK_CTRL (RTL8712_SYSCFG_ + 0x02F8)
+
+
+#endif /*__RTL8712_SYSCFG_REGDEF_H__*/
+
diff --git a/drivers/staging/rtl8712/rtl8712_timectrl_bitdef.h b/drivers/staging/rtl8712/rtl8712_timectrl_bitdef.h
new file mode 100644
index 000000000..724421582
--- /dev/null
+++ b/drivers/staging/rtl8712/rtl8712_timectrl_bitdef.h
@@ -0,0 +1,63 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2010 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ *
+ ******************************************************************************/
+#ifndef __RTL8712_TIMECTRL_BITDEF_H__
+#define __RTL8712_TIMECTRL_BITDEF_H__
+
+/*TSFTR*/
+/*SLOT*/
+/*USTIME*/
+
+/*TUBASE*/
+#define _TUBASE_MSK 0x07FF
+
+/*SIFS_CCK*/
+#define _SIFS_CCK_TRX_MSK 0xFF00
+#define _SIFS_CCK_TRX_SHT 0x8
+#define _SIFS_CCK_CTX_MSK 0x00FF
+#define _SIFS_CCK_CTX_SHT 0
+
+/*SIFS_OFDM*/
+#define _SIFS_OFDM_TRX_MSK 0xFF00
+#define _SIFS_OFDM_TRX_SHT 0x8
+#define _SIFS_OFDM_CTX_MSK 0x00FF
+#define _SIFS_OFDM_CTX_SHT 0
+
+/*PIFS*/
+/*ACKTO*/
+/*EIFS*/
+/*BCNITV*/
+/*ATIMWND*/
+
+/*DRVERLYINT*/
+#define _ENSWBCN BIT(15)
+#define _DRVERLY_TU_MSK 0x0FF0
+#define _DRVERLY_TU_SHT 4
+#define _DRVERLY_US_MSK 0x000F
+#define _DRVERLY_US_SHT 0
+
+/*BCNDMATIM*/
+#define _BCNDMATIM_MSK 0x03FF
+
+/*BCNERRTH*/
+/*MLT*/
+
+
+#endif /* __RTL8712_TIMECTRL_BITDEF_H__*/
+
diff --git a/drivers/staging/rtl8712/rtl8712_timectrl_regdef.h b/drivers/staging/rtl8712/rtl8712_timectrl_regdef.h
new file mode 100644
index 000000000..106916c7e
--- /dev/null
+++ b/drivers/staging/rtl8712/rtl8712_timectrl_regdef.h
@@ -0,0 +1,39 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2010 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ *
+ ******************************************************************************/
+#ifndef __RTL8712_TIMECTRL_REGDEF_H__
+#define __RTL8712_TIMECTRL_REGDEF_H__
+
+#define TSFTR (RTL8712_TIMECTRL_ + 0x00)
+#define USTIME (RTL8712_TIMECTRL_ + 0x08)
+#define SLOT (RTL8712_TIMECTRL_ + 0x09)
+#define TUBASE (RTL8712_TIMECTRL_ + 0x0A)
+#define SIFS_CCK (RTL8712_TIMECTRL_ + 0x0C)
+#define SIFS_OFDM (RTL8712_TIMECTRL_ + 0x0E)
+#define PIFS (RTL8712_TIMECTRL_ + 0x10)
+#define ACKTO (RTL8712_TIMECTRL_ + 0x11)
+#define EIFS (RTL8712_TIMECTRL_ + 0x12)
+#define BCNITV (RTL8712_TIMECTRL_ + 0x14)
+#define ATIMWND (RTL8712_TIMECTRL_ + 0x16)
+#define DRVERLYINT (RTL8712_TIMECTRL_ + 0x18)
+#define BCNDMATIM (RTL8712_TIMECTRL_ + 0x1A)
+#define BCNERRTH (RTL8712_TIMECTRL_ + 0x1C)
+#define MLT (RTL8712_TIMECTRL_ + 0x1D)
+
+#endif /* __RTL8712_TIMECTRL_REGDEF_H__ */
diff --git a/drivers/staging/rtl8712/rtl8712_wmac_bitdef.h b/drivers/staging/rtl8712/rtl8712_wmac_bitdef.h
new file mode 100644
index 000000000..61a3603aa
--- /dev/null
+++ b/drivers/staging/rtl8712/rtl8712_wmac_bitdef.h
@@ -0,0 +1,62 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2010 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ * Modifications for inclusion into the Linux staging tree are
+ * Copyright(c) 2010 Larry Finger. All rights reserved.
+ *
+ * Contact information:
+ * WLAN FAE <wlanfae@realtek.com>
+ * Larry Finger <Larry.Finger@lwfinger.net>
+ *
+ ******************************************************************************/
+#ifndef __RTL8712_WMAC_BITDEF_H__
+#define __RTL8712_WMAC_BITDEF_H__
+
+/*NAVCTRL*/
+#define _NAV_UPPER_EN BIT(18)
+#define _NAV_MTO_EN BIT(17)
+#define _NAV_UPPER BIT(16)
+#define _NAV_MTO_MSK 0xFF00
+#define _NAV_MTO_SHT 8
+#define _RTSRST_MSK 0x00FF
+#define _RTSRST_SHT 0
+
+/*BWOPMODE*/
+#define _20MHZBW BIT(2)
+
+/*BACAMCMD*/
+#define _BACAM_POLL BIT(31)
+#define _BACAM_RST BIT(17)
+#define _BACAM_RW BIT(16)
+#define _BACAM_ADDR_MSK 0x0000007F
+#define _BACAM_ADDR_SHT 0
+
+/*LBDLY*/
+#define _LBDLY_MSK 0x1F
+
+/*FWDLY*/
+#define _FWDLY_MSK 0x0F
+
+/*RXERR_RPT*/
+#define _RXERR_RPT_SEL_MSK 0xF0000000
+#define _RXERR_RPT_SEL_SHT 28
+#define _RPT_CNT_MSK 0x000FFFFF
+#define _RPT_CNT_SHT 0
+
+
+#endif /*__RTL8712_WMAC_BITDEF_H__*/
+
diff --git a/drivers/staging/rtl8712/rtl8712_wmac_regdef.h b/drivers/staging/rtl8712/rtl8712_wmac_regdef.h
new file mode 100644
index 000000000..d9f8347ab
--- /dev/null
+++ b/drivers/staging/rtl8712/rtl8712_wmac_regdef.h
@@ -0,0 +1,49 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2010 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ * Modifications for inclusion into the Linux staging tree are
+ * Copyright(c) 2010 Larry Finger. All rights reserved.
+ *
+ * Contact information:
+ * WLAN FAE <wlanfae@realtek.com>
+ * Larry Finger <Larry.Finger@lwfinger.net>
+ *
+ ******************************************************************************/
+#ifndef __RTL8712_WMAC_REGDEF_H__
+#define __RTL8712_WMAC_REGDEF_H__
+
+#define NAVCTRL (RTL8712_WMAC_ + 0x00)
+#define BWOPMODE (RTL8712_WMAC_ + 0x03)
+#define BACAMCMD (RTL8712_WMAC_ + 0x04)
+#define BACAMCONTENT (RTL8712_WMAC_ + 0x08)
+#define LBDLY (RTL8712_WMAC_ + 0x10)
+#define FWDLY (RTL8712_WMAC_ + 0x11)
+#define HWPC_RX_CTRL (RTL8712_WMAC_ + 0x18)
+#define MQ (RTL8712_WMAC_ + 0x20)
+#define MA (RTL8712_WMAC_ + 0x22)
+#define MS (RTL8712_WMAC_ + 0x24)
+#define CLM_RESULT (RTL8712_WMAC_ + 0x27)
+#define NHM_RPI_CNT (RTL8712_WMAC_ + 0x28)
+#define RXERR_RPT (RTL8712_WMAC_ + 0x30)
+#define NAV_PROT_LEN (RTL8712_WMAC_ + 0x34)
+#define CFEND_TH (RTL8712_WMAC_ + 0x36)
+#define AMPDU_MIN_SPACE (RTL8712_WMAC_ + 0x37)
+#define TXOP_STALL_CTRL (RTL8712_WMAC_ + 0x38)
+
+
+#endif /*__RTL8712_WMAC_REGDEF_H__*/
+
diff --git a/drivers/staging/rtl8712/rtl8712_xmit.c b/drivers/staging/rtl8712/rtl8712_xmit.c
new file mode 100644
index 000000000..a3093ac12
--- /dev/null
+++ b/drivers/staging/rtl8712/rtl8712_xmit.c
@@ -0,0 +1,764 @@
+/******************************************************************************
+ * rtl8712_xmit.c
+ *
+ * Copyright(c) 2007 - 2010 Realtek Corporation. All rights reserved.
+ * Linux device driver for RTL8192SU
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ * Modifications for inclusion into the Linux staging tree are
+ * Copyright(c) 2010 Larry Finger. All rights reserved.
+ *
+ * Contact information:
+ * WLAN FAE <wlanfae@realtek.com>
+ * Larry Finger <Larry.Finger@lwfinger.net>
+ *
+ ******************************************************************************/
+
+#define _RTL8712_XMIT_C_
+
+#include "osdep_service.h"
+#include "drv_types.h"
+#include "wifi.h"
+#include "osdep_intf.h"
+#include "usb_ops.h"
+
+static void dump_xframe(struct _adapter *padapter,
+ struct xmit_frame *pxmitframe);
+static void update_txdesc(struct xmit_frame *pxmitframe, uint *pmem, int sz);
+
+sint _r8712_init_hw_txqueue(struct hw_txqueue *phw_txqueue, u8 ac_tag)
+{
+ phw_txqueue->ac_tag = ac_tag;
+ switch (ac_tag) {
+ case BE_QUEUE_INX:
+ phw_txqueue->ff_hwaddr = RTL8712_DMA_BEQ;
+ break;
+ case BK_QUEUE_INX:
+ phw_txqueue->ff_hwaddr = RTL8712_DMA_BKQ;
+ break;
+ case VI_QUEUE_INX:
+ phw_txqueue->ff_hwaddr = RTL8712_DMA_VIQ;
+ break;
+ case VO_QUEUE_INX:
+ phw_txqueue->ff_hwaddr = RTL8712_DMA_VOQ;
+ break;
+ case BMC_QUEUE_INX:
+ phw_txqueue->ff_hwaddr = RTL8712_DMA_BEQ;
+ break;
+ }
+ return _SUCCESS;
+}
+
+int r8712_txframes_sta_ac_pending(struct _adapter *padapter,
+ struct pkt_attrib *pattrib)
+{
+ struct sta_info *psta;
+ struct tx_servq *ptxservq;
+ int priority = pattrib->priority;
+
+ psta = pattrib->psta;
+ switch (priority) {
+ case 1:
+ case 2:
+ ptxservq = &(psta->sta_xmitpriv.bk_q);
+ break;
+ case 4:
+ case 5:
+ ptxservq = &(psta->sta_xmitpriv.vi_q);
+ break;
+ case 6:
+ case 7:
+ ptxservq = &(psta->sta_xmitpriv.vo_q);
+ break;
+ case 0:
+ case 3:
+ default:
+ ptxservq = &(psta->sta_xmitpriv.be_q);
+ break;
+ }
+ return ptxservq->qcnt;
+}
+
+static u32 get_ff_hwaddr(struct xmit_frame *pxmitframe)
+{
+ u32 addr = 0;
+ struct pkt_attrib *pattrib = &pxmitframe->attrib;
+ struct _adapter *padapter = pxmitframe->padapter;
+ struct dvobj_priv *pdvobj = (struct dvobj_priv *)&padapter->dvobjpriv;
+
+ if (pxmitframe->frame_tag == TXAGG_FRAMETAG)
+ addr = RTL8712_DMA_H2CCMD;
+ else if (pxmitframe->frame_tag == MGNT_FRAMETAG)
+ addr = RTL8712_DMA_MGTQ;
+ else if (pdvobj->nr_endpoint == 6) {
+ switch (pattrib->priority) {
+ case 0:
+ case 3:
+ addr = RTL8712_DMA_BEQ;
+ break;
+ case 1:
+ case 2:
+ addr = RTL8712_DMA_BKQ;
+ break;
+ case 4:
+ case 5:
+ addr = RTL8712_DMA_VIQ;
+ break;
+ case 6:
+ case 7:
+ addr = RTL8712_DMA_VOQ;
+ break;
+ case 0x10:
+ case 0x11:
+ case 0x12:
+ case 0x13:
+ addr = RTL8712_DMA_H2CCMD;
+ break;
+ default:
+ addr = RTL8712_DMA_BEQ;
+ break;
+ }
+ } else if (pdvobj->nr_endpoint == 4) {
+ switch (pattrib->qsel) {
+ case 0:
+ case 3:
+ case 1:
+ case 2:
+ addr = RTL8712_DMA_BEQ;/*RTL8712_EP_LO;*/
+ break;
+ case 4:
+ case 5:
+ case 6:
+ case 7:
+ addr = RTL8712_DMA_VOQ;/*RTL8712_EP_HI;*/
+ break;
+ case 0x10:
+ case 0x11:
+ case 0x12:
+ case 0x13:
+ addr = RTL8712_DMA_H2CCMD;
+ break;
+ default:
+ addr = RTL8712_DMA_BEQ;/*RTL8712_EP_LO;*/
+ break;
+ }
+ }
+ return addr;
+}
+
+static struct xmit_frame *dequeue_one_xmitframe(struct xmit_priv *pxmitpriv,
+ struct hw_xmit *phwxmit,
+ struct tx_servq *ptxservq,
+ struct __queue *pframe_queue)
+{
+ struct list_head *xmitframe_plist, *xmitframe_phead;
+ struct xmit_frame *pxmitframe = NULL;
+
+ xmitframe_phead = &pframe_queue->queue;
+ xmitframe_plist = xmitframe_phead->next;
+ if ((end_of_queue_search(xmitframe_phead, xmitframe_plist)) == false) {
+ pxmitframe = LIST_CONTAINOR(xmitframe_plist,
+ struct xmit_frame, list);
+ list_del_init(&pxmitframe->list);
+ ptxservq->qcnt--;
+ phwxmit->txcmdcnt++;
+ }
+ return pxmitframe;
+}
+
+static struct xmit_frame *dequeue_xframe_ex(struct xmit_priv *pxmitpriv,
+ struct hw_xmit *phwxmit_i, sint entry)
+{
+ unsigned long irqL0;
+ struct list_head *sta_plist, *sta_phead;
+ struct hw_xmit *phwxmit;
+ struct tx_servq *ptxservq = NULL;
+ struct __queue *pframe_queue = NULL;
+ struct xmit_frame *pxmitframe = NULL;
+ int i, inx[4];
+ int j, tmp, acirp_cnt[4];
+
+ /*entry indx: 0->vo, 1->vi, 2->be, 3->bk.*/
+ inx[0] = 0; acirp_cnt[0] = pxmitpriv->voq_cnt;
+ inx[1] = 1; acirp_cnt[1] = pxmitpriv->viq_cnt;
+ inx[2] = 2; acirp_cnt[2] = pxmitpriv->beq_cnt;
+ inx[3] = 3; acirp_cnt[3] = pxmitpriv->bkq_cnt;
+ for (i = 0; i < 4; i++) {
+ for (j = i + 1; j < 4; j++) {
+ if (acirp_cnt[j] < acirp_cnt[i]) {
+ tmp = acirp_cnt[i];
+ acirp_cnt[i] = acirp_cnt[j];
+ acirp_cnt[j] = tmp;
+ tmp = inx[i];
+ inx[i] = inx[j];
+ inx[j] = tmp;
+ }
+ }
+ }
+ spin_lock_irqsave(&pxmitpriv->lock, irqL0);
+ for (i = 0; i < entry; i++) {
+ phwxmit = phwxmit_i + inx[i];
+ sta_phead = &phwxmit->sta_queue->queue;
+ sta_plist = sta_phead->next;
+ while ((end_of_queue_search(sta_phead, sta_plist)) == false) {
+ ptxservq = LIST_CONTAINOR(sta_plist, struct tx_servq,
+ tx_pending);
+ pframe_queue = &ptxservq->sta_pending;
+ pxmitframe = dequeue_one_xmitframe(pxmitpriv, phwxmit,
+ ptxservq, pframe_queue);
+ if (pxmitframe) {
+ phwxmit->accnt--;
+ goto exit_dequeue_xframe_ex;
+ }
+ sta_plist = sta_plist->next;
+ /*Remove sta node when there are no pending packets.*/
+ if (list_empty(&pframe_queue->queue)) {
+ /* must be done after sta_plist->next
+ * and before break
+ */
+ list_del_init(&ptxservq->tx_pending);
+ }
+ }
+ }
+exit_dequeue_xframe_ex:
+ spin_unlock_irqrestore(&pxmitpriv->lock, irqL0);
+ return pxmitframe;
+}
+
+void r8712_do_queue_select(struct _adapter *padapter,
+ struct pkt_attrib *pattrib)
+{
+ unsigned int qsel = 0;
+ struct dvobj_priv *pdvobj = (struct dvobj_priv *)&padapter->dvobjpriv;
+
+ if (pdvobj->nr_endpoint == 6)
+ qsel = (unsigned int) pattrib->priority;
+ else if (pdvobj->nr_endpoint == 4) {
+ qsel = (unsigned int) pattrib->priority;
+ if (qsel == 0 || qsel == 3)
+ qsel = 3;
+ else if (qsel == 1 || qsel == 2)
+ qsel = 1;
+ else if (qsel == 4 || qsel == 5)
+ qsel = 5;
+ else if (qsel == 6 || qsel == 7)
+ qsel = 7;
+ else
+ qsel = 3;
+ }
+ pattrib->qsel = qsel;
+}
+
+#ifdef CONFIG_R8712_TX_AGGR
+u8 r8712_construct_txaggr_cmd_desc(struct xmit_buf *pxmitbuf)
+{
+ struct tx_desc *ptx_desc = (struct tx_desc *)pxmitbuf->pbuf;
+
+ /* Fill up TxCmd Descriptor according as USB FW Tx Aaggregation info.*/
+ /* dw0 */
+ ptx_desc->txdw0 = cpu_to_le32(CMD_HDR_SZ&0xffff);
+ ptx_desc->txdw0 |=
+ cpu_to_le32(((TXDESC_SIZE+OFFSET_SZ)<<OFFSET_SHT)&0x00ff0000);
+ ptx_desc->txdw0 |= cpu_to_le32(OWN | FSG | LSG);
+
+ /* dw1 */
+ ptx_desc->txdw1 |= cpu_to_le32((0x13<<QSEL_SHT)&0x00001f00);
+
+ return _SUCCESS;
+}
+
+u8 r8712_construct_txaggr_cmd_hdr(struct xmit_buf *pxmitbuf)
+{
+ struct xmit_frame *pxmitframe = (struct xmit_frame *)
+ pxmitbuf->priv_data;
+ struct _adapter *padapter = pxmitframe->padapter;
+ struct cmd_priv *pcmdpriv = &(padapter->cmdpriv);
+ struct cmd_hdr *pcmd_hdr = (struct cmd_hdr *)
+ (pxmitbuf->pbuf + TXDESC_SIZE);
+
+ /* Fill up Cmd Header for USB FW Tx Aggregation.*/
+ /* dw0 */
+ pcmd_hdr->cmd_dw0 = cpu_to_le32((GEN_CMD_CODE(_AMSDU_TO_AMPDU) << 16) |
+ (pcmdpriv->cmd_seq << 24));
+ pcmdpriv->cmd_seq++;
+
+ return _SUCCESS;
+}
+
+u8 r8712_append_mpdu_unit(struct xmit_buf *pxmitbuf,
+ struct xmit_frame *pxmitframe)
+{
+ struct _adapter *padapter = pxmitframe->padapter;
+ struct tx_desc *ptx_desc = (struct tx_desc *)pxmitbuf->pbuf;
+ int last_txcmdsz = 0;
+ int padding_sz = 0;
+
+ /* 802.3->802.11 convertor */
+ r8712_xmitframe_coalesce(padapter, pxmitframe->pkt, pxmitframe);
+ /* free skb struct */
+ r8712_xmit_complete(padapter, pxmitframe);
+ if (pxmitframe->attrib.ether_type != 0x0806) {
+ if ((pxmitframe->attrib.ether_type != 0x888e) &&
+ (pxmitframe->attrib.dhcp_pkt != 1)) {
+ r8712_issue_addbareq_cmd(padapter,
+ pxmitframe->attrib.priority);
+ }
+ }
+ pxmitframe->last[0] = 1;
+ update_txdesc(pxmitframe, (uint *)(pxmitframe->buf_addr),
+ pxmitframe->attrib.last_txcmdsz);
+ /*padding zero */
+ last_txcmdsz = pxmitframe->attrib.last_txcmdsz;
+ padding_sz = (8 - (last_txcmdsz % 8));
+ if ((last_txcmdsz % 8) != 0) {
+ int i;
+
+ for (i = 0; i < padding_sz; i++)
+ *(pxmitframe->buf_addr+TXDESC_SIZE+last_txcmdsz+i) = 0;
+ }
+ /* Add the new mpdu's length */
+ ptx_desc->txdw0 = cpu_to_le32((ptx_desc->txdw0&0xffff0000) |
+ ((ptx_desc->txdw0&0x0000ffff)+
+ ((TXDESC_SIZE+last_txcmdsz+padding_sz)&0x0000ffff)));
+
+ return _SUCCESS;
+}
+
+
+u8 r8712_xmitframe_aggr_1st(struct xmit_buf *pxmitbuf,
+ struct xmit_frame *pxmitframe)
+{
+ /* linux complete context doesnt need to protect */
+ pxmitframe->pxmitbuf = pxmitbuf;
+ pxmitbuf->priv_data = pxmitframe;
+ pxmitframe->pxmit_urb[0] = pxmitbuf->pxmit_urb[0];
+ /* buffer addr assoc */
+ pxmitframe->buf_addr = pxmitbuf->pbuf+TXDESC_SIZE+CMD_HDR_SZ;
+ /*RTL8712_DMA_H2CCMD */
+ r8712_construct_txaggr_cmd_desc(pxmitbuf);
+ r8712_construct_txaggr_cmd_hdr(pxmitbuf);
+ if (r8712_append_mpdu_unit(pxmitbuf, pxmitframe) == _SUCCESS)
+ pxmitbuf->aggr_nr = 1;
+
+ return _SUCCESS;
+}
+
+u16 r8712_xmitframe_aggr_next(struct xmit_buf *pxmitbuf,
+ struct xmit_frame *pxmitframe)
+{
+ pxmitframe->pxmitbuf = pxmitbuf;
+ pxmitbuf->priv_data = pxmitframe;
+ pxmitframe->pxmit_urb[0] = pxmitbuf->pxmit_urb[0];
+ /* buffer addr assoc */
+ pxmitframe->buf_addr = pxmitbuf->pbuf + TXDESC_SIZE +
+ (((struct tx_desc *)pxmitbuf->pbuf)->txdw0 & 0x0000ffff);
+ if (r8712_append_mpdu_unit(pxmitbuf, pxmitframe) == _SUCCESS) {
+ r8712_free_xmitframe_ex(&pxmitframe->padapter->xmitpriv,
+ pxmitframe);
+ pxmitbuf->aggr_nr++;
+ }
+
+ return TXDESC_SIZE +
+ (((struct tx_desc *)pxmitbuf->pbuf)->txdw0 & 0x0000ffff);
+}
+
+u8 r8712_dump_aggr_xframe(struct xmit_buf *pxmitbuf,
+ struct xmit_frame *pxmitframe)
+{
+ struct _adapter *padapter = pxmitframe->padapter;
+ struct dvobj_priv *pdvobj = (struct dvobj_priv *) &padapter->dvobjpriv;
+ struct tx_desc *ptxdesc = (struct tx_desc *)pxmitbuf->pbuf;
+ struct cmd_hdr *pcmd_hdr = (struct cmd_hdr *)
+ (pxmitbuf->pbuf + TXDESC_SIZE);
+ u16 total_length = (u16) (ptxdesc->txdw0 & 0xffff);
+
+ /* use 1st xmitframe as media */
+ xmitframe_xmitbuf_attach(pxmitframe, pxmitbuf);
+ pcmd_hdr->cmd_dw0 = cpu_to_le32(((total_length-CMD_HDR_SZ)&0x0000ffff)|
+ (pcmd_hdr->cmd_dw0&0xffff0000));
+
+ /* urb length in cmd_dw1 */
+ pcmd_hdr->cmd_dw1 = cpu_to_le32((pxmitbuf->aggr_nr & 0xff)|
+ ((total_length+TXDESC_SIZE) << 16));
+ pxmitframe->last[0] = 1;
+ pxmitframe->bpending[0] = false;
+ pxmitframe->mem_addr = pxmitbuf->pbuf;
+
+ if ((pdvobj->ishighspeed && ((total_length+TXDESC_SIZE)%0x200) == 0) ||
+ ((!pdvobj->ishighspeed &&
+ ((total_length+TXDESC_SIZE)%0x40) == 0))) {
+ ptxdesc->txdw0 |= cpu_to_le32
+ (((TXDESC_SIZE+OFFSET_SZ+8)<<OFFSET_SHT)&0x00ff0000);
+ /*32 bytes for TX Desc + 8 bytes pending*/
+ } else {
+ ptxdesc->txdw0 |= cpu_to_le32
+ (((TXDESC_SIZE+OFFSET_SZ)<<OFFSET_SHT)&0x00ff0000);
+ /*default = 32 bytes for TX Desc*/
+ }
+ r8712_write_port(pxmitframe->padapter, RTL8712_DMA_H2CCMD,
+ total_length+TXDESC_SIZE, (u8 *)pxmitframe);
+
+ return _SUCCESS;
+}
+
+#endif
+
+static void update_txdesc(struct xmit_frame *pxmitframe, uint *pmem, int sz)
+{
+ uint qsel;
+ struct _adapter *padapter = pxmitframe->padapter;
+ struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
+ struct qos_priv *pqospriv = &pmlmepriv->qospriv;
+ struct security_priv *psecuritypriv = &padapter->securitypriv;
+ struct pkt_attrib *pattrib = &pxmitframe->attrib;
+ struct tx_desc *ptxdesc = (struct tx_desc *)pmem;
+ struct dvobj_priv *pdvobj = (struct dvobj_priv *)&padapter->dvobjpriv;
+#ifdef CONFIG_R8712_TX_AGGR
+ struct cmd_priv *pcmdpriv = (struct cmd_priv *)&padapter->cmdpriv;
+#endif
+ u8 blnSetTxDescOffset;
+ sint bmcst = IS_MCAST(pattrib->ra);
+ struct ht_priv *phtpriv = &pmlmepriv->htpriv;
+ struct tx_desc txdesc_mp;
+
+ memcpy(&txdesc_mp, ptxdesc, sizeof(struct tx_desc));
+ memset(ptxdesc, 0, sizeof(struct tx_desc));
+ /* offset 0 */
+ ptxdesc->txdw0 |= cpu_to_le32(sz&0x0000ffff);
+ if (pdvobj->ishighspeed) {
+ if (((sz + TXDESC_SIZE) % 512) == 0)
+ blnSetTxDescOffset = 1;
+ else
+ blnSetTxDescOffset = 0;
+ } else {
+ if (((sz + TXDESC_SIZE) % 64) == 0)
+ blnSetTxDescOffset = 1;
+ else
+ blnSetTxDescOffset = 0;
+ }
+ if (blnSetTxDescOffset) {
+ /* 32 bytes for TX Desc + 8 bytes pending */
+ ptxdesc->txdw0 |= cpu_to_le32(((TXDESC_SIZE+OFFSET_SZ + 8) <<
+ OFFSET_SHT) & 0x00ff0000);
+ } else {
+ /* default = 32 bytes for TX Desc */
+ ptxdesc->txdw0 |= cpu_to_le32(((TXDESC_SIZE+OFFSET_SZ) <<
+ OFFSET_SHT) & 0x00ff0000);
+ }
+ ptxdesc->txdw0 |= cpu_to_le32(OWN | FSG | LSG);
+ if (pxmitframe->frame_tag == DATA_FRAMETAG) {
+ /* offset 4 */
+ ptxdesc->txdw1 |= cpu_to_le32((pattrib->mac_id)&0x1f);
+
+#ifdef CONFIG_R8712_TX_AGGR
+ /* dirty workaround, need to check if it is aggr cmd. */
+ if ((u8 *)pmem != (u8 *)pxmitframe->pxmitbuf->pbuf) {
+ ptxdesc->txdw0 |= cpu_to_le32
+ ((0x3 << TYPE_SHT)&TYPE_MSK);
+ qsel = (uint)(pattrib->qsel & 0x0000001f);
+ if (qsel == 2)
+ qsel = 0;
+ ptxdesc->txdw1 |= cpu_to_le32
+ ((qsel << QSEL_SHT) & 0x00001f00);
+ ptxdesc->txdw2 = cpu_to_le32
+ ((qsel << RTS_RC_SHT)&0x001f0000);
+ ptxdesc->txdw6 |= cpu_to_le32
+ ((0x5 << RSVD6_SHT)&RSVD6_MSK);
+ } else {
+ ptxdesc->txdw0 |= cpu_to_le32
+ ((0x3 << TYPE_SHT)&TYPE_MSK);
+ ptxdesc->txdw1 |= cpu_to_le32
+ ((0x13 << QSEL_SHT) & 0x00001f00);
+ qsel = (uint)(pattrib->qsel & 0x0000001f);
+ if (qsel == 2)
+ qsel = 0;
+ ptxdesc->txdw2 = cpu_to_le32
+ ((qsel << RTS_RC_SHT)&0x0001f000);
+ ptxdesc->txdw7 |= cpu_to_le32
+ (pcmdpriv->cmd_seq << 24);
+ pcmdpriv->cmd_seq++;
+ }
+ pattrib->qsel = 0x13;
+#else
+ qsel = (uint)(pattrib->qsel & 0x0000001f);
+ ptxdesc->txdw1 |= cpu_to_le32((qsel << QSEL_SHT) & 0x00001f00);
+#endif
+ if (!pqospriv->qos_option)
+ ptxdesc->txdw1 |= cpu_to_le32(BIT(16));/*Non-QoS*/
+ if ((pattrib->encrypt > 0) && !pattrib->bswenc) {
+ switch (pattrib->encrypt) { /*SEC_TYPE*/
+ case _WEP40_:
+ case _WEP104_:
+ ptxdesc->txdw1 |= cpu_to_le32((0x01 << 22) &
+ 0x00c00000);
+ /*KEY_ID when WEP is used;*/
+ ptxdesc->txdw1 |= cpu_to_le32((psecuritypriv->
+ PrivacyKeyIndex << 17) &
+ 0x00060000);
+ break;
+ case _TKIP_:
+ case _TKIP_WTMIC_:
+ ptxdesc->txdw1 |= cpu_to_le32((0x02 << 22) &
+ 0x00c00000);
+ break;
+ case _AES_:
+ ptxdesc->txdw1 |= cpu_to_le32((0x03 << 22) &
+ 0x00c00000);
+ break;
+ case _NO_PRIVACY_:
+ default:
+ break;
+ }
+ }
+ /*offset 8*/
+ if (bmcst)
+ ptxdesc->txdw2 |= cpu_to_le32(BMC);
+
+ /*offset 12*/
+ /* f/w will increase the seqnum by itself, driver pass the
+ * correct priority to fw
+ * fw will check the correct priority for increasing the
+ * seqnum per tid. about usb using 4-endpoint, qsel points out
+ * the correct mapping between AC&Endpoint,
+ * the purpose is that correct mapping lets the MAC release
+ * the AC Queue list correctly. */
+ ptxdesc->txdw3 = cpu_to_le32((pattrib->priority << SEQ_SHT) &
+ 0x0fff0000);
+ if ((pattrib->ether_type != 0x888e) &&
+ (pattrib->ether_type != 0x0806) &&
+ (pattrib->dhcp_pkt != 1)) {
+ /*Not EAP & ARP type data packet*/
+ if (phtpriv->ht_option == 1) { /*B/G/N Mode*/
+ if (phtpriv->ampdu_enable != true)
+ ptxdesc->txdw2 |= cpu_to_le32(BK);
+ }
+ } else {
+ /* EAP data packet and ARP packet.
+ * Use the 1M data rate to send the EAP/ARP packet.
+ * This will maybe make the handshake smooth.
+ */
+ /*driver uses data rate*/
+ ptxdesc->txdw4 = cpu_to_le32(0x80000000);
+ ptxdesc->txdw5 = cpu_to_le32(0x001f8000);/*1M*/
+ }
+ if (pattrib->pctrl == 1) { /* mp tx packets */
+ struct tx_desc *ptxdesc_mp;
+
+ ptxdesc_mp = &txdesc_mp;
+ /* offset 8 */
+ ptxdesc->txdw2 = cpu_to_le32(ptxdesc_mp->txdw2);
+ if (bmcst)
+ ptxdesc->txdw2 |= cpu_to_le32(BMC);
+ ptxdesc->txdw2 |= cpu_to_le32(BK);
+ /* offset 16 */
+ ptxdesc->txdw4 = cpu_to_le32(ptxdesc_mp->txdw4);
+ /* offset 20 */
+ ptxdesc->txdw5 = cpu_to_le32(ptxdesc_mp->txdw5);
+ pattrib->pctrl = 0;/* reset to zero; */
+ }
+ } else if (pxmitframe->frame_tag == MGNT_FRAMETAG) {
+ /* offset 4 */
+ ptxdesc->txdw1 |= (0x05) & 0x1f;/*CAM_ID(MAC_ID), default=5;*/
+ qsel = (uint)(pattrib->qsel & 0x0000001f);
+ ptxdesc->txdw1 |= cpu_to_le32((qsel << QSEL_SHT) & 0x00001f00);
+ ptxdesc->txdw1 |= cpu_to_le32(BIT(16));/* Non-QoS */
+ /* offset 8 */
+ if (bmcst)
+ ptxdesc->txdw2 |= cpu_to_le32(BMC);
+ /* offset 12 */
+ /* f/w will increase the seqnum by itself, driver pass the
+ * correct priority to fw
+ * fw will check the correct priority for increasing the seqnum
+ * per tid. about usb using 4-endpoint, qsel points out the
+ * correct mapping between AC&Endpoint,
+ * the purpose is that correct mapping let the MAC releases
+ * the AC Queue list correctly. */
+ ptxdesc->txdw3 = cpu_to_le32((pattrib->priority << SEQ_SHT) &
+ 0x0fff0000);
+ /* offset 16 */
+ ptxdesc->txdw4 = cpu_to_le32(0x80002040);/*gtest*/
+ /* offset 20 */
+ ptxdesc->txdw5 = cpu_to_le32(0x001f8000);/* gtest 1M */
+ } else if (pxmitframe->frame_tag == TXAGG_FRAMETAG) {
+ /* offset 4 */
+ qsel = 0x13;
+ ptxdesc->txdw1 |= cpu_to_le32((qsel << QSEL_SHT) & 0x00001f00);
+ } else {
+ /* offset 4 */
+ qsel = (uint)(pattrib->priority&0x0000001f);
+ ptxdesc->txdw1 |= cpu_to_le32((qsel << QSEL_SHT) & 0x00001f00);
+ /*offset 8*/
+ /*offset 12*/
+ ptxdesc->txdw3 = cpu_to_le32((pattrib->seqnum << SEQ_SHT) &
+ 0x0fff0000);
+ /*offset 16*/
+ ptxdesc->txdw4 = cpu_to_le32(0x80002040);/*gtest*/
+ /*offset 20*/
+ ptxdesc->txdw5 = cpu_to_le32(0x001f9600);/*gtest*/
+ }
+}
+
+int r8712_xmitframe_complete(struct _adapter *padapter,
+ struct xmit_priv *pxmitpriv,
+ struct xmit_buf *pxmitbuf)
+{
+ struct hw_xmit *phwxmits;
+ sint hwentry;
+ struct xmit_frame *pxmitframe = NULL;
+#ifdef CONFIG_R8712_TX_AGGR
+ struct xmit_frame *p2ndxmitframe = NULL;
+#else
+ int res = _SUCCESS, xcnt = 0;
+#endif
+
+ phwxmits = pxmitpriv->hwxmits;
+ hwentry = pxmitpriv->hwxmit_entry;
+ if (pxmitbuf == NULL) {
+ pxmitbuf = r8712_alloc_xmitbuf(pxmitpriv);
+ if (!pxmitbuf)
+ return false;
+#ifdef CONFIG_R8712_TX_AGGR
+ pxmitbuf->aggr_nr = 0;
+#endif
+ }
+ /* 1st frame dequeued */
+ pxmitframe = dequeue_xframe_ex(pxmitpriv, phwxmits, hwentry);
+ /* need to remember the 1st frame */
+ if (pxmitframe != NULL) {
+
+#ifdef CONFIG_R8712_TX_AGGR
+ /* 1. dequeue 2nd frame
+ * 2. aggr if 2nd xframe is dequeued, else dump directly
+ */
+ if (AGGR_NR_HIGH_BOUND > 1)
+ p2ndxmitframe = dequeue_xframe_ex(pxmitpriv, phwxmits,
+ hwentry);
+ if (pxmitframe->frame_tag != DATA_FRAMETAG) {
+ r8712_free_xmitbuf(pxmitpriv, pxmitbuf);
+ return false;
+ }
+ if (p2ndxmitframe != NULL)
+ if (p2ndxmitframe->frame_tag != DATA_FRAMETAG) {
+ r8712_free_xmitbuf(pxmitpriv, pxmitbuf);
+ return false;
+ }
+ r8712_xmitframe_aggr_1st(pxmitbuf, pxmitframe);
+ if (p2ndxmitframe != NULL) {
+ u16 total_length;
+
+ total_length = r8712_xmitframe_aggr_next(
+ pxmitbuf, p2ndxmitframe);
+ do {
+ p2ndxmitframe = dequeue_xframe_ex(
+ pxmitpriv, phwxmits, hwentry);
+ if (p2ndxmitframe != NULL)
+ total_length =
+ r8712_xmitframe_aggr_next(
+ pxmitbuf,
+ p2ndxmitframe);
+ else
+ break;
+ } while (total_length <= 0x1800 &&
+ pxmitbuf->aggr_nr <= AGGR_NR_HIGH_BOUND);
+ }
+ if (pxmitbuf->aggr_nr > 0)
+ r8712_dump_aggr_xframe(pxmitbuf, pxmitframe);
+
+#else
+
+ xmitframe_xmitbuf_attach(pxmitframe, pxmitbuf);
+ if (pxmitframe->frame_tag == DATA_FRAMETAG) {
+ if (pxmitframe->attrib.priority <= 15)
+ res = r8712_xmitframe_coalesce(padapter,
+ pxmitframe->pkt, pxmitframe);
+ /* always return ndis_packet after
+ * r8712_xmitframe_coalesce */
+ r8712_xmit_complete(padapter, pxmitframe);
+ }
+ if (res == _SUCCESS)
+ dump_xframe(padapter, pxmitframe);
+ else
+ r8712_free_xmitframe_ex(pxmitpriv, pxmitframe);
+ xcnt++;
+#endif
+
+ } else { /* pxmitframe == NULL && p2ndxmitframe == NULL */
+ r8712_free_xmitbuf(pxmitpriv, pxmitbuf);
+ return false;
+ }
+ return true;
+}
+
+static void dump_xframe(struct _adapter *padapter,
+ struct xmit_frame *pxmitframe)
+{
+ int t, sz, w_sz;
+ u8 *mem_addr;
+ u32 ff_hwaddr;
+ struct pkt_attrib *pattrib = &pxmitframe->attrib;
+ struct xmit_priv *pxmitpriv = &padapter->xmitpriv;
+ struct security_priv *psecuritypriv = &padapter->securitypriv;
+
+ if (pxmitframe->attrib.ether_type != 0x0806) {
+ if (pxmitframe->attrib.ether_type != 0x888e)
+ r8712_issue_addbareq_cmd(padapter, pattrib->priority);
+ }
+ mem_addr = pxmitframe->buf_addr;
+ for (t = 0; t < pattrib->nr_frags; t++) {
+ if (t != (pattrib->nr_frags - 1)) {
+ sz = pxmitpriv->frag_len;
+ sz = sz - 4 - (psecuritypriv->sw_encrypt ? 0 :
+ pattrib->icv_len);
+ pxmitframe->last[t] = 0;
+ } else {
+ sz = pattrib->last_txcmdsz;
+ pxmitframe->last[t] = 1;
+ }
+ update_txdesc(pxmitframe, (uint *)mem_addr, sz);
+ w_sz = sz + TXDESC_SIZE;
+ pxmitframe->mem_addr = mem_addr;
+ pxmitframe->bpending[t] = false;
+ ff_hwaddr = get_ff_hwaddr(pxmitframe);
+#ifdef CONFIG_R8712_TX_AGGR
+ r8712_write_port(padapter, RTL8712_DMA_H2CCMD, w_sz,
+ (unsigned char *)pxmitframe);
+#else
+ r8712_write_port(padapter, ff_hwaddr, w_sz,
+ (unsigned char *)pxmitframe);
+#endif
+ mem_addr += w_sz;
+ mem_addr = (u8 *)RND4(((addr_t)(mem_addr)));
+ }
+}
+
+int r8712_xmit_direct(struct _adapter *padapter, struct xmit_frame *pxmitframe)
+{
+ int res = _SUCCESS;
+
+ res = r8712_xmitframe_coalesce(padapter, pxmitframe->pkt, pxmitframe);
+ pxmitframe->pkt = NULL;
+ if (res == _SUCCESS)
+ dump_xframe(padapter, pxmitframe);
+ return res;
+}
+
+int r8712_xmit_enqueue(struct _adapter *padapter, struct xmit_frame *pxmitframe)
+{
+ if (r8712_xmit_classifier(padapter, pxmitframe) == _FAIL) {
+ pxmitframe->pkt = NULL;
+ return _FAIL;
+ }
+ return _SUCCESS;
+}
diff --git a/drivers/staging/rtl8712/rtl8712_xmit.h b/drivers/staging/rtl8712/rtl8712_xmit.h
new file mode 100644
index 000000000..b50e7a1f3
--- /dev/null
+++ b/drivers/staging/rtl8712/rtl8712_xmit.h
@@ -0,0 +1,123 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2010 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ * Modifications for inclusion into the Linux staging tree are
+ * Copyright(c) 2010 Larry Finger. All rights reserved.
+ *
+ * Contact information:
+ * WLAN FAE <wlanfae@realtek.com>
+ * Larry Finger <Larry.Finger@lwfinger.net>
+ *
+ ******************************************************************************/
+#ifndef _RTL8712_XMIT_H_
+#define _RTL8712_XMIT_H_
+
+#define HWXMIT_ENTRY 4
+
+#define VO_QUEUE_INX 0
+#define VI_QUEUE_INX 1
+#define BE_QUEUE_INX 2
+#define BK_QUEUE_INX 3
+#define TS_QUEUE_INX 4
+#define MGT_QUEUE_INX 5
+#define BMC_QUEUE_INX 6
+#define BCN_QUEUE_INX 7
+
+#define HW_QUEUE_ENTRY 8
+
+#define TXDESC_SIZE 32
+#define TXDESC_OFFSET TXDESC_SIZE
+
+#define NR_AMSDU_XMITFRAME 8
+#define NR_TXAGG_XMITFRAME 8
+
+#define MAX_AMSDU_XMITBUF_SZ 8704
+#define MAX_TXAGG_XMITBUF_SZ 16384 /*16k*/
+
+
+#define tx_cmd tx_desc
+
+
+/*
+ *defined for TX DESC Operation
+ */
+
+#define MAX_TID (15)
+
+/*OFFSET 0*/
+#define OFFSET_SZ (0)
+#define OFFSET_SHT (16)
+#define OWN BIT(31)
+#define FSG BIT(27)
+#define LSG BIT(26)
+#define TYPE_SHT (24)
+#define TYPE_MSK (0x03000000)
+
+/*OFFSET 4*/
+#define PKT_OFFSET_SZ (0)
+#define QSEL_SHT (8)
+#define HWPC BIT(31)
+
+/*OFFSET 8*/
+#define BMC BIT(7)
+#define BK BIT(30)
+#define AGG_EN BIT(29)
+#define RTS_RC_SHT (16)
+
+/*OFFSET 12*/
+#define SEQ_SHT (16)
+
+/*OFFSET 16*/
+#define TXBW BIT(18)
+
+/*OFFSET 20*/
+#define DISFB BIT(15)
+#define RSVD6_MSK (0x00E00000)
+#define RSVD6_SHT (21)
+
+struct tx_desc {
+ /*DWORD 0*/
+ unsigned int txdw0;
+ unsigned int txdw1;
+ unsigned int txdw2;
+ unsigned int txdw3;
+ unsigned int txdw4;
+ unsigned int txdw5;
+ unsigned int txdw6;
+ unsigned int txdw7;
+};
+
+
+union txdesc {
+ struct tx_desc txdesc;
+ unsigned int value[TXDESC_SIZE>>2];
+};
+
+int r8712_xmitframe_complete(struct _adapter *padapter,
+ struct xmit_priv *pxmitpriv,
+ struct xmit_buf *pxmitbuf);
+void r8712_do_queue_select(struct _adapter *padapter,
+ struct pkt_attrib *pattrib);
+
+#ifdef CONFIG_R8712_TX_AGGR
+u8 r8712_xmitframe_aggr_1st(struct xmit_buf *pxmitbuf,
+ struct xmit_frame *pxmitframe);
+u8 r8712_dump_aggr_xframe(struct xmit_buf *pxmitbuf,
+ struct xmit_frame *pxmitframe);
+#endif
+
+#endif
diff --git a/drivers/staging/rtl8712/rtl871x_cmd.c b/drivers/staging/rtl8712/rtl871x_cmd.c
new file mode 100644
index 000000000..e35854d28
--- /dev/null
+++ b/drivers/staging/rtl8712/rtl871x_cmd.c
@@ -0,0 +1,1042 @@
+/******************************************************************************
+ * rtl871x_cmd.c
+ *
+ * Copyright(c) 2007 - 2010 Realtek Corporation. All rights reserved.
+ * Linux device driver for RTL8192SU
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ * Modifications for inclusion into the Linux staging tree are
+ * Copyright(c) 2010 Larry Finger. All rights reserved.
+ *
+ * Contact information:
+ * WLAN FAE <wlanfae@realtek.com>
+ * Larry Finger <Larry.Finger@lwfinger.net>
+ *
+ ******************************************************************************/
+
+#define _RTL871X_CMD_C_
+
+#include <linux/compiler.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/kref.h>
+#include <linux/netdevice.h>
+#include <linux/skbuff.h>
+#include <linux/usb.h>
+#include <linux/usb/ch9.h>
+#include <linux/circ_buf.h>
+#include <linux/uaccess.h>
+#include <asm/byteorder.h>
+#include <linux/atomic.h>
+#include <linux/semaphore.h>
+#include <linux/rtnetlink.h>
+
+#include "osdep_service.h"
+#include "drv_types.h"
+#include "recv_osdep.h"
+#include "mlme_osdep.h"
+
+/*
+Caller and the r8712_cmd_thread can protect cmd_q by spin_lock.
+No irqsave is necessary.
+*/
+
+static sint _init_cmd_priv(struct cmd_priv *pcmdpriv)
+{
+ sema_init(&(pcmdpriv->cmd_queue_sema), 0);
+ sema_init(&(pcmdpriv->terminate_cmdthread_sema), 0);
+
+ _init_queue(&(pcmdpriv->cmd_queue));
+
+ /* allocate DMA-able/Non-Page memory for cmd_buf and rsp_buf */
+ pcmdpriv->cmd_seq = 1;
+ pcmdpriv->cmd_allocated_buf = kmalloc(MAX_CMDSZ + CMDBUFF_ALIGN_SZ,
+ GFP_ATOMIC);
+ if (pcmdpriv->cmd_allocated_buf == NULL)
+ return _FAIL;
+ pcmdpriv->cmd_buf = pcmdpriv->cmd_allocated_buf + CMDBUFF_ALIGN_SZ -
+ ((addr_t)(pcmdpriv->cmd_allocated_buf) &
+ (CMDBUFF_ALIGN_SZ-1));
+ pcmdpriv->rsp_allocated_buf = kmalloc(MAX_RSPSZ + 4, GFP_ATOMIC);
+ if (pcmdpriv->rsp_allocated_buf == NULL)
+ return _FAIL;
+ pcmdpriv->rsp_buf = pcmdpriv->rsp_allocated_buf + 4 -
+ ((addr_t)(pcmdpriv->rsp_allocated_buf) & 3);
+ pcmdpriv->cmd_issued_cnt = 0;
+ pcmdpriv->cmd_done_cnt = 0;
+ pcmdpriv->rsp_cnt = 0;
+ return _SUCCESS;
+}
+
+static sint _init_evt_priv(struct evt_priv *pevtpriv)
+{
+ /* allocate DMA-able/Non-Page memory for cmd_buf and rsp_buf */
+ pevtpriv->event_seq = 0;
+ pevtpriv->evt_allocated_buf = kmalloc(MAX_EVTSZ + 4, GFP_ATOMIC);
+
+ if (pevtpriv->evt_allocated_buf == NULL)
+ return _FAIL;
+ pevtpriv->evt_buf = pevtpriv->evt_allocated_buf + 4 -
+ ((addr_t)(pevtpriv->evt_allocated_buf) & 3);
+ pevtpriv->evt_done_cnt = 0;
+ return _SUCCESS;
+}
+
+static void _free_evt_priv(struct evt_priv *pevtpriv)
+{
+ kfree(pevtpriv->evt_allocated_buf);
+}
+
+static void _free_cmd_priv(struct cmd_priv *pcmdpriv)
+{
+ if (pcmdpriv) {
+ kfree(pcmdpriv->cmd_allocated_buf);
+ kfree(pcmdpriv->rsp_allocated_buf);
+ }
+}
+
+/*
+Calling Context:
+
+_enqueue_cmd can only be called between kernel thread,
+since only spin_lock is used.
+
+ISR/Call-Back functions can't call this sub-function.
+
+*/
+
+static sint _enqueue_cmd(struct __queue *queue, struct cmd_obj *obj)
+{
+ unsigned long irqL;
+
+ if (obj == NULL)
+ return _SUCCESS;
+ spin_lock_irqsave(&queue->lock, irqL);
+ list_add_tail(&obj->list, &queue->queue);
+ spin_unlock_irqrestore(&queue->lock, irqL);
+ return _SUCCESS;
+}
+
+static struct cmd_obj *_dequeue_cmd(struct __queue *queue)
+{
+ unsigned long irqL;
+ struct cmd_obj *obj;
+
+ spin_lock_irqsave(&(queue->lock), irqL);
+ if (list_empty(&(queue->queue)))
+ obj = NULL;
+ else {
+ obj = LIST_CONTAINOR(queue->queue.next,
+ struct cmd_obj, list);
+ list_del_init(&obj->list);
+ }
+ spin_unlock_irqrestore(&(queue->lock), irqL);
+ return obj;
+}
+
+u32 r8712_init_cmd_priv(struct cmd_priv *pcmdpriv)
+{
+ return _init_cmd_priv(pcmdpriv);
+}
+
+u32 r8712_init_evt_priv(struct evt_priv *pevtpriv)
+{
+ return _init_evt_priv(pevtpriv);
+}
+
+void r8712_free_evt_priv(struct evt_priv *pevtpriv)
+{
+ _free_evt_priv(pevtpriv);
+}
+
+void r8712_free_cmd_priv(struct cmd_priv *pcmdpriv)
+{
+ _free_cmd_priv(pcmdpriv);
+}
+
+u32 r8712_enqueue_cmd(struct cmd_priv *pcmdpriv, struct cmd_obj *obj)
+{
+ int res;
+
+ if (pcmdpriv->padapter->eeprompriv.bautoload_fail_flag == true)
+ return _FAIL;
+ res = _enqueue_cmd(&pcmdpriv->cmd_queue, obj);
+ up(&pcmdpriv->cmd_queue_sema);
+ return res;
+}
+
+u32 r8712_enqueue_cmd_ex(struct cmd_priv *pcmdpriv, struct cmd_obj *obj)
+{
+ unsigned long irqL;
+ struct __queue *queue;
+
+ if (obj == NULL)
+ return _SUCCESS;
+ if (pcmdpriv->padapter->eeprompriv.bautoload_fail_flag == true)
+ return _FAIL;
+ queue = &pcmdpriv->cmd_queue;
+ spin_lock_irqsave(&queue->lock, irqL);
+ list_add_tail(&obj->list, &queue->queue);
+ spin_unlock_irqrestore(&queue->lock, irqL);
+ up(&pcmdpriv->cmd_queue_sema);
+ return _SUCCESS;
+}
+
+struct cmd_obj *r8712_dequeue_cmd(struct __queue *queue)
+{
+ return _dequeue_cmd(queue);
+}
+
+void r8712_free_cmd_obj(struct cmd_obj *pcmd)
+{
+ if ((pcmd->cmdcode != _JoinBss_CMD_) &&
+ (pcmd->cmdcode != _CreateBss_CMD_))
+ kfree(pcmd->parmbuf);
+ if (pcmd->rsp != NULL) {
+ if (pcmd->rspsz != 0)
+ kfree(pcmd->rsp);
+ }
+ kfree(pcmd);
+}
+
+/*
+r8712_sitesurvey_cmd(~)
+ ### NOTE:#### (!!!!)
+ MUST TAKE CARE THAT BEFORE CALLING THIS FUNC,
+ YOU SHOULD HAVE LOCKED pmlmepriv->lock
+*/
+u8 r8712_sitesurvey_cmd(struct _adapter *padapter,
+ struct ndis_802_11_ssid *pssid)
+{
+ struct cmd_obj *ph2c;
+ struct sitesurvey_parm *psurveyPara;
+ struct cmd_priv *pcmdpriv = &padapter->cmdpriv;
+ struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
+
+ ph2c = kmalloc(sizeof(*ph2c), GFP_ATOMIC);
+ if (ph2c == NULL)
+ return _FAIL;
+ psurveyPara = kmalloc(sizeof(*psurveyPara), GFP_ATOMIC);
+ if (psurveyPara == NULL) {
+ kfree(ph2c);
+ return _FAIL;
+ }
+ init_h2fwcmd_w_parm_no_rsp(ph2c, psurveyPara,
+ GEN_CMD_CODE(_SiteSurvey));
+ psurveyPara->bsslimit = cpu_to_le32(48);
+ psurveyPara->passive_mode = cpu_to_le32(pmlmepriv->passive_mode);
+ psurveyPara->ss_ssidlen = 0;
+ memset(psurveyPara->ss_ssid, 0, IW_ESSID_MAX_SIZE + 1);
+ if ((pssid != NULL) && (pssid->SsidLength)) {
+ memcpy(psurveyPara->ss_ssid, pssid->Ssid, pssid->SsidLength);
+ psurveyPara->ss_ssidlen = cpu_to_le32(pssid->SsidLength);
+ }
+ set_fwstate(pmlmepriv, _FW_UNDER_SURVEY);
+ r8712_enqueue_cmd(pcmdpriv, ph2c);
+ mod_timer(&pmlmepriv->scan_to_timer,
+ jiffies + msecs_to_jiffies(SCANNING_TIMEOUT));
+ padapter->ledpriv.LedControlHandler(padapter, LED_CTL_SITE_SURVEY);
+ padapter->blnEnableRxFF0Filter = 0;
+ return _SUCCESS;
+}
+
+u8 r8712_setdatarate_cmd(struct _adapter *padapter, u8 *rateset)
+{
+ struct cmd_obj *ph2c;
+ struct setdatarate_parm *pbsetdataratepara;
+ struct cmd_priv *pcmdpriv = &padapter->cmdpriv;
+
+ ph2c = kmalloc(sizeof(*ph2c), GFP_ATOMIC);
+ if (ph2c == NULL)
+ return _FAIL;
+ pbsetdataratepara = kmalloc(sizeof(*pbsetdataratepara), GFP_ATOMIC);
+ if (pbsetdataratepara == NULL) {
+ kfree(ph2c);
+ return _FAIL;
+ }
+ init_h2fwcmd_w_parm_no_rsp(ph2c, pbsetdataratepara,
+ GEN_CMD_CODE(_SetDataRate));
+ pbsetdataratepara->mac_id = 5;
+ memcpy(pbsetdataratepara->datarates, rateset, NumRates);
+ r8712_enqueue_cmd(pcmdpriv, ph2c);
+ return _SUCCESS;
+}
+
+u8 r8712_set_chplan_cmd(struct _adapter *padapter, int chplan)
+{
+ struct cmd_obj *ph2c;
+ struct SetChannelPlan_param *psetchplanpara;
+ struct cmd_priv *pcmdpriv = &padapter->cmdpriv;
+
+ ph2c = kmalloc(sizeof(*ph2c), GFP_ATOMIC);
+ if (ph2c == NULL)
+ return _FAIL;
+ psetchplanpara = kmalloc(sizeof(*psetchplanpara), GFP_ATOMIC);
+ if (psetchplanpara == NULL) {
+ kfree(ph2c);
+ return _FAIL;
+ }
+ init_h2fwcmd_w_parm_no_rsp(ph2c, psetchplanpara,
+ GEN_CMD_CODE(_SetChannelPlan));
+ psetchplanpara->ChannelPlan = chplan;
+ r8712_enqueue_cmd(pcmdpriv, ph2c);
+ return _SUCCESS;
+}
+
+u8 r8712_setbasicrate_cmd(struct _adapter *padapter, u8 *rateset)
+{
+ struct cmd_obj *ph2c;
+ struct setbasicrate_parm *pssetbasicratepara;
+ struct cmd_priv *pcmdpriv = &padapter->cmdpriv;
+
+ ph2c = kmalloc(sizeof(*ph2c), GFP_ATOMIC);
+ if (ph2c == NULL)
+ return _FAIL;
+ pssetbasicratepara = kmalloc(sizeof(*pssetbasicratepara), GFP_ATOMIC);
+ if (pssetbasicratepara == NULL) {
+ kfree(ph2c);
+ return _FAIL;
+ }
+ init_h2fwcmd_w_parm_no_rsp(ph2c, pssetbasicratepara,
+ _SetBasicRate_CMD_);
+ memcpy(pssetbasicratepara->basicrates, rateset, NumRates);
+ r8712_enqueue_cmd(pcmdpriv, ph2c);
+ return _SUCCESS;
+}
+
+/* power tracking mechanism setting */
+u8 r8712_setptm_cmd(struct _adapter *padapter, u8 type)
+{
+ struct cmd_obj *ph2c;
+ struct writePTM_parm *pwriteptmparm;
+ struct cmd_priv *pcmdpriv = &padapter->cmdpriv;
+
+ ph2c = kmalloc(sizeof(*ph2c), GFP_ATOMIC);
+ if (ph2c == NULL)
+ return _FAIL;
+ pwriteptmparm = kmalloc(sizeof(*pwriteptmparm), GFP_ATOMIC);
+ if (pwriteptmparm == NULL) {
+ kfree(ph2c);
+ return _FAIL;
+ }
+ init_h2fwcmd_w_parm_no_rsp(ph2c, pwriteptmparm, GEN_CMD_CODE(_SetPT));
+ pwriteptmparm->type = type;
+ r8712_enqueue_cmd(pcmdpriv, ph2c);
+ return _SUCCESS;
+}
+
+u8 r8712_setfwdig_cmd(struct _adapter *padapter, u8 type)
+{
+ struct cmd_obj *ph2c;
+ struct writePTM_parm *pwriteptmparm;
+ struct cmd_priv *pcmdpriv = &padapter->cmdpriv;
+
+ ph2c = kmalloc(sizeof(*ph2c), GFP_ATOMIC);
+ if (ph2c == NULL)
+ return _FAIL;
+ pwriteptmparm = kmalloc(sizeof(*pwriteptmparm), GFP_ATOMIC);
+ if (pwriteptmparm == NULL) {
+ kfree(ph2c);
+ return _FAIL;
+ }
+ init_h2fwcmd_w_parm_no_rsp(ph2c, pwriteptmparm, GEN_CMD_CODE(_SetDIG));
+ pwriteptmparm->type = type;
+ r8712_enqueue_cmd(pcmdpriv, ph2c);
+ return _SUCCESS;
+}
+
+u8 r8712_setfwra_cmd(struct _adapter *padapter, u8 type)
+{
+ struct cmd_obj *ph2c;
+ struct writePTM_parm *pwriteptmparm;
+ struct cmd_priv *pcmdpriv = &padapter->cmdpriv;
+
+ ph2c = kmalloc(sizeof(*ph2c), GFP_ATOMIC);
+ if (ph2c == NULL)
+ return _FAIL;
+ pwriteptmparm = kmalloc(sizeof(*pwriteptmparm), GFP_ATOMIC);
+ if (pwriteptmparm == NULL) {
+ kfree(ph2c);
+ return _FAIL;
+ }
+ init_h2fwcmd_w_parm_no_rsp(ph2c, pwriteptmparm, GEN_CMD_CODE(_SetRA));
+ pwriteptmparm->type = type;
+ r8712_enqueue_cmd(pcmdpriv, ph2c);
+ return _SUCCESS;
+}
+
+u8 r8712_setrfreg_cmd(struct _adapter *padapter, u8 offset, u32 val)
+{
+ struct cmd_obj *ph2c;
+ struct writeRF_parm *pwriterfparm;
+ struct cmd_priv *pcmdpriv = &padapter->cmdpriv;
+
+ ph2c = kmalloc(sizeof(*ph2c), GFP_ATOMIC);
+ if (ph2c == NULL)
+ return _FAIL;
+ pwriterfparm = kmalloc(sizeof(*pwriterfparm), GFP_ATOMIC);
+ if (pwriterfparm == NULL) {
+ kfree(ph2c);
+ return _FAIL;
+ }
+ init_h2fwcmd_w_parm_no_rsp(ph2c, pwriterfparm, GEN_CMD_CODE(_SetRFReg));
+ pwriterfparm->offset = offset;
+ pwriterfparm->value = val;
+ r8712_enqueue_cmd(pcmdpriv, ph2c);
+ return _SUCCESS;
+}
+
+u8 r8712_getrfreg_cmd(struct _adapter *padapter, u8 offset, u8 *pval)
+{
+ struct cmd_obj *ph2c;
+ struct readRF_parm *prdrfparm;
+ struct cmd_priv *pcmdpriv = &padapter->cmdpriv;
+
+ ph2c = kmalloc(sizeof(*ph2c), GFP_ATOMIC);
+ if (ph2c == NULL)
+ return _FAIL;
+ prdrfparm = kmalloc(sizeof(*prdrfparm), GFP_ATOMIC);
+ if (prdrfparm == NULL) {
+ kfree(ph2c);
+ return _FAIL;
+ }
+ INIT_LIST_HEAD(&ph2c->list);
+ ph2c->cmdcode = GEN_CMD_CODE(_GetRFReg);
+ ph2c->parmbuf = (unsigned char *)prdrfparm;
+ ph2c->cmdsz = sizeof(struct readRF_parm);
+ ph2c->rsp = pval;
+ ph2c->rspsz = sizeof(struct readRF_rsp);
+ prdrfparm->offset = offset;
+ r8712_enqueue_cmd(pcmdpriv, ph2c);
+ return _SUCCESS;
+}
+
+void r8712_getbbrfreg_cmdrsp_callback(struct _adapter *padapter,
+ struct cmd_obj *pcmd)
+{
+ kfree(pcmd->parmbuf);
+ kfree(pcmd);
+ padapter->mppriv.workparam.bcompleted = true;
+}
+
+void r8712_readtssi_cmdrsp_callback(struct _adapter *padapter,
+ struct cmd_obj *pcmd)
+{
+ kfree(pcmd->parmbuf);
+ kfree(pcmd);
+
+ padapter->mppriv.workparam.bcompleted = true;
+}
+
+u8 r8712_createbss_cmd(struct _adapter *padapter)
+{
+ struct cmd_obj *pcmd;
+ struct cmd_priv *pcmdpriv = &padapter->cmdpriv;
+ struct wlan_bssid_ex *pdev_network =
+ &padapter->registrypriv.dev_network;
+
+ padapter->ledpriv.LedControlHandler(padapter, LED_CTL_START_TO_LINK);
+ pcmd = kmalloc(sizeof(*pcmd), GFP_ATOMIC);
+ if (pcmd == NULL)
+ return _FAIL;
+ INIT_LIST_HEAD(&pcmd->list);
+ pcmd->cmdcode = _CreateBss_CMD_;
+ pcmd->parmbuf = (unsigned char *)pdev_network;
+ pcmd->cmdsz = r8712_get_ndis_wlan_bssid_ex_sz((
+ struct ndis_wlan_bssid_ex *)
+ pdev_network);
+ pcmd->rsp = NULL;
+ pcmd->rspsz = 0;
+ /* notes: translate IELength & Length after assign to cmdsz; */
+ pdev_network->Length = pcmd->cmdsz;
+ pdev_network->IELength = pdev_network->IELength;
+ pdev_network->Ssid.SsidLength = pdev_network->Ssid.SsidLength;
+ r8712_enqueue_cmd(pcmdpriv, pcmd);
+ return _SUCCESS;
+}
+
+u8 r8712_joinbss_cmd(struct _adapter *padapter, struct wlan_network *pnetwork)
+{
+ uint t_len = 0;
+ struct ndis_wlan_bssid_ex *psecnetwork;
+ struct cmd_obj *pcmd;
+ struct cmd_priv *pcmdpriv = &padapter->cmdpriv;
+ struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
+ struct qos_priv *pqospriv = &pmlmepriv->qospriv;
+ struct security_priv *psecuritypriv = &padapter->securitypriv;
+ struct registry_priv *pregistrypriv = &padapter->registrypriv;
+ enum NDIS_802_11_NETWORK_INFRASTRUCTURE ndis_network_mode = pnetwork->
+ network.InfrastructureMode;
+
+ padapter->ledpriv.LedControlHandler(padapter, LED_CTL_START_TO_LINK);
+ pcmd = kmalloc(sizeof(*pcmd), GFP_ATOMIC);
+ if (pcmd == NULL)
+ return _FAIL;
+ t_len = sizeof(u32) + 6 * sizeof(unsigned char) + 2 +
+ sizeof(struct ndis_802_11_ssid) + sizeof(u32) +
+ sizeof(s32) +
+ sizeof(enum NDIS_802_11_NETWORK_TYPE) +
+ sizeof(struct NDIS_802_11_CONFIGURATION) +
+ sizeof(enum NDIS_802_11_NETWORK_INFRASTRUCTURE) +
+ sizeof(NDIS_802_11_RATES_EX) +
+ sizeof(u32) + MAX_IE_SZ;
+
+ /* for hidden ap to set fw_state here */
+ if (check_fwstate(pmlmepriv, WIFI_STATION_STATE|WIFI_ADHOC_STATE) !=
+ true) {
+ switch (ndis_network_mode) {
+ case Ndis802_11IBSS:
+ pmlmepriv->fw_state |= WIFI_ADHOC_STATE;
+ break;
+ case Ndis802_11Infrastructure:
+ pmlmepriv->fw_state |= WIFI_STATION_STATE;
+ break;
+ case Ndis802_11APMode:
+ case Ndis802_11AutoUnknown:
+ case Ndis802_11InfrastructureMax:
+ break;
+ }
+ }
+ psecnetwork = (struct ndis_wlan_bssid_ex *)&psecuritypriv->sec_bss;
+ if (psecnetwork == NULL) {
+ kfree(pcmd);
+ return _FAIL;
+ }
+ memcpy(psecnetwork, &pnetwork->network, t_len);
+ psecuritypriv->authenticator_ie[0] = (unsigned char)
+ psecnetwork->IELength;
+ if ((psecnetwork->IELength-12) < (256 - 1))
+ memcpy(&psecuritypriv->authenticator_ie[1],
+ &psecnetwork->IEs[12], psecnetwork->IELength-12);
+ else
+ memcpy(&psecuritypriv->authenticator_ie[1],
+ &psecnetwork->IEs[12], (256-1));
+ psecnetwork->IELength = 0;
+ /* If the driver wants to use the bssid to create the connection.
+ * If not, we copy the connecting AP's MAC address to it so that
+ * the driver just has the bssid information for PMKIDList searching.
+ */
+ if (pmlmepriv->assoc_by_bssid == false)
+ ether_addr_copy(&pmlmepriv->assoc_bssid[0],
+ &pnetwork->network.MacAddress[0]);
+ psecnetwork->IELength = r8712_restruct_sec_ie(padapter,
+ &pnetwork->network.IEs[0],
+ &psecnetwork->IEs[0],
+ pnetwork->network.IELength);
+ pqospriv->qos_option = 0;
+ if (pregistrypriv->wmm_enable) {
+ u32 tmp_len;
+
+ tmp_len = r8712_restruct_wmm_ie(padapter,
+ &pnetwork->network.IEs[0],
+ &psecnetwork->IEs[0],
+ pnetwork->network.IELength,
+ psecnetwork->IELength);
+ if (psecnetwork->IELength != tmp_len) {
+ psecnetwork->IELength = tmp_len;
+ pqospriv->qos_option = 1; /* WMM IE in beacon */
+ } else
+ pqospriv->qos_option = 0; /* no WMM IE in beacon */
+ }
+ if (pregistrypriv->ht_enable) {
+ /* For WEP mode, we will use the bg mode to do the connection
+ * to avoid some IOT issues, especially for Realtek 8192u
+ * SoftAP.
+ */
+ if ((padapter->securitypriv.PrivacyAlgrthm != _WEP40_) &&
+ (padapter->securitypriv.PrivacyAlgrthm != _WEP104_)) {
+ /* restructure_ht_ie */
+ r8712_restructure_ht_ie(padapter,
+ &pnetwork->network.IEs[0],
+ &psecnetwork->IEs[0],
+ pnetwork->network.IELength,
+ &psecnetwork->IELength);
+ }
+ }
+ psecuritypriv->supplicant_ie[0] = (u8)psecnetwork->IELength;
+ if (psecnetwork->IELength < 255)
+ memcpy(&psecuritypriv->supplicant_ie[1], &psecnetwork->IEs[0],
+ psecnetwork->IELength);
+ else
+ memcpy(&psecuritypriv->supplicant_ie[1], &psecnetwork->IEs[0],
+ 255);
+ /* get cmdsz before endian conversion */
+ pcmd->cmdsz = r8712_get_ndis_wlan_bssid_ex_sz(psecnetwork);
+#ifdef __BIG_ENDIAN
+ /* wlan_network endian conversion */
+ psecnetwork->Length = cpu_to_le32(psecnetwork->Length);
+ psecnetwork->Ssid.SsidLength = cpu_to_le32(
+ psecnetwork->Ssid.SsidLength);
+ psecnetwork->Privacy = cpu_to_le32(psecnetwork->Privacy);
+ psecnetwork->Rssi = cpu_to_le32(psecnetwork->Rssi);
+ psecnetwork->NetworkTypeInUse = cpu_to_le32(
+ psecnetwork->NetworkTypeInUse);
+ psecnetwork->Configuration.ATIMWindow = cpu_to_le32(
+ psecnetwork->Configuration.ATIMWindow);
+ psecnetwork->Configuration.BeaconPeriod = cpu_to_le32(
+ psecnetwork->Configuration.BeaconPeriod);
+ psecnetwork->Configuration.DSConfig = cpu_to_le32(
+ psecnetwork->Configuration.DSConfig);
+ psecnetwork->Configuration.FHConfig.DwellTime = cpu_to_le32(
+ psecnetwork->Configuration.FHConfig.DwellTime);
+ psecnetwork->Configuration.FHConfig.HopPattern = cpu_to_le32(
+ psecnetwork->Configuration.FHConfig.HopPattern);
+ psecnetwork->Configuration.FHConfig.HopSet = cpu_to_le32(
+ psecnetwork->Configuration.FHConfig.HopSet);
+ psecnetwork->Configuration.FHConfig.Length = cpu_to_le32(
+ psecnetwork->Configuration.FHConfig.Length);
+ psecnetwork->Configuration.Length = cpu_to_le32(
+ psecnetwork->Configuration.Length);
+ psecnetwork->InfrastructureMode = cpu_to_le32(
+ psecnetwork->InfrastructureMode);
+ psecnetwork->IELength = cpu_to_le32(psecnetwork->IELength);
+#endif
+ INIT_LIST_HEAD(&pcmd->list);
+ pcmd->cmdcode = _JoinBss_CMD_;
+ pcmd->parmbuf = (unsigned char *)psecnetwork;
+ pcmd->rsp = NULL;
+ pcmd->rspsz = 0;
+ r8712_enqueue_cmd(pcmdpriv, pcmd);
+ return _SUCCESS;
+}
+
+u8 r8712_disassoc_cmd(struct _adapter *padapter) /* for sta_mode */
+{
+ struct cmd_obj *pdisconnect_cmd;
+ struct disconnect_parm *pdisconnect;
+ struct cmd_priv *pcmdpriv = &padapter->cmdpriv;
+
+ pdisconnect_cmd = kmalloc(sizeof(*pdisconnect_cmd), GFP_ATOMIC);
+ if (pdisconnect_cmd == NULL)
+ return _FAIL;
+ pdisconnect = kmalloc(sizeof(*pdisconnect), GFP_ATOMIC);
+ if (pdisconnect == NULL) {
+ kfree(pdisconnect_cmd);
+ return _FAIL;
+ }
+ init_h2fwcmd_w_parm_no_rsp(pdisconnect_cmd, pdisconnect,
+ _DisConnect_CMD_);
+ r8712_enqueue_cmd(pcmdpriv, pdisconnect_cmd);
+ return _SUCCESS;
+}
+
+u8 r8712_setopmode_cmd(struct _adapter *padapter,
+ enum NDIS_802_11_NETWORK_INFRASTRUCTURE networktype)
+{
+ struct cmd_obj *ph2c;
+ struct setopmode_parm *psetop;
+
+ struct cmd_priv *pcmdpriv = &padapter->cmdpriv;
+
+ ph2c = kmalloc(sizeof(*ph2c), GFP_ATOMIC);
+ if (ph2c == NULL)
+ return _FAIL;
+ psetop = kmalloc(sizeof(*psetop), GFP_ATOMIC);
+ if (psetop == NULL) {
+ kfree(ph2c);
+ return _FAIL;
+ }
+ init_h2fwcmd_w_parm_no_rsp(ph2c, psetop, _SetOpMode_CMD_);
+ psetop->mode = (u8)networktype;
+ r8712_enqueue_cmd(pcmdpriv, ph2c);
+ return _SUCCESS;
+}
+
+u8 r8712_setstakey_cmd(struct _adapter *padapter, u8 *psta, u8 unicast_key)
+{
+ struct cmd_obj *ph2c;
+ struct set_stakey_parm *psetstakey_para;
+ struct cmd_priv *pcmdpriv = &padapter->cmdpriv;
+ struct set_stakey_rsp *psetstakey_rsp = NULL;
+ struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
+ struct security_priv *psecuritypriv = &padapter->securitypriv;
+ struct sta_info *sta = (struct sta_info *)psta;
+
+ ph2c = kmalloc(sizeof(*ph2c), GFP_ATOMIC);
+ if (ph2c == NULL)
+ return _FAIL;
+ psetstakey_para = kmalloc(sizeof(*psetstakey_para), GFP_ATOMIC);
+ if (psetstakey_para == NULL) {
+ kfree(ph2c);
+ return _FAIL;
+ }
+ psetstakey_rsp = kmalloc(sizeof(*psetstakey_rsp), GFP_ATOMIC);
+ if (psetstakey_rsp == NULL) {
+ kfree(ph2c);
+ kfree(psetstakey_para);
+ return _FAIL;
+ }
+ init_h2fwcmd_w_parm_no_rsp(ph2c, psetstakey_para, _SetStaKey_CMD_);
+ ph2c->rsp = (u8 *) psetstakey_rsp;
+ ph2c->rspsz = sizeof(struct set_stakey_rsp);
+ ether_addr_copy(psetstakey_para->addr, sta->hwaddr);
+ if (check_fwstate(pmlmepriv, WIFI_STATION_STATE))
+ psetstakey_para->algorithm = (unsigned char)
+ psecuritypriv->PrivacyAlgrthm;
+ else
+ GET_ENCRY_ALGO(psecuritypriv, sta,
+ psetstakey_para->algorithm, false);
+ if (unicast_key == true)
+ memcpy(&psetstakey_para->key, &sta->x_UncstKey, 16);
+ else
+ memcpy(&psetstakey_para->key,
+ &psecuritypriv->XGrpKey[
+ psecuritypriv->XGrpKeyid - 1]. skey, 16);
+ r8712_enqueue_cmd(pcmdpriv, ph2c);
+ return _SUCCESS;
+}
+
+u8 r8712_setrfintfs_cmd(struct _adapter *padapter, u8 mode)
+{
+ struct cmd_obj *ph2c;
+ struct setrfintfs_parm *psetrfintfsparm;
+ struct cmd_priv *pcmdpriv = &padapter->cmdpriv;
+
+ ph2c = kmalloc(sizeof(*ph2c), GFP_ATOMIC);
+ if (ph2c == NULL)
+ return _FAIL;
+ psetrfintfsparm = kmalloc(sizeof(*psetrfintfsparm), GFP_ATOMIC);
+ if (psetrfintfsparm == NULL) {
+ kfree(ph2c);
+ return _FAIL;
+ }
+ init_h2fwcmd_w_parm_no_rsp(ph2c, psetrfintfsparm,
+ GEN_CMD_CODE(_SetRFIntFs));
+ psetrfintfsparm->rfintfs = mode;
+ r8712_enqueue_cmd(pcmdpriv, ph2c);
+ return _SUCCESS;
+}
+
+u8 r8712_setrttbl_cmd(struct _adapter *padapter,
+ struct setratable_parm *prate_table)
+{
+ struct cmd_obj *ph2c;
+ struct setratable_parm *psetrttblparm;
+ struct cmd_priv *pcmdpriv = &padapter->cmdpriv;
+
+ ph2c = kmalloc(sizeof(*ph2c), GFP_ATOMIC);
+ if (ph2c == NULL)
+ return _FAIL;
+ psetrttblparm = kmalloc(sizeof(*psetrttblparm), GFP_ATOMIC);
+ if (psetrttblparm == NULL) {
+ kfree(ph2c);
+ return _FAIL;
+ }
+ init_h2fwcmd_w_parm_no_rsp(ph2c, psetrttblparm,
+ GEN_CMD_CODE(_SetRaTable));
+ memcpy(psetrttblparm, prate_table, sizeof(struct setratable_parm));
+ r8712_enqueue_cmd(pcmdpriv, ph2c);
+ return _SUCCESS;
+}
+
+u8 r8712_gettssi_cmd(struct _adapter *padapter, u8 offset, u8 *pval)
+{
+ struct cmd_priv *pcmdpriv = &padapter->cmdpriv;
+ struct cmd_obj *ph2c;
+ struct readTSSI_parm *prdtssiparm;
+
+ ph2c = kmalloc(sizeof(*ph2c), GFP_ATOMIC);
+ if (ph2c == NULL)
+ return _FAIL;
+ prdtssiparm = kmalloc(sizeof(*prdtssiparm), GFP_ATOMIC);
+ if (prdtssiparm == NULL) {
+ kfree(ph2c);
+ return _FAIL;
+ }
+ INIT_LIST_HEAD(&ph2c->list);
+ ph2c->cmdcode = GEN_CMD_CODE(_ReadTSSI);
+ ph2c->parmbuf = (unsigned char *)prdtssiparm;
+ ph2c->cmdsz = sizeof(struct readTSSI_parm);
+ ph2c->rsp = pval;
+ ph2c->rspsz = sizeof(struct readTSSI_rsp);
+
+ prdtssiparm->offset = offset;
+ r8712_enqueue_cmd(pcmdpriv, ph2c);
+ return _SUCCESS;
+}
+
+u8 r8712_setMacAddr_cmd(struct _adapter *padapter, u8 *mac_addr)
+{
+ struct cmd_priv *pcmdpriv = &padapter->cmdpriv;
+ struct cmd_obj *ph2c;
+ struct SetMacAddr_param *psetMacAddr_para;
+
+ ph2c = kmalloc(sizeof(*ph2c), GFP_ATOMIC);
+ if (ph2c == NULL)
+ return _FAIL;
+ psetMacAddr_para = kmalloc(sizeof(*psetMacAddr_para), GFP_ATOMIC);
+ if (psetMacAddr_para == NULL) {
+ kfree(ph2c);
+ return _FAIL;
+ }
+ init_h2fwcmd_w_parm_no_rsp(ph2c, psetMacAddr_para,
+ _SetMacAddress_CMD_);
+ ether_addr_copy(psetMacAddr_para->MacAddr, mac_addr);
+ r8712_enqueue_cmd(pcmdpriv, ph2c);
+ return _SUCCESS;
+}
+
+u8 r8712_setassocsta_cmd(struct _adapter *padapter, u8 *mac_addr)
+{
+ struct cmd_priv *pcmdpriv = &padapter->cmdpriv;
+ struct cmd_obj *ph2c;
+ struct set_assocsta_parm *psetassocsta_para;
+ struct set_assocsta_rsp *psetassocsta_rsp = NULL;
+
+ ph2c = kmalloc(sizeof(*ph2c), GFP_ATOMIC);
+ if (ph2c == NULL)
+ return _FAIL;
+ psetassocsta_para = kmalloc(sizeof(*psetassocsta_para), GFP_ATOMIC);
+ if (psetassocsta_para == NULL) {
+ kfree(ph2c);
+ return _FAIL;
+ }
+ psetassocsta_rsp = kmalloc(sizeof(*psetassocsta_rsp), GFP_ATOMIC);
+ if (psetassocsta_rsp == NULL) {
+ kfree(ph2c);
+ kfree(psetassocsta_para);
+ return _FAIL;
+ }
+ init_h2fwcmd_w_parm_no_rsp(ph2c, psetassocsta_para, _SetAssocSta_CMD_);
+ ph2c->rsp = (u8 *) psetassocsta_rsp;
+ ph2c->rspsz = sizeof(struct set_assocsta_rsp);
+ ether_addr_copy(psetassocsta_para->addr, mac_addr);
+ r8712_enqueue_cmd(pcmdpriv, ph2c);
+ return _SUCCESS;
+}
+
+u8 r8712_addbareq_cmd(struct _adapter *padapter, u8 tid)
+{
+ struct cmd_priv *pcmdpriv = &padapter->cmdpriv;
+ struct cmd_obj *ph2c;
+ struct addBaReq_parm *paddbareq_parm;
+
+ ph2c = kmalloc(sizeof(*ph2c), GFP_ATOMIC);
+ if (ph2c == NULL)
+ return _FAIL;
+ paddbareq_parm = kmalloc(sizeof(*paddbareq_parm), GFP_ATOMIC);
+ if (paddbareq_parm == NULL) {
+ kfree(ph2c);
+ return _FAIL;
+ }
+ paddbareq_parm->tid = tid;
+ init_h2fwcmd_w_parm_no_rsp(ph2c, paddbareq_parm,
+ GEN_CMD_CODE(_AddBAReq));
+ r8712_enqueue_cmd_ex(pcmdpriv, ph2c);
+ return _SUCCESS;
+}
+
+u8 r8712_wdg_wk_cmd(struct _adapter *padapter)
+{
+ struct cmd_obj *ph2c;
+ struct drvint_cmd_parm *pdrvintcmd_param;
+ struct cmd_priv *pcmdpriv = &padapter->cmdpriv;
+
+ ph2c = kmalloc(sizeof(*ph2c), GFP_ATOMIC);
+ if (ph2c == NULL)
+ return _FAIL;
+ pdrvintcmd_param = kmalloc(sizeof(*pdrvintcmd_param), GFP_ATOMIC);
+ if (pdrvintcmd_param == NULL) {
+ kfree(ph2c);
+ return _FAIL;
+ }
+ pdrvintcmd_param->i_cid = WDG_WK_CID;
+ pdrvintcmd_param->sz = 0;
+ pdrvintcmd_param->pbuf = NULL;
+ init_h2fwcmd_w_parm_no_rsp(ph2c, pdrvintcmd_param, _DRV_INT_CMD_);
+ r8712_enqueue_cmd_ex(pcmdpriv, ph2c);
+ return _SUCCESS;
+}
+
+void r8712_survey_cmd_callback(struct _adapter *padapter, struct cmd_obj *pcmd)
+{
+ struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
+
+ if (pcmd->res != H2C_SUCCESS)
+ clr_fwstate(pmlmepriv, _FW_UNDER_SURVEY);
+ r8712_free_cmd_obj(pcmd);
+}
+
+void r8712_disassoc_cmd_callback(struct _adapter *padapter,
+ struct cmd_obj *pcmd)
+{
+ unsigned long irqL;
+ struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
+
+ if (pcmd->res != H2C_SUCCESS) {
+ spin_lock_irqsave(&pmlmepriv->lock, irqL);
+ set_fwstate(pmlmepriv, _FW_LINKED);
+ spin_unlock_irqrestore(&pmlmepriv->lock, irqL);
+ return;
+ }
+ r8712_free_cmd_obj(pcmd);
+}
+
+void r8712_joinbss_cmd_callback(struct _adapter *padapter, struct cmd_obj *pcmd)
+{
+ struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
+
+ if (pcmd->res != H2C_SUCCESS)
+ mod_timer(&pmlmepriv->assoc_timer,
+ jiffies + msecs_to_jiffies(1));
+ r8712_free_cmd_obj(pcmd);
+}
+
+void r8712_createbss_cmd_callback(struct _adapter *padapter,
+ struct cmd_obj *pcmd)
+{
+ unsigned long irqL;
+ struct sta_info *psta = NULL;
+ struct wlan_network *pwlan = NULL;
+ struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
+ struct ndis_wlan_bssid_ex *pnetwork = (struct ndis_wlan_bssid_ex *)
+ pcmd->parmbuf;
+ struct wlan_network *tgt_network = &(pmlmepriv->cur_network);
+
+ if (pcmd->res != H2C_SUCCESS)
+ mod_timer(&pmlmepriv->assoc_timer,
+ jiffies + msecs_to_jiffies(1));
+ del_timer(&pmlmepriv->assoc_timer);
+#ifdef __BIG_ENDIAN
+ /* endian_convert */
+ pnetwork->Length = le32_to_cpu(pnetwork->Length);
+ pnetwork->Ssid.SsidLength = le32_to_cpu(pnetwork->Ssid.SsidLength);
+ pnetwork->Privacy = le32_to_cpu(pnetwork->Privacy);
+ pnetwork->Rssi = le32_to_cpu(pnetwork->Rssi);
+ pnetwork->NetworkTypeInUse = le32_to_cpu(pnetwork->NetworkTypeInUse);
+ pnetwork->Configuration.ATIMWindow = le32_to_cpu(pnetwork->
+ Configuration.ATIMWindow);
+ pnetwork->Configuration.DSConfig = le32_to_cpu(pnetwork->
+ Configuration.DSConfig);
+ pnetwork->Configuration.FHConfig.DwellTime = le32_to_cpu(pnetwork->
+ Configuration.FHConfig.DwellTime);
+ pnetwork->Configuration.FHConfig.HopPattern = le32_to_cpu(pnetwork->
+ Configuration.FHConfig.HopPattern);
+ pnetwork->Configuration.FHConfig.HopSet = le32_to_cpu(pnetwork->
+ Configuration.FHConfig.HopSet);
+ pnetwork->Configuration.FHConfig.Length = le32_to_cpu(pnetwork->
+ Configuration.FHConfig.Length);
+ pnetwork->Configuration.Length = le32_to_cpu(pnetwork->
+ Configuration.Length);
+ pnetwork->InfrastructureMode = le32_to_cpu(pnetwork->
+ InfrastructureMode);
+ pnetwork->IELength = le32_to_cpu(pnetwork->IELength);
+#endif
+ spin_lock_irqsave(&pmlmepriv->lock, irqL);
+ if ((pmlmepriv->fw_state) & WIFI_AP_STATE) {
+ psta = r8712_get_stainfo(&padapter->stapriv,
+ pnetwork->MacAddress);
+ if (!psta) {
+ psta = r8712_alloc_stainfo(&padapter->stapriv,
+ pnetwork->MacAddress);
+ if (psta == NULL)
+ goto createbss_cmd_fail;
+ }
+ r8712_indicate_connect(padapter);
+ } else {
+ pwlan = _r8712_alloc_network(pmlmepriv);
+ if (pwlan == NULL) {
+ pwlan = r8712_get_oldest_wlan_network(
+ &pmlmepriv->scanned_queue);
+ if (pwlan == NULL)
+ goto createbss_cmd_fail;
+ pwlan->last_scanned = jiffies;
+ } else
+ list_add_tail(&(pwlan->list),
+ &pmlmepriv->scanned_queue.queue);
+ pnetwork->Length = r8712_get_ndis_wlan_bssid_ex_sz(pnetwork);
+ memcpy(&(pwlan->network), pnetwork, pnetwork->Length);
+ pwlan->fixed = true;
+ memcpy(&tgt_network->network, pnetwork,
+ (r8712_get_ndis_wlan_bssid_ex_sz(pnetwork)));
+ if (pmlmepriv->fw_state & _FW_UNDER_LINKING)
+ pmlmepriv->fw_state ^= _FW_UNDER_LINKING;
+ /* we will set _FW_LINKED when there is one more sat to
+ * join us (stassoc_event_callback) */
+ }
+createbss_cmd_fail:
+ spin_unlock_irqrestore(&pmlmepriv->lock, irqL);
+ r8712_free_cmd_obj(pcmd);
+}
+
+void r8712_setstaKey_cmdrsp_callback(struct _adapter *padapter,
+ struct cmd_obj *pcmd)
+{
+ struct sta_priv *pstapriv = &padapter->stapriv;
+ struct set_stakey_rsp *psetstakey_rsp = (struct set_stakey_rsp *)
+ (pcmd->rsp);
+ struct sta_info *psta = r8712_get_stainfo(pstapriv,
+ psetstakey_rsp->addr);
+
+ if (psta == NULL)
+ goto exit;
+ psta->aid = psta->mac_id = psetstakey_rsp->keyid; /*CAM_ID(CAM_ENTRY)*/
+exit:
+ r8712_free_cmd_obj(pcmd);
+}
+
+void r8712_setassocsta_cmdrsp_callback(struct _adapter *padapter,
+ struct cmd_obj *pcmd)
+{
+ unsigned long irqL;
+ struct sta_priv *pstapriv = &padapter->stapriv;
+ struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
+ struct set_assocsta_parm *passocsta_parm =
+ (struct set_assocsta_parm *)(pcmd->parmbuf);
+ struct set_assocsta_rsp *passocsta_rsp =
+ (struct set_assocsta_rsp *) (pcmd->rsp);
+ struct sta_info *psta = r8712_get_stainfo(pstapriv,
+ passocsta_parm->addr);
+
+ if (psta == NULL)
+ return;
+ psta->aid = psta->mac_id = passocsta_rsp->cam_id;
+ spin_lock_irqsave(&pmlmepriv->lock, irqL);
+ if ((check_fwstate(pmlmepriv, WIFI_MP_STATE)) &&
+ (check_fwstate(pmlmepriv, _FW_UNDER_LINKING)))
+ pmlmepriv->fw_state ^= _FW_UNDER_LINKING;
+ set_fwstate(pmlmepriv, _FW_LINKED);
+ spin_unlock_irqrestore(&pmlmepriv->lock, irqL);
+ r8712_free_cmd_obj(pcmd);
+}
+
+u8 r8712_disconnectCtrlEx_cmd(struct _adapter *adapter, u32 enableDrvCtrl,
+ u32 tryPktCnt, u32 tryPktInterval, u32 firstStageTO)
+{
+ struct cmd_obj *ph2c;
+ struct DisconnectCtrlEx_param *param;
+ struct cmd_priv *pcmdpriv = &adapter->cmdpriv;
+
+ ph2c = kmalloc(sizeof(*ph2c), GFP_ATOMIC);
+ if (ph2c == NULL)
+ return _FAIL;
+ param = kzalloc(sizeof(*param), GFP_ATOMIC);
+ if (param == NULL) {
+ kfree(ph2c);
+ return _FAIL;
+ }
+
+ param->EnableDrvCtrl = (unsigned char)enableDrvCtrl;
+ param->TryPktCnt = (unsigned char)tryPktCnt;
+ param->TryPktInterval = (unsigned char)tryPktInterval;
+ param->FirstStageTO = (unsigned int)firstStageTO;
+
+ init_h2fwcmd_w_parm_no_rsp(ph2c, param,
+ GEN_CMD_CODE(_DisconnectCtrlEx));
+ r8712_enqueue_cmd(pcmdpriv, ph2c);
+ return _SUCCESS;
+}
diff --git a/drivers/staging/rtl8712/rtl871x_cmd.h b/drivers/staging/rtl8712/rtl871x_cmd.h
new file mode 100644
index 000000000..cb8225b94
--- /dev/null
+++ b/drivers/staging/rtl8712/rtl871x_cmd.h
@@ -0,0 +1,790 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2010 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ * Modifications for inclusion into the Linux staging tree are
+ * Copyright(c) 2010 Larry Finger. All rights reserved.
+ *
+ * Contact information:
+ * WLAN FAE <wlanfae@realtek.com>
+ * Larry Finger <Larry.Finger@lwfinger.net>
+ *
+ ******************************************************************************/
+#ifndef __RTL871X_CMD_H_
+#define __RTL871X_CMD_H_
+
+#include "wlan_bssdef.h"
+#include "rtl871x_rf.h"
+#define C2H_MEM_SZ (16*1024)
+
+#include "osdep_service.h"
+#include "ieee80211.h"
+
+#define FREE_CMDOBJ_SZ 128
+#define MAX_CMDSZ 512
+#define MAX_RSPSZ 512
+#define MAX_EVTSZ 1024
+#define CMDBUFF_ALIGN_SZ 512
+
+struct cmd_obj {
+ u16 cmdcode;
+ u8 res;
+ u8 *parmbuf;
+ u32 cmdsz;
+ u8 *rsp;
+ u32 rspsz;
+ struct list_head list;
+};
+
+struct cmd_priv {
+ struct semaphore cmd_queue_sema;
+ struct semaphore terminate_cmdthread_sema;
+ struct __queue cmd_queue;
+ u8 cmd_seq;
+ u8 *cmd_buf; /*shall be non-paged, and 4 bytes aligned*/
+ u8 *cmd_allocated_buf;
+ u8 *rsp_buf; /*shall be non-paged, and 4 bytes aligned*/
+ u8 *rsp_allocated_buf;
+ u32 cmd_issued_cnt;
+ u32 cmd_done_cnt;
+ u32 rsp_cnt;
+ struct _adapter *padapter;
+};
+
+struct evt_obj {
+ u16 evtcode;
+ u8 res;
+ u8 *parmbuf;
+ u32 evtsz;
+ struct list_head list;
+};
+
+struct evt_priv {
+ struct __queue evt_queue;
+ u8 event_seq;
+ u8 *evt_buf; /*shall be non-paged, and 4 bytes aligned*/
+ u8 *evt_allocated_buf;
+ u32 evt_done_cnt;
+ struct tasklet_struct event_tasklet;
+};
+
+#define init_h2fwcmd_w_parm_no_rsp(pcmd, pparm, code) \
+do {\
+ INIT_LIST_HEAD(&pcmd->list);\
+ pcmd->cmdcode = code;\
+ pcmd->parmbuf = (u8 *)(pparm);\
+ pcmd->cmdsz = sizeof(*pparm);\
+ pcmd->rsp = NULL;\
+ pcmd->rspsz = 0;\
+} while (0)
+
+u32 r8712_enqueue_cmd(struct cmd_priv *pcmdpriv, struct cmd_obj *obj);
+u32 r8712_enqueue_cmd_ex(struct cmd_priv *pcmdpriv, struct cmd_obj *obj);
+struct cmd_obj *r8712_dequeue_cmd(struct __queue *queue);
+void r8712_free_cmd_obj(struct cmd_obj *pcmd);
+int r8712_cmd_thread(void *context);
+u32 r8712_init_cmd_priv(struct cmd_priv *pcmdpriv);
+void r8712_free_cmd_priv(struct cmd_priv *pcmdpriv);
+u32 r8712_init_evt_priv(struct evt_priv *pevtpriv);
+void r8712_free_evt_priv(struct evt_priv *pevtpriv);
+
+enum rtl871x_drvint_cid {
+ NONE_WK_CID,
+ WDG_WK_CID,
+ MAX_WK_CID
+};
+
+enum RFINTFS {
+ SWSI,
+ HWSI,
+ HWPI,
+};
+
+/*
+ * Caller Mode: Infra, Ad-HoC(C)
+ * Notes: To enter USB suspend mode
+ * Command Mode
+ */
+struct usb_suspend_parm {
+ u32 action; /* 1: sleep, 0:resume */
+};
+
+/*
+ * Caller Mode: Infra, Ad-Hoc
+ * Notes: To join the specified bss
+ * Command Event Mode
+ */
+struct joinbss_parm {
+ struct ndis_wlan_bssid_ex network;
+};
+
+/*
+ * Caller Mode: Infra, Ad-HoC(C)
+ * Notes: To disconnect the current associated BSS
+ * Command Mode
+ */
+struct disconnect_parm {
+ u32 rsvd;
+};
+
+/*
+ * Caller Mode: AP, Ad-HoC(M)
+ * Notes: To create a BSS
+ * Command Mode
+ */
+struct createbss_parm {
+ struct ndis_wlan_bssid_ex network;
+};
+
+/*
+ * Caller Mode: AP, Ad-HoC, Infra
+ * Notes: To set the NIC mode of RTL8711
+ * Command Mode
+ * The definition of mode:
+ *
+ * #define IW_MODE_AUTO 0 // Let the driver decides which AP to join
+ * #define IW_MODE_ADHOC 1 // Single cell network (Ad-Hoc Clients)
+ * #define IW_MODE_INFRA 2 // Multi cell network, roaming, ..
+ * #define IW_MODE_MASTER 3 // Synchronisation master or AP
+ * #define IW_MODE_REPEAT 4 // Wireless Repeater (forwarder)
+ * #define IW_MODE_SECOND 5 // Secondary master/repeater (backup)
+ * #define IW_MODE_MONITOR 6 // Passive monitor (listen only)
+*/
+struct setopmode_parm {
+ u8 mode;
+ u8 rsvd[3];
+};
+
+/*
+ * Caller Mode: AP, Ad-HoC, Infra
+ * Notes: To ask RTL8711 performing site-survey
+ * Command-Event Mode
+ */
+struct sitesurvey_parm {
+ sint passive_mode; /*active: 1, passive: 0 */
+ sint bsslimit; /* 1 ~ 48 */
+ sint ss_ssidlen;
+ u8 ss_ssid[IW_ESSID_MAX_SIZE + 1];
+};
+
+/*
+ * Caller Mode: Any
+ * Notes: To set the auth type of RTL8711. open/shared/802.1x
+ * Command Mode
+ */
+struct setauth_parm {
+ u8 mode; /*0: legacy open, 1: legacy shared 2: 802.1x*/
+ u8 _1x; /*0: PSK, 1: TLS*/
+ u8 rsvd[2];
+};
+
+/*
+ * Caller Mode: Infra
+ * a. algorithm: wep40, wep104, tkip & aes
+ * b. keytype: grp key/unicast key
+ * c. key contents
+ *
+ * when shared key ==> keyid is the camid
+ * when 802.1x ==> keyid [0:1] ==> grp key
+ * when 802.1x ==> keyid > 2 ==> unicast key
+ */
+struct setkey_parm {
+ u8 algorithm; /* encryption algorithm, could be none, wep40,
+ * TKIP, CCMP, wep104 */
+ u8 keyid;
+ u8 grpkey; /* 1: this is the grpkey for 802.1x.
+ * 0: this is the unicast key for 802.1x */
+ u8 key[16]; /* this could be 40 or 104 */
+};
+
+/*
+ * When in AP or Ad-Hoc mode, this is used to
+ * allocate an sw/hw entry for a newly associated sta.
+ * Command
+ * when shared key ==> algorithm/keyid
+ */
+struct set_stakey_parm {
+ u8 addr[ETH_ALEN];
+ u8 algorithm;
+ u8 key[16];
+};
+
+struct set_stakey_rsp {
+ u8 addr[ETH_ALEN];
+ u8 keyid;
+ u8 rsvd;
+};
+
+struct SetMacAddr_param {
+ u8 MacAddr[ETH_ALEN];
+};
+
+/*
+Caller Ad-Hoc/AP
+
+Command -Rsp(AID == CAMID) mode
+
+This is to force fw to add an sta_data entry per driver's request.
+
+FW will write an cam entry associated with it.
+
+*/
+struct set_assocsta_parm {
+ u8 addr[ETH_ALEN];
+};
+
+struct set_assocsta_rsp {
+ u8 cam_id;
+ u8 rsvd[3];
+};
+
+/*
+ Caller Ad-Hoc/AP
+
+ Command mode
+
+ This is to force fw to del an sta_data entry per driver's request
+
+ FW will invalidate the cam entry associated with it.
+
+*/
+struct del_assocsta_parm {
+ u8 addr[ETH_ALEN];
+};
+
+/*
+Caller Mode: AP/Ad-HoC(M)
+
+Notes: To notify fw that given staid has changed its power state
+
+Command Mode
+
+*/
+struct setstapwrstate_parm {
+ u8 staid;
+ u8 status;
+ u8 hwaddr[6];
+};
+
+/*
+Caller Mode: Any
+
+Notes: To setup the basic rate of RTL8711
+
+Command Mode
+
+*/
+struct setbasicrate_parm {
+ u8 basicrates[NumRates];
+};
+
+/*
+Caller Mode: Any
+
+Notes: To read the current basic rate
+
+Command-Rsp Mode
+
+*/
+struct getbasicrate_parm {
+ u32 rsvd;
+};
+
+struct getbasicrate_rsp {
+ u8 basicrates[NumRates];
+};
+
+/*
+Caller Mode: Any
+
+Notes: To setup the data rate of RTL8711
+
+Command Mode
+
+*/
+struct setdatarate_parm {
+ u8 mac_id;
+ u8 datarates[NumRates];
+};
+
+enum _RT_CHANNEL_DOMAIN {
+ RT_CHANNEL_DOMAIN_FCC = 0,
+ RT_CHANNEL_DOMAIN_IC = 1,
+ RT_CHANNEL_DOMAIN_ETSI = 2,
+ RT_CHANNEL_DOMAIN_SPAIN = 3,
+ RT_CHANNEL_DOMAIN_FRANCE = 4,
+ RT_CHANNEL_DOMAIN_MKK = 5,
+ RT_CHANNEL_DOMAIN_MKK1 = 6,
+ RT_CHANNEL_DOMAIN_ISRAEL = 7,
+ RT_CHANNEL_DOMAIN_TELEC = 8,
+
+ /* Be compatible with old channel plan. No good! */
+ RT_CHANNEL_DOMAIN_MIC = 9,
+ RT_CHANNEL_DOMAIN_GLOBAL_DOAMIN = 10,
+ RT_CHANNEL_DOMAIN_WORLD_WIDE_13 = 11,
+ RT_CHANNEL_DOMAIN_TELEC_NETGEAR = 12,
+
+ RT_CHANNEL_DOMAIN_NCC = 13,
+ RT_CHANNEL_DOMAIN_5G = 14,
+ RT_CHANNEL_DOMAIN_5G_40M = 15,
+ /*===== Add new channel plan above this line===============*/
+ RT_CHANNEL_DOMAIN_MAX,
+};
+
+
+struct SetChannelPlan_param {
+ enum _RT_CHANNEL_DOMAIN ChannelPlan;
+};
+
+/*
+Caller Mode: Any
+
+Notes: To read the current data rate
+
+Command-Rsp Mode
+
+*/
+struct getdatarate_parm {
+ u32 rsvd;
+
+};
+struct getdatarate_rsp {
+ u8 datarates[NumRates];
+};
+
+
+/*
+Caller Mode: Any
+AP: AP can use the info for the contents of beacon frame
+Infra: STA can use the info when sitesurveying
+Ad-HoC(M): Like AP
+Ad-HoC(C): Like STA
+
+
+Notes: To set the phy capability of the NIC
+
+Command Mode
+
+*/
+
+/*
+Caller Mode: Any
+
+Notes: To set the channel/modem/band
+This command will be used when channel/modem/band is changed.
+
+Command Mode
+
+*/
+/*
+Caller Mode: Any
+
+Notes: To get the current setting of channel/modem/band
+
+Command-Rsp Mode
+
+*/
+struct getphy_rsp {
+ u8 rfchannel;
+ u8 modem;
+};
+
+struct readBB_parm {
+ u8 offset;
+};
+struct readBB_rsp {
+ u8 value;
+};
+
+struct readTSSI_parm {
+ u8 offset;
+};
+struct readTSSI_rsp {
+ u8 value;
+};
+
+struct writeBB_parm {
+ u8 offset;
+ u8 value;
+};
+
+struct writePTM_parm {
+ u8 type;
+};
+
+struct readRF_parm {
+ u8 offset;
+};
+struct readRF_rsp {
+ u32 value;
+};
+
+struct writeRF_parm {
+ u32 offset;
+ u32 value;
+};
+
+struct setrfintfs_parm {
+ u8 rfintfs;
+};
+
+struct getrfintfs_parm {
+ u8 rfintfs;
+};
+
+/*
+ Notes: This command is used for H2C/C2H loopback testing
+
+ mac[0] == 0
+ ==> CMD mode, return H2C_SUCCESS.
+ The following condition must be ture under CMD mode
+ mac[1] == mac[4], mac[2] == mac[3], mac[0]=mac[5]= 0;
+ s0 == 0x1234, s1 == 0xabcd, w0 == 0x78563412, w1 == 0x5aa5def7;
+ s2 == (b1 << 8 | b0);
+
+ mac[0] == 1
+ ==> CMD_RSP mode, return H2C_SUCCESS_RSP
+
+ The rsp layout shall be:
+ rsp: parm:
+ mac[0] = mac[5];
+ mac[1] = mac[4];
+ mac[2] = mac[3];
+ mac[3] = mac[2];
+ mac[4] = mac[1];
+ mac[5] = mac[0];
+ s0 = s1;
+ s1 = swap16(s0);
+ w0 = swap32(w1);
+ b0 = b1
+ s2 = s0 + s1
+ b1 = b0
+ w1 = w0
+
+ mac[0] == 2
+ ==> CMD_EVENT mode, return H2C_SUCCESS
+ The event layout shall be:
+ event: parm:
+ mac[0] = mac[5];
+ mac[1] = mac[4];
+ mac[2] = event's sequence number, starting from 1 to parm's marc[3]
+ mac[3] = mac[2];
+ mac[4] = mac[1];
+ mac[5] = mac[0];
+ s0 = swap16(s0) - event.mac[2];
+ s1 = s1 + event.mac[2];
+ w0 = swap32(w0);
+ b0 = b1
+ s2 = s0 + event.mac[2]
+ b1 = b0
+ w1 = swap32(w1) - event.mac[2];
+
+ parm->mac[3] is the total event counts that host requested.
+
+
+ event will be the same with the cmd's param.
+
+*/
+
+/* CMD param Formart for DRV INTERNAL CMD HDL*/
+struct drvint_cmd_parm {
+ int i_cid; /*internal cmd id*/
+ int sz; /* buf sz*/
+ unsigned char *pbuf;
+};
+
+/*------------------- Below are used for RF/BB tunning ---------------------*/
+
+struct setantenna_parm {
+ u8 tx_antset;
+ u8 rx_antset;
+ u8 tx_antenna;
+ u8 rx_antenna;
+};
+
+struct enrateadaptive_parm {
+ u32 en;
+};
+
+struct settxagctbl_parm {
+ u32 txagc[MAX_RATES_LENGTH];
+};
+
+struct gettxagctbl_parm {
+ u32 rsvd;
+};
+struct gettxagctbl_rsp {
+ u32 txagc[MAX_RATES_LENGTH];
+};
+
+struct setagcctrl_parm {
+ u32 agcctrl; /* 0: pure hw, 1: fw */
+};
+
+struct setssup_parm {
+ u32 ss_ForceUp[MAX_RATES_LENGTH];
+};
+
+struct getssup_parm {
+ u32 rsvd;
+};
+struct getssup_rsp {
+ u8 ss_ForceUp[MAX_RATES_LENGTH];
+};
+
+struct setssdlevel_parm {
+ u8 ss_DLevel[MAX_RATES_LENGTH];
+};
+
+struct getssdlevel_parm {
+ u32 rsvd;
+};
+struct getssdlevel_rsp {
+ u8 ss_DLevel[MAX_RATES_LENGTH];
+};
+
+struct setssulevel_parm {
+ u8 ss_ULevel[MAX_RATES_LENGTH];
+};
+
+struct getssulevel_parm {
+ u32 rsvd;
+};
+struct getssulevel_rsp {
+ u8 ss_ULevel[MAX_RATES_LENGTH];
+};
+
+struct setcountjudge_parm {
+ u8 count_judge[MAX_RATES_LENGTH];
+};
+
+struct getcountjudge_parm {
+ u32 rsvd;
+};
+
+struct getcountjudge_rsp {
+ u8 count_judge[MAX_RATES_LENGTH];
+};
+
+struct setpwrmode_parm {
+ u8 mode;
+ u8 flag_low_traffic_en;
+ u8 flag_lpnav_en;
+ u8 flag_rf_low_snr_en;
+ u8 flag_dps_en; /* 1: dps, 0: 32k */
+ u8 bcn_rx_en;
+ u8 bcn_pass_cnt; /* fw report one beacon information to
+ * driver when it receives bcn_pass_cnt
+ * beacons. */
+ u8 bcn_to; /* beacon TO (ms). ¡§=0¡¨ no limit.*/
+ u16 bcn_itv;
+ u8 app_itv; /* only for VOIP mode. */
+ u8 awake_bcn_itv;
+ u8 smart_ps;
+ u8 bcn_pass_time; /* unit: 100ms */
+};
+
+struct setatim_parm {
+ u8 op; /*0: add, 1:del*/
+ u8 txid; /* id of dest station.*/
+};
+
+struct setratable_parm {
+ u8 ss_ForceUp[NumRates];
+ u8 ss_ULevel[NumRates];
+ u8 ss_DLevel[NumRates];
+ u8 count_judge[NumRates];
+};
+
+struct getratable_parm {
+ uint rsvd;
+};
+struct getratable_rsp {
+ u8 ss_ForceUp[NumRates];
+ u8 ss_ULevel[NumRates];
+ u8 ss_DLevel[NumRates];
+ u8 count_judge[NumRates];
+};
+
+/*to get TX,RX retry count*/
+struct gettxretrycnt_parm {
+ unsigned int rsvd;
+};
+
+struct gettxretrycnt_rsp {
+ unsigned long tx_retrycnt;
+};
+
+struct getrxretrycnt_parm {
+ unsigned int rsvd;
+};
+
+struct getrxretrycnt_rsp {
+ unsigned long rx_retrycnt;
+};
+
+/*to get BCNOK,BCNERR count*/
+struct getbcnokcnt_parm {
+ unsigned int rsvd;
+};
+
+struct getbcnokcnt_rsp {
+ unsigned long bcnokcnt;
+};
+
+struct getbcnerrcnt_parm {
+ unsigned int rsvd;
+};
+struct getbcnerrcnt_rsp {
+ unsigned long bcnerrcnt;
+};
+
+/* to get current TX power level*/
+struct getcurtxpwrlevel_parm {
+ unsigned int rsvd;
+};
+
+struct getcurtxpwrlevel_rsp {
+ unsigned short tx_power;
+};
+
+/*dynamic on/off DIG*/
+struct setdig_parm {
+ unsigned char dig_on; /* 1:on , 0:off */
+};
+
+/*dynamic on/off RA*/
+struct setra_parm {
+ unsigned char ra_on; /* 1:on , 0:off */
+};
+
+struct setprobereqextraie_parm {
+ unsigned char e_id;
+ unsigned char ie_len;
+ unsigned char ie[0];
+};
+
+struct setassocreqextraie_parm {
+ unsigned char e_id;
+ unsigned char ie_len;
+ unsigned char ie[0];
+};
+
+struct setproberspextraie_parm {
+ unsigned char e_id;
+ unsigned char ie_len;
+ unsigned char ie[0];
+};
+
+struct setassocrspextraie_parm {
+ unsigned char e_id;
+ unsigned char ie_len;
+ unsigned char ie[0];
+};
+
+struct addBaReq_parm {
+ unsigned int tid;
+};
+
+/*H2C Handler index: 46 */
+struct SetChannel_parm {
+ u32 curr_ch;
+};
+
+/*H2C Handler index: 61 */
+struct DisconnectCtrlEx_param {
+ /* MAXTIME = (2 * FirstStageTO) + (TryPktCnt * TryPktInterval) */
+ unsigned char EnableDrvCtrl;
+ unsigned char TryPktCnt;
+ unsigned char TryPktInterval; /* Unit: ms */
+ unsigned char rsvd;
+ unsigned int FirstStageTO; /* Unit: ms */
+};
+
+#define GEN_CMD_CODE(cmd) cmd ## _CMD_
+
+/*
+ * Result:
+ * 0x00: success
+ * 0x01: success, and check Response.
+ * 0x02: cmd ignored due to duplicated sequence number
+ * 0x03: cmd dropped due to invalid cmd code
+ * 0x04: reserved.
+ */
+
+#define H2C_RSP_OFFSET 512
+#define H2C_SUCCESS 0x00
+#define H2C_SUCCESS_RSP 0x01
+#define H2C_DUPLICATED 0x02
+#define H2C_DROPPED 0x03
+#define H2C_PARAMETERS_ERROR 0x04
+#define H2C_REJECTED 0x05
+#define H2C_CMD_OVERFLOW 0x06
+#define H2C_RESERVED 0x07
+
+u8 r8712_setMacAddr_cmd(struct _adapter *padapter, u8 *mac_addr);
+u8 r8712_setassocsta_cmd(struct _adapter *padapter, u8 *mac_addr);
+u8 r8712_sitesurvey_cmd(struct _adapter *padapter,
+ struct ndis_802_11_ssid *pssid);
+u8 r8712_createbss_cmd(struct _adapter *padapter);
+u8 r8712_setstakey_cmd(struct _adapter *padapter, u8 *psta, u8 unicast_key);
+u8 r8712_joinbss_cmd(struct _adapter *padapter,
+ struct wlan_network *pnetwork);
+u8 r8712_disassoc_cmd(struct _adapter *padapter);
+u8 r8712_setopmode_cmd(struct _adapter *padapter,
+ enum NDIS_802_11_NETWORK_INFRASTRUCTURE networktype);
+u8 r8712_setdatarate_cmd(struct _adapter *padapter, u8 *rateset);
+u8 r8712_set_chplan_cmd(struct _adapter *padapter, int chplan);
+u8 r8712_setbasicrate_cmd(struct _adapter *padapter, u8 *rateset);
+u8 r8712_getrfreg_cmd(struct _adapter *padapter, u8 offset, u8 *pval);
+u8 r8712_setrfintfs_cmd(struct _adapter *padapter, u8 mode);
+u8 r8712_setrfreg_cmd(struct _adapter *padapter, u8 offset, u32 val);
+u8 r8712_setrttbl_cmd(struct _adapter *padapter,
+ struct setratable_parm *prate_table);
+u8 r8712_gettssi_cmd(struct _adapter *padapter, u8 offset, u8 *pval);
+u8 r8712_setptm_cmd(struct _adapter *padapter, u8 type);
+u8 r8712_setfwdig_cmd(struct _adapter *padapter, u8 type);
+u8 r8712_setfwra_cmd(struct _adapter *padapter, u8 type);
+u8 r8712_addbareq_cmd(struct _adapter *padapter, u8 tid);
+u8 r8712_wdg_wk_cmd(struct _adapter *padapter);
+void r8712_survey_cmd_callback(struct _adapter *padapter,
+ struct cmd_obj *pcmd);
+void r8712_disassoc_cmd_callback(struct _adapter *padapter,
+ struct cmd_obj *pcmd);
+void r8712_joinbss_cmd_callback(struct _adapter *padapter,
+ struct cmd_obj *pcmd);
+void r8712_createbss_cmd_callback(struct _adapter *padapter,
+ struct cmd_obj *pcmd);
+void r8712_getbbrfreg_cmdrsp_callback(struct _adapter *padapter,
+ struct cmd_obj *pcmd);
+void r8712_readtssi_cmdrsp_callback(struct _adapter *padapter,
+ struct cmd_obj *pcmd);
+void r8712_setstaKey_cmdrsp_callback(struct _adapter *padapter,
+ struct cmd_obj *pcmd);
+void r8712_setassocsta_cmdrsp_callback(struct _adapter *padapter,
+ struct cmd_obj *pcmd);
+u8 r8712_disconnectCtrlEx_cmd(struct _adapter *adapter, u32 enableDrvCtrl,
+ u32 tryPktCnt, u32 tryPktInterval, u32 firstStageTO);
+
+struct _cmd_callback {
+ u32 cmd_code;
+ void (*callback)(struct _adapter *padapter, struct cmd_obj *cmd);
+};
+
+#include "rtl8712_cmd.h"
+
+#endif /* _CMD_H_ */
+
diff --git a/drivers/staging/rtl8712/rtl871x_debug.h b/drivers/staging/rtl8712/rtl871x_debug.h
new file mode 100644
index 000000000..74468b058
--- /dev/null
+++ b/drivers/staging/rtl8712/rtl871x_debug.h
@@ -0,0 +1,167 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2010 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ * Modifications for inclusion into the Linux staging tree are
+ * Copyright(c) 2010 Larry Finger. All rights reserved.
+ *
+ * Contact information:
+ * WLAN FAE <wlanfae@realtek.com>
+ * Larry Finger <Larry.Finger@lwfinger.net>
+ *
+ ******************************************************************************/
+#ifndef __RTL871X_DEBUG_H__
+#define __RTL871X_DEBUG_H__
+
+#include "osdep_service.h"
+#include "drv_types.h"
+
+
+#define _drv_emerg_ 1
+#define _drv_alert_ 2
+#define _drv_crit_ 3
+#define _drv_err_ 4
+#define _drv_warning_ 5
+#define _drv_notice_ 6
+#define _drv_info_ 7
+#define _drv_dump_ 8
+#define _drv_debug_ 9
+
+
+#define _module_rtl871x_xmit_c_ BIT(0)
+#define _module_xmit_osdep_c_ BIT(1)
+#define _module_rtl871x_recv_c_ BIT(2)
+#define _module_recv_osdep_c_ BIT(3)
+#define _module_rtl871x_mlme_c_ BIT(4)
+#define _module_mlme_osdep_c_ BIT(5)
+#define _module_rtl871x_sta_mgt_c_ BIT(6)
+#define _module_rtl871x_cmd_c_ BIT(7)
+#define _module_cmd_osdep_c_ BIT(8)
+#define _module_rtl871x_io_c_ BIT(9)
+#define _module_io_osdep_c_ BIT(10)
+#define _module_os_intfs_c_ BIT(11)
+#define _module_rtl871x_security_c_ BIT(12)
+#define _module_rtl871x_eeprom_c_ BIT(13)
+#define _module_hal_init_c_ BIT(14)
+#define _module_hci_hal_init_c_ BIT(15)
+#define _module_rtl871x_ioctl_c_ BIT(16)
+#define _module_rtl871x_ioctl_set_c_ BIT(17)
+#define _module_rtl871x_pwrctrl_c_ BIT(19)
+#define _module_hci_intfs_c_ BIT(20)
+#define _module_hci_ops_c_ BIT(21)
+#define _module_osdep_service_c_ BIT(22)
+#define _module_rtl871x_mp_ioctl_c_ BIT(23)
+#define _module_hci_ops_os_c_ BIT(24)
+#define _module_rtl871x_ioctl_os_c BIT(25)
+#define _module_rtl8712_cmd_c_ BIT(26)
+#define _module_rtl871x_mp_c_ BIT(27)
+#define _module_rtl8712_xmit_c_ BIT(28)
+#define _module_rtl8712_efuse_c_ BIT(29)
+#define _module_rtl8712_recv_c_ BIT(30)
+#define _module_rtl8712_led_c_ BIT(31)
+
+#undef _MODULE_DEFINE_
+
+#if defined _RTL871X_XMIT_C_
+ #define _MODULE_DEFINE_ _module_rtl871x_xmit_c_
+#elif defined _XMIT_OSDEP_C_
+ #define _MODULE_DEFINE_ _module_xmit_osdep_c_
+#elif defined _RTL871X_RECV_C_
+ #define _MODULE_DEFINE_ _module_rtl871x_recv_c_
+#elif defined _RECV_OSDEP_C_
+ #define _MODULE_DEFINE_ _module_recv_osdep_c_
+#elif defined _RTL871X_MLME_C_
+ #define _MODULE_DEFINE_ _module_rtl871x_mlme_c_
+#elif defined _MLME_OSDEP_C_
+ #define _MODULE_DEFINE_ _module_mlme_osdep_c_
+#elif defined _RTL871X_STA_MGT_C_
+ #define _MODULE_DEFINE_ _module_rtl871x_sta_mgt_c_
+#elif defined _RTL871X_CMD_C_
+ #define _MODULE_DEFINE_ _module_rtl871x_cmd_c_
+#elif defined _CMD_OSDEP_C_
+ #define _MODULE_DEFINE_ _module_cmd_osdep_c_
+#elif defined _RTL871X_IO_C_
+ #define _MODULE_DEFINE_ _module_rtl871x_io_c_
+#elif defined _IO_OSDEP_C_
+ #define _MODULE_DEFINE_ _module_io_osdep_c_
+#elif defined _OS_INTFS_C_
+ #define _MODULE_DEFINE_ _module_os_intfs_c_
+#elif defined _RTL871X_SECURITY_C_
+ #define _MODULE_DEFINE_ _module_rtl871x_security_c_
+#elif defined _RTL871X_EEPROM_C_
+ #define _MODULE_DEFINE_ _module_rtl871x_eeprom_c_
+#elif defined _HAL_INIT_C_
+ #define _MODULE_DEFINE_ _module_hal_init_c_
+#elif defined _HCI_HAL_INIT_C_
+ #define _MODULE_DEFINE_ _module_hci_hal_init_c_
+#elif defined _RTL871X_IOCTL_C_
+ #define _MODULE_DEFINE_ _module_rtl871x_ioctl_c_
+#elif defined _RTL871X_IOCTL_SET_C_
+ #define _MODULE_DEFINE_ _module_rtl871x_ioctl_set_c_
+#elif defined _RTL871X_PWRCTRL_C_
+ #define _MODULE_DEFINE_ _module_rtl871x_pwrctrl_c_
+#elif defined _HCI_INTF_C_
+ #define _MODULE_DEFINE_ _module_hci_intfs_c_
+#elif defined _HCI_OPS_C_
+ #define _MODULE_DEFINE_ _module_hci_ops_c_
+#elif defined _OSDEP_HCI_INTF_C_
+ #define _MODULE_DEFINE_ _module_hci_intfs_c_
+#elif defined _OSDEP_SERVICE_C_
+ #define _MODULE_DEFINE_ _module_osdep_service_c_
+#elif defined _RTL871X_MP_IOCTL_C_
+ #define _MODULE_DEFINE_ _module_rtl871x_mp_ioctl_c_
+#elif defined _HCI_OPS_OS_C_
+ #define _MODULE_DEFINE_ _module_hci_ops_os_c_
+#elif defined _RTL871X_IOCTL_LINUX_C_
+ #define _MODULE_DEFINE_ _module_rtl871x_ioctl_os_c
+#elif defined _RTL871X_MP_C_
+ #define _MODULE_DEFINE_ _module_rtl871x_mp_c_
+#elif defined _RTL8712_CMD_C_
+ #define _MODULE_DEFINE_ _module_rtl8712_cmd_c_
+#elif defined _RTL8712_XMIT_C_
+ #define _MODULE_DEFINE_ _module_rtl8712_xmit_c_
+#elif defined _RTL8712_EFUSE_C_
+ #define _MODULE_DEFINE_ _module_rtl8712_efuse_c_
+#elif defined _RTL8712_RECV_C_
+ #define _MODULE_DEFINE_ _module_rtl8712_recv_c_
+#else
+ #undef _MODULE_DEFINE_
+#endif
+
+#define _dbgdump printk
+
+#define MSG_8712(x, ...) {}
+
+#define DBG_8712(x, ...) {}
+
+#define WRN_8712(x, ...) {}
+
+#define ERR_8712(x, ...) {}
+
+#undef MSG_8712
+#define MSG_8712 _dbgdump
+
+#undef DBG_8712
+#define DBG_8712 _dbgdump
+
+#undef WRN_8712
+#define WRN_8712 _dbgdump
+
+#undef ERR_8712
+#define ERR_8712 _dbgdump
+
+#endif /*__RTL871X_DEBUG_H__*/
+
diff --git a/drivers/staging/rtl8712/rtl871x_eeprom.c b/drivers/staging/rtl8712/rtl871x_eeprom.c
new file mode 100644
index 000000000..50339e67d
--- /dev/null
+++ b/drivers/staging/rtl8712/rtl871x_eeprom.c
@@ -0,0 +1,233 @@
+/******************************************************************************
+ * rtl871x_eeprom.c
+ *
+ * Copyright(c) 2007 - 2010 Realtek Corporation. All rights reserved.
+ * Linux device driver for RTL8192SU
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ * Modifications for inclusion into the Linux staging tree are
+ * Copyright(c) 2010 Larry Finger. All rights reserved.
+ *
+ * Contact information:
+ * WLAN FAE <wlanfae@realtek.com>
+ * Larry Finger <Larry.Finger@lwfinger.net>
+ *
+ ******************************************************************************/
+
+#define _RTL871X_EEPROM_C_
+
+#include "osdep_service.h"
+#include "drv_types.h"
+
+static void up_clk(struct _adapter *padapter, u16 *x)
+{
+ *x = *x | _EESK;
+ r8712_write8(padapter, EE_9346CR, (u8)*x);
+ udelay(CLOCK_RATE);
+}
+
+static void down_clk(struct _adapter *padapter, u16 *x)
+{
+ *x = *x & ~_EESK;
+ r8712_write8(padapter, EE_9346CR, (u8)*x);
+ udelay(CLOCK_RATE);
+}
+
+static void shift_out_bits(struct _adapter *padapter, u16 data, u16 count)
+{
+ u16 x, mask;
+
+ if (padapter->bSurpriseRemoved == true)
+ goto out;
+ mask = 0x01 << (count - 1);
+ x = r8712_read8(padapter, EE_9346CR);
+ x &= ~(_EEDO | _EEDI);
+ do {
+ x &= ~_EEDI;
+ if (data & mask)
+ x |= _EEDI;
+ if (padapter->bSurpriseRemoved == true)
+ goto out;
+ r8712_write8(padapter, EE_9346CR, (u8)x);
+ udelay(CLOCK_RATE);
+ up_clk(padapter, &x);
+ down_clk(padapter, &x);
+ mask >>= 1;
+ } while (mask);
+ if (padapter->bSurpriseRemoved == true)
+ goto out;
+ x &= ~_EEDI;
+ r8712_write8(padapter, EE_9346CR, (u8)x);
+out:;
+}
+
+static u16 shift_in_bits(struct _adapter *padapter)
+{
+ u16 x, d = 0, i;
+
+ if (padapter->bSurpriseRemoved == true)
+ goto out;
+ x = r8712_read8(padapter, EE_9346CR);
+ x &= ~(_EEDO | _EEDI);
+ d = 0;
+ for (i = 0; i < 16; i++) {
+ d <<= 1;
+ up_clk(padapter, &x);
+ if (padapter->bSurpriseRemoved == true)
+ goto out;
+ x = r8712_read8(padapter, EE_9346CR);
+ x &= ~(_EEDI);
+ if (x & _EEDO)
+ d |= 1;
+ down_clk(padapter, &x);
+ }
+out:
+ return d;
+}
+
+static void standby(struct _adapter *padapter)
+{
+ u8 x;
+
+ x = r8712_read8(padapter, EE_9346CR);
+ x &= ~(_EECS | _EESK);
+ r8712_write8(padapter, EE_9346CR, x);
+ udelay(CLOCK_RATE);
+ x |= _EECS;
+ r8712_write8(padapter, EE_9346CR, x);
+ udelay(CLOCK_RATE);
+}
+
+static u16 wait_eeprom_cmd_done(struct _adapter *padapter)
+{
+ u8 x;
+ u16 i;
+
+ standby(padapter);
+ for (i = 0; i < 200; i++) {
+ x = r8712_read8(padapter, EE_9346CR);
+ if (x & _EEDO)
+ return true;
+ udelay(CLOCK_RATE);
+ }
+ return false;
+}
+
+static void eeprom_clean(struct _adapter *padapter)
+{
+ u16 x;
+
+ if (padapter->bSurpriseRemoved == true)
+ return;
+ x = r8712_read8(padapter, EE_9346CR);
+ if (padapter->bSurpriseRemoved == true)
+ return;
+ x &= ~(_EECS | _EEDI);
+ r8712_write8(padapter, EE_9346CR, (u8)x);
+ if (padapter->bSurpriseRemoved == true)
+ return;
+ up_clk(padapter, &x);
+ if (padapter->bSurpriseRemoved == true)
+ return;
+ down_clk(padapter, &x);
+}
+
+void r8712_eeprom_write16(struct _adapter *padapter, u16 reg, u16 data)
+{
+ u8 x;
+ u8 tmp8_ori, tmp8_new, tmp8_clk_ori, tmp8_clk_new;
+
+ tmp8_ori = r8712_read8(padapter, 0x102502f1);
+ tmp8_new = tmp8_ori & 0xf7;
+ if (tmp8_ori != tmp8_new)
+ r8712_write8(padapter, 0x102502f1, tmp8_new);
+ tmp8_clk_ori = r8712_read8(padapter, 0x10250003);
+ tmp8_clk_new = tmp8_clk_ori | 0x20;
+ if (tmp8_clk_new != tmp8_clk_ori)
+ r8712_write8(padapter, 0x10250003, tmp8_clk_new);
+ x = r8712_read8(padapter, EE_9346CR);
+ x &= ~(_EEDI | _EEDO | _EESK | _EEM0);
+ x |= _EEM1 | _EECS;
+ r8712_write8(padapter, EE_9346CR, x);
+ shift_out_bits(padapter, EEPROM_EWEN_OPCODE, 5);
+ if (padapter->EepromAddressSize == 8) /*CF+ and SDIO*/
+ shift_out_bits(padapter, 0, 6);
+ else /* USB */
+ shift_out_bits(padapter, 0, 4);
+ standby(padapter);
+ /* Erase this particular word. Write the erase opcode and register
+ * number in that order. The opcode is 3bits in length; reg is 6
+ * bits long.
+ */
+ standby(padapter);
+ /* write the new word to the EEPROM
+ * send the write opcode the EEPORM
+ */
+ shift_out_bits(padapter, EEPROM_WRITE_OPCODE, 3);
+ /* select which word in the EEPROM that we are writing to. */
+ shift_out_bits(padapter, reg, padapter->EepromAddressSize);
+ /* write the data to the selected EEPROM word. */
+ shift_out_bits(padapter, data, 16);
+ if (wait_eeprom_cmd_done(padapter)) {
+ standby(padapter);
+ shift_out_bits(padapter, EEPROM_EWDS_OPCODE, 5);
+ shift_out_bits(padapter, reg, 4);
+ eeprom_clean(padapter);
+ }
+ if (tmp8_clk_new != tmp8_clk_ori)
+ r8712_write8(padapter, 0x10250003, tmp8_clk_ori);
+ if (tmp8_new != tmp8_ori)
+ r8712_write8(padapter, 0x102502f1, tmp8_ori);
+}
+
+u16 r8712_eeprom_read16(struct _adapter *padapter, u16 reg) /*ReadEEprom*/
+{
+ u16 x;
+ u16 data = 0;
+ u8 tmp8_ori, tmp8_new, tmp8_clk_ori, tmp8_clk_new;
+
+ tmp8_ori = r8712_read8(padapter, 0x102502f1);
+ tmp8_new = tmp8_ori & 0xf7;
+ if (tmp8_ori != tmp8_new)
+ r8712_write8(padapter, 0x102502f1, tmp8_new);
+ tmp8_clk_ori = r8712_read8(padapter, 0x10250003);
+ tmp8_clk_new = tmp8_clk_ori | 0x20;
+ if (tmp8_clk_new != tmp8_clk_ori)
+ r8712_write8(padapter, 0x10250003, tmp8_clk_new);
+ if (padapter->bSurpriseRemoved == true)
+ goto out;
+ /* select EEPROM, reset bits, set _EECS */
+ x = r8712_read8(padapter, EE_9346CR);
+ if (padapter->bSurpriseRemoved == true)
+ goto out;
+ x &= ~(_EEDI | _EEDO | _EESK | _EEM0);
+ x |= _EEM1 | _EECS;
+ r8712_write8(padapter, EE_9346CR, (unsigned char)x);
+ /* write the read opcode and register number in that order
+ * The opcode is 3bits in length, reg is 6 bits long
+ */
+ shift_out_bits(padapter, EEPROM_READ_OPCODE, 3);
+ shift_out_bits(padapter, reg, padapter->EepromAddressSize);
+ /* Now read the data (16 bits) in from the selected EEPROM word */
+ data = shift_in_bits(padapter);
+ eeprom_clean(padapter);
+out:
+ if (tmp8_clk_new != tmp8_clk_ori)
+ r8712_write8(padapter, 0x10250003, tmp8_clk_ori);
+ if (tmp8_new != tmp8_ori)
+ r8712_write8(padapter, 0x102502f1, tmp8_ori);
+ return data;
+}
+
diff --git a/drivers/staging/rtl8712/rtl871x_eeprom.h b/drivers/staging/rtl8712/rtl871x_eeprom.h
new file mode 100644
index 000000000..497276e53
--- /dev/null
+++ b/drivers/staging/rtl8712/rtl871x_eeprom.h
@@ -0,0 +1,101 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2010 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ *
+ ******************************************************************************/
+#ifndef __RTL871X_EEPROM_H__
+#define __RTL871X_EEPROM_H__
+
+#include "osdep_service.h"
+
+#define RTL8712_EEPROM_ID 0x8712
+#define EEPROM_MAX_SIZE 256
+#define CLOCK_RATE 50 /*100us*/
+
+/*- EEPROM opcodes*/
+#define EEPROM_READ_OPCODE 06
+#define EEPROM_WRITE_OPCODE 05
+#define EEPROM_ERASE_OPCODE 07
+#define EEPROM_EWEN_OPCODE 19 /* Erase/write enable*/
+#define EEPROM_EWDS_OPCODE 16 /* Erase/write disable*/
+
+#define EEPROM_CID_DEFAULT 0x0
+#define EEPROM_CID_ALPHA 0x1
+#define EEPROM_CID_Senao 0x3
+#define EEPROM_CID_NetCore 0x5
+#define EEPROM_CID_CAMEO 0X8
+#define EEPROM_CID_SITECOM 0x9
+#define EEPROM_CID_COREGA 0xB
+#define EEPROM_CID_EDIMAX_BELKIN 0xC
+#define EEPROM_CID_SERCOMM_BELKIN 0xE
+#define EEPROM_CID_CAMEO1 0xF
+#define EEPROM_CID_WNC_COREGA 0x12
+#define EEPROM_CID_CLEVO 0x13
+#define EEPROM_CID_WHQL 0xFE
+
+enum RT_CUSTOMER_ID {
+ RT_CID_DEFAULT = 0,
+ RT_CID_8187_ALPHA0 = 1,
+ RT_CID_8187_SERCOMM_PS = 2,
+ RT_CID_8187_HW_LED = 3,
+ RT_CID_8187_NETGEAR = 4,
+ RT_CID_WHQL = 5,
+ RT_CID_819x_CAMEO = 6,
+ RT_CID_819x_RUNTOP = 7,
+ RT_CID_819x_Senao = 8,
+ RT_CID_TOSHIBA = 9,
+ RT_CID_819x_Netcore = 10,
+ RT_CID_Nettronix = 11,
+ RT_CID_DLINK = 12,
+ RT_CID_PRONET = 13,
+ RT_CID_COREGA = 14,
+ RT_CID_819x_ALPHA = 15,
+ RT_CID_819x_Sitecom = 16,
+ RT_CID_CCX = 17,
+ RT_CID_819x_Lenovo = 18,
+ RT_CID_819x_QMI = 19,
+ RT_CID_819x_Edimax_Belkin = 20,
+ RT_CID_819x_Sercomm_Belkin = 21,
+ RT_CID_819x_CAMEO1 = 22,
+ RT_CID_819x_MSI = 23,
+ RT_CID_819x_Acer = 24,
+ RT_CID_819x_AzWave_ASUS = 25,
+ RT_CID_819x_AzWave = 26,
+ RT_CID_819x_WNC_COREGA = 27,
+ RT_CID_819x_CLEVO = 28,
+};
+
+struct eeprom_priv {
+ u8 bautoload_fail_flag;
+ u8 bempty;
+ u8 sys_config;
+ u8 mac_addr[6];
+ u8 config0;
+ u16 channel_plan;
+ u8 country_string[3];
+ u8 tx_power_b[15];
+ u8 tx_power_g[15];
+ u8 tx_power_a[201];
+ u8 efuse_eeprom_data[EEPROM_MAX_SIZE];
+ enum RT_CUSTOMER_ID CustomerID;
+};
+
+void r8712_eeprom_write16(struct _adapter *padapter, u16 reg, u16 data);
+u16 r8712_eeprom_read16(struct _adapter *padapter, u16 reg);
+
+#endif /*__RTL871X_EEPROM_H__*/
+
diff --git a/drivers/staging/rtl8712/rtl871x_event.h b/drivers/staging/rtl8712/rtl871x_event.h
new file mode 100644
index 000000000..e03ee90d2
--- /dev/null
+++ b/drivers/staging/rtl8712/rtl871x_event.h
@@ -0,0 +1,120 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2010 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ * Modifications for inclusion into the Linux staging tree are
+ * Copyright(c) 2010 Larry Finger. All rights reserved.
+ *
+ * Contact information:
+ * WLAN FAE <wlanfae@realtek.com>
+ * Larry Finger <Larry.Finger@lwfinger.net>
+ *
+ ******************************************************************************/
+#ifndef _RTL871x_EVENT_H_
+#define _RTL871x_EVENT_H_
+
+#include "osdep_service.h"
+
+#include "wlan_bssdef.h"
+#include <linux/semaphore.h>
+#include <linux/sem.h>
+
+/*
+ * Used to report a bss has been scanned
+*/
+struct survey_event {
+ struct ndis_wlan_bssid_ex bss;
+};
+
+/*
+ * Used to report that the requested site survey has been done.
+ * bss_cnt indicates the number of bss that has been reported.
+*/
+struct surveydone_event {
+ unsigned int bss_cnt;
+
+};
+
+/*
+ * Used to report the link result of joinning the given bss
+ * join_res:
+ * -1: authentication fail
+ * -2: association fail
+ * > 0: TID
+*/
+struct joinbss_event {
+ struct wlan_network network;
+};
+
+/*
+ * Used to report a given STA has joinned the created BSS.
+ * It is used in AP/Ad-HoC(M) mode.
+*/
+struct stassoc_event {
+ unsigned char macaddr[6];
+ unsigned char rsvd[2];
+ int cam_id;
+};
+
+struct stadel_event {
+ unsigned char macaddr[6];
+ unsigned char rsvd[2];
+};
+
+struct addba_event {
+ unsigned int tid;
+};
+
+#define GEN_EVT_CODE(event) event ## _EVT_
+
+struct fwevent {
+ u32 parmsize;
+ void (*event_callback)(struct _adapter *dev, u8 *pbuf);
+};
+
+#define C2HEVENT_SZ 32
+struct event_node {
+ unsigned char *node;
+ unsigned char evt_code;
+ unsigned short evt_sz;
+ /*volatile*/ int *caller_ff_tail;
+ int caller_ff_sz;
+};
+
+struct c2hevent_queue {
+ /*volatile*/ int head;
+ /*volatile*/ int tail;
+ struct event_node nodes[C2HEVENT_SZ];
+ unsigned char seq;
+};
+
+#define NETWORK_QUEUE_SZ 4
+
+struct network_queue {
+ /*volatile*/ int head;
+ /*volatile*/ int tail;
+ struct wlan_bssid_ex networks[NETWORK_QUEUE_SZ];
+};
+
+struct ADDBA_Req_Report_parm {
+ unsigned char MacAddress[ETH_ALEN];
+ unsigned short StartSeqNum;
+ unsigned char tid;
+};
+#include "rtl8712_event.h"
+
+#endif /* _WLANEVENT_H_ */
+
diff --git a/drivers/staging/rtl8712/rtl871x_ht.h b/drivers/staging/rtl8712/rtl871x_ht.h
new file mode 100644
index 000000000..41872d937
--- /dev/null
+++ b/drivers/staging/rtl8712/rtl871x_ht.h
@@ -0,0 +1,44 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2010 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ * Modifications for inclusion into the Linux staging tree are
+ * Copyright(c) 2010 Larry Finger. All rights reserved.
+ *
+ * Contact information:
+ * WLAN FAE <wlanfae@realtek.com>
+ * Larry Finger <Larry.Finger@lwfinger.net>
+ *
+ ******************************************************************************/
+#ifndef _RTL871X_HT_H_
+#define _RTL871X_HT_H_
+
+#include "osdep_service.h"
+#include "wifi.h"
+
+struct ht_priv {
+ unsigned int ht_option;
+ unsigned int ampdu_enable;/*for enable Tx A-MPDU*/
+ unsigned char baddbareq_issued[16];
+ unsigned int tx_amsdu_enable;/*for enable Tx A-MSDU */
+ unsigned int tx_amdsu_maxlen; /* 1: 8k, 0:4k ; default:8k, for tx */
+ unsigned int rx_ampdu_maxlen; /* for rx reordering ctrl win_sz,
+ * updated when join_callback. */
+ struct ieee80211_ht_cap ht_cap;
+};
+
+#endif /*_RTL871X_HT_H_ */
+
diff --git a/drivers/staging/rtl8712/rtl871x_io.c b/drivers/staging/rtl8712/rtl871x_io.c
new file mode 100644
index 000000000..e4e5b13cb
--- /dev/null
+++ b/drivers/staging/rtl8712/rtl871x_io.c
@@ -0,0 +1,161 @@
+/******************************************************************************
+ * rtl871x_io.c
+ *
+ * Copyright(c) 2007 - 2010 Realtek Corporation. All rights reserved.
+ * Linux device driver for RTL8192SU
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ * Modifications for inclusion into the Linux staging tree are
+ * Copyright(c) 2010 Larry Finger. All rights reserved.
+ *
+ * Contact information:
+ * WLAN FAE <wlanfae@realtek.com>
+ * Larry Finger <Larry.Finger@lwfinger.net>
+ *
+ ******************************************************************************/
+/*
+ *
+ * The purpose of rtl871x_io.c
+ *
+ * a. provides the API
+ * b. provides the protocol engine
+ * c. provides the software interface between caller and the hardware interface
+ *
+ * For r8712u, both sync/async operations are provided.
+ *
+ * Only sync read/write_mem operations are provided.
+ *
+ */
+
+#define _RTL871X_IO_C_
+
+#include "osdep_service.h"
+#include "drv_types.h"
+#include "rtl871x_io.h"
+#include "osdep_intf.h"
+#include "usb_ops.h"
+
+static uint _init_intf_hdl(struct _adapter *padapter,
+ struct intf_hdl *pintf_hdl)
+{
+ struct intf_priv *pintf_priv;
+ void (*set_intf_option)(u32 *poption) = NULL;
+ void (*set_intf_funs)(struct intf_hdl *pintf_hdl);
+ void (*set_intf_ops)(struct _io_ops *pops);
+ uint (*init_intf_priv)(struct intf_priv *pintfpriv);
+
+ set_intf_option = &(r8712_usb_set_intf_option);
+ set_intf_funs = &(r8712_usb_set_intf_funs);
+ set_intf_ops = &r8712_usb_set_intf_ops;
+ init_intf_priv = &r8712_usb_init_intf_priv;
+ pintf_priv = pintf_hdl->pintfpriv = kmalloc(sizeof(struct intf_priv),
+ GFP_ATOMIC);
+ if (pintf_priv == NULL)
+ goto _init_intf_hdl_fail;
+ pintf_hdl->adapter = (u8 *)padapter;
+ set_intf_option(&pintf_hdl->intf_option);
+ set_intf_funs(pintf_hdl);
+ set_intf_ops(&pintf_hdl->io_ops);
+ pintf_priv->intf_dev = (u8 *)&(padapter->dvobjpriv);
+ if (init_intf_priv(pintf_priv) == _FAIL)
+ goto _init_intf_hdl_fail;
+ return _SUCCESS;
+_init_intf_hdl_fail:
+ kfree(pintf_priv);
+ return _FAIL;
+}
+
+static void _unload_intf_hdl(struct intf_priv *pintfpriv)
+{
+ void (*unload_intf_priv)(struct intf_priv *pintfpriv);
+
+ unload_intf_priv = &r8712_usb_unload_intf_priv;
+ unload_intf_priv(pintfpriv);
+ kfree(pintfpriv);
+}
+
+static uint register_intf_hdl(u8 *dev, struct intf_hdl *pintfhdl)
+{
+ struct _adapter *adapter = (struct _adapter *)dev;
+
+ pintfhdl->intf_option = 0;
+ pintfhdl->adapter = dev;
+ pintfhdl->intf_dev = (u8 *)&(adapter->dvobjpriv);
+ if (!_init_intf_hdl(adapter, pintfhdl))
+ goto register_intf_hdl_fail;
+ return _SUCCESS;
+register_intf_hdl_fail:
+ return false;
+}
+
+static void unregister_intf_hdl(struct intf_hdl *pintfhdl)
+{
+ _unload_intf_hdl(pintfhdl->pintfpriv);
+ memset((u8 *)pintfhdl, 0, sizeof(struct intf_hdl));
+}
+
+uint r8712_alloc_io_queue(struct _adapter *adapter)
+{
+ u32 i;
+ struct io_queue *pio_queue;
+ struct io_req *pio_req;
+
+ pio_queue = kmalloc(sizeof(*pio_queue), GFP_ATOMIC);
+ if (pio_queue == NULL)
+ goto alloc_io_queue_fail;
+ INIT_LIST_HEAD(&pio_queue->free_ioreqs);
+ INIT_LIST_HEAD(&pio_queue->processing);
+ INIT_LIST_HEAD(&pio_queue->pending);
+ spin_lock_init(&pio_queue->lock);
+ pio_queue->pallocated_free_ioreqs_buf = kmalloc(NUM_IOREQ *
+ (sizeof(struct io_req)) + 4,
+ GFP_ATOMIC);
+ if ((pio_queue->pallocated_free_ioreqs_buf) == NULL)
+ goto alloc_io_queue_fail;
+ memset(pio_queue->pallocated_free_ioreqs_buf, 0,
+ (NUM_IOREQ * (sizeof(struct io_req)) + 4));
+ pio_queue->free_ioreqs_buf = pio_queue->pallocated_free_ioreqs_buf + 4
+ - ((addr_t)(pio_queue->pallocated_free_ioreqs_buf)
+ & 3);
+ pio_req = (struct io_req *)(pio_queue->free_ioreqs_buf);
+ for (i = 0; i < NUM_IOREQ; i++) {
+ INIT_LIST_HEAD(&pio_req->list);
+ list_add_tail(&pio_req->list, &pio_queue->free_ioreqs);
+ pio_req++;
+ }
+ if ((register_intf_hdl((u8 *)adapter, &(pio_queue->intf))) == _FAIL)
+ goto alloc_io_queue_fail;
+ adapter->pio_queue = pio_queue;
+ return _SUCCESS;
+alloc_io_queue_fail:
+ if (pio_queue) {
+ kfree(pio_queue->pallocated_free_ioreqs_buf);
+ kfree(pio_queue);
+ }
+ adapter->pio_queue = NULL;
+ return _FAIL;
+}
+
+void r8712_free_io_queue(struct _adapter *adapter)
+{
+ struct io_queue *pio_queue = (struct io_queue *)(adapter->pio_queue);
+
+ if (pio_queue) {
+ kfree(pio_queue->pallocated_free_ioreqs_buf);
+ adapter->pio_queue = NULL;
+ unregister_intf_hdl(&pio_queue->intf);
+ kfree(pio_queue);
+ }
+}
diff --git a/drivers/staging/rtl8712/rtl871x_io.h b/drivers/staging/rtl8712/rtl871x_io.h
new file mode 100644
index 000000000..070cc03ce
--- /dev/null
+++ b/drivers/staging/rtl8712/rtl871x_io.h
@@ -0,0 +1,258 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2010 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ * Modifications for inclusion into the Linux staging tree are
+ * Copyright(c) 2010 Larry Finger. All rights reserved.
+ *
+ * Contact information:
+ * WLAN FAE <wlanfae@realtek.com>
+ * Larry Finger <Larry.Finger@lwfinger.net>
+ *
+ ******************************************************************************/
+#ifndef _IO_H_
+#define _IO_H_
+
+#include "osdep_service.h"
+#include "osdep_intf.h"
+
+#define NUM_IOREQ 8
+
+#define MAX_PROT_SZ (64-16)
+
+#define _IOREADY 0
+#define _IO_WAIT_COMPLETE 1
+#define _IO_WAIT_RSP 2
+
+/* IO COMMAND TYPE */
+#define _IOSZ_MASK_ (0x7F)
+#define _IO_WRITE_ BIT(7)
+#define _IO_FIXED_ BIT(8)
+#define _IO_BURST_ BIT(9)
+#define _IO_BYTE_ BIT(10)
+#define _IO_HW_ BIT(11)
+#define _IO_WORD_ BIT(12)
+#define _IO_SYNC_ BIT(13)
+#define _IO_CMDMASK_ (0x1F80)
+
+/*
+ For prompt mode accessing, caller shall free io_req
+ Otherwise, io_handler will free io_req
+*/
+/* IO STATUS TYPE */
+#define _IO_ERR_ BIT(2)
+#define _IO_SUCCESS_ BIT(1)
+#define _IO_DONE_ BIT(0)
+#define IO_RD32 (_IO_SYNC_ | _IO_WORD_)
+#define IO_RD16 (_IO_SYNC_ | _IO_HW_)
+#define IO_RD8 (_IO_SYNC_ | _IO_BYTE_)
+#define IO_RD32_ASYNC (_IO_WORD_)
+#define IO_RD16_ASYNC (_IO_HW_)
+#define IO_RD8_ASYNC (_IO_BYTE_)
+#define IO_WR32 (_IO_WRITE_ | _IO_SYNC_ | _IO_WORD_)
+#define IO_WR16 (_IO_WRITE_ | _IO_SYNC_ | _IO_HW_)
+#define IO_WR8 (_IO_WRITE_ | _IO_SYNC_ | _IO_BYTE_)
+#define IO_WR32_ASYNC (_IO_WRITE_ | _IO_WORD_)
+#define IO_WR16_ASYNC (_IO_WRITE_ | _IO_HW_)
+#define IO_WR8_ASYNC (_IO_WRITE_ | _IO_BYTE_)
+/*
+ Only Sync. burst accessing is provided.
+*/
+#define IO_WR_BURST(x) (IO_WRITE_ | _IO_SYNC_ | _IO_BURST_ | \
+ ((x) & _IOSZ_MASK_))
+#define IO_RD_BURST(x) (_IO_SYNC_ | _IO_BURST_ | ((x) & _IOSZ_MASK_))
+/*below is for the intf_option bit defition...*/
+#define _INTF_ASYNC_ BIT(0) /*support async io*/
+struct intf_priv;
+struct intf_hdl;
+struct io_queue;
+struct _io_ops {
+ uint (*_sdbus_read_bytes_to_membuf)(struct intf_priv *pintfpriv,
+ u32 addr, u32 cnt, u8 *pbuf);
+ uint (*_sdbus_read_blocks_to_membuf)(struct intf_priv *pintfpriv,
+ u32 addr, u32 cnt, u8 *pbuf);
+ u8 (*_read8)(struct intf_hdl *pintfhdl, u32 addr);
+ u16 (*_read16)(struct intf_hdl *pintfhdl, u32 addr);
+ u32 (*_read32)(struct intf_hdl *pintfhdl, u32 addr);
+ uint (*_sdbus_write_blocks_from_membuf)(struct intf_priv *pintfpriv,
+ u32 addr, u32 cnt, u8 *pbuf,
+ u8 async);
+ uint (*_sdbus_write_bytes_from_membuf)(struct intf_priv *pintfpriv,
+ u32 addr, u32 cnt, u8 *pbuf);
+ u8 (*_cmd52r)(struct intf_priv *pintfpriv, u32 addr);
+ void (*_cmd52w)(struct intf_priv *pintfpriv, u32 addr, u8 val8);
+ u8 (*_cmdfunc152r)(struct intf_priv *pintfpriv, u32 addr);
+ void (*_cmdfunc152w)(struct intf_priv *pintfpriv, u32 addr, u8 val8);
+ void (*_write8)(struct intf_hdl *pintfhdl, u32 addr, u8 val);
+ void (*_write16)(struct intf_hdl *pintfhdl, u32 addr, u16 val);
+ void (*_write32)(struct intf_hdl *pintfhdl, u32 addr, u32 val);
+ void (*_read_mem)(struct intf_hdl *pintfhdl, u32 addr, u32 cnt,
+ u8 *pmem);
+ void (*_write_mem)(struct intf_hdl *pintfhdl, u32 addr, u32 cnt,
+ u8 *pmem);
+ void (*_sync_irp_protocol_rw)(struct io_queue *pio_q);
+ u32 (*_read_port)(struct intf_hdl *pintfhdl, u32 addr, u32 cnt,
+ u8 *pmem);
+ u32 (*_write_port)(struct intf_hdl *pintfhdl, u32 addr, u32 cnt,
+ u8 *pmem);
+};
+
+struct io_req {
+ struct list_head list;
+ u32 addr;
+ /*volatile*/ u32 val;
+ u32 command;
+ u32 status;
+ u8 *pbuf;
+ void (*_async_io_callback)(struct _adapter *padapter,
+ struct io_req *pio_req, u8 *cnxt);
+ u8 *cnxt;
+};
+
+struct intf_hdl {
+ u32 intf_option;
+ u8 *adapter;
+ u8 *intf_dev;
+ struct intf_priv *pintfpriv;
+ void (*intf_hdl_init)(u8 *priv);
+ void (*intf_hdl_unload)(u8 *priv);
+ void (*intf_hdl_open)(u8 *priv);
+ void (*intf_hdl_close)(u8 *priv);
+ struct _io_ops io_ops;
+};
+
+struct reg_protocol_rd {
+
+#ifdef __LITTLE_ENDIAN
+ /* DW1 */
+ u32 NumOfTrans:4;
+ u32 Reserved1:4;
+ u32 Reserved2:24;
+ /* DW2 */
+ u32 ByteCount:7;
+ u32 WriteEnable:1; /*0:read, 1:write*/
+ u32 FixOrContinuous:1; /*0:continuous, 1: Fix*/
+ u32 BurstMode:1;
+ u32 Byte1Access:1;
+ u32 Byte2Access:1;
+ u32 Byte4Access:1;
+ u32 Reserved3:3;
+ u32 Reserved4:16;
+ /*DW3*/
+ u32 BusAddress;
+ /*DW4*/
+#else
+/*DW1*/
+ u32 Reserved1:4;
+ u32 NumOfTrans:4;
+ u32 Reserved2:24;
+ /*DW2*/
+ u32 WriteEnable:1;
+ u32 ByteCount:7;
+ u32 Reserved3:3;
+ u32 Byte4Access:1;
+ u32 Byte2Access:1;
+ u32 Byte1Access:1;
+ u32 BurstMode:1;
+ u32 FixOrContinuous:1;
+ u32 Reserved4:16;
+ /*DW3*/
+ u32 BusAddress;
+ /*DW4*/
+#endif
+};
+
+struct reg_protocol_wt {
+#ifdef __LITTLE_ENDIAN
+ /*DW1*/
+ u32 NumOfTrans:4;
+ u32 Reserved1:4;
+ u32 Reserved2:24;
+ /*DW2*/
+ u32 ByteCount:7;
+ u32 WriteEnable:1; /*0:read, 1:write*/
+ u32 FixOrContinuous:1; /*0:continuous, 1: Fix*/
+ u32 BurstMode:1;
+ u32 Byte1Access:1;
+ u32 Byte2Access:1;
+ u32 Byte4Access:1;
+ u32 Reserved3:3;
+ u32 Reserved4:16;
+ /*DW3*/
+ u32 BusAddress;
+ /*DW4*/
+ u32 Value;
+#else
+ /*DW1*/
+ u32 Reserved1:4;
+ u32 NumOfTrans:4;
+ u32 Reserved2:24;
+ /*DW2*/
+ u32 WriteEnable:1;
+ u32 ByteCount:7;
+ u32 Reserved3:3;
+ u32 Byte4Access:1;
+ u32 Byte2Access:1;
+ u32 Byte1Access:1;
+ u32 BurstMode:1;
+ u32 FixOrContinuous:1;
+ u32 Reserved4:16;
+ /*DW3*/
+ u32 BusAddress;
+ /*DW4*/
+ u32 Value;
+#endif
+};
+
+/*
+Below is the data structure used by _io_handler
+*/
+
+struct io_queue {
+ spinlock_t lock;
+ struct list_head free_ioreqs;
+ /*The io_req list that will be served in the single protocol r/w.*/
+ struct list_head pending;
+ struct list_head processing;
+ u8 *free_ioreqs_buf; /* 4-byte aligned */
+ u8 *pallocated_free_ioreqs_buf;
+ struct intf_hdl intf;
+};
+
+static inline u32 _RND4(u32 sz)
+{
+ u32 val;
+
+ val = ((sz >> 2) + ((sz & 3) ? 1 : 0)) << 2;
+ return val;
+}
+
+u8 r8712_read8(struct _adapter *adapter, u32 addr);
+u16 r8712_read16(struct _adapter *adapter, u32 addr);
+u32 r8712_read32(struct _adapter *adapter, u32 addr);
+void r8712_read_mem(struct _adapter *adapter, u32 addr, u32 cnt, u8 *pmem);
+void r8712_read_port(struct _adapter *adapter, u32 addr, u32 cnt, u8 *pmem);
+void r8712_write8(struct _adapter *adapter, u32 addr, u8 val);
+void r8712_write16(struct _adapter *adapter, u32 addr, u16 val);
+void r8712_write32(struct _adapter *adapter, u32 addr, u32 val);
+void r8712_write_mem(struct _adapter *adapter, u32 addr, u32 cnt, u8 *pmem);
+void r8712_write_port(struct _adapter *adapter, u32 addr, u32 cnt, u8 *pmem);
+/*ioreq */
+uint r8712_alloc_io_queue(struct _adapter *adapter);
+void r8712_free_io_queue(struct _adapter *adapter);
+
+#endif /*_RTL8711_IO_H_*/
+
diff --git a/drivers/staging/rtl8712/rtl871x_ioctl.h b/drivers/staging/rtl8712/rtl871x_ioctl.h
new file mode 100644
index 000000000..8e6ef5d49
--- /dev/null
+++ b/drivers/staging/rtl8712/rtl871x_ioctl.h
@@ -0,0 +1,97 @@
+#ifndef __IOCTL_H
+#define __IOCTL_H
+
+#include "osdep_service.h"
+#include "drv_types.h"
+
+#ifndef OID_802_11_CAPABILITY
+ #define OID_802_11_CAPABILITY 0x0d010122
+#endif
+
+#ifndef OID_802_11_PMKID
+ #define OID_802_11_PMKID 0x0d010123
+#endif
+
+
+/* For DDK-defined OIDs*/
+#define OID_NDIS_SEG1 0x00010100
+#define OID_NDIS_SEG2 0x00010200
+#define OID_NDIS_SEG3 0x00020100
+#define OID_NDIS_SEG4 0x01010100
+#define OID_NDIS_SEG5 0x01020100
+#define OID_NDIS_SEG6 0x01020200
+#define OID_NDIS_SEG7 0xFD010100
+#define OID_NDIS_SEG8 0x0D010100
+#define OID_NDIS_SEG9 0x0D010200
+#define OID_NDIS_SEG10 0x0D020200
+#define SZ_OID_NDIS_SEG1 23
+#define SZ_OID_NDIS_SEG2 3
+#define SZ_OID_NDIS_SEG3 6
+#define SZ_OID_NDIS_SEG4 6
+#define SZ_OID_NDIS_SEG5 4
+#define SZ_OID_NDIS_SEG6 8
+#define SZ_OID_NDIS_SEG7 7
+#define SZ_OID_NDIS_SEG8 36
+#define SZ_OID_NDIS_SEG9 24
+#define SZ_OID_NDIS_SEG10 19
+
+/* For Realtek-defined OIDs*/
+#define OID_MP_SEG1 0xFF871100
+#define OID_MP_SEG2 0xFF818000
+#define OID_MP_SEG3 0xFF818700
+#define OID_MP_SEG4 0xFF011100
+
+enum oid_type {
+ QUERY_OID,
+ SET_OID
+};
+
+struct oid_funs_node {
+ unsigned int oid_start; /*the starting number for OID*/
+ unsigned int oid_end; /*the ending number for OID*/
+ struct oid_obj_priv *node_array;
+ unsigned int array_sz; /*the size of node_array*/
+ int query_counter; /*count the number of query hits for this segment*/
+ int set_counter; /*count the number of set hits for this segment*/
+};
+
+struct oid_par_priv {
+ void *adapter_context;
+ uint oid;
+ void *information_buf;
+ unsigned long information_buf_len;
+ unsigned long *bytes_rw;
+ unsigned long *bytes_needed;
+ enum oid_type type_of_oid;
+ unsigned int dbg;
+};
+
+struct oid_obj_priv {
+ unsigned char dbg; /* 0: without OID debug message
+ * 1: with OID debug message */
+ uint (*oidfuns)(struct oid_par_priv *poid_par_priv);
+};
+
+uint oid_null_function(struct oid_par_priv *poid_par_priv);
+
+extern struct iw_handler_def r871x_handlers_def;
+
+extern uint drv_query_info(
+ struct net_device *MiniportAdapterContext,
+ uint Oid,
+ void *InformationBuffer,
+ u32 InformationBufferLength,
+ u32 *BytesWritten,
+ u32 *BytesNeeded
+);
+
+extern uint drv_set_info(
+ struct net_device *MiniportAdapterContext,
+ uint Oid,
+ void *InformationBuffer,
+ u32 InformationBufferLength,
+ u32 *BytesRead,
+ u32 *BytesNeeded
+);
+
+#endif
diff --git a/drivers/staging/rtl8712/rtl871x_ioctl_linux.c b/drivers/staging/rtl8712/rtl871x_ioctl_linux.c
new file mode 100644
index 000000000..cb0b63877
--- /dev/null
+++ b/drivers/staging/rtl8712/rtl871x_ioctl_linux.c
@@ -0,0 +1,2361 @@
+/******************************************************************************
+ * rtl871x_ioctl_linux.c
+ *
+ * Copyright(c) 2007 - 2010 Realtek Corporation. All rights reserved.
+ * Linux device driver for RTL8192SU
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ * Modifications for inclusion into the Linux staging tree are
+ * Copyright(c) 2010 Larry Finger. All rights reserved.
+ *
+ * Contact information:
+ * WLAN FAE <wlanfae@realtek.com>
+ * Larry Finger <Larry.Finger@lwfinger.net>
+ *
+ ******************************************************************************/
+
+#define _RTL871X_IOCTL_LINUX_C_
+#define _RTL871X_MP_IOCTL_C_
+
+#include "osdep_service.h"
+#include "drv_types.h"
+#include "wlan_bssdef.h"
+#include "rtl871x_debug.h"
+#include "wifi.h"
+#include "rtl871x_mlme.h"
+#include "rtl871x_ioctl.h"
+#include "rtl871x_ioctl_set.h"
+#include "rtl871x_mp_ioctl.h"
+#include "mlme_osdep.h"
+#include <linux/wireless.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/io.h>
+#include <linux/semaphore.h>
+#include <net/iw_handler.h>
+#include <linux/if_arp.h>
+#include <linux/etherdevice.h>
+
+
+#define RTL_IOCTL_WPA_SUPPLICANT (SIOCIWFIRSTPRIV + 0x1E)
+
+#define SCAN_ITEM_SIZE 768
+#define MAX_CUSTOM_LEN 64
+#define RATE_COUNT 4
+
+
+static const u32 rtl8180_rates[] = {1000000, 2000000, 5500000, 11000000,
+ 6000000, 9000000, 12000000, 18000000,
+ 24000000, 36000000, 48000000, 54000000};
+
+static const long ieee80211_wlan_frequencies[] = {
+ 2412, 2417, 2422, 2427,
+ 2432, 2437, 2442, 2447,
+ 2452, 2457, 2462, 2467,
+ 2472, 2484
+};
+
+static const char * const iw_operation_mode[] = {
+ "Auto", "Ad-Hoc", "Managed", "Master", "Repeater", "Secondary",
+ "Monitor"
+};
+
+/**
+ * hwaddr_aton - Convert ASCII string to MAC address
+ * @txt: MAC address as a string (e.g., "00:11:22:33:44:55")
+ * @addr: Buffer for the MAC address (ETH_ALEN = 6 bytes)
+ * Returns: 0 on success, -1 on failure (e.g., string not a MAC address)
+ */
+static int hwaddr_aton_i(const char *txt, u8 *addr)
+{
+ int i;
+
+ for (i = 0; i < 6; i++) {
+ int a, b;
+
+ a = hex_to_bin(*txt++);
+ if (a < 0)
+ return -1;
+ b = hex_to_bin(*txt++);
+ if (b < 0)
+ return -1;
+ *addr++ = (a << 4) | b;
+ if (i < 5 && *txt++ != ':')
+ return -1;
+ }
+ return 0;
+}
+
+void r8712_indicate_wx_assoc_event(struct _adapter *padapter)
+{
+ union iwreq_data wrqu;
+ struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
+
+ wrqu.ap_addr.sa_family = ARPHRD_ETHER;
+ memcpy(wrqu.ap_addr.sa_data, pmlmepriv->cur_network.network.MacAddress,
+ ETH_ALEN);
+ wireless_send_event(padapter->pnetdev, SIOCGIWAP, &wrqu, NULL);
+}
+
+void r8712_indicate_wx_disassoc_event(struct _adapter *padapter)
+{
+ union iwreq_data wrqu;
+
+ wrqu.ap_addr.sa_family = ARPHRD_ETHER;
+ eth_zero_addr(wrqu.ap_addr.sa_data);
+ wireless_send_event(padapter->pnetdev, SIOCGIWAP, &wrqu, NULL);
+}
+
+static inline void handle_pairwise_key(struct sta_info *psta,
+ struct ieee_param *param,
+ struct _adapter *padapter)
+{
+ /* pairwise key */
+ memcpy(psta->x_UncstKey.skey, param->u.crypt.key,
+ (param->u.crypt. key_len > 16 ? 16 : param->u.crypt.key_len));
+ if (strcmp(param->u.crypt.alg, "TKIP") == 0) { /* set mic key */
+ memcpy(psta->tkiptxmickey. skey, &(param->u.crypt.
+ key[16]), 8);
+ memcpy(psta->tkiprxmickey. skey, &(param->u.crypt.
+ key[24]), 8);
+ padapter->securitypriv. busetkipkey = false;
+ mod_timer(&padapter->securitypriv.tkip_timer,
+ jiffies + msecs_to_jiffies(50));
+ }
+ r8712_setstakey_cmd(padapter, (unsigned char *)psta, true);
+}
+
+static inline void handle_group_key(struct ieee_param *param,
+ struct _adapter *padapter)
+{
+ if (0 < param->u.crypt.idx &&
+ param->u.crypt.idx < 3) {
+ /* group key idx is 1 or 2 */
+ memcpy(padapter->securitypriv.XGrpKey[param->u.crypt.
+ idx-1].skey, param->u.crypt.key, (param->u.crypt.key_len
+ > 16 ? 16 : param->u.crypt.key_len));
+ memcpy(padapter->securitypriv.XGrptxmickey[param->
+ u.crypt.idx-1].skey, &(param->u.crypt.key[16]), 8);
+ memcpy(padapter->securitypriv. XGrprxmickey[param->
+ u.crypt.idx-1].skey, &(param->u.crypt.key[24]), 8);
+ padapter->securitypriv.binstallGrpkey = true;
+ r8712_set_key(padapter, &padapter->securitypriv,
+ param->u.crypt.idx);
+ if (padapter->registrypriv.power_mgnt > PS_MODE_ACTIVE) {
+ if (padapter->registrypriv.power_mgnt != padapter->
+ pwrctrlpriv.pwr_mode)
+ mod_timer(&padapter->mlmepriv.dhcp_timer,
+ jiffies + msecs_to_jiffies(60000));
+ }
+ }
+}
+
+static inline char *translate_scan(struct _adapter *padapter,
+ struct iw_request_info *info,
+ struct wlan_network *pnetwork,
+ char *start, char *stop)
+{
+ struct iw_event iwe;
+ struct ieee80211_ht_cap *pht_capie;
+ char *current_val;
+ s8 *p;
+ u32 i = 0, ht_ielen = 0;
+ u16 cap, ht_cap = false, mcs_rate;
+ u8 rssi;
+
+ if ((pnetwork->network.Configuration.DSConfig < 1) ||
+ (pnetwork->network.Configuration.DSConfig > 14)) {
+ if (pnetwork->network.Configuration.DSConfig < 1)
+ pnetwork->network.Configuration.DSConfig = 1;
+ else
+ pnetwork->network.Configuration.DSConfig = 14;
+ }
+ /* AP MAC address */
+ iwe.cmd = SIOCGIWAP;
+ iwe.u.ap_addr.sa_family = ARPHRD_ETHER;
+ ether_addr_copy(iwe.u.ap_addr.sa_data, pnetwork->network.MacAddress);
+ start = iwe_stream_add_event(info, start, stop, &iwe, IW_EV_ADDR_LEN);
+ /* Add the ESSID */
+ iwe.cmd = SIOCGIWESSID;
+ iwe.u.data.flags = 1;
+ iwe.u.data.length = min_t(u32, pnetwork->network.Ssid.SsidLength, 32);
+ start = iwe_stream_add_point(info, start, stop, &iwe,
+ pnetwork->network.Ssid.Ssid);
+ /* parsing HT_CAP_IE */
+ p = r8712_get_ie(&pnetwork->network.IEs[12], _HT_CAPABILITY_IE_,
+ &ht_ielen, pnetwork->network.IELength - 12);
+ if (p && ht_ielen > 0) {
+ ht_cap = true;
+ pht_capie = (struct ieee80211_ht_cap *)(p + 2);
+ memcpy(&mcs_rate, pht_capie->supp_mcs_set, 2);
+ }
+ /* Add the protocol name */
+ iwe.cmd = SIOCGIWNAME;
+ if ((r8712_is_cckratesonly_included((u8 *)&pnetwork->network.
+ SupportedRates)) == true) {
+ if (ht_cap == true)
+ snprintf(iwe.u.name, IFNAMSIZ, "IEEE 802.11bn");
+ else
+ snprintf(iwe.u.name, IFNAMSIZ, "IEEE 802.11b");
+ } else if ((r8712_is_cckrates_included((u8 *)&pnetwork->network.
+ SupportedRates)) == true) {
+ if (ht_cap == true)
+ snprintf(iwe.u.name, IFNAMSIZ, "IEEE 802.11bgn");
+ else
+ snprintf(iwe.u.name, IFNAMSIZ, "IEEE 802.11bg");
+ } else {
+ if (ht_cap == true)
+ snprintf(iwe.u.name, IFNAMSIZ, "IEEE 802.11gn");
+ else
+ snprintf(iwe.u.name, IFNAMSIZ, "IEEE 802.11g");
+ }
+ start = iwe_stream_add_event(info, start, stop, &iwe, IW_EV_CHAR_LEN);
+ /* Add mode */
+ iwe.cmd = SIOCGIWMODE;
+ memcpy((u8 *)&cap, r8712_get_capability_from_ie(pnetwork->network.IEs),
+ 2);
+ cap = le16_to_cpu(cap);
+ if (cap & (WLAN_CAPABILITY_IBSS|WLAN_CAPABILITY_BSS)) {
+ if (cap & WLAN_CAPABILITY_BSS)
+ iwe.u.mode = (u32)IW_MODE_MASTER;
+ else
+ iwe.u.mode = (u32)IW_MODE_ADHOC;
+ start = iwe_stream_add_event(info, start, stop, &iwe,
+ IW_EV_UINT_LEN);
+ }
+ /* Add frequency/channel */
+ iwe.cmd = SIOCGIWFREQ;
+ {
+ /* check legal index */
+ u8 dsconfig = pnetwork->network.Configuration.DSConfig;
+
+ if (dsconfig >= 1 && dsconfig <= sizeof(
+ ieee80211_wlan_frequencies) / sizeof(long))
+ iwe.u.freq.m = (s32)(ieee80211_wlan_frequencies[
+ pnetwork->network.Configuration.
+ DSConfig - 1] * 100000);
+ else
+ iwe.u.freq.m = 0;
+ }
+ iwe.u.freq.e = (s16)1;
+ iwe.u.freq.i = (u8)pnetwork->network.Configuration.DSConfig;
+ start = iwe_stream_add_event(info, start, stop, &iwe,
+ IW_EV_FREQ_LEN);
+ /* Add encryption capability */
+ iwe.cmd = SIOCGIWENCODE;
+ if (cap & WLAN_CAPABILITY_PRIVACY)
+ iwe.u.data.flags = (u16)(IW_ENCODE_ENABLED |
+ IW_ENCODE_NOKEY);
+ else
+ iwe.u.data.flags = (u16)(IW_ENCODE_DISABLED);
+ iwe.u.data.length = (u16)0;
+ start = iwe_stream_add_point(info, start, stop, &iwe,
+ pnetwork->network.Ssid.Ssid);
+ /*Add basic and extended rates */
+ current_val = start + iwe_stream_lcp_len(info);
+ iwe.cmd = SIOCGIWRATE;
+ iwe.u.bitrate.fixed = 0;
+ iwe.u.bitrate.disabled = 0;
+ iwe.u.bitrate.value = 0;
+ i = 0;
+ while (pnetwork->network.SupportedRates[i] != 0) {
+ /* Bit rate given in 500 kb/s units */
+ iwe.u.bitrate.value = (pnetwork->network.SupportedRates[i++] &
+ 0x7F) * 500000;
+ current_val = iwe_stream_add_value(info, start, current_val,
+ stop, &iwe, IW_EV_PARAM_LEN);
+ }
+ /* Check if we added any event */
+ if ((current_val - start) > iwe_stream_lcp_len(info))
+ start = current_val;
+ /* parsing WPA/WPA2 IE */
+ {
+ u8 buf[MAX_WPA_IE_LEN];
+ u8 wpa_ie[255], rsn_ie[255];
+ u16 wpa_len = 0, rsn_len = 0;
+ int n;
+
+ r8712_get_sec_ie(pnetwork->network.IEs,
+ pnetwork->network.IELength, rsn_ie, &rsn_len,
+ wpa_ie, &wpa_len);
+ if (wpa_len > 0) {
+ memset(buf, 0, MAX_WPA_IE_LEN);
+ n = sprintf(buf, "wpa_ie=");
+ for (i = 0; i < wpa_len; i++) {
+ n += snprintf(buf + n, MAX_WPA_IE_LEN - n,
+ "%02x", wpa_ie[i]);
+ if (n >= MAX_WPA_IE_LEN)
+ break;
+ }
+ memset(&iwe, 0, sizeof(iwe));
+ iwe.cmd = IWEVCUSTOM;
+ iwe.u.data.length = (u16)strlen(buf);
+ start = iwe_stream_add_point(info, start, stop,
+ &iwe, buf);
+ memset(&iwe, 0, sizeof(iwe));
+ iwe.cmd = IWEVGENIE;
+ iwe.u.data.length = (u16)wpa_len;
+ start = iwe_stream_add_point(info, start, stop,
+ &iwe, wpa_ie);
+ }
+ if (rsn_len > 0) {
+ memset(buf, 0, MAX_WPA_IE_LEN);
+ n = sprintf(buf, "rsn_ie=");
+ for (i = 0; i < rsn_len; i++) {
+ n += snprintf(buf + n, MAX_WPA_IE_LEN - n,
+ "%02x", rsn_ie[i]);
+ if (n >= MAX_WPA_IE_LEN)
+ break;
+ }
+ memset(&iwe, 0, sizeof(iwe));
+ iwe.cmd = IWEVCUSTOM;
+ iwe.u.data.length = strlen(buf);
+ start = iwe_stream_add_point(info, start, stop,
+ &iwe, buf);
+ memset(&iwe, 0, sizeof(iwe));
+ iwe.cmd = IWEVGENIE;
+ iwe.u.data.length = rsn_len;
+ start = iwe_stream_add_point(info, start, stop, &iwe,
+ rsn_ie);
+ }
+ }
+
+ { /* parsing WPS IE */
+ u8 wps_ie[512];
+ uint wps_ielen;
+
+ if (r8712_get_wps_ie(pnetwork->network.IEs,
+ pnetwork->network.IELength,
+ wps_ie, &wps_ielen) == true) {
+ if (wps_ielen > 2) {
+ iwe.cmd = IWEVGENIE;
+ iwe.u.data.length = (u16)wps_ielen;
+ start = iwe_stream_add_point(info, start, stop,
+ &iwe, wps_ie);
+ }
+ }
+ }
+ /* Add quality statistics */
+ iwe.cmd = IWEVQUAL;
+ rssi = r8712_signal_scale_mapping(pnetwork->network.Rssi);
+ /* we only update signal_level (signal strength) that is rssi. */
+ iwe.u.qual.updated = (u8)(IW_QUAL_QUAL_INVALID | IW_QUAL_LEVEL_UPDATED |
+ IW_QUAL_NOISE_INVALID);
+ iwe.u.qual.level = rssi; /* signal strength */
+ iwe.u.qual.qual = 0; /* signal quality */
+ iwe.u.qual.noise = 0; /* noise level */
+ start = iwe_stream_add_event(info, start, stop, &iwe, IW_EV_QUAL_LEN);
+ /* how to translate rssi to ?% */
+ return start;
+}
+
+static int wpa_set_auth_algs(struct net_device *dev, u32 value)
+{
+ struct _adapter *padapter = netdev_priv(dev);
+ int ret = 0;
+
+ if ((value & AUTH_ALG_SHARED_KEY) && (value & AUTH_ALG_OPEN_SYSTEM)) {
+ padapter->securitypriv.ndisencryptstatus =
+ Ndis802_11Encryption1Enabled;
+ padapter->securitypriv.ndisauthtype =
+ Ndis802_11AuthModeAutoSwitch;
+ padapter->securitypriv.AuthAlgrthm = 3;
+ } else if (value & AUTH_ALG_SHARED_KEY) {
+ padapter->securitypriv.ndisencryptstatus =
+ Ndis802_11Encryption1Enabled;
+ padapter->securitypriv.ndisauthtype = Ndis802_11AuthModeShared;
+ padapter->securitypriv.AuthAlgrthm = 1;
+ } else if (value & AUTH_ALG_OPEN_SYSTEM) {
+ if (padapter->securitypriv.ndisauthtype <
+ Ndis802_11AuthModeWPAPSK) {
+ padapter->securitypriv.ndisauthtype =
+ Ndis802_11AuthModeOpen;
+ padapter->securitypriv.AuthAlgrthm = 0;
+ }
+ } else
+ ret = -EINVAL;
+ return ret;
+}
+
+static int wpa_set_encryption(struct net_device *dev, struct ieee_param *param,
+ u32 param_len)
+{
+ int ret = 0;
+ u32 wep_key_idx, wep_key_len = 0;
+ struct NDIS_802_11_WEP *pwep = NULL;
+ struct _adapter *padapter = netdev_priv(dev);
+ struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
+ struct security_priv *psecuritypriv = &padapter->securitypriv;
+
+ param->u.crypt.err = 0;
+ param->u.crypt.alg[IEEE_CRYPT_ALG_NAME_LEN - 1] = '\0';
+ if (param_len != (u32)((u8 *) param->u.crypt.key - (u8 *)param) +
+ param->u.crypt.key_len)
+ return -EINVAL;
+ if (is_broadcast_ether_addr(param->sta_addr)) {
+ if (param->u.crypt.idx >= WEP_KEYS) {
+ /* for large key indices, set the default (0) */
+ param->u.crypt.idx = 0;
+ }
+ } else
+ return -EINVAL;
+ if (strcmp(param->u.crypt.alg, "WEP") == 0) {
+ netdev_info(dev, "r8712u: %s: crypt.alg = WEP\n", __func__);
+ padapter->securitypriv.ndisencryptstatus =
+ Ndis802_11Encryption1Enabled;
+ padapter->securitypriv.PrivacyAlgrthm = _WEP40_;
+ padapter->securitypriv.XGrpPrivacy = _WEP40_;
+ wep_key_idx = param->u.crypt.idx;
+ wep_key_len = param->u.crypt.key_len;
+ if (wep_key_idx >= WEP_KEYS)
+ wep_key_idx = 0;
+ if (wep_key_len > 0) {
+ wep_key_len = wep_key_len <= 5 ? 5 : 13;
+ pwep = kmalloc((u32)(wep_key_len +
+ FIELD_OFFSET(struct NDIS_802_11_WEP,
+ KeyMaterial)), GFP_ATOMIC);
+ if (pwep == NULL)
+ return -ENOMEM;
+ memset(pwep, 0, sizeof(struct NDIS_802_11_WEP));
+ pwep->KeyLength = wep_key_len;
+ pwep->Length = wep_key_len +
+ FIELD_OFFSET(struct NDIS_802_11_WEP,
+ KeyMaterial);
+ if (wep_key_len == 13) {
+ padapter->securitypriv.PrivacyAlgrthm =
+ _WEP104_;
+ padapter->securitypriv.XGrpPrivacy =
+ _WEP104_;
+ }
+ } else
+ return -EINVAL;
+ pwep->KeyIndex = wep_key_idx;
+ pwep->KeyIndex |= 0x80000000;
+ memcpy(pwep->KeyMaterial, param->u.crypt.key, pwep->KeyLength);
+ if (param->u.crypt.set_tx) {
+ if (r8712_set_802_11_add_wep(padapter, pwep) ==
+ (u8)_FAIL)
+ ret = -EOPNOTSUPP;
+ } else {
+ /* don't update "psecuritypriv->PrivacyAlgrthm" and
+ * "psecuritypriv->PrivacyKeyIndex=keyid", but can
+ * r8712_set_key to fw/cam
+ */
+ if (wep_key_idx >= WEP_KEYS) {
+ ret = -EOPNOTSUPP;
+ goto exit;
+ }
+ memcpy(&(psecuritypriv->DefKey[wep_key_idx].
+ skey[0]), pwep->KeyMaterial,
+ pwep->KeyLength);
+ psecuritypriv->DefKeylen[wep_key_idx] =
+ pwep->KeyLength;
+ r8712_set_key(padapter, psecuritypriv, wep_key_idx);
+ }
+ goto exit;
+ }
+ if (padapter->securitypriv.AuthAlgrthm == 2) { /* 802_1x */
+ struct sta_info *psta, *pbcmc_sta;
+ struct sta_priv *pstapriv = &padapter->stapriv;
+
+ if (check_fwstate(pmlmepriv, WIFI_STATION_STATE |
+ WIFI_MP_STATE) == true) { /* sta mode */
+ psta = r8712_get_stainfo(pstapriv,
+ get_bssid(pmlmepriv));
+ if (psta) {
+ psta->ieee8021x_blocked = false;
+ if ((padapter->securitypriv.ndisencryptstatus ==
+ Ndis802_11Encryption2Enabled) ||
+ (padapter->securitypriv.ndisencryptstatus ==
+ Ndis802_11Encryption3Enabled))
+ psta->XPrivacy = padapter->
+ securitypriv.PrivacyAlgrthm;
+ if (param->u.crypt.set_tx == 1)
+ handle_pairwise_key(psta, param,
+ padapter);
+ else /* group key */
+ handle_group_key(param, padapter);
+ }
+ pbcmc_sta = r8712_get_bcmc_stainfo(padapter);
+ if (pbcmc_sta) {
+ pbcmc_sta->ieee8021x_blocked = false;
+ if ((padapter->securitypriv.ndisencryptstatus ==
+ Ndis802_11Encryption2Enabled) ||
+ (padapter->securitypriv.ndisencryptstatus ==
+ Ndis802_11Encryption3Enabled))
+ pbcmc_sta->XPrivacy =
+ padapter->securitypriv.
+ PrivacyAlgrthm;
+ }
+ }
+ }
+exit:
+ kfree(pwep);
+ return ret;
+}
+
+static int r871x_set_wpa_ie(struct _adapter *padapter, char *pie,
+ unsigned short ielen)
+{
+ u8 *buf = NULL;
+ int group_cipher = 0, pairwise_cipher = 0;
+ int ret = 0;
+
+ if ((ielen > MAX_WPA_IE_LEN) || (pie == NULL))
+ return -EINVAL;
+ if (ielen) {
+ buf = kmemdup(pie, ielen, GFP_ATOMIC);
+ if (buf == NULL)
+ return -ENOMEM;
+ if (ielen < RSN_HEADER_LEN) {
+ ret = -EINVAL;
+ goto exit;
+ }
+ if (r8712_parse_wpa_ie(buf, ielen, &group_cipher,
+ &pairwise_cipher) == _SUCCESS) {
+ padapter->securitypriv.AuthAlgrthm = 2;
+ padapter->securitypriv.ndisauthtype =
+ Ndis802_11AuthModeWPAPSK;
+ }
+ if (r8712_parse_wpa2_ie(buf, ielen, &group_cipher,
+ &pairwise_cipher) == _SUCCESS) {
+ padapter->securitypriv.AuthAlgrthm = 2;
+ padapter->securitypriv.ndisauthtype =
+ Ndis802_11AuthModeWPA2PSK;
+ }
+ switch (group_cipher) {
+ case WPA_CIPHER_NONE:
+ padapter->securitypriv.XGrpPrivacy =
+ _NO_PRIVACY_;
+ padapter->securitypriv.ndisencryptstatus =
+ Ndis802_11EncryptionDisabled;
+ break;
+ case WPA_CIPHER_WEP40:
+ padapter->securitypriv.XGrpPrivacy = _WEP40_;
+ padapter->securitypriv.ndisencryptstatus =
+ Ndis802_11Encryption1Enabled;
+ break;
+ case WPA_CIPHER_TKIP:
+ padapter->securitypriv.XGrpPrivacy = _TKIP_;
+ padapter->securitypriv.ndisencryptstatus =
+ Ndis802_11Encryption2Enabled;
+ break;
+ case WPA_CIPHER_CCMP:
+ padapter->securitypriv.XGrpPrivacy = _AES_;
+ padapter->securitypriv.ndisencryptstatus =
+ Ndis802_11Encryption3Enabled;
+ break;
+ case WPA_CIPHER_WEP104:
+ padapter->securitypriv.XGrpPrivacy = _WEP104_;
+ padapter->securitypriv.ndisencryptstatus =
+ Ndis802_11Encryption1Enabled;
+ break;
+ }
+ switch (pairwise_cipher) {
+ case WPA_CIPHER_NONE:
+ padapter->securitypriv.PrivacyAlgrthm =
+ _NO_PRIVACY_;
+ padapter->securitypriv.ndisencryptstatus =
+ Ndis802_11EncryptionDisabled;
+ break;
+ case WPA_CIPHER_WEP40:
+ padapter->securitypriv.PrivacyAlgrthm = _WEP40_;
+ padapter->securitypriv.ndisencryptstatus =
+ Ndis802_11Encryption1Enabled;
+ break;
+ case WPA_CIPHER_TKIP:
+ padapter->securitypriv.PrivacyAlgrthm = _TKIP_;
+ padapter->securitypriv.ndisencryptstatus =
+ Ndis802_11Encryption2Enabled;
+ break;
+ case WPA_CIPHER_CCMP:
+ padapter->securitypriv.PrivacyAlgrthm = _AES_;
+ padapter->securitypriv.ndisencryptstatus =
+ Ndis802_11Encryption3Enabled;
+ break;
+ case WPA_CIPHER_WEP104:
+ padapter->securitypriv.PrivacyAlgrthm = _WEP104_;
+ padapter->securitypriv.ndisencryptstatus =
+ Ndis802_11Encryption1Enabled;
+ break;
+ }
+ padapter->securitypriv.wps_phase = false;
+ {/* set wps_ie */
+ u16 cnt = 0;
+ u8 eid, wps_oui[4] = {0x0, 0x50, 0xf2, 0x04};
+
+ while (cnt < ielen) {
+ eid = buf[cnt];
+
+ if ((eid == _VENDOR_SPECIFIC_IE_) &&
+ (!memcmp(&buf[cnt+2], wps_oui, 4))) {
+ netdev_info(padapter->pnetdev, "r8712u: SET WPS_IE\n");
+ padapter->securitypriv.wps_ie_len =
+ ((buf[cnt+1] + 2) <
+ (MAX_WPA_IE_LEN << 2)) ?
+ (buf[cnt + 1] + 2) :
+ (MAX_WPA_IE_LEN << 2);
+ memcpy(padapter->securitypriv.wps_ie,
+ &buf[cnt],
+ padapter->securitypriv.wps_ie_len);
+ padapter->securitypriv.wps_phase =
+ true;
+ netdev_info(padapter->pnetdev, "r8712u: SET WPS_IE, wps_phase==true\n");
+ cnt += buf[cnt+1]+2;
+ break;
+ } else
+ cnt += buf[cnt + 1] + 2;
+ }
+ }
+ }
+exit:
+ kfree(buf);
+ return ret;
+}
+
+static int r8711_wx_get_name(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct _adapter *padapter = netdev_priv(dev);
+ u32 ht_ielen = 0;
+ char *p;
+ u8 ht_cap = false;
+ struct mlme_priv *pmlmepriv = &(padapter->mlmepriv);
+ struct ndis_wlan_bssid_ex *pcur_bss = &pmlmepriv->cur_network.network;
+ NDIS_802_11_RATES_EX *prates = NULL;
+
+ if (check_fwstate(pmlmepriv, _FW_LINKED|WIFI_ADHOC_MASTER_STATE) ==
+ true) {
+ /* parsing HT_CAP_IE */
+ p = r8712_get_ie(&pcur_bss->IEs[12], _HT_CAPABILITY_IE_,
+ &ht_ielen, pcur_bss->IELength - 12);
+ if (p && ht_ielen > 0)
+ ht_cap = true;
+ prates = &pcur_bss->SupportedRates;
+ if (r8712_is_cckratesonly_included((u8 *)prates) == true) {
+ if (ht_cap == true)
+ snprintf(wrqu->name, IFNAMSIZ,
+ "IEEE 802.11bn");
+ else
+ snprintf(wrqu->name, IFNAMSIZ,
+ "IEEE 802.11b");
+ } else if ((r8712_is_cckrates_included((u8 *)prates)) == true) {
+ if (ht_cap == true)
+ snprintf(wrqu->name, IFNAMSIZ,
+ "IEEE 802.11bgn");
+ else
+ snprintf(wrqu->name, IFNAMSIZ,
+ "IEEE 802.11bg");
+ } else {
+ if (ht_cap == true)
+ snprintf(wrqu->name, IFNAMSIZ,
+ "IEEE 802.11gn");
+ else
+ snprintf(wrqu->name, IFNAMSIZ,
+ "IEEE 802.11g");
+ }
+ } else
+ snprintf(wrqu->name, IFNAMSIZ, "unassociated");
+ return 0;
+}
+
+static const long frequency_list[] = {
+ 2412, 2417, 2422, 2427, 2432, 2437, 2442, 2447, 2452, 2457, 2462,
+ 2467, 2472, 2484, 4915, 4920, 4925, 4935, 4940, 4945, 4960, 4980,
+ 5035, 5040, 5045, 5055, 5060, 5080, 5170, 5180, 5190, 5200, 5210,
+ 5220, 5230, 5240, 5260, 5280, 5300, 5320, 5500, 5520, 5540, 5560,
+ 5580, 5600, 5620, 5640, 5660, 5680, 5700, 5745, 5765, 5785, 5805,
+ 5825
+};
+
+static int r8711_wx_set_freq(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct _adapter *padapter = netdev_priv(dev);
+ struct iw_freq *fwrq = &wrqu->freq;
+ int rc = 0;
+
+/* If setting by frequency, convert to a channel */
+ if ((fwrq->e == 1) &&
+ (fwrq->m >= (int) 2.412e8) &&
+ (fwrq->m <= (int) 2.487e8)) {
+ int f = fwrq->m / 100000;
+ int c = 0;
+
+ while ((c < 14) && (f != frequency_list[c]))
+ c++;
+ fwrq->e = 0;
+ fwrq->m = c + 1;
+ }
+ /* Setting by channel number */
+ if ((fwrq->m > 14) || (fwrq->e > 0))
+ rc = -EOPNOTSUPP;
+ else {
+ int channel = fwrq->m;
+
+ if ((channel < 1) || (channel > 14))
+ rc = -EINVAL;
+ else {
+ /* Yes ! We can set it !!! */
+ padapter->registrypriv.channel = channel;
+ }
+ }
+ return rc;
+}
+
+static int r8711_wx_get_freq(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct _adapter *padapter = netdev_priv(dev);
+ struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
+ struct ndis_wlan_bssid_ex *pcur_bss = &pmlmepriv->cur_network.network;
+
+ if (check_fwstate(pmlmepriv, _FW_LINKED) == true) {
+ wrqu->freq.m = ieee80211_wlan_frequencies[
+ pcur_bss->Configuration.DSConfig-1] * 100000;
+ wrqu->freq.e = 1;
+ wrqu->freq.i = pcur_bss->Configuration.DSConfig;
+ } else {
+ return -ENOLINK;
+ }
+ return 0;
+}
+
+static int r8711_wx_set_mode(struct net_device *dev,
+ struct iw_request_info *a,
+ union iwreq_data *wrqu, char *b)
+{
+ struct _adapter *padapter = netdev_priv(dev);
+ enum NDIS_802_11_NETWORK_INFRASTRUCTURE networkType;
+
+ switch (wrqu->mode) {
+ case IW_MODE_AUTO:
+ networkType = Ndis802_11AutoUnknown;
+ break;
+ case IW_MODE_ADHOC:
+ networkType = Ndis802_11IBSS;
+ break;
+ case IW_MODE_MASTER:
+ networkType = Ndis802_11APMode;
+ break;
+ case IW_MODE_INFRA:
+ networkType = Ndis802_11Infrastructure;
+ break;
+ default:
+ return -EINVAL;
+ }
+ if (Ndis802_11APMode == networkType)
+ r8712_setopmode_cmd(padapter, networkType);
+ else
+ r8712_setopmode_cmd(padapter, Ndis802_11AutoUnknown);
+
+ r8712_set_802_11_infrastructure_mode(padapter, networkType);
+ return 0;
+}
+
+static int r8711_wx_get_mode(struct net_device *dev, struct iw_request_info *a,
+ union iwreq_data *wrqu, char *b)
+{
+ struct _adapter *padapter = netdev_priv(dev);
+ struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
+
+ if (check_fwstate(pmlmepriv, WIFI_STATION_STATE) == true)
+ wrqu->mode = IW_MODE_INFRA;
+ else if (check_fwstate(pmlmepriv,
+ WIFI_ADHOC_MASTER_STATE|WIFI_ADHOC_STATE) == true)
+ wrqu->mode = IW_MODE_ADHOC;
+ else if (check_fwstate(pmlmepriv, WIFI_AP_STATE) == true)
+ wrqu->mode = IW_MODE_MASTER;
+ else
+ wrqu->mode = IW_MODE_AUTO;
+ return 0;
+}
+
+static int r871x_wx_set_pmkid(struct net_device *dev,
+ struct iw_request_info *a,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct _adapter *padapter = netdev_priv(dev);
+ struct security_priv *psecuritypriv = &padapter->securitypriv;
+ struct iw_pmksa *pPMK = (struct iw_pmksa *) extra;
+ u8 strZeroMacAddress[ETH_ALEN] = {0x00};
+ u8 strIssueBssid[ETH_ALEN] = {0x00};
+ u8 j, blInserted = false;
+ int intReturn = false;
+
+/*
+ There are the BSSID information in the bssid.sa_data array.
+ If cmd is IW_PMKSA_FLUSH, it means the wpa_supplicant wants to clear
+ all the PMKID information. If cmd is IW_PMKSA_ADD, it means the
+ wpa_supplicant wants to add a PMKID/BSSID to driver.
+ If cmd is IW_PMKSA_REMOVE, it means the wpa_supplicant wants to
+ remove a PMKID/BSSID from driver.
+*/
+ if (pPMK == NULL)
+ return -EINVAL;
+ memcpy(strIssueBssid, pPMK->bssid.sa_data, ETH_ALEN);
+ switch (pPMK->cmd) {
+ case IW_PMKSA_ADD:
+ if (!memcmp(strIssueBssid, strZeroMacAddress, ETH_ALEN))
+ return intReturn;
+ else
+ intReturn = true;
+ blInserted = false;
+ /* overwrite PMKID */
+ for (j = 0; j < NUM_PMKID_CACHE; j++) {
+ if (!memcmp(psecuritypriv->PMKIDList[j].Bssid,
+ strIssueBssid, ETH_ALEN)) {
+ /* BSSID is matched, the same AP => rewrite
+ * with new PMKID. */
+ netdev_info(dev, "r8712u: %s: BSSID exists in the PMKList.\n",
+ __func__);
+ memcpy(psecuritypriv->PMKIDList[j].PMKID,
+ pPMK->pmkid, IW_PMKID_LEN);
+ psecuritypriv->PMKIDList[j].bUsed = true;
+ psecuritypriv->PMKIDIndex = j + 1;
+ blInserted = true;
+ break;
+ }
+ }
+ if (!blInserted) {
+ /* Find a new entry */
+ netdev_info(dev, "r8712u: %s: Use the new entry index = %d for this PMKID.\n",
+ __func__, psecuritypriv->PMKIDIndex);
+ memcpy(psecuritypriv->PMKIDList[psecuritypriv->
+ PMKIDIndex].Bssid, strIssueBssid, ETH_ALEN);
+ memcpy(psecuritypriv->PMKIDList[psecuritypriv->
+ PMKIDIndex].PMKID, pPMK->pmkid, IW_PMKID_LEN);
+ psecuritypriv->PMKIDList[psecuritypriv->PMKIDIndex].
+ bUsed = true;
+ psecuritypriv->PMKIDIndex++;
+ if (psecuritypriv->PMKIDIndex == NUM_PMKID_CACHE)
+ psecuritypriv->PMKIDIndex = 0;
+ }
+ break;
+ case IW_PMKSA_REMOVE:
+ intReturn = true;
+ for (j = 0; j < NUM_PMKID_CACHE; j++) {
+ if (!memcmp(psecuritypriv->PMKIDList[j].Bssid,
+ strIssueBssid, ETH_ALEN)) {
+ /* BSSID is matched, the same AP => Remove
+ * this PMKID information and reset it. */
+ eth_zero_addr(psecuritypriv->PMKIDList[j].Bssid);
+ psecuritypriv->PMKIDList[j].bUsed = false;
+ break;
+ }
+ }
+ break;
+ case IW_PMKSA_FLUSH:
+ memset(psecuritypriv->PMKIDList, 0,
+ sizeof(struct RT_PMKID_LIST) * NUM_PMKID_CACHE);
+ psecuritypriv->PMKIDIndex = 0;
+ intReturn = true;
+ break;
+ default:
+ netdev_info(dev, "r8712u: %s: unknown Command\n", __func__);
+ intReturn = false;
+ break;
+ }
+ return intReturn;
+}
+
+static int r8711_wx_get_sens(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ wrqu->sens.value = 0;
+ wrqu->sens.fixed = 0; /* no auto select */
+ wrqu->sens.disabled = 1;
+ return 0;
+}
+
+static int r8711_wx_get_range(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct iw_range *range = (struct iw_range *)extra;
+ u16 val;
+ int i;
+
+ wrqu->data.length = sizeof(*range);
+ memset(range, 0, sizeof(*range));
+ /* Let's try to keep this struct in the same order as in
+ * linux/include/wireless.h
+ */
+
+ /* TODO: See what values we can set, and remove the ones we can't
+ * set, or fill them with some default data.
+ */
+ /* ~5 Mb/s real (802.11b) */
+ range->throughput = 5 * 1000 * 1000;
+ /* TODO: 8711 sensitivity ? */
+ /* signal level threshold range */
+ /* percent values between 0 and 100. */
+ range->max_qual.qual = 100;
+ range->max_qual.level = 100;
+ range->max_qual.noise = 100;
+ range->max_qual.updated = 7; /* Updated all three */
+ range->avg_qual.qual = 92; /* > 8% missed beacons is 'bad' */
+ /* TODO: Find real 'good' to 'bad' threshold value for RSSI */
+ range->avg_qual.level = 20 + -98;
+ range->avg_qual.noise = 0;
+ range->avg_qual.updated = 7; /* Updated all three */
+ range->num_bitrates = RATE_COUNT;
+ for (i = 0; i < RATE_COUNT && i < IW_MAX_BITRATES; i++)
+ range->bitrate[i] = rtl8180_rates[i];
+ range->min_frag = MIN_FRAG_THRESHOLD;
+ range->max_frag = MAX_FRAG_THRESHOLD;
+ range->pm_capa = 0;
+ range->we_version_compiled = WIRELESS_EXT;
+ range->we_version_source = 16;
+ range->num_channels = 14;
+ for (i = 0, val = 0; i < 14; i++) {
+ /* Include only legal frequencies for some countries */
+ range->freq[val].i = i + 1;
+ range->freq[val].m = ieee80211_wlan_frequencies[i] * 100000;
+ range->freq[val].e = 1;
+ val++;
+ if (val == IW_MAX_FREQUENCIES)
+ break;
+ }
+ range->num_frequency = val;
+ range->enc_capa = IW_ENC_CAPA_WPA |
+ IW_ENC_CAPA_WPA2 |
+ IW_ENC_CAPA_CIPHER_TKIP |
+ IW_ENC_CAPA_CIPHER_CCMP;
+ return 0;
+}
+
+static int r8711_wx_get_rate(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra);
+
+static int r871x_wx_set_priv(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *awrq,
+ char *extra)
+{
+ int ret = 0, len = 0;
+ char *ext;
+ struct _adapter *padapter = netdev_priv(dev);
+ struct iw_point *dwrq = (struct iw_point *)awrq;
+
+ len = dwrq->length;
+ ext = memdup_user(dwrq->pointer, len);
+ if (IS_ERR(ext))
+ return PTR_ERR(ext);
+
+ if (0 == strcasecmp(ext, "RSSI")) {
+ /*Return received signal strength indicator in -db for */
+ /* current AP */
+ /*<ssid> Rssi xx */
+ struct mlme_priv *pmlmepriv = &(padapter->mlmepriv);
+ struct wlan_network *pcur_network = &pmlmepriv->cur_network;
+ /*static u8 xxxx; */
+ if (check_fwstate(pmlmepriv, _FW_LINKED) == true) {
+ sprintf(ext, "%s rssi %d",
+ pcur_network->network.Ssid.Ssid,
+ /*(xxxx=xxxx+10) */
+ ((padapter->recvpriv.fw_rssi)>>1)-95
+ /*pcur_network->network.Rssi */
+ );
+ } else {
+ sprintf(ext, "OK");
+ }
+ } else if (0 == strcasecmp(ext, "LINKSPEED")) {
+ /*Return link speed in MBPS */
+ /*LinkSpeed xx */
+ union iwreq_data wrqd;
+ int ret_inner;
+ int mbps;
+
+ ret_inner = r8711_wx_get_rate(dev, info, &wrqd, extra);
+ if (0 != ret_inner)
+ mbps = 0;
+ else
+ mbps = wrqd.bitrate.value / 1000000;
+ sprintf(ext, "LINKSPEED %d", mbps);
+ } else if (0 == strcasecmp(ext, "MACADDR")) {
+ /*Return mac address of the station */
+ /* Macaddr = xx:xx:xx:xx:xx:xx */
+ sprintf(ext, "MACADDR = %pM", dev->dev_addr);
+ } else if (0 == strcasecmp(ext, "SCAN-ACTIVE")) {
+ /*Set scan type to active */
+ /*OK if successful */
+ struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
+
+ pmlmepriv->passive_mode = 1;
+ sprintf(ext, "OK");
+ } else if (0 == strcasecmp(ext, "SCAN-PASSIVE")) {
+ /*Set scan type to passive */
+ /*OK if successful */
+ struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
+
+ pmlmepriv->passive_mode = 0;
+ sprintf(ext, "OK");
+ } else if (0 == strncmp(ext, "DCE-E", 5)) {
+ /*Set scan type to passive */
+ /*OK if successful */
+ r8712_disconnectCtrlEx_cmd(padapter
+ , 1 /*u32 enableDrvCtrl */
+ , 5 /*u32 tryPktCnt */
+ , 100 /*u32 tryPktInterval */
+ , 5000 /*u32 firstStageTO */
+ );
+ sprintf(ext, "OK");
+ } else if (0 == strncmp(ext, "DCE-D", 5)) {
+ /*Set scan type to passive */
+ /*OK if successfu */
+ r8712_disconnectCtrlEx_cmd(padapter
+ , 0 /*u32 enableDrvCtrl */
+ , 5 /*u32 tryPktCnt */
+ , 100 /*u32 tryPktInterval */
+ , 5000 /*u32 firstStageTO */
+ );
+ sprintf(ext, "OK");
+ } else {
+ netdev_info(dev, "r8712u: %s: unknown Command %s.\n",
+ __func__, ext);
+ goto FREE_EXT;
+ }
+ if (copy_to_user(dwrq->pointer, ext,
+ min(dwrq->length, (__u16)(strlen(ext)+1))))
+ ret = -EFAULT;
+
+FREE_EXT:
+ kfree(ext);
+ return ret;
+}
+
+/* set bssid flow
+ * s1. set_802_11_infrastructure_mode()
+ * s2. set_802_11_authentication_mode()
+ * s3. set_802_11_encryption_mode()
+ * s4. set_802_11_bssid()
+ *
+ * This function intends to handle the Set AP command, which specifies the
+ * MAC# of a preferred Access Point.
+ * Currently, the request comes via Wireless Extensions' SIOCSIWAP ioctl.
+ *
+ * For this operation to succeed, there is no need for the interface to be up.
+ *
+ */
+static int r8711_wx_set_wap(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *awrq,
+ char *extra)
+{
+ int ret = -EINPROGRESS;
+ struct _adapter *padapter = netdev_priv(dev);
+ struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
+ struct __queue *queue = &pmlmepriv->scanned_queue;
+ struct sockaddr *temp = (struct sockaddr *)awrq;
+ unsigned long irqL;
+ struct list_head *phead;
+ u8 *dst_bssid;
+ struct wlan_network *pnetwork = NULL;
+ enum NDIS_802_11_AUTHENTICATION_MODE authmode;
+
+ if (check_fwstate(pmlmepriv, _FW_UNDER_SURVEY) == true)
+ return -EBUSY;
+ if (check_fwstate(pmlmepriv, _FW_UNDER_LINKING) == true)
+ return ret;
+ if (temp->sa_family != ARPHRD_ETHER)
+ return -EINVAL;
+ authmode = padapter->securitypriv.ndisauthtype;
+ spin_lock_irqsave(&queue->lock, irqL);
+ phead = &queue->queue;
+ pmlmepriv->pscanned = phead->next;
+ while (1) {
+ if (end_of_queue_search(phead, pmlmepriv->pscanned) == true)
+ break;
+ pnetwork = LIST_CONTAINOR(pmlmepriv->pscanned,
+ struct wlan_network, list);
+ pmlmepriv->pscanned = pmlmepriv->pscanned->next;
+ dst_bssid = pnetwork->network.MacAddress;
+ if (!memcmp(dst_bssid, temp->sa_data, ETH_ALEN)) {
+ r8712_set_802_11_infrastructure_mode(padapter,
+ pnetwork->network.InfrastructureMode);
+ break;
+ }
+ }
+ spin_unlock_irqrestore(&queue->lock, irqL);
+ if (!ret) {
+ if (!r8712_set_802_11_authentication_mode(padapter, authmode))
+ ret = -ENOMEM;
+ else {
+ if (!r8712_set_802_11_bssid(padapter, temp->sa_data))
+ ret = -1;
+ }
+ }
+ return ret;
+}
+
+static int r8711_wx_get_wap(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct _adapter *padapter = netdev_priv(dev);
+ struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
+ struct ndis_wlan_bssid_ex *pcur_bss = &pmlmepriv->cur_network.network;
+
+ wrqu->ap_addr.sa_family = ARPHRD_ETHER;
+ if (check_fwstate(pmlmepriv, _FW_LINKED | WIFI_ADHOC_MASTER_STATE |
+ WIFI_AP_STATE))
+ ether_addr_copy(wrqu->ap_addr.sa_data, pcur_bss->MacAddress);
+ else
+ eth_zero_addr(wrqu->ap_addr.sa_data);
+ return 0;
+}
+
+static int r871x_wx_set_mlme(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ int ret = 0;
+ struct _adapter *padapter = netdev_priv(dev);
+ struct iw_mlme *mlme = (struct iw_mlme *) extra;
+
+ if (mlme == NULL)
+ return -1;
+ switch (mlme->cmd) {
+ case IW_MLME_DEAUTH:
+ if (!r8712_set_802_11_disassociate(padapter))
+ ret = -1;
+ break;
+ case IW_MLME_DISASSOC:
+ if (!r8712_set_802_11_disassociate(padapter))
+ ret = -1;
+ break;
+ default:
+ return -EOPNOTSUPP;
+ }
+ return ret;
+}
+
+/**
+ *
+ * This function intends to handle the Set Scan command.
+ * Currently, the request comes via Wireless Extensions' SIOCSIWSCAN ioctl.
+ *
+ * For this operation to succeed, the interface is brought Up beforehand.
+ *
+ */
+static int r8711_wx_set_scan(struct net_device *dev,
+ struct iw_request_info *a,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct _adapter *padapter = netdev_priv(dev);
+ struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
+ u8 status = true;
+
+ if (padapter->bDriverStopped == true) {
+ netdev_info(dev, "In %s: bDriverStopped=%d\n",
+ __func__, padapter->bDriverStopped);
+ return -1;
+ }
+ if (padapter->bup == false)
+ return -ENETDOWN;
+ if (padapter->hw_init_completed == false)
+ return -1;
+ if ((check_fwstate(pmlmepriv, _FW_UNDER_SURVEY|_FW_UNDER_LINKING)) ||
+ (pmlmepriv->sitesurveyctrl.traffic_busy == true))
+ return 0;
+ if (wrqu->data.length == sizeof(struct iw_scan_req)) {
+ struct iw_scan_req *req = (struct iw_scan_req *)extra;
+
+ if (wrqu->data.flags & IW_SCAN_THIS_ESSID) {
+ struct ndis_802_11_ssid ssid;
+ unsigned long irqL;
+ u32 len = min_t(u8, req->essid_len, IW_ESSID_MAX_SIZE);
+
+ memset((unsigned char *)&ssid, 0,
+ sizeof(struct ndis_802_11_ssid));
+ memcpy(ssid.Ssid, req->essid, len);
+ ssid.SsidLength = len;
+ spin_lock_irqsave(&pmlmepriv->lock, irqL);
+ if ((check_fwstate(pmlmepriv, _FW_UNDER_SURVEY |
+ _FW_UNDER_LINKING)) ||
+ (pmlmepriv->sitesurveyctrl.traffic_busy == true)) {
+ if (check_fwstate(pmlmepriv, _FW_UNDER_LINKING))
+ status = false;
+ } else
+ status = r8712_sitesurvey_cmd(padapter, &ssid);
+ spin_unlock_irqrestore(&pmlmepriv->lock, irqL);
+ }
+ } else
+ status = r8712_set_802_11_bssid_list_scan(padapter);
+ if (status == false)
+ return -1;
+ return 0;
+}
+
+static int r8711_wx_get_scan(struct net_device *dev,
+ struct iw_request_info *a,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct _adapter *padapter = netdev_priv(dev);
+ struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
+ struct __queue *queue = &pmlmepriv->scanned_queue;
+ struct wlan_network *pnetwork = NULL;
+ unsigned long irqL;
+ struct list_head *plist, *phead;
+ char *ev = extra;
+ char *stop = ev + wrqu->data.length;
+ u32 ret = 0, cnt = 0;
+
+ if (padapter->bDriverStopped)
+ return -EINVAL;
+ while (check_fwstate(pmlmepriv, _FW_UNDER_SURVEY|_FW_UNDER_LINKING)) {
+ msleep(30);
+ cnt++;
+ if (cnt > 100)
+ break;
+ }
+ spin_lock_irqsave(&queue->lock, irqL);
+ phead = &queue->queue;
+ plist = phead->next;
+ while (1) {
+ if (end_of_queue_search(phead, plist) == true)
+ break;
+ if ((stop - ev) < SCAN_ITEM_SIZE) {
+ ret = -E2BIG;
+ break;
+ }
+ pnetwork = LIST_CONTAINOR(plist, struct wlan_network, list);
+ ev = translate_scan(padapter, a, pnetwork, ev, stop);
+ plist = plist->next;
+ }
+ spin_unlock_irqrestore(&queue->lock, irqL);
+ wrqu->data.length = ev - extra;
+ wrqu->data.flags = 0;
+ return ret;
+}
+
+/* set ssid flow
+ * s1. set_802_11_infrastructure_mode()
+ * s2. set_802_11_authenticaion_mode()
+ * s3. set_802_11_encryption_mode()
+ * s4. set_802_11_ssid()
+ *
+ * This function intends to handle the Set ESSID command.
+ * Currently, the request comes via the Wireless Extensions' SIOCSIWESSID ioctl.
+ *
+ * For this operation to succeed, there is no need for the interface to be Up.
+ *
+ */
+static int r8711_wx_set_essid(struct net_device *dev,
+ struct iw_request_info *a,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct _adapter *padapter = netdev_priv(dev);
+ struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
+ struct __queue *queue = &pmlmepriv->scanned_queue;
+ struct wlan_network *pnetwork = NULL;
+ enum NDIS_802_11_AUTHENTICATION_MODE authmode;
+ struct ndis_802_11_ssid ndis_ssid;
+ u8 *dst_ssid, *src_ssid;
+ struct list_head *phead;
+ u32 len;
+
+ if (check_fwstate(pmlmepriv, _FW_UNDER_SURVEY))
+ return -EBUSY;
+ if (check_fwstate(pmlmepriv, _FW_UNDER_LINKING))
+ return 0;
+ if (wrqu->essid.length > IW_ESSID_MAX_SIZE)
+ return -E2BIG;
+ authmode = padapter->securitypriv.ndisauthtype;
+ if (wrqu->essid.flags && wrqu->essid.length) {
+ len = (wrqu->essid.length < IW_ESSID_MAX_SIZE) ?
+ wrqu->essid.length : IW_ESSID_MAX_SIZE;
+ memset(&ndis_ssid, 0, sizeof(struct ndis_802_11_ssid));
+ ndis_ssid.SsidLength = len;
+ memcpy(ndis_ssid.Ssid, extra, len);
+ src_ssid = ndis_ssid.Ssid;
+ phead = &queue->queue;
+ pmlmepriv->pscanned = phead->next;
+ while (1) {
+ if (end_of_queue_search(phead, pmlmepriv->pscanned))
+ break;
+ pnetwork = LIST_CONTAINOR(pmlmepriv->pscanned,
+ struct wlan_network, list);
+ pmlmepriv->pscanned = pmlmepriv->pscanned->next;
+ dst_ssid = pnetwork->network.Ssid.Ssid;
+ if ((!memcmp(dst_ssid, src_ssid, ndis_ssid.SsidLength))
+ && (pnetwork->network.Ssid.SsidLength ==
+ ndis_ssid.SsidLength)) {
+ if (check_fwstate(pmlmepriv,
+ WIFI_ADHOC_STATE)) {
+ if (pnetwork->network.
+ InfrastructureMode
+ !=
+ padapter->mlmepriv.
+ cur_network.network.
+ InfrastructureMode)
+ continue;
+ }
+
+ r8712_set_802_11_infrastructure_mode(
+ padapter,
+ pnetwork->network.InfrastructureMode);
+ break;
+ }
+ }
+ r8712_set_802_11_authentication_mode(padapter, authmode);
+ r8712_set_802_11_ssid(padapter, &ndis_ssid);
+ }
+ return -EINPROGRESS;
+}
+
+static int r8711_wx_get_essid(struct net_device *dev,
+ struct iw_request_info *a,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct _adapter *padapter = netdev_priv(dev);
+ struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
+ struct ndis_wlan_bssid_ex *pcur_bss = &pmlmepriv->cur_network.network;
+ u32 len, ret = 0;
+
+ if (check_fwstate(pmlmepriv, _FW_LINKED|WIFI_ADHOC_MASTER_STATE)) {
+ len = pcur_bss->Ssid.SsidLength;
+ wrqu->essid.length = len;
+ memcpy(extra, pcur_bss->Ssid.Ssid, len);
+ wrqu->essid.flags = 1;
+ } else {
+ ret = -ENOLINK;
+ }
+ return ret;
+}
+
+static int r8711_wx_set_rate(struct net_device *dev,
+ struct iw_request_info *a,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct _adapter *padapter = netdev_priv(dev);
+ u32 target_rate = wrqu->bitrate.value;
+ u32 fixed = wrqu->bitrate.fixed;
+ u32 ratevalue = 0;
+ u8 datarates[NumRates];
+ u8 mpdatarate[NumRates] = {11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 0xff};
+ int i, ret = 0;
+
+ if (target_rate == -1) {
+ ratevalue = 11;
+ goto set_rate;
+ }
+ target_rate = target_rate / 100000;
+ switch (target_rate) {
+ case 10:
+ ratevalue = 0;
+ break;
+ case 20:
+ ratevalue = 1;
+ break;
+ case 55:
+ ratevalue = 2;
+ break;
+ case 60:
+ ratevalue = 3;
+ break;
+ case 90:
+ ratevalue = 4;
+ break;
+ case 110:
+ ratevalue = 5;
+ break;
+ case 120:
+ ratevalue = 6;
+ break;
+ case 180:
+ ratevalue = 7;
+ break;
+ case 240:
+ ratevalue = 8;
+ break;
+ case 360:
+ ratevalue = 9;
+ break;
+ case 480:
+ ratevalue = 10;
+ break;
+ case 540:
+ ratevalue = 11;
+ break;
+ default:
+ ratevalue = 11;
+ break;
+ }
+set_rate:
+ for (i = 0; i < NumRates; i++) {
+ if (ratevalue == mpdatarate[i]) {
+ datarates[i] = mpdatarate[i];
+ if (fixed == 0)
+ break;
+ } else
+ datarates[i] = 0xff;
+ }
+ if (r8712_setdatarate_cmd(padapter, datarates) != _SUCCESS)
+ ret = -ENOMEM;
+ return ret;
+}
+
+static int r8711_wx_get_rate(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct _adapter *padapter = netdev_priv(dev);
+ struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
+ struct ndis_wlan_bssid_ex *pcur_bss = &pmlmepriv->cur_network.network;
+ struct ieee80211_ht_cap *pht_capie;
+ unsigned char rf_type = padapter->registrypriv.rf_config;
+ int i;
+ u8 *p;
+ u16 rate, max_rate = 0, ht_cap = false;
+ u32 ht_ielen = 0;
+ u8 bw_40MHz = 0, short_GI = 0;
+ u16 mcs_rate = 0;
+
+ i = 0;
+ if (check_fwstate(pmlmepriv, _FW_LINKED|WIFI_ADHOC_MASTER_STATE)) {
+ p = r8712_get_ie(&pcur_bss->IEs[12],
+ _HT_CAPABILITY_IE_, &ht_ielen,
+ pcur_bss->IELength - 12);
+ if (p && ht_ielen > 0) {
+ ht_cap = true;
+ pht_capie = (struct ieee80211_ht_cap *)(p + 2);
+ memcpy(&mcs_rate, pht_capie->supp_mcs_set, 2);
+ bw_40MHz = (pht_capie->cap_info &
+ IEEE80211_HT_CAP_SUP_WIDTH) ? 1 : 0;
+ short_GI = (pht_capie->cap_info &
+ (IEEE80211_HT_CAP_SGI_20 |
+ IEEE80211_HT_CAP_SGI_40)) ? 1 : 0;
+ }
+ while ((pcur_bss->SupportedRates[i] != 0) &&
+ (pcur_bss->SupportedRates[i] != 0xFF)) {
+ rate = pcur_bss->SupportedRates[i] & 0x7F;
+ if (rate > max_rate)
+ max_rate = rate;
+ wrqu->bitrate.fixed = 0; /* no auto select */
+ wrqu->bitrate.value = rate*500000;
+ i++;
+ }
+ if (ht_cap == true) {
+ if (mcs_rate & 0x8000 /* MCS15 */
+ &&
+ RTL8712_RF_2T2R == rf_type)
+ max_rate = (bw_40MHz) ? ((short_GI) ? 300 :
+ 270) : ((short_GI) ? 144 : 130);
+ else /* default MCS7 */
+ max_rate = (bw_40MHz) ? ((short_GI) ? 150 :
+ 135) : ((short_GI) ? 72 : 65);
+ max_rate *= 2; /* Mbps/2 */
+ }
+ wrqu->bitrate.value = max_rate * 500000;
+ } else
+ return -ENOLINK;
+ return 0;
+}
+
+static int r8711_wx_get_rts(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct _adapter *padapter = netdev_priv(dev);
+
+ wrqu->rts.value = padapter->registrypriv.rts_thresh;
+ wrqu->rts.fixed = 0; /* no auto select */
+ return 0;
+}
+
+static int r8711_wx_set_frag(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct _adapter *padapter = netdev_priv(dev);
+
+ if (wrqu->frag.disabled)
+ padapter->xmitpriv.frag_len = MAX_FRAG_THRESHOLD;
+ else {
+ if (wrqu->frag.value < MIN_FRAG_THRESHOLD ||
+ wrqu->frag.value > MAX_FRAG_THRESHOLD)
+ return -EINVAL;
+ padapter->xmitpriv.frag_len = wrqu->frag.value & ~0x1;
+ }
+ return 0;
+}
+
+static int r8711_wx_get_frag(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct _adapter *padapter = netdev_priv(dev);
+
+ wrqu->frag.value = padapter->xmitpriv.frag_len;
+ wrqu->frag.fixed = 0; /* no auto select */
+ return 0;
+}
+
+static int r8711_wx_get_retry(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ wrqu->retry.value = 7;
+ wrqu->retry.fixed = 0; /* no auto select */
+ wrqu->retry.disabled = 1;
+ return 0;
+}
+
+static int r8711_wx_set_enc(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *keybuf)
+{
+ u32 key;
+ u32 keyindex_provided;
+ struct NDIS_802_11_WEP wep;
+ enum NDIS_802_11_AUTHENTICATION_MODE authmode;
+ struct iw_point *erq = &(wrqu->encoding);
+ struct _adapter *padapter = netdev_priv(dev);
+
+ key = erq->flags & IW_ENCODE_INDEX;
+ memset(&wep, 0, sizeof(struct NDIS_802_11_WEP));
+ if (erq->flags & IW_ENCODE_DISABLED) {
+ netdev_info(dev, "r8712u: %s: EncryptionDisabled\n", __func__);
+ padapter->securitypriv.ndisencryptstatus =
+ Ndis802_11EncryptionDisabled;
+ padapter->securitypriv.PrivacyAlgrthm = _NO_PRIVACY_;
+ padapter->securitypriv.XGrpPrivacy = _NO_PRIVACY_;
+ padapter->securitypriv.AuthAlgrthm = 0; /* open system */
+ authmode = Ndis802_11AuthModeOpen;
+ padapter->securitypriv.ndisauthtype = authmode;
+ return 0;
+ }
+ if (key) {
+ if (key > WEP_KEYS)
+ return -EINVAL;
+ key--;
+ keyindex_provided = 1;
+ } else {
+ keyindex_provided = 0;
+ key = padapter->securitypriv.PrivacyKeyIndex;
+ }
+ /* set authentication mode */
+ if (erq->flags & IW_ENCODE_OPEN) {
+ netdev_info(dev, "r8712u: %s: IW_ENCODE_OPEN\n", __func__);
+ padapter->securitypriv.ndisencryptstatus =
+ Ndis802_11Encryption1Enabled;
+ padapter->securitypriv.AuthAlgrthm = 0; /* open system */
+ padapter->securitypriv.PrivacyAlgrthm = _NO_PRIVACY_;
+ padapter->securitypriv.XGrpPrivacy = _NO_PRIVACY_;
+ authmode = Ndis802_11AuthModeOpen;
+ padapter->securitypriv.ndisauthtype = authmode;
+ } else if (erq->flags & IW_ENCODE_RESTRICTED) {
+ netdev_info(dev,
+ "r8712u: %s: IW_ENCODE_RESTRICTED\n", __func__);
+ padapter->securitypriv.ndisencryptstatus =
+ Ndis802_11Encryption1Enabled;
+ padapter->securitypriv.AuthAlgrthm = 1; /* shared system */
+ padapter->securitypriv.PrivacyAlgrthm = _WEP40_;
+ padapter->securitypriv.XGrpPrivacy = _WEP40_;
+ authmode = Ndis802_11AuthModeShared;
+ padapter->securitypriv.ndisauthtype = authmode;
+ } else {
+ padapter->securitypriv.ndisencryptstatus =
+ Ndis802_11Encryption1Enabled;
+ padapter->securitypriv.AuthAlgrthm = 0; /* open system */
+ padapter->securitypriv.PrivacyAlgrthm = _NO_PRIVACY_;
+ padapter->securitypriv.XGrpPrivacy = _NO_PRIVACY_;
+ authmode = Ndis802_11AuthModeOpen;
+ padapter->securitypriv.ndisauthtype = authmode;
+ }
+ wep.KeyIndex = key;
+ if (erq->length > 0) {
+ wep.KeyLength = erq->length <= 5 ? 5 : 13;
+ wep.Length = wep.KeyLength +
+ FIELD_OFFSET(struct NDIS_802_11_WEP, KeyMaterial);
+ } else {
+ wep.KeyLength = 0;
+ if (keyindex_provided == 1) { /* set key_id only, no given
+ * KeyMaterial(erq->length==0).*/
+ padapter->securitypriv.PrivacyKeyIndex = key;
+ switch (padapter->securitypriv.DefKeylen[key]) {
+ case 5:
+ padapter->securitypriv.PrivacyAlgrthm =
+ _WEP40_;
+ break;
+ case 13:
+ padapter->securitypriv.PrivacyAlgrthm =
+ _WEP104_;
+ break;
+ default:
+ padapter->securitypriv.PrivacyAlgrthm =
+ _NO_PRIVACY_;
+ break;
+ }
+ return 0;
+ }
+ }
+ wep.KeyIndex |= 0x80000000; /* transmit key */
+ memcpy(wep.KeyMaterial, keybuf, wep.KeyLength);
+ if (r8712_set_802_11_add_wep(padapter, &wep) == _FAIL)
+ return -EOPNOTSUPP;
+ return 0;
+}
+
+static int r8711_wx_get_enc(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *keybuf)
+{
+ uint key, ret = 0;
+ struct _adapter *padapter = netdev_priv(dev);
+ struct iw_point *erq = &(wrqu->encoding);
+ struct mlme_priv *pmlmepriv = &(padapter->mlmepriv);
+
+ if (check_fwstate(pmlmepriv, _FW_LINKED) == false) {
+ if (!check_fwstate(pmlmepriv, WIFI_ADHOC_MASTER_STATE)) {
+ erq->length = 0;
+ erq->flags |= IW_ENCODE_DISABLED;
+ return 0;
+ }
+ }
+ key = erq->flags & IW_ENCODE_INDEX;
+ if (key) {
+ if (key > WEP_KEYS)
+ return -EINVAL;
+ key--;
+ } else {
+ key = padapter->securitypriv.PrivacyKeyIndex;
+ }
+ erq->flags = key + 1;
+ switch (padapter->securitypriv.ndisencryptstatus) {
+ case Ndis802_11EncryptionNotSupported:
+ case Ndis802_11EncryptionDisabled:
+ erq->length = 0;
+ erq->flags |= IW_ENCODE_DISABLED;
+ break;
+ case Ndis802_11Encryption1Enabled:
+ erq->length = padapter->securitypriv.DefKeylen[key];
+ if (erq->length) {
+ memcpy(keybuf, padapter->securitypriv.DefKey[
+ key].skey, padapter->securitypriv.
+ DefKeylen[key]);
+ erq->flags |= IW_ENCODE_ENABLED;
+ if (padapter->securitypriv.ndisauthtype ==
+ Ndis802_11AuthModeOpen)
+ erq->flags |= IW_ENCODE_OPEN;
+ else if (padapter->securitypriv.ndisauthtype ==
+ Ndis802_11AuthModeShared)
+ erq->flags |= IW_ENCODE_RESTRICTED;
+ } else {
+ erq->length = 0;
+ erq->flags |= IW_ENCODE_DISABLED;
+ }
+ break;
+ case Ndis802_11Encryption2Enabled:
+ case Ndis802_11Encryption3Enabled:
+ erq->length = 16;
+ erq->flags |= (IW_ENCODE_ENABLED | IW_ENCODE_OPEN |
+ IW_ENCODE_NOKEY);
+ break;
+ default:
+ erq->length = 0;
+ erq->flags |= IW_ENCODE_DISABLED;
+ break;
+ }
+ return ret;
+}
+
+static int r8711_wx_get_power(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ wrqu->power.value = 0;
+ wrqu->power.fixed = 0; /* no auto select */
+ wrqu->power.disabled = 1;
+ return 0;
+}
+
+static int r871x_wx_set_gen_ie(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct _adapter *padapter = netdev_priv(dev);
+
+ return r871x_set_wpa_ie(padapter, extra, wrqu->data.length);
+}
+
+static int r871x_wx_set_auth(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct _adapter *padapter = netdev_priv(dev);
+ struct iw_param *param = (struct iw_param *)&(wrqu->param);
+ int paramid;
+ int paramval;
+ int ret = 0;
+
+ paramid = param->flags & IW_AUTH_INDEX;
+ paramval = param->value;
+ switch (paramid) {
+ case IW_AUTH_WPA_VERSION:
+ break;
+ case IW_AUTH_CIPHER_PAIRWISE:
+ break;
+ case IW_AUTH_CIPHER_GROUP:
+ break;
+ case IW_AUTH_KEY_MGMT:
+ /*
+ * ??? does not use these parameters
+ */
+ break;
+ case IW_AUTH_TKIP_COUNTERMEASURES:
+ if (paramval) {
+ /* wpa_supplicant is enabling tkip countermeasure. */
+ padapter->securitypriv.btkip_countermeasure = true;
+ } else {
+ /* wpa_supplicant is disabling tkip countermeasure. */
+ padapter->securitypriv.btkip_countermeasure = false;
+ }
+ break;
+ case IW_AUTH_DROP_UNENCRYPTED:
+ /* HACK:
+ *
+ * wpa_supplicant calls set_wpa_enabled when the driver
+ * is loaded and unloaded, regardless of if WPA is being
+ * used. No other calls are made which can be used to
+ * determine if encryption will be used or not prior to
+ * association being expected. If encryption is not being
+ * used, drop_unencrypted is set to false, else true -- we
+ * can use this to determine if the CAP_PRIVACY_ON bit should
+ * be set.
+ */
+ if (padapter->securitypriv.ndisencryptstatus ==
+ Ndis802_11Encryption1Enabled) {
+ /* it means init value, or using wep,
+ * ndisencryptstatus =
+ * Ndis802_11Encryption1Enabled,
+ * then it needn't reset it;
+ */
+ break;
+ }
+
+ if (paramval) {
+ padapter->securitypriv.ndisencryptstatus =
+ Ndis802_11EncryptionDisabled;
+ padapter->securitypriv.PrivacyAlgrthm =
+ _NO_PRIVACY_;
+ padapter->securitypriv.XGrpPrivacy =
+ _NO_PRIVACY_;
+ padapter->securitypriv.AuthAlgrthm = 0;
+ padapter->securitypriv.ndisauthtype =
+ Ndis802_11AuthModeOpen;
+ }
+ break;
+ case IW_AUTH_80211_AUTH_ALG:
+ ret = wpa_set_auth_algs(dev, (u32)paramval);
+ break;
+ case IW_AUTH_WPA_ENABLED:
+ break;
+ case IW_AUTH_RX_UNENCRYPTED_EAPOL:
+ break;
+ case IW_AUTH_PRIVACY_INVOKED:
+ break;
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ return ret;
+}
+
+static int r871x_wx_set_enc_ext(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct iw_point *pencoding = &wrqu->encoding;
+ struct iw_encode_ext *pext = (struct iw_encode_ext *)extra;
+ struct ieee_param *param = NULL;
+ char *alg_name;
+ u32 param_len;
+ int ret = 0;
+
+ switch (pext->alg) {
+ case IW_ENCODE_ALG_NONE:
+ alg_name = "none";
+ break;
+ case IW_ENCODE_ALG_WEP:
+ alg_name = "WEP";
+ break;
+ case IW_ENCODE_ALG_TKIP:
+ alg_name = "TKIP";
+ break;
+ case IW_ENCODE_ALG_CCMP:
+ alg_name = "CCMP";
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ param_len = sizeof(struct ieee_param) + pext->key_len;
+ param = kzalloc(param_len, GFP_ATOMIC);
+ if (param == NULL)
+ return -ENOMEM;
+ param->cmd = IEEE_CMD_SET_ENCRYPTION;
+ memset(param->sta_addr, 0xff, ETH_ALEN);
+
+ strncpy((char *)param->u.crypt.alg, alg_name, IEEE_CRYPT_ALG_NAME_LEN);
+ if (pext->ext_flags & IW_ENCODE_EXT_GROUP_KEY)
+ param->u.crypt.set_tx = 0;
+ if (pext->ext_flags & IW_ENCODE_EXT_SET_TX_KEY)
+ param->u.crypt.set_tx = 1;
+ param->u.crypt.idx = (pencoding->flags & 0x00FF) - 1;
+ if (pext->ext_flags & IW_ENCODE_EXT_RX_SEQ_VALID)
+ memcpy(param->u.crypt.seq, pext->rx_seq, 8);
+ if (pext->key_len) {
+ param->u.crypt.key_len = pext->key_len;
+ memcpy(param + 1, pext + 1, pext->key_len);
+ }
+ ret = wpa_set_encryption(dev, param, param_len);
+ kfree(param);
+ return ret;
+}
+
+static int r871x_wx_get_nick(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ if (extra) {
+ wrqu->data.length = 8;
+ wrqu->data.flags = 1;
+ memcpy(extra, "rtl_wifi", 8);
+ }
+ return 0;
+}
+
+static int r8711_wx_read32(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *keybuf)
+{
+ struct _adapter *padapter = netdev_priv(dev);
+ u32 addr;
+ u32 data32;
+
+ get_user(addr, (u32 __user *)wrqu->data.pointer);
+ data32 = r8712_read32(padapter, addr);
+ put_user(data32, (u32 __user *)wrqu->data.pointer);
+ wrqu->data.length = (data32 & 0xffff0000) >> 16;
+ wrqu->data.flags = data32 & 0xffff;
+ get_user(addr, (u32 __user *)wrqu->data.pointer);
+ return 0;
+}
+
+static int r8711_wx_write32(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *keybuf)
+{
+ struct _adapter *padapter = netdev_priv(dev);
+ u32 addr;
+ u32 data32;
+
+ get_user(addr, (u32 __user *)wrqu->data.pointer);
+ data32 = ((u32)wrqu->data.length<<16) | (u32)wrqu->data.flags;
+ r8712_write32(padapter, addr, data32);
+ return 0;
+}
+
+static int dummy(struct net_device *dev,
+ struct iw_request_info *a,
+ union iwreq_data *wrqu, char *b)
+{
+ return -ENOSYS;
+}
+
+static int r8711_drvext_hdl(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ return 0;
+}
+
+static int r871x_mp_ioctl_hdl(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct _adapter *padapter = netdev_priv(dev);
+ struct iw_point *p = &wrqu->data;
+ struct oid_par_priv oid_par;
+ struct mp_ioctl_handler *phandler;
+ struct mp_ioctl_param *poidparam;
+ unsigned long BytesRead, BytesWritten, BytesNeeded;
+ u8 *pparmbuf, bset;
+ u16 len;
+ uint status;
+ int ret = 0;
+
+ if ((!p->length) || (!p->pointer))
+ return -EINVAL;
+
+ bset = (u8)(p->flags & 0xFFFF);
+ len = p->length;
+ pparmbuf = memdup_user(p->pointer, len);
+ if (IS_ERR(pparmbuf))
+ return PTR_ERR(pparmbuf);
+
+ poidparam = (struct mp_ioctl_param *)pparmbuf;
+ if (poidparam->subcode >= MAX_MP_IOCTL_SUBCODE) {
+ ret = -EINVAL;
+ goto _r871x_mp_ioctl_hdl_exit;
+ }
+ phandler = mp_ioctl_hdl + poidparam->subcode;
+ if ((phandler->paramsize != 0) &&
+ (poidparam->len < phandler->paramsize)) {
+ ret = -EINVAL;
+ goto _r871x_mp_ioctl_hdl_exit;
+ }
+ if (phandler->oid == 0 && phandler->handler)
+ status = phandler->handler(&oid_par);
+ else if (phandler->handler) {
+ oid_par.adapter_context = padapter;
+ oid_par.oid = phandler->oid;
+ oid_par.information_buf = poidparam->data;
+ oid_par.information_buf_len = poidparam->len;
+ oid_par.dbg = 0;
+ BytesWritten = 0;
+ BytesNeeded = 0;
+ if (bset) {
+ oid_par.bytes_rw = &BytesRead;
+ oid_par.bytes_needed = &BytesNeeded;
+ oid_par.type_of_oid = SET_OID;
+ } else {
+ oid_par.bytes_rw = &BytesWritten;
+ oid_par.bytes_needed = &BytesNeeded;
+ oid_par.type_of_oid = QUERY_OID;
+ }
+ status = phandler->handler(&oid_par);
+ /* todo:check status, BytesNeeded, etc. */
+ } else {
+ netdev_info(dev, "r8712u: %s: err!, subcode=%d, oid=%d, handler=%p\n",
+ __func__, poidparam->subcode, phandler->oid,
+ phandler->handler);
+ ret = -EFAULT;
+ goto _r871x_mp_ioctl_hdl_exit;
+ }
+ if (bset == 0x00) { /* query info */
+ if (copy_to_user(p->pointer, pparmbuf, len))
+ ret = -EFAULT;
+ }
+ if (status) {
+ ret = -EFAULT;
+ goto _r871x_mp_ioctl_hdl_exit;
+ }
+_r871x_mp_ioctl_hdl_exit:
+ kfree(pparmbuf);
+ return ret;
+}
+
+static int r871x_get_ap_info(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct _adapter *padapter = netdev_priv(dev);
+ struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
+ struct __queue *queue = &pmlmepriv->scanned_queue;
+ struct iw_point *pdata = &wrqu->data;
+ struct wlan_network *pnetwork = NULL;
+ u32 cnt = 0, wpa_ielen;
+ unsigned long irqL;
+ struct list_head *plist, *phead;
+ unsigned char *pbuf;
+ u8 bssid[ETH_ALEN];
+ char data[32];
+
+ if (padapter->bDriverStopped || (pdata == NULL))
+ return -EINVAL;
+ while (check_fwstate(pmlmepriv, _FW_UNDER_SURVEY|_FW_UNDER_LINKING)) {
+ msleep(30);
+ cnt++;
+ if (cnt > 100)
+ break;
+ }
+ pdata->flags = 0;
+ if (pdata->length >= 32) {
+ if (copy_from_user(data, pdata->pointer, 32))
+ return -EINVAL;
+ } else
+ return -EINVAL;
+ spin_lock_irqsave(&(pmlmepriv->scanned_queue.lock), irqL);
+ phead = &queue->queue;
+ plist = phead->next;
+ while (1) {
+ if (end_of_queue_search(phead, plist) == true)
+ break;
+ pnetwork = LIST_CONTAINOR(plist, struct wlan_network, list);
+ if (hwaddr_aton_i(data, bssid)) {
+ netdev_info(dev, "r8712u: Invalid BSSID '%s'.\n",
+ (u8 *)data);
+ spin_unlock_irqrestore(&(pmlmepriv->scanned_queue.lock),
+ irqL);
+ return -EINVAL;
+ }
+ netdev_info(dev, "r8712u: BSSID:%pM\n", bssid);
+ if (!memcmp(bssid, pnetwork->network.MacAddress, ETH_ALEN)) {
+ /* BSSID match, then check if supporting wpa/wpa2 */
+ pbuf = r8712_get_wpa_ie(&pnetwork->network.IEs[12],
+ &wpa_ielen, pnetwork->network.IELength-12);
+ if (pbuf && (wpa_ielen > 0)) {
+ pdata->flags = 1;
+ break;
+ }
+ pbuf = r8712_get_wpa2_ie(&pnetwork->network.IEs[12],
+ &wpa_ielen, pnetwork->network.IELength-12);
+ if (pbuf && (wpa_ielen > 0)) {
+ pdata->flags = 2;
+ break;
+ }
+ }
+ plist = plist->next;
+ }
+ spin_unlock_irqrestore(&(pmlmepriv->scanned_queue.lock), irqL);
+ if (pdata->length >= 34) {
+ if (copy_to_user((u8 __user *)pdata->pointer + 32,
+ (u8 *)&pdata->flags, 1))
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int r871x_set_pid(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct _adapter *padapter = netdev_priv(dev);
+ struct iw_point *pdata = &wrqu->data;
+
+ if ((padapter->bDriverStopped) || (pdata == NULL))
+ return -EINVAL;
+ if (copy_from_user(&padapter->pid, pdata->pointer, sizeof(int)))
+ return -EINVAL;
+ return 0;
+}
+
+static int r871x_set_chplan(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ int ret = 0;
+ struct _adapter *padapter = netdev_priv(dev);
+ struct iw_point *pdata = &wrqu->data;
+ int ch_plan = -1;
+
+ if ((padapter->bDriverStopped) || (pdata == NULL)) {
+ ret = -EINVAL;
+ goto exit;
+ }
+ ch_plan = (int)*extra;
+ r8712_set_chplan_cmd(padapter, ch_plan);
+
+exit:
+
+ return ret;
+}
+
+static int r871x_wps_start(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct _adapter *padapter = netdev_priv(dev);
+ struct iw_point *pdata = &wrqu->data;
+ u32 u32wps_start = 0;
+
+ if ((padapter->bDriverStopped) || (pdata == NULL))
+ return -EINVAL;
+ if (copy_from_user((void *)&u32wps_start, pdata->pointer, 4))
+ return -EFAULT;
+ if (u32wps_start == 0)
+ u32wps_start = *extra;
+ if (u32wps_start == 1) /* WPS Start */
+ padapter->ledpriv.LedControlHandler(padapter,
+ LED_CTL_START_WPS);
+ else if (u32wps_start == 2) /* WPS Stop because of wps success */
+ padapter->ledpriv.LedControlHandler(padapter,
+ LED_CTL_STOP_WPS);
+ else if (u32wps_start == 3) /* WPS Stop because of wps fail */
+ padapter->ledpriv.LedControlHandler(padapter,
+ LED_CTL_STOP_WPS_FAIL);
+ return 0;
+}
+
+static int wpa_set_param(struct net_device *dev, u8 name, u32 value)
+{
+ struct _adapter *padapter = netdev_priv(dev);
+
+ switch (name) {
+ case IEEE_PARAM_WPA_ENABLED:
+ padapter->securitypriv.AuthAlgrthm = 2; /* 802.1x */
+ switch ((value)&0xff) {
+ case 1: /* WPA */
+ padapter->securitypriv.ndisauthtype =
+ Ndis802_11AuthModeWPAPSK; /* WPA_PSK */
+ padapter->securitypriv.ndisencryptstatus =
+ Ndis802_11Encryption2Enabled;
+ break;
+ case 2: /* WPA2 */
+ padapter->securitypriv.ndisauthtype =
+ Ndis802_11AuthModeWPA2PSK; /* WPA2_PSK */
+ padapter->securitypriv.ndisencryptstatus =
+ Ndis802_11Encryption3Enabled;
+ break;
+ }
+ break;
+ case IEEE_PARAM_TKIP_COUNTERMEASURES:
+ break;
+ case IEEE_PARAM_DROP_UNENCRYPTED:
+ /* HACK:
+ *
+ * wpa_supplicant calls set_wpa_enabled when the driver
+ * is loaded and unloaded, regardless of if WPA is being
+ * used. No other calls are made which can be used to
+ * determine if encryption will be used or not prior to
+ * association being expected. If encryption is not being
+ * used, drop_unencrypted is set to false, else true -- we
+ * can use this to determine if the CAP_PRIVACY_ON bit should
+ * be set.
+ */
+ break;
+ case IEEE_PARAM_PRIVACY_INVOKED:
+ break;
+ case IEEE_PARAM_AUTH_ALGS:
+ return wpa_set_auth_algs(dev, value);
+ case IEEE_PARAM_IEEE_802_1X:
+ break;
+ case IEEE_PARAM_WPAX_SELECT:
+ /* added for WPA2 mixed mode */
+ break;
+ default:
+ return -EOPNOTSUPP;
+ }
+ return 0;
+}
+
+static int wpa_mlme(struct net_device *dev, u32 command, u32 reason)
+{
+ struct _adapter *padapter = netdev_priv(dev);
+
+ switch (command) {
+ case IEEE_MLME_STA_DEAUTH:
+ if (!r8712_set_802_11_disassociate(padapter))
+ return -1;
+ break;
+ case IEEE_MLME_STA_DISASSOC:
+ if (!r8712_set_802_11_disassociate(padapter))
+ return -1;
+ break;
+ default:
+ return -EOPNOTSUPP;
+ }
+ return 0;
+}
+
+static int wpa_supplicant_ioctl(struct net_device *dev, struct iw_point *p)
+{
+ struct ieee_param *param;
+ int ret = 0;
+ struct _adapter *padapter = netdev_priv(dev);
+
+ if (p->length < sizeof(struct ieee_param) || !p->pointer)
+ return -EINVAL;
+ param = memdup_user(p->pointer, p->length);
+ if (IS_ERR(param))
+ return PTR_ERR(param);
+ switch (param->cmd) {
+ case IEEE_CMD_SET_WPA_PARAM:
+ ret = wpa_set_param(dev, param->u.wpa_param.name,
+ param->u.wpa_param.value);
+ break;
+ case IEEE_CMD_SET_WPA_IE:
+ ret = r871x_set_wpa_ie(padapter, (char *)param->u.wpa_ie.data,
+ (u16)param->u.wpa_ie.len);
+ break;
+ case IEEE_CMD_SET_ENCRYPTION:
+ ret = wpa_set_encryption(dev, param, p->length);
+ break;
+ case IEEE_CMD_MLME:
+ ret = wpa_mlme(dev, param->u.mlme.command,
+ param->u.mlme.reason_code);
+ break;
+ default:
+ ret = -EOPNOTSUPP;
+ break;
+ }
+ if (ret == 0 && copy_to_user(p->pointer, param, p->length))
+ ret = -EFAULT;
+ kfree(param);
+ return ret;
+}
+
+/* based on "driver_ipw" and for hostapd */
+int r871x_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
+{
+ struct iwreq *wrq = (struct iwreq *)rq;
+
+ switch (cmd) {
+ case RTL_IOCTL_WPA_SUPPLICANT:
+ return wpa_supplicant_ioctl(dev, &wrq->u.data);
+ default:
+ return -EOPNOTSUPP;
+ }
+ return 0;
+}
+
+static iw_handler r8711_handlers[] = {
+ NULL, /* SIOCSIWCOMMIT */
+ r8711_wx_get_name, /* SIOCGIWNAME */
+ dummy, /* SIOCSIWNWID */
+ dummy, /* SIOCGIWNWID */
+ r8711_wx_set_freq, /* SIOCSIWFREQ */
+ r8711_wx_get_freq, /* SIOCGIWFREQ */
+ r8711_wx_set_mode, /* SIOCSIWMODE */
+ r8711_wx_get_mode, /* SIOCGIWMODE */
+ dummy, /* SIOCSIWSENS */
+ r8711_wx_get_sens, /* SIOCGIWSENS */
+ NULL, /* SIOCSIWRANGE */
+ r8711_wx_get_range, /* SIOCGIWRANGE */
+ r871x_wx_set_priv, /* SIOCSIWPRIV */
+ NULL, /* SIOCGIWPRIV */
+ NULL, /* SIOCSIWSTATS */
+ NULL, /* SIOCGIWSTATS */
+ dummy, /* SIOCSIWSPY */
+ dummy, /* SIOCGIWSPY */
+ NULL, /* SIOCGIWTHRSPY */
+ NULL, /* SIOCWIWTHRSPY */
+ r8711_wx_set_wap, /* SIOCSIWAP */
+ r8711_wx_get_wap, /* SIOCGIWAP */
+ r871x_wx_set_mlme, /* request MLME operation;
+ * uses struct iw_mlme */
+ dummy, /* SIOCGIWAPLIST -- deprecated */
+ r8711_wx_set_scan, /* SIOCSIWSCAN */
+ r8711_wx_get_scan, /* SIOCGIWSCAN */
+ r8711_wx_set_essid, /* SIOCSIWESSID */
+ r8711_wx_get_essid, /* SIOCGIWESSID */
+ dummy, /* SIOCSIWNICKN */
+ r871x_wx_get_nick, /* SIOCGIWNICKN */
+ NULL, /* -- hole -- */
+ NULL, /* -- hole -- */
+ r8711_wx_set_rate, /* SIOCSIWRATE */
+ r8711_wx_get_rate, /* SIOCGIWRATE */
+ dummy, /* SIOCSIWRTS */
+ r8711_wx_get_rts, /* SIOCGIWRTS */
+ r8711_wx_set_frag, /* SIOCSIWFRAG */
+ r8711_wx_get_frag, /* SIOCGIWFRAG */
+ dummy, /* SIOCSIWTXPOW */
+ dummy, /* SIOCGIWTXPOW */
+ dummy, /* SIOCSIWRETRY */
+ r8711_wx_get_retry, /* SIOCGIWRETRY */
+ r8711_wx_set_enc, /* SIOCSIWENCODE */
+ r8711_wx_get_enc, /* SIOCGIWENCODE */
+ dummy, /* SIOCSIWPOWER */
+ r8711_wx_get_power, /* SIOCGIWPOWER */
+ NULL, /*---hole---*/
+ NULL, /*---hole---*/
+ r871x_wx_set_gen_ie, /* SIOCSIWGENIE */
+ NULL, /* SIOCGIWGENIE */
+ r871x_wx_set_auth, /* SIOCSIWAUTH */
+ NULL, /* SIOCGIWAUTH */
+ r871x_wx_set_enc_ext, /* SIOCSIWENCODEEXT */
+ NULL, /* SIOCGIWENCODEEXT */
+ r871x_wx_set_pmkid, /* SIOCSIWPMKSA */
+ NULL, /*---hole---*/
+};
+
+static const struct iw_priv_args r8711_private_args[] = {
+ {
+ SIOCIWFIRSTPRIV + 0x0,
+ IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "read32"
+ },
+ {
+ SIOCIWFIRSTPRIV + 0x1,
+ IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "write32"
+ },
+ {
+ SIOCIWFIRSTPRIV + 0x2, 0, 0, "driver_ext"
+ },
+ {
+ SIOCIWFIRSTPRIV + 0x3, 0, 0, "mp_ioctl"
+ },
+ {
+ SIOCIWFIRSTPRIV + 0x4,
+ IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "apinfo"
+ },
+ {
+ SIOCIWFIRSTPRIV + 0x5,
+ IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "setpid"
+ },
+ {
+ SIOCIWFIRSTPRIV + 0x6,
+ IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "wps_start"
+ },
+ {
+ SIOCIWFIRSTPRIV + 0x7,
+ IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "chplan"
+ }
+};
+
+static iw_handler r8711_private_handler[] = {
+ r8711_wx_read32,
+ r8711_wx_write32,
+ r8711_drvext_hdl,
+ r871x_mp_ioctl_hdl,
+ r871x_get_ap_info, /*for MM DTV platform*/
+ r871x_set_pid,
+ r871x_wps_start,
+ r871x_set_chplan
+};
+
+static struct iw_statistics *r871x_get_wireless_stats(struct net_device *dev)
+{
+ struct _adapter *padapter = netdev_priv(dev);
+ struct iw_statistics *piwstats = &padapter->iwstats;
+ int tmp_level = 0;
+ int tmp_qual = 0;
+ int tmp_noise = 0;
+
+ if (check_fwstate(&padapter->mlmepriv, _FW_LINKED) != true) {
+ piwstats->qual.qual = 0;
+ piwstats->qual.level = 0;
+ piwstats->qual.noise = 0;
+ } else {
+ /* show percentage, we need transfer dbm to orignal value. */
+ tmp_level = padapter->recvpriv.fw_rssi;
+ tmp_qual = padapter->recvpriv.signal;
+ tmp_noise = padapter->recvpriv.noise;
+ piwstats->qual.level = tmp_level;
+ piwstats->qual.qual = tmp_qual;
+ piwstats->qual.noise = tmp_noise;
+ }
+ piwstats->qual.updated = IW_QUAL_ALL_UPDATED;
+ return &padapter->iwstats;
+}
+
+struct iw_handler_def r871x_handlers_def = {
+ .standard = r8711_handlers,
+ .num_standard = ARRAY_SIZE(r8711_handlers),
+ .private = r8711_private_handler,
+ .private_args = (struct iw_priv_args *)r8711_private_args,
+ .num_private = ARRAY_SIZE(r8711_private_handler),
+ .num_private_args = sizeof(r8711_private_args) /
+ sizeof(struct iw_priv_args),
+ .get_wireless_stats = r871x_get_wireless_stats
+};
diff --git a/drivers/staging/rtl8712/rtl871x_ioctl_rtl.c b/drivers/staging/rtl8712/rtl871x_ioctl_rtl.c
new file mode 100644
index 000000000..ac0baff7f
--- /dev/null
+++ b/drivers/staging/rtl8712/rtl871x_ioctl_rtl.c
@@ -0,0 +1,536 @@
+/******************************************************************************
+ * rtl871x_ioctl_rtl.c
+ *
+ * Copyright(c) 2007 - 2010 Realtek Corporation. All rights reserved.
+ * Linux device driver for RTL8192SU
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ * Modifications for inclusion into the Linux staging tree are
+ * Copyright(c) 2010 Larry Finger. All rights reserved.
+ *
+ * Contact information:
+ * WLAN FAE <wlanfae@realtek.com>
+ * Larry Finger <Larry.Finger@lwfinger.net>
+ *
+ ******************************************************************************/
+
+#define _RTL871X_IOCTL_RTL_C_
+
+#include <linux/rndis.h>
+#include "osdep_service.h"
+#include "drv_types.h"
+#include "wlan_bssdef.h"
+#include "wifi.h"
+#include "rtl871x_ioctl.h"
+#include "rtl871x_ioctl_set.h"
+#include "rtl871x_ioctl_rtl.h"
+#include "mp_custom_oid.h"
+#include "rtl871x_mp.h"
+#include "rtl871x_mp_ioctl.h"
+
+uint oid_rt_get_signal_quality_hdl(struct oid_par_priv *poid_par_priv)
+{
+ if (poid_par_priv->type_of_oid != QUERY_OID)
+ return RNDIS_STATUS_NOT_ACCEPTED;
+ return RNDIS_STATUS_SUCCESS;
+}
+
+uint oid_rt_get_small_packet_crc_hdl(struct oid_par_priv *poid_par_priv)
+{
+ struct _adapter *padapter = (struct _adapter *)
+ (poid_par_priv->adapter_context);
+
+ if (poid_par_priv->type_of_oid != QUERY_OID)
+ return RNDIS_STATUS_NOT_ACCEPTED;
+ if (poid_par_priv->information_buf_len >= sizeof(u32)) {
+ *(u32 *)poid_par_priv->information_buf =
+ padapter->recvpriv.rx_smallpacket_crcerr;
+ *poid_par_priv->bytes_rw = poid_par_priv->information_buf_len;
+ } else
+ return RNDIS_STATUS_INVALID_LENGTH;
+ return RNDIS_STATUS_SUCCESS;
+}
+
+uint oid_rt_get_middle_packet_crc_hdl(struct oid_par_priv *poid_par_priv)
+{
+ struct _adapter *padapter = (struct _adapter *)
+ (poid_par_priv->adapter_context);
+
+ if (poid_par_priv->type_of_oid != QUERY_OID)
+ return RNDIS_STATUS_NOT_ACCEPTED;
+ if (poid_par_priv->information_buf_len >= sizeof(u32)) {
+ *(u32 *)poid_par_priv->information_buf =
+ padapter->recvpriv.rx_middlepacket_crcerr;
+ *poid_par_priv->bytes_rw = poid_par_priv->information_buf_len;
+ } else
+ return RNDIS_STATUS_INVALID_LENGTH;
+ return RNDIS_STATUS_SUCCESS;
+}
+
+uint oid_rt_get_large_packet_crc_hdl(struct oid_par_priv *poid_par_priv)
+{
+ struct _adapter *padapter = (struct _adapter *)
+ (poid_par_priv->adapter_context);
+
+ if (poid_par_priv->type_of_oid != QUERY_OID)
+ return RNDIS_STATUS_NOT_ACCEPTED;
+ if (poid_par_priv->information_buf_len >= sizeof(u32)) {
+ *(u32 *)poid_par_priv->information_buf =
+ padapter->recvpriv.rx_largepacket_crcerr;
+ *poid_par_priv->bytes_rw = poid_par_priv->information_buf_len;
+ } else
+ return RNDIS_STATUS_INVALID_LENGTH;
+ return RNDIS_STATUS_SUCCESS;
+}
+
+uint oid_rt_get_tx_retry_hdl(struct oid_par_priv *poid_par_priv)
+{
+ if (poid_par_priv->type_of_oid != QUERY_OID)
+ return RNDIS_STATUS_NOT_ACCEPTED;
+ return RNDIS_STATUS_SUCCESS;
+}
+
+uint oid_rt_get_rx_retry_hdl(struct oid_par_priv *poid_par_priv)
+{
+ if (poid_par_priv->type_of_oid != QUERY_OID)
+ return RNDIS_STATUS_NOT_ACCEPTED;
+ *poid_par_priv->bytes_rw = poid_par_priv->information_buf_len;
+ return RNDIS_STATUS_SUCCESS;
+}
+
+uint oid_rt_get_rx_total_packet_hdl(struct oid_par_priv *poid_par_priv)
+{
+ struct _adapter *padapter = (struct _adapter *)
+ (poid_par_priv->adapter_context);
+
+ if (poid_par_priv->type_of_oid != QUERY_OID)
+ return RNDIS_STATUS_NOT_ACCEPTED;
+ if (poid_par_priv->information_buf_len >= sizeof(u32)) {
+ *(u32 *)poid_par_priv->information_buf =
+ padapter->recvpriv.rx_pkts +
+ padapter->recvpriv.rx_drop;
+ *poid_par_priv->bytes_rw = poid_par_priv->information_buf_len;
+ } else
+ return RNDIS_STATUS_INVALID_LENGTH;
+ return RNDIS_STATUS_SUCCESS;
+}
+
+uint oid_rt_get_tx_beacon_ok_hdl(struct oid_par_priv *poid_par_priv)
+{
+ if (poid_par_priv->type_of_oid != QUERY_OID)
+ return RNDIS_STATUS_NOT_ACCEPTED;
+ return RNDIS_STATUS_SUCCESS;
+}
+
+uint oid_rt_get_tx_beacon_err_hdl(struct oid_par_priv *poid_par_priv)
+{
+ if (poid_par_priv->type_of_oid != QUERY_OID)
+ return RNDIS_STATUS_NOT_ACCEPTED;
+ return RNDIS_STATUS_SUCCESS;
+}
+
+uint oid_rt_get_rx_icv_err_hdl(struct oid_par_priv *poid_par_priv)
+{
+ struct _adapter *padapter = (struct _adapter *)
+ (poid_par_priv->adapter_context);
+
+ if (poid_par_priv->type_of_oid != QUERY_OID)
+ return RNDIS_STATUS_NOT_ACCEPTED;
+ if (poid_par_priv->information_buf_len >= sizeof(u32)) {
+ *(uint *)poid_par_priv->information_buf =
+ padapter->recvpriv.rx_icv_err;
+ *poid_par_priv->bytes_rw = poid_par_priv->information_buf_len;
+ } else
+ return RNDIS_STATUS_INVALID_LENGTH;
+ return RNDIS_STATUS_SUCCESS;
+}
+
+uint oid_rt_set_encryption_algorithm_hdl(struct oid_par_priv
+ *poid_par_priv)
+{
+ if (poid_par_priv->type_of_oid != SET_OID)
+ return RNDIS_STATUS_NOT_ACCEPTED;
+ return RNDIS_STATUS_SUCCESS;
+}
+
+uint oid_rt_get_preamble_mode_hdl(struct oid_par_priv *poid_par_priv)
+{
+ struct _adapter *padapter = (struct _adapter *)
+ (poid_par_priv->adapter_context);
+ u32 preamblemode = 0;
+
+ if (poid_par_priv->type_of_oid != QUERY_OID)
+ return RNDIS_STATUS_NOT_ACCEPTED;
+ if (poid_par_priv->information_buf_len >= sizeof(u32)) {
+ if (padapter->registrypriv.preamble == PREAMBLE_LONG)
+ preamblemode = 0;
+ else if (padapter->registrypriv.preamble == PREAMBLE_AUTO)
+ preamblemode = 1;
+ else if (padapter->registrypriv.preamble == PREAMBLE_SHORT)
+ preamblemode = 2;
+ *(u32 *)poid_par_priv->information_buf = preamblemode;
+ *poid_par_priv->bytes_rw = poid_par_priv->information_buf_len;
+ } else
+ return RNDIS_STATUS_INVALID_LENGTH;
+ return RNDIS_STATUS_SUCCESS;
+}
+
+uint oid_rt_get_ap_ip_hdl(struct oid_par_priv *poid_par_priv)
+{
+ if (poid_par_priv->type_of_oid != QUERY_OID)
+ return RNDIS_STATUS_NOT_ACCEPTED;
+ return RNDIS_STATUS_SUCCESS;
+}
+
+uint oid_rt_get_channelplan_hdl(struct oid_par_priv *poid_par_priv)
+{
+ struct _adapter *padapter = (struct _adapter *)
+ (poid_par_priv->adapter_context);
+ struct eeprom_priv *peeprompriv = &padapter->eeprompriv;
+
+ if (poid_par_priv->type_of_oid != QUERY_OID)
+ return RNDIS_STATUS_NOT_ACCEPTED;
+ *poid_par_priv->bytes_rw = poid_par_priv->information_buf_len;
+ *(u16 *)poid_par_priv->information_buf = peeprompriv->channel_plan;
+ return RNDIS_STATUS_SUCCESS;
+}
+
+uint oid_rt_set_channelplan_hdl(struct oid_par_priv
+ *poid_par_priv)
+{
+ struct _adapter *padapter = (struct _adapter *)
+ (poid_par_priv->adapter_context);
+ struct eeprom_priv *peeprompriv = &padapter->eeprompriv;
+
+ if (poid_par_priv->type_of_oid != SET_OID)
+ return RNDIS_STATUS_NOT_ACCEPTED;
+ peeprompriv->channel_plan = *(u16 *)poid_par_priv->information_buf;
+ return RNDIS_STATUS_SUCCESS;
+}
+
+uint oid_rt_set_preamble_mode_hdl(struct oid_par_priv
+ *poid_par_priv)
+{
+ struct _adapter *padapter = (struct _adapter *)
+ (poid_par_priv->adapter_context);
+ u32 preamblemode = 0;
+
+ if (poid_par_priv->type_of_oid != SET_OID)
+ return RNDIS_STATUS_NOT_ACCEPTED;
+ if (poid_par_priv->information_buf_len >= sizeof(u32)) {
+ preamblemode = *(u32 *)poid_par_priv->information_buf;
+ if (preamblemode == 0)
+ padapter->registrypriv.preamble = PREAMBLE_LONG;
+ else if (preamblemode == 1)
+ padapter->registrypriv.preamble = PREAMBLE_AUTO;
+ else if (preamblemode == 2)
+ padapter->registrypriv.preamble = PREAMBLE_SHORT;
+ *(u32 *)poid_par_priv->information_buf = preamblemode;
+ *poid_par_priv->bytes_rw = poid_par_priv->information_buf_len;
+ } else
+ return RNDIS_STATUS_INVALID_LENGTH;
+ return RNDIS_STATUS_SUCCESS;
+}
+
+uint oid_rt_set_bcn_intvl_hdl(struct oid_par_priv *poid_par_priv)
+{
+ if (poid_par_priv->type_of_oid != SET_OID)
+ return RNDIS_STATUS_NOT_ACCEPTED;
+ return RNDIS_STATUS_SUCCESS;
+}
+
+uint oid_rt_dedicate_probe_hdl(struct oid_par_priv
+ *poid_par_priv)
+{
+ return RNDIS_STATUS_SUCCESS;
+}
+
+uint oid_rt_get_total_tx_bytes_hdl(struct oid_par_priv
+ *poid_par_priv)
+{
+ struct _adapter *padapter = (struct _adapter *)
+ (poid_par_priv->adapter_context);
+
+ if (poid_par_priv->type_of_oid != QUERY_OID)
+ return RNDIS_STATUS_NOT_ACCEPTED;
+ if (poid_par_priv->information_buf_len >= sizeof(u32)) {
+ *(u32 *)poid_par_priv->information_buf =
+ padapter->xmitpriv.tx_bytes;
+ *poid_par_priv->bytes_rw = poid_par_priv->information_buf_len;
+ } else
+ return RNDIS_STATUS_INVALID_LENGTH;
+ return RNDIS_STATUS_SUCCESS;
+}
+
+uint oid_rt_get_total_rx_bytes_hdl(struct oid_par_priv
+ *poid_par_priv)
+{
+ struct _adapter *padapter = (struct _adapter *)
+ (poid_par_priv->adapter_context);
+
+ if (poid_par_priv->type_of_oid != QUERY_OID)
+ return RNDIS_STATUS_NOT_ACCEPTED;
+ if (poid_par_priv->information_buf_len >= sizeof(u32)) {
+ *(u32 *)poid_par_priv->information_buf =
+ padapter->recvpriv.rx_bytes;
+ *poid_par_priv->bytes_rw = poid_par_priv->
+ information_buf_len;
+ } else
+ return RNDIS_STATUS_INVALID_LENGTH;
+ return RNDIS_STATUS_SUCCESS;
+}
+
+uint oid_rt_current_tx_power_level_hdl(struct oid_par_priv
+ *poid_par_priv)
+{
+ return RNDIS_STATUS_SUCCESS;
+}
+
+uint oid_rt_get_enc_key_mismatch_count_hdl(struct oid_par_priv
+ *poid_par_priv)
+{
+ if (poid_par_priv->type_of_oid != QUERY_OID)
+ return RNDIS_STATUS_NOT_ACCEPTED;
+ return RNDIS_STATUS_SUCCESS;
+}
+
+uint oid_rt_get_enc_key_match_count_hdl(struct oid_par_priv
+ *poid_par_priv)
+{
+ if (poid_par_priv->type_of_oid != QUERY_OID)
+ return RNDIS_STATUS_NOT_ACCEPTED;
+ return RNDIS_STATUS_SUCCESS;
+}
+
+uint oid_rt_get_channel_hdl(struct oid_par_priv *poid_par_priv)
+{
+ struct _adapter *padapter = (struct _adapter *)
+ (poid_par_priv->adapter_context);
+ struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
+ struct NDIS_802_11_CONFIGURATION *pnic_Config;
+ u32 channelnum;
+
+ if (poid_par_priv->type_of_oid != QUERY_OID)
+ return RNDIS_STATUS_NOT_ACCEPTED;
+ if ((check_fwstate(pmlmepriv, _FW_LINKED) == true) ||
+ (check_fwstate(pmlmepriv, WIFI_ADHOC_MASTER_STATE) == true))
+ pnic_Config = &pmlmepriv->cur_network.network.Configuration;
+ else
+ pnic_Config = &padapter->registrypriv.dev_network.
+ Configuration;
+ channelnum = pnic_Config->DSConfig;
+ *(u32 *)poid_par_priv->information_buf = channelnum;
+ *poid_par_priv->bytes_rw = poid_par_priv->information_buf_len;
+ return RNDIS_STATUS_SUCCESS;
+}
+
+uint oid_rt_get_hardware_radio_off_hdl(struct oid_par_priv
+ *poid_par_priv)
+{
+ if (poid_par_priv->type_of_oid != QUERY_OID)
+ return RNDIS_STATUS_NOT_ACCEPTED;
+ return RNDIS_STATUS_SUCCESS;
+}
+
+uint oid_rt_get_key_mismatch_hdl(struct oid_par_priv *poid_par_priv)
+{
+ if (poid_par_priv->type_of_oid != QUERY_OID)
+ return RNDIS_STATUS_NOT_ACCEPTED;
+ return RNDIS_STATUS_SUCCESS;
+}
+
+uint oid_rt_supported_wireless_mode_hdl(struct oid_par_priv
+ *poid_par_priv)
+{
+ u32 ulInfo = 0;
+
+ if (poid_par_priv->type_of_oid != QUERY_OID)
+ return RNDIS_STATUS_NOT_ACCEPTED;
+ if (poid_par_priv->information_buf_len >= sizeof(u32)) {
+ ulInfo |= 0x0100; /* WIRELESS_MODE_B */
+ ulInfo |= 0x0200; /* WIRELESS_MODE_G */
+ ulInfo |= 0x0400; /* WIRELESS_MODE_A */
+ *(u32 *) poid_par_priv->information_buf = ulInfo;
+ *poid_par_priv->bytes_rw = poid_par_priv->information_buf_len;
+ } else
+ return RNDIS_STATUS_INVALID_LENGTH;
+ return RNDIS_STATUS_SUCCESS;
+}
+
+uint oid_rt_get_channel_list_hdl(struct oid_par_priv *poid_par_priv)
+{
+ if (poid_par_priv->type_of_oid != QUERY_OID)
+ return RNDIS_STATUS_NOT_ACCEPTED;
+ return RNDIS_STATUS_SUCCESS;
+}
+
+uint oid_rt_get_scan_in_progress_hdl(struct oid_par_priv *poid_par_priv)
+{
+ if (poid_par_priv->type_of_oid != QUERY_OID)
+ return RNDIS_STATUS_NOT_ACCEPTED;
+ return RNDIS_STATUS_SUCCESS;
+}
+
+
+uint oid_rt_forced_data_rate_hdl(struct oid_par_priv *poid_par_priv)
+{
+ return RNDIS_STATUS_SUCCESS;
+}
+
+uint oid_rt_wireless_mode_for_scan_list_hdl(struct oid_par_priv
+ *poid_par_priv)
+{
+ return RNDIS_STATUS_SUCCESS;
+}
+
+uint oid_rt_get_bss_wireless_mode_hdl(struct oid_par_priv
+ *poid_par_priv)
+{
+ if (poid_par_priv->type_of_oid != QUERY_OID)
+ return RNDIS_STATUS_NOT_ACCEPTED;
+ return RNDIS_STATUS_SUCCESS;
+}
+
+uint oid_rt_scan_with_magic_packet_hdl(struct oid_par_priv
+ *poid_par_priv)
+{
+ return RNDIS_STATUS_SUCCESS;
+}
+
+uint oid_rt_ap_get_associated_station_list_hdl(struct oid_par_priv
+ *poid_par_priv)
+{
+ if (poid_par_priv->type_of_oid != QUERY_OID)
+ return RNDIS_STATUS_NOT_ACCEPTED;
+ return RNDIS_STATUS_SUCCESS;
+}
+
+uint oid_rt_ap_switch_into_ap_mode_hdl(struct oid_par_priv*
+ poid_par_priv)
+{
+ return RNDIS_STATUS_SUCCESS;
+}
+
+uint oid_rt_ap_supported_hdl(struct oid_par_priv *poid_par_priv)
+{
+ return RNDIS_STATUS_SUCCESS;
+}
+
+uint oid_rt_ap_set_passphrase_hdl(struct oid_par_priv *poid_par_priv)
+{
+ if (poid_par_priv->type_of_oid != SET_OID)
+ return RNDIS_STATUS_NOT_ACCEPTED;
+ return RNDIS_STATUS_SUCCESS;
+}
+
+uint oid_rt_pro_rf_write_registry_hdl(struct oid_par_priv*
+ poid_par_priv)
+{
+ uint status = RNDIS_STATUS_SUCCESS;
+ struct _adapter *Adapter = (struct _adapter *)
+ (poid_par_priv->adapter_context);
+
+ if (poid_par_priv->type_of_oid != SET_OID) /* QUERY_OID */
+ return RNDIS_STATUS_NOT_ACCEPTED;
+ if (poid_par_priv->information_buf_len ==
+ (sizeof(unsigned long) * 3)) {
+ if (!r8712_setrfreg_cmd(Adapter,
+ *(unsigned char *)poid_par_priv->information_buf,
+ (unsigned long)(*((unsigned long *)
+ poid_par_priv->information_buf + 2))))
+ status = RNDIS_STATUS_NOT_ACCEPTED;
+ } else
+ status = RNDIS_STATUS_INVALID_LENGTH;
+ return status;
+}
+
+uint oid_rt_pro_rf_read_registry_hdl(struct oid_par_priv *poid_par_priv)
+{
+ uint status = RNDIS_STATUS_SUCCESS;
+ struct _adapter *Adapter = (struct _adapter *)
+ (poid_par_priv->adapter_context);
+
+ if (poid_par_priv->type_of_oid != SET_OID) /* QUERY_OID */
+ return RNDIS_STATUS_NOT_ACCEPTED;
+ if (poid_par_priv->information_buf_len == (sizeof(unsigned long)*3)) {
+ if (Adapter->mppriv.act_in_progress == true)
+ status = RNDIS_STATUS_NOT_ACCEPTED;
+ else {
+ /* init workparam */
+ Adapter->mppriv.act_in_progress = true;
+ Adapter->mppriv.workparam.bcompleted = false;
+ Adapter->mppriv.workparam.act_type = MPT_READ_RF;
+ Adapter->mppriv.workparam.io_offset = *(unsigned long *)
+ poid_par_priv->information_buf;
+ Adapter->mppriv.workparam.io_value = 0xcccccccc;
+
+ /* RegOffsetValue - The offset of RF register to read.
+ * RegDataWidth - The data width of RF register to read.
+ * RegDataValue - The value to read.
+ * RegOffsetValue = *((unsigned long *)InformationBuffer);
+ * RegDataWidth = *((unsigned long *)InformationBuffer+1);
+ * RegDataValue = *((unsigned long *)InformationBuffer+2);
+ */
+ if (!r8712_getrfreg_cmd(Adapter,
+ *(unsigned char *)poid_par_priv->information_buf,
+ (unsigned char *)&Adapter->mppriv.workparam.
+ io_value))
+ status = RNDIS_STATUS_NOT_ACCEPTED;
+ }
+ } else
+ status = RNDIS_STATUS_INVALID_LENGTH;
+ return status;
+}
+
+enum _CONNECT_STATE_ {
+ CHECKINGSTATUS,
+ ASSOCIATED,
+ ADHOCMODE,
+ NOTASSOCIATED
+};
+
+uint oid_rt_get_connect_state_hdl(struct oid_par_priv *poid_par_priv)
+{
+ struct _adapter *padapter = (struct _adapter *)
+ (poid_par_priv->adapter_context);
+ struct mlme_priv *pmlmepriv = &(padapter->mlmepriv);
+ u32 ulInfo;
+
+ if (poid_par_priv->type_of_oid != QUERY_OID)
+ return RNDIS_STATUS_NOT_ACCEPTED;
+ /* nStatus==0 CheckingStatus
+ * nStatus==1 Associated
+ * nStatus==2 AdHocMode
+ * nStatus==3 NotAssociated
+ */
+ if (check_fwstate(pmlmepriv, _FW_UNDER_LINKING) == true)
+ ulInfo = CHECKINGSTATUS;
+ else if (check_fwstate(pmlmepriv, _FW_LINKED) == true)
+ ulInfo = ASSOCIATED;
+ else if (check_fwstate(pmlmepriv, WIFI_ADHOC_STATE) == true)
+ ulInfo = ADHOCMODE;
+ else
+ ulInfo = NOTASSOCIATED;
+ *(u32 *)poid_par_priv->information_buf = ulInfo;
+ *poid_par_priv->bytes_rw = poid_par_priv->information_buf_len;
+ return RNDIS_STATUS_SUCCESS;
+}
+
+uint oid_rt_set_default_key_id_hdl(struct oid_par_priv *poid_par_priv)
+{
+ if (poid_par_priv->type_of_oid != SET_OID)
+ return RNDIS_STATUS_NOT_ACCEPTED;
+ return RNDIS_STATUS_SUCCESS;
+}
diff --git a/drivers/staging/rtl8712/rtl871x_ioctl_rtl.h b/drivers/staging/rtl8712/rtl871x_ioctl_rtl.h
new file mode 100644
index 000000000..3bcceae3c
--- /dev/null
+++ b/drivers/staging/rtl8712/rtl871x_ioctl_rtl.h
@@ -0,0 +1,121 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2010 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ * Modifications for inclusion into the Linux staging tree are
+ * Copyright(c) 2010 Larry Finger. All rights reserved.
+ *
+ * Contact information:
+ * WLAN FAE <wlanfae@realtek.com>
+ * Larry Finger <Larry.Finger@lwfinger.net>
+ *
+ ******************************************************************************/
+#ifndef _RTL871X_IOCTL_RTL_H
+#define _RTL871X_IOCTL_RTL_H
+
+#include "osdep_service.h"
+#include "drv_types.h"
+
+/*************** oid_rtl_seg_01_01 **************/
+uint oid_rt_get_signal_quality_hdl(
+ struct oid_par_priv *poid_par_priv);/*84*/
+uint oid_rt_get_small_packet_crc_hdl(
+ struct oid_par_priv *poid_par_priv);
+uint oid_rt_get_middle_packet_crc_hdl(
+ struct oid_par_priv *poid_par_priv);
+uint oid_rt_get_large_packet_crc_hdl(
+ struct oid_par_priv *poid_par_priv);
+uint oid_rt_get_tx_retry_hdl(
+ struct oid_par_priv *poid_par_priv);
+uint oid_rt_get_rx_retry_hdl(
+ struct oid_par_priv *poid_par_priv);
+uint oid_rt_get_rx_total_packet_hdl(
+ struct oid_par_priv *poid_par_priv);
+uint oid_rt_get_tx_beacon_ok_hdl(
+ struct oid_par_priv *poid_par_priv);
+uint oid_rt_get_tx_beacon_err_hdl(
+ struct oid_par_priv *poid_par_priv);
+uint oid_rt_get_rx_icv_err_hdl(
+ struct oid_par_priv *poid_par_priv);/*93*/
+uint oid_rt_set_encryption_algorithm_hdl(
+ struct oid_par_priv *poid_par_priv);
+uint oid_rt_get_preamble_mode_hdl(
+ struct oid_par_priv *poid_par_priv);
+uint oid_rt_get_ap_ip_hdl(
+ struct oid_par_priv *poid_par_priv);
+uint oid_rt_get_channelplan_hdl(
+ struct oid_par_priv *poid_par_priv);
+uint oid_rt_set_channelplan_hdl(
+ struct oid_par_priv *poid_par_priv);
+uint oid_rt_set_preamble_mode_hdl(
+ struct oid_par_priv *poid_par_priv);
+uint oid_rt_set_bcn_intvl_hdl(
+ struct oid_par_priv *poid_par_priv);
+uint oid_rt_dedicate_probe_hdl(
+ struct oid_par_priv *poid_par_priv);
+uint oid_rt_get_total_tx_bytes_hdl(
+ struct oid_par_priv *poid_par_priv);
+uint oid_rt_get_total_rx_bytes_hdl(
+ struct oid_par_priv *poid_par_priv);
+uint oid_rt_current_tx_power_level_hdl(
+ struct oid_par_priv *poid_par_priv);
+uint oid_rt_get_enc_key_mismatch_count_hdl(
+ struct oid_par_priv *poid_par_priv);
+uint oid_rt_get_enc_key_match_count_hdl(
+ struct oid_par_priv *poid_par_priv);
+uint oid_rt_get_channel_hdl(
+ struct oid_par_priv *poid_par_priv);
+uint oid_rt_get_hardware_radio_off_hdl(
+ struct oid_par_priv *poid_par_priv);
+uint oid_rt_get_key_mismatch_hdl(
+ struct oid_par_priv *poid_par_priv);
+uint oid_rt_supported_wireless_mode_hdl(
+ struct oid_par_priv *poid_par_priv);
+uint oid_rt_get_channel_list_hdl(
+ struct oid_par_priv *poid_par_priv);
+uint oid_rt_get_scan_in_progress_hdl(
+ struct oid_par_priv *poid_par_priv);
+uint oid_rt_forced_data_rate_hdl(
+ struct oid_par_priv *poid_par_priv);
+uint oid_rt_wireless_mode_for_scan_list_hdl(
+ struct oid_par_priv *poid_par_priv);
+uint oid_rt_get_bss_wireless_mode_hdl(
+ struct oid_par_priv *poid_par_priv);
+uint oid_rt_scan_with_magic_packet_hdl(
+ struct oid_par_priv *poid_par_priv);
+
+/************** oid_rtl_seg_01_03 section start **************/
+uint oid_rt_ap_get_associated_station_list_hdl(
+ struct oid_par_priv *poid_par_priv);
+uint oid_rt_ap_switch_into_ap_mode_hdl(
+ struct oid_par_priv *poid_par_priv);
+uint oid_rt_ap_supported_hdl(
+ struct oid_par_priv *poid_par_priv);
+uint oid_rt_ap_set_passphrase_hdl(
+ struct oid_par_priv *poid_par_priv);
+/* oid_rtl_seg_01_11 */
+uint oid_rt_pro_rf_write_registry_hdl(
+ struct oid_par_priv *poid_par_priv);
+uint oid_rt_pro_rf_read_registry_hdl(
+ struct oid_par_priv *poid_par_priv);
+/*************** oid_rtl_seg_03_00 section start **************/
+uint oid_rt_get_connect_state_hdl(
+ struct oid_par_priv *poid_par_priv);
+uint oid_rt_set_default_key_id_hdl(
+ struct oid_par_priv *poid_par_priv);
+
+#endif
+
diff --git a/drivers/staging/rtl8712/rtl871x_ioctl_set.c b/drivers/staging/rtl8712/rtl871x_ioctl_set.c
new file mode 100644
index 000000000..22262b355
--- /dev/null
+++ b/drivers/staging/rtl8712/rtl871x_ioctl_set.c
@@ -0,0 +1,370 @@
+/******************************************************************************
+ * rtl871x_ioctl_set.c
+ *
+ * Copyright(c) 2007 - 2010 Realtek Corporation. All rights reserved.
+ * Linux device driver for RTL8192SU
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ * Modifications for inclusion into the Linux staging tree are
+ * Copyright(c) 2010 Larry Finger. All rights reserved.
+ *
+ * Contact information:
+ * WLAN FAE <wlanfae@realtek.com>
+ * Larry Finger <Larry.Finger@lwfinger.net>
+ *
+ ******************************************************************************/
+
+#define _RTL871X_IOCTL_SET_C_
+
+#include "osdep_service.h"
+#include "drv_types.h"
+#include "rtl871x_ioctl_set.h"
+#include "usb_osintf.h"
+#include "usb_ops.h"
+
+#define IS_MAC_ADDRESS_BROADCAST(addr) \
+( \
+ ((addr[0] == 0xff) && (addr[1] == 0xff) && \
+ (addr[2] == 0xff) && (addr[3] == 0xff) && \
+ (addr[4] == 0xff) && (addr[5] == 0xff)) ? true : false \
+)
+
+static u8 validate_ssid(struct ndis_802_11_ssid *ssid)
+{
+ u8 i;
+
+ if (ssid->SsidLength > 32)
+ return false;
+ for (i = 0; i < ssid->SsidLength; i++) {
+ /* wifi, printable ascii code must be supported */
+ if (!((ssid->Ssid[i] >= 0x20) && (ssid->Ssid[i] <= 0x7e)))
+ return false;
+ }
+ return true;
+}
+
+static u8 do_join(struct _adapter *padapter)
+{
+ struct list_head *plist, *phead;
+ u8 *pibss = NULL;
+ struct mlme_priv *pmlmepriv = &(padapter->mlmepriv);
+ struct __queue *queue = &(pmlmepriv->scanned_queue);
+
+ phead = &queue->queue;
+ plist = phead->next;
+ pmlmepriv->cur_network.join_res = -2;
+ pmlmepriv->fw_state |= _FW_UNDER_LINKING;
+ pmlmepriv->pscanned = plist;
+ pmlmepriv->to_join = true;
+
+ /* adhoc mode will start with an empty queue, but skip checking */
+ if (!check_fwstate(pmlmepriv, WIFI_ADHOC_STATE) &&
+ list_empty(&queue->queue)) {
+ if (pmlmepriv->fw_state & _FW_UNDER_LINKING)
+ pmlmepriv->fw_state ^= _FW_UNDER_LINKING;
+ /* when set_ssid/set_bssid for do_join(), but scanning queue
+ * is empty we try to issue sitesurvey firstly
+ */
+ if (!pmlmepriv->sitesurveyctrl.traffic_busy)
+ r8712_sitesurvey_cmd(padapter, &pmlmepriv->assoc_ssid);
+ return true;
+ } else {
+ int ret;
+
+ ret = r8712_select_and_join_from_scan(pmlmepriv);
+ if (ret == _SUCCESS)
+ mod_timer(&pmlmepriv->assoc_timer,
+ jiffies + msecs_to_jiffies(MAX_JOIN_TIMEOUT));
+ else {
+ if (check_fwstate(pmlmepriv, WIFI_ADHOC_STATE)) {
+ /* submit r8712_createbss_cmd to change to an
+ * ADHOC_MASTER pmlmepriv->lock has been
+ * acquired by caller...
+ */
+ struct wlan_bssid_ex *pdev_network =
+ &(padapter->registrypriv.dev_network);
+ pmlmepriv->fw_state = WIFI_ADHOC_MASTER_STATE;
+ pibss = padapter->registrypriv.dev_network.
+ MacAddress;
+ memcpy(&pdev_network->Ssid,
+ &pmlmepriv->assoc_ssid,
+ sizeof(struct ndis_802_11_ssid));
+ r8712_update_registrypriv_dev_network(padapter);
+ r8712_generate_random_ibss(pibss);
+ if (r8712_createbss_cmd(padapter) != _SUCCESS)
+ return false;
+ pmlmepriv->to_join = false;
+ } else {
+ /* can't associate ; reset under-linking */
+ if (pmlmepriv->fw_state & _FW_UNDER_LINKING)
+ pmlmepriv->fw_state ^=
+ _FW_UNDER_LINKING;
+ /* when set_ssid/set_bssid for do_join(), but
+ * there are no desired bss in scanning queue
+ * we try to issue sitesurvey first
+ */
+ if (!pmlmepriv->sitesurveyctrl.traffic_busy)
+ r8712_sitesurvey_cmd(padapter,
+ &pmlmepriv->assoc_ssid);
+ }
+ }
+ }
+ return true;
+}
+
+u8 r8712_set_802_11_bssid(struct _adapter *padapter, u8 *bssid)
+{
+ unsigned long irqL;
+ u8 status = true;
+ struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
+
+ if (is_zero_ether_addr(bssid) || is_broadcast_ether_addr(bssid)) {
+ status = false;
+ return status;
+ }
+ spin_lock_irqsave(&pmlmepriv->lock, irqL);
+ if (check_fwstate(pmlmepriv, _FW_UNDER_SURVEY |
+ _FW_UNDER_LINKING) == true) {
+ status = check_fwstate(pmlmepriv, _FW_UNDER_LINKING);
+ goto _Abort_Set_BSSID;
+ }
+ if (check_fwstate(pmlmepriv,
+ _FW_LINKED|WIFI_ADHOC_MASTER_STATE) == true) {
+ if (!memcmp(&pmlmepriv->cur_network.network.MacAddress, bssid,
+ ETH_ALEN)) {
+ if (!check_fwstate(pmlmepriv, WIFI_STATION_STATE))
+ goto _Abort_Set_BSSID; /* driver is in
+ * WIFI_ADHOC_MASTER_STATE */
+ } else {
+ r8712_disassoc_cmd(padapter);
+ if (check_fwstate(pmlmepriv, _FW_LINKED) == true)
+ r8712_ind_disconnect(padapter);
+ r8712_free_assoc_resources(padapter);
+ if ((check_fwstate(pmlmepriv,
+ WIFI_ADHOC_MASTER_STATE))) {
+ _clr_fwstate_(pmlmepriv,
+ WIFI_ADHOC_MASTER_STATE);
+ set_fwstate(pmlmepriv, WIFI_ADHOC_STATE);
+ }
+ }
+ }
+ memcpy(&pmlmepriv->assoc_bssid, bssid, ETH_ALEN);
+ pmlmepriv->assoc_by_bssid = true;
+ status = do_join(padapter);
+ goto done;
+_Abort_Set_BSSID:
+done:
+ spin_unlock_irqrestore(&pmlmepriv->lock, irqL);
+ return status;
+}
+
+void r8712_set_802_11_ssid(struct _adapter *padapter,
+ struct ndis_802_11_ssid *ssid)
+{
+ unsigned long irqL;
+ struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
+ struct wlan_network *pnetwork = &pmlmepriv->cur_network;
+
+ if (!padapter->hw_init_completed)
+ return;
+ spin_lock_irqsave(&pmlmepriv->lock, irqL);
+ if (check_fwstate(pmlmepriv, _FW_UNDER_SURVEY|_FW_UNDER_LINKING)) {
+ check_fwstate(pmlmepriv, _FW_UNDER_LINKING);
+ goto _Abort_Set_SSID;
+ }
+ if (check_fwstate(pmlmepriv, _FW_LINKED|WIFI_ADHOC_MASTER_STATE)) {
+ if ((pmlmepriv->assoc_ssid.SsidLength == ssid->SsidLength) &&
+ (!memcmp(&pmlmepriv->assoc_ssid.Ssid, ssid->Ssid,
+ ssid->SsidLength))) {
+ if (!check_fwstate(pmlmepriv, WIFI_STATION_STATE)) {
+ if (!r8712_is_same_ibss(padapter,
+ pnetwork)) {
+ /* if in WIFI_ADHOC_MASTER_STATE or
+ * WIFI_ADHOC_STATE, create bss or
+ * rejoin again
+ */
+ r8712_disassoc_cmd(padapter);
+ if (check_fwstate(pmlmepriv,
+ _FW_LINKED) == true)
+ r8712_ind_disconnect(padapter);
+ r8712_free_assoc_resources(padapter);
+ if (check_fwstate(pmlmepriv,
+ WIFI_ADHOC_MASTER_STATE)) {
+ _clr_fwstate_(pmlmepriv,
+ WIFI_ADHOC_MASTER_STATE);
+ set_fwstate(pmlmepriv,
+ WIFI_ADHOC_STATE);
+ }
+ } else
+ goto _Abort_Set_SSID; /* driver is in
+ * WIFI_ADHOC_MASTER_STATE */
+ }
+ } else {
+ r8712_disassoc_cmd(padapter);
+ if (check_fwstate(pmlmepriv, _FW_LINKED) == true)
+ r8712_ind_disconnect(padapter);
+ r8712_free_assoc_resources(padapter);
+ if (check_fwstate(pmlmepriv,
+ WIFI_ADHOC_MASTER_STATE) == true) {
+ _clr_fwstate_(pmlmepriv,
+ WIFI_ADHOC_MASTER_STATE);
+ set_fwstate(pmlmepriv, WIFI_ADHOC_STATE);
+ }
+ }
+ }
+ if (padapter->securitypriv.btkip_countermeasure == true)
+ goto _Abort_Set_SSID;
+ if (!validate_ssid(ssid))
+ goto _Abort_Set_SSID;
+ memcpy(&pmlmepriv->assoc_ssid, ssid, sizeof(struct ndis_802_11_ssid));
+ pmlmepriv->assoc_by_bssid = false;
+ do_join(padapter);
+ goto done;
+_Abort_Set_SSID:
+done:
+ spin_unlock_irqrestore(&pmlmepriv->lock, irqL);
+}
+
+void r8712_set_802_11_infrastructure_mode(struct _adapter *padapter,
+ enum NDIS_802_11_NETWORK_INFRASTRUCTURE networktype)
+{
+ unsigned long irqL;
+ struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
+ struct wlan_network *cur_network = &pmlmepriv->cur_network;
+ enum NDIS_802_11_NETWORK_INFRASTRUCTURE *pold_state =
+ &(cur_network->network.InfrastructureMode);
+
+ if (*pold_state != networktype) {
+ spin_lock_irqsave(&pmlmepriv->lock, irqL);
+ if ((check_fwstate(pmlmepriv, _FW_LINKED) == true) ||
+ (*pold_state == Ndis802_11IBSS))
+ r8712_disassoc_cmd(padapter);
+ if (check_fwstate(pmlmepriv,
+ _FW_LINKED|WIFI_ADHOC_MASTER_STATE) == true)
+ r8712_free_assoc_resources(padapter);
+ if ((check_fwstate(pmlmepriv, _FW_LINKED) == true) ||
+ (*pold_state == Ndis802_11Infrastructure) ||
+ (*pold_state == Ndis802_11IBSS)) {
+ /* will clr Linked_state before this function,
+ * we must have checked whether issue dis-assoc_cmd or
+ * not */
+ r8712_ind_disconnect(padapter);
+ }
+ *pold_state = networktype;
+ /* clear WIFI_STATION_STATE; WIFI_AP_STATE; WIFI_ADHOC_STATE;
+ * WIFI_ADHOC_MASTER_STATE */
+ _clr_fwstate_(pmlmepriv, WIFI_STATION_STATE | WIFI_AP_STATE |
+ WIFI_ADHOC_STATE | WIFI_ADHOC_MASTER_STATE);
+ switch (networktype) {
+ case Ndis802_11IBSS:
+ set_fwstate(pmlmepriv, WIFI_ADHOC_STATE);
+ break;
+ case Ndis802_11Infrastructure:
+ set_fwstate(pmlmepriv, WIFI_STATION_STATE);
+ break;
+ case Ndis802_11APMode:
+ set_fwstate(pmlmepriv, WIFI_AP_STATE);
+ break;
+ case Ndis802_11AutoUnknown:
+ case Ndis802_11InfrastructureMax:
+ break;
+ }
+ spin_unlock_irqrestore(&pmlmepriv->lock, irqL);
+ }
+}
+
+u8 r8712_set_802_11_disassociate(struct _adapter *padapter)
+{
+ unsigned long irqL;
+ struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
+
+ spin_lock_irqsave(&pmlmepriv->lock, irqL);
+ if (check_fwstate(pmlmepriv, _FW_LINKED) == true) {
+ r8712_disassoc_cmd(padapter);
+ r8712_ind_disconnect(padapter);
+ r8712_free_assoc_resources(padapter);
+ }
+ spin_unlock_irqrestore(&pmlmepriv->lock, irqL);
+ return true;
+}
+
+u8 r8712_set_802_11_bssid_list_scan(struct _adapter *padapter)
+{
+ struct mlme_priv *pmlmepriv = NULL;
+ unsigned long irqL;
+ u8 ret = true;
+
+ if (!padapter)
+ return false;
+ pmlmepriv = &padapter->mlmepriv;
+ if (!padapter->hw_init_completed)
+ return false;
+ spin_lock_irqsave(&pmlmepriv->lock, irqL);
+ if ((check_fwstate(pmlmepriv, _FW_UNDER_SURVEY|_FW_UNDER_LINKING)) ||
+ (pmlmepriv->sitesurveyctrl.traffic_busy == true)) {
+ /* Scan or linking is in progress, do nothing. */
+ ret = (u8)check_fwstate(pmlmepriv, _FW_UNDER_SURVEY);
+ } else {
+ r8712_free_network_queue(padapter);
+ ret = r8712_sitesurvey_cmd(padapter, NULL);
+ }
+ spin_unlock_irqrestore(&pmlmepriv->lock, irqL);
+ return ret;
+}
+
+u8 r8712_set_802_11_authentication_mode(struct _adapter *padapter,
+ enum NDIS_802_11_AUTHENTICATION_MODE authmode)
+{
+ struct security_priv *psecuritypriv = &padapter->securitypriv;
+ u8 ret;
+
+ psecuritypriv->ndisauthtype = authmode;
+ if (psecuritypriv->ndisauthtype > 3)
+ psecuritypriv->AuthAlgrthm = 2; /* 802.1x */
+ if (r8712_set_auth(padapter, psecuritypriv) == _SUCCESS)
+ ret = true;
+ else
+ ret = false;
+ return ret;
+}
+
+u8 r8712_set_802_11_add_wep(struct _adapter *padapter,
+ struct NDIS_802_11_WEP *wep)
+{
+ sint keyid;
+ struct security_priv *psecuritypriv = &padapter->securitypriv;
+
+ keyid = wep->KeyIndex & 0x3fffffff;
+ if (keyid >= WEP_KEYS)
+ return false;
+ switch (wep->KeyLength) {
+ case 5:
+ psecuritypriv->PrivacyAlgrthm = _WEP40_;
+ break;
+ case 13:
+ psecuritypriv->PrivacyAlgrthm = _WEP104_;
+ break;
+ default:
+ psecuritypriv->PrivacyAlgrthm = _NO_PRIVACY_;
+ break;
+ }
+ memcpy(psecuritypriv->DefKey[keyid].skey, &wep->KeyMaterial,
+ wep->KeyLength);
+ psecuritypriv->DefKeylen[keyid] = wep->KeyLength;
+ psecuritypriv->PrivacyKeyIndex = keyid;
+ if (r8712_set_key(padapter, psecuritypriv, keyid) == _FAIL)
+ return false;
+ return _SUCCESS;
+}
diff --git a/drivers/staging/rtl8712/rtl871x_ioctl_set.h b/drivers/staging/rtl8712/rtl871x_ioctl_set.h
new file mode 100644
index 000000000..2c94cd151
--- /dev/null
+++ b/drivers/staging/rtl8712/rtl871x_ioctl_set.h
@@ -0,0 +1,57 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2010 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ * Modifications for inclusion into the Linux staging tree are
+ * Copyright(c) 2010 Larry Finger. All rights reserved.
+ *
+ * Contact information:
+ * WLAN FAE <wlanfae@realtek.com>
+ * Larry Finger <Larry.Finger@lwfinger.net>
+ *
+ ******************************************************************************/
+#ifndef __IOCTL_SET_H
+#define __IOCTL_SET_H
+
+#include "drv_types.h"
+
+typedef u8 NDIS_802_11_PMKID_VALUE[16];
+
+struct BSSIDInfo {
+ unsigned char BSSID[6];
+ NDIS_802_11_PMKID_VALUE PMKID;
+};
+
+u8 r8712_set_802_11_authentication_mode(struct _adapter *pdapter,
+ enum NDIS_802_11_AUTHENTICATION_MODE authmode);
+
+u8 r8712_set_802_11_bssid(struct _adapter *padapter, u8 *bssid);
+
+u8 r8712_set_802_11_add_wep(struct _adapter *padapter,
+ struct NDIS_802_11_WEP *wep);
+
+u8 r8712_set_802_11_disassociate(struct _adapter *padapter);
+
+u8 r8712_set_802_11_bssid_list_scan(struct _adapter *padapter);
+
+void r8712_set_802_11_infrastructure_mode(struct _adapter *padapter,
+ enum NDIS_802_11_NETWORK_INFRASTRUCTURE networktype);
+
+void r8712_set_802_11_ssid(struct _adapter *padapter,
+ struct ndis_802_11_ssid *ssid);
+
+#endif
+
diff --git a/drivers/staging/rtl8712/rtl871x_led.h b/drivers/staging/rtl8712/rtl871x_led.h
new file mode 100644
index 000000000..eb612053a
--- /dev/null
+++ b/drivers/staging/rtl8712/rtl871x_led.h
@@ -0,0 +1,124 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2010 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ * Modifications for inclusion into the Linux staging tree are
+ * Copyright(c) 2010 Larry Finger. All rights reserved.
+ *
+ * Contact information:
+ * WLAN FAE <wlanfae@realtek.com>
+ * Larry Finger <Larry.Finger@lwfinger.net>
+ *
+ ******************************************************************************/
+#ifndef __RTL8712_LED_H
+#define __RTL8712_LED_H
+
+#include "osdep_service.h"
+#include "drv_types.h"
+
+/*===========================================================================
+ * LED customization.
+ *===========================================================================
+ */
+enum LED_CTL_MODE {
+ LED_CTL_POWER_ON = 1,
+ LED_CTL_LINK = 2,
+ LED_CTL_NO_LINK = 3,
+ LED_CTL_TX = 4,
+ LED_CTL_RX = 5,
+ LED_CTL_SITE_SURVEY = 6,
+ LED_CTL_POWER_OFF = 7,
+ LED_CTL_START_TO_LINK = 8,
+ LED_CTL_START_WPS = 9,
+ LED_CTL_STOP_WPS = 10,
+ LED_CTL_START_WPS_BOTTON = 11,
+ LED_CTL_STOP_WPS_FAIL = 12,
+ LED_CTL_STOP_WPS_FAIL_OVERLAP = 13,
+};
+
+#define IS_LED_WPS_BLINKING(_LED_871x) \
+ (((struct LED_871x *)_LED_871x)->CurrLedState == LED_BLINK_WPS \
+ || ((struct LED_871x *)_LED_871x)->CurrLedState == LED_BLINK_WPS_STOP \
+ || ((struct LED_871x *)_LED_871x)->bLedWPSBlinkInProgress)
+
+#define IS_LED_BLINKING(_LED_871x) \
+ (((struct LED_871x *)_LED_871x)->bLedWPSBlinkInProgress \
+ || ((struct LED_871x *)_LED_871x)->bLedScanBlinkInProgress)
+
+enum LED_PIN_871x {
+ LED_PIN_GPIO0,
+ LED_PIN_LED0,
+ LED_PIN_LED1
+};
+
+/*===========================================================================
+ * LED customization.
+ *===========================================================================
+ */
+enum LED_STRATEGY_871x {
+ SW_LED_MODE0, /* SW control 1 LED via GPIO0. It is default option. */
+ SW_LED_MODE1, /* 2 LEDs, through LED0 and LED1. For ALPHA. */
+ SW_LED_MODE2, /* SW control 1 LED via GPIO0,
+ * custom for AzWave 8187 minicard. */
+ SW_LED_MODE3, /* SW control 1 LED via GPIO0,
+ * customized for Sercomm Printer Server case.*/
+ SW_LED_MODE4, /*for Edimax / Belkin*/
+ SW_LED_MODE5, /*for Sercomm / Belkin*/
+ SW_LED_MODE6, /*for WNC / Corega*/
+ HW_LED, /* HW control 2 LEDs, LED0 and LED1 (there are 4 different
+ * control modes, see MAC.CONFIG1 for details.)*/
+};
+
+struct LED_871x {
+ struct _adapter *padapter;
+ enum LED_PIN_871x LedPin; /* Implementation for this SW led. */
+ u32 CurrLedState; /* Current LED state. */
+ u8 bLedOn; /* true if LED is ON */
+ u8 bSWLedCtrl;
+ u8 bLedBlinkInProgress; /*true if blinking */
+ u8 bLedNoLinkBlinkInProgress;
+ u8 bLedLinkBlinkInProgress;
+ u8 bLedStartToLinkBlinkInProgress;
+ u8 bLedScanBlinkInProgress;
+ u8 bLedWPSBlinkInProgress;
+ u32 BlinkTimes; /* No. times to toggle for blink.*/
+ u32 BlinkingLedState; /* Next state for blinking,
+ * either LED_ON or OFF.*/
+
+ struct timer_list BlinkTimer; /* Timer object for led blinking.*/
+ struct work_struct BlinkWorkItem; /* Workitem used by BlinkTimer */
+};
+
+struct led_priv {
+ /* add for led control */
+ struct LED_871x SwLed0;
+ struct LED_871x SwLed1;
+ enum LED_STRATEGY_871x LedStrategy;
+ u8 bRegUseLed;
+ void (*LedControlHandler)(struct _adapter *padapter,
+ enum LED_CTL_MODE LedAction);
+ /* add for led control */
+};
+
+/*===========================================================================
+ * Interface to manipulate LED objects.
+ *===========================================================================*/
+void r8712_InitSwLeds(struct _adapter *padapter);
+void r8712_DeInitSwLeds(struct _adapter *padapter);
+void LedControl871x(struct _adapter *padapter, enum LED_CTL_MODE LedAction);
+
+#endif
+
diff --git a/drivers/staging/rtl8712/rtl871x_mlme.c b/drivers/staging/rtl8712/rtl871x_mlme.c
new file mode 100644
index 000000000..c044b0e55
--- /dev/null
+++ b/drivers/staging/rtl8712/rtl871x_mlme.c
@@ -0,0 +1,1808 @@
+/******************************************************************************
+ * rtl871x_mlme.c
+ *
+ * Copyright(c) 2007 - 2010 Realtek Corporation. All rights reserved.
+ * Linux device driver for RTL8192SU
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ * Modifications for inclusion into the Linux staging tree are
+ * Copyright(c) 2010 Larry Finger. All rights reserved.
+ *
+ * Contact information:
+ * WLAN FAE <wlanfae@realtek.com>
+ * Larry Finger <Larry.Finger@lwfinger.net>
+ *
+ ******************************************************************************/
+
+#define _RTL871X_MLME_C_
+
+#include <linux/etherdevice.h>
+
+#include "osdep_service.h"
+#include "drv_types.h"
+#include "recv_osdep.h"
+#include "xmit_osdep.h"
+#include "mlme_osdep.h"
+#include "sta_info.h"
+#include "wifi.h"
+#include "wlan_bssdef.h"
+
+static void update_ht_cap(struct _adapter *padapter, u8 *pie, uint ie_len);
+
+static sint _init_mlme_priv(struct _adapter *padapter)
+{
+ sint i;
+ u8 *pbuf;
+ struct wlan_network *pnetwork;
+ struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
+
+ memset((u8 *)pmlmepriv, 0, sizeof(struct mlme_priv));
+ pmlmepriv->nic_hdl = (u8 *)padapter;
+ pmlmepriv->pscanned = NULL;
+ pmlmepriv->fw_state = 0;
+ pmlmepriv->cur_network.network.InfrastructureMode =
+ Ndis802_11AutoUnknown;
+ /* Maybe someday we should rename this variable to "active_mode"(Jeff)*/
+ pmlmepriv->passive_mode = 1; /* 1: active, 0: passive. */
+ spin_lock_init(&(pmlmepriv->lock));
+ spin_lock_init(&(pmlmepriv->lock2));
+ _init_queue(&(pmlmepriv->free_bss_pool));
+ _init_queue(&(pmlmepriv->scanned_queue));
+ set_scanned_network_val(pmlmepriv, 0);
+ memset(&pmlmepriv->assoc_ssid, 0, sizeof(struct ndis_802_11_ssid));
+ pbuf = kmalloc(MAX_BSS_CNT * (sizeof(struct wlan_network)),
+ GFP_ATOMIC);
+ if (pbuf == NULL)
+ return _FAIL;
+ pmlmepriv->free_bss_buf = pbuf;
+ pnetwork = (struct wlan_network *)pbuf;
+ for (i = 0; i < MAX_BSS_CNT; i++) {
+ INIT_LIST_HEAD(&(pnetwork->list));
+ list_add_tail(&(pnetwork->list),
+ &(pmlmepriv->free_bss_pool.queue));
+ pnetwork++;
+ }
+ pmlmepriv->sitesurveyctrl.last_rx_pkts = 0;
+ pmlmepriv->sitesurveyctrl.last_tx_pkts = 0;
+ pmlmepriv->sitesurveyctrl.traffic_busy = false;
+ /* allocate DMA-able/Non-Page memory for cmd_buf and rsp_buf */
+ r8712_init_mlme_timer(padapter);
+ return _SUCCESS;
+}
+
+struct wlan_network *_r8712_alloc_network(struct mlme_priv *pmlmepriv)
+{
+ unsigned long irqL;
+ struct wlan_network *pnetwork;
+ struct __queue *free_queue = &pmlmepriv->free_bss_pool;
+ struct list_head *plist = NULL;
+
+ if (list_empty(&free_queue->queue))
+ return NULL;
+ spin_lock_irqsave(&free_queue->lock, irqL);
+ plist = free_queue->queue.next;
+ pnetwork = LIST_CONTAINOR(plist, struct wlan_network, list);
+ list_del_init(&pnetwork->list);
+ pnetwork->last_scanned = jiffies;
+ pmlmepriv->num_of_scanned++;
+ spin_unlock_irqrestore(&free_queue->lock, irqL);
+ return pnetwork;
+}
+
+static void _free_network(struct mlme_priv *pmlmepriv,
+ struct wlan_network *pnetwork)
+{
+ u32 curr_time, delta_time;
+ unsigned long irqL;
+ struct __queue *free_queue = &(pmlmepriv->free_bss_pool);
+
+ if (pnetwork == NULL)
+ return;
+ if (pnetwork->fixed == true)
+ return;
+ curr_time = jiffies;
+ delta_time = (curr_time - (u32)pnetwork->last_scanned) / HZ;
+ if (delta_time < SCANQUEUE_LIFETIME)
+ return;
+ spin_lock_irqsave(&free_queue->lock, irqL);
+ list_del_init(&pnetwork->list);
+ list_add_tail(&pnetwork->list, &free_queue->queue);
+ pmlmepriv->num_of_scanned--;
+ spin_unlock_irqrestore(&free_queue->lock, irqL);
+}
+
+static void _free_network_nolock(struct mlme_priv *pmlmepriv,
+ struct wlan_network *pnetwork)
+{
+ struct __queue *free_queue = &pmlmepriv->free_bss_pool;
+
+ if (pnetwork == NULL)
+ return;
+ if (pnetwork->fixed == true)
+ return;
+ list_del_init(&pnetwork->list);
+ list_add_tail(&pnetwork->list, &free_queue->queue);
+ pmlmepriv->num_of_scanned--;
+}
+
+
+/*
+ return the wlan_network with the matching addr
+ Shall be called under atomic context...
+ to avoid possible racing condition...
+*/
+static struct wlan_network *_r8712_find_network(struct __queue *scanned_queue,
+ u8 *addr)
+{
+ unsigned long irqL;
+ struct list_head *phead, *plist;
+ struct wlan_network *pnetwork = NULL;
+
+ if (is_zero_ether_addr(addr))
+ return NULL;
+ spin_lock_irqsave(&scanned_queue->lock, irqL);
+ phead = &scanned_queue->queue;
+ plist = phead->next;
+ while (plist != phead) {
+ pnetwork = LIST_CONTAINOR(plist, struct wlan_network, list);
+ plist = plist->next;
+ if (!memcmp(addr, pnetwork->network.MacAddress, ETH_ALEN))
+ break;
+ }
+ spin_unlock_irqrestore(&scanned_queue->lock, irqL);
+ return pnetwork;
+}
+
+static void _free_network_queue(struct _adapter *padapter)
+{
+ unsigned long irqL;
+ struct list_head *phead, *plist;
+ struct wlan_network *pnetwork;
+ struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
+ struct __queue *scanned_queue = &pmlmepriv->scanned_queue;
+
+ spin_lock_irqsave(&scanned_queue->lock, irqL);
+ phead = &scanned_queue->queue;
+ plist = phead->next;
+ while (end_of_queue_search(phead, plist) == false) {
+ pnetwork = LIST_CONTAINOR(plist, struct wlan_network, list);
+ plist = plist->next;
+ _free_network(pmlmepriv, pnetwork);
+ }
+ spin_unlock_irqrestore(&scanned_queue->lock, irqL);
+}
+
+sint r8712_if_up(struct _adapter *padapter)
+{
+ sint res;
+
+ if (padapter->bDriverStopped || padapter->bSurpriseRemoved ||
+ (check_fwstate(&padapter->mlmepriv, _FW_LINKED) == false)) {
+ res = false;
+ } else
+ res = true;
+ return res;
+}
+
+void r8712_generate_random_ibss(u8 *pibss)
+{
+ u32 curtime = jiffies;
+
+ pibss[0] = 0x02; /*in ad-hoc mode bit1 must set to 1 */
+ pibss[1] = 0x11;
+ pibss[2] = 0x87;
+ pibss[3] = (u8)(curtime & 0xff);
+ pibss[4] = (u8)((curtime>>8) & 0xff);
+ pibss[5] = (u8)((curtime>>16) & 0xff);
+}
+
+uint r8712_get_ndis_wlan_bssid_ex_sz(struct ndis_wlan_bssid_ex *bss)
+{
+ uint t_len;
+
+ t_len = sizeof(u32) + 6 * sizeof(unsigned long) + 2 +
+ sizeof(struct ndis_802_11_ssid) + sizeof(u32) +
+ sizeof(s32) +
+ sizeof(enum NDIS_802_11_NETWORK_TYPE) +
+ sizeof(struct NDIS_802_11_CONFIGURATION) +
+ sizeof(enum NDIS_802_11_NETWORK_INFRASTRUCTURE) +
+ sizeof(NDIS_802_11_RATES_EX) +
+ sizeof(u32) + bss->IELength;
+ return t_len;
+}
+
+u8 *r8712_get_capability_from_ie(u8 *ie)
+{
+ return ie + 8 + 2;
+}
+
+int r8712_init_mlme_priv(struct _adapter *padapter)
+{
+ return _init_mlme_priv(padapter);
+}
+
+void r8712_free_mlme_priv(struct mlme_priv *pmlmepriv)
+{
+ kfree(pmlmepriv->free_bss_buf);
+}
+
+static struct wlan_network *alloc_network(struct mlme_priv *pmlmepriv)
+{
+ return _r8712_alloc_network(pmlmepriv);
+}
+
+static void free_network_nolock(struct mlme_priv *pmlmepriv,
+ struct wlan_network *pnetwork)
+{
+ _free_network_nolock(pmlmepriv, pnetwork);
+}
+
+void r8712_free_network_queue(struct _adapter *dev)
+{
+ _free_network_queue(dev);
+}
+
+/*
+ return the wlan_network with the matching addr
+
+ Shall be called under atomic context...
+ to avoid possible racing condition...
+*/
+static struct wlan_network *r8712_find_network(struct __queue *scanned_queue,
+ u8 *addr)
+{
+ struct wlan_network *pnetwork = _r8712_find_network(scanned_queue,
+ addr);
+
+ return pnetwork;
+}
+
+int r8712_is_same_ibss(struct _adapter *adapter, struct wlan_network *pnetwork)
+{
+ int ret = true;
+ struct security_priv *psecuritypriv = &adapter->securitypriv;
+
+ if ((psecuritypriv->PrivacyAlgrthm != _NO_PRIVACY_) &&
+ (pnetwork->network.Privacy == 0))
+ ret = false;
+ else if ((psecuritypriv->PrivacyAlgrthm == _NO_PRIVACY_) &&
+ (pnetwork->network.Privacy == 1))
+ ret = false;
+ else
+ ret = true;
+ return ret;
+
+}
+
+static int is_same_network(struct ndis_wlan_bssid_ex *src,
+ struct ndis_wlan_bssid_ex *dst)
+{
+ u16 s_cap, d_cap;
+
+ memcpy((u8 *)&s_cap, r8712_get_capability_from_ie(src->IEs), 2);
+ memcpy((u8 *)&d_cap, r8712_get_capability_from_ie(dst->IEs), 2);
+ return (src->Ssid.SsidLength == dst->Ssid.SsidLength) &&
+ (src->Configuration.DSConfig ==
+ dst->Configuration.DSConfig) &&
+ ((!memcmp(src->MacAddress, dst->MacAddress,
+ ETH_ALEN))) &&
+ ((!memcmp(src->Ssid.Ssid,
+ dst->Ssid.Ssid,
+ src->Ssid.SsidLength))) &&
+ ((s_cap & WLAN_CAPABILITY_IBSS) ==
+ (d_cap & WLAN_CAPABILITY_IBSS)) &&
+ ((s_cap & WLAN_CAPABILITY_BSS) ==
+ (d_cap & WLAN_CAPABILITY_BSS));
+
+}
+
+struct wlan_network *r8712_get_oldest_wlan_network(
+ struct __queue *scanned_queue)
+{
+ struct list_head *plist, *phead;
+ struct wlan_network *pwlan = NULL;
+ struct wlan_network *oldest = NULL;
+
+ phead = &scanned_queue->queue;
+ plist = phead->next;
+ while (1) {
+ if (end_of_queue_search(phead, plist) == true)
+ break;
+ pwlan = LIST_CONTAINOR(plist, struct wlan_network, list);
+ if (pwlan->fixed != true) {
+ if (oldest == NULL ||
+ time_after((unsigned long)oldest->last_scanned,
+ (unsigned long)pwlan->last_scanned))
+ oldest = pwlan;
+ }
+ plist = plist->next;
+ }
+ return oldest;
+}
+
+static void update_network(struct ndis_wlan_bssid_ex *dst,
+ struct ndis_wlan_bssid_ex *src,
+ struct _adapter *padapter)
+{
+ u32 last_evm = 0, tmpVal;
+
+ if (check_fwstate(&padapter->mlmepriv, _FW_LINKED) &&
+ is_same_network(&(padapter->mlmepriv.cur_network.network), src)) {
+ if (padapter->recvpriv.signal_qual_data.total_num++ >=
+ PHY_LINKQUALITY_SLID_WIN_MAX) {
+ padapter->recvpriv.signal_qual_data.total_num =
+ PHY_LINKQUALITY_SLID_WIN_MAX;
+ last_evm = padapter->recvpriv.signal_qual_data.
+ elements[padapter->recvpriv.
+ signal_qual_data.index];
+ padapter->recvpriv.signal_qual_data.total_val -=
+ last_evm;
+ }
+ padapter->recvpriv.signal_qual_data.total_val += src->Rssi;
+
+ padapter->recvpriv.signal_qual_data.
+ elements[padapter->recvpriv.signal_qual_data.
+ index++] = src->Rssi;
+ if (padapter->recvpriv.signal_qual_data.index >=
+ PHY_LINKQUALITY_SLID_WIN_MAX)
+ padapter->recvpriv.signal_qual_data.index = 0;
+ /* <1> Showed on UI for user, in percentage. */
+ tmpVal = padapter->recvpriv.signal_qual_data.total_val /
+ padapter->recvpriv.signal_qual_data.total_num;
+ padapter->recvpriv.signal = (u8)tmpVal;
+
+ src->Rssi = padapter->recvpriv.signal;
+ } else
+ src->Rssi = (src->Rssi + dst->Rssi) / 2;
+ memcpy((u8 *)dst, (u8 *)src, r8712_get_ndis_wlan_bssid_ex_sz(src));
+}
+
+static void update_current_network(struct _adapter *adapter,
+ struct ndis_wlan_bssid_ex *pnetwork)
+{
+ struct mlme_priv *pmlmepriv = &adapter->mlmepriv;
+
+ if (is_same_network(&(pmlmepriv->cur_network.network), pnetwork)) {
+ update_network(&(pmlmepriv->cur_network.network),
+ pnetwork, adapter);
+ r8712_update_protection(adapter,
+ (pmlmepriv->cur_network.network.IEs) +
+ sizeof(struct NDIS_802_11_FIXED_IEs),
+ pmlmepriv->cur_network.network.IELength);
+ }
+}
+
+/*
+Caller must hold pmlmepriv->lock first.
+*/
+static void update_scanned_network(struct _adapter *adapter,
+ struct ndis_wlan_bssid_ex *target)
+{
+ struct list_head *plist, *phead;
+
+ u32 bssid_ex_sz;
+ struct mlme_priv *pmlmepriv = &adapter->mlmepriv;
+ struct __queue *queue = &pmlmepriv->scanned_queue;
+ struct wlan_network *pnetwork = NULL;
+ struct wlan_network *oldest = NULL;
+
+ phead = &queue->queue;
+ plist = phead->next;
+
+ while (1) {
+ if (end_of_queue_search(phead, plist) == true)
+ break;
+
+ pnetwork = LIST_CONTAINOR(plist, struct wlan_network, list);
+ if (is_same_network(&pnetwork->network, target))
+ break;
+ if ((oldest == ((struct wlan_network *)0)) ||
+ time_after((unsigned long)oldest->last_scanned,
+ (unsigned long)pnetwork->last_scanned))
+ oldest = pnetwork;
+
+ plist = plist->next;
+ }
+
+
+ /* If we didn't find a match, then get a new network slot to initialize
+ * with this beacon's information */
+ if (end_of_queue_search(phead, plist) == true) {
+ if (list_empty(&pmlmepriv->free_bss_pool.queue)) {
+ /* If there are no more slots, expire the oldest */
+ pnetwork = oldest;
+ target->Rssi = (pnetwork->network.Rssi +
+ target->Rssi) / 2;
+ memcpy(&pnetwork->network, target,
+ r8712_get_ndis_wlan_bssid_ex_sz(target));
+ pnetwork->last_scanned = jiffies;
+ } else {
+ /* Otherwise just pull from the free list */
+ /* update scan_time */
+ pnetwork = alloc_network(pmlmepriv);
+ if (pnetwork == NULL)
+ return;
+ bssid_ex_sz = r8712_get_ndis_wlan_bssid_ex_sz(target);
+ target->Length = bssid_ex_sz;
+ memcpy(&pnetwork->network, target, bssid_ex_sz);
+ list_add_tail(&pnetwork->list, &queue->queue);
+ }
+ } else {
+ /* we have an entry and we are going to update it. But
+ * this entry may be already expired. In this case we
+ * do the same as we found a new net and call the new_net
+ * handler
+ */
+ update_network(&pnetwork->network, target, adapter);
+ pnetwork->last_scanned = jiffies;
+ }
+}
+
+static void rtl8711_add_network(struct _adapter *adapter,
+ struct ndis_wlan_bssid_ex *pnetwork)
+{
+ unsigned long irqL;
+ struct mlme_priv *pmlmepriv = &(((struct _adapter *)adapter)->mlmepriv);
+ struct __queue *queue = &pmlmepriv->scanned_queue;
+
+ spin_lock_irqsave(&queue->lock, irqL);
+ update_current_network(adapter, pnetwork);
+ update_scanned_network(adapter, pnetwork);
+ spin_unlock_irqrestore(&queue->lock, irqL);
+}
+
+/*select the desired network based on the capability of the (i)bss.
+ * check items: (1) security
+ * (2) network_type
+ * (3) WMM
+ * (4) HT
+ * (5) others
+ */
+static int is_desired_network(struct _adapter *adapter,
+ struct wlan_network *pnetwork)
+{
+ u8 wps_ie[512];
+ uint wps_ielen;
+ int bselected = true;
+ struct security_priv *psecuritypriv = &adapter->securitypriv;
+
+ if (psecuritypriv->wps_phase == true) {
+ if (r8712_get_wps_ie(pnetwork->network.IEs,
+ pnetwork->network.IELength, wps_ie,
+ &wps_ielen) == true)
+ return true;
+ else
+ return false;
+ }
+ if ((psecuritypriv->PrivacyAlgrthm != _NO_PRIVACY_) &&
+ (pnetwork->network.Privacy == 0))
+ bselected = false;
+ if (check_fwstate(&adapter->mlmepriv, WIFI_ADHOC_STATE) == true) {
+ if (pnetwork->network.InfrastructureMode !=
+ adapter->mlmepriv.cur_network.network.
+ InfrastructureMode)
+ bselected = false;
+ }
+ return bselected;
+}
+
+/* TODO: Perry : For Power Management */
+void r8712_atimdone_event_callback(struct _adapter *adapter, u8 *pbuf)
+{
+}
+
+void r8712_survey_event_callback(struct _adapter *adapter, u8 *pbuf)
+{
+ unsigned long flags;
+ u32 len;
+ struct ndis_wlan_bssid_ex *pnetwork;
+ struct mlme_priv *pmlmepriv = &adapter->mlmepriv;
+
+ pnetwork = (struct ndis_wlan_bssid_ex *)pbuf;
+#ifdef __BIG_ENDIAN
+ /* endian_convert */
+ pnetwork->Length = le32_to_cpu(pnetwork->Length);
+ pnetwork->Ssid.SsidLength = le32_to_cpu(pnetwork->Ssid.SsidLength);
+ pnetwork->Privacy = le32_to_cpu(pnetwork->Privacy);
+ pnetwork->Rssi = le32_to_cpu(pnetwork->Rssi);
+ pnetwork->NetworkTypeInUse = le32_to_cpu(pnetwork->NetworkTypeInUse);
+ pnetwork->Configuration.ATIMWindow =
+ le32_to_cpu(pnetwork->Configuration.ATIMWindow);
+ pnetwork->Configuration.BeaconPeriod =
+ le32_to_cpu(pnetwork->Configuration.BeaconPeriod);
+ pnetwork->Configuration.DSConfig =
+ le32_to_cpu(pnetwork->Configuration.DSConfig);
+ pnetwork->Configuration.FHConfig.DwellTime =
+ le32_to_cpu(pnetwork->Configuration.FHConfig.DwellTime);
+ pnetwork->Configuration.FHConfig.HopPattern =
+ le32_to_cpu(pnetwork->Configuration.FHConfig.HopPattern);
+ pnetwork->Configuration.FHConfig.HopSet =
+ le32_to_cpu(pnetwork->Configuration.FHConfig.HopSet);
+ pnetwork->Configuration.FHConfig.Length =
+ le32_to_cpu(pnetwork->Configuration.FHConfig.Length);
+ pnetwork->Configuration.Length =
+ le32_to_cpu(pnetwork->Configuration.Length);
+ pnetwork->InfrastructureMode =
+ le32_to_cpu(pnetwork->InfrastructureMode);
+ pnetwork->IELength = le32_to_cpu(pnetwork->IELength);
+#endif
+ len = r8712_get_ndis_wlan_bssid_ex_sz(pnetwork);
+ if (len > sizeof(struct wlan_bssid_ex))
+ return;
+ spin_lock_irqsave(&pmlmepriv->lock2, flags);
+ /* update IBSS_network 's timestamp */
+ if (check_fwstate(pmlmepriv, WIFI_ADHOC_MASTER_STATE) == true) {
+ if (!memcmp(&(pmlmepriv->cur_network.network.MacAddress),
+ pnetwork->MacAddress, ETH_ALEN)) {
+ struct wlan_network *ibss_wlan = NULL;
+
+ memcpy(pmlmepriv->cur_network.network.IEs,
+ pnetwork->IEs, 8);
+ ibss_wlan = r8712_find_network(
+ &pmlmepriv->scanned_queue,
+ pnetwork->MacAddress);
+ if (ibss_wlan) {
+ memcpy(ibss_wlan->network.IEs,
+ pnetwork->IEs, 8);
+ goto exit;
+ }
+ }
+ }
+ /* lock pmlmepriv->lock when you accessing network_q */
+ if (check_fwstate(pmlmepriv, _FW_UNDER_LINKING) == false) {
+ if (pnetwork->Ssid.Ssid[0] != 0)
+ rtl8711_add_network(adapter, pnetwork);
+ else {
+ pnetwork->Ssid.SsidLength = 8;
+ memcpy(pnetwork->Ssid.Ssid, "<hidden>", 8);
+ rtl8711_add_network(adapter, pnetwork);
+ }
+ }
+exit:
+ spin_unlock_irqrestore(&pmlmepriv->lock2, flags);
+}
+
+void r8712_surveydone_event_callback(struct _adapter *adapter, u8 *pbuf)
+{
+ unsigned long irqL;
+ struct mlme_priv *pmlmepriv = &adapter->mlmepriv;
+
+ spin_lock_irqsave(&pmlmepriv->lock, irqL);
+
+ if (check_fwstate(pmlmepriv, _FW_UNDER_SURVEY) == true) {
+ del_timer(&pmlmepriv->scan_to_timer);
+
+ _clr_fwstate_(pmlmepriv, _FW_UNDER_SURVEY);
+ }
+
+ if (pmlmepriv->to_join == true) {
+ if (check_fwstate(pmlmepriv, WIFI_ADHOC_STATE) == true) {
+ if (check_fwstate(pmlmepriv, _FW_LINKED) == false) {
+ set_fwstate(pmlmepriv, _FW_UNDER_LINKING);
+
+ if (r8712_select_and_join_from_scan(pmlmepriv)
+ == _SUCCESS)
+ mod_timer(&pmlmepriv->assoc_timer, jiffies +
+ msecs_to_jiffies(MAX_JOIN_TIMEOUT));
+ else {
+ struct wlan_bssid_ex *pdev_network =
+ &(adapter->registrypriv.dev_network);
+ u8 *pibss =
+ adapter->registrypriv.
+ dev_network.MacAddress;
+ pmlmepriv->fw_state ^= _FW_UNDER_SURVEY;
+ memcpy(&pdev_network->Ssid,
+ &pmlmepriv->assoc_ssid,
+ sizeof(struct
+ ndis_802_11_ssid));
+ r8712_update_registrypriv_dev_network
+ (adapter);
+ r8712_generate_random_ibss(pibss);
+ pmlmepriv->fw_state =
+ WIFI_ADHOC_MASTER_STATE;
+ pmlmepriv->to_join = false;
+ }
+ }
+ } else {
+ pmlmepriv->to_join = false;
+ set_fwstate(pmlmepriv, _FW_UNDER_LINKING);
+ if (r8712_select_and_join_from_scan(pmlmepriv) ==
+ _SUCCESS)
+ mod_timer(&pmlmepriv->assoc_timer, jiffies +
+ msecs_to_jiffies(MAX_JOIN_TIMEOUT));
+ else
+ _clr_fwstate_(pmlmepriv, _FW_UNDER_LINKING);
+ }
+ }
+ spin_unlock_irqrestore(&pmlmepriv->lock, irqL);
+}
+
+/*
+ *r8712_free_assoc_resources: the caller has to lock pmlmepriv->lock
+ */
+void r8712_free_assoc_resources(struct _adapter *adapter)
+{
+ unsigned long irqL;
+ struct wlan_network *pwlan = NULL;
+ struct mlme_priv *pmlmepriv = &adapter->mlmepriv;
+ struct sta_priv *pstapriv = &adapter->stapriv;
+ struct wlan_network *tgt_network = &pmlmepriv->cur_network;
+
+ pwlan = r8712_find_network(&pmlmepriv->scanned_queue,
+ tgt_network->network.MacAddress);
+
+ if (check_fwstate(pmlmepriv, WIFI_STATION_STATE|WIFI_AP_STATE)) {
+ struct sta_info *psta;
+
+ psta = r8712_get_stainfo(&adapter->stapriv,
+ tgt_network->network.MacAddress);
+
+ spin_lock_irqsave(&pstapriv->sta_hash_lock, irqL);
+ r8712_free_stainfo(adapter, psta);
+ spin_unlock_irqrestore(&pstapriv->sta_hash_lock, irqL);
+ }
+
+ if (check_fwstate(pmlmepriv,
+ WIFI_ADHOC_STATE|WIFI_ADHOC_MASTER_STATE|WIFI_AP_STATE))
+ r8712_free_all_stainfo(adapter);
+ if (pwlan)
+ pwlan->fixed = false;
+
+ if (((check_fwstate(pmlmepriv, WIFI_ADHOC_MASTER_STATE)) &&
+ (adapter->stapriv.asoc_sta_count == 1)))
+ free_network_nolock(pmlmepriv, pwlan);
+}
+
+/*
+*r8712_indicate_connect: the caller has to lock pmlmepriv->lock
+*/
+void r8712_indicate_connect(struct _adapter *padapter)
+{
+ struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
+
+ pmlmepriv->to_join = false;
+ set_fwstate(pmlmepriv, _FW_LINKED);
+ padapter->ledpriv.LedControlHandler(padapter, LED_CTL_LINK);
+ r8712_os_indicate_connect(padapter);
+ if (padapter->registrypriv.power_mgnt > PS_MODE_ACTIVE)
+ mod_timer(&pmlmepriv->dhcp_timer,
+ jiffies + msecs_to_jiffies(60000));
+}
+
+
+/*
+*r8712_ind_disconnect: the caller has to lock pmlmepriv->lock
+*/
+void r8712_ind_disconnect(struct _adapter *padapter)
+{
+ struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
+
+ if (check_fwstate(pmlmepriv, _FW_LINKED) == true) {
+ _clr_fwstate_(pmlmepriv, _FW_LINKED);
+ padapter->ledpriv.LedControlHandler(padapter, LED_CTL_NO_LINK);
+ r8712_os_indicate_disconnect(padapter);
+ }
+ if (padapter->pwrctrlpriv.pwr_mode !=
+ padapter->registrypriv.power_mgnt) {
+ del_timer(&pmlmepriv->dhcp_timer);
+ r8712_set_ps_mode(padapter, padapter->registrypriv.power_mgnt,
+ padapter->registrypriv.smart_ps);
+ }
+}
+
+/*Notes:
+ *pnetwork : returns from r8712_joinbss_event_callback
+ *ptarget_wlan: found from scanned_queue
+ *if join_res > 0, for (fw_state==WIFI_STATION_STATE), we check if
+ * "ptarget_sta" & "ptarget_wlan" exist.
+ *if join_res > 0, for (fw_state==WIFI_ADHOC_STATE), we only check
+ * if "ptarget_wlan" exist.
+ *if join_res > 0, update "cur_network->network" from
+ * "pnetwork->network" if (ptarget_wlan !=NULL).
+ */
+void r8712_joinbss_event_callback(struct _adapter *adapter, u8 *pbuf)
+{
+ unsigned long irqL = 0, irqL2;
+ struct sta_info *ptarget_sta = NULL, *pcur_sta = NULL;
+ struct sta_priv *pstapriv = &adapter->stapriv;
+ struct mlme_priv *pmlmepriv = &adapter->mlmepriv;
+ struct wlan_network *cur_network = &pmlmepriv->cur_network;
+ struct wlan_network *pcur_wlan = NULL, *ptarget_wlan = NULL;
+ unsigned int the_same_macaddr = false;
+ struct wlan_network *pnetwork;
+
+ if (sizeof(struct list_head) == 4 * sizeof(u32)) {
+ pnetwork = kmalloc(sizeof(struct wlan_network), GFP_ATOMIC);
+ if (!pnetwork)
+ return;
+ memcpy((u8 *)pnetwork+16, (u8 *)pbuf + 8,
+ sizeof(struct wlan_network) - 16);
+ } else
+ pnetwork = (struct wlan_network *)pbuf;
+
+#ifdef __BIG_ENDIAN
+ /* endian_convert */
+ pnetwork->join_res = le32_to_cpu(pnetwork->join_res);
+ pnetwork->network_type = le32_to_cpu(pnetwork->network_type);
+ pnetwork->network.Length = le32_to_cpu(pnetwork->network.Length);
+ pnetwork->network.Ssid.SsidLength =
+ le32_to_cpu(pnetwork->network.Ssid.SsidLength);
+ pnetwork->network.Privacy = le32_to_cpu(pnetwork->network.Privacy);
+ pnetwork->network.Rssi = le32_to_cpu(pnetwork->network.Rssi);
+ pnetwork->network.NetworkTypeInUse =
+ le32_to_cpu(pnetwork->network.NetworkTypeInUse);
+ pnetwork->network.Configuration.ATIMWindow =
+ le32_to_cpu(pnetwork->network.Configuration.ATIMWindow);
+ pnetwork->network.Configuration.BeaconPeriod =
+ le32_to_cpu(pnetwork->network.Configuration.BeaconPeriod);
+ pnetwork->network.Configuration.DSConfig =
+ le32_to_cpu(pnetwork->network.Configuration.DSConfig);
+ pnetwork->network.Configuration.FHConfig.DwellTime =
+ le32_to_cpu(pnetwork->network.Configuration.FHConfig.
+ DwellTime);
+ pnetwork->network.Configuration.FHConfig.HopPattern =
+ le32_to_cpu(pnetwork->network.Configuration.
+ FHConfig.HopPattern);
+ pnetwork->network.Configuration.FHConfig.HopSet =
+ le32_to_cpu(pnetwork->network.Configuration.FHConfig.HopSet);
+ pnetwork->network.Configuration.FHConfig.Length =
+ le32_to_cpu(pnetwork->network.Configuration.FHConfig.Length);
+ pnetwork->network.Configuration.Length =
+ le32_to_cpu(pnetwork->network.Configuration.Length);
+ pnetwork->network.InfrastructureMode =
+ le32_to_cpu(pnetwork->network.InfrastructureMode);
+ pnetwork->network.IELength = le32_to_cpu(pnetwork->network.IELength);
+#endif
+
+ the_same_macaddr = !memcmp(pnetwork->network.MacAddress,
+ cur_network->network.MacAddress, ETH_ALEN);
+ pnetwork->network.Length =
+ r8712_get_ndis_wlan_bssid_ex_sz(&pnetwork->network);
+ spin_lock_irqsave(&pmlmepriv->lock, irqL);
+ if (pnetwork->network.Length > sizeof(struct wlan_bssid_ex))
+ goto ignore_joinbss_callback;
+ if (pnetwork->join_res > 0) {
+ if (check_fwstate(pmlmepriv, _FW_UNDER_LINKING) == true) {
+ /*s1. find ptarget_wlan*/
+ if (check_fwstate(pmlmepriv, _FW_LINKED) == true) {
+ if (the_same_macaddr == true)
+ ptarget_wlan =
+ r8712_find_network(&pmlmepriv->
+ scanned_queue,
+ cur_network->network.MacAddress);
+ else {
+ pcur_wlan =
+ r8712_find_network(&pmlmepriv->
+ scanned_queue,
+ cur_network->network.MacAddress);
+ pcur_wlan->fixed = false;
+
+ pcur_sta = r8712_get_stainfo(pstapriv,
+ cur_network->network.MacAddress);
+ spin_lock_irqsave(&pstapriv->
+ sta_hash_lock, irqL2);
+ r8712_free_stainfo(adapter, pcur_sta);
+ spin_unlock_irqrestore(&(pstapriv->
+ sta_hash_lock), irqL2);
+
+ ptarget_wlan =
+ r8712_find_network(&pmlmepriv->
+ scanned_queue,
+ pnetwork->network.
+ MacAddress);
+ if (ptarget_wlan)
+ ptarget_wlan->fixed = true;
+ }
+ } else {
+ ptarget_wlan = r8712_find_network(&pmlmepriv->
+ scanned_queue,
+ pnetwork->network.MacAddress);
+ if (ptarget_wlan)
+ ptarget_wlan->fixed = true;
+ }
+
+ if (ptarget_wlan == NULL) {
+ if (check_fwstate(pmlmepriv,
+ _FW_UNDER_LINKING))
+ pmlmepriv->fw_state ^=
+ _FW_UNDER_LINKING;
+ goto ignore_joinbss_callback;
+ }
+
+ /*s2. find ptarget_sta & update ptarget_sta*/
+ if (check_fwstate(pmlmepriv, WIFI_STATION_STATE)) {
+ if (the_same_macaddr == true) {
+ ptarget_sta =
+ r8712_get_stainfo(pstapriv,
+ pnetwork->network.MacAddress);
+ if (ptarget_sta == NULL)
+ ptarget_sta =
+ r8712_alloc_stainfo(pstapriv,
+ pnetwork->network.MacAddress);
+ } else
+ ptarget_sta =
+ r8712_alloc_stainfo(pstapriv,
+ pnetwork->network.MacAddress);
+ if (ptarget_sta) /*update ptarget_sta*/ {
+ ptarget_sta->aid = pnetwork->join_res;
+ ptarget_sta->qos_option = 1;
+ ptarget_sta->mac_id = 5;
+ if (adapter->securitypriv.
+ AuthAlgrthm == 2) {
+ adapter->securitypriv.
+ binstallGrpkey =
+ false;
+ adapter->securitypriv.
+ busetkipkey =
+ false;
+ adapter->securitypriv.
+ bgrpkey_handshake =
+ false;
+ ptarget_sta->ieee8021x_blocked
+ = true;
+ ptarget_sta->XPrivacy =
+ adapter->securitypriv.
+ PrivacyAlgrthm;
+ memset((u8 *)&ptarget_sta->
+ x_UncstKey,
+ 0,
+ sizeof(union Keytype));
+ memset((u8 *)&ptarget_sta->
+ tkiprxmickey,
+ 0,
+ sizeof(union Keytype));
+ memset((u8 *)&ptarget_sta->
+ tkiptxmickey,
+ 0,
+ sizeof(union Keytype));
+ memset((u8 *)&ptarget_sta->
+ txpn, 0,
+ sizeof(union pn48));
+ memset((u8 *)&ptarget_sta->
+ rxpn, 0,
+ sizeof(union pn48));
+ }
+ } else {
+ if (check_fwstate(pmlmepriv,
+ _FW_UNDER_LINKING))
+ pmlmepriv->fw_state ^=
+ _FW_UNDER_LINKING;
+ goto ignore_joinbss_callback;
+ }
+ }
+
+ /*s3. update cur_network & indicate connect*/
+ memcpy(&cur_network->network, &pnetwork->network,
+ pnetwork->network.Length);
+ cur_network->aid = pnetwork->join_res;
+ /*update fw_state will clr _FW_UNDER_LINKING*/
+ switch (pnetwork->network.InfrastructureMode) {
+ case Ndis802_11Infrastructure:
+ pmlmepriv->fw_state = WIFI_STATION_STATE;
+ break;
+ case Ndis802_11IBSS:
+ pmlmepriv->fw_state = WIFI_ADHOC_STATE;
+ break;
+ default:
+ pmlmepriv->fw_state = WIFI_NULL_STATE;
+ break;
+ }
+ r8712_update_protection(adapter,
+ (cur_network->network.IEs) +
+ sizeof(struct NDIS_802_11_FIXED_IEs),
+ (cur_network->network.IELength));
+ /*TODO: update HT_Capability*/
+ update_ht_cap(adapter, cur_network->network.IEs,
+ cur_network->network.IELength);
+ /*indicate connect*/
+ if (check_fwstate(pmlmepriv, WIFI_STATION_STATE)
+ == true)
+ r8712_indicate_connect(adapter);
+ del_timer(&pmlmepriv->assoc_timer);
+ } else
+ goto ignore_joinbss_callback;
+ } else {
+ if (check_fwstate(pmlmepriv, _FW_UNDER_LINKING) == true) {
+ mod_timer(&pmlmepriv->assoc_timer,
+ jiffies + msecs_to_jiffies(1));
+ _clr_fwstate_(pmlmepriv, _FW_UNDER_LINKING);
+ }
+ }
+ignore_joinbss_callback:
+ spin_unlock_irqrestore(&pmlmepriv->lock, irqL);
+ if (sizeof(struct list_head) == 4 * sizeof(u32))
+ kfree(pnetwork);
+}
+
+void r8712_stassoc_event_callback(struct _adapter *adapter, u8 *pbuf)
+{
+ unsigned long irqL;
+ struct sta_info *psta;
+ struct mlme_priv *pmlmepriv = &(adapter->mlmepriv);
+ struct stassoc_event *pstassoc = (struct stassoc_event *)pbuf;
+
+ /* to do: */
+ if (r8712_access_ctrl(&adapter->acl_list, pstassoc->macaddr) == false)
+ return;
+ psta = r8712_get_stainfo(&adapter->stapriv, pstassoc->macaddr);
+ if (psta != NULL) {
+ /*the sta have been in sta_info_queue => do nothing
+ *(between drv has received this event before and
+ * fw have not yet to set key to CAM_ENTRY) */
+ return;
+ }
+
+ psta = r8712_alloc_stainfo(&adapter->stapriv, pstassoc->macaddr);
+ if (psta == NULL)
+ return;
+ /* to do : init sta_info variable */
+ psta->qos_option = 0;
+ psta->mac_id = le32_to_cpu((uint)pstassoc->cam_id);
+ /* psta->aid = (uint)pstassoc->cam_id; */
+
+ if (adapter->securitypriv.AuthAlgrthm == 2)
+ psta->XPrivacy = adapter->securitypriv.PrivacyAlgrthm;
+ psta->ieee8021x_blocked = false;
+ spin_lock_irqsave(&pmlmepriv->lock, irqL);
+ if ((check_fwstate(pmlmepriv, WIFI_ADHOC_MASTER_STATE) == true) ||
+ (check_fwstate(pmlmepriv, WIFI_ADHOC_STATE) == true)) {
+ if (adapter->stapriv.asoc_sta_count == 2) {
+ /* a sta + bc/mc_stainfo (not Ibss_stainfo) */
+ r8712_indicate_connect(adapter);
+ }
+ }
+ spin_unlock_irqrestore(&pmlmepriv->lock, irqL);
+}
+
+void r8712_stadel_event_callback(struct _adapter *adapter, u8 *pbuf)
+{
+ unsigned long irqL, irqL2;
+ struct sta_info *psta;
+ struct wlan_network *pwlan = NULL;
+ struct wlan_bssid_ex *pdev_network = NULL;
+ u8 *pibss = NULL;
+ struct mlme_priv *pmlmepriv = &adapter->mlmepriv;
+ struct stadel_event *pstadel = (struct stadel_event *)pbuf;
+ struct sta_priv *pstapriv = &adapter->stapriv;
+ struct wlan_network *tgt_network = &pmlmepriv->cur_network;
+
+ spin_lock_irqsave(&pmlmepriv->lock, irqL2);
+ if (check_fwstate(pmlmepriv, WIFI_STATION_STATE) == true) {
+ r8712_ind_disconnect(adapter);
+ r8712_free_assoc_resources(adapter);
+ }
+ if (check_fwstate(pmlmepriv, WIFI_ADHOC_MASTER_STATE |
+ WIFI_ADHOC_STATE)) {
+ psta = r8712_get_stainfo(&adapter->stapriv, pstadel->macaddr);
+ spin_lock_irqsave(&pstapriv->sta_hash_lock, irqL);
+ r8712_free_stainfo(adapter, psta);
+ spin_unlock_irqrestore(&pstapriv->sta_hash_lock, irqL);
+ if (adapter->stapriv.asoc_sta_count == 1) {
+ /*a sta + bc/mc_stainfo (not Ibss_stainfo) */
+ pwlan = r8712_find_network(&pmlmepriv->scanned_queue,
+ tgt_network->network.MacAddress);
+ if (pwlan) {
+ pwlan->fixed = false;
+ free_network_nolock(pmlmepriv, pwlan);
+ }
+ /*re-create ibss*/
+ pdev_network = &(adapter->registrypriv.dev_network);
+ pibss = adapter->registrypriv.dev_network.MacAddress;
+ memcpy(pdev_network, &tgt_network->network,
+ r8712_get_ndis_wlan_bssid_ex_sz(&tgt_network->
+ network));
+ memcpy(&pdev_network->Ssid,
+ &pmlmepriv->assoc_ssid,
+ sizeof(struct ndis_802_11_ssid));
+ r8712_update_registrypriv_dev_network(adapter);
+ r8712_generate_random_ibss(pibss);
+ if (check_fwstate(pmlmepriv, WIFI_ADHOC_STATE)) {
+ _clr_fwstate_(pmlmepriv, WIFI_ADHOC_STATE);
+ set_fwstate(pmlmepriv, WIFI_ADHOC_MASTER_STATE);
+ }
+ }
+ }
+ spin_unlock_irqrestore(&pmlmepriv->lock, irqL2);
+}
+
+void r8712_cpwm_event_callback(struct _adapter *adapter, u8 *pbuf)
+{
+ struct reportpwrstate_parm *preportpwrstate =
+ (struct reportpwrstate_parm *)pbuf;
+
+ preportpwrstate->state |= (u8)(adapter->pwrctrlpriv.cpwm_tog + 0x80);
+ r8712_cpwm_int_hdl(adapter, preportpwrstate);
+}
+
+/* When the Netgear 3500 AP is with WPA2PSK-AES mode, it will send
+ * the ADDBA req frame with start seq control = 0 to wifi client after
+ * the WPA handshake and the seqence number of following data packet
+ * will be 0. In this case, the Rx reorder sequence is not longer than 0
+ * and the WiFi client will drop the data with seq number 0.
+ * So, the 8712 firmware has to inform driver with receiving the
+ * ADDBA-Req frame so that the driver can reset the
+ * sequence value of Rx reorder control.
+ */
+void r8712_got_addbareq_event_callback(struct _adapter *adapter, u8 *pbuf)
+{
+ struct ADDBA_Req_Report_parm *pAddbareq_pram =
+ (struct ADDBA_Req_Report_parm *)pbuf;
+ struct sta_info *psta;
+ struct sta_priv *pstapriv = &adapter->stapriv;
+ struct recv_reorder_ctrl *precvreorder_ctrl = NULL;
+
+ psta = r8712_get_stainfo(pstapriv, pAddbareq_pram->MacAddress);
+ if (psta) {
+ precvreorder_ctrl =
+ &psta->recvreorder_ctrl[pAddbareq_pram->tid];
+ /* set the indicate_seq to 0xffff so that the rx reorder
+ * can store any following data packet.
+ */
+ precvreorder_ctrl->indicate_seq = 0xffff;
+ }
+}
+
+void r8712_wpspbc_event_callback(struct _adapter *adapter, u8 *pbuf)
+{
+ if (adapter->securitypriv.wps_hw_pbc_pressed == false)
+ adapter->securitypriv.wps_hw_pbc_pressed = true;
+}
+
+void _r8712_sitesurvey_ctrl_handler(struct _adapter *adapter)
+{
+ struct mlme_priv *pmlmepriv = &adapter->mlmepriv;
+ struct sitesurvey_ctrl *psitesurveyctrl = &pmlmepriv->sitesurveyctrl;
+ struct registry_priv *pregistrypriv = &adapter->registrypriv;
+ u64 current_tx_pkts;
+ uint current_rx_pkts;
+
+ current_tx_pkts = (adapter->xmitpriv.tx_pkts) -
+ (psitesurveyctrl->last_tx_pkts);
+ current_rx_pkts = (adapter->recvpriv.rx_pkts) -
+ (psitesurveyctrl->last_rx_pkts);
+ psitesurveyctrl->last_tx_pkts = adapter->xmitpriv.tx_pkts;
+ psitesurveyctrl->last_rx_pkts = adapter->recvpriv.rx_pkts;
+ if ((current_tx_pkts > pregistrypriv->busy_thresh) ||
+ (current_rx_pkts > pregistrypriv->busy_thresh))
+ psitesurveyctrl->traffic_busy = true;
+ else
+ psitesurveyctrl->traffic_busy = false;
+}
+
+void _r8712_join_timeout_handler(struct _adapter *adapter)
+{
+ unsigned long irqL;
+ struct mlme_priv *pmlmepriv = &adapter->mlmepriv;
+
+ if (adapter->bDriverStopped || adapter->bSurpriseRemoved)
+ return;
+ spin_lock_irqsave(&pmlmepriv->lock, irqL);
+ _clr_fwstate_(pmlmepriv, _FW_UNDER_LINKING);
+ pmlmepriv->to_join = false;
+ if (check_fwstate(pmlmepriv, _FW_LINKED) == true) {
+ r8712_os_indicate_disconnect(adapter);
+ _clr_fwstate_(pmlmepriv, _FW_LINKED);
+ }
+ if (adapter->pwrctrlpriv.pwr_mode != adapter->registrypriv.power_mgnt) {
+ r8712_set_ps_mode(adapter, adapter->registrypriv.power_mgnt,
+ adapter->registrypriv.smart_ps);
+ }
+ spin_unlock_irqrestore(&pmlmepriv->lock, irqL);
+}
+
+void r8712_scan_timeout_handler (struct _adapter *adapter)
+{
+ unsigned long irqL;
+ struct mlme_priv *pmlmepriv = &adapter->mlmepriv;
+
+ spin_lock_irqsave(&pmlmepriv->lock, irqL);
+ _clr_fwstate_(pmlmepriv, _FW_UNDER_SURVEY);
+ pmlmepriv->to_join = false; /* scan fail, so clear to_join flag */
+ spin_unlock_irqrestore(&pmlmepriv->lock, irqL);
+}
+
+void _r8712_dhcp_timeout_handler (struct _adapter *adapter)
+{
+ if (adapter->bDriverStopped || adapter->bSurpriseRemoved)
+ return;
+ if (adapter->pwrctrlpriv.pwr_mode != adapter->registrypriv.power_mgnt)
+ r8712_set_ps_mode(adapter, adapter->registrypriv.power_mgnt,
+ adapter->registrypriv.smart_ps);
+}
+
+void _r8712_wdg_timeout_handler(struct _adapter *adapter)
+{
+ r8712_wdg_wk_cmd(adapter);
+}
+
+int r8712_select_and_join_from_scan(struct mlme_priv *pmlmepriv)
+{
+ struct list_head *phead;
+ unsigned char *dst_ssid, *src_ssid;
+ struct _adapter *adapter;
+ struct __queue *queue = NULL;
+ struct wlan_network *pnetwork = NULL;
+ struct wlan_network *pnetwork_max_rssi = NULL;
+
+ adapter = (struct _adapter *)pmlmepriv->nic_hdl;
+ queue = &pmlmepriv->scanned_queue;
+ phead = &queue->queue;
+ pmlmepriv->pscanned = phead->next;
+ while (1) {
+ if (end_of_queue_search(phead, pmlmepriv->pscanned) == true) {
+ if ((pmlmepriv->assoc_by_rssi == true) &&
+ (pnetwork_max_rssi != NULL)) {
+ pnetwork = pnetwork_max_rssi;
+ goto ask_for_joinbss;
+ }
+ return _FAIL;
+ }
+ pnetwork = LIST_CONTAINOR(pmlmepriv->pscanned,
+ struct wlan_network, list);
+ if (pnetwork == NULL)
+ return _FAIL;
+ pmlmepriv->pscanned = pmlmepriv->pscanned->next;
+ if (pmlmepriv->assoc_by_bssid == true) {
+ dst_ssid = pnetwork->network.MacAddress;
+ src_ssid = pmlmepriv->assoc_bssid;
+ if (!memcmp(dst_ssid, src_ssid, ETH_ALEN)) {
+ if (check_fwstate(pmlmepriv, _FW_LINKED)) {
+ if (is_same_network(&pmlmepriv->
+ cur_network.network,
+ &pnetwork->network)) {
+ _clr_fwstate_(pmlmepriv,
+ _FW_UNDER_LINKING);
+ /*r8712_indicate_connect again*/
+ r8712_indicate_connect(adapter);
+ return 2;
+ }
+ r8712_disassoc_cmd(adapter);
+ r8712_ind_disconnect(adapter);
+ r8712_free_assoc_resources(adapter);
+ }
+ goto ask_for_joinbss;
+ }
+ } else if (pmlmepriv->assoc_ssid.SsidLength == 0)
+ goto ask_for_joinbss;
+ dst_ssid = pnetwork->network.Ssid.Ssid;
+ src_ssid = pmlmepriv->assoc_ssid.Ssid;
+ if ((pnetwork->network.Ssid.SsidLength ==
+ pmlmepriv->assoc_ssid.SsidLength) &&
+ (!memcmp(dst_ssid, src_ssid,
+ pmlmepriv->assoc_ssid.SsidLength))) {
+ if (pmlmepriv->assoc_by_rssi == true) {
+ /* if the ssid is the same, select the bss
+ * which has the max rssi*/
+ if (pnetwork_max_rssi) {
+ if (pnetwork->network.Rssi >
+ pnetwork_max_rssi->network.Rssi)
+ pnetwork_max_rssi = pnetwork;
+ } else
+ pnetwork_max_rssi = pnetwork;
+ } else if (is_desired_network(adapter, pnetwork)) {
+ if (check_fwstate(pmlmepriv, _FW_LINKED)) {
+ r8712_disassoc_cmd(adapter);
+ r8712_free_assoc_resources(adapter);
+ }
+ goto ask_for_joinbss;
+ }
+ }
+ }
+ return _FAIL;
+ask_for_joinbss:
+ return r8712_joinbss_cmd(adapter, pnetwork);
+}
+
+sint r8712_set_auth(struct _adapter *adapter,
+ struct security_priv *psecuritypriv)
+{
+ struct cmd_priv *pcmdpriv = &adapter->cmdpriv;
+ struct cmd_obj *pcmd;
+ struct setauth_parm *psetauthparm;
+
+ pcmd = kmalloc(sizeof(*pcmd), GFP_ATOMIC);
+ if (pcmd == NULL)
+ return _FAIL;
+
+ psetauthparm = kzalloc(sizeof(*psetauthparm), GFP_ATOMIC);
+ if (psetauthparm == NULL) {
+ kfree(pcmd);
+ return _FAIL;
+ }
+ psetauthparm->mode = (u8)psecuritypriv->AuthAlgrthm;
+ pcmd->cmdcode = _SetAuth_CMD_;
+ pcmd->parmbuf = (unsigned char *)psetauthparm;
+ pcmd->cmdsz = sizeof(struct setauth_parm);
+ pcmd->rsp = NULL;
+ pcmd->rspsz = 0;
+ INIT_LIST_HEAD(&pcmd->list);
+ r8712_enqueue_cmd(pcmdpriv, pcmd);
+ return _SUCCESS;
+}
+
+sint r8712_set_key(struct _adapter *adapter,
+ struct security_priv *psecuritypriv,
+ sint keyid)
+{
+ struct cmd_priv *pcmdpriv = &adapter->cmdpriv;
+ struct cmd_obj *pcmd;
+ struct setkey_parm *psetkeyparm;
+ u8 keylen;
+ sint ret = _SUCCESS;
+
+ pcmd = kmalloc(sizeof(*pcmd), GFP_ATOMIC);
+ if (pcmd == NULL)
+ return _FAIL;
+ psetkeyparm = kzalloc(sizeof(*psetkeyparm), GFP_ATOMIC);
+ if (psetkeyparm == NULL) {
+ ret = _FAIL;
+ goto err_free_cmd;
+ }
+ if (psecuritypriv->AuthAlgrthm == 2) { /* 802.1X */
+ psetkeyparm->algorithm =
+ (u8)psecuritypriv->XGrpPrivacy;
+ } else { /* WEP */
+ psetkeyparm->algorithm =
+ (u8)psecuritypriv->PrivacyAlgrthm;
+ }
+ psetkeyparm->keyid = (u8)keyid;
+
+ switch (psetkeyparm->algorithm) {
+ case _WEP40_:
+ keylen = 5;
+ memcpy(psetkeyparm->key,
+ psecuritypriv->DefKey[keyid].skey, keylen);
+ break;
+ case _WEP104_:
+ keylen = 13;
+ memcpy(psetkeyparm->key,
+ psecuritypriv->DefKey[keyid].skey, keylen);
+ break;
+ case _TKIP_:
+ if (keyid < 1 || keyid > 2) {
+ ret = _FAIL;
+ goto err_free_parm;
+ }
+ keylen = 16;
+ memcpy(psetkeyparm->key,
+ &psecuritypriv->XGrpKey[keyid - 1], keylen);
+ psetkeyparm->grpkey = 1;
+ break;
+ case _AES_:
+ if (keyid < 1 || keyid > 2) {
+ ret = _FAIL;
+ goto err_free_parm;
+ }
+ keylen = 16;
+ memcpy(psetkeyparm->key,
+ &psecuritypriv->XGrpKey[keyid - 1], keylen);
+ psetkeyparm->grpkey = 1;
+ break;
+ default:
+ ret = _FAIL;
+ goto err_free_parm;
+ }
+ pcmd->cmdcode = _SetKey_CMD_;
+ pcmd->parmbuf = (u8 *)psetkeyparm;
+ pcmd->cmdsz = (sizeof(struct setkey_parm));
+ pcmd->rsp = NULL;
+ pcmd->rspsz = 0;
+ INIT_LIST_HEAD(&pcmd->list);
+ r8712_enqueue_cmd(pcmdpriv, pcmd);
+ return ret;
+
+err_free_parm:
+ kfree(psetkeyparm);
+err_free_cmd:
+ kfree(pcmd);
+ return ret;
+}
+
+/* adjust IEs for r8712_joinbss_cmd in WMM */
+int r8712_restruct_wmm_ie(struct _adapter *adapter, u8 *in_ie, u8 *out_ie,
+ uint in_len, uint initial_out_len)
+{
+ unsigned int ielength = 0;
+ unsigned int i, j;
+
+ i = 12; /* after the fixed IE */
+ while (i < in_len) {
+ ielength = initial_out_len;
+ if (in_ie[i] == 0xDD && in_ie[i + 2] == 0x00 &&
+ in_ie[i + 3] == 0x50 && in_ie[i + 4] == 0xF2 &&
+ in_ie[i + 5] == 0x02 && i + 5 < in_len) {
+ /*WMM element ID and OUI*/
+ for (j = i; j < i + 9; j++) {
+ out_ie[ielength] = in_ie[j];
+ ielength++;
+ }
+ out_ie[initial_out_len + 1] = 0x07;
+ out_ie[initial_out_len + 6] = 0x00;
+ out_ie[initial_out_len + 8] = 0x00;
+ break;
+ }
+ i += (in_ie[i + 1] + 2); /* to the next IE element */
+ }
+ return ielength;
+}
+
+/*
+ * Ported from 8185: IsInPreAuthKeyList().
+ *
+ * Search by BSSID,
+ * Return Value:
+ * -1 :if there is no pre-auth key in the table
+ * >=0 :if there is pre-auth key, and return the entry id
+ */
+static int SecIsInPMKIDList(struct _adapter *Adapter, u8 *bssid)
+{
+ struct security_priv *psecuritypriv = &Adapter->securitypriv;
+ int i = 0;
+
+ do {
+ if (psecuritypriv->PMKIDList[i].bUsed &&
+ (!memcmp(psecuritypriv->PMKIDList[i].Bssid,
+ bssid, ETH_ALEN)))
+ break;
+ else
+ i++;
+ } while (i < NUM_PMKID_CACHE);
+
+ if (i == NUM_PMKID_CACHE) {
+ i = -1; /* Could not find. */
+ } else {
+ ; /* There is one Pre-Authentication Key for the
+ * specific BSSID. */
+ }
+ return i;
+}
+
+sint r8712_restruct_sec_ie(struct _adapter *adapter, u8 *in_ie,
+ u8 *out_ie, uint in_len)
+{
+ u8 authmode = 0, match;
+ u8 sec_ie[255], uncst_oui[4], bkup_ie[255];
+ u8 wpa_oui[4] = {0x0, 0x50, 0xf2, 0x01};
+ uint ielength, cnt, remove_cnt;
+ int iEntry;
+ struct mlme_priv *pmlmepriv = &adapter->mlmepriv;
+ struct security_priv *psecuritypriv = &adapter->securitypriv;
+ uint ndisauthmode = psecuritypriv->ndisauthtype;
+ uint ndissecuritytype = psecuritypriv->ndisencryptstatus;
+
+ if ((ndisauthmode == Ndis802_11AuthModeWPA) ||
+ (ndisauthmode == Ndis802_11AuthModeWPAPSK)) {
+ authmode = _WPA_IE_ID_;
+ uncst_oui[0] = 0x0;
+ uncst_oui[1] = 0x50;
+ uncst_oui[2] = 0xf2;
+ }
+ if ((ndisauthmode == Ndis802_11AuthModeWPA2) ||
+ (ndisauthmode == Ndis802_11AuthModeWPA2PSK)) {
+ authmode = _WPA2_IE_ID_;
+ uncst_oui[0] = 0x0;
+ uncst_oui[1] = 0x0f;
+ uncst_oui[2] = 0xac;
+ }
+ switch (ndissecuritytype) {
+ case Ndis802_11Encryption1Enabled:
+ case Ndis802_11Encryption1KeyAbsent:
+ uncst_oui[3] = 0x1;
+ break;
+ case Ndis802_11Encryption2Enabled:
+ case Ndis802_11Encryption2KeyAbsent:
+ uncst_oui[3] = 0x2;
+ break;
+ case Ndis802_11Encryption3Enabled:
+ case Ndis802_11Encryption3KeyAbsent:
+ uncst_oui[3] = 0x4;
+ break;
+ default:
+ break;
+ }
+ /*Search required WPA or WPA2 IE and copy to sec_ie[] */
+ cnt = 12;
+ match = false;
+ while (cnt < in_len) {
+ if (in_ie[cnt] == authmode) {
+ if ((authmode == _WPA_IE_ID_) &&
+ (!memcmp(&in_ie[cnt+2], &wpa_oui[0], 4))) {
+ memcpy(&sec_ie[0], &in_ie[cnt],
+ in_ie[cnt + 1] + 2);
+ match = true;
+ break;
+ }
+ if (authmode == _WPA2_IE_ID_) {
+ memcpy(&sec_ie[0], &in_ie[cnt],
+ in_ie[cnt + 1] + 2);
+ match = true;
+ break;
+ }
+ if (((authmode == _WPA_IE_ID_) &&
+ (!memcmp(&in_ie[cnt + 2], &wpa_oui[0], 4))) ||
+ (authmode == _WPA2_IE_ID_))
+ memcpy(&bkup_ie[0], &in_ie[cnt],
+ in_ie[cnt + 1] + 2);
+ }
+ cnt += in_ie[cnt+1] + 2; /*get next*/
+ }
+ /*restruct WPA IE or WPA2 IE in sec_ie[] */
+ if (match == true) {
+ if (sec_ie[0] == _WPA_IE_ID_) {
+ /* parsing SSN IE to select required encryption
+ * algorithm, and set the bc/mc encryption algorithm */
+ while (true) {
+ /*check wpa_oui tag*/
+ if (memcmp(&sec_ie[2], &wpa_oui[0], 4)) {
+ match = false;
+ break;
+ }
+ if ((sec_ie[6] != 0x01) || (sec_ie[7] != 0x0)) {
+ /*IE Ver error*/
+ match = false;
+ break;
+ }
+ if (!memcmp(&sec_ie[8], &wpa_oui[0], 3)) {
+ /* get bc/mc encryption type (group
+ * key type)*/
+ switch (sec_ie[11]) {
+ case 0x0: /*none*/
+ psecuritypriv->XGrpPrivacy =
+ _NO_PRIVACY_;
+ break;
+ case 0x1: /*WEP_40*/
+ psecuritypriv->XGrpPrivacy =
+ _WEP40_;
+ break;
+ case 0x2: /*TKIP*/
+ psecuritypriv->XGrpPrivacy =
+ _TKIP_;
+ break;
+ case 0x3: /*AESCCMP*/
+ case 0x4:
+ psecuritypriv->XGrpPrivacy =
+ _AES_;
+ break;
+ case 0x5: /*WEP_104*/
+ psecuritypriv->XGrpPrivacy =
+ _WEP104_;
+ break;
+ }
+ } else {
+ match = false;
+ break;
+ }
+ if (sec_ie[12] == 0x01) {
+ /*check the unicast encryption type*/
+ if (memcmp(&sec_ie[14],
+ &uncst_oui[0], 4)) {
+ match = false;
+ break;
+
+ } /*else the uncst_oui is match*/
+ } else { /*mixed mode, unicast_enc_type > 1*/
+ /*select the uncst_oui and remove
+ * the other uncst_oui*/
+ cnt = sec_ie[12];
+ remove_cnt = (cnt-1) * 4;
+ sec_ie[12] = 0x01;
+ memcpy(&sec_ie[14], &uncst_oui[0], 4);
+ /*remove the other unicast suit*/
+ memcpy(&sec_ie[18],
+ &sec_ie[18 + remove_cnt],
+ sec_ie[1] - 18 + 2 -
+ remove_cnt);
+ sec_ie[1] = sec_ie[1] - remove_cnt;
+ }
+ break;
+ }
+ }
+ if (authmode == _WPA2_IE_ID_) {
+ /* parsing RSN IE to select required encryption
+ * algorithm, and set the bc/mc encryption algorithm */
+ while (true) {
+ if ((sec_ie[2] != 0x01) || (sec_ie[3] != 0x0)) {
+ /*IE Ver error*/
+ match = false;
+ break;
+ }
+ if (!memcmp(&sec_ie[4], &uncst_oui[0], 3)) {
+ /*get bc/mc encryption type*/
+ switch (sec_ie[7]) {
+ case 0x1: /*WEP_40*/
+ psecuritypriv->XGrpPrivacy =
+ _WEP40_;
+ break;
+ case 0x2: /*TKIP*/
+ psecuritypriv->XGrpPrivacy =
+ _TKIP_;
+ break;
+ case 0x4: /*AESWRAP*/
+ psecuritypriv->XGrpPrivacy =
+ _AES_;
+ break;
+ case 0x5: /*WEP_104*/
+ psecuritypriv->XGrpPrivacy =
+ _WEP104_;
+ break;
+ default: /*one*/
+ psecuritypriv->XGrpPrivacy =
+ _NO_PRIVACY_;
+ break;
+ }
+ } else {
+ match = false;
+ break;
+ }
+ if (sec_ie[8] == 0x01) {
+ /*check the unicast encryption type*/
+ if (memcmp(&sec_ie[10],
+ &uncst_oui[0], 4)) {
+ match = false;
+ break;
+ } /*else the uncst_oui is match*/
+ } else { /*mixed mode, unicast_enc_type > 1*/
+ /*select the uncst_oui and remove the
+ * other uncst_oui*/
+ cnt = sec_ie[8];
+ remove_cnt = (cnt-1)*4;
+ sec_ie[8] = 0x01;
+ memcpy(&sec_ie[10], &uncst_oui[0], 4);
+ /*remove the other unicast suit*/
+ memcpy(&sec_ie[14],
+ &sec_ie[14 + remove_cnt],
+ (sec_ie[1] - 14 + 2 -
+ remove_cnt));
+ sec_ie[1] = sec_ie[1]-remove_cnt;
+ }
+ break;
+ }
+ }
+ }
+ if ((authmode == _WPA_IE_ID_) || (authmode == _WPA2_IE_ID_)) {
+ /*copy fixed ie*/
+ memcpy(out_ie, in_ie, 12);
+ ielength = 12;
+ /*copy RSN or SSN*/
+ if (match == true) {
+ memcpy(&out_ie[ielength], &sec_ie[0], sec_ie[1]+2);
+ ielength += sec_ie[1] + 2;
+ if (authmode == _WPA2_IE_ID_) {
+ /*the Pre-Authentication bit should be zero*/
+ out_ie[ielength - 1] = 0;
+ out_ie[ielength - 2] = 0;
+ }
+ r8712_report_sec_ie(adapter, authmode, sec_ie);
+ }
+ } else {
+ /*copy fixed ie only*/
+ memcpy(out_ie, in_ie, 12);
+ ielength = 12;
+ if (psecuritypriv->wps_phase == true) {
+ memcpy(out_ie+ielength, psecuritypriv->wps_ie,
+ psecuritypriv->wps_ie_len);
+ ielength += psecuritypriv->wps_ie_len;
+ }
+ }
+ iEntry = SecIsInPMKIDList(adapter, pmlmepriv->assoc_bssid);
+ if (iEntry < 0)
+ return ielength;
+ if (authmode == _WPA2_IE_ID_) {
+ out_ie[ielength] = 1;
+ ielength++;
+ out_ie[ielength] = 0; /*PMKID count = 0x0100*/
+ ielength++;
+ memcpy(&out_ie[ielength],
+ &psecuritypriv->PMKIDList[iEntry].PMKID, 16);
+ ielength += 16;
+ out_ie[13] += 18;/*PMKID length = 2+16*/
+ }
+ return ielength;
+}
+
+void r8712_init_registrypriv_dev_network(struct _adapter *adapter)
+{
+ struct registry_priv *pregistrypriv = &adapter->registrypriv;
+ struct eeprom_priv *peepriv = &adapter->eeprompriv;
+ struct wlan_bssid_ex *pdev_network = &pregistrypriv->dev_network;
+ u8 *myhwaddr = myid(peepriv);
+
+ memcpy(pdev_network->MacAddress, myhwaddr, ETH_ALEN);
+ memcpy(&pdev_network->Ssid, &pregistrypriv->ssid,
+ sizeof(struct ndis_802_11_ssid));
+ pdev_network->Configuration.Length =
+ sizeof(struct NDIS_802_11_CONFIGURATION);
+ pdev_network->Configuration.BeaconPeriod = 100;
+ pdev_network->Configuration.FHConfig.Length = 0;
+ pdev_network->Configuration.FHConfig.HopPattern = 0;
+ pdev_network->Configuration.FHConfig.HopSet = 0;
+ pdev_network->Configuration.FHConfig.DwellTime = 0;
+}
+
+void r8712_update_registrypriv_dev_network(struct _adapter *adapter)
+{
+ int sz = 0;
+ struct registry_priv *pregistrypriv = &adapter->registrypriv;
+ struct wlan_bssid_ex *pdev_network = &pregistrypriv->dev_network;
+ struct security_priv *psecuritypriv = &adapter->securitypriv;
+ struct wlan_network *cur_network = &adapter->mlmepriv.cur_network;
+
+ pdev_network->Privacy = cpu_to_le32(psecuritypriv->PrivacyAlgrthm
+ > 0 ? 1 : 0); /* adhoc no 802.1x */
+ pdev_network->Rssi = 0;
+ switch (pregistrypriv->wireless_mode) {
+ case WIRELESS_11B:
+ pdev_network->NetworkTypeInUse = cpu_to_le32(Ndis802_11DS);
+ break;
+ case WIRELESS_11G:
+ case WIRELESS_11BG:
+ pdev_network->NetworkTypeInUse = cpu_to_le32(Ndis802_11OFDM24);
+ break;
+ case WIRELESS_11A:
+ pdev_network->NetworkTypeInUse = cpu_to_le32(Ndis802_11OFDM5);
+ break;
+ default:
+ /* TODO */
+ break;
+ }
+ pdev_network->Configuration.DSConfig = cpu_to_le32(
+ pregistrypriv->channel);
+ if (cur_network->network.InfrastructureMode == Ndis802_11IBSS)
+ pdev_network->Configuration.ATIMWindow = cpu_to_le32(3);
+ pdev_network->InfrastructureMode = cpu_to_le32(
+ cur_network->network.InfrastructureMode);
+ /* 1. Supported rates
+ * 2. IE
+ */
+ sz = r8712_generate_ie(pregistrypriv);
+ pdev_network->IELength = sz;
+ pdev_network->Length = r8712_get_ndis_wlan_bssid_ex_sz(
+ (struct ndis_wlan_bssid_ex *)pdev_network);
+}
+
+/*the function is at passive_level*/
+void r8712_joinbss_reset(struct _adapter *padapter)
+{
+ int i;
+ struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
+ struct ht_priv *phtpriv = &pmlmepriv->htpriv;
+
+ /* todo: if you want to do something io/reg/hw setting before join_bss,
+ * please add code here */
+ phtpriv->ampdu_enable = false;/*reset to disabled*/
+ for (i = 0; i < 16; i++)
+ phtpriv->baddbareq_issued[i] = false;/*reset it*/
+ if (phtpriv->ht_option) {
+ /* validate usb rx aggregation */
+ r8712_write8(padapter, 0x102500D9, 48);/*TH = 48 pages, 6k*/
+ } else {
+ /* invalidate usb rx aggregation */
+ /* TH=1 => means that invalidate usb rx aggregation */
+ r8712_write8(padapter, 0x102500D9, 1);
+ }
+}
+
+/*the function is >= passive_level*/
+unsigned int r8712_restructure_ht_ie(struct _adapter *padapter, u8 *in_ie,
+ u8 *out_ie, uint in_len, uint *pout_len)
+{
+ u32 ielen, out_len;
+ unsigned char *p;
+ struct ieee80211_ht_cap ht_capie;
+ unsigned char WMM_IE[] = {0x00, 0x50, 0xf2, 0x02, 0x00, 0x01, 0x00};
+ struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
+ struct qos_priv *pqospriv = &pmlmepriv->qospriv;
+ struct ht_priv *phtpriv = &pmlmepriv->htpriv;
+
+ phtpriv->ht_option = 0;
+ p = r8712_get_ie(in_ie+12, _HT_CAPABILITY_IE_, &ielen, in_len-12);
+ if (p && (ielen > 0)) {
+ if (pqospriv->qos_option == 0) {
+ out_len = *pout_len;
+ r8712_set_ie(out_ie+out_len, _VENDOR_SPECIFIC_IE_,
+ _WMM_IE_Length_, WMM_IE, pout_len);
+ pqospriv->qos_option = 1;
+ }
+ out_len = *pout_len;
+ memset(&ht_capie, 0, sizeof(struct ieee80211_ht_cap));
+ ht_capie.cap_info = IEEE80211_HT_CAP_SUP_WIDTH |
+ IEEE80211_HT_CAP_SGI_20 |
+ IEEE80211_HT_CAP_SGI_40 |
+ IEEE80211_HT_CAP_TX_STBC |
+ IEEE80211_HT_CAP_MAX_AMSDU |
+ IEEE80211_HT_CAP_DSSSCCK40;
+ ht_capie.ampdu_params_info = (IEEE80211_HT_CAP_AMPDU_FACTOR &
+ 0x03) | (IEEE80211_HT_CAP_AMPDU_DENSITY & 0x00);
+ r8712_set_ie(out_ie+out_len, _HT_CAPABILITY_IE_,
+ sizeof(struct ieee80211_ht_cap),
+ (unsigned char *)&ht_capie, pout_len);
+ phtpriv->ht_option = 1;
+ }
+ return phtpriv->ht_option;
+}
+
+/* the function is > passive_level (in critical_section) */
+static void update_ht_cap(struct _adapter *padapter, u8 *pie, uint ie_len)
+{
+ u8 *p, max_ampdu_sz;
+ int i, len;
+ struct sta_info *bmc_sta, *psta;
+ struct ieee80211_ht_cap *pht_capie;
+ struct recv_reorder_ctrl *preorder_ctrl;
+ struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
+ struct ht_priv *phtpriv = &pmlmepriv->htpriv;
+ struct registry_priv *pregistrypriv = &padapter->registrypriv;
+ struct wlan_network *pcur_network = &(pmlmepriv->cur_network);
+
+ if (!phtpriv->ht_option)
+ return;
+ /* maybe needs check if ap supports rx ampdu. */
+ if ((phtpriv->ampdu_enable == false) &&
+ (pregistrypriv->ampdu_enable == 1))
+ phtpriv->ampdu_enable = true;
+ /*check Max Rx A-MPDU Size*/
+ len = 0;
+ p = r8712_get_ie(pie + sizeof(struct NDIS_802_11_FIXED_IEs),
+ _HT_CAPABILITY_IE_,
+ &len, ie_len -
+ sizeof(struct NDIS_802_11_FIXED_IEs));
+ if (p && len > 0) {
+ pht_capie = (struct ieee80211_ht_cap *)(p+2);
+ max_ampdu_sz = (pht_capie->ampdu_params_info &
+ IEEE80211_HT_CAP_AMPDU_FACTOR);
+ /* max_ampdu_sz (kbytes); */
+ max_ampdu_sz = 1 << (max_ampdu_sz+3);
+ phtpriv->rx_ampdu_maxlen = max_ampdu_sz;
+ }
+ /* for A-MPDU Rx reordering buffer control for bmc_sta & sta_info
+ * if A-MPDU Rx is enabled, resetting rx_ordering_ctrl
+ * wstart_b(indicate_seq) to default value=0xffff
+ * todo: check if AP can send A-MPDU packets
+ */
+ bmc_sta = r8712_get_bcmc_stainfo(padapter);
+ if (bmc_sta) {
+ for (i = 0; i < 16; i++) {
+ preorder_ctrl = &bmc_sta->recvreorder_ctrl[i];
+ preorder_ctrl->indicate_seq = 0xffff;
+ preorder_ctrl->wend_b = 0xffff;
+ }
+ }
+ psta = r8712_get_stainfo(&padapter->stapriv,
+ pcur_network->network.MacAddress);
+ if (psta) {
+ for (i = 0; i < 16; i++) {
+ preorder_ctrl = &psta->recvreorder_ctrl[i];
+ preorder_ctrl->indicate_seq = 0xffff;
+ preorder_ctrl->wend_b = 0xffff;
+ }
+ }
+ len = 0;
+ p = r8712_get_ie(pie + sizeof(struct NDIS_802_11_FIXED_IEs),
+ _HT_ADD_INFO_IE_, &len,
+ ie_len-sizeof(struct NDIS_802_11_FIXED_IEs));
+}
+
+void r8712_issue_addbareq_cmd(struct _adapter *padapter, int priority)
+{
+ struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
+ struct ht_priv *phtpriv = &pmlmepriv->htpriv;
+
+ if ((phtpriv->ht_option == 1) && (phtpriv->ampdu_enable == true)) {
+ if (phtpriv->baddbareq_issued[priority] == false) {
+ r8712_addbareq_cmd(padapter, (u8)priority);
+ phtpriv->baddbareq_issued[priority] = true;
+ }
+ }
+}
diff --git a/drivers/staging/rtl8712/rtl871x_mlme.h b/drivers/staging/rtl8712/rtl871x_mlme.h
new file mode 100644
index 000000000..42bd0bf8a
--- /dev/null
+++ b/drivers/staging/rtl8712/rtl871x_mlme.h
@@ -0,0 +1,232 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2010 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ * Modifications for inclusion into the Linux staging tree are
+ * Copyright(c) 2010 Larry Finger. All rights reserved.
+ *
+ * Contact information:
+ * WLAN FAE <wlanfae@realtek.com>
+ * Larry Finger <Larry.Finger@lwfinger.net>
+ *
+ ******************************************************************************/
+#ifndef __RTL871X_MLME_H_
+#define __RTL871X_MLME_H_
+
+#include "osdep_service.h"
+#include "drv_types.h"
+#include "wlan_bssdef.h"
+
+#define MAX_BSS_CNT 64
+#define MAX_JOIN_TIMEOUT 6000
+
+#define SCANNING_TIMEOUT 4500
+
+#define SCANQUEUE_LIFETIME 20 /* unit:sec */
+
+#define WIFI_NULL_STATE 0x00000000
+#define WIFI_ASOC_STATE 0x00000001 /* Under Linked state...*/
+#define WIFI_REASOC_STATE 0x00000002
+#define WIFI_SLEEP_STATE 0x00000004
+#define WIFI_STATION_STATE 0x00000008
+#define WIFI_AP_STATE 0x00000010
+#define WIFI_ADHOC_STATE 0x00000020
+#define WIFI_ADHOC_MASTER_STATE 0x00000040
+#define WIFI_UNDER_LINKING 0x00000080
+#define WIFI_SITE_MONITOR 0x00000800 /* to indicate the station
+ * is under site surveying*/
+#define WIFI_MP_STATE 0x00010000
+#define WIFI_MP_CTX_BACKGROUND 0x00020000 /* in cont. tx background*/
+#define WIFI_MP_CTX_ST 0x00040000 /* in cont. tx with
+ * single-tone*/
+#define WIFI_MP_CTX_BACKGROUND_PENDING 0x00080000 /* pending in cont, tx
+ * background due to out of skb*/
+#define WIFI_MP_CTX_CCK_HW 0x00100000 /* in continuous tx*/
+#define WIFI_MP_CTX_CCK_CS 0x00200000 /* in cont, tx with carrier
+ * suppression*/
+#define WIFI_MP_LPBK_STATE 0x00400000
+
+#define _FW_UNDER_LINKING WIFI_UNDER_LINKING
+#define _FW_LINKED WIFI_ASOC_STATE
+#define _FW_UNDER_SURVEY WIFI_SITE_MONITOR
+
+/*
+there are several "locks" in mlme_priv,
+since mlme_priv is a shared resource between many threads,
+like ISR/Call-Back functions, the OID handlers, and even timer functions.
+Each _queue has its own locks, already.
+Other items are protected by mlme_priv.lock.
+To avoid possible dead lock, any thread trying to modify mlme_priv
+SHALL not lock up more than one lock at a time!
+*/
+
+#define traffic_threshold 10
+#define traffic_scan_period 500
+
+struct sitesurvey_ctrl {
+ u64 last_tx_pkts;
+ uint last_rx_pkts;
+ sint traffic_busy;
+ struct timer_list sitesurvey_ctrl_timer;
+};
+
+struct mlme_priv {
+
+ spinlock_t lock;
+ spinlock_t lock2;
+ sint fw_state; /*shall we protect this variable? */
+ u8 to_join; /*flag*/
+ u8 *nic_hdl;
+ struct list_head *pscanned;
+ struct __queue free_bss_pool;
+ struct __queue scanned_queue;
+ u8 *free_bss_buf;
+ unsigned long num_of_scanned;
+ u8 passive_mode; /*add for Android's SCAN-ACTIVE/SCAN-PASSIVE */
+ struct ndis_802_11_ssid assoc_ssid;
+ u8 assoc_bssid[6];
+ struct wlan_network cur_network;
+ struct sitesurvey_ctrl sitesurveyctrl;
+ struct timer_list assoc_timer;
+ uint assoc_by_bssid;
+ uint assoc_by_rssi;
+ struct timer_list scan_to_timer; /* driver handles scan_timeout.*/
+ struct timer_list dhcp_timer; /* set dhcp to if driver in ps mode.*/
+ struct qos_priv qospriv;
+ struct ht_priv htpriv;
+ struct timer_list wdg_timer; /*watchdog periodic timer*/
+};
+
+static inline u8 *get_bssid(struct mlme_priv *pmlmepriv)
+{
+ return pmlmepriv->cur_network.network.MacAddress;
+}
+
+static inline u8 check_fwstate(struct mlme_priv *pmlmepriv, sint state)
+{
+ if (pmlmepriv->fw_state & state)
+ return true;
+ return false;
+}
+
+static inline sint get_fwstate(struct mlme_priv *pmlmepriv)
+{
+ return pmlmepriv->fw_state;
+}
+
+/*
+ * No Limit on the calling context,
+ * therefore set it to be the critical section...
+ *
+ * ### NOTE:#### (!!!!)
+ * TAKE CARE BEFORE CALLING THIS FUNC, LOCK pmlmepriv->lock
+ */
+static inline void set_fwstate(struct mlme_priv *pmlmepriv, sint state)
+{
+ pmlmepriv->fw_state |= state;
+}
+
+static inline void _clr_fwstate_(struct mlme_priv *pmlmepriv, sint state)
+{
+ pmlmepriv->fw_state &= ~state;
+}
+
+/*
+ * No Limit on the calling context,
+ * therefore set it to be the critical section...
+ */
+static inline void clr_fwstate(struct mlme_priv *pmlmepriv, sint state)
+{
+ unsigned long irqL;
+
+ spin_lock_irqsave(&pmlmepriv->lock, irqL);
+ if (check_fwstate(pmlmepriv, state) == true)
+ pmlmepriv->fw_state ^= state;
+ spin_unlock_irqrestore(&pmlmepriv->lock, irqL);
+}
+
+static inline void up_scanned_network(struct mlme_priv *pmlmepriv)
+{
+ unsigned long irqL;
+
+ spin_lock_irqsave(&pmlmepriv->lock, irqL);
+ pmlmepriv->num_of_scanned++;
+ spin_unlock_irqrestore(&pmlmepriv->lock, irqL);
+}
+
+static inline void down_scanned_network(struct mlme_priv *pmlmepriv)
+{
+ unsigned long irqL;
+
+ spin_lock_irqsave(&pmlmepriv->lock, irqL);
+ pmlmepriv->num_of_scanned--;
+ spin_unlock_irqrestore(&pmlmepriv->lock, irqL);
+}
+
+static inline void set_scanned_network_val(struct mlme_priv *pmlmepriv,
+ sint val)
+{
+ unsigned long irqL;
+
+ spin_lock_irqsave(&pmlmepriv->lock, irqL);
+ pmlmepriv->num_of_scanned = val;
+ spin_unlock_irqrestore(&pmlmepriv->lock, irqL);
+}
+
+void r8712_survey_event_callback(struct _adapter *adapter, u8 *pbuf);
+void r8712_surveydone_event_callback(struct _adapter *adapter, u8 *pbuf);
+void r8712_joinbss_event_callback(struct _adapter *adapter, u8 *pbuf);
+void r8712_stassoc_event_callback(struct _adapter *adapter, u8 *pbuf);
+void r8712_stadel_event_callback(struct _adapter *adapter, u8 *pbuf);
+void r8712_atimdone_event_callback(struct _adapter *adapter, u8 *pbuf);
+void r8712_cpwm_event_callback(struct _adapter *adapter, u8 *pbuf);
+void r8712_wpspbc_event_callback(struct _adapter *adapter, u8 *pbuf);
+void r8712_free_network_queue(struct _adapter *adapter);
+int r8712_init_mlme_priv(struct _adapter *adapter);
+void r8712_free_mlme_priv(struct mlme_priv *pmlmepriv);
+sint r8712_select_and_join_from_scan(struct mlme_priv *pmlmepriv);
+sint r8712_set_key(struct _adapter *adapter,
+ struct security_priv *psecuritypriv, sint keyid);
+sint r8712_set_auth(struct _adapter *adapter,
+ struct security_priv *psecuritypriv);
+uint r8712_get_ndis_wlan_bssid_ex_sz(struct ndis_wlan_bssid_ex *bss);
+void r8712_generate_random_ibss(u8 *pibss);
+u8 *r8712_get_capability_from_ie(u8 *ie);
+struct wlan_network *r8712_get_oldest_wlan_network(
+ struct __queue *scanned_queue);
+void r8712_free_assoc_resources(struct _adapter *adapter);
+void r8712_ind_disconnect(struct _adapter *adapter);
+void r8712_indicate_connect(struct _adapter *adapter);
+int r8712_restruct_sec_ie(struct _adapter *adapter, u8 *in_ie,
+ u8 *out_ie, uint in_len);
+int r8712_restruct_wmm_ie(struct _adapter *adapter, u8 *in_ie,
+ u8 *out_ie, uint in_len, uint initial_out_len);
+void r8712_init_registrypriv_dev_network(struct _adapter *adapter);
+void r8712_update_registrypriv_dev_network(struct _adapter *adapter);
+void _r8712_sitesurvey_ctrl_handler(struct _adapter *adapter);
+void _r8712_join_timeout_handler(struct _adapter *adapter);
+void r8712_scan_timeout_handler(struct _adapter *adapter);
+void _r8712_dhcp_timeout_handler(struct _adapter *adapter);
+void _r8712_wdg_timeout_handler(struct _adapter *adapter);
+struct wlan_network *_r8712_alloc_network(struct mlme_priv *pmlmepriv);
+sint r8712_if_up(struct _adapter *padapter);
+void r8712_joinbss_reset(struct _adapter *padapter);
+unsigned int r8712_restructure_ht_ie(struct _adapter *padapter, u8 *in_ie,
+ u8 *out_ie, uint in_len, uint *pout_len);
+void r8712_issue_addbareq_cmd(struct _adapter *padapter, int priority);
+int r8712_is_same_ibss(struct _adapter *adapter, struct wlan_network *pnetwork);
+
+#endif /*__RTL871X_MLME_H_*/
diff --git a/drivers/staging/rtl8712/rtl871x_mp.c b/drivers/staging/rtl8712/rtl871x_mp.c
new file mode 100644
index 000000000..26201ea3c
--- /dev/null
+++ b/drivers/staging/rtl8712/rtl871x_mp.c
@@ -0,0 +1,745 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2010 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ * Modifications for inclusion into the Linux staging tree are
+ * Copyright(c) 2010 Larry Finger. All rights reserved.
+ *
+ * Contact information:
+ * WLAN FAE <wlanfae@realtek.com>
+ * Larry Finger <Larry.Finger@lwfinger.net>
+ *
+ ******************************************************************************/
+#define _RTL871X_MP_C_
+
+#include "osdep_service.h"
+#include "drv_types.h"
+#include "rtl871x_mp_phy_regdef.h"
+#include "rtl8712_cmd.h"
+
+static void _init_mp_priv_(struct mp_priv *pmp_priv)
+{
+ pmp_priv->mode = _LOOPBOOK_MODE_;
+ pmp_priv->curr_ch = 1;
+ pmp_priv->curr_modem = MIXED_PHY;
+ pmp_priv->curr_rateidx = 0;
+ pmp_priv->curr_txpoweridx = 0x14;
+ pmp_priv->antenna_tx = ANTENNA_A;
+ pmp_priv->antenna_rx = ANTENNA_AB;
+ pmp_priv->check_mp_pkt = 0;
+ pmp_priv->tx_pktcount = 0;
+ pmp_priv->rx_pktcount = 0;
+ pmp_priv->rx_crcerrpktcount = 0;
+}
+
+static int init_mp_priv(struct mp_priv *pmp_priv)
+{
+ int i, res;
+ struct mp_xmit_frame *pmp_xmitframe;
+
+ _init_mp_priv_(pmp_priv);
+ _init_queue(&pmp_priv->free_mp_xmitqueue);
+ pmp_priv->pallocated_mp_xmitframe_buf = NULL;
+ pmp_priv->pallocated_mp_xmitframe_buf = kmalloc(NR_MP_XMITFRAME *
+ sizeof(struct mp_xmit_frame) + 4,
+ GFP_ATOMIC);
+ if (!pmp_priv->pallocated_mp_xmitframe_buf) {
+ res = _FAIL;
+ goto _exit_init_mp_priv;
+ }
+ pmp_priv->pmp_xmtframe_buf = pmp_priv->pallocated_mp_xmitframe_buf +
+ 4 -
+ ((addr_t)(pmp_priv->pallocated_mp_xmitframe_buf) & 3);
+ pmp_xmitframe = (struct mp_xmit_frame *)pmp_priv->pmp_xmtframe_buf;
+ for (i = 0; i < NR_MP_XMITFRAME; i++) {
+ INIT_LIST_HEAD(&(pmp_xmitframe->list));
+ list_add_tail(&(pmp_xmitframe->list),
+ &(pmp_priv->free_mp_xmitqueue.queue));
+ pmp_xmitframe->pkt = NULL;
+ pmp_xmitframe->frame_tag = MP_FRAMETAG;
+ pmp_xmitframe->padapter = pmp_priv->papdater;
+ pmp_xmitframe++;
+ }
+ pmp_priv->free_mp_xmitframe_cnt = NR_MP_XMITFRAME;
+ res = _SUCCESS;
+_exit_init_mp_priv:
+ return res;
+}
+
+static int free_mp_priv(struct mp_priv *pmp_priv)
+{
+ kfree(pmp_priv->pallocated_mp_xmitframe_buf);
+ return 0;
+}
+
+void mp871xinit(struct _adapter *padapter)
+{
+ struct mp_priv *pmppriv = &padapter->mppriv;
+
+ pmppriv->papdater = padapter;
+ init_mp_priv(pmppriv);
+}
+
+void mp871xdeinit(struct _adapter *padapter)
+{
+ struct mp_priv *pmppriv = &padapter->mppriv;
+
+ free_mp_priv(pmppriv);
+}
+
+/*
+ * Special for bb and rf reg read/write
+ */
+static u32 fw_iocmd_read(struct _adapter *pAdapter, struct IOCMD_STRUCT iocmd)
+{
+ u32 cmd32 = 0, val32 = 0;
+ u8 iocmd_class = iocmd.cmdclass;
+ u16 iocmd_value = iocmd.value;
+ u8 iocmd_idx = iocmd.index;
+
+ cmd32 = (iocmd_class << 24) | (iocmd_value << 8) | iocmd_idx;
+ if (r8712_fw_cmd(pAdapter, cmd32))
+ r8712_fw_cmd_data(pAdapter, &val32, 1);
+ else
+ val32 = 0;
+ return val32;
+}
+
+static u8 fw_iocmd_write(struct _adapter *pAdapter,
+ struct IOCMD_STRUCT iocmd, u32 value)
+{
+ u32 cmd32 = 0;
+ u8 iocmd_class = iocmd.cmdclass;
+ u32 iocmd_value = iocmd.value;
+ u8 iocmd_idx = iocmd.index;
+
+ r8712_fw_cmd_data(pAdapter, &value, 0);
+ msleep(100);
+ cmd32 = (iocmd_class << 24) | (iocmd_value << 8) | iocmd_idx;
+ return r8712_fw_cmd(pAdapter, cmd32);
+}
+
+/* offset : 0X800~0XFFF */
+u32 r8712_bb_reg_read(struct _adapter *pAdapter, u16 offset)
+{
+ u8 shift = offset & 0x0003; /* 4 byte access */
+ u16 bb_addr = offset & 0x0FFC; /* 4 byte access */
+ u32 bb_val = 0;
+ struct IOCMD_STRUCT iocmd;
+
+ iocmd.cmdclass = IOCMD_CLASS_BB_RF;
+ iocmd.value = bb_addr;
+ iocmd.index = IOCMD_BB_READ_IDX;
+ bb_val = fw_iocmd_read(pAdapter, iocmd);
+ if (shift != 0) {
+ u32 bb_val2 = 0;
+
+ bb_val >>= (shift * 8);
+ iocmd.value += 4;
+ bb_val2 = fw_iocmd_read(pAdapter, iocmd);
+ bb_val2 <<= ((4 - shift) * 8);
+ bb_val |= bb_val2;
+ }
+ return bb_val;
+}
+
+/* offset : 0X800~0XFFF */
+u8 r8712_bb_reg_write(struct _adapter *pAdapter, u16 offset, u32 value)
+{
+ u8 shift = offset & 0x0003; /* 4 byte access */
+ u16 bb_addr = offset & 0x0FFC; /* 4 byte access */
+ struct IOCMD_STRUCT iocmd;
+
+ iocmd.cmdclass = IOCMD_CLASS_BB_RF;
+ iocmd.value = bb_addr;
+ iocmd.index = IOCMD_BB_WRITE_IDX;
+ if (shift != 0) {
+ u32 oldValue = 0;
+ u32 newValue = value;
+
+ oldValue = r8712_bb_reg_read(pAdapter, iocmd.value);
+ oldValue &= (0xFFFFFFFF >> ((4 - shift) * 8));
+ value = oldValue | (newValue << (shift * 8));
+ if (!fw_iocmd_write(pAdapter, iocmd, value))
+ return false;
+ iocmd.value += 4;
+ oldValue = r8712_bb_reg_read(pAdapter, iocmd.value);
+ oldValue &= (0xFFFFFFFF << (shift * 8));
+ value = oldValue | (newValue >> ((4 - shift) * 8));
+ }
+ return fw_iocmd_write(pAdapter, iocmd, value);
+}
+
+/* offset : 0x00 ~ 0xFF */
+u32 r8712_rf_reg_read(struct _adapter *pAdapter, u8 path, u8 offset)
+{
+ u16 rf_addr = (path << 8) | offset;
+ struct IOCMD_STRUCT iocmd;
+
+ iocmd.cmdclass = IOCMD_CLASS_BB_RF;
+ iocmd.value = rf_addr;
+ iocmd.index = IOCMD_RF_READ_IDX;
+ return fw_iocmd_read(pAdapter, iocmd);
+}
+
+u8 r8712_rf_reg_write(struct _adapter *pAdapter, u8 path, u8 offset, u32 value)
+{
+ u16 rf_addr = (path << 8) | offset;
+ struct IOCMD_STRUCT iocmd;
+
+ iocmd.cmdclass = IOCMD_CLASS_BB_RF;
+ iocmd.value = rf_addr;
+ iocmd.index = IOCMD_RF_WRIT_IDX;
+ return fw_iocmd_write(pAdapter, iocmd, value);
+}
+
+static u32 bitshift(u32 bitmask)
+{
+ u32 i;
+
+ for (i = 0; i <= 31; i++)
+ if (((bitmask>>i) & 0x1) == 1)
+ break;
+ return i;
+}
+
+static u32 get_bb_reg(struct _adapter *pAdapter, u16 offset, u32 bitmask)
+{
+ u32 org_value, bit_shift, new_value;
+
+ org_value = r8712_bb_reg_read(pAdapter, offset);
+ bit_shift = bitshift(bitmask);
+ new_value = (org_value & bitmask) >> bit_shift;
+ return new_value;
+}
+
+static u8 set_bb_reg(struct _adapter *pAdapter,
+ u16 offset,
+ u32 bitmask,
+ u32 value)
+{
+ u32 org_value, bit_shift, new_value;
+
+ if (bitmask != bMaskDWord) {
+ org_value = r8712_bb_reg_read(pAdapter, offset);
+ bit_shift = bitshift(bitmask);
+ new_value = ((org_value & (~bitmask)) | (value << bit_shift));
+ } else
+ new_value = value;
+ return r8712_bb_reg_write(pAdapter, offset, new_value);
+}
+
+static u32 get_rf_reg(struct _adapter *pAdapter, u8 path, u8 offset,
+ u32 bitmask)
+{
+ u32 org_value, bit_shift, new_value;
+
+ org_value = r8712_rf_reg_read(pAdapter, path, offset);
+ bit_shift = bitshift(bitmask);
+ new_value = (org_value & bitmask) >> bit_shift;
+ return new_value;
+}
+
+static u8 set_rf_reg(struct _adapter *pAdapter, u8 path, u8 offset, u32 bitmask,
+ u32 value)
+{
+ u32 org_value, bit_shift, new_value;
+
+ if (bitmask != bMaskDWord) {
+ org_value = r8712_rf_reg_read(pAdapter, path, offset);
+ bit_shift = bitshift(bitmask);
+ new_value = ((org_value & (~bitmask)) | (value << bit_shift));
+ } else
+ new_value = value;
+ return r8712_rf_reg_write(pAdapter, path, offset, new_value);
+}
+
+/*
+ * SetChannel
+ * Description
+ * Use H2C command to change channel,
+ * not only modify rf register, but also other setting need to be done.
+ */
+void r8712_SetChannel(struct _adapter *pAdapter)
+{
+ struct cmd_priv *pcmdpriv = &pAdapter->cmdpriv;
+ struct cmd_obj *pcmd = NULL;
+ struct SetChannel_parm *pparm = NULL;
+ u16 code = GEN_CMD_CODE(_SetChannel);
+
+ pcmd = kmalloc(sizeof(*pcmd), GFP_ATOMIC);
+ if (pcmd == NULL)
+ return;
+ pparm = kmalloc(sizeof(*pparm), GFP_ATOMIC);
+ if (pparm == NULL) {
+ kfree(pcmd);
+ return;
+ }
+ pparm->curr_ch = pAdapter->mppriv.curr_ch;
+ init_h2fwcmd_w_parm_no_rsp(pcmd, pparm, code);
+ r8712_enqueue_cmd(pcmdpriv, pcmd);
+}
+
+static void SetCCKTxPower(struct _adapter *pAdapter, u8 TxPower)
+{
+ u16 TxAGC = 0;
+
+ TxAGC = TxPower;
+ set_bb_reg(pAdapter, rTxAGC_CCK_Mcs32, bTxAGCRateCCK, TxAGC);
+}
+
+static void SetOFDMTxPower(struct _adapter *pAdapter, u8 TxPower)
+{
+ u32 TxAGC = 0;
+
+ TxAGC |= ((TxPower<<24)|(TxPower<<16)|(TxPower<<8)|TxPower);
+ set_bb_reg(pAdapter, rTxAGC_Rate18_06, bTxAGCRate18_06, TxAGC);
+ set_bb_reg(pAdapter, rTxAGC_Rate54_24, bTxAGCRate54_24, TxAGC);
+ set_bb_reg(pAdapter, rTxAGC_Mcs03_Mcs00, bTxAGCRateMCS3_MCS0, TxAGC);
+ set_bb_reg(pAdapter, rTxAGC_Mcs07_Mcs04, bTxAGCRateMCS7_MCS4, TxAGC);
+ set_bb_reg(pAdapter, rTxAGC_Mcs11_Mcs08, bTxAGCRateMCS11_MCS8, TxAGC);
+ set_bb_reg(pAdapter, rTxAGC_Mcs15_Mcs12, bTxAGCRateMCS15_MCS12, TxAGC);
+}
+
+void r8712_SetTxPower(struct _adapter *pAdapter)
+{
+ u8 TxPower = pAdapter->mppriv.curr_txpoweridx;
+
+ SetCCKTxPower(pAdapter, TxPower);
+ SetOFDMTxPower(pAdapter, TxPower);
+}
+
+void r8712_SetTxAGCOffset(struct _adapter *pAdapter, u32 ulTxAGCOffset)
+{
+ u32 TxAGCOffset_B, TxAGCOffset_C, TxAGCOffset_D, tmpAGC;
+
+ TxAGCOffset_B = (ulTxAGCOffset&0x000000ff);
+ TxAGCOffset_C = (ulTxAGCOffset & 0x0000ff00)>>8;
+ TxAGCOffset_D = (ulTxAGCOffset & 0x00ff0000)>>16;
+ tmpAGC = (TxAGCOffset_D<<8 | TxAGCOffset_C<<4 | TxAGCOffset_B);
+ set_bb_reg(pAdapter, rFPGA0_TxGainStage,
+ (bXBTxAGC|bXCTxAGC|bXDTxAGC), tmpAGC);
+}
+
+void r8712_SetDataRate(struct _adapter *pAdapter)
+{
+ u8 path = RF_PATH_A;
+ u8 offset = RF_SYN_G2;
+ u32 value;
+
+ value = (pAdapter->mppriv.curr_rateidx < 4) ? 0x4440 : 0xF200;
+ r8712_rf_reg_write(pAdapter, path, offset, value);
+}
+
+void r8712_SwitchBandwidth(struct _adapter *pAdapter)
+{
+ /* 3 1.Set MAC register : BWOPMODE bit2:1 20MhzBW */
+ u8 regBwOpMode = 0;
+ u8 Bandwidth = pAdapter->mppriv.curr_bandwidth;
+
+ regBwOpMode = r8712_read8(pAdapter, 0x10250203);
+ if (Bandwidth == HT_CHANNEL_WIDTH_20)
+ regBwOpMode |= BIT(2);
+ else
+ regBwOpMode &= ~(BIT(2));
+ r8712_write8(pAdapter, 0x10250203, regBwOpMode);
+ /* 3 2.Set PHY related register */
+ switch (Bandwidth) {
+ /* 20 MHz channel*/
+ case HT_CHANNEL_WIDTH_20:
+ set_bb_reg(pAdapter, rFPGA0_RFMOD, bRFMOD, 0x0);
+ set_bb_reg(pAdapter, rFPGA1_RFMOD, bRFMOD, 0x0);
+ /* Use PHY_REG.txt default value. Do not need to change.
+ * Correct the tx power for CCK rate in 40M.
+ * It is set in Tx descriptor for 8192x series
+ */
+ set_bb_reg(pAdapter, rFPGA0_AnalogParameter2, bMaskDWord, 0x58);
+ break;
+ /* 40 MHz channel*/
+ case HT_CHANNEL_WIDTH_40:
+ set_bb_reg(pAdapter, rFPGA0_RFMOD, bRFMOD, 0x1);
+ set_bb_reg(pAdapter, rFPGA1_RFMOD, bRFMOD, 0x1);
+ /* Use PHY_REG.txt default value. Do not need to change.
+ * Correct the tx power for CCK rate in 40M.
+ * Set Control channel to upper or lower. These settings are
+ * required only for 40MHz */
+ set_bb_reg(pAdapter, rCCK0_System, bCCKSideBand,
+ (HAL_PRIME_CHNL_OFFSET_DONT_CARE>>1));
+ set_bb_reg(pAdapter, rOFDM1_LSTF, 0xC00,
+ HAL_PRIME_CHNL_OFFSET_DONT_CARE);
+ set_bb_reg(pAdapter, rFPGA0_AnalogParameter2, bMaskDWord, 0x18);
+ break;
+ default:
+ break;
+ }
+
+ /* 3 3.Set RF related register */
+ switch (Bandwidth) {
+ case HT_CHANNEL_WIDTH_20:
+ set_rf_reg(pAdapter, RF_PATH_A, RF_CHNLBW,
+ BIT(10) | BIT(11), 0x01);
+ break;
+ case HT_CHANNEL_WIDTH_40:
+ set_rf_reg(pAdapter, RF_PATH_A, RF_CHNLBW,
+ BIT(10) | BIT(11), 0x00);
+ break;
+ default:
+ break;
+ }
+}
+/*------------------------------Define structure----------------------------*/
+struct R_ANTENNA_SELECT_OFDM {
+ u32 r_tx_antenna:4;
+ u32 r_ant_l:4;
+ u32 r_ant_non_ht:4;
+ u32 r_ant_ht1:4;
+ u32 r_ant_ht2:4;
+ u32 r_ant_ht_s1:4;
+ u32 r_ant_non_ht_s1:4;
+ u32 OFDM_TXSC:2;
+ u32 Reserved:2;
+};
+
+struct R_ANTENNA_SELECT_CCK {
+ u8 r_cckrx_enable_2:2;
+ u8 r_cckrx_enable:2;
+ u8 r_ccktx_enable:4;
+};
+
+void r8712_SwitchAntenna(struct _adapter *pAdapter)
+{
+ u32 ofdm_tx_en_val = 0, ofdm_tx_ant_sel_val = 0;
+ u8 ofdm_rx_ant_sel_val = 0;
+ u8 cck_ant_select_val = 0;
+ u32 cck_ant_sel_val = 0;
+ struct R_ANTENNA_SELECT_CCK *p_cck_txrx;
+
+ p_cck_txrx = (struct R_ANTENNA_SELECT_CCK *)&cck_ant_select_val;
+
+ switch (pAdapter->mppriv.antenna_tx) {
+ case ANTENNA_A:
+ /* From SD3 Willis suggestion !!! Set RF A=TX and B as standby*/
+ set_bb_reg(pAdapter, rFPGA0_XA_HSSIParameter2, 0xe, 2);
+ set_bb_reg(pAdapter, rFPGA0_XB_HSSIParameter2, 0xe, 1);
+ ofdm_tx_en_val = 0x3;
+ ofdm_tx_ant_sel_val = 0x11111111;/* Power save */
+ p_cck_txrx->r_ccktx_enable = 0x8;
+ break;
+ case ANTENNA_B:
+ set_bb_reg(pAdapter, rFPGA0_XA_HSSIParameter2, 0xe, 1);
+ set_bb_reg(pAdapter, rFPGA0_XB_HSSIParameter2, 0xe, 2);
+ ofdm_tx_en_val = 0x3;
+ ofdm_tx_ant_sel_val = 0x22222222;/* Power save */
+ p_cck_txrx->r_ccktx_enable = 0x4;
+ break;
+ case ANTENNA_AB: /* For 8192S */
+ set_bb_reg(pAdapter, rFPGA0_XA_HSSIParameter2, 0xe, 2);
+ set_bb_reg(pAdapter, rFPGA0_XB_HSSIParameter2, 0xe, 2);
+ ofdm_tx_en_val = 0x3;
+ ofdm_tx_ant_sel_val = 0x3321333; /* Disable Power save */
+ p_cck_txrx->r_ccktx_enable = 0xC;
+ break;
+ default:
+ break;
+ }
+ /*OFDM Tx*/
+ set_bb_reg(pAdapter, rFPGA1_TxInfo, 0xffffffff, ofdm_tx_ant_sel_val);
+ /*OFDM Tx*/
+ set_bb_reg(pAdapter, rFPGA0_TxInfo, 0x0000000f, ofdm_tx_en_val);
+ switch (pAdapter->mppriv.antenna_rx) {
+ case ANTENNA_A:
+ ofdm_rx_ant_sel_val = 0x1; /* A */
+ p_cck_txrx->r_cckrx_enable = 0x0; /* default: A */
+ p_cck_txrx->r_cckrx_enable_2 = 0x0; /* option: A */
+ break;
+ case ANTENNA_B:
+ ofdm_rx_ant_sel_val = 0x2; /* B */
+ p_cck_txrx->r_cckrx_enable = 0x1; /* default: B */
+ p_cck_txrx->r_cckrx_enable_2 = 0x1; /* option: B */
+ break;
+ case ANTENNA_AB:
+ ofdm_rx_ant_sel_val = 0x3; /* AB */
+ p_cck_txrx->r_cckrx_enable = 0x0; /* default:A */
+ p_cck_txrx->r_cckrx_enable_2 = 0x1; /* option:B */
+ break;
+ default:
+ break;
+ }
+ /*OFDM Rx*/
+ set_bb_reg(pAdapter, rOFDM0_TRxPathEnable, 0x0000000f,
+ ofdm_rx_ant_sel_val);
+ /*OFDM Rx*/
+ set_bb_reg(pAdapter, rOFDM1_TRxPathEnable, 0x0000000f,
+ ofdm_rx_ant_sel_val);
+
+ cck_ant_sel_val = cck_ant_select_val;
+ /*CCK TxRx*/
+ set_bb_reg(pAdapter, rCCK0_AFESetting, bMaskByte3, cck_ant_sel_val);
+}
+
+void r8712_SetCrystalCap(struct _adapter *pAdapter)
+{
+ set_bb_reg(pAdapter, rFPGA0_AnalogParameter1, bXtalCap,
+ pAdapter->mppriv.curr_crystalcap);
+}
+
+static void TriggerRFThermalMeter(struct _adapter *pAdapter)
+{
+ /* 0x24: RF Reg[6:5] */
+ set_rf_reg(pAdapter, RF_PATH_A, RF_T_METER, bRFRegOffsetMask, 0x60);
+}
+
+static u32 ReadRFThermalMeter(struct _adapter *pAdapter)
+{
+ /* 0x24: RF Reg[4:0] */
+ return get_rf_reg(pAdapter, RF_PATH_A, RF_T_METER, 0x1F);
+}
+
+void r8712_GetThermalMeter(struct _adapter *pAdapter, u32 *value)
+{
+ TriggerRFThermalMeter(pAdapter);
+ msleep(1000);
+ *value = ReadRFThermalMeter(pAdapter);
+}
+
+void r8712_SetSingleCarrierTx(struct _adapter *pAdapter, u8 bStart)
+{
+ if (bStart) { /* Start Single Carrier. */
+ /* 1. if OFDM block on? */
+ if (!get_bb_reg(pAdapter, rFPGA0_RFMOD, bOFDMEn))
+ /*set OFDM block on*/
+ set_bb_reg(pAdapter, rFPGA0_RFMOD, bOFDMEn, bEnable);
+ /* 2. set CCK test mode off, set to CCK normal mode */
+ set_bb_reg(pAdapter, rCCK0_System, bCCKBBMode, bDisable);
+ /* 3. turn on scramble setting */
+ set_bb_reg(pAdapter, rCCK0_System, bCCKScramble, bEnable);
+ /* 4. Turn On Single Carrier Tx and off the other test modes. */
+ set_bb_reg(pAdapter, rOFDM1_LSTF, bOFDMContinueTx, bDisable);
+ set_bb_reg(pAdapter, rOFDM1_LSTF, bOFDMSingleCarrier, bEnable);
+ set_bb_reg(pAdapter, rOFDM1_LSTF, bOFDMSingleTone, bDisable);
+ } else { /* Stop Single Carrier.*/
+ /* Turn off all test modes.*/
+ set_bb_reg(pAdapter, rOFDM1_LSTF, bOFDMContinueTx, bDisable);
+ set_bb_reg(pAdapter, rOFDM1_LSTF, bOFDMSingleCarrier,
+ bDisable);
+ set_bb_reg(pAdapter, rOFDM1_LSTF, bOFDMSingleTone, bDisable);
+ msleep(20);
+ /*BB Reset*/
+ set_bb_reg(pAdapter, rPMAC_Reset, bBBResetB, 0x0);
+ set_bb_reg(pAdapter, rPMAC_Reset, bBBResetB, 0x1);
+ }
+}
+
+void r8712_SetSingleToneTx(struct _adapter *pAdapter, u8 bStart)
+{
+ u8 rfPath = pAdapter->mppriv.curr_rfpath;
+
+ switch (pAdapter->mppriv.antenna_tx) {
+ case ANTENNA_B:
+ rfPath = RF_PATH_B;
+ break;
+ case ANTENNA_A:
+ default:
+ rfPath = RF_PATH_A;
+ break;
+ }
+ if (bStart) { /* Start Single Tone.*/
+ set_bb_reg(pAdapter, rFPGA0_RFMOD, bCCKEn, bDisable);
+ set_bb_reg(pAdapter, rFPGA0_RFMOD, bOFDMEn, bDisable);
+ set_rf_reg(pAdapter, rfPath, RF_TX_G2, bRFRegOffsetMask,
+ 0xd4000);
+ msleep(100);
+ /* PAD all on.*/
+ set_rf_reg(pAdapter, rfPath, RF_AC, bRFRegOffsetMask, 0x2001f);
+ msleep(100);
+ } else { /* Stop Single Tone.*/
+ set_bb_reg(pAdapter, rFPGA0_RFMOD, bCCKEn, bEnable);
+ set_bb_reg(pAdapter, rFPGA0_RFMOD, bOFDMEn, bEnable);
+ set_rf_reg(pAdapter, rfPath, RF_TX_G2, bRFRegOffsetMask,
+ 0x54000);
+ msleep(100);
+ /* PAD all on.*/
+ set_rf_reg(pAdapter, rfPath, RF_AC, bRFRegOffsetMask, 0x30000);
+ msleep(100);
+ }
+}
+
+void r8712_SetCarrierSuppressionTx(struct _adapter *pAdapter, u8 bStart)
+{
+ if (bStart) { /* Start Carrier Suppression.*/
+ if (pAdapter->mppriv.curr_rateidx <= MPT_RATE_11M) {
+ /* 1. if CCK block on? */
+ if (!get_bb_reg(pAdapter, rFPGA0_RFMOD, bCCKEn)) {
+ /*set CCK block on*/
+ set_bb_reg(pAdapter, rFPGA0_RFMOD, bCCKEn,
+ bEnable);
+ }
+ /* Turn Off All Test Mode */
+ set_bb_reg(pAdapter, rOFDM1_LSTF, bOFDMContinueTx,
+ bDisable);
+ set_bb_reg(pAdapter, rOFDM1_LSTF, bOFDMSingleCarrier,
+ bDisable);
+ set_bb_reg(pAdapter, rOFDM1_LSTF, bOFDMSingleTone,
+ bDisable);
+ /*transmit mode*/
+ set_bb_reg(pAdapter, rCCK0_System, bCCKBBMode, 0x2);
+ /*turn off scramble setting*/
+ set_bb_reg(pAdapter, rCCK0_System, bCCKScramble,
+ bDisable);
+ /*Set CCK Tx Test Rate*/
+ /*Set FTxRate to 1Mbps*/
+ set_bb_reg(pAdapter, rCCK0_System, bCCKTxRate, 0x0);
+ }
+ } else { /* Stop Carrier Suppression. */
+ if (pAdapter->mppriv.curr_rateidx <= MPT_RATE_11M) {
+ /*normal mode*/
+ set_bb_reg(pAdapter, rCCK0_System, bCCKBBMode, 0x0);
+ /*turn on scramble setting*/
+ set_bb_reg(pAdapter, rCCK0_System, bCCKScramble,
+ bEnable);
+ /*BB Reset*/
+ set_bb_reg(pAdapter, rPMAC_Reset, bBBResetB, 0x0);
+ set_bb_reg(pAdapter, rPMAC_Reset, bBBResetB, 0x1);
+ }
+ }
+}
+
+static void SetCCKContinuousTx(struct _adapter *pAdapter, u8 bStart)
+{
+ u32 cckrate;
+
+ if (bStart) {
+ /* 1. if CCK block on? */
+ if (!get_bb_reg(pAdapter, rFPGA0_RFMOD, bCCKEn)) {
+ /*set CCK block on*/
+ set_bb_reg(pAdapter, rFPGA0_RFMOD, bCCKEn, bEnable);
+ }
+ /* Turn Off All Test Mode */
+ set_bb_reg(pAdapter, rOFDM1_LSTF, bOFDMContinueTx, bDisable);
+ set_bb_reg(pAdapter, rOFDM1_LSTF, bOFDMSingleCarrier, bDisable);
+ set_bb_reg(pAdapter, rOFDM1_LSTF, bOFDMSingleTone, bDisable);
+ /*Set CCK Tx Test Rate*/
+ cckrate = pAdapter->mppriv.curr_rateidx;
+ set_bb_reg(pAdapter, rCCK0_System, bCCKTxRate, cckrate);
+ /*transmit mode*/
+ set_bb_reg(pAdapter, rCCK0_System, bCCKBBMode, 0x2);
+ /*turn on scramble setting*/
+ set_bb_reg(pAdapter, rCCK0_System, bCCKScramble, bEnable);
+ } else {
+ /*normal mode*/
+ set_bb_reg(pAdapter, rCCK0_System, bCCKBBMode, 0x0);
+ /*turn on scramble setting*/
+ set_bb_reg(pAdapter, rCCK0_System, bCCKScramble, bEnable);
+ /*BB Reset*/
+ set_bb_reg(pAdapter, rPMAC_Reset, bBBResetB, 0x0);
+ set_bb_reg(pAdapter, rPMAC_Reset, bBBResetB, 0x1);
+ }
+} /* mpt_StartCckContTx */
+
+static void SetOFDMContinuousTx(struct _adapter *pAdapter, u8 bStart)
+{
+ if (bStart) {
+ /* 1. if OFDM block on? */
+ if (!get_bb_reg(pAdapter, rFPGA0_RFMOD, bOFDMEn)) {
+ /*set OFDM block on*/
+ set_bb_reg(pAdapter, rFPGA0_RFMOD, bOFDMEn, bEnable);
+ }
+ /* 2. set CCK test mode off, set to CCK normal mode*/
+ set_bb_reg(pAdapter, rCCK0_System, bCCKBBMode, bDisable);
+ /* 3. turn on scramble setting */
+ set_bb_reg(pAdapter, rCCK0_System, bCCKScramble, bEnable);
+ /* 4. Turn On Continue Tx and turn off the other test modes.*/
+ set_bb_reg(pAdapter, rOFDM1_LSTF, bOFDMContinueTx, bEnable);
+ set_bb_reg(pAdapter, rOFDM1_LSTF, bOFDMSingleCarrier, bDisable);
+ set_bb_reg(pAdapter, rOFDM1_LSTF, bOFDMSingleTone, bDisable);
+ } else {
+ set_bb_reg(pAdapter, rOFDM1_LSTF, bOFDMContinueTx, bDisable);
+ set_bb_reg(pAdapter, rOFDM1_LSTF, bOFDMSingleCarrier,
+ bDisable);
+ set_bb_reg(pAdapter, rOFDM1_LSTF, bOFDMSingleTone, bDisable);
+ msleep(20);
+ /*BB Reset*/
+ set_bb_reg(pAdapter, rPMAC_Reset, bBBResetB, 0x0);
+ set_bb_reg(pAdapter, rPMAC_Reset, bBBResetB, 0x1);
+ }
+} /* mpt_StartOfdmContTx */
+
+void r8712_SetContinuousTx(struct _adapter *pAdapter, u8 bStart)
+{
+ /* ADC turn off [bit24-21] adc port0 ~ port1 */
+ if (bStart) {
+ r8712_bb_reg_write(pAdapter, rRx_Wait_CCCA,
+ r8712_bb_reg_read(pAdapter,
+ rRx_Wait_CCCA) & 0xFE1FFFFF);
+ msleep(100);
+ }
+ if (pAdapter->mppriv.curr_rateidx <= MPT_RATE_11M)
+ SetCCKContinuousTx(pAdapter, bStart);
+ else if ((pAdapter->mppriv.curr_rateidx >= MPT_RATE_6M) &&
+ (pAdapter->mppriv.curr_rateidx <= MPT_RATE_MCS15))
+ SetOFDMContinuousTx(pAdapter, bStart);
+ /* ADC turn on [bit24-21] adc port0 ~ port1 */
+ if (!bStart)
+ r8712_bb_reg_write(pAdapter, rRx_Wait_CCCA,
+ r8712_bb_reg_read(pAdapter,
+ rRx_Wait_CCCA) | 0x01E00000);
+}
+
+void r8712_ResetPhyRxPktCount(struct _adapter *pAdapter)
+{
+ u32 i, phyrx_set = 0;
+
+ for (i = OFDM_PPDU_BIT; i <= HT_MPDU_FAIL_BIT; i++) {
+ phyrx_set = 0;
+ phyrx_set |= (i << 28); /*select*/
+ phyrx_set |= 0x08000000; /* set counter to zero*/
+ r8712_write32(pAdapter, RXERR_RPT, phyrx_set);
+ }
+}
+
+static u32 GetPhyRxPktCounts(struct _adapter *pAdapter, u32 selbit)
+{
+ /*selection*/
+ u32 phyrx_set = 0, count = 0;
+ u32 SelectBit;
+
+ SelectBit = selbit << 28;
+ phyrx_set |= (SelectBit & 0xF0000000);
+ r8712_write32(pAdapter, RXERR_RPT, phyrx_set);
+ /*Read packet count*/
+ count = r8712_read32(pAdapter, RXERR_RPT) & RPTMaxCount;
+ return count;
+}
+
+u32 r8712_GetPhyRxPktReceived(struct _adapter *pAdapter)
+{
+ u32 OFDM_cnt = 0, CCK_cnt = 0, HT_cnt = 0;
+
+ OFDM_cnt = GetPhyRxPktCounts(pAdapter, OFDM_MPDU_OK_BIT);
+ CCK_cnt = GetPhyRxPktCounts(pAdapter, CCK_MPDU_OK_BIT);
+ HT_cnt = GetPhyRxPktCounts(pAdapter, HT_MPDU_OK_BIT);
+ return OFDM_cnt + CCK_cnt + HT_cnt;
+}
+
+u32 r8712_GetPhyRxPktCRC32Error(struct _adapter *pAdapter)
+{
+ u32 OFDM_cnt = 0, CCK_cnt = 0, HT_cnt = 0;
+
+ OFDM_cnt = GetPhyRxPktCounts(pAdapter, OFDM_MPDU_FAIL_BIT);
+ CCK_cnt = GetPhyRxPktCounts(pAdapter, CCK_MPDU_FAIL_BIT);
+ HT_cnt = GetPhyRxPktCounts(pAdapter, HT_MPDU_FAIL_BIT);
+ return OFDM_cnt + CCK_cnt + HT_cnt;
+}
diff --git a/drivers/staging/rtl8712/rtl871x_mp.h b/drivers/staging/rtl8712/rtl871x_mp.h
new file mode 100644
index 000000000..75893f225
--- /dev/null
+++ b/drivers/staging/rtl8712/rtl871x_mp.h
@@ -0,0 +1,286 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2010 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ * Modifications for inclusion into the Linux staging tree are
+ * Copyright(c) 2010 Larry Finger. All rights reserved.
+ *
+ * Contact information:
+ * WLAN FAE <wlanfae@realtek.com>
+ * Larry Finger <Larry.Finger@lwfinger.net>
+ *
+ ******************************************************************************/
+#ifndef __RTL871X_MP_H_
+#define __RTL871X_MP_H_
+
+#define MPT_NOOP 0
+#define MPT_READ_MAC_1BYTE 1
+#define MPT_READ_MAC_2BYTE 2
+#define MPT_READ_MAC_4BYTE 3
+#define MPT_WRITE_MAC_1BYTE 4
+#define MPT_WRITE_MAC_2BYTE 5
+#define MPT_WRITE_MAC_4BYTE 6
+#define MPT_READ_BB_CCK 7
+#define MPT_WRITE_BB_CCK 8
+#define MPT_READ_BB_OFDM 9
+#define MPT_WRITE_BB_OFDM 10
+#define MPT_READ_RF 11
+#define MPT_WRITE_RF 12
+#define MPT_READ_EEPROM_1BYTE 13
+#define MPT_WRITE_EEPROM_1BYTE 14
+#define MPT_READ_EEPROM_2BYTE 15
+#define MPT_WRITE_EEPROM_2BYTE 16
+#define MPT_SET_CSTHRESHOLD 21
+#define MPT_SET_INITGAIN 22
+#define MPT_SWITCH_BAND 23
+#define MPT_SWITCH_CHANNEL 24
+#define MPT_SET_DATARATE 25
+#define MPT_SWITCH_ANTENNA 26
+#define MPT_SET_TX_POWER 27
+#define MPT_SET_CONT_TX 28
+#define MPT_SET_SINGLE_CARRIER 29
+#define MPT_SET_CARRIER_SUPPRESSION 30
+#define MPT_GET_RATE_TABLE 31
+#define MPT_READ_TSSI 32
+#define MPT_GET_THERMAL_METER 33
+#define MAX_MP_XMITBUF_SZ 2048
+#define NR_MP_XMITFRAME 8
+
+struct mp_xmit_frame {
+ struct list_head list;
+ struct pkt_attrib attrib;
+ _pkt *pkt;
+ int frame_tag;
+ struct _adapter *padapter;
+ u8 *mem_addr;
+ u16 sz[8];
+ struct urb *pxmit_urb[8];
+ u8 bpending[8];
+ u8 last[8];
+};
+
+struct mp_wiparam {
+ u32 bcompleted;
+ u32 act_type;
+ u32 io_offset;
+ u32 io_value;
+};
+
+struct mp_priv {
+ struct _adapter *papdater;
+ /*OID cmd handler*/
+ struct mp_wiparam workparam;
+ u8 act_in_progress;
+ /*Tx Section*/
+ u8 TID;
+ u32 tx_pktcount;
+ /*Rx Section*/
+ u32 rx_pktcount;
+ u32 rx_crcerrpktcount;
+ u32 rx_pktloss;
+ struct recv_stat rxstat;
+ /*RF/BB relative*/
+ u32 curr_ch;
+ u32 curr_rateidx;
+ u8 curr_bandwidth;
+ u8 curr_modem;
+ u8 curr_txpoweridx;
+ u32 curr_crystalcap;
+ u16 antenna_tx;
+ u16 antenna_rx;
+ u8 curr_rfpath;
+ u8 check_mp_pkt;
+ uint ForcedDataRate;
+ struct wlan_network mp_network;
+ unsigned char network_macaddr[6];
+ /*Testing Flag*/
+ u32 mode;/*0 for normal type packet,
+ * 1 for loopback packet (16bytes TXCMD)*/
+ sint prev_fw_state;
+ u8 *pallocated_mp_xmitframe_buf;
+ u8 *pmp_xmtframe_buf;
+ struct __queue free_mp_xmitqueue;
+ u32 free_mp_xmitframe_cnt;
+};
+
+struct IOCMD_STRUCT {
+ u8 cmdclass;
+ u16 value;
+ u8 index;
+};
+
+struct rf_reg_param {
+ u32 path;
+ u32 offset;
+ u32 value;
+};
+
+struct bb_reg_param {
+ u32 offset;
+ u32 value;
+};
+/* ======================================================================= */
+
+#define LOWER true
+#define RAISE false
+#define IOCMD_CTRL_REG 0x10250370
+#define IOCMD_DATA_REG 0x10250374
+#define IOCMD_GET_THERMAL_METER 0xFD000028
+#define IOCMD_CLASS_BB_RF 0xF0
+#define IOCMD_BB_READ_IDX 0x00
+#define IOCMD_BB_WRITE_IDX 0x01
+#define IOCMD_RF_READ_IDX 0x02
+#define IOCMD_RF_WRIT_IDX 0x03
+#define BB_REG_BASE_ADDR 0x800
+#define RF_PATH_A 0
+#define RF_PATH_B 1
+#define RF_PATH_C 2
+#define RF_PATH_D 3
+#define MAX_RF_PATH_NUMS 2
+#define _2MAC_MODE_ 0
+#define _LOOPBOOK_MODE_ 1
+
+/* MP set force data rate base on the definition. */
+enum {
+ /* CCK rate. */
+ MPT_RATE_1M, /* 0 */
+ MPT_RATE_2M,
+ MPT_RATE_55M,
+ MPT_RATE_11M, /* 3 */
+
+ /* OFDM rate. */
+ MPT_RATE_6M, /* 4 */
+ MPT_RATE_9M,
+ MPT_RATE_12M,
+ MPT_RATE_18M,
+ MPT_RATE_24M,
+ MPT_RATE_36M,
+ MPT_RATE_48M,
+ MPT_RATE_54M, /* 11 */
+
+ /* HT rate. */
+ MPT_RATE_MCS0, /* 12 */
+ MPT_RATE_MCS1,
+ MPT_RATE_MCS2,
+ MPT_RATE_MCS3,
+ MPT_RATE_MCS4,
+ MPT_RATE_MCS5,
+ MPT_RATE_MCS6,
+ MPT_RATE_MCS7, /* 19 */
+ MPT_RATE_MCS8,
+ MPT_RATE_MCS9,
+ MPT_RATE_MCS10,
+ MPT_RATE_MCS11,
+ MPT_RATE_MCS12,
+ MPT_RATE_MCS13,
+ MPT_RATE_MCS14,
+ MPT_RATE_MCS15, /* 27 */
+ MPT_RATE_LAST
+};
+
+/* Represent Channel Width in HT Capabilities */
+enum HT_CHANNEL_WIDTH {
+ HT_CHANNEL_WIDTH_20 = 0,
+ HT_CHANNEL_WIDTH_40 = 1,
+};
+
+#define MAX_TX_PWR_INDEX_N_MODE 64 /* 0x3F */
+
+enum POWER_MODE {
+ POWER_LOW = 0,
+ POWER_NORMAL
+};
+
+#define RX_PKT_BROADCAST 1
+#define RX_PKT_DEST_ADDR 2
+#define RX_PKT_PHY_MATCH 3
+
+#define RPTMaxCount 0x000FFFFF
+
+/* parameter 1 : BitMask
+ * bit 0 : OFDM PPDU
+ * bit 1 : OFDM False Alarm
+ * bit 2 : OFDM MPDU OK
+ * bit 3 : OFDM MPDU Fail
+ * bit 4 : CCK PPDU
+ * bit 5 : CCK False Alarm
+ * bit 6 : CCK MPDU ok
+ * bit 7 : CCK MPDU fail
+ * bit 8 : HT PPDU counter
+ * bit 9 : HT false alarm
+ * bit 10 : HT MPDU total
+ * bit 11 : HT MPDU OK
+ * bit 12 : HT MPDU fail
+ * bit 15 : RX full drop
+ */
+enum RXPHY_BITMASK {
+ OFDM_PPDU_BIT = 0,
+ OFDM_MPDU_OK_BIT,
+ OFDM_MPDU_FAIL_BIT,
+ CCK_PPDU_BIT,
+ CCK_MPDU_OK_BIT,
+ CCK_MPDU_FAIL_BIT,
+ HT_PPDU_BIT,
+ HT_MPDU_BIT,
+ HT_MPDU_OK_BIT,
+ HT_MPDU_FAIL_BIT,
+};
+
+enum ENCRY_CTRL_STATE {
+ HW_CONTROL, /*hw encryption& decryption*/
+ SW_CONTROL, /*sw encryption& decryption*/
+ HW_ENCRY_SW_DECRY, /*hw encryption & sw decryption*/
+ SW_ENCRY_HW_DECRY /*sw encryption & hw decryption*/
+};
+
+/* Bandwidth Offset */
+#define HAL_PRIME_CHNL_OFFSET_DONT_CARE 0
+#define HAL_PRIME_CHNL_OFFSET_LOWER 1
+#define HAL_PRIME_CHNL_OFFSET_UPPER 2
+/*=======================================================================*/
+void mp871xinit(struct _adapter *padapter);
+void mp871xdeinit(struct _adapter *padapter);
+u32 r8712_bb_reg_read(struct _adapter *Adapter, u16 offset);
+u8 r8712_bb_reg_write(struct _adapter *Adapter, u16 offset, u32 value);
+u32 r8712_rf_reg_read(struct _adapter *Adapter, u8 path, u8 offset);
+u8 r8712_rf_reg_write(struct _adapter *Adapter, u8 path,
+ u8 offset, u32 value);
+u32 r8712_get_bb_reg(struct _adapter *Adapter, u16 offset, u32 bitmask);
+u8 r8712_set_bb_reg(struct _adapter *Adapter, u16 offset,
+ u32 bitmask, u32 value);
+u32 r8712_get_rf_reg(struct _adapter *Adapter, u8 path, u8 offset,
+ u32 bitmask);
+u8 r8712_set_rf_reg(struct _adapter *Adapter, u8 path, u8 offset,
+ u32 bitmask, u32 value);
+
+void r8712_SetChannel(struct _adapter *pAdapter);
+void r8712_SetTxPower(struct _adapter *pAdapte);
+void r8712_SetTxAGCOffset(struct _adapter *pAdapter, u32 ulTxAGCOffset);
+void r8712_SetDataRate(struct _adapter *pAdapter);
+void r8712_SwitchBandwidth(struct _adapter *pAdapter);
+void r8712_SwitchAntenna(struct _adapter *pAdapter);
+void r8712_SetCrystalCap(struct _adapter *pAdapter);
+void r8712_GetThermalMeter(struct _adapter *pAdapter, u32 *value);
+void r8712_SetContinuousTx(struct _adapter *pAdapter, u8 bStart);
+void r8712_SetSingleCarrierTx(struct _adapter *pAdapter, u8 bStart);
+void r8712_SetSingleToneTx(struct _adapter *pAdapter, u8 bStart);
+void r8712_SetCarrierSuppressionTx(struct _adapter *pAdapter, u8 bStart);
+void r8712_ResetPhyRxPktCount(struct _adapter *pAdapter);
+u32 r8712_GetPhyRxPktReceived(struct _adapter *pAdapter);
+u32 r8712_GetPhyRxPktCRC32Error(struct _adapter *pAdapter);
+
+#endif /*__RTL871X_MP_H_*/
+
diff --git a/drivers/staging/rtl8712/rtl871x_mp_ioctl.c b/drivers/staging/rtl8712/rtl871x_mp_ioctl.c
new file mode 100644
index 000000000..0b5461208
--- /dev/null
+++ b/drivers/staging/rtl8712/rtl871x_mp_ioctl.c
@@ -0,0 +1,929 @@
+/******************************************************************************
+ * rtl871x_mp_ioctl.c
+ *
+ * Copyright(c) 2007 - 2010 Realtek Corporation. All rights reserved.
+ * Linux device driver for RTL8192SU
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ * Modifications for inclusion into the Linux staging tree are
+ * Copyright(c) 2010 Larry Finger. All rights reserved.
+ *
+ * Contact information:
+ * WLAN FAE <wlanfae@realtek.com>
+ * Larry Finger <Larry.Finger@lwfinger.net>
+ *
+ ******************************************************************************/
+
+#include <linux/rndis.h>
+#include "osdep_service.h"
+#include "drv_types.h"
+#include "mlme_osdep.h"
+#include "rtl871x_mp.h"
+#include "rtl871x_mp_ioctl.h"
+
+uint oid_null_function(struct oid_par_priv *poid_par_priv)
+{
+ return RNDIS_STATUS_SUCCESS;
+}
+
+uint oid_rt_wireless_mode_hdl(struct oid_par_priv *poid_par_priv)
+{
+ uint status = RNDIS_STATUS_SUCCESS;
+ struct _adapter *Adapter = (struct _adapter *)
+ (poid_par_priv->adapter_context);
+
+ if (poid_par_priv->type_of_oid == SET_OID) {
+ if (poid_par_priv->information_buf_len >= sizeof(u8))
+ Adapter->registrypriv.wireless_mode =
+ *(u8 *)poid_par_priv->information_buf;
+ else
+ status = RNDIS_STATUS_INVALID_LENGTH;
+ } else if (poid_par_priv->type_of_oid == QUERY_OID) {
+ if (poid_par_priv->information_buf_len >= sizeof(u8)) {
+ *(u8 *)poid_par_priv->information_buf =
+ Adapter->registrypriv.wireless_mode;
+ *poid_par_priv->bytes_rw =
+ poid_par_priv->information_buf_len;
+ } else
+ status = RNDIS_STATUS_INVALID_LENGTH;
+ } else {
+ status = RNDIS_STATUS_NOT_ACCEPTED;
+ }
+ return status;
+}
+
+uint oid_rt_pro_write_bb_reg_hdl(struct oid_par_priv *poid_par_priv)
+{
+ struct _adapter *Adapter = (struct _adapter *)
+ (poid_par_priv->adapter_context);
+ struct bb_reg_param *pbbreg;
+ u16 offset;
+ u32 value;
+
+ if (poid_par_priv->type_of_oid != SET_OID)
+ return RNDIS_STATUS_NOT_ACCEPTED;
+ if (poid_par_priv->information_buf_len < sizeof(struct bb_reg_param))
+ return RNDIS_STATUS_INVALID_LENGTH;
+ pbbreg = (struct bb_reg_param *)(poid_par_priv->information_buf);
+ offset = (u16)(pbbreg->offset) & 0xFFF; /*0ffset :0x800~0xfff*/
+ if (offset < BB_REG_BASE_ADDR)
+ offset |= BB_REG_BASE_ADDR;
+ value = pbbreg->value;
+ r8712_bb_reg_write(Adapter, offset, value);
+ return RNDIS_STATUS_SUCCESS;
+}
+
+uint oid_rt_pro_read_bb_reg_hdl(struct oid_par_priv *poid_par_priv)
+{
+ struct _adapter *Adapter = (struct _adapter *)
+ (poid_par_priv->adapter_context);
+ struct bb_reg_param *pbbreg;
+ u16 offset;
+ u32 value;
+
+ if (poid_par_priv->type_of_oid != QUERY_OID)
+ return RNDIS_STATUS_NOT_ACCEPTED;
+ if (poid_par_priv->information_buf_len < sizeof(struct bb_reg_param))
+ return RNDIS_STATUS_INVALID_LENGTH;
+ pbbreg = (struct bb_reg_param *)(poid_par_priv->information_buf);
+ offset = (u16)(pbbreg->offset) & 0xFFF; /*0ffset :0x800~0xfff*/
+ if (offset < BB_REG_BASE_ADDR)
+ offset |= BB_REG_BASE_ADDR;
+ value = r8712_bb_reg_read(Adapter, offset);
+ pbbreg->value = value;
+ *poid_par_priv->bytes_rw = poid_par_priv->information_buf_len;
+ return RNDIS_STATUS_SUCCESS;
+}
+
+uint oid_rt_pro_write_rf_reg_hdl(struct oid_par_priv *poid_par_priv)
+{
+ struct _adapter *Adapter = (struct _adapter *)
+ (poid_par_priv->adapter_context);
+ struct rf_reg_param *pbbreg;
+ u8 path;
+ u8 offset;
+ u32 value;
+
+ if (poid_par_priv->type_of_oid != SET_OID)
+ return RNDIS_STATUS_NOT_ACCEPTED;
+ if (poid_par_priv->information_buf_len < sizeof(struct rf_reg_param))
+ return RNDIS_STATUS_INVALID_LENGTH;
+ pbbreg = (struct rf_reg_param *)(poid_par_priv->information_buf);
+ path = (u8)pbbreg->path;
+ if (path > RF_PATH_B)
+ return RNDIS_STATUS_NOT_ACCEPTED;
+ offset = (u8)pbbreg->offset;
+ value = pbbreg->value;
+ r8712_rf_reg_write(Adapter, path, offset, value);
+ return RNDIS_STATUS_SUCCESS;
+}
+
+uint oid_rt_pro_read_rf_reg_hdl(struct oid_par_priv *poid_par_priv)
+{
+ struct _adapter *Adapter = (struct _adapter *)
+ (poid_par_priv->adapter_context);
+ struct rf_reg_param *pbbreg;
+ u8 path;
+ u8 offset;
+ u32 value;
+
+ if (poid_par_priv->type_of_oid != QUERY_OID)
+ return RNDIS_STATUS_NOT_ACCEPTED;
+ if (poid_par_priv->information_buf_len < sizeof(struct rf_reg_param))
+ return RNDIS_STATUS_INVALID_LENGTH;
+ pbbreg = (struct rf_reg_param *)(poid_par_priv->information_buf);
+ path = (u8)pbbreg->path;
+ if (path > RF_PATH_B) /* 1T2R path_a /path_b */
+ return RNDIS_STATUS_NOT_ACCEPTED;
+ offset = (u8)pbbreg->offset;
+ value = r8712_rf_reg_read(Adapter, path, offset);
+ pbbreg->value = value;
+ *poid_par_priv->bytes_rw = poid_par_priv->information_buf_len;
+ return RNDIS_STATUS_SUCCESS;
+}
+
+/*This function initializes the DUT to the MP test mode*/
+static int mp_start_test(struct _adapter *padapter)
+{
+ struct mp_priv *pmppriv = &padapter->mppriv;
+ struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
+ struct wlan_network *tgt_network = &pmlmepriv->cur_network;
+ struct ndis_wlan_bssid_ex bssid;
+ struct sta_info *psta;
+ unsigned long length;
+ unsigned long irqL;
+ int res = _SUCCESS;
+
+ /* 3 1. initialize a new struct ndis_wlan_bssid_ex */
+ memcpy(bssid.MacAddress, pmppriv->network_macaddr, ETH_ALEN);
+ bssid.Ssid.SsidLength = 16;
+ memcpy(bssid.Ssid.Ssid, (unsigned char *)"mp_pseudo_adhoc",
+ bssid.Ssid.SsidLength);
+ bssid.InfrastructureMode = Ndis802_11IBSS;
+ bssid.NetworkTypeInUse = Ndis802_11DS;
+ bssid.IELength = 0;
+ length = r8712_get_ndis_wlan_bssid_ex_sz(&bssid);
+ if (length % 4) {
+ /*round up to multiple of 4 bytes.*/
+ bssid.Length = ((length >> 2) + 1) << 2;
+ } else
+ bssid.Length = length;
+ spin_lock_irqsave(&pmlmepriv->lock, irqL);
+ if (check_fwstate(pmlmepriv, WIFI_MP_STATE) == true)
+ goto end_of_mp_start_test;
+ /*init mp_start_test status*/
+ pmppriv->prev_fw_state = get_fwstate(pmlmepriv);
+ pmlmepriv->fw_state = WIFI_MP_STATE;
+ if (pmppriv->mode == _LOOPBOOK_MODE_)
+ set_fwstate(pmlmepriv, WIFI_MP_LPBK_STATE); /*append txdesc*/
+ set_fwstate(pmlmepriv, _FW_UNDER_LINKING);
+ /* 3 2. create a new psta for mp driver */
+ /* clear psta in the cur_network, if any */
+ psta = r8712_get_stainfo(&padapter->stapriv,
+ tgt_network->network.MacAddress);
+ if (psta)
+ r8712_free_stainfo(padapter, psta);
+ psta = r8712_alloc_stainfo(&padapter->stapriv, bssid.MacAddress);
+ if (psta == NULL) {
+ res = _FAIL;
+ goto end_of_mp_start_test;
+ }
+ /* 3 3. join psudo AdHoc */
+ tgt_network->join_res = 1;
+ tgt_network->aid = psta->aid = 1;
+ memcpy(&tgt_network->network, &bssid, length);
+ _clr_fwstate_(pmlmepriv, _FW_UNDER_LINKING);
+ r8712_os_indicate_connect(padapter);
+ /* Set to LINKED STATE for MP TRX Testing */
+ set_fwstate(pmlmepriv, _FW_LINKED);
+end_of_mp_start_test:
+ spin_unlock_irqrestore(&pmlmepriv->lock, irqL);
+ return res;
+}
+
+/*This function change the DUT from the MP test mode into normal mode */
+static int mp_stop_test(struct _adapter *padapter)
+{
+ struct mp_priv *pmppriv = &padapter->mppriv;
+ struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
+ struct wlan_network *tgt_network = &pmlmepriv->cur_network;
+ struct sta_info *psta;
+ unsigned long irqL;
+
+ spin_lock_irqsave(&pmlmepriv->lock, irqL);
+ if (check_fwstate(pmlmepriv, WIFI_MP_STATE) == false)
+ goto end_of_mp_stop_test;
+ /* 3 1. disconnect psudo AdHoc */
+ r8712_os_indicate_disconnect(padapter);
+ /* 3 2. clear psta used in mp test mode. */
+ psta = r8712_get_stainfo(&padapter->stapriv,
+ tgt_network->network.MacAddress);
+ if (psta)
+ r8712_free_stainfo(padapter, psta);
+ /* 3 3. return to normal state (default:station mode) */
+ pmlmepriv->fw_state = pmppriv->prev_fw_state; /* WIFI_STATION_STATE;*/
+ /*flush the cur_network*/
+ memset(tgt_network, 0, sizeof(struct wlan_network));
+end_of_mp_stop_test:
+ spin_unlock_irqrestore(&pmlmepriv->lock, irqL);
+ return _SUCCESS;
+}
+
+int mp_start_joinbss(struct _adapter *padapter, struct ndis_802_11_ssid *pssid)
+{
+ struct mp_priv *pmppriv = &padapter->mppriv;
+ struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
+ unsigned char res = _SUCCESS;
+
+ if (check_fwstate(pmlmepriv, WIFI_MP_STATE) == false)
+ return _FAIL;
+ if (check_fwstate(pmlmepriv, _FW_LINKED) == false)
+ return _FAIL;
+ _clr_fwstate_(pmlmepriv, _FW_LINKED);
+ res = r8712_setassocsta_cmd(padapter, pmppriv->network_macaddr);
+ set_fwstate(pmlmepriv, _FW_UNDER_LINKING);
+ return res;
+}
+
+uint oid_rt_pro_set_data_rate_hdl(struct oid_par_priv
+ *poid_par_priv)
+{
+ struct _adapter *Adapter = (struct _adapter *)
+ (poid_par_priv->adapter_context);
+ u32 ratevalue;
+
+ if (poid_par_priv->type_of_oid != SET_OID)
+ return RNDIS_STATUS_NOT_ACCEPTED;
+ if (poid_par_priv->information_buf_len != sizeof(u32))
+ return RNDIS_STATUS_INVALID_LENGTH;
+ ratevalue = *((u32 *)poid_par_priv->information_buf);
+ if (ratevalue >= MPT_RATE_LAST)
+ return RNDIS_STATUS_INVALID_DATA;
+ Adapter->mppriv.curr_rateidx = ratevalue;
+ r8712_SetDataRate(Adapter);
+ return RNDIS_STATUS_SUCCESS;
+}
+
+uint oid_rt_pro_start_test_hdl(struct oid_par_priv *poid_par_priv)
+{
+ struct _adapter *Adapter = (struct _adapter *)
+ (poid_par_priv->adapter_context);
+ uint status = RNDIS_STATUS_SUCCESS;
+ u32 mode;
+ u8 val8;
+
+ if (poid_par_priv->type_of_oid != SET_OID)
+ return RNDIS_STATUS_NOT_ACCEPTED;
+ mode = *((u32 *)poid_par_priv->information_buf);
+ Adapter->mppriv.mode = mode;/* 1 for loopback*/
+ if (mp_start_test(Adapter) == _FAIL)
+ status = RNDIS_STATUS_NOT_ACCEPTED;
+ r8712_write8(Adapter, MSR, 1); /* Link in ad hoc network, 0x1025004C */
+ r8712_write8(Adapter, RCR, 0); /* RCR : disable all pkt, 0x10250048 */
+ /* RCR disable Check BSSID, 0x1025004a */
+ r8712_write8(Adapter, RCR+2, 0x57);
+ /* disable RX filter map , mgt frames will put in RX FIFO 0 */
+ r8712_write16(Adapter, RXFLTMAP0, 0x0);
+ val8 = r8712_read8(Adapter, EE_9346CR);
+ if (!(val8 & _9356SEL)) { /*boot from EFUSE*/
+ r8712_efuse_reg_init(Adapter);
+ r8712_efuse_change_max_size(Adapter);
+ r8712_efuse_reg_uninit(Adapter);
+ }
+ return status;
+}
+
+uint oid_rt_pro_stop_test_hdl(struct oid_par_priv *poid_par_priv)
+{
+ struct _adapter *Adapter = (struct _adapter *)
+ (poid_par_priv->adapter_context);
+ uint status = RNDIS_STATUS_SUCCESS;
+
+ if (poid_par_priv->type_of_oid != SET_OID)
+ return RNDIS_STATUS_NOT_ACCEPTED;
+ if (mp_stop_test(Adapter) == _FAIL)
+ status = RNDIS_STATUS_NOT_ACCEPTED;
+ return status;
+}
+
+uint oid_rt_pro_set_channel_direct_call_hdl(struct oid_par_priv
+ *poid_par_priv)
+{
+ struct _adapter *Adapter = (struct _adapter *)
+ (poid_par_priv->adapter_context);
+ u32 Channel;
+
+ if (poid_par_priv->type_of_oid != SET_OID)
+ return RNDIS_STATUS_NOT_ACCEPTED;
+ if (poid_par_priv->information_buf_len != sizeof(u32))
+ return RNDIS_STATUS_INVALID_LENGTH;
+ Channel = *((u32 *)poid_par_priv->information_buf);
+ if (Channel > 14)
+ return RNDIS_STATUS_NOT_ACCEPTED;
+ Adapter->mppriv.curr_ch = Channel;
+ r8712_SetChannel(Adapter);
+ return RNDIS_STATUS_SUCCESS;
+}
+
+uint oid_rt_pro_set_antenna_bb_hdl(struct oid_par_priv *poid_par_priv)
+{
+ struct _adapter *Adapter = (struct _adapter *)
+ (poid_par_priv->adapter_context);
+ u32 antenna;
+
+ if (poid_par_priv->type_of_oid != SET_OID)
+ return RNDIS_STATUS_NOT_ACCEPTED;
+ if (poid_par_priv->information_buf_len != sizeof(u32))
+ return RNDIS_STATUS_INVALID_LENGTH;
+ antenna = *((u32 *)poid_par_priv->information_buf);
+ Adapter->mppriv.antenna_tx = (u16)((antenna & 0xFFFF0000) >> 16);
+ Adapter->mppriv.antenna_rx = (u16)(antenna & 0x0000FFFF);
+ r8712_SwitchAntenna(Adapter);
+ return RNDIS_STATUS_SUCCESS;
+}
+
+uint oid_rt_pro_set_tx_power_control_hdl(
+ struct oid_par_priv *poid_par_priv)
+{
+ struct _adapter *Adapter = (struct _adapter *)
+ (poid_par_priv->adapter_context);
+ u32 tx_pwr_idx;
+
+ if (poid_par_priv->type_of_oid != SET_OID)
+ return RNDIS_STATUS_NOT_ACCEPTED;
+ if (poid_par_priv->information_buf_len != sizeof(u32))
+ return RNDIS_STATUS_INVALID_LENGTH;
+ tx_pwr_idx = *((u32 *)poid_par_priv->information_buf);
+ if (tx_pwr_idx > MAX_TX_PWR_INDEX_N_MODE)
+ return RNDIS_STATUS_NOT_ACCEPTED;
+ Adapter->mppriv.curr_txpoweridx = (u8)tx_pwr_idx;
+ r8712_SetTxPower(Adapter);
+ return RNDIS_STATUS_SUCCESS;
+}
+
+uint oid_rt_pro_query_tx_packet_sent_hdl(
+ struct oid_par_priv *poid_par_priv)
+{
+ uint status = RNDIS_STATUS_SUCCESS;
+ struct _adapter *Adapter = (struct _adapter *)
+ (poid_par_priv->adapter_context);
+
+ if (poid_par_priv->type_of_oid != QUERY_OID) {
+ status = RNDIS_STATUS_NOT_ACCEPTED;
+ return status;
+ }
+ if (poid_par_priv->information_buf_len == sizeof(u32)) {
+ *(u32 *)poid_par_priv->information_buf =
+ Adapter->mppriv.tx_pktcount;
+ *poid_par_priv->bytes_rw = poid_par_priv->information_buf_len;
+ } else
+ status = RNDIS_STATUS_INVALID_LENGTH;
+ return status;
+}
+
+uint oid_rt_pro_query_rx_packet_received_hdl(
+ struct oid_par_priv *poid_par_priv)
+{
+ uint status = RNDIS_STATUS_SUCCESS;
+ struct _adapter *Adapter = (struct _adapter *)
+ (poid_par_priv->adapter_context);
+
+ if (poid_par_priv->type_of_oid != QUERY_OID) {
+ status = RNDIS_STATUS_NOT_ACCEPTED;
+ return status;
+ }
+ if (poid_par_priv->information_buf_len == sizeof(u32)) {
+ *(u32 *)poid_par_priv->information_buf =
+ Adapter->mppriv.rx_pktcount;
+ *poid_par_priv->bytes_rw = poid_par_priv->information_buf_len;
+ } else
+ status = RNDIS_STATUS_INVALID_LENGTH;
+ return status;
+}
+
+uint oid_rt_pro_query_rx_packet_crc32_error_hdl(
+ struct oid_par_priv *poid_par_priv)
+{
+ uint status = RNDIS_STATUS_SUCCESS;
+ struct _adapter *Adapter = (struct _adapter *)
+ (poid_par_priv->adapter_context);
+
+ if (poid_par_priv->type_of_oid != QUERY_OID) {
+ status = RNDIS_STATUS_NOT_ACCEPTED;
+ return status;
+ }
+ if (poid_par_priv->information_buf_len == sizeof(u32)) {
+ *(u32 *)poid_par_priv->information_buf =
+ Adapter->mppriv.rx_crcerrpktcount;
+ *poid_par_priv->bytes_rw = poid_par_priv->information_buf_len;
+ } else
+ status = RNDIS_STATUS_INVALID_LENGTH;
+ return status;
+}
+
+uint oid_rt_pro_reset_tx_packet_sent_hdl(struct oid_par_priv
+ *poid_par_priv)
+{
+ struct _adapter *Adapter = (struct _adapter *)
+ (poid_par_priv->adapter_context);
+
+ if (poid_par_priv->type_of_oid != SET_OID)
+ return RNDIS_STATUS_NOT_ACCEPTED;
+ Adapter->mppriv.tx_pktcount = 0;
+ return RNDIS_STATUS_SUCCESS;
+}
+
+uint oid_rt_pro_reset_rx_packet_received_hdl(struct oid_par_priv
+ *poid_par_priv)
+{
+ uint status = RNDIS_STATUS_SUCCESS;
+ struct _adapter *Adapter = (struct _adapter *)
+ (poid_par_priv->adapter_context);
+
+ if (poid_par_priv->type_of_oid != SET_OID)
+ return RNDIS_STATUS_NOT_ACCEPTED;
+ if (poid_par_priv->information_buf_len == sizeof(u32)) {
+ Adapter->mppriv.rx_pktcount = 0;
+ Adapter->mppriv.rx_crcerrpktcount = 0;
+ } else
+ status = RNDIS_STATUS_INVALID_LENGTH;
+ return status;
+}
+
+uint oid_rt_reset_phy_rx_packet_count_hdl(struct oid_par_priv
+ *poid_par_priv)
+{
+ struct _adapter *Adapter = (struct _adapter *)
+ (poid_par_priv->adapter_context);
+
+ if (poid_par_priv->type_of_oid != SET_OID)
+ return RNDIS_STATUS_NOT_ACCEPTED;
+ r8712_ResetPhyRxPktCount(Adapter);
+ return RNDIS_STATUS_SUCCESS;
+}
+
+uint oid_rt_get_phy_rx_packet_received_hdl(struct oid_par_priv
+ *poid_par_priv)
+{
+ struct _adapter *Adapter = (struct _adapter *)
+ (poid_par_priv->adapter_context);
+
+ if (poid_par_priv->type_of_oid != QUERY_OID)
+ return RNDIS_STATUS_NOT_ACCEPTED;
+ if (poid_par_priv->information_buf_len != sizeof(u32))
+ return RNDIS_STATUS_INVALID_LENGTH;
+ *(u32 *)poid_par_priv->information_buf =
+ r8712_GetPhyRxPktReceived(Adapter);
+ *poid_par_priv->bytes_rw = poid_par_priv->information_buf_len;
+ return RNDIS_STATUS_SUCCESS;
+}
+
+uint oid_rt_get_phy_rx_packet_crc32_error_hdl(struct oid_par_priv
+ *poid_par_priv)
+{
+ struct _adapter *Adapter = (struct _adapter *)
+ (poid_par_priv->adapter_context);
+
+ if (poid_par_priv->type_of_oid != QUERY_OID)
+ return RNDIS_STATUS_NOT_ACCEPTED;
+ if (poid_par_priv->information_buf_len != sizeof(u32))
+ return RNDIS_STATUS_INVALID_LENGTH;
+ *(u32 *)poid_par_priv->information_buf =
+ r8712_GetPhyRxPktCRC32Error(Adapter);
+ *poid_par_priv->bytes_rw = poid_par_priv->information_buf_len;
+ return RNDIS_STATUS_SUCCESS;
+}
+
+uint oid_rt_pro_set_modulation_hdl(struct oid_par_priv
+ *poid_par_priv)
+{
+ struct _adapter *Adapter = (struct _adapter *)
+ (poid_par_priv->adapter_context);
+
+ if (poid_par_priv->type_of_oid != SET_OID)
+ return RNDIS_STATUS_NOT_ACCEPTED;
+
+ Adapter->mppriv.curr_modem = *((u8 *)poid_par_priv->information_buf);
+ return RNDIS_STATUS_SUCCESS;
+}
+
+uint oid_rt_pro_set_continuous_tx_hdl(struct oid_par_priv
+ *poid_par_priv)
+{
+ struct _adapter *Adapter = (struct _adapter *)
+ (poid_par_priv->adapter_context);
+ u32 bStartTest;
+
+ if (poid_par_priv->type_of_oid != SET_OID)
+ return RNDIS_STATUS_NOT_ACCEPTED;
+ bStartTest = *((u32 *)poid_par_priv->information_buf);
+ r8712_SetContinuousTx(Adapter, (u8)bStartTest);
+ return RNDIS_STATUS_SUCCESS;
+}
+
+uint oid_rt_pro_set_single_carrier_tx_hdl(struct oid_par_priv
+ *poid_par_priv)
+{
+ struct _adapter *Adapter = (struct _adapter *)
+ (poid_par_priv->adapter_context);
+ u32 bStartTest;
+
+ if (poid_par_priv->type_of_oid != SET_OID)
+ return RNDIS_STATUS_NOT_ACCEPTED;
+ bStartTest = *((u32 *)poid_par_priv->information_buf);
+ r8712_SetSingleCarrierTx(Adapter, (u8)bStartTest);
+ return RNDIS_STATUS_SUCCESS;
+}
+
+uint oid_rt_pro_set_carrier_suppression_tx_hdl(struct oid_par_priv
+ *poid_par_priv)
+{
+ struct _adapter *Adapter = (struct _adapter *)
+ (poid_par_priv->adapter_context);
+ u32 bStartTest;
+
+ if (poid_par_priv->type_of_oid != SET_OID)
+ return RNDIS_STATUS_NOT_ACCEPTED;
+ bStartTest = *((u32 *)poid_par_priv->information_buf);
+ r8712_SetCarrierSuppressionTx(Adapter, (u8)bStartTest);
+ return RNDIS_STATUS_SUCCESS;
+}
+
+uint oid_rt_pro_set_single_tone_tx_hdl(struct oid_par_priv
+ *poid_par_priv)
+{
+ struct _adapter *Adapter = (struct _adapter *)
+ (poid_par_priv->adapter_context);
+ u32 bStartTest;
+
+ if (poid_par_priv->type_of_oid != SET_OID)
+ return RNDIS_STATUS_NOT_ACCEPTED;
+ bStartTest = *((u32 *)poid_par_priv->information_buf);
+ r8712_SetSingleToneTx(Adapter, (u8)bStartTest);
+ return RNDIS_STATUS_SUCCESS;
+}
+
+uint oid_rt_pro_read_register_hdl(struct oid_par_priv
+ *poid_par_priv)
+{
+ struct _adapter *Adapter = (struct _adapter *)
+ (poid_par_priv->adapter_context);
+ uint status = RNDIS_STATUS_SUCCESS;
+ struct mp_rw_reg *RegRWStruct;
+ u16 offset;
+
+ if (poid_par_priv->type_of_oid != QUERY_OID)
+ return RNDIS_STATUS_NOT_ACCEPTED;
+ RegRWStruct = (struct mp_rw_reg *)poid_par_priv->information_buf;
+ if ((RegRWStruct->offset >= 0x10250800) &&
+ (RegRWStruct->offset <= 0x10250FFF)) {
+ /*baseband register*/
+ /*0ffset :0x800~0xfff*/
+ offset = (u16)(RegRWStruct->offset) & 0xFFF;
+ RegRWStruct->value = r8712_bb_reg_read(Adapter, offset);
+ } else {
+ switch (RegRWStruct->width) {
+ case 1:
+ RegRWStruct->value = r8712_read8(Adapter,
+ RegRWStruct->offset);
+ break;
+ case 2:
+ RegRWStruct->value = r8712_read16(Adapter,
+ RegRWStruct->offset);
+ break;
+ case 4:
+ RegRWStruct->value = r8712_read32(Adapter,
+ RegRWStruct->offset);
+ break;
+ default:
+ status = RNDIS_STATUS_NOT_ACCEPTED;
+ break;
+ }
+ }
+ *poid_par_priv->bytes_rw = poid_par_priv->information_buf_len;
+ return status;
+}
+
+uint oid_rt_pro_write_register_hdl(struct oid_par_priv *poid_par_priv)
+{
+ struct _adapter *Adapter = (struct _adapter *)
+ (poid_par_priv->adapter_context);
+ uint status = RNDIS_STATUS_SUCCESS;
+ struct mp_rw_reg *RegRWStruct;
+ u16 offset;
+ u32 value;
+ u32 oldValue = 0;
+
+ if (poid_par_priv->type_of_oid != SET_OID)
+ return RNDIS_STATUS_NOT_ACCEPTED;
+ RegRWStruct = (struct mp_rw_reg *)poid_par_priv->information_buf;
+ if ((RegRWStruct->offset >= 0x10250800) &&
+ (RegRWStruct->offset <= 0x10250FFF)) {
+ /*baseband register*/
+ offset = (u16)(RegRWStruct->offset) & 0xFFF;
+ value = RegRWStruct->value;
+ switch (RegRWStruct->width) {
+ case 1:
+ oldValue = r8712_bb_reg_read(Adapter, offset);
+ oldValue &= 0xFFFFFF00;
+ value &= 0x000000FF;
+ value |= oldValue;
+ break;
+ case 2:
+ oldValue = r8712_bb_reg_read(Adapter, offset);
+ oldValue &= 0xFFFF0000;
+ value &= 0x0000FFFF;
+ value |= oldValue;
+ break;
+ }
+ r8712_bb_reg_write(Adapter, offset, value);
+ } else {
+ switch (RegRWStruct->width) {
+ case 1:
+ r8712_write8(Adapter, RegRWStruct->offset,
+ (unsigned char)RegRWStruct->value);
+ break;
+ case 2:
+ r8712_write16(Adapter, RegRWStruct->offset,
+ (unsigned short)RegRWStruct->value);
+ break;
+ case 4:
+ r8712_write32(Adapter, RegRWStruct->offset,
+ (unsigned int)RegRWStruct->value);
+ break;
+ default:
+ status = RNDIS_STATUS_NOT_ACCEPTED;
+ break;
+ }
+
+ if ((status == RNDIS_STATUS_SUCCESS) &&
+ (RegRWStruct->offset == HIMR) &&
+ (RegRWStruct->width == 4))
+ Adapter->ImrContent = RegRWStruct->value;
+ }
+ return status;
+}
+
+uint oid_rt_get_thermal_meter_hdl(struct oid_par_priv *poid_par_priv)
+{
+ struct _adapter *Adapter = (struct _adapter *)
+ (poid_par_priv->adapter_context);
+
+ if (poid_par_priv->type_of_oid != QUERY_OID)
+ return RNDIS_STATUS_NOT_ACCEPTED;
+
+ if (Adapter->mppriv.act_in_progress == true)
+ return RNDIS_STATUS_NOT_ACCEPTED;
+
+ if (poid_par_priv->information_buf_len < sizeof(u8))
+ return RNDIS_STATUS_INVALID_LENGTH;
+ /*init workparam*/
+ Adapter->mppriv.act_in_progress = true;
+ Adapter->mppriv.workparam.bcompleted = false;
+ Adapter->mppriv.workparam.act_type = MPT_GET_THERMAL_METER;
+ Adapter->mppriv.workparam.io_offset = 0;
+ Adapter->mppriv.workparam.io_value = 0xFFFFFFFF;
+ r8712_GetThermalMeter(Adapter, &Adapter->mppriv.workparam.io_value);
+ Adapter->mppriv.workparam.bcompleted = true;
+ Adapter->mppriv.act_in_progress = false;
+ *(u32 *)poid_par_priv->information_buf =
+ Adapter->mppriv.workparam.io_value;
+ *poid_par_priv->bytes_rw = sizeof(u32);
+ return RNDIS_STATUS_SUCCESS;
+}
+
+uint oid_rt_pro_read_efuse_hdl(struct oid_par_priv *poid_par_priv)
+{
+ struct _adapter *Adapter = (struct _adapter *)
+ (poid_par_priv->adapter_context);
+
+ uint status = RNDIS_STATUS_SUCCESS;
+
+ struct EFUSE_ACCESS_STRUCT *pefuse;
+ u8 *data;
+ u16 addr = 0, cnts = 0;
+
+ if (poid_par_priv->type_of_oid != QUERY_OID)
+ return RNDIS_STATUS_NOT_ACCEPTED;
+ if (poid_par_priv->information_buf_len <
+ sizeof(struct EFUSE_ACCESS_STRUCT))
+ return RNDIS_STATUS_INVALID_LENGTH;
+ pefuse = (struct EFUSE_ACCESS_STRUCT *)poid_par_priv->information_buf;
+ addr = pefuse->start_addr;
+ cnts = pefuse->cnts;
+ data = pefuse->data;
+ memset(data, 0xFF, cnts);
+ if ((addr > 511) || (cnts < 1) || (cnts > 512) || (addr + cnts) >
+ EFUSE_MAX_SIZE)
+ return RNDIS_STATUS_NOT_ACCEPTED;
+ if (r8712_efuse_access(Adapter, true, addr, cnts, data) == false)
+ status = RNDIS_STATUS_FAILURE;
+ *poid_par_priv->bytes_rw = poid_par_priv->information_buf_len;
+ return status;
+}
+/*------------------------------------------------------------------------*/
+uint oid_rt_pro_write_efuse_hdl(struct oid_par_priv *poid_par_priv)
+{
+ struct _adapter *Adapter = (struct _adapter *)
+ (poid_par_priv->adapter_context);
+
+ uint status = RNDIS_STATUS_SUCCESS;
+
+ struct EFUSE_ACCESS_STRUCT *pefuse;
+ u8 *data;
+ u16 addr = 0, cnts = 0;
+
+ if (poid_par_priv->type_of_oid != SET_OID)
+ return RNDIS_STATUS_NOT_ACCEPTED;
+
+ pefuse = (struct EFUSE_ACCESS_STRUCT *)poid_par_priv->information_buf;
+ addr = pefuse->start_addr;
+ cnts = pefuse->cnts;
+ data = pefuse->data;
+
+ if ((addr > 511) || (cnts < 1) || (cnts > 512) ||
+ (addr + cnts) > r8712_efuse_get_max_size(Adapter))
+ return RNDIS_STATUS_NOT_ACCEPTED;
+ if (r8712_efuse_access(Adapter, false, addr, cnts, data) == false)
+ status = RNDIS_STATUS_FAILURE;
+ return status;
+}
+/*----------------------------------------------------------------------*/
+
+uint oid_rt_get_efuse_current_size_hdl(struct oid_par_priv
+ *poid_par_priv)
+{
+ struct _adapter *Adapter = (struct _adapter *)
+ (poid_par_priv->adapter_context);
+
+ if (poid_par_priv->type_of_oid != QUERY_OID)
+ return RNDIS_STATUS_NOT_ACCEPTED;
+ if (poid_par_priv->information_buf_len < sizeof(int))
+ return RNDIS_STATUS_INVALID_LENGTH;
+ r8712_efuse_reg_init(Adapter);
+ *(int *)poid_par_priv->information_buf =
+ r8712_efuse_get_current_size(Adapter);
+ r8712_efuse_reg_uninit(Adapter);
+ *poid_par_priv->bytes_rw = poid_par_priv->information_buf_len;
+ return RNDIS_STATUS_SUCCESS;
+}
+
+uint oid_rt_get_efuse_max_size_hdl(struct oid_par_priv *poid_par_priv)
+{
+ struct _adapter *Adapter = (struct _adapter *)
+ (poid_par_priv->adapter_context);
+
+ if (poid_par_priv->type_of_oid != QUERY_OID)
+ return RNDIS_STATUS_NOT_ACCEPTED;
+ if (poid_par_priv->information_buf_len < sizeof(u32))
+ return RNDIS_STATUS_INVALID_LENGTH;
+ *(int *)poid_par_priv->information_buf =
+ r8712_efuse_get_max_size(Adapter);
+ *poid_par_priv->bytes_rw = poid_par_priv->information_buf_len;
+ return RNDIS_STATUS_SUCCESS;
+}
+
+uint oid_rt_pro_efuse_hdl(struct oid_par_priv *poid_par_priv)
+{
+ uint status = RNDIS_STATUS_SUCCESS;
+
+ if (poid_par_priv->type_of_oid == QUERY_OID)
+ status = oid_rt_pro_read_efuse_hdl(poid_par_priv);
+ else
+ status = oid_rt_pro_write_efuse_hdl(poid_par_priv);
+ return status;
+}
+
+uint oid_rt_pro_efuse_map_hdl(struct oid_par_priv *poid_par_priv)
+{
+ struct _adapter *Adapter = (struct _adapter *)
+ (poid_par_priv->adapter_context);
+ uint status = RNDIS_STATUS_SUCCESS;
+ u8 *data;
+
+ *poid_par_priv->bytes_rw = 0;
+ if (poid_par_priv->information_buf_len < EFUSE_MAP_MAX_SIZE)
+ return RNDIS_STATUS_INVALID_LENGTH;
+ data = (u8 *)poid_par_priv->information_buf;
+ if (poid_par_priv->type_of_oid == QUERY_OID) {
+ if (r8712_efuse_map_read(Adapter, 0, EFUSE_MAP_MAX_SIZE, data))
+ *poid_par_priv->bytes_rw = EFUSE_MAP_MAX_SIZE;
+ else
+ status = RNDIS_STATUS_FAILURE;
+ } else {
+ /* SET_OID */
+ if (r8712_efuse_reg_init(Adapter) == true) {
+ if (r8712_efuse_map_write(Adapter, 0,
+ EFUSE_MAP_MAX_SIZE, data))
+ *poid_par_priv->bytes_rw = EFUSE_MAP_MAX_SIZE;
+ else
+ status = RNDIS_STATUS_FAILURE;
+ r8712_efuse_reg_uninit(Adapter);
+ } else {
+ status = RNDIS_STATUS_FAILURE;
+ }
+ }
+ return status;
+}
+
+uint oid_rt_set_bandwidth_hdl(struct oid_par_priv *poid_par_priv)
+{
+ struct _adapter *Adapter = (struct _adapter *)
+ (poid_par_priv->adapter_context);
+ u32 bandwidth;
+
+ if (poid_par_priv->type_of_oid != SET_OID)
+ return RNDIS_STATUS_NOT_ACCEPTED;
+ if (poid_par_priv->information_buf_len < sizeof(u32))
+ return RNDIS_STATUS_INVALID_LENGTH;
+ bandwidth = *((u32 *)poid_par_priv->information_buf);/*4*/
+ if (bandwidth != HT_CHANNEL_WIDTH_20)
+ bandwidth = HT_CHANNEL_WIDTH_40;
+ Adapter->mppriv.curr_bandwidth = (u8)bandwidth;
+ r8712_SwitchBandwidth(Adapter);
+ return RNDIS_STATUS_SUCCESS;
+}
+
+uint oid_rt_set_rx_packet_type_hdl(struct oid_par_priv
+ *poid_par_priv)
+{
+ struct _adapter *Adapter = (struct _adapter *)
+ (poid_par_priv->adapter_context);
+ u8 rx_pkt_type;
+ u32 rcr_val32;
+
+ if (poid_par_priv->type_of_oid != SET_OID)
+ return RNDIS_STATUS_NOT_ACCEPTED;
+ if (poid_par_priv->information_buf_len < sizeof(u8))
+ return RNDIS_STATUS_INVALID_LENGTH;
+ rx_pkt_type = *((u8 *)poid_par_priv->information_buf);/*4*/
+ rcr_val32 = r8712_read32(Adapter, RCR);/*RCR = 0x10250048*/
+ rcr_val32 &= ~(RCR_CBSSID | RCR_AB | RCR_AM | RCR_APM | RCR_AAP);
+ switch (rx_pkt_type) {
+ case RX_PKT_BROADCAST:
+ rcr_val32 |= (RCR_AB | RCR_AM | RCR_APM | RCR_AAP | RCR_ACRC32);
+ break;
+ case RX_PKT_DEST_ADDR:
+ rcr_val32 |= (RCR_AB | RCR_AM | RCR_APM | RCR_AAP | RCR_ACRC32);
+ break;
+ case RX_PKT_PHY_MATCH:
+ rcr_val32 |= (RCR_APM|RCR_ACRC32);
+ break;
+ default:
+ rcr_val32 &= ~(RCR_AAP |
+ RCR_APM |
+ RCR_AM |
+ RCR_AB |
+ RCR_ACRC32);
+ break;
+ }
+ if (rx_pkt_type == RX_PKT_DEST_ADDR)
+ Adapter->mppriv.check_mp_pkt = 1;
+ else
+ Adapter->mppriv.check_mp_pkt = 0;
+ r8712_write32(Adapter, RCR, rcr_val32);
+ return RNDIS_STATUS_SUCCESS;
+}
+
+/*--------------------------------------------------------------------------*/
+/*Linux*/
+unsigned int mp_ioctl_xmit_packet_hdl(struct oid_par_priv *poid_par_priv)
+{
+ return _SUCCESS;
+}
+/*-------------------------------------------------------------------------*/
+uint oid_rt_set_power_down_hdl(struct oid_par_priv *poid_par_priv)
+{
+ if (poid_par_priv->type_of_oid != SET_OID)
+ return RNDIS_STATUS_NOT_ACCEPTED;
+ /*CALL the power_down function*/
+ return RNDIS_STATUS_SUCCESS;
+}
+
+/*-------------------------------------------------------------------------- */
+uint oid_rt_get_power_mode_hdl(struct oid_par_priv *poid_par_priv)
+{
+ struct _adapter *Adapter = (struct _adapter *)
+ (poid_par_priv->adapter_context);
+
+ if (poid_par_priv->type_of_oid != QUERY_OID)
+ return RNDIS_STATUS_NOT_ACCEPTED;
+ if (poid_par_priv->information_buf_len < sizeof(u32))
+ return RNDIS_STATUS_INVALID_LENGTH;
+ *(int *)poid_par_priv->information_buf =
+ Adapter->registrypriv.low_power ? POWER_LOW : POWER_NORMAL;
+ *poid_par_priv->bytes_rw = poid_par_priv->information_buf_len;
+ return RNDIS_STATUS_SUCCESS;
+}
diff --git a/drivers/staging/rtl8712/rtl871x_mp_ioctl.h b/drivers/staging/rtl8712/rtl871x_mp_ioctl.h
new file mode 100644
index 000000000..8e7c7f8b6
--- /dev/null
+++ b/drivers/staging/rtl8712/rtl871x_mp_ioctl.h
@@ -0,0 +1,434 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2010 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ * Modifications for inclusion into the Linux staging tree are
+ * Copyright(c) 2010 Larry Finger. All rights reserved.
+ *
+ * Contact information:
+ * WLAN FAE <wlanfae@realtek.com>
+ * Larry Finger <Larry.Finger@lwfinger.net>
+ *
+ ******************************************************************************/
+#ifndef _RTL871X_MP_IOCTL_H
+#define _RTL871X_MP_IOCTL_H
+
+#include "osdep_service.h"
+#include "drv_types.h"
+#include "mp_custom_oid.h"
+#include "rtl871x_ioctl.h"
+#include "rtl871x_ioctl_rtl.h"
+#include "rtl8712_efuse.h"
+
+#define TESTFWCMDNUMBER 1000000
+#define TEST_H2CINT_WAIT_TIME 500
+#define TEST_C2HINT_WAIT_TIME 500
+#define HCI_TEST_SYSCFG_HWMASK 1
+#define _BUSCLK_40M (4 << 2)
+
+struct CFG_DBG_MSG_STRUCT {
+ u32 DebugLevel;
+ u32 DebugComponent_H32;
+ u32 DebugComponent_L32;
+};
+
+struct mp_rw_reg {
+ uint offset;
+ uint width;
+ u32 value;
+};
+
+/* for OID_RT_PRO_READ16_EEPROM & OID_RT_PRO_WRITE16_EEPROM */
+struct eeprom_rw_param {
+ uint offset;
+ u16 value;
+};
+
+struct EFUSE_ACCESS_STRUCT {
+ u16 start_addr;
+ u16 cnts;
+ u8 data[0];
+};
+
+struct burst_rw_reg {
+ uint offset;
+ uint len;
+ u8 Data[256];
+};
+
+struct usb_vendor_req {
+ u8 bRequest;
+ u16 wValue;
+ u16 wIndex;
+ u16 wLength;
+ u8 u8Dir;/*0:OUT, 1:IN */
+ u8 u8InData;
+};
+
+struct DR_VARIABLE_STRUCT {
+ u8 offset;
+ u32 variable;
+};
+
+int mp_start_joinbss(struct _adapter *padapter, struct ndis_802_11_ssid *pssid);
+
+/* oid_rtl_seg_87_11_00 */
+uint oid_rt_pro_read_register_hdl(struct oid_par_priv *poid_par_priv);
+uint oid_rt_pro_write_register_hdl(struct oid_par_priv *poid_par_priv);
+/* oid_rtl_seg_81_80_00 */
+uint oid_rt_pro_set_data_rate_hdl(
+ struct oid_par_priv *poid_par_priv);
+uint oid_rt_pro_start_test_hdl(struct oid_par_priv *poid_par_priv);
+uint oid_rt_pro_stop_test_hdl(struct oid_par_priv *poid_par_priv);
+uint oid_rt_pro_set_channel_direct_call_hdl(
+ struct oid_par_priv *poid_par_priv);
+uint oid_rt_pro_set_antenna_bb_hdl(
+ struct oid_par_priv *poid_par_priv);
+uint oid_rt_pro_set_tx_power_control_hdl(
+ struct oid_par_priv *poid_par_priv);
+/* oid_rtl_seg_81_80_20 */
+uint oid_rt_pro_query_tx_packet_sent_hdl(
+ struct oid_par_priv *poid_par_priv);
+uint oid_rt_pro_query_rx_packet_received_hdl(
+ struct oid_par_priv *poid_par_priv);
+uint oid_rt_pro_query_rx_packet_crc32_error_hdl(
+ struct oid_par_priv *poid_par_priv);
+uint oid_rt_pro_reset_tx_packet_sent_hdl(
+ struct oid_par_priv *poid_par_priv);
+uint oid_rt_pro_reset_rx_packet_received_hdl(
+ struct oid_par_priv *poid_par_priv);
+uint oid_rt_pro_set_modulation_hdl(struct oid_par_priv *poid_par_priv);
+uint oid_rt_pro_set_continuous_tx_hdl(
+ struct oid_par_priv *poid_par_priv);
+uint oid_rt_pro_set_single_carrier_tx_hdl(
+ struct oid_par_priv *poid_par_priv);
+uint oid_rt_pro_set_carrier_suppression_tx_hdl(
+ struct oid_par_priv *poid_par_priv);
+uint oid_rt_pro_set_single_tone_tx_hdl(
+ struct oid_par_priv *poid_par_priv);
+/* oid_rtl_seg_81_87 */
+uint oid_rt_pro_write_bb_reg_hdl(struct oid_par_priv *poid_par_priv);
+uint oid_rt_pro_read_bb_reg_hdl(struct oid_par_priv *poid_par_priv);
+uint oid_rt_pro_write_rf_reg_hdl(struct oid_par_priv *poid_par_priv);
+uint oid_rt_pro_read_rf_reg_hdl(struct oid_par_priv *poid_par_priv);
+/* oid_rtl_seg_81_85 */
+uint oid_rt_wireless_mode_hdl(struct oid_par_priv *poid_par_priv);
+uint oid_rt_pro_read_efuse_hdl(struct oid_par_priv *poid_par_priv);
+uint oid_rt_pro_write_efuse_hdl(struct oid_par_priv *poid_par_priv);
+uint oid_rt_get_efuse_current_size_hdl(
+ struct oid_par_priv *poid_par_priv);
+uint oid_rt_pro_efuse_hdl(struct oid_par_priv *poid_par_priv);
+uint oid_rt_pro_efuse_map_hdl(struct oid_par_priv *poid_par_priv);
+uint oid_rt_set_bandwidth_hdl(struct oid_par_priv *poid_par_priv);
+uint oid_rt_set_rx_packet_type_hdl(struct oid_par_priv *poid_par_priv);
+uint oid_rt_get_efuse_max_size_hdl(struct oid_par_priv *poid_par_priv);
+uint oid_rt_get_thermal_meter_hdl(
+ struct oid_par_priv *poid_par_priv);
+uint oid_rt_reset_phy_rx_packet_count_hdl(
+ struct oid_par_priv *poid_par_priv);
+uint oid_rt_get_phy_rx_packet_received_hdl(
+ struct oid_par_priv *poid_par_priv);
+uint oid_rt_get_phy_rx_packet_crc32_error_hdl(
+ struct oid_par_priv *poid_par_priv);
+uint oid_rt_set_power_down_hdl(
+ struct oid_par_priv *poid_par_priv);
+uint oid_rt_get_power_mode_hdl(
+ struct oid_par_priv *poid_par_priv);
+#ifdef _RTL871X_MP_IOCTL_C_ /* CAUTION!!! */
+/* This ifdef _MUST_ be left in!! */
+static const struct oid_obj_priv oid_rtl_seg_81_80_00[] = {
+ {1, &oid_null_function}, /*0x00 OID_RT_PRO_RESET_DUT */
+ {1, &oid_rt_pro_set_data_rate_hdl}, /*0x01*/
+ {1, &oid_rt_pro_start_test_hdl},/*0x02*/
+ {1, &oid_rt_pro_stop_test_hdl}, /*0x03*/
+ {1, &oid_null_function}, /*0x04 OID_RT_PRO_SET_PREAMBLE*/
+ {1, &oid_null_function}, /*0x05 OID_RT_PRO_SET_SCRAMBLER*/
+ {1, &oid_null_function}, /*0x06 OID_RT_PRO_SET_FILTER_BB*/
+ {1, &oid_null_function}, /*0x07
+ * OID_RT_PRO_SET_MANUAL_DIVERS_BB*/
+ {1, &oid_rt_pro_set_channel_direct_call_hdl}, /*0x08*/
+ {1, &oid_null_function}, /*0x09
+ * OID_RT_PRO_SET_SLEEP_MODE_DIRECT_CALL*/
+ {1, &oid_null_function}, /*0x0A
+ * OID_RT_PRO_SET_WAKE_MODE_DIRECT_CALL*/
+ {1, &oid_rt_pro_set_continuous_tx_hdl}, /*0x0B
+ * OID_RT_PRO_SET_TX_CONTINUOUS_DIRECT_CALL*/
+ {1, &oid_rt_pro_set_single_carrier_tx_hdl}, /*0x0C
+ * OID_RT_PRO_SET_SINGLE_CARRIER_TX_CONTINUOUS*/
+ {1, &oid_null_function}, /*0x0D
+ * OID_RT_PRO_SET_TX_ANTENNA_BB*/
+ {1, &oid_rt_pro_set_antenna_bb_hdl}, /*0x0E*/
+ {1, &oid_null_function}, /*0x0F OID_RT_PRO_SET_CR_SCRAMBLER*/
+ {1, &oid_null_function}, /*0x10 OID_RT_PRO_SET_CR_NEW_FILTER*/
+ {1, &oid_rt_pro_set_tx_power_control_hdl}, /*0x11
+ * OID_RT_PRO_SET_TX_POWER_CONTROL*/
+ {1, &oid_null_function}, /*0x12 OID_RT_PRO_SET_CR_TX_CONFIG*/
+ {1, &oid_null_function}, /*0x13
+ * OID_RT_PRO_GET_TX_POWER_CONTROL*/
+ {1, &oid_null_function}, /*0x14
+ * OID_RT_PRO_GET_CR_SIGNAL_QUALITY*/
+ {1, &oid_null_function}, /*0x15 OID_RT_PRO_SET_CR_SETPOINT*/
+ {1, &oid_null_function}, /*0x16 OID_RT_PRO_SET_INTEGRATOR*/
+ {1, &oid_null_function}, /*0x17 OID_RT_PRO_SET_SIGNAL_QUALITY*/
+ {1, &oid_null_function}, /*0x18 OID_RT_PRO_GET_INTEGRATOR*/
+ {1, &oid_null_function}, /*0x19 OID_RT_PRO_GET_SIGNAL_QUALITY*/
+ {1, &oid_null_function}, /*0x1A OID_RT_PRO_QUERY_EEPROM_TYPE*/
+ {1, &oid_null_function}, /*0x1B OID_RT_PRO_WRITE_MAC_ADDRESS*/
+ {1, &oid_null_function}, /*0x1C OID_RT_PRO_READ_MAC_ADDRESS*/
+ {1, &oid_null_function}, /*0x1D OID_RT_PRO_WRITE_CIS_DATA*/
+ {1, &oid_null_function}, /*0x1E OID_RT_PRO_READ_CIS_DATA*/
+ {1, &oid_null_function} /*0x1F OID_RT_PRO_WRITE_POWER_CONTROL*/
+};
+
+static const struct oid_obj_priv oid_rtl_seg_81_80_20[] = {
+ {1, &oid_null_function}, /*0x20 OID_RT_PRO_READ_POWER_CONTROL*/
+ {1, &oid_null_function}, /*0x21 OID_RT_PRO_WRITE_EEPROM*/
+ {1, &oid_null_function}, /*0x22 OID_RT_PRO_READ_EEPROM*/
+ {1, &oid_rt_pro_reset_tx_packet_sent_hdl}, /*0x23*/
+ {1, &oid_rt_pro_query_tx_packet_sent_hdl}, /*0x24*/
+ {1, &oid_rt_pro_reset_rx_packet_received_hdl}, /*0x25*/
+ {1, &oid_rt_pro_query_rx_packet_received_hdl}, /*0x26*/
+ {1, &oid_rt_pro_query_rx_packet_crc32_error_hdl},/*0x27*/
+ {1, &oid_null_function}, /*0x28
+ *OID_RT_PRO_QUERY_CURRENT_ADDRESS*/
+ {1, &oid_null_function}, /*0x29
+ *OID_RT_PRO_QUERY_PERMANENT_ADDRESS*/
+ {1, &oid_null_function}, /*0x2A
+ *OID_RT_PRO_SET_PHILIPS_RF_PARAMETERS*/
+ {1, &oid_rt_pro_set_carrier_suppression_tx_hdl},/*0x2B
+ *OID_RT_PRO_SET_CARRIER_SUPPRESSION_TX*/
+ {1, &oid_null_function}, /*0x2C OID_RT_PRO_RECEIVE_PACKET*/
+ {1, &oid_null_function}, /*0x2D OID_RT_PRO_WRITE_EEPROM_BYTE*/
+ {1, &oid_null_function}, /*0x2E OID_RT_PRO_READ_EEPROM_BYTE*/
+ {1, &oid_rt_pro_set_modulation_hdl} /*0x2F*/
+};
+
+static const struct oid_obj_priv oid_rtl_seg_81_80_40[] = {
+ {1, &oid_null_function}, /*0x40*/
+ {1, &oid_null_function}, /*0x41*/
+ {1, &oid_null_function}, /*0x42*/
+ {1, &oid_rt_pro_set_single_tone_tx_hdl}, /*0x43*/
+ {1, &oid_null_function}, /*0x44*/
+ {1, &oid_null_function} /*0x45*/
+};
+
+static const struct oid_obj_priv oid_rtl_seg_81_80_80[] = {
+ {1, &oid_null_function}, /*0x80 OID_RT_DRIVER_OPTION*/
+ {1, &oid_null_function}, /*0x81 OID_RT_RF_OFF*/
+ {1, &oid_null_function} /*0x82 OID_RT_AUTH_STATUS*/
+
+};
+
+static const struct oid_obj_priv oid_rtl_seg_81_85[] = {
+ {1, &oid_rt_wireless_mode_hdl} /*0x00 OID_RT_WIRELESS_MODE*/
+};
+
+#else /* _RTL871X_MP_IOCTL_C_ */
+extern struct oid_obj_priv oid_rtl_seg_81_80_00[32];
+extern struct oid_obj_priv oid_rtl_seg_81_80_20[16];
+extern struct oid_obj_priv oid_rtl_seg_81_80_40[6];
+extern struct oid_obj_priv oid_rtl_seg_81_80_80[3];
+extern struct oid_obj_priv oid_rtl_seg_81_85[1];
+extern struct oid_obj_priv oid_rtl_seg_81_87[5];
+extern struct oid_obj_priv oid_rtl_seg_87_11_00[32];
+extern struct oid_obj_priv oid_rtl_seg_87_11_20[5];
+extern struct oid_obj_priv oid_rtl_seg_87_11_50[2];
+extern struct oid_obj_priv oid_rtl_seg_87_11_80[1];
+extern struct oid_obj_priv oid_rtl_seg_87_11_B0[1];
+extern struct oid_obj_priv oid_rtl_seg_87_11_F0[16];
+extern struct oid_obj_priv oid_rtl_seg_87_12_00[32];
+
+#endif /* _RTL871X_MP_IOCTL_C_ */
+
+
+enum MP_MODE {
+ MP_START_MODE,
+ MP_STOP_MODE,
+ MP_ERR_MODE
+};
+
+struct rwreg_param {
+ unsigned int offset;
+ unsigned int width;
+ unsigned int value;
+};
+
+struct bbreg_param {
+ unsigned int offset;
+ unsigned int phymask;
+ unsigned int value;
+};
+
+struct txpower_param {
+ unsigned int pwr_index;
+};
+
+struct datarate_param {
+ unsigned int rate_index;
+};
+
+struct rfintfs_parm {
+ unsigned int rfintfs;
+};
+
+struct mp_xmit_packet {
+ unsigned int len;
+};
+
+struct psmode_param {
+ unsigned int ps_mode;
+ unsigned int smart_ps;
+};
+
+struct mp_ioctl_handler {
+ unsigned int paramsize;
+ unsigned int (*handler)(struct oid_par_priv *poid_par_priv);
+ unsigned int oid;
+};
+
+struct mp_ioctl_param {
+ unsigned int subcode;
+ unsigned int len;
+ unsigned char data[0];
+};
+
+#define GEN_MP_IOCTL_SUBCODE(code) _MP_IOCTL_ ## code ## _CMD_
+
+enum RTL871X_MP_IOCTL_SUBCODE {
+ GEN_MP_IOCTL_SUBCODE(MP_START), /*0*/
+ GEN_MP_IOCTL_SUBCODE(MP_STOP), /*1*/
+ GEN_MP_IOCTL_SUBCODE(READ_REG), /*2*/
+ GEN_MP_IOCTL_SUBCODE(WRITE_REG),
+ GEN_MP_IOCTL_SUBCODE(SET_CHANNEL), /*4*/
+ GEN_MP_IOCTL_SUBCODE(SET_TXPOWER), /*5*/
+ GEN_MP_IOCTL_SUBCODE(SET_DATARATE), /*6*/
+ GEN_MP_IOCTL_SUBCODE(READ_BB_REG), /*7*/
+ GEN_MP_IOCTL_SUBCODE(WRITE_BB_REG),
+ GEN_MP_IOCTL_SUBCODE(READ_RF_REG), /*9*/
+ GEN_MP_IOCTL_SUBCODE(WRITE_RF_REG),
+ GEN_MP_IOCTL_SUBCODE(SET_RF_INTFS),
+ GEN_MP_IOCTL_SUBCODE(IOCTL_XMIT_PACKET), /*12*/
+ GEN_MP_IOCTL_SUBCODE(PS_STATE), /*13*/
+ GEN_MP_IOCTL_SUBCODE(READ16_EEPROM), /*14*/
+ GEN_MP_IOCTL_SUBCODE(WRITE16_EEPROM), /*15*/
+ GEN_MP_IOCTL_SUBCODE(SET_PTM), /*16*/
+ GEN_MP_IOCTL_SUBCODE(READ_TSSI), /*17*/
+ GEN_MP_IOCTL_SUBCODE(CNTU_TX), /*18*/
+ GEN_MP_IOCTL_SUBCODE(SET_BANDWIDTH), /*19*/
+ GEN_MP_IOCTL_SUBCODE(SET_RX_PKT_TYPE), /*20*/
+ GEN_MP_IOCTL_SUBCODE(RESET_PHY_RX_PKT_CNT), /*21*/
+ GEN_MP_IOCTL_SUBCODE(GET_PHY_RX_PKT_RECV), /*22*/
+ GEN_MP_IOCTL_SUBCODE(GET_PHY_RX_PKT_ERROR), /*23*/
+ GEN_MP_IOCTL_SUBCODE(SET_POWER_DOWN), /*24*/
+ GEN_MP_IOCTL_SUBCODE(GET_THERMAL_METER), /*25*/
+ GEN_MP_IOCTL_SUBCODE(GET_POWER_MODE), /*26*/
+ GEN_MP_IOCTL_SUBCODE(EFUSE), /*27*/
+ GEN_MP_IOCTL_SUBCODE(EFUSE_MAP), /*28*/
+ GEN_MP_IOCTL_SUBCODE(GET_EFUSE_MAX_SIZE), /*29*/
+ GEN_MP_IOCTL_SUBCODE(GET_EFUSE_CURRENT_SIZE), /*30*/
+ GEN_MP_IOCTL_SUBCODE(SC_TX), /*31*/
+ GEN_MP_IOCTL_SUBCODE(CS_TX), /*32*/
+ GEN_MP_IOCTL_SUBCODE(ST_TX), /*33*/
+ GEN_MP_IOCTL_SUBCODE(SET_ANTENNA), /*34*/
+ MAX_MP_IOCTL_SUBCODE,
+};
+
+unsigned int mp_ioctl_xmit_packet_hdl(struct oid_par_priv *poid_par_priv);
+
+#ifdef _RTL871X_MP_IOCTL_C_ /* CAUTION!!! */
+/* This ifdef _MUST_ be left in!! */
+
+static struct mp_ioctl_handler mp_ioctl_hdl[] = {
+ {sizeof(u32), oid_rt_pro_start_test_hdl,
+ OID_RT_PRO_START_TEST},/*0*/
+ {sizeof(u32), oid_rt_pro_stop_test_hdl,
+ OID_RT_PRO_STOP_TEST},/*1*/
+ {sizeof(struct rwreg_param),
+ oid_rt_pro_read_register_hdl,
+ OID_RT_PRO_READ_REGISTER},/*2*/
+ {sizeof(struct rwreg_param),
+ oid_rt_pro_write_register_hdl,
+ OID_RT_PRO_WRITE_REGISTER},
+ {sizeof(u32),
+ oid_rt_pro_set_channel_direct_call_hdl,
+ OID_RT_PRO_SET_CHANNEL_DIRECT_CALL},
+ {sizeof(struct txpower_param),
+ oid_rt_pro_set_tx_power_control_hdl,
+ OID_RT_PRO_SET_TX_POWER_CONTROL},
+ {sizeof(u32),
+ oid_rt_pro_set_data_rate_hdl,
+ OID_RT_PRO_SET_DATA_RATE},
+ {sizeof(struct bb_reg_param),
+ oid_rt_pro_read_bb_reg_hdl,
+ OID_RT_PRO_READ_BB_REG},/*7*/
+ {sizeof(struct bb_reg_param),
+ oid_rt_pro_write_bb_reg_hdl,
+ OID_RT_PRO_WRITE_BB_REG},
+ {sizeof(struct rwreg_param),
+ oid_rt_pro_read_rf_reg_hdl,
+ OID_RT_PRO_RF_READ_REGISTRY},/*9*/
+ {sizeof(struct rwreg_param),
+ oid_rt_pro_write_rf_reg_hdl,
+ OID_RT_PRO_RF_WRITE_REGISTRY},
+ {sizeof(struct rfintfs_parm), NULL, 0},
+ {0, &mp_ioctl_xmit_packet_hdl, 0},/*12*/
+ {sizeof(struct psmode_param), NULL, 0},/*13*/
+ {sizeof(struct eeprom_rw_param), NULL, 0},/*14*/
+ {sizeof(struct eeprom_rw_param), NULL, 0},/*15*/
+ {sizeof(unsigned char), NULL, 0},/*16*/
+ {sizeof(u32), NULL, 0},/*17*/
+ {sizeof(u32), oid_rt_pro_set_continuous_tx_hdl,
+ OID_RT_PRO_SET_CONTINUOUS_TX},/*18*/
+ {sizeof(u32), oid_rt_set_bandwidth_hdl,
+ OID_RT_SET_BANDWIDTH},/*19*/
+ {sizeof(u32), oid_rt_set_rx_packet_type_hdl,
+ OID_RT_SET_RX_PACKET_TYPE},/*20*/
+ {0, oid_rt_reset_phy_rx_packet_count_hdl,
+ OID_RT_RESET_PHY_RX_PACKET_COUNT},/*21*/
+ {sizeof(u32), oid_rt_get_phy_rx_packet_received_hdl,
+ OID_RT_GET_PHY_RX_PACKET_RECEIVED},/*22*/
+ {sizeof(u32), oid_rt_get_phy_rx_packet_crc32_error_hdl,
+ OID_RT_GET_PHY_RX_PACKET_CRC32_ERROR},/*23*/
+ {sizeof(unsigned char), oid_rt_set_power_down_hdl,
+ OID_RT_SET_POWER_DOWN},/*24*/
+ {sizeof(u32), oid_rt_get_thermal_meter_hdl,
+ OID_RT_PRO_GET_THERMAL_METER},/*25*/
+ {sizeof(u32), oid_rt_get_power_mode_hdl,
+ OID_RT_GET_POWER_MODE},/*26*/
+ {sizeof(struct EFUSE_ACCESS_STRUCT),
+ oid_rt_pro_efuse_hdl, OID_RT_PRO_EFUSE},/*27*/
+ {EFUSE_MAP_MAX_SIZE, oid_rt_pro_efuse_map_hdl,
+ OID_RT_PRO_EFUSE_MAP},/*28*/
+ {sizeof(u32), oid_rt_get_efuse_max_size_hdl,
+ OID_RT_GET_EFUSE_MAX_SIZE},/*29*/
+ {sizeof(u32), oid_rt_get_efuse_current_size_hdl,
+ OID_RT_GET_EFUSE_CURRENT_SIZE},/*30*/
+ {sizeof(u32), oid_rt_pro_set_single_carrier_tx_hdl,
+ OID_RT_PRO_SET_SINGLE_CARRIER_TX},/*31*/
+ {sizeof(u32), oid_rt_pro_set_carrier_suppression_tx_hdl,
+ OID_RT_PRO_SET_CARRIER_SUPPRESSION_TX},/*32*/
+ {sizeof(u32), oid_rt_pro_set_single_tone_tx_hdl,
+ OID_RT_PRO_SET_SINGLE_TONE_TX},/*33*/
+ {sizeof(u32), oid_rt_pro_set_antenna_bb_hdl,
+ OID_RT_PRO_SET_ANTENNA_BB},/*34*/
+};
+
+#else /* _RTL871X_MP_IOCTL_C_ */
+extern struct mp_ioctl_handler mp_ioctl_hdl[];
+#endif /* _RTL871X_MP_IOCTL_C_ */
+
+#endif
+
diff --git a/drivers/staging/rtl8712/rtl871x_mp_phy_regdef.h b/drivers/staging/rtl8712/rtl871x_mp_phy_regdef.h
new file mode 100644
index 000000000..8e2586231
--- /dev/null
+++ b/drivers/staging/rtl8712/rtl871x_mp_phy_regdef.h
@@ -0,0 +1,1025 @@
+/*****************************************************************************
+ * Copyright(c) 2008, RealTEK Technology Inc. All Right Reserved.
+ *
+ * Module: __INC_HAL8192SPHYREG_H
+ *
+ *
+ * Note: 1. Define PMAC/BB register map
+ * 2. Define RF register map
+ * 3. PMAC/BB register bit mask.
+ * 4. RF reg bit mask.
+ * 5. Other BB/RF relative definition.
+ *
+ *
+ * Export: Constants, macro, functions(API), global variables(None).
+ *
+ * Abbrev:
+ *
+ * History:
+ * Data Who Remark
+ * 08/07/2007 MHC 1. Porting from 9x series PHYCFG.h.
+ * 2. Reorganize code architecture.
+ * 09/25/2008 MH 1. Add RL6052 register definition
+ *
+ *****************************************************************************/
+#ifndef __RTL871X_MP_PHY_REGDEF_H
+#define __RTL871X_MP_PHY_REGDEF_H
+
+
+/*--------------------------Define Parameters-------------------------------*/
+
+/*============================================================
+ * 8192S Regsiter offset definition
+ *============================================================
+ *
+ *
+ * BB-PHY register PMAC 0x100 PHY 0x800 - 0xEFF
+ * 1. PMAC duplicate register due to connection: RF_Mode, TRxRN, NumOf L-STF
+ * 2. 0x800/0x900/0xA00/0xC00/0xD00/0xE00
+ * 3. RF register 0x00-2E
+ * 4. Bit Mask for BB/RF register
+ * 5. Other definition for BB/RF R/W
+ *
+ * 1. PMAC duplicate register due to connection: RF_Mode, TRxRN, NumOf L-STF
+ * 1. Page1(0x100)
+ */
+#define rPMAC_Reset 0x100
+#define rPMAC_TxStart 0x104
+#define rPMAC_TxLegacySIG 0x108
+#define rPMAC_TxHTSIG1 0x10c
+#define rPMAC_TxHTSIG2 0x110
+#define rPMAC_PHYDebug 0x114
+#define rPMAC_TxPacketNum 0x118
+#define rPMAC_TxIdle 0x11c
+#define rPMAC_TxMACHeader0 0x120
+#define rPMAC_TxMACHeader1 0x124
+#define rPMAC_TxMACHeader2 0x128
+#define rPMAC_TxMACHeader3 0x12c
+#define rPMAC_TxMACHeader4 0x130
+#define rPMAC_TxMACHeader5 0x134
+#define rPMAC_TxDataType 0x138
+#define rPMAC_TxRandomSeed 0x13c
+#define rPMAC_CCKPLCPPreamble 0x140
+#define rPMAC_CCKPLCPHeader 0x144
+#define rPMAC_CCKCRC16 0x148
+#define rPMAC_OFDMRxCRC32OK 0x170
+#define rPMAC_OFDMRxCRC32Er 0x174
+#define rPMAC_OFDMRxParityEr 0x178
+#define rPMAC_OFDMRxCRC8Er 0x17c
+#define rPMAC_CCKCRxRC16Er 0x180
+#define rPMAC_CCKCRxRC32Er 0x184
+#define rPMAC_CCKCRxRC32OK 0x188
+#define rPMAC_TxStatus 0x18c
+
+/*
+ * 2. Page2(0x200)
+ *
+ * The following two definition are only used for USB interface.
+ *#define RF_BB_CMD_ADDR 0x02c0 // RF/BB read/write command address.
+ *#define RF_BB_CMD_DATA 0x02c4 // RF/BB read/write command data.
+ *
+ *
+ * 3. Page8(0x800)
+ */
+#define rFPGA0_RFMOD 0x800 /*RF mode & CCK TxSC RF
+ * BW Setting?? */
+#define rFPGA0_TxInfo 0x804 /* Status report?? */
+#define rFPGA0_PSDFunction 0x808
+#define rFPGA0_TxGainStage 0x80c /* Set TX PWR init gain? */
+#define rFPGA0_RFTiming1 0x810 /* Useless now */
+#define rFPGA0_RFTiming2 0x814
+#define rFPGA0_XA_HSSIParameter1 0x820 /* RF 3 wire register */
+#define rFPGA0_XA_HSSIParameter2 0x824
+#define rFPGA0_XB_HSSIParameter1 0x828
+#define rFPGA0_XB_HSSIParameter2 0x82c
+#define rFPGA0_XC_HSSIParameter1 0x830
+#define rFPGA0_XC_HSSIParameter2 0x834
+#define rFPGA0_XD_HSSIParameter1 0x838
+#define rFPGA0_XD_HSSIParameter2 0x83c
+#define rFPGA0_XA_LSSIParameter 0x840
+#define rFPGA0_XB_LSSIParameter 0x844
+#define rFPGA0_XC_LSSIParameter 0x848
+#define rFPGA0_XD_LSSIParameter 0x84c
+
+#define rFPGA0_RFWakeUpParameter 0x850 /* Useless now */
+#define rFPGA0_RFSleepUpParameter 0x854
+
+#define rFPGA0_XAB_SwitchControl 0x858 /* RF Channel switch */
+#define rFPGA0_XCD_SwitchControl 0x85c
+
+#define rFPGA0_XA_RFInterfaceOE 0x860 /* RF Channel switch */
+#define rFPGA0_XB_RFInterfaceOE 0x864
+#define rFPGA0_XC_RFInterfaceOE 0x868
+#define rFPGA0_XD_RFInterfaceOE 0x86c
+#define rFPGA0_XAB_RFInterfaceSW 0x870 /* RF Interface Software Ctrl */
+#define rFPGA0_XCD_RFInterfaceSW 0x874
+
+#define rFPGA0_XAB_RFParameter 0x878 /* RF Parameter */
+#define rFPGA0_XCD_RFParameter 0x87c
+
+#define rFPGA0_AnalogParameter1 0x880 /* Crystal cap setting
+ * RF-R/W protection
+ * for parameter4?? */
+#define rFPGA0_AnalogParameter2 0x884
+#define rFPGA0_AnalogParameter3 0x888 /* Useless now */
+#define rFPGA0_AnalogParameter4 0x88c
+
+#define rFPGA0_XA_LSSIReadBack 0x8a0 /* Tranceiver LSSI Readback */
+#define rFPGA0_XB_LSSIReadBack 0x8a4
+#define rFPGA0_XC_LSSIReadBack 0x8a8
+#define rFPGA0_XD_LSSIReadBack 0x8ac
+
+#define rFPGA0_PSDReport 0x8b4 /* Useless now */
+#define rFPGA0_XAB_RFInterfaceRB 0x8e0 /* Useless now */
+#define rFPGA0_XCD_RFInterfaceRB 0x8e4 /* Useless now */
+
+/*
+ * 4. Page9(0x900)
+ */
+#define rFPGA1_RFMOD 0x900 /* RF mode & OFDM TxSC */
+
+#define rFPGA1_TxBlock 0x904 /* Useless now */
+#define rFPGA1_DebugSelect 0x908 /* Useless now */
+#define rFPGA1_TxInfo 0x90c /* Useless now */
+
+/*
+ * 5. PageA(0xA00)
+ *
+ * Set Control channel to upper or lower.
+ * These settings are required only for 40MHz */
+#define rCCK0_System 0xa00
+
+#define rCCK0_AFESetting 0xa04 /* Disable init gain now */
+#define rCCK0_CCA 0xa08 /* Disable init gain now */
+
+#define rCCK0_RxAGC1 0xa0c
+/* AGC default value, saturation level
+ * Antenna Diversity, RX AGC, LNA Threshold, RX LNA Threshold useless now.
+ * Not the same as 90 series */
+#define rCCK0_RxAGC2 0xa10 /* AGC & DAGC */
+
+#define rCCK0_RxHP 0xa14
+
+#define rCCK0_DSPParameter1 0xa18 /* Timing recovery & Channel
+ * estimation threshold */
+#define rCCK0_DSPParameter2 0xa1c /* SQ threshold */
+
+#define rCCK0_TxFilter1 0xa20
+#define rCCK0_TxFilter2 0xa24
+#define rCCK0_DebugPort 0xa28 /* debug port and Tx filter3 */
+#define rCCK0_FalseAlarmReport 0xa2c /* 0xa2d useless now 0xa30-a4f
+ * channel report */
+#define rCCK0_TRSSIReport 0xa50
+#define rCCK0_RxReport 0xa54 /* 0xa57 */
+#define rCCK0_FACounterLower 0xa5c /* 0xa5b */
+#define rCCK0_FACounterUpper 0xa58 /* 0xa5c */
+
+/*
+ * 6. PageC(0xC00)
+ */
+#define rOFDM0_LSTF 0xc00
+#define rOFDM0_TRxPathEnable 0xc04
+#define rOFDM0_TRMuxPar 0xc08
+#define rOFDM0_TRSWIsolation 0xc0c
+
+/*RxIQ DC offset, Rx digital filter, DC notch filter */
+#define rOFDM0_XARxAFE 0xc10
+#define rOFDM0_XARxIQImbalance 0xc14 /* RxIQ imbalance matrix */
+#define rOFDM0_XBRxAFE 0xc18
+#define rOFDM0_XBRxIQImbalance 0xc1c
+#define rOFDM0_XCRxAFE 0xc20
+#define rOFDM0_XCRxIQImbalance 0xc24
+#define rOFDM0_XDRxAFE 0xc28
+#define rOFDM0_XDRxIQImbalance 0xc2c
+
+#define rOFDM0_RxDetector1 0xc30 /* PD,BW & SBD DM tune
+ * init gain */
+#define rOFDM0_RxDetector2 0xc34 /* SBD & Fame Sync. */
+#define rOFDM0_RxDetector3 0xc38 /* Frame Sync. */
+#define rOFDM0_RxDetector4 0xc3c /* PD, SBD, Frame Sync &
+ * Short-GI */
+
+#define rOFDM0_RxDSP 0xc40 /* Rx Sync Path */
+#define rOFDM0_CFOandDAGC 0xc44 /* CFO & DAGC */
+#define rOFDM0_CCADropThreshold 0xc48 /* CCA Drop threshold */
+#define rOFDM0_ECCAThreshold 0xc4c /* energy CCA */
+
+#define rOFDM0_XAAGCCore1 0xc50 /* DIG */
+#define rOFDM0_XAAGCCore2 0xc54
+#define rOFDM0_XBAGCCore1 0xc58
+#define rOFDM0_XBAGCCore2 0xc5c
+#define rOFDM0_XCAGCCore1 0xc60
+#define rOFDM0_XCAGCCore2 0xc64
+#define rOFDM0_XDAGCCore1 0xc68
+#define rOFDM0_XDAGCCore2 0xc6c
+#define rOFDM0_AGCParameter1 0xc70
+#define rOFDM0_AGCParameter2 0xc74
+#define rOFDM0_AGCRSSITable 0xc78
+#define rOFDM0_HTSTFAGC 0xc7c
+
+#define rOFDM0_XATxIQImbalance 0xc80 /* TX PWR TRACK and DIG */
+#define rOFDM0_XATxAFE 0xc84
+#define rOFDM0_XBTxIQImbalance 0xc88
+#define rOFDM0_XBTxAFE 0xc8c
+#define rOFDM0_XCTxIQImbalance 0xc90
+#define rOFDM0_XCTxAFE 0xc94
+#define rOFDM0_XDTxIQImbalance 0xc98
+#define rOFDM0_XDTxAFE 0xc9c
+
+#define rOFDM0_RxHPParameter 0xce0
+#define rOFDM0_TxPseudoNoiseWgt 0xce4
+#define rOFDM0_FrameSync 0xcf0
+#define rOFDM0_DFSReport 0xcf4
+#define rOFDM0_TxCoeff1 0xca4
+#define rOFDM0_TxCoeff2 0xca8
+#define rOFDM0_TxCoeff3 0xcac
+#define rOFDM0_TxCoeff4 0xcb0
+#define rOFDM0_TxCoeff5 0xcb4
+#define rOFDM0_TxCoeff6 0xcb8
+
+/*
+ * 7. PageD(0xD00)
+ */
+#define rOFDM1_LSTF 0xd00
+#define rOFDM1_TRxPathEnable 0xd04
+
+#define rOFDM1_CFO 0xd08 /* No setting now */
+#define rOFDM1_CSI1 0xd10
+#define rOFDM1_SBD 0xd14
+#define rOFDM1_CSI2 0xd18
+#define rOFDM1_CFOTracking 0xd2c
+#define rOFDM1_TRxMesaure1 0xd34
+#define rOFDM1_IntfDet 0xd3c
+#define rOFDM1_PseudoNoiseStateAB 0xd50
+#define rOFDM1_PseudoNoiseStateCD 0xd54
+#define rOFDM1_RxPseudoNoiseWgt 0xd58
+
+#define rOFDM_PHYCounter1 0xda0 /* cca, parity fail */
+#define rOFDM_PHYCounter2 0xda4 /* rate illegal, crc8 fail */
+#define rOFDM_PHYCounter3 0xda8 /* MCS not support */
+#define rOFDM_ShortCFOAB 0xdac /* No setting now */
+#define rOFDM_ShortCFOCD 0xdb0
+#define rOFDM_LongCFOAB 0xdb4
+#define rOFDM_LongCFOCD 0xdb8
+#define rOFDM_TailCFOAB 0xdbc
+#define rOFDM_TailCFOCD 0xdc0
+#define rOFDM_PWMeasure1 0xdc4
+#define rOFDM_PWMeasure2 0xdc8
+#define rOFDM_BWReport 0xdcc
+#define rOFDM_AGCReport 0xdd0
+#define rOFDM_RxSNR 0xdd4
+#define rOFDM_RxEVMCSI 0xdd8
+#define rOFDM_SIGReport 0xddc
+
+/*
+ * 8. PageE(0xE00)
+ */
+#define rTxAGC_Rate18_06 0xe00
+#define rTxAGC_Rate54_24 0xe04
+#define rTxAGC_CCK_Mcs32 0xe08
+#define rTxAGC_Mcs03_Mcs00 0xe10
+#define rTxAGC_Mcs07_Mcs04 0xe14
+#define rTxAGC_Mcs11_Mcs08 0xe18
+#define rTxAGC_Mcs15_Mcs12 0xe1c
+
+/* Analog- control in RX_WAIT_CCA : REG: EE0
+ * [Analog- Power & Control Register] */
+#define rRx_Wait_CCCA 0xe70
+#define rAnapar_Ctrl_BB 0xee0
+
+/*
+ * 7. RF Register 0x00-0x2E (RF 8256)
+ * RF-0222D 0x00-3F
+ *
+ * Zebra1
+ */
+#define rZebra1_HSSIEnable 0x0 /* Useless now */
+#define rZebra1_TRxEnable1 0x1
+#define rZebra1_TRxEnable2 0x2
+#define rZebra1_AGC 0x4
+#define rZebra1_ChargePump 0x5
+#define rZebra1_Channel 0x7 /* RF channel switch */
+#define rZebra1_TxGain 0x8 /* Useless now */
+#define rZebra1_TxLPF 0x9
+#define rZebra1_RxLPF 0xb
+#define rZebra1_RxHPFCorner 0xc
+
+/* Zebra4 */
+#define rGlobalCtrl 0 /* Useless now */
+#define rRTL8256_TxLPF 19
+#define rRTL8256_RxLPF 11
+
+/* RTL8258 */
+#define rRTL8258_TxLPF 0x11 /* Useless now */
+#define rRTL8258_RxLPF 0x13
+#define rRTL8258_RSSILPF 0xa
+
+/* RL6052 Register definition */
+#define RF_AC 0x00
+#define RF_IQADJ_G1 0x01
+#define RF_IQADJ_G2 0x02
+#define RF_POW_TRSW 0x05
+
+#define RF_GAIN_RX 0x06
+#define RF_GAIN_TX 0x07
+
+#define RF_TXM_IDAC 0x08
+#define RF_BS_IQGEN 0x0F
+
+#define RF_MODE1 0x10
+#define RF_MODE2 0x11
+
+#define RF_RX_AGC_HP 0x12
+#define RF_TX_AGC 0x13
+#define RF_BIAS 0x14
+#define RF_IPA 0x15
+#define RF_POW_ABILITY 0x17
+#define RF_MODE_AG 0x18
+#define rRfChannel 0x18 /* RF channel and BW switch */
+#define RF_CHNLBW 0x18 /* RF channel and BW switch */
+#define RF_TOP 0x19
+#define RF_RX_G1 0x1A
+#define RF_RX_G2 0x1B
+#define RF_RX_BB2 0x1C
+#define RF_RX_BB1 0x1D
+
+#define RF_RCK1 0x1E
+#define RF_RCK2 0x1F
+
+#define RF_TX_G1 0x20
+#define RF_TX_G2 0x21
+#define RF_TX_G3 0x22
+
+#define RF_TX_BB1 0x23
+#define RF_T_METER 0x24
+
+#define RF_SYN_G1 0x25 /* RF TX Power control */
+#define RF_SYN_G2 0x26 /* RF TX Power control */
+#define RF_SYN_G3 0x27 /* RF TX Power control */
+#define RF_SYN_G4 0x28 /* RF TX Power control */
+#define RF_SYN_G5 0x29 /* RF TX Power control */
+#define RF_SYN_G6 0x2A /* RF TX Power control */
+#define RF_SYN_G7 0x2B /* RF TX Power control */
+#define RF_SYN_G8 0x2C /* RF TX Power control */
+
+#define RF_RCK_OS 0x30 /* RF TX PA control */
+
+#define RF_TXPA_G1 0x31 /* RF TX PA control */
+#define RF_TXPA_G2 0x32 /* RF TX PA control */
+#define RF_TXPA_G3 0x33 /* RF TX PA control */
+
+/*
+ * Bit Mask
+ *
+ * 1. Page1(0x100) */
+#define bBBResetB 0x100 /* Useless now? */
+#define bGlobalResetB 0x200
+#define bOFDMTxStart 0x4
+#define bCCKTxStart 0x8
+#define bCRC32Debug 0x100
+#define bPMACLoopback 0x10
+#define bTxLSIG 0xffffff
+#define bOFDMTxRate 0xf
+#define bOFDMTxReserved 0x10
+#define bOFDMTxLength 0x1ffe0
+#define bOFDMTxParity 0x20000
+#define bTxHTSIG1 0xffffff
+#define bTxHTMCSRate 0x7f
+#define bTxHTBW 0x80
+#define bTxHTLength 0xffff00
+#define bTxHTSIG2 0xffffff
+#define bTxHTSmoothing 0x1
+#define bTxHTSounding 0x2
+#define bTxHTReserved 0x4
+#define bTxHTAggreation 0x8
+#define bTxHTSTBC 0x30
+#define bTxHTAdvanceCoding 0x40
+#define bTxHTShortGI 0x80
+#define bTxHTNumberHT_LTF 0x300
+#define bTxHTCRC8 0x3fc00
+#define bCounterReset 0x10000
+#define bNumOfOFDMTx 0xffff
+#define bNumOfCCKTx 0xffff0000
+#define bTxIdleInterval 0xffff
+#define bOFDMService 0xffff0000
+#define bTxMACHeader 0xffffffff
+#define bTxDataInit 0xff
+#define bTxHTMode 0x100
+#define bTxDataType 0x30000
+#define bTxRandomSeed 0xffffffff
+#define bCCKTxPreamble 0x1
+#define bCCKTxSFD 0xffff0000
+#define bCCKTxSIG 0xff
+#define bCCKTxService 0xff00
+#define bCCKLengthExt 0x8000
+#define bCCKTxLength 0xffff0000
+#define bCCKTxCRC16 0xffff
+#define bCCKTxStatus 0x1
+#define bOFDMTxStatus 0x2
+#define IS_BB_REG_OFFSET_92S(_Offset) ((_Offset >= 0x800) && \
+ (_Offset <= 0xfff))
+
+/* 2. Page8(0x800) */
+#define bRFMOD 0x1 /* Reg 0x800 rFPGA0_RFMOD */
+#define bJapanMode 0x2
+#define bCCKTxSC 0x30
+#define bCCKEn 0x1000000
+#define bOFDMEn 0x2000000
+
+#define bOFDMRxADCPhase 0x10000 /* Useless now */
+#define bOFDMTxDACPhase 0x40000
+#define bXATxAGC 0x3f
+#define bXBTxAGC 0xf00 /* Reg 80c rFPGA0_TxGainStage */
+#define bXCTxAGC 0xf000
+#define bXDTxAGC 0xf0000
+
+#define bPAStart 0xf0000000 /* Useless now */
+#define bTRStart 0x00f00000
+#define bRFStart 0x0000f000
+#define bBBStart 0x000000f0
+#define bBBCCKStart 0x0000000f
+#define bPAEnd 0xf /* Reg0x814 */
+#define bTREnd 0x0f000000
+#define bRFEnd 0x000f0000
+#define bCCAMask 0x000000f0 /* T2R */
+#define bR2RCCAMask 0x00000f00
+#define bHSSI_R2TDelay 0xf8000000
+#define bHSSI_T2RDelay 0xf80000
+#define bContTxHSSI 0x400 /* change gain at continue Tx */
+#define bIGFromCCK 0x200
+#define bAGCAddress 0x3f
+#define bRxHPTx 0x7000
+#define bRxHPT2R 0x38000
+#define bRxHPCCKIni 0xc0000
+#define bAGCTxCode 0xc00000
+#define bAGCRxCode 0x300000
+#define b3WireDataLength 0x800 /* Reg 0x820~84f rFPGA0_XA_HSSIParm1 */
+#define b3WireAddressLength 0x400
+#define b3WireRFPowerDown 0x1 /* Useless now */
+#define b5GPAPEPolarity 0x40000000
+#define b2GPAPEPolarity 0x80000000
+#define bRFSW_TxDefaultAnt 0x3
+#define bRFSW_TxOptionAnt 0x30
+#define bRFSW_RxDefaultAnt 0x300
+#define bRFSW_RxOptionAnt 0x3000
+#define bRFSI_3WireData 0x1
+#define bRFSI_3WireClock 0x2
+#define bRFSI_3WireLoad 0x4
+#define bRFSI_3WireRW 0x8
+#define bRFSI_3Wire 0xf
+#define bRFSI_RFENV 0x10 /* Reg 0x870 rFPGA0_XAB_RFInterfaceSW */
+#define bRFSI_TRSW 0x20 /* Useless now */
+#define bRFSI_TRSWB 0x40
+#define bRFSI_ANTSW 0x100
+#define bRFSI_ANTSWB 0x200
+#define bRFSI_PAPE 0x400
+#define bRFSI_PAPE5G 0x800
+#define bBandSelect 0x1
+#define bHTSIG2_GI 0x80
+#define bHTSIG2_Smoothing 0x01
+#define bHTSIG2_Sounding 0x02
+#define bHTSIG2_Aggreaton 0x08
+#define bHTSIG2_STBC 0x30
+#define bHTSIG2_AdvCoding 0x40
+#define bHTSIG2_NumOfHTLTF 0x300
+#define bHTSIG2_CRC8 0x3fc
+#define bHTSIG1_MCS 0x7f
+#define bHTSIG1_BandWidth 0x80
+#define bHTSIG1_HTLength 0xffff
+#define bLSIG_Rate 0xf
+#define bLSIG_Reserved 0x10
+#define bLSIG_Length 0x1fffe
+#define bLSIG_Parity 0x20
+#define bCCKRxPhase 0x4
+#define bLSSIReadAddress 0x7f800000 /* T65 RF */
+#define bLSSIReadEdge 0x80000000 /* LSSI "Read" edge signal */
+#define bLSSIReadBackData 0xfffff /* T65 RF */
+#define bLSSIReadOKFlag 0x1000 /* Useless now */
+#define bCCKSampleRate 0x8 /*0: 44MHz, 1:88MHz*/
+#define bRegulator0Standby 0x1
+#define bRegulatorPLLStandby 0x2
+#define bRegulator1Standby 0x4
+#define bPLLPowerUp 0x8
+#define bDPLLPowerUp 0x10
+#define bDA10PowerUp 0x20
+#define bAD7PowerUp 0x200
+#define bDA6PowerUp 0x2000
+#define bXtalPowerUp 0x4000
+#define b40MDClkPowerUP 0x8000
+#define bDA6DebugMode 0x20000
+#define bDA6Swing 0x380000
+
+/* Reg 0x880 rFPGA0_AnalogParameter1 20/40 CCK support switch 40/80 BB MHZ */
+#define bADClkPhase 0x4000000
+
+#define b80MClkDelay 0x18000000 /* Useless */
+#define bAFEWatchDogEnable 0x20000000
+
+/* Reg 0x884 rFPGA0_AnalogParameter2 Crystal cap */
+#define bXtalCap01 0xc0000000
+#define bXtalCap23 0x3
+#define bXtalCap92x 0x0f000000
+#define bXtalCap 0x0f000000
+#define bIntDifClkEnable 0x400 /* Useless */
+#define bExtSigClkEnable 0x800
+#define bBandgapMbiasPowerUp 0x10000
+#define bAD11SHGain 0xc0000
+#define bAD11InputRange 0x700000
+#define bAD11OPCurrent 0x3800000
+#define bIPathLoopback 0x4000000
+#define bQPathLoopback 0x8000000
+#define bAFELoopback 0x10000000
+#define bDA10Swing 0x7e0
+#define bDA10Reverse 0x800
+#define bDAClkSource 0x1000
+#define bAD7InputRange 0x6000
+#define bAD7Gain 0x38000
+#define bAD7OutputCMMode 0x40000
+#define bAD7InputCMMode 0x380000
+#define bAD7Current 0xc00000
+#define bRegulatorAdjust 0x7000000
+#define bAD11PowerUpAtTx 0x1
+#define bDA10PSAtTx 0x10
+#define bAD11PowerUpAtRx 0x100
+#define bDA10PSAtRx 0x1000
+#define bCCKRxAGCFormat 0x200
+#define bPSDFFTSamplepPoint 0xc000
+#define bPSDAverageNum 0x3000
+#define bIQPathControl 0xc00
+#define bPSDFreq 0x3ff
+#define bPSDAntennaPath 0x30
+#define bPSDIQSwitch 0x40
+#define bPSDRxTrigger 0x400000
+#define bPSDTxTrigger 0x80000000
+#define bPSDSineToneScale 0x7f000000
+#define bPSDReport 0xffff
+
+/* 3. Page9(0x900) */
+#define bOFDMTxSC 0x30000000 /* Useless */
+#define bCCKTxOn 0x1
+#define bOFDMTxOn 0x2
+#define bDebugPage 0xfff /* reset debug page and HWord, LWord */
+#define bDebugItem 0xff /* reset debug page and LWord */
+#define bAntL 0x10
+#define bAntNonHT 0x100
+#define bAntHT1 0x1000
+#define bAntHT2 0x10000
+#define bAntHT1S1 0x100000
+#define bAntNonHTS1 0x1000000
+
+/* 4. PageA(0xA00) */
+#define bCCKBBMode 0x3 /* Useless */
+#define bCCKTxPowerSaving 0x80
+#define bCCKRxPowerSaving 0x40
+
+#define bCCKSideBand 0x10 /* Reg 0xa00 rCCK0_System 20/40 switch*/
+#define bCCKScramble 0x8 /* Useless */
+#define bCCKAntDiversity 0x8000
+#define bCCKCarrierRecovery 0x4000
+#define bCCKTxRate 0x3000
+#define bCCKDCCancel 0x0800
+#define bCCKISICancel 0x0400
+#define bCCKMatchFilter 0x0200
+#define bCCKEqualizer 0x0100
+#define bCCKPreambleDetect 0x800000
+#define bCCKFastFalseCCA 0x400000
+#define bCCKChEstStart 0x300000
+#define bCCKCCACount 0x080000
+#define bCCKcs_lim 0x070000
+#define bCCKBistMode 0x80000000
+#define bCCKCCAMask 0x40000000
+#define bCCKTxDACPhase 0x4
+#define bCCKRxADCPhase 0x20000000 /* r_rx_clk */
+#define bCCKr_cp_mode0 0x0100
+#define bCCKTxDCOffset 0xf0
+#define bCCKRxDCOffset 0xf
+#define bCCKCCAMode 0xc000
+#define bCCKFalseCS_lim 0x3f00
+#define bCCKCS_ratio 0xc00000
+#define bCCKCorgBit_sel 0x300000
+#define bCCKPD_lim 0x0f0000
+#define bCCKNewCCA 0x80000000
+#define bCCKRxHPofIG 0x8000
+#define bCCKRxIG 0x7f00
+#define bCCKLNAPolarity 0x800000
+#define bCCKRx1stGain 0x7f0000
+#define bCCKRFExtend 0x20000000 /* CCK Rx inital gain polarity */
+#define bCCKRxAGCSatLevel 0x1f000000
+#define bCCKRxAGCSatCount 0xe0
+#define bCCKRxRFSettle 0x1f /* AGCsamp_dly */
+#define bCCKFixedRxAGC 0x8000
+#define bCCKAntennaPolarity 0x2000
+#define bCCKTxFilterType 0x0c00
+#define bCCKRxAGCReportType 0x0300
+#define bCCKRxDAGCEn 0x80000000
+#define bCCKRxDAGCPeriod 0x20000000
+#define bCCKRxDAGCSatLevel 0x1f000000
+#define bCCKTimingRecovery 0x800000
+#define bCCKTxC0 0x3f0000
+#define bCCKTxC1 0x3f000000
+#define bCCKTxC2 0x3f
+#define bCCKTxC3 0x3f00
+#define bCCKTxC4 0x3f0000
+#define bCCKTxC5 0x3f000000
+#define bCCKTxC6 0x3f
+#define bCCKTxC7 0x3f00
+#define bCCKDebugPort 0xff0000
+#define bCCKDACDebug 0x0f000000
+#define bCCKFalseAlarmEnable 0x8000
+#define bCCKFalseAlarmRead 0x4000
+#define bCCKTRSSI 0x7f
+#define bCCKRxAGCReport 0xfe
+#define bCCKRxReport_AntSel 0x80000000
+#define bCCKRxReport_MFOff 0x40000000
+#define bCCKRxRxReport_SQLoss 0x20000000
+#define bCCKRxReport_Pktloss 0x10000000
+#define bCCKRxReport_Lockedbit 0x08000000
+#define bCCKRxReport_RateError 0x04000000
+#define bCCKRxReport_RxRate 0x03000000
+#define bCCKRxFACounterLower 0xff
+#define bCCKRxFACounterUpper 0xff000000
+#define bCCKRxHPAGCStart 0xe000
+#define bCCKRxHPAGCFinal 0x1c00
+#define bCCKRxFalseAlarmEnable 0x8000
+#define bCCKFACounterFreeze 0x4000
+#define bCCKTxPathSel 0x10000000
+#define bCCKDefaultRxPath 0xc000000
+#define bCCKOptionRxPath 0x3000000
+
+/* 5. PageC(0xC00) */
+#define bNumOfSTF 0x3 /* Useless */
+#define bShift_L 0xc0
+#define bGI_TH 0xc
+#define bRxPathA 0x1
+#define bRxPathB 0x2
+#define bRxPathC 0x4
+#define bRxPathD 0x8
+#define bTxPathA 0x1
+#define bTxPathB 0x2
+#define bTxPathC 0x4
+#define bTxPathD 0x8
+#define bTRSSIFreq 0x200
+#define bADCBackoff 0x3000
+#define bDFIRBackoff 0xc000
+#define bTRSSILatchPhase 0x10000
+#define bRxIDCOffset 0xff
+#define bRxQDCOffset 0xff00
+#define bRxDFIRMode 0x1800000
+#define bRxDCNFType 0xe000000
+#define bRXIQImb_A 0x3ff
+#define bRXIQImb_B 0xfc00
+#define bRXIQImb_C 0x3f0000
+#define bRXIQImb_D 0xffc00000
+#define bDC_dc_Notch 0x60000
+#define bRxNBINotch 0x1f000000
+#define bPD_TH 0xf
+#define bPD_TH_Opt2 0xc000
+#define bPWED_TH 0x700
+#define bIfMF_Win_L 0x800
+#define bPD_Option 0x1000
+#define bMF_Win_L 0xe000
+#define bBW_Search_L 0x30000
+#define bwin_enh_L 0xc0000
+#define bBW_TH 0x700000
+#define bED_TH2 0x3800000
+#define bBW_option 0x4000000
+#define bRatio_TH 0x18000000
+#define bWindow_L 0xe0000000
+#define bSBD_Option 0x1
+#define bFrame_TH 0x1c
+#define bFS_Option 0x60
+#define bDC_Slope_check 0x80
+#define bFGuard_Counter_DC_L 0xe00
+#define bFrame_Weight_Short 0x7000
+#define bSub_Tune 0xe00000
+#define bFrame_DC_Length 0xe000000
+#define bSBD_start_offset 0x30000000
+#define bFrame_TH_2 0x7
+#define bFrame_GI2_TH 0x38
+#define bGI2_Sync_en 0x40
+#define bSarch_Short_Early 0x300
+#define bSarch_Short_Late 0xc00
+#define bSarch_GI2_Late 0x70000
+#define bCFOAntSum 0x1
+#define bCFOAcc 0x2
+#define bCFOStartOffset 0xc
+#define bCFOLookBack 0x70
+#define bCFOSumWeight 0x80
+#define bDAGCEnable 0x10000
+#define bTXIQImb_A 0x3ff
+#define bTXIQImb_B 0xfc00
+#define bTXIQImb_C 0x3f0000
+#define bTXIQImb_D 0xffc00000
+#define bTxIDCOffset 0xff
+#define bTxQDCOffset 0xff00
+#define bTxDFIRMode 0x10000
+#define bTxPesudoNoiseOn 0x4000000
+#define bTxPesudoNoise_A 0xff
+#define bTxPesudoNoise_B 0xff00
+#define bTxPesudoNoise_C 0xff0000
+#define bTxPesudoNoise_D 0xff000000
+#define bCCADropOption 0x20000
+#define bCCADropThres 0xfff00000
+#define bEDCCA_H 0xf
+#define bEDCCA_L 0xf0
+#define bLambda_ED 0x300
+#define bRxInitialGain 0x7f
+#define bRxAntDivEn 0x80
+#define bRxAGCAddressForLNA 0x7f00
+#define bRxHighPowerFlow 0x8000
+#define bRxAGCFreezeThres 0xc0000
+#define bRxFreezeStep_AGC1 0x300000
+#define bRxFreezeStep_AGC2 0xc00000
+#define bRxFreezeStep_AGC3 0x3000000
+#define bRxFreezeStep_AGC0 0xc000000
+#define bRxRssi_Cmp_En 0x10000000
+#define bRxQuickAGCEn 0x20000000
+#define bRxAGCFreezeThresMode 0x40000000
+#define bRxOverFlowCheckType 0x80000000
+#define bRxAGCShift 0x7f
+#define bTRSW_Tri_Only 0x80
+#define bPowerThres 0x300
+#define bRxAGCEn 0x1
+#define bRxAGCTogetherEn 0x2
+#define bRxAGCMin 0x4
+#define bRxHP_Ini 0x7
+#define bRxHP_TRLNA 0x70
+#define bRxHP_RSSI 0x700
+#define bRxHP_BBP1 0x7000
+#define bRxHP_BBP2 0x70000
+#define bRxHP_BBP3 0x700000
+#define bRSSI_H 0x7f0000 /* the threshold for high power */
+#define bRSSI_Gen 0x7f000000 /* the threshold for ant divers */
+#define bRxSettle_TRSW 0x7
+#define bRxSettle_LNA 0x38
+#define bRxSettle_RSSI 0x1c0
+#define bRxSettle_BBP 0xe00
+#define bRxSettle_RxHP 0x7000
+#define bRxSettle_AntSW_RSSI 0x38000
+#define bRxSettle_AntSW 0xc0000
+#define bRxProcessTime_DAGC 0x300000
+#define bRxSettle_HSSI 0x400000
+#define bRxProcessTime_BBPPW 0x800000
+#define bRxAntennaPowerShift 0x3000000
+#define bRSSITableSelect 0xc000000
+#define bRxHP_Final 0x7000000
+#define bRxHTSettle_BBP 0x7
+#define bRxHTSettle_HSSI 0x8
+#define bRxHTSettle_RxHP 0x70
+#define bRxHTSettle_BBPPW 0x80
+#define bRxHTSettle_Idle 0x300
+#define bRxHTSettle_Reserved 0x1c00
+#define bRxHTRxHPEn 0x8000
+#define bRxHTAGCFreezeThres 0x30000
+#define bRxHTAGCTogetherEn 0x40000
+#define bRxHTAGCMin 0x80000
+#define bRxHTAGCEn 0x100000
+#define bRxHTDAGCEn 0x200000
+#define bRxHTRxHP_BBP 0x1c00000
+#define bRxHTRxHP_Final 0xe0000000
+#define bRxPWRatioTH 0x3
+#define bRxPWRatioEn 0x4
+#define bRxMFHold 0x3800
+#define bRxPD_Delay_TH1 0x38
+#define bRxPD_Delay_TH2 0x1c0
+#define bRxPD_DC_COUNT_MAX 0x600
+#define bRxPD_Delay_TH 0x8000
+#define bRxProcess_Delay 0xf0000
+#define bRxSearchrange_GI2_Early 0x700000
+#define bRxFrame_Guard_Counter_L 0x3800000
+#define bRxSGI_Guard_L 0xc000000
+#define bRxSGI_Search_L 0x30000000
+#define bRxSGI_TH 0xc0000000
+#define bDFSCnt0 0xff
+#define bDFSCnt1 0xff00
+#define bDFSFlag 0xf0000
+#define bMFWeightSum 0x300000
+#define bMinIdxTH 0x7f000000
+#define bDAFormat 0x40000
+#define bTxChEmuEnable 0x01000000
+#define bTRSWIsolation_A 0x7f
+#define bTRSWIsolation_B 0x7f00
+#define bTRSWIsolation_C 0x7f0000
+#define bTRSWIsolation_D 0x7f000000
+#define bExtLNAGain 0x7c00
+
+/* 6. PageE(0xE00) */
+#define bSTBCEn 0x4 /* Useless */
+#define bAntennaMapping 0x10
+#define bNss 0x20
+#define bCFOAntSumD 0x200
+#define bPHYCounterReset 0x8000000
+#define bCFOReportGet 0x4000000
+#define bOFDMContinueTx 0x10000000
+#define bOFDMSingleCarrier 0x20000000
+#define bOFDMSingleTone 0x40000000
+#define bHTDetect 0x100
+#define bCFOEn 0x10000
+#define bCFOValue 0xfff00000
+#define bSigTone_Re 0x3f
+#define bSigTone_Im 0x7f00
+#define bCounter_CCA 0xffff
+#define bCounter_ParityFail 0xffff0000
+#define bCounter_RateIllegal 0xffff
+#define bCounter_CRC8Fail 0xffff0000
+#define bCounter_MCSNoSupport 0xffff
+#define bCounter_FastSync 0xffff
+#define bShortCFO 0xfff
+#define bShortCFOTLength 12 /* total */
+#define bShortCFOFLength 11 /* fraction */
+#define bLongCFO 0x7ff
+#define bLongCFOTLength 11
+#define bLongCFOFLength 11
+#define bTailCFO 0x1fff
+#define bTailCFOTLength 13
+#define bTailCFOFLength 12
+#define bmax_en_pwdB 0xffff
+#define bCC_power_dB 0xffff0000
+#define bnoise_pwdB 0xffff
+#define bPowerMeasTLength 10
+#define bPowerMeasFLength 3
+#define bRx_HT_BW 0x1
+#define bRxSC 0x6
+#define bRx_HT 0x8
+#define bNB_intf_det_on 0x1
+#define bIntf_win_len_cfg 0x30
+#define bNB_Intf_TH_cfg 0x1c0
+#define bRFGain 0x3f
+#define bTableSel 0x40
+#define bTRSW 0x80
+#define bRxSNR_A 0xff
+#define bRxSNR_B 0xff00
+#define bRxSNR_C 0xff0000
+#define bRxSNR_D 0xff000000
+#define bSNREVMTLength 8
+#define bSNREVMFLength 1
+#define bCSI1st 0xff
+#define bCSI2nd 0xff00
+#define bRxEVM1st 0xff0000
+#define bRxEVM2nd 0xff000000
+#define bSIGEVM 0xff
+#define bPWDB 0xff00
+#define bSGIEN 0x10000
+
+#define bSFactorQAM1 0xf /* Useless */
+#define bSFactorQAM2 0xf0
+#define bSFactorQAM3 0xf00
+#define bSFactorQAM4 0xf000
+#define bSFactorQAM5 0xf0000
+#define bSFactorQAM6 0xf0000
+#define bSFactorQAM7 0xf00000
+#define bSFactorQAM8 0xf000000
+#define bSFactorQAM9 0xf0000000
+#define bCSIScheme 0x100000
+
+#define bNoiseLvlTopSet 0x3 /* Useless */
+#define bChSmooth 0x4
+#define bChSmoothCfg1 0x38
+#define bChSmoothCfg2 0x1c0
+#define bChSmoothCfg3 0xe00
+#define bChSmoothCfg4 0x7000
+#define bMRCMode 0x800000
+#define bTHEVMCfg 0x7000000
+
+#define bLoopFitType 0x1 /* Useless */
+#define bUpdCFO 0x40
+#define bUpdCFOOffData 0x80
+#define bAdvUpdCFO 0x100
+#define bAdvTimeCtrl 0x800
+#define bUpdClko 0x1000
+#define bFC 0x6000
+#define bTrackingMode 0x8000
+#define bPhCmpEnable 0x10000
+#define bUpdClkoLTF 0x20000
+#define bComChCFO 0x40000
+#define bCSIEstiMode 0x80000
+#define bAdvUpdEqz 0x100000
+#define bUChCfg 0x7000000
+#define bUpdEqz 0x8000000
+
+#define bTxAGCRate18_06 0x7f7f7f7f /* Useless */
+#define bTxAGCRate54_24 0x7f7f7f7f
+#define bTxAGCRateMCS32 0x7f
+#define bTxAGCRateCCK 0x7f00
+#define bTxAGCRateMCS3_MCS0 0x7f7f7f7f
+#define bTxAGCRateMCS7_MCS4 0x7f7f7f7f
+#define bTxAGCRateMCS11_MCS8 0x7f7f7f7f
+#define bTxAGCRateMCS15_MCS12 0x7f7f7f7f
+
+/* Rx Pseduo noise */
+#define bRxPesudoNoiseOn 0x20000000 /* Useless */
+#define bRxPesudoNoise_A 0xff
+#define bRxPesudoNoise_B 0xff00
+#define bRxPesudoNoise_C 0xff0000
+#define bRxPesudoNoise_D 0xff000000
+#define bPesudoNoiseState_A 0xffff
+#define bPesudoNoiseState_B 0xffff0000
+#define bPesudoNoiseState_C 0xffff
+#define bPesudoNoiseState_D 0xffff0000
+
+/* 7. RF Register
+ * Zebra1 */
+#define bZebra1_HSSIEnable 0x8 /* Useless */
+#define bZebra1_TRxControl 0xc00
+#define bZebra1_TRxGainSetting 0x07f
+#define bZebra1_RxCorner 0xc00
+#define bZebra1_TxChargePump 0x38
+#define bZebra1_RxChargePump 0x7
+#define bZebra1_ChannelNum 0xf80
+#define bZebra1_TxLPFBW 0x400
+#define bZebra1_RxLPFBW 0x600
+
+/*Zebra4 */
+#define bRTL8256RegModeCtrl1 0x100 /* Useless */
+#define bRTL8256RegModeCtrl0 0x40
+#define bRTL8256_TxLPFBW 0x18
+#define bRTL8256_RxLPFBW 0x600
+
+/* RTL8258 */
+#define bRTL8258_TxLPFBW 0xc /* Useless */
+#define bRTL8258_RxLPFBW 0xc00
+#define bRTL8258_RSSILPFBW 0xc0
+
+/*
+ * Other Definition
+ */
+
+/* byte endable for sb_write */
+#define bByte0 0x1 /* Useless */
+#define bByte1 0x2
+#define bByte2 0x4
+#define bByte3 0x8
+#define bWord0 0x3
+#define bWord1 0xc
+#define bDWord 0xf
+
+/* for PutRegsetting & GetRegSetting BitMask */
+#define bMaskByte0 0xff /* Reg 0xc50 rOFDM0_XAAGCCore~0xC6f */
+#define bMaskByte1 0xff00
+#define bMaskByte2 0xff0000
+#define bMaskByte3 0xff000000
+#define bMaskHWord 0xffff0000
+#define bMaskLWord 0x0000ffff
+#define bMaskDWord 0xffffffff
+
+/* for PutRFRegsetting & GetRFRegSetting BitMask */
+#define bRFRegOffsetMask 0xfffff
+#define bEnable 0x1 /* Useless */
+#define bDisable 0x0
+
+#define LeftAntenna 0x0 /* Useless */
+#define RightAntenna 0x1
+
+#define tCheckTxStatus 500 /* 500ms Useless */
+#define tUpdateRxCounter 100 /* 100ms */
+
+#define rateCCK 0 /* Useless */
+#define rateOFDM 1
+#define rateHT 2
+
+/* define Register-End */
+#define bPMAC_End 0x1ff /* Useless */
+#define bFPGAPHY0_End 0x8ff
+#define bFPGAPHY1_End 0x9ff
+#define bCCKPHY0_End 0xaff
+#define bOFDMPHY0_End 0xcff
+#define bOFDMPHY1_End 0xdff
+
+#define bPMACControl 0x0 /* Useless */
+#define bWMACControl 0x1
+#define bWNICControl 0x2
+
+#define ANTENNA_A 0x1 /* Useless */
+#define ANTENNA_B 0x2
+#define ANTENNA_AB 0x3 /* ANTENNA_A |ANTENNA_B */
+
+#define ANTENNA_C 0x4
+#define ANTENNA_D 0x8
+
+
+/* accept all physical address */
+#define RCR_AAP BIT(0)
+#define RCR_APM BIT(1) /* accept physical match */
+#define RCR_AM BIT(2) /* accept multicast */
+#define RCR_AB BIT(3) /* accept broadcast */
+#define RCR_ACRC32 BIT(5) /* accept error packet */
+#define RCR_9356SEL BIT(6)
+#define RCR_AICV BIT(12) /* Accept ICV error packet */
+#define RCR_RXFTH0 (BIT(13)|BIT(14)|BIT(15)) /* Rx FIFO threshold */
+#define RCR_ADF BIT(18) /* Accept Data(frame type) frame */
+#define RCR_ACF BIT(19) /* Accept control frame */
+#define RCR_AMF BIT(20) /* Accept management frame */
+#define RCR_ADD3 BIT(21)
+#define RCR_APWRMGT BIT(22) /* Accept power management packet */
+#define RCR_CBSSID BIT(23) /* Accept BSSID match packet */
+#define RCR_ENMARP BIT(28) /* enable mac auto reset phy */
+#define RCR_EnCS1 BIT(29) /* enable carrier sense method 1 */
+#define RCR_EnCS2 BIT(30) /* enable carrier sense method 2 */
+/* Rx Early mode is performed for packet size greater than 1536 */
+#define RCR_OnlyErlPkt BIT(31)
+
+/*--------------------------Define Parameters-------------------------------*/
+
+
+#endif /*__INC_HAL8192SPHYREG_H */
+
diff --git a/drivers/staging/rtl8712/rtl871x_pwrctrl.c b/drivers/staging/rtl8712/rtl871x_pwrctrl.c
new file mode 100644
index 000000000..9bc04f474
--- /dev/null
+++ b/drivers/staging/rtl8712/rtl871x_pwrctrl.c
@@ -0,0 +1,245 @@
+/******************************************************************************
+ * rtl871x_pwrctrl.c
+ *
+ * Copyright(c) 2007 - 2010 Realtek Corporation. All rights reserved.
+ * Linux device driver for RTL8192SU
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ * Modifications for inclusion into the Linux staging tree are
+ * Copyright(c) 2010 Larry Finger. All rights reserved.
+ *
+ * Contact information:
+ * WLAN FAE <wlanfae@realtek.com>
+ * Larry Finger <Larry.Finger@lwfinger.net>
+ *
+ ******************************************************************************/
+
+#define _RTL871X_PWRCTRL_C_
+
+#include "osdep_service.h"
+#include "drv_types.h"
+#include "osdep_intf.h"
+
+#define RTL8712_SDIO_LOCAL_BASE 0X10100000
+#define SDIO_HCPWM (RTL8712_SDIO_LOCAL_BASE + 0x0081)
+
+void r8712_set_rpwm(struct _adapter *padapter, u8 val8)
+{
+ u8 rpwm;
+ struct pwrctrl_priv *pwrpriv = &padapter->pwrctrlpriv;
+
+ if (pwrpriv->rpwm == val8) {
+ if (pwrpriv->rpwm_retry == 0)
+ return;
+ }
+ if ((padapter->bDriverStopped == true) ||
+ (padapter->bSurpriseRemoved == true))
+ return;
+ rpwm = val8 | pwrpriv->tog;
+ switch (val8) {
+ case PS_STATE_S1:
+ pwrpriv->cpwm = val8;
+ break;
+ case PS_STATE_S2:/* only for USB normal powersave mode use,
+ * temp mark some code. */
+ case PS_STATE_S3:
+ case PS_STATE_S4:
+ pwrpriv->cpwm = val8;
+ break;
+ default:
+ break;
+ }
+ pwrpriv->rpwm_retry = 0;
+ pwrpriv->rpwm = val8;
+ r8712_write8(padapter, 0x1025FE58, rpwm);
+ pwrpriv->tog += 0x80;
+}
+
+void r8712_set_ps_mode(struct _adapter *padapter, uint ps_mode, uint smart_ps)
+{
+ struct pwrctrl_priv *pwrpriv = &padapter->pwrctrlpriv;
+
+ if (ps_mode > PM_Card_Disable)
+ return;
+ /* if driver is in active state, we dont need set smart_ps.*/
+ if (ps_mode == PS_MODE_ACTIVE)
+ smart_ps = 0;
+ if ((pwrpriv->pwr_mode != ps_mode) || (pwrpriv->smart_ps != smart_ps)) {
+ if (pwrpriv->pwr_mode == PS_MODE_ACTIVE)
+ pwrpriv->bSleep = true;
+ else
+ pwrpriv->bSleep = false;
+ pwrpriv->pwr_mode = ps_mode;
+ pwrpriv->smart_ps = smart_ps;
+ schedule_work(&pwrpriv->SetPSModeWorkItem);
+ }
+}
+
+/*
+ * Caller:ISR handler...
+ *
+ * This will be called when CPWM interrupt is up.
+ *
+ * using to update cpwn of drv; and drv will make a decision to up or
+ * down pwr level
+ */
+void r8712_cpwm_int_hdl(struct _adapter *padapter,
+ struct reportpwrstate_parm *preportpwrstate)
+{
+ struct pwrctrl_priv *pwrpriv = &(padapter->pwrctrlpriv);
+ struct cmd_priv *pcmdpriv = &(padapter->cmdpriv);
+
+ if (pwrpriv->cpwm_tog == ((preportpwrstate->state) & 0x80))
+ return;
+ del_timer(&padapter->pwrctrlpriv.rpwm_check_timer);
+ _enter_pwrlock(&pwrpriv->lock);
+ pwrpriv->cpwm = (preportpwrstate->state) & 0xf;
+ if (pwrpriv->cpwm >= PS_STATE_S2) {
+ if (pwrpriv->alives & CMD_ALIVE)
+ up(&(pcmdpriv->cmd_queue_sema));
+ }
+ pwrpriv->cpwm_tog = (preportpwrstate->state) & 0x80;
+ up(&pwrpriv->lock);
+}
+
+static inline void register_task_alive(struct pwrctrl_priv *pwrctrl, uint tag)
+{
+ pwrctrl->alives |= tag;
+}
+
+static inline void unregister_task_alive(struct pwrctrl_priv *pwrctrl, uint tag)
+{
+ if (pwrctrl->alives & tag)
+ pwrctrl->alives ^= tag;
+}
+
+static void _rpwm_check_handler (struct _adapter *padapter)
+{
+ struct pwrctrl_priv *pwrpriv = &padapter->pwrctrlpriv;
+
+ if (padapter->bDriverStopped == true ||
+ padapter->bSurpriseRemoved == true)
+ return;
+ if (pwrpriv->cpwm != pwrpriv->rpwm)
+ schedule_work(&pwrpriv->rpwm_workitem);
+}
+
+static void SetPSModeWorkItemCallback(struct work_struct *work)
+{
+ struct pwrctrl_priv *pwrpriv = container_of(work,
+ struct pwrctrl_priv, SetPSModeWorkItem);
+ struct _adapter *padapter = container_of(pwrpriv,
+ struct _adapter, pwrctrlpriv);
+ if (!pwrpriv->bSleep) {
+ _enter_pwrlock(&pwrpriv->lock);
+ if (pwrpriv->pwr_mode == PS_MODE_ACTIVE)
+ r8712_set_rpwm(padapter, PS_STATE_S4);
+ up(&pwrpriv->lock);
+ }
+}
+
+static void rpwm_workitem_callback(struct work_struct *work)
+{
+ struct pwrctrl_priv *pwrpriv = container_of(work,
+ struct pwrctrl_priv, rpwm_workitem);
+ struct _adapter *padapter = container_of(pwrpriv,
+ struct _adapter, pwrctrlpriv);
+ if (pwrpriv->cpwm != pwrpriv->rpwm) {
+ _enter_pwrlock(&pwrpriv->lock);
+ r8712_read8(padapter, SDIO_HCPWM);
+ pwrpriv->rpwm_retry = 1;
+ r8712_set_rpwm(padapter, pwrpriv->rpwm);
+ up(&pwrpriv->lock);
+ }
+}
+
+static void rpwm_check_handler (unsigned long data)
+{
+ struct _adapter *adapter = (struct _adapter *)data;
+
+ _rpwm_check_handler(adapter);
+}
+
+void r8712_init_pwrctrl_priv(struct _adapter *padapter)
+{
+ struct pwrctrl_priv *pwrctrlpriv = &padapter->pwrctrlpriv;
+
+ memset((unsigned char *)pwrctrlpriv, 0, sizeof(struct pwrctrl_priv));
+ sema_init(&pwrctrlpriv->lock, 1);
+ pwrctrlpriv->cpwm = PS_STATE_S4;
+ pwrctrlpriv->pwr_mode = PS_MODE_ACTIVE;
+ pwrctrlpriv->smart_ps = 0;
+ pwrctrlpriv->tog = 0x80;
+/* clear RPWM to ensure driver and fw back to initial state. */
+ r8712_write8(padapter, 0x1025FE58, 0);
+ INIT_WORK(&pwrctrlpriv->SetPSModeWorkItem, SetPSModeWorkItemCallback);
+ INIT_WORK(&pwrctrlpriv->rpwm_workitem, rpwm_workitem_callback);
+ setup_timer(&pwrctrlpriv->rpwm_check_timer, rpwm_check_handler,
+ (unsigned long)padapter);
+}
+
+/*
+Caller: r8712_cmd_thread
+
+Check if the fw_pwrstate is okay for issuing cmd.
+If not (cpwm should be is less than P2 state), then the sub-routine
+will raise the cpwm to be greater than or equal to P2.
+
+Calling Context: Passive
+
+Return Value:
+
+_SUCCESS: r8712_cmd_thread can issue cmds to firmware afterwards.
+_FAIL: r8712_cmd_thread can not do anything.
+*/
+sint r8712_register_cmd_alive(struct _adapter *padapter)
+{
+ uint res = _SUCCESS;
+ struct pwrctrl_priv *pwrctrl = &padapter->pwrctrlpriv;
+
+ _enter_pwrlock(&pwrctrl->lock);
+ register_task_alive(pwrctrl, CMD_ALIVE);
+ if (pwrctrl->cpwm < PS_STATE_S2) {
+ r8712_set_rpwm(padapter, PS_STATE_S3);
+ res = _FAIL;
+ }
+ up(&pwrctrl->lock);
+ return res;
+}
+
+/*
+Caller: ISR
+
+If ISR's txdone,
+No more pkts for TX,
+Then driver shall call this fun. to power down firmware again.
+*/
+
+void r8712_unregister_cmd_alive(struct _adapter *padapter)
+{
+ struct pwrctrl_priv *pwrctrl = &padapter->pwrctrlpriv;
+
+ _enter_pwrlock(&pwrctrl->lock);
+ unregister_task_alive(pwrctrl, CMD_ALIVE);
+ if ((pwrctrl->cpwm > PS_STATE_S2) &&
+ (pwrctrl->pwr_mode > PS_MODE_ACTIVE)) {
+ if ((pwrctrl->alives == 0) &&
+ (check_fwstate(&padapter->mlmepriv,
+ _FW_UNDER_LINKING) != true)) {
+ r8712_set_rpwm(padapter, PS_STATE_S0);
+ }
+ }
+ up(&pwrctrl->lock);
+}
diff --git a/drivers/staging/rtl8712/rtl871x_pwrctrl.h b/drivers/staging/rtl8712/rtl871x_pwrctrl.h
new file mode 100644
index 000000000..dbfb55523
--- /dev/null
+++ b/drivers/staging/rtl8712/rtl871x_pwrctrl.h
@@ -0,0 +1,131 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2010 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ * Modifications for inclusion into the Linux staging tree are
+ * Copyright(c) 2010 Larry Finger. All rights reserved.
+ *
+ * Contact information:
+ * WLAN FAE <wlanfae@realtek.com>
+ * Larry Finger <Larry.Finger@lwfinger.net>
+ *
+ ******************************************************************************/
+#ifndef __RTL871X_PWRCTRL_H_
+#define __RTL871X_PWRCTRL_H_
+
+#include "osdep_service.h"
+#include "drv_types.h"
+
+
+#define CMD_ALIVE BIT(2)
+
+enum Power_Mgnt {
+ PS_MODE_ACTIVE = 0,
+ PS_MODE_MIN,
+ PS_MODE_MAX,
+ PS_MODE_DTIM,
+ PS_MODE_VOIP,
+ PS_MODE_UAPSD_WMM,
+ PS_MODE_UAPSD,
+ PS_MODE_IBSS,
+ PS_MODE_WWLAN,
+ PM_Radio_Off,
+ PM_Card_Disable,
+ PS_MODE_NUM
+};
+
+/*
+ BIT[2:0] = HW state
+ BIT[3] = Protocol PS state, 0: register active state,
+ 1: register sleep state
+ BIT[4] = sub-state
+*/
+
+#define PS_DPS BIT(0)
+#define PS_LCLK (PS_DPS)
+#define PS_RF_OFF BIT(1)
+#define PS_ALL_ON BIT(2)
+#define PS_ST_ACTIVE BIT(3)
+#define PS_LP BIT(4) /* low performance */
+
+#define PS_STATE_MASK (0x0F)
+#define PS_STATE_HW_MASK (0x07)
+#define PS_SEQ_MASK (0xc0)
+
+#define PS_STATE(x) (PS_STATE_MASK & (x))
+#define PS_STATE_HW(x) (PS_STATE_HW_MASK & (x))
+#define PS_SEQ(x) (PS_SEQ_MASK & (x))
+
+#define PS_STATE_S0 (PS_DPS)
+#define PS_STATE_S1 (PS_LCLK)
+#define PS_STATE_S2 (PS_RF_OFF)
+#define PS_STATE_S3 (PS_ALL_ON)
+#define PS_STATE_S4 ((PS_ST_ACTIVE) | (PS_ALL_ON))
+
+
+#define PS_IS_RF_ON(x) ((x) & (PS_ALL_ON))
+#define PS_IS_ACTIVE(x) ((x) & (PS_ST_ACTIVE))
+#define CLR_PS_STATE(x) ((x) = ((x) & (0xF0)))
+
+
+struct reportpwrstate_parm {
+ unsigned char mode;
+ unsigned char state; /* the CPWM value */
+ unsigned short rsvd;
+};
+
+static inline void _enter_pwrlock(struct semaphore *plock)
+{
+ _down_sema(plock);
+}
+
+struct pwrctrl_priv {
+ struct semaphore lock;
+ /*volatile*/ u8 rpwm; /* requested power state for fw */
+ /* fw current power state. updated when 1. read from HCPWM or
+ * 2. driver lowers power level */
+ /*volatile*/ u8 cpwm;
+ /*volatile*/ u8 tog; /* toggling */
+ /*volatile*/ u8 cpwm_tog; /* toggling */
+ /*volatile*/ u8 tgt_rpwm; /* wanted power state */
+ uint pwr_mode;
+ uint smart_ps;
+ uint alives;
+ uint ImrContent; /* used to store original imr. */
+ uint bSleep; /* sleep -> active is different from active -> sleep. */
+
+ struct work_struct SetPSModeWorkItem;
+ struct work_struct rpwm_workitem;
+ struct timer_list rpwm_check_timer;
+ u8 rpwm_retry;
+ uint bSetPSModeWorkItemInProgress;
+
+ spinlock_t pnp_pwr_mgnt_lock;
+ s32 pnp_current_pwr_state;
+ u8 pnp_bstop_trx;
+ u8 pnp_wwirp_pending;
+};
+
+void r8712_init_pwrctrl_priv(struct _adapter *adapter);
+sint r8712_register_cmd_alive(struct _adapter *padapter);
+void r8712_unregister_cmd_alive(struct _adapter *padapter);
+void r8712_cpwm_int_hdl(struct _adapter *padapter,
+ struct reportpwrstate_parm *preportpwrstate);
+void r8712_set_ps_mode(struct _adapter *padapter, uint ps_mode,
+ uint smart_ps);
+void r8712_set_rpwm(struct _adapter *padapter, u8 val8);
+
+#endif /* __RTL871X_PWRCTRL_H_ */
diff --git a/drivers/staging/rtl8712/rtl871x_recv.c b/drivers/staging/rtl8712/rtl871x_recv.c
new file mode 100644
index 000000000..046a46c4c
--- /dev/null
+++ b/drivers/staging/rtl8712/rtl871x_recv.c
@@ -0,0 +1,678 @@
+/******************************************************************************
+ * rtl871x_recv.c
+ *
+ * Copyright(c) 2007 - 2010 Realtek Corporation. All rights reserved.
+ * Linux device driver for RTL8192SU
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ * Modifications for inclusion into the Linux staging tree are
+ * Copyright(c) 2010 Larry Finger. All rights reserved.
+ *
+ * Contact information:
+ * WLAN FAE <wlanfae@realtek.com>
+ * Larry Finger <Larry.Finger@lwfinger.net>
+ *
+ ******************************************************************************/
+
+#define _RTL871X_RECV_C_
+
+#include <linux/ip.h>
+#include <linux/slab.h>
+#include <linux/if_ether.h>
+#include <linux/kmemleak.h>
+#include <linux/etherdevice.h>
+
+#include "osdep_service.h"
+#include "drv_types.h"
+#include "recv_osdep.h"
+#include "mlme_osdep.h"
+#include "ethernet.h"
+#include "usb_ops.h"
+#include "wifi.h"
+
+static const u8 SNAP_ETH_TYPE_IPX[2] = {0x81, 0x37};
+
+/* Datagram Delivery Protocol */
+static const u8 SNAP_ETH_TYPE_APPLETALK_AARP[2] = {0x80, 0xf3};
+
+/* Bridge-Tunnel header (for EtherTypes ETH_P_AARP and ETH_P_IPX) */
+static const u8 bridge_tunnel_header[] = {0xaa, 0xaa, 0x03, 0x00, 0x00, 0xf8};
+
+/* Ethernet-II snap header (RFC1042 for most EtherTypes) */
+static const u8 rfc1042_header[] = {0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00};
+
+void _r8712_init_sta_recv_priv(struct sta_recv_priv *psta_recvpriv)
+{
+ memset((u8 *)psta_recvpriv, 0, sizeof(struct sta_recv_priv));
+ spin_lock_init(&psta_recvpriv->lock);
+ _init_queue(&psta_recvpriv->defrag_q);
+}
+
+sint _r8712_init_recv_priv(struct recv_priv *precvpriv,
+ struct _adapter *padapter)
+{
+ sint i;
+ union recv_frame *precvframe;
+
+ memset((unsigned char *)precvpriv, 0, sizeof(struct recv_priv));
+ spin_lock_init(&precvpriv->lock);
+ _init_queue(&precvpriv->free_recv_queue);
+ _init_queue(&precvpriv->recv_pending_queue);
+ precvpriv->adapter = padapter;
+ precvpriv->free_recvframe_cnt = NR_RECVFRAME;
+ precvpriv->pallocated_frame_buf = kmalloc(NR_RECVFRAME *
+ sizeof(union recv_frame) + RXFRAME_ALIGN_SZ,
+ GFP_ATOMIC);
+ if (precvpriv->pallocated_frame_buf == NULL)
+ return _FAIL;
+ kmemleak_not_leak(precvpriv->pallocated_frame_buf);
+ memset(precvpriv->pallocated_frame_buf, 0, NR_RECVFRAME *
+ sizeof(union recv_frame) + RXFRAME_ALIGN_SZ);
+ precvpriv->precv_frame_buf = precvpriv->pallocated_frame_buf +
+ RXFRAME_ALIGN_SZ -
+ ((addr_t)(precvpriv->pallocated_frame_buf) &
+ (RXFRAME_ALIGN_SZ-1));
+ precvframe = (union recv_frame *)precvpriv->precv_frame_buf;
+ for (i = 0; i < NR_RECVFRAME; i++) {
+ INIT_LIST_HEAD(&(precvframe->u.list));
+ list_add_tail(&(precvframe->u.list),
+ &(precvpriv->free_recv_queue.queue));
+ r8712_os_recv_resource_alloc(padapter, precvframe);
+ precvframe->u.hdr.adapter = padapter;
+ precvframe++;
+ }
+ precvpriv->rx_pending_cnt = 1;
+ return r8712_init_recv_priv(precvpriv, padapter);
+}
+
+void _r8712_free_recv_priv(struct recv_priv *precvpriv)
+{
+ kfree(precvpriv->pallocated_frame_buf);
+ r8712_free_recv_priv(precvpriv);
+}
+
+union recv_frame *r8712_alloc_recvframe(struct __queue *pfree_recv_queue)
+{
+ unsigned long irqL;
+ union recv_frame *precvframe;
+ struct list_head *plist, *phead;
+ struct _adapter *padapter;
+ struct recv_priv *precvpriv;
+
+ spin_lock_irqsave(&pfree_recv_queue->lock, irqL);
+ if (list_empty(&pfree_recv_queue->queue))
+ precvframe = NULL;
+ else {
+ phead = &pfree_recv_queue->queue;
+ plist = phead->next;
+ precvframe = LIST_CONTAINOR(plist, union recv_frame, u);
+ list_del_init(&precvframe->u.hdr.list);
+ padapter = precvframe->u.hdr.adapter;
+ if (padapter != NULL) {
+ precvpriv = &padapter->recvpriv;
+ if (pfree_recv_queue == &precvpriv->free_recv_queue)
+ precvpriv->free_recvframe_cnt--;
+ }
+ }
+ spin_unlock_irqrestore(&pfree_recv_queue->lock, irqL);
+ return precvframe;
+}
+
+/*
+caller : defrag; recvframe_chk_defrag in recv_thread (passive)
+pframequeue: defrag_queue : will be accessed in recv_thread (passive)
+
+using spin_lock to protect
+
+*/
+
+void r8712_free_recvframe_queue(struct __queue *pframequeue,
+ struct __queue *pfree_recv_queue)
+{
+ union recv_frame *precvframe;
+ struct list_head *plist, *phead;
+
+ spin_lock(&pframequeue->lock);
+ phead = &pframequeue->queue;
+ plist = phead->next;
+ while (end_of_queue_search(phead, plist) == false) {
+ precvframe = LIST_CONTAINOR(plist, union recv_frame, u);
+ plist = plist->next;
+ r8712_free_recvframe(precvframe, pfree_recv_queue);
+ }
+ spin_unlock(&pframequeue->lock);
+}
+
+sint r8712_recvframe_chkmic(struct _adapter *adapter,
+ union recv_frame *precvframe)
+{
+ sint i, res = _SUCCESS;
+ u32 datalen;
+ u8 miccode[8];
+ u8 bmic_err = false;
+ u8 *pframe, *payload, *pframemic;
+ u8 *mickey, idx, *iv;
+ struct sta_info *stainfo;
+ struct rx_pkt_attrib *prxattrib = &precvframe->u.hdr.attrib;
+ struct security_priv *psecuritypriv = &adapter->securitypriv;
+
+ stainfo = r8712_get_stainfo(&adapter->stapriv, &prxattrib->ta[0]);
+ if (prxattrib->encrypt == _TKIP_) {
+ /* calculate mic code */
+ if (stainfo != NULL) {
+ if (IS_MCAST(prxattrib->ra)) {
+ iv = precvframe->u.hdr.rx_data +
+ prxattrib->hdrlen;
+ idx = iv[3];
+ mickey = &psecuritypriv->XGrprxmickey[(((idx >>
+ 6) & 0x3)) - 1].skey[0];
+ if (psecuritypriv->binstallGrpkey == false)
+ return _FAIL;
+ } else
+ mickey = &stainfo->tkiprxmickey.skey[0];
+ /*icv_len included the mic code*/
+ datalen = precvframe->u.hdr.len - prxattrib->hdrlen -
+ prxattrib->iv_len - prxattrib->icv_len - 8;
+ pframe = precvframe->u.hdr.rx_data;
+ payload = pframe + prxattrib->hdrlen +
+ prxattrib->iv_len;
+ seccalctkipmic(mickey, pframe, payload, datalen,
+ &miccode[0],
+ (unsigned char)prxattrib->priority);
+ pframemic = payload + datalen;
+ bmic_err = false;
+ for (i = 0; i < 8; i++) {
+ if (miccode[i] != *(pframemic + i))
+ bmic_err = true;
+ }
+ if (bmic_err == true) {
+ if (prxattrib->bdecrypted == true)
+ r8712_handle_tkip_mic_err(adapter,
+ (u8)IS_MCAST(prxattrib->ra));
+ res = _FAIL;
+ } else {
+ /* mic checked ok */
+ if ((psecuritypriv->bcheck_grpkey ==
+ false) && (IS_MCAST(prxattrib->ra) ==
+ true))
+ psecuritypriv->bcheck_grpkey = true;
+ }
+ recvframe_pull_tail(precvframe, 8);
+ }
+ }
+ return res;
+}
+
+/* decrypt and set the ivlen,icvlen of the recv_frame */
+union recv_frame *r8712_decryptor(struct _adapter *padapter,
+ union recv_frame *precv_frame)
+{
+ struct rx_pkt_attrib *prxattrib = &precv_frame->u.hdr.attrib;
+ struct security_priv *psecuritypriv = &padapter->securitypriv;
+ union recv_frame *return_packet = precv_frame;
+
+ if ((prxattrib->encrypt > 0) && ((prxattrib->bdecrypted == 0) ||
+ (psecuritypriv->sw_decrypt == true))) {
+ psecuritypriv->hw_decrypted = false;
+ switch (prxattrib->encrypt) {
+ case _WEP40_:
+ case _WEP104_:
+ r8712_wep_decrypt(padapter, (u8 *)precv_frame);
+ break;
+ case _TKIP_:
+ r8712_tkip_decrypt(padapter, (u8 *)precv_frame);
+ break;
+ case _AES_:
+ r8712_aes_decrypt(padapter, (u8 *)precv_frame);
+ break;
+ default:
+ break;
+ }
+ } else if (prxattrib->bdecrypted == 1)
+ psecuritypriv->hw_decrypted = true;
+ return return_packet;
+}
+/*###set the security information in the recv_frame */
+union recv_frame *r8712_portctrl(struct _adapter *adapter,
+ union recv_frame *precv_frame)
+{
+ u8 *psta_addr, *ptr;
+ uint auth_alg;
+ struct recv_frame_hdr *pfhdr;
+ struct sta_info *psta;
+ struct sta_priv *pstapriv;
+ union recv_frame *prtnframe;
+ u16 ether_type;
+
+ pstapriv = &adapter->stapriv;
+ ptr = get_recvframe_data(precv_frame);
+ pfhdr = &precv_frame->u.hdr;
+ psta_addr = pfhdr->attrib.ta;
+ psta = r8712_get_stainfo(pstapriv, psta_addr);
+ auth_alg = adapter->securitypriv.AuthAlgrthm;
+ if (auth_alg == 2) {
+ /* get ether_type */
+ ptr = ptr + pfhdr->attrib.hdrlen + LLC_HEADER_SIZE;
+ memcpy(&ether_type, ptr, 2);
+ ether_type = ntohs((unsigned short)ether_type);
+
+ if ((psta != NULL) && (psta->ieee8021x_blocked)) {
+ /* blocked
+ * only accept EAPOL frame */
+ if (ether_type == 0x888e)
+ prtnframe = precv_frame;
+ else {
+ /*free this frame*/
+ r8712_free_recvframe(precv_frame,
+ &adapter->recvpriv.free_recv_queue);
+ prtnframe = NULL;
+ }
+ } else {
+ /* allowed
+ * check decryption status, and decrypt the
+ * frame if needed */
+ prtnframe = precv_frame;
+ /* check is the EAPOL frame or not (Rekey) */
+ if (ether_type == 0x888e) {
+ /* check Rekey */
+ prtnframe = precv_frame;
+ }
+ }
+ } else
+ prtnframe = precv_frame;
+ return prtnframe;
+}
+
+static sint recv_decache(union recv_frame *precv_frame, u8 bretry,
+ struct stainfo_rxcache *prxcache)
+{
+ sint tid = precv_frame->u.hdr.attrib.priority;
+ u16 seq_ctrl = ((precv_frame->u.hdr.attrib.seq_num&0xffff) << 4) |
+ (precv_frame->u.hdr.attrib.frag_num & 0xf);
+
+ if (tid > 15)
+ return _FAIL;
+ if (seq_ctrl == prxcache->tid_rxseq[tid])
+ return _FAIL;
+ prxcache->tid_rxseq[tid] = seq_ctrl;
+ return _SUCCESS;
+}
+
+static sint sta2sta_data_frame(struct _adapter *adapter,
+ union recv_frame *precv_frame,
+ struct sta_info **psta)
+{
+ u8 *ptr = precv_frame->u.hdr.rx_data;
+ sint ret = _SUCCESS;
+ struct rx_pkt_attrib *pattrib = &precv_frame->u.hdr.attrib;
+ struct sta_priv *pstapriv = &adapter->stapriv;
+ struct mlme_priv *pmlmepriv = &adapter->mlmepriv;
+ u8 *mybssid = get_bssid(pmlmepriv);
+ u8 *myhwaddr = myid(&adapter->eeprompriv);
+ u8 *sta_addr = NULL;
+ sint bmcast = IS_MCAST(pattrib->dst);
+
+ if ((check_fwstate(pmlmepriv, WIFI_ADHOC_STATE) == true) ||
+ (check_fwstate(pmlmepriv, WIFI_ADHOC_MASTER_STATE) == true)) {
+ /* filter packets that SA is myself or multicast or broadcast */
+ if (!memcmp(myhwaddr, pattrib->src, ETH_ALEN))
+ return _FAIL;
+ if ((memcmp(myhwaddr, pattrib->dst, ETH_ALEN)) && (!bmcast))
+ return _FAIL;
+ if (is_zero_ether_addr(pattrib->bssid) ||
+ is_zero_ether_addr(mybssid) ||
+ (memcmp(pattrib->bssid, mybssid, ETH_ALEN)))
+ return _FAIL;
+ sta_addr = pattrib->src;
+ } else if (check_fwstate(pmlmepriv, WIFI_STATION_STATE) == true) {
+ /* For Station mode, sa and bssid should always be BSSID,
+ * and DA is my mac-address */
+ if (memcmp(pattrib->bssid, pattrib->src, ETH_ALEN))
+ return _FAIL;
+ sta_addr = pattrib->bssid;
+ } else if (check_fwstate(pmlmepriv, WIFI_AP_STATE) == true) {
+ if (bmcast) {
+ /* For AP mode, if DA == MCAST, then BSSID should
+ * be also MCAST */
+ if (!IS_MCAST(pattrib->bssid))
+ return _FAIL;
+ } else { /* not mc-frame */
+ /* For AP mode, if DA is non-MCAST, then it must be
+ * BSSID, and bssid == BSSID */
+ if (memcmp(pattrib->bssid, pattrib->dst, ETH_ALEN))
+ return _FAIL;
+ sta_addr = pattrib->src;
+ }
+ } else if (check_fwstate(pmlmepriv, WIFI_MP_STATE) == true) {
+ memcpy(pattrib->dst, GetAddr1Ptr(ptr), ETH_ALEN);
+ memcpy(pattrib->src, GetAddr2Ptr(ptr), ETH_ALEN);
+ memcpy(pattrib->bssid, GetAddr3Ptr(ptr), ETH_ALEN);
+ memcpy(pattrib->ra, pattrib->dst, ETH_ALEN);
+ memcpy(pattrib->ta, pattrib->src, ETH_ALEN);
+ sta_addr = mybssid;
+ } else
+ ret = _FAIL;
+ if (bmcast)
+ *psta = r8712_get_bcmc_stainfo(adapter);
+ else
+ *psta = r8712_get_stainfo(pstapriv, sta_addr); /* get ap_info */
+ if (*psta == NULL) {
+ if (check_fwstate(pmlmepriv, WIFI_MP_STATE) == true)
+ adapter->mppriv.rx_pktloss++;
+ return _FAIL;
+ }
+ return ret;
+}
+
+static sint ap2sta_data_frame(struct _adapter *adapter,
+ union recv_frame *precv_frame,
+ struct sta_info **psta)
+{
+ u8 *ptr = precv_frame->u.hdr.rx_data;
+ struct rx_pkt_attrib *pattrib = &precv_frame->u.hdr.attrib;
+ struct sta_priv *pstapriv = &adapter->stapriv;
+ struct mlme_priv *pmlmepriv = &adapter->mlmepriv;
+ u8 *mybssid = get_bssid(pmlmepriv);
+ u8 *myhwaddr = myid(&adapter->eeprompriv);
+ sint bmcast = IS_MCAST(pattrib->dst);
+
+ if ((check_fwstate(pmlmepriv, WIFI_STATION_STATE) == true)
+ && (check_fwstate(pmlmepriv, _FW_LINKED) == true)) {
+ /* if NULL-frame, drop packet */
+ if ((GetFrameSubType(ptr)) == WIFI_DATA_NULL)
+ return _FAIL;
+ /* drop QoS-SubType Data, including QoS NULL,
+ * excluding QoS-Data */
+ if ((GetFrameSubType(ptr) & WIFI_QOS_DATA_TYPE) ==
+ WIFI_QOS_DATA_TYPE) {
+ if (GetFrameSubType(ptr) & (BIT(4) | BIT(5) | BIT(6)))
+ return _FAIL;
+ }
+
+ /* filter packets that SA is myself or multicast or broadcast */
+ if (!memcmp(myhwaddr, pattrib->src, ETH_ALEN))
+ return _FAIL;
+
+ /* da should be for me */
+ if ((memcmp(myhwaddr, pattrib->dst, ETH_ALEN)) && (!bmcast))
+ return _FAIL;
+ /* check BSSID */
+ if (is_zero_ether_addr(pattrib->bssid) ||
+ is_zero_ether_addr(mybssid) ||
+ (memcmp(pattrib->bssid, mybssid, ETH_ALEN)))
+ return _FAIL;
+ if (bmcast)
+ *psta = r8712_get_bcmc_stainfo(adapter);
+ else
+ *psta = r8712_get_stainfo(pstapriv, pattrib->bssid);
+ if (*psta == NULL)
+ return _FAIL;
+ } else if ((check_fwstate(pmlmepriv, WIFI_MP_STATE) == true) &&
+ (check_fwstate(pmlmepriv, _FW_LINKED) == true)) {
+ memcpy(pattrib->dst, GetAddr1Ptr(ptr), ETH_ALEN);
+ memcpy(pattrib->src, GetAddr2Ptr(ptr), ETH_ALEN);
+ memcpy(pattrib->bssid, GetAddr3Ptr(ptr), ETH_ALEN);
+ memcpy(pattrib->ra, pattrib->dst, ETH_ALEN);
+ memcpy(pattrib->ta, pattrib->src, ETH_ALEN);
+ memcpy(pattrib->bssid, mybssid, ETH_ALEN);
+ *psta = r8712_get_stainfo(pstapriv, pattrib->bssid);
+ if (*psta == NULL)
+ return _FAIL;
+ } else
+ return _FAIL;
+ return _SUCCESS;
+}
+
+static sint sta2ap_data_frame(struct _adapter *adapter,
+ union recv_frame *precv_frame,
+ struct sta_info **psta)
+{
+ struct rx_pkt_attrib *pattrib = &precv_frame->u.hdr.attrib;
+ struct sta_priv *pstapriv = &adapter->stapriv;
+ struct mlme_priv *pmlmepriv = &adapter->mlmepriv;
+ unsigned char *mybssid = get_bssid(pmlmepriv);
+
+ if (check_fwstate(pmlmepriv, WIFI_AP_STATE) == true) {
+ /* For AP mode, if DA is non-MCAST, then it must be BSSID,
+ * and bssid == BSSID
+ * For AP mode, RA=BSSID, TX=STA(SRC_ADDR), A3=DST_ADDR */
+ if (memcmp(pattrib->bssid, mybssid, ETH_ALEN))
+ return _FAIL;
+ *psta = r8712_get_stainfo(pstapriv, pattrib->src);
+ if (*psta == NULL)
+ return _FAIL;
+ }
+ return _SUCCESS;
+}
+
+static sint validate_recv_ctrl_frame(struct _adapter *adapter,
+ union recv_frame *precv_frame)
+{
+ return _FAIL;
+}
+
+static sint validate_recv_mgnt_frame(struct _adapter *adapter,
+ union recv_frame *precv_frame)
+{
+ return _FAIL;
+}
+
+
+static sint validate_recv_data_frame(struct _adapter *adapter,
+ union recv_frame *precv_frame)
+{
+ int res;
+ u8 bretry;
+ u8 *psa, *pda, *pbssid;
+ struct sta_info *psta = NULL;
+ u8 *ptr = precv_frame->u.hdr.rx_data;
+ struct rx_pkt_attrib *pattrib = &precv_frame->u.hdr.attrib;
+ struct security_priv *psecuritypriv = &adapter->securitypriv;
+
+ bretry = GetRetry(ptr);
+ pda = get_da(ptr);
+ psa = get_sa(ptr);
+ pbssid = get_hdr_bssid(ptr);
+ if (pbssid == NULL)
+ return _FAIL;
+ memcpy(pattrib->dst, pda, ETH_ALEN);
+ memcpy(pattrib->src, psa, ETH_ALEN);
+ memcpy(pattrib->bssid, pbssid, ETH_ALEN);
+ switch (pattrib->to_fr_ds) {
+ case 0:
+ memcpy(pattrib->ra, pda, ETH_ALEN);
+ memcpy(pattrib->ta, psa, ETH_ALEN);
+ res = sta2sta_data_frame(adapter, precv_frame, &psta);
+ break;
+ case 1:
+ memcpy(pattrib->ra, pda, ETH_ALEN);
+ memcpy(pattrib->ta, pbssid, ETH_ALEN);
+ res = ap2sta_data_frame(adapter, precv_frame, &psta);
+ break;
+ case 2:
+ memcpy(pattrib->ra, pbssid, ETH_ALEN);
+ memcpy(pattrib->ta, psa, ETH_ALEN);
+ res = sta2ap_data_frame(adapter, precv_frame, &psta);
+ break;
+ case 3:
+ memcpy(pattrib->ra, GetAddr1Ptr(ptr), ETH_ALEN);
+ memcpy(pattrib->ta, GetAddr2Ptr(ptr), ETH_ALEN);
+ return _FAIL;
+ default:
+ return _FAIL;
+ }
+ if (res == _FAIL)
+ return _FAIL;
+ if (psta == NULL)
+ return _FAIL;
+ precv_frame->u.hdr.psta = psta;
+ pattrib->amsdu = 0;
+ /* parsing QC field */
+ if (pattrib->qos == 1) {
+ pattrib->priority = GetPriority((ptr + 24));
+ pattrib->ack_policy = GetAckpolicy((ptr + 24));
+ pattrib->amsdu = GetAMsdu((ptr + 24));
+ pattrib->hdrlen = pattrib->to_fr_ds == 3 ? 32 : 26;
+ } else {
+ pattrib->priority = 0;
+ pattrib->hdrlen = (pattrib->to_fr_ds == 3) ? 30 : 24;
+ }
+
+ if (pattrib->order)/*HT-CTRL 11n*/
+ pattrib->hdrlen += 4;
+ precv_frame->u.hdr.preorder_ctrl =
+ &psta->recvreorder_ctrl[pattrib->priority];
+
+ /* decache, drop duplicate recv packets */
+ if (recv_decache(precv_frame, bretry, &psta->sta_recvpriv.rxcache) ==
+ _FAIL)
+ return _FAIL;
+
+ if (pattrib->privacy) {
+ GET_ENCRY_ALGO(psecuritypriv, psta, pattrib->encrypt,
+ IS_MCAST(pattrib->ra));
+ SET_ICE_IV_LEN(pattrib->iv_len, pattrib->icv_len,
+ pattrib->encrypt);
+ } else {
+ pattrib->encrypt = 0;
+ pattrib->iv_len = pattrib->icv_len = 0;
+ }
+ return _SUCCESS;
+}
+
+sint r8712_validate_recv_frame(struct _adapter *adapter,
+ union recv_frame *precv_frame)
+{
+ /*shall check frame subtype, to / from ds, da, bssid */
+ /*then call check if rx seq/frag. duplicated.*/
+
+ u8 type;
+ u8 subtype;
+ sint retval = _SUCCESS;
+ struct rx_pkt_attrib *pattrib = &precv_frame->u.hdr.attrib;
+
+ u8 *ptr = precv_frame->u.hdr.rx_data;
+ u8 ver = (unsigned char)(*ptr) & 0x3;
+
+ /*add version chk*/
+ if (ver != 0)
+ return _FAIL;
+ type = GetFrameType(ptr);
+ subtype = GetFrameSubType(ptr); /*bit(7)~bit(2)*/
+ pattrib->to_fr_ds = get_tofr_ds(ptr);
+ pattrib->frag_num = GetFragNum(ptr);
+ pattrib->seq_num = GetSequence(ptr);
+ pattrib->pw_save = GetPwrMgt(ptr);
+ pattrib->mfrag = GetMFrag(ptr);
+ pattrib->mdata = GetMData(ptr);
+ pattrib->privacy = GetPrivacy(ptr);
+ pattrib->order = GetOrder(ptr);
+ switch (type) {
+ case WIFI_MGT_TYPE: /*mgnt*/
+ retval = validate_recv_mgnt_frame(adapter, precv_frame);
+ break;
+ case WIFI_CTRL_TYPE:/*ctrl*/
+ retval = validate_recv_ctrl_frame(adapter, precv_frame);
+ break;
+ case WIFI_DATA_TYPE: /*data*/
+ pattrib->qos = (subtype & BIT(7)) ? 1 : 0;
+ retval = validate_recv_data_frame(adapter, precv_frame);
+ break;
+ default:
+ return _FAIL;
+ }
+ return retval;
+}
+
+sint r8712_wlanhdr_to_ethhdr(union recv_frame *precvframe)
+{
+ /*remove the wlanhdr and add the eth_hdr*/
+ sint rmv_len;
+ u16 len;
+ u8 bsnaphdr;
+ u8 *psnap_type;
+ struct ieee80211_snap_hdr *psnap;
+ struct _adapter *adapter = precvframe->u.hdr.adapter;
+ struct mlme_priv *pmlmepriv = &adapter->mlmepriv;
+
+ u8 *ptr = get_recvframe_data(precvframe); /*point to frame_ctrl field*/
+ struct rx_pkt_attrib *pattrib = &precvframe->u.hdr.attrib;
+
+ if (pattrib->encrypt)
+ recvframe_pull_tail(precvframe, pattrib->icv_len);
+ psnap = (struct ieee80211_snap_hdr *)(ptr + pattrib->hdrlen +
+ pattrib->iv_len);
+ psnap_type = ptr + pattrib->hdrlen + pattrib->iv_len + SNAP_SIZE;
+ /* convert hdr + possible LLC headers into Ethernet header */
+ if ((!memcmp(psnap, (void *)rfc1042_header, SNAP_SIZE) &&
+ (memcmp(psnap_type, (void *)SNAP_ETH_TYPE_IPX, 2)) &&
+ (memcmp(psnap_type, (void *)SNAP_ETH_TYPE_APPLETALK_AARP, 2))) ||
+ !memcmp(psnap, (void *)bridge_tunnel_header, SNAP_SIZE)) {
+ /* remove RFC1042 or Bridge-Tunnel encapsulation and
+ * replace EtherType */
+ bsnaphdr = true;
+ } else {
+ /* Leave Ethernet header part of hdr and full payload */
+ bsnaphdr = false;
+ }
+ rmv_len = pattrib->hdrlen + pattrib->iv_len +
+ (bsnaphdr ? SNAP_SIZE : 0);
+ len = precvframe->u.hdr.len - rmv_len;
+ if (check_fwstate(pmlmepriv, WIFI_MP_STATE) == true) {
+ ptr += rmv_len;
+ *ptr = 0x87;
+ *(ptr+1) = 0x12;
+ /* append rx status for mp test packets */
+ ptr = recvframe_pull(precvframe, (rmv_len -
+ sizeof(struct ethhdr) + 2) - 24);
+ memcpy(ptr, get_rxmem(precvframe), 24);
+ ptr += 24;
+ } else
+ ptr = recvframe_pull(precvframe, (rmv_len -
+ sizeof(struct ethhdr) + (bsnaphdr ? 2 : 0)));
+
+ memcpy(ptr, pattrib->dst, ETH_ALEN);
+ memcpy(ptr+ETH_ALEN, pattrib->src, ETH_ALEN);
+ if (!bsnaphdr) {
+ len = htons(len);
+ memcpy(ptr + 12, &len, 2);
+ }
+ return _SUCCESS;
+}
+
+s32 r8712_recv_entry(union recv_frame *precvframe)
+{
+ struct _adapter *padapter;
+ struct recv_priv *precvpriv;
+
+ s32 ret = _SUCCESS;
+
+ padapter = precvframe->u.hdr.adapter;
+ precvpriv = &(padapter->recvpriv);
+
+ padapter->ledpriv.LedControlHandler(padapter, LED_CTL_RX);
+
+ ret = recv_func(padapter, precvframe);
+ if (ret == _FAIL)
+ goto _recv_entry_drop;
+ precvpriv->rx_pkts++;
+ precvpriv->rx_bytes += (uint)(precvframe->u.hdr.rx_tail -
+ precvframe->u.hdr.rx_data);
+ return ret;
+_recv_entry_drop:
+ precvpriv->rx_drop++;
+ padapter->mppriv.rx_pktloss = precvpriv->rx_drop;
+ return ret;
+}
diff --git a/drivers/staging/rtl8712/rtl871x_recv.h b/drivers/staging/rtl8712/rtl871x_recv.h
new file mode 100644
index 000000000..77487bb9d
--- /dev/null
+++ b/drivers/staging/rtl8712/rtl871x_recv.h
@@ -0,0 +1,216 @@
+#ifndef _RTL871X_RECV_H_
+#define _RTL871X_RECV_H_
+
+#include "osdep_service.h"
+#include "drv_types.h"
+
+#define NR_RECVFRAME 256
+
+#define RXFRAME_ALIGN 8
+#define RXFRAME_ALIGN_SZ (1 << RXFRAME_ALIGN)
+
+#define MAX_SUBFRAME_COUNT 64
+
+#define SNAP_SIZE sizeof(struct ieee80211_snap_hdr)
+
+/* for Rx reordering buffer control */
+struct recv_reorder_ctrl {
+ struct _adapter *padapter;
+ u16 indicate_seq; /* =wstart_b, init_value=0xffff */
+ u16 wend_b;
+ u8 wsize_b;
+ struct __queue pending_recvframe_queue;
+ struct timer_list reordering_ctrl_timer;
+};
+
+struct stainfo_rxcache {
+ u16 tid_rxseq[16];
+};
+
+#define PHY_RSSI_SLID_WIN_MAX 100
+#define PHY_LINKQUALITY_SLID_WIN_MAX 20
+
+
+struct smooth_rssi_data {
+ u32 elements[100]; /* array to store values */
+ u32 index; /* index to current array to store */
+ u32 total_num; /* num of valid elements */
+ u32 total_val; /* sum of valid elements */
+};
+
+struct rx_pkt_attrib {
+
+ u8 amsdu;
+ u8 order;
+ u8 qos;
+ u8 to_fr_ds;
+ u8 frag_num;
+ u16 seq_num;
+ u8 pw_save;
+ u8 mfrag;
+ u8 mdata;
+ u8 privacy; /* in frame_ctrl field */
+ u8 bdecrypted;
+ int hdrlen; /* the WLAN Header Len */
+ int encrypt; /* 0 no encrypt. != 0 encrypt algorith */
+ int iv_len;
+ int icv_len;
+ int priority;
+ int ack_policy;
+ u8 crc_err;
+ u8 dst[ETH_ALEN];
+ u8 src[ETH_ALEN];
+ u8 ta[ETH_ALEN];
+ u8 ra[ETH_ALEN];
+ u8 bssid[ETH_ALEN];
+ u8 tcpchk_valid; /* 0: invalid, 1: valid */
+ u8 ip_chkrpt; /* 0: incorrect, 1: correct */
+ u8 tcp_chkrpt; /* 0: incorrect, 1: correct */
+ u8 signal_qual;
+ s8 rx_mimo_signal_qual[2];
+ u8 mcs_rate;
+ u8 htc;
+ u8 signal_strength;
+};
+
+/*
+accesser of recv_priv: recv_entry(dispatch / passive level);
+recv_thread(passive) ; returnpkt(dispatch)
+; halt(passive) ;
+
+using enter_critical section to protect
+*/
+struct recv_priv {
+ spinlock_t lock;
+ struct __queue free_recv_queue;
+ struct __queue recv_pending_queue;
+ u8 *pallocated_frame_buf;
+ u8 *precv_frame_buf;
+ uint free_recvframe_cnt;
+ struct _adapter *adapter;
+ uint rx_bytes;
+ uint rx_pkts;
+ uint rx_drop;
+ uint rx_icv_err;
+ uint rx_largepacket_crcerr;
+ uint rx_smallpacket_crcerr;
+ uint rx_middlepacket_crcerr;
+ u8 rx_pending_cnt;
+ uint ff_hwaddr;
+ struct tasklet_struct recv_tasklet;
+ struct sk_buff_head free_recv_skb_queue;
+ struct sk_buff_head rx_skb_queue;
+ u8 *pallocated_recv_buf;
+ u8 *precv_buf; /* 4 alignment */
+ struct __queue free_recv_buf_queue;
+ u32 free_recv_buf_queue_cnt;
+ /* For the phy informatiom */
+ s8 rssi;
+ u8 signal;
+ u8 noise;
+ u8 fw_rssi;
+ struct smooth_rssi_data signal_qual_data;
+ struct smooth_rssi_data signal_strength_data;
+};
+
+struct sta_recv_priv {
+ spinlock_t lock;
+ sint option;
+ struct __queue defrag_q; /* keeping the fragment frame until defrag */
+ struct stainfo_rxcache rxcache;
+ uint sta_rx_bytes;
+ uint sta_rx_pkts;
+ uint sta_rx_fail;
+};
+
+#include "rtl8712_recv.h"
+
+/* get a free recv_frame from pfree_recv_queue */
+union recv_frame *r8712_alloc_recvframe(struct __queue *pfree_recv_queue);
+int r8712_free_recvframe(union recv_frame *precvframe,
+ struct __queue *pfree_recv_queue);
+void r8712_free_recvframe_queue(struct __queue *pframequeue,
+ struct __queue *pfree_recv_queue);
+int r8712_wlanhdr_to_ethhdr(union recv_frame *precvframe);
+int recv_func(struct _adapter *padapter, void *pcontext);
+
+static inline u8 *get_rxmem(union recv_frame *precvframe)
+{
+ /* always return rx_head... */
+ if (precvframe == NULL)
+ return NULL;
+ return precvframe->u.hdr.rx_head;
+}
+
+static inline u8 *get_recvframe_data(union recv_frame *precvframe)
+{
+ /* always return rx_data */
+ if (precvframe == NULL)
+ return NULL;
+ return precvframe->u.hdr.rx_data;
+}
+
+static inline u8 *recvframe_pull(union recv_frame *precvframe, sint sz)
+{
+ /* used for extract sz bytes from rx_data, update rx_data and return
+ * the updated rx_data to the caller */
+ if (precvframe == NULL)
+ return NULL;
+ precvframe->u.hdr.rx_data += sz;
+ if (precvframe->u.hdr.rx_data > precvframe->u.hdr.rx_tail) {
+ precvframe->u.hdr.rx_data -= sz;
+ return NULL;
+ }
+ precvframe->u.hdr.len -= sz;
+ return precvframe->u.hdr.rx_data;
+}
+
+static inline u8 *recvframe_put(union recv_frame *precvframe, sint sz)
+{
+ /* used for append sz bytes from ptr to rx_tail, update rx_tail and
+ * return the updated rx_tail to the caller
+ * after putting, rx_tail must be still larger than rx_end. */
+ if (precvframe == NULL)
+ return NULL;
+ precvframe->u.hdr.rx_tail += sz;
+ if (precvframe->u.hdr.rx_tail > precvframe->u.hdr.rx_end) {
+ precvframe->u.hdr.rx_tail -= sz;
+ return NULL;
+ }
+ precvframe->u.hdr.len += sz;
+ return precvframe->u.hdr.rx_tail;
+}
+
+static inline u8 *recvframe_pull_tail(union recv_frame *precvframe, sint sz)
+{
+ /* rmv data from rx_tail (by yitsen)
+ * used for extract sz bytes from rx_end, update rx_end and return the
+ * updated rx_end to the caller
+ * after pulling, rx_end must be still larger than rx_data. */
+ if (precvframe == NULL)
+ return NULL;
+ precvframe->u.hdr.rx_tail -= sz;
+ if (precvframe->u.hdr.rx_tail < precvframe->u.hdr.rx_data) {
+ precvframe->u.hdr.rx_tail += sz;
+ return NULL;
+ }
+ precvframe->u.hdr.len -= sz;
+ return precvframe->u.hdr.rx_tail;
+}
+
+struct sta_info;
+
+void _r8712_init_sta_recv_priv(struct sta_recv_priv *psta_recvpriv);
+sint r8712_recvframe_chkmic(struct _adapter *adapter,
+ union recv_frame *precvframe);
+union recv_frame *r8712_decryptor(struct _adapter *adapter,
+ union recv_frame *precv_frame);
+union recv_frame *r8712_recvframe_chk_defrag(struct _adapter *adapter,
+ union recv_frame *precv_frame);
+int r8712_validate_recv_frame(struct _adapter *adapter,
+ union recv_frame *precv_frame);
+union recv_frame *r8712_portctrl(struct _adapter *adapter,
+ union recv_frame *precv_frame);
+
+#endif
+
diff --git a/drivers/staging/rtl8712/rtl871x_rf.h b/drivers/staging/rtl8712/rtl871x_rf.h
new file mode 100644
index 000000000..133ed6462
--- /dev/null
+++ b/drivers/staging/rtl8712/rtl871x_rf.h
@@ -0,0 +1,68 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2010 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ * Modifications for inclusion into the Linux staging tree are
+ * Copyright(c) 2010 Larry Finger. All rights reserved.
+ *
+ * Contact information:
+ * WLAN FAE <wlanfae@realtek.com>
+ * Larry Finger <Larry.Finger@lwfinger.net>
+ *
+ ******************************************************************************/
+#ifndef __RTL871X_RF_H_
+#define __RTL871X_RF_H_
+
+#include "rtl871x_cmd.h"
+#include "rtl871x_mp_phy_regdef.h"
+
+#define OFDM_PHY 1
+#define MIXED_PHY 2
+#define CCK_PHY 3
+#define NumRates (13)
+#define RTL8711_RF_MAX_SENS 6
+#define RTL8711_RF_DEF_SENS 4
+#define NUM_CHANNELS 15
+
+struct regulatory_class {
+ u32 starting_freq; /*MHz, */
+ u8 channel_set[NUM_CHANNELS];
+ u8 channel_cck_power[NUM_CHANNELS]; /*dbm*/
+ u8 channel_ofdm_power[NUM_CHANNELS];/*dbm*/
+ u8 txpower_limit; /*dbm*/
+ u8 channel_spacing; /*MHz*/
+ u8 modem;
+};
+
+enum _REG_PREAMBLE_MODE {
+ PREAMBLE_LONG = 1,
+ PREAMBLE_AUTO = 2,
+ PREAMBLE_SHORT = 3,
+};
+
+enum {
+ RTL8712_RFC_1T = 0x10,
+ RTL8712_RFC_2T = 0x20,
+ RTL8712_RFC_1R = 0x01,
+ RTL8712_RFC_2R = 0x02,
+ RTL8712_RFC_1T1R = 0x11,
+ RTL8712_RFC_1T2R = 0x12,
+ RTL8712_RFC_TURBO = 0x92,
+ RTL8712_RFC_2T2R = 0x22
+};
+
+#endif /*_RTL8711_RF_H_*/
+
diff --git a/drivers/staging/rtl8712/rtl871x_security.c b/drivers/staging/rtl8712/rtl871x_security.c
new file mode 100644
index 000000000..bcd1a5128
--- /dev/null
+++ b/drivers/staging/rtl8712/rtl871x_security.c
@@ -0,0 +1,1400 @@
+/******************************************************************************
+ * rtl871x_security.c
+ *
+ * Copyright(c) 2007 - 2010 Realtek Corporation. All rights reserved.
+ * Linux device driver for RTL8192SU
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ * Modifications for inclusion into the Linux staging tree are
+ * Copyright(c) 2010 Larry Finger. All rights reserved.
+ *
+ * Contact information:
+ * WLAN FAE <wlanfae@realtek.com>
+ * Larry Finger <Larry.Finger@lwfinger.net>
+ *
+ ******************************************************************************/
+
+#define _RTL871X_SECURITY_C_
+
+#include <linux/compiler.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/kref.h>
+#include <linux/netdevice.h>
+#include <linux/skbuff.h>
+#include <linux/circ_buf.h>
+#include <linux/uaccess.h>
+#include <asm/byteorder.h>
+#include <linux/atomic.h>
+#include <linux/semaphore.h>
+
+#include "osdep_service.h"
+#include "drv_types.h"
+#include "wifi.h"
+#include "osdep_intf.h"
+
+/* =====WEP related===== */
+
+#define CRC32_POLY 0x04c11db7
+
+struct arc4context {
+ u32 x;
+ u32 y;
+ u8 state[256];
+};
+
+static void arcfour_init(struct arc4context *parc4ctx, u8 *key, u32 key_len)
+{
+ u32 t, u;
+ u32 keyindex;
+ u32 stateindex;
+ u8 *state;
+ u32 counter;
+
+ state = parc4ctx->state;
+ parc4ctx->x = 0;
+ parc4ctx->y = 0;
+ for (counter = 0; counter < 256; counter++)
+ state[counter] = (u8)counter;
+ keyindex = 0;
+ stateindex = 0;
+ for (counter = 0; counter < 256; counter++) {
+ t = state[counter];
+ stateindex = (stateindex + key[keyindex] + t) & 0xff;
+ u = state[stateindex];
+ state[stateindex] = (u8)t;
+ state[counter] = (u8)u;
+ if (++keyindex >= key_len)
+ keyindex = 0;
+ }
+}
+
+static u32 arcfour_byte(struct arc4context *parc4ctx)
+{
+ u32 x;
+ u32 y;
+ u32 sx, sy;
+ u8 *state;
+
+ state = parc4ctx->state;
+ x = (parc4ctx->x + 1) & 0xff;
+ sx = state[x];
+ y = (sx + parc4ctx->y) & 0xff;
+ sy = state[y];
+ parc4ctx->x = x;
+ parc4ctx->y = y;
+ state[y] = (u8)sx;
+ state[x] = (u8)sy;
+ return state[(sx + sy) & 0xff];
+}
+
+static void arcfour_encrypt(struct arc4context *parc4ctx,
+ u8 *dest, u8 *src, u32 len)
+{
+ u32 i;
+
+ for (i = 0; i < len; i++)
+ dest[i] = src[i] ^ (unsigned char)arcfour_byte(parc4ctx);
+}
+
+static sint bcrc32initialized;
+static u32 crc32_table[256];
+
+static u8 crc32_reverseBit(u8 data)
+{
+ return ((u8)(data << 7) & 0x80) | ((data << 5) & 0x40) | ((data << 3)
+ & 0x20) | ((data << 1) & 0x10) | ((data >> 1) & 0x08) |
+ ((data >> 3) & 0x04) | ((data >> 5) & 0x02) | ((data >> 7) &
+ 0x01);
+}
+
+static void crc32_init(void)
+{
+ if (bcrc32initialized == 1)
+ return;
+ else {
+ sint i, j;
+ u32 c;
+ u8 *p = (u8 *)&c, *p1;
+ u8 k;
+
+ c = 0x12340000;
+ for (i = 0; i < 256; ++i) {
+ k = crc32_reverseBit((u8)i);
+ for (c = ((u32)k) << 24, j = 8; j > 0; --j)
+ c = c & 0x80000000 ? (c << 1) ^ CRC32_POLY :
+ (c << 1);
+ p1 = (u8 *)&crc32_table[i];
+ p1[0] = crc32_reverseBit(p[3]);
+ p1[1] = crc32_reverseBit(p[2]);
+ p1[2] = crc32_reverseBit(p[1]);
+ p1[3] = crc32_reverseBit(p[0]);
+ }
+ bcrc32initialized = 1;
+ }
+}
+
+static u32 getcrc32(u8 *buf, u32 len)
+{
+ u8 *p;
+ u32 crc;
+
+ if (!bcrc32initialized)
+ crc32_init();
+ crc = 0xffffffff; /* preload shift register, per CRC-32 spec */
+ for (p = buf; len > 0; ++p, --len)
+ crc = crc32_table[(crc ^ *p) & 0xff] ^ (crc >> 8);
+ return ~crc; /* transmit complement, per CRC-32 spec */
+}
+
+/*
+ Need to consider the fragment situation
+*/
+void r8712_wep_encrypt(struct _adapter *padapter, u8 *pxmitframe)
+{ /* exclude ICV */
+ unsigned char crc[4];
+ struct arc4context mycontext;
+ u32 curfragnum, length, keylength;
+ u8 *pframe, *payload, *iv; /*,*wepkey*/
+ u8 wepkey[16];
+ struct pkt_attrib *pattrib = &((struct xmit_frame *)
+ pxmitframe)->attrib;
+ struct security_priv *psecuritypriv = &padapter->securitypriv;
+ struct xmit_priv *pxmitpriv = &padapter->xmitpriv;
+
+ if (((struct xmit_frame *)pxmitframe)->buf_addr == NULL)
+ return;
+ pframe = ((struct xmit_frame *)pxmitframe)->buf_addr+TXDESC_OFFSET;
+ /*start to encrypt each fragment*/
+ if ((pattrib->encrypt == _WEP40_) || (pattrib->encrypt == _WEP104_)) {
+ keylength = psecuritypriv->DefKeylen[psecuritypriv->
+ PrivacyKeyIndex];
+ for (curfragnum = 0; curfragnum < pattrib->nr_frags;
+ curfragnum++) {
+ iv = pframe+pattrib->hdrlen;
+ memcpy(&wepkey[0], iv, 3);
+ memcpy(&wepkey[3], &psecuritypriv->DefKey[
+ psecuritypriv->PrivacyKeyIndex].skey[0],
+ keylength);
+ payload = pframe+pattrib->iv_len+pattrib->hdrlen;
+ if ((curfragnum + 1) == pattrib->nr_frags) {
+ length = pattrib->last_txcmdsz-pattrib->
+ hdrlen-pattrib->iv_len -
+ pattrib->icv_len;
+ *((u32 *)crc) = cpu_to_le32(getcrc32(
+ payload, length));
+ arcfour_init(&mycontext, wepkey, 3 + keylength);
+ arcfour_encrypt(&mycontext, payload, payload,
+ length);
+ arcfour_encrypt(&mycontext, payload + length,
+ crc, 4);
+ } else {
+ length = pxmitpriv->frag_len-pattrib->hdrlen -
+ pattrib->iv_len-pattrib->icv_len;
+ *((u32 *)crc) = cpu_to_le32(getcrc32(
+ payload, length));
+ arcfour_init(&mycontext, wepkey, 3 + keylength);
+ arcfour_encrypt(&mycontext, payload, payload,
+ length);
+ arcfour_encrypt(&mycontext, payload+length,
+ crc, 4);
+ pframe += pxmitpriv->frag_len;
+ pframe = (u8 *)RND4((addr_t)(pframe));
+ }
+ }
+ }
+}
+
+void r8712_wep_decrypt(struct _adapter *padapter, u8 *precvframe)
+{
+ /* exclude ICV */
+ u8 crc[4];
+ struct arc4context mycontext;
+ u32 length, keylength;
+ u8 *pframe, *payload, *iv, wepkey[16];
+ u8 keyindex;
+ struct rx_pkt_attrib *prxattrib = &(((union recv_frame *)
+ precvframe)->u.hdr.attrib);
+ struct security_priv *psecuritypriv = &padapter->securitypriv;
+
+ pframe = (unsigned char *)((union recv_frame *)precvframe)->
+ u.hdr.rx_data;
+ /* start to decrypt recvframe */
+ if ((prxattrib->encrypt == _WEP40_) || (prxattrib->encrypt ==
+ _WEP104_)) {
+ iv = pframe + prxattrib->hdrlen;
+ keyindex = (iv[3] & 0x3);
+ keylength = psecuritypriv->DefKeylen[keyindex];
+ memcpy(&wepkey[0], iv, 3);
+ memcpy(&wepkey[3], &psecuritypriv->DefKey[
+ psecuritypriv->PrivacyKeyIndex].skey[0],
+ keylength);
+ length = ((union recv_frame *)precvframe)->
+ u.hdr.len-prxattrib->hdrlen-prxattrib->iv_len;
+ payload = pframe+prxattrib->iv_len+prxattrib->hdrlen;
+ /* decrypt payload include icv */
+ arcfour_init(&mycontext, wepkey, 3 + keylength);
+ arcfour_encrypt(&mycontext, payload, payload, length);
+ /* calculate icv and compare the icv */
+ *((u32 *)crc) = cpu_to_le32(getcrc32(payload, length - 4));
+ }
+}
+
+/* 3 =====TKIP related===== */
+
+static u32 secmicgetuint32(u8 *p)
+/* Convert from Byte[] to Us4Byte32 in a portable way */
+{
+ s32 i;
+ u32 res = 0;
+
+ for (i = 0; i < 4; i++)
+ res |= ((u32)(*p++)) << (8 * i);
+ return res;
+}
+
+static void secmicputuint32(u8 *p, u32 val)
+/* Convert from Us4Byte32 to Byte[] in a portable way */
+{
+ long i;
+
+ for (i = 0; i < 4; i++) {
+ *p++ = (u8) (val & 0xff);
+ val >>= 8;
+ }
+}
+
+static void secmicclear(struct mic_data *pmicdata)
+{
+/* Reset the state to the empty message. */
+ pmicdata->L = pmicdata->K0;
+ pmicdata->R = pmicdata->K1;
+ pmicdata->nBytesInM = 0;
+ pmicdata->M = 0;
+}
+
+void r8712_secmicsetkey(struct mic_data *pmicdata, u8 *key)
+{
+ /* Set the key */
+ pmicdata->K0 = secmicgetuint32(key);
+ pmicdata->K1 = secmicgetuint32(key + 4);
+ /* and reset the message */
+ secmicclear(pmicdata);
+}
+
+static void secmicappendbyte(struct mic_data *pmicdata, u8 b)
+{
+ /* Append the byte to our word-sized buffer */
+ pmicdata->M |= ((u32)b) << (8 * pmicdata->nBytesInM);
+ pmicdata->nBytesInM++;
+ /* Process the word if it is full. */
+ if (pmicdata->nBytesInM >= 4) {
+ pmicdata->L ^= pmicdata->M;
+ pmicdata->R ^= ROL32(pmicdata->L, 17);
+ pmicdata->L += pmicdata->R;
+ pmicdata->R ^= ((pmicdata->L & 0xff00ff00) >> 8) |
+ ((pmicdata->L & 0x00ff00ff) << 8);
+ pmicdata->L += pmicdata->R;
+ pmicdata->R ^= ROL32(pmicdata->L, 3);
+ pmicdata->L += pmicdata->R;
+ pmicdata->R ^= ROR32(pmicdata->L, 2);
+ pmicdata->L += pmicdata->R;
+ /* Clear the buffer */
+ pmicdata->M = 0;
+ pmicdata->nBytesInM = 0;
+ }
+}
+
+void r8712_secmicappend(struct mic_data *pmicdata, u8 *src, u32 nbytes)
+{
+ /* This is simple */
+ while (nbytes > 0) {
+ secmicappendbyte(pmicdata, *src++);
+ nbytes--;
+ }
+}
+
+void r8712_secgetmic(struct mic_data *pmicdata, u8 *dst)
+{
+ /* Append the minimum padding */
+ secmicappendbyte(pmicdata, 0x5a);
+ secmicappendbyte(pmicdata, 0);
+ secmicappendbyte(pmicdata, 0);
+ secmicappendbyte(pmicdata, 0);
+ secmicappendbyte(pmicdata, 0);
+ /* and then zeroes until the length is a multiple of 4 */
+ while (pmicdata->nBytesInM != 0)
+ secmicappendbyte(pmicdata, 0);
+ /* The appendByte function has already computed the result. */
+ secmicputuint32(dst, pmicdata->L);
+ secmicputuint32(dst + 4, pmicdata->R);
+ /* Reset to the empty message. */
+ secmicclear(pmicdata);
+}
+
+void seccalctkipmic(u8 *key, u8 *header, u8 *data, u32 data_len, u8 *mic_code,
+ u8 pri)
+{
+
+ struct mic_data micdata;
+ u8 priority[4] = {0x0, 0x0, 0x0, 0x0};
+
+ r8712_secmicsetkey(&micdata, key);
+ priority[0] = pri;
+ /* Michael MIC pseudo header: DA, SA, 3 x 0, Priority */
+ if (header[1] & 1) { /* ToDS==1 */
+ r8712_secmicappend(&micdata, &header[16], 6); /* DA */
+ if (header[1] & 2) /* From Ds==1 */
+ r8712_secmicappend(&micdata, &header[24], 6);
+ else
+ r8712_secmicappend(&micdata, &header[10], 6);
+ } else { /* ToDS==0 */
+ r8712_secmicappend(&micdata, &header[4], 6); /* DA */
+ if (header[1] & 2) /* From Ds==1 */
+ r8712_secmicappend(&micdata, &header[16], 6);
+ else
+ r8712_secmicappend(&micdata, &header[10], 6);
+ }
+ r8712_secmicappend(&micdata, &priority[0], 4);
+ r8712_secmicappend(&micdata, data, data_len);
+ r8712_secgetmic(&micdata, mic_code);
+}
+
+/* macros for extraction/creation of unsigned char/unsigned short values */
+#define RotR1(v16) ((((v16) >> 1) & 0x7FFF) ^ (((v16) & 1) << 15))
+#define Lo8(v16) ((u8)((v16) & 0x00FF))
+#define Hi8(v16) ((u8)(((v16) >> 8) & 0x00FF))
+#define Lo16(v32) ((u16)((v32) & 0xFFFF))
+#define Hi16(v32) ((u16)(((v32) >> 16) & 0xFFFF))
+#define Mk16(hi, lo) ((lo) ^ (((u16)(hi)) << 8))
+
+/* select the Nth 16-bit word of the temporal key unsigned char array TK[] */
+#define TK16(N) Mk16(tk[2 * (N) + 1], tk[2 * (N)])
+
+/* S-box lookup: 16 bits --> 16 bits */
+#define _S_(v16) (Sbox1[0][Lo8(v16)] ^ Sbox1[1][Hi8(v16)])
+
+/* fixed algorithm "parameters" */
+#define PHASE1_LOOP_CNT 8 /* this needs to be "big enough" */
+#define TA_SIZE 6 /* 48-bit transmitter address */
+#define TK_SIZE 16 /* 128-bit temporal key */
+#define P1K_SIZE 10 /* 80-bit Phase1 key */
+#define RC4_KEY_SIZE 16 /* 128-bit RC4KEY (104 bits unknown) */
+
+
+/* 2-unsigned char by 2-unsigned char subset of the full AES S-box table */
+static const unsigned short Sbox1[2][256] = {/* Sbox for hash (can be in ROM) */
+ {
+ 0xC6A5, 0xF884, 0xEE99, 0xF68D, 0xFF0D, 0xD6BD, 0xDEB1, 0x9154,
+ 0x6050, 0x0203, 0xCEA9, 0x567D, 0xE719, 0xB562, 0x4DE6, 0xEC9A,
+ 0x8F45, 0x1F9D, 0x8940, 0xFA87, 0xEF15, 0xB2EB, 0x8EC9, 0xFB0B,
+ 0x41EC, 0xB367, 0x5FFD, 0x45EA, 0x23BF, 0x53F7, 0xE496, 0x9B5B,
+ 0x75C2, 0xE11C, 0x3DAE, 0x4C6A, 0x6C5A, 0x7E41, 0xF502, 0x834F,
+ 0x685C, 0x51F4, 0xD134, 0xF908, 0xE293, 0xAB73, 0x6253, 0x2A3F,
+ 0x080C, 0x9552, 0x4665, 0x9D5E, 0x3028, 0x37A1, 0x0A0F, 0x2FB5,
+ 0x0E09, 0x2436, 0x1B9B, 0xDF3D, 0xCD26, 0x4E69, 0x7FCD, 0xEA9F,
+ 0x121B, 0x1D9E, 0x5874, 0x342E, 0x362D, 0xDCB2, 0xB4EE, 0x5BFB,
+ 0xA4F6, 0x764D, 0xB761, 0x7DCE, 0x527B, 0xDD3E, 0x5E71, 0x1397,
+ 0xA6F5, 0xB968, 0x0000, 0xC12C, 0x4060, 0xE31F, 0x79C8, 0xB6ED,
+ 0xD4BE, 0x8D46, 0x67D9, 0x724B, 0x94DE, 0x98D4, 0xB0E8, 0x854A,
+ 0xBB6B, 0xC52A, 0x4FE5, 0xED16, 0x86C5, 0x9AD7, 0x6655, 0x1194,
+ 0x8ACF, 0xE910, 0x0406, 0xFE81, 0xA0F0, 0x7844, 0x25BA, 0x4BE3,
+ 0xA2F3, 0x5DFE, 0x80C0, 0x058A, 0x3FAD, 0x21BC, 0x7048, 0xF104,
+ 0x63DF, 0x77C1, 0xAF75, 0x4263, 0x2030, 0xE51A, 0xFD0E, 0xBF6D,
+ 0x814C, 0x1814, 0x2635, 0xC32F, 0xBEE1, 0x35A2, 0x88CC, 0x2E39,
+ 0x9357, 0x55F2, 0xFC82, 0x7A47, 0xC8AC, 0xBAE7, 0x322B, 0xE695,
+ 0xC0A0, 0x1998, 0x9ED1, 0xA37F, 0x4466, 0x547E, 0x3BAB, 0x0B83,
+ 0x8CCA, 0xC729, 0x6BD3, 0x283C, 0xA779, 0xBCE2, 0x161D, 0xAD76,
+ 0xDB3B, 0x6456, 0x744E, 0x141E, 0x92DB, 0x0C0A, 0x486C, 0xB8E4,
+ 0x9F5D, 0xBD6E, 0x43EF, 0xC4A6, 0x39A8, 0x31A4, 0xD337, 0xF28B,
+ 0xD532, 0x8B43, 0x6E59, 0xDAB7, 0x018C, 0xB164, 0x9CD2, 0x49E0,
+ 0xD8B4, 0xACFA, 0xF307, 0xCF25, 0xCAAF, 0xF48E, 0x47E9, 0x1018,
+ 0x6FD5, 0xF088, 0x4A6F, 0x5C72, 0x3824, 0x57F1, 0x73C7, 0x9751,
+ 0xCB23, 0xA17C, 0xE89C, 0x3E21, 0x96DD, 0x61DC, 0x0D86, 0x0F85,
+ 0xE090, 0x7C42, 0x71C4, 0xCCAA, 0x90D8, 0x0605, 0xF701, 0x1C12,
+ 0xC2A3, 0x6A5F, 0xAEF9, 0x69D0, 0x1791, 0x9958, 0x3A27, 0x27B9,
+ 0xD938, 0xEB13, 0x2BB3, 0x2233, 0xD2BB, 0xA970, 0x0789, 0x33A7,
+ 0x2DB6, 0x3C22, 0x1592, 0xC920, 0x8749, 0xAAFF, 0x5078, 0xA57A,
+ 0x038F, 0x59F8, 0x0980, 0x1A17, 0x65DA, 0xD731, 0x84C6, 0xD0B8,
+ 0x82C3, 0x29B0, 0x5A77, 0x1E11, 0x7BCB, 0xA8FC, 0x6DD6, 0x2C3A,
+ },
+ { /* second half is unsigned char-reversed version of first! */
+ 0xA5C6, 0x84F8, 0x99EE, 0x8DF6, 0x0DFF, 0xBDD6, 0xB1DE, 0x5491,
+ 0x5060, 0x0302, 0xA9CE, 0x7D56, 0x19E7, 0x62B5, 0xE64D, 0x9AEC,
+ 0x458F, 0x9D1F, 0x4089, 0x87FA, 0x15EF, 0xEBB2, 0xC98E, 0x0BFB,
+ 0xEC41, 0x67B3, 0xFD5F, 0xEA45, 0xBF23, 0xF753, 0x96E4, 0x5B9B,
+ 0xC275, 0x1CE1, 0xAE3D, 0x6A4C, 0x5A6C, 0x417E, 0x02F5, 0x4F83,
+ 0x5C68, 0xF451, 0x34D1, 0x08F9, 0x93E2, 0x73AB, 0x5362, 0x3F2A,
+ 0x0C08, 0x5295, 0x6546, 0x5E9D, 0x2830, 0xA137, 0x0F0A, 0xB52F,
+ 0x090E, 0x3624, 0x9B1B, 0x3DDF, 0x26CD, 0x694E, 0xCD7F, 0x9FEA,
+ 0x1B12, 0x9E1D, 0x7458, 0x2E34, 0x2D36, 0xB2DC, 0xEEB4, 0xFB5B,
+ 0xF6A4, 0x4D76, 0x61B7, 0xCE7D, 0x7B52, 0x3EDD, 0x715E, 0x9713,
+ 0xF5A6, 0x68B9, 0x0000, 0x2CC1, 0x6040, 0x1FE3, 0xC879, 0xEDB6,
+ 0xBED4, 0x468D, 0xD967, 0x4B72, 0xDE94, 0xD498, 0xE8B0, 0x4A85,
+ 0x6BBB, 0x2AC5, 0xE54F, 0x16ED, 0xC586, 0xD79A, 0x5566, 0x9411,
+ 0xCF8A, 0x10E9, 0x0604, 0x81FE, 0xF0A0, 0x4478, 0xBA25, 0xE34B,
+ 0xF3A2, 0xFE5D, 0xC080, 0x8A05, 0xAD3F, 0xBC21, 0x4870, 0x04F1,
+ 0xDF63, 0xC177, 0x75AF, 0x6342, 0x3020, 0x1AE5, 0x0EFD, 0x6DBF,
+ 0x4C81, 0x1418, 0x3526, 0x2FC3, 0xE1BE, 0xA235, 0xCC88, 0x392E,
+ 0x5793, 0xF255, 0x82FC, 0x477A, 0xACC8, 0xE7BA, 0x2B32, 0x95E6,
+ 0xA0C0, 0x9819, 0xD19E, 0x7FA3, 0x6644, 0x7E54, 0xAB3B, 0x830B,
+ 0xCA8C, 0x29C7, 0xD36B, 0x3C28, 0x79A7, 0xE2BC, 0x1D16, 0x76AD,
+ 0x3BDB, 0x5664, 0x4E74, 0x1E14, 0xDB92, 0x0A0C, 0x6C48, 0xE4B8,
+ 0x5D9F, 0x6EBD, 0xEF43, 0xA6C4, 0xA839, 0xA431, 0x37D3, 0x8BF2,
+ 0x32D5, 0x438B, 0x596E, 0xB7DA, 0x8C01, 0x64B1, 0xD29C, 0xE049,
+ 0xB4D8, 0xFAAC, 0x07F3, 0x25CF, 0xAFCA, 0x8EF4, 0xE947, 0x1810,
+ 0xD56F, 0x88F0, 0x6F4A, 0x725C, 0x2438, 0xF157, 0xC773, 0x5197,
+ 0x23CB, 0x7CA1, 0x9CE8, 0x213E, 0xDD96, 0xDC61, 0x860D, 0x850F,
+ 0x90E0, 0x427C, 0xC471, 0xAACC, 0xD890, 0x0506, 0x01F7, 0x121C,
+ 0xA3C2, 0x5F6A, 0xF9AE, 0xD069, 0x9117, 0x5899, 0x273A, 0xB927,
+ 0x38D9, 0x13EB, 0xB32B, 0x3322, 0xBBD2, 0x70A9, 0x8907, 0xA733,
+ 0xB62D, 0x223C, 0x9215, 0x20C9, 0x4987, 0xFFAA, 0x7850, 0x7AA5,
+ 0x8F03, 0xF859, 0x8009, 0x171A, 0xDA65, 0x31D7, 0xC684, 0xB8D0,
+ 0xC382, 0xB029, 0x775A, 0x111E, 0xCB7B, 0xFCA8, 0xD66D, 0x3A2C,
+ }
+};
+
+/*
+**********************************************************************
+* Routine: Phase 1 -- generate P1K, given TA, TK, IV32
+*
+* Inputs:
+* tk[] = temporal key [128 bits]
+* ta[] = transmitter's MAC address [ 48 bits]
+* iv32 = upper 32 bits of IV [ 32 bits]
+* Output:
+* p1k[] = Phase 1 key [ 80 bits]
+*
+* Note:
+* This function only needs to be called every 2**16 packets,
+* although in theory it could be called every packet.
+*
+**********************************************************************
+*/
+static void phase1(u16 *p1k, const u8 *tk, const u8 *ta, u32 iv32)
+{
+ sint i;
+
+ /* Initialize the 80 bits of P1K[] from IV32 and TA[0..5] */
+ p1k[0] = Lo16(iv32);
+ p1k[1] = Hi16(iv32);
+ p1k[2] = Mk16(ta[1], ta[0]); /* use TA[] as little-endian */
+ p1k[3] = Mk16(ta[3], ta[2]);
+ p1k[4] = Mk16(ta[5], ta[4]);
+ /* Now compute an unbalanced Feistel cipher with 80-bit block */
+ /* size on the 80-bit block P1K[], using the 128-bit key TK[] */
+ for (i = 0; i < PHASE1_LOOP_CNT; i++) { /* Each add is mod 2**16 */
+ p1k[0] += _S_(p1k[4] ^ TK16((i&1) + 0));
+ p1k[1] += _S_(p1k[0] ^ TK16((i&1) + 2));
+ p1k[2] += _S_(p1k[1] ^ TK16((i&1) + 4));
+ p1k[3] += _S_(p1k[2] ^ TK16((i&1) + 6));
+ p1k[4] += _S_(p1k[3] ^ TK16((i&1) + 0));
+ p1k[4] += (unsigned short)i; /* avoid "slide attacks" */
+ }
+}
+
+/*
+**********************************************************************
+* Routine: Phase 2 -- generate RC4KEY, given TK, P1K, IV16
+*
+* Inputs:
+* tk[] = Temporal key [128 bits]
+* p1k[] = Phase 1 output key [ 80 bits]
+* iv16 = low 16 bits of IV counter [ 16 bits]
+* Output:
+* rc4key[] = the key used to encrypt the packet [128 bits]
+*
+* Note:
+* The value {TA,IV32,IV16} for Phase1/Phase2 must be unique
+* across all packets using the same key TK value. Then, for a
+* given value of TK[], this TKIP48 construction guarantees that
+* the final RC4KEY value is unique across all packets.
+*
+* Suggested implementation optimization: if PPK[] is "overlaid"
+* appropriately on RC4KEY[], there is no need for the final
+* for loop below that copies the PPK[] result into RC4KEY[].
+*
+**********************************************************************
+*/
+static void phase2(u8 *rc4key, const u8 *tk, const u16 *p1k, u16 iv16)
+{
+ sint i;
+ u16 PPK[6]; /* temporary key for mixing */
+
+ /* Note: all adds in the PPK[] equations below are mod 2**16 */
+ for (i = 0; i < 5; i++)
+ PPK[i] = p1k[i]; /* first, copy P1K to PPK */
+ PPK[5] = p1k[4] + iv16; /* next, add in IV16 */
+ /* Bijective non-linear mixing of the 96 bits of PPK[0..5] */
+ PPK[0] += _S_(PPK[5] ^ TK16(0)); /* Mix key in each "round" */
+ PPK[1] += _S_(PPK[0] ^ TK16(1));
+ PPK[2] += _S_(PPK[1] ^ TK16(2));
+ PPK[3] += _S_(PPK[2] ^ TK16(3));
+ PPK[4] += _S_(PPK[3] ^ TK16(4));
+ PPK[5] += _S_(PPK[4] ^ TK16(5)); /* Total # S-box lookups == 6 */
+ /* Final sweep: bijective, "linear". Rotates kill LSB correlations */
+ PPK[0] += RotR1(PPK[5] ^ TK16(6));
+ PPK[1] += RotR1(PPK[0] ^ TK16(7)); /* Use all of TK[] in Phase2 */
+ PPK[2] += RotR1(PPK[1]);
+ PPK[3] += RotR1(PPK[2]);
+ PPK[4] += RotR1(PPK[3]);
+ PPK[5] += RotR1(PPK[4]);
+ /* Note: At this point, for a given key TK[0..15], the 96-bit output */
+ /* value PPK[0..5] is guaranteed to be unique, as a function */
+ /* of the 96-bit "input" value {TA,IV32,IV16}. That is, P1K */
+ /* is now a keyed permutation of {TA,IV32,IV16}. */
+ /* Set RC4KEY[0..3], which includes "cleartext" portion of RC4 key */
+ rc4key[0] = Hi8(iv16); /* RC4KEY[0..2] is the WEP IV */
+ rc4key[1] = (Hi8(iv16) | 0x20) & 0x7F; /* Help avoid weak (FMS) keys */
+ rc4key[2] = Lo8(iv16);
+ rc4key[3] = Lo8((PPK[5] ^ TK16(0)) >> 1);
+ /* Copy 96 bits of PPK[0..5] to RC4KEY[4..15] (little-endian) */
+ for (i = 0; i < 6; i++) {
+ rc4key[4 + 2 * i] = Lo8(PPK[i]);
+ rc4key[5 + 2 * i] = Hi8(PPK[i]);
+ }
+}
+
+/*The hlen isn't include the IV*/
+u32 r8712_tkip_encrypt(struct _adapter *padapter, u8 *pxmitframe)
+{ /* exclude ICV */
+ u16 pnl;
+ u32 pnh;
+ u8 rc4key[16];
+ u8 ttkey[16];
+ u8 crc[4];
+ struct arc4context mycontext;
+ u32 curfragnum, length;
+
+ u8 *pframe, *payload, *iv, *prwskey;
+ union pn48 txpn;
+ struct sta_info *stainfo;
+ struct pkt_attrib *pattrib = &((struct xmit_frame *)pxmitframe)->attrib;
+ struct xmit_priv *pxmitpriv = &padapter->xmitpriv;
+ u32 res = _SUCCESS;
+
+ if (((struct xmit_frame *)pxmitframe)->buf_addr == NULL)
+ return _FAIL;
+
+ pframe = ((struct xmit_frame *)pxmitframe)->buf_addr+TXDESC_OFFSET;
+ /* 4 start to encrypt each fragment */
+ if (pattrib->encrypt == _TKIP_) {
+ if (pattrib->psta)
+ stainfo = pattrib->psta;
+ else
+ stainfo = r8712_get_stainfo(&padapter->stapriv,
+ &pattrib->ra[0]);
+ if (stainfo != NULL) {
+ prwskey = &stainfo->x_UncstKey.skey[0];
+ for (curfragnum = 0; curfragnum < pattrib->nr_frags;
+ curfragnum++) {
+ iv = pframe + pattrib->hdrlen;
+ payload = pframe+pattrib->iv_len +
+ pattrib->hdrlen;
+ GET_TKIP_PN(iv, txpn);
+ pnl = (u16)(txpn.val);
+ pnh = (u32)(txpn.val >> 16);
+ phase1((u16 *)&ttkey[0], prwskey, &pattrib->
+ ta[0], pnh);
+ phase2(&rc4key[0], prwskey, (u16 *)&ttkey[0],
+ pnl);
+ if ((curfragnum + 1) == pattrib->nr_frags) {
+ /* 4 the last fragment */
+ length = pattrib->last_txcmdsz -
+ pattrib->hdrlen-pattrib->iv_len -
+ pattrib->icv_len;
+ *((u32 *)crc) = cpu_to_le32(
+ getcrc32(payload, length));
+ arcfour_init(&mycontext, rc4key, 16);
+ arcfour_encrypt(&mycontext, payload,
+ payload, length);
+ arcfour_encrypt(&mycontext, payload +
+ length, crc, 4);
+ } else {
+ length = pxmitpriv->frag_len-pattrib->
+ hdrlen-pattrib->
+ iv_len-pattrib->icv_len;
+ *((u32 *)crc) = cpu_to_le32(getcrc32(
+ payload, length));
+ arcfour_init(&mycontext, rc4key, 16);
+ arcfour_encrypt(&mycontext, payload,
+ payload, length);
+ arcfour_encrypt(&mycontext,
+ payload+length, crc, 4);
+ pframe += pxmitpriv->frag_len;
+ pframe = (u8 *)RND4((addr_t)(pframe));
+ }
+ }
+ } else
+ res = _FAIL;
+ }
+ return res;
+}
+
+/* The hlen doesn't include the IV */
+u32 r8712_tkip_decrypt(struct _adapter *padapter, u8 *precvframe)
+{ /* exclude ICV */
+ u16 pnl;
+ u32 pnh;
+ u8 rc4key[16];
+ u8 ttkey[16];
+ u8 crc[4];
+ struct arc4context mycontext;
+ u32 length;
+ u8 *pframe, *payload, *iv, *prwskey, idx = 0;
+ union pn48 txpn;
+ struct sta_info *stainfo;
+ struct rx_pkt_attrib *prxattrib = &((union recv_frame *)
+ precvframe)->u.hdr.attrib;
+ struct security_priv *psecuritypriv = &padapter->securitypriv;
+
+ pframe = (unsigned char *)((union recv_frame *)
+ precvframe)->u.hdr.rx_data;
+ /* 4 start to decrypt recvframe */
+ if (prxattrib->encrypt == _TKIP_) {
+ stainfo = r8712_get_stainfo(&padapter->stapriv,
+ &prxattrib->ta[0]);
+ if (stainfo != NULL) {
+ iv = pframe+prxattrib->hdrlen;
+ payload = pframe+prxattrib->iv_len + prxattrib->hdrlen;
+ length = ((union recv_frame *)precvframe)->
+ u.hdr.len - prxattrib->hdrlen -
+ prxattrib->iv_len;
+ if (IS_MCAST(prxattrib->ra)) {
+ idx = iv[3];
+ prwskey = &psecuritypriv->XGrpKey[
+ ((idx >> 6) & 0x3) - 1].skey[0];
+ if (psecuritypriv->binstallGrpkey == false)
+ return _FAIL;
+ } else
+ prwskey = &stainfo->x_UncstKey.skey[0];
+ GET_TKIP_PN(iv, txpn);
+ pnl = (u16)(txpn.val);
+ pnh = (u32)(txpn.val >> 16);
+ phase1((u16 *)&ttkey[0], prwskey, &prxattrib->ta[0],
+ pnh);
+ phase2(&rc4key[0], prwskey, (unsigned short *)
+ &ttkey[0], pnl);
+ /* 4 decrypt payload include icv */
+ arcfour_init(&mycontext, rc4key, 16);
+ arcfour_encrypt(&mycontext, payload, payload, length);
+ *((u32 *)crc) = cpu_to_le32(getcrc32(payload,
+ length - 4));
+ if (crc[3] != payload[length - 1] ||
+ crc[2] != payload[length - 2] ||
+ crc[1] != payload[length - 3] ||
+ crc[0] != payload[length - 4])
+ return _FAIL;
+ } else
+ return _FAIL;
+ }
+ return _SUCCESS;
+}
+
+/* 3 =====AES related===== */
+
+#define MAX_MSG_SIZE 2048
+/*****************************/
+/******** SBOX Table *********/
+/*****************************/
+
+static const u8 sbox_table[256] = {
+ 0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5,
+ 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76,
+ 0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0,
+ 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0,
+ 0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc,
+ 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15,
+ 0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a,
+ 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75,
+ 0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0,
+ 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84,
+ 0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b,
+ 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf,
+ 0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85,
+ 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8,
+ 0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5,
+ 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2,
+ 0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17,
+ 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73,
+ 0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88,
+ 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb,
+ 0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c,
+ 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79,
+ 0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9,
+ 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08,
+ 0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6,
+ 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a,
+ 0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e,
+ 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e,
+ 0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94,
+ 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf,
+ 0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68,
+ 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16
+};
+
+/****************************************/
+/* aes128k128d() */
+/* Performs a 128 bit AES encrypt with */
+/* 128 bit data. */
+/****************************************/
+static void xor_128(u8 *a, u8 *b, u8 *out)
+{
+ sint i;
+
+ for (i = 0; i < 16; i++)
+ out[i] = a[i] ^ b[i];
+}
+
+static void xor_32(u8 *a, u8 *b, u8 *out)
+{
+ sint i;
+
+ for (i = 0; i < 4; i++)
+ out[i] = a[i] ^ b[i];
+}
+
+static u8 sbox(u8 a)
+{
+ return sbox_table[(sint)a];
+}
+
+static void next_key(u8 *key, sint round)
+{
+ u8 rcon;
+ u8 sbox_key[4];
+ u8 rcon_table[12] = {
+ 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80,
+ 0x1b, 0x36, 0x36, 0x36
+ };
+
+ sbox_key[0] = sbox(key[13]);
+ sbox_key[1] = sbox(key[14]);
+ sbox_key[2] = sbox(key[15]);
+ sbox_key[3] = sbox(key[12]);
+ rcon = rcon_table[round];
+ xor_32(&key[0], sbox_key, &key[0]);
+ key[0] = key[0] ^ rcon;
+ xor_32(&key[4], &key[0], &key[4]);
+ xor_32(&key[8], &key[4], &key[8]);
+ xor_32(&key[12], &key[8], &key[12]);
+}
+
+static void byte_sub(u8 *in, u8 *out)
+{
+ sint i;
+
+ for (i = 0; i < 16; i++)
+ out[i] = sbox(in[i]);
+}
+
+static void shift_row(u8 *in, u8 *out)
+{
+ out[0] = in[0];
+ out[1] = in[5];
+ out[2] = in[10];
+ out[3] = in[15];
+ out[4] = in[4];
+ out[5] = in[9];
+ out[6] = in[14];
+ out[7] = in[3];
+ out[8] = in[8];
+ out[9] = in[13];
+ out[10] = in[2];
+ out[11] = in[7];
+ out[12] = in[12];
+ out[13] = in[1];
+ out[14] = in[6];
+ out[15] = in[11];
+}
+
+static void mix_column(u8 *in, u8 *out)
+{
+ sint i;
+ u8 add1b[4];
+ u8 add1bf7[4];
+ u8 rotl[4];
+ u8 swap_halfs[4];
+ u8 andf7[4];
+ u8 rotr[4];
+ u8 temp[4];
+ u8 tempb[4];
+
+ for (i = 0; i < 4; i++) {
+ if ((in[i] & 0x80) == 0x80)
+ add1b[i] = 0x1b;
+ else
+ add1b[i] = 0x00;
+ }
+ swap_halfs[0] = in[2]; /* Swap halves */
+ swap_halfs[1] = in[3];
+ swap_halfs[2] = in[0];
+ swap_halfs[3] = in[1];
+ rotl[0] = in[3]; /* Rotate left 8 bits */
+ rotl[1] = in[0];
+ rotl[2] = in[1];
+ rotl[3] = in[2];
+ andf7[0] = in[0] & 0x7f;
+ andf7[1] = in[1] & 0x7f;
+ andf7[2] = in[2] & 0x7f;
+ andf7[3] = in[3] & 0x7f;
+ for (i = 3; i > 0; i--) { /* logical shift left 1 bit */
+ andf7[i] = andf7[i] << 1;
+ if ((andf7[i-1] & 0x80) == 0x80)
+ andf7[i] = (andf7[i] | 0x01);
+ }
+ andf7[0] = andf7[0] << 1;
+ andf7[0] = andf7[0] & 0xfe;
+ xor_32(add1b, andf7, add1bf7);
+ xor_32(in, add1bf7, rotr);
+ temp[0] = rotr[0]; /* Rotate right 8 bits */
+ rotr[0] = rotr[1];
+ rotr[1] = rotr[2];
+ rotr[2] = rotr[3];
+ rotr[3] = temp[0];
+ xor_32(add1bf7, rotr, temp);
+ xor_32(swap_halfs, rotl, tempb);
+ xor_32(temp, tempb, out);
+}
+
+static void aes128k128d(u8 *key, u8 *data, u8 *ciphertext)
+{
+ sint round;
+ sint i;
+ u8 intermediatea[16];
+ u8 intermediateb[16];
+ u8 round_key[16];
+
+ for (i = 0; i < 16; i++)
+ round_key[i] = key[i];
+ for (round = 0; round < 11; round++) {
+ if (round == 0) {
+ xor_128(round_key, data, ciphertext);
+ next_key(round_key, round);
+ } else if (round == 10) {
+ byte_sub(ciphertext, intermediatea);
+ shift_row(intermediatea, intermediateb);
+ xor_128(intermediateb, round_key, ciphertext);
+ } else { /* 1 - 9 */
+ byte_sub(ciphertext, intermediatea);
+ shift_row(intermediatea, intermediateb);
+ mix_column(&intermediateb[0], &intermediatea[0]);
+ mix_column(&intermediateb[4], &intermediatea[4]);
+ mix_column(&intermediateb[8], &intermediatea[8]);
+ mix_column(&intermediateb[12], &intermediatea[12]);
+ xor_128(intermediatea, round_key, ciphertext);
+ next_key(round_key, round);
+ }
+ }
+}
+
+/************************************************/
+/* construct_mic_iv() */
+/* Builds the MIC IV from header fields and PN */
+/************************************************/
+static void construct_mic_iv(u8 *mic_iv, sint qc_exists, sint a4_exists,
+ u8 *mpdu, uint payload_length, u8 *pn_vector)
+{
+ sint i;
+
+ mic_iv[0] = 0x59;
+ if (qc_exists && a4_exists)
+ mic_iv[1] = mpdu[30] & 0x0f; /* QoS_TC */
+ if (qc_exists && !a4_exists)
+ mic_iv[1] = mpdu[24] & 0x0f; /* mute bits 7-4 */
+ if (!qc_exists)
+ mic_iv[1] = 0x00;
+ for (i = 2; i < 8; i++)
+ mic_iv[i] = mpdu[i + 8];
+ for (i = 8; i < 14; i++)
+ mic_iv[i] = pn_vector[13 - i]; /* mic_iv[8:13] = PN[5:0] */
+ mic_iv[14] = (unsigned char) (payload_length / 256);
+ mic_iv[15] = (unsigned char) (payload_length % 256);
+}
+
+/************************************************/
+/* construct_mic_header1() */
+/* Builds the first MIC header block from */
+/* header fields. */
+/************************************************/
+static void construct_mic_header1(u8 *mic_header1, sint header_length, u8 *mpdu)
+{
+ mic_header1[0] = (u8)((header_length - 2) / 256);
+ mic_header1[1] = (u8)((header_length - 2) % 256);
+ mic_header1[2] = mpdu[0] & 0xcf; /* Mute CF poll & CF ack bits */
+ /* Mute retry, more data and pwr mgt bits */
+ mic_header1[3] = mpdu[1] & 0xc7;
+ mic_header1[4] = mpdu[4]; /* A1 */
+ mic_header1[5] = mpdu[5];
+ mic_header1[6] = mpdu[6];
+ mic_header1[7] = mpdu[7];
+ mic_header1[8] = mpdu[8];
+ mic_header1[9] = mpdu[9];
+ mic_header1[10] = mpdu[10]; /* A2 */
+ mic_header1[11] = mpdu[11];
+ mic_header1[12] = mpdu[12];
+ mic_header1[13] = mpdu[13];
+ mic_header1[14] = mpdu[14];
+ mic_header1[15] = mpdu[15];
+}
+
+/************************************************/
+/* construct_mic_header2() */
+/* Builds the last MIC header block from */
+/* header fields. */
+/************************************************/
+static void construct_mic_header2(u8 *mic_header2, u8 *mpdu, sint a4_exists,
+ sint qc_exists)
+{
+ sint i;
+
+ for (i = 0; i < 16; i++)
+ mic_header2[i] = 0x00;
+ mic_header2[0] = mpdu[16]; /* A3 */
+ mic_header2[1] = mpdu[17];
+ mic_header2[2] = mpdu[18];
+ mic_header2[3] = mpdu[19];
+ mic_header2[4] = mpdu[20];
+ mic_header2[5] = mpdu[21];
+ mic_header2[6] = 0x00;
+ mic_header2[7] = 0x00; /* mpdu[23]; */
+ if (!qc_exists && a4_exists)
+ for (i = 0; i < 6; i++)
+ mic_header2[8 + i] = mpdu[24 + i]; /* A4 */
+ if (qc_exists && !a4_exists) {
+ mic_header2[8] = mpdu[24] & 0x0f; /* mute bits 15 - 4 */
+ mic_header2[9] = mpdu[25] & 0x00;
+ }
+ if (qc_exists && a4_exists) {
+ for (i = 0; i < 6; i++)
+ mic_header2[8 + i] = mpdu[24 + i]; /* A4 */
+ mic_header2[14] = mpdu[30] & 0x0f;
+ mic_header2[15] = mpdu[31] & 0x00;
+ }
+}
+
+/************************************************/
+/* construct_mic_header2() */
+/* Builds the last MIC header block from */
+/* header fields. */
+/************************************************/
+static void construct_ctr_preload(u8 *ctr_preload, sint a4_exists, sint qc_exists,
+ u8 *mpdu, u8 *pn_vector, sint c)
+{
+ sint i;
+
+ for (i = 0; i < 16; i++)
+ ctr_preload[i] = 0x00;
+ i = 0;
+ ctr_preload[0] = 0x01; /* flag */
+ if (qc_exists && a4_exists)
+ ctr_preload[1] = mpdu[30] & 0x0f;
+ if (qc_exists && !a4_exists)
+ ctr_preload[1] = mpdu[24] & 0x0f;
+ for (i = 2; i < 8; i++)
+ ctr_preload[i] = mpdu[i + 8];
+ for (i = 8; i < 14; i++)
+ ctr_preload[i] = pn_vector[13 - i];
+ ctr_preload[14] = (unsigned char) (c / 256); /* Ctr */
+ ctr_preload[15] = (unsigned char) (c % 256);
+}
+
+/************************************/
+/* bitwise_xor() */
+/* A 128 bit, bitwise exclusive or */
+/************************************/
+static void bitwise_xor(u8 *ina, u8 *inb, u8 *out)
+{
+ sint i;
+
+ for (i = 0; i < 16; i++)
+ out[i] = ina[i] ^ inb[i];
+}
+
+static sint aes_cipher(u8 *key, uint hdrlen,
+ u8 *pframe, uint plen)
+{
+ uint qc_exists, a4_exists, i, j, payload_remainder;
+ uint num_blocks, payload_index;
+
+ u8 pn_vector[6];
+ u8 mic_iv[16];
+ u8 mic_header1[16];
+ u8 mic_header2[16];
+ u8 ctr_preload[16];
+
+ /* Intermediate Buffers */
+ u8 chain_buffer[16];
+ u8 aes_out[16];
+ u8 padded_buffer[16];
+ u8 mic[8];
+ uint frtype = GetFrameType(pframe);
+ uint frsubtype = GetFrameSubType(pframe);
+
+ frsubtype >>= 4;
+ memset((void *)mic_iv, 0, 16);
+ memset((void *)mic_header1, 0, 16);
+ memset((void *)mic_header2, 0, 16);
+ memset((void *)ctr_preload, 0, 16);
+ memset((void *)chain_buffer, 0, 16);
+ memset((void *)aes_out, 0, 16);
+ memset((void *)padded_buffer, 0, 16);
+
+ if ((hdrlen == WLAN_HDR_A3_LEN) || (hdrlen == WLAN_HDR_A3_QOS_LEN))
+ a4_exists = 0;
+ else
+ a4_exists = 1;
+
+ if ((frtype == WIFI_DATA_CFACK) ||
+ (frtype == WIFI_DATA_CFPOLL) ||
+ (frtype == WIFI_DATA_CFACKPOLL)) {
+ qc_exists = 1;
+ if (hdrlen != WLAN_HDR_A3_QOS_LEN)
+ hdrlen += 2;
+ } else if ((frsubtype == 0x08) ||
+ (frsubtype == 0x09) ||
+ (frsubtype == 0x0a) ||
+ (frsubtype == 0x0b)) {
+ if (hdrlen != WLAN_HDR_A3_QOS_LEN)
+ hdrlen += 2;
+ qc_exists = 1;
+ } else
+ qc_exists = 0;
+ pn_vector[0] = pframe[hdrlen];
+ pn_vector[1] = pframe[hdrlen+1];
+ pn_vector[2] = pframe[hdrlen+4];
+ pn_vector[3] = pframe[hdrlen+5];
+ pn_vector[4] = pframe[hdrlen+6];
+ pn_vector[5] = pframe[hdrlen+7];
+ construct_mic_iv(mic_iv, qc_exists, a4_exists, pframe, plen, pn_vector);
+ construct_mic_header1(mic_header1, hdrlen, pframe);
+ construct_mic_header2(mic_header2, pframe, a4_exists, qc_exists);
+ payload_remainder = plen % 16;
+ num_blocks = plen / 16;
+ /* Find start of payload */
+ payload_index = hdrlen + 8;
+ /* Calculate MIC */
+ aes128k128d(key, mic_iv, aes_out);
+ bitwise_xor(aes_out, mic_header1, chain_buffer);
+ aes128k128d(key, chain_buffer, aes_out);
+ bitwise_xor(aes_out, mic_header2, chain_buffer);
+ aes128k128d(key, chain_buffer, aes_out);
+ for (i = 0; i < num_blocks; i++) {
+ bitwise_xor(aes_out, &pframe[payload_index], chain_buffer);
+ payload_index += 16;
+ aes128k128d(key, chain_buffer, aes_out);
+ }
+ /* Add on the final payload block if it needs padding */
+ if (payload_remainder > 0) {
+ for (j = 0; j < 16; j++)
+ padded_buffer[j] = 0x00;
+ for (j = 0; j < payload_remainder; j++)
+ padded_buffer[j] = pframe[payload_index++];
+ bitwise_xor(aes_out, padded_buffer, chain_buffer);
+ aes128k128d(key, chain_buffer, aes_out);
+ }
+ for (j = 0; j < 8; j++)
+ mic[j] = aes_out[j];
+ /* Insert MIC into payload */
+ for (j = 0; j < 8; j++)
+ pframe[payload_index+j] = mic[j];
+ payload_index = hdrlen + 8;
+ for (i = 0; i < num_blocks; i++) {
+ construct_ctr_preload(ctr_preload, a4_exists, qc_exists,
+ pframe, pn_vector, i + 1);
+ aes128k128d(key, ctr_preload, aes_out);
+ bitwise_xor(aes_out, &pframe[payload_index], chain_buffer);
+ for (j = 0; j < 16; j++)
+ pframe[payload_index++] = chain_buffer[j];
+ }
+ if (payload_remainder > 0) { /* If short final block, then pad it,*/
+ /* encrypt and copy unpadded part back */
+ construct_ctr_preload(ctr_preload, a4_exists, qc_exists,
+ pframe, pn_vector, num_blocks+1);
+ for (j = 0; j < 16; j++)
+ padded_buffer[j] = 0x00;
+ for (j = 0; j < payload_remainder; j++)
+ padded_buffer[j] = pframe[payload_index+j];
+ aes128k128d(key, ctr_preload, aes_out);
+ bitwise_xor(aes_out, padded_buffer, chain_buffer);
+ for (j = 0; j < payload_remainder; j++)
+ pframe[payload_index++] = chain_buffer[j];
+ }
+ /* Encrypt the MIC */
+ construct_ctr_preload(ctr_preload, a4_exists, qc_exists,
+ pframe, pn_vector, 0);
+ for (j = 0; j < 16; j++)
+ padded_buffer[j] = 0x00;
+ for (j = 0; j < 8; j++)
+ padded_buffer[j] = pframe[j+hdrlen+8+plen];
+ aes128k128d(key, ctr_preload, aes_out);
+ bitwise_xor(aes_out, padded_buffer, chain_buffer);
+ for (j = 0; j < 8; j++)
+ pframe[payload_index++] = chain_buffer[j];
+ return _SUCCESS;
+}
+
+u32 r8712_aes_encrypt(struct _adapter *padapter, u8 *pxmitframe)
+{ /* exclude ICV */
+ /* Intermediate Buffers */
+ sint curfragnum, length;
+ u8 *pframe, *prwskey;
+ struct sta_info *stainfo;
+ struct pkt_attrib *pattrib = &((struct xmit_frame *)
+ pxmitframe)->attrib;
+ struct xmit_priv *pxmitpriv = &padapter->xmitpriv;
+ u32 res = _SUCCESS;
+
+ if (((struct xmit_frame *)pxmitframe)->buf_addr == NULL)
+ return _FAIL;
+ pframe = ((struct xmit_frame *)pxmitframe)->buf_addr + TXDESC_OFFSET;
+ /* 4 start to encrypt each fragment */
+ if (pattrib->encrypt == _AES_) {
+ if (pattrib->psta)
+ stainfo = pattrib->psta;
+ else
+ stainfo = r8712_get_stainfo(&padapter->stapriv,
+ &pattrib->ra[0]);
+ if (stainfo != NULL) {
+ prwskey = &stainfo->x_UncstKey.skey[0];
+ for (curfragnum = 0; curfragnum < pattrib->nr_frags;
+ curfragnum++) {
+ if ((curfragnum + 1) == pattrib->nr_frags) {
+ length = pattrib->last_txcmdsz -
+ pattrib->hdrlen -
+ pattrib->iv_len -
+ pattrib->icv_len;
+ aes_cipher(prwskey, pattrib->
+ hdrlen, pframe, length);
+ } else {
+ length = pxmitpriv->frag_len -
+ pattrib->hdrlen -
+ pattrib->iv_len -
+ pattrib->icv_len;
+ aes_cipher(prwskey, pattrib->
+ hdrlen, pframe, length);
+ pframe += pxmitpriv->frag_len;
+ pframe = (u8 *)RND4((addr_t)(pframe));
+ }
+ }
+ } else
+ res = _FAIL;
+ }
+ return res;
+}
+
+static sint aes_decipher(u8 *key, uint hdrlen,
+ u8 *pframe, uint plen)
+{
+ static u8 message[MAX_MSG_SIZE];
+ uint qc_exists, a4_exists, i, j, payload_remainder;
+ uint num_blocks, payload_index;
+ u8 pn_vector[6];
+ u8 mic_iv[16];
+ u8 mic_header1[16];
+ u8 mic_header2[16];
+ u8 ctr_preload[16];
+ /* Intermediate Buffers */
+ u8 chain_buffer[16];
+ u8 aes_out[16];
+ u8 padded_buffer[16];
+ u8 mic[8];
+ uint frtype = GetFrameType(pframe);
+ uint frsubtype = GetFrameSubType(pframe);
+
+ frsubtype >>= 4;
+ memset((void *)mic_iv, 0, 16);
+ memset((void *)mic_header1, 0, 16);
+ memset((void *)mic_header2, 0, 16);
+ memset((void *)ctr_preload, 0, 16);
+ memset((void *)chain_buffer, 0, 16);
+ memset((void *)aes_out, 0, 16);
+ memset((void *)padded_buffer, 0, 16);
+ /* start to decrypt the payload */
+ /*(plen including llc, payload and mic) */
+ num_blocks = (plen - 8) / 16;
+ payload_remainder = (plen-8) % 16;
+ pn_vector[0] = pframe[hdrlen];
+ pn_vector[1] = pframe[hdrlen+1];
+ pn_vector[2] = pframe[hdrlen+4];
+ pn_vector[3] = pframe[hdrlen+5];
+ pn_vector[4] = pframe[hdrlen+6];
+ pn_vector[5] = pframe[hdrlen+7];
+ if ((hdrlen == WLAN_HDR_A3_LEN) || (hdrlen == WLAN_HDR_A3_QOS_LEN))
+ a4_exists = 0;
+ else
+ a4_exists = 1;
+ if ((frtype == WIFI_DATA_CFACK) ||
+ (frtype == WIFI_DATA_CFPOLL) ||
+ (frtype == WIFI_DATA_CFACKPOLL)) {
+ qc_exists = 1;
+ if (hdrlen != WLAN_HDR_A3_QOS_LEN)
+ hdrlen += 2;
+ } else if ((frsubtype == 0x08) ||
+ (frsubtype == 0x09) ||
+ (frsubtype == 0x0a) ||
+ (frsubtype == 0x0b)) {
+ if (hdrlen != WLAN_HDR_A3_QOS_LEN)
+ hdrlen += 2;
+ qc_exists = 1;
+ } else {
+ qc_exists = 0;
+ }
+ /* now, decrypt pframe with hdrlen offset and plen long */
+ payload_index = hdrlen + 8; /* 8 is for extiv */
+ for (i = 0; i < num_blocks; i++) {
+ construct_ctr_preload(ctr_preload, a4_exists, qc_exists,
+ pframe, pn_vector, i + 1);
+ aes128k128d(key, ctr_preload, aes_out);
+ bitwise_xor(aes_out, &pframe[payload_index], chain_buffer);
+ for (j = 0; j < 16; j++)
+ pframe[payload_index++] = chain_buffer[j];
+ }
+ if (payload_remainder > 0) { /* If short final block, pad it,*/
+ /* encrypt it and copy the unpadded part back */
+ construct_ctr_preload(ctr_preload, a4_exists, qc_exists,
+ pframe, pn_vector, num_blocks+1);
+ for (j = 0; j < 16; j++)
+ padded_buffer[j] = 0x00;
+ for (j = 0; j < payload_remainder; j++)
+ padded_buffer[j] = pframe[payload_index + j];
+ aes128k128d(key, ctr_preload, aes_out);
+ bitwise_xor(aes_out, padded_buffer, chain_buffer);
+ for (j = 0; j < payload_remainder; j++)
+ pframe[payload_index++] = chain_buffer[j];
+ }
+ /* start to calculate the mic */
+ memcpy((void *)message, pframe, (hdrlen + plen + 8));
+ pn_vector[0] = pframe[hdrlen];
+ pn_vector[1] = pframe[hdrlen+1];
+ pn_vector[2] = pframe[hdrlen+4];
+ pn_vector[3] = pframe[hdrlen+5];
+ pn_vector[4] = pframe[hdrlen+6];
+ pn_vector[5] = pframe[hdrlen+7];
+ construct_mic_iv(mic_iv, qc_exists, a4_exists, message, plen-8,
+ pn_vector);
+ construct_mic_header1(mic_header1, hdrlen, message);
+ construct_mic_header2(mic_header2, message, a4_exists, qc_exists);
+ payload_remainder = (plen - 8) % 16;
+ num_blocks = (plen - 8) / 16;
+ /* Find start of payload */
+ payload_index = hdrlen + 8;
+ /* Calculate MIC */
+ aes128k128d(key, mic_iv, aes_out);
+ bitwise_xor(aes_out, mic_header1, chain_buffer);
+ aes128k128d(key, chain_buffer, aes_out);
+ bitwise_xor(aes_out, mic_header2, chain_buffer);
+ aes128k128d(key, chain_buffer, aes_out);
+ for (i = 0; i < num_blocks; i++) {
+ bitwise_xor(aes_out, &message[payload_index], chain_buffer);
+ payload_index += 16;
+ aes128k128d(key, chain_buffer, aes_out);
+ }
+ /* Add on the final payload block if it needs padding */
+ if (payload_remainder > 0) {
+ for (j = 0; j < 16; j++)
+ padded_buffer[j] = 0x00;
+ for (j = 0; j < payload_remainder; j++)
+ padded_buffer[j] = message[payload_index++];
+ bitwise_xor(aes_out, padded_buffer, chain_buffer);
+ aes128k128d(key, chain_buffer, aes_out);
+ }
+ for (j = 0; j < 8; j++)
+ mic[j] = aes_out[j];
+ /* Insert MIC into payload */
+ for (j = 0; j < 8; j++)
+ message[payload_index+j] = mic[j];
+ payload_index = hdrlen + 8;
+ for (i = 0; i < num_blocks; i++) {
+ construct_ctr_preload(ctr_preload, a4_exists, qc_exists,
+ message, pn_vector, i + 1);
+ aes128k128d(key, ctr_preload, aes_out);
+ bitwise_xor(aes_out, &message[payload_index], chain_buffer);
+ for (j = 0; j < 16; j++)
+ message[payload_index++] = chain_buffer[j];
+ }
+ if (payload_remainder > 0) { /* If short final block, pad it,*/
+ /* encrypt and copy unpadded part back */
+ construct_ctr_preload(ctr_preload, a4_exists, qc_exists,
+ message, pn_vector, num_blocks+1);
+ for (j = 0; j < 16; j++)
+ padded_buffer[j] = 0x00;
+ for (j = 0; j < payload_remainder; j++)
+ padded_buffer[j] = message[payload_index + j];
+ aes128k128d(key, ctr_preload, aes_out);
+ bitwise_xor(aes_out, padded_buffer, chain_buffer);
+ for (j = 0; j < payload_remainder; j++)
+ message[payload_index++] = chain_buffer[j];
+ }
+ /* Encrypt the MIC */
+ construct_ctr_preload(ctr_preload, a4_exists, qc_exists, message,
+ pn_vector, 0);
+ for (j = 0; j < 16; j++)
+ padded_buffer[j] = 0x00;
+ for (j = 0; j < 8; j++)
+ padded_buffer[j] = message[j + hdrlen + plen];
+ aes128k128d(key, ctr_preload, aes_out);
+ bitwise_xor(aes_out, padded_buffer, chain_buffer);
+ for (j = 0; j < 8; j++)
+ message[payload_index++] = chain_buffer[j];
+ /* compare the mic */
+ return _SUCCESS;
+}
+
+u32 r8712_aes_decrypt(struct _adapter *padapter, u8 *precvframe)
+{ /* exclude ICV */
+ /* Intermediate Buffers */
+ sint length;
+ u8 *pframe, *prwskey, *iv, idx;
+ struct sta_info *stainfo;
+ struct rx_pkt_attrib *prxattrib = &((union recv_frame *)
+ precvframe)->u.hdr.attrib;
+ struct security_priv *psecuritypriv = &padapter->securitypriv;
+
+ pframe = (unsigned char *)((union recv_frame *)precvframe)->
+ u.hdr.rx_data;
+ /* 4 start to encrypt each fragment */
+ if (prxattrib->encrypt == _AES_) {
+ stainfo = r8712_get_stainfo(&padapter->stapriv,
+ &prxattrib->ta[0]);
+ if (stainfo != NULL) {
+ if (IS_MCAST(prxattrib->ra)) {
+ iv = pframe+prxattrib->hdrlen;
+ idx = iv[3];
+ prwskey = &psecuritypriv->XGrpKey[
+ ((idx >> 6) & 0x3) - 1].skey[0];
+ if (psecuritypriv->binstallGrpkey == false)
+ return _FAIL;
+
+ } else
+ prwskey = &stainfo->x_UncstKey.skey[0];
+ length = ((union recv_frame *)precvframe)->
+ u.hdr.len-prxattrib->hdrlen-prxattrib->iv_len;
+ aes_decipher(prwskey, prxattrib->hdrlen, pframe,
+ length);
+ } else
+ return _FAIL;
+ }
+ return _SUCCESS;
+}
+
+void r8712_use_tkipkey_handler(unsigned long data)
+{
+ struct _adapter *padapter = (struct _adapter *)data;
+
+ padapter->securitypriv.busetkipkey = true;
+}
diff --git a/drivers/staging/rtl8712/rtl871x_security.h b/drivers/staging/rtl8712/rtl871x_security.h
new file mode 100644
index 000000000..2295f0e64
--- /dev/null
+++ b/drivers/staging/rtl8712/rtl871x_security.h
@@ -0,0 +1,222 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2010 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ * Modifications for inclusion into the Linux staging tree are
+ * Copyright(c) 2010 Larry Finger. All rights reserved.
+ *
+ * Contact information:
+ * WLAN FAE <wlanfae@realtek.com>
+ * Larry Finger <Larry.Finger@lwfinger.net>
+ *
+ ******************************************************************************/
+#ifndef __RTL871X_SECURITY_H_
+#define __RTL871X_SECURITY_H_
+
+#include "osdep_service.h"
+#include "drv_types.h"
+
+#define _NO_PRIVACY_ 0x0
+#define _WEP40_ 0x1
+#define _TKIP_ 0x2
+#define _TKIP_WTMIC_ 0x3
+#define _AES_ 0x4
+#define _WEP104_ 0x5
+
+#define _WPA_IE_ID_ 0xdd
+#define _WPA2_IE_ID_ 0x30
+
+#ifndef Ndis802_11AuthModeWPA2
+#define Ndis802_11AuthModeWPA2 (Ndis802_11AuthModeWPANone + 1)
+#endif
+
+#ifndef Ndis802_11AuthModeWPA2PSK
+#define Ndis802_11AuthModeWPA2PSK (Ndis802_11AuthModeWPANone + 2)
+#endif
+
+union pn48 {
+ u64 val;
+#if defined(__BIG_ENDIAN)
+ struct {
+ u8 TSC7;
+ u8 TSC6;
+ u8 TSC5;
+ u8 TSC4;
+ u8 TSC3;
+ u8 TSC2;
+ u8 TSC1;
+ u8 TSC0;
+ } _byte_;
+#else
+ struct {
+ u8 TSC0;
+ u8 TSC1;
+ u8 TSC2;
+ u8 TSC3;
+ u8 TSC4;
+ u8 TSC5;
+ u8 TSC6;
+ u8 TSC7;
+ } _byte_;
+#endif
+};
+
+union Keytype {
+ u8 skey[16];
+ u32 lkey[4];
+};
+
+struct RT_PMKID_LIST {
+ u8 bUsed;
+ u8 Bssid[6];
+ u8 PMKID[16];
+ u8 SsidBuf[33];
+ u8 *ssid_octet;
+ u16 ssid_length;
+};
+
+struct security_priv {
+ u32 AuthAlgrthm; /* 802.11 auth, could be open, shared,
+ * 8021x and authswitch */
+ u32 PrivacyAlgrthm; /* This specify the privacy for shared
+ * auth. algorithm. */
+ u32 PrivacyKeyIndex; /* this is only valid for legendary
+ * wep, 0~3 for key id. */
+ union Keytype DefKey[4]; /* this is only valid for def. key */
+ u32 DefKeylen[4];
+ u32 XGrpPrivacy; /* This specify the privacy algthm.
+ * used for Grp key */
+ u32 XGrpKeyid; /* key id used for Grp Key */
+ union Keytype XGrpKey[2]; /* 802.1x Group Key, for
+ * inx0 and inx1 */
+ union Keytype XGrptxmickey[2];
+ union Keytype XGrprxmickey[2];
+ union pn48 Grptxpn; /* PN48 used for Grp Key xmit. */
+ union pn48 Grprxpn; /* PN48 used for Grp Key recv. */
+ u8 wps_hw_pbc_pressed;/*for hw pbc pressed*/
+ u8 wps_phase;/*for wps*/
+ u8 wps_ie[MAX_WPA_IE_LEN<<2];
+ int wps_ie_len;
+ u8 binstallGrpkey;
+ u8 busetkipkey;
+ struct timer_list tkip_timer;
+ u8 bcheck_grpkey;
+ u8 bgrpkey_handshake;
+ s32 sw_encrypt; /* from registry_priv */
+ s32 sw_decrypt; /* from registry_priv */
+ s32 hw_decrypted; /* if the rx packets is hw_decrypted==false,
+ * it means the hw has not been ready. */
+ u32 ndisauthtype; /* keeps the auth_type & enc_status from upper
+ * layer ioctl(wpa_supplicant or wzc) */
+ u32 ndisencryptstatus;
+ struct wlan_bssid_ex sec_bss; /* for joinbss (h2c buffer) usage */
+ struct NDIS_802_11_WEP ndiswep;
+ u8 assoc_info[600];
+ u8 szofcapability[256]; /* for wpa2 usage */
+ u8 oidassociation[512]; /* for wpa/wpa2 usage */
+ u8 authenticator_ie[256]; /* store ap security information element */
+ u8 supplicant_ie[256]; /* store sta security information element */
+ /* for tkip countermeasure */
+ u32 last_mic_err_time;
+ u8 btkip_countermeasure;
+ u8 btkip_wait_report;
+ u32 btkip_countermeasure_time;
+ /*-------------------------------------------------------------------
+ * For WPA2 Pre-Authentication.
+ *------------------------------------------------------------------ */
+ struct RT_PMKID_LIST PMKIDList[NUM_PMKID_CACHE];
+ u8 PMKIDIndex;
+};
+
+#define GET_ENCRY_ALGO(psecuritypriv, psta, encry_algo, bmcst) \
+do { \
+ switch (psecuritypriv->AuthAlgrthm) { \
+ case 0: \
+ case 1: \
+ case 3: \
+ encry_algo = (u8)psecuritypriv->PrivacyAlgrthm; \
+ break; \
+ case 2: \
+ if (bmcst) \
+ encry_algo = (u8)psecuritypriv->XGrpPrivacy; \
+ else \
+ encry_algo = (u8)psta->XPrivacy; \
+ break; \
+ } \
+} while (0)
+#define SET_ICE_IV_LEN(iv_len, icv_len, encrypt)\
+do {\
+ switch (encrypt) { \
+ case _WEP40_: \
+ case _WEP104_: \
+ iv_len = 4; \
+ icv_len = 4; \
+ break; \
+ case _TKIP_: \
+ iv_len = 8; \
+ icv_len = 4; \
+ break; \
+ case _AES_: \
+ iv_len = 8; \
+ icv_len = 8; \
+ break; \
+ default: \
+ iv_len = 0; \
+ icv_len = 0; \
+ break; \
+ } \
+} while (0)
+#define GET_TKIP_PN(iv, txpn) \
+do {\
+ txpn._byte_.TSC0 = iv[2];\
+ txpn._byte_.TSC1 = iv[0];\
+ txpn._byte_.TSC2 = iv[4];\
+ txpn._byte_.TSC3 = iv[5];\
+ txpn._byte_.TSC4 = iv[6];\
+ txpn._byte_.TSC5 = iv[7];\
+} while (0)
+
+#define ROL32(A, n) (((A) << (n)) | (((A)>>(32-(n))) & ((1UL << (n)) - 1)))
+#define ROR32(A, n) ROL32((A), 32 - (n))
+
+struct mic_data {
+ u32 K0, K1; /* Key */
+ u32 L, R; /* Current state */
+ u32 M; /* Message accumulator (single word) */
+ u32 nBytesInM; /* # bytes in M */
+};
+
+void seccalctkipmic(
+ u8 *key,
+ u8 *header,
+ u8 *data,
+ u32 data_len,
+ u8 *Miccode,
+ u8 priority);
+
+void r8712_secmicsetkey(struct mic_data *pmicdata, u8 *key);
+void r8712_secmicappend(struct mic_data *pmicdata, u8 *src, u32 nBytes);
+void r8712_secgetmic(struct mic_data *pmicdata, u8 *dst);
+u32 r8712_aes_encrypt(struct _adapter *padapter, u8 *pxmitframe);
+u32 r8712_tkip_encrypt(struct _adapter *padapter, u8 *pxmitframe);
+void r8712_wep_encrypt(struct _adapter *padapter, u8 *pxmitframe);
+u32 r8712_aes_decrypt(struct _adapter *padapter, u8 *precvframe);
+u32 r8712_tkip_decrypt(struct _adapter *padapter, u8 *precvframe);
+void r8712_wep_decrypt(struct _adapter *padapter, u8 *precvframe);
+void r8712_use_tkipkey_handler(unsigned long data);
+
+#endif /*__RTL871X_SECURITY_H_ */
+
diff --git a/drivers/staging/rtl8712/rtl871x_sta_mgt.c b/drivers/staging/rtl8712/rtl871x_sta_mgt.c
new file mode 100644
index 000000000..a9b93d0f6
--- /dev/null
+++ b/drivers/staging/rtl8712/rtl871x_sta_mgt.c
@@ -0,0 +1,285 @@
+/******************************************************************************
+ * rtl871x_sta_mgt.c
+ *
+ * Copyright(c) 2007 - 2010 Realtek Corporation. All rights reserved.
+ * Linux device driver for RTL8192SU
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ * Modifications for inclusion into the Linux staging tree are
+ * Copyright(c) 2010 Larry Finger. All rights reserved.
+ *
+ * Contact information:
+ * WLAN FAE <wlanfae@realtek.com>
+ * Larry Finger <Larry.Finger@lwfinger.net>
+ *
+ ******************************************************************************/
+
+#define _RTL871X_STA_MGT_C_
+
+#include "osdep_service.h"
+#include "drv_types.h"
+#include "recv_osdep.h"
+#include "xmit_osdep.h"
+#include "sta_info.h"
+
+static void _init_stainfo(struct sta_info *psta)
+{
+ memset((u8 *)psta, 0, sizeof(struct sta_info));
+ spin_lock_init(&psta->lock);
+ INIT_LIST_HEAD(&psta->list);
+ INIT_LIST_HEAD(&psta->hash_list);
+ _r8712_init_sta_xmit_priv(&psta->sta_xmitpriv);
+ _r8712_init_sta_recv_priv(&psta->sta_recvpriv);
+ INIT_LIST_HEAD(&psta->asoc_list);
+ INIT_LIST_HEAD(&psta->auth_list);
+}
+
+u32 _r8712_init_sta_priv(struct sta_priv *pstapriv)
+{
+ struct sta_info *psta;
+ s32 i;
+
+ pstapriv->pallocated_stainfo_buf = kmalloc(sizeof(struct sta_info) *
+ NUM_STA + 4, GFP_ATOMIC);
+ if (pstapriv->pallocated_stainfo_buf == NULL)
+ return _FAIL;
+ pstapriv->pstainfo_buf = pstapriv->pallocated_stainfo_buf + 4 -
+ ((addr_t)(pstapriv->pallocated_stainfo_buf) & 3);
+ _init_queue(&pstapriv->free_sta_queue);
+ spin_lock_init(&pstapriv->sta_hash_lock);
+ pstapriv->asoc_sta_count = 0;
+ _init_queue(&pstapriv->sleep_q);
+ _init_queue(&pstapriv->wakeup_q);
+ psta = (struct sta_info *)(pstapriv->pstainfo_buf);
+ for (i = 0; i < NUM_STA; i++) {
+ _init_stainfo(psta);
+ INIT_LIST_HEAD(&(pstapriv->sta_hash[i]));
+ list_add_tail(&psta->list, &pstapriv->free_sta_queue.queue);
+ psta++;
+ }
+ INIT_LIST_HEAD(&pstapriv->asoc_list);
+ INIT_LIST_HEAD(&pstapriv->auth_list);
+ return _SUCCESS;
+}
+
+/* this function is used to free the memory of lock || sema for all stainfos */
+static void mfree_all_stainfo(struct sta_priv *pstapriv)
+{
+ unsigned long irqL;
+ struct list_head *plist, *phead;
+
+ spin_lock_irqsave(&pstapriv->sta_hash_lock, irqL);
+ phead = &pstapriv->free_sta_queue.queue;
+ plist = phead->next;
+ while ((end_of_queue_search(phead, plist)) == false)
+ plist = plist->next;
+
+ spin_unlock_irqrestore(&pstapriv->sta_hash_lock, irqL);
+}
+
+
+static void mfree_sta_priv_lock(struct sta_priv *pstapriv)
+{
+ mfree_all_stainfo(pstapriv); /* be done before free sta_hash_lock */
+}
+
+u32 _r8712_free_sta_priv(struct sta_priv *pstapriv)
+{
+ if (pstapriv) {
+ mfree_sta_priv_lock(pstapriv);
+ kfree(pstapriv->pallocated_stainfo_buf);
+ }
+ return _SUCCESS;
+}
+
+struct sta_info *r8712_alloc_stainfo(struct sta_priv *pstapriv, u8 *hwaddr)
+{
+ s32 index;
+ struct list_head *phash_list;
+ struct sta_info *psta;
+ struct __queue *pfree_sta_queue;
+ struct recv_reorder_ctrl *preorder_ctrl;
+ int i = 0;
+ u16 wRxSeqInitialValue = 0xffff;
+ unsigned long flags;
+
+ pfree_sta_queue = &pstapriv->free_sta_queue;
+ spin_lock_irqsave(&(pfree_sta_queue->lock), flags);
+ if (list_empty(&pfree_sta_queue->queue))
+ psta = NULL;
+ else {
+ psta = LIST_CONTAINOR(pfree_sta_queue->queue.next,
+ struct sta_info, list);
+ list_del_init(&(psta->list));
+ _init_stainfo(psta);
+ memcpy(psta->hwaddr, hwaddr, ETH_ALEN);
+ index = wifi_mac_hash(hwaddr);
+ if (index >= NUM_STA) {
+ psta = NULL;
+ goto exit;
+ }
+ phash_list = &(pstapriv->sta_hash[index]);
+ list_add_tail(&psta->hash_list, phash_list);
+ pstapriv->asoc_sta_count++;
+
+/* For the SMC router, the sequence number of first packet of WPS handshake
+ * will be 0. In this case, this packet will be dropped by recv_decache function
+ * if we use the 0x00 as the default value for tid_rxseq variable. So, we
+ * initialize the tid_rxseq variable as the 0xffff.
+ */
+ for (i = 0; i < 16; i++)
+ memcpy(&psta->sta_recvpriv.rxcache.tid_rxseq[i],
+ &wRxSeqInitialValue, 2);
+ /* for A-MPDU Rx reordering buffer control */
+ for (i = 0; i < 16; i++) {
+ preorder_ctrl = &psta->recvreorder_ctrl[i];
+ preorder_ctrl->padapter = pstapriv->padapter;
+ preorder_ctrl->indicate_seq = 0xffff;
+ preorder_ctrl->wend_b = 0xffff;
+ preorder_ctrl->wsize_b = 64;
+ _init_queue(&preorder_ctrl->pending_recvframe_queue);
+ r8712_init_recv_timer(preorder_ctrl);
+ }
+ }
+exit:
+ spin_unlock_irqrestore(&(pfree_sta_queue->lock), flags);
+ return psta;
+}
+
+/* using pstapriv->sta_hash_lock to protect */
+void r8712_free_stainfo(struct _adapter *padapter, struct sta_info *psta)
+{
+ int i;
+ unsigned long irqL0;
+ struct __queue *pfree_sta_queue;
+ struct recv_reorder_ctrl *preorder_ctrl;
+ struct sta_xmit_priv *pstaxmitpriv;
+ struct xmit_priv *pxmitpriv = &padapter->xmitpriv;
+ struct sta_priv *pstapriv = &padapter->stapriv;
+
+ if (psta == NULL)
+ return;
+ pfree_sta_queue = &pstapriv->free_sta_queue;
+ pstaxmitpriv = &psta->sta_xmitpriv;
+ spin_lock_irqsave(&(pxmitpriv->vo_pending.lock), irqL0);
+ r8712_free_xmitframe_queue(pxmitpriv, &pstaxmitpriv->vo_q.sta_pending);
+ list_del_init(&(pstaxmitpriv->vo_q.tx_pending));
+ spin_unlock_irqrestore(&(pxmitpriv->vo_pending.lock), irqL0);
+ spin_lock_irqsave(&(pxmitpriv->vi_pending.lock), irqL0);
+ r8712_free_xmitframe_queue(pxmitpriv, &pstaxmitpriv->vi_q.sta_pending);
+ list_del_init(&(pstaxmitpriv->vi_q.tx_pending));
+ spin_unlock_irqrestore(&(pxmitpriv->vi_pending.lock), irqL0);
+ spin_lock_irqsave(&(pxmitpriv->bk_pending.lock), irqL0);
+ r8712_free_xmitframe_queue(pxmitpriv, &pstaxmitpriv->bk_q.sta_pending);
+ list_del_init(&(pstaxmitpriv->bk_q.tx_pending));
+ spin_unlock_irqrestore(&(pxmitpriv->bk_pending.lock), irqL0);
+ spin_lock_irqsave(&(pxmitpriv->be_pending.lock), irqL0);
+ r8712_free_xmitframe_queue(pxmitpriv, &pstaxmitpriv->be_q.sta_pending);
+ list_del_init(&(pstaxmitpriv->be_q.tx_pending));
+ spin_unlock_irqrestore(&(pxmitpriv->be_pending.lock), irqL0);
+ list_del_init(&psta->hash_list);
+ pstapriv->asoc_sta_count--;
+ /* re-init sta_info; 20061114 */
+ _r8712_init_sta_xmit_priv(&psta->sta_xmitpriv);
+ _r8712_init_sta_recv_priv(&psta->sta_recvpriv);
+ /* for A-MPDU Rx reordering buffer control,
+ * cancel reordering_ctrl_timer */
+ for (i = 0; i < 16; i++) {
+ preorder_ctrl = &psta->recvreorder_ctrl[i];
+ del_timer(&preorder_ctrl->reordering_ctrl_timer);
+ }
+ spin_lock(&(pfree_sta_queue->lock));
+ /* insert into free_sta_queue; 20061114 */
+ list_add_tail(&psta->list, &pfree_sta_queue->queue);
+ spin_unlock(&(pfree_sta_queue->lock));
+}
+
+/* free all stainfo which in sta_hash[all] */
+void r8712_free_all_stainfo(struct _adapter *padapter)
+{
+ unsigned long irqL;
+ struct list_head *plist, *phead;
+ s32 index;
+ struct sta_info *psta = NULL;
+ struct sta_priv *pstapriv = &padapter->stapriv;
+ struct sta_info *pbcmc_stainfo = r8712_get_bcmc_stainfo(padapter);
+
+ if (pstapriv->asoc_sta_count == 1)
+ return;
+ spin_lock_irqsave(&pstapriv->sta_hash_lock, irqL);
+ for (index = 0; index < NUM_STA; index++) {
+ phead = &(pstapriv->sta_hash[index]);
+ plist = phead->next;
+ while ((end_of_queue_search(phead, plist)) == false) {
+ psta = LIST_CONTAINOR(plist,
+ struct sta_info, hash_list);
+ plist = plist->next;
+ if (pbcmc_stainfo != psta)
+ r8712_free_stainfo(padapter, psta);
+ }
+ }
+ spin_unlock_irqrestore(&pstapriv->sta_hash_lock, irqL);
+}
+
+/* any station allocated can be searched by hash list */
+struct sta_info *r8712_get_stainfo(struct sta_priv *pstapriv, u8 *hwaddr)
+{
+ unsigned long irqL;
+ struct list_head *plist, *phead;
+ struct sta_info *psta = NULL;
+ u32 index;
+
+ if (hwaddr == NULL)
+ return NULL;
+ index = wifi_mac_hash(hwaddr);
+ spin_lock_irqsave(&pstapriv->sta_hash_lock, irqL);
+ phead = &(pstapriv->sta_hash[index]);
+ plist = phead->next;
+ while ((end_of_queue_search(phead, plist)) == false) {
+ psta = LIST_CONTAINOR(plist, struct sta_info, hash_list);
+ if ((!memcmp(psta->hwaddr, hwaddr, ETH_ALEN))) {
+ /* if found the matched address */
+ break;
+ }
+ psta = NULL;
+ plist = plist->next;
+ }
+ spin_unlock_irqrestore(&pstapriv->sta_hash_lock, irqL);
+ return psta;
+}
+
+void r8712_init_bcmc_stainfo(struct _adapter *padapter)
+{
+ unsigned char bcast_addr[6] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
+ struct sta_priv *pstapriv = &padapter->stapriv;
+
+ r8712_alloc_stainfo(pstapriv, bcast_addr);
+}
+
+struct sta_info *r8712_get_bcmc_stainfo(struct _adapter *padapter)
+{
+ struct sta_info *psta;
+ struct sta_priv *pstapriv = &padapter->stapriv;
+ u8 bc_addr[ETH_ALEN] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
+
+ psta = r8712_get_stainfo(pstapriv, bc_addr);
+ return psta;
+}
+
+
+u8 r8712_access_ctrl(struct wlan_acl_pool *pacl_list, u8 *mac_addr)
+{
+ return true;
+}
diff --git a/drivers/staging/rtl8712/rtl871x_wlan_sme.h b/drivers/staging/rtl8712/rtl871x_wlan_sme.h
new file mode 100644
index 000000000..44924d5de
--- /dev/null
+++ b/drivers/staging/rtl8712/rtl871x_wlan_sme.h
@@ -0,0 +1,47 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2010 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ * Modifications for inclusion into the Linux staging tree are
+ * Copyright(c) 2010 Larry Finger. All rights reserved.
+ *
+ * Contact information:
+ * WLAN FAE <wlanfae@realtek.com>
+ * Larry Finger <Larry.Finger@lwfinger.net>
+ *
+ ******************************************************************************/
+#ifndef _RTL871X_WLAN_SME_H_
+#define _RTL871X_WLAN_SME_H_
+
+#define MSR_APMODE 0x0C
+#define MSR_STAMODE 0x08
+#define MSR_ADHOCMODE 0x04
+#define MSR_NOLINKMODE 0x00
+#define _1M_RATE_ 0
+#define _2M_RATE_ 1
+#define _5M_RATE_ 2
+#define _11M_RATE_ 3
+#define _6M_RATE_ 4
+#define _9M_RATE_ 5
+#define _12M_RATE_ 6
+#define _18M_RATE_ 7
+#define _24M_RATE_ 8
+#define _36M_RATE_ 9
+#define _48M_RATE_ 10
+#define _54M_RATE_ 11
+
+#endif
+
diff --git a/drivers/staging/rtl8712/rtl871x_xmit.c b/drivers/staging/rtl8712/rtl871x_xmit.c
new file mode 100644
index 000000000..2e4fa8895
--- /dev/null
+++ b/drivers/staging/rtl8712/rtl871x_xmit.c
@@ -0,0 +1,1056 @@
+/******************************************************************************
+ * rtl871x_xmit.c
+ *
+ * Copyright(c) 2007 - 2010 Realtek Corporation. All rights reserved.
+ * Linux device driver for RTL8192SU
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ * Modifications for inclusion into the Linux staging tree are
+ * Copyright(c) 2010 Larry Finger. All rights reserved.
+ *
+ * Contact information:
+ * WLAN FAE <wlanfae@realtek.com>
+ * Larry Finger <Larry.Finger@lwfinger.net>
+ *
+ ******************************************************************************/
+
+#define _RTL871X_XMIT_C_
+
+#include "osdep_service.h"
+#include "drv_types.h"
+#include "wifi.h"
+#include "osdep_intf.h"
+#include "usb_ops.h"
+
+
+static const u8 P802_1H_OUI[P80211_OUI_LEN] = {0x00, 0x00, 0xf8};
+static const u8 RFC1042_OUI[P80211_OUI_LEN] = {0x00, 0x00, 0x00};
+static void init_hwxmits(struct hw_xmit *phwxmit, sint entry);
+static void alloc_hwxmits(struct _adapter *padapter);
+static void free_hwxmits(struct _adapter *padapter);
+
+static void _init_txservq(struct tx_servq *ptxservq)
+{
+ INIT_LIST_HEAD(&ptxservq->tx_pending);
+ _init_queue(&ptxservq->sta_pending);
+ ptxservq->qcnt = 0;
+}
+
+void _r8712_init_sta_xmit_priv(struct sta_xmit_priv *psta_xmitpriv)
+{
+ memset((unsigned char *)psta_xmitpriv, 0,
+ sizeof(struct sta_xmit_priv));
+ spin_lock_init(&psta_xmitpriv->lock);
+ _init_txservq(&psta_xmitpriv->be_q);
+ _init_txservq(&psta_xmitpriv->bk_q);
+ _init_txservq(&psta_xmitpriv->vi_q);
+ _init_txservq(&psta_xmitpriv->vo_q);
+ INIT_LIST_HEAD(&psta_xmitpriv->legacy_dz);
+ INIT_LIST_HEAD(&psta_xmitpriv->apsd);
+}
+
+sint _r8712_init_xmit_priv(struct xmit_priv *pxmitpriv,
+ struct _adapter *padapter)
+{
+ sint i;
+ struct xmit_buf *pxmitbuf;
+ struct xmit_frame *pxframe;
+
+ memset((unsigned char *)pxmitpriv, 0, sizeof(struct xmit_priv));
+ spin_lock_init(&pxmitpriv->lock);
+ /*
+ Please insert all the queue initialization using _init_queue below
+ */
+ pxmitpriv->adapter = padapter;
+ _init_queue(&pxmitpriv->be_pending);
+ _init_queue(&pxmitpriv->bk_pending);
+ _init_queue(&pxmitpriv->vi_pending);
+ _init_queue(&pxmitpriv->vo_pending);
+ _init_queue(&pxmitpriv->bm_pending);
+ _init_queue(&pxmitpriv->legacy_dz_queue);
+ _init_queue(&pxmitpriv->apsd_queue);
+ _init_queue(&pxmitpriv->free_xmit_queue);
+ /*
+ Please allocate memory with the sz = (struct xmit_frame) * NR_XMITFRAME,
+ and initialize free_xmit_frame below.
+ Please also apply free_txobj to link_up all the xmit_frames...
+ */
+ pxmitpriv->pallocated_frame_buf = kmalloc(NR_XMITFRAME * sizeof(struct xmit_frame) + 4,
+ GFP_ATOMIC);
+ if (pxmitpriv->pallocated_frame_buf == NULL) {
+ pxmitpriv->pxmit_frame_buf = NULL;
+ return _FAIL;
+ }
+ pxmitpriv->pxmit_frame_buf = pxmitpriv->pallocated_frame_buf + 4 -
+ ((addr_t) (pxmitpriv->pallocated_frame_buf) & 3);
+ pxframe = (struct xmit_frame *) pxmitpriv->pxmit_frame_buf;
+ for (i = 0; i < NR_XMITFRAME; i++) {
+ INIT_LIST_HEAD(&(pxframe->list));
+ pxframe->padapter = padapter;
+ pxframe->frame_tag = DATA_FRAMETAG;
+ pxframe->pkt = NULL;
+ pxframe->buf_addr = NULL;
+ pxframe->pxmitbuf = NULL;
+ list_add_tail(&(pxframe->list),
+ &(pxmitpriv->free_xmit_queue.queue));
+ pxframe++;
+ }
+ pxmitpriv->free_xmitframe_cnt = NR_XMITFRAME;
+ /*
+ init xmit hw_txqueue
+ */
+ _r8712_init_hw_txqueue(&pxmitpriv->be_txqueue, BE_QUEUE_INX);
+ _r8712_init_hw_txqueue(&pxmitpriv->bk_txqueue, BK_QUEUE_INX);
+ _r8712_init_hw_txqueue(&pxmitpriv->vi_txqueue, VI_QUEUE_INX);
+ _r8712_init_hw_txqueue(&pxmitpriv->vo_txqueue, VO_QUEUE_INX);
+ _r8712_init_hw_txqueue(&pxmitpriv->bmc_txqueue, BMC_QUEUE_INX);
+ pxmitpriv->frag_len = MAX_FRAG_THRESHOLD;
+ pxmitpriv->txirp_cnt = 1;
+ /*per AC pending irp*/
+ pxmitpriv->beq_cnt = 0;
+ pxmitpriv->bkq_cnt = 0;
+ pxmitpriv->viq_cnt = 0;
+ pxmitpriv->voq_cnt = 0;
+ /*init xmit_buf*/
+ _init_queue(&pxmitpriv->free_xmitbuf_queue);
+ _init_queue(&pxmitpriv->pending_xmitbuf_queue);
+ pxmitpriv->pallocated_xmitbuf = kmalloc(NR_XMITBUFF * sizeof(struct xmit_buf) + 4,
+ GFP_ATOMIC);
+ if (pxmitpriv->pallocated_xmitbuf == NULL)
+ return _FAIL;
+ pxmitpriv->pxmitbuf = pxmitpriv->pallocated_xmitbuf + 4 -
+ ((addr_t)(pxmitpriv->pallocated_xmitbuf) & 3);
+ pxmitbuf = (struct xmit_buf *)pxmitpriv->pxmitbuf;
+ for (i = 0; i < NR_XMITBUFF; i++) {
+ INIT_LIST_HEAD(&pxmitbuf->list);
+ pxmitbuf->pallocated_buf = kmalloc(MAX_XMITBUF_SZ + XMITBUF_ALIGN_SZ,
+ GFP_ATOMIC);
+ if (pxmitbuf->pallocated_buf == NULL)
+ return _FAIL;
+ pxmitbuf->pbuf = pxmitbuf->pallocated_buf + XMITBUF_ALIGN_SZ -
+ ((addr_t) (pxmitbuf->pallocated_buf) &
+ (XMITBUF_ALIGN_SZ - 1));
+ r8712_xmit_resource_alloc(padapter, pxmitbuf);
+ list_add_tail(&pxmitbuf->list,
+ &(pxmitpriv->free_xmitbuf_queue.queue));
+ pxmitbuf++;
+ }
+ pxmitpriv->free_xmitbuf_cnt = NR_XMITBUFF;
+ INIT_WORK(&padapter->wkFilterRxFF0, r8712_SetFilter);
+ alloc_hwxmits(padapter);
+ init_hwxmits(pxmitpriv->hwxmits, pxmitpriv->hwxmit_entry);
+ tasklet_init(&pxmitpriv->xmit_tasklet,
+ (void(*)(unsigned long))r8712_xmit_bh,
+ (unsigned long)padapter);
+ return _SUCCESS;
+}
+
+void _free_xmit_priv(struct xmit_priv *pxmitpriv)
+{
+ int i;
+ struct _adapter *padapter = pxmitpriv->adapter;
+ struct xmit_frame *pxmitframe = (struct xmit_frame *)
+ pxmitpriv->pxmit_frame_buf;
+ struct xmit_buf *pxmitbuf = (struct xmit_buf *)pxmitpriv->pxmitbuf;
+
+ if (pxmitpriv->pxmit_frame_buf == NULL)
+ return;
+ for (i = 0; i < NR_XMITFRAME; i++) {
+ r8712_xmit_complete(padapter, pxmitframe);
+ pxmitframe++;
+ }
+ for (i = 0; i < NR_XMITBUFF; i++) {
+ r8712_xmit_resource_free(padapter, pxmitbuf);
+ kfree(pxmitbuf->pallocated_buf);
+ pxmitbuf++;
+ }
+ kfree(pxmitpriv->pallocated_frame_buf);
+ kfree(pxmitpriv->pallocated_xmitbuf);
+ free_hwxmits(padapter);
+}
+
+sint r8712_update_attrib(struct _adapter *padapter, _pkt *pkt,
+ struct pkt_attrib *pattrib)
+{
+ struct pkt_file pktfile;
+ struct sta_info *psta = NULL;
+ struct ethhdr etherhdr;
+
+ struct tx_cmd txdesc;
+
+ sint bmcast;
+ struct sta_priv *pstapriv = &padapter->stapriv;
+ struct security_priv *psecuritypriv = &padapter->securitypriv;
+ struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
+ struct qos_priv *pqospriv = &pmlmepriv->qospriv;
+
+ _r8712_open_pktfile(pkt, &pktfile);
+
+ _r8712_pktfile_read(&pktfile, (unsigned char *)&etherhdr, ETH_HLEN);
+
+ pattrib->ether_type = ntohs(etherhdr.h_proto);
+
+{
+ /*If driver xmit ARP packet, driver can set ps mode to initial
+ * setting. It stands for getting DHCP or fix IP.*/
+ if (pattrib->ether_type == 0x0806) {
+ if (padapter->pwrctrlpriv.pwr_mode !=
+ padapter->registrypriv.power_mgnt) {
+ del_timer_sync(&pmlmepriv->dhcp_timer);
+ r8712_set_ps_mode(padapter, padapter->registrypriv.
+ power_mgnt, padapter->registrypriv.smart_ps);
+ }
+ }
+}
+ memcpy(pattrib->dst, &etherhdr.h_dest, ETH_ALEN);
+ memcpy(pattrib->src, &etherhdr.h_source, ETH_ALEN);
+ pattrib->pctrl = 0;
+ if ((check_fwstate(pmlmepriv, WIFI_ADHOC_STATE) == true) ||
+ (check_fwstate(pmlmepriv, WIFI_ADHOC_MASTER_STATE) == true)) {
+ memcpy(pattrib->ra, pattrib->dst, ETH_ALEN);
+ memcpy(pattrib->ta, pattrib->src, ETH_ALEN);
+ } else if (check_fwstate(pmlmepriv, WIFI_STATION_STATE)) {
+ memcpy(pattrib->ra, get_bssid(pmlmepriv), ETH_ALEN);
+ memcpy(pattrib->ta, pattrib->src, ETH_ALEN);
+ } else if (check_fwstate(pmlmepriv, WIFI_AP_STATE)) {
+ memcpy(pattrib->ra, pattrib->dst, ETH_ALEN);
+ memcpy(pattrib->ta, get_bssid(pmlmepriv), ETH_ALEN);
+ } else if (check_fwstate(pmlmepriv, WIFI_MP_STATE) == true) {
+ /*firstly, filter packet not belongs to mp*/
+ if (pattrib->ether_type != 0x8712)
+ return _FAIL;
+ /* for mp storing the txcmd per packet,
+ * according to the info of txcmd to update pattrib */
+ /*get MP_TXDESC_SIZE bytes txcmd per packet*/
+ _r8712_pktfile_read(&pktfile, (u8 *)&txdesc, TXDESC_SIZE);
+ memcpy(pattrib->ra, pattrib->dst, ETH_ALEN);
+ memcpy(pattrib->ta, pattrib->src, ETH_ALEN);
+ pattrib->pctrl = 1;
+ }
+ /* r8712_xmitframe_coalesce() overwrite this!*/
+ pattrib->pktlen = pktfile.pkt_len;
+ if (ETH_P_IP == pattrib->ether_type) {
+ /* The following is for DHCP and ARP packet, we use cck1M to
+ * tx these packets and let LPS awake some time
+ * to prevent DHCP protocol fail */
+ u8 tmp[24];
+
+ _r8712_pktfile_read(&pktfile, &tmp[0], 24);
+ pattrib->dhcp_pkt = 0;
+ if (pktfile.pkt_len > 282) {/*MINIMUM_DHCP_PACKET_SIZE)*/
+ if (ETH_P_IP == pattrib->ether_type) {/* IP header*/
+ if (((tmp[21] == 68) && (tmp[23] == 67)) ||
+ ((tmp[21] == 67) && (tmp[23] == 68))) {
+ /* 68 : UDP BOOTP client
+ * 67 : UDP BOOTP server
+ * Use low rate to send DHCP packet.*/
+ pattrib->dhcp_pkt = 1;
+ }
+ }
+ }
+ }
+ bmcast = IS_MCAST(pattrib->ra);
+ /* get sta_info*/
+ if (bmcast) {
+ psta = r8712_get_bcmc_stainfo(padapter);
+ pattrib->mac_id = 4;
+ } else {
+ if (check_fwstate(pmlmepriv, WIFI_MP_STATE) == true) {
+ psta = r8712_get_stainfo(pstapriv,
+ get_bssid(pmlmepriv));
+ pattrib->mac_id = 5;
+ } else {
+ psta = r8712_get_stainfo(pstapriv, pattrib->ra);
+ if (psta == NULL) /* drop the pkt */
+ return _FAIL;
+ if (check_fwstate(pmlmepriv, WIFI_STATION_STATE))
+ pattrib->mac_id = 5;
+ else
+ pattrib->mac_id = psta->mac_id;
+ }
+ }
+
+ if (psta) {
+ pattrib->psta = psta;
+ } else {
+ /* if we cannot get psta => drrp the pkt */
+ return _FAIL;
+ }
+
+ pattrib->ack_policy = 0;
+ /* get ether_hdr_len */
+ pattrib->pkt_hdrlen = ETH_HLEN;
+
+ if (pqospriv->qos_option)
+ r8712_set_qos(&pktfile, pattrib);
+ else {
+ pattrib->hdrlen = WLAN_HDR_A3_LEN;
+ pattrib->subtype = WIFI_DATA_TYPE;
+ pattrib->priority = 0;
+ }
+ if (psta->ieee8021x_blocked == true) {
+ pattrib->encrypt = 0;
+ if ((pattrib->ether_type != 0x888e) &&
+ (check_fwstate(pmlmepriv, WIFI_MP_STATE) == false))
+ return _FAIL;
+ } else
+ GET_ENCRY_ALGO(psecuritypriv, psta, pattrib->encrypt, bmcast);
+ switch (pattrib->encrypt) {
+ case _WEP40_:
+ case _WEP104_:
+ pattrib->iv_len = 4;
+ pattrib->icv_len = 4;
+ break;
+ case _TKIP_:
+ pattrib->iv_len = 8;
+ pattrib->icv_len = 4;
+ if (padapter->securitypriv.busetkipkey == _FAIL)
+ return _FAIL;
+ break;
+ case _AES_:
+ pattrib->iv_len = 8;
+ pattrib->icv_len = 8;
+ break;
+ default:
+ pattrib->iv_len = 0;
+ pattrib->icv_len = 0;
+ break;
+ }
+
+ if (pattrib->encrypt &&
+ ((padapter->securitypriv.sw_encrypt == true) ||
+ (psecuritypriv->hw_decrypted == false)))
+ pattrib->bswenc = true;
+ else
+ pattrib->bswenc = false;
+ /* if in MP_STATE, update pkt_attrib from mp_txcmd, and overwrite
+ * some settings above.*/
+ if (check_fwstate(pmlmepriv, WIFI_MP_STATE) == true)
+ pattrib->priority = (txdesc.txdw1 >> QSEL_SHT) & 0x1f;
+ return _SUCCESS;
+}
+
+static sint xmitframe_addmic(struct _adapter *padapter,
+ struct xmit_frame *pxmitframe)
+{
+ u32 curfragnum, length;
+ u8 *pframe, *payload, mic[8];
+ struct mic_data micdata;
+ struct sta_info *stainfo;
+ struct qos_priv *pqospriv = &(padapter->mlmepriv.qospriv);
+ struct pkt_attrib *pattrib = &pxmitframe->attrib;
+ struct security_priv *psecuritypriv = &padapter->securitypriv;
+ struct xmit_priv *pxmitpriv = &padapter->xmitpriv;
+ u8 priority[4] = {0x0, 0x0, 0x0, 0x0};
+ sint bmcst = IS_MCAST(pattrib->ra);
+
+ if (pattrib->psta)
+ stainfo = pattrib->psta;
+ else
+ stainfo = r8712_get_stainfo(&padapter->stapriv,
+ &pattrib->ra[0]);
+ if (pattrib->encrypt == _TKIP_) {
+ /*encode mic code*/
+ if (stainfo != NULL) {
+ u8 null_key[16] = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0};
+ pframe = pxmitframe->buf_addr + TXDESC_OFFSET;
+ if (bmcst) {
+ if (!memcmp(psecuritypriv->XGrptxmickey
+ [psecuritypriv->XGrpKeyid].skey,
+ null_key, 16))
+ return _FAIL;
+ /*start to calculate the mic code*/
+ r8712_secmicsetkey(&micdata,
+ psecuritypriv->
+ XGrptxmickey[psecuritypriv->
+ XGrpKeyid].skey);
+ } else {
+ if (!memcmp(&stainfo->tkiptxmickey.skey[0],
+ null_key, 16))
+ return _FAIL;
+ /* start to calculate the mic code */
+ r8712_secmicsetkey(&micdata,
+ &stainfo->tkiptxmickey.skey[0]);
+ }
+ if (pframe[1] & 1) { /* ToDS==1 */
+ r8712_secmicappend(&micdata,
+ &pframe[16], 6); /*DA*/
+ if (pframe[1]&2) /* From Ds==1 */
+ r8712_secmicappend(&micdata,
+ &pframe[24], 6);
+ else
+ r8712_secmicappend(&micdata,
+ &pframe[10], 6);
+ } else { /* ToDS==0 */
+ r8712_secmicappend(&micdata,
+ &pframe[4], 6); /* DA */
+ if (pframe[1]&2) /* From Ds==1 */
+ r8712_secmicappend(&micdata,
+ &pframe[16], 6);
+ else
+ r8712_secmicappend(&micdata,
+ &pframe[10], 6);
+ }
+ if (pqospriv->qos_option == 1)
+ priority[0] = (u8)pxmitframe->
+ attrib.priority;
+ r8712_secmicappend(&micdata, &priority[0], 4);
+ payload = pframe;
+ for (curfragnum = 0; curfragnum < pattrib->nr_frags;
+ curfragnum++) {
+ payload = (u8 *)RND4((addr_t)(payload));
+ payload = payload+pattrib->
+ hdrlen+pattrib->iv_len;
+ if ((curfragnum + 1) == pattrib->nr_frags) {
+ length = pattrib->last_txcmdsz -
+ pattrib->hdrlen -
+ pattrib->iv_len -
+ ((psecuritypriv->sw_encrypt)
+ ? pattrib->icv_len : 0);
+ r8712_secmicappend(&micdata, payload,
+ length);
+ payload = payload+length;
+ } else{
+ length = pxmitpriv->frag_len -
+ pattrib->hdrlen-pattrib->iv_len -
+ ((psecuritypriv->sw_encrypt) ?
+ pattrib->icv_len : 0);
+ r8712_secmicappend(&micdata, payload,
+ length);
+ payload = payload + length +
+ pattrib->icv_len;
+ }
+ }
+ r8712_secgetmic(&micdata, &(mic[0]));
+ /* add mic code and add the mic code length in
+ * last_txcmdsz */
+ memcpy(payload, &(mic[0]), 8);
+ pattrib->last_txcmdsz += 8;
+ payload = payload-pattrib->last_txcmdsz + 8;
+ }
+ }
+ return _SUCCESS;
+}
+
+static sint xmitframe_swencrypt(struct _adapter *padapter,
+ struct xmit_frame *pxmitframe)
+{
+ struct pkt_attrib *pattrib = &pxmitframe->attrib;
+
+ if (pattrib->bswenc) {
+ switch (pattrib->encrypt) {
+ case _WEP40_:
+ case _WEP104_:
+ r8712_wep_encrypt(padapter, (u8 *)pxmitframe);
+ break;
+ case _TKIP_:
+ r8712_tkip_encrypt(padapter, (u8 *)pxmitframe);
+ break;
+ case _AES_:
+ r8712_aes_encrypt(padapter, (u8 *)pxmitframe);
+ break;
+ default:
+ break;
+ }
+ }
+ return _SUCCESS;
+}
+
+static sint make_wlanhdr(struct _adapter *padapter, u8 *hdr,
+ struct pkt_attrib *pattrib)
+{
+ u16 *qc;
+
+ struct ieee80211_hdr *pwlanhdr = (struct ieee80211_hdr *)hdr;
+ struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
+ struct qos_priv *pqospriv = &pmlmepriv->qospriv;
+ u16 *fctrl = &pwlanhdr->frame_ctl;
+
+ memset(hdr, 0, WLANHDR_OFFSET);
+ SetFrameSubType(fctrl, pattrib->subtype);
+ if (pattrib->subtype & WIFI_DATA_TYPE) {
+ if (check_fwstate(pmlmepriv, WIFI_STATION_STATE) == true) {
+ /* to_ds = 1, fr_ds = 0; */
+ SetToDs(fctrl);
+ memcpy(pwlanhdr->addr1, get_bssid(pmlmepriv),
+ ETH_ALEN);
+ memcpy(pwlanhdr->addr2, pattrib->src, ETH_ALEN);
+ memcpy(pwlanhdr->addr3, pattrib->dst, ETH_ALEN);
+ } else if (check_fwstate(pmlmepriv, WIFI_AP_STATE) == true) {
+ /* to_ds = 0, fr_ds = 1; */
+ SetFrDs(fctrl);
+ memcpy(pwlanhdr->addr1, pattrib->dst, ETH_ALEN);
+ memcpy(pwlanhdr->addr2, get_bssid(pmlmepriv),
+ ETH_ALEN);
+ memcpy(pwlanhdr->addr3, pattrib->src, ETH_ALEN);
+ } else if ((check_fwstate(pmlmepriv, WIFI_ADHOC_STATE) == true)
+ || (check_fwstate(pmlmepriv, WIFI_ADHOC_MASTER_STATE)
+ == true)) {
+ memcpy(pwlanhdr->addr1, pattrib->dst, ETH_ALEN);
+ memcpy(pwlanhdr->addr2, pattrib->src, ETH_ALEN);
+ memcpy(pwlanhdr->addr3, get_bssid(pmlmepriv),
+ ETH_ALEN);
+ } else if (check_fwstate(pmlmepriv, WIFI_MP_STATE) == true) {
+ memcpy(pwlanhdr->addr1, pattrib->dst, ETH_ALEN);
+ memcpy(pwlanhdr->addr2, pattrib->src, ETH_ALEN);
+ memcpy(pwlanhdr->addr3, get_bssid(pmlmepriv),
+ ETH_ALEN);
+ } else
+ return _FAIL;
+
+ if (pattrib->encrypt)
+ SetPrivacy(fctrl);
+ if (pqospriv->qos_option) {
+ qc = (unsigned short *)(hdr + pattrib->hdrlen - 2);
+ if (pattrib->priority)
+ SetPriority(qc, pattrib->priority);
+ SetAckpolicy(qc, pattrib->ack_policy);
+ }
+ /* TODO: fill HT Control Field */
+ /* Update Seq Num will be handled by f/w */
+ {
+ struct sta_info *psta;
+ sint bmcst = IS_MCAST(pattrib->ra);
+
+ if (pattrib->psta)
+ psta = pattrib->psta;
+ else {
+ if (bmcst)
+ psta = r8712_get_bcmc_stainfo(padapter);
+ else
+ psta =
+ r8712_get_stainfo(&padapter->stapriv,
+ pattrib->ra);
+ }
+ if (psta) {
+ psta->sta_xmitpriv.txseq_tid
+ [pattrib->priority]++;
+ psta->sta_xmitpriv.txseq_tid[pattrib->priority]
+ &= 0xFFF;
+ pattrib->seqnum = psta->sta_xmitpriv.
+ txseq_tid[pattrib->priority];
+ SetSeqNum(hdr, pattrib->seqnum);
+ }
+ }
+ }
+ return _SUCCESS;
+}
+
+static sint r8712_put_snap(u8 *data, u16 h_proto)
+{
+ struct ieee80211_snap_hdr *snap;
+ const u8 *oui;
+
+ snap = (struct ieee80211_snap_hdr *)data;
+ snap->dsap = 0xaa;
+ snap->ssap = 0xaa;
+ snap->ctrl = 0x03;
+ if (h_proto == 0x8137 || h_proto == 0x80f3)
+ oui = P802_1H_OUI;
+ else
+ oui = RFC1042_OUI;
+ snap->oui[0] = oui[0];
+ snap->oui[1] = oui[1];
+ snap->oui[2] = oui[2];
+ *(u16 *)(data + SNAP_SIZE) = htons(h_proto);
+ return SNAP_SIZE + sizeof(u16);
+}
+
+/*
+ * This sub-routine will perform all the following:
+ * 1. remove 802.3 header.
+ * 2. create wlan_header, based on the info in pxmitframe
+ * 3. append sta's iv/ext-iv
+ * 4. append LLC
+ * 5. move frag chunk from pframe to pxmitframe->mem
+ * 6. apply sw-encrypt, if necessary.
+ */
+sint r8712_xmitframe_coalesce(struct _adapter *padapter, _pkt *pkt,
+ struct xmit_frame *pxmitframe)
+{
+ struct pkt_file pktfile;
+
+ sint frg_len, mpdu_len, llc_sz;
+ u32 mem_sz;
+ u8 frg_inx;
+ addr_t addr;
+ u8 *pframe, *mem_start, *ptxdesc;
+ struct sta_info *psta;
+ struct security_priv *psecuritypriv = &padapter->securitypriv;
+ struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
+ struct xmit_priv *pxmitpriv = &padapter->xmitpriv;
+ struct pkt_attrib *pattrib = &pxmitframe->attrib;
+ u8 *pbuf_start;
+ sint bmcst = IS_MCAST(pattrib->ra);
+
+ if (pattrib->psta == NULL)
+ return _FAIL;
+ psta = pattrib->psta;
+ if (pxmitframe->buf_addr == NULL)
+ return _FAIL;
+ pbuf_start = pxmitframe->buf_addr;
+ ptxdesc = pbuf_start;
+ mem_start = pbuf_start + TXDESC_OFFSET;
+ if (make_wlanhdr(padapter, mem_start, pattrib) == _FAIL)
+ return _FAIL;
+ _r8712_open_pktfile(pkt, &pktfile);
+ _r8712_pktfile_read(&pktfile, NULL, (uint) pattrib->pkt_hdrlen);
+ if (check_fwstate(pmlmepriv, WIFI_MP_STATE) == true) {
+ /* truncate TXDESC_SIZE bytes txcmd if at mp mode for 871x */
+ if (pattrib->ether_type == 0x8712) {
+ /* take care - update_txdesc overwrite this */
+ _r8712_pktfile_read(&pktfile, ptxdesc, TXDESC_SIZE);
+ }
+ }
+ pattrib->pktlen = pktfile.pkt_len;
+ frg_inx = 0;
+ frg_len = pxmitpriv->frag_len - 4;
+ while (1) {
+ llc_sz = 0;
+ mpdu_len = frg_len;
+ pframe = mem_start;
+ SetMFrag(mem_start);
+ pframe += pattrib->hdrlen;
+ mpdu_len -= pattrib->hdrlen;
+ /* adding icv, if necessary...*/
+ if (pattrib->iv_len) {
+ if (psta != NULL) {
+ switch (pattrib->encrypt) {
+ case _WEP40_:
+ case _WEP104_:
+ WEP_IV(pattrib->iv, psta->txpn,
+ (u8)psecuritypriv->
+ PrivacyKeyIndex);
+ break;
+ case _TKIP_:
+ if (bmcst)
+ TKIP_IV(pattrib->iv,
+ psta->txpn,
+ (u8)psecuritypriv->
+ XGrpKeyid);
+ else
+ TKIP_IV(pattrib->iv, psta->txpn,
+ 0);
+ break;
+ case _AES_:
+ if (bmcst)
+ AES_IV(pattrib->iv, psta->txpn,
+ (u8)psecuritypriv->
+ XGrpKeyid);
+ else
+ AES_IV(pattrib->iv, psta->txpn,
+ 0);
+ break;
+ }
+ }
+ memcpy(pframe, pattrib->iv, pattrib->iv_len);
+ pframe += pattrib->iv_len;
+ mpdu_len -= pattrib->iv_len;
+ }
+ if (frg_inx == 0) {
+ llc_sz = r8712_put_snap(pframe, pattrib->ether_type);
+ pframe += llc_sz;
+ mpdu_len -= llc_sz;
+ }
+ if ((pattrib->icv_len > 0) && (pattrib->bswenc))
+ mpdu_len -= pattrib->icv_len;
+ if (bmcst)
+ mem_sz = _r8712_pktfile_read(&pktfile, pframe,
+ pattrib->pktlen);
+ else
+ mem_sz = _r8712_pktfile_read(&pktfile, pframe,
+ mpdu_len);
+ pframe += mem_sz;
+ if ((pattrib->icv_len > 0) && (pattrib->bswenc)) {
+ memcpy(pframe, pattrib->icv, pattrib->icv_len);
+ pframe += pattrib->icv_len;
+ }
+ frg_inx++;
+ if (bmcst || (r8712_endofpktfile(&pktfile) == true)) {
+ pattrib->nr_frags = frg_inx;
+ pattrib->last_txcmdsz = pattrib->hdrlen +
+ pattrib->iv_len +
+ ((pattrib->nr_frags == 1) ?
+ llc_sz : 0) +
+ ((pattrib->bswenc) ?
+ pattrib->icv_len : 0) + mem_sz;
+ ClearMFrag(mem_start);
+ break;
+ }
+ addr = (addr_t)(pframe);
+ mem_start = (unsigned char *)RND4(addr) + TXDESC_OFFSET;
+ memcpy(mem_start, pbuf_start + TXDESC_OFFSET, pattrib->hdrlen);
+ }
+
+ if (xmitframe_addmic(padapter, pxmitframe) == _FAIL)
+ return _FAIL;
+ xmitframe_swencrypt(padapter, pxmitframe);
+ return _SUCCESS;
+}
+
+void r8712_update_protection(struct _adapter *padapter, u8 *ie, uint ie_len)
+{
+ uint protection;
+ u8 *perp;
+ sint erp_len;
+ struct xmit_priv *pxmitpriv = &padapter->xmitpriv;
+ struct registry_priv *pregistrypriv = &padapter->registrypriv;
+
+ switch (pxmitpriv->vcs_setting) {
+ case DISABLE_VCS:
+ pxmitpriv->vcs = NONE_VCS;
+ break;
+ case ENABLE_VCS:
+ break;
+ case AUTO_VCS:
+ default:
+ perp = r8712_get_ie(ie, _ERPINFO_IE_, &erp_len, ie_len);
+ if (perp == NULL)
+ pxmitpriv->vcs = NONE_VCS;
+ else {
+ protection = (*(perp + 2)) & BIT(1);
+ if (protection) {
+ if (pregistrypriv->vcs_type == RTS_CTS)
+ pxmitpriv->vcs = RTS_CTS;
+ else
+ pxmitpriv->vcs = CTS_TO_SELF;
+ } else
+ pxmitpriv->vcs = NONE_VCS;
+ }
+ break;
+ }
+}
+
+struct xmit_buf *r8712_alloc_xmitbuf(struct xmit_priv *pxmitpriv)
+{
+ unsigned long irqL;
+ struct xmit_buf *pxmitbuf = NULL;
+ struct list_head *plist, *phead;
+ struct __queue *pfree_xmitbuf_queue = &pxmitpriv->free_xmitbuf_queue;
+
+ spin_lock_irqsave(&pfree_xmitbuf_queue->lock, irqL);
+ if (list_empty(&pfree_xmitbuf_queue->queue))
+ pxmitbuf = NULL;
+ else {
+ phead = &pfree_xmitbuf_queue->queue;
+ plist = phead->next;
+ pxmitbuf = LIST_CONTAINOR(plist, struct xmit_buf, list);
+ list_del_init(&(pxmitbuf->list));
+ }
+ if (pxmitbuf != NULL)
+ pxmitpriv->free_xmitbuf_cnt--;
+ spin_unlock_irqrestore(&pfree_xmitbuf_queue->lock, irqL);
+ return pxmitbuf;
+}
+
+int r8712_free_xmitbuf(struct xmit_priv *pxmitpriv, struct xmit_buf *pxmitbuf)
+{
+ unsigned long irqL;
+ struct __queue *pfree_xmitbuf_queue = &pxmitpriv->free_xmitbuf_queue;
+
+ if (pxmitbuf == NULL)
+ return _FAIL;
+ spin_lock_irqsave(&pfree_xmitbuf_queue->lock, irqL);
+ list_del_init(&pxmitbuf->list);
+ list_add_tail(&(pxmitbuf->list), &pfree_xmitbuf_queue->queue);
+ pxmitpriv->free_xmitbuf_cnt++;
+ spin_unlock_irqrestore(&pfree_xmitbuf_queue->lock, irqL);
+ return _SUCCESS;
+}
+
+/*
+Calling context:
+1. OS_TXENTRY
+2. RXENTRY (rx_thread or RX_ISR/RX_CallBack)
+
+If we turn on USE_RXTHREAD, then, no need for critical section.
+Otherwise, we must use _enter/_exit critical to protect free_xmit_queue...
+
+Must be very very cautious...
+
+*/
+
+struct xmit_frame *r8712_alloc_xmitframe(struct xmit_priv *pxmitpriv)
+{
+ /*
+ Please remember to use all the osdep_service api,
+ and lock/unlock or _enter/_exit critical to protect
+ pfree_xmit_queue
+ */
+ unsigned long irqL;
+ struct xmit_frame *pxframe = NULL;
+ struct list_head *plist, *phead;
+ struct __queue *pfree_xmit_queue = &pxmitpriv->free_xmit_queue;
+
+ spin_lock_irqsave(&pfree_xmit_queue->lock, irqL);
+ if (list_empty(&pfree_xmit_queue->queue))
+ pxframe = NULL;
+ else {
+ phead = &pfree_xmit_queue->queue;
+ plist = phead->next;
+ pxframe = LIST_CONTAINOR(plist, struct xmit_frame, list);
+ list_del_init(&(pxframe->list));
+ }
+ if (pxframe != NULL) {
+ pxmitpriv->free_xmitframe_cnt--;
+ pxframe->buf_addr = NULL;
+ pxframe->pxmitbuf = NULL;
+ pxframe->attrib.psta = NULL;
+ pxframe->pkt = NULL;
+ }
+ spin_unlock_irqrestore(&pfree_xmit_queue->lock, irqL);
+ return pxframe;
+}
+
+void r8712_free_xmitframe(struct xmit_priv *pxmitpriv,
+ struct xmit_frame *pxmitframe)
+{
+ unsigned long irqL;
+ struct __queue *pfree_xmit_queue = &pxmitpriv->free_xmit_queue;
+ struct _adapter *padapter = pxmitpriv->adapter;
+
+ if (pxmitframe == NULL)
+ return;
+ spin_lock_irqsave(&pfree_xmit_queue->lock, irqL);
+ list_del_init(&pxmitframe->list);
+ if (pxmitframe->pkt)
+ pxmitframe->pkt = NULL;
+ list_add_tail(&pxmitframe->list, &pfree_xmit_queue->queue);
+ pxmitpriv->free_xmitframe_cnt++;
+ spin_unlock_irqrestore(&pfree_xmit_queue->lock, irqL);
+ if (netif_queue_stopped(padapter->pnetdev))
+ netif_wake_queue(padapter->pnetdev);
+}
+
+void r8712_free_xmitframe_ex(struct xmit_priv *pxmitpriv,
+ struct xmit_frame *pxmitframe)
+{
+ if (pxmitframe == NULL)
+ return;
+ if (pxmitframe->frame_tag == DATA_FRAMETAG)
+ r8712_free_xmitframe(pxmitpriv, pxmitframe);
+}
+
+void r8712_free_xmitframe_queue(struct xmit_priv *pxmitpriv,
+ struct __queue *pframequeue)
+{
+ unsigned long irqL;
+ struct list_head *plist, *phead;
+ struct xmit_frame *pxmitframe;
+
+ spin_lock_irqsave(&(pframequeue->lock), irqL);
+ phead = &pframequeue->queue;
+ plist = phead->next;
+ while (end_of_queue_search(phead, plist) == false) {
+ pxmitframe = LIST_CONTAINOR(plist, struct xmit_frame, list);
+ plist = plist->next;
+ r8712_free_xmitframe(pxmitpriv, pxmitframe);
+ }
+ spin_unlock_irqrestore(&(pframequeue->lock), irqL);
+}
+
+static inline struct tx_servq *get_sta_pending(struct _adapter *padapter,
+ struct __queue **ppstapending,
+ struct sta_info *psta, sint up)
+{
+
+ struct tx_servq *ptxservq;
+ struct hw_xmit *phwxmits = padapter->xmitpriv.hwxmits;
+
+ switch (up) {
+ case 1:
+ case 2:
+ ptxservq = &(psta->sta_xmitpriv.bk_q);
+ *ppstapending = &padapter->xmitpriv.bk_pending;
+ (phwxmits+3)->accnt++;
+ break;
+ case 4:
+ case 5:
+ ptxservq = &(psta->sta_xmitpriv.vi_q);
+ *ppstapending = &padapter->xmitpriv.vi_pending;
+ (phwxmits+1)->accnt++;
+ break;
+ case 6:
+ case 7:
+ ptxservq = &(psta->sta_xmitpriv.vo_q);
+ *ppstapending = &padapter->xmitpriv.vo_pending;
+ (phwxmits+0)->accnt++;
+ break;
+ case 0:
+ case 3:
+ default:
+ ptxservq = &(psta->sta_xmitpriv.be_q);
+ *ppstapending = &padapter->xmitpriv.be_pending;
+ (phwxmits + 2)->accnt++;
+ break;
+ }
+ return ptxservq;
+}
+
+/*
+ * Will enqueue pxmitframe to the proper queue, and indicate it
+ * to xx_pending list.....
+ */
+sint r8712_xmit_classifier(struct _adapter *padapter,
+ struct xmit_frame *pxmitframe)
+{
+ unsigned long irqL0;
+ struct __queue *pstapending;
+ struct sta_info *psta;
+ struct tx_servq *ptxservq;
+ struct pkt_attrib *pattrib = &pxmitframe->attrib;
+ struct sta_priv *pstapriv = &padapter->stapriv;
+ struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
+ sint bmcst = IS_MCAST(pattrib->ra);
+
+ if (pattrib->psta)
+ psta = pattrib->psta;
+ else {
+ if (bmcst)
+ psta = r8712_get_bcmc_stainfo(padapter);
+ else {
+ if (check_fwstate(pmlmepriv, WIFI_MP_STATE) == true)
+ psta = r8712_get_stainfo(pstapriv,
+ get_bssid(pmlmepriv));
+ else
+ psta = r8712_get_stainfo(pstapriv, pattrib->ra);
+ }
+ }
+ if (psta == NULL)
+ return _FAIL;
+ ptxservq = get_sta_pending(padapter, &pstapending,
+ psta, pattrib->priority);
+ spin_lock_irqsave(&pstapending->lock, irqL0);
+ if (list_empty(&ptxservq->tx_pending))
+ list_add_tail(&ptxservq->tx_pending, &pstapending->queue);
+ list_add_tail(&pxmitframe->list, &ptxservq->sta_pending.queue);
+ ptxservq->qcnt++;
+ spin_unlock_irqrestore(&pstapending->lock, irqL0);
+ return _SUCCESS;
+}
+
+static void alloc_hwxmits(struct _adapter *padapter)
+{
+ struct hw_xmit *hwxmits;
+ struct xmit_priv *pxmitpriv = &padapter->xmitpriv;
+
+ pxmitpriv->hwxmit_entry = HWXMIT_ENTRY;
+ pxmitpriv->hwxmits = kmalloc_array(pxmitpriv->hwxmit_entry,
+ sizeof(struct hw_xmit), GFP_ATOMIC);
+ if (pxmitpriv->hwxmits == NULL)
+ return;
+ hwxmits = pxmitpriv->hwxmits;
+ if (pxmitpriv->hwxmit_entry == 5) {
+ pxmitpriv->bmc_txqueue.head = 0;
+ hwxmits[0] .phwtxqueue = &pxmitpriv->bmc_txqueue;
+ hwxmits[0] .sta_queue = &pxmitpriv->bm_pending;
+ pxmitpriv->vo_txqueue.head = 0;
+ hwxmits[1] .phwtxqueue = &pxmitpriv->vo_txqueue;
+ hwxmits[1] .sta_queue = &pxmitpriv->vo_pending;
+ pxmitpriv->vi_txqueue.head = 0;
+ hwxmits[2] .phwtxqueue = &pxmitpriv->vi_txqueue;
+ hwxmits[2] .sta_queue = &pxmitpriv->vi_pending;
+ pxmitpriv->bk_txqueue.head = 0;
+ hwxmits[3] .phwtxqueue = &pxmitpriv->bk_txqueue;
+ hwxmits[3] .sta_queue = &pxmitpriv->bk_pending;
+ pxmitpriv->be_txqueue.head = 0;
+ hwxmits[4] .phwtxqueue = &pxmitpriv->be_txqueue;
+ hwxmits[4] .sta_queue = &pxmitpriv->be_pending;
+ } else if (pxmitpriv->hwxmit_entry == 4) {
+ pxmitpriv->vo_txqueue.head = 0;
+ hwxmits[0] .phwtxqueue = &pxmitpriv->vo_txqueue;
+ hwxmits[0] .sta_queue = &pxmitpriv->vo_pending;
+ pxmitpriv->vi_txqueue.head = 0;
+ hwxmits[1] .phwtxqueue = &pxmitpriv->vi_txqueue;
+ hwxmits[1] .sta_queue = &pxmitpriv->vi_pending;
+ pxmitpriv->be_txqueue.head = 0;
+ hwxmits[2] .phwtxqueue = &pxmitpriv->be_txqueue;
+ hwxmits[2] .sta_queue = &pxmitpriv->be_pending;
+ pxmitpriv->bk_txqueue.head = 0;
+ hwxmits[3] .phwtxqueue = &pxmitpriv->bk_txqueue;
+ hwxmits[3] .sta_queue = &pxmitpriv->bk_pending;
+ }
+}
+
+static void free_hwxmits(struct _adapter *padapter)
+{
+ struct xmit_priv *pxmitpriv = &padapter->xmitpriv;
+
+ kfree(pxmitpriv->hwxmits);
+}
+
+static void init_hwxmits(struct hw_xmit *phwxmit, sint entry)
+{
+ sint i;
+
+ for (i = 0; i < entry; i++, phwxmit++) {
+ spin_lock_init(&phwxmit->xmit_lock);
+ INIT_LIST_HEAD(&phwxmit->pending);
+ phwxmit->txcmdcnt = 0;
+ phwxmit->accnt = 0;
+ }
+}
+
+void xmitframe_xmitbuf_attach(struct xmit_frame *pxmitframe,
+ struct xmit_buf *pxmitbuf)
+{
+ /* pxmitbuf attach to pxmitframe */
+ pxmitframe->pxmitbuf = pxmitbuf;
+ /* urb and irp connection */
+ pxmitframe->pxmit_urb[0] = pxmitbuf->pxmit_urb[0];
+ /* buffer addr assoc */
+ pxmitframe->buf_addr = pxmitbuf->pbuf;
+ /* pxmitframe attach to pxmitbuf */
+ pxmitbuf->priv_data = pxmitframe;
+}
+
+/*
+ * tx_action == 0 == no frames to transmit
+ * tx_action > 0 ==> we have frames to transmit
+ * tx_action < 0 ==> we have frames to transmit, but TXFF is not even enough
+ * to transmit 1 frame.
+ */
+
+int r8712_pre_xmit(struct _adapter *padapter, struct xmit_frame *pxmitframe)
+{
+ unsigned long irqL;
+ int ret;
+ struct xmit_buf *pxmitbuf = NULL;
+ struct xmit_priv *pxmitpriv = &padapter->xmitpriv;
+ struct pkt_attrib *pattrib = &pxmitframe->attrib;
+
+ r8712_do_queue_select(padapter, pattrib);
+ spin_lock_irqsave(&pxmitpriv->lock, irqL);
+ if (r8712_txframes_sta_ac_pending(padapter, pattrib) > 0) {
+ ret = false;
+ r8712_xmit_enqueue(padapter, pxmitframe);
+ spin_unlock_irqrestore(&pxmitpriv->lock, irqL);
+ return ret;
+ }
+ pxmitbuf = r8712_alloc_xmitbuf(pxmitpriv);
+ if (pxmitbuf == NULL) { /*enqueue packet*/
+ ret = false;
+ r8712_xmit_enqueue(padapter, pxmitframe);
+ spin_unlock_irqrestore(&pxmitpriv->lock, irqL);
+ } else { /*dump packet directly*/
+ spin_unlock_irqrestore(&pxmitpriv->lock, irqL);
+ ret = true;
+ xmitframe_xmitbuf_attach(pxmitframe, pxmitbuf);
+ r8712_xmit_direct(padapter, pxmitframe);
+ }
+ return ret;
+}
diff --git a/drivers/staging/rtl8712/rtl871x_xmit.h b/drivers/staging/rtl8712/rtl871x_xmit.h
new file mode 100644
index 000000000..a9633c3f7
--- /dev/null
+++ b/drivers/staging/rtl8712/rtl871x_xmit.h
@@ -0,0 +1,302 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2010 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ * Modifications for inclusion into the Linux staging tree are
+ * Copyright(c) 2010 Larry Finger. All rights reserved.
+ *
+ * Contact information:
+ * WLAN FAE <wlanfae@realtek.com>
+ * Larry Finger <Larry.Finger@lwfinger.net>
+ *
+ ******************************************************************************/
+#ifndef _RTL871X_XMIT_H_
+#define _RTL871X_XMIT_H_
+
+#include "osdep_service.h"
+#include "drv_types.h"
+#include "xmit_osdep.h"
+
+#ifdef CONFIG_R8712_TX_AGGR
+#define MAX_XMITBUF_SZ (16384)
+#else
+#define MAX_XMITBUF_SZ (2048)
+#endif
+
+#define NR_XMITBUFF (4)
+
+#ifdef CONFIG_R8712_TX_AGGR
+#define AGGR_NR_HIGH_BOUND (4) /*(8) */
+#define AGGR_NR_LOW_BOUND (2)
+#endif
+
+#define XMITBUF_ALIGN_SZ 512
+#define TX_GUARD_BAND 5
+#define MAX_NUMBLKS (1)
+
+/* Fixed the Big Endian bug when using the software driver encryption.*/
+#define WEP_IV(pattrib_iv, txpn, keyidx)\
+do { \
+ pattrib_iv[0] = txpn._byte_.TSC0;\
+ pattrib_iv[1] = txpn._byte_.TSC1;\
+ pattrib_iv[2] = txpn._byte_.TSC2;\
+ pattrib_iv[3] = ((keyidx & 0x3)<<6);\
+ txpn.val = (txpn.val == 0xffffff) ? 0 : (txpn.val+1);\
+} while (0)
+
+/* Fixed the Big Endian bug when doing the Tx.
+ * The Linksys WRH54G will check this.*/
+#define TKIP_IV(pattrib_iv, txpn, keyidx)\
+do { \
+ pattrib_iv[0] = txpn._byte_.TSC1;\
+ pattrib_iv[1] = (txpn._byte_.TSC1 | 0x20) & 0x7f;\
+ pattrib_iv[2] = txpn._byte_.TSC0;\
+ pattrib_iv[3] = BIT(5) | ((keyidx & 0x3)<<6);\
+ pattrib_iv[4] = txpn._byte_.TSC2;\
+ pattrib_iv[5] = txpn._byte_.TSC3;\
+ pattrib_iv[6] = txpn._byte_.TSC4;\
+ pattrib_iv[7] = txpn._byte_.TSC5;\
+ txpn.val = txpn.val == 0xffffffffffffULL ? 0 : \
+ (txpn.val+1);\
+} while (0)
+
+#define AES_IV(pattrib_iv, txpn, keyidx)\
+do { \
+ pattrib_iv[0] = txpn._byte_.TSC0;\
+ pattrib_iv[1] = txpn._byte_.TSC1;\
+ pattrib_iv[2] = 0;\
+ pattrib_iv[3] = BIT(5) | ((keyidx & 0x3)<<6);\
+ pattrib_iv[4] = txpn._byte_.TSC2;\
+ pattrib_iv[5] = txpn._byte_.TSC3;\
+ pattrib_iv[6] = txpn._byte_.TSC4;\
+ pattrib_iv[7] = txpn._byte_.TSC5;\
+ txpn.val = txpn.val == 0xffffffffffffULL ? 0 : \
+ (txpn.val+1);\
+} while (0)
+
+struct hw_xmit {
+ spinlock_t xmit_lock;
+ struct list_head pending;
+ struct __queue *sta_queue;
+ struct hw_txqueue *phwtxqueue;
+ sint txcmdcnt;
+ int accnt;
+};
+
+struct pkt_attrib {
+ u8 type;
+ u8 subtype;
+ u8 bswenc;
+ u8 dhcp_pkt;
+
+ u16 seqnum;
+ u16 ether_type;
+ u16 pktlen; /* the original 802.3 pkt raw_data len
+ * (not include ether_hdr data) */
+ u16 last_txcmdsz;
+
+ u8 pkt_hdrlen; /*the original 802.3 pkt header len*/
+ u8 hdrlen; /*the WLAN Header Len*/
+ u8 nr_frags;
+ u8 ack_policy;
+ u8 mac_id;
+ u8 vcs_mode; /*virtual carrier sense method*/
+ u8 pctrl;/*per packet txdesc control enable*/
+ u8 qsel;
+
+ u8 priority;
+ u8 encrypt; /* when 0 indicate no encrypt. when non-zero,
+ * indicate the encrypt algorithm*/
+ u8 iv_len;
+ u8 icv_len;
+ unsigned char iv[8];
+ unsigned char icv[8];
+ u8 dst[ETH_ALEN];
+ u8 src[ETH_ALEN];
+ u8 ta[ETH_ALEN];
+ u8 ra[ETH_ALEN];
+ struct sta_info *psta;
+};
+
+#define WLANHDR_OFFSET 64
+#define DATA_FRAMETAG 0x01
+#define L2_FRAMETAG 0x02
+#define MGNT_FRAMETAG 0x03
+#define AMSDU_FRAMETAG 0x04
+#define EII_FRAMETAG 0x05
+#define IEEE8023_FRAMETAG 0x06
+#define MP_FRAMETAG 0x07
+#define TXAGG_FRAMETAG 0x08
+
+struct xmit_buf {
+ struct list_head list;
+
+ u8 *pallocated_buf;
+ u8 *pbuf;
+ void *priv_data;
+ struct urb *pxmit_urb[8];
+ u32 aggr_nr;
+};
+
+struct xmit_frame {
+ struct list_head list;
+ struct pkt_attrib attrib;
+ _pkt *pkt;
+ int frame_tag;
+ struct _adapter *padapter;
+ u8 *buf_addr;
+ struct xmit_buf *pxmitbuf;
+ u8 *mem_addr;
+ u16 sz[8];
+ struct urb *pxmit_urb[8];
+ u8 bpending[8];
+ u8 last[8];
+};
+
+struct tx_servq {
+ struct list_head tx_pending;
+ struct __queue sta_pending;
+ int qcnt;
+};
+
+struct sta_xmit_priv {
+ spinlock_t lock;
+ sint option;
+ sint apsd_setting; /* When bit mask is on, the associated edca
+ * queue supports APSD.*/
+ struct tx_servq be_q; /* priority == 0,3 */
+ struct tx_servq bk_q; /* priority == 1,2*/
+ struct tx_servq vi_q; /*priority == 4,5*/
+ struct tx_servq vo_q; /*priority == 6,7*/
+ struct list_head legacy_dz;
+ struct list_head apsd;
+ u16 txseq_tid[16];
+ uint sta_tx_bytes;
+ u64 sta_tx_pkts;
+ uint sta_tx_fail;
+};
+
+struct hw_txqueue {
+ /*volatile*/ sint head;
+ /*volatile*/ sint tail;
+ /*volatile*/ sint free_sz; /*in units of 64 bytes*/
+ /*volatile*/ sint free_cmdsz;
+ /*volatile*/ sint txsz[8];
+ uint ff_hwaddr;
+ uint cmd_hwaddr;
+ sint ac_tag;
+};
+
+struct xmit_priv {
+ spinlock_t lock;
+ struct __queue be_pending;
+ struct __queue bk_pending;
+ struct __queue vi_pending;
+ struct __queue vo_pending;
+ struct __queue bm_pending;
+ struct __queue legacy_dz_queue;
+ struct __queue apsd_queue;
+ u8 *pallocated_frame_buf;
+ u8 *pxmit_frame_buf;
+ uint free_xmitframe_cnt;
+ uint mapping_addr;
+ uint pkt_sz;
+ struct __queue free_xmit_queue;
+ struct hw_txqueue be_txqueue;
+ struct hw_txqueue bk_txqueue;
+ struct hw_txqueue vi_txqueue;
+ struct hw_txqueue vo_txqueue;
+ struct hw_txqueue bmc_txqueue;
+ uint frag_len;
+ struct _adapter *adapter;
+ u8 vcs_setting;
+ u8 vcs;
+ u8 vcs_type;
+ u16 rts_thresh;
+ uint tx_bytes;
+ u64 tx_pkts;
+ uint tx_drop;
+ struct hw_xmit *hwxmits;
+ u8 hwxmit_entry;
+ u8 txirp_cnt;
+ struct tasklet_struct xmit_tasklet;
+ struct work_struct xmit_pipe4_reset_wi;
+ struct work_struct xmit_pipe6_reset_wi;
+ struct work_struct xmit_piped_reset_wi;
+ /*per AC pending irp*/
+ int beq_cnt;
+ int bkq_cnt;
+ int viq_cnt;
+ int voq_cnt;
+ struct __queue free_amsdu_xmit_queue;
+ u8 *pallocated_amsdu_frame_buf;
+ u8 *pxmit_amsdu_frame_buf;
+ uint free_amsdu_xmitframe_cnt;
+ struct __queue free_txagg_xmit_queue;
+ u8 *pallocated_txagg_frame_buf;
+ u8 *pxmit_txagg_frame_buf;
+ uint free_txagg_xmitframe_cnt;
+ int cmdseq;
+ struct __queue free_xmitbuf_queue;
+ struct __queue pending_xmitbuf_queue;
+ u8 *pallocated_xmitbuf;
+ u8 *pxmitbuf;
+ uint free_xmitbuf_cnt;
+};
+
+static inline struct __queue *get_free_xmit_queue(
+ struct xmit_priv *pxmitpriv)
+{
+ return &(pxmitpriv->free_xmit_queue);
+}
+
+int r8712_free_xmitbuf(struct xmit_priv *pxmitpriv,
+ struct xmit_buf *pxmitbuf);
+struct xmit_buf *r8712_alloc_xmitbuf(struct xmit_priv *pxmitpriv);
+void r8712_update_protection(struct _adapter *padapter, u8 *ie, uint ie_len);
+struct xmit_frame *r8712_alloc_xmitframe(struct xmit_priv *pxmitpriv);
+void r8712_free_xmitframe(struct xmit_priv *pxmitpriv,
+ struct xmit_frame *pxmitframe);
+void r8712_free_xmitframe_queue(struct xmit_priv *pxmitpriv,
+ struct __queue *pframequeue);
+sint r8712_xmit_classifier(struct _adapter *padapter,
+ struct xmit_frame *pxmitframe);
+sint r8712_xmitframe_coalesce(struct _adapter *padapter, _pkt *pkt,
+ struct xmit_frame *pxmitframe);
+sint _r8712_init_hw_txqueue(struct hw_txqueue *phw_txqueue, u8 ac_tag);
+void _r8712_init_sta_xmit_priv(struct sta_xmit_priv *psta_xmitpriv);
+sint r8712_update_attrib(struct _adapter *padapter, _pkt *pkt,
+ struct pkt_attrib *pattrib);
+int r8712_txframes_sta_ac_pending(struct _adapter *padapter,
+ struct pkt_attrib *pattrib);
+sint _r8712_init_xmit_priv(struct xmit_priv *pxmitpriv,
+ struct _adapter *padapter);
+void _free_xmit_priv(struct xmit_priv *pxmitpriv);
+void r8712_free_xmitframe_ex(struct xmit_priv *pxmitpriv,
+ struct xmit_frame *pxmitframe);
+int r8712_pre_xmit(struct _adapter *padapter, struct xmit_frame *pxmitframe);
+int r8712_xmit_enqueue(struct _adapter *padapter,
+ struct xmit_frame *pxmitframe);
+int r8712_xmit_direct(struct _adapter *padapter, struct xmit_frame *pxmitframe);
+void r8712_xmit_bh(void *priv);
+
+void xmitframe_xmitbuf_attach(struct xmit_frame *pxmitframe,
+ struct xmit_buf *pxmitbuf);
+
+#include "rtl8712_xmit.h"
+
+#endif /*_RTL871X_XMIT_H_*/
+
diff --git a/drivers/staging/rtl8712/sta_info.h b/drivers/staging/rtl8712/sta_info.h
new file mode 100644
index 000000000..742dfa0ca
--- /dev/null
+++ b/drivers/staging/rtl8712/sta_info.h
@@ -0,0 +1,146 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2010 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ * Modifications for inclusion into the Linux staging tree are
+ * Copyright(c) 2010 Larry Finger. All rights reserved.
+ *
+ * Contact information:
+ * WLAN FAE <wlanfae@realtek.com>
+ * Larry Finger <Larry.Finger@lwfinger.net>
+ *
+ ******************************************************************************/
+#ifndef __STA_INFO_H_
+#define __STA_INFO_H_
+
+#include "osdep_service.h"
+#include "drv_types.h"
+#include "wifi.h"
+
+#define NUM_STA 32
+#define NUM_ACL 64
+
+
+/* if mode ==0, then the sta is allowed once the addr is hit.
+ * if mode ==1, then the sta is rejected once the addr is non-hit.
+ */
+struct wlan_acl_node {
+ struct list_head list;
+ u8 addr[ETH_ALEN];
+ u8 mode;
+};
+
+struct wlan_acl_pool {
+ struct wlan_acl_node aclnode[NUM_ACL];
+};
+
+struct stainfo_stats {
+
+ uint rx_pkts;
+ uint rx_bytes;
+ u64 tx_pkts;
+ uint tx_bytes;
+};
+
+struct sta_info {
+ spinlock_t lock;
+ struct list_head list; /*free_sta_queue*/
+ struct list_head hash_list; /*sta_hash*/
+ struct sta_xmit_priv sta_xmitpriv;
+ struct sta_recv_priv sta_recvpriv;
+ uint state;
+ uint aid;
+ uint mac_id;
+ uint qos_option;
+ u8 hwaddr[ETH_ALEN];
+ uint ieee8021x_blocked; /*0: allowed, 1:blocked */
+ uint XPrivacy; /*aes, tkip...*/
+ union Keytype tkiptxmickey;
+ union Keytype tkiprxmickey;
+ union Keytype x_UncstKey;
+ union pn48 txpn; /* PN48 used for Unicast xmit.*/
+ union pn48 rxpn; /* PN48 used for Unicast recv.*/
+ u8 bssrateset[16];
+ uint bssratelen;
+ s32 rssi;
+ s32 signal_quality;
+ struct stainfo_stats sta_stats;
+ /*for A-MPDU Rx reordering buffer control */
+ struct recv_reorder_ctrl recvreorder_ctrl[16];
+ struct ht_priv htpriv;
+ /* Notes:
+ * STA_Mode:
+ * curr_network(mlme_priv/security_priv/qos/ht)
+ * + sta_info: (STA & AP) CAP/INFO
+ * scan_q: AP CAP/INFO
+ * AP_Mode:
+ * curr_network(mlme_priv/security_priv/qos/ht) : AP CAP/INFO
+ * sta_info: (AP & STA) CAP/INFO
+ */
+ struct list_head asoc_list;
+ struct list_head auth_list;
+ unsigned int expire_to;
+ unsigned int auth_seq;
+ unsigned int authalg;
+ unsigned char chg_txt[128];
+ unsigned int tx_ra_bitmap;
+};
+
+struct sta_priv {
+ u8 *pallocated_stainfo_buf;
+ u8 *pstainfo_buf;
+ struct __queue free_sta_queue;
+ spinlock_t sta_hash_lock;
+ struct list_head sta_hash[NUM_STA];
+ int asoc_sta_count;
+ struct __queue sleep_q;
+ struct __queue wakeup_q;
+ struct _adapter *padapter;
+ struct list_head asoc_list;
+ struct list_head auth_list;
+ unsigned int auth_to; /* sec, time to expire in authenticating. */
+ unsigned int assoc_to; /* sec, time to expire before associating. */
+ unsigned int expire_to; /* sec , time to expire after associated. */
+};
+
+static inline u32 wifi_mac_hash(u8 *mac)
+{
+ u32 x;
+
+ x = mac[0];
+ x = (x << 2) ^ mac[1];
+ x = (x << 2) ^ mac[2];
+ x = (x << 2) ^ mac[3];
+ x = (x << 2) ^ mac[4];
+ x = (x << 2) ^ mac[5];
+ x ^= x >> 8;
+ x = x & (NUM_STA - 1);
+ return x;
+}
+
+u32 _r8712_init_sta_priv(struct sta_priv *pstapriv);
+u32 _r8712_free_sta_priv(struct sta_priv *pstapriv);
+struct sta_info *r8712_alloc_stainfo(struct sta_priv *pstapriv,
+ u8 *hwaddr);
+void r8712_free_stainfo(struct _adapter *padapter, struct sta_info *psta);
+void r8712_free_all_stainfo(struct _adapter *padapter);
+struct sta_info *r8712_get_stainfo(struct sta_priv *pstapriv, u8 *hwaddr);
+void r8712_init_bcmc_stainfo(struct _adapter *padapter);
+struct sta_info *r8712_get_bcmc_stainfo(struct _adapter *padapter);
+u8 r8712_access_ctrl(struct wlan_acl_pool *pacl_list, u8 *mac_addr);
+
+#endif /* _STA_INFO_H_ */
+
diff --git a/drivers/staging/rtl8712/usb_halinit.c b/drivers/staging/rtl8712/usb_halinit.c
new file mode 100644
index 000000000..b4ae11a78
--- /dev/null
+++ b/drivers/staging/rtl8712/usb_halinit.c
@@ -0,0 +1,317 @@
+/******************************************************************************
+ * usb_halinit.c
+ *
+ * Copyright(c) 2007 - 2010 Realtek Corporation. All rights reserved.
+ * Linux device driver for RTL8192SU
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ * Modifications for inclusion into the Linux staging tree are
+ * Copyright(c) 2010 Larry Finger. All rights reserved.
+ *
+ * Contact information:
+ * WLAN FAE <wlanfae@realtek.com>
+ * Larry Finger <Larry.Finger@lwfinger.net>
+ *
+ ******************************************************************************/
+
+#define _HCI_HAL_INIT_C_
+
+#include "osdep_service.h"
+#include "drv_types.h"
+#include "usb_ops.h"
+#include "usb_osintf.h"
+
+u8 r8712_usb_hal_bus_init(struct _adapter *padapter)
+{
+ u8 val8 = 0;
+ u8 ret = _SUCCESS;
+ int PollingCnt = 20;
+ struct registry_priv *pregistrypriv = &padapter->registrypriv;
+
+ if (pregistrypriv->chip_version == RTL8712_FPGA) {
+ val8 = 0x01;
+ /* switch to 80M clock */
+ r8712_write8(padapter, SYS_CLKR, val8);
+ val8 = r8712_read8(padapter, SPS1_CTRL);
+ val8 = val8 | 0x01;
+ /* enable VSPS12 LDO Macro block */
+ r8712_write8(padapter, SPS1_CTRL, val8);
+ val8 = r8712_read8(padapter, AFE_MISC);
+ val8 = val8 | 0x01;
+ /* Enable AFE Macro Block's Bandgap */
+ r8712_write8(padapter, AFE_MISC, val8);
+ val8 = r8712_read8(padapter, LDOA15_CTRL);
+ val8 = val8 | 0x01;
+ /* enable LDOA15 block */
+ r8712_write8(padapter, LDOA15_CTRL, val8);
+ val8 = r8712_read8(padapter, SPS1_CTRL);
+ val8 = val8 | 0x02;
+ /* Enable VSPS12_SW Macro Block */
+ r8712_write8(padapter, SPS1_CTRL, val8);
+ val8 = r8712_read8(padapter, AFE_MISC);
+ val8 = val8 | 0x02;
+ /* Enable AFE Macro Block's Mbias */
+ r8712_write8(padapter, AFE_MISC, val8);
+ val8 = r8712_read8(padapter, SYS_ISO_CTRL + 1);
+ val8 = val8 | 0x08;
+ /* isolate PCIe Analog 1.2V to PCIe 3.3V and PCIE Digital */
+ r8712_write8(padapter, SYS_ISO_CTRL + 1, val8);
+ val8 = r8712_read8(padapter, SYS_ISO_CTRL + 1);
+ val8 = val8 & 0xEF;
+ /* attatch AFE PLL to MACTOP/BB/PCIe Digital */
+ r8712_write8(padapter, SYS_ISO_CTRL + 1, val8);
+ val8 = r8712_read8(padapter, AFE_XTAL_CTRL + 1);
+ val8 = val8 & 0xFB;
+ /* enable AFE clock */
+ r8712_write8(padapter, AFE_XTAL_CTRL + 1, val8);
+ val8 = r8712_read8(padapter, AFE_PLL_CTRL);
+ val8 = val8 | 0x01;
+ /* Enable AFE PLL Macro Block */
+ r8712_write8(padapter, AFE_PLL_CTRL, val8);
+ val8 = 0xEE;
+ /* release isolation AFE PLL & MD */
+ r8712_write8(padapter, SYS_ISO_CTRL, val8);
+ val8 = r8712_read8(padapter, SYS_CLKR + 1);
+ val8 = val8 | 0x08;
+ /* enable MAC clock */
+ r8712_write8(padapter, SYS_CLKR + 1, val8);
+ val8 = r8712_read8(padapter, SYS_FUNC_EN + 1);
+ val8 = val8 | 0x08;
+ /* enable Core digital and enable IOREG R/W */
+ r8712_write8(padapter, SYS_FUNC_EN + 1, val8);
+ val8 = val8 | 0x80;
+ /* enable REG_EN */
+ r8712_write8(padapter, SYS_FUNC_EN + 1, val8);
+ val8 = r8712_read8(padapter, SYS_CLKR + 1);
+ val8 = (val8 | 0x80) & 0xBF;
+ /* switch the control path */
+ r8712_write8(padapter, SYS_CLKR + 1, val8);
+ val8 = 0xFC;
+ r8712_write8(padapter, CR, val8);
+ val8 = 0x37;
+ r8712_write8(padapter, CR + 1, val8);
+ /* reduce EndPoint & init it */
+ r8712_write8(padapter, 0x102500ab, r8712_read8(padapter,
+ 0x102500ab) | BIT(6) | BIT(7));
+ /* consideration of power consumption - init */
+ r8712_write8(padapter, 0x10250008, r8712_read8(padapter,
+ 0x10250008) & 0xfffffffb);
+ } else if (pregistrypriv->chip_version == RTL8712_1stCUT) {
+ /* Initialization for power on sequence, */
+ r8712_write8(padapter, SPS0_CTRL + 1, 0x53);
+ r8712_write8(padapter, SPS0_CTRL, 0x57);
+ /* Enable AFE Macro Block's Bandgap and Enable AFE Macro
+ * Block's Mbias
+ */
+ val8 = r8712_read8(padapter, AFE_MISC);
+ r8712_write8(padapter, AFE_MISC, (val8 | AFE_MISC_BGEN |
+ AFE_MISC_MBEN));
+ /* Enable LDOA15 block */
+ val8 = r8712_read8(padapter, LDOA15_CTRL);
+ r8712_write8(padapter, LDOA15_CTRL, (val8 | LDA15_EN));
+ val8 = r8712_read8(padapter, SPS1_CTRL);
+ r8712_write8(padapter, SPS1_CTRL, (val8 | SPS1_LDEN));
+ msleep(20);
+ /* Enable Switch Regulator Block */
+ val8 = r8712_read8(padapter, SPS1_CTRL);
+ r8712_write8(padapter, SPS1_CTRL, (val8 | SPS1_SWEN));
+ r8712_write32(padapter, SPS1_CTRL, 0x00a7b267);
+ val8 = r8712_read8(padapter, SYS_ISO_CTRL + 1);
+ r8712_write8(padapter, SYS_ISO_CTRL + 1, (val8 | 0x08));
+ /* Engineer Packet CP test Enable */
+ val8 = r8712_read8(padapter, SYS_FUNC_EN + 1);
+ r8712_write8(padapter, SYS_FUNC_EN + 1, (val8 | 0x20));
+ val8 = r8712_read8(padapter, SYS_ISO_CTRL + 1);
+ r8712_write8(padapter, SYS_ISO_CTRL + 1, (val8 & 0x6F));
+ /* Enable AFE clock */
+ val8 = r8712_read8(padapter, AFE_XTAL_CTRL + 1);
+ r8712_write8(padapter, AFE_XTAL_CTRL + 1, (val8 & 0xfb));
+ /* Enable AFE PLL Macro Block */
+ val8 = r8712_read8(padapter, AFE_PLL_CTRL);
+ r8712_write8(padapter, AFE_PLL_CTRL, (val8 | 0x11));
+ /* Attach AFE PLL to MACTOP/BB/PCIe Digital */
+ val8 = r8712_read8(padapter, SYS_ISO_CTRL);
+ r8712_write8(padapter, SYS_ISO_CTRL, (val8 & 0xEE));
+ /* Switch to 40M clock */
+ val8 = r8712_read8(padapter, SYS_CLKR);
+ r8712_write8(padapter, SYS_CLKR, val8 & (~SYS_CLKSEL));
+ /* SSC Disable */
+ val8 = r8712_read8(padapter, SYS_CLKR);
+ /* Enable MAC clock */
+ val8 = r8712_read8(padapter, SYS_CLKR + 1);
+ r8712_write8(padapter, SYS_CLKR + 1, (val8 | 0x18));
+ /* Revised POS, */
+ r8712_write8(padapter, PMC_FSM, 0x02);
+ /* Enable Core digital and enable IOREG R/W */
+ val8 = r8712_read8(padapter, SYS_FUNC_EN + 1);
+ r8712_write8(padapter, SYS_FUNC_EN + 1, (val8 | 0x08));
+ /* Enable REG_EN */
+ val8 = r8712_read8(padapter, SYS_FUNC_EN + 1);
+ r8712_write8(padapter, SYS_FUNC_EN + 1, (val8 | 0x80));
+ /* Switch the control path to FW */
+ val8 = r8712_read8(padapter, SYS_CLKR + 1);
+ r8712_write8(padapter, SYS_CLKR + 1, (val8 | 0x80) & 0xBF);
+ r8712_write8(padapter, CR, 0xFC);
+ r8712_write8(padapter, CR + 1, 0x37);
+ /* Fix the RX FIFO issue(usb error), */
+ val8 = r8712_read8(padapter, 0x1025FE5c);
+ r8712_write8(padapter, 0x1025FE5c, (val8|BIT(7)));
+ val8 = r8712_read8(padapter, 0x102500ab);
+ r8712_write8(padapter, 0x102500ab, (val8|BIT(6)|BIT(7)));
+ /* For power save, used this in the bit file after 970621 */
+ val8 = r8712_read8(padapter, SYS_CLKR);
+ r8712_write8(padapter, SYS_CLKR, val8&(~CPU_CLKSEL));
+ } else if (pregistrypriv->chip_version == RTL8712_2ndCUT ||
+ pregistrypriv->chip_version == RTL8712_3rdCUT) {
+ /* Initialization for power on sequence,
+ * E-Fuse leakage prevention sequence
+ */
+ r8712_write8(padapter, 0x37, 0xb0);
+ msleep(20);
+ r8712_write8(padapter, 0x37, 0x30);
+ /* Set control path switch to HW control and reset Digital Core,
+ * CPU Core and MAC I/O to solve FW download fail when system
+ * from resume sate.
+ */
+ val8 = r8712_read8(padapter, SYS_CLKR + 1);
+ if (val8 & 0x80) {
+ val8 &= 0x3f;
+ r8712_write8(padapter, SYS_CLKR + 1, val8);
+ }
+ val8 = r8712_read8(padapter, SYS_FUNC_EN + 1);
+ val8 &= 0x73;
+ r8712_write8(padapter, SYS_FUNC_EN + 1, val8);
+ msleep(20);
+ /* Revised POS, */
+ /* Enable AFE Macro Block's Bandgap and Enable AFE Macro
+ * Block's Mbias */
+ r8712_write8(padapter, SPS0_CTRL + 1, 0x53);
+ r8712_write8(padapter, SPS0_CTRL, 0x57);
+ val8 = r8712_read8(padapter, AFE_MISC);
+ /*Bandgap*/
+ r8712_write8(padapter, AFE_MISC, (val8 | AFE_MISC_BGEN));
+ r8712_write8(padapter, AFE_MISC, (val8 | AFE_MISC_BGEN |
+ AFE_MISC_MBEN | AFE_MISC_I32_EN));
+ /* Enable PLL Power (LDOA15V) */
+ val8 = r8712_read8(padapter, LDOA15_CTRL);
+ r8712_write8(padapter, LDOA15_CTRL, (val8 | LDA15_EN));
+ /* Enable LDOV12D block */
+ val8 = r8712_read8(padapter, LDOV12D_CTRL);
+ r8712_write8(padapter, LDOV12D_CTRL, (val8 | LDV12_EN));
+ val8 = r8712_read8(padapter, SYS_ISO_CTRL + 1);
+ r8712_write8(padapter, SYS_ISO_CTRL + 1, (val8 | 0x08));
+ /* Engineer Packet CP test Enable */
+ val8 = r8712_read8(padapter, SYS_FUNC_EN + 1);
+ r8712_write8(padapter, SYS_FUNC_EN + 1, (val8 | 0x20));
+ /* Support 64k IMEM */
+ val8 = r8712_read8(padapter, SYS_ISO_CTRL + 1);
+ r8712_write8(padapter, SYS_ISO_CTRL + 1, (val8 & 0x68));
+ /* Enable AFE clock */
+ val8 = r8712_read8(padapter, AFE_XTAL_CTRL + 1);
+ r8712_write8(padapter, AFE_XTAL_CTRL + 1, (val8 & 0xfb));
+ /* Enable AFE PLL Macro Block */
+ val8 = r8712_read8(padapter, AFE_PLL_CTRL);
+ r8712_write8(padapter, AFE_PLL_CTRL, (val8 | 0x11));
+ /* Some sample will download fw failure. The clock will be
+ * stable with 500 us delay after reset the PLL
+ * TODO: When usleep is added to kernel, change next 3
+ * udelay(500) to usleep(500)
+ */
+ udelay(500);
+ r8712_write8(padapter, AFE_PLL_CTRL, (val8 | 0x51));
+ udelay(500);
+ r8712_write8(padapter, AFE_PLL_CTRL, (val8 | 0x11));
+ udelay(500);
+ /* Attach AFE PLL to MACTOP/BB/PCIe Digital */
+ val8 = r8712_read8(padapter, SYS_ISO_CTRL);
+ r8712_write8(padapter, SYS_ISO_CTRL, (val8 & 0xEE));
+ /* Switch to 40M clock */
+ r8712_write8(padapter, SYS_CLKR, 0x00);
+ /* CPU Clock and 80M Clock SSC Disable to overcome FW download
+ * fail timing issue.
+ */
+ val8 = r8712_read8(padapter, SYS_CLKR);
+ r8712_write8(padapter, SYS_CLKR, (val8 | 0xa0));
+ /* Enable MAC clock */
+ val8 = r8712_read8(padapter, SYS_CLKR + 1);
+ r8712_write8(padapter, SYS_CLKR + 1, (val8 | 0x18));
+ /* Revised POS, */
+ r8712_write8(padapter, PMC_FSM, 0x02);
+ /* Enable Core digital and enable IOREG R/W */
+ val8 = r8712_read8(padapter, SYS_FUNC_EN + 1);
+ r8712_write8(padapter, SYS_FUNC_EN + 1, (val8 | 0x08));
+ /* Enable REG_EN */
+ val8 = r8712_read8(padapter, SYS_FUNC_EN + 1);
+ r8712_write8(padapter, SYS_FUNC_EN + 1, (val8 | 0x80));
+ /* Switch the control path to FW */
+ val8 = r8712_read8(padapter, SYS_CLKR + 1);
+ r8712_write8(padapter, SYS_CLKR + 1, (val8 | 0x80) & 0xBF);
+ r8712_write8(padapter, CR, 0xFC);
+ r8712_write8(padapter, CR + 1, 0x37);
+ /* Fix the RX FIFO issue(usb error), 970410 */
+ val8 = r8712_read8(padapter, 0x1025FE5c);
+ r8712_write8(padapter, 0x1025FE5c, (val8 | BIT(7)));
+ /* For power save, used this in the bit file after 970621 */
+ val8 = r8712_read8(padapter, SYS_CLKR);
+ r8712_write8(padapter, SYS_CLKR, val8 & (~CPU_CLKSEL));
+ /* Revised for 8051 ROM code wrong operation. */
+ r8712_write8(padapter, 0x1025fe1c, 0x80);
+ /* To make sure that TxDMA can ready to download FW.
+ * We should reset TxDMA if IMEM RPT was not ready.
+ */
+ do {
+ val8 = r8712_read8(padapter, TCR);
+ if ((val8 & _TXDMA_INIT_VALUE) == _TXDMA_INIT_VALUE)
+ break;
+ udelay(5); /* PlatformStallExecution(5); */
+ } while (PollingCnt--); /* Delay 1ms */
+
+ if (PollingCnt <= 0) {
+ val8 = r8712_read8(padapter, CR);
+ r8712_write8(padapter, CR, val8&(~_TXDMA_EN));
+ udelay(2); /* PlatformStallExecution(2); */
+ /* Reset TxDMA */
+ r8712_write8(padapter, CR, val8|_TXDMA_EN);
+ }
+ } else
+ ret = _FAIL;
+ return ret;
+}
+
+unsigned int r8712_usb_inirp_init(struct _adapter *padapter)
+{
+ u8 i;
+ struct recv_buf *precvbuf;
+ struct intf_hdl *pintfhdl = &padapter->pio_queue->intf;
+ struct recv_priv *precvpriv = &(padapter->recvpriv);
+
+ precvpriv->ff_hwaddr = RTL8712_DMA_RX0FF; /* mapping rx fifo address */
+ /* issue Rx irp to receive data */
+ precvbuf = (struct recv_buf *)precvpriv->precv_buf;
+ for (i = 0; i < NR_RECVBUFF; i++) {
+ if (r8712_usb_read_port(pintfhdl, precvpriv->ff_hwaddr, 0,
+ (unsigned char *)precvbuf) == false)
+ return _FAIL;
+ precvbuf++;
+ precvpriv->free_recv_buf_queue_cnt--;
+ }
+ return _SUCCESS;
+}
+
+unsigned int r8712_usb_inirp_deinit(struct _adapter *padapter)
+{
+ r8712_usb_read_port_cancel(padapter);
+ return _SUCCESS;
+}
diff --git a/drivers/staging/rtl8712/usb_intf.c b/drivers/staging/rtl8712/usb_intf.c
new file mode 100644
index 000000000..f8b5b332e
--- /dev/null
+++ b/drivers/staging/rtl8712/usb_intf.c
@@ -0,0 +1,648 @@
+/******************************************************************************
+ * usb_intf.c
+ *
+ * Copyright(c) 2007 - 2010 Realtek Corporation. All rights reserved.
+ * Linux device driver for RTL8192SU
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ * Modifications for inclusion into the Linux staging tree are
+ * Copyright(c) 2010 Larry Finger. All rights reserved.
+ *
+ * Contact information:
+ * WLAN FAE <wlanfae@realtek.com>
+ * Larry Finger <Larry.Finger@lwfinger.net>
+ *
+ ******************************************************************************/
+
+#define _HCI_INTF_C_
+
+#include <linux/usb.h>
+#include <linux/module.h>
+#include <linux/firmware.h>
+
+#include "osdep_service.h"
+#include "drv_types.h"
+#include "recv_osdep.h"
+#include "xmit_osdep.h"
+#include "rtl8712_efuse.h"
+#include "usb_ops.h"
+#include "usb_osintf.h"
+
+static struct usb_interface *pintf;
+
+static int r871xu_drv_init(struct usb_interface *pusb_intf,
+ const struct usb_device_id *pdid);
+
+static void r871xu_dev_remove(struct usb_interface *pusb_intf);
+
+static struct usb_device_id rtl871x_usb_id_tbl[] = {
+
+/* RTL8188SU */
+ /* Realtek */
+ {USB_DEVICE(0x0BDA, 0x8171)},
+ {USB_DEVICE(0x0bda, 0x8173)},
+ {USB_DEVICE(0x0bda, 0x8712)},
+ {USB_DEVICE(0x0bda, 0x8713)},
+ {USB_DEVICE(0x0bda, 0xC512)},
+ /* Abocom */
+ {USB_DEVICE(0x07B8, 0x8188)},
+ /* ASUS */
+ {USB_DEVICE(0x0B05, 0x1786)},
+ {USB_DEVICE(0x0B05, 0x1791)}, /* 11n mode disable */
+ /* Belkin */
+ {USB_DEVICE(0x050D, 0x945A)},
+ /* ISY IWL - Belkin clone */
+ {USB_DEVICE(0x050D, 0x11F1)},
+ /* Corega */
+ {USB_DEVICE(0x07AA, 0x0047)},
+ /* D-Link */
+ {USB_DEVICE(0x2001, 0x3306)},
+ {USB_DEVICE(0x07D1, 0x3306)}, /* 11n mode disable */
+ /* Edimax */
+ {USB_DEVICE(0x7392, 0x7611)},
+ /* EnGenius */
+ {USB_DEVICE(0x1740, 0x9603)},
+ /* Hawking */
+ {USB_DEVICE(0x0E66, 0x0016)},
+ /* Hercules */
+ {USB_DEVICE(0x06F8, 0xE034)},
+ {USB_DEVICE(0x06F8, 0xE032)},
+ /* Logitec */
+ {USB_DEVICE(0x0789, 0x0167)},
+ /* PCI */
+ {USB_DEVICE(0x2019, 0xAB28)},
+ {USB_DEVICE(0x2019, 0xED16)},
+ /* Sitecom */
+ {USB_DEVICE(0x0DF6, 0x0057)},
+ {USB_DEVICE(0x0DF6, 0x0045)},
+ {USB_DEVICE(0x0DF6, 0x0059)}, /* 11n mode disable */
+ {USB_DEVICE(0x0DF6, 0x004B)},
+ {USB_DEVICE(0x0DF6, 0x005B)},
+ {USB_DEVICE(0x0DF6, 0x005D)},
+ {USB_DEVICE(0x0DF6, 0x0063)},
+ /* Sweex */
+ {USB_DEVICE(0x177F, 0x0154)},
+ /* Thinkware */
+ {USB_DEVICE(0x0BDA, 0x5077)},
+ /* Toshiba */
+ {USB_DEVICE(0x1690, 0x0752)},
+ /* - */
+ {USB_DEVICE(0x20F4, 0x646B)},
+ {USB_DEVICE(0x083A, 0xC512)},
+ {USB_DEVICE(0x25D4, 0x4CA1)},
+ {USB_DEVICE(0x25D4, 0x4CAB)},
+
+/* RTL8191SU */
+ /* Realtek */
+ {USB_DEVICE(0x0BDA, 0x8172)},
+ {USB_DEVICE(0x0BDA, 0x8192)},
+ /* Amigo */
+ {USB_DEVICE(0x0EB0, 0x9061)},
+ /* ASUS/EKB */
+ {USB_DEVICE(0x13D3, 0x3323)},
+ {USB_DEVICE(0x13D3, 0x3311)}, /* 11n mode disable */
+ {USB_DEVICE(0x13D3, 0x3342)},
+ /* ASUS/EKBLenovo */
+ {USB_DEVICE(0x13D3, 0x3333)},
+ {USB_DEVICE(0x13D3, 0x3334)},
+ {USB_DEVICE(0x13D3, 0x3335)}, /* 11n mode disable */
+ {USB_DEVICE(0x13D3, 0x3336)}, /* 11n mode disable */
+ /* ASUS/Media BOX */
+ {USB_DEVICE(0x13D3, 0x3309)},
+ /* Belkin */
+ {USB_DEVICE(0x050D, 0x815F)},
+ /* D-Link */
+ {USB_DEVICE(0x07D1, 0x3302)},
+ {USB_DEVICE(0x07D1, 0x3300)},
+ {USB_DEVICE(0x07D1, 0x3303)},
+ /* Edimax */
+ {USB_DEVICE(0x7392, 0x7612)},
+ /* EnGenius */
+ {USB_DEVICE(0x1740, 0x9605)},
+ /* Guillemot */
+ {USB_DEVICE(0x06F8, 0xE031)},
+ /* Hawking */
+ {USB_DEVICE(0x0E66, 0x0015)},
+ /* Mediao */
+ {USB_DEVICE(0x13D3, 0x3306)},
+ /* PCI */
+ {USB_DEVICE(0x2019, 0xED18)},
+ {USB_DEVICE(0x2019, 0x4901)},
+ /* Sitecom */
+ {USB_DEVICE(0x0DF6, 0x0058)},
+ {USB_DEVICE(0x0DF6, 0x0049)},
+ {USB_DEVICE(0x0DF6, 0x004C)},
+ {USB_DEVICE(0x0DF6, 0x0064)},
+ /* Skyworth */
+ {USB_DEVICE(0x14b2, 0x3300)},
+ {USB_DEVICE(0x14b2, 0x3301)},
+ {USB_DEVICE(0x14B2, 0x3302)},
+ /* - */
+ {USB_DEVICE(0x04F2, 0xAFF2)},
+ {USB_DEVICE(0x04F2, 0xAFF5)},
+ {USB_DEVICE(0x04F2, 0xAFF6)},
+ {USB_DEVICE(0x13D3, 0x3339)},
+ {USB_DEVICE(0x13D3, 0x3340)}, /* 11n mode disable */
+ {USB_DEVICE(0x13D3, 0x3341)}, /* 11n mode disable */
+ {USB_DEVICE(0x13D3, 0x3310)},
+ {USB_DEVICE(0x13D3, 0x3325)},
+
+/* RTL8192SU */
+ /* Realtek */
+ {USB_DEVICE(0x0BDA, 0x8174)},
+ /* Belkin */
+ {USB_DEVICE(0x050D, 0x845A)},
+ /* Corega */
+ {USB_DEVICE(0x07AA, 0x0051)},
+ /* Edimax */
+ {USB_DEVICE(0x7392, 0x7622)},
+ /* NEC */
+ {USB_DEVICE(0x0409, 0x02B6)},
+ {}
+};
+
+MODULE_DEVICE_TABLE(usb, rtl871x_usb_id_tbl);
+
+static struct specific_device_id specific_device_id_tbl[] = {
+ {.idVendor = 0x0b05, .idProduct = 0x1791,
+ .flags = SPEC_DEV_ID_DISABLE_HT},
+ {.idVendor = 0x0df6, .idProduct = 0x0059,
+ .flags = SPEC_DEV_ID_DISABLE_HT},
+ {.idVendor = 0x13d3, .idProduct = 0x3306,
+ .flags = SPEC_DEV_ID_DISABLE_HT},
+ {.idVendor = 0x13D3, .idProduct = 0x3311,
+ .flags = SPEC_DEV_ID_DISABLE_HT},
+ {.idVendor = 0x13d3, .idProduct = 0x3335,
+ .flags = SPEC_DEV_ID_DISABLE_HT},
+ {.idVendor = 0x13d3, .idProduct = 0x3336,
+ .flags = SPEC_DEV_ID_DISABLE_HT},
+ {.idVendor = 0x13d3, .idProduct = 0x3340,
+ .flags = SPEC_DEV_ID_DISABLE_HT},
+ {.idVendor = 0x13d3, .idProduct = 0x3341,
+ .flags = SPEC_DEV_ID_DISABLE_HT},
+ {}
+};
+
+struct drv_priv {
+ struct usb_driver r871xu_drv;
+ int drv_registered;
+};
+
+#ifdef CONFIG_PM
+static int r871x_suspend(struct usb_interface *pusb_intf, pm_message_t state)
+{
+ struct net_device *pnetdev = usb_get_intfdata(pusb_intf);
+
+ netdev_info(pnetdev, "Suspending...\n");
+ if (!pnetdev || !netif_running(pnetdev)) {
+ netdev_info(pnetdev, "Unable to suspend\n");
+ return 0;
+ }
+ if (pnetdev->netdev_ops->ndo_stop)
+ pnetdev->netdev_ops->ndo_stop(pnetdev);
+ mdelay(10);
+ netif_device_detach(pnetdev);
+ return 0;
+}
+
+static int r871x_resume(struct usb_interface *pusb_intf)
+{
+ struct net_device *pnetdev = usb_get_intfdata(pusb_intf);
+
+ netdev_info(pnetdev, "Resuming...\n");
+ if (!pnetdev || !netif_running(pnetdev)) {
+ netdev_info(pnetdev, "Unable to resume\n");
+ return 0;
+ }
+ netif_device_attach(pnetdev);
+ if (pnetdev->netdev_ops->ndo_open)
+ pnetdev->netdev_ops->ndo_open(pnetdev);
+ return 0;
+}
+
+static int r871x_reset_resume(struct usb_interface *pusb_intf)
+{
+ /* dummy routine */
+ return 0;
+}
+
+#endif
+
+static struct drv_priv drvpriv = {
+ .r871xu_drv.name = "r8712u",
+ .r871xu_drv.id_table = rtl871x_usb_id_tbl,
+ .r871xu_drv.probe = r871xu_drv_init,
+ .r871xu_drv.disconnect = r871xu_dev_remove,
+#ifdef CONFIG_PM
+ .r871xu_drv.suspend = r871x_suspend,
+ .r871xu_drv.resume = r871x_resume,
+ .r871xu_drv.reset_resume = r871x_reset_resume,
+#endif
+};
+
+static uint r8712_usb_dvobj_init(struct _adapter *padapter)
+{
+ uint status = _SUCCESS;
+ struct usb_host_interface *phost_iface;
+ struct usb_interface_descriptor *piface_desc;
+ struct dvobj_priv *pdvobjpriv = &padapter->dvobjpriv;
+ struct usb_device *pusbd = pdvobjpriv->pusbdev;
+
+ pdvobjpriv->padapter = padapter;
+ padapter->EepromAddressSize = 6;
+ phost_iface = &pintf->altsetting[0];
+ piface_desc = &phost_iface->desc;
+ pdvobjpriv->nr_endpoint = piface_desc->bNumEndpoints;
+ if (pusbd->speed == USB_SPEED_HIGH) {
+ pdvobjpriv->ishighspeed = true;
+ dev_info(&pusbd->dev, "r8712u: USB_SPEED_HIGH with %d endpoints\n",
+ pdvobjpriv->nr_endpoint);
+ } else {
+ pdvobjpriv->ishighspeed = false;
+ dev_info(&pusbd->dev, "r8712u: USB_SPEED_LOW with %d endpoints\n",
+ pdvobjpriv->nr_endpoint);
+ }
+ if ((r8712_alloc_io_queue(padapter)) == _FAIL)
+ status = _FAIL;
+ return status;
+}
+
+static void r8712_usb_dvobj_deinit(struct _adapter *padapter)
+{
+}
+
+void rtl871x_intf_stop(struct _adapter *padapter)
+{
+ /*disable_hw_interrupt*/
+ if (!padapter->bSurpriseRemoved) {
+ /*device still exists, so driver can do i/o operation
+ * TODO: */
+ }
+
+ /* cancel in irp */
+ if (padapter->dvobjpriv.inirp_deinit)
+ padapter->dvobjpriv.inirp_deinit(padapter);
+ /* cancel out irp */
+ r8712_usb_write_port_cancel(padapter);
+ /* TODO:cancel other irps */
+}
+
+void r871x_dev_unload(struct _adapter *padapter)
+{
+ if (padapter->bup == true) {
+ /*s1.*/
+ padapter->bDriverStopped = true;
+
+ /*s3.*/
+ rtl871x_intf_stop(padapter);
+
+ /*s4.*/
+ r8712_stop_drv_threads(padapter);
+
+ /*s5.*/
+ if (!padapter->bSurpriseRemoved) {
+ padapter->hw_init_completed = false;
+ rtl8712_hal_deinit(padapter);
+ }
+
+ /*s6.*/
+ if (padapter->dvobj_deinit)
+ padapter->dvobj_deinit(padapter);
+ padapter->bup = false;
+ }
+}
+
+static void disable_ht_for_spec_devid(const struct usb_device_id *pdid,
+ struct _adapter *padapter)
+{
+ u16 vid, pid;
+ u32 flags;
+ int i;
+ int num = sizeof(specific_device_id_tbl) /
+ sizeof(struct specific_device_id);
+
+ for (i = 0; i < num; i++) {
+ vid = specific_device_id_tbl[i].idVendor;
+ pid = specific_device_id_tbl[i].idProduct;
+ flags = specific_device_id_tbl[i].flags;
+
+ if ((pdid->idVendor == vid) && (pdid->idProduct == pid) &&
+ (flags&SPEC_DEV_ID_DISABLE_HT)) {
+ padapter->registrypriv.ht_enable = 0;
+ padapter->registrypriv.cbw40_enable = 0;
+ padapter->registrypriv.ampdu_enable = 0;
+ }
+ }
+}
+
+static const struct device_type wlan_type = {
+ .name = "wlan",
+};
+
+/*
+ * drv_init() - a device potentially for us
+ *
+ * notes: drv_init() is called when the bus driver has located a card for us
+ * to support. We accept the new device by returning 0.
+*/
+static int r871xu_drv_init(struct usb_interface *pusb_intf,
+ const struct usb_device_id *pdid)
+{
+ uint status;
+ struct _adapter *padapter = NULL;
+ struct dvobj_priv *pdvobjpriv;
+ struct net_device *pnetdev;
+ struct usb_device *udev;
+
+ /* In this probe function, O.S. will provide the usb interface pointer
+ * to driver. We have to increase the reference count of the usb device
+ * structure by using the usb_get_dev function.
+ */
+ udev = interface_to_usbdev(pusb_intf);
+ usb_get_dev(udev);
+ pintf = pusb_intf;
+ /* step 1. */
+ pnetdev = r8712_init_netdev();
+ if (!pnetdev)
+ goto error;
+ padapter = netdev_priv(pnetdev);
+ disable_ht_for_spec_devid(pdid, padapter);
+ pdvobjpriv = &padapter->dvobjpriv;
+ pdvobjpriv->padapter = padapter;
+ padapter->dvobjpriv.pusbdev = udev;
+ padapter->pusb_intf = pusb_intf;
+ usb_set_intfdata(pusb_intf, pnetdev);
+ SET_NETDEV_DEV(pnetdev, &pusb_intf->dev);
+ pnetdev->dev.type = &wlan_type;
+ /* step 2. */
+ padapter->dvobj_init = &r8712_usb_dvobj_init;
+ padapter->dvobj_deinit = &r8712_usb_dvobj_deinit;
+ padapter->halpriv.hal_bus_init = &r8712_usb_hal_bus_init;
+ padapter->dvobjpriv.inirp_init = &r8712_usb_inirp_init;
+ padapter->dvobjpriv.inirp_deinit = &r8712_usb_inirp_deinit;
+ /* step 3.
+ * initialize the dvobj_priv
+ */
+ if (!padapter->dvobj_init)
+ goto error;
+ else {
+ status = padapter->dvobj_init(padapter);
+ if (status != _SUCCESS)
+ goto error;
+ }
+ /* step 4. */
+ status = r8712_init_drv_sw(padapter);
+ if (status == _FAIL)
+ goto error;
+ /* step 5. read efuse/eeprom data and get mac_addr */
+ {
+ int i, offset;
+ u8 mac[6];
+ u8 tmpU1b, AutoloadFail, eeprom_CustomerID;
+ u8 *pdata = padapter->eeprompriv.efuse_eeprom_data;
+
+ tmpU1b = r8712_read8(padapter, EE_9346CR);/*CR9346*/
+
+ /* To check system boot selection.*/
+ dev_info(&udev->dev, "r8712u: Boot from %s: Autoload %s\n",
+ (tmpU1b & _9356SEL) ? "EEPROM" : "EFUSE",
+ (tmpU1b & _EEPROM_EN) ? "OK" : "Failed");
+
+ /* To check autoload success or not.*/
+ if (tmpU1b & _EEPROM_EN) {
+ AutoloadFail = true;
+ /* The following operations prevent Efuse leakage by
+ * turning on 2.5V.
+ */
+ tmpU1b = r8712_read8(padapter, EFUSE_TEST+3);
+ r8712_write8(padapter, EFUSE_TEST + 3, tmpU1b | 0x80);
+ msleep(20);
+ r8712_write8(padapter, EFUSE_TEST + 3,
+ (tmpU1b & (~BIT(7))));
+
+ /* Retrieve Chip version.
+ * Recognize IC version by Reg0x4 BIT15.
+ */
+ tmpU1b = (u8)((r8712_read32(padapter, PMC_FSM) >> 15) &
+ 0x1F);
+ if (tmpU1b == 0x3)
+ padapter->registrypriv.chip_version =
+ RTL8712_3rdCUT;
+ else
+ padapter->registrypriv.chip_version =
+ (tmpU1b >> 1) + 1;
+ switch (padapter->registrypriv.chip_version) {
+ case RTL8712_1stCUT:
+ case RTL8712_2ndCUT:
+ case RTL8712_3rdCUT:
+ break;
+ default:
+ padapter->registrypriv.chip_version =
+ RTL8712_2ndCUT;
+ break;
+ }
+
+ for (i = 0, offset = 0; i < 128; i += 8, offset++)
+ r8712_efuse_pg_packet_read(padapter, offset,
+ &pdata[i]);
+
+ if (!r8712_initmac || !mac_pton(r8712_initmac, mac)) {
+ /* Use the mac address stored in the Efuse
+ * offset = 0x12 for usb in efuse
+ */
+ ether_addr_copy(mac, &pdata[0x12]);
+ }
+ eeprom_CustomerID = pdata[0x52];
+ switch (eeprom_CustomerID) {
+ case EEPROM_CID_ALPHA:
+ padapter->eeprompriv.CustomerID =
+ RT_CID_819x_ALPHA;
+ break;
+ case EEPROM_CID_CAMEO:
+ padapter->eeprompriv.CustomerID =
+ RT_CID_819x_CAMEO;
+ break;
+ case EEPROM_CID_SITECOM:
+ padapter->eeprompriv.CustomerID =
+ RT_CID_819x_Sitecom;
+ break;
+ case EEPROM_CID_COREGA:
+ padapter->eeprompriv.CustomerID =
+ RT_CID_COREGA;
+ break;
+ case EEPROM_CID_Senao:
+ padapter->eeprompriv.CustomerID =
+ RT_CID_819x_Senao;
+ break;
+ case EEPROM_CID_EDIMAX_BELKIN:
+ padapter->eeprompriv.CustomerID =
+ RT_CID_819x_Edimax_Belkin;
+ break;
+ case EEPROM_CID_SERCOMM_BELKIN:
+ padapter->eeprompriv.CustomerID =
+ RT_CID_819x_Sercomm_Belkin;
+ break;
+ case EEPROM_CID_WNC_COREGA:
+ padapter->eeprompriv.CustomerID =
+ RT_CID_819x_WNC_COREGA;
+ break;
+ case EEPROM_CID_WHQL:
+ break;
+ case EEPROM_CID_NetCore:
+ padapter->eeprompriv.CustomerID =
+ RT_CID_819x_Netcore;
+ break;
+ case EEPROM_CID_CAMEO1:
+ padapter->eeprompriv.CustomerID =
+ RT_CID_819x_CAMEO1;
+ break;
+ case EEPROM_CID_CLEVO:
+ padapter->eeprompriv.CustomerID =
+ RT_CID_819x_CLEVO;
+ break;
+ default:
+ padapter->eeprompriv.CustomerID =
+ RT_CID_DEFAULT;
+ break;
+ }
+ dev_info(&udev->dev, "r8712u: CustomerID = 0x%.4x\n",
+ padapter->eeprompriv.CustomerID);
+ /* Led mode */
+ switch (padapter->eeprompriv.CustomerID) {
+ case RT_CID_DEFAULT:
+ case RT_CID_819x_ALPHA:
+ case RT_CID_819x_CAMEO:
+ padapter->ledpriv.LedStrategy = SW_LED_MODE1;
+ padapter->ledpriv.bRegUseLed = true;
+ break;
+ case RT_CID_819x_Sitecom:
+ padapter->ledpriv.LedStrategy = SW_LED_MODE2;
+ padapter->ledpriv.bRegUseLed = true;
+ break;
+ case RT_CID_COREGA:
+ case RT_CID_819x_Senao:
+ padapter->ledpriv.LedStrategy = SW_LED_MODE3;
+ padapter->ledpriv.bRegUseLed = true;
+ break;
+ case RT_CID_819x_Edimax_Belkin:
+ padapter->ledpriv.LedStrategy = SW_LED_MODE4;
+ padapter->ledpriv.bRegUseLed = true;
+ break;
+ case RT_CID_819x_Sercomm_Belkin:
+ padapter->ledpriv.LedStrategy = SW_LED_MODE5;
+ padapter->ledpriv.bRegUseLed = true;
+ break;
+ case RT_CID_819x_WNC_COREGA:
+ padapter->ledpriv.LedStrategy = SW_LED_MODE6;
+ padapter->ledpriv.bRegUseLed = true;
+ break;
+ default:
+ padapter->ledpriv.LedStrategy = SW_LED_MODE0;
+ padapter->ledpriv.bRegUseLed = false;
+ break;
+ }
+ } else
+ AutoloadFail = false;
+ if (((mac[0] == 0xff) && (mac[1] == 0xff) &&
+ (mac[2] == 0xff) && (mac[3] == 0xff) &&
+ (mac[4] == 0xff) && (mac[5] == 0xff)) ||
+ ((mac[0] == 0x00) && (mac[1] == 0x00) &&
+ (mac[2] == 0x00) && (mac[3] == 0x00) &&
+ (mac[4] == 0x00) && (mac[5] == 0x00)) ||
+ (!AutoloadFail)) {
+ mac[0] = 0x00;
+ mac[1] = 0xe0;
+ mac[2] = 0x4c;
+ mac[3] = 0x87;
+ mac[4] = 0x00;
+ mac[5] = 0x00;
+ }
+ if (r8712_initmac) {
+ /* Make sure the user did not select a multicast
+ * address by setting bit 1 of first octet.
+ */
+ mac[0] &= 0xFE;
+ dev_info(&udev->dev,
+ "r8712u: MAC Address from user = %pM\n", mac);
+ } else
+ dev_info(&udev->dev,
+ "r8712u: MAC Address from efuse = %pM\n", mac);
+ ether_addr_copy(pnetdev->dev_addr, mac);
+ }
+ /* step 6. Load the firmware asynchronously */
+ if (rtl871x_load_fw(padapter))
+ goto error;
+ spin_lock_init(&padapter->lockRxFF0Filter);
+ mutex_init(&padapter->mutex_start);
+ return 0;
+error:
+ usb_put_dev(udev);
+ usb_set_intfdata(pusb_intf, NULL);
+ if (padapter && padapter->dvobj_deinit != NULL)
+ padapter->dvobj_deinit(padapter);
+ if (pnetdev)
+ free_netdev(pnetdev);
+ return -ENODEV;
+}
+
+/* rmmod module & unplug(SurpriseRemoved) will call r871xu_dev_remove()
+ * => how to recognize both */
+static void r871xu_dev_remove(struct usb_interface *pusb_intf)
+{
+ struct net_device *pnetdev = usb_get_intfdata(pusb_intf);
+ struct usb_device *udev = interface_to_usbdev(pusb_intf);
+
+ if (pnetdev) {
+ struct _adapter *padapter = netdev_priv(pnetdev);
+
+ usb_set_intfdata(pusb_intf, NULL);
+ release_firmware(padapter->fw);
+ /* never exit with a firmware callback pending */
+ wait_for_completion(&padapter->rtl8712_fw_ready);
+ if (drvpriv.drv_registered == true)
+ padapter->bSurpriseRemoved = true;
+ unregister_netdev(pnetdev); /* will call netdev_close() */
+ flush_scheduled_work();
+ udelay(1);
+ /* Stop driver mlme relation timer */
+ r8712_stop_drv_timers(padapter);
+ r871x_dev_unload(padapter);
+ r8712_free_drv_sw(padapter);
+
+ /* decrease the reference count of the usb device structure
+ * when disconnect */
+ usb_put_dev(udev);
+ }
+ /* If we didn't unplug usb dongle and remove/insert module, driver
+ * fails on sitesurvey for the first time when device is up.
+ * Reset usb port for sitesurvey fail issue. */
+ if (udev->state != USB_STATE_NOTATTACHED)
+ usb_reset_device(udev);
+}
+
+static int __init r8712u_drv_entry(void)
+{
+ drvpriv.drv_registered = true;
+ return usb_register(&drvpriv.r871xu_drv);
+}
+
+static void __exit r8712u_drv_halt(void)
+{
+ drvpriv.drv_registered = false;
+ usb_deregister(&drvpriv.r871xu_drv);
+}
+
+module_init(r8712u_drv_entry);
+module_exit(r8712u_drv_halt);
diff --git a/drivers/staging/rtl8712/usb_ops.c b/drivers/staging/rtl8712/usb_ops.c
new file mode 100644
index 000000000..c03508d93
--- /dev/null
+++ b/drivers/staging/rtl8712/usb_ops.c
@@ -0,0 +1,200 @@
+/******************************************************************************
+ * usb_ops.c
+ *
+ * Copyright(c) 2007 - 2010 Realtek Corporation. All rights reserved.
+ * Linux device driver for RTL8192SU
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ * Modifications for inclusion into the Linux staging tree are
+ * Copyright(c) 2010 Larry Finger. All rights reserved.
+ *
+ * Contact information:
+ * WLAN FAE <wlanfae@realtek.com>
+ * Larry Finger <Larry.Finger@lwfinger.net>
+ *
+ ******************************************************************************/
+
+#define _HCI_OPS_C_
+
+#include "osdep_service.h"
+#include "drv_types.h"
+#include "osdep_intf.h"
+#include "usb_ops.h"
+#include "recv_osdep.h"
+
+static u8 usb_read8(struct intf_hdl *pintfhdl, u32 addr)
+{
+ u8 request;
+ u8 requesttype;
+ u16 wvalue;
+ u16 index;
+ u16 len;
+ u32 data;
+ struct intf_priv *pintfpriv = pintfhdl->pintfpriv;
+
+ request = 0x05;
+ requesttype = 0x01; /* read_in */
+ index = 0;
+ wvalue = (u16)(addr&0x0000ffff);
+ len = 1;
+ r8712_usbctrl_vendorreq(pintfpriv, request, wvalue, index, &data, len,
+ requesttype);
+ return (u8)(le32_to_cpu(data)&0x0ff);
+}
+
+static u16 usb_read16(struct intf_hdl *pintfhdl, u32 addr)
+{
+ u8 request;
+ u8 requesttype;
+ u16 wvalue;
+ u16 index;
+ u16 len;
+ u32 data;
+ struct intf_priv *pintfpriv = pintfhdl->pintfpriv;
+
+ request = 0x05;
+ requesttype = 0x01; /* read_in */
+ index = 0;
+ wvalue = (u16)(addr&0x0000ffff);
+ len = 2;
+ r8712_usbctrl_vendorreq(pintfpriv, request, wvalue, index, &data, len,
+ requesttype);
+ return (u16)(le32_to_cpu(data)&0xffff);
+}
+
+static u32 usb_read32(struct intf_hdl *pintfhdl, u32 addr)
+{
+ u8 request;
+ u8 requesttype;
+ u16 wvalue;
+ u16 index;
+ u16 len;
+ u32 data;
+ struct intf_priv *pintfpriv = pintfhdl->pintfpriv;
+
+ request = 0x05;
+ requesttype = 0x01; /* read_in */
+ index = 0;
+ wvalue = (u16)(addr&0x0000ffff);
+ len = 4;
+ r8712_usbctrl_vendorreq(pintfpriv, request, wvalue, index, &data, len,
+ requesttype);
+ return le32_to_cpu(data);
+}
+
+static void usb_write8(struct intf_hdl *pintfhdl, u32 addr, u8 val)
+{
+ u8 request;
+ u8 requesttype;
+ u16 wvalue;
+ u16 index;
+ u16 len;
+ u32 data;
+ struct intf_priv *pintfpriv = pintfhdl->pintfpriv;
+
+ request = 0x05;
+ requesttype = 0x00; /* write_out */
+ index = 0;
+ wvalue = (u16)(addr&0x0000ffff);
+ len = 1;
+ data = val;
+ data = cpu_to_le32(data&0x000000ff);
+ r8712_usbctrl_vendorreq(pintfpriv, request, wvalue, index, &data, len,
+ requesttype);
+}
+
+static void usb_write16(struct intf_hdl *pintfhdl, u32 addr, u16 val)
+{
+ u8 request;
+ u8 requesttype;
+ u16 wvalue;
+ u16 index;
+ u16 len;
+ u32 data;
+ struct intf_priv *pintfpriv = pintfhdl->pintfpriv;
+
+ request = 0x05;
+ requesttype = 0x00; /* write_out */
+ index = 0;
+ wvalue = (u16)(addr&0x0000ffff);
+ len = 2;
+ data = val;
+ data = cpu_to_le32(data&0x0000ffff);
+ r8712_usbctrl_vendorreq(pintfpriv, request, wvalue, index, &data, len,
+ requesttype);
+}
+
+static void usb_write32(struct intf_hdl *pintfhdl, u32 addr, u32 val)
+{
+ u8 request;
+ u8 requesttype;
+ u16 wvalue;
+ u16 index;
+ u16 len;
+ u32 data;
+ struct intf_priv *pintfpriv = pintfhdl->pintfpriv;
+
+ request = 0x05;
+ requesttype = 0x00; /* write_out */
+ index = 0;
+ wvalue = (u16)(addr&0x0000ffff);
+ len = 4;
+ data = cpu_to_le32(val);
+ r8712_usbctrl_vendorreq(pintfpriv, request, wvalue, index, &data, len,
+ requesttype);
+}
+
+void r8712_usb_set_intf_option(u32 *poption)
+{
+ *poption = ((*poption) | _INTF_ASYNC_);
+}
+
+static void usb_intf_hdl_init(u8 *priv)
+{
+}
+
+static void usb_intf_hdl_unload(u8 *priv)
+{
+}
+
+static void usb_intf_hdl_open(u8 *priv)
+{
+}
+
+static void usb_intf_hdl_close(u8 *priv)
+{
+}
+
+void r8712_usb_set_intf_funs(struct intf_hdl *pintf_hdl)
+{
+ pintf_hdl->intf_hdl_init = &usb_intf_hdl_init;
+ pintf_hdl->intf_hdl_unload = &usb_intf_hdl_unload;
+ pintf_hdl->intf_hdl_open = &usb_intf_hdl_open;
+ pintf_hdl->intf_hdl_close = &usb_intf_hdl_close;
+}
+
+void r8712_usb_set_intf_ops(struct _io_ops *pops)
+{
+ memset((u8 *)pops, 0, sizeof(struct _io_ops));
+ pops->_read8 = &usb_read8;
+ pops->_read16 = &usb_read16;
+ pops->_read32 = &usb_read32;
+ pops->_read_port = &r8712_usb_read_port;
+ pops->_write8 = &usb_write8;
+ pops->_write16 = &usb_write16;
+ pops->_write32 = &usb_write32;
+ pops->_write_mem = &r8712_usb_write_mem;
+ pops->_write_port = &r8712_usb_write_port;
+}
diff --git a/drivers/staging/rtl8712/usb_ops.h b/drivers/staging/rtl8712/usb_ops.h
new file mode 100644
index 000000000..78e775a46
--- /dev/null
+++ b/drivers/staging/rtl8712/usb_ops.h
@@ -0,0 +1,50 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2010 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ * Modifications for inclusion into the Linux staging tree are
+ * Copyright(c) 2010 Larry Finger. All rights reserved.
+ *
+ * Contact information:
+ * WLAN FAE <wlanfae@realtek.com>
+ * Larry Finger <Larry.Finger@lwfinger.net>
+ *
+ ******************************************************************************/
+#ifndef __USB_OPS_H_
+#define __USB_OPS_H_
+
+#include "osdep_service.h"
+#include "drv_types.h"
+#include "osdep_intf.h"
+
+void r8712_usb_write_mem(struct intf_hdl *pintfhdl, u32 addr,
+ u32 cnt, u8 *wmem);
+u32 r8712_usb_write_port(struct intf_hdl *pintfhdl, u32 addr,
+ u32 cnt, u8 *wmem);
+u32 r8712_usb_read_port(struct intf_hdl *pintfhdl, u32 addr,
+ u32 cnt, u8 *rmem);
+void r8712_usb_set_intf_option(u32 *poption);
+void r8712_usb_set_intf_funs(struct intf_hdl *pintf_hdl);
+uint r8712_usb_init_intf_priv(struct intf_priv *pintfpriv);
+void r8712_usb_unload_intf_priv(struct intf_priv *pintfpriv);
+void r8712_usb_set_intf_ops(struct _io_ops *pops);
+void r8712_usb_read_port_cancel(struct _adapter *padapter);
+void r8712_usb_write_port_cancel(struct _adapter *padapter);
+int r8712_usbctrl_vendorreq(struct intf_priv *pintfpriv, u8 request, u16 value,
+ u16 index, void *pdata, u16 len, u8 requesttype);
+
+#endif
+
diff --git a/drivers/staging/rtl8712/usb_ops_linux.c b/drivers/staging/rtl8712/usb_ops_linux.c
new file mode 100644
index 000000000..c3a4e3f26
--- /dev/null
+++ b/drivers/staging/rtl8712/usb_ops_linux.c
@@ -0,0 +1,523 @@
+/******************************************************************************
+ * usb_ops_linux.c
+ *
+ * Copyright(c) 2007 - 2010 Realtek Corporation. All rights reserved.
+ * Linux device driver for RTL8192SU
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ * Modifications for inclusion into the Linux staging tree are
+ * Copyright(c) 2010 Larry Finger. All rights reserved.
+ *
+ * Contact information:
+ * WLAN FAE <wlanfae@realtek.com>
+ * Larry Finger <Larry.Finger@lwfinger.net>
+ *
+ ******************************************************************************/
+
+#define _HCI_OPS_OS_C_
+
+#include <linux/usb.h>
+
+#include "osdep_service.h"
+#include "drv_types.h"
+#include "osdep_intf.h"
+#include "usb_ops.h"
+
+#define RTL871X_VENQT_READ 0xc0
+#define RTL871X_VENQT_WRITE 0x40
+
+struct zero_bulkout_context {
+ void *pbuf;
+ void *purb;
+ void *pirp;
+ void *padapter;
+};
+
+uint r8712_usb_init_intf_priv(struct intf_priv *pintfpriv)
+{
+ pintfpriv->piorw_urb = usb_alloc_urb(0, GFP_ATOMIC);
+ if (!pintfpriv->piorw_urb)
+ return _FAIL;
+ sema_init(&(pintfpriv->io_retevt), 0);
+ return _SUCCESS;
+}
+
+void r8712_usb_unload_intf_priv(struct intf_priv *pintfpriv)
+{
+ if (pintfpriv->piorw_urb) {
+ usb_kill_urb(pintfpriv->piorw_urb);
+ usb_free_urb(pintfpriv->piorw_urb);
+ }
+}
+
+static unsigned int ffaddr2pipehdl(struct dvobj_priv *pdvobj, u32 addr)
+{
+ unsigned int pipe = 0;
+ struct usb_device *pusbd = pdvobj->pusbdev;
+
+ if (pdvobj->nr_endpoint == 11) {
+ switch (addr) {
+ case RTL8712_DMA_BKQ:
+ pipe = usb_sndbulkpipe(pusbd, 0x07);
+ break;
+ case RTL8712_DMA_BEQ:
+ pipe = usb_sndbulkpipe(pusbd, 0x06);
+ break;
+ case RTL8712_DMA_VIQ:
+ pipe = usb_sndbulkpipe(pusbd, 0x05);
+ break;
+ case RTL8712_DMA_VOQ:
+ pipe = usb_sndbulkpipe(pusbd, 0x04);
+ break;
+ case RTL8712_DMA_BCNQ:
+ pipe = usb_sndbulkpipe(pusbd, 0x0a);
+ break;
+ case RTL8712_DMA_BMCQ: /* HI Queue */
+ pipe = usb_sndbulkpipe(pusbd, 0x0b);
+ break;
+ case RTL8712_DMA_MGTQ:
+ pipe = usb_sndbulkpipe(pusbd, 0x0c);
+ break;
+ case RTL8712_DMA_RX0FF:
+ pipe = usb_rcvbulkpipe(pusbd, 0x03); /* in */
+ break;
+ case RTL8712_DMA_C2HCMD:
+ pipe = usb_rcvbulkpipe(pusbd, 0x09); /* in */
+ break;
+ case RTL8712_DMA_H2CCMD:
+ pipe = usb_sndbulkpipe(pusbd, 0x0d);
+ break;
+ }
+ } else if (pdvobj->nr_endpoint == 6) {
+ switch (addr) {
+ case RTL8712_DMA_BKQ:
+ pipe = usb_sndbulkpipe(pusbd, 0x07);
+ break;
+ case RTL8712_DMA_BEQ:
+ pipe = usb_sndbulkpipe(pusbd, 0x06);
+ break;
+ case RTL8712_DMA_VIQ:
+ pipe = usb_sndbulkpipe(pusbd, 0x05);
+ break;
+ case RTL8712_DMA_VOQ:
+ pipe = usb_sndbulkpipe(pusbd, 0x04);
+ break;
+ case RTL8712_DMA_RX0FF:
+ case RTL8712_DMA_C2HCMD:
+ pipe = usb_rcvbulkpipe(pusbd, 0x03); /* in */
+ break;
+ case RTL8712_DMA_H2CCMD:
+ case RTL8712_DMA_BCNQ:
+ case RTL8712_DMA_BMCQ:
+ case RTL8712_DMA_MGTQ:
+ pipe = usb_sndbulkpipe(pusbd, 0x0d);
+ break;
+ }
+ } else if (pdvobj->nr_endpoint == 4) {
+ switch (addr) {
+ case RTL8712_DMA_BEQ:
+ pipe = usb_sndbulkpipe(pusbd, 0x06);
+ break;
+ case RTL8712_DMA_VOQ:
+ pipe = usb_sndbulkpipe(pusbd, 0x04);
+ break;
+ case RTL8712_DMA_RX0FF:
+ case RTL8712_DMA_C2HCMD:
+ pipe = usb_rcvbulkpipe(pusbd, 0x03); /* in */
+ break;
+ case RTL8712_DMA_H2CCMD:
+ case RTL8712_DMA_BCNQ:
+ case RTL8712_DMA_BMCQ:
+ case RTL8712_DMA_MGTQ:
+ pipe = usb_sndbulkpipe(pusbd, 0x0d);
+ break;
+ }
+ } else
+ pipe = 0;
+ return pipe;
+}
+
+static void usb_write_mem_complete(struct urb *purb)
+{
+ struct io_queue *pio_q = (struct io_queue *)purb->context;
+ struct intf_hdl *pintf = &(pio_q->intf);
+ struct intf_priv *pintfpriv = pintf->pintfpriv;
+ struct _adapter *padapter = (struct _adapter *)pintf->adapter;
+
+ if (purb->status != 0) {
+ if (purb->status == (-ESHUTDOWN))
+ padapter->bDriverStopped = true;
+ else
+ padapter->bSurpriseRemoved = true;
+ }
+ up(&pintfpriv->io_retevt);
+}
+
+void r8712_usb_write_mem(struct intf_hdl *pintfhdl, u32 addr, u32 cnt, u8 *wmem)
+{
+ unsigned int pipe;
+ struct _adapter *padapter = (struct _adapter *)pintfhdl->adapter;
+ struct intf_priv *pintfpriv = pintfhdl->pintfpriv;
+ struct io_queue *pio_queue = (struct io_queue *)padapter->pio_queue;
+ struct dvobj_priv *pdvobj = (struct dvobj_priv *)pintfpriv->intf_dev;
+ struct usb_device *pusbd = pdvobj->pusbdev;
+ struct urb *piorw_urb = pintfpriv->piorw_urb;
+
+ if ((padapter->bDriverStopped) || (padapter->bSurpriseRemoved) ||
+ (padapter->pwrctrlpriv.pnp_bstop_trx))
+ return;
+ /* translate DMA FIFO addr to pipehandle */
+ pipe = ffaddr2pipehdl(pdvobj, addr);
+ if (pipe == 0)
+ return;
+ usb_fill_bulk_urb(piorw_urb, pusbd, pipe,
+ wmem, cnt, usb_write_mem_complete,
+ pio_queue);
+ usb_submit_urb(piorw_urb, GFP_ATOMIC);
+ _down_sema(&pintfpriv->io_retevt);
+}
+
+static void r8712_usb_read_port_complete(struct urb *purb)
+{
+ uint isevt, *pbuf;
+ struct recv_buf *precvbuf = (struct recv_buf *)purb->context;
+ struct _adapter *padapter = (struct _adapter *)precvbuf->adapter;
+ struct recv_priv *precvpriv = &padapter->recvpriv;
+
+ if (padapter->bSurpriseRemoved || padapter->bDriverStopped)
+ return;
+ if (purb->status == 0) { /* SUCCESS */
+ if ((purb->actual_length > (MAX_RECVBUF_SZ)) ||
+ (purb->actual_length < RXDESC_SIZE)) {
+ precvbuf->reuse = true;
+ r8712_read_port(padapter, precvpriv->ff_hwaddr, 0,
+ (unsigned char *)precvbuf);
+ } else {
+ precvbuf->transfer_len = purb->actual_length;
+ pbuf = (uint *)precvbuf->pbuf;
+ isevt = le32_to_cpu(*(pbuf + 1)) & 0x1ff;
+ if ((isevt & 0x1ff) == 0x1ff) {
+ r8712_rxcmd_event_hdl(padapter, pbuf);
+ precvbuf->reuse = true;
+ r8712_read_port(padapter, precvpriv->ff_hwaddr,
+ 0, (unsigned char *)precvbuf);
+ } else {
+ _pkt *pskb = precvbuf->pskb;
+
+ skb_put(pskb, purb->actual_length);
+ skb_queue_tail(&precvpriv->rx_skb_queue, pskb);
+ tasklet_hi_schedule(&precvpriv->recv_tasklet);
+ precvbuf->pskb = NULL;
+ precvbuf->reuse = false;
+ r8712_read_port(padapter, precvpriv->ff_hwaddr,
+ 0, (unsigned char *)precvbuf);
+ }
+ }
+ } else {
+ switch (purb->status) {
+ case -EINVAL:
+ case -EPIPE:
+ case -ENODEV:
+ case -ESHUTDOWN:
+ case -ENOENT:
+ padapter->bDriverStopped = true;
+ break;
+ case -EPROTO:
+ precvbuf->reuse = true;
+ r8712_read_port(padapter, precvpriv->ff_hwaddr, 0,
+ (unsigned char *)precvbuf);
+ break;
+ case -EINPROGRESS:
+ netdev_err(padapter->pnetdev, "ERROR: URB IS IN PROGRESS!\n");
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+u32 r8712_usb_read_port(struct intf_hdl *pintfhdl, u32 addr, u32 cnt, u8 *rmem)
+{
+ unsigned int pipe;
+ int err;
+ u32 tmpaddr = 0;
+ int alignment = 0;
+ u32 ret = _SUCCESS;
+ struct urb *purb = NULL;
+ struct recv_buf *precvbuf = (struct recv_buf *)rmem;
+ struct intf_priv *pintfpriv = pintfhdl->pintfpriv;
+ struct dvobj_priv *pdvobj = (struct dvobj_priv *)pintfpriv->intf_dev;
+ struct _adapter *adapter = (struct _adapter *)pdvobj->padapter;
+ struct recv_priv *precvpriv = &adapter->recvpriv;
+ struct usb_device *pusbd = pdvobj->pusbdev;
+
+ if (adapter->bDriverStopped || adapter->bSurpriseRemoved ||
+ adapter->pwrctrlpriv.pnp_bstop_trx)
+ return _FAIL;
+ if (!precvbuf->reuse == false || !precvbuf->pskb) {
+ precvbuf->pskb = skb_dequeue(&precvpriv->free_recv_skb_queue);
+ if (NULL != precvbuf->pskb)
+ precvbuf->reuse = true;
+ }
+ if (precvbuf != NULL) {
+ r8712_init_recvbuf(adapter, precvbuf);
+ /* re-assign for linux based on skb */
+ if (!precvbuf->reuse || !precvbuf->pskb) {
+ precvbuf->pskb = netdev_alloc_skb(adapter->pnetdev,
+ MAX_RECVBUF_SZ + RECVBUFF_ALIGN_SZ);
+ if (!precvbuf->pskb)
+ return _FAIL;
+ tmpaddr = (addr_t)precvbuf->pskb->data;
+ alignment = tmpaddr & (RECVBUFF_ALIGN_SZ-1);
+ skb_reserve(precvbuf->pskb,
+ (RECVBUFF_ALIGN_SZ - alignment));
+ precvbuf->phead = precvbuf->pskb->head;
+ precvbuf->pdata = precvbuf->pskb->data;
+ precvbuf->ptail = skb_tail_pointer(precvbuf->pskb);
+ precvbuf->pend = skb_end_pointer(precvbuf->pskb);
+ precvbuf->pbuf = precvbuf->pskb->data;
+ } else { /* reuse skb */
+ precvbuf->phead = precvbuf->pskb->head;
+ precvbuf->pdata = precvbuf->pskb->data;
+ precvbuf->ptail = skb_tail_pointer(precvbuf->pskb);
+ precvbuf->pend = skb_end_pointer(precvbuf->pskb);
+ precvbuf->pbuf = precvbuf->pskb->data;
+ precvbuf->reuse = false;
+ }
+ purb = precvbuf->purb;
+ /* translate DMA FIFO addr to pipehandle */
+ pipe = ffaddr2pipehdl(pdvobj, addr);
+ usb_fill_bulk_urb(purb, pusbd, pipe,
+ precvbuf->pbuf, MAX_RECVBUF_SZ,
+ r8712_usb_read_port_complete,
+ precvbuf);
+ err = usb_submit_urb(purb, GFP_ATOMIC);
+ if ((err) && (err != (-EPERM)))
+ ret = _FAIL;
+ } else
+ ret = _FAIL;
+ return ret;
+}
+
+void r8712_usb_read_port_cancel(struct _adapter *padapter)
+{
+ int i;
+ struct recv_buf *precvbuf;
+
+ precvbuf = (struct recv_buf *)padapter->recvpriv.precv_buf;
+ for (i = 0; i < NR_RECVBUFF; i++) {
+ if (precvbuf->purb)
+ usb_kill_urb(precvbuf->purb);
+ precvbuf++;
+ }
+}
+
+void r8712_xmit_bh(void *priv)
+{
+ int ret = false;
+ struct _adapter *padapter = (struct _adapter *)priv;
+ struct xmit_priv *pxmitpriv = &padapter->xmitpriv;
+
+ if ((padapter->bDriverStopped == true) ||
+ (padapter->bSurpriseRemoved == true)) {
+ netdev_err(padapter->pnetdev, "xmit_bh => bDriverStopped or bSurpriseRemoved\n");
+ return;
+ }
+ ret = r8712_xmitframe_complete(padapter, pxmitpriv, NULL);
+ if (ret == false)
+ return;
+ tasklet_hi_schedule(&pxmitpriv->xmit_tasklet);
+}
+
+static void usb_write_port_complete(struct urb *purb)
+{
+ int i;
+ struct xmit_frame *pxmitframe = (struct xmit_frame *)purb->context;
+ struct xmit_buf *pxmitbuf = pxmitframe->pxmitbuf;
+ struct _adapter *padapter = pxmitframe->padapter;
+ struct xmit_priv *pxmitpriv = &padapter->xmitpriv;
+ struct pkt_attrib *pattrib = &pxmitframe->attrib;
+
+ switch (pattrib->priority) {
+ case 1:
+ case 2:
+ pxmitpriv->bkq_cnt--;
+ break;
+ case 4:
+ case 5:
+ pxmitpriv->viq_cnt--;
+ break;
+ case 6:
+ case 7:
+ pxmitpriv->voq_cnt--;
+ break;
+ case 0:
+ case 3:
+ default:
+ pxmitpriv->beq_cnt--;
+ break;
+ }
+ pxmitpriv->txirp_cnt--;
+ for (i = 0; i < 8; i++) {
+ if (purb == pxmitframe->pxmit_urb[i]) {
+ pxmitframe->bpending[i] = false;
+ break;
+ }
+ }
+ if (padapter->bSurpriseRemoved)
+ return;
+ switch (purb->status) {
+ case 0:
+ break;
+ default:
+ netdev_warn(padapter->pnetdev,
+ "r8712u: pipe error: (%d)\n", purb->status);
+ break;
+ }
+ /* not to consider tx fragment */
+ r8712_free_xmitframe_ex(pxmitpriv, pxmitframe);
+ r8712_free_xmitbuf(pxmitpriv, pxmitbuf);
+ tasklet_hi_schedule(&pxmitpriv->xmit_tasklet);
+}
+
+u32 r8712_usb_write_port(struct intf_hdl *pintfhdl, u32 addr, u32 cnt, u8 *wmem)
+{
+ unsigned long irqL;
+ int i, status;
+ unsigned int pipe;
+ u32 ret, bwritezero;
+ struct urb *purb = NULL;
+ struct _adapter *padapter = (struct _adapter *)pintfhdl->adapter;
+ struct dvobj_priv *pdvobj = (struct dvobj_priv *)&padapter->dvobjpriv;
+ struct xmit_priv *pxmitpriv = &padapter->xmitpriv;
+ struct xmit_frame *pxmitframe = (struct xmit_frame *)wmem;
+ struct usb_device *pusbd = pdvobj->pusbdev;
+ struct pkt_attrib *pattrib = &pxmitframe->attrib;
+
+ if ((padapter->bDriverStopped) || (padapter->bSurpriseRemoved) ||
+ (padapter->pwrctrlpriv.pnp_bstop_trx))
+ return _FAIL;
+ for (i = 0; i < 8; i++) {
+ if (pxmitframe->bpending[i] == false) {
+ spin_lock_irqsave(&pxmitpriv->lock, irqL);
+ pxmitpriv->txirp_cnt++;
+ pxmitframe->bpending[i] = true;
+ switch (pattrib->priority) {
+ case 1:
+ case 2:
+ pxmitpriv->bkq_cnt++;
+ break;
+ case 4:
+ case 5:
+ pxmitpriv->viq_cnt++;
+ break;
+ case 6:
+ case 7:
+ pxmitpriv->voq_cnt++;
+ break;
+ case 0:
+ case 3:
+ default:
+ pxmitpriv->beq_cnt++;
+ break;
+ }
+ spin_unlock_irqrestore(&pxmitpriv->lock, irqL);
+ pxmitframe->sz[i] = (u16)cnt;
+ purb = pxmitframe->pxmit_urb[i];
+ break;
+ }
+ }
+ bwritezero = false;
+ if (pdvobj->ishighspeed) {
+ if (cnt > 0 && cnt % 512 == 0)
+ bwritezero = true;
+ } else {
+ if (cnt > 0 && cnt % 64 == 0)
+ bwritezero = true;
+ }
+ /* translate DMA FIFO addr to pipehandle */
+ pipe = ffaddr2pipehdl(pdvobj, addr);
+ if (pxmitpriv->free_xmitbuf_cnt%NR_XMITBUFF == 0)
+ purb->transfer_flags &= (~URB_NO_INTERRUPT);
+ else
+ purb->transfer_flags |= URB_NO_INTERRUPT;
+ if (bwritezero)
+ cnt += 8;
+ usb_fill_bulk_urb(purb, pusbd, pipe,
+ pxmitframe->mem_addr,
+ cnt, usb_write_port_complete,
+ pxmitframe); /* context is xmit_frame */
+ status = usb_submit_urb(purb, GFP_ATOMIC);
+ if (!status)
+ ret = _SUCCESS;
+ else
+ ret = _FAIL;
+ return ret;
+}
+
+void r8712_usb_write_port_cancel(struct _adapter *padapter)
+{
+ int i, j;
+ struct xmit_buf *pxmitbuf = (struct xmit_buf *)
+ padapter->xmitpriv.pxmitbuf;
+
+ for (i = 0; i < NR_XMITBUFF; i++) {
+ for (j = 0; j < 8; j++) {
+ if (pxmitbuf->pxmit_urb[j])
+ usb_kill_urb(pxmitbuf->pxmit_urb[j]);
+ }
+ pxmitbuf++;
+ }
+}
+
+int r8712_usbctrl_vendorreq(struct intf_priv *pintfpriv, u8 request, u16 value,
+ u16 index, void *pdata, u16 len, u8 requesttype)
+{
+ unsigned int pipe;
+ int status;
+ u8 reqtype;
+ struct dvobj_priv *pdvobjpriv = (struct dvobj_priv *)
+ pintfpriv->intf_dev;
+ struct usb_device *udev = pdvobjpriv->pusbdev;
+ /* For mstar platform, mstar suggests the address for USB IO
+ * should be 16 bytes alignment. Trying to fix it here.
+ */
+ u8 *palloc_buf, *pIo_buf;
+
+ palloc_buf = kmalloc((u32)len + 16, GFP_ATOMIC);
+ if (palloc_buf == NULL)
+ return -ENOMEM;
+ pIo_buf = palloc_buf + 16 - ((addr_t)(palloc_buf) & 0x0f);
+ if (requesttype == 0x01) {
+ pipe = usb_rcvctrlpipe(udev, 0); /* read_in */
+ reqtype = RTL871X_VENQT_READ;
+ } else {
+ pipe = usb_sndctrlpipe(udev, 0); /* write_out */
+ reqtype = RTL871X_VENQT_WRITE;
+ memcpy(pIo_buf, pdata, len);
+ }
+ status = usb_control_msg(udev, pipe, request, reqtype, value, index,
+ pIo_buf, len, HZ / 2);
+ if (status > 0) { /* Success this control transfer. */
+ if (requesttype == 0x01) {
+ /* For Control read transfer, we have to copy the read
+ * data from pIo_buf to pdata.
+ */
+ memcpy(pdata, pIo_buf, status);
+ }
+ }
+ kfree(palloc_buf);
+ return status;
+}
diff --git a/drivers/staging/rtl8712/usb_osintf.h b/drivers/staging/rtl8712/usb_osintf.h
new file mode 100644
index 000000000..609f9210c
--- /dev/null
+++ b/drivers/staging/rtl8712/usb_osintf.h
@@ -0,0 +1,47 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2010 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ * Modifications for inclusion into the Linux staging tree are
+ * Copyright(c) 2010 Larry Finger. All rights reserved.
+ *
+ * Contact information:
+ * WLAN FAE <wlanfae@realtek.com>
+ * Larry Finger <Larry.Finger@lwfinger.net>
+ *
+ ******************************************************************************/
+#ifndef __USB_OSINTF_H
+#define __USB_OSINTF_H
+
+#include "osdep_service.h"
+#include "drv_types.h"
+
+extern char *r8712_initmac;
+
+unsigned int r8712_usb_inirp_init(struct _adapter *padapter);
+unsigned int r8712_usb_inirp_deinit(struct _adapter *padapter);
+uint rtl871x_hal_init(struct _adapter *padapter);
+uint rtl8712_hal_deinit(struct _adapter *padapter);
+
+void rtl871x_intf_stop(struct _adapter *padapter);
+void r871x_dev_unload(struct _adapter *padapter);
+void r8712_stop_drv_threads(struct _adapter *padapter);
+void r8712_stop_drv_timers(struct _adapter *padapter);
+u8 r8712_init_drv_sw(struct _adapter *padapter);
+u8 r8712_free_drv_sw(struct _adapter *padapter);
+struct net_device *r8712_init_netdev(void);
+
+#endif
diff --git a/drivers/staging/rtl8712/wifi.h b/drivers/staging/rtl8712/wifi.h
new file mode 100644
index 000000000..17f513122
--- /dev/null
+++ b/drivers/staging/rtl8712/wifi.h
@@ -0,0 +1,594 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2010 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ * Modifications for inclusion into the Linux staging tree are
+ * Copyright(c) 2010 Larry Finger. All rights reserved.
+ *
+ * Contact information:
+ * WLAN FAE <wlanfae@realtek.com>
+ * Larry Finger <Larry.Finger@lwfinger.net>
+ *
+ ******************************************************************************/
+#ifndef _WIFI_H_
+#define _WIFI_H_
+
+#include <linux/compiler.h>
+
+#ifdef BIT
+#undef BIT
+#endif
+#define BIT(x) (1 << (x))
+
+#define WLAN_ETHHDR_LEN 14
+#define WLAN_ETHADDR_LEN 6
+#define WLAN_IEEE_OUI_LEN 3
+#define WLAN_ADDR_LEN 6
+#define WLAN_CRC_LEN 4
+#define WLAN_BSSID_LEN 6
+#define WLAN_BSS_TS_LEN 8
+#define WLAN_HDR_A3_LEN 24
+#define WLAN_HDR_A4_LEN 30
+#define WLAN_HDR_A3_QOS_LEN 26
+#define WLAN_HDR_A4_QOS_LEN 32
+#define WLAN_SSID_MAXLEN 32
+#define WLAN_DATA_MAXLEN 2312
+
+#define WLAN_A3_PN_OFFSET 24
+#define WLAN_A4_PN_OFFSET 30
+
+#define WLAN_MIN_ETHFRM_LEN 60
+#define WLAN_MAX_ETHFRM_LEN 1514
+#define WLAN_ETHHDR_LEN 14
+
+#define P80211CAPTURE_VERSION 0x80211001
+
+enum WIFI_FRAME_TYPE {
+ WIFI_MGT_TYPE = (0),
+ WIFI_CTRL_TYPE = (BIT(2)),
+ WIFI_DATA_TYPE = (BIT(3)),
+ WIFI_QOS_DATA_TYPE = (BIT(7)|BIT(3)), /*!< QoS Data */
+};
+
+enum WIFI_FRAME_SUBTYPE {
+ /* below is for mgt frame */
+ WIFI_ASSOCREQ = (0 | WIFI_MGT_TYPE),
+ WIFI_ASSOCRSP = (BIT(4) | WIFI_MGT_TYPE),
+ WIFI_REASSOCREQ = (BIT(5) | WIFI_MGT_TYPE),
+ WIFI_REASSOCRSP = (BIT(5) | BIT(4) | WIFI_MGT_TYPE),
+ WIFI_PROBEREQ = (BIT(6) | WIFI_MGT_TYPE),
+ WIFI_PROBERSP = (BIT(6) | BIT(4) | WIFI_MGT_TYPE),
+ WIFI_BEACON = (BIT(7) | WIFI_MGT_TYPE),
+ WIFI_ATIM = (BIT(7) | BIT(4) | WIFI_MGT_TYPE),
+ WIFI_DISASSOC = (BIT(7) | BIT(5) | WIFI_MGT_TYPE),
+ WIFI_AUTH = (BIT(7) | BIT(5) | BIT(4) | WIFI_MGT_TYPE),
+ WIFI_DEAUTH = (BIT(7) | BIT(6) | WIFI_MGT_TYPE),
+ WIFI_ACTION = (BIT(7) | BIT(6) | BIT(4) | WIFI_MGT_TYPE),
+ /* below is for control frame */
+ WIFI_PSPOLL = (BIT(7) | BIT(5) | WIFI_CTRL_TYPE),
+ WIFI_RTS = (BIT(7) | BIT(5) | BIT(4) | WIFI_CTRL_TYPE),
+ WIFI_CTS = (BIT(7) | BIT(6) | WIFI_CTRL_TYPE),
+ WIFI_ACK = (BIT(7) | BIT(6) | BIT(4) | WIFI_CTRL_TYPE),
+ WIFI_CFEND = (BIT(7) | BIT(6) | BIT(5) | WIFI_CTRL_TYPE),
+ WIFI_CFEND_CFACK = (BIT(7) | BIT(6) | BIT(5) | BIT(4) | WIFI_CTRL_TYPE),
+ /* below is for data frame */
+ WIFI_DATA = (0 | WIFI_DATA_TYPE),
+ WIFI_DATA_CFACK = (BIT(4) | WIFI_DATA_TYPE),
+ WIFI_DATA_CFPOLL = (BIT(5) | WIFI_DATA_TYPE),
+ WIFI_DATA_CFACKPOLL = (BIT(5) | BIT(4) | WIFI_DATA_TYPE),
+ WIFI_DATA_NULL = (BIT(6) | WIFI_DATA_TYPE),
+ WIFI_CF_ACK = (BIT(6) | BIT(4) | WIFI_DATA_TYPE),
+ WIFI_CF_POLL = (BIT(6) | BIT(5) | WIFI_DATA_TYPE),
+ WIFI_CF_ACKPOLL = (BIT(6) | BIT(5) | BIT(4) | WIFI_DATA_TYPE),
+};
+
+enum WIFI_REASON_CODE {
+ _RSON_RESERVED_ = 0,
+ _RSON_UNSPECIFIED_ = 1,
+ _RSON_AUTH_NO_LONGER_VALID_ = 2,
+ _RSON_DEAUTH_STA_LEAVING_ = 3,
+ _RSON_INACTIVITY_ = 4,
+ _RSON_UNABLE_HANDLE_ = 5,
+ _RSON_CLS2_ = 6,
+ _RSON_CLS3_ = 7,
+ _RSON_DISAOC_STA_LEAVING_ = 8,
+ _RSON_ASOC_NOT_AUTH_ = 9,
+ /* WPA reason */
+ _RSON_INVALID_IE_ = 13,
+ _RSON_MIC_FAILURE_ = 14,
+ _RSON_4WAY_HNDSHK_TIMEOUT_ = 15,
+ _RSON_GROUP_KEY_UPDATE_TIMEOUT_ = 16,
+ _RSON_DIFF_IE_ = 17,
+ _RSON_MLTCST_CIPHER_NOT_VALID_ = 18,
+ _RSON_UNICST_CIPHER_NOT_VALID_ = 19,
+ _RSON_AKMP_NOT_VALID_ = 20,
+ _RSON_UNSUPPORT_RSNE_VER_ = 21,
+ _RSON_INVALID_RSNE_CAP_ = 22,
+ _RSON_IEEE_802DOT1X_AUTH_FAIL_ = 23,
+ /* below are Realtek definitions */
+ _RSON_PMK_NOT_AVAILABLE_ = 24,
+};
+
+enum WIFI_STATUS_CODE {
+ _STATS_SUCCESSFUL_ = 0,
+ _STATS_FAILURE_ = 1,
+ _STATS_CAP_FAIL_ = 10,
+ _STATS_NO_ASOC_ = 11,
+ _STATS_OTHER_ = 12,
+ _STATS_NO_SUPP_ALG_ = 13,
+ _STATS_OUT_OF_AUTH_SEQ_ = 14,
+ _STATS_CHALLENGE_FAIL_ = 15,
+ _STATS_AUTH_TIMEOUT_ = 16,
+ _STATS_UNABLE_HANDLE_STA_ = 17,
+ _STATS_RATE_FAIL_ = 18,
+};
+
+enum WIFI_REG_DOMAIN {
+ DOMAIN_FCC = 1,
+ DOMAIN_IC = 2,
+ DOMAIN_ETSI = 3,
+ DOMAIN_SPAIN = 4,
+ DOMAIN_FRANCE = 5,
+ DOMAIN_MKK = 6,
+ DOMAIN_ISRAEL = 7,
+ DOMAIN_MKK1 = 8,
+ DOMAIN_MKK2 = 9,
+ DOMAIN_MKK3 = 10,
+ DOMAIN_MAX
+};
+
+#define _TO_DS_ BIT(8)
+#define _FROM_DS_ BIT(9)
+#define _MORE_FRAG_ BIT(10)
+#define _RETRY_ BIT(11)
+#define _PWRMGT_ BIT(12)
+#define _MORE_DATA_ BIT(13)
+#define _PRIVACY_ BIT(14)
+#define _ORDER_ BIT(15)
+
+#define SetToDs(pbuf) ({ \
+ *(unsigned short *)(pbuf) |= cpu_to_le16(_TO_DS_); \
+})
+
+#define GetToDs(pbuf) (((*(unsigned short *)(pbuf)) & \
+ le16_to_cpu(_TO_DS_)) != 0)
+
+#define ClearToDs(pbuf) ({ \
+ *(unsigned short *)(pbuf) &= (~cpu_to_le16(_TO_DS_)); \
+})
+
+#define SetFrDs(pbuf) ({ \
+ *(unsigned short *)(pbuf) |= cpu_to_le16(_FROM_DS_); \
+})
+
+#define GetFrDs(pbuf) (((*(unsigned short *)(pbuf)) & \
+ le16_to_cpu(_FROM_DS_)) != 0)
+
+#define ClearFrDs(pbuf) ({ \
+ *(unsigned short *)(pbuf) &= (~cpu_to_le16(_FROM_DS_)); \
+})
+
+#define get_tofr_ds(pframe) ((GetToDs(pframe) << 1) | GetFrDs(pframe))
+
+
+#define SetMFrag(pbuf) ({ \
+ *(unsigned short *)(pbuf) |= cpu_to_le16(_MORE_FRAG_); \
+})
+
+#define GetMFrag(pbuf) (((*(unsigned short *)(pbuf)) & \
+ le16_to_cpu(_MORE_FRAG_)) != 0)
+
+#define ClearMFrag(pbuf) ({ \
+ *(unsigned short *)(pbuf) &= (~cpu_to_le16(_MORE_FRAG_)); \
+})
+
+#define SetRetry(pbuf) ({ \
+ *(unsigned short *)(pbuf) |= cpu_to_le16(_RETRY_); \
+})
+
+#define GetRetry(pbuf) (((*(unsigned short *)(pbuf)) & \
+ le16_to_cpu(_RETRY_)) != 0)
+
+#define ClearRetry(pbuf) ({ \
+ *(unsigned short *)(pbuf) &= (~cpu_to_le16(_RETRY_)); \
+})
+
+#define SetPwrMgt(pbuf) ({ \
+ *(unsigned short *)(pbuf) |= cpu_to_le16(_PWRMGT_); \
+})
+
+#define GetPwrMgt(pbuf) (((*(unsigned short *)(pbuf)) & \
+ le16_to_cpu(_PWRMGT_)) != 0)
+
+#define ClearPwrMgt(pbuf) ({ \
+ *(unsigned short *)(pbuf) &= (~cpu_to_le16(_PWRMGT_)); \
+})
+
+#define SetMData(pbuf) ({ \
+ *(unsigned short *)(pbuf) |= cpu_to_le16(_MORE_DATA_); \
+})
+
+#define GetMData(pbuf) (((*(unsigned short *)(pbuf)) & \
+ le16_to_cpu(_MORE_DATA_)) != 0)
+
+#define ClearMData(pbuf) ({ \
+ *(unsigned short *)(pbuf) &= (~cpu_to_le16(_MORE_DATA_)); \
+})
+
+#define SetPrivacy(pbuf) ({ \
+ *(unsigned short *)(pbuf) |= cpu_to_le16(_PRIVACY_); \
+})
+
+#define GetPrivacy(pbuf) (((*(unsigned short *)(pbuf)) & \
+ le16_to_cpu(_PRIVACY_)) != 0)
+
+#define GetOrder(pbuf) (((*(unsigned short *)(pbuf)) & \
+ le16_to_cpu(_ORDER_)) != 0)
+
+#define GetFrameType(pbuf) (le16_to_cpu(*(unsigned short *)(pbuf)) & \
+ (BIT(3) | BIT(2)))
+
+#define SetFrameType(pbuf, type) \
+ do { \
+ *(unsigned short *)(pbuf) &= cpu_to_le16(~(BIT(3) | \
+ BIT(2))); \
+ *(unsigned short *)(pbuf) |= cpu_to_le16(type); \
+ } while (0)
+
+#define GetFrameSubType(pbuf) (cpu_to_le16(*(unsigned short *)(pbuf)) & \
+ (BIT(7) | BIT(6) | BIT(5) | BIT(4) | BIT(3) | \
+ BIT(2)))
+
+#define SetFrameSubType(pbuf, type) \
+ do { \
+ *(unsigned short *)(pbuf) &= cpu_to_le16(~(BIT(7) | BIT(6) | \
+ BIT(5) | BIT(4) | BIT(3) | BIT(2))); \
+ *(unsigned short *)(pbuf) |= cpu_to_le16(type); \
+ } while (0)
+
+#define GetSequence(pbuf) (cpu_to_le16(*(unsigned short *)\
+ ((addr_t)(pbuf) + 22)) >> 4)
+
+#define GetFragNum(pbuf) (cpu_to_le16(*(unsigned short *)((addr_t)\
+ (pbuf) + 22)) & 0x0f)
+
+#define SetSeqNum(pbuf, num) ({ \
+ *(unsigned short *)((addr_t)(pbuf) + 22) = \
+ ((*(unsigned short *)((addr_t)(pbuf) + 22)) & \
+ le16_to_cpu((unsigned short)0x000f)) | \
+ le16_to_cpu((unsigned short)(0xfff0 & (num << 4))); \
+})
+
+#define SetDuration(pbuf, dur) ({ \
+ *(unsigned short *)((addr_t)(pbuf) + 2) |= \
+ cpu_to_le16(0xffff & (dur)); \
+})
+
+#define SetPriority(pbuf, tid) ({ \
+ *(unsigned short *)(pbuf) |= cpu_to_le16(tid & 0xf); \
+})
+
+#define GetPriority(pbuf) ((le16_to_cpu(*(unsigned short *)(pbuf))) & 0xf)
+
+#define SetAckpolicy(pbuf, ack) ({ \
+ *(unsigned short *)(pbuf) |= cpu_to_le16((ack & 3) << 5); \
+})
+
+#define GetAckpolicy(pbuf) (((le16_to_cpu(*(unsigned short *)pbuf)) >> 5) & 0x3)
+
+#define GetAMsdu(pbuf) (((le16_to_cpu(*(unsigned short *)pbuf)) >> 7) & 0x1)
+
+#define GetAid(pbuf) (cpu_to_le16(*(unsigned short *)((addr_t)(pbuf) + 2)) \
+ & 0x3fff)
+
+#define GetAddr1Ptr(pbuf) ((unsigned char *)((addr_t)(pbuf) + 4))
+
+#define GetAddr2Ptr(pbuf) ((unsigned char *)((addr_t)(pbuf) + 10))
+
+#define GetAddr3Ptr(pbuf) ((unsigned char *)((addr_t)(pbuf) + 16))
+
+#define GetAddr4Ptr(pbuf) ((unsigned char *)((addr_t)(pbuf) + 24))
+
+
+
+static inline int IS_MCAST(unsigned char *da)
+{
+ if ((*da) & 0x01)
+ return true;
+ else
+ return false;
+}
+
+
+static inline unsigned char *get_da(unsigned char *pframe)
+{
+ unsigned char *da;
+ unsigned int to_fr_ds = (GetToDs(pframe) << 1) | GetFrDs(pframe);
+
+ switch (to_fr_ds) {
+ case 0x00: /* ToDs=0, FromDs=0 */
+ da = GetAddr1Ptr(pframe);
+ break;
+ case 0x01: /* ToDs=0, FromDs=1 */
+ da = GetAddr1Ptr(pframe);
+ break;
+ case 0x02: /* ToDs=1, FromDs=0 */
+ da = GetAddr3Ptr(pframe);
+ break;
+ default: /* ToDs=1, FromDs=1 */
+ da = GetAddr3Ptr(pframe);
+ break;
+ }
+ return da;
+}
+
+
+static inline unsigned char *get_sa(unsigned char *pframe)
+{
+ unsigned char *sa;
+ unsigned int to_fr_ds = (GetToDs(pframe) << 1) | GetFrDs(pframe);
+
+ switch (to_fr_ds) {
+ case 0x00: /* ToDs=0, FromDs=0 */
+ sa = GetAddr2Ptr(pframe);
+ break;
+ case 0x01: /* ToDs=0, FromDs=1 */
+ sa = GetAddr3Ptr(pframe);
+ break;
+ case 0x02: /* ToDs=1, FromDs=0 */
+ sa = GetAddr2Ptr(pframe);
+ break;
+ default: /* ToDs=1, FromDs=1 */
+ sa = GetAddr4Ptr(pframe);
+ break;
+ }
+
+ return sa;
+}
+
+static inline unsigned char *get_hdr_bssid(unsigned char *pframe)
+{
+ unsigned char *sa;
+ unsigned int to_fr_ds = (GetToDs(pframe) << 1) | GetFrDs(pframe);
+
+ switch (to_fr_ds) {
+ case 0x00: /* ToDs=0, FromDs=0 */
+ sa = GetAddr3Ptr(pframe);
+ break;
+ case 0x01: /* ToDs=0, FromDs=1 */
+ sa = GetAddr2Ptr(pframe);
+ break;
+ case 0x02: /* ToDs=1, FromDs=0 */
+ sa = GetAddr1Ptr(pframe);
+ break;
+ default: /* ToDs=1, FromDs=1 */
+ sa = NULL;
+ break;
+ }
+ return sa;
+}
+
+
+
+/*-----------------------------------------------------------------------------
+ Below is for the security related definition
+------------------------------------------------------------------------------*/
+#define _RESERVED_FRAME_TYPE_ 0
+#define _SKB_FRAME_TYPE_ 2
+#define _PRE_ALLOCMEM_ 1
+#define _PRE_ALLOCHDR_ 3
+#define _PRE_ALLOCLLCHDR_ 4
+#define _PRE_ALLOCICVHDR_ 5
+#define _PRE_ALLOCMICHDR_ 6
+
+#define _SIFSTIME_ ((priv->pmib->BssType.net_work_type & \
+ WIRELESS_11A) ? 16 : 10)
+#define _ACKCTSLNG_ 14 /*14 bytes long, including crclng */
+#define _CRCLNG_ 4
+
+#define _ASOCREQ_IE_OFFSET_ 4 /* excluding wlan_hdr */
+#define _ASOCRSP_IE_OFFSET_ 6
+#define _REASOCREQ_IE_OFFSET_ 10
+#define _REASOCRSP_IE_OFFSET_ 6
+#define _PROBEREQ_IE_OFFSET_ 0
+#define _PROBERSP_IE_OFFSET_ 12
+#define _AUTH_IE_OFFSET_ 6
+#define _DEAUTH_IE_OFFSET_ 0
+#define _BEACON_IE_OFFSET_ 12
+
+#define _FIXED_IE_LENGTH_ _BEACON_IE_OFFSET_
+
+#define _SSID_IE_ 0
+#define _SUPPORTEDRATES_IE_ 1
+#define _DSSET_IE_ 3
+#define _IBSS_PARA_IE_ 6
+#define _ERPINFO_IE_ 42
+#define _EXT_SUPPORTEDRATES_IE_ 50
+
+#define _HT_CAPABILITY_IE_ 45
+#define _HT_EXTRA_INFO_IE_ 61
+#define _HT_ADD_INFO_IE_ 61 /* _HT_EXTRA_INFO_IE_ */
+
+#define _VENDOR_SPECIFIC_IE_ 221
+
+#define _RESERVED47_ 47
+
+
+/* ---------------------------------------------------------------------------
+ Below is the fixed elements...
+-----------------------------------------------------------------------------*/
+#define _AUTH_ALGM_NUM_ 2
+#define _AUTH_SEQ_NUM_ 2
+#define _BEACON_ITERVAL_ 2
+#define _CAPABILITY_ 2
+#define _CURRENT_APADDR_ 6
+#define _LISTEN_INTERVAL_ 2
+#define _RSON_CODE_ 2
+#define _ASOC_ID_ 2
+#define _STATUS_CODE_ 2
+#define _TIMESTAMP_ 8
+
+#define AUTH_ODD_TO 0
+#define AUTH_EVEN_TO 1
+
+#define WLAN_ETHCONV_ENCAP 1
+#define WLAN_ETHCONV_RFC1042 2
+#define WLAN_ETHCONV_8021h 3
+
+#define cap_ESS BIT(0)
+#define cap_IBSS BIT(1)
+#define cap_CFPollable BIT(2)
+#define cap_CFRequest BIT(3)
+#define cap_Privacy BIT(4)
+#define cap_ShortPremble BIT(5)
+
+/*-----------------------------------------------------------------------------
+ Below is the definition for 802.11i / 802.1x
+------------------------------------------------------------------------------*/
+#define _IEEE8021X_MGT_ 1 /*WPA */
+#define _IEEE8021X_PSK_ 2 /* WPA with pre-shared key */
+
+/*-----------------------------------------------------------------------------
+ Below is the definition for WMM
+------------------------------------------------------------------------------*/
+#define _WMM_IE_Length_ 7 /* for WMM STA */
+#define _WMM_Para_Element_Length_ 24
+
+
+/*-----------------------------------------------------------------------------
+ Below is the definition for 802.11n
+------------------------------------------------------------------------------*/
+
+/* block-ack parameters */
+#define IEEE80211_ADDBA_PARAM_POLICY_MASK 0x0002
+#define IEEE80211_ADDBA_PARAM_TID_MASK 0x003C
+#define IEEE80211_ADDBA_PARAM_BUF_SIZE_MASK 0xFFA0
+#define IEEE80211_DELBA_PARAM_TID_MASK 0xF000
+#define IEEE80211_DELBA_PARAM_INITIATOR_MASK 0x0800
+
+#define SetOrderBit(pbuf) ({ \
+ *(unsigned short *)(pbuf) |= cpu_to_le16(_ORDER_); \
+})
+
+#define GetOrderBit(pbuf) (((*(unsigned short *)(pbuf)) & \
+ le16_to_cpu(_ORDER_)) != 0)
+
+
+/**
+ * struct ieee80211_bar - HT Block Ack Request
+ *
+ * This structure refers to "HT BlockAckReq" as
+ * described in 802.11n draft section 7.2.1.7.1
+ */
+struct ieee80211_bar {
+ unsigned short frame_control;
+ unsigned short duration;
+ unsigned char ra[6];
+ unsigned char ta[6];
+ unsigned short control;
+ unsigned short start_seq_num;
+} __packed;
+
+/* 802.11 BAR control masks */
+#define IEEE80211_BAR_CTRL_ACK_POLICY_NORMAL 0x0000
+#define IEEE80211_BAR_CTRL_CBMTID_COMPRESSED_BA 0x0004
+
+
+ /**
+ * struct ieee80211_ht_cap - HT capabilities
+ *
+ * This structure refers to "HT capabilities element" as
+ * described in 802.11n draft section 7.3.2.52
+ */
+
+struct ieee80211_ht_cap {
+ unsigned short cap_info;
+ unsigned char ampdu_params_info;
+ unsigned char supp_mcs_set[16];
+ unsigned short extended_ht_cap_info;
+ unsigned int tx_BF_cap_info;
+ unsigned char antenna_selection_info;
+} __packed;
+
+/**
+ * struct ieee80211_ht_cap - HT additional information
+ *
+ * This structure refers to "HT information element" as
+ * described in 802.11n draft section 7.3.2.53
+ */
+struct ieee80211_ht_addt_info {
+ unsigned char control_chan;
+ unsigned char ht_param;
+ unsigned short operation_mode;
+ unsigned short stbc_param;
+ unsigned char basic_set[16];
+} __packed;
+
+/* 802.11n HT capabilities masks */
+#define IEEE80211_HT_CAP_SUP_WIDTH 0x0002
+#define IEEE80211_HT_CAP_SM_PS 0x000C
+#define IEEE80211_HT_CAP_GRN_FLD 0x0010
+#define IEEE80211_HT_CAP_SGI_20 0x0020
+#define IEEE80211_HT_CAP_SGI_40 0x0040
+#define IEEE80211_HT_CAP_TX_STBC 0x0080
+#define IEEE80211_HT_CAP_DELAY_BA 0x0400
+#define IEEE80211_HT_CAP_MAX_AMSDU 0x0800
+#define IEEE80211_HT_CAP_DSSSCCK40 0x1000
+/* 802.11n HT capability AMPDU settings */
+#define IEEE80211_HT_CAP_AMPDU_FACTOR 0x03
+#define IEEE80211_HT_CAP_AMPDU_DENSITY 0x1C
+/* 802.11n HT capability MSC set */
+#define IEEE80211_SUPP_MCS_SET_UEQM 4
+#define IEEE80211_HT_CAP_MAX_STREAMS 4
+#define IEEE80211_SUPP_MCS_SET_LEN 10
+/* maximum streams the spec allows */
+#define IEEE80211_HT_CAP_MCS_TX_DEFINED 0x01
+#define IEEE80211_HT_CAP_MCS_TX_RX_DIFF 0x02
+#define IEEE80211_HT_CAP_MCS_TX_STREAMS 0x0C
+#define IEEE80211_HT_CAP_MCS_TX_UEQM 0x10
+/* 802.11n HT IE masks */
+#define IEEE80211_HT_IE_CHA_SEC_OFFSET 0x03
+#define IEEE80211_HT_IE_CHA_SEC_NONE 0x00
+#define IEEE80211_HT_IE_CHA_SEC_ABOVE 0x01
+#define IEEE80211_HT_IE_CHA_SEC_BELOW 0x03
+#define IEEE80211_HT_IE_CHA_WIDTH 0x04
+#define IEEE80211_HT_IE_HT_PROTECTION 0x0003
+#define IEEE80211_HT_IE_NON_GF_STA_PRSNT 0x0004
+#define IEEE80211_HT_IE_NON_HT_STA_PRSNT 0x0010
+
+/* block-ack parameters */
+#define IEEE80211_ADDBA_PARAM_POLICY_MASK 0x0002
+#define IEEE80211_ADDBA_PARAM_TID_MASK 0x003C
+#define IEEE80211_ADDBA_PARAM_BUF_SIZE_MASK 0xFFA0
+#define IEEE80211_DELBA_PARAM_TID_MASK 0xF000
+#define IEEE80211_DELBA_PARAM_INITIATOR_MASK 0x0800
+
+/*
+ * A-PMDU buffer sizes
+ * According to IEEE802.11n spec size varies from 8K to 64K (in powers of 2)
+ */
+#define IEEE80211_MIN_AMPDU_BUF 0x8
+#define IEEE80211_MAX_AMPDU_BUF 0x40
+
+
+/* Spatial Multiplexing Power Save Modes */
+#define WLAN_HT_CAP_SM_PS_STATIC 0
+#define WLAN_HT_CAP_SM_PS_DYNAMIC 1
+#define WLAN_HT_CAP_SM_PS_INVALID 2
+#define WLAN_HT_CAP_SM_PS_DISABLED 3
+
+#endif /* _WIFI_H_ */
+
diff --git a/drivers/staging/rtl8712/wlan_bssdef.h b/drivers/staging/rtl8712/wlan_bssdef.h
new file mode 100644
index 000000000..2ea8a3d6b
--- /dev/null
+++ b/drivers/staging/rtl8712/wlan_bssdef.h
@@ -0,0 +1,267 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2010 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ * Modifications for inclusion into the Linux staging tree are
+ * Copyright(c) 2010 Larry Finger. All rights reserved.
+ *
+ * Contact information:
+ * WLAN FAE <wlanfae@realtek.com>
+ * Larry Finger <Larry.Finger@lwfinger.net>
+ *
+ ******************************************************************************/
+#ifndef __WLAN_BSSDEF_H__
+#define __WLAN_BSSDEF_H__
+
+#define MAX_IE_SZ 768
+
+#define NDIS_802_11_LENGTH_SSID 32
+#define NDIS_802_11_LENGTH_RATES 8
+#define NDIS_802_11_LENGTH_RATES_EX 16
+
+/* Set of 8 data rates*/
+typedef unsigned char NDIS_802_11_RATES[NDIS_802_11_LENGTH_RATES];
+/* Set of 16 data rates */
+typedef unsigned char NDIS_802_11_RATES_EX[NDIS_802_11_LENGTH_RATES_EX];
+
+struct ndis_802_11_ssid {
+ u32 SsidLength;
+ u8 Ssid[32];
+};
+
+enum NDIS_802_11_NETWORK_TYPE {
+ Ndis802_11FH,
+ Ndis802_11DS,
+ Ndis802_11OFDM5,
+ Ndis802_11OFDM24,
+ Ndis802_11NetworkTypeMax /* not a real type, defined as an upper bound*/
+};
+
+struct NDIS_802_11_CONFIGURATION_FH {
+ u32 Length; /* Length of structure */
+ u32 HopPattern; /* As defined by 802.11, MSB set */
+ u32 HopSet; /* to one if non-802.11 */
+ u32 DwellTime; /* units are Kusec */
+};
+
+/*
+ FW will only save the channel number in DSConfig.
+ ODI Handler will convert the channel number to freq. number.
+*/
+struct NDIS_802_11_CONFIGURATION {
+ u32 Length; /* Length of structure */
+ u32 BeaconPeriod; /* units are Kusec */
+ u32 ATIMWindow; /* units are Kusec */
+ u32 DSConfig; /* Frequency, units are kHz */
+ struct NDIS_802_11_CONFIGURATION_FH FHConfig;
+};
+
+enum NDIS_802_11_NETWORK_INFRASTRUCTURE {
+ Ndis802_11IBSS,
+ Ndis802_11Infrastructure,
+ Ndis802_11AutoUnknown,
+ Ndis802_11InfrastructureMax, /*Not a real value,defined as upper bound*/
+ Ndis802_11APMode
+};
+
+struct NDIS_802_11_FIXED_IEs {
+ u8 Timestamp[8];
+ u16 BeaconInterval;
+ u16 Capabilities;
+};
+
+/*
+ * Length is the 4 bytes multiples of the sume of
+ * 6 * sizeof (unsigned char) + 2 + sizeof (ndis_802_11_ssid) + sizeof (u32)
+ * + sizeof (s32) + sizeof (NDIS_802_11_NETWORK_TYPE)
+ * + sizeof (struct NDIS_802_11_CONFIGURATION)
+ * + sizeof (NDIS_802_11_RATES_EX) + IELength
+
+ * Except the IELength, all other fields are fixed length. Therefore, we can
+ * define a macro to present the partial sum.
+ */
+
+struct ndis_wlan_bssid_ex {
+ u32 Length;
+ unsigned char MacAddress[6];
+ u8 Reserved[2];
+ struct ndis_802_11_ssid Ssid;
+ u32 Privacy;
+ s32 Rssi;
+ enum NDIS_802_11_NETWORK_TYPE NetworkTypeInUse;
+ struct NDIS_802_11_CONFIGURATION Configuration;
+ enum NDIS_802_11_NETWORK_INFRASTRUCTURE InfrastructureMode;
+ NDIS_802_11_RATES_EX SupportedRates;
+ u32 IELength;
+ /*(timestamp, beacon interval, and capability information) */
+ u8 IEs[MAX_IE_SZ];
+};
+
+enum NDIS_802_11_AUTHENTICATION_MODE {
+ Ndis802_11AuthModeOpen,
+ Ndis802_11AuthModeShared,
+ Ndis802_11AuthModeAutoSwitch,
+ Ndis802_11AuthModeWPA,
+ Ndis802_11AuthModeWPAPSK,
+ Ndis802_11AuthModeWPANone,
+ Ndis802_11AuthModeMax /* Not a real mode, defined as upper bound */
+};
+
+enum {
+ Ndis802_11WEPEnabled,
+ Ndis802_11Encryption1Enabled = Ndis802_11WEPEnabled,
+ Ndis802_11WEPDisabled,
+ Ndis802_11EncryptionDisabled = Ndis802_11WEPDisabled,
+ Ndis802_11WEPKeyAbsent,
+ Ndis802_11Encryption1KeyAbsent = Ndis802_11WEPKeyAbsent,
+ Ndis802_11WEPNotSupported,
+ Ndis802_11EncryptionNotSupported = Ndis802_11WEPNotSupported,
+ Ndis802_11Encryption2Enabled,
+ Ndis802_11Encryption2KeyAbsent,
+ Ndis802_11Encryption3Enabled,
+ Ndis802_11Encryption3KeyAbsent
+};
+
+#define NDIS_802_11_AI_REQFI_CAPABILITIES 1
+#define NDIS_802_11_AI_REQFI_LISTENINTERVAL 2
+#define NDIS_802_11_AI_REQFI_CURRENTAPADDRESS 4
+
+#define NDIS_802_11_AI_RESFI_CAPABILITIES 1
+#define NDIS_802_11_AI_RESFI_STATUSCODE 2
+#define NDIS_802_11_AI_RESFI_ASSOCIATIONID 4
+
+struct NDIS_802_11_AI_REQFI {
+ u16 Capabilities;
+ u16 ListenInterval;
+ unsigned char CurrentAPAddress[6];
+};
+
+struct NDIS_802_11_AI_RESFI {
+ u16 Capabilities;
+ u16 StatusCode;
+ u16 AssociationId;
+};
+
+struct NDIS_802_11_ASSOCIATION_INFORMATION {
+ u32 Length;
+ u16 AvailableRequestFixedIEs;
+ struct NDIS_802_11_AI_REQFI RequestFixedIEs;
+ u32 RequestIELength;
+ u32 OffsetRequestIEs;
+ u16 AvailableResponseFixedIEs;
+ struct NDIS_802_11_AI_RESFI ResponseFixedIEs;
+ u32 ResponseIELength;
+ u32 OffsetResponseIEs;
+};
+
+/* Key mapping keys require a BSSID*/
+struct NDIS_802_11_KEY {
+ u32 Length; /* Length of this structure */
+ u32 KeyIndex;
+ u32 KeyLength; /* length of key in bytes */
+ unsigned char BSSID[6];
+ unsigned long long KeyRSC;
+ u8 KeyMaterial[32]; /* variable length */
+};
+
+struct NDIS_802_11_REMOVE_KEY {
+ u32 Length; /* Length of this structure */
+ u32 KeyIndex;
+ unsigned char BSSID[6];
+};
+
+struct NDIS_802_11_WEP {
+ u32 Length; /* Length of this structure */
+ u32 KeyIndex; /* 0 is the per-client key,
+ * 1-N are the global keys */
+ u32 KeyLength; /* length of key in bytes */
+ u8 KeyMaterial[16]; /* variable length depending on above field */
+};
+
+/* mask for authentication/integrity fields */
+#define NDIS_802_11_AUTH_REQUEST_AUTH_FIELDS 0x0f
+#define NDIS_802_11_AUTH_REQUEST_REAUTH 0x01
+#define NDIS_802_11_AUTH_REQUEST_KEYUPDATE 0x02
+#define NDIS_802_11_AUTH_REQUEST_PAIRWISE_ERROR 0x06
+#define NDIS_802_11_AUTH_REQUEST_GROUP_ERROR 0x0E
+
+/* MIC check time, 60 seconds. */
+#define MIC_CHECK_TIME 60000000
+
+#ifndef Ndis802_11APMode
+#define Ndis802_11APMode (Ndis802_11InfrastructureMax+1)
+#endif
+
+struct wlan_network {
+ struct list_head list;
+ int network_type; /*refer to ieee80211.h for WIRELESS_11A/B/G */
+ int fixed; /* set to fixed when not to be removed asi
+ * site-surveying */
+ unsigned int last_scanned; /*timestamp for the network */
+ int aid; /*will only be valid when a BSS is joined. */
+ int join_res;
+ struct ndis_wlan_bssid_ex network; /*must be the last item */
+};
+
+enum VRTL_CARRIER_SENSE {
+ DISABLE_VCS,
+ ENABLE_VCS,
+ AUTO_VCS
+};
+
+enum VCS_TYPE {
+ NONE_VCS,
+ RTS_CTS,
+ CTS_TO_SELF
+};
+
+#define PWR_CAM 0
+#define PWR_MINPS 1
+#define PWR_MAXPS 2
+#define PWR_UAPSD 3
+#define PWR_VOIP 4
+
+enum UAPSD_MAX_SP {
+ NO_LIMIT,
+ TWO_MSDU,
+ FOUR_MSDU,
+ SIX_MSDU
+};
+
+#define NUM_PRE_AUTH_KEY 16
+#define NUM_PMKID_CACHE NUM_PRE_AUTH_KEY
+
+/*
+ * WPA2
+ */
+struct wlan_bssid_ex {
+ u32 Length;
+ unsigned char MacAddress[6];
+ u8 Reserved[2];
+ struct ndis_802_11_ssid Ssid;
+ u32 Privacy;
+ s32 Rssi;
+ enum NDIS_802_11_NETWORK_TYPE NetworkTypeInUse;
+ struct NDIS_802_11_CONFIGURATION Configuration;
+ enum NDIS_802_11_NETWORK_INFRASTRUCTURE InfrastructureMode;
+ NDIS_802_11_RATES_EX SupportedRates;
+ u32 IELength;
+ u8 IEs[MAX_IE_SZ]; /* (timestamp, beacon interval, and capability
+ * information) */
+};
+
+#endif /* #ifndef WLAN_BSSDEF_H_ */
+
diff --git a/drivers/staging/rtl8712/xmit_linux.c b/drivers/staging/rtl8712/xmit_linux.c
new file mode 100644
index 000000000..d15fb1ad4
--- /dev/null
+++ b/drivers/staging/rtl8712/xmit_linux.c
@@ -0,0 +1,198 @@
+/******************************************************************************
+ * xmit_linux.c
+ *
+ * Copyright(c) 2007 - 2010 Realtek Corporation. All rights reserved.
+ * Linux device driver for RTL8192SU
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ * Modifications for inclusion into the Linux staging tree are
+ * Copyright(c) 2010 Larry Finger. All rights reserved.
+ *
+ * Contact information:
+ * WLAN FAE <wlanfae@realtek.com>
+ * Larry Finger <Larry.Finger@lwfinger.net>
+ *
+ ******************************************************************************/
+
+#define _XMIT_OSDEP_C_
+
+#include <linux/usb.h>
+#include <linux/ip.h>
+#include <linux/if_ether.h>
+
+#include "osdep_service.h"
+#include "drv_types.h"
+
+#include "wifi.h"
+#include "mlme_osdep.h"
+#include "xmit_osdep.h"
+#include "osdep_intf.h"
+
+static uint remainder_len(struct pkt_file *pfile)
+{
+ return (uint)(pfile->buf_len - ((addr_t)(pfile->cur_addr) -
+ (addr_t)(pfile->buf_start)));
+}
+
+void _r8712_open_pktfile(_pkt *pktptr, struct pkt_file *pfile)
+{
+ pfile->pkt = pktptr;
+ pfile->cur_addr = pfile->buf_start = pktptr->data;
+ pfile->pkt_len = pfile->buf_len = pktptr->len;
+ pfile->cur_buffer = pfile->buf_start;
+}
+
+uint _r8712_pktfile_read(struct pkt_file *pfile, u8 *rmem, uint rlen)
+{
+ uint len;
+
+ len = remainder_len(pfile);
+ len = (rlen > len) ? len : rlen;
+ if (rmem)
+ skb_copy_bits(pfile->pkt, pfile->buf_len - pfile->pkt_len,
+ rmem, len);
+ pfile->cur_addr += len;
+ pfile->pkt_len -= len;
+ return len;
+}
+
+sint r8712_endofpktfile(struct pkt_file *pfile)
+{
+ if (pfile->pkt_len == 0)
+ return true;
+ else
+ return false;
+}
+
+
+void r8712_set_qos(struct pkt_file *ppktfile, struct pkt_attrib *pattrib)
+{
+ struct ethhdr etherhdr;
+ struct iphdr ip_hdr;
+ u16 UserPriority = 0;
+
+ _r8712_open_pktfile(ppktfile->pkt, ppktfile);
+ _r8712_pktfile_read(ppktfile, (unsigned char *)&etherhdr, ETH_HLEN);
+
+ /* get UserPriority from IP hdr*/
+ if (pattrib->ether_type == 0x0800) {
+ _r8712_pktfile_read(ppktfile, (u8 *)&ip_hdr, sizeof(ip_hdr));
+ /*UserPriority = (ntohs(ip_hdr.tos) >> 5) & 0x3 ;*/
+ UserPriority = ip_hdr.tos >> 5;
+ } else {
+ /* "When priority processing of data frames is supported,
+ * a STA's SME should send EAPOL-Key frames at the highest
+ * priority." */
+
+ if (pattrib->ether_type == 0x888e)
+ UserPriority = 7;
+ }
+ pattrib->priority = UserPriority;
+ pattrib->hdrlen = WLAN_HDR_A3_QOS_LEN;
+ pattrib->subtype = WIFI_QOS_DATA_TYPE;
+}
+
+void r8712_SetFilter(struct work_struct *work)
+{
+ struct _adapter *padapter = container_of(work, struct _adapter,
+ wkFilterRxFF0);
+ u8 oldvalue = 0x00, newvalue = 0x00;
+ unsigned long irqL;
+
+ oldvalue = r8712_read8(padapter, 0x117);
+ newvalue = oldvalue & 0xfe;
+ r8712_write8(padapter, 0x117, newvalue);
+
+ spin_lock_irqsave(&padapter->lockRxFF0Filter, irqL);
+ padapter->blnEnableRxFF0Filter = 1;
+ spin_unlock_irqrestore(&padapter->lockRxFF0Filter, irqL);
+ do {
+ msleep(100);
+ } while (padapter->blnEnableRxFF0Filter == 1);
+ r8712_write8(padapter, 0x117, oldvalue);
+}
+
+int r8712_xmit_resource_alloc(struct _adapter *padapter,
+ struct xmit_buf *pxmitbuf)
+{
+ int i;
+
+ for (i = 0; i < 8; i++) {
+ pxmitbuf->pxmit_urb[i] = usb_alloc_urb(0, GFP_KERNEL);
+ if (pxmitbuf->pxmit_urb[i] == NULL) {
+ netdev_err(padapter->pnetdev, "pxmitbuf->pxmit_urb[i] == NULL\n");
+ return _FAIL;
+ }
+ }
+ return _SUCCESS;
+}
+
+void r8712_xmit_resource_free(struct _adapter *padapter,
+ struct xmit_buf *pxmitbuf)
+{
+ int i;
+
+ for (i = 0; i < 8; i++) {
+ if (pxmitbuf->pxmit_urb[i]) {
+ usb_kill_urb(pxmitbuf->pxmit_urb[i]);
+ usb_free_urb(pxmitbuf->pxmit_urb[i]);
+ }
+ }
+}
+
+void r8712_xmit_complete(struct _adapter *padapter, struct xmit_frame *pxframe)
+{
+ if (pxframe->pkt)
+ dev_kfree_skb_any(pxframe->pkt);
+ pxframe->pkt = NULL;
+}
+
+int r8712_xmit_entry(_pkt *pkt, struct net_device *pnetdev)
+{
+ struct xmit_frame *pxmitframe = NULL;
+ struct _adapter *padapter = netdev_priv(pnetdev);
+ struct xmit_priv *pxmitpriv = &(padapter->xmitpriv);
+ int ret = 0;
+
+ if (r8712_if_up(padapter) == false) {
+ ret = 0;
+ goto _xmit_entry_drop;
+ }
+ pxmitframe = r8712_alloc_xmitframe(pxmitpriv);
+ if (pxmitframe == NULL) {
+ ret = 0;
+ goto _xmit_entry_drop;
+ }
+ if ((!r8712_update_attrib(padapter, pkt, &pxmitframe->attrib))) {
+ ret = 0;
+ goto _xmit_entry_drop;
+ }
+ padapter->ledpriv.LedControlHandler(padapter, LED_CTL_TX);
+ pxmitframe->pkt = pkt;
+ if (r8712_pre_xmit(padapter, pxmitframe) == true) {
+ /*dump xmitframe directly or drop xframe*/
+ dev_kfree_skb_any(pkt);
+ pxmitframe->pkt = NULL;
+ }
+ pxmitpriv->tx_pkts++;
+ pxmitpriv->tx_bytes += pxmitframe->attrib.last_txcmdsz;
+ return ret;
+_xmit_entry_drop:
+ if (pxmitframe)
+ r8712_free_xmitframe(pxmitpriv, pxmitframe);
+ pxmitpriv->tx_drop++;
+ dev_kfree_skb_any(pkt);
+ return ret;
+}
diff --git a/drivers/staging/rtl8712/xmit_osdep.h b/drivers/staging/rtl8712/xmit_osdep.h
new file mode 100644
index 000000000..8eba7ca0d
--- /dev/null
+++ b/drivers/staging/rtl8712/xmit_osdep.h
@@ -0,0 +1,64 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2010 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ *
+ * Modifications for inclusion into the Linux staging tree are
+ * Copyright(c) 2010 Larry Finger. All rights reserved.
+ *
+ * Contact information:
+ * WLAN FAE <wlanfae@realtek.com>
+ * Larry Finger <Larry.Finger@lwfinger.net>
+ *
+ ******************************************************************************/
+#ifndef __XMIT_OSDEP_H_
+#define __XMIT_OSDEP_H_
+
+#include "osdep_service.h"
+#include "drv_types.h"
+
+struct pkt_file {
+ _pkt *pkt;
+ u32 pkt_len; /*the remainder length of the open_file*/
+ _buffer *cur_buffer;
+ u8 *buf_start;
+ u8 *cur_addr;
+ u32 buf_len;
+};
+
+#define NR_XMITFRAME 256
+
+struct xmit_priv;
+struct pkt_attrib;
+struct sta_xmit_priv;
+struct xmit_frame;
+struct xmit_buf;
+
+int r8712_xmit_entry(_pkt *pkt, struct net_device *pnetdev);
+void r8712_SetFilter(struct work_struct *work);
+int r8712_xmit_resource_alloc(struct _adapter *padapter,
+ struct xmit_buf *pxmitbuf);
+void r8712_xmit_resource_free(struct _adapter *padapter,
+ struct xmit_buf *pxmitbuf);
+
+void r8712_set_qos(struct pkt_file *ppktfile,
+ struct pkt_attrib *pattrib);
+void _r8712_open_pktfile(_pkt *pktptr, struct pkt_file *pfile);
+uint _r8712_pktfile_read(struct pkt_file *pfile, u8 *rmem, uint rlen);
+sint r8712_endofpktfile(struct pkt_file *pfile);
+void r8712_xmit_complete(struct _adapter *padapter,
+ struct xmit_frame *pxframe);
+
+#endif
diff --git a/drivers/staging/rtl8723au/Kconfig b/drivers/staging/rtl8723au/Kconfig
new file mode 100644
index 000000000..435f3594d
--- /dev/null
+++ b/drivers/staging/rtl8723au/Kconfig
@@ -0,0 +1,30 @@
+config R8723AU
+ tristate "Realtek RTL8723AU Wireless LAN NIC driver"
+ depends on USB && WLAN && RFKILL
+ select WIRELESS_EXT
+ select WEXT_PRIV
+ select CFG80211
+ default n
+ ---help---
+ This option adds the Realtek RTL8723AU USB device such as found in
+ the Lenovo Yogi 13 tablet. If built as a module, it will be called r8723au.
+
+if R8723AU
+
+config 8723AU_AP_MODE
+ bool "Realtek RTL8723AU AP mode"
+ default y
+ ---help---
+ This option enables Access Point mode. Unless you know that your system
+ will never be used as an AP, or the target system has limited memory,
+ "Y" should be selected.
+
+config 8723AU_BT_COEXIST
+ bool "Realtek RTL8723AU BlueTooth Coexistence"
+ default y
+ ---help---
+ This option enables icoexistence with BlueTooth communications for the r8723au driver.
+ Unless you know that this driver will never by used with BT, or the target system has
+ limited memory, "Y" should be selected.
+
+endif
diff --git a/drivers/staging/rtl8723au/Makefile b/drivers/staging/rtl8723au/Makefile
new file mode 100644
index 000000000..3e8989018
--- /dev/null
+++ b/drivers/staging/rtl8723au/Makefile
@@ -0,0 +1,53 @@
+r8723au-y := \
+ core/rtw_cmd.o \
+ core/rtw_efuse.o \
+ core/rtw_ieee80211.o \
+ core/rtw_mlme.o \
+ core/rtw_mlme_ext.o \
+ core/rtw_pwrctrl.o \
+ core/rtw_recv.o \
+ core/rtw_security.o \
+ core/rtw_sreset.o \
+ core/rtw_sta_mgt.o \
+ core/rtw_xmit.o \
+ core/rtw_wlan_util.o \
+ hal/hal_com.o \
+ hal/hal_intf.o \
+ hal/Hal8723PwrSeq.o \
+ hal/Hal8723UHWImg_CE.o \
+ hal/HalDMOutSrc8723A_CE.o \
+ hal/HalHWImg8723A_BB.o \
+ hal/HalHWImg8723A_MAC.o \
+ hal/HalHWImg8723A_RF.o \
+ hal/HalPwrSeqCmd.o \
+ hal/odm_RegConfig8723A.o \
+ hal/odm_debug.o \
+ hal/odm_interface.o \
+ hal/odm_HWConfig.o \
+ hal/odm.o \
+ hal/rtl8723a_cmd.o \
+ hal/rtl8723a_dm.o \
+ hal/rtl8723a_hal_init.o \
+ hal/rtl8723a_phycfg.o \
+ hal/rtl8723a_rf6052.o \
+ hal/rtl8723a_rxdesc.o \
+ hal/rtl8723a_sreset.o \
+ hal/rtl8723au_recv.o \
+ hal/rtl8723au_xmit.o \
+ hal/usb_halinit.o \
+ hal/usb_ops_linux.o \
+ os_dep/ioctl_cfg80211.o \
+ os_dep/mlme_linux.o \
+ os_dep/os_intfs.o \
+ os_dep/recv_linux.o \
+ os_dep/usb_intf.o \
+ os_dep/usb_ops_linux.o \
+ os_dep/xmit_linux.o
+
+r8723au-$(CONFIG_8723AU_BT_COEXIST) += hal/rtl8723a_bt-coexist.o
+r8723au-$(CONFIG_8723AU_AP_MODE) += core/rtw_ap.o
+
+obj-$(CONFIG_R8723AU) := r8723au.o
+
+ccflags-y += $(call cc-option,-Wtype-limits,)
+ccflags-y += -D__CHECK_ENDIAN__ -I$(src)/include
diff --git a/drivers/staging/rtl8723au/TODO b/drivers/staging/rtl8723au/TODO
new file mode 100644
index 000000000..175a0ceb7
--- /dev/null
+++ b/drivers/staging/rtl8723au/TODO
@@ -0,0 +1,13 @@
+TODO:
+- find and remove code valid only for 5 HGz. Many of the obvious
+ ones have been removed, but things like channel > 14 still exist.
+- find and remove any code for other chips that is left over
+- convert any remaining unusual variable types
+- find codes that can use %pM and %Nph formatting
+- checkpatch.pl fixes - most of the remaining ones are lines too long. Many
+ of them will require refactoring
+- merge Realtek's bugfixes and new features into the driver
+- switch to use MAC80211
+
+Please send any patches to Greg Kroah-Hartman <gregkh@linux.com>,
+Jes Sorensen <Jes.Sorensen@redhat.com>, and Larry Finger <Larry.Finger@lwfinger.net>.
diff --git a/drivers/staging/rtl8723au/core/rtw_ap.c b/drivers/staging/rtl8723au/core/rtw_ap.c
new file mode 100644
index 000000000..645668950
--- /dev/null
+++ b/drivers/staging/rtl8723au/core/rtw_ap.c
@@ -0,0 +1,1910 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2012 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ ******************************************************************************/
+#define _RTW_AP_C_
+
+#include <osdep_service.h>
+#include <drv_types.h>
+#include <linux/ieee80211.h>
+#include <wifi.h>
+#include <rtl8723a_cmd.h>
+#include <rtl8723a_hal.h>
+#include <asm/unaligned.h>
+
+extern unsigned char WMM_OUI23A[];
+extern unsigned char WPS_OUI23A[];
+extern unsigned char P2P_OUI23A[];
+
+void init_mlme_ap_info23a(struct rtw_adapter *padapter)
+{
+ struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
+ struct sta_priv *pstapriv = &padapter->stapriv;
+ struct wlan_acl_pool *pacl_list = &pstapriv->acl_list;
+
+ spin_lock_init(&pmlmepriv->bcn_update_lock);
+
+ /* for ACL */
+ _rtw_init_queue23a(&pacl_list->acl_node_q);
+
+ start_ap_mode23a(padapter);
+}
+
+void free_mlme_ap_info23a(struct rtw_adapter *padapter)
+{
+ struct sta_info *psta = NULL;
+ struct sta_priv *pstapriv = &padapter->stapriv;
+ struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
+ struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv;
+ struct mlme_ext_info *pmlmeinfo = &pmlmeext->mlmext_info;
+
+ pmlmepriv->update_bcn = false;
+ pmlmeext->bstart_bss = false;
+
+ rtw_sta_flush23a(padapter);
+
+ pmlmeinfo->state = MSR_NOLINK;
+
+ /* free_assoc_sta_resources */
+ rtw_free_all_stainfo23a(padapter);
+
+ /* free bc/mc sta_info */
+ psta = rtw_get_bcmc_stainfo23a(padapter);
+ spin_lock_bh(&pstapriv->sta_hash_lock);
+ rtw_free_stainfo23a(padapter, psta);
+ spin_unlock_bh(&pstapriv->sta_hash_lock);
+}
+
+static void update_BCNTIM(struct rtw_adapter *padapter)
+{
+ struct sta_priv *pstapriv = &padapter->stapriv;
+ struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv;
+ struct mlme_ext_info *pmlmeinfo = &pmlmeext->mlmext_info;
+ struct wlan_bssid_ex *pnetwork_mlmeext = &pmlmeinfo->network;
+ unsigned char *pie = pnetwork_mlmeext->IEs;
+ u8 *p, *dst_ie, *premainder_ie = NULL, *pbackup_remainder_ie = NULL;
+ uint offset, tmp_len, tim_ielen, tim_ie_offset, remainder_ielen;
+
+ p = rtw_get_ie23a(pie, WLAN_EID_TIM, &tim_ielen,
+ pnetwork_mlmeext->IELength);
+ if (p != NULL && tim_ielen > 0) {
+ tim_ielen += 2;
+
+ premainder_ie = p+tim_ielen;
+
+ tim_ie_offset = (int)(p - pie);
+
+ remainder_ielen = pnetwork_mlmeext->IELength - tim_ie_offset - tim_ielen;
+
+ /* append TIM IE from dst_ie offset */
+ dst_ie = p;
+ } else {
+ tim_ielen = 0;
+
+ /* calculate head_len */
+ offset = 0;
+
+ /* get ssid_ie len */
+ p = rtw_get_ie23a(pie, WLAN_EID_SSID,
+ &tmp_len, pnetwork_mlmeext->IELength);
+ if (p != NULL)
+ offset += tmp_len+2;
+
+ /* get supported rates len */
+ p = rtw_get_ie23a(pie, WLAN_EID_SUPP_RATES,
+ &tmp_len, pnetwork_mlmeext->IELength);
+ if (p != NULL)
+ offset += tmp_len+2;
+
+ /* DS Parameter Set IE, len = 3 */
+ offset += 3;
+
+ premainder_ie = pie + offset;
+
+ remainder_ielen = pnetwork_mlmeext->IELength - offset - tim_ielen;
+
+ /* append TIM IE from offset */
+ dst_ie = pie + offset;
+ }
+
+ if (remainder_ielen > 0) {
+ pbackup_remainder_ie = kmalloc(remainder_ielen, GFP_ATOMIC);
+ if (pbackup_remainder_ie && premainder_ie)
+ memcpy(pbackup_remainder_ie, premainder_ie, remainder_ielen);
+ }
+
+ *dst_ie++ = WLAN_EID_TIM;
+
+ if ((pstapriv->tim_bitmap&0xff00) && (pstapriv->tim_bitmap&0x00fc))
+ tim_ielen = 5;
+ else
+ tim_ielen = 4;
+
+ *dst_ie++ = tim_ielen;
+
+ *dst_ie++ = 0;/* DTIM count */
+ *dst_ie++ = 1;/* DTIM period */
+
+ if (pstapriv->tim_bitmap & BIT(0))/* for bc/mc frames */
+ *dst_ie++ = BIT(0);/* bitmap ctrl */
+ else
+ *dst_ie++ = 0;
+
+ if (tim_ielen == 4) {
+ *dst_ie++ = pstapriv->tim_bitmap & 0xff;
+ } else if (tim_ielen == 5) {
+ put_unaligned_le16(pstapriv->tim_bitmap, dst_ie);
+ dst_ie += 2;
+ }
+
+ /* copy remainder IE */
+ if (pbackup_remainder_ie) {
+ memcpy(dst_ie, pbackup_remainder_ie, remainder_ielen);
+
+ kfree(pbackup_remainder_ie);
+ }
+
+ offset = (uint)(dst_ie - pie);
+ pnetwork_mlmeext->IELength = offset + remainder_ielen;
+
+ set_tx_beacon_cmd23a(padapter);
+}
+
+static u8 chk_sta_is_alive(struct sta_info *psta)
+{
+ u8 ret = false;
+
+ if ((psta->sta_stats.last_rx_data_pkts +
+ psta->sta_stats.last_rx_ctrl_pkts) !=
+ (psta->sta_stats.rx_data_pkts + psta->sta_stats.rx_ctrl_pkts))
+ ret = true;
+
+ sta_update_last_rx_pkts(psta);
+
+ return ret;
+}
+
+void expire_timeout_chk23a(struct rtw_adapter *padapter)
+{
+ struct list_head *phead, *plist, *ptmp;
+ u8 updated = 0;
+ struct sta_info *psta;
+ struct sta_priv *pstapriv = &padapter->stapriv;
+ u8 chk_alive_num = 0;
+ struct sta_info *chk_alive_list[NUM_STA];
+ int i;
+
+ spin_lock_bh(&pstapriv->auth_list_lock);
+
+ phead = &pstapriv->auth_list;
+
+ /* check auth_queue */
+ list_for_each_safe(plist, ptmp, phead) {
+ psta = container_of(plist, struct sta_info, auth_list);
+
+ if (psta->expire_to > 0) {
+ psta->expire_to--;
+ if (psta->expire_to == 0) {
+ list_del_init(&psta->auth_list);
+ pstapriv->auth_list_cnt--;
+
+ DBG_8723A("auth expire %pM\n", psta->hwaddr);
+
+ spin_unlock_bh(&pstapriv->auth_list_lock);
+
+ spin_lock_bh(&pstapriv->sta_hash_lock);
+ rtw_free_stainfo23a(padapter, psta);
+ spin_unlock_bh(&pstapriv->sta_hash_lock);
+
+ spin_lock_bh(&pstapriv->auth_list_lock);
+ }
+ }
+
+ }
+
+ spin_unlock_bh(&pstapriv->auth_list_lock);
+
+ spin_lock_bh(&pstapriv->asoc_list_lock);
+
+ phead = &pstapriv->asoc_list;
+
+ /* check asoc_queue */
+ list_for_each_safe(plist, ptmp, phead) {
+ psta = container_of(plist, struct sta_info, asoc_list);
+
+ if (chk_sta_is_alive(psta) || !psta->expire_to) {
+ psta->expire_to = pstapriv->expire_to;
+ psta->keep_alive_trycnt = 0;
+ } else {
+ psta->expire_to--;
+ }
+
+ if (psta->expire_to <= 0) {
+ struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv;
+
+ if (padapter->registrypriv.wifi_spec == 1) {
+ psta->expire_to = pstapriv->expire_to;
+ continue;
+ }
+
+ if (psta->state & WIFI_SLEEP_STATE) {
+ if (!(psta->state & WIFI_STA_ALIVE_CHK_STATE)) {
+ /* to check if alive by another methods if station is at ps mode. */
+ psta->expire_to = pstapriv->expire_to;
+ psta->state |= WIFI_STA_ALIVE_CHK_STATE;
+
+ /* to update bcn with tim_bitmap for this station */
+ pstapriv->tim_bitmap |= CHKBIT(psta->aid);
+ update_beacon23a(padapter, WLAN_EID_TIM, NULL, false);
+
+ if (!pmlmeext->active_keep_alive_check)
+ continue;
+ }
+ }
+
+ if (pmlmeext->active_keep_alive_check) {
+ chk_alive_list[chk_alive_num++] = psta;
+ continue;
+ }
+
+ list_del_init(&psta->asoc_list);
+ pstapriv->asoc_list_cnt--;
+
+ DBG_8723A("asoc expire %pM, state = 0x%x\n",
+ psta->hwaddr, psta->state);
+ updated = ap_free_sta23a(padapter, psta, false, WLAN_REASON_DEAUTH_LEAVING);
+ } else {
+ /* TODO: Aging mechanism to digest frames in sleep_q to avoid running out of xmitframe */
+ if (psta->sleepq_len > (NR_XMITFRAME/pstapriv->asoc_list_cnt)
+ && padapter->xmitpriv.free_xmitframe_cnt < ((NR_XMITFRAME/pstapriv->asoc_list_cnt)/2)
+ ) {
+ DBG_8723A("%s sta:%pM, sleepq_len:%u, free_xmitframe_cnt:%u, asoc_list_cnt:%u, clear sleep_q\n",
+ __func__,
+ psta->hwaddr,
+ psta->sleepq_len,
+ padapter->xmitpriv.free_xmitframe_cnt,
+ pstapriv->asoc_list_cnt);
+ wakeup_sta_to_xmit23a(padapter, psta);
+ }
+ }
+ }
+
+ spin_unlock_bh(&pstapriv->asoc_list_lock);
+
+ if (chk_alive_num) {
+
+ u8 backup_oper_channel = 0;
+ struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv;
+ /* switch to correct channel of current network before issue keep-alive frames */
+ if (rtw_get_oper_ch23a(padapter) != pmlmeext->cur_channel) {
+ backup_oper_channel = rtw_get_oper_ch23a(padapter);
+ SelectChannel23a(padapter, pmlmeext->cur_channel);
+ }
+
+ /* issue null data to check sta alive*/
+ for (i = 0; i < chk_alive_num; i++) {
+
+ int ret = _FAIL;
+
+ psta = chk_alive_list[i];
+ if (!(psta->state & _FW_LINKED))
+ continue;
+
+ if (psta->state & WIFI_SLEEP_STATE)
+ ret = issue_nulldata23a(padapter, psta->hwaddr, 0, 1, 50);
+ else
+ ret = issue_nulldata23a(padapter, psta->hwaddr, 0, 3, 50);
+
+ psta->keep_alive_trycnt++;
+ if (ret == _SUCCESS) {
+ DBG_8723A("asoc check, sta(%pM) is alive\n",
+ psta->hwaddr);
+ psta->expire_to = pstapriv->expire_to;
+ psta->keep_alive_trycnt = 0;
+ continue;
+ } else if (psta->keep_alive_trycnt <= 3) {
+ DBG_8723A("ack check for asoc expire, keep_alive_trycnt =%d\n", psta->keep_alive_trycnt);
+ psta->expire_to = 1;
+ continue;
+ }
+
+ psta->keep_alive_trycnt = 0;
+
+ DBG_8723A("asoc expire %pM, state = 0x%x\n",
+ psta->hwaddr, psta->state);
+ spin_lock_bh(&pstapriv->asoc_list_lock);
+ if (!list_empty(&psta->asoc_list)) {
+ list_del_init(&psta->asoc_list);
+ pstapriv->asoc_list_cnt--;
+ updated = ap_free_sta23a(padapter, psta, false, WLAN_REASON_DEAUTH_LEAVING);
+ }
+ spin_unlock_bh(&pstapriv->asoc_list_lock);
+
+ }
+
+ if (backup_oper_channel > 0) /* back to the original operation channel */
+ SelectChannel23a(padapter, backup_oper_channel);
+}
+
+ associated_clients_update23a(padapter, updated);
+}
+
+void add_RATid23a(struct rtw_adapter *padapter, struct sta_info *psta, u8 rssi_level)
+{
+ int i;
+ u8 rf_type;
+ u32 init_rate = 0;
+ unsigned char sta_band = 0, raid, shortGIrate = false;
+ unsigned char limit;
+ unsigned int tx_ra_bitmap = 0;
+ struct ht_priv *psta_ht = NULL;
+ struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
+ struct wlan_bssid_ex *pcur_network = &pmlmepriv->cur_network.network;
+
+ if (psta)
+ psta_ht = &psta->htpriv;
+ else
+ return;
+
+ if (!(psta->state & _FW_LINKED))
+ return;
+
+ /* b/g mode ra_bitmap */
+ for (i = 0; i < sizeof(psta->bssrateset); i++) {
+ if (psta->bssrateset[i])
+ tx_ra_bitmap |= rtw_get_bit_value_from_ieee_value23a(psta->bssrateset[i]&0x7f);
+ }
+ /* n mode ra_bitmap */
+ if (psta_ht->ht_option) {
+ rf_type = rtl8723a_get_rf_type(padapter);
+
+ if (rf_type == RF_2T2R)
+ limit = 16;/* 2R */
+ else
+ limit = 8;/* 1R */
+
+ for (i = 0; i < limit; i++) {
+ if (psta_ht->ht_cap.mcs.rx_mask[i / 8] & BIT(i % 8))
+ tx_ra_bitmap |= BIT(i + 12);
+ }
+
+ /* max short GI rate */
+ shortGIrate = psta_ht->sgi;
+ }
+
+ if (pcur_network->DSConfig > 14) {
+ /* 5G band */
+ if (tx_ra_bitmap & 0xffff000)
+ sta_band |= WIRELESS_11_5N | WIRELESS_11A;
+ else
+ sta_band |= WIRELESS_11A;
+ } else {
+ if (tx_ra_bitmap & 0xffff000)
+ sta_band |= WIRELESS_11_24N | WIRELESS_11G | WIRELESS_11B;
+ else if (tx_ra_bitmap & 0xff0)
+ sta_band |= WIRELESS_11G | WIRELESS_11B;
+ else
+ sta_band |= WIRELESS_11B;
+ }
+
+ psta->wireless_mode = sta_band;
+
+ raid = networktype_to_raid23a(sta_band);
+ init_rate = get_highest_rate_idx23a(tx_ra_bitmap&0x0fffffff)&0x3f;
+
+ if (psta->aid < NUM_STA) {
+ u8 arg = 0;
+
+ arg = psta->mac_id&0x1f;
+
+ arg |= BIT(7);/* support entry 2~31 */
+
+ if (shortGIrate == true)
+ arg |= BIT(5);
+
+ tx_ra_bitmap |= ((raid<<28)&0xf0000000);
+
+ DBG_8723A("%s => mac_id:%d , raid:%d , bitmap = 0x%x, arg = "
+ "0x%x\n",
+ __func__, psta->mac_id, raid, tx_ra_bitmap, arg);
+
+ /* bitmap[0:27] = tx_rate_bitmap */
+ /* bitmap[28:31]= Rate Adaptive id */
+ /* arg[0:4] = macid */
+ /* arg[5] = Short GI */
+ rtl8723a_add_rateatid(padapter, tx_ra_bitmap, arg, rssi_level);
+
+ if (shortGIrate == true)
+ init_rate |= BIT(6);
+
+ /* set ra_id, init_rate */
+ psta->raid = raid;
+ psta->init_rate = init_rate;
+
+ } else
+ DBG_8723A("station aid %d exceed the max number\n", psta->aid);
+}
+
+static void update_bmc_sta(struct rtw_adapter *padapter)
+{
+ u32 init_rate = 0;
+ unsigned char network_type, raid;
+ int i, supportRateNum = 0;
+ unsigned int tx_ra_bitmap = 0;
+ struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
+ struct wlan_bssid_ex *pcur_network = &pmlmepriv->cur_network.network;
+ struct sta_info *psta = rtw_get_bcmc_stainfo23a(padapter);
+
+ if (psta) {
+ psta->aid = 0;/* default set to 0 */
+ psta->mac_id = psta->aid + 1;
+
+ psta->qos_option = 0;
+ psta->htpriv.ht_option = false;
+
+ psta->ieee8021x_blocked = 0;
+
+ memset((void *)&psta->sta_stats, 0,
+ sizeof(struct stainfo_stats));
+
+ /* prepare for add_RATid23a */
+ supportRateNum = rtw_get_rateset_len23a((u8 *)&pcur_network->SupportedRates);
+ network_type = rtw_check_network_type23a((u8 *)&pcur_network->SupportedRates, supportRateNum, 1);
+
+ memcpy(psta->bssrateset, &pcur_network->SupportedRates, supportRateNum);
+ psta->bssratelen = supportRateNum;
+
+ /* b/g mode ra_bitmap */
+ for (i = 0; i < supportRateNum; i++) {
+ if (psta->bssrateset[i])
+ tx_ra_bitmap |= rtw_get_bit_value_from_ieee_value23a(psta->bssrateset[i]&0x7f);
+ }
+
+ if (pcur_network->DSConfig > 14) {
+ /* force to A mode. 5G doesn't support CCK rates */
+ network_type = WIRELESS_11A;
+ tx_ra_bitmap = 0x150; /* 6, 12, 24 Mbps */
+ } else {
+ /* force to b mode */
+ network_type = WIRELESS_11B;
+ tx_ra_bitmap = 0xf;
+ }
+
+ raid = networktype_to_raid23a(network_type);
+ init_rate = get_highest_rate_idx23a(tx_ra_bitmap&0x0fffffff)&0x3f;
+
+ /* ap mode */
+ rtl8723a_SetHalODMVar(padapter, HAL_ODM_STA_INFO, psta, true);
+
+ {
+ u8 arg = 0;
+
+ arg = psta->mac_id&0x1f;
+
+ arg |= BIT(7);
+
+ tx_ra_bitmap |= ((raid<<28)&0xf0000000);
+
+ DBG_8723A("update_bmc_sta, mask = 0x%x, arg = 0x%x\n", tx_ra_bitmap, arg);
+
+ /* bitmap[0:27] = tx_rate_bitmap */
+ /* bitmap[28:31]= Rate Adaptive id */
+ /* arg[0:4] = macid */
+ /* arg[5] = Short GI */
+ rtl8723a_add_rateatid(padapter, tx_ra_bitmap, arg, 0);
+ }
+
+ /* set ra_id, init_rate */
+ psta->raid = raid;
+ psta->init_rate = init_rate;
+
+ spin_lock_bh(&psta->lock);
+ psta->state = _FW_LINKED;
+ spin_unlock_bh(&psta->lock);
+
+ } else
+ DBG_8723A("add_RATid23a_bmc_sta error!\n");
+}
+
+/* notes: */
+/* AID: 1~MAX for sta and 0 for bc/mc in ap/adhoc mode */
+/* MAC_ID = AID+1 for sta in ap/adhoc mode */
+/* MAC_ID = 1 for bc/mc for sta/ap/adhoc */
+/* MAC_ID = 0 for bssid for sta/ap/adhoc */
+/* CAM_ID = 0~3 for default key, cmd_id = macid + 3, macid = aid+1; */
+
+void update_sta_info23a_apmode23a(struct rtw_adapter *padapter, struct sta_info *psta)
+{
+ struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
+ struct security_priv *psecuritypriv = &padapter->securitypriv;
+ struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv;
+ struct ht_priv *phtpriv_ap = &pmlmepriv->htpriv;
+ struct ht_priv *phtpriv_sta = &psta->htpriv;
+ /* set intf_tag to if1 */
+
+ psta->mac_id = psta->aid+1;
+ DBG_8723A("%s\n", __func__);
+
+ /* ap mode */
+ rtl8723a_SetHalODMVar(padapter, HAL_ODM_STA_INFO, psta, true);
+
+ if (psecuritypriv->dot11AuthAlgrthm == dot11AuthAlgrthm_8021X)
+ psta->ieee8021x_blocked = true;
+ else
+ psta->ieee8021x_blocked = false;
+
+ /* update sta's cap */
+
+ /* ERP */
+ VCS_update23a(padapter, psta);
+ /* HT related cap */
+ if (phtpriv_sta->ht_option) {
+ /* check if sta supports rx ampdu */
+ phtpriv_sta->ampdu_enable = phtpriv_ap->ampdu_enable;
+
+ /* check if sta support s Short GI */
+ if ((phtpriv_sta->ht_cap.cap_info & phtpriv_ap->ht_cap.cap_info) & cpu_to_le16(IEEE80211_HT_CAP_SGI_20|IEEE80211_HT_CAP_SGI_40))
+ phtpriv_sta->sgi = true;
+
+ /* bwmode */
+ if ((phtpriv_sta->ht_cap.cap_info & phtpriv_ap->ht_cap.cap_info) & cpu_to_le16(IEEE80211_HT_CAP_SUP_WIDTH_20_40)) {
+ /* phtpriv_sta->bwmode = HT_CHANNEL_WIDTH_40; */
+ phtpriv_sta->bwmode = pmlmeext->cur_bwmode;
+ phtpriv_sta->ch_offset = pmlmeext->cur_ch_offset;
+
+ }
+
+ psta->qos_option = true;
+
+ } else {
+ phtpriv_sta->ampdu_enable = false;
+
+ phtpriv_sta->sgi = false;
+ phtpriv_sta->bwmode = HT_CHANNEL_WIDTH_20;
+ phtpriv_sta->ch_offset = HAL_PRIME_CHNL_OFFSET_DONT_CARE;
+ }
+
+ /* Rx AMPDU */
+ send_delba23a(padapter, 0, psta->hwaddr);/* recipient */
+
+ /* TX AMPDU */
+ send_delba23a(padapter, 1, psta->hwaddr);/* originator */
+ phtpriv_sta->agg_enable_bitmap = 0x0;/* reset */
+ phtpriv_sta->candidate_tid_bitmap = 0x0;/* reset */
+
+ /* todo: init other variables */
+
+ memset((void *)&psta->sta_stats, 0, sizeof(struct stainfo_stats));
+
+ spin_lock_bh(&psta->lock);
+ psta->state |= _FW_LINKED;
+ spin_unlock_bh(&psta->lock);
+}
+
+static void update_hw_ht_param(struct rtw_adapter *padapter)
+{
+ unsigned char max_AMPDU_len;
+ unsigned char min_MPDU_spacing;
+ struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv;
+ struct mlme_ext_info *pmlmeinfo = &pmlmeext->mlmext_info;
+
+ DBG_8723A("%s\n", __func__);
+
+ /* handle A-MPDU parameter field */
+ /*
+ AMPDU_para [1:0]:Max AMPDU Len => 0:8k , 1:16k, 2:32k, 3:64k
+ AMPDU_para [4:2]:Min MPDU Start Spacing
+ */
+ max_AMPDU_len = pmlmeinfo->ht_cap.ampdu_params_info &
+ IEEE80211_HT_AMPDU_PARM_FACTOR;
+
+ min_MPDU_spacing = (pmlmeinfo->ht_cap.ampdu_params_info &
+ IEEE80211_HT_AMPDU_PARM_DENSITY) >> 2;
+
+ rtl8723a_set_ampdu_min_space(padapter, min_MPDU_spacing);
+ rtl8723a_set_ampdu_factor(padapter, max_AMPDU_len);
+
+ /* Config SM Power Save setting */
+ pmlmeinfo->SM_PS = (le16_to_cpu(pmlmeinfo->ht_cap.cap_info) &
+ IEEE80211_HT_CAP_SM_PS) >> 2;
+ if (pmlmeinfo->SM_PS == WLAN_HT_CAP_SM_PS_STATIC)
+ DBG_8723A("%s(): WLAN_HT_CAP_SM_PS_STATIC\n", __func__);
+}
+
+static void start_bss_network(struct rtw_adapter *padapter, u8 *pbuf)
+{
+ const u8 *p;
+ u8 val8, cur_channel, cur_bwmode, cur_ch_offset;
+ u16 bcn_interval;
+ u32 acparm;
+ struct registry_priv *pregpriv = &padapter->registrypriv;
+ struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
+ struct security_priv *psecuritypriv = &padapter->securitypriv;
+ struct wlan_bssid_ex *pnetwork = &pmlmepriv->cur_network.network;
+ struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv;
+ struct mlme_ext_info *pmlmeinfo = &pmlmeext->mlmext_info;
+ struct wlan_bssid_ex *pnetwork_mlmeext = &pmlmeinfo->network;
+ struct ieee80211_ht_operation *pht_info = NULL;
+
+ bcn_interval = (u16)pnetwork->beacon_interval;
+ cur_channel = pnetwork->DSConfig;
+ cur_bwmode = HT_CHANNEL_WIDTH_20;
+ cur_ch_offset = HAL_PRIME_CHNL_OFFSET_DONT_CARE;
+
+ /* check if there is wps ie, */
+ /* if there is wpsie in beacon, the hostapd will update beacon twice when stating hostapd, */
+ /* and at first time the security ie (RSN/WPA IE) will not include in beacon. */
+ if (NULL == cfg80211_find_vendor_ie(WLAN_OUI_MICROSOFT,
+ WLAN_OUI_TYPE_MICROSOFT_WPS,
+ pnetwork->IEs,
+ pnetwork->IELength))
+ pmlmeext->bstart_bss = true;
+
+ /* todo: update wmm, ht cap */
+ /* pmlmeinfo->WMM_enable; */
+ /* pmlmeinfo->HT_enable; */
+ if (pmlmepriv->qos_option)
+ pmlmeinfo->WMM_enable = true;
+ if (pmlmepriv->htpriv.ht_option) {
+ pmlmeinfo->WMM_enable = true;
+ pmlmeinfo->HT_enable = true;
+
+ update_hw_ht_param(padapter);
+ }
+
+ if (pmlmepriv->cur_network.join_res != true) {
+ /* setting only at first time */
+ /* WEP Key will be set before this function, do not clear CAM. */
+ if (psecuritypriv->dot11PrivacyAlgrthm !=
+ WLAN_CIPHER_SUITE_WEP40 &&
+ psecuritypriv->dot11PrivacyAlgrthm !=
+ WLAN_CIPHER_SUITE_WEP104)
+ flush_all_cam_entry23a(padapter); /* clear CAM */
+ }
+
+ /* set MSR to AP_Mode */
+ rtl8723a_set_media_status(padapter, MSR_AP);
+
+ /* Set BSSID REG */
+ hw_var_set_bssid(padapter, pnetwork->MacAddress);
+
+ /* Set EDCA param reg */
+ acparm = 0x002F3217; /* VO */
+ rtl8723a_set_ac_param_vo(padapter, acparm);
+ acparm = 0x005E4317; /* VI */
+ rtl8723a_set_ac_param_vi(padapter, acparm);
+ acparm = 0x005ea42b;
+ rtl8723a_set_ac_param_be(padapter, acparm);
+ acparm = 0x0000A444; /* BK */
+ rtl8723a_set_ac_param_bk(padapter, acparm);
+
+ /* Set Security */
+ val8 = (psecuritypriv->dot11AuthAlgrthm == dot11AuthAlgrthm_8021X) ?
+ 0xcc : 0xcf;
+ rtl8723a_set_sec_cfg(padapter, val8);
+
+ /* Beacon Control related register */
+ rtl8723a_set_beacon_interval(padapter, bcn_interval);
+
+ UpdateBrateTbl23a(padapter, pnetwork->SupportedRates);
+ HalSetBrateCfg23a(padapter, pnetwork->SupportedRates);
+
+ if (!pmlmepriv->cur_network.join_res) {
+ /* setting only at first time */
+
+ /* disable dynamic functions, such as high power, DIG */
+
+ /* turn on all dynamic functions */
+ rtl8723a_odm_support_ability_set(padapter,
+ DYNAMIC_ALL_FUNC_ENABLE);
+ }
+ /* set channel, bwmode */
+
+ p = cfg80211_find_ie(WLAN_EID_HT_OPERATION, pnetwork->IEs,
+ pnetwork->IELength);
+ if (p && p[1]) {
+ pht_info = (struct ieee80211_ht_operation *)(p + 2);
+
+ if (pregpriv->cbw40_enable && pht_info->ht_param &
+ IEEE80211_HT_PARAM_CHAN_WIDTH_ANY) {
+ /* switch to the 40M Hz mode */
+ cur_bwmode = HT_CHANNEL_WIDTH_40;
+ switch (pht_info->ht_param &
+ IEEE80211_HT_PARAM_CHA_SEC_OFFSET) {
+ case IEEE80211_HT_PARAM_CHA_SEC_ABOVE:
+ /* pmlmeext->cur_ch_offset =
+ HAL_PRIME_CHNL_OFFSET_LOWER; */
+ cur_ch_offset = HAL_PRIME_CHNL_OFFSET_LOWER;
+ break;
+ case IEEE80211_HT_PARAM_CHA_SEC_BELOW:
+ cur_ch_offset = HAL_PRIME_CHNL_OFFSET_UPPER;
+ break;
+ default:
+ cur_ch_offset = HAL_PRIME_CHNL_OFFSET_DONT_CARE;
+ break;
+ }
+ }
+ }
+ /* TODO: need to judge the phy parameters on concurrent mode for single phy */
+ set_channel_bwmode23a(padapter, cur_channel, cur_ch_offset, cur_bwmode);
+
+ DBG_8723A("CH =%d, BW =%d, offset =%d\n", cur_channel, cur_bwmode,
+ cur_ch_offset);
+
+ pmlmeext->cur_channel = cur_channel;
+ pmlmeext->cur_bwmode = cur_bwmode;
+ pmlmeext->cur_ch_offset = cur_ch_offset;
+ pmlmeext->cur_wireless_mode = pmlmepriv->cur_network.network_type;
+
+ /* update cur_wireless_mode */
+ update_wireless_mode23a(padapter);
+
+ /* update capability after cur_wireless_mode updated */
+ update_capinfo23a(padapter, pnetwork->capability);
+
+ /* let pnetwork_mlmeext == pnetwork_mlme. */
+ memcpy(pnetwork_mlmeext, pnetwork, pnetwork->Length);
+
+ if (pmlmeext->bstart_bss) {
+ update_beacon23a(padapter, WLAN_EID_TIM, NULL, false);
+
+ /* issue beacon frame */
+ if (send_beacon23a(padapter) == _FAIL)
+ DBG_8723A("issue_beacon23a, fail!\n");
+ }
+
+ /* update bc/mc sta_info */
+ update_bmc_sta(padapter);
+}
+
+int rtw_check_beacon_data23a(struct rtw_adapter *padapter,
+ struct ieee80211_mgmt *mgmt, unsigned int len)
+{
+ int ret = _SUCCESS;
+ u8 *p;
+ u8 *pHT_caps_ie = NULL;
+ u8 *pHT_info_ie = NULL;
+ struct sta_info *psta = NULL;
+ u16 ht_cap = false;
+ uint ie_len = 0;
+ int group_cipher, pairwise_cipher;
+ u8 channel, network_type, supportRate[NDIS_802_11_LENGTH_RATES_EX];
+ int supportRateNum = 0;
+ u8 WMM_PARA_IE[] = {0x00, 0x50, 0xf2, 0x02, 0x01, 0x01};
+ struct registry_priv *pregistrypriv = &padapter->registrypriv;
+ struct security_priv *psecuritypriv = &padapter->securitypriv;
+ struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
+ struct wlan_bssid_ex *pbss_network = &pmlmepriv->cur_network.network;
+ u8 *ie = pbss_network->IEs;
+ u8 *pbuf = mgmt->u.beacon.variable;
+
+ len -= offsetof(struct ieee80211_mgmt, u.beacon.variable);
+ /* SSID */
+ /* Supported rates */
+ /* DS Params */
+ /* WLAN_EID_COUNTRY */
+ /* ERP Information element */
+ /* Extended supported rates */
+ /* WPA/WPA2 */
+ /* Wi-Fi Wireless Multimedia Extensions */
+ /* ht_capab, ht_oper */
+ /* WPS IE */
+
+ DBG_8723A("%s, len =%d\n", __func__, len);
+
+ if (!check_fwstate(pmlmepriv, WIFI_AP_STATE))
+ return _FAIL;
+
+ if (len > MAX_IE_SZ)
+ return _FAIL;
+
+ pbss_network->IELength = len;
+
+ memset(ie, 0, MAX_IE_SZ);
+
+ memcpy(ie, pbuf, pbss_network->IELength);
+
+ if (pbss_network->ifmode != NL80211_IFTYPE_AP &&
+ pbss_network->ifmode != NL80211_IFTYPE_P2P_GO)
+ return _FAIL;
+
+ pbss_network->Rssi = 0;
+
+ memcpy(pbss_network->MacAddress, myid(&padapter->eeprompriv), ETH_ALEN);
+
+ /* SSID */
+ p = rtw_get_ie23a(ie, WLAN_EID_SSID, &ie_len, pbss_network->IELength);
+ if (p && ie_len > 0) {
+ memset(&pbss_network->Ssid, 0, sizeof(struct cfg80211_ssid));
+ memcpy(pbss_network->Ssid.ssid, (p + 2), ie_len);
+ pbss_network->Ssid.ssid_len = ie_len;
+ }
+
+ /* channel */
+ channel = 0;
+ p = rtw_get_ie23a(ie, WLAN_EID_DS_PARAMS, &ie_len,
+ pbss_network->IELength);
+ if (p && ie_len > 0)
+ channel = *(p + 2);
+
+ pbss_network->DSConfig = channel;
+
+ memset(supportRate, 0, NDIS_802_11_LENGTH_RATES_EX);
+ /* get supported rates */
+ p = rtw_get_ie23a(ie, WLAN_EID_SUPP_RATES, &ie_len,
+ pbss_network->IELength);
+ if (p) {
+ memcpy(supportRate, p+2, ie_len);
+ supportRateNum = ie_len;
+ }
+
+ /* get ext_supported rates */
+ p = rtw_get_ie23a(ie, WLAN_EID_EXT_SUPP_RATES,
+ &ie_len, pbss_network->IELength);
+ if (p) {
+ memcpy(supportRate+supportRateNum, p+2, ie_len);
+ supportRateNum += ie_len;
+ }
+
+ network_type = rtw_check_network_type23a(supportRate,
+ supportRateNum, channel);
+
+ rtw_set_supported_rate23a(pbss_network->SupportedRates, network_type);
+
+ /* parsing ERP_IE */
+ p = rtw_get_ie23a(ie, WLAN_EID_ERP_INFO, &ie_len,
+ pbss_network->IELength);
+ if (p && ie_len > 0)
+ ERP_IE_handler23a(padapter, p);
+
+ /* update privacy/security */
+ if (pbss_network->capability & BIT(4))
+ pbss_network->Privacy = 1;
+ else
+ pbss_network->Privacy = 0;
+
+ psecuritypriv->wpa_psk = 0;
+
+ /* wpa2 */
+ group_cipher = 0; pairwise_cipher = 0;
+ psecuritypriv->wpa2_group_cipher = 0;
+ psecuritypriv->wpa2_pairwise_cipher = 0;
+ p = rtw_get_ie23a(ie, WLAN_EID_RSN, &ie_len,
+ pbss_network->IELength);
+ if (p && ie_len > 0) {
+ if (rtw_parse_wpa2_ie23a(p, ie_len+2, &group_cipher,
+ &pairwise_cipher, NULL) == _SUCCESS) {
+ psecuritypriv->dot11AuthAlgrthm = dot11AuthAlgrthm_8021X;
+
+ psecuritypriv->dot8021xalg = 1;/* psk, todo:802.1x */
+ psecuritypriv->wpa_psk |= BIT(1);
+
+ psecuritypriv->wpa2_group_cipher = group_cipher;
+ psecuritypriv->wpa2_pairwise_cipher = pairwise_cipher;
+ }
+ }
+
+ /* wpa */
+ ie_len = 0;
+ group_cipher = 0;
+ pairwise_cipher = 0;
+ psecuritypriv->wpa_group_cipher = 0;
+ psecuritypriv->wpa_pairwise_cipher = 0;
+ for (p = ie; ; p += (ie_len + 2)) {
+ p = rtw_get_ie23a(p, WLAN_EID_VENDOR_SPECIFIC, &ie_len,
+ pbss_network->IELength - (ie_len + 2));
+ if ((p) && (!memcmp(p+2, RTW_WPA_OUI23A_TYPE, 4))) {
+ if (rtw_parse_wpa_ie23a(p, ie_len+2, &group_cipher,
+ &pairwise_cipher, NULL) == _SUCCESS) {
+ psecuritypriv->dot11AuthAlgrthm = dot11AuthAlgrthm_8021X;
+
+ /* psk, todo:802.1x */
+ psecuritypriv->dot8021xalg = 1;
+
+ psecuritypriv->wpa_psk |= BIT(0);
+
+ psecuritypriv->wpa_group_cipher = group_cipher;
+ psecuritypriv->wpa_pairwise_cipher = pairwise_cipher;
+ }
+ break;
+ }
+
+ if ((p == NULL) || (ie_len == 0))
+ break;
+ }
+
+ /* wmm */
+ ie_len = 0;
+ pmlmepriv->qos_option = 0;
+ if (pregistrypriv->wmm_enable) {
+ for (p = ie; ; p += (ie_len + 2)) {
+ p = rtw_get_ie23a(p, WLAN_EID_VENDOR_SPECIFIC, &ie_len,
+ (pbss_network->IELength -
+ (ie_len + 2)));
+ if ((p) && !memcmp(p+2, WMM_PARA_IE, 6)) {
+ pmlmepriv->qos_option = 1;
+
+ *(p+8) |= BIT(7);/* QoS Info, support U-APSD */
+
+ /* disable all ACM bits since the WMM admission
+ * control is not supported
+ */
+ *(p + 10) &= ~BIT(4); /* BE */
+ *(p + 14) &= ~BIT(4); /* BK */
+ *(p + 18) &= ~BIT(4); /* VI */
+ *(p + 22) &= ~BIT(4); /* VO */
+ break;
+ }
+ if ((p == NULL) || (ie_len == 0))
+ break;
+ }
+ }
+ /* parsing HT_CAP_IE */
+ p = rtw_get_ie23a(ie, WLAN_EID_HT_CAPABILITY, &ie_len,
+ pbss_network->IELength);
+ if (p && ie_len > 0) {
+ u8 rf_type;
+
+ struct ieee80211_ht_cap *pht_cap = (struct ieee80211_ht_cap *)(p+2);
+
+ pHT_caps_ie = p;
+
+ ht_cap = true;
+ network_type |= WIRELESS_11_24N;
+
+ rf_type = rtl8723a_get_rf_type(padapter);
+
+ if ((psecuritypriv->wpa_pairwise_cipher & WPA_CIPHER_CCMP) ||
+ (psecuritypriv->wpa2_pairwise_cipher & WPA_CIPHER_CCMP))
+ pht_cap->ampdu_params_info |= (IEEE80211_HT_AMPDU_PARM_DENSITY & (0x07<<2));
+ else
+ pht_cap->ampdu_params_info |= (IEEE80211_HT_AMPDU_PARM_DENSITY&0x00);
+
+ /* set Max Rx AMPDU size to 64K */
+ pht_cap->ampdu_params_info |= (IEEE80211_HT_AMPDU_PARM_FACTOR & 0x03);
+
+ if (rf_type == RF_1T1R) {
+ pht_cap->mcs.rx_mask[0] = 0xff;
+ pht_cap->mcs.rx_mask[1] = 0x0;
+ }
+
+ memcpy(&pmlmepriv->htpriv.ht_cap, p+2, ie_len);
+ }
+
+ /* parsing HT_INFO_IE */
+ p = rtw_get_ie23a(ie, WLAN_EID_HT_OPERATION, &ie_len,
+ pbss_network->IELength);
+ if (p && ie_len > 0)
+ pHT_info_ie = p;
+
+ pmlmepriv->cur_network.network_type = network_type;
+
+ pmlmepriv->htpriv.ht_option = false;
+
+ /* ht_cap */
+ if (pregistrypriv->ht_enable && ht_cap) {
+ pmlmepriv->htpriv.ht_option = true;
+ pmlmepriv->qos_option = 1;
+
+ if (pregistrypriv->ampdu_enable == 1)
+ pmlmepriv->htpriv.ampdu_enable = true;
+
+ HT_caps_handler23a(padapter, pHT_caps_ie);
+
+ HT_info_handler23a(padapter, pHT_info_ie);
+ }
+
+ pbss_network->Length = get_wlan_bssid_ex_sz(pbss_network);
+
+ /* issue beacon to start bss network */
+ start_bss_network(padapter, (u8 *)pbss_network);
+
+ /* alloc sta_info for ap itself */
+ psta = rtw_get_stainfo23a(&padapter->stapriv, pbss_network->MacAddress);
+ if (!psta) {
+ psta = rtw_alloc_stainfo23a(&padapter->stapriv,
+ pbss_network->MacAddress,
+ GFP_KERNEL);
+ if (!psta)
+ return _FAIL;
+ }
+ /* fix bug of flush_cam_entry at STOP AP mode */
+ psta->state |= WIFI_AP_STATE;
+ rtw_indicate_connect23a(padapter);
+
+ /* for check if already set beacon */
+ pmlmepriv->cur_network.join_res = true;
+
+ return ret;
+}
+
+void rtw_set_macaddr_acl23a(struct rtw_adapter *padapter, int mode)
+{
+ struct sta_priv *pstapriv = &padapter->stapriv;
+ struct wlan_acl_pool *pacl_list = &pstapriv->acl_list;
+
+ DBG_8723A("%s, mode =%d\n", __func__, mode);
+
+ pacl_list->mode = mode;
+}
+
+int rtw_acl_add_sta23a(struct rtw_adapter *padapter, u8 *addr)
+{
+ struct list_head *plist, *phead;
+ u8 added = false;
+ int i, ret = 0;
+ struct rtw_wlan_acl_node *paclnode;
+ struct sta_priv *pstapriv = &padapter->stapriv;
+ struct wlan_acl_pool *pacl_list = &pstapriv->acl_list;
+ struct rtw_queue *pacl_node_q = &pacl_list->acl_node_q;
+
+ DBG_8723A("%s(acl_num =%d) =%pM\n", __func__, pacl_list->num, addr);
+
+ if ((NUM_ACL-1) < pacl_list->num)
+ return -1;
+
+ spin_lock_bh(&pacl_node_q->lock);
+
+ phead = get_list_head(pacl_node_q);
+
+ list_for_each(plist, phead) {
+ paclnode = container_of(plist, struct rtw_wlan_acl_node, list);
+
+ if (!memcmp(paclnode->addr, addr, ETH_ALEN)) {
+ if (paclnode->valid == true) {
+ added = true;
+ DBG_8723A("%s, sta has been added\n", __func__);
+ break;
+ }
+ }
+ }
+
+ spin_unlock_bh(&pacl_node_q->lock);
+
+ if (added)
+ return ret;
+
+ spin_lock_bh(&pacl_node_q->lock);
+
+ for (i = 0; i < NUM_ACL; i++) {
+ paclnode = &pacl_list->aclnode[i];
+
+ if (!paclnode->valid) {
+ INIT_LIST_HEAD(&paclnode->list);
+
+ memcpy(paclnode->addr, addr, ETH_ALEN);
+
+ paclnode->valid = true;
+
+ list_add_tail(&paclnode->list, get_list_head(pacl_node_q));
+
+ pacl_list->num++;
+
+ break;
+ }
+ }
+
+ DBG_8723A("%s, acl_num =%d\n", __func__, pacl_list->num);
+
+ spin_unlock_bh(&pacl_node_q->lock);
+ return ret;
+}
+
+int rtw_acl_remove_sta23a(struct rtw_adapter *padapter, u8 *addr)
+{
+ struct list_head *plist, *phead, *ptmp;
+ struct rtw_wlan_acl_node *paclnode;
+ struct sta_priv *pstapriv = &padapter->stapriv;
+ struct wlan_acl_pool *pacl_list = &pstapriv->acl_list;
+ struct rtw_queue *pacl_node_q = &pacl_list->acl_node_q;
+
+ DBG_8723A("%s(acl_num =%d) = %pM\n", __func__, pacl_list->num, addr);
+
+ spin_lock_bh(&pacl_node_q->lock);
+
+ phead = get_list_head(pacl_node_q);
+
+ list_for_each_safe(plist, ptmp, phead) {
+ paclnode = container_of(plist, struct rtw_wlan_acl_node, list);
+
+ if (!memcmp(paclnode->addr, addr, ETH_ALEN)) {
+ if (paclnode->valid) {
+ paclnode->valid = false;
+
+ list_del_init(&paclnode->list);
+
+ pacl_list->num--;
+ }
+ }
+ }
+
+ spin_unlock_bh(&pacl_node_q->lock);
+
+ DBG_8723A("%s, acl_num =%d\n", __func__, pacl_list->num);
+
+ return 0;
+}
+
+static void update_bcn_fixed_ie(struct rtw_adapter *padapter)
+{
+ DBG_8723A("%s\n", __func__);
+}
+
+static void update_bcn_erpinfo_ie(struct rtw_adapter *padapter)
+{
+ struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
+ struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv;
+ struct mlme_ext_info *pmlmeinfo = &pmlmeext->mlmext_info;
+ struct wlan_bssid_ex *pnetwork = &pmlmeinfo->network;
+ unsigned char *p, *ie = pnetwork->IEs;
+ u32 len = 0;
+
+ DBG_8723A("%s, ERP_enable =%d\n", __func__, pmlmeinfo->ERP_enable);
+
+ if (!pmlmeinfo->ERP_enable)
+ return;
+
+ /* parsing ERP_IE */
+ p = rtw_get_ie23a(ie, WLAN_EID_ERP_INFO, &len, pnetwork->IELength);
+ if (p && len > 0) {
+ if (pmlmepriv->num_sta_non_erp == 1)
+ p[2] |= WLAN_ERP_NON_ERP_PRESENT |
+ WLAN_ERP_USE_PROTECTION;
+ else
+ p[2] &= ~(WLAN_ERP_NON_ERP_PRESENT |
+ WLAN_ERP_USE_PROTECTION);
+
+ if (pmlmepriv->num_sta_no_short_preamble > 0)
+ p[2] |= WLAN_ERP_BARKER_PREAMBLE;
+ else
+ p[2] &= ~(WLAN_ERP_BARKER_PREAMBLE);
+
+ ERP_IE_handler23a(padapter, p);
+ }
+}
+
+static void update_bcn_htcap_ie(struct rtw_adapter *padapter)
+{
+ DBG_8723A("%s\n", __func__);
+}
+
+static void update_bcn_htinfo_ie(struct rtw_adapter *padapter)
+{
+ DBG_8723A("%s\n", __func__);
+}
+
+static void update_bcn_rsn_ie(struct rtw_adapter *padapter)
+{
+ DBG_8723A("%s\n", __func__);
+}
+
+static void update_bcn_wpa_ie(struct rtw_adapter *padapter)
+{
+ DBG_8723A("%s\n", __func__);
+}
+
+static void update_bcn_wmm_ie(struct rtw_adapter *padapter)
+{
+ DBG_8723A("%s\n", __func__);
+}
+
+static void update_bcn_wps_ie(struct rtw_adapter *padapter)
+{
+ DBG_8723A("%s\n", __func__);
+}
+
+static void update_bcn_p2p_ie(struct rtw_adapter *padapter)
+{
+}
+
+static void update_bcn_vendor_spec_ie(struct rtw_adapter *padapter, u8 *oui)
+{
+ DBG_8723A("%s\n", __func__);
+
+ if (!memcmp(RTW_WPA_OUI23A_TYPE, oui, 4))
+ update_bcn_wpa_ie(padapter);
+ else if (!memcmp(WMM_OUI23A, oui, 4))
+ update_bcn_wmm_ie(padapter);
+ else if (!memcmp(WPS_OUI23A, oui, 4))
+ update_bcn_wps_ie(padapter);
+ else if (!memcmp(P2P_OUI23A, oui, 4))
+ update_bcn_p2p_ie(padapter);
+ else
+ DBG_8723A("unknown OUI type!\n");
+}
+
+void update_beacon23a(struct rtw_adapter *padapter, u8 ie_id, u8 *oui, u8 tx)
+{
+ struct mlme_priv *pmlmepriv;
+ struct mlme_ext_priv *pmlmeext;
+ /* struct mlme_ext_info *pmlmeinfo; */
+
+ /* DBG_8723A("%s\n", __func__); */
+
+ if (!padapter)
+ return;
+
+ pmlmepriv = &padapter->mlmepriv;
+ pmlmeext = &padapter->mlmeextpriv;
+ /* pmlmeinfo = &pmlmeext->mlmext_info; */
+
+ if (false == pmlmeext->bstart_bss)
+ return;
+
+ spin_lock_bh(&pmlmepriv->bcn_update_lock);
+
+ switch (ie_id) {
+ case 0xFF:
+ /* 8: TimeStamp, 2: Beacon Interval 2:Capability */
+ update_bcn_fixed_ie(padapter);
+ break;
+
+ case WLAN_EID_TIM:
+ update_BCNTIM(padapter);
+ break;
+
+ case WLAN_EID_ERP_INFO:
+ update_bcn_erpinfo_ie(padapter);
+ break;
+
+ case WLAN_EID_HT_CAPABILITY:
+ update_bcn_htcap_ie(padapter);
+ break;
+
+ case WLAN_EID_RSN:
+ update_bcn_rsn_ie(padapter);
+ break;
+
+ case WLAN_EID_HT_OPERATION:
+ update_bcn_htinfo_ie(padapter);
+ break;
+
+ case WLAN_EID_VENDOR_SPECIFIC:
+ update_bcn_vendor_spec_ie(padapter, oui);
+ break;
+
+ default:
+ break;
+ }
+
+ pmlmepriv->update_bcn = true;
+
+ spin_unlock_bh(&pmlmepriv->bcn_update_lock);
+
+ if (tx)
+ set_tx_beacon_cmd23a(padapter);
+}
+
+/*
+op_mode
+Set to 0 (HT pure) under the following conditions
+ - all STAs in the BSS are 20/40 MHz HT in 20/40 MHz BSS or
+ - all STAs in the BSS are 20 MHz HT in 20 MHz BSS
+Set to 1 (HT non-member protection) if there may be non-HT STAs
+ in both the primary and the secondary channel
+Set to 2 if only HT STAs are associated in BSS,
+ however and at least one 20 MHz HT STA is associated
+Set to 3 (HT mixed mode) when one or more non-HT STAs are associated
+ (currently non-GF HT station is considered as non-HT STA also)
+*/
+static int rtw_ht_operation_update(struct rtw_adapter *padapter)
+{
+ u16 cur_op_mode, new_op_mode;
+ int op_mode_changes = 0;
+ struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
+ struct ht_priv *phtpriv_ap = &pmlmepriv->htpriv;
+
+ if (pmlmepriv->htpriv.ht_option)
+ return 0;
+
+ /* if (!iface->conf->ieee80211n || iface->conf->ht_op_mode_fixed) */
+ /* return 0; */
+
+ DBG_8723A("%s current operation mode = 0x%X\n",
+ __func__, pmlmepriv->ht_op_mode);
+
+ if (!(pmlmepriv->ht_op_mode & IEEE80211_HT_OP_MODE_NON_GF_STA_PRSNT)
+ && pmlmepriv->num_sta_ht_no_gf) {
+ pmlmepriv->ht_op_mode |=
+ IEEE80211_HT_OP_MODE_NON_GF_STA_PRSNT;
+ op_mode_changes++;
+ } else if ((pmlmepriv->ht_op_mode &
+ IEEE80211_HT_OP_MODE_NON_GF_STA_PRSNT) &&
+ pmlmepriv->num_sta_ht_no_gf == 0) {
+ pmlmepriv->ht_op_mode &=
+ ~IEEE80211_HT_OP_MODE_NON_GF_STA_PRSNT;
+ op_mode_changes++;
+ }
+
+ if (!(pmlmepriv->ht_op_mode & IEEE80211_HT_OP_MODE_NON_HT_STA_PRSNT) &&
+ (pmlmepriv->num_sta_no_ht || pmlmepriv->olbc_ht)) {
+ pmlmepriv->ht_op_mode |= IEEE80211_HT_OP_MODE_NON_HT_STA_PRSNT;
+ op_mode_changes++;
+ } else if ((pmlmepriv->ht_op_mode &
+ IEEE80211_HT_OP_MODE_NON_HT_STA_PRSNT) &&
+ (pmlmepriv->num_sta_no_ht == 0 && !pmlmepriv->olbc_ht)) {
+ pmlmepriv->ht_op_mode &=
+ ~IEEE80211_HT_OP_MODE_NON_HT_STA_PRSNT;
+ op_mode_changes++;
+ }
+
+ /* Note: currently we switch to the MIXED op mode if HT non-greenfield
+ * station is associated. Probably it's a theoretical case, since
+ * it looks like all known HT STAs support greenfield.
+ */
+ if (pmlmepriv->num_sta_no_ht ||
+ (pmlmepriv->ht_op_mode & IEEE80211_HT_OP_MODE_NON_GF_STA_PRSNT))
+ new_op_mode = IEEE80211_HT_OP_MODE_PROTECTION_NONHT_MIXED;
+ else if ((le16_to_cpu(phtpriv_ap->ht_cap.cap_info) &
+ IEEE80211_HT_CAP_SUP_WIDTH_20_40) &&
+ pmlmepriv->num_sta_ht_20mhz)
+ new_op_mode = IEEE80211_HT_OP_MODE_PROTECTION_20MHZ;
+ else if (pmlmepriv->olbc_ht)
+ new_op_mode = IEEE80211_HT_OP_MODE_PROTECTION_NONMEMBER;
+ else
+ new_op_mode = IEEE80211_HT_OP_MODE_PROTECTION_NONE;
+
+ cur_op_mode = pmlmepriv->ht_op_mode & IEEE80211_HT_OP_MODE_PROTECTION;
+ if (cur_op_mode != new_op_mode) {
+ pmlmepriv->ht_op_mode &= ~IEEE80211_HT_OP_MODE_PROTECTION;
+ pmlmepriv->ht_op_mode |= new_op_mode;
+ op_mode_changes++;
+ }
+
+ DBG_8723A("%s new operation mode = 0x%X changes =%d\n",
+ __func__, pmlmepriv->ht_op_mode, op_mode_changes);
+
+ return op_mode_changes;
+}
+
+void associated_clients_update23a(struct rtw_adapter *padapter, u8 updated)
+{
+ /* update associated stations cap. */
+ if (updated == true) {
+ struct list_head *phead, *plist, *ptmp;
+ struct sta_info *psta;
+ struct sta_priv *pstapriv = &padapter->stapriv;
+
+ spin_lock_bh(&pstapriv->asoc_list_lock);
+
+ phead = &pstapriv->asoc_list;
+
+ list_for_each_safe(plist, ptmp, phead) {
+ psta = container_of(plist, struct sta_info, asoc_list);
+
+ VCS_update23a(padapter, psta);
+ }
+
+ spin_unlock_bh(&pstapriv->asoc_list_lock);
+ }
+}
+
+/* called > TSR LEVEL for USB or SDIO Interface*/
+void bss_cap_update_on_sta_join23a(struct rtw_adapter *padapter, struct sta_info *psta)
+{
+ u8 beacon_updated = false;
+ struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
+ struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv;
+
+ if (!(psta->flags & WLAN_STA_SHORT_PREAMBLE)) {
+ if (!psta->no_short_preamble_set) {
+ psta->no_short_preamble_set = 1;
+
+ pmlmepriv->num_sta_no_short_preamble++;
+
+ if ((pmlmeext->cur_wireless_mode > WIRELESS_11B) &&
+ (pmlmepriv->num_sta_no_short_preamble == 1)) {
+ beacon_updated = true;
+ update_beacon23a(padapter, 0xFF, NULL, true);
+ }
+
+ }
+ } else {
+ if (psta->no_short_preamble_set) {
+ psta->no_short_preamble_set = 0;
+
+ pmlmepriv->num_sta_no_short_preamble--;
+
+ if ((pmlmeext->cur_wireless_mode > WIRELESS_11B) &&
+ (pmlmepriv->num_sta_no_short_preamble == 0)) {
+ beacon_updated = true;
+ update_beacon23a(padapter, 0xFF, NULL, true);
+ }
+
+ }
+ }
+
+ if (psta->flags & WLAN_STA_NONERP) {
+ if (!psta->nonerp_set) {
+ psta->nonerp_set = 1;
+
+ pmlmepriv->num_sta_non_erp++;
+
+ if (pmlmepriv->num_sta_non_erp == 1) {
+ beacon_updated = true;
+ update_beacon23a(padapter, WLAN_EID_ERP_INFO, NULL, true);
+ }
+ }
+
+ } else {
+ if (psta->nonerp_set) {
+ psta->nonerp_set = 0;
+
+ pmlmepriv->num_sta_non_erp--;
+
+ if (pmlmepriv->num_sta_non_erp == 0) {
+ beacon_updated = true;
+ update_beacon23a(padapter, WLAN_EID_ERP_INFO, NULL, true);
+ }
+ }
+
+ }
+
+ if (!(psta->capability & WLAN_CAPABILITY_SHORT_SLOT_TIME)) {
+ if (!psta->no_short_slot_time_set) {
+ psta->no_short_slot_time_set = 1;
+
+ pmlmepriv->num_sta_no_short_slot_time++;
+
+ if ((pmlmeext->cur_wireless_mode > WIRELESS_11B) &&
+ (pmlmepriv->num_sta_no_short_slot_time == 1)) {
+ beacon_updated = true;
+ update_beacon23a(padapter, 0xFF, NULL, true);
+ }
+
+ }
+ } else {
+ if (psta->no_short_slot_time_set) {
+ psta->no_short_slot_time_set = 0;
+
+ pmlmepriv->num_sta_no_short_slot_time--;
+
+ if ((pmlmeext->cur_wireless_mode > WIRELESS_11B) &&
+ (pmlmepriv->num_sta_no_short_slot_time == 0)) {
+ beacon_updated = true;
+ update_beacon23a(padapter, 0xFF, NULL, true);
+ }
+ }
+ }
+
+ if (psta->flags & WLAN_STA_HT) {
+ u16 ht_capab = le16_to_cpu(psta->htpriv.ht_cap.cap_info);
+
+ DBG_8723A("HT: STA %pM HT Capabilities Info: 0x%04x\n",
+ psta->hwaddr, ht_capab);
+
+ if (psta->no_ht_set) {
+ psta->no_ht_set = 0;
+ pmlmepriv->num_sta_no_ht--;
+ }
+
+ if ((ht_capab & IEEE80211_HT_CAP_GRN_FLD) == 0) {
+ if (!psta->no_ht_gf_set) {
+ psta->no_ht_gf_set = 1;
+ pmlmepriv->num_sta_ht_no_gf++;
+ }
+ DBG_8723A("%s STA %pM - no greenfield, num of non-gf stations %d\n",
+ __func__, psta->hwaddr,
+ pmlmepriv->num_sta_ht_no_gf);
+ }
+
+ if ((ht_capab & IEEE80211_HT_CAP_SUP_WIDTH_20_40) == 0) {
+ if (!psta->ht_20mhz_set) {
+ psta->ht_20mhz_set = 1;
+ pmlmepriv->num_sta_ht_20mhz++;
+ }
+ DBG_8723A("%s STA %pM - 20 MHz HT, num of 20MHz HT STAs %d\n",
+ __func__, psta->hwaddr,
+ pmlmepriv->num_sta_ht_20mhz);
+ }
+
+ } else {
+ if (!psta->no_ht_set) {
+ psta->no_ht_set = 1;
+ pmlmepriv->num_sta_no_ht++;
+ }
+ if (pmlmepriv->htpriv.ht_option) {
+ DBG_8723A("%s STA %pM - no HT, num of non-HT stations %d\n",
+ __func__, psta->hwaddr,
+ pmlmepriv->num_sta_no_ht);
+ }
+ }
+
+ if (rtw_ht_operation_update(padapter) > 0) {
+ update_beacon23a(padapter, WLAN_EID_HT_CAPABILITY, NULL, false);
+ update_beacon23a(padapter, WLAN_EID_HT_OPERATION, NULL, true);
+ }
+
+ /* update associated stations cap. */
+ associated_clients_update23a(padapter, beacon_updated);
+
+ DBG_8723A("%s, updated =%d\n", __func__, beacon_updated);
+}
+
+u8 bss_cap_update_on_sta_leave23a(struct rtw_adapter *padapter, struct sta_info *psta)
+{
+ u8 beacon_updated = false;
+ struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
+ struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv;
+
+ if (!psta)
+ return beacon_updated;
+
+ if (psta->no_short_preamble_set) {
+ psta->no_short_preamble_set = 0;
+ pmlmepriv->num_sta_no_short_preamble--;
+ if (pmlmeext->cur_wireless_mode > WIRELESS_11B
+ && pmlmepriv->num_sta_no_short_preamble == 0) {
+ beacon_updated = true;
+ update_beacon23a(padapter, 0xFF, NULL, true);
+ }
+ }
+
+ if (psta->nonerp_set) {
+ psta->nonerp_set = 0;
+ pmlmepriv->num_sta_non_erp--;
+ if (pmlmepriv->num_sta_non_erp == 0) {
+ beacon_updated = true;
+ update_beacon23a(padapter, WLAN_EID_ERP_INFO,
+ NULL, true);
+ }
+ }
+
+ if (psta->no_short_slot_time_set) {
+ psta->no_short_slot_time_set = 0;
+ pmlmepriv->num_sta_no_short_slot_time--;
+ if (pmlmeext->cur_wireless_mode > WIRELESS_11B
+ && pmlmepriv->num_sta_no_short_slot_time == 0) {
+ beacon_updated = true;
+ update_beacon23a(padapter, 0xFF, NULL, true);
+ }
+ }
+
+ if (psta->no_ht_gf_set) {
+ psta->no_ht_gf_set = 0;
+ pmlmepriv->num_sta_ht_no_gf--;
+ }
+
+ if (psta->no_ht_set) {
+ psta->no_ht_set = 0;
+ pmlmepriv->num_sta_no_ht--;
+ }
+
+ if (psta->ht_20mhz_set) {
+ psta->ht_20mhz_set = 0;
+ pmlmepriv->num_sta_ht_20mhz--;
+ }
+
+ if (rtw_ht_operation_update(padapter) > 0) {
+ update_beacon23a(padapter, WLAN_EID_HT_CAPABILITY, NULL, false);
+ update_beacon23a(padapter, WLAN_EID_HT_OPERATION, NULL, true);
+ }
+
+ /* update associated stations cap. */
+
+ DBG_8723A("%s, updated =%d\n", __func__, beacon_updated);
+
+ return beacon_updated;
+}
+
+u8 ap_free_sta23a(struct rtw_adapter *padapter, struct sta_info *psta, bool active, u16 reason)
+{
+ struct sta_priv *pstapriv = &padapter->stapriv;
+ u8 beacon_updated = false;
+
+ if (!psta)
+ return beacon_updated;
+
+ if (active) {
+ /* tear down Rx AMPDU */
+ send_delba23a(padapter, 0, psta->hwaddr);/* recipient */
+
+ /* tear down TX AMPDU */
+ send_delba23a(padapter, 1, psta->hwaddr);/* originator */
+
+ issue_deauth23a(padapter, psta->hwaddr, reason);
+ }
+
+ psta->htpriv.agg_enable_bitmap = 0x0;/* reset */
+ psta->htpriv.candidate_tid_bitmap = 0x0;/* reset */
+
+ /* report_del_sta_event23a(padapter, psta->hwaddr, reason); */
+
+ /* clear cam entry / key */
+ /* clear_cam_entry23a(padapter, (psta->mac_id + 3)); */
+ rtw_clearstakey_cmd23a(padapter, (u8 *)psta, (u8)(psta->mac_id + 3),
+ true);
+
+ spin_lock_bh(&psta->lock);
+ psta->state &= ~_FW_LINKED;
+ spin_unlock_bh(&psta->lock);
+
+ rtw_cfg80211_indicate_sta_disassoc(padapter, psta->hwaddr, reason);
+
+ report_del_sta_event23a(padapter, psta->hwaddr, reason);
+
+ beacon_updated = bss_cap_update_on_sta_leave23a(padapter, psta);
+
+ spin_lock_bh(&pstapriv->sta_hash_lock);
+ rtw_free_stainfo23a(padapter, psta);
+ spin_unlock_bh(&pstapriv->sta_hash_lock);
+
+ return beacon_updated;
+}
+
+int rtw_ap_inform_ch_switch23a (struct rtw_adapter *padapter, u8 new_ch, u8 ch_offset)
+{
+ struct list_head *phead, *plist;
+ struct sta_info *psta = NULL;
+ struct sta_priv *pstapriv = &padapter->stapriv;
+ struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv;
+ struct mlme_ext_info *pmlmeinfo = &pmlmeext->mlmext_info;
+ u8 bc_addr[ETH_ALEN] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
+
+ if ((pmlmeinfo->state&0x03) != MSR_AP)
+ return 0;
+
+ DBG_8723A("%s(%s): with ch:%u, offset:%u\n", __func__,
+ padapter->pnetdev->name, new_ch, ch_offset);
+
+ spin_lock_bh(&pstapriv->asoc_list_lock);
+ phead = &pstapriv->asoc_list;
+
+ list_for_each(plist, phead) {
+ psta = container_of(plist, struct sta_info, asoc_list);
+
+ issue_action_spct_ch_switch23a (padapter, psta->hwaddr, new_ch, ch_offset);
+ psta->expire_to = ((pstapriv->expire_to * 2) > 5) ? 5 : (pstapriv->expire_to * 2);
+ }
+ spin_unlock_bh(&pstapriv->asoc_list_lock);
+
+ issue_action_spct_ch_switch23a (padapter, bc_addr, new_ch, ch_offset);
+
+ return 0;
+}
+
+int rtw_sta_flush23a(struct rtw_adapter *padapter)
+{
+ struct list_head *phead, *plist, *ptmp;
+ struct sta_info *psta;
+ struct sta_priv *pstapriv = &padapter->stapriv;
+ struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv;
+ struct mlme_ext_info *pmlmeinfo = &pmlmeext->mlmext_info;
+ u8 bc_addr[ETH_ALEN] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
+ u8 chk_alive_num = 0;
+ struct sta_info *chk_alive_list[NUM_STA];
+ int i;
+
+ DBG_8723A("%s(%s)\n", __func__, padapter->pnetdev->name);
+
+ if ((pmlmeinfo->state&0x03) != MSR_AP)
+ return 0;
+
+ spin_lock_bh(&pstapriv->asoc_list_lock);
+ phead = &pstapriv->asoc_list;
+
+ list_for_each_safe(plist, ptmp, phead) {
+ psta = container_of(plist, struct sta_info, asoc_list);
+
+ /* Remove sta from asoc_list */
+ list_del_init(&psta->asoc_list);
+ pstapriv->asoc_list_cnt--;
+
+ /* Keep sta for ap_free_sta23a() beyond this asoc_list loop */
+ chk_alive_list[chk_alive_num++] = psta;
+ }
+ spin_unlock_bh(&pstapriv->asoc_list_lock);
+
+ /* For each sta in chk_alive_list, call ap_free_sta23a */
+ for (i = 0; i < chk_alive_num; i++)
+ ap_free_sta23a(padapter, chk_alive_list[i], true,
+ WLAN_REASON_DEAUTH_LEAVING);
+
+ issue_deauth23a(padapter, bc_addr, WLAN_REASON_DEAUTH_LEAVING);
+
+ associated_clients_update23a(padapter, true);
+
+ return 0;
+}
+
+/* called > TSR LEVEL for USB or SDIO Interface*/
+void sta_info_update23a(struct rtw_adapter *padapter, struct sta_info *psta)
+{
+ int flags = psta->flags;
+ struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
+
+ /* update wmm cap. */
+ if (WLAN_STA_WME&flags)
+ psta->qos_option = 1;
+ else
+ psta->qos_option = 0;
+
+ if (pmlmepriv->qos_option == 0)
+ psta->qos_option = 0;
+
+ /* update 802.11n ht cap. */
+ if (WLAN_STA_HT&flags) {
+ psta->htpriv.ht_option = true;
+ psta->qos_option = 1;
+ } else {
+ psta->htpriv.ht_option = false;
+ }
+
+ if (!pmlmepriv->htpriv.ht_option)
+ psta->htpriv.ht_option = false;
+
+ update_sta_info23a_apmode23a(padapter, psta);
+}
+
+/* called >= TSR LEVEL for USB or SDIO Interface*/
+void ap_sta_info_defer_update23a(struct rtw_adapter *padapter, struct sta_info *psta)
+{
+ if (psta->state & _FW_LINKED) {
+ /* add ratid */
+ add_RATid23a(padapter, psta, 0);/* DM_RATR_STA_INIT */
+ }
+}
+
+/* restore hw setting from sw data structures */
+void rtw_ap_restore_network(struct rtw_adapter *padapter)
+{
+ struct mlme_priv *mlmepriv = &padapter->mlmepriv;
+ struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv;
+ struct sta_priv *pstapriv = &padapter->stapriv;
+ struct sta_info *psta;
+ struct security_priv *psecuritypriv = &padapter->securitypriv;
+ struct list_head *phead, *plist, *ptmp;
+ u8 chk_alive_num = 0;
+ struct sta_info *chk_alive_list[NUM_STA];
+ int i;
+
+ rtw_setopmode_cmd23a(padapter, NL80211_IFTYPE_AP);
+
+ set_channel_bwmode23a(padapter, pmlmeext->cur_channel, pmlmeext->cur_ch_offset, pmlmeext->cur_bwmode);
+
+ start_bss_network(padapter, (u8 *)&mlmepriv->cur_network.network);
+
+ if (padapter->securitypriv.dot11PrivacyAlgrthm ==
+ WLAN_CIPHER_SUITE_TKIP ||
+ padapter->securitypriv.dot11PrivacyAlgrthm ==
+ WLAN_CIPHER_SUITE_CCMP) {
+ /* restore group key, WEP keys is restored in ips_leave23a() */
+ rtw_set_key23a(padapter, psecuritypriv,
+ psecuritypriv->dot118021XGrpKeyid, 0);
+ }
+
+ /* per sta pairwise key and settings */
+ if (padapter->securitypriv.dot11PrivacyAlgrthm !=
+ WLAN_CIPHER_SUITE_TKIP &&
+ padapter->securitypriv.dot11PrivacyAlgrthm !=
+ WLAN_CIPHER_SUITE_CCMP) {
+ return;
+ }
+
+ spin_lock_bh(&pstapriv->asoc_list_lock);
+
+ phead = &pstapriv->asoc_list;
+
+ list_for_each_safe(plist, ptmp, phead) {
+ psta = container_of(plist, struct sta_info, asoc_list);
+
+ chk_alive_list[chk_alive_num++] = psta;
+ }
+
+ spin_unlock_bh(&pstapriv->asoc_list_lock);
+
+ for (i = 0; i < chk_alive_num; i++) {
+ psta = chk_alive_list[i];
+
+ if (psta->state & _FW_LINKED) {
+ Update_RA_Entry23a(padapter, psta);
+ /* pairwise key */
+ rtw_setstakey_cmd23a(padapter, (unsigned char *)psta, true);
+ }
+ }
+}
+
+void start_ap_mode23a(struct rtw_adapter *padapter)
+{
+ int i;
+ struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
+ struct sta_priv *pstapriv = &padapter->stapriv;
+ struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv;
+ struct wlan_acl_pool *pacl_list = &pstapriv->acl_list;
+
+ pmlmepriv->update_bcn = false;
+
+ /* init_mlme_ap_info23a(padapter); */
+ pmlmeext->bstart_bss = false;
+
+ pmlmepriv->num_sta_non_erp = 0;
+
+ pmlmepriv->num_sta_no_short_slot_time = 0;
+
+ pmlmepriv->num_sta_no_short_preamble = 0;
+
+ pmlmepriv->num_sta_ht_no_gf = 0;
+ pmlmepriv->num_sta_no_ht = 0;
+ pmlmepriv->num_sta_ht_20mhz = 0;
+
+ pmlmepriv->olbc = false;
+
+ pmlmepriv->olbc_ht = false;
+
+ pmlmepriv->ht_op_mode = 0;
+
+ for (i = 0; i < NUM_STA; i++)
+ pstapriv->sta_aid[i] = NULL;
+
+ /* for ACL */
+ INIT_LIST_HEAD(&pacl_list->acl_node_q.queue);
+ pacl_list->num = 0;
+ pacl_list->mode = 0;
+ for (i = 0; i < NUM_ACL; i++) {
+ INIT_LIST_HEAD(&pacl_list->aclnode[i].list);
+ pacl_list->aclnode[i].valid = false;
+ }
+}
+
+void stop_ap_mode23a(struct rtw_adapter *padapter)
+{
+ struct list_head *phead, *plist, *ptmp;
+ struct rtw_wlan_acl_node *paclnode;
+ struct sta_info *psta = NULL;
+ struct sta_priv *pstapriv = &padapter->stapriv;
+ struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
+ struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv;
+ struct wlan_acl_pool *pacl_list = &pstapriv->acl_list;
+ struct rtw_queue *pacl_node_q = &pacl_list->acl_node_q;
+
+ pmlmepriv->update_bcn = false;
+ pmlmeext->bstart_bss = false;
+
+ /* reset and init security priv , this can refine with rtw_reset_securitypriv23a */
+ memset((unsigned char *)&padapter->securitypriv, 0, sizeof (struct security_priv));
+ padapter->securitypriv.ndisauthtype = Ndis802_11AuthModeOpen;
+ padapter->securitypriv.ndisencryptstatus = Ndis802_11WEPDisabled;
+
+ /* for ACL */
+ spin_lock_bh(&pacl_node_q->lock);
+ phead = get_list_head(pacl_node_q);
+
+ list_for_each_safe(plist, ptmp, phead) {
+ paclnode = container_of(plist, struct rtw_wlan_acl_node, list);
+
+ if (paclnode->valid == true) {
+ paclnode->valid = false;
+
+ list_del_init(&paclnode->list);
+
+ pacl_list->num--;
+ }
+ }
+ spin_unlock_bh(&pacl_node_q->lock);
+
+ DBG_8723A("%s, free acl_node_queue, num =%d\n", __func__, pacl_list->num);
+
+ rtw_sta_flush23a(padapter);
+
+ /* free_assoc_sta_resources */
+ rtw_free_all_stainfo23a(padapter);
+
+ psta = rtw_get_bcmc_stainfo23a(padapter);
+ spin_lock_bh(&pstapriv->sta_hash_lock);
+ rtw_free_stainfo23a(padapter, psta);
+ spin_unlock_bh(&pstapriv->sta_hash_lock);
+
+ rtw_init_bcmc_stainfo23a(padapter);
+
+ rtw23a_free_mlme_priv_ie_data(pmlmepriv);
+}
diff --git a/drivers/staging/rtl8723au/core/rtw_cmd.c b/drivers/staging/rtl8723au/core/rtw_cmd.c
new file mode 100644
index 000000000..46aea16cb
--- /dev/null
+++ b/drivers/staging/rtl8723au/core/rtw_cmd.c
@@ -0,0 +1,1469 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2012 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ ******************************************************************************/
+#define _RTW_CMD_C_
+
+#include <osdep_service.h>
+#include <drv_types.h>
+#include <recv_osdep.h>
+#include <mlme_osdep.h>
+#include <rtl8723a_cmd.h>
+#include <rtw_sreset.h>
+
+static struct cmd_hdl wlancmds[] = {
+ GEN_DRV_CMD_HANDLER(0, NULL) /*0*/
+ GEN_DRV_CMD_HANDLER(0, NULL)
+ GEN_DRV_CMD_HANDLER(0, NULL)
+ GEN_DRV_CMD_HANDLER(0, NULL)
+ GEN_DRV_CMD_HANDLER(0, NULL)
+ GEN_DRV_CMD_HANDLER(0, NULL)
+ GEN_MLME_EXT_HANDLER(0, NULL)
+ GEN_MLME_EXT_HANDLER(0, NULL)
+ GEN_MLME_EXT_HANDLER(0, NULL)
+ GEN_MLME_EXT_HANDLER(0, NULL)
+ GEN_MLME_EXT_HANDLER(0, NULL) /*10*/
+ GEN_MLME_EXT_HANDLER(0, NULL)
+ GEN_MLME_EXT_HANDLER(0, NULL)
+ GEN_MLME_EXT_HANDLER(0, NULL)
+ GEN_MLME_EXT_HANDLER(sizeof(struct wlan_bssid_ex), join_cmd_hdl23a) /*14*/
+ GEN_MLME_EXT_HANDLER(sizeof(struct disconnect_parm), disconnect_hdl23a)
+ GEN_MLME_EXT_HANDLER(sizeof(struct wlan_bssid_ex), createbss_hdl23a)
+ GEN_MLME_EXT_HANDLER(sizeof(struct setopmode_parm), setopmode_hdl23a)
+ GEN_MLME_EXT_HANDLER(sizeof(struct sitesurvey_parm), sitesurvey_cmd_hdl23a) /*18*/
+ GEN_MLME_EXT_HANDLER(sizeof(struct setauth_parm), setauth_hdl23a)
+ GEN_MLME_EXT_HANDLER(sizeof(struct setkey_parm), setkey_hdl23a) /*20*/
+ GEN_MLME_EXT_HANDLER(sizeof(struct set_stakey_parm), set_stakey_hdl23a)
+ GEN_MLME_EXT_HANDLER(sizeof(struct set_assocsta_parm), NULL)
+ GEN_MLME_EXT_HANDLER(sizeof(struct del_assocsta_parm), NULL)
+ GEN_MLME_EXT_HANDLER(sizeof(struct setstapwrstate_parm), NULL)
+ GEN_MLME_EXT_HANDLER(sizeof(struct setbasicrate_parm), NULL)
+ GEN_MLME_EXT_HANDLER(sizeof(struct getbasicrate_parm), NULL)
+ GEN_MLME_EXT_HANDLER(sizeof(struct setdatarate_parm), NULL)
+ GEN_MLME_EXT_HANDLER(sizeof(struct getdatarate_parm), NULL)
+ GEN_MLME_EXT_HANDLER(sizeof(struct setphyinfo_parm), NULL)
+ GEN_MLME_EXT_HANDLER(sizeof(struct getphyinfo_parm), NULL) /*30*/
+ GEN_MLME_EXT_HANDLER(sizeof(struct setphy_parm), NULL)
+ GEN_MLME_EXT_HANDLER(sizeof(struct getphy_parm), NULL)
+ GEN_MLME_EXT_HANDLER(0, NULL)
+ GEN_MLME_EXT_HANDLER(0, NULL)
+ GEN_MLME_EXT_HANDLER(0, NULL)
+ GEN_MLME_EXT_HANDLER(0, NULL)
+ GEN_MLME_EXT_HANDLER(0, NULL)
+ GEN_MLME_EXT_HANDLER(0, NULL)
+ GEN_MLME_EXT_HANDLER(0, NULL)
+ GEN_MLME_EXT_HANDLER(0, NULL) /*40*/
+ GEN_MLME_EXT_HANDLER(0, NULL)
+ GEN_MLME_EXT_HANDLER(0, NULL)
+ GEN_MLME_EXT_HANDLER(0, NULL)
+ GEN_MLME_EXT_HANDLER(0, NULL)
+ GEN_MLME_EXT_HANDLER(sizeof(struct addBaReq_parm), add_ba_hdl23a)
+ GEN_MLME_EXT_HANDLER(sizeof(struct set_ch_parm), set_ch_hdl23a) /* 46 */
+ GEN_MLME_EXT_HANDLER(0, NULL)
+ GEN_MLME_EXT_HANDLER(0, NULL)
+ GEN_MLME_EXT_HANDLER(0, NULL)
+ GEN_MLME_EXT_HANDLER(0, NULL) /*50*/
+ GEN_MLME_EXT_HANDLER(0, NULL)
+ GEN_MLME_EXT_HANDLER(0, NULL)
+ GEN_MLME_EXT_HANDLER(0, NULL)
+ GEN_MLME_EXT_HANDLER(0, NULL)
+ GEN_MLME_EXT_HANDLER(sizeof(struct Tx_Beacon_param), tx_beacon_hdl23a) /*55*/
+
+ GEN_MLME_EXT_HANDLER(0, mlme_evt_hdl23a) /*56*/
+ GEN_MLME_EXT_HANDLER(0, rtw_drvextra_cmd_hdl23a) /*57*/
+
+ GEN_MLME_EXT_HANDLER(0, h2c_msg_hdl23a) /*58*/
+ GEN_MLME_EXT_HANDLER(sizeof(struct SetChannelPlan_param), set_chplan_hdl23a) /*59*/
+ GEN_MLME_EXT_HANDLER(sizeof(struct LedBlink_param), led_blink_hdl23a) /*60*/
+
+ GEN_MLME_EXT_HANDLER(sizeof(struct SetChannelSwitch_param), set_csa_hdl23a) /*61*/
+ GEN_MLME_EXT_HANDLER(sizeof(struct TDLSoption_param), tdls_hdl23a) /*62*/
+};
+
+struct _cmd_callback rtw_cmd_callback[] = {
+ {GEN_CMD_CODE(_Read_MACREG), NULL}, /*0*/
+ {GEN_CMD_CODE(_Write_MACREG), NULL},
+ {GEN_CMD_CODE(_Read_BBREG), &rtw_getbbrfreg_cmdrsp_callback23a},
+ {GEN_CMD_CODE(_Write_BBREG), NULL},
+ {GEN_CMD_CODE(_Read_RFREG), &rtw_getbbrfreg_cmdrsp_callback23a},
+ {GEN_CMD_CODE(_Write_RFREG), NULL}, /*5*/
+ {GEN_CMD_CODE(_Read_EEPROM), NULL},
+ {GEN_CMD_CODE(_Write_EEPROM), NULL},
+ {GEN_CMD_CODE(_Read_EFUSE), NULL},
+ {GEN_CMD_CODE(_Write_EFUSE), NULL},
+
+ {GEN_CMD_CODE(_Read_CAM), NULL}, /*10*/
+ {GEN_CMD_CODE(_Write_CAM), NULL},
+ {GEN_CMD_CODE(_setBCNITV), NULL},
+ {GEN_CMD_CODE(_setMBIDCFG), NULL},
+ {GEN_CMD_CODE(_JoinBss), &rtw_joinbss_cmd23a_callback}, /*14*/
+ {GEN_CMD_CODE(_DisConnect), &rtw_disassoc_cmd23a_callback}, /*15*/
+ {GEN_CMD_CODE(_CreateBss), &rtw_createbss_cmd23a_callback},
+ {GEN_CMD_CODE(_SetOpMode), NULL},
+ {GEN_CMD_CODE(_SiteSurvey), &rtw_survey_cmd_callback23a}, /*18*/
+ {GEN_CMD_CODE(_SetAuth), NULL},
+
+ {GEN_CMD_CODE(_SetKey), NULL}, /*20*/
+ {GEN_CMD_CODE(_SetStaKey), &rtw_setstaKey_cmdrsp_callback23a},
+ {GEN_CMD_CODE(_SetAssocSta), &rtw_setassocsta_cmdrsp_callback23a},
+ {GEN_CMD_CODE(_DelAssocSta), NULL},
+ {GEN_CMD_CODE(_SetStaPwrState), NULL},
+ {GEN_CMD_CODE(_SetBasicRate), NULL}, /*25*/
+ {GEN_CMD_CODE(_GetBasicRate), NULL},
+ {GEN_CMD_CODE(_SetDataRate), NULL},
+ {GEN_CMD_CODE(_GetDataRate), NULL},
+ {GEN_CMD_CODE(_SetPhyInfo), NULL},
+
+ {GEN_CMD_CODE(_GetPhyInfo), NULL}, /*30*/
+ {GEN_CMD_CODE(_SetPhy), NULL},
+ {GEN_CMD_CODE(_GetPhy), NULL},
+ {GEN_CMD_CODE(_readRssi), NULL},
+ {GEN_CMD_CODE(_readGain), NULL},
+ {GEN_CMD_CODE(_SetAtim), NULL}, /*35*/
+ {GEN_CMD_CODE(_SetPwrMode), NULL},
+ {GEN_CMD_CODE(_JoinbssRpt), NULL},
+ {GEN_CMD_CODE(_SetRaTable), NULL},
+ {GEN_CMD_CODE(_GetRaTable), NULL},
+
+ {GEN_CMD_CODE(_GetCCXReport), NULL}, /*40*/
+ {GEN_CMD_CODE(_GetDTMReport), NULL},
+ {GEN_CMD_CODE(_GetTXRateStatistics), NULL},
+ {GEN_CMD_CODE(_SetUsbSuspend), NULL},
+ {GEN_CMD_CODE(_SetH2cLbk), NULL},
+ {GEN_CMD_CODE(_AddBAReq), NULL}, /*45*/
+ {GEN_CMD_CODE(_SetChannel), NULL}, /*46*/
+ {GEN_CMD_CODE(_SetTxPower), NULL},
+ {GEN_CMD_CODE(_SwitchAntenna), NULL},
+ {GEN_CMD_CODE(_SetCrystalCap), NULL},
+ {GEN_CMD_CODE(_SetSingleCarrierTx), NULL}, /*50*/
+
+ {GEN_CMD_CODE(_SetSingleToneTx), NULL}, /*51*/
+ {GEN_CMD_CODE(_SetCarrierSuppressionTx), NULL},
+ {GEN_CMD_CODE(_SetContinuousTx), NULL},
+ {GEN_CMD_CODE(_SwitchBandwidth), NULL}, /*54*/
+ {GEN_CMD_CODE(_TX_Beacon), NULL},/*55*/
+
+ {GEN_CMD_CODE(_Set_MLME_EVT), NULL},/*56*/
+ {GEN_CMD_CODE(_Set_Drv_Extra), NULL},/*57*/
+ {GEN_CMD_CODE(_Set_H2C_MSG), NULL},/*58*/
+ {GEN_CMD_CODE(_SetChannelPlan), NULL},/*59*/
+ {GEN_CMD_CODE(_LedBlink), NULL},/*60*/
+
+ {GEN_CMD_CODE(_SetChannelSwitch), NULL},/*61*/
+ {GEN_CMD_CODE(_TDLS), NULL},/*62*/
+};
+
+/*
+Caller and the rtw_cmd_thread23a can protect cmd_q by spin_lock.
+No irqsave is necessary.
+*/
+
+int rtw_init_cmd_priv23a(struct cmd_priv *pcmdpriv)
+{
+ int res = _SUCCESS;
+
+ pcmdpriv->cmd_issued_cnt = 0;
+ pcmdpriv->cmd_done_cnt = 0;
+ pcmdpriv->rsp_cnt = 0;
+
+ pcmdpriv->wq = alloc_workqueue("rtl8723au_cmd", 0, 1);
+ if (!pcmdpriv->wq)
+ res = _FAIL;
+
+ return res;
+}
+
+/* forward definition */
+
+static void rtw_irq_work(struct work_struct *work);
+
+u32 rtw_init_evt_priv23a(struct evt_priv *pevtpriv)
+{
+ pevtpriv->wq = alloc_workqueue("rtl8723au_evt", 0, 1);
+
+ INIT_WORK(&pevtpriv->irq_wk, rtw_irq_work);
+
+ return _SUCCESS;
+}
+
+void rtw_free_evt_priv23a(struct evt_priv *pevtpriv)
+{
+ cancel_work_sync(&pevtpriv->irq_wk);
+}
+
+static int rtw_cmd_filter(struct cmd_priv *pcmdpriv, struct cmd_obj *cmd_obj)
+{
+ /* set to true to allow enqueuing cmd when hw_init_completed is false */
+ u8 bAllow = false;
+
+ if (cmd_obj->cmdcode == GEN_CMD_CODE(_SetChannelPlan))
+ bAllow = true;
+
+ if (pcmdpriv->padapter->hw_init_completed == false && bAllow == false)
+ return _FAIL;
+ return _SUCCESS;
+}
+
+static void rtw_cmd_work(struct work_struct *work);
+
+int rtw_enqueue_cmd23a(struct cmd_priv *pcmdpriv, struct cmd_obj *cmd_obj)
+{
+ int res = _FAIL;
+
+ if (!cmd_obj)
+ goto exit;
+
+ cmd_obj->padapter = pcmdpriv->padapter;
+
+ res = rtw_cmd_filter(pcmdpriv, cmd_obj);
+ if (res == _FAIL) {
+ rtw_free_cmd_obj23a(cmd_obj);
+ goto exit;
+ }
+
+ INIT_WORK(&cmd_obj->work, rtw_cmd_work);
+
+ res = queue_work(pcmdpriv->wq, &cmd_obj->work);
+
+ if (!res) {
+ printk(KERN_ERR "%s: Call to queue_work() failed\n", __func__);
+ res = _FAIL;
+ } else
+ res = _SUCCESS;
+exit:
+
+ return res;
+}
+
+void rtw_free_cmd_obj23a(struct cmd_obj *pcmd)
+{
+
+ if (pcmd->cmdcode != _JoinBss_CMD_ &&
+ pcmd->cmdcode != _CreateBss_CMD_) {
+ /* free parmbuf in cmd_obj */
+ kfree(pcmd->parmbuf);
+ }
+
+ if (pcmd->rsp) {
+ if (pcmd->rspsz != 0) {
+ /* free rsp in cmd_obj */
+ kfree(pcmd->rsp);
+ }
+ }
+
+ kfree(pcmd);
+}
+
+static void rtw_cmd_work(struct work_struct *work)
+{
+ int (*cmd_hdl)(struct rtw_adapter *padapter, const u8 *pbuf);
+ void (*pcmd_callback)(struct rtw_adapter *dev, struct cmd_obj *pcmd);
+ struct cmd_priv *pcmdpriv;
+ struct cmd_obj *pcmd = container_of(work, struct cmd_obj, work);
+
+ pcmdpriv = &pcmd->padapter->cmdpriv;
+
+ if (rtw_cmd_filter(pcmdpriv, pcmd) == _FAIL) {
+ pcmd->res = H2C_DROPPED;
+ goto post_process;
+ }
+
+ pcmdpriv->cmd_issued_cnt++;
+
+ pcmd->cmdsz = ALIGN(pcmd->cmdsz, 4);
+
+ if (pcmd->cmdcode < (sizeof(wlancmds)/sizeof(struct cmd_hdl))) {
+ cmd_hdl = wlancmds[pcmd->cmdcode].h2cfuns;
+
+ if (cmd_hdl)
+ pcmd->res = cmd_hdl(pcmd->padapter, pcmd->parmbuf);
+ else
+ pcmd->res = H2C_DROPPED;
+ } else
+ pcmd->res = H2C_PARAMETERS_ERROR;
+
+post_process:
+ /* call callback function for post-processed */
+ if (pcmd->cmdcode < (sizeof(rtw_cmd_callback) /
+ sizeof(struct _cmd_callback))) {
+ pcmd_callback = rtw_cmd_callback[pcmd->cmdcode].callback;
+ if (!pcmd_callback) {
+ RT_TRACE(_module_rtl871x_cmd_c_, _drv_info_,
+ "mlme_cmd_hdl(): pcmd_callback = 0x%p, cmdcode = 0x%x\n",
+ pcmd_callback, pcmd->cmdcode);
+ rtw_free_cmd_obj23a(pcmd);
+ } else {
+ /* need consider that free cmd_obj in
+ rtw_cmd_callback */
+ pcmd_callback(pcmd->padapter, pcmd);
+ }
+ } else {
+ RT_TRACE(_module_rtl871x_cmd_c_, _drv_err_,
+ "%s: cmdcode = 0x%x callback not defined!\n",
+ __func__, pcmd->cmdcode);
+ rtw_free_cmd_obj23a(pcmd);
+ }
+}
+
+
+int rtw_sitesurvey_cmd23a(struct rtw_adapter *padapter,
+ struct cfg80211_ssid *ssid, int ssid_num,
+ struct rtw_ieee80211_channel *ch, int ch_num)
+{
+ int res = _FAIL;
+ struct cmd_obj *ph2c;
+ struct sitesurvey_parm *psurveyPara;
+ struct cmd_priv *pcmdpriv = &padapter->cmdpriv;
+ struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
+
+ if (check_fwstate(pmlmepriv, _FW_LINKED))
+ rtw_lps_ctrl_wk_cmd23a(padapter, LPS_CTRL_SCAN, 1);
+
+ ph2c = kzalloc(sizeof(struct cmd_obj), GFP_ATOMIC);
+ if (!ph2c)
+ return _FAIL;
+
+ psurveyPara = kzalloc(sizeof(struct sitesurvey_parm), GFP_ATOMIC);
+ if (!psurveyPara) {
+ kfree(ph2c);
+ return _FAIL;
+ }
+
+ rtw_free_network_queue23a(padapter);
+
+ RT_TRACE(_module_rtl871x_cmd_c_, _drv_info_,
+ "%s: flush network queue\n", __func__);
+
+ init_h2fwcmd_w_parm_no_rsp(ph2c, psurveyPara,
+ GEN_CMD_CODE(_SiteSurvey));
+
+ /* psurveyPara->bsslimit = 48; */
+ psurveyPara->scan_mode = pmlmepriv->scan_mode;
+
+ /* prepare ssid list */
+ if (ssid) {
+ int i;
+
+ for (i = 0; i < ssid_num && i < RTW_SSID_SCAN_AMOUNT; i++) {
+ if (ssid[i].ssid_len) {
+ memcpy(&psurveyPara->ssid[i], &ssid[i],
+ sizeof(struct cfg80211_ssid));
+ psurveyPara->ssid_num++;
+ }
+ }
+ }
+
+ /* prepare channel list */
+ if (ch) {
+ int i;
+
+ for (i = 0; i < ch_num && i < RTW_CHANNEL_SCAN_AMOUNT; i++) {
+ if (ch[i].hw_value &&
+ !(ch[i].flags & IEEE80211_CHAN_DISABLED)) {
+ memcpy(&psurveyPara->ch[i], &ch[i],
+ sizeof(struct rtw_ieee80211_channel));
+ psurveyPara->ch_num++;
+ }
+ }
+ }
+
+ set_fwstate(pmlmepriv, _FW_UNDER_SURVEY);
+
+ res = rtw_enqueue_cmd23a(pcmdpriv, ph2c);
+
+ if (res == _SUCCESS) {
+ mod_timer(&pmlmepriv->scan_to_timer, jiffies +
+ msecs_to_jiffies(SCANNING_TIMEOUT));
+
+ pmlmepriv->scan_interval = SCAN_INTERVAL;/* 30*2 sec = 60sec */
+ } else
+ _clr_fwstate_(pmlmepriv, _FW_UNDER_SURVEY);
+
+ return res;
+}
+
+void rtw_getbbrfreg_cmdrsp_callback23a(struct rtw_adapter *padapter,
+ struct cmd_obj *pcmd)
+{
+ kfree(pcmd->parmbuf);
+ kfree(pcmd);
+}
+
+int rtw_createbss_cmd23a(struct rtw_adapter *padapter)
+{
+ struct cmd_obj *pcmd;
+ struct cmd_priv *pcmdpriv = &padapter->cmdpriv;
+ struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
+ struct wlan_bssid_ex *pdev_network;
+ u8 res = _SUCCESS;
+
+ pdev_network = &padapter->registrypriv.dev_network;
+
+ if (pmlmepriv->assoc_ssid.ssid_len == 0) {
+ RT_TRACE(_module_rtl871x_cmd_c_, _drv_info_,
+ "createbss for Any SSid:%s\n",
+ pmlmepriv->assoc_ssid.ssid);
+ } else {
+ RT_TRACE(_module_rtl871x_cmd_c_, _drv_info_,
+ "createbss for SSid:%s\n",
+ pmlmepriv->assoc_ssid.ssid);
+ }
+
+ pcmd = kzalloc(sizeof(struct cmd_obj), GFP_ATOMIC);
+ if (!pcmd) {
+ res = _FAIL;
+ goto exit;
+ }
+
+ pcmd->cmdcode = _CreateBss_CMD_;
+ pcmd->parmbuf = (unsigned char *)pdev_network;
+ pcmd->cmdsz = get_wlan_bssid_ex_sz(pdev_network);
+ pcmd->rsp = NULL;
+ pcmd->rspsz = 0;
+
+ pdev_network->Length = pcmd->cmdsz;
+
+ res = rtw_enqueue_cmd23a(pcmdpriv, pcmd);
+
+exit:
+
+ return res;
+}
+
+int rtw_joinbss_cmd23a(struct rtw_adapter *padapter,
+ struct wlan_network *pnetwork)
+{
+ int res = _SUCCESS;
+ struct wlan_bssid_ex *psecnetwork;
+ struct cmd_obj *pcmd;
+ struct cmd_priv *pcmdpriv = &padapter->cmdpriv;
+ struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
+ struct security_priv *psecuritypriv = &padapter->securitypriv;
+ struct registry_priv *pregistrypriv = &padapter->registrypriv;
+ struct ht_priv *phtpriv = &pmlmepriv->htpriv;
+ enum nl80211_iftype ifmode;
+ struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv;
+ struct mlme_ext_info *pmlmeinfo = &pmlmeext->mlmext_info;
+
+ ifmode = pnetwork->network.ifmode;
+
+ if (pmlmepriv->assoc_ssid.ssid_len == 0) {
+ RT_TRACE(_module_rtl871x_cmd_c_, _drv_info_,
+ "+Join cmd: Any SSid\n");
+ } else {
+ RT_TRACE(_module_rtl871x_cmd_c_, _drv_notice_,
+ "+Join cmd: SSid =[%s]\n",
+ pmlmepriv->assoc_ssid.ssid);
+ }
+
+ pcmd = kzalloc(sizeof(struct cmd_obj), GFP_ATOMIC);
+ if (!pcmd) {
+ res = _FAIL;
+ RT_TRACE(_module_rtl871x_cmd_c_, _drv_err_,
+ "rtw_joinbss_cmd23a: memory allocate for cmd_obj fail!!!\n");
+ goto exit;
+ }
+
+ /* for hidden ap to set fw_state here */
+ if (!check_fwstate(pmlmepriv, WIFI_STATION_STATE|WIFI_ADHOC_STATE)) {
+ switch (ifmode) {
+ case NL80211_IFTYPE_ADHOC:
+ set_fwstate(pmlmepriv, WIFI_ADHOC_STATE);
+ break;
+ case NL80211_IFTYPE_P2P_CLIENT:
+ case NL80211_IFTYPE_STATION:
+ set_fwstate(pmlmepriv, WIFI_STATION_STATE);
+ break;
+ default:
+ break;
+ }
+ }
+
+ psecnetwork = &psecuritypriv->sec_bss;
+ if (!psecnetwork) {
+ kfree(pcmd);
+ res = _FAIL;
+
+ RT_TRACE(_module_rtl871x_cmd_c_, _drv_err_,
+ "rtw_joinbss_cmd23a :psecnetwork == NULL!!!\n");
+
+ goto exit;
+ }
+
+ memset(psecnetwork, 0, sizeof(struct wlan_bssid_ex));
+
+ memcpy(psecnetwork, &pnetwork->network,
+ get_wlan_bssid_ex_sz(&pnetwork->network));
+
+ psecnetwork->IELength = 0;
+ /* Added by Albert 2009/02/18 */
+ /* If the the driver wants to use the bssid to create the
+ * connection. If not, we have to copy the connecting AP's
+ * MAC address to it so that the driver just has the bssid
+ * information for PMKIDList searching. */
+
+ if (pmlmepriv->assoc_by_bssid == false)
+ ether_addr_copy(&pmlmepriv->assoc_bssid[0],
+ &pnetwork->network.MacAddress[0]);
+
+ psecnetwork->IELength =
+ rtw_restruct_sec_ie23a(padapter, &pnetwork->network.IEs[0],
+ &psecnetwork->IEs[0],
+ pnetwork->network.IELength);
+
+ pmlmepriv->qos_option = 0;
+
+ if (pregistrypriv->wmm_enable) {
+ u32 tmp_len;
+
+ tmp_len = rtw_restruct_wmm_ie23a(padapter,
+ &pnetwork->network.IEs[0],
+ &psecnetwork->IEs[0],
+ pnetwork->network.IELength,
+ psecnetwork->IELength);
+
+ if (psecnetwork->IELength != tmp_len) {
+ psecnetwork->IELength = tmp_len;
+ /* There is WMM IE in this corresp. beacon */
+ pmlmepriv->qos_option = 1;
+ } else {
+ /* There is no WMM IE in this corresp. beacon */
+ pmlmepriv->qos_option = 0;
+ }
+ }
+
+ phtpriv->ht_option = false;
+ if (pregistrypriv->ht_enable) {
+ u32 algo = padapter->securitypriv.dot11PrivacyAlgrthm;
+ /* Added by Albert 2010/06/23 */
+ /* For the WEP mode, we will use the bg mode to do
+ the connection to avoid some IOT issue. */
+ /* Especially for Realtek 8192u SoftAP. */
+ if (algo != WLAN_CIPHER_SUITE_WEP40 &&
+ algo != WLAN_CIPHER_SUITE_WEP104 &&
+ algo != WLAN_CIPHER_SUITE_TKIP) {
+ /* rtw_restructure_ht_ie23a */
+ rtw_restructure_ht_ie23a(padapter,
+ &pnetwork->network.IEs[0],
+ &psecnetwork->IEs[0],
+ pnetwork->network.IELength,
+ &psecnetwork->IELength);
+ }
+ }
+
+ pmlmeinfo->assoc_AP_vendor =
+ check_assoc_AP23a(pnetwork->network.IEs,
+ pnetwork->network.IELength);
+
+ if (pmlmeinfo->assoc_AP_vendor == HT_IOT_PEER_TENDA)
+ padapter->pwrctrlpriv.smart_ps = 0;
+ else
+ padapter->pwrctrlpriv.smart_ps =
+ padapter->registrypriv.smart_ps;
+
+ DBG_8723A("%s: smart_ps =%d\n", __func__,
+ padapter->pwrctrlpriv.smart_ps);
+
+ /* get cmdsz before endian conversion */
+ pcmd->cmdsz = get_wlan_bssid_ex_sz(psecnetwork);
+
+ pcmd->cmdcode = _JoinBss_CMD_;/* GEN_CMD_CODE(_JoinBss) */
+ pcmd->parmbuf = (unsigned char *)psecnetwork;
+ pcmd->rsp = NULL;
+ pcmd->rspsz = 0;
+
+ res = rtw_enqueue_cmd23a(pcmdpriv, pcmd);
+exit:
+
+ return res;
+}
+
+int rtw_disassoc_cmd23a(struct rtw_adapter *padapter, u32 deauth_timeout_ms,
+ bool enqueue)
+{
+ struct cmd_obj *cmdobj = NULL;
+ struct disconnect_parm *param = NULL;
+ struct cmd_priv *cmdpriv = &padapter->cmdpriv;
+ int res = _SUCCESS;
+
+ RT_TRACE(_module_rtl871x_cmd_c_, _drv_notice_,
+ "+rtw_disassoc_cmd23a\n");
+
+ /* prepare cmd parameter */
+ param = kzalloc(sizeof(*param), GFP_ATOMIC);
+ if (param == NULL) {
+ res = _FAIL;
+ goto exit;
+ }
+ param->deauth_timeout_ms = deauth_timeout_ms;
+
+ if (enqueue) {
+ /* need enqueue, prepare cmd_obj and enqueue */
+ cmdobj = kzalloc(sizeof(struct cmd_obj), GFP_ATOMIC);
+ if (!cmdobj) {
+ res = _FAIL;
+ kfree(param);
+ goto exit;
+ }
+ init_h2fwcmd_w_parm_no_rsp(cmdobj, param, _DisConnect_CMD_);
+ res = rtw_enqueue_cmd23a(cmdpriv, cmdobj);
+ } else {
+ /* no need to enqueue, do the cmd hdl directly and
+ free cmd parameter */
+ if (H2C_SUCCESS != disconnect_hdl23a(padapter, (u8 *)param))
+ res = _FAIL;
+ kfree(param);
+ }
+
+exit:
+ return res;
+}
+
+int rtw_setopmode_cmd23a(struct rtw_adapter *padapter,
+ enum nl80211_iftype ifmode)
+{
+ struct cmd_obj *ph2c;
+ struct setopmode_parm *psetop;
+ struct cmd_priv *pcmdpriv = &padapter->cmdpriv;
+ int res = _SUCCESS;
+
+ ph2c = kzalloc(sizeof(struct cmd_obj), GFP_KERNEL);
+ if (!ph2c) {
+ res = false;
+ goto exit;
+ }
+ psetop = kzalloc(sizeof(struct setopmode_parm), GFP_KERNEL);
+
+ if (!psetop) {
+ kfree(ph2c);
+ res = false;
+ goto exit;
+ }
+
+ init_h2fwcmd_w_parm_no_rsp(ph2c, psetop, _SetOpMode_CMD_);
+ psetop->mode = ifmode;
+
+ res = rtw_enqueue_cmd23a(pcmdpriv, ph2c);
+exit:
+ return res;
+}
+
+int rtw_setstakey_cmd23a(struct rtw_adapter *padapter, u8 *psta, u8 unicast_key)
+{
+ struct cmd_obj *ph2c;
+ struct set_stakey_parm *psetstakey_para;
+ struct cmd_priv *pcmdpriv = &padapter->cmdpriv;
+ struct set_stakey_rsp *psetstakey_rsp = NULL;
+ struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
+ struct security_priv *psecuritypriv = &padapter->securitypriv;
+ struct sta_info *sta = (struct sta_info *)psta;
+ int res = _SUCCESS;
+
+ ph2c = kzalloc(sizeof(struct cmd_obj), GFP_KERNEL);
+ if (!ph2c) {
+ res = _FAIL;
+ goto exit;
+ }
+
+ psetstakey_para = kzalloc(sizeof(struct set_stakey_parm), GFP_KERNEL);
+ if (!psetstakey_para) {
+ kfree(ph2c);
+ res = _FAIL;
+ goto exit;
+ }
+
+ psetstakey_rsp = kzalloc(sizeof(struct set_stakey_rsp), GFP_KERNEL);
+ if (!psetstakey_rsp) {
+ kfree(ph2c);
+ kfree(psetstakey_para);
+ res = _FAIL;
+ goto exit;
+ }
+
+ init_h2fwcmd_w_parm_no_rsp(ph2c, psetstakey_para, _SetStaKey_CMD_);
+ ph2c->rsp = (u8 *) psetstakey_rsp;
+ ph2c->rspsz = sizeof(struct set_stakey_rsp);
+
+ ether_addr_copy(psetstakey_para->addr, sta->hwaddr);
+
+ if (check_fwstate(pmlmepriv, WIFI_STATION_STATE)) {
+ psetstakey_para->algorithm =
+ (unsigned char)psecuritypriv->dot11PrivacyAlgrthm;
+ } else {
+ GET_ENCRY_ALGO(psecuritypriv, sta, psetstakey_para->algorithm,
+ false);
+ }
+
+ if (unicast_key == true) {
+ memcpy(&psetstakey_para->key, &sta->dot118021x_UncstKey, 16);
+ } else {
+ int idx = psecuritypriv->dot118021XGrpKeyid;
+
+ memcpy(&psetstakey_para->key,
+ &psecuritypriv->dot118021XGrpKey[idx].skey, 16);
+ }
+
+ /* jeff: set this because at least sw key is ready */
+ padapter->securitypriv.busetkipkey = 1;
+
+ res = rtw_enqueue_cmd23a(pcmdpriv, ph2c);
+
+exit:
+
+ return res;
+}
+
+int rtw_clearstakey_cmd23a(struct rtw_adapter *padapter, u8 *psta, u8 entry,
+ u8 enqueue)
+{
+ struct cmd_obj *ph2c;
+ struct set_stakey_parm *psetstakey_para;
+ struct cmd_priv *pcmdpriv = &padapter->cmdpriv;
+ struct set_stakey_rsp *psetstakey_rsp = NULL;
+ struct sta_info *sta = (struct sta_info *)psta;
+ int res = _SUCCESS;
+
+ if (!enqueue) {
+ clear_cam_entry23a(padapter, entry);
+ } else {
+ ph2c = kzalloc(sizeof(struct cmd_obj), GFP_KERNEL);
+ if (!ph2c) {
+ res = _FAIL;
+ goto exit;
+ }
+
+ psetstakey_para = kzalloc(sizeof(struct set_stakey_parm),
+ GFP_KERNEL);
+ if (!psetstakey_para) {
+ kfree(ph2c);
+ res = _FAIL;
+ goto exit;
+ }
+
+ psetstakey_rsp = kzalloc(sizeof(struct set_stakey_rsp),
+ GFP_KERNEL);
+ if (!psetstakey_rsp) {
+ kfree(ph2c);
+ kfree(psetstakey_para);
+ res = _FAIL;
+ goto exit;
+ }
+
+ init_h2fwcmd_w_parm_no_rsp(ph2c, psetstakey_para,
+ _SetStaKey_CMD_);
+ ph2c->rsp = (u8 *) psetstakey_rsp;
+ ph2c->rspsz = sizeof(struct set_stakey_rsp);
+
+ ether_addr_copy(psetstakey_para->addr, sta->hwaddr);
+
+ psetstakey_para->algorithm = 0;
+
+ psetstakey_para->id = entry;
+
+ res = rtw_enqueue_cmd23a(pcmdpriv, ph2c);
+ }
+exit:
+ return res;
+}
+
+int rtw_addbareq_cmd23a(struct rtw_adapter *padapter, u8 tid, u8 *addr)
+{
+ struct cmd_priv *pcmdpriv = &padapter->cmdpriv;
+ struct cmd_obj *ph2c;
+ struct addBaReq_parm *paddbareq_parm;
+ int res = _SUCCESS;
+
+ if (tid >= MAXTID) {
+ res = _FAIL;
+ goto exit;
+ }
+
+ ph2c = kzalloc(sizeof(struct cmd_obj), GFP_ATOMIC);
+ if (!ph2c) {
+ res = _FAIL;
+ goto exit;
+ }
+
+ paddbareq_parm = kzalloc(sizeof(struct addBaReq_parm), GFP_ATOMIC);
+ if (!paddbareq_parm) {
+ kfree(ph2c);
+ res = _FAIL;
+ goto exit;
+ }
+
+ paddbareq_parm->tid = tid;
+ ether_addr_copy(paddbareq_parm->addr, addr);
+
+ init_h2fwcmd_w_parm_no_rsp(ph2c, paddbareq_parm,
+ GEN_CMD_CODE(_AddBAReq));
+
+ res = rtw_enqueue_cmd23a(pcmdpriv, ph2c);
+exit:
+ return res;
+}
+
+int rtw_dynamic_chk_wk_cmd23a(struct rtw_adapter *padapter)
+{
+ struct cmd_obj *ph2c;
+ struct drvextra_cmd_parm *pdrvextra_cmd_parm;
+ struct cmd_priv *pcmdpriv = &padapter->cmdpriv;
+ int res = _SUCCESS;
+
+ ph2c = kzalloc(sizeof(struct cmd_obj), GFP_ATOMIC);
+ if (!ph2c) {
+ res = _FAIL;
+ goto exit;
+ }
+
+ pdrvextra_cmd_parm = kzalloc(sizeof(*pdrvextra_cmd_parm), GFP_ATOMIC);
+ if (!pdrvextra_cmd_parm) {
+ kfree(ph2c);
+ res = _FAIL;
+ goto exit;
+ }
+
+ pdrvextra_cmd_parm->ec_id = DYNAMIC_CHK_WK_CID;
+ pdrvextra_cmd_parm->type_size = 0;
+ pdrvextra_cmd_parm->pbuf = (u8 *)padapter;
+
+ init_h2fwcmd_w_parm_no_rsp(ph2c, pdrvextra_cmd_parm,
+ GEN_CMD_CODE(_Set_Drv_Extra));
+
+ res = rtw_enqueue_cmd23a(pcmdpriv, ph2c);
+exit:
+
+ return res;
+}
+
+static void traffic_status_watchdog(struct rtw_adapter *padapter)
+{
+ u8 bEnterPS;
+ u8 bBusyTraffic = false, bTxBusyTraffic = false, bRxBusyTraffic = false;
+ u8 bHigherBusyTraffic = false, bHigherBusyRxTraffic = false;
+ u8 bHigherBusyTxTraffic = false;
+ struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
+ int BusyThreshold = 100;
+ struct rt_link_detect *ldi = &pmlmepriv->LinkDetectInfo;
+
+ /* */
+ /* Determine if our traffic is busy now */
+ /* */
+ if (check_fwstate(pmlmepriv, _FW_LINKED)) {
+ if (rtl8723a_BT_coexist(padapter))
+ BusyThreshold = 50;
+ else if (ldi->bBusyTraffic)
+ BusyThreshold = 75;
+ /* if we raise bBusyTraffic in last watchdog, using
+ lower threshold. */
+ if (ldi->NumRxOkInPeriod > BusyThreshold ||
+ ldi->NumTxOkInPeriod > BusyThreshold) {
+ bBusyTraffic = true;
+
+ if (ldi->NumRxOkInPeriod > ldi->NumTxOkInPeriod)
+ bRxBusyTraffic = true;
+ else
+ bTxBusyTraffic = true;
+ }
+
+ /* Higher Tx/Rx data. */
+ if (ldi->NumRxOkInPeriod > 4000 ||
+ ldi->NumTxOkInPeriod > 4000) {
+ bHigherBusyTraffic = true;
+
+ if (ldi->NumRxOkInPeriod > ldi->NumTxOkInPeriod)
+ bHigherBusyRxTraffic = true;
+ else
+ bHigherBusyTxTraffic = true;
+ }
+
+ if (!rtl8723a_BT_coexist(padapter) ||
+ !rtl8723a_BT_using_antenna_1(padapter)) {
+ /* check traffic for powersaving. */
+ if (((ldi->NumRxUnicastOkInPeriod +
+ ldi->NumTxOkInPeriod) > 8) ||
+ ldi->NumRxUnicastOkInPeriod > 2)
+ bEnterPS = false;
+ else
+ bEnterPS = true;
+
+ /* LeisurePS only work in infra mode. */
+ if (bEnterPS)
+ LPS_Enter23a(padapter);
+ else
+ LPS_Leave23a(padapter);
+ }
+ } else
+ LPS_Leave23a(padapter);
+
+ ldi->NumRxOkInPeriod = 0;
+ ldi->NumTxOkInPeriod = 0;
+ ldi->NumRxUnicastOkInPeriod = 0;
+ ldi->bBusyTraffic = bBusyTraffic;
+ ldi->bTxBusyTraffic = bTxBusyTraffic;
+ ldi->bRxBusyTraffic = bRxBusyTraffic;
+ ldi->bHigherBusyTraffic = bHigherBusyTraffic;
+ ldi->bHigherBusyRxTraffic = bHigherBusyRxTraffic;
+ ldi->bHigherBusyTxTraffic = bHigherBusyTxTraffic;
+}
+
+static void dynamic_chk_wk_hdl(struct rtw_adapter *padapter, u8 *pbuf, int sz)
+{
+ struct mlme_priv *pmlmepriv;
+
+ padapter = (struct rtw_adapter *)pbuf;
+ pmlmepriv = &padapter->mlmepriv;
+
+#ifdef CONFIG_8723AU_AP_MODE
+ if (check_fwstate(pmlmepriv, WIFI_AP_STATE))
+ expire_timeout_chk23a(padapter);
+#endif
+
+ rtl8723a_sreset_xmit_status_check(padapter);
+
+ linked_status_chk23a(padapter);
+ traffic_status_watchdog(padapter);
+
+ rtl8723a_HalDmWatchDog(padapter);
+
+ /* */
+ /* BT-Coexist */
+ /* */
+ rtl8723a_BT_do_coexist(padapter);
+}
+
+static void lps_ctrl_wk_hdl(struct rtw_adapter *padapter, u8 lps_ctrl_type)
+{
+ struct pwrctrl_priv *pwrpriv = &padapter->pwrctrlpriv;
+ struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
+ u8 mstatus;
+
+ if (check_fwstate(pmlmepriv, WIFI_ADHOC_MASTER_STATE) ||
+ check_fwstate(pmlmepriv, WIFI_ADHOC_STATE))
+ return;
+
+ switch (lps_ctrl_type) {
+ case LPS_CTRL_SCAN:
+ rtl8723a_BT_wifiscan_notify(padapter, true);
+ if (!rtl8723a_BT_using_antenna_1(padapter)) {
+ if (check_fwstate(pmlmepriv, _FW_LINKED))
+ LPS_Leave23a(padapter);
+ }
+ break;
+ case LPS_CTRL_JOINBSS:
+ LPS_Leave23a(padapter);
+ break;
+ case LPS_CTRL_CONNECT:
+ mstatus = 1;/* connect */
+ /* Reset LPS Setting */
+ padapter->pwrctrlpriv.LpsIdleCount = 0;
+ rtl8723a_set_FwJoinBssReport_cmd(padapter, 1);
+ rtl8723a_BT_mediastatus_notify(padapter, mstatus);
+ break;
+ case LPS_CTRL_DISCONNECT:
+ mstatus = 0;/* disconnect */
+ rtl8723a_BT_mediastatus_notify(padapter, mstatus);
+ if (!rtl8723a_BT_using_antenna_1(padapter))
+ LPS_Leave23a(padapter);
+ rtl8723a_set_FwJoinBssReport_cmd(padapter, 0);
+ break;
+ case LPS_CTRL_SPECIAL_PACKET:
+ pwrpriv->DelayLPSLastTimeStamp = jiffies;
+ rtl8723a_BT_specialpacket_notify(padapter);
+ if (!rtl8723a_BT_using_antenna_1(padapter))
+ LPS_Leave23a(padapter);
+ break;
+ case LPS_CTRL_LEAVE:
+ rtl8723a_BT_lps_leave(padapter);
+ if (!rtl8723a_BT_using_antenna_1(padapter))
+ LPS_Leave23a(padapter);
+ break;
+
+ default:
+ break;
+ }
+}
+
+int rtw_lps_ctrl_wk_cmd23a(struct rtw_adapter *padapter,
+ u8 lps_ctrl_type, u8 enqueue)
+{
+ struct cmd_obj *ph2c;
+ struct drvextra_cmd_parm *pdrvextra_cmd_parm;
+ struct cmd_priv *pcmdpriv = &padapter->cmdpriv;
+ int res = _SUCCESS;
+
+ if (enqueue) {
+ ph2c = kzalloc(sizeof(struct cmd_obj), GFP_ATOMIC);
+ if (!ph2c) {
+ res = _FAIL;
+ goto exit;
+ }
+
+ pdrvextra_cmd_parm = kzalloc(sizeof(struct drvextra_cmd_parm),
+ GFP_ATOMIC);
+ if (!pdrvextra_cmd_parm) {
+ kfree(ph2c);
+ res = _FAIL;
+ goto exit;
+ }
+
+ pdrvextra_cmd_parm->ec_id = LPS_CTRL_WK_CID;
+ pdrvextra_cmd_parm->type_size = lps_ctrl_type;
+ pdrvextra_cmd_parm->pbuf = NULL;
+
+ init_h2fwcmd_w_parm_no_rsp(ph2c, pdrvextra_cmd_parm,
+ GEN_CMD_CODE(_Set_Drv_Extra));
+
+ res = rtw_enqueue_cmd23a(pcmdpriv, ph2c);
+ } else
+ lps_ctrl_wk_hdl(padapter, lps_ctrl_type);
+exit:
+
+ return res;
+}
+
+int rtw_ps_cmd23a(struct rtw_adapter *padapter)
+{
+ struct cmd_obj *ppscmd;
+ struct drvextra_cmd_parm *pdrvextra_cmd_parm;
+ struct cmd_priv *pcmdpriv = &padapter->cmdpriv;
+ int res = _SUCCESS;
+
+ ppscmd = kzalloc(sizeof(struct cmd_obj), GFP_ATOMIC);
+ if (!ppscmd) {
+ res = _FAIL;
+ goto exit;
+ }
+
+ pdrvextra_cmd_parm = kzalloc(sizeof(struct drvextra_cmd_parm),
+ GFP_ATOMIC);
+ if (!pdrvextra_cmd_parm) {
+ kfree(ppscmd);
+ res = _FAIL;
+ goto exit;
+ }
+
+ pdrvextra_cmd_parm->ec_id = POWER_SAVING_CTRL_WK_CID;
+ pdrvextra_cmd_parm->pbuf = NULL;
+ init_h2fwcmd_w_parm_no_rsp(ppscmd, pdrvextra_cmd_parm,
+ GEN_CMD_CODE(_Set_Drv_Extra));
+
+ res = rtw_enqueue_cmd23a(pcmdpriv, ppscmd);
+exit:
+
+ return res;
+}
+
+#ifdef CONFIG_8723AU_AP_MODE
+
+static void rtw_chk_hi_queue_hdl(struct rtw_adapter *padapter)
+{
+ int cnt = 0;
+ struct sta_info *psta_bmc;
+ struct sta_priv *pstapriv = &padapter->stapriv;
+
+ psta_bmc = rtw_get_bcmc_stainfo23a(padapter);
+ if (!psta_bmc)
+ return;
+
+ if (psta_bmc->sleepq_len == 0) {
+ bool val;
+
+ val = rtl8723a_chk_hi_queue_empty(padapter);
+
+ while (!val) {
+ msleep(100);
+
+ cnt++;
+
+ if (cnt > 10)
+ break;
+
+ val = rtl8723a_chk_hi_queue_empty(padapter);
+ }
+
+ if (cnt <= 10) {
+ pstapriv->tim_bitmap &= ~BIT(0);
+ pstapriv->sta_dz_bitmap &= ~BIT(0);
+
+ update_beacon23a(padapter, WLAN_EID_TIM, NULL, false);
+ } else /* re check again */
+ rtw_chk_hi_queue_cmd23a(padapter);
+ }
+}
+
+int rtw_chk_hi_queue_cmd23a(struct rtw_adapter *padapter)
+{
+ struct cmd_obj *ph2c;
+ struct drvextra_cmd_parm *pdrvextra_cmd_parm;
+ struct cmd_priv *pcmdpriv = &padapter->cmdpriv;
+ int res = _SUCCESS;
+
+ ph2c = kzalloc(sizeof(struct cmd_obj), GFP_ATOMIC);
+ if (!ph2c) {
+ res = _FAIL;
+ goto exit;
+ }
+
+ pdrvextra_cmd_parm = kzalloc(sizeof(struct drvextra_cmd_parm),
+ GFP_ATOMIC);
+ if (!pdrvextra_cmd_parm) {
+ kfree(ph2c);
+ res = _FAIL;
+ goto exit;
+ }
+
+ pdrvextra_cmd_parm->ec_id = CHECK_HIQ_WK_CID;
+ pdrvextra_cmd_parm->type_size = 0;
+ pdrvextra_cmd_parm->pbuf = NULL;
+
+ init_h2fwcmd_w_parm_no_rsp(ph2c, pdrvextra_cmd_parm,
+ GEN_CMD_CODE(_Set_Drv_Extra));
+
+ res = rtw_enqueue_cmd23a(pcmdpriv, ph2c);
+exit:
+
+ return res;
+}
+#endif
+
+int rtw_c2h_wk_cmd23a(struct rtw_adapter *padapter, u8 *c2h_evt)
+{
+ struct cmd_obj *ph2c;
+ struct drvextra_cmd_parm *pdrvextra_cmd_parm;
+ struct cmd_priv *pcmdpriv = &padapter->cmdpriv;
+ int res = _SUCCESS;
+
+ ph2c = kzalloc(sizeof(struct cmd_obj), GFP_ATOMIC);
+ if (!ph2c) {
+ res = _FAIL;
+ goto exit;
+ }
+
+ pdrvextra_cmd_parm = kzalloc(sizeof(struct drvextra_cmd_parm),
+ GFP_ATOMIC);
+ if (!pdrvextra_cmd_parm) {
+ kfree(ph2c);
+ res = _FAIL;
+ goto exit;
+ }
+
+ pdrvextra_cmd_parm->ec_id = C2H_WK_CID;
+ pdrvextra_cmd_parm->type_size = c2h_evt?16:0;
+ pdrvextra_cmd_parm->pbuf = c2h_evt;
+
+ init_h2fwcmd_w_parm_no_rsp(ph2c, pdrvextra_cmd_parm,
+ GEN_CMD_CODE(_Set_Drv_Extra));
+
+ res = rtw_enqueue_cmd23a(pcmdpriv, ph2c);
+
+exit:
+
+ return res;
+}
+
+static int c2h_evt_hdl(struct rtw_adapter *adapter, struct c2h_evt_hdr *c2h_evt)
+{
+ int ret = _FAIL;
+ u8 buf[16];
+
+ if (!c2h_evt) {
+ /* No c2h event in cmd_obj, read c2h event before handling*/
+ if (c2h_evt_read23a(adapter, buf) == _SUCCESS) {
+ c2h_evt = (struct c2h_evt_hdr *)buf;
+
+ ret = c2h_handler_8723a(adapter, c2h_evt);
+ }
+ } else
+ ret = c2h_handler_8723a(adapter, c2h_evt);
+
+ return ret;
+}
+
+static void rtw_irq_work(struct work_struct *work)
+{
+ struct evt_priv *evtpriv;
+ struct rtw_adapter *adapter;
+
+ evtpriv = container_of(work, struct evt_priv, irq_wk);
+ adapter = container_of(evtpriv, struct rtw_adapter, evtpriv);
+
+ c2h_evt_clear23a(adapter);
+}
+
+void rtw_evt_work(struct work_struct *work)
+{
+ struct evt_work *ework;
+ struct rtw_adapter *adapter;
+
+ ework = container_of(work, struct evt_work, work);
+ adapter = ework->adapter;
+
+ c2h_evt_clear23a(adapter);
+
+ if (!c2h_evt_exist(&ework->u.c2h_evt)) {
+ kfree(ework);
+ return;
+ }
+
+ if (c2h_id_filter_ccx_8723a(ework->u.c2h_evt.id) == true) {
+ /* Handle CCX report here */
+ c2h_handler_8723a(adapter, &ework->u.c2h_evt);
+ kfree(ework);
+ } else {
+ /*
+ * Enqueue into cmd_thread for others.
+ * ework will be turned into a c2h_evt and freed once it
+ * has been consumed.
+ */
+ rtw_c2h_wk_cmd23a(adapter, (u8 *)&ework->u.c2h_evt);
+ }
+}
+
+int rtw_drvextra_cmd_hdl23a(struct rtw_adapter *padapter, const u8 *pbuf)
+{
+ const struct drvextra_cmd_parm *pdrvextra_cmd;
+
+ if (!pbuf)
+ return H2C_PARAMETERS_ERROR;
+
+ pdrvextra_cmd = (struct drvextra_cmd_parm *)pbuf;
+
+ switch (pdrvextra_cmd->ec_id) {
+ case DYNAMIC_CHK_WK_CID:
+ dynamic_chk_wk_hdl(padapter, pdrvextra_cmd->pbuf,
+ pdrvextra_cmd->type_size);
+ break;
+ case POWER_SAVING_CTRL_WK_CID:
+ rtw_ps_processor23a(padapter);
+ break;
+ case LPS_CTRL_WK_CID:
+ lps_ctrl_wk_hdl(padapter, (u8)pdrvextra_cmd->type_size);
+ break;
+#ifdef CONFIG_8723AU_AP_MODE
+ case CHECK_HIQ_WK_CID:
+ rtw_chk_hi_queue_hdl(padapter);
+ break;
+#endif /* CONFIG_8723AU_AP_MODE */
+ case C2H_WK_CID:
+ c2h_evt_hdl(padapter,
+ (struct c2h_evt_hdr *)pdrvextra_cmd->pbuf);
+ break;
+
+ default:
+ break;
+ }
+
+ if (pdrvextra_cmd->pbuf && (pdrvextra_cmd->type_size > 0)) {
+ kfree(pdrvextra_cmd->pbuf);
+ /*
+ * No need to set pdrvextra_cmd->pbuf = NULL as we were
+ * operating on a copy of the original pcmd->parmbuf
+ * created in rtw_cmd_work().
+ */
+ }
+
+ return H2C_SUCCESS;
+}
+
+void rtw_survey_cmd_callback23a(struct rtw_adapter *padapter,
+ struct cmd_obj *pcmd)
+{
+ struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
+
+ if (pcmd->res == H2C_DROPPED) {
+ /* TODO: cancel timer and do timeout handler directly... */
+ /* need to make timeout handlerOS independent */
+ mod_timer(&pmlmepriv->scan_to_timer,
+ jiffies + msecs_to_jiffies(1));
+ } else if (pcmd->res != H2C_SUCCESS) {
+ mod_timer(&pmlmepriv->scan_to_timer,
+ jiffies + msecs_to_jiffies(1));
+ RT_TRACE(_module_rtl871x_cmd_c_, _drv_err_,
+ "********Error: MgntActrtw_set_802_11_bssid23a_LIST_SCAN Fail ************\n");
+ }
+
+ /* free cmd */
+ rtw_free_cmd_obj23a(pcmd);
+}
+
+void rtw_disassoc_cmd23a_callback(struct rtw_adapter *padapter,
+ struct cmd_obj *pcmd)
+{
+ struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
+
+ if (pcmd->res != H2C_SUCCESS) {
+ spin_lock_bh(&pmlmepriv->lock);
+ set_fwstate(pmlmepriv, _FW_LINKED);
+ spin_unlock_bh(&pmlmepriv->lock);
+ RT_TRACE(_module_rtl871x_cmd_c_, _drv_err_,
+ "***Error: disconnect_cmd_callback Fail ***\n");
+ return;
+ }
+
+ /* free cmd */
+ rtw_free_cmd_obj23a(pcmd);
+}
+
+void rtw_joinbss_cmd23a_callback(struct rtw_adapter *padapter,
+ struct cmd_obj *pcmd)
+{
+ struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
+
+ if (pcmd->res == H2C_DROPPED) {
+ /* TODO: cancel timer and do timeout handler directly... */
+ /* need to make timeout handlerOS independent */
+ mod_timer(&pmlmepriv->assoc_timer,
+ jiffies + msecs_to_jiffies(1));
+ } else if (pcmd->res != H2C_SUCCESS) {
+ RT_TRACE(_module_rtl871x_cmd_c_, _drv_err_,
+ "********Error:rtw_select_and_join_from_scanned_queue Wait Sema Fail ************\n");
+ mod_timer(&pmlmepriv->assoc_timer,
+ jiffies + msecs_to_jiffies(1));
+ }
+
+ rtw_free_cmd_obj23a(pcmd);
+}
+
+void rtw_createbss_cmd23a_callback(struct rtw_adapter *padapter,
+ struct cmd_obj *pcmd)
+{
+ struct sta_info *psta;
+ struct wlan_network *pwlan;
+ struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
+ struct wlan_bssid_ex *pnetwork = (struct wlan_bssid_ex *)pcmd->parmbuf;
+ struct wlan_network *tgt_network = &pmlmepriv->cur_network;
+
+ if (pcmd->res != H2C_SUCCESS) {
+ RT_TRACE(_module_rtl871x_cmd_c_, _drv_err_,
+ "********Error: rtw_createbss_cmd23a_callback Fail ************\n");
+ mod_timer(&pmlmepriv->assoc_timer,
+ jiffies + msecs_to_jiffies(1));
+ }
+
+ del_timer_sync(&pmlmepriv->assoc_timer);
+
+ if (check_fwstate(pmlmepriv, WIFI_AP_STATE)) {
+ psta = rtw_get_stainfo23a(&padapter->stapriv,
+ pnetwork->MacAddress);
+ if (!psta) {
+ psta = rtw_alloc_stainfo23a(&padapter->stapriv,
+ pnetwork->MacAddress,
+ GFP_KERNEL);
+ if (!psta) {
+ RT_TRACE(_module_rtl871x_cmd_c_, _drv_err_,
+ "Can't alloc sta_info when createbss_cmd_callback\n");
+ goto createbss_cmd_fail;
+ }
+ }
+
+ spin_lock_bh(&pmlmepriv->lock);
+ rtw_indicate_connect23a(padapter);
+ spin_unlock_bh(&pmlmepriv->lock);
+ } else {
+ pwlan = rtw_alloc_network(pmlmepriv, GFP_KERNEL);
+ spin_lock_bh(&pmlmepriv->scanned_queue.lock);
+ if (!pwlan) {
+ pwlan = rtw_get_oldest_wlan_network23a(&pmlmepriv->scanned_queue);
+ if (!pwlan) {
+ RT_TRACE(_module_rtl871x_cmd_c_, _drv_err_,
+ "Error: can't get pwlan in rtw23a_joinbss_event_cb\n");
+ spin_unlock_bh(&pmlmepriv->scanned_queue.lock);
+ goto createbss_cmd_fail;
+ }
+ pwlan->last_scanned = jiffies;
+ } else {
+ list_add_tail(&pwlan->list,
+ &pmlmepriv->scanned_queue.queue);
+ }
+
+ pnetwork->Length = get_wlan_bssid_ex_sz(pnetwork);
+ memcpy(&pwlan->network, pnetwork, pnetwork->Length);
+ /* pwlan->fixed = true; */
+
+ /* list_add_tail(&pwlan->list,
+ &pmlmepriv->scanned_queue.queue); */
+
+ /* copy pdev_network information to
+ pmlmepriv->cur_network */
+ memcpy(&tgt_network->network, pnetwork,
+ get_wlan_bssid_ex_sz(pnetwork));
+
+ /* reset DSConfig */
+
+ clr_fwstate(pmlmepriv, _FW_UNDER_LINKING);
+
+ spin_unlock_bh(&pmlmepriv->scanned_queue.lock);
+ /* we will set _FW_LINKED when there is one more sat to
+ join us (rtw_stassoc_event_callback23a) */
+ }
+
+createbss_cmd_fail:
+
+ rtw_free_cmd_obj23a(pcmd);
+}
+
+void rtw_setstaKey_cmdrsp_callback23a(struct rtw_adapter *padapter,
+ struct cmd_obj *pcmd)
+{
+ struct sta_priv *pstapriv;
+ struct set_stakey_rsp *psetstakey_rsp;
+ struct sta_info *psta;
+
+ pstapriv = &padapter->stapriv;
+ psetstakey_rsp = (struct set_stakey_rsp *) (pcmd->rsp);
+ psta = rtw_get_stainfo23a(pstapriv, psetstakey_rsp->addr);
+
+ if (!psta) {
+ RT_TRACE(_module_rtl871x_cmd_c_, _drv_err_,
+ "ERROR: rtw_setstaKey_cmdrsp_callback23a => can't get sta_info\n");
+ goto exit;
+ }
+
+exit:
+
+ rtw_free_cmd_obj23a(pcmd);
+}
+
+void rtw_setassocsta_cmdrsp_callback23a(struct rtw_adapter *padapter,
+ struct cmd_obj *pcmd)
+{
+ struct sta_priv *pstapriv = &padapter->stapriv;
+ struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
+ struct set_assocsta_parm *passocsta_parm;
+ struct set_assocsta_rsp *passocsta_rsp;
+ struct sta_info *psta;
+
+ passocsta_parm = (struct set_assocsta_parm *)(pcmd->parmbuf);
+ passocsta_rsp = (struct set_assocsta_rsp *) (pcmd->rsp);
+ psta = rtw_get_stainfo23a(pstapriv, passocsta_parm->addr);
+
+ if (psta == NULL) {
+ RT_TRACE(_module_rtl871x_cmd_c_, _drv_err_,
+ "ERROR: setassocsta_cmdrsp_callbac => can't get sta_info\n");
+ goto exit;
+ }
+
+ psta->aid = psta->mac_id = passocsta_rsp->cam_id;
+
+ spin_lock_bh(&pmlmepriv->lock);
+
+ if (check_fwstate(pmlmepriv, WIFI_MP_STATE) &&
+ check_fwstate(pmlmepriv, _FW_UNDER_LINKING))
+ _clr_fwstate_(pmlmepriv, _FW_UNDER_LINKING);
+
+ set_fwstate(pmlmepriv, _FW_LINKED);
+ spin_unlock_bh(&pmlmepriv->lock);
+
+exit:
+ rtw_free_cmd_obj23a(pcmd);
+}
diff --git a/drivers/staging/rtl8723au/core/rtw_efuse.c b/drivers/staging/rtl8723au/core/rtw_efuse.c
new file mode 100644
index 000000000..92a34db3b
--- /dev/null
+++ b/drivers/staging/rtl8723au/core/rtw_efuse.c
@@ -0,0 +1,715 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2011 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ ******************************************************************************/
+#define _RTW_EFUSE_C_
+
+#include <osdep_service.h>
+#include <drv_types.h>
+
+#include <rtw_efuse.h>
+#include <rtl8723a_hal.h>
+#include <usb_ops_linux.h>
+
+/*------------------------Define local variable------------------------------*/
+
+/* */
+#define REG_EFUSE_CTRL 0x0030
+#define EFUSE_CTRL REG_EFUSE_CTRL /* E-Fuse Control. */
+/* */
+
+#define VOLTAGE_V25 0x03
+#define LDOE25_SHIFT 28
+
+/*-----------------------------------------------------------------------------
+ * Function: Efuse_PowerSwitch
+ *
+ * Overview: When we want to enable write operation, we should change to
+ * pwr on state. When we stop write, we should switch to 500k mode
+ * and disable LDO 2.5V.
+ *
+ * Input: NONE
+ *
+ * Output: NONE
+ *
+ * Return: NONE
+ *
+ * Revised History:
+ * When Who Remark
+ * 11/17/2008 MHC Create Version 0.
+ *
+ *---------------------------------------------------------------------------*/
+static void Efuse_PowerSwitch(struct rtw_adapter *padapter,
+ u8 bWrite, u8 PwrState)
+{
+ u8 tempval;
+ u16 tmpV16;
+
+ if (PwrState == true) {
+ rtl8723au_write8(padapter, REG_EFUSE_ACCESS, EFUSE_ACCESS_ON);
+
+ /* 1.2V Power: From VDDON with Power
+ Cut(0x0000h[15]), default valid */
+ tmpV16 = rtl8723au_read16(padapter, REG_SYS_ISO_CTRL);
+ if (!(tmpV16 & PWC_EV12V)) {
+ tmpV16 |= PWC_EV12V;
+ rtl8723au_write16(padapter, REG_SYS_ISO_CTRL, tmpV16);
+ }
+ /* Reset: 0x0000h[28], default valid */
+ tmpV16 = rtl8723au_read16(padapter, REG_SYS_FUNC_EN);
+ if (!(tmpV16 & FEN_ELDR)) {
+ tmpV16 |= FEN_ELDR;
+ rtl8723au_write16(padapter, REG_SYS_FUNC_EN, tmpV16);
+ }
+
+ /* Clock: Gated(0x0008h[5]) 8M(0x0008h[1]) clock
+ from ANA, default valid */
+ tmpV16 = rtl8723au_read16(padapter, REG_SYS_CLKR);
+ if ((!(tmpV16 & LOADER_CLK_EN)) || (!(tmpV16 & ANA8M))) {
+ tmpV16 |= (LOADER_CLK_EN | ANA8M);
+ rtl8723au_write16(padapter, REG_SYS_CLKR, tmpV16);
+ }
+
+ if (bWrite == true) {
+ /* Enable LDO 2.5V before read/write action */
+ tempval = rtl8723au_read8(padapter, EFUSE_TEST + 3);
+ tempval &= 0x0F;
+ tempval |= (VOLTAGE_V25 << 4);
+ rtl8723au_write8(padapter, EFUSE_TEST + 3,
+ tempval | 0x80);
+ }
+ } else {
+ rtl8723au_write8(padapter, REG_EFUSE_ACCESS, EFUSE_ACCESS_OFF);
+
+ if (bWrite == true) {
+ /* Disable LDO 2.5V after read/write action */
+ tempval = rtl8723au_read8(padapter, EFUSE_TEST + 3);
+ rtl8723au_write8(padapter, EFUSE_TEST + 3,
+ tempval & 0x7F);
+ }
+ }
+}
+
+u16
+Efuse_GetCurrentSize23a(struct rtw_adapter *pAdapter, u8 efuseType)
+{
+ u16 ret = 0;
+
+ if (efuseType == EFUSE_WIFI)
+ ret = rtl8723a_EfuseGetCurrentSize_WiFi(pAdapter);
+ else
+ ret = rtl8723a_EfuseGetCurrentSize_BT(pAdapter);
+
+ return ret;
+}
+
+/* 11/16/2008 MH Add description. Get current efuse area enabled word!!. */
+u8
+Efuse_CalculateWordCnts23a(u8 word_en)
+{
+ return hweight8((~word_en) & 0xf);
+}
+
+/* */
+/* Description: */
+/* Execute E-Fuse read byte operation. */
+/* Referred from SD1 Richard. */
+/* */
+/* Assumption: */
+/* 1. Boot from E-Fuse and successfully auto-load. */
+/* 2. PASSIVE_LEVEL (USB interface) */
+/* */
+/* Created by Roger, 2008.10.21. */
+/* */
+void
+ReadEFuseByte23a(struct rtw_adapter *Adapter, u16 _offset, u8 *pbuf)
+{
+ u32 value32;
+ u8 readbyte;
+ u16 retry;
+
+ /* Write Address */
+ rtl8723au_write8(Adapter, EFUSE_CTRL+1, (_offset & 0xff));
+ readbyte = rtl8723au_read8(Adapter, EFUSE_CTRL+2);
+ rtl8723au_write8(Adapter, EFUSE_CTRL+2,
+ ((_offset >> 8) & 0x03) | (readbyte & 0xfc));
+
+ /* Write bit 32 0 */
+ readbyte = rtl8723au_read8(Adapter, EFUSE_CTRL+3);
+ rtl8723au_write8(Adapter, EFUSE_CTRL+3, readbyte & 0x7f);
+
+ /* Check bit 32 read-ready */
+ retry = 0;
+ value32 = rtl8723au_read32(Adapter, EFUSE_CTRL);
+ while (!((value32 >> 24) & 0x80) && retry < 10000) {
+ value32 = rtl8723au_read32(Adapter, EFUSE_CTRL);
+ retry++;
+ }
+
+ /* 20100205 Joseph: Add delay suggested by SD1 Victor. */
+ /* This fix the problem that Efuse read error in high temperature condition. */
+ /* Designer says that there shall be some delay after ready bit is set, or the */
+ /* result will always stay on last data we read. */
+ udelay(50);
+ value32 = rtl8723au_read32(Adapter, EFUSE_CTRL);
+
+ *pbuf = (u8)(value32 & 0xff);
+}
+
+void
+EFUSE_GetEfuseDefinition23a(struct rtw_adapter *pAdapter, u8 efuseType,
+ u8 type, void *pOut)
+{
+ u8 *pu1Tmp;
+ u16 *pu2Tmp;
+ u8 *pMax_section;
+
+ switch (type) {
+ case TYPE_EFUSE_MAX_SECTION:
+ pMax_section = pOut;
+
+ if (efuseType == EFUSE_WIFI)
+ *pMax_section = EFUSE_MAX_SECTION_8723A;
+ else
+ *pMax_section = EFUSE_BT_MAX_SECTION;
+ break;
+
+ case TYPE_EFUSE_REAL_CONTENT_LEN:
+ pu2Tmp = pOut;
+
+ if (efuseType == EFUSE_WIFI)
+ *pu2Tmp = EFUSE_REAL_CONTENT_LEN_8723A;
+ else
+ *pu2Tmp = EFUSE_BT_REAL_CONTENT_LEN;
+ break;
+
+ case TYPE_AVAILABLE_EFUSE_BYTES_BANK:
+ pu2Tmp = pOut;
+
+ if (efuseType == EFUSE_WIFI)
+ *pu2Tmp = (EFUSE_REAL_CONTENT_LEN_8723A -
+ EFUSE_OOB_PROTECT_BYTES);
+ else
+ *pu2Tmp = (EFUSE_BT_REAL_BANK_CONTENT_LEN -
+ EFUSE_PROTECT_BYTES_BANK);
+ break;
+
+ case TYPE_AVAILABLE_EFUSE_BYTES_TOTAL:
+ pu2Tmp = pOut;
+
+ if (efuseType == EFUSE_WIFI)
+ *pu2Tmp = (EFUSE_REAL_CONTENT_LEN_8723A -
+ EFUSE_OOB_PROTECT_BYTES);
+ else
+ *pu2Tmp = (EFUSE_BT_REAL_CONTENT_LEN -
+ (EFUSE_PROTECT_BYTES_BANK * 3));
+ break;
+
+ case TYPE_EFUSE_MAP_LEN:
+ pu2Tmp = pOut;
+
+ if (efuseType == EFUSE_WIFI)
+ *pu2Tmp = EFUSE_MAP_LEN_8723A;
+ else
+ *pu2Tmp = EFUSE_BT_MAP_LEN;
+ break;
+
+ case TYPE_EFUSE_PROTECT_BYTES_BANK:
+ pu1Tmp = pOut;
+
+ if (efuseType == EFUSE_WIFI)
+ *pu1Tmp = EFUSE_OOB_PROTECT_BYTES;
+ else
+ *pu1Tmp = EFUSE_PROTECT_BYTES_BANK;
+ break;
+
+ case TYPE_EFUSE_CONTENT_LEN_BANK:
+ pu2Tmp = pOut;
+
+ if (efuseType == EFUSE_WIFI)
+ *pu2Tmp = EFUSE_REAL_CONTENT_LEN_8723A;
+ else
+ *pu2Tmp = EFUSE_BT_REAL_BANK_CONTENT_LEN;
+ break;
+
+ default:
+ pu1Tmp = pOut;
+ *pu1Tmp = 0;
+ break;
+ }
+}
+
+/*-----------------------------------------------------------------------------
+ * Function: EFUSE_Read1Byte23a
+ *
+ * Overview: Copy from WMAC fot EFUSE read 1 byte.
+ *
+ * Input: NONE
+ *
+ * Output: NONE
+ *
+ * Return: NONE
+ *
+ * Revised History:
+ * When Who Remark
+ * 09/23/2008 MHC Copy from WMAC.
+ *
+ *---------------------------------------------------------------------------*/
+u8
+EFUSE_Read1Byte23a(struct rtw_adapter *Adapter, u16 Address)
+{
+ u8 data;
+ u8 Bytetemp = {0x00};
+ u8 temp = {0x00};
+ u32 k = 0;
+ u16 contentLen = 0;
+
+ EFUSE_GetEfuseDefinition23a(Adapter, EFUSE_WIFI,
+ TYPE_EFUSE_REAL_CONTENT_LEN,
+ (void *)&contentLen);
+
+ if (Address < contentLen) { /* E-fuse 512Byte */
+ /* Write E-fuse Register address bit0~7 */
+ temp = Address & 0xFF;
+ rtl8723au_write8(Adapter, EFUSE_CTRL+1, temp);
+ Bytetemp = rtl8723au_read8(Adapter, EFUSE_CTRL+2);
+ /* Write E-fuse Register address bit8~9 */
+ temp = ((Address >> 8) & 0x03) | (Bytetemp & 0xFC);
+ rtl8723au_write8(Adapter, EFUSE_CTRL+2, temp);
+
+ /* Write 0x30[31]= 0 */
+ Bytetemp = rtl8723au_read8(Adapter, EFUSE_CTRL+3);
+ temp = Bytetemp & 0x7F;
+ rtl8723au_write8(Adapter, EFUSE_CTRL+3, temp);
+
+ /* Wait Write-ready (0x30[31]= 1) */
+ Bytetemp = rtl8723au_read8(Adapter, EFUSE_CTRL+3);
+ while (!(Bytetemp & 0x80)) {
+ Bytetemp = rtl8723au_read8(Adapter, EFUSE_CTRL+3);
+ k++;
+ if (k == 1000) {
+ k = 0;
+ break;
+ }
+ }
+ data = rtl8723au_read8(Adapter, EFUSE_CTRL);
+ return data;
+ } else
+ return 0xFF;
+}/* EFUSE_Read1Byte23a */
+
+/*-----------------------------------------------------------------------------
+ * Function: EFUSE_Write1Byte
+ *
+ * Overview: Copy from WMAC fot EFUSE write 1 byte.
+ *
+ * Input: NONE
+ *
+ * Output: NONE
+ *
+ * Return: NONE
+ *
+ * Revised History:
+ * When Who Remark
+ * 09/23/2008 MHC Copy from WMAC.
+ *
+ *---------------------------------------------------------------------------*/
+
+void
+EFUSE_Write1Byte(struct rtw_adapter *Adapter, u16 Address, u8 Value);
+void
+EFUSE_Write1Byte(struct rtw_adapter *Adapter, u16 Address, u8 Value)
+{
+ u8 Bytetemp = {0x00};
+ u8 temp = {0x00};
+ u32 k = 0;
+ u16 contentLen = 0;
+
+ EFUSE_GetEfuseDefinition23a(Adapter, EFUSE_WIFI,
+ TYPE_EFUSE_REAL_CONTENT_LEN,
+ (void *)&contentLen);
+
+ if (Address < contentLen) { /* E-fuse 512Byte */
+ rtl8723au_write8(Adapter, EFUSE_CTRL, Value);
+
+ /* Write E-fuse Register address bit0~7 */
+ temp = Address & 0xFF;
+ rtl8723au_write8(Adapter, EFUSE_CTRL+1, temp);
+ Bytetemp = rtl8723au_read8(Adapter, EFUSE_CTRL+2);
+
+ /* Write E-fuse Register address bit8~9 */
+ temp = ((Address >> 8) & 0x03) | (Bytetemp & 0xFC);
+ rtl8723au_write8(Adapter, EFUSE_CTRL+2, temp);
+
+ /* Write 0x30[31]= 1 */
+ Bytetemp = rtl8723au_read8(Adapter, EFUSE_CTRL+3);
+ temp = Bytetemp | 0x80;
+ rtl8723au_write8(Adapter, EFUSE_CTRL+3, temp);
+
+ /* Wait Write-ready (0x30[31]= 0) */
+ Bytetemp = rtl8723au_read8(Adapter, EFUSE_CTRL+3);
+ while (Bytetemp & 0x80) {
+ Bytetemp = rtl8723au_read8(Adapter, EFUSE_CTRL+3);
+ k++;
+ if (k == 100) {
+ k = 0;
+ break;
+ }
+ }
+ }
+}/* EFUSE_Write1Byte */
+
+/* 11/16/2008 MH Read one byte from real Efuse. */
+int
+efuse_OneByteRead23a(struct rtw_adapter *pAdapter, u16 addr, u8 *data)
+{
+ u8 tmpidx = 0;
+ int bResult;
+
+ /* -----------------e-fuse reg ctrl --------------------------------- */
+ /* address */
+ rtl8723au_write8(pAdapter, EFUSE_CTRL+1, (u8)(addr&0xff));
+ rtl8723au_write8(pAdapter, EFUSE_CTRL+2, ((u8)((addr>>8) &0x03)) |
+ (rtl8723au_read8(pAdapter, EFUSE_CTRL+2)&0xFC));
+
+ rtl8723au_write8(pAdapter, EFUSE_CTRL+3, 0x72);/* read cmd */
+
+ while(!(0x80 &rtl8723au_read8(pAdapter, EFUSE_CTRL+3)) && (tmpidx<100))
+ tmpidx++;
+ if (tmpidx < 100) {
+ *data = rtl8723au_read8(pAdapter, EFUSE_CTRL);
+ bResult = _SUCCESS;
+ } else {
+ *data = 0xff;
+ bResult = _FAIL;
+ }
+ return bResult;
+}
+
+/* 11/16/2008 MH Write one byte to reald Efuse. */
+int
+efuse_OneByteWrite23a(struct rtw_adapter *pAdapter, u16 addr, u8 data)
+{
+ u8 tmpidx = 0;
+ int bResult;
+
+ /* return 0; */
+
+ /* -----------------e-fuse reg ctrl --------------------------------- */
+ /* address */
+ rtl8723au_write8(pAdapter, EFUSE_CTRL+1, (u8)(addr&0xff));
+ rtl8723au_write8(pAdapter, EFUSE_CTRL+2,
+ (rtl8723au_read8(pAdapter, EFUSE_CTRL+2)&0xFC)|(u8)((addr>>8)&0x03));
+ rtl8723au_write8(pAdapter, EFUSE_CTRL, data);/* data */
+
+ rtl8723au_write8(pAdapter, EFUSE_CTRL+3, 0xF2);/* write cmd */
+
+ while((0x80 & rtl8723au_read8(pAdapter, EFUSE_CTRL+3)) &&
+ (tmpidx<100)) {
+ tmpidx++;
+ }
+
+ if (tmpidx < 100)
+ bResult = _SUCCESS;
+ else
+ bResult = _FAIL;
+
+ return bResult;
+}
+
+/*-----------------------------------------------------------------------------
+ * Function: efuse_WordEnableDataRead23a
+ *
+ * Overview: Read allowed word in current efuse section data.
+ *
+ * Input: NONE
+ *
+ * Output: NONE
+ *
+ * Return: NONE
+ *
+ * Revised History:
+ * When Who Remark
+ * 11/16/2008 MHC Create Version 0.
+ * 11/21/2008 MHC Fix Write bug when we only enable late word.
+ *
+ *---------------------------------------------------------------------------*/
+void
+efuse_WordEnableDataRead23a(u8 word_en,
+ u8 *sourdata,
+ u8 *targetdata)
+{
+ if (!(word_en&BIT(0))) {
+ targetdata[0] = sourdata[0];
+ targetdata[1] = sourdata[1];
+ }
+ if (!(word_en&BIT(1))) {
+ targetdata[2] = sourdata[2];
+ targetdata[3] = sourdata[3];
+ }
+ if (!(word_en&BIT(2))) {
+ targetdata[4] = sourdata[4];
+ targetdata[5] = sourdata[5];
+ }
+ if (!(word_en&BIT(3))) {
+ targetdata[6] = sourdata[6];
+ targetdata[7] = sourdata[7];
+ }
+}
+
+static int efuse_read8(struct rtw_adapter *padapter, u16 address, u8 *value)
+{
+ return efuse_OneByteRead23a(padapter, address, value);
+}
+
+static int efuse_write8(struct rtw_adapter *padapter, u16 address, u8 *value)
+{
+ return efuse_OneByteWrite23a(padapter, address, *value);
+}
+
+/*
+ * read/write raw efuse data
+ */
+int rtw_efuse_access23a(struct rtw_adapter *padapter, u8 bWrite, u16 start_addr,
+ u16 cnts, u8 *data)
+{
+ int i = 0;
+ u16 real_content_len = 0, max_available_size = 0;
+ int res = _FAIL ;
+ int (*rw8)(struct rtw_adapter *, u16, u8*);
+
+ EFUSE_GetEfuseDefinition23a(padapter, EFUSE_WIFI,
+ TYPE_EFUSE_REAL_CONTENT_LEN,
+ (void *)&real_content_len);
+ EFUSE_GetEfuseDefinition23a(padapter, EFUSE_WIFI,
+ TYPE_AVAILABLE_EFUSE_BYTES_TOTAL,
+ (void *)&max_available_size);
+
+ if (start_addr > real_content_len)
+ return _FAIL;
+
+ if (true == bWrite) {
+ if ((start_addr + cnts) > max_available_size)
+ return _FAIL;
+ rw8 = &efuse_write8;
+ } else
+ rw8 = &efuse_read8;
+
+ Efuse_PowerSwitch(padapter, bWrite, true);
+
+ /* e-fuse one byte read / write */
+ for (i = 0; i < cnts; i++) {
+ if (start_addr >= real_content_len) {
+ res = _FAIL;
+ break;
+ }
+
+ res = rw8(padapter, start_addr++, data++);
+ if (res == _FAIL)
+ break;
+ }
+
+ Efuse_PowerSwitch(padapter, bWrite, false);
+
+ return res;
+}
+/* */
+u16 efuse_GetMaxSize23a(struct rtw_adapter *padapter)
+{
+ u16 max_size;
+ EFUSE_GetEfuseDefinition23a(padapter, EFUSE_WIFI,
+ TYPE_AVAILABLE_EFUSE_BYTES_TOTAL,
+ (void *)&max_size);
+ return max_size;
+}
+/* */
+int rtw_efuse_map_read23a(struct rtw_adapter *padapter,
+ u16 addr, u16 cnts, u8 *data)
+{
+ u16 mapLen = 0;
+
+ EFUSE_GetEfuseDefinition23a(padapter, EFUSE_WIFI,
+ TYPE_EFUSE_MAP_LEN, (void *)&mapLen);
+
+ if ((addr + cnts) > mapLen)
+ return _FAIL;
+
+ Efuse_PowerSwitch(padapter, false, true);
+
+ rtl8723a_readefuse(padapter, EFUSE_WIFI, addr, cnts, data);
+
+ Efuse_PowerSwitch(padapter, false, false);
+
+ return _SUCCESS;
+}
+
+int rtw_BT_efuse_map_read23a(struct rtw_adapter *padapter,
+ u16 addr, u16 cnts, u8 *data)
+{
+ u16 mapLen = 0;
+
+ EFUSE_GetEfuseDefinition23a(padapter, EFUSE_BT,
+ TYPE_EFUSE_MAP_LEN, (void *)&mapLen);
+
+ if ((addr + cnts) > mapLen)
+ return _FAIL;
+
+ Efuse_PowerSwitch(padapter, false, true);
+
+ rtl8723a_readefuse(padapter, EFUSE_BT, addr, cnts, data);
+
+ Efuse_PowerSwitch(padapter, false, false);
+
+ return _SUCCESS;
+}
+
+/*-----------------------------------------------------------------------------
+ * Function: Efuse_ReadAllMap
+ *
+ * Overview: Read All Efuse content
+ *
+ * Input: NONE
+ *
+ * Output: NONE
+ *
+ * Return: NONE
+ *
+ * Revised History:
+ * When Who Remark
+ * 11/11/2008 MHC Create Version 0.
+ *
+ *---------------------------------------------------------------------------*/
+void
+Efuse_ReadAllMap(struct rtw_adapter *pAdapter, u8 efuseType, u8 *Efuse);
+void
+Efuse_ReadAllMap(struct rtw_adapter *pAdapter, u8 efuseType, u8 *Efuse)
+{
+ u16 mapLen = 0;
+
+ Efuse_PowerSwitch(pAdapter, false, true);
+
+ EFUSE_GetEfuseDefinition23a(pAdapter, efuseType, TYPE_EFUSE_MAP_LEN,
+ (void *)&mapLen);
+
+ rtl8723a_readefuse(pAdapter, efuseType, 0, mapLen, Efuse);
+
+ Efuse_PowerSwitch(pAdapter, false, false);
+}
+
+/*-----------------------------------------------------------------------------
+ * Function: efuse_ShadowRead1Byte
+ * efuse_ShadowRead2Byte
+ * efuse_ShadowRead4Byte
+ *
+ * Overview: Read from efuse init map by one/two/four bytes !!!!!
+ *
+ * Input: NONE
+ *
+ * Output: NONE
+ *
+ * Return: NONE
+ *
+ * Revised History:
+ * When Who Remark
+ * 11/12/2008 MHC Create Version 0.
+ *
+ *---------------------------------------------------------------------------*/
+static void
+efuse_ShadowRead1Byte(struct rtw_adapter *pAdapter, u16 Offset, u8 *Value)
+{
+ struct eeprom_priv *pEEPROM = GET_EEPROM_EFUSE_PRIV(pAdapter);
+
+ *Value = pEEPROM->efuse_eeprom_data[Offset];
+} /* EFUSE_ShadowRead23a1Byte */
+
+/* Read Two Bytes */
+static void
+efuse_ShadowRead2Byte(struct rtw_adapter *pAdapter, u16 Offset, u16 *Value)
+{
+ struct eeprom_priv *pEEPROM = GET_EEPROM_EFUSE_PRIV(pAdapter);
+
+ *Value = pEEPROM->efuse_eeprom_data[Offset];
+ *Value |= pEEPROM->efuse_eeprom_data[Offset+1]<<8;
+} /* EFUSE_ShadowRead23a2Byte */
+
+/* Read Four Bytes */
+static void
+efuse_ShadowRead4Byte(struct rtw_adapter *pAdapter, u16 Offset, u32 *Value)
+{
+ struct eeprom_priv *pEEPROM = GET_EEPROM_EFUSE_PRIV(pAdapter);
+
+ *Value = pEEPROM->efuse_eeprom_data[Offset];
+ *Value |= pEEPROM->efuse_eeprom_data[Offset+1]<<8;
+ *Value |= pEEPROM->efuse_eeprom_data[Offset+2]<<16;
+ *Value |= pEEPROM->efuse_eeprom_data[Offset+3]<<24;
+} /* efuse_ShadowRead4Byte */
+
+/*-----------------------------------------------------------------------------
+ * Function: EFUSE_ShadowMapUpdate23a
+ *
+ * Overview: Transfer current EFUSE content to shadow init and modify map.
+ *
+ * Input: NONE
+ *
+ * Output: NONE
+ *
+ * Return: NONE
+ *
+ * Revised History:
+ * When Who Remark
+ * 11/13/2008 MHC Create Version 0.
+ *
+ *---------------------------------------------------------------------------*/
+void EFUSE_ShadowMapUpdate23a(struct rtw_adapter *pAdapter, u8 efuseType)
+{
+ struct eeprom_priv *pEEPROM = GET_EEPROM_EFUSE_PRIV(pAdapter);
+ u16 mapLen = 0;
+
+ EFUSE_GetEfuseDefinition23a(pAdapter, efuseType,
+ TYPE_EFUSE_MAP_LEN, (void *)&mapLen);
+
+ if (pEEPROM->bautoload_fail_flag == true)
+ memset(pEEPROM->efuse_eeprom_data, 0xFF, mapLen);
+ else
+ Efuse_ReadAllMap(pAdapter, efuseType,
+ pEEPROM->efuse_eeprom_data);
+
+}/* EFUSE_ShadowMapUpdate23a */
+
+/*-----------------------------------------------------------------------------
+ * Function: EFUSE_ShadowRead23a
+ *
+ * Overview: Read from efuse init map !!!!!
+ *
+ * Input: NONE
+ *
+ * Output: NONE
+ *
+ * Return: NONE
+ *
+ * Revised History:
+ * When Who Remark
+ * 11/12/2008 MHC Create Version 0.
+ *
+ *---------------------------------------------------------------------------*/
+void
+EFUSE_ShadowRead23a(struct rtw_adapter *pAdapter,
+ u8 Type, u16 Offset, u32 *Value)
+{
+ if (Type == 1)
+ efuse_ShadowRead1Byte(pAdapter, Offset, (u8 *)Value);
+ else if (Type == 2)
+ efuse_ShadowRead2Byte(pAdapter, Offset, (u16 *)Value);
+ else if (Type == 4)
+ efuse_ShadowRead4Byte(pAdapter, Offset, (u32 *)Value);
+} /* EFUSE_ShadowRead23a */
diff --git a/drivers/staging/rtl8723au/core/rtw_ieee80211.c b/drivers/staging/rtl8723au/core/rtw_ieee80211.c
new file mode 100644
index 000000000..cdd7bc402
--- /dev/null
+++ b/drivers/staging/rtl8723au/core/rtw_ieee80211.c
@@ -0,0 +1,854 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2011 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ ******************************************************************************/
+#define _IEEE80211_C
+
+#include <drv_types.h>
+#include <linux/ieee80211.h>
+#include <ieee80211.h>
+#include <wifi.h>
+#include <osdep_service.h>
+#include <wlan_bssdef.h>
+
+u8 RTW_WPA_OUI23A_TYPE[] = { 0x00, 0x50, 0xf2, 1 };
+u16 RTW_WPA_VERSION23A = 1;
+u8 WPA_AUTH_KEY_MGMT_NONE23A[] = { 0x00, 0x50, 0xf2, 0 };
+u8 WPA_AUTH_KEY_MGMT_UNSPEC_802_1X23A[] = { 0x00, 0x50, 0xf2, 1 };
+u8 WPA_AUTH_KEY_MGMT_PSK_OVER_802_1X23A[] = { 0x00, 0x50, 0xf2, 2 };
+u8 WPA_CIPHER_SUITE_NONE23A[] = { 0x00, 0x50, 0xf2, 0 };
+u8 WPA_CIPHER_SUITE_WEP4023A[] = { 0x00, 0x50, 0xf2, 1 };
+u8 WPA_CIPHER_SUITE_TKIP23A[] = { 0x00, 0x50, 0xf2, 2 };
+u8 WPA_CIPHER_SUITE_WRAP23A[] = { 0x00, 0x50, 0xf2, 3 };
+u8 WPA_CIPHER_SUITE_CCMP23A[] = { 0x00, 0x50, 0xf2, 4 };
+u8 WPA_CIPHER_SUITE_WEP10423A[] = { 0x00, 0x50, 0xf2, 5 };
+
+u8 RSN_AUTH_KEY_MGMT_UNSPEC_802_1X23A[] = { 0x00, 0x0f, 0xac, 1 };
+u8 RSN_AUTH_KEY_MGMT_PSK_OVER_802_1X23A[] = { 0x00, 0x0f, 0xac, 2 };
+u8 RSN_CIPHER_SUITE_NONE23A[] = { 0x00, 0x0f, 0xac, 0 };
+u8 RSN_CIPHER_SUITE_WEP4023A[] = { 0x00, 0x0f, 0xac, 1 };
+u8 RSN_CIPHER_SUITE_TKIP23A[] = { 0x00, 0x0f, 0xac, 2 };
+u8 RSN_CIPHER_SUITE_WRAP23A[] = { 0x00, 0x0f, 0xac, 3 };
+u8 RSN_CIPHER_SUITE_CCMP23A[] = { 0x00, 0x0f, 0xac, 4 };
+u8 RSN_CIPHER_SUITE_WEP10423A[] = { 0x00, 0x0f, 0xac, 5 };
+/* */
+/* for adhoc-master to generate ie and provide supported-rate to fw */
+/* */
+
+static u8 WIFI_CCKRATES[] = {
+ IEEE80211_CCK_RATE_1MB | IEEE80211_BASIC_RATE_MASK,
+ IEEE80211_CCK_RATE_2MB | IEEE80211_BASIC_RATE_MASK,
+ IEEE80211_CCK_RATE_5MB | IEEE80211_BASIC_RATE_MASK,
+ IEEE80211_CCK_RATE_11MB | IEEE80211_BASIC_RATE_MASK
+};
+
+static u8 WIFI_OFDMRATES[] = {
+ IEEE80211_OFDM_RATE_6MB,
+ IEEE80211_OFDM_RATE_9MB,
+ IEEE80211_OFDM_RATE_12MB,
+ IEEE80211_OFDM_RATE_18MB,
+ IEEE80211_OFDM_RATE_24MB,
+ IEEE80211_OFDM_RATE_36MB,
+ IEEE80211_OFDM_RATE_48MB,
+ IEEE80211_OFDM_RATE_54MB
+};
+
+int rtw_get_bit_value_from_ieee_value23a(u8 val)
+{
+ unsigned char dot11_rate_table[]=
+ {2, 4, 11, 22, 12, 18, 24, 36, 48, 72, 96, 108, 0};
+
+ int i = 0;
+
+ while (dot11_rate_table[i] != 0) {
+ if (dot11_rate_table[i] == val)
+ return BIT(i);
+ i++;
+ }
+ return 0;
+}
+
+static bool rtw_is_cckrates_included(u8 *rate)
+{
+ u32 i = 0;
+
+ while (rate[i]) {
+ if ((rate[i] & 0x7f) == 2 || (rate[i] & 0x7f) == 4 ||
+ (rate[i] & 0x7f) == 11 || (rate[i] & 0x7f) == 22)
+ return true;
+ i++;
+ }
+
+ return false;
+}
+
+static bool rtw_is_cckratesonly_included(u8 *rate)
+{
+ u32 i = 0;
+
+ while (rate[i]) {
+ if ((rate[i] & 0x7f) != 2 && (rate[i] & 0x7f) != 4 &&
+ (rate[i] & 0x7f) != 11 && (rate[i] & 0x7f) != 22)
+ return false;
+
+ i++;
+ }
+
+ return true;
+}
+
+int rtw_check_network_type23a(unsigned char *rate, int ratelen, int channel)
+{
+ if (channel > 14) {
+ if (rtw_is_cckrates_included(rate))
+ return WIRELESS_INVALID;
+ else
+ return WIRELESS_11A;
+ } else { /* could be pure B, pure G, or B/G */
+ if (rtw_is_cckratesonly_included(rate))
+ return WIRELESS_11B;
+ else if (rtw_is_cckrates_included(rate))
+ return WIRELESS_11BG;
+ else
+ return WIRELESS_11G;
+ }
+}
+
+/* rtw_set_ie23a will update frame length */
+u8 *rtw_set_ie23a(u8 *pbuf, int index, uint len, const u8 *source, uint *frlen)
+{
+
+ *pbuf = (u8)index;
+
+ *(pbuf + 1) = (u8)len;
+
+ if (len > 0)
+ memcpy((void *)(pbuf + 2), (void *)source, len);
+
+ *frlen = *frlen + (len + 2);
+
+ return pbuf + len + 2;
+}
+
+inline u8 *rtw_set_ie23a_ch_switch (u8 *buf, u32 *buf_len, u8 ch_switch_mode,
+ u8 new_ch, u8 ch_switch_cnt)
+{
+ u8 ie_data[3];
+
+ ie_data[0] = ch_switch_mode;
+ ie_data[1] = new_ch;
+ ie_data[2] = ch_switch_cnt;
+ return rtw_set_ie23a(buf, WLAN_EID_CHANNEL_SWITCH, 3, ie_data, buf_len);
+}
+
+inline u8 hal_ch_offset_to_secondary_ch_offset23a(u8 ch_offset)
+{
+ if (ch_offset == HAL_PRIME_CHNL_OFFSET_LOWER)
+ return IEEE80211_HT_PARAM_CHA_SEC_BELOW;
+ else if (ch_offset == HAL_PRIME_CHNL_OFFSET_UPPER)
+ return IEEE80211_HT_PARAM_CHA_SEC_ABOVE;
+
+ return IEEE80211_HT_PARAM_CHA_SEC_NONE;
+}
+
+inline u8 *rtw_set_ie23a_secondary_ch_offset(u8 *buf, u32 *buf_len,
+ u8 secondary_ch_offset)
+{
+ return rtw_set_ie23a(buf, WLAN_EID_SECONDARY_CHANNEL_OFFSET,
+ 1, &secondary_ch_offset, buf_len);
+}
+
+/*----------------------------------------------------------------------------
+index: the information element id index, limit is the limit for search
+-----------------------------------------------------------------------------*/
+u8 *rtw_get_ie23a(u8 *pbuf, int index, int *len, int limit)
+{
+ int tmp, i;
+ u8 *p;
+
+ if (limit < 1) {
+
+ return NULL;
+ }
+
+ p = pbuf;
+ i = 0;
+ *len = 0;
+ while (1) {
+ if (*p == index) {
+ *len = *(p + 1);
+ return p;
+ } else {
+ tmp = *(p + 1);
+ p += (tmp + 2);
+ i += (tmp + 2);
+ }
+ if (i >= limit)
+ break;
+ }
+
+ return NULL;
+}
+
+/**
+ * rtw_get_ie23a_ex - Search specific IE from a series of IEs
+ * @in_ie: Address of IEs to search
+ * @in_len: Length limit from in_ie
+ * @eid: Element ID to match
+ * @oui: OUI to match
+ * @oui_len: OUI length
+ * @ie: If not NULL and the specific IE is found, the IE will be copied
+ * to the buf starting from the specific IE
+ * @ielen: If not NULL and the specific IE is found, will set to the length
+ * of the entire IE
+ *
+ * Returns: The address of the specific IE found, or NULL
+ */
+u8 *rtw_get_ie23a_ex(u8 *in_ie, uint in_len, u8 eid, u8 *oui, u8 oui_len,
+ u8 *ie, uint *ielen)
+{
+ uint cnt;
+ u8 *target_ie = NULL;
+
+ if (ielen)
+ *ielen = 0;
+
+ if (!in_ie || in_len <= 0)
+ return target_ie;
+
+ cnt = 0;
+
+ while (cnt < in_len) {
+ if (eid == in_ie[cnt] &&
+ (!oui || !memcmp(&in_ie[cnt+2], oui, oui_len))) {
+ target_ie = &in_ie[cnt];
+
+ if (ie)
+ memcpy(ie, &in_ie[cnt], in_ie[cnt+1]+2);
+
+ if (ielen)
+ *ielen = in_ie[cnt+1]+2;
+ break;
+ } else {
+ cnt += in_ie[cnt + 1] + 2; /* goto next */
+ }
+ }
+
+ return target_ie;
+}
+
+/**
+ * rtw_ies_remove_ie23a - Find matching IEs and remove
+ * @ies: Address of IEs to search
+ * @ies_len: Pointer of length of ies, will update to new length
+ * @offset: The offset to start search
+ * @eid: Element ID to match
+ * @oui: OUI to match
+ * @oui_len: OUI length
+ *
+ * Returns: _SUCCESS: ies is updated, _FAIL: not updated
+ */
+int rtw_ies_remove_ie23a(u8 *ies, uint *ies_len, uint offset, u8 eid,
+ u8 *oui, u8 oui_len)
+{
+ int ret = _FAIL;
+ u8 *target_ie;
+ u32 target_ielen;
+ u8 *start;
+ uint search_len;
+
+ if (!ies || !ies_len || *ies_len <= offset)
+ goto exit;
+
+ start = ies + offset;
+ search_len = *ies_len - offset;
+
+ while (1) {
+ target_ie = rtw_get_ie23a_ex(start, search_len, eid, oui, oui_len,
+ NULL, &target_ielen);
+ if (target_ie && target_ielen) {
+ u8 buf[MAX_IE_SZ] = {0};
+ u8 *remain_ies = target_ie + target_ielen;
+ uint remain_len = search_len - (remain_ies - start);
+
+ memcpy(buf, remain_ies, remain_len);
+ memcpy(target_ie, buf, remain_len);
+ *ies_len = *ies_len - target_ielen;
+ ret = _SUCCESS;
+
+ start = target_ie;
+ search_len = remain_len;
+ } else {
+ break;
+ }
+ }
+exit:
+ return ret;
+}
+
+void rtw_set_supported_rate23a(u8 *SupportedRates, uint mode)
+{
+
+
+ memset(SupportedRates, 0, NDIS_802_11_LENGTH_RATES_EX);
+
+ switch (mode) {
+ case WIRELESS_11B:
+ memcpy(SupportedRates, WIFI_CCKRATES, IEEE80211_CCK_RATE_LEN);
+ break;
+
+ case WIRELESS_11G:
+ case WIRELESS_11A:
+ case WIRELESS_11_5N:
+ case WIRELESS_11A_5N:/* Todo: no basic rate for ofdm ? */
+ memcpy(SupportedRates, WIFI_OFDMRATES,
+ IEEE80211_NUM_OFDM_RATESLEN);
+ break;
+
+ case WIRELESS_11BG:
+ case WIRELESS_11G_24N:
+ case WIRELESS_11_24N:
+ case WIRELESS_11BG_24N:
+ memcpy(SupportedRates, WIFI_CCKRATES, IEEE80211_CCK_RATE_LEN);
+ memcpy(SupportedRates + IEEE80211_CCK_RATE_LEN, WIFI_OFDMRATES,
+ IEEE80211_NUM_OFDM_RATESLEN);
+ break;
+ }
+
+}
+
+uint rtw_get_rateset_len23a(u8 *rateset)
+{
+ uint i = 0;
+
+ while(1) {
+ if (rateset[i] == 0)
+ break;
+
+ if (i > 12)
+ break;
+
+ i++;
+ }
+
+ return i;
+}
+
+int rtw_generate_ie23a(struct registry_priv *pregistrypriv)
+{
+ u8 wireless_mode;
+ int sz = 0, rateLen;
+ struct wlan_bssid_ex* pdev_network = &pregistrypriv->dev_network;
+ u8* ie = pdev_network->IEs;
+ u16 cap;
+
+ pdev_network->tsf = 0;
+
+ cap = WLAN_CAPABILITY_IBSS;
+
+ if (pregistrypriv->preamble == PREAMBLE_SHORT)
+ cap |= WLAN_CAPABILITY_SHORT_PREAMBLE;
+
+ if (pdev_network->Privacy)
+ cap |= WLAN_CAPABILITY_PRIVACY;
+
+ pdev_network->capability = cap;
+
+ /* SSID */
+ ie = rtw_set_ie23a(ie, WLAN_EID_SSID, pdev_network->Ssid.ssid_len,
+ pdev_network->Ssid.ssid, &sz);
+
+ /* supported rates */
+ if (pregistrypriv->wireless_mode == WIRELESS_11ABGN) {
+ if (pdev_network->DSConfig > 14)
+ wireless_mode = WIRELESS_11A_5N;
+ else
+ wireless_mode = WIRELESS_11BG_24N;
+ } else {
+ wireless_mode = pregistrypriv->wireless_mode;
+ }
+
+ rtw_set_supported_rate23a(pdev_network->SupportedRates, wireless_mode) ;
+
+ rateLen = rtw_get_rateset_len23a(pdev_network->SupportedRates);
+
+ if (rateLen > 8) {
+ ie = rtw_set_ie23a(ie, WLAN_EID_SUPP_RATES, 8,
+ pdev_network->SupportedRates, &sz);
+ /* ie = rtw_set_ie23a(ie, _EXT_SUPPORTEDRATES_IE_, (rateLen - 8), (pdev_network->SupportedRates + 8), &sz); */
+ } else {
+ ie = rtw_set_ie23a(ie, WLAN_EID_SUPP_RATES, rateLen,
+ pdev_network->SupportedRates, &sz);
+ }
+
+ /* DS parameter set */
+ ie = rtw_set_ie23a(ie, WLAN_EID_DS_PARAMS, 1,
+ (u8 *)&pdev_network->DSConfig, &sz);
+
+ /* IBSS Parameter Set */
+
+ ie = rtw_set_ie23a(ie, WLAN_EID_IBSS_PARAMS, 2,
+ (u8 *)&pdev_network->ATIMWindow, &sz);
+
+ if (rateLen > 8) {
+ ie = rtw_set_ie23a(ie, WLAN_EID_EXT_SUPP_RATES, (rateLen - 8),
+ (pdev_network->SupportedRates + 8), &sz);
+ }
+
+
+
+ /* return _SUCCESS; */
+
+ return sz;
+}
+
+static int rtw_get_wpa_cipher_suite(const u8 *s)
+{
+ if (!memcmp(s, WPA_CIPHER_SUITE_NONE23A, WPA_SELECTOR_LEN))
+ return WPA_CIPHER_NONE;
+ if (!memcmp(s, WPA_CIPHER_SUITE_WEP4023A, WPA_SELECTOR_LEN))
+ return WPA_CIPHER_WEP40;
+ if (!memcmp(s, WPA_CIPHER_SUITE_TKIP23A, WPA_SELECTOR_LEN))
+ return WPA_CIPHER_TKIP;
+ if (!memcmp(s, WPA_CIPHER_SUITE_CCMP23A, WPA_SELECTOR_LEN))
+ return WPA_CIPHER_CCMP;
+ if (!memcmp(s, WPA_CIPHER_SUITE_WEP10423A, WPA_SELECTOR_LEN))
+ return WPA_CIPHER_WEP104;
+
+ return 0;
+}
+
+static int rtw_get_wpa2_cipher_suite(const u8 *s)
+{
+ if (!memcmp(s, RSN_CIPHER_SUITE_NONE23A, RSN_SELECTOR_LEN))
+ return WPA_CIPHER_NONE;
+ if (!memcmp(s, RSN_CIPHER_SUITE_WEP4023A, RSN_SELECTOR_LEN))
+ return WPA_CIPHER_WEP40;
+ if (!memcmp(s, RSN_CIPHER_SUITE_TKIP23A, RSN_SELECTOR_LEN))
+ return WPA_CIPHER_TKIP;
+ if (!memcmp(s, RSN_CIPHER_SUITE_CCMP23A, RSN_SELECTOR_LEN))
+ return WPA_CIPHER_CCMP;
+ if (!memcmp(s, RSN_CIPHER_SUITE_WEP10423A, RSN_SELECTOR_LEN))
+ return WPA_CIPHER_WEP104;
+
+ return 0;
+}
+
+int rtw_parse_wpa_ie23a(const u8* wpa_ie, int wpa_ie_len, int *group_cipher, int *pairwise_cipher, int *is_8021x)
+{
+ int i, ret = _SUCCESS;
+ int left, count;
+ const u8 *pos;
+
+ if (wpa_ie_len <= 0) {
+ /* No WPA IE - fail silently */
+ return _FAIL;
+ }
+
+ if (wpa_ie[1] != (u8)(wpa_ie_len - 2))
+ return _FAIL;
+
+ pos = wpa_ie;
+
+ pos += 8;
+ left = wpa_ie_len - 8;
+
+ /* group_cipher */
+ if (left >= WPA_SELECTOR_LEN) {
+
+ *group_cipher = rtw_get_wpa_cipher_suite(pos);
+
+ pos += WPA_SELECTOR_LEN;
+ left -= WPA_SELECTOR_LEN;
+ } else if (left > 0) {
+ RT_TRACE(_module_rtl871x_mlme_c_, _drv_err_,
+ "%s: ie length mismatch, %u too much\n",
+ __func__, left);
+
+ return _FAIL;
+ }
+
+ /* pairwise_cipher */
+ if (left >= 2) {
+ /* count = le16_to_cpu(*(u16*)pos); */
+ count = get_unaligned_le16(pos);
+ pos += 2;
+ left -= 2;
+
+ if (count == 0 || left < count * WPA_SELECTOR_LEN) {
+ RT_TRACE(_module_rtl871x_mlme_c_, _drv_err_,
+ "%s: ie count botch (pairwise), count %u left %u\n",
+ __func__, count, left);
+ return _FAIL;
+ }
+
+ for (i = 0; i < count; i++) {
+ *pairwise_cipher |= rtw_get_wpa_cipher_suite(pos);
+
+ pos += WPA_SELECTOR_LEN;
+ left -= WPA_SELECTOR_LEN;
+ }
+ } else if (left == 1) {
+ RT_TRACE(_module_rtl871x_mlme_c_, _drv_err_,
+ "%s: ie too short (for key mgmt)\n", __func__);
+ return _FAIL;
+ }
+
+ if (is_8021x) {
+ if (left >= 6) {
+ pos += 2;
+ if (!memcmp(pos, RTW_WPA_OUI23A_TYPE, 4)) {
+ RT_TRACE(_module_rtl871x_mlme_c_, _drv_info_,
+ "%s : there has 802.1x auth\n",
+ __func__);
+ *is_8021x = 1;
+ }
+ }
+ }
+
+ return ret;
+}
+
+int rtw_parse_wpa2_ie23a(const u8 *rsn_ie, int rsn_ie_len, int *group_cipher,
+ int *pairwise_cipher, int *is_8021x)
+{
+ int i, ret = _SUCCESS;
+ int left, count;
+ const u8 *pos;
+ u8 SUITE_1X[4] = {0x00, 0x0f, 0xac, 0x01};
+
+ if (rsn_ie_len <= 0) {
+ /* No RSN IE - fail silently */
+ return _FAIL;
+ }
+
+ if (*rsn_ie != WLAN_EID_RSN || *(rsn_ie+1) != (u8)(rsn_ie_len - 2)) {
+ return _FAIL;
+ }
+
+ pos = rsn_ie;
+ pos += 4;
+ left = rsn_ie_len - 4;
+
+ /* group_cipher */
+ if (left >= RSN_SELECTOR_LEN) {
+ *group_cipher = rtw_get_wpa2_cipher_suite(pos);
+
+ pos += RSN_SELECTOR_LEN;
+ left -= RSN_SELECTOR_LEN;
+ } else if (left > 0) {
+ RT_TRACE(_module_rtl871x_mlme_c_, _drv_err_,
+ "%s: ie length mismatch, %u too much\n",
+ __func__, left);
+ return _FAIL;
+ }
+
+ /* pairwise_cipher */
+ if (left >= 2) {
+ /* count = le16_to_cpu(*(u16*)pos); */
+ count = get_unaligned_le16(pos);
+ pos += 2;
+ left -= 2;
+
+ if (count == 0 || left < count * RSN_SELECTOR_LEN) {
+ RT_TRACE(_module_rtl871x_mlme_c_, _drv_err_,
+ "%s: ie count botch (pairwise), count %u left %u\n",
+ __func__, count, left);
+ return _FAIL;
+ }
+
+ for (i = 0; i < count; i++) {
+ *pairwise_cipher |= rtw_get_wpa2_cipher_suite(pos);
+
+ pos += RSN_SELECTOR_LEN;
+ left -= RSN_SELECTOR_LEN;
+ }
+ } else if (left == 1) {
+ RT_TRACE(_module_rtl871x_mlme_c_, _drv_err_,
+ "%s: ie too short (for key mgmt)\n", __func__);
+
+ return _FAIL;
+ }
+
+ if (is_8021x) {
+ if (left >= 6) {
+ pos += 2;
+ if (!memcmp(pos, SUITE_1X, 4)) {
+ RT_TRACE(_module_rtl871x_mlme_c_, _drv_info_,
+ "%s (): there has 802.1x auth\n",
+ __func__);
+ *is_8021x = 1;
+ }
+ }
+ }
+
+ return ret;
+}
+
+/**
+ * rtw_get_wps_attr23a - Search a specific WPS attribute from a given WPS IE
+ * @wps_ie: Address of WPS IE to search
+ * @wps_ielen: Length limit from wps_ie
+ * @target_attr_id: The attribute ID of WPS attribute to search
+ * @buf_attr: If not NULL and the WPS attribute is found, WPS attribute
+ * will be copied to the buf starting from buf_attr
+ * @len_attr: If not NULL and the WPS attribute is found, will set to the
+ * length of the entire WPS attribute
+ *
+ * Returns: the address of the specific WPS attribute found, or NULL
+ */
+const u8 *rtw_get_wps_attr23a(const u8 *wps_ie, uint wps_ielen,
+ u16 target_attr_id, u8 *buf_attr, u32 *len_attr)
+{
+ const u8 *attr_ptr = NULL;
+ const u8 *target_attr_ptr = NULL;
+ u8 wps_oui[4] = {0x00, 0x50, 0xF2, 0x04};
+
+ if (len_attr)
+ *len_attr = 0;
+
+ if (wps_ie[0] != WLAN_EID_VENDOR_SPECIFIC ||
+ memcmp(wps_ie + 2, wps_oui, 4)) {
+ return attr_ptr;
+ }
+
+ /* 6 = 1(Element ID) + 1(Length) + 4(WPS OUI) */
+ attr_ptr = wps_ie + 6; /* goto first attr */
+
+ while (attr_ptr - wps_ie < wps_ielen) {
+ /* 4 = 2(Attribute ID) + 2(Length) */
+ u16 attr_id = get_unaligned_be16(attr_ptr);
+ u16 attr_data_len = get_unaligned_be16(attr_ptr + 2);
+ u16 attr_len = attr_data_len + 4;
+
+ /* DBG_8723A("%s attr_ptr:%p, id:%u, length:%u\n", __func__, attr_ptr, attr_id, attr_data_len); */
+ if (attr_id == target_attr_id) {
+ target_attr_ptr = attr_ptr;
+
+ if (buf_attr)
+ memcpy(buf_attr, attr_ptr, attr_len);
+
+ if (len_attr)
+ *len_attr = attr_len;
+
+ break;
+ } else {
+ attr_ptr += attr_len; /* goto next */
+ }
+ }
+
+ return target_attr_ptr;
+}
+
+/**
+ * rtw_get_wps_attr_content23a - Search a specific WPS attribute content
+ * from a given WPS IE
+ * @wps_ie: Address of WPS IE to search
+ * @wps_ielen: Length limit from wps_ie
+ * @target_attr_id: The attribute ID of WPS attribute to search
+ * @buf_content: If not NULL and the WPS attribute is found, WPS attribute
+ * content will be copied to the buf starting from buf_content
+ * @len_content: If not NULL and the WPS attribute is found, will set to the
+ * length of the WPS attribute content
+ *
+ * Returns: the address of the specific WPS attribute content found, or NULL
+ */
+const u8 *rtw_get_wps_attr_content23a(const u8 *wps_ie, uint wps_ielen,
+ u16 target_attr_id, u8 *buf_content)
+{
+ const u8 *attr_ptr;
+ u32 attr_len;
+
+ attr_ptr = rtw_get_wps_attr23a(wps_ie, wps_ielen, target_attr_id,
+ NULL, &attr_len);
+
+ if (attr_ptr && attr_len) {
+ if (buf_content)
+ memcpy(buf_content, attr_ptr + 4, attr_len - 4);
+
+ return attr_ptr + 4;
+ }
+
+ return NULL;
+}
+
+static int rtw_get_cipher_info(struct wlan_network *pnetwork)
+{
+ const u8 *pbuf;
+ int group_cipher = 0, pairwise_cipher = 0, is8021x = 0;
+ int ret = _FAIL;
+ int r, plen;
+ char *pie;
+
+ pie = pnetwork->network.IEs;
+ plen = pnetwork->network.IELength;
+
+ pbuf = cfg80211_find_vendor_ie(WLAN_OUI_MICROSOFT,
+ WLAN_OUI_TYPE_MICROSOFT_WPA, pie, plen);
+
+ if (pbuf && pbuf[1] > 0) {
+ RT_TRACE(_module_rtl871x_mlme_c_, _drv_info_,
+ "rtw_get_cipher_info: wpa_ielen: %d\n", pbuf[1]);
+ r = rtw_parse_wpa_ie23a(pbuf, pbuf[1] + 2, &group_cipher,
+ &pairwise_cipher, &is8021x);
+ if (r == _SUCCESS) {
+ pnetwork->BcnInfo.pairwise_cipher = pairwise_cipher;
+ pnetwork->BcnInfo.group_cipher = group_cipher;
+ pnetwork->BcnInfo.is_8021x = is8021x;
+ RT_TRACE(_module_rtl871x_mlme_c_, _drv_info_,
+ "%s: pnetwork->pairwise_cipher: %d, is_8021x is %d\n",
+ __func__, pnetwork->BcnInfo.pairwise_cipher,
+ pnetwork->BcnInfo.is_8021x);
+ ret = _SUCCESS;
+ }
+ } else {
+ pbuf = cfg80211_find_ie(WLAN_EID_RSN, pie, plen);
+
+ if (pbuf && pbuf[1] > 0) {
+ RT_TRACE(_module_rtl871x_mlme_c_, _drv_info_,
+ "get RSN IE\n");
+ r = rtw_parse_wpa2_ie23a(pbuf, pbuf[1] + 2,
+ &group_cipher, &pairwise_cipher,
+ &is8021x);
+ if (r == _SUCCESS) {
+ RT_TRACE(_module_rtl871x_mlme_c_, _drv_info_,
+ "get RSN IE OK!!!\n");
+ pnetwork->BcnInfo.pairwise_cipher =
+ pairwise_cipher;
+ pnetwork->BcnInfo.group_cipher = group_cipher;
+ pnetwork->BcnInfo.is_8021x = is8021x;
+ RT_TRACE(_module_rtl871x_mlme_c_, _drv_info_,
+ "%s: pnetwork->pairwise_cipher: %d,pnetwork->group_cipher is %d, is_8021x is %d\n",
+ __func__,
+ pnetwork->BcnInfo.pairwise_cipher,
+ pnetwork->BcnInfo.group_cipher,
+ pnetwork->BcnInfo.is_8021x);
+ ret = _SUCCESS;
+ }
+ }
+ }
+
+ return ret;
+}
+
+void rtw_get_bcn_info23a(struct wlan_network *pnetwork)
+{
+ u8 bencrypt = 0;
+ int pie_len;
+ u8 *pie;
+ const u8 *p;
+
+ if (pnetwork->network.capability & WLAN_CAPABILITY_PRIVACY) {
+ bencrypt = 1;
+ pnetwork->network.Privacy = 1;
+ } else
+ pnetwork->BcnInfo.encryp_protocol = ENCRYP_PROTOCOL_OPENSYS;
+
+ RT_TRACE(_module_rtl871x_mlme_c_, _drv_info_,
+ "%s: ssid =%s\n", __func__, pnetwork->network.Ssid.ssid);
+
+ pie = pnetwork->network.IEs;
+ pie_len = pnetwork->network.IELength;
+
+ p = cfg80211_find_ie(WLAN_EID_RSN, pie, pie_len);
+ if (p && p[1]) {
+ pnetwork->BcnInfo.encryp_protocol = ENCRYP_PROTOCOL_WPA2;
+ } else if (cfg80211_find_vendor_ie(WLAN_OUI_MICROSOFT,
+ WLAN_OUI_TYPE_MICROSOFT_WPA,
+ pie, pie_len)) {
+ pnetwork->BcnInfo.encryp_protocol = ENCRYP_PROTOCOL_WPA;
+ } else {
+ if (bencrypt)
+ pnetwork->BcnInfo.encryp_protocol = ENCRYP_PROTOCOL_WEP;
+ }
+ RT_TRACE(_module_rtl871x_mlme_c_, _drv_info_,
+ "%s: pnetwork->encryp_protocol is %x\n", __func__,
+ pnetwork->BcnInfo.encryp_protocol);
+ RT_TRACE(_module_rtl871x_mlme_c_, _drv_info_,
+ "%s: pnetwork->encryp_protocol is %x\n", __func__,
+ pnetwork->BcnInfo.encryp_protocol);
+ rtw_get_cipher_info(pnetwork);
+
+ /* get bwmode and ch_offset */
+}
+
+/* show MCS rate, unit: 100Kbps */
+u16 rtw_mcs_rate23a(u8 rf_type, u8 bw_40MHz, u8 short_GI_20, u8 short_GI_40,
+ struct ieee80211_mcs_info *mcs)
+{
+ u16 max_rate = 0;
+
+ if (rf_type == RF_1T1R) {
+ if (mcs->rx_mask[0] & BIT(7))
+ max_rate = (bw_40MHz) ? ((short_GI_40)?1500:1350):
+ ((short_GI_20)?722:650);
+ else if (mcs->rx_mask[0] & BIT(6))
+ max_rate = (bw_40MHz) ? ((short_GI_40)?1350:1215):
+ ((short_GI_20)?650:585);
+ else if (mcs->rx_mask[0] & BIT(5))
+ max_rate = (bw_40MHz) ? ((short_GI_40)?1200:1080):
+ ((short_GI_20)?578:520);
+ else if (mcs->rx_mask[0] & BIT(4))
+ max_rate = (bw_40MHz) ? ((short_GI_40)?900:810):
+ ((short_GI_20)?433:390);
+ else if (mcs->rx_mask[0] & BIT(3))
+ max_rate = (bw_40MHz) ? ((short_GI_40)?600:540):
+ ((short_GI_20)?289:260);
+ else if (mcs->rx_mask[0] & BIT(2))
+ max_rate = (bw_40MHz) ? ((short_GI_40)?450:405):
+ ((short_GI_20)?217:195);
+ else if (mcs->rx_mask[0] & BIT(1))
+ max_rate = (bw_40MHz) ? ((short_GI_40)?300:270):
+ ((short_GI_20)?144:130);
+ else if (mcs->rx_mask[0] & BIT(0))
+ max_rate = (bw_40MHz) ? ((short_GI_40)?150:135):
+ ((short_GI_20)?72:65);
+ } else {
+ if (mcs->rx_mask[1]) {
+ if (mcs->rx_mask[1] & BIT(7))
+ max_rate = (bw_40MHz) ? ((short_GI_40)?3000:2700):((short_GI_20)?1444:1300);
+ else if (mcs->rx_mask[1] & BIT(6))
+ max_rate = (bw_40MHz) ? ((short_GI_40)?2700:2430):((short_GI_20)?1300:1170);
+ else if (mcs->rx_mask[1] & BIT(5))
+ max_rate = (bw_40MHz) ? ((short_GI_40)?2400:2160):((short_GI_20)?1156:1040);
+ else if (mcs->rx_mask[1] & BIT(4))
+ max_rate = (bw_40MHz) ? ((short_GI_40)?1800:1620):((short_GI_20)?867:780);
+ else if (mcs->rx_mask[1] & BIT(3))
+ max_rate = (bw_40MHz) ? ((short_GI_40)?1200:1080):((short_GI_20)?578:520);
+ else if (mcs->rx_mask[1] & BIT(2))
+ max_rate = (bw_40MHz) ? ((short_GI_40)?900:810):((short_GI_20)?433:390);
+ else if (mcs->rx_mask[1] & BIT(1))
+ max_rate = (bw_40MHz) ? ((short_GI_40)?600:540):((short_GI_20)?289:260);
+ else if (mcs->rx_mask[1] & BIT(0))
+ max_rate = (bw_40MHz) ? ((short_GI_40)?300:270):((short_GI_20)?144:130);
+ } else {
+ if (mcs->rx_mask[0] & BIT(7))
+ max_rate = (bw_40MHz) ? ((short_GI_40)?1500:1350):((short_GI_20)?722:650);
+ else if (mcs->rx_mask[0] & BIT(6))
+ max_rate = (bw_40MHz) ? ((short_GI_40)?1350:1215):((short_GI_20)?650:585);
+ else if (mcs->rx_mask[0] & BIT(5))
+ max_rate = (bw_40MHz) ? ((short_GI_40)?1200:1080):((short_GI_20)?578:520);
+ else if (mcs->rx_mask[0] & BIT(4))
+ max_rate = (bw_40MHz) ? ((short_GI_40)?900:810):((short_GI_20)?433:390);
+ else if (mcs->rx_mask[0] & BIT(3))
+ max_rate = (bw_40MHz) ? ((short_GI_40)?600:540):((short_GI_20)?289:260);
+ else if (mcs->rx_mask[0] & BIT(2))
+ max_rate = (bw_40MHz) ? ((short_GI_40)?450:405):((short_GI_20)?217:195);
+ else if (mcs->rx_mask[0] & BIT(1))
+ max_rate = (bw_40MHz) ? ((short_GI_40)?300:270):((short_GI_20)?144:130);
+ else if (mcs->rx_mask[0] & BIT(0))
+ max_rate = (bw_40MHz) ? ((short_GI_40)?150:135):((short_GI_20)?72:65);
+ }
+ }
+ return max_rate;
+}
diff --git a/drivers/staging/rtl8723au/core/rtw_mlme.c b/drivers/staging/rtl8723au/core/rtw_mlme.c
new file mode 100644
index 000000000..3c09ea9b7
--- /dev/null
+++ b/drivers/staging/rtl8723au/core/rtw_mlme.c
@@ -0,0 +1,2339 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2011 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ ******************************************************************************/
+#define _RTW_MLME_C_
+
+#include <osdep_service.h>
+#include <drv_types.h>
+#include <recv_osdep.h>
+#include <xmit_osdep.h>
+#include <hal_intf.h>
+#include <mlme_osdep.h>
+#include <sta_info.h>
+#include <linux/ieee80211.h>
+#include <wifi.h>
+#include <wlan_bssdef.h>
+#include <rtw_sreset.h>
+
+static struct wlan_network *
+rtw_select_candidate_from_queue(struct mlme_priv *pmlmepriv);
+static int rtw_do_join(struct rtw_adapter *padapter);
+
+static void rtw_init_mlme_timer(struct rtw_adapter *padapter)
+{
+ struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
+
+ setup_timer(&pmlmepriv->assoc_timer, rtw23a_join_to_handler,
+ (unsigned long)padapter);
+
+ setup_timer(&pmlmepriv->scan_to_timer, rtw_scan_timeout_handler23a,
+ (unsigned long)padapter);
+
+ setup_timer(&pmlmepriv->dynamic_chk_timer,
+ rtw_dynamic_check_timer_handler, (unsigned long)padapter);
+
+ setup_timer(&pmlmepriv->set_scan_deny_timer,
+ rtw_set_scan_deny_timer_hdl, (unsigned long)padapter);
+}
+
+int rtw_init_mlme_priv23a(struct rtw_adapter *padapter)
+{
+ struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
+
+ pmlmepriv->nic_hdl = padapter;
+
+ pmlmepriv->fw_state = 0;
+ pmlmepriv->cur_network.network.ifmode = NL80211_IFTYPE_UNSPECIFIED;
+ /* 1: active, 0: pasive. Maybe someday we should rename this
+ varable to "active_mode" (Jeff) */
+ pmlmepriv->scan_mode = SCAN_ACTIVE;
+
+ spin_lock_init(&pmlmepriv->lock);
+ _rtw_init_queue23a(&pmlmepriv->scanned_queue);
+
+ memset(&pmlmepriv->assoc_ssid, 0, sizeof(struct cfg80211_ssid));
+
+ rtw_clear_scan_deny(padapter);
+
+ rtw_init_mlme_timer(padapter);
+ return _SUCCESS;
+}
+
+#ifdef CONFIG_8723AU_AP_MODE
+static void rtw_free_mlme_ie_data(u8 **ppie, u32 *plen)
+{
+ if (*ppie) {
+ kfree(*ppie);
+ *plen = 0;
+ *ppie = NULL;
+ }
+}
+#endif
+
+void rtw23a_free_mlme_priv_ie_data(struct mlme_priv *pmlmepriv)
+{
+#ifdef CONFIG_8723AU_AP_MODE
+ kfree(pmlmepriv->assoc_req);
+ kfree(pmlmepriv->assoc_rsp);
+ rtw_free_mlme_ie_data(&pmlmepriv->wps_probe_req_ie,
+ &pmlmepriv->wps_probe_req_ie_len);
+#endif
+}
+
+void rtw_free_mlme_priv23a(struct mlme_priv *pmlmepriv)
+{
+ RT_TRACE(_module_rtl871x_mlme_c_, _drv_err_,
+ "rtw_free_mlme_priv23a\n");
+
+ rtw23a_free_mlme_priv_ie_data(pmlmepriv);
+}
+
+struct wlan_network *rtw_alloc_network(struct mlme_priv *pmlmepriv, gfp_t gfp)
+{
+ struct wlan_network *pnetwork;
+
+ pnetwork = kzalloc(sizeof(struct wlan_network), gfp);
+ if (pnetwork) {
+ INIT_LIST_HEAD(&pnetwork->list);
+ pnetwork->network_type = 0;
+ pnetwork->fixed = false;
+ pnetwork->last_scanned = jiffies;
+ pnetwork->join_res = 0;
+ }
+
+ return pnetwork;
+}
+
+static void _rtw_free_network23a(struct mlme_priv *pmlmepriv,
+ struct wlan_network *pnetwork)
+{
+ if (!pnetwork)
+ return;
+
+ if (pnetwork->fixed == true)
+ return;
+
+ list_del_init(&pnetwork->list);
+
+ kfree(pnetwork);
+}
+
+/*
+ return the wlan_network with the matching addr
+
+ Shall be called under atomic context... to avoid possible racing condition...
+*/
+struct wlan_network *
+rtw_find_network23a(struct rtw_queue *scanned_queue, u8 *addr)
+{
+ struct list_head *phead, *plist;
+ struct wlan_network *pnetwork = NULL;
+
+ if (is_zero_ether_addr(addr)) {
+ pnetwork = NULL;
+ goto exit;
+ }
+
+ /* spin_lock_bh(&scanned_queue->lock); */
+
+ phead = get_list_head(scanned_queue);
+ plist = phead->next;
+
+ while (plist != phead) {
+ pnetwork = container_of(plist, struct wlan_network, list);
+
+ if (ether_addr_equal(addr, pnetwork->network.MacAddress))
+ break;
+
+ plist = plist->next;
+ }
+
+ if (plist == phead)
+ pnetwork = NULL;
+
+ /* spin_unlock_bh(&scanned_queue->lock); */
+
+exit:
+
+ return pnetwork;
+}
+
+void rtw_free_network_queue23a(struct rtw_adapter *padapter)
+{
+ struct list_head *phead, *plist, *ptmp;
+ struct wlan_network *pnetwork;
+ struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
+ struct rtw_queue *scanned_queue = &pmlmepriv->scanned_queue;
+
+ spin_lock_bh(&scanned_queue->lock);
+
+ phead = get_list_head(scanned_queue);
+
+ list_for_each_safe(plist, ptmp, phead) {
+ pnetwork = container_of(plist, struct wlan_network, list);
+
+ _rtw_free_network23a(pmlmepriv, pnetwork);
+ }
+
+ spin_unlock_bh(&scanned_queue->lock);
+}
+
+int rtw_if_up23a(struct rtw_adapter *padapter)
+{
+ int res;
+
+ if (padapter->bDriverStopped || padapter->bSurpriseRemoved ||
+ !check_fwstate(&padapter->mlmepriv, _FW_LINKED)) {
+ RT_TRACE(_module_rtl871x_mlme_c_, _drv_info_,
+ "rtw_if_up23a:bDriverStopped(%d) OR bSurpriseRemoved(%d)\n",
+ padapter->bDriverStopped, padapter->bSurpriseRemoved);
+ res = false;
+ } else
+ res = true;
+
+ return res;
+}
+
+void rtw_generate_random_ibss23a(u8 *pibss)
+{
+ unsigned long curtime = jiffies;
+
+ pibss[0] = 0x02; /* in ad-hoc mode bit1 must set to 1 */
+ pibss[1] = 0x11;
+ pibss[2] = 0x87;
+ pibss[3] = curtime & 0xff;/* p[0]; */
+ pibss[4] = (curtime >> 8) & 0xff;/* p[1]; */
+ pibss[5] = (curtime >> 16) & 0xff;/* p[2]; */
+}
+
+void rtw_set_roaming(struct rtw_adapter *adapter, u8 to_roaming)
+{
+ if (to_roaming == 0)
+ adapter->mlmepriv.to_join = false;
+ adapter->mlmepriv.to_roaming = to_roaming;
+}
+
+static void _rtw_roaming(struct rtw_adapter *padapter,
+ struct wlan_network *tgt_network)
+{
+ struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
+ struct wlan_network *pnetwork;
+ int do_join_r;
+
+ if (tgt_network)
+ pnetwork = tgt_network;
+ else
+ pnetwork = &pmlmepriv->cur_network;
+
+ if (padapter->mlmepriv.to_roaming > 0) {
+ DBG_8723A("roaming from %s(%pM), length:%d\n",
+ pnetwork->network.Ssid.ssid,
+ pnetwork->network.MacAddress,
+ pnetwork->network.Ssid.ssid_len);
+ memcpy(&pmlmepriv->assoc_ssid, &pnetwork->network.Ssid,
+ sizeof(struct cfg80211_ssid));
+
+ pmlmepriv->assoc_by_bssid = false;
+
+ while (1) {
+ do_join_r = rtw_do_join(padapter);
+ if (do_join_r == _SUCCESS)
+ break;
+ else {
+ DBG_8723A("roaming do_join return %d\n",
+ do_join_r);
+ pmlmepriv->to_roaming--;
+
+ if (padapter->mlmepriv.to_roaming > 0)
+ continue;
+ else {
+ DBG_8723A("%s(%d) -to roaming fail, "
+ "indicate_disconnect\n",
+ __func__, __LINE__);
+ rtw_indicate_disconnect23a(padapter);
+ break;
+ }
+ }
+ }
+ }
+}
+
+void rtw23a_roaming(struct rtw_adapter *padapter,
+ struct wlan_network *tgt_network)
+{
+ struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
+
+ spin_lock_bh(&pmlmepriv->lock);
+ _rtw_roaming(padapter, tgt_network);
+ spin_unlock_bh(&pmlmepriv->lock);
+}
+
+static void rtw_free_network_nolock(struct mlme_priv *pmlmepriv,
+ struct wlan_network *pnetwork)
+{
+ _rtw_free_network23a(pmlmepriv, pnetwork);
+}
+
+bool rtw_is_same_ibss23a(struct rtw_adapter *adapter,
+ struct wlan_network *pnetwork)
+{
+ int ret;
+ struct security_priv *psecuritypriv = &adapter->securitypriv;
+
+ if (psecuritypriv->dot11PrivacyAlgrthm != 0 &&
+ pnetwork->network.Privacy == 0)
+ ret = false;
+ else if (psecuritypriv->dot11PrivacyAlgrthm == 0 &&
+ pnetwork->network.Privacy == 1)
+ ret = false;
+ else
+ ret = true;
+
+ return ret;
+}
+
+inline int is_same_ess(struct wlan_bssid_ex *a, struct wlan_bssid_ex *b);
+inline int is_same_ess(struct wlan_bssid_ex *a, struct wlan_bssid_ex *b)
+{
+ return (a->Ssid.ssid_len == b->Ssid.ssid_len) &&
+ !memcmp(a->Ssid.ssid, b->Ssid.ssid, a->Ssid.ssid_len);
+}
+
+int is_same_network23a(struct wlan_bssid_ex *src, struct wlan_bssid_ex *dst)
+{
+ u16 s_cap, d_cap;
+
+ s_cap = src->capability;
+ d_cap = dst->capability;
+
+ return ((src->Ssid.ssid_len == dst->Ssid.ssid_len) &&
+ /* (src->DSConfig == dst->DSConfig) && */
+ ether_addr_equal(src->MacAddress, dst->MacAddress) &&
+ !memcmp(src->Ssid.ssid, dst->Ssid.ssid, src->Ssid.ssid_len) &&
+ (s_cap & WLAN_CAPABILITY_IBSS) ==
+ (d_cap & WLAN_CAPABILITY_IBSS) &&
+ (s_cap & WLAN_CAPABILITY_ESS) == (d_cap & WLAN_CAPABILITY_ESS));
+}
+
+struct wlan_network *
+rtw_get_oldest_wlan_network23a(struct rtw_queue *scanned_queue)
+{
+ struct list_head *plist, *phead;
+ struct wlan_network *pwlan;
+ struct wlan_network *oldest = NULL;
+
+ phead = get_list_head(scanned_queue);
+
+ list_for_each(plist, phead) {
+ pwlan = container_of(plist, struct wlan_network, list);
+
+ if (pwlan->fixed != true) {
+ if (!oldest || time_after(oldest->last_scanned,
+ pwlan->last_scanned))
+ oldest = pwlan;
+ }
+ }
+
+ return oldest;
+}
+
+void update_network23a(struct wlan_bssid_ex *dst, struct wlan_bssid_ex *src,
+ struct rtw_adapter *padapter, bool update_ie)
+{
+ u8 ss_ori = dst->SignalStrength;
+ u8 sq_ori = dst->SignalQuality;
+ long rssi_ori = dst->Rssi;
+
+ u8 ss_smp = src->SignalStrength;
+ u8 sq_smp = src->SignalQuality;
+ long rssi_smp = src->Rssi;
+
+ u8 ss_final;
+ u8 sq_final;
+ long rssi_final;
+
+ DBG_8723A("%s %s(%pM, ch%u) ss_ori:%3u, sq_ori:%3u, rssi_ori:%3ld, "
+ "ss_smp:%3u, sq_smp:%3u, rssi_smp:%3ld\n",
+ __func__, src->Ssid.ssid, src->MacAddress,
+ src->DSConfig, ss_ori, sq_ori, rssi_ori,
+ ss_smp, sq_smp, rssi_smp
+ );
+
+ /* The rule below is 1/5 for sample value, 4/5 for history value */
+ if (check_fwstate(&padapter->mlmepriv, _FW_LINKED) &&
+ is_same_network23a(&padapter->mlmepriv.cur_network.network, src)) {
+ /* Take the recvpriv's value for the connected AP*/
+ ss_final = padapter->recvpriv.signal_strength;
+ sq_final = padapter->recvpriv.signal_qual;
+ /* the rssi value here is undecorated, and will be
+ used for antenna diversity */
+ if (sq_smp != 101) /* from the right channel */
+ rssi_final = (src->Rssi+dst->Rssi*4)/5;
+ else
+ rssi_final = rssi_ori;
+ } else {
+ if (sq_smp != 101) { /* from the right channel */
+ ss_final = ((u32)src->SignalStrength +
+ (u32)dst->SignalStrength * 4) / 5;
+ sq_final = ((u32)src->SignalQuality +
+ (u32)dst->SignalQuality * 4) / 5;
+ rssi_final = src->Rssi+dst->Rssi * 4 / 5;
+ } else {
+ /* bss info not receiving from the right channel, use
+ the original RX signal infos */
+ ss_final = dst->SignalStrength;
+ sq_final = dst->SignalQuality;
+ rssi_final = dst->Rssi;
+ }
+
+ }
+
+ if (update_ie)
+ memcpy(dst, src, get_wlan_bssid_ex_sz(src));
+
+ dst->SignalStrength = ss_final;
+ dst->SignalQuality = sq_final;
+ dst->Rssi = rssi_final;
+
+ DBG_8723A("%s %s(%pM), SignalStrength:%u, SignalQuality:%u, "
+ "RawRSSI:%ld\n", __func__, dst->Ssid.ssid, dst->MacAddress,
+ dst->SignalStrength, dst->SignalQuality, dst->Rssi);
+}
+
+static void update_current_network(struct rtw_adapter *adapter,
+ struct wlan_bssid_ex *pnetwork)
+{
+ struct mlme_priv *pmlmepriv = &adapter->mlmepriv;
+
+ if (check_fwstate(pmlmepriv, _FW_LINKED) &&
+ is_same_network23a(&pmlmepriv->cur_network.network, pnetwork)) {
+ update_network23a(&pmlmepriv->cur_network.network,
+ pnetwork, adapter, true);
+
+ rtw_update_protection23a(adapter,
+ pmlmepriv->cur_network.network.IEs,
+ pmlmepriv->cur_network.network.IELength);
+ }
+}
+
+/*
+
+Caller must hold pmlmepriv->lock first.
+
+*/
+static void rtw_update_scanned_network(struct rtw_adapter *adapter,
+ struct wlan_bssid_ex *target)
+{
+ struct list_head *plist, *phead;
+ struct mlme_priv *pmlmepriv = &adapter->mlmepriv;
+ struct wlan_network *pnetwork = NULL;
+ struct wlan_network *oldest = NULL;
+ struct rtw_queue *queue = &pmlmepriv->scanned_queue;
+ u32 bssid_ex_sz;
+ int found = 0;
+
+ spin_lock_bh(&queue->lock);
+ phead = get_list_head(queue);
+
+ list_for_each(plist, phead) {
+ pnetwork = container_of(plist, struct wlan_network, list);
+
+ if (is_same_network23a(&pnetwork->network, target)) {
+ found = 1;
+ break;
+ }
+ if (!oldest || time_after(oldest->last_scanned,
+ pnetwork->last_scanned))
+ oldest = pnetwork;
+ }
+
+ /* If we didn't find a match, then get a new network slot to initialize
+ * with this beacon's information */
+ if (!found) {
+ pnetwork = rtw_alloc_network(pmlmepriv, GFP_ATOMIC);
+ if (!pnetwork) {
+ if (!oldest) {
+ RT_TRACE(_module_rtl871x_mlme_c_, _drv_err_,
+ "something wrong here\n");
+ goto exit;
+ }
+ pnetwork = oldest;
+ } else
+ list_add_tail(&pnetwork->list, &queue->queue);
+
+ bssid_ex_sz = get_wlan_bssid_ex_sz(target);
+ target->Length = bssid_ex_sz;
+ memcpy(&pnetwork->network, target, bssid_ex_sz);
+
+ /* variable initialize */
+ pnetwork->fixed = false;
+ pnetwork->last_scanned = jiffies;
+
+ pnetwork->network_type = 0;
+ pnetwork->join_res = 0;
+
+ /* bss info not receiving from the right channel */
+ if (pnetwork->network.SignalQuality == 101)
+ pnetwork->network.SignalQuality = 0;
+ } else {
+ /*
+ * we have an entry and we are going to update it. But
+ * this entry may be already expired. In this case we
+ * do the same as we found a new net and call the
+ * new_net handler
+ */
+ bool update_ie = true;
+
+ pnetwork->last_scanned = jiffies;
+
+ /* target.reserved == 1, means that scanned network is
+ * a bcn frame. */
+ if (pnetwork->network.IELength > target->IELength &&
+ target->reserved == 1)
+ update_ie = false;
+
+ update_network23a(&pnetwork->network, target, adapter,
+ update_ie);
+ }
+
+exit:
+ spin_unlock_bh(&queue->lock);
+}
+
+static void rtw_add_network(struct rtw_adapter *adapter,
+ struct wlan_bssid_ex *pnetwork)
+{
+ update_current_network(adapter, pnetwork);
+ rtw_update_scanned_network(adapter, pnetwork);
+}
+
+/* select the desired network based on the capability of the (i)bss. */
+/* check items: (1) security */
+/* (2) network_type */
+/* (3) WMM */
+/* (4) HT */
+/* (5) others */
+static int rtw_is_desired_network(struct rtw_adapter *adapter,
+ struct wlan_network *pnetwork)
+{
+ struct security_priv *psecuritypriv = &adapter->securitypriv;
+ struct mlme_priv *pmlmepriv = &adapter->mlmepriv;
+ u32 desired_encmode;
+ u32 privacy;
+ int bselected = true;
+
+ desired_encmode = psecuritypriv->ndisencryptstatus;
+ privacy = pnetwork->network.Privacy;
+
+ if (check_fwstate(pmlmepriv, WIFI_UNDER_WPS)) {
+ if (cfg80211_find_vendor_ie(WLAN_OUI_MICROSOFT,
+ WLAN_OUI_TYPE_MICROSOFT_WPA,
+ pnetwork->network.IEs,
+ pnetwork->network.IELength))
+ return true;
+ else
+ return false;
+ }
+ if (adapter->registrypriv.wifi_spec == 1) {
+ /* for correct flow of 8021X to do.... */
+ if (desired_encmode == Ndis802_11EncryptionDisabled &&
+ privacy != 0)
+ bselected = false;
+ }
+
+ if (desired_encmode != Ndis802_11EncryptionDisabled && privacy == 0) {
+ DBG_8723A("desired_encmode: %d, privacy: %d\n",
+ desired_encmode, privacy);
+ bselected = false;
+ }
+
+ if (check_fwstate(pmlmepriv, WIFI_ADHOC_STATE)) {
+ if (pnetwork->network.ifmode !=
+ pmlmepriv->cur_network.network.ifmode)
+ bselected = false;
+ }
+
+ return bselected;
+}
+
+void rtw_survey_event_cb23a(struct rtw_adapter *adapter, const u8 *pbuf)
+{
+ u32 len;
+ struct wlan_bssid_ex *pnetwork;
+ struct mlme_priv *pmlmepriv = &adapter->mlmepriv;
+ struct survey_event *survey = (struct survey_event *)pbuf;
+
+ pnetwork = survey->bss;
+
+ RT_TRACE(_module_rtl871x_mlme_c_, _drv_info_,
+ "rtw_survey_event_cb23a, ssid=%s\n", pnetwork->Ssid.ssid);
+
+ len = get_wlan_bssid_ex_sz(pnetwork);
+ if (len > (sizeof(struct wlan_bssid_ex))) {
+ RT_TRACE(_module_rtl871x_mlme_c_, _drv_err_,
+ "****rtw_survey_event_cb23a: return a wrong bss ***\n");
+ return;
+ }
+
+ spin_lock_bh(&pmlmepriv->lock);
+
+ /* update IBSS_network 's timestamp */
+ if (check_fwstate(pmlmepriv, WIFI_ADHOC_MASTER_STATE)) {
+ if (ether_addr_equal(pmlmepriv->cur_network.network.MacAddress,
+ pnetwork->MacAddress)) {
+ struct wlan_network *ibss_wlan;
+
+ pmlmepriv->cur_network.network.beacon_interval =
+ pnetwork->beacon_interval;
+ pmlmepriv->cur_network.network.capability =
+ pnetwork->capability;
+ pmlmepriv->cur_network.network.tsf = pnetwork->tsf;
+ spin_lock_bh(&pmlmepriv->scanned_queue.lock);
+ ibss_wlan = rtw_find_network23a(
+ &pmlmepriv->scanned_queue,
+ pnetwork->MacAddress);
+ if (ibss_wlan) {
+ pmlmepriv->cur_network.network.beacon_interval =
+ ibss_wlan->network.beacon_interval;
+ pmlmepriv->cur_network.network.capability =
+ ibss_wlan->network.capability;
+ pmlmepriv->cur_network.network.tsf =
+ ibss_wlan->network.tsf;
+ spin_unlock_bh(&pmlmepriv->scanned_queue.lock);
+ goto exit;
+ }
+ spin_unlock_bh(&pmlmepriv->scanned_queue.lock);
+ }
+ }
+
+ /* lock pmlmepriv->lock when you accessing network_q */
+ if (!check_fwstate(pmlmepriv, _FW_UNDER_LINKING)) {
+ if (pnetwork->Ssid.ssid[0] == 0)
+ pnetwork->Ssid.ssid_len = 0;
+
+ rtw_add_network(adapter, pnetwork);
+ }
+
+exit:
+
+ spin_unlock_bh(&pmlmepriv->lock);
+
+ kfree(survey->bss);
+ survey->bss = NULL;
+}
+
+void
+rtw_surveydone_event_callback23a(struct rtw_adapter *adapter, const u8 *pbuf)
+{
+ struct mlme_priv *pmlmepriv = &adapter->mlmepriv;
+ struct mlme_ext_priv *pmlmeext = &adapter->mlmeextpriv;
+ int ret;
+
+ spin_lock_bh(&pmlmepriv->lock);
+
+ if (pmlmepriv->wps_probe_req_ie) {
+ pmlmepriv->wps_probe_req_ie_len = 0;
+ kfree(pmlmepriv->wps_probe_req_ie);
+ pmlmepriv->wps_probe_req_ie = NULL;
+ }
+
+ RT_TRACE(_module_rtl871x_mlme_c_, _drv_info_,
+ "rtw_surveydone_event_callback23a: fw_state:%x\n",
+ get_fwstate(pmlmepriv));
+
+ if (check_fwstate(pmlmepriv, _FW_UNDER_SURVEY)) {
+ del_timer_sync(&pmlmepriv->scan_to_timer);
+
+ _clr_fwstate_(pmlmepriv, _FW_UNDER_SURVEY);
+ } else {
+ RT_TRACE(_module_rtl871x_mlme_c_, _drv_err_,
+ "nic status =%x, survey done event comes too late!\n",
+ get_fwstate(pmlmepriv));
+ }
+
+ rtw_set_signal_stat_timer(&adapter->recvpriv);
+
+ if (pmlmepriv->to_join == true) {
+ set_fwstate(pmlmepriv, _FW_UNDER_LINKING);
+ if (check_fwstate(pmlmepriv, WIFI_ADHOC_STATE)) {
+ ret = rtw_select_and_join_from_scanned_queue23a(
+ pmlmepriv);
+ if (ret != _SUCCESS)
+ rtw_do_join_adhoc(adapter);
+ } else {
+ pmlmepriv->to_join = false;
+ ret = rtw_select_and_join_from_scanned_queue23a(
+ pmlmepriv);
+ if (ret != _SUCCESS) {
+ DBG_8723A("try_to_join, but select scanning "
+ "queue fail, to_roaming:%d\n",
+ adapter->mlmepriv.to_roaming);
+ if (adapter->mlmepriv.to_roaming) {
+ if (--pmlmepriv->to_roaming == 0 ||
+ rtw_sitesurvey_cmd23a(
+ adapter,
+ &pmlmepriv->assoc_ssid, 1,
+ NULL, 0) != _SUCCESS) {
+ rtw_set_roaming(adapter, 0);
+ rtw_free_assoc_resources23a(
+ adapter, 1);
+ rtw_indicate_disconnect23a(
+ adapter);
+ } else
+ pmlmepriv->to_join = true;
+ }
+ _clr_fwstate_(pmlmepriv, _FW_UNDER_LINKING);
+ }
+ }
+ }
+
+ spin_unlock_bh(&pmlmepriv->lock);
+
+ rtw_os_xmit_schedule23a(adapter);
+
+ if (pmlmeext->sitesurvey_res.bss_cnt == 0)
+ rtw_sreset_reset(adapter);
+
+ rtw_cfg80211_surveydone_event_callback(adapter);
+}
+
+static void free_scanqueue(struct mlme_priv *pmlmepriv)
+{
+ struct wlan_network *pnetwork;
+ struct rtw_queue *scan_queue = &pmlmepriv->scanned_queue;
+ struct list_head *plist, *phead, *ptemp;
+
+ RT_TRACE(_module_rtl871x_mlme_c_, _drv_notice_, "+free_scanqueue\n");
+ spin_lock_bh(&scan_queue->lock);
+
+ phead = get_list_head(scan_queue);
+
+ list_for_each_safe(plist, ptemp, phead) {
+ pnetwork = container_of(plist, struct wlan_network, list);
+ pnetwork->fixed = false;
+ _rtw_free_network23a(pmlmepriv, pnetwork);
+ }
+
+ spin_unlock_bh(&scan_queue->lock);
+}
+
+/*
+ *rtw_free_assoc_resources23a: the caller has to lock pmlmepriv->lock
+ */
+void rtw_free_assoc_resources23a(struct rtw_adapter *adapter,
+ int lock_scanned_queue)
+{
+ struct wlan_network *pwlan;
+ struct mlme_priv *pmlmepriv = &adapter->mlmepriv;
+ struct sta_priv *pstapriv = &adapter->stapriv;
+ struct wlan_network *tgt_network = &pmlmepriv->cur_network;
+ struct sta_info *psta;
+
+ RT_TRACE(_module_rtl871x_mlme_c_, _drv_notice_,
+ "+rtw_free_assoc_resources23a\n");
+ RT_TRACE(_module_rtl871x_mlme_c_, _drv_info_,
+ "tgt_network->network.MacAddress=%pM ssid=%s\n",
+ tgt_network->network.MacAddress,
+ tgt_network->network.Ssid.ssid);
+
+ if (check_fwstate(pmlmepriv, WIFI_STATION_STATE|WIFI_AP_STATE)) {
+ psta = rtw_get_stainfo23a(&adapter->stapriv,
+ tgt_network->network.MacAddress);
+
+ spin_lock_bh(&pstapriv->sta_hash_lock);
+ rtw_free_stainfo23a(adapter, psta);
+ spin_unlock_bh(&pstapriv->sta_hash_lock);
+ }
+
+ if (check_fwstate(pmlmepriv, WIFI_ADHOC_STATE |
+ WIFI_ADHOC_MASTER_STATE|WIFI_AP_STATE)) {
+ rtw_free_all_stainfo23a(adapter);
+
+ psta = rtw_get_bcmc_stainfo23a(adapter);
+ spin_lock_bh(&pstapriv->sta_hash_lock);
+ rtw_free_stainfo23a(adapter, psta);
+ spin_unlock_bh(&pstapriv->sta_hash_lock);
+
+ rtw_init_bcmc_stainfo23a(adapter);
+ }
+
+ if (lock_scanned_queue)
+ spin_lock_bh(&pmlmepriv->scanned_queue.lock);
+
+ pwlan = rtw_find_network23a(&pmlmepriv->scanned_queue,
+ tgt_network->network.MacAddress);
+ if (pwlan)
+ pwlan->fixed = false;
+ else
+ RT_TRACE(_module_rtl871x_mlme_c_, _drv_err_,
+ "rtw_free_assoc_resources23a : pwlan== NULL\n");
+
+ if (check_fwstate(pmlmepriv, WIFI_ADHOC_MASTER_STATE) &&
+ adapter->stapriv.asoc_sta_count == 1)
+ rtw_free_network_nolock(pmlmepriv, pwlan);
+
+ if (lock_scanned_queue)
+ spin_unlock_bh(&pmlmepriv->scanned_queue.lock);
+
+ pmlmepriv->key_mask = 0;
+}
+
+/*
+*rtw_indicate_connect23a: the caller has to lock pmlmepriv->lock
+*/
+void rtw_indicate_connect23a(struct rtw_adapter *padapter)
+{
+ struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
+
+ RT_TRACE(_module_rtl871x_mlme_c_, _drv_err_,
+ "+rtw_indicate_connect23a\n");
+
+ pmlmepriv->to_join = false;
+
+ if (!check_fwstate(&padapter->mlmepriv, _FW_LINKED)) {
+ set_fwstate(pmlmepriv, _FW_LINKED);
+
+ rtw_cfg80211_indicate_connect(padapter);
+
+ netif_carrier_on(padapter->pnetdev);
+
+ if (padapter->pid[2] != 0)
+ kill_pid(find_vpid(padapter->pid[2]), SIGALRM, 1);
+ }
+
+ rtw_set_roaming(padapter, 0);
+
+ rtw_set_scan_deny(padapter, 3000);
+
+ RT_TRACE(_module_rtl871x_mlme_c_, _drv_err_,
+ "-rtw_indicate_connect23a: fw_state=0x%08x\n",
+ get_fwstate(pmlmepriv));
+}
+
+/*
+ *rtw_indicate_disconnect23a: the caller has to lock pmlmepriv->lock
+ */
+void rtw_indicate_disconnect23a(struct rtw_adapter *padapter)
+{
+ struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
+
+ RT_TRACE(_module_rtl871x_mlme_c_, _drv_err_,
+ "+rtw_indicate_disconnect23a\n");
+
+ _clr_fwstate_(pmlmepriv, _FW_UNDER_LINKING|WIFI_UNDER_WPS);
+
+ /* DBG_8723A("clear wps when %s\n", __func__); */
+
+ if (padapter->mlmepriv.to_roaming > 0)
+ _clr_fwstate_(pmlmepriv, _FW_LINKED);
+
+ if (check_fwstate(&padapter->mlmepriv, _FW_LINKED) ||
+ padapter->mlmepriv.to_roaming <= 0) {
+ rtw_os_indicate_disconnect23a(padapter);
+
+ /* set ips_deny_time to avoid enter IPS before LPS leave */
+ padapter->pwrctrlpriv.ips_deny_time =
+ jiffies + msecs_to_jiffies(3000);
+
+ _clr_fwstate_(pmlmepriv, _FW_LINKED);
+
+ rtw_clear_scan_deny(padapter);
+ }
+
+ rtw_lps_ctrl_wk_cmd23a(padapter, LPS_CTRL_DISCONNECT, 1);
+}
+
+void rtw_scan_abort23a(struct rtw_adapter *adapter)
+{
+ unsigned long start;
+ struct mlme_priv *pmlmepriv = &adapter->mlmepriv;
+ struct mlme_ext_priv *pmlmeext = &adapter->mlmeextpriv;
+
+ start = jiffies;
+ pmlmeext->scan_abort = true;
+ while (check_fwstate(pmlmepriv, _FW_UNDER_SURVEY) &&
+ jiffies_to_msecs(jiffies - start) <= 200) {
+ if (adapter->bDriverStopped || adapter->bSurpriseRemoved)
+ break;
+
+ DBG_8723A("%s(%s): fw_state = _FW_UNDER_SURVEY!\n",
+ __func__, adapter->pnetdev->name);
+ msleep(20);
+ }
+
+ if (check_fwstate(pmlmepriv, _FW_UNDER_SURVEY)) {
+ if (!adapter->bDriverStopped && !adapter->bSurpriseRemoved)
+ DBG_8723A("%s(%s): waiting for scan_abort time out!\n",
+ __func__, adapter->pnetdev->name);
+ rtw_cfg80211_indicate_scan_done(wdev_to_priv(adapter->rtw_wdev),
+ true);
+ }
+ pmlmeext->scan_abort = false;
+}
+
+static struct sta_info *
+rtw_joinbss_update_stainfo(struct rtw_adapter *padapter,
+ struct wlan_network *pnetwork)
+{
+ int i;
+ struct sta_info *bmc_sta, *psta;
+ struct recv_reorder_ctrl *preorder_ctrl;
+ struct sta_priv *pstapriv = &padapter->stapriv;
+
+ psta = rtw_get_stainfo23a(pstapriv, pnetwork->network.MacAddress);
+ if (!psta)
+ psta = rtw_alloc_stainfo23a(pstapriv,
+ pnetwork->network.MacAddress,
+ GFP_ATOMIC);
+
+ if (psta) { /* update ptarget_sta */
+ DBG_8723A("%s\n", __func__);
+
+ psta->aid = pnetwork->join_res;
+ psta->mac_id = 0;
+
+ /* sta mode */
+ rtl8723a_SetHalODMVar(padapter, HAL_ODM_STA_INFO, psta, true);
+
+ /* security related */
+ if (padapter->securitypriv.dot11AuthAlgrthm ==
+ dot11AuthAlgrthm_8021X) {
+ padapter->securitypriv.binstallGrpkey = 0;
+ padapter->securitypriv.busetkipkey = 0;
+
+ psta->ieee8021x_blocked = true;
+ psta->dot118021XPrivacy =
+ padapter->securitypriv.dot11PrivacyAlgrthm;
+
+ memset(&psta->dot118021x_UncstKey, 0,
+ sizeof (union Keytype));
+
+ memset(&psta->dot11tkiprxmickey, 0,
+ sizeof (union Keytype));
+ memset(&psta->dot11tkiptxmickey, 0,
+ sizeof (union Keytype));
+
+ memset(&psta->dot11txpn, 0, sizeof (union pn48));
+ memset(&psta->dot11rxpn, 0, sizeof (union pn48));
+ }
+
+ /* Commented by Albert 2012/07/21 */
+ /* When doing the WPS, the wps_ie_len won't equal to 0 */
+ /* And the Wi-Fi driver shouldn't allow the data packet
+ to be transmitted. */
+ if (padapter->securitypriv.wps_ie_len != 0) {
+ psta->ieee8021x_blocked = true;
+ padapter->securitypriv.wps_ie_len = 0;
+ }
+
+ /* for A-MPDU Rx reordering buffer control for bmc_sta &
+ * sta_info */
+ /* if A-MPDU Rx is enabled, resetting
+ rx_ordering_ctrl wstart_b(indicate_seq) to default
+ value = 0xffff */
+ /* todo: check if AP can send A-MPDU packets */
+ for (i = 0; i < 16 ; i++) {
+ /* preorder_ctrl = &precvpriv->recvreorder_ctrl[i]; */
+ preorder_ctrl = &psta->recvreorder_ctrl[i];
+ preorder_ctrl->enable = false;
+ preorder_ctrl->indicate_seq = 0xffff;
+ preorder_ctrl->wend_b = 0xffff;
+ /* max_ampdu_sz; ex. 32(kbytes) -> wsize_b = 32 */
+ preorder_ctrl->wsize_b = 64;
+ }
+
+ bmc_sta = rtw_get_bcmc_stainfo23a(padapter);
+ if (bmc_sta) {
+ for (i = 0; i < 16 ; i++) {
+ preorder_ctrl = &bmc_sta->recvreorder_ctrl[i];
+ preorder_ctrl->enable = false;
+ preorder_ctrl->indicate_seq = 0xffff;
+ preorder_ctrl->wend_b = 0xffff;
+ /* max_ampdu_sz; ex. 32(kbytes) ->
+ wsize_b = 32 */
+ preorder_ctrl->wsize_b = 64;
+ }
+ }
+
+ /* misc. */
+ update_sta_info23a(padapter, psta);
+
+ }
+
+ return psta;
+}
+
+/* pnetwork : returns from rtw23a_joinbss_event_cb */
+/* ptarget_wlan: found from scanned_queue */
+static void
+rtw_joinbss_update_network23a(struct rtw_adapter *padapter,
+ struct wlan_network *ptarget_wlan,
+ struct wlan_network *pnetwork)
+{
+ struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
+ struct wlan_network *cur_network = &pmlmepriv->cur_network;
+
+ DBG_8723A("%s\n", __func__);
+
+ RT_TRACE(_module_rtl871x_mlme_c_, _drv_info_,
+ "fw_state:%x, BSSID:%pM\n",
+ get_fwstate(pmlmepriv),
+ pnetwork->network.MacAddress);
+
+ /* why not use ptarget_wlan?? */
+ memcpy(&cur_network->network, &pnetwork->network,
+ pnetwork->network.Length);
+ /* some IEs in pnetwork is wrong, so we should use ptarget_wlan IEs */
+ cur_network->network.IELength = ptarget_wlan->network.IELength;
+ memcpy(&cur_network->network.IEs[0], &ptarget_wlan->network.IEs[0],
+ MAX_IE_SZ);
+
+ cur_network->network.capability = ptarget_wlan->network.capability;
+ cur_network->network.beacon_interval =
+ ptarget_wlan->network.beacon_interval;
+ cur_network->network.tsf = ptarget_wlan->network.tsf;
+
+ rtw_set_signal_stat_timer(&padapter->recvpriv);
+ padapter->recvpriv.signal_strength =
+ ptarget_wlan->network.SignalStrength;
+ padapter->recvpriv.signal_qual = ptarget_wlan->network.SignalQuality;
+ /*
+ * the ptarget_wlan->network.Rssi is raw data, we use
+ * ptarget_wlan->network.SignalStrength instead (has scaled)
+ */
+ DBG_8723A("%s signal_strength:%3u, signal_qual:%3u\n",
+ __func__, padapter->recvpriv.signal_strength,
+ padapter->recvpriv.signal_qual);
+ rtw_set_signal_stat_timer(&padapter->recvpriv);
+
+ /* update fw_state will clr _FW_UNDER_LINKING here indirectly */
+ switch (pnetwork->network.ifmode) {
+ case NL80211_IFTYPE_P2P_CLIENT:
+ case NL80211_IFTYPE_STATION:
+ if (pmlmepriv->fw_state & WIFI_UNDER_WPS)
+ pmlmepriv->fw_state = WIFI_STATION_STATE|WIFI_UNDER_WPS;
+ else
+ pmlmepriv->fw_state = WIFI_STATION_STATE;
+ break;
+ case NL80211_IFTYPE_ADHOC:
+ pmlmepriv->fw_state = WIFI_ADHOC_STATE;
+ break;
+ default:
+ pmlmepriv->fw_state = WIFI_NULL_STATE;
+ RT_TRACE(_module_rtl871x_mlme_c_, _drv_err_,
+ "Invalid network_mode\n");
+ break;
+ }
+
+ rtw_update_protection23a(padapter, cur_network->network.IEs,
+ cur_network->network.IELength);
+
+ rtw_update_ht_cap23a(padapter, cur_network->network.IEs,
+ cur_network->network.IELength);
+}
+
+/*
+ * Notes:
+ * the function could be > passive_level (the same context as Rx tasklet)
+ * pnetwork : returns from rtw23a_joinbss_event_cb
+ * ptarget_wlan: found from scanned_queue
+ * if join_res > 0, for (fw_state==WIFI_STATION_STATE),
+ * we check if "ptarget_sta" & "ptarget_wlan" exist.
+ * if join_res > 0, for (fw_state==WIFI_ADHOC_STATE),
+ * we only check if "ptarget_wlan" exist.
+ * if join_res > 0, update "cur_network->network" from "pnetwork->network"
+ * if (ptarget_wlan !=NULL).
+ */
+
+void rtw_joinbss_event_prehandle23a(struct rtw_adapter *adapter, u8 *pbuf)
+{
+ struct sta_info *ptarget_sta, *pcur_sta;
+ struct sta_priv *pstapriv = &adapter->stapriv;
+ struct mlme_priv *pmlmepriv = &adapter->mlmepriv;
+ struct wlan_network *pnetwork = (struct wlan_network *)pbuf;
+ struct wlan_network *cur_network = &pmlmepriv->cur_network;
+ struct wlan_network *pcur_wlan, *ptarget_wlan = NULL;
+ bool the_same_macaddr;
+
+ RT_TRACE(_module_rtl871x_mlme_c_, _drv_info_,
+ "joinbss event call back received with res=%d\n",
+ pnetwork->join_res);
+
+ if (pmlmepriv->assoc_ssid.ssid_len == 0) {
+ RT_TRACE(_module_rtl871x_mlme_c_, _drv_err_,
+ "@@@@@ joinbss event call back for Any SSid\n");
+ } else {
+ RT_TRACE(_module_rtl871x_mlme_c_, _drv_err_,
+ "@@@@@ rtw23a_joinbss_event_cb for SSid:%s\n",
+ pmlmepriv->assoc_ssid.ssid);
+ }
+
+ if (ether_addr_equal(pnetwork->network.MacAddress,
+ cur_network->network.MacAddress))
+ the_same_macaddr = true;
+ else
+ the_same_macaddr = false;
+
+ pnetwork->network.Length = get_wlan_bssid_ex_sz(&pnetwork->network);
+ if (pnetwork->network.Length > sizeof(struct wlan_bssid_ex)) {
+ RT_TRACE(_module_rtl871x_mlme_c_, _drv_err_,
+ "***joinbss_evt_callback return a wrong bss ***\n");
+ return;
+ }
+
+ spin_lock_bh(&pmlmepriv->lock);
+
+ RT_TRACE(_module_rtl871x_mlme_c_, _drv_info_,
+ "rtw23a_joinbss_event_cb !! _enter_critical\n");
+
+ if (pnetwork->join_res > 0) {
+ spin_lock_bh(&pmlmepriv->scanned_queue.lock);
+ if (check_fwstate(pmlmepriv, _FW_UNDER_LINKING)) {
+ /* s1. find ptarget_wlan */
+ if (check_fwstate(pmlmepriv, _FW_LINKED)) {
+ if (the_same_macaddr) {
+ ptarget_wlan = rtw_find_network23a(&pmlmepriv->scanned_queue, cur_network->network.MacAddress);
+ } else {
+ pcur_wlan = rtw_find_network23a(&pmlmepriv->scanned_queue, cur_network->network.MacAddress);
+ if (pcur_wlan)
+ pcur_wlan->fixed = false;
+
+ pcur_sta = rtw_get_stainfo23a(pstapriv, cur_network->network.MacAddress);
+ if (pcur_sta) {
+ spin_lock_bh(&pstapriv->sta_hash_lock);
+ rtw_free_stainfo23a(adapter,
+ pcur_sta);
+ spin_unlock_bh(&pstapriv->sta_hash_lock);
+ }
+
+ ptarget_wlan = rtw_find_network23a(&pmlmepriv->scanned_queue, pnetwork->network.MacAddress);
+ if (check_fwstate(pmlmepriv,
+ WIFI_STATION_STATE)) {
+ if (ptarget_wlan)
+ ptarget_wlan->fixed =
+ true;
+ }
+ }
+
+ } else {
+ ptarget_wlan = rtw_find_network23a(
+ &pmlmepriv->scanned_queue,
+ pnetwork->network.MacAddress);
+ if (check_fwstate(pmlmepriv,
+ WIFI_STATION_STATE)) {
+ if (ptarget_wlan)
+ ptarget_wlan->fixed = true;
+ }
+ }
+
+ /* s2. update cur_network */
+ if (ptarget_wlan)
+ rtw_joinbss_update_network23a(adapter,
+ ptarget_wlan,
+ pnetwork);
+ else {
+ RT_TRACE(_module_rtl871x_mlme_c_, _drv_err_,
+ "Can't find ptarget_wlan when joinbss_event callback\n");
+ spin_unlock_bh(&pmlmepriv->scanned_queue.lock);
+ goto ignore_joinbss_callback;
+ }
+
+ /* s3. find ptarget_sta & update ptarget_sta after
+ update cur_network only for station mode */
+ if (check_fwstate(pmlmepriv, WIFI_STATION_STATE)) {
+ ptarget_sta = rtw_joinbss_update_stainfo(
+ adapter, pnetwork);
+ if (!ptarget_sta) {
+ RT_TRACE(_module_rtl871x_mlme_c_,
+ _drv_err_,
+ "Can't update stainfo when joinbss_event callback\n");
+ spin_unlock_bh(&pmlmepriv->scanned_queue.lock);
+ goto ignore_joinbss_callback;
+ }
+ }
+
+ /* s4. indicate connect */
+ if (check_fwstate(pmlmepriv, WIFI_STATION_STATE))
+ rtw_indicate_connect23a(adapter);
+ else {
+ /* adhoc mode will rtw_indicate_connect23a
+ when rtw_stassoc_event_callback23a */
+ RT_TRACE(_module_rtl871x_mlme_c_, _drv_info_,
+ "adhoc mode, fw_state:%x\n",
+ get_fwstate(pmlmepriv));
+ }
+
+ /* s5. Cancle assoc_timer */
+ del_timer_sync(&pmlmepriv->assoc_timer);
+
+ RT_TRACE(_module_rtl871x_mlme_c_, _drv_info_,
+ "Cancle assoc_timer\n");
+ } else {
+ RT_TRACE(_module_rtl871x_mlme_c_, _drv_err_,
+ "rtw23a_joinbss_event_cb err: fw_state:%x\n",
+ get_fwstate(pmlmepriv));
+ spin_unlock_bh(&pmlmepriv->scanned_queue.lock);
+ goto ignore_joinbss_callback;
+ }
+ spin_unlock_bh(&pmlmepriv->scanned_queue.lock);
+ } else if (pnetwork->join_res == -4) {
+ rtw_reset_securitypriv23a(adapter);
+ mod_timer(&pmlmepriv->assoc_timer,
+ jiffies + msecs_to_jiffies(1));
+
+ /* rtw_free_assoc_resources23a(adapter, 1); */
+
+ if (check_fwstate(pmlmepriv, _FW_UNDER_LINKING)) {
+ RT_TRACE(_module_rtl871x_mlme_c_, _drv_err_,
+ "fail! clear _FW_UNDER_LINKING ^^^fw_state=%x\n",
+ get_fwstate(pmlmepriv));
+ _clr_fwstate_(pmlmepriv, _FW_UNDER_LINKING);
+ }
+ } else {
+ /* if join_res < 0 (join fails), then try again */
+ mod_timer(&pmlmepriv->assoc_timer,
+ jiffies + msecs_to_jiffies(1));
+ _clr_fwstate_(pmlmepriv, _FW_UNDER_LINKING);
+ }
+
+ignore_joinbss_callback:
+
+ spin_unlock_bh(&pmlmepriv->lock);
+}
+
+void rtw23a_joinbss_event_cb(struct rtw_adapter *adapter, const u8 *pbuf)
+{
+ struct wlan_network *pnetwork = (struct wlan_network *)pbuf;
+
+ mlmeext_joinbss_event_callback23a(adapter, pnetwork->join_res);
+
+ rtw_os_xmit_schedule23a(adapter);
+}
+
+void rtw_stassoc_event_callback23a(struct rtw_adapter *adapter, const u8 *pbuf)
+{
+ struct sta_info *psta;
+ struct mlme_priv *pmlmepriv = &adapter->mlmepriv;
+ struct stassoc_event *pstassoc = (struct stassoc_event *)pbuf;
+ struct wlan_network *cur_network = &pmlmepriv->cur_network;
+ struct wlan_network *ptarget_wlan;
+
+ if (rtw_access_ctrl23a(adapter, pstassoc->macaddr) == false)
+ return;
+
+#ifdef CONFIG_8723AU_AP_MODE
+ if (check_fwstate(pmlmepriv, WIFI_AP_STATE)) {
+ psta = rtw_get_stainfo23a(&adapter->stapriv, pstassoc->macaddr);
+ if (psta) {
+ /* bss_cap_update_on_sta_join23a(adapter, psta); */
+ /* sta_info_update23a(adapter, psta); */
+ ap_sta_info_defer_update23a(adapter, psta);
+ }
+ return;
+ }
+#endif
+ /* for AD-HOC mode */
+ psta = rtw_get_stainfo23a(&adapter->stapriv, pstassoc->macaddr);
+ if (psta != NULL) {
+ /* the sta have been in sta_info_queue => do nothing */
+ RT_TRACE(_module_rtl871x_mlme_c_, _drv_err_,
+ "Error: rtw_stassoc_event_callback23a: sta has been in sta_hash_queue\n");
+ /* between drv has received this event before and
+ fw have not yet to set key to CAM_ENTRY) */
+ return;
+ }
+
+ psta = rtw_alloc_stainfo23a(&adapter->stapriv, pstassoc->macaddr,
+ GFP_KERNEL);
+ if (!psta) {
+ RT_TRACE(_module_rtl871x_mlme_c_, _drv_err_,
+ "Can't alloc sta_info when rtw_stassoc_event_callback23a\n");
+ return;
+ }
+
+ /* to do : init sta_info variable */
+ psta->qos_option = 0;
+ psta->mac_id = (uint)pstassoc->cam_id;
+ /* psta->aid = (uint)pstassoc->cam_id; */
+ DBG_8723A("%s\n", __func__);
+ /* for ad-hoc mode */
+ rtl8723a_SetHalODMVar(adapter, HAL_ODM_STA_INFO, psta, true);
+
+ if (adapter->securitypriv.dot11AuthAlgrthm == dot11AuthAlgrthm_8021X)
+ psta->dot118021XPrivacy =
+ adapter->securitypriv.dot11PrivacyAlgrthm;
+
+ psta->ieee8021x_blocked = false;
+
+ spin_lock_bh(&pmlmepriv->lock);
+
+ if (check_fwstate(pmlmepriv, WIFI_ADHOC_MASTER_STATE) ||
+ check_fwstate(pmlmepriv, WIFI_ADHOC_STATE)) {
+ if (adapter->stapriv.asoc_sta_count == 2) {
+ spin_lock_bh(&pmlmepriv->scanned_queue.lock);
+ ptarget_wlan =
+ rtw_find_network23a(&pmlmepriv->scanned_queue,
+ cur_network->network.MacAddress);
+ if (ptarget_wlan)
+ ptarget_wlan->fixed = true;
+ spin_unlock_bh(&pmlmepriv->scanned_queue.lock);
+ /* a sta + bc/mc_stainfo (not Ibss_stainfo) */
+ rtw_indicate_connect23a(adapter);
+ }
+ }
+
+ spin_unlock_bh(&pmlmepriv->lock);
+
+ mlmeext_sta_add_event_callback23a(adapter, psta);
+}
+
+void rtw_stadel_event_callback23a(struct rtw_adapter *adapter, const u8 *pbuf)
+{
+ int mac_id;
+ struct sta_info *psta;
+ struct wlan_network *pwlan;
+ struct wlan_bssid_ex *pdev_network;
+ struct mlme_priv *pmlmepriv = &adapter->mlmepriv;
+ struct stadel_event *pstadel = (struct stadel_event *)pbuf;
+ struct sta_priv *pstapriv = &adapter->stapriv;
+ struct wlan_network *tgt_network = &pmlmepriv->cur_network;
+
+ psta = rtw_get_stainfo23a(&adapter->stapriv, pstadel->macaddr);
+ if (psta)
+ mac_id = psta->mac_id;
+ else
+ mac_id = pstadel->mac_id;
+
+ DBG_8723A("%s(mac_id=%d)=%pM\n", __func__, mac_id, pstadel->macaddr);
+
+ if (check_fwstate(pmlmepriv, WIFI_AP_STATE))
+ return;
+
+ mlmeext_sta_del_event_callback23a(adapter);
+
+ spin_lock_bh(&pmlmepriv->lock);
+
+ if (check_fwstate(pmlmepriv, WIFI_STATION_STATE)) {
+ if (adapter->mlmepriv.to_roaming > 0) {
+ /* this stadel_event is caused by roaming,
+ decrease to_roaming */
+ pmlmepriv->to_roaming--;
+ } else if (adapter->mlmepriv.to_roaming == 0)
+ rtw_set_roaming(adapter, adapter->registrypriv.max_roaming_times);
+ if (*((u16 *)pstadel->rsvd) != WLAN_REASON_EXPIRATION_CHK)
+ rtw_set_roaming(adapter, 0); /* don't roam */
+
+ rtw_free_uc_swdec_pending_queue23a(adapter);
+
+ rtw_free_assoc_resources23a(adapter, 1);
+ rtw_indicate_disconnect23a(adapter);
+ spin_lock_bh(&pmlmepriv->scanned_queue.lock);
+ /* remove the network entry in scanned_queue */
+ pwlan = rtw_find_network23a(&pmlmepriv->scanned_queue,
+ tgt_network->network.MacAddress);
+ if (pwlan) {
+ pwlan->fixed = false;
+ rtw_free_network_nolock(pmlmepriv, pwlan);
+ }
+ spin_unlock_bh(&pmlmepriv->scanned_queue.lock);
+
+ _rtw_roaming(adapter, tgt_network);
+ }
+
+ if (check_fwstate(pmlmepriv, WIFI_ADHOC_MASTER_STATE) ||
+ check_fwstate(pmlmepriv, WIFI_ADHOC_STATE)) {
+
+ spin_lock_bh(&pstapriv->sta_hash_lock);
+ rtw_free_stainfo23a(adapter, psta);
+ spin_unlock_bh(&pstapriv->sta_hash_lock);
+
+ /* a sta + bc/mc_stainfo (not Ibss_stainfo) */
+ if (adapter->stapriv.asoc_sta_count == 1) {
+ spin_lock_bh(&pmlmepriv->scanned_queue.lock);
+ /* free old ibss network */
+ /* pwlan = rtw_find_network23a(
+ &pmlmepriv->scanned_queue, pstadel->macaddr); */
+ pwlan = rtw_find_network23a(&pmlmepriv->scanned_queue,
+ tgt_network->network.MacAddress);
+ if (pwlan) {
+ pwlan->fixed = false;
+ rtw_free_network_nolock(pmlmepriv, pwlan);
+ }
+ spin_unlock_bh(&pmlmepriv->scanned_queue.lock);
+ /* re-create ibss */
+ pdev_network = &adapter->registrypriv.dev_network;
+
+ memcpy(pdev_network, &tgt_network->network,
+ get_wlan_bssid_ex_sz(&tgt_network->network));
+
+ rtw_do_join_adhoc(adapter);
+ }
+ }
+
+ spin_unlock_bh(&pmlmepriv->lock);
+}
+
+/*
+* rtw23a_join_to_handler - Timeout/failure handler for CMD JoinBss
+* @adapter: pointer to _adapter structure
+*/
+void rtw23a_join_to_handler (unsigned long data)
+{
+ struct rtw_adapter *adapter = (struct rtw_adapter *)data;
+ struct mlme_priv *pmlmepriv = &adapter->mlmepriv;
+ int do_join_r;
+
+ DBG_8723A("%s, fw_state=%x\n", __func__, get_fwstate(pmlmepriv));
+
+ if (adapter->bDriverStopped || adapter->bSurpriseRemoved)
+ return;
+
+ spin_lock_bh(&pmlmepriv->lock);
+
+ if (adapter->mlmepriv.to_roaming > 0) {
+ /* join timeout caused by roaming */
+ while (1) {
+ pmlmepriv->to_roaming--;
+ if (adapter->mlmepriv.to_roaming != 0) {
+ /* try another */
+ DBG_8723A("%s try another roaming\n", __func__);
+ do_join_r = rtw_do_join(adapter);
+ if (do_join_r != _SUCCESS) {
+ DBG_8723A("%s roaming do_join return "
+ "%d\n", __func__ , do_join_r);
+ continue;
+ }
+ break;
+ } else {
+ DBG_8723A("%s We've try roaming but fail\n",
+ __func__);
+ rtw_indicate_disconnect23a(adapter);
+ break;
+ }
+ }
+ } else {
+ rtw_indicate_disconnect23a(adapter);
+ free_scanqueue(pmlmepriv);/* */
+
+ /* indicate disconnect for the case that join_timeout and
+ check_fwstate != FW_LINKED */
+ rtw_cfg80211_indicate_disconnect(adapter);
+ }
+
+ spin_unlock_bh(&pmlmepriv->lock);
+
+}
+
+/*
+* rtw_scan_timeout_handler23a - Timeout/Failure handler for CMD SiteSurvey
+* @data: pointer to _adapter structure
+*/
+void rtw_scan_timeout_handler23a(unsigned long data)
+{
+ struct rtw_adapter *adapter = (struct rtw_adapter *)data;
+ struct mlme_priv *pmlmepriv = &adapter->mlmepriv;
+
+ DBG_8723A("%s(%s): fw_state =%x\n", __func__, adapter->pnetdev->name,
+ get_fwstate(pmlmepriv));
+
+ spin_lock_bh(&pmlmepriv->lock);
+
+ _clr_fwstate_(pmlmepriv, _FW_UNDER_SURVEY);
+
+ spin_unlock_bh(&pmlmepriv->lock);
+
+ rtw_cfg80211_indicate_scan_done(wdev_to_priv(adapter->rtw_wdev), true);
+}
+
+void rtw_dynamic_check_timer_handler(unsigned long data)
+{
+ struct rtw_adapter *adapter = (struct rtw_adapter *)data;
+
+ if (adapter->hw_init_completed == false)
+ goto out;
+
+ if (adapter->bDriverStopped == true ||
+ adapter->bSurpriseRemoved == true)
+ goto out;
+
+ if (adapter->net_closed == true)
+ goto out;
+
+ rtw_dynamic_chk_wk_cmd23a(adapter);
+
+out:
+ mod_timer(&adapter->mlmepriv.dynamic_chk_timer,
+ jiffies + msecs_to_jiffies(2000));
+}
+
+inline bool rtw_is_scan_deny(struct rtw_adapter *adapter)
+{
+ struct mlme_priv *mlmepriv = &adapter->mlmepriv;
+
+ return (atomic_read(&mlmepriv->set_scan_deny) != 0) ? true : false;
+}
+
+void rtw_clear_scan_deny(struct rtw_adapter *adapter)
+{
+ struct mlme_priv *mlmepriv = &adapter->mlmepriv;
+
+ atomic_set(&mlmepriv->set_scan_deny, 0);
+}
+
+void rtw_set_scan_deny_timer_hdl(unsigned long data)
+{
+ struct rtw_adapter *adapter = (struct rtw_adapter *)data;
+
+ rtw_clear_scan_deny(adapter);
+}
+
+void rtw_set_scan_deny(struct rtw_adapter *adapter, u32 ms)
+{
+ struct mlme_priv *mlmepriv = &adapter->mlmepriv;
+
+ atomic_set(&mlmepriv->set_scan_deny, 1);
+ mod_timer(&mlmepriv->set_scan_deny_timer,
+ jiffies + msecs_to_jiffies(ms));
+}
+
+#if defined(IEEE80211_SCAN_RESULT_EXPIRE)
+#define RTW_SCAN_RESULT_EXPIRE \
+ ((IEEE80211_SCAN_RESULT_EXPIRE / (HZ*1000)) - 1000) /* 3000 -1000 */
+#else
+#define RTW_SCAN_RESULT_EXPIRE 2000
+#endif
+
+/*
+* Select a new join candidate from the original @param candidate and
+* @param competitor
+* @return true: candidate is updated
+* @return false: candidate is not updated
+*/
+static int rtw_check_join_candidate(struct mlme_priv *pmlmepriv,
+ struct wlan_network **candidate,
+ struct wlan_network *competitor)
+{
+ int updated = false;
+ struct rtw_adapter *adapter;
+
+ adapter = container_of(pmlmepriv, struct rtw_adapter, mlmepriv);
+
+ /* check bssid, if needed */
+ if (pmlmepriv->assoc_by_bssid == true) {
+ if (!ether_addr_equal(competitor->network.MacAddress,
+ pmlmepriv->assoc_bssid))
+ goto exit;
+ }
+
+ /* check ssid, if needed */
+ if (pmlmepriv->assoc_ssid.ssid_len) {
+ if (competitor->network.Ssid.ssid_len !=
+ pmlmepriv->assoc_ssid.ssid_len ||
+ memcmp(competitor->network.Ssid.ssid,
+ pmlmepriv->assoc_ssid.ssid,
+ pmlmepriv->assoc_ssid.ssid_len))
+ goto exit;
+ }
+
+ if (rtw_is_desired_network(adapter, competitor) == false)
+ goto exit;
+
+ if (adapter->mlmepriv.to_roaming > 0) {
+ unsigned int passed;
+
+ passed = jiffies_to_msecs(jiffies - competitor->last_scanned);
+ if (passed >= RTW_SCAN_RESULT_EXPIRE ||
+ is_same_ess(&competitor->network,
+ &pmlmepriv->cur_network.network) == false)
+ goto exit;
+ }
+
+ if (!*candidate ||
+ (*candidate)->network.Rssi<competitor->network.Rssi) {
+ *candidate = competitor;
+ updated = true;
+ }
+
+ if (updated) {
+ DBG_8723A("[by_bssid:%u][assoc_ssid:%s][to_roaming:%u] new candidate: %s(%pM) rssi:%d\n",
+ pmlmepriv->assoc_by_bssid,
+ pmlmepriv->assoc_ssid.ssid,
+ adapter->mlmepriv.to_roaming,
+ (*candidate)->network.Ssid.ssid,
+ (*candidate)->network.MacAddress,
+ (int)(*candidate)->network.Rssi);
+ }
+
+exit:
+ return updated;
+}
+
+/*
+Calling context:
+The caller of the sub-routine will be in critical section...
+
+The caller must hold the following spinlock
+
+pmlmepriv->lock
+
+*/
+
+static int rtw_do_join(struct rtw_adapter *padapter)
+{
+ struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
+ int ret;
+
+ pmlmepriv->cur_network.join_res = -2;
+
+ set_fwstate(pmlmepriv, _FW_UNDER_LINKING);
+
+ pmlmepriv->to_join = true;
+
+ ret = rtw_select_and_join_from_scanned_queue23a(pmlmepriv);
+ if (ret == _SUCCESS) {
+ pmlmepriv->to_join = false;
+ } else {
+ if (check_fwstate(pmlmepriv, WIFI_ADHOC_STATE)) {
+ /* switch to ADHOC_MASTER */
+ ret = rtw_do_join_adhoc(padapter);
+ if (ret != _SUCCESS)
+ goto exit;
+ } else {
+ /* can't associate ; reset under-linking */
+ _clr_fwstate_(pmlmepriv, _FW_UNDER_LINKING);
+
+ ret = _FAIL;
+ pmlmepriv->to_join = false;
+ }
+ }
+
+exit:
+ return ret;
+}
+
+static struct wlan_network *
+rtw_select_candidate_from_queue(struct mlme_priv *pmlmepriv)
+{
+ struct wlan_network *pnetwork, *candidate = NULL;
+ struct rtw_queue *queue = &pmlmepriv->scanned_queue;
+ struct list_head *phead, *plist, *ptmp;
+
+ spin_lock_bh(&pmlmepriv->scanned_queue.lock);
+ phead = get_list_head(queue);
+
+ list_for_each_safe(plist, ptmp, phead) {
+ pnetwork = container_of(plist, struct wlan_network, list);
+ if (!pnetwork) {
+ RT_TRACE(_module_rtl871x_mlme_c_, _drv_err_,
+ "%s: return _FAIL:(pnetwork == NULL)\n",
+ __func__);
+ goto exit;
+ }
+
+ rtw_check_join_candidate(pmlmepriv, &candidate, pnetwork);
+ }
+
+exit:
+ spin_unlock_bh(&pmlmepriv->scanned_queue.lock);
+ return candidate;
+}
+
+
+int rtw_do_join_adhoc(struct rtw_adapter *adapter)
+{
+ struct mlme_priv *pmlmepriv = &adapter->mlmepriv;
+ struct wlan_bssid_ex *pdev_network;
+ u8 *ibss;
+ int ret;
+
+ pdev_network = &adapter->registrypriv.dev_network;
+ ibss = adapter->registrypriv.dev_network.MacAddress;
+
+ _clr_fwstate_(pmlmepriv, _FW_UNDER_SURVEY);
+
+ RT_TRACE(_module_rtl871x_mlme_c_, _drv_err_,
+ "switching to adhoc master\n");
+
+ memcpy(&pdev_network->Ssid, &pmlmepriv->assoc_ssid,
+ sizeof(struct cfg80211_ssid));
+
+ rtw_update_registrypriv_dev_network23a(adapter);
+ rtw_generate_random_ibss23a(ibss);
+
+ pmlmepriv->fw_state = WIFI_ADHOC_MASTER_STATE;
+
+ ret = rtw_createbss_cmd23a(adapter);
+ if (ret != _SUCCESS) {
+ RT_TRACE(_module_rtl871x_mlme_c_, _drv_err_,
+ "Error =>rtw_createbss_cmd23a status FAIL\n");
+ } else {
+ pmlmepriv->to_join = false;
+ }
+
+ return ret;
+}
+
+int rtw_do_join_network(struct rtw_adapter *adapter,
+ struct wlan_network *candidate)
+{
+ int ret;
+
+ /* check for situation of _FW_LINKED */
+ if (check_fwstate(&adapter->mlmepriv, _FW_LINKED)) {
+ DBG_8723A("%s: _FW_LINKED while ask_for_joinbss!\n", __func__);
+
+ rtw_disassoc_cmd23a(adapter, 0, true);
+ rtw_indicate_disconnect23a(adapter);
+ rtw_free_assoc_resources23a(adapter, 0);
+ }
+ set_fwstate(&adapter->mlmepriv, _FW_UNDER_LINKING);
+
+ ret = rtw_joinbss_cmd23a(adapter, candidate);
+
+ if (ret == _SUCCESS)
+ mod_timer(&adapter->mlmepriv.assoc_timer,
+ jiffies + msecs_to_jiffies(MAX_JOIN_TIMEOUT));
+
+ return ret;
+}
+
+int rtw_select_and_join_from_scanned_queue23a(struct mlme_priv *pmlmepriv)
+{
+ struct rtw_adapter *adapter;
+ struct wlan_network *candidate = NULL;
+ int ret;
+
+ adapter = pmlmepriv->nic_hdl;
+
+ candidate = rtw_select_candidate_from_queue(pmlmepriv);
+ if (!candidate) {
+ DBG_8723A("%s: return _FAIL(candidate == NULL)\n", __func__);
+ ret = _FAIL;
+ goto exit;
+ } else {
+ DBG_8723A("%s: candidate: %s(%pM, ch:%u)\n",
+ __func__,
+ candidate->network.Ssid.ssid,
+ candidate->network.MacAddress,
+ candidate->network.DSConfig);
+ }
+
+ ret = rtw_do_join_network(adapter, candidate);
+
+exit:
+ return ret;
+}
+
+int rtw_set_auth23a(struct rtw_adapter *adapter,
+ struct security_priv *psecuritypriv)
+{
+ struct cmd_obj *pcmd;
+ struct setauth_parm *psetauthparm;
+ struct cmd_priv *pcmdpriv = &adapter->cmdpriv;
+ int res = _SUCCESS;
+
+ pcmd = kzalloc(sizeof(struct cmd_obj), GFP_KERNEL);
+ if (!pcmd) {
+ res = _FAIL; /* try again */
+ goto exit;
+ }
+
+ psetauthparm = kzalloc(sizeof(struct setauth_parm), GFP_KERNEL);
+ if (!psetauthparm) {
+ kfree(pcmd);
+ res = _FAIL;
+ goto exit;
+ }
+
+ psetauthparm->mode = (unsigned char)psecuritypriv->dot11AuthAlgrthm;
+
+ pcmd->cmdcode = _SetAuth_CMD_;
+ pcmd->parmbuf = (unsigned char *)psetauthparm;
+ pcmd->cmdsz = (sizeof(struct setauth_parm));
+ pcmd->rsp = NULL;
+ pcmd->rspsz = 0;
+
+ RT_TRACE(_module_rtl871x_mlme_c_, _drv_err_,
+ "after enqueue set_auth_cmd, auth_mode=%x\n",
+ psecuritypriv->dot11AuthAlgrthm);
+
+ res = rtw_enqueue_cmd23a(pcmdpriv, pcmd);
+
+exit:
+
+ return res;
+}
+
+int rtw_set_key23a(struct rtw_adapter *adapter,
+ struct security_priv *psecuritypriv, int keyid, u8 set_tx)
+{
+ u8 keylen;
+ struct cmd_obj *pcmd;
+ struct setkey_parm *psetkeyparm;
+ struct cmd_priv *pcmdpriv = &adapter->cmdpriv;
+ struct mlme_priv *pmlmepriv = &adapter->mlmepriv;
+ int res = _SUCCESS;
+
+ if (keyid >= 4) {
+ res = _FAIL;
+ goto exit;
+ }
+
+ pcmd = kzalloc(sizeof(struct cmd_obj), GFP_KERNEL);
+ if (!pcmd) {
+ res = _FAIL; /* try again */
+ goto exit;
+ }
+ psetkeyparm = kzalloc(sizeof(struct setkey_parm), GFP_KERNEL);
+ if (!psetkeyparm) {
+ kfree(pcmd);
+ res = _FAIL;
+ goto exit;
+ }
+
+ if (psecuritypriv->dot11AuthAlgrthm == dot11AuthAlgrthm_8021X) {
+ psetkeyparm->algorithm = (unsigned char)
+ psecuritypriv->dot118021XGrpPrivacy;
+ RT_TRACE(_module_rtl871x_mlme_c_, _drv_err_,
+ "rtw_set_key23a: psetkeyparm->algorithm = (unsigned char)psecuritypriv->dot118021XGrpPrivacy =%d\n",
+ psetkeyparm->algorithm);
+ } else {
+ psetkeyparm->algorithm = (u8)psecuritypriv->dot11PrivacyAlgrthm;
+ RT_TRACE(_module_rtl871x_mlme_c_, _drv_err_,
+ "rtw_set_key23a: psetkeyparm->algorithm = (u8)psecuritypriv->dot11PrivacyAlgrthm =%d\n",
+ psetkeyparm->algorithm);
+ }
+ psetkeyparm->keyid = keyid;/* 0~3 */
+ psetkeyparm->set_tx = set_tx;
+ if (is_wep_enc(psetkeyparm->algorithm))
+ pmlmepriv->key_mask |= BIT(psetkeyparm->keyid);
+
+ DBG_8723A("==> rtw_set_key23a algorithm(%x), keyid(%x), key_mask(%x)\n",
+ psetkeyparm->algorithm, psetkeyparm->keyid,
+ pmlmepriv->key_mask);
+ RT_TRACE(_module_rtl871x_mlme_c_, _drv_err_,
+ "rtw_set_key23a: psetkeyparm->algorithm =%d psetkeyparm->keyid = (u8)keyid =%d\n",
+ psetkeyparm->algorithm, keyid);
+
+ switch (psetkeyparm->algorithm) {
+ case WLAN_CIPHER_SUITE_WEP40:
+ keylen = 5;
+ memcpy(&psetkeyparm->key[0],
+ &psecuritypriv->wep_key[keyid].key, keylen);
+ break;
+ case WLAN_CIPHER_SUITE_WEP104:
+ keylen = 13;
+ memcpy(&psetkeyparm->key[0],
+ &psecuritypriv->wep_key[keyid].key, keylen);
+ break;
+ case WLAN_CIPHER_SUITE_TKIP:
+ keylen = 16;
+ memcpy(&psetkeyparm->key,
+ &psecuritypriv->dot118021XGrpKey[keyid], keylen);
+ psetkeyparm->grpkey = 1;
+ break;
+ case WLAN_CIPHER_SUITE_CCMP:
+ keylen = 16;
+ memcpy(&psetkeyparm->key,
+ &psecuritypriv->dot118021XGrpKey[keyid], keylen);
+ psetkeyparm->grpkey = 1;
+ break;
+ default:
+ RT_TRACE(_module_rtl871x_mlme_c_, _drv_err_,
+ "rtw_set_key23a:psecuritypriv->dot11PrivacyAlgrthm = %x (must be 1 or 2 or 4 or 5)\n",
+ psecuritypriv->dot11PrivacyAlgrthm);
+ res = _FAIL;
+ kfree(pcmd);
+ kfree(psetkeyparm);
+ goto exit;
+ }
+
+ pcmd->cmdcode = _SetKey_CMD_;
+ pcmd->parmbuf = (u8 *)psetkeyparm;
+ pcmd->cmdsz = (sizeof(struct setkey_parm));
+ pcmd->rsp = NULL;
+ pcmd->rspsz = 0;
+
+ /* sema_init(&pcmd->cmd_sem, 0); */
+
+ res = rtw_enqueue_cmd23a(pcmdpriv, pcmd);
+
+exit:
+
+ return res;
+}
+
+/* adjust IEs for rtw_joinbss_cmd23a in WMM */
+int rtw_restruct_wmm_ie23a(struct rtw_adapter *adapter, u8 *in_ie,
+ u8 *out_ie, uint in_len, uint initial_out_len)
+{
+ int ielength;
+ const u8 *p;
+
+ ielength = initial_out_len;
+
+ p = cfg80211_find_vendor_ie(WLAN_OUI_MICROSOFT,
+ WLAN_OUI_TYPE_MICROSOFT_WMM,
+ in_ie, in_len);
+
+ if (p && p[1]) {
+ memcpy(out_ie + initial_out_len, p, 9);
+
+ out_ie[initial_out_len + 1] = 7;
+ out_ie[initial_out_len + 6] = 0;
+ out_ie[initial_out_len + 8] = 0;
+
+ ielength += 9;
+ }
+
+ return ielength;
+}
+
+/* */
+/* Ported from 8185: IsInPreAuthKeyList().
+ (Renamed from SecIsInPreAuthKeyList(), 2006-10-13.) */
+/* Added by Annie, 2006-05-07. */
+/* */
+/* Search by BSSID, */
+/* Return Value: */
+/* -1 :if there is no pre-auth key in the table */
+/* >= 0 :if there is pre-auth key, and return the entry id */
+/* */
+/* */
+
+static int SecIsInPMKIDList(struct rtw_adapter *Adapter, u8 *bssid)
+{
+ struct security_priv *psecuritypriv = &Adapter->securitypriv;
+ int i = 0;
+
+ do {
+ if (psecuritypriv->PMKIDList[i].bUsed &&
+ ether_addr_equal(psecuritypriv->PMKIDList[i].Bssid, bssid)) {
+ break;
+ } else {
+ i++;
+ /* continue; */
+ }
+ } while (i < NUM_PMKID_CACHE);
+
+ if (i == NUM_PMKID_CACHE)
+ i = -1;/* Could not find. */
+ else {
+ /* There is one Pre-Authentication Key for
+ the specific BSSID. */
+ }
+
+ return i;
+}
+
+/* */
+/* Check the RSN IE length */
+/* If the RSN IE length <= 20, the RSN IE didn't include
+ the PMKID information */
+/* 0-11th element in the array are the fixed IE */
+/* 12th element in the array is the IE */
+/* 13th element in the array is the IE length */
+/* */
+
+static int rtw_append_pmkid(struct rtw_adapter *Adapter, int iEntry,
+ u8 *ie, uint ie_len)
+{
+ struct security_priv *psecuritypriv = &Adapter->securitypriv;
+
+ if (ie[1] <= 20) {
+ /* The RSN IE didn't include the PMK ID,
+ append the PMK information */
+ ie[ie_len] = 1;
+ ie_len++;
+ ie[ie_len] = 0; /* PMKID count = 0x0100 */
+ ie_len++;
+ memcpy(&ie[ie_len],
+ &psecuritypriv->PMKIDList[iEntry].PMKID, 16);
+
+ ie_len += 16;
+ ie[1] += 18;/* PMKID length = 2+16 */
+ }
+ return ie_len;
+}
+
+int rtw_restruct_sec_ie23a(struct rtw_adapter *adapter, u8 *in_ie, u8 *out_ie,
+ uint in_len)
+{
+ u8 authmode;
+ uint ielength;
+ int iEntry;
+ struct mlme_priv *pmlmepriv = &adapter->mlmepriv;
+ struct security_priv *psecuritypriv = &adapter->securitypriv;
+ uint ndisauthmode = psecuritypriv->ndisauthtype;
+ uint ndissecuritytype = psecuritypriv->ndisencryptstatus;
+
+ RT_TRACE(_module_rtl871x_mlme_c_, _drv_notice_,
+ "+rtw_restruct_sec_ie23a: ndisauthmode=%d ndissecuritytype=%d\n",
+ ndisauthmode, ndissecuritytype);
+
+ ielength = 0;
+ if (ndisauthmode == Ndis802_11AuthModeWPA ||
+ ndisauthmode == Ndis802_11AuthModeWPAPSK)
+ authmode = WLAN_EID_VENDOR_SPECIFIC;
+ if (ndisauthmode == Ndis802_11AuthModeWPA2 ||
+ ndisauthmode == Ndis802_11AuthModeWPA2PSK)
+ authmode = WLAN_EID_RSN;
+
+ if (check_fwstate(pmlmepriv, WIFI_UNDER_WPS)) {
+ memcpy(out_ie + ielength, psecuritypriv->wps_ie,
+ psecuritypriv->wps_ie_len);
+
+ ielength += psecuritypriv->wps_ie_len;
+ } else if (authmode == WLAN_EID_VENDOR_SPECIFIC ||
+ authmode == WLAN_EID_RSN) {
+ /* copy RSN or SSN */
+ memcpy(&out_ie[ielength], &psecuritypriv->supplicant_ie[0],
+ psecuritypriv->supplicant_ie[1] + 2);
+ ielength += psecuritypriv->supplicant_ie[1] + 2;
+ }
+
+ iEntry = SecIsInPMKIDList(adapter, pmlmepriv->assoc_bssid);
+ if (iEntry < 0)
+ return ielength;
+ else {
+ if (authmode == WLAN_EID_RSN)
+ ielength = rtw_append_pmkid(adapter, iEntry,
+ out_ie, ielength);
+ }
+
+ return ielength;
+}
+
+void rtw_init_registrypriv_dev_network23a(struct rtw_adapter *adapter)
+{
+ struct registry_priv *pregistrypriv = &adapter->registrypriv;
+ struct eeprom_priv *peepriv = &adapter->eeprompriv;
+ struct wlan_bssid_ex *pdev_network = &pregistrypriv->dev_network;
+ u8 *myhwaddr = myid(peepriv);
+
+ ether_addr_copy(pdev_network->MacAddress, myhwaddr);
+
+ memcpy(&pdev_network->Ssid, &pregistrypriv->ssid,
+ sizeof(struct cfg80211_ssid));
+
+ pdev_network->beacon_interval = 100;
+}
+
+void rtw_update_registrypriv_dev_network23a(struct rtw_adapter *adapter)
+{
+ int sz = 0;
+ struct registry_priv *pregistrypriv = &adapter->registrypriv;
+ struct wlan_bssid_ex *pdev_network = &pregistrypriv->dev_network;
+ struct security_priv *psecuritypriv = &adapter->securitypriv;
+ struct wlan_network *cur_network = &adapter->mlmepriv.cur_network;
+ /* struct xmit_priv *pxmitpriv = &adapter->xmitpriv; */
+
+ pdev_network->Privacy =
+ (psecuritypriv->dot11PrivacyAlgrthm > 0 ? 1 : 0);
+
+ pdev_network->Rssi = 0;
+
+ pdev_network->DSConfig = pregistrypriv->channel;
+ RT_TRACE(_module_rtl871x_mlme_c_, _drv_info_,
+ "pregistrypriv->channel =%d, pdev_network->DSConfig = 0x%x\n",
+ pregistrypriv->channel, pdev_network->DSConfig);
+
+ if (cur_network->network.ifmode == NL80211_IFTYPE_ADHOC)
+ pdev_network->ATIMWindow = 0;
+
+ pdev_network->ifmode = cur_network->network.ifmode;
+
+ /* 1. Supported rates */
+ /* 2. IE */
+
+ sz = rtw_generate_ie23a(pregistrypriv);
+
+ pdev_network->IELength = sz;
+
+ pdev_network->Length =
+ get_wlan_bssid_ex_sz(pdev_network);
+
+ /* notes: translate IELength & Length after assign the
+ Length to cmdsz in createbss_cmd(); */
+ /* pdev_network->IELength = cpu_to_le32(sz); */
+}
+
+/* the function is at passive_level */
+void rtw_joinbss_reset23a(struct rtw_adapter *padapter)
+{
+ u8 threshold;
+ struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
+ struct ht_priv *phtpriv = &pmlmepriv->htpriv;
+
+ /* todo: if you want to do something io/reg/hw setting
+ before join_bss, please add code here */
+
+ pmlmepriv->num_FortyMHzIntolerant = 0;
+
+ pmlmepriv->num_sta_no_ht = 0;
+
+ phtpriv->ampdu_enable = false;/* reset to disabled */
+
+ /* TH = 1 => means that invalidate usb rx aggregation */
+ /* TH = 0 => means that validate usb rx aggregation, use init value. */
+ if (phtpriv->ht_option) {
+ if (padapter->registrypriv.wifi_spec == 1)
+ threshold = 1;
+ else
+ threshold = 0;
+ } else
+ threshold = 1;
+
+ rtl8723a_set_rxdma_agg_pg_th(padapter, threshold);
+}
+
+/* the function is >= passive_level */
+bool rtw_restructure_ht_ie23a(struct rtw_adapter *padapter, u8 *in_ie,
+ u8 *out_ie, uint in_len, uint *pout_len)
+{
+ u32 out_len;
+ int max_rx_ampdu_factor;
+ unsigned char *pframe;
+ const u8 *p;
+ struct ieee80211_ht_cap ht_capie;
+ u8 WMM_IE[7] = {0x00, 0x50, 0xf2, 0x02, 0x00, 0x01, 0x00};
+ struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
+ struct ht_priv *phtpriv = &pmlmepriv->htpriv;
+
+ phtpriv->ht_option = false;
+
+ p = cfg80211_find_ie(WLAN_EID_HT_CAPABILITY, in_ie, in_len);
+
+ if (p && p[1] > 0) {
+ u32 rx_packet_offset, max_recvbuf_sz;
+
+ if (pmlmepriv->qos_option == 0) {
+ out_len = *pout_len;
+ pframe = rtw_set_ie23a(out_ie + out_len,
+ WLAN_EID_VENDOR_SPECIFIC,
+ sizeof(WMM_IE), WMM_IE,
+ pout_len);
+
+ pmlmepriv->qos_option = 1;
+ }
+
+ out_len = *pout_len;
+
+ memset(&ht_capie, 0, sizeof(struct ieee80211_ht_cap));
+
+ ht_capie.cap_info = cpu_to_le16(IEEE80211_HT_CAP_SUP_WIDTH_20_40 |
+ IEEE80211_HT_CAP_SGI_20 | IEEE80211_HT_CAP_SGI_40 |
+ IEEE80211_HT_CAP_TX_STBC | IEEE80211_HT_CAP_DSSSCCK40);
+
+ GetHalDefVar8192CUsb(padapter, HAL_DEF_RX_PACKET_OFFSET,
+ &rx_packet_offset);
+ GetHalDefVar8192CUsb(padapter, HAL_DEF_MAX_RECVBUF_SZ,
+ &max_recvbuf_sz);
+
+ GetHalDefVar8192CUsb(padapter, HW_VAR_MAX_RX_AMPDU_FACTOR,
+ &max_rx_ampdu_factor);
+ ht_capie.ampdu_params_info = max_rx_ampdu_factor & 0x03;
+
+ if (padapter->securitypriv.dot11PrivacyAlgrthm ==
+ WLAN_CIPHER_SUITE_CCMP)
+ ht_capie.ampdu_params_info |=
+ (IEEE80211_HT_AMPDU_PARM_DENSITY& (0x07 << 2));
+ else
+ ht_capie.ampdu_params_info |=
+ (IEEE80211_HT_AMPDU_PARM_DENSITY & 0x00);
+
+ pframe = rtw_set_ie23a(out_ie + out_len, WLAN_EID_HT_CAPABILITY,
+ sizeof(struct ieee80211_ht_cap),
+ (unsigned char *)&ht_capie, pout_len);
+
+ phtpriv->ht_option = true;
+
+ p = cfg80211_find_ie(WLAN_EID_HT_OPERATION, in_ie, in_len);
+ if (p && (p[1] == sizeof(struct ieee80211_ht_operation))) {
+ out_len = *pout_len;
+ pframe = rtw_set_ie23a(out_ie + out_len,
+ WLAN_EID_HT_OPERATION,
+ p[1], p + 2 , pout_len);
+ }
+ }
+
+ return phtpriv->ht_option;
+}
+
+/* the function is > passive_level (in critical_section) */
+void rtw_update_ht_cap23a(struct rtw_adapter *padapter, u8 *pie, uint ie_len)
+{
+ u8 max_ampdu_sz;
+ const u8 *p;
+ struct ieee80211_ht_cap *pht_capie;
+ struct ieee80211_ht_operation *pht_addtinfo;
+ struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
+ struct ht_priv *phtpriv = &pmlmepriv->htpriv;
+ struct registry_priv *pregistrypriv = &padapter->registrypriv;
+ struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv;
+ struct mlme_ext_info *pmlmeinfo = &pmlmeext->mlmext_info;
+
+ if (!phtpriv->ht_option)
+ return;
+
+ if ((!pmlmeinfo->HT_info_enable) || (!pmlmeinfo->HT_caps_enable))
+ return;
+
+ DBG_8723A("+rtw_update_ht_cap23a()\n");
+
+ /* maybe needs check if ap supports rx ampdu. */
+ if (!phtpriv->ampdu_enable && pregistrypriv->ampdu_enable == 1) {
+ if (pregistrypriv->wifi_spec == 1)
+ phtpriv->ampdu_enable = false;
+ else
+ phtpriv->ampdu_enable = true;
+ } else if (pregistrypriv->ampdu_enable == 2)
+ phtpriv->ampdu_enable = true;
+
+ /* check Max Rx A-MPDU Size */
+ p = cfg80211_find_ie(WLAN_EID_HT_CAPABILITY, pie, ie_len);
+
+ if (p && p[1] > 0) {
+ pht_capie = (struct ieee80211_ht_cap *)(p + 2);
+ max_ampdu_sz = pht_capie->ampdu_params_info &
+ IEEE80211_HT_AMPDU_PARM_FACTOR;
+ /* max_ampdu_sz (kbytes); */
+ max_ampdu_sz = 1 << (max_ampdu_sz + 3);
+
+ phtpriv->rx_ampdu_maxlen = max_ampdu_sz;
+ }
+
+ p = cfg80211_find_ie(WLAN_EID_HT_OPERATION, pie, ie_len);
+ if (p && p[1] > 0) {
+ pht_addtinfo = (struct ieee80211_ht_operation *)(p + 2);
+ /* todo: */
+ }
+
+ /* update cur_bwmode & cur_ch_offset */
+ if (pregistrypriv->cbw40_enable &&
+ pmlmeinfo->ht_cap.cap_info &
+ cpu_to_le16(IEEE80211_HT_CAP_SUP_WIDTH_20_40) &&
+ pmlmeinfo->HT_info.ht_param & IEEE80211_HT_PARAM_CHAN_WIDTH_ANY) {
+ int i;
+ u8 rf_type;
+
+ rf_type = rtl8723a_get_rf_type(padapter);
+
+ /* update the MCS rates */
+ for (i = 0; i < IEEE80211_HT_MCS_MASK_LEN; i++) {
+ if (rf_type == RF_1T1R || rf_type == RF_1T2R)
+ pmlmeinfo->ht_cap.mcs.rx_mask[i] &=
+ MCS_rate_1R23A[i];
+ else
+ pmlmeinfo->ht_cap.mcs.rx_mask[i] &=
+ MCS_rate_2R23A[i];
+ }
+ /* switch to the 40M Hz mode according to the AP */
+ pmlmeext->cur_bwmode = HT_CHANNEL_WIDTH_40;
+ switch (pmlmeinfo->HT_info.ht_param &
+ IEEE80211_HT_PARAM_CHA_SEC_OFFSET) {
+ case IEEE80211_HT_PARAM_CHA_SEC_ABOVE:
+ pmlmeext->cur_ch_offset = HAL_PRIME_CHNL_OFFSET_LOWER;
+ break;
+
+ case IEEE80211_HT_PARAM_CHA_SEC_BELOW:
+ pmlmeext->cur_ch_offset = HAL_PRIME_CHNL_OFFSET_UPPER;
+ break;
+
+ default:
+ pmlmeext->cur_ch_offset =
+ HAL_PRIME_CHNL_OFFSET_DONT_CARE;
+ break;
+ }
+ }
+
+ /* */
+ /* Config SM Power Save setting */
+ /* */
+ pmlmeinfo->SM_PS =
+ (le16_to_cpu(pmlmeinfo->ht_cap.cap_info) &
+ IEEE80211_HT_CAP_SM_PS) >> IEEE80211_HT_CAP_SM_PS_SHIFT;
+ if (pmlmeinfo->SM_PS == WLAN_HT_CAP_SM_PS_STATIC)
+ DBG_8723A("%s(): WLAN_HT_CAP_SM_PS_STATIC\n", __func__);
+
+ /* */
+ /* Config current HT Protection mode. */
+ /* */
+ pmlmeinfo->HT_protection =
+ le16_to_cpu(pmlmeinfo->HT_info.operation_mode) &
+ IEEE80211_HT_OP_MODE_PROTECTION;
+}
+
+void rtw_issue_addbareq_cmd23a(struct rtw_adapter *padapter,
+ struct xmit_frame *pxmitframe)
+{
+ u8 issued;
+ int priority;
+ struct sta_info *psta;
+ struct ht_priv *phtpriv;
+ struct pkt_attrib *pattrib = &pxmitframe->attrib;
+ s32 bmcst = is_multicast_ether_addr(pattrib->ra);
+
+ if (bmcst || padapter->mlmepriv.LinkDetectInfo.NumTxOkInPeriod < 100)
+ return;
+
+ priority = pattrib->priority;
+
+ if (pattrib->psta)
+ psta = pattrib->psta;
+ else {
+ DBG_8723A("%s, call rtw_get_stainfo23a()\n", __func__);
+ psta = rtw_get_stainfo23a(&padapter->stapriv, pattrib->ra);
+ }
+
+ if (!psta) {
+ DBG_8723A("%s, psta == NUL\n", __func__);
+ return;
+ }
+
+ if (!(psta->state &_FW_LINKED)) {
+ DBG_8723A("%s, psta->state(0x%x) != _FW_LINKED\n",
+ __func__, psta->state);
+ return;
+ }
+
+ phtpriv = &psta->htpriv;
+
+ if (phtpriv->ht_option && phtpriv->ampdu_enable) {
+ issued = (phtpriv->agg_enable_bitmap>>priority)&0x1;
+ issued |= (phtpriv->candidate_tid_bitmap>>priority)&0x1;
+
+ if (issued == 0) {
+ DBG_8723A("rtw_issue_addbareq_cmd23a, p =%d\n",
+ priority);
+ psta->htpriv.candidate_tid_bitmap |= BIT(priority);
+ rtw_addbareq_cmd23a(padapter, (u8) priority,
+ pattrib->ra);
+ }
+ }
+}
+
+int rtw_linked_check(struct rtw_adapter *padapter)
+{
+ if (check_fwstate(&padapter->mlmepriv, WIFI_AP_STATE) ||
+ check_fwstate(&padapter->mlmepriv,
+ WIFI_ADHOC_STATE|WIFI_ADHOC_MASTER_STATE)) {
+ if (padapter->stapriv.asoc_sta_count > 2)
+ return true;
+ } else { /* Station mode */
+ if (check_fwstate(&padapter->mlmepriv, _FW_LINKED))
+ return true;
+ }
+ return false;
+}
diff --git a/drivers/staging/rtl8723au/core/rtw_mlme_ext.c b/drivers/staging/rtl8723au/core/rtw_mlme_ext.c
new file mode 100644
index 000000000..196beafde
--- /dev/null
+++ b/drivers/staging/rtl8723au/core/rtw_mlme_ext.c
@@ -0,0 +1,6224 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2012 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ ******************************************************************************/
+#define _RTW_MLME_EXT_C_
+
+#include <osdep_service.h>
+#include <drv_types.h>
+#include <wifi.h>
+#include <rtw_mlme_ext.h>
+#include <wlan_bssdef.h>
+#include <mlme_osdep.h>
+#include <recv_osdep.h>
+#include <linux/ieee80211.h>
+#include <rtl8723a_hal.h>
+
+static int OnAssocReq23a(struct rtw_adapter *padapter, struct recv_frame *precv_frame);
+static int OnAssocRsp23a(struct rtw_adapter *padapter, struct recv_frame *precv_frame);
+static int OnProbeReq23a(struct rtw_adapter *padapter, struct recv_frame *precv_frame);
+static int OnProbeRsp23a(struct rtw_adapter *padapter, struct recv_frame *precv_frame);
+static int DoReserved23a(struct rtw_adapter *padapter, struct recv_frame *precv_frame);
+static int OnBeacon23a(struct rtw_adapter *padapter, struct recv_frame *precv_frame);
+static int OnAtim23a(struct rtw_adapter *padapter, struct recv_frame *precv_frame);
+static int OnDisassoc23a(struct rtw_adapter *padapter, struct recv_frame *precv_frame);
+static int OnAuth23aClient23a(struct rtw_adapter *padapter, struct recv_frame *precv_frame);
+static int OnDeAuth23a(struct rtw_adapter *padapter, struct recv_frame *precv_frame);
+static int OnAction23a(struct rtw_adapter *padapter, struct recv_frame *precv_frame);
+
+static int on_action_spct23a(struct rtw_adapter *padapter, struct recv_frame *precv_frame);
+static int OnAction23a_qos(struct rtw_adapter *padapter, struct recv_frame *precv_frame);
+static int OnAction23a_dls(struct rtw_adapter *padapter, struct recv_frame *precv_frame);
+static int OnAction23a_back23a(struct rtw_adapter *padapter, struct recv_frame *precv_frame);
+static int on_action_public23a(struct rtw_adapter *padapter, struct recv_frame *precv_frame);
+static int OnAction23a_ht(struct rtw_adapter *padapter, struct recv_frame *precv_frame);
+static int OnAction23a_wmm(struct rtw_adapter *padapter, struct recv_frame *precv_frame);
+static int OnAction23a_p2p(struct rtw_adapter *padapter, struct recv_frame *precv_frame);
+
+static void issue_assocreq(struct rtw_adapter *padapter);
+static void issue_probereq(struct rtw_adapter *padapter,
+ struct cfg80211_ssid *pssid, u8 *da);
+static int issue_probereq_ex(struct rtw_adapter *padapter,
+ struct cfg80211_ssid *pssid,
+ u8 *da, int try_cnt, int wait_ms);
+static void issue_probersp(struct rtw_adapter *padapter, unsigned char *da,
+ u8 is_valid_p2p_probereq);
+static void issue_auth(struct rtw_adapter *padapter, struct sta_info *psta,
+ unsigned short status);
+static int issue_deauth_ex(struct rtw_adapter *padapter, u8 *da,
+ unsigned short reason, int try_cnt, int wait_ms);
+static void start_clnt_assoc(struct rtw_adapter *padapter);
+static void start_clnt_auth(struct rtw_adapter *padapter);
+static void start_clnt_join(struct rtw_adapter *padapter);
+static void start_create_ibss(struct rtw_adapter *padapter);
+static struct wlan_bssid_ex *collect_bss_info(struct rtw_adapter *padapter,
+ struct recv_frame *precv_frame);
+
+#ifdef CONFIG_8723AU_AP_MODE
+static int OnAuth23a(struct rtw_adapter *padapter, struct recv_frame *precv_frame);
+static void issue_assocrsp(struct rtw_adapter *padapter, unsigned short status,
+ struct sta_info *pstat, u16 pkt_type);
+#endif
+
+static struct mlme_handler mlme_sta_tbl[]={
+ {"OnAssocReq23a", &OnAssocReq23a},
+ {"OnAssocRsp23a", &OnAssocRsp23a},
+ {"OnReAssocReq", &OnAssocReq23a},
+ {"OnReAssocRsp", &OnAssocRsp23a},
+ {"OnProbeReq23a", &OnProbeReq23a},
+ {"OnProbeRsp23a", &OnProbeRsp23a},
+
+ /*----------------------------------------------------------
+ below 2 are reserved
+ -----------------------------------------------------------*/
+ {"DoReserved23a", &DoReserved23a},
+ {"DoReserved23a", &DoReserved23a},
+ {"OnBeacon23a", &OnBeacon23a},
+ {"OnATIM", &OnAtim23a},
+ {"OnDisassoc23a", &OnDisassoc23a},
+ {"OnAuth23a", &OnAuth23aClient23a},
+ {"OnDeAuth23a", &OnDeAuth23a},
+ {"OnAction23a", &OnAction23a},
+};
+
+static struct action_handler OnAction23a_tbl[]={
+ {WLAN_CATEGORY_SPECTRUM_MGMT, "ACTION_SPECTRUM_MGMT", on_action_spct23a},
+ {WLAN_CATEGORY_QOS, "ACTION_QOS", &OnAction23a_qos},
+ {WLAN_CATEGORY_DLS, "ACTION_DLS", &OnAction23a_dls},
+ {WLAN_CATEGORY_BACK, "ACTION_BACK", &OnAction23a_back23a},
+ {WLAN_CATEGORY_PUBLIC, "ACTION_PUBLIC", on_action_public23a},
+ {WLAN_CATEGORY_HT, "ACTION_HT", &OnAction23a_ht},
+ {WLAN_CATEGORY_SA_QUERY, "ACTION_SA_QUERY", &DoReserved23a},
+ {WLAN_CATEGORY_WMM, "ACTION_WMM", &OnAction23a_wmm},
+ {WLAN_CATEGORY_VENDOR_SPECIFIC, "ACTION_P2P", &OnAction23a_p2p},
+};
+
+static u8 null_addr[ETH_ALEN]= {0, 0, 0, 0, 0, 0};
+
+/**************************************************
+OUI definitions for the vendor specific IE
+***************************************************/
+unsigned char WMM_OUI23A[] = {0x00, 0x50, 0xf2, 0x02};
+unsigned char WPS_OUI23A[] = {0x00, 0x50, 0xf2, 0x04};
+unsigned char P2P_OUI23A[] = {0x50, 0x6F, 0x9A, 0x09};
+unsigned char WFD_OUI23A[] = {0x50, 0x6F, 0x9A, 0x0A};
+
+unsigned char WMM_INFO_OUI23A[] = {0x00, 0x50, 0xf2, 0x02, 0x00, 0x01};
+unsigned char WMM_PARA_OUI23A[] = {0x00, 0x50, 0xf2, 0x02, 0x01, 0x01};
+
+static unsigned char REALTEK_96B_IE[] = {0x00, 0xe0, 0x4c, 0x02, 0x01, 0x20};
+
+/********************************************************
+MCS rate definitions
+*********************************************************/
+unsigned char MCS_rate_2R23A[16] = {
+ 0xff, 0xff, 0x0, 0x0, 0x01, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0};
+unsigned char MCS_rate_1R23A[16] = {
+ 0xff, 0x00, 0x0, 0x0, 0x01, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0};
+
+/********************************************************
+ChannelPlan definitions
+*********************************************************/
+
+static struct rt_channel_plan_2g RTW_ChannelPlan2G[RT_CHANNEL_DOMAIN_2G_MAX] = {
+ /* 0x00, RT_CHANNEL_DOMAIN_2G_WORLD , Passive scan CH 12, 13 */
+ {{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13}, 13},
+ /* 0x01, RT_CHANNEL_DOMAIN_2G_ETSI1 */
+ {{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13}, 13},
+ /* 0x02, RT_CHANNEL_DOMAIN_2G_FCC1 */
+ {{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}, 11},
+ /* 0x03, RT_CHANNEL_DOMAIN_2G_MIKK1 */
+ {{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14}, 14},
+ /* 0x04, RT_CHANNEL_DOMAIN_2G_ETSI2 */
+ {{10, 11, 12, 13}, 4},
+ /* 0x05, RT_CHANNEL_DOMAIN_2G_NULL */
+ {{}, 0},
+};
+
+static struct rt_channel_plan_5g RTW_ChannelPlan5G[RT_CHANNEL_DOMAIN_5G_MAX] = {
+ /* 0x00, RT_CHANNEL_DOMAIN_5G_NULL */
+ {{}, 0},
+ /* 0x01, RT_CHANNEL_DOMAIN_5G_ETSI1 */
+ {{36, 40, 44, 48, 52, 56, 60, 64, 100, 104, 108, 112,
+ 116, 120, 124, 128, 132, 136, 140}, 19},
+ /* 0x02, RT_CHANNEL_DOMAIN_5G_ETSI2 */
+ {{36, 40, 44, 48, 52, 56, 60, 64, 100, 104, 108, 112,
+ 116, 120, 124, 128, 132, 136, 140, 149, 153, 157, 161, 165}, 24},
+ /* 0x03, RT_CHANNEL_DOMAIN_5G_ETSI3 */
+ {{36, 40, 44, 48, 52, 56, 60, 64, 100, 104, 108, 112,
+ 116, 120, 124, 128, 132, 149, 153, 157, 161, 165}, 22},
+ /* 0x04, RT_CHANNEL_DOMAIN_5G_FCC1 */
+ {{36, 40, 44, 48, 52, 56, 60, 64, 100, 104, 108, 112,
+ 116, 120, 124, 128, 132, 136, 140, 149, 153, 157, 161, 165}, 24},
+ /* 0x05, RT_CHANNEL_DOMAIN_5G_FCC2 */
+ {{36, 40, 44, 48, 149, 153, 157, 161, 165}, 9},
+ /* 0x06, RT_CHANNEL_DOMAIN_5G_FCC3 */
+ {{36, 40, 44, 48, 52, 56, 60, 64, 149, 153, 157, 161, 165}, 13},
+ /* 0x07, RT_CHANNEL_DOMAIN_5G_FCC4 */
+ {{36, 40, 44, 48, 52, 56, 60, 64, 149, 153, 157, 161}, 12},
+ /* 0x08, RT_CHANNEL_DOMAIN_5G_FCC5 */
+ {{149, 153, 157, 161, 165}, 5},
+ /* 0x09, RT_CHANNEL_DOMAIN_5G_FCC6 */
+ {{36, 40, 44, 48, 52, 56, 60, 64}, 8},
+ /* 0x0A, RT_CHANNEL_DOMAIN_5G_FCC7_IC1 */
+ {{36, 40, 44, 48, 52, 56, 60, 64, 100, 104, 108, 112,
+ 116, 136, 140, 149, 153, 157, 161, 165}, 20},
+ /* 0x0B, RT_CHANNEL_DOMAIN_5G_KCC1 */
+ {{36, 40, 44, 48, 52, 56, 60, 64, 100, 104, 108, 112,
+ 116, 120, 124, 149, 153, 157, 161, 165}, 20},
+ /* 0x0C, RT_CHANNEL_DOMAIN_5G_MKK1 */
+ {{36, 40, 44, 48, 52, 56, 60, 64, 100, 104, 108, 112,
+ 116, 120, 124, 128, 132, 136, 140}, 19},
+ /* 0x0D, RT_CHANNEL_DOMAIN_5G_MKK2 */
+ {{36, 40, 44, 48, 52, 56, 60, 64}, 8},
+ /* 0x0E, RT_CHANNEL_DOMAIN_5G_MKK3 */
+ {{100, 104, 108, 112, 116, 120, 124, 128, 132, 136, 140}, 11},
+ /* 0x0F, RT_CHANNEL_DOMAIN_5G_NCC1 */
+ {{56, 60, 64, 100, 104, 108, 112, 116, 136, 140, 149,
+ 153, 157, 161, 165}, 15},
+ /* 0x10, RT_CHANNEL_DOMAIN_5G_NCC2 */
+ {{56, 60, 64, 149, 153, 157, 161, 165}, 8},
+
+ /* Driver self defined for old channel plan Compatible,
+ Remember to modify if have new channel plan definition ===== */
+ /* 0x11, RT_CHANNEL_DOMAIN_5G_FCC */
+ {{36, 40, 44, 48, 52, 56, 60, 64, 100, 104, 108, 112,
+ 116, 132, 136, 140, 149, 153, 157, 161, 165}, 21},
+ /* 0x12, RT_CHANNEL_DOMAIN_5G_JAPAN_NO_DFS */
+ {{36, 40, 44, 48}, 4},
+ /* 0x13, RT_CHANNEL_DOMAIN_5G_FCC4_NO_DFS */
+ {{36, 40, 44, 48, 149, 153, 157, 161}, 8},
+};
+
+static struct rt_channel_plan_map RTW_ChannelPlanMap[RT_CHANNEL_DOMAIN_MAX] = {
+ /* 0x00 ~ 0x1F , Old Define ===== */
+ {0x02, 0x11}, /* 0x00, RT_CHANNEL_DOMAIN_FCC */
+ {0x02, 0x0A}, /* 0x01, RT_CHANNEL_DOMAIN_IC */
+ {0x01, 0x01}, /* 0x02, RT_CHANNEL_DOMAIN_ETSI */
+ {0x01, 0x00}, /* 0x03, RT_CHANNEL_DOMAIN_SPAIN */
+ {0x01, 0x00}, /* 0x04, RT_CHANNEL_DOMAIN_FRANCE */
+ {0x03, 0x00}, /* 0x05, RT_CHANNEL_DOMAIN_MKK */
+ {0x03, 0x00}, /* 0x06, RT_CHANNEL_DOMAIN_MKK1 */
+ {0x01, 0x09}, /* 0x07, RT_CHANNEL_DOMAIN_ISRAEL */
+ {0x03, 0x09}, /* 0x08, RT_CHANNEL_DOMAIN_TELEC */
+ {0x03, 0x00}, /* 0x09, RT_CHANNEL_DOMAIN_GLOBAL_DOAMIN */
+ {0x00, 0x00}, /* 0x0A, RT_CHANNEL_DOMAIN_WORLD_WIDE_13 */
+ {0x02, 0x0F}, /* 0x0B, RT_CHANNEL_DOMAIN_TAIWAN */
+ {0x01, 0x08}, /* 0x0C, RT_CHANNEL_DOMAIN_CHINA */
+ {0x02, 0x06}, /* 0x0D, RT_CHANNEL_DOMAIN_SINGAPORE_INDIA_MEXICO */
+ {0x02, 0x0B}, /* 0x0E, RT_CHANNEL_DOMAIN_KOREA */
+ {0x02, 0x09}, /* 0x0F, RT_CHANNEL_DOMAIN_TURKEY */
+ {0x01, 0x01}, /* 0x10, RT_CHANNEL_DOMAIN_JAPAN */
+ {0x02, 0x05}, /* 0x11, RT_CHANNEL_DOMAIN_FCC_NO_DFS */
+ {0x01, 0x12}, /* 0x12, RT_CHANNEL_DOMAIN_JAPAN_NO_DFS */
+ {0x00, 0x04}, /* 0x13, RT_CHANNEL_DOMAIN_WORLD_WIDE_5G */
+ {0x02, 0x10}, /* 0x14, RT_CHANNEL_DOMAIN_TAIWAN_NO_DFS */
+ {0x00, 0x12}, /* 0x15, RT_CHANNEL_DOMAIN_ETSI_NO_DFS */
+ {0x00, 0x13}, /* 0x16, RT_CHANNEL_DOMAIN_KOREA_NO_DFS */
+ {0x03, 0x12}, /* 0x17, RT_CHANNEL_DOMAIN_JAPAN_NO_DFS */
+ {0x05, 0x08}, /* 0x18, RT_CHANNEL_DOMAIN_PAKISTAN_NO_DFS */
+ {0x02, 0x08}, /* 0x19, RT_CHANNEL_DOMAIN_TAIWAN2_NO_DFS */
+ {0x00, 0x00}, /* 0x1A, */
+ {0x00, 0x00}, /* 0x1B, */
+ {0x00, 0x00}, /* 0x1C, */
+ {0x00, 0x00}, /* 0x1D, */
+ {0x00, 0x00}, /* 0x1E, */
+ {0x05, 0x04}, /* 0x1F, RT_CHANNEL_DOMAIN_WORLD_WIDE_ONLY_5G */
+ /* 0x20 ~ 0x7F , New Define ===== */
+ {0x00, 0x00}, /* 0x20, RT_CHANNEL_DOMAIN_WORLD_NULL */
+ {0x01, 0x00}, /* 0x21, RT_CHANNEL_DOMAIN_ETSI1_NULL */
+ {0x02, 0x00}, /* 0x22, RT_CHANNEL_DOMAIN_FCC1_NULL */
+ {0x03, 0x00}, /* 0x23, RT_CHANNEL_DOMAIN_MKK1_NULL */
+ {0x04, 0x00}, /* 0x24, RT_CHANNEL_DOMAIN_ETSI2_NULL */
+ {0x02, 0x04}, /* 0x25, RT_CHANNEL_DOMAIN_FCC1_FCC1 */
+ {0x00, 0x01}, /* 0x26, RT_CHANNEL_DOMAIN_WORLD_ETSI1 */
+ {0x03, 0x0C}, /* 0x27, RT_CHANNEL_DOMAIN_MKK1_MKK1 */
+ {0x00, 0x0B}, /* 0x28, RT_CHANNEL_DOMAIN_WORLD_KCC1 */
+ {0x00, 0x05}, /* 0x29, RT_CHANNEL_DOMAIN_WORLD_FCC2 */
+ {0x00, 0x00}, /* 0x2A, */
+ {0x00, 0x00}, /* 0x2B, */
+ {0x00, 0x00}, /* 0x2C, */
+ {0x00, 0x00}, /* 0x2D, */
+ {0x00, 0x00}, /* 0x2E, */
+ {0x00, 0x00}, /* 0x2F, */
+ {0x00, 0x06}, /* 0x30, RT_CHANNEL_DOMAIN_WORLD_FCC3 */
+ {0x00, 0x07}, /* 0x31, RT_CHANNEL_DOMAIN_WORLD_FCC4 */
+ {0x00, 0x08}, /* 0x32, RT_CHANNEL_DOMAIN_WORLD_FCC5 */
+ {0x00, 0x09}, /* 0x33, RT_CHANNEL_DOMAIN_WORLD_FCC6 */
+ {0x02, 0x0A}, /* 0x34, RT_CHANNEL_DOMAIN_FCC1_FCC7 */
+ {0x00, 0x02}, /* 0x35, RT_CHANNEL_DOMAIN_WORLD_ETSI2 */
+ {0x00, 0x03}, /* 0x36, RT_CHANNEL_DOMAIN_WORLD_ETSI3 */
+ {0x03, 0x0D}, /* 0x37, RT_CHANNEL_DOMAIN_MKK1_MKK2 */
+ {0x03, 0x0E}, /* 0x38, RT_CHANNEL_DOMAIN_MKK1_MKK3 */
+ {0x02, 0x0F}, /* 0x39, RT_CHANNEL_DOMAIN_FCC1_NCC1 */
+ {0x00, 0x00}, /* 0x3A, */
+ {0x00, 0x00}, /* 0x3B, */
+ {0x00, 0x00}, /* 0x3C, */
+ {0x00, 0x00}, /* 0x3D, */
+ {0x00, 0x00}, /* 0x3E, */
+ {0x00, 0x00}, /* 0x3F, */
+ {0x02, 0x10}, /* 0x40, RT_CHANNEL_DOMAIN_FCC1_NCC2 */
+ {0x03, 0x00}, /* 0x41, RT_CHANNEL_DOMAIN_GLOBAL_DOAMIN_2G */
+};
+
+static struct rt_channel_plan_map RTW_CHANNEL_PLAN_MAP_REALTEK_DEFINE =
+{0x03, 0x02}; /* use the conbination for max channel numbers */
+
+static void dummy_event_callback(struct rtw_adapter *adapter, const u8 *pbuf)
+{
+}
+
+static struct fwevent wlanevents[] =
+{
+ {0, &dummy_event_callback}, /*0*/
+ {0, NULL},
+ {0, NULL},
+ {0, NULL},
+ {0, NULL},
+ {0, NULL},
+ {0, NULL},
+ {0, NULL},
+ {0, &rtw_survey_event_cb23a}, /*8*/
+ {sizeof (struct surveydone_event), &rtw_surveydone_event_callback23a},
+ {0, &rtw23a_joinbss_event_cb}, /*10*/
+ {sizeof(struct stassoc_event), &rtw_stassoc_event_callback23a},
+ {sizeof(struct stadel_event), &rtw_stadel_event_callback23a},
+ {0, &dummy_event_callback},
+ {0, &dummy_event_callback},
+ {0, NULL}, /*15*/
+ {0, NULL},
+ {0, NULL},
+ {0, NULL},
+ {0, &dummy_event_callback},
+ {0, NULL}, /*20*/
+ {0, NULL},
+ {0, NULL},
+ {0, &dummy_event_callback},
+ {0, NULL},
+};
+
+
+static void rtw_correct_TSF(struct rtw_adapter *padapter)
+{
+ hw_var_set_correct_tsf(padapter);
+}
+
+static void
+rtw_update_TSF(struct mlme_ext_priv *pmlmeext, struct ieee80211_mgmt *mgmt)
+{
+ pmlmeext->TSFValue = get_unaligned_le64(&mgmt->u.beacon.timestamp);
+}
+
+/*
+ * Search the @param channel_num in given @param channel_set
+ * @ch_set: the given channel set
+ * @ch: the given channel number
+ *
+ * return the index of channel_num in channel_set, -1 if not found
+ */
+int rtw_ch_set_search_ch23a(struct rt_channel_info *ch_set, const u32 ch)
+{
+ int i;
+
+ for (i = 0; ch_set[i]. ChannelNum != 0; i++) {
+ if (ch == ch_set[i].ChannelNum)
+ break;
+ }
+
+ if (i >= ch_set[i].ChannelNum)
+ return -1;
+ return i;
+}
+
+/****************************************************************************
+
+Following are the initialization functions for WiFi MLME
+
+*****************************************************************************/
+
+int init_hw_mlme_ext23a(struct rtw_adapter *padapter)
+{
+ struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv;
+
+ set_channel_bwmode23a(padapter, pmlmeext->cur_channel,
+ pmlmeext->cur_ch_offset, pmlmeext->cur_bwmode);
+ return _SUCCESS;
+}
+
+static void init_mlme_ext_priv23a_value(struct rtw_adapter *padapter)
+{
+ struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv;
+ struct mlme_ext_info *pmlmeinfo = &pmlmeext->mlmext_info;
+ unsigned char mixed_datarate[NumRates] = {
+ _1M_RATE_, _2M_RATE_, _5M_RATE_, _11M_RATE_, _6M_RATE_,
+ _9M_RATE_, _12M_RATE_, _18M_RATE_, _24M_RATE_, _36M_RATE_,
+ _48M_RATE_, _54M_RATE_, 0xff};
+ unsigned char mixed_basicrate[NumRates] = {
+ _1M_RATE_, _2M_RATE_, _5M_RATE_, _11M_RATE_, _6M_RATE_,
+ _12M_RATE_, _24M_RATE_, 0xff,};
+
+ atomic_set(&pmlmeext->event_seq, 0);
+ /* reset to zero when disconnect at client mode */
+ pmlmeext->mgnt_seq = 0;
+
+ pmlmeext->cur_channel = padapter->registrypriv.channel;
+ pmlmeext->cur_bwmode = HT_CHANNEL_WIDTH_20;
+ pmlmeext->cur_ch_offset = HAL_PRIME_CHNL_OFFSET_DONT_CARE;
+
+ pmlmeext->retry = 0;
+
+ pmlmeext->cur_wireless_mode = padapter->registrypriv.wireless_mode;
+
+ memcpy(pmlmeext->datarate, mixed_datarate, NumRates);
+ memcpy(pmlmeext->basicrate, mixed_basicrate, NumRates);
+
+ if (pmlmeext->cur_channel > 14)
+ pmlmeext->tx_rate = IEEE80211_OFDM_RATE_6MB;
+ else
+ pmlmeext->tx_rate = IEEE80211_CCK_RATE_1MB;
+
+ pmlmeext->sitesurvey_res.state = SCAN_DISABLE;
+ pmlmeext->sitesurvey_res.channel_idx = 0;
+ pmlmeext->sitesurvey_res.bss_cnt = 0;
+ pmlmeext->scan_abort = false;
+
+ pmlmeinfo->state = MSR_NOLINK;
+ pmlmeinfo->reauth_count = 0;
+ pmlmeinfo->reassoc_count = 0;
+ pmlmeinfo->link_count = 0;
+ pmlmeinfo->auth_seq = 0;
+ pmlmeinfo->auth_algo = dot11AuthAlgrthm_Open;
+ pmlmeinfo->key_index = 0;
+ pmlmeinfo->iv = 0;
+
+ pmlmeinfo->enc_algo = 0;
+ pmlmeinfo->authModeToggle = 0;
+
+ memset(pmlmeinfo->chg_txt, 0, 128);
+
+ pmlmeinfo->slotTime = SHORT_SLOT_TIME;
+ pmlmeinfo->preamble_mode = PREAMBLE_AUTO;
+
+ pmlmeinfo->dialogToken = 0;
+
+ pmlmeext->action_public_rxseq = 0xffff;
+ pmlmeext->action_public_dialog_token = 0xff;
+}
+
+static int has_channel(struct rt_channel_info *channel_set,
+ u8 chanset_size, u8 chan) {
+ int i;
+
+ for (i = 0; i < chanset_size; i++) {
+ if (channel_set[i].ChannelNum == chan)
+ return 1;
+ }
+
+ return 0;
+}
+
+static void init_channel_list(struct rtw_adapter *padapter,
+ struct rt_channel_info *channel_set,
+ u8 chanset_size,
+ struct p2p_channels *channel_list)
+{
+ struct p2p_oper_class_map op_class[] = {
+ { IEEE80211G, 81, 1, 13, 1, BW20 },
+ { IEEE80211G, 82, 14, 14, 1, BW20 },
+ { IEEE80211A, 115, 36, 48, 4, BW20 },
+ { IEEE80211A, 116, 36, 44, 8, BW40PLUS },
+ { IEEE80211A, 117, 40, 48, 8, BW40MINUS },
+ { IEEE80211A, 124, 149, 161, 4, BW20 },
+ { IEEE80211A, 125, 149, 169, 4, BW20 },
+ { IEEE80211A, 126, 149, 157, 8, BW40PLUS },
+ { IEEE80211A, 127, 153, 161, 8, BW40MINUS },
+ { -1, 0, 0, 0, 0, BW20 }
+ };
+
+ int cla, op;
+
+ cla = 0;
+
+ for (op = 0; op_class[op].op_class; op++) {
+ u8 ch;
+ struct p2p_oper_class_map *o = &op_class[op];
+ struct p2p_reg_class *reg = NULL;
+
+ for (ch = o->min_chan; ch <= o->max_chan; ch += o->inc) {
+ if (!has_channel(channel_set, chanset_size, ch))
+ continue;
+
+ if ((0 == padapter->registrypriv.ht_enable) &&
+ (o->inc == 8))
+ continue;
+
+ if ((0 == (padapter->registrypriv.cbw40_enable & BIT(1))) &&
+ ((BW40MINUS == o->bw) || (BW40PLUS == o->bw)))
+ continue;
+
+ if (reg == NULL) {
+ reg = &channel_list->reg_class[cla];
+ cla++;
+ reg->reg_class = o->op_class;
+ reg->channels = 0;
+ }
+ reg->channel[reg->channels] = ch;
+ reg->channels++;
+ }
+ }
+ channel_list->reg_classes = cla;
+}
+
+static u8 init_channel_set(struct rtw_adapter *padapter, u8 cplan,
+ struct rt_channel_info *c_set)
+{
+ u8 i, ch_size = 0;
+ u8 b5GBand = false, b2_4GBand = false;
+ u8 Index2G = 0, Index5G = 0;
+
+ memset(c_set, 0, sizeof(struct rt_channel_info) * MAX_CHANNEL_NUM);
+
+ if (cplan >= RT_CHANNEL_DOMAIN_MAX &&
+ cplan != RT_CHANNEL_DOMAIN_REALTEK_DEFINE) {
+ DBG_8723A("ChannelPlan ID %x error !!!!!\n", cplan);
+ return ch_size;
+ }
+
+ if (padapter->registrypriv.wireless_mode & WIRELESS_11G) {
+ b2_4GBand = true;
+ if (RT_CHANNEL_DOMAIN_REALTEK_DEFINE == cplan)
+ Index2G = RTW_CHANNEL_PLAN_MAP_REALTEK_DEFINE.Index2G;
+ else
+ Index2G = RTW_ChannelPlanMap[cplan].Index2G;
+ }
+
+ if (padapter->registrypriv.wireless_mode & WIRELESS_11A) {
+ b5GBand = true;
+ if (RT_CHANNEL_DOMAIN_REALTEK_DEFINE == cplan)
+ Index5G = RTW_CHANNEL_PLAN_MAP_REALTEK_DEFINE.Index5G;
+ else
+ Index5G = RTW_ChannelPlanMap[cplan].Index5G;
+ }
+
+ if (b2_4GBand) {
+ for (i = 0; i < RTW_ChannelPlan2G[Index2G].Len; i++) {
+ c_set[ch_size].ChannelNum =
+ RTW_ChannelPlan2G[Index2G].Channel[i];
+
+ if ((RT_CHANNEL_DOMAIN_GLOBAL_DOAMIN == cplan) ||
+ /* Channel 1~11 is active, and 12~14 is passive */
+ RT_CHANNEL_DOMAIN_GLOBAL_DOAMIN_2G == cplan) {
+ if (c_set[ch_size].ChannelNum >= 1 &&
+ c_set[ch_size].ChannelNum <= 11)
+ c_set[ch_size].ScanType = SCAN_ACTIVE;
+ else if (c_set[ch_size].ChannelNum >= 12 &&
+ c_set[ch_size].ChannelNum <= 14)
+ c_set[ch_size].ScanType = SCAN_PASSIVE;
+ } else if (RT_CHANNEL_DOMAIN_WORLD_WIDE_13 == cplan ||
+ RT_CHANNEL_DOMAIN_WORLD_WIDE_5G == cplan ||
+ RT_CHANNEL_DOMAIN_2G_WORLD == Index2G) {
+ /* channel 12~13, passive scan */
+ if (c_set[ch_size].ChannelNum <= 11)
+ c_set[ch_size].ScanType = SCAN_ACTIVE;
+ else
+ c_set[ch_size].ScanType = SCAN_PASSIVE;
+ } else
+ c_set[ch_size].ScanType = SCAN_ACTIVE;
+
+ ch_size++;
+ }
+ }
+
+ if (b5GBand) {
+ for (i = 0; i < RTW_ChannelPlan5G[Index5G].Len; i++) {
+ if (RTW_ChannelPlan5G[Index5G].Channel[i] <= 48 ||
+ RTW_ChannelPlan5G[Index5G].Channel[i] >= 149) {
+ c_set[ch_size].ChannelNum =
+ RTW_ChannelPlan5G[Index5G].Channel[i];
+ if (RT_CHANNEL_DOMAIN_WORLD_WIDE_5G == cplan) {
+ /* passive scan for all 5G channels */
+ c_set[ch_size].ScanType =
+ SCAN_PASSIVE;
+ } else
+ c_set[ch_size].ScanType =
+ SCAN_ACTIVE;
+ DBG_8723A("%s(): channel_set[%d].ChannelNum = "
+ "%d\n", __func__, ch_size,
+ c_set[ch_size].ChannelNum);
+ ch_size++;
+ }
+ }
+ }
+
+ return ch_size;
+}
+
+int init_mlme_ext_priv23a(struct rtw_adapter *padapter)
+{
+ struct registry_priv *pregistrypriv = &padapter->registrypriv;
+ struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv;
+ struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
+ struct mlme_ext_info *pmlmeinfo = &pmlmeext->mlmext_info;
+
+ pmlmeext->padapter = padapter;
+
+ init_mlme_ext_priv23a_value(padapter);
+ pmlmeinfo->bAcceptAddbaReq = pregistrypriv->bAcceptAddbaReq;
+
+ init_mlme_ext_timer23a(padapter);
+
+#ifdef CONFIG_8723AU_AP_MODE
+ init_mlme_ap_info23a(padapter);
+#endif
+
+ pmlmeext->max_chan_nums = init_channel_set(padapter,
+ pmlmepriv->ChannelPlan,
+ pmlmeext->channel_set);
+ init_channel_list(padapter, pmlmeext->channel_set,
+ pmlmeext->max_chan_nums, &pmlmeext->channel_list);
+
+ pmlmeext->chan_scan_time = SURVEY_TO;
+ pmlmeext->mlmeext_init = true;
+
+ pmlmeext->active_keep_alive_check = true;
+ return _SUCCESS;
+}
+
+void free_mlme_ext_priv23a (struct mlme_ext_priv *pmlmeext)
+{
+ struct rtw_adapter *padapter = pmlmeext->padapter;
+
+ if (!padapter)
+ return;
+
+ if (padapter->bDriverStopped == true) {
+ del_timer_sync(&pmlmeext->survey_timer);
+ del_timer_sync(&pmlmeext->link_timer);
+ /* del_timer_sync(&pmlmeext->ADDBA_timer); */
+ }
+}
+
+static void
+_mgt_dispatcher23a(struct rtw_adapter *padapter, struct mlme_handler *ptable,
+ struct recv_frame *precv_frame)
+{
+ struct sk_buff *skb = precv_frame->pkt;
+ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
+
+ if (ptable->func) {
+ /* receive the frames that ra(a1) is my address
+ or ra(a1) is bc address. */
+ if (!ether_addr_equal(hdr->addr1, myid(&padapter->eeprompriv))&&
+ !is_broadcast_ether_addr(hdr->addr1))
+ return;
+
+ ptable->func(padapter, precv_frame);
+ }
+}
+
+void mgt_dispatcher23a(struct rtw_adapter *padapter,
+ struct recv_frame *precv_frame)
+{
+ struct mlme_handler *ptable;
+#ifdef CONFIG_8723AU_AP_MODE
+ struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
+#endif /* CONFIG_8723AU_AP_MODE */
+ struct sk_buff *skb = precv_frame->pkt;
+ struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *) skb->data;
+ struct sta_info *psta;
+ u16 stype;
+ int index;
+
+ if (!ieee80211_is_mgmt(mgmt->frame_control))
+ return;
+
+ /* receive the frames that ra(a1) is my address or ra(a1) is
+ bc address. */
+ if (!ether_addr_equal(mgmt->da, myid(&padapter->eeprompriv)) &&
+ !is_broadcast_ether_addr(mgmt->da))
+ return;
+
+ ptable = mlme_sta_tbl;
+
+ stype = le16_to_cpu(mgmt->frame_control) & IEEE80211_FCTL_STYPE;
+ index = stype >> 4;
+
+ if (index > 13) {
+ RT_TRACE(_module_rtl871x_mlme_c_, _drv_err_,
+ "Currently we do not support reserved sub-fr-type =%d\n",
+ index);
+ return;
+ }
+ ptable += index;
+
+ psta = rtw_get_stainfo23a(&padapter->stapriv, mgmt->sa);
+
+ if (psta) {
+ if (ieee80211_has_retry(mgmt->frame_control)) {
+ if (precv_frame->attrib.seq_num ==
+ psta->RxMgmtFrameSeqNum) {
+ /* drop the duplicate management frame */
+ DBG_8723A("Drop duplicate management frame "
+ "with seq_num = %d.\n",
+ precv_frame->attrib.seq_num);
+ return;
+ }
+ }
+ psta->RxMgmtFrameSeqNum = precv_frame->attrib.seq_num;
+ }
+
+#ifdef CONFIG_8723AU_AP_MODE
+ switch (stype) {
+ case IEEE80211_STYPE_AUTH:
+ if (check_fwstate(pmlmepriv, WIFI_AP_STATE))
+ ptable->func = &OnAuth23a;
+ else
+ ptable->func = &OnAuth23aClient23a;
+ /* pass through */
+ case IEEE80211_STYPE_ASSOC_REQ:
+ case IEEE80211_STYPE_REASSOC_REQ:
+ _mgt_dispatcher23a(padapter, ptable, precv_frame);
+ break;
+ case IEEE80211_STYPE_PROBE_REQ:
+ if (check_fwstate(pmlmepriv, WIFI_AP_STATE))
+ _mgt_dispatcher23a(padapter, ptable, precv_frame);
+ else
+ _mgt_dispatcher23a(padapter, ptable, precv_frame);
+ break;
+ case IEEE80211_STYPE_BEACON:
+ _mgt_dispatcher23a(padapter, ptable, precv_frame);
+ break;
+ case IEEE80211_STYPE_ACTION:
+ /* if (check_fwstate(pmlmepriv, WIFI_AP_STATE)) */
+ _mgt_dispatcher23a(padapter, ptable, precv_frame);
+ break;
+ default:
+ _mgt_dispatcher23a(padapter, ptable, precv_frame);
+ break;
+ }
+#else
+ _mgt_dispatcher23a(padapter, ptable, precv_frame);
+#endif
+}
+
+/****************************************************************************
+
+Following are the callback functions for each subtype of the management frames
+
+*****************************************************************************/
+
+static int
+OnProbeReq23a(struct rtw_adapter *padapter, struct recv_frame *precv_frame)
+{
+ const u8 *ie;
+ struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
+ struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv;
+ struct mlme_ext_info *pmlmeinfo = &pmlmeext->mlmext_info;
+ struct wlan_bssid_ex *cur = &pmlmeinfo->network;
+ struct sk_buff *skb = precv_frame->pkt;
+ struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *) skb->data;
+ int len = skb->len;
+
+ if (check_fwstate(pmlmepriv, WIFI_STATION_STATE))
+ return _SUCCESS;
+
+ if (!check_fwstate(pmlmepriv, _FW_LINKED) &&
+ !check_fwstate(pmlmepriv,
+ WIFI_ADHOC_MASTER_STATE | WIFI_AP_STATE))
+ return _SUCCESS;
+
+ if (unlikely(!ieee80211_is_probe_req(mgmt->frame_control))) {
+ printk(KERN_WARNING "%s: Received non probe request frame\n",
+ __func__);
+ return _FAIL;
+ }
+
+ len -= offsetof(struct ieee80211_mgmt, u.probe_req.variable);
+
+ ie = cfg80211_find_ie(WLAN_EID_SSID, mgmt->u.probe_req.variable, len);
+
+ /* check (wildcard) SSID */
+ if (!ie)
+ goto out;
+
+ if ((ie[1] && memcmp(ie + 2, cur->Ssid.ssid, cur->Ssid.ssid_len)) ||
+ (ie[1] == 0 && pmlmeinfo->hidden_ssid_mode)) {
+ return _SUCCESS;
+ }
+
+ if (check_fwstate(pmlmepriv, _FW_LINKED) &&
+ pmlmepriv->cur_network.join_res)
+ issue_probersp(padapter, mgmt->sa, false);
+
+out:
+ return _SUCCESS;
+}
+
+static int
+OnProbeRsp23a(struct rtw_adapter *padapter, struct recv_frame *precv_frame)
+{
+ struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv;
+
+ if (pmlmeext->sitesurvey_res.state == SCAN_PROCESS) {
+ report_survey_event23a(padapter, precv_frame);
+ return _SUCCESS;
+ }
+
+ return _SUCCESS;
+}
+
+static int
+OnBeacon23a(struct rtw_adapter *padapter, struct recv_frame *precv_frame)
+{
+ int cam_idx;
+ struct sta_info *psta;
+ struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv;
+ struct mlme_ext_info *pmlmeinfo = &pmlmeext->mlmext_info;
+ struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
+ struct sta_priv *pstapriv = &padapter->stapriv;
+ struct sk_buff *skb = precv_frame->pkt;
+ struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *) skb->data;
+ int pkt_len = skb->len;
+ struct wlan_bssid_ex *pbss;
+ int ret = _SUCCESS;
+ u8 *p, *pie;
+ int pie_len;
+ u32 ielen = 0;
+
+ pie = mgmt->u.beacon.variable;
+ pie_len = pkt_len - offsetof(struct ieee80211_mgmt, u.beacon.variable);
+ p = rtw_get_ie23a(pie, WLAN_EID_EXT_SUPP_RATES, &ielen, pie_len);
+ if (p && ielen > 0) {
+ if (p[1 + ielen] == 0x2D && p[2 + ielen] != 0x2D) {
+ /* Invalid value 0x2D is detected in Extended Supported
+ * Rates (ESR) IE. Try to fix the IE length to avoid
+ * failed Beacon parsing.
+ */
+ DBG_8723A("[WIFIDBG] Error in ESR IE is detected in "
+ "Beacon of BSSID: %pM. Fix the length of "
+ "ESR IE to avoid failed Beacon parsing.\n",
+ mgmt->bssid);
+ p[1] = ielen - 1;
+ }
+ }
+
+ if (pmlmeext->sitesurvey_res.state == SCAN_PROCESS) {
+ report_survey_event23a(padapter, precv_frame);
+ return _SUCCESS;
+ }
+
+ if (!ether_addr_equal(mgmt->bssid,
+ get_my_bssid23a(&pmlmeinfo->network)))
+ goto out;
+
+ if (pmlmeinfo->state & WIFI_FW_AUTH_NULL) {
+ /* we should update current network before auth,
+ or some IE is wrong */
+ pbss = collect_bss_info(padapter, precv_frame);
+ if (pbss) {
+ update_network23a(&pmlmepriv->cur_network.network, pbss,
+ padapter, true);
+ rtw_get_bcn_info23a(&pmlmepriv->cur_network);
+ kfree(pbss);
+ }
+
+ /* check the vendor of the assoc AP */
+ pmlmeinfo->assoc_AP_vendor =
+ check_assoc_AP23a((u8 *)&mgmt->u.beacon, pkt_len -
+ offsetof(struct ieee80211_mgmt, u));
+
+ /* update TSF Value */
+ rtw_update_TSF(pmlmeext, mgmt);
+
+ /* start auth */
+ start_clnt_auth(padapter);
+
+ return _SUCCESS;
+ }
+
+ if (((pmlmeinfo->state & 0x03) == MSR_AP) &&
+ (pmlmeinfo->state & WIFI_FW_ASSOC_SUCCESS)) {
+ psta = rtw_get_stainfo23a(pstapriv, mgmt->sa);
+ if (psta) {
+ ret = rtw_check_bcn_info23a(padapter, mgmt, pkt_len);
+ if (ret != _SUCCESS) {
+ DBG_8723A_LEVEL(_drv_always_, "ap has changed, "
+ "disconnect now\n");
+ receive_disconnect23a(padapter, pmlmeinfo->network.MacAddress, 65535);
+ return _SUCCESS;
+ }
+ /* update WMM, ERP in the beacon */
+ /* todo: the timer is used instead of
+ the number of the beacon received */
+ if ((sta_rx_pkts(psta) & 0xf) == 0) {
+ /* DBG_8723A("update_bcn_info\n"); */
+ update_beacon23a_info(padapter, mgmt,
+ pkt_len, psta);
+ }
+ }
+ } else if ((pmlmeinfo->state&0x03) == MSR_ADHOC) {
+ psta = rtw_get_stainfo23a(pstapriv, mgmt->sa);
+ if (psta) {
+ /* update WMM, ERP in the beacon */
+ /* todo: the timer is used instead of the
+ number of the beacon received */
+ if ((sta_rx_pkts(psta) & 0xf) == 0) {
+ /* DBG_8723A("update_bcn_info\n"); */
+ update_beacon23a_info(padapter, mgmt,
+ pkt_len, psta);
+ }
+ } else {
+ /* allocate a new CAM entry for IBSS station */
+ cam_idx = allocate_fw_sta_entry23a(padapter);
+ if (cam_idx == NUM_STA)
+ goto out;
+
+ /* get supported rate */
+ if (update_sta_support_rate23a(padapter, pie, pie_len,
+ cam_idx) == _FAIL) {
+ pmlmeinfo->FW_sta_info[cam_idx].status = 0;
+ goto out;
+ }
+
+ /* update TSF Value */
+ rtw_update_TSF(pmlmeext, mgmt);
+
+ /* report sta add event */
+ report_add_sta_event23a(padapter, mgmt->sa,
+ cam_idx);
+ }
+ }
+
+out:
+
+ return _SUCCESS;
+}
+
+#ifdef CONFIG_8723AU_AP_MODE
+static int
+OnAuth23a(struct rtw_adapter *padapter, struct recv_frame *precv_frame)
+{
+ static struct sta_info stat;
+ struct sta_info *pstat = NULL;
+ struct sta_priv *pstapriv = &padapter->stapriv;
+ struct security_priv *psecuritypriv = &padapter->securitypriv;
+ struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv;
+ struct mlme_ext_info *pmlmeinfo = &pmlmeext->mlmext_info;
+ struct sk_buff *skb = precv_frame->pkt;
+ struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *) skb->data;
+ u8 *pframe;
+ const u8 *p;
+ unsigned char *sa;
+ u16 auth_mode, seq, algorithm;
+ int status, len = skb->len;
+
+ if ((pmlmeinfo->state & 0x03) != MSR_AP)
+ return _FAIL;
+
+ DBG_8723A("+OnAuth23a\n");
+
+ sa = mgmt->sa;
+
+ auth_mode = psecuritypriv->dot11AuthAlgrthm;
+
+ pframe = mgmt->u.auth.variable;
+ len = skb->len - offsetof(struct ieee80211_mgmt, u.auth.variable);
+
+ seq = le16_to_cpu(mgmt->u.auth.auth_transaction);
+ algorithm = le16_to_cpu(mgmt->u.auth.auth_alg);
+
+ DBG_8723A("auth alg =%x, seq =%X\n", algorithm, seq);
+
+ if (auth_mode == 2 &&
+ psecuritypriv->dot11PrivacyAlgrthm != WLAN_CIPHER_SUITE_WEP40 &&
+ psecuritypriv->dot11PrivacyAlgrthm != WLAN_CIPHER_SUITE_WEP104)
+ auth_mode = 0;
+
+ /* rx a shared-key auth but shared not enabled, or */
+ /* rx a open-system auth but shared-key is enabled */
+ if ((algorithm != WLAN_AUTH_OPEN && auth_mode == 0) ||
+ (algorithm == WLAN_AUTH_OPEN && auth_mode == 1)) {
+ DBG_8723A("auth rejected due to bad alg [alg =%d, auth_mib "
+ "=%d] %02X%02X%02X%02X%02X%02X\n",
+ algorithm, auth_mode,
+ sa[0], sa[1], sa[2], sa[3], sa[4], sa[5]);
+
+ status = WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG;
+
+ goto auth_fail;
+ }
+
+ if (rtw_access_ctrl23a(padapter, sa) == false) {
+ status = WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA;
+ goto auth_fail;
+ }
+
+ pstat = rtw_get_stainfo23a(pstapriv, sa);
+ if (!pstat) {
+ /* allocate a new one */
+ DBG_8723A("going to alloc stainfo for sa =%pM\n", sa);
+ pstat = rtw_alloc_stainfo23a(pstapriv, sa, GFP_ATOMIC);
+ if (!pstat) {
+ DBG_8723A(" Exceed the upper limit of supported "
+ "clients...\n");
+ status = WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA;
+ goto auth_fail;
+ }
+
+ pstat->state = WIFI_FW_AUTH_NULL;
+ pstat->auth_seq = 0;
+
+ /* pstat->flags = 0; */
+ /* pstat->capability = 0; */
+ } else {
+ spin_lock_bh(&pstapriv->asoc_list_lock);
+ if (!list_empty(&pstat->asoc_list)) {
+ list_del_init(&pstat->asoc_list);
+ pstapriv->asoc_list_cnt--;
+ if (pstat->expire_to > 0) {
+ /* TODO: STA re_auth within expire_to */
+ }
+ }
+ spin_unlock_bh(&pstapriv->asoc_list_lock);
+
+ if (seq == 1) {
+ /* TODO: STA re_auth and auth timeout */
+ }
+ }
+
+ spin_lock_bh(&pstapriv->auth_list_lock);
+ if (list_empty(&pstat->auth_list)) {
+ list_add_tail(&pstat->auth_list, &pstapriv->auth_list);
+ pstapriv->auth_list_cnt++;
+ }
+ spin_unlock_bh(&pstapriv->auth_list_lock);
+
+ if (pstat->auth_seq == 0)
+ pstat->expire_to = pstapriv->auth_to;
+
+ if ((pstat->auth_seq + 1) != seq) {
+ DBG_8723A("(1)auth rejected because out of seq [rx_seq =%d, "
+ "exp_seq =%d]!\n", seq, pstat->auth_seq+1);
+ status = WLAN_STATUS_UNKNOWN_AUTH_TRANSACTION;
+ goto auth_fail;
+ }
+
+ if (algorithm == WLAN_AUTH_OPEN && (auth_mode == 0 || auth_mode == 2)) {
+ if (seq == 1) {
+ pstat->state &= ~WIFI_FW_AUTH_NULL;
+ pstat->state |= WIFI_FW_AUTH_SUCCESS;
+ pstat->expire_to = pstapriv->assoc_to;
+ pstat->authalg = algorithm;
+ } else {
+ DBG_8723A("(2)auth rejected because out of seq "
+ "[rx_seq =%d, exp_seq =%d]!\n",
+ seq, pstat->auth_seq+1);
+ status = WLAN_STATUS_UNKNOWN_AUTH_TRANSACTION;
+ goto auth_fail;
+ }
+ } else { /* shared system or auto authentication */
+ if (seq == 1) {
+ /* prepare for the challenging txt... */
+ pstat->state &= ~WIFI_FW_AUTH_NULL;
+ pstat->state |= WIFI_FW_AUTH_STATE;
+ pstat->authalg = algorithm;
+ pstat->auth_seq = 2;
+ } else if (seq == 3) {
+ /* checking for challenging txt... */
+ DBG_8723A("checking for challenging txt...\n");
+
+ p = cfg80211_find_ie(WLAN_EID_CHALLENGE, pframe, len);
+ if (!p || p[1] <= 0) {
+ DBG_8723A("auth rejected because challenge "
+ "failure!(1)\n");
+ status = WLAN_STATUS_CHALLENGE_FAIL;
+ goto auth_fail;
+ }
+
+ if (!memcmp(p + 2, pstat->chg_txt, 128)) {
+ pstat->state &= ~WIFI_FW_AUTH_STATE;
+ pstat->state |= WIFI_FW_AUTH_SUCCESS;
+ /* challenging txt is correct... */
+ pstat->expire_to = pstapriv->assoc_to;
+ } else {
+ DBG_8723A("auth rejected because challenge "
+ "failure!\n");
+ status = WLAN_STATUS_CHALLENGE_FAIL;
+ goto auth_fail;
+ }
+ } else {
+ DBG_8723A("(3)auth rejected because out of seq "
+ "[rx_seq =%d, exp_seq =%d]!\n",
+ seq, pstat->auth_seq+1);
+ status = WLAN_STATUS_UNKNOWN_AUTH_TRANSACTION;
+ goto auth_fail;
+ }
+ }
+
+ /* Now, we are going to issue_auth... */
+ pstat->auth_seq = seq + 1;
+
+ issue_auth(padapter, pstat, WLAN_STATUS_SUCCESS);
+
+ if (pstat->state & WIFI_FW_AUTH_SUCCESS)
+ pstat->auth_seq = 0;
+
+ return _SUCCESS;
+
+auth_fail:
+
+ if (pstat)
+ rtw_free_stainfo23a(padapter, pstat);
+
+ pstat = &stat;
+ memset((char *)pstat, '\0', sizeof(stat));
+ pstat->auth_seq = 2;
+ ether_addr_copy(pstat->hwaddr, sa);
+
+ issue_auth(padapter, pstat, (unsigned short)status);
+
+ return _FAIL;
+}
+#endif
+
+static int
+OnAuth23aClient23a(struct rtw_adapter *padapter, struct recv_frame *precv_frame)
+{
+ unsigned int seq, status, algthm;
+ unsigned int go2asoc = 0;
+ struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv;
+ struct mlme_ext_info *pmlmeinfo = &pmlmeext->mlmext_info;
+ struct sk_buff *skb = precv_frame->pkt;
+ struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *) skb->data;
+ const u8 *p;
+ u8 *pie;
+ int plen = skb->len;
+
+ DBG_8723A("%s\n", __func__);
+
+ /* check A1 matches or not */
+ if (!ether_addr_equal(myid(&padapter->eeprompriv), mgmt->da))
+ return _SUCCESS;
+
+ if (!(pmlmeinfo->state & WIFI_FW_AUTH_STATE))
+ return _SUCCESS;
+
+ pie = mgmt->u.auth.variable;
+ plen -= offsetof(struct ieee80211_mgmt, u.auth.variable);
+
+ algthm = le16_to_cpu(mgmt->u.auth.auth_alg);
+ seq = le16_to_cpu(mgmt->u.auth.auth_transaction);
+ status = le16_to_cpu(mgmt->u.auth.status_code);
+
+ if (status) {
+ DBG_8723A("clnt auth fail, status: %d\n", status);
+ /* pmlmeinfo->auth_algo == dot11AuthAlgrthm_Auto) */
+ if (status == WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG) {
+ if (pmlmeinfo->auth_algo == dot11AuthAlgrthm_Shared)
+ pmlmeinfo->auth_algo = dot11AuthAlgrthm_Open;
+ else
+ pmlmeinfo->auth_algo = dot11AuthAlgrthm_Shared;
+ /* pmlmeinfo->reauth_count = 0; */
+ }
+
+ set_link_timer(pmlmeext, 1);
+ goto authclnt_fail;
+ }
+
+ if (seq == 2) {
+ if (pmlmeinfo->auth_algo == dot11AuthAlgrthm_Shared) {
+ /* legendary shared system */
+ p = cfg80211_find_ie(WLAN_EID_CHALLENGE, pie, plen);
+
+ if (!p) {
+ /* DBG_8723A("marc: no challenge text?\n"); */
+ goto authclnt_fail;
+ }
+
+ memcpy((void *)(pmlmeinfo->chg_txt), p + 2, p[1]);
+ pmlmeinfo->auth_seq = 3;
+ issue_auth(padapter, NULL, 0);
+ set_link_timer(pmlmeext, REAUTH_TO);
+
+ return _SUCCESS;
+ } else {
+ /* open system */
+ go2asoc = 1;
+ }
+ } else if (seq == 4) {
+ if (pmlmeinfo->auth_algo == dot11AuthAlgrthm_Shared)
+ go2asoc = 1;
+ else
+ goto authclnt_fail;
+ } else {
+ /* this is also illegal */
+ /* DBG_8723A("marc: clnt auth failed due to illegal seq =%x\n",
+ seq); */
+ goto authclnt_fail;
+ }
+
+ if (go2asoc) {
+ DBG_8723A_LEVEL(_drv_always_, "auth success, start assoc\n");
+ start_clnt_assoc(padapter);
+ return _SUCCESS;
+ }
+
+authclnt_fail:
+
+ /* pmlmeinfo->state &= ~(WIFI_FW_AUTH_STATE); */
+
+ return _FAIL;
+}
+
+#ifdef CONFIG_8723AU_AP_MODE
+static int rtw_validate_vendor_specific_ies(const u8 *pos, int elen)
+{
+ unsigned int oui;
+
+ /* first 3 bytes in vendor specific information element are the IEEE
+ * OUI of the vendor. The following byte is used a vendor specific
+ * sub-type. */
+ if (elen < 4) {
+ DBG_8723A("short vendor specific information element "
+ "ignored (len =%i)\n", elen);
+ return -EINVAL;
+ }
+
+ oui = RTW_GET_BE24(pos);
+ switch (oui) {
+ case WLAN_OUI_MICROSOFT:
+ /* Microsoft/Wi-Fi information elements are further typed and
+ * subtyped */
+ switch (pos[3]) {
+ case WLAN_OUI_TYPE_MICROSOFT_WPA:
+ /* Microsoft OUI (00:50:F2) with OUI Type 1:
+ * real WPA information element */
+ break;
+ case WLAN_OUI_TYPE_MICROSOFT_WMM:
+ if (elen < 5) {
+ DBG_8723A("short WME information element "
+ "ignored (len =%i)\n", elen);
+ return -EINVAL;
+ }
+ switch (pos[4]) {
+ case WME_OUI_SUBTYPE_INFORMATION_ELEMENT:
+ case WME_OUI_SUBTYPE_PARAMETER_ELEMENT:
+ break;
+ case WME_OUI_SUBTYPE_TSPEC_ELEMENT:
+ break;
+ default:
+ DBG_8723A("unknown WME information element "
+ "ignored (subtype =%d len =%i)\n",
+ pos[4], elen);
+ return -EINVAL;
+ }
+ break;
+ case WLAN_OUI_TYPE_MICROSOFT_WPS:
+ /* Wi-Fi Protected Setup (WPS) IE */
+ break;
+ default:
+ DBG_8723A("Unknown Microsoft information element "
+ "ignored (type =%d len =%i)\n",
+ pos[3], elen);
+ return -EINVAL;
+ }
+ break;
+
+ case OUI_BROADCOM:
+ switch (pos[3]) {
+ case VENDOR_HT_CAPAB_OUI_TYPE:
+ break;
+ default:
+ DBG_8723A("Unknown Broadcom information element "
+ "ignored (type =%d len =%i)\n", pos[3], elen);
+ return -EINVAL;
+ }
+ break;
+
+ default:
+ DBG_8723A("unknown vendor specific information element "
+ "ignored (vendor OUI %02x:%02x:%02x len =%i)\n",
+ pos[0], pos[1], pos[2], elen);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int rtw_validate_frame_ies(const u8 *start, uint len)
+{
+ const u8 *pos = start;
+ int left = len;
+ int unknown = 0;
+
+ while (left >= 2) {
+ u8 id, elen;
+
+ id = *pos++;
+ elen = *pos++;
+ left -= 2;
+
+ if (elen > left) {
+ DBG_8723A("%s: IEEE 802.11 failed (id =%d elen =%d "
+ "left =%i)\n", __func__, id, elen, left);
+ return -EINVAL;
+ }
+
+ switch (id) {
+ case WLAN_EID_SSID:
+ case WLAN_EID_SUPP_RATES:
+ case WLAN_EID_FH_PARAMS:
+ case WLAN_EID_DS_PARAMS:
+ case WLAN_EID_CF_PARAMS:
+ case WLAN_EID_TIM:
+ case WLAN_EID_IBSS_PARAMS:
+ case WLAN_EID_CHALLENGE:
+ case WLAN_EID_ERP_INFO:
+ case WLAN_EID_EXT_SUPP_RATES:
+ break;
+ case WLAN_EID_VENDOR_SPECIFIC:
+ if (rtw_validate_vendor_specific_ies(pos, elen))
+ unknown++;
+ break;
+ case WLAN_EID_RSN:
+ case WLAN_EID_PWR_CAPABILITY:
+ case WLAN_EID_SUPPORTED_CHANNELS:
+ case WLAN_EID_MOBILITY_DOMAIN:
+ case WLAN_EID_FAST_BSS_TRANSITION:
+ case WLAN_EID_TIMEOUT_INTERVAL:
+ case WLAN_EID_HT_CAPABILITY:
+ case WLAN_EID_HT_OPERATION:
+ default:
+ unknown++;
+ DBG_8723A("%s IEEE 802.11 ignored unknown element "
+ "(id =%d elen =%d)\n", __func__, id, elen);
+ break;
+ }
+
+ left -= elen;
+ pos += elen;
+ }
+
+ if (left)
+ return -EINVAL;
+
+ return 0;
+}
+#endif
+
+static int
+OnAssocReq23a(struct rtw_adapter *padapter, struct recv_frame *precv_frame)
+{
+#ifdef CONFIG_8723AU_AP_MODE
+ u16 capab_info, listen_interval;
+ struct sta_info *pstat;
+ unsigned char reassoc;
+ int i, wpa_ie_len, left;
+ unsigned char supportRate[16];
+ int supportRateNum;
+ unsigned short status = WLAN_STATUS_SUCCESS;
+ struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
+ struct security_priv *psecuritypriv = &padapter->securitypriv;
+ struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv;
+ struct mlme_ext_info *pmlmeinfo = &pmlmeext->mlmext_info;
+ struct wlan_bssid_ex *cur = &pmlmeinfo->network;
+ struct sta_priv *pstapriv = &padapter->stapriv;
+ struct sk_buff *skb = precv_frame->pkt;
+ struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *) skb->data;
+ const u8 *pos, *p, *wpa_ie, *wps_ie;
+ u8 *pframe = skb->data;
+ uint pkt_len = skb->len;
+ int r;
+
+ if ((pmlmeinfo->state & 0x03) != MSR_AP)
+ return _FAIL;
+
+ left = pkt_len - sizeof(struct ieee80211_hdr_3addr);
+ if (ieee80211_is_assoc_req(mgmt->frame_control)) {
+ reassoc = 0;
+ pos = mgmt->u.assoc_req.variable;
+ left -= offsetof(struct ieee80211_mgmt, u.assoc_req.variable);
+ } else { /* WIFI_REASSOCREQ */
+ reassoc = 1;
+ pos = mgmt->u.reassoc_req.variable;
+ left -= offsetof(struct ieee80211_mgmt, u.reassoc_req.variable);
+ }
+
+ if (left < 0) {
+ DBG_8723A("handle_assoc(reassoc =%d) - too short payload "
+ "(len =%lu)\n", reassoc, (unsigned long)pkt_len);
+ return _FAIL;
+ }
+
+ pstat = rtw_get_stainfo23a(pstapriv, mgmt->sa);
+ if (!pstat) {
+ status = WLAN_REASON_CLASS2_FRAME_FROM_NONAUTH_STA;
+ goto asoc_class2_error;
+ }
+
+ /* These two are located at the same offsets whether it's an
+ * assoc_req or a reassoc_req */
+ capab_info = get_unaligned_le16(&mgmt->u.assoc_req.capab_info);
+ listen_interval =
+ get_unaligned_le16(&mgmt->u.assoc_req.listen_interval);
+
+ DBG_8723A("%s\n", __func__);
+
+ /* check if this stat has been successfully authenticated/assocated */
+ if (!(pstat->state & WIFI_FW_AUTH_SUCCESS)) {
+ if (!(pstat->state & WIFI_FW_ASSOC_SUCCESS)) {
+ status = WLAN_REASON_CLASS2_FRAME_FROM_NONAUTH_STA;
+ goto asoc_class2_error;
+ } else {
+ pstat->state &= (~WIFI_FW_ASSOC_SUCCESS);
+ pstat->state |= WIFI_FW_ASSOC_STATE;
+ }
+ } else {
+ pstat->state &= (~WIFI_FW_AUTH_SUCCESS);
+ pstat->state |= WIFI_FW_ASSOC_STATE;
+ }
+
+ pstat->capability = capab_info;
+
+ /* now parse all ieee802_11 ie to point to elems */
+
+ if (rtw_validate_frame_ies(pos, left)) {
+ DBG_8723A("STA %pM sent invalid association request\n",
+ pstat->hwaddr);
+ status = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ goto OnAssocReq23aFail;
+ }
+
+ /* now we should check all the fields... */
+ /* checking SSID */
+ p = cfg80211_find_ie(WLAN_EID_SSID, pos, left);
+ if (!p || p[1] == 0) {
+ /* broadcast ssid, however it is not allowed in assocreq */
+ DBG_8723A("STA %pM sent invalid association request lacking an SSID\n",
+ pstat->hwaddr);
+ status = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ goto OnAssocReq23aFail;
+ } else {
+ /* check if ssid match */
+ if (memcmp(p + 2, cur->Ssid.ssid, cur->Ssid.ssid_len))
+ status = WLAN_STATUS_UNSPECIFIED_FAILURE;
+
+ if (p[1] != cur->Ssid.ssid_len)
+ status = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ }
+
+ if (status != WLAN_STATUS_SUCCESS)
+ goto OnAssocReq23aFail;
+
+ /* check if the supported rate is ok */
+ p = cfg80211_find_ie(WLAN_EID_SUPP_RATES, pos, left);
+ if (!p) {
+ DBG_8723A("Rx a sta assoc-req which supported rate is "
+ "empty!\n");
+ /* use our own rate set as statoin used */
+ /* memcpy(supportRate, AP_BSSRATE, AP_BSSRATE_LEN); */
+ /* supportRateNum = AP_BSSRATE_LEN; */
+
+ status = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ goto OnAssocReq23aFail;
+ } else {
+ memcpy(supportRate, p + 2, p[1]);
+ supportRateNum = p[1];
+
+ p = cfg80211_find_ie(WLAN_EID_EXT_SUPP_RATES, pos, left);
+ if (p) {
+ if (supportRateNum <= sizeof(supportRate)) {
+ memcpy(supportRate+supportRateNum, p + 2, p[1]);
+ supportRateNum += p[1];
+ }
+ }
+ }
+
+ /* todo: mask supportRate between AP & STA -> move to update raid */
+ /* get_matched_rate(pmlmeext, supportRate, &supportRateNum, 0); */
+
+ /* update station supportRate */
+ pstat->bssratelen = supportRateNum;
+ memcpy(pstat->bssrateset, supportRate, supportRateNum);
+ Update23aTblForSoftAP(pstat->bssrateset, pstat->bssratelen);
+
+ /* check RSN/WPA/WPS */
+ pstat->dot8021xalg = 0;
+ pstat->wpa_psk = 0;
+ pstat->wpa_group_cipher = 0;
+ pstat->wpa2_group_cipher = 0;
+ pstat->wpa_pairwise_cipher = 0;
+ pstat->wpa2_pairwise_cipher = 0;
+ memset(pstat->wpa_ie, 0, sizeof(pstat->wpa_ie));
+
+ wpa_ie = cfg80211_find_ie(WLAN_EID_RSN, pos, left);
+ if (!wpa_ie)
+ wpa_ie = cfg80211_find_vendor_ie(WLAN_OUI_MICROSOFT,
+ WLAN_OUI_TYPE_MICROSOFT_WPA,
+ pos, left);
+ if (wpa_ie) {
+ int group_cipher = 0, pairwise_cipher = 0;
+
+ wpa_ie_len = wpa_ie[1];
+ if (psecuritypriv->wpa_psk & BIT(1)) {
+ r = rtw_parse_wpa2_ie23a(wpa_ie, wpa_ie_len + 2,
+ &group_cipher,
+ &pairwise_cipher, NULL);
+ if (r == _SUCCESS) {
+ pstat->dot8021xalg = 1;/* psk, todo:802.1x */
+ pstat->wpa_psk |= BIT(1);
+
+ pstat->wpa2_group_cipher = group_cipher &
+ psecuritypriv->wpa2_group_cipher;
+ pstat->wpa2_pairwise_cipher = pairwise_cipher &
+ psecuritypriv->wpa2_pairwise_cipher;
+ } else
+ status = WLAN_STATUS_INVALID_IE;
+ } else if (psecuritypriv->wpa_psk & BIT(0)) {
+ r = rtw_parse_wpa_ie23a(wpa_ie, wpa_ie_len + 2,
+ &group_cipher, &pairwise_cipher,
+ NULL);
+ if (r == _SUCCESS) {
+ pstat->dot8021xalg = 1;/* psk, todo:802.1x */
+ pstat->wpa_psk |= BIT(0);
+
+ pstat->wpa_group_cipher = group_cipher &
+ psecuritypriv->wpa_group_cipher;
+ pstat->wpa_pairwise_cipher = pairwise_cipher &
+ psecuritypriv->wpa_pairwise_cipher;
+ } else
+ status = WLAN_STATUS_INVALID_IE;
+ } else {
+ wpa_ie = NULL;
+ wpa_ie_len = 0;
+ }
+ if (wpa_ie && status == WLAN_STATUS_SUCCESS) {
+ if (!pstat->wpa_group_cipher)
+ status = WLAN_STATUS_INVALID_GROUP_CIPHER;
+
+ if (!pstat->wpa_pairwise_cipher)
+ status = WLAN_STATUS_INVALID_PAIRWISE_CIPHER;
+ }
+ }
+
+ if (status != WLAN_STATUS_SUCCESS)
+ goto OnAssocReq23aFail;
+
+ pstat->flags &= ~(WLAN_STA_WPS | WLAN_STA_MAYBE_WPS);
+
+ wps_ie = cfg80211_find_vendor_ie(WLAN_OUI_MICROSOFT,
+ WLAN_OUI_TYPE_MICROSOFT_WPS,
+ pos, left);
+
+ if (!wpa_ie) {
+ if (wps_ie) {
+ DBG_8723A("STA included WPS IE in (Re)Association "
+ "Request - assume WPS is used\n");
+ pstat->flags |= WLAN_STA_WPS;
+ } else {
+ DBG_8723A("STA did not include WPA/RSN IE in (Re)"
+ "Association Request - possible WPS use\n");
+ pstat->flags |= WLAN_STA_MAYBE_WPS;
+ }
+ } else {
+ int copy_len;
+
+ if (psecuritypriv->wpa_psk == 0) {
+ DBG_8723A("STA %pM: WPA/RSN IE in association request, but AP don't support WPA/RSN\n",
+ pstat->hwaddr);
+
+ status = WLAN_STATUS_INVALID_IE;
+
+ goto OnAssocReq23aFail;
+ }
+
+ if (wps_ie) {
+ DBG_8723A("STA included WPS IE in (Re)Association "
+ "Request - WPS is used\n");
+ pstat->flags |= WLAN_STA_WPS;
+ copy_len = 0;
+ } else {
+ copy_len = ((wpa_ie_len + 2) > sizeof(pstat->wpa_ie)) ?
+ sizeof(pstat->wpa_ie) : (wpa_ie_len + 2);
+ }
+
+ if (copy_len > 0)
+ memcpy(pstat->wpa_ie, wpa_ie - 2, copy_len);
+ }
+
+ /* check if there is WMM IE & support WWM-PS */
+ pstat->flags &= ~WLAN_STA_WME;
+ pstat->qos_option = 0;
+ pstat->qos_info = 0;
+ pstat->has_legacy_ac = true;
+ pstat->uapsd_vo = 0;
+ pstat->uapsd_vi = 0;
+ pstat->uapsd_be = 0;
+ pstat->uapsd_bk = 0;
+ if (pmlmepriv->qos_option) {
+ const u8 *end = pos + left;
+
+ p = pos;
+
+ for (;;) {
+ left = end - p;
+ p = cfg80211_find_vendor_ie(WLAN_OUI_MICROSOFT,
+ WLAN_OUI_TYPE_MICROSOFT_WMM,
+ p, left);
+ if (p) {
+ pstat->flags |= WLAN_STA_WME;
+
+ pstat->qos_option = 1;
+ pstat->qos_info = *(p + 8);
+
+ pstat->max_sp_len =
+ (pstat->qos_info >> 5) & 0x3;
+
+ if ((pstat->qos_info & 0xf) != 0xf)
+ pstat->has_legacy_ac = true;
+ else
+ pstat->has_legacy_ac = false;
+
+ if (pstat->qos_info & 0xf) {
+ if (pstat->qos_info & BIT(0))
+ pstat->uapsd_vo = BIT(0)|BIT(1);
+ else
+ pstat->uapsd_vo = 0;
+
+ if (pstat->qos_info & BIT(1))
+ pstat->uapsd_vi = BIT(0)|BIT(1);
+ else
+ pstat->uapsd_vi = 0;
+
+ if (pstat->qos_info & BIT(2))
+ pstat->uapsd_bk = BIT(0)|BIT(1);
+ else
+ pstat->uapsd_bk = 0;
+
+ if (pstat->qos_info & BIT(3))
+ pstat->uapsd_be = BIT(0)|BIT(1);
+ else
+ pstat->uapsd_be = 0;
+
+ break;
+ }
+ } else {
+ break;
+ }
+ p = p + p[1] + 2;
+ }
+ }
+
+ /* save HT capabilities in the sta object */
+ memset(&pstat->htpriv.ht_cap, 0, sizeof(struct ieee80211_ht_cap));
+ p = cfg80211_find_ie(WLAN_EID_HT_CAPABILITY, pos, left);
+
+ if (p && p[1] >= sizeof(struct ieee80211_ht_cap)) {
+ pstat->flags |= WLAN_STA_HT;
+
+ pstat->flags |= WLAN_STA_WME;
+
+ memcpy(&pstat->htpriv.ht_cap, p + 2,
+ sizeof(struct ieee80211_ht_cap));
+ } else
+ pstat->flags &= ~WLAN_STA_HT;
+
+ if (!pmlmepriv->htpriv.ht_option && pstat->flags & WLAN_STA_HT){
+ status = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ goto OnAssocReq23aFail;
+ }
+
+ if (pstat->flags & WLAN_STA_HT &&
+ (pstat->wpa2_pairwise_cipher & WPA_CIPHER_TKIP ||
+ pstat->wpa_pairwise_cipher & WPA_CIPHER_TKIP)) {
+ DBG_8723A("HT: %pM tried to use TKIP with HT association\n",
+ pstat->hwaddr);
+
+ /* status = WLAN_STATUS_CIPHER_REJECTED_PER_POLICY; */
+ /* goto OnAssocReq23aFail; */
+ }
+
+ pstat->flags |= WLAN_STA_NONERP;
+ for (i = 0; i < pstat->bssratelen; i++) {
+ if ((pstat->bssrateset[i] & 0x7f) > 22) {
+ pstat->flags &= ~WLAN_STA_NONERP;
+ break;
+ }
+ }
+
+ if (pstat->capability & WLAN_CAPABILITY_SHORT_PREAMBLE)
+ pstat->flags |= WLAN_STA_SHORT_PREAMBLE;
+ else
+ pstat->flags &= ~WLAN_STA_SHORT_PREAMBLE;
+
+ if (status != WLAN_STATUS_SUCCESS)
+ goto OnAssocReq23aFail;
+
+ /* TODO: identify_proprietary_vendor_ie(); */
+ /* Realtek proprietary IE */
+ /* identify if this is Broadcom sta */
+ /* identify if this is ralink sta */
+ /* Customer proprietary IE */
+
+ /* get a unique AID */
+ if (pstat->aid > 0) {
+ DBG_8723A(" old AID %d\n", pstat->aid);
+ } else {
+ for (pstat->aid = 1; pstat->aid <= NUM_STA; pstat->aid++)
+ if (pstapriv->sta_aid[pstat->aid - 1] == NULL)
+ break;
+
+ if (pstat->aid > NUM_STA)
+ pstat->aid = NUM_STA;
+ if (pstat->aid > pstapriv->max_num_sta) {
+
+ pstat->aid = 0;
+
+ DBG_8723A(" no room for more AIDs\n");
+
+ status = WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA;
+
+ goto OnAssocReq23aFail;
+ } else {
+ pstapriv->sta_aid[pstat->aid - 1] = pstat;
+ DBG_8723A("allocate new AID = (%d)\n", pstat->aid);
+ }
+ }
+
+ pstat->state &= ~WIFI_FW_ASSOC_STATE;
+ pstat->state |= WIFI_FW_ASSOC_SUCCESS;
+
+ spin_lock_bh(&pstapriv->auth_list_lock);
+ if (!list_empty(&pstat->auth_list)) {
+ list_del_init(&pstat->auth_list);
+ pstapriv->auth_list_cnt--;
+ }
+ spin_unlock_bh(&pstapriv->auth_list_lock);
+
+ spin_lock_bh(&pstapriv->asoc_list_lock);
+ if (list_empty(&pstat->asoc_list)) {
+ pstat->expire_to = pstapriv->expire_to;
+ list_add_tail(&pstat->asoc_list, &pstapriv->asoc_list);
+ pstapriv->asoc_list_cnt++;
+ }
+ spin_unlock_bh(&pstapriv->asoc_list_lock);
+
+ /* now the station is qualified to join our BSS... */
+ if (pstat && pstat->state & WIFI_FW_ASSOC_SUCCESS &&
+ status == WLAN_STATUS_SUCCESS) {
+#ifdef CONFIG_8723AU_AP_MODE
+ /* 1 bss_cap_update & sta_info_update23a */
+ bss_cap_update_on_sta_join23a(padapter, pstat);
+ sta_info_update23a(padapter, pstat);
+
+ /* issue assoc rsp before notify station join event. */
+ if (ieee80211_is_assoc_req(mgmt->frame_control))
+ issue_assocrsp(padapter, status, pstat,
+ IEEE80211_STYPE_ASSOC_RESP);
+ else
+ issue_assocrsp(padapter, status, pstat,
+ IEEE80211_STYPE_REASSOC_RESP);
+
+ /* 2 - report to upper layer */
+ DBG_8723A("indicate_sta_join_event to upper layer - hostapd\n");
+ rtw_cfg80211_indicate_sta_assoc(padapter, pframe, pkt_len);
+
+ /* 3-(1) report sta add event */
+ report_add_sta_event23a(padapter, pstat->hwaddr, pstat->aid);
+#endif
+ }
+
+ return _SUCCESS;
+
+asoc_class2_error:
+
+#ifdef CONFIG_8723AU_AP_MODE
+ issue_deauth23a(padapter, mgmt->sa, status);
+#endif
+ return _FAIL;
+
+OnAssocReq23aFail:
+
+#ifdef CONFIG_8723AU_AP_MODE
+ pstat->aid = 0;
+ if (ieee80211_is_assoc_req(mgmt->frame_control))
+ issue_assocrsp(padapter, status, pstat,
+ IEEE80211_STYPE_ASSOC_RESP);
+ else
+ issue_assocrsp(padapter, status, pstat,
+ IEEE80211_STYPE_REASSOC_RESP);
+#endif
+
+#endif /* CONFIG_8723AU_AP_MODE */
+
+ return _FAIL;
+}
+
+static int
+OnAssocRsp23a(struct rtw_adapter *padapter, struct recv_frame *precv_frame)
+{
+ struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
+ struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv;
+ struct mlme_ext_info *pmlmeinfo = &pmlmeext->mlmext_info;
+ struct sk_buff *skb = precv_frame->pkt;
+ struct ieee80211_mgmt *pmgmt = (struct ieee80211_mgmt *) skb->data;
+ int res;
+ unsigned short status;
+ const u8 *p, *pie;
+ u8 *pframe = skb->data;
+ int pkt_len = skb->len;
+ int pielen;
+
+ DBG_8723A("%s\n", __func__);
+
+ /* check A1 matches or not */
+ if (!ether_addr_equal(myid(&padapter->eeprompriv), pmgmt->da))
+ return _SUCCESS;
+
+ if (!(pmlmeinfo->state & (WIFI_FW_AUTH_SUCCESS | WIFI_FW_ASSOC_STATE)))
+ return _SUCCESS;
+
+ if (pmlmeinfo->state & WIFI_FW_ASSOC_SUCCESS)
+ return _SUCCESS;
+
+ del_timer_sync(&pmlmeext->link_timer);
+
+ /* status */
+ status = le16_to_cpu(pmgmt->u.assoc_resp.status_code);
+ if (status > 0) {
+ DBG_8723A("assoc reject, status code: %d\n", status);
+ pmlmeinfo->state = MSR_NOLINK;
+ res = -4;
+ goto report_assoc_result;
+ }
+
+ /* get capabilities */
+ pmlmeinfo->capability = le16_to_cpu(pmgmt->u.assoc_resp.capab_info);
+
+ /* set slot time */
+ pmlmeinfo->slotTime = (pmlmeinfo->capability & BIT(10))? 9: 20;
+
+ /* AID */
+ res = pmlmeinfo->aid = le16_to_cpu(pmgmt->u.assoc_resp.aid) & 0x3fff;
+
+ pie = pframe + offsetof(struct ieee80211_mgmt, u.assoc_resp.variable);
+ pielen = pkt_len -
+ offsetof(struct ieee80211_mgmt, u.assoc_resp.variable);
+
+ p = cfg80211_find_ie(WLAN_EID_HT_CAPABILITY,
+ pmgmt->u.assoc_resp.variable, pielen);
+ if (p && p[1])
+ HT_caps_handler23a(padapter, p);
+
+ p = cfg80211_find_ie(WLAN_EID_HT_OPERATION,
+ pmgmt->u.assoc_resp.variable, pielen);
+ if (p && p[1])
+ HT_info_handler23a(padapter, p);
+
+ p = cfg80211_find_ie(WLAN_EID_ERP_INFO,
+ pmgmt->u.assoc_resp.variable, pielen);
+ if (p && p[1])
+ ERP_IE_handler23a(padapter, p);
+
+ pie = pframe + offsetof(struct ieee80211_mgmt, u.assoc_resp.variable);
+ while (true) {
+ p = cfg80211_find_vendor_ie(WLAN_OUI_MICROSOFT,
+ WLAN_OUI_TYPE_MICROSOFT_WMM,
+ pie, pframe + pkt_len - pie);
+ if (!p)
+ break;
+
+ pie = p + p[1] + 2;
+ /* if this IE is too short, try the next */
+ if (p[1] <= 4)
+ continue;
+ /* if this IE is WMM params, we found what we wanted */
+ if (p[6] == 1)
+ break;
+ }
+
+ if (p && p[1])
+ WMM_param_handler23a(padapter, p);
+
+ pmlmeinfo->state &= ~WIFI_FW_ASSOC_STATE;
+ pmlmeinfo->state |= WIFI_FW_ASSOC_SUCCESS;
+
+ /* Update Basic Rate Table for spec, 2010-12-28 , by thomas */
+ UpdateBrateTbl23a(padapter, pmlmeinfo->network.SupportedRates);
+
+report_assoc_result:
+ pmlmepriv->assoc_rsp_len = 0;
+ if (res > 0) {
+ kfree(pmlmepriv->assoc_rsp);
+ pmlmepriv->assoc_rsp = kmalloc(pkt_len, GFP_ATOMIC);
+ if (pmlmepriv->assoc_rsp) {
+ memcpy(pmlmepriv->assoc_rsp, pframe, pkt_len);
+ pmlmepriv->assoc_rsp_len = pkt_len;
+ }
+ } else
+ kfree(pmlmepriv->assoc_rsp);
+
+ report_join_res23a(padapter, res);
+
+ return _SUCCESS;
+}
+
+static int
+OnDeAuth23a(struct rtw_adapter *padapter, struct recv_frame *precv_frame)
+{
+ unsigned short reason;
+ struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
+ struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv;
+ struct mlme_ext_info *pmlmeinfo = &pmlmeext->mlmext_info;
+ struct sk_buff *skb = precv_frame->pkt;
+ struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *) skb->data;
+
+ if (!ether_addr_equal(mgmt->bssid,
+ get_my_bssid23a(&pmlmeinfo->network)))
+ return _SUCCESS;
+
+ reason = le16_to_cpu(mgmt->u.deauth.reason_code);
+
+ DBG_8723A("%s Reason code(%d)\n", __func__, reason);
+
+#ifdef CONFIG_8723AU_AP_MODE
+ if (check_fwstate(pmlmepriv, WIFI_AP_STATE)) {
+ struct sta_info *psta;
+ struct sta_priv *pstapriv = &padapter->stapriv;
+
+ DBG_8723A_LEVEL(_drv_always_, "ap recv deauth reason code(%d) "
+ "sta:%pM\n", reason, mgmt->sa);
+
+ psta = rtw_get_stainfo23a(pstapriv, mgmt->sa);
+ if (psta) {
+ u8 updated = 0;
+
+ spin_lock_bh(&pstapriv->asoc_list_lock);
+ if (!list_empty(&psta->asoc_list)) {
+ list_del_init(&psta->asoc_list);
+ pstapriv->asoc_list_cnt--;
+ updated = ap_free_sta23a(padapter, psta,
+ false, reason);
+ }
+ spin_unlock_bh(&pstapriv->asoc_list_lock);
+
+ associated_clients_update23a(padapter, updated);
+ }
+
+ return _SUCCESS;
+ } else
+#endif
+ {
+ DBG_8723A_LEVEL(_drv_always_, "sta recv deauth reason code(%d) "
+ "sta:%pM\n", reason, mgmt->bssid);
+
+ receive_disconnect23a(padapter, mgmt->bssid, reason);
+ }
+ pmlmepriv->LinkDetectInfo.bBusyTraffic = false;
+
+ return _SUCCESS;
+}
+
+static int
+OnDisassoc23a(struct rtw_adapter *padapter, struct recv_frame *precv_frame)
+{
+ unsigned short reason;
+ struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
+ struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv;
+ struct mlme_ext_info *pmlmeinfo = &pmlmeext->mlmext_info;
+ struct sk_buff *skb = precv_frame->pkt;
+ struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *) skb->data;
+
+ if (!ether_addr_equal(mgmt->bssid,
+ get_my_bssid23a(&pmlmeinfo->network)))
+ return _SUCCESS;
+
+ reason = le16_to_cpu(mgmt->u.disassoc.reason_code);
+
+ DBG_8723A("%s Reason code(%d)\n", __func__, reason);
+
+#ifdef CONFIG_8723AU_AP_MODE
+ if (check_fwstate(pmlmepriv, WIFI_AP_STATE)) {
+ struct sta_info *psta;
+ struct sta_priv *pstapriv = &padapter->stapriv;
+
+ DBG_8723A_LEVEL(_drv_always_, "ap recv disassoc reason code(%d)"
+ " sta:%pM\n", reason, mgmt->sa);
+
+ psta = rtw_get_stainfo23a(pstapriv, mgmt->sa);
+ if (psta) {
+ u8 updated = 0;
+
+ spin_lock_bh(&pstapriv->asoc_list_lock);
+ if (!list_empty(&psta->asoc_list)) {
+ list_del_init(&psta->asoc_list);
+ pstapriv->asoc_list_cnt--;
+ updated = ap_free_sta23a(padapter, psta,
+ false, reason);
+ }
+ spin_unlock_bh(&pstapriv->asoc_list_lock);
+
+ associated_clients_update23a(padapter, updated);
+ }
+
+ return _SUCCESS;
+ } else
+#endif
+ {
+ DBG_8723A_LEVEL(_drv_always_, "ap recv disassoc reason "
+ "code(%d) sta:%pM\n", reason, mgmt->bssid);
+
+ receive_disconnect23a(padapter, mgmt->bssid, reason);
+ }
+ pmlmepriv->LinkDetectInfo.bBusyTraffic = false;
+ return _SUCCESS;
+}
+
+static int
+OnAtim23a(struct rtw_adapter *padapter, struct recv_frame *precv_frame)
+{
+ DBG_8723A("%s\n", __func__);
+ return _SUCCESS;
+}
+
+static int
+on_action_spct23a(struct rtw_adapter *padapter, struct recv_frame *precv_frame)
+{
+ return _FAIL;
+}
+
+static int
+OnAction23a_qos(struct rtw_adapter *padapter, struct recv_frame *precv_frame)
+{
+ return _SUCCESS;
+}
+
+static int
+OnAction23a_dls(struct rtw_adapter *padapter, struct recv_frame *precv_frame)
+{
+ return _SUCCESS;
+}
+
+static int OnAction23a_back23a(struct rtw_adapter *padapter,
+ struct recv_frame *precv_frame)
+{
+ u8 *addr;
+ struct sta_info *psta = NULL;
+ struct recv_reorder_ctrl *preorder_ctrl;
+ unsigned char category, action;
+ unsigned short tid, status, capab, params, reason_code = 0;
+ struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv;
+ struct mlme_ext_info *pmlmeinfo = &pmlmeext->mlmext_info;
+ struct sk_buff *skb = precv_frame->pkt;
+ struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *) skb->data;
+ struct sta_priv *pstapriv = &padapter->stapriv;
+
+ /* check RA matches or not */
+ if (!ether_addr_equal(myid(&padapter->eeprompriv), mgmt->da))
+ return _SUCCESS;
+
+ DBG_8723A("%s\n", __func__);
+
+ if ((pmlmeinfo->state&0x03) != MSR_AP)
+ if (!(pmlmeinfo->state & WIFI_FW_ASSOC_SUCCESS))
+ return _SUCCESS;
+
+ addr = mgmt->sa;
+ psta = rtw_get_stainfo23a(pstapriv, addr);
+
+ if (!psta)
+ return _SUCCESS;
+
+ category = mgmt->u.action.category;
+ if (category == WLAN_CATEGORY_BACK) { /* representing Block Ack */
+ if (!pmlmeinfo->HT_enable)
+ return _SUCCESS;
+ /* action_code is located in the same place for all
+ action events, so pick any */
+ action = mgmt->u.action.u.wme_action.action_code;
+ DBG_8723A("%s, action =%d\n", __func__, action);
+ switch (action) {
+ case WLAN_ACTION_ADDBA_REQ: /* ADDBA request */
+ memcpy(&pmlmeinfo->ADDBA_req,
+ &mgmt->u.action.u.addba_req.dialog_token,
+ sizeof(struct ADDBA_request));
+ process_addba_req23a(padapter,
+ (u8 *)&pmlmeinfo->ADDBA_req, addr);
+ if (pmlmeinfo->bAcceptAddbaReq == true)
+ issue_action_BA23a(padapter, addr,
+ WLAN_ACTION_ADDBA_RESP, 0);
+ else {
+ /* reject ADDBA Req */
+ issue_action_BA23a(padapter, addr,
+ WLAN_ACTION_ADDBA_RESP, 37);
+ }
+ break;
+ case WLAN_ACTION_ADDBA_RESP: /* ADDBA response */
+ status = get_unaligned_le16(
+ &mgmt->u.action.u.addba_resp.status);
+ capab = get_unaligned_le16(
+ &mgmt->u.action.u.addba_resp.capab);
+ tid = (capab & IEEE80211_ADDBA_PARAM_TID_MASK) >> 2;
+ if (status == 0) { /* successful */
+ DBG_8723A("agg_enable for TID =%d\n", tid);
+ psta->htpriv.agg_enable_bitmap |= BIT(tid);
+ psta->htpriv.candidate_tid_bitmap &= ~BIT(tid);
+ } else
+ psta->htpriv.agg_enable_bitmap &= ~BIT(tid);
+ break;
+
+ case WLAN_ACTION_DELBA: /* DELBA */
+ params = get_unaligned_le16(
+ &mgmt->u.action.u.delba.params);
+ tid = params >> 12;
+
+ if (params & IEEE80211_DELBA_PARAM_INITIATOR_MASK) {
+ preorder_ctrl = &psta->recvreorder_ctrl[tid];
+ preorder_ctrl->enable = false;
+ preorder_ctrl->indicate_seq = 0xffff;
+ } else {
+ psta->htpriv.agg_enable_bitmap &= ~BIT(tid);
+ psta->htpriv.candidate_tid_bitmap &= ~BIT(tid);
+ }
+ reason_code = get_unaligned_le16(
+ &mgmt->u.action.u.delba.reason_code);
+ /* todo: how to notify the host while receiving
+ DELETE BA */
+ break;
+ default:
+ break;
+ }
+ }
+ return _SUCCESS;
+}
+
+static int on_action_public23a(struct rtw_adapter *padapter,
+ struct recv_frame *precv_frame)
+{
+ struct sk_buff *skb = precv_frame->pkt;
+ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
+ u8 *pframe = skb->data;
+ int freq, channel;
+
+ /* check RA matches or not */
+ if (!ether_addr_equal(myid(&padapter->eeprompriv), hdr->addr1))
+ return _FAIL;
+
+ channel = rtw_get_oper_ch23a(padapter);
+
+ if (channel <= RTW_CH_MAX_2G_CHANNEL)
+ freq = ieee80211_channel_to_frequency(channel,
+ IEEE80211_BAND_2GHZ);
+ else
+ freq = ieee80211_channel_to_frequency(channel,
+ IEEE80211_BAND_5GHZ);
+
+ if (cfg80211_rx_mgmt(padapter->rtw_wdev, freq, 0, pframe,
+ skb->len, 0))
+ return _SUCCESS;
+
+ return _FAIL;
+}
+
+static int
+OnAction23a_ht(struct rtw_adapter *padapter, struct recv_frame *precv_frame)
+{
+ return _SUCCESS;
+}
+
+static int
+OnAction23a_wmm(struct rtw_adapter *padapter, struct recv_frame *precv_frame)
+{
+ return _SUCCESS;
+}
+
+static int
+OnAction23a_p2p(struct rtw_adapter *padapter, struct recv_frame *precv_frame)
+{
+ return _SUCCESS;
+}
+
+static int
+OnAction23a(struct rtw_adapter *padapter, struct recv_frame *precv_frame)
+{
+ int i;
+ u8 category;
+ struct action_handler *ptable;
+ struct sk_buff *skb = precv_frame->pkt;
+ struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *) skb->data;
+
+ category = mgmt->u.action.category;
+
+ for (i = 0;
+ i < sizeof(OnAction23a_tbl) / sizeof(struct action_handler); i++) {
+ ptable = &OnAction23a_tbl[i];
+
+ if (category == ptable->num)
+ ptable->func(padapter, precv_frame);
+ }
+
+ return _SUCCESS;
+}
+
+static int DoReserved23a(struct rtw_adapter *padapter,
+ struct recv_frame *precv_frame)
+{
+ return _SUCCESS;
+}
+
+struct xmit_frame *alloc_mgtxmitframe23a(struct xmit_priv *pxmitpriv)
+{
+ struct xmit_frame *pmgntframe;
+ struct xmit_buf *pxmitbuf;
+
+ pmgntframe = rtw_alloc_xmitframe23a_ext(pxmitpriv);
+
+ if (!pmgntframe) {
+ DBG_8723A("%s(%s): alloc xmitframe fail\n", __func__,
+ pxmitpriv->adapter->pnetdev->name);
+ goto exit;
+ }
+
+ pxmitbuf = rtw_alloc_xmitbuf23a_ext(pxmitpriv);
+ if (!pxmitbuf) {
+ DBG_8723A("%s(%s): alloc xmitbuf fail\n", __func__,
+ pxmitpriv->adapter->pnetdev->name);
+ rtw_free_xmitframe23a(pxmitpriv, pmgntframe);
+ pmgntframe = NULL;
+ goto exit;
+ }
+
+ pmgntframe->frame_tag = MGNT_FRAMETAG;
+ pmgntframe->pxmitbuf = pxmitbuf;
+ pmgntframe->buf_addr = pxmitbuf->pbuf;
+ pxmitbuf->priv_data = pmgntframe;
+
+exit:
+ return pmgntframe;
+}
+
+/****************************************************************************
+
+Following are some TX functions for WiFi MLME
+
+*****************************************************************************/
+
+void update_mgnt_tx_rate23a(struct rtw_adapter *padapter, u8 rate)
+{
+ struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv;
+
+ pmlmeext->tx_rate = rate;
+ DBG_8723A("%s(): rate = %x\n", __func__, rate);
+}
+
+void update_mgntframe_attrib23a(struct rtw_adapter *padapter,
+ struct pkt_attrib *pattrib)
+{
+ struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv;
+
+ memset((u8 *)pattrib, 0, sizeof(struct pkt_attrib));
+
+ pattrib->hdrlen = 24;
+ pattrib->nr_frags = 1;
+ pattrib->priority = 7;
+ pattrib->mac_id = 0;
+ pattrib->qsel = 0x12;
+
+ pattrib->pktlen = 0;
+
+ if (pmlmeext->cur_wireless_mode & WIRELESS_11B)
+ pattrib->raid = 6;/* b mode */
+ else
+ pattrib->raid = 5;/* a/g mode */
+
+ pattrib->encrypt = 0;
+ pattrib->bswenc = false;
+
+ pattrib->qos_en = false;
+ pattrib->ht_en = false;
+ pattrib->bwmode = HT_CHANNEL_WIDTH_20;
+ pattrib->ch_offset = HAL_PRIME_CHNL_OFFSET_DONT_CARE;
+ pattrib->sgi = false;
+
+ pattrib->seqnum = pmlmeext->mgnt_seq;
+
+ pattrib->retry_ctrl = true;
+}
+
+void dump_mgntframe23a(struct rtw_adapter *padapter,
+ struct xmit_frame *pmgntframe)
+{
+ if (padapter->bSurpriseRemoved == true ||
+ padapter->bDriverStopped == true)
+ return;
+
+ rtl8723au_mgnt_xmit(padapter, pmgntframe);
+}
+
+int dump_mgntframe23a_and_wait(struct rtw_adapter *padapter,
+ struct xmit_frame *pmgntframe, int timeout_ms)
+{
+ int ret = _FAIL;
+ unsigned long irqL;
+ struct xmit_priv *pxmitpriv = &padapter->xmitpriv;
+ struct xmit_buf *pxmitbuf = pmgntframe->pxmitbuf;
+ struct submit_ctx sctx;
+
+ if (padapter->bSurpriseRemoved == true ||
+ padapter->bDriverStopped == true)
+ return ret;
+
+ rtw_sctx_init23a(&sctx, timeout_ms);
+ pxmitbuf->sctx = &sctx;
+
+ ret = rtl8723au_mgnt_xmit(padapter, pmgntframe);
+
+ if (ret == _SUCCESS)
+ ret = rtw_sctx_wait23a(&sctx);
+
+ spin_lock_irqsave(&pxmitpriv->lock_sctx, irqL);
+ pxmitbuf->sctx = NULL;
+ spin_unlock_irqrestore(&pxmitpriv->lock_sctx, irqL);
+
+ return ret;
+}
+
+int dump_mgntframe23a_and_wait_ack23a(struct rtw_adapter *padapter,
+ struct xmit_frame *pmgntframe)
+{
+ int ret = _FAIL;
+ u32 timeout_ms = 500;/* 500ms */
+ struct xmit_priv *pxmitpriv = &padapter->xmitpriv;
+
+ if (padapter->bSurpriseRemoved == true ||
+ padapter->bDriverStopped == true)
+ return _FAIL;
+
+ mutex_lock(&pxmitpriv->ack_tx_mutex);
+ pxmitpriv->ack_tx = true;
+
+ pmgntframe->ack_report = 1;
+ if (rtl8723au_mgnt_xmit(padapter, pmgntframe) == _SUCCESS)
+ ret = rtw_ack_tx_wait23a(pxmitpriv, timeout_ms);
+
+ pxmitpriv->ack_tx = false;
+ mutex_unlock(&pxmitpriv->ack_tx_mutex);
+
+ return ret;
+}
+
+static int update_hidden_ssid(u8 *ies, u32 ies_len, u8 hidden_ssid_mode)
+{
+ u8 *ssid_ie;
+ int ssid_len_ori;
+ int len_diff = 0;
+ u8 *next_ie;
+ u32 remain_len;
+
+ ssid_ie = rtw_get_ie23a(ies, WLAN_EID_SSID, &ssid_len_ori, ies_len);
+
+ /* DBG_8723A("%s hidden_ssid_mode:%u, ssid_ie:%p, ssid_len_ori:%d\n",
+ __func__, hidden_ssid_mode, ssid_ie, ssid_len_ori); */
+
+ if (ssid_ie && ssid_len_ori > 0) {
+ switch (hidden_ssid_mode) {
+ case 1:
+ next_ie = ssid_ie + 2 + ssid_len_ori;
+ remain_len = ies_len -(next_ie-ies);
+
+ ssid_ie[1] = 0;
+ memcpy(ssid_ie+2, next_ie, remain_len);
+ len_diff -= ssid_len_ori;
+
+ break;
+ case 2:
+ memset(&ssid_ie[2], 0, ssid_len_ori);
+ break;
+ default:
+ break;
+ }
+ }
+
+ return len_diff;
+}
+
+void issue_beacon23a(struct rtw_adapter *padapter, int timeout_ms)
+{
+ struct xmit_frame *pmgntframe;
+ struct pkt_attrib *pattrib;
+ unsigned char *pframe;
+ struct ieee80211_mgmt *mgmt;
+ unsigned int rate_len;
+ struct xmit_priv *pxmitpriv = &padapter->xmitpriv;
+ struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
+ struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv;
+ struct mlme_ext_info *pmlmeinfo = &pmlmeext->mlmext_info;
+ struct wlan_bssid_ex *cur_network = &pmlmeinfo->network;
+ u8 bc_addr[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
+ const u8 *wps_ie;
+ u8 sr = 0;
+ int len_diff;
+
+ /* DBG_8723A("%s\n", __func__); */
+
+ pmgntframe = alloc_mgtxmitframe23a(pxmitpriv);
+ if (!pmgntframe) {
+ DBG_8723A("%s, alloc mgnt frame fail\n", __func__);
+ return;
+ }
+#ifdef CONFIG_8723AU_AP_MODE
+ spin_lock_bh(&pmlmepriv->bcn_update_lock);
+#endif
+
+ /* update attribute */
+ pattrib = &pmgntframe->attrib;
+ update_mgntframe_attrib23a(padapter, pattrib);
+ pattrib->qsel = 0x10;
+
+ memset(pmgntframe->buf_addr, 0, WLANHDR_OFFSET + TXDESC_OFFSET);
+
+ pframe = (u8 *)(pmgntframe->buf_addr) + TXDESC_OFFSET;
+ mgmt = (struct ieee80211_mgmt *)pframe;
+
+ mgmt->frame_control =
+ cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_BEACON);
+ mgmt->seq_ctrl = 0;
+
+ ether_addr_copy(mgmt->da, bc_addr);
+ ether_addr_copy(mgmt->sa, myid(&padapter->eeprompriv));
+ ether_addr_copy(mgmt->bssid, get_my_bssid23a(cur_network));
+
+ /* timestamp will be inserted by hardware */
+
+ put_unaligned_le16(cur_network->beacon_interval,
+ &mgmt->u.beacon.beacon_int);
+
+ put_unaligned_le16(cur_network->capability,
+ &mgmt->u.beacon.capab_info);
+
+ pframe = mgmt->u.beacon.variable;
+ pattrib->pktlen = offsetof(struct ieee80211_mgmt, u.beacon.variable);
+
+ if ((pmlmeinfo->state & 0x03) == MSR_AP) {
+ u8 *iebuf;
+ int buflen;
+ /* DBG_8723A("ie len =%d\n", cur_network->IELength); */
+ memcpy(pframe, cur_network->IEs, cur_network->IELength);
+ len_diff = update_hidden_ssid(pframe, cur_network->IELength,
+ pmlmeinfo->hidden_ssid_mode);
+ pframe += (cur_network->IELength+len_diff);
+ pattrib->pktlen += (cur_network->IELength+len_diff);
+
+ iebuf = mgmt->u.beacon.variable;
+ buflen = pattrib->pktlen -
+ offsetof(struct ieee80211_mgmt, u.beacon.variable);
+ wps_ie = cfg80211_find_vendor_ie(WLAN_OUI_MICROSOFT,
+ WLAN_OUI_TYPE_MICROSOFT_WPS,
+ iebuf, buflen);
+
+ if (wps_ie && wps_ie[1] > 0) {
+ rtw_get_wps_attr_content23a(wps_ie, wps_ie[1],
+ WPS_ATTR_SELECTED_REGISTRAR,
+ (u8 *)&sr);
+ }
+ if (sr != 0)
+ set_fwstate(pmlmepriv, WIFI_UNDER_WPS);
+ else
+ _clr_fwstate_(pmlmepriv, WIFI_UNDER_WPS);
+
+ goto _issue_bcn;
+ }
+
+ /* SSID */
+ pframe = rtw_set_ie23a(pframe, WLAN_EID_SSID,
+ cur_network->Ssid.ssid_len,
+ cur_network->Ssid.ssid, &pattrib->pktlen);
+
+ /* supported rates... */
+ rate_len = rtw_get_rateset_len23a(cur_network->SupportedRates);
+ pframe = rtw_set_ie23a(pframe, WLAN_EID_SUPP_RATES,
+ ((rate_len > 8)? 8: rate_len),
+ cur_network->SupportedRates, &pattrib->pktlen);
+
+ /* DS parameter set */
+ pframe = rtw_set_ie23a(pframe, WLAN_EID_DS_PARAMS, 1, (unsigned char *)
+ &cur_network->DSConfig, &pattrib->pktlen);
+
+ /* if ((pmlmeinfo->state&0x03) == MSR_ADHOC) */
+ {
+ u8 erpinfo = 0;
+ u32 ATIMWindow;
+ /* IBSS Parameter Set... */
+ /* ATIMWindow = cur->ATIMWindow; */
+ ATIMWindow = 0;
+ pframe = rtw_set_ie23a(pframe, WLAN_EID_IBSS_PARAMS, 2,
+ (unsigned char *)&ATIMWindow,
+ &pattrib->pktlen);
+
+ /* ERP IE */
+ pframe = rtw_set_ie23a(pframe, WLAN_EID_ERP_INFO, 1,
+ &erpinfo, &pattrib->pktlen);
+ }
+
+ /* EXTERNDED SUPPORTED RATE */
+ if (rate_len > 8)
+ pframe = rtw_set_ie23a(pframe, WLAN_EID_EXT_SUPP_RATES,
+ rate_len - 8,
+ cur_network->SupportedRates + 8,
+ &pattrib->pktlen);
+
+ /* todo:HT for adhoc */
+
+_issue_bcn:
+
+#ifdef CONFIG_8723AU_AP_MODE
+ pmlmepriv->update_bcn = false;
+
+ spin_unlock_bh(&pmlmepriv->bcn_update_lock);
+#endif
+
+ if ((pattrib->pktlen + TXDESC_SIZE) > 512) {
+ DBG_8723A("beacon frame too large\n");
+ return;
+ }
+
+ pattrib->last_txcmdsz = pattrib->pktlen;
+
+ /* DBG_8723A("issue bcn_sz =%d\n", pattrib->last_txcmdsz); */
+ if (timeout_ms > 0)
+ dump_mgntframe23a_and_wait(padapter, pmgntframe, timeout_ms);
+ else
+ dump_mgntframe23a(padapter, pmgntframe);
+}
+
+static void issue_probersp(struct rtw_adapter *padapter, unsigned char *da,
+ u8 is_valid_p2p_probereq)
+{
+ struct xmit_frame *pmgntframe;
+ struct pkt_attrib *pattrib;
+ unsigned char *pframe;
+ struct ieee80211_mgmt *mgmt;
+ unsigned char *mac, *bssid;
+ struct xmit_priv *pxmitpriv = &padapter->xmitpriv;
+#ifdef CONFIG_8723AU_AP_MODE
+ const u8 *pwps_ie;
+ u8 *ssid_ie;
+ int ssid_ielen;
+ int ssid_ielen_diff;
+ u8 buf[MAX_IE_SZ];
+#endif
+ struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv;
+ struct mlme_ext_info *pmlmeinfo = &pmlmeext->mlmext_info;
+ struct wlan_bssid_ex *cur_network = &pmlmeinfo->network;
+ unsigned int rate_len;
+
+ /* DBG_8723A("%s\n", __func__); */
+
+ if (cur_network->IELength > MAX_IE_SZ)
+ return;
+
+ pmgntframe = alloc_mgtxmitframe23a(pxmitpriv);
+ if (!pmgntframe) {
+ DBG_8723A("%s, alloc mgnt frame fail\n", __func__);
+ return;
+ }
+
+ /* update attribute */
+ pattrib = &pmgntframe->attrib;
+ update_mgntframe_attrib23a(padapter, pattrib);
+
+ memset(pmgntframe->buf_addr, 0, WLANHDR_OFFSET + TXDESC_OFFSET);
+
+ pframe = (u8 *)pmgntframe->buf_addr + TXDESC_OFFSET;
+ mgmt = (struct ieee80211_mgmt *)pframe;
+
+ mac = myid(&padapter->eeprompriv);
+ bssid = cur_network->MacAddress;
+
+ mgmt->frame_control =
+ cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_PROBE_RESP);
+
+ ether_addr_copy(mgmt->da, da);
+ ether_addr_copy(mgmt->sa, mac);
+ ether_addr_copy(mgmt->bssid, bssid);
+
+ mgmt->seq_ctrl = cpu_to_le16(IEEE80211_SN_TO_SEQ(pmlmeext->mgnt_seq));
+ pmlmeext->mgnt_seq++;
+
+ pattrib->hdrlen = sizeof(struct ieee80211_hdr_3addr);
+
+ /* timestamp will be inserted by hardware */
+ put_unaligned_le16(cur_network->beacon_interval,
+ &mgmt->u.probe_resp.beacon_int);
+
+ put_unaligned_le16(cur_network->capability,
+ &mgmt->u.probe_resp.capab_info);
+
+ pframe = mgmt->u.probe_resp.variable;
+ pattrib->pktlen =
+ offsetof(struct ieee80211_mgmt, u.probe_resp.variable);
+
+ /* below for ad-hoc mode */
+
+#ifdef CONFIG_8723AU_AP_MODE
+ if ((pmlmeinfo->state & 0x03) == MSR_AP) {
+ pwps_ie = cfg80211_find_vendor_ie(WLAN_OUI_MICROSOFT,
+ WLAN_OUI_TYPE_MICROSOFT_WPS,
+ cur_network->IEs,
+ cur_network->IELength);
+
+ memcpy(pframe, cur_network->IEs, cur_network->IELength);
+ pframe += cur_network->IELength;
+ pattrib->pktlen += cur_network->IELength;
+
+ /* retrieve SSID IE from cur_network->Ssid */
+
+ ssid_ie = rtw_get_ie23a(mgmt->u.probe_resp.variable,
+ WLAN_EID_SSID, &ssid_ielen,
+ pframe - mgmt->u.probe_resp.variable);
+
+ ssid_ielen_diff = cur_network->Ssid.ssid_len - ssid_ielen;
+
+ if (ssid_ie && cur_network->Ssid.ssid_len) {
+ uint remainder_ielen;
+ u8 *remainder_ie;
+
+ remainder_ie = ssid_ie + 2;
+
+ remainder_ielen = pframe - remainder_ie;
+
+ DBG_8723A_LEVEL(_drv_warning_, "%s(%s): "
+ "remainder_ielen > MAX_IE_SZ\n",
+ __func__, padapter->pnetdev->name);
+ if (remainder_ielen > MAX_IE_SZ)
+ remainder_ielen = MAX_IE_SZ;
+
+ memcpy(buf, remainder_ie, remainder_ielen);
+ memcpy(remainder_ie + ssid_ielen_diff, buf,
+ remainder_ielen);
+ *(ssid_ie + 1) = cur_network->Ssid.ssid_len;
+ memcpy(ssid_ie + 2, cur_network->Ssid.ssid,
+ cur_network->Ssid.ssid_len);
+
+ pframe += ssid_ielen_diff;
+ pattrib->pktlen += ssid_ielen_diff;
+ }
+ } else
+#endif
+ {
+ /* SSID */
+ pframe = rtw_set_ie23a(pframe, WLAN_EID_SSID,
+ cur_network->Ssid.ssid_len,
+ cur_network->Ssid.ssid,
+ &pattrib->pktlen);
+
+ /* supported rates... */
+ rate_len = rtw_get_rateset_len23a(cur_network->SupportedRates);
+ pframe = rtw_set_ie23a(pframe, WLAN_EID_SUPP_RATES,
+ ((rate_len > 8)? 8: rate_len),
+ cur_network->SupportedRates,
+ &pattrib->pktlen);
+
+ /* DS parameter set */
+ pframe = rtw_set_ie23a(pframe, WLAN_EID_DS_PARAMS, 1,
+ (unsigned char *)&cur_network->DSConfig,
+ &pattrib->pktlen);
+
+ if ((pmlmeinfo->state & 0x03) == MSR_ADHOC) {
+ u8 erpinfo = 0;
+ u32 ATIMWindow;
+ /* IBSS Parameter Set... */
+ /* ATIMWindow = cur->ATIMWindow; */
+ ATIMWindow = 0;
+ pframe = rtw_set_ie23a(pframe, WLAN_EID_IBSS_PARAMS, 2,
+ (unsigned char *)&ATIMWindow,
+ &pattrib->pktlen);
+
+ /* ERP IE */
+ pframe = rtw_set_ie23a(pframe, WLAN_EID_ERP_INFO, 1,
+ &erpinfo, &pattrib->pktlen);
+ }
+
+ /* EXTERNDED SUPPORTED RATE */
+ if (rate_len > 8)
+ pframe = rtw_set_ie23a(pframe, WLAN_EID_EXT_SUPP_RATES,
+ rate_len - 8,
+ cur_network->SupportedRates + 8,
+ &pattrib->pktlen);
+
+ /* todo:HT for adhoc */
+ }
+
+ pattrib->last_txcmdsz = pattrib->pktlen;
+
+ dump_mgntframe23a(padapter, pmgntframe);
+
+ return;
+}
+
+static int _issue_probereq(struct rtw_adapter *padapter,
+ struct cfg80211_ssid *pssid, u8 *da, int wait_ack)
+{
+ int ret = _FAIL;
+ struct xmit_frame *pmgntframe;
+ struct pkt_attrib *pattrib;
+ unsigned char *pframe;
+ struct ieee80211_hdr *pwlanhdr;
+ unsigned char *mac;
+ unsigned char bssrate[NumRates];
+ struct xmit_priv *pxmitpriv = &padapter->xmitpriv;
+ struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
+ struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv;
+ int bssrate_len = 0;
+ u8 bc_addr[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
+
+ RT_TRACE(_module_rtl871x_mlme_c_, _drv_notice_,
+ "+%s\n", __func__);
+
+ pmgntframe = alloc_mgtxmitframe23a(pxmitpriv);
+ if (!pmgntframe)
+ goto exit;
+
+ /* update attribute */
+ pattrib = &pmgntframe->attrib;
+ update_mgntframe_attrib23a(padapter, pattrib);
+
+ memset(pmgntframe->buf_addr, 0, WLANHDR_OFFSET + TXDESC_OFFSET);
+
+ pframe = (u8 *)(pmgntframe->buf_addr) + TXDESC_OFFSET;
+ pwlanhdr = (struct ieee80211_hdr *)pframe;
+
+ mac = myid(&padapter->eeprompriv);
+
+ pwlanhdr->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
+ IEEE80211_STYPE_PROBE_REQ);
+
+ if (da) {
+ /* unicast probe request frame */
+ ether_addr_copy(pwlanhdr->addr1, da);
+ ether_addr_copy(pwlanhdr->addr3, da);
+ } else {
+ /* broadcast probe request frame */
+ ether_addr_copy(pwlanhdr->addr1, bc_addr);
+ ether_addr_copy(pwlanhdr->addr3, bc_addr);
+ }
+
+ ether_addr_copy(pwlanhdr->addr2, mac);
+
+ pwlanhdr->seq_ctrl =
+ cpu_to_le16(IEEE80211_SN_TO_SEQ(pmlmeext->mgnt_seq));
+
+ pmlmeext->mgnt_seq++;
+
+ pframe += sizeof (struct ieee80211_hdr_3addr);
+ pattrib->pktlen = sizeof (struct ieee80211_hdr_3addr);
+
+ if (pssid)
+ pframe = rtw_set_ie23a(pframe, WLAN_EID_SSID, pssid->ssid_len,
+ pssid->ssid, &pattrib->pktlen);
+ else
+ pframe = rtw_set_ie23a(pframe, WLAN_EID_SSID, 0, NULL,
+ &pattrib->pktlen);
+
+ get_rate_set23a(padapter, bssrate, &bssrate_len);
+
+ if (bssrate_len > 8) {
+ pframe = rtw_set_ie23a(pframe, WLAN_EID_SUPP_RATES, 8,
+ bssrate, &pattrib->pktlen);
+ pframe = rtw_set_ie23a(pframe, WLAN_EID_EXT_SUPP_RATES,
+ (bssrate_len - 8), (bssrate + 8),
+ &pattrib->pktlen);
+ } else {
+ pframe = rtw_set_ie23a(pframe, WLAN_EID_SUPP_RATES,
+ bssrate_len, bssrate, &pattrib->pktlen);
+ }
+
+ /* add wps_ie for wps2.0 */
+ if (pmlmepriv->wps_probe_req_ie_len>0 && pmlmepriv->wps_probe_req_ie) {
+ memcpy(pframe, pmlmepriv->wps_probe_req_ie,
+ pmlmepriv->wps_probe_req_ie_len);
+ pframe += pmlmepriv->wps_probe_req_ie_len;
+ pattrib->pktlen += pmlmepriv->wps_probe_req_ie_len;
+ }
+
+ pattrib->last_txcmdsz = pattrib->pktlen;
+
+ RT_TRACE(_module_rtl871x_mlme_c_, _drv_notice_,
+ "issuing probe_req, tx_len =%d\n", pattrib->last_txcmdsz);
+
+ if (wait_ack) {
+ ret = dump_mgntframe23a_and_wait_ack23a(padapter, pmgntframe);
+ } else {
+ dump_mgntframe23a(padapter, pmgntframe);
+ ret = _SUCCESS;
+ }
+
+exit:
+ return ret;
+}
+
+static inline void issue_probereq(struct rtw_adapter *padapter,
+ struct cfg80211_ssid *pssid, u8 *da)
+{
+ _issue_probereq(padapter, pssid, da, false);
+}
+
+static int issue_probereq_ex(struct rtw_adapter *padapter,
+ struct cfg80211_ssid *pssid, u8 *da,
+ int try_cnt, int wait_ms)
+{
+ int ret;
+ int i = 0;
+ unsigned long start = jiffies;
+
+ do {
+ ret = _issue_probereq(padapter, pssid, da,
+ wait_ms > 0 ? true : false);
+
+ i++;
+
+ if (padapter->bDriverStopped || padapter->bSurpriseRemoved)
+ break;
+
+ if (i < try_cnt && wait_ms > 0 && ret == _FAIL)
+ msleep(wait_ms);
+
+ } while((i < try_cnt) && ((ret == _FAIL) || (wait_ms == 0)));
+
+ if (ret != _FAIL) {
+ ret = _SUCCESS;
+ goto exit;
+ }
+
+ if (try_cnt && wait_ms) {
+ if (da)
+ DBG_8723A("%s(%s): to %pM, ch:%u%s, %d/%d in %u ms\n",
+ __func__, padapter->pnetdev->name,
+ da, rtw_get_oper_ch23a(padapter),
+ ret == _SUCCESS ? ", acked" : "", i, try_cnt,
+ jiffies_to_msecs(jiffies - start));
+ else
+ DBG_8723A("%s(%s):, ch:%u%s, %d/%d in %u ms\n",
+ __func__, padapter->pnetdev->name,
+ rtw_get_oper_ch23a(padapter),
+ ret == _SUCCESS ? ", acked" : "", i, try_cnt,
+ jiffies_to_msecs(jiffies - start));
+ }
+exit:
+ return ret;
+}
+
+/* if psta == NULL, indiate we are station(client) now... */
+static void issue_auth(struct rtw_adapter *padapter, struct sta_info *psta,
+ unsigned short status)
+{
+ struct xmit_frame *pmgntframe;
+ struct pkt_attrib *pattrib;
+ unsigned char *pframe;
+ struct ieee80211_mgmt *mgmt;
+ unsigned int val32;
+ u16 auth_algo;
+ int use_shared_key = 0;
+ struct xmit_priv *pxmitpriv = &padapter->xmitpriv;
+ struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv;
+ struct mlme_ext_info *pmlmeinfo = &pmlmeext->mlmext_info;
+
+ pmgntframe = alloc_mgtxmitframe23a(pxmitpriv);
+ if (!pmgntframe)
+ return;
+
+ /* update attribute */
+ pattrib = &pmgntframe->attrib;
+ update_mgntframe_attrib23a(padapter, pattrib);
+
+ memset(pmgntframe->buf_addr, 0, WLANHDR_OFFSET + TXDESC_OFFSET);
+
+ pframe = (u8 *)(pmgntframe->buf_addr) + TXDESC_OFFSET;
+ mgmt = (struct ieee80211_mgmt *)pframe;
+
+ mgmt->frame_control =
+ cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_AUTH);
+ mgmt->seq_ctrl = cpu_to_le16(IEEE80211_SN_TO_SEQ(pmlmeext->mgnt_seq));
+ pmlmeext->mgnt_seq++;
+
+ pattrib->pktlen = offsetof(struct ieee80211_mgmt, u.auth.variable);
+
+ if (psta) { /* for AP mode */
+#ifdef CONFIG_8723AU_AP_MODE
+ unsigned short val16;
+
+ ether_addr_copy(mgmt->da, psta->hwaddr);
+ ether_addr_copy(mgmt->sa, myid(&padapter->eeprompriv));
+ ether_addr_copy(mgmt->bssid, myid(&padapter->eeprompriv));
+
+ /* setting auth algo number */
+ val16 = (u16)psta->authalg;
+
+ if (status != WLAN_STATUS_SUCCESS)
+ val16 = 0;
+
+ if (val16)
+ use_shared_key = 1;
+
+ mgmt->u.auth.auth_alg = cpu_to_le16(val16);
+
+ /* setting auth seq number */
+ mgmt->u.auth.auth_transaction =
+ cpu_to_le16((u16)psta->auth_seq);
+
+ /* setting status code... */
+ mgmt->u.auth.status_code = cpu_to_le16(status);
+
+ pframe = mgmt->u.auth.variable;
+ /* added challenging text... */
+ if ((psta->auth_seq == 2) &&
+ (psta->state & WIFI_FW_AUTH_STATE) && (use_shared_key == 1))
+ pframe = rtw_set_ie23a(pframe, WLAN_EID_CHALLENGE, 128,
+ psta->chg_txt, &pattrib->pktlen);
+#endif
+ } else {
+ struct ieee80211_mgmt *iv_mgmt;
+
+ ether_addr_copy(mgmt->da, get_my_bssid23a(&pmlmeinfo->network));
+ ether_addr_copy(mgmt->sa, myid(&padapter->eeprompriv));
+ ether_addr_copy(mgmt->bssid,
+ get_my_bssid23a(&pmlmeinfo->network));
+
+ /* setting auth algo number */
+ /* 0:OPEN System, 1:Shared key */
+ if (pmlmeinfo->auth_algo == dot11AuthAlgrthm_Shared) {
+ use_shared_key = 1;
+ auth_algo = WLAN_AUTH_SHARED_KEY;
+ } else
+ auth_algo = WLAN_AUTH_OPEN;
+
+ /* DBG_8723A("%s auth_algo = %s auth_seq =%d\n", __func__,
+ (pmlmeinfo->auth_algo == 0)?"OPEN":"SHARED",
+ pmlmeinfo->auth_seq); */
+
+ /* setting IV for auth seq #3 */
+ if ((pmlmeinfo->auth_seq == 3) &&
+ (pmlmeinfo->state & WIFI_FW_AUTH_STATE) &&
+ (use_shared_key == 1)) {
+ u32 *piv = (u32 *)&mgmt->u.auth;
+
+ iv_mgmt = (struct ieee80211_mgmt *)(pframe + 4);
+ /* DBG_8723A("==> iv(%d), key_index(%d)\n",
+ pmlmeinfo->iv, pmlmeinfo->key_index); */
+ val32 = (pmlmeinfo->iv & 0x3fffffff) |
+ (pmlmeinfo->key_index << 30);
+ pmlmeinfo->iv++;
+ put_unaligned_le32(val32, piv);
+
+ pattrib->pktlen += 4;
+
+ pattrib->iv_len = IEEE80211_WEP_IV_LEN;
+ } else
+ iv_mgmt = mgmt;
+
+ iv_mgmt->u.auth.auth_alg = cpu_to_le16(auth_algo);
+
+ /* setting auth seq number */
+ iv_mgmt->u.auth.auth_transaction =
+ cpu_to_le16(pmlmeinfo->auth_seq);
+
+ /* setting status code... */
+ iv_mgmt->u.auth.status_code = cpu_to_le16(status);
+
+ pframe = iv_mgmt->u.auth.variable;
+
+ /* then checking to see if sending challenging text... */
+ if ((pmlmeinfo->auth_seq == 3) &&
+ (pmlmeinfo->state & WIFI_FW_AUTH_STATE) &&
+ (use_shared_key == 1)) {
+ pframe = rtw_set_ie23a(pframe, WLAN_EID_CHALLENGE, 128,
+ pmlmeinfo->chg_txt,
+ &pattrib->pktlen);
+
+ mgmt->frame_control |=
+ cpu_to_le16(IEEE80211_FCTL_PROTECTED);
+
+ pattrib->hdrlen = sizeof(struct ieee80211_hdr_3addr);
+
+ pattrib->encrypt = WLAN_CIPHER_SUITE_WEP40;
+
+ pattrib->icv_len = IEEE80211_WEP_ICV_LEN;
+
+ pattrib->pktlen += pattrib->icv_len;
+ }
+ }
+
+ pattrib->last_txcmdsz = pattrib->pktlen;
+
+ rtw_wep_encrypt23a(padapter, pmgntframe);
+ DBG_8723A("%s\n", __func__);
+ dump_mgntframe23a(padapter, pmgntframe);
+
+ return;
+}
+
+#ifdef CONFIG_8723AU_AP_MODE
+static void issue_assocrsp(struct rtw_adapter *padapter, unsigned short status,
+ struct sta_info *pstat, u16 pkt_type)
+{
+ struct xmit_frame *pmgntframe;
+ struct ieee80211_mgmt *mgmt;
+ struct pkt_attrib *pattrib;
+ unsigned char *pframe;
+ struct xmit_priv *pxmitpriv = &padapter->xmitpriv;
+ struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
+ struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv;
+ struct mlme_ext_info *pmlmeinfo = &pmlmeext->mlmext_info;
+ struct wlan_bssid_ex *pnetwork = &pmlmeinfo->network;
+ const u8 *p;
+ u8 *ie = pnetwork->IEs;
+
+ DBG_8723A("%s\n", __func__);
+
+ pmgntframe = alloc_mgtxmitframe23a(pxmitpriv);
+ if (!pmgntframe)
+ return;
+
+ /* update attribute */
+ pattrib = &pmgntframe->attrib;
+ update_mgntframe_attrib23a(padapter, pattrib);
+
+ memset(pmgntframe->buf_addr, 0, WLANHDR_OFFSET + TXDESC_OFFSET);
+
+ pframe = (u8 *)(pmgntframe->buf_addr) + TXDESC_OFFSET;
+ mgmt = (struct ieee80211_mgmt *)pframe;
+
+ mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | pkt_type);
+
+ ether_addr_copy(mgmt->da, pstat->hwaddr);
+ ether_addr_copy(mgmt->sa, myid(&padapter->eeprompriv));
+ ether_addr_copy(mgmt->bssid, get_my_bssid23a(&pmlmeinfo->network));
+
+ mgmt->seq_ctrl = cpu_to_le16(IEEE80211_SN_TO_SEQ(pmlmeext->mgnt_seq));
+
+ pmlmeext->mgnt_seq++;
+
+ pattrib->hdrlen = sizeof(struct ieee80211_hdr_3addr);
+ pattrib->pktlen =
+ offsetof(struct ieee80211_mgmt, u.assoc_resp.variable);
+
+ mgmt->u.assoc_resp.capab_info = cpu_to_le16(pnetwork->capability);
+ mgmt->u.assoc_resp.status_code = cpu_to_le16(status);
+ mgmt->u.assoc_resp.aid = cpu_to_le16(pstat->aid | BIT(14) | BIT(15));
+
+ pframe = mgmt->u.assoc_resp.variable;
+
+ if (pstat->bssratelen <= 8) {
+ pframe = rtw_set_ie23a(pframe, WLAN_EID_SUPP_RATES,
+ pstat->bssratelen, pstat->bssrateset,
+ &pattrib->pktlen);
+ } else {
+ pframe = rtw_set_ie23a(pframe, WLAN_EID_SUPP_RATES, 8,
+ pstat->bssrateset, &pattrib->pktlen);
+ pframe = rtw_set_ie23a(pframe, WLAN_EID_EXT_SUPP_RATES,
+ pstat->bssratelen - 8,
+ pstat->bssrateset + 8, &pattrib->pktlen);
+ }
+
+ if (pstat->flags & WLAN_STA_HT && pmlmepriv->htpriv.ht_option) {
+ /* FILL HT CAP INFO IE */
+ /* p = hostapd_eid_ht_capabilities_info(hapd, p); */
+ p = cfg80211_find_ie(WLAN_EID_HT_CAPABILITY, ie,
+ pnetwork->IELength);
+ if (p && p[1]) {
+ memcpy(pframe, p, p[1] + 2);
+ pframe += (p[1] + 2);
+ pattrib->pktlen += (p[1] + 2);
+ }
+
+ /* FILL HT ADD INFO IE */
+ /* p = hostapd_eid_ht_operation(hapd, p); */
+ p = cfg80211_find_ie(WLAN_EID_HT_OPERATION, ie,
+ pnetwork->IELength);
+ if (p && p[1] > 0) {
+ memcpy(pframe, p, p[1] + 2);
+ pframe += (p[1] + 2);
+ pattrib->pktlen += (p[1] + 2);
+ }
+ }
+
+ /* FILL WMM IE */
+ if (pstat->flags & WLAN_STA_WME && pmlmepriv->qos_option) {
+ unsigned char WMM_PARA_IE[] = {0x00, 0x50, 0xf2, 0x02,
+ 0x01, 0x01};
+ int ie_len = 0;
+
+ for (p = ie; ; p += (ie_len + 2)) {
+ p = cfg80211_find_ie(WLAN_EID_VENDOR_SPECIFIC, p,
+ pnetwork->IELength - (ie_len + 2));
+ if (p)
+ ie_len = p[1];
+ else
+ ie_len = 0;
+ if (p && !memcmp(p + 2, WMM_PARA_IE, 6)) {
+ memcpy(pframe, p, ie_len + 2);
+ pframe += (ie_len + 2);
+ pattrib->pktlen += (ie_len + 2);
+
+ break;
+ }
+
+ if (!p || ie_len == 0)
+ break;
+ }
+ }
+
+ if (pmlmeinfo->assoc_AP_vendor == HT_IOT_PEER_REALTEK) {
+ pframe = rtw_set_ie23a(pframe, WLAN_EID_VENDOR_SPECIFIC, 6,
+ REALTEK_96B_IE, &pattrib->pktlen);
+ }
+
+ pattrib->last_txcmdsz = pattrib->pktlen;
+
+ dump_mgntframe23a(padapter, pmgntframe);
+}
+#endif
+
+static void issue_assocreq(struct rtw_adapter *padapter)
+{
+ int ret = _FAIL;
+ struct xmit_frame *pmgntframe;
+ struct pkt_attrib *pattrib;
+ unsigned char *pframe;
+ const u8 *p;
+ struct ieee80211_mgmt *mgmt;
+ unsigned int i, j, index = 0;
+ unsigned char rf_type, bssrate[NumRates], sta_bssrate[NumRates];
+ struct registry_priv *pregpriv = &padapter->registrypriv;
+ struct xmit_priv *pxmitpriv = &padapter->xmitpriv;
+ struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
+ struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv;
+ struct mlme_ext_info *pmlmeinfo = &pmlmeext->mlmext_info;
+ int bssrate_len = 0, sta_bssrate_len = 0, pie_len;
+ u8 *pie;
+
+ pmgntframe = alloc_mgtxmitframe23a(pxmitpriv);
+ if (!pmgntframe)
+ goto exit;
+
+ /* update attribute */
+ pattrib = &pmgntframe->attrib;
+ update_mgntframe_attrib23a(padapter, pattrib);
+
+ memset(pmgntframe->buf_addr, 0, WLANHDR_OFFSET + TXDESC_OFFSET);
+
+ pframe = (u8 *)pmgntframe->buf_addr + TXDESC_OFFSET;
+ mgmt = (struct ieee80211_mgmt *)pframe;
+
+ mgmt->frame_control =
+ cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_ASSOC_REQ);
+
+ ether_addr_copy(mgmt->da, get_my_bssid23a(&pmlmeinfo->network));
+ ether_addr_copy(mgmt->sa, myid(&padapter->eeprompriv));
+ ether_addr_copy(mgmt->bssid, get_my_bssid23a(&pmlmeinfo->network));
+
+ mgmt->seq_ctrl = cpu_to_le16(IEEE80211_SN_TO_SEQ(pmlmeext->mgnt_seq));
+ pmlmeext->mgnt_seq++;
+
+ /* caps */
+ put_unaligned_le16(pmlmeinfo->network.capability,
+ &mgmt->u.assoc_req.capab_info);
+ /* todo: listen interval for power saving */
+ put_unaligned_le16(3, &mgmt->u.assoc_req.listen_interval);
+
+ pframe = mgmt->u.assoc_req.variable;
+ pattrib->pktlen = offsetof(struct ieee80211_mgmt, u.assoc_req.variable);
+
+ /* SSID */
+ pframe = rtw_set_ie23a(pframe, WLAN_EID_SSID,
+ pmlmeinfo->network.Ssid.ssid_len,
+ pmlmeinfo->network.Ssid.ssid, &pattrib->pktlen);
+
+ /* supported rate & extended supported rate */
+
+ get_rate_set23a(padapter, sta_bssrate, &sta_bssrate_len);
+ /* DBG_8723A("sta_bssrate_len =%d\n", sta_bssrate_len); */
+
+ /* for JAPAN, channel 14 can only uses B Mode(CCK) */
+ if (pmlmeext->cur_channel == 14)
+ sta_bssrate_len = 4;
+
+ /* for (i = 0; i < sta_bssrate_len; i++) { */
+ /* DBG_8723A("sta_bssrate[%d]=%02X\n", i, sta_bssrate[i]); */
+ /* */
+
+ for (i = 0; i < NDIS_802_11_LENGTH_RATES_EX; i++) {
+ if (pmlmeinfo->network.SupportedRates[i] == 0)
+ break;
+ DBG_8723A("network.SupportedRates[%d]=%02X\n", i,
+ pmlmeinfo->network.SupportedRates[i]);
+ }
+
+ for (i = 0; i < NDIS_802_11_LENGTH_RATES_EX; i++) {
+ if (pmlmeinfo->network.SupportedRates[i] == 0)
+ break;
+
+ /* Check if the AP's supported rates are also
+ supported by STA. */
+ for (j = 0; j < sta_bssrate_len; j++) {
+ /* Avoid the proprietary data rate (22Mbps) of
+ Handlink WSG-4000 AP */
+ if ((pmlmeinfo->network.SupportedRates[i] |
+ IEEE80211_BASIC_RATE_MASK) ==
+ (sta_bssrate[j] | IEEE80211_BASIC_RATE_MASK)) {
+ /* DBG_8723A("match i = %d, j =%d\n", i, j); */
+ break;
+ }
+ }
+
+ if (j == sta_bssrate_len) {
+ /* the rate is not supported by STA */
+ DBG_8723A("%s(): the rate[%d]=%02X is not supported by "
+ "STA!\n", __func__, i,
+ pmlmeinfo->network.SupportedRates[i]);
+ } else {
+ /* the rate is supported by STA */
+ bssrate[index++] = pmlmeinfo->network.SupportedRates[i];
+ }
+ }
+
+ bssrate_len = index;
+ DBG_8723A("bssrate_len = %d\n", bssrate_len);
+
+ if (bssrate_len == 0) {
+ rtw_free_xmitbuf23a(pxmitpriv, pmgntframe->pxmitbuf);
+ rtw_free_xmitframe23a(pxmitpriv, pmgntframe);
+ goto exit; /* don't connect to AP if no joint supported rate */
+ }
+
+ if (bssrate_len > 8) {
+ pframe = rtw_set_ie23a(pframe, WLAN_EID_SUPP_RATES, 8,
+ bssrate, &pattrib->pktlen);
+ pframe = rtw_set_ie23a(pframe, WLAN_EID_EXT_SUPP_RATES,
+ (bssrate_len - 8), (bssrate + 8),
+ &pattrib->pktlen);
+ } else
+ pframe = rtw_set_ie23a(pframe, WLAN_EID_SUPP_RATES,
+ bssrate_len, bssrate, &pattrib->pktlen);
+
+ /* RSN */
+
+ pie = pmlmeinfo->network.IEs;
+ pie_len = pmlmeinfo->network.IELength;
+
+ p = cfg80211_find_ie(WLAN_EID_RSN, pie, pie_len);
+ if (p)
+ pframe = rtw_set_ie23a(pframe, WLAN_EID_RSN, p[1], p + 2,
+ &pattrib->pktlen);
+
+ /* HT caps */
+ if (padapter->mlmepriv.htpriv.ht_option) {
+ p = cfg80211_find_ie(WLAN_EID_HT_CAPABILITY, pie, pie_len);
+
+ if (p && !is_ap_in_tkip23a(padapter)) {
+ struct ieee80211_ht_cap *cap = &pmlmeinfo->ht_cap;
+
+ memcpy(cap, p + 2, sizeof(struct ieee80211_ht_cap));
+
+ /* to disable 40M Hz support while gd_bw_40MHz_en = 0 */
+ if (pregpriv->cbw40_enable == 0) {
+ cap->cap_info &= ~cpu_to_le16(
+ IEEE80211_HT_CAP_SGI_40 |
+ IEEE80211_HT_CAP_SUP_WIDTH_20_40);
+ } else {
+ cap->cap_info |= cpu_to_le16(
+ IEEE80211_HT_CAP_SUP_WIDTH_20_40);
+ }
+
+ /* todo: disable SM power save mode */
+ cap->cap_info |= cpu_to_le16(IEEE80211_HT_CAP_SM_PS);
+
+ rf_type = rtl8723a_get_rf_type(padapter);
+ /* switch (pregpriv->rf_config) */
+ switch (rf_type) {
+ case RF_1T1R:
+ /* RX STBC One spatial stream */
+ if (pregpriv->rx_stbc)
+ cap->cap_info |= cpu_to_le16(1 << IEEE80211_HT_CAP_RX_STBC_SHIFT);
+
+ memcpy(&cap->mcs, MCS_rate_1R23A, 16);
+ break;
+
+ case RF_2T2R:
+ case RF_1T2R:
+ default:
+ /* enable for 2.4/5 GHz */
+ if (pregpriv->rx_stbc == 0x3 ||
+ (pmlmeext->cur_wireless_mode &
+ WIRELESS_11_24N &&
+ /* enable for 2.4GHz */
+ pregpriv->rx_stbc == 0x1) ||
+ (pmlmeext->cur_wireless_mode &
+ WIRELESS_11_5N &&
+ pregpriv->rx_stbc == 0x2) ||
+ /* enable for 5GHz */
+ pregpriv->wifi_spec == 1) {
+ DBG_8723A("declare supporting RX "
+ "STBC\n");
+ /* RX STBC two spatial stream */
+ cap->cap_info |= cpu_to_le16(2 << IEEE80211_HT_CAP_RX_STBC_SHIFT);
+ }
+ memcpy(&cap->mcs, MCS_rate_2R23A, 16);
+ break;
+ }
+
+ if (rtl8723a_BT_coexist(padapter) &&
+ rtl8723a_BT_using_antenna_1(padapter)) {
+ /* set to 8K */
+ cap->ampdu_params_info &=
+ ~IEEE80211_HT_AMPDU_PARM_FACTOR;
+/* cap->ampdu_params_info |= MAX_AMPDU_FACTOR_8K */
+ }
+
+ pframe = rtw_set_ie23a(pframe, WLAN_EID_HT_CAPABILITY,
+ p[1], (u8 *)&pmlmeinfo->ht_cap,
+ &pattrib->pktlen);
+ }
+ }
+
+ /* vendor specific IE, such as WPA, WMM, WPS */
+ for (i = 0; i < pmlmeinfo->network.IELength;) {
+ p = pmlmeinfo->network.IEs + i;
+
+ switch (p[0]) {
+ case WLAN_EID_VENDOR_SPECIFIC:
+ if (!memcmp(p + 2, RTW_WPA_OUI23A_TYPE, 4) ||
+ !memcmp(p + 2, WMM_OUI23A, 4) ||
+ !memcmp(p + 2, WPS_OUI23A, 4)) {
+ u8 plen = p[1];
+
+ if (!padapter->registrypriv.wifi_spec) {
+ /* Commented by Kurt 20110629 */
+ /* In some older APs, WPS handshake */
+ /* would be fail if we append vender
+ extensions informations to AP */
+ if (!memcmp(p + 2, WPS_OUI23A, 4))
+ plen = 14;
+ }
+ pframe = rtw_set_ie23a(pframe,
+ WLAN_EID_VENDOR_SPECIFIC,
+ plen, p + 2,
+ &pattrib->pktlen);
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ i += p[1] + 2;
+ }
+
+ if (pmlmeinfo->assoc_AP_vendor == HT_IOT_PEER_REALTEK)
+ pframe = rtw_set_ie23a(pframe, WLAN_EID_VENDOR_SPECIFIC, 6,
+ REALTEK_96B_IE, &pattrib->pktlen);
+
+ pattrib->last_txcmdsz = pattrib->pktlen;
+ dump_mgntframe23a(padapter, pmgntframe);
+
+ ret = _SUCCESS;
+
+exit:
+ pmlmepriv->assoc_req_len = 0;
+ if (ret == _SUCCESS) {
+ kfree(pmlmepriv->assoc_req);
+ pmlmepriv->assoc_req = kmalloc(pattrib->pktlen, GFP_ATOMIC);
+ if (pmlmepriv->assoc_req) {
+ memcpy(pmlmepriv->assoc_req, mgmt, pattrib->pktlen);
+ pmlmepriv->assoc_req_len = pattrib->pktlen;
+ }
+ } else
+ kfree(pmlmepriv->assoc_req);
+
+ return;
+}
+
+/* when wait_ack is true, this function should be called at process context */
+static int _issue_nulldata23a(struct rtw_adapter *padapter, unsigned char *da,
+ unsigned int power_mode, int wait_ack)
+{
+ int ret = _FAIL;
+ struct xmit_frame *pmgntframe;
+ struct pkt_attrib *pattrib;
+ unsigned char *pframe;
+ struct ieee80211_hdr *pwlanhdr;
+ struct xmit_priv *pxmitpriv;
+ struct mlme_ext_priv *pmlmeext;
+ struct mlme_ext_info *pmlmeinfo;
+
+ /* DBG_8723A("%s:%d\n", __func__, power_mode); */
+
+ if (!padapter)
+ goto exit;
+
+ pxmitpriv = &padapter->xmitpriv;
+ pmlmeext = &padapter->mlmeextpriv;
+ pmlmeinfo = &pmlmeext->mlmext_info;
+
+ pmgntframe = alloc_mgtxmitframe23a(pxmitpriv);
+ if (!pmgntframe)
+ goto exit;
+
+ /* update attribute */
+ pattrib = &pmgntframe->attrib;
+ update_mgntframe_attrib23a(padapter, pattrib);
+ pattrib->retry_ctrl = false;
+
+ memset(pmgntframe->buf_addr, 0, WLANHDR_OFFSET + TXDESC_OFFSET);
+
+ pframe = (u8 *)(pmgntframe->buf_addr) + TXDESC_OFFSET;
+ pwlanhdr = (struct ieee80211_hdr *)pframe;
+
+ pwlanhdr->frame_control = cpu_to_le16(IEEE80211_FTYPE_DATA |
+ IEEE80211_STYPE_NULLFUNC);
+
+ if ((pmlmeinfo->state&0x03) == MSR_AP)
+ pwlanhdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_FROMDS);
+ else if ((pmlmeinfo->state&0x03) == MSR_INFRA)
+ pwlanhdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_TODS);
+
+ if (power_mode)
+ pwlanhdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_PM);
+
+ ether_addr_copy(pwlanhdr->addr1, da);
+ ether_addr_copy(pwlanhdr->addr2, myid(&padapter->eeprompriv));
+ ether_addr_copy(pwlanhdr->addr3, get_my_bssid23a(&pmlmeinfo->network));
+
+ pwlanhdr->seq_ctrl =
+ cpu_to_le16(IEEE80211_SN_TO_SEQ(pmlmeext->mgnt_seq));
+ pmlmeext->mgnt_seq++;
+
+ pframe += sizeof(struct ieee80211_hdr_3addr);
+ pattrib->pktlen = sizeof(struct ieee80211_hdr_3addr);
+
+ pattrib->last_txcmdsz = pattrib->pktlen;
+
+ if (wait_ack)
+ ret = dump_mgntframe23a_and_wait_ack23a(padapter, pmgntframe);
+ else {
+ dump_mgntframe23a(padapter, pmgntframe);
+ ret = _SUCCESS;
+ }
+
+exit:
+ return ret;
+}
+
+/* when wait_ms >0 , this function should be called at process context */
+/* da == NULL for station mode */
+int issue_nulldata23a(struct rtw_adapter *padapter, unsigned char *da,
+ unsigned int power_mode, int try_cnt, int wait_ms)
+{
+ int ret;
+ int i = 0;
+ unsigned long start = jiffies;
+ struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv;
+ struct mlme_ext_info *pmlmeinfo = &pmlmeext->mlmext_info;
+
+ /* da == NULL, assume it's null data for sta to ap*/
+ if (da == NULL)
+ da = get_my_bssid23a(&pmlmeinfo->network);
+
+ do {
+ ret = _issue_nulldata23a(padapter, da, power_mode,
+ wait_ms > 0 ? true : false);
+
+ i++;
+
+ if (padapter->bDriverStopped || padapter->bSurpriseRemoved)
+ break;
+
+ if (i < try_cnt && wait_ms > 0 && ret == _FAIL)
+ msleep(wait_ms);
+
+ } while((i < try_cnt) && ((ret == _FAIL) || (wait_ms == 0)));
+
+ if (ret != _FAIL) {
+ ret = _SUCCESS;
+ goto exit;
+ }
+
+ if (try_cnt && wait_ms) {
+ if (da)
+ DBG_8723A("%s(%s): to %pM, ch:%u%s, %d/%d in %u ms\n",
+ __func__, padapter->pnetdev->name,
+ da, rtw_get_oper_ch23a(padapter),
+ ret == _SUCCESS ? ", acked" : "", i, try_cnt,
+ jiffies_to_msecs(jiffies - start));
+ else
+ DBG_8723A("%s(%s):, ch:%u%s, %d/%d in %u ms\n",
+ __func__, padapter->pnetdev->name,
+ rtw_get_oper_ch23a(padapter),
+ ret == _SUCCESS ? ", acked" : "", i, try_cnt,
+ jiffies_to_msecs(jiffies - start));
+ }
+exit:
+ return ret;
+}
+
+/* when wait_ack is true, this function should be called at process context */
+static int _issue_qos_nulldata23a(struct rtw_adapter *padapter,
+ unsigned char *da, u16 tid, int wait_ack)
+{
+ int ret = _FAIL;
+ struct xmit_frame *pmgntframe;
+ struct pkt_attrib *pattrib;
+ unsigned char *pframe;
+ struct ieee80211_qos_hdr *pwlanhdr;
+ struct xmit_priv *pxmitpriv = &padapter->xmitpriv;
+ struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv;
+ struct mlme_ext_info *pmlmeinfo = &pmlmeext->mlmext_info;
+
+ DBG_8723A("%s\n", __func__);
+
+ pmgntframe = alloc_mgtxmitframe23a(pxmitpriv);
+ if (!pmgntframe)
+ goto exit;
+
+ /* update attribute */
+ pattrib = &pmgntframe->attrib;
+ update_mgntframe_attrib23a(padapter, pattrib);
+
+ pattrib->hdrlen += 2;
+ pattrib->qos_en = true;
+ pattrib->eosp = 1;
+ pattrib->ack_policy = 0;
+ pattrib->mdata = 0;
+
+ memset(pmgntframe->buf_addr, 0, WLANHDR_OFFSET + TXDESC_OFFSET);
+
+ pframe = (u8 *)(pmgntframe->buf_addr) + TXDESC_OFFSET;
+ pwlanhdr = (struct ieee80211_qos_hdr *)pframe;
+
+ pwlanhdr->frame_control = cpu_to_le16(IEEE80211_FTYPE_DATA |
+ IEEE80211_STYPE_QOS_NULLFUNC);
+
+ if ((pmlmeinfo->state&0x03) == MSR_AP)
+ pwlanhdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_FROMDS);
+ else if ((pmlmeinfo->state&0x03) == MSR_INFRA)
+ pwlanhdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_TODS);
+
+ if (pattrib->mdata)
+ pwlanhdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_MOREDATA);
+
+ pwlanhdr->qos_ctrl = cpu_to_le16(tid & IEEE80211_QOS_CTL_TID_MASK);
+ pwlanhdr->qos_ctrl |= cpu_to_le16((pattrib->ack_policy << 5) &
+ IEEE80211_QOS_CTL_ACK_POLICY_MASK);
+ if (pattrib->eosp)
+ pwlanhdr->qos_ctrl |= cpu_to_le16(IEEE80211_QOS_CTL_EOSP);
+
+ ether_addr_copy(pwlanhdr->addr1, da);
+ ether_addr_copy(pwlanhdr->addr2, myid(&padapter->eeprompriv));
+ ether_addr_copy(pwlanhdr->addr3, get_my_bssid23a(&pmlmeinfo->network));
+
+ pwlanhdr->seq_ctrl =
+ cpu_to_le16(IEEE80211_SN_TO_SEQ(pmlmeext->mgnt_seq));
+ pmlmeext->mgnt_seq++;
+
+ pframe += sizeof(struct ieee80211_qos_hdr);
+ pattrib->pktlen = sizeof(struct ieee80211_qos_hdr);
+
+ pattrib->last_txcmdsz = pattrib->pktlen;
+
+ if (wait_ack)
+ ret = dump_mgntframe23a_and_wait_ack23a(padapter, pmgntframe);
+ else {
+ dump_mgntframe23a(padapter, pmgntframe);
+ ret = _SUCCESS;
+ }
+
+exit:
+ return ret;
+}
+
+/* when wait_ms >0 , this function should be called at process context */
+/* da == NULL for station mode */
+int issue_qos_nulldata23a(struct rtw_adapter *padapter, unsigned char *da,
+ u16 tid, int try_cnt, int wait_ms)
+{
+ int ret;
+ int i = 0;
+ unsigned long start = jiffies;
+ struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv;
+ struct mlme_ext_info *pmlmeinfo = &pmlmeext->mlmext_info;
+
+ /* da == NULL, assume it's null data for sta to ap*/
+ if (da == NULL)
+ da = get_my_bssid23a(&pmlmeinfo->network);
+
+ do {
+ ret = _issue_qos_nulldata23a(padapter, da, tid,
+ wait_ms > 0 ? true : false);
+
+ i++;
+
+ if (padapter->bDriverStopped || padapter->bSurpriseRemoved)
+ break;
+
+ if (i < try_cnt && wait_ms > 0 && ret == _FAIL)
+ msleep(wait_ms);
+ } while((i < try_cnt) && ((ret == _FAIL)||(wait_ms == 0)));
+
+ if (ret != _FAIL) {
+ ret = _SUCCESS;
+ goto exit;
+ }
+
+ if (try_cnt && wait_ms) {
+ if (da)
+ DBG_8723A("%s(%s): to %pM, ch:%u%s, %d/%d in %u ms\n",
+ __func__, padapter->pnetdev->name,
+ da, rtw_get_oper_ch23a(padapter),
+ ret == _SUCCESS ? ", acked" : "", i, try_cnt,
+ jiffies_to_msecs(jiffies - start));
+ else
+ DBG_8723A("%s(%s):, ch:%u%s, %d/%d in %u ms\n",
+ __func__, padapter->pnetdev->name,
+ rtw_get_oper_ch23a(padapter),
+ ret == _SUCCESS ? ", acked" : "", i, try_cnt,
+ jiffies_to_msecs(jiffies - start));
+ }
+exit:
+ return ret;
+}
+
+static int _issue_deauth(struct rtw_adapter *padapter, unsigned char *da,
+ unsigned short reason, u8 wait_ack)
+{
+ struct xmit_frame *pmgntframe;
+ struct pkt_attrib *pattrib;
+ struct ieee80211_mgmt *mgmt;
+ struct xmit_priv *pxmitpriv = &padapter->xmitpriv;
+ struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv;
+ struct mlme_ext_info *pmlmeinfo = &pmlmeext->mlmext_info;
+ int ret = _FAIL;
+
+ /* DBG_8723A("%s to %pM\n", __func__, da); */
+
+ pmgntframe = alloc_mgtxmitframe23a(pxmitpriv);
+ if (!pmgntframe)
+ goto exit;
+
+ /* update attribute */
+ pattrib = &pmgntframe->attrib;
+ update_mgntframe_attrib23a(padapter, pattrib);
+ pattrib->retry_ctrl = false;
+
+ memset(pmgntframe->buf_addr, 0, WLANHDR_OFFSET + TXDESC_OFFSET);
+
+ mgmt = (struct ieee80211_mgmt *)(pmgntframe->buf_addr + TXDESC_OFFSET);
+
+ mgmt->frame_control =
+ cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_DEAUTH);
+
+ ether_addr_copy(mgmt->da, da);
+ ether_addr_copy(mgmt->sa, myid(&padapter->eeprompriv));
+ ether_addr_copy(mgmt->bssid, get_my_bssid23a(&pmlmeinfo->network));
+
+ mgmt->seq_ctrl = cpu_to_le16(IEEE80211_SN_TO_SEQ(pmlmeext->mgnt_seq));
+ pmlmeext->mgnt_seq++;
+
+ pattrib->pktlen = sizeof(struct ieee80211_hdr_3addr) + 2;
+
+ mgmt->u.deauth.reason_code = cpu_to_le16(reason);
+
+ pattrib->last_txcmdsz = pattrib->pktlen;
+
+ if (wait_ack)
+ ret = dump_mgntframe23a_and_wait_ack23a(padapter, pmgntframe);
+ else {
+ dump_mgntframe23a(padapter, pmgntframe);
+ ret = _SUCCESS;
+ }
+
+exit:
+ return ret;
+}
+
+int issue_deauth23a(struct rtw_adapter *padapter, unsigned char *da,
+ unsigned short reason)
+{
+ DBG_8723A("%s to %pM\n", __func__, da);
+ return _issue_deauth(padapter, da, reason, false);
+}
+
+static int issue_deauth_ex(struct rtw_adapter *padapter, u8 *da,
+ unsigned short reason, int try_cnt, int wait_ms)
+{
+ int ret;
+ int i = 0;
+ unsigned long start = jiffies;
+
+ do {
+ ret = _issue_deauth(padapter, da, reason,
+ wait_ms >0 ? true : false);
+
+ i++;
+
+ if (padapter->bDriverStopped || padapter->bSurpriseRemoved)
+ break;
+
+ if (i < try_cnt && wait_ms > 0 && ret == _FAIL)
+ msleep(wait_ms);
+
+ } while((i < try_cnt) && ((ret == _FAIL)||(wait_ms == 0)));
+
+ if (ret != _FAIL) {
+ ret = _SUCCESS;
+ goto exit;
+ }
+
+ if (try_cnt && wait_ms) {
+ if (da)
+ DBG_8723A("%s(%s): to %pM, ch:%u%s, %d/%d in %u ms\n",
+ __func__, padapter->pnetdev->name,
+ da, rtw_get_oper_ch23a(padapter),
+ ret == _SUCCESS ? ", acked" : "", i, try_cnt,
+ jiffies_to_msecs(jiffies - start));
+ else
+ DBG_8723A("%s(%s):, ch:%u%s, %d/%d in %u ms\n",
+ __func__, padapter->pnetdev->name,
+ rtw_get_oper_ch23a(padapter),
+ ret == _SUCCESS ? ", acked" : "", i, try_cnt,
+ jiffies_to_msecs(jiffies - start));
+ }
+exit:
+ return ret;
+}
+
+void issue_action_spct_ch_switch23a(struct rtw_adapter *padapter,
+ u8 *ra, u8 new_ch, u8 ch_offset)
+{
+ struct xmit_frame *pmgntframe;
+ struct pkt_attrib *pattrib;
+ unsigned char *pframe;
+ struct ieee80211_mgmt *mgmt;
+ struct xmit_priv *pxmitpriv = &padapter->xmitpriv;
+ struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv;
+
+ DBG_8723A("%s(%s): ra=%pM, ch:%u, offset:%u\n",
+ __func__, padapter->pnetdev->name, ra, new_ch, ch_offset);
+
+ pmgntframe = alloc_mgtxmitframe23a(pxmitpriv);
+ if (!pmgntframe)
+ return;
+
+ /* update attribute */
+ pattrib = &pmgntframe->attrib;
+ update_mgntframe_attrib23a(padapter, pattrib);
+
+ memset(pmgntframe->buf_addr, 0, WLANHDR_OFFSET + TXDESC_OFFSET);
+
+ mgmt = (struct ieee80211_mgmt *)(pmgntframe->buf_addr + TXDESC_OFFSET);
+
+ mgmt->frame_control =
+ cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_ACTION);
+
+ ether_addr_copy(mgmt->da, ra); /* RA */
+ ether_addr_copy(mgmt->sa, myid(&padapter->eeprompriv)); /* TA */
+ ether_addr_copy(mgmt->bssid, ra); /* DA = RA */
+
+ mgmt->seq_ctrl = cpu_to_le16(IEEE80211_SN_TO_SEQ(pmlmeext->mgnt_seq));
+ pmlmeext->mgnt_seq++;
+
+ mgmt->u.action.category = WLAN_CATEGORY_SPECTRUM_MGMT;
+ mgmt->u.action.u.chan_switch.action_code = WLAN_ACTION_SPCT_CHL_SWITCH;
+
+ pframe = mgmt->u.action.u.chan_switch.variable;
+ pattrib->pktlen = offsetof(struct ieee80211_mgmt,
+ u.action.u.chan_switch.variable);
+
+ pframe = rtw_set_ie23a_ch_switch (pframe, &pattrib->pktlen, 0,
+ new_ch, 0);
+ pframe = rtw_set_ie23a_secondary_ch_offset(pframe, &pattrib->pktlen,
+ hal_ch_offset_to_secondary_ch_offset23a(ch_offset));
+
+ pattrib->last_txcmdsz = pattrib->pktlen;
+
+ dump_mgntframe23a(padapter, pmgntframe);
+}
+
+void issue_action_BA23a(struct rtw_adapter *padapter,
+ const unsigned char *raddr,
+ unsigned char action, unsigned short status)
+{
+ u16 start_seq;
+ u16 BA_para_set;
+ u16 BA_starting_seqctrl;
+ u16 BA_para;
+ int max_rx_ampdu_factor;
+ struct xmit_frame *pmgntframe;
+ struct pkt_attrib *pattrib;
+ struct ieee80211_mgmt *mgmt;
+ struct xmit_priv *pxmitpriv = &padapter->xmitpriv;
+ struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv;
+ struct mlme_ext_info *pmlmeinfo = &pmlmeext->mlmext_info;
+ struct sta_info *psta;
+ struct sta_priv *pstapriv = &padapter->stapriv;
+ struct registry_priv *pregpriv = &padapter->registrypriv;
+ u8 tendaAPMac[] = {0xC8, 0x3A, 0x35};
+
+ DBG_8723A("%s, action =%d, status =%d\n", __func__, action, status);
+
+ pmgntframe = alloc_mgtxmitframe23a(pxmitpriv);
+ if (!pmgntframe)
+ return;
+
+ /* update attribute */
+ pattrib = &pmgntframe->attrib;
+ update_mgntframe_attrib23a(padapter, pattrib);
+
+ memset(pmgntframe->buf_addr, 0, WLANHDR_OFFSET + TXDESC_OFFSET);
+
+ mgmt = (struct ieee80211_mgmt *)(pmgntframe->buf_addr + TXDESC_OFFSET);
+
+ mgmt->frame_control =
+ cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_ACTION);
+
+ ether_addr_copy(mgmt->da, raddr);
+ ether_addr_copy(mgmt->sa, myid(&padapter->eeprompriv));
+ ether_addr_copy(mgmt->bssid, get_my_bssid23a(&pmlmeinfo->network));
+
+ mgmt->seq_ctrl = cpu_to_le16(IEEE80211_SN_TO_SEQ(pmlmeext->mgnt_seq));
+ pmlmeext->mgnt_seq++;
+
+ mgmt->u.action.category = WLAN_CATEGORY_BACK;
+
+ pattrib->pktlen = sizeof(struct ieee80211_hdr_3addr) + 1;
+
+ status = cpu_to_le16(status);
+
+ switch (action) {
+ case WLAN_ACTION_ADDBA_REQ:
+ pattrib->pktlen += sizeof(mgmt->u.action.u.addba_req);
+
+ mgmt->u.action.u.addba_req.action_code = action;
+
+ do {
+ pmlmeinfo->dialogToken++;
+ } while (pmlmeinfo->dialogToken == 0);
+
+ mgmt->u.action.u.addba_req.dialog_token =
+ pmlmeinfo->dialogToken;
+
+ if (rtl8723a_BT_coexist(padapter) &&
+ rtl8723a_BT_using_antenna_1(padapter) &&
+ (pmlmeinfo->assoc_AP_vendor != broadcomAP ||
+ memcmp(raddr, tendaAPMac, 3))) {
+ /* A-MSDU NOT Supported */
+ BA_para_set = 0;
+ /* immediate Block Ack */
+ BA_para_set |= (1 << 1) &
+ IEEE80211_ADDBA_PARAM_POLICY_MASK;
+ /* TID */
+ BA_para_set |= (status << 2) &
+ IEEE80211_ADDBA_PARAM_TID_MASK;
+ /* max buffer size is 8 MSDU */
+ BA_para_set |= (8 << 6) &
+ IEEE80211_ADDBA_PARAM_BUF_SIZE_MASK;
+ } else {
+ /* immediate ack & 64 buffer size */
+ BA_para_set = 0x1002 | ((status & 0xf) << 2);
+ }
+
+ put_unaligned_le16(BA_para_set,
+ &mgmt->u.action.u.addba_req.capab);
+
+ /* 5ms */
+ put_unaligned_le16(5000, &mgmt->u.action.u.addba_req.timeout);
+
+ psta = rtw_get_stainfo23a(pstapriv, raddr);
+ if (psta) {
+ int idx;
+
+ idx = status & 0x07;
+ start_seq =
+ (psta->sta_xmitpriv.txseq_tid[idx] & 0xfff) + 1;
+
+ DBG_8723A("BA_starting_seqctrl = %d for TID =%d\n",
+ start_seq, idx);
+
+ psta->BA_starting_seqctrl[idx] = start_seq;
+
+ BA_starting_seqctrl = start_seq << 4;
+ } else
+ BA_starting_seqctrl = 0;
+
+ put_unaligned_le16(BA_starting_seqctrl,
+ &mgmt->u.action.u.addba_req.start_seq_num);
+
+ break;
+
+ case WLAN_ACTION_ADDBA_RESP:
+ pattrib->pktlen += sizeof(mgmt->u.action.u.addba_resp);
+
+ mgmt->u.action.u.addba_resp.action_code = action;
+ mgmt->u.action.u.addba_resp.dialog_token =
+ pmlmeinfo->ADDBA_req.dialog_token;
+ put_unaligned_le16(status,
+ &mgmt->u.action.u.addba_resp.status);
+
+ GetHalDefVar8192CUsb(padapter, HW_VAR_MAX_RX_AMPDU_FACTOR,
+ &max_rx_ampdu_factor);
+
+ BA_para = le16_to_cpu(pmlmeinfo->ADDBA_req.BA_para_set) & 0x3f;
+ if (max_rx_ampdu_factor == IEEE80211_HT_MAX_AMPDU_64K)
+ BA_para_set = BA_para | 0x1000; /* 64 buffer size */
+ else if (max_rx_ampdu_factor == IEEE80211_HT_MAX_AMPDU_32K)
+ BA_para_set = BA_para | 0x0800; /* 32 buffer size */
+ else if (max_rx_ampdu_factor == IEEE80211_HT_MAX_AMPDU_16K)
+ BA_para_set = BA_para | 0x0400; /* 16 buffer size */
+ else if (max_rx_ampdu_factor == IEEE80211_HT_MAX_AMPDU_8K)
+ BA_para_set = BA_para | 0x0200; /* 8 buffer size */
+ else
+ BA_para_set = BA_para | 0x1000; /* 64 buffer size */
+
+ if (rtl8723a_BT_coexist(padapter) &&
+ rtl8723a_BT_using_antenna_1(padapter) &&
+ (pmlmeinfo->assoc_AP_vendor != broadcomAP ||
+ memcmp(raddr, tendaAPMac, 3))) {
+ /* max buffer size is 8 MSDU */
+ BA_para_set &= ~IEEE80211_ADDBA_PARAM_BUF_SIZE_MASK;
+ BA_para_set |= (8 << 6) &
+ IEEE80211_ADDBA_PARAM_BUF_SIZE_MASK;
+ }
+
+ if (pregpriv->ampdu_amsdu == 0)/* disabled */
+ BA_para_set &= ~BIT(0);
+ else if (pregpriv->ampdu_amsdu == 1)/* enabled */
+ BA_para_set |= BIT(0);
+
+ put_unaligned_le16(BA_para_set,
+ &mgmt->u.action.u.addba_resp.capab);
+
+ put_unaligned_le16(pmlmeinfo->ADDBA_req.BA_timeout_value,
+ &mgmt->u.action.u.addba_resp.timeout);
+
+ pattrib->pktlen += 8;
+ break;
+ case WLAN_ACTION_DELBA:
+ pattrib->pktlen += sizeof(mgmt->u.action.u.delba);
+
+ mgmt->u.action.u.delba.action_code = action;
+ BA_para_set = (status & 0x1F) << 3;
+ mgmt->u.action.u.delba.params = cpu_to_le16(BA_para_set);
+ mgmt->u.action.u.delba.reason_code =
+ cpu_to_le16(WLAN_REASON_QSTA_NOT_USE);
+
+ pattrib->pktlen += 5;
+ break;
+ default:
+ break;
+ }
+
+ pattrib->last_txcmdsz = pattrib->pktlen;
+
+ dump_mgntframe23a(padapter, pmgntframe);
+}
+
+int send_delba23a(struct rtw_adapter *padapter, u8 initiator, u8 *addr)
+{
+ struct sta_priv *pstapriv = &padapter->stapriv;
+ struct sta_info *psta = NULL;
+ /* struct recv_reorder_ctrl *preorder_ctrl; */
+ struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv;
+ struct mlme_ext_info *pmlmeinfo = &pmlmeext->mlmext_info;
+ u16 tid;
+
+ if ((pmlmeinfo->state&0x03) != MSR_AP)
+ if (!(pmlmeinfo->state & WIFI_FW_ASSOC_SUCCESS))
+ return _SUCCESS;
+
+ psta = rtw_get_stainfo23a(pstapriv, addr);
+ if (psta == NULL)
+ return _SUCCESS;
+
+ if (initiator == 0) { /* recipient */
+ for (tid = 0; tid < MAXTID; tid++) {
+ if (psta->recvreorder_ctrl[tid].enable == true) {
+ DBG_8723A("rx agg disable tid(%d)\n", tid);
+ issue_action_BA23a(padapter, addr, WLAN_ACTION_DELBA, (((tid <<1) |initiator)&0x1F));
+ psta->recvreorder_ctrl[tid].enable = false;
+ psta->recvreorder_ctrl[tid].indicate_seq = 0xffff;
+ }
+ }
+ } else if (initiator == 1) { /* originator */
+ for (tid = 0; tid < MAXTID; tid++) {
+ if (psta->htpriv.agg_enable_bitmap & BIT(tid)) {
+ DBG_8723A("tx agg disable tid(%d)\n", tid);
+ issue_action_BA23a(padapter, addr, WLAN_ACTION_DELBA, (((tid <<1) |initiator)&0x1F));
+ psta->htpriv.agg_enable_bitmap &= ~BIT(tid);
+ psta->htpriv.candidate_tid_bitmap &= ~BIT(tid);
+
+ }
+ }
+ }
+ return _SUCCESS;
+}
+
+int send_beacon23a(struct rtw_adapter *padapter)
+{
+ bool bxmitok;
+ int issue = 0;
+ int poll = 0;
+ unsigned long start = jiffies;
+ unsigned int passing_time;
+
+ rtl8723a_bcn_valid(padapter);
+ do {
+ issue_beacon23a(padapter, 100);
+ issue++;
+ do {
+ yield();
+ bxmitok = rtl8723a_get_bcn_valid(padapter);
+ poll++;
+ } while ((poll % 10) != 0 && !bxmitok &&
+ !padapter->bSurpriseRemoved &&
+ !padapter->bDriverStopped);
+
+ } while (!bxmitok && issue<100 && !padapter->bSurpriseRemoved &&
+ !padapter->bDriverStopped);
+
+ if (padapter->bSurpriseRemoved || padapter->bDriverStopped)
+ return _FAIL;
+
+ passing_time = jiffies_to_msecs(jiffies - start);
+
+ if (!bxmitok) {
+ DBG_8723A("%s fail! %u ms\n", __func__, passing_time);
+ return _FAIL;
+ } else {
+
+ if (passing_time > 100 || issue > 3)
+ DBG_8723A("%s success, issue:%d, poll:%d, %u ms\n",
+ __func__, issue, poll, passing_time);
+ return _SUCCESS;
+ }
+}
+
+/****************************************************************************
+
+Following are some utitity functions for WiFi MLME
+
+*****************************************************************************/
+
+bool IsLegal5GChannel(struct rtw_adapter *Adapter, u8 channel)
+{
+
+ int i = 0;
+ u8 Channel_5G[45] = {36, 38, 40, 42, 44, 46, 48, 50, 52, 54, 56, 58,
+ 60, 62, 64, 100, 102, 104, 106, 108, 110, 112,
+ 114, 116, 118, 120, 122, 124, 126, 128, 130, 132,
+ 134, 136, 138, 140, 149, 151, 153, 155, 157, 159,
+ 161, 163, 165};
+ for (i = 0; i < sizeof(Channel_5G); i++)
+ if (channel == Channel_5G[i])
+ return true;
+ return false;
+}
+
+static void rtw_site_survey(struct rtw_adapter *padapter)
+{
+ unsigned char survey_channel = 0;
+ enum rt_scan_type ScanType = SCAN_PASSIVE;
+ struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv;
+ struct mlme_ext_info *pmlmeinfo = &pmlmeext->mlmext_info;
+ struct rtw_ieee80211_channel *ch;
+
+ if (pmlmeext->sitesurvey_res.channel_idx <
+ pmlmeext->sitesurvey_res.ch_num) {
+ ch = &pmlmeext->sitesurvey_res.ch[pmlmeext->sitesurvey_res.channel_idx];
+ survey_channel = ch->hw_value;
+ ScanType = (ch->flags & IEEE80211_CHAN_NO_IR) ?
+ SCAN_PASSIVE : SCAN_ACTIVE;
+ }
+
+ if (survey_channel != 0) {
+ /* PAUSE 4-AC Queue when site_survey */
+ if (pmlmeext->sitesurvey_res.channel_idx == 0)
+ set_channel_bwmode23a(padapter, survey_channel,
+ HAL_PRIME_CHNL_OFFSET_DONT_CARE,
+ HT_CHANNEL_WIDTH_20);
+ else
+ SelectChannel23a(padapter, survey_channel);
+
+ if (ScanType == SCAN_ACTIVE) /* obey the channel plan setting... */
+ {
+ int i;
+
+ for (i = 0;i<RTW_SSID_SCAN_AMOUNT;i++) {
+ if (pmlmeext->sitesurvey_res.ssid[i].ssid_len) {
+ /* todo: to issue two probe req??? */
+ issue_probereq(padapter, &pmlmeext->sitesurvey_res.ssid[i], NULL);
+ /* msleep(SURVEY_TO>>1); */
+ issue_probereq(padapter, &pmlmeext->sitesurvey_res.ssid[i], NULL);
+ }
+ }
+
+ if (pmlmeext->sitesurvey_res.scan_mode == SCAN_ACTIVE) {
+ /* todo: to issue two probe req??? */
+ issue_probereq(padapter, NULL, NULL);
+ /* msleep(SURVEY_TO>>1); */
+ issue_probereq(padapter, NULL, NULL);
+ }
+ }
+
+ set_survey_timer(pmlmeext, pmlmeext->chan_scan_time);
+ } else {
+ /* channel number is 0 or this channel is not valid. */
+ pmlmeext->sitesurvey_res.state = SCAN_COMPLETE;
+
+ /* switch back to the original channel */
+
+ set_channel_bwmode23a(padapter, pmlmeext->cur_channel,
+ pmlmeext->cur_ch_offset,
+ pmlmeext->cur_bwmode);
+
+ /* flush 4-AC Queue after rtw_site_survey */
+ /* val8 = 0; */
+
+ /* config MSR */
+ rtl8723a_set_media_status(padapter, pmlmeinfo->state & 0x3);
+
+ /* restore RX GAIN */
+ rtl8723a_set_initial_gain(padapter, 0xff);
+ /* turn on dynamic functions */
+ rtl8723a_odm_support_ability_restore(padapter);
+
+ if (is_client_associated_to_ap23a(padapter) == true)
+ issue_nulldata23a(padapter, NULL, 0, 3, 500);
+
+ rtl8723a_mlme_sitesurvey(padapter, 0);
+
+ report_surveydone_event23a(padapter);
+
+ pmlmeext->chan_scan_time = SURVEY_TO;
+ pmlmeext->sitesurvey_res.state = SCAN_DISABLE;
+ }
+
+ return;
+}
+
+/* collect bss info from Beacon and Probe request/response frames. */
+static struct wlan_bssid_ex *collect_bss_info(struct rtw_adapter *padapter,
+ struct recv_frame *precv_frame)
+{
+ struct sk_buff *skb = precv_frame->pkt;
+ struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *) skb->data;
+ struct registry_priv *pregistrypriv = &padapter->registrypriv;
+ struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv;
+ struct mlme_ext_info *pmlmeinfo = &pmlmeext->mlmext_info;
+ struct wlan_bssid_ex *bssid;
+ const u8 *p;
+ u8 *pie;
+ unsigned int length;
+ int i;
+
+ length = skb->len;
+
+ bssid = kzalloc(sizeof(struct wlan_bssid_ex), GFP_ATOMIC);
+ if (!bssid)
+ return NULL;
+
+ if (ieee80211_is_beacon(mgmt->frame_control)) {
+ length -= offsetof(struct ieee80211_mgmt, u.beacon.variable);
+ pie = mgmt->u.beacon.variable;
+ bssid->reserved = 1;
+ bssid->capability =
+ get_unaligned_le16(&mgmt->u.beacon.capab_info);
+ bssid->beacon_interval =
+ get_unaligned_le16(&mgmt->u.beacon.beacon_int);
+ bssid->tsf = get_unaligned_le64(&mgmt->u.beacon.timestamp);
+ } else if (ieee80211_is_probe_req(mgmt->frame_control)) {
+ length -= offsetof(struct ieee80211_mgmt, u.probe_req.variable);
+ pie = mgmt->u.probe_req.variable;
+ bssid->reserved = 2;
+ bssid->capability = 0;
+ bssid->beacon_interval =
+ padapter->registrypriv.dev_network.beacon_interval;
+ bssid->tsf = 0;
+ } else if (ieee80211_is_probe_resp(mgmt->frame_control)) {
+ length -=
+ offsetof(struct ieee80211_mgmt, u.probe_resp.variable);
+ pie = mgmt->u.probe_resp.variable;
+ bssid->reserved = 3;
+ bssid->capability =
+ get_unaligned_le16(&mgmt->u.probe_resp.capab_info);
+ bssid->beacon_interval =
+ get_unaligned_le16(&mgmt->u.probe_resp.beacon_int);
+ bssid->tsf = get_unaligned_le64(&mgmt->u.probe_resp.timestamp);
+ } else {
+ length -= offsetof(struct ieee80211_mgmt, u.beacon.variable);
+ pie = mgmt->u.beacon.variable;
+ bssid->reserved = 0;
+ bssid->capability =
+ get_unaligned_le16(&mgmt->u.beacon.capab_info);
+ bssid->beacon_interval =
+ padapter->registrypriv.dev_network.beacon_interval;
+ bssid->tsf = 0;
+ }
+
+ if (length > MAX_IE_SZ) {
+ /* DBG_8723A("IE too long for survey event\n"); */
+ kfree(bssid);
+ return NULL;
+ }
+
+ bssid->Length = offsetof(struct wlan_bssid_ex, IEs) + length;
+
+ /* below is to copy the information element */
+ bssid->IELength = length;
+ memcpy(bssid->IEs, pie, bssid->IELength);
+
+ /* get the signal strength */
+ /* in dBM.raw data */
+ bssid->Rssi = precv_frame->attrib.phy_info.RecvSignalPower;
+ bssid->SignalQuality =
+ precv_frame->attrib.phy_info.SignalQuality;/* in percentage */
+ bssid->SignalStrength =
+ precv_frame->attrib.phy_info.SignalStrength;/* in percentage */
+
+ /* checking SSID */
+ p = cfg80211_find_ie(WLAN_EID_SSID, bssid->IEs, bssid->IELength);
+
+ if (!p) {
+ DBG_8723A("marc: cannot find SSID for survey event\n");
+ goto fail;
+ }
+
+ if (p[1] > IEEE80211_MAX_SSID_LEN) {
+ DBG_8723A("%s()-%d: IE too long (%d) for survey "
+ "event\n", __func__, __LINE__, p[1]);
+ goto fail;
+ }
+ memcpy(bssid->Ssid.ssid, p + 2, p[1]);
+ bssid->Ssid.ssid_len = p[1];
+
+ /* checking rate info... */
+ i = 0;
+ p = cfg80211_find_ie(WLAN_EID_SUPP_RATES, bssid->IEs, bssid->IELength);
+ if (p) {
+ if (p[1] > NDIS_802_11_LENGTH_RATES_EX) {
+ DBG_8723A("%s()-%d: IE too long (%d) for survey "
+ "event\n", __func__, __LINE__, p[1]);
+ goto fail;
+ }
+ memcpy(bssid->SupportedRates, p + 2, p[1]);
+ i = p[1];
+ }
+
+ p = cfg80211_find_ie(WLAN_EID_EXT_SUPP_RATES, bssid->IEs,
+ bssid->IELength);
+ if (p) {
+ if (p[1] > (NDIS_802_11_LENGTH_RATES_EX-i)) {
+ DBG_8723A("%s()-%d: IE too long (%d) for survey "
+ "event\n", __func__, __LINE__, p[1]);
+ goto fail;
+ }
+ memcpy(bssid->SupportedRates + i, p + 2, p[1]);
+ }
+
+ /* Checking for DSConfig */
+ p = cfg80211_find_ie(WLAN_EID_DS_PARAMS, bssid->IEs, bssid->IELength);
+
+ bssid->DSConfig = 0;
+
+ if (p) {
+ bssid->DSConfig = p[2];
+ } else {/* In 5G, some ap do not have DSSET IE */
+ /* checking HT info for channel */
+ p = cfg80211_find_ie(WLAN_EID_HT_OPERATION, bssid->IEs,
+ bssid->IELength);
+ if (p) {
+ struct ieee80211_ht_operation *HT_info =
+ (struct ieee80211_ht_operation *)(p + 2);
+ bssid->DSConfig = HT_info->primary_chan;
+ } else /* use current channel */
+ bssid->DSConfig = rtw_get_oper_ch23a(padapter);
+ }
+
+ if (ieee80211_is_probe_req(mgmt->frame_control)) {
+ /* FIXME */
+ bssid->ifmode = NL80211_IFTYPE_STATION;
+ ether_addr_copy(bssid->MacAddress, mgmt->sa);
+ bssid->Privacy = 1;
+ return bssid;
+ }
+
+ if (bssid->capability & WLAN_CAPABILITY_ESS) {
+ bssid->ifmode = NL80211_IFTYPE_STATION;
+ ether_addr_copy(bssid->MacAddress, mgmt->sa);
+ } else {
+ bssid->ifmode = NL80211_IFTYPE_ADHOC;
+ ether_addr_copy(bssid->MacAddress, mgmt->bssid);
+ }
+
+ if (bssid->capability & WLAN_CAPABILITY_PRIVACY)
+ bssid->Privacy = 1;
+ else
+ bssid->Privacy = 0;
+
+ bssid->ATIMWindow = 0;
+
+ /* 20/40 BSS Coexistence check */
+ if (pregistrypriv->wifi_spec == 1 &&
+ pmlmeinfo->bwmode_updated == false) {
+ struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
+
+ p = cfg80211_find_ie(WLAN_EID_HT_CAPABILITY, bssid->IEs,
+ bssid->IELength);
+ if (p && p[1] > 0) {
+ struct ieee80211_ht_cap *pHT_caps;
+
+ pHT_caps = (struct ieee80211_ht_cap *)(p + 2);
+
+ if (pHT_caps->cap_info &
+ cpu_to_le16(IEEE80211_HT_CAP_40MHZ_INTOLERANT))
+ pmlmepriv->num_FortyMHzIntolerant++;
+ } else
+ pmlmepriv->num_sta_no_ht++;
+ }
+
+
+ /* mark bss info receiving from nearby channel as SignalQuality 101 */
+ if (bssid->DSConfig != rtw_get_oper_ch23a(padapter))
+ bssid->SignalQuality = 101;
+
+ return bssid;
+fail:
+ kfree (bssid);
+ return NULL;
+}
+
+static void start_create_ibss(struct rtw_adapter *padapter)
+{
+ unsigned short caps;
+ struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv;
+ struct mlme_ext_info *pmlmeinfo = &pmlmeext->mlmext_info;
+ struct wlan_bssid_ex *pnetwork = &pmlmeinfo->network;
+
+ pmlmeext->cur_channel = (u8)pnetwork->DSConfig;
+ pmlmeinfo->bcn_interval = pnetwork->beacon_interval;
+
+ /* update wireless mode */
+ update_wireless_mode23a(padapter);
+
+ /* update capability */
+ caps = pnetwork->capability;
+ update_capinfo23a(padapter, caps);
+ if (caps & WLAN_CAPABILITY_IBSS) { /* adhoc master */
+ rtl8723a_set_sec_cfg(padapter, 0xcf);
+
+ /* switch channel */
+ /* SelectChannel23a(padapter, pmlmeext->cur_channel, HAL_PRIME_CHNL_OFFSET_DONT_CARE); */
+ set_channel_bwmode23a(padapter, pmlmeext->cur_channel, HAL_PRIME_CHNL_OFFSET_DONT_CARE, HT_CHANNEL_WIDTH_20);
+
+ rtl8723a_SetBeaconRelatedRegisters(padapter);
+
+ /* set msr to MSR_ADHOC */
+ pmlmeinfo->state = MSR_ADHOC;
+ rtl8723a_set_media_status(padapter, pmlmeinfo->state & 0x3);
+
+ /* issue beacon */
+ if (send_beacon23a(padapter) == _FAIL) {
+ RT_TRACE(_module_rtl871x_mlme_c_, _drv_err_,
+ "issuing beacon frame fail....\n");
+
+ report_join_res23a(padapter, -1);
+ pmlmeinfo->state = MSR_NOLINK;
+ } else {
+ hw_var_set_bssid(padapter, padapter->registrypriv.dev_network.MacAddress);
+ hw_var_set_mlme_join(padapter, 0);
+
+ report_join_res23a(padapter, 1);
+ pmlmeinfo->state |= WIFI_FW_ASSOC_SUCCESS;
+ }
+ } else {
+ DBG_8723A("%s: invalid cap:%x\n", __func__, caps);
+ return;
+ }
+}
+
+static void start_clnt_join(struct rtw_adapter *padapter)
+{
+ unsigned short caps;
+ u8 val8;
+ struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv;
+ struct mlme_ext_info *pmlmeinfo = &pmlmeext->mlmext_info;
+ struct wlan_bssid_ex *pnetwork = &pmlmeinfo->network;
+ int beacon_timeout;
+
+ pmlmeext->cur_channel = (u8)pnetwork->DSConfig;
+ pmlmeinfo->bcn_interval = pnetwork->beacon_interval;
+
+ /* update wireless mode */
+ update_wireless_mode23a(padapter);
+
+ /* update capability */
+ caps = pnetwork->capability;
+ update_capinfo23a(padapter, caps);
+ if (caps & WLAN_CAPABILITY_ESS) {
+ /* switch channel */
+ set_channel_bwmode23a(padapter, pmlmeext->cur_channel, pmlmeext->cur_ch_offset, pmlmeext->cur_bwmode);
+
+ rtl8723a_set_media_status(padapter, MSR_INFRA);
+
+ val8 = (pmlmeinfo->auth_algo == dot11AuthAlgrthm_8021X) ?
+ 0xcc: 0xcf;
+
+ rtl8723a_set_sec_cfg(padapter, val8);
+
+ /* switch channel */
+ /* set_channel_bwmode23a(padapter, pmlmeext->cur_channel, pmlmeext->cur_ch_offset, pmlmeext->cur_bwmode); */
+
+ /* here wait for receiving the beacon to start auth */
+ /* and enable a timer */
+ beacon_timeout = decide_wait_for_beacon_timeout23a(pmlmeinfo->bcn_interval);
+ set_link_timer(pmlmeext, beacon_timeout);
+ mod_timer(&padapter->mlmepriv.assoc_timer, jiffies +
+ msecs_to_jiffies((REAUTH_TO * REAUTH_LIMIT) + (REASSOC_TO*REASSOC_LIMIT) + beacon_timeout));
+ pmlmeinfo->state = WIFI_FW_AUTH_NULL | MSR_INFRA;
+ } else if (caps & WLAN_CAPABILITY_IBSS) { /* adhoc client */
+ rtl8723a_set_media_status(padapter, MSR_ADHOC);
+
+ rtl8723a_set_sec_cfg(padapter, 0xcf);
+
+ /* switch channel */
+ set_channel_bwmode23a(padapter, pmlmeext->cur_channel, pmlmeext->cur_ch_offset, pmlmeext->cur_bwmode);
+
+ rtl8723a_SetBeaconRelatedRegisters(padapter);
+
+ pmlmeinfo->state = MSR_ADHOC;
+
+ report_join_res23a(padapter, 1);
+ } else {
+ /* DBG_8723A("marc: invalid cap:%x\n", caps); */
+ return;
+ }
+}
+
+static void start_clnt_auth(struct rtw_adapter *padapter)
+{
+ struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv;
+ struct mlme_ext_info *pmlmeinfo = &pmlmeext->mlmext_info;
+
+ del_timer_sync(&pmlmeext->link_timer);
+
+ pmlmeinfo->state &= (~WIFI_FW_AUTH_NULL);
+ pmlmeinfo->state |= WIFI_FW_AUTH_STATE;
+
+ pmlmeinfo->auth_seq = 1;
+ pmlmeinfo->reauth_count = 0;
+ pmlmeinfo->reassoc_count = 0;
+ pmlmeinfo->link_count = 0;
+ pmlmeext->retry = 0;
+
+ /* Because of AP's not receiving deauth before */
+ /* AP may: 1)not response auth or 2)deauth us after link is complete */
+ /* issue deauth before issuing auth to deal with the situation */
+ /* Commented by Albert 2012/07/21 */
+ /* For the Win8 P2P connection, it will be hard to have a
+ successful connection if this Wi-Fi doesn't connect to it. */
+ issue_deauth23a(padapter, (&pmlmeinfo->network)->MacAddress,
+ WLAN_REASON_DEAUTH_LEAVING);
+
+ DBG_8723A_LEVEL(_drv_always_, "start auth\n");
+ issue_auth(padapter, NULL, 0);
+
+ set_link_timer(pmlmeext, REAUTH_TO);
+}
+
+static void start_clnt_assoc(struct rtw_adapter *padapter)
+{
+ struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv;
+ struct mlme_ext_info *pmlmeinfo = &pmlmeext->mlmext_info;
+
+ del_timer_sync(&pmlmeext->link_timer);
+
+ pmlmeinfo->state &= (~(WIFI_FW_AUTH_NULL | WIFI_FW_AUTH_STATE));
+ pmlmeinfo->state |= (WIFI_FW_AUTH_SUCCESS | WIFI_FW_ASSOC_STATE);
+
+ issue_assocreq(padapter);
+
+ set_link_timer(pmlmeext, REASSOC_TO);
+}
+
+int receive_disconnect23a(struct rtw_adapter *padapter,
+ unsigned char *MacAddr, unsigned short reason)
+{
+ struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv;
+ struct mlme_ext_info *pmlmeinfo = &pmlmeext->mlmext_info;
+
+ /* check A3 */
+ if (!ether_addr_equal(MacAddr, get_my_bssid23a(&pmlmeinfo->network)))
+ return _SUCCESS;
+
+ DBG_8723A("%s\n", __func__);
+
+ if ((pmlmeinfo->state&0x03) == MSR_INFRA) {
+ if (pmlmeinfo->state & WIFI_FW_ASSOC_SUCCESS) {
+ pmlmeinfo->state = MSR_NOLINK;
+ report_del_sta_event23a(padapter, MacAddr, reason);
+
+ } else if (pmlmeinfo->state & WIFI_FW_LINKING_STATE) {
+ pmlmeinfo->state = MSR_NOLINK;
+ report_join_res23a(padapter, -2);
+ }
+ }
+
+ return _SUCCESS;
+}
+
+static void process_80211d(struct rtw_adapter *padapter,
+ struct wlan_bssid_ex *bssid)
+{
+ struct registry_priv *pregistrypriv;
+ struct mlme_ext_priv *pmlmeext;
+ struct rt_channel_info *chplan_new;
+ u8 channel;
+ u8 i;
+
+ pregistrypriv = &padapter->registrypriv;
+ pmlmeext = &padapter->mlmeextpriv;
+
+ /* Adjust channel plan by AP Country IE */
+ if (pregistrypriv->enable80211d &&
+ !pmlmeext->update_channel_plan_by_ap_done) {
+ const u8 *ie, *p;
+ struct rt_channel_plan chplan_ap;
+ struct rt_channel_info chplan_sta[MAX_CHANNEL_NUM];
+ u8 country[4];
+ u8 fcn; /* first channel number */
+ u8 noc; /* number of channel */
+ u8 j, k;
+
+ ie = cfg80211_find_ie(WLAN_EID_COUNTRY, bssid->IEs,
+ bssid->IELength);
+ if (!ie || ie[1] < IEEE80211_COUNTRY_IE_MIN_LEN)
+ return;
+
+ p = ie + 2;
+ ie += ie[1];
+ ie += 2;
+
+ memcpy(country, p, 3);
+ country[3] = '\0';
+
+ p += 3;
+ RT_TRACE(_module_rtl871x_mlme_c_, _drv_notice_,
+ "%s: 802.11d country =%s\n", __func__, country);
+
+ i = 0;
+ while ((ie - p) >= 3) {
+ fcn = *(p++);
+ noc = *(p++);
+ p++;
+
+ for (j = 0; j < noc; j++) {
+ if (fcn <= 14)
+ channel = fcn + j; /* 2.4 GHz */
+ else
+ channel = fcn + j * 4; /* 5 GHz */
+
+ chplan_ap.Channel[i++] = channel;
+ }
+ }
+ chplan_ap.Len = i;
+
+ memcpy(chplan_sta, pmlmeext->channel_set, sizeof(chplan_sta));
+ memset(pmlmeext->channel_set, 0, sizeof(pmlmeext->channel_set));
+ chplan_new = pmlmeext->channel_set;
+
+ i = j = k = 0;
+ if (pregistrypriv->wireless_mode & WIRELESS_11G) {
+ do {
+ if (i == MAX_CHANNEL_NUM ||
+ chplan_sta[i].ChannelNum == 0 ||
+ chplan_sta[i].ChannelNum > 14)
+ break;
+
+ if (j == chplan_ap.Len ||
+ chplan_ap.Channel[j] > 14)
+ break;
+
+ if (chplan_sta[i].ChannelNum ==
+ chplan_ap.Channel[j]) {
+ chplan_new[k].ChannelNum =
+ chplan_ap.Channel[j];
+ chplan_new[k].ScanType = SCAN_ACTIVE;
+ i++;
+ j++;
+ k++;
+ } else if (chplan_sta[i].ChannelNum <
+ chplan_ap.Channel[j]) {
+ chplan_new[k].ChannelNum =
+ chplan_sta[i].ChannelNum;
+ chplan_new[k].ScanType =
+ SCAN_PASSIVE;
+ i++;
+ k++;
+ } else if (chplan_sta[i].ChannelNum >
+ chplan_ap.Channel[j]) {
+ chplan_new[k].ChannelNum =
+ chplan_ap.Channel[j];
+ chplan_new[k].ScanType =
+ SCAN_ACTIVE;
+ j++;
+ k++;
+ }
+ } while (1);
+
+ /* change AP not support channel to Passive scan */
+ while (i < MAX_CHANNEL_NUM &&
+ chplan_sta[i].ChannelNum != 0 &&
+ chplan_sta[i].ChannelNum <= 14) {
+ chplan_new[k].ChannelNum =
+ chplan_sta[i].ChannelNum;
+ chplan_new[k].ScanType = SCAN_PASSIVE;
+ i++;
+ k++;
+ }
+
+ /* add channel AP supported */
+ while (j < chplan_ap.Len && chplan_ap.Channel[j] <= 14){
+ chplan_new[k].ChannelNum = chplan_ap.Channel[j];
+ chplan_new[k].ScanType = SCAN_ACTIVE;
+ j++;
+ k++;
+ }
+ } else {
+ /* keep original STA 2.4G channel plan */
+ while (i < MAX_CHANNEL_NUM &&
+ chplan_sta[i].ChannelNum != 0 &&
+ chplan_sta[i].ChannelNum <= 14) {
+ chplan_new[k].ChannelNum =
+ chplan_sta[i].ChannelNum;
+ chplan_new[k].ScanType = chplan_sta[i].ScanType;
+ i++;
+ k++;
+ }
+
+ /* skip AP 2.4G channel plan */
+ while (j < chplan_ap.Len && chplan_ap.Channel[j] <= 14)
+ j++;
+ }
+
+ if (pregistrypriv->wireless_mode & WIRELESS_11A) {
+ do {
+ if (i == MAX_CHANNEL_NUM ||
+ chplan_sta[i].ChannelNum == 0)
+ break;
+
+ if (j == chplan_ap.Len ||
+ chplan_ap.Channel[j] == 0)
+ break;
+
+ if (chplan_sta[i].ChannelNum ==
+ chplan_ap.Channel[j]) {
+ chplan_new[k].ChannelNum =
+ chplan_ap.Channel[j];
+ chplan_new[k].ScanType = SCAN_ACTIVE;
+ i++;
+ j++;
+ k++;
+ } else if (chplan_sta[i].ChannelNum <
+ chplan_ap.Channel[j]) {
+ chplan_new[k].ChannelNum =
+ chplan_sta[i].ChannelNum;
+ chplan_new[k].ScanType = SCAN_PASSIVE;
+ i++;
+ k++;
+ } else if (chplan_sta[i].ChannelNum >
+ chplan_ap.Channel[j]) {
+ chplan_new[k].ChannelNum =
+ chplan_ap.Channel[j];
+ chplan_new[k].ScanType = SCAN_ACTIVE;
+ j++;
+ k++;
+ }
+ } while (1);
+
+ /* change AP not support channel to Passive scan */
+ while (i < MAX_CHANNEL_NUM &&
+ chplan_sta[i].ChannelNum != 0) {
+ chplan_new[k].ChannelNum =
+ chplan_sta[i].ChannelNum;
+ chplan_new[k].ScanType = SCAN_PASSIVE;
+ i++;
+ k++;
+ }
+
+ /* add channel AP supported */
+ while (j < chplan_ap.Len && chplan_ap.Channel[j] != 0) {
+ chplan_new[k].ChannelNum = chplan_ap.Channel[j];
+ chplan_new[k].ScanType = SCAN_ACTIVE;
+ j++;
+ k++;
+ }
+ } else {
+ /* keep original STA 5G channel plan */
+ while (i < MAX_CHANNEL_NUM &&
+ chplan_sta[i].ChannelNum != 0) {
+ chplan_new[k].ChannelNum =
+ chplan_sta[i].ChannelNum;
+ chplan_new[k].ScanType = chplan_sta[i].ScanType;
+ i++;
+ k++;
+ }
+ }
+ pmlmeext->update_channel_plan_by_ap_done = 1;
+ }
+
+ /* If channel is used by AP, set channel scan type to active */
+ channel = bssid->DSConfig;
+ chplan_new = pmlmeext->channel_set;
+ i = 0;
+ while (i < MAX_CHANNEL_NUM && chplan_new[i].ChannelNum != 0) {
+ if (chplan_new[i].ChannelNum == channel) {
+ if (chplan_new[i].ScanType == SCAN_PASSIVE) {
+ /* 5G Bnad 2, 3 (DFS) doesn't change
+ to active scan */
+ if (channel >= 52 && channel <= 144)
+ break;
+
+ chplan_new[i].ScanType = SCAN_ACTIVE;
+ RT_TRACE(_module_rtl871x_mlme_c_, _drv_notice_,
+ "%s: change channel %d scan type from passive to active\n",
+ __func__, channel);
+ }
+ break;
+ }
+ i++;
+ }
+}
+
+/****************************************************************************
+
+Following are the functions to report events
+
+*****************************************************************************/
+
+void report_survey_event23a(struct rtw_adapter *padapter,
+ struct recv_frame *precv_frame)
+{
+ struct cmd_obj *pcmd_obj;
+ u8 *pevtcmd;
+ u32 cmdsz;
+ struct survey_event *psurvey_evt;
+ struct C2HEvent_Header *pc2h_evt_hdr;
+ struct mlme_ext_priv *pmlmeext;
+ struct cmd_priv *pcmdpriv;
+
+ if (!padapter)
+ return;
+
+ pmlmeext = &padapter->mlmeextpriv;
+ pcmdpriv = &padapter->cmdpriv;
+
+ pcmd_obj = kzalloc(sizeof(struct cmd_obj), GFP_ATOMIC);
+ if (!pcmd_obj)
+ return;
+
+ cmdsz = sizeof(struct survey_event) + sizeof(struct C2HEvent_Header);
+ pevtcmd = kzalloc(cmdsz, GFP_ATOMIC);
+ if (!pevtcmd) {
+ kfree(pcmd_obj);
+ return;
+ }
+
+ pcmd_obj->cmdcode = GEN_CMD_CODE(_Set_MLME_EVT);
+ pcmd_obj->cmdsz = cmdsz;
+ pcmd_obj->parmbuf = pevtcmd;
+
+ pcmd_obj->rsp = NULL;
+ pcmd_obj->rspsz = 0;
+
+ pc2h_evt_hdr = (struct C2HEvent_Header *)(pevtcmd);
+ pc2h_evt_hdr->len = sizeof(struct survey_event);
+ pc2h_evt_hdr->ID = GEN_EVT_CODE(_Survey);
+ pc2h_evt_hdr->seq = atomic_inc_return(&pmlmeext->event_seq);
+
+ psurvey_evt = (struct survey_event*)(pevtcmd + sizeof(struct C2HEvent_Header));
+
+ psurvey_evt->bss = collect_bss_info(padapter, precv_frame);
+ if (!psurvey_evt->bss) {
+ kfree(pcmd_obj);
+ kfree(pevtcmd);
+ return;
+ }
+
+ process_80211d(padapter, psurvey_evt->bss);
+
+ rtw_enqueue_cmd23a(pcmdpriv, pcmd_obj);
+
+ pmlmeext->sitesurvey_res.bss_cnt++;
+
+ return;
+}
+
+void report_surveydone_event23a(struct rtw_adapter *padapter)
+{
+ struct cmd_obj *pcmd_obj;
+ u8 *pevtcmd;
+ u32 cmdsz;
+ struct surveydone_event *psurveydone_evt;
+ struct C2HEvent_Header *pc2h_evt_hdr;
+ struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv;
+ struct cmd_priv *pcmdpriv = &padapter->cmdpriv;
+
+ pcmd_obj = kzalloc(sizeof(struct cmd_obj), GFP_ATOMIC);
+ if (!pcmd_obj)
+ return;
+
+ cmdsz = sizeof(struct surveydone_event) + sizeof(struct C2HEvent_Header);
+ pevtcmd = kzalloc(cmdsz, GFP_ATOMIC);
+ if (!pevtcmd) {
+ kfree(pcmd_obj);
+ return;
+ }
+
+ pcmd_obj->cmdcode = GEN_CMD_CODE(_Set_MLME_EVT);
+ pcmd_obj->cmdsz = cmdsz;
+ pcmd_obj->parmbuf = pevtcmd;
+
+ pcmd_obj->rsp = NULL;
+ pcmd_obj->rspsz = 0;
+
+ pc2h_evt_hdr = (struct C2HEvent_Header *)(pevtcmd);
+ pc2h_evt_hdr->len = sizeof(struct surveydone_event);
+ pc2h_evt_hdr->ID = GEN_EVT_CODE(_SurveyDone);
+ pc2h_evt_hdr->seq = atomic_inc_return(&pmlmeext->event_seq);
+
+ psurveydone_evt = (struct surveydone_event*)(pevtcmd + sizeof(struct C2HEvent_Header));
+ psurveydone_evt->bss_cnt = pmlmeext->sitesurvey_res.bss_cnt;
+
+ DBG_8723A("survey done event(%x)\n", psurveydone_evt->bss_cnt);
+
+ rtw_enqueue_cmd23a(pcmdpriv, pcmd_obj);
+
+ return;
+}
+
+void report_join_res23a(struct rtw_adapter *padapter, int res)
+{
+ struct cmd_obj *pcmd_obj;
+ u8 *pevtcmd;
+ u32 cmdsz;
+ struct joinbss_event *pjoinbss_evt;
+ struct C2HEvent_Header *pc2h_evt_hdr;
+ struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv;
+ struct mlme_ext_info *pmlmeinfo = &pmlmeext->mlmext_info;
+ struct cmd_priv *pcmdpriv = &padapter->cmdpriv;
+
+ pcmd_obj = kzalloc(sizeof(struct cmd_obj), GFP_ATOMIC);
+ if (!pcmd_obj)
+ return;
+
+ cmdsz = sizeof(struct joinbss_event) + sizeof(struct C2HEvent_Header);
+ pevtcmd = kzalloc(cmdsz, GFP_ATOMIC);
+ if (!pevtcmd) {
+ kfree(pcmd_obj);
+ return;
+ }
+
+ pcmd_obj->cmdcode = GEN_CMD_CODE(_Set_MLME_EVT);
+ pcmd_obj->cmdsz = cmdsz;
+ pcmd_obj->parmbuf = pevtcmd;
+
+ pcmd_obj->rsp = NULL;
+ pcmd_obj->rspsz = 0;
+
+ pc2h_evt_hdr = (struct C2HEvent_Header *)(pevtcmd);
+ pc2h_evt_hdr->len = sizeof(struct joinbss_event);
+ pc2h_evt_hdr->ID = GEN_EVT_CODE(_JoinBss);
+ pc2h_evt_hdr->seq = atomic_inc_return(&pmlmeext->event_seq);
+
+ pjoinbss_evt = (struct joinbss_event*)(pevtcmd + sizeof(struct C2HEvent_Header));
+ memcpy((unsigned char *)&pjoinbss_evt->network.network,
+ &pmlmeinfo->network, sizeof(struct wlan_bssid_ex));
+ pjoinbss_evt->network.join_res = res;
+
+ DBG_8723A("report_join_res23a(%d)\n", res);
+
+ rtw_joinbss_event_prehandle23a(padapter, (u8 *)&pjoinbss_evt->network);
+
+ rtw_enqueue_cmd23a(pcmdpriv, pcmd_obj);
+
+ return;
+}
+
+void report_del_sta_event23a(struct rtw_adapter *padapter,
+ unsigned char *MacAddr, unsigned short reason)
+{
+ struct cmd_obj *pcmd_obj;
+ u8 *pevtcmd;
+ u32 cmdsz;
+ struct sta_info *psta;
+ int mac_id;
+ struct stadel_event *pdel_sta_evt;
+ struct C2HEvent_Header *pc2h_evt_hdr;
+ struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv;
+ struct cmd_priv *pcmdpriv = &padapter->cmdpriv;
+
+ pcmd_obj = kzalloc(sizeof(struct cmd_obj), GFP_ATOMIC);
+ if (!pcmd_obj)
+ return;
+
+ cmdsz = sizeof(struct stadel_event) + sizeof(struct C2HEvent_Header);
+ pevtcmd = kzalloc(cmdsz, GFP_ATOMIC);
+ if (!pevtcmd) {
+ kfree(pcmd_obj);
+ return;
+ }
+
+ pcmd_obj->cmdcode = GEN_CMD_CODE(_Set_MLME_EVT);
+ pcmd_obj->cmdsz = cmdsz;
+ pcmd_obj->parmbuf = pevtcmd;
+
+ pcmd_obj->rsp = NULL;
+ pcmd_obj->rspsz = 0;
+
+ pc2h_evt_hdr = (struct C2HEvent_Header *)(pevtcmd);
+ pc2h_evt_hdr->len = sizeof(struct stadel_event);
+ pc2h_evt_hdr->ID = GEN_EVT_CODE(_DelSTA);
+ pc2h_evt_hdr->seq = atomic_inc_return(&pmlmeext->event_seq);
+
+ pdel_sta_evt = (struct stadel_event*)(pevtcmd + sizeof(struct C2HEvent_Header));
+ ether_addr_copy((unsigned char *)&pdel_sta_evt->macaddr, MacAddr);
+ memcpy((unsigned char *)pdel_sta_evt->rsvd, (unsigned char *)&reason,
+ 2);
+
+ psta = rtw_get_stainfo23a(&padapter->stapriv, MacAddr);
+ if (psta)
+ mac_id = (int)psta->mac_id;
+ else
+ mac_id = -1;
+
+ pdel_sta_evt->mac_id = mac_id;
+
+ DBG_8723A("report_del_sta_event23a: delete STA, mac_id =%d\n", mac_id);
+
+ rtw_enqueue_cmd23a(pcmdpriv, pcmd_obj);
+
+ return;
+}
+
+void report_add_sta_event23a(struct rtw_adapter *padapter,
+ unsigned char *MacAddr, int cam_idx)
+{
+ struct cmd_obj *pcmd_obj;
+ u8 *pevtcmd;
+ u32 cmdsz;
+ struct stassoc_event *padd_sta_evt;
+ struct C2HEvent_Header *pc2h_evt_hdr;
+ struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv;
+ struct cmd_priv *pcmdpriv = &padapter->cmdpriv;
+
+ pcmd_obj = kzalloc(sizeof(struct cmd_obj), GFP_ATOMIC);
+ if (!pcmd_obj)
+ return;
+
+ cmdsz = sizeof(struct stassoc_event) + sizeof(struct C2HEvent_Header);
+ pevtcmd = kzalloc(cmdsz, GFP_ATOMIC);
+ if (!pevtcmd) {
+ kfree(pcmd_obj);
+ return;
+ }
+
+ pcmd_obj->cmdcode = GEN_CMD_CODE(_Set_MLME_EVT);
+ pcmd_obj->cmdsz = cmdsz;
+ pcmd_obj->parmbuf = pevtcmd;
+
+ pcmd_obj->rsp = NULL;
+ pcmd_obj->rspsz = 0;
+
+ pc2h_evt_hdr = (struct C2HEvent_Header *)(pevtcmd);
+ pc2h_evt_hdr->len = sizeof(struct stassoc_event);
+ pc2h_evt_hdr->ID = GEN_EVT_CODE(_AddSTA);
+ pc2h_evt_hdr->seq = atomic_inc_return(&pmlmeext->event_seq);
+
+ padd_sta_evt = (struct stassoc_event*)(pevtcmd + sizeof(struct C2HEvent_Header));
+ ether_addr_copy((unsigned char *)&padd_sta_evt->macaddr, MacAddr);
+ padd_sta_evt->cam_id = cam_idx;
+
+ DBG_8723A("report_add_sta_event23a: add STA\n");
+
+ rtw_enqueue_cmd23a(pcmdpriv, pcmd_obj);
+
+ return;
+}
+
+/****************************************************************************
+
+Following are the event callback functions
+
+*****************************************************************************/
+
+/* for sta/adhoc mode */
+void update_sta_info23a(struct rtw_adapter *padapter, struct sta_info *psta)
+{
+ struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
+ struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv;
+ struct mlme_ext_info *pmlmeinfo = &pmlmeext->mlmext_info;
+
+ /* ERP */
+ VCS_update23a(padapter, psta);
+
+ /* HT */
+ if (pmlmepriv->htpriv.ht_option) {
+ psta->htpriv.ht_option = true;
+
+ psta->htpriv.ampdu_enable = pmlmepriv->htpriv.ampdu_enable;
+
+ if (support_short_GI23a(padapter, &pmlmeinfo->ht_cap))
+ psta->htpriv.sgi = true;
+
+ psta->qos_option = true;
+
+ } else {
+ psta->htpriv.ht_option = false;
+
+ psta->htpriv.ampdu_enable = false;
+
+ psta->htpriv.sgi = false;
+ psta->qos_option = false;
+
+ }
+ psta->htpriv.bwmode = pmlmeext->cur_bwmode;
+ psta->htpriv.ch_offset = pmlmeext->cur_ch_offset;
+
+ psta->htpriv.agg_enable_bitmap = 0x0;/* reset */
+ psta->htpriv.candidate_tid_bitmap = 0x0;/* reset */
+
+ /* QoS */
+ if (pmlmepriv->qos_option)
+ psta->qos_option = true;
+
+ psta->state = _FW_LINKED;
+}
+
+void mlmeext_joinbss_event_callback23a(struct rtw_adapter *padapter,
+ int join_res)
+{
+ struct sta_info *psta, *psta_bmc;
+ struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv;
+ struct mlme_ext_info *pmlmeinfo = &pmlmeext->mlmext_info;
+ struct wlan_bssid_ex *cur_network = &pmlmeinfo->network;
+ struct sta_priv *pstapriv = &padapter->stapriv;
+
+ if (join_res < 0) {
+ hw_var_set_mlme_join(padapter, 1);
+ hw_var_set_bssid(padapter, null_addr);
+
+ /* restore to initial setting. */
+ update_tx_basic_rate23a(padapter,
+ padapter->registrypriv.wireless_mode);
+
+ goto exit_mlmeext_joinbss_event_callback23a;
+ }
+
+ if ((pmlmeinfo->state&0x03) == MSR_ADHOC) {
+ /* for bc/mc */
+ psta_bmc = rtw_get_bcmc_stainfo23a(padapter);
+ if (psta_bmc) {
+ pmlmeinfo->FW_sta_info[psta_bmc->mac_id].psta = psta_bmc;
+ update_bmc_sta_support_rate23a(padapter, psta_bmc->mac_id);
+ Update_RA_Entry23a(padapter, psta_bmc);
+ }
+ }
+
+ /* turn on dynamic functions */
+ rtl8723a_odm_support_ability_set(padapter, DYNAMIC_ALL_FUNC_ENABLE);
+
+ /* update IOT-releated issue */
+ update_IOT_info23a(padapter);
+
+ HalSetBrateCfg23a(padapter, cur_network->SupportedRates);
+
+ /* BCN interval */
+ rtl8723a_set_beacon_interval(padapter, pmlmeinfo->bcn_interval);
+
+ /* update capability */
+ update_capinfo23a(padapter, pmlmeinfo->capability);
+
+ /* WMM, Update EDCA param */
+ WMMOnAssocRsp23a(padapter);
+
+ /* HT */
+ HTOnAssocRsp23a(padapter);
+
+ /* Set cur_channel&cur_bwmode&cur_ch_offset */
+ set_channel_bwmode23a(padapter, pmlmeext->cur_channel, pmlmeext->cur_ch_offset, pmlmeext->cur_bwmode);
+
+ psta = rtw_get_stainfo23a(pstapriv, cur_network->MacAddress);
+ if (psta) { /* only for infra. mode */
+ pmlmeinfo->FW_sta_info[psta->mac_id].psta = psta;
+
+ /* DBG_8723A("set_sta_rate23a\n"); */
+
+ psta->wireless_mode = pmlmeext->cur_wireless_mode;
+
+ /* set per sta rate after updating HT cap. */
+ set_sta_rate23a(padapter, psta);
+ }
+
+ hw_var_set_mlme_join(padapter, 2);
+
+ if ((pmlmeinfo->state&0x03) == MSR_INFRA) {
+ /* correcting TSF */
+ rtw_correct_TSF(padapter);
+
+ /* set_link_timer(pmlmeext, DISCONNECT_TO); */
+ }
+
+ rtw_lps_ctrl_wk_cmd23a(padapter, LPS_CTRL_CONNECT, 0);
+
+exit_mlmeext_joinbss_event_callback23a:
+ DBG_8723A("=>%s\n", __func__);
+}
+
+void mlmeext_sta_add_event_callback23a(struct rtw_adapter *padapter,
+ struct sta_info *psta)
+{
+ struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv;
+ struct mlme_ext_info *pmlmeinfo = &pmlmeext->mlmext_info;
+
+ DBG_8723A("%s\n", __func__);
+
+ if ((pmlmeinfo->state & 0x03) == MSR_ADHOC) {
+ /* adhoc master or sta_count>1 */
+ if (pmlmeinfo->state & WIFI_FW_ASSOC_SUCCESS) {
+ /* nothing to do */
+ } else { /* adhoc client */
+ /* correcting TSF */
+ rtw_correct_TSF(padapter);
+
+ /* start beacon */
+ if (send_beacon23a(padapter) != _SUCCESS) {
+ pmlmeinfo->FW_sta_info[psta->mac_id].status = 0;
+
+ pmlmeinfo->state ^= MSR_ADHOC;
+
+ return;
+ }
+
+ pmlmeinfo->state |= WIFI_FW_ASSOC_SUCCESS;
+ }
+ hw_var_set_mlme_join(padapter, 2);
+ }
+
+ pmlmeinfo->FW_sta_info[psta->mac_id].psta = psta;
+
+ /* rate radaptive */
+ Update_RA_Entry23a(padapter, psta);
+
+ /* update adhoc sta_info */
+ update_sta_info23a(padapter, psta);
+}
+
+void mlmeext_sta_del_event_callback23a(struct rtw_adapter *padapter)
+{
+ struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv;
+ struct mlme_ext_info *pmlmeinfo = &pmlmeext->mlmext_info;
+
+ if (is_client_associated_to_ap23a(padapter) ||
+ is_IBSS_empty23a(padapter)) {
+ /* set_opmode_cmd(padapter, infra_client_with_mlme); */
+
+ hw_var_set_mlme_disconnect(padapter);
+ hw_var_set_bssid(padapter, null_addr);
+
+ /* restore to initial setting. */
+ update_tx_basic_rate23a(padapter,
+ padapter->registrypriv.wireless_mode);
+
+ /* switch to the 20M Hz mode after disconnect */
+ pmlmeext->cur_bwmode = HT_CHANNEL_WIDTH_20;
+ pmlmeext->cur_ch_offset = HAL_PRIME_CHNL_OFFSET_DONT_CARE;
+
+ set_channel_bwmode23a(padapter, pmlmeext->cur_channel,
+ pmlmeext->cur_ch_offset,
+ pmlmeext->cur_bwmode);
+
+ flush_all_cam_entry23a(padapter);
+
+ pmlmeinfo->state = MSR_NOLINK;
+
+ /* set MSR to no link state -> infra. mode */
+ rtl8723a_set_media_status(padapter, MSR_INFRA);
+
+ del_timer_sync(&pmlmeext->link_timer);
+ }
+}
+
+static u8 chk_ap_is_alive(struct rtw_adapter *padapter, struct sta_info *psta)
+{
+ u8 ret = false;
+
+ if (sta_rx_data_pkts(psta) == sta_last_rx_data_pkts(psta) &&
+ sta_rx_beacon_pkts(psta) == sta_last_rx_beacon_pkts(psta) &&
+ sta_rx_probersp_pkts(psta) == sta_last_rx_probersp_pkts(psta))
+ ret = false;
+ else
+ ret = true;
+
+ sta_update_last_rx_pkts(psta);
+ return ret;
+}
+
+void linked_status_chk23a(struct rtw_adapter *padapter)
+{
+ u32 i;
+ struct sta_info *psta;
+ struct xmit_priv *pxmitpriv = &padapter->xmitpriv;
+ struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv;
+ struct mlme_ext_info *pmlmeinfo = &pmlmeext->mlmext_info;
+ struct sta_priv *pstapriv = &padapter->stapriv;
+
+ if (is_client_associated_to_ap23a(padapter)) {
+ /* linked infrastructure client mode */
+
+ int tx_chk = _SUCCESS, rx_chk = _SUCCESS;
+ int rx_chk_limit;
+
+ rx_chk_limit = 4;
+
+ psta = rtw_get_stainfo23a(pstapriv,
+ pmlmeinfo->network.MacAddress);
+ if (psta) {
+ bool is_p2p_enable = false;
+
+ if (chk_ap_is_alive(padapter, psta) == false)
+ rx_chk = _FAIL;
+
+ if (pxmitpriv->last_tx_pkts == pxmitpriv->tx_pkts)
+ tx_chk = _FAIL;
+
+ if (pmlmeext->active_keep_alive_check &&
+ (rx_chk == _FAIL || tx_chk == _FAIL)) {
+ u8 backup_oper_channel = 0;
+
+ /* switch to correct channel of current
+ network before issue keep-alive frames */
+ if (rtw_get_oper_ch23a(padapter) !=
+ pmlmeext->cur_channel) {
+ backup_oper_channel =
+ rtw_get_oper_ch23a(padapter);
+ SelectChannel23a(padapter,
+ pmlmeext->cur_channel);
+ }
+
+ if (rx_chk != _SUCCESS)
+ issue_probereq_ex(padapter, &pmlmeinfo->network.Ssid, psta->hwaddr, 3, 1);
+
+ if ((tx_chk != _SUCCESS &&
+ pmlmeinfo->link_count++ == 0xf) ||
+ rx_chk != _SUCCESS) {
+ tx_chk = issue_nulldata23a(padapter,
+ psta->hwaddr,
+ 0, 3, 1);
+ /* if tx acked and p2p disabled,
+ set rx_chk _SUCCESS to reset retry
+ count */
+ if (tx_chk == _SUCCESS &&
+ !is_p2p_enable)
+ rx_chk = _SUCCESS;
+ }
+
+ /* back to the original operation channel */
+ if (backup_oper_channel>0)
+ SelectChannel23a(padapter,
+ backup_oper_channel);
+ } else {
+ if (rx_chk != _SUCCESS) {
+ if (pmlmeext->retry == 0) {
+ issue_probereq(padapter, &pmlmeinfo->network.Ssid, pmlmeinfo->network.MacAddress);
+ issue_probereq(padapter, &pmlmeinfo->network.Ssid, pmlmeinfo->network.MacAddress);
+ issue_probereq(padapter, &pmlmeinfo->network.Ssid, pmlmeinfo->network.MacAddress);
+ }
+ }
+
+ if (tx_chk != _SUCCESS &&
+ pmlmeinfo->link_count++ == 0xf)
+ tx_chk = issue_nulldata23a(padapter,
+ NULL, 0, 1,
+ 0);
+ }
+
+ if (rx_chk == _FAIL) {
+ pmlmeext->retry++;
+ if (pmlmeext->retry > rx_chk_limit) {
+ DBG_8723A_LEVEL(_drv_always_,
+ "%s(%s): disconnect or "
+ "roaming\n", __func__,
+ padapter->pnetdev->name);
+ receive_disconnect23a(padapter, pmlmeinfo->network.MacAddress,
+ WLAN_REASON_EXPIRATION_CHK);
+ return;
+ }
+ } else
+ pmlmeext->retry = 0;
+
+ if (tx_chk == _FAIL)
+ pmlmeinfo->link_count &= 0xf;
+ else {
+ pxmitpriv->last_tx_pkts = pxmitpriv->tx_pkts;
+ pmlmeinfo->link_count = 0;
+ }
+
+ }
+ } else if (is_client_associated_to_ibss23a(padapter)) {
+ /* linked IBSS mode */
+ /* for each assoc list entry to check the rx pkt counter */
+ for (i = IBSS_START_MAC_ID; i < NUM_STA; i++) {
+ if (pmlmeinfo->FW_sta_info[i].status == 1) {
+ psta = pmlmeinfo->FW_sta_info[i].psta;
+
+ if (!psta)
+ continue;
+
+ if (pmlmeinfo->FW_sta_info[i].rx_pkt ==
+ sta_rx_pkts(psta)) {
+
+ if (pmlmeinfo->FW_sta_info[i].retry<3) {
+ pmlmeinfo->FW_sta_info[i].retry++;
+ } else {
+ pmlmeinfo->FW_sta_info[i].retry = 0;
+ pmlmeinfo->FW_sta_info[i].status = 0;
+ report_del_sta_event23a(padapter, psta->hwaddr,
+ 65535/* indicate disconnect caused by no rx */
+ );
+ }
+ } else {
+ pmlmeinfo->FW_sta_info[i].retry = 0;
+ pmlmeinfo->FW_sta_info[i].rx_pkt = (u32)sta_rx_pkts(psta);
+ }
+ }
+ }
+ /* set_link_timer(pmlmeext, DISCONNECT_TO); */
+ }
+}
+
+static void survey_timer_hdl(unsigned long data)
+{
+ struct rtw_adapter *padapter = (struct rtw_adapter *)data;
+ struct cmd_obj *ph2c;
+ struct sitesurvey_parm *psurveyPara;
+ struct cmd_priv *pcmdpriv = &padapter->cmdpriv;
+ struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv;
+
+ /* issue rtw_sitesurvey_cmd23a */
+ if (pmlmeext->sitesurvey_res.state > SCAN_START) {
+ if (pmlmeext->sitesurvey_res.state == SCAN_PROCESS)
+ pmlmeext->sitesurvey_res.channel_idx++;
+
+ if (pmlmeext->scan_abort == true) {
+ pmlmeext->sitesurvey_res.channel_idx =
+ pmlmeext->sitesurvey_res.ch_num;
+ DBG_8723A("%s idx:%d\n", __func__,
+ pmlmeext->sitesurvey_res.channel_idx);
+
+ pmlmeext->scan_abort = false;/* reset */
+ }
+
+ ph2c = kzalloc(sizeof(struct cmd_obj), GFP_ATOMIC);
+ if (!ph2c)
+ goto exit_survey_timer_hdl;
+
+ psurveyPara = kzalloc(sizeof(struct sitesurvey_parm),
+ GFP_ATOMIC);
+ if (!psurveyPara) {
+ kfree(ph2c);
+ goto exit_survey_timer_hdl;
+ }
+
+ init_h2fwcmd_w_parm_no_rsp(ph2c, psurveyPara,
+ GEN_CMD_CODE(_SiteSurvey));
+ rtw_enqueue_cmd23a(pcmdpriv, ph2c);
+ }
+
+exit_survey_timer_hdl:
+ return;
+}
+
+static void link_timer_hdl(unsigned long data)
+{
+ struct rtw_adapter *padapter = (struct rtw_adapter *)data;
+ /* static unsigned int rx_pkt = 0; */
+ /* static u64 tx_cnt = 0; */
+ /* struct xmit_priv *pxmitpriv = &padapter->xmitpriv; */
+ struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv;
+ struct mlme_ext_info *pmlmeinfo = &pmlmeext->mlmext_info;
+ /* struct sta_priv *pstapriv = &padapter->stapriv; */
+
+ if (pmlmeinfo->state & WIFI_FW_AUTH_NULL) {
+ DBG_8723A("link_timer_hdl:no beacon while connecting\n");
+ pmlmeinfo->state = MSR_NOLINK;
+ report_join_res23a(padapter, -3);
+ } else if (pmlmeinfo->state & WIFI_FW_AUTH_STATE) {
+ /* re-auth timer */
+ if (++pmlmeinfo->reauth_count > REAUTH_LIMIT) {
+ /* if (pmlmeinfo->auth_algo != dot11AuthAlgrthm_Auto) */
+ /* */
+ pmlmeinfo->state = 0;
+ report_join_res23a(padapter, -1);
+ return;
+ /* */
+ /* else */
+ /* */
+ /* pmlmeinfo->auth_algo = dot11AuthAlgrthm_Shared; */
+ /* pmlmeinfo->reauth_count = 0; */
+ /* */
+ }
+
+ DBG_8723A("link_timer_hdl: auth timeout and try again\n");
+ pmlmeinfo->auth_seq = 1;
+ issue_auth(padapter, NULL, 0);
+ set_link_timer(pmlmeext, REAUTH_TO);
+ } else if (pmlmeinfo->state & WIFI_FW_ASSOC_STATE) {
+ /* re-assoc timer */
+ if (++pmlmeinfo->reassoc_count > REASSOC_LIMIT) {
+ pmlmeinfo->state = MSR_NOLINK;
+ report_join_res23a(padapter, -2);
+ return;
+ }
+
+ DBG_8723A("link_timer_hdl: assoc timeout and try again\n");
+ issue_assocreq(padapter);
+ set_link_timer(pmlmeext, REASSOC_TO);
+ }
+
+ return;
+}
+
+static void addba_timer_hdl(unsigned long data)
+{
+ struct sta_info *psta = (struct sta_info *)data;
+ struct ht_priv *phtpriv;
+
+ if (!psta)
+ return;
+
+ phtpriv = &psta->htpriv;
+
+ if (phtpriv->ht_option && phtpriv->ampdu_enable) {
+ if (phtpriv->candidate_tid_bitmap)
+ phtpriv->candidate_tid_bitmap = 0x0;
+ }
+}
+
+void init_addba_retry_timer23a(struct sta_info *psta)
+{
+ setup_timer(&psta->addba_retry_timer, addba_timer_hdl,
+ (unsigned long)psta);
+}
+
+void init_mlme_ext_timer23a(struct rtw_adapter *padapter)
+{
+ struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv;
+
+ setup_timer(&pmlmeext->survey_timer, survey_timer_hdl,
+ (unsigned long)padapter);
+
+ setup_timer(&pmlmeext->link_timer, link_timer_hdl,
+ (unsigned long)padapter);
+}
+
+int NULL_hdl23a(struct rtw_adapter *padapter, const u8 *pbuf)
+{
+ return H2C_SUCCESS;
+}
+
+int setopmode_hdl23a(struct rtw_adapter *padapter, const u8 *pbuf)
+{
+ enum nl80211_iftype type;
+ struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv;
+ struct mlme_ext_info *pmlmeinfo = &pmlmeext->mlmext_info;
+ const struct setopmode_parm *psetop = (struct setopmode_parm *)pbuf;
+
+ switch (psetop->mode) {
+ case NL80211_IFTYPE_P2P_GO:
+ case NL80211_IFTYPE_AP:
+ pmlmeinfo->state = MSR_AP;
+ type = MSR_AP;
+ break;
+ case NL80211_IFTYPE_P2P_CLIENT:
+ case NL80211_IFTYPE_STATION:
+ /* clear state */
+ pmlmeinfo->state &= ~(BIT(0)|BIT(1));
+ /* set to STATION_STATE */
+ pmlmeinfo->state |= MSR_INFRA;
+ type = MSR_INFRA;
+ break;
+ case NL80211_IFTYPE_ADHOC:
+ type = MSR_ADHOC;
+ break;
+ default:
+ type = MSR_NOLINK;
+ break;
+ }
+
+ hw_var_set_opmode(padapter, type);
+ /* Set_NETYPE0_MSR(padapter, type); */
+
+ return H2C_SUCCESS;
+}
+
+int createbss_hdl23a(struct rtw_adapter *padapter, const u8 *pbuf)
+{
+ struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv;
+ struct mlme_ext_info *pmlmeinfo = &pmlmeext->mlmext_info;
+ struct wlan_bssid_ex *pnetwork = &pmlmeinfo->network;
+ const struct wlan_bssid_ex *pparm = (struct wlan_bssid_ex *)pbuf;
+ /* u32 initialgain; */
+
+ if (pparm->ifmode == NL80211_IFTYPE_AP ||
+ pparm->ifmode == NL80211_IFTYPE_P2P_GO) {
+#ifdef CONFIG_8723AU_AP_MODE
+ if (pmlmeinfo->state == MSR_AP) {
+ /* todo: */
+ return H2C_SUCCESS;
+ }
+#endif
+ }
+
+ /* below is for ad-hoc master */
+ if (pparm->ifmode == NL80211_IFTYPE_ADHOC) {
+ rtw_joinbss_reset23a(padapter);
+
+ pmlmeext->cur_bwmode = HT_CHANNEL_WIDTH_20;
+ pmlmeext->cur_ch_offset = HAL_PRIME_CHNL_OFFSET_DONT_CARE;
+ pmlmeinfo->ERP_enable = 0;
+ pmlmeinfo->WMM_enable = 0;
+ pmlmeinfo->HT_enable = 0;
+ pmlmeinfo->HT_caps_enable = 0;
+ pmlmeinfo->HT_info_enable = 0;
+
+ /* disable dynamic functions, such as high power, DIG */
+ rtl8723a_odm_support_ability_backup(padapter);
+
+ rtl8723a_odm_support_ability_clr(padapter,
+ DYNAMIC_FUNC_DISABLE);
+
+ /* cancel link timer */
+ del_timer_sync(&pmlmeext->link_timer);
+
+ /* clear CAM */
+ flush_all_cam_entry23a(padapter);
+
+ if (pparm->IELength > MAX_IE_SZ)/* Check pbuf->IELength */
+ return H2C_PARAMETERS_ERROR;
+
+ memcpy(pnetwork, pparm, sizeof(struct wlan_bssid_ex));
+
+ start_create_ibss(padapter);
+ }
+
+ return H2C_SUCCESS;
+}
+
+int join_cmd_hdl23a(struct rtw_adapter *padapter, const u8 *pbuf)
+{
+ struct registry_priv *pregpriv = &padapter->registrypriv;
+ struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv;
+ struct mlme_ext_info *pmlmeinfo = &pmlmeext->mlmext_info;
+ struct wlan_bssid_ex *pnetwork = &pmlmeinfo->network;
+ const struct wlan_bssid_ex *pparm = (struct wlan_bssid_ex *)pbuf;
+ struct ieee80211_ht_operation *pht_info;
+ u32 i;
+ u8 *p;
+ /* u32 initialgain; */
+ /* u32 acparm; */
+
+ /* check already connecting to AP or not */
+ if (pmlmeinfo->state & WIFI_FW_ASSOC_SUCCESS) {
+ if (pmlmeinfo->state & MSR_INFRA)
+ issue_deauth_ex(padapter, pnetwork->MacAddress,
+ WLAN_REASON_DEAUTH_LEAVING, 5, 100);
+
+ pmlmeinfo->state = MSR_NOLINK;
+
+ /* clear CAM */
+ flush_all_cam_entry23a(padapter);
+
+ del_timer_sync(&pmlmeext->link_timer);
+
+ /* set MSR to nolink -> infra. mode */
+ rtl8723a_set_media_status(padapter, MSR_INFRA);
+
+ hw_var_set_mlme_disconnect(padapter);
+ }
+
+ rtw_joinbss_reset23a(padapter);
+
+ pmlmeext->cur_bwmode = HT_CHANNEL_WIDTH_20;
+ pmlmeext->cur_ch_offset = HAL_PRIME_CHNL_OFFSET_DONT_CARE;
+ pmlmeinfo->ERP_enable = 0;
+ pmlmeinfo->WMM_enable = 0;
+ pmlmeinfo->HT_enable = 0;
+ pmlmeinfo->HT_caps_enable = 0;
+ pmlmeinfo->HT_info_enable = 0;
+ pmlmeinfo->bwmode_updated = false;
+ /* pmlmeinfo->assoc_AP_vendor = HT_IOT_PEER_MAX; */
+
+ if (pparm->IELength > MAX_IE_SZ)/* Check pbuf->IELength */
+ return H2C_PARAMETERS_ERROR;
+
+ memcpy(pnetwork, pbuf, sizeof(struct wlan_bssid_ex));
+
+ /* Check AP vendor to move rtw_joinbss_cmd23a() */
+ /* pmlmeinfo->assoc_AP_vendor = check_assoc_AP23a(pnetwork->IEs,
+ pnetwork->IELength); */
+
+ for (i = 0; i < pnetwork->IELength;) {
+ p = pnetwork->IEs + i;
+
+ switch (p[0]) {
+ case WLAN_EID_VENDOR_SPECIFIC:/* Get WMM IE. */
+ if (!memcmp(p + 2, WMM_OUI23A, 4))
+ pmlmeinfo->WMM_enable = 1;
+ break;
+
+ case WLAN_EID_HT_CAPABILITY: /* Get HT Cap IE. */
+ pmlmeinfo->HT_caps_enable = 1;
+ break;
+
+ case WLAN_EID_HT_OPERATION: /* Get HT Info IE. */
+ pmlmeinfo->HT_info_enable = 1;
+
+ /* spec case only for cisco's ap because cisco's ap
+ * issue assoc rsp using mcs rate @40MHz or @20MHz */
+ pht_info = (struct ieee80211_ht_operation *)(p + 2);
+
+ if (pregpriv->cbw40_enable &&
+ (pht_info->ht_param &
+ IEEE80211_HT_PARAM_CHAN_WIDTH_ANY)) {
+ /* switch to the 40M Hz mode according to AP */
+ pmlmeext->cur_bwmode = HT_CHANNEL_WIDTH_40;
+ switch (pht_info->ht_param &
+ IEEE80211_HT_PARAM_CHA_SEC_OFFSET) {
+ case IEEE80211_HT_PARAM_CHA_SEC_ABOVE:
+ pmlmeext->cur_ch_offset =
+ HAL_PRIME_CHNL_OFFSET_LOWER;
+ break;
+
+ case IEEE80211_HT_PARAM_CHA_SEC_BELOW:
+ pmlmeext->cur_ch_offset =
+ HAL_PRIME_CHNL_OFFSET_UPPER;
+ break;
+
+ default:
+ pmlmeext->cur_ch_offset =
+ HAL_PRIME_CHNL_OFFSET_DONT_CARE;
+ break;
+ }
+
+ DBG_8723A("set ch/bw before connected\n");
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ i += (p[1] + 2);
+ }
+
+ hw_var_set_bssid(padapter, pmlmeinfo->network.MacAddress);
+ hw_var_set_mlme_join(padapter, 0);
+
+ /* cancel link timer */
+ del_timer_sync(&pmlmeext->link_timer);
+
+ start_clnt_join(padapter);
+
+ return H2C_SUCCESS;
+}
+
+int disconnect_hdl23a(struct rtw_adapter *padapter, const u8 *pbuf)
+{
+ const struct disconnect_parm *param = (struct disconnect_parm *)pbuf;
+ struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv;
+ struct mlme_ext_info *pmlmeinfo = &pmlmeext->mlmext_info;
+ struct wlan_bssid_ex *pnetwork = &pmlmeinfo->network;
+
+ if (is_client_associated_to_ap23a(padapter)) {
+ issue_deauth_ex(padapter, pnetwork->MacAddress,
+ WLAN_REASON_DEAUTH_LEAVING,
+ param->deauth_timeout_ms/100, 100);
+ }
+
+ /* set_opmode_cmd(padapter, infra_client_with_mlme); */
+
+ /* pmlmeinfo->state = MSR_NOLINK; */
+
+ hw_var_set_mlme_disconnect(padapter);
+ hw_var_set_bssid(padapter, null_addr);
+
+ /* restore to initial setting. */
+ update_tx_basic_rate23a(padapter, padapter->registrypriv.wireless_mode);
+
+ if ((pmlmeinfo->state & 0x03) == MSR_ADHOC ||
+ (pmlmeinfo->state & 0x03) == MSR_AP)
+ rtl8723a_set_bcn_func(padapter, 0); /* Stop BCN */
+
+ /* set MSR to no link state -> infra. mode */
+ rtl8723a_set_media_status(padapter, MSR_INFRA);
+
+ pmlmeinfo->state = MSR_NOLINK;
+
+ /* switch to the 20M Hz mode after disconnect */
+ pmlmeext->cur_bwmode = HT_CHANNEL_WIDTH_20;
+ pmlmeext->cur_ch_offset = HAL_PRIME_CHNL_OFFSET_DONT_CARE;
+
+ set_channel_bwmode23a(padapter, pmlmeext->cur_channel,
+ pmlmeext->cur_ch_offset, pmlmeext->cur_bwmode);
+
+ flush_all_cam_entry23a(padapter);
+
+ del_timer_sync(&pmlmeext->link_timer);
+
+ rtw_free_uc_swdec_pending_queue23a(padapter);
+
+ return H2C_SUCCESS;
+}
+
+static int
+rtw_scan_ch_decision(struct rtw_adapter *padapter,
+ struct rtw_ieee80211_channel *out, u32 out_num,
+ const struct rtw_ieee80211_channel *in, u32 in_num)
+{
+ int i, j;
+ int scan_ch_num = 0;
+ int set_idx;
+ struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv;
+
+ /* clear out first */
+ memset(out, 0, sizeof(struct rtw_ieee80211_channel)*out_num);
+
+ /* acquire channels from in */
+ j = 0;
+ for (i = 0;i<in_num;i++) {
+ if (in[i].hw_value &&
+ !(in[i].flags & IEEE80211_CHAN_DISABLED) &&
+ (set_idx = rtw_ch_set_search_ch23a(pmlmeext->channel_set,
+ in[i].hw_value)) >= 0) {
+ memcpy(&out[j], &in[i],
+ sizeof(struct rtw_ieee80211_channel));
+
+ if (pmlmeext->channel_set[set_idx].ScanType ==
+ SCAN_PASSIVE)
+ out[j].flags &= IEEE80211_CHAN_NO_IR;
+
+ j++;
+ }
+ if (j>= out_num)
+ break;
+ }
+
+ /* if out is empty, use channel_set as default */
+ if (j == 0) {
+ for (i = 0;i<pmlmeext->max_chan_nums;i++) {
+ out[i].hw_value = pmlmeext->channel_set[i].ChannelNum;
+
+ if (pmlmeext->channel_set[i].ScanType == SCAN_PASSIVE)
+ out[i].flags &= IEEE80211_CHAN_NO_IR;
+
+ j++;
+ }
+ }
+
+ if (padapter->setband == GHZ_24) { /* 2.4G */
+ for (i = 0; i < j ; i++) {
+ if (out[i].hw_value > 35)
+ memset(&out[i], 0,
+ sizeof(struct rtw_ieee80211_channel));
+ else
+ scan_ch_num++;
+ }
+ j = scan_ch_num;
+ } else if (padapter->setband == GHZ_50) { /* 5G */
+ for (i = 0; i < j ; i++) {
+ if (out[i].hw_value > 35) {
+ memcpy(&out[scan_ch_num++], &out[i],
+ sizeof(struct rtw_ieee80211_channel));
+ }
+ }
+ j = scan_ch_num;
+ } else
+ {}
+
+ return j;
+}
+
+int sitesurvey_cmd_hdl23a(struct rtw_adapter *padapter, const u8 *pbuf)
+{
+ struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv;
+ const struct sitesurvey_parm *pparm = (struct sitesurvey_parm *)pbuf;
+ u8 bdelayscan = false;
+ u32 initialgain;
+ u32 i;
+
+ if (pmlmeext->sitesurvey_res.state == SCAN_DISABLE) {
+ pmlmeext->sitesurvey_res.state = SCAN_START;
+ pmlmeext->sitesurvey_res.bss_cnt = 0;
+ pmlmeext->sitesurvey_res.channel_idx = 0;
+
+ for (i = 0; i < RTW_SSID_SCAN_AMOUNT; i++) {
+ if (pparm->ssid[i].ssid_len) {
+ memcpy(pmlmeext->sitesurvey_res.ssid[i].ssid,
+ pparm->ssid[i].ssid,
+ IEEE80211_MAX_SSID_LEN);
+ pmlmeext->sitesurvey_res.ssid[i].ssid_len =
+ pparm->ssid[i].ssid_len;
+ } else {
+ pmlmeext->sitesurvey_res.ssid[i].ssid_len = 0;
+ }
+ }
+
+ pmlmeext->sitesurvey_res.ch_num =
+ rtw_scan_ch_decision(padapter,
+ pmlmeext->sitesurvey_res.ch,
+ RTW_CHANNEL_SCAN_AMOUNT,
+ pparm->ch, pparm->ch_num);
+
+ pmlmeext->sitesurvey_res.scan_mode = pparm->scan_mode;
+
+ /* issue null data if associating to the AP */
+ if (is_client_associated_to_ap23a(padapter)) {
+ pmlmeext->sitesurvey_res.state = SCAN_TXNULL;
+
+ /* switch to correct channel of current network
+ before issue keep-alive frames */
+ if (rtw_get_oper_ch23a(padapter) !=
+ pmlmeext->cur_channel)
+ SelectChannel23a(padapter,
+ pmlmeext->cur_channel);
+
+ issue_nulldata23a(padapter, NULL, 1, 3, 500);
+
+ bdelayscan = true;
+ }
+
+ if (bdelayscan) {
+ /* delay 50ms to protect nulldata(1). */
+ set_survey_timer(pmlmeext, 50);
+ return H2C_SUCCESS;
+ }
+ }
+
+ if (pmlmeext->sitesurvey_res.state == SCAN_START ||
+ pmlmeext->sitesurvey_res.state == SCAN_TXNULL) {
+ /* disable dynamic functions, such as high power, DIG */
+ rtl8723a_odm_support_ability_backup(padapter);
+ rtl8723a_odm_support_ability_clr(padapter,
+ DYNAMIC_FUNC_DISABLE);
+
+ /* config the initial gain under scanning, need to
+ write the BB registers */
+ if (wdev_to_priv(padapter->rtw_wdev)->p2p_enabled == true)
+ initialgain = 0x30;
+ else
+ initialgain = 0x1E;
+
+ rtl8723a_set_initial_gain(padapter, initialgain);
+
+ /* set MSR to no link state */
+ rtl8723a_set_media_status(padapter, MSR_NOLINK);
+
+ rtl8723a_mlme_sitesurvey(padapter, 1);
+
+ pmlmeext->sitesurvey_res.state = SCAN_PROCESS;
+ }
+
+ rtw_site_survey(padapter);
+
+ return H2C_SUCCESS;
+}
+
+int setauth_hdl23a(struct rtw_adapter *padapter, const u8 *pbuf)
+{
+ const struct setauth_parm *pparm = (struct setauth_parm *)pbuf;
+ struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv;
+ struct mlme_ext_info *pmlmeinfo = &pmlmeext->mlmext_info;
+
+ if (pparm->mode < 4)
+ pmlmeinfo->auth_algo = pparm->mode;
+
+ return H2C_SUCCESS;
+}
+
+int setkey_hdl23a(struct rtw_adapter *padapter, const u8 *pbuf)
+{
+ unsigned short ctrl;
+ const struct setkey_parm *pparm = (struct setkey_parm *)pbuf;
+ struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv;
+ struct mlme_ext_info *pmlmeinfo = &pmlmeext->mlmext_info;
+ unsigned char null_sta[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+
+ /* main tx key for wep. */
+ if (pparm->set_tx)
+ pmlmeinfo->key_index = pparm->keyid;
+
+ /* write cam */
+ ctrl = BIT(15) | (pparm->algorithm) << 2 | pparm->keyid;
+
+ DBG_8723A_LEVEL(_drv_always_, "set group key to hw: alg:%d(WEP40-1 "
+ "WEP104-5 TKIP-2 AES-4) keyid:%d\n",
+ pparm->algorithm, pparm->keyid);
+ rtl8723a_cam_write(padapter, pparm->keyid, ctrl, null_sta, pparm->key);
+
+ /* allow multicast packets to driver */
+ rtl8723a_on_rcr_am(padapter);
+
+ return H2C_SUCCESS;
+}
+
+int set_stakey_hdl23a(struct rtw_adapter *padapter, const u8 *pbuf)
+{
+ u16 ctrl = 0;
+ u8 cam_id;/* cam_entry */
+ struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv;
+ struct mlme_ext_info *pmlmeinfo = &pmlmeext->mlmext_info;
+ const struct set_stakey_parm *pparm = (struct set_stakey_parm *)pbuf;
+
+ /* cam_entry: */
+ /* 0~3 for default key */
+
+ /* for concurrent mode (ap+sta): */
+ /* default key is disable, using sw encrypt/decrypt */
+ /* cam_entry = 4 for sta mode (macid = 0) */
+ /* cam_entry(macid+3) = 5 ~ N for ap mode (aid = 1~N, macid = 2 ~N) */
+
+ /* for concurrent mode (sta+sta): */
+ /* default key is disable, using sw encrypt/decrypt */
+ /* cam_entry = 4 mapping to macid = 0 */
+ /* cam_entry = 5 mapping to macid = 2 */
+
+ cam_id = 4;
+
+ DBG_8723A_LEVEL(_drv_always_, "set pairwise key to hw: alg:%d(WEP40-1 "
+ "WEP104-5 TKIP-2 AES-4) camid:%d\n",
+ pparm->algorithm, cam_id);
+ if ((pmlmeinfo->state & 0x03) == MSR_AP) {
+ struct sta_info *psta;
+ struct sta_priv *pstapriv = &padapter->stapriv;
+
+ if (pparm->algorithm == 0) { /* clear cam entry */
+ clear_cam_entry23a(padapter, pparm->id);
+ return H2C_SUCCESS_RSP;
+ }
+
+ psta = rtw_get_stainfo23a(pstapriv, pparm->addr);
+ if (psta) {
+ ctrl = BIT(15) | (pparm->algorithm << 2);
+
+ DBG_8723A("r871x_set_stakey_hdl23a(): enc_algorithm "
+ "=%d\n", pparm->algorithm);
+
+ if (psta->mac_id < 1 || psta->mac_id > (NUM_STA - 4)) {
+ DBG_8723A("r871x_set_stakey_hdl23a():set_stakey"
+ " failed, mac_id(aid) =%d\n",
+ psta->mac_id);
+ return H2C_REJECTED;
+ }
+
+ /* 0~3 for default key, cmd_id = macid + 3,
+ macid = aid+1; */
+ cam_id = psta->mac_id + 3;
+
+ DBG_8723A("Write CAM, mac_addr =%x:%x:%x:%x:%x:%x, "
+ "cam_entry =%d\n", pparm->addr[0],
+ pparm->addr[1], pparm->addr[2],
+ pparm->addr[3], pparm->addr[4],
+ pparm->addr[5], cam_id);
+
+ rtl8723a_cam_write(padapter, cam_id, ctrl,
+ pparm->addr, pparm->key);
+
+ return H2C_SUCCESS_RSP;
+ } else {
+ DBG_8723A("r871x_set_stakey_hdl23a(): sta has been "
+ "free\n");
+ return H2C_REJECTED;
+ }
+ }
+
+ /* below for sta mode */
+
+ if (pparm->algorithm == 0) { /* clear cam entry */
+ clear_cam_entry23a(padapter, pparm->id);
+ return H2C_SUCCESS;
+ }
+
+ ctrl = BIT(15) | (pparm->algorithm << 2);
+
+ rtl8723a_cam_write(padapter, cam_id, ctrl, pparm->addr, pparm->key);
+
+ pmlmeinfo->enc_algo = pparm->algorithm;
+
+ return H2C_SUCCESS;
+}
+
+int add_ba_hdl23a(struct rtw_adapter *padapter, const u8 *pbuf)
+{
+ const struct addBaReq_parm *pparm = (struct addBaReq_parm *)pbuf;
+ struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv;
+ struct mlme_ext_info *pmlmeinfo = &pmlmeext->mlmext_info;
+ struct sta_info *psta;
+
+ psta = rtw_get_stainfo23a(&padapter->stapriv, pparm->addr);
+
+ if (!psta)
+ return H2C_SUCCESS;
+
+ if (((pmlmeinfo->state & WIFI_FW_ASSOC_SUCCESS) &&
+ pmlmeinfo->HT_enable) ||
+ (pmlmeinfo->state & 0x03) == MSR_AP) {
+ issue_action_BA23a(padapter, pparm->addr,
+ WLAN_ACTION_ADDBA_REQ, (u16)pparm->tid);
+ mod_timer(&psta->addba_retry_timer,
+ jiffies + msecs_to_jiffies(ADDBA_TO));
+ } else
+ psta->htpriv.candidate_tid_bitmap &= ~BIT(pparm->tid);
+
+ return H2C_SUCCESS;
+}
+
+int set_tx_beacon_cmd23a(struct rtw_adapter *padapter)
+{
+ struct cmd_obj *ph2c;
+ struct Tx_Beacon_param *ptxBeacon_parm;
+ struct cmd_priv *pcmdpriv = &padapter->cmdpriv;
+ struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv;
+ struct mlme_ext_info *pmlmeinfo = &pmlmeext->mlmext_info;
+ u8 res = _SUCCESS;
+ int len_diff = 0;
+
+ ph2c = kzalloc(sizeof(struct cmd_obj), GFP_ATOMIC);
+ if (!ph2c) {
+ res = _FAIL;
+ goto exit;
+ }
+
+ ptxBeacon_parm = kzalloc(sizeof(struct Tx_Beacon_param), GFP_ATOMIC);
+ if (!ptxBeacon_parm) {
+ kfree(ph2c);
+ res = _FAIL;
+ goto exit;
+ }
+
+ memcpy(&ptxBeacon_parm->network, &pmlmeinfo->network,
+ sizeof(struct wlan_bssid_ex));
+
+ len_diff = update_hidden_ssid(ptxBeacon_parm->network.IEs,
+ ptxBeacon_parm->network.IELength,
+ pmlmeinfo->hidden_ssid_mode);
+ ptxBeacon_parm->network.IELength += len_diff;
+
+ init_h2fwcmd_w_parm_no_rsp(ph2c, ptxBeacon_parm,
+ GEN_CMD_CODE(_TX_Beacon));
+
+ res = rtw_enqueue_cmd23a(pcmdpriv, ph2c);
+
+exit:
+ return res;
+}
+
+int mlme_evt_hdl23a(struct rtw_adapter *padapter, const u8 *pbuf)
+{
+ u8 evt_code, evt_seq;
+ u16 evt_sz;
+ const struct C2HEvent_Header *c2h;
+ void (*event_callback)(struct rtw_adapter *dev, const u8 *pbuf);
+
+ c2h = (struct C2HEvent_Header *)pbuf;
+ evt_sz = c2h->len;
+ evt_seq = c2h->seq;
+ evt_code = c2h->ID;
+
+ /* checking if event code is valid */
+ if (evt_code >= MAX_C2HEVT) {
+ RT_TRACE(_module_rtl871x_cmd_c_, _drv_err_,
+ "Event Code(%d) mismatch!\n", evt_code);
+ goto _abort_event_;
+ }
+
+ /* checking if event size match the event parm size */
+ if (wlanevents[evt_code].parmsize != 0 &&
+ wlanevents[evt_code].parmsize != evt_sz) {
+ RT_TRACE(_module_rtl871x_cmd_c_, _drv_err_,
+ "Event(%d) Parm Size mismatch (%d vs %d)!\n",
+ evt_code, wlanevents[evt_code].parmsize, evt_sz);
+ goto _abort_event_;
+ }
+
+ event_callback = wlanevents[evt_code].event_callback;
+ event_callback(padapter, pbuf + sizeof(struct C2HEvent_Header));
+
+_abort_event_:
+
+ return H2C_SUCCESS;
+}
+
+int h2c_msg_hdl23a(struct rtw_adapter *padapter, const u8 *pbuf)
+{
+ if (!pbuf)
+ return H2C_PARAMETERS_ERROR;
+
+ return H2C_SUCCESS;
+}
+
+int tx_beacon_hdl23a(struct rtw_adapter *padapter, const u8 *pbuf)
+{
+ if (send_beacon23a(padapter) == _FAIL) {
+ DBG_8723A("issue_beacon23a, fail!\n");
+ return H2C_PARAMETERS_ERROR;
+ }
+#ifdef CONFIG_8723AU_AP_MODE
+ else { /* tx bc/mc frames after update TIM */
+ struct sta_info *psta_bmc;
+ struct list_head *plist, *phead, *ptmp;
+ struct xmit_frame *pxmitframe;
+ struct xmit_priv *pxmitpriv = &padapter->xmitpriv;
+ struct sta_priv *pstapriv = &padapter->stapriv;
+
+ /* for BC/MC Frames */
+ psta_bmc = rtw_get_bcmc_stainfo23a(padapter);
+ if (!psta_bmc)
+ return H2C_SUCCESS;
+
+ if (pstapriv->tim_bitmap & BIT(0) && psta_bmc->sleepq_len > 0) {
+ msleep(10);/* 10ms, ATIM(HIQ) Windows */
+ /* spin_lock_bh(&psta_bmc->sleep_q.lock); */
+ spin_lock_bh(&pxmitpriv->lock);
+
+ phead = get_list_head(&psta_bmc->sleep_q);
+
+ list_for_each_safe(plist, ptmp, phead) {
+ pxmitframe = container_of(plist,
+ struct xmit_frame,
+ list);
+
+ list_del_init(&pxmitframe->list);
+
+ psta_bmc->sleepq_len--;
+ if (psta_bmc->sleepq_len>0)
+ pxmitframe->attrib.mdata = 1;
+ else
+ pxmitframe->attrib.mdata = 0;
+
+ pxmitframe->attrib.triggered = 1;
+
+ pxmitframe->attrib.qsel = 0x11;/* HIQ */
+
+ rtl8723au_hal_xmitframe_enqueue(padapter,
+ pxmitframe);
+ }
+
+ /* spin_unlock_bh(&psta_bmc->sleep_q.lock); */
+ spin_unlock_bh(&pxmitpriv->lock);
+ }
+ }
+#endif
+
+ return H2C_SUCCESS;
+}
+
+int set_ch_hdl23a(struct rtw_adapter *padapter, const u8 *pbuf)
+{
+ const struct set_ch_parm *set_ch_parm;
+ struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv;
+
+ if (!pbuf)
+ return H2C_PARAMETERS_ERROR;
+
+ set_ch_parm = (struct set_ch_parm *)pbuf;
+
+ DBG_8723A("%s(%s): ch:%u, bw:%u, ch_offset:%u\n", __func__,
+ padapter->pnetdev->name, set_ch_parm->ch,
+ set_ch_parm->bw, set_ch_parm->ch_offset);
+
+ pmlmeext->cur_channel = set_ch_parm->ch;
+ pmlmeext->cur_ch_offset = set_ch_parm->ch_offset;
+ pmlmeext->cur_bwmode = set_ch_parm->bw;
+
+ set_channel_bwmode23a(padapter, set_ch_parm->ch,
+ set_ch_parm->ch_offset, set_ch_parm->bw);
+
+ return H2C_SUCCESS;
+}
+
+int set_chplan_hdl23a(struct rtw_adapter *padapter, const u8 *pbuf)
+{
+ const struct SetChannelPlan_param *setChannelPlan_param;
+ struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv;
+
+ if (!pbuf)
+ return H2C_PARAMETERS_ERROR;
+
+ setChannelPlan_param = (struct SetChannelPlan_param *)pbuf;
+
+ pmlmeext->max_chan_nums =
+ init_channel_set(padapter, setChannelPlan_param->channel_plan,
+ pmlmeext->channel_set);
+ init_channel_list(padapter, pmlmeext->channel_set,
+ pmlmeext->max_chan_nums, &pmlmeext->channel_list);
+
+ return H2C_SUCCESS;
+}
+
+int led_blink_hdl23a(struct rtw_adapter *padapter, const u8 *pbuf)
+{
+ struct LedBlink_param *ledBlink_param;
+
+ if (!pbuf)
+ return H2C_PARAMETERS_ERROR;
+
+ ledBlink_param = (struct LedBlink_param *)pbuf;
+
+ return H2C_SUCCESS;
+}
+
+int set_csa_hdl23a(struct rtw_adapter *padapter, const u8 *pbuf)
+{
+ return H2C_REJECTED;
+}
+
+/* TDLS_WRCR : write RCR DATA BIT */
+/* TDLS_SD_PTI : issue peer traffic indication */
+/* TDLS_CS_OFF : go back to the channel linked with AP,
+ terminating channel switch procedure */
+/* TDLS_INIT_CH_SEN : init channel sensing, receive all data and
+ mgnt frame */
+/* TDLS_DONE_CH_SEN : channel sensing and report candidate channel */
+/* TDLS_OFF_CH : first time set channel to off channel */
+/* TDLS_BASE_CH : go back tp the channel linked with AP when set
+ base channel as target channel */
+/* TDLS_P_OFF_CH : periodically go to off channel */
+/* TDLS_P_BASE_CH : periodically go back to base channel */
+/* TDLS_RS_RCR : restore RCR */
+/* TDLS_CKALV_PH1 : check alive timer phase1 */
+/* TDLS_CKALV_PH2 : check alive timer phase2 */
+/* TDLS_FREE_STA : free tdls sta */
+int tdls_hdl23a(struct rtw_adapter *padapter, const u8 *pbuf)
+{
+ return H2C_REJECTED;
+}
diff --git a/drivers/staging/rtl8723au/core/rtw_pwrctrl.c b/drivers/staging/rtl8723au/core/rtw_pwrctrl.c
new file mode 100644
index 000000000..7488a1049
--- /dev/null
+++ b/drivers/staging/rtl8723au/core/rtw_pwrctrl.c
@@ -0,0 +1,606 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2012 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ ******************************************************************************/
+#define _RTW_PWRCTRL_C_
+
+#include <osdep_service.h>
+#include <drv_types.h>
+#include <osdep_intf.h>
+#include <rtl8723a_cmd.h>
+#include <rtw_sreset.h>
+
+#include <rtl8723a_bt_intf.h>
+#include <usb_ops_linux.h>
+
+void ips_enter23a(struct rtw_adapter *padapter)
+{
+ struct pwrctrl_priv *pwrpriv = &padapter->pwrctrlpriv;
+
+ down(&pwrpriv->lock);
+
+ pwrpriv->bips_processing = true;
+
+ /* syn ips_mode with request */
+ pwrpriv->ips_mode = pwrpriv->ips_mode_req;
+
+ pwrpriv->ips_enter23a_cnts++;
+ DBG_8723A("==>ips_enter23a cnts:%d\n", pwrpriv->ips_enter23a_cnts);
+ rtl8723a_BT_disable_coexist(padapter);
+
+ if (pwrpriv->change_rfpwrstate == rf_off) {
+ pwrpriv->bpower_saving = true;
+ DBG_8723A_LEVEL(_drv_always_, "nolinked power save enter\n");
+
+ if (pwrpriv->ips_mode == IPS_LEVEL_2)
+ pwrpriv->bkeepfwalive = true;
+
+ rtw_ips_pwr_down23a(padapter);
+ pwrpriv->rf_pwrstate = rf_off;
+ }
+ pwrpriv->bips_processing = false;
+
+ up(&pwrpriv->lock);
+}
+
+int ips_leave23a(struct rtw_adapter *padapter)
+{
+ struct pwrctrl_priv *pwrpriv = &padapter->pwrctrlpriv;
+ struct security_priv *psecuritypriv = &padapter->securitypriv;
+ struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
+ int result = _SUCCESS;
+ int keyid;
+
+ down(&pwrpriv->lock);
+
+ if (pwrpriv->rf_pwrstate == rf_off && !pwrpriv->bips_processing) {
+ pwrpriv->bips_processing = true;
+ pwrpriv->change_rfpwrstate = rf_on;
+ pwrpriv->ips_leave23a_cnts++;
+ DBG_8723A("==>ips_leave23a cnts:%d\n",
+ pwrpriv->ips_leave23a_cnts);
+
+ result = rtw_ips_pwr_up23a(padapter);
+ if (result == _SUCCESS)
+ pwrpriv->rf_pwrstate = rf_on;
+
+ DBG_8723A_LEVEL(_drv_always_, "nolinked power save leave\n");
+
+ if (psecuritypriv->dot11PrivacyAlgrthm ==
+ WLAN_CIPHER_SUITE_WEP40 ||
+ psecuritypriv->dot11PrivacyAlgrthm ==
+ WLAN_CIPHER_SUITE_WEP104) {
+ DBG_8723A("==>%s, channel(%d), processing(%x)\n",
+ __func__, padapter->mlmeextpriv.cur_channel,
+ pwrpriv->bips_processing);
+ set_channel_bwmode23a(padapter,
+ padapter->mlmeextpriv.cur_channel,
+ HAL_PRIME_CHNL_OFFSET_DONT_CARE,
+ HT_CHANNEL_WIDTH_20);
+ for (keyid = 0; keyid < 4; keyid++) {
+ if (pmlmepriv->key_mask & BIT(keyid)) {
+ if (keyid ==
+ psecuritypriv->dot11PrivacyKeyIndex)
+ result = rtw_set_key23a(padapter, psecuritypriv, keyid, 1);
+ else
+ result = rtw_set_key23a(padapter, psecuritypriv, keyid, 0);
+ }
+ }
+ }
+
+ DBG_8723A("==> ips_leave23a.....LED(0x%08x)...\n",
+ rtl8723au_read32(padapter, 0x4c));
+ pwrpriv->bips_processing = false;
+
+ pwrpriv->bkeepfwalive = false;
+ pwrpriv->bpower_saving = false;
+ }
+
+ up(&pwrpriv->lock);
+
+ return result;
+}
+
+
+static bool rtw_pwr_unassociated_idle(struct rtw_adapter *adapter)
+{
+ struct mlme_priv *pmlmepriv = &adapter->mlmepriv;
+ struct xmit_priv *pxmit_priv = &adapter->xmitpriv;
+
+ bool ret = false;
+
+ if (time_after_eq(adapter->pwrctrlpriv.ips_deny_time, jiffies))
+ goto exit;
+
+ if (check_fwstate(pmlmepriv, WIFI_ASOC_STATE|WIFI_SITE_MONITOR) ||
+ check_fwstate(pmlmepriv, WIFI_UNDER_LINKING|WIFI_UNDER_WPS) ||
+ check_fwstate(pmlmepriv, WIFI_AP_STATE) ||
+ check_fwstate(pmlmepriv, WIFI_ADHOC_MASTER_STATE|WIFI_ADHOC_STATE)){
+ goto exit;
+ }
+
+ if (pxmit_priv->free_xmitbuf_cnt != NR_XMITBUFF ||
+ pxmit_priv->free_xmit_extbuf_cnt != NR_XMIT_EXTBUFF) {
+ DBG_8723A_LEVEL(_drv_always_,
+ "There are some pkts to transmit\n");
+ DBG_8723A_LEVEL(_drv_info_, "free_xmitbuf_cnt: %d, "
+ "free_xmit_extbuf_cnt: %d\n",
+ pxmit_priv->free_xmitbuf_cnt,
+ pxmit_priv->free_xmit_extbuf_cnt);
+ goto exit;
+ }
+
+ ret = true;
+
+exit:
+ return ret;
+}
+
+void rtw_ps_processor23a(struct rtw_adapter *padapter)
+{
+ struct pwrctrl_priv *pwrpriv = &padapter->pwrctrlpriv;
+ struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
+
+ pwrpriv->ps_processing = true;
+
+ if (pwrpriv->bips_processing == true)
+ goto exit;
+
+ if (pwrpriv->ips_mode_req == IPS_NONE)
+ goto exit;
+
+ if (!rtw_pwr_unassociated_idle(padapter))
+ goto exit;
+
+ if (pwrpriv->rf_pwrstate == rf_on &&
+ (pwrpriv->pwr_state_check_cnts % 4) == 0) {
+ DBG_8723A("==>%s .fw_state(%x)\n", __func__,
+ get_fwstate(pmlmepriv));
+ pwrpriv->change_rfpwrstate = rf_off;
+ ips_enter23a(padapter);
+ }
+exit:
+ rtw_set_pwr_state_check_timer(&padapter->pwrctrlpriv);
+ pwrpriv->ps_processing = false;
+}
+
+static void pwr_state_check_handler(unsigned long data)
+{
+ struct rtw_adapter *padapter = (struct rtw_adapter *)data;
+
+ rtw_ps_cmd23a(padapter);
+}
+
+/*
+ *
+ * Parameters
+ * padapter
+ * pslv power state level, only could be PS_STATE_S0 ~ PS_STATE_S4
+ *
+ */
+void rtw_set_rpwm23a(struct rtw_adapter *padapter, u8 pslv)
+{
+ u8 rpwm;
+ struct pwrctrl_priv *pwrpriv = &padapter->pwrctrlpriv;
+
+ pslv = PS_STATE(pslv);
+
+ if (pwrpriv->btcoex_rfon) {
+ if (pslv < PS_STATE_S4)
+ pslv = PS_STATE_S3;
+ }
+
+ if (pwrpriv->rpwm == pslv) {
+ RT_TRACE(_module_rtl871x_pwrctrl_c_, _drv_err_,
+ "%s: Already set rpwm[0x%02X], new = 0x%02X!\n",
+ __func__, pwrpriv->rpwm, pslv);
+ return;
+ }
+
+ if (padapter->bSurpriseRemoved == true ||
+ padapter->hw_init_completed == false) {
+ RT_TRACE(_module_rtl871x_pwrctrl_c_, _drv_err_,
+ "%s: SurpriseRemoved(%d) hw_init_completed(%d)\n",
+ __func__, padapter->bSurpriseRemoved,
+ padapter->hw_init_completed);
+
+ pwrpriv->cpwm = PS_STATE_S4;
+
+ return;
+ }
+
+ if (padapter->bDriverStopped == true) {
+ RT_TRACE(_module_rtl871x_pwrctrl_c_, _drv_err_,
+ "%s: change power state(0x%02X) when DriverStopped\n",
+ __func__, pslv);
+
+ if (pslv < PS_STATE_S2) {
+ RT_TRACE(_module_rtl871x_pwrctrl_c_, _drv_err_,
+ "%s: Reject to enter PS_STATE(0x%02X) lower than S2 when DriverStopped!!\n",
+ __func__, pslv);
+ return;
+ }
+ }
+
+ rpwm = pslv | pwrpriv->tog;
+ RT_TRACE(_module_rtl871x_pwrctrl_c_, _drv_notice_,
+ "rtw_set_rpwm23a: rpwm = 0x%02x cpwm = 0x%02x\n",
+ rpwm, pwrpriv->cpwm);
+
+ pwrpriv->rpwm = pslv;
+
+ rtl8723a_set_rpwm(padapter, rpwm);
+
+ pwrpriv->tog += 0x80;
+ pwrpriv->cpwm = pslv;
+}
+
+static bool PS_RDY_CHECK(struct rtw_adapter *padapter)
+{
+ unsigned long delta_time;
+ struct pwrctrl_priv *pwrpriv = &padapter->pwrctrlpriv;
+ struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
+
+ delta_time = jiffies - pwrpriv->DelayLPSLastTimeStamp;
+
+ if (delta_time < LPS_DELAY_TIME)
+ return false;
+
+ if (!check_fwstate(pmlmepriv, _FW_LINKED) ||
+ check_fwstate(pmlmepriv, _FW_UNDER_SURVEY) ||
+ check_fwstate(pmlmepriv, WIFI_AP_STATE) ||
+ check_fwstate(pmlmepriv, WIFI_ADHOC_MASTER_STATE) ||
+ check_fwstate(pmlmepriv, WIFI_ADHOC_STATE))
+ return false;
+ if (pwrpriv->bInSuspend)
+ return false;
+ if (padapter->securitypriv.dot11AuthAlgrthm == dot11AuthAlgrthm_8021X &&
+ !padapter->securitypriv.binstallGrpkey) {
+ DBG_8723A("Group handshake still in progress !!!\n");
+ return false;
+ }
+ if (!rtw_cfg80211_pwr_mgmt(padapter))
+ return false;
+
+ return true;
+}
+
+void rtw_set_ps_mode23a(struct rtw_adapter *padapter, u8 ps_mode,
+ u8 smart_ps, u8 bcn_ant_mode)
+{
+ struct pwrctrl_priv *pwrpriv = &padapter->pwrctrlpriv;
+
+ RT_TRACE(_module_rtl871x_pwrctrl_c_, _drv_notice_,
+ "%s: PowerMode =%d Smart_PS =%d\n",
+ __func__, ps_mode, smart_ps);
+
+ if (ps_mode > PM_Card_Disable) {
+ RT_TRACE(_module_rtl871x_pwrctrl_c_, _drv_err_,
+ "ps_mode:%d error\n", ps_mode);
+ return;
+ }
+
+ if (pwrpriv->pwr_mode == ps_mode) {
+ if (PS_MODE_ACTIVE == ps_mode)
+ return;
+
+ if (pwrpriv->smart_ps == smart_ps &&
+ pwrpriv->bcn_ant_mode == bcn_ant_mode)
+ return;
+ }
+
+ if (ps_mode == PS_MODE_ACTIVE) {
+ DBG_8723A("rtw_set_ps_mode23a: Leave 802.11 power save\n");
+
+ pwrpriv->pwr_mode = ps_mode;
+ rtw_set_rpwm23a(padapter, PS_STATE_S4);
+ rtl8723a_set_FwPwrMode_cmd(padapter, ps_mode);
+ pwrpriv->bFwCurrentInPSMode = false;
+ } else {
+ if (PS_RDY_CHECK(padapter) ||
+ rtl8723a_BT_using_antenna_1(padapter)) {
+ DBG_8723A("%s: Enter 802.11 power save\n", __func__);
+
+ pwrpriv->bFwCurrentInPSMode = true;
+ pwrpriv->pwr_mode = ps_mode;
+ pwrpriv->smart_ps = smart_ps;
+ pwrpriv->bcn_ant_mode = bcn_ant_mode;
+ rtl8723a_set_FwPwrMode_cmd(padapter, ps_mode);
+
+ rtw_set_rpwm23a(padapter, PS_STATE_S2);
+ }
+ }
+}
+
+/*
+ * Return:
+ * 0: Leave OK
+ * -1: Timeout
+ * -2: Other error
+ */
+s32 LPS_RF_ON_check23a(struct rtw_adapter *padapter, u32 delay_ms)
+{
+ unsigned long start_time, end_time;
+ u8 bAwake = false;
+ s32 err = 0;
+
+ start_time = jiffies;
+ end_time = start_time + msecs_to_jiffies(delay_ms);
+
+ while (1) {
+ bAwake = rtl8723a_get_fwlps_rf_on(padapter);
+ if (bAwake == true)
+ break;
+
+ if (padapter->bSurpriseRemoved == true) {
+ err = -2;
+ DBG_8723A("%s: device surprise removed!!\n", __func__);
+ break;
+ }
+
+ if (time_after(jiffies, end_time)) {
+ err = -1;
+ DBG_8723A("%s: Wait for FW LPS leave more than %u "
+ "ms!\n", __func__, delay_ms);
+ break;
+ }
+ udelay(100);
+ }
+
+ return err;
+}
+
+/* Description: */
+/* Enter the leisure power save mode. */
+void LPS_Enter23a(struct rtw_adapter *padapter)
+{
+ struct pwrctrl_priv *pwrpriv = &padapter->pwrctrlpriv;
+
+ if (!PS_RDY_CHECK(padapter))
+ return;
+
+ if (pwrpriv->bLeisurePs) {
+ /* Idle for a while if we connect to AP a while ago. */
+ if (pwrpriv->LpsIdleCount >= 2) { /* 4 Sec */
+ if (pwrpriv->pwr_mode == PS_MODE_ACTIVE) {
+ pwrpriv->bpower_saving = true;
+ DBG_8723A("%s smart_ps:%d\n", __func__,
+ pwrpriv->smart_ps);
+ /* For Tenda W311R IOT issue */
+ rtw_set_ps_mode23a(padapter,
+ pwrpriv->power_mgnt,
+ pwrpriv->smart_ps, 0);
+ }
+ } else
+ pwrpriv->LpsIdleCount++;
+ }
+}
+
+/* Description: */
+/* Leave the leisure power save mode. */
+void LPS_Leave23a(struct rtw_adapter *padapter)
+{
+#define LPS_LEAVE_TIMEOUT_MS 100
+
+ struct pwrctrl_priv *pwrpriv = &padapter->pwrctrlpriv;
+
+ if (pwrpriv->bLeisurePs) {
+ if (pwrpriv->pwr_mode != PS_MODE_ACTIVE) {
+ rtw_set_ps_mode23a(padapter, PS_MODE_ACTIVE, 0, 0);
+
+ if (pwrpriv->pwr_mode == PS_MODE_ACTIVE)
+ LPS_RF_ON_check23a(padapter,
+ LPS_LEAVE_TIMEOUT_MS);
+ }
+ }
+
+ pwrpriv->bpower_saving = false;
+}
+
+/* Description: Leave all power save mode: LPS, FwLPS, IPS if needed. */
+/* Move code to function by tynli. 2010.03.26. */
+void LeaveAllPowerSaveMode23a(struct rtw_adapter *Adapter)
+{
+ struct mlme_priv *pmlmepriv = &Adapter->mlmepriv;
+ u8 enqueue = 0;
+
+ /* DBG_8723A("%s.....\n", __func__); */
+ if (check_fwstate(pmlmepriv, _FW_LINKED))
+ rtw_lps_ctrl_wk_cmd23a(Adapter, LPS_CTRL_LEAVE, enqueue);
+}
+
+void rtw_init_pwrctrl_priv23a(struct rtw_adapter *padapter)
+{
+ struct pwrctrl_priv *pwrctrlpriv = &padapter->pwrctrlpriv;
+
+ sema_init(&pwrctrlpriv->lock, 1);
+ pwrctrlpriv->rf_pwrstate = rf_on;
+ pwrctrlpriv->ips_enter23a_cnts = 0;
+ pwrctrlpriv->ips_leave23a_cnts = 0;
+ pwrctrlpriv->bips_processing = false;
+
+ pwrctrlpriv->ips_mode = padapter->registrypriv.ips_mode;
+ pwrctrlpriv->ips_mode_req = padapter->registrypriv.ips_mode;
+
+ pwrctrlpriv->pwr_state_check_interval = RTW_PWR_STATE_CHK_INTERVAL;
+ pwrctrlpriv->pwr_state_check_cnts = 0;
+ pwrctrlpriv->bInSuspend = false;
+ pwrctrlpriv->bkeepfwalive = false;
+
+ pwrctrlpriv->LpsIdleCount = 0;
+
+ /* PS_MODE_MIN; */
+ pwrctrlpriv->power_mgnt = padapter->registrypriv.power_mgnt;
+ pwrctrlpriv->bLeisurePs =
+ (PS_MODE_ACTIVE != pwrctrlpriv->power_mgnt)?true:false;
+
+ pwrctrlpriv->bFwCurrentInPSMode = false;
+
+ pwrctrlpriv->rpwm = 0;
+ pwrctrlpriv->cpwm = PS_STATE_S4;
+
+ pwrctrlpriv->pwr_mode = PS_MODE_ACTIVE;
+ pwrctrlpriv->smart_ps = padapter->registrypriv.smart_ps;
+ pwrctrlpriv->bcn_ant_mode = 0;
+
+ pwrctrlpriv->tog = 0x80;
+
+ pwrctrlpriv->btcoex_rfon = false;
+
+ setup_timer(&pwrctrlpriv->pwr_state_check_timer,
+ pwr_state_check_handler, (unsigned long)padapter);
+}
+
+void rtw_free_pwrctrl_priv(struct rtw_adapter *adapter)
+{
+}
+
+inline void rtw_set_ips_deny23a(struct rtw_adapter *padapter, u32 ms)
+{
+ struct pwrctrl_priv *pwrpriv = &padapter->pwrctrlpriv;
+
+ pwrpriv->ips_deny_time = jiffies + msecs_to_jiffies(ms);
+}
+
+/*
+* rtw_pwr_wakeup - Wake the NIC up from: 1)IPS. 2)USB autosuspend
+* @adapter: pointer to _adapter structure
+* @ips_deffer_ms: the ms will prevent from falling into IPS after wakeup
+* Return _SUCCESS or _FAIL
+*/
+
+int _rtw_pwr_wakeup23a(struct rtw_adapter *padapter, u32 ips_deffer_ms, const char *caller)
+{
+ struct pwrctrl_priv *pwrpriv = &padapter->pwrctrlpriv;
+ struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
+ int ret = _SUCCESS;
+ unsigned long start = jiffies;
+ unsigned long new_deny_time;
+
+ new_deny_time = jiffies + msecs_to_jiffies(ips_deffer_ms);
+
+ if (time_before(pwrpriv->ips_deny_time, new_deny_time))
+ pwrpriv->ips_deny_time = new_deny_time;
+
+ if (pwrpriv->ps_processing) {
+ DBG_8723A("%s wait ps_processing...\n", __func__);
+ while (pwrpriv->ps_processing &&
+ jiffies_to_msecs(jiffies - start) <= 3000)
+ msleep(10);
+ if (pwrpriv->ps_processing)
+ DBG_8723A("%s wait ps_processing timeout\n", __func__);
+ else
+ DBG_8723A("%s wait ps_processing done\n", __func__);
+ }
+
+ if (rtw_sreset_inprogress(padapter)) {
+ DBG_8723A("%s wait sreset_inprogress...\n", __func__);
+ while (rtw_sreset_inprogress(padapter) &&
+ jiffies_to_msecs(jiffies - start) <= 4000)
+ msleep(10);
+ if (rtw_sreset_inprogress(padapter))
+ DBG_8723A("%s wait sreset_inprogress timeout\n",
+ __func__);
+ else
+ DBG_8723A("%s wait sreset_inprogress done\n", __func__);
+ }
+
+ if (pwrpriv->bInSuspend) {
+ DBG_8723A("%s wait bInSuspend...\n", __func__);
+ while (pwrpriv->bInSuspend &&
+ (jiffies_to_msecs(jiffies - start) <= 3000)) {
+ msleep(10);
+ }
+ if (pwrpriv->bInSuspend)
+ DBG_8723A("%s wait bInSuspend timeout\n", __func__);
+ else
+ DBG_8723A("%s wait bInSuspend done\n", __func__);
+ }
+
+ /* System suspend is not allowed to wakeup */
+ if (pwrpriv->bInSuspend) {
+ ret = _FAIL;
+ goto exit;
+ }
+
+ /* I think this should be check in IPS, LPS, autosuspend functions... */
+ if (check_fwstate(pmlmepriv, _FW_LINKED)) {
+ ret = _SUCCESS;
+ goto exit;
+ }
+
+ if (rf_off == pwrpriv->rf_pwrstate) {
+ DBG_8723A("%s call ips_leave23a....\n", __func__);
+ if (ips_leave23a(padapter)== _FAIL) {
+ DBG_8723A("======> ips_leave23a fail.............\n");
+ ret = _FAIL;
+ goto exit;
+ }
+ }
+
+ /* TODO: the following checking need to be merged... */
+ if (padapter->bDriverStopped || !padapter->bup ||
+ !padapter->hw_init_completed) {
+ DBG_8723A("%s: bDriverStopped =%d, bup =%d, hw_init_completed "
+ "=%u\n", caller, padapter->bDriverStopped,
+ padapter->bup, padapter->hw_init_completed);
+ ret = _FAIL;
+ goto exit;
+ }
+
+exit:
+ new_deny_time = jiffies + msecs_to_jiffies(ips_deffer_ms);
+ if (time_before(pwrpriv->ips_deny_time, new_deny_time))
+ pwrpriv->ips_deny_time = new_deny_time;
+ return ret;
+}
+
+int rtw_pm_set_lps23a(struct rtw_adapter *padapter, u8 mode)
+{
+ int ret = 0;
+ struct pwrctrl_priv *pwrctrlpriv = &padapter->pwrctrlpriv;
+
+ if (mode < PS_MODE_NUM) {
+ if (pwrctrlpriv->power_mgnt != mode) {
+ if (PS_MODE_ACTIVE == mode)
+ LeaveAllPowerSaveMode23a(padapter);
+ else
+ pwrctrlpriv->LpsIdleCount = 2;
+ pwrctrlpriv->power_mgnt = mode;
+ pwrctrlpriv->bLeisurePs =
+ (PS_MODE_ACTIVE != pwrctrlpriv->power_mgnt) ?
+ true:false;
+ }
+ } else
+ ret = -EINVAL;
+
+ return ret;
+}
+
+int rtw_pm_set_ips23a(struct rtw_adapter *padapter, u8 mode)
+{
+ struct pwrctrl_priv *pwrctrlpriv = &padapter->pwrctrlpriv;
+
+ if (mode != IPS_NORMAL && mode != IPS_LEVEL_2 && mode != IPS_NONE)
+ return -EINVAL;
+
+ pwrctrlpriv->ips_mode_req = mode;
+ if (mode == IPS_NONE) {
+ DBG_8723A("%s %s\n", __func__, "IPS_NONE");
+ if (padapter->bSurpriseRemoved == 0 &&
+ rtw_pwr_wakeup(padapter) == _FAIL)
+ return -EFAULT;
+ }
+
+ return 0;
+}
diff --git a/drivers/staging/rtl8723au/core/rtw_recv.c b/drivers/staging/rtl8723au/core/rtw_recv.c
new file mode 100644
index 000000000..274a4b65c
--- /dev/null
+++ b/drivers/staging/rtl8723au/core/rtw_recv.c
@@ -0,0 +1,2335 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2012 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ ******************************************************************************/
+#define _RTW_RECV_C_
+#include <osdep_service.h>
+#include <drv_types.h>
+#include <recv_osdep.h>
+#include <mlme_osdep.h>
+#include <linux/ip.h>
+#include <linux/if_ether.h>
+#include <usb_ops.h>
+#include <linux/ieee80211.h>
+#include <wifi.h>
+#include <rtl8723a_recv.h>
+#include <rtl8723a_xmit.h>
+
+void rtw_signal_stat_timer_hdl23a(unsigned long data);
+
+void _rtw_init_sta_recv_priv23a(struct sta_recv_priv *psta_recvpriv)
+{
+
+
+
+ spin_lock_init(&psta_recvpriv->lock);
+
+ /* for (i = 0; i<MAX_RX_NUMBLKS; i++) */
+ /* _rtw_init_queue23a(&psta_recvpriv->blk_strms[i]); */
+
+ _rtw_init_queue23a(&psta_recvpriv->defrag_q);
+
+
+}
+
+int _rtw_init_recv_priv23a(struct recv_priv *precvpriv,
+ struct rtw_adapter *padapter)
+{
+ struct recv_frame *precvframe;
+ int i;
+ int res = _SUCCESS;
+
+ spin_lock_init(&precvpriv->lock);
+
+ _rtw_init_queue23a(&precvpriv->free_recv_queue);
+ _rtw_init_queue23a(&precvpriv->recv_pending_queue);
+ _rtw_init_queue23a(&precvpriv->uc_swdec_pending_queue);
+
+ precvpriv->adapter = padapter;
+
+ for (i = 0; i < NR_RECVFRAME ; i++) {
+ precvframe = kzalloc(sizeof(struct recv_frame), GFP_KERNEL);
+ if (!precvframe)
+ break;
+ INIT_LIST_HEAD(&precvframe->list);
+
+ list_add_tail(&precvframe->list,
+ &precvpriv->free_recv_queue.queue);
+
+ precvframe->adapter = padapter;
+ precvframe++;
+ }
+
+ precvpriv->free_recvframe_cnt = i;
+ precvpriv->rx_pending_cnt = 1;
+
+ res = rtl8723au_init_recv_priv(padapter);
+
+ setup_timer(&precvpriv->signal_stat_timer, rtw_signal_stat_timer_hdl23a,
+ (unsigned long)padapter);
+
+ precvpriv->signal_stat_sampling_interval = 1000; /* ms */
+
+ rtw_set_signal_stat_timer(precvpriv);
+
+ return res;
+}
+
+void _rtw_free_recv_priv23a (struct recv_priv *precvpriv)
+{
+ struct rtw_adapter *padapter = precvpriv->adapter;
+ struct recv_frame *precvframe;
+ struct list_head *plist, *ptmp;
+
+ rtw_free_uc_swdec_pending_queue23a(padapter);
+
+ list_for_each_safe(plist, ptmp, &precvpriv->free_recv_queue.queue) {
+ precvframe = container_of(plist, struct recv_frame, list);
+ list_del_init(&precvframe->list);
+ kfree(precvframe);
+ }
+
+ rtl8723au_free_recv_priv(padapter);
+}
+
+struct recv_frame *rtw_alloc_recvframe23a(struct rtw_queue *pfree_recv_queue)
+{
+ struct recv_frame *pframe;
+ struct list_head *plist, *phead;
+ struct rtw_adapter *padapter;
+ struct recv_priv *precvpriv;
+
+ spin_lock_bh(&pfree_recv_queue->lock);
+
+ if (list_empty(&pfree_recv_queue->queue))
+ pframe = NULL;
+ else {
+ phead = get_list_head(pfree_recv_queue);
+
+ plist = phead->next;
+
+ pframe = container_of(plist, struct recv_frame, list);
+
+ list_del_init(&pframe->list);
+ padapter = pframe->adapter;
+ if (padapter) {
+ precvpriv = &padapter->recvpriv;
+ if (pfree_recv_queue == &precvpriv->free_recv_queue)
+ precvpriv->free_recvframe_cnt--;
+ }
+ }
+
+ spin_unlock_bh(&pfree_recv_queue->lock);
+
+ return pframe;
+}
+
+int rtw_free_recvframe23a(struct recv_frame *precvframe)
+{
+ struct rtw_adapter *padapter = precvframe->adapter;
+ struct recv_priv *precvpriv = &padapter->recvpriv;
+ struct rtw_queue *pfree_recv_queue;
+
+ if (precvframe->pkt) {
+ dev_kfree_skb_any(precvframe->pkt);/* free skb by driver */
+ precvframe->pkt = NULL;
+ }
+
+ pfree_recv_queue = &precvpriv->free_recv_queue;
+ spin_lock_bh(&pfree_recv_queue->lock);
+
+ list_del_init(&precvframe->list);
+
+ list_add_tail(&precvframe->list, get_list_head(pfree_recv_queue));
+
+ if (padapter) {
+ if (pfree_recv_queue == &precvpriv->free_recv_queue)
+ precvpriv->free_recvframe_cnt++;
+ }
+
+ spin_unlock_bh(&pfree_recv_queue->lock);
+
+
+
+ return _SUCCESS;
+}
+
+int rtw_enqueue_recvframe23a(struct recv_frame *precvframe, struct rtw_queue *queue)
+{
+ struct rtw_adapter *padapter = precvframe->adapter;
+ struct recv_priv *precvpriv = &padapter->recvpriv;
+
+ spin_lock_bh(&queue->lock);
+
+ list_del_init(&precvframe->list);
+
+ list_add_tail(&precvframe->list, get_list_head(queue));
+
+ if (padapter) {
+ if (queue == &precvpriv->free_recv_queue)
+ precvpriv->free_recvframe_cnt++;
+ }
+
+ spin_unlock_bh(&queue->lock);
+
+ return _SUCCESS;
+}
+
+/*
+caller : defrag ; recvframe_chk_defrag23a in recv_thread (passive)
+pframequeue: defrag_queue : will be accessed in recv_thread (passive)
+
+using spinlock to protect
+
+*/
+
+static void rtw_free_recvframe23a_queue(struct rtw_queue *pframequeue)
+{
+ struct recv_frame *hdr;
+ struct list_head *plist, *phead, *ptmp;
+
+ spin_lock(&pframequeue->lock);
+
+ phead = get_list_head(pframequeue);
+ plist = phead->next;
+
+ list_for_each_safe(plist, ptmp, phead) {
+ hdr = container_of(plist, struct recv_frame, list);
+ rtw_free_recvframe23a(hdr);
+ }
+
+ spin_unlock(&pframequeue->lock);
+}
+
+u32 rtw_free_uc_swdec_pending_queue23a(struct rtw_adapter *adapter)
+{
+ u32 cnt = 0;
+ struct recv_frame *pending_frame;
+
+ while ((pending_frame = rtw_alloc_recvframe23a(&adapter->recvpriv.uc_swdec_pending_queue))) {
+ rtw_free_recvframe23a(pending_frame);
+ DBG_8723A("%s: dequeue uc_swdec_pending_queue\n", __func__);
+ cnt++;
+ }
+
+ return cnt;
+}
+
+int rtw_enqueue_recvbuf23a_to_head(struct recv_buf *precvbuf, struct rtw_queue *queue)
+{
+ spin_lock_bh(&queue->lock);
+
+ list_del_init(&precvbuf->list);
+ list_add(&precvbuf->list, get_list_head(queue));
+
+ spin_unlock_bh(&queue->lock);
+
+ return _SUCCESS;
+}
+
+int rtw_enqueue_recvbuf23a(struct recv_buf *precvbuf, struct rtw_queue *queue)
+{
+ unsigned long irqL;
+
+ spin_lock_irqsave(&queue->lock, irqL);
+
+ list_del_init(&precvbuf->list);
+
+ list_add_tail(&precvbuf->list, get_list_head(queue));
+ spin_unlock_irqrestore(&queue->lock, irqL);
+ return _SUCCESS;
+}
+
+struct recv_buf *rtw_dequeue_recvbuf23a (struct rtw_queue *queue)
+{
+ unsigned long irqL;
+ struct recv_buf *precvbuf;
+ struct list_head *plist, *phead;
+
+ spin_lock_irqsave(&queue->lock, irqL);
+
+ if (list_empty(&queue->queue)) {
+ precvbuf = NULL;
+ } else {
+ phead = get_list_head(queue);
+
+ plist = phead->next;
+
+ precvbuf = container_of(plist, struct recv_buf, list);
+
+ list_del_init(&precvbuf->list);
+ }
+
+ spin_unlock_irqrestore(&queue->lock, irqL);
+
+ return precvbuf;
+}
+
+int recvframe_chkmic(struct rtw_adapter *adapter,
+ struct recv_frame *precvframe);
+int recvframe_chkmic(struct rtw_adapter *adapter,
+ struct recv_frame *precvframe) {
+
+ int i, res = _SUCCESS;
+ u32 datalen;
+ u8 miccode[8];
+ u8 bmic_err = false, brpt_micerror = true;
+ u8 *pframe, *payload, *pframemic;
+ u8 *mickey;
+ /* u8 *iv, rxdata_key_idx = 0; */
+ struct sta_info *stainfo;
+ struct rx_pkt_attrib *prxattrib = &precvframe->attrib;
+ struct security_priv *psecuritypriv = &adapter->securitypriv;
+
+ struct mlme_ext_priv *pmlmeext = &adapter->mlmeextpriv;
+ struct mlme_ext_info *pmlmeinfo = &pmlmeext->mlmext_info;
+
+
+ stainfo = rtw_get_stainfo23a(&adapter->stapriv, &prxattrib->ta[0]);
+
+ if (prxattrib->encrypt == WLAN_CIPHER_SUITE_TKIP) {
+ RT_TRACE(_module_rtl871x_recv_c_, _drv_info_,
+ "recvframe_chkmic:prxattrib->encrypt == WLAN_CIPHER_SUITE_TKIP\n");
+ RT_TRACE(_module_rtl871x_recv_c_, _drv_info_,
+ "recvframe_chkmic:da = %pM\n", prxattrib->ra);
+
+ /* calculate mic code */
+ if (stainfo != NULL) {
+ if (is_multicast_ether_addr(prxattrib->ra)) {
+ mickey = &psecuritypriv->dot118021XGrprxmickey[prxattrib->key_index].skey[0];
+
+ RT_TRACE(_module_rtl871x_recv_c_, _drv_info_,
+ "recvframe_chkmic: bcmc key\n");
+
+ if (!psecuritypriv->binstallGrpkey) {
+ res = _FAIL;
+ RT_TRACE(_module_rtl871x_recv_c_,
+ _drv_err_,
+ "recvframe_chkmic:didn't install group key!\n");
+ DBG_8723A("\n recvframe_chkmic:didn't "
+ "install group key!!!!!!\n");
+ goto exit;
+ }
+ } else {
+ mickey = &stainfo->dot11tkiprxmickey.skey[0];
+ RT_TRACE(_module_rtl871x_recv_c_, _drv_err_,
+ "recvframe_chkmic: unicast key\n");
+ }
+
+ /* icv_len included the mic code */
+ datalen = precvframe->pkt->len-prxattrib->
+ hdrlen-prxattrib->iv_len-prxattrib->icv_len - 8;
+ pframe = precvframe->pkt->data;
+ payload = pframe + prxattrib->hdrlen +
+ prxattrib->iv_len;
+
+ RT_TRACE(_module_rtl871x_recv_c_, _drv_info_,
+ "prxattrib->iv_len =%d prxattrib->icv_len =%d\n",
+ prxattrib->iv_len, prxattrib->icv_len);
+
+ /* care the length of the data */
+ rtw_seccalctkipmic23a(mickey, pframe, payload,
+ datalen, &miccode[0],
+ (unsigned char)prxattrib->priority);
+
+ pframemic = payload + datalen;
+
+ bmic_err = false;
+
+ for (i = 0; i < 8; i++) {
+ if (miccode[i] != *(pframemic + i)) {
+ RT_TRACE(_module_rtl871x_recv_c_,
+ _drv_err_,
+ "recvframe_chkmic:miccode[%d](%02x) != *(pframemic+%d)(%02x)\n",
+ i, miccode[i],
+ i, *(pframemic + i));
+ bmic_err = true;
+ }
+ }
+
+ if (bmic_err == true) {
+ int i;
+
+ RT_TRACE(_module_rtl871x_recv_c_, _drv_err_,
+ "*(pframemic-8)-*(pframemic-1) =0x%02x:0x%02x:0x%02x:0x%02x:0x%02x:0x%02x:0x%02x:0x%02x\n",
+ *(pframemic - 8), *(pframemic - 7),
+ *(pframemic - 6), *(pframemic - 5),
+ *(pframemic - 4), *(pframemic - 3),
+ *(pframemic - 2), *(pframemic - 1));
+ RT_TRACE(_module_rtl871x_recv_c_, _drv_err_,
+ "*(pframemic-16)-*(pframemic-9) =0x%02x:0x%02x:0x%02x:0x%02x:0x%02x:0x%02x:0x%02x:0x%02x\n",
+ *(pframemic - 16), *(pframemic - 15),
+ *(pframemic - 14), *(pframemic - 13),
+ *(pframemic - 12), *(pframemic - 11),
+ *(pframemic - 10), *(pframemic - 9));
+
+ RT_TRACE(_module_rtl871x_recv_c_, _drv_err_,
+ "====== demp packet (len =%d) ======\n",
+ precvframe->pkt->len);
+ for (i = 0; i < precvframe->pkt->len; i = i + 8) {
+ RT_TRACE(_module_rtl871x_recv_c_,
+ _drv_err_,
+ "0x%02x:0x%02x:0x%02x:0x%02x:0x%02x:0x%02x:0x%02x:0x%02x\n",
+ *(precvframe->pkt->data+i),
+ *(precvframe->pkt->data+i+1),
+ *(precvframe->pkt->data+i+2),
+ *(precvframe->pkt->data+i+3),
+ *(precvframe->pkt->data+i+4),
+ *(precvframe->pkt->data+i+5),
+ *(precvframe->pkt->data+i+6),
+ *(precvframe->pkt->data+i+7));
+ }
+ RT_TRACE(_module_rtl871x_recv_c_, _drv_err_,
+ "====== demp packet end [len =%d]======\n",
+ precvframe->pkt->len);
+ RT_TRACE(_module_rtl871x_recv_c_, _drv_err_,
+ "hrdlen =%d\n", prxattrib->hdrlen);
+
+ RT_TRACE(_module_rtl871x_recv_c_, _drv_err_,
+ "ra = %pM psecuritypriv->binstallGrpkey =%d\n",
+ prxattrib->ra,
+ psecuritypriv->binstallGrpkey);
+
+ /* double check key_index for some timing
+ issue, cannot compare with
+ psecuritypriv->dot118021XGrpKeyid also
+ cause timing issue */
+ if ((is_multicast_ether_addr(prxattrib->ra)) &&
+ (prxattrib->key_index !=
+ pmlmeinfo->key_index))
+ brpt_micerror = false;
+
+ if ((prxattrib->bdecrypted == true) &&
+ (brpt_micerror == true)) {
+ rtw_handle_tkip_mic_err23a(adapter, (u8)is_multicast_ether_addr(prxattrib->ra));
+ RT_TRACE(_module_rtl871x_recv_c_, _drv_err_,
+ "mic error :prxattrib->bdecrypted =%d\n",
+ prxattrib->bdecrypted);
+ DBG_8723A(" mic error :prxattrib->"
+ "bdecrypted =%d\n",
+ prxattrib->bdecrypted);
+ } else {
+ RT_TRACE(_module_rtl871x_recv_c_,
+ _drv_err_,
+ "mic error :prxattrib->bdecrypted =%d\n",
+ prxattrib->bdecrypted);
+ DBG_8723A(" mic error :prxattrib->"
+ "bdecrypted =%d\n",
+ prxattrib->bdecrypted);
+ }
+
+ res = _FAIL;
+ } else {
+ /* mic checked ok */
+ if (!psecuritypriv->bcheck_grpkey &&
+ is_multicast_ether_addr(prxattrib->ra)) {
+ psecuritypriv->bcheck_grpkey = 1;
+ RT_TRACE(_module_rtl871x_recv_c_,
+ _drv_err_,
+ "psecuritypriv->bcheck_grpkey = true\n");
+ }
+ }
+ } else {
+ RT_TRACE(_module_rtl871x_recv_c_, _drv_err_,
+ "recvframe_chkmic: rtw_get_stainfo23a ==NULL!!!\n");
+ }
+
+ skb_trim(precvframe->pkt, precvframe->pkt->len - 8);
+ }
+
+exit:
+
+
+
+ return res;
+}
+
+/* decrypt and set the ivlen, icvlen of the recv_frame */
+struct recv_frame *decryptor(struct rtw_adapter *padapter,
+ struct recv_frame *precv_frame);
+struct recv_frame *decryptor(struct rtw_adapter *padapter,
+ struct recv_frame *precv_frame)
+{
+ struct rx_pkt_attrib *prxattrib = &precv_frame->attrib;
+ struct security_priv *psecuritypriv = &padapter->securitypriv;
+ struct recv_frame *return_packet = precv_frame;
+ int res = _SUCCESS;
+
+ RT_TRACE(_module_rtl871x_recv_c_, _drv_info_,
+ "prxstat->decrypted =%x prxattrib->encrypt = 0x%03x\n",
+ prxattrib->bdecrypted, prxattrib->encrypt);
+
+ if (prxattrib->encrypt > 0) {
+ u8 *iv = precv_frame->pkt->data + prxattrib->hdrlen;
+
+ prxattrib->key_index = (((iv[3]) >> 6) & 0x3);
+
+ if (prxattrib->key_index > WEP_KEYS) {
+ DBG_8723A("prxattrib->key_index(%d) > WEP_KEYS\n",
+ prxattrib->key_index);
+
+ switch (prxattrib->encrypt) {
+ case WLAN_CIPHER_SUITE_WEP40:
+ case WLAN_CIPHER_SUITE_WEP104:
+ prxattrib->key_index =
+ psecuritypriv->dot11PrivacyKeyIndex;
+ break;
+ case WLAN_CIPHER_SUITE_TKIP:
+ case WLAN_CIPHER_SUITE_CCMP:
+ default:
+ prxattrib->key_index =
+ psecuritypriv->dot118021XGrpKeyid;
+ break;
+ }
+ }
+ }
+
+ if ((prxattrib->encrypt > 0) && ((prxattrib->bdecrypted == 0))) {
+ psecuritypriv->hw_decrypted = 0;
+ switch (prxattrib->encrypt) {
+ case WLAN_CIPHER_SUITE_WEP40:
+ case WLAN_CIPHER_SUITE_WEP104:
+ rtw_wep_decrypt23a(padapter, precv_frame);
+ break;
+ case WLAN_CIPHER_SUITE_TKIP:
+ res = rtw_tkip_decrypt23a(padapter, precv_frame);
+ break;
+ case WLAN_CIPHER_SUITE_CCMP:
+ res = rtw_aes_decrypt23a(padapter, precv_frame);
+ break;
+ default:
+ break;
+ }
+ } else if (prxattrib->bdecrypted == 1 && prxattrib->encrypt > 0 &&
+ (psecuritypriv->busetkipkey == 1 ||
+ prxattrib->encrypt != WLAN_CIPHER_SUITE_TKIP)) {
+ psecuritypriv->hw_decrypted = 1;
+ }
+
+ if (res == _FAIL) {
+ rtw_free_recvframe23a(return_packet);
+ return_packet = NULL;
+ }
+
+
+
+ return return_packet;
+}
+
+/* set the security information in the recv_frame */
+static struct recv_frame *portctrl(struct rtw_adapter *adapter,
+ struct recv_frame *precv_frame)
+{
+ u8 *psta_addr, *ptr;
+ uint auth_alg;
+ struct recv_frame *pfhdr;
+ struct sta_info *psta;
+ struct sta_priv *pstapriv ;
+ struct recv_frame *prtnframe;
+ u16 ether_type;
+ u16 eapol_type = ETH_P_PAE;/* for Funia BD's WPA issue */
+ struct rx_pkt_attrib *pattrib;
+
+ pstapriv = &adapter->stapriv;
+
+ auth_alg = adapter->securitypriv.dot11AuthAlgrthm;
+
+ pfhdr = precv_frame;
+ pattrib = &pfhdr->attrib;
+ psta_addr = pattrib->ta;
+ psta = rtw_get_stainfo23a(pstapriv, psta_addr);
+
+ RT_TRACE(_module_rtl871x_recv_c_, _drv_info_,
+ "########portctrl:adapter->securitypriv.dot11AuthAlgrthm =%d\n",
+ adapter->securitypriv.dot11AuthAlgrthm);
+
+ prtnframe = precv_frame;
+
+ if (auth_alg == dot11AuthAlgrthm_8021X) {
+ /* get ether_type */
+ ptr = pfhdr->pkt->data + pfhdr->attrib.hdrlen;
+
+ ether_type = (ptr[6] << 8) | ptr[7];
+
+ if (psta && psta->ieee8021x_blocked) {
+ /* blocked */
+ /* only accept EAPOL frame */
+ RT_TRACE(_module_rtl871x_recv_c_, _drv_info_,
+ "########portctrl:psta->ieee8021x_blocked ==1\n");
+
+ if (ether_type != eapol_type) {
+ /* free this frame */
+ rtw_free_recvframe23a(precv_frame);
+ prtnframe = NULL;
+ }
+ }
+ }
+
+ return prtnframe;
+}
+
+int recv_decache(struct recv_frame *precv_frame, u8 bretry,
+ struct stainfo_rxcache *prxcache);
+int recv_decache(struct recv_frame *precv_frame, u8 bretry,
+ struct stainfo_rxcache *prxcache)
+{
+ int tid = precv_frame->attrib.priority;
+
+ u16 seq_ctrl = ((precv_frame->attrib.seq_num & 0xffff) << 4) |
+ (precv_frame->attrib.frag_num & 0xf);
+
+
+
+ if (tid > 15) {
+ RT_TRACE(_module_rtl871x_recv_c_, _drv_notice_,
+ "recv_decache, (tid>15)! seq_ctrl = 0x%x, tid = 0x%x\n",
+ seq_ctrl, tid);
+
+ return _FAIL;
+ }
+
+ if (1) { /* if (bretry) */
+ if (seq_ctrl == prxcache->tid_rxseq[tid]) {
+ RT_TRACE(_module_rtl871x_recv_c_, _drv_notice_,
+ "recv_decache, seq_ctrl = 0x%x, tid = 0x%x, tid_rxseq = 0x%x\n",
+ seq_ctrl, tid, prxcache->tid_rxseq[tid]);
+
+ return _FAIL;
+ }
+ }
+
+ prxcache->tid_rxseq[tid] = seq_ctrl;
+
+
+
+ return _SUCCESS;
+}
+
+void process23a_pwrbit_data(struct rtw_adapter *padapter,
+ struct recv_frame *precv_frame);
+void process23a_pwrbit_data(struct rtw_adapter *padapter,
+ struct recv_frame *precv_frame)
+{
+#ifdef CONFIG_8723AU_AP_MODE
+ unsigned char pwrbit;
+ struct sk_buff *skb = precv_frame->pkt;
+ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
+ struct rx_pkt_attrib *pattrib = &precv_frame->attrib;
+ struct sta_priv *pstapriv = &padapter->stapriv;
+ struct sta_info *psta = NULL;
+
+ psta = rtw_get_stainfo23a(pstapriv, pattrib->src);
+
+ if (psta) {
+ pwrbit = ieee80211_has_pm(hdr->frame_control);
+
+ if (pwrbit) {
+ if (!(psta->state & WIFI_SLEEP_STATE))
+ stop_sta_xmit23a(padapter, psta);
+ } else {
+ if (psta->state & WIFI_SLEEP_STATE)
+ wakeup_sta_to_xmit23a(padapter, psta);
+ }
+ }
+
+#endif
+}
+
+void process_wmmps_data(struct rtw_adapter *padapter,
+ struct recv_frame *precv_frame);
+void process_wmmps_data(struct rtw_adapter *padapter,
+ struct recv_frame *precv_frame)
+{
+#ifdef CONFIG_8723AU_AP_MODE
+ struct rx_pkt_attrib *pattrib = &precv_frame->attrib;
+ struct sta_priv *pstapriv = &padapter->stapriv;
+ struct sta_info *psta = NULL;
+
+ psta = rtw_get_stainfo23a(pstapriv, pattrib->src);
+
+ if (!psta)
+ return;
+
+
+ if (!psta->qos_option)
+ return;
+
+ if (!(psta->qos_info & 0xf))
+ return;
+
+ if (psta->state & WIFI_SLEEP_STATE) {
+ u8 wmmps_ac = 0;
+
+ switch (pattrib->priority) {
+ case 1:
+ case 2:
+ wmmps_ac = psta->uapsd_bk & BIT(1);
+ break;
+ case 4:
+ case 5:
+ wmmps_ac = psta->uapsd_vi & BIT(1);
+ break;
+ case 6:
+ case 7:
+ wmmps_ac = psta->uapsd_vo & BIT(1);
+ break;
+ case 0:
+ case 3:
+ default:
+ wmmps_ac = psta->uapsd_be & BIT(1);
+ break;
+ }
+
+ if (wmmps_ac) {
+ if (psta->sleepq_ac_len > 0) {
+ /* process received triggered frame */
+ xmit_delivery_enabled_frames23a(padapter, psta);
+ } else {
+ /* issue one qos null frame with More data bit = 0 and the EOSP bit set (= 1) */
+ issue_qos_nulldata23a(padapter, psta->hwaddr,
+ (u16)pattrib->priority,
+ 0, 0);
+ }
+ }
+ }
+
+#endif
+}
+
+static void count_rx_stats(struct rtw_adapter *padapter,
+ struct recv_frame *prframe, struct sta_info *sta)
+{
+ int sz;
+ struct sta_info *psta = NULL;
+ struct stainfo_stats *pstats = NULL;
+ struct rx_pkt_attrib *pattrib = & prframe->attrib;
+ struct recv_priv *precvpriv = &padapter->recvpriv;
+
+ sz = prframe->pkt->len;
+ precvpriv->rx_bytes += sz;
+
+ padapter->mlmepriv.LinkDetectInfo.NumRxOkInPeriod++;
+
+ if ((!is_broadcast_ether_addr(pattrib->dst)) &&
+ (!is_multicast_ether_addr(pattrib->dst)))
+ padapter->mlmepriv.LinkDetectInfo.NumRxUnicastOkInPeriod++;
+
+ if (sta)
+ psta = sta;
+ else
+ psta = prframe->psta;
+
+ if (psta) {
+ pstats = &psta->sta_stats;
+
+ pstats->rx_data_pkts++;
+ pstats->rx_bytes += sz;
+ }
+}
+
+static int sta2sta_data_frame(struct rtw_adapter *adapter,
+ struct recv_frame *precv_frame,
+ struct sta_info**psta)
+{
+ struct sk_buff *skb = precv_frame->pkt;
+ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
+ int ret = _SUCCESS;
+ struct rx_pkt_attrib *pattrib = & precv_frame->attrib;
+ struct sta_priv *pstapriv = &adapter->stapriv;
+ struct mlme_priv *pmlmepriv = &adapter->mlmepriv;
+ u8 *mybssid = get_bssid(pmlmepriv);
+ u8 *myhwaddr = myid(&adapter->eeprompriv);
+ u8 *sta_addr = NULL;
+ int bmcast = is_multicast_ether_addr(pattrib->dst);
+
+
+
+ if (check_fwstate(pmlmepriv, WIFI_ADHOC_STATE) ||
+ check_fwstate(pmlmepriv, WIFI_ADHOC_MASTER_STATE)) {
+
+ /* filter packets that SA is myself or multicast or broadcast */
+ if (ether_addr_equal(myhwaddr, pattrib->src)) {
+ RT_TRACE(_module_rtl871x_recv_c_, _drv_err_,
+ "SA == myself\n");
+ ret = _FAIL;
+ goto exit;
+ }
+
+ if (!ether_addr_equal(myhwaddr, pattrib->dst) && !bmcast) {
+ ret = _FAIL;
+ goto exit;
+ }
+
+ if (ether_addr_equal(pattrib->bssid, "\x0\x0\x0\x0\x0\x0") ||
+ ether_addr_equal(mybssid, "\x0\x0\x0\x0\x0\x0") ||
+ !ether_addr_equal(pattrib->bssid, mybssid)) {
+ ret = _FAIL;
+ goto exit;
+ }
+
+ sta_addr = pattrib->src;
+ } else if (check_fwstate(pmlmepriv, WIFI_STATION_STATE)) {
+ /* For Station mode, sa and bssid should always be BSSID,
+ and DA is my mac-address */
+ if (!ether_addr_equal(pattrib->bssid, pattrib->src)) {
+ RT_TRACE(_module_rtl871x_recv_c_, _drv_err_,
+ "bssid != TA under STATION_MODE; drop pkt\n");
+ ret = _FAIL;
+ goto exit;
+ }
+
+ sta_addr = pattrib->bssid;
+
+ } else if (check_fwstate(pmlmepriv, WIFI_AP_STATE)) {
+ if (bmcast) {
+ /* For AP mode, if DA == MCAST, then BSSID should be also MCAST */
+ if (!is_multicast_ether_addr(pattrib->bssid)) {
+ ret = _FAIL;
+ goto exit;
+ }
+ } else { /* not mc-frame */
+ /* For AP mode, if DA is non-MCAST, then it must
+ be BSSID, and bssid == BSSID */
+ if (!ether_addr_equal(pattrib->bssid, pattrib->dst)) {
+ ret = _FAIL;
+ goto exit;
+ }
+
+ sta_addr = pattrib->src;
+ }
+ } else if (check_fwstate(pmlmepriv, WIFI_MP_STATE)) {
+ ether_addr_copy(pattrib->dst, hdr->addr1);
+ ether_addr_copy(pattrib->src, hdr->addr2);
+ ether_addr_copy(pattrib->bssid, hdr->addr3);
+ ether_addr_copy(pattrib->ra, pattrib->dst);
+ ether_addr_copy(pattrib->ta, pattrib->src);
+
+ sta_addr = mybssid;
+ } else {
+ ret = _FAIL;
+ }
+
+ if (bmcast)
+ *psta = rtw_get_bcmc_stainfo23a(adapter);
+ else
+ *psta = rtw_get_stainfo23a(pstapriv, sta_addr); /* get ap_info */
+
+ if (*psta == NULL) {
+ RT_TRACE(_module_rtl871x_recv_c_, _drv_err_,
+ "can't get psta under sta2sta_data_frame ; drop pkt\n");
+ ret = _FAIL;
+ goto exit;
+ }
+
+exit:
+
+ return ret;
+}
+
+int ap2sta_data_frame(struct rtw_adapter *adapter,
+ struct recv_frame *precv_frame,
+ struct sta_info **psta);
+int ap2sta_data_frame(struct rtw_adapter *adapter,
+ struct recv_frame *precv_frame,
+ struct sta_info **psta)
+{
+ struct sk_buff *skb = precv_frame->pkt;
+ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
+ struct rx_pkt_attrib *pattrib = & precv_frame->attrib;
+ int ret = _SUCCESS;
+ struct sta_priv *pstapriv = &adapter->stapriv;
+ struct mlme_priv *pmlmepriv = &adapter->mlmepriv;
+ u8 *mybssid = get_bssid(pmlmepriv);
+ u8 *myhwaddr = myid(&adapter->eeprompriv);
+ int bmcast = is_multicast_ether_addr(pattrib->dst);
+
+
+
+ if (check_fwstate(pmlmepriv, WIFI_STATION_STATE) &&
+ (check_fwstate(pmlmepriv, _FW_LINKED) ||
+ check_fwstate(pmlmepriv, _FW_UNDER_LINKING))) {
+
+ /* filter packets that SA is myself or multicast or broadcast */
+ if (ether_addr_equal(myhwaddr, pattrib->src)) {
+ RT_TRACE(_module_rtl871x_recv_c_, _drv_err_,
+ "SA == myself\n");
+ ret = _FAIL;
+ goto exit;
+ }
+
+ /* da should be for me */
+ if (!ether_addr_equal(myhwaddr, pattrib->dst) && !bmcast) {
+ RT_TRACE(_module_rtl871x_recv_c_, _drv_info_,
+ "ap2sta_data_frame: compare DA failed; DA=%pM\n",
+ pattrib->dst);
+ ret = _FAIL;
+ goto exit;
+ }
+
+ /* check BSSID */
+ if (ether_addr_equal(pattrib->bssid, "\x0\x0\x0\x0\x0\x0") ||
+ ether_addr_equal(mybssid, "\x0\x0\x0\x0\x0\x0") ||
+ !ether_addr_equal(pattrib->bssid, mybssid)) {
+ RT_TRACE(_module_rtl871x_recv_c_, _drv_info_,
+ "ap2sta_data_frame: compare BSSID failed; BSSID=%pM\n",
+ pattrib->bssid);
+ RT_TRACE(_module_rtl871x_recv_c_, _drv_info_,
+ "mybssid=%pM\n", mybssid);
+
+ if (!bmcast) {
+ DBG_8723A("issue_deauth23a to the nonassociated ap=%pM for the reason(7)\n",
+ pattrib->bssid);
+ issue_deauth23a(adapter, pattrib->bssid,
+ WLAN_REASON_CLASS3_FRAME_FROM_NONASSOC_STA);
+ }
+
+ ret = _FAIL;
+ goto exit;
+ }
+
+ if (bmcast)
+ *psta = rtw_get_bcmc_stainfo23a(adapter);
+ else
+ /* get ap_info */
+ *psta = rtw_get_stainfo23a(pstapriv, pattrib->bssid);
+
+ if (*psta == NULL) {
+ RT_TRACE(_module_rtl871x_recv_c_, _drv_err_,
+ "ap2sta: can't get psta under STATION_MODE; drop pkt\n");
+ ret = _FAIL;
+ goto exit;
+ }
+
+ if (ieee80211_is_nullfunc(hdr->frame_control)) {
+ /* No data, will not indicate to upper layer,
+ temporily count it here */
+ count_rx_stats(adapter, precv_frame, *psta);
+ ret = RTW_RX_HANDLED;
+ goto exit;
+ }
+
+ } else if (check_fwstate(pmlmepriv, WIFI_MP_STATE) &&
+ check_fwstate(pmlmepriv, _FW_LINKED)) {
+ ether_addr_copy(pattrib->dst, hdr->addr1);
+ ether_addr_copy(pattrib->src, hdr->addr2);
+ ether_addr_copy(pattrib->bssid, hdr->addr3);
+ ether_addr_copy(pattrib->ra, pattrib->dst);
+ ether_addr_copy(pattrib->ta, pattrib->src);
+
+ /* */
+ ether_addr_copy(pattrib->bssid, mybssid);
+
+ /* get sta_info */
+ *psta = rtw_get_stainfo23a(pstapriv, pattrib->bssid);
+ if (*psta == NULL) {
+ RT_TRACE(_module_rtl871x_recv_c_, _drv_err_,
+ "can't get psta under MP_MODE ; drop pkt\n");
+ ret = _FAIL;
+ goto exit;
+ }
+ } else if (check_fwstate(pmlmepriv, WIFI_AP_STATE)) {
+ /* Special case */
+ ret = RTW_RX_HANDLED;
+ goto exit;
+ } else {
+ if (ether_addr_equal(myhwaddr, pattrib->dst) && !bmcast) {
+ *psta = rtw_get_stainfo23a(pstapriv, pattrib->bssid);
+ if (*psta == NULL) {
+ DBG_8723A("issue_deauth23a to the ap=%pM for the reason(7)\n",
+ pattrib->bssid);
+
+ issue_deauth23a(adapter, pattrib->bssid,
+ WLAN_REASON_CLASS3_FRAME_FROM_NONASSOC_STA);
+ }
+ }
+
+ ret = _FAIL;
+ }
+
+exit:
+
+
+
+ return ret;
+}
+
+int sta2ap_data_frame(struct rtw_adapter *adapter,
+ struct recv_frame *precv_frame,
+ struct sta_info **psta);
+int sta2ap_data_frame(struct rtw_adapter *adapter,
+ struct recv_frame *precv_frame,
+ struct sta_info **psta)
+{
+ struct sk_buff *skb = precv_frame->pkt;
+ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
+ struct rx_pkt_attrib *pattrib = & precv_frame->attrib;
+ struct sta_priv *pstapriv = &adapter->stapriv;
+ struct mlme_priv *pmlmepriv = &adapter->mlmepriv;
+ unsigned char *mybssid = get_bssid(pmlmepriv);
+ int ret = _SUCCESS;
+
+
+
+ if (check_fwstate(pmlmepriv, WIFI_AP_STATE)) {
+ /* For AP mode, RA = BSSID, TX = STA(SRC_ADDR), A3 = DST_ADDR */
+ if (!ether_addr_equal(pattrib->bssid, mybssid)) {
+ ret = _FAIL;
+ goto exit;
+ }
+
+ *psta = rtw_get_stainfo23a(pstapriv, pattrib->src);
+ if (*psta == NULL) {
+ RT_TRACE(_module_rtl871x_recv_c_, _drv_err_,
+ "can't get psta under AP_MODE; drop pkt\n");
+ DBG_8723A("issue_deauth23a to sta=%pM for the reason(7)\n",
+ pattrib->src);
+
+ issue_deauth23a(adapter, pattrib->src,
+ WLAN_REASON_CLASS3_FRAME_FROM_NONASSOC_STA);
+
+ ret = RTW_RX_HANDLED;
+ goto exit;
+ }
+
+ process23a_pwrbit_data(adapter, precv_frame);
+
+ /* We only get here if it's a data frame, so no need to
+ * confirm data frame type first */
+ if (ieee80211_is_data_qos(hdr->frame_control))
+ process_wmmps_data(adapter, precv_frame);
+
+ if (ieee80211_is_nullfunc(hdr->frame_control)) {
+ /* No data, will not indicate to upper layer,
+ temporily count it here */
+ count_rx_stats(adapter, precv_frame, *psta);
+ ret = RTW_RX_HANDLED;
+ goto exit;
+ }
+ } else {
+ u8 *myhwaddr = myid(&adapter->eeprompriv);
+
+ if (!ether_addr_equal(pattrib->ra, myhwaddr)) {
+ ret = RTW_RX_HANDLED;
+ goto exit;
+ }
+ DBG_8723A("issue_deauth23a to sta=%pM for the reason(7)\n",
+ pattrib->src);
+ issue_deauth23a(adapter, pattrib->src,
+ WLAN_REASON_CLASS3_FRAME_FROM_NONASSOC_STA);
+ ret = RTW_RX_HANDLED;
+ goto exit;
+ }
+
+exit:
+
+
+
+ return ret;
+}
+
+static int validate_recv_ctrl_frame(struct rtw_adapter *padapter,
+ struct recv_frame *precv_frame)
+{
+#ifdef CONFIG_8723AU_AP_MODE
+ struct rx_pkt_attrib *pattrib = &precv_frame->attrib;
+ struct sta_priv *pstapriv = &padapter->stapriv;
+ struct sk_buff *skb = precv_frame->pkt;
+ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
+
+ if (!ieee80211_is_ctl(hdr->frame_control))
+ return _FAIL;
+
+ /* receive the frames that ra(a1) is my address */
+ if (!ether_addr_equal(hdr->addr1, myid(&padapter->eeprompriv)))
+ return _FAIL;
+
+ /* only handle ps-poll */
+ if (ieee80211_is_pspoll(hdr->frame_control)) {
+ struct ieee80211_pspoll *psp = (struct ieee80211_pspoll *)hdr;
+ u16 aid;
+ u8 wmmps_ac = 0;
+ struct sta_info *psta = NULL;
+
+ aid = le16_to_cpu(psp->aid) & 0x3fff;
+ psta = rtw_get_stainfo23a(pstapriv, hdr->addr2);
+
+ if (!psta || psta->aid != aid)
+ return _FAIL;
+
+ /* for rx pkt statistics */
+ psta->sta_stats.rx_ctrl_pkts++;
+
+ switch (pattrib->priority) {
+ case 1:
+ case 2:
+ wmmps_ac = psta->uapsd_bk & BIT(0);
+ break;
+ case 4:
+ case 5:
+ wmmps_ac = psta->uapsd_vi & BIT(0);
+ break;
+ case 6:
+ case 7:
+ wmmps_ac = psta->uapsd_vo & BIT(0);
+ break;
+ case 0:
+ case 3:
+ default:
+ wmmps_ac = psta->uapsd_be & BIT(0);
+ break;
+ }
+
+ if (wmmps_ac)
+ return _FAIL;
+
+ if (psta->state & WIFI_STA_ALIVE_CHK_STATE) {
+ DBG_8723A("%s alive check-rx ps-poll\n", __func__);
+ psta->expire_to = pstapriv->expire_to;
+ psta->state ^= WIFI_STA_ALIVE_CHK_STATE;
+ }
+
+ if ((psta->state & WIFI_SLEEP_STATE) &&
+ (pstapriv->sta_dz_bitmap & CHKBIT(psta->aid))) {
+ struct list_head *xmitframe_plist, *xmitframe_phead;
+ struct xmit_frame *pxmitframe;
+ struct xmit_priv *pxmitpriv = &padapter->xmitpriv;
+
+ spin_lock_bh(&pxmitpriv->lock);
+
+ xmitframe_phead = get_list_head(&psta->sleep_q);
+ xmitframe_plist = xmitframe_phead->next;
+
+ if (!list_empty(xmitframe_phead)) {
+ pxmitframe = container_of(xmitframe_plist,
+ struct xmit_frame,
+ list);
+
+ xmitframe_plist = xmitframe_plist->next;
+
+ list_del_init(&pxmitframe->list);
+
+ psta->sleepq_len--;
+
+ if (psta->sleepq_len>0)
+ pxmitframe->attrib.mdata = 1;
+ else
+ pxmitframe->attrib.mdata = 0;
+
+ pxmitframe->attrib.triggered = 1;
+
+ /* DBG_8723A("handling ps-poll, q_len =%d, tim =%x\n", psta->sleepq_len, pstapriv->tim_bitmap); */
+
+ rtl8723au_hal_xmitframe_enqueue(padapter,
+ pxmitframe);
+
+ if (psta->sleepq_len == 0) {
+ pstapriv->tim_bitmap &= ~CHKBIT(psta->aid);
+
+ /* DBG_8723A("after handling ps-poll, tim =%x\n", pstapriv->tim_bitmap); */
+
+ /* update BCN for TIM IE */
+ /* update_BCNTIM(padapter); */
+ update_beacon23a(padapter, WLAN_EID_TIM,
+ NULL, false);
+ }
+
+ /* spin_unlock_bh(&psta->sleep_q.lock); */
+ spin_unlock_bh(&pxmitpriv->lock);
+
+ } else {
+ /* spin_unlock_bh(&psta->sleep_q.lock); */
+ spin_unlock_bh(&pxmitpriv->lock);
+
+ /* DBG_8723A("no buffered packets to xmit\n"); */
+ if (pstapriv->tim_bitmap & CHKBIT(psta->aid)) {
+ if (psta->sleepq_len == 0) {
+ DBG_8723A("no buffered packets "
+ "to xmit\n");
+
+ /* issue nulldata with More data bit = 0 to indicate we have no buffered packets */
+ issue_nulldata23a(padapter,
+ psta->hwaddr,
+ 0, 0, 0);
+ } else {
+ DBG_8723A("error!psta->sleepq"
+ "_len =%d\n",
+ psta->sleepq_len);
+ psta->sleepq_len = 0;
+ }
+
+ pstapriv->tim_bitmap &= ~CHKBIT(psta->aid);
+
+ /* update BCN for TIM IE */
+ /* update_BCNTIM(padapter); */
+ update_beacon23a(padapter, WLAN_EID_TIM,
+ NULL, false);
+ }
+ }
+ }
+ }
+
+#endif
+ return _FAIL;
+}
+
+struct recv_frame *recvframe_chk_defrag23a(struct rtw_adapter *padapter,
+ struct recv_frame *precv_frame);
+static int validate_recv_mgnt_frame(struct rtw_adapter *padapter,
+ struct recv_frame *precv_frame)
+{
+ struct sta_info *psta;
+ struct sk_buff *skb;
+ struct ieee80211_hdr *hdr;
+ /* struct mlme_priv *pmlmepriv = &adapter->mlmepriv; */
+
+ RT_TRACE(_module_rtl871x_recv_c_, _drv_info_,
+ "+validate_recv_mgnt_frame\n");
+
+ precv_frame = recvframe_chk_defrag23a(padapter, precv_frame);
+ if (precv_frame == NULL) {
+ RT_TRACE(_module_rtl871x_recv_c_, _drv_notice_,
+ "%s: fragment packet\n", __func__);
+ return _SUCCESS;
+ }
+
+ skb = precv_frame->pkt;
+ hdr = (struct ieee80211_hdr *) skb->data;
+
+ /* for rx pkt statistics */
+ psta = rtw_get_stainfo23a(&padapter->stapriv, hdr->addr2);
+ if (psta) {
+ psta->sta_stats.rx_mgnt_pkts++;
+
+ if (ieee80211_is_beacon(hdr->frame_control))
+ psta->sta_stats.rx_beacon_pkts++;
+ else if (ieee80211_is_probe_req(hdr->frame_control))
+ psta->sta_stats.rx_probereq_pkts++;
+ else if (ieee80211_is_probe_resp(hdr->frame_control)) {
+ if (ether_addr_equal(padapter->eeprompriv.mac_addr,
+ hdr->addr1))
+ psta->sta_stats.rx_probersp_pkts++;
+ else if (is_broadcast_ether_addr(hdr->addr1) ||
+ is_multicast_ether_addr(hdr->addr1))
+ psta->sta_stats.rx_probersp_bm_pkts++;
+ else
+ psta->sta_stats.rx_probersp_uo_pkts++;
+ }
+ }
+
+ mgt_dispatcher23a(padapter, precv_frame);
+
+ return _SUCCESS;
+}
+
+static int validate_recv_data_frame(struct rtw_adapter *adapter,
+ struct recv_frame *precv_frame)
+{
+ u8 bretry;
+ u8 *psa, *pda;
+ struct sta_info *psta = NULL;
+ struct rx_pkt_attrib *pattrib = & precv_frame->attrib;
+ struct security_priv *psecuritypriv = &adapter->securitypriv;
+ int ret = _SUCCESS;
+ struct sk_buff *skb = precv_frame->pkt;
+ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
+
+
+
+ bretry = ieee80211_has_retry(hdr->frame_control);
+ pda = ieee80211_get_DA(hdr);
+ psa = ieee80211_get_SA(hdr);
+
+ ether_addr_copy(pattrib->dst, pda);
+ ether_addr_copy(pattrib->src, psa);
+
+ switch (hdr->frame_control &
+ cpu_to_le16(IEEE80211_FCTL_TODS | IEEE80211_FCTL_FROMDS)) {
+ case cpu_to_le16(0):
+ ether_addr_copy(pattrib->bssid, hdr->addr3);
+ ether_addr_copy(pattrib->ra, pda);
+ ether_addr_copy(pattrib->ta, psa);
+ ret = sta2sta_data_frame(adapter, precv_frame, &psta);
+ break;
+
+ case cpu_to_le16(IEEE80211_FCTL_FROMDS):
+ ether_addr_copy(pattrib->bssid, hdr->addr2);
+ ether_addr_copy(pattrib->ra, pda);
+ ether_addr_copy(pattrib->ta, hdr->addr2);
+ ret = ap2sta_data_frame(adapter, precv_frame, &psta);
+ break;
+
+ case cpu_to_le16(IEEE80211_FCTL_TODS):
+ ether_addr_copy(pattrib->bssid, hdr->addr1);
+ ether_addr_copy(pattrib->ra, hdr->addr1);
+ ether_addr_copy(pattrib->ta, psa);
+ ret = sta2ap_data_frame(adapter, precv_frame, &psta);
+ break;
+
+ case cpu_to_le16(IEEE80211_FCTL_TODS | IEEE80211_FCTL_FROMDS):
+ /*
+ * There is no BSSID in this case, but the driver has been
+ * using addr1 so far, so keep it for now.
+ */
+ ether_addr_copy(pattrib->bssid, hdr->addr1);
+ ether_addr_copy(pattrib->ra, hdr->addr1);
+ ether_addr_copy(pattrib->ta, hdr->addr2);
+ ret = _FAIL;
+ RT_TRACE(_module_rtl871x_recv_c_, _drv_err_, "case 3\n");
+ break;
+ }
+
+ if ((ret == _FAIL) || (ret == RTW_RX_HANDLED))
+ goto exit;
+
+ if (!psta) {
+ RT_TRACE(_module_rtl871x_recv_c_, _drv_err_,
+ "after to_fr_ds_chk; psta == NULL\n");
+ ret = _FAIL;
+ goto exit;
+ }
+
+ /* psta->rssi = prxcmd->rssi; */
+ /* psta->signal_quality = prxcmd->sq; */
+ precv_frame->psta = psta;
+
+ pattrib->hdrlen = sizeof(struct ieee80211_hdr_3addr);
+ if (ieee80211_has_a4(hdr->frame_control))
+ pattrib->hdrlen += ETH_ALEN;
+
+ /* parsing QC field */
+ if (pattrib->qos == 1) {
+ __le16 *qptr = (__le16 *)ieee80211_get_qos_ctl(hdr);
+ u16 qos_ctrl = le16_to_cpu(*qptr);
+
+ pattrib->priority = qos_ctrl & IEEE80211_QOS_CTL_TID_MASK;
+ pattrib->ack_policy = (qos_ctrl >> 5) & 3;
+ pattrib->amsdu =
+ (qos_ctrl & IEEE80211_QOS_CTL_A_MSDU_PRESENT) >> 7;
+ pattrib->hdrlen += IEEE80211_QOS_CTL_LEN;
+
+ if (pattrib->priority != 0 && pattrib->priority != 3) {
+ adapter->recvpriv.bIsAnyNonBEPkts = true;
+ }
+ } else {
+ pattrib->priority = 0;
+ pattrib->ack_policy = 0;
+ pattrib->amsdu = 0;
+ }
+
+ if (pattrib->order) { /* HT-CTRL 11n */
+ pattrib->hdrlen += 4;
+ }
+
+ precv_frame->preorder_ctrl = &psta->recvreorder_ctrl[pattrib->priority];
+
+ /* decache, drop duplicate recv packets */
+ if (recv_decache(precv_frame, bretry, &psta->sta_recvpriv.rxcache) ==
+ _FAIL) {
+ RT_TRACE(_module_rtl871x_recv_c_, _drv_err_,
+ "decache : drop pkt\n");
+ ret = _FAIL;
+ goto exit;
+ }
+
+ if (pattrib->privacy) {
+ RT_TRACE(_module_rtl871x_recv_c_, _drv_info_,
+ "validate_recv_data_frame:pattrib->privacy =%x\n",
+ pattrib->privacy);
+ RT_TRACE(_module_rtl871x_recv_c_, _drv_info_,
+ "^^^^^^^^^^^is_multicast_ether_addr(pattrib->ra(0x%02x)) =%d^^^^^^^^^^^^^^^6\n",
+ pattrib->ra[0],
+ is_multicast_ether_addr(pattrib->ra));
+
+ GET_ENCRY_ALGO(psecuritypriv, psta, pattrib->encrypt,
+ is_multicast_ether_addr(pattrib->ra));
+
+ RT_TRACE(_module_rtl871x_recv_c_, _drv_info_,
+ "pattrib->encrypt =%d\n", pattrib->encrypt);
+
+ switch (pattrib->encrypt) {
+ case WLAN_CIPHER_SUITE_WEP40:
+ case WLAN_CIPHER_SUITE_WEP104:
+ pattrib->iv_len = IEEE80211_WEP_IV_LEN;
+ pattrib->icv_len = IEEE80211_WEP_ICV_LEN;
+ break;
+ case WLAN_CIPHER_SUITE_TKIP:
+ pattrib->iv_len = IEEE80211_TKIP_IV_LEN;
+ pattrib->icv_len = IEEE80211_TKIP_ICV_LEN;
+ break;
+ case WLAN_CIPHER_SUITE_CCMP:
+ pattrib->iv_len = IEEE80211_CCMP_HDR_LEN;
+ pattrib->icv_len = IEEE80211_CCMP_MIC_LEN;
+ break;
+ default:
+ pattrib->iv_len = 0;
+ pattrib->icv_len = 0;
+ break;
+ }
+ } else {
+ pattrib->encrypt = 0;
+ pattrib->iv_len = 0;
+ pattrib->icv_len = 0;
+ }
+
+exit:
+
+
+
+ return ret;
+}
+
+static void dump_rx_pkt(struct sk_buff *skb, u16 type, int level)
+{
+ int i;
+ u8 *ptr;
+
+ if ((level == 1) ||
+ ((level == 2) && (type == IEEE80211_FTYPE_MGMT)) ||
+ ((level == 3) && (type == IEEE80211_FTYPE_DATA))) {
+
+ ptr = skb->data;
+
+ DBG_8723A("#############################\n");
+
+ for (i = 0; i < 64; i = i + 8)
+ DBG_8723A("%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X:\n",
+ *(ptr + i), *(ptr + i + 1), *(ptr + i + 2),
+ *(ptr + i + 3), *(ptr + i + 4),
+ *(ptr + i + 5), *(ptr + i + 6),
+ *(ptr + i + 7));
+ DBG_8723A("#############################\n");
+ }
+}
+
+static int validate_recv_frame(struct rtw_adapter *adapter,
+ struct recv_frame *precv_frame)
+{
+ /* shall check frame subtype, to / from ds, da, bssid */
+
+ /* then call check if rx seq/frag. duplicated. */
+ u8 type;
+ u8 subtype;
+ int retval = _SUCCESS;
+ struct rx_pkt_attrib *pattrib = & precv_frame->attrib;
+ struct sk_buff *skb = precv_frame->pkt;
+ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
+ u8 ver;
+ u8 bDumpRxPkt;
+ u16 seq_ctrl, fctl;
+
+ fctl = le16_to_cpu(hdr->frame_control);
+ ver = fctl & IEEE80211_FCTL_VERS;
+ type = fctl & IEEE80211_FCTL_FTYPE;
+ subtype = fctl & IEEE80211_FCTL_STYPE;
+
+ /* add version chk */
+ if (ver != 0) {
+ RT_TRACE(_module_rtl871x_recv_c_, _drv_err_,
+ "validate_recv_data_frame fail! (ver!= 0)\n");
+ retval = _FAIL;
+ goto exit;
+ }
+
+ seq_ctrl = le16_to_cpu(hdr->seq_ctrl);
+ pattrib->frag_num = seq_ctrl & IEEE80211_SCTL_FRAG;
+ pattrib->seq_num = seq_ctrl >> 4;
+
+ pattrib->pw_save = ieee80211_has_pm(hdr->frame_control);
+ pattrib->mfrag = ieee80211_has_morefrags(hdr->frame_control);
+ pattrib->mdata = ieee80211_has_moredata(hdr->frame_control);
+ pattrib->privacy = ieee80211_has_protected(hdr->frame_control);
+ pattrib->order = ieee80211_has_order(hdr->frame_control);
+
+ GetHalDefVar8192CUsb(adapter, HAL_DEF_DBG_DUMP_RXPKT, &bDumpRxPkt);
+
+ if (unlikely(bDumpRxPkt == 1))
+ dump_rx_pkt(skb, type, bDumpRxPkt);
+
+ switch (type) {
+ case IEEE80211_FTYPE_MGMT:
+ retval = validate_recv_mgnt_frame(adapter, precv_frame);
+ if (retval == _FAIL) {
+ RT_TRACE(_module_rtl871x_recv_c_, _drv_err_,
+ "validate_recv_mgnt_frame fail\n");
+ }
+ retval = _FAIL; /* only data frame return _SUCCESS */
+ break;
+ case IEEE80211_FTYPE_CTL:
+ retval = validate_recv_ctrl_frame(adapter, precv_frame);
+ if (retval == _FAIL) {
+ RT_TRACE(_module_rtl871x_recv_c_, _drv_err_,
+ "validate_recv_ctrl_frame fail\n");
+ }
+ retval = _FAIL; /* only data frame return _SUCCESS */
+ break;
+ case IEEE80211_FTYPE_DATA:
+ pattrib->qos = (subtype & IEEE80211_STYPE_QOS_DATA) ? 1 : 0;
+ retval = validate_recv_data_frame(adapter, precv_frame);
+ if (retval == _FAIL) {
+ struct recv_priv *precvpriv = &adapter->recvpriv;
+
+ precvpriv->rx_drop++;
+ }
+ break;
+ default:
+ RT_TRACE(_module_rtl871x_recv_c_, _drv_err_,
+ "validate_recv_data_frame fail! type = 0x%x\n", type);
+ retval = _FAIL;
+ break;
+ }
+
+exit:
+ return retval;
+}
+
+/* remove the wlanhdr and add the eth_hdr */
+
+static int wlanhdr_to_ethhdr (struct recv_frame *precvframe)
+{
+ u16 eth_type, len, hdrlen;
+ u8 bsnaphdr;
+ u8 *psnap;
+ struct rtw_adapter *adapter = precvframe->adapter;
+ struct mlme_priv *pmlmepriv = &adapter->mlmepriv;
+
+ struct sk_buff *skb = precvframe->pkt;
+ u8 *ptr;
+ struct rx_pkt_attrib *pattrib = &precvframe->attrib;
+
+
+
+ ptr = skb->data;
+ hdrlen = pattrib->hdrlen;
+ psnap = ptr + hdrlen;
+ eth_type = (psnap[6] << 8) | psnap[7];
+ /* convert hdr + possible LLC headers into Ethernet header */
+ /* eth_type = (psnap_type[0] << 8) | psnap_type[1]; */
+ if ((ether_addr_equal(psnap, rfc1042_header) &&
+ eth_type != ETH_P_AARP && eth_type != ETH_P_IPX) ||
+ ether_addr_equal(psnap, bridge_tunnel_header)) {
+ /* remove RFC1042 or Bridge-Tunnel encapsulation
+ and replace EtherType */
+ bsnaphdr = true;
+ hdrlen += SNAP_SIZE;
+ } else {
+ /* Leave Ethernet header part of hdr and full payload */
+ bsnaphdr = false;
+ eth_type = (psnap[0] << 8) | psnap[1];
+ }
+
+ len = skb->len - hdrlen;
+
+ RT_TRACE(_module_rtl871x_recv_c_, _drv_info_,
+ "=== pattrib->hdrlen: %x, pattrib->iv_len:%x ===\n",
+ pattrib->hdrlen, pattrib->iv_len);
+
+ pattrib->eth_type = eth_type;
+ if (check_fwstate(pmlmepriv, WIFI_MP_STATE)) {
+ ptr += hdrlen;
+ *ptr = 0x87;
+ *(ptr + 1) = 0x12;
+
+ eth_type = 0x8712;
+ /* append rx status for mp test packets */
+
+ ptr = skb_pull(skb, (hdrlen - sizeof(struct ethhdr) + 2) - 24);
+ memcpy(ptr, skb->head, 24);
+ ptr += 24;
+ } else {
+ ptr = skb_pull(skb, (hdrlen - sizeof(struct ethhdr) +
+ (bsnaphdr ? 2:0)));
+ }
+
+ ether_addr_copy(ptr, pattrib->dst);
+ ether_addr_copy(ptr + ETH_ALEN, pattrib->src);
+
+ if (!bsnaphdr) {
+ len = htons(len);
+ memcpy(ptr + 12, &len, 2);
+ }
+
+
+ return _SUCCESS;
+}
+
+/* perform defrag */
+struct recv_frame *recvframe_defrag(struct rtw_adapter *adapter,
+ struct rtw_queue *defrag_q);
+struct recv_frame *recvframe_defrag(struct rtw_adapter *adapter,
+ struct rtw_queue *defrag_q)
+{
+ struct list_head *plist, *phead, *ptmp;
+ u8 *data, wlanhdr_offset;
+ u8 curfragnum;
+ struct recv_frame *pnfhdr;
+ struct recv_frame *prframe, *pnextrframe;
+ struct rtw_queue *pfree_recv_queue;
+ struct sk_buff *skb;
+
+
+
+ curfragnum = 0;
+ pfree_recv_queue = &adapter->recvpriv.free_recv_queue;
+
+ phead = get_list_head(defrag_q);
+ plist = phead->next;
+ prframe = container_of(plist, struct recv_frame, list);
+ list_del_init(&prframe->list);
+ skb = prframe->pkt;
+
+ if (curfragnum != prframe->attrib.frag_num) {
+ /* the first fragment number must be 0 */
+ /* free the whole queue */
+ rtw_free_recvframe23a(prframe);
+ rtw_free_recvframe23a_queue(defrag_q);
+
+ return NULL;
+ }
+
+ curfragnum++;
+
+ phead = get_list_head(defrag_q);
+
+ data = prframe->pkt->data;
+
+ list_for_each_safe(plist, ptmp, phead) {
+ pnfhdr = container_of(plist, struct recv_frame, list);
+ pnextrframe = (struct recv_frame *)pnfhdr;
+ /* check the fragment sequence (2nd ~n fragment frame) */
+
+ if (curfragnum != pnfhdr->attrib.frag_num) {
+ /* the fragment number must be increasing
+ (after decache) */
+ /* release the defrag_q & prframe */
+ rtw_free_recvframe23a(prframe);
+ rtw_free_recvframe23a_queue(defrag_q);
+ return NULL;
+ }
+
+ curfragnum++;
+
+ /* copy the 2nd~n fragment frame's payload to the
+ first fragment */
+ /* get the 2nd~last fragment frame's payload */
+
+ wlanhdr_offset = pnfhdr->attrib.hdrlen + pnfhdr->attrib.iv_len;
+
+ skb_pull(pnfhdr->pkt, wlanhdr_offset);
+
+ /* append to first fragment frame's tail
+ (if privacy frame, pull the ICV) */
+
+ skb_trim(skb, skb->len - prframe->attrib.icv_len);
+
+ memcpy(skb_tail_pointer(skb), pnfhdr->pkt->data,
+ pnfhdr->pkt->len);
+
+ skb_put(skb, pnfhdr->pkt->len);
+
+ prframe->attrib.icv_len = pnfhdr->attrib.icv_len;
+ }
+
+ /* free the defrag_q queue and return the prframe */
+ rtw_free_recvframe23a_queue(defrag_q);
+
+ RT_TRACE(_module_rtl871x_recv_c_, _drv_info_,
+ "Performance defrag!!!!!\n");
+
+
+
+ return prframe;
+}
+
+/* check if need to defrag, if needed queue the frame to defrag_q */
+struct recv_frame *recvframe_chk_defrag23a(struct rtw_adapter *padapter,
+ struct recv_frame *precv_frame)
+{
+ u8 ismfrag;
+ u8 fragnum;
+ u8 *psta_addr;
+ struct recv_frame *pfhdr;
+ struct sta_info *psta;
+ struct sta_priv *pstapriv;
+ struct list_head *phead;
+ struct recv_frame *prtnframe = NULL;
+ struct rtw_queue *pfree_recv_queue, *pdefrag_q;
+
+
+
+ pstapriv = &padapter->stapriv;
+
+ pfhdr = precv_frame;
+
+ pfree_recv_queue = &padapter->recvpriv.free_recv_queue;
+
+ /* need to define struct of wlan header frame ctrl */
+ ismfrag = pfhdr->attrib.mfrag;
+ fragnum = pfhdr->attrib.frag_num;
+
+ psta_addr = pfhdr->attrib.ta;
+ psta = rtw_get_stainfo23a(pstapriv, psta_addr);
+ if (!psta) {
+ struct ieee80211_hdr *hdr =
+ (struct ieee80211_hdr *) pfhdr->pkt->data;
+ if (!ieee80211_is_data(hdr->frame_control)) {
+ psta = rtw_get_bcmc_stainfo23a(padapter);
+ pdefrag_q = &psta->sta_recvpriv.defrag_q;
+ } else
+ pdefrag_q = NULL;
+ } else
+ pdefrag_q = &psta->sta_recvpriv.defrag_q;
+
+ if ((ismfrag == 0) && (fragnum == 0)) {
+ prtnframe = precv_frame;/* isn't a fragment frame */
+ }
+
+ if (ismfrag == 1) {
+ /* 0~(n-1) fragment frame */
+ /* enqueue to defraf_g */
+ if (pdefrag_q != NULL) {
+ if (fragnum == 0) {
+ /* the first fragment */
+ if (!list_empty(&pdefrag_q->queue)) {
+ /* free current defrag_q */
+ rtw_free_recvframe23a_queue(pdefrag_q);
+ }
+ }
+
+ /* Then enqueue the 0~(n-1) fragment into the
+ defrag_q */
+
+ /* spin_lock(&pdefrag_q->lock); */
+ phead = get_list_head(pdefrag_q);
+ list_add_tail(&pfhdr->list, phead);
+ /* spin_unlock(&pdefrag_q->lock); */
+
+ RT_TRACE(_module_rtl871x_recv_c_, _drv_info_,
+ "Enqueuq: ismfrag = %d, fragnum = %d\n",
+ ismfrag, fragnum);
+
+ prtnframe = NULL;
+
+ } else {
+ /* can't find this ta's defrag_queue,
+ so free this recv_frame */
+ rtw_free_recvframe23a(precv_frame);
+ prtnframe = NULL;
+ RT_TRACE(_module_rtl871x_recv_c_, _drv_err_,
+ "Free because pdefrag_q == NULL: ismfrag = %d, fragnum = %d\n",
+ ismfrag, fragnum);
+ }
+ }
+
+ if ((ismfrag == 0) && (fragnum != 0)) {
+ /* the last fragment frame */
+ /* enqueue the last fragment */
+ if (pdefrag_q != NULL) {
+ /* spin_lock(&pdefrag_q->lock); */
+ phead = get_list_head(pdefrag_q);
+ list_add_tail(&pfhdr->list, phead);
+ /* spin_unlock(&pdefrag_q->lock); */
+
+ /* call recvframe_defrag to defrag */
+ RT_TRACE(_module_rtl871x_recv_c_, _drv_info_,
+ "defrag: ismfrag = %d, fragnum = %d\n",
+ ismfrag, fragnum);
+ precv_frame = recvframe_defrag(padapter, pdefrag_q);
+ prtnframe = precv_frame;
+ } else {
+ /* can't find this ta's defrag_queue,
+ so free this recv_frame */
+ rtw_free_recvframe23a(precv_frame);
+ prtnframe = NULL;
+ RT_TRACE(_module_rtl871x_recv_c_, _drv_err_,
+ "Free because pdefrag_q == NULL: ismfrag = %d, fragnum = %d\n",
+ ismfrag, fragnum);
+ }
+
+ }
+
+ if ((prtnframe != NULL) && (prtnframe->attrib.privacy)) {
+ /* after defrag we must check tkip mic code */
+ if (recvframe_chkmic(padapter, prtnframe) == _FAIL) {
+ RT_TRACE(_module_rtl871x_recv_c_, _drv_err_,
+ "recvframe_chkmic(padapter, prtnframe) ==_FAIL\n");
+ rtw_free_recvframe23a(prtnframe);
+ prtnframe = NULL;
+ }
+ }
+
+
+
+ return prtnframe;
+}
+
+int amsdu_to_msdu(struct rtw_adapter *padapter, struct recv_frame *prframe);
+int amsdu_to_msdu(struct rtw_adapter *padapter, struct recv_frame *prframe)
+{
+ struct rx_pkt_attrib *pattrib;
+ struct sk_buff *skb, *sub_skb;
+ struct sk_buff_head skb_list;
+
+ pattrib = &prframe->attrib;
+
+ skb = prframe->pkt;
+ skb_pull(skb, prframe->attrib.hdrlen);
+ __skb_queue_head_init(&skb_list);
+
+ ieee80211_amsdu_to_8023s(skb, &skb_list, NULL, 0, 0, false);
+
+ while (!skb_queue_empty(&skb_list)) {
+ sub_skb = __skb_dequeue(&skb_list);
+
+ sub_skb->protocol = eth_type_trans(sub_skb, padapter->pnetdev);
+ sub_skb->dev = padapter->pnetdev;
+
+ sub_skb->ip_summed = CHECKSUM_NONE;
+
+ netif_rx(sub_skb);
+ }
+
+ prframe->pkt = NULL;
+ rtw_free_recvframe23a(prframe);
+ return _SUCCESS;
+}
+
+int check_indicate_seq(struct recv_reorder_ctrl *preorder_ctrl, u16 seq_num);
+int check_indicate_seq(struct recv_reorder_ctrl *preorder_ctrl, u16 seq_num)
+{
+ u8 wsize = preorder_ctrl->wsize_b;
+ u16 wend = (preorder_ctrl->indicate_seq + wsize -1) & 0xFFF;
+
+ /* Rx Reorder initialize condition. */
+ if (preorder_ctrl->indicate_seq == 0xFFFF)
+ preorder_ctrl->indicate_seq = seq_num;
+
+ /* Drop out the packet which SeqNum is smaller than WinStart */
+ if (SN_LESS(seq_num, preorder_ctrl->indicate_seq))
+ return false;
+
+ /* */
+ /* Sliding window manipulation. Conditions includes: */
+ /* 1. Incoming SeqNum is equal to WinStart =>Window shift 1 */
+ /* 2. Incoming SeqNum is larger than the WinEnd => Window shift N */
+ /* */
+ if (SN_EQUAL(seq_num, preorder_ctrl->indicate_seq)) {
+ preorder_ctrl->indicate_seq =
+ (preorder_ctrl->indicate_seq + 1) & 0xFFF;
+ } else if (SN_LESS(wend, seq_num)) {
+ /* boundary situation, when seq_num cross 0xFFF */
+ if (seq_num >= (wsize - 1))
+ preorder_ctrl->indicate_seq = seq_num + 1 -wsize;
+ else
+ preorder_ctrl->indicate_seq = 0xFFF - (wsize - (seq_num + 1)) + 1;
+ }
+ return true;
+}
+
+static int enqueue_reorder_recvframe23a(struct recv_reorder_ctrl *preorder_ctrl,
+ struct recv_frame *prframe)
+{
+ struct rx_pkt_attrib *pattrib = &prframe->attrib;
+ struct rtw_queue *ppending_recvframe_queue;
+ struct list_head *phead, *plist, *ptmp;
+ struct recv_frame *hdr;
+ struct rx_pkt_attrib *pnextattrib;
+
+ ppending_recvframe_queue = &preorder_ctrl->pending_recvframe_queue;
+ /* DbgPrint("+enqueue_reorder_recvframe23a()\n"); */
+
+ /* spin_lock_irqsave(&ppending_recvframe_queue->lock); */
+ /* spin_lock_ex(&ppending_recvframe_queue->lock); */
+
+ phead = get_list_head(ppending_recvframe_queue);
+
+ list_for_each_safe(plist, ptmp, phead) {
+ hdr = container_of(plist, struct recv_frame, list);
+ pnextattrib = &hdr->attrib;
+
+ if (SN_LESS(pnextattrib->seq_num, pattrib->seq_num)) {
+ continue;
+ } else if (SN_EQUAL(pnextattrib->seq_num, pattrib->seq_num)) {
+ /* Duplicate entry is found!! Do not insert current entry. */
+
+ /* spin_unlock_irqrestore(&ppending_recvframe_queue->lock); */
+ return false;
+ } else {
+ break;
+ }
+
+ /* DbgPrint("enqueue_reorder_recvframe23a():while\n"); */
+ }
+
+ /* spin_lock_irqsave(&ppending_recvframe_queue->lock); */
+ /* spin_lock_ex(&ppending_recvframe_queue->lock); */
+
+ list_del_init(&prframe->list);
+
+ list_add_tail(&prframe->list, plist);
+
+ /* spin_unlock_ex(&ppending_recvframe_queue->lock); */
+ /* spin_unlock_irqrestore(&ppending_recvframe_queue->lock); */
+
+ return true;
+}
+
+int recv_indicatepkts_in_order(struct rtw_adapter *padapter,
+ struct recv_reorder_ctrl *preorder_ctrl,
+ int bforced);
+int recv_indicatepkts_in_order(struct rtw_adapter *padapter,
+ struct recv_reorder_ctrl *preorder_ctrl,
+ int bforced)
+{
+ /* u8 bcancelled; */
+ struct list_head *phead, *plist;
+ struct recv_frame *prframe;
+ struct rx_pkt_attrib *pattrib;
+ /* u8 index = 0; */
+ int bPktInBuf = false;
+ struct recv_priv *precvpriv;
+ struct rtw_queue *ppending_recvframe_queue;
+
+ precvpriv = &padapter->recvpriv;
+ ppending_recvframe_queue = &preorder_ctrl->pending_recvframe_queue;
+ /* DbgPrint("+recv_indicatepkts_in_order\n"); */
+
+ /* spin_lock_irqsave(&ppending_recvframe_queue->lock); */
+ /* spin_lock_ex(&ppending_recvframe_queue->lock); */
+
+ phead = get_list_head(ppending_recvframe_queue);
+ plist = phead->next;
+
+ /* Handling some condition for forced indicate case. */
+ if (bforced) {
+ if (list_empty(phead)) {
+ /* spin_unlock_irqrestore(&ppending_recvframe_queue->lock); */
+ /* spin_unlock_ex(&ppending_recvframe_queue->lock); */
+ return true;
+ }
+
+ prframe = container_of(plist, struct recv_frame, list);
+ pattrib = &prframe->attrib;
+ preorder_ctrl->indicate_seq = pattrib->seq_num;
+ }
+
+ /* Prepare indication list and indication. */
+ /* Check if there is any packet need indicate. */
+ while (!list_empty(phead)) {
+
+ prframe = container_of(plist, struct recv_frame, list);
+ pattrib = &prframe->attrib;
+
+ if (!SN_LESS(preorder_ctrl->indicate_seq, pattrib->seq_num)) {
+ RT_TRACE(_module_rtl871x_recv_c_, _drv_notice_,
+ "recv_indicatepkts_in_order: indicate =%d seq =%d amsdu =%d\n",
+ preorder_ctrl->indicate_seq,
+ pattrib->seq_num, pattrib->amsdu);
+
+ plist = plist->next;
+ list_del_init(&prframe->list);
+
+ if (SN_EQUAL(preorder_ctrl->indicate_seq,
+ pattrib->seq_num)) {
+ preorder_ctrl->indicate_seq =
+ (preorder_ctrl->indicate_seq + 1)&0xFFF;
+ }
+
+ if (!pattrib->amsdu) {
+ if ((padapter->bDriverStopped == false) &&
+ (padapter->bSurpriseRemoved == false)) {
+ rtw_recv_indicatepkt23a(padapter, prframe);
+ }
+ } else {
+ if (amsdu_to_msdu(padapter, prframe) !=
+ _SUCCESS)
+ rtw_free_recvframe23a(prframe);
+ }
+
+ /* Update local variables. */
+ bPktInBuf = false;
+
+ } else {
+ bPktInBuf = true;
+ break;
+ }
+
+ /* DbgPrint("recv_indicatepkts_in_order():while\n"); */
+ }
+
+ /* spin_unlock_ex(&ppending_recvframe_queue->lock); */
+ /* spin_unlock_irqrestore(&ppending_recvframe_queue->lock); */
+
+ return bPktInBuf;
+}
+
+int recv_indicatepkt_reorder(struct rtw_adapter *padapter,
+ struct recv_frame *prframe);
+int recv_indicatepkt_reorder(struct rtw_adapter *padapter,
+ struct recv_frame *prframe)
+{
+ int retval = _SUCCESS;
+ struct rx_pkt_attrib *pattrib;
+ struct recv_reorder_ctrl *preorder_ctrl;
+ struct rtw_queue *ppending_recvframe_queue;
+
+ pattrib = &prframe->attrib;
+ preorder_ctrl = prframe->preorder_ctrl;
+ ppending_recvframe_queue = &preorder_ctrl->pending_recvframe_queue;
+
+ if (!pattrib->amsdu) {
+ /* s1. */
+ wlanhdr_to_ethhdr(prframe);
+
+ if ((pattrib->qos!= 1) || (pattrib->eth_type == ETH_P_ARP) ||
+ (pattrib->ack_policy != 0)) {
+ if ((padapter->bDriverStopped == false) &&
+ (padapter->bSurpriseRemoved == false)) {
+ RT_TRACE(_module_rtl871x_recv_c_, _drv_notice_,
+ "@@@@ recv_indicatepkt_reorder -recv_func recv_indicatepkt\n");
+
+ rtw_recv_indicatepkt23a(padapter, prframe);
+ return _SUCCESS;
+ }
+
+ return _FAIL;
+ }
+
+ if (preorder_ctrl->enable == false) {
+ /* indicate this recv_frame */
+ preorder_ctrl->indicate_seq = pattrib->seq_num;
+ rtw_recv_indicatepkt23a(padapter, prframe);
+
+ preorder_ctrl->indicate_seq =
+ (preorder_ctrl->indicate_seq + 1) % 4096;
+ return _SUCCESS;
+ }
+ } else {
+ /* temp filter -> means didn't support A-MSDUs in a A-MPDU */
+ if (preorder_ctrl->enable == false) {
+ preorder_ctrl->indicate_seq = pattrib->seq_num;
+ retval = amsdu_to_msdu(padapter, prframe);
+
+ preorder_ctrl->indicate_seq =
+ (preorder_ctrl->indicate_seq + 1) % 4096;
+ return retval;
+ }
+ }
+
+ spin_lock_bh(&ppending_recvframe_queue->lock);
+
+ RT_TRACE(_module_rtl871x_recv_c_, _drv_notice_,
+ "recv_indicatepkt_reorder: indicate =%d seq =%d\n",
+ preorder_ctrl->indicate_seq, pattrib->seq_num);
+
+ /* s2. check if winstart_b(indicate_seq) needs to been updated */
+ if (!check_indicate_seq(preorder_ctrl, pattrib->seq_num)) {
+ goto _err_exit;
+ }
+
+ /* s3. Insert all packet into Reorder Queue to maintain its ordering. */
+ if (!enqueue_reorder_recvframe23a(preorder_ctrl, prframe)) {
+ goto _err_exit;
+ }
+
+ /* s4. */
+ /* Indication process. */
+ /* After Packet dropping and Sliding Window shifting as above,
+ we can now just indicate the packets */
+ /* with the SeqNum smaller than latest WinStart and buffer
+ other packets. */
+ /* */
+ /* For Rx Reorder condition: */
+ /* 1. All packets with SeqNum smaller than WinStart => Indicate */
+ /* 2. All packets with SeqNum larger than or equal to WinStart =>
+ Buffer it. */
+ /* */
+
+ if (recv_indicatepkts_in_order(padapter, preorder_ctrl, false) == true) {
+ mod_timer(&preorder_ctrl->reordering_ctrl_timer,
+ jiffies + msecs_to_jiffies(REORDER_WAIT_TIME));
+ spin_unlock_bh(&ppending_recvframe_queue->lock);
+ } else {
+ spin_unlock_bh(&ppending_recvframe_queue->lock);
+ del_timer_sync(&preorder_ctrl->reordering_ctrl_timer);
+ }
+ return _SUCCESS;
+
+_err_exit:
+
+ spin_unlock_bh(&ppending_recvframe_queue->lock);
+ return _FAIL;
+}
+
+void rtw_reordering_ctrl_timeout_handler23a(unsigned long pcontext)
+{
+ struct recv_reorder_ctrl *preorder_ctrl;
+ struct rtw_adapter *padapter;
+ struct rtw_queue *ppending_recvframe_queue;
+
+ preorder_ctrl = (struct recv_reorder_ctrl *)pcontext;
+ padapter = preorder_ctrl->padapter;
+ ppending_recvframe_queue = &preorder_ctrl->pending_recvframe_queue;
+
+ if (padapter->bDriverStopped || padapter->bSurpriseRemoved) {
+ return;
+ }
+
+ /* DBG_8723A("+rtw_reordering_ctrl_timeout_handler23a() =>\n"); */
+
+ spin_lock_bh(&ppending_recvframe_queue->lock);
+
+ if (recv_indicatepkts_in_order(padapter, preorder_ctrl, true) == true) {
+ mod_timer(&preorder_ctrl->reordering_ctrl_timer,
+ jiffies + msecs_to_jiffies(REORDER_WAIT_TIME));
+ }
+
+ spin_unlock_bh(&ppending_recvframe_queue->lock);
+}
+
+int process_recv_indicatepkts(struct rtw_adapter *padapter,
+ struct recv_frame *prframe);
+int process_recv_indicatepkts(struct rtw_adapter *padapter,
+ struct recv_frame *prframe)
+{
+ int retval = _SUCCESS;
+ /* struct recv_priv *precvpriv = &padapter->recvpriv; */
+ /* struct rx_pkt_attrib *pattrib = &prframe->attrib; */
+ struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
+ struct ht_priv *phtpriv = &pmlmepriv->htpriv;
+
+ if (phtpriv->ht_option == true) { /* B/G/N Mode */
+ /* prframe->preorder_ctrl = &precvpriv->recvreorder_ctrl[pattrib->priority]; */
+
+ /* including perform A-MPDU Rx Ordering Buffer Control */
+ if (recv_indicatepkt_reorder(padapter, prframe) != _SUCCESS) {
+ if ((padapter->bDriverStopped == false) &&
+ (padapter->bSurpriseRemoved == false)) {
+ retval = _FAIL;
+ return retval;
+ }
+ }
+ } else { /* B/G mode */
+ retval = wlanhdr_to_ethhdr(prframe);
+ if (retval != _SUCCESS) {
+ RT_TRACE(_module_rtl871x_recv_c_, _drv_err_,
+ "wlanhdr_to_ethhdr: drop pkt\n");
+ return retval;
+ }
+
+ if ((padapter->bDriverStopped == false) &&
+ (padapter->bSurpriseRemoved == false)) {
+ /* indicate this recv_frame */
+ RT_TRACE(_module_rtl871x_recv_c_, _drv_notice_,
+ "@@@@ process_recv_indicatepkts- recv_func recv_indicatepkt\n");
+ rtw_recv_indicatepkt23a(padapter, prframe);
+ } else {
+ RT_TRACE(_module_rtl871x_recv_c_, _drv_notice_,
+ "@@@@ process_recv_indicatepkts- recv_func free_indicatepkt\n");
+
+ RT_TRACE(_module_rtl871x_recv_c_, _drv_notice_,
+ "recv_func:bDriverStopped(%d) OR bSurpriseRemoved(%d)\n",
+ padapter->bDriverStopped,
+ padapter->bSurpriseRemoved);
+ retval = _FAIL;
+ return retval;
+ }
+
+ }
+
+ return retval;
+}
+
+static int recv_func_prehandle(struct rtw_adapter *padapter,
+ struct recv_frame *rframe)
+{
+ int ret = _SUCCESS;
+
+ /* check the frame crtl field and decache */
+ ret = validate_recv_frame(padapter, rframe);
+ if (ret != _SUCCESS) {
+ RT_TRACE(_module_rtl871x_recv_c_, _drv_info_,
+ "recv_func: validate_recv_frame fail! drop pkt\n");
+ rtw_free_recvframe23a(rframe);
+ goto exit;
+ }
+
+exit:
+ return ret;
+}
+
+static int recv_func_posthandle(struct rtw_adapter *padapter,
+ struct recv_frame *prframe)
+{
+ int ret = _SUCCESS;
+ struct recv_frame *orig_prframe = prframe;
+ struct recv_priv *precvpriv = &padapter->recvpriv;
+
+ /* DATA FRAME */
+ prframe = decryptor(padapter, prframe);
+ if (prframe == NULL) {
+ RT_TRACE(_module_rtl871x_recv_c_, _drv_err_,
+ "decryptor: drop pkt\n");
+ ret = _FAIL;
+ goto _recv_data_drop;
+ }
+
+ prframe = recvframe_chk_defrag23a(padapter, prframe);
+ if (!prframe) {
+ RT_TRACE(_module_rtl871x_recv_c_, _drv_err_,
+ "recvframe_chk_defrag23a: drop pkt\n");
+ goto _recv_data_drop;
+ }
+
+ /*
+ * Pull off crypto headers
+ */
+ if (prframe->attrib.iv_len > 0) {
+ skb_pull(prframe->pkt, prframe->attrib.iv_len);
+ }
+
+ if (prframe->attrib.icv_len > 0) {
+ skb_trim(prframe->pkt,
+ prframe->pkt->len - prframe->attrib.icv_len);
+ }
+
+ prframe = portctrl(padapter, prframe);
+ if (!prframe) {
+ RT_TRACE(_module_rtl871x_recv_c_, _drv_err_,
+ "portctrl: drop pkt\n");
+ ret = _FAIL;
+ goto _recv_data_drop;
+ }
+
+ count_rx_stats(padapter, prframe, NULL);
+
+ ret = process_recv_indicatepkts(padapter, prframe);
+ if (ret != _SUCCESS) {
+ RT_TRACE(_module_rtl871x_recv_c_, _drv_err_,
+ "recv_func: process_recv_indicatepkts fail!\n");
+ rtw_free_recvframe23a(orig_prframe);/* free this recv_frame */
+ goto _recv_data_drop;
+ }
+ return ret;
+
+_recv_data_drop:
+ precvpriv->rx_drop++;
+ return ret;
+}
+
+int rtw_recv_entry23a(struct recv_frame *rframe)
+{
+ int ret, r;
+ struct rtw_adapter *padapter = rframe->adapter;
+ struct rx_pkt_attrib *prxattrib = &rframe->attrib;
+ struct recv_priv *recvpriv = &padapter->recvpriv;
+ struct security_priv *psecuritypriv = &padapter->securitypriv;
+ struct mlme_priv *mlmepriv = &padapter->mlmepriv;
+
+ /* check if need to handle uc_swdec_pending_queue*/
+ if (check_fwstate(mlmepriv, WIFI_STATION_STATE) &&
+ psecuritypriv->busetkipkey) {
+ struct recv_frame *pending_frame;
+
+ while ((pending_frame = rtw_alloc_recvframe23a(&padapter->recvpriv.uc_swdec_pending_queue))) {
+ r = recv_func_posthandle(padapter, pending_frame);
+ if (r == _SUCCESS)
+ DBG_8723A("%s: dequeue uc_swdec_pending_queue\n", __func__);
+ }
+ }
+
+ ret = recv_func_prehandle(padapter, rframe);
+
+ if (ret == _SUCCESS) {
+ /* check if need to enqueue into uc_swdec_pending_queue*/
+ if (check_fwstate(mlmepriv, WIFI_STATION_STATE) &&
+ !is_multicast_ether_addr(prxattrib->ra) &&
+ prxattrib->encrypt > 0 &&
+ (prxattrib->bdecrypted == 0) &&
+ !is_wep_enc(psecuritypriv->dot11PrivacyAlgrthm) &&
+ !psecuritypriv->busetkipkey) {
+ rtw_enqueue_recvframe23a(rframe, &padapter->recvpriv.uc_swdec_pending_queue);
+ DBG_8723A("%s: no key, enqueue uc_swdec_pending_queue\n", __func__);
+ goto exit;
+ }
+
+ ret = recv_func_posthandle(padapter, rframe);
+
+ recvpriv->rx_pkts++;
+ }
+
+exit:
+ return ret;
+}
+
+void rtw_signal_stat_timer_hdl23a(unsigned long data)
+{
+ struct rtw_adapter *adapter = (struct rtw_adapter *)data;
+ struct recv_priv *recvpriv = &adapter->recvpriv;
+
+ u32 tmp_s, tmp_q;
+ u8 avg_signal_strength = 0;
+ u8 avg_signal_qual = 0;
+ u32 num_signal_strength = 0;
+ u32 num_signal_qual = 0;
+ u8 _alpha = 3; /* this value is based on converging_constant = 5000 */
+ /* and sampling_interval = 1000 */
+
+ if (recvpriv->signal_strength_data.update_req == 0) {
+ /* update_req is clear, means we got rx */
+ avg_signal_strength = recvpriv->signal_strength_data.avg_val;
+ num_signal_strength = recvpriv->signal_strength_data.total_num;
+ /* after avg_vals are acquired, we can re-stat */
+ /* the signal values */
+ recvpriv->signal_strength_data.update_req = 1;
+ }
+
+ if (recvpriv->signal_qual_data.update_req == 0) {
+ /* update_req is clear, means we got rx */
+ avg_signal_qual = recvpriv->signal_qual_data.avg_val;
+ num_signal_qual = recvpriv->signal_qual_data.total_num;
+ /* after avg_vals are acquired, we can re-stat */
+ /*the signal values */
+ recvpriv->signal_qual_data.update_req = 1;
+ }
+
+ /* update value of signal_strength, rssi, signal_qual */
+ if (!check_fwstate(&adapter->mlmepriv, _FW_UNDER_SURVEY)) {
+ tmp_s = avg_signal_strength + (_alpha - 1) *
+ recvpriv->signal_strength;
+ if (tmp_s %_alpha)
+ tmp_s = tmp_s / _alpha + 1;
+ else
+ tmp_s = tmp_s / _alpha;
+ if (tmp_s > 100)
+ tmp_s = 100;
+
+ tmp_q = avg_signal_qual + (_alpha - 1) * recvpriv->signal_qual;
+ if (tmp_q %_alpha)
+ tmp_q = tmp_q / _alpha + 1;
+ else
+ tmp_q = tmp_q / _alpha;
+ if (tmp_q > 100)
+ tmp_q = 100;
+
+ recvpriv->signal_strength = tmp_s;
+ recvpriv->signal_qual = tmp_q;
+
+ DBG_8723A("%s signal_strength:%3u, signal_qual:%3u, "
+ "num_signal_strength:%u, num_signal_qual:%u\n",
+ __func__, recvpriv->signal_strength,
+ recvpriv->signal_qual, num_signal_strength,
+ num_signal_qual);
+ }
+
+ rtw_set_signal_stat_timer(recvpriv);
+}
diff --git a/drivers/staging/rtl8723au/core/rtw_security.c b/drivers/staging/rtl8723au/core/rtw_security.c
new file mode 100644
index 000000000..af53c92fc
--- /dev/null
+++ b/drivers/staging/rtl8723au/core/rtw_security.c
@@ -0,0 +1,1635 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2011 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ ******************************************************************************/
+#define _RTW_SECURITY_C_
+
+#include <osdep_service.h>
+#include <drv_types.h>
+#include <wifi.h>
+#include <osdep_intf.h>
+
+/* WEP related ===== */
+
+#define CRC32_POLY 0x04c11db7
+
+struct arc4context {
+ u32 x;
+ u32 y;
+ u8 state[256];
+};
+
+static void arcfour_init(struct arc4context *parc4ctx, u8 *key, u32 key_len)
+{
+ u32 t, u;
+ u32 keyindex;
+ u32 stateindex;
+ u8 *state;
+ u32 counter;
+
+ state = parc4ctx->state;
+ parc4ctx->x = 0;
+ parc4ctx->y = 0;
+ for (counter = 0; counter < 256; counter++)
+ state[counter] = (u8)counter;
+ keyindex = 0;
+ stateindex = 0;
+ for (counter = 0; counter < 256; counter++) {
+ t = state[counter];
+ stateindex = (stateindex + key[keyindex] + t) & 0xff;
+ u = state[stateindex];
+ state[stateindex] = (u8)t;
+ state[counter] = (u8)u;
+ if (++keyindex >= key_len)
+ keyindex = 0;
+ }
+
+}
+
+static u32 arcfour_byte(struct arc4context *parc4ctx)
+{
+ u32 x;
+ u32 y;
+ u32 sx, sy;
+ u8 *state;
+
+ state = parc4ctx->state;
+ x = (parc4ctx->x + 1) & 0xff;
+ sx = state[x];
+ y = (sx + parc4ctx->y) & 0xff;
+ sy = state[y];
+ parc4ctx->x = x;
+ parc4ctx->y = y;
+ state[y] = (u8)sx;
+ state[x] = (u8)sy;
+
+ return state[(sx + sy) & 0xff];
+}
+
+static void arcfour_encrypt(struct arc4context *parc4ctx, u8 *dest,
+ u8 *src, u32 len)
+{
+ u32 i;
+
+ for (i = 0; i < len; i++)
+ dest[i] = src[i] ^ (unsigned char)arcfour_byte(parc4ctx);
+}
+
+static int bcrc32initialized;
+static u32 crc32_table[256];
+
+static u8 crc32_reverseBit(u8 data)
+{
+ u8 retval = ((data << 7) & 0x80) | ((data << 5) & 0x40) |
+ ((data << 3) & 0x20) | ((data << 1) & 0x10) |
+ ((data >> 1) & 0x08) | ((data >> 3) & 0x04) |
+ ((data >> 5) & 0x02) | ((data >> 7) & 0x01);
+ return retval;
+}
+
+static void crc32_init(void)
+{
+ int i, j;
+ u32 c;
+ u8 *p, *p1;
+ u8 k;
+
+ if (bcrc32initialized == 1)
+ return;
+
+ p = (u8 *) &c;
+ c = 0x12340000;
+
+ for (i = 0; i < 256; ++i) {
+ k = crc32_reverseBit((u8)i);
+
+ for (c = ((u32)k) << 24, j = 8; j > 0; --j)
+ c = c & 0x80000000 ? (c << 1) ^ CRC32_POLY : (c << 1);
+
+ p1 = (u8 *)&crc32_table[i];
+
+ p1[0] = crc32_reverseBit(p[3]);
+ p1[1] = crc32_reverseBit(p[2]);
+ p1[2] = crc32_reverseBit(p[1]);
+ p1[3] = crc32_reverseBit(p[0]);
+ }
+
+ bcrc32initialized = 1;
+}
+
+static u32 getcrc32(u8 *buf, int len)
+{
+ u8 *p;
+ u32 crc;
+
+ if (bcrc32initialized == 0)
+ crc32_init();
+
+ crc = 0xffffffff; /* preload shift register, per CRC-32 spec */
+
+ for (p = buf; len > 0; ++p, --len)
+ crc = crc32_table[(crc ^ *p) & 0xff] ^ (crc >> 8);
+
+ return ~crc; /* transmit complement, per CRC-32 spec */
+}
+
+/* Need to consider the fragment situation */
+void rtw_wep_encrypt23a(struct rtw_adapter *padapter,
+ struct xmit_frame *pxmitframe)
+{
+ /* exclude ICV */
+ unsigned char crc[4];
+ struct arc4context mycontext;
+ int curfragnum, length, index;
+ u32 keylength;
+ u8 *pframe, *payload, *iv; /* wepkey */
+ u8 wepkey[16];
+ u8 hw_hdr_offset = 0;
+ struct pkt_attrib *pattrib = &pxmitframe->attrib;
+ struct security_priv *psecuritypriv = &padapter->securitypriv;
+ struct xmit_priv *pxmitpriv = &padapter->xmitpriv;
+
+ if (!pxmitframe->buf_addr)
+ return;
+
+ hw_hdr_offset = TXDESC_OFFSET;
+
+ pframe = pxmitframe->buf_addr + hw_hdr_offset;
+
+ /* start to encrypt each fragment */
+ if (pattrib->encrypt != WLAN_CIPHER_SUITE_WEP40 &&
+ pattrib->encrypt != WLAN_CIPHER_SUITE_WEP104)
+ return;
+
+ index = psecuritypriv->dot11PrivacyKeyIndex;
+ keylength = psecuritypriv->wep_key[index].keylen;
+
+ for (curfragnum = 0; curfragnum < pattrib->nr_frags ; curfragnum++) {
+ iv = pframe + pattrib->hdrlen;
+ memcpy(&wepkey[0], iv, 3);
+ memcpy(&wepkey[3], &psecuritypriv->wep_key[index].key,
+ keylength);
+ payload = pframe + pattrib->iv_len + pattrib->hdrlen;
+
+ if ((curfragnum + 1) == pattrib->nr_frags) {
+ /* the last fragment */
+ length = pattrib->last_txcmdsz - pattrib->hdrlen -
+ pattrib->iv_len - pattrib->icv_len;
+
+ *((u32 *)crc) = cpu_to_le32(getcrc32(payload, length));
+
+ arcfour_init(&mycontext, wepkey, 3 + keylength);
+ arcfour_encrypt(&mycontext, payload, payload, length);
+ arcfour_encrypt(&mycontext, payload + length, crc, 4);
+ } else {
+ length = pxmitpriv->frag_len - pattrib->hdrlen -
+ pattrib->iv_len - pattrib->icv_len;
+ *((u32 *)crc) = cpu_to_le32(getcrc32(payload, length));
+ arcfour_init(&mycontext, wepkey, 3 + keylength);
+ arcfour_encrypt(&mycontext, payload, payload, length);
+ arcfour_encrypt(&mycontext, payload + length, crc, 4);
+
+ pframe += pxmitpriv->frag_len;
+ pframe = PTR_ALIGN(pframe, 4);
+ }
+ }
+
+}
+
+void rtw_wep_decrypt23a(struct rtw_adapter *padapter,
+ struct recv_frame *precvframe)
+{
+ /* exclude ICV */
+ u32 actual_crc, expected_crc;
+ struct arc4context mycontext;
+ int length;
+ u32 keylength;
+ u8 *pframe, *payload, *iv, wepkey[16];
+ u8 keyindex;
+ struct rx_pkt_attrib *prxattrib = &precvframe->attrib;
+ struct security_priv *psecuritypriv = &padapter->securitypriv;
+ struct sk_buff *skb = precvframe->pkt;
+
+ pframe = skb->data;
+
+ /* start to decrypt recvframe */
+ if (prxattrib->encrypt != WLAN_CIPHER_SUITE_WEP40 &&
+ prxattrib->encrypt != WLAN_CIPHER_SUITE_WEP104)
+ return;
+
+ iv = pframe + prxattrib->hdrlen;
+ /* keyindex = (iv[3]&0x3); */
+ keyindex = prxattrib->key_index;
+ keylength = psecuritypriv->wep_key[keyindex].keylen;
+ memcpy(&wepkey[0], iv, 3);
+ /* memcpy(&wepkey[3], &psecuritypriv->dot11DefKey[psecuritypriv->dot11PrivacyKeyIndex].skey[0], keylength); */
+ memcpy(&wepkey[3], &psecuritypriv->wep_key[keyindex].key, keylength);
+ length = skb->len - prxattrib->hdrlen - prxattrib->iv_len;
+
+ payload = pframe + prxattrib->iv_len + prxattrib->hdrlen;
+
+ /* decrypt payload include icv */
+ arcfour_init(&mycontext, wepkey, 3 + keylength);
+ arcfour_encrypt(&mycontext, payload, payload, length);
+
+ /* calculate icv and compare the icv */
+ actual_crc = le32_to_cpu(getcrc32(payload, length - 4));
+ expected_crc = le32_to_cpu(get_unaligned_le32(&payload[length - 4]));
+
+ if (actual_crc != expected_crc) {
+ RT_TRACE(_module_rtl871x_security_c_, _drv_err_,
+ "%s:icv CRC mismatch: "
+ "actual: %08x, expected: %08x\n",
+ __func__, actual_crc, expected_crc);
+ }
+}
+
+/* 3 ===== TKIP related ===== */
+
+static u32 secmicgetuint32(u8 *p)
+/* Convert from Byte[] to u32 in a portable way */
+{
+ s32 i;
+ u32 res = 0;
+
+ for (i = 0; i < 4; i++)
+ res |= ((u32)(*p++)) << (8 * i);
+
+ return res;
+}
+
+static void secmicputuint32(u8 *p, u32 val)
+/* Convert from long to Byte[] in a portable way */
+{
+ long i;
+
+ for (i = 0; i < 4; i++) {
+ *p++ = (u8) (val & 0xff);
+ val >>= 8;
+ }
+
+}
+
+static void secmicclear(struct mic_data *pmicdata)
+{
+/* Reset the state to the empty message. */
+
+ pmicdata->L = pmicdata->K0;
+ pmicdata->R = pmicdata->K1;
+ pmicdata->nBytesInM = 0;
+ pmicdata->M = 0;
+
+}
+
+void rtw_secmicsetkey23a(struct mic_data *pmicdata, u8 *key)
+{
+ /* Set the key */
+
+ pmicdata->K0 = secmicgetuint32(key);
+ pmicdata->K1 = secmicgetuint32(key + 4);
+ /* and reset the message */
+ secmicclear(pmicdata);
+
+}
+
+void rtw_secmicappend23abyte23a(struct mic_data *pmicdata, u8 b)
+{
+
+ /* Append the byte to our word-sized buffer */
+ pmicdata->M |= ((unsigned long)b) << (8 * pmicdata->nBytesInM);
+ pmicdata->nBytesInM++;
+ /* Process the word if it is full. */
+ if (pmicdata->nBytesInM >= 4) {
+ pmicdata->L ^= pmicdata->M;
+ pmicdata->R ^= ROL32(pmicdata->L, 17);
+ pmicdata->L += pmicdata->R;
+ pmicdata->R ^= ((pmicdata->L & 0xff00ff00) >> 8) | ((pmicdata->L & 0x00ff00ff) << 8);
+ pmicdata->L += pmicdata->R;
+ pmicdata->R ^= ROL32(pmicdata->L, 3);
+ pmicdata->L += pmicdata->R;
+ pmicdata->R ^= ROR32(pmicdata->L, 2);
+ pmicdata->L += pmicdata->R;
+ /* Clear the buffer */
+ pmicdata->M = 0;
+ pmicdata->nBytesInM = 0;
+ }
+
+}
+
+void rtw_secmicappend23a(struct mic_data *pmicdata, u8 *src, u32 nbytes)
+{
+
+ /* This is simple */
+ while (nbytes > 0) {
+ rtw_secmicappend23abyte23a(pmicdata, *src++);
+ nbytes--;
+ }
+
+}
+
+void rtw_secgetmic23a(struct mic_data *pmicdata, u8 *dst)
+{
+
+ /* Append the minimum padding */
+ rtw_secmicappend23abyte23a(pmicdata, 0x5a);
+ rtw_secmicappend23abyte23a(pmicdata, 0);
+ rtw_secmicappend23abyte23a(pmicdata, 0);
+ rtw_secmicappend23abyte23a(pmicdata, 0);
+ rtw_secmicappend23abyte23a(pmicdata, 0);
+ /* and then zeroes until the length is a multiple of 4 */
+ while (pmicdata->nBytesInM != 0)
+ rtw_secmicappend23abyte23a(pmicdata, 0);
+ /* The appendByte function has already computed the result. */
+ secmicputuint32(dst, pmicdata->L);
+ secmicputuint32(dst + 4, pmicdata->R);
+ /* Reset to the empty message. */
+ secmicclear(pmicdata);
+
+}
+
+void rtw_seccalctkipmic23a(u8 *key, u8 *header, u8 *data, u32 data_len,
+ u8 *mic_code, u8 pri)
+{
+
+ struct mic_data micdata;
+ u8 priority[4] = {0x0, 0x0, 0x0, 0x0};
+
+ rtw_secmicsetkey23a(&micdata, key);
+ priority[0] = pri;
+
+ /* Michael MIC pseudo header: DA, SA, 3 x 0, Priority */
+ if (header[1]&1) { /* ToDS == 1 */
+ rtw_secmicappend23a(&micdata, &header[16], 6); /* DA */
+ if (header[1]&2) /* From Ds == 1 */
+ rtw_secmicappend23a(&micdata, &header[24], 6);
+ else
+ rtw_secmicappend23a(&micdata, &header[10], 6);
+ } else { /* ToDS == 0 */
+ rtw_secmicappend23a(&micdata, &header[4], 6); /* DA */
+ if (header[1]&2) /* From Ds == 1 */
+ rtw_secmicappend23a(&micdata, &header[16], 6);
+ else
+ rtw_secmicappend23a(&micdata, &header[10], 6);
+
+ }
+ rtw_secmicappend23a(&micdata, &priority[0], 4);
+
+ rtw_secmicappend23a(&micdata, data, data_len);
+
+ rtw_secgetmic23a(&micdata, mic_code);
+
+}
+
+/* macros for extraction/creation of unsigned char/unsigned short values */
+#define RotR1(v16) ((((v16) >> 1) & 0x7FFF) ^ (((v16) & 1) << 15))
+#define Lo8(v16) ((u8)((v16) & 0x00FF))
+#define Hi8(v16) ((u8)(((v16) >> 8) & 0x00FF))
+#define Lo16(v32) ((u16)((v32) & 0xFFFF))
+#define Hi16(v32) ((u16)(((v32) >> 16) & 0xFFFF))
+#define Mk16(hi, lo) ((lo) ^ (((u16)(hi)) << 8))
+
+/* select the Nth 16-bit word of the temporal key unsigned char array TK[] */
+#define TK16(N) Mk16(tk[2 * (N) + 1], tk[2 * (N)])
+
+/* S-box lookup: 16 bits --> 16 bits */
+#define _S_(v16) (Sbox1[0][Lo8(v16)] ^ Sbox1[1][Hi8(v16)])
+
+/* fixed algorithm "parameters" */
+#define PHASE1_LOOP_CNT 8 /* this needs to be "big enough" */
+#define TA_SIZE 6 /* 48-bit transmitter address */
+#define TK_SIZE 16 /* 128-bit temporal key */
+#define P1K_SIZE 10 /* 80-bit Phase1 key */
+#define RC4_KEY_SIZE 16 /* 128-bit RC4KEY (104 bits unknown) */
+
+/* 2-unsigned char by 2-unsigned char subset of the full AES S-box table */
+static const unsigned short Sbox1[2][256] = {
+ /* Sbox for hash (can be in ROM) */
+ {
+ 0xC6A5, 0xF884, 0xEE99, 0xF68D, 0xFF0D, 0xD6BD, 0xDEB1, 0x9154,
+ 0x6050, 0x0203, 0xCEA9, 0x567D, 0xE719, 0xB562, 0x4DE6, 0xEC9A,
+ 0x8F45, 0x1F9D, 0x8940, 0xFA87, 0xEF15, 0xB2EB, 0x8EC9, 0xFB0B,
+ 0x41EC, 0xB367, 0x5FFD, 0x45EA, 0x23BF, 0x53F7, 0xE496, 0x9B5B,
+ 0x75C2, 0xE11C, 0x3DAE, 0x4C6A, 0x6C5A, 0x7E41, 0xF502, 0x834F,
+ 0x685C, 0x51F4, 0xD134, 0xF908, 0xE293, 0xAB73, 0x6253, 0x2A3F,
+ 0x080C, 0x9552, 0x4665, 0x9D5E, 0x3028, 0x37A1, 0x0A0F, 0x2FB5,
+ 0x0E09, 0x2436, 0x1B9B, 0xDF3D, 0xCD26, 0x4E69, 0x7FCD, 0xEA9F,
+ 0x121B, 0x1D9E, 0x5874, 0x342E, 0x362D, 0xDCB2, 0xB4EE, 0x5BFB,
+ 0xA4F6, 0x764D, 0xB761, 0x7DCE, 0x527B, 0xDD3E, 0x5E71, 0x1397,
+ 0xA6F5, 0xB968, 0x0000, 0xC12C, 0x4060, 0xE31F, 0x79C8, 0xB6ED,
+ 0xD4BE, 0x8D46, 0x67D9, 0x724B, 0x94DE, 0x98D4, 0xB0E8, 0x854A,
+ 0xBB6B, 0xC52A, 0x4FE5, 0xED16, 0x86C5, 0x9AD7, 0x6655, 0x1194,
+ 0x8ACF, 0xE910, 0x0406, 0xFE81, 0xA0F0, 0x7844, 0x25BA, 0x4BE3,
+ 0xA2F3, 0x5DFE, 0x80C0, 0x058A, 0x3FAD, 0x21BC, 0x7048, 0xF104,
+ 0x63DF, 0x77C1, 0xAF75, 0x4263, 0x2030, 0xE51A, 0xFD0E, 0xBF6D,
+ 0x814C, 0x1814, 0x2635, 0xC32F, 0xBEE1, 0x35A2, 0x88CC, 0x2E39,
+ 0x9357, 0x55F2, 0xFC82, 0x7A47, 0xC8AC, 0xBAE7, 0x322B, 0xE695,
+ 0xC0A0, 0x1998, 0x9ED1, 0xA37F, 0x4466, 0x547E, 0x3BAB, 0x0B83,
+ 0x8CCA, 0xC729, 0x6BD3, 0x283C, 0xA779, 0xBCE2, 0x161D, 0xAD76,
+ 0xDB3B, 0x6456, 0x744E, 0x141E, 0x92DB, 0x0C0A, 0x486C, 0xB8E4,
+ 0x9F5D, 0xBD6E, 0x43EF, 0xC4A6, 0x39A8, 0x31A4, 0xD337, 0xF28B,
+ 0xD532, 0x8B43, 0x6E59, 0xDAB7, 0x018C, 0xB164, 0x9CD2, 0x49E0,
+ 0xD8B4, 0xACFA, 0xF307, 0xCF25, 0xCAAF, 0xF48E, 0x47E9, 0x1018,
+ 0x6FD5, 0xF088, 0x4A6F, 0x5C72, 0x3824, 0x57F1, 0x73C7, 0x9751,
+ 0xCB23, 0xA17C, 0xE89C, 0x3E21, 0x96DD, 0x61DC, 0x0D86, 0x0F85,
+ 0xE090, 0x7C42, 0x71C4, 0xCCAA, 0x90D8, 0x0605, 0xF701, 0x1C12,
+ 0xC2A3, 0x6A5F, 0xAEF9, 0x69D0, 0x1791, 0x9958, 0x3A27, 0x27B9,
+ 0xD938, 0xEB13, 0x2BB3, 0x2233, 0xD2BB, 0xA970, 0x0789, 0x33A7,
+ 0x2DB6, 0x3C22, 0x1592, 0xC920, 0x8749, 0xAAFF, 0x5078, 0xA57A,
+ 0x038F, 0x59F8, 0x0980, 0x1A17, 0x65DA, 0xD731, 0x84C6, 0xD0B8,
+ 0x82C3, 0x29B0, 0x5A77, 0x1E11, 0x7BCB, 0xA8FC, 0x6DD6, 0x2C3A,
+ },
+ { /* second half of table is unsigned char-reversed version of first! */
+ 0xA5C6, 0x84F8, 0x99EE, 0x8DF6, 0x0DFF, 0xBDD6, 0xB1DE, 0x5491,
+ 0x5060, 0x0302, 0xA9CE, 0x7D56, 0x19E7, 0x62B5, 0xE64D, 0x9AEC,
+ 0x458F, 0x9D1F, 0x4089, 0x87FA, 0x15EF, 0xEBB2, 0xC98E, 0x0BFB,
+ 0xEC41, 0x67B3, 0xFD5F, 0xEA45, 0xBF23, 0xF753, 0x96E4, 0x5B9B,
+ 0xC275, 0x1CE1, 0xAE3D, 0x6A4C, 0x5A6C, 0x417E, 0x02F5, 0x4F83,
+ 0x5C68, 0xF451, 0x34D1, 0x08F9, 0x93E2, 0x73AB, 0x5362, 0x3F2A,
+ 0x0C08, 0x5295, 0x6546, 0x5E9D, 0x2830, 0xA137, 0x0F0A, 0xB52F,
+ 0x090E, 0x3624, 0x9B1B, 0x3DDF, 0x26CD, 0x694E, 0xCD7F, 0x9FEA,
+ 0x1B12, 0x9E1D, 0x7458, 0x2E34, 0x2D36, 0xB2DC, 0xEEB4, 0xFB5B,
+ 0xF6A4, 0x4D76, 0x61B7, 0xCE7D, 0x7B52, 0x3EDD, 0x715E, 0x9713,
+ 0xF5A6, 0x68B9, 0x0000, 0x2CC1, 0x6040, 0x1FE3, 0xC879, 0xEDB6,
+ 0xBED4, 0x468D, 0xD967, 0x4B72, 0xDE94, 0xD498, 0xE8B0, 0x4A85,
+ 0x6BBB, 0x2AC5, 0xE54F, 0x16ED, 0xC586, 0xD79A, 0x5566, 0x9411,
+ 0xCF8A, 0x10E9, 0x0604, 0x81FE, 0xF0A0, 0x4478, 0xBA25, 0xE34B,
+ 0xF3A2, 0xFE5D, 0xC080, 0x8A05, 0xAD3F, 0xBC21, 0x4870, 0x04F1,
+ 0xDF63, 0xC177, 0x75AF, 0x6342, 0x3020, 0x1AE5, 0x0EFD, 0x6DBF,
+ 0x4C81, 0x1418, 0x3526, 0x2FC3, 0xE1BE, 0xA235, 0xCC88, 0x392E,
+ 0x5793, 0xF255, 0x82FC, 0x477A, 0xACC8, 0xE7BA, 0x2B32, 0x95E6,
+ 0xA0C0, 0x9819, 0xD19E, 0x7FA3, 0x6644, 0x7E54, 0xAB3B, 0x830B,
+ 0xCA8C, 0x29C7, 0xD36B, 0x3C28, 0x79A7, 0xE2BC, 0x1D16, 0x76AD,
+ 0x3BDB, 0x5664, 0x4E74, 0x1E14, 0xDB92, 0x0A0C, 0x6C48, 0xE4B8,
+ 0x5D9F, 0x6EBD, 0xEF43, 0xA6C4, 0xA839, 0xA431, 0x37D3, 0x8BF2,
+ 0x32D5, 0x438B, 0x596E, 0xB7DA, 0x8C01, 0x64B1, 0xD29C, 0xE049,
+ 0xB4D8, 0xFAAC, 0x07F3, 0x25CF, 0xAFCA, 0x8EF4, 0xE947, 0x1810,
+ 0xD56F, 0x88F0, 0x6F4A, 0x725C, 0x2438, 0xF157, 0xC773, 0x5197,
+ 0x23CB, 0x7CA1, 0x9CE8, 0x213E, 0xDD96, 0xDC61, 0x860D, 0x850F,
+ 0x90E0, 0x427C, 0xC471, 0xAACC, 0xD890, 0x0506, 0x01F7, 0x121C,
+ 0xA3C2, 0x5F6A, 0xF9AE, 0xD069, 0x9117, 0x5899, 0x273A, 0xB927,
+ 0x38D9, 0x13EB, 0xB32B, 0x3322, 0xBBD2, 0x70A9, 0x8907, 0xA733,
+ 0xB62D, 0x223C, 0x9215, 0x20C9, 0x4987, 0xFFAA, 0x7850, 0x7AA5,
+ 0x8F03, 0xF859, 0x8009, 0x171A, 0xDA65, 0x31D7, 0xC684, 0xB8D0,
+ 0xC382, 0xB029, 0x775A, 0x111E, 0xCB7B, 0xFCA8, 0xD66D, 0x3A2C,
+ }
+};
+
+ /*
+**********************************************************************
+* Routine: Phase 1 -- generate P1K, given TA, TK, IV32
+*
+* Inputs:
+* tk[] = temporal key [128 bits]
+* ta[] = transmitter's MAC address [ 48 bits]
+* iv32 = upper 32 bits of IV [ 32 bits]
+* Output:
+* p1k[] = Phase 1 key [ 80 bits]
+*
+* Note:
+* This function only needs to be called every 2**16 packets,
+* although in theory it could be called every packet.
+*
+**********************************************************************
+*/
+static void phase1(u16 *p1k, const u8 *tk, const u8 *ta, u32 iv32)
+{
+ int i;
+
+ /* Initialize the 80 bits of P1K[] from IV32 and TA[0..5] */
+ p1k[0] = Lo16(iv32);
+ p1k[1] = Hi16(iv32);
+ p1k[2] = Mk16(ta[1], ta[0]); /* use TA[] as little-endian */
+ p1k[3] = Mk16(ta[3], ta[2]);
+ p1k[4] = Mk16(ta[5], ta[4]);
+
+ /* Now compute an unbalanced Feistel cipher with 80-bit block */
+ /* size on the 80-bit block P1K[], using the 128-bit key TK[] */
+ for (i = 0; i < PHASE1_LOOP_CNT; i++) {
+ /* Each add operation here is mod 2**16 */
+ p1k[0] += _S_(p1k[4] ^ TK16((i & 1) + 0));
+ p1k[1] += _S_(p1k[0] ^ TK16((i & 1) + 2));
+ p1k[2] += _S_(p1k[1] ^ TK16((i & 1) + 4));
+ p1k[3] += _S_(p1k[2] ^ TK16((i & 1) + 6));
+ p1k[4] += _S_(p1k[3] ^ TK16((i & 1) + 0));
+ p1k[4] += (unsigned short) i; /* avoid "slide attacks" */
+ }
+
+}
+
+/*
+**********************************************************************
+* Routine: Phase 2 -- generate RC4KEY, given TK, P1K, IV16
+*
+* Inputs:
+* tk[] = Temporal key [128 bits]
+* p1k[] = Phase 1 output key [ 80 bits]
+* iv16 = low 16 bits of IV counter [ 16 bits]
+* Output:
+* rc4key[] = the key used to encrypt the packet [128 bits]
+*
+* Note:
+* The value {TA, IV32, IV16} for Phase1/Phase2 must be unique
+* across all packets using the same key TK value. Then, for a
+* given value of TK[], this TKIP48 construction guarantees that
+* the final RC4KEY value is unique across all packets.
+*
+* Suggested implementation optimization: if PPK[] is "overlaid"
+* appropriately on RC4KEY[], there is no need for the final
+* for loop below that copies the PPK[] result into RC4KEY[].
+*
+**********************************************************************
+*/
+static void phase2(u8 *rc4key, const u8 *tk, const u16 *p1k, u16 iv16)
+{
+ int i;
+ u16 PPK[6]; /* temporary key for mixing */
+
+ /* Note: all adds in the PPK[] equations below are mod 2**16 */
+ for (i = 0; i < 5; i++)
+ PPK[i] = p1k[i]; /* first, copy P1K to PPK */
+
+ PPK[5] = p1k[4] + iv16; /* next, add in IV16 */
+
+ /* Bijective non-linear mixing of the 96 bits of PPK[0..5] */
+ PPK[0] += _S_(PPK[5] ^ TK16(0)); /* Mix key in each "round" */
+ PPK[1] += _S_(PPK[0] ^ TK16(1));
+ PPK[2] += _S_(PPK[1] ^ TK16(2));
+ PPK[3] += _S_(PPK[2] ^ TK16(3));
+ PPK[4] += _S_(PPK[3] ^ TK16(4));
+ PPK[5] += _S_(PPK[4] ^ TK16(5)); /* Total # S-box lookups == 6 */
+
+ /* Final sweep: bijective, "linear". Rotates kill LSB correlations */
+ PPK[0] += RotR1(PPK[5] ^ TK16(6));
+ PPK[1] += RotR1(PPK[0] ^ TK16(7)); /* Use all of TK[] in Phase2 */
+ PPK[2] += RotR1(PPK[1]);
+ PPK[3] += RotR1(PPK[2]);
+ PPK[4] += RotR1(PPK[3]);
+ PPK[5] += RotR1(PPK[4]);
+ /* Note: At this point, for a given key TK[0..15], the 96-bit output */
+ /* value PPK[0..5] is guaranteed to be unique, as a function */
+ /* of the 96-bit "input" value {TA, IV32, IV16}. That is, */
+ /* P1K is now a keyed permutation of {TA, IV32, IV16}. */
+
+ /* Set RC4KEY[0..3], which includes "cleartext" portion of RC4 key */
+ rc4key[0] = Hi8(iv16); /* RC4KEY[0..2] is the WEP IV */
+ rc4key[1] = (Hi8(iv16) | 0x20) & 0x7F; /* Help avoid weak (FMS) keys */
+ rc4key[2] = Lo8(iv16);
+ rc4key[3] = Lo8((PPK[5] ^ TK16(0)) >> 1);
+
+ /* Copy 96 bits of PPK[0..5] to RC4KEY[4..15] (little-endian) */
+ for (i = 0; i < 6; i++) {
+ rc4key[4 + 2 * i] = Lo8(PPK[i]);
+ rc4key[5 + 2 * i] = Hi8(PPK[i]);
+ }
+
+}
+
+/* The hlen isn't include the IV */
+int rtw_tkip_encrypt23a(struct rtw_adapter *padapter,
+ struct xmit_frame *pxmitframe)
+{
+ u16 pnl;
+ u32 pnh;
+ u8 rc4key[16];
+ u8 ttkey[16];
+ u8 crc[4];
+ u8 hw_hdr_offset = 0;
+ struct arc4context mycontext;
+ int curfragnum, length;
+ u32 prwskeylen;
+ u8 *pframe, *payload, *iv, *prwskey;
+ union pn48 dot11txpn;
+ struct sta_info *stainfo;
+ struct pkt_attrib *pattrib = &pxmitframe->attrib;
+ struct security_priv *psecuritypriv = &padapter->securitypriv;
+ struct xmit_priv *pxmitpriv = &padapter->xmitpriv;
+ int res = _SUCCESS;
+
+ if (pattrib->encrypt != WLAN_CIPHER_SUITE_TKIP)
+ return _FAIL;
+
+ if (!pxmitframe->buf_addr)
+ return _FAIL;
+
+ hw_hdr_offset = TXDESC_OFFSET;
+
+ pframe = pxmitframe->buf_addr + hw_hdr_offset;
+
+ if (pattrib->psta)
+ stainfo = pattrib->psta;
+ else {
+ DBG_8723A("%s, call rtw_get_stainfo()\n", __func__);
+ stainfo = rtw_get_stainfo23a(&padapter->stapriv,
+ &pattrib->ra[0]);
+ }
+
+ if (stainfo == NULL) {
+ RT_TRACE(_module_rtl871x_security_c_, _drv_err_,
+ "%s: stainfo == NULL!!!\n", __func__);
+ DBG_8723A("%s, psta == NUL\n", __func__);
+ return _FAIL;
+ }
+
+ RT_TRACE(_module_rtl871x_security_c_, _drv_err_,
+ "%s: stainfo!= NULL!!!\n", __func__);
+
+ if (!(stainfo->state & _FW_LINKED)) {
+ DBG_8723A("%s, psta->state(0x%x) != _FW_LINKED\n", __func__, stainfo->state);
+ return _FAIL;
+ }
+
+ if (is_multicast_ether_addr(pattrib->ra))
+ prwskey = psecuritypriv->dot118021XGrpKey[psecuritypriv->dot118021XGrpKeyid].skey;
+ else
+ prwskey = &stainfo->dot118021x_UncstKey.skey[0];
+
+ prwskeylen = 16;
+
+ /* 4 start to encrypt each fragment */
+ for (curfragnum = 0; curfragnum < pattrib->nr_frags; curfragnum++) {
+ iv = pframe + pattrib->hdrlen;
+ payload = pframe + pattrib->iv_len + pattrib->hdrlen;
+
+ GET_TKIP_PN(iv, dot11txpn);
+
+ pnl = (u16)(dot11txpn.val);
+ pnh = (u32)(dot11txpn.val>>16);
+
+ phase1((u16 *)&ttkey[0], prwskey, &pattrib->ta[0], pnh);
+
+ phase2(&rc4key[0], prwskey, (u16 *)&ttkey[0], pnl);
+
+ if ((curfragnum + 1) == pattrib->nr_frags) { /* 4 the last fragment */
+ length = (pattrib->last_txcmdsz -
+ pattrib->hdrlen -
+ pattrib->iv_len -
+ pattrib->icv_len);
+
+ RT_TRACE(_module_rtl871x_security_c_, _drv_info_,
+ "pattrib->iv_len =%x, pattrib->icv_len =%x\n",
+ pattrib->iv_len,
+ pattrib->icv_len);
+ *((u32 *)crc) = cpu_to_le32(getcrc32(payload, length));
+
+ arcfour_init(&mycontext, rc4key, 16);
+ arcfour_encrypt(&mycontext, payload, payload, length);
+ arcfour_encrypt(&mycontext, payload + length, crc, 4);
+
+ } else {
+ length = (pxmitpriv->frag_len -
+ pattrib->hdrlen -
+ pattrib->iv_len -
+ pattrib->icv_len);
+
+ *((u32 *)crc) = cpu_to_le32(getcrc32(payload, length));
+ arcfour_init(&mycontext, rc4key, 16);
+ arcfour_encrypt(&mycontext, payload, payload, length);
+ arcfour_encrypt(&mycontext, payload + length, crc, 4);
+
+ pframe += pxmitpriv->frag_len;
+ pframe = PTR_ALIGN(pframe, 4);
+ }
+ }
+
+ return res;
+}
+
+/* The hlen isn't include the IV */
+int rtw_tkip_decrypt23a(struct rtw_adapter *padapter,
+ struct recv_frame *precvframe)
+{
+ u16 pnl;
+ u32 pnh;
+ u8 rc4key[16];
+ u8 ttkey[16];
+ u32 actual_crc, expected_crc;
+ struct arc4context mycontext;
+ int length;
+ u32 prwskeylen;
+ u8 *pframe, *payload, *iv, *prwskey;
+ union pn48 dot11txpn;
+ struct sta_info *stainfo;
+ struct rx_pkt_attrib *prxattrib = &precvframe->attrib;
+ struct security_priv *psecuritypriv = &padapter->securitypriv;
+ struct sk_buff *skb = precvframe->pkt;
+ int res = _SUCCESS;
+
+ if (prxattrib->encrypt != WLAN_CIPHER_SUITE_TKIP)
+ return _FAIL;
+
+ pframe = skb->data;
+
+ stainfo = rtw_get_stainfo23a(&padapter->stapriv,
+ &prxattrib->ta[0]);
+ if (stainfo == NULL) {
+ RT_TRACE(_module_rtl871x_security_c_, _drv_err_,
+ "%s: stainfo == NULL!!!\n", __func__);
+ return _FAIL;
+ }
+
+ /* 4 start to decrypt recvframe */
+ if (is_multicast_ether_addr(prxattrib->ra)) {
+ if (psecuritypriv->binstallGrpkey == 0) {
+ res = _FAIL;
+ DBG_8723A("%s:rx bc/mc packets, but didn't install group key!!!!!!!!!!\n", __func__);
+ goto exit;
+ }
+ prwskey = psecuritypriv->dot118021XGrpKey[prxattrib->key_index].skey;
+ prwskeylen = 16;
+ } else {
+ RT_TRACE(_module_rtl871x_security_c_, _drv_err_,
+ "%s: stainfo!= NULL!!!\n", __func__);
+ prwskey = &stainfo->dot118021x_UncstKey.skey[0];
+ prwskeylen = 16;
+ }
+
+ iv = pframe + prxattrib->hdrlen;
+ payload = pframe + prxattrib->iv_len + prxattrib->hdrlen;
+ length = skb->len - prxattrib->hdrlen - prxattrib->iv_len;
+
+ GET_TKIP_PN(iv, dot11txpn);
+
+ pnl = (u16)(dot11txpn.val);
+ pnh = (u32)(dot11txpn.val>>16);
+
+ phase1((u16 *)&ttkey[0], prwskey, &prxattrib->ta[0], pnh);
+ phase2(&rc4key[0], prwskey, (unsigned short *)&ttkey[0], pnl);
+
+ /* 4 decrypt payload include icv */
+ arcfour_init(&mycontext, rc4key, 16);
+ arcfour_encrypt(&mycontext, payload, payload, length);
+
+ actual_crc = le32_to_cpu(getcrc32(payload, length - 4));
+ expected_crc = le32_to_cpu(get_unaligned_le32(&payload[length - 4]));
+
+ if (actual_crc != expected_crc) {
+ RT_TRACE(_module_rtl871x_security_c_, _drv_err_,
+ "%s:icv CRC mismatch: "
+ "actual: %08x, expected: %08x\n",
+ __func__, actual_crc, expected_crc);
+ res = _FAIL;
+ }
+
+exit:
+ return res;
+}
+
+/* 3 ===== AES related ===== */
+
+#define MAX_MSG_SIZE 2048
+/*****************************/
+/******** SBOX Table *********/
+/*****************************/
+
+static u8 sbox_table[256] = {
+ 0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5,
+ 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76,
+ 0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0,
+ 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0,
+ 0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc,
+ 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15,
+ 0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a,
+ 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75,
+ 0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0,
+ 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84,
+ 0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b,
+ 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf,
+ 0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85,
+ 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8,
+ 0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5,
+ 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2,
+ 0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17,
+ 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73,
+ 0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88,
+ 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb,
+ 0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c,
+ 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79,
+ 0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9,
+ 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08,
+ 0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6,
+ 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a,
+ 0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e,
+ 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e,
+ 0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94,
+ 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf,
+ 0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68,
+ 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16
+};
+
+/*****************************/
+/**** Function Prototypes ****/
+/*****************************/
+
+static void construct_mic_header2(u8 *mic_header2, u8 *mpdu, int a4_exists,
+ int qc_exists);
+
+static void xor_128(u8 *a, u8 *b, u8 *out)
+{
+ int i;
+
+ for (i = 0; i < 16; i++)
+ out[i] = a[i] ^ b[i];
+}
+
+static void xor_32(u8 *a, u8 *b, u8 *out)
+{
+ int i;
+
+ for (i = 0; i < 4; i++)
+ out[i] = a[i] ^ b[i];
+}
+
+static u8 sbox(u8 a)
+{
+ return sbox_table[(int)a];
+}
+
+static void next_key(u8 *key, int round)
+{
+ u8 rcon;
+ u8 sbox_key[4];
+ u8 rcon_table[12] = {
+ 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80,
+ 0x1b, 0x36, 0x36, 0x36
+ };
+
+ sbox_key[0] = sbox(key[13]);
+ sbox_key[1] = sbox(key[14]);
+ sbox_key[2] = sbox(key[15]);
+ sbox_key[3] = sbox(key[12]);
+
+ rcon = rcon_table[round];
+
+ xor_32(&key[0], sbox_key, &key[0]);
+ key[0] = key[0] ^ rcon;
+
+ xor_32(&key[4], &key[0], &key[4]);
+ xor_32(&key[8], &key[4], &key[8]);
+ xor_32(&key[12], &key[8], &key[12]);
+
+}
+
+static void byte_sub(u8 *in, u8 *out)
+{
+ int i;
+
+ for (i = 0; i < 16; i++)
+ out[i] = sbox(in[i]);
+}
+
+static void shift_row(u8 *in, u8 *out)
+{
+
+ out[0] = in[0];
+ out[1] = in[5];
+ out[2] = in[10];
+ out[3] = in[15];
+ out[4] = in[4];
+ out[5] = in[9];
+ out[6] = in[14];
+ out[7] = in[3];
+ out[8] = in[8];
+ out[9] = in[13];
+ out[10] = in[2];
+ out[11] = in[7];
+ out[12] = in[12];
+ out[13] = in[1];
+ out[14] = in[6];
+ out[15] = in[11];
+
+}
+
+static void mix_column(u8 *in, u8 *out)
+{
+ int i;
+ u8 add1b[4];
+ u8 add1bf7[4];
+ u8 rotl[4];
+ u8 swap_halfs[4];
+ u8 andf7[4];
+ u8 rotr[4];
+ u8 temp[4];
+ u8 tempb[4];
+
+ for (i = 0; i < 4; i++) {
+ if ((in[i] & 0x80) == 0x80)
+ add1b[i] = 0x1b;
+ else
+ add1b[i] = 0x00;
+ }
+
+ swap_halfs[0] = in[2]; /* Swap halfs */
+ swap_halfs[1] = in[3];
+ swap_halfs[2] = in[0];
+ swap_halfs[3] = in[1];
+
+ rotl[0] = in[3]; /* Rotate left 8 bits */
+ rotl[1] = in[0];
+ rotl[2] = in[1];
+ rotl[3] = in[2];
+
+ andf7[0] = in[0] & 0x7f;
+ andf7[1] = in[1] & 0x7f;
+ andf7[2] = in[2] & 0x7f;
+ andf7[3] = in[3] & 0x7f;
+
+ for (i = 3; i > 0; i--) { /* logical shift left 1 bit */
+ andf7[i] = andf7[i] << 1;
+ if ((andf7[i - 1] & 0x80) == 0x80)
+ andf7[i] = (andf7[i] | 0x01);
+ }
+ andf7[0] = andf7[0] << 1;
+ andf7[0] = andf7[0] & 0xfe;
+
+ xor_32(add1b, andf7, add1bf7);
+
+ xor_32(in, add1bf7, rotr);
+
+ temp[0] = rotr[0]; /* Rotate right 8 bits */
+ rotr[0] = rotr[1];
+ rotr[1] = rotr[2];
+ rotr[2] = rotr[3];
+ rotr[3] = temp[0];
+
+ xor_32(add1bf7, rotr, temp);
+ xor_32(swap_halfs, rotl, tempb);
+ xor_32(temp, tempb, out);
+
+}
+
+static void aes128k128d(u8 *key, u8 *data, u8 *ciphertext)
+{
+ int round;
+ int i;
+ u8 intermediatea[16];
+ u8 intermediateb[16];
+ u8 round_key[16];
+
+ for (i = 0; i < 16; i++)
+ round_key[i] = key[i];
+
+ for (round = 0; round < 11; round++) {
+ if (round == 0) {
+ xor_128(round_key, data, ciphertext);
+ next_key(round_key, round);
+ } else if (round == 10) {
+ byte_sub(ciphertext, intermediatea);
+ shift_row(intermediatea, intermediateb);
+ xor_128(intermediateb, round_key, ciphertext);
+ } else { /* 1 - 9 */
+ byte_sub(ciphertext, intermediatea);
+ shift_row(intermediatea, intermediateb);
+ mix_column(&intermediateb[0], &intermediatea[0]);
+ mix_column(&intermediateb[4], &intermediatea[4]);
+ mix_column(&intermediateb[8], &intermediatea[8]);
+ mix_column(&intermediateb[12], &intermediatea[12]);
+ xor_128(intermediatea, round_key, ciphertext);
+ next_key(round_key, round);
+ }
+ }
+
+}
+
+/************************************************/
+/* construct_mic_iv() */
+/* Builds the MIC IV from header fields and PN */
+/************************************************/
+static void construct_mic_iv(u8 *mic_iv, int qc_exists, int a4_exists, u8 *mpdu,
+ uint payload_length, u8 *pn_vector)
+{
+ int i;
+
+ mic_iv[0] = 0x59;
+ if (qc_exists && a4_exists)
+ mic_iv[1] = mpdu[30] & 0x0f; /* QoS_TC */
+ if (qc_exists && !a4_exists)
+ mic_iv[1] = mpdu[24] & 0x0f; /* mute bits 7-4 */
+ if (!qc_exists)
+ mic_iv[1] = 0x00;
+ for (i = 2; i < 8; i++)
+ mic_iv[i] = mpdu[i + 8]; /* mic_iv[2:7] = A2[0:5] = mpdu[10:15] */
+ for (i = 8; i < 14; i++)
+ mic_iv[i] = pn_vector[13 - i]; /* mic_iv[8:13] = PN[5:0] */
+ mic_iv[14] = (unsigned char)(payload_length / 256);
+ mic_iv[15] = (unsigned char)(payload_length % 256);
+}
+
+/************************************************/
+/* construct_mic_header1() */
+/* Builds the first MIC header block from */
+/* header fields. */
+/************************************************/
+static void construct_mic_header1(u8 *mic_header1, int header_length, u8 *mpdu)
+{
+ mic_header1[0] = (u8)((header_length - 2) / 256);
+ mic_header1[1] = (u8)((header_length - 2) % 256);
+ mic_header1[2] = mpdu[0] & 0xcf; /* Mute CF poll & CF ack bits */
+ mic_header1[3] = mpdu[1] & 0xc7; /* Mute retry, more data and pwr mgt bits */
+ mic_header1[4] = mpdu[4]; /* A1 */
+ mic_header1[5] = mpdu[5];
+ mic_header1[6] = mpdu[6];
+ mic_header1[7] = mpdu[7];
+ mic_header1[8] = mpdu[8];
+ mic_header1[9] = mpdu[9];
+ mic_header1[10] = mpdu[10]; /* A2 */
+ mic_header1[11] = mpdu[11];
+ mic_header1[12] = mpdu[12];
+ mic_header1[13] = mpdu[13];
+ mic_header1[14] = mpdu[14];
+ mic_header1[15] = mpdu[15];
+
+}
+
+/************************************************/
+/* construct_mic_header2() */
+/* Builds the last MIC header block from */
+/* header fields. */
+/************************************************/
+static void construct_mic_header2(u8 *mic_header2, u8 *mpdu, int a4_exists,
+ int qc_exists)
+{
+ int i;
+
+ for (i = 0; i < 16; i++)
+ mic_header2[i] = 0x00;
+
+ mic_header2[0] = mpdu[16]; /* A3 */
+ mic_header2[1] = mpdu[17];
+ mic_header2[2] = mpdu[18];
+ mic_header2[3] = mpdu[19];
+ mic_header2[4] = mpdu[20];
+ mic_header2[5] = mpdu[21];
+
+ mic_header2[6] = 0x00;
+ mic_header2[7] = 0x00; /* mpdu[23]; */
+
+ if (!qc_exists && a4_exists) {
+ for (i = 0; i < 6; i++)
+ mic_header2[8+i] = mpdu[24+i]; /* A4 */
+ }
+
+ if (qc_exists && !a4_exists) {
+ mic_header2[8] = mpdu[24] & 0x0f; /* mute bits 15 - 4 */
+ mic_header2[9] = mpdu[25] & 0x00;
+ }
+
+ if (qc_exists && a4_exists) {
+ for (i = 0; i < 6; i++)
+ mic_header2[8+i] = mpdu[24+i]; /* A4 */
+
+ mic_header2[14] = mpdu[30] & 0x0f;
+ mic_header2[15] = mpdu[31] & 0x00;
+ }
+
+}
+
+/************************************************/
+/* construct_mic_header2() */
+/* Builds the last MIC header block from */
+/* header fields. */
+/************************************************/
+static void construct_ctr_preload(u8 *ctr_preload, int a4_exists, int qc_exists,
+ u8 *mpdu, u8 *pn_vector, int c)
+{
+ int i = 0;
+
+ for (i = 0; i < 16; i++)
+ ctr_preload[i] = 0x00;
+
+ i = 0;
+
+ ctr_preload[0] = 0x01; /* flag */
+ if (qc_exists && a4_exists)
+ ctr_preload[1] = mpdu[30] & 0x0f; /* QoC_Control */
+ if (qc_exists && !a4_exists)
+ ctr_preload[1] = mpdu[24] & 0x0f;
+
+ for (i = 2; i < 8; i++)
+ ctr_preload[i] = mpdu[i + 8]; /* ctr_preload[2:7] = A2[0:5] = mpdu[10:15] */
+ for (i = 8; i < 14; i++)
+ ctr_preload[i] = pn_vector[13 - i]; /* ctr_preload[8:13] = PN[5:0] */
+ ctr_preload[14] = (unsigned char) (c / 256); /* Ctr */
+ ctr_preload[15] = (unsigned char) (c % 256);
+
+}
+
+/************************************/
+/* bitwise_xor() */
+/* A 128 bit, bitwise exclusive or */
+/************************************/
+static void bitwise_xor(u8 *ina, u8 *inb, u8 *out)
+{
+ int i;
+
+ for (i = 0; i < 16; i++)
+ out[i] = ina[i] ^ inb[i];
+}
+
+static int aes_cipher(u8 *key, uint hdrlen, u8 *pframe, uint plen)
+{
+ uint qc_exists, a4_exists, i, j, payload_remainder,
+ num_blocks, payload_index;
+ u8 pn_vector[6];
+ u8 mic_iv[16];
+ u8 mic_header1[16];
+ u8 mic_header2[16];
+ u8 ctr_preload[16];
+ /* Intermediate Buffers */
+ u8 chain_buffer[16];
+ u8 aes_out[16];
+ u8 padded_buffer[16];
+ u8 mic[8];
+ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)pframe;
+ u16 frsubtype = le16_to_cpu(hdr->frame_control) & IEEE80211_FCTL_STYPE;
+
+ memset((void *)mic_iv, 0, 16);
+ memset((void *)mic_header1, 0, 16);
+ memset((void *)mic_header2, 0, 16);
+ memset((void *)ctr_preload, 0, 16);
+ memset((void *)chain_buffer, 0, 16);
+ memset((void *)aes_out, 0, 16);
+ memset((void *)padded_buffer, 0, 16);
+
+ if ((hdrlen == sizeof(struct ieee80211_hdr_3addr) ||
+ (hdrlen == sizeof(struct ieee80211_qos_hdr))))
+ a4_exists = 0;
+ else
+ a4_exists = 1;
+
+ if (ieee80211_is_data(hdr->frame_control)) {
+ if ((frsubtype == IEEE80211_STYPE_DATA_CFACK) ||
+ (frsubtype == IEEE80211_STYPE_DATA_CFPOLL) ||
+ (frsubtype == IEEE80211_STYPE_DATA_CFACKPOLL)) {
+ qc_exists = 1;
+ if (hdrlen != sizeof(struct ieee80211_qos_hdr))
+ hdrlen += 2;
+ } else if ((frsubtype == IEEE80211_STYPE_QOS_DATA) ||
+ (frsubtype == IEEE80211_STYPE_QOS_DATA_CFACK) ||
+ (frsubtype == IEEE80211_STYPE_QOS_DATA_CFPOLL) ||
+ (frsubtype == IEEE80211_STYPE_QOS_DATA_CFACKPOLL)) {
+ if (hdrlen != sizeof(struct ieee80211_qos_hdr))
+ hdrlen += 2;
+ qc_exists = 1;
+ } else {
+ qc_exists = 0;
+ }
+ } else {
+ qc_exists = 0;
+ }
+ pn_vector[0] = pframe[hdrlen];
+ pn_vector[1] = pframe[hdrlen + 1];
+ pn_vector[2] = pframe[hdrlen + 4];
+ pn_vector[3] = pframe[hdrlen + 5];
+ pn_vector[4] = pframe[hdrlen + 6];
+ pn_vector[5] = pframe[hdrlen + 7];
+
+ construct_mic_iv(mic_iv, qc_exists, a4_exists, pframe, plen, pn_vector);
+
+ construct_mic_header1(mic_header1, hdrlen, pframe);
+ construct_mic_header2(mic_header2, pframe, a4_exists, qc_exists);
+
+ payload_remainder = plen % 16;
+ num_blocks = plen / 16;
+
+ /* Find start of payload */
+ payload_index = hdrlen + 8;
+
+ /* Calculate MIC */
+ aes128k128d(key, mic_iv, aes_out);
+ bitwise_xor(aes_out, mic_header1, chain_buffer);
+ aes128k128d(key, chain_buffer, aes_out);
+ bitwise_xor(aes_out, mic_header2, chain_buffer);
+ aes128k128d(key, chain_buffer, aes_out);
+
+ for (i = 0; i < num_blocks; i++) {
+ bitwise_xor(aes_out, &pframe[payload_index], chain_buffer);
+
+ payload_index += 16;
+ aes128k128d(key, chain_buffer, aes_out);
+ }
+
+ /* Add on the final payload block if it needs padding */
+ if (payload_remainder > 0) {
+ for (j = 0; j < 16; j++)
+ padded_buffer[j] = 0x00;
+ for (j = 0; j < payload_remainder; j++)
+ padded_buffer[j] = pframe[payload_index++];
+ bitwise_xor(aes_out, padded_buffer, chain_buffer);
+ aes128k128d(key, chain_buffer, aes_out);
+ }
+
+ for (j = 0; j < 8; j++)
+ mic[j] = aes_out[j];
+
+ /* Insert MIC into payload */
+ for (j = 0; j < 8; j++)
+ pframe[payload_index + j] = mic[j];
+
+ payload_index = hdrlen + 8;
+ for (i = 0; i < num_blocks; i++) {
+ construct_ctr_preload(ctr_preload, a4_exists, qc_exists,
+ pframe, pn_vector, i + 1);
+ aes128k128d(key, ctr_preload, aes_out);
+ bitwise_xor(aes_out, &pframe[payload_index], chain_buffer);
+ for (j = 0; j < 16; j++)
+ pframe[payload_index++] = chain_buffer[j];
+ }
+
+ if (payload_remainder > 0) {
+ /* If there is a short final block, then pad it,
+ * encrypt it and copy the unpadded part back
+ */
+ construct_ctr_preload(ctr_preload, a4_exists, qc_exists, pframe,
+ pn_vector, num_blocks + 1);
+
+ for (j = 0; j < 16; j++)
+ padded_buffer[j] = 0x00;
+ for (j = 0; j < payload_remainder; j++)
+ padded_buffer[j] = pframe[payload_index + j];
+ aes128k128d(key, ctr_preload, aes_out);
+ bitwise_xor(aes_out, padded_buffer, chain_buffer);
+ for (j = 0; j < payload_remainder; j++)
+ pframe[payload_index++] = chain_buffer[j];
+ }
+
+ /* Encrypt the MIC */
+ construct_ctr_preload(ctr_preload, a4_exists, qc_exists, pframe,
+ pn_vector, 0);
+
+ for (j = 0; j < 16; j++)
+ padded_buffer[j] = 0x00;
+ for (j = 0; j < 8; j++)
+ padded_buffer[j] = pframe[j + hdrlen + 8 + plen];
+
+ aes128k128d(key, ctr_preload, aes_out);
+ bitwise_xor(aes_out, padded_buffer, chain_buffer);
+ for (j = 0; j < 8; j++)
+ pframe[payload_index++] = chain_buffer[j];
+
+ return _SUCCESS;
+}
+
+int rtw_aes_encrypt23a(struct rtw_adapter *padapter,
+ struct xmit_frame *pxmitframe)
+{ /* exclude ICV */
+ /* Intermediate Buffers */
+ int curfragnum, length;
+ u32 prwskeylen;
+ u8 *pframe, *prwskey;
+ u8 hw_hdr_offset = 0;
+ struct sta_info *stainfo;
+ struct pkt_attrib *pattrib = &pxmitframe->attrib;
+ struct security_priv *psecuritypriv = &padapter->securitypriv;
+ struct xmit_priv *pxmitpriv = &padapter->xmitpriv;
+ int res = _SUCCESS;
+
+ if (!pxmitframe->buf_addr)
+ return _FAIL;
+
+ hw_hdr_offset = TXDESC_OFFSET;
+
+ pframe = pxmitframe->buf_addr + hw_hdr_offset;
+
+ /* 4 start to encrypt each fragment */
+ if (pattrib->encrypt != WLAN_CIPHER_SUITE_CCMP)
+ return _FAIL;
+
+ if (pattrib->psta) {
+ stainfo = pattrib->psta;
+ } else {
+ DBG_8723A("%s, call rtw_get_stainfo23a()\n", __func__);
+ stainfo = rtw_get_stainfo23a(&padapter->stapriv, &pattrib->ra[0]);
+ }
+
+ if (!stainfo) {
+ RT_TRACE(_module_rtl871x_security_c_, _drv_err_,
+ "%s: stainfo == NULL!!!\n", __func__);
+ DBG_8723A("%s, psta == NUL\n", __func__);
+ res = _FAIL;
+ goto out;
+ }
+ if (!(stainfo->state & _FW_LINKED)) {
+ DBG_8723A("%s, psta->state(0x%x) != _FW_LINKED\n",
+ __func__, stainfo->state);
+ return _FAIL;
+ }
+ RT_TRACE(_module_rtl871x_security_c_, _drv_err_,
+ "%s: stainfo!= NULL!!!\n", __func__);
+
+ if (is_multicast_ether_addr(pattrib->ra))
+ prwskey = psecuritypriv->dot118021XGrpKey[psecuritypriv->dot118021XGrpKeyid].skey;
+ else
+ prwskey = &stainfo->dot118021x_UncstKey.skey[0];
+
+ prwskeylen = 16;
+
+ for (curfragnum = 0; curfragnum < pattrib->nr_frags; curfragnum++) {
+ /* 4 the last fragment */
+ if ((curfragnum + 1) == pattrib->nr_frags) {
+ length = pattrib->last_txcmdsz -
+ pattrib->hdrlen-pattrib->iv_len -
+ pattrib->icv_len;
+
+ aes_cipher(prwskey, pattrib->hdrlen, pframe, length);
+ } else {
+ length = pxmitpriv->frag_len-pattrib->hdrlen -
+ pattrib->iv_len - pattrib->icv_len;
+
+ aes_cipher(prwskey, pattrib->hdrlen, pframe, length);
+ pframe += pxmitpriv->frag_len;
+ pframe = PTR_ALIGN(pframe, 4);
+ }
+ }
+out:
+ return res;
+}
+
+static int aes_decipher(u8 *key, uint hdrlen, u8 *pframe, uint plen)
+{
+ static u8 message[MAX_MSG_SIZE];
+ uint qc_exists, a4_exists, i, j, payload_remainder,
+ num_blocks, payload_index;
+ int res = _SUCCESS;
+ u8 pn_vector[6];
+ u8 mic_iv[16];
+ u8 mic_header1[16];
+ u8 mic_header2[16];
+ u8 ctr_preload[16];
+ /* Intermediate Buffers */
+ u8 chain_buffer[16];
+ u8 aes_out[16];
+ u8 padded_buffer[16];
+ u8 mic[8];
+ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)pframe;
+ u16 frsubtype = le16_to_cpu(hdr->frame_control) & IEEE80211_FCTL_STYPE;
+
+ memset((void *)mic_iv, 0, 16);
+ memset((void *)mic_header1, 0, 16);
+ memset((void *)mic_header2, 0, 16);
+ memset((void *)ctr_preload, 0, 16);
+ memset((void *)chain_buffer, 0, 16);
+ memset((void *)aes_out, 0, 16);
+ memset((void *)padded_buffer, 0, 16);
+
+ /* start to decrypt the payload */
+
+ num_blocks = (plen - 8) / 16; /* plen including llc, payload_length and mic) */
+
+ payload_remainder = (plen - 8) % 16;
+
+ pn_vector[0] = pframe[hdrlen];
+ pn_vector[1] = pframe[hdrlen + 1];
+ pn_vector[2] = pframe[hdrlen + 4];
+ pn_vector[3] = pframe[hdrlen + 5];
+ pn_vector[4] = pframe[hdrlen + 6];
+ pn_vector[5] = pframe[hdrlen + 7];
+
+ if ((hdrlen == sizeof(struct ieee80211_hdr_3addr) ||
+ (hdrlen == sizeof(struct ieee80211_qos_hdr))))
+ a4_exists = 0;
+ else
+ a4_exists = 1;
+
+ if (ieee80211_is_data(hdr->frame_control)) {
+ if ((frsubtype == IEEE80211_STYPE_DATA_CFACK) ||
+ (frsubtype == IEEE80211_STYPE_DATA_CFPOLL) ||
+ (frsubtype == IEEE80211_STYPE_DATA_CFACKPOLL)) {
+ qc_exists = 1;
+ if (hdrlen != sizeof(struct ieee80211_hdr_3addr))
+ hdrlen += 2;
+ } else if ((frsubtype == IEEE80211_STYPE_QOS_DATA) ||
+ (frsubtype == IEEE80211_STYPE_QOS_DATA_CFACK) ||
+ (frsubtype == IEEE80211_STYPE_QOS_DATA_CFPOLL) ||
+ (frsubtype == IEEE80211_STYPE_QOS_DATA_CFACKPOLL)) {
+ if (hdrlen != sizeof(struct ieee80211_hdr_3addr))
+ hdrlen += 2;
+ qc_exists = 1;
+ } else {
+ qc_exists = 0;
+ }
+ } else {
+ qc_exists = 0;
+ }
+
+ /* now, decrypt pframe with hdrlen offset and plen long */
+
+ payload_index = hdrlen + 8; /* 8 is for extiv */
+
+ for (i = 0; i < num_blocks; i++) {
+ construct_ctr_preload(ctr_preload, a4_exists, qc_exists,
+ pframe, pn_vector, i + 1);
+
+ aes128k128d(key, ctr_preload, aes_out);
+ bitwise_xor(aes_out, &pframe[payload_index], chain_buffer);
+
+ for (j = 0; j < 16; j++)
+ pframe[payload_index++] = chain_buffer[j];
+ }
+
+ if (payload_remainder > 0) {
+ /* If there is a short final block, then pad it,
+ * encrypt it and copy the unpadded part back
+ */
+ construct_ctr_preload(ctr_preload, a4_exists, qc_exists, pframe,
+ pn_vector, num_blocks + 1);
+
+ for (j = 0; j < 16; j++)
+ padded_buffer[j] = 0x00;
+ for (j = 0; j < payload_remainder; j++)
+ padded_buffer[j] = pframe[payload_index + j];
+ aes128k128d(key, ctr_preload, aes_out);
+ bitwise_xor(aes_out, padded_buffer, chain_buffer);
+ for (j = 0; j < payload_remainder; j++)
+ pframe[payload_index++] = chain_buffer[j];
+ }
+
+ /* start to calculate the mic */
+ if ((hdrlen + plen + 8) <= MAX_MSG_SIZE)
+ memcpy(message, pframe, (hdrlen + plen + 8)); /* 8 is for ext iv len */
+
+ pn_vector[0] = pframe[hdrlen];
+ pn_vector[1] = pframe[hdrlen + 1];
+ pn_vector[2] = pframe[hdrlen + 4];
+ pn_vector[3] = pframe[hdrlen + 5];
+ pn_vector[4] = pframe[hdrlen + 6];
+ pn_vector[5] = pframe[hdrlen + 7];
+
+ construct_mic_iv(mic_iv, qc_exists, a4_exists, message,
+ plen - 8, pn_vector);
+
+ construct_mic_header1(mic_header1, hdrlen, message);
+ construct_mic_header2(mic_header2, message, a4_exists, qc_exists);
+
+ payload_remainder = (plen - 8) % 16;
+ num_blocks = (plen - 8) / 16;
+
+ /* Find start of payload */
+ payload_index = hdrlen + 8;
+
+ /* Calculate MIC */
+ aes128k128d(key, mic_iv, aes_out);
+ bitwise_xor(aes_out, mic_header1, chain_buffer);
+ aes128k128d(key, chain_buffer, aes_out);
+ bitwise_xor(aes_out, mic_header2, chain_buffer);
+ aes128k128d(key, chain_buffer, aes_out);
+
+ for (i = 0; i < num_blocks; i++) {
+ bitwise_xor(aes_out, &message[payload_index], chain_buffer);
+
+ payload_index += 16;
+ aes128k128d(key, chain_buffer, aes_out);
+ }
+
+ /* Add on the final payload block if it needs padding */
+ if (payload_remainder > 0) {
+ for (j = 0; j < 16; j++)
+ padded_buffer[j] = 0x00;
+ for (j = 0; j < payload_remainder; j++)
+ padded_buffer[j] = message[payload_index++];
+ bitwise_xor(aes_out, padded_buffer, chain_buffer);
+ aes128k128d(key, chain_buffer, aes_out);
+ }
+
+ for (j = 0 ; j < 8; j++)
+ mic[j] = aes_out[j];
+
+ /* Insert MIC into payload */
+ for (j = 0; j < 8; j++)
+ message[payload_index + j] = mic[j];
+
+ payload_index = hdrlen + 8;
+ for (i = 0; i < num_blocks; i++) {
+ construct_ctr_preload(ctr_preload, a4_exists, qc_exists,
+ message, pn_vector, i + 1);
+ aes128k128d(key, ctr_preload, aes_out);
+ bitwise_xor(aes_out, &message[payload_index], chain_buffer);
+ for (j = 0; j < 16; j++)
+ message[payload_index++] = chain_buffer[j];
+ }
+
+ if (payload_remainder > 0) {
+ /* If there is a short final block, then pad it,
+ * encrypt it and copy the unpadded part back
+ */
+ construct_ctr_preload(ctr_preload, a4_exists, qc_exists,
+ message, pn_vector, num_blocks + 1);
+
+ for (j = 0; j < 16; j++)
+ padded_buffer[j] = 0x00;
+ for (j = 0; j < payload_remainder; j++)
+ padded_buffer[j] = message[payload_index + j];
+ aes128k128d(key, ctr_preload, aes_out);
+ bitwise_xor(aes_out, padded_buffer, chain_buffer);
+ for (j = 0; j < payload_remainder; j++)
+ message[payload_index++] = chain_buffer[j];
+ }
+
+ /* Encrypt the MIC */
+ construct_ctr_preload(ctr_preload, a4_exists, qc_exists, message,
+ pn_vector, 0);
+
+ for (j = 0; j < 16; j++)
+ padded_buffer[j] = 0x00;
+ for (j = 0; j < 8; j++)
+ padded_buffer[j] = message[j + hdrlen + 8 + plen - 8];
+
+ aes128k128d(key, ctr_preload, aes_out);
+ bitwise_xor(aes_out, padded_buffer, chain_buffer);
+ for (j = 0; j < 8; j++)
+ message[payload_index++] = chain_buffer[j];
+
+ /* compare the mic */
+ for (i = 0; i < 8; i++) {
+ if (pframe[hdrlen + 8 + plen - 8 + i] != message[hdrlen + 8 + plen - 8 + i]) {
+ RT_TRACE(_module_rtl871x_security_c_, _drv_err_,
+ "%s:mic check error mic[%d]: pframe(%x) != message(%x)\n",
+ __func__, i,
+ pframe[hdrlen + 8 + plen - 8 + i],
+ message[hdrlen + 8 + plen - 8 + i]);
+ DBG_8723A("%s:mic check error mic[%d]: pframe(%x) != message(%x)\n",
+ __func__, i,
+ pframe[hdrlen + 8 + plen - 8 + i],
+ message[hdrlen + 8 + plen - 8 + i]);
+ res = _FAIL;
+ }
+ }
+ return res;
+}
+
+int rtw_aes_decrypt23a(struct rtw_adapter *padapter,
+ struct recv_frame *precvframe)
+{ /* exclude ICV */
+ struct sta_info *stainfo;
+ struct rx_pkt_attrib *prxattrib = &precvframe->attrib;
+ struct security_priv *psecuritypriv = &padapter->securitypriv;
+ struct sk_buff *skb = precvframe->pkt;
+ int length;
+ u8 *pframe, *prwskey;
+ int res = _SUCCESS;
+
+ pframe = skb->data;
+ /* 4 start to encrypt each fragment */
+ if (prxattrib->encrypt != WLAN_CIPHER_SUITE_CCMP)
+ return _FAIL;
+
+ stainfo = rtw_get_stainfo23a(&padapter->stapriv, &prxattrib->ta[0]);
+ if (!stainfo) {
+ RT_TRACE(_module_rtl871x_security_c_, _drv_err_,
+ "%s: stainfo == NULL!!!\n", __func__);
+ res = _FAIL;
+ goto exit;
+ }
+
+ RT_TRACE(_module_rtl871x_security_c_, _drv_err_,
+ "%s: stainfo!= NULL!!!\n", __func__);
+
+ if (is_multicast_ether_addr(prxattrib->ra)) {
+ /* in concurrent we should use sw decrypt in
+ * group key, so we remove this message
+ */
+ if (!psecuritypriv->binstallGrpkey) {
+ res = _FAIL;
+ DBG_8723A("%s:rx bc/mc packets, but didn't install "
+ "group key!!!!!!!!!!\n", __func__);
+ goto exit;
+ }
+ prwskey = psecuritypriv->dot118021XGrpKey[prxattrib->key_index].skey;
+ if (psecuritypriv->dot118021XGrpKeyid != prxattrib->key_index) {
+ DBG_8723A("not match packet_index =%d, install_index ="
+ "%d\n", prxattrib->key_index,
+ psecuritypriv->dot118021XGrpKeyid);
+ res = _FAIL;
+ goto exit;
+ }
+ } else {
+ prwskey = &stainfo->dot118021x_UncstKey.skey[0];
+ }
+
+ length = skb->len - prxattrib->hdrlen - prxattrib->iv_len;
+
+ res = aes_decipher(prwskey, prxattrib->hdrlen, pframe, length);
+exit:
+ return res;
+}
+
+void rtw_use_tkipkey_handler23a(void *FunctionContext)
+{
+ struct rtw_adapter *padapter = (struct rtw_adapter *)FunctionContext;
+
+ RT_TRACE(_module_rtl871x_security_c_, _drv_err_,
+ "^^^%s ^^^\n", __func__);
+ padapter->securitypriv.busetkipkey = 1;
+ RT_TRACE(_module_rtl871x_security_c_, _drv_err_,
+ "^^^%s padapter->securitypriv.busetkipkey =%d^^^\n",
+ __func__, padapter->securitypriv.busetkipkey);
+}
diff --git a/drivers/staging/rtl8723au/core/rtw_sreset.c b/drivers/staging/rtl8723au/core/rtw_sreset.c
new file mode 100644
index 000000000..29a29d92a
--- /dev/null
+++ b/drivers/staging/rtl8723au/core/rtw_sreset.c
@@ -0,0 +1,214 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2012 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * 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.
+ *
+ ******************************************************************************/
+
+#include <rtw_sreset.h>
+#include <usb_ops_linux.h>
+
+void rtw_sreset_init(struct rtw_adapter *padapter)
+{
+ struct hal_data_8723a *pHalData = GET_HAL_DATA(padapter);
+ struct sreset_priv *psrtpriv = &pHalData->srestpriv;
+
+ mutex_init(&psrtpriv->silentreset_mutex);
+ psrtpriv->silent_reset_inprogress = false;
+ psrtpriv->last_tx_time = 0;
+ psrtpriv->last_tx_complete_time = 0;
+}
+
+void rtw_sreset_reset_value(struct rtw_adapter *padapter)
+{
+ struct hal_data_8723a *pHalData = GET_HAL_DATA(padapter);
+ struct sreset_priv *psrtpriv = &pHalData->srestpriv;
+
+ psrtpriv->silent_reset_inprogress = false;
+ psrtpriv->last_tx_time = 0;
+ psrtpriv->last_tx_complete_time = 0;
+}
+
+bool rtw_sreset_inprogress(struct rtw_adapter *padapter)
+{
+ struct rtw_adapter *primary_adapter = GET_PRIMARY_ADAPTER(padapter);
+ struct hal_data_8723a *pHalData = GET_HAL_DATA(primary_adapter);
+
+ return pHalData->srestpriv.silent_reset_inprogress;
+}
+
+static void sreset_restore_security_station(struct rtw_adapter *padapter)
+{
+ struct mlme_priv *mlmepriv = &padapter->mlmepriv;
+ struct sta_priv *pstapriv = &padapter->stapriv;
+ struct sta_info *psta;
+ struct mlme_ext_info *pmlmeinfo = &padapter->mlmeextpriv.mlmext_info;
+ u8 val8;
+
+ if (pmlmeinfo->auth_algo == dot11AuthAlgrthm_8021X)
+ val8 = 0xcc;
+ else
+ val8 = 0xcf;
+
+ rtl8723a_set_sec_cfg(padapter, val8);
+
+ if (padapter->securitypriv.dot11PrivacyAlgrthm ==
+ WLAN_CIPHER_SUITE_TKIP ||
+ padapter->securitypriv.dot11PrivacyAlgrthm ==
+ WLAN_CIPHER_SUITE_CCMP) {
+ psta = rtw_get_stainfo23a(pstapriv, get_bssid(mlmepriv));
+ if (psta == NULL) {
+ /* DEBUG_ERR(("Set wpa_set_encryption: Obtain Sta_info fail\n")); */
+ } else {
+ /* pairwise key */
+ rtw_setstakey_cmd23a(padapter, (unsigned char *)psta, true);
+ /* group key */
+ rtw_set_key23a(padapter,&padapter->securitypriv, padapter->securitypriv.dot118021XGrpKeyid, 0);
+ }
+ }
+}
+
+static void sreset_restore_network_station(struct rtw_adapter *padapter)
+{
+ struct mlme_priv *mlmepriv = &padapter->mlmepriv;
+ struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv;
+ struct mlme_ext_info *pmlmeinfo = &pmlmeext->mlmext_info;
+ u8 threshold;
+
+ rtw_setopmode_cmd23a(padapter, NL80211_IFTYPE_STATION);
+
+ /* TH = 1 => means that invalidate usb rx aggregation */
+ /* TH = 0 => means that validate usb rx aggregation, use init value. */
+ if (mlmepriv->htpriv.ht_option) {
+ if (padapter->registrypriv.wifi_spec == 1)
+ threshold = 1;
+ else
+ threshold = 0;
+ } else
+ threshold = 1;
+
+ rtl8723a_set_rxdma_agg_pg_th(padapter, threshold);
+
+ set_channel_bwmode23a(padapter, pmlmeext->cur_channel,
+ pmlmeext->cur_ch_offset, pmlmeext->cur_bwmode);
+
+ hw_var_set_bssid(padapter, pmlmeinfo->network.MacAddress);
+ hw_var_set_mlme_join(padapter, 0);
+
+ rtl8723a_set_media_status(padapter, pmlmeinfo->state & 0x3);
+
+ mlmeext_joinbss_event_callback23a(padapter, 1);
+ /* restore Sequence No. */
+ rtl8723au_write8(padapter, REG_NQOS_SEQ, padapter->xmitpriv.nqos_ssn);
+
+ sreset_restore_security_station(padapter);
+}
+
+static void sreset_restore_network_status(struct rtw_adapter *padapter)
+{
+ struct mlme_priv *mlmepriv = &padapter->mlmepriv;
+
+ if (check_fwstate(mlmepriv, WIFI_STATION_STATE)) {
+ DBG_8723A("%s(%s): fwstate:0x%08x - WIFI_STATION_STATE\n",
+ __func__, padapter->pnetdev->name,
+ get_fwstate(mlmepriv));
+ sreset_restore_network_station(padapter);
+#ifdef CONFIG_8723AU_AP_MODE
+ } else if (check_fwstate(mlmepriv, WIFI_AP_STATE)) {
+ DBG_8723A("%s(%s): fwstate:0x%08x - WIFI_AP_STATE\n",
+ __func__, padapter->pnetdev->name,
+ get_fwstate(mlmepriv));
+ rtw_ap_restore_network(padapter);
+#endif
+ } else if (check_fwstate(mlmepriv, WIFI_ADHOC_STATE)) {
+ DBG_8723A("%s(%s): fwstate:0x%08x - WIFI_ADHOC_STATE\n",
+ __func__, padapter->pnetdev->name,
+ get_fwstate(mlmepriv));
+ } else {
+ DBG_8723A("%s(%s): fwstate:0x%08x - ???\n", __func__,
+ padapter->pnetdev->name, get_fwstate(mlmepriv));
+ }
+}
+
+static void sreset_stop_adapter(struct rtw_adapter *padapter)
+{
+ struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
+ struct xmit_priv *pxmitpriv = &padapter->xmitpriv;
+
+ if (padapter == NULL)
+ return;
+
+ DBG_8723A("%s(%s)\n", __func__, padapter->pnetdev->name);
+
+ if (!rtw_netif_queue_stopped(padapter->pnetdev))
+ netif_tx_stop_all_queues(padapter->pnetdev);
+
+ rtw_cancel_all_timer23a(padapter);
+
+ /* TODO: OS and HCI independent */
+ tasklet_kill(&pxmitpriv->xmit_tasklet);
+
+ if (check_fwstate(pmlmepriv, _FW_UNDER_SURVEY))
+ rtw_scan_abort23a(padapter);
+
+ if (check_fwstate(pmlmepriv, _FW_UNDER_LINKING))
+ rtw23a_join_to_handler((unsigned long)padapter);
+}
+
+static void sreset_start_adapter(struct rtw_adapter *padapter)
+{
+ struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
+ struct xmit_priv *pxmitpriv = &padapter->xmitpriv;
+
+ if (padapter == NULL)
+ return;
+
+ DBG_8723A("%s(%s)\n", __func__, padapter->pnetdev->name);
+
+ if (check_fwstate(pmlmepriv, _FW_LINKED))
+ sreset_restore_network_status(padapter);
+
+ /* TODO: OS and HCI independent */
+ tasklet_hi_schedule(&pxmitpriv->xmit_tasklet);
+
+ mod_timer(&padapter->mlmepriv.dynamic_chk_timer,
+ jiffies + msecs_to_jiffies(2000));
+
+ if (rtw_netif_queue_stopped(padapter->pnetdev))
+ netif_tx_wake_all_queues(padapter->pnetdev);
+}
+
+void rtw_sreset_reset(struct rtw_adapter *active_adapter)
+{
+ struct rtw_adapter *padapter = GET_PRIMARY_ADAPTER(active_adapter);
+ struct hal_data_8723a *pHalData = GET_HAL_DATA(padapter);
+ struct sreset_priv *psrtpriv = &pHalData->srestpriv;
+ struct pwrctrl_priv *pwrpriv = &padapter->pwrctrlpriv;
+ unsigned long start = jiffies;
+
+ DBG_8723A("%s\n", __func__);
+
+ mutex_lock(&psrtpriv->silentreset_mutex);
+ psrtpriv->silent_reset_inprogress = true;
+ pwrpriv->change_rfpwrstate = rf_off;
+
+ sreset_stop_adapter(padapter);
+
+ ips_enter23a(padapter);
+ ips_leave23a(padapter);
+
+ sreset_start_adapter(padapter);
+ psrtpriv->silent_reset_inprogress = false;
+ mutex_unlock(&psrtpriv->silentreset_mutex);
+
+ DBG_8723A("%s done in %d ms\n", __func__,
+ jiffies_to_msecs(jiffies - start));
+}
diff --git a/drivers/staging/rtl8723au/core/rtw_sta_mgt.c b/drivers/staging/rtl8723au/core/rtw_sta_mgt.c
new file mode 100644
index 000000000..b06bff745
--- /dev/null
+++ b/drivers/staging/rtl8723au/core/rtw_sta_mgt.c
@@ -0,0 +1,451 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2011 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ ******************************************************************************/
+#define _RTW_STA_MGT_C_
+
+#include <osdep_service.h>
+#include <drv_types.h>
+#include <recv_osdep.h>
+#include <xmit_osdep.h>
+#include <mlme_osdep.h>
+#include <sta_info.h>
+#include <rtl8723a_hal.h>
+
+static const u8 bc_addr[ETH_ALEN] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
+
+static void _rtw_init_stainfo(struct sta_info *psta)
+{
+ memset((u8 *)psta, 0, sizeof(struct sta_info));
+ spin_lock_init(&psta->lock);
+ INIT_LIST_HEAD(&psta->list);
+ INIT_LIST_HEAD(&psta->hash_list);
+ _rtw_init_queue23a(&psta->sleep_q);
+ psta->sleepq_len = 0;
+ _rtw_init_sta_xmit_priv23a(&psta->sta_xmitpriv);
+ _rtw_init_sta_recv_priv23a(&psta->sta_recvpriv);
+#ifdef CONFIG_8723AU_AP_MODE
+ INIT_LIST_HEAD(&psta->asoc_list);
+ INIT_LIST_HEAD(&psta->auth_list);
+ psta->expire_to = 0;
+ psta->flags = 0;
+ psta->capability = 0;
+ psta->bpairwise_key_installed = false;
+ psta->nonerp_set = 0;
+ psta->no_short_slot_time_set = 0;
+ psta->no_short_preamble_set = 0;
+ psta->no_ht_gf_set = 0;
+ psta->no_ht_set = 0;
+ psta->ht_20mhz_set = 0;
+ psta->keep_alive_trycnt = 0;
+#endif /* CONFIG_8723AU_AP_MODE */
+}
+
+int _rtw_init_sta_priv23a(struct sta_priv *pstapriv)
+{
+ int i;
+
+ spin_lock_init(&pstapriv->sta_hash_lock);
+ pstapriv->asoc_sta_count = 0;
+ for (i = 0; i < NUM_STA; i++)
+ INIT_LIST_HEAD(&pstapriv->sta_hash[i]);
+
+#ifdef CONFIG_8723AU_AP_MODE
+ pstapriv->sta_dz_bitmap = 0;
+ pstapriv->tim_bitmap = 0;
+ INIT_LIST_HEAD(&pstapriv->asoc_list);
+ INIT_LIST_HEAD(&pstapriv->auth_list);
+ spin_lock_init(&pstapriv->asoc_list_lock);
+ spin_lock_init(&pstapriv->auth_list_lock);
+ pstapriv->asoc_list_cnt = 0;
+ pstapriv->auth_list_cnt = 0;
+ pstapriv->auth_to = 3; /* 3*2 = 6 sec */
+ pstapriv->assoc_to = 3;
+ /* pstapriv->expire_to = 900; 900*2 = 1800 sec = 30 min,
+ expire after no any traffic. */
+ /* pstapriv->expire_to = 30; 30*2 = 60 sec = 1 min,
+ expire after no any traffic. */
+ pstapriv->expire_to = 3; /* 3*2 = 6 sec */
+ pstapriv->max_num_sta = NUM_STA;
+#endif
+ return _SUCCESS;
+}
+
+int _rtw_free_sta_priv23a(struct sta_priv *pstapriv)
+{
+ struct list_head *phead, *plist, *ptmp;
+ struct sta_info *psta;
+ struct recv_reorder_ctrl *preorder_ctrl;
+ int index;
+
+ if (pstapriv) {
+ /* delete all reordering_ctrl_timer */
+ spin_lock_bh(&pstapriv->sta_hash_lock);
+ for (index = 0; index < NUM_STA; index++) {
+ phead = &pstapriv->sta_hash[index];
+
+ list_for_each_safe(plist, ptmp, phead) {
+ int i;
+
+ psta = container_of(plist, struct sta_info,
+ hash_list);
+ for (i = 0; i < 16 ; i++) {
+ preorder_ctrl = &psta->recvreorder_ctrl[i];
+ del_timer_sync(&preorder_ctrl->reordering_ctrl_timer);
+ }
+ }
+ }
+ spin_unlock_bh(&pstapriv->sta_hash_lock);
+ /*===============================*/
+ }
+ return _SUCCESS;
+}
+
+struct sta_info *
+rtw_alloc_stainfo23a(struct sta_priv *pstapriv, const u8 *hwaddr, gfp_t gfp)
+{
+ struct list_head *phash_list;
+ struct sta_info *psta;
+ struct recv_reorder_ctrl *preorder_ctrl;
+ s32 index;
+ int i = 0;
+ u16 wRxSeqInitialValue = 0xffff;
+
+ psta = kmalloc(sizeof(struct sta_info), gfp);
+ if (!psta)
+ return NULL;
+
+ spin_lock_bh(&pstapriv->sta_hash_lock);
+
+ _rtw_init_stainfo(psta);
+
+ psta->padapter = pstapriv->padapter;
+
+ ether_addr_copy(psta->hwaddr, hwaddr);
+
+ index = wifi_mac_hash(hwaddr);
+
+ RT_TRACE(_module_rtl871x_sta_mgt_c_, _drv_info_,
+ "rtw_alloc_stainfo23a: index = %x\n", index);
+ if (index >= NUM_STA) {
+ RT_TRACE(_module_rtl871x_sta_mgt_c_, _drv_err_,
+ "ERROR => rtw_alloc_stainfo23a: index >= NUM_STA\n");
+ psta = NULL;
+ goto exit;
+ }
+ phash_list = &pstapriv->sta_hash[index];
+
+ list_add_tail(&psta->hash_list, phash_list);
+
+ pstapriv->asoc_sta_count++;
+
+/* For the SMC router, the sequence number of first packet of WPS
+ handshake will be 0. */
+/* In this case, this packet will be dropped by recv_decache function
+ if we use the 0x00 as the default value for tid_rxseq variable. */
+/* So, we initialize the tid_rxseq variable as the 0xffff. */
+
+ for (i = 0; i < 16; i++)
+ memcpy(&psta->sta_recvpriv.rxcache.tid_rxseq[i],
+ &wRxSeqInitialValue, 2);
+
+ RT_TRACE(_module_rtl871x_sta_mgt_c_, _drv_info_,
+ "alloc number_%d stainfo with hwaddr = %pM\n",
+ pstapriv->asoc_sta_count, hwaddr);
+
+ init_addba_retry_timer23a(psta);
+
+ /* for A-MPDU Rx reordering buffer control */
+ for (i = 0; i < 16; i++) {
+ preorder_ctrl = &psta->recvreorder_ctrl[i];
+
+ preorder_ctrl->padapter = pstapriv->padapter;
+
+ preorder_ctrl->enable = false;
+
+ preorder_ctrl->indicate_seq = 0xffff;
+ preorder_ctrl->wend_b = 0xffff;
+ /* preorder_ctrl->wsize_b = (NR_RECVBUFF-2); */
+ preorder_ctrl->wsize_b = 64;/* 64; */
+
+ _rtw_init_queue23a(&preorder_ctrl->pending_recvframe_queue);
+
+ rtw_init_recv_timer23a(preorder_ctrl);
+ }
+ /* init for DM */
+ psta->rssi_stat.UndecoratedSmoothedPWDB = (-1);
+ psta->rssi_stat.UndecoratedSmoothedCCK = (-1);
+
+ /* init for the sequence number of received management frame */
+ psta->RxMgmtFrameSeqNum = 0xffff;
+exit:
+ spin_unlock_bh(&pstapriv->sta_hash_lock);
+ return psta;
+}
+
+/* using pstapriv->sta_hash_lock to protect */
+int rtw_free_stainfo23a(struct rtw_adapter *padapter, struct sta_info *psta)
+{
+ struct recv_reorder_ctrl *preorder_ctrl;
+ struct sta_xmit_priv *pstaxmitpriv;
+ struct xmit_priv *pxmitpriv = &padapter->xmitpriv;
+ struct sta_priv *pstapriv = &padapter->stapriv;
+ struct hw_xmit *phwxmit;
+ int i;
+
+ if (psta == NULL)
+ goto exit;
+
+ spin_lock_bh(&psta->lock);
+ psta->state &= ~_FW_LINKED;
+ spin_unlock_bh(&psta->lock);
+
+ pstaxmitpriv = &psta->sta_xmitpriv;
+
+ spin_lock_bh(&pxmitpriv->lock);
+
+ rtw_free_xmitframe_queue23a(pxmitpriv, &psta->sleep_q);
+ psta->sleepq_len = 0;
+
+ /* vo */
+ rtw_free_xmitframe_queue23a(pxmitpriv, &pstaxmitpriv->vo_q.sta_pending);
+ list_del_init(&pstaxmitpriv->vo_q.tx_pending);
+ phwxmit = pxmitpriv->hwxmits;
+ phwxmit->accnt -= pstaxmitpriv->vo_q.qcnt;
+ pstaxmitpriv->vo_q.qcnt = 0;
+
+ /* vi */
+ rtw_free_xmitframe_queue23a(pxmitpriv, &pstaxmitpriv->vi_q.sta_pending);
+ list_del_init(&pstaxmitpriv->vi_q.tx_pending);
+ phwxmit = pxmitpriv->hwxmits+1;
+ phwxmit->accnt -= pstaxmitpriv->vi_q.qcnt;
+ pstaxmitpriv->vi_q.qcnt = 0;
+
+ /* be */
+ rtw_free_xmitframe_queue23a(pxmitpriv, &pstaxmitpriv->be_q.sta_pending);
+ list_del_init(&pstaxmitpriv->be_q.tx_pending);
+ phwxmit = pxmitpriv->hwxmits+2;
+ phwxmit->accnt -= pstaxmitpriv->be_q.qcnt;
+ pstaxmitpriv->be_q.qcnt = 0;
+
+ /* bk */
+ rtw_free_xmitframe_queue23a(pxmitpriv, &pstaxmitpriv->bk_q.sta_pending);
+ list_del_init(&pstaxmitpriv->bk_q.tx_pending);
+ phwxmit = pxmitpriv->hwxmits+3;
+ phwxmit->accnt -= pstaxmitpriv->bk_q.qcnt;
+ pstaxmitpriv->bk_q.qcnt = 0;
+
+ spin_unlock_bh(&pxmitpriv->lock);
+
+ list_del_init(&psta->hash_list);
+ RT_TRACE(_module_rtl871x_sta_mgt_c_, _drv_err_,
+ "free number_%d stainfo with hwaddr = %pM\n",
+ pstapriv->asoc_sta_count, psta->hwaddr);
+ pstapriv->asoc_sta_count--;
+
+ /* re-init sta_info; 20061114 will be init in alloc_stainfo */
+ /* _rtw_init_sta_xmit_priv23a(&psta->sta_xmitpriv); */
+ /* _rtw_init_sta_recv_priv23a(&psta->sta_recvpriv); */
+
+ del_timer_sync(&psta->addba_retry_timer);
+
+ /* for A-MPDU Rx reordering buffer control,
+ cancel reordering_ctrl_timer */
+ for (i = 0; i < 16; i++) {
+ struct list_head *phead, *plist;
+ struct recv_frame *prframe;
+ struct rtw_queue *ppending_recvframe_queue;
+
+ preorder_ctrl = &psta->recvreorder_ctrl[i];
+
+ del_timer_sync(&preorder_ctrl->reordering_ctrl_timer);
+
+ ppending_recvframe_queue =
+ &preorder_ctrl->pending_recvframe_queue;
+
+ spin_lock_bh(&ppending_recvframe_queue->lock);
+ phead = get_list_head(ppending_recvframe_queue);
+ plist = phead->next;
+
+ while (!list_empty(phead)) {
+ prframe = container_of(plist, struct recv_frame, list);
+ plist = plist->next;
+ list_del_init(&prframe->list);
+ rtw_free_recvframe23a(prframe);
+ }
+ spin_unlock_bh(&ppending_recvframe_queue->lock);
+ }
+ if (!(psta->state & WIFI_AP_STATE))
+ rtl8723a_SetHalODMVar(padapter, HAL_ODM_STA_INFO, psta, false);
+#ifdef CONFIG_8723AU_AP_MODE
+ spin_lock_bh(&pstapriv->auth_list_lock);
+ if (!list_empty(&psta->auth_list)) {
+ list_del_init(&psta->auth_list);
+ pstapriv->auth_list_cnt--;
+ }
+ spin_unlock_bh(&pstapriv->auth_list_lock);
+
+ psta->expire_to = 0;
+
+ psta->sleepq_ac_len = 0;
+ psta->qos_info = 0;
+
+ psta->max_sp_len = 0;
+ psta->uapsd_bk = 0;
+ psta->uapsd_be = 0;
+ psta->uapsd_vi = 0;
+ psta->uapsd_vo = 0;
+
+ psta->has_legacy_ac = 0;
+
+ pstapriv->sta_dz_bitmap &= ~CHKBIT(psta->aid);
+ pstapriv->tim_bitmap &= ~CHKBIT(psta->aid);
+
+ if ((psta->aid > 0) && (pstapriv->sta_aid[psta->aid - 1] == psta)) {
+ pstapriv->sta_aid[psta->aid - 1] = NULL;
+ psta->aid = 0;
+ }
+#endif /* CONFIG_8723AU_AP_MODE */
+
+ kfree(psta);
+exit:
+ return _SUCCESS;
+}
+
+/* free all stainfo which in sta_hash[all] */
+void rtw_free_all_stainfo23a(struct rtw_adapter *padapter)
+{
+ struct list_head *plist, *phead, *ptmp;
+ struct sta_info *psta;
+ struct sta_priv *pstapriv = &padapter->stapriv;
+ struct sta_info *pbcmc_stainfo = rtw_get_bcmc_stainfo23a(padapter);
+ s32 index;
+
+ if (pstapriv->asoc_sta_count == 1)
+ return;
+
+ spin_lock_bh(&pstapriv->sta_hash_lock);
+
+ for (index = 0; index < NUM_STA; index++) {
+ phead = &pstapriv->sta_hash[index];
+
+ list_for_each_safe(plist, ptmp, phead) {
+ psta = container_of(plist, struct sta_info, hash_list);
+
+ if (pbcmc_stainfo != psta)
+ rtw_free_stainfo23a(padapter, psta);
+ }
+ }
+ spin_unlock_bh(&pstapriv->sta_hash_lock);
+}
+
+/* any station allocated can be searched by hash list */
+struct sta_info *rtw_get_stainfo23a(struct sta_priv *pstapriv, const u8 *hwaddr)
+{
+ struct list_head *plist, *phead;
+ struct sta_info *psta = NULL;
+ u32 index;
+ const u8 *addr;
+
+ if (hwaddr == NULL)
+ return NULL;
+
+ if (is_multicast_ether_addr(hwaddr))
+ addr = bc_addr;
+ else
+ addr = hwaddr;
+
+ index = wifi_mac_hash(addr);
+
+ spin_lock_bh(&pstapriv->sta_hash_lock);
+
+ phead = &pstapriv->sta_hash[index];
+
+ list_for_each(plist, phead) {
+ psta = container_of(plist, struct sta_info, hash_list);
+
+ /* if found the matched address */
+ if (ether_addr_equal(psta->hwaddr, addr))
+ break;
+
+ psta = NULL;
+ }
+ spin_unlock_bh(&pstapriv->sta_hash_lock);
+ return psta;
+}
+
+int rtw_init_bcmc_stainfo23a(struct rtw_adapter *padapter)
+{
+ struct sta_priv *pstapriv = &padapter->stapriv;
+ struct sta_info *psta;
+ struct tx_servq *ptxservq;
+ int res = _SUCCESS;
+
+ psta = rtw_alloc_stainfo23a(pstapriv, bc_addr, GFP_KERNEL);
+ if (psta == NULL) {
+ res = _FAIL;
+ RT_TRACE(_module_rtl871x_sta_mgt_c_, _drv_err_,
+ "rtw_alloc_stainfo23a fail\n");
+ return res;
+ }
+ /* default broadcast & multicast use macid 1 */
+ psta->mac_id = 1;
+
+ ptxservq = &psta->sta_xmitpriv.be_q;
+ return _SUCCESS;
+}
+
+struct sta_info *rtw_get_bcmc_stainfo23a(struct rtw_adapter *padapter)
+{
+ struct sta_info *psta;
+ struct sta_priv *pstapriv = &padapter->stapriv;
+
+ psta = rtw_get_stainfo23a(pstapriv, bc_addr);
+ return psta;
+}
+
+bool rtw_access_ctrl23a(struct rtw_adapter *padapter, u8 *mac_addr)
+{
+ bool res = true;
+#ifdef CONFIG_8723AU_AP_MODE
+ struct list_head *plist, *phead;
+ struct rtw_wlan_acl_node *paclnode;
+ bool match = false;
+ struct sta_priv *pstapriv = &padapter->stapriv;
+ struct wlan_acl_pool *pacl_list = &pstapriv->acl_list;
+ struct rtw_queue *pacl_node_q = &pacl_list->acl_node_q;
+
+ spin_lock_bh(&pacl_node_q->lock);
+ phead = get_list_head(pacl_node_q);
+
+ list_for_each(plist, phead) {
+ paclnode = container_of(plist, struct rtw_wlan_acl_node, list);
+
+ if (ether_addr_equal(paclnode->addr, mac_addr)) {
+ if (paclnode->valid) {
+ match = true;
+ break;
+ }
+ }
+ }
+ spin_unlock_bh(&pacl_node_q->lock);
+
+ if (pacl_list->mode == 1)/* accept unless in deny list */
+ res = (match) ? false : true;
+ else if (pacl_list->mode == 2)/* deny unless in accept list */
+ res = (match) ? true : false;
+ else
+ res = true;
+#endif
+ return res;
+}
diff --git a/drivers/staging/rtl8723au/core/rtw_wlan_util.c b/drivers/staging/rtl8723au/core/rtw_wlan_util.c
new file mode 100644
index 000000000..5280338aa
--- /dev/null
+++ b/drivers/staging/rtl8723au/core/rtw_wlan_util.c
@@ -0,0 +1,1553 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2012 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ ******************************************************************************/
+#define _RTW_WLAN_UTIL_C_
+
+#include <osdep_service.h>
+#include <drv_types.h>
+#include <linux/ieee80211.h>
+#include <wifi.h>
+#include <rtl8723a_spec.h>
+
+static unsigned char ARTHEROS_OUI1[] = {0x00, 0x03, 0x7f};
+static unsigned char ARTHEROS_OUI2[] = {0x00, 0x13, 0x74};
+
+static unsigned char BROADCOM_OUI1[] = {0x00, 0x10, 0x18};
+static unsigned char BROADCOM_OUI2[] = {0x00, 0x0a, 0xf7};
+
+static unsigned char CISCO_OUI[] = {0x00, 0x40, 0x96};
+static unsigned char MARVELL_OUI[] = {0x00, 0x50, 0x43};
+static unsigned char RALINK_OUI[] = {0x00, 0x0c, 0x43};
+static unsigned char REALTEK_OUI[] = {0x00, 0xe0, 0x4c};
+static unsigned char AIRGOCAP_OUI[] = {0x00, 0x0a, 0xf5};
+static unsigned char EPIGRAM_OUI[] = {0x00, 0x90, 0x4c};
+
+static unsigned char WPA_TKIP_CIPHER[4] = {0x00, 0x50, 0xf2, 0x02};
+static unsigned char RSN_TKIP_CIPHER[4] = {0x00, 0x0f, 0xac, 0x02};
+
+#define R2T_PHY_DELAY 0
+
+/* define WAIT_FOR_BCN_TO_MIN 3000 */
+#define WAIT_FOR_BCN_TO_MIN 6000
+#define WAIT_FOR_BCN_TO_MAX 20000
+
+static u8 rtw_basic_rate_cck[4] = {
+ IEEE80211_CCK_RATE_1MB | IEEE80211_BASIC_RATE_MASK,
+ IEEE80211_CCK_RATE_2MB | IEEE80211_BASIC_RATE_MASK,
+ IEEE80211_CCK_RATE_5MB | IEEE80211_BASIC_RATE_MASK,
+ IEEE80211_CCK_RATE_11MB | IEEE80211_BASIC_RATE_MASK
+};
+
+static u8 rtw_basic_rate_ofdm[3] = {
+ IEEE80211_OFDM_RATE_6MB | IEEE80211_BASIC_RATE_MASK,
+ IEEE80211_OFDM_RATE_12MB | IEEE80211_BASIC_RATE_MASK,
+ IEEE80211_OFDM_RATE_24MB | IEEE80211_BASIC_RATE_MASK
+};
+
+static u8 rtw_basic_rate_mix[7] = {
+ IEEE80211_CCK_RATE_1MB | IEEE80211_BASIC_RATE_MASK,
+ IEEE80211_CCK_RATE_2MB | IEEE80211_BASIC_RATE_MASK,
+ IEEE80211_CCK_RATE_5MB | IEEE80211_BASIC_RATE_MASK,
+ IEEE80211_CCK_RATE_11MB | IEEE80211_BASIC_RATE_MASK,
+ IEEE80211_OFDM_RATE_6MB | IEEE80211_BASIC_RATE_MASK,
+ IEEE80211_OFDM_RATE_12MB | IEEE80211_BASIC_RATE_MASK,
+ IEEE80211_OFDM_RATE_24MB | IEEE80211_BASIC_RATE_MASK
+};
+
+int cckrates_included23a(unsigned char *rate, int ratelen)
+{
+ int i;
+
+ for (i = 0; i < ratelen; i++) {
+ if (((rate[i]) & 0x7f) == 2 || ((rate[i]) & 0x7f) == 4 ||
+ ((rate[i]) & 0x7f) == 11 || ((rate[i]) & 0x7f) == 22)
+ return true;
+ }
+
+ return false;
+}
+
+int cckratesonly_included23a(unsigned char *rate, int ratelen)
+{
+ int i;
+
+ for (i = 0; i < ratelen; i++) {
+ if (((rate[i]) & 0x7f) != 2 && ((rate[i]) & 0x7f) != 4 &&
+ ((rate[i]) & 0x7f) != 11 && ((rate[i]) & 0x7f) != 22)
+ return false;
+ }
+
+ return true;
+}
+
+unsigned char networktype_to_raid23a(unsigned char network_type)
+{
+ unsigned char raid;
+
+ switch (network_type) {
+ case WIRELESS_11B:
+ raid = RATR_INX_WIRELESS_B;
+ break;
+ case WIRELESS_11A:
+ case WIRELESS_11G:
+ raid = RATR_INX_WIRELESS_G;
+ break;
+ case WIRELESS_11BG:
+ raid = RATR_INX_WIRELESS_GB;
+ break;
+ case WIRELESS_11_24N:
+ case WIRELESS_11_5N:
+ raid = RATR_INX_WIRELESS_N;
+ break;
+ case WIRELESS_11A_5N:
+ case WIRELESS_11G_24N:
+ raid = RATR_INX_WIRELESS_NG;
+ break;
+ case WIRELESS_11BG_24N:
+ raid = RATR_INX_WIRELESS_NGB;
+ break;
+ default:
+ raid = RATR_INX_WIRELESS_GB;
+ break;
+ }
+ return raid;
+}
+
+u8 judge_network_type23a(struct rtw_adapter *padapter,
+ unsigned char *rate, int ratelen)
+{
+ u8 network_type = 0;
+ struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv;
+ struct mlme_ext_info *pmlmeinfo = &pmlmeext->mlmext_info;
+
+ if (pmlmeext->cur_channel > 14) {
+ if (pmlmeinfo->HT_enable)
+ network_type = WIRELESS_11_5N;
+ network_type |= WIRELESS_11A;
+ } else {
+ if (pmlmeinfo->HT_enable)
+ network_type = WIRELESS_11_24N;
+
+ if ((cckratesonly_included23a(rate, ratelen)) == true)
+ network_type |= WIRELESS_11B;
+ else if ((cckrates_included23a(rate, ratelen)) == true)
+ network_type |= WIRELESS_11BG;
+ else
+ network_type |= WIRELESS_11G;
+ }
+ return network_type;
+}
+
+static unsigned char ratetbl_val_2wifirate(unsigned char rate)
+{
+ unsigned char val = 0;
+
+ switch (rate & 0x7f) {
+ case 0:
+ val = IEEE80211_CCK_RATE_1MB;
+ break;
+ case 1:
+ val = IEEE80211_CCK_RATE_2MB;
+ break;
+ case 2:
+ val = IEEE80211_CCK_RATE_5MB;
+ break;
+ case 3:
+ val = IEEE80211_CCK_RATE_11MB;
+ break;
+ case 4:
+ val = IEEE80211_OFDM_RATE_6MB;
+ break;
+ case 5:
+ val = IEEE80211_OFDM_RATE_9MB;
+ break;
+ case 6:
+ val = IEEE80211_OFDM_RATE_12MB;
+ break;
+ case 7:
+ val = IEEE80211_OFDM_RATE_18MB;
+ break;
+ case 8:
+ val = IEEE80211_OFDM_RATE_24MB;
+ break;
+ case 9:
+ val = IEEE80211_OFDM_RATE_36MB;
+ break;
+ case 10:
+ val = IEEE80211_OFDM_RATE_48MB;
+ break;
+ case 11:
+ val = IEEE80211_OFDM_RATE_54MB;
+ break;
+ }
+ return val;
+}
+
+static int is_basicrate(struct rtw_adapter *padapter, unsigned char rate)
+{
+ int i;
+ unsigned char val;
+ struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv;
+
+ for (i = 0; i < NumRates; i++) {
+ val = pmlmeext->basicrate[i];
+
+ if (val != 0xff && val != 0xfe) {
+ if (rate == ratetbl_val_2wifirate(val))
+ return true;
+ }
+ }
+
+ return false;
+}
+
+static unsigned int ratetbl2rateset(struct rtw_adapter *padapter,
+ unsigned char *rateset)
+{
+ int i;
+ unsigned char rate;
+ unsigned int len = 0;
+ struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv;
+
+ for (i = 0; i < NumRates; i++) {
+ rate = pmlmeext->datarate[i];
+
+ switch (rate) {
+ case 0xff:
+ return len;
+ case 0xfe:
+ continue;
+ default:
+ rate = ratetbl_val_2wifirate(rate);
+
+ if (is_basicrate(padapter, rate) == true)
+ rate |= IEEE80211_BASIC_RATE_MASK;
+
+ rateset[len] = rate;
+ len++;
+ break;
+ }
+ }
+ return len;
+}
+
+void get_rate_set23a(struct rtw_adapter *padapter,
+ unsigned char *pbssrate, int *bssrate_len)
+{
+ unsigned char supportedrates[NumRates];
+
+ memset(supportedrates, 0, NumRates);
+ *bssrate_len = ratetbl2rateset(padapter, supportedrates);
+ memcpy(pbssrate, supportedrates, *bssrate_len);
+}
+
+void UpdateBrateTbl23a(struct rtw_adapter *Adapter, u8 *mBratesOS)
+{
+ u8 i;
+ u8 rate;
+
+ /* 1M, 2M, 5.5M, 11M, 6M, 12M, 24M are mandatory. */
+ for (i = 0; i < NDIS_802_11_LENGTH_RATES_EX; i++) {
+ rate = mBratesOS[i] & 0x7f;
+ switch (rate) {
+ case IEEE80211_CCK_RATE_1MB:
+ case IEEE80211_CCK_RATE_2MB:
+ case IEEE80211_CCK_RATE_5MB:
+ case IEEE80211_CCK_RATE_11MB:
+ case IEEE80211_OFDM_RATE_6MB:
+ case IEEE80211_OFDM_RATE_12MB:
+ case IEEE80211_OFDM_RATE_24MB:
+ mBratesOS[i] |= IEEE80211_BASIC_RATE_MASK;
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+void Update23aTblForSoftAP(u8 *bssrateset, u32 bssratelen)
+{
+ u8 i;
+ u8 rate;
+
+ for (i = 0; i < bssratelen; i++) {
+ rate = bssrateset[i] & 0x7f;
+ switch (rate) {
+ case IEEE80211_CCK_RATE_1MB:
+ case IEEE80211_CCK_RATE_2MB:
+ case IEEE80211_CCK_RATE_5MB:
+ case IEEE80211_CCK_RATE_11MB:
+ bssrateset[i] |= IEEE80211_BASIC_RATE_MASK;
+ break;
+ }
+ }
+}
+
+inline u8 rtw_get_oper_ch23a(struct rtw_adapter *adapter)
+{
+ return adapter_to_dvobj(adapter)->oper_channel;
+}
+
+inline void rtw_set_oper_ch23a(struct rtw_adapter *adapter, u8 ch)
+{
+ adapter_to_dvobj(adapter)->oper_channel = ch;
+}
+
+inline u8 rtw_get_oper_bw23a(struct rtw_adapter *adapter)
+{
+ return adapter_to_dvobj(adapter)->oper_bwmode;
+}
+
+inline void rtw_set_oper_bw23a(struct rtw_adapter *adapter, u8 bw)
+{
+ adapter_to_dvobj(adapter)->oper_bwmode = bw;
+}
+
+inline u8 rtw_get_oper_ch23aoffset(struct rtw_adapter *adapter)
+{
+ return adapter_to_dvobj(adapter)->oper_ch_offset;
+}
+
+inline void rtw_set_oper_ch23aoffset23a(struct rtw_adapter *adapter, u8 offset)
+{
+ adapter_to_dvobj(adapter)->oper_ch_offset = offset;
+}
+
+void SelectChannel23a(struct rtw_adapter *padapter, unsigned char channel)
+{
+ mutex_lock(&adapter_to_dvobj(padapter)->setch_mutex);
+
+ /* saved channel info */
+ rtw_set_oper_ch23a(padapter, channel);
+
+ PHY_SwChnl8723A(padapter, channel);
+
+ mutex_unlock(&adapter_to_dvobj(padapter)->setch_mutex);
+}
+
+static void set_bwmode(struct rtw_adapter *padapter, unsigned short bwmode,
+ unsigned char channel_offset)
+{
+ mutex_lock(&adapter_to_dvobj(padapter)->setbw_mutex);
+
+ /* saved bw info */
+ rtw_set_oper_bw23a(padapter, bwmode);
+ rtw_set_oper_ch23aoffset23a(padapter, channel_offset);
+
+ PHY_SetBWMode23a8723A(padapter, (enum ht_channel_width)bwmode,
+ channel_offset);
+
+ mutex_unlock(&adapter_to_dvobj(padapter)->setbw_mutex);
+}
+
+void set_channel_bwmode23a(struct rtw_adapter *padapter, unsigned char channel,
+ unsigned char channel_offset, unsigned short bwmode)
+{
+ u8 center_ch;
+
+ if (bwmode == HT_CHANNEL_WIDTH_20 ||
+ channel_offset == HAL_PRIME_CHNL_OFFSET_DONT_CARE) {
+ /* SelectChannel23a(padapter, channel); */
+ center_ch = channel;
+ } else {
+ /* switch to the proper channel */
+ if (channel_offset == HAL_PRIME_CHNL_OFFSET_LOWER) {
+ /* SelectChannel23a(padapter, channel + 2); */
+ center_ch = channel + 2;
+ } else {
+ /* SelectChannel23a(padapter, channel - 2); */
+ center_ch = channel - 2;
+ }
+ }
+
+ /* set Channel */
+ mutex_lock(&adapter_to_dvobj(padapter)->setch_mutex);
+
+ /* saved channel/bw info */
+ rtw_set_oper_ch23a(padapter, channel);
+ rtw_set_oper_bw23a(padapter, bwmode);
+ rtw_set_oper_ch23aoffset23a(padapter, channel_offset);
+
+ PHY_SwChnl8723A(padapter, center_ch); /* set center channel */
+
+ mutex_unlock(&adapter_to_dvobj(padapter)->setch_mutex);
+
+ set_bwmode(padapter, bwmode, channel_offset);
+}
+
+inline u8 *get_my_bssid23a(struct wlan_bssid_ex *pnetwork)
+{
+ return pnetwork->MacAddress;
+}
+
+bool is_client_associated_to_ap23a(struct rtw_adapter *padapter)
+{
+ struct mlme_ext_priv *pmlmeext;
+ struct mlme_ext_info *pmlmeinfo;
+
+ if (!padapter)
+ return false;
+
+ pmlmeext = &padapter->mlmeextpriv;
+ pmlmeinfo = &pmlmeext->mlmext_info;
+
+ if (pmlmeinfo->state & WIFI_FW_ASSOC_SUCCESS &&
+ (pmlmeinfo->state & 0x03) == MSR_INFRA)
+ return true;
+ else
+ return false;
+}
+
+bool is_client_associated_to_ibss23a(struct rtw_adapter *padapter)
+{
+ struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv;
+ struct mlme_ext_info *pmlmeinfo = &pmlmeext->mlmext_info;
+
+ if (pmlmeinfo->state & WIFI_FW_ASSOC_SUCCESS &&
+ (pmlmeinfo->state & 0x03) == MSR_ADHOC)
+ return true;
+ else
+ return false;
+}
+
+bool is_IBSS_empty23a(struct rtw_adapter *padapter)
+{
+ unsigned int i;
+ struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv;
+ struct mlme_ext_info *pmlmeinfo = &pmlmeext->mlmext_info;
+
+ for (i = IBSS_START_MAC_ID; i < NUM_STA; i++) {
+ if (pmlmeinfo->FW_sta_info[i].status == 1)
+ return false;
+ }
+
+ return true;
+}
+
+unsigned int decide_wait_for_beacon_timeout23a(unsigned int bcn_interval)
+{
+ if ((bcn_interval << 2) < WAIT_FOR_BCN_TO_MIN)
+ return WAIT_FOR_BCN_TO_MIN;
+ else if ((bcn_interval << 2) > WAIT_FOR_BCN_TO_MAX)
+ return WAIT_FOR_BCN_TO_MAX;
+ else
+ return bcn_interval << 2;
+}
+
+void clear_cam_entry23a(struct rtw_adapter *padapter, u8 entry)
+{
+ unsigned char null_sta[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+
+ unsigned char null_key[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00};
+
+ rtl8723a_cam_write(padapter, entry, 0, null_sta, null_key);
+}
+
+int allocate_fw_sta_entry23a(struct rtw_adapter *padapter)
+{
+ unsigned int mac_id;
+ struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv;
+ struct mlme_ext_info *pmlmeinfo = &pmlmeext->mlmext_info;
+
+ for (mac_id = IBSS_START_MAC_ID; mac_id < NUM_STA; mac_id++) {
+ if (pmlmeinfo->FW_sta_info[mac_id].status == 0) {
+ pmlmeinfo->FW_sta_info[mac_id].status = 1;
+ pmlmeinfo->FW_sta_info[mac_id].retry = 0;
+ break;
+ }
+ }
+
+ return mac_id;
+}
+
+void flush_all_cam_entry23a(struct rtw_adapter *padapter)
+{
+ struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv;
+ struct mlme_ext_info *pmlmeinfo = &pmlmeext->mlmext_info;
+
+ rtl8723a_cam_invalidate_all(padapter);
+
+ memset(pmlmeinfo->FW_sta_info, 0, sizeof(pmlmeinfo->FW_sta_info));
+}
+
+int WMM_param_handler23a(struct rtw_adapter *padapter, const u8 *p)
+{
+ /* struct registry_priv *pregpriv = &padapter->registrypriv; */
+ struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
+ struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv;
+ struct mlme_ext_info *pmlmeinfo = &pmlmeext->mlmext_info;
+
+ if (pmlmepriv->qos_option == 0) {
+ pmlmeinfo->WMM_enable = 0;
+ return _FAIL;
+ }
+
+ pmlmeinfo->WMM_enable = 1;
+ memcpy(&pmlmeinfo->WMM_param, p + 2 + 6,
+ sizeof(struct WMM_para_element));
+ return true;
+}
+
+void WMMOnAssocRsp23a(struct rtw_adapter *padapter)
+{
+ u8 ACI, ACM, AIFS, ECWMin, ECWMax, aSifsTime;
+ u8 acm_mask;
+ u16 TXOP;
+ u32 acParm, i;
+ u32 edca[4], inx[4];
+ struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv;
+ struct mlme_ext_info *pmlmeinfo = &pmlmeext->mlmext_info;
+ struct xmit_priv *pxmitpriv = &padapter->xmitpriv;
+ struct registry_priv *pregpriv = &padapter->registrypriv;
+
+ if (pmlmeinfo->WMM_enable == 0) {
+ padapter->mlmepriv.acm_mask = 0;
+ return;
+ }
+
+ acm_mask = 0;
+
+ if (pmlmeext->cur_wireless_mode == WIRELESS_11B)
+ aSifsTime = 10;
+ else
+ aSifsTime = 16;
+
+ for (i = 0; i < 4; i++) {
+ ACI = (pmlmeinfo->WMM_param.ac_param[i].ACI_AIFSN >> 5) & 0x03;
+ ACM = (pmlmeinfo->WMM_param.ac_param[i].ACI_AIFSN >> 4) & 0x01;
+
+ /* AIFS = AIFSN * slot time + SIFS - r2t phy delay */
+ AIFS = (pmlmeinfo->WMM_param.ac_param[i].ACI_AIFSN & 0x0f) *
+ pmlmeinfo->slotTime + aSifsTime;
+
+ ECWMin = pmlmeinfo->WMM_param.ac_param[i].CW & 0x0f;
+ ECWMax = (pmlmeinfo->WMM_param.ac_param[i].CW & 0xf0) >> 4;
+ TXOP = le16_to_cpu(pmlmeinfo->WMM_param.ac_param[i].TXOP_limit);
+
+ acParm = AIFS | (ECWMin << 8) | (ECWMax << 12) | (TXOP << 16);
+
+ switch (ACI) {
+ case 0x0:
+ rtl8723a_set_ac_param_be(padapter, acParm);
+ acm_mask |= (ACM? BIT(1):0);
+ edca[XMIT_BE_QUEUE] = acParm;
+ break;
+ case 0x1:
+ rtl8723a_set_ac_param_bk(padapter, acParm);
+ /* acm_mask |= (ACM? BIT(0):0); */
+ edca[XMIT_BK_QUEUE] = acParm;
+ break;
+ case 0x2:
+ rtl8723a_set_ac_param_vi(padapter, acParm);
+ acm_mask |= (ACM? BIT(2):0);
+ edca[XMIT_VI_QUEUE] = acParm;
+ break;
+ case 0x3:
+ rtl8723a_set_ac_param_vo(padapter, acParm);
+ acm_mask |= (ACM? BIT(3):0);
+ edca[XMIT_VO_QUEUE] = acParm;
+ break;
+ }
+
+ DBG_8723A("WMM(%x): %x, %x\n", ACI, ACM, acParm);
+ }
+
+ if (padapter->registrypriv.acm_method == 1)
+ rtl8723a_set_acm_ctrl(padapter, acm_mask);
+ else
+ padapter->mlmepriv.acm_mask = acm_mask;
+
+ inx[0] = 0; inx[1] = 1; inx[2] = 2; inx[3] = 3;
+
+ if (pregpriv->wifi_spec == 1) {
+ u32 j, tmp, change_inx = false;
+
+ /* entry indx: 0->vo, 1->vi, 2->be, 3->bk. */
+ for (i = 0; i < 4; i++) {
+ for (j = i+1; j < 4; j++) {
+ /* compare CW and AIFS */
+ if ((edca[j] & 0xFFFF) < (edca[i] & 0xFFFF)) {
+ change_inx = true;
+ } else if ((edca[j] & 0xFFFF) ==
+ (edca[i] & 0xFFFF)) {
+ /* compare TXOP */
+ if ((edca[j] >> 16) > (edca[i] >> 16))
+ change_inx = true;
+ }
+
+ if (change_inx) {
+ tmp = edca[i];
+ edca[i] = edca[j];
+ edca[j] = tmp;
+
+ tmp = inx[i];
+ inx[i] = inx[j];
+ inx[j] = tmp;
+
+ change_inx = false;
+ }
+ }
+ }
+ }
+
+ for (i = 0; i<4; i++) {
+ pxmitpriv->wmm_para_seq[i] = inx[i];
+ DBG_8723A("wmm_para_seq(%d): %d\n", i,
+ pxmitpriv->wmm_para_seq[i]);
+ }
+}
+
+static void bwmode_update_check(struct rtw_adapter *padapter, const u8 *p)
+{
+ struct ieee80211_ht_operation *pHT_info;
+ struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
+ struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv;
+ struct mlme_ext_info *pmlmeinfo = &pmlmeext->mlmext_info;
+ struct registry_priv *pregistrypriv = &padapter->registrypriv;
+ struct ht_priv *phtpriv = &pmlmepriv->htpriv;
+ unsigned char new_bwmode;
+ unsigned char new_ch_offset;
+
+ if (!p)
+ return;
+ if (!phtpriv->ht_option)
+ return;
+ if (p[1] != sizeof(struct ieee80211_ht_operation))
+ return;
+
+ pHT_info = (struct ieee80211_ht_operation *)(p + 2);
+
+ if ((pHT_info->ht_param & IEEE80211_HT_PARAM_CHAN_WIDTH_ANY) &&
+ pregistrypriv->cbw40_enable) {
+ new_bwmode = HT_CHANNEL_WIDTH_40;
+
+ switch (pHT_info->ht_param & IEEE80211_HT_PARAM_CHA_SEC_OFFSET){
+ case IEEE80211_HT_PARAM_CHA_SEC_ABOVE:
+ new_ch_offset = HAL_PRIME_CHNL_OFFSET_LOWER;
+ break;
+ case IEEE80211_HT_PARAM_CHA_SEC_BELOW:
+ new_ch_offset = HAL_PRIME_CHNL_OFFSET_UPPER;
+ break;
+ default:
+ new_ch_offset = HAL_PRIME_CHNL_OFFSET_DONT_CARE;
+ break;
+ }
+ } else {
+ new_bwmode = HT_CHANNEL_WIDTH_20;
+ new_ch_offset = HAL_PRIME_CHNL_OFFSET_DONT_CARE;
+ }
+
+ if (new_bwmode != pmlmeext->cur_bwmode ||
+ new_ch_offset != pmlmeext->cur_ch_offset) {
+ pmlmeinfo->bwmode_updated = true;
+
+ pmlmeext->cur_bwmode = new_bwmode;
+ pmlmeext->cur_ch_offset = new_ch_offset;
+
+ /* update HT info also */
+ HT_info_handler23a(padapter, p);
+ } else
+ pmlmeinfo->bwmode_updated = false;
+
+ if (pmlmeinfo->bwmode_updated) {
+ struct sta_info *psta;
+ struct wlan_bssid_ex *cur_network = &pmlmeinfo->network;
+ struct sta_priv *pstapriv = &padapter->stapriv;
+
+ /* set_channel_bwmode23a(padapter, pmlmeext->cur_channel,
+ pmlmeext->cur_ch_offset, pmlmeext->cur_bwmode); */
+
+ /* update ap's stainfo */
+ psta = rtw_get_stainfo23a(pstapriv, cur_network->MacAddress);
+ if (psta) {
+ struct ht_priv *phtpriv_sta = &psta->htpriv;
+
+ if (phtpriv_sta->ht_option) {
+ /* bwmode */
+ phtpriv_sta->bwmode = pmlmeext->cur_bwmode;
+ phtpriv_sta->ch_offset =
+ pmlmeext->cur_ch_offset;
+ } else {
+ phtpriv_sta->bwmode = HT_CHANNEL_WIDTH_20;
+ phtpriv_sta->ch_offset =
+ HAL_PRIME_CHNL_OFFSET_DONT_CARE;
+ }
+ }
+ }
+}
+
+void HT_caps_handler23a(struct rtw_adapter *padapter, const u8 *p)
+{
+ unsigned int i;
+ u8 rf_type;
+ u8 max_AMPDU_len, min_MPDU_spacing;
+ struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv;
+ struct mlme_ext_info *pmlmeinfo = &pmlmeext->mlmext_info;
+ struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
+ struct ht_priv *phtpriv = &pmlmepriv->htpriv;
+ struct ieee80211_ht_cap *cap;
+ u8 *dstcap;
+
+ if (!p)
+ return;
+
+ if (!phtpriv->ht_option)
+ return;
+
+ pmlmeinfo->HT_caps_enable = 1;
+
+ cap = &pmlmeinfo->ht_cap;
+ dstcap = (u8 *)cap;
+ for (i = 0; i < p[1]; i++) {
+ if (i != 2) {
+ dstcap[i] &= p[i + 2];
+ } else {
+ /* modify from fw by Thomas 2010/11/17 */
+ if ((cap->ampdu_params_info &
+ IEEE80211_HT_AMPDU_PARM_FACTOR) >
+ (p[i + 2] & IEEE80211_HT_AMPDU_PARM_FACTOR))
+ max_AMPDU_len = p[i + 2] &
+ IEEE80211_HT_AMPDU_PARM_FACTOR;
+ else
+ max_AMPDU_len = cap->ampdu_params_info &
+ IEEE80211_HT_AMPDU_PARM_FACTOR;
+
+ if ((cap->ampdu_params_info &
+ IEEE80211_HT_AMPDU_PARM_DENSITY) >
+ (p[i + 2] & IEEE80211_HT_AMPDU_PARM_DENSITY))
+ min_MPDU_spacing = cap->ampdu_params_info &
+ IEEE80211_HT_AMPDU_PARM_DENSITY;
+ else
+ min_MPDU_spacing = p[i + 2] &
+ IEEE80211_HT_AMPDU_PARM_DENSITY;
+
+ cap->ampdu_params_info =
+ max_AMPDU_len | min_MPDU_spacing;
+ }
+ }
+
+ rf_type = rtl8723a_get_rf_type(padapter);
+
+ /* update the MCS rates */
+ for (i = 0; i < IEEE80211_HT_MCS_MASK_LEN; i++) {
+ if (rf_type == RF_1T1R || rf_type == RF_1T2R)
+ cap->mcs.rx_mask[i] &= MCS_rate_1R23A[i];
+ else
+ cap->mcs.rx_mask[i] &= MCS_rate_2R23A[i];
+ }
+}
+
+void HT_info_handler23a(struct rtw_adapter *padapter, const u8 *p)
+{
+ struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv;
+ struct mlme_ext_info *pmlmeinfo = &pmlmeext->mlmext_info;
+ struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
+ struct ht_priv *phtpriv = &pmlmepriv->htpriv;
+
+ if (!p)
+ return;
+
+ if (!phtpriv->ht_option)
+ return;
+
+ if (p[1] != sizeof(struct ieee80211_ht_operation))
+ return;
+
+ pmlmeinfo->HT_info_enable = 1;
+ memcpy(&pmlmeinfo->HT_info, p + 2, p[1]);
+}
+
+void HTOnAssocRsp23a(struct rtw_adapter *padapter)
+{
+ unsigned char max_AMPDU_len;
+ unsigned char min_MPDU_spacing;
+ /* struct registry_priv *pregpriv = &padapter->registrypriv; */
+ struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv;
+ struct mlme_ext_info *pmlmeinfo = &pmlmeext->mlmext_info;
+
+ DBG_8723A("%s\n", __func__);
+
+ if (pmlmeinfo->HT_info_enable && pmlmeinfo->HT_caps_enable)
+ pmlmeinfo->HT_enable = 1;
+ else {
+ pmlmeinfo->HT_enable = 0;
+ /* set_channel_bwmode23a(padapter, pmlmeext->cur_channel,
+ pmlmeext->cur_ch_offset, pmlmeext->cur_bwmode); */
+ return;
+ }
+
+ /* handle A-MPDU parameter field */
+ /*
+ AMPDU_para [1:0]:Max AMPDU Len => 0:8k , 1:16k, 2:32k, 3:64k
+ AMPDU_para [4:2]:Min MPDU Start Spacing
+ */
+ max_AMPDU_len = pmlmeinfo->ht_cap.ampdu_params_info &
+ IEEE80211_HT_AMPDU_PARM_FACTOR;
+
+ min_MPDU_spacing =
+ (pmlmeinfo->ht_cap.ampdu_params_info &
+ IEEE80211_HT_AMPDU_PARM_DENSITY) >> 2;
+
+ rtl8723a_set_ampdu_min_space(padapter, min_MPDU_spacing);
+ rtl8723a_set_ampdu_factor(padapter, max_AMPDU_len);
+}
+
+void ERP_IE_handler23a(struct rtw_adapter *padapter, const u8 *p)
+{
+ struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv;
+ struct mlme_ext_info *pmlmeinfo = &pmlmeext->mlmext_info;
+
+ if (p[1] > 1)
+ return;
+
+ pmlmeinfo->ERP_enable = 1;
+ memcpy(&pmlmeinfo->ERP_IE, p + 2, p[1]);
+}
+
+void VCS_update23a(struct rtw_adapter *padapter, struct sta_info *psta)
+{
+ struct registry_priv *pregpriv = &padapter->registrypriv;
+ struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv;
+ struct mlme_ext_info *pmlmeinfo = &pmlmeext->mlmext_info;
+
+ switch (pregpriv->vrtl_carrier_sense) { /* 0:off 1:on 2:auto */
+ case 0: /* off */
+ psta->rtsen = 0;
+ psta->cts2self = 0;
+ break;
+ case 1: /* on */
+ if (pregpriv->vcs_type == RTS_CTS) {
+ psta->rtsen = 1;
+ psta->cts2self = 0;
+ } else {
+ psta->rtsen = 0;
+ psta->cts2self = 1;
+ }
+ break;
+ case 2: /* auto */
+ default:
+ if (pmlmeinfo->ERP_enable && pmlmeinfo->ERP_IE & BIT(1)) {
+ if (pregpriv->vcs_type == RTS_CTS) {
+ psta->rtsen = 1;
+ psta->cts2self = 0;
+ } else {
+ psta->rtsen = 0;
+ psta->cts2self = 1;
+ }
+ } else {
+ psta->rtsen = 0;
+ psta->cts2self = 0;
+ }
+ break;
+ }
+}
+
+int rtw_check_bcn_info23a(struct rtw_adapter *Adapter,
+ struct ieee80211_mgmt *mgmt, u32 pkt_len)
+{
+ struct wlan_network *cur_network = &Adapter->mlmepriv.cur_network;
+ struct ieee80211_ht_operation *pht_info;
+ unsigned short val16;
+ u8 crypto, bcn_channel;
+ int group_cipher = 0, pairwise_cipher = 0, is_8021x = 0, r;
+ int pie_len, ssid_len, privacy;
+ const u8 *p, *ssid;
+
+ if (!is_client_associated_to_ap23a(Adapter))
+ return _SUCCESS;
+
+ if (unlikely(!ieee80211_is_beacon(mgmt->frame_control))) {
+ printk(KERN_WARNING "%s: received a non beacon frame!\n",
+ __func__);
+ return _FAIL;
+ }
+
+ if (!ether_addr_equal(cur_network->network.MacAddress, mgmt->bssid)) {
+ DBG_8723A("%s: linked but recv other bssid bcn %pM %pM\n",
+ __func__, mgmt->bssid,
+ cur_network->network.MacAddress);
+ return _FAIL;
+ }
+
+ /* check bw and channel offset */
+ /* parsing HT_CAP_IE */
+ pie_len = pkt_len - offsetof(struct ieee80211_mgmt, u.beacon.variable);
+
+ /* Checking for channel */
+ p = cfg80211_find_ie(WLAN_EID_DS_PARAMS, mgmt->u.beacon.variable,
+ pie_len);
+ if (p)
+ bcn_channel = p[2];
+ else {
+ /* In 5G, some ap do not have DSSET IE checking HT
+ info for channel */
+ p = cfg80211_find_ie(WLAN_EID_HT_OPERATION,
+ mgmt->u.beacon.variable, pie_len);
+
+ if (p && p[1] > 0) {
+ pht_info = (struct ieee80211_ht_operation *)(p + 2);
+ bcn_channel = pht_info->primary_chan;
+ } else { /* we don't find channel IE, so don't check it */
+ DBG_8723A("Oops: %s we don't find channel IE, so don't "
+ "check it\n", __func__);
+ bcn_channel = Adapter->mlmeextpriv.cur_channel;
+ }
+ }
+ if (bcn_channel != Adapter->mlmeextpriv.cur_channel) {
+ DBG_8723A("%s beacon channel:%d cur channel:%d disconnect\n",
+ __func__, bcn_channel,
+ Adapter->mlmeextpriv.cur_channel);
+ goto _mismatch;
+ }
+
+ /* checking SSID */
+ p = cfg80211_find_ie(WLAN_EID_SSID, mgmt->u.beacon.variable, pie_len);
+ if (p && p[1]) {
+ ssid = p + 2;
+ ssid_len = p[1];
+ } else {
+ DBG_8723A("%s marc: cannot find SSID for survey event\n",
+ __func__);
+ ssid = NULL;
+ ssid_len = 0;
+ }
+
+ RT_TRACE(_module_rtl871x_mlme_c_, _drv_info_,
+ "%s bssid.Ssid.Ssid:%s bssid.Ssid.SsidLength:%d cur_network->network.Ssid.Ssid:%s len:%d\n",
+ __func__, ssid, ssid_len, cur_network->network.Ssid.ssid,
+ cur_network->network.Ssid.ssid_len);
+
+ if (ssid_len != cur_network->network.Ssid.ssid_len || ssid_len > 32 ||
+ (ssid_len &&
+ memcmp(ssid, cur_network->network.Ssid.ssid, ssid_len))) {
+ DBG_8723A("%s(), SSID is not match return FAIL\n", __func__);
+ goto _mismatch;
+ }
+
+ /* check encryption info */
+ val16 = le16_to_cpu(mgmt->u.beacon.capab_info);
+
+ if (val16 & WLAN_CAPABILITY_PRIVACY)
+ privacy = 1;
+ else
+ privacy = 0;
+
+ RT_TRACE(_module_rtl871x_mlme_c_, _drv_info_,
+ "%s(): cur_network->network.Privacy is %d, bssid.Privacy is %d\n",
+ __func__, cur_network->network.Privacy, privacy);
+ if (cur_network->network.Privacy != privacy) {
+ DBG_8723A("%s(), privacy is not match return FAIL\n", __func__);
+ goto _mismatch;
+ }
+
+ p = cfg80211_find_ie(WLAN_EID_RSN, mgmt->u.beacon.variable, pie_len);
+ if (p && p[1]) {
+ crypto = ENCRYP_PROTOCOL_WPA2;
+ if (p && p[1]) {
+ r = rtw_parse_wpa2_ie23a(p, p[1] + 2, &group_cipher,
+ &pairwise_cipher, &is_8021x);
+ if (r == _SUCCESS)
+ RT_TRACE(_module_rtl871x_mlme_c_, _drv_info_,
+ "%s pnetwork->pairwise_cipher: %d, pnetwork->group_cipher: %d, is_802x : %d\n",
+ __func__, pairwise_cipher,
+ group_cipher, is_8021x);
+ }
+ } else {
+ p = cfg80211_find_vendor_ie(WLAN_OUI_MICROSOFT,
+ WLAN_OUI_TYPE_MICROSOFT_WPA,
+ mgmt->u.beacon.variable, pie_len);
+ if (p && p[1]) {
+ crypto = ENCRYP_PROTOCOL_WPA;
+ r = rtw_parse_wpa_ie23a(p, p[1] + 2, &group_cipher,
+ &pairwise_cipher, &is_8021x);
+ if (r == _SUCCESS)
+ RT_TRACE(_module_rtl871x_mlme_c_, _drv_info_,
+ "%s pnetwork->pairwise_cipher: %d, group_cipher is %d, is_8021x is %d\n",
+ __func__, pairwise_cipher,
+ group_cipher, is_8021x);
+ } else {
+ if (privacy)
+ crypto = ENCRYP_PROTOCOL_WEP;
+ else
+ crypto = ENCRYP_PROTOCOL_OPENSYS;
+ }
+ }
+
+ if (cur_network->BcnInfo.encryp_protocol != crypto) {
+ DBG_8723A("%s(): encryption mismatch, return FAIL\n", __func__);
+ goto _mismatch;
+ }
+
+ if (crypto == ENCRYP_PROTOCOL_WPA || crypto == ENCRYP_PROTOCOL_WPA2) {
+ RT_TRACE(_module_rtl871x_mlme_c_, _drv_err_,
+ "%s cur_network->group_cipher is %d: %d\n", __func__,
+ cur_network->BcnInfo.group_cipher, group_cipher);
+ if (pairwise_cipher != cur_network->BcnInfo.pairwise_cipher ||
+ group_cipher != cur_network->BcnInfo.group_cipher) {
+ DBG_8723A("%s pairwise_cipher(%x:%x) or group_cipher "
+ "(%x:%x) is not match, return FAIL\n",
+ __func__, pairwise_cipher,
+ cur_network->BcnInfo.pairwise_cipher,
+ group_cipher,
+ cur_network->BcnInfo.group_cipher);
+ goto _mismatch;
+ }
+
+ if (is_8021x != cur_network->BcnInfo.is_8021x) {
+ DBG_8723A("%s authentication is not match, return "
+ "FAIL\n", __func__);
+ goto _mismatch;
+ }
+ }
+
+ return _SUCCESS;
+
+_mismatch:
+
+ return _FAIL;
+}
+
+void update_beacon23a_info(struct rtw_adapter *padapter,
+ struct ieee80211_mgmt *mgmt,
+ uint pkt_len, struct sta_info *psta)
+{
+ unsigned int len;
+ const u8 *p;
+
+ len = pkt_len - offsetof(struct ieee80211_mgmt, u.beacon.variable);
+
+ p = cfg80211_find_ie(WLAN_EID_HT_OPERATION, mgmt->u.beacon.variable,
+ len);
+ if (p)
+ bwmode_update_check(padapter, p);
+
+ p = cfg80211_find_ie(WLAN_EID_ERP_INFO, mgmt->u.beacon.variable, len);
+ if (p) {
+ ERP_IE_handler23a(padapter, p);
+ VCS_update23a(padapter, psta);
+ }
+}
+
+bool is_ap_in_tkip23a(struct rtw_adapter *padapter)
+{
+ u32 i;
+ struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv;
+ struct mlme_ext_info *pmlmeinfo = &pmlmeext->mlmext_info;
+ struct wlan_bssid_ex *cur_network = &pmlmeinfo->network;
+ const u8 *p;
+
+ if (cur_network->capability & WLAN_CAPABILITY_PRIVACY) {
+ for (i = 0; i < pmlmeinfo->network.IELength;) {
+ p = pmlmeinfo->network.IEs + i;
+
+ switch (p[0]) {
+ case WLAN_EID_VENDOR_SPECIFIC:
+ if (!memcmp(p + 2, RTW_WPA_OUI23A_TYPE, 4) &&
+ !memcmp(p + 2 + 12, WPA_TKIP_CIPHER, 4))
+ return true;
+ break;
+ case WLAN_EID_RSN:
+ if (!memcmp(p + 2 + 8, RSN_TKIP_CIPHER, 4))
+ return true;
+ break;
+ default:
+ break;
+ }
+ i += (p[1] + 2);
+ }
+ return false;
+ } else
+ return false;
+}
+
+bool should_forbid_n_rate23a(struct rtw_adapter *padapter)
+{
+ u32 i;
+ struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
+ struct wlan_bssid_ex *cur_network = &pmlmepriv->cur_network.network;
+ const u8 *p;
+
+ if (cur_network->capability & WLAN_CAPABILITY_PRIVACY) {
+ for (i = 0; i < cur_network->IELength;) {
+ p = cur_network->IEs + i;
+
+ switch (p[0]) {
+ case WLAN_EID_VENDOR_SPECIFIC:
+ if (!memcmp(p + 2, RTW_WPA_OUI23A_TYPE, 4) &&
+ (!memcmp(p + 2 + 12,
+ WPA_CIPHER_SUITE_CCMP23A, 4) ||
+ !memcmp(p + 2 + 16,
+ WPA_CIPHER_SUITE_CCMP23A, 4)))
+ return false;
+ break;
+ case WLAN_EID_RSN:
+ if (!memcmp(p + 2 + 8,
+ RSN_CIPHER_SUITE_CCMP23A, 4) ||
+ !memcmp(p + 2 + 12,
+ RSN_CIPHER_SUITE_CCMP23A, 4))
+ return false;
+ default:
+ break;
+ }
+
+ i += (p[1] + 2);
+ }
+ return true;
+ } else {
+ return false;
+ }
+}
+
+bool is_ap_in_wep23a(struct rtw_adapter *padapter)
+{
+ u32 i;
+ struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv;
+ struct mlme_ext_info *pmlmeinfo = &pmlmeext->mlmext_info;
+ struct wlan_bssid_ex *cur_network = &pmlmeinfo->network;
+ const u8 *p;
+
+ if (cur_network->capability & WLAN_CAPABILITY_PRIVACY) {
+ for (i = 0; i < pmlmeinfo->network.IELength;) {
+ p = pmlmeinfo->network.IEs + i;
+
+ switch (p[0]) {
+ case WLAN_EID_VENDOR_SPECIFIC:
+ if (!memcmp(p + 2, RTW_WPA_OUI23A_TYPE, 4))
+ return false;
+ break;
+ case WLAN_EID_RSN:
+ return false;
+
+ default:
+ break;
+ }
+
+ i += (p[1] + 2);
+ }
+
+ return true;
+ } else
+ return false;
+}
+
+static int wifirate2_ratetbl_inx23a(unsigned char rate)
+{
+ int inx = 0;
+
+ rate = rate & 0x7f;
+
+ switch (rate) {
+ case 54*2:
+ inx = 11;
+ break;
+ case 48*2:
+ inx = 10;
+ break;
+ case 36*2:
+ inx = 9;
+ break;
+ case 24*2:
+ inx = 8;
+ break;
+ case 18*2:
+ inx = 7;
+ break;
+ case 12*2:
+ inx = 6;
+ break;
+ case 9*2:
+ inx = 5;
+ break;
+ case 6*2:
+ inx = 4;
+ break;
+ case 11*2:
+ inx = 3;
+ break;
+ case 11:
+ inx = 2;
+ break;
+ case 2*2:
+ inx = 1;
+ break;
+ case 1*2:
+ inx = 0;
+ break;
+ }
+ return inx;
+}
+
+unsigned int update_basic_rate23a(unsigned char *ptn, unsigned int ptn_sz)
+{
+ unsigned int i, num_of_rate;
+ unsigned int mask = 0;
+
+ num_of_rate = (ptn_sz > NumRates)? NumRates: ptn_sz;
+
+ for (i = 0; i < num_of_rate; i++) {
+ if ((*(ptn + i)) & 0x80)
+ mask |= 0x1 << wifirate2_ratetbl_inx23a(*(ptn + i));
+ }
+ return mask;
+}
+
+unsigned int update_supported_rate23a(unsigned char *ptn, unsigned int ptn_sz)
+{
+ unsigned int i, num_of_rate;
+ unsigned int mask = 0;
+
+ num_of_rate = (ptn_sz > NumRates) ? NumRates : ptn_sz;
+
+ for (i = 0; i < num_of_rate; i++)
+ mask |= 0x1 << wifirate2_ratetbl_inx23a(*(ptn + i));
+ return mask;
+}
+
+unsigned int update_MSC_rate23a(struct ieee80211_ht_cap *pHT_caps)
+{
+ unsigned int mask = 0;
+
+ mask = pHT_caps->mcs.rx_mask[0] << 12 |
+ pHT_caps->mcs.rx_mask[1] << 20;
+
+ return mask;
+}
+
+int support_short_GI23a(struct rtw_adapter *padapter,
+ struct ieee80211_ht_cap *pHT_caps)
+{
+ struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv;
+ struct mlme_ext_info *pmlmeinfo = &pmlmeext->mlmext_info;
+ unsigned char bit_offset;
+
+ if (!pmlmeinfo->HT_enable)
+ return _FAIL;
+ if (pmlmeinfo->assoc_AP_vendor == HT_IOT_PEER_RALINK)
+ return _FAIL;
+ bit_offset = (pmlmeext->cur_bwmode & HT_CHANNEL_WIDTH_40)? 6: 5;
+
+ if (pHT_caps->cap_info & cpu_to_le16(0x1 << bit_offset))
+ return _SUCCESS;
+ else
+ return _FAIL;
+}
+
+unsigned char get_highest_rate_idx23a(u32 mask)
+{
+ int i;
+ unsigned char rate_idx = 0;
+
+ for (i = 27; i >= 0; i--) {
+ if (mask & BIT(i)) {
+ rate_idx = i;
+ break;
+ }
+ }
+ return rate_idx;
+}
+
+void Update_RA_Entry23a(struct rtw_adapter *padapter, struct sta_info *psta)
+{
+ rtw_hal_update_ra_mask23a(psta, 0);
+}
+
+static void enable_rate_adaptive(struct rtw_adapter *padapter,
+ struct sta_info *psta)
+{
+ Update_RA_Entry23a(padapter, psta);
+}
+
+void set_sta_rate23a(struct rtw_adapter *padapter, struct sta_info *psta)
+{
+ /* rate adaptive */
+ enable_rate_adaptive(padapter, psta);
+}
+
+/* Update RRSR and Rate for USERATE */
+void update_tx_basic_rate23a(struct rtw_adapter *padapter, u8 wirelessmode)
+{
+ unsigned char supported_rates[NDIS_802_11_LENGTH_RATES_EX];
+
+ memset(supported_rates, 0, NDIS_802_11_LENGTH_RATES_EX);
+
+ if (wirelessmode == WIRELESS_11B) {
+ memcpy(supported_rates, rtw_basic_rate_cck, 4);
+ } else if (wirelessmode & WIRELESS_11B) {
+ memcpy(supported_rates, rtw_basic_rate_mix, 7);
+ } else {
+ memcpy(supported_rates, rtw_basic_rate_ofdm, 3);
+ }
+
+ if (wirelessmode & WIRELESS_11B)
+ update_mgnt_tx_rate23a(padapter, IEEE80211_CCK_RATE_1MB);
+ else
+ update_mgnt_tx_rate23a(padapter, IEEE80211_OFDM_RATE_6MB);
+
+ HalSetBrateCfg23a(padapter, supported_rates);
+}
+
+unsigned char check_assoc_AP23a(u8 *pframe, uint len)
+{
+ int i;
+ u8 epigram_vendor_flag;
+ u8 ralink_vendor_flag;
+ const u8 *p;
+
+ epigram_vendor_flag = 0;
+ ralink_vendor_flag = 0;
+
+ for (i = 0; i < len;) {
+ p = pframe + i;
+
+ switch (p[0]) {
+ case WLAN_EID_VENDOR_SPECIFIC:
+ if (!memcmp(p + 2, ARTHEROS_OUI1, 3) ||
+ !memcmp(p + 2, ARTHEROS_OUI2, 3)) {
+ DBG_8723A("link to Artheros AP\n");
+ return HT_IOT_PEER_ATHEROS;
+ } else if (!memcmp(p + 2, BROADCOM_OUI1, 3) ||
+ !memcmp(p + 2, BROADCOM_OUI2, 3)) {
+ DBG_8723A("link to Broadcom AP\n");
+ return HT_IOT_PEER_BROADCOM;
+ } else if (!memcmp(p + 2, MARVELL_OUI, 3)) {
+ DBG_8723A("link to Marvell AP\n");
+ return HT_IOT_PEER_MARVELL;
+ } else if (!memcmp(p + 2, RALINK_OUI, 3)) {
+ if (!ralink_vendor_flag)
+ ralink_vendor_flag = 1;
+ else {
+ DBG_8723A("link to Ralink AP\n");
+ return HT_IOT_PEER_RALINK;
+ }
+ } else if (!memcmp(p + 2, CISCO_OUI, 3)) {
+ DBG_8723A("link to Cisco AP\n");
+ return HT_IOT_PEER_CISCO;
+ } else if (!memcmp(p + 2, REALTEK_OUI, 3)) {
+ DBG_8723A("link to Realtek 96B\n");
+ return HT_IOT_PEER_REALTEK;
+ } else if (!memcmp(p + 2, AIRGOCAP_OUI, 3)) {
+ DBG_8723A("link to Airgo Cap\n");
+ return HT_IOT_PEER_AIRGO;
+ } else if (!memcmp(p + 2, EPIGRAM_OUI, 3)) {
+ epigram_vendor_flag = 1;
+ if (ralink_vendor_flag) {
+ DBG_8723A("link to Tenda W311R AP\n");
+ return HT_IOT_PEER_TENDA;
+ } else
+ DBG_8723A("Capture EPIGRAM_OUI\n");
+ } else
+ break;
+ default:
+ break;
+ }
+
+ i += (p[1] + 2);
+ }
+
+ if (ralink_vendor_flag && !epigram_vendor_flag) {
+ DBG_8723A("link to Ralink AP\n");
+ return HT_IOT_PEER_RALINK;
+ } else if (ralink_vendor_flag && epigram_vendor_flag) {
+ DBG_8723A("link to Tenda W311R AP\n");
+ return HT_IOT_PEER_TENDA;
+ } else {
+ DBG_8723A("link to new AP\n");
+ return HT_IOT_PEER_UNKNOWN;
+ }
+}
+
+void update_IOT_info23a(struct rtw_adapter *padapter)
+{
+ struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv;
+ struct mlme_ext_info *pmlmeinfo = &pmlmeext->mlmext_info;
+
+ switch (pmlmeinfo->assoc_AP_vendor) {
+ case HT_IOT_PEER_MARVELL:
+ pmlmeinfo->turboMode_cts2self = 1;
+ pmlmeinfo->turboMode_rtsen = 0;
+ break;
+ case HT_IOT_PEER_RALINK:
+ pmlmeinfo->turboMode_cts2self = 0;
+ pmlmeinfo->turboMode_rtsen = 1;
+ /* disable high power */
+ rtl8723a_odm_support_ability_clr(padapter, (u32)
+ ~DYNAMIC_BB_DYNAMIC_TXPWR);
+ break;
+ case HT_IOT_PEER_REALTEK:
+ /* rtw_write16(padapter, 0x4cc, 0xffff); */
+ /* rtw_write16(padapter, 0x546, 0x01c0); */
+ /* disable high power */
+ rtl8723a_odm_support_ability_clr(padapter, (u32)
+ ~DYNAMIC_BB_DYNAMIC_TXPWR);
+ break;
+ default:
+ pmlmeinfo->turboMode_cts2self = 0;
+ pmlmeinfo->turboMode_rtsen = 1;
+ break;
+ }
+}
+
+void update_capinfo23a(struct rtw_adapter *Adapter, u16 updateCap)
+{
+ struct mlme_ext_priv *pmlmeext = &Adapter->mlmeextpriv;
+ struct mlme_ext_info *pmlmeinfo = &pmlmeext->mlmext_info;
+
+ if (updateCap & cShortPreamble) {
+ /* Short Preamble */
+ if (pmlmeinfo->preamble_mode != PREAMBLE_SHORT) {
+ /* PREAMBLE_LONG or PREAMBLE_AUTO */
+ pmlmeinfo->preamble_mode = PREAMBLE_SHORT;
+ rtl8723a_ack_preamble(Adapter, true);
+ }
+ } else { /* Long Preamble */
+ if (pmlmeinfo->preamble_mode != PREAMBLE_LONG) {
+ /* PREAMBLE_SHORT or PREAMBLE_AUTO */
+ pmlmeinfo->preamble_mode = PREAMBLE_LONG;
+ rtl8723a_ack_preamble(Adapter, false);
+ }
+ }
+ if (updateCap & cIBSS) {
+ /* Filen: See 802.11-2007 p.91 */
+ pmlmeinfo->slotTime = NON_SHORT_SLOT_TIME;
+ } else {
+ /* Filen: See 802.11-2007 p.90 */
+ if (pmlmeext->cur_wireless_mode &
+ (WIRELESS_11G | WIRELESS_11_24N)) {
+ if (updateCap & cShortSlotTime) { /* Short Slot Time */
+ if (pmlmeinfo->slotTime != SHORT_SLOT_TIME)
+ pmlmeinfo->slotTime = SHORT_SLOT_TIME;
+ } else { /* Long Slot Time */
+ if (pmlmeinfo->slotTime != NON_SHORT_SLOT_TIME)
+ pmlmeinfo->slotTime =
+ NON_SHORT_SLOT_TIME;
+ }
+ } else if (pmlmeext->cur_wireless_mode &
+ (WIRELESS_11A | WIRELESS_11_5N)) {
+ pmlmeinfo->slotTime = SHORT_SLOT_TIME;
+ } else {
+ /* B Mode */
+ pmlmeinfo->slotTime = NON_SHORT_SLOT_TIME;
+ }
+ }
+ rtl8723a_set_slot_time(Adapter, pmlmeinfo->slotTime);
+}
+
+void update_wireless_mode23a(struct rtw_adapter *padapter)
+{
+ int ratelen, network_type = 0;
+ struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv;
+ struct mlme_ext_info *pmlmeinfo = &pmlmeext->mlmext_info;
+ struct wlan_bssid_ex *cur_network = &pmlmeinfo->network;
+ unsigned char *rate = cur_network->SupportedRates;
+
+ ratelen = rtw_get_rateset_len23a(cur_network->SupportedRates);
+
+ if ((pmlmeinfo->HT_info_enable) && (pmlmeinfo->HT_caps_enable))
+ pmlmeinfo->HT_enable = 1;
+
+ if (pmlmeext->cur_channel > 14) {
+ if (pmlmeinfo->HT_enable)
+ network_type = WIRELESS_11_5N;
+ network_type |= WIRELESS_11A;
+ } else {
+ if (pmlmeinfo->HT_enable)
+ network_type = WIRELESS_11_24N;
+
+ if (cckratesonly_included23a(rate, ratelen) == true)
+ network_type |= WIRELESS_11B;
+ else if (cckrates_included23a(rate, ratelen) == true)
+ network_type |= WIRELESS_11BG;
+ else
+ network_type |= WIRELESS_11G;
+ }
+
+ pmlmeext->cur_wireless_mode =
+ network_type & padapter->registrypriv.wireless_mode;
+
+ /* 0x0808 -> for CCK, 0x0a0a -> for OFDM */
+ /* change this value if having IOT issues. */
+ rtl8723a_set_resp_sifs(padapter, 0x08, 0x08, 0x0a, 0x0a);
+
+ if (pmlmeext->cur_wireless_mode & WIRELESS_11B)
+ update_mgnt_tx_rate23a(padapter, IEEE80211_CCK_RATE_1MB);
+ else
+ update_mgnt_tx_rate23a(padapter, IEEE80211_OFDM_RATE_6MB);
+}
+
+void update_bmc_sta_support_rate23a(struct rtw_adapter *padapter, u32 mac_id)
+{
+ struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv;
+ struct mlme_ext_info *pmlmeinfo = &pmlmeext->mlmext_info;
+
+ if (pmlmeext->cur_wireless_mode & WIRELESS_11B) {
+ /* Only B, B/G, and B/G/N AP could use CCK rate */
+ memcpy((pmlmeinfo->FW_sta_info[mac_id].SupportedRates),
+ rtw_basic_rate_cck, 4);
+ } else {
+ memcpy(pmlmeinfo->FW_sta_info[mac_id].SupportedRates,
+ rtw_basic_rate_ofdm, 3);
+ }
+}
+
+int update_sta_support_rate23a(struct rtw_adapter *padapter, u8 *pvar_ie,
+ uint var_ie_len, int cam_idx)
+{
+ int supportRateNum = 0;
+ struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv;
+ struct mlme_ext_info *pmlmeinfo = &pmlmeext->mlmext_info;
+ const u8 *p;
+
+ p = cfg80211_find_ie(WLAN_EID_SUPP_RATES, pvar_ie, var_ie_len);
+ if (!p)
+ return _FAIL;
+
+ memcpy(pmlmeinfo->FW_sta_info[cam_idx].SupportedRates, p + 2, p[1]);
+ supportRateNum = p[1];
+
+ p = cfg80211_find_ie(WLAN_EID_EXT_SUPP_RATES, pvar_ie, var_ie_len);
+ if (p)
+ memcpy(pmlmeinfo->FW_sta_info[cam_idx].SupportedRates +
+ supportRateNum, p + 2, p[1]);
+ return _SUCCESS;
+}
+
+void process_addba_req23a(struct rtw_adapter *padapter,
+ u8 *paddba_req, u8 *addr)
+{
+ struct sta_info *psta;
+ u16 tid, start_seq, param;
+ struct recv_reorder_ctrl *preorder_ctrl;
+ struct sta_priv *pstapriv = &padapter->stapriv;
+ struct ADDBA_request *preq = (struct ADDBA_request *)paddba_req;
+ struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv;
+ struct mlme_ext_info *pmlmeinfo = &pmlmeext->mlmext_info;
+
+ psta = rtw_get_stainfo23a(pstapriv, addr);
+
+ if (psta) {
+ start_seq = le16_to_cpu(preq->BA_starting_seqctrl) >> 4;
+
+ param = le16_to_cpu(preq->BA_para_set);
+ tid = (param >> 2) & 0x0f;
+
+ preorder_ctrl = &psta->recvreorder_ctrl[tid];
+
+ preorder_ctrl->indicate_seq = 0xffff;
+
+ preorder_ctrl->enable = (pmlmeinfo->bAcceptAddbaReq == true) ?
+ true : false;
+ }
+}
diff --git a/drivers/staging/rtl8723au/core/rtw_xmit.c b/drivers/staging/rtl8723au/core/rtw_xmit.c
new file mode 100644
index 000000000..a4b6bb6c7
--- /dev/null
+++ b/drivers/staging/rtl8723au/core/rtw_xmit.c
@@ -0,0 +1,2377 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2012 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ ******************************************************************************/
+#define _RTW_XMIT_C_
+
+#include <osdep_service.h>
+#include <drv_types.h>
+#include <wifi.h>
+#include <osdep_intf.h>
+#include <linux/ip.h>
+#include <usb_ops.h>
+#include <rtl8723a_xmit.h>
+
+static void _init_txservq(struct tx_servq *ptxservq)
+{
+
+ INIT_LIST_HEAD(&ptxservq->tx_pending);
+ _rtw_init_queue23a(&ptxservq->sta_pending);
+ ptxservq->qcnt = 0;
+
+}
+
+void _rtw_init_sta_xmit_priv23a(struct sta_xmit_priv *psta_xmitpriv)
+{
+
+ spin_lock_init(&psta_xmitpriv->lock);
+
+ /* for (i = 0 ; i < MAX_NUMBLKS; i++) */
+ /* _init_txservq(&psta_xmitpriv->blk_q[i]); */
+
+ _init_txservq(&psta_xmitpriv->be_q);
+ _init_txservq(&psta_xmitpriv->bk_q);
+ _init_txservq(&psta_xmitpriv->vi_q);
+ _init_txservq(&psta_xmitpriv->vo_q);
+ INIT_LIST_HEAD(&psta_xmitpriv->legacy_dz);
+ INIT_LIST_HEAD(&psta_xmitpriv->apsd);
+
+}
+
+int _rtw_init_xmit_priv23a(struct xmit_priv *pxmitpriv,
+ struct rtw_adapter *padapter)
+{
+ int i;
+ struct xmit_buf *pxmitbuf;
+ struct xmit_frame *pxframe;
+ int res = _SUCCESS;
+ u32 max_xmit_extbuf_size = MAX_XMIT_EXTBUF_SZ;
+ u32 num_xmit_extbuf = NR_XMIT_EXTBUFF;
+
+ spin_lock_init(&pxmitpriv->lock);
+ spin_lock_init(&pxmitpriv->lock_sctx);
+ sema_init(&pxmitpriv->xmit_sema, 0);
+ sema_init(&pxmitpriv->terminate_xmitthread_sema, 0);
+
+ pxmitpriv->adapter = padapter;
+
+ _rtw_init_queue23a(&pxmitpriv->be_pending);
+ _rtw_init_queue23a(&pxmitpriv->bk_pending);
+ _rtw_init_queue23a(&pxmitpriv->vi_pending);
+ _rtw_init_queue23a(&pxmitpriv->vo_pending);
+ _rtw_init_queue23a(&pxmitpriv->bm_pending);
+
+ _rtw_init_queue23a(&pxmitpriv->free_xmit_queue);
+
+ for (i = 0; i < NR_XMITFRAME; i++) {
+ pxframe = kzalloc(sizeof(struct xmit_frame), GFP_KERNEL);
+ if (!pxframe)
+ break;
+ INIT_LIST_HEAD(&pxframe->list);
+
+ pxframe->padapter = padapter;
+ pxframe->frame_tag = NULL_FRAMETAG;
+
+ list_add_tail(&pxframe->list,
+ &pxmitpriv->free_xmit_queue.queue);
+ }
+
+ pxmitpriv->free_xmitframe_cnt = i;
+
+ pxmitpriv->frag_len = MAX_FRAG_THRESHOLD;
+
+ /* init xmit_buf */
+ _rtw_init_queue23a(&pxmitpriv->free_xmitbuf_queue);
+ INIT_LIST_HEAD(&pxmitpriv->xmitbuf_list);
+ _rtw_init_queue23a(&pxmitpriv->pending_xmitbuf_queue);
+
+ for (i = 0; i < NR_XMITBUFF; i++) {
+ pxmitbuf = kzalloc(sizeof(struct xmit_buf), GFP_KERNEL);
+ if (!pxmitbuf)
+ goto fail;
+ INIT_LIST_HEAD(&pxmitbuf->list);
+ INIT_LIST_HEAD(&pxmitbuf->list2);
+
+ pxmitbuf->padapter = padapter;
+
+ /* Tx buf allocation may fail sometimes, so sleep and retry. */
+ res = rtw_os_xmit_resource_alloc23a(padapter, pxmitbuf,
+ (MAX_XMITBUF_SZ + XMITBUF_ALIGN_SZ));
+ if (res == _FAIL) {
+ goto fail;
+ }
+
+ list_add_tail(&pxmitbuf->list,
+ &pxmitpriv->free_xmitbuf_queue.queue);
+ list_add_tail(&pxmitbuf->list2,
+ &pxmitpriv->xmitbuf_list);
+ }
+
+ pxmitpriv->free_xmitbuf_cnt = NR_XMITBUFF;
+
+ /* init xframe_ext queue, the same count as extbuf */
+ _rtw_init_queue23a(&pxmitpriv->free_xframe_ext_queue);
+
+ for (i = 0; i < num_xmit_extbuf; i++) {
+ pxframe = kzalloc(sizeof(struct xmit_frame), GFP_KERNEL);
+ if (!pxframe)
+ break;
+ INIT_LIST_HEAD(&pxframe->list);
+
+ pxframe->padapter = padapter;
+ pxframe->frame_tag = NULL_FRAMETAG;
+
+ pxframe->pkt = NULL;
+
+ pxframe->buf_addr = NULL;
+ pxframe->pxmitbuf = NULL;
+
+ pxframe->ext_tag = 1;
+
+ list_add_tail(&pxframe->list,
+ &pxmitpriv->free_xframe_ext_queue.queue);
+ }
+ pxmitpriv->free_xframe_ext_cnt = i;
+
+ /* Init xmit extension buff */
+ _rtw_init_queue23a(&pxmitpriv->free_xmit_extbuf_queue);
+ INIT_LIST_HEAD(&pxmitpriv->xmitextbuf_list);
+
+ for (i = 0; i < num_xmit_extbuf; i++) {
+ pxmitbuf = kzalloc(sizeof(struct xmit_buf), GFP_KERNEL);
+ if (!pxmitbuf)
+ goto fail;
+ INIT_LIST_HEAD(&pxmitbuf->list);
+ INIT_LIST_HEAD(&pxmitbuf->list2);
+
+ pxmitbuf->padapter = padapter;
+
+ /* Tx buf allocation may fail sometimes, so sleep and retry. */
+ res = rtw_os_xmit_resource_alloc23a(padapter, pxmitbuf,
+ max_xmit_extbuf_size + XMITBUF_ALIGN_SZ);
+ if (res == _FAIL) {
+ goto exit;
+ }
+
+ list_add_tail(&pxmitbuf->list,
+ &pxmitpriv->free_xmit_extbuf_queue.queue);
+ list_add_tail(&pxmitbuf->list2,
+ &pxmitpriv->xmitextbuf_list);
+ }
+
+ pxmitpriv->free_xmit_extbuf_cnt = num_xmit_extbuf;
+
+ rtw_alloc_hwxmits23a(padapter);
+ rtw_init_hwxmits23a(pxmitpriv->hwxmits, pxmitpriv->hwxmit_entry);
+
+ for (i = 0; i < 4; i ++)
+ pxmitpriv->wmm_para_seq[i] = i;
+
+ sema_init(&pxmitpriv->tx_retevt, 0);
+
+ pxmitpriv->ack_tx = false;
+ mutex_init(&pxmitpriv->ack_tx_mutex);
+ rtw_sctx_init23a(&pxmitpriv->ack_tx_ops, 0);
+ tasklet_init(&padapter->xmitpriv.xmit_tasklet,
+ (void(*)(unsigned long))rtl8723au_xmit_tasklet,
+ (unsigned long)padapter);
+
+exit:
+
+ return res;
+fail:
+ goto exit;
+}
+
+void _rtw_free_xmit_priv23a (struct xmit_priv *pxmitpriv)
+{
+ struct rtw_adapter *padapter = pxmitpriv->adapter;
+ struct xmit_frame *pxframe;
+ struct xmit_buf *pxmitbuf;
+ struct list_head *plist, *ptmp;
+
+ list_for_each_safe(plist, ptmp, &pxmitpriv->free_xmit_queue.queue) {
+ pxframe = container_of(plist, struct xmit_frame, list);
+ list_del_init(&pxframe->list);
+ rtw_os_xmit_complete23a(padapter, pxframe);
+ kfree(pxframe);
+ }
+
+ list_for_each_safe(plist, ptmp, &pxmitpriv->xmitbuf_list) {
+ pxmitbuf = container_of(plist, struct xmit_buf, list2);
+ list_del_init(&pxmitbuf->list2);
+ rtw_os_xmit_resource_free23a(padapter, pxmitbuf);
+ kfree(pxmitbuf);
+ }
+
+ /* free xframe_ext queue, the same count as extbuf */
+ list_for_each_safe(plist, ptmp,
+ &pxmitpriv->free_xframe_ext_queue.queue) {
+ pxframe = container_of(plist, struct xmit_frame, list);
+ list_del_init(&pxframe->list);
+ rtw_os_xmit_complete23a(padapter, pxframe);
+ kfree(pxframe);
+ }
+
+ /* free xmit extension buff */
+ list_for_each_safe(plist, ptmp, &pxmitpriv->xmitextbuf_list) {
+ pxmitbuf = container_of(plist, struct xmit_buf, list2);
+ list_del_init(&pxmitbuf->list2);
+ rtw_os_xmit_resource_free23a(padapter, pxmitbuf);
+ kfree(pxmitbuf);
+ }
+
+ rtw_free_hwxmits23a(padapter);
+ mutex_destroy(&pxmitpriv->ack_tx_mutex);
+}
+
+static void update_attrib_vcs_info(struct rtw_adapter *padapter, struct xmit_frame *pxmitframe)
+{
+ u32 sz;
+ struct pkt_attrib *pattrib = &pxmitframe->attrib;
+ struct sta_info *psta = pattrib->psta;
+ struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv;
+ struct mlme_ext_info *pmlmeinfo = &pmlmeext->mlmext_info;
+
+ if (pattrib->psta) {
+ psta = pattrib->psta;
+ } else {
+ DBG_8723A("%s, call rtw_get_stainfo23a()\n", __func__);
+ psta = rtw_get_stainfo23a(&padapter->stapriv, &pattrib->ra[0]);
+ }
+
+ if (psta == NULL) {
+ DBG_8723A("%s, psta == NUL\n", __func__);
+ return;
+ }
+
+ if (!(psta->state &_FW_LINKED)) {
+ DBG_8723A("%s, psta->state(0x%x) != _FW_LINKED\n", __func__, psta->state);
+ return;
+ }
+
+ if (pattrib->nr_frags != 1)
+ sz = padapter->xmitpriv.frag_len;
+ else /* no frag */
+ sz = pattrib->last_txcmdsz;
+
+ /* (1) RTS_Threshold is compared to the MPDU, not MSDU. */
+ /* (2) If there are more than one frag in this MSDU, only the first frag uses protection frame. */
+ /* Other fragments are protected by previous fragment. */
+ /* So we only need to check the length of first fragment. */
+ if (pmlmeext->cur_wireless_mode < WIRELESS_11_24N || padapter->registrypriv.wifi_spec) {
+ if (sz > padapter->registrypriv.rts_thresh) {
+ pattrib->vcs_mode = RTS_CTS;
+ } else {
+ if (psta->rtsen)
+ pattrib->vcs_mode = RTS_CTS;
+ else if (psta->cts2self)
+ pattrib->vcs_mode = CTS_TO_SELF;
+ else
+ pattrib->vcs_mode = NONE_VCS;
+ }
+ } else {
+ while (true) {
+ /* IOT action */
+ if (pmlmeinfo->assoc_AP_vendor == HT_IOT_PEER_ATHEROS &&
+ pattrib->ampdu_en &&
+ padapter->securitypriv.dot11PrivacyAlgrthm ==
+ WLAN_CIPHER_SUITE_CCMP) {
+ pattrib->vcs_mode = CTS_TO_SELF;
+ break;
+ }
+
+ /* check ERP protection */
+ if (psta->rtsen || psta->cts2self) {
+ if (psta->rtsen)
+ pattrib->vcs_mode = RTS_CTS;
+ else if (psta->cts2self)
+ pattrib->vcs_mode = CTS_TO_SELF;
+
+ break;
+ }
+
+ /* check HT op mode */
+ if (pattrib->ht_en) {
+ u8 HTOpMode = pmlmeinfo->HT_protection;
+
+ if ((pmlmeext->cur_bwmode && (HTOpMode == 2 || HTOpMode == 3)) ||
+ (!pmlmeext->cur_bwmode && HTOpMode == 3)) {
+ pattrib->vcs_mode = RTS_CTS;
+ break;
+ }
+ }
+
+ /* check rts */
+ if (sz > padapter->registrypriv.rts_thresh) {
+ pattrib->vcs_mode = RTS_CTS;
+ break;
+ }
+
+ /* to do list: check MIMO power save condition. */
+
+ /* check AMPDU aggregation for TXOP */
+ if (pattrib->ampdu_en) {
+ pattrib->vcs_mode = RTS_CTS;
+ break;
+ }
+
+ pattrib->vcs_mode = NONE_VCS;
+ break;
+ }
+ }
+}
+
+static void update_attrib_phy_info(struct pkt_attrib *pattrib, struct sta_info *psta)
+{
+ /*if (psta->rtsen)
+ pattrib->vcs_mode = RTS_CTS;
+ else if (psta->cts2self)
+ pattrib->vcs_mode = CTS_TO_SELF;
+ else
+ pattrib->vcs_mode = NONE_VCS;*/
+
+ pattrib->mdata = 0;
+ pattrib->eosp = 0;
+ pattrib->triggered = 0;
+
+ /* qos_en, ht_en, init rate, , bw, ch_offset, sgi */
+ pattrib->qos_en = psta->qos_option;
+
+ pattrib->raid = psta->raid;
+ pattrib->ht_en = psta->htpriv.ht_option;
+ pattrib->bwmode = psta->htpriv.bwmode;
+ pattrib->ch_offset = psta->htpriv.ch_offset;
+ pattrib->sgi = psta->htpriv.sgi;
+ pattrib->ampdu_en = false;
+
+ pattrib->retry_ctrl = false;
+}
+
+u8 qos_acm23a(u8 acm_mask, u8 priority)
+{
+ u8 change_priority = priority;
+
+ switch (priority) {
+ case 0:
+ case 3:
+ if (acm_mask & BIT(1))
+ change_priority = 1;
+ break;
+ case 1:
+ case 2:
+ break;
+ case 4:
+ case 5:
+ if (acm_mask & BIT(2))
+ change_priority = 0;
+ break;
+ case 6:
+ case 7:
+ if (acm_mask & BIT(3))
+ change_priority = 5;
+ break;
+ default:
+ DBG_8723A("qos_acm23a(): invalid pattrib->priority: %d!!!\n",
+ priority);
+ change_priority = 0;
+ break;
+ }
+
+ return change_priority;
+}
+
+static void set_qos(struct sk_buff *skb, struct pkt_attrib *pattrib)
+{
+ u8 *pframe = skb->data;
+ struct iphdr *ip_hdr;
+ u8 UserPriority = 0;
+
+ /* get UserPriority from IP hdr */
+ if (pattrib->ether_type == ETH_P_IP) {
+ ip_hdr = (struct iphdr *)(pframe + ETH_HLEN);
+ UserPriority = ip_hdr->tos >> 5;
+ } else if (pattrib->ether_type == ETH_P_PAE) {
+ /* "When priority processing of data frames is supported, */
+ /* a STA's SME should send EAPOL-Key frames at the highest
+ priority." */
+ UserPriority = 7;
+ }
+
+ pattrib->priority = UserPriority;
+ pattrib->hdrlen = sizeof(struct ieee80211_qos_hdr);
+ pattrib->type = IEEE80211_FTYPE_DATA | IEEE80211_STYPE_QOS_DATA;
+}
+
+static int update_attrib(struct rtw_adapter *padapter,
+ struct sk_buff *skb, struct pkt_attrib *pattrib)
+{
+ struct sta_info *psta = NULL;
+ int bmcast;
+ struct sta_priv *pstapriv = &padapter->stapriv;
+ struct security_priv *psecuritypriv = &padapter->securitypriv;
+ struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
+ int res = _SUCCESS;
+ struct ethhdr *ehdr = (struct ethhdr *) skb->data;
+
+ pattrib->ether_type = ntohs(ehdr->h_proto);
+
+ ether_addr_copy(pattrib->dst, ehdr->h_dest);
+ ether_addr_copy(pattrib->src, ehdr->h_source);
+
+ pattrib->pctrl = 0;
+
+ if (check_fwstate(pmlmepriv, WIFI_ADHOC_STATE) ||
+ check_fwstate(pmlmepriv, WIFI_ADHOC_MASTER_STATE)) {
+ ether_addr_copy(pattrib->ra, pattrib->dst);
+ ether_addr_copy(pattrib->ta, pattrib->src);
+ } else if (check_fwstate(pmlmepriv, WIFI_STATION_STATE)) {
+ ether_addr_copy(pattrib->ra, get_bssid(pmlmepriv));
+ ether_addr_copy(pattrib->ta, pattrib->src);
+ } else if (check_fwstate(pmlmepriv, WIFI_AP_STATE)) {
+ ether_addr_copy(pattrib->ra, pattrib->dst);
+ ether_addr_copy(pattrib->ta, get_bssid(pmlmepriv));
+ }
+
+ pattrib->pktlen = skb->len - ETH_HLEN;
+
+ if (pattrib->ether_type == ETH_P_IP) {
+ /* The following is for DHCP and ARP packet, we use cck1M
+ to tx these packets and let LPS awake some time */
+ /* to prevent DHCP protocol fail */
+ pattrib->dhcp_pkt = 0;
+ /* MINIMUM_DHCP_PACKET_SIZE) { */
+ if (pattrib->pktlen > 282 + 24) {
+ if (pattrib->ether_type == ETH_P_IP) {/* IP header */
+ u8 *pframe = skb->data;
+
+ pframe += ETH_HLEN;
+
+ if ((pframe[21] == 68 && pframe[23] == 67) ||
+ (pframe[21] == 67 && pframe[23] == 68)) {
+ /* 68 : UDP BOOTP client */
+ /* 67 : UDP BOOTP server */
+ RT_TRACE(_module_rtl871x_xmit_c_,
+ _drv_err_,
+ "======================update_attrib: get DHCP Packet\n");
+ pattrib->dhcp_pkt = 1;
+ }
+ }
+ }
+ } else if (pattrib->ether_type == ETH_P_PAE) {
+ DBG_8723A_LEVEL(_drv_always_, "send eapol packet\n");
+ }
+
+ if ((pattrib->ether_type == ETH_P_PAE) || (pattrib->dhcp_pkt == 1)) {
+ rtw_set_scan_deny(padapter, 3000);
+ }
+
+ /* If EAPOL , ARP , OR DHCP packet, driver must be in active mode. */
+ if ((pattrib->ether_type == ETH_P_ARP) ||
+ (pattrib->ether_type == ETH_P_PAE) || (pattrib->dhcp_pkt == 1)) {
+ rtw_lps_ctrl_wk_cmd23a(padapter, LPS_CTRL_SPECIAL_PACKET, 1);
+ }
+
+ bmcast = is_multicast_ether_addr(pattrib->ra);
+
+ /* get sta_info */
+ if (bmcast) {
+ psta = rtw_get_bcmc_stainfo23a(padapter);
+ } else {
+ psta = rtw_get_stainfo23a(pstapriv, pattrib->ra);
+ if (psta == NULL) { /* if we cannot get psta => drrp the pkt */
+ RT_TRACE(_module_rtl871x_xmit_c_, _drv_alert_,
+ "update_attrib => get sta_info fail, ra:%pM\n",
+ pattrib->ra);
+ res = _FAIL;
+ goto exit;
+ } else if (check_fwstate(pmlmepriv, WIFI_AP_STATE) &&
+ (!(psta->state & _FW_LINKED))) {
+ res = _FAIL;
+ goto exit;
+ }
+ }
+
+ if (psta) {
+ pattrib->mac_id = psta->mac_id;
+ /* DBG_8723A("%s ==> mac_id(%d)\n", __func__, pattrib->mac_id); */
+ pattrib->psta = psta;
+ } else {
+ /* if we cannot get psta => drop the pkt */
+ RT_TRACE(_module_rtl871x_xmit_c_, _drv_alert_,
+ "update_attrib => get sta_info fail, ra:%pM\n",
+ pattrib->ra);
+ res = _FAIL;
+ goto exit;
+ }
+
+ pattrib->ack_policy = 0;
+ /* get ether_hdr_len */
+
+ /* pattrib->ether_type == 0x8100) ? (14 + 4): 14; vlan tag */
+ pattrib->pkt_hdrlen = ETH_HLEN;
+
+ pattrib->hdrlen = sizeof(struct ieee80211_hdr_3addr);
+ pattrib->type = IEEE80211_FTYPE_DATA;
+ pattrib->priority = 0;
+
+ if (check_fwstate(pmlmepriv, WIFI_AP_STATE | WIFI_ADHOC_STATE |
+ WIFI_ADHOC_MASTER_STATE)) {
+ if (psta->qos_option)
+ set_qos(skb, pattrib);
+ } else {
+ if (pmlmepriv->qos_option) {
+ set_qos(skb, pattrib);
+
+ if (pmlmepriv->acm_mask != 0) {
+ pattrib->priority = qos_acm23a(pmlmepriv->acm_mask,
+ pattrib->priority);
+ }
+ }
+ }
+
+ if (psta->ieee8021x_blocked == true) {
+ RT_TRACE(_module_rtl871x_xmit_c_, _drv_err_,
+ "psta->ieee8021x_blocked == true\n");
+
+ pattrib->encrypt = 0;
+
+ if ((pattrib->ether_type != ETH_P_PAE) &&
+ !check_fwstate(pmlmepriv, WIFI_MP_STATE)) {
+ RT_TRACE(_module_rtl871x_xmit_c_, _drv_err_,
+ "psta->ieee8021x_blocked == true, pattrib->ether_type(%.4x) != 0x888e\n",
+ pattrib->ether_type);
+ res = _FAIL;
+ goto exit;
+ }
+ } else {
+ GET_ENCRY_ALGO(psecuritypriv, psta, pattrib->encrypt, bmcast);
+
+ switch (psecuritypriv->dot11AuthAlgrthm) {
+ case dot11AuthAlgrthm_Open:
+ case dot11AuthAlgrthm_Shared:
+ case dot11AuthAlgrthm_Auto:
+ pattrib->key_idx =
+ (u8)psecuritypriv->dot11PrivacyKeyIndex;
+ break;
+ case dot11AuthAlgrthm_8021X:
+ if (bmcast)
+ pattrib->key_idx =
+ (u8)psecuritypriv->dot118021XGrpKeyid;
+ else
+ pattrib->key_idx = 0;
+ break;
+ default:
+ pattrib->key_idx = 0;
+ break;
+ }
+
+ }
+
+ switch (pattrib->encrypt) {
+ case WLAN_CIPHER_SUITE_WEP40:
+ case WLAN_CIPHER_SUITE_WEP104:
+ pattrib->iv_len = IEEE80211_WEP_IV_LEN;
+ pattrib->icv_len = IEEE80211_WEP_ICV_LEN;
+ break;
+
+ case WLAN_CIPHER_SUITE_TKIP:
+ pattrib->iv_len = IEEE80211_TKIP_IV_LEN;
+ pattrib->icv_len = IEEE80211_TKIP_ICV_LEN;
+
+ if (!padapter->securitypriv.busetkipkey) {
+ RT_TRACE(_module_rtl871x_xmit_c_, _drv_err_,
+ "padapter->securitypriv.busetkipkey(%d) == false drop packet\n",
+ padapter->securitypriv.busetkipkey);
+ res = _FAIL;
+ goto exit;
+ }
+
+ break;
+ case WLAN_CIPHER_SUITE_CCMP:
+ RT_TRACE(_module_rtl871x_xmit_c_, _drv_info_,
+ "pattrib->encrypt =%d (WLAN_CIPHER_SUITE_CCMP)\n",
+ pattrib->encrypt);
+ pattrib->iv_len = IEEE80211_CCMP_HDR_LEN;
+ pattrib->icv_len = IEEE80211_CCMP_MIC_LEN;
+ break;
+
+ default:
+ pattrib->iv_len = 0;
+ pattrib->icv_len = 0;
+ break;
+ }
+
+ RT_TRACE(_module_rtl871x_xmit_c_, _drv_info_,
+ "update_attrib: encrypt =%d\n", pattrib->encrypt);
+
+ if (pattrib->encrypt && !psecuritypriv->hw_decrypted) {
+ pattrib->bswenc = true;
+ RT_TRACE(_module_rtl871x_xmit_c_, _drv_err_,
+ "update_attrib: encrypt =%d bswenc = true\n",
+ pattrib->encrypt);
+ } else {
+ pattrib->bswenc = false;
+ RT_TRACE(_module_rtl871x_xmit_c_, _drv_info_,
+ "update_attrib: bswenc = false\n");
+ }
+ update_attrib_phy_info(pattrib, psta);
+
+exit:
+
+ return res;
+}
+
+static int xmitframe_addmic(struct rtw_adapter *padapter,
+ struct xmit_frame *pxmitframe) {
+ struct mic_data micdata;
+ struct sta_info *stainfo;
+ struct pkt_attrib *pattrib = &pxmitframe->attrib;
+ struct security_priv *psecuritypriv = &padapter->securitypriv;
+ struct xmit_priv *pxmitpriv = &padapter->xmitpriv;
+ int curfragnum, length;
+ u8 *pframe, *payload, mic[8];
+ u8 priority[4]= {0x0, 0x0, 0x0, 0x0};
+ u8 hw_hdr_offset = 0;
+ int bmcst = is_multicast_ether_addr(pattrib->ra);
+
+ if (pattrib->psta) {
+ stainfo = pattrib->psta;
+ } else {
+ DBG_8723A("%s, call rtw_get_stainfo23a()\n", __func__);
+ stainfo = rtw_get_stainfo23a(&padapter->stapriv, &pattrib->ra[0]);
+ }
+
+ if (!stainfo) {
+ DBG_8723A("%s, psta == NUL\n", __func__);
+ return _FAIL;
+ }
+
+ if (!(stainfo->state &_FW_LINKED)) {
+ DBG_8723A("%s, psta->state(0x%x) != _FW_LINKED\n",
+ __func__, stainfo->state);
+ return _FAIL;
+ }
+
+ hw_hdr_offset = TXDESC_OFFSET;
+
+ if (pattrib->encrypt == WLAN_CIPHER_SUITE_TKIP) {
+ /* encode mic code */
+ if (stainfo) {
+ u8 null_key[16]={0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0};
+
+ pframe = pxmitframe->buf_addr + hw_hdr_offset;
+
+ if (bmcst) {
+ if (!memcmp(psecuritypriv->dot118021XGrptxmickey[psecuritypriv->dot118021XGrpKeyid].skey, null_key, 16)) {
+ return _FAIL;
+ }
+ /* start to calculate the mic code */
+ rtw_secmicsetkey23a(&micdata, psecuritypriv->dot118021XGrptxmickey[psecuritypriv->dot118021XGrpKeyid].skey);
+ } else {
+ if (!memcmp(&stainfo->dot11tkiptxmickey.skey[0],
+ null_key, 16)) {
+ return _FAIL;
+ }
+ /* start to calculate the mic code */
+ rtw_secmicsetkey23a(&micdata, &stainfo->dot11tkiptxmickey.skey[0]);
+ }
+
+ if (pframe[1] & 1) { /* ToDS == 1 */
+ /* DA */
+ rtw_secmicappend23a(&micdata, &pframe[16], 6);
+ if (pframe[1] & 2) /* From Ds == 1 */
+ rtw_secmicappend23a(&micdata,
+ &pframe[24], 6);
+ else
+ rtw_secmicappend23a(&micdata,
+ &pframe[10], 6);
+ } else { /* ToDS == 0 */
+ /* DA */
+ rtw_secmicappend23a(&micdata, &pframe[4], 6);
+ if (pframe[1] & 2) /* From Ds == 1 */
+ rtw_secmicappend23a(&micdata,
+ &pframe[16], 6);
+ else
+ rtw_secmicappend23a(&micdata,
+ &pframe[10], 6);
+ }
+
+ /* if (pmlmepriv->qos_option == 1) */
+ if (pattrib->qos_en)
+ priority[0] = (u8)pxmitframe->attrib.priority;
+
+ rtw_secmicappend23a(&micdata, &priority[0], 4);
+
+ payload = pframe;
+
+ for (curfragnum = 0; curfragnum < pattrib->nr_frags;
+ curfragnum++) {
+ payload = PTR_ALIGN(payload, 4);
+ RT_TRACE(_module_rtl871x_xmit_c_, _drv_err_,
+ "=== curfragnum =%d, pframe = 0x%.2x, 0x%.2x, 0x%.2x, 0x%.2x, 0x%.2x, 0x%.2x, 0x%.2x, 0x%.2x,!!!\n",
+ curfragnum, *payload, *(payload + 1),
+ *(payload + 2), *(payload + 3),
+ *(payload + 4), *(payload + 5),
+ *(payload + 6), *(payload + 7));
+
+ payload = payload + pattrib->hdrlen +
+ pattrib->iv_len;
+ RT_TRACE(_module_rtl871x_xmit_c_, _drv_err_,
+ "curfragnum =%d pattrib->hdrlen =%d pattrib->iv_len =%d\n",
+ curfragnum,
+ pattrib->hdrlen, pattrib->iv_len);
+ if ((curfragnum + 1) == pattrib->nr_frags) {
+ length = pattrib->last_txcmdsz -
+ pattrib->hdrlen -
+ pattrib->iv_len -
+ ((pattrib->bswenc) ?
+ pattrib->icv_len : 0);
+ rtw_secmicappend23a(&micdata, payload,
+ length);
+ payload = payload + length;
+ } else {
+ length = pxmitpriv->frag_len -
+ pattrib->hdrlen -
+ pattrib->iv_len -
+ ((pattrib->bswenc) ?
+ pattrib->icv_len : 0);
+ rtw_secmicappend23a(&micdata, payload,
+ length);
+ payload = payload + length +
+ pattrib->icv_len;
+ RT_TRACE(_module_rtl871x_xmit_c_,
+ _drv_err_,
+ "curfragnum =%d length =%d pattrib->icv_len =%d\n",
+ curfragnum, length,
+ pattrib->icv_len);
+ }
+ }
+ rtw_secgetmic23a(&micdata, &mic[0]);
+ RT_TRACE(_module_rtl871x_xmit_c_, _drv_err_,
+ "xmitframe_addmic: before add mic code!!\n");
+ RT_TRACE(_module_rtl871x_xmit_c_, _drv_err_,
+ "xmitframe_addmic: pattrib->last_txcmdsz =%d!!!\n",
+ pattrib->last_txcmdsz);
+ RT_TRACE(_module_rtl871x_xmit_c_, _drv_err_,
+ "xmitframe_addmic: mic[0]= 0x%.2x , mic[1]=0x%.2x , mic[2]= 0x%.2x , mic[3]= 0x%.2x\nmic[4]= 0x%.2x , mic[5]= 0x%.2x , mic[6]= 0x%.2x , mic[7]= 0x%.2x !!!!\n",
+ mic[0], mic[1], mic[2], mic[3],
+ mic[4], mic[5], mic[6], mic[7]);
+ /* add mic code and add the mic code length
+ in last_txcmdsz */
+
+ memcpy(payload, &mic[0], 8);
+ pattrib->last_txcmdsz += 8;
+
+ RT_TRACE(_module_rtl871x_xmit_c_, _drv_info_,
+ "======== last pkt ========\n");
+ payload = payload - pattrib->last_txcmdsz + 8;
+ for (curfragnum = 0; curfragnum < pattrib->last_txcmdsz;
+ curfragnum = curfragnum + 8) {
+ RT_TRACE(_module_rtl871x_xmit_c_, _drv_info_,
+ "%.2x, %.2x, %.2x, %.2x, %.2x, %.2x, %.2x, %.2x\n",
+ *(payload + curfragnum),
+ *(payload + curfragnum + 1),
+ *(payload + curfragnum + 2),
+ *(payload + curfragnum + 3),
+ *(payload + curfragnum + 4),
+ *(payload + curfragnum + 5),
+ *(payload + curfragnum + 6),
+ *(payload + curfragnum + 7));
+ }
+ } else {
+ RT_TRACE(_module_rtl871x_xmit_c_, _drv_err_,
+ "xmitframe_addmic: rtw_get_stainfo23a ==NULL!!!\n");
+ }
+ }
+
+ return _SUCCESS;
+}
+
+static int xmitframe_swencrypt(struct rtw_adapter *padapter,
+ struct xmit_frame *pxmitframe)
+{
+ struct pkt_attrib *pattrib = &pxmitframe->attrib;
+
+ /* if ((psecuritypriv->sw_encrypt)||(pattrib->bswenc)) */
+ if (pattrib->bswenc) {
+ /* DBG_8723A("start xmitframe_swencrypt\n"); */
+ RT_TRACE(_module_rtl871x_xmit_c_, _drv_alert_,
+ "### xmitframe_swencrypt\n");
+ switch (pattrib->encrypt) {
+ case WLAN_CIPHER_SUITE_WEP40:
+ case WLAN_CIPHER_SUITE_WEP104:
+ rtw_wep_encrypt23a(padapter, pxmitframe);
+ break;
+ case WLAN_CIPHER_SUITE_TKIP:
+ rtw_tkip_encrypt23a(padapter, pxmitframe);
+ break;
+ case WLAN_CIPHER_SUITE_CCMP:
+ rtw_aes_encrypt23a(padapter, pxmitframe);
+ break;
+ default:
+ break;
+ }
+
+ } else {
+ RT_TRACE(_module_rtl871x_xmit_c_, _drv_notice_,
+ "### xmitframe_hwencrypt\n");
+ }
+
+ return _SUCCESS;
+}
+
+static int rtw_make_wlanhdr(struct rtw_adapter *padapter, u8 *hdr,
+ struct pkt_attrib *pattrib)
+{
+ struct ieee80211_hdr *pwlanhdr = (struct ieee80211_hdr *)hdr;
+ struct ieee80211_qos_hdr *qoshdr;
+ struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
+ u8 qos_option = false;
+ int res = _SUCCESS;
+
+ struct sta_info *psta;
+
+ int bmcst = is_multicast_ether_addr(pattrib->ra);
+
+ if (pattrib->psta) {
+ psta = pattrib->psta;
+ } else {
+ DBG_8723A("%s, call rtw_get_stainfo23a()\n", __func__);
+ if (bmcst) {
+ psta = rtw_get_bcmc_stainfo23a(padapter);
+ } else {
+ psta = rtw_get_stainfo23a(&padapter->stapriv, pattrib->ra);
+ }
+ }
+
+ if (psta == NULL) {
+ DBG_8723A("%s, psta == NUL\n", __func__);
+ return _FAIL;
+ }
+
+ if (!(psta->state &_FW_LINKED)) {
+ DBG_8723A("%s, psta->state(0x%x) != _FW_LINKED\n", __func__, psta->state);
+ return _FAIL;
+ }
+
+ memset(hdr, 0, WLANHDR_OFFSET);
+
+ pwlanhdr->frame_control = cpu_to_le16(pattrib->type);
+
+ if (pattrib->type & IEEE80211_FTYPE_DATA) {
+ if (check_fwstate(pmlmepriv, WIFI_STATION_STATE)) {
+ /* to_ds = 1, fr_ds = 0; */
+ /* Data transfer to AP */
+ pwlanhdr->frame_control |=
+ cpu_to_le16(IEEE80211_FCTL_TODS);
+ ether_addr_copy(pwlanhdr->addr1, get_bssid(pmlmepriv));
+ ether_addr_copy(pwlanhdr->addr2, pattrib->src);
+ ether_addr_copy(pwlanhdr->addr3, pattrib->dst);
+
+ if (pmlmepriv->qos_option)
+ qos_option = true;
+
+ } else if (check_fwstate(pmlmepriv, WIFI_AP_STATE)) {
+ /* to_ds = 0, fr_ds = 1; */
+ pwlanhdr->frame_control |=
+ cpu_to_le16(IEEE80211_FCTL_FROMDS);
+ ether_addr_copy(pwlanhdr->addr1, pattrib->dst);
+ ether_addr_copy(pwlanhdr->addr2, get_bssid(pmlmepriv));
+ ether_addr_copy(pwlanhdr->addr3, pattrib->src);
+
+ if (psta->qos_option)
+ qos_option = true;
+ } else if (check_fwstate(pmlmepriv, WIFI_ADHOC_STATE) ||
+ check_fwstate(pmlmepriv, WIFI_ADHOC_MASTER_STATE)) {
+ ether_addr_copy(pwlanhdr->addr1, pattrib->dst);
+ ether_addr_copy(pwlanhdr->addr2, pattrib->src);
+ ether_addr_copy(pwlanhdr->addr3, get_bssid(pmlmepriv));
+
+ if (psta->qos_option)
+ qos_option = true;
+ }
+ else {
+ RT_TRACE(_module_rtl871x_xmit_c_, _drv_err_,
+ "fw_state:%x is not allowed to xmit frame\n",
+ get_fwstate(pmlmepriv));
+ res = _FAIL;
+ goto exit;
+ }
+ if (pattrib->mdata)
+ pwlanhdr->frame_control |=
+ cpu_to_le16(IEEE80211_FCTL_MOREDATA);
+ if (pattrib->encrypt)
+ pwlanhdr->frame_control |=
+ cpu_to_le16(IEEE80211_FCTL_PROTECTED);
+ if (qos_option) {
+ qoshdr = (struct ieee80211_qos_hdr *)hdr;
+
+ qoshdr->qos_ctrl = cpu_to_le16(
+ pattrib->priority & IEEE80211_QOS_CTL_TID_MASK);
+
+ qoshdr->qos_ctrl |= cpu_to_le16(
+ (pattrib->ack_policy << 5) &
+ IEEE80211_QOS_CTL_ACK_POLICY_MASK);
+
+ if (pattrib->eosp)
+ qoshdr->qos_ctrl |=
+ cpu_to_le16(IEEE80211_QOS_CTL_EOSP);
+ }
+ /* TODO: fill HT Control Field */
+
+ /* Update Seq Num will be handled by f/w */
+ if (psta) {
+ psta->sta_xmitpriv.txseq_tid[pattrib->priority]++;
+ psta->sta_xmitpriv.txseq_tid[pattrib->priority] &= 0xFFF;
+ pattrib->seqnum = psta->sta_xmitpriv.txseq_tid[pattrib->priority];
+ /* We dont need to worry about frag bits here */
+ pwlanhdr->seq_ctrl = cpu_to_le16(IEEE80211_SN_TO_SEQ(
+ pattrib->seqnum));
+ /* check if enable ampdu */
+ if (pattrib->ht_en && psta->htpriv.ampdu_enable) {
+ if (pattrib->priority >= 16)
+ printk(KERN_WARNING "%s: Invalid "
+ "pattrib->priority %i\n",
+ __func__, pattrib->priority);
+ if (psta->htpriv.agg_enable_bitmap &
+ BIT(pattrib->priority))
+ pattrib->ampdu_en = true;
+ }
+ /* re-check if enable ampdu by BA_starting_seqctrl */
+ if (pattrib->ampdu_en) {
+ u16 tx_seq;
+
+ tx_seq = psta->BA_starting_seqctrl[pattrib->priority & 0x0f];
+
+ /* check BA_starting_seqctrl */
+ if (SN_LESS(pattrib->seqnum, tx_seq)) {
+ /* DBG_8723A("tx ampdu seqnum(%d) < tx_seq(%d)\n", pattrib->seqnum, tx_seq); */
+ pattrib->ampdu_en = false;/* AGG BK */
+ } else if (SN_EQUAL(pattrib->seqnum, tx_seq)) {
+ psta->BA_starting_seqctrl[pattrib->priority & 0x0f] = (tx_seq+1)&0xfff;
+ pattrib->ampdu_en = true;/* AGG EN */
+ } else {
+ /* DBG_8723A("tx ampdu over run\n"); */
+ psta->BA_starting_seqctrl[pattrib->priority & 0x0f] = (pattrib->seqnum+1)&0xfff;
+ pattrib->ampdu_en = true;/* AGG EN */
+ }
+ }
+ }
+ }
+exit:
+ return res;
+}
+
+s32 rtw_txframes_pending23a(struct rtw_adapter *padapter)
+{
+ struct xmit_priv *pxmitpriv = &padapter->xmitpriv;
+
+ return (!list_empty(&pxmitpriv->be_pending.queue)) ||
+ (!list_empty(&pxmitpriv->bk_pending.queue)) ||
+ (!list_empty(&pxmitpriv->vi_pending.queue)) ||
+ (!list_empty(&pxmitpriv->vo_pending.queue));
+}
+
+s32 rtw_txframes_sta_ac_pending23a(struct rtw_adapter *padapter,
+ struct pkt_attrib *pattrib)
+{
+ struct sta_info *psta;
+ struct tx_servq *ptxservq;
+ int priority = pattrib->priority;
+
+ if (pattrib->psta) {
+ psta = pattrib->psta;
+ } else {
+ DBG_8723A("%s, call rtw_get_stainfo23a()\n", __func__);
+ psta = rtw_get_stainfo23a(&padapter->stapriv, &pattrib->ra[0]);
+ }
+ if (psta == NULL) {
+ DBG_8723A("%s, psta == NUL\n", __func__);
+ return 0;
+ }
+ if (!(psta->state &_FW_LINKED)) {
+ DBG_8723A("%s, psta->state(0x%x) != _FW_LINKED\n", __func__,
+ psta->state);
+ return 0;
+ }
+ switch (priority) {
+ case 1:
+ case 2:
+ ptxservq = &psta->sta_xmitpriv.bk_q;
+ break;
+ case 4:
+ case 5:
+ ptxservq = &psta->sta_xmitpriv.vi_q;
+ break;
+ case 6:
+ case 7:
+ ptxservq = &psta->sta_xmitpriv.vo_q;
+ break;
+ case 0:
+ case 3:
+ default:
+ ptxservq = &psta->sta_xmitpriv.be_q;
+ break;
+ }
+ return ptxservq->qcnt;
+}
+
+/* Logical Link Control(LLC) SubNetwork Attachment Point(SNAP) header
+ * IEEE LLC/SNAP header contains 8 octets
+ * First 3 octets comprise the LLC portion
+ * SNAP portion, 5 octets, is divided into two fields:
+ * Organizationally Unique Identifier(OUI), 3 octets,
+ * type, defined by that organization, 2 octets.
+ */
+static int rtw_put_snap(u8 *data, u16 h_proto)
+{
+ if (h_proto == ETH_P_IPX || h_proto == ETH_P_AARP)
+ ether_addr_copy(data, bridge_tunnel_header);
+ else
+ ether_addr_copy(data, rfc1042_header);
+
+ data += ETH_ALEN;
+ put_unaligned_be16(h_proto, data);
+ return ETH_ALEN + sizeof(u16);
+}
+
+/*
+
+This sub-routine will perform all the following:
+
+1. remove 802.3 header.
+2. create wlan_header, based on the info in pxmitframe
+3. append sta's iv/ext-iv
+4. append LLC
+5. move frag chunk from pframe to pxmitframe->mem
+6. apply sw-encrypt, if necessary.
+
+*/
+int rtw_xmitframe_coalesce23a(struct rtw_adapter *padapter, struct sk_buff *skb,
+ struct xmit_frame *pxmitframe)
+{
+ struct sta_info *psta;
+ struct xmit_priv *pxmitpriv = &padapter->xmitpriv;
+ struct pkt_attrib *pattrib = &pxmitframe->attrib;
+ struct ieee80211_hdr *hdr;
+ s32 frg_inx, frg_len, mpdu_len, llc_sz, mem_sz;
+ u8 *pframe, *mem_start;
+ u8 hw_hdr_offset;
+ u8 *pbuf_start;
+ u8 *pdata = skb->data;
+ int data_len = skb->len;
+ s32 bmcst = is_multicast_ether_addr(pattrib->ra);
+ int res = _SUCCESS;
+
+ if (pattrib->psta)
+ psta = pattrib->psta;
+ else {
+ DBG_8723A("%s, call rtw_get_stainfo23a()\n", __func__);
+ psta = rtw_get_stainfo23a(&padapter->stapriv, pattrib->ra);
+ }
+
+ if (!psta) {
+ DBG_8723A("%s, psta == NUL\n", __func__);
+ return _FAIL;
+ }
+
+ if (!(psta->state &_FW_LINKED)) {
+ DBG_8723A("%s, psta->state(0x%x) != _FW_LINKED\n",
+ __func__, psta->state);
+ return _FAIL;
+ }
+
+ if (!pxmitframe->buf_addr) {
+ DBG_8723A("==> %s buf_addr == NULL\n", __func__);
+ return _FAIL;
+ }
+
+ pbuf_start = pxmitframe->buf_addr;
+
+ hw_hdr_offset = TXDESC_OFFSET;
+
+ mem_start = pbuf_start + hw_hdr_offset;
+
+ if (rtw_make_wlanhdr(padapter, mem_start, pattrib) == _FAIL) {
+ RT_TRACE(_module_rtl871x_xmit_c_, _drv_err_,
+ "%s: rtw_make_wlanhdr fail; drop pkt\n", __func__);
+ res = _FAIL;
+ goto exit;
+ }
+
+ pdata += pattrib->pkt_hdrlen;
+ data_len -= pattrib->pkt_hdrlen;
+
+ frg_inx = 0;
+ frg_len = pxmitpriv->frag_len - 4;/* 2346-4 = 2342 */
+
+ while (1) {
+ llc_sz = 0;
+
+ mpdu_len = frg_len;
+
+ pframe = mem_start;
+ hdr = (struct ieee80211_hdr *)mem_start;
+
+ pframe += pattrib->hdrlen;
+ mpdu_len -= pattrib->hdrlen;
+
+ /* adding icv, if necessary... */
+ if (pattrib->iv_len) {
+ if (psta) {
+ switch (pattrib->encrypt) {
+ case WLAN_CIPHER_SUITE_WEP40:
+ case WLAN_CIPHER_SUITE_WEP104:
+ WEP_IV(pattrib->iv, psta->dot11txpn,
+ pattrib->key_idx);
+ break;
+ case WLAN_CIPHER_SUITE_TKIP:
+ if (bmcst)
+ TKIP_IV(pattrib->iv,
+ psta->dot11txpn,
+ pattrib->key_idx);
+ else
+ TKIP_IV(pattrib->iv,
+ psta->dot11txpn, 0);
+ break;
+ case WLAN_CIPHER_SUITE_CCMP:
+ if (bmcst)
+ AES_IV(pattrib->iv,
+ psta->dot11txpn,
+ pattrib->key_idx);
+ else
+ AES_IV(pattrib->iv,
+ psta->dot11txpn, 0);
+ break;
+ }
+ }
+
+ memcpy(pframe, pattrib->iv, pattrib->iv_len);
+
+ RT_TRACE(_module_rtl871x_xmit_c_, _drv_notice_,
+ "rtw_xmiaframe_coalesce23a: keyid =%d pattrib->iv[3]=%.2x pframe =%.2x %.2x %.2x %.2x\n",
+ padapter->securitypriv.dot11PrivacyKeyIndex,
+ pattrib->iv[3], *pframe, *(pframe+1),
+ *(pframe+2), *(pframe+3));
+ pframe += pattrib->iv_len;
+ mpdu_len -= pattrib->iv_len;
+ }
+ if (frg_inx == 0) {
+ llc_sz = rtw_put_snap(pframe, pattrib->ether_type);
+ pframe += llc_sz;
+ mpdu_len -= llc_sz;
+ }
+
+ if (pattrib->icv_len > 0 && pattrib->bswenc)
+ mpdu_len -= pattrib->icv_len;
+
+ if (bmcst)
+ /* don't do fragment to broadcast/multicast packets */
+ mem_sz = min_t(s32, data_len, pattrib->pktlen);
+ else
+ mem_sz = min_t(s32, data_len, mpdu_len);
+
+ memcpy(pframe, pdata, mem_sz);
+
+ pframe += mem_sz;
+ pdata += mem_sz;
+ data_len -= mem_sz;
+
+ if ((pattrib->icv_len >0) && (pattrib->bswenc)) {
+ memcpy(pframe, pattrib->icv, pattrib->icv_len);
+ pframe += pattrib->icv_len;
+ }
+
+ frg_inx++;
+
+ if (bmcst || data_len <= 0) {
+ pattrib->nr_frags = frg_inx;
+
+ pattrib->last_txcmdsz = pattrib->hdrlen +
+ pattrib->iv_len +
+ ((pattrib->nr_frags == 1) ?
+ llc_sz : 0) +
+ ((pattrib->bswenc) ?
+ pattrib->icv_len : 0) + mem_sz;
+ hdr->frame_control &=
+ ~cpu_to_le16(IEEE80211_FCTL_MOREFRAGS);
+
+ break;
+ } else {
+ RT_TRACE(_module_rtl871x_xmit_c_, _drv_err_,
+ "%s: There're still something in packet!\n",
+ __func__);
+ }
+ hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_MOREFRAGS);
+
+ mem_start = PTR_ALIGN(pframe, 4) + hw_hdr_offset;
+ memcpy(mem_start, pbuf_start + hw_hdr_offset, pattrib->hdrlen);
+ }
+
+ if (xmitframe_addmic(padapter, pxmitframe) == _FAIL) {
+ RT_TRACE(_module_rtl871x_xmit_c_, _drv_err_,
+ "xmitframe_addmic(padapter, pxmitframe) == _FAIL\n");
+ DBG_8723A("xmitframe_addmic(padapter, pxmitframe) == _FAIL\n");
+ res = _FAIL;
+ goto exit;
+ }
+
+ xmitframe_swencrypt(padapter, pxmitframe);
+
+ if (bmcst == false)
+ update_attrib_vcs_info(padapter, pxmitframe);
+ else
+ pattrib->vcs_mode = NONE_VCS;
+
+exit:
+ return res;
+}
+
+void rtw_update_protection23a(struct rtw_adapter *padapter, u8 *ie, uint ie_len)
+{
+ struct xmit_priv *pxmitpriv = &padapter->xmitpriv;
+ struct registry_priv *pregistrypriv = &padapter->registrypriv;
+ uint protection;
+ const u8 *p;
+
+ switch (pregistrypriv->vrtl_carrier_sense) {
+ case DISABLE_VCS:
+ pxmitpriv->vcs = NONE_VCS;
+ break;
+ case ENABLE_VCS:
+ break;
+ case AUTO_VCS:
+ default:
+ p = cfg80211_find_ie(WLAN_EID_ERP_INFO, ie, ie_len);
+ if (!p)
+ pxmitpriv->vcs = NONE_VCS;
+ else {
+ protection = (*(p + 2)) & BIT(1);
+ if (protection) {
+ if (pregistrypriv->vcs_type == RTS_CTS)
+ pxmitpriv->vcs = RTS_CTS;
+ else
+ pxmitpriv->vcs = CTS_TO_SELF;
+ } else {
+ pxmitpriv->vcs = NONE_VCS;
+ }
+ }
+ break;
+ }
+}
+
+void rtw_count_tx_stats23a(struct rtw_adapter *padapter, struct xmit_frame *pxmitframe, int sz)
+{
+ struct sta_info *psta = NULL;
+ struct stainfo_stats *pstats = NULL;
+ struct xmit_priv *pxmitpriv = &padapter->xmitpriv;
+ struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
+
+ if (pxmitframe->frame_tag == DATA_FRAMETAG) {
+ pxmitpriv->tx_bytes += sz;
+ pmlmepriv->LinkDetectInfo.NumTxOkInPeriod++;
+
+ psta = pxmitframe->attrib.psta;
+ if (psta) {
+ pstats = &psta->sta_stats;
+ pstats->tx_pkts++;
+ pstats->tx_bytes += sz;
+ }
+ }
+}
+
+struct xmit_buf *rtw_alloc_xmitbuf23a_ext(struct xmit_priv *pxmitpriv)
+{
+ unsigned long irqL;
+ struct xmit_buf *pxmitbuf = NULL;
+ struct list_head *phead;
+ struct rtw_queue *pfree_queue = &pxmitpriv->free_xmit_extbuf_queue;
+
+ spin_lock_irqsave(&pfree_queue->lock, irqL);
+
+ phead = get_list_head(pfree_queue);
+
+ if (!list_empty(phead)) {
+ pxmitbuf = list_first_entry(phead, struct xmit_buf, list);
+
+ list_del_init(&pxmitbuf->list);
+
+ pxmitpriv->free_xmit_extbuf_cnt--;
+ pxmitbuf->priv_data = NULL;
+ pxmitbuf->ext_tag = true;
+
+ if (pxmitbuf->sctx) {
+ DBG_8723A("%s pxmitbuf->sctx is not NULL\n", __func__);
+ rtw23a_sctx_done_err(&pxmitbuf->sctx, RTW_SCTX_DONE_BUF_ALLOC);
+ }
+ }
+
+ spin_unlock_irqrestore(&pfree_queue->lock, irqL);
+
+ return pxmitbuf;
+}
+
+int rtw_free_xmitbuf_ext23a(struct xmit_priv *pxmitpriv,
+ struct xmit_buf *pxmitbuf)
+{
+ unsigned long irqL;
+ struct rtw_queue *pfree_queue = &pxmitpriv->free_xmit_extbuf_queue;
+
+ if (pxmitbuf == NULL)
+ return _FAIL;
+
+ spin_lock_irqsave(&pfree_queue->lock, irqL);
+
+ list_del_init(&pxmitbuf->list);
+
+ list_add_tail(&pxmitbuf->list, get_list_head(pfree_queue));
+ pxmitpriv->free_xmit_extbuf_cnt++;
+
+ spin_unlock_irqrestore(&pfree_queue->lock, irqL);
+
+ return _SUCCESS;
+}
+
+struct xmit_buf *rtw_alloc_xmitbuf23a(struct xmit_priv *pxmitpriv)
+{
+ unsigned long irqL;
+ struct xmit_buf *pxmitbuf = NULL;
+ struct list_head *phead;
+ struct rtw_queue *pfree_xmitbuf_queue = &pxmitpriv->free_xmitbuf_queue;
+
+ /* DBG_8723A("+rtw_alloc_xmitbuf23a\n"); */
+
+ spin_lock_irqsave(&pfree_xmitbuf_queue->lock, irqL);
+
+ phead = get_list_head(pfree_xmitbuf_queue);
+
+ if (!list_empty(phead)) {
+ pxmitbuf = list_first_entry(phead, struct xmit_buf, list);
+
+ list_del_init(&pxmitbuf->list);
+
+ pxmitpriv->free_xmitbuf_cnt--;
+ pxmitbuf->priv_data = NULL;
+ pxmitbuf->ext_tag = false;
+ pxmitbuf->flags = XMIT_VO_QUEUE;
+
+ if (pxmitbuf->sctx) {
+ DBG_8723A("%s pxmitbuf->sctx is not NULL\n", __func__);
+ rtw23a_sctx_done_err(&pxmitbuf->sctx, RTW_SCTX_DONE_BUF_ALLOC);
+ }
+ }
+
+ spin_unlock_irqrestore(&pfree_xmitbuf_queue->lock, irqL);
+
+ return pxmitbuf;
+}
+
+int rtw_free_xmitbuf23a(struct xmit_priv *pxmitpriv, struct xmit_buf *pxmitbuf)
+{
+ unsigned long irqL;
+ struct rtw_queue *pfree_xmitbuf_queue = &pxmitpriv->free_xmitbuf_queue;
+
+ /* DBG_8723A("+rtw_free_xmitbuf23a\n"); */
+
+ if (pxmitbuf == NULL)
+ return _FAIL;
+
+ if (pxmitbuf->sctx) {
+ DBG_8723A("%s pxmitbuf->sctx is not NULL\n", __func__);
+ rtw23a_sctx_done_err(&pxmitbuf->sctx, RTW_SCTX_DONE_BUF_FREE);
+ }
+
+ if (pxmitbuf->ext_tag) {
+ rtw_free_xmitbuf_ext23a(pxmitpriv, pxmitbuf);
+ } else {
+ spin_lock_irqsave(&pfree_xmitbuf_queue->lock, irqL);
+
+ list_del_init(&pxmitbuf->list);
+
+ list_add_tail(&pxmitbuf->list,
+ get_list_head(pfree_xmitbuf_queue));
+
+ pxmitpriv->free_xmitbuf_cnt++;
+ spin_unlock_irqrestore(&pfree_xmitbuf_queue->lock, irqL);
+ }
+
+ return _SUCCESS;
+}
+
+static void rtw_init_xmitframe(struct xmit_frame *pxframe)
+{
+ if (pxframe != NULL) {
+ /* default value setting */
+ pxframe->buf_addr = NULL;
+ pxframe->pxmitbuf = NULL;
+
+ memset(&pxframe->attrib, 0, sizeof(struct pkt_attrib));
+ /* pxframe->attrib.psta = NULL; */
+
+ pxframe->frame_tag = DATA_FRAMETAG;
+
+ pxframe->pkt = NULL;
+ pxframe->pkt_offset = 1;/* default use pkt_offset to fill tx desc */
+
+ pxframe->ack_report = 0;
+ }
+}
+
+/*
+Calling context:
+1. OS_TXENTRY
+2. RXENTRY (rx_thread or RX_ISR/RX_CallBack)
+
+If we turn on USE_RXTHREAD, then, no need for critical section.
+Otherwise, we must use _enter/_exit critical to protect free_xmit_queue...
+
+Must be very very cautious...
+
+*/
+static struct xmit_frame *rtw_alloc_xmitframe(struct xmit_priv *pxmitpriv)
+{
+ struct xmit_frame *pxframe = NULL;
+ struct list_head *plist, *phead;
+ struct rtw_queue *pfree_xmit_queue = &pxmitpriv->free_xmit_queue;
+
+ spin_lock_bh(&pfree_xmit_queue->lock);
+
+ if (list_empty(&pfree_xmit_queue->queue)) {
+ RT_TRACE(_module_rtl871x_xmit_c_, _drv_info_,
+ "rtw_alloc_xmitframe:%d\n",
+ pxmitpriv->free_xmitframe_cnt);
+ pxframe = NULL;
+ } else {
+ phead = get_list_head(pfree_xmit_queue);
+
+ plist = phead->next;
+
+ pxframe = container_of(plist, struct xmit_frame, list);
+
+ list_del_init(&pxframe->list);
+ pxmitpriv->free_xmitframe_cnt--;
+ RT_TRACE(_module_rtl871x_xmit_c_, _drv_info_,
+ "rtw_alloc_xmitframe():free_xmitframe_cnt =%d\n",
+ pxmitpriv->free_xmitframe_cnt);
+ }
+
+ spin_unlock_bh(&pfree_xmit_queue->lock);
+
+ rtw_init_xmitframe(pxframe);
+
+ return pxframe;
+}
+
+struct xmit_frame *rtw_alloc_xmitframe23a_ext(struct xmit_priv *pxmitpriv)
+{
+ struct xmit_frame *pxframe = NULL;
+ struct list_head *plist, *phead;
+ struct rtw_queue *queue = &pxmitpriv->free_xframe_ext_queue;
+
+ spin_lock_bh(&queue->lock);
+
+ if (list_empty(&queue->queue)) {
+ RT_TRACE(_module_rtl871x_xmit_c_, _drv_info_,
+ "rtw_alloc_xmitframe23a_ext:%d\n",
+ pxmitpriv->free_xframe_ext_cnt);
+ pxframe = NULL;
+ } else {
+ phead = get_list_head(queue);
+ plist = phead->next;
+ pxframe = container_of(plist, struct xmit_frame, list);
+
+ list_del_init(&pxframe->list);
+ pxmitpriv->free_xframe_ext_cnt--;
+ RT_TRACE(_module_rtl871x_xmit_c_, _drv_info_,
+ "rtw_alloc_xmitframe23a_ext():free_xmitframe_cnt =%d\n",
+ pxmitpriv->free_xframe_ext_cnt);
+ }
+
+ spin_unlock_bh(&queue->lock);
+
+ rtw_init_xmitframe(pxframe);
+
+ return pxframe;
+}
+
+s32 rtw_free_xmitframe23a(struct xmit_priv *pxmitpriv, struct xmit_frame *pxmitframe)
+{
+ struct rtw_queue *queue = NULL;
+ struct rtw_adapter *padapter = pxmitpriv->adapter;
+ struct sk_buff *pndis_pkt = NULL;
+
+ if (pxmitframe == NULL) {
+ RT_TRACE(_module_rtl871x_xmit_c_, _drv_err_,
+ "====== rtw_free_xmitframe23a():pxmitframe == NULL!!!!!!!!!!\n");
+ goto exit;
+ }
+
+ if (pxmitframe->pkt) {
+ pndis_pkt = pxmitframe->pkt;
+ pxmitframe->pkt = NULL;
+ }
+
+ if (pxmitframe->ext_tag == 0)
+ queue = &pxmitpriv->free_xmit_queue;
+ else if (pxmitframe->ext_tag == 1)
+ queue = &pxmitpriv->free_xframe_ext_queue;
+
+ if (!queue)
+ goto check_pkt_complete;
+ spin_lock_bh(&queue->lock);
+
+ list_del_init(&pxmitframe->list);
+ list_add_tail(&pxmitframe->list, get_list_head(queue));
+ if (pxmitframe->ext_tag == 0) {
+ pxmitpriv->free_xmitframe_cnt++;
+ RT_TRACE(_module_rtl871x_xmit_c_, _drv_debug_,
+ "rtw_free_xmitframe23a():free_xmitframe_cnt =%d\n",
+ pxmitpriv->free_xmitframe_cnt);
+ } else if (pxmitframe->ext_tag == 1) {
+ pxmitpriv->free_xframe_ext_cnt++;
+ RT_TRACE(_module_rtl871x_xmit_c_, _drv_debug_,
+ "rtw_free_xmitframe23a():free_xframe_ext_cnt =%d\n",
+ pxmitpriv->free_xframe_ext_cnt);
+ }
+
+ spin_unlock_bh(&queue->lock);
+
+check_pkt_complete:
+
+ if (pndis_pkt)
+ rtw_os_pkt_complete23a(padapter, pndis_pkt);
+
+exit:
+
+ return _SUCCESS;
+}
+
+void rtw_free_xmitframe_queue23a(struct xmit_priv *pxmitpriv,
+ struct rtw_queue *pframequeue)
+{
+ struct list_head *plist, *phead, *ptmp;
+ struct xmit_frame *pxmitframe;
+
+ spin_lock_bh(&pframequeue->lock);
+
+ phead = get_list_head(pframequeue);
+
+ list_for_each_safe(plist, ptmp, phead) {
+ pxmitframe = container_of(plist, struct xmit_frame, list);
+
+ rtw_free_xmitframe23a(pxmitpriv, pxmitframe);
+ }
+ spin_unlock_bh(&pframequeue->lock);
+
+}
+
+int rtw_xmitframe_enqueue23a(struct rtw_adapter *padapter,
+ struct xmit_frame *pxmitframe)
+{
+ if (rtw_xmit23a_classifier(padapter, pxmitframe) == _FAIL) {
+ RT_TRACE(_module_rtl871x_xmit_c_, _drv_err_,
+ "rtw_xmitframe_enqueue23a: drop xmit pkt for classifier fail\n");
+ return _FAIL;
+ }
+
+ return _SUCCESS;
+}
+
+static struct xmit_frame *
+dequeue_one_xmitframe(struct xmit_priv *pxmitpriv, struct hw_xmit *phwxmit,
+ struct tx_servq *ptxservq, struct rtw_queue *pframe_queue)
+{
+ struct list_head *phead;
+ struct xmit_frame *pxmitframe = NULL;
+
+ phead = get_list_head(pframe_queue);
+
+ if (!list_empty(phead)) {
+ pxmitframe = list_first_entry(phead, struct xmit_frame, list);
+ list_del_init(&pxmitframe->list);
+ ptxservq->qcnt--;
+ }
+ return pxmitframe;
+}
+
+struct xmit_frame *
+rtw_dequeue_xframe23a(struct xmit_priv *pxmitpriv, struct hw_xmit *phwxmit_i,
+ int entry)
+{
+ struct list_head *sta_plist, *sta_phead, *ptmp;
+ struct hw_xmit *phwxmit;
+ struct tx_servq *ptxservq = NULL;
+ struct rtw_queue *pframe_queue = NULL;
+ struct xmit_frame *pxmitframe = NULL;
+ struct rtw_adapter *padapter = pxmitpriv->adapter;
+ struct registry_priv *pregpriv = &padapter->registrypriv;
+ int i, inx[4];
+
+ inx[0] = 0;
+ inx[1] = 1;
+ inx[2] = 2;
+ inx[3] = 3;
+ if (pregpriv->wifi_spec == 1) {
+ int j;
+
+ for (j = 0; j < 4; j++)
+ inx[j] = pxmitpriv->wmm_para_seq[j];
+ }
+
+ spin_lock_bh(&pxmitpriv->lock);
+
+ for (i = 0; i < entry; i++) {
+ phwxmit = phwxmit_i + inx[i];
+
+ sta_phead = get_list_head(phwxmit->sta_queue);
+
+ list_for_each_safe(sta_plist, ptmp, sta_phead) {
+ ptxservq = container_of(sta_plist, struct tx_servq,
+ tx_pending);
+
+ pframe_queue = &ptxservq->sta_pending;
+
+ pxmitframe = dequeue_one_xmitframe(pxmitpriv, phwxmit, ptxservq, pframe_queue);
+
+ if (pxmitframe) {
+ phwxmit->accnt--;
+
+ /* Remove sta node when there is no pending packets. */
+ /* must be done after get_next and
+ before break */
+ if (list_empty(&pframe_queue->queue))
+ list_del_init(&ptxservq->tx_pending);
+ goto exit;
+ }
+ }
+ }
+exit:
+ spin_unlock_bh(&pxmitpriv->lock);
+ return pxmitframe;
+}
+
+struct tx_servq *rtw_get_sta_pending23a(struct rtw_adapter *padapter, struct sta_info *psta, int up, u8 *ac)
+{
+ struct tx_servq *ptxservq = NULL;
+
+ switch (up) {
+ case 1:
+ case 2:
+ ptxservq = &psta->sta_xmitpriv.bk_q;
+ *(ac) = 3;
+ RT_TRACE(_module_rtl871x_xmit_c_, _drv_info_,
+ "rtw_get_sta_pending23a : BK\n");
+ break;
+ case 4:
+ case 5:
+ ptxservq = &psta->sta_xmitpriv.vi_q;
+ *(ac) = 1;
+ RT_TRACE(_module_rtl871x_xmit_c_, _drv_info_,
+ "rtw_get_sta_pending23a : VI\n");
+ break;
+ case 6:
+ case 7:
+ ptxservq = &psta->sta_xmitpriv.vo_q;
+ *(ac) = 0;
+ RT_TRACE(_module_rtl871x_xmit_c_, _drv_info_,
+ "rtw_get_sta_pending23a : VO\n");
+ break;
+ case 0:
+ case 3:
+ default:
+ ptxservq = &psta->sta_xmitpriv.be_q;
+ *(ac) = 2;
+ RT_TRACE(_module_rtl871x_xmit_c_, _drv_info_,
+ "rtw_get_sta_pending23a : BE\n");
+ break;
+ }
+ return ptxservq;
+}
+
+/*
+ * Will enqueue pxmitframe to the proper queue,
+ * and indicate it to xx_pending list.....
+ */
+int rtw_xmit23a_classifier(struct rtw_adapter *padapter,
+ struct xmit_frame *pxmitframe)
+{
+ struct sta_info *psta;
+ struct tx_servq *ptxservq;
+ struct pkt_attrib *pattrib = &pxmitframe->attrib;
+ struct sta_priv *pstapriv = &padapter->stapriv;
+ struct hw_xmit *phwxmits = padapter->xmitpriv.hwxmits;
+ u8 ac_index;
+ int res = _SUCCESS;
+
+ if (pattrib->psta) {
+ psta = pattrib->psta;
+ } else {
+ DBG_8723A("%s, call rtw_get_stainfo23a()\n", __func__);
+ psta = rtw_get_stainfo23a(pstapriv, pattrib->ra);
+ }
+ if (psta == NULL) {
+ res = _FAIL;
+ DBG_8723A("rtw_xmit23a_classifier: psta == NULL\n");
+ RT_TRACE(_module_rtl871x_xmit_c_, _drv_err_,
+ "rtw_xmit23a_classifier: psta == NULL\n");
+ goto exit;
+ }
+ if (!(psta->state & _FW_LINKED)) {
+ DBG_8723A("%s, psta->state(0x%x) != _FW_LINKED\n", __func__,
+ psta->state);
+ return _FAIL;
+ }
+ ptxservq = rtw_get_sta_pending23a(padapter, psta, pattrib->priority,
+ (u8 *)(&ac_index));
+
+ if (list_empty(&ptxservq->tx_pending)) {
+ list_add_tail(&ptxservq->tx_pending,
+ get_list_head(phwxmits[ac_index].sta_queue));
+ }
+
+ list_add_tail(&pxmitframe->list, get_list_head(&ptxservq->sta_pending));
+ ptxservq->qcnt++;
+ phwxmits[ac_index].accnt++;
+exit:
+ return res;
+}
+
+void rtw_alloc_hwxmits23a(struct rtw_adapter *padapter)
+{
+ struct hw_xmit *hwxmits;
+ struct xmit_priv *pxmitpriv = &padapter->xmitpriv;
+ int size;
+
+ pxmitpriv->hwxmit_entry = HWXMIT_ENTRY;
+
+ size = sizeof(struct hw_xmit) * (pxmitpriv->hwxmit_entry + 1);
+ pxmitpriv->hwxmits = kzalloc(size, GFP_KERNEL);
+
+ hwxmits = pxmitpriv->hwxmits;
+
+ if (pxmitpriv->hwxmit_entry == 5) {
+ /* pxmitpriv->bmc_txqueue.head = 0; */
+ /* hwxmits[0] .phwtxqueue = &pxmitpriv->bmc_txqueue; */
+ hwxmits[0] .sta_queue = &pxmitpriv->bm_pending;
+
+ /* pxmitpriv->vo_txqueue.head = 0; */
+ /* hwxmits[1] .phwtxqueue = &pxmitpriv->vo_txqueue; */
+ hwxmits[1] .sta_queue = &pxmitpriv->vo_pending;
+
+ /* pxmitpriv->vi_txqueue.head = 0; */
+ /* hwxmits[2] .phwtxqueue = &pxmitpriv->vi_txqueue; */
+ hwxmits[2] .sta_queue = &pxmitpriv->vi_pending;
+
+ /* pxmitpriv->bk_txqueue.head = 0; */
+ /* hwxmits[3] .phwtxqueue = &pxmitpriv->bk_txqueue; */
+ hwxmits[3] .sta_queue = &pxmitpriv->bk_pending;
+
+ /* pxmitpriv->be_txqueue.head = 0; */
+ /* hwxmits[4] .phwtxqueue = &pxmitpriv->be_txqueue; */
+ hwxmits[4] .sta_queue = &pxmitpriv->be_pending;
+
+ } else if (pxmitpriv->hwxmit_entry == 4) {
+
+ /* pxmitpriv->vo_txqueue.head = 0; */
+ /* hwxmits[0] .phwtxqueue = &pxmitpriv->vo_txqueue; */
+ hwxmits[0] .sta_queue = &pxmitpriv->vo_pending;
+
+ /* pxmitpriv->vi_txqueue.head = 0; */
+ /* hwxmits[1] .phwtxqueue = &pxmitpriv->vi_txqueue; */
+ hwxmits[1] .sta_queue = &pxmitpriv->vi_pending;
+
+ /* pxmitpriv->be_txqueue.head = 0; */
+ /* hwxmits[2] .phwtxqueue = &pxmitpriv->be_txqueue; */
+ hwxmits[2] .sta_queue = &pxmitpriv->be_pending;
+
+ /* pxmitpriv->bk_txqueue.head = 0; */
+ /* hwxmits[3] .phwtxqueue = &pxmitpriv->bk_txqueue; */
+ hwxmits[3] .sta_queue = &pxmitpriv->bk_pending;
+ } else {
+
+ }
+}
+
+void rtw_free_hwxmits23a(struct rtw_adapter *padapter)
+{
+ struct hw_xmit *hwxmits;
+ struct xmit_priv *pxmitpriv = &padapter->xmitpriv;
+
+ hwxmits = pxmitpriv->hwxmits;
+ kfree(hwxmits);
+}
+
+void rtw_init_hwxmits23a(struct hw_xmit *phwxmit, int entry)
+{
+ int i;
+
+ for (i = 0; i < entry; i++, phwxmit++)
+ phwxmit->accnt = 0;
+}
+
+u32 rtw_get_ff_hwaddr23a(struct xmit_frame *pxmitframe)
+{
+ u32 addr;
+ struct pkt_attrib *pattrib = &pxmitframe->attrib;
+
+ switch (pattrib->qsel) {
+ case 0:
+ case 3:
+ addr = BE_QUEUE_INX;
+ break;
+ case 1:
+ case 2:
+ addr = BK_QUEUE_INX;
+ break;
+ case 4:
+ case 5:
+ addr = VI_QUEUE_INX;
+ break;
+ case 6:
+ case 7:
+ addr = VO_QUEUE_INX;
+ break;
+ case 0x10:
+ addr = BCN_QUEUE_INX;
+ break;
+ case 0x11:/* BC/MC in PS (HIQ) */
+ addr = HIGH_QUEUE_INX;
+ break;
+ case 0x12:
+ default:
+ addr = MGT_QUEUE_INX;
+ break;
+ }
+
+ return addr;
+}
+
+/*
+ * The main transmit(tx) entry
+ *
+ * Return
+ * 1 enqueue
+ * 0 success, hardware will handle this xmit frame(packet)
+ * <0 fail
+ */
+int rtw_xmit23a(struct rtw_adapter *padapter, struct sk_buff *skb)
+{
+ struct xmit_priv *pxmitpriv = &padapter->xmitpriv;
+ struct xmit_frame *pxmitframe = NULL;
+ int res;
+
+ pxmitframe = rtw_alloc_xmitframe(pxmitpriv);
+
+ if (pxmitframe == NULL) {
+ RT_TRACE(_module_xmit_osdep_c_, _drv_err_,
+ "rtw_xmit23a: no more pxmitframe\n");
+ return -1;
+ }
+
+ res = update_attrib(padapter, skb, &pxmitframe->attrib);
+
+ if (res == _FAIL) {
+ RT_TRACE(_module_xmit_osdep_c_, _drv_err_,
+ "rtw_xmit23a: update attrib fail\n");
+ rtw_free_xmitframe23a(pxmitpriv, pxmitframe);
+ return -1;
+ }
+ pxmitframe->pkt = skb;
+
+ pxmitframe->attrib.qsel = pxmitframe->attrib.priority;
+
+#ifdef CONFIG_8723AU_AP_MODE
+ spin_lock_bh(&pxmitpriv->lock);
+ if (xmitframe_enqueue_for_sleeping_sta23a(padapter, pxmitframe)) {
+ spin_unlock_bh(&pxmitpriv->lock);
+ return 1;
+ }
+ spin_unlock_bh(&pxmitpriv->lock);
+#endif
+
+ if (rtl8723au_hal_xmit(padapter, pxmitframe) == false)
+ return 1;
+
+ return 0;
+}
+
+#if defined(CONFIG_8723AU_AP_MODE)
+
+int xmitframe_enqueue_for_sleeping_sta23a(struct rtw_adapter *padapter, struct xmit_frame *pxmitframe)
+{
+ int ret = false;
+ struct sta_info *psta = NULL;
+ struct sta_priv *pstapriv = &padapter->stapriv;
+ struct pkt_attrib *pattrib = &pxmitframe->attrib;
+ struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
+ int bmcst = is_multicast_ether_addr(pattrib->ra);
+
+ if (!check_fwstate(pmlmepriv, WIFI_AP_STATE))
+ return ret;
+
+ if (pattrib->psta) {
+ psta = pattrib->psta;
+ } else {
+ DBG_8723A("%s, call rtw_get_stainfo23a()\n", __func__);
+ psta = rtw_get_stainfo23a(pstapriv, pattrib->ra);
+ }
+
+ if (psta == NULL) {
+ DBG_8723A("%s, psta == NUL\n", __func__);
+ return false;
+ }
+
+ if (!(psta->state & _FW_LINKED)) {
+ DBG_8723A("%s, psta->state(0x%x) != _FW_LINKED\n", __func__,
+ psta->state);
+ return false;
+ }
+
+ if (pattrib->triggered == 1) {
+ if (bmcst)
+ pattrib->qsel = 0x11;/* HIQ */
+ return ret;
+ }
+
+ if (bmcst) {
+ spin_lock_bh(&psta->sleep_q.lock);
+
+ if (pstapriv->sta_dz_bitmap) {
+ /* if anyone sta is in ps mode */
+ list_del_init(&pxmitframe->list);
+
+ /* spin_lock_bh(&psta->sleep_q.lock); */
+
+ list_add_tail(&pxmitframe->list, get_list_head(&psta->sleep_q));
+
+ psta->sleepq_len++;
+
+ pstapriv->tim_bitmap |= BIT(0);/* */
+ pstapriv->sta_dz_bitmap |= BIT(0);
+
+ /* DBG_8723A("enqueue, sq_len =%d, tim =%x\n", psta->sleepq_len, pstapriv->tim_bitmap); */
+
+ /* tx bc/mc packets after update bcn */
+ update_beacon23a(padapter, WLAN_EID_TIM, NULL, false);
+
+ /* spin_unlock_bh(&psta->sleep_q.lock); */
+
+ ret = true;
+
+ }
+
+ spin_unlock_bh(&psta->sleep_q.lock);
+
+ return ret;
+
+ }
+
+ spin_lock_bh(&psta->sleep_q.lock);
+
+ if (psta->state&WIFI_SLEEP_STATE) {
+ u8 wmmps_ac = 0;
+
+ if (pstapriv->sta_dz_bitmap & CHKBIT(psta->aid)) {
+ list_del_init(&pxmitframe->list);
+
+ /* spin_lock_bh(&psta->sleep_q.lock); */
+
+ list_add_tail(&pxmitframe->list, get_list_head(&psta->sleep_q));
+
+ psta->sleepq_len++;
+
+ switch (pattrib->priority) {
+ case 1:
+ case 2:
+ wmmps_ac = psta->uapsd_bk & BIT(0);
+ break;
+ case 4:
+ case 5:
+ wmmps_ac = psta->uapsd_vi & BIT(0);
+ break;
+ case 6:
+ case 7:
+ wmmps_ac = psta->uapsd_vo & BIT(0);
+ break;
+ case 0:
+ case 3:
+ default:
+ wmmps_ac = psta->uapsd_be & BIT(0);
+ break;
+ }
+
+ if (wmmps_ac)
+ psta->sleepq_ac_len++;
+
+ if (((psta->has_legacy_ac) && (!wmmps_ac)) ||
+ ((!psta->has_legacy_ac) && (wmmps_ac))) {
+ pstapriv->tim_bitmap |= CHKBIT(psta->aid);
+
+ if (psta->sleepq_len == 1) {
+ /* update BCN for TIM IE */
+ update_beacon23a(padapter, WLAN_EID_TIM,
+ NULL, false);
+ }
+ }
+
+ /* spin_unlock_bh(&psta->sleep_q.lock); */
+
+ /* if (psta->sleepq_len > (NR_XMITFRAME>>3)) */
+ /* */
+ /* wakeup_sta_to_xmit23a(padapter, psta); */
+ /* */
+
+ ret = true;
+
+ }
+
+ }
+
+ spin_unlock_bh(&psta->sleep_q.lock);
+
+ return ret;
+}
+
+static void
+dequeue_xmitframes_to_sleeping_queue(struct rtw_adapter *padapter,
+ struct sta_info *psta,
+ struct rtw_queue *pframequeue)
+{
+ int ret;
+ struct list_head *plist, *phead, *ptmp;
+ u8 ac_index;
+ struct tx_servq *ptxservq;
+ struct pkt_attrib *pattrib;
+ struct xmit_frame *pxmitframe;
+ struct hw_xmit *phwxmits = padapter->xmitpriv.hwxmits;
+
+ phead = get_list_head(pframequeue);
+
+ list_for_each_safe(plist, ptmp, phead) {
+ pxmitframe = container_of(plist, struct xmit_frame, list);
+
+ ret = xmitframe_enqueue_for_sleeping_sta23a(padapter, pxmitframe);
+
+ if (ret == true) {
+ pattrib = &pxmitframe->attrib;
+
+ ptxservq = rtw_get_sta_pending23a(padapter, psta, pattrib->priority, (u8 *)(&ac_index));
+
+ ptxservq->qcnt--;
+ phwxmits[ac_index].accnt--;
+ } else {
+ /* DBG_8723A("xmitframe_enqueue_for_sleeping_sta23a return false\n"); */
+ }
+ }
+}
+
+void stop_sta_xmit23a(struct rtw_adapter *padapter, struct sta_info *psta)
+{
+ struct sta_info *psta_bmc;
+ struct sta_xmit_priv *pstaxmitpriv;
+ struct sta_priv *pstapriv = &padapter->stapriv;
+ struct xmit_priv *pxmitpriv = &padapter->xmitpriv;
+
+ pstaxmitpriv = &psta->sta_xmitpriv;
+
+ /* for BC/MC Frames */
+ psta_bmc = rtw_get_bcmc_stainfo23a(padapter);
+
+ spin_lock_bh(&pxmitpriv->lock);
+
+ psta->state |= WIFI_SLEEP_STATE;
+
+ pstapriv->sta_dz_bitmap |= CHKBIT(psta->aid);
+
+ dequeue_xmitframes_to_sleeping_queue(padapter, psta, &pstaxmitpriv->vo_q.sta_pending);
+ list_del_init(&pstaxmitpriv->vo_q.tx_pending);
+
+ dequeue_xmitframes_to_sleeping_queue(padapter, psta, &pstaxmitpriv->vi_q.sta_pending);
+ list_del_init(&pstaxmitpriv->vi_q.tx_pending);
+
+ dequeue_xmitframes_to_sleeping_queue(padapter, psta,
+ &pstaxmitpriv->be_q.sta_pending);
+ list_del_init(&pstaxmitpriv->be_q.tx_pending);
+
+ dequeue_xmitframes_to_sleeping_queue(padapter, psta,
+ &pstaxmitpriv->bk_q.sta_pending);
+ list_del_init(&pstaxmitpriv->bk_q.tx_pending);
+
+ /* for BC/MC Frames */
+ pstaxmitpriv = &psta_bmc->sta_xmitpriv;
+ dequeue_xmitframes_to_sleeping_queue(padapter, psta_bmc,
+ &pstaxmitpriv->be_q.sta_pending);
+ list_del_init(&pstaxmitpriv->be_q.tx_pending);
+
+ spin_unlock_bh(&pxmitpriv->lock);
+}
+
+void wakeup_sta_to_xmit23a(struct rtw_adapter *padapter, struct sta_info *psta)
+{
+ u8 update_mask = 0, wmmps_ac = 0;
+ struct sta_info *psta_bmc;
+ struct list_head *plist, *phead, *ptmp;
+ struct xmit_frame *pxmitframe = NULL;
+ struct sta_priv *pstapriv = &padapter->stapriv;
+ struct xmit_priv *pxmitpriv = &padapter->xmitpriv;
+
+ spin_lock_bh(&pxmitpriv->lock);
+
+ phead = get_list_head(&psta->sleep_q);
+
+ list_for_each_safe(plist, ptmp, phead) {
+ pxmitframe = container_of(plist, struct xmit_frame, list);
+ list_del_init(&pxmitframe->list);
+
+ switch (pxmitframe->attrib.priority) {
+ case 1:
+ case 2:
+ wmmps_ac = psta->uapsd_bk & BIT(1);
+ break;
+ case 4:
+ case 5:
+ wmmps_ac = psta->uapsd_vi & BIT(1);
+ break;
+ case 6:
+ case 7:
+ wmmps_ac = psta->uapsd_vo & BIT(1);
+ break;
+ case 0:
+ case 3:
+ default:
+ wmmps_ac = psta->uapsd_be & BIT(1);
+ break;
+ }
+
+ psta->sleepq_len--;
+ if (psta->sleepq_len > 0)
+ pxmitframe->attrib.mdata = 1;
+ else
+ pxmitframe->attrib.mdata = 0;
+
+ if (wmmps_ac) {
+ psta->sleepq_ac_len--;
+ if (psta->sleepq_ac_len > 0) {
+ pxmitframe->attrib.mdata = 1;
+ pxmitframe->attrib.eosp = 0;
+ } else {
+ pxmitframe->attrib.mdata = 0;
+ pxmitframe->attrib.eosp = 1;
+ }
+ }
+
+ pxmitframe->attrib.triggered = 1;
+ rtl8723au_hal_xmitframe_enqueue(padapter, pxmitframe);
+ }
+
+ if (psta->sleepq_len == 0) {
+ pstapriv->tim_bitmap &= ~CHKBIT(psta->aid);
+
+ /* update BCN for TIM IE */
+ update_mask = BIT(0);
+
+ if (psta->state&WIFI_SLEEP_STATE)
+ psta->state ^= WIFI_SLEEP_STATE;
+
+ if (psta->state & WIFI_STA_ALIVE_CHK_STATE) {
+ psta->expire_to = pstapriv->expire_to;
+ psta->state ^= WIFI_STA_ALIVE_CHK_STATE;
+ }
+
+ pstapriv->sta_dz_bitmap &= ~CHKBIT(psta->aid);
+ }
+
+ /* spin_unlock_bh(&psta->sleep_q.lock); */
+ spin_unlock_bh(&pxmitpriv->lock);
+
+ /* for BC/MC Frames */
+ psta_bmc = rtw_get_bcmc_stainfo23a(padapter);
+ if (!psta_bmc)
+ return;
+
+ if ((pstapriv->sta_dz_bitmap&0xfffe) == 0x0) {
+ /* no any sta in ps mode */
+ spin_lock_bh(&pxmitpriv->lock);
+
+ phead = get_list_head(&psta_bmc->sleep_q);
+
+ list_for_each_safe(plist, ptmp, phead) {
+ pxmitframe = container_of(plist, struct xmit_frame,
+ list);
+
+ list_del_init(&pxmitframe->list);
+
+ psta_bmc->sleepq_len--;
+ if (psta_bmc->sleepq_len > 0)
+ pxmitframe->attrib.mdata = 1;
+ else
+ pxmitframe->attrib.mdata = 0;
+
+ pxmitframe->attrib.triggered = 1;
+ rtl8723au_hal_xmitframe_enqueue(padapter, pxmitframe);
+ }
+ if (psta_bmc->sleepq_len == 0) {
+ pstapriv->tim_bitmap &= ~BIT(0);
+ pstapriv->sta_dz_bitmap &= ~BIT(0);
+
+ /* update BCN for TIM IE */
+ /* update_BCNTIM(padapter); */
+ update_mask |= BIT(1);
+ }
+
+ /* spin_unlock_bh(&psta_bmc->sleep_q.lock); */
+ spin_unlock_bh(&pxmitpriv->lock);
+ }
+
+ if (update_mask)
+ update_beacon23a(padapter, WLAN_EID_TIM, NULL, false);
+}
+
+void xmit_delivery_enabled_frames23a(struct rtw_adapter *padapter,
+ struct sta_info *psta)
+{
+ u8 wmmps_ac = 0;
+ struct list_head *plist, *phead, *ptmp;
+ struct xmit_frame *pxmitframe;
+ struct sta_priv *pstapriv = &padapter->stapriv;
+ struct xmit_priv *pxmitpriv = &padapter->xmitpriv;
+
+ /* spin_lock_bh(&psta->sleep_q.lock); */
+ spin_lock_bh(&pxmitpriv->lock);
+
+ phead = get_list_head(&psta->sleep_q);
+
+ list_for_each_safe(plist, ptmp, phead) {
+ pxmitframe = container_of(plist, struct xmit_frame, list);
+
+ switch (pxmitframe->attrib.priority) {
+ case 1:
+ case 2:
+ wmmps_ac = psta->uapsd_bk & BIT(1);
+ break;
+ case 4:
+ case 5:
+ wmmps_ac = psta->uapsd_vi & BIT(1);
+ break;
+ case 6:
+ case 7:
+ wmmps_ac = psta->uapsd_vo & BIT(1);
+ break;
+ case 0:
+ case 3:
+ default:
+ wmmps_ac = psta->uapsd_be & BIT(1);
+ break;
+ }
+
+ if (!wmmps_ac)
+ continue;
+
+ list_del_init(&pxmitframe->list);
+
+ psta->sleepq_len--;
+ psta->sleepq_ac_len--;
+
+ if (psta->sleepq_ac_len > 0) {
+ pxmitframe->attrib.mdata = 1;
+ pxmitframe->attrib.eosp = 0;
+ } else {
+ pxmitframe->attrib.mdata = 0;
+ pxmitframe->attrib.eosp = 1;
+ }
+
+ pxmitframe->attrib.triggered = 1;
+
+ rtl8723au_hal_xmitframe_enqueue(padapter, pxmitframe);
+
+ if ((psta->sleepq_ac_len == 0) && (!psta->has_legacy_ac) &&
+ (wmmps_ac)) {
+ pstapriv->tim_bitmap &= ~CHKBIT(psta->aid);
+
+ /* update BCN for TIM IE */
+ update_beacon23a(padapter, WLAN_EID_TIM, NULL, false);
+ }
+ }
+ spin_unlock_bh(&pxmitpriv->lock);
+}
+
+#endif
+
+void rtw_sctx_init23a(struct submit_ctx *sctx, int timeout_ms)
+{
+ sctx->timeout_ms = timeout_ms;
+ init_completion(&sctx->done);
+ sctx->status = RTW_SCTX_SUBMITTED;
+}
+
+int rtw_sctx_wait23a(struct submit_ctx *sctx)
+{
+ int ret = _FAIL;
+ unsigned long expire;
+ int status = 0;
+
+ expire = sctx->timeout_ms ? msecs_to_jiffies(sctx->timeout_ms) :
+ MAX_SCHEDULE_TIMEOUT;
+ if (!wait_for_completion_timeout(&sctx->done, expire)) {
+ /* timeout, do something?? */
+ status = RTW_SCTX_DONE_TIMEOUT;
+ DBG_8723A("%s timeout\n", __func__);
+ } else {
+ status = sctx->status;
+ }
+
+ if (status == RTW_SCTX_DONE_SUCCESS)
+ ret = _SUCCESS;
+
+ return ret;
+}
+
+static bool rtw_sctx_chk_waring_status(int status)
+{
+ switch (status) {
+ case RTW_SCTX_DONE_UNKNOWN:
+ case RTW_SCTX_DONE_BUF_ALLOC:
+ case RTW_SCTX_DONE_BUF_FREE:
+ case RTW_SCTX_DONE_DRV_STOP:
+ case RTW_SCTX_DONE_DEV_REMOVE:
+ return true;
+ default:
+ return false;
+ }
+}
+
+void rtw23a_sctx_done_err(struct submit_ctx **sctx, int status)
+{
+ if (*sctx) {
+ if (rtw_sctx_chk_waring_status(status))
+ DBG_8723A("%s status:%d\n", __func__, status);
+ (*sctx)->status = status;
+ complete(&(*sctx)->done);
+ *sctx = NULL;
+ }
+}
+
+int rtw_ack_tx_wait23a(struct xmit_priv *pxmitpriv, u32 timeout_ms)
+{
+ struct submit_ctx *pack_tx_ops = &pxmitpriv->ack_tx_ops;
+
+ pack_tx_ops->timeout_ms = timeout_ms;
+ pack_tx_ops->status = RTW_SCTX_SUBMITTED;
+
+ return rtw_sctx_wait23a(pack_tx_ops);
+}
+
diff --git a/drivers/staging/rtl8723au/hal/Hal8723PwrSeq.c b/drivers/staging/rtl8723au/hal/Hal8723PwrSeq.c
new file mode 100644
index 000000000..747f86cdd
--- /dev/null
+++ b/drivers/staging/rtl8723au/hal/Hal8723PwrSeq.c
@@ -0,0 +1,80 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2012 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * 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.
+ *
+ ******************************************************************************/
+
+#include "Hal8723PwrSeq.h"
+
+/*
+ drivers should parse below arrays and do the corresponding actions
+*/
+/* 3 Power on Array */
+struct wlan_pwr_cfg rtl8723AU_power_on_flow[RTL8723A_TRANS_CARDEMU_TO_ACT_STEPS+RTL8723A_TRANS_END_STEPS] = {
+ RTL8723A_TRANS_CARDEMU_TO_ACT
+ RTL8723A_TRANS_END
+};
+
+/* 3 Radio off GPIO Array */
+struct wlan_pwr_cfg rtl8723AU_radio_off_flow[RTL8723A_TRANS_ACT_TO_CARDEMU_STEPS+RTL8723A_TRANS_END_STEPS] = {
+ RTL8723A_TRANS_ACT_TO_CARDEMU
+ RTL8723A_TRANS_END
+};
+
+/* 3 Card Disable Array */
+struct wlan_pwr_cfg rtl8723AU_card_disable_flow[RTL8723A_TRANS_ACT_TO_CARDEMU_STEPS+RTL8723A_TRANS_CARDEMU_TO_PDN_STEPS+RTL8723A_TRANS_END_STEPS] = {
+ RTL8723A_TRANS_ACT_TO_CARDEMU
+ RTL8723A_TRANS_CARDEMU_TO_CARDDIS
+ RTL8723A_TRANS_END
+};
+
+/* 3 Card Enable Array */
+struct wlan_pwr_cfg rtl8723AU_card_enable_flow[RTL8723A_TRANS_ACT_TO_CARDEMU_STEPS+RTL8723A_TRANS_CARDEMU_TO_PDN_STEPS+RTL8723A_TRANS_END_STEPS] = {
+ RTL8723A_TRANS_CARDDIS_TO_CARDEMU
+ RTL8723A_TRANS_CARDEMU_TO_ACT
+ RTL8723A_TRANS_END
+};
+
+/* 3 Suspend Array */
+struct wlan_pwr_cfg rtl8723AU_suspend_flow[RTL8723A_TRANS_ACT_TO_CARDEMU_STEPS+RTL8723A_TRANS_CARDEMU_TO_SUS_STEPS+RTL8723A_TRANS_END_STEPS] = {
+ RTL8723A_TRANS_ACT_TO_CARDEMU
+ RTL8723A_TRANS_CARDEMU_TO_SUS
+ RTL8723A_TRANS_END
+};
+
+/* 3 Resume Array */
+struct wlan_pwr_cfg rtl8723AU_resume_flow[RTL8723A_TRANS_ACT_TO_CARDEMU_STEPS+RTL8723A_TRANS_CARDEMU_TO_SUS_STEPS+RTL8723A_TRANS_END_STEPS] = {
+ RTL8723A_TRANS_SUS_TO_CARDEMU
+ RTL8723A_TRANS_CARDEMU_TO_ACT
+ RTL8723A_TRANS_END
+};
+
+/* 3 HWPDN Array */
+struct wlan_pwr_cfg rtl8723AU_hwpdn_flow[RTL8723A_TRANS_ACT_TO_CARDEMU_STEPS+RTL8723A_TRANS_CARDEMU_TO_PDN_STEPS+RTL8723A_TRANS_END_STEPS] = {
+ RTL8723A_TRANS_ACT_TO_CARDEMU
+ RTL8723A_TRANS_CARDEMU_TO_PDN
+ RTL8723A_TRANS_END
+};
+
+/* 3 Enter LPS */
+struct wlan_pwr_cfg rtl8723AU_enter_lps_flow[RTL8723A_TRANS_ACT_TO_LPS_STEPS+RTL8723A_TRANS_END_STEPS] = {
+ /* FW behavior */
+ RTL8723A_TRANS_ACT_TO_LPS
+ RTL8723A_TRANS_END
+};
+
+/* 3 Leave LPS */
+struct wlan_pwr_cfg rtl8723AU_leave_lps_flow[RTL8723A_TRANS_LPS_TO_ACT_STEPS+RTL8723A_TRANS_END_STEPS] = {
+ /* FW behavior */
+ RTL8723A_TRANS_LPS_TO_ACT
+ RTL8723A_TRANS_END
+};
diff --git a/drivers/staging/rtl8723au/hal/Hal8723UHWImg_CE.c b/drivers/staging/rtl8723au/hal/Hal8723UHWImg_CE.c
new file mode 100644
index 000000000..56833da63
--- /dev/null
+++ b/drivers/staging/rtl8723au/hal/Hal8723UHWImg_CE.c
@@ -0,0 +1,136 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2012 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * 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.
+ *
+ ******************************************************************************/
+
+/*Created on 2013/01/14, 15:51*/
+#include "odm_precomp.h"
+
+u32 Rtl8723UPHY_REG_Array_PG[Rtl8723UPHY_REG_Array_PGLength] = {
+ 0xe00, 0xffffffff, 0x0a0c0c0c,
+ 0xe04, 0xffffffff, 0x02040608,
+ 0xe08, 0x0000ff00, 0x00000000,
+ 0x86c, 0xffffff00, 0x00000000,
+ 0xe10, 0xffffffff, 0x0a0c0d0e,
+ 0xe14, 0xffffffff, 0x02040608,
+ 0xe18, 0xffffffff, 0x0a0c0d0e,
+ 0xe1c, 0xffffffff, 0x02040608,
+ 0x830, 0xffffffff, 0x0a0c0c0c,
+ 0x834, 0xffffffff, 0x02040608,
+ 0x838, 0xffffff00, 0x00000000,
+ 0x86c, 0x000000ff, 0x00000000,
+ 0x83c, 0xffffffff, 0x0a0c0d0e,
+ 0x848, 0xffffffff, 0x02040608,
+ 0x84c, 0xffffffff, 0x0a0c0d0e,
+ 0x868, 0xffffffff, 0x02040608,
+ 0xe00, 0xffffffff, 0x00000000,
+ 0xe04, 0xffffffff, 0x00000000,
+ 0xe08, 0x0000ff00, 0x00000000,
+ 0x86c, 0xffffff00, 0x00000000,
+ 0xe10, 0xffffffff, 0x00000000,
+ 0xe14, 0xffffffff, 0x00000000,
+ 0xe18, 0xffffffff, 0x00000000,
+ 0xe1c, 0xffffffff, 0x00000000,
+ 0x830, 0xffffffff, 0x00000000,
+ 0x834, 0xffffffff, 0x00000000,
+ 0x838, 0xffffff00, 0x00000000,
+ 0x86c, 0x000000ff, 0x00000000,
+ 0x83c, 0xffffffff, 0x00000000,
+ 0x848, 0xffffffff, 0x00000000,
+ 0x84c, 0xffffffff, 0x00000000,
+ 0x868, 0xffffffff, 0x00000000,
+ 0xe00, 0xffffffff, 0x04040404,
+ 0xe04, 0xffffffff, 0x00020204,
+ 0xe08, 0x0000ff00, 0x00000000,
+ 0x86c, 0xffffff00, 0x00000000,
+ 0xe10, 0xffffffff, 0x06060606,
+ 0xe14, 0xffffffff, 0x00020406,
+ 0xe18, 0xffffffff, 0x00000000,
+ 0xe1c, 0xffffffff, 0x00000000,
+ 0x830, 0xffffffff, 0x04040404,
+ 0x834, 0xffffffff, 0x00020204,
+ 0x838, 0xffffff00, 0x00000000,
+ 0x86c, 0x000000ff, 0x00000000,
+ 0x83c, 0xffffffff, 0x06060606,
+ 0x848, 0xffffffff, 0x00020406,
+ 0x84c, 0xffffffff, 0x00000000,
+ 0x868, 0xffffffff, 0x00000000,
+ 0xe00, 0xffffffff, 0x00000000,
+ 0xe04, 0xffffffff, 0x00000000,
+ 0xe08, 0x0000ff00, 0x00000000,
+ 0x86c, 0xffffff00, 0x00000000,
+ 0xe10, 0xffffffff, 0x00000000,
+ 0xe14, 0xffffffff, 0x00000000,
+ 0xe18, 0xffffffff, 0x00000000,
+ 0xe1c, 0xffffffff, 0x00000000,
+ 0x830, 0xffffffff, 0x00000000,
+ 0x834, 0xffffffff, 0x00000000,
+ 0x838, 0xffffff00, 0x00000000,
+ 0x86c, 0x000000ff, 0x00000000,
+ 0x83c, 0xffffffff, 0x00000000,
+ 0x848, 0xffffffff, 0x00000000,
+ 0x84c, 0xffffffff, 0x00000000,
+ 0x868, 0xffffffff, 0x00000000,
+ 0xe00, 0xffffffff, 0x00000000,
+ 0xe04, 0xffffffff, 0x00000000,
+ 0xe08, 0x0000ff00, 0x00000000,
+ 0x86c, 0xffffff00, 0x00000000,
+ 0xe10, 0xffffffff, 0x00000000,
+ 0xe14, 0xffffffff, 0x00000000,
+ 0xe18, 0xffffffff, 0x00000000,
+ 0xe1c, 0xffffffff, 0x00000000,
+ 0x830, 0xffffffff, 0x00000000,
+ 0x834, 0xffffffff, 0x00000000,
+ 0x838, 0xffffff00, 0x00000000,
+ 0x86c, 0x000000ff, 0x00000000,
+ 0x83c, 0xffffffff, 0x00000000,
+ 0x848, 0xffffffff, 0x00000000,
+ 0x84c, 0xffffffff, 0x00000000,
+ 0x868, 0xffffffff, 0x00000000,
+ 0xe00, 0xffffffff, 0x04040404,
+ 0xe04, 0xffffffff, 0x00020204,
+ 0xe08, 0x0000ff00, 0x00000000,
+ 0x86c, 0xffffff00, 0x00000000,
+ 0xe10, 0xffffffff, 0x00000000,
+ 0xe14, 0xffffffff, 0x00000000,
+ 0xe18, 0xffffffff, 0x00000000,
+ 0xe1c, 0xffffffff, 0x00000000,
+ 0x830, 0xffffffff, 0x04040404,
+ 0x834, 0xffffffff, 0x00020204,
+ 0x838, 0xffffff00, 0x00000000,
+ 0x86c, 0x000000ff, 0x00000000,
+ 0x83c, 0xffffffff, 0x00000000,
+ 0x848, 0xffffffff, 0x00000000,
+ 0x84c, 0xffffffff, 0x00000000,
+ 0x868, 0xffffffff, 0x00000000,
+ 0xe00, 0xffffffff, 0x00000000,
+ 0xe04, 0xffffffff, 0x00000000,
+ 0xe08, 0x0000ff00, 0x00000000,
+ 0x86c, 0xffffff00, 0x00000000,
+ 0xe10, 0xffffffff, 0x00000000,
+ 0xe14, 0xffffffff, 0x00000000,
+ 0xe18, 0xffffffff, 0x00000000,
+ 0xe1c, 0xffffffff, 0x00000000,
+ 0x830, 0xffffffff, 0x00000000,
+ 0x834, 0xffffffff, 0x00000000,
+ 0x838, 0xffffff00, 0x00000000,
+ 0x86c, 0x000000ff, 0x00000000,
+ 0x83c, 0xffffffff, 0x00000000,
+ 0x848, 0xffffffff, 0x00000000,
+ 0x84c, 0xffffffff, 0x00000000,
+ 0x868, 0xffffffff, 0x00000000,
+ };
+
+u32 Rtl8723UMACPHY_Array_PG[Rtl8723UMACPHY_Array_PGLength] = {
+ 0x0,
+};
diff --git a/drivers/staging/rtl8723au/hal/HalDMOutSrc8723A_CE.c b/drivers/staging/rtl8723au/hal/HalDMOutSrc8723A_CE.c
new file mode 100644
index 000000000..3f9ec9e00
--- /dev/null
+++ b/drivers/staging/rtl8723au/hal/HalDMOutSrc8723A_CE.c
@@ -0,0 +1,1097 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2011 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * 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.
+ *
+ ******************************************************************************/
+/* Description: */
+/* This file is for 92CE/92CU dynamic mechanism only */
+
+/* include files */
+
+#include "odm_precomp.h"
+#include <usb_ops_linux.h>
+
+#define DPK_DELTA_MAPPING_NUM 13
+#define index_mapping_HP_NUM 15
+/* 091212 chiyokolin */
+static void
+odm_TXPowerTrackingCallback_ThermalMeter_92C(struct rtw_adapter *Adapter)
+{
+ struct hal_data_8723a *pHalData = GET_HAL_DATA(Adapter);
+ struct dm_priv *pdmpriv = &pHalData->dmpriv;
+ u8 ThermalValue = 0, delta, delta_LCK, delta_IQK, delta_HP;
+ int ele_A, ele_D, TempCCk, X, value32;
+ int Y, ele_C;
+ s8 OFDM_index[2], CCK_index = 0, OFDM_index_old[2] = {0};
+ s8 CCK_index_old = 0;
+ int i = 0;
+ u8 OFDM_min_index = 6, rf; /* OFDM BB Swing should be less than +3.0dB*/
+ u8 ThermalValue_HP_count = 0;
+ u32 ThermalValue_HP = 0;
+ s32 index_mapping_HP[index_mapping_HP_NUM] = {
+ 0, 1, 3, 4, 6,
+ 7, 9, 10, 12, 13,
+ 15, 16, 18, 19, 21
+ };
+ s8 index_HP;
+
+ pdmpriv->TXPowerTrackingCallbackCnt++; /* cosa add for debug */
+ pdmpriv->bTXPowerTrackingInit = true;
+
+ if (pHalData->CurrentChannel == 14 && !pdmpriv->bCCKinCH14)
+ pdmpriv->bCCKinCH14 = true;
+ else if (pHalData->CurrentChannel != 14 && pdmpriv->bCCKinCH14)
+ pdmpriv->bCCKinCH14 = false;
+
+ ThermalValue = (u8)PHY_QueryRFReg(Adapter, RF_PATH_A, RF_T_METER,
+ 0x1f);/* 0x24: RF Reg[4:0] */
+
+ rtl8723a_phy_ap_calibrate(Adapter, (ThermalValue -
+ pHalData->EEPROMThermalMeter));
+
+ if (pHalData->rf_type == RF_2T2R)
+ rf = 2;
+ else
+ rf = 1;
+
+ if (ThermalValue) {
+ /* Query OFDM path A default setting */
+ ele_D = rtl8723au_read32(Adapter, rOFDM0_XATxIQImbalance) &
+ bMaskOFDM_D;
+ for (i = 0; i < OFDM_TABLE_SIZE_92C; i++) {
+ /* find the index */
+ if (ele_D == (OFDMSwingTable23A[i]&bMaskOFDM_D)) {
+ OFDM_index_old[0] = (u8)i;
+ break;
+ }
+ }
+
+ /* Query OFDM path B default setting */
+ if (pHalData->rf_type == RF_2T2R) {
+ ele_D = rtl8723au_read32(Adapter,
+ rOFDM0_XBTxIQImbalance);
+ ele_D &= bMaskOFDM_D;
+ for (i = 0; i < OFDM_TABLE_SIZE_92C; i++) { /* find the index */
+ if (ele_D == (OFDMSwingTable23A[i]&bMaskOFDM_D)) {
+ OFDM_index_old[1] = (u8)i;
+ break;
+ }
+ }
+ }
+
+ /* Query CCK default setting From 0xa24 */
+ TempCCk = rtl8723au_read32(Adapter, rCCK0_TxFilter2) & bMaskCCK;
+ for (i = 0 ; i < CCK_TABLE_SIZE ; i++) {
+ if (pdmpriv->bCCKinCH14) {
+ if (!memcmp(&TempCCk,
+ &CCKSwingTable_Ch1423A[i][2], 4)) {
+ CCK_index_old = (u8)i;
+ break;
+ }
+ } else {
+ if (!memcmp(&TempCCk,
+ &CCKSwingTable_Ch1_Ch1323A[i][2], 4)) {
+ CCK_index_old = (u8)i;
+ break;
+ }
+ }
+ }
+
+ if (!pdmpriv->ThermalValue) {
+ pdmpriv->ThermalValue = pHalData->EEPROMThermalMeter;
+ pdmpriv->ThermalValue_LCK = ThermalValue;
+ pdmpriv->ThermalValue_IQK = ThermalValue;
+ pdmpriv->ThermalValue_DPK = pHalData->EEPROMThermalMeter;
+
+ for (i = 0; i < rf; i++) {
+ pdmpriv->OFDM_index_HP[i] = OFDM_index_old[i];
+ pdmpriv->OFDM_index[i] = OFDM_index_old[i];
+ }
+ pdmpriv->CCK_index_HP = CCK_index_old;
+ pdmpriv->CCK_index = CCK_index_old;
+ }
+
+ if (pHalData->BoardType == BOARD_USB_High_PA) {
+ pdmpriv->ThermalValue_HP[pdmpriv->ThermalValue_HP_index] = ThermalValue;
+ pdmpriv->ThermalValue_HP_index++;
+ if (pdmpriv->ThermalValue_HP_index == HP_THERMAL_NUM)
+ pdmpriv->ThermalValue_HP_index = 0;
+
+ for (i = 0; i < HP_THERMAL_NUM; i++) {
+ if (pdmpriv->ThermalValue_HP[i]) {
+ ThermalValue_HP += pdmpriv->ThermalValue_HP[i];
+ ThermalValue_HP_count++;
+ }
+ }
+
+ if (ThermalValue_HP_count)
+ ThermalValue = (u8)(ThermalValue_HP / ThermalValue_HP_count);
+ }
+
+ delta = (ThermalValue > pdmpriv->ThermalValue) ?
+ (ThermalValue - pdmpriv->ThermalValue) :
+ (pdmpriv->ThermalValue - ThermalValue);
+ if (pHalData->BoardType == BOARD_USB_High_PA) {
+ if (pdmpriv->bDoneTxpower)
+ delta_HP = (ThermalValue > pdmpriv->ThermalValue) ?
+ (ThermalValue - pdmpriv->ThermalValue) :
+ (pdmpriv->ThermalValue - ThermalValue);
+ else
+ delta_HP = ThermalValue > pHalData->EEPROMThermalMeter ?
+ (ThermalValue - pHalData->EEPROMThermalMeter) :
+ (pHalData->EEPROMThermalMeter - ThermalValue);
+ } else {
+ delta_HP = 0;
+ }
+ delta_LCK = (ThermalValue > pdmpriv->ThermalValue_LCK) ?
+ (ThermalValue - pdmpriv->ThermalValue_LCK) :
+ (pdmpriv->ThermalValue_LCK - ThermalValue);
+ delta_IQK = (ThermalValue > pdmpriv->ThermalValue_IQK) ?
+ (ThermalValue - pdmpriv->ThermalValue_IQK) :
+ (pdmpriv->ThermalValue_IQK - ThermalValue);
+
+ if (delta_LCK > 1) {
+ pdmpriv->ThermalValue_LCK = ThermalValue;
+ rtl8723a_phy_lc_calibrate(Adapter);
+ }
+
+ if ((delta > 0 || delta_HP > 0) && pdmpriv->TxPowerTrackControl) {
+ if (pHalData->BoardType == BOARD_USB_High_PA) {
+ pdmpriv->bDoneTxpower = true;
+ delta_HP = ThermalValue > pHalData->EEPROMThermalMeter ?
+ (ThermalValue - pHalData->EEPROMThermalMeter) :
+ (pHalData->EEPROMThermalMeter - ThermalValue);
+
+ if (delta_HP > index_mapping_HP_NUM-1)
+ index_HP = index_mapping_HP[index_mapping_HP_NUM-1];
+ else
+ index_HP = index_mapping_HP[delta_HP];
+
+ if (ThermalValue > pHalData->EEPROMThermalMeter) {
+ /* set larger Tx power */
+ for (i = 0; i < rf; i++)
+ OFDM_index[i] = pdmpriv->OFDM_index_HP[i] - index_HP;
+ CCK_index = pdmpriv->CCK_index_HP - index_HP;
+ } else {
+ for (i = 0; i < rf; i++)
+ OFDM_index[i] = pdmpriv->OFDM_index_HP[i] + index_HP;
+ CCK_index = pdmpriv->CCK_index_HP + index_HP;
+ }
+
+ delta_HP = (ThermalValue > pdmpriv->ThermalValue) ?
+ (ThermalValue - pdmpriv->ThermalValue) :
+ (pdmpriv->ThermalValue - ThermalValue);
+ } else {
+ if (ThermalValue > pdmpriv->ThermalValue) {
+ for (i = 0; i < rf; i++)
+ pdmpriv->OFDM_index[i] -= delta;
+ pdmpriv->CCK_index -= delta;
+ } else {
+ for (i = 0; i < rf; i++)
+ pdmpriv->OFDM_index[i] += delta;
+ pdmpriv->CCK_index += delta;
+ }
+ }
+
+ /* no adjust */
+ if (pHalData->BoardType != BOARD_USB_High_PA) {
+ if (ThermalValue > pHalData->EEPROMThermalMeter) {
+ for (i = 0; i < rf; i++)
+ OFDM_index[i] = pdmpriv->OFDM_index[i]+1;
+ CCK_index = pdmpriv->CCK_index+1;
+ } else {
+ for (i = 0; i < rf; i++)
+ OFDM_index[i] = pdmpriv->OFDM_index[i];
+ CCK_index = pdmpriv->CCK_index;
+ }
+ }
+ for (i = 0; i < rf; i++) {
+ if (OFDM_index[i] > (OFDM_TABLE_SIZE_92C-1))
+ OFDM_index[i] = (OFDM_TABLE_SIZE_92C-1);
+ else if (OFDM_index[i] < OFDM_min_index)
+ OFDM_index[i] = OFDM_min_index;
+ }
+
+ if (CCK_index > (CCK_TABLE_SIZE-1))
+ CCK_index = CCK_TABLE_SIZE-1;
+ else if (CCK_index < 0)
+ CCK_index = 0;
+ }
+
+ if (pdmpriv->TxPowerTrackControl &&
+ (delta != 0 || delta_HP != 0)) {
+ /* Adujst OFDM Ant_A according to IQK result */
+ ele_D = (OFDMSwingTable23A[OFDM_index[0]] & 0xFFC00000)>>22;
+ X = pdmpriv->RegE94;
+ Y = pdmpriv->RegE9C;
+
+ if (X != 0) {
+ if ((X & 0x00000200) != 0)
+ X = X | 0xFFFFFC00;
+ ele_A = ((X * ele_D)>>8)&0x000003FF;
+
+ /* new element C = element D x Y */
+ if ((Y & 0x00000200) != 0)
+ Y = Y | 0xFFFFFC00;
+ ele_C = ((Y * ele_D)>>8)&0x000003FF;
+
+ /* write new elements A, C, D to regC80 and regC94, element B is always 0 */
+ value32 = (ele_D<<22)|((ele_C&0x3F)<<16)|ele_A;
+ rtl8723au_write32(Adapter,
+ rOFDM0_XATxIQImbalance,
+ value32);
+
+ value32 = (ele_C&0x000003C0)>>6;
+ PHY_SetBBReg(Adapter, rOFDM0_XCTxAFE, bMaskH4Bits, value32);
+
+ value32 = ((X * ele_D)>>7)&0x01;
+ PHY_SetBBReg(Adapter, rOFDM0_ECCAThreshold,
+ BIT(31), value32);
+
+ value32 = ((Y * ele_D)>>7)&0x01;
+ PHY_SetBBReg(Adapter, rOFDM0_ECCAThreshold,
+ BIT(29), value32);
+ } else {
+ rtl8723au_write32(Adapter,
+ rOFDM0_XATxIQImbalance,
+ OFDMSwingTable23A[OFDM_index[0]]);
+ PHY_SetBBReg(Adapter, rOFDM0_XCTxAFE,
+ bMaskH4Bits, 0x00);
+ PHY_SetBBReg(Adapter, rOFDM0_ECCAThreshold,
+ BIT(31) | BIT(29), 0x00);
+ }
+
+ /* Adjust CCK according to IQK result */
+ if (!pdmpriv->bCCKinCH14) {
+ rtl8723au_write8(Adapter, 0xa22, CCKSwingTable_Ch1_Ch1323A[CCK_index][0]);
+ rtl8723au_write8(Adapter, 0xa23, CCKSwingTable_Ch1_Ch1323A[CCK_index][1]);
+ rtl8723au_write8(Adapter, 0xa24, CCKSwingTable_Ch1_Ch1323A[CCK_index][2]);
+ rtl8723au_write8(Adapter, 0xa25, CCKSwingTable_Ch1_Ch1323A[CCK_index][3]);
+ rtl8723au_write8(Adapter, 0xa26, CCKSwingTable_Ch1_Ch1323A[CCK_index][4]);
+ rtl8723au_write8(Adapter, 0xa27, CCKSwingTable_Ch1_Ch1323A[CCK_index][5]);
+ rtl8723au_write8(Adapter, 0xa28, CCKSwingTable_Ch1_Ch1323A[CCK_index][6]);
+ rtl8723au_write8(Adapter, 0xa29, CCKSwingTable_Ch1_Ch1323A[CCK_index][7]);
+ } else {
+ rtl8723au_write8(Adapter, 0xa22, CCKSwingTable_Ch1423A[CCK_index][0]);
+ rtl8723au_write8(Adapter, 0xa23, CCKSwingTable_Ch1423A[CCK_index][1]);
+ rtl8723au_write8(Adapter, 0xa24, CCKSwingTable_Ch1423A[CCK_index][2]);
+ rtl8723au_write8(Adapter, 0xa25, CCKSwingTable_Ch1423A[CCK_index][3]);
+ rtl8723au_write8(Adapter, 0xa26, CCKSwingTable_Ch1423A[CCK_index][4]);
+ rtl8723au_write8(Adapter, 0xa27, CCKSwingTable_Ch1423A[CCK_index][5]);
+ rtl8723au_write8(Adapter, 0xa28, CCKSwingTable_Ch1423A[CCK_index][6]);
+ rtl8723au_write8(Adapter, 0xa29, CCKSwingTable_Ch1423A[CCK_index][7]);
+ }
+
+ if (pHalData->rf_type == RF_2T2R) {
+ ele_D = (OFDMSwingTable23A[(u8)OFDM_index[1]] & 0xFFC00000)>>22;
+
+ /* new element A = element D x X */
+ X = pdmpriv->RegEB4;
+ Y = pdmpriv->RegEBC;
+
+ if (X != 0) {
+ if ((X & 0x00000200) != 0) /* consider minus */
+ X = X | 0xFFFFFC00;
+ ele_A = ((X * ele_D)>>8)&0x000003FF;
+
+ /* new element C = element D x Y */
+ if ((Y & 0x00000200) != 0)
+ Y = Y | 0xFFFFFC00;
+ ele_C = ((Y * ele_D)>>8)&0x00003FF;
+
+ /* write new elements A, C, D to regC88 and regC9C, element B is always 0 */
+ value32 = (ele_D<<22)|((ele_C&0x3F)<<16) | ele_A;
+ rtl8723au_write32(Adapter, rOFDM0_XBTxIQImbalance, value32);
+
+ value32 = (ele_C&0x000003C0)>>6;
+ PHY_SetBBReg(Adapter, rOFDM0_XDTxAFE, bMaskH4Bits, value32);
+
+ value32 = ((X * ele_D)>>7)&0x01;
+ PHY_SetBBReg(Adapter,
+ rOFDM0_ECCAThreshold,
+ BIT(27), value32);
+
+ value32 = ((Y * ele_D)>>7)&0x01;
+ PHY_SetBBReg(Adapter,
+ rOFDM0_ECCAThreshold,
+ BIT(25), value32);
+ } else {
+ rtl8723au_write32(Adapter,
+ rOFDM0_XBTxIQImbalance,
+ OFDMSwingTable23A[OFDM_index[1]]);
+ PHY_SetBBReg(Adapter,
+ rOFDM0_XDTxAFE,
+ bMaskH4Bits, 0x00);
+ PHY_SetBBReg(Adapter,
+ rOFDM0_ECCAThreshold,
+ BIT(27) | BIT(25), 0x00);
+ }
+ }
+
+ }
+ if (delta_IQK > 3) {
+ pdmpriv->ThermalValue_IQK = ThermalValue;
+ rtl8723a_phy_iq_calibrate(Adapter, false);
+ }
+
+ /* update thermal meter value */
+ if (pdmpriv->TxPowerTrackControl)
+ pdmpriv->ThermalValue = ThermalValue;
+ }
+ pdmpriv->TXPowercount = 0;
+}
+
+/* Description: */
+/* - Dispatch TxPower Tracking direct call ONLY for 92s. */
+/* - We shall NOT schedule Workitem within PASSIVE LEVEL, which will cause system resource */
+/* leakage under some platform. */
+/* Assumption: */
+/* PASSIVE_LEVEL when this routine is called. */
+static void ODM_TXPowerTracking92CDirectCall(struct rtw_adapter *Adapter)
+{
+ odm_TXPowerTrackingCallback_ThermalMeter_92C(Adapter);
+}
+
+void rtl8723a_odm_check_tx_power_tracking(struct rtw_adapter *Adapter)
+{
+ struct hal_data_8723a *pHalData = GET_HAL_DATA(Adapter);
+ struct dm_priv *pdmpriv = &pHalData->dmpriv;
+
+ if (!pdmpriv->TM_Trigger) { /* at least delay 1 sec */
+ PHY_SetRFReg(Adapter, RF_PATH_A, RF_T_METER, bRFRegOffsetMask, 0x60);
+
+ pdmpriv->TM_Trigger = 1;
+ return;
+ } else {
+ ODM_TXPowerTracking92CDirectCall(Adapter);
+ pdmpriv->TM_Trigger = 0;
+ }
+}
+
+/* IQK */
+#define MAX_TOLERANCE 5
+#define IQK_DELAY_TIME 1 /* ms */
+
+static u8 _PHY_PathA_IQK(struct rtw_adapter *pAdapter, bool configPathB)
+{
+ u32 regEAC, regE94, regE9C, regEA4;
+ u8 result = 0x00;
+ struct hal_data_8723a *pHalData = GET_HAL_DATA(pAdapter);
+
+ /* path-A IQK setting */
+ rtl8723au_write32(pAdapter, rTx_IQK_Tone_A, 0x10008c1f);
+ rtl8723au_write32(pAdapter, rRx_IQK_Tone_A, 0x10008c1f);
+ rtl8723au_write32(pAdapter, rTx_IQK_PI_A, 0x82140102);
+
+ rtl8723au_write32(pAdapter, rRx_IQK_PI_A, configPathB ? 0x28160202 :
+ IS_81xxC_VENDOR_UMC_B_CUT(pHalData->VersionID)?0x28160202:0x28160502);
+
+ /* path-B IQK setting */
+ if (configPathB) {
+ rtl8723au_write32(pAdapter, rTx_IQK_Tone_B, 0x10008c22);
+ rtl8723au_write32(pAdapter, rRx_IQK_Tone_B, 0x10008c22);
+ rtl8723au_write32(pAdapter, rTx_IQK_PI_B, 0x82140102);
+ rtl8723au_write32(pAdapter, rRx_IQK_PI_B, 0x28160202);
+ }
+
+ /* LO calibration setting */
+ rtl8723au_write32(pAdapter, rIQK_AGC_Rsp, 0x001028d1);
+
+ /* One shot, path A LOK & IQK */
+ rtl8723au_write32(pAdapter, rIQK_AGC_Pts, 0xf9000000);
+ rtl8723au_write32(pAdapter, rIQK_AGC_Pts, 0xf8000000);
+
+ /* delay x ms */
+ /* PlatformStallExecution(IQK_DELAY_TIME*1000); */
+ udelay(IQK_DELAY_TIME*1000);
+
+ /* Check failed */
+ regEAC = rtl8723au_read32(pAdapter, rRx_Power_After_IQK_A_2);
+ regE94 = rtl8723au_read32(pAdapter, rTx_Power_Before_IQK_A);
+ regE9C = rtl8723au_read32(pAdapter, rTx_Power_After_IQK_A);
+ regEA4 = rtl8723au_read32(pAdapter, rRx_Power_Before_IQK_A_2);
+
+ if (!(regEAC & BIT(28)) &&
+ (((regE94 & 0x03FF0000)>>16) != 0x142) &&
+ (((regE9C & 0x03FF0000)>>16) != 0x42))
+ result |= 0x01;
+ else /* if Tx not OK, ignore Rx */
+ return result;
+
+ if (!(regEAC & BIT(27)) && /* if Tx is OK, check whether Rx is OK */
+ (((regEA4 & 0x03FF0000)>>16) != 0x132) &&
+ (((regEAC & 0x03FF0000)>>16) != 0x36))
+ result |= 0x02;
+ else
+ DBG_8723A("Path A Rx IQK fail!!\n");
+ return result;
+}
+
+static u8 _PHY_PathB_IQK(struct rtw_adapter *pAdapter)
+{
+ u32 regEAC, regEB4, regEBC, regEC4, regECC;
+ u8 result = 0x00;
+
+ /* One shot, path B LOK & IQK */
+ rtl8723au_write32(pAdapter, rIQK_AGC_Cont, 0x00000002);
+ rtl8723au_write32(pAdapter, rIQK_AGC_Cont, 0x00000000);
+
+ /* delay x ms */
+ udelay(IQK_DELAY_TIME*1000);
+
+ /* Check failed */
+ regEAC = rtl8723au_read32(pAdapter, rRx_Power_After_IQK_A_2);
+ regEB4 = rtl8723au_read32(pAdapter, rTx_Power_Before_IQK_B);
+ regEBC = rtl8723au_read32(pAdapter, rTx_Power_After_IQK_B);
+ regEC4 = rtl8723au_read32(pAdapter, rRx_Power_Before_IQK_B_2);
+ regECC = rtl8723au_read32(pAdapter, rRx_Power_After_IQK_B_2);
+
+ if (!(regEAC & BIT(31)) &&
+ (((regEB4 & 0x03FF0000)>>16) != 0x142) &&
+ (((regEBC & 0x03FF0000)>>16) != 0x42))
+ result |= 0x01;
+ else
+ return result;
+
+ if (!(regEAC & BIT(30)) &&
+ (((regEC4 & 0x03FF0000)>>16) != 0x132) &&
+ (((regECC & 0x03FF0000)>>16) != 0x36))
+ result |= 0x02;
+ else
+ DBG_8723A("Path B Rx IQK fail!!\n");
+ return result;
+}
+
+static void _PHY_PathAFillIQKMatrix(struct rtw_adapter *pAdapter,
+ bool bIQKOK,
+ int result[][8],
+ u8 final_candidate,
+ bool bTxOnly
+ )
+{
+ u32 Oldval_0, X, TX0_A, reg;
+ s32 Y, TX0_C;
+
+ DBG_8723A("Path A IQ Calibration %s !\n", (bIQKOK)?"Success":"Failed");
+
+ if (final_candidate == 0xFF) {
+ return;
+ } else if (bIQKOK) {
+ Oldval_0 = rtl8723au_read32(pAdapter, rOFDM0_XATxIQImbalance);
+ Oldval_0 = (Oldval_0 >> 22) & 0x3FF;
+
+ X = result[final_candidate][0];
+ if ((X & 0x00000200) != 0)
+ X = X | 0xFFFFFC00;
+ TX0_A = (X * Oldval_0) >> 8;
+ PHY_SetBBReg(pAdapter, rOFDM0_XATxIQImbalance, 0x3FF, TX0_A);
+ PHY_SetBBReg(pAdapter, rOFDM0_ECCAThreshold, BIT(31),
+ ((X * Oldval_0>>7) & 0x1));
+
+ Y = result[final_candidate][1];
+ if ((Y & 0x00000200) != 0)
+ Y = Y | 0xFFFFFC00;
+ TX0_C = (Y * Oldval_0) >> 8;
+ PHY_SetBBReg(pAdapter, rOFDM0_XCTxAFE, 0xF0000000,
+ ((TX0_C&0x3C0)>>6));
+ PHY_SetBBReg(pAdapter, rOFDM0_XATxIQImbalance, 0x003F0000,
+ (TX0_C&0x3F));
+ PHY_SetBBReg(pAdapter, rOFDM0_ECCAThreshold, BIT(29),
+ ((Y * Oldval_0>>7) & 0x1));
+
+ if (bTxOnly) {
+ DBG_8723A("_PHY_PathAFillIQKMatrix only Tx OK\n");
+ return;
+ }
+
+ reg = result[final_candidate][2];
+ PHY_SetBBReg(pAdapter, rOFDM0_XARxIQImbalance, 0x3FF, reg);
+
+ reg = result[final_candidate][3] & 0x3F;
+ PHY_SetBBReg(pAdapter, rOFDM0_XARxIQImbalance, 0xFC00, reg);
+
+ reg = (result[final_candidate][3] >> 6) & 0xF;
+ PHY_SetBBReg(pAdapter, rOFDM0_RxIQExtAnta, 0xF0000000, reg);
+ }
+}
+
+static void _PHY_PathBFillIQKMatrix(struct rtw_adapter *pAdapter, bool bIQKOK, int result[][8], u8 final_candidate, bool bTxOnly)
+{
+ u32 Oldval_1, X, TX1_A, reg;
+ s32 Y, TX1_C;
+
+ DBG_8723A("Path B IQ Calibration %s !\n", (bIQKOK)?"Success":"Failed");
+
+ if (final_candidate == 0xFF) {
+ return;
+ } else if (bIQKOK) {
+ Oldval_1 = rtl8723au_read32(pAdapter, rOFDM0_XBTxIQImbalance);
+ Oldval_1 = (Oldval_1 >> 22) & 0x3FF;
+
+ X = result[final_candidate][4];
+ if ((X & 0x00000200) != 0)
+ X = X | 0xFFFFFC00;
+ TX1_A = (X * Oldval_1) >> 8;
+ PHY_SetBBReg(pAdapter, rOFDM0_XBTxIQImbalance, 0x3FF, TX1_A);
+ PHY_SetBBReg(pAdapter, rOFDM0_ECCAThreshold, BIT(27),
+ ((X * Oldval_1 >> 7) & 0x1));
+
+ Y = result[final_candidate][5];
+ if ((Y & 0x00000200) != 0)
+ Y = Y | 0xFFFFFC00;
+ TX1_C = (Y * Oldval_1) >> 8;
+ PHY_SetBBReg(pAdapter, rOFDM0_XDTxAFE, 0xF0000000,
+ ((TX1_C & 0x3C0) >> 6));
+ PHY_SetBBReg(pAdapter, rOFDM0_XBTxIQImbalance, 0x003F0000,
+ (TX1_C & 0x3F));
+ PHY_SetBBReg(pAdapter, rOFDM0_ECCAThreshold, BIT(25),
+ ((Y * Oldval_1 >> 7) & 0x1));
+
+ if (bTxOnly)
+ return;
+
+ reg = result[final_candidate][6];
+ PHY_SetBBReg(pAdapter, rOFDM0_XBRxIQImbalance, 0x3FF, reg);
+
+ reg = result[final_candidate][7] & 0x3F;
+ PHY_SetBBReg(pAdapter, rOFDM0_XBRxIQImbalance, 0xFC00, reg);
+
+ reg = (result[final_candidate][7] >> 6) & 0xF;
+ PHY_SetBBReg(pAdapter, rOFDM0_AGCRSSITable, 0x0000F000, reg);
+ }
+}
+
+static void _PHY_SaveADDARegisters(struct rtw_adapter *pAdapter, u32 *ADDAReg, u32 *ADDABackup, u32 RegisterNum)
+{
+ u32 i;
+
+ for (i = 0 ; i < RegisterNum ; i++) {
+ ADDABackup[i] = rtl8723au_read32(pAdapter, ADDAReg[i]);
+ }
+}
+
+static void _PHY_SaveMACRegisters(struct rtw_adapter *pAdapter, u32 *MACReg,
+ u32 *MACBackup)
+{
+ u32 i;
+
+ for (i = 0 ; i < (IQK_MAC_REG_NUM - 1); i++) {
+ MACBackup[i] = rtl8723au_read8(pAdapter, MACReg[i]);
+ }
+ MACBackup[i] = rtl8723au_read32(pAdapter, MACReg[i]);
+}
+
+static void _PHY_ReloadADDARegisters(struct rtw_adapter *pAdapter,
+ u32 *ADDAReg, u32 *ADDABackup,
+ u32 RegiesterNum)
+{
+ u32 i;
+
+ for (i = 0 ; i < RegiesterNum ; i++) {
+ rtl8723au_write32(pAdapter, ADDAReg[i], ADDABackup[i]);
+ }
+}
+
+static void _PHY_ReloadMACRegisters(struct rtw_adapter *pAdapter,
+ u32 *MACReg, u32 *MACBackup)
+{
+ u32 i;
+
+ for (i = 0 ; i < (IQK_MAC_REG_NUM - 1); i++)
+ rtl8723au_write8(pAdapter, MACReg[i], (u8)MACBackup[i]);
+
+ rtl8723au_write32(pAdapter, MACReg[i], MACBackup[i]);
+}
+
+static void _PHY_PathADDAOn(struct rtw_adapter *pAdapter, u32 *ADDAReg,
+ bool isPathAOn, bool is2T)
+{
+ u32 pathOn;
+ u32 i;
+
+ pathOn = isPathAOn ? 0x04db25a4 : 0x0b1b25a4;
+ if (!is2T) {
+ pathOn = 0x0bdb25a0;
+ rtl8723au_write32(pAdapter, ADDAReg[0], 0x0b1b25a0);
+ } else {
+ rtl8723au_write32(pAdapter, ADDAReg[0], pathOn);
+ }
+
+ for (i = 1 ; i < IQK_ADDA_REG_NUM ; i++)
+ rtl8723au_write32(pAdapter, ADDAReg[i], pathOn);
+}
+
+static void _PHY_MACSettingCalibration(struct rtw_adapter *pAdapter,
+ u32 *MACReg, u32 *MACBackup)
+{
+ u32 i = 0;
+
+ rtl8723au_write8(pAdapter, MACReg[i], 0x3F);
+
+ for (i = 1 ; i < (IQK_MAC_REG_NUM - 1); i++) {
+ rtl8723au_write8(pAdapter, MACReg[i],
+ (u8)(MACBackup[i] & ~BIT(3)));
+ }
+ rtl8723au_write8(pAdapter, MACReg[i], (u8)(MACBackup[i] & ~BIT(5)));
+}
+
+static void _PHY_PathAStandBy(struct rtw_adapter *pAdapter)
+{
+ rtl8723au_write32(pAdapter, rFPGA0_IQK, 0x0);
+ rtl8723au_write32(pAdapter, 0x840, 0x00010000);
+ rtl8723au_write32(pAdapter, rFPGA0_IQK, 0x80800000);
+}
+
+static void _PHY_PIModeSwitch(struct rtw_adapter *pAdapter, bool PIMode)
+{
+ u32 mode;
+
+ mode = PIMode ? 0x01000100 : 0x01000000;
+ rtl8723au_write32(pAdapter, 0x820, mode);
+ rtl8723au_write32(pAdapter, 0x828, mode);
+}
+
+/*
+return false => do IQK again
+*/
+static bool _PHY_SimularityCompare(struct rtw_adapter *pAdapter, int result[][8], u8 c1, u8 c2)
+{
+ u32 i, j, diff, SimularityBitMap, bound = 0;
+ struct hal_data_8723a *pHalData = GET_HAL_DATA(pAdapter);
+ u8 final_candidate[2] = {0xFF, 0xFF}; /* for path A and path B */
+ bool bResult = true;
+
+ if (pHalData->rf_type == RF_2T2R)
+ bound = 8;
+ else
+ bound = 4;
+
+ SimularityBitMap = 0;
+
+ for (i = 0; i < bound; i++) {
+ diff = (result[c1][i] > result[c2][i]) ? (result[c1][i] - result[c2][i]) : (result[c2][i] - result[c1][i]);
+ if (diff > MAX_TOLERANCE) {
+ if ((i == 2 || i == 6) && !SimularityBitMap) {
+ if (result[c1][i]+result[c1][i+1] == 0)
+ final_candidate[(i/4)] = c2;
+ else if (result[c2][i]+result[c2][i+1] == 0)
+ final_candidate[(i/4)] = c1;
+ else
+ SimularityBitMap = SimularityBitMap|(1<<i);
+ } else {
+ SimularityBitMap = SimularityBitMap|(1<<i);
+ }
+ }
+ }
+
+ if (SimularityBitMap == 0) {
+ for (i = 0; i < (bound/4); i++) {
+ if (final_candidate[i] != 0xFF) {
+ for (j = i*4; j < (i+1)*4-2; j++)
+ result[3][j] = result[final_candidate[i]][j];
+ bResult = false;
+ }
+ }
+ return bResult;
+ } else if (!(SimularityBitMap & 0x0F)) {
+ /* path A OK */
+ for (i = 0; i < 4; i++)
+ result[3][i] = result[c1][i];
+ return false;
+ } else if (!(SimularityBitMap & 0xF0) && pHalData->rf_type == RF_2T2R) {
+ /* path B OK */
+ for (i = 4; i < 8; i++)
+ result[3][i] = result[c1][i];
+ return false;
+ } else {
+ return false;
+ }
+}
+
+static void _PHY_IQCalibrate(struct rtw_adapter *pAdapter, int result[][8], u8 t, bool is2T)
+{
+ struct hal_data_8723a *pHalData = GET_HAL_DATA(pAdapter);
+ struct dm_priv *pdmpriv = &pHalData->dmpriv;
+ u32 i;
+ u8 PathAOK, PathBOK;
+ u32 ADDA_REG[IQK_ADDA_REG_NUM] = {
+ rFPGA0_XCD_SwitchControl, rBlue_Tooth,
+ rRx_Wait_CCA, rTx_CCK_RFON,
+ rTx_CCK_BBON, rTx_OFDM_RFON,
+ rTx_OFDM_BBON, rTx_To_Rx,
+ rTx_To_Tx, rRx_CCK,
+ rRx_OFDM, rRx_Wait_RIFS,
+ rRx_TO_Rx, rStandby,
+ rSleep, rPMPD_ANAEN
+ };
+
+ u32 IQK_MAC_REG[IQK_MAC_REG_NUM] = {
+ REG_TXPAUSE, REG_BCN_CTRL,
+ REG_BCN_CTRL_1, REG_GPIO_MUXCFG
+ };
+
+ u32 IQK_BB_REG_92C[IQK_BB_REG_NUM] = {
+ rOFDM0_TRxPathEnable, rOFDM0_TRMuxPar,
+ rFPGA0_XCD_RFInterfaceSW, rConfig_AntA, rConfig_AntB,
+ rFPGA0_XAB_RFInterfaceSW, rFPGA0_XA_RFInterfaceOE,
+ rFPGA0_XB_RFInterfaceOE, rFPGA0_RFMOD
+ };
+
+ const u32 retryCount = 2;
+
+ /* Note: IQ calibration must be performed after loading */
+ /* PHY_REG.txt , and radio_a, radio_b.txt */
+
+ u32 bbvalue;
+
+ if (t == 0) {
+ bbvalue = rtl8723au_read32(pAdapter, rFPGA0_RFMOD);
+
+ /* Save ADDA parameters, turn Path A ADDA on */
+ _PHY_SaveADDARegisters(pAdapter, ADDA_REG, pdmpriv->ADDA_backup, IQK_ADDA_REG_NUM);
+ _PHY_SaveMACRegisters(pAdapter, IQK_MAC_REG, pdmpriv->IQK_MAC_backup);
+ _PHY_SaveADDARegisters(pAdapter, IQK_BB_REG_92C, pdmpriv->IQK_BB_backup, IQK_BB_REG_NUM);
+ }
+ _PHY_PathADDAOn(pAdapter, ADDA_REG, true, is2T);
+
+ if (t == 0)
+ pdmpriv->bRfPiEnable = (u8)
+ PHY_QueryBBReg(pAdapter, rFPGA0_XA_HSSIParameter1,
+ BIT(8));
+
+ if (!pdmpriv->bRfPiEnable) {
+ /* Switch BB to PI mode to do IQ Calibration. */
+ _PHY_PIModeSwitch(pAdapter, true);
+ }
+
+ PHY_SetBBReg(pAdapter, rFPGA0_RFMOD, BIT(24), 0x00);
+ rtl8723au_write32(pAdapter, rOFDM0_TRxPathEnable, 0x03a05600);
+ rtl8723au_write32(pAdapter, rOFDM0_TRMuxPar, 0x000800e4);
+ rtl8723au_write32(pAdapter, rFPGA0_XCD_RFInterfaceSW, 0x22204000);
+ PHY_SetBBReg(pAdapter, rFPGA0_XAB_RFInterfaceSW, BIT(10), 0x01);
+ PHY_SetBBReg(pAdapter, rFPGA0_XAB_RFInterfaceSW, BIT(26), 0x01);
+ PHY_SetBBReg(pAdapter, rFPGA0_XA_RFInterfaceOE, BIT(10), 0x00);
+ PHY_SetBBReg(pAdapter, rFPGA0_XB_RFInterfaceOE, BIT(10), 0x00);
+
+ if (is2T) {
+ rtl8723au_write32(pAdapter,
+ rFPGA0_XA_LSSIParameter, 0x00010000);
+ rtl8723au_write32(pAdapter,
+ rFPGA0_XB_LSSIParameter, 0x00010000);
+ }
+
+ /* MAC settings */
+ _PHY_MACSettingCalibration(pAdapter, IQK_MAC_REG, pdmpriv->IQK_MAC_backup);
+
+ /* Page B init */
+ rtl8723au_write32(pAdapter, rConfig_AntA, 0x00080000);
+
+ if (is2T)
+ rtl8723au_write32(pAdapter, rConfig_AntB, 0x00080000);
+
+ /* IQ calibration setting */
+ rtl8723au_write32(pAdapter, rFPGA0_IQK, 0x80800000);
+ rtl8723au_write32(pAdapter, rTx_IQK, 0x01007c00);
+ rtl8723au_write32(pAdapter, rRx_IQK, 0x01004800);
+
+ for (i = 0 ; i < retryCount ; i++) {
+ PathAOK = _PHY_PathA_IQK(pAdapter, is2T);
+ if (PathAOK == 0x03) {
+ DBG_8723A("Path A IQK Success!!\n");
+ result[t][0] = (rtl8723au_read32(pAdapter, rTx_Power_Before_IQK_A)&0x3FF0000)>>16;
+ result[t][1] = (rtl8723au_read32(pAdapter, rTx_Power_After_IQK_A)&0x3FF0000)>>16;
+ result[t][2] = (rtl8723au_read32(pAdapter, rRx_Power_Before_IQK_A_2)&0x3FF0000)>>16;
+ result[t][3] = (rtl8723au_read32(pAdapter, rRx_Power_After_IQK_A_2)&0x3FF0000)>>16;
+ break;
+ } else if (i == (retryCount-1) && PathAOK == 0x01) {
+ /* Tx IQK OK */
+ DBG_8723A("Path A IQK Only Tx Success!!\n");
+
+ result[t][0] = (rtl8723au_read32(pAdapter, rTx_Power_Before_IQK_A)&0x3FF0000)>>16;
+ result[t][1] = (rtl8723au_read32(pAdapter, rTx_Power_After_IQK_A)&0x3FF0000)>>16;
+ }
+ }
+
+ if (0x00 == PathAOK) {
+ DBG_8723A("Path A IQK failed!!\n");
+ }
+
+ if (is2T) {
+ _PHY_PathAStandBy(pAdapter);
+
+ /* Turn Path B ADDA on */
+ _PHY_PathADDAOn(pAdapter, ADDA_REG, false, is2T);
+
+ for (i = 0 ; i < retryCount ; i++) {
+ PathBOK = _PHY_PathB_IQK(pAdapter);
+ if (PathBOK == 0x03) {
+ DBG_8723A("Path B IQK Success!!\n");
+ result[t][4] = (rtl8723au_read32(pAdapter, rTx_Power_Before_IQK_B)&0x3FF0000)>>16;
+ result[t][5] = (rtl8723au_read32(pAdapter, rTx_Power_After_IQK_B)&0x3FF0000)>>16;
+ result[t][6] = (rtl8723au_read32(pAdapter, rRx_Power_Before_IQK_B_2)&0x3FF0000)>>16;
+ result[t][7] = (rtl8723au_read32(pAdapter, rRx_Power_After_IQK_B_2)&0x3FF0000)>>16;
+ break;
+ } else if (i == (retryCount - 1) && PathBOK == 0x01) {
+ /* Tx IQK OK */
+ DBG_8723A("Path B Only Tx IQK Success!!\n");
+ result[t][4] = (rtl8723au_read32(pAdapter, rTx_Power_Before_IQK_B)&0x3FF0000)>>16;
+ result[t][5] = (rtl8723au_read32(pAdapter, rTx_Power_After_IQK_B)&0x3FF0000)>>16;
+ }
+ }
+
+ if (0x00 == PathBOK) {
+ DBG_8723A("Path B IQK failed!!\n");
+ }
+ }
+
+ /* Back to BB mode, load original value */
+ rtl8723au_write32(pAdapter, rFPGA0_IQK, 0);
+
+ if (t != 0) {
+ if (!pdmpriv->bRfPiEnable) {
+ /* Switch back BB to SI mode after finish IQ Calibration. */
+ _PHY_PIModeSwitch(pAdapter, false);
+ }
+
+ /* Reload ADDA power saving parameters */
+ _PHY_ReloadADDARegisters(pAdapter, ADDA_REG, pdmpriv->ADDA_backup, IQK_ADDA_REG_NUM);
+
+ /* Reload MAC parameters */
+ _PHY_ReloadMACRegisters(pAdapter, IQK_MAC_REG, pdmpriv->IQK_MAC_backup);
+
+ /* Reload BB parameters */
+ _PHY_ReloadADDARegisters(pAdapter, IQK_BB_REG_92C, pdmpriv->IQK_BB_backup, IQK_BB_REG_NUM);
+
+ /* Restore RX initial gain */
+ rtl8723au_write32(pAdapter,
+ rFPGA0_XA_LSSIParameter, 0x00032ed3);
+ if (is2T) {
+ rtl8723au_write32(pAdapter,
+ rFPGA0_XB_LSSIParameter, 0x00032ed3);
+ }
+
+ /* load 0xe30 IQC default value */
+ rtl8723au_write32(pAdapter, rTx_IQK_Tone_A, 0x01008c00);
+ rtl8723au_write32(pAdapter, rRx_IQK_Tone_A, 0x01008c00);
+
+ }
+}
+
+static void _PHY_LCCalibrate(struct rtw_adapter *pAdapter, bool is2T)
+{
+ u8 tmpReg;
+ u32 RF_Amode = 0, RF_Bmode = 0, LC_Cal;
+
+ /* Check continuous TX and Packet TX */
+ tmpReg = rtl8723au_read8(pAdapter, 0xd03);
+
+ if ((tmpReg&0x70) != 0) {
+ /* Deal with contisuous TX case */
+ /* disable all continuous TX */
+ rtl8723au_write8(pAdapter, 0xd03, tmpReg&0x8F);
+ } else {
+ /* Deal with Packet TX case */
+ /* block all queues */
+ rtl8723au_write8(pAdapter, REG_TXPAUSE, 0xFF);
+ }
+
+ if ((tmpReg&0x70) != 0) {
+ /* 1. Read original RF mode */
+ /* Path-A */
+ RF_Amode = PHY_QueryRFReg(pAdapter, RF_PATH_A, RF_AC, bMask12Bits);
+
+ /* Path-B */
+ if (is2T)
+ RF_Bmode = PHY_QueryRFReg(pAdapter, RF_PATH_B, RF_AC, bMask12Bits);
+
+ /* 2. Set RF mode = standby mode */
+ /* Path-A */
+ PHY_SetRFReg(pAdapter, RF_PATH_A, RF_AC, bMask12Bits, (RF_Amode&0x8FFFF)|0x10000);
+
+ /* Path-B */
+ if (is2T)
+ PHY_SetRFReg(pAdapter, RF_PATH_B, RF_AC, bMask12Bits, (RF_Bmode&0x8FFFF)|0x10000);
+ }
+
+ /* 3. Read RF reg18 */
+ LC_Cal = PHY_QueryRFReg(pAdapter, RF_PATH_A, RF_CHNLBW, bMask12Bits);
+
+ /* 4. Set LC calibration begin */
+ PHY_SetRFReg(pAdapter, RF_PATH_A, RF_CHNLBW, bMask12Bits, LC_Cal|0x08000);
+
+ msleep(100);
+
+ /* Restore original situation */
+ if ((tmpReg&0x70) != 0) { /* Deal with contuous TX case */
+ /* Path-A */
+ rtl8723au_write8(pAdapter, 0xd03, tmpReg);
+ PHY_SetRFReg(pAdapter, RF_PATH_A, RF_AC, bMask12Bits, RF_Amode);
+
+ /* Path-B */
+ if (is2T)
+ PHY_SetRFReg(pAdapter, RF_PATH_B, RF_AC, bMask12Bits, RF_Bmode);
+ } else /* Deal with Packet TX case */
+ rtl8723au_write8(pAdapter, REG_TXPAUSE, 0x00);
+}
+
+/* Analog Pre-distortion calibration */
+#define APK_BB_REG_NUM 8
+#define APK_CURVE_REG_NUM 4
+#define PATH_NUM 2
+
+void rtl8723a_phy_iq_calibrate(struct rtw_adapter *pAdapter, bool bReCovery)
+{
+ struct hal_data_8723a *pHalData = GET_HAL_DATA(pAdapter);
+ struct dm_priv *pdmpriv = &pHalData->dmpriv;
+ s32 result[4][8]; /* last is final result */
+ u8 i, final_candidate;
+ bool bPathAOK, bPathBOK;
+ s32 RegE94, RegE9C, RegEA4, RegEAC, RegEB4, RegEBC, RegEC4;
+ s32 RegECC, RegTmp = 0;
+ bool is12simular, is13simular, is23simular;
+ bool bStartContTx = false, bSingleTone = false;
+ bool bCarrierSuppression = false;
+ u32 IQK_BB_REG_92C[IQK_BB_REG_NUM] = {
+ rOFDM0_XARxIQImbalance, rOFDM0_XBRxIQImbalance,
+ rOFDM0_ECCAThreshold, rOFDM0_AGCRSSITable,
+ rOFDM0_XATxIQImbalance, rOFDM0_XBTxIQImbalance,
+ rOFDM0_XCTxAFE, rOFDM0_XDTxAFE,
+ rOFDM0_RxIQExtAnta
+ };
+
+ /* ignore IQK when continuous Tx */
+ if (bStartContTx || bSingleTone || bCarrierSuppression)
+ return;
+
+ if (bReCovery) {
+ _PHY_ReloadADDARegisters(pAdapter, IQK_BB_REG_92C, pdmpriv->IQK_BB_backup_recover, 9);
+ return;
+ }
+ DBG_8723A("IQK:Start!!!\n");
+
+ for (i = 0; i < 8; i++) {
+ result[0][i] = 0;
+ result[1][i] = 0;
+ result[2][i] = 0;
+ result[3][i] = 0;
+ }
+ final_candidate = 0xff;
+ bPathAOK = false;
+ bPathBOK = false;
+ is12simular = false;
+ is23simular = false;
+ is13simular = false;
+
+ for (i = 0; i < 3; i++) {
+ if (pHalData->rf_type == RF_2T2R)
+ _PHY_IQCalibrate(pAdapter, result, i, true);
+ else /* For 88C 1T1R */
+ _PHY_IQCalibrate(pAdapter, result, i, false);
+
+ if (i == 1) {
+ is12simular = _PHY_SimularityCompare(pAdapter, result, 0, 1);
+ if (is12simular) {
+ final_candidate = 0;
+ break;
+ }
+ }
+
+ if (i == 2) {
+ is13simular = _PHY_SimularityCompare(pAdapter, result, 0, 2);
+ if (is13simular) {
+ final_candidate = 0;
+ break;
+ }
+
+ is23simular = _PHY_SimularityCompare(pAdapter, result, 1, 2);
+ if (is23simular) {
+ final_candidate = 1;
+ } else {
+ for (i = 0; i < 8; i++)
+ RegTmp += result[3][i];
+
+ if (RegTmp != 0)
+ final_candidate = 3;
+ else
+ final_candidate = 0xFF;
+ }
+ }
+ }
+
+ for (i = 0; i < 4; i++) {
+ RegE94 = result[i][0];
+ RegE9C = result[i][1];
+ RegEA4 = result[i][2];
+ RegEAC = result[i][3];
+ RegEB4 = result[i][4];
+ RegEBC = result[i][5];
+ RegEC4 = result[i][6];
+ RegECC = result[i][7];
+ }
+
+ if (final_candidate != 0xff) {
+ RegE94 = result[final_candidate][0];
+ pdmpriv->RegE94 = RegE94;
+ RegE9C = result[final_candidate][1];
+ pdmpriv->RegE9C = RegE9C;
+ RegEA4 = result[final_candidate][2];
+ RegEAC = result[final_candidate][3];
+ RegEB4 = result[final_candidate][4];
+ pdmpriv->RegEB4 = RegEB4;
+ RegEBC = result[final_candidate][5];
+ pdmpriv->RegEBC = RegEBC;
+ RegEC4 = result[final_candidate][6];
+ RegECC = result[final_candidate][7];
+ DBG_8723A("IQK: final_candidate is %x\n", final_candidate);
+ DBG_8723A("IQK: RegE94 =%x RegE9C =%x RegEA4 =%x RegEAC =%x RegEB4 =%x RegEBC =%x RegEC4 =%x RegECC =%x\n ",
+ RegE94, RegE9C, RegEA4, RegEAC, RegEB4, RegEBC, RegEC4, RegECC);
+ bPathAOK = bPathBOK = true;
+ } else {
+ RegE94 = RegEB4 = pdmpriv->RegE94 = pdmpriv->RegEB4 = 0x100; /* X default value */
+ RegE9C = RegEBC = pdmpriv->RegE9C = pdmpriv->RegEBC = 0x0; /* Y default value */
+ }
+
+ if ((RegE94 != 0)/*&&(RegEA4 != 0)*/)
+ _PHY_PathAFillIQKMatrix(pAdapter, bPathAOK, result, final_candidate, (RegEA4 == 0));
+
+ if (pHalData->rf_type == RF_2T2R) {
+ if ((RegEB4 != 0)/*&&(RegEC4 != 0)*/)
+ _PHY_PathBFillIQKMatrix(pAdapter, bPathBOK, result,
+ final_candidate, (RegEC4 == 0));
+ }
+
+ _PHY_SaveADDARegisters(pAdapter, IQK_BB_REG_92C, pdmpriv->IQK_BB_backup_recover, 9);
+}
+
+void rtl8723a_phy_lc_calibrate(struct rtw_adapter *pAdapter)
+{
+ struct hal_data_8723a *pHalData = GET_HAL_DATA(pAdapter);
+ struct mlme_ext_priv *pmlmeext = &pAdapter->mlmeextpriv;
+ bool bStartContTx = false, bSingleTone = false, bCarrierSuppression = false;
+
+ /* ignore IQK when continuous Tx */
+ if (bStartContTx || bSingleTone || bCarrierSuppression)
+ return;
+
+ if (pmlmeext->sitesurvey_res.state == SCAN_PROCESS)
+ return;
+
+ if (pHalData->rf_type == RF_2T2R)
+ _PHY_LCCalibrate(pAdapter, true);
+ else /* For 88C 1T1R */
+ _PHY_LCCalibrate(pAdapter, false);
+}
+
+void
+rtl8723a_phy_ap_calibrate(struct rtw_adapter *pAdapter, char delta)
+{
+}
diff --git a/drivers/staging/rtl8723au/hal/HalHWImg8723A_BB.c b/drivers/staging/rtl8723au/hal/HalHWImg8723A_BB.c
new file mode 100644
index 000000000..e8cab9e97
--- /dev/null
+++ b/drivers/staging/rtl8723au/hal/HalHWImg8723A_BB.c
@@ -0,0 +1,565 @@
+/******************************************************************************
+*
+* Copyright(c) 2007 - 2011 Realtek Corporation. All rights reserved.
+*
+* This program is free software; you can redistribute it and/or modify it
+* under the terms of version 2 of the GNU General Public License as
+* published by the Free Software Foundation.
+*
+* 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.
+*
+******************************************************************************/
+
+#include "odm_precomp.h"
+
+static bool CheckCondition(const u32 Condition, const u32 Hex)
+{
+ u32 _board = (Hex & 0x000000FF);
+ u32 _interface = (Hex & 0x0000FF00) >> 8;
+ u32 _platform = (Hex & 0x00FF0000) >> 16;
+ u32 cond = Condition;
+
+ if (Condition == 0xCDCDCDCD)
+ return true;
+
+ cond = Condition & 0x000000FF;
+ if ((_board == cond) && cond != 0x00)
+ return false;
+
+ cond = Condition & 0x0000FF00;
+ cond >>= 8;
+ if ((_interface & cond) == 0 && cond != 0x07)
+ return false;
+
+ cond = Condition & 0x00FF0000;
+ cond >>= 16;
+ if ((_platform & cond) == 0 && cond != 0x0F)
+ return false;
+ return true;
+}
+
+/******************************************************************************
+* AGC_TAB_1T.TXT
+******************************************************************************/
+
+static u32 Array_AGC_TAB_1T_8723A[] = {
+ 0xC78, 0x7B000001,
+ 0xC78, 0x7B010001,
+ 0xC78, 0x7B020001,
+ 0xC78, 0x7B030001,
+ 0xC78, 0x7B040001,
+ 0xC78, 0x7B050001,
+ 0xC78, 0x7A060001,
+ 0xC78, 0x79070001,
+ 0xC78, 0x78080001,
+ 0xC78, 0x77090001,
+ 0xC78, 0x760A0001,
+ 0xC78, 0x750B0001,
+ 0xC78, 0x740C0001,
+ 0xC78, 0x730D0001,
+ 0xC78, 0x720E0001,
+ 0xC78, 0x710F0001,
+ 0xC78, 0x70100001,
+ 0xC78, 0x6F110001,
+ 0xC78, 0x6E120001,
+ 0xC78, 0x6D130001,
+ 0xC78, 0x6C140001,
+ 0xC78, 0x6B150001,
+ 0xC78, 0x6A160001,
+ 0xC78, 0x69170001,
+ 0xC78, 0x68180001,
+ 0xC78, 0x67190001,
+ 0xC78, 0x661A0001,
+ 0xC78, 0x651B0001,
+ 0xC78, 0x641C0001,
+ 0xC78, 0x631D0001,
+ 0xC78, 0x621E0001,
+ 0xC78, 0x611F0001,
+ 0xC78, 0x60200001,
+ 0xC78, 0x49210001,
+ 0xC78, 0x48220001,
+ 0xC78, 0x47230001,
+ 0xC78, 0x46240001,
+ 0xC78, 0x45250001,
+ 0xC78, 0x44260001,
+ 0xC78, 0x43270001,
+ 0xC78, 0x42280001,
+ 0xC78, 0x41290001,
+ 0xC78, 0x402A0001,
+ 0xC78, 0x262B0001,
+ 0xC78, 0x252C0001,
+ 0xC78, 0x242D0001,
+ 0xC78, 0x232E0001,
+ 0xC78, 0x222F0001,
+ 0xC78, 0x21300001,
+ 0xC78, 0x20310001,
+ 0xC78, 0x06320001,
+ 0xC78, 0x05330001,
+ 0xC78, 0x04340001,
+ 0xC78, 0x03350001,
+ 0xC78, 0x02360001,
+ 0xC78, 0x01370001,
+ 0xC78, 0x00380001,
+ 0xC78, 0x00390001,
+ 0xC78, 0x003A0001,
+ 0xC78, 0x003B0001,
+ 0xC78, 0x003C0001,
+ 0xC78, 0x003D0001,
+ 0xC78, 0x003E0001,
+ 0xC78, 0x003F0001,
+ 0xC78, 0x7B400001,
+ 0xC78, 0x7B410001,
+ 0xC78, 0x7B420001,
+ 0xC78, 0x7B430001,
+ 0xC78, 0x7B440001,
+ 0xC78, 0x7B450001,
+ 0xC78, 0x7A460001,
+ 0xC78, 0x79470001,
+ 0xC78, 0x78480001,
+ 0xC78, 0x77490001,
+ 0xC78, 0x764A0001,
+ 0xC78, 0x754B0001,
+ 0xC78, 0x744C0001,
+ 0xC78, 0x734D0001,
+ 0xC78, 0x724E0001,
+ 0xC78, 0x714F0001,
+ 0xC78, 0x70500001,
+ 0xC78, 0x6F510001,
+ 0xC78, 0x6E520001,
+ 0xC78, 0x6D530001,
+ 0xC78, 0x6C540001,
+ 0xC78, 0x6B550001,
+ 0xC78, 0x6A560001,
+ 0xC78, 0x69570001,
+ 0xC78, 0x68580001,
+ 0xC78, 0x67590001,
+ 0xC78, 0x665A0001,
+ 0xC78, 0x655B0001,
+ 0xC78, 0x645C0001,
+ 0xC78, 0x635D0001,
+ 0xC78, 0x625E0001,
+ 0xC78, 0x615F0001,
+ 0xC78, 0x60600001,
+ 0xC78, 0x49610001,
+ 0xC78, 0x48620001,
+ 0xC78, 0x47630001,
+ 0xC78, 0x46640001,
+ 0xC78, 0x45650001,
+ 0xC78, 0x44660001,
+ 0xC78, 0x43670001,
+ 0xC78, 0x42680001,
+ 0xC78, 0x41690001,
+ 0xC78, 0x406A0001,
+ 0xC78, 0x266B0001,
+ 0xC78, 0x256C0001,
+ 0xC78, 0x246D0001,
+ 0xC78, 0x236E0001,
+ 0xC78, 0x226F0001,
+ 0xC78, 0x21700001,
+ 0xC78, 0x20710001,
+ 0xC78, 0x06720001,
+ 0xC78, 0x05730001,
+ 0xC78, 0x04740001,
+ 0xC78, 0x03750001,
+ 0xC78, 0x02760001,
+ 0xC78, 0x01770001,
+ 0xC78, 0x00780001,
+ 0xC78, 0x00790001,
+ 0xC78, 0x007A0001,
+ 0xC78, 0x007B0001,
+ 0xC78, 0x007C0001,
+ 0xC78, 0x007D0001,
+ 0xC78, 0x007E0001,
+ 0xC78, 0x007F0001,
+ 0xC78, 0x3800001E,
+ 0xC78, 0x3801001E,
+ 0xC78, 0x3802001E,
+ 0xC78, 0x3803001E,
+ 0xC78, 0x3804001E,
+ 0xC78, 0x3805001E,
+ 0xC78, 0x3806001E,
+ 0xC78, 0x3807001E,
+ 0xC78, 0x3808001E,
+ 0xC78, 0x3C09001E,
+ 0xC78, 0x3E0A001E,
+ 0xC78, 0x400B001E,
+ 0xC78, 0x440C001E,
+ 0xC78, 0x480D001E,
+ 0xC78, 0x4C0E001E,
+ 0xC78, 0x500F001E,
+ 0xC78, 0x5210001E,
+ 0xC78, 0x5611001E,
+ 0xC78, 0x5A12001E,
+ 0xC78, 0x5E13001E,
+ 0xC78, 0x6014001E,
+ 0xC78, 0x6015001E,
+ 0xC78, 0x6016001E,
+ 0xC78, 0x6217001E,
+ 0xC78, 0x6218001E,
+ 0xC78, 0x6219001E,
+ 0xC78, 0x621A001E,
+ 0xC78, 0x621B001E,
+ 0xC78, 0x621C001E,
+ 0xC78, 0x621D001E,
+ 0xC78, 0x621E001E,
+ 0xC78, 0x621F001E,
+};
+
+#define READ_NEXT_PAIR(v1, v2, i) \
+ do { \
+ i += 2; v1 = Array[i]; v2 = Array[i+1]; \
+ } while (0)
+
+void ODM_ReadAndConfig_AGC_TAB_1T_8723A(struct dm_odm_t *pDM_Odm)
+{
+ u32 hex;
+ u32 i;
+ u8 platform = 0x04;
+ u8 board = pDM_Odm->BoardType;
+ u32 ArrayLen = sizeof(Array_AGC_TAB_1T_8723A)/sizeof(u32);
+ u32 *Array = Array_AGC_TAB_1T_8723A;
+
+ hex = board;
+ hex += ODM_ITRF_USB << 8;
+ hex += platform << 16;
+ hex += 0xFF000000;
+ for (i = 0; i < ArrayLen; i += 2) {
+ u32 v1 = Array[i];
+ u32 v2 = Array[i+1];
+
+ /* This (offset, data) pair meets the condition. */
+ if (v1 < 0xCDCDCDCD) {
+ odm_ConfigBB_AGC_8723A(pDM_Odm, v1, v2);
+ continue;
+ } else {
+ if (!CheckCondition(Array[i], hex)) {
+ /* Discard the following (offset, data) pairs */
+ READ_NEXT_PAIR(v1, v2, i);
+ while (v2 != 0xDEAD &&
+ v2 != 0xCDEF &&
+ v2 != 0xCDCD && i < ArrayLen - 2)
+ READ_NEXT_PAIR(v1, v2, i);
+ i -= 2; /* prevent from for-loop += 2 */
+ } else {
+ /* Configure matched pairs and skip to
+ end of if-else. */
+ READ_NEXT_PAIR(v1, v2, i);
+ while (v2 != 0xDEAD &&
+ v2 != 0xCDEF &&
+ v2 != 0xCDCD && i < ArrayLen - 2) {
+ odm_ConfigBB_AGC_8723A(pDM_Odm, v1, v2);
+ READ_NEXT_PAIR(v1, v2, i);
+ }
+ while (v2 != 0xDEAD && i < ArrayLen - 2)
+ READ_NEXT_PAIR(v1, v2, i);
+ }
+ }
+ }
+}
+
+/******************************************************************************
+* PHY_REG_1T.TXT
+******************************************************************************/
+
+static u32 Array_PHY_REG_1T_8723A[] = {
+ 0x800, 0x80040000,
+ 0x804, 0x00000003,
+ 0x808, 0x0000FC00,
+ 0x80C, 0x0000000A,
+ 0x810, 0x10001331,
+ 0x814, 0x020C3D10,
+ 0x818, 0x02200385,
+ 0x81C, 0x00000000,
+ 0x820, 0x01000100,
+ 0x824, 0x00390004,
+ 0x828, 0x00000000,
+ 0x82C, 0x00000000,
+ 0x830, 0x00000000,
+ 0x834, 0x00000000,
+ 0x838, 0x00000000,
+ 0x83C, 0x00000000,
+ 0x840, 0x00010000,
+ 0x844, 0x00000000,
+ 0x848, 0x00000000,
+ 0x84C, 0x00000000,
+ 0x850, 0x00000000,
+ 0x854, 0x00000000,
+ 0x858, 0x569A569A,
+ 0x85C, 0x001B25A4,
+ 0x860, 0x66F60110,
+ 0x864, 0x061F0130,
+ 0x868, 0x00000000,
+ 0x86C, 0x32323200,
+ 0x870, 0x07000760,
+ 0x874, 0x22004000,
+ 0x878, 0x00000808,
+ 0x87C, 0x00000000,
+ 0x880, 0xC0083070,
+ 0x884, 0x000004D5,
+ 0x888, 0x00000000,
+ 0x88C, 0xCCC000C0,
+ 0x890, 0x00000800,
+ 0x894, 0xFFFFFFFE,
+ 0x898, 0x40302010,
+ 0x89C, 0x00706050,
+ 0x900, 0x00000000,
+ 0x904, 0x00000023,
+ 0x908, 0x00000000,
+ 0x90C, 0x81121111,
+ 0xA00, 0x00D047C8,
+ 0xA04, 0x80FF000C,
+ 0xA08, 0x8C838300,
+ 0xA0C, 0x2E68120F,
+ 0xA10, 0x9500BB78,
+ 0xA14, 0x11144028,
+ 0xA18, 0x00881117,
+ 0xA1C, 0x89140F00,
+ 0xA20, 0x1A1B0000,
+ 0xA24, 0x090E1317,
+ 0xA28, 0x00000204,
+ 0xA2C, 0x00D30000,
+ 0xA70, 0x101FBF00,
+ 0xA74, 0x00000007,
+ 0xA78, 0x00000900,
+ 0xC00, 0x48071D40,
+ 0xC04, 0x03A05611,
+ 0xC08, 0x000000E4,
+ 0xC0C, 0x6C6C6C6C,
+ 0xC10, 0x08800000,
+ 0xC14, 0x40000100,
+ 0xC18, 0x08800000,
+ 0xC1C, 0x40000100,
+ 0xC20, 0x00000000,
+ 0xC24, 0x00000000,
+ 0xC28, 0x00000000,
+ 0xC2C, 0x00000000,
+ 0xC30, 0x69E9AC44,
+ 0xFF0F011F, 0xABCD,
+ 0xC34, 0x469652CF,
+ 0xCDCDCDCD, 0xCDCD,
+ 0xC34, 0x469652AF,
+ 0xFF0F011F, 0xDEAD,
+ 0xC38, 0x49795994,
+ 0xC3C, 0x0A97971C,
+ 0xC40, 0x1F7C403F,
+ 0xC44, 0x000100B7,
+ 0xC48, 0xEC020107,
+ 0xC4C, 0x007F037F,
+ 0xC50, 0x69543420,
+ 0xC54, 0x43BC0094,
+ 0xC58, 0x69543420,
+ 0xC5C, 0x433C0094,
+ 0xC60, 0x00000000,
+ 0xFF0F011F, 0xABCD,
+ 0xC64, 0x7116848B,
+ 0xCDCDCDCD, 0xCDCD,
+ 0xC64, 0x7112848B,
+ 0xFF0F011F, 0xDEAD,
+ 0xC68, 0x47C00BFF,
+ 0xC6C, 0x00000036,
+ 0xC70, 0x2C7F000D,
+ 0xC74, 0x018610DB,
+ 0xC78, 0x0000001F,
+ 0xC7C, 0x00B91612,
+ 0xC80, 0x40000100,
+ 0xC84, 0x20F60000,
+ 0xC88, 0x40000100,
+ 0xC8C, 0x20200000,
+ 0xC90, 0x00121820,
+ 0xC94, 0x00000000,
+ 0xC98, 0x00121820,
+ 0xC9C, 0x00007F7F,
+ 0xCA0, 0x00000000,
+ 0xCA4, 0x00000080,
+ 0xCA8, 0x00000000,
+ 0xCAC, 0x00000000,
+ 0xCB0, 0x00000000,
+ 0xCB4, 0x00000000,
+ 0xCB8, 0x00000000,
+ 0xCBC, 0x28000000,
+ 0xCC0, 0x00000000,
+ 0xCC4, 0x00000000,
+ 0xCC8, 0x00000000,
+ 0xCCC, 0x00000000,
+ 0xCD0, 0x00000000,
+ 0xCD4, 0x00000000,
+ 0xCD8, 0x64B22427,
+ 0xCDC, 0x00766932,
+ 0xCE0, 0x00222222,
+ 0xCE4, 0x00000000,
+ 0xCE8, 0x37644302,
+ 0xCEC, 0x2F97D40C,
+ 0xD00, 0x00080740,
+ 0xD04, 0x00020401,
+ 0xD08, 0x0000907F,
+ 0xD0C, 0x20010201,
+ 0xD10, 0xA0633333,
+ 0xD14, 0x3333BC43,
+ 0xD18, 0x7A8F5B6B,
+ 0xD2C, 0xCC979975,
+ 0xD30, 0x00000000,
+ 0xD34, 0x80608000,
+ 0xD38, 0x00000000,
+ 0xD3C, 0x00027293,
+ 0xD40, 0x00000000,
+ 0xD44, 0x00000000,
+ 0xD48, 0x00000000,
+ 0xD4C, 0x00000000,
+ 0xD50, 0x6437140A,
+ 0xD54, 0x00000000,
+ 0xD58, 0x00000000,
+ 0xD5C, 0x30032064,
+ 0xD60, 0x4653DE68,
+ 0xD64, 0x04518A3C,
+ 0xD68, 0x00002101,
+ 0xD6C, 0x2A201C16,
+ 0xD70, 0x1812362E,
+ 0xD74, 0x322C2220,
+ 0xD78, 0x000E3C24,
+ 0xE00, 0x2A2A2A2A,
+ 0xE04, 0x2A2A2A2A,
+ 0xE08, 0x03902A2A,
+ 0xE10, 0x2A2A2A2A,
+ 0xE14, 0x2A2A2A2A,
+ 0xE18, 0x2A2A2A2A,
+ 0xE1C, 0x2A2A2A2A,
+ 0xE28, 0x00000000,
+ 0xE30, 0x1000DC1F,
+ 0xE34, 0x10008C1F,
+ 0xE38, 0x02140102,
+ 0xE3C, 0x681604C2,
+ 0xE40, 0x01007C00,
+ 0xE44, 0x01004800,
+ 0xE48, 0xFB000000,
+ 0xE4C, 0x000028D1,
+ 0xE50, 0x1000DC1F,
+ 0xE54, 0x10008C1F,
+ 0xE58, 0x02140102,
+ 0xE5C, 0x28160D05,
+ 0xE60, 0x00000008,
+ 0xE68, 0x001B25A4,
+ 0xE6C, 0x631B25A0,
+ 0xE70, 0x631B25A0,
+ 0xE74, 0x081B25A0,
+ 0xE78, 0x081B25A0,
+ 0xE7C, 0x081B25A0,
+ 0xE80, 0x081B25A0,
+ 0xE84, 0x631B25A0,
+ 0xE88, 0x081B25A0,
+ 0xE8C, 0x631B25A0,
+ 0xED0, 0x631B25A0,
+ 0xED4, 0x631B25A0,
+ 0xED8, 0x631B25A0,
+ 0xEDC, 0x001B25A0,
+ 0xEE0, 0x001B25A0,
+ 0xEEC, 0x6B1B25A0,
+ 0xF14, 0x00000003,
+ 0xF4C, 0x00000000,
+ 0xF00, 0x00000300,
+};
+
+void ODM_ReadAndConfig_PHY_REG_1T_8723A(struct dm_odm_t *pDM_Odm)
+{
+ u32 hex = 0;
+ u32 i = 0;
+ u8 platform = 0x04;
+ u8 board = pDM_Odm->BoardType;
+ u32 ArrayLen = sizeof(Array_PHY_REG_1T_8723A)/sizeof(u32);
+ u32 *Array = Array_PHY_REG_1T_8723A;
+
+ hex += board;
+ hex += ODM_ITRF_USB << 8;
+ hex += platform << 16;
+ hex += 0xFF000000;
+ for (i = 0; i < ArrayLen; i += 2) {
+ u32 v1 = Array[i];
+ u32 v2 = Array[i+1];
+
+ /* This (offset, data) pair meets the condition. */
+ if (v1 < 0xCDCDCDCD) {
+ odm_ConfigBB_PHY_8723A(pDM_Odm, v1, v2);
+ continue;
+ } else {
+ if (!CheckCondition(Array[i], hex)) {
+ /* Discard the following (offset, data) pairs */
+ READ_NEXT_PAIR(v1, v2, i);
+ while (v2 != 0xDEAD &&
+ v2 != 0xCDEF &&
+ v2 != 0xCDCD && i < ArrayLen - 2)
+ READ_NEXT_PAIR(v1, v2, i);
+ i -= 2; /* prevent from for-loop += 2 */
+ } else {
+ /* Configure matched pairs and skip to
+ end of if-else. */
+ READ_NEXT_PAIR(v1, v2, i);
+ while (v2 != 0xDEAD &&
+ v2 != 0xCDEF &&
+ v2 != 0xCDCD && i < ArrayLen - 2) {
+ odm_ConfigBB_PHY_8723A(pDM_Odm, v1, v2);
+ READ_NEXT_PAIR(v1, v2, i);
+ }
+ while (v2 != 0xDEAD && i < ArrayLen - 2)
+ READ_NEXT_PAIR(v1, v2, i);
+ }
+ }
+ }
+}
+
+/******************************************************************************
+* PHY_REG_MP.TXT
+******************************************************************************/
+
+static u32 Array_PHY_REG_MP_8723A[] = {
+ 0xC30, 0x69E9AC4A,
+ 0xC3C, 0x0A979718,
+};
+
+void ODM_ReadAndConfig_PHY_REG_MP_8723A(struct dm_odm_t *pDM_Odm)
+{
+ u32 hex = 0;
+ u32 i;
+ u8 platform = 0x04;
+ u8 board = pDM_Odm->BoardType;
+ u32 ArrayLen = sizeof(Array_PHY_REG_MP_8723A)/sizeof(u32);
+ u32 *Array = Array_PHY_REG_MP_8723A;
+
+ hex += board;
+ hex += ODM_ITRF_USB << 8;
+ hex += platform << 16;
+ hex += 0xFF000000;
+ for (i = 0; i < ArrayLen; i += 2) {
+ u32 v1 = Array[i];
+ u32 v2 = Array[i+1];
+
+ /* This (offset, data) pair meets the condition. */
+ if (v1 < 0xCDCDCDCD) {
+ odm_ConfigBB_PHY_8723A(pDM_Odm, v1, v2);
+ continue;
+ } else {
+ if (!CheckCondition(Array[i], hex)) {
+ /* Discard the following (offset, data) pairs */
+ READ_NEXT_PAIR(v1, v2, i);
+ while (v2 != 0xDEAD &&
+ v2 != 0xCDEF &&
+ v2 != 0xCDCD && i < ArrayLen - 2)
+ READ_NEXT_PAIR(v1, v2, i);
+ i -= 2; /* prevent from for-loop += 2 */
+ } else {
+ /* Configure matched pairs and skip to
+ end of if-else. */
+ READ_NEXT_PAIR(v1, v2, i);
+ while (v2 != 0xDEAD &&
+ v2 != 0xCDEF &&
+ v2 != 0xCDCD && i < ArrayLen - 2) {
+ odm_ConfigBB_PHY_8723A(pDM_Odm, v1, v2);
+ READ_NEXT_PAIR(v1, v2, i);
+ }
+ while (v2 != 0xDEAD && i < ArrayLen - 2)
+ READ_NEXT_PAIR(v1, v2, i);
+ }
+ }
+ }
+}
diff --git a/drivers/staging/rtl8723au/hal/HalHWImg8723A_MAC.c b/drivers/staging/rtl8723au/hal/HalHWImg8723A_MAC.c
new file mode 100644
index 000000000..93b2d183d
--- /dev/null
+++ b/drivers/staging/rtl8723au/hal/HalHWImg8723A_MAC.c
@@ -0,0 +1,187 @@
+/******************************************************************************
+*
+* Copyright(c) 2007 - 2011 Realtek Corporation. All rights reserved.
+*
+* This program is free software; you can redistribute it and/or modify it
+* under the terms of version 2 of the GNU General Public License as
+* published by the Free Software Foundation.
+*
+* 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.
+*
+******************************************************************************/
+
+#include "odm_precomp.h"
+
+static bool CheckCondition(const u32 Condition, const u32 Hex)
+{
+ u32 _board = (Hex & 0x000000FF);
+ u32 _interface = (Hex & 0x0000FF00) >> 8;
+ u32 _platform = (Hex & 0x00FF0000) >> 16;
+ u32 cond = Condition;
+
+ if (Condition == 0xCDCDCDCD)
+ return true;
+
+ cond = Condition & 0x000000FF;
+ if ((_board == cond) && cond != 0x00)
+ return false;
+
+ cond = Condition & 0x0000FF00;
+ cond >>= 8;
+ if ((_interface & cond) == 0 && cond != 0x07)
+ return false;
+
+ cond = Condition & 0x00FF0000;
+ cond >>= 16;
+ if ((_platform & cond) == 0 && cond != 0x0F)
+ return false;
+ return true;
+}
+
+/******************************************************************************
+* MAC_REG.TXT
+******************************************************************************/
+
+static u32 Array_MAC_REG_8723A[] = {
+ 0x420, 0x00000080,
+ 0x423, 0x00000000,
+ 0x430, 0x00000000,
+ 0x431, 0x00000000,
+ 0x432, 0x00000000,
+ 0x433, 0x00000001,
+ 0x434, 0x00000004,
+ 0x435, 0x00000005,
+ 0x436, 0x00000006,
+ 0x437, 0x00000007,
+ 0x438, 0x00000000,
+ 0x439, 0x00000000,
+ 0x43A, 0x00000000,
+ 0x43B, 0x00000001,
+ 0x43C, 0x00000004,
+ 0x43D, 0x00000005,
+ 0x43E, 0x00000006,
+ 0x43F, 0x00000007,
+ 0x440, 0x0000005D,
+ 0x441, 0x00000001,
+ 0x442, 0x00000000,
+ 0x444, 0x00000015,
+ 0x445, 0x000000F0,
+ 0x446, 0x0000000F,
+ 0x447, 0x00000000,
+ 0x458, 0x00000041,
+ 0x459, 0x000000A8,
+ 0x45A, 0x00000072,
+ 0x45B, 0x000000B9,
+ 0x460, 0x00000066,
+ 0x461, 0x00000066,
+ 0x462, 0x00000008,
+ 0x463, 0x00000003,
+ 0x4C8, 0x000000FF,
+ 0x4C9, 0x00000008,
+ 0x4CC, 0x000000FF,
+ 0x4CD, 0x000000FF,
+ 0x4CE, 0x00000001,
+ 0x500, 0x00000026,
+ 0x501, 0x000000A2,
+ 0x502, 0x0000002F,
+ 0x503, 0x00000000,
+ 0x504, 0x00000028,
+ 0x505, 0x000000A3,
+ 0x506, 0x0000005E,
+ 0x507, 0x00000000,
+ 0x508, 0x0000002B,
+ 0x509, 0x000000A4,
+ 0x50A, 0x0000005E,
+ 0x50B, 0x00000000,
+ 0x50C, 0x0000004F,
+ 0x50D, 0x000000A4,
+ 0x50E, 0x00000000,
+ 0x50F, 0x00000000,
+ 0x512, 0x0000001C,
+ 0x514, 0x0000000A,
+ 0x515, 0x00000010,
+ 0x516, 0x0000000A,
+ 0x517, 0x00000010,
+ 0x51A, 0x00000016,
+ 0x524, 0x0000000F,
+ 0x525, 0x0000004F,
+ 0x546, 0x00000040,
+ 0x547, 0x00000000,
+ 0x550, 0x00000010,
+ 0x551, 0x00000010,
+ 0x559, 0x00000002,
+ 0x55A, 0x00000002,
+ 0x55D, 0x000000FF,
+ 0x605, 0x00000030,
+ 0x608, 0x0000000E,
+ 0x609, 0x0000002A,
+ 0x652, 0x00000020,
+ 0x63C, 0x0000000A,
+ 0x63D, 0x0000000A,
+ 0x63E, 0x0000000E,
+ 0x63F, 0x0000000E,
+ 0x66E, 0x00000005,
+ 0x700, 0x00000021,
+ 0x701, 0x00000043,
+ 0x702, 0x00000065,
+ 0x703, 0x00000087,
+ 0x708, 0x00000021,
+ 0x709, 0x00000043,
+ 0x70A, 0x00000065,
+ 0x70B, 0x00000087,
+};
+
+void ODM_ReadAndConfig_MAC_REG_8723A(struct dm_odm_t *pDM_Odm)
+{
+ #define READ_NEXT_PAIR(v1, v2, i) \
+ do { \
+ i += 2; v1 = Array[i]; v2 = Array[i+1]; \
+ } while (0)
+
+ u32 hex = 0;
+ u32 i = 0;
+ u8 platform = 0x04;
+ u8 board = pDM_Odm->BoardType;
+ u32 ArrayLen = sizeof(Array_MAC_REG_8723A)/sizeof(u32);
+ u32 *Array = Array_MAC_REG_8723A;
+
+ hex += board;
+ hex += ODM_ITRF_USB << 8;
+ hex += platform << 16;
+ hex += 0xFF000000;
+ for (i = 0; i < ArrayLen; i += 2) {
+ u32 v1 = Array[i];
+ u32 v2 = Array[i+1];
+
+ /* This (offset, data) pair meets the condition. */
+ if (v1 < 0xCDCDCDCD) {
+ odm_ConfigMAC_8723A(pDM_Odm, v1, (u8)v2);
+ continue;
+ } else {
+ if (!CheckCondition(Array[i], hex)) {
+ /* Discard the following (offset, data) pairs. */
+ READ_NEXT_PAIR(v1, v2, i);
+ while (v2 != 0xDEAD &&
+ v2 != 0xCDEF &&
+ v2 != 0xCDCD && i < ArrayLen - 2)
+ READ_NEXT_PAIR(v1, v2, i);
+ i -= 2; /* prevent from for-loop += 2 */
+ } else {
+ /* Configure matched pairs and skip to end of if-else. */
+ READ_NEXT_PAIR(v1, v2, i);
+ while (v2 != 0xDEAD &&
+ v2 != 0xCDEF &&
+ v2 != 0xCDCD && i < ArrayLen - 2) {
+ odm_ConfigMAC_8723A(pDM_Odm, v1, (u8)v2);
+ READ_NEXT_PAIR(v1, v2, i);
+ }
+
+ while (v2 != 0xDEAD && i < ArrayLen - 2)
+ READ_NEXT_PAIR(v1, v2, i);
+ }
+ }
+ }
+}
diff --git a/drivers/staging/rtl8723au/hal/HalHWImg8723A_RF.c b/drivers/staging/rtl8723au/hal/HalHWImg8723A_RF.c
new file mode 100644
index 000000000..dbf571e8b
--- /dev/null
+++ b/drivers/staging/rtl8723au/hal/HalHWImg8723A_RF.c
@@ -0,0 +1,259 @@
+/******************************************************************************
+*
+* Copyright(c) 2007 - 2011 Realtek Corporation. All rights reserved.
+*
+* This program is free software; you can redistribute it and/or modify it
+* under the terms of version 2 of the GNU General Public License as
+* published by the Free Software Foundation.
+*
+* 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.
+*
+******************************************************************************/
+
+#include "odm_precomp.h"
+
+static bool CheckCondition(const u32 Condition, const u32 Hex)
+{
+ u32 _board = (Hex & 0x000000FF);
+ u32 _interface = (Hex & 0x0000FF00) >> 8;
+ u32 _platform = (Hex & 0x00FF0000) >> 16;
+ u32 cond = Condition;
+
+ if (Condition == 0xCDCDCDCD)
+ return true;
+
+ cond = Condition & 0x000000FF;
+ if ((_board == cond) && cond != 0x00)
+ return false;
+
+ cond = Condition & 0x0000FF00;
+ cond >>= 8;
+ if ((_interface & cond) == 0 && cond != 0x07)
+ return false;
+
+ cond = Condition & 0x00FF0000;
+ cond >>= 16;
+ if ((_platform & cond) == 0 && cond != 0x0F)
+ return false;
+ return true;
+}
+
+/******************************************************************************
+* RadioA_1T.TXT
+******************************************************************************/
+
+static u32 Array_RadioA_1T_8723A[] = {
+ 0x000, 0x00030159,
+ 0x001, 0x00031284,
+ 0x002, 0x00098000,
+ 0xFF0F011F, 0xABCD,
+ 0x003, 0x00018C63,
+ 0xCDCDCDCD, 0xCDCD,
+ 0x003, 0x00039C63,
+ 0xFF0F011F, 0xDEAD,
+ 0x004, 0x000210E7,
+ 0x009, 0x0002044F,
+ 0x00A, 0x0001A3F1,
+ 0x00B, 0x00014787,
+ 0x00C, 0x000896FE,
+ 0x00D, 0x0000E02C,
+ 0x00E, 0x00039CE7,
+ 0x00F, 0x00000451,
+ 0x019, 0x00000000,
+ 0x01A, 0x00030355,
+ 0x01B, 0x00060A00,
+ 0x01C, 0x000FC378,
+ 0x01D, 0x000A1250,
+ 0x01E, 0x0000024F,
+ 0x01F, 0x00000000,
+ 0x020, 0x0000B614,
+ 0x021, 0x0006C000,
+ 0x022, 0x00000000,
+ 0x023, 0x00001558,
+ 0x024, 0x00000060,
+ 0x025, 0x00000483,
+ 0x026, 0x0004F000,
+ 0x027, 0x000EC7D9,
+ 0x028, 0x00057730,
+ 0x029, 0x00004783,
+ 0x02A, 0x00000001,
+ 0x02B, 0x00021334,
+ 0x02A, 0x00000000,
+ 0x02B, 0x00000054,
+ 0x02A, 0x00000001,
+ 0x02B, 0x00000808,
+ 0x02B, 0x00053333,
+ 0x02C, 0x0000000C,
+ 0x02A, 0x00000002,
+ 0x02B, 0x00000808,
+ 0x02B, 0x0005B333,
+ 0x02C, 0x0000000D,
+ 0x02A, 0x00000003,
+ 0x02B, 0x00000808,
+ 0x02B, 0x00063333,
+ 0x02C, 0x0000000D,
+ 0x02A, 0x00000004,
+ 0x02B, 0x00000808,
+ 0x02B, 0x0006B333,
+ 0x02C, 0x0000000D,
+ 0x02A, 0x00000005,
+ 0x02B, 0x00000808,
+ 0x02B, 0x00073333,
+ 0x02C, 0x0000000D,
+ 0x02A, 0x00000006,
+ 0x02B, 0x00000709,
+ 0x02B, 0x0005B333,
+ 0x02C, 0x0000000D,
+ 0x02A, 0x00000007,
+ 0x02B, 0x00000709,
+ 0x02B, 0x00063333,
+ 0x02C, 0x0000000D,
+ 0x02A, 0x00000008,
+ 0x02B, 0x0000060A,
+ 0x02B, 0x0004B333,
+ 0x02C, 0x0000000D,
+ 0x02A, 0x00000009,
+ 0x02B, 0x0000060A,
+ 0x02B, 0x00053333,
+ 0x02C, 0x0000000D,
+ 0x02A, 0x0000000A,
+ 0x02B, 0x0000060A,
+ 0x02B, 0x0005B333,
+ 0x02C, 0x0000000D,
+ 0x02A, 0x0000000B,
+ 0x02B, 0x0000060A,
+ 0x02B, 0x00063333,
+ 0x02C, 0x0000000D,
+ 0x02A, 0x0000000C,
+ 0x02B, 0x0000060A,
+ 0x02B, 0x0006B333,
+ 0x02C, 0x0000000D,
+ 0x02A, 0x0000000D,
+ 0x02B, 0x0000060A,
+ 0x02B, 0x00073333,
+ 0x02C, 0x0000000D,
+ 0x02A, 0x0000000E,
+ 0x02B, 0x0000050B,
+ 0x02B, 0x00066666,
+ 0x02C, 0x0000001A,
+ 0x02A, 0x000E0000,
+ 0x010, 0x0004000F,
+ 0x011, 0x000E31FC,
+ 0x010, 0x0006000F,
+ 0x011, 0x000FF9F8,
+ 0x010, 0x0002000F,
+ 0x011, 0x000203F9,
+ 0x010, 0x0003000F,
+ 0x011, 0x000FF500,
+ 0x010, 0x00000000,
+ 0x011, 0x00000000,
+ 0x010, 0x0008000F,
+ 0x011, 0x0003F100,
+ 0x010, 0x0009000F,
+ 0x011, 0x00023100,
+ 0x012, 0x00032000,
+ 0x012, 0x00071000,
+ 0x012, 0x000B0000,
+ 0x012, 0x000FC000,
+ 0x013, 0x000287B3,
+ 0x013, 0x000244B7,
+ 0x013, 0x000204AB,
+ 0x013, 0x0001C49F,
+ 0x013, 0x00018493,
+ 0x013, 0x0001429B,
+ 0x013, 0x00010299,
+ 0x013, 0x0000C29C,
+ 0x013, 0x000081A0,
+ 0x013, 0x000040AC,
+ 0x013, 0x00000020,
+ 0x014, 0x0001944C,
+ 0x014, 0x00059444,
+ 0x014, 0x0009944C,
+ 0x014, 0x000D9444,
+ 0xFF0F011F, 0xABCD,
+ 0x015, 0x0000F424,
+ 0x015, 0x0004F424,
+ 0x015, 0x0008F424,
+ 0x015, 0x000CF424,
+ 0xCDCDCDCD, 0xCDCD,
+ 0x015, 0x0000F474,
+ 0x015, 0x0004F477,
+ 0x015, 0x0008F455,
+ 0x015, 0x000CF455,
+ 0xFF0F011F, 0xDEAD,
+ 0x016, 0x00000339,
+ 0x016, 0x00040339,
+ 0x016, 0x00080339,
+ 0xFF0F011F, 0xABCD,
+ 0x016, 0x000C0356,
+ 0xCDCDCDCD, 0xCDCD,
+ 0x016, 0x000C0366,
+ 0xFF0F011F, 0xDEAD,
+ 0x000, 0x00010159,
+ 0x018, 0x0000F401,
+ 0x0FE, 0x00000000,
+ 0x0FE, 0x00000000,
+ 0x01F, 0x00000003,
+ 0x0FE, 0x00000000,
+ 0x0FE, 0x00000000,
+ 0x01E, 0x00000247,
+ 0x01F, 0x00000000,
+ 0x000, 0x00030159,
+};
+
+void ODM_ReadAndConfig_RadioA_1T_8723A(struct dm_odm_t *pDM_Odm)
+{
+ #define READ_NEXT_PAIR(v1, v2, i) \
+ do { \
+ i += 2; v1 = Array[i]; v2 = Array[i+1];\
+ } while (0)
+
+ u32 hex = 0;
+ u32 i = 0;
+ u8 platform = 0x04;
+ u8 board = pDM_Odm->BoardType;
+ u32 ArrayLen = sizeof(Array_RadioA_1T_8723A)/sizeof(u32);
+ u32 *Array = Array_RadioA_1T_8723A;
+
+ hex += board;
+ hex += ODM_ITRF_USB << 8;
+ hex += platform << 16;
+ hex += 0xFF000000;
+
+ for (i = 0; i < ArrayLen; i += 2) {
+ u32 v1 = Array[i];
+ u32 v2 = Array[i+1];
+
+ /* This (offset, data) pair meets the condition. */
+ if (v1 < 0xCDCDCDCD) {
+ odm_ConfigRFReg_8723A(pDM_Odm, v1, v2, RF_PATH_A, v1);
+ continue;
+ } else {
+ if (!CheckCondition(Array[i], hex)) {
+ /* Discard the following (offset, data) pairs. */
+ READ_NEXT_PAIR(v1, v2, i);
+ while (v2 != 0xDEAD &&
+ v2 != 0xCDEF &&
+ v2 != 0xCDCD && i < ArrayLen - 2)
+ READ_NEXT_PAIR(v1, v2, i);
+ i -= 2; /* prevent from for-loop += 2 */
+ } else {
+ /* Configure matched pairs and skip to end of if-else. */
+ READ_NEXT_PAIR(v1, v2, i);
+ while (v2 != 0xDEAD &&
+ v2 != 0xCDEF &&
+ v2 != 0xCDCD && i < ArrayLen - 2) {
+ odm_ConfigRFReg_8723A(pDM_Odm, v1, v2,
+ RF_PATH_A, v1);
+ READ_NEXT_PAIR(v1, v2, i);
+ }
+
+ while (v2 != 0xDEAD && i < ArrayLen - 2)
+ READ_NEXT_PAIR(v1, v2, i);
+ }
+ }
+ }
+}
diff --git a/drivers/staging/rtl8723au/hal/HalPwrSeqCmd.c b/drivers/staging/rtl8723au/hal/HalPwrSeqCmd.c
new file mode 100644
index 000000000..ae090ab11
--- /dev/null
+++ b/drivers/staging/rtl8723au/hal/HalPwrSeqCmd.c
@@ -0,0 +1,156 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2011 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * 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.
+ *
+ ******************************************************************************/
+/*++
+Copyright (c) Realtek Semiconductor Corp. All rights reserved.
+
+Module Name:
+ HalPwrSeqCmd.c
+
+Abstract:
+ Implement HW Power sequence configuration CMD handling routine for
+ Realtek devices.
+
+Major Change History:
+ When Who What
+ ---------- --------------- -------------------------------
+ 2011-10-26 Lucas Modify to be compatible with SD4-CE driver.
+ 2011-07-07 Roger Create.
+
+--*/
+#include <HalPwrSeqCmd.h>
+#include <usb_ops_linux.h>
+
+/* */
+/* Description: */
+/* This routine deal with the Power Configuration CMDs parsing
+ for RTL8723/RTL8188E Series IC. */
+/* */
+/* Assumption: */
+/* We should follow specific format which was released from
+ HW SD. */
+/* */
+/* 2011.07.07, added by Roger. */
+/* */
+u8 HalPwrSeqCmdParsing23a(struct rtw_adapter *padapter, u8 CutVersion,
+ u8 FabVersion, u8 InterfaceType,
+ struct wlan_pwr_cfg PwrSeqCmd[])
+{
+ struct wlan_pwr_cfg PwrCfgCmd;
+ u8 bPollingBit;
+ u32 AryIdx = 0;
+ u8 value;
+ u32 offset;
+ u32 pollingCount = 0; /* polling autoload done. */
+ u32 maxPollingCnt = 5000;
+
+ do {
+ PwrCfgCmd = PwrSeqCmd[AryIdx];
+
+ RT_TRACE(_module_hal_init_c_, _drv_info_,
+ "HalPwrSeqCmdParsing23a: offset(%#x) cut_msk(%#x) fab_msk(%#x) interface_msk(%#x) base(%#x) cmd(%#x) msk(%#x) value(%#x)\n",
+ GET_PWR_CFG_OFFSET(PwrCfgCmd),
+ GET_PWR_CFG_CUT_MASK(PwrCfgCmd),
+ GET_PWR_CFG_FAB_MASK(PwrCfgCmd),
+ GET_PWR_CFG_INTF_MASK(PwrCfgCmd),
+ GET_PWR_CFG_BASE(PwrCfgCmd),
+ GET_PWR_CFG_CMD(PwrCfgCmd),
+ GET_PWR_CFG_MASK(PwrCfgCmd),
+ GET_PWR_CFG_VALUE(PwrCfgCmd));
+
+ /* 2 Only Handle the command whose FAB, CUT, and Interface are
+ matched */
+ if ((GET_PWR_CFG_FAB_MASK(PwrCfgCmd) & FabVersion) &&
+ (GET_PWR_CFG_CUT_MASK(PwrCfgCmd) & CutVersion) &&
+ (GET_PWR_CFG_INTF_MASK(PwrCfgCmd) & InterfaceType)) {
+ switch (GET_PWR_CFG_CMD(PwrCfgCmd)) {
+ case PWR_CMD_READ:
+ RT_TRACE(_module_hal_init_c_, _drv_info_,
+ "HalPwrSeqCmdParsing23a: PWR_CMD_READ\n");
+ break;
+
+ case PWR_CMD_WRITE:
+ RT_TRACE(_module_hal_init_c_, _drv_info_,
+ "HalPwrSeqCmdParsing23a: PWR_CMD_WRITE\n");
+ offset = GET_PWR_CFG_OFFSET(PwrCfgCmd);
+
+ /* Read the value from system register */
+ value = rtl8723au_read8(padapter, offset);
+
+ value &= ~(GET_PWR_CFG_MASK(PwrCfgCmd));
+ value |= (GET_PWR_CFG_VALUE(PwrCfgCmd) &
+ GET_PWR_CFG_MASK(PwrCfgCmd));
+
+ /* Write the value back to sytem register */
+ rtl8723au_write8(padapter, offset, value);
+ break;
+
+ case PWR_CMD_POLLING:
+ RT_TRACE(_module_hal_init_c_, _drv_info_,
+ "HalPwrSeqCmdParsing23a: PWR_CMD_POLLING\n");
+
+ bPollingBit = false;
+ offset = GET_PWR_CFG_OFFSET(PwrCfgCmd);
+ do {
+ value = rtl8723au_read8(padapter,
+ offset);
+
+ value &= GET_PWR_CFG_MASK(PwrCfgCmd);
+ if (value ==
+ (GET_PWR_CFG_VALUE(PwrCfgCmd) &
+ GET_PWR_CFG_MASK(PwrCfgCmd)))
+ bPollingBit = true;
+ else
+ udelay(10);
+
+ if (pollingCount++ > maxPollingCnt) {
+ DBG_8723A("Fail to polling "
+ "Offset[%#x]\n",
+ offset);
+ return false;
+ }
+ } while (!bPollingBit);
+
+ break;
+
+ case PWR_CMD_DELAY:
+ RT_TRACE(_module_hal_init_c_, _drv_info_,
+ "HalPwrSeqCmdParsing23a: PWR_CMD_DELAY\n");
+ if (GET_PWR_CFG_VALUE(PwrCfgCmd) ==
+ PWRSEQ_DELAY_US)
+ udelay(GET_PWR_CFG_OFFSET(PwrCfgCmd));
+ else
+ udelay(GET_PWR_CFG_OFFSET(PwrCfgCmd) *
+ 1000);
+ break;
+
+ case PWR_CMD_END:
+ /* When this command is parsed, end
+ the process */
+ RT_TRACE(_module_hal_init_c_, _drv_info_,
+ "HalPwrSeqCmdParsing23a: PWR_CMD_END\n");
+ return true;
+
+ default:
+ RT_TRACE(_module_hal_init_c_, _drv_err_,
+ "HalPwrSeqCmdParsing23a: Unknown CMD!!\n");
+ break;
+ }
+ }
+
+ AryIdx++; /* Add Array Index */
+ } while (1);
+
+ return true;
+}
diff --git a/drivers/staging/rtl8723au/hal/hal_com.c b/drivers/staging/rtl8723au/hal/hal_com.c
new file mode 100644
index 000000000..530db57e8
--- /dev/null
+++ b/drivers/staging/rtl8723au/hal/hal_com.c
@@ -0,0 +1,853 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2011 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * 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.
+ *
+ ******************************************************************************/
+#include <osdep_service.h>
+#include <drv_types.h>
+
+#include <hal_intf.h>
+#include <hal_com.h>
+#include <rtl8723a_hal.h>
+#include <usb_ops_linux.h>
+
+#define _HAL_INIT_C_
+
+#define EEPROM_CHANNEL_PLAN_BY_HW_MASK 0x80
+
+/* return the final channel plan decision */
+/* hw_channel_plan: channel plan from HW (efuse/eeprom) */
+/* sw_channel_plan: channel plan from SW (registry/module param) */
+/* def_channel_plan: channel plan used when the former two is invalid */
+u8 hal_com_get_channel_plan23a(struct rtw_adapter *padapter, u8 hw_channel_plan,
+ u8 sw_channel_plan, u8 def_channel_plan,
+ bool AutoLoadFail)
+{
+ u8 swConfig;
+ u8 chnlPlan;
+
+ swConfig = true;
+ if (!AutoLoadFail) {
+ if (!rtw_is_channel_plan_valid(sw_channel_plan))
+ swConfig = false;
+ if (hw_channel_plan & EEPROM_CHANNEL_PLAN_BY_HW_MASK)
+ swConfig = false;
+ }
+
+ if (swConfig == true)
+ chnlPlan = sw_channel_plan;
+ else
+ chnlPlan = hw_channel_plan & (~EEPROM_CHANNEL_PLAN_BY_HW_MASK);
+
+ if (!rtw_is_channel_plan_valid(chnlPlan))
+ chnlPlan = def_channel_plan;
+
+ return chnlPlan;
+}
+
+u8 MRateToHwRate23a(u8 rate)
+{
+ u8 ret = DESC_RATE1M;
+
+ switch (rate) {
+ /* CCK and OFDM non-HT rates */
+ case IEEE80211_CCK_RATE_1MB:
+ ret = DESC_RATE1M;
+ break;
+ case IEEE80211_CCK_RATE_2MB:
+ ret = DESC_RATE2M;
+ break;
+ case IEEE80211_CCK_RATE_5MB:
+ ret = DESC_RATE5_5M;
+ break;
+ case IEEE80211_CCK_RATE_11MB:
+ ret = DESC_RATE11M;
+ break;
+ case IEEE80211_OFDM_RATE_6MB:
+ ret = DESC_RATE6M;
+ break;
+ case IEEE80211_OFDM_RATE_9MB:
+ ret = DESC_RATE9M;
+ break;
+ case IEEE80211_OFDM_RATE_12MB:
+ ret = DESC_RATE12M;
+ break;
+ case IEEE80211_OFDM_RATE_18MB:
+ ret = DESC_RATE18M;
+ break;
+ case IEEE80211_OFDM_RATE_24MB:
+ ret = DESC_RATE24M;
+ break;
+ case IEEE80211_OFDM_RATE_36MB:
+ ret = DESC_RATE36M;
+ break;
+ case IEEE80211_OFDM_RATE_48MB:
+ ret = DESC_RATE48M;
+ break;
+ case IEEE80211_OFDM_RATE_54MB:
+ ret = DESC_RATE54M;
+ break;
+
+ /* HT rates since here */
+ /* case MGN_MCS0: ret = DESC_RATEMCS0; break; */
+ /* case MGN_MCS1: ret = DESC_RATEMCS1; break; */
+ /* case MGN_MCS2: ret = DESC_RATEMCS2; break; */
+ /* case MGN_MCS3: ret = DESC_RATEMCS3; break; */
+ /* case MGN_MCS4: ret = DESC_RATEMCS4; break; */
+ /* case MGN_MCS5: ret = DESC_RATEMCS5; break; */
+ /* case MGN_MCS6: ret = DESC_RATEMCS6; break; */
+ /* case MGN_MCS7: ret = DESC_RATEMCS7; break; */
+
+ default:
+ break;
+ }
+ return ret;
+}
+
+void HalSetBrateCfg23a(struct rtw_adapter *padapter, u8 *mBratesOS)
+{
+ struct hal_data_8723a *pHalData = GET_HAL_DATA(padapter);
+ u8 i, is_brate, brate;
+ u16 brate_cfg = 0;
+ u8 rate_index;
+
+ for (i = 0; i < NDIS_802_11_LENGTH_RATES_EX; i++) {
+ is_brate = mBratesOS[i] & IEEE80211_BASIC_RATE_MASK;
+ brate = mBratesOS[i] & 0x7f;
+
+ if (is_brate) {
+ switch (brate) {
+ case IEEE80211_CCK_RATE_1MB:
+ brate_cfg |= RATE_1M;
+ break;
+ case IEEE80211_CCK_RATE_2MB:
+ brate_cfg |= RATE_2M;
+ break;
+ case IEEE80211_CCK_RATE_5MB:
+ brate_cfg |= RATE_5_5M;
+ break;
+ case IEEE80211_CCK_RATE_11MB:
+ brate_cfg |= RATE_11M;
+ break;
+ case IEEE80211_OFDM_RATE_6MB:
+ brate_cfg |= RATE_6M;
+ break;
+ case IEEE80211_OFDM_RATE_9MB:
+ brate_cfg |= RATE_9M;
+ break;
+ case IEEE80211_OFDM_RATE_12MB:
+ brate_cfg |= RATE_12M;
+ break;
+ case IEEE80211_OFDM_RATE_18MB:
+ brate_cfg |= RATE_18M;
+ break;
+ case IEEE80211_OFDM_RATE_24MB:
+ brate_cfg |= RATE_24M;
+ break;
+ case IEEE80211_OFDM_RATE_36MB:
+ brate_cfg |= RATE_36M;
+ break;
+ case IEEE80211_OFDM_RATE_48MB:
+ brate_cfg |= RATE_48M;
+ break;
+ case IEEE80211_OFDM_RATE_54MB:
+ brate_cfg |= RATE_54M;
+ break;
+ }
+ }
+ }
+
+ /* 2007.01.16, by Emily */
+ /* Select RRSR (in Legacy-OFDM and CCK) */
+ /* For 8190, we select only 24M, 12M, 6M, 11M, 5.5M, 2M,
+ and 1M from the Basic rate. */
+ /* We do not use other rates. */
+ /* 2011.03.30 add by Luke Lee */
+ /* CCK 2M ACK should be disabled for some BCM and Atheros AP IOT */
+ /* because CCK 2M has poor TXEVM */
+ /* CCK 5.5M & 11M ACK should be enabled for better
+ performance */
+
+ brate_cfg = (brate_cfg | 0xd) & 0x15d;
+ pHalData->BasicRateSet = brate_cfg;
+ brate_cfg |= 0x01; /* default enable 1M ACK rate */
+ DBG_8723A("HW_VAR_BASIC_RATE: BrateCfg(%#x)\n", brate_cfg);
+
+ /* Set RRSR rate table. */
+ rtl8723au_write8(padapter, REG_RRSR, brate_cfg & 0xff);
+ rtl8723au_write8(padapter, REG_RRSR + 1, (brate_cfg >> 8) & 0xff);
+ rtl8723au_write8(padapter, REG_RRSR + 2,
+ rtl8723au_read8(padapter, REG_RRSR + 2) & 0xf0);
+
+ rate_index = 0;
+ /* Set RTS initial rate */
+ while (brate_cfg > 0x1) {
+ brate_cfg >>= 1;
+ rate_index++;
+ }
+ /* Ziv - Check */
+ rtl8723au_write8(padapter, REG_INIRTS_RATE_SEL, rate_index);
+}
+
+static void _OneOutPipeMapping(struct rtw_adapter *pAdapter)
+{
+ struct dvobj_priv *pdvobjpriv = adapter_to_dvobj(pAdapter);
+
+ pdvobjpriv->Queue2Pipe[0] = pdvobjpriv->RtOutPipe[0]; /* VO */
+ pdvobjpriv->Queue2Pipe[1] = pdvobjpriv->RtOutPipe[0]; /* VI */
+ pdvobjpriv->Queue2Pipe[2] = pdvobjpriv->RtOutPipe[0]; /* BE */
+ pdvobjpriv->Queue2Pipe[3] = pdvobjpriv->RtOutPipe[0]; /* BK */
+
+ pdvobjpriv->Queue2Pipe[4] = pdvobjpriv->RtOutPipe[0]; /* BCN */
+ pdvobjpriv->Queue2Pipe[5] = pdvobjpriv->RtOutPipe[0]; /* MGT */
+ pdvobjpriv->Queue2Pipe[6] = pdvobjpriv->RtOutPipe[0]; /* HIGH */
+ pdvobjpriv->Queue2Pipe[7] = pdvobjpriv->RtOutPipe[0]; /* TXCMD */
+}
+
+static void _TwoOutPipeMapping(struct rtw_adapter *pAdapter, bool bWIFICfg)
+{
+ struct dvobj_priv *pdvobjpriv = adapter_to_dvobj(pAdapter);
+
+ if (bWIFICfg) { /* WMM */
+ /* BK, BE, VI, VO, BCN, CMD, MGT, HIGH, HCCA */
+ /* 0, 1, 0, 1, 0, 0, 0, 0, 0 }; */
+ /* 0:H, 1:L */
+ pdvobjpriv->Queue2Pipe[0] = pdvobjpriv->RtOutPipe[1]; /* VO */
+ pdvobjpriv->Queue2Pipe[1] = pdvobjpriv->RtOutPipe[0]; /* VI */
+ pdvobjpriv->Queue2Pipe[2] = pdvobjpriv->RtOutPipe[1]; /* BE */
+ pdvobjpriv->Queue2Pipe[3] = pdvobjpriv->RtOutPipe[0]; /* BK */
+
+ pdvobjpriv->Queue2Pipe[4] = pdvobjpriv->RtOutPipe[0]; /* BCN */
+ pdvobjpriv->Queue2Pipe[5] = pdvobjpriv->RtOutPipe[0]; /* MGT */
+ pdvobjpriv->Queue2Pipe[6] = pdvobjpriv->RtOutPipe[0]; /* HIGH */
+ pdvobjpriv->Queue2Pipe[7] = pdvobjpriv->RtOutPipe[0]; /* TXCMD*/
+ } else { /* typical setting */
+ /* BK, BE, VI, VO, BCN, CMD, MGT, HIGH, HCCA */
+ /* 1, 1, 0, 0, 0, 0, 0, 0, 0 }; */
+ /* 0:H, 1:L */
+ pdvobjpriv->Queue2Pipe[0] = pdvobjpriv->RtOutPipe[0]; /* VO */
+ pdvobjpriv->Queue2Pipe[1] = pdvobjpriv->RtOutPipe[0]; /* VI */
+ pdvobjpriv->Queue2Pipe[2] = pdvobjpriv->RtOutPipe[1]; /* BE */
+ pdvobjpriv->Queue2Pipe[3] = pdvobjpriv->RtOutPipe[1]; /* BK */
+
+ pdvobjpriv->Queue2Pipe[4] = pdvobjpriv->RtOutPipe[0]; /* BCN */
+ pdvobjpriv->Queue2Pipe[5] = pdvobjpriv->RtOutPipe[0]; /* MGT */
+ pdvobjpriv->Queue2Pipe[6] = pdvobjpriv->RtOutPipe[0]; /* HIGH */
+ pdvobjpriv->Queue2Pipe[7] = pdvobjpriv->RtOutPipe[0]; /* TXCMD*/
+ }
+}
+
+static void _ThreeOutPipeMapping(struct rtw_adapter *pAdapter, bool bWIFICfg)
+{
+ struct dvobj_priv *pdvobjpriv = adapter_to_dvobj(pAdapter);
+
+ if (bWIFICfg) { /* for WMM */
+ /* BK, BE, VI, VO, BCN, CMD, MGT, HIGH, HCCA */
+ /* 1, 2, 1, 0, 0, 0, 0, 0, 0 }; */
+ /* 0:H, 1:N, 2:L */
+ pdvobjpriv->Queue2Pipe[0] = pdvobjpriv->RtOutPipe[0]; /* VO */
+ pdvobjpriv->Queue2Pipe[1] = pdvobjpriv->RtOutPipe[1]; /* VI */
+ pdvobjpriv->Queue2Pipe[2] = pdvobjpriv->RtOutPipe[2]; /* BE */
+ pdvobjpriv->Queue2Pipe[3] = pdvobjpriv->RtOutPipe[1]; /* BK */
+
+ pdvobjpriv->Queue2Pipe[4] = pdvobjpriv->RtOutPipe[0]; /* BCN */
+ pdvobjpriv->Queue2Pipe[5] = pdvobjpriv->RtOutPipe[0]; /* MGT */
+ pdvobjpriv->Queue2Pipe[6] = pdvobjpriv->RtOutPipe[0]; /* HIGH */
+ pdvobjpriv->Queue2Pipe[7] = pdvobjpriv->RtOutPipe[0]; /* TXCMD*/
+ } else { /* typical setting */
+ /* BK, BE, VI, VO, BCN, CMD, MGT, HIGH, HCCA */
+ /* 2, 2, 1, 0, 0, 0, 0, 0, 0 }; */
+ /* 0:H, 1:N, 2:L */
+ pdvobjpriv->Queue2Pipe[0] = pdvobjpriv->RtOutPipe[0]; /* VO */
+ pdvobjpriv->Queue2Pipe[1] = pdvobjpriv->RtOutPipe[1]; /* VI */
+ pdvobjpriv->Queue2Pipe[2] = pdvobjpriv->RtOutPipe[2]; /* BE */
+ pdvobjpriv->Queue2Pipe[3] = pdvobjpriv->RtOutPipe[2]; /* BK */
+
+ pdvobjpriv->Queue2Pipe[4] = pdvobjpriv->RtOutPipe[0]; /* BCN */
+ pdvobjpriv->Queue2Pipe[5] = pdvobjpriv->RtOutPipe[0]; /* MGT */
+ pdvobjpriv->Queue2Pipe[6] = pdvobjpriv->RtOutPipe[0]; /* HIGH */
+ pdvobjpriv->Queue2Pipe[7] = pdvobjpriv->RtOutPipe[0]; /* TXCMD*/
+ }
+}
+
+bool Hal_MappingOutPipe23a(struct rtw_adapter *pAdapter, u8 NumOutPipe)
+{
+ struct registry_priv *pregistrypriv = &pAdapter->registrypriv;
+ bool bWIFICfg = (pregistrypriv->wifi_spec) ? true : false;
+ bool result = true;
+
+ switch (NumOutPipe) {
+ case 2:
+ _TwoOutPipeMapping(pAdapter, bWIFICfg);
+ break;
+ case 3:
+ _ThreeOutPipeMapping(pAdapter, bWIFICfg);
+ break;
+ case 1:
+ _OneOutPipeMapping(pAdapter);
+ break;
+ default:
+ result = false;
+ break;
+ }
+
+ return result;
+}
+
+/*
+* C2H event format:
+* Field TRIGGER CONTENT CMD_SEQ CMD_LEN CMD_ID
+* BITS [127:120] [119:16] [15:8] [7:4] [3:0]
+*/
+
+void c2h_evt_clear23a(struct rtw_adapter *adapter)
+{
+ rtl8723au_write8(adapter, REG_C2HEVT_CLEAR, C2H_EVT_HOST_CLOSE);
+}
+
+int c2h_evt_read23a(struct rtw_adapter *adapter, u8 *buf)
+{
+ int ret = _FAIL;
+ struct c2h_evt_hdr *c2h_evt;
+ int i;
+ u8 trigger;
+
+ if (buf == NULL)
+ goto exit;
+
+ trigger = rtl8723au_read8(adapter, REG_C2HEVT_CLEAR);
+
+ if (trigger == C2H_EVT_HOST_CLOSE)
+ goto exit; /* Not ready */
+ else if (trigger != C2H_EVT_FW_CLOSE)
+ goto clear_evt; /* Not a valid value */
+
+ c2h_evt = (struct c2h_evt_hdr *)buf;
+
+ memset(c2h_evt, 0, 16);
+
+ *buf = rtl8723au_read8(adapter, REG_C2HEVT_MSG_NORMAL);
+ *(buf + 1) = rtl8723au_read8(adapter, REG_C2HEVT_MSG_NORMAL + 1);
+
+ RT_PRINT_DATA(_module_hal_init_c_, _drv_info_, "c2h_evt_read23a(): ",
+ &c2h_evt, sizeof(c2h_evt));
+
+ if (0) {
+ DBG_8723A("%s id:%u, len:%u, seq:%u, trigger:0x%02x\n",
+ __func__, c2h_evt->id, c2h_evt->plen, c2h_evt->seq,
+ trigger);
+ }
+
+ /* Read the content */
+ for (i = 0; i < c2h_evt->plen; i++)
+ c2h_evt->payload[i] = rtl8723au_read8(adapter,
+ REG_C2HEVT_MSG_NORMAL +
+ sizeof(*c2h_evt) + i);
+
+ RT_PRINT_DATA(_module_hal_init_c_, _drv_info_,
+ "c2h_evt_read23a(): Command Content:\n", c2h_evt->payload,
+ c2h_evt->plen);
+
+ ret = _SUCCESS;
+
+clear_evt:
+ /*
+ * Clear event to notify FW we have read the command.
+ * If this field isn't clear, the FW won't update the
+ * next command message.
+ */
+ c2h_evt_clear23a(adapter);
+exit:
+ return ret;
+}
+
+void
+rtl8723a_set_ampdu_min_space(struct rtw_adapter *padapter, u8 MinSpacingToSet)
+{
+ u8 SecMinSpace;
+
+ if (MinSpacingToSet <= 7) {
+ switch (padapter->securitypriv.dot11PrivacyAlgrthm) {
+ case 0:
+ case WLAN_CIPHER_SUITE_CCMP:
+ SecMinSpace = 0;
+ break;
+
+ case WLAN_CIPHER_SUITE_WEP40:
+ case WLAN_CIPHER_SUITE_WEP104:
+ case WLAN_CIPHER_SUITE_TKIP:
+ SecMinSpace = 6;
+ break;
+ default:
+ SecMinSpace = 7;
+ break;
+ }
+
+ if (MinSpacingToSet < SecMinSpace)
+ MinSpacingToSet = SecMinSpace;
+
+ MinSpacingToSet |=
+ rtl8723au_read8(padapter, REG_AMPDU_MIN_SPACE) & 0xf8;
+ rtl8723au_write8(padapter, REG_AMPDU_MIN_SPACE,
+ MinSpacingToSet);
+ }
+}
+
+void rtl8723a_set_ampdu_factor(struct rtw_adapter *padapter, u8 FactorToSet)
+{
+ u8 RegToSet_Normal[4] = { 0x41, 0xa8, 0x72, 0xb9 };
+ u8 MaxAggNum;
+ u8 *pRegToSet;
+ u8 index = 0;
+
+ pRegToSet = RegToSet_Normal; /* 0xb972a841; */
+
+ if (rtl8723a_BT_enabled(padapter) &&
+ rtl8723a_BT_using_antenna_1(padapter))
+ MaxAggNum = 0x8;
+ else
+ MaxAggNum = 0xF;
+
+ if (FactorToSet <= 3) {
+ FactorToSet = 1 << (FactorToSet + 2);
+ if (FactorToSet > MaxAggNum)
+ FactorToSet = MaxAggNum;
+
+ for (index = 0; index < 4; index++) {
+ if ((pRegToSet[index] & 0xf0) > (FactorToSet << 4))
+ pRegToSet[index] = (pRegToSet[index] & 0x0f) |
+ (FactorToSet << 4);
+
+ if ((pRegToSet[index] & 0x0f) > FactorToSet)
+ pRegToSet[index] = (pRegToSet[index] & 0xf0) |
+ FactorToSet;
+
+ rtl8723au_write8(padapter, REG_AGGLEN_LMT + index,
+ pRegToSet[index]);
+ }
+ }
+}
+
+void rtl8723a_set_acm_ctrl(struct rtw_adapter *padapter, u8 ctrl)
+{
+ u8 hwctrl = 0;
+
+ if (ctrl != 0) {
+ hwctrl |= AcmHw_HwEn;
+
+ if (ctrl & BIT(1)) /* BE */
+ hwctrl |= AcmHw_BeqEn;
+
+ if (ctrl & BIT(2)) /* VI */
+ hwctrl |= AcmHw_ViqEn;
+
+ if (ctrl & BIT(3)) /* VO */
+ hwctrl |= AcmHw_VoqEn;
+ }
+
+ DBG_8723A("[HW_VAR_ACM_CTRL] Write 0x%02X\n", hwctrl);
+ rtl8723au_write8(padapter, REG_ACMHWCTRL, hwctrl);
+}
+
+void rtl8723a_set_media_status(struct rtw_adapter *padapter, u8 status)
+{
+ u8 val8;
+
+ val8 = rtl8723au_read8(padapter, MSR) & 0x0c;
+ val8 |= status;
+ rtl8723au_write8(padapter, MSR, val8);
+}
+
+void rtl8723a_set_media_status1(struct rtw_adapter *padapter, u8 status)
+{
+ u8 val8;
+
+ val8 = rtl8723au_read8(padapter, MSR) & 0x03;
+ val8 |= status << 2;
+ rtl8723au_write8(padapter, MSR, val8);
+}
+
+void rtl8723a_set_bcn_func(struct rtw_adapter *padapter, u8 val)
+{
+ if (val)
+ SetBcnCtrlReg23a(padapter, EN_BCN_FUNCTION | EN_TXBCN_RPT, 0);
+ else
+ SetBcnCtrlReg23a(padapter, 0, EN_BCN_FUNCTION | EN_TXBCN_RPT);
+}
+
+void rtl8723a_check_bssid(struct rtw_adapter *padapter, u8 val)
+{
+ u32 val32;
+
+ val32 = rtl8723au_read32(padapter, REG_RCR);
+ if (val)
+ val32 |= RCR_CBSSID_DATA | RCR_CBSSID_BCN;
+ else
+ val32 &= ~(RCR_CBSSID_DATA | RCR_CBSSID_BCN);
+ rtl8723au_write32(padapter, REG_RCR, val32);
+}
+
+void rtl8723a_mlme_sitesurvey(struct rtw_adapter *padapter, u8 flag)
+{
+ if (flag) { /* under sitesurvey */
+ u32 v32;
+
+ /* config RCR to receive different BSSID & not
+ to receive data frame */
+ v32 = rtl8723au_read32(padapter, REG_RCR);
+ v32 &= ~(RCR_CBSSID_BCN);
+ rtl8723au_write32(padapter, REG_RCR, v32);
+ /* reject all data frame */
+ rtl8723au_write16(padapter, REG_RXFLTMAP2, 0);
+
+ /* disable update TSF */
+ SetBcnCtrlReg23a(padapter, DIS_TSF_UDT, 0);
+ } else { /* sitesurvey done */
+
+ struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv;
+ struct mlme_ext_info *pmlmeinfo;
+ u32 v32;
+
+ pmlmeinfo = &pmlmeext->mlmext_info;
+
+ if ((is_client_associated_to_ap23a(padapter) == true) ||
+ ((pmlmeinfo->state & 0x03) == MSR_ADHOC) ||
+ ((pmlmeinfo->state & 0x03) == MSR_AP)) {
+ /* enable to rx data frame */
+ rtl8723au_write16(padapter, REG_RXFLTMAP2, 0xFFFF);
+
+ /* enable update TSF */
+ SetBcnCtrlReg23a(padapter, 0, DIS_TSF_UDT);
+ }
+
+ v32 = rtl8723au_read32(padapter, REG_RCR);
+ v32 |= RCR_CBSSID_BCN;
+ rtl8723au_write32(padapter, REG_RCR, v32);
+ }
+
+ rtl8723a_BT_wifiscan_notify(padapter, flag ? true : false);
+}
+
+void rtl8723a_on_rcr_am(struct rtw_adapter *padapter)
+{
+ rtl8723au_write32(padapter, REG_RCR,
+ rtl8723au_read32(padapter, REG_RCR) | RCR_AM);
+ DBG_8723A("%s, %d, RCR = %x\n", __func__, __LINE__,
+ rtl8723au_read32(padapter, REG_RCR));
+}
+
+void rtl8723a_off_rcr_am(struct rtw_adapter *padapter)
+{
+ rtl8723au_write32(padapter, REG_RCR,
+ rtl8723au_read32(padapter, REG_RCR) & (~RCR_AM));
+ DBG_8723A("%s, %d, RCR = %x\n", __func__, __LINE__,
+ rtl8723au_read32(padapter, REG_RCR));
+}
+
+void rtl8723a_set_slot_time(struct rtw_adapter *padapter, u8 slottime)
+{
+ u8 u1bAIFS, aSifsTime;
+ struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv;
+ struct mlme_ext_info *pmlmeinfo = &pmlmeext->mlmext_info;
+
+ rtl8723au_write8(padapter, REG_SLOT, slottime);
+
+ if (pmlmeinfo->WMM_enable == 0) {
+ if (pmlmeext->cur_wireless_mode == WIRELESS_11B)
+ aSifsTime = 10;
+ else
+ aSifsTime = 16;
+
+ u1bAIFS = aSifsTime + (2 * pmlmeinfo->slotTime);
+
+ /* <Roger_EXP> Temporary removed, 2008.06.20. */
+ rtl8723au_write8(padapter, REG_EDCA_VO_PARAM, u1bAIFS);
+ rtl8723au_write8(padapter, REG_EDCA_VI_PARAM, u1bAIFS);
+ rtl8723au_write8(padapter, REG_EDCA_BE_PARAM, u1bAIFS);
+ rtl8723au_write8(padapter, REG_EDCA_BK_PARAM, u1bAIFS);
+ }
+}
+
+void rtl8723a_ack_preamble(struct rtw_adapter *padapter, u8 bShortPreamble)
+{
+ struct hal_data_8723a *pHalData = GET_HAL_DATA(padapter);
+ u8 regTmp;
+
+ /* Joseph marked out for Netgear 3500 TKIP
+ channel 7 issue.(Temporarily) */
+ regTmp = (pHalData->nCur40MhzPrimeSC) << 5;
+ /* regTmp = 0; */
+ if (bShortPreamble)
+ regTmp |= 0x80;
+ rtl8723au_write8(padapter, REG_RRSR + 2, regTmp);
+}
+
+void rtl8723a_set_sec_cfg(struct rtw_adapter *padapter, u8 sec)
+{
+ rtl8723au_write8(padapter, REG_SECCFG, sec);
+}
+
+void rtl8723a_cam_empty_entry(struct rtw_adapter *padapter, u8 ucIndex)
+{
+ u8 i;
+ u32 ulCommand = 0;
+ u32 ulContent = 0;
+ u32 ulEncAlgo = CAM_AES;
+
+ for (i = 0; i < CAM_CONTENT_COUNT; i++) {
+ /* filled id in CAM config 2 byte */
+ if (i == 0) {
+ ulContent |= (ucIndex & 0x03) |
+ ((u16) (ulEncAlgo) << 2);
+ /* ulContent |= CAM_VALID; */
+ } else {
+ ulContent = 0;
+ }
+ /* polling bit, and No Write enable, and address */
+ ulCommand = CAM_CONTENT_COUNT * ucIndex + i;
+ ulCommand = ulCommand | CAM_POLLINIG | CAM_WRITE;
+ /* write content 0 is equall to mark invalid */
+ /* delay_ms(40); */
+ rtl8723au_write32(padapter, WCAMI, ulContent);
+ /* delay_ms(40); */
+ rtl8723au_write32(padapter, REG_CAMCMD, ulCommand);
+ }
+}
+
+void rtl8723a_cam_invalidate_all(struct rtw_adapter *padapter)
+{
+ rtl8723au_write32(padapter, REG_CAMCMD, CAM_POLLINIG | BIT(30));
+}
+
+void rtl8723a_cam_write(struct rtw_adapter *padapter,
+ u8 entry, u16 ctrl, const u8 *mac, const u8 *key)
+{
+ u32 cmd;
+ unsigned int i, val, addr;
+ int j;
+
+ addr = entry << 3;
+
+ for (j = 5; j >= 0; j--) {
+ switch (j) {
+ case 0:
+ val = ctrl | (mac[0] << 16) | (mac[1] << 24);
+ break;
+ case 1:
+ val = mac[2] | (mac[3] << 8) |
+ (mac[4] << 16) | (mac[5] << 24);
+ break;
+ default:
+ i = (j - 2) << 2;
+ val = key[i] | (key[i+1] << 8) |
+ (key[i+2] << 16) | (key[i+3] << 24);
+ break;
+ }
+
+ rtl8723au_write32(padapter, WCAMI, val);
+ cmd = CAM_POLLINIG | CAM_WRITE | (addr + j);
+ rtl8723au_write32(padapter, REG_CAMCMD, cmd);
+
+ /* DBG_8723A("%s => cam write: %x, %x\n", __func__, cmd, val);*/
+ }
+}
+
+void rtl8723a_fifo_cleanup(struct rtw_adapter *padapter)
+{
+#define RW_RELEASE_EN BIT(18)
+#define RXDMA_IDLE BIT(17)
+
+ struct pwrctrl_priv *pwrpriv = &padapter->pwrctrlpriv;
+ u8 trycnt = 100;
+
+ /* pause tx */
+ rtl8723au_write8(padapter, REG_TXPAUSE, 0xff);
+
+ /* keep sn */
+ padapter->xmitpriv.nqos_ssn = rtl8723au_read8(padapter, REG_NQOS_SEQ);
+
+ if (pwrpriv->bkeepfwalive != true) {
+ u32 v32;
+
+ /* RX DMA stop */
+ v32 = rtl8723au_read32(padapter, REG_RXPKT_NUM);
+ v32 |= RW_RELEASE_EN;
+ rtl8723au_write32(padapter, REG_RXPKT_NUM, v32);
+ do {
+ v32 = rtl8723au_read32(padapter,
+ REG_RXPKT_NUM) & RXDMA_IDLE;
+ if (!v32)
+ break;
+ } while (trycnt--);
+ if (trycnt == 0)
+ DBG_8723A("Stop RX DMA failed......\n");
+
+ /* RQPN Load 0 */
+ rtl8723au_write16(padapter, REG_RQPN_NPQ, 0);
+ rtl8723au_write32(padapter, REG_RQPN, 0x80000000);
+ mdelay(10);
+ }
+}
+
+void rtl8723a_bcn_valid(struct rtw_adapter *padapter)
+{
+ /* BCN_VALID, BIT16 of REG_TDECTRL = BIT0 of REG_TDECTRL+2,
+ write 1 to clear, Clear by sw */
+ rtl8723au_write8(padapter, REG_TDECTRL + 2,
+ rtl8723au_read8(padapter, REG_TDECTRL + 2) | BIT(0));
+}
+
+bool rtl8723a_get_bcn_valid(struct rtw_adapter *padapter)
+{
+ bool retval;
+
+ retval = (rtl8723au_read8(padapter, REG_TDECTRL + 2) & BIT(0)) ? true : false;
+
+ return retval;
+}
+
+void rtl8723a_set_beacon_interval(struct rtw_adapter *padapter, u16 interval)
+{
+ rtl8723au_write16(padapter, REG_BCN_INTERVAL, interval);
+}
+
+void rtl8723a_set_resp_sifs(struct rtw_adapter *padapter,
+ u8 r2t1, u8 r2t2, u8 t2t1, u8 t2t2)
+{
+ /* SIFS_Timer = 0x0a0a0808; */
+ /* RESP_SIFS for CCK */
+ /* SIFS_T2T_CCK (0x08) */
+ rtl8723au_write8(padapter, REG_R2T_SIFS, r2t1);
+ /* SIFS_R2T_CCK(0x08) */
+ rtl8723au_write8(padapter, REG_R2T_SIFS + 1, r2t2);
+ /* RESP_SIFS for OFDM */
+ /* SIFS_T2T_OFDM (0x0a) */
+ rtl8723au_write8(padapter, REG_T2T_SIFS, t2t1);
+ /* SIFS_R2T_OFDM(0x0a) */
+ rtl8723au_write8(padapter, REG_T2T_SIFS + 1, t2t2);
+}
+
+void rtl8723a_set_ac_param_vo(struct rtw_adapter *padapter, u32 vo)
+{
+ rtl8723au_write32(padapter, REG_EDCA_VO_PARAM, vo);
+}
+
+void rtl8723a_set_ac_param_vi(struct rtw_adapter *padapter, u32 vi)
+{
+ rtl8723au_write32(padapter, REG_EDCA_VI_PARAM, vi);
+}
+
+void rtl8723a_set_ac_param_be(struct rtw_adapter *padapter, u32 be)
+{
+ struct hal_data_8723a *pHalData = GET_HAL_DATA(padapter);
+
+ pHalData->AcParam_BE = be;
+ rtl8723au_write32(padapter, REG_EDCA_BE_PARAM, be);
+}
+
+void rtl8723a_set_ac_param_bk(struct rtw_adapter *padapter, u32 bk)
+{
+ rtl8723au_write32(padapter, REG_EDCA_BK_PARAM, bk);
+}
+
+void rtl8723a_set_rxdma_agg_pg_th(struct rtw_adapter *padapter, u8 val)
+{
+ rtl8723au_write8(padapter, REG_RXDMA_AGG_PG_TH, val);
+}
+
+void rtl8723a_set_initial_gain(struct rtw_adapter *padapter, u32 rx_gain)
+{
+ struct hal_data_8723a *pHalData = GET_HAL_DATA(padapter);
+ struct dig_t *pDigTable = &pHalData->odmpriv.DM_DigTable;
+
+ if (rx_gain == 0xff) /* restore rx gain */
+ ODM_Write_DIG23a(&pHalData->odmpriv, pDigTable->BackupIGValue);
+ else {
+ pDigTable->BackupIGValue = pDigTable->CurIGValue;
+ ODM_Write_DIG23a(&pHalData->odmpriv, rx_gain);
+ }
+}
+
+void rtl8723a_odm_support_ability_restore(struct rtw_adapter *padapter)
+{
+ struct hal_data_8723a *pHalData = GET_HAL_DATA(padapter);
+
+ pHalData->odmpriv.SupportAbility = pHalData->odmpriv.BK_SupportAbility;
+}
+
+void rtl8723a_odm_support_ability_backup(struct rtw_adapter *padapter)
+{
+ struct hal_data_8723a *pHalData = GET_HAL_DATA(padapter);
+
+ pHalData->odmpriv.BK_SupportAbility = pHalData->odmpriv.SupportAbility;
+}
+
+void rtl8723a_odm_support_ability_set(struct rtw_adapter *padapter, u32 val)
+{
+ struct hal_data_8723a *pHalData = GET_HAL_DATA(padapter);
+
+ if (val == DYNAMIC_ALL_FUNC_ENABLE)
+ pHalData->odmpriv.SupportAbility = pHalData->dmpriv.InitODMFlag;
+ else
+ pHalData->odmpriv.SupportAbility |= val;
+}
+
+void rtl8723a_odm_support_ability_clr(struct rtw_adapter *padapter, u32 val)
+{
+ struct hal_data_8723a *pHalData = GET_HAL_DATA(padapter);
+
+ pHalData->odmpriv.SupportAbility &= val;
+}
+
+void rtl8723a_set_rpwm(struct rtw_adapter *padapter, u8 val)
+{
+ rtl8723au_write8(padapter, REG_USB_HRPWM, val);
+}
+
+u8 rtl8723a_get_rf_type(struct rtw_adapter *padapter)
+{
+ struct hal_data_8723a *pHalData = GET_HAL_DATA(padapter);
+
+ return pHalData->rf_type;
+}
+
+bool rtl8723a_get_fwlps_rf_on(struct rtw_adapter *padapter)
+{
+ bool retval;
+ u32 valRCR;
+
+ /* When we halt NIC, we should check if FW LPS is leave. */
+
+ if ((padapter->bSurpriseRemoved == true) ||
+ (padapter->pwrctrlpriv.rf_pwrstate == rf_off)) {
+ /* If it is in HW/SW Radio OFF or IPS state, we do
+ not check Fw LPS Leave, because Fw is unload. */
+ retval = true;
+ } else {
+ valRCR = rtl8723au_read32(padapter, REG_RCR);
+ if (valRCR & 0x00070000)
+ retval = false;
+ else
+ retval = true;
+ }
+
+ return retval;
+}
+
+bool rtl8723a_chk_hi_queue_empty(struct rtw_adapter *padapter)
+{
+ u32 hgq;
+
+ hgq = rtl8723au_read32(padapter, REG_HGQ_INFORMATION);
+
+ return ((hgq & 0x0000ff00) == 0) ? true : false;
+}
diff --git a/drivers/staging/rtl8723au/hal/hal_intf.c b/drivers/staging/rtl8723au/hal/hal_intf.c
new file mode 100644
index 000000000..5383e6925
--- /dev/null
+++ b/drivers/staging/rtl8723au/hal/hal_intf.c
@@ -0,0 +1,42 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2012 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ ******************************************************************************/
+
+#define _HAL_INTF_C_
+#include <osdep_service.h>
+#include <drv_types.h>
+
+#include <hal_intf.h>
+
+#include <rtl8723a_hal.h>
+
+void rtw_hal_update_ra_mask23a(struct sta_info *psta, u8 rssi_level)
+{
+ struct rtw_adapter *padapter;
+ struct mlme_priv *pmlmepriv;
+
+ if (!psta)
+ return;
+
+ padapter = psta->padapter;
+
+ pmlmepriv = &padapter->mlmepriv;
+
+ if (check_fwstate(pmlmepriv, WIFI_AP_STATE)) {
+#ifdef CONFIG_8723AU_AP_MODE
+ add_RATid23a(padapter, psta, rssi_level);
+#endif
+ } else
+ rtl8723a_update_ramask(padapter, psta->mac_id, rssi_level);
+}
diff --git a/drivers/staging/rtl8723au/hal/odm.c b/drivers/staging/rtl8723au/hal/odm.c
new file mode 100644
index 000000000..ec543cfe1
--- /dev/null
+++ b/drivers/staging/rtl8723au/hal/odm.c
@@ -0,0 +1,1738 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2011 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * 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.
+ *
+ ******************************************************************************/
+
+#include "odm_precomp.h"
+#include "usb_ops_linux.h"
+
+static const u16 dB_Invert_Table[8][12] = {
+ {1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 4, 4},
+ {4, 5, 6, 6, 7, 8, 9, 10, 11, 13, 14, 16},
+ {18, 20, 22, 25, 28, 32, 35, 40, 45, 50, 56, 63},
+ {71, 79, 89, 100, 112, 126, 141, 158, 178, 200, 224, 251},
+ {282, 316, 355, 398, 447, 501, 562, 631, 708, 794, 891, 1000},
+ {1122, 1259, 1413, 1585, 1778, 1995, 2239, 2512, 2818, 3162, 3548, 3981},
+ {4467, 5012, 5623, 6310, 7079, 7943, 8913, 10000, 11220, 12589, 14125, 15849},
+ {17783, 19953, 22387, 25119, 28184, 31623, 35481, 39811, 44668, 50119, 56234, 65535}
+};
+
+static u32 EDCAParam[HT_IOT_PEER_MAX][3] = { /* UL DL */
+ {0x5ea42b, 0x5ea42b, 0x5ea42b}, /* 0:unknown AP */
+ {0xa44f, 0x5ea44f, 0x5e431c}, /* 1:realtek AP */
+ {0x5ea42b, 0x5ea42b, 0x5ea42b}, /* 2:unknown AP => realtek_92SE */
+ {0x5ea32b, 0x5ea42b, 0x5e4322}, /* 3:broadcom AP */
+ {0x5ea422, 0x00a44f, 0x00a44f}, /* 4:ralink AP */
+ {0x5ea322, 0x00a630, 0x00a44f}, /* 5:atheros AP */
+ {0x5e4322, 0x5e4322, 0x5e4322},/* 6:cisco AP */
+ {0x5ea44f, 0x00a44f, 0x5ea42b}, /* 8:marvell AP */
+ {0x5ea42b, 0x5ea42b, 0x5ea42b}, /* 10:unknown AP => 92U AP */
+ {0x5ea42b, 0xa630, 0x5e431c}, /* 11:airgocap AP */
+};
+
+/* EDCA Paramter for AP/ADSL by Mingzhi 2011-11-22 */
+
+/* Global var */
+u32 OFDMSwingTable23A[OFDM_TABLE_SIZE_92D] = {
+ 0x7f8001fe, /* 0, +6.0dB */
+ 0x788001e2, /* 1, +5.5dB */
+ 0x71c001c7, /* 2, +5.0dB */
+ 0x6b8001ae, /* 3, +4.5dB */
+ 0x65400195, /* 4, +4.0dB */
+ 0x5fc0017f, /* 5, +3.5dB */
+ 0x5a400169, /* 6, +3.0dB */
+ 0x55400155, /* 7, +2.5dB */
+ 0x50800142, /* 8, +2.0dB */
+ 0x4c000130, /* 9, +1.5dB */
+ 0x47c0011f, /* 10, +1.0dB */
+ 0x43c0010f, /* 11, +0.5dB */
+ 0x40000100, /* 12, +0dB */
+ 0x3c8000f2, /* 13, -0.5dB */
+ 0x390000e4, /* 14, -1.0dB */
+ 0x35c000d7, /* 15, -1.5dB */
+ 0x32c000cb, /* 16, -2.0dB */
+ 0x300000c0, /* 17, -2.5dB */
+ 0x2d4000b5, /* 18, -3.0dB */
+ 0x2ac000ab, /* 19, -3.5dB */
+ 0x288000a2, /* 20, -4.0dB */
+ 0x26000098, /* 21, -4.5dB */
+ 0x24000090, /* 22, -5.0dB */
+ 0x22000088, /* 23, -5.5dB */
+ 0x20000080, /* 24, -6.0dB */
+ 0x1e400079, /* 25, -6.5dB */
+ 0x1c800072, /* 26, -7.0dB */
+ 0x1b00006c, /* 27. -7.5dB */
+ 0x19800066, /* 28, -8.0dB */
+ 0x18000060, /* 29, -8.5dB */
+ 0x16c0005b, /* 30, -9.0dB */
+ 0x15800056, /* 31, -9.5dB */
+ 0x14400051, /* 32, -10.0dB */
+ 0x1300004c, /* 33, -10.5dB */
+ 0x12000048, /* 34, -11.0dB */
+ 0x11000044, /* 35, -11.5dB */
+ 0x10000040, /* 36, -12.0dB */
+ 0x0f00003c,/* 37, -12.5dB */
+ 0x0e400039,/* 38, -13.0dB */
+ 0x0d800036,/* 39, -13.5dB */
+ 0x0cc00033,/* 40, -14.0dB */
+ 0x0c000030,/* 41, -14.5dB */
+ 0x0b40002d,/* 42, -15.0dB */
+};
+
+u8 CCKSwingTable_Ch1_Ch1323A[CCK_TABLE_SIZE][8] = {
+ {0x36, 0x35, 0x2e, 0x25, 0x1c, 0x12, 0x09, 0x04}, /* 0, +0dB */
+ {0x33, 0x32, 0x2b, 0x23, 0x1a, 0x11, 0x08, 0x04}, /* 1, -0.5dB */
+ {0x30, 0x2f, 0x29, 0x21, 0x19, 0x10, 0x08, 0x03}, /* 2, -1.0dB */
+ {0x2d, 0x2d, 0x27, 0x1f, 0x18, 0x0f, 0x08, 0x03}, /* 3, -1.5dB */
+ {0x2b, 0x2a, 0x25, 0x1e, 0x16, 0x0e, 0x07, 0x03}, /* 4, -2.0dB */
+ {0x28, 0x28, 0x22, 0x1c, 0x15, 0x0d, 0x07, 0x03}, /* 5, -2.5dB */
+ {0x26, 0x25, 0x21, 0x1b, 0x14, 0x0d, 0x06, 0x03}, /* 6, -3.0dB */
+ {0x24, 0x23, 0x1f, 0x19, 0x13, 0x0c, 0x06, 0x03}, /* 7, -3.5dB */
+ {0x22, 0x21, 0x1d, 0x18, 0x11, 0x0b, 0x06, 0x02}, /* 8, -4.0dB */
+ {0x20, 0x20, 0x1b, 0x16, 0x11, 0x08, 0x05, 0x02}, /* 9, -4.5dB */
+ {0x1f, 0x1e, 0x1a, 0x15, 0x10, 0x0a, 0x05, 0x02}, /* 10, -5.0dB */
+ {0x1d, 0x1c, 0x18, 0x14, 0x0f, 0x0a, 0x05, 0x02}, /* 11, -5.5dB */
+ {0x1b, 0x1a, 0x17, 0x13, 0x0e, 0x09, 0x04, 0x02}, /* 12, -6.0dB */
+ {0x1a, 0x19, 0x16, 0x12, 0x0d, 0x09, 0x04, 0x02}, /* 13, -6.5dB */
+ {0x18, 0x17, 0x15, 0x11, 0x0c, 0x08, 0x04, 0x02}, /* 14, -7.0dB */
+ {0x17, 0x16, 0x13, 0x10, 0x0c, 0x08, 0x04, 0x02}, /* 15, -7.5dB */
+ {0x16, 0x15, 0x12, 0x0f, 0x0b, 0x07, 0x04, 0x01}, /* 16, -8.0dB */
+ {0x14, 0x14, 0x11, 0x0e, 0x0b, 0x07, 0x03, 0x02}, /* 17, -8.5dB */
+ {0x13, 0x13, 0x10, 0x0d, 0x0a, 0x06, 0x03, 0x01}, /* 18, -9.0dB */
+ {0x12, 0x12, 0x0f, 0x0c, 0x09, 0x06, 0x03, 0x01}, /* 19, -9.5dB */
+ {0x11, 0x11, 0x0f, 0x0c, 0x09, 0x06, 0x03, 0x01}, /* 20, -10.0dB */
+ {0x10, 0x10, 0x0e, 0x0b, 0x08, 0x05, 0x03, 0x01}, /* 21, -10.5dB */
+ {0x0f, 0x0f, 0x0d, 0x0b, 0x08, 0x05, 0x03, 0x01}, /* 22, -11.0dB */
+ {0x0e, 0x0e, 0x0c, 0x0a, 0x08, 0x05, 0x02, 0x01}, /* 23, -11.5dB */
+ {0x0d, 0x0d, 0x0c, 0x0a, 0x07, 0x05, 0x02, 0x01}, /* 24, -12.0dB */
+ {0x0d, 0x0c, 0x0b, 0x09, 0x07, 0x04, 0x02, 0x01}, /* 25, -12.5dB */
+ {0x0c, 0x0c, 0x0a, 0x09, 0x06, 0x04, 0x02, 0x01}, /* 26, -13.0dB */
+ {0x0b, 0x0b, 0x0a, 0x08, 0x06, 0x04, 0x02, 0x01}, /* 27, -13.5dB */
+ {0x0b, 0x0a, 0x09, 0x08, 0x06, 0x04, 0x02, 0x01}, /* 28, -14.0dB */
+ {0x0a, 0x0a, 0x09, 0x07, 0x05, 0x03, 0x02, 0x01}, /* 29, -14.5dB */
+ {0x0a, 0x09, 0x08, 0x07, 0x05, 0x03, 0x02, 0x01}, /* 30, -15.0dB */
+ {0x09, 0x09, 0x08, 0x06, 0x05, 0x03, 0x01, 0x01}, /* 31, -15.5dB */
+ {0x09, 0x08, 0x07, 0x06, 0x04, 0x03, 0x01, 0x01} /* 32, -16.0dB */
+};
+
+u8 CCKSwingTable_Ch1423A[CCK_TABLE_SIZE][8] = {
+ {0x36, 0x35, 0x2e, 0x1b, 0x00, 0x00, 0x00, 0x00}, /* 0, +0dB */
+ {0x33, 0x32, 0x2b, 0x19, 0x00, 0x00, 0x00, 0x00}, /* 1, -0.5dB */
+ {0x30, 0x2f, 0x29, 0x18, 0x00, 0x00, 0x00, 0x00}, /* 2, -1.0dB */
+ {0x2d, 0x2d, 0x17, 0x17, 0x00, 0x00, 0x00, 0x00}, /* 3, -1.5dB */
+ {0x2b, 0x2a, 0x25, 0x15, 0x00, 0x00, 0x00, 0x00}, /* 4, -2.0dB */
+ {0x28, 0x28, 0x24, 0x14, 0x00, 0x00, 0x00, 0x00}, /* 5, -2.5dB */
+ {0x26, 0x25, 0x21, 0x13, 0x00, 0x00, 0x00, 0x00}, /* 6, -3.0dB */
+ {0x24, 0x23, 0x1f, 0x12, 0x00, 0x00, 0x00, 0x00}, /* 7, -3.5dB */
+ {0x22, 0x21, 0x1d, 0x11, 0x00, 0x00, 0x00, 0x00}, /* 8, -4.0dB */
+ {0x20, 0x20, 0x1b, 0x10, 0x00, 0x00, 0x00, 0x00}, /* 9, -4.5dB */
+ {0x1f, 0x1e, 0x1a, 0x0f, 0x00, 0x00, 0x00, 0x00}, /* 10, -5.0dB */
+ {0x1d, 0x1c, 0x18, 0x0e, 0x00, 0x00, 0x00, 0x00}, /* 11, -5.5dB */
+ {0x1b, 0x1a, 0x17, 0x0e, 0x00, 0x00, 0x00, 0x00}, /* 12, -6.0dB */
+ {0x1a, 0x19, 0x16, 0x0d, 0x00, 0x00, 0x00, 0x00}, /* 13, -6.5dB */
+ {0x18, 0x17, 0x15, 0x0c, 0x00, 0x00, 0x00, 0x00}, /* 14, -7.0dB */
+ {0x17, 0x16, 0x13, 0x0b, 0x00, 0x00, 0x00, 0x00}, /* 15, -7.5dB */
+ {0x16, 0x15, 0x12, 0x0b, 0x00, 0x00, 0x00, 0x00}, /* 16, -8.0dB */
+ {0x14, 0x14, 0x11, 0x0a, 0x00, 0x00, 0x00, 0x00}, /* 17, -8.5dB */
+ {0x13, 0x13, 0x10, 0x0a, 0x00, 0x00, 0x00, 0x00}, /* 18, -9.0dB */
+ {0x12, 0x12, 0x0f, 0x09, 0x00, 0x00, 0x00, 0x00}, /* 19, -9.5dB */
+ {0x11, 0x11, 0x0f, 0x09, 0x00, 0x00, 0x00, 0x00}, /* 20, -10.0dB */
+ {0x10, 0x10, 0x0e, 0x08, 0x00, 0x00, 0x00, 0x00}, /* 21, -10.5dB */
+ {0x0f, 0x0f, 0x0d, 0x08, 0x00, 0x00, 0x00, 0x00}, /* 22, -11.0dB */
+ {0x0e, 0x0e, 0x0c, 0x07, 0x00, 0x00, 0x00, 0x00}, /* 23, -11.5dB */
+ {0x0d, 0x0d, 0x0c, 0x07, 0x00, 0x00, 0x00, 0x00}, /* 24, -12.0dB */
+ {0x0d, 0x0c, 0x0b, 0x06, 0x00, 0x00, 0x00, 0x00}, /* 25, -12.5dB */
+ {0x0c, 0x0c, 0x0a, 0x06, 0x00, 0x00, 0x00, 0x00}, /* 26, -13.0dB */
+ {0x0b, 0x0b, 0x0a, 0x06, 0x00, 0x00, 0x00, 0x00}, /* 27, -13.5dB */
+ {0x0b, 0x0a, 0x09, 0x05, 0x00, 0x00, 0x00, 0x00}, /* 28, -14.0dB */
+ {0x0a, 0x0a, 0x09, 0x05, 0x00, 0x00, 0x00, 0x00}, /* 29, -14.5dB */
+ {0x0a, 0x09, 0x08, 0x05, 0x00, 0x00, 0x00, 0x00}, /* 30, -15.0dB */
+ {0x09, 0x09, 0x08, 0x05, 0x00, 0x00, 0x00, 0x00}, /* 31, -15.5dB */
+ {0x09, 0x08, 0x07, 0x04, 0x00, 0x00, 0x00, 0x00} /* 32, -16.0dB */
+};
+
+/* Local Function predefine. */
+
+/* START------------COMMON INFO RELATED--------------- */
+void odm_CommonInfoSelfInit23a(struct dm_odm_t *pDM_Odm);
+
+static void odm_CommonInfoSelfUpdate(struct hal_data_8723a *pHalData);
+
+void odm_CmnInfoInit_Debug23a(struct dm_odm_t *pDM_Odm);
+
+void odm_CmnInfoUpdate_Debug23a(struct dm_odm_t *pDM_Odm);
+
+/* START---------------DIG--------------------------- */
+void odm_FalseAlarmCounterStatistics23a(struct dm_odm_t *pDM_Odm);
+
+void odm_DIG23aInit(struct dm_odm_t *pDM_Odm);
+
+void odm_DIG23a(struct rtw_adapter *adapter);
+
+void odm_CCKPacketDetectionThresh23a(struct dm_odm_t *pDM_Odm);
+/* END---------------DIG--------------------------- */
+
+/* START-------BB POWER SAVE----------------------- */
+void odm23a_DynBBPSInit(struct dm_odm_t *pDM_Odm);
+
+void odm_DynamicBBPowerSaving23a(struct dm_odm_t *pDM_Odm);
+
+/* END---------BB POWER SAVE----------------------- */
+
+void odm_DynamicTxPower23aInit(struct dm_odm_t *pDM_Odm);
+
+static void odm_RSSIMonitorCheck(struct dm_odm_t *pDM_Odm);
+void odm_DynamicTxPower23a(struct dm_odm_t *pDM_Odm);
+
+static void odm_RefreshRateAdaptiveMask(struct dm_odm_t *pDM_Odm);
+
+void odm_RateAdaptiveMaskInit23a(struct dm_odm_t *pDM_Odm);
+
+static void odm_TXPowerTrackingInit(struct dm_odm_t *pDM_Odm);
+
+static void odm_EdcaTurboCheck23a(struct dm_odm_t *pDM_Odm);
+static void ODM_EdcaTurboInit23a(struct dm_odm_t *pDM_Odm);
+
+#define RxDefaultAnt1 0x65a9
+#define RxDefaultAnt2 0x569a
+
+bool odm_StaDefAntSel(struct dm_odm_t *pDM_Odm,
+ u32 OFDM_Ant1_Cnt,
+ u32 OFDM_Ant2_Cnt,
+ u32 CCK_Ant1_Cnt,
+ u32 CCK_Ant2_Cnt,
+ u8 *pDefAnt
+ );
+
+void odm_SetRxIdleAnt(struct dm_odm_t *pDM_Odm,
+ u8 Ant,
+ bool bDualPath
+);
+
+/* 3 Export Interface */
+
+/* 2011/09/21 MH Add to describe different team necessary resource allocate?? */
+void ODM23a_DMInit(struct dm_odm_t *pDM_Odm)
+{
+ /* For all IC series */
+ odm_CommonInfoSelfInit23a(pDM_Odm);
+ odm_CmnInfoInit_Debug23a(pDM_Odm);
+ odm_DIG23aInit(pDM_Odm);
+ odm_RateAdaptiveMaskInit23a(pDM_Odm);
+
+ odm23a_DynBBPSInit(pDM_Odm);
+ odm_DynamicTxPower23aInit(pDM_Odm);
+ odm_TXPowerTrackingInit(pDM_Odm);
+ ODM_EdcaTurboInit23a(pDM_Odm);
+}
+
+/* 2011/09/20 MH This is the entry pointer for all team to execute HW out source DM. */
+/* You can not add any dummy function here, be care, you can only use DM structure */
+/* to perform any new ODM_DM. */
+void ODM_DMWatchdog23a(struct rtw_adapter *adapter)
+{
+ struct hal_data_8723a *pHalData = GET_HAL_DATA(adapter);
+ struct dm_odm_t *pDM_Odm = &pHalData->odmpriv;
+ struct pwrctrl_priv *pwrctrlpriv = &adapter->pwrctrlpriv;
+
+ /* 2012.05.03 Luke: For all IC series */
+ odm_CmnInfoUpdate_Debug23a(pDM_Odm);
+ odm_CommonInfoSelfUpdate(pHalData);
+ odm_FalseAlarmCounterStatistics23a(pDM_Odm);
+ odm_RSSIMonitorCheck(pDM_Odm);
+
+ /* 8723A or 8189ES platform */
+ /* NeilChen--2012--08--24-- */
+ /* Fix Leave LPS issue */
+ if ((pDM_Odm->Adapter->pwrctrlpriv.pwr_mode != PS_MODE_ACTIVE) &&/* in LPS mode */
+ (pDM_Odm->SupportICType & ODM_RTL8723A)) {
+ ODM_RT_TRACE(pDM_Odm, ODM_COMP_DIG, ODM_DBG_LOUD, ("----Step1: odm_DIG23a is in LPS mode\n"));
+ ODM_RT_TRACE(pDM_Odm, ODM_COMP_DIG, ODM_DBG_LOUD, ("---Step2: 8723AS is in LPS mode\n"));
+ odm_DIG23abyRSSI_LPS(pDM_Odm);
+ } else {
+ odm_DIG23a(adapter);
+ }
+
+ odm_CCKPacketDetectionThresh23a(pDM_Odm);
+
+ if (pwrctrlpriv->bpower_saving)
+ return;
+
+ odm_RefreshRateAdaptiveMask(pDM_Odm);
+
+ odm_DynamicBBPowerSaving23a(pDM_Odm);
+
+ odm_EdcaTurboCheck23a(pDM_Odm);
+}
+
+/* */
+/* Init /.. Fixed HW value. Only init time. */
+/* */
+void ODM_CmnInfoInit23a(struct dm_odm_t *pDM_Odm,
+ enum odm_cmninfo CmnInfo,
+ u32 Value
+ )
+{
+ /* ODM_RT_TRACE(pDM_Odm,); */
+
+ /* */
+ /* This section is used for init value */
+ /* */
+ switch (CmnInfo) {
+ /* Fixed ODM value. */
+ case ODM_CMNINFO_MP_TEST_CHIP:
+ pDM_Odm->bIsMPChip = (u8)Value;
+ break;
+ case ODM_CMNINFO_IC_TYPE:
+ pDM_Odm->SupportICType = Value;
+ break;
+ case ODM_CMNINFO_CUT_VER:
+ pDM_Odm->CutVersion = (u8)Value;
+ break;
+ case ODM_CMNINFO_FAB_VER:
+ pDM_Odm->FabVersion = (u8)Value;
+ break;
+ case ODM_CMNINFO_BOARD_TYPE:
+ pDM_Odm->BoardType = (u8)Value;
+ break;
+ case ODM_CMNINFO_EXT_LNA:
+ pDM_Odm->ExtLNA = (u8)Value;
+ break;
+ case ODM_CMNINFO_EXT_PA:
+ pDM_Odm->ExtPA = (u8)Value;
+ break;
+ case ODM_CMNINFO_EXT_TRSW:
+ pDM_Odm->ExtTRSW = (u8)Value;
+ break;
+ case ODM_CMNINFO_BINHCT_TEST:
+ pDM_Odm->bInHctTest = (bool)Value;
+ break;
+ case ODM_CMNINFO_BWIFI_TEST:
+ pDM_Odm->bWIFITest = (bool)Value;
+ break;
+ case ODM_CMNINFO_SMART_CONCURRENT:
+ pDM_Odm->bDualMacSmartConcurrent = (bool)Value;
+ break;
+ /* To remove the compiler warning, must add an empty default statement to handle the other values. */
+ default:
+ /* do nothing */
+ break;
+ }
+}
+
+void ODM_CmnInfoPtrArrayHook23a(struct dm_odm_t *pDM_Odm, enum odm_cmninfo CmnInfo,
+ u16 Index, void *pValue)
+{
+ /* Hook call by reference pointer. */
+ switch (CmnInfo) {
+ /* Dynamic call by reference pointer. */
+ case ODM_CMNINFO_STA_STATUS:
+ pDM_Odm->pODM_StaInfo[Index] = (struct sta_info *)pValue;
+ break;
+ /* To remove the compiler warning, must add an empty default statement to handle the other values. */
+ default:
+ /* do nothing */
+ break;
+ }
+}
+
+/* Update Band/CHannel/.. The values are dynamic but non-per-packet. */
+void ODM_CmnInfoUpdate23a(struct dm_odm_t *pDM_Odm, u32 CmnInfo, u64 Value)
+{
+ /* This init variable may be changed in run time. */
+ switch (CmnInfo) {
+ case ODM_CMNINFO_WIFI_DIRECT:
+ pDM_Odm->bWIFI_Direct = (bool)Value;
+ break;
+ case ODM_CMNINFO_WIFI_DISPLAY:
+ pDM_Odm->bWIFI_Display = (bool)Value;
+ break;
+ case ODM_CMNINFO_LINK:
+ pDM_Odm->bLinked = (bool)Value;
+ break;
+ case ODM_CMNINFO_RSSI_MIN:
+ pDM_Odm->RSSI_Min = (u8)Value;
+ break;
+ case ODM_CMNINFO_DBG_COMP:
+ pDM_Odm->DebugComponents = Value;
+ break;
+ case ODM_CMNINFO_DBG_LEVEL:
+ pDM_Odm->DebugLevel = (u32)Value;
+ break;
+ case ODM_CMNINFO_RA_THRESHOLD_HIGH:
+ pDM_Odm->RateAdaptive.HighRSSIThresh = (u8)Value;
+ break;
+ case ODM_CMNINFO_RA_THRESHOLD_LOW:
+ pDM_Odm->RateAdaptive.LowRSSIThresh = (u8)Value;
+ break;
+ }
+
+}
+
+void odm_CommonInfoSelfInit23a(struct dm_odm_t *pDM_Odm)
+{
+ u32 val32;
+
+ val32 = rtl8723au_read32(pDM_Odm->Adapter, rFPGA0_XA_HSSIParameter2);
+ if (val32 & BIT(9))
+ pDM_Odm->bCckHighPower = true;
+ else
+ pDM_Odm->bCckHighPower = false;
+
+ pDM_Odm->RFPathRxEnable =
+ rtl8723au_read32(pDM_Odm->Adapter, rOFDM0_TRxPathEnable) & 0x0F;
+
+ ODM_InitDebugSetting23a(pDM_Odm);
+}
+
+static void odm_CommonInfoSelfUpdate(struct hal_data_8723a *pHalData)
+{
+ struct dm_odm_t *pDM_Odm = &pHalData->odmpriv;
+ struct sta_info *pEntry;
+ u8 EntryCnt = 0;
+ u8 i;
+
+ for (i = 0; i < ODM_ASSOCIATE_ENTRY_NUM; i++) {
+ pEntry = pDM_Odm->pODM_StaInfo[i];
+ if (pEntry)
+ EntryCnt++;
+ }
+ if (EntryCnt == 1)
+ pDM_Odm->bOneEntryOnly = true;
+ else
+ pDM_Odm->bOneEntryOnly = false;
+}
+
+void odm_CmnInfoInit_Debug23a(struct dm_odm_t *pDM_Odm)
+{
+ ODM_RT_TRACE(pDM_Odm, ODM_COMP_COMMON, ODM_DBG_LOUD, ("odm_CmnInfoInit_Debug23a ==>\n"));
+ ODM_RT_TRACE(pDM_Odm, ODM_COMP_COMMON, ODM_DBG_LOUD, ("SupportAbility = 0x%x\n", pDM_Odm->SupportAbility));
+ ODM_RT_TRACE(pDM_Odm, ODM_COMP_COMMON, ODM_DBG_LOUD, ("SupportICType = 0x%x\n", pDM_Odm->SupportICType));
+ ODM_RT_TRACE(pDM_Odm, ODM_COMP_COMMON, ODM_DBG_LOUD, ("CutVersion =%d\n", pDM_Odm->CutVersion));
+ ODM_RT_TRACE(pDM_Odm, ODM_COMP_COMMON, ODM_DBG_LOUD, ("FabVersion =%d\n", pDM_Odm->FabVersion));
+ ODM_RT_TRACE(pDM_Odm, ODM_COMP_COMMON, ODM_DBG_LOUD, ("BoardType =%d\n", pDM_Odm->BoardType));
+ ODM_RT_TRACE(pDM_Odm, ODM_COMP_COMMON, ODM_DBG_LOUD, ("ExtLNA =%d\n", pDM_Odm->ExtLNA));
+ ODM_RT_TRACE(pDM_Odm, ODM_COMP_COMMON, ODM_DBG_LOUD, ("ExtPA =%d\n", pDM_Odm->ExtPA));
+ ODM_RT_TRACE(pDM_Odm, ODM_COMP_COMMON, ODM_DBG_LOUD, ("ExtTRSW =%d\n", pDM_Odm->ExtTRSW));
+ ODM_RT_TRACE(pDM_Odm, ODM_COMP_COMMON, ODM_DBG_LOUD, ("bInHctTest =%d\n", pDM_Odm->bInHctTest));
+ ODM_RT_TRACE(pDM_Odm, ODM_COMP_COMMON, ODM_DBG_LOUD, ("bWIFITest =%d\n", pDM_Odm->bWIFITest));
+ ODM_RT_TRACE(pDM_Odm, ODM_COMP_COMMON, ODM_DBG_LOUD, ("bDualMacSmartConcurrent =%d\n", pDM_Odm->bDualMacSmartConcurrent));
+
+}
+
+void odm_CmnInfoUpdate_Debug23a(struct dm_odm_t *pDM_Odm)
+{
+ ODM_RT_TRACE(pDM_Odm, ODM_COMP_COMMON, ODM_DBG_LOUD, ("odm_CmnInfoUpdate_Debug23a ==>\n"));
+ ODM_RT_TRACE(pDM_Odm, ODM_COMP_COMMON, ODM_DBG_LOUD, ("bWIFI_Direct =%d\n", pDM_Odm->bWIFI_Direct));
+ ODM_RT_TRACE(pDM_Odm, ODM_COMP_COMMON, ODM_DBG_LOUD, ("bWIFI_Display =%d\n", pDM_Odm->bWIFI_Display));
+ ODM_RT_TRACE(pDM_Odm, ODM_COMP_COMMON, ODM_DBG_LOUD, ("bLinked =%d\n", pDM_Odm->bLinked));
+ ODM_RT_TRACE(pDM_Odm, ODM_COMP_COMMON, ODM_DBG_LOUD, ("RSSI_Min =%d\n", pDM_Odm->RSSI_Min));
+}
+
+void ODM_Write_DIG23a(struct dm_odm_t *pDM_Odm, u8 CurrentIGI)
+{
+ struct rtw_adapter *adapter = pDM_Odm->Adapter;
+ struct dig_t *pDM_DigTable = &pDM_Odm->DM_DigTable;
+ u32 val32;
+
+ if (pDM_DigTable->CurIGValue != CurrentIGI) {
+ val32 = rtl8723au_read32(adapter, ODM_REG_IGI_A_11N);
+ val32 &= ~ODM_BIT_IGI_11N;
+ val32 |= CurrentIGI;
+ rtl8723au_write32(adapter, ODM_REG_IGI_A_11N, val32);
+ ODM_RT_TRACE(pDM_Odm, ODM_COMP_DIG, ODM_DBG_LOUD,
+ ("CurrentIGI(0x%02x). \n", CurrentIGI));
+ pDM_DigTable->CurIGValue = CurrentIGI;
+ }
+ ODM_RT_TRACE(pDM_Odm, ODM_COMP_DIG, ODM_DBG_LOUD,
+ ("ODM_Write_DIG23a():CurrentIGI = 0x%x \n", CurrentIGI));
+}
+
+/* Need LPS mode for CE platform --2012--08--24--- */
+/* 8723AS/8189ES */
+void odm_DIG23abyRSSI_LPS(struct dm_odm_t *pDM_Odm)
+{
+ struct rtw_adapter *pAdapter = pDM_Odm->Adapter;
+ struct false_alarm_stats *pFalseAlmCnt = &pDM_Odm->FalseAlmCnt;
+ u8 RSSI_Lower = DM_DIG_MIN_NIC; /* 0x1E or 0x1C */
+ u8 bFwCurrentInPSMode = false;
+ u8 CurrentIGI = pDM_Odm->RSSI_Min;
+
+ if (!(pDM_Odm->SupportICType & ODM_RTL8723A))
+ return;
+
+ CurrentIGI = CurrentIGI+RSSI_OFFSET_DIG;
+ bFwCurrentInPSMode = pAdapter->pwrctrlpriv.bFwCurrentInPSMode;
+
+ /* Using FW PS mode to make IGI */
+ if (bFwCurrentInPSMode) {
+ ODM_RT_TRACE(pDM_Odm, ODM_COMP_DIG, ODM_DBG_LOUD,
+ ("---Neil---odm_DIG23a is in LPS mode\n"));
+ /* Adjust by FA in LPS MODE */
+ if (pFalseAlmCnt->Cnt_all > DM_DIG_FA_TH2_LPS)
+ CurrentIGI = CurrentIGI+2;
+ else if (pFalseAlmCnt->Cnt_all > DM_DIG_FA_TH1_LPS)
+ CurrentIGI = CurrentIGI+1;
+ else if (pFalseAlmCnt->Cnt_all < DM_DIG_FA_TH0_LPS)
+ CurrentIGI = CurrentIGI-1;
+ } else {
+ CurrentIGI = RSSI_Lower;
+ }
+
+ /* Lower bound checking */
+
+ /* RSSI Lower bound check */
+ if ((pDM_Odm->RSSI_Min-10) > DM_DIG_MIN_NIC)
+ RSSI_Lower = (pDM_Odm->RSSI_Min-10);
+ else
+ RSSI_Lower = DM_DIG_MIN_NIC;
+
+ /* Upper and Lower Bound checking */
+ if (CurrentIGI > DM_DIG_MAX_NIC)
+ CurrentIGI = DM_DIG_MAX_NIC;
+ else if (CurrentIGI < RSSI_Lower)
+ CurrentIGI = RSSI_Lower;
+
+ ODM_Write_DIG23a(pDM_Odm, CurrentIGI);
+}
+
+void odm_DIG23aInit(struct dm_odm_t *pDM_Odm)
+{
+ struct dig_t *pDM_DigTable = &pDM_Odm->DM_DigTable;
+ u32 val32;
+
+ val32 = rtl8723au_read32(pDM_Odm->Adapter, ODM_REG_IGI_A_11N);
+ pDM_DigTable->CurIGValue = val32 & ODM_BIT_IGI_11N;
+
+ pDM_DigTable->RssiLowThresh = DM_DIG_THRESH_LOW;
+ pDM_DigTable->RssiHighThresh = DM_DIG_THRESH_HIGH;
+ pDM_DigTable->FALowThresh = DM_FALSEALARM_THRESH_LOW;
+ pDM_DigTable->FAHighThresh = DM_FALSEALARM_THRESH_HIGH;
+ if (pDM_Odm->BoardType == ODM_BOARD_HIGHPWR) {
+ pDM_DigTable->rx_gain_range_max = DM_DIG_MAX_NIC;
+ pDM_DigTable->rx_gain_range_min = DM_DIG_MIN_NIC;
+ } else {
+ pDM_DigTable->rx_gain_range_max = DM_DIG_MAX_NIC;
+ pDM_DigTable->rx_gain_range_min = DM_DIG_MIN_NIC;
+ }
+ pDM_DigTable->BackoffVal = DM_DIG_BACKOFF_DEFAULT;
+ pDM_DigTable->BackoffVal_range_max = DM_DIG_BACKOFF_MAX;
+ pDM_DigTable->BackoffVal_range_min = DM_DIG_BACKOFF_MIN;
+ pDM_DigTable->PreCCK_CCAThres = 0xFF;
+ pDM_DigTable->CurCCK_CCAThres = 0x83;
+ pDM_DigTable->ForbiddenIGI = DM_DIG_MIN_NIC;
+ pDM_DigTable->LargeFAHit = 0;
+ pDM_DigTable->Recover_cnt = 0;
+ pDM_DigTable->DIG_Dynamic_MIN_0 = DM_DIG_MIN_NIC;
+ pDM_DigTable->DIG_Dynamic_MIN_1 = DM_DIG_MIN_NIC;
+ pDM_DigTable->bMediaConnect_0 = false;
+ pDM_DigTable->bMediaConnect_1 = false;
+}
+
+void odm_DIG23a(struct rtw_adapter *adapter)
+{
+ struct hal_data_8723a *pHalData = GET_HAL_DATA(adapter);
+ struct dm_odm_t *pDM_Odm = &pHalData->odmpriv;
+ struct dig_t *pDM_DigTable = &pDM_Odm->DM_DigTable;
+ struct false_alarm_stats *pFalseAlmCnt = &pDM_Odm->FalseAlmCnt;
+ u8 DIG_Dynamic_MIN;
+ u8 DIG_MaxOfMin;
+ bool FirstConnect, FirstDisConnect;
+ u8 dm_dig_max, dm_dig_min;
+ u8 CurrentIGI = pDM_DigTable->CurIGValue;
+
+ ODM_RT_TRACE(pDM_Odm, ODM_COMP_DIG, ODM_DBG_LOUD,
+ ("odm_DIG23a() ==>\n"));
+ if (adapter->mlmepriv.bScanInProcess) {
+ ODM_RT_TRACE(pDM_Odm, ODM_COMP_DIG, ODM_DBG_LOUD,
+ ("odm_DIG23a() Return: In Scan Progress \n"));
+ return;
+ }
+
+ DIG_Dynamic_MIN = pDM_DigTable->DIG_Dynamic_MIN_0;
+ FirstConnect = (pDM_Odm->bLinked) && (!pDM_DigTable->bMediaConnect_0);
+ FirstDisConnect = (!pDM_Odm->bLinked) &&
+ (pDM_DigTable->bMediaConnect_0);
+
+ /* 1 Boundary Decision */
+ if ((pDM_Odm->SupportICType & ODM_RTL8723A) &&
+ (pDM_Odm->BoardType == ODM_BOARD_HIGHPWR || pDM_Odm->ExtLNA)) {
+ dm_dig_max = DM_DIG_MAX_NIC_HP;
+ dm_dig_min = DM_DIG_MIN_NIC_HP;
+ DIG_MaxOfMin = DM_DIG_MAX_AP_HP;
+ } else {
+ dm_dig_max = DM_DIG_MAX_NIC;
+ dm_dig_min = DM_DIG_MIN_NIC;
+ DIG_MaxOfMin = DM_DIG_MAX_AP;
+ }
+
+ if (pDM_Odm->bLinked) {
+ /* 2 8723A Series, offset need to be 10 */
+ if (pDM_Odm->SupportICType == ODM_RTL8723A) {
+ /* 2 Upper Bound */
+ if ((pDM_Odm->RSSI_Min + 10) > DM_DIG_MAX_NIC)
+ pDM_DigTable->rx_gain_range_max = DM_DIG_MAX_NIC;
+ else if ((pDM_Odm->RSSI_Min + 10) < DM_DIG_MIN_NIC)
+ pDM_DigTable->rx_gain_range_max = DM_DIG_MIN_NIC;
+ else
+ pDM_DigTable->rx_gain_range_max = pDM_Odm->RSSI_Min + 10;
+
+ /* 2 If BT is Concurrent, need to set Lower Bound */
+ DIG_Dynamic_MIN = DM_DIG_MIN_NIC;
+ } else {
+ /* 2 Modify DIG upper bound */
+ if ((pDM_Odm->RSSI_Min + 20) > dm_dig_max)
+ pDM_DigTable->rx_gain_range_max = dm_dig_max;
+ else if ((pDM_Odm->RSSI_Min + 20) < dm_dig_min)
+ pDM_DigTable->rx_gain_range_max = dm_dig_min;
+ else
+ pDM_DigTable->rx_gain_range_max = pDM_Odm->RSSI_Min + 20;
+
+ /* 2 Modify DIG lower bound */
+ if (pDM_Odm->bOneEntryOnly) {
+ if (pDM_Odm->RSSI_Min < dm_dig_min)
+ DIG_Dynamic_MIN = dm_dig_min;
+ else if (pDM_Odm->RSSI_Min > DIG_MaxOfMin)
+ DIG_Dynamic_MIN = DIG_MaxOfMin;
+ else
+ DIG_Dynamic_MIN = pDM_Odm->RSSI_Min;
+ ODM_RT_TRACE(pDM_Odm, ODM_COMP_DIG, ODM_DBG_LOUD,
+ ("odm_DIG23a() : bOneEntryOnly = true, DIG_Dynamic_MIN = 0x%x\n",
+ DIG_Dynamic_MIN));
+ ODM_RT_TRACE(pDM_Odm, ODM_COMP_DIG, ODM_DBG_LOUD,
+ ("odm_DIG23a() : pDM_Odm->RSSI_Min =%d\n",
+ pDM_Odm->RSSI_Min));
+ } else {
+ DIG_Dynamic_MIN = dm_dig_min;
+ }
+ }
+ } else {
+ pDM_DigTable->rx_gain_range_max = dm_dig_max;
+ DIG_Dynamic_MIN = dm_dig_min;
+ ODM_RT_TRACE(pDM_Odm, ODM_COMP_DIG, ODM_DBG_LOUD, ("odm_DIG23a() : No Link\n"));
+ }
+
+ /* 1 Modify DIG lower bound, deal with abnormally large false alarm */
+ if (pFalseAlmCnt->Cnt_all > 10000) {
+ ODM_RT_TRACE(pDM_Odm, ODM_COMP_DIG, ODM_DBG_LOUD,
+ ("dm_DIG(): Abnornally false alarm case. \n"));
+
+ if (pDM_DigTable->LargeFAHit != 3)
+ pDM_DigTable->LargeFAHit++;
+ if (pDM_DigTable->ForbiddenIGI < CurrentIGI) {
+ pDM_DigTable->ForbiddenIGI = CurrentIGI;
+ pDM_DigTable->LargeFAHit = 1;
+ }
+
+ if (pDM_DigTable->LargeFAHit >= 3) {
+ if ((pDM_DigTable->ForbiddenIGI+1) > pDM_DigTable->rx_gain_range_max)
+ pDM_DigTable->rx_gain_range_min = pDM_DigTable->rx_gain_range_max;
+ else
+ pDM_DigTable->rx_gain_range_min = (pDM_DigTable->ForbiddenIGI + 1);
+ pDM_DigTable->Recover_cnt = 3600; /* 3600 = 2hr */
+ }
+ } else {
+ /* Recovery mechanism for IGI lower bound */
+ if (pDM_DigTable->Recover_cnt != 0) {
+ pDM_DigTable->Recover_cnt--;
+ } else {
+ if (pDM_DigTable->LargeFAHit < 3) {
+ if ((pDM_DigTable->ForbiddenIGI - 1) < DIG_Dynamic_MIN) {
+ pDM_DigTable->ForbiddenIGI = DIG_Dynamic_MIN; /* DM_DIG_MIN; */
+ pDM_DigTable->rx_gain_range_min = DIG_Dynamic_MIN; /* DM_DIG_MIN; */
+ ODM_RT_TRACE(pDM_Odm, ODM_COMP_DIG, ODM_DBG_LOUD,
+ ("odm_DIG23a(): Normal Case: At Lower Bound\n"));
+ } else {
+ pDM_DigTable->ForbiddenIGI--;
+ pDM_DigTable->rx_gain_range_min = (pDM_DigTable->ForbiddenIGI + 1);
+ ODM_RT_TRACE(pDM_Odm, ODM_COMP_DIG, ODM_DBG_LOUD,
+ ("odm_DIG23a(): Normal Case: Approach Lower Bound\n"));
+ }
+ } else {
+ pDM_DigTable->LargeFAHit = 0;
+ }
+ }
+ }
+ ODM_RT_TRACE(pDM_Odm, ODM_COMP_DIG, ODM_DBG_LOUD, ("odm_DIG23a(): pDM_DigTable->LargeFAHit =%d\n", pDM_DigTable->LargeFAHit));
+
+ /* 1 Adjust initial gain by false alarm */
+ if (pDM_Odm->bLinked) {
+ ODM_RT_TRACE(pDM_Odm, ODM_COMP_DIG, ODM_DBG_LOUD, ("odm_DIG23a(): DIG AfterLink\n"));
+ if (FirstConnect) {
+ CurrentIGI = pDM_Odm->RSSI_Min;
+ ODM_RT_TRACE(pDM_Odm, ODM_COMP_DIG, ODM_DBG_LOUD, ("DIG: First Connect\n"));
+ } else {
+ if (pFalseAlmCnt->Cnt_all > DM_DIG_FA_TH2)
+ CurrentIGI = CurrentIGI + 4;/* pDM_DigTable->CurIGValue = pDM_DigTable->PreIGValue+2; */
+ else if (pFalseAlmCnt->Cnt_all > DM_DIG_FA_TH1)
+ CurrentIGI = CurrentIGI + 2;/* pDM_DigTable->CurIGValue = pDM_DigTable->PreIGValue+1; */
+ else if (pFalseAlmCnt->Cnt_all < DM_DIG_FA_TH0)
+ CurrentIGI = CurrentIGI - 2;/* pDM_DigTable->CurIGValue = pDM_DigTable->PreIGValue-1; */
+ }
+ } else {
+ ODM_RT_TRACE(pDM_Odm, ODM_COMP_DIG, ODM_DBG_LOUD, ("odm_DIG23a(): DIG BeforeLink\n"));
+ if (FirstDisConnect) {
+ CurrentIGI = pDM_DigTable->rx_gain_range_min;
+ ODM_RT_TRACE(pDM_Odm, ODM_COMP_DIG, ODM_DBG_LOUD, ("odm_DIG23a(): First DisConnect \n"));
+ } else {
+ /* 2012.03.30 LukeLee: enable DIG before link but with very high thresholds */
+ if (pFalseAlmCnt->Cnt_all > 10000)
+ CurrentIGI = CurrentIGI + 2;/* pDM_DigTable->CurIGValue = pDM_DigTable->PreIGValue+2; */
+ else if (pFalseAlmCnt->Cnt_all > 8000)
+ CurrentIGI = CurrentIGI + 1;/* pDM_DigTable->CurIGValue = pDM_DigTable->PreIGValue+1; */
+ else if (pFalseAlmCnt->Cnt_all < 500)
+ CurrentIGI = CurrentIGI - 1;/* pDM_DigTable->CurIGValue = pDM_DigTable->PreIGValue-1; */
+ ODM_RT_TRACE(pDM_Odm, ODM_COMP_DIG, ODM_DBG_LOUD, ("odm_DIG23a(): England DIG \n"));
+ }
+ }
+ ODM_RT_TRACE(pDM_Odm, ODM_COMP_DIG, ODM_DBG_LOUD, ("odm_DIG23a(): DIG End Adjust IGI\n"));
+ /* 1 Check initial gain by upper/lower bound */
+ if (CurrentIGI > pDM_DigTable->rx_gain_range_max)
+ CurrentIGI = pDM_DigTable->rx_gain_range_max;
+ if (CurrentIGI < pDM_DigTable->rx_gain_range_min)
+ CurrentIGI = pDM_DigTable->rx_gain_range_min;
+
+ ODM_RT_TRACE(pDM_Odm, ODM_COMP_DIG, ODM_DBG_LOUD, ("odm_DIG23a(): rx_gain_range_max = 0x%x, rx_gain_range_min = 0x%x\n",
+ pDM_DigTable->rx_gain_range_max, pDM_DigTable->rx_gain_range_min));
+ ODM_RT_TRACE(pDM_Odm, ODM_COMP_DIG, ODM_DBG_LOUD, ("odm_DIG23a(): TotalFA =%d\n", pFalseAlmCnt->Cnt_all));
+ ODM_RT_TRACE(pDM_Odm, ODM_COMP_DIG, ODM_DBG_LOUD, ("odm_DIG23a(): CurIGValue = 0x%x\n", CurrentIGI));
+
+ /* 2 High power RSSI threshold */
+
+ ODM_Write_DIG23a(pDM_Odm, CurrentIGI);/* ODM_Write_DIG23a(pDM_Odm, pDM_DigTable->CurIGValue); */
+ pDM_DigTable->bMediaConnect_0 = pDM_Odm->bLinked;
+ pDM_DigTable->DIG_Dynamic_MIN_0 = DIG_Dynamic_MIN;
+}
+
+/* 3 ============================================================ */
+/* 3 FASLE ALARM CHECK */
+/* 3 ============================================================ */
+
+void odm_FalseAlarmCounterStatistics23a(struct dm_odm_t *pDM_Odm)
+{
+ struct rtw_adapter *adapter = pDM_Odm->Adapter;
+ struct false_alarm_stats *FalseAlmCnt = &pDM_Odm->FalseAlmCnt;
+ u32 ret_value, val32;
+
+ /* hold ofdm counter */
+ /* hold page C counter */
+ val32 = rtl8723au_read32(adapter, ODM_REG_OFDM_FA_HOLDC_11N);
+ val32 |= BIT(31);
+ rtl8723au_write32(adapter, ODM_REG_OFDM_FA_HOLDC_11N, val32);
+ /* hold page D counter */
+ val32 = rtl8723au_read32(adapter, ODM_REG_OFDM_FA_RSTD_11N);
+ val32 |= BIT(31);
+ rtl8723au_write32(adapter, ODM_REG_OFDM_FA_RSTD_11N, val32);
+ ret_value = rtl8723au_read32(adapter, ODM_REG_OFDM_FA_TYPE1_11N);
+ FalseAlmCnt->Cnt_Fast_Fsync = (ret_value&0xffff);
+ FalseAlmCnt->Cnt_SB_Search_fail = (ret_value & 0xffff0000)>>16;
+ ret_value = rtl8723au_read32(adapter, ODM_REG_OFDM_FA_TYPE2_11N);
+ FalseAlmCnt->Cnt_OFDM_CCA = (ret_value&0xffff);
+ FalseAlmCnt->Cnt_Parity_Fail = (ret_value & 0xffff0000)>>16;
+ ret_value = rtl8723au_read32(adapter, ODM_REG_OFDM_FA_TYPE3_11N);
+ FalseAlmCnt->Cnt_Rate_Illegal = (ret_value&0xffff);
+ FalseAlmCnt->Cnt_Crc8_fail = (ret_value & 0xffff0000)>>16;
+ ret_value = rtl8723au_read32(adapter, ODM_REG_OFDM_FA_TYPE4_11N);
+ FalseAlmCnt->Cnt_Mcs_fail = (ret_value&0xffff);
+
+ FalseAlmCnt->Cnt_Ofdm_fail = FalseAlmCnt->Cnt_Parity_Fail +
+ FalseAlmCnt->Cnt_Rate_Illegal +
+ FalseAlmCnt->Cnt_Crc8_fail +
+ FalseAlmCnt->Cnt_Mcs_fail +
+ FalseAlmCnt->Cnt_Fast_Fsync +
+ FalseAlmCnt->Cnt_SB_Search_fail;
+ /* hold cck counter */
+ val32 = rtl8723au_read32(adapter, ODM_REG_CCK_FA_RST_11N);
+ val32 |= (BIT(12) | BIT(14));
+ rtl8723au_write32(adapter, ODM_REG_CCK_FA_RST_11N, val32);
+
+ ret_value = rtl8723au_read32(adapter, ODM_REG_CCK_FA_LSB_11N) & 0xff;
+ FalseAlmCnt->Cnt_Cck_fail = ret_value;
+ ret_value = rtl8723au_read32(adapter, ODM_REG_CCK_FA_MSB_11N) >> 16;
+ FalseAlmCnt->Cnt_Cck_fail += (ret_value & 0xff00);
+
+ ret_value = rtl8723au_read32(adapter, ODM_REG_CCK_CCA_CNT_11N);
+ FalseAlmCnt->Cnt_CCK_CCA =
+ ((ret_value&0xFF)<<8) | ((ret_value&0xFF00)>>8);
+
+ FalseAlmCnt->Cnt_all = (FalseAlmCnt->Cnt_Fast_Fsync +
+ FalseAlmCnt->Cnt_SB_Search_fail +
+ FalseAlmCnt->Cnt_Parity_Fail +
+ FalseAlmCnt->Cnt_Rate_Illegal +
+ FalseAlmCnt->Cnt_Crc8_fail +
+ FalseAlmCnt->Cnt_Mcs_fail +
+ FalseAlmCnt->Cnt_Cck_fail);
+
+ FalseAlmCnt->Cnt_CCA_all =
+ FalseAlmCnt->Cnt_OFDM_CCA + FalseAlmCnt->Cnt_CCK_CCA;
+
+ if (pDM_Odm->SupportICType >= ODM_RTL8723A) {
+ /* reset false alarm counter registers */
+ val32 = rtl8723au_read32(adapter, ODM_REG_OFDM_FA_RSTC_11N);
+ val32 |= BIT(31);
+ rtl8723au_write32(adapter, ODM_REG_OFDM_FA_RSTC_11N, val32);
+ val32 = rtl8723au_read32(adapter, ODM_REG_OFDM_FA_RSTC_11N);
+ val32 &= ~BIT(31);
+ rtl8723au_write32(adapter, ODM_REG_OFDM_FA_RSTC_11N, val32);
+
+ val32 = rtl8723au_read32(adapter, ODM_REG_OFDM_FA_RSTD_11N);
+ val32 |= BIT(27);
+ rtl8723au_write32(adapter, ODM_REG_OFDM_FA_RSTD_11N, val32);
+ val32 = rtl8723au_read32(adapter, ODM_REG_OFDM_FA_RSTD_11N);
+ val32 &= ~BIT(27);
+ rtl8723au_write32(adapter, ODM_REG_OFDM_FA_RSTD_11N, val32);
+
+ /* update ofdm counter */
+ /* update page C counter */
+ val32 = rtl8723au_read32(adapter, ODM_REG_OFDM_FA_HOLDC_11N);
+ val32 &= ~BIT(31);
+ rtl8723au_write32(adapter, ODM_REG_OFDM_FA_HOLDC_11N, val32);
+
+ /* update page D counter */
+ val32 = rtl8723au_read32(adapter, ODM_REG_OFDM_FA_RSTD_11N);
+ val32 &= ~BIT(31);
+ rtl8723au_write32(adapter, ODM_REG_OFDM_FA_RSTD_11N, val32);
+
+ /* reset CCK CCA counter */
+ val32 = rtl8723au_read32(adapter, ODM_REG_CCK_FA_RST_11N);
+ val32 &= ~(BIT(12) | BIT(13) | BIT(14) | BIT(15));
+ rtl8723au_write32(adapter, ODM_REG_CCK_FA_RST_11N, val32);
+
+ val32 = rtl8723au_read32(adapter, ODM_REG_CCK_FA_RST_11N);
+ val32 |= (BIT(13) | BIT(15));
+ rtl8723au_write32(adapter, ODM_REG_CCK_FA_RST_11N, val32);
+ }
+
+ ODM_RT_TRACE(pDM_Odm, ODM_COMP_FA_CNT, ODM_DBG_LOUD,
+ ("Enter odm_FalseAlarmCounterStatistics23a\n"));
+ ODM_RT_TRACE(pDM_Odm, ODM_COMP_FA_CNT, ODM_DBG_LOUD,
+ ("Cnt_Fast_Fsync =%d, Cnt_SB_Search_fail =%d\n",
+ FalseAlmCnt->Cnt_Fast_Fsync,
+ FalseAlmCnt->Cnt_SB_Search_fail));
+ ODM_RT_TRACE(pDM_Odm, ODM_COMP_FA_CNT, ODM_DBG_LOUD,
+ ("Cnt_Parity_Fail =%d, Cnt_Rate_Illegal =%d\n",
+ FalseAlmCnt->Cnt_Parity_Fail,
+ FalseAlmCnt->Cnt_Rate_Illegal));
+ ODM_RT_TRACE(pDM_Odm, ODM_COMP_FA_CNT, ODM_DBG_LOUD,
+ ("Cnt_Crc8_fail =%d, Cnt_Mcs_fail =%d\n",
+ FalseAlmCnt->Cnt_Crc8_fail, FalseAlmCnt->Cnt_Mcs_fail));
+
+ ODM_RT_TRACE(pDM_Odm, ODM_COMP_FA_CNT, ODM_DBG_LOUD,
+ ("Cnt_Cck_fail =%d\n", FalseAlmCnt->Cnt_Cck_fail));
+ ODM_RT_TRACE(pDM_Odm, ODM_COMP_FA_CNT, ODM_DBG_LOUD,
+ ("Cnt_Ofdm_fail =%d\n", FalseAlmCnt->Cnt_Ofdm_fail));
+ ODM_RT_TRACE(pDM_Odm, ODM_COMP_FA_CNT, ODM_DBG_LOUD,
+ ("Total False Alarm =%d\n", FalseAlmCnt->Cnt_all));
+}
+
+/* 3 ============================================================ */
+/* 3 CCK Packet Detect Threshold */
+/* 3 ============================================================ */
+
+void odm_CCKPacketDetectionThresh23a(struct dm_odm_t *pDM_Odm)
+{
+ struct false_alarm_stats *FalseAlmCnt = &pDM_Odm->FalseAlmCnt;
+ u8 CurCCK_CCAThres;
+
+ if (pDM_Odm->ExtLNA)
+ return;
+
+ if (pDM_Odm->bLinked) {
+ if (pDM_Odm->RSSI_Min > 25) {
+ CurCCK_CCAThres = 0xcd;
+ } else if (pDM_Odm->RSSI_Min <= 25 && pDM_Odm->RSSI_Min > 10) {
+ CurCCK_CCAThres = 0x83;
+ } else {
+ if (FalseAlmCnt->Cnt_Cck_fail > 1000)
+ CurCCK_CCAThres = 0x83;
+ else
+ CurCCK_CCAThres = 0x40;
+ }
+ } else {
+ if (FalseAlmCnt->Cnt_Cck_fail > 1000)
+ CurCCK_CCAThres = 0x83;
+ else
+ CurCCK_CCAThres = 0x40;
+ }
+
+ ODM_Write_CCK_CCA_Thres23a(pDM_Odm, CurCCK_CCAThres);
+}
+
+void ODM_Write_CCK_CCA_Thres23a(struct dm_odm_t *pDM_Odm, u8 CurCCK_CCAThres)
+{
+ struct dig_t *pDM_DigTable = &pDM_Odm->DM_DigTable;
+
+ if (pDM_DigTable->CurCCK_CCAThres != CurCCK_CCAThres)
+ rtl8723au_write8(pDM_Odm->Adapter, ODM_REG(CCK_CCA, pDM_Odm),
+ CurCCK_CCAThres);
+ pDM_DigTable->PreCCK_CCAThres = pDM_DigTable->CurCCK_CCAThres;
+ pDM_DigTable->CurCCK_CCAThres = CurCCK_CCAThres;
+}
+
+/* 3 ============================================================ */
+/* 3 BB Power Save */
+/* 3 ============================================================ */
+void odm23a_DynBBPSInit(struct dm_odm_t *pDM_Odm)
+{
+ struct dynamic_pwr_sav *pDM_PSTable = &pDM_Odm->DM_PSTable;
+
+ pDM_PSTable->PreCCAState = CCA_MAX;
+ pDM_PSTable->CurCCAState = CCA_MAX;
+ pDM_PSTable->PreRFState = RF_MAX;
+ pDM_PSTable->CurRFState = RF_MAX;
+ pDM_PSTable->Rssi_val_min = 0;
+ pDM_PSTable->initialize = 0;
+}
+
+void odm_DynamicBBPowerSaving23a(struct dm_odm_t *pDM_Odm)
+{
+ return;
+}
+
+void ODM_RF_Saving23a(struct dm_odm_t *pDM_Odm, u8 bForceInNormal)
+{
+ struct dynamic_pwr_sav *pDM_PSTable = &pDM_Odm->DM_PSTable;
+ struct rtw_adapter *adapter = pDM_Odm->Adapter;
+ u32 val32;
+ u8 Rssi_Up_bound = 30;
+ u8 Rssi_Low_bound = 25;
+ if (pDM_PSTable->initialize == 0) {
+
+ pDM_PSTable->Reg874 =
+ rtl8723au_read32(adapter, 0x874) & 0x1CC000;
+ pDM_PSTable->RegC70 =
+ rtl8723au_read32(adapter, 0xc70) & BIT(3);
+ pDM_PSTable->Reg85C =
+ rtl8723au_read32(adapter, 0x85c) & 0xFF000000;
+ pDM_PSTable->RegA74 = rtl8723au_read32(adapter, 0xa74) & 0xF000;
+ pDM_PSTable->initialize = 1;
+ }
+
+ if (!bForceInNormal) {
+ if (pDM_Odm->RSSI_Min != 0xFF) {
+ if (pDM_PSTable->PreRFState == RF_Normal) {
+ if (pDM_Odm->RSSI_Min >= Rssi_Up_bound)
+ pDM_PSTable->CurRFState = RF_Save;
+ else
+ pDM_PSTable->CurRFState = RF_Normal;
+ } else {
+ if (pDM_Odm->RSSI_Min <= Rssi_Low_bound)
+ pDM_PSTable->CurRFState = RF_Normal;
+ else
+ pDM_PSTable->CurRFState = RF_Save;
+ }
+ } else {
+ pDM_PSTable->CurRFState = RF_MAX;
+ }
+ } else {
+ pDM_PSTable->CurRFState = RF_Normal;
+ }
+
+ if (pDM_PSTable->PreRFState != pDM_PSTable->CurRFState) {
+ if (pDM_PSTable->CurRFState == RF_Save) {
+ /* <tynli_note> 8723 RSSI report will be wrong.
+ * Set 0x874[5]= 1 when enter BB power saving mode. */
+ /* Suggested by SD3 Yu-Nan. 2011.01.20. */
+ /* Reg874[5]= 1b'1 */
+ if (pDM_Odm->SupportICType == ODM_RTL8723A) {
+ val32 = rtl8723au_read32(adapter, 0x874);
+ val32 |= BIT(5);
+ rtl8723au_write32(adapter, 0x874, val32);
+ }
+ /* Reg874[20:18]= 3'b010 */
+ val32 = rtl8723au_read32(adapter, 0x874);
+ val32 &= ~(BIT(18) | BIT(20));
+ val32 |= BIT(19);
+ rtl8723au_write32(adapter, 0x874, val32);
+ /* RegC70[3]= 1'b0 */
+ val32 = rtl8723au_read32(adapter, 0xc70);
+ val32 &= ~BIT(3);
+ rtl8723au_write32(adapter, 0xc70, val32);
+ /* Reg85C[31:24]= 0x63 */
+ val32 = rtl8723au_read32(adapter, 0x85c);
+ val32 &= 0x00ffffff;
+ val32 |= 0x63000000;
+ rtl8723au_write32(adapter, 0x85c, val32);
+ /* Reg874[15:14]= 2'b10 */
+ val32 = rtl8723au_read32(adapter, 0x874);
+ val32 &= ~BIT(14);
+ val32 |= BIT(15);
+ rtl8723au_write32(adapter, 0x874, val32);
+ /* RegA75[7:4]= 0x3 */
+ val32 = rtl8723au_read32(adapter, 0xa74);
+ val32 &= ~(BIT(14) | BIT(15));
+ val32 |= (BIT(12) | BIT(13));
+ rtl8723au_write32(adapter, 0xa74, val32);
+ /* Reg818[28]= 1'b0 */
+ val32 = rtl8723au_read32(adapter, 0x818);
+ val32 &= ~BIT(28);
+ rtl8723au_write32(adapter, 0x818, val32);
+ /* Reg818[28]= 1'b1 */
+ val32 = rtl8723au_read32(adapter, 0x818);
+ val32 |= BIT(28);
+ rtl8723au_write32(adapter, 0x818, val32);
+ } else {
+ val32 = rtl8723au_read32(adapter, 0x874);
+ val32 |= pDM_PSTable->Reg874;
+ rtl8723au_write32(adapter, 0x874, val32);
+
+ val32 = rtl8723au_read32(adapter, 0xc70);
+ val32 |= pDM_PSTable->RegC70;
+ rtl8723au_write32(adapter, 0xc70, val32);
+
+ val32 = rtl8723au_read32(adapter, 0x85c);
+ val32 |= pDM_PSTable->Reg85C;
+ rtl8723au_write32(adapter, 0x85c, val32);
+
+ val32 = rtl8723au_read32(adapter, 0xa74);
+ val32 |= pDM_PSTable->RegA74;
+ rtl8723au_write32(adapter, 0xa74, val32);
+
+ val32 = rtl8723au_read32(adapter, 0x818);
+ val32 &= ~BIT(28);
+ rtl8723au_write32(adapter, 0x818, val32);
+
+ /* Reg874[5]= 1b'0 */
+ if (pDM_Odm->SupportICType == ODM_RTL8723A) {
+ val32 = rtl8723au_read32(adapter, 0x874);
+ val32 &= ~BIT(5);
+ rtl8723au_write32(adapter, 0x874, val32);
+ }
+ }
+ pDM_PSTable->PreRFState = pDM_PSTable->CurRFState;
+ }
+}
+
+/* 3 ============================================================ */
+/* 3 RATR MASK */
+/* 3 ============================================================ */
+/* 3 ============================================================ */
+/* 3 Rate Adaptive */
+/* 3 ============================================================ */
+
+void odm_RateAdaptiveMaskInit23a(struct dm_odm_t *pDM_Odm)
+{
+ struct odm_rate_adapt *pOdmRA = &pDM_Odm->RateAdaptive;
+
+ pOdmRA->Type = DM_Type_ByDriver;
+
+ pOdmRA->RATRState = DM_RATR_STA_INIT;
+ pOdmRA->HighRSSIThresh = 50;
+ pOdmRA->LowRSSIThresh = 20;
+}
+
+u32 ODM_Get_Rate_Bitmap23a(struct hal_data_8723a *pHalData, u32 macid,
+ u32 ra_mask, u8 rssi_level)
+{
+ struct dm_odm_t *pDM_Odm = &pHalData->odmpriv;
+ struct sta_info *pEntry;
+ u32 rate_bitmap = 0x0fffffff;
+ u8 WirelessMode;
+
+ pEntry = pDM_Odm->pODM_StaInfo[macid];
+ if (!pEntry)
+ return ra_mask;
+
+ WirelessMode = pEntry->wireless_mode;
+
+ switch (WirelessMode) {
+ case ODM_WM_B:
+ if (ra_mask & 0x0000000c) /* 11M or 5.5M enable */
+ rate_bitmap = 0x0000000d;
+ else
+ rate_bitmap = 0x0000000f;
+ break;
+ case (ODM_WM_A|ODM_WM_G):
+ if (rssi_level == DM_RATR_STA_HIGH)
+ rate_bitmap = 0x00000f00;
+ else
+ rate_bitmap = 0x00000ff0;
+ break;
+ case (ODM_WM_B|ODM_WM_G):
+ if (rssi_level == DM_RATR_STA_HIGH)
+ rate_bitmap = 0x00000f00;
+ else if (rssi_level == DM_RATR_STA_MIDDLE)
+ rate_bitmap = 0x00000ff0;
+ else
+ rate_bitmap = 0x00000ff5;
+ break;
+ case (ODM_WM_B|ODM_WM_G|ODM_WM_N24G):
+ case (ODM_WM_A|ODM_WM_B|ODM_WM_G|ODM_WM_N24G):
+ if (pHalData->rf_type == RF_1T2R ||
+ pHalData->rf_type == RF_1T1R) {
+ if (rssi_level == DM_RATR_STA_HIGH) {
+ rate_bitmap = 0x000f0000;
+ } else if (rssi_level == DM_RATR_STA_MIDDLE) {
+ rate_bitmap = 0x000ff000;
+ } else {
+ if (pHalData->CurrentChannelBW ==
+ HT_CHANNEL_WIDTH_40)
+ rate_bitmap = 0x000ff015;
+ else
+ rate_bitmap = 0x000ff005;
+ }
+ } else {
+ if (rssi_level == DM_RATR_STA_HIGH) {
+ rate_bitmap = 0x0f8f0000;
+ } else if (rssi_level == DM_RATR_STA_MIDDLE) {
+ rate_bitmap = 0x0f8ff000;
+ } else {
+ if (pHalData->CurrentChannelBW ==
+ HT_CHANNEL_WIDTH_40)
+ rate_bitmap = 0x0f8ff015;
+ else
+ rate_bitmap = 0x0f8ff005;
+ }
+ }
+ break;
+ default:
+ /* case WIRELESS_11_24N: */
+ /* case WIRELESS_11_5N: */
+ if (pHalData->rf_type == RF_1T2R)
+ rate_bitmap = 0x000fffff;
+ else
+ rate_bitmap = 0x0fffffff;
+ break;
+ }
+
+ ODM_RT_TRACE(pDM_Odm, ODM_COMP_RA_MASK, ODM_DBG_LOUD,
+ (" ==> rssi_level:0x%02x, WirelessMode:0x%02x, rate_bitmap:0x%08x \n",
+ rssi_level, WirelessMode, rate_bitmap));
+
+ return rate_bitmap;
+}
+
+/*-----------------------------------------------------------------------------
+ * Function: odm_RefreshRateAdaptiveMask()
+ *
+ * Overview: Update rate table mask according to rssi
+ *
+ * Input: NONE
+ *
+ * Output: NONE
+ *
+ * Return: NONE
+ *
+ * Revised History:
+ *When Who Remark
+ *05/27/2009 hpfan Create Version 0.
+ *
+ *---------------------------------------------------------------------------*/
+static void odm_RefreshRateAdaptiveMask(struct dm_odm_t *pDM_Odm)
+{
+ struct rtw_adapter *pAdapter = pDM_Odm->Adapter;
+ u32 smoothed;
+ u8 i;
+
+ if (pAdapter->bDriverStopped) {
+ ODM_RT_TRACE(pDM_Odm, ODM_COMP_RA_MASK, ODM_DBG_TRACE,
+ ("<---- %s: driver is going to unload\n",
+ __func__));
+ return;
+ }
+
+ for (i = 0; i < ODM_ASSOCIATE_ENTRY_NUM; i++) {
+ struct sta_info *pstat = pDM_Odm->pODM_StaInfo[i];
+ if (pstat) {
+ smoothed = pstat->rssi_stat.UndecoratedSmoothedPWDB;
+ if (ODM_RAStateCheck23a(pDM_Odm, smoothed, false,
+ &pstat->rssi_level)) {
+ ODM_RT_TRACE(pDM_Odm, ODM_COMP_RA_MASK,
+ ODM_DBG_LOUD,
+ ("RSSI:%d, RSSI_LEVEL:%d\n",
+ smoothed,
+ pstat->rssi_level));
+ rtw_hal_update_ra_mask23a(pstat,
+ pstat->rssi_level);
+ }
+ }
+ }
+}
+
+/* Return Value: bool */
+/* - true: RATRState is changed. */
+bool ODM_RAStateCheck23a(struct dm_odm_t *pDM_Odm, s32 RSSI, bool bForceUpdate,
+ u8 *pRATRState)
+{
+ struct odm_rate_adapt *pRA = &pDM_Odm->RateAdaptive;
+ const u8 GoUpGap = 5;
+ u8 HighRSSIThreshForRA = pRA->HighRSSIThresh;
+ u8 LowRSSIThreshForRA = pRA->LowRSSIThresh;
+ u8 RATRState;
+
+ /* Threshold Adjustment: */
+ /* when RSSI state trends to go up one or two levels, make sure RSSI is high enough. */
+ /* Here GoUpGap is added to solve the boundary's level alternation issue. */
+ switch (*pRATRState) {
+ case DM_RATR_STA_INIT:
+ case DM_RATR_STA_HIGH:
+ break;
+ case DM_RATR_STA_MIDDLE:
+ HighRSSIThreshForRA += GoUpGap;
+ break;
+ case DM_RATR_STA_LOW:
+ HighRSSIThreshForRA += GoUpGap;
+ LowRSSIThreshForRA += GoUpGap;
+ break;
+ default:
+ ODM_RT_ASSERT(pDM_Odm, false, ("wrong rssi level setting %d !",
+ *pRATRState));
+ break;
+ }
+
+ /* Decide RATRState by RSSI. */
+ if (RSSI > HighRSSIThreshForRA)
+ RATRState = DM_RATR_STA_HIGH;
+ else if (RSSI > LowRSSIThreshForRA)
+ RATRState = DM_RATR_STA_MIDDLE;
+ else
+ RATRState = DM_RATR_STA_LOW;
+
+ if (*pRATRState != RATRState || bForceUpdate) {
+ ODM_RT_TRACE(pDM_Odm, ODM_COMP_RA_MASK, ODM_DBG_LOUD,
+ ("RSSI Level %d -> %d\n", *pRATRState, RATRState));
+ *pRATRState = RATRState;
+ return true;
+ }
+ return false;
+}
+
+/* 3 ============================================================ */
+/* 3 Dynamic Tx Power */
+/* 3 ============================================================ */
+
+void odm_DynamicTxPower23aInit(struct dm_odm_t *pDM_Odm)
+{
+ struct rtw_adapter *Adapter = pDM_Odm->Adapter;
+ struct hal_data_8723a *pHalData = GET_HAL_DATA(Adapter);
+ struct dm_priv *pdmpriv = &pHalData->dmpriv;
+
+ /*
+ * This is never changed, so we should be able to clean up the
+ * code checking for different values in rtl8723a_rf6052.c
+ */
+ pdmpriv->DynamicTxHighPowerLvl = TxHighPwrLevel_Normal;
+}
+
+static void
+FindMinimumRSSI(struct rtw_adapter *pAdapter)
+{
+ struct hal_data_8723a *pHalData = GET_HAL_DATA(pAdapter);
+ struct dm_priv *pdmpriv = &pHalData->dmpriv;
+ struct dm_odm_t *pDM_Odm = &pHalData->odmpriv;
+
+ /* 1 1.Determine the minimum RSSI */
+
+ if (!pDM_Odm->bLinked && !pdmpriv->EntryMinUndecoratedSmoothedPWDB)
+ pdmpriv->MinUndecoratedPWDBForDM = 0;
+ else
+ pdmpriv->MinUndecoratedPWDBForDM =
+ pdmpriv->EntryMinUndecoratedSmoothedPWDB;
+}
+
+static void odm_RSSIMonitorCheck(struct dm_odm_t *pDM_Odm)
+{
+ struct rtw_adapter *Adapter = pDM_Odm->Adapter;
+ struct hal_data_8723a *pHalData = GET_HAL_DATA(Adapter);
+ struct dm_priv *pdmpriv = &pHalData->dmpriv;
+ int i;
+ int MaxDB = 0, MinDB = 0xff;
+ u8 sta_cnt = 0;
+ u32 tmpdb;
+ u32 PWDB_rssi[NUM_STA] = {0};/* 0~15]:MACID, [16~31]:PWDB_rssi */
+ struct sta_info *psta;
+
+ if (!pDM_Odm->bLinked)
+ return;
+
+ for (i = 0; i < ODM_ASSOCIATE_ENTRY_NUM; i++) {
+ psta = pDM_Odm->pODM_StaInfo[i];
+ if (psta) {
+ if (psta->rssi_stat.UndecoratedSmoothedPWDB < MinDB)
+ MinDB = psta->rssi_stat.UndecoratedSmoothedPWDB;
+
+ if (psta->rssi_stat.UndecoratedSmoothedPWDB > MaxDB)
+ MaxDB = psta->rssi_stat.UndecoratedSmoothedPWDB;
+
+ if (psta->rssi_stat.UndecoratedSmoothedPWDB != -1) {
+ tmpdb = psta->rssi_stat.UndecoratedSmoothedPWDB;
+ PWDB_rssi[sta_cnt++] = psta->mac_id |
+ (tmpdb << 16);
+ }
+ }
+ }
+
+ for (i = 0; i < sta_cnt; i++) {
+ if (PWDB_rssi[i] != (0))
+ rtl8723a_set_rssi_cmd(Adapter, (u8 *)&PWDB_rssi[i]);
+ }
+
+ pdmpriv->EntryMaxUndecoratedSmoothedPWDB = MaxDB;
+
+ if (MinDB != 0xff) /* If associated entry is found */
+ pdmpriv->EntryMinUndecoratedSmoothedPWDB = MinDB;
+ else
+ pdmpriv->EntryMinUndecoratedSmoothedPWDB = 0;
+
+ FindMinimumRSSI(Adapter);/* get pdmpriv->MinUndecoratedPWDBForDM */
+
+ ODM_CmnInfoUpdate23a(&pHalData->odmpriv, ODM_CMNINFO_RSSI_MIN,
+ pdmpriv->MinUndecoratedPWDBForDM);
+}
+
+/* endif */
+/* 3 ============================================================ */
+/* 3 Tx Power Tracking */
+/* 3 ============================================================ */
+
+static void odm_TXPowerTrackingInit(struct dm_odm_t *pDM_Odm)
+{
+ struct rtw_adapter *Adapter = pDM_Odm->Adapter;
+ struct hal_data_8723a *pHalData = GET_HAL_DATA(Adapter);
+ struct dm_priv *pdmpriv = &pHalData->dmpriv;
+
+ pdmpriv->bTXPowerTracking = true;
+ pdmpriv->TXPowercount = 0;
+ pdmpriv->bTXPowerTrackingInit = false;
+ pdmpriv->TxPowerTrackControl = true;
+ MSG_8723A("pdmpriv->TxPowerTrackControl = %d\n",
+ pdmpriv->TxPowerTrackControl);
+
+ pDM_Odm->RFCalibrateInfo.TxPowerTrackControl = true;
+}
+
+/* EDCA Turbo */
+static void ODM_EdcaTurboInit23a(struct dm_odm_t *pDM_Odm)
+{
+ struct rtw_adapter *Adapter = pDM_Odm->Adapter;
+
+ pDM_Odm->DM_EDCA_Table.bCurrentTurboEDCA = false;
+ Adapter->recvpriv.bIsAnyNonBEPkts = false;
+
+ ODM_RT_TRACE(pDM_Odm, ODM_COMP_EDCA_TURBO, ODM_DBG_LOUD,
+ ("Orginial VO PARAM: 0x%x\n",
+ rtl8723au_read32(Adapter, ODM_EDCA_VO_PARAM)));
+ ODM_RT_TRACE(pDM_Odm, ODM_COMP_EDCA_TURBO, ODM_DBG_LOUD,
+ ("Orginial VI PARAM: 0x%x\n",
+ rtl8723au_read32(Adapter, ODM_EDCA_VI_PARAM)));
+ ODM_RT_TRACE(pDM_Odm, ODM_COMP_EDCA_TURBO, ODM_DBG_LOUD,
+ ("Orginial BE PARAM: 0x%x\n",
+ rtl8723au_read32(Adapter, ODM_EDCA_BE_PARAM)));
+ ODM_RT_TRACE(pDM_Odm, ODM_COMP_EDCA_TURBO, ODM_DBG_LOUD,
+ ("Orginial BK PARAM: 0x%x\n",
+ rtl8723au_read32(Adapter, ODM_EDCA_BK_PARAM)));
+}
+
+static void odm_EdcaTurboCheck23a(struct dm_odm_t *pDM_Odm)
+{
+ struct rtw_adapter *Adapter = pDM_Odm->Adapter;
+ struct hal_data_8723a *pHalData = GET_HAL_DATA(Adapter);
+ struct xmit_priv *pxmitpriv = &Adapter->xmitpriv;
+ struct recv_priv *precvpriv = &Adapter->recvpriv;
+ struct registry_priv *pregpriv = &Adapter->registrypriv;
+ struct mlme_ext_priv *pmlmeext = &Adapter->mlmeextpriv;
+ struct mlme_ext_info *pmlmeinfo = &pmlmeext->mlmext_info;
+ u32 trafficIndex;
+ u32 edca_param;
+ u64 cur_tx_bytes;
+ u64 cur_rx_bytes;
+
+ /* For AP/ADSL use struct rtl8723a_priv * */
+ /* For CE/NIC use struct rtw_adapter * */
+
+ /*
+ * 2011/09/29 MH In HW integration first stage, we provide 4
+ * different handle to operate at the same time. In the stage2/3,
+ * we need to prive universal interface and merge all HW dynamic
+ * mechanism.
+ */
+
+ if ((pregpriv->wifi_spec == 1))/* (pmlmeinfo->HT_enable == 0)) */
+ goto dm_CheckEdcaTurbo_EXIT;
+
+ if (pmlmeinfo->assoc_AP_vendor >= HT_IOT_PEER_MAX)
+ goto dm_CheckEdcaTurbo_EXIT;
+
+ if (rtl8723a_BT_disable_EDCA_turbo(Adapter))
+ goto dm_CheckEdcaTurbo_EXIT;
+
+ /* Check if the status needs to be changed. */
+ if (!precvpriv->bIsAnyNonBEPkts) {
+ cur_tx_bytes = pxmitpriv->tx_bytes - pxmitpriv->last_tx_bytes;
+ cur_rx_bytes = precvpriv->rx_bytes - precvpriv->last_rx_bytes;
+
+ /* traffic, TX or RX */
+ if ((pmlmeinfo->assoc_AP_vendor == HT_IOT_PEER_RALINK) ||
+ (pmlmeinfo->assoc_AP_vendor == HT_IOT_PEER_ATHEROS)) {
+ if (cur_tx_bytes > (cur_rx_bytes << 2)) {
+ /* Uplink TP is present. */
+ trafficIndex = UP_LINK;
+ } else { /* Balance TP is present. */
+ trafficIndex = DOWN_LINK;
+ }
+ } else {
+ if (cur_rx_bytes > (cur_tx_bytes << 2)) {
+ /* Downlink TP is present. */
+ trafficIndex = DOWN_LINK;
+ } else { /* Balance TP is present. */
+ trafficIndex = UP_LINK;
+ }
+ }
+
+ if ((pDM_Odm->DM_EDCA_Table.prv_traffic_idx != trafficIndex) ||
+ (!pDM_Odm->DM_EDCA_Table.bCurrentTurboEDCA)) {
+ if ((pmlmeinfo->assoc_AP_vendor == HT_IOT_PEER_CISCO) &&
+ (pmlmeext->cur_wireless_mode & WIRELESS_11_24N))
+ edca_param = EDCAParam[pmlmeinfo->assoc_AP_vendor][trafficIndex];
+ else
+ edca_param = EDCAParam[HT_IOT_PEER_UNKNOWN][trafficIndex];
+ rtl8723au_write32(Adapter, REG_EDCA_BE_PARAM,
+ edca_param);
+
+ pDM_Odm->DM_EDCA_Table.prv_traffic_idx = trafficIndex;
+ }
+
+ pDM_Odm->DM_EDCA_Table.bCurrentTurboEDCA = true;
+ } else {
+ /* Turn Off EDCA turbo here. */
+ /* Restore original EDCA according to the declaration of AP. */
+ if (pDM_Odm->DM_EDCA_Table.bCurrentTurboEDCA) {
+ rtl8723au_write32(Adapter, REG_EDCA_BE_PARAM,
+ pHalData->AcParam_BE);
+ pDM_Odm->DM_EDCA_Table.bCurrentTurboEDCA = false;
+ }
+ }
+
+dm_CheckEdcaTurbo_EXIT:
+ /* Set variables for next time. */
+ precvpriv->bIsAnyNonBEPkts = false;
+ pxmitpriv->last_tx_bytes = pxmitpriv->tx_bytes;
+ precvpriv->last_rx_bytes = precvpriv->rx_bytes;
+}
+
+u32 GetPSDData(struct dm_odm_t *pDM_Odm, unsigned int point,
+ u8 initial_gain_psd)
+{
+ struct rtw_adapter *adapter = pDM_Odm->Adapter;
+ u32 psd_report, val32;
+
+ /* Set DCO frequency index, offset = (40MHz/SamplePts)*point */
+ val32 = rtl8723au_read32(adapter, 0x808);
+ val32 &= ~0x3ff;
+ val32 |= (point & 0x3ff);
+ rtl8723au_write32(adapter, 0x808, val32);
+
+ /* Start PSD calculation, Reg808[22]= 0->1 */
+ val32 = rtl8723au_read32(adapter, 0x808);
+ val32 |= BIT(22);
+ rtl8723au_write32(adapter, 0x808, val32);
+ /* Need to wait for HW PSD report */
+ udelay(30);
+ val32 = rtl8723au_read32(adapter, 0x808);
+ val32 &= ~BIT(22);
+ rtl8723au_write32(adapter, 0x808, val32);
+ /* Read PSD report, Reg8B4[15:0] */
+ psd_report = rtl8723au_read32(adapter, 0x8B4) & 0x0000FFFF;
+
+ psd_report = (u32)(ConvertTo_dB23a(psd_report)) +
+ (u32)(initial_gain_psd-0x1c);
+
+ return psd_report;
+}
+
+u32 ConvertTo_dB23a(u32 Value)
+{
+ u8 i;
+ u8 j;
+ u32 dB;
+
+ Value = Value & 0xFFFF;
+
+ for (i = 0; i < 8; i++) {
+ if (Value <= dB_Invert_Table[i][11])
+ break;
+ }
+
+ if (i >= 8)
+ return 96; /* maximum 96 dB */
+
+ for (j = 0; j < 12; j++) {
+ if (Value <= dB_Invert_Table[i][j])
+ break;
+ }
+
+ dB = i*12 + j + 1;
+
+ return dB;
+}
+
+/* */
+/* Description: */
+/* Set Single/Dual Antenna default setting for products that do not
+ * do detection in advance. */
+/* */
+/* Added by Joseph, 2012.03.22 */
+/* */
+void ODM_SingleDualAntennaDefaultSetting(struct dm_odm_t *pDM_Odm)
+{
+ struct sw_ant_sw *pDM_SWAT_Table = &pDM_Odm->DM_SWAT_Table;
+
+ pDM_SWAT_Table->ANTA_ON = true;
+ pDM_SWAT_Table->ANTB_ON = true;
+}
+
+/* 2 8723A ANT DETECT */
+
+static void odm_PHY_SaveAFERegisters(struct dm_odm_t *pDM_Odm, u32 *AFEReg,
+ u32 *AFEBackup, u32 RegisterNum)
+{
+ u32 i;
+
+ for (i = 0 ; i < RegisterNum ; i++)
+ AFEBackup[i] = rtl8723au_read32(pDM_Odm->Adapter, AFEReg[i]);
+}
+
+static void odm_PHY_ReloadAFERegisters(struct dm_odm_t *pDM_Odm, u32 *AFEReg,
+ u32 *AFEBackup, u32 RegiesterNum)
+{
+ u32 i;
+
+ for (i = 0 ; i < RegiesterNum; i++)
+ rtl8723au_write32(pDM_Odm->Adapter, AFEReg[i], AFEBackup[i]);
+}
+
+/* 2 8723A ANT DETECT */
+/* Description: */
+/* Implement IQK single tone for RF DPK loopback and BB PSD scanning. */
+/* This function is cooperated with BB team Neil. */
+bool ODM_SingleDualAntennaDetection(struct dm_odm_t *pDM_Odm, u8 mode)
+{
+ struct sw_ant_sw *pDM_SWAT_Table = &pDM_Odm->DM_SWAT_Table;
+ struct rtw_adapter *adapter = pDM_Odm->Adapter;
+ u32 CurrentChannel, RfLoopReg;
+ u8 n;
+ u32 Reg88c, Regc08, Reg874, Regc50, val32;
+ u8 initial_gain = 0x5a;
+ u32 PSD_report_tmp;
+ u32 AntA_report = 0x0, AntB_report = 0x0, AntO_report = 0x0;
+ bool bResult = true;
+ u32 AFE_Backup[16];
+ u32 AFE_REG_8723A[16] = {
+ rRx_Wait_CCA, rTx_CCK_RFON,
+ rTx_CCK_BBON, rTx_OFDM_RFON,
+ rTx_OFDM_BBON, rTx_To_Rx,
+ rTx_To_Tx, rRx_CCK,
+ rRx_OFDM, rRx_Wait_RIFS,
+ rRx_TO_Rx, rStandby,
+ rSleep, rPMPD_ANAEN,
+ rFPGA0_XCD_SwitchControl, rBlue_Tooth};
+
+ if (!(pDM_Odm->SupportICType & ODM_RTL8723A))
+ return bResult;
+
+ if (!(pDM_Odm->SupportAbility&ODM_BB_ANT_DIV))
+ return bResult;
+ /* 1 Backup Current RF/BB Settings */
+
+ CurrentChannel = ODM_GetRFReg(pDM_Odm, RF_PATH_A, ODM_CHANNEL,
+ bRFRegOffsetMask);
+ RfLoopReg = ODM_GetRFReg(pDM_Odm, RF_PATH_A, 0x00, bRFRegOffsetMask);
+ /* change to Antenna A */
+ val32 = rtl8723au_read32(adapter, rFPGA0_XA_RFInterfaceOE);
+ val32 &= ~0x300;
+ val32 |= 0x100; /* Enable antenna A */
+ rtl8723au_write32(adapter, rFPGA0_XA_RFInterfaceOE, val32);
+
+ /* Step 1: USE IQK to transmitter single tone */
+
+ udelay(10);
+
+ /* Store A Path Register 88c, c08, 874, c50 */
+ Reg88c = rtl8723au_read32(adapter, rFPGA0_AnalogParameter4);
+ Regc08 = rtl8723au_read32(adapter, rOFDM0_TRMuxPar);
+ Reg874 = rtl8723au_read32(adapter, rFPGA0_XCD_RFInterfaceSW);
+ Regc50 = rtl8723au_read32(adapter, rOFDM0_XAAGCCore1);
+
+ /* Store AFE Registers */
+ odm_PHY_SaveAFERegisters(pDM_Odm, AFE_REG_8723A, AFE_Backup, 16);
+
+ /* Set PSD 128 pts */
+ val32 = rtl8723au_read32(adapter, rFPGA0_PSDFunction);
+ val32 &= ~(BIT(14) | BIT(15));
+ rtl8723au_write32(adapter, rFPGA0_PSDFunction, val32);
+
+ /* To SET CH1 to do */
+ ODM_SetRFReg(pDM_Odm, RF_PATH_A, ODM_CHANNEL, bRFRegOffsetMask, 0x01);
+
+ /* AFE all on step */
+ rtl8723au_write32(adapter, rRx_Wait_CCA, 0x6FDB25A4);
+ rtl8723au_write32(adapter, rTx_CCK_RFON, 0x6FDB25A4);
+ rtl8723au_write32(adapter, rTx_CCK_BBON, 0x6FDB25A4);
+ rtl8723au_write32(adapter, rTx_OFDM_RFON, 0x6FDB25A4);
+ rtl8723au_write32(adapter, rTx_OFDM_BBON, 0x6FDB25A4);
+ rtl8723au_write32(adapter, rTx_To_Rx, 0x6FDB25A4);
+ rtl8723au_write32(adapter, rTx_To_Tx, 0x6FDB25A4);
+ rtl8723au_write32(adapter, rRx_CCK, 0x6FDB25A4);
+ rtl8723au_write32(adapter, rRx_OFDM, 0x6FDB25A4);
+ rtl8723au_write32(adapter, rRx_Wait_RIFS, 0x6FDB25A4);
+ rtl8723au_write32(adapter, rRx_TO_Rx, 0x6FDB25A4);
+ rtl8723au_write32(adapter, rStandby, 0x6FDB25A4);
+ rtl8723au_write32(adapter, rSleep, 0x6FDB25A4);
+ rtl8723au_write32(adapter, rPMPD_ANAEN, 0x6FDB25A4);
+ rtl8723au_write32(adapter, rFPGA0_XCD_SwitchControl, 0x6FDB25A4);
+ rtl8723au_write32(adapter, rBlue_Tooth, 0x6FDB25A4);
+
+ /* 3 wire Disable */
+ rtl8723au_write32(adapter, rFPGA0_AnalogParameter4, 0xCCF000C0);
+
+ /* BB IQK Setting */
+ rtl8723au_write32(adapter, rOFDM0_TRMuxPar, 0x000800E4);
+ rtl8723au_write32(adapter, rFPGA0_XCD_RFInterfaceSW, 0x22208000);
+
+ /* IQK setting tone@ 4.34Mhz */
+ rtl8723au_write32(adapter, rTx_IQK_Tone_A, 0x10008C1C);
+ rtl8723au_write32(adapter, rTx_IQK, 0x01007c00);
+
+ /* Page B init */
+ rtl8723au_write32(adapter, rConfig_AntA, 0x00080000);
+ rtl8723au_write32(adapter, rConfig_AntA, 0x0f600000);
+ rtl8723au_write32(adapter, rRx_IQK, 0x01004800);
+ rtl8723au_write32(adapter, rRx_IQK_Tone_A, 0x10008c1f);
+ rtl8723au_write32(adapter, rTx_IQK_PI_A, 0x82150008);
+ rtl8723au_write32(adapter, rRx_IQK_PI_A, 0x28150008);
+ rtl8723au_write32(adapter, rIQK_AGC_Rsp, 0x001028d0);
+
+ /* RF loop Setting */
+ ODM_SetRFReg(pDM_Odm, RF_PATH_A, 0x0, 0xFFFFF, 0x50008);
+
+ /* IQK Single tone start */
+ rtl8723au_write32(adapter, rFPGA0_IQK, 0x80800000);
+ rtl8723au_write32(adapter, rIQK_AGC_Pts, 0xf8000000);
+ udelay(1000);
+ PSD_report_tmp = 0x0;
+
+ for (n = 0; n < 2; n++) {
+ PSD_report_tmp = GetPSDData(pDM_Odm, 14, initial_gain);
+ if (PSD_report_tmp > AntA_report)
+ AntA_report = PSD_report_tmp;
+ }
+
+ PSD_report_tmp = 0x0;
+
+ val32 = rtl8723au_read32(adapter, rFPGA0_XA_RFInterfaceOE);
+ val32 &= ~0x300;
+ val32 |= 0x200; /* Enable antenna B */
+ rtl8723au_write32(adapter, rFPGA0_XA_RFInterfaceOE, val32);
+ udelay(10);
+
+ for (n = 0; n < 2; n++) {
+ PSD_report_tmp = GetPSDData(pDM_Odm, 14, initial_gain);
+ if (PSD_report_tmp > AntB_report)
+ AntB_report = PSD_report_tmp;
+ }
+
+ /* change to open case */
+ /* change to Ant A and B all open case */
+ val32 = rtl8723au_read32(adapter, rFPGA0_XA_RFInterfaceOE);
+ val32 &= ~0x300;
+ rtl8723au_write32(adapter, rFPGA0_XA_RFInterfaceOE, val32);
+ udelay(10);
+
+ for (n = 0; n < 2; n++) {
+ PSD_report_tmp = GetPSDData(pDM_Odm, 14, initial_gain);
+ if (PSD_report_tmp > AntO_report)
+ AntO_report = PSD_report_tmp;
+ }
+
+ /* Close IQK Single Tone function */
+ rtl8723au_write32(adapter, rFPGA0_IQK, 0x00000000);
+ PSD_report_tmp = 0x0;
+
+ /* 1 Return to antanna A */
+ val32 = rtl8723au_read32(adapter, rFPGA0_XA_RFInterfaceOE);
+ val32 &= ~0x300;
+ val32 |= 0x100; /* Enable antenna A */
+ rtl8723au_write32(adapter, rFPGA0_XA_RFInterfaceOE, val32);
+ rtl8723au_write32(adapter, rFPGA0_AnalogParameter4, Reg88c);
+ rtl8723au_write32(adapter, rOFDM0_TRMuxPar, Regc08);
+ rtl8723au_write32(adapter, rFPGA0_XCD_RFInterfaceSW, Reg874);
+ val32 = rtl8723au_read32(adapter, rOFDM0_XAAGCCore1);
+ val32 &= ~0x7f;
+ val32 |= 0x40;
+ rtl8723au_write32(adapter, rOFDM0_XAAGCCore1, val32);
+
+ rtl8723au_write32(adapter, rOFDM0_XAAGCCore1, Regc50);
+ ODM_SetRFReg(pDM_Odm, RF_PATH_A, RF_CHNLBW, bRFRegOffsetMask,
+ CurrentChannel);
+ ODM_SetRFReg(pDM_Odm, RF_PATH_A, 0x00, bRFRegOffsetMask, RfLoopReg);
+
+ /* Reload AFE Registers */
+ odm_PHY_ReloadAFERegisters(pDM_Odm, AFE_REG_8723A, AFE_Backup, 16);
+
+ ODM_RT_TRACE(pDM_Odm, ODM_COMP_ANT_DIV, ODM_DBG_LOUD,
+ ("psd_report_A[%d]= %d \n", 2416, AntA_report));
+ ODM_RT_TRACE(pDM_Odm, ODM_COMP_ANT_DIV, ODM_DBG_LOUD,
+ ("psd_report_B[%d]= %d \n", 2416, AntB_report));
+ ODM_RT_TRACE(pDM_Odm, ODM_COMP_ANT_DIV, ODM_DBG_LOUD,
+ ("psd_report_O[%d]= %d \n", 2416, AntO_report));
+
+ /* 2 Test Ant B based on Ant A is ON */
+ if (mode == ANTTESTB) {
+ if (AntA_report >= 100) {
+ if (AntB_report > (AntA_report+1)) {
+ pDM_SWAT_Table->ANTB_ON = false;
+ ODM_RT_TRACE(pDM_Odm, ODM_COMP_ANT_DIV, ODM_DBG_LOUD, ("ODM_SingleDualAntennaDetection(): Single Antenna A\n"));
+ } else {
+ pDM_SWAT_Table->ANTB_ON = true;
+ ODM_RT_TRACE(pDM_Odm, ODM_COMP_ANT_DIV, ODM_DBG_LOUD, ("ODM_SingleDualAntennaDetection(): Dual Antenna is A and B\n"));
+ }
+ } else {
+ ODM_RT_TRACE(pDM_Odm, ODM_COMP_ANT_DIV, ODM_DBG_LOUD, ("ODM_SingleDualAntennaDetection(): Need to check again\n"));
+ pDM_SWAT_Table->ANTB_ON = false; /* Set Antenna B off as default */
+ bResult = false;
+ }
+ } else if (mode == ANTTESTALL) {
+ /* 2 Test Ant A and B based on DPDT Open */
+ if ((AntO_report >= 100) & (AntO_report < 118)) {
+ if (AntA_report > (AntO_report+1)) {
+ pDM_SWAT_Table->ANTA_ON = false;
+ ODM_RT_TRACE(pDM_Odm, ODM_COMP_ANT_DIV,
+ ODM_DBG_LOUD, ("Ant A is OFF"));
+ } else {
+ pDM_SWAT_Table->ANTA_ON = true;
+ ODM_RT_TRACE(pDM_Odm, ODM_COMP_ANT_DIV,
+ ODM_DBG_LOUD, ("Ant A is ON"));
+ }
+
+ if (AntB_report > (AntO_report+2)) {
+ pDM_SWAT_Table->ANTB_ON = false;
+ ODM_RT_TRACE(pDM_Odm, ODM_COMP_ANT_DIV,
+ ODM_DBG_LOUD, ("Ant B is OFF"));
+ } else {
+ pDM_SWAT_Table->ANTB_ON = true;
+ ODM_RT_TRACE(pDM_Odm, ODM_COMP_ANT_DIV,
+ ODM_DBG_LOUD, ("Ant B is ON"));
+ }
+ }
+ } else {
+ ODM_RT_TRACE(pDM_Odm, ODM_COMP_ANT_DIV, ODM_DBG_LOUD,
+ ("ODM_SingleDualAntennaDetection(): Need to check again\n"));
+ /* Set Antenna A on as default */
+ pDM_SWAT_Table->ANTA_ON = true;
+ /* Set Antenna B off as default */
+ pDM_SWAT_Table->ANTB_ON = false;
+ bResult = false;
+ }
+
+ return bResult;
+}
diff --git a/drivers/staging/rtl8723au/hal/odm_HWConfig.c b/drivers/staging/rtl8723au/hal/odm_HWConfig.c
new file mode 100644
index 000000000..7b9799e3d
--- /dev/null
+++ b/drivers/staging/rtl8723au/hal/odm_HWConfig.c
@@ -0,0 +1,400 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2011 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * 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.
+ *
+ ******************************************************************************/
+
+/* */
+/* include files */
+/* */
+
+#include "odm_precomp.h"
+
+static u8 odm_QueryRxPwrPercentage(s8 AntPower)
+{
+ if ((AntPower <= -100) || (AntPower >= 20))
+ return 0;
+ else if (AntPower >= 0)
+ return 100;
+ else
+ return 100 + AntPower;
+}
+
+static s32 odm_SignalScaleMapping_92CSeries(struct dm_odm_t *pDM_Odm, s32 CurrSig)
+{
+ s32 RetSig = 0;
+
+ if (CurrSig >= 51 && CurrSig <= 100)
+ RetSig = 100;
+ else if (CurrSig >= 41 && CurrSig <= 50)
+ RetSig = 80 + ((CurrSig - 40)*2);
+ else if (CurrSig >= 31 && CurrSig <= 40)
+ RetSig = 66 + (CurrSig - 30);
+ else if (CurrSig >= 21 && CurrSig <= 30)
+ RetSig = 54 + (CurrSig - 20);
+ else if (CurrSig >= 10 && CurrSig <= 20)
+ RetSig = 42 + (((CurrSig - 10) * 2) / 3);
+ else if (CurrSig >= 5 && CurrSig <= 9)
+ RetSig = 22 + (((CurrSig - 5) * 3) / 2);
+ else if (CurrSig >= 1 && CurrSig <= 4)
+ RetSig = 6 + (((CurrSig - 1) * 3) / 2);
+ else
+ RetSig = CurrSig;
+
+ return RetSig;
+}
+
+static s32 odm_SignalScaleMapping(struct dm_odm_t *pDM_Odm, s32 CurrSig)
+{
+ return odm_SignalScaleMapping_92CSeries(pDM_Odm, CurrSig);
+}
+
+static u8
+odm_EVMdbToPercentage(
+ s8 Value
+ )
+{
+ /* */
+ /* -33dB~0dB to 0%~99% */
+ /* */
+ s8 ret_val;
+
+ ret_val = Value;
+
+ if (ret_val >= 0)
+ ret_val = 0;
+ if (ret_val <= -33)
+ ret_val = -33;
+
+ ret_val = 0 - ret_val;
+ ret_val *= 3;
+
+ if (ret_val == 99)
+ ret_val = 100;
+
+ return ret_val;
+}
+
+static void odm_RxPhyStatus92CSeries_Parsing(struct dm_odm_t *pDM_Odm,
+ struct phy_info *pPhyInfo,
+ u8 *pPhyStatus,
+ struct odm_packet_info *pPktinfo)
+{
+ struct phy_status_rpt *pPhyStaRpt = (struct phy_status_rpt *)pPhyStatus;
+ u8 i, Max_spatial_stream;
+ s8 rx_pwr[4], rx_pwr_all = 0;
+ u8 EVM, PWDB_ALL = 0, PWDB_ALL_BT;
+ u8 RSSI, total_rssi = 0;
+ u8 isCCKrate = 0;
+ u8 rf_rx_num = 0;
+ u8 cck_highpwr = 0;
+
+ isCCKrate = (pPktinfo->Rate <= DESC92C_RATE11M) ? true : false;
+ pPhyInfo->RxMIMOSignalQuality[RF_PATH_A] = -1;
+ pPhyInfo->RxMIMOSignalQuality[RF_PATH_B] = -1;
+
+ if (isCCKrate) {
+ u8 report;
+ u8 cck_agc_rpt;
+
+ pDM_Odm->PhyDbgInfo.NumQryPhyStatusCCK++;
+ /* (1)Hardware does not provide RSSI for CCK */
+ /* (2)PWDB, Average PWDB cacluated by hardware (for rate adaptive) */
+
+ cck_highpwr = pDM_Odm->bCckHighPower;
+
+ cck_agc_rpt = pPhyStaRpt->cck_agc_rpt_ofdm_cfosho_a;
+
+ /* The RSSI formula should be modified according to the gain table */
+ if (!cck_highpwr) {
+ report = (cck_agc_rpt & 0xc0)>>6;
+ switch (report) {
+ /* Modify the RF RNA gain value to -40, -20, -2, 14 by Jenyu's suggestion */
+ /* Note: different RF with the different RNA gain. */
+ case 0x3:
+ rx_pwr_all = -46 - (cck_agc_rpt & 0x3e);
+ break;
+ case 0x2:
+ rx_pwr_all = -26 - (cck_agc_rpt & 0x3e);
+ break;
+ case 0x1:
+ rx_pwr_all = -12 - (cck_agc_rpt & 0x3e);
+ break;
+ case 0x0:
+ rx_pwr_all = 16 - (cck_agc_rpt & 0x3e);
+ break;
+ }
+ } else {
+ report = (cck_agc_rpt & 0x60)>>5;
+ switch (report) {
+ case 0x3:
+ rx_pwr_all = -46 - ((cck_agc_rpt & 0x1f)<<1);
+ break;
+ case 0x2:
+ rx_pwr_all = -26 - ((cck_agc_rpt & 0x1f)<<1);
+ break;
+ case 0x1:
+ rx_pwr_all = -12 - ((cck_agc_rpt & 0x1f)<<1);
+ break;
+ case 0x0:
+ rx_pwr_all = 16 - ((cck_agc_rpt & 0x1f)<<1);
+ break;
+ }
+ }
+
+ PWDB_ALL = odm_QueryRxPwrPercentage(rx_pwr_all);
+
+ /* Modification for ext-LNA board */
+ if (pDM_Odm->BoardType == ODM_BOARD_HIGHPWR) {
+ if ((cck_agc_rpt>>7) == 0) {
+ PWDB_ALL = (PWDB_ALL > 94) ? 100 : (PWDB_ALL+6);
+ } else {
+ if (PWDB_ALL > 38)
+ PWDB_ALL -= 16;
+ else
+ PWDB_ALL = (PWDB_ALL <= 16) ? (PWDB_ALL>>2) : (PWDB_ALL-12);
+ }
+
+ /* CCK modification */
+ if (PWDB_ALL > 25 && PWDB_ALL <= 60)
+ PWDB_ALL += 6;
+ } else { /* Modification for int-LNA board */
+ if (PWDB_ALL > 99)
+ PWDB_ALL -= 8;
+ else if (PWDB_ALL > 50 && PWDB_ALL <= 68)
+ PWDB_ALL += 4;
+ }
+ pPhyInfo->RxPWDBAll = PWDB_ALL;
+ pPhyInfo->BTRxRSSIPercentage = PWDB_ALL;
+ pPhyInfo->RecvSignalPower = rx_pwr_all;
+ /* (3) Get Signal Quality (EVM) */
+ if (pPktinfo->bPacketMatchBSSID) {
+ u8 SQ, SQ_rpt;
+
+ SQ_rpt = pPhyStaRpt->cck_sig_qual_ofdm_pwdb_all;
+
+ if (SQ_rpt > 64)
+ SQ = 0;
+ else if (SQ_rpt < 20)
+ SQ = 100;
+ else
+ SQ = ((64-SQ_rpt) * 100) / 44;
+
+ pPhyInfo->SignalQuality = SQ;
+ pPhyInfo->RxMIMOSignalQuality[RF_PATH_A] = SQ;
+ pPhyInfo->RxMIMOSignalQuality[RF_PATH_B] = -1;
+ }
+ } else { /* is OFDM rate */
+ pDM_Odm->PhyDbgInfo.NumQryPhyStatusOFDM++;
+
+ /* (1)Get RSSI for HT rate */
+
+ for (i = RF_PATH_A; i < RF_PATH_MAX; i++) {
+ /* 2008/01/30 MH we will judge RF RX path now. */
+ if (pDM_Odm->RFPathRxEnable & BIT(i))
+ rf_rx_num++;
+
+ rx_pwr[i] = ((pPhyStaRpt->path_agc[i].gain & 0x3F)*2) - 110;
+
+ pPhyInfo->RxPwr[i] = rx_pwr[i];
+
+ /* Translate DBM to percentage. */
+ RSSI = odm_QueryRxPwrPercentage(rx_pwr[i]);
+ total_rssi += RSSI;
+
+ /* Modification for ext-LNA board */
+ if (pDM_Odm->BoardType == ODM_BOARD_HIGHPWR) {
+ if ((pPhyStaRpt->path_agc[i].trsw) == 1)
+ RSSI = (RSSI > 94) ? 100 : (RSSI+6);
+ else
+ RSSI = (RSSI <= 16) ? (RSSI>>3) : (RSSI-16);
+
+ if ((RSSI <= 34) && (RSSI >= 4))
+ RSSI -= 4;
+ }
+
+ pPhyInfo->RxMIMOSignalStrength[i] = (u8) RSSI;
+
+ /* Get Rx snr value in DB */
+ pPhyInfo->RxSNR[i] = pDM_Odm->PhyDbgInfo.RxSNRdB[i] = (s32)(pPhyStaRpt->path_rxsnr[i]/2);
+ }
+
+ /* (2)PWDB, Average PWDB cacluated by hardware (for rate adaptive) */
+ rx_pwr_all = (((pPhyStaRpt->cck_sig_qual_ofdm_pwdb_all) >> 1) & 0x7f)-110;
+
+ PWDB_ALL = odm_QueryRxPwrPercentage(rx_pwr_all);
+ PWDB_ALL_BT = PWDB_ALL;
+
+ pPhyInfo->RxPWDBAll = PWDB_ALL;
+ pPhyInfo->BTRxRSSIPercentage = PWDB_ALL_BT;
+ pPhyInfo->RxPower = rx_pwr_all;
+ pPhyInfo->RecvSignalPower = rx_pwr_all;
+
+ /* (3)EVM of HT rate */
+ if (pPktinfo->Rate >= DESC92C_RATEMCS8 && pPktinfo->Rate <= DESC92C_RATEMCS15)
+ Max_spatial_stream = 2; /* both spatial stream make sense */
+ else
+ Max_spatial_stream = 1; /* only spatial stream 1 makes sense */
+
+ for (i = 0; i < Max_spatial_stream; i++) {
+ /* Do not use shift operation like "rx_evmX >>= 1" because the compilor of free build environment */
+ /* fill most significant bit to "zero" when doing shifting operation which may change a negative */
+ /* value to positive one, then the dbm value (which is supposed to be negative) is not correct anymore. */
+ EVM = odm_EVMdbToPercentage((pPhyStaRpt->stream_rxevm[i])); /* dbm */
+
+ if (pPktinfo->bPacketMatchBSSID) {
+ if (i == RF_PATH_A) {
+ /* Fill value in RFD, Get the first spatial stream only */
+ pPhyInfo->SignalQuality = (u8)(EVM & 0xff);
+ }
+ pPhyInfo->RxMIMOSignalQuality[i] = (u8)(EVM & 0xff);
+ }
+ }
+ }
+ /* UI BSS List signal strength(in percentage), make it good looking, from 0~100. */
+ /* It is assigned to the BSS List in GetValueFromBeaconOrProbeRsp(). */
+ if (isCCKrate) {
+ pPhyInfo->SignalStrength = (u8)(odm_SignalScaleMapping(pDM_Odm, PWDB_ALL));/* PWDB_ALL; */
+ } else {
+ if (rf_rx_num != 0)
+ pPhyInfo->SignalStrength = (u8)(odm_SignalScaleMapping(pDM_Odm, total_rssi /= rf_rx_num));
+ }
+}
+
+void odm_Init_RSSIForDM23a(struct dm_odm_t *pDM_Odm)
+{
+}
+
+static void odm_Process_RSSIForDM(struct dm_odm_t *pDM_Odm,
+ struct phy_info *pPhyInfo,
+ struct odm_packet_info *pPktinfo)
+{
+ s32 UndecoratedSmoothedPWDB, UndecoratedSmoothedCCK;
+ s32 UndecoratedSmoothedOFDM, RSSI_Ave;
+ u8 isCCKrate = 0;
+ u8 RSSI_max, RSSI_min, i;
+ u32 OFDM_pkt = 0;
+ u32 Weighting = 0;
+ struct sta_info *pEntry;
+
+ if (pPktinfo->StationID == 0xFF)
+ return;
+
+ pEntry = pDM_Odm->pODM_StaInfo[pPktinfo->StationID];
+ if (!pEntry)
+ return;
+ if ((!pPktinfo->bPacketMatchBSSID))
+ return;
+
+ isCCKrate = (pPktinfo->Rate <= DESC92C_RATE11M) ? true : false;
+
+ /* Smart Antenna Debug Message------------------*/
+
+ UndecoratedSmoothedCCK = pEntry->rssi_stat.UndecoratedSmoothedCCK;
+ UndecoratedSmoothedOFDM = pEntry->rssi_stat.UndecoratedSmoothedOFDM;
+ UndecoratedSmoothedPWDB = pEntry->rssi_stat.UndecoratedSmoothedPWDB;
+
+ if (pPktinfo->bPacketToSelf || pPktinfo->bPacketBeacon) {
+ if (!isCCKrate) { /* ofdm rate */
+ if (pPhyInfo->RxMIMOSignalStrength[RF_PATH_B] == 0) {
+ RSSI_Ave = pPhyInfo->RxMIMOSignalStrength[RF_PATH_A];
+ } else {
+ if (pPhyInfo->RxMIMOSignalStrength[RF_PATH_A] > pPhyInfo->RxMIMOSignalStrength[RF_PATH_B]) {
+ RSSI_max = pPhyInfo->RxMIMOSignalStrength[RF_PATH_A];
+ RSSI_min = pPhyInfo->RxMIMOSignalStrength[RF_PATH_B];
+ } else {
+ RSSI_max = pPhyInfo->RxMIMOSignalStrength[RF_PATH_B];
+ RSSI_min = pPhyInfo->RxMIMOSignalStrength[RF_PATH_A];
+ }
+ if ((RSSI_max - RSSI_min) < 3)
+ RSSI_Ave = RSSI_max;
+ else if ((RSSI_max - RSSI_min) < 6)
+ RSSI_Ave = RSSI_max - 1;
+ else if ((RSSI_max - RSSI_min) < 10)
+ RSSI_Ave = RSSI_max - 2;
+ else
+ RSSI_Ave = RSSI_max - 3;
+ }
+
+ /* 1 Process OFDM RSSI */
+ if (UndecoratedSmoothedOFDM <= 0) {
+ /* initialize */
+ UndecoratedSmoothedOFDM = pPhyInfo->RxPWDBAll;
+ } else {
+ if (pPhyInfo->RxPWDBAll > (u32)UndecoratedSmoothedOFDM) {
+ UndecoratedSmoothedOFDM =
+ (((UndecoratedSmoothedOFDM)*(Rx_Smooth_Factor-1)) +
+ (RSSI_Ave)) / (Rx_Smooth_Factor);
+ UndecoratedSmoothedOFDM = UndecoratedSmoothedOFDM + 1;
+ } else {
+ UndecoratedSmoothedOFDM =
+ (((UndecoratedSmoothedOFDM)*(Rx_Smooth_Factor-1)) +
+ (RSSI_Ave)) / (Rx_Smooth_Factor);
+ }
+ }
+ pEntry->rssi_stat.PacketMap =
+ (pEntry->rssi_stat.PacketMap<<1) | BIT(0);
+ } else {
+ RSSI_Ave = pPhyInfo->RxPWDBAll;
+
+ /* 1 Process CCK RSSI */
+ if (UndecoratedSmoothedCCK <= 0) {
+ /* initialize */
+ UndecoratedSmoothedCCK = pPhyInfo->RxPWDBAll;
+ } else {
+ if (pPhyInfo->RxPWDBAll > (u32)UndecoratedSmoothedCCK) {
+ UndecoratedSmoothedCCK =
+ (((UndecoratedSmoothedCCK)*(Rx_Smooth_Factor-1)) +
+ (pPhyInfo->RxPWDBAll)) / (Rx_Smooth_Factor);
+ UndecoratedSmoothedCCK = UndecoratedSmoothedCCK + 1;
+ } else {
+ UndecoratedSmoothedCCK =
+ (((UndecoratedSmoothedCCK)*(Rx_Smooth_Factor-1)) +
+ (pPhyInfo->RxPWDBAll)) / (Rx_Smooth_Factor);
+ }
+ }
+ pEntry->rssi_stat.PacketMap = pEntry->rssi_stat.PacketMap<<1;
+ }
+
+ /* 2011.07.28 LukeLee: modified to prevent unstable CCK RSSI */
+ if (pEntry->rssi_stat.ValidBit >= 64)
+ pEntry->rssi_stat.ValidBit = 64;
+ else
+ pEntry->rssi_stat.ValidBit++;
+
+ for (i = 0; i < pEntry->rssi_stat.ValidBit; i++)
+ OFDM_pkt +=
+ (u8)(pEntry->rssi_stat.PacketMap>>i) & BIT(0);
+
+ if (pEntry->rssi_stat.ValidBit == 64) {
+ Weighting = ((OFDM_pkt<<4) > 64)?64:(OFDM_pkt<<4);
+ UndecoratedSmoothedPWDB = (Weighting*UndecoratedSmoothedOFDM+(64-Weighting)*UndecoratedSmoothedCCK)>>6;
+ } else {
+ if (pEntry->rssi_stat.ValidBit != 0)
+ UndecoratedSmoothedPWDB = (OFDM_pkt*UndecoratedSmoothedOFDM+(pEntry->rssi_stat.ValidBit-OFDM_pkt)*UndecoratedSmoothedCCK)/pEntry->rssi_stat.ValidBit;
+ else
+ UndecoratedSmoothedPWDB = 0;
+ }
+ pEntry->rssi_stat.UndecoratedSmoothedCCK = UndecoratedSmoothedCCK;
+ pEntry->rssi_stat.UndecoratedSmoothedOFDM = UndecoratedSmoothedOFDM;
+ pEntry->rssi_stat.UndecoratedSmoothedPWDB = UndecoratedSmoothedPWDB;
+ }
+}
+
+void ODM_PhyStatusQuery23a(struct dm_odm_t *pDM_Odm, struct phy_info *pPhyInfo,
+ u8 *pPhyStatus, struct odm_packet_info *pPktinfo)
+{
+ odm_RxPhyStatus92CSeries_Parsing(pDM_Odm, pPhyInfo,
+ pPhyStatus, pPktinfo);
+
+ odm_Process_RSSIForDM(pDM_Odm, pPhyInfo, pPktinfo);
+}
diff --git a/drivers/staging/rtl8723au/hal/odm_RegConfig8723A.c b/drivers/staging/rtl8723au/hal/odm_RegConfig8723A.c
new file mode 100644
index 000000000..342dec3e9
--- /dev/null
+++ b/drivers/staging/rtl8723au/hal/odm_RegConfig8723A.c
@@ -0,0 +1,88 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2011 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * 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.
+ *
+ ******************************************************************************/
+
+#include "odm_precomp.h"
+#include "usb_ops_linux.h"
+
+void
+odm_ConfigRFReg_8723A(
+ struct dm_odm_t *pDM_Odm,
+ u32 Addr,
+ u32 Data,
+ enum RF_RADIO_PATH RF_PATH,
+ u32 RegAddr
+ )
+{
+ if (Addr == 0xfe) {
+ msleep(50);
+ } else if (Addr == 0xfd) {
+ mdelay(5);
+ } else if (Addr == 0xfc) {
+ mdelay(1);
+ } else if (Addr == 0xfb) {
+ udelay(50);
+ } else if (Addr == 0xfa) {
+ udelay(5);
+ } else if (Addr == 0xf9) {
+ udelay(1);
+ } else {
+ ODM_SetRFReg(pDM_Odm, RF_PATH, RegAddr, bRFRegOffsetMask, Data);
+ /* Add 1us delay between BB/RF register setting. */
+ udelay(1);
+ }
+}
+
+void odm_ConfigMAC_8723A(struct dm_odm_t *pDM_Odm, u32 addr, u8 data)
+{
+ rtl8723au_write8(pDM_Odm->Adapter, addr, data);
+ ODM_RT_TRACE(pDM_Odm, ODM_COMP_INIT, ODM_DBG_LOUD,
+ ("===> %s: [MAC_REG] %08X %08X\n", __func__, addr, data));
+}
+
+void odm_ConfigBB_AGC_8723A(struct dm_odm_t *pDM_Odm, u32 addr, u32 data)
+{
+ rtl8723au_write32(pDM_Odm->Adapter, addr, data);
+ /* Add 1us delay between BB/RF register setting. */
+ udelay(1);
+
+ ODM_RT_TRACE(pDM_Odm, ODM_COMP_INIT, ODM_DBG_LOUD,
+ ("===> %s: [AGC_TAB] %08X %08X\n", __func__, addr, data));
+}
+
+void
+odm_ConfigBB_PHY_8723A(struct dm_odm_t *pDM_Odm, u32 addr, u32 data)
+{
+ if (addr == 0xfe)
+ msleep(50);
+ else if (addr == 0xfd)
+ mdelay(5);
+ else if (addr == 0xfc)
+ mdelay(1);
+ else if (addr == 0xfb)
+ udelay(50);
+ else if (addr == 0xfa)
+ udelay(5);
+ else if (addr == 0xf9)
+ udelay(1);
+ else if (addr == 0xa24)
+ pDM_Odm->RFCalibrateInfo.RegA24 = data;
+ rtl8723au_write32(pDM_Odm->Adapter, addr, data);
+
+ /* Add 1us delay between BB/RF register setting. */
+ udelay(1);
+
+ ODM_RT_TRACE(pDM_Odm, ODM_COMP_INIT, ODM_DBG_LOUD,
+ ("===> %s: [PHY_REG] %08X %08X\n", __func__, addr, data));
+}
diff --git a/drivers/staging/rtl8723au/hal/odm_debug.c b/drivers/staging/rtl8723au/hal/odm_debug.c
new file mode 100644
index 000000000..cb2bdda6b
--- /dev/null
+++ b/drivers/staging/rtl8723au/hal/odm_debug.c
@@ -0,0 +1,39 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2011 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * 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.
+ *
+ ******************************************************************************/
+
+#include "odm_precomp.h"
+
+void ODM_InitDebugSetting23a(struct dm_odm_t *pDM_Odm)
+{
+ pDM_Odm->DebugLevel = ODM_DBG_TRACE;
+ pDM_Odm->DebugComponents = 0;
+}
+
+u32 GlobalDebugLevel23A;
+
+void rt_trace(int comp, int level, const char *fmt, ...)
+{
+ struct va_format vaf;
+ va_list args;
+
+ va_start(args, fmt);
+
+ vaf.fmt = fmt;
+ vaf.va = &args;
+
+ pr_info(DRIVER_PREFIX " [0x%08x,%d] %pV", comp, level, &vaf);
+
+ va_end(args);
+}
diff --git a/drivers/staging/rtl8723au/hal/odm_interface.c b/drivers/staging/rtl8723au/hal/odm_interface.c
new file mode 100644
index 000000000..d8f679027
--- /dev/null
+++ b/drivers/staging/rtl8723au/hal/odm_interface.c
@@ -0,0 +1,49 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2011 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * 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.
+ *
+ ******************************************************************************/
+
+/* */
+/* include files */
+/* */
+
+#include "odm_precomp.h"
+/* */
+/* ODM IO Relative API. */
+/* */
+#include <usb_ops_linux.h>
+
+void ODM_SetRFReg(
+ struct dm_odm_t *pDM_Odm,
+ enum RF_RADIO_PATH eRFPath,
+ u32 RegAddr,
+ u32 BitMask,
+ u32 Data
+ )
+{
+ struct rtw_adapter *Adapter = pDM_Odm->Adapter;
+
+ PHY_SetRFReg(Adapter, eRFPath, RegAddr, BitMask, Data);
+}
+
+u32 ODM_GetRFReg(
+ struct dm_odm_t *pDM_Odm,
+ enum RF_RADIO_PATH eRFPath,
+ u32 RegAddr,
+ u32 BitMask
+ )
+{
+ struct rtw_adapter *Adapter = pDM_Odm->Adapter;
+
+ return PHY_QueryRFReg(Adapter, eRFPath, RegAddr, BitMask);
+}
diff --git a/drivers/staging/rtl8723au/hal/rtl8723a_bt-coexist.c b/drivers/staging/rtl8723au/hal/rtl8723a_bt-coexist.c
new file mode 100644
index 000000000..cf15f8083
--- /dev/null
+++ b/drivers/staging/rtl8723au/hal/rtl8723a_bt-coexist.c
@@ -0,0 +1,11297 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2012 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ *published by the Free Software Foundation.
+ *
+ * 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.
+ *
+ ******************************************************************************/
+#include <drv_types.h>
+#include <rtl8723a_hal.h>
+#include <usb_ops_linux.h>
+
+#define DIS_PS_RX_BCN
+
+u32 BTCoexDbgLevel = _bt_dbg_off_;
+
+#define RTPRINT(_Comp, _Level, Fmt)\
+do {\
+ if ((BTCoexDbgLevel == _bt_dbg_on_)) {\
+ printk Fmt;\
+ } \
+} while (0)
+
+#define RTPRINT_ADDR(dbgtype, dbgflag, printstr, _Ptr)\
+if ((BTCoexDbgLevel == _bt_dbg_on_)) {\
+ u32 __i; \
+ u8 *ptr = (u8 *)_Ptr; \
+ printk printstr; \
+ printk(" "); \
+ for (__i = 0; __i < 6; __i++) \
+ printk("%02X%s", ptr[__i], (__i == 5)?"":"-"); \
+ printk("\n"); \
+}
+#define RTPRINT_DATA(dbgtype, dbgflag, _TitleString, _HexData, _HexDataLen)\
+if ((BTCoexDbgLevel == _bt_dbg_on_)) {\
+ u32 __i; \
+ u8 *ptr = (u8 *)_HexData; \
+ printk(_TitleString); \
+ for (__i = 0; __i < (u32)_HexDataLen; __i++) { \
+ printk("%02X%s", ptr[__i], (((__i + 1) % 4) == 0)?" ":" ");\
+ if (((__i + 1) % 16) == 0) \
+ printk("\n"); \
+ } \
+ printk("\n"); \
+}
+/* Added by Annie, 2005-11-22. */
+#define MAX_STR_LEN 64
+/* I want to see ASCII 33 to 126 only. Otherwise, I print '?'. */
+#define PRINTABLE(_ch) (_ch >= ' ' && _ch <= '~')
+#define RT_PRINT_STR(_Comp, _Level, _TitleString, _Ptr, _Len) \
+ { \
+ u32 __i; \
+ u8 buffer[MAX_STR_LEN]; \
+ u32 length = (_Len < MAX_STR_LEN) ? _Len : (MAX_STR_LEN-1);\
+ memset(buffer, 0, MAX_STR_LEN); \
+ memcpy(buffer, (u8 *)_Ptr, length); \
+ for (__i = 0; __i < length; __i++) { \
+ if (!PRINTABLE(buffer[__i])) \
+ buffer[__i] = '?'; \
+ } \
+ buffer[length] = '\0'; \
+ printk(_TitleString); \
+ printk(": %d, <%s>\n", _Len, buffer); \
+ }
+
+#define DCMD_Printf(...)
+#define RT_ASSERT(...)
+
+#define rsprintf snprintf
+
+#define GetDefaultAdapter(padapter) padapter
+
+#define PlatformZeroMemory(ptr, sz) memset(ptr, 0, sz)
+
+#define PlatformProcessHCICommands(...)
+#define PlatformTxBTQueuedPackets(...)
+#define PlatformIndicateBTACLData(...) (RT_STATUS_SUCCESS)
+#define PlatformAcquireSpinLock(padapter, type)
+#define PlatformReleaseSpinLock(padapter, type)
+
+#define GET_UNDECORATED_AVERAGE_RSSI(padapter) \
+ (GET_HAL_DATA(padapter)->dmpriv.EntryMinUndecoratedSmoothedPWDB)
+#define RT_RF_CHANGE_SOURCE u32
+
+enum {
+ RT_JOIN_INFRA = 1,
+ RT_JOIN_IBSS = 2,
+ RT_START_IBSS = 3,
+ RT_NO_ACTION = 4,
+};
+
+/* power saving */
+
+/* ===== Below this line is sync from SD7 driver COMMOM/BT.c ===== */
+
+static u8 BT_Operation(struct rtw_adapter *padapter)
+{
+ struct bt_30info *pBTInfo = GET_BT_INFO(padapter);
+ struct bt_mgnt *pBtMgnt = &pBTInfo->BtMgnt;
+
+ if (pBtMgnt->BtOperationOn)
+ return true;
+ else
+ return false;
+}
+
+static u8 BT_IsLegalChannel(struct rtw_adapter *padapter, u8 channel)
+{
+ struct rt_channel_info *pChanneList = NULL;
+ u8 channelLen, i;
+
+ pChanneList = padapter->mlmeextpriv.channel_set;
+ channelLen = padapter->mlmeextpriv.max_chan_nums;
+
+ for (i = 0; i < channelLen; i++) {
+ RTPRINT(FIOCTL, IOCTL_STATE,
+ ("Check if chnl(%d) in channel plan contains bt target chnl(%d) for BT connection\n",
+ pChanneList[i].ChannelNum, channel));
+ if ((channel == pChanneList[i].ChannelNum) ||
+ (channel == pChanneList[i].ChannelNum + 2))
+ return channel;
+ }
+ return 0;
+}
+
+void BT_SignalCompensation(struct rtw_adapter *padapter, u8 *rssi_wifi, u8 *rssi_bt)
+{
+ BTDM_SignalCompensation(padapter, rssi_wifi, rssi_bt);
+}
+
+void rtl8723a_BT_wifiscan_notify(struct rtw_adapter *padapter, u8 scanType)
+{
+ BTHCI_WifiScanNotify(padapter, scanType);
+ BTDM_CheckAntSelMode(padapter);
+ BTDM_WifiScanNotify(padapter, scanType);
+}
+
+void rtl8723a_BT_wifiassociate_notify(struct rtw_adapter *padapter, u8 action)
+{
+ /* action : */
+ /* true = associate start */
+ /* false = associate finished */
+ if (action)
+ BTDM_CheckAntSelMode(padapter);
+
+ BTDM_WifiAssociateNotify(padapter, action);
+}
+
+void BT_HaltProcess(struct rtw_adapter *padapter)
+{
+ BTDM_ForHalt(padapter);
+}
+
+/* ===== End of sync from SD7 driver COMMOM/BT.c ===== */
+
+#define i64fmt "ll"
+#define UINT64_C(v) (v)
+
+#define FillOctetString(_os, _octet, _len) \
+ (_os).Octet = (u8 *)(_octet); \
+ (_os).Length = (_len);
+
+static enum rt_status PlatformIndicateBTEvent(
+ struct rtw_adapter *padapter,
+ void *pEvntData,
+ u32 dataLen
+ )
+{
+ enum rt_status rt_status = RT_STATUS_FAILURE;
+
+ RTPRINT(FIOCTL, IOCTL_BT_EVENT_DETAIL, ("BT event start, %d bytes data to Transferred!!\n", dataLen));
+ RTPRINT_DATA(FIOCTL, IOCTL_BT_EVENT_DETAIL, "To transfer Hex Data :\n",
+ pEvntData, dataLen);
+
+ BT_EventParse(padapter, pEvntData, dataLen);
+
+ printk(KERN_WARNING "%s: Linux has no way to report BT event!!\n", __func__);
+
+ RTPRINT(FIOCTL, IOCTL_BT_EVENT_DETAIL, ("BT event end, %s\n",
+ (rt_status == RT_STATUS_SUCCESS) ? "SUCCESS" : "FAIL"));
+
+ return rt_status;
+}
+
+/* ===== Below this line is sync from SD7 driver COMMOM/bt_hci.c ===== */
+
+static u8 bthci_GetLocalChannel(struct rtw_adapter *padapter)
+{
+ return padapter->mlmeextpriv.cur_channel;
+}
+
+static u8 bthci_GetCurrentEntryNum(struct rtw_adapter *padapter, u8 PhyHandle)
+{
+ struct bt_30info *pBTInfo = GET_BT_INFO(padapter);
+ u8 i;
+
+ for (i = 0; i < MAX_BT_ASOC_ENTRY_NUM; i++) {
+ if ((pBTInfo->BtAsocEntry[i].bUsed) &&
+ (pBTInfo->BtAsocEntry[i].PhyLinkCmdData.BtPhyLinkhandle == PhyHandle))
+ return i;
+ }
+
+ return 0xFF;
+}
+
+static void bthci_DecideBTChannel(struct rtw_adapter *padapter, u8 EntryNum)
+{
+/*PMGNT_INFO pMgntInfo = &padapter->MgntInfo; */
+ struct mlme_priv *pmlmepriv;
+ struct bt_30info *pBTInfo;
+ struct bt_mgnt *pBtMgnt;
+ struct bt_hci_info *pBtHciInfo;
+ struct chnl_txpower_triple *pTriple_subband = NULL;
+ struct common_triple *pTriple;
+ u8 i, j, localchnl, firstRemoteLegalChnlInTriplet = 0;
+ u8 regulatory_skipLen = 0;
+ u8 subbandTripletCnt = 0;
+
+ pmlmepriv = &padapter->mlmepriv;
+ pBTInfo = GET_BT_INFO(padapter);
+ pBtMgnt = &pBTInfo->BtMgnt;
+ pBtHciInfo = &pBTInfo->BtHciInfo;
+
+ pBtMgnt->CheckChnlIsSuit = true;
+ localchnl = bthci_GetLocalChannel(padapter);
+
+ pTriple = (struct common_triple *)
+ &pBtHciInfo->BTPreChnllist[COUNTRY_STR_LEN];
+
+ /* contains country string, len is 3 */
+ for (i = 0; i < (pBtHciInfo->BtPreChnlListLen-COUNTRY_STR_LEN); i += 3, pTriple++) {
+ /* */
+ /* check every triplet, an triplet may be */
+ /* regulatory extension identifier or sub-band triplet */
+ /* */
+ if (pTriple->byte_1st == 0xc9) {
+ /* Regulatory Extension Identifier, skip it */
+ RTPRINT(FIOCTL, (IOCTL_BT_HCICMD|IOCTL_BT_LOGO),
+ ("Find Regulatory ID, regulatory class = %d\n", pTriple->byte_2nd));
+ regulatory_skipLen += 3;
+ pTriple_subband = NULL;
+ continue;
+ } else { /* Sub-band triplet */
+ RTPRINT(FIOCTL, (IOCTL_BT_HCICMD|IOCTL_BT_LOGO), ("Find Sub-band triplet \n"));
+ subbandTripletCnt++;
+ pTriple_subband = (struct chnl_txpower_triple *)pTriple;
+ /* if remote first legal channel not found, then find first remote channel */
+ /* and it's legal for our channel plan. */
+
+ /* search the sub-band triplet and find if remote channel is legal to our channel plan. */
+ for (j = pTriple_subband->FirstChnl; j < (pTriple_subband->FirstChnl+pTriple_subband->NumChnls); j++) {
+ RTPRINT(FIOCTL, (IOCTL_BT_HCICMD|IOCTL_BT_LOGO), (" Check if chnl(%d) is legal\n", j));
+ if (BT_IsLegalChannel(padapter, j)) {
+ /* remote channel is legal for our channel plan. */
+ firstRemoteLegalChnlInTriplet = j;
+ RTPRINT(FIOCTL, (IOCTL_BT_HCICMD|IOCTL_BT_LOGO),
+ ("Find first remote legal channel : %d\n",
+ firstRemoteLegalChnlInTriplet));
+
+ /* If we find a remote legal channel in the sub-band triplet */
+ /* and only BT connection is established(local not connect to any AP or IBSS), */
+ /* then we just switch channel to remote channel. */
+ if (!(check_fwstate(pmlmepriv, WIFI_ASOC_STATE|WIFI_ADHOC_STATE|WIFI_AP_STATE) ||
+ BTHCI_HsConnectionEstablished(padapter))) {
+ pBtMgnt->BTChannel = firstRemoteLegalChnlInTriplet;
+ RTPRINT(FIOCTL, (IOCTL_BT_HCICMD|IOCTL_BT_LOGO), ("Remote legal channel (%d) is selected, Local not connect to any!!\n", pBtMgnt->BTChannel));
+ return;
+ } else {
+ if ((localchnl >= firstRemoteLegalChnlInTriplet) &&
+ (localchnl < (pTriple_subband->FirstChnl+pTriple_subband->NumChnls))) {
+ pBtMgnt->BTChannel = localchnl;
+ RTPRINT(FIOCTL, (IOCTL_BT_HCICMD|IOCTL_BT_LOGO), ("Local channel (%d) is selected, wifi or BT connection exists\n", pBtMgnt->BTChannel));
+ return;
+ }
+ }
+ break;
+ }
+ }
+ }
+ }
+
+ if (subbandTripletCnt) {
+ /* if any preferred channel triplet exists */
+ RTPRINT(FIOCTL, (IOCTL_BT_HCICMD|IOCTL_BT_LOGO), ("There are %d sub band triplet exists, ", subbandTripletCnt));
+ if (firstRemoteLegalChnlInTriplet == 0) {
+ /* no legal channel is found, reject the connection. */
+ RTPRINT(FIOCTL, (IOCTL_BT_HCICMD|IOCTL_BT_LOGO), ("no legal channel is found!!\n"));
+ } else {
+ /* Remote Legal channel is found but not match to local */
+ /* wifi connection exists), so reject the connection. */
+ RTPRINT(FIOCTL, (IOCTL_BT_HCICMD|IOCTL_BT_LOGO),
+ ("Remote Legal channel is found but not match to local(wifi connection exists)!!\n"));
+ }
+ pBtMgnt->CheckChnlIsSuit = false;
+ } else {
+ /* There are not any preferred channel triplet exists */
+ /* Use current legal channel as the bt channel. */
+ RTPRINT(FIOCTL, (IOCTL_BT_HCICMD|IOCTL_BT_LOGO), ("No sub band triplet exists!!\n"));
+ }
+ pBtMgnt->BTChannel = localchnl;
+ RTPRINT(FIOCTL, (IOCTL_BT_HCICMD|IOCTL_BT_LOGO), ("Local channel (%d) is selected!!\n", pBtMgnt->BTChannel));
+}
+
+/* Success:return true */
+/* Fail:return false */
+static u8 bthci_GetAssocInfo(struct rtw_adapter *padapter, u8 EntryNum)
+{
+ struct bt_30info *pBTInfo;
+ struct bt_hci_info *pBtHciInfo;
+ u8 tempBuf[256];
+ u8 i = 0;
+ u8 BaseMemoryShift = 0;
+ u16 TotalLen = 0;
+ struct amp_assoc_structure *pAmpAsoc;
+
+ RTPRINT(FIOCTL, IOCTL_BT_HCICMD, ("GetAssocInfo start\n"));
+ pBTInfo = GET_BT_INFO(padapter);
+ pBtHciInfo = &pBTInfo->BtHciInfo;
+
+ if (pBTInfo->BtAsocEntry[EntryNum].AmpAsocCmdData.LenSoFar == 0) {
+ if (pBTInfo->BtAsocEntry[EntryNum].AmpAsocCmdData.AMPAssocRemLen < (MAX_AMP_ASSOC_FRAG_LEN))
+ TotalLen = pBTInfo->BtAsocEntry[EntryNum].AmpAsocCmdData.AMPAssocRemLen;
+ else if (pBTInfo->BtAsocEntry[EntryNum].AmpAsocCmdData.AMPAssocRemLen == (MAX_AMP_ASSOC_FRAG_LEN))
+ TotalLen = MAX_AMP_ASSOC_FRAG_LEN;
+ } else if (pBTInfo->BtAsocEntry[EntryNum].AmpAsocCmdData.LenSoFar > 0)
+ TotalLen = pBTInfo->BtAsocEntry[EntryNum].AmpAsocCmdData.LenSoFar;
+
+ while ((pBTInfo->BtAsocEntry[EntryNum].AmpAsocCmdData.LenSoFar >= BaseMemoryShift) || TotalLen > BaseMemoryShift) {
+ RTPRINT(FIOCTL, IOCTL_BT_HCICMD_DETAIL, ("GetAssocInfo, TotalLen =%d, BaseMemoryShift =%d\n", TotalLen, BaseMemoryShift));
+ memcpy(tempBuf,
+ (u8 *)pBTInfo->BtAsocEntry[EntryNum].AmpAsocCmdData.AMPAssocfragment+BaseMemoryShift,
+ TotalLen-BaseMemoryShift);
+ RTPRINT_DATA(FIOCTL, IOCTL_BT_HCICMD_DETAIL, "GetAssocInfo :\n",
+ tempBuf, TotalLen-BaseMemoryShift);
+
+ pAmpAsoc = (struct amp_assoc_structure *)tempBuf;
+ le16_to_cpus(&pAmpAsoc->Length);
+ BaseMemoryShift += 3 + pAmpAsoc->Length;
+
+ RTPRINT(FIOCTL, IOCTL_BT_HCICMD, ("TypeID = 0x%x, ", pAmpAsoc->TypeID));
+ RTPRINT_DATA(FIOCTL, IOCTL_BT_HCICMD, "Hex Data: \n", pAmpAsoc->Data, pAmpAsoc->Length);
+ switch (pAmpAsoc->TypeID) {
+ case AMP_MAC_ADDR:
+ RTPRINT(FIOCTL, IOCTL_BT_HCICMD, ("==> AMP_MAC_ADDR\n"));
+ if (pAmpAsoc->Length > 6)
+ return false;
+ memcpy(pBTInfo->BtAsocEntry[EntryNum].BTRemoteMACAddr, pAmpAsoc->Data, 6);
+ RTPRINT_ADDR(FIOCTL, IOCTL_BT_HCICMD, ("Remote Mac address \n"), pBTInfo->BtAsocEntry[EntryNum].BTRemoteMACAddr);
+ break;
+ case AMP_PREFERRED_CHANNEL_LIST:
+ RTPRINT(FIOCTL, IOCTL_BT_HCICMD, ("==> AMP_PREFERRED_CHANNEL_LIST\n"));
+ pBtHciInfo->BtPreChnlListLen = pAmpAsoc->Length;
+ memcpy(pBtHciInfo->BTPreChnllist,
+ pAmpAsoc->Data,
+ pBtHciInfo->BtPreChnlListLen);
+ RTPRINT_DATA(FIOCTL, IOCTL_BT_HCICMD, "Preferred channel list : \n", pBtHciInfo->BTPreChnllist, pBtHciInfo->BtPreChnlListLen);
+ bthci_DecideBTChannel(padapter, EntryNum);
+ break;
+ case AMP_CONNECTED_CHANNEL:
+ RTPRINT(FIOCTL, IOCTL_BT_HCICMD, ("==> AMP_CONNECTED_CHANNEL\n"));
+ pBtHciInfo->BTConnectChnlListLen = pAmpAsoc->Length;
+ memcpy(pBtHciInfo->BTConnectChnllist,
+ pAmpAsoc->Data,
+ pBtHciInfo->BTConnectChnlListLen);
+ break;
+ case AMP_80211_PAL_CAP_LIST:
+ RTPRINT(FIOCTL, IOCTL_BT_HCICMD, ("==> AMP_80211_PAL_CAP_LIST\n"));
+ pBTInfo->BtAsocEntry[EntryNum].BTCapability = *(u32 *)(pAmpAsoc->Data);
+ if (pBTInfo->BtAsocEntry[EntryNum].BTCapability & 0x00000001) {
+ /* TODO: */
+
+ /* Signifies PAL capable of utilizing received activity reports. */
+ }
+ if (pBTInfo->BtAsocEntry[EntryNum].BTCapability & 0x00000002) {
+ /* TODO: */
+ /* Signifies PAL is capable of utilizing scheduling information received in an activity reports. */
+ }
+ break;
+ case AMP_80211_PAL_VISION:
+ pBtHciInfo->BTPalVersion = *(u8 *)(pAmpAsoc->Data);
+ pBtHciInfo->BTPalCompanyID = *(u16 *)(((u8 *)(pAmpAsoc->Data))+1);
+ pBtHciInfo->BTPalsubversion = *(u16 *)(((u8 *)(pAmpAsoc->Data))+3);
+ RTPRINT(FIOCTL, (IOCTL_BT_HCICMD|IOCTL_BT_LOGO), ("==> AMP_80211_PAL_VISION PalVersion 0x%x, PalCompanyID 0x%x, Palsubversion 0x%x\n",
+ pBtHciInfo->BTPalVersion,
+ pBtHciInfo->BTPalCompanyID,
+ pBtHciInfo->BTPalsubversion));
+ break;
+ default:
+ RTPRINT(FIOCTL, IOCTL_BT_HCICMD, ("==> Unsupport TypeID !!\n"));
+ break;
+ }
+ i++;
+ }
+ RTPRINT(FIOCTL, IOCTL_BT_HCICMD, ("GetAssocInfo end\n"));
+
+ return true;
+}
+
+static u8 bthci_AddEntry(struct rtw_adapter *padapter)
+{
+ struct bt_30info *pBTInfo;
+ struct bt_mgnt *pBtMgnt;
+ u8 i;
+
+ pBTInfo = GET_BT_INFO(padapter);
+ pBtMgnt = &pBTInfo->BtMgnt;
+
+ for (i = 0; i < MAX_BT_ASOC_ENTRY_NUM; i++) {
+ if (pBTInfo->BtAsocEntry[i].bUsed == false) {
+ pBTInfo->BtAsocEntry[i].bUsed = true;
+ pBtMgnt->CurrentConnectEntryNum = i;
+ break;
+ }
+ }
+
+ if (i == MAX_BT_ASOC_ENTRY_NUM) {
+ RTPRINT(FIOCTL, IOCTL_STATE, ("bthci_AddEntry(), Add entry fail!!\n"));
+ return false;
+ }
+ return true;
+}
+
+static u8 bthci_DiscardTxPackets(struct rtw_adapter *padapter, u16 LLH)
+{
+ return false;
+}
+
+static u8
+bthci_CheckLogLinkBehavior(
+ struct rtw_adapter *padapter,
+ struct hci_flow_spec TxFlowSpec
+ )
+{
+ u8 ID = TxFlowSpec.Identifier;
+ u8 ServiceType = TxFlowSpec.ServiceType;
+ u16 MaxSDUSize = TxFlowSpec.MaximumSDUSize;
+ u32 SDUInterArrivatime = TxFlowSpec.SDUInterArrivalTime;
+ u8 match = false;
+
+ switch (ID) {
+ case 1:
+ if (ServiceType == BT_LL_BE) {
+ match = true;
+ RTPRINT(FIOCTL, (IOCTL_BT_EVENT|IOCTL_BT_LOGO), ("Logical Link Type = TX best effort flowspec\n"));
+ } else if ((ServiceType == BT_LL_GU) && (MaxSDUSize == 0xffff)) {
+ match = true;
+ RTPRINT(FIOCTL, (IOCTL_BT_EVENT|IOCTL_BT_LOGO), ("Logical Link Type = RX guaranteed latency flowspec\n"));
+ } else if ((ServiceType == BT_LL_GU) && (MaxSDUSize == 2500)) {
+ RTPRINT(FIOCTL, (IOCTL_BT_EVENT|IOCTL_BT_LOGO), ("Logical Link Type = RX guaranteed Large latency flowspec\n"));
+ }
+ break;
+ case 2:
+ if (ServiceType == BT_LL_BE) {
+ match = true;
+ RTPRINT(FIOCTL, (IOCTL_BT_EVENT|IOCTL_BT_LOGO), ("Logical Link Type = RX best effort flowspec\n"));
+
+ }
+ break;
+ case 3:
+ if ((ServiceType == BT_LL_GU) && (MaxSDUSize == 1492)) {
+ match = true;
+ RTPRINT(FIOCTL, (IOCTL_BT_EVENT|IOCTL_BT_LOGO), ("Logical Link Type = TX guaranteed latency flowspec\n"));
+ } else if ((ServiceType == BT_LL_GU) && (MaxSDUSize == 2500)) {
+ RTPRINT(FIOCTL, (IOCTL_BT_EVENT|IOCTL_BT_LOGO), ("Logical Link Type = TX guaranteed Large latency flowspec\n"));
+ }
+ break;
+ case 4:
+ if (ServiceType == BT_LL_BE) {
+ if ((SDUInterArrivatime == 0xffffffff) && (ServiceType == BT_LL_BE) && (MaxSDUSize == 1492)) {
+ match = true;
+ RTPRINT(FIOCTL, (IOCTL_BT_EVENT|IOCTL_BT_LOGO), ("Logical Link Type = TX/RX aggregated best effort flowspec\n"));
+ }
+ } else if (ServiceType == BT_LL_GU) {
+ if (SDUInterArrivatime == 100) {
+ match = true;
+ RTPRINT(FIOCTL, (IOCTL_BT_EVENT|IOCTL_BT_LOGO), ("Logical Link Type = TX/RX guaranteed bandwidth flowspec\n"));
+ }
+ }
+ break;
+ default:
+ RTPRINT(FIOCTL, (IOCTL_BT_EVENT|IOCTL_BT_LOGO), ("Logical Link Type = Unknow Type !!!!!!!!\n"));
+ break;
+ }
+
+ RTPRINT(FIOCTL, (IOCTL_BT_HCICMD|IOCTL_BT_LOGO),
+ ("ID = 0x%x, ServiceType = 0x%x, MaximumSDUSize = 0x%x, SDUInterArrivalTime = 0x%x, AccessLatency = 0x%x, FlushTimeout = 0x%x\n",
+ TxFlowSpec.Identifier, TxFlowSpec.ServiceType, MaxSDUSize,
+ SDUInterArrivatime, TxFlowSpec.AccessLatency, TxFlowSpec.FlushTimeout));
+ return match;
+}
+
+static u16 bthci_AssocMACAddr(struct rtw_adapter *padapter, void *pbuf)
+{
+ struct amp_assoc_structure *pAssoStrc = (struct amp_assoc_structure *)pbuf;
+ pAssoStrc->TypeID = AMP_MAC_ADDR;
+ pAssoStrc->Length = 0x06;
+ memcpy(&pAssoStrc->Data[0], padapter->eeprompriv.mac_addr, 6);
+ RTPRINT_DATA(FIOCTL, (IOCTL_BT_HCICMD|IOCTL_BT_HCICMD_DETAIL|IOCTL_BT_LOGO),
+ ("AssocMACAddr : \n"), pAssoStrc, pAssoStrc->Length+3);
+
+ return pAssoStrc->Length + 3;
+}
+
+static u16
+bthci_PALCapabilities(
+ struct rtw_adapter *padapter,
+ void *pbuf
+ )
+{
+ struct amp_assoc_structure *pAssoStrc = (struct amp_assoc_structure *)pbuf;
+
+ pAssoStrc->TypeID = AMP_80211_PAL_CAP_LIST;
+ pAssoStrc->Length = 0x04;
+
+ pAssoStrc->Data[0] = 0x00;
+ pAssoStrc->Data[1] = 0x00;
+
+ RTPRINT_DATA(FIOCTL, IOCTL_BT_HCICMD_DETAIL, ("PALCapabilities:\n"), pAssoStrc, pAssoStrc->Length+3);
+ RTPRINT(FIOCTL, IOCTL_BT_LOGO, ("PALCapabilities \n"));
+
+ RTPRINT(FIOCTL, IOCTL_BT_LOGO, (" TypeID = 0x%x,\n Length = 0x%x,\n Content = 0x0000\n",
+ pAssoStrc->TypeID,
+ pAssoStrc->Length));
+
+ return pAssoStrc->Length + 3;
+}
+
+static u16 bthci_AssocPreferredChannelList(struct rtw_adapter *padapter,
+ void *pbuf, u8 EntryNum)
+{
+ struct bt_30info *pBTInfo;
+ struct amp_assoc_structure *pAssoStrc;
+ struct amp_pref_chnl_regulatory *pReg;
+ struct chnl_txpower_triple *pTriple;
+ char ctrString[3] = {'X', 'X', 'X'};
+ u32 len = 0;
+ u8 preferredChnl;
+
+ pBTInfo = GET_BT_INFO(padapter);
+ pAssoStrc = (struct amp_assoc_structure *)pbuf;
+ pReg = (struct amp_pref_chnl_regulatory *)&pAssoStrc->Data[3];
+
+ preferredChnl = bthci_GetLocalChannel(padapter);
+ pAssoStrc->TypeID = AMP_PREFERRED_CHANNEL_LIST;
+
+ /* locale unknown */
+ memcpy(&pAssoStrc->Data[0], &ctrString[0], 3);
+ pReg->reXId = 201;
+ pReg->regulatoryClass = 254;
+ pReg->coverageClass = 0;
+ len += 6;
+ RTPRINT(FIOCTL, (IOCTL_BT_HCICMD | IOCTL_BT_LOGO), ("PREFERRED_CHNL_LIST\n"));
+ RTPRINT(FIOCTL, (IOCTL_BT_HCICMD | IOCTL_BT_LOGO), ("XXX, 201, 254, 0\n"));
+ /* at the following, chnl 1~11 should be contained */
+ pTriple = (struct chnl_txpower_triple *)&pAssoStrc->Data[len];
+
+ /* (1) if any wifi or bt HS connection exists */
+ if ((pBTInfo->BtAsocEntry[EntryNum].AMPRole == AMP_BTAP_CREATOR) ||
+ (check_fwstate(&padapter->mlmepriv, WIFI_ASOC_STATE |
+ WIFI_ADHOC_STATE | WIFI_ADHOC_MASTER_STATE |
+ WIFI_AP_STATE)) ||
+ BTHCI_HsConnectionEstablished(padapter)) {
+ pTriple->FirstChnl = preferredChnl;
+ pTriple->NumChnls = 1;
+ pTriple->MaxTxPowerInDbm = 20;
+ len += 3;
+ RTPRINT(FIOCTL, (IOCTL_BT_HCICMD | IOCTL_BT_LOGO), ("First Channel = %d, Channel Num = %d, MaxDbm = %d\n",
+ pTriple->FirstChnl,
+ pTriple->NumChnls,
+ pTriple->MaxTxPowerInDbm));
+ }
+
+ pAssoStrc->Length = (u16)len;
+ RTPRINT_DATA(FIOCTL, IOCTL_BT_HCICMD, ("AssocPreferredChannelList : \n"), pAssoStrc, pAssoStrc->Length+3);
+
+ return pAssoStrc->Length + 3;
+}
+
+static u16 bthci_AssocPALVer(struct rtw_adapter *padapter, void *pbuf)
+{
+ struct amp_assoc_structure *pAssoStrc = (struct amp_assoc_structure *)pbuf;
+ u8 *pu1Tmp;
+ u16 *pu2Tmp;
+
+ pAssoStrc->TypeID = AMP_80211_PAL_VISION;
+ pAssoStrc->Length = 0x5;
+ pu1Tmp = &pAssoStrc->Data[0];
+ *pu1Tmp = 0x1; /* PAL Version */
+ pu2Tmp = (u16 *)&pAssoStrc->Data[1];
+ *pu2Tmp = 0x5D; /* SIG Company identifier of 802.11 PAL vendor */
+ pu2Tmp = (u16 *)&pAssoStrc->Data[3];
+ *pu2Tmp = 0x1; /* PAL Sub-version specifier */
+
+ RTPRINT_DATA(FIOCTL, IOCTL_BT_HCICMD_DETAIL, ("AssocPALVer : \n"), pAssoStrc, pAssoStrc->Length+3);
+ RTPRINT(FIOCTL, IOCTL_BT_LOGO, ("AssocPALVer \n"));
+
+ RTPRINT(FIOCTL, IOCTL_BT_LOGO, (" TypeID = 0x%x,\n Length = 0x%x,\n PAL Version = 0x01,\n PAL vendor = 0x01,\n PAL Sub-version specifier = 0x01\n",
+ pAssoStrc->TypeID,
+ pAssoStrc->Length));
+ return pAssoStrc->Length + 3;
+}
+
+static u8 bthci_CheckRfStateBeforeConnect(struct rtw_adapter *padapter)
+{
+ struct bt_30info *pBTInfo;
+ enum rt_rf_power_state RfState;
+
+ pBTInfo = GET_BT_INFO(padapter);
+
+ RfState = padapter->pwrctrlpriv.rf_pwrstate;
+
+ if (RfState != rf_on) {
+ mod_timer(&pBTInfo->BTPsDisableTimer,
+ jiffies + msecs_to_jiffies(50));
+ return false;
+ }
+ return true;
+}
+
+static void bthci_ResponderStartToScan(struct rtw_adapter *padapter)
+{
+}
+
+static u8 bthci_PhyLinkConnectionInProgress(struct rtw_adapter *padapter, u8 PhyLinkHandle)
+{
+ struct bt_30info *pBTInfo;
+ struct bt_mgnt *pBtMgnt;
+
+ pBTInfo = GET_BT_INFO(padapter);
+ pBtMgnt = &pBTInfo->BtMgnt;
+
+ if (pBtMgnt->bPhyLinkInProgress &&
+ (pBtMgnt->BtCurrentPhyLinkhandle == PhyLinkHandle))
+ return true;
+ return false;
+}
+
+static void bthci_ResetFlowSpec(struct rtw_adapter *padapter, u8 EntryNum, u8 index)
+{
+ struct bt_30info *pBTinfo;
+
+ pBTinfo = GET_BT_INFO(padapter);
+
+ pBTinfo->BtAsocEntry[EntryNum].LogLinkCmdData[index].BtLogLinkhandle = 0;
+ pBTinfo->BtAsocEntry[EntryNum].LogLinkCmdData[index].BtPhyLinkhandle = 0;
+ pBTinfo->BtAsocEntry[EntryNum].LogLinkCmdData[index].bLLCompleteEventIsSet = false;
+ pBTinfo->BtAsocEntry[EntryNum].LogLinkCmdData[index].bLLCancelCMDIsSetandComplete = false;
+ pBTinfo->BtAsocEntry[EntryNum].LogLinkCmdData[index].BtTxFlowSpecID = 0;
+ pBTinfo->BtAsocEntry[EntryNum].LogLinkCmdData[index].TxPacketCount = 0;
+
+ pBTinfo->BtAsocEntry[EntryNum].LogLinkCmdData[index].Tx_Flow_Spec.Identifier = 0x01;
+ pBTinfo->BtAsocEntry[EntryNum].LogLinkCmdData[index].Tx_Flow_Spec.ServiceType = SERVICE_BEST_EFFORT;
+ pBTinfo->BtAsocEntry[EntryNum].LogLinkCmdData[index].Tx_Flow_Spec.MaximumSDUSize = 0xffff;
+ pBTinfo->BtAsocEntry[EntryNum].LogLinkCmdData[index].Tx_Flow_Spec.SDUInterArrivalTime = 0xffffffff;
+ pBTinfo->BtAsocEntry[EntryNum].LogLinkCmdData[index].Tx_Flow_Spec.AccessLatency = 0xffffffff;
+ pBTinfo->BtAsocEntry[EntryNum].LogLinkCmdData[index].Tx_Flow_Spec.FlushTimeout = 0xffffffff;
+
+ pBTinfo->BtAsocEntry[EntryNum].LogLinkCmdData[index].Rx_Flow_Spec.Identifier = 0x01;
+ pBTinfo->BtAsocEntry[EntryNum].LogLinkCmdData[index].Rx_Flow_Spec.ServiceType = SERVICE_BEST_EFFORT;
+ pBTinfo->BtAsocEntry[EntryNum].LogLinkCmdData[index].Rx_Flow_Spec.MaximumSDUSize = 0xffff;
+ pBTinfo->BtAsocEntry[EntryNum].LogLinkCmdData[index].Rx_Flow_Spec.SDUInterArrivalTime = 0xffffffff;
+ pBTinfo->BtAsocEntry[EntryNum].LogLinkCmdData[index].Rx_Flow_Spec.AccessLatency = 0xffffffff;
+ pBTinfo->BtAsocEntry[EntryNum].LogLinkCmdData[index].Rx_Flow_Spec.FlushTimeout = 0xffffffff;
+}
+
+static void bthci_ResetEntry(struct rtw_adapter *padapter, u8 EntryNum)
+{
+ struct bt_30info *pBTinfo;
+ struct bt_mgnt *pBtMgnt;
+ u8 j;
+
+ pBTinfo = GET_BT_INFO(padapter);
+ pBtMgnt = &pBTinfo->BtMgnt;
+
+ pBTinfo->BtAsocEntry[EntryNum].bUsed = false;
+ pBTinfo->BtAsocEntry[EntryNum].BtCurrentState = HCI_STATE_DISCONNECTED;
+ pBTinfo->BtAsocEntry[EntryNum].BtNextState = HCI_STATE_DISCONNECTED;
+
+ pBTinfo->BtAsocEntry[EntryNum].AmpAsocCmdData.AMPAssocRemLen = 0;
+ pBTinfo->BtAsocEntry[EntryNum].AmpAsocCmdData.BtPhyLinkhandle = 0;
+ if (pBTinfo->BtAsocEntry[EntryNum].AmpAsocCmdData.AMPAssocfragment != NULL)
+ memset(pBTinfo->BtAsocEntry[EntryNum].AmpAsocCmdData.AMPAssocfragment, 0, TOTAL_ALLOCIATE_ASSOC_LEN);
+ pBTinfo->BtAsocEntry[EntryNum].AmpAsocCmdData.LenSoFar = 0;
+
+ pBTinfo->BtAsocEntry[EntryNum].PhyLinkCmdData.BtAMPKeyType = 0;
+ pBTinfo->BtAsocEntry[EntryNum].PhyLinkCmdData.BtPhyLinkhandle = 0;
+ memset(pBTinfo->BtAsocEntry[EntryNum].PhyLinkCmdData.BtAMPKey, 0,
+ pBTinfo->BtAsocEntry[EntryNum].PhyLinkCmdData.BtAMPKeyLen);
+ pBTinfo->BtAsocEntry[EntryNum].PhyLinkCmdData.BtAMPKeyLen = 0;
+
+ /* 0x640; 0.625ms*1600 = 1000ms, 0.625ms*16000 = 10000ms */
+ pBTinfo->BtAsocEntry[EntryNum].PhyLinkCmdData.LinkSuperversionTimeout = 0x3e80;
+
+ pBTinfo->BtAsocEntry[EntryNum].AMPRole = AMP_BTAP_NONE;
+
+ pBTinfo->BtAsocEntry[EntryNum].mAssoc = false;
+ pBTinfo->BtAsocEntry[EntryNum].b4waySuccess = false;
+
+ /* Reset BT WPA */
+ pBTinfo->BtAsocEntry[EntryNum].KeyReplayCounter = 0;
+ pBTinfo->BtAsocEntry[EntryNum].BTWPAAuthState = STATE_WPA_AUTH_UNINITIALIZED;
+
+ pBTinfo->BtAsocEntry[EntryNum].bSendSupervisionPacket = false;
+ pBTinfo->BtAsocEntry[EntryNum].NoRxPktCnt = 0;
+ pBTinfo->BtAsocEntry[EntryNum].ShortRangeMode = 0;
+ pBTinfo->BtAsocEntry[EntryNum].rxSuvpPktCnt = 0;
+
+ for (j = 0; j < MAX_LOGICAL_LINK_NUM; j++)
+ bthci_ResetFlowSpec(padapter, EntryNum, j);
+
+ pBtMgnt->BTAuthCount = 0;
+ pBtMgnt->BTAsocCount = 0;
+ pBtMgnt->BTCurrentConnectType = BT_DISCONNECT;
+ pBtMgnt->BTReceiveConnectPkt = BT_DISCONNECT;
+
+ HALBT_RemoveKey(padapter, EntryNum);
+}
+
+static void bthci_RemoveEntryByEntryNum(struct rtw_adapter *padapter, u8 EntryNum)
+{
+ struct bt_30info *pBTInfo = GET_BT_INFO(padapter);
+ struct bt_mgnt *pBtMgnt = &pBTInfo->BtMgnt;
+
+ bthci_ResetEntry(padapter, EntryNum);
+
+ if (pBtMgnt->CurrentBTConnectionCnt > 0)
+ pBtMgnt->CurrentBTConnectionCnt--;
+
+ RTPRINT(FIOCTL, IOCTL_STATE, ("[BT Flag], CurrentBTConnectionCnt = %d!!\n",
+ pBtMgnt->CurrentBTConnectionCnt));
+
+ if (pBtMgnt->CurrentBTConnectionCnt > 0) {
+ pBtMgnt->BtOperationOn = true;
+ } else {
+ pBtMgnt->BtOperationOn = false;
+ RTPRINT(FIOCTL, IOCTL_STATE, ("[BT Flag], Bt Operation OFF!!\n"));
+ }
+
+ if (!pBtMgnt->BtOperationOn) {
+ del_timer_sync(&pBTInfo->BTHCIDiscardAclDataTimer);
+ del_timer_sync(&pBTInfo->BTBeaconTimer);
+ pBtMgnt->bStartSendSupervisionPkt = false;
+ }
+}
+
+static u8
+bthci_CommandCompleteHeader(
+ u8 *pbuf,
+ u16 OGF,
+ u16 OCF,
+ enum hci_status status
+ )
+{
+ struct packet_irp_hcievent_data *PPacketIrpEvent = (struct packet_irp_hcievent_data *)pbuf;
+ u8 NumHCI_Comm = 0x1;
+
+ PPacketIrpEvent->EventCode = HCI_EVENT_COMMAND_COMPLETE;
+ PPacketIrpEvent->Data[0] = NumHCI_Comm; /* packet # */
+ PPacketIrpEvent->Data[1] = HCIOPCODELOW(OCF, OGF);
+ PPacketIrpEvent->Data[2] = HCIOPCODEHIGHT(OCF, OGF);
+
+ if (OGF == OGF_EXTENSION) {
+ if (OCF == HCI_SET_RSSI_VALUE) {
+ RTPRINT(FIOCTL, (IOCTL_BT_EVENT_PERIODICAL),
+ ("[BT event], CommandComplete, Num_HCI_Comm = 0x%x, Opcode = 0x%02x%02x, status = 0x%x, OGF = 0x%x, OCF = 0x%x\n",
+ NumHCI_Comm, (HCIOPCODEHIGHT(OCF, OGF)), (HCIOPCODELOW(OCF, OGF)), status, OGF, OCF));
+ } else {
+ RTPRINT(FIOCTL, (IOCTL_BT_HCICMD_EXT),
+ ("[BT event], CommandComplete, Num_HCI_Comm = 0x%x, Opcode = 0x%02x%02x, status = 0x%x, OGF = 0x%x, OCF = 0x%x\n",
+ NumHCI_Comm, (HCIOPCODEHIGHT(OCF, OGF)), (HCIOPCODELOW(OCF, OGF)), status, OGF, OCF));
+ }
+ } else {
+ RTPRINT(FIOCTL, (IOCTL_BT_EVENT|IOCTL_BT_LOGO),
+ ("[BT event], CommandComplete, Num_HCI_Comm = 0x%x, Opcode = 0x%02x%02x, status = 0x%x, OGF = 0x%x, OCF = 0x%x\n",
+ NumHCI_Comm, (HCIOPCODEHIGHT(OCF, OGF)), (HCIOPCODELOW(OCF, OGF)), status, OGF, OCF));
+ }
+ return 3;
+}
+
+static u8 bthci_ExtensionEventHeaderRtk(u8 *pbuf, u8 extensionEvent)
+{
+ struct packet_irp_hcievent_data *PPacketIrpEvent = (struct packet_irp_hcievent_data *)pbuf;
+ PPacketIrpEvent->EventCode = HCI_EVENT_EXTENSION_RTK;
+ PPacketIrpEvent->Data[0] = extensionEvent; /* extension event code */
+
+ return 1;
+}
+
+static enum rt_status
+bthci_IndicateEvent(
+ struct rtw_adapter *padapter,
+ void *pEvntData,
+ u32 dataLen
+ )
+{
+ enum rt_status rt_status;
+
+ rt_status = PlatformIndicateBTEvent(padapter, pEvntData, dataLen);
+
+ return rt_status;
+}
+
+static void
+bthci_EventWriteRemoteAmpAssoc(
+ struct rtw_adapter *padapter,
+ enum hci_status status,
+ u8 PLHandle
+ )
+{
+ u8 localBuf[TmpLocalBufSize] = "";
+ u8 *pRetPar;
+ u8 len = 0;
+ struct packet_irp_hcievent_data *PPacketIrpEvent;
+
+ PlatformZeroMemory(&localBuf[0], TmpLocalBufSize);
+ PPacketIrpEvent = (struct packet_irp_hcievent_data *)(&localBuf[0]);
+
+ len += bthci_CommandCompleteHeader(&localBuf[0],
+ OGF_STATUS_PARAMETERS,
+ HCI_WRITE_REMOTE_AMP_ASSOC,
+ status);
+ RTPRINT(FIOCTL, (IOCTL_BT_HCICMD|IOCTL_BT_LOGO), ("PhyLinkHandle = 0x%x, status = %d\n", PLHandle, status));
+ /* Return parameters starts from here */
+ pRetPar = &PPacketIrpEvent->Data[len];
+ pRetPar[0] = status; /* status */
+ pRetPar[1] = PLHandle;
+ len += 2;
+ PPacketIrpEvent->Length = len;
+
+ bthci_IndicateEvent(padapter, PPacketIrpEvent, len+2);
+}
+
+static void
+bthci_EventEnhancedFlushComplete(
+ struct rtw_adapter *padapter,
+ u16 LLH
+ )
+{
+ u8 localBuf[4] = "";
+ struct packet_irp_hcievent_data *PPacketIrpEvent;
+
+ RTPRINT(FIOCTL, IOCTL_BT_EVENT, ("EventEnhancedFlushComplete, LLH = 0x%x\n", LLH));
+
+ PPacketIrpEvent = (struct packet_irp_hcievent_data *)(&localBuf[0]);
+ PPacketIrpEvent->EventCode = HCI_EVENT_ENHANCED_FLUSH_COMPLETE;
+ PPacketIrpEvent->Length = 2;
+ /* Logical link handle */
+ PPacketIrpEvent->Data[0] = TWOBYTE_LOWBYTE(LLH);
+ PPacketIrpEvent->Data[1] = TWOBYTE_HIGHTBYTE(LLH);
+
+ bthci_IndicateEvent(padapter, PPacketIrpEvent, 4);
+}
+
+static void
+bthci_EventShortRangeModeChangeComplete(
+ struct rtw_adapter *padapter,
+ enum hci_status HciStatus,
+ u8 ShortRangeState,
+ u8 EntryNum
+ )
+{
+ struct bt_30info *pBTInfo = GET_BT_INFO(padapter);
+ struct bt_hci_info *pBtHciInfo = &pBTInfo->BtHciInfo;
+ u8 localBuf[5] = "";
+ struct packet_irp_hcievent_data *PPacketIrpEvent;
+
+ if (!(pBtHciInfo->BTEventMaskPage2 & EMP2_HCI_EVENT_SHORT_RANGE_MODE_CHANGE_COMPLETE)) {
+ RTPRINT(FIOCTL, IOCTL_BT_EVENT,
+ ("[BT event], Short Range Mode Change Complete, Ignore to send this event due to event mask page 2\n"));
+ return;
+ }
+ RTPRINT(FIOCTL, IOCTL_BT_EVENT, ("[BT event], Short Range Mode Change Complete, Status = %d\n , PLH = 0x%x\n, Short_Range_Mode_State = 0x%x\n",
+ HciStatus, pBTInfo->BtAsocEntry[EntryNum].PhyLinkCmdData.BtPhyLinkhandle, ShortRangeState));
+
+ PPacketIrpEvent = (struct packet_irp_hcievent_data *)(&localBuf[0]);
+ PPacketIrpEvent->EventCode = HCI_EVENT_SHORT_RANGE_MODE_CHANGE_COMPLETE;
+ PPacketIrpEvent->Length = 3;
+ PPacketIrpEvent->Data[0] = HciStatus;
+ PPacketIrpEvent->Data[1] = pBTInfo->BtAsocEntry[EntryNum].PhyLinkCmdData.BtPhyLinkhandle;
+ PPacketIrpEvent->Data[2] = ShortRangeState;
+ bthci_IndicateEvent(padapter, PPacketIrpEvent, 5);
+}
+
+static void bthci_EventSendFlowSpecModifyComplete(struct rtw_adapter *padapter,
+ enum hci_status HciStatus,
+ u16 logicHandle)
+{
+ u8 localBuf[5] = "";
+ struct packet_irp_hcievent_data *PPacketIrpEvent;
+ struct bt_30info *pBTInfo = GET_BT_INFO(padapter);
+ struct bt_hci_info *pBtHciInfo = &pBTInfo->BtHciInfo;
+
+ if (!(pBtHciInfo->BTEventMaskPage2 & EMP2_HCI_EVENT_FLOW_SPEC_MODIFY_COMPLETE)) {
+ RTPRINT(FIOCTL, (IOCTL_BT_EVENT|IOCTL_BT_LOGO),
+ ("[BT event], Flow Spec Modify Complete, Ignore to send this event due to event mask page 2\n"));
+ return;
+ }
+ RTPRINT(FIOCTL, (IOCTL_BT_EVENT|IOCTL_BT_LOGO),
+ ("[BT event], Flow Spec Modify Complete, status = 0x%x, LLH = 0x%x\n", HciStatus, logicHandle));
+ PPacketIrpEvent = (struct packet_irp_hcievent_data *)(&localBuf[0]);
+ PPacketIrpEvent->EventCode = HCI_EVENT_FLOW_SPEC_MODIFY_COMPLETE;
+ PPacketIrpEvent->Length = 3;
+
+ PPacketIrpEvent->Data[0] = HciStatus;
+ /* Logical link handle */
+ PPacketIrpEvent->Data[1] = TWOBYTE_LOWBYTE(logicHandle);
+ PPacketIrpEvent->Data[2] = TWOBYTE_HIGHTBYTE(logicHandle);
+
+ bthci_IndicateEvent(padapter, PPacketIrpEvent, 5);
+}
+
+static void
+bthci_EventExtWifiScanNotify(
+ struct rtw_adapter *padapter,
+ u8 scanType
+ )
+{
+ struct bt_30info *pBTInfo = GET_BT_INFO(padapter);
+ struct bt_mgnt *pBtMgnt = &pBTInfo->BtMgnt;
+ u8 len = 0;
+ u8 localBuf[7] = "";
+ u8 *pRetPar;
+ u8 *pu1Temp;
+ struct packet_irp_hcievent_data *PPacketIrpEvent;
+
+ if (!pBtMgnt->BtOperationOn)
+ return;
+
+ PPacketIrpEvent = (struct packet_irp_hcievent_data *)(&localBuf[0]);
+
+ len += bthci_ExtensionEventHeaderRtk(&localBuf[0], HCI_EVENT_EXT_WIFI_SCAN_NOTIFY);
+
+ /* Return parameters starts from here */
+ pRetPar = &PPacketIrpEvent->Data[len];
+ pu1Temp = (u8 *)&pRetPar[0];
+ *pu1Temp = scanType;
+ len += 1;
+
+ PPacketIrpEvent->Length = len;
+
+ if (bthci_IndicateEvent(padapter, PPacketIrpEvent, len+2) == RT_STATUS_SUCCESS) {
+ RTPRINT(FIOCTL, IOCTL_BT_EVENT, ("[BT event], Wifi scan notify, scan type = %d\n",
+ scanType));
+ }
+}
+
+static void
+bthci_EventAMPReceiverReport(
+ struct rtw_adapter *padapter,
+ u8 Reason
+ )
+{
+ struct bt_30info *pBTInfo = GET_BT_INFO(padapter);
+ struct bt_hci_info *pBtHciInfo = &pBTInfo->BtHciInfo;
+
+ if (pBtHciInfo->bTestNeedReport) {
+ u8 localBuf[20] = "";
+ u32 *pu4Temp;
+ u16 *pu2Temp;
+ struct packet_irp_hcievent_data *PPacketIrpEvent;
+
+ RTPRINT(FIOCTL, (IOCTL_BT_EVENT|IOCTL_BT_LOGO), (" HCI_EVENT_AMP_RECEIVER_REPORT\n"));
+ PPacketIrpEvent = (struct packet_irp_hcievent_data *)(&localBuf[0]);
+ PPacketIrpEvent->EventCode = HCI_EVENT_AMP_RECEIVER_REPORT;
+ PPacketIrpEvent->Length = 2;
+
+ PPacketIrpEvent->Data[0] = pBtHciInfo->TestCtrType;
+
+ PPacketIrpEvent->Data[1] = Reason;
+
+ pu4Temp = (u32 *)&PPacketIrpEvent->Data[2];
+ *pu4Temp = pBtHciInfo->TestEventType;
+
+ pu2Temp = (u16 *)&PPacketIrpEvent->Data[6];
+ *pu2Temp = pBtHciInfo->TestNumOfFrame;
+
+ pu2Temp = (u16 *)&PPacketIrpEvent->Data[8];
+ *pu2Temp = pBtHciInfo->TestNumOfErrFrame;
+
+ pu4Temp = (u32 *)&PPacketIrpEvent->Data[10];
+ *pu4Temp = pBtHciInfo->TestNumOfBits;
+
+ pu4Temp = (u32 *)&PPacketIrpEvent->Data[14];
+ *pu4Temp = pBtHciInfo->TestNumOfErrBits;
+
+ bthci_IndicateEvent(padapter, PPacketIrpEvent, 20);
+
+ /* Return to Idel state with RX and TX off. */
+
+ }
+
+ pBtHciInfo->TestNumOfFrame = 0x00;
+}
+
+static void
+bthci_EventChannelSelected(
+ struct rtw_adapter *padapter,
+ u8 EntryNum
+ )
+{
+ struct bt_30info *pBTInfo = GET_BT_INFO(padapter);
+ struct bt_hci_info *pBtHciInfo = &pBTInfo->BtHciInfo;
+ u8 localBuf[3] = "";
+ struct packet_irp_hcievent_data *PPacketIrpEvent;
+
+ if (!(pBtHciInfo->BTEventMaskPage2 & EMP2_HCI_EVENT_CHANNEL_SELECT)) {
+ RTPRINT(FIOCTL, IOCTL_BT_EVENT,
+ ("[BT event], Channel Selected, Ignore to send this event due to event mask page 2\n"));
+ return;
+ }
+
+ RTPRINT(FIOCTL, IOCTL_BT_EVENT|IOCTL_STATE,
+ ("[BT event], Channel Selected, PhyLinkHandle %d\n",
+ pBTInfo->BtAsocEntry[EntryNum].PhyLinkCmdData.BtPhyLinkhandle));
+
+ PPacketIrpEvent = (struct packet_irp_hcievent_data *)(&localBuf[0]);
+ PPacketIrpEvent->EventCode = HCI_EVENT_CHANNEL_SELECT;
+ PPacketIrpEvent->Length = 1;
+ PPacketIrpEvent->Data[0] = pBTInfo->BtAsocEntry[EntryNum].PhyLinkCmdData.BtPhyLinkhandle;
+ bthci_IndicateEvent(padapter, PPacketIrpEvent, 3);
+}
+
+static void
+bthci_EventDisconnectPhyLinkComplete(
+ struct rtw_adapter *padapter,
+ enum hci_status HciStatus,
+ enum hci_status Reason,
+ u8 EntryNum
+ )
+{
+ struct bt_30info *pBTInfo = GET_BT_INFO(padapter);
+ struct bt_hci_info *pBtHciInfo = &pBTInfo->BtHciInfo;
+ u8 localBuf[5] = "";
+ struct packet_irp_hcievent_data *PPacketIrpEvent;
+
+ if (!(pBtHciInfo->BTEventMaskPage2 & EMP2_HCI_EVENT_DISCONNECT_PHY_LINK_COMPLETE)) {
+ RTPRINT(FIOCTL, IOCTL_BT_EVENT,
+ ("[BT event], Disconnect Physical Link Complete, Ignore to send this event due to event mask page 2\n"));
+ return;
+ }
+ RTPRINT(FIOCTL, IOCTL_BT_EVENT,
+ ("[BT event], Disconnect Physical Link Complete, Status = 0x%x, PLH = 0x%x Reason = 0x%x\n",
+ HciStatus, pBTInfo->BtAsocEntry[EntryNum].PhyLinkCmdData.BtPhyLinkhandle, Reason));
+ PPacketIrpEvent = (struct packet_irp_hcievent_data *)(&localBuf[0]);
+ PPacketIrpEvent->EventCode = HCI_EVENT_DISCONNECT_PHY_LINK_COMPLETE;
+ PPacketIrpEvent->Length = 3;
+ PPacketIrpEvent->Data[0] = HciStatus;
+ PPacketIrpEvent->Data[1] = pBTInfo->BtAsocEntry[EntryNum].PhyLinkCmdData.BtPhyLinkhandle;
+ PPacketIrpEvent->Data[2] = Reason;
+ bthci_IndicateEvent(padapter, PPacketIrpEvent, 5);
+}
+
+static void
+bthci_EventPhysicalLinkComplete(
+ struct rtw_adapter *padapter,
+ enum hci_status HciStatus,
+ u8 EntryNum,
+ u8 PLHandle
+ )
+{
+ struct bt_30info *pBTInfo = GET_BT_INFO(padapter);
+ struct bt_mgnt *pBtMgnt = &pBTInfo->BtMgnt;
+ struct bt_hci_info *pBtHciInfo = &pBTInfo->BtHciInfo;
+ struct bt_dgb *pBtDbg = &pBTInfo->BtDbg;
+ u8 localBuf[4] = "";
+ struct packet_irp_hcievent_data *PPacketIrpEvent;
+ u8 PL_handle;
+
+ pBtMgnt->bPhyLinkInProgress = false;
+ pBtDbg->dbgHciInfo.hciCmdPhyLinkStatus = HciStatus;
+ if (!(pBtHciInfo->BTEventMaskPage2 & EMP2_HCI_EVENT_PHY_LINK_COMPLETE)) {
+ RTPRINT(FIOCTL, IOCTL_BT_EVENT,
+ ("[BT event], Physical Link Complete, Ignore to send this event due to event mask page 2\n"));
+ return;
+ }
+
+ if (EntryNum == 0xff) {
+ /* connection not started yet, just use the input physical link handle to response. */
+ PL_handle = PLHandle;
+ } else {
+ /* connection is under progress, use the phy link handle we recorded. */
+ PL_handle = pBTInfo->BtAsocEntry[EntryNum].PhyLinkCmdData.BtPhyLinkhandle;
+ pBTInfo->BtAsocEntry[EntryNum].bNeedPhysLinkCompleteEvent = false;
+ }
+
+ RTPRINT(FIOCTL, IOCTL_BT_EVENT, ("[BT event], Physical Link Complete, Status = 0x%x PhyLinkHandle = 0x%x\n", HciStatus,
+ PL_handle));
+
+ PPacketIrpEvent = (struct packet_irp_hcievent_data *)(&localBuf[0]);
+ PPacketIrpEvent->EventCode = HCI_EVENT_PHY_LINK_COMPLETE;
+ PPacketIrpEvent->Length = 2;
+
+ PPacketIrpEvent->Data[0] = HciStatus;
+ PPacketIrpEvent->Data[1] = PL_handle;
+ bthci_IndicateEvent(padapter, PPacketIrpEvent, 4);
+
+}
+
+static void
+bthci_EventCommandStatus(
+ struct rtw_adapter *padapter,
+ u8 OGF,
+ u16 OCF,
+ enum hci_status HciStatus
+ )
+{
+
+ u8 localBuf[6] = "";
+ struct packet_irp_hcievent_data *PPacketIrpEvent;
+ u8 Num_Hci_Comm = 0x1;
+ RTPRINT(FIOCTL, IOCTL_BT_EVENT,
+ ("[BT event], CommandStatus, Opcode = 0x%02x%02x, OGF = 0x%x, OCF = 0x%x, Status = 0x%x, Num_HCI_COMM = 0x%x\n",
+ (HCIOPCODEHIGHT(OCF, OGF)), (HCIOPCODELOW(OCF, OGF)), OGF, OCF, HciStatus, Num_Hci_Comm));
+
+ PPacketIrpEvent = (struct packet_irp_hcievent_data *)(&localBuf[0]);
+ PPacketIrpEvent->EventCode = HCI_EVENT_COMMAND_STATUS;
+ PPacketIrpEvent->Length = 4;
+ PPacketIrpEvent->Data[0] = HciStatus; /* current pending */
+ PPacketIrpEvent->Data[1] = Num_Hci_Comm; /* packet # */
+ PPacketIrpEvent->Data[2] = HCIOPCODELOW(OCF, OGF);
+ PPacketIrpEvent->Data[3] = HCIOPCODEHIGHT(OCF, OGF);
+
+ bthci_IndicateEvent(padapter, PPacketIrpEvent, 6);
+
+}
+
+static void
+bthci_EventLogicalLinkComplete(
+ struct rtw_adapter *padapter,
+ enum hci_status HciStatus,
+ u8 PhyLinkHandle,
+ u16 LogLinkHandle,
+ u8 LogLinkIndex,
+ u8 EntryNum
+ )
+{
+/*PMGNT_INFO pMgntInfo = &padapter->MgntInfo; */
+ struct bt_30info *pBTInfo = GET_BT_INFO(padapter);
+ struct bt_hci_info *pBtHciInfo = &pBTInfo->BtHciInfo;
+ u8 localBuf[7] = "";
+ struct packet_irp_hcievent_data *PPacketIrpEvent;
+
+ if (!(pBtHciInfo->BTEventMaskPage2 & EMP2_HCI_EVENT_LOGICAL_LINK_COMPLETE)) {
+ RTPRINT(FIOCTL, IOCTL_BT_EVENT,
+ ("[BT event], Logical Link Complete, Ignore to send this event due to event mask page 2\n"));
+ return;
+ }
+ RTPRINT(FIOCTL, IOCTL_BT_EVENT, ("[BT event], Logical Link Complete, PhyLinkHandle = 0x%x, LogLinkHandle = 0x%x, Status = 0x%x\n",
+ PhyLinkHandle, LogLinkHandle, HciStatus));
+
+ PPacketIrpEvent = (struct packet_irp_hcievent_data *)(&localBuf[0]);
+ PPacketIrpEvent->EventCode = HCI_EVENT_LOGICAL_LINK_COMPLETE;
+ PPacketIrpEvent->Length = 5;
+
+ PPacketIrpEvent->Data[0] = HciStatus;/* status code */
+ /* Logical link handle */
+ PPacketIrpEvent->Data[1] = TWOBYTE_LOWBYTE(LogLinkHandle);
+ PPacketIrpEvent->Data[2] = TWOBYTE_HIGHTBYTE(LogLinkHandle);
+ /* Physical link handle */
+ PPacketIrpEvent->Data[3] = TWOBYTE_LOWBYTE(PhyLinkHandle);
+ /* corresponding Tx flow spec ID */
+ if (HciStatus == HCI_STATUS_SUCCESS) {
+ PPacketIrpEvent->Data[4] =
+ pBTInfo->BtAsocEntry[EntryNum].LogLinkCmdData[LogLinkIndex].Tx_Flow_Spec.Identifier;
+ } else {
+ PPacketIrpEvent->Data[4] = 0x0;
+ }
+
+ bthci_IndicateEvent(padapter, PPacketIrpEvent, 7);
+}
+
+static void
+bthci_EventDisconnectLogicalLinkComplete(
+ struct rtw_adapter *padapter,
+ enum hci_status HciStatus,
+ u16 LogLinkHandle,
+ enum hci_status Reason
+ )
+{
+ u8 localBuf[6] = "";
+ struct bt_30info *pBTInfo = GET_BT_INFO(padapter);
+ struct bt_hci_info *pBtHciInfo = &pBTInfo->BtHciInfo;
+ struct packet_irp_hcievent_data *PPacketIrpEvent;
+
+ if (!(pBtHciInfo->BTEventMaskPage2 & EMP2_HCI_EVENT_DISCONNECT_LOGICAL_LINK_COMPLETE)) {
+ RTPRINT(FIOCTL, IOCTL_BT_EVENT, ("[BT event], Disconnect Logical Link Complete, Ignore to send this event due to event mask page 2\n"));
+ return;
+ }
+ RTPRINT(FIOCTL, IOCTL_BT_EVENT, ("[BT event], Disconnect Logical Link Complete, Status = 0x%x, LLH = 0x%x Reason = 0x%x\n", HciStatus, LogLinkHandle, Reason));
+
+ PPacketIrpEvent = (struct packet_irp_hcievent_data *)(&localBuf[0]);
+ PPacketIrpEvent->EventCode = HCI_EVENT_DISCONNECT_LOGICAL_LINK_COMPLETE;
+ PPacketIrpEvent->Length = 4;
+
+ PPacketIrpEvent->Data[0] = HciStatus;
+ /* Logical link handle */
+ PPacketIrpEvent->Data[1] = TWOBYTE_LOWBYTE(LogLinkHandle);
+ PPacketIrpEvent->Data[2] = TWOBYTE_HIGHTBYTE(LogLinkHandle);
+ /* Disconnect reason */
+ PPacketIrpEvent->Data[3] = Reason;
+
+ bthci_IndicateEvent(padapter, PPacketIrpEvent, 6);
+}
+
+static void
+bthci_EventFlushOccurred(
+ struct rtw_adapter *padapter,
+ u16 LogLinkHandle
+ )
+{
+ u8 localBuf[4] = "";
+ struct packet_irp_hcievent_data *PPacketIrpEvent;
+ RTPRINT(FIOCTL, IOCTL_BT_EVENT, ("bthci_EventFlushOccurred(), LLH = 0x%x\n", LogLinkHandle));
+
+ PPacketIrpEvent = (struct packet_irp_hcievent_data *)(&localBuf[0]);
+ PPacketIrpEvent->EventCode = HCI_EVENT_FLUSH_OCCRUED;
+ PPacketIrpEvent->Length = 2;
+ /* Logical link handle */
+ PPacketIrpEvent->Data[0] = TWOBYTE_LOWBYTE(LogLinkHandle);
+ PPacketIrpEvent->Data[1] = TWOBYTE_HIGHTBYTE(LogLinkHandle);
+
+ bthci_IndicateEvent(padapter, PPacketIrpEvent, 4);
+}
+
+static enum hci_status
+bthci_BuildPhysicalLink(
+ struct rtw_adapter *padapter,
+ struct packet_irp_hcicmd_data *pHciCmd,
+ u16 OCF
+)
+{
+ enum hci_status status = HCI_STATUS_SUCCESS;
+ struct bt_30info *pBTInfo = GET_BT_INFO(padapter);
+ struct bt_mgnt *pBtMgnt = &pBTInfo->BtMgnt;
+ u8 EntryNum, PLH;
+
+ /* Send HCI Command status event to AMP. */
+ bthci_EventCommandStatus(padapter,
+ LINK_CONTROL_COMMANDS,
+ OCF,
+ HCI_STATUS_SUCCESS);
+
+ PLH = *((u8 *)pHciCmd->Data);
+
+ /* Check if resource or bt connection is under progress, if yes, reject the link creation. */
+ if (!bthci_AddEntry(padapter)) {
+ status = HCI_STATUS_CONNECT_RJT_LIMIT_RESOURCE;
+ bthci_EventPhysicalLinkComplete(padapter, status, INVALID_ENTRY_NUM, PLH);
+ return status;
+ }
+
+ EntryNum = pBtMgnt->CurrentConnectEntryNum;
+ pBTInfo->BtAsocEntry[EntryNum].PhyLinkCmdData.BtPhyLinkhandle = PLH;
+ pBtMgnt->BtCurrentPhyLinkhandle = PLH;
+
+ if (pBTInfo->BtAsocEntry[EntryNum].AmpAsocCmdData.AMPAssocfragment == NULL) {
+ RTPRINT(FIOCTL, (IOCTL_BT_HCICMD|IOCTL_BT_LOGO), ("Create/Accept PhysicalLink, AMP controller is busy\n"));
+ status = HCI_STATUS_CONTROLLER_BUSY;
+ bthci_EventPhysicalLinkComplete(padapter, status, INVALID_ENTRY_NUM, PLH);
+ return status;
+ }
+
+ /* Record Key and the info */
+ pBTInfo->BtAsocEntry[EntryNum].PhyLinkCmdData.BtAMPKeyLen = (*((u8 *)pHciCmd->Data+1));
+ pBTInfo->BtAsocEntry[EntryNum].PhyLinkCmdData.BtAMPKeyType = (*((u8 *)pHciCmd->Data+2));
+ memcpy(pBTInfo->BtAsocEntry[EntryNum].PhyLinkCmdData.BtAMPKey,
+ (((u8 *)pHciCmd->Data+3)), pBTInfo->BtAsocEntry[EntryNum].PhyLinkCmdData.BtAMPKeyLen);
+ memcpy(pBTInfo->BtAsocEntry[EntryNum].PMK, pBTInfo->BtAsocEntry[EntryNum].PhyLinkCmdData.BtAMPKey, PMK_LEN);
+ RTPRINT(FIOCTL, IOCTL_BT_HCICMD, ("BuildPhysicalLink, EntryNum = %d, PLH = 0x%x KeyLen = 0x%x, KeyType = 0x%x\n",
+ EntryNum, pBTInfo->BtAsocEntry[EntryNum].PhyLinkCmdData.BtPhyLinkhandle,
+ pBTInfo->BtAsocEntry[EntryNum].PhyLinkCmdData.BtAMPKeyLen,
+ pBTInfo->BtAsocEntry[EntryNum].PhyLinkCmdData.BtAMPKeyType));
+ RTPRINT_DATA(FIOCTL, (IOCTL_BT_LOGO|IOCTL_BT_HCICMD), ("BtAMPKey\n"), pBTInfo->BtAsocEntry[EntryNum].PhyLinkCmdData.BtAMPKey,
+ pBTInfo->BtAsocEntry[EntryNum].PhyLinkCmdData.BtAMPKeyLen);
+ RTPRINT_DATA(FIOCTL, (IOCTL_BT_LOGO|IOCTL_BT_HCICMD), ("PMK\n"), pBTInfo->BtAsocEntry[EntryNum].PMK,
+ PMK_LEN);
+
+ if (OCF == HCI_CREATE_PHYSICAL_LINK) {
+ /* These macros require braces */
+ BTHCI_SM_WITH_INFO(padapter, HCI_STATE_DISCONNECTED, STATE_CMD_CREATE_PHY_LINK, EntryNum);
+ } else if (OCF == HCI_ACCEPT_PHYSICAL_LINK) {
+ BTHCI_SM_WITH_INFO(padapter, HCI_STATE_DISCONNECTED, STATE_CMD_ACCEPT_PHY_LINK, EntryNum);
+ }
+
+ return status;
+}
+
+static void
+bthci_BuildLogicalLink(
+ struct rtw_adapter *padapter,
+ struct packet_irp_hcicmd_data *pHciCmd,
+ u16 OCF
+ )
+{
+ enum hci_status status = HCI_STATUS_SUCCESS;
+ struct bt_30info *pBTinfo = GET_BT_INFO(padapter);
+ struct bt_mgnt *pBtMgnt = &pBTinfo->BtMgnt;
+ u8 PhyLinkHandle, EntryNum;
+ static u16 AssignLogHandle = 1;
+
+ struct hci_flow_spec TxFlowSpec;
+ struct hci_flow_spec RxFlowSpec;
+ u32 MaxSDUSize, ArriveTime, Bandwidth;
+
+ PhyLinkHandle = *((u8 *)pHciCmd->Data);
+
+ EntryNum = bthci_GetCurrentEntryNum(padapter, PhyLinkHandle);
+
+ memcpy(&TxFlowSpec,
+ &pHciCmd->Data[1], sizeof(struct hci_flow_spec));
+ memcpy(&RxFlowSpec,
+ &pHciCmd->Data[17], sizeof(struct hci_flow_spec));
+
+ MaxSDUSize = TxFlowSpec.MaximumSDUSize;
+ ArriveTime = TxFlowSpec.SDUInterArrivalTime;
+
+ if (bthci_CheckLogLinkBehavior(padapter, TxFlowSpec) && bthci_CheckLogLinkBehavior(padapter, RxFlowSpec))
+ Bandwidth = BTTOTALBANDWIDTH;
+ else if (MaxSDUSize == 0xffff && ArriveTime == 0xffffffff)
+ Bandwidth = BTTOTALBANDWIDTH;
+ else
+ Bandwidth = MaxSDUSize*8*1000/(ArriveTime+244);
+
+ RTPRINT(FIOCTL, IOCTL_BT_HCICMD,
+ ("BuildLogicalLink, PhyLinkHandle = 0x%x, MaximumSDUSize = 0x%x, SDUInterArrivalTime = 0x%x, Bandwidth = 0x%x\n",
+ PhyLinkHandle, MaxSDUSize, ArriveTime, Bandwidth));
+
+ if (EntryNum == 0xff) {
+ RTPRINT(FIOCTL, IOCTL_BT_HCICMD, ("Invalid Physical Link handle = 0x%x, status = HCI_STATUS_UNKNOW_CONNECT_ID, return\n", PhyLinkHandle));
+ status = HCI_STATUS_UNKNOW_CONNECT_ID;
+
+ /* When we receive Create/Accept logical link command, we should send command status event first. */
+ bthci_EventCommandStatus(padapter,
+ LINK_CONTROL_COMMANDS,
+ OCF,
+ status);
+ return;
+ }
+
+ if (!pBtMgnt->bLogLinkInProgress) {
+ if (bthci_PhyLinkConnectionInProgress(padapter, PhyLinkHandle)) {
+ RTPRINT(FIOCTL, IOCTL_BT_HCICMD, ("Physical link connection in progress, status = HCI_STATUS_CMD_DISALLOW, return\n"));
+ status = HCI_STATUS_CMD_DISALLOW;
+
+ pBtMgnt->bPhyLinkInProgressStartLL = true;
+ /* When we receive Create/Accept logical link command, we should send command status event first. */
+ bthci_EventCommandStatus(padapter,
+ LINK_CONTROL_COMMANDS,
+ OCF,
+ status);
+
+ return;
+ }
+
+ if (Bandwidth > BTTOTALBANDWIDTH) {
+ RTPRINT(FIOCTL, IOCTL_BT_HCICMD, ("status = HCI_STATUS_QOS_REJECT, Bandwidth = 0x%x, return\n", Bandwidth));
+ status = HCI_STATUS_QOS_REJECT;
+
+ /* When we receive Create/Accept logical link command, we should send command status event first. */
+ bthci_EventCommandStatus(padapter,
+ LINK_CONTROL_COMMANDS,
+ OCF,
+ status);
+ } else {
+ RTPRINT(FIOCTL, IOCTL_BT_HCICMD, ("status = HCI_STATUS_SUCCESS\n"));
+ status = HCI_STATUS_SUCCESS;
+
+ /* When we receive Create/Accept logical link command, we should send command status event first. */
+ bthci_EventCommandStatus(padapter,
+ LINK_CONTROL_COMMANDS,
+ OCF,
+ status);
+
+ }
+
+ if (pBTinfo->BtAsocEntry[EntryNum].BtCurrentState != HCI_STATE_CONNECTED) {
+ bthci_EventLogicalLinkComplete(padapter,
+ HCI_STATUS_CMD_DISALLOW, 0, 0, 0, EntryNum);
+ } else {
+ u8 i, find = 0;
+
+ pBtMgnt->bLogLinkInProgress = true;
+
+ /* find an unused logical link index and copy the data */
+ for (i = 0; i < MAX_LOGICAL_LINK_NUM; i++) {
+ if (pBTinfo->BtAsocEntry[EntryNum].LogLinkCmdData[i].BtLogLinkhandle == 0) {
+ enum hci_status LogCompEventstatus = HCI_STATUS_SUCCESS;
+
+ pBTinfo->BtAsocEntry[EntryNum].LogLinkCmdData[i].BtPhyLinkhandle = *((u8 *)pHciCmd->Data);
+ pBTinfo->BtAsocEntry[EntryNum].LogLinkCmdData[i].BtLogLinkhandle = AssignLogHandle;
+ RTPRINT(FIOCTL, IOCTL_BT_HCICMD, ("BuildLogicalLink, EntryNum = %d, physical link handle = 0x%x, logical link handle = 0x%x\n",
+ EntryNum, pBTinfo->BtAsocEntry[EntryNum].PhyLinkCmdData.BtPhyLinkhandle,
+ pBTinfo->BtAsocEntry[EntryNum].LogLinkCmdData[i].BtLogLinkhandle));
+ memcpy(&pBTinfo->BtAsocEntry[EntryNum].LogLinkCmdData[i].Tx_Flow_Spec,
+ &TxFlowSpec, sizeof(struct hci_flow_spec));
+ memcpy(&pBTinfo->BtAsocEntry[EntryNum].LogLinkCmdData[i].Rx_Flow_Spec,
+ &RxFlowSpec, sizeof(struct hci_flow_spec));
+
+ pBTinfo->BtAsocEntry[EntryNum].LogLinkCmdData[i].bLLCompleteEventIsSet = false;
+
+ if (pBTinfo->BtAsocEntry[EntryNum].LogLinkCmdData[i].bLLCancelCMDIsSetandComplete)
+ LogCompEventstatus = HCI_STATUS_UNKNOW_CONNECT_ID;
+ bthci_EventLogicalLinkComplete(padapter,
+ LogCompEventstatus,
+ pBTinfo->BtAsocEntry[EntryNum].LogLinkCmdData[i].BtPhyLinkhandle,
+ pBTinfo->BtAsocEntry[EntryNum].LogLinkCmdData[i].BtLogLinkhandle, i, EntryNum);
+
+ pBTinfo->BtAsocEntry[EntryNum].LogLinkCmdData[i].bLLCompleteEventIsSet = true;
+
+ find = 1;
+ pBtMgnt->BtCurrentLogLinkhandle = AssignLogHandle;
+ AssignLogHandle++;
+ break;
+ }
+ }
+
+ if (!find) {
+ bthci_EventLogicalLinkComplete(padapter,
+ HCI_STATUS_CONNECT_RJT_LIMIT_RESOURCE, 0, 0, 0, EntryNum);
+ }
+ pBtMgnt->bLogLinkInProgress = false;
+ }
+ } else {
+ bthci_EventLogicalLinkComplete(padapter,
+ HCI_STATUS_CONTROLLER_BUSY, 0, 0, 0, EntryNum);
+ }
+
+}
+
+static void
+bthci_StartBeaconAndConnect(
+ struct rtw_adapter *padapter,
+ struct packet_irp_hcicmd_data *pHciCmd,
+ u8 CurrentAssocNum
+ )
+{
+/*PMGNT_INFO pMgntInfo = &padapter->MgntInfo; */
+ struct bt_30info *pBTInfo = GET_BT_INFO(padapter);
+ struct bt_mgnt *pBtMgnt = &pBTInfo->BtMgnt;
+
+ RTPRINT(FIOCTL, IOCTL_BT_HCICMD, ("StartBeaconAndConnect, CurrentAssocNum =%d, AMPRole =%d\n",
+ CurrentAssocNum,
+ pBTInfo->BtAsocEntry[CurrentAssocNum].AMPRole));
+
+ if (!pBtMgnt->CheckChnlIsSuit) {
+ bthci_EventPhysicalLinkComplete(padapter, HCI_STATUS_CONNECT_REJ_NOT_SUIT_CHNL_FOUND, CurrentAssocNum, INVALID_PL_HANDLE);
+ bthci_RemoveEntryByEntryNum(padapter, CurrentAssocNum);
+ return;
+ }
+
+ if (pBTInfo->BtAsocEntry[CurrentAssocNum].AMPRole == AMP_BTAP_CREATOR) {
+ rsprintf((char *)pBTInfo->BtAsocEntry[CurrentAssocNum].BTSsidBuf, 32, "AMP-%02x-%02x-%02x-%02x-%02x-%02x",
+ padapter->eeprompriv.mac_addr[0],
+ padapter->eeprompriv.mac_addr[1],
+ padapter->eeprompriv.mac_addr[2],
+ padapter->eeprompriv.mac_addr[3],
+ padapter->eeprompriv.mac_addr[4],
+ padapter->eeprompriv.mac_addr[5]);
+ } else if (pBTInfo->BtAsocEntry[CurrentAssocNum].AMPRole == AMP_BTAP_JOINER) {
+ rsprintf((char *)pBTInfo->BtAsocEntry[CurrentAssocNum].BTSsidBuf, 32, "AMP-%02x-%02x-%02x-%02x-%02x-%02x",
+ pBTInfo->BtAsocEntry[CurrentAssocNum].BTRemoteMACAddr[0],
+ pBTInfo->BtAsocEntry[CurrentAssocNum].BTRemoteMACAddr[1],
+ pBTInfo->BtAsocEntry[CurrentAssocNum].BTRemoteMACAddr[2],
+ pBTInfo->BtAsocEntry[CurrentAssocNum].BTRemoteMACAddr[3],
+ pBTInfo->BtAsocEntry[CurrentAssocNum].BTRemoteMACAddr[4],
+ pBTInfo->BtAsocEntry[CurrentAssocNum].BTRemoteMACAddr[5]);
+ }
+
+ FillOctetString(pBTInfo->BtAsocEntry[CurrentAssocNum].BTSsid, pBTInfo->BtAsocEntry[CurrentAssocNum].BTSsidBuf, 21);
+ pBTInfo->BtAsocEntry[CurrentAssocNum].BTSsid.Length = 21;
+
+ /* To avoid set the start ap or connect twice, or the original connection will be disconnected. */
+ if (!pBtMgnt->bBTConnectInProgress) {
+ pBtMgnt->bBTConnectInProgress = true;
+ RTPRINT(FIOCTL, IOCTL_STATE, ("[BT Flag], BT Connect in progress ON!!\n"));
+ BTHCI_SM_WITH_INFO(padapter, HCI_STATE_STARTING, STATE_CMD_MAC_START_COMPLETE, CurrentAssocNum);
+
+ /* 20100325 Joseph: Check RF ON/OFF. */
+ /* If RF OFF, it reschedule connecting operation after 50ms. */
+ if (!bthci_CheckRfStateBeforeConnect(padapter))
+ return;
+
+ if (pBTInfo->BtAsocEntry[CurrentAssocNum].AMPRole == AMP_BTAP_CREATOR) {
+ /* These macros need braces */
+ BTHCI_SM_WITH_INFO(padapter, HCI_STATE_CONNECTING, STATE_CMD_MAC_CONNECT_COMPLETE, CurrentAssocNum);
+ } else if (pBTInfo->BtAsocEntry[CurrentAssocNum].AMPRole == AMP_BTAP_JOINER) {
+ bthci_ResponderStartToScan(padapter);
+ }
+ }
+ RT_PRINT_STR(_module_rtl871x_mlme_c_, _drv_notice_,
+ "StartBeaconAndConnect, SSID:\n",
+ pBTInfo->BtAsocEntry[pBtMgnt->CurrentConnectEntryNum].BTSsid.Octet,
+ pBTInfo->BtAsocEntry[pBtMgnt->CurrentConnectEntryNum].BTSsid.Length);
+}
+
+static void bthci_ResetBtMgnt(struct bt_mgnt *pBtMgnt)
+{
+ pBtMgnt->BtOperationOn = false;
+ pBtMgnt->bBTConnectInProgress = false;
+ pBtMgnt->bLogLinkInProgress = false;
+ pBtMgnt->bPhyLinkInProgress = false;
+ pBtMgnt->bPhyLinkInProgressStartLL = false;
+ pBtMgnt->DisconnectEntryNum = 0xff;
+ pBtMgnt->bStartSendSupervisionPkt = false;
+ pBtMgnt->JoinerNeedSendAuth = false;
+ pBtMgnt->CurrentBTConnectionCnt = 0;
+ pBtMgnt->BTCurrentConnectType = BT_DISCONNECT;
+ pBtMgnt->BTReceiveConnectPkt = BT_DISCONNECT;
+ pBtMgnt->BTAuthCount = 0;
+ pBtMgnt->btLogoTest = 0;
+}
+
+static void bthci_ResetBtHciInfo(struct bt_hci_info *pBtHciInfo)
+{
+ pBtHciInfo->BTEventMask = 0;
+ pBtHciInfo->BTEventMaskPage2 = 0;
+ pBtHciInfo->ConnAcceptTimeout = 10000;
+ pBtHciInfo->PageTimeout = 0x30;
+ pBtHciInfo->LocationDomainAware = 0x0;
+ pBtHciInfo->LocationDomain = 0x5858;
+ pBtHciInfo->LocationDomainOptions = 0x58;
+ pBtHciInfo->LocationOptions = 0x0;
+ pBtHciInfo->FlowControlMode = 0x1; /* 0:Packet based data flow control mode(BR/EDR), 1: Data block based data flow control mode(AMP). */
+
+ pBtHciInfo->enFlush_LLH = 0;
+ pBtHciInfo->FLTO_LLH = 0;
+
+ /* Test command only */
+ pBtHciInfo->bTestIsEnd = true;
+ pBtHciInfo->bInTestMode = false;
+ pBtHciInfo->bTestNeedReport = false;
+ pBtHciInfo->TestScenario = 0xff;
+ pBtHciInfo->TestReportInterval = 0x01;
+ pBtHciInfo->TestCtrType = 0x5d;
+ pBtHciInfo->TestEventType = 0x00;
+ pBtHciInfo->TestNumOfFrame = 0;
+ pBtHciInfo->TestNumOfErrFrame = 0;
+ pBtHciInfo->TestNumOfBits = 0;
+ pBtHciInfo->TestNumOfErrBits = 0;
+}
+
+static void bthci_ResetBtSec(struct rtw_adapter *padapter, struct bt_security *pBtSec)
+{
+/*PMGNT_INFO pMgntInfo = &padapter->MgntInfo; */
+
+ /* Set BT used HW or SW encrypt !! */
+ if (GET_HAL_DATA(padapter)->bBTMode)
+ pBtSec->bUsedHwEncrypt = true;
+ else
+ pBtSec->bUsedHwEncrypt = false;
+ RT_TRACE(_module_rtl871x_security_c_, _drv_info_,
+ "%s: bUsedHwEncrypt =%d\n", __func__, pBtSec->bUsedHwEncrypt);
+
+ pBtSec->RSNIE.Octet = pBtSec->RSNIEBuf;
+}
+
+static void bthci_ResetBtExtInfo(struct bt_mgnt *pBtMgnt)
+{
+ u8 i;
+
+ for (i = 0; i < MAX_BT_ASOC_ENTRY_NUM; i++) {
+ pBtMgnt->ExtConfig.linkInfo[i].ConnectHandle = 0;
+ pBtMgnt->ExtConfig.linkInfo[i].IncomingTrafficMode = 0;
+ pBtMgnt->ExtConfig.linkInfo[i].OutgoingTrafficMode = 0;
+ pBtMgnt->ExtConfig.linkInfo[i].BTProfile = BT_PROFILE_NONE;
+ pBtMgnt->ExtConfig.linkInfo[i].BTCoreSpec = BT_SPEC_2_1_EDR;
+ pBtMgnt->ExtConfig.linkInfo[i].BT_RSSI = 0;
+ pBtMgnt->ExtConfig.linkInfo[i].TrafficProfile = BT_PROFILE_NONE;
+ pBtMgnt->ExtConfig.linkInfo[i].linkRole = BT_LINK_MASTER;
+ }
+
+ pBtMgnt->ExtConfig.CurrentConnectHandle = 0;
+ pBtMgnt->ExtConfig.CurrentIncomingTrafficMode = 0;
+ pBtMgnt->ExtConfig.CurrentOutgoingTrafficMode = 0;
+ pBtMgnt->ExtConfig.MIN_BT_RSSI = 0;
+ pBtMgnt->ExtConfig.NumberOfHandle = 0;
+ pBtMgnt->ExtConfig.NumberOfSCO = 0;
+ pBtMgnt->ExtConfig.CurrentBTStatus = 0;
+ pBtMgnt->ExtConfig.HCIExtensionVer = 0;
+
+ pBtMgnt->ExtConfig.bManualControl = false;
+ pBtMgnt->ExtConfig.bBTBusy = false;
+ pBtMgnt->ExtConfig.bBTA2DPBusy = false;
+}
+
+static enum hci_status bthci_CmdReset(struct rtw_adapter *_padapter, u8 bNeedSendEvent)
+{
+ enum hci_status status = HCI_STATUS_SUCCESS;
+ struct rtw_adapter *padapter;
+/*PMGNT_INFO pMgntInfo = &padapter->MgntInfo; */
+ struct bt_30info *pBTInfo;
+ struct bt_mgnt *pBtMgnt;
+ struct bt_hci_info *pBtHciInfo;
+ struct bt_security *pBtSec;
+ struct bt_dgb *pBtDbg;
+ u8 i;
+
+ RTPRINT(FIOCTL, IOCTL_BT_HCICMD, ("bthci_CmdReset()\n"));
+
+ padapter = GetDefaultAdapter(_padapter);
+ pBTInfo = GET_BT_INFO(padapter);
+ pBtMgnt = &pBTInfo->BtMgnt;
+ pBtHciInfo = &pBTInfo->BtHciInfo;
+ pBtSec = &pBTInfo->BtSec;
+ pBtDbg = &pBTInfo->BtDbg;
+
+ pBTInfo->padapter = padapter;
+
+ for (i = 0; i < MAX_BT_ASOC_ENTRY_NUM; i++)
+ bthci_ResetEntry(padapter, i);
+
+ bthci_ResetBtMgnt(pBtMgnt);
+ bthci_ResetBtHciInfo(pBtHciInfo);
+ bthci_ResetBtSec(padapter, pBtSec);
+
+ pBtMgnt->BTChannel = BT_Default_Chnl;
+ pBtMgnt->CheckChnlIsSuit = true;
+
+ pBTInfo->BTBeaconTmrOn = false;
+
+ pBtMgnt->bCreateSpportQos = true;
+
+ del_timer_sync(&pBTInfo->BTHCIDiscardAclDataTimer);
+ del_timer_sync(&pBTInfo->BTBeaconTimer);
+
+ HALBT_SetRtsCtsNoLenLimit(padapter);
+ /* */
+ /* Maybe we need to take care Group != AES case !! */
+ /* now we Pairwise and Group all used AES !! */
+
+ bthci_ResetBtExtInfo(pBtMgnt);
+
+ /* send command complete event here when all data are received. */
+ if (bNeedSendEvent) {
+ u8 localBuf[6] = "";
+ u8 *pRetPar;
+ u8 len = 0;
+ struct packet_irp_hcievent_data *PPacketIrpEvent;
+
+ PPacketIrpEvent = (struct packet_irp_hcievent_data *)(&localBuf[0]);
+
+ len += bthci_CommandCompleteHeader(&localBuf[0],
+ OGF_SET_EVENT_MASK_COMMAND,
+ HCI_RESET,
+ status);
+
+ /* Return parameters starts from here */
+ pRetPar = &PPacketIrpEvent->Data[len];
+ pRetPar[0] = status; /* status */
+ len += 1;
+ PPacketIrpEvent->Length = len;
+
+ bthci_IndicateEvent(padapter, PPacketIrpEvent, len+2);
+ }
+
+ return status;
+}
+
+static enum hci_status
+bthci_CmdWriteRemoteAMPAssoc(
+ struct rtw_adapter *padapter,
+ struct packet_irp_hcicmd_data *pHciCmd
+ )
+{
+ enum hci_status status = HCI_STATUS_SUCCESS;
+ struct bt_30info *pBTInfo = GET_BT_INFO(padapter);
+ struct bt_dgb *pBtDbg = &pBTInfo->BtDbg;
+ u8 CurrentAssocNum;
+ u8 PhyLinkHandle;
+
+ pBtDbg->dbgHciInfo.hciCmdCntWriteRemoteAmpAssoc++;
+ PhyLinkHandle = *((u8 *)pHciCmd->Data);
+ CurrentAssocNum = bthci_GetCurrentEntryNum(padapter, PhyLinkHandle);
+
+ if (CurrentAssocNum == 0xff) {
+ RTPRINT(FIOCTL, (IOCTL_BT_HCICMD|IOCTL_BT_LOGO), ("WriteRemoteAMPAssoc, No such Handle in the Entry\n"));
+ status = HCI_STATUS_UNKNOW_CONNECT_ID;
+ bthci_EventWriteRemoteAmpAssoc(padapter, status, PhyLinkHandle);
+ return status;
+ }
+
+ if (pBTInfo->BtAsocEntry[CurrentAssocNum].AmpAsocCmdData.AMPAssocfragment == NULL) {
+ RTPRINT(FIOCTL, (IOCTL_BT_HCICMD|IOCTL_BT_LOGO), ("WriteRemoteAMPAssoc, AMP controller is busy\n"));
+ status = HCI_STATUS_CONTROLLER_BUSY;
+ bthci_EventWriteRemoteAmpAssoc(padapter, status, PhyLinkHandle);
+ return status;
+ }
+
+ pBTInfo->BtAsocEntry[CurrentAssocNum].AmpAsocCmdData.BtPhyLinkhandle = PhyLinkHandle;/* u8 *)pHciCmd->Data); */
+ pBTInfo->BtAsocEntry[CurrentAssocNum].AmpAsocCmdData.LenSoFar = *((u16 *)((u8 *)pHciCmd->Data+1));
+ pBTInfo->BtAsocEntry[CurrentAssocNum].AmpAsocCmdData.AMPAssocRemLen = *((u16 *)((u8 *)pHciCmd->Data+3));
+
+ RTPRINT(FIOCTL, (IOCTL_BT_HCICMD_DETAIL|IOCTL_BT_LOGO), ("WriteRemoteAMPAssoc, LenSoFar = 0x%x, AssocRemLen = 0x%x\n",
+ pBTInfo->BtAsocEntry[CurrentAssocNum].AmpAsocCmdData.LenSoFar,
+ pBTInfo->BtAsocEntry[CurrentAssocNum].AmpAsocCmdData.AMPAssocRemLen));
+
+ RTPRINT_DATA(FIOCTL, (IOCTL_BT_HCICMD_DETAIL|IOCTL_BT_LOGO),
+ ("WriteRemoteAMPAssoc fragment \n"),
+ pHciCmd->Data,
+ pBTInfo->BtAsocEntry[CurrentAssocNum].AmpAsocCmdData.AMPAssocRemLen+5);
+ if ((pBTInfo->BtAsocEntry[CurrentAssocNum].AmpAsocCmdData.AMPAssocRemLen) > MAX_AMP_ASSOC_FRAG_LEN) {
+ memcpy(((u8 *)pBTInfo->BtAsocEntry[CurrentAssocNum].AmpAsocCmdData.AMPAssocfragment+(pBTInfo->BtAsocEntry[CurrentAssocNum].AmpAsocCmdData.LenSoFar*(sizeof(u8)))),
+ (u8 *)pHciCmd->Data+5,
+ MAX_AMP_ASSOC_FRAG_LEN);
+ } else {
+ memcpy((u8 *)(pBTInfo->BtAsocEntry[CurrentAssocNum].AmpAsocCmdData.AMPAssocfragment)+(pBTInfo->BtAsocEntry[CurrentAssocNum].AmpAsocCmdData.LenSoFar*(sizeof(u8))),
+ ((u8 *)pHciCmd->Data+5),
+ (pBTInfo->BtAsocEntry[CurrentAssocNum].AmpAsocCmdData.AMPAssocRemLen));
+
+ RTPRINT_DATA(FIOCTL, (IOCTL_BT_HCICMD_DETAIL|IOCTL_BT_LOGO), "WriteRemoteAMPAssoc :\n",
+ pHciCmd->Data+5, pBTInfo->BtAsocEntry[CurrentAssocNum].AmpAsocCmdData.AMPAssocRemLen);
+
+ if (!bthci_GetAssocInfo(padapter, CurrentAssocNum))
+ status = HCI_STATUS_INVALID_HCI_CMD_PARA_VALUE;
+
+ bthci_EventWriteRemoteAmpAssoc(padapter, status, PhyLinkHandle);
+
+ bthci_StartBeaconAndConnect(padapter, pHciCmd, CurrentAssocNum);
+ }
+
+ return status;
+}
+
+/* 7.3.13 */
+static enum hci_status bthci_CmdReadConnectionAcceptTimeout(struct rtw_adapter *padapter)
+{
+ enum hci_status status = HCI_STATUS_SUCCESS;
+/*PMGNT_INFO pMgntInfo = &padapter->MgntInfo; */
+ struct bt_30info *pBTInfo = GET_BT_INFO(padapter);
+ struct bt_hci_info *pBtHciInfo = &pBTInfo->BtHciInfo;
+ u8 localBuf[8] = "";
+ u8 *pRetPar;
+ u8 len = 0;
+ struct packet_irp_hcievent_data *PPacketIrpEvent;
+ u16 *pu2Temp;
+
+ PPacketIrpEvent = (struct packet_irp_hcievent_data *)(&localBuf[0]);
+
+ len += bthci_CommandCompleteHeader(&localBuf[0],
+ OGF_SET_EVENT_MASK_COMMAND,
+ HCI_READ_CONNECTION_ACCEPT_TIMEOUT,
+ status);
+
+ /* Return parameters starts from here */
+ pRetPar = &PPacketIrpEvent->Data[len];
+ pRetPar[0] = status; /* status */
+ pu2Temp = (u16 *)&pRetPar[1]; /* Conn_Accept_Timeout */
+ *pu2Temp = pBtHciInfo->ConnAcceptTimeout;
+ len += 3;
+ PPacketIrpEvent->Length = len;
+
+ bthci_IndicateEvent(padapter, PPacketIrpEvent, len+2);
+
+ return status;
+}
+
+/* 7.3.14 */
+static enum hci_status
+bthci_CmdWriteConnectionAcceptTimeout(
+ struct rtw_adapter *padapter,
+ struct packet_irp_hcicmd_data *pHciCmd
+ )
+{
+ enum hci_status status = HCI_STATUS_SUCCESS;
+ struct bt_30info *pBTInfo = GET_BT_INFO(padapter);
+ struct bt_hci_info *pBtHciInfo = &pBTInfo->BtHciInfo;
+ u16 *pu2Temp;
+ u8 localBuf[6] = "";
+ u8 *pRetPar;
+ u8 len = 0;
+ struct packet_irp_hcievent_data *PPacketIrpEvent;
+
+ pu2Temp = (u16 *)&pHciCmd->Data[0];
+ pBtHciInfo->ConnAcceptTimeout = *pu2Temp;
+ RTPRINT(FIOCTL, IOCTL_BT_HCICMD_DETAIL, ("ConnAcceptTimeout = 0x%x",
+ pBtHciInfo->ConnAcceptTimeout));
+
+ /* send command complete event here when all data are received. */
+ PPacketIrpEvent = (struct packet_irp_hcievent_data *)(&localBuf[0]);
+
+ len += bthci_CommandCompleteHeader(&localBuf[0],
+ OGF_SET_EVENT_MASK_COMMAND,
+ HCI_WRITE_CONNECTION_ACCEPT_TIMEOUT,
+ status);
+
+ /* Return parameters starts from here */
+ pRetPar = &PPacketIrpEvent->Data[len];
+ pRetPar[0] = status; /* status */
+ len += 1;
+ PPacketIrpEvent->Length = len;
+
+ bthci_IndicateEvent(padapter, PPacketIrpEvent, len+2);
+
+ return status;
+}
+
+static enum hci_status
+bthci_CmdReadPageTimeout(
+ struct rtw_adapter *padapter,
+ struct packet_irp_hcicmd_data *pHciCmd
+ )
+{
+ enum hci_status status = HCI_STATUS_SUCCESS;
+ struct bt_30info *pBTInfo = GET_BT_INFO(padapter);
+ struct bt_hci_info *pBtHciInfo = &pBTInfo->BtHciInfo;
+ u8 localBuf[8] = "";
+ u8 *pRetPar;
+ u8 len = 0;
+ struct packet_irp_hcievent_data *PPacketIrpEvent;
+ u16 *pu2Temp;
+
+ PPacketIrpEvent = (struct packet_irp_hcievent_data *)(&localBuf[0]);
+
+ len += bthci_CommandCompleteHeader(&localBuf[0],
+ OGF_SET_EVENT_MASK_COMMAND,
+ HCI_READ_PAGE_TIMEOUT,
+ status);
+
+ RTPRINT(FIOCTL, IOCTL_BT_HCICMD, ("Read PageTimeout = 0x%x\n", pBtHciInfo->PageTimeout));
+ /* Return parameters starts from here */
+ pRetPar = &PPacketIrpEvent->Data[len];
+ pRetPar[0] = status; /* status */
+ pu2Temp = (u16 *)&pRetPar[1]; /* Page_Timeout */
+ *pu2Temp = pBtHciInfo->PageTimeout;
+ len += 3;
+ PPacketIrpEvent->Length = len;
+
+ bthci_IndicateEvent(padapter, PPacketIrpEvent, len+2);
+
+ return status;
+}
+
+static enum hci_status
+bthci_CmdWritePageTimeout(
+ struct rtw_adapter *padapter,
+ struct packet_irp_hcicmd_data *pHciCmd
+ )
+{
+ enum hci_status status = HCI_STATUS_SUCCESS;
+ struct bt_30info *pBTInfo = GET_BT_INFO(padapter);
+ struct bt_hci_info *pBtHciInfo = &pBTInfo->BtHciInfo;
+ u16 *pu2Temp;
+
+ pu2Temp = (u16 *)&pHciCmd->Data[0];
+ pBtHciInfo->PageTimeout = *pu2Temp;
+ RTPRINT(FIOCTL, IOCTL_BT_HCICMD, ("Write PageTimeout = 0x%x\n",
+ pBtHciInfo->PageTimeout));
+
+ /* send command complete event here when all data are received. */
+ {
+ u8 localBuf[6] = "";
+ u8 *pRetPar;
+ u8 len = 0;
+ struct packet_irp_hcievent_data *PPacketIrpEvent;
+
+ PPacketIrpEvent = (struct packet_irp_hcievent_data *)(&localBuf[0]);
+
+ len += bthci_CommandCompleteHeader(&localBuf[0],
+ OGF_SET_EVENT_MASK_COMMAND,
+ HCI_WRITE_PAGE_TIMEOUT,
+ status);
+
+ /* Return parameters starts from here */
+ pRetPar = &PPacketIrpEvent->Data[len];
+ pRetPar[0] = status; /* status */
+ len += 1;
+ PPacketIrpEvent->Length = len;
+
+ bthci_IndicateEvent(padapter, PPacketIrpEvent, len+2);
+ }
+
+ return status;
+}
+
+static enum hci_status
+bthci_CmdReadLinkSupervisionTimeout(
+ struct rtw_adapter *padapter,
+ struct packet_irp_hcicmd_data *pHciCmd
+ )
+{
+ enum hci_status status = HCI_STATUS_SUCCESS;
+ struct bt_30info *pBTinfo = GET_BT_INFO(padapter);
+ u8 physicalLinkHandle, EntryNum;
+
+ physicalLinkHandle = *((u8 *)pHciCmd->Data);
+
+ EntryNum = bthci_GetCurrentEntryNum(padapter, physicalLinkHandle);
+
+ if (EntryNum == 0xff) {
+ RTPRINT(FIOCTL, IOCTL_BT_HCICMD, ("ReadLinkSupervisionTimeout, No such Handle in the Entry\n"));
+ status = HCI_STATUS_UNKNOW_CONNECT_ID;
+ return status;
+ }
+
+ if (pBTinfo->BtAsocEntry[EntryNum].PhyLinkCmdData.BtPhyLinkhandle != physicalLinkHandle)
+ status = HCI_STATUS_UNKNOW_CONNECT_ID;
+
+ {
+ u8 localBuf[10] = "";
+ u8 *pRetPar;
+ u8 len = 0;
+ struct packet_irp_hcievent_data *PPacketIrpEvent;
+ u16 *pu2Temp;
+
+ PPacketIrpEvent = (struct packet_irp_hcievent_data *)(&localBuf[0]);
+
+ len += bthci_CommandCompleteHeader(&localBuf[0],
+ OGF_SET_EVENT_MASK_COMMAND,
+ HCI_READ_LINK_SUPERVISION_TIMEOUT,
+ status);
+
+ /* Return parameters starts from here */
+ pRetPar = &PPacketIrpEvent->Data[len];
+ pRetPar[0] = status;
+ pRetPar[1] = pBTinfo->BtAsocEntry[EntryNum].PhyLinkCmdData.BtPhyLinkhandle;
+ pRetPar[2] = 0;
+ pu2Temp = (u16 *)&pRetPar[3]; /* Conn_Accept_Timeout */
+ *pu2Temp = pBTinfo->BtAsocEntry[EntryNum].PhyLinkCmdData.LinkSuperversionTimeout;
+ len += 5;
+ PPacketIrpEvent->Length = len;
+
+ bthci_IndicateEvent(padapter, PPacketIrpEvent, len+2);
+ }
+
+ return status;
+}
+
+static enum hci_status
+bthci_CmdWriteLinkSupervisionTimeout(
+ struct rtw_adapter *padapter,
+ struct packet_irp_hcicmd_data *pHciCmd
+ )
+{
+ enum hci_status status = HCI_STATUS_SUCCESS;
+ struct bt_30info *pBTinfo = GET_BT_INFO(padapter);
+ u8 physicalLinkHandle, EntryNum;
+
+ physicalLinkHandle = *((u8 *)pHciCmd->Data);
+
+ EntryNum = bthci_GetCurrentEntryNum(padapter, physicalLinkHandle);
+
+ if (EntryNum == 0xff) {
+ RTPRINT(FIOCTL, IOCTL_BT_HCICMD, ("WriteLinkSupervisionTimeout, No such Handle in the Entry\n"));
+ status = HCI_STATUS_UNKNOW_CONNECT_ID;
+ } else {
+ if (pBTinfo->BtAsocEntry[EntryNum].PhyLinkCmdData.BtPhyLinkhandle != physicalLinkHandle) {
+ status = HCI_STATUS_UNKNOW_CONNECT_ID;
+ } else {
+ pBTinfo->BtAsocEntry[EntryNum].PhyLinkCmdData.LinkSuperversionTimeout = *((u16 *)(((u8 *)pHciCmd->Data)+2));
+ RTPRINT(FIOCTL, IOCTL_STATE, ("BT Write LinkSuperversionTimeout[%d] = 0x%x\n",
+ EntryNum, pBTinfo->BtAsocEntry[EntryNum].PhyLinkCmdData.LinkSuperversionTimeout));
+ }
+ }
+
+ {
+ u8 localBuf[8] = "";
+ u8 *pRetPar;
+ u8 len = 0;
+ struct packet_irp_hcievent_data *PPacketIrpEvent;
+
+ PPacketIrpEvent = (struct packet_irp_hcievent_data *)(&localBuf[0]);
+
+ len += bthci_CommandCompleteHeader(&localBuf[0],
+ OGF_SET_EVENT_MASK_COMMAND,
+ HCI_WRITE_LINK_SUPERVISION_TIMEOUT,
+ status);
+
+ /* Return parameters starts from here */
+ pRetPar = &PPacketIrpEvent->Data[len];
+ pRetPar[0] = status;
+ pRetPar[1] = pBTinfo->BtAsocEntry[EntryNum].PhyLinkCmdData.BtPhyLinkhandle;
+ pRetPar[2] = 0;
+ len += 3;
+ PPacketIrpEvent->Length = len;
+
+ bthci_IndicateEvent(padapter, PPacketIrpEvent, len+2);
+ }
+
+ return status;
+}
+
+static enum hci_status
+bthci_CmdEnhancedFlush(
+ struct rtw_adapter *padapter,
+ struct packet_irp_hcicmd_data *pHciCmd
+ )
+{
+ enum hci_status status = HCI_STATUS_SUCCESS;
+ struct bt_30info *pBTinfo = GET_BT_INFO(padapter);
+ struct bt_hci_info *pBtHciInfo = &pBTinfo->BtHciInfo;
+ u16 logicHandle;
+ u8 Packet_Type;
+
+ logicHandle = *((u16 *)&pHciCmd->Data[0]);
+ Packet_Type = pHciCmd->Data[2];
+
+ if (Packet_Type != 0)
+ status = HCI_STATUS_INVALID_HCI_CMD_PARA_VALUE;
+ else
+ pBtHciInfo->enFlush_LLH = logicHandle;
+
+ if (bthci_DiscardTxPackets(padapter, pBtHciInfo->enFlush_LLH))
+ bthci_EventFlushOccurred(padapter, pBtHciInfo->enFlush_LLH);
+
+ /* should send command status event */
+ bthci_EventCommandStatus(padapter,
+ OGF_SET_EVENT_MASK_COMMAND,
+ HCI_ENHANCED_FLUSH,
+ status);
+
+ if (pBtHciInfo->enFlush_LLH) {
+ bthci_EventEnhancedFlushComplete(padapter, pBtHciInfo->enFlush_LLH);
+ pBtHciInfo->enFlush_LLH = 0;
+ }
+
+ return status;
+}
+
+static enum hci_status
+bthci_CmdReadLogicalLinkAcceptTimeout(
+ struct rtw_adapter *padapter,
+ struct packet_irp_hcicmd_data *pHciCmd
+ )
+{
+ enum hci_status status = HCI_STATUS_SUCCESS;
+/*PMGNT_INFO pMgntInfo = &padapter->MgntInfo; */
+ struct bt_30info *pBTInfo = GET_BT_INFO(padapter);
+ struct bt_hci_info *pBtHciInfo = &pBTInfo->BtHciInfo;
+ u8 localBuf[8] = "";
+ u8 *pRetPar;
+ u8 len = 0;
+ struct packet_irp_hcievent_data *PPacketIrpEvent;
+ u16 *pu2Temp;
+
+ PPacketIrpEvent = (struct packet_irp_hcievent_data *)(&localBuf[0]);
+
+ len += bthci_CommandCompleteHeader(&localBuf[0],
+ OGF_SET_EVENT_MASK_COMMAND,
+ HCI_READ_LOGICAL_LINK_ACCEPT_TIMEOUT,
+ status);
+
+ /* Return parameters starts from here */
+ pRetPar = &PPacketIrpEvent->Data[len];
+ pRetPar[0] = status;
+
+ pu2Temp = (u16 *)&pRetPar[1]; /* Conn_Accept_Timeout */
+ *pu2Temp = pBtHciInfo->LogicalAcceptTimeout;
+ len += 3;
+ PPacketIrpEvent->Length = len;
+
+ bthci_IndicateEvent(padapter, PPacketIrpEvent, len+2);
+
+ return status;
+}
+
+static enum hci_status
+bthci_CmdWriteLogicalLinkAcceptTimeout(
+ struct rtw_adapter *padapter,
+ struct packet_irp_hcicmd_data *pHciCmd
+ )
+{
+ enum hci_status status = HCI_STATUS_SUCCESS;
+/*PMGNT_INFO pMgntInfo = &padapter->MgntInfo; */
+ struct bt_30info *pBTInfo = GET_BT_INFO(padapter);
+ struct bt_hci_info *pBtHciInfo = &pBTInfo->BtHciInfo;
+ u8 localBuf[6] = "";
+ u8 *pRetPar;
+ u8 len = 0;
+ struct packet_irp_hcievent_data *PPacketIrpEvent;
+
+ pBtHciInfo->LogicalAcceptTimeout = *((u16 *)pHciCmd->Data);
+
+ PPacketIrpEvent = (struct packet_irp_hcievent_data *)(&localBuf[0]);
+
+ len += bthci_CommandCompleteHeader(&localBuf[0],
+ OGF_SET_EVENT_MASK_COMMAND,
+ HCI_WRITE_LOGICAL_LINK_ACCEPT_TIMEOUT,
+ status);
+
+ /* Return parameters starts from here */
+ pRetPar = &PPacketIrpEvent->Data[len];
+ pRetPar[0] = status;
+
+ len += 1;
+ PPacketIrpEvent->Length = len;
+
+ bthci_IndicateEvent(padapter, PPacketIrpEvent, len+2);
+ return status;
+}
+
+static enum hci_status
+bthci_CmdSetEventMask(
+ struct rtw_adapter *padapter,
+ struct packet_irp_hcicmd_data *pHciCmd
+ )
+{
+ enum hci_status status = HCI_STATUS_SUCCESS;
+/*PMGNT_INFO pMgntInfo = &padapter->MgntInfo; */
+ struct bt_30info *pBTInfo = GET_BT_INFO(padapter);
+ struct bt_hci_info *pBtHciInfo = &pBTInfo->BtHciInfo;
+ u8 *pu8Temp;
+ u8 localBuf[6] = "";
+ u8 *pRetPar;
+ u8 len = 0;
+ struct packet_irp_hcievent_data *PPacketIrpEvent;
+
+ pu8Temp = (u8 *)&pHciCmd->Data[0];
+ pBtHciInfo->BTEventMask = *pu8Temp;
+ RTPRINT(FIOCTL, IOCTL_BT_HCICMD_DETAIL, ("BTEventMask = 0x%"i64fmt"x\n",
+ pBtHciInfo->BTEventMask));
+
+ /* send command complete event here when all data are received. */
+ PPacketIrpEvent = (struct packet_irp_hcievent_data *)(&localBuf[0]);
+
+ len += bthci_CommandCompleteHeader(&localBuf[0],
+ OGF_SET_EVENT_MASK_COMMAND,
+ HCI_SET_EVENT_MASK,
+ status);
+
+ /* Return parameters starts from here */
+ pRetPar = &PPacketIrpEvent->Data[len];
+ pRetPar[0] = status; /* status */
+ len += 1;
+ PPacketIrpEvent->Length = len;
+
+ bthci_IndicateEvent(padapter, PPacketIrpEvent, len+2);
+
+ return status;
+}
+
+/* 7.3.69 */
+static enum hci_status
+bthci_CmdSetEventMaskPage2(
+ struct rtw_adapter *padapter,
+ struct packet_irp_hcicmd_data *pHciCmd
+ )
+{
+ enum hci_status status = HCI_STATUS_SUCCESS;
+ struct bt_30info *pBTInfo = GET_BT_INFO(padapter);
+ struct bt_hci_info *pBtHciInfo = &pBTInfo->BtHciInfo;
+ u8 *pu8Temp;
+ u8 localBuf[6] = "";
+ u8 *pRetPar;
+ u8 len = 0;
+ struct packet_irp_hcievent_data *PPacketIrpEvent;
+
+ pu8Temp = (u8 *)&pHciCmd->Data[0];
+ pBtHciInfo->BTEventMaskPage2 = *pu8Temp;
+ RTPRINT(FIOCTL, (IOCTL_BT_HCICMD_DETAIL|IOCTL_BT_LOGO), ("BTEventMaskPage2 = 0x%"i64fmt"x\n",
+ pBtHciInfo->BTEventMaskPage2));
+
+ /* send command complete event here when all data are received. */
+ PPacketIrpEvent = (struct packet_irp_hcievent_data *)(&localBuf[0]);
+
+ len += bthci_CommandCompleteHeader(&localBuf[0],
+ OGF_SET_EVENT_MASK_COMMAND,
+ HCI_SET_EVENT_MASK_PAGE_2,
+ status);
+
+ /* Return parameters starts from here */
+ pRetPar = &PPacketIrpEvent->Data[len];
+ pRetPar[0] = status; /* status */
+ len += 1;
+ PPacketIrpEvent->Length = len;
+
+ bthci_IndicateEvent(padapter, PPacketIrpEvent, len+2);
+
+ return status;
+}
+
+static enum hci_status
+bthci_CmdReadLocationData(
+ struct rtw_adapter *padapter,
+ struct packet_irp_hcicmd_data *pHciCmd
+ )
+{
+ enum hci_status status = HCI_STATUS_SUCCESS;
+ struct bt_30info *pBTInfo = GET_BT_INFO(padapter);
+ struct bt_hci_info *pBtHciInfo = &pBTInfo->BtHciInfo;
+ u8 localBuf[12] = "";
+ u8 *pRetPar;
+ u8 len = 0;
+ struct packet_irp_hcievent_data *PPacketIrpEvent;
+ u16 *pu2Temp;
+
+ PPacketIrpEvent = (struct packet_irp_hcievent_data *)(&localBuf[0]);
+
+ len += bthci_CommandCompleteHeader(&localBuf[0],
+ OGF_SET_EVENT_MASK_COMMAND,
+ HCI_READ_LOCATION_DATA,
+ status);
+ RTPRINT(FIOCTL, IOCTL_BT_HCICMD, ("DomainAware = 0x%x\n", pBtHciInfo->LocationDomainAware));
+ RTPRINT(FIOCTL, IOCTL_BT_HCICMD, ("Domain = 0x%x\n", pBtHciInfo->LocationDomain));
+ RTPRINT(FIOCTL, IOCTL_BT_HCICMD, ("DomainOptions = 0x%x\n", pBtHciInfo->LocationDomainOptions));
+ RTPRINT(FIOCTL, IOCTL_BT_HCICMD, ("Options = 0x%x\n", pBtHciInfo->LocationOptions));
+
+ /* Return parameters starts from here */
+ pRetPar = &PPacketIrpEvent->Data[len];
+ pRetPar[0] = status;
+
+ pRetPar[1] = pBtHciInfo->LocationDomainAware; /* 0x0; Location_Domain_Aware */
+ pu2Temp = (u16 *)&pRetPar[2]; /* Location_Domain */
+ *pu2Temp = pBtHciInfo->LocationDomain; /* 0x5858; */
+ pRetPar[4] = pBtHciInfo->LocationDomainOptions; /* 0x58; Location_Domain_Options */
+ pRetPar[5] = pBtHciInfo->LocationOptions; /* 0x0; Location_Options */
+ len += 6;
+ PPacketIrpEvent->Length = len;
+
+ bthci_IndicateEvent(padapter, PPacketIrpEvent, len+2);
+ return status;
+}
+
+static enum hci_status
+bthci_CmdWriteLocationData(
+ struct rtw_adapter *padapter,
+ struct packet_irp_hcicmd_data *pHciCmd
+ )
+{
+ enum hci_status status = HCI_STATUS_SUCCESS;
+ struct bt_30info *pBTInfo = GET_BT_INFO(padapter);
+ struct bt_hci_info *pBtHciInfo = &pBTInfo->BtHciInfo;
+ u16 *pu2Temp;
+ u8 localBuf[6] = "";
+ u8 *pRetPar;
+ u8 len = 0;
+ struct packet_irp_hcievent_data *PPacketIrpEvent;
+
+ pBtHciInfo->LocationDomainAware = pHciCmd->Data[0];
+ pu2Temp = (u16 *)&pHciCmd->Data[1];
+ pBtHciInfo->LocationDomain = *pu2Temp;
+ pBtHciInfo->LocationDomainOptions = pHciCmd->Data[3];
+ pBtHciInfo->LocationOptions = pHciCmd->Data[4];
+ RTPRINT(FIOCTL, IOCTL_BT_HCICMD, ("DomainAware = 0x%x\n", pBtHciInfo->LocationDomainAware));
+ RTPRINT(FIOCTL, IOCTL_BT_HCICMD, ("Domain = 0x%x\n", pBtHciInfo->LocationDomain));
+ RTPRINT(FIOCTL, IOCTL_BT_HCICMD, ("DomainOptions = 0x%x\n", pBtHciInfo->LocationDomainOptions));
+ RTPRINT(FIOCTL, IOCTL_BT_HCICMD, ("Options = 0x%x\n", pBtHciInfo->LocationOptions));
+
+ /* send command complete event here when all data are received. */
+ PPacketIrpEvent = (struct packet_irp_hcievent_data *)(&localBuf[0]);
+
+ len += bthci_CommandCompleteHeader(&localBuf[0],
+ OGF_SET_EVENT_MASK_COMMAND,
+ HCI_WRITE_LOCATION_DATA,
+ status);
+
+ /* Return parameters starts from here */
+ pRetPar = &PPacketIrpEvent->Data[len];
+ pRetPar[0] = status; /* status */
+ len += 1;
+ PPacketIrpEvent->Length = len;
+
+ bthci_IndicateEvent(padapter, PPacketIrpEvent, len+2);
+
+ return status;
+}
+
+static enum hci_status
+bthci_CmdReadFlowControlMode(
+ struct rtw_adapter *padapter,
+ struct packet_irp_hcicmd_data *pHciCmd
+ )
+{
+ enum hci_status status = HCI_STATUS_SUCCESS;
+ struct bt_30info *pBTInfo = GET_BT_INFO(padapter);
+ struct bt_hci_info *pBtHciInfo = &pBTInfo->BtHciInfo;
+ u8 localBuf[7] = "";
+ u8 *pRetPar;
+ u8 len = 0;
+ struct packet_irp_hcievent_data *PPacketIrpEvent;
+
+ PPacketIrpEvent = (struct packet_irp_hcievent_data *)(&localBuf[0]);
+
+ len += bthci_CommandCompleteHeader(&localBuf[0],
+ OGF_SET_EVENT_MASK_COMMAND,
+ HCI_READ_FLOW_CONTROL_MODE,
+ status);
+
+ /* Return parameters starts from here */
+ pRetPar = &PPacketIrpEvent->Data[len];
+ pRetPar[0] = status;
+ pRetPar[1] = pBtHciInfo->FlowControlMode; /* Flow Control Mode */
+ len += 2;
+ PPacketIrpEvent->Length = len;
+
+ bthci_IndicateEvent(padapter, PPacketIrpEvent, len+2);
+ return status;
+}
+
+static enum hci_status
+bthci_CmdWriteFlowControlMode(
+ struct rtw_adapter *padapter,
+ struct packet_irp_hcicmd_data *pHciCmd
+ )
+{
+ enum hci_status status = HCI_STATUS_SUCCESS;
+ struct bt_30info *pBTInfo = GET_BT_INFO(padapter);
+ struct bt_hci_info *pBtHciInfo = &pBTInfo->BtHciInfo;
+ u8 localBuf[6] = "";
+ u8 *pRetPar;
+ u8 len = 0;
+ struct packet_irp_hcievent_data *PPacketIrpEvent;
+
+ pBtHciInfo->FlowControlMode = pHciCmd->Data[0];
+
+ /* send command complete event here when all data are received. */
+ PPacketIrpEvent = (struct packet_irp_hcievent_data *)(&localBuf[0]);
+
+ len += bthci_CommandCompleteHeader(&localBuf[0],
+ OGF_SET_EVENT_MASK_COMMAND,
+ HCI_WRITE_FLOW_CONTROL_MODE,
+ status);
+
+ /* Return parameters starts from here */
+ pRetPar = &PPacketIrpEvent->Data[len];
+ pRetPar[0] = status; /* status */
+ len += 1;
+ PPacketIrpEvent->Length = len;
+
+ bthci_IndicateEvent(padapter, PPacketIrpEvent, len+2);
+
+ return status;
+}
+
+static enum hci_status
+bthci_CmdReadBestEffortFlushTimeout(
+ struct rtw_adapter *padapter,
+ struct packet_irp_hcicmd_data *pHciCmd
+ )
+{
+ enum hci_status status = HCI_STATUS_SUCCESS;
+ struct bt_30info *pBTinfo = GET_BT_INFO(padapter);
+ u16 i, j, logicHandle;
+ u32 BestEffortFlushTimeout = 0xffffffff;
+ u8 find = 0;
+
+ logicHandle = *((u16 *)pHciCmd->Data);
+ /* find an matched logical link index and copy the data */
+ for (j = 0; j < MAX_BT_ASOC_ENTRY_NUM; j++) {
+ for (i = 0; i < MAX_LOGICAL_LINK_NUM; i++) {
+ if (pBTinfo->BtAsocEntry[j].LogLinkCmdData[i].BtLogLinkhandle == logicHandle) {
+ BestEffortFlushTimeout = pBTinfo->BtAsocEntry[j].LogLinkCmdData[i].BestEffortFlushTimeout;
+ find = 1;
+ break;
+ }
+ }
+ }
+
+ if (!find)
+ status = HCI_STATUS_UNKNOW_CONNECT_ID;
+
+ {
+ u8 localBuf[10] = "";
+ u8 *pRetPar;
+ u8 len = 0;
+ struct packet_irp_hcievent_data *PPacketIrpEvent;
+ u32 *pu4Temp;
+
+ PPacketIrpEvent = (struct packet_irp_hcievent_data *)(&localBuf[0]);
+
+ len += bthci_CommandCompleteHeader(&localBuf[0],
+ OGF_SET_EVENT_MASK_COMMAND,
+ HCI_READ_BEST_EFFORT_FLUSH_TIMEOUT,
+ status);
+
+ /* Return parameters starts from here */
+ pRetPar = &PPacketIrpEvent->Data[len];
+ pRetPar[0] = status;
+ pu4Temp = (u32 *)&pRetPar[1]; /* Best_Effort_Flush_Timeout */
+ *pu4Temp = BestEffortFlushTimeout;
+ len += 5;
+ PPacketIrpEvent->Length = len;
+
+ bthci_IndicateEvent(padapter, PPacketIrpEvent, len+2);
+ }
+ return status;
+}
+
+static enum hci_status
+bthci_CmdWriteBestEffortFlushTimeout(
+ struct rtw_adapter *padapter,
+ struct packet_irp_hcicmd_data *pHciCmd
+ )
+{
+ enum hci_status status = HCI_STATUS_SUCCESS;
+ struct bt_30info *pBTinfo = GET_BT_INFO(padapter);
+ u16 i, j, logicHandle;
+ u32 BestEffortFlushTimeout = 0xffffffff;
+ u8 find = 0;
+
+ logicHandle = *((u16 *)pHciCmd->Data);
+ BestEffortFlushTimeout = *((u32 *)(pHciCmd->Data+1));
+
+ /* find an matched logical link index and copy the data */
+ for (j = 0; j < MAX_BT_ASOC_ENTRY_NUM; j++) {
+ for (i = 0; i < MAX_LOGICAL_LINK_NUM; i++) {
+ if (pBTinfo->BtAsocEntry[j].LogLinkCmdData[i].BtLogLinkhandle == logicHandle) {
+ pBTinfo->BtAsocEntry[j].LogLinkCmdData[i].BestEffortFlushTimeout = BestEffortFlushTimeout;
+ find = 1;
+ break;
+ }
+ }
+ }
+
+ if (!find)
+ status = HCI_STATUS_UNKNOW_CONNECT_ID;
+
+ {
+ u8 localBuf[6] = "";
+ u8 *pRetPar;
+ u8 len = 0;
+ struct packet_irp_hcievent_data *PPacketIrpEvent;
+
+ PPacketIrpEvent = (struct packet_irp_hcievent_data *)(&localBuf[0]);
+
+ len += bthci_CommandCompleteHeader(&localBuf[0],
+ OGF_SET_EVENT_MASK_COMMAND,
+ HCI_WRITE_BEST_EFFORT_FLUSH_TIMEOUT,
+ status);
+
+ /* Return parameters starts from here */
+ pRetPar = &PPacketIrpEvent->Data[len];
+ pRetPar[0] = status;
+ len += 1;
+ PPacketIrpEvent->Length = len;
+
+ bthci_IndicateEvent(padapter, PPacketIrpEvent, len+2);
+ }
+ return status;
+}
+
+static enum hci_status
+bthci_CmdShortRangeMode(
+ struct rtw_adapter *padapter,
+ struct packet_irp_hcicmd_data *pHciCmd
+ )
+{
+ enum hci_status status = HCI_STATUS_SUCCESS;
+ struct bt_30info *pBTInfo = GET_BT_INFO(padapter);
+ u8 PhyLinkHandle, EntryNum, ShortRangeMode;
+
+ PhyLinkHandle = pHciCmd->Data[0];
+ ShortRangeMode = pHciCmd->Data[1];
+ RTPRINT(FIOCTL, IOCTL_BT_HCICMD, ("PLH = 0x%x, Short_Range_Mode = 0x%x\n", PhyLinkHandle, ShortRangeMode));
+
+ EntryNum = bthci_GetCurrentEntryNum(padapter, PhyLinkHandle);
+ if (EntryNum != 0xff) {
+ pBTInfo->BtAsocEntry[EntryNum].ShortRangeMode = ShortRangeMode;
+ } else {
+ RTPRINT(FIOCTL, IOCTL_BT_HCICMD, ("No such PLH(0x%x)\n", PhyLinkHandle));
+ status = HCI_STATUS_UNKNOW_CONNECT_ID;
+ }
+
+ bthci_EventCommandStatus(padapter,
+ OGF_SET_EVENT_MASK_COMMAND,
+ HCI_SHORT_RANGE_MODE,
+ status);
+
+ bthci_EventShortRangeModeChangeComplete(padapter, status, ShortRangeMode, EntryNum);
+
+ return status;
+}
+
+static enum hci_status bthci_CmdReadLocalSupportedCommands(struct rtw_adapter *padapter)
+{
+ enum hci_status status = HCI_STATUS_SUCCESS;
+ u8 localBuf[TmpLocalBufSize] = "";
+ u8 *pRetPar, *pSupportedCmds;
+ u8 len = 0;
+ struct packet_irp_hcievent_data *PPacketIrpEvent;
+
+ /* send command complete event here when all data are received. */
+ PlatformZeroMemory(&localBuf[0], TmpLocalBufSize);
+ PPacketIrpEvent = (struct packet_irp_hcievent_data *)(&localBuf[0]);
+
+ len += bthci_CommandCompleteHeader(&localBuf[0],
+ OGF_INFORMATIONAL_PARAMETERS,
+ HCI_READ_LOCAL_SUPPORTED_COMMANDS,
+ status);
+
+ /* Return parameters starts from here */
+ pRetPar = &PPacketIrpEvent->Data[len];
+ pRetPar[0] = status; /* status */
+ len += 1;
+ pSupportedCmds = &pRetPar[1];
+ RTPRINT(FIOCTL, (IOCTL_BT_HCICMD|IOCTL_BT_LOGO), ("Octet[5]= 0xc0\nBit [6]= Set Event Mask, [7]= Reset\n"));
+ pSupportedCmds[5] = 0xc0;
+ RTPRINT(FIOCTL, (IOCTL_BT_HCICMD|IOCTL_BT_LOGO), ("Octet[6]= 0x01\nBit [0]= Set Event Filter\n"));
+ pSupportedCmds[6] = 0x01;
+ RTPRINT(FIOCTL, (IOCTL_BT_HCICMD|IOCTL_BT_LOGO), ("Octet[7]= 0x0c\nBit [2]= Read Connection Accept Timeout, [3]= Write Connection Accept Timeout\n"));
+ pSupportedCmds[7] = 0x0c;
+ RTPRINT(FIOCTL, (IOCTL_BT_HCICMD|IOCTL_BT_LOGO), ("Octet[10]= 0x80\nBit [7]= Host Number Of Completed Packets\n"));
+ pSupportedCmds[10] = 0x80;
+ RTPRINT(FIOCTL, (IOCTL_BT_HCICMD|IOCTL_BT_LOGO), ("Octet[11]= 0x03\nBit [0]= Read Link Supervision Timeout, [1]= Write Link Supervision Timeout\n"));
+ pSupportedCmds[11] = 0x03;
+ RTPRINT(FIOCTL, (IOCTL_BT_HCICMD|IOCTL_BT_LOGO), ("Octet[14]= 0xa8\nBit [3]= Read Local Version Information, [5]= Read Local Supported Features, [7]= Read Buffer Size\n"));
+ pSupportedCmds[14] = 0xa8;
+ RTPRINT(FIOCTL, (IOCTL_BT_HCICMD|IOCTL_BT_LOGO), ("Octet[15]= 0x1c\nBit [2]= Read Failed Contact Count, [3]= Reset Failed Contact Count, [4]= Get Link Quality\n"));
+ pSupportedCmds[15] = 0x1c;
+ /* pSupportedCmds[16] = 0x04; */
+ RTPRINT(FIOCTL, (IOCTL_BT_HCICMD|IOCTL_BT_LOGO), ("Octet[19]= 0x40\nBit [6]= Enhanced Flush\n"));
+ pSupportedCmds[19] = 0x40;
+ RTPRINT(FIOCTL, (IOCTL_BT_HCICMD|IOCTL_BT_LOGO), ("Octet[21]= 0xff\nBit [0]= Create Physical Link, [1]= Accept Physical Link, [2]= Disconnect Physical Link, [3]= Create Logical Link\n"));
+ RTPRINT(FIOCTL, (IOCTL_BT_HCICMD|IOCTL_BT_LOGO), (" [4]= Accept Logical Link, [5]= Disconnect Logical Link, [6]= Logical Link Cancel, [7]= Flow Spec Modify\n"));
+ pSupportedCmds[21] = 0xff;
+ RTPRINT(FIOCTL, (IOCTL_BT_HCICMD|IOCTL_BT_LOGO), ("Octet[22]= 0xff\nBit [0]= Read Logical Link Accept Timeout, [1]= Write Logical Link Accept Timeout, [2]= Set Event Mask Page 2, [3]= Read Location Data\n"));
+ RTPRINT(FIOCTL, (IOCTL_BT_HCICMD|IOCTL_BT_LOGO), (" [4]= Write Location Data, [5]= Read Local AMP Info, [6]= Read Local AMP_ASSOC, [7]= Write Remote AMP_ASSOC\n"));
+ pSupportedCmds[22] = 0xff;
+ RTPRINT(FIOCTL, (IOCTL_BT_HCICMD|IOCTL_BT_LOGO), ("Octet[23]= 0x07\nBit [0]= Read Flow Control Mode, [1]= Write Flow Control Mode, [2]= Read Data Block Size\n"));
+ pSupportedCmds[23] = 0x07;
+ RTPRINT(FIOCTL, (IOCTL_BT_HCICMD|IOCTL_BT_LOGO), ("Octet[24]= 0x1c\nBit [2]= Read Best Effort Flush Timeout, [3]= Write Best Effort Flush Timeout, [4]= Short Range Mode\n"));
+ pSupportedCmds[24] = 0x1c;
+ len += 64;
+ PPacketIrpEvent->Length = len;
+
+ bthci_IndicateEvent(padapter, PPacketIrpEvent, len+2);
+
+ return status;
+}
+
+static enum hci_status bthci_CmdReadLocalSupportedFeatures(struct rtw_adapter *padapter)
+{
+ enum hci_status status = HCI_STATUS_SUCCESS;
+ u8 localBuf[TmpLocalBufSize] = "";
+ u8 *pRetPar;
+ u8 len = 0;
+ struct packet_irp_hcievent_data *PPacketIrpEvent;
+
+ /* send command complete event here when all data are received. */
+ PlatformZeroMemory(&localBuf[0], TmpLocalBufSize);
+ PPacketIrpEvent = (struct packet_irp_hcievent_data *)(&localBuf[0]);
+
+ len += bthci_CommandCompleteHeader(&localBuf[0],
+ OGF_INFORMATIONAL_PARAMETERS,
+ HCI_READ_LOCAL_SUPPORTED_FEATURES,
+ status);
+
+ /* Return parameters starts from here */
+ pRetPar = &PPacketIrpEvent->Data[len];
+ pRetPar[0] = status; /* status */
+ len += 9;
+ PPacketIrpEvent->Length = len;
+
+ bthci_IndicateEvent(padapter, PPacketIrpEvent, len+2);
+ return status;
+}
+
+static enum hci_status bthci_CmdReadLocalAMPAssoc(struct rtw_adapter *padapter,
+ struct packet_irp_hcicmd_data *pHciCmd)
+{
+ enum hci_status status = HCI_STATUS_SUCCESS;
+ struct bt_30info *pBTInfo = GET_BT_INFO(padapter);
+ struct bt_mgnt *pBtMgnt = &pBTInfo->BtMgnt;
+ struct bt_dgb *pBtDbg = &pBTInfo->BtDbg;
+ u8 PhyLinkHandle, EntryNum;
+
+ pBtDbg->dbgHciInfo.hciCmdCntReadLocalAmpAssoc++;
+ PhyLinkHandle = *((u8 *)pHciCmd->Data);
+ EntryNum = bthci_GetCurrentEntryNum(padapter, PhyLinkHandle);
+
+ if ((EntryNum == 0xff) && PhyLinkHandle != 0) {
+ RTPRINT(FIOCTL, IOCTL_BT_HCICMD, ("ReadLocalAMPAssoc, EntryNum = %d !!!!!, physical link handle = 0x%x\n",
+ EntryNum, PhyLinkHandle));
+ status = HCI_STATUS_UNKNOW_CONNECT_ID;
+ } else if (pBtMgnt->bPhyLinkInProgressStartLL) {
+ status = HCI_STATUS_UNKNOW_CONNECT_ID;
+ pBtMgnt->bPhyLinkInProgressStartLL = false;
+ } else {
+ pBTInfo->BtAsocEntry[EntryNum].AmpAsocCmdData.BtPhyLinkhandle = *((u8 *)pHciCmd->Data);
+ pBTInfo->BtAsocEntry[EntryNum].AmpAsocCmdData.LenSoFar = *((u16 *)((u8 *)pHciCmd->Data+1));
+ pBTInfo->BtAsocEntry[EntryNum].AmpAsocCmdData.MaxRemoteASSOCLen = *((u16 *)((u8 *)pHciCmd->Data+3));
+ RTPRINT(FIOCTL, IOCTL_BT_HCICMD_DETAIL, ("ReadLocalAMPAssoc, LenSoFar =%d, MaxRemoteASSOCLen =%d\n",
+ pBTInfo->BtAsocEntry[EntryNum].AmpAsocCmdData.LenSoFar,
+ pBTInfo->BtAsocEntry[EntryNum].AmpAsocCmdData.MaxRemoteASSOCLen));
+ }
+
+ RTPRINT(FIOCTL, IOCTL_BT_HCICMD, ("ReadLocalAMPAssoc, EntryNum = %d !!!!!, physical link handle = 0x%x, LengthSoFar = %x \n",
+ EntryNum, PhyLinkHandle, pBTInfo->BtAsocEntry[EntryNum].AmpAsocCmdData.LenSoFar));
+
+ /* send command complete event here when all data are received. */
+ {
+ struct packet_irp_hcievent_data *PPacketIrpEvent;
+
+ /* PVOID buffer = padapter->IrpHCILocalbuf.Ptr; */
+ u8 localBuf[TmpLocalBufSize] = "";
+ u16 *pRemainLen;
+ u32 totalLen = 0;
+ u16 typeLen = 0, remainLen = 0, ret_index = 0;
+ u8 *pRetPar;
+
+ PlatformZeroMemory(&localBuf[0], TmpLocalBufSize);
+ /* PPacketIrpEvent = (struct packet_irp_hcievent_data *)(buffer); */
+ PPacketIrpEvent = (struct packet_irp_hcievent_data *)(&localBuf[0]);
+
+ totalLen += bthci_CommandCompleteHeader(&localBuf[0],
+ OGF_STATUS_PARAMETERS,
+ HCI_READ_LOCAL_AMP_ASSOC,
+ status);
+ RTPRINT(FIOCTL, IOCTL_BT_HCICMD, ("ReadLocalAMPAssoc, Remaining_Len =%d \n", remainLen));
+ /* Return parameters starts from here */
+ pRetPar = &PPacketIrpEvent->Data[totalLen];
+ pRetPar[0] = status; /* status */
+ pRetPar[1] = *((u8 *)pHciCmd->Data);
+ pRemainLen = (u16 *)&pRetPar[2]; /* AMP_ASSOC_Remaining_Length */
+ totalLen += 4; /* 0]~[3] */
+ ret_index = 4;
+
+ typeLen = bthci_AssocMACAddr(padapter, &pRetPar[ret_index]);
+ totalLen += typeLen;
+ remainLen += typeLen;
+ ret_index += typeLen;
+ typeLen = bthci_AssocPreferredChannelList(padapter, &pRetPar[ret_index], EntryNum);
+ totalLen += typeLen;
+ remainLen += typeLen;
+ ret_index += typeLen;
+ typeLen = bthci_PALCapabilities(padapter, &pRetPar[ret_index]);
+ totalLen += typeLen;
+ remainLen += typeLen;
+ ret_index += typeLen;
+ typeLen = bthci_AssocPALVer(padapter, &pRetPar[ret_index]);
+ totalLen += typeLen;
+ remainLen += typeLen;
+ PPacketIrpEvent->Length = (u8)totalLen;
+ *pRemainLen = remainLen; /* AMP_ASSOC_Remaining_Length */
+ RTPRINT(FIOCTL, IOCTL_BT_HCICMD, ("ReadLocalAMPAssoc, Remaining_Len =%d \n", remainLen));
+ RTPRINT_DATA(FIOCTL, (IOCTL_BT_HCICMD_DETAIL|IOCTL_BT_LOGO), ("AMP_ASSOC_fragment : \n"), PPacketIrpEvent->Data, totalLen);
+
+ bthci_IndicateEvent(padapter, PPacketIrpEvent, totalLen+2);
+ }
+
+ return status;
+}
+
+static enum hci_status bthci_CmdReadFailedContactCounter(struct rtw_adapter *padapter,
+ struct packet_irp_hcicmd_data *pHciCmd)
+{
+
+ enum hci_status status = HCI_STATUS_SUCCESS;
+ struct bt_30info *pBTInfo = GET_BT_INFO(padapter);
+ struct bt_hci_info *pBtHciInfo = &pBTInfo->BtHciInfo;
+ u8 localBuf[TmpLocalBufSize] = "";
+ u8 *pRetPar;
+ u8 len = 0;
+ struct packet_irp_hcievent_data *PPacketIrpEvent;
+ u16 handle;
+
+ handle = *((u16 *)pHciCmd->Data);
+ /* send command complete event here when all data are received. */
+ PlatformZeroMemory(&localBuf[0], TmpLocalBufSize);
+ PPacketIrpEvent = (struct packet_irp_hcievent_data *)(&localBuf[0]);
+
+ len += bthci_CommandCompleteHeader(&localBuf[0],
+ OGF_STATUS_PARAMETERS,
+ HCI_READ_FAILED_CONTACT_COUNTER,
+ status);
+
+ /* Return parameters starts from here */
+ pRetPar = &PPacketIrpEvent->Data[len];
+ pRetPar[0] = status; /* status */
+ pRetPar[1] = TWOBYTE_LOWBYTE(handle);
+ pRetPar[2] = TWOBYTE_HIGHTBYTE(handle);
+ pRetPar[3] = TWOBYTE_LOWBYTE(pBtHciInfo->FailContactCount);
+ pRetPar[4] = TWOBYTE_HIGHTBYTE(pBtHciInfo->FailContactCount);
+ len += 5;
+ PPacketIrpEvent->Length = len;
+
+ bthci_IndicateEvent(padapter, PPacketIrpEvent, len+2);
+
+ return status;
+}
+
+static enum hci_status
+bthci_CmdResetFailedContactCounter(
+ struct rtw_adapter *padapter,
+ struct packet_irp_hcicmd_data *pHciCmd
+ )
+{
+ enum hci_status status = HCI_STATUS_SUCCESS;
+/*PMGNT_INFO pMgntInfo = &padapter->MgntInfo; */
+ struct bt_30info *pBTInfo = GET_BT_INFO(padapter);
+ struct bt_hci_info *pBtHciInfo = &pBTInfo->BtHciInfo;
+ u16 handle;
+ u8 localBuf[TmpLocalBufSize] = "";
+ u8 *pRetPar;
+ u8 len = 0;
+ struct packet_irp_hcievent_data *PPacketIrpEvent;
+
+ handle = *((u16 *)pHciCmd->Data);
+ pBtHciInfo->FailContactCount = 0;
+
+ /* send command complete event here when all data are received. */
+ PlatformZeroMemory(&localBuf[0], TmpLocalBufSize);
+ /* PPacketIrpEvent = (struct packet_irp_hcievent_data *)(buffer); */
+ PPacketIrpEvent = (struct packet_irp_hcievent_data *)(&localBuf[0]);
+
+ len += bthci_CommandCompleteHeader(&localBuf[0],
+ OGF_STATUS_PARAMETERS,
+ HCI_RESET_FAILED_CONTACT_COUNTER,
+ status);
+
+ /* Return parameters starts from here */
+ pRetPar = &PPacketIrpEvent->Data[len];
+ pRetPar[0] = status; /* status */
+ pRetPar[1] = TWOBYTE_LOWBYTE(handle);
+ pRetPar[2] = TWOBYTE_HIGHTBYTE(handle);
+ len += 3;
+ PPacketIrpEvent->Length = len;
+
+ bthci_IndicateEvent(padapter, PPacketIrpEvent, len+2);
+ return status;
+}
+
+/* */
+/* BT 3.0+HS [Vol 2] 7.4.1 */
+/* */
+static enum hci_status
+bthci_CmdReadLocalVersionInformation(
+ struct rtw_adapter *padapter
+ )
+{
+ enum hci_status status = HCI_STATUS_SUCCESS;
+ /* send command complete event here when all data are received. */
+ u8 localBuf[TmpLocalBufSize] = "";
+ u8 *pRetPar;
+ u8 len = 0;
+ struct packet_irp_hcievent_data *PPacketIrpEvent;
+ u16 *pu2Temp;
+
+ PlatformZeroMemory(&localBuf[0], TmpLocalBufSize);
+ PPacketIrpEvent = (struct packet_irp_hcievent_data *)(&localBuf[0]);
+
+ len += bthci_CommandCompleteHeader(&localBuf[0],
+ OGF_INFORMATIONAL_PARAMETERS,
+ HCI_READ_LOCAL_VERSION_INFORMATION,
+ status);
+
+ /* Return parameters starts from here */
+ pRetPar = &PPacketIrpEvent->Data[len];
+ pRetPar[0] = status; /* status */
+ pRetPar[1] = 0x05; /* HCI_Version */
+ pu2Temp = (u16 *)&pRetPar[2]; /* HCI_Revision */
+ *pu2Temp = 0x0001;
+ pRetPar[4] = 0x05; /* LMP/PAL_Version */
+ pu2Temp = (u16 *)&pRetPar[5]; /* Manufacturer_Name */
+ *pu2Temp = 0x005d;
+ pu2Temp = (u16 *)&pRetPar[7]; /* LMP/PAL_Subversion */
+ *pu2Temp = 0x0001;
+ len += 9;
+ PPacketIrpEvent->Length = len;
+
+ RTPRINT(FIOCTL, (IOCTL_BT_HCICMD_DETAIL|IOCTL_BT_LOGO), ("LOCAL_VERSION_INFORMATION\n"));
+ RTPRINT(FIOCTL, (IOCTL_BT_HCICMD_DETAIL|IOCTL_BT_LOGO), ("Status %x\n", status));
+ RTPRINT(FIOCTL, (IOCTL_BT_HCICMD_DETAIL|IOCTL_BT_LOGO), ("HCI_Version = 0x05\n"));
+ RTPRINT(FIOCTL, (IOCTL_BT_HCICMD_DETAIL|IOCTL_BT_LOGO), ("HCI_Revision = 0x0001\n"));
+ RTPRINT(FIOCTL, (IOCTL_BT_HCICMD_DETAIL|IOCTL_BT_LOGO), ("LMP/PAL_Version = 0x05\n"));
+ RTPRINT(FIOCTL, (IOCTL_BT_HCICMD_DETAIL|IOCTL_BT_LOGO), ("Manufacturer_Name = 0x0001\n"));
+ RTPRINT(FIOCTL, (IOCTL_BT_HCICMD_DETAIL|IOCTL_BT_LOGO), ("LMP/PAL_Subversion = 0x0001\n"));
+
+ bthci_IndicateEvent(padapter, PPacketIrpEvent, len+2);
+
+ return status;
+}
+
+/* 7.4.7 */
+static enum hci_status bthci_CmdReadDataBlockSize(struct rtw_adapter *padapter)
+{
+ enum hci_status status = HCI_STATUS_SUCCESS;
+ u8 localBuf[TmpLocalBufSize] = "";
+ u8 *pRetPar;
+ u8 len = 0;
+ struct packet_irp_hcievent_data *PPacketIrpEvent;
+ u16 *pu2Temp;
+
+ PlatformZeroMemory(&localBuf[0], TmpLocalBufSize);
+ PPacketIrpEvent = (struct packet_irp_hcievent_data *)(&localBuf[0]);
+
+ len += bthci_CommandCompleteHeader(&localBuf[0],
+ OGF_INFORMATIONAL_PARAMETERS,
+ HCI_READ_DATA_BLOCK_SIZE,
+ status);
+
+ /* Return parameters starts from here */
+ pRetPar = &PPacketIrpEvent->Data[len];
+ pRetPar[0] = HCI_STATUS_SUCCESS; /* status */
+ pu2Temp = (u16 *)&pRetPar[1]; /* Max_ACL_Data_Packet_Length */
+ *pu2Temp = Max80211PALPDUSize;
+
+ pu2Temp = (u16 *)&pRetPar[3]; /* Data_Block_Length */
+ *pu2Temp = Max80211PALPDUSize;
+ pu2Temp = (u16 *)&pRetPar[5]; /* Total_Num_Data_Blocks */
+ *pu2Temp = BTTotalDataBlockNum;
+ len += 7;
+ PPacketIrpEvent->Length = len;
+
+ bthci_IndicateEvent(padapter, PPacketIrpEvent, len+2);
+
+ return status;
+}
+
+/* 7.4.5 */
+static enum hci_status bthci_CmdReadBufferSize(struct rtw_adapter *padapter)
+{
+ enum hci_status status = HCI_STATUS_SUCCESS;
+ u8 localBuf[TmpLocalBufSize] = "";
+ u8 *pRetPar;
+ u8 len = 0;
+ struct packet_irp_hcievent_data *PPacketIrpEvent;
+ u16 *pu2Temp;
+
+ PlatformZeroMemory(&localBuf[0], TmpLocalBufSize);
+ /* PPacketIrpEvent = (struct packet_irp_hcievent_data *)(buffer); */
+ PPacketIrpEvent = (struct packet_irp_hcievent_data *)(&localBuf[0]);
+
+ len += bthci_CommandCompleteHeader(&localBuf[0],
+ OGF_INFORMATIONAL_PARAMETERS,
+ HCI_READ_BUFFER_SIZE,
+ status);
+ RTPRINT(FIOCTL, IOCTL_BT_HCICMD, ("Synchronous_Data_Packet_Length = 0x%x\n", BTSynDataPacketLength));
+ RTPRINT(FIOCTL, IOCTL_BT_HCICMD, ("Total_Num_ACL_Data_Packets = 0x%x\n", BTTotalDataBlockNum));
+ RTPRINT(FIOCTL, IOCTL_BT_HCICMD, ("Total_Num_Synchronous_Data_Packets = 0x%x\n", BTTotalDataBlockNum));
+ /* Return parameters starts from here */
+ pRetPar = &PPacketIrpEvent->Data[len];
+ pRetPar[0] = status; /* status */
+ pu2Temp = (u16 *)&pRetPar[1]; /* HC_ACL_Data_Packet_Length */
+ *pu2Temp = Max80211PALPDUSize;
+
+ pRetPar[3] = BTSynDataPacketLength; /* HC_Synchronous_Data_Packet_Length */
+ pu2Temp = (u16 *)&pRetPar[4]; /* HC_Total_Num_ACL_Data_Packets */
+ *pu2Temp = BTTotalDataBlockNum;
+ pu2Temp = (u16 *)&pRetPar[6]; /* HC_Total_Num_Synchronous_Data_Packets */
+ *pu2Temp = BTTotalDataBlockNum;
+ len += 8;
+ PPacketIrpEvent->Length = len;
+
+ bthci_IndicateEvent(padapter, PPacketIrpEvent, len+2);
+
+ return status;
+}
+
+static enum hci_status bthci_CmdReadLocalAMPInfo(struct rtw_adapter *padapter)
+{
+ enum hci_status status = HCI_STATUS_SUCCESS;
+ struct pwrctrl_priv *ppwrctrl = &padapter->pwrctrlpriv;
+ u8 localBuf[TmpLocalBufSize] = "";
+ u8 *pRetPar;
+ u8 len = 0;
+ struct packet_irp_hcievent_data *PPacketIrpEvent;
+ u16 *pu2Temp;
+ u32 *pu4Temp;
+ u32 TotalBandwidth = BTTOTALBANDWIDTH, MaxBandGUBandwidth = BTMAXBANDGUBANDWIDTH;
+ u8 ControlType = 0x01, AmpStatus = 0x01;
+ u32 MaxFlushTimeout = 10000, BestEffortFlushTimeout = 5000;
+ u16 MaxPDUSize = Max80211PALPDUSize, PalCap = 0x1, AmpAssocLen = Max80211AMPASSOCLen, MinLatency = 20;
+
+ if ((ppwrctrl->rfoff_reason & RF_CHANGE_BY_HW) ||
+ (ppwrctrl->rfoff_reason & RF_CHANGE_BY_SW)) {
+ AmpStatus = AMP_STATUS_NO_CAPACITY_FOR_BT;
+ }
+
+ PlatformZeroMemory(&localBuf[0], TmpLocalBufSize);
+ /* PPacketIrpEvent = (struct packet_irp_hcievent_data *)(buffer); */
+ PPacketIrpEvent = (struct packet_irp_hcievent_data *)(&localBuf[0]);
+
+ len += bthci_CommandCompleteHeader(&localBuf[0],
+ OGF_STATUS_PARAMETERS,
+ HCI_READ_LOCAL_AMP_INFO,
+ status);
+
+ /* Return parameters starts from here */
+ pRetPar = &PPacketIrpEvent->Data[len];
+ pRetPar[0] = status; /* status */
+ pRetPar[1] = AmpStatus; /* AMP_Status */
+ pu4Temp = (u32 *)&pRetPar[2]; /* Total_Bandwidth */
+ *pu4Temp = TotalBandwidth; /* 0x19bfcc00;0x7530; */
+ pu4Temp = (u32 *)&pRetPar[6]; /* Max_Guaranteed_Bandwidth */
+ *pu4Temp = MaxBandGUBandwidth; /* 0x19bfcc00;0x4e20; */
+ pu4Temp = (u32 *)&pRetPar[10]; /* Min_Latency */
+ *pu4Temp = MinLatency; /* 150; */
+ pu4Temp = (u32 *)&pRetPar[14]; /* Max_PDU_Size */
+ *pu4Temp = MaxPDUSize;
+ pRetPar[18] = ControlType; /* Controller_Type */
+ pu2Temp = (u16 *)&pRetPar[19]; /* PAL_Capabilities */
+ *pu2Temp = PalCap;
+ pu2Temp = (u16 *)&pRetPar[21]; /* AMP_ASSOC_Length */
+ *pu2Temp = AmpAssocLen;
+ pu4Temp = (u32 *)&pRetPar[23]; /* Max_Flush_Timeout */
+ *pu4Temp = MaxFlushTimeout;
+ pu4Temp = (u32 *)&pRetPar[27]; /* Best_Effort_Flush_Timeout */
+ *pu4Temp = BestEffortFlushTimeout;
+ len += 31;
+ PPacketIrpEvent->Length = len;
+ RTPRINT(FIOCTL, IOCTL_BT_HCICMD, ("AmpStatus = 0x%x\n",
+ AmpStatus));
+ RTPRINT(FIOCTL, IOCTL_BT_HCICMD, ("TotalBandwidth = 0x%x, MaxBandGUBandwidth = 0x%x, MinLatency = 0x%x, \n MaxPDUSize = 0x%x, ControlType = 0x%x\n",
+ TotalBandwidth, MaxBandGUBandwidth, MinLatency, MaxPDUSize, ControlType));
+ RTPRINT(FIOCTL, IOCTL_BT_HCICMD, ("PalCap = 0x%x, AmpAssocLen = 0x%x, MaxFlushTimeout = 0x%x, BestEffortFlushTimeout = 0x%x\n",
+ PalCap, AmpAssocLen, MaxFlushTimeout, BestEffortFlushTimeout));
+ bthci_IndicateEvent(padapter, PPacketIrpEvent, len+2);
+ return status;
+}
+
+static enum hci_status
+bthci_CmdCreatePhysicalLink(
+ struct rtw_adapter *padapter,
+ struct packet_irp_hcicmd_data *pHciCmd
+ )
+{
+ enum hci_status status;
+ struct bt_30info *pBTInfo = GET_BT_INFO(padapter);
+ struct bt_dgb *pBtDbg = &pBTInfo->BtDbg;
+
+ pBtDbg->dbgHciInfo.hciCmdCntCreatePhyLink++;
+
+ status = bthci_BuildPhysicalLink(padapter,
+ pHciCmd, HCI_CREATE_PHYSICAL_LINK);
+
+ return status;
+}
+
+static enum hci_status
+bthci_CmdReadLinkQuality(
+ struct rtw_adapter *padapter,
+ struct packet_irp_hcicmd_data *pHciCmd
+ )
+{
+ enum hci_status status = HCI_STATUS_SUCCESS;
+ struct bt_30info *pBTInfo = GET_BT_INFO(padapter);
+ u16 PLH;
+ u8 EntryNum, LinkQuality = 0x55;
+
+ PLH = *((u16 *)&pHciCmd->Data[0]);
+ RTPRINT(FIOCTL, IOCTL_BT_HCICMD, ("PLH = 0x%x\n", PLH));
+
+ EntryNum = bthci_GetCurrentEntryNum(padapter, (u8)PLH);
+ if (EntryNum == 0xff) {
+ RTPRINT(FIOCTL, IOCTL_BT_HCICMD, ("No such PLH(0x%x)\n", PLH));
+ status = HCI_STATUS_UNKNOW_CONNECT_ID;
+ }
+
+ {
+ u8 localBuf[11] = "";
+ u8 *pRetPar;
+ u8 len = 0;
+ struct packet_irp_hcievent_data *PPacketIrpEvent;
+
+ PPacketIrpEvent = (struct packet_irp_hcievent_data *)(&localBuf[0]);
+
+ len += bthci_CommandCompleteHeader(&localBuf[0],
+ OGF_STATUS_PARAMETERS,
+ HCI_READ_LINK_QUALITY,
+ status);
+
+ RTPRINT(FIOCTL, IOCTL_BT_HCICMD, (" PLH = 0x%x\n Link Quality = 0x%x\n", PLH, LinkQuality));
+
+ /* Return parameters starts from here */
+ pRetPar = &PPacketIrpEvent->Data[len];
+ pRetPar[0] = status; /* status */
+ *((u16 *)&pRetPar[1]) = pBTInfo->BtAsocEntry[EntryNum].PhyLinkCmdData.BtPhyLinkhandle; /* Handle */
+ pRetPar[3] = 0x55; /* Link Quailty */
+ len += 4;
+ PPacketIrpEvent->Length = len;
+
+ bthci_IndicateEvent(padapter, PPacketIrpEvent, len+2);
+ }
+
+ return status;
+}
+
+static enum hci_status
+bthci_CmdCreateLogicalLink(
+ struct rtw_adapter *padapter,
+ struct packet_irp_hcicmd_data *pHciCmd
+ )
+{
+ struct bt_30info *pBTInfo = GET_BT_INFO(padapter);
+ struct bt_dgb *pBtDbg = &pBTInfo->BtDbg;
+
+ pBtDbg->dbgHciInfo.hciCmdCntCreateLogLink++;
+
+ bthci_BuildLogicalLink(padapter, pHciCmd,
+ HCI_CREATE_LOGICAL_LINK);
+
+ return HCI_STATUS_SUCCESS;
+}
+
+static enum hci_status
+bthci_CmdAcceptLogicalLink(
+ struct rtw_adapter *padapter,
+ struct packet_irp_hcicmd_data *pHciCmd
+ )
+{
+ struct bt_30info *pBTInfo = GET_BT_INFO(padapter);
+ struct bt_dgb *pBtDbg = &pBTInfo->BtDbg;
+
+ pBtDbg->dbgHciInfo.hciCmdCntAcceptLogLink++;
+
+ bthci_BuildLogicalLink(padapter, pHciCmd,
+ HCI_ACCEPT_LOGICAL_LINK);
+
+ return HCI_STATUS_SUCCESS;
+}
+
+static enum hci_status
+bthci_CmdDisconnectLogicalLink(
+ struct rtw_adapter *padapter,
+ struct packet_irp_hcicmd_data *pHciCmd
+ )
+{
+ enum hci_status status = HCI_STATUS_SUCCESS;
+/*PMGNT_INFO pMgntInfo = &padapter->MgntInfo; */
+ struct bt_30info *pBTinfo = GET_BT_INFO(padapter);
+ struct bt_mgnt *pBtMgnt = &pBTinfo->BtMgnt;
+ struct bt_dgb *pBtDbg = &pBTinfo->BtDbg;
+ u16 logicHandle;
+ u8 i, j, find = 0, LogLinkCount = 0;
+
+ pBtDbg->dbgHciInfo.hciCmdCntDisconnectLogLink++;
+
+ logicHandle = *((u16 *)pHciCmd->Data);
+ RTPRINT(FIOCTL, IOCTL_BT_HCICMD, ("DisconnectLogicalLink, logicHandle = 0x%x\n", logicHandle));
+
+ /* find an created logical link index and clear the data */
+ for (j = 0; j < MAX_BT_ASOC_ENTRY_NUM; j++) {
+ for (i = 0; i < MAX_LOGICAL_LINK_NUM; i++) {
+ if (pBTinfo->BtAsocEntry[j].LogLinkCmdData[i].BtLogLinkhandle == logicHandle) {
+ RTPRINT(FIOCTL, IOCTL_BT_HCICMD, ("DisconnectLogicalLink, logicHandle is matched 0x%x\n", logicHandle));
+ bthci_ResetFlowSpec(padapter, j, i);
+ find = 1;
+ pBtMgnt->DisconnectEntryNum = j;
+ break;
+ }
+ }
+ }
+
+ if (!find)
+ status = HCI_STATUS_UNKNOW_CONNECT_ID;
+
+ /* To check each */
+ for (i = 0; i < MAX_LOGICAL_LINK_NUM; i++) {
+ if (pBTinfo->BtAsocEntry[pBtMgnt->DisconnectEntryNum].LogLinkCmdData[i].BtLogLinkhandle != 0)
+ LogLinkCount++;
+ }
+
+ /* When we receive Create logical link command, we should send command status event first. */
+ bthci_EventCommandStatus(padapter,
+ LINK_CONTROL_COMMANDS,
+ HCI_DISCONNECT_LOGICAL_LINK,
+ status);
+ /* */
+ /* When we determines the logical link is established, we should send command complete event. */
+ /* */
+ if (status == HCI_STATUS_SUCCESS) {
+ bthci_EventDisconnectLogicalLinkComplete(padapter, status,
+ logicHandle, HCI_STATUS_CONNECT_TERMINATE_LOCAL_HOST);
+ }
+
+ if (LogLinkCount == 0)
+ mod_timer(&pBTinfo->BTDisconnectPhyLinkTimer,
+ jiffies + msecs_to_jiffies(100));
+
+ return status;
+}
+
+static enum hci_status
+bthci_CmdLogicalLinkCancel(struct rtw_adapter *padapter,
+ struct packet_irp_hcicmd_data *pHciCmd)
+{
+ enum hci_status status = HCI_STATUS_SUCCESS;
+ struct bt_30info *pBTinfo = GET_BT_INFO(padapter);
+ struct bt_mgnt *pBtMgnt = &pBTinfo->BtMgnt;
+ u8 CurrentEntryNum, CurrentLogEntryNum;
+
+ u8 physicalLinkHandle, TxFlowSpecID, i;
+ u16 CurrentLogicalHandle;
+
+ physicalLinkHandle = *((u8 *)pHciCmd->Data);
+ TxFlowSpecID = *(((u8 *)pHciCmd->Data)+1);
+
+ RTPRINT(FIOCTL, IOCTL_BT_HCICMD, ("LogicalLinkCancel, physicalLinkHandle = 0x%x, TxFlowSpecID = 0x%x\n",
+ physicalLinkHandle, TxFlowSpecID));
+
+ CurrentEntryNum = pBtMgnt->CurrentConnectEntryNum;
+ CurrentLogicalHandle = pBtMgnt->BtCurrentLogLinkhandle;
+
+ RTPRINT(FIOCTL, IOCTL_BT_HCICMD, ("CurrentEntryNum = 0x%x, CurrentLogicalHandle = 0x%x\n",
+ CurrentEntryNum, CurrentLogicalHandle));
+
+ CurrentLogEntryNum = 0xff;
+ for (i = 0; i < MAX_LOGICAL_LINK_NUM; i++) {
+ if ((CurrentLogicalHandle == pBTinfo->BtAsocEntry[CurrentEntryNum].LogLinkCmdData[i].BtLogLinkhandle) &&
+ (physicalLinkHandle == pBTinfo->BtAsocEntry[CurrentEntryNum].LogLinkCmdData[i].BtPhyLinkhandle)) {
+ CurrentLogEntryNum = i;
+ break;
+ }
+ }
+
+ if (CurrentLogEntryNum == 0xff) {
+ RTPRINT(FIOCTL, IOCTL_BT_HCICMD, ("LogicalLinkCancel, CurrentLogEntryNum == 0xff !!!!\n"));
+ status = HCI_STATUS_UNKNOW_CONNECT_ID;
+ return status;
+ } else {
+ if (pBTinfo->BtAsocEntry[CurrentEntryNum].LogLinkCmdData[CurrentLogEntryNum].bLLCompleteEventIsSet) {
+ RTPRINT(FIOCTL, IOCTL_BT_HCICMD, ("LogicalLinkCancel, LLCompleteEventIsSet!!!!\n"));
+ status = HCI_STATUS_ACL_CONNECT_EXISTS;
+ }
+ }
+
+ {
+ u8 localBuf[8] = "";
+ u8 *pRetPar;
+ u8 len = 0;
+ struct packet_irp_hcievent_data *PPacketIrpEvent;
+
+ PPacketIrpEvent = (struct packet_irp_hcievent_data *)(&localBuf[0]);
+
+ len += bthci_CommandCompleteHeader(&localBuf[0],
+ LINK_CONTROL_COMMANDS,
+ HCI_LOGICAL_LINK_CANCEL,
+ status);
+
+ /* Return parameters starts from here */
+ pRetPar = &PPacketIrpEvent->Data[len];
+ pRetPar[0] = status; /* status */
+ pRetPar[1] = pBTinfo->BtAsocEntry[CurrentEntryNum].LogLinkCmdData[CurrentLogEntryNum].BtPhyLinkhandle;
+ pRetPar[2] = pBTinfo->BtAsocEntry[CurrentEntryNum].LogLinkCmdData[CurrentLogEntryNum].BtTxFlowSpecID;
+ len += 3;
+ PPacketIrpEvent->Length = len;
+
+ bthci_IndicateEvent(padapter, PPacketIrpEvent, len+2);
+ }
+
+ pBTinfo->BtAsocEntry[CurrentEntryNum].LogLinkCmdData[CurrentLogEntryNum].bLLCancelCMDIsSetandComplete = true;
+
+ return status;
+}
+
+static enum hci_status
+bthci_CmdFlowSpecModify(struct rtw_adapter *padapter,
+ struct packet_irp_hcicmd_data *pHciCmd)
+{
+ enum hci_status status = HCI_STATUS_SUCCESS;
+/*PMGNT_INFO pMgntInfo = &padapter->MgntInfo; */
+ struct bt_30info *pBTinfo = GET_BT_INFO(padapter);
+ u8 i, j, find = 0;
+ u16 logicHandle;
+
+ logicHandle = *((u16 *)pHciCmd->Data);
+ /* find an matched logical link index and copy the data */
+ for (j = 0; j < MAX_BT_ASOC_ENTRY_NUM; j++) {
+ for (i = 0; i < MAX_LOGICAL_LINK_NUM; i++) {
+ if (pBTinfo->BtAsocEntry[j].LogLinkCmdData[i].BtLogLinkhandle == logicHandle) {
+ memcpy(&pBTinfo->BtAsocEntry[j].LogLinkCmdData[i].Tx_Flow_Spec,
+ &pHciCmd->Data[2], sizeof(struct hci_flow_spec));
+ memcpy(&pBTinfo->BtAsocEntry[j].LogLinkCmdData[i].Rx_Flow_Spec,
+ &pHciCmd->Data[18], sizeof(struct hci_flow_spec));
+
+ bthci_CheckLogLinkBehavior(padapter, pBTinfo->BtAsocEntry[j].LogLinkCmdData[i].Tx_Flow_Spec);
+ find = 1;
+ break;
+ }
+ }
+ }
+ RTPRINT(FIOCTL, IOCTL_BT_LOGO, ("FlowSpecModify, LLH = 0x%x, \n", logicHandle));
+
+ /* When we receive Flow Spec Modify command, we should send command status event first. */
+ bthci_EventCommandStatus(padapter,
+ LINK_CONTROL_COMMANDS,
+ HCI_FLOW_SPEC_MODIFY,
+ HCI_STATUS_SUCCESS);
+
+ if (!find)
+ status = HCI_STATUS_UNKNOW_CONNECT_ID;
+
+ bthci_EventSendFlowSpecModifyComplete(padapter, status, logicHandle);
+
+ return status;
+}
+
+static enum hci_status
+bthci_CmdAcceptPhysicalLink(struct rtw_adapter *padapter,
+ struct packet_irp_hcicmd_data *pHciCmd)
+{
+ enum hci_status status;
+ struct bt_30info *pBTInfo = GET_BT_INFO(padapter);
+ struct bt_dgb *pBtDbg = &pBTInfo->BtDbg;
+
+ pBtDbg->dbgHciInfo.hciCmdCntAcceptPhyLink++;
+
+ status = bthci_BuildPhysicalLink(padapter,
+ pHciCmd, HCI_ACCEPT_PHYSICAL_LINK);
+
+ return status;
+}
+
+static enum hci_status
+bthci_CmdDisconnectPhysicalLink(struct rtw_adapter *padapter,
+ struct packet_irp_hcicmd_data *pHciCmd)
+{
+ enum hci_status status = HCI_STATUS_SUCCESS;
+ struct bt_30info *pBTInfo = GET_BT_INFO(padapter);
+ struct bt_dgb *pBtDbg = &pBTInfo->BtDbg;
+ u8 PLH, CurrentEntryNum, PhysLinkDisconnectReason;
+
+ pBtDbg->dbgHciInfo.hciCmdCntDisconnectPhyLink++;
+
+ PLH = *((u8 *)pHciCmd->Data);
+ PhysLinkDisconnectReason = *((u8 *)pHciCmd->Data+1);
+ RTPRINT(FIOCTL, IOCTL_BT_HCICMD, ("HCI_DISCONNECT_PHYSICAL_LINK PhyHandle = 0x%x, Reason = 0x%x\n",
+ PLH, PhysLinkDisconnectReason));
+
+ CurrentEntryNum = bthci_GetCurrentEntryNum(padapter, PLH);
+
+ if (CurrentEntryNum == 0xff) {
+ RTPRINT(FIOCTL, IOCTL_BT_HCICMD,
+ ("DisconnectPhysicalLink, No such Handle in the Entry\n"));
+ status = HCI_STATUS_UNKNOW_CONNECT_ID;
+ } else {
+ pBTInfo->BtAsocEntry[CurrentEntryNum].PhyLinkDisconnectReason =
+ (enum hci_status)PhysLinkDisconnectReason;
+ }
+ /* Send HCI Command status event to AMP. */
+ bthci_EventCommandStatus(padapter, LINK_CONTROL_COMMANDS,
+ HCI_DISCONNECT_PHYSICAL_LINK, status);
+
+ if (status != HCI_STATUS_SUCCESS)
+ return status;
+
+ /* The macros below require { and } in the if statement */
+ if (pBTInfo->BtAsocEntry[CurrentEntryNum].BtCurrentState == HCI_STATE_DISCONNECTED) {
+ BTHCI_SM_WITH_INFO(padapter, HCI_STATE_DISCONNECTED, STATE_CMD_DISCONNECT_PHY_LINK, CurrentEntryNum);
+ } else {
+ BTHCI_SM_WITH_INFO(padapter, HCI_STATE_DISCONNECTING, STATE_CMD_DISCONNECT_PHY_LINK, CurrentEntryNum);
+ }
+ return status;
+}
+
+static enum hci_status
+bthci_CmdSetACLLinkDataFlowMode(struct rtw_adapter *padapter,
+ struct packet_irp_hcicmd_data *pHciCmd)
+{
+ enum hci_status status = HCI_STATUS_SUCCESS;
+ struct bt_30info *pBTInfo = GET_BT_INFO(padapter);
+ struct bt_mgnt *pBtMgnt = &pBTInfo->BtMgnt;
+ u8 localBuf[8] = "";
+ u8 *pRetPar;
+ u8 len = 0;
+ struct packet_irp_hcievent_data *PPacketIrpEvent;
+ u16 *pu2Temp;
+
+ pBtMgnt->ExtConfig.CurrentConnectHandle = *((u16 *)pHciCmd->Data);
+ pBtMgnt->ExtConfig.CurrentIncomingTrafficMode = *((u8 *)pHciCmd->Data)+2;
+ pBtMgnt->ExtConfig.CurrentOutgoingTrafficMode = *((u8 *)pHciCmd->Data)+3;
+ RTPRINT(FIOCTL, IOCTL_BT_HCICMD_EXT, ("Connection Handle = 0x%x, Incoming Traffic mode = 0x%x, Outgoing Traffic mode = 0x%x",
+ pBtMgnt->ExtConfig.CurrentConnectHandle,
+ pBtMgnt->ExtConfig.CurrentIncomingTrafficMode,
+ pBtMgnt->ExtConfig.CurrentOutgoingTrafficMode));
+
+
+ PPacketIrpEvent = (struct packet_irp_hcievent_data *)(&localBuf[0]);
+
+ len += bthci_CommandCompleteHeader(&localBuf[0],
+ OGF_EXTENSION,
+ HCI_SET_ACL_LINK_DATA_FLOW_MODE,
+ status);
+
+ /* Return parameters starts from here */
+ pRetPar = &PPacketIrpEvent->Data[len];
+ pRetPar[0] = status; /* status */
+
+ pu2Temp = (u16 *)&pRetPar[1];
+ *pu2Temp = pBtMgnt->ExtConfig.CurrentConnectHandle;
+ len += 3;
+ PPacketIrpEvent->Length = len;
+
+ bthci_IndicateEvent(padapter, PPacketIrpEvent, len+2);
+ return status;
+}
+
+static enum hci_status
+bthci_CmdSetACLLinkStatus(struct rtw_adapter *padapter,
+ struct packet_irp_hcicmd_data *pHciCmd)
+{
+ enum hci_status status = HCI_STATUS_SUCCESS;
+ struct bt_30info *pBTInfo = GET_BT_INFO(padapter);
+ struct bt_mgnt *pBtMgnt = &pBTInfo->BtMgnt;
+ struct bt_dgb *pBtDbg = &pBTInfo->BtDbg;
+ u8 i;
+ u8 *pTriple;
+
+ pBtDbg->dbgHciInfo.hciCmdCntSetAclLinkStatus++;
+ RTPRINT_DATA(FIOCTL, IOCTL_BT_HCICMD_EXT, "SetACLLinkStatus, Hex Data :\n",
+ &pHciCmd->Data[0], pHciCmd->Length);
+
+ /* Only Core Stack v251 and later version support this command. */
+ pBtMgnt->bSupportProfile = true;
+
+ pBtMgnt->ExtConfig.NumberOfHandle = *((u8 *)pHciCmd->Data);
+ RTPRINT(FIOCTL, IOCTL_BT_HCICMD_EXT, ("NumberOfHandle = 0x%x\n", pBtMgnt->ExtConfig.NumberOfHandle));
+
+ pTriple = &pHciCmd->Data[1];
+ for (i = 0; i < pBtMgnt->ExtConfig.NumberOfHandle; i++) {
+ pBtMgnt->ExtConfig.linkInfo[i].ConnectHandle = *((u16 *)&pTriple[0]);
+ pBtMgnt->ExtConfig.linkInfo[i].IncomingTrafficMode = pTriple[2];
+ pBtMgnt->ExtConfig.linkInfo[i].OutgoingTrafficMode = pTriple[3];
+ RTPRINT(FIOCTL, IOCTL_BT_HCICMD_EXT,
+ ("Connection_Handle = 0x%x, Incoming Traffic mode = 0x%x, Outgoing Traffic Mode = 0x%x\n",
+ pBtMgnt->ExtConfig.linkInfo[i].ConnectHandle,
+ pBtMgnt->ExtConfig.linkInfo[i].IncomingTrafficMode,
+ pBtMgnt->ExtConfig.linkInfo[i].OutgoingTrafficMode));
+ pTriple += 4;
+ }
+
+ {
+ u8 localBuf[6] = "";
+ u8 *pRetPar;
+ u8 len = 0;
+ struct packet_irp_hcievent_data *PPacketIrpEvent;
+
+ PPacketIrpEvent = (struct packet_irp_hcievent_data *)(&localBuf[0]);
+
+ len += bthci_CommandCompleteHeader(&localBuf[0],
+ OGF_EXTENSION,
+ HCI_SET_ACL_LINK_STATUS,
+ status);
+
+ /* Return parameters starts from here */
+ pRetPar = &PPacketIrpEvent->Data[len];
+ pRetPar[0] = status; /* status */
+
+ len += 1;
+ PPacketIrpEvent->Length = len;
+
+ bthci_IndicateEvent(padapter, PPacketIrpEvent, len+2);
+ }
+
+ return status;
+}
+
+static enum hci_status
+bthci_CmdSetSCOLinkStatus(
+ struct rtw_adapter *padapter,
+ struct packet_irp_hcicmd_data *pHciCmd
+ )
+{
+ enum hci_status status = HCI_STATUS_SUCCESS;
+ struct bt_30info *pBTInfo = GET_BT_INFO(padapter);
+ struct bt_mgnt *pBtMgnt = &pBTInfo->BtMgnt;
+ struct bt_dgb *pBtDbg = &pBTInfo->BtDbg;
+
+ pBtDbg->dbgHciInfo.hciCmdCntSetScoLinkStatus++;
+ pBtMgnt->ExtConfig.NumberOfSCO = *((u8 *)pHciCmd->Data);
+ RTPRINT(FIOCTL, IOCTL_BT_HCICMD_EXT, ("NumberOfSCO = 0x%x\n",
+ pBtMgnt->ExtConfig.NumberOfSCO));
+
+ {
+ u8 localBuf[6] = "";
+ u8 *pRetPar;
+ u8 len = 0;
+ struct packet_irp_hcievent_data *PPacketIrpEvent;
+
+ PPacketIrpEvent = (struct packet_irp_hcievent_data *)(&localBuf[0]);
+
+ len += bthci_CommandCompleteHeader(&localBuf[0],
+ OGF_EXTENSION,
+ HCI_SET_SCO_LINK_STATUS,
+ status);
+
+ /* Return parameters starts from here */
+ pRetPar = &PPacketIrpEvent->Data[len];
+ pRetPar[0] = status; /* status */
+
+ len += 1;
+ PPacketIrpEvent->Length = len;
+
+ bthci_IndicateEvent(padapter, PPacketIrpEvent, len+2);
+ }
+
+ return status;
+}
+
+static enum hci_status
+bthci_CmdSetRSSIValue(
+ struct rtw_adapter *padapter,
+ struct packet_irp_hcicmd_data *pHciCmd
+ )
+{
+ enum hci_status status = HCI_STATUS_SUCCESS;
+ struct bt_30info *pBTInfo = GET_BT_INFO(padapter);
+ struct bt_mgnt *pBtMgnt = &pBTInfo->BtMgnt;
+ s8 min_bt_rssi = 0;
+ u8 i;
+ for (i = 0; i < pBtMgnt->ExtConfig.NumberOfHandle; i++) {
+ if (pBtMgnt->ExtConfig.linkInfo[i].ConnectHandle == *((u16 *)&pHciCmd->Data[0])) {
+ pBtMgnt->ExtConfig.linkInfo[i].BT_RSSI = (s8)(pHciCmd->Data[2]);
+ RTPRINT(FIOCTL, IOCTL_BT_EVENT_PERIODICAL,
+ ("Connection_Handle = 0x%x, RSSI = %d \n",
+ pBtMgnt->ExtConfig.linkInfo[i].ConnectHandle,
+ pBtMgnt->ExtConfig.linkInfo[i].BT_RSSI));
+ }
+ /* get the minimum bt rssi value */
+ if (pBtMgnt->ExtConfig.linkInfo[i].BT_RSSI <= min_bt_rssi)
+ min_bt_rssi = pBtMgnt->ExtConfig.linkInfo[i].BT_RSSI;
+ }
+
+ pBtMgnt->ExtConfig.MIN_BT_RSSI = min_bt_rssi;
+ RTPRINT(FBT, BT_TRACE, ("[bt rssi], the min rssi is %d\n", min_bt_rssi));
+
+ {
+ u8 localBuf[6] = "";
+ u8 *pRetPar;
+ u8 len = 0;
+ struct packet_irp_hcievent_data *PPacketIrpEvent;
+
+ PPacketIrpEvent = (struct packet_irp_hcievent_data *)(&localBuf[0]);
+
+ len += bthci_CommandCompleteHeader(&localBuf[0],
+ OGF_EXTENSION,
+ HCI_SET_RSSI_VALUE,
+ status);
+
+ /* Return parameters starts from here */
+ pRetPar = &PPacketIrpEvent->Data[len];
+ pRetPar[0] = status; /* status */
+
+ len += 1;
+ PPacketIrpEvent->Length = len;
+
+ bthci_IndicateEvent(padapter, PPacketIrpEvent, len+2);
+ }
+
+ return status;
+}
+
+static enum hci_status
+bthci_CmdSetCurrentBluetoothStatus(
+ struct rtw_adapter *padapter,
+ struct packet_irp_hcicmd_data *pHciCmd
+ )
+{
+ enum hci_status status = HCI_STATUS_SUCCESS;
+/*PMGNT_INFO pMgntInfo = &padapter->MgntInfo; */
+ struct bt_30info *pBTInfo = GET_BT_INFO(padapter);
+ struct bt_mgnt *pBtMgnt = &pBTInfo->BtMgnt;
+
+ pBtMgnt->ExtConfig.CurrentBTStatus = *((u8 *)&pHciCmd->Data[0]);
+ RTPRINT(FIOCTL, IOCTL_BT_HCICMD_EXT, ("SetCurrentBluetoothStatus, CurrentBTStatus = 0x%x\n",
+ pBtMgnt->ExtConfig.CurrentBTStatus));
+
+ {
+ u8 localBuf[6] = "";
+ u8 *pRetPar;
+ u8 len = 0;
+ struct packet_irp_hcievent_data *PPacketIrpEvent;
+
+ PPacketIrpEvent = (struct packet_irp_hcievent_data *)(&localBuf[0]);
+
+ len += bthci_CommandCompleteHeader(&localBuf[0],
+ OGF_EXTENSION,
+ HCI_SET_CURRENT_BLUETOOTH_STATUS,
+ status);
+
+ /* Return parameters starts from here */
+ pRetPar = &PPacketIrpEvent->Data[len];
+ pRetPar[0] = status; /* status */
+ len += 1;
+
+ PPacketIrpEvent->Length = len;
+
+ bthci_IndicateEvent(padapter, PPacketIrpEvent, len+2);
+ }
+
+ return status;
+}
+
+static enum hci_status
+bthci_CmdExtensionVersionNotify(
+ struct rtw_adapter *padapter,
+ struct packet_irp_hcicmd_data *pHciCmd
+ )
+{
+ enum hci_status status = HCI_STATUS_SUCCESS;
+ struct bt_30info *pBTInfo = GET_BT_INFO(padapter);
+ struct bt_mgnt *pBtMgnt = &pBTInfo->BtMgnt;
+ struct bt_dgb *pBtDbg = &pBTInfo->BtDbg;
+
+ pBtDbg->dbgHciInfo.hciCmdCntExtensionVersionNotify++;
+ RTPRINT_DATA(FIOCTL, IOCTL_BT_HCICMD_EXT, "ExtensionVersionNotify, Hex Data :\n",
+ &pHciCmd->Data[0], pHciCmd->Length);
+
+ pBtMgnt->ExtConfig.HCIExtensionVer = *((u16 *)&pHciCmd->Data[0]);
+ RTPRINT(FIOCTL, IOCTL_BT_HCICMD_EXT, ("HCIExtensionVer = 0x%x\n", pBtMgnt->ExtConfig.HCIExtensionVer));
+
+ {
+ u8 localBuf[6] = "";
+ u8 *pRetPar;
+ u8 len = 0;
+ struct packet_irp_hcievent_data *PPacketIrpEvent;
+
+ PPacketIrpEvent = (struct packet_irp_hcievent_data *)(&localBuf[0]);
+
+ len += bthci_CommandCompleteHeader(&localBuf[0],
+ OGF_EXTENSION,
+ HCI_EXTENSION_VERSION_NOTIFY,
+ status);
+
+ /* Return parameters starts from here */
+ pRetPar = &PPacketIrpEvent->Data[len];
+ pRetPar[0] = status; /* status */
+
+ len += 1;
+ PPacketIrpEvent->Length = len;
+
+ bthci_IndicateEvent(padapter, PPacketIrpEvent, len+2);
+ }
+
+ return status;
+}
+
+static enum hci_status
+bthci_CmdLinkStatusNotify(
+ struct rtw_adapter *padapter,
+ struct packet_irp_hcicmd_data *pHciCmd
+ )
+{
+ enum hci_status status = HCI_STATUS_SUCCESS;
+ struct bt_30info *pBTInfo = GET_BT_INFO(padapter);
+ struct bt_mgnt *pBtMgnt = &pBTInfo->BtMgnt;
+ struct bt_dgb *pBtDbg = &pBTInfo->BtDbg;
+ u8 i;
+ u8 *pTriple;
+
+ pBtDbg->dbgHciInfo.hciCmdCntLinkStatusNotify++;
+ RTPRINT_DATA(FIOCTL, IOCTL_BT_HCICMD_EXT, "LinkStatusNotify, Hex Data :\n",
+ &pHciCmd->Data[0], pHciCmd->Length);
+
+ /* Current only RTL8723 support this command. */
+ pBtMgnt->bSupportProfile = true;
+
+ pBtMgnt->ExtConfig.NumberOfHandle = *((u8 *)pHciCmd->Data);
+ RTPRINT(FIOCTL, IOCTL_BT_HCICMD_EXT, ("NumberOfHandle = 0x%x\n", pBtMgnt->ExtConfig.NumberOfHandle));
+ RTPRINT(FIOCTL, IOCTL_BT_HCICMD_EXT, ("HCIExtensionVer = %d\n", pBtMgnt->ExtConfig.HCIExtensionVer));
+
+ pTriple = &pHciCmd->Data[1];
+ for (i = 0; i < pBtMgnt->ExtConfig.NumberOfHandle; i++) {
+ if (pBtMgnt->ExtConfig.HCIExtensionVer < 1) {
+ pBtMgnt->ExtConfig.linkInfo[i].ConnectHandle = *((u16 *)&pTriple[0]);
+ pBtMgnt->ExtConfig.linkInfo[i].BTProfile = pTriple[2];
+ pBtMgnt->ExtConfig.linkInfo[i].BTCoreSpec = pTriple[3];
+ RTPRINT(FIOCTL, IOCTL_BT_HCICMD_EXT,
+ ("Connection_Handle = 0x%x, BTProfile =%d, BTSpec =%d\n",
+ pBtMgnt->ExtConfig.linkInfo[i].ConnectHandle,
+ pBtMgnt->ExtConfig.linkInfo[i].BTProfile,
+ pBtMgnt->ExtConfig.linkInfo[i].BTCoreSpec));
+ pTriple += 4;
+ } else if (pBtMgnt->ExtConfig.HCIExtensionVer >= 1) {
+ pBtMgnt->ExtConfig.linkInfo[i].ConnectHandle = *((u16 *)&pTriple[0]);
+ pBtMgnt->ExtConfig.linkInfo[i].BTProfile = pTriple[2];
+ pBtMgnt->ExtConfig.linkInfo[i].BTCoreSpec = pTriple[3];
+ pBtMgnt->ExtConfig.linkInfo[i].linkRole = pTriple[4];
+ RTPRINT(FIOCTL, IOCTL_BT_HCICMD_EXT,
+ ("Connection_Handle = 0x%x, BTProfile =%d, BTSpec =%d, LinkRole =%d\n",
+ pBtMgnt->ExtConfig.linkInfo[i].ConnectHandle,
+ pBtMgnt->ExtConfig.linkInfo[i].BTProfile,
+ pBtMgnt->ExtConfig.linkInfo[i].BTCoreSpec,
+ pBtMgnt->ExtConfig.linkInfo[i].linkRole));
+ pTriple += 5;
+ }
+
+ }
+ BTHCI_UpdateBTProfileRTKToMoto(padapter);
+ {
+ u8 localBuf[6] = "";
+ u8 *pRetPar;
+ u8 len = 0;
+ struct packet_irp_hcievent_data *PPacketIrpEvent;
+
+ PPacketIrpEvent = (struct packet_irp_hcievent_data *)(&localBuf[0]);
+
+ len += bthci_CommandCompleteHeader(&localBuf[0],
+ OGF_EXTENSION,
+ HCI_LINK_STATUS_NOTIFY,
+ status);
+
+ /* Return parameters starts from here */
+ pRetPar = &PPacketIrpEvent->Data[len];
+ pRetPar[0] = status; /* status */
+
+ len += 1;
+ PPacketIrpEvent->Length = len;
+
+ bthci_IndicateEvent(padapter, PPacketIrpEvent, len+2);
+ }
+
+ return status;
+}
+
+static enum hci_status
+bthci_CmdBtOperationNotify(
+ struct rtw_adapter *padapter,
+ struct packet_irp_hcicmd_data *pHciCmd
+ )
+{
+ enum hci_status status = HCI_STATUS_SUCCESS;
+ struct bt_30info *pBTInfo = GET_BT_INFO(padapter);
+ struct bt_mgnt *pBtMgnt = &pBTInfo->BtMgnt;
+
+ RTPRINT_DATA(FIOCTL, IOCTL_BT_HCICMD_EXT, "Bt Operation notify, Hex Data :\n",
+ &pHciCmd->Data[0], pHciCmd->Length);
+
+ pBtMgnt->ExtConfig.btOperationCode = *((u8 *)pHciCmd->Data);
+ RTPRINT(FIOCTL, IOCTL_BT_HCICMD_EXT, ("btOperationCode = 0x%x\n", pBtMgnt->ExtConfig.btOperationCode));
+ switch (pBtMgnt->ExtConfig.btOperationCode) {
+ case HCI_BT_OP_NONE:
+ RTPRINT(FIOCTL, IOCTL_BT_HCICMD_EXT, ("[bt operation] : Operation None!!\n"));
+ break;
+ case HCI_BT_OP_INQUIRY_START:
+ RTPRINT(FIOCTL, IOCTL_BT_HCICMD_EXT, ("[bt operation] : Inquire start!!\n"));
+ break;
+ case HCI_BT_OP_INQUIRY_FINISH:
+ RTPRINT(FIOCTL, IOCTL_BT_HCICMD_EXT, ("[bt operation] : Inquire finished!!\n"));
+ break;
+ case HCI_BT_OP_PAGING_START:
+ RTPRINT(FIOCTL, IOCTL_BT_HCICMD_EXT, ("[bt operation] : Paging is started!!\n"));
+ break;
+ case HCI_BT_OP_PAGING_SUCCESS:
+ RTPRINT(FIOCTL, IOCTL_BT_HCICMD_EXT, ("[bt operation] : Paging complete successfully!!\n"));
+ break;
+ case HCI_BT_OP_PAGING_UNSUCCESS:
+ RTPRINT(FIOCTL, IOCTL_BT_HCICMD_EXT, ("[bt operation] : Paging complete unsuccessfully!!\n"));
+ break;
+ case HCI_BT_OP_PAIRING_START:
+ RTPRINT(FIOCTL, IOCTL_BT_HCICMD_EXT, ("[bt operation] : Pairing start!!\n"));
+ break;
+ case HCI_BT_OP_PAIRING_FINISH:
+ RTPRINT(FIOCTL, IOCTL_BT_HCICMD_EXT, ("[bt operation] : Pairing finished!!\n"));
+ break;
+ case HCI_BT_OP_BT_DEV_ENABLE:
+ RTPRINT(FIOCTL, IOCTL_BT_HCICMD_EXT, ("[bt operation] : BT Device is enabled!!\n"));
+ break;
+ case HCI_BT_OP_BT_DEV_DISABLE:
+ RTPRINT(FIOCTL, IOCTL_BT_HCICMD_EXT, ("[bt operation] : BT Device is disabled!!\n"));
+ break;
+ default:
+ RTPRINT(FIOCTL, IOCTL_BT_HCICMD_EXT, ("[bt operation] : Unknown, error!!\n"));
+ break;
+ }
+ BTDM_AdjustForBtOperation(padapter);
+ {
+ u8 localBuf[6] = "";
+ u8 *pRetPar;
+ u8 len = 0;
+ struct packet_irp_hcievent_data *PPacketIrpEvent;
+
+ PPacketIrpEvent = (struct packet_irp_hcievent_data *)(&localBuf[0]);
+
+ len += bthci_CommandCompleteHeader(&localBuf[0],
+ OGF_EXTENSION,
+ HCI_BT_OPERATION_NOTIFY,
+ status);
+
+ /* Return parameters starts from here */
+ pRetPar = &PPacketIrpEvent->Data[len];
+ pRetPar[0] = status; /* status */
+
+ len += 1;
+ PPacketIrpEvent->Length = len;
+
+ bthci_IndicateEvent(padapter, PPacketIrpEvent, len+2);
+ }
+
+ return status;
+}
+
+static enum hci_status
+bthci_CmdEnableWifiScanNotify(struct rtw_adapter *padapter,
+ struct packet_irp_hcicmd_data *pHciCmd)
+{
+ enum hci_status status = HCI_STATUS_SUCCESS;
+ struct bt_30info *pBTInfo = GET_BT_INFO(padapter);
+ struct bt_mgnt *pBtMgnt = &pBTInfo->BtMgnt;
+
+ RTPRINT_DATA(FIOCTL, IOCTL_BT_HCICMD_EXT, "Enable Wifi scan notify, Hex Data :\n",
+ &pHciCmd->Data[0], pHciCmd->Length);
+
+ pBtMgnt->ExtConfig.bEnableWifiScanNotify = *((u8 *)pHciCmd->Data);
+ RTPRINT(FIOCTL, IOCTL_BT_HCICMD_EXT, ("bEnableWifiScanNotify = %d\n", pBtMgnt->ExtConfig.bEnableWifiScanNotify));
+
+ {
+ u8 localBuf[6] = "";
+ u8 *pRetPar;
+ u8 len = 0;
+ struct packet_irp_hcievent_data *PPacketIrpEvent;
+
+ PPacketIrpEvent = (struct packet_irp_hcievent_data *)(&localBuf[0]);
+
+ len += bthci_CommandCompleteHeader(&localBuf[0],
+ OGF_EXTENSION,
+ HCI_ENABLE_WIFI_SCAN_NOTIFY,
+ status);
+
+ /* Return parameters starts from here */
+ pRetPar = &PPacketIrpEvent->Data[len];
+ pRetPar[0] = status; /* status */
+
+ len += 1;
+ PPacketIrpEvent->Length = len;
+
+ bthci_IndicateEvent(padapter, PPacketIrpEvent, len+2);
+ }
+
+ return status;
+}
+
+static enum hci_status
+bthci_CmdWIFICurrentChannel(struct rtw_adapter *padapter,
+ struct packet_irp_hcicmd_data *pHciCmd)
+{
+ enum hci_status status = HCI_STATUS_SUCCESS;
+ struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv;
+ u8 chnl = pmlmeext->cur_channel;
+
+ if (pmlmeext->cur_bwmode == HT_CHANNEL_WIDTH_40) {
+ if (pmlmeext->cur_ch_offset == HAL_PRIME_CHNL_OFFSET_UPPER)
+ chnl += 2;
+ else if (pmlmeext->cur_ch_offset == HAL_PRIME_CHNL_OFFSET_LOWER)
+ chnl -= 2;
+ }
+
+ RTPRINT(FIOCTL, IOCTL_BT_HCICMD_EXT, ("Current Channel = 0x%x\n", chnl));
+
+ {
+ u8 localBuf[8] = "";
+ u8 *pRetPar;
+ u8 len = 0;
+ struct packet_irp_hcievent_data *PPacketIrpEvent;
+
+ PPacketIrpEvent = (struct packet_irp_hcievent_data *)(&localBuf[0]);
+
+ len += bthci_CommandCompleteHeader(&localBuf[0],
+ OGF_EXTENSION,
+ HCI_WIFI_CURRENT_CHANNEL,
+ status);
+
+ /* Return parameters starts from here */
+ pRetPar = &PPacketIrpEvent->Data[len];
+ pRetPar[0] = status; /* status */
+ pRetPar[1] = chnl; /* current channel */
+ len += 2;
+ PPacketIrpEvent->Length = len;
+
+ bthci_IndicateEvent(padapter, PPacketIrpEvent, len+2);
+ }
+
+ return status;
+}
+
+static enum hci_status
+bthci_CmdWIFICurrentBandwidth(struct rtw_adapter *padapter,
+ struct packet_irp_hcicmd_data *pHciCmd)
+{
+ enum hci_status status = HCI_STATUS_SUCCESS;
+ enum ht_channel_width bw;
+ u8 CurrentBW = 0;
+
+ bw = padapter->mlmeextpriv.cur_bwmode;
+
+ if (bw == HT_CHANNEL_WIDTH_20)
+ CurrentBW = 0;
+ else if (bw == HT_CHANNEL_WIDTH_40)
+ CurrentBW = 1;
+
+ RTPRINT(FIOCTL, IOCTL_BT_HCICMD_EXT, ("Current BW = 0x%x\n",
+ CurrentBW));
+
+ {
+ u8 localBuf[8] = "";
+ u8 *pRetPar;
+ u8 len = 0;
+ struct packet_irp_hcievent_data *PPacketIrpEvent;
+
+ PPacketIrpEvent = (struct packet_irp_hcievent_data *)(&localBuf[0]);
+
+ len += bthci_CommandCompleteHeader(&localBuf[0],
+ OGF_EXTENSION,
+ HCI_WIFI_CURRENT_BANDWIDTH,
+ status);
+
+ /* Return parameters starts from here */
+ pRetPar = &PPacketIrpEvent->Data[len];
+ pRetPar[0] = status; /* status */
+ pRetPar[1] = CurrentBW; /* current BW */
+ len += 2;
+ PPacketIrpEvent->Length = len;
+
+ bthci_IndicateEvent(padapter, PPacketIrpEvent, len+2);
+ }
+
+ return status;
+}
+
+static enum hci_status
+bthci_CmdWIFIConnectionStatus(
+ struct rtw_adapter *padapter,
+ struct packet_irp_hcicmd_data *pHciCmd
+ )
+{
+ enum hci_status status = HCI_STATUS_SUCCESS;
+ u8 connectStatus = HCI_WIFI_NOT_CONNECTED;
+
+ if (check_fwstate(&padapter->mlmepriv, WIFI_AP_STATE)) {
+ if (padapter->stapriv.asoc_sta_count >= 3)
+ connectStatus = HCI_WIFI_CONNECTED;
+ else
+ connectStatus = HCI_WIFI_NOT_CONNECTED;
+ } else if (check_fwstate(&padapter->mlmepriv, WIFI_ADHOC_STATE|WIFI_ADHOC_MASTER_STATE|WIFI_ASOC_STATE)) {
+ connectStatus = HCI_WIFI_CONNECTED;
+ } else if (check_fwstate(&padapter->mlmepriv, WIFI_UNDER_LINKING)) {
+ connectStatus = HCI_WIFI_CONNECT_IN_PROGRESS;
+ } else {
+ connectStatus = HCI_WIFI_NOT_CONNECTED;
+ }
+
+ {
+ u8 localBuf[8] = "";
+ u8 *pRetPar;
+ u8 len = 0;
+ struct packet_irp_hcievent_data *PPacketIrpEvent;
+
+ PPacketIrpEvent = (struct packet_irp_hcievent_data *)(&localBuf[0]);
+
+ len += bthci_CommandCompleteHeader(&localBuf[0],
+ OGF_EXTENSION,
+ HCI_WIFI_CONNECTION_STATUS,
+ status);
+
+ /* Return parameters starts from here */
+ pRetPar = &PPacketIrpEvent->Data[len];
+ pRetPar[0] = status; /* status */
+ pRetPar[1] = connectStatus; /* connect status */
+ len += 2;
+ PPacketIrpEvent->Length = len;
+
+ bthci_IndicateEvent(padapter, PPacketIrpEvent, len+2);
+ }
+
+ return status;
+}
+
+static enum hci_status
+bthci_CmdEnableDeviceUnderTestMode(
+ struct rtw_adapter *padapter,
+ struct packet_irp_hcicmd_data *pHciCmd
+ )
+{
+ enum hci_status status = HCI_STATUS_SUCCESS;
+ struct bt_30info *pBTInfo = GET_BT_INFO(padapter);
+ struct bt_hci_info *pBtHciInfo = &pBTInfo->BtHciInfo;
+
+ pBtHciInfo->bInTestMode = true;
+ pBtHciInfo->bTestIsEnd = false;
+
+ /* send command complete event here when all data are received. */
+ {
+ u8 localBuf[6] = "";
+ u8 *pRetPar;
+ u8 len = 0;
+ struct packet_irp_hcievent_data *PPacketIrpEvent;
+
+ PPacketIrpEvent = (struct packet_irp_hcievent_data *)(&localBuf[0]);
+
+ len += bthci_CommandCompleteHeader(&localBuf[0],
+ OGF_TESTING_COMMANDS,
+ HCI_ENABLE_DEVICE_UNDER_TEST_MODE,
+ status);
+
+ /* Return parameters starts from here */
+ pRetPar = &PPacketIrpEvent->Data[len];
+ pRetPar[0] = status; /* status */
+ len += 1;
+ PPacketIrpEvent->Length = len;
+
+ bthci_IndicateEvent(padapter, PPacketIrpEvent, len+2);
+ }
+
+ return status;
+}
+
+static enum hci_status
+bthci_CmdAMPTestEnd(struct rtw_adapter *padapter,
+ struct packet_irp_hcicmd_data *pHciCmd)
+{
+ enum hci_status status = HCI_STATUS_SUCCESS;
+ struct bt_30info *pBTInfo = GET_BT_INFO(padapter);
+ struct bt_hci_info *pBtHciInfo = &pBTInfo->BtHciInfo;
+
+ if (!pBtHciInfo->bInTestMode) {
+ RTPRINT(FIOCTL, (IOCTL_BT_EVENT|IOCTL_BT_LOGO), ("Not in Test mode, return status = HCI_STATUS_CMD_DISALLOW\n"));
+ status = HCI_STATUS_CMD_DISALLOW;
+ return status;
+ }
+
+ pBtHciInfo->bTestIsEnd = true;
+
+ del_timer_sync(&pBTInfo->BTTestSendPacketTimer);
+
+ rtl8723a_check_bssid(padapter, true);
+
+ /* send command complete event here when all data are received. */
+ {
+ u8 localBuf[4] = "";
+ struct packet_irp_hcievent_data *PPacketIrpEvent;
+
+ RTPRINT(FIOCTL, (IOCTL_BT_EVENT|IOCTL_BT_LOGO), ("AMP Test End Event \n"));
+ PPacketIrpEvent = (struct packet_irp_hcievent_data *)(&localBuf[0]);
+ PPacketIrpEvent->EventCode = HCI_EVENT_AMP_TEST_END;
+ PPacketIrpEvent->Length = 2;
+
+ PPacketIrpEvent->Data[0] = status;
+ PPacketIrpEvent->Data[1] = pBtHciInfo->TestScenario;
+
+ bthci_IndicateEvent(padapter, PPacketIrpEvent, 4);
+ }
+
+ bthci_EventAMPReceiverReport(padapter, 0x01);
+
+ return status;
+}
+
+static enum hci_status
+bthci_CmdAMPTestCommand(struct rtw_adapter *padapter,
+ struct packet_irp_hcicmd_data *pHciCmd)
+{
+ enum hci_status status = HCI_STATUS_SUCCESS;
+ struct bt_30info *pBTInfo = GET_BT_INFO(padapter);
+ struct bt_hci_info *pBtHciInfo = &pBTInfo->BtHciInfo;
+
+ if (!pBtHciInfo->bInTestMode) {
+ RTPRINT(FIOCTL, (IOCTL_BT_EVENT|IOCTL_BT_LOGO), ("Not in Test mode, return status = HCI_STATUS_CMD_DISALLOW\n"));
+ status = HCI_STATUS_CMD_DISALLOW;
+ return status;
+ }
+
+ pBtHciInfo->TestScenario = *((u8 *)pHciCmd->Data);
+
+ if (pBtHciInfo->TestScenario == 0x01)
+ RTPRINT(FIOCTL, (IOCTL_BT_EVENT|IOCTL_BT_LOGO), ("TX Single Test \n"));
+ else if (pBtHciInfo->TestScenario == 0x02)
+ RTPRINT(FIOCTL, (IOCTL_BT_EVENT|IOCTL_BT_LOGO), ("Receive Frame Test \n"));
+ else
+ RTPRINT(FIOCTL, (IOCTL_BT_EVENT|IOCTL_BT_LOGO), ("No Such Test !!!!!!!!!!!!!!!!!! \n"));
+
+ if (pBtHciInfo->bTestIsEnd) {
+ u8 localBuf[5] = "";
+ struct packet_irp_hcievent_data *PPacketIrpEvent;
+
+ RTPRINT(FIOCTL, (IOCTL_BT_EVENT|IOCTL_BT_LOGO), ("AMP Test End Event \n"));
+ PPacketIrpEvent = (struct packet_irp_hcievent_data *)(&localBuf[0]);
+ PPacketIrpEvent->EventCode = HCI_EVENT_AMP_TEST_END;
+ PPacketIrpEvent->Length = 2;
+
+ PPacketIrpEvent->Data[0] = status;
+ PPacketIrpEvent->Data[1] = pBtHciInfo->TestScenario ;
+
+ bthci_IndicateEvent(padapter, PPacketIrpEvent, 4);
+
+ /* Return to Idel state with RX and TX off. */
+
+ return status;
+ }
+
+ /* should send command status event */
+ bthci_EventCommandStatus(padapter,
+ OGF_TESTING_COMMANDS,
+ HCI_AMP_TEST_COMMAND,
+ status);
+
+ /* The HCI_AMP_Start Test Event shall be generated when the */
+ /* HCI_AMP_Test_Command has completed and the first data is ready to be sent */
+ /* or received. */
+
+ {
+ u8 localBuf[5] = "";
+ struct packet_irp_hcievent_data *PPacketIrpEvent;
+
+ RTPRINT(FIOCTL, (IOCTL_BT_EVENT|IOCTL_BT_LOGO), (" HCI_AMP_Start Test Event \n"));
+ PPacketIrpEvent = (struct packet_irp_hcievent_data *)(&localBuf[0]);
+ PPacketIrpEvent->EventCode = HCI_EVENT_AMP_START_TEST;
+ PPacketIrpEvent->Length = 2;
+
+ PPacketIrpEvent->Data[0] = status;
+ PPacketIrpEvent->Data[1] = pBtHciInfo->TestScenario ;
+
+ bthci_IndicateEvent(padapter, PPacketIrpEvent, 4);
+
+ /* Return to Idel state with RX and TX off. */
+ }
+
+ if (pBtHciInfo->TestScenario == 0x01) {
+ /*
+ When in a transmitter test scenario and the frames/bursts count have been
+ transmitted the HCI_AMP_Test_End event shall be sent.
+ */
+ mod_timer(&pBTInfo->BTTestSendPacketTimer,
+ jiffies + msecs_to_jiffies(50));
+ RTPRINT(FIOCTL, (IOCTL_BT_EVENT|IOCTL_BT_LOGO), ("TX Single Test \n"));
+ } else if (pBtHciInfo->TestScenario == 0x02) {
+ rtl8723a_check_bssid(padapter, false);
+ RTPRINT(FIOCTL, (IOCTL_BT_EVENT|IOCTL_BT_LOGO), ("Receive Frame Test \n"));
+ }
+
+ return status;
+}
+
+static enum hci_status
+bthci_CmdEnableAMPReceiverReports(struct rtw_adapter *padapter,
+ struct packet_irp_hcicmd_data *pHciCmd)
+{
+ enum hci_status status = HCI_STATUS_SUCCESS;
+ struct bt_30info *pBTInfo = GET_BT_INFO(padapter);
+ struct bt_hci_info *pBtHciInfo = &pBTInfo->BtHciInfo;
+
+ if (!pBtHciInfo->bInTestMode) {
+ status = HCI_STATUS_CMD_DISALLOW;
+ /* send command complete event here when all data are received. */
+ {
+ u8 localBuf[6] = "";
+ u8 *pRetPar;
+ u8 len = 0;
+ struct packet_irp_hcievent_data *PPacketIrpEvent;
+
+ PPacketIrpEvent = (struct packet_irp_hcievent_data *)(&localBuf[0]);
+
+ len += bthci_CommandCompleteHeader(&localBuf[0],
+ OGF_TESTING_COMMANDS,
+ HCI_ENABLE_AMP_RECEIVER_REPORTS,
+ status);
+
+ /* Return parameters starts from here */
+ pRetPar = &PPacketIrpEvent->Data[len];
+ pRetPar[0] = status; /* status */
+ len += 1;
+ PPacketIrpEvent->Length = len;
+
+ bthci_IndicateEvent(padapter, PPacketIrpEvent, len+2);
+ }
+ return status;
+ }
+
+ pBtHciInfo->bTestNeedReport = *((u8 *)pHciCmd->Data);
+ pBtHciInfo->TestReportInterval = (*((u8 *)pHciCmd->Data+2));
+
+ bthci_EventAMPReceiverReport(padapter, 0x00);
+
+ /* send command complete event here when all data are received. */
+ {
+ u8 localBuf[6] = "";
+ u8 *pRetPar;
+ u8 len = 0;
+ struct packet_irp_hcievent_data *PPacketIrpEvent;
+
+ PPacketIrpEvent = (struct packet_irp_hcievent_data *)(&localBuf[0]);
+
+ len += bthci_CommandCompleteHeader(&localBuf[0],
+ OGF_TESTING_COMMANDS,
+ HCI_ENABLE_AMP_RECEIVER_REPORTS,
+ status);
+
+ /* Return parameters starts from here */
+ pRetPar = &PPacketIrpEvent->Data[len];
+ pRetPar[0] = status; /* status */
+ len += 1;
+ PPacketIrpEvent->Length = len;
+
+ bthci_IndicateEvent(padapter, PPacketIrpEvent, len+2);
+ }
+
+ return status;
+}
+
+static enum hci_status
+bthci_CmdHostBufferSize(struct rtw_adapter *padapter,
+ struct packet_irp_hcicmd_data *pHciCmd)
+{
+ struct bt_30info *pBTInfo = GET_BT_INFO(padapter);
+ struct bt_mgnt *pBtMgnt = &pBTInfo->BtMgnt;
+ struct packet_irp_hcievent_data *PPacketIrpEvent;
+ enum hci_status status = HCI_STATUS_SUCCESS;
+ u8 localBuf[6] = "";
+ u8 *pRetPar;
+ u8 len = 0;
+
+ pBTInfo->BtAsocEntry[pBtMgnt->CurrentConnectEntryNum].ACLPacketsData.ACLDataPacketLen = *((u16 *)pHciCmd->Data);
+ pBTInfo->BtAsocEntry[pBtMgnt->CurrentConnectEntryNum].SyncDataPacketLen = *((u8 *)(pHciCmd->Data+2));
+ pBTInfo->BtAsocEntry[pBtMgnt->CurrentConnectEntryNum].TotalNumACLDataPackets = *((u16 *)(pHciCmd->Data+3));
+ pBTInfo->BtAsocEntry[pBtMgnt->CurrentConnectEntryNum].TotalSyncNumDataPackets = *((u16 *)(pHciCmd->Data+5));
+
+ /* send command complete event here when all data are received. */
+ PPacketIrpEvent = (struct packet_irp_hcievent_data *)(&localBuf[0]);
+
+ len += bthci_CommandCompleteHeader(&localBuf[0],
+ OGF_SET_EVENT_MASK_COMMAND,
+ HCI_HOST_BUFFER_SIZE,
+ status);
+
+ /* Return parameters starts from here */
+ pRetPar = &PPacketIrpEvent->Data[len];
+ pRetPar[0] = status; /* status */
+ len += 1;
+ PPacketIrpEvent->Length = len;
+
+ bthci_IndicateEvent(padapter, PPacketIrpEvent, len+2);
+
+ return status;
+}
+
+static enum hci_status
+bthci_UnknownCMD(struct rtw_adapter *padapter, struct packet_irp_hcicmd_data *pHciCmd)
+{
+ enum hci_status status = HCI_STATUS_UNKNOW_HCI_CMD;
+ struct bt_30info *pBTInfo = GET_BT_INFO(padapter);
+ struct bt_dgb *pBtDbg = &pBTInfo->BtDbg;
+
+ pBtDbg->dbgHciInfo.hciCmdCntUnknown++;
+ bthci_EventCommandStatus(padapter,
+ (u8)pHciCmd->OGF,
+ pHciCmd->OCF,
+ status);
+
+ return status;
+}
+
+static enum hci_status
+bthci_HandleOGFInformationalParameters(struct rtw_adapter *padapter,
+ struct packet_irp_hcicmd_data *pHciCmd)
+{
+ enum hci_status status = HCI_STATUS_SUCCESS;
+
+ switch (pHciCmd->OCF) {
+ case HCI_READ_LOCAL_VERSION_INFORMATION:
+ RTPRINT(FIOCTL, IOCTL_BT_HCICMD, ("HCI_READ_LOCAL_VERSION_INFORMATION\n"));
+ status = bthci_CmdReadLocalVersionInformation(padapter);
+ break;
+ case HCI_READ_LOCAL_SUPPORTED_COMMANDS:
+ RTPRINT(FIOCTL, IOCTL_BT_HCICMD, ("HCI_READ_LOCAL_SUPPORTED_COMMANDS\n"));
+ status = bthci_CmdReadLocalSupportedCommands(padapter);
+ break;
+ case HCI_READ_LOCAL_SUPPORTED_FEATURES:
+ RTPRINT(FIOCTL, IOCTL_BT_HCICMD, ("HCI_READ_LOCAL_SUPPORTED_FEATURES\n"));
+ status = bthci_CmdReadLocalSupportedFeatures(padapter);
+ break;
+ case HCI_READ_BUFFER_SIZE:
+ RTPRINT(FIOCTL, IOCTL_BT_HCICMD, ("HCI_READ_BUFFER_SIZE\n"));
+ status = bthci_CmdReadBufferSize(padapter);
+ break;
+ case HCI_READ_DATA_BLOCK_SIZE:
+ RTPRINT(FIOCTL, IOCTL_BT_HCICMD, ("HCI_READ_DATA_BLOCK_SIZE\n"));
+ status = bthci_CmdReadDataBlockSize(padapter);
+ break;
+ default:
+ RTPRINT(FIOCTL, IOCTL_BT_HCICMD, ("bthci_HandleOGFInformationalParameters(), Unknown case = 0x%x\n", pHciCmd->OCF));
+ RTPRINT(FIOCTL, IOCTL_BT_HCICMD, ("HCI_UNKNOWN_COMMAND\n"));
+ status = bthci_UnknownCMD(padapter, pHciCmd);
+ break;
+ }
+ return status;
+}
+
+static enum hci_status
+bthci_HandleOGFSetEventMaskCMD(struct rtw_adapter *padapter,
+ struct packet_irp_hcicmd_data *pHciCmd)
+{
+ enum hci_status status = HCI_STATUS_SUCCESS;
+
+ switch (pHciCmd->OCF) {
+ case HCI_SET_EVENT_MASK:
+ RTPRINT(FIOCTL, IOCTL_BT_HCICMD, ("HCI_SET_EVENT_MASK\n"));
+ status = bthci_CmdSetEventMask(padapter, pHciCmd);
+ break;
+ case HCI_RESET:
+ RTPRINT(FIOCTL, IOCTL_BT_HCICMD, ("HCI_RESET\n"));
+ status = bthci_CmdReset(padapter, true);
+ break;
+ case HCI_READ_CONNECTION_ACCEPT_TIMEOUT:
+ RTPRINT(FIOCTL, IOCTL_BT_HCICMD, ("HCI_READ_CONNECTION_ACCEPT_TIMEOUT\n"));
+ status = bthci_CmdReadConnectionAcceptTimeout(padapter);
+ break;
+ case HCI_SET_EVENT_FILTER:
+ RTPRINT(FIOCTL, IOCTL_BT_HCICMD, ("HCI_SET_EVENT_FILTER\n"));
+ break;
+ case HCI_WRITE_CONNECTION_ACCEPT_TIMEOUT:
+ RTPRINT(FIOCTL, IOCTL_BT_HCICMD, ("HCI_WRITE_CONNECTION_ACCEPT_TIMEOUT\n"));
+ status = bthci_CmdWriteConnectionAcceptTimeout(padapter, pHciCmd);
+ break;
+ case HCI_READ_PAGE_TIMEOUT:
+ RTPRINT(FIOCTL, IOCTL_BT_HCICMD, ("HCI_READ_PAGE_TIMEOUT\n"));
+ status = bthci_CmdReadPageTimeout(padapter, pHciCmd);
+ break;
+ case HCI_WRITE_PAGE_TIMEOUT:
+ RTPRINT(FIOCTL, IOCTL_BT_HCICMD, ("HCI_WRITE_PAGE_TIMEOUT\n"));
+ status = bthci_CmdWritePageTimeout(padapter, pHciCmd);
+ break;
+ case HCI_HOST_NUMBER_OF_COMPLETED_PACKETS:
+ RTPRINT(FIOCTL, IOCTL_BT_HCICMD, ("HCI_HOST_NUMBER_OF_COMPLETED_PACKETS\n"));
+ break;
+ case HCI_READ_LINK_SUPERVISION_TIMEOUT:
+ RTPRINT(FIOCTL, IOCTL_BT_HCICMD, ("HCI_READ_LINK_SUPERVISION_TIMEOUT\n"));
+ status = bthci_CmdReadLinkSupervisionTimeout(padapter, pHciCmd);
+ break;
+ case HCI_WRITE_LINK_SUPERVISION_TIMEOUT:
+ RTPRINT(FIOCTL, IOCTL_BT_HCICMD, ("HCI_WRITE_LINK_SUPERVISION_TIMEOUT\n"));
+ status = bthci_CmdWriteLinkSupervisionTimeout(padapter, pHciCmd);
+ break;
+ case HCI_ENHANCED_FLUSH:
+ RTPRINT(FIOCTL, IOCTL_BT_HCICMD, ("HCI_ENHANCED_FLUSH\n"));
+ status = bthci_CmdEnhancedFlush(padapter, pHciCmd);
+ break;
+ case HCI_READ_LOGICAL_LINK_ACCEPT_TIMEOUT:
+ RTPRINT(FIOCTL, IOCTL_BT_HCICMD, ("HCI_READ_LOGICAL_LINK_ACCEPT_TIMEOUT\n"));
+ status = bthci_CmdReadLogicalLinkAcceptTimeout(padapter, pHciCmd);
+ break;
+ case HCI_WRITE_LOGICAL_LINK_ACCEPT_TIMEOUT:
+ RTPRINT(FIOCTL, IOCTL_BT_HCICMD, ("HCI_WRITE_LOGICAL_LINK_ACCEPT_TIMEOUT\n"));
+ status = bthci_CmdWriteLogicalLinkAcceptTimeout(padapter, pHciCmd);
+ break;
+ case HCI_SET_EVENT_MASK_PAGE_2:
+ RTPRINT(FIOCTL, IOCTL_BT_HCICMD, ("HCI_SET_EVENT_MASK_PAGE_2\n"));
+ status = bthci_CmdSetEventMaskPage2(padapter, pHciCmd);
+ break;
+ case HCI_READ_LOCATION_DATA:
+ RTPRINT(FIOCTL, IOCTL_BT_HCICMD, ("HCI_READ_LOCATION_DATA\n"));
+ status = bthci_CmdReadLocationData(padapter, pHciCmd);
+ break;
+ case HCI_WRITE_LOCATION_DATA:
+ RTPRINT(FIOCTL, IOCTL_BT_HCICMD, ("HCI_WRITE_LOCATION_DATA\n"));
+ status = bthci_CmdWriteLocationData(padapter, pHciCmd);
+ break;
+ case HCI_READ_FLOW_CONTROL_MODE:
+ RTPRINT(FIOCTL, IOCTL_BT_HCICMD, ("HCI_READ_FLOW_CONTROL_MODE\n"));
+ status = bthci_CmdReadFlowControlMode(padapter, pHciCmd);
+ break;
+ case HCI_WRITE_FLOW_CONTROL_MODE:
+ RTPRINT(FIOCTL, IOCTL_BT_HCICMD, ("HCI_WRITE_FLOW_CONTROL_MODE\n"));
+ status = bthci_CmdWriteFlowControlMode(padapter, pHciCmd);
+ break;
+ case HCI_READ_BEST_EFFORT_FLUSH_TIMEOUT:
+ RTPRINT(FIOCTL, IOCTL_BT_HCICMD, ("HCI_READ_BEST_EFFORT_FLUSH_TIMEOUT\n"));
+ status = bthci_CmdReadBestEffortFlushTimeout(padapter, pHciCmd);
+ break;
+ case HCI_WRITE_BEST_EFFORT_FLUSH_TIMEOUT:
+ RTPRINT(FIOCTL, IOCTL_BT_HCICMD, ("HCI_WRITE_BEST_EFFORT_FLUSH_TIMEOUT\n"));
+ status = bthci_CmdWriteBestEffortFlushTimeout(padapter, pHciCmd);
+ break;
+ case HCI_SHORT_RANGE_MODE:
+ RTPRINT(FIOCTL, IOCTL_BT_HCICMD, ("HCI_SHORT_RANGE_MODE\n"));
+ status = bthci_CmdShortRangeMode(padapter, pHciCmd);
+ break;
+ case HCI_HOST_BUFFER_SIZE:
+ RTPRINT(FIOCTL, IOCTL_BT_HCICMD, ("HCI_HOST_BUFFER_SIZE\n"));
+ status = bthci_CmdHostBufferSize(padapter, pHciCmd);
+ break;
+ default:
+ RTPRINT(FIOCTL, IOCTL_BT_HCICMD, ("bthci_HandleOGFSetEventMaskCMD(), Unknown case = 0x%x\n", pHciCmd->OCF));
+ RTPRINT(FIOCTL, IOCTL_BT_HCICMD, ("HCI_UNKNOWN_COMMAND\n"));
+ status = bthci_UnknownCMD(padapter, pHciCmd);
+ break;
+ }
+ return status;
+}
+
+static enum hci_status
+bthci_HandleOGFStatusParameters(struct rtw_adapter *padapter,
+ struct packet_irp_hcicmd_data *pHciCmd)
+{
+ enum hci_status status = HCI_STATUS_SUCCESS;
+
+ switch (pHciCmd->OCF) {
+ case HCI_READ_FAILED_CONTACT_COUNTER:
+ RTPRINT(FIOCTL, IOCTL_BT_HCICMD, ("HCI_READ_FAILED_CONTACT_COUNTER\n"));
+ status = bthci_CmdReadFailedContactCounter(padapter, pHciCmd);
+ break;
+ case HCI_RESET_FAILED_CONTACT_COUNTER:
+ RTPRINT(FIOCTL, IOCTL_BT_HCICMD, ("HCI_RESET_FAILED_CONTACT_COUNTER\n"));
+ status = bthci_CmdResetFailedContactCounter(padapter, pHciCmd);
+ break;
+ case HCI_READ_LINK_QUALITY:
+ RTPRINT(FIOCTL, IOCTL_BT_HCICMD, ("HCI_READ_LINK_QUALITY\n"));
+ status = bthci_CmdReadLinkQuality(padapter, pHciCmd);
+ break;
+ case HCI_READ_RSSI:
+ RTPRINT(FIOCTL, IOCTL_BT_HCICMD, ("HCI_READ_RSSI\n"));
+ break;
+ case HCI_READ_LOCAL_AMP_INFO:
+ RTPRINT(FIOCTL, IOCTL_BT_HCICMD, ("HCI_READ_LOCAL_AMP_INFO\n"));
+ status = bthci_CmdReadLocalAMPInfo(padapter);
+ break;
+ case HCI_READ_LOCAL_AMP_ASSOC:
+ RTPRINT(FIOCTL, IOCTL_BT_HCICMD, ("HCI_READ_LOCAL_AMP_ASSOC\n"));
+ status = bthci_CmdReadLocalAMPAssoc(padapter, pHciCmd);
+ break;
+ case HCI_WRITE_REMOTE_AMP_ASSOC:
+ RTPRINT(FIOCTL, IOCTL_BT_HCICMD, ("HCI_WRITE_REMOTE_AMP_ASSOC\n"));
+ status = bthci_CmdWriteRemoteAMPAssoc(padapter, pHciCmd);
+ break;
+ default:
+ RTPRINT(FIOCTL, IOCTL_BT_HCICMD, ("bthci_HandleOGFStatusParameters(), Unknown case = 0x%x\n", pHciCmd->OCF));
+ RTPRINT(FIOCTL, IOCTL_BT_HCICMD, ("HCI_UNKNOWN_COMMAND\n"));
+ status = bthci_UnknownCMD(padapter, pHciCmd);
+ break;
+ }
+ return status;
+}
+
+static enum hci_status
+bthci_HandleOGFLinkControlCMD(struct rtw_adapter *padapter,
+ struct packet_irp_hcicmd_data *pHciCmd)
+{
+ enum hci_status status = HCI_STATUS_SUCCESS;
+
+ switch (pHciCmd->OCF) {
+ case HCI_CREATE_PHYSICAL_LINK:
+ RTPRINT(FIOCTL, IOCTL_BT_HCICMD, ("HCI_CREATE_PHYSICAL_LINK\n"));
+ status = bthci_CmdCreatePhysicalLink(padapter, pHciCmd);
+ break;
+ case HCI_ACCEPT_PHYSICAL_LINK:
+ RTPRINT(FIOCTL, IOCTL_BT_HCICMD, ("HCI_ACCEPT_PHYSICAL_LINK\n"));
+ status = bthci_CmdAcceptPhysicalLink(padapter, pHciCmd);
+ break;
+ case HCI_DISCONNECT_PHYSICAL_LINK:
+ RTPRINT(FIOCTL, IOCTL_BT_HCICMD, ("HCI_DISCONNECT_PHYSICAL_LINK\n"));
+ status = bthci_CmdDisconnectPhysicalLink(padapter, pHciCmd);
+ break;
+ case HCI_CREATE_LOGICAL_LINK:
+ RTPRINT(FIOCTL, IOCTL_BT_HCICMD, ("HCI_CREATE_LOGICAL_LINK\n"));
+ status = bthci_CmdCreateLogicalLink(padapter, pHciCmd);
+ break;
+ case HCI_ACCEPT_LOGICAL_LINK:
+ RTPRINT(FIOCTL, IOCTL_BT_HCICMD, ("HCI_ACCEPT_LOGICAL_LINK\n"));
+ status = bthci_CmdAcceptLogicalLink(padapter, pHciCmd);
+ break;
+ case HCI_DISCONNECT_LOGICAL_LINK:
+ RTPRINT(FIOCTL, IOCTL_BT_HCICMD, ("HCI_DISCONNECT_LOGICAL_LINK\n"));
+ status = bthci_CmdDisconnectLogicalLink(padapter, pHciCmd);
+ break;
+ case HCI_LOGICAL_LINK_CANCEL:
+ RTPRINT(FIOCTL, IOCTL_BT_HCICMD, ("HCI_LOGICAL_LINK_CANCEL\n"));
+ status = bthci_CmdLogicalLinkCancel(padapter, pHciCmd);
+ break;
+ case HCI_FLOW_SPEC_MODIFY:
+ RTPRINT(FIOCTL, IOCTL_BT_HCICMD, ("HCI_FLOW_SPEC_MODIFY\n"));
+ status = bthci_CmdFlowSpecModify(padapter, pHciCmd);
+ break;
+ default:
+ RTPRINT(FIOCTL, IOCTL_BT_HCICMD, ("bthci_HandleOGFLinkControlCMD(), Unknown case = 0x%x\n", pHciCmd->OCF));
+ RTPRINT(FIOCTL, IOCTL_BT_HCICMD, ("HCI_UNKNOWN_COMMAND\n"));
+ status = bthci_UnknownCMD(padapter, pHciCmd);
+ break;
+ }
+ return status;
+}
+
+static enum hci_status
+bthci_HandleOGFTestingCMD(struct rtw_adapter *padapter,
+ struct packet_irp_hcicmd_data *pHciCmd)
+{
+ enum hci_status status = HCI_STATUS_SUCCESS;
+ switch (pHciCmd->OCF) {
+ case HCI_ENABLE_DEVICE_UNDER_TEST_MODE:
+ RTPRINT(FIOCTL, IOCTL_BT_HCICMD, ("HCI_ENABLE_DEVICE_UNDER_TEST_MODE\n"));
+ bthci_CmdEnableDeviceUnderTestMode(padapter, pHciCmd);
+ break;
+ case HCI_AMP_TEST_END:
+ RTPRINT(FIOCTL, IOCTL_BT_HCICMD, ("HCI_AMP_TEST_END\n"));
+ bthci_CmdAMPTestEnd(padapter, pHciCmd);
+ break;
+ case HCI_AMP_TEST_COMMAND:
+ RTPRINT(FIOCTL, IOCTL_BT_HCICMD, ("HCI_AMP_TEST_COMMAND\n"));
+ bthci_CmdAMPTestCommand(padapter, pHciCmd);
+ break;
+ case HCI_ENABLE_AMP_RECEIVER_REPORTS:
+ RTPRINT(FIOCTL, IOCTL_BT_HCICMD, ("HCI_ENABLE_AMP_RECEIVER_REPORTS\n"));
+ bthci_CmdEnableAMPReceiverReports(padapter, pHciCmd);
+ break;
+ default:
+ RTPRINT(FIOCTL, IOCTL_BT_HCICMD, ("HCI_UNKNOWN_COMMAND\n"));
+ status = bthci_UnknownCMD(padapter, pHciCmd);
+ break;
+ }
+ return status;
+}
+
+static enum hci_status
+bthci_HandleOGFExtension(struct rtw_adapter *padapter,
+ struct packet_irp_hcicmd_data *pHciCmd)
+{
+ enum hci_status status = HCI_STATUS_SUCCESS;
+ switch (pHciCmd->OCF) {
+ case HCI_SET_ACL_LINK_DATA_FLOW_MODE:
+ RTPRINT(FIOCTL, IOCTL_BT_HCICMD_EXT, ("HCI_SET_ACL_LINK_DATA_FLOW_MODE\n"));
+ status = bthci_CmdSetACLLinkDataFlowMode(padapter, pHciCmd);
+ break;
+ case HCI_SET_ACL_LINK_STATUS:
+ RTPRINT(FIOCTL, IOCTL_BT_HCICMD_EXT, ("HCI_SET_ACL_LINK_STATUS\n"));
+ status = bthci_CmdSetACLLinkStatus(padapter, pHciCmd);
+ break;
+ case HCI_SET_SCO_LINK_STATUS:
+ RTPRINT(FIOCTL, IOCTL_BT_HCICMD_EXT, ("HCI_SET_SCO_LINK_STATUS\n"));
+ status = bthci_CmdSetSCOLinkStatus(padapter, pHciCmd);
+ break;
+ case HCI_SET_RSSI_VALUE:
+ RTPRINT(FIOCTL, IOCTL_BT_EVENT_PERIODICAL, ("HCI_SET_RSSI_VALUE\n"));
+ status = bthci_CmdSetRSSIValue(padapter, pHciCmd);
+ break;
+ case HCI_SET_CURRENT_BLUETOOTH_STATUS:
+ RTPRINT(FIOCTL, IOCTL_BT_HCICMD_EXT, ("HCI_SET_CURRENT_BLUETOOTH_STATUS\n"));
+ status = bthci_CmdSetCurrentBluetoothStatus(padapter, pHciCmd);
+ break;
+ /* The following is for RTK8723 */
+
+ case HCI_EXTENSION_VERSION_NOTIFY:
+ RTPRINT(FIOCTL, IOCTL_BT_HCICMD_EXT, ("HCI_EXTENSION_VERSION_NOTIFY\n"));
+ status = bthci_CmdExtensionVersionNotify(padapter, pHciCmd);
+ break;
+ case HCI_LINK_STATUS_NOTIFY:
+ RTPRINT(FIOCTL, IOCTL_BT_HCICMD_EXT, ("HCI_LINK_STATUS_NOTIFY\n"));
+ status = bthci_CmdLinkStatusNotify(padapter, pHciCmd);
+ break;
+ case HCI_BT_OPERATION_NOTIFY:
+ RTPRINT(FIOCTL, IOCTL_BT_HCICMD_EXT, ("HCI_BT_OPERATION_NOTIFY\n"));
+ status = bthci_CmdBtOperationNotify(padapter, pHciCmd);
+ break;
+ case HCI_ENABLE_WIFI_SCAN_NOTIFY:
+ RTPRINT(FIOCTL, IOCTL_BT_HCICMD_EXT, ("HCI_ENABLE_WIFI_SCAN_NOTIFY\n"));
+ status = bthci_CmdEnableWifiScanNotify(padapter, pHciCmd);
+ break;
+
+ /* The following is for IVT */
+ case HCI_WIFI_CURRENT_CHANNEL:
+ RTPRINT(FIOCTL, IOCTL_BT_HCICMD_EXT, ("HCI_WIFI_CURRENT_CHANNEL\n"));
+ status = bthci_CmdWIFICurrentChannel(padapter, pHciCmd);
+ break;
+ case HCI_WIFI_CURRENT_BANDWIDTH:
+ RTPRINT(FIOCTL, IOCTL_BT_HCICMD_EXT, ("HCI_WIFI_CURRENT_BANDWIDTH\n"));
+ status = bthci_CmdWIFICurrentBandwidth(padapter, pHciCmd);
+ break;
+ case HCI_WIFI_CONNECTION_STATUS:
+ RTPRINT(FIOCTL, IOCTL_BT_HCICMD_EXT, ("HCI_WIFI_CONNECTION_STATUS\n"));
+ status = bthci_CmdWIFIConnectionStatus(padapter, pHciCmd);
+ break;
+
+ default:
+ RTPRINT(FIOCTL, IOCTL_BT_HCICMD_EXT, ("HCI_UNKNOWN_COMMAND\n"));
+ status = bthci_UnknownCMD(padapter, pHciCmd);
+ break;
+ }
+ return status;
+}
+
+static void
+bthci_StateStarting(struct rtw_adapter *padapter,
+ enum hci_state_with_cmd StateCmd, u8 EntryNum)
+{
+ struct bt_30info *pBTInfo = GET_BT_INFO(padapter);
+ struct bt_mgnt *pBtMgnt = &pBTInfo->BtMgnt;
+
+ RTPRINT(FIOCTL, IOCTL_STATE, ("[BT state], [Starting], "));
+ switch (StateCmd) {
+ case STATE_CMD_CONNECT_ACCEPT_TIMEOUT:
+ RTPRINT(FIOCTL, IOCTL_STATE, ("STATE_CMD_CONNECT_ACCEPT_TIMEOUT\n"));
+ pBTInfo->BtAsocEntry[EntryNum].PhysLinkCompleteStatus = HCI_STATUS_CONNECT_ACCEPT_TIMEOUT;
+ pBtMgnt->bNeedNotifyAMPNoCap = true;
+ BTHCI_DisconnectPeer(padapter, EntryNum);
+ break;
+ case STATE_CMD_DISCONNECT_PHY_LINK:
+ RTPRINT(FIOCTL, IOCTL_STATE, ("STATE_CMD_DISCONNECT_PHY_LINK\n"));
+
+ bthci_EventDisconnectPhyLinkComplete(padapter,
+ HCI_STATUS_SUCCESS,
+ pBTInfo->BtAsocEntry[EntryNum].PhyLinkDisconnectReason,
+ EntryNum);
+
+ del_timer_sync(&pBTInfo->BTHCIJoinTimeoutTimer);
+
+ pBTInfo->BtAsocEntry[EntryNum].PhysLinkCompleteStatus = HCI_STATUS_UNKNOW_CONNECT_ID;
+
+ BTHCI_DisconnectPeer(padapter, EntryNum);
+ break;
+ case STATE_CMD_MAC_START_COMPLETE:
+ RTPRINT(FIOCTL, IOCTL_STATE, ("STATE_CMD_MAC_START_COMPLETE\n"));
+ if (pBTInfo->BtAsocEntry[EntryNum].AMPRole == AMP_BTAP_CREATOR)
+ bthci_EventChannelSelected(padapter, EntryNum);
+ break;
+ default:
+ RTPRINT(FIOCTL, IOCTL_STATE, ("State command(%d) is Wrong !!!\n", StateCmd));
+ break;
+ }
+}
+
+static void
+bthci_StateConnecting(struct rtw_adapter *padapter,
+ enum hci_state_with_cmd StateCmd, u8 EntryNum)
+{
+ struct bt_30info *pBTInfo = GET_BT_INFO(padapter);
+ struct bt_mgnt *pBtMgnt = &pBTInfo->BtMgnt;
+
+ RTPRINT(FIOCTL, IOCTL_STATE, ("[BT state], [Connecting], "));
+ switch (StateCmd) {
+ case STATE_CMD_CONNECT_ACCEPT_TIMEOUT:
+ RTPRINT(FIOCTL, IOCTL_STATE, ("STATE_CMD_CONNECT_ACCEPT_TIMEOUT\n"));
+ pBTInfo->BtAsocEntry[EntryNum].PhysLinkCompleteStatus = HCI_STATUS_CONNECT_ACCEPT_TIMEOUT;
+ pBtMgnt->bNeedNotifyAMPNoCap = true;
+ BTHCI_DisconnectPeer(padapter, EntryNum);
+ break;
+ case STATE_CMD_MAC_CONNECT_COMPLETE:
+ RTPRINT(FIOCTL, IOCTL_STATE, ("STATE_CMD_MAC_CONNECT_COMPLETE\n"));
+
+ if (pBTInfo->BtAsocEntry[EntryNum].AMPRole == AMP_BTAP_JOINER) {
+ RT_TRACE(_module_rtl871x_security_c_, _drv_info_,
+ "StateConnecting\n");
+ }
+ break;
+ case STATE_CMD_DISCONNECT_PHY_LINK:
+ RTPRINT(FIOCTL, IOCTL_STATE, ("STATE_CMD_DISCONNECT_PHY_LINK\n"));
+
+ bthci_EventDisconnectPhyLinkComplete(padapter,
+ HCI_STATUS_SUCCESS,
+ pBTInfo->BtAsocEntry[EntryNum].PhyLinkDisconnectReason,
+ EntryNum);
+
+ pBTInfo->BtAsocEntry[EntryNum].PhysLinkCompleteStatus = HCI_STATUS_UNKNOW_CONNECT_ID;
+
+ del_timer_sync(&pBTInfo->BTHCIJoinTimeoutTimer);
+
+ BTHCI_DisconnectPeer(padapter, EntryNum);
+
+ break;
+ case STATE_CMD_MAC_CONNECT_CANCEL_INDICATE:
+ RTPRINT(FIOCTL, IOCTL_STATE, ("STATE_CMD_MAC_CONNECT_CANCEL_INDICATE\n"));
+ pBTInfo->BtAsocEntry[EntryNum].PhysLinkCompleteStatus = HCI_STATUS_CONTROLLER_BUSY;
+ /* Because this state cmd is caused by the BTHCI_EventAMPStatusChange(), */
+ /* we don't need to send event in the following BTHCI_DisconnectPeer() again. */
+ pBtMgnt->bNeedNotifyAMPNoCap = false;
+ BTHCI_DisconnectPeer(padapter, EntryNum);
+ break;
+ default:
+ RTPRINT(FIOCTL, IOCTL_STATE, ("State command(%d) is Wrong !!!\n", StateCmd));
+ break;
+ }
+}
+
+static void
+bthci_StateConnected(struct rtw_adapter *padapter,
+ enum hci_state_with_cmd StateCmd, u8 EntryNum)
+{
+/*PMGNT_INFO pMgntInfo = &padapter->MgntInfo; */
+ struct bt_30info *pBTInfo = GET_BT_INFO(padapter);
+ struct bt_mgnt *pBtMgnt = &pBTInfo->BtMgnt;
+ u8 i;
+ u16 logicHandle = 0;
+
+ RTPRINT(FIOCTL, IOCTL_STATE, ("[BT state], [Connected], "));
+ switch (StateCmd) {
+ case STATE_CMD_DISCONNECT_PHY_LINK:
+ RTPRINT(FIOCTL, IOCTL_STATE, ("STATE_CMD_DISCONNECT_PHY_LINK\n"));
+
+ /* When we are trying to disconnect the phy link, we should disconnect log link first, */
+ for (i = 0; i < MAX_LOGICAL_LINK_NUM; i++) {
+ if (pBTInfo->BtAsocEntry[EntryNum].LogLinkCmdData->BtLogLinkhandle != 0) {
+ logicHandle = pBTInfo->BtAsocEntry[EntryNum].LogLinkCmdData->BtLogLinkhandle;
+
+ bthci_EventDisconnectLogicalLinkComplete(padapter, HCI_STATUS_SUCCESS,
+ logicHandle, pBTInfo->BtAsocEntry[EntryNum].PhyLinkDisconnectReason);
+
+ pBTInfo->BtAsocEntry[EntryNum].LogLinkCmdData->BtLogLinkhandle = 0;
+ }
+ }
+
+ bthci_EventDisconnectPhyLinkComplete(padapter,
+ HCI_STATUS_SUCCESS,
+ pBTInfo->BtAsocEntry[EntryNum].PhyLinkDisconnectReason,
+ EntryNum);
+
+ del_timer_sync(&pBTInfo->BTHCIJoinTimeoutTimer);
+
+ BTHCI_DisconnectPeer(padapter, EntryNum);
+ break;
+
+ case STATE_CMD_MAC_DISCONNECT_INDICATE:
+ RTPRINT(FIOCTL, IOCTL_STATE, ("STATE_CMD_MAC_DISCONNECT_INDICATE\n"));
+
+ bthci_EventDisconnectPhyLinkComplete(padapter,
+ HCI_STATUS_SUCCESS,
+ /* TODO: Remote Host not local host */
+ HCI_STATUS_CONNECT_TERMINATE_LOCAL_HOST,
+ EntryNum);
+ BTHCI_DisconnectPeer(padapter, EntryNum);
+
+ break;
+ case STATE_CMD_ENTER_STATE:
+ RTPRINT(FIOCTL, IOCTL_STATE, ("STATE_CMD_ENTER_STATE\n"));
+
+ if (pBtMgnt->bBTConnectInProgress) {
+ pBtMgnt->bBTConnectInProgress = false;
+ RTPRINT(FIOCTL, IOCTL_STATE, ("[BT Flag], BT Connect in progress OFF!!\n"));
+ }
+ pBTInfo->BtAsocEntry[EntryNum].BtCurrentState = HCI_STATE_CONNECTED;
+ pBTInfo->BtAsocEntry[EntryNum].b4waySuccess = true;
+ pBtMgnt->bStartSendSupervisionPkt = true;
+
+ /* for rate adaptive */
+
+ rtl8723a_update_ramask(padapter,
+ MAX_FW_SUPPORT_MACID_NUM-1-EntryNum, 0);
+
+ HalSetBrateCfg23a(padapter, padapter->mlmepriv.cur_network.network.SupportedRates);
+ BTDM_SetFwChnlInfo(padapter, RT_MEDIA_CONNECT);
+ break;
+ default:
+ RTPRINT(FIOCTL, IOCTL_STATE, ("State command(%d) is Wrong !!!\n", StateCmd));
+ break;
+ }
+}
+
+static void
+bthci_StateAuth(struct rtw_adapter *padapter, enum hci_state_with_cmd StateCmd,
+ u8 EntryNum)
+{
+ struct bt_30info *pBTInfo = GET_BT_INFO(padapter);
+ struct bt_mgnt *pBtMgnt = &pBTInfo->BtMgnt;
+
+ RTPRINT(FIOCTL, IOCTL_STATE, ("[BT state], [Authenticating], "));
+ switch (StateCmd) {
+ case STATE_CMD_CONNECT_ACCEPT_TIMEOUT:
+ RTPRINT(FIOCTL, IOCTL_STATE, ("STATE_CMD_CONNECT_ACCEPT_TIMEOUT\n"));
+ pBTInfo->BtAsocEntry[EntryNum].PhysLinkCompleteStatus = HCI_STATUS_CONNECT_ACCEPT_TIMEOUT;
+ pBtMgnt->bNeedNotifyAMPNoCap = true;
+ BTHCI_DisconnectPeer(padapter, EntryNum);
+ break;
+ case STATE_CMD_DISCONNECT_PHY_LINK:
+ RTPRINT(FIOCTL, IOCTL_STATE, ("STATE_CMD_DISCONNECT_PHY_LINK\n"));
+ bthci_EventDisconnectPhyLinkComplete(padapter,
+ HCI_STATUS_SUCCESS,
+ pBTInfo->BtAsocEntry[EntryNum].PhyLinkDisconnectReason,
+ EntryNum);
+
+ pBTInfo->BtAsocEntry[EntryNum].PhysLinkCompleteStatus = HCI_STATUS_UNKNOW_CONNECT_ID;
+
+ del_timer_sync(&pBTInfo->BTHCIJoinTimeoutTimer);
+
+ BTHCI_DisconnectPeer(padapter, EntryNum);
+ break;
+ case STATE_CMD_4WAY_FAILED:
+ RTPRINT(FIOCTL, IOCTL_STATE, ("STATE_CMD_4WAY_FAILED\n"));
+
+ pBTInfo->BtAsocEntry[EntryNum].PhysLinkCompleteStatus = HCI_STATUS_AUTH_FAIL;
+ pBtMgnt->bNeedNotifyAMPNoCap = true;
+
+ BTHCI_DisconnectPeer(padapter, EntryNum);
+
+ del_timer_sync(&pBTInfo->BTHCIJoinTimeoutTimer);
+ break;
+ case STATE_CMD_4WAY_SUCCESSED:
+ RTPRINT(FIOCTL, IOCTL_STATE, ("STATE_CMD_4WAY_SUCCESSED\n"));
+
+ bthci_EventPhysicalLinkComplete(padapter, HCI_STATUS_SUCCESS, EntryNum, INVALID_PL_HANDLE);
+
+ del_timer_sync(&pBTInfo->BTHCIJoinTimeoutTimer);
+
+ BTHCI_SM_WITH_INFO(padapter, HCI_STATE_CONNECTED, STATE_CMD_ENTER_STATE, EntryNum);
+ break;
+ default:
+ RTPRINT(FIOCTL, IOCTL_STATE, ("State command(%d) is Wrong !!!\n", StateCmd));
+ break;
+ }
+}
+
+static void
+bthci_StateDisconnecting(struct rtw_adapter *padapter,
+ enum hci_state_with_cmd StateCmd, u8 EntryNum)
+{
+ struct bt_30info *pBTInfo = GET_BT_INFO(padapter);
+ struct bt_mgnt *pBtMgnt = &pBTInfo->BtMgnt;
+
+ RTPRINT(FIOCTL, IOCTL_STATE, ("[BT state], [Disconnecting], "));
+ switch (StateCmd) {
+ case STATE_CMD_MAC_CONNECT_CANCEL_INDICATE:
+ RTPRINT(FIOCTL, IOCTL_STATE, ("STATE_CMD_MAC_CONNECT_CANCEL_INDICATE\n"));
+ if (pBTInfo->BtAsocEntry[EntryNum].bNeedPhysLinkCompleteEvent) {
+ bthci_EventPhysicalLinkComplete(padapter,
+ pBTInfo->BtAsocEntry[EntryNum].PhysLinkCompleteStatus,
+ EntryNum, INVALID_PL_HANDLE);
+ }
+
+ if (pBtMgnt->bBTConnectInProgress) {
+ pBtMgnt->bBTConnectInProgress = false;
+ RTPRINT(FIOCTL, IOCTL_STATE, ("[BT Flag], BT Connect in progress OFF!!\n"));
+ }
+
+ BTHCI_SM_WITH_INFO(padapter, HCI_STATE_DISCONNECTED, STATE_CMD_ENTER_STATE, EntryNum);
+ break;
+ case STATE_CMD_DISCONNECT_PHY_LINK:
+ RTPRINT(FIOCTL, IOCTL_STATE, ("STATE_CMD_DISCONNECT_PHY_LINK\n"));
+
+ bthci_EventDisconnectPhyLinkComplete(padapter,
+ HCI_STATUS_SUCCESS,
+ pBTInfo->BtAsocEntry[EntryNum].PhyLinkDisconnectReason,
+ EntryNum);
+
+ del_timer_sync(&pBTInfo->BTHCIJoinTimeoutTimer);
+
+ BTHCI_DisconnectPeer(padapter, EntryNum);
+ break;
+ default:
+ RTPRINT(FIOCTL, IOCTL_STATE, ("State command(%d) is Wrong !!!\n", StateCmd));
+ break;
+ }
+}
+
+static void
+bthci_StateDisconnected(struct rtw_adapter *padapter,
+ enum hci_state_with_cmd StateCmd, u8 EntryNum)
+{
+/*PMGNT_INFO pMgntInfo = &padapter->MgntInfo; */
+ struct bt_30info *pBTInfo = GET_BT_INFO(padapter);
+ struct bt_hci_info *pBtHciInfo = &pBTInfo->BtHciInfo;
+ struct bt_mgnt *pBtMgnt = &pBTInfo->BtMgnt;
+
+ RTPRINT(FIOCTL, IOCTL_STATE, ("[BT state], [Disconnected], "));
+ switch (StateCmd) {
+ case STATE_CMD_CREATE_PHY_LINK:
+ case STATE_CMD_ACCEPT_PHY_LINK:
+ if (StateCmd == STATE_CMD_CREATE_PHY_LINK)
+ RTPRINT(FIOCTL, IOCTL_STATE, ("STATE_CMD_CREATE_PHY_LINK\n"));
+ else
+ RTPRINT(FIOCTL, IOCTL_STATE, ("STATE_CMD_ACCEPT_PHY_LINK\n"));
+
+ RTPRINT(FIOCTL, IOCTL_STATE, ("[BT PS], Disable IPS and LPS\n"));
+ ips_leave23a(padapter);
+ LPS_Leave23a(padapter);
+
+ pBtMgnt->bPhyLinkInProgress = true;
+ pBtMgnt->BTCurrentConnectType = BT_DISCONNECT;
+ pBtMgnt->CurrentBTConnectionCnt++;
+ RTPRINT(FIOCTL, IOCTL_STATE, ("[BT Flag], CurrentBTConnectionCnt = %d\n",
+ pBtMgnt->CurrentBTConnectionCnt));
+ pBtMgnt->BtOperationOn = true;
+ RTPRINT(FIOCTL, IOCTL_STATE, ("[BT Flag], Bt Operation ON!! CurrentConnectEntryNum = %d\n",
+ pBtMgnt->CurrentConnectEntryNum));
+
+ if (pBtMgnt->bBTConnectInProgress) {
+ bthci_EventPhysicalLinkComplete(padapter, HCI_STATUS_CONTROLLER_BUSY, INVALID_ENTRY_NUM, pBtMgnt->BtCurrentPhyLinkhandle);
+ bthci_RemoveEntryByEntryNum(padapter, EntryNum);
+ return;
+ }
+
+ if (StateCmd == STATE_CMD_CREATE_PHY_LINK)
+ pBTInfo->BtAsocEntry[EntryNum].AMPRole = AMP_BTAP_CREATOR;
+ else
+ pBTInfo->BtAsocEntry[EntryNum].AMPRole = AMP_BTAP_JOINER;
+
+ /* 1. MAC not yet in selected channel */
+ while (check_fwstate(&padapter->mlmepriv, WIFI_ASOC_STATE|WIFI_SITE_MONITOR)) {
+ RTPRINT(FIOCTL, IOCTL_STATE, ("Scan/Roaming/Wifi Link is in Progress, wait 200 ms\n"));
+ mdelay(200);
+ }
+ /* 2. MAC already in selected channel */
+ RTPRINT(FIOCTL, IOCTL_STATE, ("Channel is Ready\n"));
+ mod_timer(&pBTInfo->BTHCIJoinTimeoutTimer,
+ jiffies + msecs_to_jiffies(pBtHciInfo->ConnAcceptTimeout));
+
+ pBTInfo->BtAsocEntry[EntryNum].bNeedPhysLinkCompleteEvent = true;
+ break;
+ case STATE_CMD_DISCONNECT_PHY_LINK:
+ RTPRINT(FIOCTL, IOCTL_STATE, ("STATE_CMD_DISCONNECT_PHY_LINK\n"));
+
+ del_timer_sync(&pBTInfo->BTHCIJoinTimeoutTimer);
+
+ bthci_EventDisconnectPhyLinkComplete(padapter,
+ HCI_STATUS_SUCCESS,
+ pBTInfo->BtAsocEntry[EntryNum].PhyLinkDisconnectReason,
+ EntryNum);
+
+ if (pBTInfo->BtAsocEntry[EntryNum].bNeedPhysLinkCompleteEvent) {
+ bthci_EventPhysicalLinkComplete(padapter,
+ HCI_STATUS_UNKNOW_CONNECT_ID,
+ EntryNum, INVALID_PL_HANDLE);
+ }
+
+ if (pBtMgnt->bBTConnectInProgress) {
+ pBtMgnt->bBTConnectInProgress = false;
+ RTPRINT(FIOCTL, IOCTL_STATE, ("[BT Flag], BT Connect in progress OFF!!\n"));
+ }
+ BTHCI_SM_WITH_INFO(padapter, HCI_STATE_DISCONNECTED, STATE_CMD_ENTER_STATE, EntryNum);
+ bthci_RemoveEntryByEntryNum(padapter, EntryNum);
+ break;
+ case STATE_CMD_ENTER_STATE:
+ RTPRINT(FIOCTL, IOCTL_STATE, ("STATE_CMD_ENTER_STATE\n"));
+ break;
+ default:
+ RTPRINT(FIOCTL, IOCTL_STATE, ("State command(%d) is Wrong !!!\n", StateCmd));
+ break;
+ }
+}
+
+void BTHCI_EventParse(struct rtw_adapter *padapter, void *pEvntData, u32 dataLen)
+{
+}
+
+u8 BTHCI_HsConnectionEstablished(struct rtw_adapter *padapter)
+{
+ u8 bBtConnectionExist = false;
+ struct bt_30info *pBtinfo = GET_BT_INFO(padapter);
+ u8 i;
+
+ for (i = 0; i < MAX_BT_ASOC_ENTRY_NUM; i++) {
+ if (pBtinfo->BtAsocEntry[i].b4waySuccess) {
+ bBtConnectionExist = true;
+ break;
+ }
+ }
+
+/*RTPRINT(FIOCTL, IOCTL_STATE, (" BTHCI_HsConnectionEstablished(), connection exist = %d\n", bBtConnectionExist)); */
+
+ return bBtConnectionExist;
+}
+
+static u8
+BTHCI_CheckProfileExist(struct rtw_adapter *padapter,
+ enum bt_traffic_mode_profile Profile)
+{
+ struct bt_30info *pBTInfo = GET_BT_INFO(padapter);
+ struct bt_mgnt *pBtMgnt = &pBTInfo->BtMgnt;
+ u8 IsPRofile = false;
+ u8 i = 0;
+
+ for (i = 0; i < pBtMgnt->ExtConfig.NumberOfHandle; i++) {
+ if (pBtMgnt->ExtConfig.linkInfo[i].TrafficProfile == Profile) {
+ IsPRofile = true;
+ break;
+ }
+ }
+
+ return IsPRofile;
+}
+
+void BTHCI_UpdateBTProfileRTKToMoto(struct rtw_adapter *padapter)
+{
+ struct bt_30info *pBTInfo = GET_BT_INFO(padapter);
+ struct bt_mgnt *pBtMgnt = &pBTInfo->BtMgnt;
+ u8 i = 0;
+
+ pBtMgnt->ExtConfig.NumberOfSCO = 0;
+
+ for (i = 0; i < pBtMgnt->ExtConfig.NumberOfHandle; i++) {
+ pBtMgnt->ExtConfig.linkInfo[i].TrafficProfile = BT_PROFILE_NONE;
+
+ if (pBtMgnt->ExtConfig.linkInfo[i].BTProfile == BT_PROFILE_SCO)
+ pBtMgnt->ExtConfig.NumberOfSCO++;
+
+ pBtMgnt->ExtConfig.linkInfo[i].TrafficProfile = pBtMgnt->ExtConfig.linkInfo[i].BTProfile;
+ switch (pBtMgnt->ExtConfig.linkInfo[i].TrafficProfile) {
+ case BT_PROFILE_SCO:
+ break;
+ case BT_PROFILE_PAN:
+ pBtMgnt->ExtConfig.linkInfo[i].IncomingTrafficMode = BT_MOTOR_EXT_BE;
+ pBtMgnt->ExtConfig.linkInfo[i].OutgoingTrafficMode = BT_MOTOR_EXT_BE;
+ break;
+ case BT_PROFILE_A2DP:
+ pBtMgnt->ExtConfig.linkInfo[i].IncomingTrafficMode = BT_MOTOR_EXT_GULB;
+ pBtMgnt->ExtConfig.linkInfo[i].OutgoingTrafficMode = BT_MOTOR_EXT_GULB;
+ break;
+ case BT_PROFILE_HID:
+ pBtMgnt->ExtConfig.linkInfo[i].IncomingTrafficMode = BT_MOTOR_EXT_GUL;
+ pBtMgnt->ExtConfig.linkInfo[i].OutgoingTrafficMode = BT_MOTOR_EXT_BE;
+ break;
+ default:
+ break;
+ }
+ }
+
+ RTPRINT(FBT, BT_TRACE, ("[DM][BT], RTK, NumberOfHandle = %d, NumberOfSCO = %d\n",
+ pBtMgnt->ExtConfig.NumberOfHandle, pBtMgnt->ExtConfig.NumberOfSCO));
+}
+
+void BTHCI_WifiScanNotify(struct rtw_adapter *padapter, u8 scanType)
+{
+ struct bt_30info *pBTInfo = GET_BT_INFO(padapter);
+ struct bt_mgnt *pBtMgnt = &pBTInfo->BtMgnt;
+
+ if (pBtMgnt->ExtConfig.bEnableWifiScanNotify)
+ bthci_EventExtWifiScanNotify(padapter, scanType);
+}
+
+void
+BTHCI_StateMachine(
+ struct rtw_adapter *padapter,
+ u8 StateToEnter,
+ enum hci_state_with_cmd StateCmd,
+ u8 EntryNum
+ )
+{
+ struct bt_30info *pBTInfo = GET_BT_INFO(padapter);
+ struct bt_mgnt *pBtMgnt = &pBTInfo->BtMgnt;
+
+ if (EntryNum == 0xff) {
+ RTPRINT(FIOCTL, IOCTL_STATE, (" StateMachine, error EntryNum = 0x%x \n", EntryNum));
+ return;
+ }
+ RTPRINT(FIOCTL, IOCTL_STATE, (" StateMachine, EntryNum = 0x%x, CurrentState = 0x%x, BtNextState = 0x%x, StateCmd = 0x%x , StateToEnter = 0x%x\n",
+ EntryNum, pBTInfo->BtAsocEntry[EntryNum].BtCurrentState, pBTInfo->BtAsocEntry[EntryNum].BtNextState, StateCmd, StateToEnter));
+
+ if (pBTInfo->BtAsocEntry[EntryNum].BtNextState & StateToEnter) {
+ pBTInfo->BtAsocEntry[EntryNum].BtCurrentState = StateToEnter;
+
+ switch (StateToEnter) {
+ case HCI_STATE_STARTING:
+ pBTInfo->BtAsocEntry[EntryNum].BtNextState = HCI_STATE_DISCONNECTING | HCI_STATE_CONNECTING;
+ bthci_StateStarting(padapter, StateCmd, EntryNum);
+ break;
+ case HCI_STATE_CONNECTING:
+ pBTInfo->BtAsocEntry[EntryNum].BtNextState = HCI_STATE_CONNECTING | HCI_STATE_DISCONNECTING | HCI_STATE_AUTHENTICATING;
+ bthci_StateConnecting(padapter, StateCmd, EntryNum);
+ break;
+ case HCI_STATE_AUTHENTICATING:
+ pBTInfo->BtAsocEntry[EntryNum].BtNextState = HCI_STATE_DISCONNECTING | HCI_STATE_CONNECTED;
+ bthci_StateAuth(padapter, StateCmd, EntryNum);
+ break;
+ case HCI_STATE_CONNECTED:
+ pBTInfo->BtAsocEntry[EntryNum].BtNextState = HCI_STATE_CONNECTED | HCI_STATE_DISCONNECTING;
+ bthci_StateConnected(padapter, StateCmd, EntryNum);
+ break;
+ case HCI_STATE_DISCONNECTING:
+ pBTInfo->BtAsocEntry[EntryNum].BtNextState = HCI_STATE_DISCONNECTED | HCI_STATE_DISCONNECTING;
+ bthci_StateDisconnecting(padapter, StateCmd, EntryNum);
+ break;
+ case HCI_STATE_DISCONNECTED:
+ pBTInfo->BtAsocEntry[EntryNum].BtNextState = HCI_STATE_DISCONNECTED | HCI_STATE_STARTING | HCI_STATE_CONNECTING;
+ bthci_StateDisconnected(padapter, StateCmd, EntryNum);
+ break;
+ default:
+ RTPRINT(FIOCTL, IOCTL_STATE, (" StateMachine, Unknown state to enter!!!\n"));
+ break;
+ }
+ } else {
+ RTPRINT(FIOCTL, IOCTL_STATE, (" StateMachine, Wrong state to enter\n"));
+ }
+
+ /* 20100325 Joseph: Disable/Enable IPS/LPS according to BT status. */
+ if (!pBtMgnt->bBTConnectInProgress && !pBtMgnt->BtOperationOn) {
+ RTPRINT(FIOCTL, IOCTL_STATE, ("[BT PS], ips_enter23a()\n"));
+ ips_enter23a(padapter);
+ }
+}
+
+void BTHCI_DisconnectPeer(struct rtw_adapter *padapter, u8 EntryNum)
+{
+ struct bt_30info *pBTInfo = GET_BT_INFO(padapter);
+ struct bt_mgnt *pBtMgnt = &pBTInfo->BtMgnt;
+
+ RTPRINT(FIOCTL, IOCTL_BT_HCICMD, (" BTHCI_DisconnectPeer()\n"));
+
+ BTHCI_SM_WITH_INFO(padapter, HCI_STATE_DISCONNECTING, STATE_CMD_MAC_CONNECT_CANCEL_INDICATE, EntryNum);
+
+ if (pBTInfo->BtAsocEntry[EntryNum].bUsed) {
+/*BTPKT_SendDeauthentication(padapter, pBTInfo->BtAsocEntry[EntryNum].BTRemoteMACAddr, unspec_reason); not porting yet */
+ }
+
+ if (pBtMgnt->bBTConnectInProgress) {
+ pBtMgnt->bBTConnectInProgress = false;
+ RTPRINT(FIOCTL, IOCTL_STATE, ("[BT Flag], BT Connect in progress OFF!!\n"));
+ }
+
+ bthci_RemoveEntryByEntryNum(padapter, EntryNum);
+
+ if (pBtMgnt->bNeedNotifyAMPNoCap) {
+ RTPRINT(FIOCTL, IOCTL_STATE, ("[BT AMPStatus], set to invalid in BTHCI_DisconnectPeer()\n"));
+ BTHCI_EventAMPStatusChange(padapter, AMP_STATUS_NO_CAPACITY_FOR_BT);
+ }
+}
+
+void BTHCI_EventNumOfCompletedDataBlocks(struct rtw_adapter *padapter)
+{
+/*PMGNT_INFO pMgntInfo = &padapter->MgntInfo; */
+ struct bt_30info *pBTInfo = GET_BT_INFO(padapter);
+ struct bt_hci_info *pBtHciInfo = &pBTInfo->BtHciInfo;
+ u8 localBuf[TmpLocalBufSize] = "";
+ u8 *pRetPar, *pTriple;
+ u8 len = 0, i, j, handleNum = 0;
+ struct packet_irp_hcievent_data *PPacketIrpEvent;
+ u16 *pu2Temp, *pPackets, *pHandle, *pDblocks;
+ u8 sent = 0;
+
+ PPacketIrpEvent = (struct packet_irp_hcievent_data *)(&localBuf[0]);
+
+ if (!(pBtHciInfo->BTEventMaskPage2 & EMP2_HCI_EVENT_NUM_OF_COMPLETE_DATA_BLOCKS)) {
+ RTPRINT(FIOCTL, IOCTL_BT_EVENT, ("[BT event], Num Of Completed DataBlocks, Ignore to send NumOfCompletedDataBlocksEvent due to event mask page 2\n"));
+ return;
+ }
+
+ /* Return parameters starts from here */
+ pRetPar = &PPacketIrpEvent->Data[0];
+ pTriple = &pRetPar[3];
+ for (j = 0; j < MAX_BT_ASOC_ENTRY_NUM; j++) {
+
+ for (i = 0; i < MAX_LOGICAL_LINK_NUM; i++) {
+ if (pBTInfo->BtAsocEntry[j].LogLinkCmdData[i].BtLogLinkhandle) {
+ handleNum++;
+ pHandle = (u16 *)&pTriple[0]; /* Handle[i] */
+ pPackets = (u16 *)&pTriple[2]; /* Num_Of_Completed_Packets[i] */
+ pDblocks = (u16 *)&pTriple[4]; /* Num_Of_Completed_Blocks[i] */
+ *pHandle = pBTInfo->BtAsocEntry[j].LogLinkCmdData[i].BtLogLinkhandle;
+ *pPackets = (u16)pBTInfo->BtAsocEntry[j].LogLinkCmdData[i].TxPacketCount;
+ *pDblocks = (u16)pBTInfo->BtAsocEntry[j].LogLinkCmdData[i].TxPacketCount;
+ if (pBTInfo->BtAsocEntry[j].LogLinkCmdData[i].TxPacketCount) {
+ sent = 1;
+ RTPRINT(FIOCTL, IOCTL_BT_EVENT_DETAIL,
+ ("[BT event], Num Of Completed DataBlocks, Handle = 0x%x, Num_Of_Completed_Packets = 0x%x, Num_Of_Completed_Blocks = 0x%x\n",
+ *pHandle, *pPackets, *pDblocks));
+ }
+ pBTInfo->BtAsocEntry[j].LogLinkCmdData[i].TxPacketCount = 0;
+ len += 6;
+ pTriple += len;
+ }
+ }
+ }
+
+ pRetPar[2] = handleNum; /* Number_of_Handles */
+ len += 1;
+ pu2Temp = (u16 *)&pRetPar[0];
+ *pu2Temp = BTTotalDataBlockNum;
+ len += 2;
+
+ PPacketIrpEvent->EventCode = HCI_EVENT_NUM_OF_COMPLETE_DATA_BLOCKS;
+ PPacketIrpEvent->Length = len;
+ if (handleNum && sent)
+ bthci_IndicateEvent(padapter, PPacketIrpEvent, len+2);
+}
+
+void BTHCI_EventAMPStatusChange(struct rtw_adapter *padapter, u8 AMP_Status)
+{
+ struct bt_30info *pBTInfo = GET_BT_INFO(padapter);
+ struct bt_mgnt *pBtMgnt = &pBTInfo->BtMgnt;
+ struct packet_irp_hcievent_data *PPacketIrpEvent;
+ u8 len = 0;
+ u8 localBuf[7] = "";
+ u8 *pRetPar;
+
+ if (AMP_Status == AMP_STATUS_NO_CAPACITY_FOR_BT) {
+ pBtMgnt->BTNeedAMPStatusChg = true;
+ pBtMgnt->bNeedNotifyAMPNoCap = false;
+
+ BTHCI_DisconnectAll(padapter);
+ } else if (AMP_Status == AMP_STATUS_FULL_CAPACITY_FOR_BT) {
+ pBtMgnt->BTNeedAMPStatusChg = false;
+ }
+
+ PPacketIrpEvent = (struct packet_irp_hcievent_data *)(&localBuf[0]);
+ /* Return parameters starts from here */
+ pRetPar = &PPacketIrpEvent->Data[0];
+
+ pRetPar[0] = 0; /* Status */
+ len += 1;
+ pRetPar[1] = AMP_Status; /* AMP_Status */
+ len += 1;
+
+ PPacketIrpEvent->EventCode = HCI_EVENT_AMP_STATUS_CHANGE;
+ PPacketIrpEvent->Length = len;
+ if (bthci_IndicateEvent(padapter, PPacketIrpEvent, len+2) == RT_STATUS_SUCCESS)
+ RTPRINT(FIOCTL, (IOCTL_BT_EVENT|IOCTL_STATE), ("[BT event], AMP Status Change, AMP_Status = %d\n", AMP_Status));
+}
+
+void BTHCI_DisconnectAll(struct rtw_adapter *padapter)
+{
+ struct bt_30info *pBTInfo = GET_BT_INFO(padapter);
+ u8 i;
+
+ RTPRINT(FIOCTL, IOCTL_STATE, (" DisconnectALL()\n"));
+
+ for (i = 0; i < MAX_BT_ASOC_ENTRY_NUM; i++) {
+ if (pBTInfo->BtAsocEntry[i].b4waySuccess) {
+ BTHCI_SM_WITH_INFO(padapter, HCI_STATE_CONNECTED, STATE_CMD_DISCONNECT_PHY_LINK, i);
+ } else if (pBTInfo->BtAsocEntry[i].bUsed) {
+ if (pBTInfo->BtAsocEntry[i].BtCurrentState == HCI_STATE_CONNECTING) {
+ BTHCI_SM_WITH_INFO(padapter, HCI_STATE_CONNECTING, STATE_CMD_MAC_CONNECT_CANCEL_INDICATE, i);
+ } else if (pBTInfo->BtAsocEntry[i].BtCurrentState == HCI_STATE_DISCONNECTING) {
+ BTHCI_SM_WITH_INFO(padapter, HCI_STATE_DISCONNECTING, STATE_CMD_MAC_CONNECT_CANCEL_INDICATE, i);
+ }
+ }
+ }
+}
+
+enum hci_status
+BTHCI_HandleHCICMD(
+ struct rtw_adapter *padapter,
+ struct packet_irp_hcicmd_data *pHciCmd
+ )
+{
+ enum hci_status status = HCI_STATUS_SUCCESS;
+ struct bt_30info *pBTInfo = GET_BT_INFO(padapter);
+ struct bt_dgb *pBtDbg = &pBTInfo->BtDbg;
+
+ RTPRINT(FIOCTL, IOCTL_BT_HCICMD, ("\n"));
+ RTPRINT(FIOCTL, (IOCTL_BT_HCICMD_DETAIL|IOCTL_BT_LOGO), ("HCI Command start, OGF = 0x%x, OCF = 0x%x, Length = 0x%x\n",
+ pHciCmd->OGF, pHciCmd->OCF, pHciCmd->Length));
+ if (pHciCmd->Length) {
+ RTPRINT_DATA(FIOCTL, (IOCTL_BT_HCICMD_DETAIL|IOCTL_BT_LOGO), "HCI Command, Hex Data :\n",
+ &pHciCmd->Data[0], pHciCmd->Length);
+ }
+ if (pHciCmd->OGF == OGF_EXTENSION) {
+ if (pHciCmd->OCF == HCI_SET_RSSI_VALUE)
+ RTPRINT(FIOCTL, IOCTL_BT_EVENT_PERIODICAL, ("[BT cmd], "));
+ else
+ RTPRINT(FIOCTL, IOCTL_BT_HCICMD_EXT, ("[BT cmd], "));
+ } else {
+ RTPRINT(FIOCTL, IOCTL_BT_HCICMD, ("[BT cmd], "));
+ }
+
+ pBtDbg->dbgHciInfo.hciCmdCnt++;
+
+ switch (pHciCmd->OGF) {
+ case LINK_CONTROL_COMMANDS:
+ status = bthci_HandleOGFLinkControlCMD(padapter, pHciCmd);
+ break;
+ case HOLD_MODE_COMMAND:
+ break;
+ case OGF_SET_EVENT_MASK_COMMAND:
+ status = bthci_HandleOGFSetEventMaskCMD(padapter, pHciCmd);
+ break;
+ case OGF_INFORMATIONAL_PARAMETERS:
+ status = bthci_HandleOGFInformationalParameters(padapter, pHciCmd);
+ break;
+ case OGF_STATUS_PARAMETERS:
+ status = bthci_HandleOGFStatusParameters(padapter, pHciCmd);
+ break;
+ case OGF_TESTING_COMMANDS:
+ status = bthci_HandleOGFTestingCMD(padapter, pHciCmd);
+ break;
+ case OGF_EXTENSION:
+ status = bthci_HandleOGFExtension(padapter, pHciCmd);
+ break;
+ default:
+ RTPRINT(FIOCTL, IOCTL_BT_HCICMD, ("HCI Command(), Unknown OGF = 0x%x\n", pHciCmd->OGF));
+ RTPRINT(FIOCTL, IOCTL_BT_HCICMD, ("HCI_UNKNOWN_COMMAND\n"));
+ status = bthci_UnknownCMD(padapter, pHciCmd);
+ break;
+ }
+ RTPRINT(FIOCTL, IOCTL_BT_HCICMD_DETAIL, ("HCI Command execution end!!\n"));
+
+ return status;
+}
+
+/* ===== End of sync from SD7 driver COMMOM/bt_hci.c ===== */
+
+static const char *const BtStateString[] = {
+ "BT_DISABLED",
+ "BT_NO_CONNECTION",
+ "BT_CONNECT_IDLE",
+ "BT_INQ_OR_PAG",
+ "BT_ACL_ONLY_BUSY",
+ "BT_SCO_ONLY_BUSY",
+ "BT_ACL_SCO_BUSY",
+ "BT_ACL_INQ_OR_PAG",
+ "BT_STATE_NOT_DEFINED"
+};
+
+/* ===== Below this line is sync from SD7 driver HAL/BTCoexist/HalBtc87231Ant.c ===== */
+
+static void btdm_SetFwIgnoreWlanAct(struct rtw_adapter *padapter, u8 bEnable)
+{
+ struct hal_data_8723a *pHalData = GET_HAL_DATA(padapter);
+ u8 H2C_Parameter[1] = {0};
+
+ if (bEnable) {
+ RTPRINT(FBT, BT_TRACE, ("[BTCoex], BT Ignore Wlan_Act !!\n"));
+ H2C_Parameter[0] |= BIT(0); /* function enable */
+ pHalData->bt_coexist.bFWCoexistAllOff = false;
+ } else {
+ RTPRINT(FBT, BT_TRACE, ("[BTCoex], BT don't ignore Wlan_Act !!\n"));
+ }
+
+ RTPRINT(FBT, BT_TRACE, ("[BTCoex], set FW for BT Ignore Wlan_Act, write 0x25 = 0x%02x\n",
+ H2C_Parameter[0]));
+
+ FillH2CCmd(padapter, BT_IGNORE_WLAN_ACT_EID, 1, H2C_Parameter);
+}
+
+static void btdm_NotifyFwScan(struct rtw_adapter *padapter, u8 scanType)
+{
+ u8 H2C_Parameter[1] = {0};
+
+ if (scanType == true)
+ H2C_Parameter[0] = 0x1;
+
+ RTPRINT(FBT, BT_TRACE, ("[BTCoex], Notify FW for wifi scan, write 0x3b = 0x%02x\n",
+ H2C_Parameter[0]));
+
+ FillH2CCmd(padapter, 0x3b, 1, H2C_Parameter);
+}
+
+static void btdm_1AntSetPSMode(struct rtw_adapter *padapter,
+ u8 enable, u8 smartps, u8 mode)
+{
+ struct pwrctrl_priv *pwrctrl;
+
+ RTPRINT(FBT, BT_TRACE, ("[BTCoex], Current LPS(%s, %d), smartps =%d\n", enable == true?"ON":"OFF", mode, smartps));
+
+ pwrctrl = &padapter->pwrctrlpriv;
+
+ if (enable == true) {
+ rtw_set_ps_mode23a(padapter, PS_MODE_MIN, smartps, mode);
+ } else {
+ rtw_set_ps_mode23a(padapter, PS_MODE_ACTIVE, 0, 0);
+ LPS_RF_ON_check23a(padapter, 100);
+ }
+}
+
+static void btdm_1AntTSFSwitch(struct rtw_adapter *padapter, u8 enable)
+{
+ u8 oldVal, newVal;
+
+ oldVal = rtl8723au_read8(padapter, 0x550);
+
+ if (enable)
+ newVal = oldVal | EN_BCN_FUNCTION;
+ else
+ newVal = oldVal & ~EN_BCN_FUNCTION;
+
+ if (oldVal != newVal)
+ rtl8723au_write8(padapter, 0x550, newVal);
+}
+
+static u8 btdm_Is1AntPsTdmaStateChange(struct rtw_adapter *padapter)
+{
+ struct hal_data_8723a *pHalData = GET_HAL_DATA(padapter);
+ struct btdm_8723a_1ant *pBtdm8723 = &pHalData->bt_coexist.halCoex8723.btdm1Ant;
+
+ if ((pBtdm8723->bPrePsTdmaOn != pBtdm8723->bCurPsTdmaOn) ||
+ (pBtdm8723->prePsTdma != pBtdm8723->curPsTdma))
+ return true;
+ else
+ return false;
+}
+
+/* Before enter TDMA, make sure Power Saving is enable! */
+static void
+btdm_1AntPsTdma(
+ struct rtw_adapter *padapter,
+ u8 bTurnOn,
+ u8 type
+ )
+{
+ struct hal_data_8723a *pHalData = GET_HAL_DATA(padapter);
+ struct btdm_8723a_1ant *pBtdm8723 = &pHalData->bt_coexist.halCoex8723.btdm1Ant;
+
+ pBtdm8723->bCurPsTdmaOn = bTurnOn;
+ pBtdm8723->curPsTdma = type;
+ if (bTurnOn) {
+ switch (type) {
+ case 1: /* A2DP Level-1 or FTP/OPP */
+ default:
+ if (btdm_Is1AntPsTdmaStateChange(padapter)) {
+ /* wide duration for WiFi */
+ BTDM_SetFw3a(padapter, 0xd3, 0x1a, 0x1a, 0x0, 0x58);
+ }
+ break;
+ case 2: /* A2DP Level-2 */
+ if (btdm_Is1AntPsTdmaStateChange(padapter)) {
+ /* normal duration for WiFi */
+ BTDM_SetFw3a(padapter, 0xd3, 0x12, 0x12, 0x0, 0x58);
+ }
+ break;
+ case 3: /* BT FTP/OPP */
+ if (btdm_Is1AntPsTdmaStateChange(padapter)) {
+ /* normal duration for WiFi */
+ BTDM_SetFw3a(padapter, 0xd3, 0x30, 0x03, 0x10, 0x58);
+
+ }
+ break;
+ case 4: /* for wifi scan & BT is connected */
+ if (btdm_Is1AntPsTdmaStateChange(padapter)) {
+ /* protect 3 beacons in 3-beacon period & no Tx pause at BT slot */
+ BTDM_SetFw3a(padapter, 0x93, 0x15, 0x03, 0x14, 0x0);
+ }
+ break;
+ case 5: /* for WiFi connected-busy & BT is Non-Connected-Idle */
+ if (btdm_Is1AntPsTdmaStateChange(padapter)) {
+ /* SCO mode, Ant fixed at WiFi, WLAN_Act toggle */
+ BTDM_SetFw3a(padapter, 0x61, 0x15, 0x03, 0x31, 0x00);
+ }
+ break;
+ case 9: /* ACL high-retry type - 2 */
+ if (btdm_Is1AntPsTdmaStateChange(padapter)) {
+ /* narrow duration for WiFi */
+ BTDM_SetFw3a(padapter, 0xd3, 0xa, 0xa, 0x0, 0x58); /* narrow duration for WiFi */
+ }
+ break;
+ case 10: /* for WiFi connect idle & BT ACL busy or WiFi Connected-Busy & BT is Inquiry */
+ if (btdm_Is1AntPsTdmaStateChange(padapter))
+ BTDM_SetFw3a(padapter, 0x13, 0xa, 0xa, 0x0, 0x40);
+ break;
+ case 11: /* ACL high-retry type - 3 */
+ if (btdm_Is1AntPsTdmaStateChange(padapter)) {
+ /* narrow duration for WiFi */
+ BTDM_SetFw3a(padapter, 0xd3, 0x05, 0x05, 0x00, 0x58);
+ }
+ break;
+ case 12: /* for WiFi Connected-Busy & BT is Connected-Idle */
+ if (btdm_Is1AntPsTdmaStateChange(padapter)) {
+ /* Allow High-Pri BT */
+ BTDM_SetFw3a(padapter, 0xeb, 0x0a, 0x03, 0x31, 0x18);
+ }
+ break;
+ case 20: /* WiFi only busy , TDMA mode for power saving */
+ if (btdm_Is1AntPsTdmaStateChange(padapter))
+ BTDM_SetFw3a(padapter, 0x13, 0x25, 0x25, 0x00, 0x00);
+ break;
+ case 27: /* WiFi DHCP/Site Survey & BT SCO busy */
+ if (btdm_Is1AntPsTdmaStateChange(padapter))
+ BTDM_SetFw3a(padapter, 0xa3, 0x25, 0x03, 0x31, 0x98);
+ break;
+ case 28: /* WiFi DHCP/Site Survey & BT idle */
+ if (btdm_Is1AntPsTdmaStateChange(padapter))
+ BTDM_SetFw3a(padapter, 0x69, 0x25, 0x03, 0x31, 0x00);
+ break;
+ case 29: /* WiFi DHCP/Site Survey & BT ACL busy */
+ if (btdm_Is1AntPsTdmaStateChange(padapter)) {
+ BTDM_SetFw3a(padapter, 0xeb, 0x1a, 0x1a, 0x01, 0x18);
+ rtl8723au_write32(padapter, 0x6c0, 0x5afa5afa);
+ rtl8723au_write32(padapter, 0x6c4, 0x5afa5afa);
+ }
+ break;
+ case 30: /* WiFi idle & BT Inquiry */
+ if (btdm_Is1AntPsTdmaStateChange(padapter))
+ BTDM_SetFw3a(padapter, 0x93, 0x15, 0x03, 0x14, 0x00);
+ break;
+ case 31: /* BT HID */
+ if (btdm_Is1AntPsTdmaStateChange(padapter))
+ BTDM_SetFw3a(padapter, 0xd3, 0x1a, 0x1a, 0x00, 0x58);
+ break;
+ case 32: /* BT SCO & Inquiry */
+ if (btdm_Is1AntPsTdmaStateChange(padapter))
+ BTDM_SetFw3a(padapter, 0xab, 0x0a, 0x03, 0x11, 0x98);
+ break;
+ case 33: /* BT SCO & WiFi site survey */
+ if (btdm_Is1AntPsTdmaStateChange(padapter))
+ BTDM_SetFw3a(padapter, 0xa3, 0x25, 0x03, 0x30, 0x98);
+ break;
+ case 34: /* BT HID & WiFi site survey */
+ if (btdm_Is1AntPsTdmaStateChange(padapter))
+ BTDM_SetFw3a(padapter, 0xd3, 0x1a, 0x1a, 0x00, 0x18);
+ break;
+ case 35: /* BT HID & WiFi Connecting */
+ if (btdm_Is1AntPsTdmaStateChange(padapter))
+ BTDM_SetFw3a(padapter, 0xe3, 0x1a, 0x1a, 0x00, 0x18);
+ break;
+ }
+ } else {
+ /* disable PS-TDMA */
+ switch (type) {
+ case 8:
+ if (btdm_Is1AntPsTdmaStateChange(padapter)) {
+ /* Antenna control by PTA, 0x870 = 0x310 */
+ BTDM_SetFw3a(padapter, 0x8, 0x0, 0x0, 0x0, 0x0);
+ }
+ break;
+ case 0:
+ default:
+ if (btdm_Is1AntPsTdmaStateChange(padapter)) {
+ /* Antenna control by PTA, 0x870 = 0x310 */
+ BTDM_SetFw3a(padapter, 0x0, 0x0, 0x0, 0x8, 0x0);
+ }
+ /* Switch Antenna to BT */
+ rtl8723au_write16(padapter, 0x860, 0x210);
+ RTPRINT(FBT, BT_TRACE, ("[BTCoex], 0x860 = 0x210, Switch Antenna to BT\n"));
+ break;
+ case 9:
+ if (btdm_Is1AntPsTdmaStateChange(padapter)) {
+ /* Antenna control by PTA, 0x870 = 0x310 */
+ BTDM_SetFw3a(padapter, 0x0, 0x0, 0x0, 0x8, 0x0);
+ }
+ /* Switch Antenna to WiFi */
+ rtl8723au_write16(padapter, 0x860, 0x110);
+ RTPRINT(FBT, BT_TRACE, ("[BTCoex], 0x860 = 0x110, Switch Antenna to WiFi\n"));
+ break;
+ }
+ }
+
+ RTPRINT(FBT, BT_TRACE, ("[BTCoex], Current TDMA(%s, %d)\n",
+ pBtdm8723->bCurPsTdmaOn?"ON":"OFF", pBtdm8723->curPsTdma));
+
+ /* update pre state */
+ pBtdm8723->bPrePsTdmaOn = pBtdm8723->bCurPsTdmaOn;
+ pBtdm8723->prePsTdma = pBtdm8723->curPsTdma;
+}
+
+static void
+_btdm_1AntSetPSTDMA(struct rtw_adapter *padapter, u8 bPSEn, u8 smartps,
+ u8 psOption, u8 bTDMAOn, u8 tdmaType)
+{
+ struct pwrctrl_priv *pwrctrl;
+ struct hal_data_8723a *pHalData;
+ struct btdm_8723a_1ant *pBtdm8723;
+ u8 psMode;
+ u8 bSwitchPS;
+
+ if (!check_fwstate(&padapter->mlmepriv, WIFI_STATION_STATE) &&
+ (get_fwstate(&padapter->mlmepriv) != WIFI_NULL_STATE)) {
+ btdm_1AntPsTdma(padapter, bTDMAOn, tdmaType);
+ return;
+ }
+ psOption &= ~BIT(0);
+
+ RTPRINT(FBT, BT_TRACE,
+ ("[BTCoex], Set LPS(%s, %d) TDMA(%s, %d)\n",
+ bPSEn == true?"ON":"OFF", psOption,
+ bTDMAOn == true?"ON":"OFF", tdmaType));
+
+ pwrctrl = &padapter->pwrctrlpriv;
+ pHalData = GET_HAL_DATA(padapter);
+ pBtdm8723 = &pHalData->bt_coexist.halCoex8723.btdm1Ant;
+
+ if (bPSEn) {
+ if (pBtdm8723->bWiFiHalt) {
+ RTPRINT(FBT, BT_TRACE, ("[BTCoex], Enable PS Fail, WiFi in Halt!!\n"));
+ return;
+ }
+
+ if (pwrctrl->bInSuspend) {
+ RTPRINT(FBT, BT_TRACE, ("[BTCoex], Enable PS Fail, WiFi in Suspend!!\n"));
+ return;
+ }
+
+ if (padapter->bDriverStopped) {
+ RTPRINT(FBT, BT_TRACE, ("[BTCoex], Enable PS Fail, WiFi driver stopped!!\n"));
+ return;
+ }
+
+ if (padapter->bSurpriseRemoved) {
+ RTPRINT(FBT, BT_TRACE, ("[BTCoex], Enable PS Fail, WiFi Surprise Removed!!\n"));
+ return;
+ }
+
+ psMode = PS_MODE_MIN;
+ } else {
+ psMode = PS_MODE_ACTIVE;
+ psOption = 0;
+ }
+
+ if (psMode != pwrctrl->pwr_mode) {
+ bSwitchPS = true;
+ } else if (psMode != PS_MODE_ACTIVE) {
+ if (psOption != pwrctrl->bcn_ant_mode)
+ bSwitchPS = true;
+ else if (smartps != pwrctrl->smart_ps)
+ bSwitchPS = true;
+ else
+ bSwitchPS = false;
+ } else {
+ bSwitchPS = false;
+ }
+
+ if (bSwitchPS) {
+ /* disable TDMA */
+ if (pBtdm8723->bCurPsTdmaOn) {
+ if (!bTDMAOn) {
+ btdm_1AntPsTdma(padapter, false, tdmaType);
+ } else {
+ if (!rtl8723a_BT_enabled(padapter) ||
+ (pHalData->bt_coexist.halCoex8723.c2hBtInfo == BT_INFO_STATE_NO_CONNECTION) ||
+ (pHalData->bt_coexist.halCoex8723.c2hBtInfo == BT_INFO_STATE_CONNECT_IDLE) ||
+ (tdmaType == 29))
+ btdm_1AntPsTdma(padapter, false, 9);
+ else
+ btdm_1AntPsTdma(padapter, false, 0);
+ }
+ }
+
+ /* change Power Save State */
+ btdm_1AntSetPSMode(padapter, bPSEn, smartps, psOption);
+ }
+
+ btdm_1AntPsTdma(padapter, bTDMAOn, tdmaType);
+}
+
+static void
+btdm_1AntSetPSTDMA(struct rtw_adapter *padapter, u8 bPSEn,
+ u8 psOption, u8 bTDMAOn, u8 tdmaType)
+{
+ _btdm_1AntSetPSTDMA(padapter, bPSEn, 0, psOption, bTDMAOn, tdmaType);
+}
+
+static void btdm_1AntWifiParaAdjust(struct rtw_adapter *padapter, u8 bEnable)
+{
+ struct hal_data_8723a *pHalData = GET_HAL_DATA(padapter);
+ struct btdm_8723a_1ant *pBtdm8723 = &pHalData->bt_coexist.halCoex8723.btdm1Ant;
+
+ if (bEnable) {
+ pBtdm8723->curWifiPara = 1;
+ if (pBtdm8723->preWifiPara != pBtdm8723->curWifiPara)
+ BTDM_SetSwPenaltyTxRateAdaptive(padapter, BT_TX_RATE_ADAPTIVE_LOW_PENALTY);
+ } else {
+ pBtdm8723->curWifiPara = 2;
+ if (pBtdm8723->preWifiPara != pBtdm8723->curWifiPara)
+ BTDM_SetSwPenaltyTxRateAdaptive(padapter, BT_TX_RATE_ADAPTIVE_NORMAL);
+ }
+
+}
+
+static void btdm_1AntPtaParaReload(struct rtw_adapter *padapter)
+{
+ /* PTA parameter */
+ rtl8723au_write8(padapter, 0x6cc, 0x0); /* 1-Ant coex */
+ rtl8723au_write32(padapter, 0x6c8, 0xffff); /* wifi break table */
+ rtl8723au_write32(padapter, 0x6c4, 0x55555555); /* coex table */
+
+ /* Antenna switch control parameter */
+ rtl8723au_write32(padapter, 0x858, 0xaaaaaaaa);
+ if (IS_8723A_A_CUT(GET_HAL_DATA(padapter)->VersionID)) {
+ /* SPDT(connected with TRSW) control by hardware PTA */
+ rtl8723au_write32(padapter, 0x870, 0x0);
+ rtl8723au_write8(padapter, 0x40, 0x24);
+ } else {
+ rtl8723au_write8(padapter, 0x40, 0x20);
+ /* set antenna at bt side if ANTSW is software control */
+ rtl8723au_write16(padapter, 0x860, 0x210);
+ /* SPDT(connected with TRSW) control by hardware PTA */
+ rtl8723au_write32(padapter, 0x870, 0x300);
+ /* ANTSW keep by GNT_BT */
+ rtl8723au_write32(padapter, 0x874, 0x22804000);
+ }
+
+ /* coexistence parameters */
+ rtl8723au_write8(padapter, 0x778, 0x1); /* enable RTK mode PTA */
+
+ /* BT don't ignore WLAN_Act */
+ btdm_SetFwIgnoreWlanAct(padapter, false);
+}
+
+/*
+ * Return
+ *1: upgrade (add WiFi duration time)
+ *0: keep
+ *-1: downgrade (add BT duration time)
+ */
+static s8 btdm_1AntTdmaJudgement(struct rtw_adapter *padapter, u8 retry)
+{
+ struct hal_data_8723a *pHalData;
+ struct btdm_8723a_1ant *pBtdm8723;
+ static s8 up, dn, m = 1, n = 3, WaitCount;
+ s8 ret;
+
+ pHalData = GET_HAL_DATA(padapter);
+ pBtdm8723 = &pHalData->bt_coexist.halCoex8723.btdm1Ant;
+ ret = 0;
+
+ if (pBtdm8723->psTdmaMonitorCnt == 0) {
+ up = 0;
+ dn = 0;
+ m = 1;
+ n = 3;
+ WaitCount = 0;
+ } else {
+ WaitCount++;
+ }
+
+ if (retry == 0) {
+ /* no retry in the last 2-second duration */
+ up++;
+ dn--;
+ if (dn < 0)
+ dn = 0;
+ if (up >= 3*m) {
+ /* retry = 0 in consecutive 3m*(2s), add WiFi duration */
+ ret = 1;
+
+ n = 3;
+ up = 0;
+ dn = 0;
+ WaitCount = 0;
+ }
+ } else if (retry <= 3) {
+ /* retry<= 3 in the last 2-second duration */
+ up--;
+ dn++;
+ if (up < 0)
+ up = 0;
+
+ if (dn == 2) {
+ /* retry<= 3 in consecutive 2*(2s), minus WiFi duration (add BT duration) */
+ ret = -1;
+
+ /* record how many time downgrad WiFi duration */
+ if (WaitCount <= 2)
+ m++;
+ else
+ m = 1;
+ /* the max number of m is 20 */
+ /* the longest time of upgrade WiFi duration is 20*3*2s = 120s */
+ if (m >= 20)
+ m = 20;
+ up = 0;
+ dn = 0;
+ WaitCount = 0;
+ }
+ } else {
+ /* retry count > 3 */
+ /* retry>3, minus WiFi duration (add BT duration) */
+ ret = -1;
+
+ /* record how many time downgrad WiFi duration */
+ if (WaitCount == 1)
+ m++;
+ else
+ m = 1;
+ if (m >= 20)
+ m = 20;
+
+ up = 0;
+ dn = 0;
+ WaitCount = 0;
+ }
+ return ret;
+}
+
+static void btdm_1AntTdmaDurationAdjustForACL(struct rtw_adapter *padapter)
+{
+ struct hal_data_8723a *pHalData = GET_HAL_DATA(padapter);
+ struct btdm_8723a_1ant *pBtdm8723 = &pHalData->bt_coexist.halCoex8723.btdm1Ant;
+
+ if (pBtdm8723->psTdmaGlobalCnt != pBtdm8723->psTdmaMonitorCnt) {
+ pBtdm8723->psTdmaMonitorCnt = 0;
+ pBtdm8723->psTdmaGlobalCnt = 0;
+ }
+ if (pBtdm8723->psTdmaMonitorCnt == 0) {
+ btdm_1AntSetPSTDMA(padapter, true, 0, true, 2);
+ pBtdm8723->psTdmaDuAdjType = 2;
+ } else {
+ /* Now we only have 4 level Ps Tdma, */
+ /* if that's not the following 4 level(will changed by wifi scan, dhcp...), */
+ /* then we have to adjust it back to the previous record one. */
+ if ((pBtdm8723->curPsTdma != 1) &&
+ (pBtdm8723->curPsTdma != 2) &&
+ (pBtdm8723->curPsTdma != 9) &&
+ (pBtdm8723->curPsTdma != 11)) {
+ btdm_1AntSetPSTDMA(padapter, true, 0, true, pBtdm8723->psTdmaDuAdjType);
+ } else {
+ s32 judge = 0;
+
+ judge = btdm_1AntTdmaJudgement(padapter, pHalData->bt_coexist.halCoex8723.btRetryCnt);
+ if (judge == -1) {
+ if (pBtdm8723->curPsTdma == 1) {
+ /* Decrease WiFi duration for high BT retry */
+ if (pHalData->bt_coexist.halCoex8723.btInfoExt)
+ pBtdm8723->psTdmaDuAdjType = 9;
+ else
+ pBtdm8723->psTdmaDuAdjType = 2;
+ btdm_1AntSetPSTDMA(padapter, true, 0, true, pBtdm8723->psTdmaDuAdjType);
+ } else if (pBtdm8723->curPsTdma == 2) {
+ btdm_1AntSetPSTDMA(padapter, true, 0, true, 9);
+ pBtdm8723->psTdmaDuAdjType = 9;
+ } else if (pBtdm8723->curPsTdma == 9) {
+ btdm_1AntSetPSTDMA(padapter, true, 0, true, 11);
+ pBtdm8723->psTdmaDuAdjType = 11;
+ }
+ } else if (judge == 1) {
+ if (pBtdm8723->curPsTdma == 11) {
+ btdm_1AntSetPSTDMA(padapter, true, 0, true, 9);
+ pBtdm8723->psTdmaDuAdjType = 9;
+ } else if (pBtdm8723->curPsTdma == 9) {
+ if (pHalData->bt_coexist.halCoex8723.btInfoExt)
+ pBtdm8723->psTdmaDuAdjType = 9;
+ else
+ pBtdm8723->psTdmaDuAdjType = 2;
+ btdm_1AntSetPSTDMA(padapter, true, 0, true, pBtdm8723->psTdmaDuAdjType);
+ } else if (pBtdm8723->curPsTdma == 2) {
+ if (pHalData->bt_coexist.halCoex8723.btInfoExt)
+ pBtdm8723->psTdmaDuAdjType = 9;
+ else
+ pBtdm8723->psTdmaDuAdjType = 1;
+ btdm_1AntSetPSTDMA(padapter, true, 0, true, pBtdm8723->psTdmaDuAdjType);
+ }
+ }
+ }
+ RTPRINT(FBT, BT_TRACE,
+ ("[BTCoex], ACL current TDMA(%s, %d)\n",
+ (pBtdm8723->bCurPsTdmaOn ? "ON" : "OFF"), pBtdm8723->curPsTdma));
+ }
+ pBtdm8723->psTdmaMonitorCnt++;
+}
+
+static void btdm_1AntCoexProcessForWifiConnect(struct rtw_adapter *padapter)
+{
+ struct mlme_priv *pmlmepriv;
+ struct hal_data_8723a *pHalData;
+ struct bt_coexist_8723a *pBtCoex;
+ struct btdm_8723a_1ant *pBtdm8723;
+ u8 BtState;
+
+ pmlmepriv = &padapter->mlmepriv;
+ pHalData = GET_HAL_DATA(padapter);
+ pBtCoex = &pHalData->bt_coexist.halCoex8723;
+ pBtdm8723 = &pBtCoex->btdm1Ant;
+ BtState = pBtCoex->c2hBtInfo;
+
+ RTPRINT(FBT, BT_TRACE, ("[BTCoex], WiFi is %s\n",
+ BTDM_IsWifiBusy(padapter)?"Busy":"IDLE"));
+ RTPRINT(FBT, BT_TRACE, ("[BTCoex], BT is %s\n",
+ BtStateString[BtState]));
+
+ padapter->pwrctrlpriv.btcoex_rfon = false;
+
+ if (!BTDM_IsWifiBusy(padapter) &&
+ !check_fwstate(&padapter->mlmepriv, WIFI_AP_STATE) &&
+ (BtState == BT_INFO_STATE_NO_CONNECTION ||
+ BtState == BT_INFO_STATE_CONNECT_IDLE)) {
+ switch (BtState) {
+ case BT_INFO_STATE_NO_CONNECTION:
+ _btdm_1AntSetPSTDMA(padapter, true, 2, 0x26, false, 9);
+ break;
+ case BT_INFO_STATE_CONNECT_IDLE:
+ _btdm_1AntSetPSTDMA(padapter, true, 2, 0x26, false, 0);
+ break;
+ }
+ } else {
+ switch (BtState) {
+ case BT_INFO_STATE_NO_CONNECTION:
+ case BT_INFO_STATE_CONNECT_IDLE:
+ /* WiFi is Busy */
+ btdm_1AntSetPSTDMA(padapter, false, 0, true, 5);
+ rtl8723au_write32(padapter, 0x6c0, 0x5a5a5a5a);
+ rtl8723au_write32(padapter, 0x6c4, 0x5a5a5a5a);
+ break;
+ case BT_INFO_STATE_ACL_INQ_OR_PAG:
+ RTPRINT(FBT, BT_TRACE,
+ ("[BTCoex], BT PROFILE is "
+ "BT_INFO_STATE_ACL_INQ_OR_PAG\n"));
+ case BT_INFO_STATE_INQ_OR_PAG:
+ padapter->pwrctrlpriv.btcoex_rfon = true;
+ btdm_1AntSetPSTDMA(padapter, true, 0, true, 30);
+ break;
+ case BT_INFO_STATE_SCO_ONLY_BUSY:
+ case BT_INFO_STATE_ACL_SCO_BUSY:
+ if (true == pBtCoex->bC2hBtInquiryPage)
+ btdm_1AntSetPSTDMA(padapter, false, 0,
+ true, 32);
+ else {
+#ifdef BTCOEX_CMCC_TEST
+ btdm_1AntSetPSTDMA(padapter, false, 0,
+ true, 23);
+#else /* !BTCOEX_CMCC_TEST */
+ btdm_1AntSetPSTDMA(padapter, false, 0,
+ false, 8);
+ rtl8723au_write32(padapter, 0x6c0, 0x5a5a5a5a);
+ rtl8723au_write32(padapter, 0x6c4, 0x5a5a5a5a);
+#endif /* !BTCOEX_CMCC_TEST */
+ }
+ break;
+ case BT_INFO_STATE_ACL_ONLY_BUSY:
+ padapter->pwrctrlpriv.btcoex_rfon = true;
+ if (pBtCoex->c2hBtProfile == BT_INFO_HID) {
+ RTPRINT(FBT, BT_TRACE,
+ ("[BTCoex], BT PROFILE is HID\n"));
+ btdm_1AntSetPSTDMA(padapter, true, 0, true, 31);
+ } else if (pBtCoex->c2hBtProfile == BT_INFO_FTP) {
+ RTPRINT(FBT, BT_TRACE,
+ ("[BTCoex], BT PROFILE is FTP/OPP\n"));
+ btdm_1AntSetPSTDMA(padapter, true, 0, true, 3);
+ } else if (pBtCoex->c2hBtProfile == (BT_INFO_A2DP|BT_INFO_FTP)) {
+ RTPRINT(FBT, BT_TRACE,
+ ("[BTCoex], BT PROFILE is A2DP_FTP\n"));
+ btdm_1AntSetPSTDMA(padapter, true, 0, true, 11);
+ } else {
+ if (pBtCoex->c2hBtProfile == BT_INFO_A2DP)
+ RTPRINT(FBT, BT_TRACE,
+ ("[BTCoex], BT PROFILE is "
+ "A2DP\n"));
+ else
+ RTPRINT(FBT, BT_TRACE,
+ ("[BTCoex], BT PROFILE is "
+ "UNKNOWN(0x%02X)! Use A2DP "
+ "Profile\n",
+ pBtCoex->c2hBtProfile));
+ btdm_1AntTdmaDurationAdjustForACL(padapter);
+ }
+ break;
+ }
+ }
+
+ pBtdm8723->psTdmaGlobalCnt++;
+}
+
+static void
+btdm_1AntUpdateHalRAMask(struct rtw_adapter *padapter, u32 mac_id, u32 filter)
+{
+ u8 init_rate = 0;
+ u8 raid, arg;
+ u32 mask;
+ u8 shortGIrate = false;
+ int supportRateNum = 0;
+ struct sta_info *psta;
+ struct hal_data_8723a *pHalData;
+ struct dm_priv *pdmpriv;
+ struct mlme_ext_priv *pmlmeext;
+ struct mlme_ext_info *pmlmeinfo;
+ struct wlan_bssid_ex *cur_network;
+
+ RTPRINT(FBT, BT_TRACE, ("[BTCoex], %s, MACID =%d, filter = 0x%08x!!\n",
+ __func__, mac_id, filter));
+
+ pHalData = GET_HAL_DATA(padapter);
+ pdmpriv = &pHalData->dmpriv;
+ pmlmeext = &padapter->mlmeextpriv;
+ pmlmeinfo = &pmlmeext->mlmext_info;
+ cur_network = &pmlmeinfo->network;
+
+ if (mac_id >= NUM_STA) { /* CAM_SIZE */
+ RTPRINT(FBT, BT_TRACE, ("[BTCoex], %s, MACID =%d illegal!!\n",
+ __func__, mac_id));
+ return;
+ }
+
+ psta = pmlmeinfo->FW_sta_info[mac_id].psta;
+ if (!psta) {
+ RTPRINT(FBT, BT_TRACE, ("[BTCoex], %s, Can't find station!!\n",
+ __func__));
+ return;
+ }
+
+ raid = psta->raid;
+
+ switch (mac_id) {
+ case 0:/* for infra mode */
+ supportRateNum =
+ rtw_get_rateset_len23a(cur_network->SupportedRates);
+ mask = update_supported_rate23a(cur_network->SupportedRates,
+ supportRateNum);
+ mask |= (pmlmeinfo->HT_enable) ?
+ update_MSC_rate23a(&pmlmeinfo->ht_cap):0;
+ if (support_short_GI23a(padapter, &pmlmeinfo->ht_cap))
+ shortGIrate = true;
+ break;
+ case 1:/* for broadcast/multicast */
+ supportRateNum = rtw_get_rateset_len23a(
+ pmlmeinfo->FW_sta_info[mac_id].SupportedRates);
+ mask = update_basic_rate23a(cur_network->SupportedRates,
+ supportRateNum);
+ break;
+ default: /* for each sta in IBSS */
+ supportRateNum = rtw_get_rateset_len23a(
+ pmlmeinfo->FW_sta_info[mac_id].SupportedRates);
+ mask = update_supported_rate23a(cur_network->SupportedRates,
+ supportRateNum);
+ break;
+ }
+ mask |= ((raid<<28)&0xf0000000);
+ mask &= 0xffffffff;
+ mask &= ~filter;
+ init_rate = get_highest_rate_idx23a(mask)&0x3f;
+
+ arg = mac_id&0x1f;/* MACID */
+ arg |= BIT(7);
+ if (true == shortGIrate)
+ arg |= BIT(5);
+
+ RTPRINT(FBT, BT_TRACE,
+ ("[BTCoex], Update FW RAID entry, MASK = 0x%08x, "
+ "arg = 0x%02x\n", mask, arg));
+
+ rtl8723a_set_raid_cmd(padapter, mask, arg);
+
+ psta->init_rate = init_rate;
+ pdmpriv->INIDATA_RATE[mac_id] = init_rate;
+}
+
+static void
+btdm_1AntUpdateHalRAMaskForSCO(struct rtw_adapter *padapter, u8 forceUpdate)
+{
+ struct btdm_8723a_1ant *pBtdm8723;
+ struct sta_priv *pstapriv;
+ struct wlan_bssid_ex *cur_network;
+ struct sta_info *psta;
+ u32 macid;
+ u32 filter = 0;
+
+ pBtdm8723 = &GET_HAL_DATA(padapter)->bt_coexist.halCoex8723.btdm1Ant;
+
+ if (pBtdm8723->bRAChanged == true && forceUpdate == false)
+ return;
+
+ pstapriv = &padapter->stapriv;
+ cur_network = &padapter->mlmeextpriv.mlmext_info.network;
+ psta = rtw_get_stainfo23a(pstapriv, cur_network->MacAddress);
+ macid = psta->mac_id;
+
+ filter |= BIT(_1M_RATE_);
+ filter |= BIT(_2M_RATE_);
+ filter |= BIT(_5M_RATE_);
+ filter |= BIT(_11M_RATE_);
+ filter |= BIT(_6M_RATE_);
+ filter |= BIT(_9M_RATE_);
+
+ btdm_1AntUpdateHalRAMask(padapter, macid, filter);
+
+ pBtdm8723->bRAChanged = true;
+}
+
+static void btdm_1AntRecoverHalRAMask(struct rtw_adapter *padapter)
+{
+ struct btdm_8723a_1ant *pBtdm8723;
+ struct sta_priv *pstapriv;
+ struct wlan_bssid_ex *cur_network;
+ struct sta_info *psta;
+
+ pBtdm8723 = &GET_HAL_DATA(padapter)->bt_coexist.halCoex8723.btdm1Ant;
+
+ if (pBtdm8723->bRAChanged == false)
+ return;
+
+ pstapriv = &padapter->stapriv;
+ cur_network = &padapter->mlmeextpriv.mlmext_info.network;
+ psta = rtw_get_stainfo23a(pstapriv, cur_network->MacAddress);
+
+ Update_RA_Entry23a(padapter, psta);
+
+ pBtdm8723->bRAChanged = false;
+}
+
+static void
+btdm_1AntBTStateChangeHandler(struct rtw_adapter *padapter,
+ enum bt_state_1ant oldState,
+ enum bt_state_1ant newState)
+{
+ struct hal_data_8723a *phaldata;
+ RTPRINT(FBT, BT_TRACE, ("[BTCoex], BT state change, %s => %s\n",
+ BtStateString[oldState],
+ BtStateString[newState]));
+
+ /* BT default ignore wlan active, */
+ /* WiFi MUST disable this when BT is enable */
+ if (newState > BT_INFO_STATE_DISABLED)
+ btdm_SetFwIgnoreWlanAct(padapter, false);
+
+ if ((check_fwstate(&padapter->mlmepriv, WIFI_STATION_STATE)) &&
+ (BTDM_IsWifiConnectionExist(padapter))) {
+ if ((newState == BT_INFO_STATE_SCO_ONLY_BUSY) ||
+ (newState == BT_INFO_STATE_ACL_SCO_BUSY)) {
+ btdm_1AntUpdateHalRAMaskForSCO(padapter, false);
+ } else {
+ /* Recover original RA setting */
+ btdm_1AntRecoverHalRAMask(padapter);
+ }
+ } else {
+ phaldata = GET_HAL_DATA(padapter);
+ phaldata->bt_coexist.halCoex8723.btdm1Ant.bRAChanged = false;
+ }
+
+ if (oldState == newState)
+ return;
+
+ if (oldState == BT_INFO_STATE_ACL_ONLY_BUSY) {
+ struct hal_data_8723a *Hal = GET_HAL_DATA(padapter);
+ Hal->bt_coexist.halCoex8723.btdm1Ant.psTdmaMonitorCnt = 0;
+ Hal->bt_coexist.halCoex8723.btdm1Ant.psTdmaMonitorCntForSCO = 0;
+ }
+
+ if ((oldState == BT_INFO_STATE_SCO_ONLY_BUSY) ||
+ (oldState == BT_INFO_STATE_ACL_SCO_BUSY)) {
+ struct hal_data_8723a *Hal = GET_HAL_DATA(padapter);
+ Hal->bt_coexist.halCoex8723.btdm1Ant.psTdmaMonitorCntForSCO = 0;
+ }
+
+ /* Active 2Ant mechanism when BT Connected */
+ if ((oldState == BT_INFO_STATE_DISABLED) ||
+ (oldState == BT_INFO_STATE_NO_CONNECTION)) {
+ if ((newState != BT_INFO_STATE_DISABLED) &&
+ (newState != BT_INFO_STATE_NO_CONNECTION)) {
+ BTDM_SetSwRfRxLpfCorner(padapter,
+ BT_RF_RX_LPF_CORNER_SHRINK);
+ BTDM_AGCTable(padapter, BT_AGCTABLE_ON);
+ BTDM_BBBackOffLevel(padapter, BT_BB_BACKOFF_ON);
+ }
+ } else {
+ if ((newState == BT_INFO_STATE_DISABLED) ||
+ (newState == BT_INFO_STATE_NO_CONNECTION)) {
+ BTDM_SetSwRfRxLpfCorner(padapter,
+ BT_RF_RX_LPF_CORNER_RESUME);
+ BTDM_AGCTable(padapter, BT_AGCTABLE_OFF);
+ BTDM_BBBackOffLevel(padapter, BT_BB_BACKOFF_OFF);
+ }
+ }
+}
+
+static void btdm_1AntBtCoexistHandler(struct rtw_adapter *padapter)
+{
+ struct hal_data_8723a *pHalData;
+ struct bt_coexist_8723a *pBtCoex8723;
+ struct btdm_8723a_1ant *pBtdm8723;
+
+ pHalData = GET_HAL_DATA(padapter);
+ pBtCoex8723 = &pHalData->bt_coexist.halCoex8723;
+ pBtdm8723 = &pBtCoex8723->btdm1Ant;
+ padapter->pwrctrlpriv.btcoex_rfon = false;
+ if (!rtl8723a_BT_enabled(padapter)) {
+ RTPRINT(FBT, BT_TRACE, ("[BTCoex], BT is disabled\n"));
+
+ if (BTDM_IsWifiConnectionExist(padapter)) {
+ RTPRINT(FBT, BT_TRACE,
+ ("[BTCoex], wifi is connected\n"));
+
+ if (BTDM_IsWifiBusy(padapter)) {
+ RTPRINT(FBT, BT_TRACE,
+ ("[BTCoex], Wifi is busy\n"));
+ btdm_1AntSetPSTDMA(padapter, false, 0,
+ false, 9);
+ } else {
+ RTPRINT(FBT, BT_TRACE,
+ ("[BTCoex], Wifi is idle\n"));
+ _btdm_1AntSetPSTDMA(padapter, true, 2, 1,
+ false, 9);
+ }
+ } else {
+ RTPRINT(FBT, BT_TRACE,
+ ("[BTCoex], wifi is disconnected\n"));
+
+ btdm_1AntSetPSTDMA(padapter, false, 0, false, 9);
+ }
+ } else {
+ RTPRINT(FBT, BT_TRACE, ("[BTCoex], BT is enabled\n"));
+
+ if (BTDM_IsWifiConnectionExist(padapter)) {
+ RTPRINT(FBT, BT_TRACE,
+ ("[BTCoex], wifi is connected\n"));
+
+ btdm_1AntWifiParaAdjust(padapter, true);
+ btdm_1AntCoexProcessForWifiConnect(padapter);
+ } else {
+ RTPRINT(FBT, BT_TRACE,
+ ("[BTCoex], wifi is disconnected\n"));
+
+ /* Antenna switch at BT side(0x870 = 0x300,
+ 0x860 = 0x210) after PSTDMA off */
+ btdm_1AntWifiParaAdjust(padapter, false);
+ btdm_1AntSetPSTDMA(padapter, false, 0, false, 0);
+ }
+ }
+
+ btdm_1AntBTStateChangeHandler(padapter, pBtCoex8723->prec2hBtInfo,
+ pBtCoex8723->c2hBtInfo);
+ pBtCoex8723->prec2hBtInfo = pBtCoex8723->c2hBtInfo;
+}
+
+void BTDM_1AntSignalCompensation(struct rtw_adapter *padapter,
+ u8 *rssi_wifi, u8 *rssi_bt)
+{
+ struct hal_data_8723a *pHalData;
+ struct btdm_8723a_1ant *pBtdm8723;
+ u8 RSSI_WiFi_Cmpnstn, RSSI_BT_Cmpnstn;
+
+ pHalData = GET_HAL_DATA(padapter);
+ pBtdm8723 = &pHalData->bt_coexist.halCoex8723.btdm1Ant;
+ RSSI_WiFi_Cmpnstn = 0;
+ RSSI_BT_Cmpnstn = 0;
+
+ switch (pBtdm8723->curPsTdma) {
+ case 1: /* WiFi 52ms */
+ RSSI_WiFi_Cmpnstn = 11; /* 22*0.48 */
+ break;
+ case 2: /* WiFi 36ms */
+ RSSI_WiFi_Cmpnstn = 14; /* 22*0.64 */
+ break;
+ case 9: /* WiFi 20ms */
+ RSSI_WiFi_Cmpnstn = 18; /* 22*0.80 */
+ break;
+ case 11: /* WiFi 10ms */
+ RSSI_WiFi_Cmpnstn = 20; /* 22*0.90 */
+ break;
+ case 4: /* WiFi 21ms */
+ RSSI_WiFi_Cmpnstn = 17; /* 22*0.79 */
+ break;
+ case 16: /* WiFi 24ms */
+ RSSI_WiFi_Cmpnstn = 18; /* 22*0.76 */
+ break;
+ case 18: /* WiFi 37ms */
+ RSSI_WiFi_Cmpnstn = 14; /* 22*0.64 */
+ break;
+ case 23: /* Level-1, Antenna switch to BT at all time */
+ case 24: /* Level-2, Antenna switch to BT at all time */
+ case 25: /* Level-3a, Antenna switch to BT at all time */
+ case 26: /* Level-3b, Antenna switch to BT at all time */
+ case 27: /* Level-3b, Antenna switch to BT at all time */
+ case 33: /* BT SCO & WiFi site survey */
+ RSSI_WiFi_Cmpnstn = 22;
+ break;
+ default:
+ break;
+ }
+
+ if (rssi_wifi && RSSI_WiFi_Cmpnstn) {
+ RTPRINT(FBT, BT_TRACE,
+ ("[BTCoex], 1AntSgnlCmpnstn, case %d, WiFiCmpnstn "
+ "=%d(%d => %d)\n", pBtdm8723->curPsTdma,
+ RSSI_WiFi_Cmpnstn, *rssi_wifi,
+ *rssi_wifi+RSSI_WiFi_Cmpnstn));
+ *rssi_wifi += RSSI_WiFi_Cmpnstn;
+ }
+
+ if (rssi_bt && RSSI_BT_Cmpnstn) {
+ RTPRINT(FBT, BT_TRACE,
+ ("[BTCoex], 1AntSgnlCmpnstn, case %d, BTCmpnstn "
+ "=%d(%d => %d)\n", pBtdm8723->curPsTdma,
+ RSSI_BT_Cmpnstn, *rssi_bt, *rssi_bt+RSSI_BT_Cmpnstn));
+ *rssi_bt += RSSI_BT_Cmpnstn;
+ }
+}
+
+static void BTDM_1AntParaInit(struct rtw_adapter *padapter)
+{
+ struct hal_data_8723a *pHalData;
+ struct bt_coexist_8723a *pBtCoex;
+ struct btdm_8723a_1ant *pBtdm8723;
+
+ pHalData = GET_HAL_DATA(padapter);
+ pBtCoex = &pHalData->bt_coexist.halCoex8723;
+ pBtdm8723 = &pBtCoex->btdm1Ant;
+
+ /* Enable counter statistics */
+ rtl8723au_write8(padapter, 0x76e, 0x4);
+ btdm_1AntPtaParaReload(padapter);
+
+ pBtdm8723->wifiRssiThresh = 48;
+
+ pBtdm8723->bWiFiHalt = false;
+ pBtdm8723->bRAChanged = false;
+
+ if ((pBtCoex->c2hBtInfo != BT_INFO_STATE_DISABLED) &&
+ (pBtCoex->c2hBtInfo != BT_INFO_STATE_NO_CONNECTION)) {
+ BTDM_SetSwRfRxLpfCorner(padapter, BT_RF_RX_LPF_CORNER_SHRINK);
+ BTDM_AGCTable(padapter, BT_AGCTABLE_ON);
+ BTDM_BBBackOffLevel(padapter, BT_BB_BACKOFF_ON);
+ }
+}
+
+static void BTDM_1AntForHalt(struct rtw_adapter *padapter)
+{
+ RTPRINT(FBT, BT_TRACE, ("\n[BTCoex], 1Ant for halt\n"));
+
+ GET_HAL_DATA(padapter)->bt_coexist.halCoex8723.btdm1Ant.bWiFiHalt =
+ true;
+
+ btdm_1AntWifiParaAdjust(padapter, false);
+
+ /* don't use btdm_1AntSetPSTDMA() here */
+ /* it will call rtw_set_ps_mode23a() and request pwrpriv->lock. */
+ /* This will lead to deadlock, if this function is called in IPS */
+ /* Lucas@20130205 */
+ btdm_1AntPsTdma(padapter, false, 0);
+
+ btdm_SetFwIgnoreWlanAct(padapter, true);
+}
+
+static void BTDM_1AntLpsLeave(struct rtw_adapter *padapter)
+{
+ RTPRINT(FBT, BT_TRACE, ("\n[BTCoex], 1Ant for LPS Leave\n"));
+
+ /* Prevent from entering LPS again */
+ GET_HAL_DATA(padapter)->bt_coexist.halCoex8723.btdm1Ant.bWiFiHalt =
+ true;
+
+ btdm_1AntSetPSTDMA(padapter, false, 0, false, 8);
+/*btdm_1AntPsTdma(padapter, false, 8); */
+}
+
+static void BTDM_1AntWifiAssociateNotify(struct rtw_adapter *padapter, u8 type)
+{
+ struct hal_data_8723a *pHalData = GET_HAL_DATA(padapter);
+
+ RTPRINT(FBT, BT_TRACE,
+ ("\n[BTCoex], 1Ant for associate, type =%d\n", type));
+
+ if (type) {
+ rtl8723a_CheckAntenna_Selection(padapter);
+ if (!rtl8723a_BT_enabled(padapter))
+ btdm_1AntSetPSTDMA(padapter, false, 0, false, 9);
+ else {
+ struct bt_coexist_8723a *pBtCoex;
+ u8 BtState;
+
+ pBtCoex = &pHalData->bt_coexist.halCoex8723;
+ BtState = pBtCoex->c2hBtInfo;
+
+ btdm_1AntTSFSwitch(padapter, true);
+
+ if (BtState == BT_INFO_STATE_NO_CONNECTION ||
+ BtState == BT_INFO_STATE_CONNECT_IDLE) {
+ btdm_1AntSetPSTDMA(padapter, false, 0,
+ true, 28);
+ } else if (BtState == BT_INFO_STATE_SCO_ONLY_BUSY ||
+ BtState == BT_INFO_STATE_ACL_SCO_BUSY) {
+ btdm_1AntSetPSTDMA(padapter, false, 0,
+ false, 8);
+ rtl8723au_write32(padapter, 0x6c0, 0x5a5a5a5a);
+ rtl8723au_write32(padapter, 0x6c4, 0x5a5a5a5a);
+ } else if (BtState == BT_INFO_STATE_ACL_ONLY_BUSY ||
+ BtState == BT_INFO_STATE_ACL_INQ_OR_PAG) {
+ if (pBtCoex->c2hBtProfile == BT_INFO_HID)
+ btdm_1AntSetPSTDMA(padapter, false, 0,
+ true, 35);
+ else
+ btdm_1AntSetPSTDMA(padapter, false, 0,
+ true, 29);
+ }
+ }
+ } else {
+ if (!rtl8723a_BT_enabled(padapter)) {
+ if (!BTDM_IsWifiConnectionExist(padapter)) {
+ btdm_1AntPsTdma(padapter, false, 0);
+ btdm_1AntTSFSwitch(padapter, false);
+ }
+ }
+
+ btdm_1AntBtCoexistHandler(padapter);
+ }
+}
+
+static void
+BTDM_1AntMediaStatusNotify(struct rtw_adapter *padapter,
+ enum rt_media_status mstatus)
+{
+ struct bt_coexist_8723a *pBtCoex;
+
+ pBtCoex = &GET_HAL_DATA(padapter)->bt_coexist.halCoex8723;
+
+ RTPRINT(FBT, BT_TRACE,
+ ("\n\n[BTCoex]******************************\n"));
+ RTPRINT(FBT, BT_TRACE, ("[BTCoex], MediaStatus, WiFi %s !!\n",
+ mstatus == RT_MEDIA_CONNECT?"CONNECT":"DISCONNECT"));
+ RTPRINT(FBT, BT_TRACE, ("[BTCoex]******************************\n"));
+
+ if (RT_MEDIA_CONNECT == mstatus) {
+ if (check_fwstate(&padapter->mlmepriv, WIFI_STATION_STATE)) {
+ if (pBtCoex->c2hBtInfo == BT_INFO_STATE_SCO_ONLY_BUSY ||
+ pBtCoex->c2hBtInfo == BT_INFO_STATE_ACL_SCO_BUSY)
+ btdm_1AntUpdateHalRAMaskForSCO(padapter, true);
+ }
+
+ padapter->pwrctrlpriv.DelayLPSLastTimeStamp = jiffies;
+ BTDM_1AntForDhcp(padapter);
+ } else {
+ /* DBG_8723A("%s rtl8723a_DeinitAntenna_Selection\n",
+ __func__); */
+ rtl8723a_DeinitAntenna_Selection(padapter);
+ btdm_1AntBtCoexistHandler(padapter);
+ pBtCoex->btdm1Ant.bRAChanged = false;
+ }
+}
+
+void BTDM_1AntForDhcp(struct rtw_adapter *padapter)
+{
+ struct hal_data_8723a *pHalData;
+ u8 BtState;
+ struct bt_coexist_8723a *pBtCoex;
+ struct btdm_8723a_1ant *pBtdm8723;
+
+ pHalData = GET_HAL_DATA(padapter);
+ pBtCoex = &pHalData->bt_coexist.halCoex8723;
+ BtState = pBtCoex->c2hBtInfo;
+ pBtdm8723 = &pBtCoex->btdm1Ant;
+
+ RTPRINT(FBT, BT_TRACE, ("\n[BTCoex], 1Ant for DHCP\n"));
+ RTPRINT(FBT, BT_TRACE, ("[BTCoex], 1Ant for DHCP, WiFi is %s\n",
+ BTDM_IsWifiBusy(padapter)?"Busy":"IDLE"));
+ RTPRINT(FBT, BT_TRACE, ("[BTCoex], 1Ant for DHCP, %s\n",
+ BtStateString[BtState]));
+
+ BTDM_1AntWifiAssociateNotify(padapter, true);
+}
+
+static void BTDM_1AntWifiScanNotify(struct rtw_adapter *padapter, u8 scanType)
+{
+ struct hal_data_8723a *pHalData;
+ u8 BtState;
+ struct bt_coexist_8723a *pBtCoex;
+ struct btdm_8723a_1ant *pBtdm8723;
+
+ pHalData = GET_HAL_DATA(padapter);
+ BtState = pHalData->bt_coexist.halCoex8723.c2hBtInfo;
+ pBtCoex = &pHalData->bt_coexist.halCoex8723;
+ pBtdm8723 = &pBtCoex->btdm1Ant;
+
+ RTPRINT(FBT, BT_TRACE, ("\n[BTCoex], 1Ant for wifi scan =%d!!\n",
+ scanType));
+ RTPRINT(FBT, BT_TRACE, ("[BTCoex], 1Ant for wifi scan, WiFi is %s\n",
+ BTDM_IsWifiBusy(padapter)?"Busy":"IDLE"));
+ RTPRINT(FBT, BT_TRACE, ("[BTCoex], 1Ant for wifi scan, %s\n",
+ BtStateString[BtState]));
+
+ if (scanType) {
+ rtl8723a_CheckAntenna_Selection(padapter);
+ if (!rtl8723a_BT_enabled(padapter)) {
+ btdm_1AntSetPSTDMA(padapter, false, 0, false, 9);
+ } else if (BTDM_IsWifiConnectionExist(padapter) == false) {
+ BTDM_1AntWifiAssociateNotify(padapter, true);
+ } else {
+ if ((BtState == BT_INFO_STATE_SCO_ONLY_BUSY) ||
+ (BtState == BT_INFO_STATE_ACL_SCO_BUSY)) {
+ if (pBtCoex->bC2hBtInquiryPage) {
+ btdm_1AntSetPSTDMA(padapter, false, 0,
+ true, 32);
+ } else {
+ padapter->pwrctrlpriv.btcoex_rfon =
+ true;
+ btdm_1AntSetPSTDMA(padapter, true, 0,
+ true, 33);
+ }
+ } else if (true == pBtCoex->bC2hBtInquiryPage) {
+ padapter->pwrctrlpriv.btcoex_rfon = true;
+ btdm_1AntSetPSTDMA(padapter, true, 0, true, 30);
+ } else if (BtState == BT_INFO_STATE_ACL_ONLY_BUSY) {
+ padapter->pwrctrlpriv.btcoex_rfon = true;
+ if (pBtCoex->c2hBtProfile == BT_INFO_HID)
+ btdm_1AntSetPSTDMA(padapter, true, 0,
+ true, 34);
+ else
+ btdm_1AntSetPSTDMA(padapter, true, 0,
+ true, 4);
+ } else {
+ padapter->pwrctrlpriv.btcoex_rfon = true;
+ btdm_1AntSetPSTDMA(padapter, true, 0, true, 5);
+ }
+ }
+
+ btdm_NotifyFwScan(padapter, 1);
+ } else {
+ /* WiFi_Finish_Scan */
+ btdm_NotifyFwScan(padapter, 0);
+ btdm_1AntBtCoexistHandler(padapter);
+ }
+}
+
+static void BTDM_1AntFwC2hBtInfo8723A(struct rtw_adapter *padapter)
+{
+ struct hal_data_8723a *pHalData;
+ struct bt_30info *pBTInfo;
+ struct bt_mgnt *pBtMgnt;
+ struct bt_coexist_8723a *pBtCoex;
+ u8 u1tmp, btState;
+
+ pHalData = GET_HAL_DATA(padapter);
+ pBTInfo = GET_BT_INFO(padapter);
+ pBtMgnt = &pBTInfo->BtMgnt;
+ pBtCoex = &pHalData->bt_coexist.halCoex8723;
+
+ u1tmp = pBtCoex->c2hBtInfoOriginal;
+ /* sco BUSY bit is not used on voice over PCM platform */
+ btState = u1tmp & 0xF;
+ pBtCoex->c2hBtProfile = u1tmp & 0xE0;
+
+ /* default set bt to idle state. */
+ pBtMgnt->ExtConfig.bBTBusy = false;
+ pHalData->bt_coexist.CurrentState |= BT_COEX_STATE_BT_IDLE;
+
+ /* check BIT2 first ==> check if bt is under inquiry or page scan */
+ if (btState & BIT(2))
+ pBtCoex->bC2hBtInquiryPage = true;
+ else
+ pBtCoex->bC2hBtInquiryPage = false;
+ btState &= ~BIT(2);
+
+ if (!(btState & BIT(0)))
+ pBtCoex->c2hBtInfo = BT_INFO_STATE_NO_CONNECTION;
+ else {
+ if (btState == 0x1)
+ pBtCoex->c2hBtInfo = BT_INFO_STATE_CONNECT_IDLE;
+ else if (btState == 0x9) {
+ if (pBtCoex->bC2hBtInquiryPage == true)
+ pBtCoex->c2hBtInfo =
+ BT_INFO_STATE_ACL_INQ_OR_PAG;
+ else
+ pBtCoex->c2hBtInfo =
+ BT_INFO_STATE_ACL_ONLY_BUSY;
+ pBtMgnt->ExtConfig.bBTBusy = true;
+ } else if (btState == 0x3) {
+ pBtCoex->c2hBtInfo = BT_INFO_STATE_SCO_ONLY_BUSY;
+ pBtMgnt->ExtConfig.bBTBusy = true;
+ } else if (btState == 0xb) {
+ pBtCoex->c2hBtInfo = BT_INFO_STATE_ACL_SCO_BUSY;
+ pBtMgnt->ExtConfig.bBTBusy = true;
+ } else
+ pBtCoex->c2hBtInfo = BT_INFO_STATE_MAX;
+ if (pBtMgnt->ExtConfig.bBTBusy)
+ pHalData->bt_coexist.CurrentState &=
+ ~BT_COEX_STATE_BT_IDLE;
+ }
+
+ if (BT_INFO_STATE_NO_CONNECTION == pBtCoex->c2hBtInfo ||
+ BT_INFO_STATE_CONNECT_IDLE == pBtCoex->c2hBtInfo) {
+ if (pBtCoex->bC2hBtInquiryPage)
+ pBtCoex->c2hBtInfo = BT_INFO_STATE_INQ_OR_PAG;
+ }
+
+ RTPRINT(FBT, BT_TRACE, ("[BTC2H], %s(%d)\n",
+ BtStateString[pBtCoex->c2hBtInfo], pBtCoex->c2hBtInfo));
+
+ if (pBtCoex->c2hBtProfile != BT_INFO_HID)
+ pBtCoex->c2hBtProfile &= ~BT_INFO_HID;
+}
+
+void BTDM_1AntBtCoexist8723A(struct rtw_adapter *padapter)
+{
+ struct mlme_priv *pmlmepriv;
+ struct hal_data_8723a *pHalData;
+ unsigned long delta_time;
+
+ pmlmepriv = &padapter->mlmepriv;
+ pHalData = GET_HAL_DATA(padapter);
+
+ if (check_fwstate(pmlmepriv, WIFI_SITE_MONITOR)) {
+ /* already done in BTDM_1AntForScan() */
+ RTPRINT(FBT, BT_TRACE,
+ ("[BTCoex], wifi is under scan progress!!\n"));
+ return;
+ }
+
+ if (check_fwstate(pmlmepriv, WIFI_UNDER_LINKING)) {
+ RTPRINT(FBT, BT_TRACE,
+ ("[BTCoex], wifi is under link progress!!\n"));
+ return;
+ }
+
+ /* under DHCP(Special packet) */
+ delta_time = jiffies - padapter->pwrctrlpriv.DelayLPSLastTimeStamp;
+ delta_time = jiffies_to_msecs(delta_time);
+ if (delta_time < 500) {
+ RTPRINT(FBT, BT_TRACE, ("[BTCoex], wifi is under DHCP "
+ "progress(%li ms)!!\n", delta_time));
+ return;
+ }
+
+ BTDM_CheckWiFiState(padapter);
+
+ btdm_1AntBtCoexistHandler(padapter);
+}
+
+/* ===== End of sync from SD7 driver HAL/BTCoexist/HalBtc87231Ant.c ===== */
+
+/* ===== Below this line is sync from SD7 driver HAL/BTCoexist/HalBtc87232Ant.c ===== */
+
+/* local function start with btdm_ */
+static u8 btdm_ActionAlgorithm(struct rtw_adapter *padapter)
+{
+ struct bt_30info *pBTInfo = GET_BT_INFO(padapter);
+ struct bt_mgnt *pBtMgnt = &pBTInfo->BtMgnt;
+ struct hal_data_8723a *pHalData = GET_HAL_DATA(padapter);
+ struct btdm_8723a_2ant *pBtdm8723 = &pHalData->bt_coexist.halCoex8723.btdm2Ant;
+ u8 bScoExist = false, bBtLinkExist = false, bBtHsModeExist = false;
+ u8 algorithm = BT_2ANT_COEX_ALGO_UNDEFINED;
+
+ if (pBtMgnt->ExtConfig.NumberOfHandle)
+ bBtLinkExist = true;
+ if (pBtMgnt->ExtConfig.NumberOfSCO)
+ bScoExist = true;
+ if (BT_HsConnectionEstablished(padapter))
+ bBtHsModeExist = true;
+
+ /* here we get BT status first */
+ /* 1) initialize */
+ pBtdm8723->btStatus = BT_2ANT_BT_STATUS_IDLE;
+
+ if ((bScoExist) || (bBtHsModeExist) ||
+ (BTHCI_CheckProfileExist(padapter, BT_PROFILE_HID))) {
+ RTPRINT(FBT, BT_TRACE, ("[BTCoex], SCO or HID or HS exists, set BT non-idle !!!\n"));
+ pBtdm8723->btStatus = BT_2ANT_BT_STATUS_NON_IDLE;
+ } else {
+ /* A2dp profile */
+ if ((pBtMgnt->ExtConfig.NumberOfHandle == 1) &&
+ (BTHCI_CheckProfileExist(padapter, BT_PROFILE_A2DP))) {
+ if (BTDM_BtTxRxCounterL(padapter) < 100) {
+ RTPRINT(FBT, BT_TRACE, ("[BTCoex], A2DP, low priority tx+rx < 100, set BT connected-idle!!!\n"));
+ pBtdm8723->btStatus = BT_2ANT_BT_STATUS_CONNECTED_IDLE;
+ } else {
+ RTPRINT(FBT, BT_TRACE, ("[BTCoex], A2DP, low priority tx+rx >= 100, set BT non-idle!!!\n"));
+ pBtdm8723->btStatus = BT_2ANT_BT_STATUS_NON_IDLE;
+ }
+ }
+ /* Pan profile */
+ if ((pBtMgnt->ExtConfig.NumberOfHandle == 1) &&
+ (BTHCI_CheckProfileExist(padapter, BT_PROFILE_PAN))) {
+ if (BTDM_BtTxRxCounterL(padapter) < 600) {
+ RTPRINT(FBT, BT_TRACE, ("[BTCoex], PAN, low priority tx+rx < 600, set BT connected-idle!!!\n"));
+ pBtdm8723->btStatus = BT_2ANT_BT_STATUS_CONNECTED_IDLE;
+ } else {
+ if (pHalData->bt_coexist.halCoex8723.lowPriorityTx) {
+ if ((pHalData->bt_coexist.halCoex8723.lowPriorityRx /
+ pHalData->bt_coexist.halCoex8723.lowPriorityTx) > 9) {
+ RTPRINT(FBT, BT_TRACE, ("[BTCoex], PAN, low priority rx/tx > 9, set BT connected-idle!!!\n"));
+ pBtdm8723->btStatus = BT_2ANT_BT_STATUS_CONNECTED_IDLE;
+ }
+ }
+ }
+ if (BT_2ANT_BT_STATUS_CONNECTED_IDLE != pBtdm8723->btStatus) {
+ RTPRINT(FBT, BT_TRACE, ("[BTCoex], PAN, set BT non-idle!!!\n"));
+ pBtdm8723->btStatus = BT_2ANT_BT_STATUS_NON_IDLE;
+ }
+ }
+ /* Pan+A2dp profile */
+ if ((pBtMgnt->ExtConfig.NumberOfHandle == 2) &&
+ (BTHCI_CheckProfileExist(padapter, BT_PROFILE_A2DP)) &&
+ (BTHCI_CheckProfileExist(padapter, BT_PROFILE_PAN))) {
+ if (BTDM_BtTxRxCounterL(padapter) < 600) {
+ RTPRINT(FBT, BT_TRACE, ("[BTCoex], PAN+A2DP, low priority tx+rx < 600, set BT connected-idle!!!\n"));
+ pBtdm8723->btStatus = BT_2ANT_BT_STATUS_CONNECTED_IDLE;
+ } else {
+ if (pHalData->bt_coexist.halCoex8723.lowPriorityTx) {
+ if ((pHalData->bt_coexist.halCoex8723.lowPriorityRx /
+ pHalData->bt_coexist.halCoex8723.lowPriorityTx) > 9) {
+ RTPRINT(FBT, BT_TRACE, ("[BTCoex], PAN+A2DP, low priority rx/tx > 9, set BT connected-idle!!!\n"));
+ pBtdm8723->btStatus = BT_2ANT_BT_STATUS_CONNECTED_IDLE;
+ }
+ }
+ }
+ if (BT_2ANT_BT_STATUS_CONNECTED_IDLE != pBtdm8723->btStatus) {
+ RTPRINT(FBT, BT_TRACE, ("[BTCoex], PAN+A2DP, set BT non-idle!!!\n"));
+ pBtdm8723->btStatus = BT_2ANT_BT_STATUS_NON_IDLE;
+ }
+ }
+ }
+ if (BT_2ANT_BT_STATUS_IDLE != pBtdm8723->btStatus)
+ pBtMgnt->ExtConfig.bBTBusy = true;
+ else
+ pBtMgnt->ExtConfig.bBTBusy = false;
+
+ if (!bBtLinkExist) {
+ RTPRINT(FBT, BT_TRACE, ("[BTCoex], No profile exists!!!\n"));
+ return algorithm;
+ }
+
+ if (pBtMgnt->ExtConfig.NumberOfHandle == 1) {
+ if (bScoExist) {
+ RTPRINT(FBT, BT_TRACE, ("[BTCoex], SCO only\n"));
+ algorithm = BT_2ANT_COEX_ALGO_SCO;
+ } else {
+ if (BTHCI_CheckProfileExist(padapter, BT_PROFILE_HID)) {
+ RTPRINT(FBT, BT_TRACE, ("[BTCoex], HID only\n"));
+ algorithm = BT_2ANT_COEX_ALGO_HID;
+ } else if (BTHCI_CheckProfileExist(padapter, BT_PROFILE_A2DP)) {
+ RTPRINT(FBT, BT_TRACE, ("[BTCoex], A2DP only\n"));
+ algorithm = BT_2ANT_COEX_ALGO_A2DP;
+ } else if (BTHCI_CheckProfileExist(padapter, BT_PROFILE_PAN)) {
+ if (bBtHsModeExist) {
+ RTPRINT(FBT, BT_TRACE, ("[BTCoex], PAN(HS) only\n"));
+ algorithm = BT_2ANT_COEX_ALGO_PANHS;
+ } else {
+ RTPRINT(FBT, BT_TRACE, ("[BTCoex], PAN(EDR) only\n"));
+ algorithm = BT_2ANT_COEX_ALGO_PANEDR;
+ }
+ } else {
+ RTPRINT(FBT, BT_TRACE, ("[BTCoex], Error!!! NO matched profile for NumberOfHandle =%d \n",
+ pBtMgnt->ExtConfig.NumberOfHandle));
+ }
+ }
+ } else if (pBtMgnt->ExtConfig.NumberOfHandle == 2) {
+ if (bScoExist) {
+ if (BTHCI_CheckProfileExist(padapter, BT_PROFILE_HID)) {
+ RTPRINT(FBT, BT_TRACE, ("[BTCoex], SCO + HID\n"));
+ algorithm = BT_2ANT_COEX_ALGO_HID;
+ } else if (BTHCI_CheckProfileExist(padapter, BT_PROFILE_A2DP)) {
+ RTPRINT(FBT, BT_TRACE, ("[BTCoex], Error!!! SCO + A2DP\n"));
+ } else if (BTHCI_CheckProfileExist(padapter, BT_PROFILE_PAN)) {
+ if (bBtHsModeExist) {
+ RTPRINT(FBT, BT_TRACE, ("[BTCoex], SCO + PAN(HS)\n"));
+ algorithm = BT_2ANT_COEX_ALGO_SCO;
+ } else {
+ RTPRINT(FBT, BT_TRACE, ("[BTCoex], SCO + PAN(EDR)\n"));
+ algorithm = BT_2ANT_COEX_ALGO_PANEDR_HID;
+ }
+ } else {
+ RTPRINT(FBT, BT_TRACE, ("[BTCoex], Error!!! SCO exists but why NO matched ACL profile for NumberOfHandle =%d\n",
+ pBtMgnt->ExtConfig.NumberOfHandle));
+ }
+ } else {
+ if (BTHCI_CheckProfileExist(padapter, BT_PROFILE_HID) &&
+ BTHCI_CheckProfileExist(padapter, BT_PROFILE_A2DP)) {
+ RTPRINT(FBT, BT_TRACE, ("[BTCoex], HID + A2DP\n"));
+ algorithm = BT_2ANT_COEX_ALGO_HID_A2DP;
+ } else if (BTHCI_CheckProfileExist(padapter, BT_PROFILE_HID) &&
+ BTHCI_CheckProfileExist(padapter, BT_PROFILE_PAN)) {
+ if (bBtHsModeExist) {
+ RTPRINT(FBT, BT_TRACE, ("[BTCoex], HID + PAN(HS)\n"));
+ algorithm = BT_2ANT_COEX_ALGO_HID_A2DP;
+ } else {
+ RTPRINT(FBT, BT_TRACE, ("[BTCoex], HID + PAN(EDR)\n"));
+ algorithm = BT_2ANT_COEX_ALGO_PANEDR_HID;
+ }
+ } else if (BTHCI_CheckProfileExist(padapter, BT_PROFILE_PAN) &&
+ BTHCI_CheckProfileExist(padapter, BT_PROFILE_A2DP)) {
+ if (bBtHsModeExist) {
+ RTPRINT(FBT, BT_TRACE, ("[BTCoex], A2DP + PAN(HS)\n"));
+ algorithm = BT_2ANT_COEX_ALGO_A2DP;
+ } else {
+ RTPRINT(FBT, BT_TRACE, ("[BTCoex], A2DP + PAN(EDR)\n"));
+ algorithm = BT_2ANT_COEX_ALGO_PANEDR_A2DP;
+ }
+ } else {
+ RTPRINT(FBT, BT_TRACE, ("[BTCoex], Error!!! NO matched profile for NumberOfHandle =%d\n",
+ pBtMgnt->ExtConfig.NumberOfHandle));
+ }
+ }
+ } else if (pBtMgnt->ExtConfig.NumberOfHandle == 3) {
+ if (bScoExist) {
+ if (BTHCI_CheckProfileExist(padapter, BT_PROFILE_HID) &&
+ BTHCI_CheckProfileExist(padapter, BT_PROFILE_A2DP)) {
+ RTPRINT(FBT, BT_TRACE, ("[BTCoex], Error!!! SCO + HID + A2DP\n"));
+ } else if (BTHCI_CheckProfileExist(padapter, BT_PROFILE_HID) &&
+ BTHCI_CheckProfileExist(padapter, BT_PROFILE_PAN)) {
+ if (bBtHsModeExist) {
+ RTPRINT(FBT, BT_TRACE, ("[BTCoex], SCO + HID + PAN(HS)\n"));
+ algorithm = BT_2ANT_COEX_ALGO_HID_A2DP;
+ } else {
+ RTPRINT(FBT, BT_TRACE, ("[BTCoex], SCO + HID + PAN(EDR)\n"));
+ algorithm = BT_2ANT_COEX_ALGO_PANEDR_HID;
+ }
+ } else if (BTHCI_CheckProfileExist(padapter, BT_PROFILE_PAN) &&
+ BTHCI_CheckProfileExist(padapter, BT_PROFILE_A2DP)) {
+ if (bBtHsModeExist) {
+ RTPRINT(FBT, BT_TRACE, ("[BTCoex], Error!!! SCO + A2DP + PAN(HS)\n"));
+ algorithm = BT_2ANT_COEX_ALGO_SCO;
+ } else {
+ RTPRINT(FBT, BT_TRACE, ("[BTCoex], Error!!! SCO + A2DP + PAN(EDR)\n"));
+ }
+ } else {
+ RTPRINT(FBT, BT_TRACE, ("[BTCoex], Error!!! SCO exists but why NO matched profile for NumberOfHandle =%d\n",
+ pBtMgnt->ExtConfig.NumberOfHandle));
+ }
+ } else {
+ if (BTHCI_CheckProfileExist(padapter, BT_PROFILE_HID) &&
+ BTHCI_CheckProfileExist(padapter, BT_PROFILE_PAN) &&
+ BTHCI_CheckProfileExist(padapter, BT_PROFILE_A2DP)) {
+ if (bBtHsModeExist) {
+ RTPRINT(FBT, BT_TRACE, ("[BTCoex], HID + A2DP + PAN(HS)\n"));
+ algorithm = BT_2ANT_COEX_ALGO_HID_A2DP_PANHS;
+ } else {
+ RTPRINT(FBT, BT_TRACE, ("[BTCoex], HID + A2DP + PAN(EDR)\n"));
+ algorithm = BT_2ANT_COEX_ALGO_HID_A2DP_PANEDR;
+ }
+ } else {
+ RTPRINT(FBT, BT_TRACE, ("[BTCoex], Error!!! NO matched profile for NumberOfHandle =%d\n",
+ pBtMgnt->ExtConfig.NumberOfHandle));
+ }
+ }
+ } else if (pBtMgnt->ExtConfig.NumberOfHandle >= 3) {
+ if (bScoExist) {
+ if (BTHCI_CheckProfileExist(padapter, BT_PROFILE_HID) &&
+ BTHCI_CheckProfileExist(padapter, BT_PROFILE_PAN) &&
+ BTHCI_CheckProfileExist(padapter, BT_PROFILE_A2DP)) {
+ if (bBtHsModeExist)
+ RTPRINT(FBT, BT_TRACE, ("[BTCoex], Error!!! SCO + HID + A2DP + PAN(HS)\n"));
+ else
+ RTPRINT(FBT, BT_TRACE, ("[BTCoex], Error!!! SCO + HID + A2DP + PAN(EDR)\n"));
+ } else {
+ RTPRINT(FBT, BT_TRACE, ("[BTCoex], Error!!! SCO exists but why NO matched profile for NumberOfHandle =%d\n",
+ pBtMgnt->ExtConfig.NumberOfHandle));
+ }
+ } else {
+ RTPRINT(FBT, BT_TRACE, ("[BTCoex], Error!!! NO matched profile for NumberOfHandle =%d\n",
+ pBtMgnt->ExtConfig.NumberOfHandle));
+ }
+ }
+ return algorithm;
+}
+
+static u8 btdm_NeedToDecBtPwr(struct rtw_adapter *padapter)
+{
+ struct hal_data_8723a *pHalData = GET_HAL_DATA(padapter);
+ u8 bRet = false;
+
+ if (BT_Operation(padapter)) {
+ if (pHalData->dmpriv.EntryMinUndecoratedSmoothedPWDB > 47) {
+ RTPRINT(FBT, BT_TRACE, ("Need to decrease bt power for HS mode!!\n"));
+ bRet = true;
+ } else {
+ RTPRINT(FBT, BT_TRACE, ("NO Need to decrease bt power for HS mode!!\n"));
+ }
+ } else {
+ if (BTDM_IsWifiConnectionExist(padapter)) {
+ RTPRINT(FBT, BT_TRACE, ("Need to decrease bt power for Wifi is connected!!\n"));
+ bRet = true;
+ }
+ }
+ return bRet;
+}
+
+static void
+btdm_SetCoexTable(struct rtw_adapter *padapter, u32 val0x6c0,
+ u32 val0x6c8, u8 val0x6cc)
+{
+ RTPRINT(FBT, BT_TRACE, ("set coex table, set 0x6c0 = 0x%x\n", val0x6c0));
+ rtl8723au_write32(padapter, 0x6c0, val0x6c0);
+
+ RTPRINT(FBT, BT_TRACE, ("set coex table, set 0x6c8 = 0x%x\n", val0x6c8));
+ rtl8723au_write32(padapter, 0x6c8, val0x6c8);
+
+ RTPRINT(FBT, BT_TRACE, ("set coex table, set 0x6cc = 0x%x\n", val0x6cc));
+ rtl8723au_write8(padapter, 0x6cc, val0x6cc);
+}
+
+static void
+btdm_SetSwFullTimeDacSwing(struct rtw_adapter *padapter, u8 bSwDacSwingOn,
+ u32 swDacSwingLvl)
+{
+ struct hal_data_8723a *pHalData = GET_HAL_DATA(padapter);
+
+ if (bSwDacSwingOn) {
+ RTPRINT(FBT, BT_TRACE, ("[BTCoex], SwDacSwing = 0x%x\n", swDacSwingLvl));
+ PHY_SetBBReg(padapter, 0x880, 0xff000000, swDacSwingLvl);
+ pHalData->bt_coexist.bSWCoexistAllOff = false;
+ } else {
+ RTPRINT(FBT, BT_TRACE, ("[BTCoex], SwDacSwing Off!\n"));
+ PHY_SetBBReg(padapter, 0x880, 0xff000000, 0xc0);
+ }
+}
+
+static void
+btdm_SetFwDacSwingLevel(struct rtw_adapter *padapter, u8 dacSwingLvl)
+{
+ u8 H2C_Parameter[1] = {0};
+
+ H2C_Parameter[0] = dacSwingLvl;
+
+ RTPRINT(FBT, BT_TRACE, ("[BTCoex], Set Dac Swing Level = 0x%x\n", dacSwingLvl));
+ RTPRINT(FBT, BT_TRACE, ("[BTCoex], write 0x29 = 0x%x\n", H2C_Parameter[0]));
+
+ FillH2CCmd(padapter, 0x29, 1, H2C_Parameter);
+}
+
+static void btdm_2AntDecBtPwr(struct rtw_adapter *padapter, u8 bDecBtPwr)
+{
+ struct hal_data_8723a *pHalData = GET_HAL_DATA(padapter);
+ struct btdm_8723a_2ant *pBtdm8723 = &pHalData->bt_coexist.halCoex8723.btdm2Ant;
+
+ RTPRINT(FBT, BT_TRACE,
+ ("[BTCoex], Dec BT power = %s\n",
+ ((bDecBtPwr) ? "ON" : "OFF")));
+ pBtdm8723->bCurDecBtPwr = bDecBtPwr;
+
+ if (pBtdm8723->bPreDecBtPwr == pBtdm8723->bCurDecBtPwr)
+ return;
+
+ BTDM_SetFwDecBtPwr(padapter, pBtdm8723->bCurDecBtPwr);
+
+ pBtdm8723->bPreDecBtPwr = pBtdm8723->bCurDecBtPwr;
+}
+
+static void
+btdm_2AntFwDacSwingLvl(struct rtw_adapter *padapter, u8 fwDacSwingLvl)
+{
+ struct hal_data_8723a *pHalData = GET_HAL_DATA(padapter);
+ struct btdm_8723a_2ant *pBtdm8723 = &pHalData->bt_coexist.halCoex8723.btdm2Ant;
+
+ RTPRINT(FBT, BT_TRACE, ("[BTCoex], set FW Dac Swing level = %d\n", fwDacSwingLvl));
+ pBtdm8723->curFwDacSwingLvl = fwDacSwingLvl;
+
+ /* RTPRINT(FBT, BT_TRACE, ("[BTCoex], preFwDacSwingLvl =%d, curFwDacSwingLvl =%d\n", */
+ /*pBtdm8723->preFwDacSwingLvl, pBtdm8723->curFwDacSwingLvl)); */
+
+ if (pBtdm8723->preFwDacSwingLvl == pBtdm8723->curFwDacSwingLvl)
+ return;
+
+ btdm_SetFwDacSwingLevel(padapter, pBtdm8723->curFwDacSwingLvl);
+
+ pBtdm8723->preFwDacSwingLvl = pBtdm8723->curFwDacSwingLvl;
+}
+
+static void
+btdm_2AntRfShrink(struct rtw_adapter *padapter, u8 bRxRfShrinkOn)
+{
+ struct hal_data_8723a *pHalData = GET_HAL_DATA(padapter);
+ struct btdm_8723a_2ant *pBtdm8723 = &pHalData->bt_coexist.halCoex8723.btdm2Ant;
+
+ RTPRINT(FBT, BT_TRACE,
+ ("[BTCoex], turn Rx RF Shrink = %s\n",
+ ((bRxRfShrinkOn) ? "ON" : "OFF")));
+ pBtdm8723->bCurRfRxLpfShrink = bRxRfShrinkOn;
+
+ /* RTPRINT(FBT, BT_TRACE, ("[BTCoex], bPreRfRxLpfShrink =%d, bCurRfRxLpfShrink =%d\n", */
+ /*pBtdm8723->bPreRfRxLpfShrink, pBtdm8723->bCurRfRxLpfShrink)); */
+
+ if (pBtdm8723->bPreRfRxLpfShrink == pBtdm8723->bCurRfRxLpfShrink)
+ return;
+
+ BTDM_SetSwRfRxLpfCorner(padapter, (u8)pBtdm8723->bCurRfRxLpfShrink);
+
+ pBtdm8723->bPreRfRxLpfShrink = pBtdm8723->bCurRfRxLpfShrink;
+}
+
+static void
+btdm_2AntLowPenaltyRa(struct rtw_adapter *padapter, u8 bLowPenaltyRa)
+{
+ struct hal_data_8723a *pHalData = GET_HAL_DATA(padapter);
+ struct btdm_8723a_2ant *pBtdm8723 = &pHalData->bt_coexist.halCoex8723.btdm2Ant;
+
+ RTPRINT(FBT, BT_TRACE,
+ ("[BTCoex], turn LowPenaltyRA = %s\n",
+ ((bLowPenaltyRa) ? "ON" : "OFF")));
+ pBtdm8723->bCurLowPenaltyRa = bLowPenaltyRa;
+
+ /* RTPRINT(FBT, BT_TRACE, ("[BTCoex], bPreLowPenaltyRa =%d, bCurLowPenaltyRa =%d\n", */
+ /*pBtdm8723->bPreLowPenaltyRa, pBtdm8723->bCurLowPenaltyRa)); */
+
+ if (pBtdm8723->bPreLowPenaltyRa == pBtdm8723->bCurLowPenaltyRa)
+ return;
+
+ BTDM_SetSwPenaltyTxRateAdaptive(padapter, (u8)pBtdm8723->bCurLowPenaltyRa);
+
+ pBtdm8723->bPreLowPenaltyRa = pBtdm8723->bCurLowPenaltyRa;
+}
+
+static void
+btdm_2AntDacSwing(struct rtw_adapter *padapter,
+ u8 bDacSwingOn, u32 dacSwingLvl)
+{
+ struct hal_data_8723a *pHalData = GET_HAL_DATA(padapter);
+ struct btdm_8723a_2ant *pBtdm8723 = &pHalData->bt_coexist.halCoex8723.btdm2Ant;
+
+ RTPRINT(FBT, BT_TRACE,
+ ("[BTCoex], turn DacSwing =%s, dacSwingLvl = 0x%x\n",
+ (bDacSwingOn ? "ON" : "OFF"), dacSwingLvl));
+ pBtdm8723->bCurDacSwingOn = bDacSwingOn;
+ pBtdm8723->curDacSwingLvl = dacSwingLvl;
+
+ if ((pBtdm8723->bPreDacSwingOn == pBtdm8723->bCurDacSwingOn) &&
+ (pBtdm8723->preDacSwingLvl == pBtdm8723->curDacSwingLvl))
+ return;
+
+ mdelay(30);
+ btdm_SetSwFullTimeDacSwing(padapter, bDacSwingOn, dacSwingLvl);
+
+ pBtdm8723->bPreDacSwingOn = pBtdm8723->bCurDacSwingOn;
+ pBtdm8723->preDacSwingLvl = pBtdm8723->curDacSwingLvl;
+}
+
+static void btdm_2AntAdcBackOff(struct rtw_adapter *padapter, u8 bAdcBackOff)
+{
+ struct hal_data_8723a *pHalData = GET_HAL_DATA(padapter);
+ struct btdm_8723a_2ant *pBtdm8723 = &pHalData->bt_coexist.halCoex8723.btdm2Ant;
+
+ RTPRINT(FBT, BT_TRACE,
+ ("[BTCoex], turn AdcBackOff = %s\n",
+ ((bAdcBackOff) ? "ON" : "OFF")));
+ pBtdm8723->bCurAdcBackOff = bAdcBackOff;
+
+ if (pBtdm8723->bPreAdcBackOff == pBtdm8723->bCurAdcBackOff)
+ return;
+
+ BTDM_BBBackOffLevel(padapter, (u8)pBtdm8723->bCurAdcBackOff);
+
+ pBtdm8723->bPreAdcBackOff = pBtdm8723->bCurAdcBackOff;
+}
+
+static void btdm_2AntAgcTable(struct rtw_adapter *padapter, u8 bAgcTableEn)
+{
+ struct hal_data_8723a *pHalData = GET_HAL_DATA(padapter);
+ struct btdm_8723a_2ant *pBtdm8723 = &pHalData->bt_coexist.halCoex8723.btdm2Ant;
+
+ RTPRINT(FBT, BT_TRACE,
+ ("[BTCoex], %s Agc Table\n", ((bAgcTableEn) ? "Enable" : "Disable")));
+ pBtdm8723->bCurAgcTableEn = bAgcTableEn;
+
+ /* RTPRINT(FBT, BT_TRACE, ("[BTCoex], bPreAgcTableEn =%d, bCurAgcTableEn =%d\n", */
+ /*pBtdm8723->bPreAgcTableEn, pBtdm8723->bCurAgcTableEn)); */
+
+ if (pBtdm8723->bPreAgcTableEn == pBtdm8723->bCurAgcTableEn)
+ return;
+
+ BTDM_AGCTable(padapter, (u8)bAgcTableEn);
+
+ pBtdm8723->bPreAgcTableEn = pBtdm8723->bCurAgcTableEn;
+}
+
+static void
+btdm_2AntCoexTable(struct rtw_adapter *padapter,
+ u32 val0x6c0, u32 val0x6c8, u8 val0x6cc)
+{
+ struct hal_data_8723a *pHalData = GET_HAL_DATA(padapter);
+ struct btdm_8723a_2ant *pBtdm8723 = &pHalData->bt_coexist.halCoex8723.btdm2Ant;
+
+ RTPRINT(FBT, BT_TRACE, ("[BTCoex], write Coex Table 0x6c0 = 0x%x, 0x6c8 = 0x%x, 0x6cc = 0x%x\n",
+ val0x6c0, val0x6c8, val0x6cc));
+ pBtdm8723->curVal0x6c0 = val0x6c0;
+ pBtdm8723->curVal0x6c8 = val0x6c8;
+ pBtdm8723->curVal0x6cc = val0x6cc;
+
+ /* RTPRINT(FBT, BT_TRACE, ("[BTCoex], preVal0x6c0 = 0x%x, preVal0x6c8 = 0x%x, preVal0x6cc = 0x%x !!\n", */
+ /*pBtdm8723->preVal0x6c0, pBtdm8723->preVal0x6c8, pBtdm8723->preVal0x6cc)); */
+ /* RTPRINT(FBT, BT_TRACE, ("[BTCoex], curVal0x6c0 = 0x%x, curVal0x6c8 = 0x%x, curVal0x6cc = 0x%x !!\n", */
+ /*pBtdm8723->curVal0x6c0, pBtdm8723->curVal0x6c8, pBtdm8723->curVal0x6cc)); */
+
+ if ((pBtdm8723->preVal0x6c0 == pBtdm8723->curVal0x6c0) &&
+ (pBtdm8723->preVal0x6c8 == pBtdm8723->curVal0x6c8) &&
+ (pBtdm8723->preVal0x6cc == pBtdm8723->curVal0x6cc))
+ return;
+
+ btdm_SetCoexTable(padapter, val0x6c0, val0x6c8, val0x6cc);
+
+ pBtdm8723->preVal0x6c0 = pBtdm8723->curVal0x6c0;
+ pBtdm8723->preVal0x6c8 = pBtdm8723->curVal0x6c8;
+ pBtdm8723->preVal0x6cc = pBtdm8723->curVal0x6cc;
+}
+
+static void btdm_2AntIgnoreWlanAct(struct rtw_adapter *padapter, u8 bEnable)
+{
+ struct hal_data_8723a *pHalData = GET_HAL_DATA(padapter);
+ struct btdm_8723a_2ant *pBtdm8723 = &pHalData->bt_coexist.halCoex8723.btdm2Ant;
+
+ RTPRINT(FBT, BT_TRACE,
+ ("[BTCoex], turn Ignore WlanAct %s\n", (bEnable ? "ON" : "OFF")));
+ pBtdm8723->bCurIgnoreWlanAct = bEnable;
+
+
+ if (pBtdm8723->bPreIgnoreWlanAct == pBtdm8723->bCurIgnoreWlanAct)
+ return;
+
+ btdm_SetFwIgnoreWlanAct(padapter, bEnable);
+ pBtdm8723->bPreIgnoreWlanAct = pBtdm8723->bCurIgnoreWlanAct;
+}
+
+static void
+btdm_2AntSetFw3a(struct rtw_adapter *padapter, u8 byte1, u8 byte2,
+ u8 byte3, u8 byte4, u8 byte5)
+{
+ u8 H2C_Parameter[5] = {0};
+
+ struct hal_data_8723a *pHalData = GET_HAL_DATA(padapter);
+
+ /* byte1[1:0] != 0 means enable pstdma */
+ /* for 2Ant bt coexist, if byte1 != 0 means enable pstdma */
+ if (byte1)
+ pHalData->bt_coexist.bFWCoexistAllOff = false;
+ H2C_Parameter[0] = byte1;
+ H2C_Parameter[1] = byte2;
+ H2C_Parameter[2] = byte3;
+ H2C_Parameter[3] = byte4;
+ H2C_Parameter[4] = byte5;
+
+ pHalData->bt_coexist.fw3aVal[0] = byte1;
+ pHalData->bt_coexist.fw3aVal[1] = byte2;
+ pHalData->bt_coexist.fw3aVal[2] = byte3;
+ pHalData->bt_coexist.fw3aVal[3] = byte4;
+ pHalData->bt_coexist.fw3aVal[4] = byte5;
+
+ RTPRINT(FBT, BT_TRACE, ("[BTCoex], FW write 0x3a(5bytes) = 0x%x%08x\n",
+ H2C_Parameter[0],
+ H2C_Parameter[1]<<24|H2C_Parameter[2]<<16|H2C_Parameter[3]<<8|H2C_Parameter[4]));
+
+ FillH2CCmd(padapter, 0x3a, 5, H2C_Parameter);
+ }
+
+static void btdm_2AntPsTdma(struct rtw_adapter *padapter, u8 bTurnOn, u8 type)
+{
+ struct hal_data_8723a *pHalData = GET_HAL_DATA(padapter);
+ struct btdm_8723a_2ant *pBtdm8723 = &pHalData->bt_coexist.halCoex8723.btdm2Ant;
+ u32 btTxRxCnt = 0;
+ u8 bTurnOnByCnt = false;
+ u8 psTdmaTypeByCnt = 0;
+
+ btTxRxCnt = BTDM_BtTxRxCounterH(padapter)+BTDM_BtTxRxCounterL(padapter);
+ RTPRINT(FBT, BT_TRACE, ("[BTCoex], BT TxRx Counters = %d\n", btTxRxCnt));
+ if (btTxRxCnt > 3000) {
+ bTurnOnByCnt = true;
+ psTdmaTypeByCnt = 8;
+
+ RTPRINT(FBT, BT_TRACE,
+ ("[BTCoex], For BTTxRxCounters, turn %s PS TDMA, type =%d\n",
+ (bTurnOnByCnt ? "ON" : "OFF"), psTdmaTypeByCnt));
+ pBtdm8723->bCurPsTdmaOn = bTurnOnByCnt;
+ pBtdm8723->curPsTdma = psTdmaTypeByCnt;
+ } else {
+ RTPRINT(FBT, BT_TRACE,
+ ("[BTCoex], turn %s PS TDMA, type =%d\n",
+ (bTurnOn ? "ON" : "OFF"), type));
+ pBtdm8723->bCurPsTdmaOn = bTurnOn;
+ pBtdm8723->curPsTdma = type;
+ }
+
+ if ((pBtdm8723->bPrePsTdmaOn == pBtdm8723->bCurPsTdmaOn) &&
+ (pBtdm8723->prePsTdma == pBtdm8723->curPsTdma))
+ return;
+
+ if (bTurnOn) {
+ switch (type) {
+ case 1:
+ default:
+ btdm_2AntSetFw3a(padapter, 0xe3, 0x1a, 0x1a, 0xa1, 0x98);
+ break;
+ case 2:
+ btdm_2AntSetFw3a(padapter, 0xe3, 0x12, 0x12, 0xa1, 0x98);
+ break;
+ case 3:
+ btdm_2AntSetFw3a(padapter, 0xe3, 0xa, 0xa, 0xa1, 0x98);
+ break;
+ case 4:
+ btdm_2AntSetFw3a(padapter, 0xa3, 0x5, 0x5, 0xa1, 0x80);
+ break;
+ case 5:
+ btdm_2AntSetFw3a(padapter, 0xe3, 0x1a, 0x1a, 0x20, 0x98);
+ break;
+ case 6:
+ btdm_2AntSetFw3a(padapter, 0xe3, 0x12, 0x12, 0x20, 0x98);
+ break;
+ case 7:
+ btdm_2AntSetFw3a(padapter, 0xe3, 0xa, 0xa, 0x20, 0x98);
+ break;
+ case 8:
+ btdm_2AntSetFw3a(padapter, 0xa3, 0x5, 0x5, 0x20, 0x80);
+ break;
+ case 9:
+ btdm_2AntSetFw3a(padapter, 0xe3, 0x1a, 0x1a, 0xa1, 0x98);
+ break;
+ case 10:
+ btdm_2AntSetFw3a(padapter, 0xe3, 0x12, 0x12, 0xa1, 0x98);
+ break;
+ case 11:
+ btdm_2AntSetFw3a(padapter, 0xe3, 0xa, 0xa, 0xa1, 0x98);
+ break;
+ case 12:
+ btdm_2AntSetFw3a(padapter, 0xe3, 0x5, 0x5, 0xa1, 0x98);
+ break;
+ case 13:
+ btdm_2AntSetFw3a(padapter, 0xe3, 0x1a, 0x1a, 0x20, 0x98);
+ break;
+ case 14:
+ btdm_2AntSetFw3a(padapter, 0xe3, 0x12, 0x12, 0x20, 0x98);
+ break;
+ case 15:
+ btdm_2AntSetFw3a(padapter, 0xe3, 0xa, 0xa, 0x20, 0x98);
+ break;
+ case 16:
+ btdm_2AntSetFw3a(padapter, 0xe3, 0x5, 0x5, 0x20, 0x98);
+ break;
+ case 17:
+ btdm_2AntSetFw3a(padapter, 0xa3, 0x2f, 0x2f, 0x20, 0x80);
+ break;
+ case 18:
+ btdm_2AntSetFw3a(padapter, 0xe3, 0x5, 0x5, 0xa1, 0x98);
+ break;
+ case 19:
+ btdm_2AntSetFw3a(padapter, 0xe3, 0x25, 0x25, 0xa1, 0x98);
+ break;
+ case 20:
+ btdm_2AntSetFw3a(padapter, 0xe3, 0x25, 0x25, 0x20, 0x98);
+ break;
+ }
+ } else {
+ /* disable PS tdma */
+ switch (type) {
+ case 0:
+ btdm_2AntSetFw3a(padapter, 0x0, 0x0, 0x0, 0x8, 0x0);
+ break;
+ case 1:
+ btdm_2AntSetFw3a(padapter, 0x0, 0x0, 0x0, 0x0, 0x0);
+ break;
+ default:
+ btdm_2AntSetFw3a(padapter, 0x0, 0x0, 0x0, 0x8, 0x0);
+ break;
+ }
+ }
+
+ /* update pre state */
+ pBtdm8723->bPrePsTdmaOn = pBtdm8723->bCurPsTdmaOn;
+ pBtdm8723->prePsTdma = pBtdm8723->curPsTdma;
+}
+
+static void btdm_2AntBtInquiryPage(struct rtw_adapter *padapter)
+{
+ btdm_2AntCoexTable(padapter, 0x55555555, 0xffff, 0x3);
+ btdm_2AntIgnoreWlanAct(padapter, false);
+ btdm_2AntPsTdma(padapter, true, 8);
+}
+
+static u8 btdm_HoldForBtInqPage(struct rtw_adapter *padapter)
+{
+ struct hal_data_8723a *pHalData = GET_HAL_DATA(padapter);
+ u32 curTime = jiffies;
+
+ if (pHalData->bt_coexist.halCoex8723.bC2hBtInquiryPage) {
+ /* bt inquiry or page is started. */
+ if (pHalData->bt_coexist.halCoex8723.btInqPageStartTime == 0) {
+ pHalData->bt_coexist.halCoex8723.btInqPageStartTime = curTime;
+ RTPRINT(FBT, BT_TRACE, ("[BTCoex], BT Inquiry/page is started at time : 0x%lx \n",
+ pHalData->bt_coexist.halCoex8723.btInqPageStartTime));
+ }
+ }
+ RTPRINT(FBT, BT_TRACE, ("[BTCoex], BT Inquiry/page started time : 0x%lx, curTime : 0x%x \n",
+ pHalData->bt_coexist.halCoex8723.btInqPageStartTime, curTime));
+
+ if (pHalData->bt_coexist.halCoex8723.btInqPageStartTime) {
+ if (((curTime - pHalData->bt_coexist.halCoex8723.btInqPageStartTime)/1000000) >= 10) {
+ RTPRINT(FBT, BT_TRACE, ("[BTCoex], BT Inquiry/page >= 10sec!!!"));
+ pHalData->bt_coexist.halCoex8723.btInqPageStartTime = 0;
+ }
+ }
+
+ if (pHalData->bt_coexist.halCoex8723.btInqPageStartTime) {
+ btdm_2AntCoexTable(padapter, 0x55555555, 0xffff, 0x3);
+ btdm_2AntIgnoreWlanAct(padapter, false);
+ btdm_2AntPsTdma(padapter, true, 8);
+ return true;
+ } else {
+ return false;
+ }
+}
+
+static u8 btdm_Is2Ant8723ACommonAction(struct rtw_adapter *padapter)
+{
+ struct hal_data_8723a *pHalData = GET_HAL_DATA(padapter);
+ struct btdm_8723a_2ant *pBtdm8723 = &pHalData->bt_coexist.halCoex8723.btdm2Ant;
+ u8 bCommon = false;
+
+ RTPRINT(FBT, BT_TRACE, ("%s :BTDM_IsWifiConnectionExist =%x check_fwstate =%x pmlmepriv->fw_state = 0x%x\n", __func__, BTDM_IsWifiConnectionExist(padapter), check_fwstate(&padapter->mlmepriv, (_FW_UNDER_SURVEY|_FW_UNDER_LINKING)), padapter->mlmepriv.fw_state));
+
+ if ((!BTDM_IsWifiConnectionExist(padapter)) &&
+ (!check_fwstate(&padapter->mlmepriv, (_FW_UNDER_SURVEY|_FW_UNDER_LINKING))) &&
+ (BT_2ANT_BT_STATUS_IDLE == pBtdm8723->btStatus)) {
+ RTPRINT(FBT, BT_TRACE, ("Wifi idle + Bt idle!!\n"));
+
+ btdm_2AntLowPenaltyRa(padapter, false);
+ btdm_2AntRfShrink(padapter, false);
+ btdm_2AntCoexTable(padapter, 0x55555555, 0xffff, 0x3);
+
+ btdm_2AntIgnoreWlanAct(padapter, false);
+ btdm_2AntPsTdma(padapter, false, 0);
+ btdm_2AntFwDacSwingLvl(padapter, 0x20);
+ btdm_2AntDecBtPwr(padapter, false);
+
+ btdm_2AntAgcTable(padapter, false);
+ btdm_2AntAdcBackOff(padapter, false);
+ btdm_2AntDacSwing(padapter, false, 0xc0);
+
+ bCommon = true;
+ } else if (((BTDM_IsWifiConnectionExist(padapter)) ||
+ (check_fwstate(&padapter->mlmepriv, (_FW_UNDER_SURVEY|_FW_UNDER_LINKING)))) &&
+ (BT_2ANT_BT_STATUS_IDLE == pBtdm8723->btStatus)) {
+ RTPRINT(FBT, BT_TRACE, ("Wifi non-idle + BT idle!!\n"));
+
+ btdm_2AntLowPenaltyRa(padapter, true);
+ btdm_2AntRfShrink(padapter, false);
+ btdm_2AntCoexTable(padapter, 0x55555555, 0xffff, 0x3);
+
+ btdm_2AntIgnoreWlanAct(padapter, false);
+ btdm_2AntPsTdma(padapter, false, 0);
+ btdm_2AntFwDacSwingLvl(padapter, 0x20);
+ btdm_2AntDecBtPwr(padapter, true);
+
+ btdm_2AntAgcTable(padapter, false);
+ btdm_2AntAdcBackOff(padapter, false);
+ btdm_2AntDacSwing(padapter, false, 0xc0);
+
+ bCommon = true;
+ } else if ((!BTDM_IsWifiConnectionExist(padapter)) &&
+ (!check_fwstate(&padapter->mlmepriv, (_FW_UNDER_SURVEY|_FW_UNDER_LINKING))) &&
+ (BT_2ANT_BT_STATUS_CONNECTED_IDLE == pBtdm8723->btStatus)) {
+ RTPRINT(FBT, BT_TRACE, ("Wifi idle + Bt connected idle!!\n"));
+
+ btdm_2AntLowPenaltyRa(padapter, true);
+ btdm_2AntRfShrink(padapter, true);
+ btdm_2AntCoexTable(padapter, 0x55555555, 0xffff, 0x3);
+
+ btdm_2AntIgnoreWlanAct(padapter, false);
+ btdm_2AntPsTdma(padapter, false, 0);
+ btdm_2AntFwDacSwingLvl(padapter, 0x20);
+ btdm_2AntDecBtPwr(padapter, false);
+
+ btdm_2AntAgcTable(padapter, false);
+ btdm_2AntAdcBackOff(padapter, false);
+ btdm_2AntDacSwing(padapter, false, 0xc0);
+
+ bCommon = true;
+ } else if (((BTDM_IsWifiConnectionExist(padapter)) ||
+ (check_fwstate(&padapter->mlmepriv, (_FW_UNDER_SURVEY|_FW_UNDER_LINKING)))) &&
+ (BT_2ANT_BT_STATUS_CONNECTED_IDLE == pBtdm8723->btStatus)) {
+ RTPRINT(FBT, BT_TRACE, ("Wifi non-idle + Bt connected idle!!\n"));
+
+ btdm_2AntLowPenaltyRa(padapter, true);
+ btdm_2AntRfShrink(padapter, true);
+ btdm_2AntCoexTable(padapter, 0x55555555, 0xffff, 0x3);
+
+ btdm_2AntIgnoreWlanAct(padapter, false);
+ btdm_2AntPsTdma(padapter, false, 0);
+ btdm_2AntFwDacSwingLvl(padapter, 0x20);
+ btdm_2AntDecBtPwr(padapter, true);
+
+ btdm_2AntAgcTable(padapter, false);
+ btdm_2AntAdcBackOff(padapter, false);
+ btdm_2AntDacSwing(padapter, false, 0xc0);
+
+ bCommon = true;
+ } else if ((!BTDM_IsWifiConnectionExist(padapter)) &&
+ (!check_fwstate(&padapter->mlmepriv, (_FW_UNDER_SURVEY|_FW_UNDER_LINKING))) &&
+ (BT_2ANT_BT_STATUS_NON_IDLE == pBtdm8723->btStatus)) {
+ RTPRINT(FBT, BT_TRACE, ("Wifi idle + BT non-idle!!\n"));
+
+ btdm_2AntLowPenaltyRa(padapter, true);
+ btdm_2AntRfShrink(padapter, true);
+ btdm_2AntCoexTable(padapter, 0x55555555, 0xffff, 0x3);
+
+ btdm_2AntIgnoreWlanAct(padapter, false);
+ btdm_2AntPsTdma(padapter, false, 0);
+ btdm_2AntFwDacSwingLvl(padapter, 0x20);
+ btdm_2AntDecBtPwr(padapter, false);
+
+ btdm_2AntAgcTable(padapter, false);
+ btdm_2AntAdcBackOff(padapter, false);
+ btdm_2AntDacSwing(padapter, false, 0xc0);
+
+ bCommon = true;
+ } else {
+ RTPRINT(FBT, BT_TRACE, ("Wifi non-idle + BT non-idle!!\n"));
+ btdm_2AntLowPenaltyRa(padapter, true);
+ btdm_2AntRfShrink(padapter, true);
+ btdm_2AntCoexTable(padapter, 0x55555555, 0xffff, 0x3);
+ btdm_2AntIgnoreWlanAct(padapter, false);
+ btdm_2AntFwDacSwingLvl(padapter, 0x20);
+
+ bCommon = false;
+ }
+ return bCommon;
+}
+
+static void
+btdm_2AntTdmaDurationAdjust(struct rtw_adapter *padapter, u8 bScoHid,
+ u8 bTxPause, u8 maxInterval)
+{
+ struct hal_data_8723a *pHalData = GET_HAL_DATA(padapter);
+ struct btdm_8723a_2ant *pBtdm8723 = &pHalData->bt_coexist.halCoex8723.btdm2Ant;
+ static s32 up, dn, m, n, WaitCount;
+ s32 result; /* 0: no change, +1: increase WiFi duration, -1: decrease WiFi duration */
+ u8 retryCount = 0;
+
+ RTPRINT(FBT, BT_TRACE, ("[BTCoex], TdmaDurationAdjust()\n"));
+
+ if (pBtdm8723->bResetTdmaAdjust) {
+ pBtdm8723->bResetTdmaAdjust = false;
+ RTPRINT(FBT, BT_TRACE, ("[BTCoex], first run TdmaDurationAdjust()!!\n"));
+ if (bScoHid) {
+ if (bTxPause) {
+ btdm_2AntPsTdma(padapter, true, 15);
+ pBtdm8723->psTdmaDuAdjType = 15;
+ } else {
+ btdm_2AntPsTdma(padapter, true, 11);
+ pBtdm8723->psTdmaDuAdjType = 11;
+ }
+ } else {
+ if (bTxPause) {
+ btdm_2AntPsTdma(padapter, true, 7);
+ pBtdm8723->psTdmaDuAdjType = 7;
+ } else {
+ btdm_2AntPsTdma(padapter, true, 3);
+ pBtdm8723->psTdmaDuAdjType = 3;
+ }
+ }
+ up = 0;
+ dn = 0;
+ m = 1;
+ n = 3;
+ result = 0;
+ WaitCount = 0;
+ } else {
+ /* accquire the BT TRx retry count from BT_Info byte2 */
+ retryCount = pHalData->bt_coexist.halCoex8723.btRetryCnt;
+ RTPRINT(FBT, BT_TRACE, ("[BTCoex], retryCount = %d\n", retryCount));
+ result = 0;
+ WaitCount++;
+
+ if (retryCount == 0) { /* no retry in the last 2-second duration */
+ up++;
+ dn--;
+
+ if (dn <= 0)
+ dn = 0;
+
+ if (up >= n) { /* if ³sẠ̈ n ­Ó2¬í retry count¬°0, «h½Ơ¼eWiFi duration */
+ WaitCount = 0;
+ n = 3;
+ up = 0;
+ dn = 0;
+ result = 1;
+ RTPRINT(FBT, BT_TRACE, ("[BTCoex], Increase wifi duration!!\n"));
+ }
+ } else if (retryCount <= 3) { /* <= 3 retry in the last 2-second duration */
+ up--;
+ dn++;
+
+ if (up <= 0)
+ up = 0;
+
+ if (dn == 2) { /* if ³sẠ̈ 2 ­Ó2¬í retry count< 3, «h½Ơ¯¶WiFi duration */
+ if (WaitCount <= 2)
+ m++; /* ÁקK¤@ª½¦b¨â­Ólevel¤¤¨Ó¦^ */
+ else
+ m = 1;
+
+ if (m >= 20) /* m ³̀¤j­È = 20 ' ³̀¤j120¬í recheck¬O§_½Ơ¾ă WiFi duration. */
+ m = 20;
+
+ n = 3*m;
+ up = 0;
+ dn = 0;
+ WaitCount = 0;
+ result = -1;
+ RTPRINT(FBT, BT_TRACE, ("[BTCoex], Decrease wifi duration for retryCounter<3!!\n"));
+ }
+ } else { /* retry count > 3, ¥u­n1¦¸ retry count > 3, «h½Ơ¯¶WiFi duration */
+ if (WaitCount == 1)
+ m++; /* ÁקK¤@ª½¦b¨â­Ólevel¤¤¨Ó¦^ */
+ else
+ m = 1;
+
+ if (m >= 20) /* m ³̀¤j­È = 20 ' ³̀¤j120¬í recheck¬O§_½Ơ¾ă WiFi duration. */
+ m = 20;
+ n = 3*m;
+ up = 0;
+ dn = 0;
+ WaitCount = 0;
+ result = -1;
+ RTPRINT(FBT, BT_TRACE, ("[BTCoex], Decrease wifi duration for retryCounter>3!!\n"));
+ }
+
+ RTPRINT(FBT, BT_TRACE, ("[BTCoex], max Interval = %d\n", maxInterval));
+ if (maxInterval == 1) {
+ if (bTxPause) {
+ RTPRINT(FBT, BT_TRACE, ("[BTCoex], TxPause = 1\n"));
+ if (pBtdm8723->curPsTdma == 1) {
+ btdm_2AntPsTdma(padapter, true, 5);
+ pBtdm8723->psTdmaDuAdjType = 5;
+ } else if (pBtdm8723->curPsTdma == 2) {
+ btdm_2AntPsTdma(padapter, true, 6);
+ pBtdm8723->psTdmaDuAdjType = 6;
+ } else if (pBtdm8723->curPsTdma == 3) {
+ btdm_2AntPsTdma(padapter, true, 7);
+ pBtdm8723->psTdmaDuAdjType = 7;
+ } else if (pBtdm8723->curPsTdma == 4) {
+ btdm_2AntPsTdma(padapter, true, 8);
+ pBtdm8723->psTdmaDuAdjType = 8;
+ }
+ if (pBtdm8723->curPsTdma == 9) {
+ btdm_2AntPsTdma(padapter, true, 13);
+ pBtdm8723->psTdmaDuAdjType = 13;
+ } else if (pBtdm8723->curPsTdma == 10) {
+ btdm_2AntPsTdma(padapter, true, 14);
+ pBtdm8723->psTdmaDuAdjType = 14;
+ } else if (pBtdm8723->curPsTdma == 11) {
+ btdm_2AntPsTdma(padapter, true, 15);
+ pBtdm8723->psTdmaDuAdjType = 15;
+ } else if (pBtdm8723->curPsTdma == 12) {
+ btdm_2AntPsTdma(padapter, true, 16);
+ pBtdm8723->psTdmaDuAdjType = 16;
+ }
+
+ if (result == -1) {
+ if (pBtdm8723->curPsTdma == 5) {
+ btdm_2AntPsTdma(padapter, true, 6);
+ pBtdm8723->psTdmaDuAdjType = 6;
+ } else if (pBtdm8723->curPsTdma == 6) {
+ btdm_2AntPsTdma(padapter, true, 7);
+ pBtdm8723->psTdmaDuAdjType = 7;
+ } else if (pBtdm8723->curPsTdma == 7) {
+ btdm_2AntPsTdma(padapter, true, 8);
+ pBtdm8723->psTdmaDuAdjType = 8;
+ } else if (pBtdm8723->curPsTdma == 13) {
+ btdm_2AntPsTdma(padapter, true, 14);
+ pBtdm8723->psTdmaDuAdjType = 14;
+ } else if (pBtdm8723->curPsTdma == 14) {
+ btdm_2AntPsTdma(padapter, true, 15);
+ pBtdm8723->psTdmaDuAdjType = 15;
+ } else if (pBtdm8723->curPsTdma == 15) {
+ btdm_2AntPsTdma(padapter, true, 16);
+ pBtdm8723->psTdmaDuAdjType = 16;
+ }
+ } else if (result == 1) {
+ if (pBtdm8723->curPsTdma == 8) {
+ btdm_2AntPsTdma(padapter, true, 7);
+ pBtdm8723->psTdmaDuAdjType = 7;
+ } else if (pBtdm8723->curPsTdma == 7) {
+ btdm_2AntPsTdma(padapter, true, 6);
+ pBtdm8723->psTdmaDuAdjType = 6;
+ } else if (pBtdm8723->curPsTdma == 6) {
+ btdm_2AntPsTdma(padapter, true, 5);
+ pBtdm8723->psTdmaDuAdjType = 5;
+ } else if (pBtdm8723->curPsTdma == 16) {
+ btdm_2AntPsTdma(padapter, true, 15);
+ pBtdm8723->psTdmaDuAdjType = 15;
+ } else if (pBtdm8723->curPsTdma == 15) {
+ btdm_2AntPsTdma(padapter, true, 14);
+ pBtdm8723->psTdmaDuAdjType = 14;
+ } else if (pBtdm8723->curPsTdma == 14) {
+ btdm_2AntPsTdma(padapter, true, 13);
+ pBtdm8723->psTdmaDuAdjType = 13;
+ }
+ }
+ } else {
+ RTPRINT(FBT, BT_TRACE, ("[BTCoex], TxPause = 0\n"));
+ if (pBtdm8723->curPsTdma == 5) {
+ btdm_2AntPsTdma(padapter, true, 1);
+ pBtdm8723->psTdmaDuAdjType = 1;
+ } else if (pBtdm8723->curPsTdma == 6) {
+ btdm_2AntPsTdma(padapter, true, 2);
+ pBtdm8723->psTdmaDuAdjType = 2;
+ } else if (pBtdm8723->curPsTdma == 7) {
+ btdm_2AntPsTdma(padapter, true, 3);
+ pBtdm8723->psTdmaDuAdjType = 3;
+ } else if (pBtdm8723->curPsTdma == 8) {
+ btdm_2AntPsTdma(padapter, true, 4);
+ pBtdm8723->psTdmaDuAdjType = 4;
+ }
+ if (pBtdm8723->curPsTdma == 13) {
+ btdm_2AntPsTdma(padapter, true, 9);
+ pBtdm8723->psTdmaDuAdjType = 9;
+ } else if (pBtdm8723->curPsTdma == 14) {
+ btdm_2AntPsTdma(padapter, true, 10);
+ pBtdm8723->psTdmaDuAdjType = 10;
+ } else if (pBtdm8723->curPsTdma == 15) {
+ btdm_2AntPsTdma(padapter, true, 11);
+ pBtdm8723->psTdmaDuAdjType = 11;
+ } else if (pBtdm8723->curPsTdma == 16) {
+ btdm_2AntPsTdma(padapter, true, 12);
+ pBtdm8723->psTdmaDuAdjType = 12;
+ }
+
+ if (result == -1) {
+ if (pBtdm8723->curPsTdma == 1) {
+ btdm_2AntPsTdma(padapter, true, 2);
+ pBtdm8723->psTdmaDuAdjType = 2;
+ } else if (pBtdm8723->curPsTdma == 2) {
+ btdm_2AntPsTdma(padapter, true, 3);
+ pBtdm8723->psTdmaDuAdjType = 3;
+ } else if (pBtdm8723->curPsTdma == 3) {
+ btdm_2AntPsTdma(padapter, true, 4);
+ pBtdm8723->psTdmaDuAdjType = 4;
+ } else if (pBtdm8723->curPsTdma == 9) {
+ btdm_2AntPsTdma(padapter, true, 10);
+ pBtdm8723->psTdmaDuAdjType = 10;
+ } else if (pBtdm8723->curPsTdma == 10) {
+ btdm_2AntPsTdma(padapter, true, 11);
+ pBtdm8723->psTdmaDuAdjType = 11;
+ } else if (pBtdm8723->curPsTdma == 11) {
+ btdm_2AntPsTdma(padapter, true, 12);
+ pBtdm8723->psTdmaDuAdjType = 12;
+ }
+ } else if (result == 1) {
+ if (pBtdm8723->curPsTdma == 4) {
+ btdm_2AntPsTdma(padapter, true, 3);
+ pBtdm8723->psTdmaDuAdjType = 3;
+ } else if (pBtdm8723->curPsTdma == 3) {
+ btdm_2AntPsTdma(padapter, true, 2);
+ pBtdm8723->psTdmaDuAdjType = 2;
+ } else if (pBtdm8723->curPsTdma == 2) {
+ btdm_2AntPsTdma(padapter, true, 1);
+ pBtdm8723->psTdmaDuAdjType = 1;
+ } else if (pBtdm8723->curPsTdma == 12) {
+ btdm_2AntPsTdma(padapter, true, 11);
+ pBtdm8723->psTdmaDuAdjType = 11;
+ } else if (pBtdm8723->curPsTdma == 11) {
+ btdm_2AntPsTdma(padapter, true, 10);
+ pBtdm8723->psTdmaDuAdjType = 10;
+ } else if (pBtdm8723->curPsTdma == 10) {
+ btdm_2AntPsTdma(padapter, true, 9);
+ pBtdm8723->psTdmaDuAdjType = 9;
+ }
+ }
+ }
+ } else if (maxInterval == 2) {
+ if (bTxPause) {
+ RTPRINT(FBT, BT_TRACE, ("[BTCoex], TxPause = 1\n"));
+ if (pBtdm8723->curPsTdma == 1) {
+ btdm_2AntPsTdma(padapter, true, 6);
+ pBtdm8723->psTdmaDuAdjType = 6;
+ } else if (pBtdm8723->curPsTdma == 2) {
+ btdm_2AntPsTdma(padapter, true, 6);
+ pBtdm8723->psTdmaDuAdjType = 6;
+ } else if (pBtdm8723->curPsTdma == 3) {
+ btdm_2AntPsTdma(padapter, true, 7);
+ pBtdm8723->psTdmaDuAdjType = 7;
+ } else if (pBtdm8723->curPsTdma == 4) {
+ btdm_2AntPsTdma(padapter, true, 8);
+ pBtdm8723->psTdmaDuAdjType = 8;
+ }
+ if (pBtdm8723->curPsTdma == 9) {
+ btdm_2AntPsTdma(padapter, true, 14);
+ pBtdm8723->psTdmaDuAdjType = 14;
+ } else if (pBtdm8723->curPsTdma == 10) {
+ btdm_2AntPsTdma(padapter, true, 14);
+ pBtdm8723->psTdmaDuAdjType = 14;
+ } else if (pBtdm8723->curPsTdma == 11) {
+ btdm_2AntPsTdma(padapter, true, 15);
+ pBtdm8723->psTdmaDuAdjType = 15;
+ } else if (pBtdm8723->curPsTdma == 12) {
+ btdm_2AntPsTdma(padapter, true, 16);
+ pBtdm8723->psTdmaDuAdjType = 16;
+ }
+ if (result == -1) {
+ if (pBtdm8723->curPsTdma == 5) {
+ btdm_2AntPsTdma(padapter, true, 6);
+ pBtdm8723->psTdmaDuAdjType = 6;
+ } else if (pBtdm8723->curPsTdma == 6) {
+ btdm_2AntPsTdma(padapter, true, 7);
+ pBtdm8723->psTdmaDuAdjType = 7;
+ } else if (pBtdm8723->curPsTdma == 7) {
+ btdm_2AntPsTdma(padapter, true, 8);
+ pBtdm8723->psTdmaDuAdjType = 8;
+ } else if (pBtdm8723->curPsTdma == 13) {
+ btdm_2AntPsTdma(padapter, true, 14);
+ pBtdm8723->psTdmaDuAdjType = 14;
+ } else if (pBtdm8723->curPsTdma == 14) {
+ btdm_2AntPsTdma(padapter, true, 15);
+ pBtdm8723->psTdmaDuAdjType = 15;
+ } else if (pBtdm8723->curPsTdma == 15) {
+ btdm_2AntPsTdma(padapter, true, 16);
+ pBtdm8723->psTdmaDuAdjType = 16;
+ }
+ } else if (result == 1) {
+ if (pBtdm8723->curPsTdma == 8) {
+ btdm_2AntPsTdma(padapter, true, 7);
+ pBtdm8723->psTdmaDuAdjType = 7;
+ } else if (pBtdm8723->curPsTdma == 7) {
+ btdm_2AntPsTdma(padapter, true, 6);
+ pBtdm8723->psTdmaDuAdjType = 6;
+ } else if (pBtdm8723->curPsTdma == 6) {
+ btdm_2AntPsTdma(padapter, true, 6);
+ pBtdm8723->psTdmaDuAdjType = 6;
+ } else if (pBtdm8723->curPsTdma == 16) {
+ btdm_2AntPsTdma(padapter, true, 15);
+ pBtdm8723->psTdmaDuAdjType = 15;
+ } else if (pBtdm8723->curPsTdma == 15) {
+ btdm_2AntPsTdma(padapter, true, 14);
+ pBtdm8723->psTdmaDuAdjType = 14;
+ } else if (pBtdm8723->curPsTdma == 14) {
+ btdm_2AntPsTdma(padapter, true, 14);
+ pBtdm8723->psTdmaDuAdjType = 14;
+ }
+ }
+ } else {
+ RTPRINT(FBT, BT_TRACE, ("[BTCoex], TxPause = 0\n"));
+ if (pBtdm8723->curPsTdma == 5) {
+ btdm_2AntPsTdma(padapter, true, 2);
+ pBtdm8723->psTdmaDuAdjType = 2;
+ } else if (pBtdm8723->curPsTdma == 6) {
+ btdm_2AntPsTdma(padapter, true, 2);
+ pBtdm8723->psTdmaDuAdjType = 2;
+ } else if (pBtdm8723->curPsTdma == 7) {
+ btdm_2AntPsTdma(padapter, true, 3);
+ pBtdm8723->psTdmaDuAdjType = 3;
+ } else if (pBtdm8723->curPsTdma == 8) {
+ btdm_2AntPsTdma(padapter, true, 4);
+ pBtdm8723->psTdmaDuAdjType = 4;
+ }
+ if (pBtdm8723->curPsTdma == 13) {
+ btdm_2AntPsTdma(padapter, true, 10);
+ pBtdm8723->psTdmaDuAdjType = 10;
+ } else if (pBtdm8723->curPsTdma == 14) {
+ btdm_2AntPsTdma(padapter, true, 10);
+ pBtdm8723->psTdmaDuAdjType = 10;
+ } else if (pBtdm8723->curPsTdma == 15) {
+ btdm_2AntPsTdma(padapter, true, 11);
+ pBtdm8723->psTdmaDuAdjType = 11;
+ } else if (pBtdm8723->curPsTdma == 16) {
+ btdm_2AntPsTdma(padapter, true, 12);
+ pBtdm8723->psTdmaDuAdjType = 12;
+ }
+ if (result == -1) {
+ if (pBtdm8723->curPsTdma == 1) {
+ btdm_2AntPsTdma(padapter, true, 2);
+ pBtdm8723->psTdmaDuAdjType = 2;
+ } else if (pBtdm8723->curPsTdma == 2) {
+ btdm_2AntPsTdma(padapter, true, 3);
+ pBtdm8723->psTdmaDuAdjType = 3;
+ } else if (pBtdm8723->curPsTdma == 3) {
+ btdm_2AntPsTdma(padapter, true, 4);
+ pBtdm8723->psTdmaDuAdjType = 4;
+ } else if (pBtdm8723->curPsTdma == 9) {
+ btdm_2AntPsTdma(padapter, true, 10);
+ pBtdm8723->psTdmaDuAdjType = 10;
+ } else if (pBtdm8723->curPsTdma == 10) {
+ btdm_2AntPsTdma(padapter, true, 11);
+ pBtdm8723->psTdmaDuAdjType = 11;
+ } else if (pBtdm8723->curPsTdma == 11) {
+ btdm_2AntPsTdma(padapter, true, 12);
+ pBtdm8723->psTdmaDuAdjType = 12;
+ }
+ } else if (result == 1) {
+ if (pBtdm8723->curPsTdma == 4) {
+ btdm_2AntPsTdma(padapter, true, 3);
+ pBtdm8723->psTdmaDuAdjType = 3;
+ } else if (pBtdm8723->curPsTdma == 3) {
+ btdm_2AntPsTdma(padapter, true, 2);
+ pBtdm8723->psTdmaDuAdjType = 2;
+ } else if (pBtdm8723->curPsTdma == 2) {
+ btdm_2AntPsTdma(padapter, true, 2);
+ pBtdm8723->psTdmaDuAdjType = 2;
+ } else if (pBtdm8723->curPsTdma == 12) {
+ btdm_2AntPsTdma(padapter, true, 11);
+ pBtdm8723->psTdmaDuAdjType = 11;
+ } else if (pBtdm8723->curPsTdma == 11) {
+ btdm_2AntPsTdma(padapter, true, 10);
+ pBtdm8723->psTdmaDuAdjType = 10;
+ } else if (pBtdm8723->curPsTdma == 10) {
+ btdm_2AntPsTdma(padapter, true, 10);
+ pBtdm8723->psTdmaDuAdjType = 10;
+ }
+ }
+ }
+ } else if (maxInterval == 3) {
+ if (bTxPause) {
+ RTPRINT(FBT, BT_TRACE, ("[BTCoex], TxPause = 1\n"));
+ if (pBtdm8723->curPsTdma == 1) {
+ btdm_2AntPsTdma(padapter, true, 7);
+ pBtdm8723->psTdmaDuAdjType = 7;
+ } else if (pBtdm8723->curPsTdma == 2) {
+ btdm_2AntPsTdma(padapter, true, 7);
+ pBtdm8723->psTdmaDuAdjType = 7;
+ } else if (pBtdm8723->curPsTdma == 3) {
+ btdm_2AntPsTdma(padapter, true, 7);
+ pBtdm8723->psTdmaDuAdjType = 7;
+ } else if (pBtdm8723->curPsTdma == 4) {
+ btdm_2AntPsTdma(padapter, true, 8);
+ pBtdm8723->psTdmaDuAdjType = 8;
+ }
+ if (pBtdm8723->curPsTdma == 9) {
+ btdm_2AntPsTdma(padapter, true, 15);
+ pBtdm8723->psTdmaDuAdjType = 15;
+ } else if (pBtdm8723->curPsTdma == 10) {
+ btdm_2AntPsTdma(padapter, true, 15);
+ pBtdm8723->psTdmaDuAdjType = 15;
+ } else if (pBtdm8723->curPsTdma == 11) {
+ btdm_2AntPsTdma(padapter, true, 15);
+ pBtdm8723->psTdmaDuAdjType = 15;
+ } else if (pBtdm8723->curPsTdma == 12) {
+ btdm_2AntPsTdma(padapter, true, 16);
+ pBtdm8723->psTdmaDuAdjType = 16;
+ }
+ if (result == -1) {
+ if (pBtdm8723->curPsTdma == 5) {
+ btdm_2AntPsTdma(padapter, true, 7);
+ pBtdm8723->psTdmaDuAdjType = 7;
+ } else if (pBtdm8723->curPsTdma == 6) {
+ btdm_2AntPsTdma(padapter, true, 7);
+ pBtdm8723->psTdmaDuAdjType = 7;
+ } else if (pBtdm8723->curPsTdma == 7) {
+ btdm_2AntPsTdma(padapter, true, 8);
+ pBtdm8723->psTdmaDuAdjType = 8;
+ } else if (pBtdm8723->curPsTdma == 13) {
+ btdm_2AntPsTdma(padapter, true, 15);
+ pBtdm8723->psTdmaDuAdjType = 15;
+ } else if (pBtdm8723->curPsTdma == 14) {
+ btdm_2AntPsTdma(padapter, true, 15);
+ pBtdm8723->psTdmaDuAdjType = 15;
+ } else if (pBtdm8723->curPsTdma == 15) {
+ btdm_2AntPsTdma(padapter, true, 16);
+ pBtdm8723->psTdmaDuAdjType = 16;
+ }
+ } else if (result == 1) {
+ if (pBtdm8723->curPsTdma == 8) {
+ btdm_2AntPsTdma(padapter, true, 7);
+ pBtdm8723->psTdmaDuAdjType = 7;
+ } else if (pBtdm8723->curPsTdma == 7) {
+ btdm_2AntPsTdma(padapter, true, 7);
+ pBtdm8723->psTdmaDuAdjType = 7;
+ } else if (pBtdm8723->curPsTdma == 6) {
+ btdm_2AntPsTdma(padapter, true, 7);
+ pBtdm8723->psTdmaDuAdjType = 7;
+ } else if (pBtdm8723->curPsTdma == 16) {
+ btdm_2AntPsTdma(padapter, true, 15);
+ pBtdm8723->psTdmaDuAdjType = 15;
+ } else if (pBtdm8723->curPsTdma == 15) {
+ btdm_2AntPsTdma(padapter, true, 15);
+ pBtdm8723->psTdmaDuAdjType = 15;
+ } else if (pBtdm8723->curPsTdma == 14) {
+ btdm_2AntPsTdma(padapter, true, 15);
+ pBtdm8723->psTdmaDuAdjType = 15;
+ }
+ }
+ } else {
+ RTPRINT(FBT, BT_TRACE, ("[BTCoex], TxPause = 0\n"));
+ if (pBtdm8723->curPsTdma == 5) {
+ btdm_2AntPsTdma(padapter, true, 3);
+ pBtdm8723->psTdmaDuAdjType = 3;
+ } else if (pBtdm8723->curPsTdma == 6) {
+ btdm_2AntPsTdma(padapter, true, 3);
+ pBtdm8723->psTdmaDuAdjType = 3;
+ } else if (pBtdm8723->curPsTdma == 7) {
+ btdm_2AntPsTdma(padapter, true, 3);
+ pBtdm8723->psTdmaDuAdjType = 3;
+ } else if (pBtdm8723->curPsTdma == 8) {
+ btdm_2AntPsTdma(padapter, true, 4);
+ pBtdm8723->psTdmaDuAdjType = 4;
+ }
+ if (pBtdm8723->curPsTdma == 13) {
+ btdm_2AntPsTdma(padapter, true, 11);
+ pBtdm8723->psTdmaDuAdjType = 11;
+ } else if (pBtdm8723->curPsTdma == 14) {
+ btdm_2AntPsTdma(padapter, true, 11);
+ pBtdm8723->psTdmaDuAdjType = 11;
+ } else if (pBtdm8723->curPsTdma == 15) {
+ btdm_2AntPsTdma(padapter, true, 11);
+ pBtdm8723->psTdmaDuAdjType = 11;
+ } else if (pBtdm8723->curPsTdma == 16) {
+ btdm_2AntPsTdma(padapter, true, 12);
+ pBtdm8723->psTdmaDuAdjType = 12;
+ }
+ if (result == -1) {
+ if (pBtdm8723->curPsTdma == 1) {
+ btdm_2AntPsTdma(padapter, true, 3);
+ pBtdm8723->psTdmaDuAdjType = 3;
+ } else if (pBtdm8723->curPsTdma == 2) {
+ btdm_2AntPsTdma(padapter, true, 3);
+ pBtdm8723->psTdmaDuAdjType = 3;
+ } else if (pBtdm8723->curPsTdma == 3) {
+ btdm_2AntPsTdma(padapter, true, 4);
+ pBtdm8723->psTdmaDuAdjType = 4;
+ } else if (pBtdm8723->curPsTdma == 9) {
+ btdm_2AntPsTdma(padapter, true, 11);
+ pBtdm8723->psTdmaDuAdjType = 11;
+ } else if (pBtdm8723->curPsTdma == 10) {
+ btdm_2AntPsTdma(padapter, true, 11);
+ pBtdm8723->psTdmaDuAdjType = 11;
+ } else if (pBtdm8723->curPsTdma == 11) {
+ btdm_2AntPsTdma(padapter, true, 12);
+ pBtdm8723->psTdmaDuAdjType = 12;
+ }
+ } else if (result == 1) {
+ if (pBtdm8723->curPsTdma == 4) {
+ btdm_2AntPsTdma(padapter, true, 3);
+ pBtdm8723->psTdmaDuAdjType = 3;
+ } else if (pBtdm8723->curPsTdma == 3) {
+ btdm_2AntPsTdma(padapter, true, 3);
+ pBtdm8723->psTdmaDuAdjType = 3;
+ } else if (pBtdm8723->curPsTdma == 2) {
+ btdm_2AntPsTdma(padapter, true, 3);
+ pBtdm8723->psTdmaDuAdjType = 3;
+ } else if (pBtdm8723->curPsTdma == 12) {
+ btdm_2AntPsTdma(padapter, true, 11);
+ pBtdm8723->psTdmaDuAdjType = 11;
+ } else if (pBtdm8723->curPsTdma == 11) {
+ btdm_2AntPsTdma(padapter, true, 11);
+ pBtdm8723->psTdmaDuAdjType = 11;
+ } else if (pBtdm8723->curPsTdma == 10) {
+ btdm_2AntPsTdma(padapter, true, 11);
+ pBtdm8723->psTdmaDuAdjType = 11;
+ }
+ }
+ }
+ }
+ }
+ RTPRINT(FBT, BT_TRACE, ("[BTCoex], PsTdma type : recordPsTdma =%d\n", pBtdm8723->psTdmaDuAdjType));
+ /* if current PsTdma not match with the recorded one (when scan, dhcp...), */
+ /* then we have to adjust it back to the previous record one. */
+ if (pBtdm8723->curPsTdma != pBtdm8723->psTdmaDuAdjType) {
+ RTPRINT(FBT, BT_TRACE, ("[BTCoex], PsTdma type dismatch!!!, curPsTdma =%d, recordPsTdma =%d\n",
+ pBtdm8723->curPsTdma, pBtdm8723->psTdmaDuAdjType));
+
+ if (!check_fwstate(&padapter->mlmepriv, _FW_UNDER_SURVEY|_FW_UNDER_LINKING))
+ btdm_2AntPsTdma(padapter, true, pBtdm8723->psTdmaDuAdjType);
+ else
+ RTPRINT(FBT, BT_TRACE, ("[BTCoex], roaming/link/scan is under progress, will adjust next time!!!\n"));
+ }
+}
+
+/* default Action */
+/* SCO only or SCO+PAN(HS) */
+static void btdm_2Ant8723ASCOAction(struct rtw_adapter *padapter)
+{
+ u8 btRssiState, btRssiState1;
+
+ if (btdm_NeedToDecBtPwr(padapter))
+ btdm_2AntDecBtPwr(padapter, true);
+ else
+ btdm_2AntDecBtPwr(padapter, false);
+
+ if (BTDM_IsHT40(padapter)) {
+ RTPRINT(FBT, BT_TRACE, ("HT40\n"));
+ btRssiState = BTDM_CheckCoexRSSIState(padapter, 2, 37, 0);
+ /* fw mechanism */
+ if ((btRssiState == BT_RSSI_STATE_HIGH) ||
+ (btRssiState == BT_RSSI_STATE_STAY_HIGH)) {
+ RTPRINT(FBT, BT_TRACE, ("Wifi rssi-1 high \n"));
+ PlatformEFIOWrite1Byte(padapter, 0x883, 0x40);
+ btdm_2AntPsTdma(padapter, true, 11);
+ } else {
+ RTPRINT(FBT, BT_TRACE, ("Wifi rssi-1 low \n"));
+ btdm_2AntPsTdma(padapter, true, 15);
+ }
+
+ /* sw mechanism */
+ btdm_2AntAgcTable(padapter, false);
+ btdm_2AntAdcBackOff(padapter, true);
+ btdm_2AntDacSwing(padapter, false, 0xc0);
+ } else {
+ RTPRINT(FBT, BT_TRACE, ("HT20 or Legacy\n"));
+ btRssiState = BTDM_CheckCoexRSSIState(padapter, 2, 37, 0);
+ btRssiState1 = BTDM_CheckCoexRSSIState1(padapter, 2, 27, 0);
+
+ /* fw mechanism */
+ if ((btRssiState1 == BT_RSSI_STATE_HIGH) ||
+ (btRssiState1 == BT_RSSI_STATE_STAY_HIGH)) {
+ RTPRINT(FBT, BT_TRACE, ("Wifi rssi-1 high \n"));
+ PlatformEFIOWrite1Byte(padapter, 0x883, 0x40);
+ btdm_2AntPsTdma(padapter, true, 11);
+ } else {
+ RTPRINT(FBT, BT_TRACE, ("Wifi rssi-1 low \n"));
+ btdm_2AntPsTdma(padapter, true, 15);
+ }
+
+ /* sw mechanism */
+ if ((btRssiState == BT_RSSI_STATE_HIGH) ||
+ (btRssiState == BT_RSSI_STATE_STAY_HIGH)) {
+ RTPRINT(FBT, BT_TRACE, ("Wifi rssi high \n"));
+ btdm_2AntAgcTable(padapter, true);
+ btdm_2AntAdcBackOff(padapter, true);
+ btdm_2AntDacSwing(padapter, false, 0xc0);
+ } else {
+ RTPRINT(FBT, BT_TRACE, ("Wifi rssi low \n"));
+ btdm_2AntAgcTable(padapter, false);
+ btdm_2AntAdcBackOff(padapter, false);
+ btdm_2AntDacSwing(padapter, false, 0xc0);
+ }
+ }
+}
+
+static void btdm_2Ant8723AHIDAction(struct rtw_adapter *padapter)
+{
+ u8 btRssiState, btRssiState1;
+
+ if (btdm_NeedToDecBtPwr(padapter))
+ btdm_2AntDecBtPwr(padapter, true);
+ else
+ btdm_2AntDecBtPwr(padapter, false);
+
+ if (BTDM_IsHT40(padapter)) {
+ RTPRINT(FBT, BT_TRACE, ("HT40\n"));
+ btRssiState = BTDM_CheckCoexRSSIState(padapter, 2, 37, 0);
+ /* fw mechanism */
+ if ((btRssiState == BT_RSSI_STATE_HIGH) ||
+ (btRssiState == BT_RSSI_STATE_STAY_HIGH)) {
+ RTPRINT(FBT, BT_TRACE, ("Wifi rssi-1 high \n"));
+ PlatformEFIOWrite1Byte(padapter, 0x883, 0x40);
+ btdm_2AntPsTdma(padapter, true, 9);
+ } else {
+ RTPRINT(FBT, BT_TRACE, ("Wifi rssi-1 low \n"));
+ btdm_2AntPsTdma(padapter, true, 13);
+ }
+
+ /* sw mechanism */
+ btdm_2AntAgcTable(padapter, false);
+ btdm_2AntAdcBackOff(padapter, false);
+ btdm_2AntDacSwing(padapter, false, 0xc0);
+ } else {
+ RTPRINT(FBT, BT_TRACE, ("HT20 or Legacy\n"));
+ btRssiState = BTDM_CheckCoexRSSIState(padapter, 2, 37, 0);
+ btRssiState1 = BTDM_CheckCoexRSSIState1(padapter, 2, 27, 0);
+
+ /* fw mechanism */
+ if ((btRssiState1 == BT_RSSI_STATE_HIGH) ||
+ (btRssiState1 == BT_RSSI_STATE_STAY_HIGH)) {
+ RTPRINT(FBT, BT_TRACE, ("Wifi rssi-1 high \n"));
+ PlatformEFIOWrite1Byte(padapter, 0x883, 0x40);
+ btdm_2AntPsTdma(padapter, true, 9);
+ } else {
+ RTPRINT(FBT, BT_TRACE, ("Wifi rssi-1 low \n"));
+ btdm_2AntPsTdma(padapter, true, 13);
+ }
+
+ /* sw mechanism */
+ if ((btRssiState == BT_RSSI_STATE_HIGH) ||
+ (btRssiState == BT_RSSI_STATE_STAY_HIGH)) {
+ RTPRINT(FBT, BT_TRACE, ("Wifi rssi high \n"));
+ btdm_2AntAgcTable(padapter, true);
+ btdm_2AntAdcBackOff(padapter, true);
+ btdm_2AntDacSwing(padapter, false, 0xc0);
+ } else {
+ RTPRINT(FBT, BT_TRACE, ("Wifi rssi low \n"));
+ btdm_2AntAgcTable(padapter, false);
+ btdm_2AntAdcBackOff(padapter, false);
+ btdm_2AntDacSwing(padapter, false, 0xc0);
+ }
+ }
+}
+
+/* A2DP only / PAN(EDR) only/ A2DP+PAN(HS) */
+static void btdm_2Ant8723AA2DPAction(struct rtw_adapter *padapter)
+{
+ u8 btRssiState, btRssiState1;
+ struct hal_data_8723a *pHalData = GET_HAL_DATA(padapter);
+ u8 btInfoExt = pHalData->bt_coexist.halCoex8723.btInfoExt;
+
+ if (btdm_NeedToDecBtPwr(padapter))
+ btdm_2AntDecBtPwr(padapter, true);
+ else
+ btdm_2AntDecBtPwr(padapter, false);
+
+ if (BTDM_IsHT40(padapter)) {
+ RTPRINT(FBT, BT_TRACE, ("HT40\n"));
+ btRssiState = BTDM_CheckCoexRSSIState(padapter, 2, 37, 0);
+
+ /* fw mechanism */
+ if ((btRssiState == BT_RSSI_STATE_HIGH) ||
+ (btRssiState == BT_RSSI_STATE_STAY_HIGH)) {
+ RTPRINT(FBT, BT_TRACE, ("Wifi rssi high \n"));
+ PlatformEFIOWrite1Byte(padapter, 0x883, 0x40);
+
+ if (btInfoExt&BIT(0)) { /* a2dp rate, 1:basic /0:edr */
+ RTPRINT(FBT, BT_TRACE, ("a2dp basic rate \n"));
+ btdm_2AntTdmaDurationAdjust(padapter, false, false, 3);
+ } else {
+ RTPRINT(FBT, BT_TRACE, ("a2dp edr rate \n"));
+ btdm_2AntTdmaDurationAdjust(padapter, false, false, 1);
+ }
+ } else {
+ RTPRINT(FBT, BT_TRACE, ("Wifi rssi low \n"));
+ if (btInfoExt&BIT(0)) { /* a2dp rate, 1:basic /0:edr */
+ RTPRINT(FBT, BT_TRACE, ("a2dp basic rate \n"));
+ btdm_2AntTdmaDurationAdjust(padapter, false, true, 3);
+ } else {
+ RTPRINT(FBT, BT_TRACE, ("a2dp edr rate \n"));
+ btdm_2AntTdmaDurationAdjust(padapter, false, true, 1);
+ }
+ }
+
+ /* sw mechanism */
+ btdm_2AntAgcTable(padapter, false);
+ btdm_2AntAdcBackOff(padapter, true);
+ btdm_2AntDacSwing(padapter, false, 0xc0);
+ } else {
+ RTPRINT(FBT, BT_TRACE, ("HT20 or Legacy\n"));
+ btRssiState = BTDM_CheckCoexRSSIState(padapter, 2, 37, 0);
+ btRssiState1 = BTDM_CheckCoexRSSIState1(padapter, 2, 27, 0);
+
+ /* fw mechanism */
+ if ((btRssiState1 == BT_RSSI_STATE_HIGH) ||
+ (btRssiState1 == BT_RSSI_STATE_STAY_HIGH)) {
+ RTPRINT(FBT, BT_TRACE, ("Wifi rssi-1 high \n"));
+ PlatformEFIOWrite1Byte(padapter, 0x883, 0x40);
+ if (btInfoExt&BIT(0)) { /* a2dp rate, 1:basic /0:edr */
+ RTPRINT(FBT, BT_TRACE, ("a2dp basic rate \n"));
+ btdm_2AntTdmaDurationAdjust(padapter, false, false, 3);
+ } else {
+ RTPRINT(FBT, BT_TRACE, ("a2dp edr rate \n"));
+ btdm_2AntTdmaDurationAdjust(padapter, false, false, 1);
+ }
+ } else {
+ RTPRINT(FBT, BT_TRACE, ("Wifi rssi-1 low \n"));
+ if (btInfoExt&BIT(0)) { /* a2dp rate, 1:basic /0:edr */
+ RTPRINT(FBT, BT_TRACE, ("a2dp basic rate \n"));
+ btdm_2AntTdmaDurationAdjust(padapter, false, true, 3);
+ } else {
+ RTPRINT(FBT, BT_TRACE, ("a2dp edr rate \n"));
+ btdm_2AntTdmaDurationAdjust(padapter, false, true, 1);
+ }
+ }
+
+ /* sw mechanism */
+ if ((btRssiState == BT_RSSI_STATE_HIGH) ||
+ (btRssiState == BT_RSSI_STATE_STAY_HIGH)) {
+ RTPRINT(FBT, BT_TRACE, ("Wifi rssi high \n"));
+ btdm_2AntAgcTable(padapter, true);
+ btdm_2AntAdcBackOff(padapter, true);
+ btdm_2AntDacSwing(padapter, false, 0xc0);
+ } else {
+ RTPRINT(FBT, BT_TRACE, ("Wifi rssi low \n"));
+ btdm_2AntAgcTable(padapter, false);
+ btdm_2AntAdcBackOff(padapter, false);
+ btdm_2AntDacSwing(padapter, false, 0xc0);
+ }
+ }
+}
+
+static void btdm_2Ant8723APANEDRAction(struct rtw_adapter *padapter)
+{
+ u8 btRssiState, btRssiState1;
+
+ if (btdm_NeedToDecBtPwr(padapter))
+ btdm_2AntDecBtPwr(padapter, true);
+ else
+ btdm_2AntDecBtPwr(padapter, false);
+
+ if (BTDM_IsHT40(padapter)) {
+ RTPRINT(FBT, BT_TRACE, ("HT40\n"));
+ btRssiState = BTDM_CheckCoexRSSIState(padapter, 2, 37, 0);
+
+ /* fw mechanism */
+ if ((btRssiState == BT_RSSI_STATE_HIGH) ||
+ (btRssiState == BT_RSSI_STATE_STAY_HIGH)) {
+ RTPRINT(FBT, BT_TRACE, ("Wifi rssi high \n"));
+ PlatformEFIOWrite1Byte(padapter, 0x883, 0x40);
+ btdm_2AntPsTdma(padapter, true, 2);
+ } else {
+ RTPRINT(FBT, BT_TRACE, ("Wifi rssi low \n"));
+ btdm_2AntPsTdma(padapter, true, 6);
+ }
+
+ /* sw mechanism */
+ btdm_2AntAgcTable(padapter, false);
+ btdm_2AntAdcBackOff(padapter, true);
+ btdm_2AntDacSwing(padapter, false, 0xc0);
+ } else {
+ RTPRINT(FBT, BT_TRACE, ("HT20 or Legacy\n"));
+ btRssiState = BTDM_CheckCoexRSSIState(padapter, 2, 37, 0);
+ btRssiState1 = BTDM_CheckCoexRSSIState1(padapter, 2, 27, 0);
+
+ /* fw mechanism */
+ if ((btRssiState1 == BT_RSSI_STATE_HIGH) ||
+ (btRssiState1 == BT_RSSI_STATE_STAY_HIGH)) {
+ RTPRINT(FBT, BT_TRACE, ("Wifi rssi-1 high \n"));
+ PlatformEFIOWrite1Byte(padapter, 0x883, 0x40);
+ btdm_2AntPsTdma(padapter, true, 2);
+ } else {
+ RTPRINT(FBT, BT_TRACE, ("Wifi rssi-1 low \n"));
+ btdm_2AntPsTdma(padapter, true, 6);
+ }
+
+ /* sw mechanism */
+ if ((btRssiState == BT_RSSI_STATE_HIGH) ||
+ (btRssiState == BT_RSSI_STATE_STAY_HIGH)) {
+ RTPRINT(FBT, BT_TRACE, ("Wifi rssi high \n"));
+ btdm_2AntAgcTable(padapter, true);
+ btdm_2AntAdcBackOff(padapter, true);
+ btdm_2AntDacSwing(padapter, false, 0xc0);
+ } else {
+ RTPRINT(FBT, BT_TRACE, ("Wifi rssi low \n"));
+ btdm_2AntAgcTable(padapter, false);
+ btdm_2AntAdcBackOff(padapter, false);
+ btdm_2AntDacSwing(padapter, false, 0xc0);
+ }
+ }
+}
+
+/* PAN(HS) only */
+static void btdm_2Ant8723APANHSAction(struct rtw_adapter *padapter)
+{
+ u8 btRssiState;
+
+ if (BTDM_IsHT40(padapter)) {
+ RTPRINT(FBT, BT_TRACE, ("HT40\n"));
+ btRssiState = BTDM_CheckCoexRSSIState(padapter, 2, 47, 0);
+ /* fw mechanism */
+ if ((btRssiState == BT_RSSI_STATE_HIGH) ||
+ (btRssiState == BT_RSSI_STATE_STAY_HIGH)) {
+ RTPRINT(FBT, BT_TRACE, ("Wifi rssi high \n"));
+ btdm_2AntDecBtPwr(padapter, true);
+ } else {
+ RTPRINT(FBT, BT_TRACE, ("Wifi rssi low \n"));
+ btdm_2AntDecBtPwr(padapter, false);
+ }
+ btdm_2AntPsTdma(padapter, false, 0);
+
+ /* sw mechanism */
+ btdm_2AntAgcTable(padapter, false);
+ btdm_2AntAdcBackOff(padapter, true);
+ btdm_2AntDacSwing(padapter, false, 0xc0);
+ } else {
+ RTPRINT(FBT, BT_TRACE, ("HT20 or Legacy\n"));
+ btRssiState = BTDM_CheckCoexRSSIState(padapter, 2, 47, 0);
+
+ if ((btRssiState == BT_RSSI_STATE_HIGH) ||
+ (btRssiState == BT_RSSI_STATE_STAY_HIGH)) {
+ RTPRINT(FBT, BT_TRACE, ("Wifi rssi high\n"));
+ /* fw mechanism */
+ btdm_2AntDecBtPwr(padapter, true);
+ btdm_2AntPsTdma(padapter, false, 0);
+
+ /* sw mechanism */
+ btdm_2AntAgcTable(padapter, true);
+ btdm_2AntAdcBackOff(padapter, true);
+ btdm_2AntDacSwing(padapter, false, 0xc0);
+ } else {
+ RTPRINT(FBT, BT_TRACE, ("Wifi rssi low\n"));
+ /* fw mechanism */
+ btdm_2AntDecBtPwr(padapter, false);
+ btdm_2AntPsTdma(padapter, false, 0);
+
+ /* sw mechanism */
+ btdm_2AntAgcTable(padapter, false);
+ btdm_2AntAdcBackOff(padapter, false);
+ btdm_2AntDacSwing(padapter, false, 0xc0);
+ }
+ }
+}
+
+/* PAN(EDR)+A2DP */
+static void btdm_2Ant8723APANEDRA2DPAction(struct rtw_adapter *padapter)
+{
+ u8 btRssiState, btRssiState1, btInfoExt;
+ struct hal_data_8723a *pHalData = GET_HAL_DATA(padapter);
+
+ btInfoExt = pHalData->bt_coexist.halCoex8723.btInfoExt;
+
+ if (btdm_NeedToDecBtPwr(padapter))
+ btdm_2AntDecBtPwr(padapter, true);
+ else
+ btdm_2AntDecBtPwr(padapter, false);
+
+ if (BTDM_IsHT40(padapter)) {
+ RTPRINT(FBT, BT_TRACE, ("HT40\n"));
+ btRssiState = BTDM_CheckCoexRSSIState(padapter, 2, 37, 0);
+
+ if ((btRssiState == BT_RSSI_STATE_HIGH) ||
+ (btRssiState == BT_RSSI_STATE_STAY_HIGH)) {
+ RTPRINT(FBT, BT_TRACE, ("Wifi rssi high \n"));
+ /* fw mechanism */
+ PlatformEFIOWrite1Byte(padapter, 0x883, 0x40);
+
+ if (btInfoExt&BIT(0)) { /* a2dp rate, 1:basic /0:edr */
+ RTPRINT(FBT, BT_TRACE, ("a2dp basic rate \n"));
+ btdm_2AntPsTdma(padapter, true, 4);
+ } else {
+ RTPRINT(FBT, BT_TRACE, ("a2dp edr rate \n"));
+ btdm_2AntPsTdma(padapter, true, 2);
+ }
+ } else {
+ RTPRINT(FBT, BT_TRACE, ("Wifi rssi low \n"));
+ /* fw mechanism */
+ if (btInfoExt&BIT(0)) { /* a2dp rate, 1:basic /0:edr */
+ RTPRINT(FBT, BT_TRACE, ("a2dp basic rate \n"));
+ btdm_2AntPsTdma(padapter, true, 8);
+ } else {
+ RTPRINT(FBT, BT_TRACE, ("a2dp edr rate \n"));
+ btdm_2AntPsTdma(padapter, true, 6);
+ }
+ }
+
+ /* sw mechanism */
+ btdm_2AntAgcTable(padapter, false);
+ btdm_2AntAdcBackOff(padapter, true);
+ btdm_2AntDacSwing(padapter, false, 0xc0);
+ } else {
+ RTPRINT(FBT, BT_TRACE, ("HT20 or Legacy\n"));
+ btRssiState = BTDM_CheckCoexRSSIState(padapter, 2, 37, 0);
+ btRssiState1 = BTDM_CheckCoexRSSIState1(padapter, 2, 27, 0);
+
+ if ((btRssiState1 == BT_RSSI_STATE_HIGH) ||
+ (btRssiState1 == BT_RSSI_STATE_STAY_HIGH)) {
+ RTPRINT(FBT, BT_TRACE, ("Wifi rssi-1 high \n"));
+ PlatformEFIOWrite1Byte(padapter, 0x883, 0x40);
+ /* fw mechanism */
+ if (btInfoExt&BIT(0)) { /* a2dp rate, 1:basic /0:edr */
+ RTPRINT(FBT, BT_TRACE, ("a2dp basic rate \n"));
+ btdm_2AntPsTdma(padapter, true, 4);
+ } else {
+ RTPRINT(FBT, BT_TRACE, ("a2dp edr rate \n"));
+ btdm_2AntPsTdma(padapter, true, 2);
+ }
+ } else {
+ RTPRINT(FBT, BT_TRACE, ("Wifi rssi-1 low \n"));
+ /* fw mechanism */
+ if (btInfoExt&BIT(0)) { /* a2dp rate, 1:basic /0:edr */
+ RTPRINT(FBT, BT_TRACE, ("a2dp basic rate \n"));
+ btdm_2AntPsTdma(padapter, true, 8);
+ } else {
+ RTPRINT(FBT, BT_TRACE, ("a2dp edr rate \n"));
+ btdm_2AntPsTdma(padapter, true, 6);
+ }
+ }
+
+ /* sw mechanism */
+ if ((btRssiState == BT_RSSI_STATE_HIGH) ||
+ (btRssiState == BT_RSSI_STATE_STAY_HIGH)) {
+ RTPRINT(FBT, BT_TRACE, ("Wifi rssi high \n"));
+ btdm_2AntAgcTable(padapter, true);
+ btdm_2AntAdcBackOff(padapter, true);
+ btdm_2AntDacSwing(padapter, false, 0xc0);
+ } else {
+ RTPRINT(FBT, BT_TRACE, ("Wifi rssi low \n"));
+ btdm_2AntAgcTable(padapter, false);
+ btdm_2AntAdcBackOff(padapter, false);
+ btdm_2AntDacSwing(padapter, false, 0xc0);
+ }
+ }
+}
+
+static void btdm_2Ant8723APANEDRHIDAction(struct rtw_adapter *padapter)
+{
+ u8 btRssiState, btRssiState1;
+
+ if (btdm_NeedToDecBtPwr(padapter))
+ btdm_2AntDecBtPwr(padapter, true);
+ else
+ btdm_2AntDecBtPwr(padapter, false);
+
+ if (BTDM_IsHT40(padapter)) {
+ RTPRINT(FBT, BT_TRACE, ("HT40\n"));
+ btRssiState = BTDM_CheckCoexRSSIState(padapter, 2, 37, 0);
+ /* fw mechanism */
+ if ((btRssiState == BT_RSSI_STATE_HIGH) ||
+ (btRssiState == BT_RSSI_STATE_STAY_HIGH)) {
+ RTPRINT(FBT, BT_TRACE, ("Wifi rssi high \n"));
+ PlatformEFIOWrite1Byte(padapter, 0x883, 0x40);
+ btdm_2AntPsTdma(padapter, true, 10);
+ } else {
+ RTPRINT(FBT, BT_TRACE, ("Wifi rssi low \n"));
+ btdm_2AntPsTdma(padapter, true, 14);
+ }
+
+ /* sw mechanism */
+ btdm_2AntAgcTable(padapter, false);
+ btdm_2AntAdcBackOff(padapter, true);
+ btdm_2AntDacSwing(padapter, false, 0xc0);
+ } else {
+ RTPRINT(FBT, BT_TRACE, ("HT20 or Legacy\n"));
+ btRssiState = BTDM_CheckCoexRSSIState(padapter, 2, 37, 0);
+ btRssiState1 = BTDM_CheckCoexRSSIState1(padapter, 2, 27, 0);
+
+ /* fw mechanism */
+ if ((btRssiState1 == BT_RSSI_STATE_HIGH) ||
+ (btRssiState1 == BT_RSSI_STATE_STAY_HIGH)) {
+ RTPRINT(FBT, BT_TRACE, ("Wifi rssi-1 high \n"));
+ PlatformEFIOWrite1Byte(padapter, 0x883, 0x40);
+ btdm_2AntPsTdma(padapter, true, 10);
+ } else {
+ RTPRINT(FBT, BT_TRACE, ("Wifi rssi-1 low \n"));
+ btdm_2AntPsTdma(padapter, true, 14);
+ }
+
+ /* sw mechanism */
+ if ((btRssiState == BT_RSSI_STATE_HIGH) ||
+ (btRssiState == BT_RSSI_STATE_STAY_HIGH)) {
+ RTPRINT(FBT, BT_TRACE, ("Wifi rssi high \n"));
+ btdm_2AntAgcTable(padapter, true);
+ btdm_2AntAdcBackOff(padapter, true);
+ btdm_2AntDacSwing(padapter, false, 0xc0);
+ } else {
+ RTPRINT(FBT, BT_TRACE, ("Wifi rssi low \n"));
+ btdm_2AntAgcTable(padapter, false);
+ btdm_2AntAdcBackOff(padapter, false);
+ btdm_2AntDacSwing(padapter, false, 0xc0);
+ }
+ }
+}
+
+/* HID+A2DP+PAN(EDR) */
+static void btdm_2Ant8723AHIDA2DPPANEDRAction(struct rtw_adapter *padapter)
+{
+ u8 btRssiState, btRssiState1, btInfoExt;
+ struct hal_data_8723a *pHalData = GET_HAL_DATA(padapter);
+
+ btInfoExt = pHalData->bt_coexist.halCoex8723.btInfoExt;
+
+ if (btdm_NeedToDecBtPwr(padapter))
+ btdm_2AntDecBtPwr(padapter, true);
+ else
+ btdm_2AntDecBtPwr(padapter, false);
+
+ if (BTDM_IsHT40(padapter)) {
+ RTPRINT(FBT, BT_TRACE, ("HT40\n"));
+ btRssiState = BTDM_CheckCoexRSSIState(padapter, 2, 37, 0);
+ if ((btRssiState == BT_RSSI_STATE_HIGH) ||
+ (btRssiState == BT_RSSI_STATE_STAY_HIGH)) {
+ RTPRINT(FBT, BT_TRACE, ("Wifi rssi high \n"));
+ PlatformEFIOWrite1Byte(padapter, 0x883, 0x40);
+
+ if (btInfoExt&BIT(0)) { /* a2dp rate, 1:basic /0:edr */
+ RTPRINT(FBT, BT_TRACE, ("a2dp basic rate \n"));
+ btdm_2AntPsTdma(padapter, true, 12);
+ } else {
+ RTPRINT(FBT, BT_TRACE, ("a2dp edr rate \n"));
+ btdm_2AntPsTdma(padapter, true, 10);
+ }
+ } else {
+ RTPRINT(FBT, BT_TRACE, ("Wifi rssi low \n"));
+ if (btInfoExt&BIT(0)) { /* a2dp rate, 1:basic /0:edr */
+ RTPRINT(FBT, BT_TRACE, ("a2dp basic rate \n"));
+ btdm_2AntPsTdma(padapter, true, 16);
+ } else {
+ RTPRINT(FBT, BT_TRACE, ("a2dp edr rate \n"));
+ btdm_2AntPsTdma(padapter, true, 14);
+ }
+ }
+
+ /* sw mechanism */
+ btdm_2AntAgcTable(padapter, false);
+ btdm_2AntAdcBackOff(padapter, true);
+ btdm_2AntDacSwing(padapter, false, 0xc0);
+ } else {
+ RTPRINT(FBT, BT_TRACE, ("HT20 or Legacy\n"));
+ btRssiState1 = BTDM_CheckCoexRSSIState1(padapter, 2, 37, 0);
+ btRssiState = BTDM_CheckCoexRSSIState(padapter, 2, 27, 0);
+ if ((btRssiState == BT_RSSI_STATE_HIGH) ||
+ (btRssiState == BT_RSSI_STATE_STAY_HIGH)) {
+ RTPRINT(FBT, BT_TRACE, ("Wifi rssi high \n"));
+ PlatformEFIOWrite1Byte(padapter, 0x883, 0x40);
+
+ if (btInfoExt&BIT(0)) { /* a2dp rate, 1:basic /0:edr */
+ RTPRINT(FBT, BT_TRACE, ("a2dp basic rate \n"));
+ btdm_2AntPsTdma(padapter, true, 12);
+ } else {
+ RTPRINT(FBT, BT_TRACE, ("a2dp edr rate \n"));
+ btdm_2AntPsTdma(padapter, true, 10);
+ }
+ } else {
+ RTPRINT(FBT, BT_TRACE, ("Wifi rssi low \n"));
+ if (btInfoExt&BIT(0)) { /* a2dp rate, 1:basic /0:edr */
+ RTPRINT(FBT, BT_TRACE, ("a2dp basic rate \n"));
+ btdm_2AntPsTdma(padapter, true, 16);
+ } else {
+ RTPRINT(FBT, BT_TRACE, ("a2dp edr rate \n"));
+ btdm_2AntPsTdma(padapter, true, 14);
+ }
+ }
+
+ /* sw mechanism */
+ if ((btRssiState1 == BT_RSSI_STATE_HIGH) ||
+ (btRssiState1 == BT_RSSI_STATE_STAY_HIGH)) {
+ RTPRINT(FBT, BT_TRACE, ("Wifi rssi-1 high \n"));
+ btdm_2AntAgcTable(padapter, true);
+ btdm_2AntAdcBackOff(padapter, true);
+ btdm_2AntDacSwing(padapter, false, 0xc0);
+ } else {
+ RTPRINT(FBT, BT_TRACE, ("Wifi rssi-1 high \n"));
+ btdm_2AntAgcTable(padapter, false);
+ btdm_2AntAdcBackOff(padapter, false);
+ btdm_2AntDacSwing(padapter, false, 0xc0);
+ }
+ }
+}
+
+static void btdm_2Ant8723AHIDA2DPAction(struct rtw_adapter *padapter)
+{
+ struct hal_data_8723a *pHalData = GET_HAL_DATA(padapter);
+ u8 btRssiState, btRssiState1, btInfoExt;
+
+ btInfoExt = pHalData->bt_coexist.halCoex8723.btInfoExt;
+
+ if (btdm_NeedToDecBtPwr(padapter))
+ btdm_2AntDecBtPwr(padapter, true);
+ else
+ btdm_2AntDecBtPwr(padapter, false);
+
+ if (BTDM_IsHT40(padapter)) {
+ RTPRINT(FBT, BT_TRACE, ("HT40\n"));
+ btRssiState = BTDM_CheckCoexRSSIState(padapter, 2, 37, 0);
+ if ((btRssiState == BT_RSSI_STATE_HIGH) ||
+ (btRssiState == BT_RSSI_STATE_STAY_HIGH)) {
+ RTPRINT(FBT, BT_TRACE, ("Wifi rssi high \n"));
+ PlatformEFIOWrite1Byte(padapter, 0x883, 0x40);
+
+ if (btInfoExt&BIT(0)) { /* a2dp rate, 1:basic /0:edr */
+ RTPRINT(FBT, BT_TRACE, ("a2dp basic rate \n"));
+ btdm_2AntTdmaDurationAdjust(padapter, true, false, 3);
+ } else {
+ RTPRINT(FBT, BT_TRACE, ("a2dp edr rate \n"));
+ btdm_2AntTdmaDurationAdjust(padapter, true, false, 1);
+ }
+ } else {
+ RTPRINT(FBT, BT_TRACE, ("Wifi rssi low \n"));
+ if (btInfoExt&BIT(0)) { /* a2dp rate, 1:basic /0:edr */
+ RTPRINT(FBT, BT_TRACE, ("a2dp basic rate \n"));
+ btdm_2AntTdmaDurationAdjust(padapter, true, true, 3);
+ } else {
+ RTPRINT(FBT, BT_TRACE, ("a2dp edr rate \n"));
+ btdm_2AntTdmaDurationAdjust(padapter, true, true, 1);
+ }
+ }
+ /* sw mechanism */
+ btdm_2AntAgcTable(padapter, false);
+ btdm_2AntAdcBackOff(padapter, true);
+ btdm_2AntDacSwing(padapter, false, 0xc0);
+ } else {
+ RTPRINT(FBT, BT_TRACE, ("HT20 or Legacy\n"));
+ btRssiState = BTDM_CheckCoexRSSIState(padapter, 2, 37, 0);
+ btRssiState1 = BTDM_CheckCoexRSSIState(padapter, 2, 27, 0);
+
+ if ((btRssiState == BT_RSSI_STATE_HIGH) ||
+ (btRssiState == BT_RSSI_STATE_STAY_HIGH)) {
+ RTPRINT(FBT, BT_TRACE, ("Wifi rssi high \n"));
+ PlatformEFIOWrite1Byte(padapter, 0x883, 0x40);
+
+ if (btInfoExt&BIT(0)) { /* a2dp rate, 1:basic /0:edr */
+ RTPRINT(FBT, BT_TRACE, ("a2dp basic rate \n"));
+ btdm_2AntTdmaDurationAdjust(padapter, true, false, 3);
+ } else {
+ RTPRINT(FBT, BT_TRACE, ("a2dp edr rate \n"));
+ btdm_2AntTdmaDurationAdjust(padapter, true, false, 1);
+ }
+ } else {
+ RTPRINT(FBT, BT_TRACE, ("Wifi rssi low \n"));
+ if (btInfoExt&BIT(0)) { /* a2dp rate, 1:basic /0:edr */
+ RTPRINT(FBT, BT_TRACE, ("a2dp basic rate \n"));
+ btdm_2AntTdmaDurationAdjust(padapter, true, true, 3);
+ } else {
+ RTPRINT(FBT, BT_TRACE, ("a2dp edr rate \n"));
+ btdm_2AntTdmaDurationAdjust(padapter, true, true, 1);
+ }
+ }
+ if ((btRssiState1 == BT_RSSI_STATE_HIGH) ||
+ (btRssiState1 == BT_RSSI_STATE_STAY_HIGH)) {
+ RTPRINT(FBT, BT_TRACE, ("Wifi rssi-1 high \n"));
+ /* sw mechanism */
+ btdm_2AntAgcTable(padapter, true);
+ btdm_2AntAdcBackOff(padapter, true);
+ btdm_2AntDacSwing(padapter, false, 0xc0);
+ } else {
+ RTPRINT(FBT, BT_TRACE, ("Wifi rssi-1 low \n"));
+ /* sw mechanism */
+ btdm_2AntAgcTable(padapter, false);
+ btdm_2AntAdcBackOff(padapter, false);
+ btdm_2AntDacSwing(padapter, false, 0xc0);
+ }
+ }
+}
+
+static void btdm_2Ant8723AA2dp(struct rtw_adapter *padapter)
+{
+ struct hal_data_8723a *pHalData = GET_HAL_DATA(padapter);
+ u8 btRssiState, btRssiState1, btInfoExt;
+
+ btInfoExt = pHalData->bt_coexist.halCoex8723.btInfoExt;
+
+ if (btdm_NeedToDecBtPwr(padapter))
+ btdm_2AntDecBtPwr(padapter, true);
+ else
+ btdm_2AntDecBtPwr(padapter, false);
+ /* coex table */
+ btdm_2AntCoexTable(padapter, 0x55555555, 0xffff, 0x3);
+ btdm_2AntIgnoreWlanAct(padapter, false);
+
+ if (BTDM_IsHT40(padapter)) {
+ RTPRINT(FBT, BT_TRACE, ("HT40\n"));
+ btRssiState = BTDM_CheckCoexRSSIState(padapter, 2, 37, 0);
+ /* fw mechanism */
+ if ((btRssiState == BT_RSSI_STATE_HIGH) ||
+ (btRssiState == BT_RSSI_STATE_STAY_HIGH)) {
+ RTPRINT(FBT, BT_TRACE, ("Wifi rssi-1 high \n"));
+ PlatformEFIOWrite1Byte(padapter, 0x883, 0x40);
+ btdm_2AntTdmaDurationAdjust(padapter, false, false, 1);
+ } else {
+ RTPRINT(FBT, BT_TRACE, ("Wifi rssi-1 low \n"));
+ btdm_2AntTdmaDurationAdjust(padapter, false, true, 1);
+ }
+
+ /* sw mechanism */
+ btdm_2AntAgcTable(padapter, false);
+ btdm_2AntAdcBackOff(padapter, true);
+ btdm_2AntDacSwing(padapter, false, 0xc0);
+ } else {
+ RTPRINT(FBT, BT_TRACE, ("HT20 or Legacy\n"));
+ btRssiState = BTDM_CheckCoexRSSIState(padapter, 2, 47, 0);
+ btRssiState1 = BTDM_CheckCoexRSSIState1(padapter, 2, 27, 0);
+
+ /* fw mechanism */
+ if ((btRssiState1 == BT_RSSI_STATE_HIGH) ||
+ (btRssiState1 == BT_RSSI_STATE_STAY_HIGH)) {
+ RTPRINT(FBT, BT_TRACE, ("Wifi rssi-1 high \n"));
+ PlatformEFIOWrite1Byte(padapter, 0x883, 0x40);
+ btdm_2AntTdmaDurationAdjust(padapter, false, false, 1);
+ } else {
+ RTPRINT(FBT, BT_TRACE, ("Wifi rssi-1 low \n"));
+ btdm_2AntTdmaDurationAdjust(padapter, false, true, 1);
+ }
+
+ /* sw mechanism */
+ if ((btRssiState == BT_RSSI_STATE_HIGH) ||
+ (btRssiState == BT_RSSI_STATE_STAY_HIGH)) {
+ RTPRINT(FBT, BT_TRACE, ("Wifi rssi high \n"));
+ btdm_2AntAgcTable(padapter, true);
+ btdm_2AntAdcBackOff(padapter, true);
+ btdm_2AntDacSwing(padapter, false, 0xc0);
+ } else {
+ RTPRINT(FBT, BT_TRACE, ("Wifi rssi low \n"));
+ btdm_2AntAgcTable(padapter, false);
+ btdm_2AntAdcBackOff(padapter, false);
+ btdm_2AntDacSwing(padapter, false, 0xc0);
+ }
+ }
+}
+
+/* extern function start with BTDM_ */
+static void BTDM_2AntParaInit(struct rtw_adapter *padapter)
+{
+
+ struct hal_data_8723a *pHalData = GET_HAL_DATA(padapter);
+ struct btdm_8723a_2ant *pBtdm8723 = &pHalData->bt_coexist.halCoex8723.btdm2Ant;
+
+ RTPRINT(FBT, BT_TRACE, ("[BTCoex], 2Ant Parameter Init!!\n"));
+
+ /* Enable counter statistics */
+ rtl8723au_write8(padapter, 0x76e, 0x4);
+ rtl8723au_write8(padapter, 0x778, 0x3);
+ rtl8723au_write8(padapter, 0x40, 0x20);
+
+ /* force to reset coex mechanism */
+ pBtdm8723->preVal0x6c0 = 0x0;
+ btdm_2AntCoexTable(padapter, 0x55555555, 0xffff, 0x3);
+
+ pBtdm8723->bPrePsTdmaOn = true;
+ btdm_2AntPsTdma(padapter, false, 0);
+
+ pBtdm8723->preFwDacSwingLvl = 0x10;
+ btdm_2AntFwDacSwingLvl(padapter, 0x20);
+
+ pBtdm8723->bPreDecBtPwr = true;
+ btdm_2AntDecBtPwr(padapter, false);
+
+ pBtdm8723->bPreAgcTableEn = true;
+ btdm_2AntAgcTable(padapter, false);
+
+ pBtdm8723->bPreAdcBackOff = true;
+ btdm_2AntAdcBackOff(padapter, false);
+
+ pBtdm8723->bPreLowPenaltyRa = true;
+ btdm_2AntLowPenaltyRa(padapter, false);
+
+ pBtdm8723->bPreRfRxLpfShrink = true;
+ btdm_2AntRfShrink(padapter, false);
+
+ pBtdm8723->bPreDacSwingOn = true;
+ btdm_2AntDacSwing(padapter, false, 0xc0);
+
+ pBtdm8723->bPreIgnoreWlanAct = true;
+ btdm_2AntIgnoreWlanAct(padapter, false);
+}
+
+static void BTDM_2AntHwCoexAllOff8723A(struct rtw_adapter *padapter)
+{
+ btdm_2AntCoexTable(padapter, 0x55555555, 0xffff, 0x3);
+}
+
+static void BTDM_2AntFwCoexAllOff8723A(struct rtw_adapter *padapter)
+{
+ btdm_2AntIgnoreWlanAct(padapter, false);
+ btdm_2AntPsTdma(padapter, false, 0);
+ btdm_2AntFwDacSwingLvl(padapter, 0x20);
+ btdm_2AntDecBtPwr(padapter, false);
+}
+
+static void BTDM_2AntSwCoexAllOff8723A(struct rtw_adapter *padapter)
+{
+ btdm_2AntAgcTable(padapter, false);
+ btdm_2AntAdcBackOff(padapter, false);
+ btdm_2AntLowPenaltyRa(padapter, false);
+ btdm_2AntRfShrink(padapter, false);
+ btdm_2AntDacSwing(padapter, false, 0xc0);
+}
+
+static void BTDM_2AntFwC2hBtInfo8723A(struct rtw_adapter *padapter)
+{
+ struct bt_30info *pBTInfo = GET_BT_INFO(padapter);
+ struct bt_mgnt *pBtMgnt = &pBTInfo->BtMgnt;
+ struct hal_data_8723a *pHalData = GET_HAL_DATA(padapter);
+ struct btdm_8723a_2ant *pBtdm8723 = &pHalData->bt_coexist.halCoex8723.btdm2Ant;
+ u8 btInfo = 0;
+ u8 algorithm = BT_2ANT_COEX_ALGO_UNDEFINED;
+ u8 bBtLinkExist = false, bBtHsModeExist = false;
+
+ btInfo = pHalData->bt_coexist.halCoex8723.c2hBtInfoOriginal;
+ pBtdm8723->btStatus = BT_2ANT_BT_STATUS_IDLE;
+
+ /* check BIT2 first ==> check if bt is under inquiry or page scan */
+ if (btInfo & BIT(2)) {
+ if (!pHalData->bt_coexist.halCoex8723.bC2hBtInquiryPage) {
+ pBtMgnt->ExtConfig.bHoldForBtOperation = true;
+ pBtMgnt->ExtConfig.bHoldPeriodCnt = 1;
+ btdm_2AntBtInquiryPage(padapter);
+ } else {
+ pBtMgnt->ExtConfig.bHoldPeriodCnt++;
+ btdm_HoldForBtInqPage(padapter);
+ }
+ pHalData->bt_coexist.halCoex8723.bC2hBtInquiryPage = true;
+
+ } else {
+ pHalData->bt_coexist.halCoex8723.bC2hBtInquiryPage = false;
+ pBtMgnt->ExtConfig.bHoldForBtOperation = false;
+ pBtMgnt->ExtConfig.bHoldPeriodCnt = 0;
+
+ }
+ RTPRINT(FBT, BT_TRACE,
+ ("[BTC2H], pHalData->bt_coexist.halCoex8723.bC2hBtInquiryPage =%x pBtMgnt->ExtConfig.bHoldPeriodCnt =%x pBtMgnt->ExtConfig.bHoldForBtOperation =%x\n",
+ pHalData->bt_coexist.halCoex8723.bC2hBtInquiryPage,
+ pBtMgnt->ExtConfig.bHoldPeriodCnt,
+ pBtMgnt->ExtConfig.bHoldForBtOperation));
+
+ RTPRINT(FBT, BT_TRACE,
+ ("[BTC2H], btInfo =%x pHalData->bt_coexist.halCoex8723.c2hBtInfoOriginal =%x\n",
+ btInfo, pHalData->bt_coexist.halCoex8723.c2hBtInfoOriginal));
+ if (btInfo&BT_INFO_ACL) {
+ RTPRINT(FBT, BT_TRACE, ("[BTC2H], BTInfo: bConnect = true btInfo =%x\n", btInfo));
+ bBtLinkExist = true;
+ if (((btInfo&(BT_INFO_FTP|BT_INFO_A2DP|BT_INFO_HID|BT_INFO_SCO_BUSY)) != 0) ||
+ pHalData->bt_coexist.halCoex8723.btRetryCnt > 0) {
+ pBtdm8723->btStatus = BT_2ANT_BT_STATUS_NON_IDLE;
+ } else {
+ pBtdm8723->btStatus = BT_2ANT_BT_STATUS_CONNECTED_IDLE;
+ }
+
+ if (btInfo&BT_INFO_SCO || btInfo&BT_INFO_SCO_BUSY) {
+ if (btInfo&BT_INFO_FTP || btInfo&BT_INFO_A2DP || btInfo&BT_INFO_HID) {
+ switch (btInfo&0xe0) {
+ case BT_INFO_HID:
+ RTPRINT(FBT, BT_TRACE, ("[BTCoex], SCO + HID\n"));
+ algorithm = BT_2ANT_COEX_ALGO_HID;
+ break;
+ case BT_INFO_A2DP:
+ RTPRINT(FBT, BT_TRACE, ("[BTCoex], Error!!! SCO + A2DP\n"));
+ break;
+ case BT_INFO_FTP:
+ if (bBtHsModeExist) {
+ RTPRINT(FBT, BT_TRACE, ("[BTCoex], SCO + PAN(HS)\n"));
+ algorithm = BT_2ANT_COEX_ALGO_SCO;
+ } else {
+ RTPRINT(FBT, BT_TRACE, ("[BTCoex], SCO + PAN(EDR)\n"));
+ algorithm = BT_2ANT_COEX_ALGO_PANEDR_HID;
+ }
+ break;
+ case (BT_INFO_HID | BT_INFO_A2DP):
+ RTPRINT(FBT, BT_TRACE, ("[BTCoex], HID + A2DP\n"));
+ algorithm = BT_2ANT_COEX_ALGO_HID_A2DP;
+ break;
+ case (BT_INFO_HID | BT_INFO_FTP):
+ if (bBtHsModeExist) {
+ RTPRINT(FBT, BT_TRACE, ("[BTCoex], HID + PAN(HS)\n"));
+ algorithm = BT_2ANT_COEX_ALGO_HID_A2DP;
+ } else {
+ RTPRINT(FBT, BT_TRACE, ("[BTCoex], HID + PAN(EDR)\n"));
+ algorithm = BT_2ANT_COEX_ALGO_PANEDR_HID;
+ }
+ break;
+ case (BT_INFO_A2DP | BT_INFO_FTP):
+ if (bBtHsModeExist) {
+ RTPRINT(FBT, BT_TRACE, ("[BTCoex], A2DP + PAN(HS)\n"));
+ algorithm = BT_2ANT_COEX_ALGO_A2DP;
+ } else {
+ RTPRINT(FBT, BT_TRACE, ("[BTCoex], A2DP + PAN(EDR)\n"));
+ algorithm = BT_2ANT_COEX_ALGO_PANEDR_A2DP;
+ }
+ break;
+ case (BT_INFO_HID | BT_INFO_A2DP | BT_INFO_FTP):
+ if (bBtHsModeExist) {
+ RTPRINT(FBT, BT_TRACE, ("[BTCoex], HID + A2DP + PAN(HS)\n"));
+ algorithm = BT_2ANT_COEX_ALGO_HID_A2DP;
+ } else {
+ RTPRINT(FBT, BT_TRACE, ("[BTCoex], HID + A2DP + PAN(EDR)\n"));
+ algorithm = BT_2ANT_COEX_ALGO_HID_A2DP_PANEDR;
+ }
+ break;
+ }
+ } else {
+ RTPRINT(FBT, BT_TRACE, ("[BTCoex], SCO only\n"));
+ algorithm = BT_2ANT_COEX_ALGO_SCO;
+ }
+ } else {
+ RTPRINT(FBT, BT_TRACE, ("[BTCoex], non SCO\n"));
+ switch (btInfo&0xe0) {
+ case BT_INFO_HID:
+ RTPRINT(FBT, BT_TRACE, ("[BTCoex], HID\n"));
+ algorithm = BT_2ANT_COEX_ALGO_HID;
+ break;
+ case BT_INFO_A2DP:
+ RTPRINT(FBT, BT_TRACE, ("[BTCoex], A2DP\n"));
+ algorithm = BT_2ANT_COEX_ALGO_A2DP;
+ break;
+ case BT_INFO_FTP:
+ RTPRINT(FBT, BT_TRACE, ("[BTCoex], PAN(EDR)\n"));
+ algorithm = BT_2ANT_COEX_ALGO_PANEDR_HID;
+ break;
+ case (BT_INFO_HID | BT_INFO_A2DP):
+ RTPRINT(FBT, BT_TRACE, ("[BTCoex], HID + A2DP\n"));
+ algorithm = BT_2ANT_COEX_ALGO_HID_A2DP;
+ break;
+ case (BT_INFO_HID|BT_INFO_FTP):
+ if (bBtHsModeExist) {
+ RTPRINT(FBT, BT_TRACE, ("[BTCoex], HID + PAN(HS)\n"));
+ algorithm = BT_2ANT_COEX_ALGO_HID_A2DP;
+ } else {
+ RTPRINT(FBT, BT_TRACE, ("[BTCoex], HID + PAN(EDR)\n"));
+ algorithm = BT_2ANT_COEX_ALGO_PANEDR_HID;
+ }
+ break;
+ case (BT_INFO_A2DP|BT_INFO_FTP):
+ if (bBtHsModeExist) {
+ RTPRINT(FBT, BT_TRACE, ("[BTCoex], A2DP + PAN(HS)\n"));
+ algorithm = BT_2ANT_COEX_ALGO_A2DP;
+ } else {
+ RTPRINT(FBT, BT_TRACE, ("[BTCoex], A2DP + PAN(EDR)\n"));
+ algorithm = BT_2ANT_COEX_ALGO_PANEDR_A2DP;
+ }
+ break;
+ case (BT_INFO_HID|BT_INFO_A2DP|BT_INFO_FTP):
+ if (bBtHsModeExist) {
+ RTPRINT(FBT, BT_TRACE, ("[BTCoex], HID + A2DP + PAN(HS)\n"));
+ algorithm = BT_2ANT_COEX_ALGO_HID_A2DP;
+ } else {
+ RTPRINT(FBT, BT_TRACE, ("[BTCoex], HID + A2DP + PAN(EDR)\n"));
+ algorithm = BT_2ANT_COEX_ALGO_HID_A2DP_PANEDR;
+ }
+ break;
+ }
+
+ }
+ } else {
+ RTPRINT(FBT, BT_TRACE, ("[BTC2H], BTInfo: bConnect = false\n"));
+ pBtdm8723->btStatus = BT_2ANT_BT_STATUS_IDLE;
+ }
+
+ pBtdm8723->curAlgorithm = algorithm;
+ RTPRINT(FBT, BT_TRACE, ("[BTCoex], Algorithm = %d \n", pBtdm8723->curAlgorithm));
+
+/* From */
+ BTDM_CheckWiFiState(padapter);
+ if (pBtMgnt->ExtConfig.bManualControl) {
+ RTPRINT(FBT, BT_TRACE, ("Action Manual control, won't execute bt coexist mechanism!!\n"));
+ return;
+ }
+}
+
+void BTDM_2AntBtCoexist8723A(struct rtw_adapter *padapter)
+{
+ struct bt_30info *pBTInfo = GET_BT_INFO(padapter);
+ struct bt_mgnt *pBtMgnt = &pBTInfo->BtMgnt;
+ struct bt_dgb *pBtDbg = &pBTInfo->BtDbg;
+ u8 btInfoOriginal = 0;
+ struct hal_data_8723a *pHalData = GET_HAL_DATA(padapter);
+ struct btdm_8723a_2ant *pBtdm8723 = &pHalData->bt_coexist.halCoex8723.btdm2Ant;
+
+ if (BTDM_BtProfileSupport(padapter)) {
+ if (pBtMgnt->ExtConfig.bHoldForBtOperation) {
+ RTPRINT(FBT, BT_TRACE, ("Action for BT Operation adjust!!\n"));
+ return;
+ }
+ if (pBtMgnt->ExtConfig.bHoldPeriodCnt) {
+ RTPRINT(FBT, BT_TRACE, ("Hold BT inquiry/page scan setting (cnt = %d)!!\n",
+ pBtMgnt->ExtConfig.bHoldPeriodCnt));
+ if (pBtMgnt->ExtConfig.bHoldPeriodCnt >= 11) {
+ pBtMgnt->ExtConfig.bHoldPeriodCnt = 0;
+ /* next time the coexist parameters should be reset again. */
+ } else {
+ pBtMgnt->ExtConfig.bHoldPeriodCnt++;
+ }
+ return;
+ }
+
+ if (pBtDbg->dbgCtrl)
+ RTPRINT(FBT, BT_TRACE, ("[Dbg control], "));
+
+ pBtdm8723->curAlgorithm = btdm_ActionAlgorithm(padapter);
+ RTPRINT(FBT, BT_TRACE, ("[BTCoex], Algorithm = %d \n", pBtdm8723->curAlgorithm));
+
+ if (btdm_Is2Ant8723ACommonAction(padapter)) {
+ RTPRINT(FBT, BT_TRACE, ("Action 2-Ant common.\n"));
+ pBtdm8723->bResetTdmaAdjust = true;
+ } else {
+ if (pBtdm8723->curAlgorithm != pBtdm8723->preAlgorithm) {
+ RTPRINT(FBT, BT_TRACE, ("[BTCoex], preAlgorithm =%d, curAlgorithm =%d\n",
+ pBtdm8723->preAlgorithm, pBtdm8723->curAlgorithm));
+ pBtdm8723->bResetTdmaAdjust = true;
+ }
+ switch (pBtdm8723->curAlgorithm) {
+ case BT_2ANT_COEX_ALGO_SCO:
+ RTPRINT(FBT, BT_TRACE, ("Action 2-Ant, algorithm = SCO.\n"));
+ btdm_2Ant8723ASCOAction(padapter);
+ break;
+ case BT_2ANT_COEX_ALGO_HID:
+ RTPRINT(FBT, BT_TRACE, ("Action 2-Ant, algorithm = HID.\n"));
+ btdm_2Ant8723AHIDAction(padapter);
+ break;
+ case BT_2ANT_COEX_ALGO_A2DP:
+ RTPRINT(FBT, BT_TRACE, ("Action 2-Ant, algorithm = A2DP.\n"));
+ btdm_2Ant8723AA2DPAction(padapter);
+ break;
+ case BT_2ANT_COEX_ALGO_PANEDR:
+ RTPRINT(FBT, BT_TRACE, ("Action 2-Ant, algorithm = PAN(EDR).\n"));
+ btdm_2Ant8723APANEDRAction(padapter);
+ break;
+ case BT_2ANT_COEX_ALGO_PANHS:
+ RTPRINT(FBT, BT_TRACE, ("Action 2-Ant, algorithm = HS mode.\n"));
+ btdm_2Ant8723APANHSAction(padapter);
+ break;
+ case BT_2ANT_COEX_ALGO_PANEDR_A2DP:
+ RTPRINT(FBT, BT_TRACE, ("Action 2-Ant, algorithm = PAN+A2DP.\n"));
+ btdm_2Ant8723APANEDRA2DPAction(padapter);
+ break;
+ case BT_2ANT_COEX_ALGO_PANEDR_HID:
+ RTPRINT(FBT, BT_TRACE, ("Action 2-Ant, algorithm = PAN(EDR)+HID.\n"));
+ btdm_2Ant8723APANEDRHIDAction(padapter);
+ break;
+ case BT_2ANT_COEX_ALGO_HID_A2DP_PANEDR:
+ RTPRINT(FBT, BT_TRACE, ("Action 2-Ant, algorithm = HID+A2DP+PAN.\n"));
+ btdm_2Ant8723AHIDA2DPPANEDRAction(padapter);
+ break;
+ case BT_2ANT_COEX_ALGO_HID_A2DP:
+ RTPRINT(FBT, BT_TRACE, ("Action 2-Ant, algorithm = HID+A2DP.\n"));
+ btdm_2Ant8723AHIDA2DPAction(padapter);
+ break;
+ default:
+ RTPRINT(FBT, BT_TRACE, ("Action 2-Ant, algorithm = 0.\n"));
+ btdm_2Ant8723AA2DPAction(padapter);
+ break;
+ }
+ pBtdm8723->preAlgorithm = pBtdm8723->curAlgorithm;
+ }
+ } else {
+ RTPRINT(FBT, BT_TRACE, ("[BTCoex] Get bt info by fw!!\n"));
+ /* msg shows c2h rsp for bt_info is received or not. */
+ if (pHalData->bt_coexist.halCoex8723.bC2hBtInfoReqSent)
+ RTPRINT(FBT, BT_TRACE, ("[BTCoex] c2h for btInfo not rcvd yet!!\n"));
+
+ btInfoOriginal = pHalData->bt_coexist.halCoex8723.c2hBtInfoOriginal;
+
+ if (pBtMgnt->ExtConfig.bHoldForBtOperation) {
+ RTPRINT(FBT, BT_TRACE, ("Action for BT Operation adjust!!\n"));
+ return;
+ }
+ if (pBtMgnt->ExtConfig.bHoldPeriodCnt) {
+ RTPRINT(FBT, BT_TRACE,
+ ("Hold BT inquiry/page scan setting (cnt = %d)!!\n",
+ pBtMgnt->ExtConfig.bHoldPeriodCnt));
+ if (pBtMgnt->ExtConfig.bHoldPeriodCnt >= 11) {
+ pBtMgnt->ExtConfig.bHoldPeriodCnt = 0;
+ /* next time the coexist parameters should be reset again. */
+ } else {
+ pBtMgnt->ExtConfig.bHoldPeriodCnt++;
+ }
+ return;
+ }
+
+ if (pBtDbg->dbgCtrl)
+ RTPRINT(FBT, BT_TRACE, ("[Dbg control], "));
+ if (btdm_Is2Ant8723ACommonAction(padapter)) {
+ RTPRINT(FBT, BT_TRACE, ("Action 2-Ant common.\n"));
+ pBtdm8723->bResetTdmaAdjust = true;
+ } else {
+ if (pBtdm8723->curAlgorithm != pBtdm8723->preAlgorithm) {
+ RTPRINT(FBT, BT_TRACE,
+ ("[BTCoex], preAlgorithm =%d, curAlgorithm =%d\n",
+ pBtdm8723->preAlgorithm,
+ pBtdm8723->curAlgorithm));
+ pBtdm8723->bResetTdmaAdjust = true;
+ }
+ switch (pBtdm8723->curAlgorithm) {
+ case BT_2ANT_COEX_ALGO_SCO:
+ RTPRINT(FBT, BT_TRACE, ("Action 2-Ant, algorithm = SCO.\n"));
+ btdm_2Ant8723ASCOAction(padapter);
+ break;
+ case BT_2ANT_COEX_ALGO_HID:
+ RTPRINT(FBT, BT_TRACE, ("Action 2-Ant, algorithm = HID.\n"));
+ btdm_2Ant8723AHIDAction(padapter);
+ break;
+ case BT_2ANT_COEX_ALGO_A2DP:
+ RTPRINT(FBT, BT_TRACE, ("Action 2-Ant, algorithm = A2DP.\n"));
+ btdm_2Ant8723AA2dp(padapter);
+ break;
+ case BT_2ANT_COEX_ALGO_PANEDR:
+ RTPRINT(FBT, BT_TRACE, ("Action 2-Ant, algorithm = PAN(EDR).\n"));
+ btdm_2Ant8723APANEDRAction(padapter);
+ break;
+ case BT_2ANT_COEX_ALGO_PANHS:
+ RTPRINT(FBT, BT_TRACE, ("Action 2-Ant, algorithm = HS mode.\n"));
+ btdm_2Ant8723APANHSAction(padapter);
+ break;
+ case BT_2ANT_COEX_ALGO_PANEDR_A2DP:
+ RTPRINT(FBT, BT_TRACE, ("Action 2-Ant, algorithm = PAN+A2DP.\n"));
+ btdm_2Ant8723APANEDRA2DPAction(padapter);
+ break;
+ case BT_2ANT_COEX_ALGO_PANEDR_HID:
+ RTPRINT(FBT, BT_TRACE, ("Action 2-Ant, algorithm = PAN(EDR)+HID.\n"));
+ btdm_2Ant8723APANEDRHIDAction(padapter);
+ break;
+ case BT_2ANT_COEX_ALGO_HID_A2DP_PANEDR:
+ RTPRINT(FBT, BT_TRACE, ("Action 2-Ant, algorithm = HID+A2DP+PAN.\n"));
+ btdm_2Ant8723AHIDA2DPPANEDRAction(padapter);
+ break;
+ case BT_2ANT_COEX_ALGO_HID_A2DP:
+ RTPRINT(FBT, BT_TRACE, ("Action 2-Ant, algorithm = HID+A2DP.\n"));
+ btdm_2Ant8723AHIDA2DPAction(padapter);
+ break;
+ default:
+ RTPRINT(FBT, BT_TRACE, ("Action 2-Ant, algorithm = 0.\n"));
+ btdm_2Ant8723AA2DPAction(padapter);
+ break;
+ }
+ pBtdm8723->preAlgorithm = pBtdm8723->curAlgorithm;
+ }
+ }
+}
+
+/* ===== End of sync from SD7 driver HAL/BTCoexist/HalBtc87232Ant.c ===== */
+
+/* ===== Below this line is sync from SD7 driver HAL/BTCoexist/HalBtc8723.c ===== */
+
+static u8 btCoexDbgBuf[BT_TMP_BUF_SIZE];
+
+static const char *const BtProfileString[] = {
+ "NONE",
+ "A2DP",
+ "PAN",
+ "HID",
+ "SCO",
+};
+
+static const char *const BtSpecString[] = {
+ "1.0b",
+ "1.1",
+ "1.2",
+ "2.0+EDR",
+ "2.1+EDR",
+ "3.0+HS",
+ "4.0",
+};
+
+static const char *const BtLinkRoleString[] = {
+ "Master",
+ "Slave",
+};
+
+static u8 btdm_BtWifiAntNum(struct rtw_adapter *padapter)
+{
+ struct hal_data_8723a *pHalData = GET_HAL_DATA(padapter);
+ struct bt_coexist_8723a *pBtCoex = &pHalData->bt_coexist.halCoex8723;
+
+ if (Ant_x2 == pHalData->bt_coexist.BT_Ant_Num) {
+ if (Ant_x2 == pBtCoex->TotalAntNum)
+ return Ant_x2;
+ else
+ return Ant_x1;
+ } else {
+ return Ant_x1;
+ }
+ return Ant_x2;
+}
+
+static void btdm_BtHwCountersMonitor(struct rtw_adapter *padapter)
+{
+ struct hal_data_8723a *pHalData = GET_HAL_DATA(padapter);
+ u32 regHPTxRx, regLPTxRx, u4Tmp;
+ u32 regHPTx = 0, regHPRx = 0, regLPTx = 0, regLPRx = 0;
+
+ regHPTxRx = REG_HIGH_PRIORITY_TXRX;
+ regLPTxRx = REG_LOW_PRIORITY_TXRX;
+
+ u4Tmp = rtl8723au_read32(padapter, regHPTxRx);
+ regHPTx = u4Tmp & bMaskLWord;
+ regHPRx = (u4Tmp & bMaskHWord)>>16;
+
+ u4Tmp = rtl8723au_read32(padapter, regLPTxRx);
+ regLPTx = u4Tmp & bMaskLWord;
+ regLPRx = (u4Tmp & bMaskHWord)>>16;
+
+ pHalData->bt_coexist.halCoex8723.highPriorityTx = regHPTx;
+ pHalData->bt_coexist.halCoex8723.highPriorityRx = regHPRx;
+ pHalData->bt_coexist.halCoex8723.lowPriorityTx = regLPTx;
+ pHalData->bt_coexist.halCoex8723.lowPriorityRx = regLPRx;
+
+ RTPRINT(FBT, BT_TRACE, ("High Priority Tx/Rx = %d / %d\n", regHPTx, regHPRx));
+ RTPRINT(FBT, BT_TRACE, ("Low Priority Tx/Rx = %d / %d\n", regLPTx, regLPRx));
+
+ /* reset counter */
+ rtl8723au_write8(padapter, 0x76e, 0xc);
+}
+
+/* This function check if 8723 bt is disabled */
+static void btdm_BtEnableDisableCheck8723A(struct rtw_adapter *padapter)
+{
+ u8 btAlife = true;
+ struct hal_data_8723a *pHalData = GET_HAL_DATA(padapter);
+
+#ifdef CHECK_BT_EXIST_FROM_REG
+ u8 val8;
+
+ /* ox68[28]= 1 => BT enable; otherwise disable */
+ val8 = rtl8723au_read8(padapter, 0x6B);
+ if (!(val8 & BIT(4)))
+ btAlife = false;
+
+ if (btAlife)
+ pHalData->bt_coexist.bCurBtDisabled = false;
+ else
+ pHalData->bt_coexist.bCurBtDisabled = true;
+#else
+ if (pHalData->bt_coexist.halCoex8723.highPriorityTx == 0 &&
+ pHalData->bt_coexist.halCoex8723.highPriorityRx == 0 &&
+ pHalData->bt_coexist.halCoex8723.lowPriorityTx == 0 &&
+ pHalData->bt_coexist.halCoex8723.lowPriorityRx == 0)
+ btAlife = false;
+ if (pHalData->bt_coexist.halCoex8723.highPriorityTx == 0xeaea &&
+ pHalData->bt_coexist.halCoex8723.highPriorityRx == 0xeaea &&
+ pHalData->bt_coexist.halCoex8723.lowPriorityTx == 0xeaea &&
+ pHalData->bt_coexist.halCoex8723.lowPriorityRx == 0xeaea)
+ btAlife = false;
+ if (pHalData->bt_coexist.halCoex8723.highPriorityTx == 0xffff &&
+ pHalData->bt_coexist.halCoex8723.highPriorityRx == 0xffff &&
+ pHalData->bt_coexist.halCoex8723.lowPriorityTx == 0xffff &&
+ pHalData->bt_coexist.halCoex8723.lowPriorityRx == 0xffff)
+ btAlife = false;
+ if (btAlife) {
+ pHalData->bt_coexist.btActiveZeroCnt = 0;
+ pHalData->bt_coexist.bCurBtDisabled = false;
+ RTPRINT(FBT, BT_TRACE, ("8723A BT is enabled !!\n"));
+ } else {
+ pHalData->bt_coexist.btActiveZeroCnt++;
+ RTPRINT(FBT, BT_TRACE, ("8723A bt all counters = 0, %d times!!\n",
+ pHalData->bt_coexist.btActiveZeroCnt));
+ if (pHalData->bt_coexist.btActiveZeroCnt >= 2) {
+ pHalData->bt_coexist.bCurBtDisabled = true;
+ RTPRINT(FBT, BT_TRACE, ("8723A BT is disabled !!\n"));
+ }
+ }
+#endif
+
+ if (!pHalData->bt_coexist.bCurBtDisabled) {
+ if (BTDM_IsWifiConnectionExist(padapter))
+ BTDM_SetFwChnlInfo(padapter, RT_MEDIA_CONNECT);
+ else
+ BTDM_SetFwChnlInfo(padapter, RT_MEDIA_DISCONNECT);
+ }
+
+ if (pHalData->bt_coexist.bPreBtDisabled !=
+ pHalData->bt_coexist.bCurBtDisabled) {
+ RTPRINT(FBT, BT_TRACE, ("8723A BT is from %s to %s!!\n",
+ (pHalData->bt_coexist.bPreBtDisabled ? "disabled":"enabled"),
+ (pHalData->bt_coexist.bCurBtDisabled ? "disabled":"enabled")));
+ pHalData->bt_coexist.bPreBtDisabled = pHalData->bt_coexist.bCurBtDisabled;
+ }
+}
+
+static void btdm_BTCoexist8723AHandler(struct rtw_adapter *padapter)
+{
+ struct hal_data_8723a *pHalData;
+
+ pHalData = GET_HAL_DATA(padapter);
+
+ if (btdm_BtWifiAntNum(padapter) == Ant_x2) {
+ RTPRINT(FBT, BT_TRACE, ("[BTCoex], 2 Ant mechanism\n"));
+ BTDM_2AntBtCoexist8723A(padapter);
+ } else {
+ RTPRINT(FBT, BT_TRACE, ("[BTCoex], 1 Ant mechanism\n"));
+ BTDM_1AntBtCoexist8723A(padapter);
+ }
+
+ if (!BTDM_IsSameCoexistState(padapter)) {
+ RTPRINT(FBT, BT_TRACE, ("[BTCoex], Coexist State[bitMap] change from 0x%"i64fmt"x to 0x%"i64fmt"x\n",
+ pHalData->bt_coexist.PreviousState,
+ pHalData->bt_coexist.CurrentState));
+ pHalData->bt_coexist.PreviousState = pHalData->bt_coexist.CurrentState;
+
+ RTPRINT(FBT, BT_TRACE, ("["));
+ if (pHalData->bt_coexist.CurrentState & BT_COEX_STATE_BT30)
+ RTPRINT(FBT, BT_TRACE, ("BT 3.0, "));
+ if (pHalData->bt_coexist.CurrentState & BT_COEX_STATE_WIFI_HT20)
+ RTPRINT(FBT, BT_TRACE, ("HT20, "));
+ if (pHalData->bt_coexist.CurrentState & BT_COEX_STATE_WIFI_HT40)
+ RTPRINT(FBT, BT_TRACE, ("HT40, "));
+ if (pHalData->bt_coexist.CurrentState & BT_COEX_STATE_WIFI_LEGACY)
+ RTPRINT(FBT, BT_TRACE, ("Legacy, "));
+ if (pHalData->bt_coexist.CurrentState & BT_COEX_STATE_WIFI_RSSI_LOW)
+ RTPRINT(FBT, BT_TRACE, ("Rssi_Low, "));
+ if (pHalData->bt_coexist.CurrentState & BT_COEX_STATE_WIFI_RSSI_MEDIUM)
+ RTPRINT(FBT, BT_TRACE, ("Rssi_Mid, "));
+ if (pHalData->bt_coexist.CurrentState & BT_COEX_STATE_WIFI_RSSI_HIGH)
+ RTPRINT(FBT, BT_TRACE, ("Rssi_High, "));
+ if (pHalData->bt_coexist.CurrentState & BT_COEX_STATE_WIFI_IDLE)
+ RTPRINT(FBT, BT_TRACE, ("Wifi_Idle, "));
+ if (pHalData->bt_coexist.CurrentState & BT_COEX_STATE_WIFI_UPLINK)
+ RTPRINT(FBT, BT_TRACE, ("Wifi_Uplink, "));
+ if (pHalData->bt_coexist.CurrentState & BT_COEX_STATE_WIFI_DOWNLINK)
+ RTPRINT(FBT, BT_TRACE, ("Wifi_Downlink, "));
+ if (pHalData->bt_coexist.CurrentState & BT_COEX_STATE_BT_IDLE)
+ RTPRINT(FBT, BT_TRACE, ("BT_idle, "));
+ if (pHalData->bt_coexist.CurrentState & BT_COEX_STATE_PROFILE_HID)
+ RTPRINT(FBT, BT_TRACE, ("PRO_HID, "));
+ if (pHalData->bt_coexist.CurrentState & BT_COEX_STATE_PROFILE_A2DP)
+ RTPRINT(FBT, BT_TRACE, ("PRO_A2DP, "));
+ if (pHalData->bt_coexist.CurrentState & BT_COEX_STATE_PROFILE_PAN)
+ RTPRINT(FBT, BT_TRACE, ("PRO_PAN, "));
+ if (pHalData->bt_coexist.CurrentState & BT_COEX_STATE_PROFILE_SCO)
+ RTPRINT(FBT, BT_TRACE, ("PRO_SCO, "));
+ RTPRINT(FBT, BT_TRACE, ("]\n"));
+ }
+}
+
+/* extern function start with BTDM_ */
+u32 BTDM_BtTxRxCounterH(struct rtw_adapter *padapter)
+{
+ struct hal_data_8723a *pHalData = GET_HAL_DATA(padapter);
+ u32 counters = 0;
+
+ counters = pHalData->bt_coexist.halCoex8723.highPriorityTx+
+ pHalData->bt_coexist.halCoex8723.highPriorityRx;
+ return counters;
+}
+
+u32 BTDM_BtTxRxCounterL(struct rtw_adapter *padapter)
+{
+ struct hal_data_8723a *pHalData = GET_HAL_DATA(padapter);
+ u32 counters = 0;
+
+ counters = pHalData->bt_coexist.halCoex8723.lowPriorityTx+
+ pHalData->bt_coexist.halCoex8723.lowPriorityRx;
+ return counters;
+}
+
+void BTDM_SetFwChnlInfo(struct rtw_adapter *padapter, enum rt_media_status mstatus)
+{
+ struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv;
+ struct bt_30info *pBTInfo = GET_BT_INFO(padapter);
+ struct bt_mgnt *pBtMgnt = &pBTInfo->BtMgnt;
+ u8 H2C_Parameter[3] = {0};
+ u8 chnl;
+
+ /* opMode */
+ if (RT_MEDIA_CONNECT == mstatus)
+ H2C_Parameter[0] = 0x1; /* 0: disconnected, 1:connected */
+
+ if (check_fwstate(&padapter->mlmepriv, WIFI_ASOC_STATE)) {
+ /* channel */
+ chnl = pmlmeext->cur_channel;
+ if (BTDM_IsHT40(padapter)) {
+ if (pmlmeext->cur_ch_offset == HAL_PRIME_CHNL_OFFSET_UPPER)
+ chnl -= 2;
+ else if (pmlmeext->cur_ch_offset == HAL_PRIME_CHNL_OFFSET_LOWER)
+ chnl += 2;
+ }
+ H2C_Parameter[1] = chnl;
+ } else { /* check if HS link is exists */
+ /* channel */
+ if (BT_Operation(padapter))
+ H2C_Parameter[1] = pBtMgnt->BTChannel;
+ else
+ H2C_Parameter[1] = pmlmeext->cur_channel;
+ }
+
+ if (BTDM_IsHT40(padapter))
+ H2C_Parameter[2] = 0x30;
+ else
+ H2C_Parameter[2] = 0x20;
+
+ FillH2CCmd(padapter, 0x19, 3, H2C_Parameter);
+}
+
+u8 BTDM_IsWifiConnectionExist(struct rtw_adapter *padapter)
+{
+ u8 bRet = false;
+
+ if (BTHCI_HsConnectionEstablished(padapter))
+ bRet = true;
+
+ if (check_fwstate(&padapter->mlmepriv, WIFI_ASOC_STATE) == true)
+ bRet = true;
+
+ return bRet;
+}
+
+void BTDM_SetFw3a(
+ struct rtw_adapter *padapter,
+ u8 byte1,
+ u8 byte2,
+ u8 byte3,
+ u8 byte4,
+ u8 byte5
+ )
+{
+ u8 H2C_Parameter[5] = {0};
+
+ if (rtl8723a_BT_using_antenna_1(padapter)) {
+ if ((!check_fwstate(&padapter->mlmepriv, WIFI_STATION_STATE)) &&
+ (get_fwstate(&padapter->mlmepriv) != WIFI_NULL_STATE)) {
+ /* for softap mode */
+ struct hal_data_8723a *pHalData = GET_HAL_DATA(padapter);
+ struct bt_coexist_8723a *pBtCoex = &pHalData->bt_coexist.halCoex8723;
+ u8 BtState = pBtCoex->c2hBtInfo;
+
+ if ((BtState != BT_INFO_STATE_NO_CONNECTION) &&
+ (BtState != BT_INFO_STATE_CONNECT_IDLE)) {
+ if (byte1 & BIT(4)) {
+ byte1 &= ~BIT(4);
+ byte1 |= BIT(5);
+ }
+
+ byte5 |= BIT(5);
+ if (byte5 & BIT(6))
+ byte5 &= ~BIT(6);
+ }
+ }
+ }
+
+ H2C_Parameter[0] = byte1;
+ H2C_Parameter[1] = byte2;
+ H2C_Parameter[2] = byte3;
+ H2C_Parameter[3] = byte4;
+ H2C_Parameter[4] = byte5;
+
+ RTPRINT(FBT, BT_TRACE, ("[BTCoex], FW write 0x3a(5bytes) = 0x%02x%08x\n",
+ H2C_Parameter[0],
+ H2C_Parameter[1]<<24|H2C_Parameter[2]<<16|H2C_Parameter[3]<<8|H2C_Parameter[4]));
+
+ FillH2CCmd(padapter, 0x3a, 5, H2C_Parameter);
+}
+
+void BTDM_QueryBtInformation(struct rtw_adapter *padapter)
+{
+ u8 H2C_Parameter[1] = {0};
+ struct hal_data_8723a *pHalData;
+ struct bt_coexist_8723a *pBtCoex;
+
+ pHalData = GET_HAL_DATA(padapter);
+ pBtCoex = &pHalData->bt_coexist.halCoex8723;
+
+ if (!rtl8723a_BT_enabled(padapter)) {
+ pBtCoex->c2hBtInfo = BT_INFO_STATE_DISABLED;
+ pBtCoex->bC2hBtInfoReqSent = false;
+ return;
+ }
+
+ if (pBtCoex->c2hBtInfo == BT_INFO_STATE_DISABLED)
+ pBtCoex->c2hBtInfo = BT_INFO_STATE_NO_CONNECTION;
+
+ if (pBtCoex->bC2hBtInfoReqSent == true)
+ RTPRINT(FBT, BT_TRACE, ("[BTCoex], didn't recv previous BtInfo report!\n"));
+ else
+ pBtCoex->bC2hBtInfoReqSent = true;
+
+ H2C_Parameter[0] |= BIT(0); /* trigger */
+
+/*RTPRINT(FBT, BT_TRACE, ("[BTCoex], Query Bt information, write 0x38 = 0x%x\n", */
+/*H2C_Parameter[0])); */
+
+ FillH2CCmd(padapter, 0x38, 1, H2C_Parameter);
+}
+
+void BTDM_SetSwRfRxLpfCorner(struct rtw_adapter *padapter, u8 type)
+{
+ struct hal_data_8723a *pHalData = GET_HAL_DATA(padapter);
+
+ if (BT_RF_RX_LPF_CORNER_SHRINK == type) {
+ /* Shrink RF Rx LPF corner */
+ RTPRINT(FBT, BT_TRACE, ("Shrink RF Rx LPF corner!!\n"));
+ PHY_SetRFReg(padapter, PathA, 0x1e, bRFRegOffsetMask, 0xf0ff7);
+ pHalData->bt_coexist.bSWCoexistAllOff = false;
+ } else if (BT_RF_RX_LPF_CORNER_RESUME == type) {
+ /* Resume RF Rx LPF corner */
+ RTPRINT(FBT, BT_TRACE, ("Resume RF Rx LPF corner!!\n"));
+ PHY_SetRFReg(padapter, PathA, 0x1e, bRFRegOffsetMask, pHalData->bt_coexist.BtRfRegOrigin1E);
+ }
+}
+
+void
+BTDM_SetSwPenaltyTxRateAdaptive(
+ struct rtw_adapter *padapter,
+ u8 raType
+ )
+{
+ struct hal_data_8723a *pHalData = GET_HAL_DATA(padapter);
+ u8 tmpU1;
+
+ tmpU1 = rtl8723au_read8(padapter, 0x4fd);
+ tmpU1 |= BIT(0);
+ if (BT_TX_RATE_ADAPTIVE_LOW_PENALTY == raType) {
+ tmpU1 &= ~BIT(2);
+ pHalData->bt_coexist.bSWCoexistAllOff = false;
+ } else if (BT_TX_RATE_ADAPTIVE_NORMAL == raType) {
+ tmpU1 |= BIT(2);
+ }
+
+ rtl8723au_write8(padapter, 0x4fd, tmpU1);
+}
+
+void BTDM_SetFwDecBtPwr(struct rtw_adapter *padapter, u8 bDecBtPwr)
+{
+ struct hal_data_8723a *pHalData = GET_HAL_DATA(padapter);
+ u8 H2C_Parameter[1] = {0};
+
+ H2C_Parameter[0] = 0;
+
+ if (bDecBtPwr) {
+ H2C_Parameter[0] |= BIT(1);
+ pHalData->bt_coexist.bFWCoexistAllOff = false;
+ }
+
+ RTPRINT(FBT, BT_TRACE, ("[BTCoex], decrease Bt Power : %s, write 0x21 = 0x%x\n",
+ (bDecBtPwr ? "Yes!!" : "No!!"), H2C_Parameter[0]));
+
+ FillH2CCmd(padapter, 0x21, 1, H2C_Parameter);
+}
+
+u8 BTDM_BtProfileSupport(struct rtw_adapter *padapter)
+{
+ u8 bRet = false;
+ struct bt_30info *pBTInfo = GET_BT_INFO(padapter);
+ struct bt_mgnt *pBtMgnt = &pBTInfo->BtMgnt;
+ struct hal_data_8723a *pHalData = GET_HAL_DATA(padapter);
+
+ if (pBtMgnt->bSupportProfile &&
+ !pHalData->bt_coexist.halCoex8723.bForceFwBtInfo)
+ bRet = true;
+
+ return bRet;
+}
+
+static void BTDM_AdjustForBtOperation8723A(struct rtw_adapter *padapter)
+{
+ /* BTDM_2AntAdjustForBtOperation8723(padapter); */
+}
+
+static void BTDM_FwC2hBtRssi8723A(struct rtw_adapter *padapter, u8 *tmpBuf)
+{
+ struct hal_data_8723a *pHalData = GET_HAL_DATA(padapter);
+ u8 percent = 0, u1tmp = 0;
+
+ u1tmp = tmpBuf[0];
+ percent = u1tmp*2+10;
+
+ pHalData->bt_coexist.halCoex8723.btRssi = percent;
+/*RTPRINT(FBT, BT_TRACE, ("[BTC2H], BT RSSI =%d\n", percent)); */
+}
+
+void
+rtl8723a_fw_c2h_BT_info(struct rtw_adapter *padapter, u8 *tmpBuf, u8 length)
+{
+ struct hal_data_8723a *pHalData;
+ struct bt_30info *pBTInfo;
+ struct bt_mgnt *pBtMgnt;
+ struct bt_coexist_8723a *pBtCoex;
+ u8 i;
+
+ pHalData = GET_HAL_DATA(padapter);
+ pBTInfo = GET_BT_INFO(padapter);
+ pBtMgnt = &pBTInfo->BtMgnt;
+ pBtCoex = &pHalData->bt_coexist.halCoex8723;
+
+ pBtCoex->bC2hBtInfoReqSent = false;
+
+ RTPRINT(FBT, BT_TRACE, ("[BTC2H], BT info[%d]=[", length));
+
+ pBtCoex->btRetryCnt = 0;
+ for (i = 0; i < length; i++) {
+ switch (i) {
+ case 0:
+ pBtCoex->c2hBtInfoOriginal = tmpBuf[i];
+ break;
+ case 1:
+ pBtCoex->btRetryCnt = tmpBuf[i];
+ break;
+ case 2:
+ BTDM_FwC2hBtRssi8723A(padapter, &tmpBuf[i]);
+ break;
+ case 3:
+ pBtCoex->btInfoExt = tmpBuf[i]&BIT(0);
+ break;
+ }
+
+ if (i == length-1)
+ RTPRINT(FBT, BT_TRACE, ("0x%02x]\n", tmpBuf[i]));
+ else
+ RTPRINT(FBT, BT_TRACE, ("0x%02x, ", tmpBuf[i]));
+ }
+ RTPRINT(FBT, BT_TRACE, ("[BTC2H], BT RSSI =%d\n", pBtCoex->btRssi));
+ if (pBtCoex->btInfoExt)
+ RTPRINT(FBT, BT_TRACE, ("[BTC2H], pBtCoex->btInfoExt =%x\n", pBtCoex->btInfoExt));
+
+ if (btdm_BtWifiAntNum(padapter) == Ant_x1)
+ BTDM_1AntFwC2hBtInfo8723A(padapter);
+ else
+ BTDM_2AntFwC2hBtInfo8723A(padapter);
+
+ if (pBtMgnt->ExtConfig.bManualControl) {
+ RTPRINT(FBT, BT_TRACE, ("%s: Action Manual control!!\n", __func__));
+ return;
+ }
+
+ btdm_BTCoexist8723AHandler(padapter);
+}
+
+static void BTDM_Display8723ABtCoexInfo(struct rtw_adapter *padapter)
+{
+ struct hal_data_8723a *pHalData = GET_HAL_DATA(padapter);
+ struct bt_coexist_8723a *pBtCoex = &pHalData->bt_coexist.halCoex8723;
+ struct bt_30info *pBTInfo = GET_BT_INFO(padapter);
+ struct bt_mgnt *pBtMgnt = &pBTInfo->BtMgnt;
+ u8 u1Tmp, u1Tmp1, u1Tmp2, i, btInfoExt, psTdmaCase = 0;
+ u32 u4Tmp[4];
+ u8 antNum = Ant_x2;
+
+ rsprintf(btCoexDbgBuf, BT_TMP_BUF_SIZE, "\r\n ============[BT Coexist info]============");
+ DCMD_Printf(btCoexDbgBuf);
+
+ if (!rtl8723a_BT_coexist(padapter)) {
+ rsprintf(btCoexDbgBuf, BT_TMP_BUF_SIZE, "\r\n BT not exists !!!");
+ DCMD_Printf(btCoexDbgBuf);
+ return;
+ }
+
+ antNum = btdm_BtWifiAntNum(padapter);
+ rsprintf(btCoexDbgBuf, BT_TMP_BUF_SIZE, "\r\n %-35s = %d/%d ", "Ant mechanism PG/Now run :", \
+ ((pHalData->bt_coexist.BT_Ant_Num == Ant_x2) ? 2 : 1), ((antNum == Ant_x2) ? 2 : 1));
+ DCMD_Printf(btCoexDbgBuf);
+
+ if (pBtMgnt->ExtConfig.bManualControl) {
+ rsprintf(btCoexDbgBuf, BT_TMP_BUF_SIZE, "\r\n %-35s", "[Action Manual control]!!");
+ DCMD_Printf(btCoexDbgBuf);
+ } else {
+ rsprintf(btCoexDbgBuf, BT_TMP_BUF_SIZE, "\r\n %-35s = %s / %d", "BT stack/ hci ext ver", \
+ ((pBtMgnt->bSupportProfile) ? "Yes" : "No"), pBtMgnt->ExtConfig.HCIExtensionVer);
+ DCMD_Printf(btCoexDbgBuf);
+ }
+
+ rsprintf(btCoexDbgBuf, BT_TMP_BUF_SIZE, "\n %-35s = / %d", "Dot11 channel / BT channel", \
+ pBtMgnt->BTChannel);
+ DCMD_Printf(btCoexDbgBuf);
+
+ rsprintf(btCoexDbgBuf, BT_TMP_BUF_SIZE, "\n %-35s = %d / %d / %d", "Wifi/BT/HS rssi", \
+ BTDM_GetRxSS(padapter),
+ pHalData->bt_coexist.halCoex8723.btRssi,
+ pHalData->dmpriv.EntryMinUndecoratedSmoothedPWDB);
+ DCMD_Printf(btCoexDbgBuf);
+
+ if (!pBtMgnt->ExtConfig.bManualControl) {
+ rsprintf(btCoexDbgBuf, BT_TMP_BUF_SIZE, "\n %-35s = %s / %s ", "WIfi status",
+ ((BTDM_Legacy(padapter)) ? "Legacy" : (((BTDM_IsHT40(padapter)) ? "HT40" : "HT20"))),
+ ((!BTDM_IsWifiBusy(padapter)) ? "idle" : ((BTDM_IsWifiUplink(padapter)) ? "uplink" : "downlink")));
+ DCMD_Printf(btCoexDbgBuf);
+
+ if (pBtMgnt->bSupportProfile) {
+ rsprintf(btCoexDbgBuf, BT_TMP_BUF_SIZE, "\r\n %-35s = %d / %d / %d / %d", "SCO/HID/PAN/A2DP",
+ ((BTHCI_CheckProfileExist(padapter, BT_PROFILE_SCO)) ? 1 : 0),
+ ((BTHCI_CheckProfileExist(padapter, BT_PROFILE_HID)) ? 1 : 0),
+ ((BTHCI_CheckProfileExist(padapter, BT_PROFILE_PAN)) ? 1 : 0),
+ ((BTHCI_CheckProfileExist(padapter, BT_PROFILE_A2DP)) ? 1 : 0));
+ DCMD_Printf(btCoexDbgBuf);
+
+ for (i = 0; i < pBtMgnt->ExtConfig.NumberOfHandle; i++) {
+ if (pBtMgnt->ExtConfig.HCIExtensionVer >= 1) {
+ rsprintf(btCoexDbgBuf, BT_TMP_BUF_SIZE, "\r\n %-35s = %s/ %s/ %s", "Bt link type/spec/role",
+ BtProfileString[pBtMgnt->ExtConfig.linkInfo[i].BTProfile],
+ BtSpecString[pBtMgnt->ExtConfig.linkInfo[i].BTCoreSpec],
+ BtLinkRoleString[pBtMgnt->ExtConfig.linkInfo[i].linkRole]);
+ DCMD_Printf(btCoexDbgBuf);
+
+ btInfoExt = pHalData->bt_coexist.halCoex8723.btInfoExt;
+ rsprintf(btCoexDbgBuf, BT_TMP_BUF_SIZE, "\r\n %-35s = %s", "A2DP rate", \
+ (btInfoExt & BIT(0)) ?
+ "Basic rate" : "EDR rate");
+ DCMD_Printf(btCoexDbgBuf);
+ } else {
+ rsprintf(btCoexDbgBuf, BT_TMP_BUF_SIZE, "\r\n %-35s = %s/ %s", "Bt link type/spec", \
+ BtProfileString[pBtMgnt->ExtConfig.linkInfo[i].BTProfile],
+ BtSpecString[pBtMgnt->ExtConfig.linkInfo[i].BTCoreSpec]);
+ DCMD_Printf(btCoexDbgBuf);
+ }
+ }
+ }
+ }
+
+ /* Sw mechanism */
+ if (!pBtMgnt->ExtConfig.bManualControl) {
+ rsprintf(btCoexDbgBuf, BT_TMP_BUF_SIZE, "\r\n %-35s", "============[Sw BT Coex mechanism]============");
+ DCMD_Printf(btCoexDbgBuf);
+ rsprintf(btCoexDbgBuf, BT_TMP_BUF_SIZE, "\r\n %-35s = %d ", "AGC Table", \
+ pBtCoex->btdm2Ant.bCurAgcTableEn);
+ DCMD_Printf(btCoexDbgBuf);
+ rsprintf(btCoexDbgBuf, BT_TMP_BUF_SIZE, "\r\n %-35s = %d ", "ADC Backoff", \
+ pBtCoex->btdm2Ant.bCurAdcBackOff);
+ DCMD_Printf(btCoexDbgBuf);
+ rsprintf(btCoexDbgBuf, BT_TMP_BUF_SIZE, "\r\n %-35s = %d ", "Low penalty RA", \
+ pBtCoex->btdm2Ant.bCurLowPenaltyRa);
+ DCMD_Printf(btCoexDbgBuf);
+ rsprintf(btCoexDbgBuf, BT_TMP_BUF_SIZE, "\r\n %-35s = %d ", "RF Rx LPF Shrink", \
+ pBtCoex->btdm2Ant.bCurRfRxLpfShrink);
+ DCMD_Printf(btCoexDbgBuf);
+ }
+ u4Tmp[0] = PHY_QueryRFReg(padapter, PathA, 0x1e, 0xff0);
+ rsprintf(btCoexDbgBuf, BT_TMP_BUF_SIZE, "\r\n %-35s = 0x%x/ 0x%x", "RF-A, 0x1e[11:4]/original val", \
+ u4Tmp[0], pHalData->bt_coexist.BtRfRegOrigin1E);
+ DCMD_Printf(btCoexDbgBuf);
+
+ /* Fw mechanism */
+ if (!pBtMgnt->ExtConfig.bManualControl) {
+ rsprintf(btCoexDbgBuf, BT_TMP_BUF_SIZE, "\r\n %-35s", "============[Fw BT Coex mechanism]============");
+ DCMD_Printf(btCoexDbgBuf);
+ }
+ if (!pBtMgnt->ExtConfig.bManualControl) {
+ if (btdm_BtWifiAntNum(padapter) == Ant_x1)
+ psTdmaCase = pHalData->bt_coexist.halCoex8723.btdm1Ant.curPsTdma;
+ else
+ psTdmaCase = pHalData->bt_coexist.halCoex8723.btdm2Ant.curPsTdma;
+ rsprintf(btCoexDbgBuf, BT_TMP_BUF_SIZE, "\r\n %-35s = %02x %02x %02x %02x %02x case-%d", "PS TDMA(0x3a)", \
+ pHalData->bt_coexist.fw3aVal[0], pHalData->bt_coexist.fw3aVal[1],
+ pHalData->bt_coexist.fw3aVal[2], pHalData->bt_coexist.fw3aVal[3],
+ pHalData->bt_coexist.fw3aVal[4], psTdmaCase);
+ DCMD_Printf(btCoexDbgBuf);
+
+ rsprintf(btCoexDbgBuf, BT_TMP_BUF_SIZE, "\r\n %-35s = %d ", "Decrease Bt Power", \
+ pBtCoex->btdm2Ant.bCurDecBtPwr);
+ DCMD_Printf(btCoexDbgBuf);
+ }
+ u1Tmp = rtl8723au_read8(padapter, 0x778);
+ u1Tmp1 = rtl8723au_read8(padapter, 0x783);
+ u1Tmp2 = rtl8723au_read8(padapter, 0x796);
+ rsprintf(btCoexDbgBuf, BT_TMP_BUF_SIZE, "\r\n %-35s = 0x%x/ 0x%x/ 0x%x", "0x778/ 0x783/ 0x796", \
+ u1Tmp, u1Tmp1, u1Tmp2);
+ DCMD_Printf(btCoexDbgBuf);
+
+ if (!pBtMgnt->ExtConfig.bManualControl) {
+ rsprintf(btCoexDbgBuf, BT_TMP_BUF_SIZE, "\r\n %-35s = 0x%x / 0x%x", "Sw DacSwing Ctrl/Val", \
+ pBtCoex->btdm2Ant.bCurDacSwingOn, pBtCoex->btdm2Ant.curDacSwingLvl);
+ DCMD_Printf(btCoexDbgBuf);
+ }
+ u4Tmp[0] = rtl8723au_read32(padapter, 0x880);
+ rsprintf(btCoexDbgBuf, BT_TMP_BUF_SIZE, "\r\n %-35s = 0x%x", "0x880", \
+ u4Tmp[0]);
+ DCMD_Printf(btCoexDbgBuf);
+
+ /* Hw mechanism */
+ if (!pBtMgnt->ExtConfig.bManualControl) {
+ rsprintf(btCoexDbgBuf, BT_TMP_BUF_SIZE, "\r\n %-35s", "============[Hw BT Coex mechanism]============");
+ DCMD_Printf(btCoexDbgBuf);
+ }
+
+ u1Tmp = rtl8723au_read8(padapter, 0x40);
+ rsprintf(btCoexDbgBuf, BT_TMP_BUF_SIZE, "\r\n %-35s = 0x%x", "0x40", \
+ u1Tmp);
+ DCMD_Printf(btCoexDbgBuf);
+
+ u4Tmp[0] = rtl8723au_read32(padapter, 0x550);
+ u1Tmp = rtl8723au_read8(padapter, 0x522);
+ rsprintf(btCoexDbgBuf, BT_TMP_BUF_SIZE, "\r\n %-35s = 0x%x/0x%x", "0x550(bcn contrl)/0x522", \
+ u4Tmp[0], u1Tmp);
+ DCMD_Printf(btCoexDbgBuf);
+
+ u4Tmp[0] = rtl8723au_read32(padapter, 0x484);
+ rsprintf(btCoexDbgBuf, BT_TMP_BUF_SIZE, "\r\n %-35s = 0x%x", "0x484(rate adaptive)", \
+ u4Tmp[0]);
+ DCMD_Printf(btCoexDbgBuf);
+
+ u4Tmp[0] = rtl8723au_read32(padapter, 0x50);
+ rsprintf(btCoexDbgBuf, BT_TMP_BUF_SIZE, "\r\n %-35s = 0x%x", "0xc50(dig)", \
+ u4Tmp[0]);
+ DCMD_Printf(btCoexDbgBuf);
+
+ u4Tmp[0] = rtl8723au_read32(padapter, 0xda0);
+ u4Tmp[1] = rtl8723au_read32(padapter, 0xda4);
+ u4Tmp[2] = rtl8723au_read32(padapter, 0xda8);
+ u4Tmp[3] = rtl8723au_read32(padapter, 0xdac);
+ rsprintf(btCoexDbgBuf, BT_TMP_BUF_SIZE, "\r\n %-35s = 0x%x/ 0x%x/ 0x%x/ 0x%x", "0xda0/0xda4/0xda8/0xdac(FA cnt)", \
+ u4Tmp[0], u4Tmp[1], u4Tmp[2], u4Tmp[3]);
+ DCMD_Printf(btCoexDbgBuf);
+
+ u4Tmp[0] = rtl8723au_read32(padapter, 0x6c0);
+ u4Tmp[1] = rtl8723au_read32(padapter, 0x6c4);
+ u4Tmp[2] = rtl8723au_read32(padapter, 0x6c8);
+ u1Tmp = rtl8723au_read8(padapter, 0x6cc);
+ rsprintf(btCoexDbgBuf, BT_TMP_BUF_SIZE, "\r\n %-35s = 0x%x/ 0x%x/ 0x%x/ 0x%x", "0x6c0/0x6c4/0x6c8/0x6cc(coexTable)", \
+ u4Tmp[0], u4Tmp[1], u4Tmp[2], u1Tmp);
+ DCMD_Printf(btCoexDbgBuf);
+
+ /* u4Tmp = rtl8723au_read32(padapter, 0x770); */
+ rsprintf(btCoexDbgBuf, BT_TMP_BUF_SIZE, "\r\n %-35s = %d / %d", "0x770(Hi pri Rx[31:16]/Tx[15:0])", \
+ pHalData->bt_coexist.halCoex8723.highPriorityRx,
+ pHalData->bt_coexist.halCoex8723.highPriorityTx);
+ DCMD_Printf(btCoexDbgBuf);
+ /* u4Tmp = rtl8723au_read32(padapter, 0x774); */
+ rsprintf(btCoexDbgBuf, BT_TMP_BUF_SIZE, "\r\n %-35s = %d / %d", "0x774(Lo pri Rx[31:16]/Tx[15:0])", \
+ pHalData->bt_coexist.halCoex8723.lowPriorityRx,
+ pHalData->bt_coexist.halCoex8723.lowPriorityTx);
+ DCMD_Printf(btCoexDbgBuf);
+
+ /* Tx mgnt queue hang or not, 0x41b should = 0xf, ex: 0xd ==>hang */
+ u1Tmp = rtl8723au_read8(padapter, 0x41b);
+ rsprintf(btCoexDbgBuf, BT_TMP_BUF_SIZE, "\r\n %-35s = 0x%x", "0x41b (hang chk == 0xf)", \
+ u1Tmp);
+ DCMD_Printf(btCoexDbgBuf);
+ rsprintf(btCoexDbgBuf, BT_TMP_BUF_SIZE, "\r\n %-35s = 0x%x", "lastHMEBoxNum", \
+ pHalData->LastHMEBoxNum);
+ DCMD_Printf(btCoexDbgBuf);
+}
+
+static void
+BTDM_8723ASignalCompensation(struct rtw_adapter *padapter,
+ u8 *rssi_wifi, u8 *rssi_bt)
+{
+ if (btdm_BtWifiAntNum(padapter) == Ant_x1)
+ BTDM_1AntSignalCompensation(padapter, rssi_wifi, rssi_bt);
+}
+
+static void BTDM_8723AInit(struct rtw_adapter *padapter)
+{
+ if (btdm_BtWifiAntNum(padapter) == Ant_x2)
+ BTDM_2AntParaInit(padapter);
+ else
+ BTDM_1AntParaInit(padapter);
+}
+
+static void BTDM_HWCoexAllOff8723A(struct rtw_adapter *padapter)
+{
+ struct bt_30info *pBTInfo = GET_BT_INFO(padapter);
+ struct bt_mgnt *pBtMgnt = &pBTInfo->BtMgnt;
+
+ if (pBtMgnt->ExtConfig.bManualControl)
+ return;
+
+ if (btdm_BtWifiAntNum(padapter) == Ant_x2)
+ BTDM_2AntHwCoexAllOff8723A(padapter);
+}
+
+static void BTDM_FWCoexAllOff8723A(struct rtw_adapter *padapter)
+{
+ struct bt_30info *pBTInfo = GET_BT_INFO(padapter);
+ struct bt_mgnt *pBtMgnt = &pBTInfo->BtMgnt;
+
+ if (pBtMgnt->ExtConfig.bManualControl)
+ return;
+
+ if (btdm_BtWifiAntNum(padapter) == Ant_x2)
+ BTDM_2AntFwCoexAllOff8723A(padapter);
+}
+
+static void BTDM_SWCoexAllOff8723A(struct rtw_adapter *padapter)
+{
+ struct bt_30info *pBTInfo = GET_BT_INFO(padapter);
+ struct bt_mgnt *pBtMgnt = &pBTInfo->BtMgnt;
+
+ if (pBtMgnt->ExtConfig.bManualControl)
+ return;
+
+ if (btdm_BtWifiAntNum(padapter) == Ant_x2)
+ BTDM_2AntSwCoexAllOff8723A(padapter);
+}
+
+static void
+BTDM_Set8723ABtCoexCurrAntNum(struct rtw_adapter *padapter, u8 antNum)
+{
+ struct hal_data_8723a *pHalData = GET_HAL_DATA(padapter);
+ struct bt_coexist_8723a *pBtCoex = &pHalData->bt_coexist.halCoex8723;
+
+ if (antNum == 1)
+ pBtCoex->TotalAntNum = Ant_x1;
+ else if (antNum == 2)
+ pBtCoex->TotalAntNum = Ant_x2;
+}
+
+void rtl8723a_BT_lps_leave(struct rtw_adapter *padapter)
+{
+ struct bt_30info *pBTInfo = GET_BT_INFO(padapter);
+ struct bt_mgnt *pBtMgnt = &pBTInfo->BtMgnt;
+
+ if (pBtMgnt->ExtConfig.bManualControl)
+ return;
+
+ if (btdm_BtWifiAntNum(padapter) == Ant_x1)
+ BTDM_1AntLpsLeave(padapter);
+}
+
+static void BTDM_ForHalt8723A(struct rtw_adapter *padapter)
+{
+ struct bt_30info *pBTInfo = GET_BT_INFO(padapter);
+ struct bt_mgnt *pBtMgnt = &pBTInfo->BtMgnt;
+
+ if (pBtMgnt->ExtConfig.bManualControl)
+ return;
+
+ if (btdm_BtWifiAntNum(padapter) == Ant_x1)
+ BTDM_1AntForHalt(padapter);
+}
+
+static void BTDM_WifiScanNotify8723A(struct rtw_adapter *padapter, u8 scanType)
+{
+ struct bt_30info *pBTInfo = GET_BT_INFO(padapter);
+ struct bt_mgnt *pBtMgnt = &pBTInfo->BtMgnt;
+
+ if (pBtMgnt->ExtConfig.bManualControl)
+ return;
+
+ if (btdm_BtWifiAntNum(padapter) == Ant_x1)
+ BTDM_1AntWifiScanNotify(padapter, scanType);
+}
+
+static void
+BTDM_WifiAssociateNotify8723A(struct rtw_adapter *padapter, u8 action)
+{
+ struct bt_30info *pBTInfo = GET_BT_INFO(padapter);
+ struct bt_mgnt *pBtMgnt = &pBTInfo->BtMgnt;
+
+ if (pBtMgnt->ExtConfig.bManualControl)
+ return;
+
+ if (btdm_BtWifiAntNum(padapter) == Ant_x1)
+ BTDM_1AntWifiAssociateNotify(padapter, action);
+}
+
+static void
+BTDM_MediaStatusNotify8723A(struct rtw_adapter *padapter,
+ enum rt_media_status mstatus)
+{
+ struct bt_30info *pBTInfo = GET_BT_INFO(padapter);
+ struct bt_mgnt *pBtMgnt = &pBTInfo->BtMgnt;
+
+ RTPRINT(FBT, BT_TRACE, ("[BTCoex], MediaStatusNotify, %s\n",
+ mstatus?"connect":"disconnect"));
+
+ BTDM_SetFwChnlInfo(padapter, mstatus);
+
+ if (pBtMgnt->ExtConfig.bManualControl)
+ return;
+
+ if (btdm_BtWifiAntNum(padapter) == Ant_x1)
+ BTDM_1AntMediaStatusNotify(padapter, mstatus);
+}
+
+static void BTDM_ForDhcp8723A(struct rtw_adapter *padapter)
+{
+ struct bt_30info *pBTInfo = GET_BT_INFO(padapter);
+ struct bt_mgnt *pBtMgnt = &pBTInfo->BtMgnt;
+
+ if (pBtMgnt->ExtConfig.bManualControl)
+ return;
+
+ if (btdm_BtWifiAntNum(padapter) == Ant_x1)
+ BTDM_1AntForDhcp(padapter);
+}
+
+bool rtl8723a_BT_using_antenna_1(struct rtw_adapter *padapter)
+{
+ if (btdm_BtWifiAntNum(padapter) == Ant_x1)
+ return true;
+ else
+ return false;
+}
+
+static void BTDM_BTCoexist8723A(struct rtw_adapter *padapter)
+{
+ struct hal_data_8723a *pHalData;
+ struct bt_30info *pBTInfo;
+ struct bt_mgnt *pBtMgnt;
+ struct bt_coexist_8723a *pBtCoex;
+
+ pHalData = GET_HAL_DATA(padapter);
+ pBTInfo = GET_BT_INFO(padapter);
+ pBtMgnt = &pBTInfo->BtMgnt;
+ pBtCoex = &pHalData->bt_coexist.halCoex8723;
+
+ RTPRINT(FBT, BT_TRACE, ("[BTCoex], beacon RSSI = 0x%x(%d)\n",
+ pHalData->dmpriv.EntryMinUndecoratedSmoothedPWDB,
+ pHalData->dmpriv.EntryMinUndecoratedSmoothedPWDB));
+
+ btdm_BtHwCountersMonitor(padapter);
+ btdm_BtEnableDisableCheck8723A(padapter);
+
+ if (pBtMgnt->ExtConfig.bManualControl) {
+ RTPRINT(FBT, BT_TRACE, ("%s: Action Manual control!!\n", __func__));
+ return;
+ }
+
+ if (pBtCoex->bC2hBtInfoReqSent) {
+ if (!rtl8723a_BT_enabled(padapter)) {
+ pBtCoex->c2hBtInfo = BT_INFO_STATE_DISABLED;
+ } else {
+ if (pBtCoex->c2hBtInfo == BT_INFO_STATE_DISABLED)
+ pBtCoex->c2hBtInfo = BT_INFO_STATE_NO_CONNECTION;
+ }
+
+ btdm_BTCoexist8723AHandler(padapter);
+ } else if (!rtl8723a_BT_enabled(padapter)) {
+ pBtCoex->c2hBtInfo = BT_INFO_STATE_DISABLED;
+ btdm_BTCoexist8723AHandler(padapter);
+ }
+
+ BTDM_QueryBtInformation(padapter);
+}
+
+/* ===== End of sync from SD7 driver HAL/BTCoexist/HalBtc8723.c ===== */
+
+/* ===== Below this line is sync from SD7 driver HAL/BTCoexist/HalBtcCsr1Ant.c ===== */
+
+/* local function start with btdm_ */
+/* extern function start with BTDM_ */
+
+static void BTDM_SetAntenna(struct rtw_adapter *padapter, u8 who)
+{
+}
+
+void
+BTDM_SingleAnt(
+ struct rtw_adapter *padapter,
+ u8 bSingleAntOn,
+ u8 bInterruptOn,
+ u8 bMultiNAVOn
+ )
+{
+ struct hal_data_8723a *pHalData = GET_HAL_DATA(padapter);
+ u8 H2C_Parameter[3] = {0};
+
+ if (pHalData->bt_coexist.BT_Ant_Num != Ant_x1)
+ return;
+
+ H2C_Parameter[2] = 0;
+ H2C_Parameter[1] = 0;
+ H2C_Parameter[0] = 0;
+
+ if (bInterruptOn) {
+ H2C_Parameter[2] |= 0x02; /* BIT1 */
+ pHalData->bt_coexist.bFWCoexistAllOff = false;
+ }
+ pHalData->bt_coexist.bInterruptOn = bInterruptOn;
+
+ if (bSingleAntOn) {
+ H2C_Parameter[2] |= 0x10; /* BIT4 */
+ pHalData->bt_coexist.bFWCoexistAllOff = false;
+ }
+ pHalData->bt_coexist.bSingleAntOn = bSingleAntOn;
+
+ if (bMultiNAVOn) {
+ H2C_Parameter[2] |= 0x20; /* BIT5 */
+ pHalData->bt_coexist.bFWCoexistAllOff = false;
+ }
+ pHalData->bt_coexist.bMultiNAVOn = bMultiNAVOn;
+
+ RTPRINT(FBT, BT_TRACE, ("[DM][BT], SingleAntenna =[%s:%s:%s], write 0xe = 0x%x\n",
+ bSingleAntOn?"ON":"OFF", bInterruptOn?"ON":"OFF", bMultiNAVOn?"ON":"OFF",
+ H2C_Parameter[0]<<16|H2C_Parameter[1]<<8|H2C_Parameter[2]));
+}
+
+void BTDM_CheckBTIdleChange1Ant(struct rtw_adapter *padapter)
+{
+ struct bt_30info *pBTInfo = GET_BT_INFO(padapter);
+ struct bt_mgnt *pBtMgnt = &pBTInfo->BtMgnt;
+ struct hal_data_8723a *pHalData = GET_HAL_DATA(padapter);
+/*PMGNT_INFO pMgntInfo = &padapter->MgntInfo; */
+ u8 stateChange = false;
+ u32 BT_Polling, Ratio_Act, Ratio_STA;
+ u32 BT_Active, BT_State;
+ u32 regBTActive = 0, regBTState = 0, regBTPolling = 0;
+
+ if (!rtl8723a_BT_coexist(padapter))
+ return;
+ if (pBtMgnt->ExtConfig.bManualControl)
+ return;
+ if (pHalData->bt_coexist.BT_CoexistType != BT_CSR_BC8)
+ return;
+ if (pHalData->bt_coexist.BT_Ant_Num != Ant_x1)
+ return;
+
+ /* The following we only consider CSR BC8 and fw version should be >= 62 */
+ RTPRINT(FBT, BT_TRACE, ("[DM][BT], FirmwareVersion = 0x%x(%d)\n",
+ pHalData->FirmwareVersion, pHalData->FirmwareVersion));
+ regBTActive = REG_BT_ACTIVE;
+ regBTState = REG_BT_STATE;
+ if (pHalData->FirmwareVersion >= FW_VER_BT_REG1)
+ regBTPolling = REG_BT_POLLING1;
+ else
+ regBTPolling = REG_BT_POLLING;
+
+ BT_Active = rtl8723au_read32(padapter, regBTActive);
+ RTPRINT(FBT, BT_TRACE, ("[DM][BT], BT_Active(0x%x) =%x\n", regBTActive, BT_Active));
+ BT_Active = BT_Active & 0x00ffffff;
+
+ BT_State = rtl8723au_read32(padapter, regBTState);
+ RTPRINT(FBT, BT_TRACE, ("[DM][BT], BT_State(0x%x) =%x\n", regBTState, BT_State));
+ BT_State = BT_State & 0x00ffffff;
+
+ BT_Polling = rtl8723au_read32(padapter, regBTPolling);
+ RTPRINT(FBT, BT_TRACE, ("[DM][BT], BT_Polling(0x%x) =%x\n", regBTPolling, BT_Polling));
+
+ if (BT_Active == 0xffffffff && BT_State == 0xffffffff && BT_Polling == 0xffffffff)
+ return;
+ if (BT_Polling == 0)
+ return;
+
+ Ratio_Act = BT_Active*1000/BT_Polling;
+ Ratio_STA = BT_State*1000/BT_Polling;
+
+ pHalData->bt_coexist.Ratio_Tx = Ratio_Act;
+ pHalData->bt_coexist.Ratio_PRI = Ratio_STA;
+
+ RTPRINT(FBT, BT_TRACE, ("[DM][BT], Ratio_Act =%d\n", Ratio_Act));
+ RTPRINT(FBT, BT_TRACE, ("[DM][BT], Ratio_STA =%d\n", Ratio_STA));
+
+ if (Ratio_STA < 60 && Ratio_Act < 500) { /* BT PAN idle */
+ pHalData->bt_coexist.CurrentState |= BT_COEX_STATE_BT_PAN_IDLE;
+ pHalData->bt_coexist.CurrentState &= ~BT_COEX_STATE_BT_PAN_DOWNLINK;
+ pHalData->bt_coexist.CurrentState &= ~BT_COEX_STATE_BT_PAN_UPLINK;
+ } else {
+ pHalData->bt_coexist.CurrentState &= ~BT_COEX_STATE_BT_PAN_IDLE;
+
+ if (Ratio_STA) {
+ /* Check if BT PAN (under BT 2.1) is uplink or downlink */
+ if ((Ratio_Act/Ratio_STA) < 2) {
+ /* BT PAN Uplink */
+ pHalData->bt_coexist.BT21TrafficStatistics.bTxBusyTraffic = true;
+ pHalData->bt_coexist.CurrentState |= BT_COEX_STATE_BT_PAN_UPLINK;
+ pHalData->bt_coexist.BT21TrafficStatistics.bRxBusyTraffic = false;
+ pHalData->bt_coexist.CurrentState &= ~BT_COEX_STATE_BT_PAN_DOWNLINK;
+ } else {
+ /* BT PAN downlink */
+ pHalData->bt_coexist.BT21TrafficStatistics.bTxBusyTraffic = false;
+ pHalData->bt_coexist.CurrentState &= ~BT_COEX_STATE_BT_PAN_UPLINK;
+ pHalData->bt_coexist.BT21TrafficStatistics.bRxBusyTraffic = true;
+ pHalData->bt_coexist.CurrentState |= BT_COEX_STATE_BT_PAN_DOWNLINK;
+ }
+ } else {
+ /* BT PAN downlink */
+ pHalData->bt_coexist.BT21TrafficStatistics.bTxBusyTraffic = false;
+ pHalData->bt_coexist.CurrentState &= ~BT_COEX_STATE_BT_PAN_UPLINK;
+ pHalData->bt_coexist.BT21TrafficStatistics.bRxBusyTraffic = true;
+ pHalData->bt_coexist.CurrentState |= BT_COEX_STATE_BT_PAN_DOWNLINK;
+ }
+ }
+
+ /* Check BT is idle or not */
+ if (pBtMgnt->ExtConfig.NumberOfHandle == 0 &&
+ pBtMgnt->ExtConfig.NumberOfSCO == 0) {
+ pBtMgnt->ExtConfig.bBTBusy = false;
+ pHalData->bt_coexist.CurrentState |= BT_COEX_STATE_BT_IDLE;
+ } else {
+ if (Ratio_STA < 60) {
+ pBtMgnt->ExtConfig.bBTBusy = false;
+ pHalData->bt_coexist.CurrentState |= BT_COEX_STATE_BT_IDLE;
+ } else {
+ pBtMgnt->ExtConfig.bBTBusy = true;
+ pHalData->bt_coexist.CurrentState &= ~BT_COEX_STATE_BT_IDLE;
+ }
+ }
+
+ if (pBtMgnt->ExtConfig.NumberOfHandle == 0 &&
+ pBtMgnt->ExtConfig.NumberOfSCO == 0) {
+ pHalData->bt_coexist.CurrentState &= ~BT_COEX_STATE_BT_RSSI_LOW;
+ pBtMgnt->ExtConfig.MIN_BT_RSSI = 0;
+ BTDM_SetAntenna(padapter, BTDM_ANT_BT_IDLE);
+ } else {
+ if (pBtMgnt->ExtConfig.MIN_BT_RSSI <= -5) {
+ pHalData->bt_coexist.CurrentState |= BT_COEX_STATE_BT_RSSI_LOW;
+ RTPRINT(FBT, BT_TRACE, ("[DM][BT], core stack notify bt rssi Low\n"));
+ } else {
+ pHalData->bt_coexist.CurrentState &= ~BT_COEX_STATE_BT_RSSI_LOW;
+ RTPRINT(FBT, BT_TRACE, ("[DM][BT], core stack notify bt rssi Normal\n"));
+ }
+ }
+
+ if (pHalData->bt_coexist.bBTBusyTraffic != pBtMgnt->ExtConfig.bBTBusy) {
+ /* BT idle or BT non-idle */
+ pHalData->bt_coexist.bBTBusyTraffic = pBtMgnt->ExtConfig.bBTBusy;
+ stateChange = true;
+ }
+
+ if (stateChange) {
+ if (!pBtMgnt->ExtConfig.bBTBusy)
+ RTPRINT(FBT, BT_TRACE, ("[DM][BT], BT is idle or disable\n"));
+ else
+ RTPRINT(FBT, BT_TRACE, ("[DM][BT], BT is non-idle\n"));
+ }
+ if (!pBtMgnt->ExtConfig.bBTBusy) {
+ RTPRINT(FBT, BT_TRACE, ("[DM][BT], BT is idle or disable\n"));
+ if (check_fwstate(&padapter->mlmepriv, WIFI_UNDER_LINKING|WIFI_SITE_MONITOR) == true)
+ BTDM_SetAntenna(padapter, BTDM_ANT_WIFI);
+ }
+}
+
+/* ===== End of sync from SD7 driver HAL/BTCoexist/HalBtcCsr1Ant.c ===== */
+
+/* ===== Below this line is sync from SD7 driver HAL/BTCoexist/HalBtcCsr2Ant.c ===== */
+
+/* local function start with btdm_ */
+
+/* Note: */
+/* In the following, FW should be done before SW mechanism. */
+/* BTDM_Balance(), BTDM_DiminishWiFi(), BT_NAV() should be done */
+/* before BTDM_AGCTable(), BTDM_BBBackOffLevel(), btdm_DacSwing(). */
+
+/* extern function start with BTDM_ */
+
+void
+BTDM_DiminishWiFi(
+ struct rtw_adapter *padapter,
+ u8 bDACOn,
+ u8 bInterruptOn,
+ u8 DACSwingLevel,
+ u8 bNAVOn
+ )
+{
+ struct hal_data_8723a *pHalData = GET_HAL_DATA(padapter);
+ u8 H2C_Parameter[3] = {0};
+
+ if (pHalData->bt_coexist.BT_Ant_Num != Ant_x2)
+ return;
+
+ if ((pHalData->bt_coexist.CurrentState & BT_COEX_STATE_BT_RSSI_LOW) &&
+ (DACSwingLevel == 0x20)) {
+ RTPRINT(FBT, BT_TRACE, ("[BT]DiminishWiFi 0x20 original, but set 0x18 for Low RSSI!\n"));
+ DACSwingLevel = 0x18;
+ }
+
+ H2C_Parameter[2] = 0;
+ H2C_Parameter[1] = DACSwingLevel;
+ H2C_Parameter[0] = 0;
+ if (bDACOn) {
+ H2C_Parameter[2] |= 0x01; /* BIT0 */
+ if (bInterruptOn)
+ H2C_Parameter[2] |= 0x02; /* BIT1 */
+ pHalData->bt_coexist.bFWCoexistAllOff = false;
+ }
+ if (bNAVOn) {
+ H2C_Parameter[2] |= 0x08; /* BIT3 */
+ pHalData->bt_coexist.bFWCoexistAllOff = false;
+ }
+
+ RTPRINT(FBT, BT_TRACE, ("[DM][BT], bDACOn = %s, bInterruptOn = %s, write 0xe = 0x%x\n",
+ bDACOn?"ON":"OFF", bInterruptOn?"ON":"OFF",
+ H2C_Parameter[0]<<16|H2C_Parameter[1]<<8|H2C_Parameter[2]));
+ RTPRINT(FBT, BT_TRACE, ("[DM][BT], bNAVOn = %s\n",
+ bNAVOn?"ON":"OFF"));
+}
+
+/* ===== End of sync from SD7 driver HAL/BTCoexist/HalBtcCsr2Ant.c ===== */
+
+/* ===== Below this line is sync from SD7 driver HAL/BTCoexist/HalBtCoexist.c ===== */
+
+/* local function */
+static void btdm_ResetFWCoexState(struct rtw_adapter *padapter)
+{
+ struct hal_data_8723a *pHalData = GET_HAL_DATA(padapter);
+
+ pHalData->bt_coexist.CurrentState = 0;
+ pHalData->bt_coexist.PreviousState = 0;
+}
+
+static void btdm_InitBtCoexistDM(struct rtw_adapter *padapter)
+{
+ struct hal_data_8723a *pHalData = GET_HAL_DATA(padapter);
+
+ /* 20100415 Joseph: Restore RF register 0x1E and 0x1F value for further usage. */
+ pHalData->bt_coexist.BtRfRegOrigin1E = PHY_QueryRFReg(padapter, PathA, RF_RCK1, bRFRegOffsetMask);
+ pHalData->bt_coexist.BtRfRegOrigin1F = PHY_QueryRFReg(padapter, PathA, RF_RCK2, 0xf0);
+
+ pHalData->bt_coexist.CurrentState = 0;
+ pHalData->bt_coexist.PreviousState = 0;
+
+ BTDM_8723AInit(padapter);
+ pHalData->bt_coexist.bInitlized = true;
+}
+
+/* */
+/* extern function */
+/* */
+void BTDM_CheckAntSelMode(struct rtw_adapter *padapter)
+{
+}
+
+void BTDM_FwC2hBtRssi(struct rtw_adapter *padapter, u8 *tmpBuf)
+{
+ BTDM_FwC2hBtRssi8723A(padapter, tmpBuf);
+}
+
+void BTDM_DisplayBtCoexInfo(struct rtw_adapter *padapter)
+{
+ BTDM_Display8723ABtCoexInfo(padapter);
+}
+
+void BTDM_RejectAPAggregatedPacket(struct rtw_adapter *padapter, u8 bReject)
+{
+}
+
+u8 BTDM_IsHT40(struct rtw_adapter *padapter)
+{
+ u8 isht40 = true;
+ enum ht_channel_width bw;
+
+ bw = padapter->mlmeextpriv.cur_bwmode;
+
+ if (bw == HT_CHANNEL_WIDTH_20)
+ isht40 = false;
+ else if (bw == HT_CHANNEL_WIDTH_40)
+ isht40 = true;
+
+ return isht40;
+}
+
+u8 BTDM_Legacy(struct rtw_adapter *padapter)
+{
+ struct mlme_ext_priv *pmlmeext;
+ u8 isLegacy = false;
+
+ pmlmeext = &padapter->mlmeextpriv;
+ if ((pmlmeext->cur_wireless_mode == WIRELESS_11B) ||
+ (pmlmeext->cur_wireless_mode == WIRELESS_11G) ||
+ (pmlmeext->cur_wireless_mode == WIRELESS_11BG))
+ isLegacy = true;
+
+ return isLegacy;
+}
+
+void BTDM_CheckWiFiState(struct rtw_adapter *padapter)
+{
+ struct hal_data_8723a *pHalData;
+ struct mlme_priv *pmlmepriv;
+ struct bt_30info *pBTInfo;
+ struct bt_mgnt *pBtMgnt;
+
+ pHalData = GET_HAL_DATA(padapter);
+ pmlmepriv = &padapter->mlmepriv;
+ pBTInfo = GET_BT_INFO(padapter);
+ pBtMgnt = &pBTInfo->BtMgnt;
+
+ if (pmlmepriv->LinkDetectInfo.bBusyTraffic) {
+ pHalData->bt_coexist.CurrentState &= ~BT_COEX_STATE_WIFI_IDLE;
+
+ if (pmlmepriv->LinkDetectInfo.bTxBusyTraffic)
+ pHalData->bt_coexist.CurrentState |= BT_COEX_STATE_WIFI_UPLINK;
+ else
+ pHalData->bt_coexist.CurrentState &= ~BT_COEX_STATE_WIFI_UPLINK;
+
+ if (pmlmepriv->LinkDetectInfo.bRxBusyTraffic)
+ pHalData->bt_coexist.CurrentState |= BT_COEX_STATE_WIFI_DOWNLINK;
+ else
+ pHalData->bt_coexist.CurrentState &= ~BT_COEX_STATE_WIFI_DOWNLINK;
+ } else {
+ pHalData->bt_coexist.CurrentState |= BT_COEX_STATE_WIFI_IDLE;
+ pHalData->bt_coexist.CurrentState &= ~BT_COEX_STATE_WIFI_UPLINK;
+ pHalData->bt_coexist.CurrentState &= ~BT_COEX_STATE_WIFI_DOWNLINK;
+ }
+
+ if (BTDM_Legacy(padapter)) {
+ pHalData->bt_coexist.CurrentState |= BT_COEX_STATE_WIFI_LEGACY;
+ pHalData->bt_coexist.CurrentState &= ~BT_COEX_STATE_WIFI_HT20;
+ pHalData->bt_coexist.CurrentState &= ~BT_COEX_STATE_WIFI_HT40;
+ } else {
+ pHalData->bt_coexist.CurrentState &= ~BT_COEX_STATE_WIFI_LEGACY;
+ if (BTDM_IsHT40(padapter)) {
+ pHalData->bt_coexist.CurrentState |= BT_COEX_STATE_WIFI_HT40;
+ pHalData->bt_coexist.CurrentState &= ~BT_COEX_STATE_WIFI_HT20;
+ } else {
+ pHalData->bt_coexist.CurrentState |= BT_COEX_STATE_WIFI_HT20;
+ pHalData->bt_coexist.CurrentState &= ~BT_COEX_STATE_WIFI_HT40;
+ }
+ }
+
+ if (pBtMgnt->BtOperationOn)
+ pHalData->bt_coexist.CurrentState |= BT_COEX_STATE_BT30;
+ else
+ pHalData->bt_coexist.CurrentState &= ~BT_COEX_STATE_BT30;
+}
+
+s32 BTDM_GetRxSS(struct rtw_adapter *padapter)
+{
+/*PMGNT_INFO pMgntInfo = &padapter->MgntInfo; */
+ struct mlme_priv *pmlmepriv;
+ struct hal_data_8723a *pHalData;
+ s32 UndecoratedSmoothedPWDB = 0;
+
+ pmlmepriv = &padapter->mlmepriv;
+ pHalData = GET_HAL_DATA(padapter);
+
+ if (check_fwstate(pmlmepriv, _FW_LINKED)) {
+ UndecoratedSmoothedPWDB = GET_UNDECORATED_AVERAGE_RSSI(padapter);
+ } else { /* associated entry pwdb */
+ UndecoratedSmoothedPWDB = pHalData->dmpriv.EntryMinUndecoratedSmoothedPWDB;
+ /* pHalData->BT_EntryMinUndecoratedSmoothedPWDB */
+ }
+ RTPRINT(FBT, BT_TRACE, ("BTDM_GetRxSS() = %d\n", UndecoratedSmoothedPWDB));
+ return UndecoratedSmoothedPWDB;
+}
+
+static s32 BTDM_GetRxBeaconSS(struct rtw_adapter *padapter)
+{
+/*PMGNT_INFO pMgntInfo = &padapter->MgntInfo; */
+ struct mlme_priv *pmlmepriv;
+ struct hal_data_8723a *pHalData;
+ s32 pwdbBeacon = 0;
+
+ pmlmepriv = &padapter->mlmepriv;
+ pHalData = GET_HAL_DATA(padapter);
+
+ if (check_fwstate(pmlmepriv, _FW_LINKED)) {
+ /* pwdbBeacon = pHalData->dmpriv.UndecoratedSmoothedBeacon; */
+ pwdbBeacon = pHalData->dmpriv.EntryMinUndecoratedSmoothedPWDB;
+ }
+ RTPRINT(FBT, BT_TRACE, ("BTDM_GetRxBeaconSS() = %d\n", pwdbBeacon));
+ return pwdbBeacon;
+}
+
+/* Get beacon rssi state */
+u8 BTDM_CheckCoexBcnRssiState(struct rtw_adapter *padapter, u8 levelNum,
+ u8 RssiThresh, u8 RssiThresh1)
+{
+ struct hal_data_8723a *pHalData = GET_HAL_DATA(padapter);
+ s32 pwdbBeacon = 0;
+ u8 bcnRssiState = 0;
+
+ pwdbBeacon = BTDM_GetRxBeaconSS(padapter);
+
+ if (levelNum == 2) {
+ pHalData->bt_coexist.CurrentState &= ~BT_COEX_STATE_WIFI_RSSI_BEACON_MEDIUM;
+
+ if ((pHalData->bt_coexist.preRssiStateBeacon == BT_RSSI_STATE_LOW) ||
+ (pHalData->bt_coexist.preRssiStateBeacon == BT_RSSI_STATE_STAY_LOW)) {
+ if (pwdbBeacon >= (RssiThresh+BT_FW_COEX_THRESH_TOL)) {
+ bcnRssiState = BT_RSSI_STATE_HIGH;
+ pHalData->bt_coexist.CurrentState |= BT_COEX_STATE_WIFI_RSSI_BEACON_HIGH;
+ pHalData->bt_coexist.CurrentState &= ~BT_COEX_STATE_WIFI_RSSI_BEACON_LOW;
+ RTPRINT(FBT, BT_TRACE, ("[DM][BT], RSSI_BEACON state switch to High\n"));
+ } else {
+ bcnRssiState = BT_RSSI_STATE_STAY_LOW;
+ RTPRINT(FBT, BT_TRACE, ("[DM][BT], RSSI_BEACON state stay at Low\n"));
+ }
+ } else {
+ if (pwdbBeacon < RssiThresh) {
+ bcnRssiState = BT_RSSI_STATE_LOW;
+ pHalData->bt_coexist.CurrentState |= BT_COEX_STATE_WIFI_RSSI_BEACON_LOW;
+ pHalData->bt_coexist.CurrentState &= ~BT_COEX_STATE_WIFI_RSSI_BEACON_HIGH;
+ RTPRINT(FBT, BT_TRACE, ("[DM][BT], RSSI_BEACON state switch to Low\n"));
+ } else {
+ bcnRssiState = BT_RSSI_STATE_STAY_HIGH;
+ RTPRINT(FBT, BT_TRACE, ("[DM][BT], RSSI_BEACON state stay at High\n"));
+ }
+ }
+ } else if (levelNum == 3) {
+ if (RssiThresh > RssiThresh1) {
+ RTPRINT(FBT, BT_TRACE, ("[DM][BT], RSSI_BEACON thresh error!!\n"));
+ return pHalData->bt_coexist.preRssiStateBeacon;
+ }
+
+ if ((pHalData->bt_coexist.preRssiStateBeacon == BT_RSSI_STATE_LOW) ||
+ (pHalData->bt_coexist.preRssiStateBeacon == BT_RSSI_STATE_STAY_LOW)) {
+ if (pwdbBeacon >= (RssiThresh+BT_FW_COEX_THRESH_TOL)) {
+ bcnRssiState = BT_RSSI_STATE_MEDIUM;
+ pHalData->bt_coexist.CurrentState |= BT_COEX_STATE_WIFI_RSSI_BEACON_MEDIUM;
+ pHalData->bt_coexist.CurrentState &= ~BT_COEX_STATE_WIFI_RSSI_BEACON_LOW;
+ pHalData->bt_coexist.CurrentState &= ~BT_COEX_STATE_WIFI_RSSI_BEACON_HIGH;
+ RTPRINT(FBT, BT_TRACE, ("[DM][BT], RSSI_BEACON state switch to Medium\n"));
+ } else {
+ bcnRssiState = BT_RSSI_STATE_STAY_LOW;
+ RTPRINT(FBT, BT_TRACE, ("[DM][BT], RSSI_BEACON state stay at Low\n"));
+ }
+ } else if ((pHalData->bt_coexist.preRssiStateBeacon == BT_RSSI_STATE_MEDIUM) ||
+ (pHalData->bt_coexist.preRssiStateBeacon == BT_RSSI_STATE_STAY_MEDIUM)) {
+ if (pwdbBeacon >= (RssiThresh1+BT_FW_COEX_THRESH_TOL)) {
+ bcnRssiState = BT_RSSI_STATE_HIGH;
+ pHalData->bt_coexist.CurrentState |= BT_COEX_STATE_WIFI_RSSI_BEACON_HIGH;
+ pHalData->bt_coexist.CurrentState &= ~BT_COEX_STATE_WIFI_RSSI_BEACON_LOW;
+ pHalData->bt_coexist.CurrentState &= ~BT_COEX_STATE_WIFI_RSSI_BEACON_MEDIUM;
+ RTPRINT(FBT, BT_TRACE, ("[DM][BT], RSSI_BEACON state switch to High\n"));
+ } else if (pwdbBeacon < RssiThresh) {
+ bcnRssiState = BT_RSSI_STATE_LOW;
+ pHalData->bt_coexist.CurrentState |= BT_COEX_STATE_WIFI_RSSI_BEACON_LOW;
+ pHalData->bt_coexist.CurrentState &= ~BT_COEX_STATE_WIFI_RSSI_BEACON_HIGH;
+ pHalData->bt_coexist.CurrentState &= ~BT_COEX_STATE_WIFI_RSSI_BEACON_MEDIUM;
+ RTPRINT(FBT, BT_TRACE, ("[DM][BT], RSSI_BEACON state switch to Low\n"));
+ } else {
+ bcnRssiState = BT_RSSI_STATE_STAY_MEDIUM;
+ RTPRINT(FBT, BT_TRACE, ("[DM][BT], RSSI_BEACON state stay at Medium\n"));
+ }
+ } else {
+ if (pwdbBeacon < RssiThresh1) {
+ bcnRssiState = BT_RSSI_STATE_MEDIUM;
+ pHalData->bt_coexist.CurrentState |= BT_COEX_STATE_WIFI_RSSI_BEACON_MEDIUM;
+ pHalData->bt_coexist.CurrentState &= ~BT_COEX_STATE_WIFI_RSSI_BEACON_HIGH;
+ pHalData->bt_coexist.CurrentState &= ~BT_COEX_STATE_WIFI_RSSI_BEACON_LOW;
+ RTPRINT(FBT, BT_TRACE, ("[DM][BT], RSSI_BEACON state switch to Medium\n"));
+ } else {
+ bcnRssiState = BT_RSSI_STATE_STAY_HIGH;
+ RTPRINT(FBT, BT_TRACE, ("[DM][BT], RSSI_BEACON state stay at High\n"));
+ }
+ }
+ }
+
+ pHalData->bt_coexist.preRssiStateBeacon = bcnRssiState;
+
+ return bcnRssiState;
+}
+
+u8 BTDM_CheckCoexRSSIState1(struct rtw_adapter *padapter, u8 levelNum,
+ u8 RssiThresh, u8 RssiThresh1)
+{
+ struct hal_data_8723a *pHalData = GET_HAL_DATA(padapter);
+ s32 UndecoratedSmoothedPWDB = 0;
+ u8 btRssiState = 0;
+
+ UndecoratedSmoothedPWDB = BTDM_GetRxSS(padapter);
+
+ if (levelNum == 2) {
+ pHalData->bt_coexist.CurrentState &= ~BT_COEX_STATE_WIFI_RSSI_1_MEDIUM;
+
+ if ((pHalData->bt_coexist.preRssiState1 == BT_RSSI_STATE_LOW) ||
+ (pHalData->bt_coexist.preRssiState1 == BT_RSSI_STATE_STAY_LOW)) {
+ if (UndecoratedSmoothedPWDB >= (RssiThresh+BT_FW_COEX_THRESH_TOL)) {
+ btRssiState = BT_RSSI_STATE_HIGH;
+ pHalData->bt_coexist.CurrentState |= BT_COEX_STATE_WIFI_RSSI_1_HIGH;
+ pHalData->bt_coexist.CurrentState &= ~BT_COEX_STATE_WIFI_RSSI_1_LOW;
+ RTPRINT(FBT, BT_TRACE, ("[DM][BT], RSSI_1 state switch to High\n"));
+ } else {
+ btRssiState = BT_RSSI_STATE_STAY_LOW;
+ RTPRINT(FBT, BT_TRACE, ("[DM][BT], RSSI_1 state stay at Low\n"));
+ }
+ } else {
+ if (UndecoratedSmoothedPWDB < RssiThresh) {
+ btRssiState = BT_RSSI_STATE_LOW;
+ pHalData->bt_coexist.CurrentState |= BT_COEX_STATE_WIFI_RSSI_1_LOW;
+ pHalData->bt_coexist.CurrentState &= ~BT_COEX_STATE_WIFI_RSSI_1_HIGH;
+ RTPRINT(FBT, BT_TRACE, ("[DM][BT], RSSI_1 state switch to Low\n"));
+ } else {
+ btRssiState = BT_RSSI_STATE_STAY_HIGH;
+ RTPRINT(FBT, BT_TRACE, ("[DM][BT], RSSI_1 state stay at High\n"));
+ }
+ }
+ } else if (levelNum == 3) {
+ if (RssiThresh > RssiThresh1) {
+ RTPRINT(FBT, BT_TRACE, ("[DM][BT], RSSI_1 thresh error!!\n"));
+ return pHalData->bt_coexist.preRssiState1;
+ }
+
+ if ((pHalData->bt_coexist.preRssiState1 == BT_RSSI_STATE_LOW) ||
+ (pHalData->bt_coexist.preRssiState1 == BT_RSSI_STATE_STAY_LOW)) {
+ if (UndecoratedSmoothedPWDB >= (RssiThresh+BT_FW_COEX_THRESH_TOL)) {
+ btRssiState = BT_RSSI_STATE_MEDIUM;
+ pHalData->bt_coexist.CurrentState |= BT_COEX_STATE_WIFI_RSSI_1_MEDIUM;
+ pHalData->bt_coexist.CurrentState &= ~BT_COEX_STATE_WIFI_RSSI_1_LOW;
+ pHalData->bt_coexist.CurrentState &= ~BT_COEX_STATE_WIFI_RSSI_1_HIGH;
+ RTPRINT(FBT, BT_TRACE, ("[DM][BT], RSSI_1 state switch to Medium\n"));
+ } else {
+ btRssiState = BT_RSSI_STATE_STAY_LOW;
+ RTPRINT(FBT, BT_TRACE, ("[DM][BT], RSSI_1 state stay at Low\n"));
+ }
+ } else if ((pHalData->bt_coexist.preRssiState1 == BT_RSSI_STATE_MEDIUM) ||
+ (pHalData->bt_coexist.preRssiState1 == BT_RSSI_STATE_STAY_MEDIUM)) {
+ if (UndecoratedSmoothedPWDB >= (RssiThresh1+BT_FW_COEX_THRESH_TOL)) {
+ btRssiState = BT_RSSI_STATE_HIGH;
+ pHalData->bt_coexist.CurrentState |= BT_COEX_STATE_WIFI_RSSI_1_HIGH;
+ pHalData->bt_coexist.CurrentState &= ~BT_COEX_STATE_WIFI_RSSI_1_LOW;
+ pHalData->bt_coexist.CurrentState &= ~BT_COEX_STATE_WIFI_RSSI_1_MEDIUM;
+ RTPRINT(FBT, BT_TRACE, ("[DM][BT], RSSI_1 state switch to High\n"));
+ } else if (UndecoratedSmoothedPWDB < RssiThresh) {
+ btRssiState = BT_RSSI_STATE_LOW;
+ pHalData->bt_coexist.CurrentState |= BT_COEX_STATE_WIFI_RSSI_1_LOW;
+ pHalData->bt_coexist.CurrentState &= ~BT_COEX_STATE_WIFI_RSSI_1_HIGH;
+ pHalData->bt_coexist.CurrentState &= ~BT_COEX_STATE_WIFI_RSSI_1_MEDIUM;
+ RTPRINT(FBT, BT_TRACE, ("[DM][BT], RSSI_1 state switch to Low\n"));
+ } else {
+ btRssiState = BT_RSSI_STATE_STAY_MEDIUM;
+ RTPRINT(FBT, BT_TRACE, ("[DM][BT], RSSI_1 state stay at Medium\n"));
+ }
+ } else {
+ if (UndecoratedSmoothedPWDB < RssiThresh1) {
+ btRssiState = BT_RSSI_STATE_MEDIUM;
+ pHalData->bt_coexist.CurrentState |= BT_COEX_STATE_WIFI_RSSI_1_MEDIUM;
+ pHalData->bt_coexist.CurrentState &= ~BT_COEX_STATE_WIFI_RSSI_1_HIGH;
+ pHalData->bt_coexist.CurrentState &= ~BT_COEX_STATE_WIFI_RSSI_1_LOW;
+ RTPRINT(FBT, BT_TRACE, ("[DM][BT], RSSI_1 state switch to Medium\n"));
+ } else {
+ btRssiState = BT_RSSI_STATE_STAY_HIGH;
+ RTPRINT(FBT, BT_TRACE, ("[DM][BT], RSSI_1 state stay at High\n"));
+ }
+ }
+ }
+
+ pHalData->bt_coexist.preRssiState1 = btRssiState;
+
+ return btRssiState;
+}
+
+u8 BTDM_CheckCoexRSSIState(struct rtw_adapter *padapter, u8 levelNum,
+ u8 RssiThresh, u8 RssiThresh1)
+{
+ struct hal_data_8723a *pHalData = GET_HAL_DATA(padapter);
+ s32 UndecoratedSmoothedPWDB = 0;
+ u8 btRssiState = 0;
+
+ UndecoratedSmoothedPWDB = BTDM_GetRxSS(padapter);
+
+ if (levelNum == 2) {
+ pHalData->bt_coexist.CurrentState &= ~BT_COEX_STATE_WIFI_RSSI_MEDIUM;
+
+ if ((pHalData->bt_coexist.preRssiState == BT_RSSI_STATE_LOW) ||
+ (pHalData->bt_coexist.preRssiState == BT_RSSI_STATE_STAY_LOW)) {
+ if (UndecoratedSmoothedPWDB >= (RssiThresh+BT_FW_COEX_THRESH_TOL)) {
+ btRssiState = BT_RSSI_STATE_HIGH;
+ pHalData->bt_coexist.CurrentState |= BT_COEX_STATE_WIFI_RSSI_HIGH;
+ pHalData->bt_coexist.CurrentState &= ~BT_COEX_STATE_WIFI_RSSI_LOW;
+ RTPRINT(FBT, BT_TRACE, ("[DM][BT], RSSI state switch to High\n"));
+ } else {
+ btRssiState = BT_RSSI_STATE_STAY_LOW;
+ RTPRINT(FBT, BT_TRACE, ("[DM][BT], RSSI state stay at Low\n"));
+ }
+ } else {
+ if (UndecoratedSmoothedPWDB < RssiThresh) {
+ btRssiState = BT_RSSI_STATE_LOW;
+ pHalData->bt_coexist.CurrentState |= BT_COEX_STATE_WIFI_RSSI_LOW;
+ pHalData->bt_coexist.CurrentState &= ~BT_COEX_STATE_WIFI_RSSI_HIGH;
+ RTPRINT(FBT, BT_TRACE, ("[DM][BT], RSSI state switch to Low\n"));
+ } else {
+ btRssiState = BT_RSSI_STATE_STAY_HIGH;
+ RTPRINT(FBT, BT_TRACE, ("[DM][BT], RSSI state stay at High\n"));
+ }
+ }
+ } else if (levelNum == 3) {
+ if (RssiThresh > RssiThresh1) {
+ RTPRINT(FBT, BT_TRACE, ("[DM][BT], RSSI thresh error!!\n"));
+ return pHalData->bt_coexist.preRssiState;
+ }
+
+ if ((pHalData->bt_coexist.preRssiState == BT_RSSI_STATE_LOW) ||
+ (pHalData->bt_coexist.preRssiState == BT_RSSI_STATE_STAY_LOW)) {
+ if (UndecoratedSmoothedPWDB >= (RssiThresh+BT_FW_COEX_THRESH_TOL)) {
+ btRssiState = BT_RSSI_STATE_MEDIUM;
+ pHalData->bt_coexist.CurrentState |= BT_COEX_STATE_WIFI_RSSI_MEDIUM;
+ pHalData->bt_coexist.CurrentState &= ~BT_COEX_STATE_WIFI_RSSI_LOW;
+ pHalData->bt_coexist.CurrentState &= ~BT_COEX_STATE_WIFI_RSSI_HIGH;
+ RTPRINT(FBT, BT_TRACE, ("[DM][BT], RSSI state switch to Medium\n"));
+ } else {
+ btRssiState = BT_RSSI_STATE_STAY_LOW;
+ RTPRINT(FBT, BT_TRACE, ("[DM][BT], RSSI state stay at Low\n"));
+ }
+ } else if ((pHalData->bt_coexist.preRssiState == BT_RSSI_STATE_MEDIUM) ||
+ (pHalData->bt_coexist.preRssiState == BT_RSSI_STATE_STAY_MEDIUM)) {
+ if (UndecoratedSmoothedPWDB >= (RssiThresh1+BT_FW_COEX_THRESH_TOL)) {
+ btRssiState = BT_RSSI_STATE_HIGH;
+ pHalData->bt_coexist.CurrentState |= BT_COEX_STATE_WIFI_RSSI_HIGH;
+ pHalData->bt_coexist.CurrentState &= ~BT_COEX_STATE_WIFI_RSSI_LOW;
+ pHalData->bt_coexist.CurrentState &= ~BT_COEX_STATE_WIFI_RSSI_MEDIUM;
+ RTPRINT(FBT, BT_TRACE, ("[DM][BT], RSSI state switch to High\n"));
+ } else if (UndecoratedSmoothedPWDB < RssiThresh) {
+ btRssiState = BT_RSSI_STATE_LOW;
+ pHalData->bt_coexist.CurrentState |= BT_COEX_STATE_WIFI_RSSI_LOW;
+ pHalData->bt_coexist.CurrentState &= ~BT_COEX_STATE_WIFI_RSSI_HIGH;
+ pHalData->bt_coexist.CurrentState &= ~BT_COEX_STATE_WIFI_RSSI_MEDIUM;
+ RTPRINT(FBT, BT_TRACE, ("[DM][BT], RSSI state switch to Low\n"));
+ } else {
+ btRssiState = BT_RSSI_STATE_STAY_MEDIUM;
+ RTPRINT(FBT, BT_TRACE, ("[DM][BT], RSSI state stay at Medium\n"));
+ }
+ } else {
+ if (UndecoratedSmoothedPWDB < RssiThresh1) {
+ btRssiState = BT_RSSI_STATE_MEDIUM;
+ pHalData->bt_coexist.CurrentState |= BT_COEX_STATE_WIFI_RSSI_MEDIUM;
+ pHalData->bt_coexist.CurrentState &= ~BT_COEX_STATE_WIFI_RSSI_HIGH;
+ pHalData->bt_coexist.CurrentState &= ~BT_COEX_STATE_WIFI_RSSI_LOW;
+ RTPRINT(FBT, BT_TRACE, ("[DM][BT], RSSI state switch to Medium\n"));
+ } else {
+ btRssiState = BT_RSSI_STATE_STAY_HIGH;
+ RTPRINT(FBT, BT_TRACE, ("[DM][BT], RSSI state stay at High\n"));
+ }
+ }
+ }
+
+ pHalData->bt_coexist.preRssiState = btRssiState;
+
+ return btRssiState;
+}
+
+bool rtl8723a_BT_disable_EDCA_turbo(struct rtw_adapter *padapter)
+{
+ struct bt_mgnt *pBtMgnt;
+ struct hal_data_8723a *pHalData;
+ u8 bBtChangeEDCA = false;
+ u32 EDCA_BT_BE = 0x5ea42b, cur_EDCA_reg;
+ bool bRet = false;
+
+ pHalData = GET_HAL_DATA(padapter);
+ pBtMgnt = &pHalData->BtInfo.BtMgnt;
+
+ if (!rtl8723a_BT_coexist(padapter)) {
+ bRet = false;
+ pHalData->bt_coexist.lastBtEdca = 0;
+ return bRet;
+ }
+ if (!((pBtMgnt->bSupportProfile) ||
+ (pHalData->bt_coexist.BT_CoexistType == BT_CSR_BC8))) {
+ bRet = false;
+ pHalData->bt_coexist.lastBtEdca = 0;
+ return bRet;
+ }
+
+ if (rtl8723a_BT_using_antenna_1(padapter)) {
+ bRet = false;
+ pHalData->bt_coexist.lastBtEdca = 0;
+ return bRet;
+ }
+
+ if (pHalData->bt_coexist.exec_cnt < 3)
+ pHalData->bt_coexist.exec_cnt++;
+ else
+ pHalData->bt_coexist.bEDCAInitialized = true;
+
+ /* When BT is non idle */
+ if (!(pHalData->bt_coexist.CurrentState & BT_COEX_STATE_BT_IDLE)) {
+ RTPRINT(FBT, BT_TRACE, ("BT state non idle, set bt EDCA\n"));
+
+ /* aggr_num = 0x0909; */
+ if (pHalData->odmpriv.DM_EDCA_Table.bCurrentTurboEDCA) {
+ bBtChangeEDCA = true;
+ pHalData->odmpriv.DM_EDCA_Table.bCurrentTurboEDCA = false;
+ pHalData->dmpriv.prv_traffic_idx = 3;
+ }
+ cur_EDCA_reg = rtl8723au_read32(padapter, REG_EDCA_BE_PARAM);
+
+ if (cur_EDCA_reg != EDCA_BT_BE)
+ bBtChangeEDCA = true;
+ if (bBtChangeEDCA || !pHalData->bt_coexist.bEDCAInitialized) {
+ rtl8723au_write32(padapter, REG_EDCA_BE_PARAM,
+ EDCA_BT_BE);
+ pHalData->bt_coexist.lastBtEdca = EDCA_BT_BE;
+ }
+ bRet = true;
+ } else {
+ RTPRINT(FBT, BT_TRACE, ("BT state idle, set original EDCA\n"));
+ pHalData->bt_coexist.lastBtEdca = 0;
+ bRet = false;
+ }
+ return bRet;
+}
+
+void
+BTDM_Balance(
+ struct rtw_adapter *padapter,
+ u8 bBalanceOn,
+ u8 ms0,
+ u8 ms1
+ )
+{
+ struct hal_data_8723a *pHalData = GET_HAL_DATA(padapter);
+ u8 H2C_Parameter[3] = {0};
+
+ if (bBalanceOn) {
+ H2C_Parameter[2] = 1;
+ H2C_Parameter[1] = ms1;
+ H2C_Parameter[0] = ms0;
+ pHalData->bt_coexist.bFWCoexistAllOff = false;
+ } else {
+ H2C_Parameter[2] = 0;
+ H2C_Parameter[1] = 0;
+ H2C_Parameter[0] = 0;
+ }
+ pHalData->bt_coexist.bBalanceOn = bBalanceOn;
+
+ RTPRINT(FBT, BT_TRACE, ("[DM][BT], Balance =[%s:%dms:%dms], write 0xc = 0x%x\n",
+ bBalanceOn?"ON":"OFF", ms0, ms1,
+ H2C_Parameter[0]<<16|H2C_Parameter[1]<<8|H2C_Parameter[2]));
+
+ FillH2CCmd(padapter, 0xc, 3, H2C_Parameter);
+}
+
+void BTDM_AGCTable(struct rtw_adapter *padapter, u8 type)
+{
+ struct hal_data_8723a *pHalData = GET_HAL_DATA(padapter);
+ if (type == BT_AGCTABLE_OFF) {
+ RTPRINT(FBT, BT_TRACE, ("[BT]AGCTable Off!\n"));
+ rtl8723au_write32(padapter, 0xc78, 0x641c0001);
+ rtl8723au_write32(padapter, 0xc78, 0x631d0001);
+ rtl8723au_write32(padapter, 0xc78, 0x621e0001);
+ rtl8723au_write32(padapter, 0xc78, 0x611f0001);
+ rtl8723au_write32(padapter, 0xc78, 0x60200001);
+
+ PHY_SetRFReg(padapter, PathA, RF_RX_AGC_HP, bRFRegOffsetMask, 0x32000);
+ PHY_SetRFReg(padapter, PathA, RF_RX_AGC_HP, bRFRegOffsetMask, 0x71000);
+ PHY_SetRFReg(padapter, PathA, RF_RX_AGC_HP, bRFRegOffsetMask, 0xb0000);
+ PHY_SetRFReg(padapter, PathA, RF_RX_AGC_HP, bRFRegOffsetMask, 0xfc000);
+ PHY_SetRFReg(padapter, PathA, RF_RX_G1, bRFRegOffsetMask, 0x30355);
+
+ pHalData->bt_coexist.b8723aAgcTableOn = false;
+ } else if (type == BT_AGCTABLE_ON) {
+ RTPRINT(FBT, BT_TRACE, ("[BT]AGCTable On!\n"));
+ rtl8723au_write32(padapter, 0xc78, 0x4e1c0001);
+ rtl8723au_write32(padapter, 0xc78, 0x4d1d0001);
+ rtl8723au_write32(padapter, 0xc78, 0x4c1e0001);
+ rtl8723au_write32(padapter, 0xc78, 0x4b1f0001);
+ rtl8723au_write32(padapter, 0xc78, 0x4a200001);
+
+ PHY_SetRFReg(padapter, PathA, RF_RX_AGC_HP, bRFRegOffsetMask, 0xdc000);
+ PHY_SetRFReg(padapter, PathA, RF_RX_AGC_HP, bRFRegOffsetMask, 0x90000);
+ PHY_SetRFReg(padapter, PathA, RF_RX_AGC_HP, bRFRegOffsetMask, 0x51000);
+ PHY_SetRFReg(padapter, PathA, RF_RX_AGC_HP, bRFRegOffsetMask, 0x12000);
+ PHY_SetRFReg(padapter, PathA, RF_RX_G1, bRFRegOffsetMask, 0x00355);
+
+ pHalData->bt_coexist.b8723aAgcTableOn = true;
+
+ pHalData->bt_coexist.bSWCoexistAllOff = false;
+ }
+}
+
+void BTDM_BBBackOffLevel(struct rtw_adapter *padapter, u8 type)
+{
+ struct hal_data_8723a *pHalData = GET_HAL_DATA(padapter);
+
+ if (type == BT_BB_BACKOFF_OFF) {
+ RTPRINT(FBT, BT_TRACE, ("[BT]BBBackOffLevel Off!\n"));
+ rtl8723au_write32(padapter, 0xc04, 0x3a05611);
+ } else if (type == BT_BB_BACKOFF_ON) {
+ RTPRINT(FBT, BT_TRACE, ("[BT]BBBackOffLevel On!\n"));
+ rtl8723au_write32(padapter, 0xc04, 0x3a07611);
+ pHalData->bt_coexist.bSWCoexistAllOff = false;
+ }
+}
+
+void BTDM_FWCoexAllOff(struct rtw_adapter *padapter)
+{
+ struct hal_data_8723a *pHalData = GET_HAL_DATA(padapter);
+
+ RTPRINT(FBT, BT_TRACE, ("BTDM_FWCoexAllOff()\n"));
+ if (pHalData->bt_coexist.bFWCoexistAllOff)
+ return;
+ RTPRINT(FBT, BT_TRACE, ("BTDM_FWCoexAllOff(), real Do\n"));
+
+ BTDM_FWCoexAllOff8723A(padapter);
+
+ pHalData->bt_coexist.bFWCoexistAllOff = true;
+}
+
+void BTDM_SWCoexAllOff(struct rtw_adapter *padapter)
+{
+ struct hal_data_8723a *pHalData = GET_HAL_DATA(padapter);
+
+ RTPRINT(FBT, BT_TRACE, ("BTDM_SWCoexAllOff()\n"));
+ if (pHalData->bt_coexist.bSWCoexistAllOff)
+ return;
+ RTPRINT(FBT, BT_TRACE, ("BTDM_SWCoexAllOff(), real Do\n"));
+ BTDM_SWCoexAllOff8723A(padapter);
+
+ pHalData->bt_coexist.bSWCoexistAllOff = true;
+}
+
+void BTDM_HWCoexAllOff(struct rtw_adapter *padapter)
+{
+ struct hal_data_8723a *pHalData = GET_HAL_DATA(padapter);
+
+ RTPRINT(FBT, BT_TRACE, ("BTDM_HWCoexAllOff()\n"));
+ if (pHalData->bt_coexist.bHWCoexistAllOff)
+ return;
+ RTPRINT(FBT, BT_TRACE, ("BTDM_HWCoexAllOff(), real Do\n"));
+
+ BTDM_HWCoexAllOff8723A(padapter);
+
+ pHalData->bt_coexist.bHWCoexistAllOff = true;
+}
+
+void BTDM_CoexAllOff(struct rtw_adapter *padapter)
+{
+ BTDM_FWCoexAllOff(padapter);
+ BTDM_SWCoexAllOff(padapter);
+ BTDM_HWCoexAllOff(padapter);
+}
+
+void rtl8723a_BT_disable_coexist(struct rtw_adapter *padapter)
+{
+ struct pwrctrl_priv *ppwrctrl = &padapter->pwrctrlpriv;
+
+ if (!rtl8723a_BT_coexist(padapter))
+ return;
+
+ /* 8723 1Ant doesn't need to turn off bt coexist mechanism. */
+ if (rtl8723a_BT_using_antenna_1(padapter))
+ return;
+
+ /* Before enter IPS, turn off FW BT Co-exist mechanism */
+ if (ppwrctrl->reg_rfoff == rf_on) {
+ RTPRINT(FBT, BT_TRACE, ("[BT][DM], Before enter IPS, turn off all Coexist DM\n"));
+ btdm_ResetFWCoexState(padapter);
+ BTDM_CoexAllOff(padapter);
+ BTDM_SetAntenna(padapter, BTDM_ANT_BT);
+ }
+}
+
+void BTDM_SignalCompensation(struct rtw_adapter *padapter, u8 *rssi_wifi, u8 *rssi_bt)
+{
+ BTDM_8723ASignalCompensation(padapter, rssi_wifi, rssi_bt);
+}
+
+void rtl8723a_BT_do_coexist(struct rtw_adapter *padapter)
+{
+ struct hal_data_8723a *pHalData = GET_HAL_DATA(padapter);
+
+ if (!rtl8723a_BT_coexist(padapter)) {
+ RTPRINT(FBT, BT_TRACE, ("[DM][BT], BT not exists!!\n"));
+ return;
+ }
+
+ if (!pHalData->bt_coexist.bInitlized) {
+ RTPRINT(FBT, BT_TRACE, ("[DM][BT], btdm_InitBtCoexistDM()\n"));
+ btdm_InitBtCoexistDM(padapter);
+ }
+
+ RTPRINT(FBT, BT_TRACE, ("\n\n[DM][BT], BTDM start!!\n"));
+
+ BTDM_PWDBMonitor(padapter);
+
+ RTPRINT(FBT, BT_TRACE, ("[DM][BT], HW type is 8723\n"));
+ BTDM_BTCoexist8723A(padapter);
+ RTPRINT(FBT, BT_TRACE, ("[DM][BT], BTDM end!!\n\n"));
+}
+
+void BTDM_UpdateCoexState(struct rtw_adapter *padapter)
+{
+ struct hal_data_8723a *pHalData = GET_HAL_DATA(padapter);
+
+ if (!BTDM_IsSameCoexistState(padapter)) {
+ RTPRINT(FBT, BT_TRACE, ("[BTCoex], Coexist State[bitMap] change from 0x%"i64fmt"x to 0x%"i64fmt"x, changeBits = 0x%"i64fmt"x\n",
+ pHalData->bt_coexist.PreviousState,
+ pHalData->bt_coexist.CurrentState,
+ (pHalData->bt_coexist.PreviousState^pHalData->bt_coexist.CurrentState)));
+ pHalData->bt_coexist.PreviousState = pHalData->bt_coexist.CurrentState;
+ }
+}
+
+u8 BTDM_IsSameCoexistState(struct rtw_adapter *padapter)
+{
+ struct hal_data_8723a *pHalData = GET_HAL_DATA(padapter);
+
+ if (pHalData->bt_coexist.PreviousState == pHalData->bt_coexist.CurrentState) {
+ return true;
+ } else {
+ RTPRINT(FBT, BT_TRACE, ("[DM][BT], Coexist state changed!!\n"));
+ return false;
+ }
+}
+
+void BTDM_PWDBMonitor(struct rtw_adapter *padapter)
+{
+ struct bt_30info *pBTInfo = GET_BT_INFO(GetDefaultAdapter(padapter));
+ struct bt_mgnt *pBtMgnt = &pBTInfo->BtMgnt;
+ struct hal_data_8723a *pHalData = GET_HAL_DATA(padapter);
+ u8 H2C_Parameter[3] = {0};
+ s32 tmpBTEntryMaxPWDB = 0, tmpBTEntryMinPWDB = 0xff;
+ u8 i;
+
+ if (pBtMgnt->BtOperationOn) {
+ for (i = 0; i < MAX_BT_ASOC_ENTRY_NUM; i++) {
+ if (pBTInfo->BtAsocEntry[i].bUsed) {
+ if (pBTInfo->BtAsocEntry[i].UndecoratedSmoothedPWDB < tmpBTEntryMinPWDB)
+ tmpBTEntryMinPWDB = pBTInfo->BtAsocEntry[i].UndecoratedSmoothedPWDB;
+ if (pBTInfo->BtAsocEntry[i].UndecoratedSmoothedPWDB > tmpBTEntryMaxPWDB)
+ tmpBTEntryMaxPWDB = pBTInfo->BtAsocEntry[i].UndecoratedSmoothedPWDB;
+ /* Report every BT connection (HS mode) RSSI to FW */
+ H2C_Parameter[2] = (u8)(pBTInfo->BtAsocEntry[i].UndecoratedSmoothedPWDB & 0xFF);
+ H2C_Parameter[0] = (MAX_FW_SUPPORT_MACID_NUM-1-i);
+ RTPRINT(FDM, DM_BT30, ("RSSI report for BT[%d], H2C_Par = 0x%x\n", i, H2C_Parameter[0]));
+ FillH2CCmd(padapter, RSSI_SETTING_EID, 3, H2C_Parameter);
+ RTPRINT_ADDR(FDM, (DM_PWDB|DM_BT30), ("BT_Entry Mac :"),
+ pBTInfo->BtAsocEntry[i].BTRemoteMACAddr)
+ RTPRINT(FDM, (DM_PWDB|DM_BT30),
+ ("BT rx pwdb[%d] = 0x%x(%d)\n", i,
+ pBTInfo->BtAsocEntry[i].UndecoratedSmoothedPWDB,
+ pBTInfo->BtAsocEntry[i].UndecoratedSmoothedPWDB));
+ }
+ }
+ if (tmpBTEntryMaxPWDB != 0) { /* If associated entry is found */
+ pHalData->dmpriv.BT_EntryMaxUndecoratedSmoothedPWDB = tmpBTEntryMaxPWDB;
+ RTPRINT(FDM, (DM_PWDB|DM_BT30), ("BT_EntryMaxPWDB = 0x%x(%d)\n",
+ tmpBTEntryMaxPWDB, tmpBTEntryMaxPWDB));
+ } else {
+ pHalData->dmpriv.BT_EntryMaxUndecoratedSmoothedPWDB = 0;
+ }
+ if (tmpBTEntryMinPWDB != 0xff) { /* If associated entry is found */
+ pHalData->dmpriv.BT_EntryMinUndecoratedSmoothedPWDB = tmpBTEntryMinPWDB;
+ RTPRINT(FDM, (DM_PWDB|DM_BT30), ("BT_EntryMinPWDB = 0x%x(%d)\n",
+ tmpBTEntryMinPWDB, tmpBTEntryMinPWDB));
+ } else {
+ pHalData->dmpriv.BT_EntryMinUndecoratedSmoothedPWDB = 0;
+ }
+ }
+}
+
+u8 BTDM_IsBTBusy(struct rtw_adapter *padapter)
+{
+ struct bt_30info *pBTInfo = GET_BT_INFO(padapter);
+ struct bt_mgnt *pBtMgnt = &pBTInfo->BtMgnt;
+
+ if (pBtMgnt->ExtConfig.bBTBusy)
+ return true;
+ else
+ return false;
+}
+
+u8 BTDM_IsWifiBusy(struct rtw_adapter *padapter)
+{
+/*PMGNT_INFO pMgntInfo = &GetDefaultAdapter(padapter)->MgntInfo; */
+ struct mlme_priv *pmlmepriv = &GetDefaultAdapter(padapter)->mlmepriv;
+ struct bt_30info *pBTInfo = GET_BT_INFO(padapter);
+ struct bt_traffic *pBtTraffic = &pBTInfo->BtTraffic;
+
+ if (pmlmepriv->LinkDetectInfo.bBusyTraffic ||
+ pBtTraffic->Bt30TrafficStatistics.bTxBusyTraffic ||
+ pBtTraffic->Bt30TrafficStatistics.bRxBusyTraffic)
+ return true;
+ else
+ return false;
+}
+
+u8 BTDM_IsCoexistStateChanged(struct rtw_adapter *padapter)
+{
+ struct hal_data_8723a *pHalData = GET_HAL_DATA(padapter);
+
+ if (pHalData->bt_coexist.PreviousState == pHalData->bt_coexist.CurrentState)
+ return false;
+ else
+ return true;
+}
+
+u8 BTDM_IsWifiUplink(struct rtw_adapter *padapter)
+{
+/*PMGNT_INFO pMgntInfo = &GetDefaultAdapter(padapter)->MgntInfo; */
+ struct mlme_priv *pmlmepriv;
+ struct bt_30info *pBTInfo;
+ struct bt_traffic *pBtTraffic;
+
+ pmlmepriv = &padapter->mlmepriv;
+ pBTInfo = GET_BT_INFO(padapter);
+ pBtTraffic = &pBTInfo->BtTraffic;
+
+ if ((pmlmepriv->LinkDetectInfo.bTxBusyTraffic) ||
+ (pBtTraffic->Bt30TrafficStatistics.bTxBusyTraffic))
+ return true;
+ else
+ return false;
+}
+
+u8 BTDM_IsWifiDownlink(struct rtw_adapter *padapter)
+{
+/*PMGNT_INFO pMgntInfo = &GetDefaultAdapter(padapter)->MgntInfo; */
+ struct mlme_priv *pmlmepriv;
+ struct bt_30info *pBTInfo;
+ struct bt_traffic *pBtTraffic;
+
+ pmlmepriv = &padapter->mlmepriv;
+ pBTInfo = GET_BT_INFO(padapter);
+ pBtTraffic = &pBTInfo->BtTraffic;
+
+ if ((pmlmepriv->LinkDetectInfo.bRxBusyTraffic) ||
+ (pBtTraffic->Bt30TrafficStatistics.bRxBusyTraffic))
+ return true;
+ else
+ return false;
+}
+
+u8 BTDM_IsBTHSMode(struct rtw_adapter *padapter)
+{
+/*PMGNT_INFO pMgntInfo = &GetDefaultAdapter(padapter)->MgntInfo; */
+ struct hal_data_8723a *pHalData;
+ struct bt_mgnt *pBtMgnt;
+
+ pHalData = GET_HAL_DATA(padapter);
+ pBtMgnt = &pHalData->BtInfo.BtMgnt;
+
+ if (pBtMgnt->BtOperationOn)
+ return true;
+ else
+ return false;
+}
+
+u8 BTDM_IsBTUplink(struct rtw_adapter *padapter)
+{
+ struct hal_data_8723a *pHalData = GET_HAL_DATA(padapter);
+
+ if (pHalData->bt_coexist.BT21TrafficStatistics.bTxBusyTraffic)
+ return true;
+ else
+ return false;
+}
+
+u8 BTDM_IsBTDownlink(struct rtw_adapter *padapter)
+{
+ struct hal_data_8723a *pHalData = GET_HAL_DATA(padapter);
+
+ if (pHalData->bt_coexist.BT21TrafficStatistics.bRxBusyTraffic)
+ return true;
+ else
+ return false;
+}
+
+void BTDM_AdjustForBtOperation(struct rtw_adapter *padapter)
+{
+ RTPRINT(FBT, BT_TRACE, ("[BT][DM], BTDM_AdjustForBtOperation()\n"));
+ BTDM_AdjustForBtOperation8723A(padapter);
+}
+
+void BTDM_SetBtCoexCurrAntNum(struct rtw_adapter *padapter, u8 antNum)
+{
+ BTDM_Set8723ABtCoexCurrAntNum(padapter, antNum);
+}
+
+void BTDM_ForHalt(struct rtw_adapter *padapter)
+{
+ if (!rtl8723a_BT_coexist(padapter))
+ return;
+
+ BTDM_ForHalt8723A(padapter);
+ GET_HAL_DATA(padapter)->bt_coexist.bInitlized = false;
+}
+
+void BTDM_WifiScanNotify(struct rtw_adapter *padapter, u8 scanType)
+{
+ if (!rtl8723a_BT_coexist(padapter))
+ return;
+
+ BTDM_WifiScanNotify8723A(padapter, scanType);
+}
+
+void BTDM_WifiAssociateNotify(struct rtw_adapter *padapter, u8 action)
+{
+ if (!rtl8723a_BT_coexist(padapter))
+ return;
+
+ BTDM_WifiAssociateNotify8723A(padapter, action);
+}
+
+void rtl8723a_BT_mediastatus_notify(struct rtw_adapter *padapter,
+ enum rt_media_status mstatus)
+{
+ if (!rtl8723a_BT_coexist(padapter))
+ return;
+
+ BTDM_MediaStatusNotify8723A(padapter, mstatus);
+}
+
+void rtl8723a_BT_specialpacket_notify(struct rtw_adapter *padapter)
+{
+ if (!rtl8723a_BT_coexist(padapter))
+ return;
+
+ BTDM_ForDhcp8723A(padapter);
+}
+
+void BTDM_ResetActionProfileState(struct rtw_adapter *padapter)
+{
+ struct hal_data_8723a *pHalData = GET_HAL_DATA(padapter);
+
+ pHalData->bt_coexist.CurrentState &= ~\
+ (BT_COEX_STATE_PROFILE_HID|BT_COEX_STATE_PROFILE_A2DP|
+ BT_COEX_STATE_PROFILE_PAN|BT_COEX_STATE_PROFILE_SCO);
+}
+
+u8 BTDM_IsActionSCO(struct rtw_adapter *padapter)
+{
+ struct hal_data_8723a *pHalData;
+ struct bt_30info *pBTInfo;
+ struct bt_mgnt *pBtMgnt;
+ struct bt_dgb *pBtDbg;
+ u8 bRet;
+
+ pHalData = GET_HAL_DATA(padapter);
+ pBTInfo = GET_BT_INFO(padapter);
+ pBtMgnt = &pBTInfo->BtMgnt;
+ pBtDbg = &pBTInfo->BtDbg;
+ bRet = false;
+
+ if (pBtDbg->dbgCtrl) {
+ if (pBtDbg->dbgProfile == BT_DBG_PROFILE_SCO) {
+ pHalData->bt_coexist.CurrentState |= BT_COEX_STATE_PROFILE_SCO;
+ bRet = true;
+ }
+ } else {
+ if (pBtMgnt->ExtConfig.NumberOfSCO > 0) {
+ pHalData->bt_coexist.CurrentState |= BT_COEX_STATE_PROFILE_SCO;
+ bRet = true;
+ }
+ }
+ return bRet;
+}
+
+u8 BTDM_IsActionHID(struct rtw_adapter *padapter)
+{
+ struct bt_30info *pBTInfo;
+ struct hal_data_8723a *pHalData;
+ struct bt_mgnt *pBtMgnt;
+ struct bt_dgb *pBtDbg;
+ u8 bRet;
+
+ pHalData = GET_HAL_DATA(padapter);
+ pBTInfo = GET_BT_INFO(padapter);
+ pBtMgnt = &pBTInfo->BtMgnt;
+ pBtDbg = &pBTInfo->BtDbg;
+ bRet = false;
+
+ if (pBtDbg->dbgCtrl) {
+ if (pBtDbg->dbgProfile == BT_DBG_PROFILE_HID) {
+ pHalData->bt_coexist.CurrentState |= BT_COEX_STATE_PROFILE_HID;
+ bRet = true;
+ }
+ } else {
+ if (BTHCI_CheckProfileExist(padapter, BT_PROFILE_HID) &&
+ pBtMgnt->ExtConfig.NumberOfHandle == 1) {
+ pHalData->bt_coexist.CurrentState |= BT_COEX_STATE_PROFILE_HID;
+ bRet = true;
+ }
+ }
+ return bRet;
+}
+
+u8 BTDM_IsActionA2DP(struct rtw_adapter *padapter)
+{
+ struct hal_data_8723a *pHalData;
+ struct bt_30info *pBTInfo;
+ struct bt_mgnt *pBtMgnt;
+ struct bt_dgb *pBtDbg;
+ u8 bRet;
+
+ pHalData = GET_HAL_DATA(padapter);
+ pBTInfo = GET_BT_INFO(padapter);
+ pBtMgnt = &pBTInfo->BtMgnt;
+ pBtDbg = &pBTInfo->BtDbg;
+ bRet = false;
+
+ if (pBtDbg->dbgCtrl) {
+ if (pBtDbg->dbgProfile == BT_DBG_PROFILE_A2DP) {
+ pHalData->bt_coexist.CurrentState |= BT_COEX_STATE_PROFILE_A2DP;
+ bRet = true;
+ }
+ } else {
+ if (BTHCI_CheckProfileExist(padapter, BT_PROFILE_A2DP) &&
+ pBtMgnt->ExtConfig.NumberOfHandle == 1) {
+ pHalData->bt_coexist.CurrentState |= BT_COEX_STATE_PROFILE_A2DP;
+ bRet = true;
+ }
+ }
+ return bRet;
+}
+
+u8 BTDM_IsActionPAN(struct rtw_adapter *padapter)
+{
+ struct hal_data_8723a *pHalData;
+ struct bt_30info *pBTInfo;
+ struct bt_mgnt *pBtMgnt;
+ struct bt_dgb *pBtDbg;
+ u8 bRet;
+
+ pHalData = GET_HAL_DATA(padapter);
+ pBTInfo = GET_BT_INFO(padapter);
+ pBtMgnt = &pBTInfo->BtMgnt;
+ pBtDbg = &pBTInfo->BtDbg;
+ bRet = false;
+
+ if (pBtDbg->dbgCtrl) {
+ if (pBtDbg->dbgProfile == BT_DBG_PROFILE_PAN) {
+ pHalData->bt_coexist.CurrentState |= BT_COEX_STATE_PROFILE_PAN;
+ bRet = true;
+ }
+ } else {
+ if (BTHCI_CheckProfileExist(padapter, BT_PROFILE_PAN) &&
+ pBtMgnt->ExtConfig.NumberOfHandle == 1) {
+ pHalData->bt_coexist.CurrentState |= BT_COEX_STATE_PROFILE_PAN;
+ bRet = true;
+ }
+ }
+ return bRet;
+}
+
+u8 BTDM_IsActionHIDA2DP(struct rtw_adapter *padapter)
+{
+ struct hal_data_8723a *pHalData;
+ struct bt_30info *pBTInfo;
+ struct bt_mgnt *pBtMgnt;
+ struct bt_dgb *pBtDbg;
+ u8 bRet;
+
+ pHalData = GET_HAL_DATA(padapter);
+ pBTInfo = GET_BT_INFO(padapter);
+ pBtMgnt = &pBTInfo->BtMgnt;
+ pBtDbg = &pBTInfo->BtDbg;
+ bRet = false;
+
+ if (pBtDbg->dbgCtrl) {
+ if (pBtDbg->dbgProfile == BT_DBG_PROFILE_HID_A2DP) {
+ pHalData->bt_coexist.CurrentState |= (BT_COEX_STATE_PROFILE_HID|BT_COEX_STATE_PROFILE_A2DP);
+ bRet = true;
+ }
+ } else {
+ if (BTHCI_CheckProfileExist(padapter, BT_PROFILE_HID) &&
+ BTHCI_CheckProfileExist(padapter, BT_PROFILE_A2DP)) {
+ pHalData->bt_coexist.CurrentState |= (BT_COEX_STATE_PROFILE_HID|BT_COEX_STATE_PROFILE_A2DP);
+ bRet = true;
+ }
+ }
+ return bRet;
+}
+
+u8 BTDM_IsActionHIDPAN(struct rtw_adapter *padapter)
+{
+ struct hal_data_8723a *pHalData;
+ struct bt_30info *pBTInfo;
+ struct bt_dgb *pBtDbg;
+ u8 bRet;
+
+ pHalData = GET_HAL_DATA(padapter);
+ pBTInfo = GET_BT_INFO(padapter);
+ pBtDbg = &pBTInfo->BtDbg;
+ bRet = false;
+
+ if (pBtDbg->dbgCtrl) {
+ if (pBtDbg->dbgProfile == BT_DBG_PROFILE_HID_PAN) {
+ pHalData->bt_coexist.CurrentState |= (BT_COEX_STATE_PROFILE_HID|BT_COEX_STATE_PROFILE_PAN);
+ bRet = true;
+ }
+ } else {
+ if (BTHCI_CheckProfileExist(padapter, BT_PROFILE_HID) &&
+ BTHCI_CheckProfileExist(padapter, BT_PROFILE_PAN)) {
+ pHalData->bt_coexist.CurrentState |= (BT_COEX_STATE_PROFILE_HID|BT_COEX_STATE_PROFILE_PAN);
+ bRet = true;
+ }
+ }
+ return bRet;
+}
+
+u8 BTDM_IsActionPANA2DP(struct rtw_adapter *padapter)
+{
+ struct hal_data_8723a *pHalData;
+ struct bt_30info *pBTInfo;
+ struct bt_dgb *pBtDbg;
+ u8 bRet;
+
+ pHalData = GET_HAL_DATA(padapter);
+ pBTInfo = GET_BT_INFO(padapter);
+ pBtDbg = &pBTInfo->BtDbg;
+ bRet = false;
+
+ if (pBtDbg->dbgCtrl) {
+ if (pBtDbg->dbgProfile == BT_DBG_PROFILE_PAN_A2DP) {
+ pHalData->bt_coexist.CurrentState |= (BT_COEX_STATE_PROFILE_PAN|BT_COEX_STATE_PROFILE_A2DP);
+ bRet = true;
+ }
+ } else {
+ if (BTHCI_CheckProfileExist(padapter, BT_PROFILE_PAN) && BTHCI_CheckProfileExist(padapter, BT_PROFILE_A2DP)) {
+ pHalData->bt_coexist.CurrentState |= (BT_COEX_STATE_PROFILE_PAN|BT_COEX_STATE_PROFILE_A2DP);
+ bRet = true;
+ }
+ }
+ return bRet;
+}
+
+bool rtl8723a_BT_enabled(struct rtw_adapter *padapter)
+{
+ struct hal_data_8723a *pHalData = GET_HAL_DATA(padapter);
+
+ if (pHalData->bt_coexist.bCurBtDisabled)
+ return false;
+ else
+ return true;
+}
+
+/* ===== End of sync from SD7 driver HAL/BTCoexist/HalBtCoexist.c ===== */
+
+/* ===== Below this line is sync from SD7 driver HAL/HalBT.c ===== */
+
+/* */
+/*local function */
+/* */
+
+static void halbt_InitHwConfig8723A(struct rtw_adapter *padapter)
+{
+}
+
+/* */
+/*extern function */
+/* */
+u8 HALBT_GetPGAntNum(struct rtw_adapter *padapter)
+{
+ struct hal_data_8723a *pHalData = GET_HAL_DATA(padapter);
+
+ return pHalData->bt_coexist.BT_Ant_Num;
+}
+
+void HALBT_SetKey(struct rtw_adapter *padapter, u8 EntryNum)
+{
+ struct bt_30info *pBTinfo;
+ struct bt_asoc_entry *pBtAssocEntry;
+ u16 usConfig = 0;
+
+ pBTinfo = GET_BT_INFO(padapter);
+ pBtAssocEntry = &pBTinfo->BtAsocEntry[EntryNum];
+
+ pBtAssocEntry->HwCAMIndex = BT_HWCAM_STAR + EntryNum;
+
+ usConfig = CAM_VALID | (CAM_AES << 2);
+ rtl8723a_cam_write(padapter, pBtAssocEntry->HwCAMIndex, usConfig,
+ pBtAssocEntry->BTRemoteMACAddr,
+ pBtAssocEntry->PTK + TKIP_ENC_KEY_POS);
+}
+
+void HALBT_RemoveKey(struct rtw_adapter *padapter, u8 EntryNum)
+{
+ struct bt_30info *pBTinfo;
+ struct bt_asoc_entry *pBtAssocEntry;
+
+ pBTinfo = GET_BT_INFO(padapter);
+ pBtAssocEntry = &pBTinfo->BtAsocEntry[EntryNum];
+
+ if (pBTinfo->BtAsocEntry[EntryNum].HwCAMIndex != 0) {
+ /* ToDo : add New HALBT_RemoveKey function !! */
+ if (pBtAssocEntry->HwCAMIndex >= BT_HWCAM_STAR &&
+ pBtAssocEntry->HwCAMIndex < HALF_CAM_ENTRY)
+ rtl8723a_cam_empty_entry(padapter,
+ pBtAssocEntry->HwCAMIndex);
+ pBTinfo->BtAsocEntry[EntryNum].HwCAMIndex = 0;
+ }
+}
+
+void rtl8723a_BT_init_hal_vars(struct rtw_adapter *padapter)
+{
+ struct hal_data_8723a *pHalData;
+
+ pHalData = GET_HAL_DATA(padapter);
+
+ pHalData->bt_coexist.BluetoothCoexist = pHalData->EEPROMBluetoothCoexist;
+ pHalData->bt_coexist.BT_Ant_Num = pHalData->EEPROMBluetoothAntNum;
+ pHalData->bt_coexist.BT_CoexistType = pHalData->EEPROMBluetoothType;
+ pHalData->bt_coexist.BT_Ant_isolation = pHalData->EEPROMBluetoothAntIsolation;
+ pHalData->bt_coexist.bt_radiosharedtype = pHalData->EEPROMBluetoothRadioShared;
+
+ RT_TRACE(_module_hal_init_c_, _drv_info_,
+ "BT Coexistance = 0x%x\n", rtl8723a_BT_coexist(padapter));
+
+ if (rtl8723a_BT_coexist(padapter)) {
+ if (pHalData->bt_coexist.BT_Ant_Num == Ant_x2) {
+ BTDM_SetBtCoexCurrAntNum(padapter, 2);
+ RT_TRACE(_module_hal_init_c_, _drv_info_,
+ "BlueTooth BT_Ant_Num = Antx2\n");
+ } else if (pHalData->bt_coexist.BT_Ant_Num == Ant_x1) {
+ BTDM_SetBtCoexCurrAntNum(padapter, 1);
+ RT_TRACE(_module_hal_init_c_, _drv_info_,
+ "BlueTooth BT_Ant_Num = Antx1\n");
+ }
+ pHalData->bt_coexist.bBTBusyTraffic = false;
+ pHalData->bt_coexist.bBTTrafficModeSet = false;
+ pHalData->bt_coexist.bBTNonTrafficModeSet = false;
+ pHalData->bt_coexist.CurrentState = 0;
+ pHalData->bt_coexist.PreviousState = 0;
+
+ RT_TRACE(_module_hal_init_c_, _drv_info_,
+ "bt_radiosharedType = 0x%x\n",
+ pHalData->bt_coexist.bt_radiosharedtype);
+ }
+}
+
+bool rtl8723a_BT_coexist(struct rtw_adapter *padapter)
+{
+ struct hal_data_8723a *pHalData = GET_HAL_DATA(padapter);
+
+ if (pHalData->bt_coexist.BluetoothCoexist)
+ return true;
+ else
+ return false;
+}
+
+u8 HALBT_BTChipType(struct rtw_adapter *padapter)
+{
+ struct hal_data_8723a *pHalData = GET_HAL_DATA(padapter);
+
+ return pHalData->bt_coexist.BT_CoexistType;
+}
+
+void rtl8723a_BT_init_hwconfig(struct rtw_adapter *padapter)
+{
+ halbt_InitHwConfig8723A(padapter);
+ rtl8723a_BT_do_coexist(padapter);
+}
+
+void HALBT_SetRtsCtsNoLenLimit(struct rtw_adapter *padapter)
+{
+}
+
+/* ===== End of sync from SD7 driver HAL/HalBT.c ===== */
+
+void rtl8723a_dual_antenna_detection(struct rtw_adapter *padapter)
+{
+ struct hal_data_8723a *pHalData;
+ struct dm_odm_t *pDM_Odm;
+ struct sw_ant_sw *pDM_SWAT_Table;
+ u8 i;
+
+ pHalData = GET_HAL_DATA(padapter);
+ pDM_Odm = &pHalData->odmpriv;
+ pDM_SWAT_Table = &pDM_Odm->DM_SWAT_Table;
+
+ /* */
+ /* <Roger_Notes> RTL8723A Single and Dual antenna dynamic detection
+ mechanism when RF power state is on. */
+ /* We should take power tracking, IQK, LCK, RCK RF read/write
+ operation into consideration. */
+ /* 2011.12.15. */
+ /* */
+ if (!pHalData->bAntennaDetected) {
+ u8 btAntNum = BT_GetPGAntNum(padapter);
+
+ /* Set default antenna B status */
+ if (btAntNum == Ant_x2)
+ pDM_SWAT_Table->ANTB_ON = true;
+ else if (btAntNum == Ant_x1)
+ pDM_SWAT_Table->ANTB_ON = false;
+ else
+ pDM_SWAT_Table->ANTB_ON = true;
+
+ if (pHalData->CustomerID != RT_CID_TOSHIBA) {
+ for (i = 0; i < MAX_ANTENNA_DETECTION_CNT; i++) {
+ if (ODM_SingleDualAntennaDetection
+ (&pHalData->odmpriv, ANTTESTALL) == true)
+ break;
+ }
+
+ /* Set default antenna number for BT coexistence */
+ if (btAntNum == Ant_x2)
+ BT_SetBtCoexCurrAntNum(padapter,
+ pDM_SWAT_Table->
+ ANTB_ON ? 2 : 1);
+ }
+ pHalData->bAntennaDetected = true;
+ }
+}
diff --git a/drivers/staging/rtl8723au/hal/rtl8723a_cmd.c b/drivers/staging/rtl8723au/hal/rtl8723a_cmd.c
new file mode 100644
index 000000000..11e1108d0
--- /dev/null
+++ b/drivers/staging/rtl8723au/hal/rtl8723a_cmd.c
@@ -0,0 +1,756 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2012 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ ******************************************************************************/
+#define _RTL8723A_CMD_C_
+
+#include <osdep_service.h>
+#include <drv_types.h>
+#include <recv_osdep.h>
+#include <mlme_osdep.h>
+#include <rtl8723a_hal.h>
+#include <usb_ops_linux.h>
+
+#define RTL92C_MAX_H2C_BOX_NUMS 4
+#define RTL92C_MAX_CMD_LEN 5
+#define MESSAGE_BOX_SIZE 4
+#define EX_MESSAGE_BOX_SIZE 2
+
+static u8 _is_fw_read_cmd_down(struct rtw_adapter *padapter, u8 msgbox_num)
+{
+ u8 read_down = false;
+ int retry_cnts = 100;
+ u8 valid;
+
+ do {
+ valid = rtl8723au_read8(padapter, REG_HMETFR) & BIT(msgbox_num);
+ if (0 == valid)
+ read_down = true;
+ } while ((!read_down) && (retry_cnts--));
+
+ return read_down;
+}
+
+/*****************************************
+* H2C Msg format :
+*| 31 - 8 |7 | 6 - 0 |
+*| h2c_msg |Ext_bit |CMD_ID |
+*
+******************************************/
+int FillH2CCmd(struct rtw_adapter *padapter, u8 ElementID, u32 CmdLen,
+ u8 *pCmdBuffer)
+{
+ u8 bcmd_down = false;
+ s32 retry_cnts = 100;
+ u8 h2c_box_num;
+ u32 msgbox_addr;
+ u32 msgbox_ex_addr;
+ struct hal_data_8723a *pHalData = GET_HAL_DATA(padapter);
+ u32 h2c_cmd = 0;
+ u16 h2c_cmd_ex = 0;
+ int ret = _FAIL;
+
+ padapter = GET_PRIMARY_ADAPTER(padapter);
+ pHalData = GET_HAL_DATA(padapter);
+
+ mutex_lock(&adapter_to_dvobj(padapter)->h2c_fwcmd_mutex);
+
+ if (!pCmdBuffer)
+ goto exit;
+ if (CmdLen > RTL92C_MAX_CMD_LEN)
+ goto exit;
+ if (padapter->bSurpriseRemoved == true)
+ goto exit;
+
+ /* pay attention to if race condition happened in H2C cmd setting. */
+ do {
+ h2c_box_num = pHalData->LastHMEBoxNum;
+
+ if (!_is_fw_read_cmd_down(padapter, h2c_box_num)) {
+ DBG_8723A(" fw read cmd failed...\n");
+ goto exit;
+ }
+
+ if (CmdLen <= 3) {
+ memcpy((u8 *)(&h2c_cmd)+1, pCmdBuffer, CmdLen);
+ } else {
+ memcpy((u8 *)(&h2c_cmd_ex), pCmdBuffer, EX_MESSAGE_BOX_SIZE);
+ memcpy((u8 *)(&h2c_cmd)+1, pCmdBuffer+2, (CmdLen-EX_MESSAGE_BOX_SIZE));
+ *(u8 *)(&h2c_cmd) |= BIT(7);
+ }
+
+ *(u8 *)(&h2c_cmd) |= ElementID;
+
+ if (h2c_cmd & BIT(7)) {
+ msgbox_ex_addr = REG_HMEBOX_EXT_0 + (h2c_box_num * EX_MESSAGE_BOX_SIZE);
+ h2c_cmd_ex = le16_to_cpu(h2c_cmd_ex);
+ rtl8723au_write16(padapter, msgbox_ex_addr, h2c_cmd_ex);
+ }
+ msgbox_addr = REG_HMEBOX_0 + (h2c_box_num * MESSAGE_BOX_SIZE);
+ h2c_cmd = le32_to_cpu(h2c_cmd);
+ rtl8723au_write32(padapter, msgbox_addr, h2c_cmd);
+
+ bcmd_down = true;
+
+ pHalData->LastHMEBoxNum = (h2c_box_num+1) % RTL92C_MAX_H2C_BOX_NUMS;
+
+ } while ((!bcmd_down) && (retry_cnts--));
+
+ ret = _SUCCESS;
+
+exit:
+ mutex_unlock(&adapter_to_dvobj(padapter)->h2c_fwcmd_mutex);
+ return ret;
+}
+
+int rtl8723a_set_rssi_cmd(struct rtw_adapter *padapter, u8 *param)
+{
+ *((u32 *)param) = cpu_to_le32(*((u32 *)param));
+
+ FillH2CCmd(padapter, RSSI_SETTING_EID, 3, param);
+
+ return _SUCCESS;
+}
+
+int rtl8723a_set_raid_cmd(struct rtw_adapter *padapter, u32 mask, u8 arg)
+{
+ u8 buf[5];
+
+ memset(buf, 0, 5);
+ mask = cpu_to_le32(mask);
+ memcpy(buf, &mask, 4);
+ buf[4] = arg;
+
+ FillH2CCmd(padapter, MACID_CONFIG_EID, 5, buf);
+
+ return _SUCCESS;
+}
+
+/* bitmap[0:27] = tx_rate_bitmap */
+/* bitmap[28:31]= Rate Adaptive id */
+/* arg[0:4] = macid */
+/* arg[5] = Short GI */
+void rtl8723a_add_rateatid(struct rtw_adapter *pAdapter, u32 bitmap, u8 arg, u8 rssi_level)
+{
+ struct hal_data_8723a *pHalData = GET_HAL_DATA(pAdapter);
+ u8 macid = arg & 0x1f;
+ u32 raid = bitmap & 0xf0000000;
+
+ bitmap &= 0x0fffffff;
+ if (rssi_level != DM_RATR_STA_INIT)
+ bitmap = ODM_Get_Rate_Bitmap23a(pHalData, macid, bitmap,
+ rssi_level);
+
+ bitmap |= raid;
+
+ rtl8723a_set_raid_cmd(pAdapter, bitmap, arg);
+}
+
+void rtl8723a_set_FwPwrMode_cmd(struct rtw_adapter *padapter, u8 Mode)
+{
+ struct setpwrmode_parm H2CSetPwrMode;
+ struct pwrctrl_priv *pwrpriv = &padapter->pwrctrlpriv;
+ struct hal_data_8723a *pHalData = GET_HAL_DATA(padapter);
+
+ DBG_8723A("%s: Mode =%d SmartPS =%d UAPSD =%d BcnMode = 0x%02x\n", __func__,
+ Mode, pwrpriv->smart_ps, padapter->registrypriv.uapsd_enable, pwrpriv->bcn_ant_mode);
+
+ /* Forece leave RF low power mode for 1T1R to
+ prevent conficting setting in Fw power */
+ /* saving sequence. 2010.06.07. Added by tynli.
+ Suggested by SD3 yschang. */
+ if (Mode != PS_MODE_ACTIVE && pHalData->rf_type != RF_2T2R)
+ ODM_RF_Saving23a(&pHalData->odmpriv, true);
+
+ H2CSetPwrMode.Mode = Mode;
+ H2CSetPwrMode.SmartPS = pwrpriv->smart_ps;
+ H2CSetPwrMode.AwakeInterval = 1;
+ H2CSetPwrMode.bAllQueueUAPSD = padapter->registrypriv.uapsd_enable;
+ H2CSetPwrMode.BcnAntMode = pwrpriv->bcn_ant_mode;
+
+ FillH2CCmd(padapter, SET_PWRMODE_EID, sizeof(H2CSetPwrMode), (u8 *)&H2CSetPwrMode);
+
+}
+
+static void
+ConstructBeacon(struct rtw_adapter *padapter, u8 *pframe, u32 *pLength)
+{
+ struct ieee80211_mgmt *mgmt;
+ u32 rate_len, pktlen;
+ struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv;
+ struct mlme_ext_info *pmlmeinfo = &pmlmeext->mlmext_info;
+ struct wlan_bssid_ex *cur_network = &pmlmeinfo->network;
+ u8 bc_addr[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
+
+ /* DBG_8723A("%s\n", __func__); */
+
+ mgmt = (struct ieee80211_mgmt *)pframe;
+
+ mgmt->frame_control =
+ cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_BEACON);
+
+ ether_addr_copy(mgmt->da, bc_addr);
+ ether_addr_copy(mgmt->sa, myid(&padapter->eeprompriv));
+ ether_addr_copy(mgmt->bssid, get_my_bssid23a(cur_network));
+
+ /* A Beacon frame shouldn't have fragment bits set */
+ mgmt->seq_ctrl = 0;
+
+ /* timestamp will be inserted by hardware */
+
+ put_unaligned_le16(cur_network->beacon_interval,
+ &mgmt->u.beacon.beacon_int);
+
+ put_unaligned_le16(cur_network->capability,
+ &mgmt->u.beacon.capab_info);
+
+ pframe = mgmt->u.beacon.variable;
+ pktlen = offsetof(struct ieee80211_mgmt, u.beacon.variable);
+
+ if ((pmlmeinfo->state&0x03) == MSR_AP) {
+ /* DBG_8723A("ie len =%d\n", cur_network->IELength); */
+ pktlen += cur_network->IELength;
+ memcpy(pframe, cur_network->IEs, pktlen);
+
+ goto _ConstructBeacon;
+ }
+
+ /* below for ad-hoc mode */
+
+ /* SSID */
+ pframe = rtw_set_ie23a(pframe, WLAN_EID_SSID,
+ cur_network->Ssid.ssid_len,
+ cur_network->Ssid.ssid, &pktlen);
+
+ /* supported rates... */
+ rate_len = rtw_get_rateset_len23a(cur_network->SupportedRates);
+ pframe = rtw_set_ie23a(pframe, WLAN_EID_SUPP_RATES, ((rate_len > 8) ?
+ 8 : rate_len), cur_network->SupportedRates, &pktlen);
+
+ /* DS parameter set */
+ pframe = rtw_set_ie23a(pframe, WLAN_EID_DS_PARAMS, 1, (unsigned char *)
+ &cur_network->DSConfig, &pktlen);
+
+ if ((pmlmeinfo->state&0x03) == MSR_ADHOC) {
+ u32 ATIMWindow;
+ /* IBSS Parameter Set... */
+ /* ATIMWindow = cur->ATIMWindow; */
+ ATIMWindow = 0;
+ pframe = rtw_set_ie23a(pframe, WLAN_EID_IBSS_PARAMS, 2,
+ (unsigned char *)&ATIMWindow, &pktlen);
+ }
+
+ /* todo: ERP IE */
+
+ /* EXTERNDED SUPPORTED RATE */
+ if (rate_len > 8)
+ pframe = rtw_set_ie23a(pframe, WLAN_EID_EXT_SUPP_RATES,
+ (rate_len - 8),
+ (cur_network->SupportedRates + 8),
+ &pktlen);
+
+ /* todo:HT for adhoc */
+
+_ConstructBeacon:
+
+ if ((pktlen + TXDESC_SIZE) > 512) {
+ DBG_8723A("beacon frame too large\n");
+ return;
+ }
+
+ *pLength = pktlen;
+
+ /* DBG_8723A("%s bcn_sz =%d\n", __func__, pktlen); */
+
+}
+
+static void ConstructPSPoll(struct rtw_adapter *padapter,
+ u8 *pframe, u32 *pLength)
+{
+ struct ieee80211_hdr *pwlanhdr;
+ struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv;
+ struct mlme_ext_info *pmlmeinfo = &pmlmeext->mlmext_info;
+
+ pwlanhdr = (struct ieee80211_hdr *)pframe;
+
+ /* Frame control. */
+ pwlanhdr->frame_control =
+ cpu_to_le16(IEEE80211_FTYPE_CTL | IEEE80211_STYPE_PSPOLL);
+ pwlanhdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_PM);
+
+ /* AID. */
+ pwlanhdr->duration_id = cpu_to_le16(pmlmeinfo->aid | 0xc000);
+
+ /* BSSID. */
+ memcpy(pwlanhdr->addr1, get_my_bssid23a(&pmlmeinfo->network), ETH_ALEN);
+
+ /* TA. */
+ memcpy(pwlanhdr->addr2, myid(&padapter->eeprompriv), ETH_ALEN);
+
+ *pLength = 16;
+}
+
+static void
+ConstructNullFunctionData(struct rtw_adapter *padapter, u8 *pframe,
+ u32 *pLength, u8 *StaAddr, u8 bQoS, u8 AC,
+ u8 bEosp, u8 bForcePowerSave)
+{
+ struct ieee80211_hdr *pwlanhdr;
+ u32 pktlen;
+ struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
+ struct wlan_network *cur_network = &pmlmepriv->cur_network;
+ struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv;
+ struct mlme_ext_info *pmlmeinfo = &pmlmeext->mlmext_info;
+
+ pwlanhdr = (struct ieee80211_hdr *)pframe;
+
+ pwlanhdr->frame_control = 0;
+ pwlanhdr->seq_ctrl = 0;
+
+ if (bForcePowerSave)
+ pwlanhdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_PM);
+
+ switch (cur_network->network.ifmode) {
+ case NL80211_IFTYPE_P2P_CLIENT:
+ case NL80211_IFTYPE_STATION:
+ pwlanhdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_TODS);
+ memcpy(pwlanhdr->addr1,
+ get_my_bssid23a(&pmlmeinfo->network), ETH_ALEN);
+ memcpy(pwlanhdr->addr2, myid(&padapter->eeprompriv),
+ ETH_ALEN);
+ memcpy(pwlanhdr->addr3, StaAddr, ETH_ALEN);
+ break;
+ case NL80211_IFTYPE_P2P_GO:
+ case NL80211_IFTYPE_AP:
+ pwlanhdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_FROMDS);
+ memcpy(pwlanhdr->addr1, StaAddr, ETH_ALEN);
+ memcpy(pwlanhdr->addr2,
+ get_my_bssid23a(&pmlmeinfo->network), ETH_ALEN);
+ memcpy(pwlanhdr->addr3, myid(&padapter->eeprompriv),
+ ETH_ALEN);
+ break;
+ case NL80211_IFTYPE_ADHOC:
+ default:
+ memcpy(pwlanhdr->addr1, StaAddr, ETH_ALEN);
+ memcpy(pwlanhdr->addr2, myid(&padapter->eeprompriv), ETH_ALEN);
+ memcpy(pwlanhdr->addr3,
+ get_my_bssid23a(&pmlmeinfo->network), ETH_ALEN);
+ break;
+ }
+
+ if (bQoS == true) {
+ struct ieee80211_qos_hdr *qoshdr;
+ qoshdr = (struct ieee80211_qos_hdr *)pframe;
+
+ qoshdr->frame_control |=
+ cpu_to_le16(IEEE80211_FTYPE_DATA |
+ IEEE80211_STYPE_QOS_NULLFUNC);
+
+ qoshdr->qos_ctrl = cpu_to_le16(AC & IEEE80211_QOS_CTL_TID_MASK);
+ if (bEosp)
+ qoshdr->qos_ctrl |= cpu_to_le16(IEEE80211_QOS_CTL_EOSP);
+
+ pktlen = sizeof(struct ieee80211_qos_hdr);
+ } else {
+ pwlanhdr->frame_control |=
+ cpu_to_le16(IEEE80211_FTYPE_DATA |
+ IEEE80211_STYPE_NULLFUNC);
+
+ pktlen = sizeof(struct ieee80211_hdr_3addr);
+ }
+
+ *pLength = pktlen;
+}
+
+static void ConstructProbeRsp(struct rtw_adapter *padapter, u8 *pframe,
+ u32 *pLength, u8 *StaAddr, bool bHideSSID)
+{
+ struct ieee80211_mgmt *mgmt;
+ u8 *mac, *bssid;
+ u32 pktlen;
+ struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv;
+ struct mlme_ext_info *pmlmeinfo = &pmlmeext->mlmext_info;
+ struct wlan_bssid_ex *cur_network = &pmlmeinfo->network;
+
+ /* DBG_8723A("%s\n", __func__); */
+
+ mgmt = (struct ieee80211_mgmt *)pframe;
+
+ mac = myid(&padapter->eeprompriv);
+ bssid = cur_network->MacAddress;
+
+ mgmt->frame_control =
+ cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_PROBE_RESP);
+
+ mgmt->seq_ctrl = 0;
+
+ memcpy(mgmt->da, StaAddr, ETH_ALEN);
+ memcpy(mgmt->sa, mac, ETH_ALEN);
+ memcpy(mgmt->bssid, bssid, ETH_ALEN);
+
+ put_unaligned_le64(cur_network->tsf,
+ &mgmt->u.probe_resp.timestamp);
+ put_unaligned_le16(cur_network->beacon_interval,
+ &mgmt->u.probe_resp.beacon_int);
+ put_unaligned_le16(cur_network->capability,
+ &mgmt->u.probe_resp.capab_info);
+
+ pktlen = offsetof(struct ieee80211_mgmt, u.probe_resp.variable);
+
+ if (cur_network->IELength > MAX_IE_SZ)
+ return;
+
+ memcpy(mgmt->u.probe_resp.variable, cur_network->IEs,
+ cur_network->IELength);
+ pktlen += (cur_network->IELength);
+
+ *pLength = pktlen;
+}
+
+/* */
+/* Description: Fill the reserved packets that FW will use to RSVD page. */
+/* Now we just send 4 types packet to rsvd page. */
+/* (1)Beacon, (2)Ps-poll, (3)Null data, (4)ProbeRsp. */
+/* Input: */
+/* bDLFinished - false: At the first time we will send all the packets as a large packet to Hw, */
+/* so we need to set the packet length to total lengh. */
+/* true: At the second time, we should send the first packet (default:beacon) */
+/* to Hw again and set the lengh in descriptor to the real beacon lengh. */
+/* 2009.10.15 by tynli. */
+static void SetFwRsvdPagePkt(struct rtw_adapter *padapter, bool bDLFinished)
+{
+ struct hal_data_8723a *pHalData;
+ struct xmit_frame *pmgntframe;
+ struct pkt_attrib *pattrib;
+ struct xmit_priv *pxmitpriv;
+ struct mlme_ext_priv *pmlmeext;
+ struct mlme_ext_info *pmlmeinfo;
+ u32 BeaconLength = 0, ProbeRspLength = 0, PSPollLength;
+ u32 NullDataLength, QosNullLength, BTQosNullLength;
+ u8 *ReservedPagePacket;
+ u8 PageNum, PageNeed, TxDescLen;
+ u16 BufIndex;
+ u32 TotalPacketLen;
+ struct rsvdpage_loc RsvdPageLoc;
+
+ DBG_8723A("%s\n", __func__);
+
+ ReservedPagePacket = kzalloc(1000, GFP_KERNEL);
+ if (ReservedPagePacket == NULL) {
+ DBG_8723A("%s: alloc ReservedPagePacket fail!\n", __func__);
+ return;
+ }
+
+ pHalData = GET_HAL_DATA(padapter);
+ pxmitpriv = &padapter->xmitpriv;
+ pmlmeext = &padapter->mlmeextpriv;
+ pmlmeinfo = &pmlmeext->mlmext_info;
+
+ TxDescLen = TXDESC_SIZE;
+ PageNum = 0;
+
+ /* 3 (1) beacon */
+ BufIndex = TXDESC_OFFSET;
+ ConstructBeacon(padapter, &ReservedPagePacket[BufIndex], &BeaconLength);
+
+ /* When we count the first page size, we need to reserve description size for the RSVD */
+ /* packet, it will be filled in front of the packet in TXPKTBUF. */
+ PageNeed = (u8)PageNum_128(TxDescLen + BeaconLength);
+ /* To reserved 2 pages for beacon buffer. 2010.06.24. */
+ if (PageNeed == 1)
+ PageNeed += 1;
+ PageNum += PageNeed;
+ pHalData->FwRsvdPageStartOffset = PageNum;
+
+ BufIndex += PageNeed*128;
+
+ /* 3 (2) ps-poll */
+ RsvdPageLoc.LocPsPoll = PageNum;
+ ConstructPSPoll(padapter, &ReservedPagePacket[BufIndex], &PSPollLength);
+ rtl8723a_fill_fake_txdesc(padapter, &ReservedPagePacket[BufIndex-TxDescLen], PSPollLength, true, false);
+
+ PageNeed = (u8)PageNum_128(TxDescLen + PSPollLength);
+ PageNum += PageNeed;
+
+ BufIndex += PageNeed*128;
+
+ /* 3 (3) null data */
+ RsvdPageLoc.LocNullData = PageNum;
+ ConstructNullFunctionData(padapter, &ReservedPagePacket[BufIndex],
+ &NullDataLength,
+ get_my_bssid23a(&pmlmeinfo->network),
+ false, 0, 0, false);
+ rtl8723a_fill_fake_txdesc(padapter,
+ &ReservedPagePacket[BufIndex-TxDescLen],
+ NullDataLength, false, false);
+
+ PageNeed = (u8)PageNum_128(TxDescLen + NullDataLength);
+ PageNum += PageNeed;
+
+ BufIndex += PageNeed*128;
+
+ /* 3 (4) probe response */
+ RsvdPageLoc.LocProbeRsp = PageNum;
+ ConstructProbeRsp(
+ padapter,
+ &ReservedPagePacket[BufIndex],
+ &ProbeRspLength,
+ get_my_bssid23a(&pmlmeinfo->network),
+ false);
+ rtl8723a_fill_fake_txdesc(padapter, &ReservedPagePacket[BufIndex-TxDescLen], ProbeRspLength, false, false);
+
+ PageNeed = (u8)PageNum_128(TxDescLen + ProbeRspLength);
+ PageNum += PageNeed;
+
+ BufIndex += PageNeed*128;
+
+ /* 3 (5) Qos null data */
+ RsvdPageLoc.LocQosNull = PageNum;
+ ConstructNullFunctionData(
+ padapter,
+ &ReservedPagePacket[BufIndex],
+ &QosNullLength,
+ get_my_bssid23a(&pmlmeinfo->network),
+ true, 0, 0, false);
+ rtl8723a_fill_fake_txdesc(padapter, &ReservedPagePacket[BufIndex-TxDescLen], QosNullLength, false, false);
+
+ PageNeed = (u8)PageNum_128(TxDescLen + QosNullLength);
+ PageNum += PageNeed;
+
+ BufIndex += PageNeed*128;
+
+ /* 3 (6) BT Qos null data */
+ RsvdPageLoc.LocBTQosNull = PageNum;
+ ConstructNullFunctionData(
+ padapter,
+ &ReservedPagePacket[BufIndex],
+ &BTQosNullLength,
+ get_my_bssid23a(&pmlmeinfo->network),
+ true, 0, 0, false);
+ rtl8723a_fill_fake_txdesc(padapter, &ReservedPagePacket[BufIndex-TxDescLen], BTQosNullLength, false, true);
+
+ TotalPacketLen = BufIndex + BTQosNullLength;
+
+ pmgntframe = alloc_mgtxmitframe23a(pxmitpriv);
+ if (pmgntframe == NULL)
+ goto exit;
+
+ /* update attribute */
+ pattrib = &pmgntframe->attrib;
+ update_mgntframe_attrib23a(padapter, pattrib);
+ pattrib->qsel = 0x10;
+ pattrib->pktlen = pattrib->last_txcmdsz = TotalPacketLen - TXDESC_OFFSET;
+ memcpy(pmgntframe->buf_addr, ReservedPagePacket, TotalPacketLen);
+
+ rtl8723au_mgnt_xmit(padapter, pmgntframe);
+
+ DBG_8723A("%s: Set RSVD page location to Fw\n", __func__);
+ FillH2CCmd(padapter, RSVD_PAGE_EID, sizeof(RsvdPageLoc), (u8 *)&RsvdPageLoc);
+
+exit:
+ kfree(ReservedPagePacket);
+}
+
+void rtl8723a_set_FwJoinBssReport_cmd(struct rtw_adapter *padapter, u8 mstatus)
+{
+ struct joinbssrpt_parm JoinBssRptParm;
+ struct hal_data_8723a *pHalData = GET_HAL_DATA(padapter);
+ struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv;
+ struct mlme_ext_info *pmlmeinfo = &pmlmeext->mlmext_info;
+
+ DBG_8723A("%s mstatus(%x)\n", __func__, mstatus);
+
+ if (mstatus == 1) {
+ bool bRecover = false;
+ u8 v8;
+
+ /* We should set AID, correct TSF, HW seq enable before set JoinBssReport to Fw in 88/92C. */
+ /* Suggested by filen. Added by tynli. */
+ rtl8723au_write16(padapter, REG_BCN_PSR_RPT,
+ 0xC000|pmlmeinfo->aid);
+ /* Do not set TSF again here or vWiFi beacon DMA INT will not work. */
+ /* correct_TSF23a(padapter, pmlmeext); */
+ /* Hw sequende enable by dedault. 2010.06.23. by tynli. */
+ /* rtl8723au_write16(padapter, REG_NQOS_SEQ, ((pmlmeext->mgnt_seq+100)&0xFFF)); */
+ /* rtl8723au_write8(padapter, REG_HWSEQ_CTRL, 0xFF); */
+
+ /* set REG_CR bit 8 */
+ v8 = rtl8723au_read8(padapter, REG_CR+1);
+ v8 |= BIT(0); /* ENSWBCN */
+ rtl8723au_write8(padapter, REG_CR+1, v8);
+
+ /* Disable Hw protection for a time which revserd for Hw sending beacon. */
+ /* Fix download reserved page packet fail that access collision with the protection time. */
+ /* 2010.05.11. Added by tynli. */
+/* SetBcnCtrlReg23a(padapter, 0, BIT(3)); */
+/* SetBcnCtrlReg23a(padapter, BIT(4), 0); */
+ SetBcnCtrlReg23a(padapter, BIT(4), BIT(3));
+
+ /* Set FWHW_TXQ_CTRL 0x422[6]= 0 to tell Hw the packet is not a real beacon frame. */
+ if (pHalData->RegFwHwTxQCtrl & BIT(6))
+ bRecover = true;
+
+ /* To tell Hw the packet is not a real beacon frame. */
+ /* U1bTmp = rtl8723au_read8(padapter, REG_FWHW_TXQ_CTRL+2); */
+ rtl8723au_write8(padapter, REG_FWHW_TXQ_CTRL + 2,
+ pHalData->RegFwHwTxQCtrl & ~BIT(6));
+ pHalData->RegFwHwTxQCtrl &= ~BIT(6);
+ SetFwRsvdPagePkt(padapter, 0);
+
+ /* 2010.05.11. Added by tynli. */
+ SetBcnCtrlReg23a(padapter, BIT(3), BIT(4));
+
+ /* To make sure that if there exists an adapter which would like to send beacon. */
+ /* If exists, the origianl value of 0x422[6] will be 1, we should check this to */
+ /* prevent from setting 0x422[6] to 0 after download reserved page, or it will cause */
+ /* the beacon cannot be sent by HW. */
+ /* 2010.06.23. Added by tynli. */
+ if (bRecover) {
+ rtl8723au_write8(padapter, REG_FWHW_TXQ_CTRL + 2,
+ pHalData->RegFwHwTxQCtrl | BIT(6));
+ pHalData->RegFwHwTxQCtrl |= BIT(6);
+ }
+
+ /* Clear CR[8] or beacon packet will not be send to TxBuf anymore. */
+ v8 = rtl8723au_read8(padapter, REG_CR+1);
+ v8 &= ~BIT(0); /* ~ENSWBCN */
+ rtl8723au_write8(padapter, REG_CR+1, v8);
+ }
+
+ JoinBssRptParm.OpMode = mstatus;
+
+ FillH2CCmd(padapter, JOINBSS_RPT_EID, sizeof(JoinBssRptParm), (u8 *)&JoinBssRptParm);
+
+}
+
+#ifdef CONFIG_8723AU_BT_COEXIST
+static void SetFwRsvdPagePkt_BTCoex(struct rtw_adapter *padapter)
+{
+ struct hal_data_8723a *pHalData;
+ struct xmit_frame *pmgntframe;
+ struct pkt_attrib *pattrib;
+ struct xmit_priv *pxmitpriv;
+ struct mlme_ext_priv *pmlmeext;
+ struct mlme_ext_info *pmlmeinfo;
+ u8 fakemac[6] = {0x00, 0xe0, 0x4c, 0x00, 0x00, 0x00};
+ u32 NullDataLength, BTQosNullLength;
+ u8 *ReservedPagePacket;
+ u8 PageNum, PageNeed, TxDescLen;
+ u16 BufIndex;
+ u32 TotalPacketLen;
+ struct rsvdpage_loc RsvdPageLoc;
+
+ DBG_8723A("+%s\n", __func__);
+
+ ReservedPagePacket = kzalloc(1024, GFP_KERNEL);
+ if (ReservedPagePacket == NULL) {
+ DBG_8723A("%s: alloc ReservedPagePacket fail!\n", __func__);
+ return;
+ }
+
+ pHalData = GET_HAL_DATA(padapter);
+ pxmitpriv = &padapter->xmitpriv;
+ pmlmeext = &padapter->mlmeextpriv;
+ pmlmeinfo = &pmlmeext->mlmext_info;
+
+ TxDescLen = TXDESC_SIZE;
+ PageNum = 0;
+
+ /* 3 (1) beacon */
+ BufIndex = TXDESC_OFFSET;
+ /* skip Beacon Packet */
+ PageNeed = 3;
+
+ PageNum += PageNeed;
+ pHalData->FwRsvdPageStartOffset = PageNum;
+
+ BufIndex += PageNeed*128;
+
+ /* 3 (3) null data */
+ RsvdPageLoc.LocNullData = PageNum;
+ ConstructNullFunctionData(
+ padapter,
+ &ReservedPagePacket[BufIndex],
+ &NullDataLength,
+ fakemac,
+ false, 0, 0, false);
+ rtl8723a_fill_fake_txdesc(padapter, &ReservedPagePacket[BufIndex-TxDescLen], NullDataLength, false, false);
+
+ PageNeed = (u8)PageNum_128(TxDescLen + NullDataLength);
+ PageNum += PageNeed;
+
+ BufIndex += PageNeed*128;
+
+ /* 3 (6) BT Qos null data */
+ RsvdPageLoc.LocBTQosNull = PageNum;
+ ConstructNullFunctionData(
+ padapter,
+ &ReservedPagePacket[BufIndex],
+ &BTQosNullLength,
+ fakemac,
+ true, 0, 0, false);
+ rtl8723a_fill_fake_txdesc(padapter, &ReservedPagePacket[BufIndex-TxDescLen], BTQosNullLength, false, true);
+
+ TotalPacketLen = BufIndex + BTQosNullLength;
+
+ pmgntframe = alloc_mgtxmitframe23a(pxmitpriv);
+ if (pmgntframe == NULL)
+ goto exit;
+
+ /* update attribute */
+ pattrib = &pmgntframe->attrib;
+ update_mgntframe_attrib23a(padapter, pattrib);
+ pattrib->qsel = 0x10;
+ pattrib->pktlen = pattrib->last_txcmdsz = TotalPacketLen - TXDESC_OFFSET;
+ memcpy(pmgntframe->buf_addr, ReservedPagePacket, TotalPacketLen);
+
+ rtl8723au_mgnt_xmit(padapter, pmgntframe);
+
+ DBG_8723A("%s: Set RSVD page location to Fw\n", __func__);
+ FillH2CCmd(padapter, RSVD_PAGE_EID, sizeof(RsvdPageLoc), (u8 *)&RsvdPageLoc);
+
+exit:
+ kfree(ReservedPagePacket);
+}
+
+void rtl8723a_set_BTCoex_AP_mode_FwRsvdPkt_cmd(struct rtw_adapter *padapter)
+{
+ struct hal_data_8723a *pHalData;
+ u8 bRecover = false;
+
+ DBG_8723A("+%s\n", __func__);
+
+ pHalData = GET_HAL_DATA(padapter);
+
+ /* Set FWHW_TXQ_CTRL 0x422[6]= 0 to tell Hw the packet is not a real beacon frame. */
+ if (pHalData->RegFwHwTxQCtrl & BIT(6))
+ bRecover = true;
+
+ /* To tell Hw the packet is not a real beacon frame. */
+ pHalData->RegFwHwTxQCtrl &= ~BIT(6);
+ rtl8723au_write8(padapter, REG_FWHW_TXQ_CTRL + 2,
+ pHalData->RegFwHwTxQCtrl);
+ SetFwRsvdPagePkt_BTCoex(padapter);
+
+ /* To make sure that if there exists an adapter which would like to send beacon. */
+ /* If exists, the origianl value of 0x422[6] will be 1, we should check this to */
+ /* prevent from setting 0x422[6] to 0 after download reserved page, or it will cause */
+ /* the beacon cannot be sent by HW. */
+ /* 2010.06.23. Added by tynli. */
+ if (bRecover) {
+ pHalData->RegFwHwTxQCtrl |= BIT(6);
+ rtl8723au_write8(padapter, REG_FWHW_TXQ_CTRL + 2,
+ pHalData->RegFwHwTxQCtrl);
+ }
+}
+#endif
diff --git a/drivers/staging/rtl8723au/hal/rtl8723a_dm.c b/drivers/staging/rtl8723au/hal/rtl8723a_dm.c
new file mode 100644
index 000000000..1e831f2d1
--- /dev/null
+++ b/drivers/staging/rtl8723au/hal/rtl8723a_dm.c
@@ -0,0 +1,194 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2012 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * 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.
+ *
+ ******************************************************************************/
+/* */
+/* Description: */
+/* */
+/* This file is for 92CE/92CU dynamic mechanism only */
+/* */
+/* */
+/* */
+#define _RTL8723A_DM_C_
+
+/* */
+/* include files */
+/* */
+#include <osdep_service.h>
+#include <drv_types.h>
+
+#include <rtl8723a_hal.h>
+#include <usb_ops_linux.h>
+
+/* */
+/* Global var */
+/* */
+
+static void dm_CheckPbcGPIO(struct rtw_adapter *padapter)
+{
+ u8 tmp1byte;
+ u8 bPbcPressed = false;
+
+ if (!padapter->registrypriv.hw_wps_pbc)
+ return;
+
+ tmp1byte = rtl8723au_read8(padapter, GPIO_IO_SEL);
+ tmp1byte |= (HAL_8192C_HW_GPIO_WPS_BIT);
+ /* enable GPIO[2] as output mode */
+ rtl8723au_write8(padapter, GPIO_IO_SEL, tmp1byte);
+
+ tmp1byte &= ~(HAL_8192C_HW_GPIO_WPS_BIT);
+ /* reset the floating voltage level */
+ rtl8723au_write8(padapter, GPIO_IN, tmp1byte);
+
+ tmp1byte = rtl8723au_read8(padapter, GPIO_IO_SEL);
+ tmp1byte &= ~(HAL_8192C_HW_GPIO_WPS_BIT);
+ /* enable GPIO[2] as input mode */
+ rtl8723au_write8(padapter, GPIO_IO_SEL, tmp1byte);
+
+ tmp1byte = rtl8723au_read8(padapter, GPIO_IN);
+
+ if (tmp1byte == 0xff)
+ return;
+
+ if (tmp1byte&HAL_8192C_HW_GPIO_WPS_BIT)
+ bPbcPressed = true;
+
+ if (bPbcPressed) {
+ /* Here we only set bPbcPressed to true */
+ /* After trigger PBC, the variable will be set to false */
+ DBG_8723A("CheckPbcGPIO - PBC is pressed\n");
+
+ if (padapter->pid[0] == 0) {
+ /* 0 is the default value and it means the application
+ * monitors the HW PBC doesn't privde its pid to driver.
+ */
+ return;
+ }
+
+ kill_pid(find_vpid(padapter->pid[0]), SIGUSR1, 1);
+ }
+}
+
+/* Initialize GPIO setting registers */
+/* functions */
+
+void rtl8723a_init_dm_priv(struct rtw_adapter *Adapter)
+{
+ struct hal_data_8723a *pHalData = GET_HAL_DATA(Adapter);
+ struct dm_priv *pdmpriv = &pHalData->dmpriv;
+ struct dm_odm_t *pDM_Odm = &pHalData->odmpriv;
+ u8 cut_ver, fab_ver;
+
+ memset(pdmpriv, 0, sizeof(struct dm_priv));
+ memset(pDM_Odm, 0, sizeof(*pDM_Odm));
+
+ pDM_Odm->Adapter = Adapter;
+
+ ODM_CmnInfoInit23a(pDM_Odm, ODM_CMNINFO_IC_TYPE, ODM_RTL8723A);
+
+ if (IS_8723A_A_CUT(pHalData->VersionID)) {
+ fab_ver = ODM_UMC;
+ cut_ver = ODM_CUT_A;
+ } else if (IS_8723A_B_CUT(pHalData->VersionID)) {
+ fab_ver = ODM_UMC;
+ cut_ver = ODM_CUT_B;
+ } else {
+ fab_ver = ODM_TSMC;
+ cut_ver = ODM_CUT_A;
+ }
+ ODM_CmnInfoInit23a(pDM_Odm, ODM_CMNINFO_FAB_VER, fab_ver);
+ ODM_CmnInfoInit23a(pDM_Odm, ODM_CMNINFO_CUT_VER, cut_ver);
+ ODM_CmnInfoInit23a(pDM_Odm, ODM_CMNINFO_MP_TEST_CHIP, IS_NORMAL_CHIP(pHalData->VersionID));
+
+ ODM_CmnInfoInit23a(pDM_Odm, ODM_CMNINFO_BOARD_TYPE, pHalData->BoardType);
+
+ if (pHalData->BoardType == BOARD_USB_High_PA) {
+ ODM_CmnInfoInit23a(pDM_Odm, ODM_CMNINFO_EXT_LNA, true);
+ ODM_CmnInfoInit23a(pDM_Odm, ODM_CMNINFO_EXT_PA, true);
+ }
+ ODM_CmnInfoInit23a(pDM_Odm, ODM_CMNINFO_BWIFI_TEST, Adapter->registrypriv.wifi_spec);
+}
+
+static void Update_ODM_ComInfo_8723a(struct rtw_adapter *Adapter)
+{
+ struct hal_data_8723a *pHalData = GET_HAL_DATA(Adapter);
+ struct dm_odm_t *pDM_Odm = &pHalData->odmpriv;
+ struct dm_priv *pdmpriv = &pHalData->dmpriv;
+ int i;
+ pdmpriv->InitODMFlag = 0;
+ /* Pointer reference */
+ rtl8723a_odm_support_ability_set(Adapter, DYNAMIC_ALL_FUNC_ENABLE);
+
+ for (i = 0; i < NUM_STA; i++)
+ ODM_CmnInfoPtrArrayHook23a(pDM_Odm, ODM_CMNINFO_STA_STATUS, i, NULL);
+}
+
+void rtl8723a_InitHalDm(struct rtw_adapter *Adapter)
+{
+ struct hal_data_8723a *pHalData = GET_HAL_DATA(Adapter);
+ struct dm_priv *pdmpriv = &pHalData->dmpriv;
+ struct dm_odm_t *pDM_Odm = &pHalData->odmpriv;
+ u8 i;
+
+ Update_ODM_ComInfo_8723a(Adapter);
+ ODM23a_DMInit(pDM_Odm);
+ /* Save REG_INIDATA_RATE_SEL value for TXDESC. */
+ for (i = 0; i < 32; i++)
+ pdmpriv->INIDATA_RATE[i] = rtl8723au_read8(Adapter, REG_INIDATA_RATE_SEL+i) & 0x3f;
+}
+
+void
+rtl8723a_HalDmWatchDog(
+ struct rtw_adapter *Adapter
+ )
+{
+ bool bFwCurrentInPSMode = false;
+ bool bFwPSAwake = true;
+ u8 bLinked = false;
+ u8 hw_init_completed = false;
+ struct hal_data_8723a *pHalData = GET_HAL_DATA(Adapter);
+ struct dm_priv *pdmpriv = &pHalData->dmpriv;
+
+ hw_init_completed = Adapter->hw_init_completed;
+
+ if (hw_init_completed == false)
+ goto skip_dm;
+
+ bFwCurrentInPSMode = Adapter->pwrctrlpriv.bFwCurrentInPSMode;
+ bFwPSAwake = rtl8723a_get_fwlps_rf_on(Adapter);
+
+ if (!bFwCurrentInPSMode && bFwPSAwake) {
+ /* Read REG_INIDATA_RATE_SEL value for TXDESC. */
+ if (check_fwstate(&Adapter->mlmepriv, WIFI_STATION_STATE)) {
+ pdmpriv->INIDATA_RATE[0] = rtl8723au_read8(Adapter, REG_INIDATA_RATE_SEL) & 0x3f;
+ } else {
+ u8 i;
+ for (i = 1 ; i < (Adapter->stapriv.asoc_sta_count + 1); i++)
+ pdmpriv->INIDATA_RATE[i] = rtl8723au_read8(Adapter, (REG_INIDATA_RATE_SEL+i)) & 0x3f;
+ }
+ }
+
+ /* ODM */
+ if (rtw_linked_check(Adapter))
+ bLinked = true;
+
+ ODM_CmnInfoUpdate23a(&pHalData->odmpriv, ODM_CMNINFO_LINK, bLinked);
+ ODM_DMWatchdog23a(Adapter);
+
+skip_dm:
+
+ /* Check GPIO to determine current RF on/off and Pbc status. */
+ /* Check Hardware Radio ON/OFF or not */
+ dm_CheckPbcGPIO(Adapter);
+}
diff --git a/drivers/staging/rtl8723au/hal/rtl8723a_hal_init.c b/drivers/staging/rtl8723au/hal/rtl8723a_hal_init.c
new file mode 100644
index 000000000..75e3645de
--- /dev/null
+++ b/drivers/staging/rtl8723au/hal/rtl8723a_hal_init.c
@@ -0,0 +1,2102 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2012 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ ******************************************************************************/
+#define _HAL_INIT_C_
+
+#include <linux/firmware.h>
+#include <drv_types.h>
+#include <rtw_efuse.h>
+
+#include <rtl8723a_hal.h>
+#include <usb_ops_linux.h>
+
+static void _FWDownloadEnable(struct rtw_adapter *padapter, bool enable)
+{
+ u8 tmp;
+
+ if (enable) {
+ /* 8051 enable */
+ tmp = rtl8723au_read8(padapter, REG_SYS_FUNC_EN + 1);
+ rtl8723au_write8(padapter, REG_SYS_FUNC_EN + 1, tmp | 0x04);
+
+ /* MCU firmware download enable. */
+ tmp = rtl8723au_read8(padapter, REG_MCUFWDL);
+ rtl8723au_write8(padapter, REG_MCUFWDL, tmp | 0x01);
+
+ /* 8051 reset */
+ tmp = rtl8723au_read8(padapter, REG_MCUFWDL + 2);
+ rtl8723au_write8(padapter, REG_MCUFWDL + 2, tmp & 0xf7);
+ } else {
+ /* MCU firmware download disable. */
+ tmp = rtl8723au_read8(padapter, REG_MCUFWDL);
+ rtl8723au_write8(padapter, REG_MCUFWDL, tmp & 0xfe);
+
+ /* Reserved for fw extension. */
+ rtl8723au_write8(padapter, REG_MCUFWDL + 1, 0x00);
+ }
+}
+
+static int
+_PageWrite(struct rtw_adapter *padapter, u32 page, void *buffer, u32 size)
+{
+ u8 value8;
+ u8 u8Page = (u8) (page & 0x07);
+
+ if (size > MAX_PAGE_SIZE)
+ return _FAIL;
+
+ value8 = (rtl8723au_read8(padapter, REG_MCUFWDL + 2) & 0xF8) | u8Page;
+ rtl8723au_write8(padapter, REG_MCUFWDL + 2, value8);
+
+ return rtl8723au_writeN(padapter, FW_8723A_START_ADDRESS, size, buffer);
+}
+
+static int _WriteFW(struct rtw_adapter *padapter, void *buffer, u32 size)
+{
+ /* Since we need dynamic decide method of dwonload fw, so we
+ call this function to get chip version. */
+ /* We can remove _ReadChipVersion from ReadpadapterInfo8192C later. */
+ int ret = _SUCCESS;
+ u32 pageNums, remainSize;
+ u32 page, offset;
+ u8 *bufferPtr = (u8 *) buffer;
+
+ pageNums = size / MAX_PAGE_SIZE;
+ /* RT_ASSERT((pageNums <= 4),
+ ("Page numbers should not greater then 4 \n")); */
+ remainSize = size % MAX_PAGE_SIZE;
+
+ for (page = 0; page < pageNums; page++) {
+ offset = page * MAX_PAGE_SIZE;
+ ret = _PageWrite(padapter, page, bufferPtr + offset,
+ MAX_PAGE_SIZE);
+
+ if (ret == _FAIL)
+ goto exit;
+ }
+ if (remainSize) {
+ offset = pageNums * MAX_PAGE_SIZE;
+ page = pageNums;
+ ret = _PageWrite(padapter, page, bufferPtr + offset,
+ remainSize);
+
+ if (ret == _FAIL)
+ goto exit;
+ }
+ RT_TRACE(_module_hal_init_c_, _drv_info_,
+ "_WriteFW Done- for Normal chip.\n");
+
+exit:
+ return ret;
+}
+
+static int _FWFreeToGo(struct rtw_adapter *padapter)
+{
+ u32 counter = 0;
+ u32 value32;
+
+ /* polling CheckSum report */
+ do {
+ value32 = rtl8723au_read32(padapter, REG_MCUFWDL);
+ if (value32 & FWDL_ChkSum_rpt)
+ break;
+ } while (counter++ < POLLING_READY_TIMEOUT_COUNT);
+
+ if (counter >= POLLING_READY_TIMEOUT_COUNT) {
+ RT_TRACE(_module_hal_init_c_, _drv_err_,
+ "%s: chksum report fail! REG_MCUFWDL:0x%08x\n",
+ __func__, value32);
+ return _FAIL;
+ }
+ RT_TRACE(_module_hal_init_c_, _drv_info_,
+ "%s: Checksum report OK! REG_MCUFWDL:0x%08x\n", __func__,
+ value32);
+
+ value32 = rtl8723au_read32(padapter, REG_MCUFWDL);
+ value32 |= MCUFWDL_RDY;
+ value32 &= ~WINTINI_RDY;
+ rtl8723au_write32(padapter, REG_MCUFWDL, value32);
+
+ /* polling for FW ready */
+ counter = 0;
+ do {
+ value32 = rtl8723au_read32(padapter, REG_MCUFWDL);
+ if (value32 & WINTINI_RDY) {
+ RT_TRACE(_module_hal_init_c_, _drv_info_,
+ "%s: Polling FW ready success!! REG_MCUFWDL:0x%08x\n",
+ __func__, value32);
+ return _SUCCESS;
+ }
+ udelay(5);
+ } while (counter++ < POLLING_READY_TIMEOUT_COUNT);
+
+ RT_TRACE(_module_hal_init_c_, _drv_err_,
+ "%s: Polling FW ready fail!! REG_MCUFWDL:0x%08x\n",
+ __func__, value32);
+ return _FAIL;
+}
+
+#define IS_FW_81xxC(padapter) (((GET_HAL_DATA(padapter))->FirmwareSignature & 0xFFF0) == 0x88C0)
+
+void rtl8723a_FirmwareSelfReset(struct rtw_adapter *padapter)
+{
+ struct hal_data_8723a *pHalData = GET_HAL_DATA(padapter);
+ u8 u1bTmp;
+ u8 Delay = 100;
+
+ if (!(IS_FW_81xxC(padapter) &&
+ ((pHalData->FirmwareVersion < 0x21) ||
+ (pHalData->FirmwareVersion == 0x21 &&
+ pHalData->FirmwareSubVersion < 0x01)))) {
+ /* after 88C Fw v33.1 */
+ /* 0x1cf = 0x20. Inform 8051 to reset. 2009.12.25. tynli_test */
+ rtl8723au_write8(padapter, REG_HMETFR + 3, 0x20);
+
+ u1bTmp = rtl8723au_read8(padapter, REG_SYS_FUNC_EN + 1);
+ while (u1bTmp & BIT(2)) {
+ Delay--;
+ if (Delay == 0)
+ break;
+ udelay(50);
+ u1bTmp = rtl8723au_read8(padapter, REG_SYS_FUNC_EN + 1);
+ }
+ RT_TRACE(_module_hal_init_c_, _drv_info_,
+ "-%s: 8051 reset success (%d)\n", __func__,
+ Delay);
+
+ if ((Delay == 0)) {
+ /* force firmware reset */
+ u1bTmp = rtl8723au_read8(padapter, REG_SYS_FUNC_EN + 1);
+ rtl8723au_write8(padapter, REG_SYS_FUNC_EN + 1,
+ u1bTmp & ~BIT(2));
+ }
+ }
+}
+
+/* */
+/* Description: */
+/* Download 8192C firmware code. */
+/* */
+/* */
+int rtl8723a_FirmwareDownload(struct rtw_adapter *padapter)
+{
+ int rtStatus = _SUCCESS;
+ u8 writeFW_retry = 0;
+ unsigned long fwdl_start_time;
+ struct hal_data_8723a *pHalData = GET_HAL_DATA(padapter);
+ struct dvobj_priv *dvobj = adapter_to_dvobj(padapter);
+ struct device *device = dvobj_to_dev(dvobj);
+ struct rt_8723a_firmware_hdr *pFwHdr = NULL;
+ const struct firmware *fw;
+ char *fw_name;
+ u8 *firmware_buf = NULL;
+ u8 *buf;
+ int fw_size;
+ static int log_version;
+
+ RT_TRACE(_module_hal_init_c_, _drv_info_, "+%s\n", __func__);
+
+ if (IS_8723A_A_CUT(pHalData->VersionID)) {
+ fw_name = "/*(DEBLOBBED)*/";
+ RT_TRACE(_module_hal_init_c_, _drv_info_,
+ "rtl8723a_FirmwareDownload: R8723FwImageArray_UMC for RTL8723A A CUT\n");
+ } else if (IS_8723A_B_CUT(pHalData->VersionID)) {
+ /* WLAN Fw. */
+ if (padapter->registrypriv.wifi_spec == 1) {
+ fw_name = "/*(DEBLOBBED)*/";
+ DBG_8723A(" Rtl8723_FwUMCBCutImageArrayWithoutBT for "
+ "RTL8723A B CUT\n");
+ } else {
+ if (rtl8723a_BT_coexist(padapter)) {
+ fw_name = "/*(DEBLOBBED)*/";
+ DBG_8723A(" Rtl8723_FwUMCBCutImageArrayWithBT "
+ "for RTL8723A B CUT\n");
+ } else {
+ fw_name = "/*(DEBLOBBED)*/";
+ DBG_8723A(" Rtl8723_FwUMCBCutImageArrayWithout "
+ "BT for RTL8723A B CUT\n");
+ }
+ }
+ } else {
+ /* <Roger_TODO> We should download proper RAM Code here
+ to match the ROM code. */
+ RT_TRACE(_module_hal_init_c_, _drv_err_,
+ "%s: unknown version!\n", __func__);
+ rtStatus = _FAIL;
+ goto Exit;
+ }
+
+ pr_info("rtl8723au: Loading firmware %s\n", fw_name);
+ if (reject_firmware(&fw, fw_name, device)) {
+ pr_err("rtl8723au: request_firmware load failed\n");
+ rtStatus = _FAIL;
+ goto Exit;
+ }
+ if (!fw) {
+ pr_err("rtl8723au: Firmware %s not available\n", fw_name);
+ rtStatus = _FAIL;
+ goto Exit;
+ }
+ firmware_buf = kmemdup(fw->data, fw->size, GFP_KERNEL);
+ if (!firmware_buf) {
+ rtStatus = _FAIL;
+ goto Exit;
+ }
+ buf = firmware_buf;
+ fw_size = fw->size;
+ release_firmware(fw);
+
+ /* To Check Fw header. Added by tynli. 2009.12.04. */
+ pFwHdr = (struct rt_8723a_firmware_hdr *)firmware_buf;
+
+ pHalData->FirmwareVersion = le16_to_cpu(pFwHdr->Version);
+ pHalData->FirmwareSubVersion = pFwHdr->Subversion;
+ pHalData->FirmwareSignature = le16_to_cpu(pFwHdr->Signature);
+
+ DBG_8723A("%s: fw_ver =%d fw_subver =%d sig = 0x%x\n",
+ __func__, pHalData->FirmwareVersion,
+ pHalData->FirmwareSubVersion, pHalData->FirmwareSignature);
+
+ if (!log_version++)
+ pr_info("%sFirmware Version %d, SubVersion %d, Signature "
+ "0x%x\n", DRIVER_PREFIX, pHalData->FirmwareVersion,
+ pHalData->FirmwareSubVersion,
+ pHalData->FirmwareSignature);
+
+ if (IS_FW_HEADER_EXIST(pFwHdr)) {
+ /* Shift 32 bytes for FW header */
+ buf = buf + 32;
+ fw_size = fw_size - 32;
+ }
+
+ /* Suggested by Filen. If 8051 is running in RAM code, driver should
+ inform Fw to reset by itself, */
+ /* or it will cause download Fw fail. 2010.02.01. by tynli. */
+ if (rtl8723au_read8(padapter, REG_MCUFWDL) & RAM_DL_SEL) {
+ /* 8051 RAM code */
+ rtl8723a_FirmwareSelfReset(padapter);
+ rtl8723au_write8(padapter, REG_MCUFWDL, 0x00);
+ }
+
+ _FWDownloadEnable(padapter, true);
+ fwdl_start_time = jiffies;
+ while (1) {
+ /* reset the FWDL chksum */
+ rtl8723au_write8(padapter, REG_MCUFWDL,
+ rtl8723au_read8(padapter, REG_MCUFWDL) |
+ FWDL_ChkSum_rpt);
+
+ rtStatus = _WriteFW(padapter, buf, fw_size);
+
+ if (rtStatus == _SUCCESS ||
+ (jiffies_to_msecs(jiffies - fwdl_start_time) > 500 &&
+ writeFW_retry++ >= 3))
+ break;
+
+ DBG_8723A("%s writeFW_retry:%u, time after fwdl_start_time:"
+ "%ums\n", __func__, writeFW_retry,
+ jiffies_to_msecs(jiffies - fwdl_start_time));
+ }
+ _FWDownloadEnable(padapter, false);
+ if (_SUCCESS != rtStatus) {
+ DBG_8723A("DL Firmware failed!\n");
+ goto Exit;
+ }
+
+ rtStatus = _FWFreeToGo(padapter);
+ if (_SUCCESS != rtStatus) {
+ RT_TRACE(_module_hal_init_c_, _drv_err_,
+ "DL Firmware failed!\n");
+ goto Exit;
+ }
+ RT_TRACE(_module_hal_init_c_, _drv_info_,
+ "Firmware is ready to run!\n");
+
+Exit:
+ kfree(firmware_buf);
+ return rtStatus;
+}
+
+void rtl8723a_InitializeFirmwareVars(struct rtw_adapter *padapter)
+{
+ struct hal_data_8723a *pHalData = GET_HAL_DATA(padapter);
+
+ /* Init Fw LPS related. */
+ padapter->pwrctrlpriv.bFwCurrentInPSMode = false;
+
+ /* Init H2C counter. by tynli. 2009.12.09. */
+ pHalData->LastHMEBoxNum = 0;
+}
+
+/* */
+/* Efuse related code */
+/* */
+static u8
+hal_EfuseSwitchToBank(struct rtw_adapter *padapter, u8 bank)
+{
+ u8 bRet = false;
+ u32 value32 = 0;
+
+ DBG_8723A("%s: Efuse switch bank to %d\n", __func__, bank);
+ value32 = rtl8723au_read32(padapter, EFUSE_TEST);
+ bRet = true;
+ switch (bank) {
+ case 0:
+ value32 = (value32 & ~EFUSE_SEL_MASK) |
+ EFUSE_SEL(EFUSE_WIFI_SEL_0);
+ break;
+ case 1:
+ value32 = (value32 & ~EFUSE_SEL_MASK) |
+ EFUSE_SEL(EFUSE_BT_SEL_0);
+ break;
+ case 2:
+ value32 = (value32 & ~EFUSE_SEL_MASK) |
+ EFUSE_SEL(EFUSE_BT_SEL_1);
+ break;
+ case 3:
+ value32 = (value32 & ~EFUSE_SEL_MASK) |
+ EFUSE_SEL(EFUSE_BT_SEL_2);
+ break;
+ default:
+ value32 = (value32 & ~EFUSE_SEL_MASK) |
+ EFUSE_SEL(EFUSE_WIFI_SEL_0);
+ bRet = false;
+ break;
+ }
+ rtl8723au_write32(padapter, EFUSE_TEST, value32);
+
+ return bRet;
+}
+
+static void
+hal_ReadEFuse_WiFi(struct rtw_adapter *padapter,
+ u16 _offset, u16 _size_byte, u8 *pbuf)
+{
+ u8 *efuseTbl = NULL;
+ u16 eFuse_Addr = 0;
+ u8 offset, wden;
+ u8 efuseHeader, efuseExtHdr, efuseData;
+ u16 i, total, used;
+ struct hal_data_8723a *pHalData = GET_HAL_DATA(padapter);
+
+ /* Do NOT excess total size of EFuse table.
+ Added by Roger, 2008.11.10. */
+ if ((_offset + _size_byte) > EFUSE_MAP_LEN_8723A) {
+ DBG_8723A("%s: Invalid offset(%#x) with read bytes(%#x)!!\n",
+ __func__, _offset, _size_byte);
+ return;
+ }
+
+ efuseTbl = kmalloc(EFUSE_MAP_LEN_8723A, GFP_KERNEL);
+ if (efuseTbl == NULL) {
+ DBG_8723A("%s: alloc efuseTbl fail!\n", __func__);
+ return;
+ }
+ /* 0xff will be efuse default value instead of 0x00. */
+ memset(efuseTbl, 0xFF, EFUSE_MAP_LEN_8723A);
+
+ /* switch bank back to bank 0 for later BT and wifi use. */
+ hal_EfuseSwitchToBank(padapter, 0);
+
+ while (AVAILABLE_EFUSE_ADDR(eFuse_Addr)) {
+ ReadEFuseByte23a(padapter, eFuse_Addr++, &efuseHeader);
+ if (efuseHeader == 0xFF) {
+ DBG_8723A("%s: data end at address =%#x\n", __func__,
+ eFuse_Addr);
+ break;
+ }
+
+ /* Check PG header for section num. */
+ if (EXT_HEADER(efuseHeader)) { /* extended header */
+ offset = GET_HDR_OFFSET_2_0(efuseHeader);
+
+ ReadEFuseByte23a(padapter, eFuse_Addr++, &efuseExtHdr);
+ if (ALL_WORDS_DISABLED(efuseExtHdr))
+ continue;
+
+ offset |= ((efuseExtHdr & 0xF0) >> 1);
+ wden = efuseExtHdr & 0x0F;
+ } else {
+ offset = (efuseHeader >> 4) & 0x0f;
+ wden = efuseHeader & 0x0f;
+ }
+
+ if (offset < EFUSE_MAX_SECTION_8723A) {
+ u16 addr;
+ /* Get word enable value from PG header */
+
+ addr = offset * PGPKT_DATA_SIZE;
+ for (i = 0; i < EFUSE_MAX_WORD_UNIT; i++) {
+ /* Check word enable condition in the section */
+ if (!(wden & (0x01 << i))) {
+ ReadEFuseByte23a(padapter, eFuse_Addr++,
+ &efuseData);
+ efuseTbl[addr] = efuseData;
+
+ ReadEFuseByte23a(padapter, eFuse_Addr++,
+ &efuseData);
+ efuseTbl[addr + 1] = efuseData;
+ }
+ addr += 2;
+ }
+ } else {
+ DBG_8723A(KERN_ERR "%s: offset(%d) is illegal!!\n",
+ __func__, offset);
+ eFuse_Addr += Efuse_CalculateWordCnts23a(wden) * 2;
+ }
+ }
+
+ /* Copy from Efuse map to output pointer memory!!! */
+ for (i = 0; i < _size_byte; i++)
+ pbuf[i] = efuseTbl[_offset + i];
+
+ /* Calculate Efuse utilization */
+ EFUSE_GetEfuseDefinition23a(padapter, EFUSE_WIFI,
+ TYPE_AVAILABLE_EFUSE_BYTES_TOTAL, &total);
+ used = eFuse_Addr - 1;
+ pHalData->EfuseUsedBytes = used;
+
+ kfree(efuseTbl);
+}
+
+static void
+hal_ReadEFuse_BT(struct rtw_adapter *padapter,
+ u16 _offset, u16 _size_byte, u8 *pbuf)
+{
+ u8 *efuseTbl;
+ u8 bank;
+ u16 eFuse_Addr;
+ u8 efuseHeader, efuseExtHdr, efuseData;
+ u8 offset, wden;
+ u16 i, total, used;
+ struct hal_data_8723a *pHalData = GET_HAL_DATA(padapter);
+
+ /* Do NOT excess total size of EFuse table.
+ Added by Roger, 2008.11.10. */
+ if ((_offset + _size_byte) > EFUSE_BT_MAP_LEN) {
+ DBG_8723A("%s: Invalid offset(%#x) with read bytes(%#x)!!\n",
+ __func__, _offset, _size_byte);
+ return;
+ }
+
+ efuseTbl = kmalloc(EFUSE_BT_MAP_LEN, GFP_KERNEL);
+ if (efuseTbl == NULL) {
+ DBG_8723A("%s: efuseTbl malloc fail!\n", __func__);
+ return;
+ }
+ /* 0xff will be efuse default value instead of 0x00. */
+ memset(efuseTbl, 0xFF, EFUSE_BT_MAP_LEN);
+
+ EFUSE_GetEfuseDefinition23a(padapter, EFUSE_BT,
+ TYPE_AVAILABLE_EFUSE_BYTES_BANK, &total);
+
+ for (bank = 1; bank < EFUSE_MAX_BANK; bank++) {
+ if (hal_EfuseSwitchToBank(padapter, bank) == false) {
+ DBG_8723A("%s: hal_EfuseSwitchToBank Fail!!\n",
+ __func__);
+ goto exit;
+ }
+
+ eFuse_Addr = 0;
+
+ while (AVAILABLE_EFUSE_ADDR(eFuse_Addr)) {
+ ReadEFuseByte23a(padapter, eFuse_Addr++, &efuseHeader);
+ if (efuseHeader == 0xFF)
+ break;
+
+ /* Check PG header for section num. */
+ if (EXT_HEADER(efuseHeader)) { /* extended header */
+ offset = GET_HDR_OFFSET_2_0(efuseHeader);
+
+ ReadEFuseByte23a(padapter, eFuse_Addr++,
+ &efuseExtHdr);
+ if (ALL_WORDS_DISABLED(efuseExtHdr))
+ continue;
+
+ offset |= ((efuseExtHdr & 0xF0) >> 1);
+ wden = efuseExtHdr & 0x0F;
+ } else {
+ offset = (efuseHeader >> 4) & 0x0f;
+ wden = efuseHeader & 0x0f;
+ }
+
+ if (offset < EFUSE_BT_MAX_SECTION) {
+ u16 addr;
+
+ /* Get word enable value from PG header */
+
+ addr = offset * PGPKT_DATA_SIZE;
+ for (i = 0; i < EFUSE_MAX_WORD_UNIT; i++) {
+ /* Check word enable condition in
+ the section */
+ if (!(wden & (0x01 << i))) {
+ ReadEFuseByte23a(padapter,
+ eFuse_Addr++,
+ &efuseData);
+ efuseTbl[addr] = efuseData;
+
+ ReadEFuseByte23a(padapter,
+ eFuse_Addr++,
+ &efuseData);
+ efuseTbl[addr + 1] = efuseData;
+ }
+ addr += 2;
+ }
+ } else {
+ DBG_8723A(KERN_ERR
+ "%s: offset(%d) is illegal!!\n",
+ __func__, offset);
+ eFuse_Addr += Efuse_CalculateWordCnts23a(wden) * 2;
+ }
+ }
+
+ if ((eFuse_Addr - 1) < total) {
+ DBG_8723A("%s: bank(%d) data end at %#x\n",
+ __func__, bank, eFuse_Addr - 1);
+ break;
+ }
+ }
+
+ /* switch bank back to bank 0 for later BT and wifi use. */
+ hal_EfuseSwitchToBank(padapter, 0);
+
+ /* Copy from Efuse map to output pointer memory!!! */
+ for (i = 0; i < _size_byte; i++)
+ pbuf[i] = efuseTbl[_offset + i];
+
+ /* */
+ /* Calculate Efuse utilization. */
+ /* */
+ EFUSE_GetEfuseDefinition23a(padapter, EFUSE_BT,
+ TYPE_AVAILABLE_EFUSE_BYTES_TOTAL, &total);
+ used = (EFUSE_BT_REAL_BANK_CONTENT_LEN * (bank - 1)) + eFuse_Addr - 1;
+ pHalData->BTEfuseUsedBytes = used;
+
+exit:
+ kfree(efuseTbl);
+}
+
+void
+rtl8723a_readefuse(struct rtw_adapter *padapter,
+ u8 efuseType, u16 _offset, u16 _size_byte, u8 *pbuf)
+{
+ if (efuseType == EFUSE_WIFI)
+ hal_ReadEFuse_WiFi(padapter, _offset, _size_byte, pbuf);
+ else
+ hal_ReadEFuse_BT(padapter, _offset, _size_byte, pbuf);
+}
+
+u16 rtl8723a_EfuseGetCurrentSize_WiFi(struct rtw_adapter *padapter)
+{
+ u16 efuse_addr = 0;
+ u8 hoffset = 0, hworden = 0;
+ u8 efuse_data, word_cnts = 0;
+ struct hal_data_8723a *pHalData = GET_HAL_DATA(padapter);
+
+ efuse_addr = pHalData->EfuseUsedBytes;
+
+ DBG_8723A("%s: start_efuse_addr = 0x%X\n", __func__, efuse_addr);
+
+ /* switch bank back to bank 0 for later BT and wifi use. */
+ hal_EfuseSwitchToBank(padapter, 0);
+
+ while (AVAILABLE_EFUSE_ADDR(efuse_addr)) {
+ if (efuse_OneByteRead23a(padapter, efuse_addr, &efuse_data) ==
+ _FAIL) {
+ DBG_8723A(KERN_ERR "%s: efuse_OneByteRead23a Fail! "
+ "addr = 0x%X !!\n", __func__, efuse_addr);
+ break;
+ }
+
+ if (efuse_data == 0xFF)
+ break;
+
+ if (EXT_HEADER(efuse_data)) {
+ hoffset = GET_HDR_OFFSET_2_0(efuse_data);
+ efuse_addr++;
+ efuse_OneByteRead23a(padapter, efuse_addr, &efuse_data);
+ if (ALL_WORDS_DISABLED(efuse_data))
+ continue;
+
+ hoffset |= ((efuse_data & 0xF0) >> 1);
+ hworden = efuse_data & 0x0F;
+ } else {
+ hoffset = (efuse_data >> 4) & 0x0F;
+ hworden = efuse_data & 0x0F;
+ }
+
+ word_cnts = Efuse_CalculateWordCnts23a(hworden);
+ efuse_addr += (word_cnts * 2) + 1;
+ }
+
+ pHalData->EfuseUsedBytes = efuse_addr;
+
+ DBG_8723A("%s: CurrentSize =%d\n", __func__, efuse_addr);
+
+ return efuse_addr;
+}
+
+u16 rtl8723a_EfuseGetCurrentSize_BT(struct rtw_adapter *padapter)
+{
+ u16 btusedbytes;
+ u16 efuse_addr;
+ u8 bank, startBank;
+ u8 hoffset = 0, hworden = 0;
+ u8 efuse_data, word_cnts = 0;
+ u16 retU2 = 0;
+ struct hal_data_8723a *pHalData = GET_HAL_DATA(padapter);
+
+ btusedbytes = pHalData->BTEfuseUsedBytes;
+
+ efuse_addr = (u16) ((btusedbytes % EFUSE_BT_REAL_BANK_CONTENT_LEN));
+ startBank = (u8) (1 + (btusedbytes / EFUSE_BT_REAL_BANK_CONTENT_LEN));
+
+ DBG_8723A("%s: start from bank =%d addr = 0x%X\n", __func__, startBank,
+ efuse_addr);
+
+ EFUSE_GetEfuseDefinition23a(padapter, EFUSE_BT,
+ TYPE_AVAILABLE_EFUSE_BYTES_BANK, &retU2);
+
+ for (bank = startBank; bank < EFUSE_MAX_BANK; bank++) {
+ if (hal_EfuseSwitchToBank(padapter, bank) == false) {
+ DBG_8723A(KERN_ERR "%s: switch bank(%d) Fail!!\n",
+ __func__, bank);
+ bank = EFUSE_MAX_BANK;
+ break;
+ }
+
+ /* only when bank is switched we have to reset
+ the efuse_addr. */
+ if (bank != startBank)
+ efuse_addr = 0;
+
+ while (AVAILABLE_EFUSE_ADDR(efuse_addr)) {
+ if (efuse_OneByteRead23a(padapter, efuse_addr,
+ &efuse_data) == _FAIL) {
+ DBG_8723A(KERN_ERR "%s: efuse_OneByteRead23a Fail!"
+ " addr = 0x%X !!\n",
+ __func__, efuse_addr);
+ bank = EFUSE_MAX_BANK;
+ break;
+ }
+
+ if (efuse_data == 0xFF)
+ break;
+
+ if (EXT_HEADER(efuse_data)) {
+ hoffset = GET_HDR_OFFSET_2_0(efuse_data);
+ efuse_addr++;
+ efuse_OneByteRead23a(padapter, efuse_addr,
+ &efuse_data);
+ if (ALL_WORDS_DISABLED(efuse_data)) {
+ efuse_addr++;
+ continue;
+ }
+
+ hoffset |= ((efuse_data & 0xF0) >> 1);
+ hworden = efuse_data & 0x0F;
+ } else {
+ hoffset = (efuse_data >> 4) & 0x0F;
+ hworden = efuse_data & 0x0F;
+ }
+ word_cnts = Efuse_CalculateWordCnts23a(hworden);
+ /* read next header */
+ efuse_addr += (word_cnts * 2) + 1;
+ }
+
+ /* Check if we need to check next bank efuse */
+ if (efuse_addr < retU2)
+ break; /* don't need to check next bank. */
+ }
+
+ retU2 = ((bank - 1) * EFUSE_BT_REAL_BANK_CONTENT_LEN) + efuse_addr;
+ pHalData->BTEfuseUsedBytes = retU2;
+
+ DBG_8723A("%s: CurrentSize =%d\n", __func__, retU2);
+ return retU2;
+}
+
+void rtl8723a_read_chip_version(struct rtw_adapter *padapter)
+{
+ u32 value32;
+ struct hal_version ChipVersion;
+ struct hal_data_8723a *pHalData;
+
+ pHalData = GET_HAL_DATA(padapter);
+
+ value32 = rtl8723au_read32(padapter, REG_SYS_CFG);
+ ChipVersion.ICType = CHIP_8723A;
+ ChipVersion.ChipType = ((value32 & RTL_ID) ? TEST_CHIP : NORMAL_CHIP);
+ pHalData->rf_type = RF_1T1R;
+ ChipVersion.VendorType =
+ ((value32 & VENDOR_ID) ? CHIP_VENDOR_UMC : CHIP_VENDOR_TSMC);
+ ChipVersion.CUTVersion = (value32 & CHIP_VER_RTL_MASK) >> CHIP_VER_RTL_SHIFT; /* IC version (CUT) */
+
+ /* For regulator mode. by tynli. 2011.01.14 */
+ pHalData->RegulatorMode = ((value32 & SPS_SEL) ?
+ RT_LDO_REGULATOR : RT_SWITCHING_REGULATOR);
+
+ value32 = rtl8723au_read32(padapter, REG_GPIO_OUTSTS);
+ /* ROM code version. */
+ ChipVersion.ROMVer = (value32 & RF_RL_ID) >> 20;
+
+ /* For multi-function consideration. Added by Roger, 2010.10.06. */
+ pHalData->MultiFunc = RT_MULTI_FUNC_NONE;
+ value32 = rtl8723au_read32(padapter, REG_MULTI_FUNC_CTRL);
+ pHalData->MultiFunc |=
+ ((value32 & WL_FUNC_EN) ? RT_MULTI_FUNC_WIFI : 0);
+ pHalData->MultiFunc |= ((value32 & BT_FUNC_EN) ? RT_MULTI_FUNC_BT : 0);
+ pHalData->MultiFunc |=
+ ((value32 & GPS_FUNC_EN) ? RT_MULTI_FUNC_GPS : 0);
+ pHalData->PolarityCtl =
+ ((value32 & WL_HWPDN_SL) ? RT_POLARITY_HIGH_ACT :
+ RT_POLARITY_LOW_ACT);
+ pHalData->VersionID = ChipVersion;
+
+ MSG_8723A("RF_Type is %x!!\n", pHalData->rf_type);
+}
+
+/* */
+/* */
+/* 20100209 Joseph: */
+/* This function is used only for 92C to set REG_BCN_CTRL(0x550) register. */
+/* We just reserve the value of the register in variable
+ pHalData->RegBcnCtrlVal and then operate */
+/* the value of the register via atomic operation. */
+/* This prevents from race condition when setting this register. */
+/* The value of pHalData->RegBcnCtrlVal is initialized in
+ HwConfigureRTL8192CE() function. */
+/* */
+void SetBcnCtrlReg23a(struct rtw_adapter *padapter, u8 SetBits, u8 ClearBits)
+{
+ u8 val8;
+
+ val8 = rtl8723au_read8(padapter, REG_BCN_CTRL);
+ val8 |= SetBits;
+ val8 &= ~ClearBits;
+
+ rtl8723au_write8(padapter, REG_BCN_CTRL, val8);
+}
+
+void rtl8723a_InitBeaconParameters(struct rtw_adapter *padapter)
+{
+ rtl8723au_write16(padapter, REG_BCN_CTRL, 0x1010);
+
+ /* TODO: Remove these magic number */
+ rtl8723au_write16(padapter, REG_TBTT_PROHIBIT, 0x6404); /* ms */
+ /* Firmware will control REG_DRVERLYINT when power saving is enable, */
+ /* so don't set this register on STA mode. */
+ if (check_fwstate(&padapter->mlmepriv, WIFI_STATION_STATE) == false)
+ rtl8723au_write8(padapter, REG_DRVERLYINT,
+ DRIVER_EARLY_INT_TIME);
+ /* 2ms */
+ rtl8723au_write8(padapter, REG_BCNDMATIM, BCN_DMA_ATIME_INT_TIME);
+
+ /* Suggested by designer timchen. Change beacon AIFS to the
+ largest number beacause test chip does not contension before
+ sending beacon. by tynli. 2009.11.03 */
+ rtl8723au_write16(padapter, REG_BCNTCFG, 0x660F);
+}
+
+static void ResumeTxBeacon(struct rtw_adapter *padapter)
+{
+ struct hal_data_8723a *pHalData = GET_HAL_DATA(padapter);
+
+ /* 2010.03.01. Marked by tynli. No need to call workitem beacause
+ we record the value */
+ /* which should be read from register to a global variable. */
+
+ RT_TRACE(_module_hci_hal_init_c_, _drv_info_, "+ResumeTxBeacon\n");
+
+ pHalData->RegFwHwTxQCtrl |= BIT(6);
+ rtl8723au_write8(padapter, REG_FWHW_TXQ_CTRL + 2,
+ pHalData->RegFwHwTxQCtrl);
+ rtl8723au_write8(padapter, REG_TBTT_PROHIBIT + 1, 0xff);
+ pHalData->RegReg542 |= BIT(0);
+ rtl8723au_write8(padapter, REG_TBTT_PROHIBIT + 2, pHalData->RegReg542);
+}
+
+static void StopTxBeacon(struct rtw_adapter *padapter)
+{
+ struct hal_data_8723a *pHalData = GET_HAL_DATA(padapter);
+
+ /* 2010.03.01. Marked by tynli. No need to call workitem beacause
+ we record the value */
+ /* which should be read from register to a global variable. */
+
+ RT_TRACE(_module_hci_hal_init_c_, _drv_info_, "+StopTxBeacon\n");
+
+ pHalData->RegFwHwTxQCtrl &= ~BIT(6);
+ rtl8723au_write8(padapter, REG_FWHW_TXQ_CTRL + 2,
+ pHalData->RegFwHwTxQCtrl);
+ rtl8723au_write8(padapter, REG_TBTT_PROHIBIT + 1, 0x64);
+ pHalData->RegReg542 &= ~BIT(0);
+ rtl8723au_write8(padapter, REG_TBTT_PROHIBIT + 2, pHalData->RegReg542);
+}
+
+static void _BeaconFunctionEnable(struct rtw_adapter *padapter, u8 Enable,
+ u8 Linked)
+{
+ SetBcnCtrlReg23a(padapter, DIS_TSF_UDT | EN_BCN_FUNCTION | DIS_BCNQ_SUB,
+ 0);
+ rtl8723au_write8(padapter, REG_RD_CTRL + 1, 0x6F);
+}
+
+void rtl8723a_SetBeaconRelatedRegisters(struct rtw_adapter *padapter)
+{
+ u32 value32;
+ struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv;
+ struct mlme_ext_info *pmlmeinfo = &pmlmeext->mlmext_info;
+
+ /* reset TSF, enable update TSF, correcting TSF On Beacon */
+
+ /* REG_BCN_INTERVAL */
+ /* REG_BCNDMATIM */
+ /* REG_ATIMWND */
+ /* REG_TBTT_PROHIBIT */
+ /* REG_DRVERLYINT */
+ /* REG_BCN_MAX_ERR */
+ /* REG_BCNTCFG (0x510) */
+ /* REG_DUAL_TSF_RST */
+ /* REG_BCN_CTRL (0x550) */
+
+ /* */
+ /* ATIM window */
+ /* */
+ rtl8723au_write16(padapter, REG_ATIMWND, 2);
+
+ /* */
+ /* Beacon interval (in unit of TU). */
+ /* */
+ rtl8723au_write16(padapter, REG_BCN_INTERVAL, pmlmeinfo->bcn_interval);
+
+ rtl8723a_InitBeaconParameters(padapter);
+
+ rtl8723au_write8(padapter, REG_SLOT, 0x09);
+
+ /* */
+ /* Reset TSF Timer to zero, added by Roger. 2008.06.24 */
+ /* */
+ value32 = rtl8723au_read32(padapter, REG_TCR);
+ value32 &= ~TSFRST;
+ rtl8723au_write32(padapter, REG_TCR, value32);
+
+ value32 |= TSFRST;
+ rtl8723au_write32(padapter, REG_TCR, value32);
+
+ /* NOTE: Fix test chip's bug (about contention windows's randomness) */
+ if (check_fwstate(&padapter->mlmepriv, WIFI_ADHOC_STATE |
+ WIFI_ADHOC_MASTER_STATE | WIFI_AP_STATE) == true) {
+ rtl8723au_write8(padapter, REG_RXTSF_OFFSET_CCK, 0x50);
+ rtl8723au_write8(padapter, REG_RXTSF_OFFSET_OFDM, 0x50);
+ }
+
+ _BeaconFunctionEnable(padapter, true, true);
+
+ ResumeTxBeacon(padapter);
+ SetBcnCtrlReg23a(padapter, DIS_BCNQ_SUB, 0);
+}
+
+void rtl8723a_SetHalODMVar(struct rtw_adapter *Adapter,
+ enum hal_odm_variable eVariable,
+ void *pValue1, bool bSet)
+{
+ struct hal_data_8723a *pHalData = GET_HAL_DATA(Adapter);
+ struct dm_odm_t *podmpriv = &pHalData->odmpriv;
+ switch (eVariable) {
+ case HAL_ODM_STA_INFO:
+ {
+ struct sta_info *psta = (struct sta_info *)pValue1;
+
+ if (bSet) {
+ DBG_8723A("Set STA_(%d) info\n", psta->mac_id);
+ ODM_CmnInfoPtrArrayHook23a(podmpriv,
+ ODM_CMNINFO_STA_STATUS,
+ psta->mac_id, psta);
+ } else {
+ DBG_8723A("Clean STA_(%d) info\n", psta->mac_id);
+ ODM_CmnInfoPtrArrayHook23a(podmpriv,
+ ODM_CMNINFO_STA_STATUS,
+ psta->mac_id, NULL);
+ }
+ }
+ break;
+ case HAL_ODM_P2P_STATE:
+ ODM_CmnInfoUpdate23a(podmpriv, ODM_CMNINFO_WIFI_DIRECT, bSet);
+ break;
+ case HAL_ODM_WIFI_DISPLAY_STATE:
+ ODM_CmnInfoUpdate23a(podmpriv, ODM_CMNINFO_WIFI_DISPLAY, bSet);
+ break;
+ default:
+ break;
+ }
+}
+
+void rtl8723a_notch_filter(struct rtw_adapter *adapter, bool enable)
+{
+ if (enable) {
+ DBG_8723A("Enable notch filter\n");
+ rtl8723au_write8(adapter, rOFDM0_RxDSP + 1,
+ rtl8723au_read8(adapter, rOFDM0_RxDSP + 1) |
+ BIT(1));
+ } else {
+ DBG_8723A("Disable notch filter\n");
+ rtl8723au_write8(adapter, rOFDM0_RxDSP + 1,
+ rtl8723au_read8(adapter, rOFDM0_RxDSP + 1) &
+ ~BIT(1));
+ }
+}
+
+bool c2h_id_filter_ccx_8723a(u8 id)
+{
+ bool ret = false;
+ if (id == C2H_CCX_TX_RPT)
+ ret = true;
+
+ return ret;
+}
+
+int c2h_handler_8723a(struct rtw_adapter *padapter, struct c2h_evt_hdr *c2h_evt)
+{
+ int ret = _SUCCESS;
+ u8 i = 0;
+
+ if (c2h_evt == NULL) {
+ DBG_8723A("%s c2h_evt is NULL\n", __func__);
+ ret = _FAIL;
+ goto exit;
+ }
+
+ switch (c2h_evt->id) {
+ case C2H_DBG:
+ RT_TRACE(_module_hal_init_c_, _drv_info_,
+ "C2HCommandHandler: %s\n", c2h_evt->payload);
+ break;
+
+ case C2H_CCX_TX_RPT:
+ handle_txrpt_ccx_8723a(padapter, c2h_evt->payload);
+ break;
+ case C2H_EXT_RA_RPT:
+ break;
+ case C2H_HW_INFO_EXCH:
+ RT_TRACE(_module_hal_init_c_, _drv_info_,
+ "[BT], C2H_HW_INFO_EXCH\n");
+ for (i = 0; i < c2h_evt->plen; i++) {
+ RT_TRACE(_module_hal_init_c_, _drv_info_,
+ "[BT], tmpBuf[%d]= 0x%x\n", i,
+ c2h_evt->payload[i]);
+ }
+ break;
+
+ case C2H_C2H_H2C_TEST:
+ RT_TRACE(_module_hal_init_c_, _drv_info_,
+ "[BT], C2H_H2C_TEST\n");
+ RT_TRACE(_module_hal_init_c_, _drv_info_,
+ "[BT], tmpBuf[0]/[1]/[2]/[3]/[4]= 0x%x/ 0x%x/ 0x%x/ 0x%x/ 0x%x\n",
+ c2h_evt->payload[0],
+ c2h_evt->payload[1], c2h_evt->payload[2],
+ c2h_evt->payload[3], c2h_evt->payload[4]);
+ break;
+
+ case C2H_BT_INFO:
+ DBG_8723A("%s , Got C2H_BT_INFO \n", __func__);
+ rtl8723a_fw_c2h_BT_info(padapter,
+ c2h_evt->payload, c2h_evt->plen);
+ break;
+
+ default:
+ ret = _FAIL;
+ break;
+ }
+
+exit:
+ return ret;
+}
+
+void handle_txrpt_ccx_8723a(struct rtw_adapter *adapter, void *buf)
+{
+ struct txrpt_ccx_8723a *txrpt_ccx = buf;
+ struct submit_ctx *pack_tx_ops = &adapter->xmitpriv.ack_tx_ops;
+
+ if (txrpt_ccx->int_ccx && adapter->xmitpriv.ack_tx) {
+ if (txrpt_ccx->pkt_ok)
+ rtw23a_sctx_done_err(&pack_tx_ops,
+ RTW_SCTX_DONE_SUCCESS);
+ else
+ rtw23a_sctx_done_err(&pack_tx_ops,
+ RTW_SCTX_DONE_CCX_PKT_FAIL);
+ }
+}
+
+void rtl8723a_InitAntenna_Selection(struct rtw_adapter *padapter)
+{
+ u8 val;
+
+ val = rtl8723au_read8(padapter, REG_LEDCFG2);
+ /* Let 8051 take control antenna settting */
+ val |= BIT(7); /* DPDT_SEL_EN, 0x4C[23] */
+ rtl8723au_write8(padapter, REG_LEDCFG2, val);
+}
+
+void rtl8723a_CheckAntenna_Selection(struct rtw_adapter *padapter)
+{
+ u8 val;
+
+ val = rtl8723au_read8(padapter, REG_LEDCFG2);
+ /* Let 8051 take control antenna settting */
+ if (!(val & BIT(7))) {
+ val |= BIT(7); /* DPDT_SEL_EN, 0x4C[23] */
+ rtl8723au_write8(padapter, REG_LEDCFG2, val);
+ }
+}
+
+void rtl8723a_DeinitAntenna_Selection(struct rtw_adapter *padapter)
+{
+ u8 val;
+
+ val = rtl8723au_read8(padapter, REG_LEDCFG2);
+ /* Let 8051 take control antenna settting */
+ val &= ~BIT(7); /* DPDT_SEL_EN, clear 0x4C[23] */
+ rtl8723au_write8(padapter, REG_LEDCFG2, val);
+}
+
+void rtl8723a_init_default_value(struct rtw_adapter *padapter)
+{
+ struct hal_data_8723a *pHalData;
+ struct dm_priv *pdmpriv;
+ u8 i;
+
+ pHalData = GET_HAL_DATA(padapter);
+ pdmpriv = &pHalData->dmpriv;
+
+ /* init default value */
+ pHalData->bIQKInitialized = false;
+ if (!padapter->pwrctrlpriv.bkeepfwalive)
+ pHalData->LastHMEBoxNum = 0;
+
+ pHalData->bIQKInitialized = false;
+
+ /* init dm default value */
+ pdmpriv->TM_Trigger = 0; /* for IQK */
+/* pdmpriv->binitialized = false; */
+/* pdmpriv->prv_traffic_idx = 3; */
+/* pdmpriv->initialize = 0; */
+
+ pdmpriv->ThermalValue_HP_index = 0;
+ for (i = 0; i < HP_THERMAL_NUM; i++)
+ pdmpriv->ThermalValue_HP[i] = 0;
+
+ /* init Efuse variables */
+ pHalData->EfuseUsedBytes = 0;
+ pHalData->BTEfuseUsedBytes = 0;
+}
+
+u8 GetEEPROMSize8723A(struct rtw_adapter *padapter)
+{
+ u8 size = 0;
+ u32 cr;
+
+ cr = rtl8723au_read16(padapter, REG_9346CR);
+ /* 6: EEPROM used is 93C46, 4: boot from E-Fuse. */
+ size = (cr & BOOT_FROM_EEPROM) ? 6 : 4;
+
+ MSG_8723A("EEPROM type is %s\n", size == 4 ? "E-FUSE" : "93C46");
+
+ return size;
+}
+
+/* */
+/* */
+/* LLT R/W/Init function */
+/* */
+/* */
+static int _LLTWrite(struct rtw_adapter *padapter, u32 address, u32 data)
+{
+ int status = _SUCCESS;
+ s32 count = 0;
+ u32 value = _LLT_INIT_ADDR(address) | _LLT_INIT_DATA(data) |
+ _LLT_OP(_LLT_WRITE_ACCESS);
+ u16 LLTReg = REG_LLT_INIT;
+
+ rtl8723au_write32(padapter, LLTReg, value);
+
+ /* polling */
+ do {
+ value = rtl8723au_read32(padapter, LLTReg);
+ if (_LLT_NO_ACTIVE == _LLT_OP_VALUE(value))
+ break;
+
+ if (count > POLLING_LLT_THRESHOLD) {
+ RT_TRACE(_module_hal_init_c_, _drv_err_,
+ "Failed to polling write LLT done at address %d!\n",
+ address);
+ status = _FAIL;
+ break;
+ }
+ } while (count++);
+
+ return status;
+}
+
+int InitLLTTable23a(struct rtw_adapter *padapter, u32 boundary)
+{
+ int status = _SUCCESS;
+ u32 i;
+ u32 txpktbuf_bndy = boundary;
+ u32 Last_Entry_Of_TxPktBuf = LAST_ENTRY_OF_TX_PKT_BUFFER;
+
+ for (i = 0; i < (txpktbuf_bndy - 1); i++) {
+ status = _LLTWrite(padapter, i, i + 1);
+ if (status != _SUCCESS)
+ return status;
+ }
+
+ /* end of list */
+ status = _LLTWrite(padapter, (txpktbuf_bndy - 1), 0xFF);
+ if (status != _SUCCESS)
+ return status;
+
+ /* Make the other pages as ring buffer */
+ /* This ring buffer is used as beacon buffer if we config this
+ MAC as two MAC transfer. */
+ /* Otherwise used as local loopback buffer. */
+ for (i = txpktbuf_bndy; i < Last_Entry_Of_TxPktBuf; i++) {
+ status = _LLTWrite(padapter, i, (i + 1));
+ if (_SUCCESS != status)
+ return status;
+ }
+
+ /* Let last entry point to the start entry of ring buffer */
+ status = _LLTWrite(padapter, Last_Entry_Of_TxPktBuf, txpktbuf_bndy);
+ if (status != _SUCCESS)
+ return status;
+
+ return status;
+}
+
+static void _DisableGPIO(struct rtw_adapter *padapter)
+{
+/***************************************
+j. GPIO_PIN_CTRL 0x44[31:0]= 0x000
+k.Value = GPIO_PIN_CTRL[7:0]
+l. GPIO_PIN_CTRL 0x44[31:0] = 0x00FF0000 | (value <<8); write external PIN level
+m. GPIO_MUXCFG 0x42 [15:0] = 0x0780
+n. LEDCFG 0x4C[15:0] = 0x8080
+***************************************/
+ u32 value32;
+ u32 u4bTmp;
+
+ /* 1. Disable GPIO[7:0] */
+ rtl8723au_write16(padapter, REG_GPIO_PIN_CTRL + 2, 0x0000);
+ value32 = rtl8723au_read32(padapter, REG_GPIO_PIN_CTRL) & 0xFFFF00FF;
+ u4bTmp = value32 & 0x000000FF;
+ value32 |= ((u4bTmp << 8) | 0x00FF0000);
+ rtl8723au_write32(padapter, REG_GPIO_PIN_CTRL, value32);
+
+ /* */
+ /* <Roger_Notes> For RTL8723u multi-function configuration which
+ was autoload from Efuse offset 0x0a and 0x0b, */
+ /* WLAN HW GPIO[9], GPS HW GPIO[10] and BT HW GPIO[11]. */
+ /* Added by Roger, 2010.10.07. */
+ /* */
+ /* 2. Disable GPIO[8] and GPIO[12] */
+
+ /* Configure all pins as input mode. */
+ rtl8723au_write16(padapter, REG_GPIO_IO_SEL_2, 0x0000);
+ value32 = rtl8723au_read32(padapter, REG_GPIO_PIN_CTRL_2) & 0xFFFF001F;
+ u4bTmp = value32 & 0x0000001F;
+ /* Set pin 8, 10, 11 and pin 12 to output mode. */
+ value32 |= ((u4bTmp << 8) | 0x001D0000);
+ rtl8723au_write32(padapter, REG_GPIO_PIN_CTRL_2, value32);
+
+ /* 3. Disable LED0 & 1 */
+ rtl8723au_write16(padapter, REG_LEDCFG0, 0x8080);
+} /* end of _DisableGPIO() */
+
+static void _DisableRFAFEAndResetBB8192C(struct rtw_adapter *padapter)
+{
+/**************************************
+a. TXPAUSE 0x522[7:0] = 0xFF Pause MAC TX queue
+b. RF path 0 offset 0x00 = 0x00 disable RF
+c. APSD_CTRL 0x600[7:0] = 0x40
+d. SYS_FUNC_EN 0x02[7:0] = 0x16 reset BB state machine
+e. SYS_FUNC_EN 0x02[7:0] = 0x14 reset BB state machine
+***************************************/
+ u8 value8;
+
+ rtl8723au_write8(padapter, REG_TXPAUSE, 0xFF);
+
+ PHY_SetRFReg(padapter, RF_PATH_A, 0x0, bMaskByte0, 0x0);
+
+ value8 = APSDOFF;
+ rtl8723au_write8(padapter, REG_APSD_CTRL, value8); /* 0x40 */
+
+ /* Set BB reset at first */
+ value8 = FEN_USBD | FEN_USBA | FEN_BB_GLB_RSTn;
+ rtl8723au_write8(padapter, REG_SYS_FUNC_EN, value8); /* 0x16 */
+
+ /* Set global reset. */
+ value8 &= ~FEN_BB_GLB_RSTn;
+ rtl8723au_write8(padapter, REG_SYS_FUNC_EN, value8); /* 0x14 */
+
+ /* 2010/08/12 MH We need to set BB/GLBAL reset to save power
+ for SS mode. */
+}
+
+static void _ResetDigitalProcedure1_92C(struct rtw_adapter *padapter,
+ bool bWithoutHWSM)
+{
+ struct hal_data_8723a *pHalData = GET_HAL_DATA(padapter);
+
+ if (IS_FW_81xxC(padapter) && (pHalData->FirmwareVersion <= 0x20)) {
+ /*****************************
+ f. MCUFWDL 0x80[7:0]= 0 reset MCU ready status
+ g. SYS_FUNC_EN 0x02[10]= 0 reset MCU register, (8051 reset)
+ h. SYS_FUNC_EN 0x02[15-12]= 5 reset MAC register, DCORE
+ i. SYS_FUNC_EN 0x02[10]= 1 enable MCU register,
+ (8051 enable)
+ ******************************/
+ u16 valu16;
+ rtl8723au_write8(padapter, REG_MCUFWDL, 0);
+
+ valu16 = rtl8723au_read16(padapter, REG_SYS_FUNC_EN);
+ /* reset MCU , 8051 */
+ rtl8723au_write16(padapter, REG_SYS_FUNC_EN,
+ valu16 & ~FEN_CPUEN);
+
+ valu16 = rtl8723au_read16(padapter, REG_SYS_FUNC_EN) & 0x0FFF;
+ /* reset MAC */
+ rtl8723au_write16(padapter, REG_SYS_FUNC_EN,
+ valu16 | FEN_HWPDN | FEN_ELDR);
+
+ valu16 = rtl8723au_read16(padapter, REG_SYS_FUNC_EN);
+ /* enable MCU , 8051 */
+ rtl8723au_write16(padapter, REG_SYS_FUNC_EN,
+ valu16 | FEN_CPUEN);
+ } else {
+ u8 retry_cnts = 0;
+ u8 val8;
+
+ val8 = rtl8723au_read8(padapter, REG_MCUFWDL);
+
+ /* 2010/08/12 MH For USB SS, we can not stop 8051 when we
+ are trying to enter IPS/HW&SW radio off. For
+ S3/S4/S5/Disable, we can stop 8051 because */
+ /* we will init FW when power on again. */
+ /* If we want to SS mode, we can not reset 8051. */
+ if ((val8 & BIT(1)) && padapter->bFWReady) {
+ /* IF fw in RAM code, do reset */
+ /* 2010/08/25 MH Accordign to RD alfred's
+ suggestion, we need to disable other */
+ /* HRCV INT to influence 8051 reset. */
+ rtl8723au_write8(padapter, REG_FWIMR, 0x20);
+ /* 2011/02/15 MH According to Alex's
+ suggestion, close mask to prevent
+ incorrect FW write operation. */
+ rtl8723au_write8(padapter, REG_FTIMR, 0x00);
+ rtl8723au_write8(padapter, REG_FSIMR, 0x00);
+
+ /* 8051 reset by self */
+ rtl8723au_write8(padapter, REG_HMETFR + 3, 0x20);
+
+ while ((retry_cnts++ < 100) &&
+ (rtl8723au_read16(padapter, REG_SYS_FUNC_EN) &
+ FEN_CPUEN)) {
+ udelay(50); /* us */
+ }
+
+ if (retry_cnts >= 100) {
+ /* Reset MAC and Enable 8051 */
+ rtl8723au_write8(padapter,
+ REG_SYS_FUNC_EN + 1, 0x50);
+ mdelay(10);
+ }
+ }
+ /* Reset MAC and Enable 8051 */
+ rtl8723au_write8(padapter, REG_SYS_FUNC_EN + 1, 0x54);
+ rtl8723au_write8(padapter, REG_MCUFWDL, 0);
+ }
+
+ if (bWithoutHWSM) {
+ /*****************************
+ Without HW auto state machine
+ g. SYS_CLKR 0x08[15:0] = 0x30A3 disable MAC clock
+ h. AFE_PLL_CTRL 0x28[7:0] = 0x80 disable AFE PLL
+ i. AFE_XTAL_CTRL 0x24[15:0] = 0x880F gated AFE DIG_CLOCK
+ j. SYS_ISO_CTRL 0x00[7:0] = 0xF9 isolated digital to PON
+ ******************************/
+ /* modify to 0x70A3 by Scott. */
+ rtl8723au_write16(padapter, REG_SYS_CLKR, 0x70A3);
+ rtl8723au_write8(padapter, REG_AFE_PLL_CTRL, 0x80);
+ rtl8723au_write16(padapter, REG_AFE_XTAL_CTRL, 0x880F);
+ rtl8723au_write8(padapter, REG_SYS_ISO_CTRL, 0xF9);
+ } else {
+ /* Disable all RF/BB power */
+ rtl8723au_write8(padapter, REG_RF_CTRL, 0x00);
+ }
+}
+
+static void _ResetDigitalProcedure2(struct rtw_adapter *padapter)
+{
+/*****************************
+k. SYS_FUNC_EN 0x03[7:0] = 0x44 disable ELDR runction
+l. SYS_CLKR 0x08[15:0] = 0x3083 disable ELDR clock
+m. SYS_ISO_CTRL 0x01[7:0] = 0x83 isolated ELDR to PON
+******************************/
+ /* modify to 0x70a3 by Scott. */
+ rtl8723au_write16(padapter, REG_SYS_CLKR, 0x70a3);
+ /* modify to 0x82 by Scott. */
+ rtl8723au_write8(padapter, REG_SYS_ISO_CTRL + 1, 0x82);
+}
+
+static void _DisableAnalog(struct rtw_adapter *padapter, bool bWithoutHWSM)
+{
+ struct hal_data_8723a *pHalData = GET_HAL_DATA(padapter);
+ u16 value16;
+ u8 value8;
+
+ if (bWithoutHWSM) {
+ /*****************************
+ n. LDOA15_CTRL 0x20[7:0] = 0x04 disable A15 power
+ o. LDOV12D_CTRL 0x21[7:0] = 0x54 disable digital core power
+ r. When driver call disable, the ASIC will turn off remaining
+ clock automatically
+ ******************************/
+
+ rtl8723au_write8(padapter, REG_LDOA15_CTRL, 0x04);
+ /* rtl8723au_write8(padapter, REG_LDOV12D_CTRL, 0x54); */
+
+ value8 = rtl8723au_read8(padapter, REG_LDOV12D_CTRL);
+ value8 &= ~LDV12_EN;
+ rtl8723au_write8(padapter, REG_LDOV12D_CTRL, value8);
+ }
+
+ /*****************************
+ h. SPS0_CTRL 0x11[7:0] = 0x23 enter PFM mode
+ i. APS_FSMCO 0x04[15:0] = 0x4802 set USB suspend
+ ******************************/
+ value8 = 0x23;
+ if (IS_81xxC_VENDOR_UMC_B_CUT(pHalData->VersionID))
+ value8 |= BIT(3);
+
+ rtl8723au_write8(padapter, REG_SPS0_CTRL, value8);
+
+ if (bWithoutHWSM) {
+ /* value16 |= (APDM_HOST | FSM_HSUS |/PFM_ALDN); */
+ /* 2010/08/31 According to Filen description, we need to
+ use HW to shut down 8051 automatically. */
+ /* Becasue suspend operatione need the asistance of 8051
+ to wait for 3ms. */
+ value16 = APDM_HOST | AFSM_HSUS | PFM_ALDN;
+ } else {
+ value16 = APDM_HOST | AFSM_HSUS | PFM_ALDN;
+ }
+
+ rtl8723au_write16(padapter, REG_APS_FSMCO, value16); /* 0x4802 */
+
+ rtl8723au_write8(padapter, REG_RSV_CTRL, 0x0e);
+}
+
+/* HW Auto state machine */
+int CardDisableHWSM(struct rtw_adapter *padapter, u8 resetMCU)
+{
+ if (padapter->bSurpriseRemoved)
+ return _SUCCESS;
+
+ /* RF Off Sequence ==== */
+ _DisableRFAFEAndResetBB8192C(padapter);
+
+ /* ==== Reset digital sequence ====== */
+ _ResetDigitalProcedure1_92C(padapter, false);
+
+ /* ==== Pull GPIO PIN to balance level and LED control ====== */
+ _DisableGPIO(padapter);
+
+ /* ==== Disable analog sequence === */
+ _DisableAnalog(padapter, false);
+
+ RT_TRACE(_module_hci_hal_init_c_, _drv_info_,
+ "======> Card disable finished.\n");
+
+ return _SUCCESS;
+}
+
+/* without HW Auto state machine */
+int CardDisableWithoutHWSM(struct rtw_adapter *padapter)
+{
+ if (padapter->bSurpriseRemoved)
+ return _SUCCESS;
+
+ /* RF Off Sequence ==== */
+ _DisableRFAFEAndResetBB8192C(padapter);
+
+ /* ==== Reset digital sequence ====== */
+ _ResetDigitalProcedure1_92C(padapter, true);
+
+ /* ==== Pull GPIO PIN to balance level and LED control ====== */
+ _DisableGPIO(padapter);
+
+ /* ==== Reset digital sequence ====== */
+ _ResetDigitalProcedure2(padapter);
+
+ /* ==== Disable analog sequence === */
+ _DisableAnalog(padapter, true);
+
+ return _SUCCESS;
+}
+
+void Hal_InitPGData(struct rtw_adapter *padapter, u8 *PROMContent)
+{
+ struct eeprom_priv *pEEPROM = GET_EEPROM_EFUSE_PRIV(padapter);
+
+ if (!pEEPROM->bautoload_fail_flag) { /* autoload OK. */
+ if (!pEEPROM->EepromOrEfuse) {
+ /* Read EFUSE real map to shadow. */
+ EFUSE_ShadowMapUpdate23a(padapter, EFUSE_WIFI);
+ memcpy(PROMContent, pEEPROM->efuse_eeprom_data,
+ HWSET_MAX_SIZE);
+ }
+ } else {
+ RT_TRACE(_module_hci_hal_init_c_, _drv_notice_,
+ "AutoLoad Fail reported from CR9346!!\n");
+ /* update to default value 0xFF */
+ if (!pEEPROM->EepromOrEfuse)
+ EFUSE_ShadowMapUpdate23a(padapter, EFUSE_WIFI);
+ memcpy(PROMContent, pEEPROM->efuse_eeprom_data,
+ HWSET_MAX_SIZE);
+ }
+}
+
+void Hal_EfuseParseIDCode(struct rtw_adapter *padapter, u8 *hwinfo)
+{
+ struct eeprom_priv *pEEPROM = GET_EEPROM_EFUSE_PRIV(padapter);
+/* struct hal_data_8723a *pHalData = GET_HAL_DATA(padapter); */
+ u16 EEPROMId;
+
+ /* Checl 0x8129 again for making sure autoload status!! */
+ EEPROMId = le16_to_cpu(*((u16 *) hwinfo));
+ if (EEPROMId != RTL_EEPROM_ID) {
+ DBG_8723A("EEPROM ID(%#x) is invalid!!\n", EEPROMId);
+ pEEPROM->bautoload_fail_flag = true;
+ } else {
+ pEEPROM->bautoload_fail_flag = false;
+ }
+
+ RT_TRACE(_module_hal_init_c_, _drv_info_,
+ "EEPROM ID = 0x%04x\n", EEPROMId);
+}
+
+static void Hal_EEValueCheck(u8 EEType, void *pInValue, void *pOutValue)
+{
+ switch (EEType) {
+ case EETYPE_TX_PWR:
+ {
+ u8 *pIn, *pOut;
+ pIn = (u8 *) pInValue;
+ pOut = (u8 *) pOutValue;
+ if (*pIn <= 63)
+ *pOut = *pIn;
+ else {
+ RT_TRACE(_module_hci_hal_init_c_, _drv_err_,
+ "EETYPE_TX_PWR, value =%d is invalid, set to default = 0x%x\n",
+ *pIn, EEPROM_Default_TxPowerLevel);
+ *pOut = EEPROM_Default_TxPowerLevel;
+ }
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+static void
+Hal_ReadPowerValueFromPROM_8723A(struct txpowerinfo *pwrInfo,
+ u8 *PROMContent, bool AutoLoadFail)
+{
+ u32 rfPath, eeAddr, group, rfPathMax = 1;
+
+ memset(pwrInfo, 0, sizeof(*pwrInfo));
+
+ if (AutoLoadFail) {
+ for (group = 0; group < MAX_CHNL_GROUP; group++) {
+ for (rfPath = 0; rfPath < rfPathMax; rfPath++) {
+ pwrInfo->CCKIndex[rfPath][group] =
+ EEPROM_Default_TxPowerLevel;
+ pwrInfo->HT40_1SIndex[rfPath][group] =
+ EEPROM_Default_TxPowerLevel;
+ pwrInfo->HT40_2SIndexDiff[rfPath][group] =
+ EEPROM_Default_HT40_2SDiff;
+ pwrInfo->HT20IndexDiff[rfPath][group] =
+ EEPROM_Default_HT20_Diff;
+ pwrInfo->OFDMIndexDiff[rfPath][group] =
+ EEPROM_Default_LegacyHTTxPowerDiff;
+ pwrInfo->HT40MaxOffset[rfPath][group] =
+ EEPROM_Default_HT40_PwrMaxOffset;
+ pwrInfo->HT20MaxOffset[rfPath][group] =
+ EEPROM_Default_HT20_PwrMaxOffset;
+ }
+ }
+ pwrInfo->TSSI_A[0] = EEPROM_Default_TSSI;
+ return;
+ }
+
+ for (rfPath = 0; rfPath < rfPathMax; rfPath++) {
+ for (group = 0; group < MAX_CHNL_GROUP; group++) {
+ eeAddr =
+ EEPROM_CCK_TX_PWR_INX_8723A + (rfPath * 3) + group;
+ /* pwrInfo->CCKIndex[rfPath][group] =
+ PROMContent[eeAddr]; */
+ Hal_EEValueCheck(EETYPE_TX_PWR, &PROMContent[eeAddr],
+ &pwrInfo->CCKIndex[rfPath][group]);
+ eeAddr = EEPROM_HT40_1S_TX_PWR_INX_8723A +
+ (rfPath * 3) + group;
+ /* pwrInfo->HT40_1SIndex[rfPath][group] =
+ PROMContent[eeAddr]; */
+ Hal_EEValueCheck(EETYPE_TX_PWR, &PROMContent[eeAddr],
+ &pwrInfo->HT40_1SIndex[rfPath][group]);
+ }
+ }
+
+ for (group = 0; group < MAX_CHNL_GROUP; group++) {
+ for (rfPath = 0; rfPath < rfPathMax; rfPath++) {
+ pwrInfo->HT40_2SIndexDiff[rfPath][group] = 0;
+ pwrInfo->HT20IndexDiff[rfPath][group] =
+ (PROMContent
+ [EEPROM_HT20_TX_PWR_INX_DIFF_8723A +
+ group] >> (rfPath * 4)) & 0xF;
+ /* 4bit sign number to 8 bit sign number */
+ if (pwrInfo->HT20IndexDiff[rfPath][group] & BIT(3))
+ pwrInfo->HT20IndexDiff[rfPath][group] |= 0xF0;
+
+ pwrInfo->OFDMIndexDiff[rfPath][group] =
+ (PROMContent[EEPROM_OFDM_TX_PWR_INX_DIFF_8723A +
+ group] >> (rfPath * 4)) & 0xF;
+
+ pwrInfo->HT40MaxOffset[rfPath][group] =
+ (PROMContent[EEPROM_HT40_MAX_PWR_OFFSET_8723A +
+ group] >> (rfPath * 4)) & 0xF;
+
+ pwrInfo->HT20MaxOffset[rfPath][group] =
+ (PROMContent[EEPROM_HT20_MAX_PWR_OFFSET_8723A +
+ group] >> (rfPath * 4)) & 0xF;
+ }
+ }
+
+ pwrInfo->TSSI_A[0] = PROMContent[EEPROM_TSSI_A_8723A];
+}
+
+static u8 Hal_GetChnlGroup(u8 chnl)
+{
+ u8 group = 0;
+
+ if (chnl < 3) /* Cjanel 1-3 */
+ group = 0;
+ else if (chnl < 9) /* Channel 4-9 */
+ group = 1;
+ else /* Channel 10-14 */
+ group = 2;
+
+ return group;
+}
+
+void
+Hal_EfuseParsetxpowerinfo_8723A(struct rtw_adapter *padapter,
+ u8 *PROMContent, bool AutoLoadFail)
+{
+ struct hal_data_8723a *pHalData = GET_HAL_DATA(padapter);
+ struct txpowerinfo pwrInfo;
+ u8 rfPath, ch, group, rfPathMax = 1;
+ u8 pwr, diff;
+
+ Hal_ReadPowerValueFromPROM_8723A(&pwrInfo, PROMContent, AutoLoadFail);
+ for (rfPath = 0; rfPath < rfPathMax; rfPath++) {
+ for (ch = 0; ch < CHANNEL_MAX_NUMBER; ch++) {
+ group = Hal_GetChnlGroup(ch);
+
+ pHalData->TxPwrLevelCck[rfPath][ch] =
+ pwrInfo.CCKIndex[rfPath][group];
+ pHalData->TxPwrLevelHT40_1S[rfPath][ch] =
+ pwrInfo.HT40_1SIndex[rfPath][group];
+
+ pHalData->TxPwrHt20Diff[rfPath][ch] =
+ pwrInfo.HT20IndexDiff[rfPath][group];
+ pHalData->TxPwrLegacyHtDiff[rfPath][ch] =
+ pwrInfo.OFDMIndexDiff[rfPath][group];
+ pHalData->PwrGroupHT20[rfPath][ch] =
+ pwrInfo.HT20MaxOffset[rfPath][group];
+ pHalData->PwrGroupHT40[rfPath][ch] =
+ pwrInfo.HT40MaxOffset[rfPath][group];
+
+ pwr = pwrInfo.HT40_1SIndex[rfPath][group];
+ diff = pwrInfo.HT40_2SIndexDiff[rfPath][group];
+
+ pHalData->TxPwrLevelHT40_2S[rfPath][ch] =
+ (pwr > diff) ? (pwr - diff) : 0;
+ }
+ }
+ for (rfPath = 0; rfPath < RF_PATH_MAX; rfPath++) {
+ for (ch = 0; ch < CHANNEL_MAX_NUMBER; ch++) {
+ RT_TRACE(_module_hci_hal_init_c_, _drv_info_,
+ "RF(%u)-Ch(%u) [CCK / HT40_1S / HT40_2S] = [0x%x / 0x%x / 0x%x]\n",
+ rfPath, ch,
+ pHalData->TxPwrLevelCck[rfPath][ch],
+ pHalData->TxPwrLevelHT40_1S[rfPath][ch],
+ pHalData->TxPwrLevelHT40_2S[rfPath][ch]);
+
+ }
+ }
+ for (ch = 0; ch < CHANNEL_MAX_NUMBER; ch++) {
+ RT_TRACE(_module_hci_hal_init_c_, _drv_info_,
+ "RF-A Ht20 to HT40 Diff[%u] = 0x%x(%d)\n", ch,
+ pHalData->TxPwrHt20Diff[RF_PATH_A][ch],
+ pHalData->TxPwrHt20Diff[RF_PATH_A][ch]);
+ }
+ for (ch = 0; ch < CHANNEL_MAX_NUMBER; ch++)
+ RT_TRACE(_module_hci_hal_init_c_, _drv_info_,
+ "RF-A Legacy to Ht40 Diff[%u] = 0x%x\n", ch,
+ pHalData->TxPwrLegacyHtDiff[RF_PATH_A][ch]);
+ for (ch = 0; ch < CHANNEL_MAX_NUMBER; ch++) {
+ RT_TRACE(_module_hci_hal_init_c_, _drv_info_,
+ "RF-B Ht20 to HT40 Diff[%u] = 0x%x(%d)\n", ch,
+ pHalData->TxPwrHt20Diff[RF_PATH_B][ch],
+ pHalData->TxPwrHt20Diff[RF_PATH_B][ch]);
+ }
+ for (ch = 0; ch < CHANNEL_MAX_NUMBER; ch++)
+ RT_TRACE(_module_hci_hal_init_c_, _drv_info_,
+ "RF-B Legacy to HT40 Diff[%u] = 0x%x\n", ch,
+ pHalData->TxPwrLegacyHtDiff[RF_PATH_B][ch]);
+ if (!AutoLoadFail) {
+ struct registry_priv *registry_par = &padapter->registrypriv;
+ if (registry_par->regulatory_tid == 0xff) {
+ if (PROMContent[RF_OPTION1_8723A] == 0xff)
+ pHalData->EEPROMRegulatory = 0;
+ else
+ pHalData->EEPROMRegulatory =
+ PROMContent[RF_OPTION1_8723A] & 0x7;
+ } else {
+ pHalData->EEPROMRegulatory =
+ registry_par->regulatory_tid;
+ }
+ } else {
+ pHalData->EEPROMRegulatory = 0;
+ }
+ RT_TRACE(_module_hci_hal_init_c_, _drv_info_,
+ "EEPROMRegulatory = 0x%x\n", pHalData->EEPROMRegulatory);
+
+ if (!AutoLoadFail)
+ pHalData->bTXPowerDataReadFromEEPORM = true;
+}
+
+void
+Hal_EfuseParseBTCoexistInfo_8723A(struct rtw_adapter *padapter,
+ u8 *hwinfo, bool AutoLoadFail)
+{
+ struct hal_data_8723a *pHalData = GET_HAL_DATA(padapter);
+ u8 tempval;
+ u32 tmpu4;
+
+ if (!AutoLoadFail) {
+ tmpu4 = rtl8723au_read32(padapter, REG_MULTI_FUNC_CTRL);
+ if (tmpu4 & BT_FUNC_EN)
+ pHalData->EEPROMBluetoothCoexist = 1;
+ else
+ pHalData->EEPROMBluetoothCoexist = 0;
+ pHalData->EEPROMBluetoothType = BT_RTL8723A;
+
+ /* The following need to be checked with newer version of */
+ /* eeprom spec */
+ tempval = hwinfo[RF_OPTION4_8723A];
+ pHalData->EEPROMBluetoothAntNum = (tempval & 0x1);
+ pHalData->EEPROMBluetoothAntIsolation = (tempval & 0x10) >> 4;
+ pHalData->EEPROMBluetoothRadioShared = (tempval & 0x20) >> 5;
+ } else {
+ pHalData->EEPROMBluetoothCoexist = 0;
+ pHalData->EEPROMBluetoothType = BT_RTL8723A;
+ pHalData->EEPROMBluetoothAntNum = Ant_x2;
+ pHalData->EEPROMBluetoothAntIsolation = 0;
+ pHalData->EEPROMBluetoothRadioShared = BT_Radio_Shared;
+ }
+
+ rtl8723a_BT_init_hal_vars(padapter);
+}
+
+void
+Hal_EfuseParseEEPROMVer(struct rtw_adapter *padapter,
+ u8 *hwinfo, bool AutoLoadFail)
+{
+ struct hal_data_8723a *pHalData = GET_HAL_DATA(padapter);
+
+ if (!AutoLoadFail)
+ pHalData->EEPROMVersion = hwinfo[EEPROM_VERSION_8723A];
+ else
+ pHalData->EEPROMVersion = 1;
+ RT_TRACE(_module_hci_hal_init_c_, _drv_info_,
+ "Hal_EfuseParseEEPROMVer(), EEVer = %d\n",
+ pHalData->EEPROMVersion);
+}
+
+void
+rtl8723a_EfuseParseChnlPlan(struct rtw_adapter *padapter,
+ u8 *hwinfo, bool AutoLoadFail)
+{
+ padapter->mlmepriv.ChannelPlan =
+ hal_com_get_channel_plan23a(padapter, hwinfo ?
+ hwinfo[EEPROM_ChannelPlan_8723A]:0xFF,
+ padapter->registrypriv.channel_plan,
+ RT_CHANNEL_DOMAIN_WORLD_WIDE_13,
+ AutoLoadFail);
+
+ DBG_8723A("mlmepriv.ChannelPlan = 0x%02x\n",
+ padapter->mlmepriv.ChannelPlan);
+}
+
+void
+Hal_EfuseParseCustomerID(struct rtw_adapter *padapter,
+ u8 *hwinfo, bool AutoLoadFail)
+{
+ struct hal_data_8723a *pHalData = GET_HAL_DATA(padapter);
+
+ if (!AutoLoadFail) {
+ pHalData->EEPROMCustomerID = hwinfo[EEPROM_CustomID_8723A];
+ pHalData->EEPROMSubCustomerID =
+ hwinfo[EEPROM_SubCustomID_8723A];
+ } else {
+ pHalData->EEPROMCustomerID = 0;
+ pHalData->EEPROMSubCustomerID = 0;
+ }
+ RT_TRACE(_module_hci_hal_init_c_, _drv_info_,
+ "EEPROM Customer ID: 0x%2x\n", pHalData->EEPROMCustomerID);
+ RT_TRACE(_module_hci_hal_init_c_, _drv_info_,
+ "EEPROM SubCustomer ID: 0x%02x\n",
+ pHalData->EEPROMSubCustomerID);
+}
+
+void
+Hal_EfuseParseAntennaDiversity(struct rtw_adapter *padapter,
+ u8 *hwinfo, bool AutoLoadFail)
+{
+}
+
+void
+Hal_EfuseParseRateIndicationOption(struct rtw_adapter *padapter,
+ u8 *hwinfo, bool AutoLoadFail)
+{
+}
+
+void
+Hal_EfuseParseXtal_8723A(struct rtw_adapter *pAdapter,
+ u8 *hwinfo, u8 AutoLoadFail)
+{
+ struct hal_data_8723a *pHalData = GET_HAL_DATA(pAdapter);
+
+ if (!AutoLoadFail) {
+ pHalData->CrystalCap = hwinfo[EEPROM_XTAL_K_8723A];
+ if (pHalData->CrystalCap == 0xFF)
+ pHalData->CrystalCap = EEPROM_Default_CrystalCap_8723A;
+ } else {
+ pHalData->CrystalCap = EEPROM_Default_CrystalCap_8723A;
+ }
+ RT_TRACE(_module_hci_hal_init_c_, _drv_info_,
+ "%s: CrystalCap = 0x%2x\n", __func__,
+ pHalData->CrystalCap);
+}
+
+void
+Hal_EfuseParseThermalMeter_8723A(struct rtw_adapter *padapter,
+ u8 *PROMContent, bool AutoloadFail)
+{
+ struct hal_data_8723a *pHalData = GET_HAL_DATA(padapter);
+
+ /* */
+ /* ThermalMeter from EEPROM */
+ /* */
+ if (!AutoloadFail)
+ pHalData->EEPROMThermalMeter =
+ PROMContent[EEPROM_THERMAL_METER_8723A];
+ else
+ pHalData->EEPROMThermalMeter = EEPROM_Default_ThermalMeter;
+
+ if ((pHalData->EEPROMThermalMeter == 0xff) || AutoloadFail) {
+ pHalData->bAPKThermalMeterIgnore = true;
+ pHalData->EEPROMThermalMeter = EEPROM_Default_ThermalMeter;
+ }
+
+ DBG_8723A("%s: ThermalMeter = 0x%x\n", __func__,
+ pHalData->EEPROMThermalMeter);
+}
+
+static void rtl8723a_cal_txdesc_chksum(struct tx_desc *ptxdesc)
+{
+ u16 *usPtr = (u16 *) ptxdesc;
+ u32 count = 16; /* (32 bytes / 2 bytes per XOR) => 16 times */
+ u32 index;
+ u16 checksum = 0;
+
+ /* Clear first */
+ ptxdesc->txdw7 &= cpu_to_le32(0xffff0000);
+
+ for (index = 0; index < count; index++)
+ checksum ^= le16_to_cpu(*(usPtr + index));
+
+ ptxdesc->txdw7 |= cpu_to_le32(checksum & 0x0000ffff);
+}
+
+/*
+ * Description: In normal chip, we should send some packet to Hw which
+ * will be used by Fw in FW LPS mode. The function is to fill the Tx
+ * descriptor of this packets, then
+ */
+/* Fw can tell Hw to send these packet derectly. */
+/* Added by tynli. 2009.10.15. */
+/* */
+void rtl8723a_fill_fake_txdesc(struct rtw_adapter *padapter, u8 *pDesc,
+ u32 BufferLen, u8 IsPsPoll, u8 IsBTQosNull)
+{
+ struct tx_desc *ptxdesc;
+
+ /* Clear all status */
+ ptxdesc = (struct tx_desc *)pDesc;
+ memset(pDesc, 0, TXDESC_SIZE);
+
+ /* offset 0 */
+ /* own, bFirstSeg, bLastSeg; */
+ ptxdesc->txdw0 |= cpu_to_le32(OWN | FSG | LSG);
+
+ /* 32 bytes for TX Desc */
+ ptxdesc->txdw0 |= cpu_to_le32(((TXDESC_SIZE + OFFSET_SZ) <<
+ OFFSET_SHT) & 0x00ff0000);
+
+ /* Buffer size + command header */
+ ptxdesc->txdw0 |= cpu_to_le32(BufferLen & 0x0000ffff);
+
+ /* offset 4 */
+ /* Fixed queue of Mgnt queue */
+ ptxdesc->txdw1 |= cpu_to_le32((QSLT_MGNT << QSEL_SHT) & 0x00001f00);
+
+ /* Set NAVUSEHDR to prevent Ps-poll AId filed to be changed
+ to error vlaue by Hw. */
+ if (IsPsPoll) {
+ ptxdesc->txdw1 |= cpu_to_le32(NAVUSEHDR);
+ } else {
+ /* Hw set sequence number */
+ ptxdesc->txdw4 |= cpu_to_le32(BIT(7));
+ /* set bit3 to 1. Suugested by TimChen. 2009.12.29. */
+ ptxdesc->txdw3 |= cpu_to_le32((8 << 28));
+ }
+
+ if (true == IsBTQosNull)
+ ptxdesc->txdw2 |= cpu_to_le32(BIT(23)); /* BT NULL */
+
+ /* offset 16 */
+ ptxdesc->txdw4 |= cpu_to_le32(BIT(8)); /* driver uses rate */
+
+ /* USB interface drop packet if the checksum of descriptor isn't
+ correct. */
+ /* Using this checksum can let hardware recovery from packet bulk
+ out error (e.g. Cancel URC, Bulk out error.). */
+ rtl8723a_cal_txdesc_chksum(ptxdesc);
+}
+
+void hw_var_set_opmode(struct rtw_adapter *padapter, u8 mode)
+{
+ u8 val8;
+
+ if (mode == MSR_INFRA || mode == MSR_NOLINK) {
+ StopTxBeacon(padapter);
+
+ /* disable atim wnd */
+ val8 = DIS_TSF_UDT | EN_BCN_FUNCTION | DIS_ATIM;
+ SetBcnCtrlReg23a(padapter, val8, ~val8);
+ } else if (mode == MSR_ADHOC) {
+ ResumeTxBeacon(padapter);
+
+ val8 = DIS_TSF_UDT | EN_BCN_FUNCTION | DIS_BCNQ_SUB;
+ SetBcnCtrlReg23a(padapter, val8, ~val8);
+ } else if (mode == MSR_AP) {
+ /* add NULL Data and BT NULL Data Packets to FW RSVD Page */
+ rtl8723a_set_BTCoex_AP_mode_FwRsvdPkt_cmd(padapter);
+
+ ResumeTxBeacon(padapter);
+
+ val8 = DIS_TSF_UDT | DIS_BCNQ_SUB;
+ SetBcnCtrlReg23a(padapter, val8, ~val8);
+
+ /* Set RCR */
+ /* rtl8723au_write32(padapter, REG_RCR, 0x70002a8e);
+ CBSSID_DATA must set to 0 */
+ /* CBSSID_DATA must set to 0 */
+ rtl8723au_write32(padapter, REG_RCR, 0x7000228e);
+ /* enable to rx data frame */
+ rtl8723au_write16(padapter, REG_RXFLTMAP2, 0xFFFF);
+ /* enable to rx ps-poll */
+ rtl8723au_write16(padapter, REG_RXFLTMAP1, 0x0400);
+
+ /* Beacon Control related register for first time */
+ /* 2ms */
+ rtl8723au_write8(padapter, REG_BCNDMATIM, 0x02);
+ /* 5ms */
+ rtl8723au_write8(padapter, REG_DRVERLYINT, 0x05);
+ /* 10ms for port0 */
+ rtl8723au_write8(padapter, REG_ATIMWND, 0x0a);
+ rtl8723au_write16(padapter, REG_BCNTCFG, 0x00);
+ rtl8723au_write16(padapter, REG_TBTT_PROHIBIT, 0xff04);
+ /* +32767 (~32ms) */
+ rtl8723au_write16(padapter, REG_TSFTR_SYN_OFFSET, 0x7fff);
+
+ /* reset TSF */
+ rtl8723au_write8(padapter, REG_DUAL_TSF_RST, BIT(0));
+
+ /* enable BCN Function */
+ /* don't enable update TSF (due to TSF update when
+ beacon/probe rsp are received) */
+ val8 = DIS_TSF_UDT | EN_BCN_FUNCTION |
+ EN_TXBCN_RPT | DIS_BCNQ_SUB;
+ SetBcnCtrlReg23a(padapter, val8, ~val8);
+ }
+
+ val8 = rtl8723au_read8(padapter, MSR);
+ val8 = (val8 & 0xC) | mode;
+ rtl8723au_write8(padapter, MSR, val8);
+}
+
+void hw_var_set_macaddr(struct rtw_adapter *padapter, u8 *val)
+{
+ u8 idx = 0;
+ u32 reg_macid;
+
+ reg_macid = REG_MACID;
+
+ for (idx = 0; idx < 6; idx++)
+ rtl8723au_write8(padapter, (reg_macid + idx), val[idx]);
+}
+
+void hw_var_set_bssid(struct rtw_adapter *padapter, u8 *val)
+{
+ u8 idx = 0;
+ u32 reg_bssid;
+
+ reg_bssid = REG_BSSID;
+
+ for (idx = 0; idx < 6; idx++)
+ rtl8723au_write8(padapter, (reg_bssid + idx), val[idx]);
+}
+
+void hw_var_set_correct_tsf(struct rtw_adapter *padapter)
+{
+ u64 tsf;
+ u32 reg_tsftr;
+ struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv;
+ struct mlme_ext_info *pmlmeinfo = &pmlmeext->mlmext_info;
+
+ /* tsf = pmlmeext->TSFValue - ((u32)pmlmeext->TSFValue %
+ (pmlmeinfo->bcn_interval*1024)) - 1024; us */
+ tsf = pmlmeext->TSFValue -
+ do_div(pmlmeext->TSFValue,
+ (pmlmeinfo->bcn_interval * 1024)) - 1024; /* us */
+
+ if (((pmlmeinfo->state & 0x03) == MSR_ADHOC) ||
+ ((pmlmeinfo->state & 0x03) == MSR_AP)) {
+ /* pHalData->RegTxPause |= STOP_BCNQ;BIT(6) */
+ /* rtl8723au_write8(padapter, REG_TXPAUSE,
+ (rtl8723au_read8(Adapter, REG_TXPAUSE)|BIT(6))); */
+ StopTxBeacon(padapter);
+ }
+
+ reg_tsftr = REG_TSFTR;
+
+ /* disable related TSF function */
+ SetBcnCtrlReg23a(padapter, 0, EN_BCN_FUNCTION);
+
+ rtl8723au_write32(padapter, reg_tsftr, tsf);
+ rtl8723au_write32(padapter, reg_tsftr + 4, tsf >> 32);
+
+ /* enable related TSF function */
+ SetBcnCtrlReg23a(padapter, EN_BCN_FUNCTION, 0);
+
+ if (((pmlmeinfo->state & 0x03) == MSR_ADHOC) ||
+ ((pmlmeinfo->state & 0x03) == MSR_AP))
+ ResumeTxBeacon(padapter);
+}
+
+void hw_var_set_mlme_disconnect(struct rtw_adapter *padapter)
+{
+ /* reject all data frames */
+ rtl8723au_write16(padapter, REG_RXFLTMAP2, 0);
+
+ /* reset TSF */
+ rtl8723au_write8(padapter, REG_DUAL_TSF_RST, BIT(0));
+
+ /* disable update TSF */
+ SetBcnCtrlReg23a(padapter, DIS_TSF_UDT, 0);
+}
+
+void hw_var_set_mlme_join(struct rtw_adapter *padapter, u8 type)
+{
+ u8 RetryLimit = 0x30;
+
+ struct hal_data_8723a *pHalData = GET_HAL_DATA(padapter);
+ struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
+
+ if (type == 0) { /* prepare to join */
+ u32 v32;
+
+ /* enable to rx data frame.Accept all data frame */
+ /* rtl8723au_write32(padapter, REG_RCR,
+ rtl8723au_read32(padapter, REG_RCR)|RCR_ADF); */
+ rtl8723au_write16(padapter, REG_RXFLTMAP2, 0xFFFF);
+
+ v32 = rtl8723au_read32(padapter, REG_RCR);
+ v32 |= RCR_CBSSID_DATA | RCR_CBSSID_BCN;
+ rtl8723au_write32(padapter, REG_RCR, v32);
+
+ if (check_fwstate(pmlmepriv, WIFI_STATION_STATE) == true)
+ RetryLimit =
+ (pHalData->CustomerID == RT_CID_CCX) ? 7 : 48;
+ else /* Ad-hoc Mode */
+ RetryLimit = 0x7;
+ } else if (type == 1) { /* joinbss_event callback when join res < 0 */
+ /* config RCR to receive different BSSID & not to
+ receive data frame during linking */
+ rtl8723au_write16(padapter, REG_RXFLTMAP2, 0);
+ } else if (type == 2) { /* sta add event callback */
+ /* enable update TSF */
+ SetBcnCtrlReg23a(padapter, 0, DIS_TSF_UDT);
+
+ if (check_fwstate(pmlmepriv,
+ WIFI_ADHOC_STATE | WIFI_ADHOC_MASTER_STATE)) {
+ /* fixed beacon issue for 8191su........... */
+ rtl8723au_write8(padapter, 0x542, 0x02);
+ RetryLimit = 0x7;
+ }
+ }
+
+ rtl8723au_write16(padapter, REG_RL,
+ RetryLimit << RETRY_LIMIT_SHORT_SHIFT | RetryLimit <<
+ RETRY_LIMIT_LONG_SHIFT);
+
+ switch (type) {
+ case 0:
+ /* prepare to join */
+ rtl8723a_BT_wifiassociate_notify(padapter, true);
+ break;
+ case 1:
+ /* joinbss_event callback when join res < 0 */
+ rtl8723a_BT_wifiassociate_notify(padapter, false);
+ break;
+ case 2:
+ /* sta add event callback */
+/* BT_WifiMediaStatusNotify(padapter, RT_MEDIA_CONNECT); */
+ break;
+ }
+}
diff --git a/drivers/staging/rtl8723au/hal/rtl8723a_phycfg.c b/drivers/staging/rtl8723au/hal/rtl8723a_phycfg.c
new file mode 100644
index 000000000..46a30659c
--- /dev/null
+++ b/drivers/staging/rtl8723au/hal/rtl8723a_phycfg.c
@@ -0,0 +1,980 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2011 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ ******************************************************************************/
+#define _RTL8723A_PHYCFG_C_
+
+#include <osdep_service.h>
+#include <drv_types.h>
+
+#include <rtl8723a_hal.h>
+#include <usb_ops_linux.h>
+
+/*---------------------------Define Local Constant---------------------------*/
+/* Channel switch:The size of command tables for switch channel*/
+#define MAX_PRECMD_CNT 16
+#define MAX_RFDEPENDCMD_CNT 16
+#define MAX_POSTCMD_CNT 16
+
+#define MAX_DOZE_WAITING_TIMES_9x 64
+
+/*---------------------------Define Local Constant---------------------------*/
+
+/*------------------------Define global variable-----------------------------*/
+
+/*------------------------Define local variable------------------------------*/
+
+/*--------------------Define export function prototype-----------------------*/
+/* Please refer to header file */
+/*--------------------Define export function prototype-----------------------*/
+
+/*----------------------------Function Body----------------------------------*/
+/* */
+/* 1. BB register R/W API */
+/* */
+
+/**
+* Function: phy_CalculateBitShift
+*
+* OverView: Get shifted position of the BitMask
+*
+* Input:
+* u32 BitMask,
+*
+* Output: none
+* Return: u32 Return the shift bit bit position of the mask
+*/
+static u32 phy_CalculateBitShift(u32 BitMask)
+{
+ u32 i;
+
+ for (i = 0; i <= 31; i++) {
+ if (((BitMask>>i) & 0x1) == 1)
+ break;
+ }
+
+ return i;
+}
+
+/**
+* Function: PHY_QueryBBReg
+*
+* OverView: Read "sepcific bits" from BB register
+*
+* Input:
+* struct rtw_adapter * Adapter,
+* u32 RegAddr, Target address to be readback
+* u32 BitMask Target bit position in the
+* target address to be readback
+* Output:
+* None
+* Return:
+* u32 Data The readback register value
+* Note:
+* This function is equal to "GetRegSetting" in PHY programming guide
+*/
+u32
+PHY_QueryBBReg(struct rtw_adapter *Adapter, u32 RegAddr, u32 BitMask)
+{
+ u32 ReturnValue = 0, OriginalValue, BitShift;
+
+ OriginalValue = rtl8723au_read32(Adapter, RegAddr);
+ BitShift = phy_CalculateBitShift(BitMask);
+ ReturnValue = (OriginalValue & BitMask) >> BitShift;
+ return ReturnValue;
+}
+
+/**
+* Function: PHY_SetBBReg
+*
+* OverView: Write "Specific bits" to BB register (page 8~)
+*
+* Input:
+* struct rtw_adapter * Adapter,
+* u32 RegAddr, Target address to be modified
+* u32 BitMask Target bit position in the
+* target address to be modified
+* u32 Data The new register value in the
+* target bit position of the
+* target address
+*
+* Output:
+* None
+* Return:
+* None
+* Note:
+* This function is equal to "PutRegSetting" in PHY programming guide
+*/
+
+void
+PHY_SetBBReg(struct rtw_adapter *Adapter, u32 RegAddr, u32 BitMask, u32 Data)
+{
+ u32 OriginalValue, BitShift;
+
+ if (BitMask != bMaskDWord) {/* if not "double word" write */
+ OriginalValue = rtl8723au_read32(Adapter, RegAddr);
+ BitShift = phy_CalculateBitShift(BitMask);
+ Data = (OriginalValue & (~BitMask)) | (Data << BitShift);
+ }
+
+ rtl8723au_write32(Adapter, RegAddr, Data);
+
+ /* RTPRINT(FPHY, PHY_BBW, ("BBW MASK = 0x%lx Addr[0x%lx]= 0x%lx\n", BitMask, RegAddr, Data)); */
+}
+
+/* */
+/* 2. RF register R/W API */
+/* */
+
+/**
+* Function: phy_RFSerialRead
+*
+* OverView: Read regster from RF chips
+*
+* Input:
+* struct rtw_adapter * Adapter,
+* enum RF_RADIO_PATH eRFPath, Radio path of A/B/C/D
+* u32 Offset, The target address to be read
+*
+* Output: None
+* Return: u32 reback value
+* Note: Threre are three types of serial operations:
+* 1. Software serial write
+* 2. Hardware LSSI-Low Speed Serial Interface
+* 3. Hardware HSSI-High speed
+* serial write. Driver need to implement (1) and (2).
+* This function is equal to the combination of RF_ReadReg() and
+* RFLSSIRead()
+*/
+static u32
+phy_RFSerialRead(struct rtw_adapter *Adapter, enum RF_RADIO_PATH eRFPath,
+ u32 Offset)
+{
+ u32 retValue = 0;
+ struct hal_data_8723a *pHalData = GET_HAL_DATA(Adapter);
+ struct bb_reg_define *pPhyReg = &pHalData->PHYRegDef[eRFPath];
+ u32 NewOffset;
+ u32 tmplong, tmplong2;
+ u8 RfPiEnable = 0;
+ /* */
+ /* Make sure RF register offset is correct */
+ /* */
+ Offset &= 0x3f;
+
+ /* */
+ /* Switch page for 8256 RF IC */
+ /* */
+ NewOffset = Offset;
+
+ /* 2009/06/17 MH We can not execute IO for power save or
+ other accident mode. */
+ /* if (RT_CANNOT_IO(Adapter)) */
+ /* */
+ /* RTPRINT(FPHY, PHY_RFR, ("phy_RFSerialRead return all one\n")); */
+ /* return 0xFFFFFFFF; */
+ /* */
+
+ /* For 92S LSSI Read RFLSSIRead */
+ /* For RF A/B write 0x824/82c(does not work in the future) */
+ /* We must use 0x824 for RF A and B to execute read trigger */
+ tmplong = rtl8723au_read32(Adapter, rFPGA0_XA_HSSIParameter2);
+ if (eRFPath == RF_PATH_A)
+ tmplong2 = tmplong;
+ else
+ tmplong2 = rtl8723au_read32(Adapter, pPhyReg->rfHSSIPara2);
+
+ tmplong2 = (tmplong2 & ~bLSSIReadAddress) |
+ (NewOffset << 23) | bLSSIReadEdge; /* T65 RF */
+
+ rtl8723au_write32(Adapter, rFPGA0_XA_HSSIParameter2,
+ tmplong & (~bLSSIReadEdge));
+ udelay(10);/* PlatformStallExecution(10); */
+
+ rtl8723au_write32(Adapter, pPhyReg->rfHSSIPara2, tmplong2);
+ udelay(100);/* PlatformStallExecution(100); */
+
+ rtl8723au_write32(Adapter, rFPGA0_XA_HSSIParameter2,
+ tmplong | bLSSIReadEdge);
+ udelay(10);/* PlatformStallExecution(10); */
+
+ if (eRFPath == RF_PATH_A)
+ RfPiEnable = (u8)PHY_QueryBBReg(Adapter,
+ rFPGA0_XA_HSSIParameter1,
+ BIT(8));
+ else if (eRFPath == RF_PATH_B)
+ RfPiEnable = (u8)PHY_QueryBBReg(Adapter,
+ rFPGA0_XB_HSSIParameter1,
+ BIT(8));
+
+ if (RfPiEnable) {
+ /* Read from BBreg8b8, 12 bits for 8190, 20bits for T65 RF */
+ retValue = PHY_QueryBBReg(Adapter, pPhyReg->rfLSSIReadBackPi,
+ bLSSIReadBackData);
+ /* DBG_8723A("Readback from RF-PI : 0x%x\n", retValue); */
+ } else {
+ /* Read from BBreg8a0, 12 bits for 8190, 20 bits for T65 RF */
+ retValue = PHY_QueryBBReg(Adapter, pPhyReg->rfLSSIReadBack,
+ bLSSIReadBackData);
+ /* DBG_8723A("Readback from RF-SI : 0x%x\n", retValue); */
+ }
+ /* DBG_8723A("RFR-%d Addr[0x%x]= 0x%x\n", eRFPath, pPhyReg->rfLSSIReadBack, retValue); */
+
+ return retValue;
+}
+
+/**
+* Function: phy_RFSerialWrite
+*
+* OverView: Write data to RF register (page 8~)
+*
+* Input:
+* struct rtw_adapter * Adapter,
+* enum RF_RADIO_PATH eRFPath, Radio path of A/B/C/D
+* u32 Offset, The target address to be read
+* u32 Data The new register Data in the target
+* bit position of the target to be read
+*
+* Output:
+* None
+* Return:
+* None
+* Note:
+* Threre are three types of serial operations:
+* 1. Software serial write
+* 2. Hardware LSSI-Low Speed Serial Interface
+* 3. Hardware HSSI-High speed
+* serial write. Driver need to implement (1) and (2).
+* This function is equal to the combination of RF_ReadReg() and
+* RFLSSIRead()
+*
+* Note: For RF8256 only
+* The total count of RTL8256(Zebra4) register is around 36 bit it only employs
+* 4-bit RF address. RTL8256 uses "register mode control bit"
+* (Reg00[12], Reg00[10]) to access register address bigger than 0xf.
+* See "Appendix-4 in PHY Configuration programming guide" for more details.
+* Thus, we define a sub-finction for RTL8526 register address conversion
+* ===========================================================
+* Register Mode: RegCTL[1] RegCTL[0] Note
+* (Reg00[12]) (Reg00[10])
+* ===========================================================
+* Reg_Mode0 0 x Reg 0 ~15(0x0 ~ 0xf)
+* ------------------------------------------------------------------
+* Reg_Mode1 1 0 Reg 16 ~30(0x1 ~ 0xf)
+* ------------------------------------------------------------------
+* Reg_Mode2 1 1 Reg 31 ~ 45(0x1 ~ 0xf)
+* ------------------------------------------------------------------
+*
+* 2008/09/02 MH Add 92S RF definition
+*/
+static void
+phy_RFSerialWrite(struct rtw_adapter *Adapter, enum RF_RADIO_PATH eRFPath,
+ u32 Offset, u32 Data)
+{
+ u32 DataAndAddr = 0;
+ struct hal_data_8723a *pHalData = GET_HAL_DATA(Adapter);
+ struct bb_reg_define *pPhyReg = &pHalData->PHYRegDef[eRFPath];
+ u32 NewOffset;
+
+ /* 2009/06/17 MH We can not execute IO for power save or
+ other accident mode. */
+ /* if (RT_CANNOT_IO(Adapter)) */
+ /* */
+ /* RTPRINT(FPHY, PHY_RFW, ("phy_RFSerialWrite stop\n")); */
+ /* return; */
+ /* */
+
+ Offset &= 0x3f;
+
+ /* */
+ /* Shadow Update */
+ /* */
+ /* PHY_RFShadowWrite(Adapter, eRFPath, Offset, Data); */
+
+ /* */
+ /* Switch page for 8256 RF IC */
+ /* */
+ NewOffset = Offset;
+
+ /* */
+ /* Put write addr in [5:0] and write data in [31:16] */
+ /* */
+ /* DataAndAddr = (Data<<16) | (NewOffset&0x3f); */
+ /* T65 RF */
+ DataAndAddr = ((NewOffset<<20) | (Data&0x000fffff)) & 0x0fffffff;
+
+ /* */
+ /* Write Operation */
+ /* */
+ rtl8723au_write32(Adapter, pPhyReg->rf3wireOffset, DataAndAddr);
+}
+
+/**
+* Function: PHY_QueryRFReg
+*
+* OverView: Query "Specific bits" to RF register (page 8~)
+*
+* Input:
+* struct rtw_adapter * Adapter,
+* enum RF_RADIO_PATH eRFPath, Radio path of A/B/C/D
+* u32 RegAddr, The target address to be read
+* u32BitMask The target bit position in the target
+* address to be read
+*
+* Output:
+* None
+* Return:
+* u32 Readback value
+* Note:
+* This function is equal to "GetRFRegSetting" in PHY programming guide
+*/
+u32
+PHY_QueryRFReg(struct rtw_adapter *Adapter, enum RF_RADIO_PATH eRFPath,
+ u32 RegAddr, u32 BitMask)
+{
+ u32 Original_Value, Readback_Value, BitShift;
+ /* struct hal_data_8723a *pHalData = GET_HAL_DATA(Adapter); */
+ /* u8 RFWaitCounter = 0; */
+ /* _irqL irqL; */
+
+ Original_Value = phy_RFSerialRead(Adapter, eRFPath, RegAddr);
+
+ BitShift = phy_CalculateBitShift(BitMask);
+ Readback_Value = (Original_Value & BitMask) >> BitShift;
+
+ return Readback_Value;
+}
+
+/**
+* Function: PHY_SetRFReg
+*
+* OverView: Write "Specific bits" to RF register (page 8~)
+*
+* Input:
+* struct rtw_adapter * Adapter,
+* enum RF_RADIO_PATH eRFPath, Radio path of A/B/C/D
+* u32 RegAddr, The target address to be modified
+* u32 BitMask The target bit position in the target
+* address to be modified
+* u32 Data The new register Data in the target
+* bit position of the target address
+*
+* Output:
+* None
+* Return:
+* None
+* Note: This function is equal to "PutRFRegSetting" in PHY programming guide
+*/
+void
+PHY_SetRFReg(struct rtw_adapter *Adapter, enum RF_RADIO_PATH eRFPath,
+ u32 RegAddr, u32 BitMask, u32 Data)
+{
+ /* struct hal_data_8723a *pHalData = GET_HAL_DATA(Adapter); */
+ /* u8 RFWaitCounter = 0; */
+ u32 Original_Value, BitShift;
+
+ /* RF data is 12 bits only */
+ if (BitMask != bRFRegOffsetMask) {
+ Original_Value = phy_RFSerialRead(Adapter, eRFPath, RegAddr);
+ BitShift = phy_CalculateBitShift(BitMask);
+ Data = (Original_Value & (~BitMask)) | (Data << BitShift);
+ }
+
+ phy_RFSerialWrite(Adapter, eRFPath, RegAddr, Data);
+}
+
+/* 3. Initial MAC/BB/RF config by reading MAC/BB/RF txt. */
+
+/*-----------------------------------------------------------------------------
+ * Function: PHY_MACConfig8723A
+ *
+ * Overview: Condig MAC by header file or parameter file.
+ *
+ * Input: NONE
+ *
+ * Output: NONE
+ *
+ * Return: NONE
+ *
+ * Revised History:
+ * When Who Remark
+ * 08/12/2008 MHC Create Version 0.
+ *
+ *---------------------------------------------------------------------------*/
+int PHY_MACConfig8723A(struct rtw_adapter *Adapter)
+{
+ struct hal_data_8723a *pHalData = GET_HAL_DATA(Adapter);
+
+ /* */
+ /* Config MAC */
+ /* */
+ ODM_ReadAndConfig_MAC_REG_8723A(&pHalData->odmpriv);
+
+ /* 2010.07.13 AMPDU aggregation number 9 */
+ rtl8723au_write8(Adapter, REG_MAX_AGGR_NUM, 0x0A);
+ if (pHalData->rf_type == RF_2T2R &&
+ BOARD_USB_DONGLE == pHalData->BoardType)
+ rtl8723au_write8(Adapter, 0x40, 0x04);
+
+ return _SUCCESS;
+}
+
+/**
+* Function: phy_InitBBRFRegisterDefinition
+*
+* OverView: Initialize Register definition offset for Radio Path A/B/C/D
+*
+* Input:
+* struct rtw_adapter * Adapter,
+*
+* Output: None
+* Return: None
+* Note:
+* The initialization value is constant and it should never be changes
+*/
+static void
+phy_InitBBRFRegisterDefinition(struct rtw_adapter *Adapter)
+{
+ struct hal_data_8723a *pHalData = GET_HAL_DATA(Adapter);
+
+ /* RF Interface Sowrtware Control */
+ /* 16 LSBs if read 32-bit from 0x870 */
+ pHalData->PHYRegDef[RF_PATH_A].rfintfs = rFPGA0_XAB_RFInterfaceSW;
+ /* 16 MSBs if read 32-bit from 0x870 (16-bit for 0x872) */
+ pHalData->PHYRegDef[RF_PATH_B].rfintfs = rFPGA0_XAB_RFInterfaceSW;
+
+ /* RF Interface Readback Value */
+ /* 16 LSBs if read 32-bit from 0x8E0 */
+ pHalData->PHYRegDef[RF_PATH_A].rfintfi = rFPGA0_XAB_RFInterfaceRB;
+ /* 16 MSBs if read 32-bit from 0x8E0 (16-bit for 0x8E2) */
+ pHalData->PHYRegDef[RF_PATH_B].rfintfi = rFPGA0_XAB_RFInterfaceRB;
+
+ /* RF Interface Output (and Enable) */
+ /* 16 LSBs if read 32-bit from 0x860 */
+ pHalData->PHYRegDef[RF_PATH_A].rfintfo = rFPGA0_XA_RFInterfaceOE;
+ /* 16 LSBs if read 32-bit from 0x864 */
+ pHalData->PHYRegDef[RF_PATH_B].rfintfo = rFPGA0_XB_RFInterfaceOE;
+
+ /* RF Interface (Output and) Enable */
+ /* 16 MSBs if read 32-bit from 0x860 (16-bit for 0x862) */
+ pHalData->PHYRegDef[RF_PATH_A].rfintfe = rFPGA0_XA_RFInterfaceOE;
+ /* 16 MSBs if read 32-bit from 0x864 (16-bit for 0x866) */
+ pHalData->PHYRegDef[RF_PATH_B].rfintfe = rFPGA0_XB_RFInterfaceOE;
+
+ /* Addr of LSSI. Wirte RF register by driver */
+ pHalData->PHYRegDef[RF_PATH_A].rf3wireOffset = rFPGA0_XA_LSSIParameter;
+ pHalData->PHYRegDef[RF_PATH_B].rf3wireOffset = rFPGA0_XB_LSSIParameter;
+
+ /* RF parameter */
+ /* BB Band Select */
+ pHalData->PHYRegDef[RF_PATH_A].rfLSSI_Select = rFPGA0_XAB_RFParameter;
+ pHalData->PHYRegDef[RF_PATH_B].rfLSSI_Select = rFPGA0_XAB_RFParameter;
+
+ /* Tx AGC Gain Stage (same for all path. Should we remove this?) */
+ pHalData->PHYRegDef[RF_PATH_A].rfTxGainStage = rFPGA0_TxGainStage;
+ pHalData->PHYRegDef[RF_PATH_B].rfTxGainStage = rFPGA0_TxGainStage;
+
+ /* Tranceiver A~D HSSI Parameter-1 */
+ /* wire control parameter1 */
+ pHalData->PHYRegDef[RF_PATH_A].rfHSSIPara1 = rFPGA0_XA_HSSIParameter1;
+ /* wire control parameter1 */
+ pHalData->PHYRegDef[RF_PATH_B].rfHSSIPara1 = rFPGA0_XB_HSSIParameter1;
+
+ /* Tranceiver A~D HSSI Parameter-2 */
+ /* wire control parameter2 */
+ pHalData->PHYRegDef[RF_PATH_A].rfHSSIPara2 = rFPGA0_XA_HSSIParameter2;
+ /* wire control parameter2 */
+ pHalData->PHYRegDef[RF_PATH_B].rfHSSIPara2 = rFPGA0_XB_HSSIParameter2;
+
+ /* RF switch Control */
+ pHalData->PHYRegDef[RF_PATH_A].rfSwitchControl =
+ rFPGA0_XAB_SwitchControl; /* TR/Ant switch control */
+ pHalData->PHYRegDef[RF_PATH_B].rfSwitchControl =
+ rFPGA0_XAB_SwitchControl;
+
+ /* AGC control 1 */
+ pHalData->PHYRegDef[RF_PATH_A].rfAGCControl1 = rOFDM0_XAAGCCore1;
+ pHalData->PHYRegDef[RF_PATH_B].rfAGCControl1 = rOFDM0_XBAGCCore1;
+
+ /* AGC control 2 */
+ pHalData->PHYRegDef[RF_PATH_A].rfAGCControl2 = rOFDM0_XAAGCCore2;
+ pHalData->PHYRegDef[RF_PATH_B].rfAGCControl2 = rOFDM0_XBAGCCore2;
+
+ /* RX AFE control 1 */
+ pHalData->PHYRegDef[RF_PATH_A].rfRxIQImbalance = rOFDM0_XARxIQImbalance;
+ pHalData->PHYRegDef[RF_PATH_B].rfRxIQImbalance = rOFDM0_XBRxIQImbalance;
+
+ /* RX AFE control 1 */
+ pHalData->PHYRegDef[RF_PATH_A].rfRxAFE = rOFDM0_XARxAFE;
+ pHalData->PHYRegDef[RF_PATH_B].rfRxAFE = rOFDM0_XBRxAFE;
+
+ /* Tx AFE control 1 */
+ pHalData->PHYRegDef[RF_PATH_A].rfTxIQImbalance = rOFDM0_XATxIQImbalance;
+ pHalData->PHYRegDef[RF_PATH_B].rfTxIQImbalance = rOFDM0_XBTxIQImbalance;
+
+ /* Tx AFE control 2 */
+ pHalData->PHYRegDef[RF_PATH_A].rfTxAFE = rOFDM0_XATxAFE;
+ pHalData->PHYRegDef[RF_PATH_B].rfTxAFE = rOFDM0_XBTxAFE;
+
+ /* Tranceiver LSSI Readback SI mode */
+ pHalData->PHYRegDef[RF_PATH_A].rfLSSIReadBack = rFPGA0_XA_LSSIReadBack;
+ pHalData->PHYRegDef[RF_PATH_B].rfLSSIReadBack = rFPGA0_XB_LSSIReadBack;
+
+ /* Tranceiver LSSI Readback PI mode */
+ pHalData->PHYRegDef[RF_PATH_A].rfLSSIReadBackPi =
+ TransceiverA_HSPI_Readback;
+ pHalData->PHYRegDef[RF_PATH_B].rfLSSIReadBackPi =
+ TransceiverB_HSPI_Readback;
+}
+
+/* The following is for High Power PA */
+static void
+storePwrIndexDiffRateOffset(struct rtw_adapter *Adapter, u32 RegAddr,
+ u32 BitMask, u32 Data)
+{
+ struct hal_data_8723a *pHalData = GET_HAL_DATA(Adapter);
+
+ if (RegAddr == rTxAGC_A_Rate18_06) {
+ pHalData->MCSTxPowerLevelOriginalOffset[pHalData->pwrGroupCnt][0] = Data;
+ }
+ if (RegAddr == rTxAGC_A_Rate54_24) {
+ pHalData->MCSTxPowerLevelOriginalOffset[pHalData->pwrGroupCnt][1] = Data;
+ }
+ if (RegAddr == rTxAGC_A_CCK1_Mcs32) {
+ pHalData->MCSTxPowerLevelOriginalOffset[pHalData->pwrGroupCnt][6] = Data;
+ }
+ if (RegAddr == rTxAGC_B_CCK11_A_CCK2_11 && BitMask == 0xffffff00) {
+ pHalData->MCSTxPowerLevelOriginalOffset[pHalData->pwrGroupCnt][7] = Data;
+ }
+ if (RegAddr == rTxAGC_A_Mcs03_Mcs00) {
+ pHalData->MCSTxPowerLevelOriginalOffset[pHalData->pwrGroupCnt][2] = Data;
+ }
+ if (RegAddr == rTxAGC_A_Mcs07_Mcs04) {
+ pHalData->MCSTxPowerLevelOriginalOffset[pHalData->pwrGroupCnt][3] = Data;
+ }
+ if (RegAddr == rTxAGC_A_Mcs11_Mcs08) {
+ pHalData->MCSTxPowerLevelOriginalOffset[pHalData->pwrGroupCnt][4] = Data;
+ }
+ if (RegAddr == rTxAGC_A_Mcs15_Mcs12) {
+ pHalData->MCSTxPowerLevelOriginalOffset[pHalData->pwrGroupCnt][5] = Data;
+ }
+ if (RegAddr == rTxAGC_B_Rate18_06) {
+ pHalData->MCSTxPowerLevelOriginalOffset[pHalData->pwrGroupCnt][8] = Data;
+ }
+ if (RegAddr == rTxAGC_B_Rate54_24) {
+ pHalData->MCSTxPowerLevelOriginalOffset[pHalData->pwrGroupCnt][9] = Data;
+ }
+ if (RegAddr == rTxAGC_B_CCK1_55_Mcs32) {
+ pHalData->MCSTxPowerLevelOriginalOffset[pHalData->pwrGroupCnt][14] = Data;
+ }
+ if (RegAddr == rTxAGC_B_CCK11_A_CCK2_11 && BitMask == 0x000000ff) {
+ pHalData->MCSTxPowerLevelOriginalOffset[pHalData->pwrGroupCnt][15] = Data;
+ }
+ if (RegAddr == rTxAGC_B_Mcs03_Mcs00) {
+ pHalData->MCSTxPowerLevelOriginalOffset[pHalData->pwrGroupCnt][10] = Data;
+ }
+ if (RegAddr == rTxAGC_B_Mcs07_Mcs04) {
+ pHalData->MCSTxPowerLevelOriginalOffset[pHalData->pwrGroupCnt][11] = Data;
+ }
+ if (RegAddr == rTxAGC_B_Mcs11_Mcs08) {
+ pHalData->MCSTxPowerLevelOriginalOffset[pHalData->pwrGroupCnt][12] = Data;
+ }
+ if (RegAddr == rTxAGC_B_Mcs15_Mcs12) {
+ pHalData->MCSTxPowerLevelOriginalOffset[pHalData->pwrGroupCnt][13] = Data;
+ pHalData->pwrGroupCnt++;
+ }
+}
+
+/*-----------------------------------------------------------------------------
+ * Function: phy_ConfigBBWithPgHeaderFile
+ *
+ * Overview: Config PHY_REG_PG array
+ *
+ * Input: NONE
+ *
+ * Output: NONE
+ *
+ * Return: NONE
+ *
+ * Revised History:
+ * When Who Remark
+ * 11/06/2008 MHC Add later!!!!!!.. Please modify for new files!!!!
+ * 11/10/2008 tynli Modify to mew files.
+ *---------------------------------------------------------------------------*/
+static int
+phy_ConfigBBWithPgHeaderFile(struct rtw_adapter *Adapter)
+{
+ int i;
+ u32 *Rtl819XPHY_REGArray_Table_PG;
+ u16 PHY_REGArrayPGLen;
+
+ PHY_REGArrayPGLen = Rtl8723_PHY_REG_Array_PGLength;
+ Rtl819XPHY_REGArray_Table_PG = (u32 *)Rtl8723_PHY_REG_Array_PG;
+
+ for (i = 0; i < PHY_REGArrayPGLen; i = i + 3) {
+ storePwrIndexDiffRateOffset(Adapter,
+ Rtl819XPHY_REGArray_Table_PG[i],
+ Rtl819XPHY_REGArray_Table_PG[i+1],
+ Rtl819XPHY_REGArray_Table_PG[i+2]);
+ }
+
+ return _SUCCESS;
+}
+
+static void
+phy_BB8192C_Config_1T(struct rtw_adapter *Adapter)
+{
+ /* for path - B */
+ PHY_SetBBReg(Adapter, rFPGA0_TxInfo, 0x3, 0x2);
+ PHY_SetBBReg(Adapter, rFPGA1_TxInfo, 0x300033, 0x200022);
+
+ /* 20100519 Joseph: Add for 1T2R config. Suggested by Kevin,
+ Jenyu and Yunan. */
+ PHY_SetBBReg(Adapter, rCCK0_AFESetting, bMaskByte3, 0x45);
+ PHY_SetBBReg(Adapter, rOFDM0_TRxPathEnable, bMaskByte0, 0x23);
+ /* B path first AGC */
+ PHY_SetBBReg(Adapter, rOFDM0_AGCParameter1, 0x30, 0x1);
+
+ PHY_SetBBReg(Adapter, 0xe74, 0x0c000000, 0x2);
+ PHY_SetBBReg(Adapter, 0xe78, 0x0c000000, 0x2);
+ PHY_SetBBReg(Adapter, 0xe7c, 0x0c000000, 0x2);
+ PHY_SetBBReg(Adapter, 0xe80, 0x0c000000, 0x2);
+ PHY_SetBBReg(Adapter, 0xe88, 0x0c000000, 0x2);
+}
+
+static int
+phy_BB8723a_Config_ParaFile(struct rtw_adapter *Adapter)
+{
+ struct eeprom_priv *pEEPROM = GET_EEPROM_EFUSE_PRIV(Adapter);
+ struct hal_data_8723a *pHalData = GET_HAL_DATA(Adapter);
+ int rtStatus = _SUCCESS;
+
+ /* */
+ /* 1. Read PHY_REG.TXT BB INIT!! */
+ /* We will seperate as 88C / 92C according to chip version */
+ /* */
+ ODM_ReadAndConfig_PHY_REG_1T_8723A(&pHalData->odmpriv);
+
+ /* */
+ /* 20100318 Joseph: Config 2T2R to 1T2R if necessary. */
+ /* */
+ if (pHalData->rf_type == RF_1T2R) {
+ phy_BB8192C_Config_1T(Adapter);
+ DBG_8723A("phy_BB8723a_Config_ParaFile():Config to 1T!!\n");
+ }
+
+ /* */
+ /* 2. If EEPROM or EFUSE autoload OK, We must config by
+ PHY_REG_PG.txt */
+ /* */
+ if (pEEPROM->bautoload_fail_flag == false) {
+ pHalData->pwrGroupCnt = 0;
+
+ rtStatus = phy_ConfigBBWithPgHeaderFile(Adapter);
+ }
+
+ if (rtStatus != _SUCCESS)
+ goto phy_BB8190_Config_ParaFile_Fail;
+
+ /* */
+ /* 3. BB AGC table Initialization */
+ /* */
+ ODM_ReadAndConfig_AGC_TAB_1T_8723A(&pHalData->odmpriv);
+
+phy_BB8190_Config_ParaFile_Fail:
+
+ return rtStatus;
+}
+
+int
+PHY_BBConfig8723A(struct rtw_adapter *Adapter)
+{
+ int rtStatus = _SUCCESS;
+ struct hal_data_8723a *pHalData = GET_HAL_DATA(Adapter);
+ u8 TmpU1B = 0;
+ u8 CrystalCap;
+
+ phy_InitBBRFRegisterDefinition(Adapter);
+
+ /* Suggested by Scott. tynli_test. 2010.12.30. */
+ /* 1. 0x28[1] = 1 */
+ TmpU1B = rtl8723au_read8(Adapter, REG_AFE_PLL_CTRL);
+ udelay(2);
+ rtl8723au_write8(Adapter, REG_AFE_PLL_CTRL, TmpU1B | BIT(1));
+ udelay(2);
+
+ /* 2. 0x29[7:0] = 0xFF */
+ rtl8723au_write8(Adapter, REG_AFE_PLL_CTRL+1, 0xff);
+ udelay(2);
+
+ /* 3. 0x02[1:0] = 2b'11 */
+ TmpU1B = rtl8723au_read8(Adapter, REG_SYS_FUNC_EN);
+ rtl8723au_write8(Adapter, REG_SYS_FUNC_EN,
+ (TmpU1B | FEN_BB_GLB_RSTn | FEN_BBRSTB));
+
+ /* 4. 0x25[6] = 0 */
+ TmpU1B = rtl8723au_read8(Adapter, REG_AFE_XTAL_CTRL + 1);
+ rtl8723au_write8(Adapter, REG_AFE_XTAL_CTRL+1, TmpU1B & ~BIT(6));
+
+ /* 5. 0x24[20] = 0 Advised by SD3 Alex Wang. 2011.02.09. */
+ TmpU1B = rtl8723au_read8(Adapter, REG_AFE_XTAL_CTRL+2);
+ rtl8723au_write8(Adapter, REG_AFE_XTAL_CTRL+2, TmpU1B & ~BIT(4));
+
+ /* 6. 0x1f[7:0] = 0x07 */
+ rtl8723au_write8(Adapter, REG_RF_CTRL, 0x07);
+
+ /* */
+ /* Config BB and AGC */
+ /* */
+ rtStatus = phy_BB8723a_Config_ParaFile(Adapter);
+
+/* only for B-cut */
+ if (pHalData->EEPROMVersion >= 0x01) {
+ CrystalCap = pHalData->CrystalCap & 0x3F;
+ PHY_SetBBReg(Adapter, REG_MAC_PHY_CTRL, 0xFFF000,
+ (CrystalCap | (CrystalCap << 6)));
+ }
+
+ rtl8723au_write32(Adapter, REG_LDOA15_CTRL, 0x01572505);
+ return rtStatus;
+}
+
+static void getTxPowerIndex(struct rtw_adapter *Adapter,
+ u8 channel, u8 *cckPowerLevel, u8 *ofdmPowerLevel)
+{
+ struct hal_data_8723a *pHalData = GET_HAL_DATA(Adapter);
+ u8 index = (channel - 1);
+ /* 1. CCK */
+ cckPowerLevel[RF_PATH_A] = pHalData->TxPwrLevelCck[RF_PATH_A][index];
+ cckPowerLevel[RF_PATH_B] = pHalData->TxPwrLevelCck[RF_PATH_B][index];
+
+ /* 2. OFDM for 1S or 2S */
+ if (GET_RF_TYPE(Adapter) == RF_1T2R || GET_RF_TYPE(Adapter) == RF_1T1R) {
+ /* Read HT 40 OFDM TX power */
+ ofdmPowerLevel[RF_PATH_A] =
+ pHalData->TxPwrLevelHT40_1S[RF_PATH_A][index];
+ ofdmPowerLevel[RF_PATH_B] =
+ pHalData->TxPwrLevelHT40_1S[RF_PATH_B][index];
+ } else if (GET_RF_TYPE(Adapter) == RF_2T2R) {
+ /* Read HT 40 OFDM TX power */
+ ofdmPowerLevel[RF_PATH_A] =
+ pHalData->TxPwrLevelHT40_2S[RF_PATH_A][index];
+ ofdmPowerLevel[RF_PATH_B] =
+ pHalData->TxPwrLevelHT40_2S[RF_PATH_B][index];
+ }
+}
+
+static void ccxPowerIndexCheck(struct rtw_adapter *Adapter, u8 channel,
+ u8 *cckPowerLevel, u8 *ofdmPowerLevel)
+{
+}
+
+/*-----------------------------------------------------------------------------
+ * Function: SetTxPowerLevel8723A()
+ *
+ * Overview: This function is export to "HalCommon" moudule
+ * We must consider RF path later!!!!!!!
+ *
+ * Input: struct rtw_adapter * Adapter
+ * u8 channel
+ *
+ * Output: NONE
+ *
+ * Return: NONE
+ *
+ *---------------------------------------------------------------------------*/
+void PHY_SetTxPowerLevel8723A(struct rtw_adapter *Adapter, u8 channel)
+{
+ struct hal_data_8723a *pHalData = GET_HAL_DATA(Adapter);
+ u8 cckPowerLevel[2], ofdmPowerLevel[2]; /* [0]:RF-A, [1]:RF-B */
+
+ if (pHalData->bTXPowerDataReadFromEEPORM == false)
+ return;
+
+ getTxPowerIndex(Adapter, channel, &cckPowerLevel[0],
+ &ofdmPowerLevel[0]);
+
+ ccxPowerIndexCheck(Adapter, channel, &cckPowerLevel[0],
+ &ofdmPowerLevel[0]);
+
+ rtl823a_phy_rf6052setccktxpower(Adapter, &cckPowerLevel[0]);
+ rtl8723a_PHY_RF6052SetOFDMTxPower(Adapter, &ofdmPowerLevel[0], channel);
+}
+
+/*-----------------------------------------------------------------------------
+ * Function: PHY_SetBWMode23aCallback8192C()
+ *
+ * Overview: Timer callback function for SetSetBWMode23a
+ *
+ * Input: PRT_TIMER pTimer
+ *
+ * Output: NONE
+ *
+ * Return: NONE
+ *
+ * Note:
+ * (1) We do not take j mode into consideration now
+ * (2) Will two workitem of "switch channel" and
+ * "switch channel bandwidth" run concurrently?
+ *---------------------------------------------------------------------------*/
+static void
+_PHY_SetBWMode23a92C(struct rtw_adapter *Adapter)
+{
+ struct hal_data_8723a *pHalData = GET_HAL_DATA(Adapter);
+ u8 regBwOpMode;
+ u8 regRRSR_RSC;
+
+ if (Adapter->bDriverStopped)
+ return;
+
+ /* 3 */
+ /* 3<1>Set MAC register */
+ /* 3 */
+
+ regBwOpMode = rtl8723au_read8(Adapter, REG_BWOPMODE);
+ regRRSR_RSC = rtl8723au_read8(Adapter, REG_RRSR+2);
+
+ switch (pHalData->CurrentChannelBW) {
+ case HT_CHANNEL_WIDTH_20:
+ regBwOpMode |= BW_OPMODE_20MHZ;
+ rtl8723au_write8(Adapter, REG_BWOPMODE, regBwOpMode);
+ break;
+ case HT_CHANNEL_WIDTH_40:
+ regBwOpMode &= ~BW_OPMODE_20MHZ;
+ rtl8723au_write8(Adapter, REG_BWOPMODE, regBwOpMode);
+ regRRSR_RSC = (regRRSR_RSC & 0x90) |
+ (pHalData->nCur40MhzPrimeSC << 5);
+ rtl8723au_write8(Adapter, REG_RRSR+2, regRRSR_RSC);
+ break;
+
+ default:
+ break;
+ }
+
+ /* 3 */
+ /* 3<2>Set PHY related register */
+ /* 3 */
+ switch (pHalData->CurrentChannelBW) {
+ /* 20 MHz channel*/
+ case HT_CHANNEL_WIDTH_20:
+ PHY_SetBBReg(Adapter, rFPGA0_RFMOD, bRFMOD, 0x0);
+ PHY_SetBBReg(Adapter, rFPGA1_RFMOD, bRFMOD, 0x0);
+ PHY_SetBBReg(Adapter, rFPGA0_AnalogParameter2, BIT(10), 1);
+
+ break;
+
+ /* 40 MHz channel*/
+ case HT_CHANNEL_WIDTH_40:
+ PHY_SetBBReg(Adapter, rFPGA0_RFMOD, bRFMOD, 0x1);
+ PHY_SetBBReg(Adapter, rFPGA1_RFMOD, bRFMOD, 0x1);
+
+ /* Set Control channel to upper or lower. These settings
+ are required only for 40MHz */
+ PHY_SetBBReg(Adapter, rCCK0_System, bCCKSideBand,
+ (pHalData->nCur40MhzPrimeSC >> 1));
+ PHY_SetBBReg(Adapter, rOFDM1_LSTF, 0xC00,
+ pHalData->nCur40MhzPrimeSC);
+ PHY_SetBBReg(Adapter, rFPGA0_AnalogParameter2, BIT(10), 0);
+
+ PHY_SetBBReg(Adapter, 0x818, BIT(26) | BIT(27),
+ (pHalData->nCur40MhzPrimeSC ==
+ HAL_PRIME_CHNL_OFFSET_LOWER) ? 2:1);
+ break;
+
+ default:
+ break;
+ }
+ /* Skip over setting of J-mode in BB register here. Default value
+ is "None J mode". Emily 20070315 */
+
+ /* Added it for 20/40 mhz switch time evaluation by guangan 070531 */
+ /* NowL = PlatformEFIORead4Byte(Adapter, TSFR); */
+ /* NowH = PlatformEFIORead4Byte(Adapter, TSFR+4); */
+ /* EndTime = ((u64)NowH << 32) + NowL; */
+
+ rtl8723a_phy_rf6052set_bw(Adapter, pHalData->CurrentChannelBW);
+}
+
+ /*-----------------------------------------------------------------------------
+ * Function: SetBWMode23a8190Pci()
+ *
+ * Overview: This function is export to "HalCommon" moudule
+ *
+ * Input: struct rtw_adapter * Adapter
+ * enum ht_channel_width Bandwidth 20M or 40M
+ *
+ * Output: NONE
+ *
+ * Return: NONE
+ *
+ * Note: We do not take j mode into consideration now
+ *---------------------------------------------------------------------------*/
+void
+PHY_SetBWMode23a8723A(struct rtw_adapter *Adapter,
+ enum ht_channel_width Bandwidth, unsigned char Offset)
+{
+ struct hal_data_8723a *pHalData = GET_HAL_DATA(Adapter);
+ enum ht_channel_width tmpBW = pHalData->CurrentChannelBW;
+
+ pHalData->CurrentChannelBW = Bandwidth;
+
+ pHalData->nCur40MhzPrimeSC = Offset;
+
+ if ((!Adapter->bDriverStopped) && (!Adapter->bSurpriseRemoved))
+ _PHY_SetBWMode23a92C(Adapter);
+ else
+ pHalData->CurrentChannelBW = tmpBW;
+}
+
+static void _PHY_SwChnl8723A(struct rtw_adapter *Adapter, u8 channel)
+{
+ enum RF_RADIO_PATH eRFPath;
+ u32 param1, param2;
+ struct hal_data_8723a *pHalData = GET_HAL_DATA(Adapter);
+
+ /* s1. pre common command - CmdID_SetTxPowerLevel */
+ PHY_SetTxPowerLevel8723A(Adapter, channel);
+
+ /* s2. RF dependent command - CmdID_RF_WriteReg,
+ param1 = RF_CHNLBW, param2 = channel */
+ param1 = RF_CHNLBW;
+ param2 = channel;
+ for (eRFPath = 0; eRFPath < pHalData->NumTotalRFPath; eRFPath++) {
+ pHalData->RfRegChnlVal[eRFPath] =
+ (pHalData->RfRegChnlVal[eRFPath] & 0xfffffc00) | param2;
+ PHY_SetRFReg(Adapter, eRFPath, param1,
+ bRFRegOffsetMask, pHalData->RfRegChnlVal[eRFPath]);
+ }
+
+ /* s3. post common command - CmdID_End, None */
+}
+
+void PHY_SwChnl8723A(struct rtw_adapter *Adapter, u8 channel)
+{
+ struct hal_data_8723a *pHalData = GET_HAL_DATA(Adapter);
+ u8 tmpchannel = pHalData->CurrentChannel;
+ bool result = true;
+
+ if (channel == 0)
+ channel = 1;
+
+ pHalData->CurrentChannel = channel;
+
+ if ((!Adapter->bDriverStopped) && (!Adapter->bSurpriseRemoved)) {
+ _PHY_SwChnl8723A(Adapter, channel);
+
+ if (!result)
+ pHalData->CurrentChannel = tmpchannel;
+ } else {
+ pHalData->CurrentChannel = tmpchannel;
+ }
+}
diff --git a/drivers/staging/rtl8723au/hal/rtl8723a_rf6052.c b/drivers/staging/rtl8723au/hal/rtl8723a_rf6052.c
new file mode 100644
index 000000000..3e3f18634
--- /dev/null
+++ b/drivers/staging/rtl8723au/hal/rtl8723a_rf6052.c
@@ -0,0 +1,517 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2011 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * 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.
+ *
+ ******************************************************************************/
+/******************************************************************************
+ *
+ *
+ * Module: rtl8192c_rf6052.c (Source C File)
+ *
+ * Note: Provide RF 6052 series relative API.
+ *
+ * Function:
+ *
+ * Export:
+ *
+ * Abbrev:
+ *
+ * History:
+ * Data Who Remark
+ *
+ * 09/25/2008 MHC Create initial version.
+ * 11/05/2008 MHC Add API for tw power setting.
+ *
+ *
+******************************************************************************/
+
+#define _RTL8723A_RF6052_C_
+
+#include <osdep_service.h>
+#include <drv_types.h>
+
+#include <rtl8723a_hal.h>
+#include <usb_ops_linux.h>
+
+/*-----------------------------------------------------------------------------
+ * Function: PHY_RF6052SetBandwidth()
+ *
+ * Overview: This function is called by SetBWMode23aCallback8190Pci() only
+ *
+ * Input: struct rtw_adapter * Adapter
+ * WIRELESS_BANDWIDTH_E Bandwidth 20M or 40M
+ *
+ * Output: NONE
+ *
+ * Return: NONE
+ *
+ * Note: For RF type 0222D
+ *---------------------------------------------------------------------------*/
+void rtl8723a_phy_rf6052set_bw(struct rtw_adapter *Adapter,
+ enum ht_channel_width Bandwidth) /* 20M or 40M */
+{
+ struct hal_data_8723a *pHalData = GET_HAL_DATA(Adapter);
+
+ switch (Bandwidth) {
+ case HT_CHANNEL_WIDTH_20:
+ pHalData->RfRegChnlVal[0] =
+ (pHalData->RfRegChnlVal[0] & 0xfffff3ff) | 0x0400;
+ PHY_SetRFReg(Adapter, RF_PATH_A, RF_CHNLBW, bRFRegOffsetMask,
+ pHalData->RfRegChnlVal[0]);
+ break;
+ case HT_CHANNEL_WIDTH_40:
+ pHalData->RfRegChnlVal[0] =
+ (pHalData->RfRegChnlVal[0] & 0xfffff3ff);
+ PHY_SetRFReg(Adapter, RF_PATH_A, RF_CHNLBW, bRFRegOffsetMask,
+ pHalData->RfRegChnlVal[0]);
+ break;
+ default:
+ break;
+ }
+}
+
+/*-----------------------------------------------------------------------------
+ * Function: PHY_RF6052SetCckTxPower
+ *
+ * Overview:
+ *
+ * Input: NONE
+ *
+ * Output: NONE
+ *
+ * Return: NONE
+ *
+ * Revised History:
+ * When Who Remark
+ * 11/05/2008 MHC Simulate 8192series..
+ *
+ *---------------------------------------------------------------------------*/
+
+void rtl823a_phy_rf6052setccktxpower(struct rtw_adapter *Adapter,
+ u8 *pPowerlevel)
+{
+ struct hal_data_8723a *pHalData = GET_HAL_DATA(Adapter);
+ struct dm_priv *pdmpriv = &pHalData->dmpriv;
+ struct mlme_ext_priv *pmlmeext = &Adapter->mlmeextpriv;
+ u32 TxAGC[2] = {0, 0}, tmpval = 0;
+ bool TurboScanOff = false;
+ u8 idx1, idx2;
+ u8 *ptr;
+
+ /* According to SD3 eechou's suggestion, we need to disable
+ turbo scan for RU. */
+ /* Otherwise, external PA will be broken if power index > 0x20. */
+ if (pHalData->EEPROMRegulatory != 0 || pHalData->ExternalPA)
+ TurboScanOff = true;
+
+ if (pmlmeext->sitesurvey_res.state == SCAN_PROCESS) {
+ TxAGC[RF_PATH_A] = 0x3f3f3f3f;
+ TxAGC[RF_PATH_B] = 0x3f3f3f3f;
+
+ TurboScanOff = true;/* disable turbo scan */
+
+ if (TurboScanOff) {
+ for (idx1 = RF_PATH_A; idx1 <= RF_PATH_B; idx1++) {
+ TxAGC[idx1] = pPowerlevel[idx1] |
+ (pPowerlevel[idx1] << 8) |
+ (pPowerlevel[idx1] << 16) |
+ (pPowerlevel[idx1] << 24);
+ /* 2010/10/18 MH For external PA module.
+ We need to limit power index to be less
+ than 0x20. */
+ if (TxAGC[idx1] > 0x20 && pHalData->ExternalPA)
+ TxAGC[idx1] = 0x20;
+ }
+ }
+ } else {
+/* 20100427 Joseph: Driver dynamic Tx power shall not affect Tx
+ * power. It shall be determined by power training mechanism. */
+/* Currently, we cannot fully disable driver dynamic tx power
+ * mechanism because it is referenced by BT coexist mechanism. */
+/* In the future, two mechanism shall be separated from each other
+ * and maintained independantly. Thanks for Lanhsin's reminder. */
+ if (pdmpriv->DynamicTxHighPowerLvl == TxHighPwrLevel_Level1) {
+ TxAGC[RF_PATH_A] = 0x10101010;
+ TxAGC[RF_PATH_B] = 0x10101010;
+ } else if (pdmpriv->DynamicTxHighPowerLvl ==
+ TxHighPwrLevel_Level2) {
+ TxAGC[RF_PATH_A] = 0x00000000;
+ TxAGC[RF_PATH_B] = 0x00000000;
+ } else {
+ for (idx1 = RF_PATH_A; idx1 <= RF_PATH_B; idx1++) {
+ TxAGC[idx1] = pPowerlevel[idx1] |
+ (pPowerlevel[idx1] << 8) |
+ (pPowerlevel[idx1] << 16) |
+ (pPowerlevel[idx1] << 24);
+ }
+
+ if (pHalData->EEPROMRegulatory == 0) {
+ tmpval = (pHalData->MCSTxPowerLevelOriginalOffset[0][6]) +
+ (pHalData->MCSTxPowerLevelOriginalOffset[0][7]<<8);
+ TxAGC[RF_PATH_A] += tmpval;
+
+ tmpval = (pHalData->MCSTxPowerLevelOriginalOffset[0][14]) +
+ (pHalData->MCSTxPowerLevelOriginalOffset[0][15]<<24);
+ TxAGC[RF_PATH_B] += tmpval;
+ }
+ }
+ }
+
+ for (idx1 = RF_PATH_A; idx1 <= RF_PATH_B; idx1++) {
+ ptr = (u8 *)(&TxAGC[idx1]);
+ for (idx2 = 0; idx2 < 4; idx2++) {
+ if (*ptr > RF6052_MAX_TX_PWR)
+ *ptr = RF6052_MAX_TX_PWR;
+ ptr++;
+ }
+ }
+
+ /* rf-A cck tx power */
+ tmpval = TxAGC[RF_PATH_A] & 0xff;
+ PHY_SetBBReg(Adapter, rTxAGC_A_CCK1_Mcs32, bMaskByte1, tmpval);
+ tmpval = TxAGC[RF_PATH_A] >> 8;
+ PHY_SetBBReg(Adapter, rTxAGC_B_CCK11_A_CCK2_11, 0xffffff00, tmpval);
+
+ /* rf-B cck tx power */
+ tmpval = TxAGC[RF_PATH_B] >> 24;
+ PHY_SetBBReg(Adapter, rTxAGC_B_CCK11_A_CCK2_11, bMaskByte0, tmpval);
+ tmpval = TxAGC[RF_PATH_B] & 0x00ffffff;
+ PHY_SetBBReg(Adapter, rTxAGC_B_CCK1_55_Mcs32, 0xffffff00, tmpval);
+} /* PHY_RF6052SetCckTxPower */
+
+/* powerbase0 for OFDM rates */
+/* powerbase1 for HT MCS rates */
+static void getPowerBase(struct rtw_adapter *Adapter, u8 *pPowerLevel,
+ u8 Channel, u32 *OfdmBase, u32 *MCSBase)
+{
+ struct hal_data_8723a *pHalData = GET_HAL_DATA(Adapter);
+ u32 powerBase0, powerBase1;
+ u8 Legacy_pwrdiff = 0;
+ s8 HT20_pwrdiff = 0;
+ u8 i, powerlevel[2];
+
+ for (i = 0; i < 2; i++) {
+ powerlevel[i] = pPowerLevel[i];
+ Legacy_pwrdiff = pHalData->TxPwrLegacyHtDiff[i][Channel-1];
+ powerBase0 = powerlevel[i] + Legacy_pwrdiff;
+
+ powerBase0 = powerBase0 << 24 | powerBase0 << 16 |
+ powerBase0 << 8 | powerBase0;
+ *(OfdmBase + i) = powerBase0;
+ }
+
+ for (i = 0; i < 2; i++) {
+ /* Check HT20 to HT40 diff */
+ if (pHalData->CurrentChannelBW == HT_CHANNEL_WIDTH_20) {
+ HT20_pwrdiff = pHalData->TxPwrHt20Diff[i][Channel-1];
+ powerlevel[i] += HT20_pwrdiff;
+ }
+ powerBase1 = powerlevel[i];
+ powerBase1 = powerBase1 << 24 | powerBase1 << 16 |
+ powerBase1 << 8 | powerBase1;
+ *(MCSBase + i) = powerBase1;
+ }
+}
+
+static void
+getTxPowerWriteValByRegulatory(struct rtw_adapter *Adapter, u8 Channel,
+ u8 index, u32 *powerBase0, u32 *powerBase1,
+ u32 *pOutWriteVal)
+{
+ struct hal_data_8723a *pHalData = GET_HAL_DATA(Adapter);
+ struct dm_priv *pdmpriv = &pHalData->dmpriv;
+ u8 i, chnlGroup = 0, pwr_diff_limit[4];
+ u32 writeVal, customer_limit, rf;
+
+ /* Index 0 & 1 = legacy OFDM, 2-5 = HT_MCS rate */
+ for (rf = 0; rf < 2; rf++) {
+ switch (pHalData->EEPROMRegulatory) {
+ case 0: /* Realtek better performance */
+ /* increase power diff defined by Realtek for
+ * large power */
+ chnlGroup = 0;
+ writeVal = pHalData->MCSTxPowerLevelOriginalOffset[chnlGroup][index+(rf?8:0)] +
+ ((index < 2) ? powerBase0[rf] : powerBase1[rf]);
+ break;
+ case 1: /* Realtek regulatory */
+ /* increase power diff defined by Realtek for
+ * regulatory */
+ if (pHalData->pwrGroupCnt == 1)
+ chnlGroup = 0;
+ if (pHalData->pwrGroupCnt >= 3) {
+ if (Channel <= 3)
+ chnlGroup = 0;
+ else if (Channel >= 4 && Channel <= 9)
+ chnlGroup = 1;
+ else if (Channel > 9)
+ chnlGroup = 2;
+
+ if (pHalData->CurrentChannelBW ==
+ HT_CHANNEL_WIDTH_20)
+ chnlGroup++;
+ else
+ chnlGroup += 4;
+ }
+ writeVal = pHalData->MCSTxPowerLevelOriginalOffset[chnlGroup][index+(rf?8:0)] +
+ ((index < 2) ? powerBase0[rf] :
+ powerBase1[rf]);
+ break;
+ case 2: /* Better regulatory */
+ /* don't increase any power diff */
+ writeVal = (index < 2) ? powerBase0[rf] :
+ powerBase1[rf];
+ break;
+ case 3: /* Customer defined power diff. */
+ chnlGroup = 0;
+
+ for (i = 0; i < 4; i++) {
+ pwr_diff_limit[i] = (u8)((pHalData->MCSTxPowerLevelOriginalOffset[chnlGroup][index +
+ (rf ? 8 : 0)]&(0x7f << (i*8))) >> (i*8));
+ if (pHalData->CurrentChannelBW == HT_CHANNEL_WIDTH_40) {
+ if (pwr_diff_limit[i] > pHalData->PwrGroupHT40[rf][Channel-1])
+ pwr_diff_limit[i] = pHalData->PwrGroupHT40[rf][Channel-1];
+ } else {
+ if (pwr_diff_limit[i] > pHalData->PwrGroupHT20[rf][Channel-1])
+ pwr_diff_limit[i] = pHalData->PwrGroupHT20[rf][Channel-1];
+ }
+ }
+ customer_limit = (pwr_diff_limit[3]<<24) | (pwr_diff_limit[2]<<16) |
+ (pwr_diff_limit[1]<<8) | (pwr_diff_limit[0]);
+ writeVal = customer_limit + ((index<2)?powerBase0[rf]:powerBase1[rf]);
+ break;
+ default:
+ chnlGroup = 0;
+ writeVal = pHalData->MCSTxPowerLevelOriginalOffset[chnlGroup][index+(rf?8:0)] +
+ ((index < 2) ? powerBase0[rf] : powerBase1[rf]);
+ break;
+ }
+
+/* 20100427 Joseph: Driver dynamic Tx power shall not affect Tx power.
+ It shall be determined by power training mechanism. */
+/* Currently, we cannot fully disable driver dynamic tx power mechanism
+ because it is referenced by BT coexist mechanism. */
+/* In the future, two mechanism shall be separated from each other and
+ maintained independantly. Thanks for Lanhsin's reminder. */
+
+ if (pdmpriv->DynamicTxHighPowerLvl == TxHighPwrLevel_Level1)
+ writeVal = 0x14141414;
+ else if (pdmpriv->DynamicTxHighPowerLvl ==
+ TxHighPwrLevel_Level2)
+ writeVal = 0x00000000;
+
+ /* 20100628 Joseph: High power mode for BT-Coexist mechanism. */
+ /* This mechanism is only applied when
+ Driver-Highpower-Mechanism is OFF. */
+ if (pdmpriv->DynamicTxHighPowerLvl == TxHighPwrLevel_BT1)
+ writeVal = writeVal - 0x06060606;
+ else if (pdmpriv->DynamicTxHighPowerLvl == TxHighPwrLevel_BT2)
+ writeVal = writeVal;
+ *(pOutWriteVal + rf) = writeVal;
+ }
+}
+
+static void writeOFDMPowerReg(struct rtw_adapter *Adapter, u8 index,
+ u32 *pValue)
+{
+ struct hal_data_8723a *pHalData = GET_HAL_DATA(Adapter);
+ u16 RegOffset_A[6] = {
+ rTxAGC_A_Rate18_06, rTxAGC_A_Rate54_24,
+ rTxAGC_A_Mcs03_Mcs00, rTxAGC_A_Mcs07_Mcs04,
+ rTxAGC_A_Mcs11_Mcs08, rTxAGC_A_Mcs15_Mcs12
+ };
+ u16 RegOffset_B[6] = {
+ rTxAGC_B_Rate18_06, rTxAGC_B_Rate54_24,
+ rTxAGC_B_Mcs03_Mcs00, rTxAGC_B_Mcs07_Mcs04,
+ rTxAGC_B_Mcs11_Mcs08, rTxAGC_B_Mcs15_Mcs12
+ };
+ u8 i, rf, pwr_val[4];
+ u32 writeVal;
+ u16 RegOffset;
+
+ for (rf = 0; rf < 2; rf++) {
+ writeVal = pValue[rf];
+ for (i = 0; i < 4; i++) {
+ pwr_val[i] = (u8)((writeVal &
+ (0x7f << (i * 8))) >> (i * 8));
+ if (pwr_val[i] > RF6052_MAX_TX_PWR)
+ pwr_val[i] = RF6052_MAX_TX_PWR;
+ }
+ writeVal = pwr_val[3] << 24 | pwr_val[2] << 16 |
+ pwr_val[1] << 8 | pwr_val[0];
+
+ if (rf == 0)
+ RegOffset = RegOffset_A[index];
+ else
+ RegOffset = RegOffset_B[index];
+
+ rtl8723au_write32(Adapter, RegOffset, writeVal);
+
+ /* 201005115 Joseph: Set Tx Power diff for Tx power
+ training mechanism. */
+ if (((pHalData->rf_type == RF_2T2R) &&
+ (RegOffset == rTxAGC_A_Mcs15_Mcs12 ||
+ RegOffset == rTxAGC_B_Mcs15_Mcs12)) ||
+ ((pHalData->rf_type != RF_2T2R) &&
+ (RegOffset == rTxAGC_A_Mcs07_Mcs04 ||
+ RegOffset == rTxAGC_B_Mcs07_Mcs04))) {
+ writeVal = pwr_val[3];
+ if (RegOffset == rTxAGC_A_Mcs15_Mcs12 ||
+ RegOffset == rTxAGC_A_Mcs07_Mcs04)
+ RegOffset = 0xc90;
+ if (RegOffset == rTxAGC_B_Mcs15_Mcs12 ||
+ RegOffset == rTxAGC_B_Mcs07_Mcs04)
+ RegOffset = 0xc98;
+ for (i = 0; i < 3; i++) {
+ if (i != 2)
+ writeVal = (writeVal > 8) ?
+ (writeVal - 8) : 0;
+ else
+ writeVal = (writeVal > 6) ?
+ (writeVal - 6) : 0;
+ rtl8723au_write8(Adapter, RegOffset + i,
+ (u8)writeVal);
+ }
+ }
+ }
+}
+/*-----------------------------------------------------------------------------
+ * Function: PHY_RF6052SetOFDMTxPower
+ *
+ * Overview: For legacy and HY OFDM, we must read EEPROM TX power index for
+ * different channel and read original value in TX power
+ * register area from 0xe00. We increase offset and
+ * original value to be correct tx pwr.
+ *
+ * Input: NONE
+ *
+ * Output: NONE
+ *
+ * Return: NONE
+ *
+ * Revised History:
+ * When Remark
+ * 11/05/2008 MHC Simulate 8192 series method.
+ * 01/06/2009 MHC 1. Prevent Path B tx power overflow or
+ * underflow dure to A/B pwr difference or
+ * legacy/HT pwr diff.
+ * 2. We concern with path B legacy/HT OFDM difference.
+ * 01/22/2009 MHC Support new EPRO format from SD3.
+ *
+ *---------------------------------------------------------------------------*/
+void rtl8723a_PHY_RF6052SetOFDMTxPower(struct rtw_adapter *Adapter,
+ u8 *pPowerLevel, u8 Channel)
+{
+ u32 writeVal[2], powerBase0[2], powerBase1[2];
+ u8 index = 0;
+
+ getPowerBase(Adapter, pPowerLevel, Channel,
+ &powerBase0[0], &powerBase1[0]);
+
+ for (index = 0; index < 6; index++) {
+ getTxPowerWriteValByRegulatory(Adapter, Channel, index,
+ &powerBase0[0], &powerBase1[0], &writeVal[0]);
+
+ writeOFDMPowerReg(Adapter, index, &writeVal[0]);
+ }
+}
+
+static int phy_RF6052_Config_ParaFile(struct rtw_adapter *Adapter)
+{
+ u32 u4RegValue = 0;
+ u8 eRFPath;
+ struct bb_reg_define *pPhyReg;
+ int rtStatus = _SUCCESS;
+ struct hal_data_8723a *pHalData = GET_HAL_DATA(Adapter);
+
+ /* 3----------------------------------------------------------------- */
+ /* 3 <2> Initialize RF */
+ /* 3----------------------------------------------------------------- */
+ for (eRFPath = 0; eRFPath < pHalData->NumTotalRFPath; eRFPath++) {
+
+ pPhyReg = &pHalData->PHYRegDef[eRFPath];
+
+ /*----Store original RFENV control type----*/
+ switch (eRFPath) {
+ case RF_PATH_A:
+ u4RegValue = PHY_QueryBBReg(Adapter, pPhyReg->rfintfs,
+ bRFSI_RFENV);
+ break;
+ case RF_PATH_B:
+ u4RegValue = PHY_QueryBBReg(Adapter, pPhyReg->rfintfs,
+ bRFSI_RFENV << 16);
+ break;
+ }
+
+ /*----Set RF_ENV enable----*/
+ PHY_SetBBReg(Adapter, pPhyReg->rfintfe, bRFSI_RFENV << 16, 0x1);
+ udelay(1);/* PlatformStallExecution(1); */
+
+ /*----Set RF_ENV output high----*/
+ PHY_SetBBReg(Adapter, pPhyReg->rfintfo, bRFSI_RFENV, 0x1);
+ udelay(1);/* PlatformStallExecution(1); */
+
+ /* Set bit number of Address and Data for RF register */
+ PHY_SetBBReg(Adapter, pPhyReg->rfHSSIPara2, b3WireAddressLength,
+ 0x0); /* Set 1 to 4 bits for 8255 */
+ udelay(1);/* PlatformStallExecution(1); */
+
+ PHY_SetBBReg(Adapter, pPhyReg->rfHSSIPara2, b3WireDataLength,
+ 0x0); /* Set 0 to 12 bits for 8255 */
+ udelay(1);/* PlatformStallExecution(1); */
+
+ /*----Initialize RF fom connfiguration file----*/
+ switch (eRFPath) {
+ case RF_PATH_A:
+ ODM_ReadAndConfig_RadioA_1T_8723A(&pHalData->odmpriv);
+ break;
+ case RF_PATH_B:
+ break;
+ }
+
+ /*----Restore RFENV control type----*/;
+ switch (eRFPath) {
+ case RF_PATH_A:
+ PHY_SetBBReg(Adapter, pPhyReg->rfintfs,
+ bRFSI_RFENV, u4RegValue);
+ break;
+ case RF_PATH_B:
+ PHY_SetBBReg(Adapter, pPhyReg->rfintfs,
+ bRFSI_RFENV << 16, u4RegValue);
+ break;
+ }
+
+ if (rtStatus != _SUCCESS) {
+ goto phy_RF6052_Config_ParaFile_Fail;
+ }
+ }
+phy_RF6052_Config_ParaFile_Fail:
+ return rtStatus;
+}
+
+int PHY_RF6052_Config8723A(struct rtw_adapter *Adapter)
+{
+ struct hal_data_8723a *pHalData = GET_HAL_DATA(Adapter);
+ int rtStatus = _SUCCESS;
+
+ /* Initialize general global value */
+ /* TODO: Extend RF_PATH_C and RF_PATH_D in the future */
+ if (pHalData->rf_type == RF_1T1R)
+ pHalData->NumTotalRFPath = 1;
+ else
+ pHalData->NumTotalRFPath = 2;
+
+ /* Config BB and RF */
+ rtStatus = phy_RF6052_Config_ParaFile(Adapter);
+ return rtStatus;
+}
+
+/* End of HalRf6052.c */
diff --git a/drivers/staging/rtl8723au/hal/rtl8723a_rxdesc.c b/drivers/staging/rtl8723au/hal/rtl8723a_rxdesc.c
new file mode 100644
index 000000000..81b5efe64
--- /dev/null
+++ b/drivers/staging/rtl8723au/hal/rtl8723a_rxdesc.c
@@ -0,0 +1,69 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2011 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ ******************************************************************************/
+#define _RTL8723A_REDESC_C_
+
+#include <osdep_service.h>
+#include <drv_types.h>
+#include <rtl8723a_hal.h>
+
+static void process_rssi(struct rtw_adapter *padapter,
+ struct recv_frame *prframe)
+{
+ struct rx_pkt_attrib *pattrib = &prframe->attrib;
+ struct signal_stat *signal_stat = &padapter->recvpriv.signal_strength_data;
+
+ if (signal_stat->update_req) {
+ signal_stat->total_num = 0;
+ signal_stat->total_val = 0;
+ signal_stat->update_req = 0;
+ }
+
+ signal_stat->total_num++;
+ signal_stat->total_val += pattrib->phy_info.SignalStrength;
+ signal_stat->avg_val = signal_stat->total_val / signal_stat->total_num;
+}
+
+static void process_link_qual(struct rtw_adapter *padapter,
+ struct recv_frame *prframe)
+{
+ struct rx_pkt_attrib *pattrib;
+ struct signal_stat *signal_stat;
+
+ if (prframe == NULL || padapter == NULL)
+ return;
+
+ pattrib = &prframe->attrib;
+ signal_stat = &padapter->recvpriv.signal_qual_data;
+
+ if (signal_stat->update_req) {
+ signal_stat->total_num = 0;
+ signal_stat->total_val = 0;
+ signal_stat->update_req = 0;
+ }
+
+ signal_stat->total_num++;
+ signal_stat->total_val += pattrib->phy_info.SignalQuality;
+ signal_stat->avg_val = signal_stat->total_val / signal_stat->total_num;
+}
+
+/* void rtl8723a_process_phy_info(struct rtw_adapter *padapter, union recv_frame *prframe) */
+void rtl8723a_process_phy_info(struct rtw_adapter *padapter, void *prframe)
+{
+ struct recv_frame *precvframe = prframe;
+ /* Check RSSI */
+ process_rssi(padapter, precvframe);
+ /* Check EVM */
+ process_link_qual(padapter, precvframe);
+}
diff --git a/drivers/staging/rtl8723au/hal/rtl8723a_sreset.c b/drivers/staging/rtl8723au/hal/rtl8723a_sreset.c
new file mode 100644
index 000000000..3c46294b8
--- /dev/null
+++ b/drivers/staging/rtl8723au/hal/rtl8723a_sreset.c
@@ -0,0 +1,55 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2012 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ ******************************************************************************/
+#define _RTL8723A_SRESET_C_
+
+#include <rtl8723a_sreset.h>
+#include <rtl8723a_hal.h>
+#include <usb_ops_linux.h>
+
+void rtl8723a_sreset_xmit_status_check(struct rtw_adapter *padapter)
+{
+ struct hal_data_8723a *pHalData = GET_HAL_DATA(padapter);
+ struct sreset_priv *psrtpriv = &pHalData->srestpriv;
+
+ unsigned long current_time;
+ struct xmit_priv *pxmitpriv = &padapter->xmitpriv;
+ unsigned int diff_time;
+ u32 txdma_status;
+
+ txdma_status = rtl8723au_read32(padapter, REG_TXDMA_STATUS);
+ if (txdma_status != 0) {
+ DBG_8723A("%s REG_TXDMA_STATUS:0x%08x\n", __func__, txdma_status);
+ rtw_sreset_reset(padapter);
+ }
+
+ current_time = jiffies;
+
+ if (0 == pxmitpriv->free_xmitbuf_cnt || 0 == pxmitpriv->free_xmit_extbuf_cnt) {
+
+ diff_time = jiffies_to_msecs(jiffies - psrtpriv->last_tx_time);
+
+ if (diff_time > 2000) {
+ if (psrtpriv->last_tx_complete_time == 0) {
+ psrtpriv->last_tx_complete_time = current_time;
+ } else {
+ diff_time = jiffies_to_msecs(jiffies - psrtpriv->last_tx_complete_time);
+ if (diff_time > 4000) {
+ DBG_8723A("%s tx hang\n", __func__);
+ rtw_sreset_reset(padapter);
+ }
+ }
+ }
+ }
+}
diff --git a/drivers/staging/rtl8723au/hal/rtl8723au_recv.c b/drivers/staging/rtl8723au/hal/rtl8723au_recv.c
new file mode 100644
index 000000000..0fec84bcb
--- /dev/null
+++ b/drivers/staging/rtl8723au/hal/rtl8723au_recv.c
@@ -0,0 +1,267 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2011 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ ******************************************************************************/
+#define _RTL8192CU_RECV_C_
+#include <osdep_service.h>
+#include <drv_types.h>
+#include <recv_osdep.h>
+#include <mlme_osdep.h>
+#include <linux/ip.h>
+#include <linux/if_ether.h>
+#include <usb_ops.h>
+#include <wifi.h>
+#include <rtl8723a_hal.h>
+
+int rtl8723au_init_recv_priv(struct rtw_adapter *padapter)
+{
+ struct recv_priv *precvpriv = &padapter->recvpriv;
+ int i, size, res = _SUCCESS;
+ struct recv_buf *precvbuf;
+ unsigned long tmpaddr;
+ unsigned long alignment;
+ struct sk_buff *pskb;
+
+ tasklet_init(&precvpriv->recv_tasklet,
+ (void(*)(unsigned long))rtl8723au_recv_tasklet,
+ (unsigned long)padapter);
+
+ precvpriv->int_in_urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!precvpriv->int_in_urb)
+ DBG_8723A("alloc_urb for interrupt in endpoint fail !!!!\n");
+ precvpriv->int_in_buf = kzalloc(USB_INTR_CONTENT_LENGTH, GFP_KERNEL);
+ if (!precvpriv->int_in_buf)
+ DBG_8723A("alloc_mem for interrupt in endpoint fail !!!!\n");
+
+ size = NR_RECVBUFF * sizeof(struct recv_buf);
+ precvpriv->precv_buf = kzalloc(size, GFP_KERNEL);
+ if (!precvpriv->precv_buf) {
+ res = _FAIL;
+ RT_TRACE(_module_rtl871x_recv_c_, _drv_err_,
+ "alloc recv_buf fail!\n");
+ goto exit;
+ }
+
+ precvbuf = (struct recv_buf *)precvpriv->precv_buf;
+
+ for (i = 0; i < NR_RECVBUFF; i++) {
+ INIT_LIST_HEAD(&precvbuf->list);
+
+ precvbuf->purb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!precvbuf->purb)
+ break;
+
+ precvbuf->adapter = padapter;
+
+ precvbuf++;
+ }
+
+ skb_queue_head_init(&precvpriv->rx_skb_queue);
+ skb_queue_head_init(&precvpriv->free_recv_skb_queue);
+
+ for (i = 0; i < NR_PREALLOC_RECV_SKB; i++) {
+ size = MAX_RECVBUF_SZ + RECVBUFF_ALIGN_SZ;
+ pskb = __netdev_alloc_skb(padapter->pnetdev, size, GFP_KERNEL);
+
+ if (pskb) {
+ pskb->dev = padapter->pnetdev;
+
+ tmpaddr = (unsigned long)pskb->data;
+ alignment = tmpaddr & (RECVBUFF_ALIGN_SZ-1);
+ skb_reserve(pskb, (RECVBUFF_ALIGN_SZ - alignment));
+
+ skb_queue_tail(&precvpriv->free_recv_skb_queue, pskb);
+ }
+
+ pskb = NULL;
+ }
+
+exit:
+ return res;
+}
+
+void rtl8723au_free_recv_priv(struct rtw_adapter *padapter)
+{
+ int i;
+ struct recv_buf *precvbuf;
+ struct recv_priv *precvpriv = &padapter->recvpriv;
+
+ precvbuf = (struct recv_buf *)precvpriv->precv_buf;
+
+ for (i = 0; i < NR_RECVBUFF; i++) {
+ usb_free_urb(precvbuf->purb);
+
+ if (precvbuf->pskb)
+ dev_kfree_skb_any(precvbuf->pskb);
+
+ precvbuf++;
+ }
+
+ kfree(precvpriv->precv_buf);
+
+ usb_free_urb(precvpriv->int_in_urb);
+ kfree(precvpriv->int_in_buf);
+
+ if (skb_queue_len(&precvpriv->rx_skb_queue))
+ DBG_8723A(KERN_WARNING "rx_skb_queue not empty\n");
+
+ skb_queue_purge(&precvpriv->rx_skb_queue);
+
+ if (skb_queue_len(&precvpriv->free_recv_skb_queue)) {
+ DBG_8723A(KERN_WARNING "free_recv_skb_queue not empty, %d\n",
+ skb_queue_len(&precvpriv->free_recv_skb_queue));
+ }
+
+ skb_queue_purge(&precvpriv->free_recv_skb_queue);
+}
+
+struct recv_stat_cpu {
+ u32 rxdw0;
+ u32 rxdw1;
+ u32 rxdw2;
+ u32 rxdw3;
+ u32 rxdw4;
+ u32 rxdw5;
+};
+
+void update_recvframe_attrib(struct recv_frame *precvframe,
+ struct recv_stat *prxstat)
+{
+ struct rx_pkt_attrib *pattrib;
+ struct recv_stat_cpu report;
+ struct rxreport_8723a *prxreport;
+
+ report.rxdw0 = le32_to_cpu(prxstat->rxdw0);
+ report.rxdw1 = le32_to_cpu(prxstat->rxdw1);
+ report.rxdw2 = le32_to_cpu(prxstat->rxdw2);
+ report.rxdw3 = le32_to_cpu(prxstat->rxdw3);
+ report.rxdw4 = le32_to_cpu(prxstat->rxdw4);
+ report.rxdw5 = le32_to_cpu(prxstat->rxdw5);
+
+ prxreport = (struct rxreport_8723a *)&report;
+
+ pattrib = &precvframe->attrib;
+ memset(pattrib, 0, sizeof(struct rx_pkt_attrib));
+
+ /* update rx report to recv_frame attribute */
+ pattrib->pkt_len = (u16)prxreport->pktlen;
+ pattrib->drvinfo_sz = (u8)(prxreport->drvinfosize << 3);
+ pattrib->physt = (u8)prxreport->physt;
+
+ pattrib->crc_err = (u8)prxreport->crc32;
+ pattrib->icv_err = (u8)prxreport->icverr;
+
+ pattrib->bdecrypted = (u8)(prxreport->swdec ? 0 : 1);
+ pattrib->encrypt = (u8)prxreport->security;
+
+ pattrib->qos = (u8)prxreport->qos;
+ pattrib->priority = (u8)prxreport->tid;
+
+ pattrib->amsdu = (u8)prxreport->amsdu;
+
+ pattrib->seq_num = (u16)prxreport->seq;
+ pattrib->frag_num = (u8)prxreport->frag;
+ pattrib->mfrag = (u8)prxreport->mf;
+ pattrib->mdata = (u8)prxreport->md;
+
+ pattrib->mcs_rate = (u8)prxreport->rxmcs;
+ pattrib->rxht = (u8)prxreport->rxht;
+}
+
+void update_recvframe_phyinfo(struct recv_frame *precvframe,
+ struct phy_stat *pphy_status)
+{
+ struct rtw_adapter *padapter = precvframe->adapter;
+ struct rx_pkt_attrib *pattrib = &precvframe->attrib;
+ struct hal_data_8723a *pHalData = GET_HAL_DATA(padapter);
+ struct phy_info *pPHYInfo = &pattrib->phy_info;
+ struct odm_packet_info pkt_info;
+ u8 *sa = NULL, *da;
+ struct sta_priv *pstapriv;
+ struct sta_info *psta;
+ struct sk_buff *skb = precvframe->pkt;
+ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
+ bool matchbssid = false;
+ u8 *bssid;
+
+ matchbssid = !ieee80211_is_ctl(hdr->frame_control) &&
+ !pattrib->icv_err && !pattrib->crc_err;
+
+ if (matchbssid) {
+ switch (hdr->frame_control &
+ cpu_to_le16(IEEE80211_FCTL_TODS |
+ IEEE80211_FCTL_FROMDS)) {
+ case cpu_to_le16(IEEE80211_FCTL_TODS):
+ bssid = hdr->addr1;
+ break;
+ case cpu_to_le16(IEEE80211_FCTL_FROMDS):
+ bssid = hdr->addr2;
+ break;
+ case cpu_to_le16(0):
+ bssid = hdr->addr3;
+ break;
+ default:
+ bssid = NULL;
+ matchbssid = false;
+ }
+
+ if (bssid)
+ matchbssid = ether_addr_equal(
+ get_bssid(&padapter->mlmepriv), bssid);
+ }
+
+ pkt_info.bPacketMatchBSSID = matchbssid;
+
+ da = ieee80211_get_DA(hdr);
+ pkt_info.bPacketToSelf = pkt_info.bPacketMatchBSSID &&
+ (!memcmp(da, myid(&padapter->eeprompriv), ETH_ALEN));
+
+ pkt_info.bPacketBeacon = pkt_info.bPacketMatchBSSID &&
+ ieee80211_is_beacon(hdr->frame_control);
+
+ pkt_info.StationID = 0xFF;
+ if (pkt_info.bPacketBeacon) {
+ if (check_fwstate(&padapter->mlmepriv, WIFI_STATION_STATE) == true)
+ sa = padapter->mlmepriv.cur_network.network.MacAddress;
+ /* to do Ad-hoc */
+ } else {
+ sa = ieee80211_get_SA(hdr);
+ }
+
+ pstapriv = &padapter->stapriv;
+ psta = rtw_get_stainfo23a(pstapriv, sa);
+ if (psta) {
+ pkt_info.StationID = psta->mac_id;
+ /* printk("%s ==> StationID(%d)\n", __func__, pkt_info.StationID); */
+ }
+ pkt_info.Rate = pattrib->mcs_rate;
+
+ ODM_PhyStatusQuery23a(&pHalData->odmpriv, pPHYInfo,
+ (u8 *)pphy_status, &pkt_info);
+ precvframe->psta = NULL;
+ if (pkt_info.bPacketMatchBSSID &&
+ (check_fwstate(&padapter->mlmepriv, WIFI_AP_STATE) == true)) {
+ if (psta) {
+ precvframe->psta = psta;
+ rtl8723a_process_phy_info(padapter, precvframe);
+ }
+ } else if (pkt_info.bPacketToSelf || pkt_info.bPacketBeacon) {
+ if (check_fwstate(&padapter->mlmepriv,
+ WIFI_ADHOC_STATE|WIFI_ADHOC_MASTER_STATE) ==
+ true) {
+ if (psta)
+ precvframe->psta = psta;
+ }
+ rtl8723a_process_phy_info(padapter, precvframe);
+ }
+}
diff --git a/drivers/staging/rtl8723au/hal/rtl8723au_xmit.c b/drivers/staging/rtl8723au/hal/rtl8723au_xmit.c
new file mode 100644
index 000000000..6bf87fe86
--- /dev/null
+++ b/drivers/staging/rtl8723au/hal/rtl8723au_xmit.c
@@ -0,0 +1,520 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2011 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ ******************************************************************************/
+#define _RTL8192C_XMIT_C_
+#include <osdep_service.h>
+#include <drv_types.h>
+#include <wifi.h>
+#include <osdep_intf.h>
+#include <usb_ops.h>
+/* include <rtl8192c_hal.h> */
+#include <rtl8723a_hal.h>
+
+static int urb_zero_packet_chk(struct rtw_adapter *padapter, int sz)
+{
+ int blnSetTxDescOffset;
+ struct dvobj_priv *pdvobj = adapter_to_dvobj(padapter);
+
+ if (pdvobj->ishighspeed) {
+ if (((sz + TXDESC_SIZE) % 512) == 0)
+ blnSetTxDescOffset = 1;
+ else
+ blnSetTxDescOffset = 0;
+ } else {
+ if (((sz + TXDESC_SIZE) % 64) == 0)
+ blnSetTxDescOffset = 1;
+ else
+ blnSetTxDescOffset = 0;
+ }
+ return blnSetTxDescOffset;
+}
+
+static void rtl8192cu_cal_txdesc_chksum(struct tx_desc *ptxdesc)
+{
+ __le16 *usPtr = (__le16 *)ptxdesc;
+ u32 count = 16; /* (32 bytes / 2 bytes per XOR) => 16 times */
+ u32 index;
+ u16 checksum = 0;
+
+ /* Clear first */
+ ptxdesc->txdw7 &= cpu_to_le32(0xffff0000);
+
+ for (index = 0 ; index < count ; index++)
+ checksum = checksum ^ le16_to_cpu(*(usPtr + index));
+
+ ptxdesc->txdw7 |= cpu_to_le32(0x0000ffff&checksum);
+}
+
+static void fill_txdesc_sectype(struct pkt_attrib *pattrib, struct tx_desc *ptxdesc)
+{
+ if ((pattrib->encrypt > 0) && !pattrib->bswenc) {
+ switch (pattrib->encrypt) {
+ /* SEC_TYPE */
+ case WLAN_CIPHER_SUITE_WEP40:
+ case WLAN_CIPHER_SUITE_WEP104:
+ ptxdesc->txdw1 |= cpu_to_le32((0x01<<22)&0x00c00000);
+ break;
+ case WLAN_CIPHER_SUITE_TKIP:
+ /* ptxdesc->txdw1 |= cpu_to_le32((0x02<<22)&0x00c00000); */
+ ptxdesc->txdw1 |= cpu_to_le32((0x01<<22)&0x00c00000);
+ break;
+ case WLAN_CIPHER_SUITE_CCMP:
+ ptxdesc->txdw1 |= cpu_to_le32((0x03<<22)&0x00c00000);
+ break;
+ case 0:
+ default:
+ break;
+ }
+ }
+}
+
+static void fill_txdesc_vcs(struct pkt_attrib *pattrib, __le32 *pdw)
+{
+ /* DBG_8723A("cvs_mode =%d\n", pattrib->vcs_mode); */
+
+ switch (pattrib->vcs_mode) {
+ case RTS_CTS:
+ *pdw |= cpu_to_le32(BIT(12));
+ break;
+ case CTS_TO_SELF:
+ *pdw |= cpu_to_le32(BIT(11));
+ break;
+ case NONE_VCS:
+ default:
+ break;
+ }
+
+ if (pattrib->vcs_mode) {
+ *pdw |= cpu_to_le32(BIT(13));
+
+ /* Set RTS BW */
+ if (pattrib->ht_en) {
+ *pdw |= (pattrib->bwmode&HT_CHANNEL_WIDTH_40) ? cpu_to_le32(BIT(27)) : 0;
+
+ if (pattrib->ch_offset == HAL_PRIME_CHNL_OFFSET_LOWER)
+ *pdw |= cpu_to_le32((0x01<<28)&0x30000000);
+ else if (pattrib->ch_offset == HAL_PRIME_CHNL_OFFSET_UPPER)
+ *pdw |= cpu_to_le32((0x02<<28)&0x30000000);
+ else if (pattrib->ch_offset == HAL_PRIME_CHNL_OFFSET_DONT_CARE)
+ *pdw |= 0;
+ else
+ *pdw |= cpu_to_le32((0x03<<28)&0x30000000);
+ }
+ }
+}
+
+static void fill_txdesc_phy(struct pkt_attrib *pattrib, __le32 *pdw)
+{
+ if (pattrib->ht_en) {
+ *pdw |= (pattrib->bwmode&HT_CHANNEL_WIDTH_40) ? cpu_to_le32(BIT(25)) : 0;
+
+ if (pattrib->ch_offset == HAL_PRIME_CHNL_OFFSET_LOWER)
+ *pdw |= cpu_to_le32((0x01<<20)&0x003f0000);
+ else if (pattrib->ch_offset == HAL_PRIME_CHNL_OFFSET_UPPER)
+ *pdw |= cpu_to_le32((0x02<<20)&0x003f0000);
+ else if (pattrib->ch_offset == HAL_PRIME_CHNL_OFFSET_DONT_CARE)
+ *pdw |= 0;
+ else
+ *pdw |= cpu_to_le32((0x03<<20)&0x003f0000);
+ }
+}
+
+static s32 update_txdesc(struct xmit_frame *pxmitframe, u8 *pmem, s32 sz)
+{
+ int pull = 0;
+ uint qsel;
+ struct rtw_adapter *padapter = pxmitframe->padapter;
+ struct pkt_attrib *pattrib = &pxmitframe->attrib;
+ struct hal_data_8723a *pHalData = GET_HAL_DATA(padapter);
+ struct dm_priv *pdmpriv = &pHalData->dmpriv;
+ struct tx_desc *ptxdesc = (struct tx_desc *)pmem;
+ struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv;
+ struct mlme_ext_info *pmlmeinfo = &pmlmeext->mlmext_info;
+ int bmcst = is_multicast_ether_addr(pattrib->ra);
+
+ if (urb_zero_packet_chk(padapter, sz) == 0) {
+ ptxdesc = (struct tx_desc *)(pmem+PACKET_OFFSET_SZ);
+ pull = 1;
+ pxmitframe->pkt_offset--;
+ }
+
+ memset(ptxdesc, 0, sizeof(struct tx_desc));
+
+ if (pxmitframe->frame_tag == DATA_FRAMETAG) {
+ /* offset 4 */
+ ptxdesc->txdw1 |= cpu_to_le32(pattrib->mac_id&0x1f);
+
+ qsel = (uint)(pattrib->qsel & 0x0000001f);
+ ptxdesc->txdw1 |= cpu_to_le32((qsel << QSEL_SHT) & 0x00001f00);
+
+ ptxdesc->txdw1 |= cpu_to_le32((pattrib->raid<<16) & 0x000f0000);
+
+ fill_txdesc_sectype(pattrib, ptxdesc);
+
+ if (pattrib->ampdu_en)
+ ptxdesc->txdw1 |= cpu_to_le32(BIT(5));/* AGG EN */
+ else
+ ptxdesc->txdw1 |= cpu_to_le32(BIT(6));/* AGG BK */
+
+ /* offset 8 */
+
+ /* offset 12 */
+ ptxdesc->txdw3 |= cpu_to_le32((pattrib->seqnum<<16)&0xffff0000);
+
+ /* offset 16 , offset 20 */
+ if (pattrib->qos_en)
+ ptxdesc->txdw4 |= cpu_to_le32(BIT(6));/* QoS */
+
+ if ((pattrib->ether_type != 0x888e) &&
+ (pattrib->ether_type != 0x0806) &&
+ (pattrib->dhcp_pkt != 1)) {
+ /* Non EAP & ARP & DHCP type data packet */
+
+ fill_txdesc_vcs(pattrib, &ptxdesc->txdw4);
+ fill_txdesc_phy(pattrib, &ptxdesc->txdw4);
+
+ ptxdesc->txdw4 |= cpu_to_le32(0x00000008);/* RTS Rate = 24M */
+ ptxdesc->txdw5 |= cpu_to_le32(0x0001ff00);/* */
+
+ /* use REG_INIDATA_RATE_SEL value */
+ ptxdesc->txdw5 |= cpu_to_le32(pdmpriv->INIDATA_RATE[pattrib->mac_id]);
+ } else {
+ /* EAP data packet and ARP packet. */
+ /* Use the 1M data rate to send the EAP/ARP packet. */
+ /* This will maybe make the handshake smooth. */
+
+ ptxdesc->txdw1 |= cpu_to_le32(BIT(6));/* AGG BK */
+
+ ptxdesc->txdw4 |= cpu_to_le32(BIT(8));/* driver uses rate */
+
+ if (pmlmeinfo->preamble_mode == PREAMBLE_SHORT)
+ ptxdesc->txdw4 |= cpu_to_le32(BIT(24));/* DATA_SHORT */
+
+ ptxdesc->txdw5 |= cpu_to_le32(MRateToHwRate23a(pmlmeext->tx_rate));
+ }
+ } else if (pxmitframe->frame_tag == MGNT_FRAMETAG) {
+ /* offset 4 */
+ ptxdesc->txdw1 |= cpu_to_le32(pattrib->mac_id&0x1f);
+
+ qsel = (uint)(pattrib->qsel&0x0000001f);
+ ptxdesc->txdw1 |= cpu_to_le32((qsel<<QSEL_SHT)&0x00001f00);
+
+ ptxdesc->txdw1 |= cpu_to_le32((pattrib->raid<<16) & 0x000f0000);
+
+ /* offset 8 */
+ /* CCX-TXRPT ack for xmit mgmt frames. */
+ if (pxmitframe->ack_report)
+ ptxdesc->txdw2 |= cpu_to_le32(BIT(19));
+
+ /* offset 12 */
+ ptxdesc->txdw3 |= cpu_to_le32((pattrib->seqnum<<16)&0xffff0000);
+
+ /* offset 16 */
+ ptxdesc->txdw4 |= cpu_to_le32(BIT(8));/* driver uses rate */
+
+ /* offset 20 */
+ ptxdesc->txdw5 |= cpu_to_le32(BIT(17));/* retry limit enable */
+ ptxdesc->txdw5 |= cpu_to_le32(0x00180000);/* retry limit = 6 */
+
+ ptxdesc->txdw5 |= cpu_to_le32(MRateToHwRate23a(pmlmeext->tx_rate));
+ } else if (pxmitframe->frame_tag == TXAGG_FRAMETAG) {
+ DBG_8723A("pxmitframe->frame_tag == TXAGG_FRAMETAG\n");
+ } else {
+ DBG_8723A("pxmitframe->frame_tag = %d\n",
+ pxmitframe->frame_tag);
+
+ /* offset 4 */
+ ptxdesc->txdw1 |= cpu_to_le32((4)&0x1f);/* CAM_ID(MAC_ID) */
+
+ ptxdesc->txdw1 |= cpu_to_le32((6<<16) & 0x000f0000);/* raid */
+
+ /* offset 8 */
+
+ /* offset 12 */
+ ptxdesc->txdw3 |= cpu_to_le32((pattrib->seqnum<<16)&0xffff0000);
+
+ /* offset 16 */
+ ptxdesc->txdw4 |= cpu_to_le32(BIT(8));/* driver uses rate */
+
+ /* offset 20 */
+ ptxdesc->txdw5 |= cpu_to_le32(MRateToHwRate23a(pmlmeext->tx_rate));
+ }
+
+ /* (1) The sequence number of each non-Qos frame / broadcast / multicast / */
+ /* mgnt frame should be controled by Hw because Fw will also send null data */
+ /* which we cannot control when Fw LPS enable. */
+ /* --> default enable non-Qos data sequense number. 2010.06.23. by tynli. */
+ /* (2) Enable HW SEQ control for beacon packet, because we use Hw beacon. */
+ /* (3) Use HW Qos SEQ to control the seq num of Ext port non-Qos packets. */
+ if (!pattrib->qos_en) {
+ /* Hw set sequence number */
+ ptxdesc->txdw4 |= cpu_to_le32(BIT(7));
+ /* set bit3 to 1. */
+ ptxdesc->txdw3 |= cpu_to_le32((8 << 28));
+ }
+
+ /* offset 0 */
+ ptxdesc->txdw0 |= cpu_to_le32(sz&0x0000ffff);
+ ptxdesc->txdw0 |= cpu_to_le32(OWN | FSG | LSG);
+ ptxdesc->txdw0 |= cpu_to_le32(((TXDESC_SIZE+OFFSET_SZ)<<OFFSET_SHT)&0x00ff0000);/* 32 bytes for TX Desc */
+
+ if (bmcst)
+ ptxdesc->txdw0 |= cpu_to_le32(BIT(24));
+
+ RT_TRACE(_module_rtl871x_xmit_c_, _drv_info_,
+ "offset0-txdesc = 0x%x\n", ptxdesc->txdw0);
+
+ /* offset 4 */
+ /* pkt_offset, unit:8 bytes padding */
+ if (pxmitframe->pkt_offset > 0)
+ ptxdesc->txdw1 |= cpu_to_le32((pxmitframe->pkt_offset << 26) & 0x7c000000);
+
+ rtl8192cu_cal_txdesc_chksum(ptxdesc);
+ return pull;
+}
+
+static int rtw_dump_xframe(struct rtw_adapter *padapter,
+ struct xmit_frame *pxmitframe)
+{
+ int ret = _SUCCESS;
+ int inner_ret = _SUCCESS;
+ int t, sz, w_sz, pull = 0;
+ u8 *mem_addr;
+ u32 ff_hwaddr;
+ struct xmit_buf *pxmitbuf = pxmitframe->pxmitbuf;
+ struct pkt_attrib *pattrib = &pxmitframe->attrib;
+ struct xmit_priv *pxmitpriv = &padapter->xmitpriv;
+
+ if (pxmitframe->frame_tag == DATA_FRAMETAG &&
+ pxmitframe->attrib.ether_type != ETH_P_ARP &&
+ pxmitframe->attrib.ether_type != ETH_P_PAE &&
+ pxmitframe->attrib.dhcp_pkt != 1)
+ rtw_issue_addbareq_cmd23a(padapter, pxmitframe);
+
+ mem_addr = pxmitframe->buf_addr;
+
+ RT_TRACE(_module_rtl871x_xmit_c_, _drv_info_, "rtw_dump_xframe()\n");
+
+ for (t = 0; t < pattrib->nr_frags; t++) {
+ if (inner_ret != _SUCCESS && ret == _SUCCESS)
+ ret = _FAIL;
+
+ if (t != (pattrib->nr_frags - 1)) {
+ RT_TRACE(_module_rtl871x_xmit_c_, _drv_err_,
+ "pattrib->nr_frags =%d\n", pattrib->nr_frags);
+
+ sz = pxmitpriv->frag_len;
+ sz = sz - 4 - pattrib->icv_len;
+ } else {
+ /* no frag */
+ sz = pattrib->last_txcmdsz;
+ }
+
+ pull = update_txdesc(pxmitframe, mem_addr, sz);
+
+ if (pull) {
+ mem_addr += PACKET_OFFSET_SZ; /* pull txdesc head */
+
+ pxmitframe->buf_addr = mem_addr;
+
+ w_sz = sz + TXDESC_SIZE;
+ } else {
+ w_sz = sz + TXDESC_SIZE + PACKET_OFFSET_SZ;
+ }
+
+ ff_hwaddr = rtw_get_ff_hwaddr23a(pxmitframe);
+ inner_ret = rtl8723au_write_port(padapter, ff_hwaddr,
+ w_sz, pxmitbuf);
+ rtw_count_tx_stats23a(padapter, pxmitframe, sz);
+
+ RT_TRACE(_module_rtl871x_xmit_c_, _drv_info_,
+ "rtw_write_port, w_sz =%d\n", w_sz);
+
+ mem_addr += w_sz;
+
+ mem_addr = PTR_ALIGN(mem_addr, 4);
+ }
+
+ rtw_free_xmitframe23a(pxmitpriv, pxmitframe);
+
+ if (ret != _SUCCESS)
+ rtw23a_sctx_done_err(&pxmitbuf->sctx, RTW_SCTX_DONE_UNKNOWN);
+
+ return ret;
+}
+
+bool rtl8723au_xmitframe_complete(struct rtw_adapter *padapter,
+ struct xmit_priv *pxmitpriv,
+ struct xmit_buf *pxmitbuf)
+{
+ struct hw_xmit *phwxmits;
+ struct xmit_frame *pxmitframe;
+ int hwentry;
+ int res = _SUCCESS, xcnt = 0;
+
+ phwxmits = pxmitpriv->hwxmits;
+ hwentry = pxmitpriv->hwxmit_entry;
+
+ RT_TRACE(_module_rtl871x_xmit_c_, _drv_info_, "xmitframe_complete()\n");
+
+ if (pxmitbuf == NULL) {
+ pxmitbuf = rtw_alloc_xmitbuf23a(pxmitpriv);
+ if (!pxmitbuf)
+ return false;
+ }
+ pxmitframe = rtw_dequeue_xframe23a(pxmitpriv, phwxmits, hwentry);
+
+ if (pxmitframe) {
+ pxmitframe->pxmitbuf = pxmitbuf;
+
+ pxmitframe->buf_addr = pxmitbuf->pbuf;
+
+ pxmitbuf->priv_data = pxmitframe;
+
+ if (pxmitframe->frame_tag == DATA_FRAMETAG) {
+ if (pxmitframe->attrib.priority <= 15)/* TID0~15 */
+ res = rtw_xmitframe_coalesce23a(padapter, pxmitframe->pkt, pxmitframe);
+
+ rtw_os_xmit_complete23a(padapter, pxmitframe);/* always return ndis_packet after rtw_xmitframe_coalesce23a */
+ }
+
+ RT_TRACE(_module_rtl871x_xmit_c_, _drv_info_,
+ "xmitframe_complete(): rtw_dump_xframe\n");
+
+ if (res == _SUCCESS) {
+ rtw_dump_xframe(padapter, pxmitframe);
+ } else {
+ rtw_free_xmitbuf23a(pxmitpriv, pxmitbuf);
+ rtw_free_xmitframe23a(pxmitpriv, pxmitframe);
+ }
+ xcnt++;
+ } else {
+ rtw_free_xmitbuf23a(pxmitpriv, pxmitbuf);
+ return false;
+ }
+ return true;
+}
+
+static int xmitframe_direct(struct rtw_adapter *padapter,
+ struct xmit_frame *pxmitframe)
+{
+ int res;
+
+ res = rtw_xmitframe_coalesce23a(padapter, pxmitframe->pkt, pxmitframe);
+ if (res == _SUCCESS)
+ rtw_dump_xframe(padapter, pxmitframe);
+ return res;
+}
+
+/*
+ * Return
+ * true dump packet directly
+ * false enqueue packet
+ */
+bool rtl8723au_hal_xmit(struct rtw_adapter *padapter,
+ struct xmit_frame *pxmitframe)
+{
+ int res;
+ struct xmit_buf *pxmitbuf = NULL;
+ struct xmit_priv *pxmitpriv = &padapter->xmitpriv;
+ struct pkt_attrib *pattrib = &pxmitframe->attrib;
+ struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
+
+ pattrib->qsel = pattrib->priority;
+ spin_lock_bh(&pxmitpriv->lock);
+
+#ifdef CONFIG_8723AU_AP_MODE
+ if (xmitframe_enqueue_for_sleeping_sta23a(padapter, pxmitframe)) {
+ struct sta_info *psta;
+ struct sta_priv *pstapriv = &padapter->stapriv;
+
+ spin_unlock_bh(&pxmitpriv->lock);
+
+ if (pattrib->psta)
+ psta = pattrib->psta;
+ else
+ psta = rtw_get_stainfo23a(pstapriv, pattrib->ra);
+
+ if (psta) {
+ if (psta->sleepq_len > (NR_XMITFRAME>>3))
+ wakeup_sta_to_xmit23a(padapter, psta);
+ }
+
+ return false;
+ }
+#endif
+
+ if (rtw_txframes_sta_ac_pending23a(padapter, pattrib) > 0)
+ goto enqueue;
+
+ if (check_fwstate(pmlmepriv, _FW_UNDER_SURVEY|_FW_UNDER_LINKING) == true)
+ goto enqueue;
+
+ pxmitbuf = rtw_alloc_xmitbuf23a(pxmitpriv);
+ if (pxmitbuf == NULL)
+ goto enqueue;
+
+ spin_unlock_bh(&pxmitpriv->lock);
+
+ pxmitframe->pxmitbuf = pxmitbuf;
+ pxmitframe->buf_addr = pxmitbuf->pbuf;
+ pxmitbuf->priv_data = pxmitframe;
+
+ if (xmitframe_direct(padapter, pxmitframe) != _SUCCESS) {
+ rtw_free_xmitbuf23a(pxmitpriv, pxmitbuf);
+ rtw_free_xmitframe23a(pxmitpriv, pxmitframe);
+ }
+ return true;
+
+enqueue:
+ res = rtw_xmitframe_enqueue23a(padapter, pxmitframe);
+ spin_unlock_bh(&pxmitpriv->lock);
+
+ if (res != _SUCCESS) {
+ RT_TRACE(_module_xmit_osdep_c_, _drv_err_,
+ "pre_xmitframe: enqueue xmitframe fail\n");
+ rtw_free_xmitframe23a(pxmitpriv, pxmitframe);
+
+ /* Trick, make the statistics correct */
+ pxmitpriv->tx_pkts--;
+ pxmitpriv->tx_drop++;
+ return true;
+ }
+ return false;
+}
+
+int rtl8723au_mgnt_xmit(struct rtw_adapter *padapter,
+ struct xmit_frame *pmgntframe)
+{
+ return rtw_dump_xframe(padapter, pmgntframe);
+}
+
+int rtl8723au_hal_xmitframe_enqueue(struct rtw_adapter *padapter,
+ struct xmit_frame *pxmitframe)
+{
+ struct xmit_priv *pxmitpriv = &padapter->xmitpriv;
+ int err;
+
+ err = rtw_xmitframe_enqueue23a(padapter, pxmitframe);
+ if (err != _SUCCESS) {
+ rtw_free_xmitframe23a(pxmitpriv, pxmitframe);
+
+ /* Trick, make the statistics correct */
+ pxmitpriv->tx_pkts--;
+ pxmitpriv->tx_drop++;
+ } else {
+ tasklet_hi_schedule(&pxmitpriv->xmit_tasklet);
+ }
+ return err;
+}
diff --git a/drivers/staging/rtl8723au/hal/usb_halinit.c b/drivers/staging/rtl8723au/hal/usb_halinit.c
new file mode 100644
index 000000000..42ae29d26
--- /dev/null
+++ b/drivers/staging/rtl8723au/hal/usb_halinit.c
@@ -0,0 +1,1273 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2011 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ ******************************************************************************/
+#define _HCI_HAL_INIT_C_
+
+#include <osdep_service.h>
+#include <drv_types.h>
+#include <rtw_efuse.h>
+
+#include <HalPwrSeqCmd.h>
+#include <Hal8723PwrSeq.h>
+#include <rtl8723a_hal.h>
+#include <linux/ieee80211.h>
+
+#include <usb_ops.h>
+
+static void phy_SsPwrSwitch92CU(struct rtw_adapter *Adapter,
+ enum rt_rf_power_state eRFPowerState);
+
+static void
+_ConfigChipOutEP(struct rtw_adapter *pAdapter, u8 NumOutPipe)
+{
+ u8 value8;
+ struct hal_data_8723a *pHalData = GET_HAL_DATA(pAdapter);
+
+ pHalData->OutEpQueueSel = 0;
+ pHalData->OutEpNumber = 0;
+
+ /* Normal and High queue */
+ value8 = rtl8723au_read8(pAdapter, (REG_NORMAL_SIE_EP + 1));
+
+ if (value8 & USB_NORMAL_SIE_EP_MASK) {
+ pHalData->OutEpQueueSel |= TX_SELE_HQ;
+ pHalData->OutEpNumber++;
+ }
+
+ if ((value8 >> USB_NORMAL_SIE_EP_SHIFT) & USB_NORMAL_SIE_EP_MASK) {
+ pHalData->OutEpQueueSel |= TX_SELE_NQ;
+ pHalData->OutEpNumber++;
+ }
+
+ /* Low queue */
+ value8 = rtl8723au_read8(pAdapter, (REG_NORMAL_SIE_EP + 2));
+ if (value8 & USB_NORMAL_SIE_EP_MASK) {
+ pHalData->OutEpQueueSel |= TX_SELE_LQ;
+ pHalData->OutEpNumber++;
+ }
+
+ /* TODO: Error recovery for this case */
+ /* RT_ASSERT((NumOutPipe == pHalData->OutEpNumber),
+ ("Out EP number isn't match! %d(Descriptor) != %d (SIE reg)\n",
+ (u32)NumOutPipe, (u32)pHalData->OutEpNumber)); */
+}
+
+bool rtl8723au_chip_configure(struct rtw_adapter *padapter)
+{
+ struct hal_data_8723a *pHalData = GET_HAL_DATA(padapter);
+ struct dvobj_priv *pdvobjpriv = adapter_to_dvobj(padapter);
+ u8 NumInPipe = pdvobjpriv->RtNumInPipes;
+ u8 NumOutPipe = pdvobjpriv->RtNumOutPipes;
+
+ _ConfigChipOutEP(padapter, NumOutPipe);
+
+ /* Normal chip with one IN and one OUT doesn't have interrupt IN EP. */
+ if (pHalData->OutEpNumber == 1) {
+ if (NumInPipe != 1)
+ return false;
+ }
+
+ return Hal_MappingOutPipe23a(padapter, NumOutPipe);
+}
+
+static int _InitPowerOn(struct rtw_adapter *padapter)
+{
+ u16 value16;
+ u8 value8;
+
+ /* RSV_CTRL 0x1C[7:0] = 0x00
+ unlock ISO/CLK/Power control register */
+ rtl8723au_write8(padapter, REG_RSV_CTRL, 0x0);
+
+ /* HW Power on sequence */
+ if (!HalPwrSeqCmdParsing23a(padapter, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK,
+ PWR_INTF_USB_MSK, rtl8723AU_card_enable_flow))
+ return _FAIL;
+
+ /* 0x04[19] = 1, suggest by Jackie 2011.05.09, reset 8051 */
+ value8 = rtl8723au_read8(padapter, REG_APS_FSMCO+2);
+ rtl8723au_write8(padapter, REG_APS_FSMCO + 2, value8 | BIT(3));
+
+ /* Enable MAC DMA/WMAC/SCHEDULE/SEC block */
+ /* Set CR bit10 to enable 32k calibration. Suggested by SD1 Gimmy.
+ Added by tynli. 2011.08.31. */
+ value16 = rtl8723au_read16(padapter, REG_CR);
+ value16 |= (HCI_TXDMA_EN | HCI_RXDMA_EN | TXDMA_EN | RXDMA_EN |
+ PROTOCOL_EN | SCHEDULE_EN | MACTXEN | MACRXEN |
+ ENSEC | CALTMR_EN);
+ rtl8723au_write16(padapter, REG_CR, value16);
+
+ /* for Efuse PG, suggest by Jackie 2011.11.23 */
+ PHY_SetBBReg(padapter, REG_EFUSE_CTRL, BIT(28)|BIT(29)|BIT(30), 0x06);
+
+ return _SUCCESS;
+}
+
+/* Shall USB interface init this? */
+static void _InitInterrupt(struct rtw_adapter *Adapter)
+{
+ u32 value32;
+
+ /* HISR - turn all on */
+ value32 = 0xFFFFFFFF;
+ rtl8723au_write32(Adapter, REG_HISR, value32);
+
+ /* HIMR - turn all on */
+ rtl8723au_write32(Adapter, REG_HIMR, value32);
+}
+
+static void _InitQueueReservedPage(struct rtw_adapter *Adapter)
+{
+ struct hal_data_8723a *pHalData = GET_HAL_DATA(Adapter);
+ struct registry_priv *pregistrypriv = &Adapter->registrypriv;
+ u32 numHQ = 0;
+ u32 numLQ = 0;
+ u32 numNQ = 0;
+ u32 numPubQ;
+ u32 value32;
+ u8 value8;
+ bool bWiFiConfig = pregistrypriv->wifi_spec;
+
+ /* RT_ASSERT((outEPNum>= 2), ("for WMM , number of out-ep "
+ "must more than or equal to 2!\n")); */
+
+ numPubQ = bWiFiConfig ? WMM_NORMAL_PAGE_NUM_PUBQ : NORMAL_PAGE_NUM_PUBQ;
+
+ if (pHalData->OutEpQueueSel & TX_SELE_HQ) {
+ numHQ = bWiFiConfig ?
+ WMM_NORMAL_PAGE_NUM_HPQ : NORMAL_PAGE_NUM_HPQ;
+ }
+
+ if (pHalData->OutEpQueueSel & TX_SELE_LQ) {
+ numLQ = bWiFiConfig ?
+ WMM_NORMAL_PAGE_NUM_LPQ : NORMAL_PAGE_NUM_LPQ;
+ }
+ /* NOTE: This step shall be proceed before
+ writting REG_RQPN. */
+ if (pHalData->OutEpQueueSel & TX_SELE_NQ) {
+ numNQ = bWiFiConfig ?
+ WMM_NORMAL_PAGE_NUM_NPQ : NORMAL_PAGE_NUM_NPQ;
+ }
+ value8 = (u8)_NPQ(numNQ);
+ rtl8723au_write8(Adapter, REG_RQPN_NPQ, value8);
+
+ /* TX DMA */
+ value32 = _HPQ(numHQ) | _LPQ(numLQ) | _PUBQ(numPubQ) | LD_RQPN;
+ rtl8723au_write32(Adapter, REG_RQPN, value32);
+}
+
+static void _InitTxBufferBoundary(struct rtw_adapter *Adapter)
+{
+ struct registry_priv *pregistrypriv = &Adapter->registrypriv;
+
+ u8 txpktbuf_bndy;
+
+ if (!pregistrypriv->wifi_spec)
+ txpktbuf_bndy = TX_PAGE_BOUNDARY;
+ else /* for WMM */
+ txpktbuf_bndy = WMM_NORMAL_TX_PAGE_BOUNDARY;
+
+ rtl8723au_write8(Adapter, REG_TXPKTBUF_BCNQ_BDNY, txpktbuf_bndy);
+ rtl8723au_write8(Adapter, REG_TXPKTBUF_MGQ_BDNY, txpktbuf_bndy);
+ rtl8723au_write8(Adapter, REG_TXPKTBUF_WMAC_LBK_BF_HD, txpktbuf_bndy);
+ rtl8723au_write8(Adapter, REG_TRXFF_BNDY, txpktbuf_bndy);
+ rtl8723au_write8(Adapter, REG_TDECTRL+1, txpktbuf_bndy);
+}
+
+static void _InitPageBoundary(struct rtw_adapter *Adapter)
+{
+ /* RX Page Boundary */
+ /* srand(static_cast<unsigned int>(time(NULL))); */
+ u16 rxff_bndy = 0x27FF;/* rand() % 1) ? 0x27FF : 0x23FF; */
+
+ rtl8723au_write16(Adapter, (REG_TRXFF_BNDY + 2), rxff_bndy);
+
+ /* TODO: ?? shall we set tx boundary? */
+}
+
+static void
+_InitNormalChipRegPriority(struct rtw_adapter *Adapter, u16 beQ, u16 bkQ,
+ u16 viQ, u16 voQ, u16 mgtQ, u16 hiQ)
+{
+ u16 value16 = rtl8723au_read16(Adapter, REG_TRXDMA_CTRL) & 0x7;
+
+ value16 |= _TXDMA_BEQ_MAP(beQ) | _TXDMA_BKQ_MAP(bkQ) |
+ _TXDMA_VIQ_MAP(viQ) | _TXDMA_VOQ_MAP(voQ) |
+ _TXDMA_MGQ_MAP(mgtQ) | _TXDMA_HIQ_MAP(hiQ);
+
+ rtl8723au_write16(Adapter, REG_TRXDMA_CTRL, value16);
+}
+
+static void _InitNormalChipOneOutEpPriority(struct rtw_adapter *Adapter)
+{
+ struct hal_data_8723a *pHalData = GET_HAL_DATA(Adapter);
+ u16 value = 0;
+
+ switch (pHalData->OutEpQueueSel) {
+ case TX_SELE_HQ:
+ value = QUEUE_HIGH;
+ break;
+ case TX_SELE_LQ:
+ value = QUEUE_LOW;
+ break;
+ case TX_SELE_NQ:
+ value = QUEUE_NORMAL;
+ break;
+ default:
+ /* RT_ASSERT(false, ("Shall not reach here!\n")); */
+ break;
+ }
+
+ _InitNormalChipRegPriority(Adapter, value, value, value,
+ value, value, value);
+}
+
+static void _InitNormalChipTwoOutEpPriority(struct rtw_adapter *Adapter)
+{
+ struct hal_data_8723a *pHalData = GET_HAL_DATA(Adapter);
+ struct registry_priv *pregistrypriv = &Adapter->registrypriv;
+ u16 beQ, bkQ, viQ, voQ, mgtQ, hiQ;
+ u16 valueHi = 0;
+ u16 valueLow = 0;
+
+ switch (pHalData->OutEpQueueSel) {
+ case (TX_SELE_HQ | TX_SELE_LQ):
+ valueHi = QUEUE_HIGH;
+ valueLow = QUEUE_LOW;
+ break;
+ case (TX_SELE_NQ | TX_SELE_LQ):
+ valueHi = QUEUE_NORMAL;
+ valueLow = QUEUE_LOW;
+ break;
+ case (TX_SELE_HQ | TX_SELE_NQ):
+ valueHi = QUEUE_HIGH;
+ valueLow = QUEUE_NORMAL;
+ break;
+ default:
+ /* RT_ASSERT(false, ("Shall not reach here!\n")); */
+ break;
+ }
+
+ if (!pregistrypriv->wifi_spec) {
+ beQ = valueLow;
+ bkQ = valueLow;
+ viQ = valueHi;
+ voQ = valueHi;
+ mgtQ = valueHi;
+ hiQ = valueHi;
+ } else {/* for WMM , CONFIG_OUT_EP_WIFI_MODE */
+ beQ = valueLow;
+ bkQ = valueHi;
+ viQ = valueHi;
+ voQ = valueLow;
+ mgtQ = valueHi;
+ hiQ = valueHi;
+ }
+
+ _InitNormalChipRegPriority(Adapter, beQ, bkQ, viQ, voQ, mgtQ, hiQ);
+}
+
+static void _InitNormalChipThreeOutEpPriority(struct rtw_adapter *Adapter)
+{
+ struct registry_priv *pregistrypriv = &Adapter->registrypriv;
+ u16 beQ, bkQ, viQ, voQ, mgtQ, hiQ;
+
+ if (!pregistrypriv->wifi_spec) {/* typical setting */
+ beQ = QUEUE_LOW;
+ bkQ = QUEUE_LOW;
+ viQ = QUEUE_NORMAL;
+ voQ = QUEUE_HIGH;
+ mgtQ = QUEUE_HIGH;
+ hiQ = QUEUE_HIGH;
+ } else {/* for WMM */
+ beQ = QUEUE_LOW;
+ bkQ = QUEUE_NORMAL;
+ viQ = QUEUE_NORMAL;
+ voQ = QUEUE_HIGH;
+ mgtQ = QUEUE_HIGH;
+ hiQ = QUEUE_HIGH;
+ }
+ _InitNormalChipRegPriority(Adapter, beQ, bkQ, viQ, voQ, mgtQ, hiQ);
+}
+
+static void _InitQueuePriority(struct rtw_adapter *Adapter)
+{
+ struct hal_data_8723a *pHalData = GET_HAL_DATA(Adapter);
+
+ switch (pHalData->OutEpNumber) {
+ case 1:
+ _InitNormalChipOneOutEpPriority(Adapter);
+ break;
+ case 2:
+ _InitNormalChipTwoOutEpPriority(Adapter);
+ break;
+ case 3:
+ _InitNormalChipThreeOutEpPriority(Adapter);
+ break;
+ default:
+ /* RT_ASSERT(false, ("Shall not reach here!\n")); */
+ break;
+ }
+}
+
+static void _InitTransferPageSize(struct rtw_adapter *Adapter)
+{
+ /* Tx page size is always 128. */
+
+ u8 value8;
+ value8 = _PSRX(PBP_128) | _PSTX(PBP_128);
+ rtl8723au_write8(Adapter, REG_PBP, value8);
+}
+
+static void _InitDriverInfoSize(struct rtw_adapter *Adapter, u8 drvInfoSize)
+{
+ rtl8723au_write8(Adapter, REG_RX_DRVINFO_SZ, drvInfoSize);
+}
+
+static void _InitWMACSetting(struct rtw_adapter *Adapter)
+{
+ struct hal_data_8723a *pHalData = GET_HAL_DATA(Adapter);
+
+ /* don't turn on AAP, it will allow all packets to driver */
+ pHalData->ReceiveConfig = RCR_APM | RCR_AM | RCR_AB | RCR_CBSSID_DATA |
+ RCR_CBSSID_BCN | RCR_APP_ICV | RCR_AMF |
+ RCR_HTC_LOC_CTRL | RCR_APP_MIC |
+ RCR_APP_PHYSTS;
+
+ /* some REG_RCR will be modified later by
+ phy_ConfigMACWithHeaderFile() */
+ rtl8723au_write32(Adapter, REG_RCR, pHalData->ReceiveConfig);
+
+ /* Accept all multicast address */
+ rtl8723au_write32(Adapter, REG_MAR, 0xFFFFFFFF);
+ rtl8723au_write32(Adapter, REG_MAR + 4, 0xFFFFFFFF);
+
+ /* Accept all data frames */
+ /* value16 = 0xFFFF; */
+ /* rtl8723au_write16(Adapter, REG_RXFLTMAP2, value16); */
+
+ /* 2010.09.08 hpfan */
+ /* Since ADF is removed from RCR, ps-poll will not be indicate
+ to driver, */
+ /* RxFilterMap should mask ps-poll to gurantee AP mode can
+ rx ps-poll. */
+ /* value16 = 0x400; */
+ /* rtl8723au_write16(Adapter, REG_RXFLTMAP1, value16); */
+
+ /* Accept all management frames */
+ /* value16 = 0xFFFF; */
+ /* rtl8723au_write16(Adapter, REG_RXFLTMAP0, value16); */
+
+ /* enable RX_SHIFT bits */
+ /* rtl8723au_write8(Adapter, REG_TRXDMA_CTRL, rtl8723au_read8(Adapter,
+ REG_TRXDMA_CTRL)|BIT(1)); */
+}
+
+static void _InitAdaptiveCtrl(struct rtw_adapter *Adapter)
+{
+ u16 value16;
+ u32 value32;
+
+ /* Response Rate Set */
+ value32 = rtl8723au_read32(Adapter, REG_RRSR);
+ value32 &= ~RATE_BITMAP_ALL;
+ value32 |= RATE_RRSR_CCK_ONLY_1M;
+ rtl8723au_write32(Adapter, REG_RRSR, value32);
+
+ /* CF-END Threshold */
+ /* m_spIoBase->rtl8723au_write8(REG_CFEND_TH, 0x1); */
+
+ /* SIFS (used in NAV) */
+ value16 = _SPEC_SIFS_CCK(0x10) | _SPEC_SIFS_OFDM(0x10);
+ rtl8723au_write16(Adapter, REG_SPEC_SIFS, value16);
+
+ /* Retry Limit */
+ value16 = _LRL(0x30) | _SRL(0x30);
+ rtl8723au_write16(Adapter, REG_RL, value16);
+}
+
+static void _InitRateFallback(struct rtw_adapter *Adapter)
+{
+ /* Set Data Auto Rate Fallback Retry Count register. */
+ rtl8723au_write32(Adapter, REG_DARFRC, 0x00000000);
+ rtl8723au_write32(Adapter, REG_DARFRC+4, 0x10080404);
+ rtl8723au_write32(Adapter, REG_RARFRC, 0x04030201);
+ rtl8723au_write32(Adapter, REG_RARFRC+4, 0x08070605);
+}
+
+static void _InitEDCA(struct rtw_adapter *Adapter)
+{
+ /* Set Spec SIFS (used in NAV) */
+ rtl8723au_write16(Adapter, REG_SPEC_SIFS, 0x100a);
+ rtl8723au_write16(Adapter, REG_MAC_SPEC_SIFS, 0x100a);
+
+ /* Set SIFS for CCK */
+ rtl8723au_write16(Adapter, REG_SIFS_CTX, 0x100a);
+
+ /* Set SIFS for OFDM */
+ rtl8723au_write16(Adapter, REG_SIFS_TRX, 0x100a);
+
+ /* TXOP */
+ rtl8723au_write32(Adapter, REG_EDCA_BE_PARAM, 0x005EA42B);
+ rtl8723au_write32(Adapter, REG_EDCA_BK_PARAM, 0x0000A44F);
+ rtl8723au_write32(Adapter, REG_EDCA_VI_PARAM, 0x005EA324);
+ rtl8723au_write32(Adapter, REG_EDCA_VO_PARAM, 0x002FA226);
+}
+
+static void _InitRDGSetting(struct rtw_adapter *Adapter)
+{
+ rtl8723au_write8(Adapter, REG_RD_CTRL, 0xFF);
+ rtl8723au_write16(Adapter, REG_RD_NAV_NXT, 0x200);
+ rtl8723au_write8(Adapter, REG_RD_RESP_PKT_TH, 0x05);
+}
+
+static void _InitRetryFunction(struct rtw_adapter *Adapter)
+{
+ u8 value8;
+
+ value8 = rtl8723au_read8(Adapter, REG_FWHW_TXQ_CTRL);
+ value8 |= EN_AMPDU_RTY_NEW;
+ rtl8723au_write8(Adapter, REG_FWHW_TXQ_CTRL, value8);
+
+ /* Set ACK timeout */
+ rtl8723au_write8(Adapter, REG_ACKTO, 0x40);
+}
+
+static void _InitRFType(struct rtw_adapter *Adapter)
+{
+ struct hal_data_8723a *pHalData = GET_HAL_DATA(Adapter);
+
+ pHalData->rf_type = RF_1T1R;
+}
+
+/* Set CCK and OFDM Block "ON" */
+static void _BBTurnOnBlock(struct rtw_adapter *Adapter)
+{
+ PHY_SetBBReg(Adapter, rFPGA0_RFMOD, bCCKEn, 0x1);
+ PHY_SetBBReg(Adapter, rFPGA0_RFMOD, bOFDMEn, 0x1);
+}
+
+#define MgntActSet_RF_State(...)
+static void _RfPowerSave(struct rtw_adapter *padapter)
+{
+}
+
+enum {
+ Antenna_Lfet = 1,
+ Antenna_Right = 2,
+};
+
+enum rt_rf_power_state RfOnOffDetect23a(struct rtw_adapter *pAdapter)
+{
+ /* struct hal_data_8723a *pHalData = GET_HAL_DATA(pAdapter); */
+ u8 val8;
+ enum rt_rf_power_state rfpowerstate = rf_off;
+
+ rtl8723au_write8(pAdapter, REG_MAC_PINMUX_CFG,
+ rtl8723au_read8(pAdapter,
+ REG_MAC_PINMUX_CFG) & ~BIT(3));
+ val8 = rtl8723au_read8(pAdapter, REG_GPIO_IO_SEL);
+ DBG_8723A("GPIO_IN =%02x\n", val8);
+ rfpowerstate = (val8 & BIT(3)) ? rf_on : rf_off;
+
+ return rfpowerstate;
+}
+
+int rtl8723au_hal_init(struct rtw_adapter *Adapter)
+{
+ struct hal_data_8723a *pHalData = GET_HAL_DATA(Adapter);
+ struct pwrctrl_priv *pwrctrlpriv = &Adapter->pwrctrlpriv;
+ struct registry_priv *pregistrypriv = &Adapter->registrypriv;
+ u8 val8 = 0;
+ u32 boundary;
+ int status = _SUCCESS;
+ bool mac_on;
+
+ unsigned long init_start_time = jiffies;
+
+ Adapter->hw_init_completed = false;
+
+ if (Adapter->pwrctrlpriv.bkeepfwalive) {
+ phy_SsPwrSwitch92CU(Adapter, rf_on);
+
+ if (pHalData->bIQKInitialized) {
+ rtl8723a_phy_iq_calibrate(Adapter, true);
+ } else {
+ rtl8723a_phy_iq_calibrate(Adapter, false);
+ pHalData->bIQKInitialized = true;
+ }
+ rtl8723a_odm_check_tx_power_tracking(Adapter);
+ rtl8723a_phy_lc_calibrate(Adapter);
+
+ goto exit;
+ }
+
+ /* Check if MAC has already power on. by tynli. 2011.05.27. */
+ val8 = rtl8723au_read8(Adapter, REG_CR);
+ RT_TRACE(_module_hci_hal_init_c_, _drv_info_,
+ "%s: REG_CR 0x100 = 0x%02x\n", __func__, val8);
+ /* Fix 92DU-VC S3 hang with the reason is that secondary mac is not
+ initialized. */
+ /* 0x100 value of first mac is 0xEA while 0x100 value of secondary
+ is 0x00 */
+ if (val8 == 0xEA) {
+ mac_on = false;
+ } else {
+ mac_on = true;
+ RT_TRACE(_module_hci_hal_init_c_, _drv_info_,
+ "%s: MAC has already power on\n", __func__);
+ }
+
+ status = _InitPowerOn(Adapter);
+ if (status == _FAIL) {
+ RT_TRACE(_module_hci_hal_init_c_, _drv_err_,
+ "Failed to init power on!\n");
+ goto exit;
+ }
+
+ if (!pregistrypriv->wifi_spec) {
+ boundary = TX_PAGE_BOUNDARY;
+ } else {
+ /* for WMM */
+ boundary = WMM_NORMAL_TX_PAGE_BOUNDARY;
+ }
+
+ if (!mac_on) {
+ status = InitLLTTable23a(Adapter, boundary);
+ if (status == _FAIL) {
+ RT_TRACE(_module_hci_hal_init_c_, _drv_err_,
+ "Failed to init LLT table\n");
+ goto exit;
+ }
+ }
+
+ if (pHalData->bRDGEnable)
+ _InitRDGSetting(Adapter);
+
+ status = rtl8723a_FirmwareDownload(Adapter);
+ if (status != _SUCCESS) {
+ Adapter->bFWReady = false;
+ DBG_8723A("fw download fail!\n");
+ goto exit;
+ } else {
+ Adapter->bFWReady = true;
+ DBG_8723A("fw download ok!\n");
+ }
+
+ rtl8723a_InitializeFirmwareVars(Adapter);
+
+ if (pwrctrlpriv->reg_rfoff == true) {
+ pwrctrlpriv->rf_pwrstate = rf_off;
+ }
+
+ /* 2010/08/09 MH We need to check if we need to turnon or off RF after detecting */
+ /* HW GPIO pin. Before PHY_RFConfig8192C. */
+ /* HalDetectPwrDownMode(Adapter); */
+ /* 2010/08/26 MH If Efuse does not support sective suspend then disable the function. */
+ /* HalDetectSelectiveSuspendMode(Adapter); */
+
+ /* Set RF type for BB/RF configuration */
+ _InitRFType(Adapter);/* _ReadRFType() */
+
+ /* Save target channel */
+ /* <Roger_Notes> Current Channel will be updated again later. */
+ pHalData->CurrentChannel = 6;/* default set to 6 */
+
+ status = PHY_MACConfig8723A(Adapter);
+ if (status == _FAIL) {
+ DBG_8723A("PHY_MACConfig8723A fault !!\n");
+ goto exit;
+ }
+
+ /* */
+ /* d. Initialize BB related configurations. */
+ /* */
+ status = PHY_BBConfig8723A(Adapter);
+ if (status == _FAIL) {
+ DBG_8723A("PHY_BBConfig8723A fault !!\n");
+ goto exit;
+ }
+
+ /* Add for tx power by rate fine tune. We need to call the function after BB config. */
+ /* Because the tx power by rate table is inited in BB config. */
+
+ status = PHY_RF6052_Config8723A(Adapter);
+ if (status == _FAIL) {
+ DBG_8723A("PHY_RF6052_Config8723A failed!!\n");
+ goto exit;
+ }
+
+ /* reducing 80M spur */
+ rtl8723au_write32(Adapter, REG_AFE_XTAL_CTRL, 0x0381808d);
+ rtl8723au_write32(Adapter, REG_AFE_PLL_CTRL, 0xf0ffff83);
+ rtl8723au_write32(Adapter, REG_AFE_PLL_CTRL, 0xf0ffff82);
+ rtl8723au_write32(Adapter, REG_AFE_PLL_CTRL, 0xf0ffff83);
+
+ /* RFSW Control */
+ /* 0x804[14]= 0 */
+ rtl8723au_write32(Adapter, rFPGA0_TxInfo, 0x00000003);
+ /* 0x870[6:5]= b'11 */
+ rtl8723au_write32(Adapter, rFPGA0_XAB_RFInterfaceSW, 0x07000760);
+ /* 0x860[6:5]= b'00 */
+ rtl8723au_write32(Adapter, rFPGA0_XA_RFInterfaceOE, 0x66F60210);
+
+ RT_TRACE(_module_hci_hal_init_c_, _drv_info_,
+ "%s: 0x870 = value 0x%x\n", __func__,
+ rtl8723au_read32(Adapter, 0x870));
+
+ /* */
+ /* Joseph Note: Keep RfRegChnlVal for later use. */
+ /* */
+ pHalData->RfRegChnlVal[0] = PHY_QueryRFReg(Adapter, RF_PATH_A,
+ RF_CHNLBW, bRFRegOffsetMask);
+ pHalData->RfRegChnlVal[1] = PHY_QueryRFReg(Adapter, RF_PATH_B,
+ RF_CHNLBW, bRFRegOffsetMask);
+
+ if (!mac_on) {
+ _InitQueueReservedPage(Adapter);
+ _InitTxBufferBoundary(Adapter);
+ }
+ _InitQueuePriority(Adapter);
+ _InitPageBoundary(Adapter);
+ _InitTransferPageSize(Adapter);
+
+ /* Get Rx PHY status in order to report RSSI and others. */
+ _InitDriverInfoSize(Adapter, DRVINFO_SZ);
+
+ _InitInterrupt(Adapter);
+ hw_var_set_macaddr(Adapter, Adapter->eeprompriv.mac_addr);
+ rtl8723a_set_media_status(Adapter, MSR_INFRA);
+ _InitWMACSetting(Adapter);
+ _InitAdaptiveCtrl(Adapter);
+ _InitEDCA(Adapter);
+ _InitRateFallback(Adapter);
+ _InitRetryFunction(Adapter);
+ rtl8723a_InitBeaconParameters(Adapter);
+
+ _BBTurnOnBlock(Adapter);
+ /* NicIFSetMacAddress(padapter, padapter->PermanentAddress); */
+
+ rtl8723a_cam_invalidate_all(Adapter);
+
+ /* 2010/12/17 MH We need to set TX power according to EFUSE content at first. */
+ PHY_SetTxPowerLevel8723A(Adapter, pHalData->CurrentChannel);
+
+ rtl8723a_InitAntenna_Selection(Adapter);
+
+ /* HW SEQ CTRL */
+ /* set 0x0 to 0xFF by tynli. Default enable HW SEQ NUM. */
+ rtl8723au_write8(Adapter, REG_HWSEQ_CTRL, 0xFF);
+
+ /* */
+ /* Disable BAR, suggested by Scott */
+ /* 2010.04.09 add by hpfan */
+ /* */
+ rtl8723au_write32(Adapter, REG_BAR_MODE_CTRL, 0x0201ffff);
+
+ if (pregistrypriv->wifi_spec)
+ rtl8723au_write16(Adapter, REG_FAST_EDCA_CTRL, 0);
+
+ /* Move by Neo for USB SS from above setp */
+ _RfPowerSave(Adapter);
+
+ /* 2010/08/26 MH Merge from 8192CE. */
+ /* sherry masked that it has been done in _RfPowerSave */
+ /* 20110927 */
+ /* recovery for 8192cu and 9723Au 20111017 */
+ if (pwrctrlpriv->rf_pwrstate == rf_on) {
+ if (pHalData->bIQKInitialized) {
+ rtl8723a_phy_iq_calibrate(Adapter, true);
+ } else {
+ rtl8723a_phy_iq_calibrate(Adapter, false);
+ pHalData->bIQKInitialized = true;
+ }
+
+ rtl8723a_odm_check_tx_power_tracking(Adapter);
+
+ rtl8723a_phy_lc_calibrate(Adapter);
+
+ rtl8723a_dual_antenna_detection(Adapter);
+ }
+
+ /* fixed USB interface interference issue */
+ rtl8723au_write8(Adapter, 0xfe40, 0xe0);
+ rtl8723au_write8(Adapter, 0xfe41, 0x8d);
+ rtl8723au_write8(Adapter, 0xfe42, 0x80);
+ rtl8723au_write32(Adapter, 0x20c, 0xfd0320);
+ /* Solve too many protocol error on USB bus */
+ if (!IS_81xxC_VENDOR_UMC_A_CUT(pHalData->VersionID)) {
+ /* 0xE6 = 0x94 */
+ rtl8723au_write8(Adapter, 0xFE40, 0xE6);
+ rtl8723au_write8(Adapter, 0xFE41, 0x94);
+ rtl8723au_write8(Adapter, 0xFE42, 0x80);
+
+ /* 0xE0 = 0x19 */
+ rtl8723au_write8(Adapter, 0xFE40, 0xE0);
+ rtl8723au_write8(Adapter, 0xFE41, 0x19);
+ rtl8723au_write8(Adapter, 0xFE42, 0x80);
+
+ /* 0xE5 = 0x91 */
+ rtl8723au_write8(Adapter, 0xFE40, 0xE5);
+ rtl8723au_write8(Adapter, 0xFE41, 0x91);
+ rtl8723au_write8(Adapter, 0xFE42, 0x80);
+
+ /* 0xE2 = 0x81 */
+ rtl8723au_write8(Adapter, 0xFE40, 0xE2);
+ rtl8723au_write8(Adapter, 0xFE41, 0x81);
+ rtl8723au_write8(Adapter, 0xFE42, 0x80);
+
+ }
+
+/* _InitPABias(Adapter); */
+
+ /* Init BT hw config. */
+ rtl8723a_BT_init_hwconfig(Adapter);
+
+ rtl8723a_InitHalDm(Adapter);
+
+ val8 = (WiFiNavUpperUs + HAL_8723A_NAV_UPPER_UNIT - 1) /
+ HAL_8723A_NAV_UPPER_UNIT;
+ rtl8723au_write8(Adapter, REG_NAV_UPPER, val8);
+
+ /* 2011/03/09 MH debug only, UMC-B cut pass 2500 S5 test, but we need to fin root cause. */
+ if (((rtl8723au_read32(Adapter, rFPGA0_RFMOD) & 0xFF000000) !=
+ 0x83000000)) {
+ PHY_SetBBReg(Adapter, rFPGA0_RFMOD, BIT(24), 1);
+ RT_TRACE(_module_hci_hal_init_c_, _drv_err_,
+ "%s: IQK fail recover\n", __func__);
+ }
+
+ /* ack for xmit mgmt frames. */
+ rtl8723au_write32(Adapter, REG_FWHW_TXQ_CTRL,
+ rtl8723au_read32(Adapter, REG_FWHW_TXQ_CTRL)|BIT(12));
+
+exit:
+ if (status == _SUCCESS) {
+ Adapter->hw_init_completed = true;
+
+ if (Adapter->registrypriv.notch_filter == 1)
+ rtl8723a_notch_filter(Adapter, 1);
+ }
+
+ DBG_8723A("%s in %dms\n", __func__,
+ jiffies_to_msecs(jiffies - init_start_time));
+ return status;
+}
+
+static void phy_SsPwrSwitch92CU(struct rtw_adapter *Adapter,
+ enum rt_rf_power_state eRFPowerState)
+{
+ struct hal_data_8723a *pHalData = GET_HAL_DATA(Adapter);
+ u8 sps0;
+
+ sps0 = rtl8723au_read8(Adapter, REG_SPS0_CTRL);
+
+ switch (eRFPowerState) {
+ case rf_on:
+ /* 1. Enable MAC Clock. Can not be enabled now. */
+ /* WriteXBYTE(REG_SYS_CLKR+1,
+ ReadXBYTE(REG_SYS_CLKR+1) | BIT(3)); */
+
+ /* 2. Force PWM, Enable SPS18_LDO_Marco_Block */
+ rtl8723au_write8(Adapter, REG_SPS0_CTRL,
+ sps0 | BIT(0) | BIT(3));
+
+ /* 3. restore BB, AFE control register. */
+ /* RF */
+ if (pHalData->rf_type == RF_2T2R)
+ PHY_SetBBReg(Adapter, rFPGA0_XAB_RFParameter,
+ 0x380038, 1);
+ else
+ PHY_SetBBReg(Adapter, rFPGA0_XAB_RFParameter,
+ 0x38, 1);
+ PHY_SetBBReg(Adapter, rOFDM0_TRxPathEnable, 0xf0, 1);
+ PHY_SetBBReg(Adapter, rFPGA0_RFMOD, BIT(1), 0);
+
+ /* AFE */
+ if (pHalData->rf_type == RF_2T2R)
+ rtl8723au_write32(Adapter, rRx_Wait_CCA, 0x63DB25A0);
+ else if (pHalData->rf_type == RF_1T1R)
+ rtl8723au_write32(Adapter, rRx_Wait_CCA, 0x631B25A0);
+
+ /* 4. issue 3-wire command that RF set to Rx idle
+ mode. This is used to re-write the RX idle mode. */
+ /* We can only prvide a usual value instead and then
+ HW will modify the value by itself. */
+ PHY_SetRFReg(Adapter, RF_PATH_A, RF_AC,
+ bRFRegOffsetMask, 0x32D95);
+ if (pHalData->rf_type == RF_2T2R) {
+ PHY_SetRFReg(Adapter, RF_PATH_B, RF_AC,
+ bRFRegOffsetMask, 0x32D95);
+ }
+ break;
+ case rf_sleep:
+ case rf_off:
+ if (IS_81xxC_VENDOR_UMC_B_CUT(pHalData->VersionID))
+ sps0 &= ~BIT(0);
+ else
+ sps0 &= ~(BIT(0) | BIT(3));
+
+ RT_TRACE(_module_hal_init_c_, _drv_err_, "SS LVL1\n");
+ /* Disable RF and BB only for SelectSuspend. */
+
+ /* 1. Set BB/RF to shutdown. */
+ /* (1) Reg878[5:3]= 0 RF rx_code for
+ preamble power saving */
+ /* (2)Reg878[21:19]= 0 Turn off RF-B */
+ /* (3) RegC04[7:4]= 0 Turn off all paths
+ for packet detection */
+ /* (4) Reg800[1] = 1 enable preamble power saving */
+ Adapter->pwrctrlpriv.PS_BBRegBackup[PSBBREG_RF0] =
+ rtl8723au_read32(Adapter, rFPGA0_XAB_RFParameter);
+ Adapter->pwrctrlpriv.PS_BBRegBackup[PSBBREG_RF1] =
+ rtl8723au_read32(Adapter, rOFDM0_TRxPathEnable);
+ Adapter->pwrctrlpriv.PS_BBRegBackup[PSBBREG_RF2] =
+ rtl8723au_read32(Adapter, rFPGA0_RFMOD);
+ if (pHalData->rf_type == RF_2T2R) {
+ PHY_SetBBReg(Adapter, rFPGA0_XAB_RFParameter,
+ 0x380038, 0);
+ } else if (pHalData->rf_type == RF_1T1R) {
+ PHY_SetBBReg(Adapter, rFPGA0_XAB_RFParameter, 0x38, 0);
+ }
+ PHY_SetBBReg(Adapter, rOFDM0_TRxPathEnable, 0xf0, 0);
+ PHY_SetBBReg(Adapter, rFPGA0_RFMOD, BIT(1), 1);
+
+ /* 2 .AFE control register to power down. bit[30:22] */
+ Adapter->pwrctrlpriv.PS_BBRegBackup[PSBBREG_AFE0] =
+ rtl8723au_read32(Adapter, rRx_Wait_CCA);
+ if (pHalData->rf_type == RF_2T2R)
+ rtl8723au_write32(Adapter, rRx_Wait_CCA, 0x00DB25A0);
+ else if (pHalData->rf_type == RF_1T1R)
+ rtl8723au_write32(Adapter, rRx_Wait_CCA, 0x001B25A0);
+
+ /* 3. issue 3-wire command that RF set to power down.*/
+ PHY_SetRFReg(Adapter, RF_PATH_A, RF_AC, bRFRegOffsetMask, 0);
+ if (pHalData->rf_type == RF_2T2R)
+ PHY_SetRFReg(Adapter, RF_PATH_B, RF_AC,
+ bRFRegOffsetMask, 0);
+
+ /* 4. Force PFM , disable SPS18_LDO_Marco_Block */
+ rtl8723au_write8(Adapter, REG_SPS0_CTRL, sps0);
+ break;
+ default:
+ break;
+ }
+}
+
+static void CardDisableRTL8723U(struct rtw_adapter *Adapter)
+{
+ u8 u1bTmp;
+
+ DBG_8723A("CardDisableRTL8723U\n");
+ /* USB-MF Card Disable Flow */
+ /* 1. Run LPS WL RFOFF flow */
+ HalPwrSeqCmdParsing23a(Adapter, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK,
+ PWR_INTF_USB_MSK, rtl8723AU_enter_lps_flow);
+
+ /* 2. 0x1F[7:0] = 0 turn off RF */
+ rtl8723au_write8(Adapter, REG_RF_CTRL, 0x00);
+
+ /* ==== Reset digital sequence ====== */
+ if ((rtl8723au_read8(Adapter, REG_MCUFWDL) & BIT(7)) &&
+ Adapter->bFWReady) /* 8051 RAM code */
+ rtl8723a_FirmwareSelfReset(Adapter);
+
+ /* Reset MCU. Suggested by Filen. 2011.01.26. by tynli. */
+ u1bTmp = rtl8723au_read8(Adapter, REG_SYS_FUNC_EN+1);
+ rtl8723au_write8(Adapter, REG_SYS_FUNC_EN+1, u1bTmp & ~BIT(2));
+
+ /* g. MCUFWDL 0x80[1:0]= 0 reset MCU ready status */
+ rtl8723au_write8(Adapter, REG_MCUFWDL, 0x00);
+
+ /* ==== Reset digital sequence end ====== */
+ /* Card disable power action flow */
+ HalPwrSeqCmdParsing23a(Adapter, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK,
+ PWR_INTF_USB_MSK,
+ rtl8723AU_card_disable_flow);
+
+ /* Reset MCU IO Wrapper, added by Roger, 2011.08.30. */
+ u1bTmp = rtl8723au_read8(Adapter, REG_RSV_CTRL + 1);
+ rtl8723au_write8(Adapter, REG_RSV_CTRL+1, u1bTmp & ~BIT(0));
+ u1bTmp = rtl8723au_read8(Adapter, REG_RSV_CTRL + 1);
+ rtl8723au_write8(Adapter, REG_RSV_CTRL+1, u1bTmp | BIT(0));
+
+ /* 7. RSV_CTRL 0x1C[7:0] = 0x0E lock ISO/CLK/Power control register */
+ rtl8723au_write8(Adapter, REG_RSV_CTRL, 0x0e);
+}
+
+int rtl8723au_hal_deinit(struct rtw_adapter *padapter)
+{
+ DBG_8723A("==> %s\n", __func__);
+
+#ifdef CONFIG_8723AU_BT_COEXIST
+ BT_HaltProcess(padapter);
+#endif
+ /* 2011/02/18 To Fix RU LNA power leakage problem. We need to
+ execute below below in Adapter init and halt sequence.
+ According to EEchou's opinion, we can enable the ability for all */
+ /* IC. Accord to johnny's opinion, only RU need the support. */
+ CardDisableRTL8723U(padapter);
+
+ padapter->hw_init_completed = false;
+
+ return _SUCCESS;
+}
+
+int rtl8723au_inirp_init(struct rtw_adapter *Adapter)
+{
+ u8 i;
+ struct recv_buf *precvbuf;
+ int status;
+ struct recv_priv *precvpriv = &Adapter->recvpriv;
+ struct hal_data_8723a *pHalData = GET_HAL_DATA(Adapter);
+
+ status = _SUCCESS;
+
+ RT_TRACE(_module_hci_hal_init_c_, _drv_info_, "===> usb_inirp_init\n");
+
+ /* issue Rx irp to receive data */
+ precvbuf = (struct recv_buf *)precvpriv->precv_buf;
+ for (i = 0; i < NR_RECVBUFF; i++) {
+ if (rtl8723au_read_port(Adapter, 0, precvbuf) == _FAIL) {
+ RT_TRACE(_module_hci_hal_init_c_, _drv_err_,
+ "usb_rx_init: usb_read_port error\n");
+ status = _FAIL;
+ goto exit;
+ }
+ precvbuf++;
+ }
+ if (rtl8723au_read_interrupt(Adapter) == _FAIL) {
+ RT_TRACE(_module_hci_hal_init_c_, _drv_err_,
+ "%s: usb_read_interrupt error\n", __func__);
+ status = _FAIL;
+ }
+ pHalData->IntrMask[0] = rtl8723au_read32(Adapter, REG_USB_HIMR);
+ MSG_8723A("pHalData->IntrMask = 0x%04x\n", pHalData->IntrMask[0]);
+ pHalData->IntrMask[0] |= UHIMR_C2HCMD|UHIMR_CPWM;
+ rtl8723au_write32(Adapter, REG_USB_HIMR, pHalData->IntrMask[0]);
+exit:
+ RT_TRACE(_module_hci_hal_init_c_, _drv_info_,
+ "<=== usb_inirp_init\n");
+ return status;
+}
+
+int rtl8723au_inirp_deinit(struct rtw_adapter *Adapter)
+{
+ struct hal_data_8723a *pHalData = GET_HAL_DATA(Adapter);
+
+ RT_TRACE(_module_hci_hal_init_c_, _drv_info_,
+ "===> usb_rx_deinit\n");
+ rtl8723au_read_port_cancel(Adapter);
+ pHalData->IntrMask[0] = rtl8723au_read32(Adapter, REG_USB_HIMR);
+ MSG_8723A("%s pHalData->IntrMask = 0x%04x\n", __func__,
+ pHalData->IntrMask[0]);
+ pHalData->IntrMask[0] = 0x0;
+ rtl8723au_write32(Adapter, REG_USB_HIMR, pHalData->IntrMask[0]);
+ RT_TRACE(_module_hci_hal_init_c_, _drv_info_,
+ "<=== usb_rx_deinit\n");
+ return _SUCCESS;
+}
+
+static void _ReadBoardType(struct rtw_adapter *Adapter, u8 *PROMContent,
+ bool AutoloadFail)
+{
+ struct hal_data_8723a *pHalData = GET_HAL_DATA(Adapter);
+ u8 boardType = BOARD_USB_DONGLE;
+
+ if (AutoloadFail) {
+ if (IS_8723_SERIES(pHalData->VersionID))
+ pHalData->rf_type = RF_1T1R;
+ else
+ pHalData->rf_type = RF_2T2R;
+ pHalData->BoardType = boardType;
+ return;
+ }
+
+ boardType = PROMContent[EEPROM_NORMAL_BoardType];
+ boardType &= BOARD_TYPE_NORMAL_MASK;/* bit[7:5] */
+ boardType >>= 5;
+
+ pHalData->BoardType = boardType;
+ MSG_8723A("_ReadBoardType(%x)\n", pHalData->BoardType);
+
+ if (boardType == BOARD_USB_High_PA)
+ pHalData->ExternalPA = 1;
+}
+
+static void Hal_EfuseParseMACAddr_8723AU(struct rtw_adapter *padapter,
+ u8 *hwinfo, bool AutoLoadFail)
+{
+ u16 i;
+ u8 sMacAddr[ETH_ALEN] = {0x00, 0xE0, 0x4C, 0x87, 0x23, 0x00};
+ struct eeprom_priv *pEEPROM = GET_EEPROM_EFUSE_PRIV(padapter);
+
+ if (AutoLoadFail) {
+ for (i = 0; i < 6; i++)
+ pEEPROM->mac_addr[i] = sMacAddr[i];
+ } else {
+ /* Read Permanent MAC address */
+ memcpy(pEEPROM->mac_addr, &hwinfo[EEPROM_MAC_ADDR_8723AU],
+ ETH_ALEN);
+ }
+
+ RT_TRACE(_module_hci_hal_init_c_, _drv_notice_,
+ "Hal_EfuseParseMACAddr_8723AU: Permanent Address =%02x:%02x:%02x:%02x:%02x:%02x\n",
+ pEEPROM->mac_addr[0], pEEPROM->mac_addr[1],
+ pEEPROM->mac_addr[2], pEEPROM->mac_addr[3],
+ pEEPROM->mac_addr[4], pEEPROM->mac_addr[5]);
+}
+
+static void readAdapterInfo(struct rtw_adapter *padapter)
+{
+ struct eeprom_priv *pEEPROM = GET_EEPROM_EFUSE_PRIV(padapter);
+ /* struct hal_data_8723a * pHalData = GET_HAL_DATA(padapter); */
+ u8 hwinfo[HWSET_MAX_SIZE];
+
+ Hal_InitPGData(padapter, hwinfo);
+ Hal_EfuseParseIDCode(padapter, hwinfo);
+ Hal_EfuseParseEEPROMVer(padapter, hwinfo,
+ pEEPROM->bautoload_fail_flag);
+ Hal_EfuseParseMACAddr_8723AU(padapter, hwinfo,
+ pEEPROM->bautoload_fail_flag);
+ Hal_EfuseParsetxpowerinfo_8723A(padapter, hwinfo,
+ pEEPROM->bautoload_fail_flag);
+ _ReadBoardType(padapter, hwinfo, pEEPROM->bautoload_fail_flag);
+ Hal_EfuseParseBTCoexistInfo_8723A(padapter, hwinfo,
+ pEEPROM->bautoload_fail_flag);
+
+ rtl8723a_EfuseParseChnlPlan(padapter, hwinfo,
+ pEEPROM->bautoload_fail_flag);
+ Hal_EfuseParseThermalMeter_8723A(padapter, hwinfo,
+ pEEPROM->bautoload_fail_flag);
+/* _ReadRFSetting(Adapter, PROMContent, pEEPROM->bautoload_fail_flag); */
+/* _ReadPSSetting(Adapter, PROMContent, pEEPROM->bautoload_fail_flag); */
+ Hal_EfuseParseAntennaDiversity(padapter, hwinfo,
+ pEEPROM->bautoload_fail_flag);
+
+ Hal_EfuseParseEEPROMVer(padapter, hwinfo, pEEPROM->bautoload_fail_flag);
+ Hal_EfuseParseCustomerID(padapter, hwinfo,
+ pEEPROM->bautoload_fail_flag);
+ Hal_EfuseParseRateIndicationOption(padapter, hwinfo,
+ pEEPROM->bautoload_fail_flag);
+ Hal_EfuseParseXtal_8723A(padapter, hwinfo,
+ pEEPROM->bautoload_fail_flag);
+
+ /* hal_CustomizedBehavior_8723U(Adapter); */
+
+/* Adapter->bDongle = (PROMContent[EEPROM_EASY_REPLACEMENT] == 1)? 0: 1; */
+ DBG_8723A("%s(): REPLACEMENT = %x\n", __func__, padapter->bDongle);
+}
+
+static void _ReadPROMContent(struct rtw_adapter *Adapter)
+{
+ struct eeprom_priv *pEEPROM = GET_EEPROM_EFUSE_PRIV(Adapter);
+ u8 eeValue;
+
+ eeValue = rtl8723au_read8(Adapter, REG_9346CR);
+ /* To check system boot selection. */
+ pEEPROM->EepromOrEfuse = (eeValue & BOOT_FROM_EEPROM) ? true : false;
+ pEEPROM->bautoload_fail_flag = (eeValue & EEPROM_EN) ? false : true;
+
+ DBG_8723A("Boot from %s, Autoload %s !\n",
+ (pEEPROM->EepromOrEfuse ? "EEPROM" : "EFUSE"),
+ (pEEPROM->bautoload_fail_flag ? "Fail" : "OK"));
+
+ readAdapterInfo(Adapter);
+}
+
+/* */
+/* Description: */
+/* We should set Efuse cell selection to WiFi cell in default. */
+/* */
+/* Assumption: */
+/* PASSIVE_LEVEL */
+/* */
+/* Added by Roger, 2010.11.23. */
+/* */
+static void hal_EfuseCellSel(struct rtw_adapter *Adapter)
+{
+ u32 value32;
+
+ value32 = rtl8723au_read32(Adapter, EFUSE_TEST);
+ value32 = (value32 & ~EFUSE_SEL_MASK) | EFUSE_SEL(EFUSE_WIFI_SEL_0);
+ rtl8723au_write32(Adapter, EFUSE_TEST, value32);
+}
+
+void rtl8723a_read_adapter_info(struct rtw_adapter *Adapter)
+{
+ unsigned long start = jiffies;
+
+ /* Read EEPROM size before call any EEPROM function */
+ Adapter->EepromAddressSize = GetEEPROMSize8723A(Adapter);
+
+ MSG_8723A("====> _ReadAdapterInfo8723AU\n");
+
+ hal_EfuseCellSel(Adapter);
+
+ _ReadPROMContent(Adapter);
+
+ MSG_8723A("<==== _ReadAdapterInfo8723AU in %d ms\n",
+ jiffies_to_msecs(jiffies - start));
+}
+
+/* */
+/* Description: */
+/* Query setting of specified variable. */
+/* */
+int GetHalDefVar8192CUsb(struct rtw_adapter *Adapter,
+ enum hal_def_variable eVariable, void *pValue)
+{
+ struct hal_data_8723a *pHalData = GET_HAL_DATA(Adapter);
+ int bResult = _SUCCESS;
+
+ switch (eVariable) {
+ case HAL_DEF_UNDERCORATEDSMOOTHEDPWDB:
+ *((int *)pValue) = pHalData->dmpriv.UndecoratedSmoothedPWDB;
+ break;
+ case HAL_DEF_IS_SUPPORT_ANT_DIV:
+ break;
+ case HAL_DEF_CURRENT_ANTENNA:
+ break;
+ case HAL_DEF_DRVINFO_SZ:
+ *((u32 *)pValue) = DRVINFO_SZ;
+ break;
+ case HAL_DEF_MAX_RECVBUF_SZ:
+ *((u32 *)pValue) = MAX_RECVBUF_SZ;
+ break;
+ case HAL_DEF_RX_PACKET_OFFSET:
+ *((u32 *)pValue) = RXDESC_SIZE + DRVINFO_SZ;
+ break;
+ case HAL_DEF_DBG_DUMP_RXPKT:
+ *((u8 *)pValue) = pHalData->bDumpRxPkt;
+ break;
+ case HAL_DEF_DBG_DM_FUNC:
+ *((u32 *)pValue) = pHalData->odmpriv.SupportAbility;
+ break;
+ case HW_VAR_MAX_RX_AMPDU_FACTOR:
+ *((u32 *)pValue) = IEEE80211_HT_MAX_AMPDU_64K;
+ break;
+ case HW_DEF_ODM_DBG_FLAG:
+ {
+ struct dm_odm_t *pDM_Odm = &pHalData->odmpriv;
+ printk("pDM_Odm->DebugComponents = 0x%llx\n",
+ pDM_Odm->DebugComponents);
+ }
+ break;
+ default:
+ bResult = _FAIL;
+ break;
+ }
+
+ return bResult;
+}
+
+void rtl8723a_update_ramask(struct rtw_adapter *padapter,
+ u32 mac_id, u8 rssi_level)
+{
+ struct sta_info *psta;
+ struct FW_Sta_Info *fw_sta;
+ struct hal_data_8723a *pHalData = GET_HAL_DATA(padapter);
+ struct dm_priv *pdmpriv = &pHalData->dmpriv;
+ struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv;
+ struct mlme_ext_info *pmlmeinfo = &pmlmeext->mlmext_info;
+ struct wlan_bssid_ex *cur_network = &pmlmeinfo->network;
+ u8 init_rate, networkType, raid, arg;
+ u32 mask, rate_bitmap;
+ u8 shortGIrate = false;
+ int supportRateNum;
+
+ if (mac_id >= NUM_STA) /* CAM_SIZE */
+ return;
+
+ psta = pmlmeinfo->FW_sta_info[mac_id].psta;
+ if (psta == NULL)
+ return;
+
+ switch (mac_id) {
+ case 0:/* for infra mode */
+ supportRateNum =
+ rtw_get_rateset_len23a(cur_network->SupportedRates);
+ networkType = judge_network_type23a(padapter,
+ cur_network->SupportedRates,
+ supportRateNum) & 0xf;
+ /* pmlmeext->cur_wireless_mode = networkType; */
+ raid = networktype_to_raid23a(networkType);
+
+ mask = update_supported_rate23a(cur_network->SupportedRates,
+ supportRateNum);
+ mask |= (pmlmeinfo->HT_enable) ?
+ update_MSC_rate23a(&pmlmeinfo->ht_cap) : 0;
+
+ if (support_short_GI23a(padapter, &pmlmeinfo->ht_cap))
+ shortGIrate = true;
+ break;
+
+ case 1:/* for broadcast/multicast */
+ fw_sta = &pmlmeinfo->FW_sta_info[mac_id];
+ supportRateNum = rtw_get_rateset_len23a(fw_sta->SupportedRates);
+ if (pmlmeext->cur_wireless_mode & WIRELESS_11B)
+ networkType = WIRELESS_11B;
+ else
+ networkType = WIRELESS_11G;
+ raid = networktype_to_raid23a(networkType);
+
+ mask = update_basic_rate23a(cur_network->SupportedRates,
+ supportRateNum);
+ break;
+
+ default: /* for each sta in IBSS */
+ fw_sta = &pmlmeinfo->FW_sta_info[mac_id];
+ supportRateNum = rtw_get_rateset_len23a(fw_sta->SupportedRates);
+ networkType = judge_network_type23a(padapter,
+ fw_sta->SupportedRates,
+ supportRateNum) & 0xf;
+ /* pmlmeext->cur_wireless_mode = networkType; */
+ raid = networktype_to_raid23a(networkType);
+
+ mask = update_supported_rate23a(cur_network->SupportedRates,
+ supportRateNum);
+
+ /* todo: support HT in IBSS */
+ break;
+ }
+
+ /* mask &= 0x0fffffff; */
+ rate_bitmap = ODM_Get_Rate_Bitmap23a(pHalData, mac_id, mask,
+ rssi_level);
+ DBG_8723A("%s => mac_id:%d, networkType:0x%02x, "
+ "mask:0x%08x\n\t ==> rssi_level:%d, rate_bitmap:0x%08x\n",
+ __func__, mac_id, networkType, mask, rssi_level, rate_bitmap);
+
+ mask &= rate_bitmap;
+ mask |= ((raid << 28) & 0xf0000000);
+
+ init_rate = get_highest_rate_idx23a(mask) & 0x3f;
+
+ arg = mac_id & 0x1f;/* MACID */
+ arg |= BIT(7);
+
+ if (shortGIrate == true)
+ arg |= BIT(5);
+
+ DBG_8723A("update raid entry, mask = 0x%x, arg = 0x%x\n", mask, arg);
+
+ rtl8723a_set_raid_cmd(padapter, mask, arg);
+
+ /* set ra_id */
+ psta->raid = raid;
+ psta->init_rate = init_rate;
+
+ /* set correct initial date rate for each mac_id */
+ pdmpriv->INIDATA_RATE[mac_id] = init_rate;
+}
diff --git a/drivers/staging/rtl8723au/hal/usb_ops_linux.c b/drivers/staging/rtl8723au/hal/usb_ops_linux.c
new file mode 100644
index 000000000..371e6b373
--- /dev/null
+++ b/drivers/staging/rtl8723au/hal/usb_ops_linux.c
@@ -0,0 +1,694 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2011 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ ******************************************************************************/
+#define _HCI_OPS_OS_C_
+
+#include <osdep_service.h>
+#include <drv_types.h>
+#include <osdep_intf.h>
+#include <usb_ops.h>
+#include <recv_osdep.h>
+#include <rtl8723a_hal.h>
+#include <rtl8723a_recv.h>
+
+u8 rtl8723au_read8(struct rtw_adapter *padapter, u16 addr)
+{
+ struct dvobj_priv *pdvobjpriv = adapter_to_dvobj(padapter);
+ struct usb_device *udev = pdvobjpriv->pusbdev;
+ int len;
+ u8 data;
+
+ mutex_lock(&pdvobjpriv->usb_vendor_req_mutex);
+ len = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0),
+ REALTEK_USB_VENQT_CMD_REQ, REALTEK_USB_VENQT_READ,
+ addr, 0, &pdvobjpriv->usb_buf.val8, sizeof(data),
+ RTW_USB_CONTROL_MSG_TIMEOUT);
+
+ data = pdvobjpriv->usb_buf.val8;
+ mutex_unlock(&pdvobjpriv->usb_vendor_req_mutex);
+
+ return data;
+}
+
+u16 rtl8723au_read16(struct rtw_adapter *padapter, u16 addr)
+{
+ struct dvobj_priv *pdvobjpriv = adapter_to_dvobj(padapter);
+ struct usb_device *udev = pdvobjpriv->pusbdev;
+ int len;
+ u16 data;
+
+ mutex_lock(&pdvobjpriv->usb_vendor_req_mutex);
+ len = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0),
+ REALTEK_USB_VENQT_CMD_REQ, REALTEK_USB_VENQT_READ,
+ addr, 0, &pdvobjpriv->usb_buf.val16, sizeof(data),
+ RTW_USB_CONTROL_MSG_TIMEOUT);
+
+ data = le16_to_cpu(pdvobjpriv->usb_buf.val16);
+ mutex_unlock(&pdvobjpriv->usb_vendor_req_mutex);
+
+ return data;
+}
+
+u32 rtl8723au_read32(struct rtw_adapter *padapter, u16 addr)
+{
+ struct dvobj_priv *pdvobjpriv = adapter_to_dvobj(padapter);
+ struct usb_device *udev = pdvobjpriv->pusbdev;
+ int len;
+ u32 data;
+
+ mutex_lock(&pdvobjpriv->usb_vendor_req_mutex);
+ len = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0),
+ REALTEK_USB_VENQT_CMD_REQ, REALTEK_USB_VENQT_READ,
+ addr, 0, &pdvobjpriv->usb_buf.val32, sizeof(data),
+ RTW_USB_CONTROL_MSG_TIMEOUT);
+
+ data = le32_to_cpu(pdvobjpriv->usb_buf.val32);
+ mutex_unlock(&pdvobjpriv->usb_vendor_req_mutex);
+
+ return data;
+}
+
+int rtl8723au_write8(struct rtw_adapter *padapter, u16 addr, u8 val)
+{
+ struct dvobj_priv *pdvobjpriv = adapter_to_dvobj(padapter);
+ struct usb_device *udev = pdvobjpriv->pusbdev;
+ int ret;
+
+ mutex_lock(&pdvobjpriv->usb_vendor_req_mutex);
+ pdvobjpriv->usb_buf.val8 = val;
+
+ ret = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
+ REALTEK_USB_VENQT_CMD_REQ,
+ REALTEK_USB_VENQT_WRITE,
+ addr, 0, &pdvobjpriv->usb_buf.val8, sizeof(val),
+ RTW_USB_CONTROL_MSG_TIMEOUT);
+
+ if (ret != sizeof(val))
+ ret = _FAIL;
+ else
+ ret = _SUCCESS;
+
+ mutex_unlock(&pdvobjpriv->usb_vendor_req_mutex);
+ return ret;
+}
+
+int rtl8723au_write16(struct rtw_adapter *padapter, u16 addr, u16 val)
+{
+ struct dvobj_priv *pdvobjpriv = adapter_to_dvobj(padapter);
+ struct usb_device *udev = pdvobjpriv->pusbdev;
+ int ret;
+
+ mutex_lock(&pdvobjpriv->usb_vendor_req_mutex);
+ pdvobjpriv->usb_buf.val16 = cpu_to_le16(val);
+
+ ret = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
+ REALTEK_USB_VENQT_CMD_REQ,
+ REALTEK_USB_VENQT_WRITE,
+ addr, 0, &pdvobjpriv->usb_buf.val16, sizeof(val),
+ RTW_USB_CONTROL_MSG_TIMEOUT);
+
+ if (ret != sizeof(val))
+ ret = _FAIL;
+ else
+ ret = _SUCCESS;
+
+ mutex_unlock(&pdvobjpriv->usb_vendor_req_mutex);
+ return ret;
+}
+
+int rtl8723au_write32(struct rtw_adapter *padapter, u16 addr, u32 val)
+{
+ struct dvobj_priv *pdvobjpriv = adapter_to_dvobj(padapter);
+ struct usb_device *udev = pdvobjpriv->pusbdev;
+ int ret;
+
+ mutex_lock(&pdvobjpriv->usb_vendor_req_mutex);
+ pdvobjpriv->usb_buf.val32 = cpu_to_le32(val);
+
+ ret = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
+ REALTEK_USB_VENQT_CMD_REQ,
+ REALTEK_USB_VENQT_WRITE,
+ addr, 0, &pdvobjpriv->usb_buf.val32, sizeof(val),
+ RTW_USB_CONTROL_MSG_TIMEOUT);
+
+ if (ret != sizeof(val))
+ ret = _FAIL;
+ else
+ ret = _SUCCESS;
+
+ mutex_unlock(&pdvobjpriv->usb_vendor_req_mutex);
+ return ret;
+}
+
+int rtl8723au_writeN(struct rtw_adapter *padapter, u16 addr, u16 len, u8 *buf)
+{
+ struct dvobj_priv *pdvobjpriv = adapter_to_dvobj(padapter);
+ struct usb_device *udev = pdvobjpriv->pusbdev;
+ int ret;
+
+ ret = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
+ REALTEK_USB_VENQT_CMD_REQ,
+ REALTEK_USB_VENQT_WRITE,
+ addr, 0, buf, len, RTW_USB_CONTROL_MSG_TIMEOUT);
+
+ if (ret != len)
+ return _FAIL;
+ return _SUCCESS;
+}
+
+/*
+ * Description:
+ * Recognize the interrupt content by reading the interrupt
+ * register or content and masking interrupt mask (IMR)
+ * if it is our NIC's interrupt. After recognizing, we may clear
+ * the all interrupts (ISR).
+ * Arguments:
+ * [in] Adapter -
+ * The adapter context.
+ * [in] pContent -
+ * Under PCI interface, this field is ignord.
+ * Under USB interface, the content is the interrupt
+ * content pointer.
+ * Under SDIO interface, this is the interrupt type which
+ * is Local interrupt or system interrupt.
+ * [in] ContentLen -
+ * The length in byte of pContent.
+ * Return:
+ * If any interrupt matches the mask (IMR), return true, and
+ * return false otherwise.
+ */
+static bool
+InterruptRecognized8723AU(struct rtw_adapter *Adapter, void *pContent,
+ u32 ContentLen)
+{
+ struct hal_data_8723a *pHalData = GET_HAL_DATA(Adapter);
+ u8 *buffer = (u8 *)pContent;
+ struct reportpwrstate_parm report;
+
+ memcpy(&pHalData->IntArray[0], &buffer[USB_INTR_CONTENT_HISR_OFFSET],
+ 4);
+ pHalData->IntArray[0] &= pHalData->IntrMask[0];
+
+ /* For HISR extension. Added by tynli. 2009.10.07. */
+ memcpy(&pHalData->IntArray[1],
+ &buffer[USB_INTR_CONTENT_HISRE_OFFSET], 4);
+ pHalData->IntArray[1] &= pHalData->IntrMask[1];
+
+ /* We sholud remove this function later because DDK suggest
+ * not to executing too many operations in MPISR */
+
+ memcpy(&report.state, &buffer[USB_INTR_CPWM_OFFSET], 1);
+
+ return (pHalData->IntArray[0] & pHalData->IntrMask[0]) != 0 ||
+ (pHalData->IntArray[1] & pHalData->IntrMask[1]) != 0;
+}
+
+static void usb_read_interrupt_complete(struct urb *purb)
+{
+ int err;
+ struct rtw_adapter *padapter = (struct rtw_adapter *)purb->context;
+
+ if (padapter->bSurpriseRemoved || padapter->bDriverStopped ||
+ padapter->bReadPortCancel) {
+ DBG_8723A("%s() RX Warning! bDriverStopped(%d) OR "
+ "bSurpriseRemoved(%d) bReadPortCancel(%d)\n",
+ __func__, padapter->bDriverStopped,
+ padapter->bSurpriseRemoved,
+ padapter->bReadPortCancel);
+ return;
+ }
+
+ if (purb->status == 0) {
+ struct c2h_evt_hdr *c2h_evt;
+
+ c2h_evt = (struct c2h_evt_hdr *)purb->transfer_buffer;
+
+ if (purb->actual_length > USB_INTR_CONTENT_LENGTH) {
+ DBG_8723A("usb_read_interrupt_complete: purb->actual_"
+ "length > USB_INTR_CONTENT_LENGTH\n");
+ goto urb_submit;
+ }
+
+ InterruptRecognized8723AU(padapter, purb->transfer_buffer,
+ purb->actual_length);
+
+ if (c2h_evt_exist(c2h_evt)) {
+ if (c2h_id_filter_ccx_8723a(c2h_evt->id)) {
+ /* Handle CCX report here */
+ handle_txrpt_ccx_8723a(padapter, (void *)
+ c2h_evt->payload);
+ schedule_work(&padapter->evtpriv.irq_wk);
+ } else {
+ struct evt_work *c2w;
+ int res;
+
+ c2w = kmalloc(sizeof(struct evt_work),
+ GFP_ATOMIC);
+
+ if (!c2w) {
+ printk(KERN_WARNING "%s: unable to "
+ "allocate work buffer\n",
+ __func__);
+ goto urb_submit;
+ }
+
+ c2w->adapter = padapter;
+ INIT_WORK(&c2w->work, rtw_evt_work);
+ memcpy(c2w->u.buf, purb->transfer_buffer, 16);
+
+ res = queue_work(padapter->evtpriv.wq,
+ &c2w->work);
+
+ if (!res) {
+ printk(KERN_ERR "%s: Call to "
+ "queue_work() failed\n",
+ __func__);
+ kfree(c2w);
+ goto urb_submit;
+ }
+ }
+ }
+
+urb_submit:
+ err = usb_submit_urb(purb, GFP_ATOMIC);
+ if (err && (err != -EPERM)) {
+ DBG_8723A("cannot submit interrupt in-token(err = "
+ "0x%08x), urb_status = %d\n",
+ err, purb->status);
+ }
+ } else {
+ DBG_8723A("###=> usb_read_interrupt_complete => urb "
+ "status(%d)\n", purb->status);
+
+ switch (purb->status) {
+ case -EINVAL:
+ case -EPIPE:
+ case -ENODEV:
+ case -ESHUTDOWN:
+ RT_TRACE(_module_hci_ops_os_c_, _drv_err_,
+ "usb_read_port_complete:bSurpriseRemoved =true\n");
+ /* Fall Through here */
+ case -ENOENT:
+ padapter->bDriverStopped = true;
+ RT_TRACE(_module_hci_ops_os_c_, _drv_err_,
+ "usb_read_port_complete:bDriverStopped =true\n");
+ break;
+ case -EPROTO:
+ break;
+ case -EINPROGRESS:
+ DBG_8723A("ERROR: URB IS IN PROGRESS!\n");
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+int rtl8723au_read_interrupt(struct rtw_adapter *adapter)
+{
+ int err;
+ unsigned int pipe;
+ int ret = _SUCCESS;
+ struct dvobj_priv *pdvobj = adapter_to_dvobj(adapter);
+ struct recv_priv *precvpriv = &adapter->recvpriv;
+ struct usb_device *pusbd = pdvobj->pusbdev;
+
+ /* translate DMA FIFO addr to pipehandle */
+ pipe = usb_rcvintpipe(pusbd, pdvobj->RtInPipe[1]);
+
+ usb_fill_int_urb(precvpriv->int_in_urb, pusbd, pipe,
+ precvpriv->int_in_buf, USB_INTR_CONTENT_LENGTH,
+ usb_read_interrupt_complete, adapter, 1);
+
+ err = usb_submit_urb(precvpriv->int_in_urb, GFP_ATOMIC);
+ if (err && (err != -EPERM)) {
+ DBG_8723A("cannot submit interrupt in-token(err = 0x%08x),"
+ "urb_status = %d\n", err,
+ precvpriv->int_in_urb->status);
+ ret = _FAIL;
+ }
+
+ return ret;
+}
+
+static int recvbuf2recvframe(struct rtw_adapter *padapter, struct sk_buff *pskb)
+{
+ u8 *pbuf;
+ u8 shift_sz = 0;
+ u16 pkt_cnt;
+ u32 pkt_offset, skb_len, alloc_sz;
+ int transfer_len;
+ struct recv_stat *prxstat;
+ struct phy_stat *pphy_info;
+ struct sk_buff *pkt_copy;
+ struct recv_frame *precvframe;
+ struct rx_pkt_attrib *pattrib;
+ struct recv_priv *precvpriv = &padapter->recvpriv;
+ struct rtw_queue *pfree_recv_queue = &precvpriv->free_recv_queue;
+
+ transfer_len = (int)pskb->len;
+ pbuf = pskb->data;
+
+ prxstat = (struct recv_stat *)pbuf;
+ pkt_cnt = (le32_to_cpu(prxstat->rxdw2) >> 16) & 0xff;
+
+ do {
+ RT_TRACE(_module_rtl871x_recv_c_, _drv_info_,
+ "recvbuf2recvframe: rxdesc = offsset 0:0x%08x, 4:0x%08x, 8:0x%08x, C:0x%08x\n",
+ prxstat->rxdw0, prxstat->rxdw1,
+ prxstat->rxdw2, prxstat->rxdw4);
+
+ prxstat = (struct recv_stat *)pbuf;
+
+ precvframe = rtw_alloc_recvframe23a(pfree_recv_queue);
+ if (!precvframe) {
+ RT_TRACE(_module_rtl871x_recv_c_, _drv_err_,
+ "recvbuf2recvframe: precvframe == NULL\n");
+ DBG_8723A("%s()-%d: rtw_alloc_recvframe23a() failed! RX "
+ "Drop!\n", __func__, __LINE__);
+ goto _exit_recvbuf2recvframe;
+ }
+
+ INIT_LIST_HEAD(&precvframe->list);
+
+ update_recvframe_attrib(precvframe, prxstat);
+
+ pattrib = &precvframe->attrib;
+
+ if (pattrib->crc_err) {
+ DBG_8723A("%s()-%d: RX Warning! rx CRC ERROR !!\n",
+ __func__, __LINE__);
+ rtw_free_recvframe23a(precvframe);
+ goto _exit_recvbuf2recvframe;
+ }
+
+ pkt_offset = RXDESC_SIZE + pattrib->drvinfo_sz +
+ pattrib->shift_sz + pattrib->pkt_len;
+
+ if (pattrib->pkt_len <= 0 || pkt_offset > transfer_len) {
+ RT_TRACE(_module_rtl871x_recv_c_, _drv_info_,
+ "recvbuf2recvframe: pkt_len<= 0\n");
+ DBG_8723A("%s()-%d: RX Warning!\n",
+ __func__, __LINE__);
+ rtw_free_recvframe23a(precvframe);
+ goto _exit_recvbuf2recvframe;
+ }
+
+ /* Modified by Albert 20101213 */
+ /* For 8 bytes IP header alignment. */
+ /* Qos data, wireless lan header length is 26 */
+ if (pattrib->qos)
+ shift_sz = 6;
+ else
+ shift_sz = 0;
+
+ skb_len = pattrib->pkt_len;
+
+ /* for first fragment packet, driver need allocate
+ * 1536+drvinfo_sz+RXDESC_SIZE to defrag packet.
+ * modify alloc_sz for recvive crc error packet
+ * by thomas 2011-06-02 */
+ if (pattrib->mfrag == 1 && pattrib->frag_num == 0) {
+ /* alloc_sz = 1664; 1664 is 128 alignment. */
+ if (skb_len <= 1650)
+ alloc_sz = 1664;
+ else
+ alloc_sz = skb_len + 14;
+ } else {
+ alloc_sz = skb_len;
+ /* 6 is for IP header 8 bytes alignment in QoS packet case. */
+ /* 8 is for skb->data 4 bytes alignment. */
+ alloc_sz += 14;
+ }
+
+ pkt_copy = netdev_alloc_skb(padapter->pnetdev, alloc_sz);
+ if (pkt_copy) {
+ pkt_copy->dev = padapter->pnetdev;
+ precvframe->pkt = pkt_copy;
+ /* force pkt_copy->data at 8-byte alignment address */
+ skb_reserve(pkt_copy, 8 -
+ ((unsigned long)(pkt_copy->data) & 7));
+ /*force ip_hdr at 8-byte alignment address
+ according to shift_sz. */
+ skb_reserve(pkt_copy, shift_sz);
+ memcpy(pkt_copy->data, pbuf + pattrib->shift_sz +
+ pattrib->drvinfo_sz + RXDESC_SIZE, skb_len);
+ skb_put(pkt_copy, skb_len);
+ } else {
+ if (pattrib->mfrag == 1 && pattrib->frag_num == 0) {
+ DBG_8723A("recvbuf2recvframe: alloc_skb fail, "
+ "drop frag frame \n");
+ rtw_free_recvframe23a(precvframe);
+ goto _exit_recvbuf2recvframe;
+ }
+
+ precvframe->pkt = skb_clone(pskb, GFP_ATOMIC);
+ if (!precvframe->pkt) {
+ DBG_8723A("recvbuf2recvframe: skb_clone "
+ "fail\n");
+ rtw_free_recvframe23a(precvframe);
+ goto _exit_recvbuf2recvframe;
+ }
+ }
+
+ if (pattrib->physt) {
+ pphy_info = (struct phy_stat *)(pbuf + RXDESC_OFFSET);
+ update_recvframe_phyinfo(precvframe, pphy_info);
+ }
+
+ if (rtw_recv_entry23a(precvframe) != _SUCCESS)
+ RT_TRACE(_module_rtl871x_recv_c_, _drv_err_,
+ "recvbuf2recvframe: rtw_recv_entry23a(precvframe) != _SUCCESS\n");
+
+ pkt_cnt--;
+ transfer_len -= pkt_offset;
+ pbuf += pkt_offset;
+ precvframe = NULL;
+ pkt_copy = NULL;
+
+ if (transfer_len > 0 && pkt_cnt == 0)
+ pkt_cnt = (le32_to_cpu(prxstat->rxdw2)>>16) & 0xff;
+
+ } while (transfer_len > 0 && pkt_cnt > 0);
+
+_exit_recvbuf2recvframe:
+
+ return _SUCCESS;
+}
+
+void rtl8723au_recv_tasklet(void *priv)
+{
+ struct sk_buff *pskb;
+ struct rtw_adapter *padapter = (struct rtw_adapter *)priv;
+ struct recv_priv *precvpriv = &padapter->recvpriv;
+
+ while (NULL != (pskb = skb_dequeue(&precvpriv->rx_skb_queue))) {
+ if (padapter->bDriverStopped || padapter->bSurpriseRemoved) {
+ DBG_8723A("recv_tasklet => bDriverStopped or "
+ "bSurpriseRemoved \n");
+ dev_kfree_skb_any(pskb);
+ break;
+ }
+
+ recvbuf2recvframe(padapter, pskb);
+ skb_reset_tail_pointer(pskb);
+
+ pskb->len = 0;
+
+ skb_queue_tail(&precvpriv->free_recv_skb_queue, pskb);
+ }
+}
+
+static void usb_read_port_complete(struct urb *purb)
+{
+ struct recv_buf *precvbuf = (struct recv_buf *)purb->context;
+ struct rtw_adapter *padapter = (struct rtw_adapter *)precvbuf->adapter;
+ struct recv_priv *precvpriv = &padapter->recvpriv;
+
+ RT_TRACE(_module_hci_ops_os_c_, _drv_err_,
+ "usb_read_port_complete!!!\n");
+
+ precvpriv->rx_pending_cnt--;
+
+ if (padapter->bSurpriseRemoved || padapter->bDriverStopped ||
+ padapter->bReadPortCancel) {
+ RT_TRACE(_module_hci_ops_os_c_, _drv_err_,
+ "usb_read_port_complete:bDriverStopped(%d) OR bSurpriseRemoved(%d)\n",
+ padapter->bDriverStopped, padapter->bSurpriseRemoved);
+
+ DBG_8723A("%s()-%d: RX Warning! bDriverStopped(%d) OR "
+ "bSurpriseRemoved(%d) bReadPortCancel(%d)\n",
+ __func__, __LINE__, padapter->bDriverStopped,
+ padapter->bSurpriseRemoved, padapter->bReadPortCancel);
+ return;
+ }
+
+ if (purb->status == 0) {
+ if (purb->actual_length > MAX_RECVBUF_SZ ||
+ purb->actual_length < RXDESC_SIZE) {
+ RT_TRACE(_module_hci_ops_os_c_, _drv_err_,
+ "usb_read_port_complete: (purb->actual_length > MAX_RECVBUF_SZ) || (purb->actual_length < RXDESC_SIZE)\n");
+ rtl8723au_read_port(padapter, 0, precvbuf);
+ DBG_8723A("%s()-%d: RX Warning!\n",
+ __func__, __LINE__);
+ } else {
+ rtw_reset_continual_urb_error(
+ adapter_to_dvobj(padapter));
+
+ skb_put(precvbuf->pskb, purb->actual_length);
+ skb_queue_tail(&precvpriv->rx_skb_queue,
+ precvbuf->pskb);
+
+ if (skb_queue_len(&precvpriv->rx_skb_queue) <= 1)
+ tasklet_schedule(&precvpriv->recv_tasklet);
+
+ precvbuf->pskb = NULL;
+ rtl8723au_read_port(padapter, 0, precvbuf);
+ }
+ } else {
+ RT_TRACE(_module_hci_ops_os_c_, _drv_err_,
+ "usb_read_port_complete : purb->status(%d) != 0\n",
+ purb->status);
+ skb_put(precvbuf->pskb, purb->actual_length);
+ precvbuf->pskb = NULL;
+
+ DBG_8723A("###=> usb_read_port_complete => urb status(%d)\n",
+ purb->status);
+
+ if (rtw_inc_and_chk_continual_urb_error(
+ adapter_to_dvobj(padapter))) {
+ padapter->bSurpriseRemoved = true;
+ }
+
+ switch (purb->status) {
+ case -EINVAL:
+ case -EPIPE:
+ case -ENODEV:
+ case -ESHUTDOWN:
+ RT_TRACE(_module_hci_ops_os_c_, _drv_err_,
+ "usb_read_port_complete:bSurpriseRemoved = true\n");
+ /* Intentional fall through here */
+ case -ENOENT:
+ padapter->bDriverStopped = true;
+ RT_TRACE(_module_hci_ops_os_c_, _drv_err_,
+ "usb_read_port_complete:bDriverStopped = true\n");
+ break;
+ case -EPROTO:
+ case -EOVERFLOW:
+ rtl8723au_read_port(padapter, 0, precvbuf);
+ break;
+ case -EINPROGRESS:
+ DBG_8723A("ERROR: URB IS IN PROGRESS!\n");
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+int rtl8723au_read_port(struct rtw_adapter *adapter, u32 cnt,
+ struct recv_buf *precvbuf)
+{
+ struct urb *purb;
+ struct dvobj_priv *pdvobj = adapter_to_dvobj(adapter);
+ struct recv_priv *precvpriv = &adapter->recvpriv;
+ struct usb_device *pusbd = pdvobj->pusbdev;
+ int err;
+ unsigned int pipe;
+ unsigned long tmpaddr;
+ unsigned long alignment;
+ int ret = _SUCCESS;
+
+ if (adapter->bDriverStopped || adapter->bSurpriseRemoved) {
+ RT_TRACE(_module_hci_ops_os_c_, _drv_err_,
+ "usb_read_port:(padapter->bDriverStopped ||padapter->bSurpriseRemoved)!!!\n");
+ return _FAIL;
+ }
+
+ if (!precvbuf) {
+ RT_TRACE(_module_hci_ops_os_c_, _drv_err_,
+ "usb_read_port:precvbuf == NULL\n");
+ return _FAIL;
+ }
+
+ if (!precvbuf->pskb)
+ precvbuf->pskb = skb_dequeue(&precvpriv->free_recv_skb_queue);
+
+ /* re-assign for linux based on skb */
+ if (!precvbuf->pskb) {
+ precvbuf->pskb = netdev_alloc_skb(adapter->pnetdev, MAX_RECVBUF_SZ + RECVBUFF_ALIGN_SZ);
+ if (precvbuf->pskb == NULL) {
+ RT_TRACE(_module_hci_ops_os_c_, _drv_err_,
+ "init_recvbuf(): alloc_skb fail!\n");
+ return _FAIL;
+ }
+
+ tmpaddr = (unsigned long)precvbuf->pskb->data;
+ alignment = tmpaddr & (RECVBUFF_ALIGN_SZ-1);
+ skb_reserve(precvbuf->pskb, (RECVBUFF_ALIGN_SZ - alignment));
+ }
+
+ precvpriv->rx_pending_cnt++;
+
+ purb = precvbuf->purb;
+
+ /* translate DMA FIFO addr to pipehandle */
+ pipe = usb_rcvbulkpipe(pusbd, pdvobj->RtInPipe[0]);
+
+ usb_fill_bulk_urb(purb, pusbd, pipe, precvbuf->pskb->data,
+ MAX_RECVBUF_SZ, usb_read_port_complete,
+ precvbuf);/* context is precvbuf */
+
+ err = usb_submit_urb(purb, GFP_ATOMIC);
+ if ((err) && (err != -EPERM)) {
+ RT_TRACE(_module_hci_ops_os_c_, _drv_err_,
+ "cannot submit rx in-token(err = 0x%.8x), URB_STATUS = 0x%.8x\n",
+ err, purb->status);
+ DBG_8723A("cannot submit rx in-token(err = 0x%08x), urb_status "
+ "= %d\n", err, purb->status);
+ ret = _FAIL;
+ }
+ return ret;
+}
+
+void rtl8723au_xmit_tasklet(void *priv)
+{
+ int ret;
+ struct rtw_adapter *padapter = (struct rtw_adapter *)priv;
+ struct xmit_priv *pxmitpriv = &padapter->xmitpriv;
+
+ if (check_fwstate(&padapter->mlmepriv, _FW_UNDER_SURVEY))
+ return;
+
+ while (1) {
+ if (padapter->bDriverStopped || padapter->bSurpriseRemoved ||
+ padapter->bWritePortCancel) {
+ DBG_8723A("xmit_tasklet => bDriverStopped or "
+ "bSurpriseRemoved or bWritePortCancel\n");
+ break;
+ }
+
+ ret = rtl8723au_xmitframe_complete(padapter, pxmitpriv, NULL);
+
+ if (!ret)
+ break;
+ }
+}
+
+void rtl8723au_set_hw_type(struct rtw_adapter *padapter)
+{
+ padapter->chip_type = RTL8723A;
+ padapter->HardwareType = HARDWARE_TYPE_RTL8723AU;
+ DBG_8723A("CHIP TYPE: RTL8723A\n");
+}
diff --git a/drivers/staging/rtl8723au/include/Hal8723APhyCfg.h b/drivers/staging/rtl8723au/include/Hal8723APhyCfg.h
new file mode 100644
index 000000000..bcf36579f
--- /dev/null
+++ b/drivers/staging/rtl8723au/include/Hal8723APhyCfg.h
@@ -0,0 +1,162 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2011 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * 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.
+ *
+ *
+ ******************************************************************************/
+#ifndef __INC_HAL8723PHYCFG_H__
+#define __INC_HAL8723PHYCFG_H__
+
+/*------------------------------Define structure----------------------------*/
+enum RF_RADIO_PATH {
+ RF_PATH_A = 0, /* Radio Path A */
+ RF_PATH_B = 1, /* Radio Path B */
+ RF_PATH_MAX /* Max RF number 90 support */
+};
+
+#define CHANNEL_MAX_NUMBER 14 /* 14 is the max channel number */
+
+enum WIRELESS_MODE {
+ WIRELESS_MODE_UNKNOWN = 0x00,
+ WIRELESS_MODE_A = BIT(2),
+ WIRELESS_MODE_B = BIT(0),
+ WIRELESS_MODE_G = BIT(1),
+ WIRELESS_MODE_AUTO = BIT(5),
+ WIRELESS_MODE_N_24G = BIT(3),
+ WIRELESS_MODE_N_5G = BIT(4),
+ WIRELESS_MODE_AC = BIT(6)
+};
+
+struct bb_reg_define {
+ u32 rfintfs; /* set software control: */
+ /* 0x870~0x877[8 bytes] */
+ u32 rfintfi; /* readback data: */
+ /* 0x8e0~0x8e7[8 bytes] */
+ u32 rfintfo; /* output data: */
+ /* 0x860~0x86f [16 bytes] */
+ u32 rfintfe; /* output enable: */
+ /* 0x860~0x86f [16 bytes] */
+ u32 rf3wireOffset; /* LSSI data: */
+ /* 0x840~0x84f [16 bytes] */
+ u32 rfLSSI_Select; /* BB Band Select: */
+ /* 0x878~0x87f [8 bytes] */
+ u32 rfTxGainStage; /* Tx gain stage: */
+ /* 0x80c~0x80f [4 bytes] */
+ u32 rfHSSIPara1; /* wire parameter control1 : */
+ /* 0x820~0x823, 0x828~0x82b, 0x830~0x833, 0x838~0x83b [16 bytes] */
+ u32 rfHSSIPara2; /* wire parameter control2 : */
+ /* 0x824~0x827, 0x82c~0x82f, 0x834~0x837, 0x83c~0x83f [16 bytes] */
+ u32 rfSwitchControl; /* Tx Rx antenna control : */
+ /* 0x858~0x85f [16 bytes] */
+ u32 rfAGCControl1; /* AGC parameter control1 : */
+ /* 0xc50~0xc53, 0xc58~0xc5b, 0xc60~0xc63, 0xc68~0xc6b [16 bytes] */
+ u32 rfAGCControl2; /* AGC parameter control2 : */
+ /* 0xc54~0xc57, 0xc5c~0xc5f, 0xc64~0xc67, 0xc6c~0xc6f [16 bytes] */
+ u32 rfRxIQImbalance; /* OFDM Rx IQ imbalance matrix : */
+ /* 0xc14~0xc17, 0xc1c~0xc1f, 0xc24~0xc27, 0xc2c~0xc2f [16 bytes] */
+ u32 rfRxAFE; /* Rx IQ DC ofset and Rx digital filter, Rx DC notch filter : */
+ /* 0xc10~0xc13, 0xc18~0xc1b, 0xc20~0xc23, 0xc28~0xc2b [16 bytes] */
+ u32 rfTxIQImbalance; /* OFDM Tx IQ imbalance matrix */
+ /* 0xc80~0xc83, 0xc88~0xc8b, 0xc90~0xc93, 0xc98~0xc9b [16 bytes] */
+ u32 rfTxAFE; /* Tx IQ DC Offset and Tx DFIR type */
+ /* 0xc84~0xc87, 0xc8c~0xc8f, 0xc94~0xc97, 0xc9c~0xc9f [16 bytes] */
+ u32 rfLSSIReadBack; /* LSSI RF readback data SI mode */
+ /* 0x8a0~0x8af [16 bytes] */
+ u32 rfLSSIReadBackPi; /* LSSI RF readback data PI mode 0x8b8-8bc for Path A and B */
+};
+
+struct r_antenna_sel_ofdm {
+ u32 r_tx_antenna:4;
+ u32 r_ant_l:4;
+ u32 r_ant_non_ht:4;
+ u32 r_ant_ht1:4;
+ u32 r_ant_ht2:4;
+ u32 r_ant_ht_s1:4;
+ u32 r_ant_non_ht_s1:4;
+ u32 OFDM_TXSC:2;
+ u32 Reserved:2;
+};
+
+struct r_antenna_sel_cck {
+ u8 r_cckrx_enable_2:2;
+ u8 r_cckrx_enable:2;
+ u8 r_ccktx_enable:4;
+};
+
+/*------------------------------Define structure----------------------------*/
+
+
+/*------------------------Export global variable----------------------------*/
+/*------------------------Export global variable----------------------------*/
+
+
+/*------------------------Export Macro Definition---------------------------*/
+/*------------------------Export Macro Definition---------------------------*/
+
+
+/*--------------------------Exported Function prototype---------------------*/
+/* */
+/* BB and RF register read/write */
+/* */
+u32 PHY_QueryBBReg(struct rtw_adapter *Adapter, u32 RegAddr,
+ u32 BitMask);
+void PHY_SetBBReg(struct rtw_adapter *Adapter, u32 RegAddr,
+ u32 BitMask, u32 Data);
+u32 PHY_QueryRFReg(struct rtw_adapter *Adapter,
+ enum RF_RADIO_PATH eRFPath, u32 RegAddr,
+ u32 BitMask);
+void PHY_SetRFReg(struct rtw_adapter *Adapter,
+ enum RF_RADIO_PATH eRFPath, u32 RegAddr,
+ u32 BitMask, u32 Data);
+
+/* */
+/* BB TX Power R/W */
+/* */
+void PHY_SetTxPowerLevel8723A(struct rtw_adapter *Adapter, u8 channel);
+
+/* */
+/* Switch bandwidth for 8723A */
+/* */
+void PHY_SetBWMode23a8723A(struct rtw_adapter *pAdapter,
+ enum ht_channel_width ChnlWidth,
+ unsigned char Offset);
+
+/* */
+/* channel switch related funciton */
+/* */
+void PHY_SwChnl8723A(struct rtw_adapter *pAdapter, u8 channel);
+ /* Call after initialization */
+void ChkFwCmdIoDone(struct rtw_adapter *Adapter);
+
+/* */
+/* Modify the value of the hw register when beacon interval be changed. */
+/* */
+void
+rtl8192c_PHY_SetBeaconHwReg(struct rtw_adapter *Adapter, u16 BeaconInterval);
+
+
+void PHY_SwitchEphyParameter(struct rtw_adapter *Adapter);
+
+void PHY_EnableHostClkReq(struct rtw_adapter *Adapter);
+
+bool
+SetAntennaConfig92C(struct rtw_adapter *Adapter, u8 DefaultAnt);
+
+/*--------------------------Exported Function prototype---------------------*/
+
+#define PHY_SetMacReg PHY_SetBBReg
+
+/* MAC/BB/RF HAL config */
+int PHY_BBConfig8723A(struct rtw_adapter *Adapter);
+s32 PHY_MACConfig8723A(struct rtw_adapter *padapter);
+
+#endif
diff --git a/drivers/staging/rtl8723au/include/Hal8723APhyReg.h b/drivers/staging/rtl8723au/include/Hal8723APhyReg.h
new file mode 100644
index 000000000..759928f78
--- /dev/null
+++ b/drivers/staging/rtl8723au/include/Hal8723APhyReg.h
@@ -0,0 +1,1078 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2011 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * 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.
+ *
+ *
+ ******************************************************************************/
+#ifndef __INC_HAL8723APHYREG_H__
+#define __INC_HAL8723APHYREG_H__
+
+/* 1. PMAC duplicate register due to connection: RF_Mode, TRxRN, NumOf L-STF */
+/* 1. Page1(0x100) */
+#define rPMAC_Reset 0x100
+#define rPMAC_TxStart 0x104
+#define rPMAC_TxLegacySIG 0x108
+#define rPMAC_TxHTSIG1 0x10c
+#define rPMAC_TxHTSIG2 0x110
+#define rPMAC_PHYDebug 0x114
+#define rPMAC_TxPacketNum 0x118
+#define rPMAC_TxIdle 0x11c
+#define rPMAC_TxMACHeader0 0x120
+#define rPMAC_TxMACHeader1 0x124
+#define rPMAC_TxMACHeader2 0x128
+#define rPMAC_TxMACHeader3 0x12c
+#define rPMAC_TxMACHeader4 0x130
+#define rPMAC_TxMACHeader5 0x134
+#define rPMAC_TxDataType 0x138
+#define rPMAC_TxRandomSeed 0x13c
+#define rPMAC_CCKPLCPPreamble 0x140
+#define rPMAC_CCKPLCPHeader 0x144
+#define rPMAC_CCKCRC16 0x148
+#define rPMAC_OFDMRxCRC32OK 0x170
+#define rPMAC_OFDMRxCRC32Er 0x174
+#define rPMAC_OFDMRxParityEr 0x178
+#define rPMAC_OFDMRxCRC8Er 0x17c
+#define rPMAC_CCKCRxRC16Er 0x180
+#define rPMAC_CCKCRxRC32Er 0x184
+#define rPMAC_CCKCRxRC32OK 0x188
+#define rPMAC_TxStatus 0x18c
+
+/* 2. Page2(0x200) */
+/* The following two definition are only used for USB interface. */
+#define RF_BB_CMD_ADDR 0x02c0 /* RF/BB read/write command address. */
+#define RF_BB_CMD_DATA 0x02c4 /* RF/BB read/write command data. */
+
+/* 3. Page8(0x800) */
+#define rFPGA0_RFMOD 0x800 /* RF mode & CCK TxSC RF BW Setting?? */
+
+#define rFPGA0_TxInfo 0x804 /* Status report?? */
+#define rFPGA0_PSDFunction 0x808
+
+#define rFPGA0_TxGainStage 0x80c /* Set TX PWR init gain? */
+
+#define rFPGA0_RFTiming1 0x810 /* Useless now */
+#define rFPGA0_RFTiming2 0x814
+
+#define rFPGA0_XA_HSSIParameter1 0x820 /* RF 3 wire register */
+#define rFPGA0_XA_HSSIParameter2 0x824
+#define rFPGA0_XB_HSSIParameter1 0x828
+#define rFPGA0_XB_HSSIParameter2 0x82c
+#define rTxAGC_B_Rate18_06 0x830
+#define rTxAGC_B_Rate54_24 0x834
+#define rTxAGC_B_CCK1_55_Mcs32 0x838
+#define rTxAGC_B_Mcs03_Mcs00 0x83c
+
+#define rTxAGC_B_Mcs07_Mcs04 0x848
+#define rTxAGC_B_Mcs11_Mcs08 0x84c
+
+#define rFPGA0_XA_LSSIParameter 0x840
+#define rFPGA0_XB_LSSIParameter 0x844
+
+#define rFPGA0_RFWakeUpParameter 0x850 /* Useless now */
+#define rFPGA0_RFSleepUpParameter 0x854
+
+#define rFPGA0_XAB_SwitchControl 0x858 /* RF Channel switch */
+#define rFPGA0_XCD_SwitchControl 0x85c
+
+#define rFPGA0_XA_RFInterfaceOE 0x860 /* RF Channel switch */
+#define rFPGA0_XB_RFInterfaceOE 0x864
+
+#define rTxAGC_B_Mcs15_Mcs12 0x868
+#define rTxAGC_B_CCK11_A_CCK2_11 0x86c
+
+#define rFPGA0_XAB_RFInterfaceSW 0x870 /* RF Interface Software Control */
+#define rFPGA0_XCD_RFInterfaceSW 0x874
+
+#define rFPGA0_XAB_RFParameter 0x878 /* RF Parameter */
+#define rFPGA0_XCD_RFParameter 0x87c
+
+#define rFPGA0_AnalogParameter1 0x880 /* Crystal cap setting RF-R/W protection for parameter4?? */
+#define rFPGA0_AnalogParameter2 0x884
+#define rFPGA0_AnalogParameter3 0x888 /* Useless now */
+#define rFPGA0_AnalogParameter4 0x88c
+
+#define rFPGA0_XA_LSSIReadBack 0x8a0 /* Tranceiver LSSI Readback */
+#define rFPGA0_XB_LSSIReadBack 0x8a4
+#define rFPGA0_XC_LSSIReadBack 0x8a8
+#define rFPGA0_XD_LSSIReadBack 0x8ac
+
+#define rFPGA0_PSDReport 0x8b4 /* Useless now */
+#define TransceiverA_HSPI_Readback 0x8b8 /* Transceiver A HSPI Readback */
+#define TransceiverB_HSPI_Readback 0x8bc /* Transceiver B HSPI Readback */
+#define rFPGA0_XAB_RFInterfaceRB 0x8e0 /* Useless now RF Interface Readback Value */
+#define rFPGA0_XCD_RFInterfaceRB 0x8e4 /* Useless now */
+
+/* 4. Page9(0x900) */
+#define rFPGA1_RFMOD 0x900 /* RF mode & OFDM TxSC RF BW Setting?? */
+
+#define rFPGA1_TxBlock 0x904 /* Useless now */
+#define rFPGA1_DebugSelect 0x908 /* Useless now */
+#define rFPGA1_TxInfo 0x90c /* Useless now Status report?? */
+
+/* 5. PageA(0xA00) */
+/* Set Control channel to upper or lower. These settings are required only for 40MHz */
+#define rCCK0_System 0xa00
+
+#define rCCK0_AFESetting 0xa04 /* Disable init gain now Select RX path by RSSI */
+#define rCCK0_CCA 0xa08 /* Disable init gain now Init gain */
+
+#define rCCK0_RxAGC1 0xa0c /* AGC default value, saturation level Antenna Diversity, RX AGC, LNA Threshold, RX LNA Threshold useless now. Not the same as 90 series */
+#define rCCK0_RxAGC2 0xa10 /* AGC & DAGC */
+
+#define rCCK0_RxHP 0xa14
+
+#define rCCK0_DSPParameter1 0xa18 /* Timing recovery & Channel estimation threshold */
+#define rCCK0_DSPParameter2 0xa1c /* SQ threshold */
+
+#define rCCK0_TxFilter1 0xa20
+#define rCCK0_TxFilter2 0xa24
+#define rCCK0_DebugPort 0xa28 /* debug port and Tx filter3 */
+#define rCCK0_FalseAlarmReport 0xa2c /* 0xa2d useless now 0xa30-a4f channel report */
+#define rCCK0_TRSSIReport 0xa50
+#define rCCK0_RxReport 0xa54 /* 0xa57 */
+#define rCCK0_FACounterLower 0xa5c /* 0xa5b */
+#define rCCK0_FACounterUpper 0xa58 /* 0xa5c */
+/* PageB(0xB00) */
+#define rPdp_AntA 0xb00
+#define rPdp_AntA_4 0xb04
+#define rConfig_Pmpd_AntA 0xb28
+#define rConfig_AntA 0xb68
+#define rConfig_AntB 0xb6c
+#define rPdp_AntB 0xb70
+#define rPdp_AntB_4 0xb74
+#define rConfig_Pmpd_AntB 0xb98
+#define rAPK 0xbd8
+
+/* 6. PageC(0xC00) */
+#define rOFDM0_LSTF 0xc00
+
+#define rOFDM0_TRxPathEnable 0xc04
+#define rOFDM0_TRMuxPar 0xc08
+#define rOFDM0_TRSWIsolation 0xc0c
+
+#define rOFDM0_XARxAFE 0xc10 /* RxIQ DC offset, Rx digital filter, DC notch filter */
+#define rOFDM0_XARxIQImbalance 0xc14 /* RxIQ imblance matrix */
+#define rOFDM0_XBRxAFE 0xc18
+#define rOFDM0_XBRxIQImbalance 0xc1c
+#define rOFDM0_XCRxAFE 0xc20
+#define rOFDM0_XCRxIQImbalance 0xc24
+#define rOFDM0_XDRxAFE 0xc28
+#define rOFDM0_XDRxIQImbalance 0xc2c
+
+#define rOFDM0_RxDetector1 0xc30 /* PD,BW & SBD DM tune init gain */
+#define rOFDM0_RxDetector2 0xc34 /* SBD & Fame Sync. */
+#define rOFDM0_RxDetector3 0xc38 /* Frame Sync. */
+#define rOFDM0_RxDetector4 0xc3c /* PD, SBD, Frame Sync & Short-GI */
+
+#define rOFDM0_RxDSP 0xc40 /* Rx Sync Path */
+#define rOFDM0_CFOandDAGC 0xc44 /* CFO & DAGC */
+#define rOFDM0_CCADropThreshold 0xc48 /* CCA Drop threshold */
+#define rOFDM0_ECCAThreshold 0xc4c /* energy CCA */
+
+#define rOFDM0_XAAGCCore1 0xc50 /* DIG */
+#define rOFDM0_XAAGCCore2 0xc54
+#define rOFDM0_XBAGCCore1 0xc58
+#define rOFDM0_XBAGCCore2 0xc5c
+#define rOFDM0_XCAGCCore1 0xc60
+#define rOFDM0_XCAGCCore2 0xc64
+#define rOFDM0_XDAGCCore1 0xc68
+#define rOFDM0_XDAGCCore2 0xc6c
+
+#define rOFDM0_AGCParameter1 0xc70
+#define rOFDM0_AGCParameter2 0xc74
+#define rOFDM0_AGCRSSITable 0xc78
+#define rOFDM0_HTSTFAGC 0xc7c
+
+#define rOFDM0_XATxIQImbalance 0xc80 /* TX PWR TRACK and DIG */
+#define rOFDM0_XATxAFE 0xc84
+#define rOFDM0_XBTxIQImbalance 0xc88
+#define rOFDM0_XBTxAFE 0xc8c
+#define rOFDM0_XCTxIQImbalance 0xc90
+#define rOFDM0_XCTxAFE 0xc94
+#define rOFDM0_XDTxIQImbalance 0xc98
+#define rOFDM0_XDTxAFE 0xc9c
+
+#define rOFDM0_RxIQExtAnta 0xca0
+#define rOFDM0_TxCoeff1 0xca4
+#define rOFDM0_TxCoeff2 0xca8
+#define rOFDM0_TxCoeff3 0xcac
+#define rOFDM0_TxCoeff4 0xcb0
+#define rOFDM0_TxCoeff5 0xcb4
+#define rOFDM0_TxCoeff6 0xcb8
+#define rOFDM0_RxHPParameter 0xce0
+#define rOFDM0_TxPseudoNoiseWgt 0xce4
+#define rOFDM0_FrameSync 0xcf0
+#define rOFDM0_DFSReport 0xcf4
+
+/* 7. PageD(0xD00) */
+#define rOFDM1_LSTF 0xd00
+#define rOFDM1_TRxPathEnable 0xd04
+
+#define rOFDM1_CFO 0xd08 /* No setting now */
+#define rOFDM1_CSI1 0xd10
+#define rOFDM1_SBD 0xd14
+#define rOFDM1_CSI2 0xd18
+#define rOFDM1_CFOTracking 0xd2c
+#define rOFDM1_TRxMesaure1 0xd34
+#define rOFDM1_IntfDet 0xd3c
+#define rOFDM1_PseudoNoiseStateAB 0xd50
+#define rOFDM1_PseudoNoiseStateCD 0xd54
+#define rOFDM1_RxPseudoNoiseWgt 0xd58
+
+#define rOFDM_PHYCounter1 0xda0 /* cca, parity fail */
+#define rOFDM_PHYCounter2 0xda4 /* rate illegal, crc8 fail */
+#define rOFDM_PHYCounter3 0xda8 /* MCS not support */
+
+#define rOFDM_ShortCFOAB 0xdac /* No setting now */
+#define rOFDM_ShortCFOCD 0xdb0
+#define rOFDM_LongCFOAB 0xdb4
+#define rOFDM_LongCFOCD 0xdb8
+#define rOFDM_TailCFOAB 0xdbc
+#define rOFDM_TailCFOCD 0xdc0
+#define rOFDM_PWMeasure1 0xdc4
+#define rOFDM_PWMeasure2 0xdc8
+#define rOFDM_BWReport 0xdcc
+#define rOFDM_AGCReport 0xdd0
+#define rOFDM_RxSNR 0xdd4
+#define rOFDM_RxEVMCSI 0xdd8
+#define rOFDM_SIGReport 0xddc
+
+
+/* 8. PageE(0xE00) */
+#define rTxAGC_A_Rate18_06 0xe00
+#define rTxAGC_A_Rate54_24 0xe04
+#define rTxAGC_A_CCK1_Mcs32 0xe08
+#define rTxAGC_A_Mcs03_Mcs00 0xe10
+#define rTxAGC_A_Mcs07_Mcs04 0xe14
+#define rTxAGC_A_Mcs11_Mcs08 0xe18
+#define rTxAGC_A_Mcs15_Mcs12 0xe1c
+
+#define rFPGA0_IQK 0xe28
+#define rTx_IQK_Tone_A 0xe30
+#define rRx_IQK_Tone_A 0xe34
+#define rTx_IQK_PI_A 0xe38
+#define rRx_IQK_PI_A 0xe3c
+
+#define rTx_IQK 0xe40
+#define rRx_IQK 0xe44
+#define rIQK_AGC_Pts 0xe48
+#define rIQK_AGC_Rsp 0xe4c
+#define rTx_IQK_Tone_B 0xe50
+#define rRx_IQK_Tone_B 0xe54
+#define rTx_IQK_PI_B 0xe58
+#define rRx_IQK_PI_B 0xe5c
+#define rIQK_AGC_Cont 0xe60
+
+#define rBlue_Tooth 0xe6c
+#define rRx_Wait_CCA 0xe70
+#define rTx_CCK_RFON 0xe74
+#define rTx_CCK_BBON 0xe78
+#define rTx_OFDM_RFON 0xe7c
+#define rTx_OFDM_BBON 0xe80
+#define rTx_To_Rx 0xe84
+#define rTx_To_Tx 0xe88
+#define rRx_CCK 0xe8c
+
+#define rTx_Power_Before_IQK_A 0xe94
+#define rTx_Power_After_IQK_A 0xe9c
+
+#define rRx_Power_Before_IQK_A 0xea0
+#define rRx_Power_Before_IQK_A_2 0xea4
+#define rRx_Power_After_IQK_A 0xea8
+#define rRx_Power_After_IQK_A_2 0xeac
+
+#define rTx_Power_Before_IQK_B 0xeb4
+#define rTx_Power_After_IQK_B 0xebc
+
+#define rRx_Power_Before_IQK_B 0xec0
+#define rRx_Power_Before_IQK_B_2 0xec4
+#define rRx_Power_After_IQK_B 0xec8
+#define rRx_Power_After_IQK_B_2 0xecc
+
+#define rRx_OFDM 0xed0
+#define rRx_Wait_RIFS 0xed4
+#define rRx_TO_Rx 0xed8
+#define rStandby 0xedc
+#define rSleep 0xee0
+#define rPMPD_ANAEN 0xeec
+
+/* 7. RF Register 0x00-0x2E (RF 8256) */
+/* RF-0222D 0x00-3F */
+/* Zebra1 */
+#define rZebra1_HSSIEnable 0x0 /* Useless now */
+#define rZebra1_TRxEnable1 0x1
+#define rZebra1_TRxEnable2 0x2
+#define rZebra1_AGC 0x4
+#define rZebra1_ChargePump 0x5
+#define rZebra1_Channel 0x7 /* RF channel switch */
+
+#define rZebra1_TxGain 0x8 /* Useless now */
+#define rZebra1_TxLPF 0x9
+#define rZebra1_RxLPF 0xb
+#define rZebra1_RxHPFCorner 0xc
+
+/* Zebra4 */
+#define rGlobalCtrl 0 /* Useless now */
+#define rRTL8256_TxLPF 19
+#define rRTL8256_RxLPF 11
+
+/* RTL8258 */
+#define rRTL8258_TxLPF 0x11 /* Useless now */
+#define rRTL8258_RxLPF 0x13
+#define rRTL8258_RSSILPF 0xa
+
+/* RL6052 Register definition */
+#define RF_AC 0x00
+#define RF_IQADJ_G1 0x01
+#define RF_IQADJ_G2 0x02
+#define RF_BS_PA_APSET_G1_G4 0x03
+#define RF_BS_PA_APSET_G5_G8 0x04
+#define RF_POW_TRSW 0x05
+#define RF_GAIN_RX 0x06
+#define RF_GAIN_TX 0x07
+#define RF_TXM_IDAC 0x08
+#define RF_IPA_G 0x09
+#define RF_TXBIAS_G 0x0A
+#define RF_TXPA_AG 0x0B
+#define RF_IPA_A 0x0C
+#define RF_TXBIAS_A 0x0D
+#define RF_BS_PA_APSET_G9_G11 0x0E
+#define RF_BS_IQGEN 0x0F
+#define RF_MODE1 0x10
+#define RF_MODE2 0x11
+#define RF_RX_AGC_HP 0x12
+#define RF_TX_AGC 0x13
+#define RF_BIAS 0x14
+#define RF_IPA 0x15
+#define RF_TXBIAS 0x16
+#define RF_POW_ABILITY 0x17
+#define RF_MODE_AG 0x18
+#define rRfChannel 0x18 /* RF channel and BW switch */
+#define RF_CHNLBW 0x18 /* RF channel and BW switch */
+#define RF_TOP 0x19
+#define RF_RX_G1 0x1A
+#define RF_RX_G2 0x1B
+#define RF_RX_BB2 0x1C
+#define RF_RX_BB1 0x1D
+#define RF_RCK1 0x1E
+#define RF_RCK2 0x1F
+#define RF_TX_G1 0x20
+#define RF_TX_G2 0x21
+#define RF_TX_G3 0x22
+#define RF_TX_BB1 0x23
+#define RF_T_METER 0x24
+#define RF_SYN_G1 0x25 /* RF TX Power control */
+#define RF_SYN_G2 0x26 /* RF TX Power control */
+#define RF_SYN_G3 0x27 /* RF TX Power control */
+#define RF_SYN_G4 0x28 /* RF TX Power control */
+#define RF_SYN_G5 0x29 /* RF TX Power control */
+#define RF_SYN_G6 0x2A /* RF TX Power control */
+#define RF_SYN_G7 0x2B /* RF TX Power control */
+#define RF_SYN_G8 0x2C /* RF TX Power control */
+
+#define RF_RCK_OS 0x30 /* RF TX PA control */
+
+#define RF_TXPA_G1 0x31 /* RF TX PA control */
+#define RF_TXPA_G2 0x32 /* RF TX PA control */
+#define RF_TXPA_G3 0x33 /* RF TX PA control */
+
+/* Bit Mask */
+/* 1. Page1(0x100) */
+#define bBBResetB 0x100 /* Useless now? */
+#define bGlobalResetB 0x200
+#define bOFDMTxStart 0x4
+#define bCCKTxStart 0x8
+#define bCRC32Debug 0x100
+#define bPMACLoopback 0x10
+#define bTxLSIG 0xffffff
+#define bOFDMTxRate 0xf
+#define bOFDMTxReserved 0x10
+#define bOFDMTxLength 0x1ffe0
+#define bOFDMTxParity 0x20000
+#define bTxHTSIG1 0xffffff
+#define bTxHTMCSRate 0x7f
+#define bTxHTBW 0x80
+#define bTxHTLength 0xffff00
+#define bTxHTSIG2 0xffffff
+#define bTxHTSmoothing 0x1
+#define bTxHTSounding 0x2
+#define bTxHTReserved 0x4
+#define bTxHTAggreation 0x8
+#define bTxHTSTBC 0x30
+#define bTxHTAdvanceCoding 0x40
+#define bTxHTShortGI 0x80
+#define bTxHTNumberHT_LTF 0x300
+#define bTxHTCRC8 0x3fc00
+#define bCounterReset 0x10000
+#define bNumOfOFDMTx 0xffff
+#define bNumOfCCKTx 0xffff0000
+#define bTxIdleInterval 0xffff
+#define bOFDMService 0xffff0000
+#define bTxMACHeader 0xffffffff
+#define bTxDataInit 0xff
+#define bTxHTMode 0x100
+#define bTxDataType 0x30000
+#define bTxRandomSeed 0xffffffff
+#define bCCKTxPreamble 0x1
+#define bCCKTxSFD 0xffff0000
+#define bCCKTxSIG 0xff
+#define bCCKTxService 0xff00
+#define bCCKLengthExt 0x8000
+#define bCCKTxLength 0xffff0000
+#define bCCKTxCRC16 0xffff
+#define bCCKTxStatus 0x1
+#define bOFDMTxStatus 0x2
+
+#define IS_BB_REG_OFFSET_92S(_Offset) \
+ ((_Offset >= 0x800) && (_Offset <= 0xfff))
+
+/* 2. Page8(0x800) */
+#define bRFMOD 0x1 /* Reg 0x800 rFPGA0_RFMOD */
+#define bJapanMode 0x2
+#define bCCKTxSC 0x30
+#define bCCKEn 0x1000000
+#define bOFDMEn 0x2000000
+
+#define bOFDMRxADCPhase 0x10000 /* Useless now */
+#define bOFDMTxDACPhase 0x40000
+#define bXATxAGC 0x3f
+
+#define bAntennaSelect 0x0300
+
+#define bXBTxAGC 0xf00 /* Reg 80c rFPGA0_TxGainStage */
+#define bXCTxAGC 0xf000
+#define bXDTxAGC 0xf0000
+
+#define bPAStart 0xf0000000 /* Useless now */
+#define bTRStart 0x00f00000
+#define bRFStart 0x0000f000
+#define bBBStart 0x000000f0
+#define bBBCCKStart 0x0000000f
+#define bPAEnd 0xf /* Reg0x814 */
+#define bTREnd 0x0f000000
+#define bRFEnd 0x000f0000
+#define bCCAMask 0x000000f0 /* T2R */
+#define bR2RCCAMask 0x00000f00
+#define bHSSI_R2TDelay 0xf8000000
+#define bHSSI_T2RDelay 0xf80000
+#define bContTxHSSI 0x400 /* chane gain at continue Tx */
+#define bIGFromCCK 0x200
+#define bAGCAddress 0x3f
+#define bRxHPTx 0x7000
+#define bRxHPT2R 0x38000
+#define bRxHPCCKIni 0xc0000
+#define bAGCTxCode 0xc00000
+#define bAGCRxCode 0x300000
+
+#define b3WireDataLength 0x800 /* Reg 0x820~84f rFPGA0_XA_HSSIParameter1 */
+#define b3WireAddressLength 0x400
+
+#define b3WireRFPowerDown 0x1 /* Useless now */
+/* define bHWSISelect 0x8 */
+#define b5GPAPEPolarity 0x40000000
+#define b2GPAPEPolarity 0x80000000
+#define bRFSW_TxDefaultAnt 0x3
+#define bRFSW_TxOptionAnt 0x30
+#define bRFSW_RxDefaultAnt 0x300
+#define bRFSW_RxOptionAnt 0x3000
+#define bRFSI_3WireData 0x1
+#define bRFSI_3WireClock 0x2
+#define bRFSI_3WireLoad 0x4
+#define bRFSI_3WireRW 0x8
+#define bRFSI_3Wire 0xf
+
+#define bRFSI_RFENV 0x10 /* Reg 0x870 rFPGA0_XAB_RFInterfaceSW */
+
+#define bRFSI_TRSW 0x20 /* Useless now */
+#define bRFSI_TRSWB 0x40
+#define bRFSI_ANTSW 0x100
+#define bRFSI_ANTSWB 0x200
+#define bRFSI_PAPE 0x400
+#define bRFSI_PAPE5G 0x800
+#define bBandSelect 0x1
+#define bHTSIG2_GI 0x80
+#define bHTSIG2_Smoothing 0x01
+#define bHTSIG2_Sounding 0x02
+#define bHTSIG2_Aggreaton 0x08
+#define bHTSIG2_STBC 0x30
+#define bHTSIG2_AdvCoding 0x40
+#define bHTSIG2_NumOfHTLTF 0x300
+#define bHTSIG2_CRC8 0x3fc
+#define bHTSIG1_MCS 0x7f
+#define bHTSIG1_BandWidth 0x80
+#define bHTSIG1_HTLength 0xffff
+#define bLSIG_Rate 0xf
+#define bLSIG_Reserved 0x10
+#define bLSIG_Length 0x1fffe
+#define bLSIG_Parity 0x20
+#define bCCKRxPhase 0x4
+
+#define bLSSIReadAddress 0x7f800000 /* T65 RF */
+
+#define bLSSIReadEdge 0x80000000 /* LSSI "Read" edge signal */
+
+#define bLSSIReadBackData 0xfffff /* T65 RF */
+
+#define bLSSIReadOKFlag 0x1000 /* Useless now */
+#define bCCKSampleRate 0x8 /* 0: 44MHz, 1:88MHz */
+#define bRegulator0Standby 0x1
+#define bRegulatorPLLStandby 0x2
+#define bRegulator1Standby 0x4
+#define bPLLPowerUp 0x8
+#define bDPLLPowerUp 0x10
+#define bDA10PowerUp 0x20
+#define bAD7PowerUp 0x200
+#define bDA6PowerUp 0x2000
+#define bXtalPowerUp 0x4000
+#define b40MDClkPowerUP 0x8000
+#define bDA6DebugMode 0x20000
+#define bDA6Swing 0x380000
+
+#define bADClkPhase 0x4000000 /* Reg 0x880 rFPGA0_AnalogParameter1 20/40 CCK support switch 40/80 BB MHZ */
+
+#define b80MClkDelay 0x18000000 /* Useless */
+#define bAFEWatchDogEnable 0x20000000
+
+#define bXtalCap01 0xc0000000 /* Reg 0x884 rFPGA0_AnalogParameter2 Crystal cap */
+#define bXtalCap23 0x3
+#define bXtalCap92x 0x0f000000
+#define bXtalCap 0x0f000000
+
+#define bIntDifClkEnable 0x400 /* Useless */
+#define bExtSigClkEnable 0x800
+#define bBandgapMbiasPowerUp 0x10000
+#define bAD11SHGain 0xc0000
+#define bAD11InputRange 0x700000
+#define bAD11OPCurrent 0x3800000
+#define bIPathLoopback 0x4000000
+#define bQPathLoopback 0x8000000
+#define bAFELoopback 0x10000000
+#define bDA10Swing 0x7e0
+#define bDA10Reverse 0x800
+#define bDAClkSource 0x1000
+#define bAD7InputRange 0x6000
+#define bAD7Gain 0x38000
+#define bAD7OutputCMMode 0x40000
+#define bAD7InputCMMode 0x380000
+#define bAD7Current 0xc00000
+#define bRegulatorAdjust 0x7000000
+#define bAD11PowerUpAtTx 0x1
+#define bDA10PSAtTx 0x10
+#define bAD11PowerUpAtRx 0x100
+#define bDA10PSAtRx 0x1000
+#define bCCKRxAGCFormat 0x200
+#define bPSDFFTSamplepPoint 0xc000
+#define bPSDAverageNum 0x3000
+#define bIQPathControl 0xc00
+#define bPSDFreq 0x3ff
+#define bPSDAntennaPath 0x30
+#define bPSDIQSwitch 0x40
+#define bPSDRxTrigger 0x400000
+#define bPSDTxTrigger 0x80000000
+#define bPSDSineToneScale 0x7f000000
+#define bPSDReport 0xffff
+
+/* 3. Page9(0x900) */
+#define bOFDMTxSC 0x30000000 /* Useless */
+#define bCCKTxOn 0x1
+#define bOFDMTxOn 0x2
+#define bDebugPage 0xfff /* reset debug page and also HWord, LWord */
+#define bDebugItem 0xff /* reset debug page and LWord */
+#define bAntL 0x10
+#define bAntNonHT 0x100
+#define bAntHT1 0x1000
+#define bAntHT2 0x10000
+#define bAntHT1S1 0x100000
+#define bAntNonHTS1 0x1000000
+
+/* 4. PageA(0xA00) */
+#define bCCKBBMode 0x3 /* Useless */
+#define bCCKTxPowerSaving 0x80
+#define bCCKRxPowerSaving 0x40
+
+#define bCCKSideBand 0x10 /* Reg 0xa00 rCCK0_System 20/40 switch */
+
+#define bCCKScramble 0x8 /* Useless */
+#define bCCKAntDiversity 0x8000
+#define bCCKCarrierRecovery 0x4000
+#define bCCKTxRate 0x3000
+#define bCCKDCCancel 0x0800
+#define bCCKISICancel 0x0400
+#define bCCKMatchFilter 0x0200
+#define bCCKEqualizer 0x0100
+#define bCCKPreambleDetect 0x800000
+#define bCCKFastFalseCCA 0x400000
+#define bCCKChEstStart 0x300000
+#define bCCKCCACount 0x080000
+#define bCCKcs_lim 0x070000
+#define bCCKBistMode 0x80000000
+#define bCCKCCAMask 0x40000000
+#define bCCKTxDACPhase 0x4
+#define bCCKRxADCPhase 0x20000000 /* r_rx_clk */
+#define bCCKr_cp_mode0 0x0100
+#define bCCKTxDCOffset 0xf0
+#define bCCKRxDCOffset 0xf
+#define bCCKCCAMode 0xc000
+#define bCCKFalseCS_lim 0x3f00
+#define bCCKCS_ratio 0xc00000
+#define bCCKCorgBit_sel 0x300000
+#define bCCKPD_lim 0x0f0000
+#define bCCKNewCCA 0x80000000
+#define bCCKRxHPofIG 0x8000
+#define bCCKRxIG 0x7f00
+#define bCCKLNAPolarity 0x800000
+#define bCCKRx1stGain 0x7f0000
+#define bCCKRFExtend 0x20000000 /* CCK Rx Iinital gain polarity */
+#define bCCKRxAGCSatLevel 0x1f000000
+#define bCCKRxAGCSatCount 0xe0
+#define bCCKRxRFSettle 0x1f /* AGCsamp_dly */
+#define bCCKFixedRxAGC 0x8000
+/* define bCCKRxAGCFormat 0x4000 remove to HSSI register 0x824 */
+#define bCCKAntennaPolarity 0x2000
+#define bCCKTxFilterType 0x0c00
+#define bCCKRxAGCReportType 0x0300
+#define bCCKRxDAGCEn 0x80000000
+#define bCCKRxDAGCPeriod 0x20000000
+#define bCCKRxDAGCSatLevel 0x1f000000
+#define bCCKTimingRecovery 0x800000
+#define bCCKTxC0 0x3f0000
+#define bCCKTxC1 0x3f000000
+#define bCCKTxC2 0x3f
+#define bCCKTxC3 0x3f00
+#define bCCKTxC4 0x3f0000
+#define bCCKTxC5 0x3f000000
+#define bCCKTxC6 0x3f
+#define bCCKTxC7 0x3f00
+#define bCCKDebugPort 0xff0000
+#define bCCKDACDebug 0x0f000000
+#define bCCKFalseAlarmEnable 0x8000
+#define bCCKFalseAlarmRead 0x4000
+#define bCCKTRSSI 0x7f
+#define bCCKRxAGCReport 0xfe
+#define bCCKRxReport_AntSel 0x80000000
+#define bCCKRxReport_MFOff 0x40000000
+#define bCCKRxRxReport_SQLoss 0x20000000
+#define bCCKRxReport_Pktloss 0x10000000
+#define bCCKRxReport_Lockedbit 0x08000000
+#define bCCKRxReport_RateError 0x04000000
+#define bCCKRxReport_RxRate 0x03000000
+#define bCCKRxFACounterLower 0xff
+#define bCCKRxFACounterUpper 0xff000000
+#define bCCKRxHPAGCStart 0xe000
+#define bCCKRxHPAGCFinal 0x1c00
+#define bCCKRxFalseAlarmEnable 0x8000
+#define bCCKFACounterFreeze 0x4000
+#define bCCKTxPathSel 0x10000000
+#define bCCKDefaultRxPath 0xc000000
+#define bCCKOptionRxPath 0x3000000
+
+/* 5. PageC(0xC00) */
+#define bNumOfSTF 0x3 /* Useless */
+#define bShift_L 0xc0
+#define bGI_TH 0xc
+#define bRxPathA 0x1
+#define bRxPathB 0x2
+#define bRxPathC 0x4
+#define bRxPathD 0x8
+#define bTxPathA 0x1
+#define bTxPathB 0x2
+#define bTxPathC 0x4
+#define bTxPathD 0x8
+#define bTRSSIFreq 0x200
+#define bADCBackoff 0x3000
+#define bDFIRBackoff 0xc000
+#define bTRSSILatchPhase 0x10000
+#define bRxIDCOffset 0xff
+#define bRxQDCOffset 0xff00
+#define bRxDFIRMode 0x1800000
+#define bRxDCNFType 0xe000000
+#define bRXIQImb_A 0x3ff
+#define bRXIQImb_B 0xfc00
+#define bRXIQImb_C 0x3f0000
+#define bRXIQImb_D 0xffc00000
+#define bDC_dc_Notch 0x60000
+#define bRxNBINotch 0x1f000000
+#define bPD_TH 0xf
+#define bPD_TH_Opt2 0xc000
+#define bPWED_TH 0x700
+#define bIfMF_Win_L 0x800
+#define bPD_Option 0x1000
+#define bMF_Win_L 0xe000
+#define bBW_Search_L 0x30000
+#define bwin_enh_L 0xc0000
+#define bBW_TH 0x700000
+#define bED_TH2 0x3800000
+#define bBW_option 0x4000000
+#define bRatio_TH 0x18000000
+#define bWindow_L 0xe0000000
+#define bSBD_Option 0x1
+#define bFrame_TH 0x1c
+#define bFS_Option 0x60
+#define bDC_Slope_check 0x80
+#define bFGuard_Counter_DC_L 0xe00
+#define bFrame_Weight_Short 0x7000
+#define bSub_Tune 0xe00000
+#define bFrame_DC_Length 0xe000000
+#define bSBD_start_offset 0x30000000
+#define bFrame_TH_2 0x7
+#define bFrame_GI2_TH 0x38
+#define bGI2_Sync_en 0x40
+#define bSarch_Short_Early 0x300
+#define bSarch_Short_Late 0xc00
+#define bSarch_GI2_Late 0x70000
+#define bCFOAntSum 0x1
+#define bCFOAcc 0x2
+#define bCFOStartOffset 0xc
+#define bCFOLookBack 0x70
+#define bCFOSumWeight 0x80
+#define bDAGCEnable 0x10000
+#define bTXIQImb_A 0x3ff
+#define bTXIQImb_B 0xfc00
+#define bTXIQImb_C 0x3f0000
+#define bTXIQImb_D 0xffc00000
+#define bTxIDCOffset 0xff
+#define bTxQDCOffset 0xff00
+#define bTxDFIRMode 0x10000
+#define bTxPesudoNoiseOn 0x4000000
+#define bTxPesudoNoise_A 0xff
+#define bTxPesudoNoise_B 0xff00
+#define bTxPesudoNoise_C 0xff0000
+#define bTxPesudoNoise_D 0xff000000
+#define bCCADropOption 0x20000
+#define bCCADropThres 0xfff00000
+#define bEDCCA_H 0xf
+#define bEDCCA_L 0xf0
+#define bLambda_ED 0x300
+#define bRxInitialGain 0x7f
+#define bRxAntDivEn 0x80
+#define bRxAGCAddressForLNA 0x7f00
+#define bRxHighPowerFlow 0x8000
+#define bRxAGCFreezeThres 0xc0000
+#define bRxFreezeStep_AGC1 0x300000
+#define bRxFreezeStep_AGC2 0xc00000
+#define bRxFreezeStep_AGC3 0x3000000
+#define bRxFreezeStep_AGC0 0xc000000
+#define bRxRssi_Cmp_En 0x10000000
+#define bRxQuickAGCEn 0x20000000
+#define bRxAGCFreezeThresMode 0x40000000
+#define bRxOverFlowCheckType 0x80000000
+#define bRxAGCShift 0x7f
+#define bTRSW_Tri_Only 0x80
+#define bPowerThres 0x300
+#define bRxAGCEn 0x1
+#define bRxAGCTogetherEn 0x2
+#define bRxAGCMin 0x4
+#define bRxHP_Ini 0x7
+#define bRxHP_TRLNA 0x70
+#define bRxHP_RSSI 0x700
+#define bRxHP_BBP1 0x7000
+#define bRxHP_BBP2 0x70000
+#define bRxHP_BBP3 0x700000
+#define bRSSI_H 0x7f0000 /* the threshold for high power */
+#define bRSSI_Gen 0x7f000000 /* the threshold for ant diversity */
+#define bRxSettle_TRSW 0x7
+#define bRxSettle_LNA 0x38
+#define bRxSettle_RSSI 0x1c0
+#define bRxSettle_BBP 0xe00
+#define bRxSettle_RxHP 0x7000
+#define bRxSettle_AntSW_RSSI 0x38000
+#define bRxSettle_AntSW 0xc0000
+#define bRxProcessTime_DAGC 0x300000
+#define bRxSettle_HSSI 0x400000
+#define bRxProcessTime_BBPPW 0x800000
+#define bRxAntennaPowerShift 0x3000000
+#define bRSSITableSelect 0xc000000
+#define bRxHP_Final 0x7000000
+#define bRxHTSettle_BBP 0x7
+#define bRxHTSettle_HSSI 0x8
+#define bRxHTSettle_RxHP 0x70
+#define bRxHTSettle_BBPPW 0x80
+#define bRxHTSettle_Idle 0x300
+#define bRxHTSettle_Reserved 0x1c00
+#define bRxHTRxHPEn 0x8000
+#define bRxHTAGCFreezeThres 0x30000
+#define bRxHTAGCTogetherEn 0x40000
+#define bRxHTAGCMin 0x80000
+#define bRxHTAGCEn 0x100000
+#define bRxHTDAGCEn 0x200000
+#define bRxHTRxHP_BBP 0x1c00000
+#define bRxHTRxHP_Final 0xe0000000
+#define bRxPWRatioTH 0x3
+#define bRxPWRatioEn 0x4
+#define bRxMFHold 0x3800
+#define bRxPD_Delay_TH1 0x38
+#define bRxPD_Delay_TH2 0x1c0
+#define bRxPD_DC_COUNT_MAX 0x600
+/* define bRxMF_Hold 0x3800 */
+#define bRxPD_Delay_TH 0x8000
+#define bRxProcess_Delay 0xf0000
+#define bRxSearchrange_GI2_Early 0x700000
+#define bRxFrame_Guard_Counter_L 0x3800000
+#define bRxSGI_Guard_L 0xc000000
+#define bRxSGI_Search_L 0x30000000
+#define bRxSGI_TH 0xc0000000
+#define bDFSCnt0 0xff
+#define bDFSCnt1 0xff00
+#define bDFSFlag 0xf0000
+#define bMFWeightSum 0x300000
+#define bMinIdxTH 0x7f000000
+#define bDAFormat 0x40000
+#define bTxChEmuEnable 0x01000000
+#define bTRSWIsolation_A 0x7f
+#define bTRSWIsolation_B 0x7f00
+#define bTRSWIsolation_C 0x7f0000
+#define bTRSWIsolation_D 0x7f000000
+#define bExtLNAGain 0x7c00
+
+/* 6. PageE(0xE00) */
+#define bSTBCEn 0x4 /* Useless */
+#define bAntennaMapping 0x10
+#define bNss 0x20
+#define bCFOAntSumD 0x200
+#define bPHYCounterReset 0x8000000
+#define bCFOReportGet 0x4000000
+#define bOFDMContinueTx 0x10000000
+#define bOFDMSingleCarrier 0x20000000
+#define bOFDMSingleTone 0x40000000
+/* define bRxPath1 0x01 */
+/* define bRxPath2 0x02 */
+/* define bRxPath3 0x04 */
+/* define bRxPath4 0x08 */
+/* define bTxPath1 0x10 */
+/* define bTxPath2 0x20 */
+#define bHTDetect 0x100
+#define bCFOEn 0x10000
+#define bCFOValue 0xfff00000
+#define bSigTone_Re 0x3f
+#define bSigTone_Im 0x7f00
+#define bCounter_CCA 0xffff
+#define bCounter_ParityFail 0xffff0000
+#define bCounter_RateIllegal 0xffff
+#define bCounter_CRC8Fail 0xffff0000
+#define bCounter_MCSNoSupport 0xffff
+#define bCounter_FastSync 0xffff
+#define bShortCFO 0xfff
+#define bShortCFOTLength 12 /* total */
+#define bShortCFOFLength 11 /* fraction */
+#define bLongCFO 0x7ff
+#define bLongCFOTLength 11
+#define bLongCFOFLength 11
+#define bTailCFO 0x1fff
+#define bTailCFOTLength 13
+#define bTailCFOFLength 12
+#define bmax_en_pwdB 0xffff
+#define bCC_power_dB 0xffff0000
+#define bnoise_pwdB 0xffff
+#define bPowerMeasTLength 10
+#define bPowerMeasFLength 3
+#define bRx_HT_BW 0x1
+#define bRxSC 0x6
+#define bRx_HT 0x8
+#define bNB_intf_det_on 0x1
+#define bIntf_win_len_cfg 0x30
+#define bNB_Intf_TH_cfg 0x1c0
+#define bRFGain 0x3f
+#define bTableSel 0x40
+#define bTRSW 0x80
+#define bRxSNR_A 0xff
+#define bRxSNR_B 0xff00
+#define bRxSNR_C 0xff0000
+#define bRxSNR_D 0xff000000
+#define bSNREVMTLength 8
+#define bSNREVMFLength 1
+#define bCSI1st 0xff
+#define bCSI2nd 0xff00
+#define bRxEVM1st 0xff0000
+#define bRxEVM2nd 0xff000000
+#define bSIGEVM 0xff
+#define bPWDB 0xff00
+#define bSGIEN 0x10000
+
+#define bSFactorQAM1 0xf /* Useless */
+#define bSFactorQAM2 0xf0
+#define bSFactorQAM3 0xf00
+#define bSFactorQAM4 0xf000
+#define bSFactorQAM5 0xf0000
+#define bSFactorQAM6 0xf0000
+#define bSFactorQAM7 0xf00000
+#define bSFactorQAM8 0xf000000
+#define bSFactorQAM9 0xf0000000
+#define bCSIScheme 0x100000
+
+#define bNoiseLvlTopSet 0x3 /* Useless */
+#define bChSmooth 0x4
+#define bChSmoothCfg1 0x38
+#define bChSmoothCfg2 0x1c0
+#define bChSmoothCfg3 0xe00
+#define bChSmoothCfg4 0x7000
+#define bMRCMode 0x800000
+#define bTHEVMCfg 0x7000000
+
+#define bLoopFitType 0x1 /* Useless */
+#define bUpdCFO 0x40
+#define bUpdCFOOffData 0x80
+#define bAdvUpdCFO 0x100
+#define bAdvTimeCtrl 0x800
+#define bUpdClko 0x1000
+#define bFC 0x6000
+#define bTrackingMode 0x8000
+#define bPhCmpEnable 0x10000
+#define bUpdClkoLTF 0x20000
+#define bComChCFO 0x40000
+#define bCSIEstiMode 0x80000
+#define bAdvUpdEqz 0x100000
+#define bUChCfg 0x7000000
+#define bUpdEqz 0x8000000
+
+/* Rx Pseduo noise */
+#define bRxPesudoNoiseOn 0x20000000 /* Useless */
+#define bRxPesudoNoise_A 0xff
+#define bRxPesudoNoise_B 0xff00
+#define bRxPesudoNoise_C 0xff0000
+#define bRxPesudoNoise_D 0xff000000
+#define bPesudoNoiseState_A 0xffff
+#define bPesudoNoiseState_B 0xffff0000
+#define bPesudoNoiseState_C 0xffff
+#define bPesudoNoiseState_D 0xffff0000
+
+/* 7. RF Register */
+/* Zebra1 */
+#define bZebra1_HSSIEnable 0x8 /* Useless */
+#define bZebra1_TRxControl 0xc00
+#define bZebra1_TRxGainSetting 0x07f
+#define bZebra1_RxCorner 0xc00
+#define bZebra1_TxChargePump 0x38
+#define bZebra1_RxChargePump 0x7
+#define bZebra1_ChannelNum 0xf80
+#define bZebra1_TxLPFBW 0x400
+#define bZebra1_RxLPFBW 0x600
+
+/* Zebra4 */
+#define bRTL8256RegModeCtrl1 0x100 /* Useless */
+#define bRTL8256RegModeCtrl0 0x40
+#define bRTL8256_TxLPFBW 0x18
+#define bRTL8256_RxLPFBW 0x600
+
+/* RTL8258 */
+#define bRTL8258_TxLPFBW 0xc /* Useless */
+#define bRTL8258_RxLPFBW 0xc00
+#define bRTL8258_RSSILPFBW 0xc0
+
+
+/* Other Definition */
+
+/* byte endable for sb_write */
+#define bByte0 0x1 /* Useless */
+#define bByte1 0x2
+#define bByte2 0x4
+#define bByte3 0x8
+#define bWord0 0x3
+#define bWord1 0xc
+#define bDWord 0xf
+
+/* for PutRegsetting & GetRegSetting BitMask */
+#define bMaskByte0 0xff /* Reg 0xc50 rOFDM0_XAAGCCore~0xC6f */
+#define bMaskByte1 0xff00
+#define bMaskByte2 0xff0000
+#define bMaskByte3 0xff000000
+#define bMaskHWord 0xffff0000
+#define bMaskLWord 0x0000ffff
+#define bMaskDWord 0xffffffff
+#define bMask12Bits 0xfff
+#define bMaskH4Bits 0xf0000000
+#define bMaskOFDM_D 0xffc00000
+#define bMaskCCK 0x3f3f3f3f
+
+/* for PutRFRegsetting & GetRFRegSetting BitMask */
+#define bRFRegOffsetMask 0xfffff
+
+#define bDisable 0x0
+
+#define LeftAntenna 0x0 /* Useless */
+#define RightAntenna 0x1
+
+#define tCheckTxStatus 500 /* 500ms Useless */
+#define tUpdateRxCounter 100 /* 100ms */
+
+#define rateCCK 0 /* Useless */
+#define rateOFDM 1
+#define rateHT 2
+
+/* define Register-End */
+#define bPMAC_End 0x1ff /* Useless */
+#define bFPGAPHY0_End 0x8ff
+#define bFPGAPHY1_End 0x9ff
+#define bCCKPHY0_End 0xaff
+#define bOFDMPHY0_End 0xcff
+#define bOFDMPHY1_End 0xdff
+
+/* define max debug item in each debug page */
+/* define bMaxItem_FPGA_PHY0 0x9 */
+/* define bMaxItem_FPGA_PHY1 0x3 */
+/* define bMaxItem_PHY_11B 0x16 */
+/* define bMaxItem_OFDM_PHY0 0x29 */
+/* define bMaxItem_OFDM_PHY1 0x0 */
+
+#define bPMACControl 0x0 /* Useless */
+#define bWMACControl 0x1
+#define bWNICControl 0x2
+
+#define PathA 0x0 /* Useless */
+#define PathB 0x1
+#define PathC 0x2
+#define PathD 0x3
+
+/* PageB(0xB00) */
+#define rPdp_AntA 0xb00
+#define rPdp_AntA_4 0xb04
+#define rPdp_AntA_8 0xb08
+#define rPdp_AntA_C 0xb0c
+#define rPdp_AntA_18 0xb18
+#define rPdp_AntA_1C 0xb1c
+#define rPdp_AntA_20 0xb20
+#define rPdp_AntA_24 0xb24
+
+#define rConfig_Pmpd_AntA 0xb28
+#define rConfig_ram64x16 0xb2c
+
+#define rBndA 0xb30
+#define rHssiPar 0xb34
+
+#define rConfig_AntA 0xb68
+#define rConfig_AntB 0xb6c
+
+#define rPdp_AntB 0xb70
+#define rPdp_AntB_4 0xb74
+#define rPdp_AntB_8 0xb78
+#define rPdp_AntB_C 0xb7c
+#define rPdp_AntB_10 0xb80
+#define rPdp_AntB_14 0xb84
+#define rPdp_AntB_18 0xb88
+#define rPdp_AntB_1C 0xb8c
+#define rPdp_AntB_20 0xb90
+#define rPdp_AntB_24 0xb94
+
+#define rConfig_Pmpd_AntB 0xb98
+
+#define rBndB 0xba0
+
+#define rAPK 0xbd8
+#define rPm_Rx0_AntA 0xbdc
+#define rPm_Rx1_AntA 0xbe0
+#define rPm_Rx2_AntA 0xbe4
+#define rPm_Rx3_AntA 0xbe8
+#define rPm_Rx0_AntB 0xbec
+#define rPm_Rx1_AntB 0xbf0
+#define rPm_Rx2_AntB 0xbf4
+#define rPm_Rx3_AntB 0xbf8
+
+#endif
diff --git a/drivers/staging/rtl8723au/include/Hal8723PwrSeq.h b/drivers/staging/rtl8723au/include/Hal8723PwrSeq.h
new file mode 100644
index 000000000..3771d6bb5
--- /dev/null
+++ b/drivers/staging/rtl8723au/include/Hal8723PwrSeq.h
@@ -0,0 +1,126 @@
+#ifndef __HAL8723PWRSEQ_H__
+#define __HAL8723PWRSEQ_H__
+/*
+ Check document WM-20110607-Paul-RTL8723A_Power_Architecture-R02.vsd
+ There are 6 HW Power States:
+ 0: POFF--Power Off
+ 1: PDN--Power Down
+ 2: CARDEMU--Card Emulation
+ 3: ACT--Active Mode
+ 4: LPS--Low Power State
+ 5: SUS--Suspend
+
+ The transision from different states are defined below
+ TRANS_CARDEMU_TO_ACT
+ TRANS_ACT_TO_CARDEMU
+ TRANS_CARDEMU_TO_SUS
+ TRANS_SUS_TO_CARDEMU
+ TRANS_CARDEMU_TO_PDN
+ TRANS_ACT_TO_LPS
+ TRANS_LPS_TO_ACT
+
+ TRANS_END
+*/
+#include "HalPwrSeqCmd.h"
+#include "rtl8723a_spec.h"
+
+#define RTL8723A_TRANS_CARDEMU_TO_ACT_STEPS 15
+#define RTL8723A_TRANS_ACT_TO_CARDEMU_STEPS 15
+#define RTL8723A_TRANS_CARDEMU_TO_SUS_STEPS 15
+#define RTL8723A_TRANS_SUS_TO_CARDEMU_STEPS 15
+#define RTL8723A_TRANS_CARDEMU_TO_PDN_STEPS 15
+#define RTL8723A_TRANS_PDN_TO_CARDEMU_STEPS 15
+#define RTL8723A_TRANS_ACT_TO_LPS_STEPS 15
+#define RTL8723A_TRANS_LPS_TO_ACT_STEPS 15
+#define RTL8723A_TRANS_END_STEPS 1
+
+
+/* format
+ * { offset, cut_msk, fab_msk|interface_msk, base|cmd, msk, value }, comments here
+ */
+#define RTL8723A_TRANS_CARDEMU_TO_ACT \
+ {0x0020, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_USB_MSK, PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(0), BIT(0)}, /*0x20[0] = 1b'1 enable LDOA12 MACRO block for all interface*/ \
+ {0x0067, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_USB_MSK, PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(4), 0}, /*0x67[0] = 0 to disable BT_GPS_SEL pins*/ \
+ {0x0001, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_USB_MSK, PWR_BASEADDR_MAC, PWR_CMD_DELAY, 1, PWRSEQ_DELAY_MS},/*Delay 1ms*/ \
+ {0x0000, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_USB_MSK, PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(5), 0}, /*0x00[5] = 1b'0 release analog Ips to digital , 1:isolation*/ \
+ {0x0005, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(2), 0},/* disable SW LPS 0x04[10]= 0*/ \
+ {0x0006, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, PWR_BASEADDR_MAC, PWR_CMD_POLLING, BIT(1), BIT(1)},/* wait till 0x04[17] = 1 power ready*/ \
+ {0x0006, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(0), BIT(0)},/* release WLON reset 0x04[16]= 1*/ \
+ {0x0005, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(7), 0},/* disable HWPDN 0x04[15]= 0*/ \
+ {0x0005, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, PWR_BASEADDR_MAC, PWR_CMD_WRITE, (BIT(4)|BIT(3)), 0},/* disable WL suspend*/ \
+ {0x0005, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(0), BIT(0)},/* polling until return 0*/ \
+ {0x0005, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, PWR_BASEADDR_MAC, PWR_CMD_POLLING, BIT(0), 0},/**/ \
+ {0x004E, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(7), 1},/*0x4C[23] = 0x4E[7] = 1, switch DPDT_SEL_P output from WL BB */\
+
+#define RTL8723A_TRANS_ACT_TO_CARDEMU \
+ {0x001F, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, PWR_BASEADDR_MAC, PWR_CMD_WRITE, 0xFF, 0},/*0x1F[7:0] = 0 turn off RF*/ \
+ {0x004E, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(7), 0},/*0x4C[23] = 0x4E[7] = 0, switch DPDT_SEL_P output from register 0x65[2] */\
+ {0x0005, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(1), BIT(1)}, /*0x04[9] = 1 turn off MAC by HW state machine*/ \
+ {0x0005, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, PWR_BASEADDR_MAC, PWR_CMD_POLLING, BIT(1), 0}, /*wait till 0x04[9] = 0 polling until return 0 to disable*/ \
+ {0x0000, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_USB_MSK, PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(5), BIT(5)}, /*0x00[5] = 1b'1 analog Ips to digital , 1:isolation*/ \
+ {0x0020, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_USB_MSK, PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(0), 0}, /*0x20[0] = 1b'0 disable LDOA12 MACRO block*/ \
+
+
+#define RTL8723A_TRANS_CARDEMU_TO_SUS \
+ {0x0005, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_USB_MSK, PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(3)|BIT(4), BIT(3)}, /*0x04[12:11] = 2b'01 enable WL suspend*/
+
+#define RTL8723A_TRANS_SUS_TO_CARDEMU \
+ {0x0005, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(3) | BIT(7), 0}, /*clear suspend enable and power down enable*/ \
+ {0x0005, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(3)|BIT(4), 0}, /*0x04[12:11] = 2b'01enable WL suspend*/
+
+#define RTL8723A_TRANS_CARDEMU_TO_CARDDIS \
+ {0x0005, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_USB_MSK, PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(3)|BIT(4), BIT(3)}, /*0x04[12:11] = 2b'01 enable WL suspend*/ \
+ {0x004A, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_USB_MSK, PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(0), BIT(0)}, /*0x48[16] = 1 to enable GPIO9 as EXT WAKEUP*/
+
+#define RTL8723A_TRANS_CARDDIS_TO_CARDEMU \
+ {0x0005, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(3) | BIT(7), 0}, /*clear suspend enable and power down enable*/ \
+ {0x004A, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_USB_MSK, PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(0), 0}, /*0x48[16] = 0 to disable GPIO9 as EXT WAKEUP*/ \
+ {0x0005, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(3)|BIT(4), 0}, /*0x04[12:11] = 2b'01enable WL suspend*/
+
+#define RTL8723A_TRANS_CARDEMU_TO_PDN \
+ {0x0007, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_USB_MSK, PWR_BASEADDR_MAC, PWR_CMD_WRITE, 0xFF, 0x20}, /*0x07[7:0] = 0x20 SOP option to disable BG/MB/ACK/SWR*/ \
+ {0x0006, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(0), 0},/* 0x04[16] = 0*/\
+ {0x0005, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(7), BIT(7)},/* 0x04[15] = 1*/
+
+#define RTL8723A_TRANS_PDN_TO_CARDEMU \
+ {0x0005, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(7), 0},/* 0x04[15] = 0*/
+
+#define RTL8723A_TRANS_ACT_TO_LPS \
+ {0x0522, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, PWR_BASEADDR_MAC, PWR_CMD_WRITE, 0xFF, 0xFF},/*Tx Pause*/ \
+ {0x05F8, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, PWR_BASEADDR_MAC, PWR_CMD_POLLING, 0xFF, 0},/*Should be zero if no packet is transmitting*/ \
+ {0x05F9, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, PWR_BASEADDR_MAC, PWR_CMD_POLLING, 0xFF, 0},/*Should be zero if no packet is transmitting*/ \
+ {0x05FA, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, PWR_BASEADDR_MAC, PWR_CMD_POLLING, 0xFF, 0},/*Should be zero if no packet is transmitting*/ \
+ {0x05FB, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, PWR_BASEADDR_MAC, PWR_CMD_POLLING, 0xFF, 0},/*Should be zero if no packet is transmitting*/ \
+ {0x0002, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(0), 0},/*CCK and OFDM are disabled, and clock are gated*/ \
+ {0x0002, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, PWR_BASEADDR_MAC, PWR_CMD_DELAY, 0, PWRSEQ_DELAY_US},/*Delay 1us*/ \
+ {0x0002, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(1), 0},/*Whole BB is reset*/ \
+ {0x0100, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, PWR_BASEADDR_MAC, PWR_CMD_WRITE, 0xFF, 0x03},/*Reset MAC TRX*/ \
+ {0x0101, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(1), 0},/*check if removed later*/ \
+ {0x0553, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(5), BIT(5)},/*Respond TxOK to scheduler*/
+
+#define RTL8723A_TRANS_LPS_TO_ACT \
+ {0xFE58, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_USB_MSK, PWR_BASEADDR_MAC, PWR_CMD_WRITE, 0xFF, 0x84}, /*USB RPWM*/\
+ {0x0002, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, PWR_BASEADDR_MAC, PWR_CMD_DELAY, 0, PWRSEQ_DELAY_MS}, /*Delay*/\
+ {0x0008, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(4), 0}, /*. 0x08[4] = 0 switch TSF to 40M*/\
+ {0x0109, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, PWR_BASEADDR_MAC, PWR_CMD_POLLING, BIT(7), 0}, /*Polling 0x109[7]= 0 TSF in 40M*/\
+ {0x0029, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(6)|BIT(7), 0}, /*. 0x29[7:6] = 2b'00 enable BB clock*/\
+ {0x0101, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(1), BIT(1)}, /*. 0x101[1] = 1*/\
+ {0x0100, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, PWR_BASEADDR_MAC, PWR_CMD_WRITE, 0xFF, 0xFF}, /*. 0x100[7:0] = 0xFF enable WMAC TRX*/\
+ {0x0002, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(1)|BIT(0), BIT(1)|BIT(0)}, /*. 0x02[1:0] = 2b'11 enable BB macro*/\
+ {0x0522, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, PWR_BASEADDR_MAC, PWR_CMD_WRITE, 0xFF, 0}, /*. 0x522 = 0*/
+
+#define RTL8723A_TRANS_END \
+ {0xFFFF, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, 0, PWR_CMD_END, 0, 0},
+
+
+extern struct wlan_pwr_cfg rtl8723AU_power_on_flow[RTL8723A_TRANS_CARDEMU_TO_ACT_STEPS+RTL8723A_TRANS_END_STEPS];
+extern struct wlan_pwr_cfg rtl8723AU_radio_off_flow[RTL8723A_TRANS_ACT_TO_CARDEMU_STEPS+RTL8723A_TRANS_END_STEPS];
+extern struct wlan_pwr_cfg rtl8723AU_card_disable_flow[RTL8723A_TRANS_ACT_TO_CARDEMU_STEPS+RTL8723A_TRANS_CARDEMU_TO_PDN_STEPS+RTL8723A_TRANS_END_STEPS];
+extern struct wlan_pwr_cfg rtl8723AU_card_enable_flow[RTL8723A_TRANS_ACT_TO_CARDEMU_STEPS+RTL8723A_TRANS_CARDEMU_TO_PDN_STEPS+RTL8723A_TRANS_END_STEPS];
+extern struct wlan_pwr_cfg rtl8723AU_suspend_flow[RTL8723A_TRANS_ACT_TO_CARDEMU_STEPS+RTL8723A_TRANS_CARDEMU_TO_SUS_STEPS+RTL8723A_TRANS_END_STEPS];
+extern struct wlan_pwr_cfg rtl8723AU_resume_flow[RTL8723A_TRANS_ACT_TO_CARDEMU_STEPS+RTL8723A_TRANS_CARDEMU_TO_SUS_STEPS+RTL8723A_TRANS_END_STEPS];
+extern struct wlan_pwr_cfg rtl8723AU_hwpdn_flow[RTL8723A_TRANS_ACT_TO_CARDEMU_STEPS+RTL8723A_TRANS_CARDEMU_TO_PDN_STEPS+RTL8723A_TRANS_END_STEPS];
+extern struct wlan_pwr_cfg rtl8723AU_enter_lps_flow[RTL8723A_TRANS_ACT_TO_LPS_STEPS+RTL8723A_TRANS_END_STEPS];
+extern struct wlan_pwr_cfg rtl8723AU_leave_lps_flow[RTL8723A_TRANS_LPS_TO_ACT_STEPS+RTL8723A_TRANS_END_STEPS];
+
+#endif
diff --git a/drivers/staging/rtl8723au/include/Hal8723UHWImg_CE.h b/drivers/staging/rtl8723au/include/Hal8723UHWImg_CE.h
new file mode 100644
index 000000000..c834b3a73
--- /dev/null
+++ b/drivers/staging/rtl8723au/include/Hal8723UHWImg_CE.h
@@ -0,0 +1,29 @@
+#ifndef __INC_HAL8723U_FW_IMG_H
+#define __INC_HAL8723U_FW_IMG_H
+
+/*Created on 2013/01/14, 15:51*/
+
+/* FW v16 enable usb interrupt */
+#define Rtl8723UImgArrayLength 22172
+extern u8 Rtl8723UFwImgArray[Rtl8723UImgArrayLength];
+#define Rtl8723UBTImgArrayLength 1
+extern u8 Rtl8723UFwBTImgArray[Rtl8723UBTImgArrayLength];
+
+#define Rtl8723UUMCBCutImgArrayWithBTLength 24118
+#define Rtl8723UUMCBCutImgArrayWithoutBTLength 19200
+
+extern u8 Rtl8723UFwUMCBCutImgArrayWithBT[Rtl8723UUMCBCutImgArrayWithBTLength];
+extern u8 Rtl8723UFwUMCBCutImgArrayWithoutBT[Rtl8723UUMCBCutImgArrayWithoutBTLength];
+
+#define Rtl8723SUMCBCutMPImgArrayLength 24174
+extern const u8 Rtl8723SFwUMCBCutMPImgArray[Rtl8723SUMCBCutMPImgArrayLength];
+
+#define Rtl8723EBTImgArrayLength 15276
+extern u8 Rtl8723EFwBTImgArray[Rtl8723EBTImgArrayLength];
+
+#define Rtl8723UPHY_REG_Array_PGLength 336
+extern u32 Rtl8723UPHY_REG_Array_PG[Rtl8723UPHY_REG_Array_PGLength];
+#define Rtl8723UMACPHY_Array_PGLength 1
+extern u32 Rtl8723UMACPHY_Array_PG[Rtl8723UMACPHY_Array_PGLength];
+
+#endif /* ifndef __INC_HAL8723U_FW_IMG_H */
diff --git a/drivers/staging/rtl8723au/include/HalDMOutSrc8723A.h b/drivers/staging/rtl8723au/include/HalDMOutSrc8723A.h
new file mode 100644
index 000000000..d7651f7a6
--- /dev/null
+++ b/drivers/staging/rtl8723au/include/HalDMOutSrc8723A.h
@@ -0,0 +1,64 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2011 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * 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.
+ *
+ *
+ ******************************************************************************/
+#ifndef __RTL8723A_ODM_H__
+#define __RTL8723A_ODM_H__
+/* */
+
+#define RSSI_CCK 0
+#define RSSI_OFDM 1
+#define RSSI_DEFAULT 2
+
+#define IQK_MAC_REG_NUM 4
+#define IQK_ADDA_REG_NUM 16
+#define IQK_BB_REG_NUM 9
+#define HP_THERMAL_NUM 8
+
+
+/* */
+/* structure and define */
+/* */
+
+
+
+
+/*------------------------Export global variable----------------------------*/
+/*------------------------Export global variable----------------------------*/
+/*------------------------Export Marco Definition---------------------------*/
+/* define DM_MultiSTA_InitGainChangeNotify(Event) {DM_DigTable.CurMultiSTAConnectState = Event;} */
+
+
+/* */
+/* function prototype */
+/* */
+
+/* */
+/* IQ calibrate */
+/* */
+void rtl8723a_phy_iq_calibrate(struct rtw_adapter *pAdapter, bool bReCovery);
+
+/* */
+/* LC calibrate */
+/* */
+void rtl8723a_phy_lc_calibrate(struct rtw_adapter *pAdapter);
+
+/* */
+/* AP calibrate */
+/* */
+void rtl8723a_phy_ap_calibrate(struct rtw_adapter *pAdapter, char delta);
+
+void rtl8723a_odm_check_tx_power_tracking(struct rtw_adapter *Adapter);
+
+#endif
diff --git a/drivers/staging/rtl8723au/include/HalHWImg8723A_BB.h b/drivers/staging/rtl8723au/include/HalHWImg8723A_BB.h
new file mode 100644
index 000000000..127609404
--- /dev/null
+++ b/drivers/staging/rtl8723au/include/HalHWImg8723A_BB.h
@@ -0,0 +1,38 @@
+/******************************************************************************
+*
+* Copyright(c) 2007 - 2011 Realtek Corporation. All rights reserved.
+*
+* This program is free software; you can redistribute it and/or modify it
+* under the terms of version 2 of the GNU General Public License as
+* published by the Free Software Foundation.
+*
+* 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.
+*
+*
+******************************************************************************/
+
+#ifndef __INC_BB_8723A_HW_IMG_H
+#define __INC_BB_8723A_HW_IMG_H
+
+/******************************************************************************
+* AGC_TAB_1T.TXT
+******************************************************************************/
+
+void ODM_ReadAndConfig_AGC_TAB_1T_8723A(struct dm_odm_t *pDM_Odm);
+
+/******************************************************************************
+* PHY_REG_1T.TXT
+******************************************************************************/
+
+void ODM_ReadAndConfig_PHY_REG_1T_8723A(struct dm_odm_t *pDM_Odm);
+
+/******************************************************************************
+* PHY_REG_MP.TXT
+******************************************************************************/
+
+void ODM_ReadAndConfig_PHY_REG_MP_8723A(struct dm_odm_t *pDM_Odm);
+
+#endif /* end of HWIMG_SUPPORT */
diff --git a/drivers/staging/rtl8723au/include/HalHWImg8723A_FW.h b/drivers/staging/rtl8723au/include/HalHWImg8723A_FW.h
new file mode 100644
index 000000000..7ee363b99
--- /dev/null
+++ b/drivers/staging/rtl8723au/include/HalHWImg8723A_FW.h
@@ -0,0 +1,28 @@
+/******************************************************************************
+*
+* Copyright(c) 2007 - 2012 Realtek Corporation. All rights reserved.
+*
+* This program is free software; you can redistribute it and/or modify it
+* under the terms of version 2 of the GNU General Public License as
+* published by the Free Software Foundation.
+*
+* 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.
+*
+*
+******************************************************************************/
+
+#ifndef __INC_FW_8723A_HW_IMG_H
+#define __INC_FW_8723A_HW_IMG_H
+
+
+/******************************************************************************
+* rtl8723fw_B.TXT
+******************************************************************************/
+
+void ODM_ReadFirmware_8723A_rtl8723fw_B(struct dm_odm_t *pDM_Odm,
+ u8 *pFirmware, u32 *pFirmwareSize);
+
+#endif /* end of HWIMG_SUPPORT */
diff --git a/drivers/staging/rtl8723au/include/HalHWImg8723A_MAC.h b/drivers/staging/rtl8723au/include/HalHWImg8723A_MAC.h
new file mode 100644
index 000000000..201be1f87
--- /dev/null
+++ b/drivers/staging/rtl8723au/include/HalHWImg8723A_MAC.h
@@ -0,0 +1,26 @@
+/******************************************************************************
+*
+* Copyright(c) 2007 - 2011 Realtek Corporation. All rights reserved.
+*
+* This program is free software; you can redistribute it and/or modify it
+* under the terms of version 2 of the GNU General Public License as
+* published by the Free Software Foundation.
+*
+* 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.
+*
+*
+******************************************************************************/
+
+#ifndef __INC_MAC_8723A_HW_IMG_H
+#define __INC_MAC_8723A_HW_IMG_H
+
+/******************************************************************************
+* MAC_REG.TXT
+******************************************************************************/
+
+void ODM_ReadAndConfig_MAC_REG_8723A(struct dm_odm_t *pDM_Odm);
+
+#endif /* end of HWIMG_SUPPORT */
diff --git a/drivers/staging/rtl8723au/include/HalHWImg8723A_RF.h b/drivers/staging/rtl8723au/include/HalHWImg8723A_RF.h
new file mode 100644
index 000000000..c9af1c375
--- /dev/null
+++ b/drivers/staging/rtl8723au/include/HalHWImg8723A_RF.h
@@ -0,0 +1,25 @@
+/******************************************************************************
+*
+* Copyright(c) 2007 - 2011 Realtek Corporation. All rights reserved.
+*
+* This program is free software; you can redistribute it and/or modify it
+* under the terms of version 2 of the GNU General Public License as
+* published by the Free Software Foundation.
+*
+* 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.
+*
+******************************************************************************/
+
+#ifndef __INC_RF_8723A_HW_IMG_H
+#define __INC_RF_8723A_HW_IMG_H
+
+/******************************************************************************
+* RadioA_1T.TXT
+******************************************************************************/
+
+void ODM_ReadAndConfig_RadioA_1T_8723A(struct dm_odm_t *pDM_Odm);
+
+#endif /* end of HWIMG_SUPPORT */
diff --git a/drivers/staging/rtl8723au/include/HalPwrSeqCmd.h b/drivers/staging/rtl8723au/include/HalPwrSeqCmd.h
new file mode 100644
index 000000000..12e03a36f
--- /dev/null
+++ b/drivers/staging/rtl8723au/include/HalPwrSeqCmd.h
@@ -0,0 +1,130 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2011 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * 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.
+ *
+ *
+ ******************************************************************************/
+#ifndef __HALPWRSEQCMD_H__
+#define __HALPWRSEQCMD_H__
+
+#include <drv_types.h>
+
+/*---------------------------------------------*/
+/*---------------------------------------------*/
+#define PWR_CMD_READ 0x00
+ /* offset: the read register offset */
+ /* msk: the mask of the read value */
+ /* value: N/A, left by 0 */
+ /* note: dirver shall implement this function by read & msk */
+
+#define PWR_CMD_WRITE 0x01
+ /* offset: the read register offset */
+ /* msk: the mask of the write bits */
+ /* value: write value */
+ /* note: driver shall implement this cmd by read & msk after write */
+
+#define PWR_CMD_POLLING 0x02
+ /* offset: the read register offset */
+ /* msk: the mask of the polled value */
+ /* value: the value to be polled, masked by the msd field. */
+ /* note: driver shall implement this cmd by */
+ /* do{ */
+ /* if( (Read(offset) & msk) == (value & msk) ) */
+ /* break; */
+ /* } while(not timeout); */
+
+#define PWR_CMD_DELAY 0x03
+ /* offset: the value to delay */
+ /* msk: N/A */
+ /* value: the unit of delay, 0: us, 1: ms */
+
+#define PWR_CMD_END 0x04
+ /* offset: N/A */
+ /* msk: N/A */
+ /* value: N/A */
+
+/*---------------------------------------------*/
+/* 3 The value of base: 4 bits */
+/*---------------------------------------------*/
+ /* define the base address of each block */
+#define PWR_BASEADDR_MAC 0x00
+#define PWR_BASEADDR_USB 0x01
+#define PWR_BASEADDR_PCIE 0x02
+#define PWR_BASEADDR_SDIO 0x03
+
+/*---------------------------------------------*/
+/* 3 The value of interface_msk: 4 bits */
+/*---------------------------------------------*/
+#define PWR_INTF_SDIO_MSK BIT(0)
+#define PWR_INTF_USB_MSK BIT(1)
+#define PWR_INTF_PCI_MSK BIT(2)
+#define PWR_INTF_ALL_MSK (BIT(0)|BIT(1)|BIT(2)|BIT(3))
+
+/*---------------------------------------------*/
+/* 3 The value of fab_msk: 4 bits */
+/*---------------------------------------------*/
+#define PWR_FAB_TSMC_MSK BIT(0)
+#define PWR_FAB_UMC_MSK BIT(1)
+#define PWR_FAB_ALL_MSK (BIT(0)|BIT(1)|BIT(2)|BIT(3))
+
+/*---------------------------------------------*/
+/* 3 The value of cut_msk: 8 bits */
+/*---------------------------------------------*/
+#define PWR_CUT_TESTCHIP_MSK BIT(0)
+#define PWR_CUT_A_MSK BIT(1)
+#define PWR_CUT_B_MSK BIT(2)
+#define PWR_CUT_C_MSK BIT(3)
+#define PWR_CUT_D_MSK BIT(4)
+#define PWR_CUT_E_MSK BIT(5)
+#define PWR_CUT_F_MSK BIT(6)
+#define PWR_CUT_G_MSK BIT(7)
+#define PWR_CUT_ALL_MSK 0xFF
+
+
+enum pwrseq_delay_unit {
+ PWRSEQ_DELAY_US,
+ PWRSEQ_DELAY_MS,
+};
+
+struct wlan_pwr_cfg {
+ u16 offset;
+ u8 cut_msk;
+ u8 fab_msk:4;
+ u8 interface_msk:4;
+ u8 base:4;
+ u8 cmd:4;
+ u8 msk;
+ u8 value;
+};
+
+
+#define GET_PWR_CFG_OFFSET(__PWR_CMD) __PWR_CMD.offset
+#define GET_PWR_CFG_CUT_MASK(__PWR_CMD) __PWR_CMD.cut_msk
+#define GET_PWR_CFG_FAB_MASK(__PWR_CMD) __PWR_CMD.fab_msk
+#define GET_PWR_CFG_INTF_MASK(__PWR_CMD) __PWR_CMD.interface_msk
+#define GET_PWR_CFG_BASE(__PWR_CMD) __PWR_CMD.base
+#define GET_PWR_CFG_CMD(__PWR_CMD) __PWR_CMD.cmd
+#define GET_PWR_CFG_MASK(__PWR_CMD) __PWR_CMD.msk
+#define GET_PWR_CFG_VALUE(__PWR_CMD) __PWR_CMD.value
+
+
+/* */
+/* Prototype of protected function. */
+/* */
+u8 HalPwrSeqCmdParsing23a(
+ struct rtw_adapter *padapter,
+ u8 CutVersion,
+ u8 FabVersion,
+ u8 InterfaceType,
+ struct wlan_pwr_cfg PwrCfgCmd[]);
+
+#endif
diff --git a/drivers/staging/rtl8723au/include/HalVerDef.h b/drivers/staging/rtl8723au/include/HalVerDef.h
new file mode 100644
index 000000000..2a0e4ea7a
--- /dev/null
+++ b/drivers/staging/rtl8723au/include/HalVerDef.h
@@ -0,0 +1,114 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2011 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * 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.
+ *
+ ******************************************************************************/
+#ifndef __HAL_VERSION_DEF_H__
+#define __HAL_VERSION_DEF_H__
+
+enum hal_ic_type {
+ CHIP_8192S = 0,
+ CHIP_8188C = 1,
+ CHIP_8192C = 2,
+ CHIP_8192D = 3,
+ CHIP_8723A = 4,
+ CHIP_8188E = 5,
+ CHIP_8881A = 6,
+ CHIP_8812A = 7,
+ CHIP_8821A = 8,
+ CHIP_8723B = 9,
+ CHIP_8192E = 10,
+};
+
+enum hal_chip_type {
+ TEST_CHIP = 0,
+ NORMAL_CHIP = 1,
+ FPGA = 2,
+};
+
+enum hal_cut_version {
+ A_CUT_VERSION = 0,
+ B_CUT_VERSION = 1,
+ C_CUT_VERSION = 2,
+ D_CUT_VERSION = 3,
+ E_CUT_VERSION = 4,
+ F_CUT_VERSION = 5,
+ G_CUT_VERSION = 6,
+};
+
+/* HAL_Manufacturer */
+enum hal_vendor {
+ CHIP_VENDOR_TSMC = 0,
+ CHIP_VENDOR_UMC = 1,
+};
+
+struct hal_version {
+ enum hal_ic_type ICType;
+ enum hal_chip_type ChipType;
+ enum hal_cut_version CUTVersion;
+ enum hal_vendor VendorType;
+ u8 ROMVer;
+};
+
+/* Get element */
+#define GET_CVID_IC_TYPE(version) ((version).ICType)
+#define GET_CVID_CHIP_TYPE(version) ((version).ChipType)
+#define GET_CVID_MANUFACTUER(version) ((version).VendorType)
+#define GET_CVID_CUT_VERSION(version) ((version).CUTVersion)
+#define GET_CVID_ROM_VERSION(version) (((version).ROMVer) & ROM_VERSION_MASK)
+
+/* Common Macro. -- */
+
+#define IS_81XXC(version) \
+ (((GET_CVID_IC_TYPE(version) == CHIP_8192C) || \
+ (GET_CVID_IC_TYPE(version) == CHIP_8188C)) ? true : false)
+#define IS_8723_SERIES(version) \
+ ((GET_CVID_IC_TYPE(version) == CHIP_8723A) ? true : false)
+
+#define IS_TEST_CHIP(version) \
+ ((GET_CVID_CHIP_TYPE(version) == TEST_CHIP) ? true : false)
+#define IS_NORMAL_CHIP(version) \
+ ((GET_CVID_CHIP_TYPE(version) == NORMAL_CHIP) ? true : false)
+
+#define IS_A_CUT(version) \
+ ((GET_CVID_CUT_VERSION(version) == A_CUT_VERSION) ? true : false)
+#define IS_B_CUT(version) \
+ ((GET_CVID_CUT_VERSION(version) == B_CUT_VERSION) ? true : false)
+#define IS_C_CUT(version) \
+ ((GET_CVID_CUT_VERSION(version) == C_CUT_VERSION) ? true : false)
+#define IS_D_CUT(version) \
+ ((GET_CVID_CUT_VERSION(version) == D_CUT_VERSION) ? true : false)
+#define IS_E_CUT(version) \
+ ((GET_CVID_CUT_VERSION(version) == E_CUT_VERSION) ? true : false)
+
+#define IS_CHIP_VENDOR_TSMC(version) \
+ ((GET_CVID_MANUFACTUER(version) == CHIP_VENDOR_TSMC) ? true : false)
+#define IS_CHIP_VENDOR_UMC(version) \
+ ((GET_CVID_MANUFACTUER(version) == CHIP_VENDOR_UMC) ? true : false)
+
+/* Chip version Macro. -- */
+
+#define IS_81xxC_VENDOR_UMC_A_CUT(version) \
+ (IS_81XXC(version)?(IS_CHIP_VENDOR_UMC(version) ? \
+ (IS_A_CUT(version) ? true : false) : false) : false)
+#define IS_81xxC_VENDOR_UMC_B_CUT(version) \
+ (IS_81XXC(version) ? (IS_CHIP_VENDOR_UMC(version) ? \
+ (IS_B_CUT(version) ? true : false) : false): false)
+#define IS_81xxC_VENDOR_UMC_C_CUT(version) \
+ (IS_81XXC(version)?(IS_CHIP_VENDOR_UMC(version) ? \
+ (IS_C_CUT(version) ? true : false) : false) : false)
+#define IS_8723A_A_CUT(version) \
+ ((IS_8723_SERIES(version)) ? (IS_A_CUT(version) ? true : false) : false)
+#define IS_8723A_B_CUT(version) \
+ ((IS_8723_SERIES(version)) ? (IS_B_CUT(version) ? true : false) : false)
+
+#endif
diff --git a/drivers/staging/rtl8723au/include/drv_types.h b/drivers/staging/rtl8723au/include/drv_types.h
new file mode 100644
index 000000000..e83463aeb
--- /dev/null
+++ b/drivers/staging/rtl8723au/include/drv_types.h
@@ -0,0 +1,274 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2012 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * 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.
+ *
+ ******************************************************************************/
+/*-----------------------------------------------------------------------------
+
+ For type defines and data structure defines
+
+------------------------------------------------------------------------------*/
+
+
+#ifndef __DRV_TYPES_H__
+#define __DRV_TYPES_H__
+
+#include <osdep_service.h>
+#include <wlan_bssdef.h>
+
+
+enum _NIC_VERSION {
+ RTL8711_NIC,
+ RTL8712_NIC,
+ RTL8713_NIC,
+ RTL8716_NIC
+
+};
+
+
+#include <rtw_ht.h>
+
+#include <rtw_cmd.h>
+#include <rtw_xmit.h>
+#include <rtw_recv.h>
+#include <hal_intf.h>
+#include <hal_com.h>
+#include <rtw_security.h>
+#include <rtw_pwrctrl.h>
+#include <rtw_io.h>
+#include <rtw_eeprom.h>
+#include <sta_info.h>
+#include <rtw_mlme.h>
+#include <rtw_debug.h>
+#include <rtw_rf.h>
+#include <rtw_event.h>
+#include <rtw_mlme_ext.h>
+#include <rtw_ap.h>
+
+#include "ioctl_cfg80211.h"
+
+struct registry_priv {
+ u8 chip_version;
+ u8 rfintfs;
+ struct cfg80211_ssid ssid;
+ u8 channel;/* ad-hoc support requirement */
+ u8 wireless_mode;/* A, B, G, auto */
+ u8 scan_mode;/* active, passive */
+ u8 preamble;/* long, short, auto */
+ u8 vrtl_carrier_sense;/* Enable, Disable, Auto */
+ u8 vcs_type;/* RTS/CTS, CTS-to-self */
+ u16 rts_thresh;
+ u16 frag_thresh;
+ u8 adhoc_tx_pwr;
+ u8 soft_ap;
+ u8 power_mgnt;
+ u8 ips_mode;
+ u8 smart_ps;
+ u8 long_retry_lmt;
+ u8 short_retry_lmt;
+ u16 busy_thresh;
+ u8 ack_policy;
+ u8 software_encrypt;
+ u8 software_decrypt;
+ u8 acm_method;
+ /* UAPSD */
+ u8 wmm_enable;
+ u8 uapsd_enable;
+
+ struct wlan_bssid_ex dev_network;
+
+ u8 ht_enable;
+ u8 cbw40_enable;
+ u8 ampdu_enable;/* for tx */
+ u8 rx_stbc;
+ u8 ampdu_amsdu;/* A-MPDU Supports A-MSDU is permitted */
+ u8 lowrate_two_xmit;
+
+ u8 rf_config;
+ u8 low_power;
+
+ u8 wifi_spec;/* !turbo_mode */
+
+ u8 channel_plan;
+#ifdef CONFIG_8723AU_BT_COEXIST
+ u8 btcoex;
+ u8 bt_iso;
+ u8 bt_sco;
+ u8 bt_ampdu;
+#endif
+ bool bAcceptAddbaReq;
+
+ u8 antdiv_cfg;
+ u8 antdiv_type;
+
+ u8 hwpdn_mode;/* 0:disable,1:enable,2:decide by EFUSE config */
+ u8 hwpwrp_detect;/* 0:disable,1:enable */
+
+ u8 hw_wps_pbc;/* 0:disable,1:enable */
+
+ u8 max_roaming_times; /* max number driver will try to roaming */
+
+ u8 enable80211d;
+
+ u8 ifname[16];
+ u8 if2name[16];
+
+ u8 notch_filter;
+
+ u8 regulatory_tid;
+};
+
+
+#define MAX_CONTINUAL_URB_ERR 4
+
+#define GET_PRIMARY_ADAPTER(padapter) \
+ (((struct rtw_adapter *)padapter)->dvobj->if1)
+
+enum _IFACE_ID {
+ IFACE_ID0, /* maping to PRIMARY_ADAPTER */
+ IFACE_ID1, /* maping to SECONDARY_ADAPTER */
+ IFACE_ID2,
+ IFACE_ID3,
+ IFACE_ID_MAX,
+};
+
+struct dvobj_priv {
+ struct rtw_adapter *if1; /* PRIMARY_ADAPTER */
+ struct rtw_adapter *if2; /* SECONDARY_ADAPTER */
+
+ /* for local/global synchronization */
+ struct mutex hw_init_mutex;
+ struct mutex h2c_fwcmd_mutex;
+ struct mutex setch_mutex;
+ struct mutex setbw_mutex;
+
+ unsigned char oper_channel; /* saved chan info when set chan bw */
+ unsigned char oper_bwmode;
+ unsigned char oper_ch_offset;/* PRIME_CHNL_OFFSET */
+
+ struct rtw_adapter *padapters[IFACE_ID_MAX];
+ u8 iface_nums; /* total number of ifaces used runtime */
+
+ /* For 92D, DMDP have 2 interface. */
+ u8 InterfaceNumber;
+ u8 NumInterfaces;
+
+ /* In /Out Pipe information */
+ int RtInPipe[2];
+ int RtOutPipe[3];
+ u8 Queue2Pipe[HW_QUEUE_ENTRY];/* for out pipe mapping */
+
+/*-------- below is for USB INTERFACE --------*/
+
+ u8 nr_endpoint;
+ u8 ishighspeed;
+ u8 RtNumInPipes;
+ u8 RtNumOutPipes;
+ int ep_num[5]; /* endpoint number */
+
+ struct mutex usb_vendor_req_mutex;
+
+ union {
+ __le32 val32;
+ __le16 val16;
+ u8 val8;
+ } usb_buf;
+
+ struct usb_interface *pusbintf;
+ struct usb_device *pusbdev;
+ atomic_t continual_urb_error;
+
+/*-------- below is for PCIE INTERFACE --------*/
+
+};
+
+static inline struct device *dvobj_to_dev(struct dvobj_priv *dvobj)
+{
+ /* todo: get interface type from dvobj and the return the dev accordingly */
+ return &dvobj->pusbintf->dev;
+}
+
+enum _IFACE_TYPE {
+ IFACE_PORT0, /* mapping to port0 for C/D series chips */
+ IFACE_PORT1, /* mapping to port1 for C/D series chip */
+ MAX_IFACE_PORT,
+};
+
+enum _ADAPTER_TYPE {
+ PRIMARY_ADAPTER,
+ SECONDARY_ADAPTER,
+ MAX_ADAPTER,
+};
+
+struct rtw_adapter {
+ int pid[3];/* process id from UI, 0:wps, 1:hostapd, 2:dhcpcd */
+ int bDongle;/* build-in module or external dongle */
+ u16 chip_type;
+ u16 HardwareType;
+
+ struct dvobj_priv *dvobj;
+ struct mlme_priv mlmepriv;
+ struct mlme_ext_priv mlmeextpriv;
+ struct cmd_priv cmdpriv;
+ struct evt_priv evtpriv;
+ struct xmit_priv xmitpriv;
+ struct recv_priv recvpriv;
+ struct sta_priv stapriv;
+ struct security_priv securitypriv;
+ struct registry_priv registrypriv;
+ struct pwrctrl_priv pwrctrlpriv;
+ struct eeprom_priv eeprompriv;
+
+ u32 setband;
+
+ void *HalData;
+
+ s32 bDriverStopped;
+ s32 bSurpriseRemoved;
+ s32 bCardDisableWOHSM;
+
+ u32 IsrContent;
+ u32 ImrContent;
+
+ u8 EepromAddressSize;
+ u8 hw_init_completed;
+ u8 bDriverIsGoingToUnload;
+ u8 init_adpt_in_progress;
+ u8 bHaltInProgress;
+
+ struct net_device *pnetdev;
+
+ /* used by rtw_rereg_nd_name related function */
+ int bup;
+ struct net_device_stats stats;
+
+ struct wireless_dev *rtw_wdev;
+ int net_closed;
+
+ u8 bFWReady;
+ u8 bReadPortCancel;
+ u8 bWritePortCancel;
+
+ /* extend to support multi interface */
+ /* IFACE_ID0 is equals to PRIMARY_ADAPTER */
+ /* IFACE_ID1 is equals to SECONDARY_ADAPTER */
+ u8 iface_id;
+};
+
+#define adapter_to_dvobj(adapter) (adapter->dvobj)
+
+static inline u8 *myid(struct eeprom_priv *peepriv)
+{
+ return peepriv->mac_addr;
+}
+
+#endif /* __DRV_TYPES_H__ */
diff --git a/drivers/staging/rtl8723au/include/hal_com.h b/drivers/staging/rtl8723au/include/hal_com.h
new file mode 100644
index 000000000..9c50320b2
--- /dev/null
+++ b/drivers/staging/rtl8723au/include/hal_com.h
@@ -0,0 +1,182 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2011 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * 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.
+ *
+ ******************************************************************************/
+#ifndef __HAL_COMMON_H__
+#define __HAL_COMMON_H__
+
+/* */
+/* Rate Definition */
+/* */
+/* CCK */
+#define RATR_1M 0x00000001
+#define RATR_2M 0x00000002
+#define RATR_55M 0x00000004
+#define RATR_11M 0x00000008
+/* OFDM */
+#define RATR_6M 0x00000010
+#define RATR_9M 0x00000020
+#define RATR_12M 0x00000040
+#define RATR_18M 0x00000080
+#define RATR_24M 0x00000100
+#define RATR_36M 0x00000200
+#define RATR_48M 0x00000400
+#define RATR_54M 0x00000800
+/* MCS 1 Spatial Stream */
+#define RATR_MCS0 0x00001000
+#define RATR_MCS1 0x00002000
+#define RATR_MCS2 0x00004000
+#define RATR_MCS3 0x00008000
+#define RATR_MCS4 0x00010000
+#define RATR_MCS5 0x00020000
+#define RATR_MCS6 0x00040000
+#define RATR_MCS7 0x00080000
+/* MCS 2 Spatial Stream */
+#define RATR_MCS8 0x00100000
+#define RATR_MCS9 0x00200000
+#define RATR_MCS10 0x00400000
+#define RATR_MCS11 0x00800000
+#define RATR_MCS12 0x01000000
+#define RATR_MCS13 0x02000000
+#define RATR_MCS14 0x04000000
+#define RATR_MCS15 0x08000000
+
+/* CCK */
+#define RATE_1M BIT(0)
+#define RATE_2M BIT(1)
+#define RATE_5_5M BIT(2)
+#define RATE_11M BIT(3)
+/* OFDM */
+#define RATE_6M BIT(4)
+#define RATE_9M BIT(5)
+#define RATE_12M BIT(6)
+#define RATE_18M BIT(7)
+#define RATE_24M BIT(8)
+#define RATE_36M BIT(9)
+#define RATE_48M BIT(10)
+#define RATE_54M BIT(11)
+
+/*------------------------------ Tx Desc definition Macro ------------------------*/
+/* pragma mark -- Tx Desc related definition. -- */
+/* */
+/* */
+/* Rate */
+/* */
+/* CCK Rates, TxHT = 0 */
+#define DESC_RATE1M 0x00
+#define DESC_RATE2M 0x01
+#define DESC_RATE5_5M 0x02
+#define DESC_RATE11M 0x03
+
+/* OFDM Rates, TxHT = 0 */
+#define DESC_RATE6M 0x04
+#define DESC_RATE9M 0x05
+#define DESC_RATE12M 0x06
+#define DESC_RATE18M 0x07
+#define DESC_RATE24M 0x08
+#define DESC_RATE36M 0x09
+#define DESC_RATE48M 0x0a
+#define DESC_RATE54M 0x0b
+
+/* MCS Rates, TxHT = 1 */
+#define DESC_RATEMCS0 0x0c
+#define DESC_RATEMCS1 0x0d
+#define DESC_RATEMCS2 0x0e
+#define DESC_RATEMCS3 0x0f
+#define DESC_RATEMCS4 0x10
+#define DESC_RATEMCS5 0x11
+#define DESC_RATEMCS6 0x12
+#define DESC_RATEMCS7 0x13
+#define DESC_RATEMCS8 0x14
+#define DESC_RATEMCS9 0x15
+#define DESC_RATEMCS10 0x16
+#define DESC_RATEMCS11 0x17
+#define DESC_RATEMCS12 0x18
+#define DESC_RATEMCS13 0x19
+#define DESC_RATEMCS14 0x1a
+#define DESC_RATEMCS15 0x1b
+#define DESC_RATEMCS15_SG 0x1c
+#define DESC_RATEMCS32 0x20
+
+#define REG_P2P_CTWIN 0x0572 /* 1 Byte long (in unit of TU) */
+#define REG_NOA_DESC_SEL 0x05CF
+#define REG_NOA_DESC_DURATION 0x05E0
+#define REG_NOA_DESC_INTERVAL 0x05E4
+#define REG_NOA_DESC_START 0x05E8
+#define REG_NOA_DESC_COUNT 0x05EC
+
+#include "HalVerDef.h"
+
+
+u8 /* return the final channel plan decision */
+hal_com_get_channel_plan23a(
+ struct rtw_adapter *padapter,
+ u8 hw_channel_plan, /* channel plan from HW (efuse/eeprom) */
+ u8 sw_channel_plan, /* channel plan from SW (registry/module param) */
+ u8 def_channel_plan, /* channel plan used when the former two is invalid */
+ bool AutoLoadFail
+ );
+
+u8 MRateToHwRate23a(u8 rate);
+
+void HalSetBrateCfg23a(struct rtw_adapter *padapter, u8 *mBratesOS);
+
+bool
+Hal_MappingOutPipe23a(struct rtw_adapter *pAdapter, u8 NumOutPipe);
+
+void c2h_evt_clear23a(struct rtw_adapter *adapter);
+s32 c2h_evt_read23a(struct rtw_adapter *adapter, u8 *buf);
+
+void rtl8723a_set_ampdu_min_space(struct rtw_adapter *padapter, u8 MinSpacingToSet);
+void rtl8723a_set_ampdu_factor(struct rtw_adapter *padapter, u8 FactorToSet);
+void rtl8723a_set_acm_ctrl(struct rtw_adapter *padapter, u8 ctrl);
+void rtl8723a_set_media_status(struct rtw_adapter *padapter, u8 status);
+void rtl8723a_set_media_status1(struct rtw_adapter *padapter, u8 status);
+void rtl8723a_set_bcn_func(struct rtw_adapter *padapter, u8 val);
+void rtl8723a_check_bssid(struct rtw_adapter *padapter, u8 val);
+void rtl8723a_mlme_sitesurvey(struct rtw_adapter *padapter, u8 flag);
+void rtl8723a_on_rcr_am(struct rtw_adapter *padapter);
+void rtl8723a_off_rcr_am(struct rtw_adapter *padapter);
+void rtl8723a_set_slot_time(struct rtw_adapter *padapter, u8 slottime);
+void rtl8723a_ack_preamble(struct rtw_adapter *padapter, u8 bShortPreamble);
+void rtl8723a_set_sec_cfg(struct rtw_adapter *padapter, u8 sec);
+void rtl8723a_cam_empty_entry(struct rtw_adapter *padapter, u8 ucIndex);
+void rtl8723a_cam_invalidate_all(struct rtw_adapter *padapter);
+void rtl8723a_cam_write(struct rtw_adapter *padapter,
+ u8 entry, u16 ctrl, const u8 *mac, const u8 *key);
+void rtl8723a_fifo_cleanup(struct rtw_adapter *padapter);
+void rtl8723a_set_apfm_on_mac(struct rtw_adapter *padapter, u8 val);
+void rtl8723a_bcn_valid(struct rtw_adapter *padapter);
+bool rtl8723a_get_bcn_valid(struct rtw_adapter *padapter);
+void rtl8723a_set_beacon_interval(struct rtw_adapter *padapter, u16 interval);
+void rtl8723a_set_resp_sifs(struct rtw_adapter *padapter,
+ u8 r2t1, u8 r2t2, u8 t2t1, u8 t2t2);
+void rtl8723a_set_ac_param_vo(struct rtw_adapter *padapter, u32 vo);
+void rtl8723a_set_ac_param_vi(struct rtw_adapter *padapter, u32 vi);
+void rtl8723a_set_ac_param_be(struct rtw_adapter *padapter, u32 be);
+void rtl8723a_set_ac_param_bk(struct rtw_adapter *padapter, u32 bk);
+void rtl8723a_set_rxdma_agg_pg_th(struct rtw_adapter *padapter, u8 val);
+void rtl8723a_set_initial_gain(struct rtw_adapter *padapter, u32 rx_gain);
+
+void rtl8723a_odm_support_ability_write(struct rtw_adapter *padapter, u32 val);
+void rtl8723a_odm_support_ability_backup(struct rtw_adapter *padapter);
+void rtl8723a_odm_support_ability_restore(struct rtw_adapter *padapter);
+void rtl8723a_odm_support_ability_set(struct rtw_adapter *padapter, u32 val);
+void rtl8723a_odm_support_ability_clr(struct rtw_adapter *padapter, u32 val);
+
+void rtl8723a_set_rpwm(struct rtw_adapter *padapter, u8 val);
+u8 rtl8723a_get_rf_type(struct rtw_adapter *padapter);
+bool rtl8723a_get_fwlps_rf_on(struct rtw_adapter *padapter);
+bool rtl8723a_chk_hi_queue_empty(struct rtw_adapter *padapter);
+
+#endif /* __HAL_COMMON_H__ */
diff --git a/drivers/staging/rtl8723au/include/hal_intf.h b/drivers/staging/rtl8723au/include/hal_intf.h
new file mode 100644
index 000000000..b924d47fc
--- /dev/null
+++ b/drivers/staging/rtl8723au/include/hal_intf.h
@@ -0,0 +1,115 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2012 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * 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.
+ *
+ ******************************************************************************/
+#ifndef __HAL_INTF_H__
+#define __HAL_INTF_H__
+
+#include <osdep_service.h>
+#include <drv_types.h>
+
+enum _CHIP_TYPE {
+ NULL_CHIP_TYPE,
+ RTL8712_8188S_8191S_8192S,
+ RTL8188C_8192C,
+ RTL8192D,
+ RTL8723A,
+ RTL8188E,
+ MAX_CHIP_TYPE
+};
+
+enum hal_def_variable {
+ HAL_DEF_UNDERCORATEDSMOOTHEDPWDB,
+ HAL_DEF_IS_SUPPORT_ANT_DIV,
+ HAL_DEF_CURRENT_ANTENNA,
+ HAL_DEF_DRVINFO_SZ,
+ HAL_DEF_MAX_RECVBUF_SZ,
+ HAL_DEF_RX_PACKET_OFFSET,
+ HAL_DEF_DBG_DUMP_RXPKT,/* for dbg */
+ HAL_DEF_DBG_DM_FUNC,/* for dbg */
+ HAL_DEF_RA_DECISION_RATE,
+ HAL_DEF_RA_SGI,
+ HAL_DEF_PT_PWR_STATUS,
+ HW_VAR_MAX_RX_AMPDU_FACTOR,
+ HW_DEF_RA_INFO_DUMP,
+ HAL_DEF_DBG_DUMP_TXPKT,
+ HW_DEF_FA_CNT_DUMP,
+ HW_DEF_ODM_DBG_FLAG,
+};
+
+enum hal_odm_variable {
+ HAL_ODM_STA_INFO,
+ HAL_ODM_P2P_STATE,
+ HAL_ODM_WIFI_DISPLAY_STATE,
+};
+
+enum rt_eeprom_type {
+ EEPROM_93C46,
+ EEPROM_93C56,
+ EEPROM_BOOT_EFUSE,
+};
+
+
+
+#define RF_CHANGE_BY_INIT 0
+#define RF_CHANGE_BY_IPS BIT(28)
+#define RF_CHANGE_BY_PS BIT(29)
+#define RF_CHANGE_BY_HW BIT(30)
+#define RF_CHANGE_BY_SW BIT(31)
+
+enum hardware_type {
+ HARDWARE_TYPE_RTL8180,
+ HARDWARE_TYPE_RTL8185,
+ HARDWARE_TYPE_RTL8187,
+ HARDWARE_TYPE_RTL8188,
+ HARDWARE_TYPE_RTL8190P,
+ HARDWARE_TYPE_RTL8192E,
+ HARDWARE_TYPE_RTL819xU,
+ HARDWARE_TYPE_RTL8192SE,
+ HARDWARE_TYPE_RTL8192SU,
+ HARDWARE_TYPE_RTL8192CE,
+ HARDWARE_TYPE_RTL8192CU,
+ HARDWARE_TYPE_RTL8192DE,
+ HARDWARE_TYPE_RTL8192DU,
+ HARDWARE_TYPE_RTL8723AE,
+ HARDWARE_TYPE_RTL8723AU,
+ HARDWARE_TYPE_RTL8723AS,
+ HARDWARE_TYPE_RTL8188EE,
+ HARDWARE_TYPE_RTL8188EU,
+ HARDWARE_TYPE_RTL8188ES,
+ HARDWARE_TYPE_MAX,
+};
+
+#define GET_EEPROM_EFUSE_PRIV(adapter) (&adapter->eeprompriv)
+
+void rtw_hal_def_value_init23a(struct rtw_adapter *padapter);
+int pm_netdev_open23a(struct net_device *pnetdev, u8 bnormal);
+
+int rtl8723au_hal_init(struct rtw_adapter *padapter);
+int rtl8723au_hal_deinit(struct rtw_adapter *padapter);
+void rtw_hal_stop(struct rtw_adapter *padapter);
+
+void rtw_hal_update_ra_mask23a(struct sta_info *psta, u8 rssi_level);
+void rtw_hal_clone_data(struct rtw_adapter *dst_padapter, struct rtw_adapter *src_padapter);
+
+void hw_var_set_correct_tsf(struct rtw_adapter *padapter);
+void hw_var_set_mlme_disconnect(struct rtw_adapter *padapter);
+void hw_var_set_opmode(struct rtw_adapter *padapter, u8 mode);
+void hw_var_set_macaddr(struct rtw_adapter *padapter, u8 *val);
+void hw_var_set_bssid(struct rtw_adapter *padapter, u8 *val);
+void hw_var_set_mlme_join(struct rtw_adapter *padapter, u8 type);
+
+int GetHalDefVar8192CUsb(struct rtw_adapter *Adapter,
+ enum hal_def_variable eVariable, void *pValue);
+
+#endif /* __HAL_INTF_H__ */
diff --git a/drivers/staging/rtl8723au/include/ieee80211.h b/drivers/staging/rtl8723au/include/ieee80211.h
new file mode 100644
index 000000000..3aa40a325
--- /dev/null
+++ b/drivers/staging/rtl8723au/include/ieee80211.h
@@ -0,0 +1,341 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2011 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * 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.
+ *
+ ******************************************************************************/
+#ifndef __IEEE80211_H
+#define __IEEE80211_H
+
+#include <osdep_service.h>
+#include <drv_types.h>
+#include "linux/ieee80211.h"
+#include "wifi.h"
+
+#include <linux/wireless.h>
+
+#if (WIRELESS_EXT < 22)
+#error "Obsolete pre 2007 wireless extensions are not supported"
+#endif
+
+
+#ifdef CONFIG_8723AU_AP_MODE
+
+/* STA flags */
+#define WLAN_STA_AUTH BIT(0)
+#define WLAN_STA_ASSOC BIT(1)
+#define WLAN_STA_PS BIT(2)
+#define WLAN_STA_TIM BIT(3)
+#define WLAN_STA_PERM BIT(4)
+#define WLAN_STA_AUTHORIZED BIT(5)
+#define WLAN_STA_PENDING_POLL BIT(6) /* pending activity poll not ACKed */
+#define WLAN_STA_SHORT_PREAMBLE BIT(7)
+#define WLAN_STA_PREAUTH BIT(8)
+#define WLAN_STA_WME BIT(9)
+#define WLAN_STA_MFP BIT(10)
+#define WLAN_STA_HT BIT(11)
+#define WLAN_STA_WPS BIT(12)
+#define WLAN_STA_MAYBE_WPS BIT(13)
+#define WLAN_STA_NONERP BIT(31)
+
+#endif
+
+#define WPA_CIPHER_NONE BIT(0)
+#define WPA_CIPHER_WEP40 BIT(1)
+#define WPA_CIPHER_WEP104 BIT(2)
+#define WPA_CIPHER_TKIP BIT(3)
+#define WPA_CIPHER_CCMP BIT(4)
+
+
+
+#define WPA_SELECTOR_LEN 4
+extern u8 RTW_WPA_OUI23A_TYPE[] ;
+extern u16 RTW_WPA_VERSION23A ;
+extern u8 WPA_AUTH_KEY_MGMT_NONE23A[];
+extern u8 WPA_AUTH_KEY_MGMT_UNSPEC_802_1X23A[];
+extern u8 WPA_AUTH_KEY_MGMT_PSK_OVER_802_1X23A[];
+extern u8 WPA_CIPHER_SUITE_NONE23A[];
+extern u8 WPA_CIPHER_SUITE_WEP4023A[];
+extern u8 WPA_CIPHER_SUITE_TKIP23A[];
+extern u8 WPA_CIPHER_SUITE_WRAP23A[];
+extern u8 WPA_CIPHER_SUITE_CCMP23A[];
+extern u8 WPA_CIPHER_SUITE_WEP10423A[];
+
+
+#define RSN_HEADER_LEN 4
+#define RSN_SELECTOR_LEN 4
+
+extern u8 RSN_AUTH_KEY_MGMT_UNSPEC_802_1X23A[];
+extern u8 RSN_AUTH_KEY_MGMT_PSK_OVER_802_1X23A[];
+extern u8 RSN_CIPHER_SUITE_NONE23A[];
+extern u8 RSN_CIPHER_SUITE_WEP4023A[];
+extern u8 RSN_CIPHER_SUITE_TKIP23A[];
+extern u8 RSN_CIPHER_SUITE_WRAP23A[];
+extern u8 RSN_CIPHER_SUITE_CCMP23A[];
+extern u8 RSN_CIPHER_SUITE_WEP10423A[];
+
+enum ratr_table_mode {
+ RATR_INX_WIRELESS_NGB = 0, /* BGN 40 Mhz 2SS 1SS */
+ RATR_INX_WIRELESS_NG = 1, /* GN or N */
+ RATR_INX_WIRELESS_NB = 2, /* BGN 20 Mhz 2SS 1SS or BN */
+ RATR_INX_WIRELESS_N = 3,
+ RATR_INX_WIRELESS_GB = 4,
+ RATR_INX_WIRELESS_G = 5,
+ RATR_INX_WIRELESS_B = 6,
+ RATR_INX_WIRELESS_MC = 7,
+ RATR_INX_WIRELESS_AC_N = 8,
+};
+
+enum NETWORK_TYPE
+{
+ WIRELESS_INVALID = 0,
+ /* Sub-Element */
+ /* tx: cck only , rx: cck only, hw: cck */
+ WIRELESS_11B = BIT(0),
+ /* tx: ofdm only, rx: ofdm & cck, hw: cck & ofdm */
+ WIRELESS_11G = BIT(1),
+ /* tx: ofdm only, rx: ofdm only, hw: ofdm only */
+ WIRELESS_11A = BIT(2),
+ /* tx: MCS only, rx: MCS & cck, hw: MCS & cck */
+ WIRELESS_11_24N = BIT(3),
+ /* tx: MCS only, rx: MCS & ofdm, hw: ofdm only */
+ WIRELESS_11_5N = BIT(4),
+ /* WIRELESS_AUTO = BIT(5), */
+ WIRELESS_AC = BIT(6),
+
+ /* Combination */
+ /* tx: cck & ofdm, rx: cck & ofdm & MCS, hw: cck & ofdm */
+ WIRELESS_11BG = WIRELESS_11B|WIRELESS_11G,
+ /* tx: ofdm & MCS, rx: ofdm & cck & MCS, hw: cck & ofdm */
+ WIRELESS_11G_24N = WIRELESS_11G | WIRELESS_11_24N,
+ /* tx: ofdm & MCS, rx: ofdm & MCS, hw: ofdm only */
+ WIRELESS_11A_5N = WIRELESS_11A | WIRELESS_11_5N,
+ /* tx: ofdm & cck & MCS, rx: ofdm & cck & MCS, hw: ofdm & cck */
+ WIRELESS_11BG_24N = WIRELESS_11B | WIRELESS_11G | WIRELESS_11_24N,
+ /* tx: ofdm & MCS, rx: ofdm & MCS, hw: ofdm only */
+ WIRELESS_11AGN = WIRELESS_11A | WIRELESS_11G | WIRELESS_11_24N |
+ WIRELESS_11_5N,
+ WIRELESS_11ABGN = WIRELESS_11A | WIRELESS_11B | WIRELESS_11G |
+ WIRELESS_11_24N | WIRELESS_11_5N,
+};
+
+#define SUPPORTED_24G_NETTYPE_MSK (WIRELESS_11B | WIRELESS_11G | WIRELESS_11_24N)
+#define SUPPORTED_5G_NETTYPE_MSK (WIRELESS_11A | WIRELESS_11_5N)
+
+#define IsSupported24G(NetType) (NetType & SUPPORTED_24G_NETTYPE_MSK ? true : false)
+#define IsSupported5G(NetType) (NetType & SUPPORTED_5G_NETTYPE_MSK ? true : false)
+
+#define IsEnableHWCCK(NetType) IsSupported24G(NetType)
+#define IsEnableHWOFDM(NetType) (NetType & (WIRELESS_11G|WIRELESS_11_24N|SUPPORTED_5G_NETTYPE_MSK) ? true : false)
+
+#define IsSupportedRxCCK(NetType) IsEnableHWCCK(NetType)
+#define IsSupportedRxOFDM(NetType) IsEnableHWOFDM(NetType)
+#define IsSupportedRxMCS(NetType) IsEnableHWOFDM(NetType)
+
+#define IsSupportedTxCCK(NetType) (NetType & (WIRELESS_11B) ? true : false)
+#define IsSupportedTxOFDM(NetType) (NetType & (WIRELESS_11G|WIRELESS_11A) ? true : false)
+#define IsSupportedTxMCS(NetType) (NetType & (WIRELESS_11_24N|WIRELESS_11_5N) ? true : false)
+
+
+#define MIN_FRAG_THRESHOLD 256U
+#define MAX_FRAG_THRESHOLD 2346U
+
+/* QoS,QOS */
+#define NORMAL_ACK 0
+#define NO_ACK 1
+#define NON_EXPLICIT_ACK 2
+#define BLOCK_ACK 3
+
+/* IEEE 802.11 defines */
+
+#define P80211_OUI_LEN 3
+
+struct ieee80211_snap_hdr {
+ u8 dsap; /* always 0xAA */
+ u8 ssap; /* always 0xAA */
+ u8 ctrl; /* always 0x03 */
+ u8 oui[P80211_OUI_LEN]; /* organizational universal id */
+} __attribute__ ((packed));
+
+
+#define SNAP_SIZE sizeof(struct ieee80211_snap_hdr)
+
+#define WLAN_REASON_JOIN_WRONG_CHANNEL 65534
+#define WLAN_REASON_EXPIRATION_CHK 65535
+
+#define IEEE80211_CCK_RATE_LEN 4
+#define IEEE80211_NUM_OFDM_RATESLEN 8
+
+
+#define IEEE80211_CCK_RATE_1MB 0x02
+#define IEEE80211_CCK_RATE_2MB 0x04
+#define IEEE80211_CCK_RATE_5MB 0x0B
+#define IEEE80211_CCK_RATE_11MB 0x16
+#define IEEE80211_OFDM_RATE_LEN 8
+#define IEEE80211_OFDM_RATE_6MB 0x0C
+#define IEEE80211_OFDM_RATE_9MB 0x12
+#define IEEE80211_OFDM_RATE_12MB 0x18
+#define IEEE80211_OFDM_RATE_18MB 0x24
+#define IEEE80211_OFDM_RATE_24MB 0x30
+#define IEEE80211_OFDM_RATE_36MB 0x48
+#define IEEE80211_OFDM_RATE_48MB 0x60
+#define IEEE80211_OFDM_RATE_54MB 0x6C
+#define IEEE80211_BASIC_RATE_MASK 0x80
+
+#define IEEE80211_CCK_RATE_1MB_MASK (1<<0)
+#define IEEE80211_CCK_RATE_2MB_MASK (1<<1)
+#define IEEE80211_CCK_RATE_5MB_MASK (1<<2)
+#define IEEE80211_CCK_RATE_11MB_MASK (1<<3)
+#define IEEE80211_OFDM_RATE_6MB_MASK (1<<4)
+#define IEEE80211_OFDM_RATE_9MB_MASK (1<<5)
+#define IEEE80211_OFDM_RATE_12MB_MASK (1<<6)
+#define IEEE80211_OFDM_RATE_18MB_MASK (1<<7)
+#define IEEE80211_OFDM_RATE_24MB_MASK (1<<8)
+#define IEEE80211_OFDM_RATE_36MB_MASK (1<<9)
+#define IEEE80211_OFDM_RATE_48MB_MASK (1<<10)
+#define IEEE80211_OFDM_RATE_54MB_MASK (1<<11)
+
+#define IEEE80211_CCK_RATES_MASK 0x0000000F
+#define IEEE80211_CCK_BASIC_RATES_MASK (IEEE80211_CCK_RATE_1MB_MASK | \
+ IEEE80211_CCK_RATE_2MB_MASK)
+#define IEEE80211_CCK_DEFAULT_RATES_MASK (IEEE80211_CCK_BASIC_RATES_MASK | \
+ IEEE80211_CCK_RATE_5MB_MASK | \
+ IEEE80211_CCK_RATE_11MB_MASK)
+
+#define IEEE80211_OFDM_RATES_MASK 0x00000FF0
+#define IEEE80211_OFDM_BASIC_RATES_MASK (IEEE80211_OFDM_RATE_6MB_MASK | \
+ IEEE80211_OFDM_RATE_12MB_MASK | \
+ IEEE80211_OFDM_RATE_24MB_MASK)
+#define IEEE80211_OFDM_DEFAULT_RATES_MASK (IEEE80211_OFDM_BASIC_RATES_MASK | \
+ IEEE80211_OFDM_RATE_9MB_MASK | \
+ IEEE80211_OFDM_RATE_18MB_MASK | \
+ IEEE80211_OFDM_RATE_36MB_MASK | \
+ IEEE80211_OFDM_RATE_48MB_MASK | \
+ IEEE80211_OFDM_RATE_54MB_MASK)
+#define IEEE80211_DEFAULT_RATES_MASK (IEEE80211_OFDM_DEFAULT_RATES_MASK | \
+ IEEE80211_CCK_DEFAULT_RATES_MASK)
+
+#define IEEE80211_NUM_OFDM_RATES 8
+#define IEEE80211_NUM_CCK_RATES 4
+#define IEEE80211_OFDM_SHIFT_MASK_A 4
+
+#define WEP_KEYS 4
+
+
+/* MAX_RATES_LENGTH needs to be 12. The spec says 8, and many APs
+ * only use 8, and then use extended rates for the remaining supported
+ * rates. Other APs, however, stick all of their supported rates on the
+ * main rates information element... */
+#define MAX_RATES_LENGTH 12
+#define MAX_RATES_EX_LENGTH 16
+#define MAX_CHANNEL_NUMBER 161
+#define RTW_CH_MAX_2G_CHANNEL 14 /* Max channel in 2G band */
+
+#define MAX_WPA_IE_LEN 256
+#define MAX_WPS_IE_LEN 256
+#define MAX_P2P_IE_LEN 256
+#define MAX_WFD_IE_LEN 128
+
+/*
+join_res:
+-1: authentication fail
+-2: association fail
+> 0: TID
+*/
+
+#define MAXTID 16
+
+#define WME_OUI_TYPE 2
+#define WME_OUI_SUBTYPE_INFORMATION_ELEMENT 0
+#define WME_OUI_SUBTYPE_PARAMETER_ELEMENT 1
+#define WME_OUI_SUBTYPE_TSPEC_ELEMENT 2
+#define WME_VERSION 1
+
+
+#define OUI_BROADCOM 0x00904c /* Broadcom (Epigram) */
+
+#define VENDOR_HT_CAPAB_OUI_TYPE 0x33 /* 00-90-4c:0x33 */
+
+/* Represent channel details, subset of ieee80211_channel */
+struct rtw_ieee80211_channel {
+ /* enum ieee80211_band band; */
+ /* u16 center_freq; */
+ u16 hw_value;
+ u32 flags;
+ /* int max_antenna_gain; */
+ /* int max_power; */
+ /* int max_reg_power; */
+ /* bool beacon_found; */
+ /* u32 orig_flags; */
+ /* int orig_mag; */
+ /* int orig_mpwr; */
+};
+
+#define CHAN_FMT \
+ /*"band:%d, "*/ \
+ /*"center_freq:%u, "*/ \
+ "hw_value:%u, " \
+ "flags:0x%08x" \
+ /*"max_antenna_gain:%d\n"*/ \
+ /*"max_power:%d\n"*/ \
+ /*"max_reg_power:%d\n"*/ \
+ /*"beacon_found:%u\n"*/ \
+ /*"orig_flags:0x%08x\n"*/ \
+ /*"orig_mag:%d\n"*/ \
+ /*"orig_mpwr:%d\n"*/
+
+#define CHAN_ARG(channel) \
+ /*(channel)->band*/ \
+ /*, (channel)->center_freq*/ \
+ (channel)->hw_value \
+ , (channel)->flags \
+ /*, (channel)->max_antenna_gain*/ \
+ /*, (channel)->max_power*/ \
+ /*, (channel)->max_reg_power*/ \
+ /*, (channel)->beacon_found*/ \
+ /*, (channel)->orig_flags*/ \
+ /*, (channel)->orig_mag*/ \
+ /*, (channel)->orig_mpwr*/ \
+
+u8 *rtw_set_ie23a(u8 *pbuf, int index, uint len, const u8 *source, uint *frlen);
+
+u8 hal_ch_offset_to_secondary_ch_offset23a(u8 ch_offset);
+u8 *rtw_set_ie23a_ch_switch(u8 *buf, u32 *buf_len, u8 ch_switch_mode, u8 new_ch, u8 ch_switch_cnt);
+u8 *rtw_set_ie23a_secondary_ch_offset(u8 *buf, u32 *buf_len, u8 secondary_ch_offset);
+
+u8 *rtw_get_ie23a(u8*pbuf, int index, int *len, int limit);
+u8 *rtw_get_ie23a_ex(u8 *in_ie, uint in_len, u8 eid, u8 *oui, u8 oui_len, u8 *ie, uint *ielen);
+int rtw_ies_remove_ie23a(u8 *ies, uint *ies_len, uint offset, u8 eid, u8 *oui, u8 oui_len);
+
+void rtw_set_supported_rate23a(u8 *SupportedRates, uint mode);
+
+int rtw_parse_wpa_ie23a(const u8* wpa_ie, int wpa_ie_len, int *group_cipher, int *pairwise_cipher, int *is_8021x);
+int rtw_parse_wpa2_ie23a(const u8* wpa_ie, int wpa_ie_len, int *group_cipher, int *pairwise_cipher, int *is_8021x);
+
+const u8 *rtw_get_wps_attr23a(const u8 *wps_ie, uint wps_ielen, u16 target_attr_id ,u8 *buf_attr, u32 *len_attr);
+const u8 *rtw_get_wps_attr_content23a(const u8 *wps_ie, uint wps_ielen, u16 target_attr_id ,u8 *buf_content);
+
+uint rtw_get_rateset_len23a(u8 *rateset);
+
+struct registry_priv;
+int rtw_generate_ie23a(struct registry_priv *pregistrypriv);
+
+
+int rtw_get_bit_value_from_ieee_value23a(u8 val);
+
+int rtw_check_network_type23a(unsigned char *rate, int ratelen, int channel);
+
+void rtw_get_bcn_info23a(struct wlan_network *pnetwork);
+
+u16 rtw_mcs_rate23a(u8 rf_type, u8 bw_40MHz, u8 short_GI_20, u8 short_GI_40,
+ struct ieee80211_mcs_info *mcs);
+
+#endif /* IEEE80211_H */
diff --git a/drivers/staging/rtl8723au/include/ioctl_cfg80211.h b/drivers/staging/rtl8723au/include/ioctl_cfg80211.h
new file mode 100644
index 000000000..3a4ead54f
--- /dev/null
+++ b/drivers/staging/rtl8723au/include/ioctl_cfg80211.h
@@ -0,0 +1,66 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2011 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * 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.
+ *
+ ******************************************************************************/
+#ifndef __IOCTL_CFG80211_H__
+#define __IOCTL_CFG80211_H__
+
+struct rtw_wdev_priv {
+ struct wireless_dev *rtw_wdev;
+
+ struct rtw_adapter *padapter;
+
+ struct cfg80211_scan_request *scan_request;
+ spinlock_t scan_req_lock;
+
+ struct net_device *pmon_ndev;/* for monitor interface */
+ char ifname_mon[IFNAMSIZ + 1]; /* name for monitor interface */
+
+ u8 p2p_enabled;
+
+ bool power_mgmt;
+};
+
+#define wdev_to_priv(w) ((struct rtw_wdev_priv *)(wdev_priv(w)))
+
+#define wiphy_to_adapter(x) \
+ (struct rtw_adapter *)(((struct rtw_wdev_priv *) \
+ wiphy_priv(x))->padapter)
+
+#define wiphy_to_wdev(x) \
+ (struct wireless_dev *)(((struct rtw_wdev_priv *) \
+ wiphy_priv(x))->rtw_wdev)
+
+int rtw_wdev_alloc(struct rtw_adapter *padapter, struct device *dev);
+void rtw_wdev_free(struct wireless_dev *wdev);
+void rtw_wdev_unregister(struct wireless_dev *wdev);
+
+void rtw_cfg80211_init_wiphy(struct rtw_adapter *padapter);
+
+void rtw_cfg80211_surveydone_event_callback(struct rtw_adapter *padapter);
+
+void rtw_cfg80211_indicate_connect(struct rtw_adapter *padapter);
+void rtw_cfg80211_indicate_disconnect(struct rtw_adapter *padapter);
+void rtw_cfg80211_indicate_scan_done(struct rtw_wdev_priv *pwdev_priv,
+ bool aborted);
+
+#ifdef CONFIG_8723AU_AP_MODE
+void rtw_cfg80211_indicate_sta_assoc(struct rtw_adapter *padapter,
+ u8 *pmgmt_frame, uint frame_len);
+void rtw_cfg80211_indicate_sta_disassoc(struct rtw_adapter *padapter,
+ unsigned char *da, unsigned short reason);
+#endif /* CONFIG_8723AU_AP_MODE */
+
+bool rtw_cfg80211_pwr_mgmt(struct rtw_adapter *adapter);
+
+#endif /* __IOCTL_CFG80211_H__ */
diff --git a/drivers/staging/rtl8723au/include/mlme_osdep.h b/drivers/staging/rtl8723au/include/mlme_osdep.h
new file mode 100644
index 000000000..4bb5525b7
--- /dev/null
+++ b/drivers/staging/rtl8723au/include/mlme_osdep.h
@@ -0,0 +1,24 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2011 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * 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.
+ *
+ ******************************************************************************/
+#ifndef __MLME_OSDEP_H_
+#define __MLME_OSDEP_H_
+
+#include <osdep_service.h>
+#include <drv_types.h>
+
+void rtw_os_indicate_disconnect23a(struct rtw_adapter *adapter);
+void rtw_reset_securitypriv23a(struct rtw_adapter *adapter);
+
+#endif /* _MLME_OSDEP_H_ */
diff --git a/drivers/staging/rtl8723au/include/odm.h b/drivers/staging/rtl8723au/include/odm.h
new file mode 100644
index 000000000..24f2f28c4
--- /dev/null
+++ b/drivers/staging/rtl8723au/include/odm.h
@@ -0,0 +1,860 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2011 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * 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.
+ *
+ ******************************************************************************/
+
+
+#ifndef __HALDMOUTSRC_H__
+#define __HALDMOUTSRC_H__
+
+/* */
+/* Definition */
+/* */
+/* */
+/* 2011/09/22 MH Define all team supprt ability. */
+/* */
+
+/* */
+/* 2011/09/22 MH Define for all teams. Please Define the constan in your precomp header. */
+/* */
+/* define DM_ODM_SUPPORT_AP 0 */
+/* define DM_ODM_SUPPORT_ADSL 0 */
+/* define DM_ODM_SUPPORT_CE 0 */
+/* define DM_ODM_SUPPORT_MP 1 */
+
+#define TP_MODE 0
+#define RSSI_MODE 1
+#define TRAFFIC_LOW 0
+#define TRAFFIC_HIGH 1
+
+
+/* */
+/* 3 Tx Power Tracking */
+/* 3============================================================ */
+#define DPK_DELTA_MAPPING_NUM 13
+#define index_mapping_HP_NUM 15
+
+
+/* */
+/* 3 PSD Handler */
+/* 3============================================================ */
+
+#define AFH_PSD 1 /* 0:normal PSD scan, 1: only do 20 pts PSD */
+#define MODE_40M 0 /* 0:20M, 1:40M */
+#define PSD_TH2 3
+#define PSD_CHMIN 20 /* Minimum channel number for BT AFH */
+#define SIR_STEP_SIZE 3
+#define Smooth_Size_1 5
+#define Smooth_TH_1 3
+#define Smooth_Size_2 10
+#define Smooth_TH_2 4
+#define Smooth_Size_3 20
+#define Smooth_TH_3 4
+#define Smooth_Step_Size 5
+#define Adaptive_SIR 1
+#define PSD_RESCAN 4
+#define PSD_SCAN_INTERVAL 700 /* ms */
+
+/* 8723A High Power IGI Setting */
+#define DM_DIG_HIGH_PWR_IGI_LOWER_BOUND 0x22
+#define DM_DIG_Gmode_HIGH_PWR_IGI_LOWER_BOUND 0x28
+#define DM_DIG_HIGH_PWR_THRESHOLD 0x3a
+
+/* LPS define */
+#define DM_DIG_FA_TH0_LPS 4 /* 4 in lps */
+#define DM_DIG_FA_TH1_LPS 15 /* 15 lps */
+#define DM_DIG_FA_TH2_LPS 30 /* 30 lps */
+#define RSSI_OFFSET_DIG 0x05;
+
+/* ANT Test */
+#define ANTTESTALL 0x00 /* Ant A or B will be Testing */
+#define ANTTESTA 0x01 /* Ant A will be Testing */
+#define ANTTESTB 0x02 /* Ant B will be testing */
+
+
+/* */
+/* structure and define */
+/* */
+
+struct dig_t {
+ u8 Dig_Enable_Flag;
+ u8 Dig_Ext_Port_Stage;
+
+ int RssiLowThresh;
+ int RssiHighThresh;
+
+ u32 FALowThresh;
+ u32 FAHighThresh;
+
+ u8 CurSTAConnectState;
+ u8 PreSTAConnectState;
+ u8 CurMultiSTAConnectState;
+
+ u8 PreIGValue;
+ u8 CurIGValue;
+ u8 BackupIGValue;
+
+ s8 BackoffVal;
+ s8 BackoffVal_range_max;
+ s8 BackoffVal_range_min;
+ u8 rx_gain_range_max;
+ u8 rx_gain_range_min;
+ u8 Rssi_val_min;
+
+ u8 PreCCK_CCAThres;
+ u8 CurCCK_CCAThres;
+ u8 PreCCKPDState;
+ u8 CurCCKPDState;
+
+ u8 LargeFAHit;
+ u8 ForbiddenIGI;
+ u32 Recover_cnt;
+
+ u8 DIG_Dynamic_MIN_0;
+ u8 DIG_Dynamic_MIN_1;
+ bool bMediaConnect_0;
+ bool bMediaConnect_1;
+
+ u32 RSSI_max;
+};
+
+struct dynamic_pwr_sav {
+ u8 PreCCAState;
+ u8 CurCCAState;
+
+ u8 PreRFState;
+ u8 CurRFState;
+
+ int Rssi_val_min;
+
+ u8 initialize;
+ u32 Reg874, RegC70, Reg85C, RegA74;
+};
+
+struct false_alarm_stats {
+ u32 Cnt_Parity_Fail;
+ u32 Cnt_Rate_Illegal;
+ u32 Cnt_Crc8_fail;
+ u32 Cnt_Mcs_fail;
+ u32 Cnt_Ofdm_fail;
+ u32 Cnt_Cck_fail;
+ u32 Cnt_all;
+ u32 Cnt_Fast_Fsync;
+ u32 Cnt_SB_Search_fail;
+ u32 Cnt_OFDM_CCA;
+ u32 Cnt_CCK_CCA;
+ u32 Cnt_CCA_all;
+ u32 Cnt_BW_USC; /* Gary */
+ u32 Cnt_BW_LSC; /* Gary */
+};
+
+#define ASSOCIATE_ENTRY_NUM 32 /* Max size of AsocEntry[]. */
+#define ODM_ASSOCIATE_ENTRY_NUM ASSOCIATE_ENTRY_NUM
+
+/* This indicates two different the steps. */
+/* In SWAW_STEP_PEAK, driver needs to switch antenna and listen to the signal on the air. */
+/* In SWAW_STEP_DETERMINE, driver just compares the signal captured in SWAW_STEP_PEAK */
+/* with original RSSI to determine if it is necessary to switch antenna. */
+#define SWAW_STEP_PEAK 0
+#define SWAW_STEP_DETERMINE 1
+
+#define TP_MODE 0
+#define RSSI_MODE 1
+#define TRAFFIC_LOW 0
+#define TRAFFIC_HIGH 1
+
+struct sw_ant_sw {
+ u8 try_flag;
+ s32 PreRSSI;
+ u8 CurAntenna;
+ u8 PreAntenna;
+ u8 RSSI_Trying;
+ u8 TestMode;
+ u8 bTriggerAntennaSwitch;
+ u8 SelectAntennaMap;
+ u8 RSSI_target;
+
+ /* Before link Antenna Switch check */
+ u8 SWAS_NoLink_State;
+ u32 SWAS_NoLink_BK_Reg860;
+ bool ANTA_ON; /* To indicate Ant A is or not */
+ bool ANTB_ON; /* To indicate Ant B is on or not */
+
+ s32 RSSI_sum_A;
+ s32 RSSI_sum_B;
+ s32 RSSI_cnt_A;
+ s32 RSSI_cnt_B;
+
+ u64 lastTxOkCnt;
+ u64 lastRxOkCnt;
+ u64 TXByteCnt_A;
+ u64 TXByteCnt_B;
+ u64 RXByteCnt_A;
+ u64 RXByteCnt_B;
+ u8 TrafficLoad;
+};
+
+struct edca_turbo {
+ bool bCurrentTurboEDCA;
+ u32 prv_traffic_idx; /* edca turbo */
+};
+
+struct odm_rate_adapt {
+ u8 Type; /* DM_Type_ByFW/DM_Type_ByDriver */
+ u8 HighRSSIThresh; /* if RSSI > HighRSSIThresh => RATRState is DM_RATR_STA_HIGH */
+ u8 LowRSSIThresh; /* if RSSI <= LowRSSIThresh => RATRState is DM_RATR_STA_LOW */
+ u8 RATRState; /* Current RSSI level, DM_RATR_STA_HIGH/DM_RATR_STA_MIDDLE/DM_RATR_STA_LOW */
+ u32 LastRATR; /* RATR Register Content */
+};
+
+#define IQK_MAC_REG_NUM 4
+#define IQK_ADDA_REG_NUM 16
+#define IQK_BB_REG_NUM_MAX 10
+#define IQK_BB_REG_NUM 9
+#define HP_THERMAL_NUM 8
+
+#define AVG_THERMAL_NUM 8
+#define IQK_Matrix_REG_NUM 8
+#define IQK_Matrix_Settings_NUM 1+24+21
+
+#define DM_Type_ByFW 0
+#define DM_Type_ByDriver 1
+
+/* Declare for common info */
+
+struct odm_phy_dbg_info {
+ /* ODM Write,debug info */
+ s8 RxSNRdB[RF_PATH_MAX];
+ u64 NumQryPhyStatus;
+ u64 NumQryPhyStatusCCK;
+ u64 NumQryPhyStatusOFDM;
+ /* Others */
+ s32 RxEVM[RF_PATH_MAX];
+
+};
+
+struct odm_packet_info {
+ u8 Rate;
+ u8 StationID;
+ bool bPacketMatchBSSID;
+ bool bPacketToSelf;
+ bool bPacketBeacon;
+};
+
+
+enum {
+ /* BB Team */
+ ODM_DIG = 0x00000001,
+ ODM_HIGH_POWER = 0x00000002,
+ ODM_CCK_CCA_TH = 0x00000004,
+ ODM_FA_STATISTICS = 0x00000008,
+ ODM_RAMASK = 0x00000010,
+ ODM_RSSI_MONITOR = 0x00000020,
+ ODM_SW_ANTDIV = 0x00000040,
+ ODM_HW_ANTDIV = 0x00000080,
+ ODM_BB_PWRSV = 0x00000100,
+ ODM_2TPATHDIV = 0x00000200,
+ ODM_1TPATHDIV = 0x00000400,
+ ODM_PSD2AFH = 0x00000800
+};
+
+/* */
+/* 2011/10/20 MH Define Common info enum for all team. */
+/* */
+
+enum odm_cmninfo {
+ /* Fixed value: */
+ /* */
+
+ ODM_CMNINFO_MP_TEST_CHIP = 2,
+ ODM_CMNINFO_IC_TYPE, /* enum odm_ic_type_def */
+ ODM_CMNINFO_CUT_VER, /* enum odm_cut_version */
+ ODM_CMNINFO_FAB_VER, /* enum odm_fab_version */
+ ODM_CMNINFO_BOARD_TYPE, /* enum odm_board_type */
+ ODM_CMNINFO_EXT_LNA, /* true */
+ ODM_CMNINFO_EXT_PA,
+ ODM_CMNINFO_EXT_TRSW,
+ ODM_CMNINFO_BINHCT_TEST,
+ ODM_CMNINFO_BWIFI_TEST,
+ ODM_CMNINFO_SMART_CONCURRENT,
+
+
+ /* */
+ /* Dynamic value: */
+ /* */
+ ODM_CMNINFO_MP_MODE,
+
+ ODM_CMNINFO_WIFI_DIRECT,
+ ODM_CMNINFO_WIFI_DISPLAY,
+ ODM_CMNINFO_LINK,
+ ODM_CMNINFO_RSSI_MIN,
+ ODM_CMNINFO_DBG_COMP, /* u64 */
+ ODM_CMNINFO_DBG_LEVEL, /* u32 */
+ ODM_CMNINFO_RA_THRESHOLD_HIGH, /* u8 */
+ ODM_CMNINFO_RA_THRESHOLD_LOW, /* u8 */
+ ODM_CMNINFO_RF_ANTENNA_TYPE, /* u8 */
+ ODM_CMNINFO_BT_DISABLED,
+ ODM_CMNINFO_BT_OPERATION,
+ ODM_CMNINFO_BT_DIG,
+ ODM_CMNINFO_BT_BUSY, /* Check Bt is using or not */
+ ODM_CMNINFO_BT_DISABLE_EDCA,
+
+ /* */
+ /* Dynamic ptr array hook itms. */
+ /* */
+ ODM_CMNINFO_STA_STATUS,
+ ODM_CMNINFO_PHY_STATUS,
+ ODM_CMNINFO_MAC_STATUS,
+
+ ODM_CMNINFO_MAX,
+};
+
+/* Define ODM support ability. ODM_CMNINFO_ABILITY */
+enum {
+ /* BB ODM section BIT 0-15 */
+ ODM_BB_ANT_DIV = BIT(6),
+};
+
+/* ODM_CMNINFO_INTERFACE */
+enum odm_interface_def {
+ ODM_ITRF_PCIE = 0x1,
+ ODM_ITRF_USB = 0x2,
+ ODM_ITRF_SDIO = 0x4,
+ ODM_ITRF_ALL = 0x7,
+};
+
+/* ODM_CMNINFO_IC_TYPE */
+enum odm_ic_type_def {
+ ODM_RTL8192S = BIT(0),
+ ODM_RTL8192C = BIT(1),
+ ODM_RTL8192D = BIT(2),
+ ODM_RTL8723A = BIT(3),
+ ODM_RTL8188E = BIT(4),
+ ODM_RTL8812 = BIT(5),
+ ODM_RTL8821 = BIT(6),
+};
+
+/* ODM_CMNINFO_CUT_VER */
+enum odm_cut_version {
+ ODM_CUT_A = 1,
+ ODM_CUT_B = 2,
+ ODM_CUT_C = 3,
+ ODM_CUT_D = 4,
+ ODM_CUT_E = 5,
+ ODM_CUT_F = 6,
+ ODM_CUT_TEST = 7,
+};
+
+/* ODM_CMNINFO_FAB_VER */
+enum odm_fab_version {
+ ODM_TSMC = 0,
+ ODM_UMC = 1,
+};
+
+/* For example 1T2R (A+AB = BIT0|BIT4|BIT5) */
+enum rf_path_def {
+ ODM_RF_TX_A = BIT(0),
+ ODM_RF_TX_B = BIT(1),
+ ODM_RF_TX_C = BIT(2),
+ ODM_RF_TX_D = BIT(3),
+ ODM_RF_RX_A = BIT(4),
+ ODM_RF_RX_B = BIT(5),
+ ODM_RF_RX_C = BIT(6),
+ ODM_RF_RX_D = BIT(7),
+};
+
+/* ODM Dynamic common info value definition */
+
+enum odm_mac_phy_mode {
+ ODM_SMSP = 0,
+ ODM_DMSP = 1,
+ ODM_DMDP = 2,
+};
+
+
+enum odm_bt_coexist {
+ ODM_BT_BUSY = 1,
+ ODM_BT_ON = 2,
+ ODM_BT_OFF = 3,
+ ODM_BT_NONE = 4,
+};
+
+/* ODM_CMNINFO_OP_MODE */
+enum odm_operation_mode {
+ ODM_NO_LINK = BIT(0),
+ ODM_LINK = BIT(1),
+ ODM_SCAN = BIT(2),
+ ODM_POWERSAVE = BIT(3),
+ ODM_AP_MODE = BIT(4),
+ ODM_CLIENT_MODE = BIT(5),
+ ODM_AD_HOC = BIT(6),
+ ODM_WIFI_DIRECT = BIT(7),
+ ODM_WIFI_DISPLAY = BIT(8),
+};
+
+/* ODM_CMNINFO_WM_MODE */
+enum odm_wireless_mode {
+ ODM_WM_UNKNOW = 0x0,
+ ODM_WM_B = BIT(0),
+ ODM_WM_G = BIT(1),
+ ODM_WM_A = BIT(2),
+ ODM_WM_N24G = BIT(3),
+ ODM_WM_N5G = BIT(4),
+ ODM_WM_AUTO = BIT(5),
+ ODM_WM_AC = BIT(6),
+};
+
+/* ODM_CMNINFO_BAND */
+enum odm_band_type {
+ ODM_BAND_2_4G = BIT(0),
+ ODM_BAND_5G = BIT(1),
+
+};
+
+/* ODM_CMNINFO_SEC_CHNL_OFFSET */
+enum odm_sec_chnl_offset {
+ ODM_DONT_CARE = 0,
+ ODM_BELOW = 1,
+ ODM_ABOVE = 2
+};
+
+/* ODM_CMNINFO_CHNL */
+
+/* ODM_CMNINFO_BOARD_TYPE */
+enum odm_board_type {
+ ODM_BOARD_NORMAL = 0,
+ ODM_BOARD_HIGHPWR = 1,
+ ODM_BOARD_MINICARD = 2,
+ ODM_BOARD_SLIM = 3,
+ ODM_BOARD_COMBO = 4,
+
+};
+
+/* ODM_CMNINFO_ONE_PATH_CCA */
+enum odm_cca_path {
+ ODM_CCA_2R = 0,
+ ODM_CCA_1R_A = 1,
+ ODM_CCA_1R_B = 2,
+};
+
+struct iqk_matrix_regs_set {
+ bool bIQKDone;
+ s32 Value[1][IQK_Matrix_REG_NUM];
+};
+
+struct odm_rf_cal_t {
+ /* for tx power tracking */
+
+ u32 RegA24; /* for TempCCK */
+ s32 RegE94;
+ s32 RegE9C;
+ s32 RegEB4;
+ s32 RegEBC;
+
+ /* u8 bTXPowerTracking; */
+ u8 TXPowercount;
+ bool bTXPowerTrackingInit;
+ bool bTXPowerTracking;
+ u8 TxPowerTrackControl; /* for mp mode, turn off txpwrtracking as default */
+ u8 TM_Trigger;
+ u8 InternalPA5G[2]; /* pathA / pathB */
+
+ u8 ThermalMeter[2]; /* ThermalMeter, index 0 for RFIC0, and 1 for RFIC1 */
+ u8 ThermalValue;
+ u8 ThermalValue_LCK;
+ u8 ThermalValue_IQK;
+ u8 ThermalValue_DPK;
+ u8 ThermalValue_AVG[AVG_THERMAL_NUM];
+ u8 ThermalValue_AVG_index;
+ u8 ThermalValue_RxGain;
+ u8 ThermalValue_Crystal;
+ u8 ThermalValue_DPKstore;
+ u8 ThermalValue_DPKtrack;
+ bool TxPowerTrackingInProgress;
+ bool bDPKenable;
+
+ bool bReloadtxpowerindex;
+ u8 bRfPiEnable;
+ u32 TXPowerTrackingCallbackCnt; /* cosa add for debug */
+
+ u8 bCCKinCH14;
+ u8 CCK_index;
+ u8 OFDM_index[2];
+ bool bDoneTxpower;
+
+ u8 ThermalValue_HP[HP_THERMAL_NUM];
+ u8 ThermalValue_HP_index;
+ struct iqk_matrix_regs_set IQKMatrixRegSetting[IQK_Matrix_Settings_NUM];
+
+ u8 Delta_IQK;
+ u8 Delta_LCK;
+
+ /* for IQK */
+ u32 RegC04;
+ u32 Reg874;
+ u32 RegC08;
+ u32 RegB68;
+ u32 RegB6C;
+ u32 Reg870;
+ u32 Reg860;
+ u32 Reg864;
+
+ bool bIQKInitialized;
+ bool bLCKInProgress;
+ bool bAntennaDetected;
+ u32 ADDA_backup[IQK_ADDA_REG_NUM];
+ u32 IQK_MAC_backup[IQK_MAC_REG_NUM];
+ u32 IQK_BB_backup_recover[9];
+ u32 IQK_BB_backup[IQK_BB_REG_NUM];
+
+ /* for APK */
+ u32 APKoutput[2][2]; /* path A/B; output1_1a/output1_2a */
+ u8 bAPKdone;
+ u8 bAPKThermalMeterIgnore;
+ u8 bDPdone;
+ u8 bDPPathAOK;
+ u8 bDPPathBOK;
+};
+
+enum ant_dif_type {
+ NO_ANTDIV = 0xFF,
+ CG_TRX_HW_ANTDIV = 0x01,
+ CGCS_RX_HW_ANTDIV = 0x02,
+ FIXED_HW_ANTDIV = 0x03,
+ CG_TRX_SMART_ANTDIV = 0x04,
+ CGCS_RX_SW_ANTDIV = 0x05,
+};
+
+/* 2011/09/22 MH Copy from SD4 defined structure. We use to support PHY DM integration. */
+struct dm_odm_t {
+ /* */
+ /* Add for different team use temporarily */
+ /* */
+ struct rtw_adapter *Adapter; /* For CE/NIC team */
+
+ u64 DebugComponents;
+ u32 DebugLevel;
+
+/* ODM HANDLE, DRIVER NEEDS NOT TO HOOK------ */
+ bool bCckHighPower;
+ u8 RFPathRxEnable; /* ODM_CMNINFO_RFPATH_ENABLE */
+/* ODM HANDLE, DRIVER NEEDS NOT TO HOOK------ */
+
+/* 1 COMMON INFORMATION */
+
+ /* Init Value */
+/* HOOK BEFORE REG INIT----------- */
+ /* ODM Support Ability DIG/RATR/TX_PWR_TRACK/ ¡K¡K = 1/2/3/¡K */
+ u32 SupportAbility;
+ /* ODM composite or independent. Bit oriented/ 92C+92D+ .... or any other type = 1/2/3/... */
+ u32 SupportICType;
+ /* Cut Version TestChip/A-cut/B-cut... = 0/1/2/3/... */
+ u8 CutVersion;
+ /* Fab Version TSMC/UMC = 0/1 */
+ u8 FabVersion;
+ /* Board Type Normal/HighPower/MiniCard/SLIM/Combo/... = 0/1/2/3/4/... */
+ u8 BoardType;
+ /* with external LNA NO/Yes = 0/1 */
+ u8 ExtLNA;
+ /* with external PA NO/Yes = 0/1 */
+ u8 ExtPA;
+ /* with external TRSW NO/Yes = 0/1 */
+ u8 ExtTRSW;
+ bool bInHctTest;
+ bool bWIFITest;
+
+ bool bDualMacSmartConcurrent;
+ u32 BK_SupportAbility;
+/* HOOK BEFORE REG INIT----------- */
+
+ /* */
+ /* Dynamic Value */
+ /* */
+/* POINTER REFERENCE----------- */
+
+ u8 u8_temp;
+ bool bool_temp;
+ struct rtw_adapter *PADAPTER_temp;
+
+/* POINTER REFERENCE----------- */
+ /* */
+/* CALL BY VALUE------------- */
+ bool bWIFI_Direct;
+ bool bWIFI_Display;
+ bool bLinked;
+ u8 RSSI_Min;
+ u8 InterfaceIndex; /* Add for 92D dual MAC: 0--Mac0 1--Mac1 */
+ bool bIsMPChip;
+ bool bOneEntryOnly;
+ /* Common info for BTDM */
+ bool bBtDisabled; /* BT is disabled */
+ bool bBtHsOperation; /* BT HS mode is under progress */
+ u8 btHsDigVal; /* use BT rssi to decide the DIG value */
+ bool bBtDisableEdcaTurbo; /* Under some condition, don't enable the EDCA Turbo */
+ bool bBtBusy; /* BT is busy. */
+/* CALL BY VALUE------------- */
+
+ /* 2 Define STA info. */
+ /* _ODM_STA_INFO */
+ /* 2012/01/12 MH For MP, we need to reduce one array pointer for default port.?? */
+ struct sta_info * pODM_StaInfo[ODM_ASSOCIATE_ENTRY_NUM];
+
+ /* Latest packet phy info (ODM write) */
+ struct odm_phy_dbg_info PhyDbgInfo;
+ /* PHY_INFO_88E PhyInfo; */
+
+ /* Latest packet phy info (ODM write) */
+ /* MAC_INFO_88E MacInfo; */
+
+ /* Different Team independt structure?? */
+
+ /* */
+ /* TX_RTP_CMN TX_retrpo; */
+ /* TX_RTP_88E TX_retrpo; */
+ /* TX_RTP_8195 TX_retrpo; */
+
+ /* */
+ /* ODM Structure */
+ /* */
+ struct dig_t DM_DigTable;
+ struct dynamic_pwr_sav DM_PSTable;
+ struct false_alarm_stats FalseAlmCnt;
+ struct false_alarm_stats FlaseAlmCntBuddyAdapter;
+ struct sw_ant_sw DM_SWAT_Table;
+
+ struct edca_turbo DM_EDCA_Table;
+ u32 WMMEDCA_BE;
+ /* Copy from SD4 structure */
+ /* */
+ /* ================================================== */
+ /* */
+
+ /* PSD */
+ u8 RSSI_BT; /* come from BT */
+ struct odm_rate_adapt RateAdaptive;
+
+
+ struct odm_rf_cal_t RFCalibrateInfo;
+}; /* DM_Dynamic_Mechanism_Structure */
+
+enum odm_rf_content {
+ odm_radioa_txt = 0x1000,
+ odm_radiob_txt = 0x1001,
+ odm_radioc_txt = 0x1002,
+ odm_radiod_txt = 0x1003
+};
+
+/* Status code */
+enum rt_status {
+ RT_STATUS_SUCCESS,
+ RT_STATUS_FAILURE,
+ RT_STATUS_PENDING,
+ RT_STATUS_RESOURCE,
+ RT_STATUS_INVALID_CONTEXT,
+ RT_STATUS_INVALID_PARAMETER,
+ RT_STATUS_NOT_SUPPORT,
+ RT_STATUS_OS_API_FAILED,
+};
+
+/* include "odm_function.h" */
+
+/* 3=========================================================== */
+/* 3 DIG */
+/* 3=========================================================== */
+
+enum dm_dig_op {
+ DIG_TYPE_THRESH_HIGH = 0,
+ DIG_TYPE_THRESH_LOW = 1,
+ DIG_TYPE_BACKOFF = 2,
+ DIG_TYPE_RX_GAIN_MIN = 3,
+ DIG_TYPE_RX_GAIN_MAX = 4,
+ DIG_TYPE_ENABLE = 5,
+ DIG_TYPE_DISABLE = 6,
+ DIG_OP_TYPE_MAX
+};
+
+#define DM_DIG_THRESH_HIGH 40
+#define DM_DIG_THRESH_LOW 35
+
+#define DM_SCAN_RSSI_TH 0x14 /* scan return issue for LC */
+
+
+#define DM_FALSEALARM_THRESH_LOW 400
+#define DM_FALSEALARM_THRESH_HIGH 1000
+
+#define DM_DIG_MAX_NIC 0x4e
+#define DM_DIG_MIN_NIC 0x1e
+
+#define DM_DIG_MAX_AP 0x32
+#define DM_DIG_MIN_AP 0x20
+
+#define DM_DIG_MAX_NIC_HP 0x46
+#define DM_DIG_MIN_NIC_HP 0x2e
+
+#define DM_DIG_MAX_AP_HP 0x42
+#define DM_DIG_MIN_AP_HP 0x30
+
+/* vivi 92c&92d has different definition, 20110504 */
+/* this is for 92c */
+#define DM_DIG_FA_TH0 0x200
+#define DM_DIG_FA_TH1 0x300
+#define DM_DIG_FA_TH2 0x400
+/* this is for 92d */
+#define DM_DIG_FA_TH0_92D 0x100
+#define DM_DIG_FA_TH1_92D 0x400
+#define DM_DIG_FA_TH2_92D 0x600
+
+#define DM_DIG_BACKOFF_MAX 12
+#define DM_DIG_BACKOFF_MIN -4
+#define DM_DIG_BACKOFF_DEFAULT 10
+
+/* 3=========================================================== */
+/* 3 AGC RX High Power Mode */
+/* 3=========================================================== */
+#define LNA_Low_Gain_1 0x64
+#define LNA_Low_Gain_2 0x5A
+#define LNA_Low_Gain_3 0x58
+
+#define FA_RXHP_TH1 5000
+#define FA_RXHP_TH2 1500
+#define FA_RXHP_TH3 800
+#define FA_RXHP_TH4 600
+#define FA_RXHP_TH5 500
+
+/* 3=========================================================== */
+/* 3 EDCA */
+/* 3=========================================================== */
+
+/* 3=========================================================== */
+/* 3 Dynamic Tx Power */
+/* 3=========================================================== */
+/* Dynamic Tx Power Control Threshold */
+#define TX_POWER_NEAR_FIELD_THRESH_LVL2 74
+#define TX_POWER_NEAR_FIELD_THRESH_LVL1 67
+#define TX_POWER_NEAR_FIELD_THRESH_AP 0x3F
+
+#define TxHighPwrLevel_Normal 0
+#define TxHighPwrLevel_Level1 1
+#define TxHighPwrLevel_Level2 2
+#define TxHighPwrLevel_BT1 3
+#define TxHighPwrLevel_BT2 4
+#define TxHighPwrLevel_15 5
+#define TxHighPwrLevel_35 6
+#define TxHighPwrLevel_50 7
+#define TxHighPwrLevel_70 8
+#define TxHighPwrLevel_100 9
+
+/* 3=========================================================== */
+/* 3 Rate Adaptive */
+/* 3=========================================================== */
+#define DM_RATR_STA_INIT 0
+#define DM_RATR_STA_HIGH 1
+#define DM_RATR_STA_MIDDLE 2
+#define DM_RATR_STA_LOW 3
+
+/* 3=========================================================== */
+/* 3 BB Power Save */
+/* 3=========================================================== */
+
+
+enum dm_1r_cca {
+ CCA_1R =0,
+ CCA_2R = 1,
+ CCA_MAX = 2,
+};
+
+enum dm_rf_def {
+ RF_Save =0,
+ RF_Normal = 1,
+ RF_MAX = 2,
+};
+
+/* 3=========================================================== */
+/* 3 Antenna Diversity */
+/* 3=========================================================== */
+enum dm_swas {
+ Antenna_A = 1,
+ Antenna_B = 2,
+ Antenna_MAX = 3,
+};
+
+/* Maximal number of antenna detection mechanism needs to perform, added by Roger, 2011.12.28. */
+#define MAX_ANTENNA_DETECTION_CNT 10
+
+/* */
+/* Extern Global Variables. */
+/* */
+#define OFDM_TABLE_SIZE_92C 37
+#define OFDM_TABLE_SIZE_92D 43
+#define CCK_TABLE_SIZE 33
+
+extern u32 OFDMSwingTable23A[OFDM_TABLE_SIZE_92D];
+extern u8 CCKSwingTable_Ch1_Ch1323A[CCK_TABLE_SIZE][8];
+extern u8 CCKSwingTable_Ch1423A [CCK_TABLE_SIZE][8];
+
+
+
+/* 20100514 Joseph: Add definition for antenna switching test after link. */
+/* This indicates two different the steps. */
+/* In SWAW_STEP_PEAK, driver needs to switch antenna and listen to the signal on the air. */
+/* In SWAW_STEP_DETERMINE, driver just compares the signal captured in SWAW_STEP_PEAK */
+/* with original RSSI to determine if it is necessary to switch antenna. */
+#define SWAW_STEP_PEAK 0
+#define SWAW_STEP_DETERMINE 1
+
+struct hal_data_8723a;
+
+void ODM_Write_DIG23a(struct dm_odm_t *pDM_Odm, u8 CurrentIGI);
+void ODM_Write_CCK_CCA_Thres23a(struct dm_odm_t *pDM_Odm, u8 CurCCK_CCAThres);
+
+void ODM_SetAntenna(struct dm_odm_t *pDM_Odm, u8 Antenna);
+
+
+#define dm_RF_Saving ODM_RF_Saving23a
+void ODM_RF_Saving23a(struct dm_odm_t *pDM_Odm, u8 bForceInNormal);
+
+#define dm_CheckTXPowerTracking ODM_TXPowerTrackingCheck23a
+void ODM_TXPowerTrackingCheck23a(struct dm_odm_t *pDM_Odm);
+
+bool ODM_RAStateCheck23a(struct dm_odm_t *pDM_Odm, s32 RSSI, bool bForceUpdate,
+ u8 *pRATRState);
+
+
+u32 ConvertTo_dB23a(u32 Value);
+
+u32 GetPSDData(struct dm_odm_t *pDM_Odm, unsigned int point, u8 initial_gain_psd);
+
+void odm_DIG23abyRSSI_LPS(struct dm_odm_t *pDM_Odm);
+
+u32 ODM_Get_Rate_Bitmap23a(struct hal_data_8723a *pHalData, u32 macid, u32 ra_mask, u8 rssi_level);
+
+
+void ODM23a_DMInit(struct dm_odm_t *pDM_Odm);
+
+void ODM_DMWatchdog23a(struct rtw_adapter *adapter);
+
+void ODM_CmnInfoInit23a(struct dm_odm_t *pDM_Odm, enum odm_cmninfo CmnInfo, u32 Value);
+
+void ODM_CmnInfoPtrArrayHook23a(struct dm_odm_t *pDM_Odm, enum odm_cmninfo CmnInfo, u16 Index, void *pValue);
+
+void ODM_CmnInfoUpdate23a(struct dm_odm_t *pDM_Odm, u32 CmnInfo, u64 Value);
+
+void ODM_ResetIQKResult(struct dm_odm_t *pDM_Odm);
+
+void ODM_AntselStatistics_88C(struct dm_odm_t *pDM_Odm, u8 MacId, u32 PWDBAll, bool isCCKrate);
+
+void ODM_SingleDualAntennaDefaultSetting(struct dm_odm_t *pDM_Odm);
+
+bool ODM_SingleDualAntennaDetection(struct dm_odm_t *pDM_Odm, u8 mode);
+
+#endif
diff --git a/drivers/staging/rtl8723au/include/odm_HWConfig.h b/drivers/staging/rtl8723au/include/odm_HWConfig.h
new file mode 100644
index 000000000..ce7abe770
--- /dev/null
+++ b/drivers/staging/rtl8723au/include/odm_HWConfig.h
@@ -0,0 +1,155 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2011 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * 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.
+ *
+ ******************************************************************************/
+
+
+#ifndef __HALHWOUTSRC_H__
+#define __HALHWOUTSRC_H__
+
+#include <Hal8723APhyCfg.h>
+
+/* */
+/* Definition */
+/* */
+/* */
+/* */
+/* CCK Rates, TxHT = 0 */
+#define DESC92C_RATE1M 0x00
+#define DESC92C_RATE2M 0x01
+#define DESC92C_RATE5_5M 0x02
+#define DESC92C_RATE11M 0x03
+
+/* OFDM Rates, TxHT = 0 */
+#define DESC92C_RATE6M 0x04
+#define DESC92C_RATE9M 0x05
+#define DESC92C_RATE12M 0x06
+#define DESC92C_RATE18M 0x07
+#define DESC92C_RATE24M 0x08
+#define DESC92C_RATE36M 0x09
+#define DESC92C_RATE48M 0x0a
+#define DESC92C_RATE54M 0x0b
+
+/* MCS Rates, TxHT = 1 */
+#define DESC92C_RATEMCS0 0x0c
+#define DESC92C_RATEMCS1 0x0d
+#define DESC92C_RATEMCS2 0x0e
+#define DESC92C_RATEMCS3 0x0f
+#define DESC92C_RATEMCS4 0x10
+#define DESC92C_RATEMCS5 0x11
+#define DESC92C_RATEMCS6 0x12
+#define DESC92C_RATEMCS7 0x13
+#define DESC92C_RATEMCS8 0x14
+#define DESC92C_RATEMCS9 0x15
+#define DESC92C_RATEMCS10 0x16
+#define DESC92C_RATEMCS11 0x17
+#define DESC92C_RATEMCS12 0x18
+#define DESC92C_RATEMCS13 0x19
+#define DESC92C_RATEMCS14 0x1a
+#define DESC92C_RATEMCS15 0x1b
+#define DESC92C_RATEMCS15_SG 0x1c
+#define DESC92C_RATEMCS32 0x20
+
+
+/* */
+/* structure and define */
+/* */
+
+struct phy_rx_agc_info {
+ #ifdef __LITTLE_ENDIAN
+ u8 gain:7, trsw:1;
+ #else
+ u8 trsw:1, gain:7;
+ #endif
+};
+
+struct phy_status_rpt {
+ struct phy_rx_agc_info path_agc[RF_PATH_MAX];
+ u8 ch_corr[RF_PATH_MAX];
+ u8 cck_sig_qual_ofdm_pwdb_all;
+ u8 cck_agc_rpt_ofdm_cfosho_a;
+ u8 cck_rpt_b_ofdm_cfosho_b;
+ u8 rsvd_1;/* ch_corr_msb; */
+ u8 noise_power_db_msb;
+ u8 path_cfotail[RF_PATH_MAX];
+ u8 pcts_mask[RF_PATH_MAX];
+ s8 stream_rxevm[RF_PATH_MAX];
+ u8 path_rxsnr[RF_PATH_MAX];
+ u8 noise_power_db_lsb;
+ u8 rsvd_2[3];
+ u8 stream_csi[RF_PATH_MAX];
+ u8 stream_target_csi[RF_PATH_MAX];
+ s8 sig_evm;
+ u8 rsvd_3;
+
+#ifdef __LITTLE_ENDIAN
+ u8 antsel_rx_keep_2:1; /* ex_intf_flg:1; */
+ u8 sgi_en:1;
+ u8 rxsc:2;
+ u8 idle_long:1;
+ u8 r_ant_train_en:1;
+ u8 ant_sel_b:1;
+ u8 ant_sel:1;
+#else /* _BIG_ENDIAN_ */
+ u8 ant_sel:1;
+ u8 ant_sel_b:1;
+ u8 r_ant_train_en:1;
+ u8 idle_long:1;
+ u8 rxsc:2;
+ u8 sgi_en:1;
+ u8 antsel_rx_keep_2:1; /* ex_intf_flg:1; */
+#endif
+};
+
+
+struct phy_status_rpt_8195 {
+ struct phy_rx_agc_info path_agc[2];
+ u8 ch_num[2];
+ u8 cck_sig_qual_ofdm_pwdb_all;
+ u8 cck_agc_rpt_ofdm_cfosho_a;
+ u8 cck_bb_pwr_ofdm_cfosho_b;
+ u8 cck_rx_path; /* CCK_RX_PATH [3:0] (with regA07[3:0] definition) */
+ u8 rsvd_1;
+ u8 path_cfotail[2];
+ u8 pcts_mask[2];
+ s8 stream_rxevm[2];
+ u8 path_rxsnr[2];
+ u8 rsvd_2[2];
+ u8 stream_snr[2];
+ u8 stream_csi[2];
+ u8 rsvd_3[2];
+ s8 sig_evm;
+ u8 rsvd_4;
+#ifdef __LITTLE_ENDIAN
+ u8 antidx_anta:3;
+ u8 antidx_antb:3;
+ u8 rsvd_5:2;
+#else /* _BIG_ENDIAN_ */
+ u8 rsvd_5:2;
+ u8 antidx_antb:3;
+ u8 antidx_anta:3;
+#endif
+};
+
+
+void odm_Init_RSSIForDM23a(struct dm_odm_t *pDM_Odm);
+
+void
+ODM_PhyStatusQuery23a(
+ struct dm_odm_t *pDM_Odm,
+ struct phy_info *pPhyInfo,
+ u8 * pPhyStatus,
+ struct odm_packet_info *pPktinfo
+ );
+
+#endif
diff --git a/drivers/staging/rtl8723au/include/odm_RegConfig8723A.h b/drivers/staging/rtl8723au/include/odm_RegConfig8723A.h
new file mode 100644
index 000000000..f2a54d829
--- /dev/null
+++ b/drivers/staging/rtl8723au/include/odm_RegConfig8723A.h
@@ -0,0 +1,27 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2011 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * 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.
+ *
+ ******************************************************************************/
+#ifndef __INC_ODM_REGCONFIG_H_8723A
+#define __INC_ODM_REGCONFIG_H_8723A
+
+void odm_ConfigRFReg_8723A(struct dm_odm_t *pDM_Odm, u32 Addr, u32 Data,
+ enum RF_RADIO_PATH RF_PATH, u32 RegAddr);
+
+void odm_ConfigMAC_8723A(struct dm_odm_t *pDM_Odm, u32 Addr, u8 Data);
+
+void odm_ConfigBB_AGC_8723A(struct dm_odm_t *pDM_Odm, u32 addr, u32 data);
+
+void odm_ConfigBB_PHY_8723A(struct dm_odm_t *pDM_Odm, u32 addr, u32 data);
+
+#endif /* end of SUPPORT */
diff --git a/drivers/staging/rtl8723au/include/odm_RegDefine11N.h b/drivers/staging/rtl8723au/include/odm_RegDefine11N.h
new file mode 100644
index 000000000..27782154f
--- /dev/null
+++ b/drivers/staging/rtl8723au/include/odm_RegDefine11N.h
@@ -0,0 +1,165 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2011 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * 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.
+ *
+ ******************************************************************************/
+
+#ifndef __ODM_REGDEFINE11N_H__
+#define __ODM_REGDEFINE11N_H__
+
+
+/* 2 RF REG LIST */
+#define ODM_REG_RF_MODE_11N 0x00
+#define ODM_REG_RF_0B_11N 0x0B
+#define ODM_REG_CHNBW_11N 0x18
+#define ODM_REG_T_METER_11N 0x24
+#define ODM_REG_RF_25_11N 0x25
+#define ODM_REG_RF_26_11N 0x26
+#define ODM_REG_RF_27_11N 0x27
+#define ODM_REG_RF_2B_11N 0x2B
+#define ODM_REG_RF_2C_11N 0x2C
+#define ODM_REG_RXRF_A3_11N 0x3C
+#define ODM_REG_T_METER_92D_11N 0x42
+#define ODM_REG_T_METER_88E_11N 0x42
+
+
+
+/* 2 BB REG LIST */
+/* PAGE 8 */
+#define ODM_REG_BB_CTRL_11N 0x800
+#define ODM_REG_RF_PIN_11N 0x804
+#define ODM_REG_PSD_CTRL_11N 0x808
+#define ODM_REG_TX_ANT_CTRL_11N 0x80C
+#define ODM_REG_BB_PWR_SAV5_11N 0x818
+#define ODM_REG_CCK_RPT_FORMAT_11N 0x824
+#define ODM_REG_RX_DEFUALT_A_11N 0x858
+#define ODM_REG_RX_DEFUALT_B_11N 0x85A
+#define ODM_REG_BB_PWR_SAV3_11N 0x85C
+#define ODM_REG_ANTSEL_CTRL_11N 0x860
+#define ODM_REG_RX_ANT_CTRL_11N 0x864
+#define ODM_REG_PIN_CTRL_11N 0x870
+#define ODM_REG_BB_PWR_SAV1_11N 0x874
+#define ODM_REG_ANTSEL_PATH_11N 0x878
+#define ODM_REG_BB_3WIRE_11N 0x88C
+#define ODM_REG_SC_CNT_11N 0x8C4
+#define ODM_REG_PSD_DATA_11N 0x8B4
+/* PAGE 9 */
+#define ODM_REG_ANT_MAPPING1_11N 0x914
+#define ODM_REG_ANT_MAPPING2_11N 0x918
+/* PAGE A */
+#define ODM_REG_CCK_ANTDIV_PARA1_11N 0xA00
+#define ODM_REG_CCK_CCA_11N 0xA0A
+#define ODM_REG_CCK_ANTDIV_PARA2_11N 0xA0C
+#define ODM_REG_CCK_ANTDIV_PARA3_11N 0xA10
+#define ODM_REG_CCK_ANTDIV_PARA4_11N 0xA14
+#define ODM_REG_CCK_FILTER_PARA1_11N 0xA22
+#define ODM_REG_CCK_FILTER_PARA2_11N 0xA23
+#define ODM_REG_CCK_FILTER_PARA3_11N 0xA24
+#define ODM_REG_CCK_FILTER_PARA4_11N 0xA25
+#define ODM_REG_CCK_FILTER_PARA5_11N 0xA26
+#define ODM_REG_CCK_FILTER_PARA6_11N 0xA27
+#define ODM_REG_CCK_FILTER_PARA7_11N 0xA28
+#define ODM_REG_CCK_FILTER_PARA8_11N 0xA29
+#define ODM_REG_CCK_FA_RST_11N 0xA2C
+#define ODM_REG_CCK_FA_MSB_11N 0xA58
+#define ODM_REG_CCK_FA_LSB_11N 0xA5C
+#define ODM_REG_CCK_CCA_CNT_11N 0xA60
+#define ODM_REG_BB_PWR_SAV4_11N 0xA74
+/* PAGE B */
+#define ODM_REG_LNA_SWITCH_11N 0xB2C
+#define ODM_REG_PATH_SWITCH_11N 0xB30
+#define ODM_REG_RSSI_CTRL_11N 0xB38
+#define ODM_REG_CONFIG_ANTA_11N 0xB68
+#define ODM_REG_RSSI_BT_11N 0xB9C
+/* PAGE C */
+#define ODM_REG_OFDM_FA_HOLDC_11N 0xC00
+#define ODM_REG_RX_PATH_11N 0xC04
+#define ODM_REG_TRMUX_11N 0xC08
+#define ODM_REG_OFDM_FA_RSTC_11N 0xC0C
+#define ODM_REG_RXIQI_MATRIX_11N 0xC14
+#define ODM_REG_TXIQK_MATRIX_LSB1_11N 0xC4C
+#define ODM_REG_IGI_A_11N 0xC50
+#define ODM_REG_ANTDIV_PARA2_11N 0xC54
+#define ODM_REG_IGI_B_11N 0xC58
+#define ODM_REG_ANTDIV_PARA3_11N 0xC5C
+#define ODM_REG_BB_PWR_SAV2_11N 0xC70
+#define ODM_REG_RX_OFF_11N 0xC7C
+#define ODM_REG_TXIQK_MATRIXA_11N 0xC80
+#define ODM_REG_TXIQK_MATRIXB_11N 0xC88
+#define ODM_REG_TXIQK_MATRIXA_LSB2_11N 0xC94
+#define ODM_REG_TXIQK_MATRIXB_LSB2_11N 0xC9C
+#define ODM_REG_RXIQK_MATRIX_LSB_11N 0xCA0
+#define ODM_REG_ANTDIV_PARA1_11N 0xCA4
+#define ODM_REG_OFDM_FA_TYPE1_11N 0xCF0
+/* PAGE D */
+#define ODM_REG_OFDM_FA_RSTD_11N 0xD00
+#define ODM_REG_OFDM_FA_TYPE2_11N 0xDA0
+#define ODM_REG_OFDM_FA_TYPE3_11N 0xDA4
+#define ODM_REG_OFDM_FA_TYPE4_11N 0xDA8
+/* PAGE E */
+#define ODM_REG_TXAGC_A_6_18_11N 0xE00
+#define ODM_REG_TXAGC_A_24_54_11N 0xE04
+#define ODM_REG_TXAGC_A_1_MCS32_11N 0xE08
+#define ODM_REG_TXAGC_A_MCS0_3_11N 0xE10
+#define ODM_REG_TXAGC_A_MCS4_7_11N 0xE14
+#define ODM_REG_TXAGC_A_MCS8_11_11N 0xE18
+#define ODM_REG_TXAGC_A_MCS12_15_11N 0xE1C
+#define ODM_REG_FPGA0_IQK_11N 0xE28
+#define ODM_REG_TXIQK_TONE_A_11N 0xE30
+#define ODM_REG_RXIQK_TONE_A_11N 0xE34
+#define ODM_REG_TXIQK_PI_A_11N 0xE38
+#define ODM_REG_RXIQK_PI_A_11N 0xE3C
+#define ODM_REG_TXIQK_11N 0xE40
+#define ODM_REG_RXIQK_11N 0xE44
+#define ODM_REG_IQK_AGC_PTS_11N 0xE48
+#define ODM_REG_IQK_AGC_RSP_11N 0xE4C
+#define ODM_REG_BLUETOOTH_11N 0xE6C
+#define ODM_REG_RX_WAIT_CCA_11N 0xE70
+#define ODM_REG_TX_CCK_RFON_11N 0xE74
+#define ODM_REG_TX_CCK_BBON_11N 0xE78
+#define ODM_REG_OFDM_RFON_11N 0xE7C
+#define ODM_REG_OFDM_BBON_11N 0xE80
+#define ODM_REG_TX2RX_11N 0xE84
+#define ODM_REG_TX2TX_11N 0xE88
+#define ODM_REG_RX_CCK_11N 0xE8C
+#define ODM_REG_RX_OFDM_11N 0xED0
+#define ODM_REG_RX_WAIT_RIFS_11N 0xED4
+#define ODM_REG_RX2RX_11N 0xED8
+#define ODM_REG_STANDBY_11N 0xEDC
+#define ODM_REG_SLEEP_11N 0xEE0
+#define ODM_REG_PMPD_ANAEN_11N 0xEEC
+
+
+
+
+
+
+
+/* 2 MAC REG LIST */
+#define ODM_REG_BB_RST_11N 0x02
+#define ODM_REG_ANTSEL_PIN_11N 0x4C
+#define ODM_REG_EARLY_MODE_11N 0x4D0
+#define ODM_REG_RSSI_MONITOR_11N 0x4FE
+#define ODM_REG_EDCA_VO_11N 0x500
+#define ODM_REG_EDCA_VI_11N 0x504
+#define ODM_REG_EDCA_BE_11N 0x508
+#define ODM_REG_EDCA_BK_11N 0x50C
+#define ODM_REG_TXPAUSE_11N 0x522
+#define ODM_REG_RESP_TX_11N 0x6D8
+#define ODM_REG_ANT_TRAIN_PARA1_11N 0x7b0
+#define ODM_REG_ANT_TRAIN_PARA2_11N 0x7b4
+
+
+/* DIG Related */
+#define ODM_BIT_IGI_11N 0x0000007F
+
+#endif
diff --git a/drivers/staging/rtl8723au/include/odm_debug.h b/drivers/staging/rtl8723au/include/odm_debug.h
new file mode 100644
index 000000000..83be5bab9
--- /dev/null
+++ b/drivers/staging/rtl8723au/include/odm_debug.h
@@ -0,0 +1,117 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2011 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * 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.
+ *
+ ******************************************************************************/
+
+
+#ifndef __ODM_DBG_H__
+#define __ODM_DBG_H__
+
+
+/* */
+/* Define the debug levels */
+/* */
+/* 1. DBG_TRACE and DBG_LOUD are used for normal cases. */
+/* So that, they can help SW engineer to develope or trace states changed */
+/* and also help HW enginner to trace every operation to and from HW, */
+/* e.g IO, Tx, Rx. */
+/* */
+/* 2. DBG_WARNNING and DBG_SERIOUS are used for unusual or error cases, */
+/* which help us to debug SW or HW. */
+/* */
+/* */
+/* */
+/* Never used in a call to ODM_RT_TRACE()! */
+/* */
+#define ODM_DBG_OFF 1
+
+/* */
+/* Fatal bug. */
+/* For example, Tx/Rx/IO locked up, OS hangs, memory access violation, */
+/* resource allocation failed, unexpected HW behavior, HW BUG and so on. */
+/* */
+#define ODM_DBG_SERIOUS 2
+
+/* */
+/* Abnormal, rare, or unexpeted cases. */
+/* For example, IRP/Packet/OID canceled, device suprisely unremoved and so on. */
+/* */
+#define ODM_DBG_WARNING 3
+
+/* */
+/* Normal case with useful information about current SW or HW state. */
+/* For example, Tx/Rx descriptor to fill, Tx/Rx descriptor completed status, */
+/* SW protocol state change, dynamic mechanism state change and so on. */
+/* */
+#define ODM_DBG_LOUD 4
+
+/* */
+/* Normal case with detail execution flow or information. */
+/* */
+#define ODM_DBG_TRACE 5
+
+/* */
+/* Define the tracing components */
+/* */
+/* */
+/* BB Functions */
+#define ODM_COMP_DIG BIT(0)
+#define ODM_COMP_RA_MASK BIT(1)
+#define ODM_COMP_DYNAMIC_TXPWR BIT(2)
+#define ODM_COMP_FA_CNT BIT(3)
+#define ODM_COMP_RSSI_MONITOR BIT(4)
+#define ODM_COMP_CCK_PD BIT(5)
+#define ODM_COMP_ANT_DIV BIT(6)
+#define ODM_COMP_PWR_SAVE BIT(7)
+#define ODM_COMP_PWR_TRAIN BIT(8)
+#define ODM_COMP_RATE_ADAPTIVE BIT(9)
+#define ODM_COMP_PATH_DIV BIT(10)
+#define ODM_COMP_PSD BIT(11)
+#define ODM_COMP_DYNAMIC_PRICCA BIT(12)
+#define ODM_COMP_RXHP BIT(13)
+/* MAC Functions */
+#define ODM_COMP_EDCA_TURBO BIT(16)
+#define ODM_COMP_EARLY_MODE BIT(17)
+/* RF Functions */
+#define ODM_COMP_TX_PWR_TRACK BIT(24)
+#define ODM_COMP_RX_GAIN_TRACK BIT(25)
+#define ODM_COMP_CALIBRATION BIT(26)
+/* Common Functions */
+#define ODM_COMP_COMMON BIT(30)
+#define ODM_COMP_INIT BIT(31)
+
+/*------------------------Export Macro Definition---------------------------*/
+ #define RT_PRINTK(fmt, args...) printk("%s(): " fmt, __func__, ## args);
+
+#ifndef ASSERT
+ #define ASSERT(expr)
+#endif
+
+#define ODM_RT_TRACE(pDM_Odm, comp, level, fmt) \
+ if(((comp) & pDM_Odm->DebugComponents) && (level <= pDM_Odm->DebugLevel)) \
+ { \
+ printk("[ODM-8723A] "); \
+ RT_PRINTK fmt; \
+ }
+
+#define ODM_RT_ASSERT(pDM_Odm, expr, fmt) \
+ if(!(expr)) { \
+ printk("Assertion failed! %s at ......\n", #expr); \
+ printk(" ......%s,%s,line=%d\n", __FILE__, __func__, __LINE__);\
+ RT_PRINTK fmt; \
+ ASSERT(false); \
+ }
+
+void ODM_InitDebugSetting23a(struct dm_odm_t *pDM_Odm);
+
+#endif /* __ODM_DBG_H__ */
diff --git a/drivers/staging/rtl8723au/include/odm_interface.h b/drivers/staging/rtl8723au/include/odm_interface.h
new file mode 100644
index 000000000..1d3bf03b5
--- /dev/null
+++ b/drivers/staging/rtl8723au/include/odm_interface.h
@@ -0,0 +1,62 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2011 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * 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.
+ *
+ ******************************************************************************/
+
+
+#ifndef __ODM_INTERFACE_H__
+#define __ODM_INTERFACE_H__
+
+
+/* _cat: implemented by Token-Pasting Operator. */
+
+/*===================================
+
+#define ODM_REG_DIG_11N 0xC50
+#define ODM_REG_DIG_11AC 0xDDD
+
+ODM_REG(DIG,_pDM_Odm)
+=====================================*/
+
+#define _reg_11N(_name) ODM_REG_##_name##_11N
+#define _reg_11AC(_name) ODM_REG_##_name##_11AC
+#define _bit_11N(_name) ODM_BIT_##_name##_11N
+#define _bit_11AC(_name) ODM_BIT_##_name##_11AC
+
+#define _cat(_name, _func) \
+ ( \
+ _func##_11N(_name) \
+ )
+
+/* _name: name of register or bit. */
+/* Example: "ODM_REG(R_A_AGC_CORE1, pDM_Odm)" */
+/* gets "ODM_R_A_AGC_CORE1" or "ODM_R_A_AGC_CORE1_8192C", depends on SupportICType. */
+#define ODM_REG(_name, _pDM_Odm) _cat(_name, _reg)
+#define ODM_BIT(_name, _pDM_Odm) _cat(_name, _bit)
+
+/* */
+/* 2012/02/17 MH For non-MP compile pass only. Linux does not support workitem. */
+/* Suggest HW team to use thread instead of workitem. Windows also support the feature. */
+/* */
+typedef void (*RT_WORKITEM_CALL_BACK)(struct work_struct *pContext);
+
+/* */
+/* =========== EXtern Function Prototype */
+/* */
+
+void ODM_SetRFReg(struct dm_odm_t *pDM_Odm, enum RF_RADIO_PATH eRFPath,
+ u32 RegAddr, u32 BitMask, u32 Data);
+u32 ODM_GetRFReg(struct dm_odm_t *pDM_Odm, enum RF_RADIO_PATH eRFPath,
+ u32 RegAddr, u32 BitMask);
+
+#endif /* __ODM_INTERFACE_H__ */
diff --git a/drivers/staging/rtl8723au/include/odm_precomp.h b/drivers/staging/rtl8723au/include/odm_precomp.h
new file mode 100644
index 000000000..fb793c8ba
--- /dev/null
+++ b/drivers/staging/rtl8723au/include/odm_precomp.h
@@ -0,0 +1,49 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2011 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * 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.
+ *
+ ******************************************************************************/
+
+#ifndef __ODM_PRECOMP_H__
+#define __ODM_PRECOMP_H__
+
+/* 2 Config Flags and Structs - defined by each ODM Type */
+
+#include <osdep_service.h>
+#include <drv_types.h>
+#include <hal_intf.h>
+
+
+/* 2 Hardware Parameter Files */
+#include "Hal8723UHWImg_CE.h"
+
+
+/* 2 OutSrc Header Files */
+
+#include "odm.h"
+#include "odm_HWConfig.h"
+#include "odm_debug.h"
+#include "odm_RegDefine11N.h"
+
+#include "HalDMOutSrc8723A.h" /* for IQK,LCK,Power-tracking */
+#include "rtl8723a_hal.h"
+
+#include "odm_interface.h"
+#include "odm_reg.h"
+
+#include "HalHWImg8723A_MAC.h"
+#include "HalHWImg8723A_RF.h"
+#include "HalHWImg8723A_BB.h"
+#include "HalHWImg8723A_FW.h"
+#include "odm_RegConfig8723A.h"
+
+#endif /* __ODM_PRECOMP_H__ */
diff --git a/drivers/staging/rtl8723au/include/odm_reg.h b/drivers/staging/rtl8723au/include/odm_reg.h
new file mode 100644
index 000000000..c18433120
--- /dev/null
+++ b/drivers/staging/rtl8723au/include/odm_reg.h
@@ -0,0 +1,111 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2011 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * 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.
+ *
+ ******************************************************************************/
+/* */
+/* File Name: odm_reg.h */
+/* */
+/* Description: */
+/* */
+/* This file is for general register definition. */
+/* */
+/* */
+/* */
+#ifndef __HAL_ODM_REG_H__
+#define __HAL_ODM_REG_H__
+
+/* */
+/* Register Definition */
+/* */
+
+/* MAC REG */
+#define ODM_BB_RESET 0x002
+#define ODM_DUMMY 0x4fe
+#define ODM_EDCA_VO_PARAM 0x500
+#define ODM_EDCA_VI_PARAM 0x504
+#define ODM_EDCA_BE_PARAM 0x508
+#define ODM_EDCA_BK_PARAM 0x50C
+#define ODM_TXPAUSE 0x522
+
+/* BB REG */
+#define ODM_FPGA_PHY0_PAGE8 0x800
+#define ODM_PSD_SETTING 0x808
+#define ODM_AFE_SETTING 0x818
+#define ODM_TXAGC_B_6_18 0x830
+#define ODM_TXAGC_B_24_54 0x834
+#define ODM_TXAGC_B_MCS32_5 0x838
+#define ODM_TXAGC_B_MCS0_MCS3 0x83c
+#define ODM_TXAGC_B_MCS4_MCS7 0x848
+#define ODM_TXAGC_B_MCS8_MCS11 0x84c
+#define ODM_ANALOG_REGISTER 0x85c
+#define ODM_RF_INTERFACE_OUTPUT 0x860
+#define ODM_TXAGC_B_MCS12_MCS15 0x868
+#define ODM_TXAGC_B_11_A_2_11 0x86c
+#define ODM_AD_DA_LSB_MASK 0x874
+#define ODM_ENABLE_3_WIRE 0x88c
+#define ODM_PSD_REPORT 0x8b4
+#define ODM_R_ANT_SELECT 0x90c
+#define ODM_CCK_ANT_SELECT 0xa07
+#define ODM_CCK_PD_THRESH 0xa0a
+#define ODM_CCK_RF_REG1 0xa11
+#define ODM_CCK_MATCH_FILTER 0xa20
+#define ODM_CCK_RAKE_MAC 0xa2e
+#define ODM_CCK_CNT_RESET 0xa2d
+#define ODM_CCK_TX_DIVERSITY 0xa2f
+#define ODM_CCK_FA_CNT_MSB 0xa5b
+#define ODM_CCK_FA_CNT_LSB 0xa5c
+#define ODM_CCK_NEW_FUNCTION 0xa75
+#define ODM_OFDM_PHY0_PAGE_C 0xc00
+#define ODM_OFDM_RX_ANT 0xc04
+#define ODM_R_A_RXIQI 0xc14
+#define ODM_R_A_AGC_CORE1 0xc50
+#define ODM_R_A_AGC_CORE2 0xc54
+#define ODM_R_B_AGC_CORE1 0xc58
+#define ODM_R_AGC_PAR 0xc70
+#define ODM_R_HTSTF_AGC_PAR 0xc7c
+#define ODM_TX_PWR_TRAINING_A 0xc90
+#define ODM_TX_PWR_TRAINING_B 0xc98
+#define ODM_OFDM_FA_CNT1 0xcf0
+#define ODM_OFDM_PHY0_PAGE_D 0xd00
+#define ODM_OFDM_FA_CNT2 0xda0
+#define ODM_OFDM_FA_CNT3 0xda4
+#define ODM_OFDM_FA_CNT4 0xda8
+#define ODM_TXAGC_A_6_18 0xe00
+#define ODM_TXAGC_A_24_54 0xe04
+#define ODM_TXAGC_A_1_MCS32 0xe08
+#define ODM_TXAGC_A_MCS0_MCS3 0xe10
+#define ODM_TXAGC_A_MCS4_MCS7 0xe14
+#define ODM_TXAGC_A_MCS8_MCS11 0xe18
+#define ODM_TXAGC_A_MCS12_MCS15 0xe1c
+
+/* RF REG */
+#define ODM_GAIN_SETTING 0x00
+#define ODM_CHANNEL 0x18
+
+/* Ant Detect Reg */
+#define ODM_DPDT 0x300
+
+/* PSD Init */
+#define ODM_PSDREG 0x808
+
+/* 92D Path Div */
+#define PATHDIV_REG 0xB30
+#define PATHDIV_TRI 0xBA0
+
+/* */
+/* Bitmap Definition */
+/* */
+
+#define BIT_FA_RESET BIT(0)
+
+#endif
diff --git a/drivers/staging/rtl8723au/include/osdep_intf.h b/drivers/staging/rtl8723au/include/osdep_intf.h
new file mode 100644
index 000000000..a157eb2e7
--- /dev/null
+++ b/drivers/staging/rtl8723au/include/osdep_intf.h
@@ -0,0 +1,45 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2011 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * 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.
+ *
+ ******************************************************************************/
+
+#ifndef __OSDEP_INTF_H_
+#define __OSDEP_INTF_H_
+
+#include <osdep_service.h>
+#include <drv_types.h>
+
+int rtw_init_drv_sw23a(struct rtw_adapter *padapter);
+int rtw_free_drv_sw23a(struct rtw_adapter *padapter);
+int rtw_reset_drv_sw23a(struct rtw_adapter *padapter);
+
+void rtw_cancel_all_timer23a(struct rtw_adapter *padapter);
+
+int rtw_init_netdev23a_name23a(struct net_device *pnetdev, const char *ifname);
+struct net_device *rtw_init_netdev23a(struct rtw_adapter *padapter);
+
+u16 rtw_recv_select_queue23a(struct sk_buff *skb);
+
+void rtw_ips_dev_unload23a(struct rtw_adapter *padapter);
+
+int rtw_ips_pwr_up23a(struct rtw_adapter *padapter);
+void rtw_ips_pwr_down23a(struct rtw_adapter *padapter);
+
+int rtw_drv_register_netdev(struct rtw_adapter *padapter);
+void rtw_ndev_destructor(struct net_device *ndev);
+
+int rtl8723au_inirp_init(struct rtw_adapter *Adapter);
+int rtl8723au_inirp_deinit(struct rtw_adapter *Adapter);
+void rtl8723a_usb_intf_stop(struct rtw_adapter *padapter);
+
+#endif /* _OSDEP_INTF_H_ */
diff --git a/drivers/staging/rtl8723au/include/osdep_service.h b/drivers/staging/rtl8723au/include/osdep_service.h
new file mode 100644
index 000000000..dedb41874
--- /dev/null
+++ b/drivers/staging/rtl8723au/include/osdep_service.h
@@ -0,0 +1,88 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2011 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * 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.
+ *
+ ******************************************************************************/
+#ifndef __OSDEP_SERVICE_H_
+#define __OSDEP_SERVICE_H_
+
+#define _FAIL 0
+#define _SUCCESS 1
+#define RTW_RX_HANDLED 2
+
+#include <linux/spinlock.h>
+#include <linux/compiler.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/kref.h>
+#include <linux/netdevice.h>
+#include <linux/skbuff.h>
+#include <asm/uaccess.h>
+#include <asm/byteorder.h>
+#include <asm/atomic.h>
+#include <asm/io.h>
+#include <linux/semaphore.h>
+#include <linux/sem.h>
+#include <linux/sched.h>
+#include <linux/etherdevice.h>
+#include <linux/wireless.h>
+#include <linux/if_arp.h>
+#include <linux/rtnetlink.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h> /* for struct tasklet_struct */
+#include <linux/ip.h>
+
+#include <net/ieee80211_radiotap.h>
+#include <net/cfg80211.h>
+
+struct rtw_queue {
+ struct list_head queue;
+ spinlock_t lock;
+};
+
+static inline struct list_head *get_list_head(struct rtw_queue *queue)
+{
+ return &queue->queue;
+}
+
+static inline int rtw_netif_queue_stopped(struct net_device *pnetdev)
+{
+ return (netif_tx_queue_stopped(netdev_get_tx_queue(pnetdev, 0)) &&
+ netif_tx_queue_stopped(netdev_get_tx_queue(pnetdev, 1)) &&
+ netif_tx_queue_stopped(netdev_get_tx_queue(pnetdev, 2)) &&
+ netif_tx_queue_stopped(netdev_get_tx_queue(pnetdev, 3)));
+}
+
+static inline u32 CHKBIT(u32 x)
+{
+ WARN_ON(x >= 32);
+ if (x >= 32)
+ return 0;
+ return BIT(x);
+}
+
+extern unsigned char MCS_rate_2R23A[16];
+
+extern unsigned char MCS_rate_2R23A[16];
+extern unsigned char MCS_rate_1R23A[16];
+
+void _rtw_init_queue23a(struct rtw_queue *pqueue);
+
+/* Macros for handling unaligned memory accesses */
+
+#define RTW_GET_BE24(a) ((((u32) (a)[0]) << 16) | (((u32) (a)[1]) << 8) | \
+ ((u32) (a)[2]))
+
+#endif
diff --git a/drivers/staging/rtl8723au/include/recv_osdep.h b/drivers/staging/rtl8723au/include/recv_osdep.h
new file mode 100644
index 000000000..c2d3f1bd5
--- /dev/null
+++ b/drivers/staging/rtl8723au/include/recv_osdep.h
@@ -0,0 +1,36 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2011 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * 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.
+ *
+ ******************************************************************************/
+#ifndef __RECV_OSDEP_H_
+#define __RECV_OSDEP_H_
+
+#include <osdep_service.h>
+#include <drv_types.h>
+
+int _rtw_init_recv_priv23a(struct recv_priv *precvpriv, struct rtw_adapter *padapter);
+void _rtw_free_recv_priv23a (struct recv_priv *precvpriv);
+
+int rtw_recv_entry23a(struct recv_frame *precv_frame);
+int rtw_recv_indicatepkt23a(struct rtw_adapter *adapter, struct recv_frame *precv_frame);
+
+void rtw_handle_tkip_mic_err23a(struct rtw_adapter *padapter, u8 bgroup);
+
+int rtw_init_recv_priv(struct recv_priv *precvpriv, struct rtw_adapter *padapter);
+void rtw_free_recv_priv (struct recv_priv *precvpriv);
+
+int rtw_os_recv_resource_init(struct recv_priv *precvpriv, struct rtw_adapter *padapter);
+
+void rtw_init_recv_timer23a(struct recv_reorder_ctrl *preorder_ctrl);
+
+#endif
diff --git a/drivers/staging/rtl8723au/include/rtl8723a_bt-coexist.h b/drivers/staging/rtl8723au/include/rtl8723a_bt-coexist.h
new file mode 100644
index 000000000..7add5dfe0
--- /dev/null
+++ b/drivers/staging/rtl8723au/include/rtl8723a_bt-coexist.h
@@ -0,0 +1,1627 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2012 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * 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.
+ *
+ ******************************************************************************/
+#ifndef __RTL8723A_BT_COEXIST_H__
+#define __RTL8723A_BT_COEXIST_H__
+
+#include <drv_types.h>
+#include "odm_precomp.h"
+
+
+/* HEADER/PlatformDef.h */
+enum rt_media_status {
+ RT_MEDIA_DISCONNECT = 0,
+ RT_MEDIA_CONNECT = 1
+};
+
+/* ===== Below this line is sync from SD7 driver COMMON/BT.h ===== */
+
+#define BT_TMP_BUF_SIZE 100
+
+void BT_SignalCompensation(struct rtw_adapter *padapter,
+ u8 *rssi_wifi, u8 *rssi_bt);
+void BT_HaltProcess(struct rtw_adapter *padapter);
+void BT_LpsLeave(struct rtw_adapter *padapter);
+
+
+#define BT_HsConnectionEstablished(Adapter) false
+/* ===== End of sync from SD7 driver COMMON/BT.h ===== */
+
+/* HEADER/SecurityType.h */
+#define TKIP_ENC_KEY_POS 32 /* KEK_LEN+KEK_LEN) */
+#define MAXRSNIELEN 256
+
+/* COMMON/Protocol802_11.h */
+/* */
+/* 802.11 Management frame Status Code field */
+/* */
+struct octet_string {
+ u8 *Octet;
+ u16 Length;
+};
+
+
+/* AES_CCMP specific */
+enum {
+ AESCCMP_BLK_SIZE = 16, /* # octets in an AES block */
+ AESCCMP_MAX_PACKET = 4*512, /* largest packet size */
+ AESCCMP_N_RESERVED = 0, /* reserved nonce octet value */
+ AESCCMP_A_DATA = 0x40, /* the Adata bit in the flags */
+ AESCCMP_M_SHIFT = 3, /* how much to shift the 3-bit M field */
+ AESCCMP_L_SHIFT = 0, /* how much to shift the 3-bit L field */
+ AESCCMP_L_SIZE = 2, /* size of the l(m) length field (in octets) */
+ AESCCMP_OFFSET_SC = 22,
+ AESCCMP_OFFSET_DURATION = 4,
+ AESCCMP_OFFSET_A2 = 10,
+ AESCCMP_OFFSET_A4 = 24,
+ AESCCMP_QC_TID_MASK = 0x0f,
+ AESCCMP_BLK_SIZE_TOTAL = 16*16, /* Added by Annie for CKIP AES MIC BSOD, 2006-08-17. */
+ /* 16*8 < 4*60 Resove to 16*16 */
+};
+
+/* Key Length */
+#define PMK_LEN 32
+#define PTK_LEN_TKIP 64
+#define GTK_LEN 32
+#define KEY_NONCE_LEN 32
+
+
+/* COMMON/Dot11d.h */
+struct chnl_txpower_triple {
+ u8 FirstChnl;
+ u8 NumChnls;
+ s8 MaxTxPowerInDbm;
+};
+
+
+/* ===== Below this line is sync from SD7 driver COMMON/bt_hci.h ===== */
+/* The following is for BT 3.0 + HS HCI COMMAND ERRORS CODES */
+
+#define Max80211PALPDUSize 1492
+#define Max80211AMPASSOCLen 672
+#define MinGUserPrio 4
+#define MaxGUserPrio 7
+#define BEUserPrio0 0
+#define BEUserPrio1 3
+#define Max80211BeaconPeriod 2000
+#define ShortRangeModePowerMax 4
+
+#define BT_Default_Chnl 10
+#define ACLDataHeaderLen 4
+
+#define BTTotalDataBlockNum 0x100
+#define BTLocalBufNum 0x200
+#define BTMaxDataBlockLen 0x800
+#define BTTOTALBANDWIDTH 0x7530
+#define BTMAXBANDGUBANDWIDTH 0x4e20
+#define TmpLocalBufSize 0x100
+#define BTSynDataPacketLength 0xff
+/* */
+
+#define BTMaxAuthCount 5
+#define BTMaxAsocCount 5
+
+#define MAX_LOGICAL_LINK_NUM 2 /* temporarily define */
+#define MAX_BT_ASOC_ENTRY_NUM 2 /* temporarily define */
+
+#define INVALID_PL_HANDLE 0xff
+#define INVALID_ENTRY_NUM 0xff
+/* */
+
+#define CAM_BT_START_INDEX (HALF_CAM_ENTRY - 4) /* MAX_BT_ASOC_ENTRY_NUM : 4 !!! */
+#define BT_HWCAM_STAR CAM_BT_START_INDEX /* We used HALF_CAM_ENTRY ~ HALF_CAM_ENTRY -MAX_BT_ASOC_ENTRY_NUM */
+
+enum hci_status {
+ HCI_STATUS_SUCCESS = 0x00, /* Success */
+ HCI_STATUS_UNKNOW_HCI_CMD = 0x01, /* Unknown HCI Command */
+ HCI_STATUS_UNKNOW_CONNECT_ID = 0X02, /* Unknown Connection Identifier */
+ HCI_STATUS_HW_FAIL = 0X03, /* Hardware Failure */
+ HCI_STATUS_PAGE_TIMEOUT = 0X04, /* Page Timeout */
+ HCI_STATUS_AUTH_FAIL = 0X05, /* Authentication Failure */
+ HCI_STATUS_PIN_OR_KEY_MISSING = 0X06, /* PIN or Key Missing */
+ HCI_STATUS_MEM_CAP_EXCEED = 0X07, /* Memory Capacity Exceeded */
+ HCI_STATUS_CONNECT_TIMEOUT = 0X08, /* Connection Timeout */
+ HCI_STATUS_CONNECT_LIMIT = 0X09, /* Connection Limit Exceeded */
+ HCI_STATUS_SYN_CONNECT_LIMIT = 0X0a, /* Synchronous Connection Limit To A Device Exceeded */
+ HCI_STATUS_ACL_CONNECT_EXISTS = 0X0b, /* ACL Connection Already Exists */
+ HCI_STATUS_CMD_DISALLOW = 0X0c, /* Command Disallowed */
+ HCI_STATUS_CONNECT_RJT_LIMIT_RESOURCE = 0X0d, /* Connection Rejected due to Limited Resources */
+ HCI_STATUS_CONNECT_RJT_SEC_REASON = 0X0e, /* Connection Rejected Due To Security Reasons */
+ HCI_STATUS_CONNECT_RJT_UNACCEPT_BD_ADDR = 0X0f, /* Connection Rejected due to Unacceptable BD_ADDR */
+ HCI_STATUS_CONNECT_ACCEPT_TIMEOUT = 0X10, /* Connection Accept Timeout Exceeded */
+ HCI_STATUS_UNSUPPORT_FEATURE_PARA_VALUE = 0X11, /* Unsupported Feature or Parameter Value */
+ HCI_STATUS_INVALID_HCI_CMD_PARA_VALUE = 0X12, /* Invalid HCI Command Parameters */
+ HCI_STATUS_REMOTE_USER_TERMINATE_CONNECT = 0X13, /* Remote User Terminated Connection */
+ HCI_STATUS_REMOTE_DEV_TERMINATE_LOW_RESOURCE = 0X14, /* Remote Device Terminated Connection due to Low Resources */
+ HCI_STATUS_REMOTE_DEV_TERMINATE_CONNECT_POWER_OFF = 0X15, /* Remote Device Terminated Connection due to Power Off */
+ HCI_STATUS_CONNECT_TERMINATE_LOCAL_HOST = 0X16, /* Connection Terminated By Local Host */
+ HCI_STATUS_REPEATE_ATTEMPT = 0X17, /* Repeated Attempts */
+ HCI_STATUS_PAIR_NOT_ALLOW = 0X18, /* Pairing Not Allowed */
+ HCI_STATUS_UNKNOW_LMP_PDU = 0X19, /* Unknown LMP PDU */
+ HCI_STATUS_UNSUPPORT_REMOTE_LMP_FEATURE = 0X1a, /* Unsupported Remote Feature / Unsupported LMP Feature */
+ HCI_STATUS_SOC_OFFSET_REJECT = 0X1b, /* SCO Offset Rejected */
+ HCI_STATUS_SOC_INTERVAL_REJECT = 0X1c, /* SCO Interval Rejected */
+ HCI_STATUS_SOC_AIR_MODE_REJECT = 0X1d,/* SCO Air Mode Rejected */
+ HCI_STATUS_INVALID_LMP_PARA = 0X1e, /* Invalid LMP Parameters */
+ HCI_STATUS_UNSPECIFIC_ERROR = 0X1f, /* Unspecified Error */
+ HCI_STATUS_UNSUPPORT_LMP_PARA_VALUE = 0X20, /* Unsupported LMP Parameter Value */
+ HCI_STATUS_ROLE_CHANGE_NOT_ALLOW = 0X21, /* Role Change Not Allowed */
+ HCI_STATUS_LMP_RESPONSE_TIMEOUT = 0X22, /* LMP Response Timeout */
+ HCI_STATUS_LMP_ERROR_TRANSACTION_COLLISION = 0X23, /* LMP Error Transaction Collision */
+ HCI_STATUS_LMP_PDU_NOT_ALLOW = 0X24, /* LMP PDU Not Allowed */
+ HCI_STATUS_ENCRYPTION_MODE_NOT_ALLOW = 0X25, /* Encryption Mode Not Acceptable */
+ HCI_STATUS_LINK_KEY_CAN_NOT_CHANGE = 0X26, /* Link Key Can Not be Changed */
+ HCI_STATUS_REQUEST_QOS_NOT_SUPPORT = 0X27, /* Requested QoS Not Supported */
+ HCI_STATUS_INSTANT_PASSED = 0X28, /* Instant Passed */
+ HCI_STATUS_PAIRING_UNIT_KEY_NOT_SUPPORT = 0X29, /* Pairing With Unit Key Not Supported */
+ HCI_STATUS_DIFFERENT_TRANSACTION_COLLISION = 0X2a, /* Different Transaction Collision */
+ HCI_STATUS_RESERVE_1 = 0X2b, /* Reserved */
+ HCI_STATUS_QOS_UNACCEPT_PARA = 0X2c, /* QoS Unacceptable Parameter */
+ HCI_STATUS_QOS_REJECT = 0X2d, /* QoS Rejected */
+ HCI_STATUS_CHNL_CLASSIFICATION_NOT_SUPPORT = 0X2e, /* Channel Classification Not Supported */
+ HCI_STATUS_INSUFFICIENT_SECURITY = 0X2f, /* Insufficient Security */
+ HCI_STATUS_PARA_OUT_OF_RANGE = 0x30, /* Parameter Out Of Mandatory Range */
+ HCI_STATUS_RESERVE_2 = 0X31, /* Reserved */
+ HCI_STATUS_ROLE_SWITCH_PENDING = 0X32, /* Role Switch Pending */
+ HCI_STATUS_RESERVE_3 = 0X33, /* Reserved */
+ HCI_STATUS_RESERVE_SOLT_VIOLATION = 0X34, /* Reserved Slot Violation */
+ HCI_STATUS_ROLE_SWITCH_FAIL = 0X35, /* Role Switch Failed */
+ HCI_STATUS_EXTEND_INQUIRY_RSP_TOO_LARGE = 0X36, /* Extended Inquiry Response Too Large */
+ HCI_STATUS_SEC_SIMPLE_PAIRING_NOT_SUPPORT = 0X37, /* Secure Simple Pairing Not Supported By Host. */
+ HCI_STATUS_HOST_BUSY_PAIRING = 0X38, /* Host Busy - Pairing */
+ HCI_STATUS_CONNECT_REJ_NOT_SUIT_CHNL_FOUND = 0X39, /* Connection Rejected due to No Suitable Channel Found */
+ HCI_STATUS_CONTROLLER_BUSY = 0X3a /* CONTROLLER BUSY */
+};
+
+/* */
+/* The following is for BT 3.0 + HS HCI COMMAND */
+/* */
+
+/* bit 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 */
+/* | OCF | OGF | */
+/* */
+
+/* OGF 0x01 */
+#define LINK_CONTROL_COMMANDS 0x01
+enum link_control_commands {
+ HCI_INQUIRY = 0x0001,
+ HCI_INQUIRY_CANCEL = 0x0002,
+ HCI_PERIODIC_INQUIRY_MODE = 0x0003,
+ HCI_EXIT_PERIODIC_INQUIRY_MODE = 0x0004,
+ HCI_CREATE_CONNECTION = 0x0005,
+ HCI_DISCONNECT = 0x0006,
+ HCI_CREATE_CONNECTION_CANCEL = 0x0008,
+ HCI_ACCEPT_CONNECTIONREQUEST = 0x0009,
+ HCI_REJECT_CONNECTION_REQUEST = 0x000a,
+ HCI_LINK_KEY_REQUEST_REPLY = 0x000b,
+ HCI_LINK_KEY_REQUEST_NEGATIVE_REPLY = 0x000c,
+ HCI_PIN_CODE_REQUEST_REPLY = 0x000d,
+ HCI_PIN_CODE_REQUEST_NEGATIVE_REPLY = 0x000e,
+ HCI_CHANGE_CONNECTION_PACKET_TYPE = 0x000f,
+ HCI_AUTHENTICATION_REQUESTED = 0x0011,
+ HCI_SET_CONNECTION_ENCRYPTION = 0x0013,
+ HCI_CHANGE_CONNECTION_LINK_KEY = 0x0015,
+ HCI_MASTER_LINK_KEY = 0x0017,
+ HCI_REMOTE_NAME_REQUEST = 0x0019,
+ HCI_REMOTE_NAME_REQUEST_CANCEL = 0x001a,
+ HCI_READ_REMOTE_SUPPORTED_FEATURES = 0x001b,
+ HCI_READ_REMOTE_EXTENDED_FEATURES = 0x001c,
+ HCI_READ_REMOTE_VERSION_INFORMATION = 0x001d,
+ HCI_READ_CLOCK_OFFSET = 0x001f,
+ HCI_READ_LMP_HANDLE = 0x0020,
+ HCI_SETUP_SYNCHRONOUS_CONNECTION = 0x0028,
+ HCI_ACCEPT_SYNCHRONOUS_CONNECTION_REQUEST = 0x0029,
+ HCI_REJECT_SYNCHRONOUS_CONNECTION_REQUEST = 0x002a,
+ HCI_IO_CAPABILITY_REQUEST_REPLY = 0x002b,
+ HCI_USER_CONFIRMATION_REQUEST_REPLY = 0x002c,
+ HCI_USER_CONFIRMATION_REQUEST_NEGATIVE_REPLY = 0x002d,
+ HCI_USER_PASSKEY_REQUEST_REPLY = 0x002e,
+ HCI_USER_PASSKEY_REQUESTNEGATIVE_REPLY = 0x002f,
+ HCI_REMOTE_OOB_DATA_REQUEST_REPLY = 0x0030,
+ HCI_REMOTE_OOB_DATA_REQUEST_NEGATIVE_REPLY = 0x0033,
+ HCI_IO_CAPABILITY_REQUEST_NEGATIVE_REPLY = 0x0034,
+ HCI_CREATE_PHYSICAL_LINK = 0x0035,
+ HCI_ACCEPT_PHYSICAL_LINK = 0x0036,
+ HCI_DISCONNECT_PHYSICAL_LINK = 0x0037,
+ HCI_CREATE_LOGICAL_LINK = 0x0038,
+ HCI_ACCEPT_LOGICAL_LINK = 0x0039,
+ HCI_DISCONNECT_LOGICAL_LINK = 0x003a,
+ HCI_LOGICAL_LINK_CANCEL = 0x003b,
+ HCI_FLOW_SPEC_MODIFY = 0x003c
+};
+
+/* OGF 0x02 */
+#define HOLD_MODE_COMMAND 0x02
+enum hold_mode_command {
+ HCI_HOLD_MODE = 0x0001,
+ HCI_SNIFF_MODE = 0x0002,
+ HCI_EXIT_SNIFF_MODE = 0x0003,
+ HCI_PARK_STATE = 0x0005,
+ HCI_EXIT_PARK_STATE = 0x0006,
+ HCI_QOS_SETUP = 0x0007,
+ HCI_ROLE_DISCOVERY = 0x0009,
+ HCI_SWITCH_ROLE = 0x000b,
+ HCI_READ_LINK_POLICY_SETTINGS = 0x000c,
+ HCI_WRITE_LINK_POLICY_SETTINGS = 0x000d,
+ HCI_READ_DEFAULT_LINK_POLICY_SETTINGS = 0x000e,
+ HCI_WRITE_DEFAULT_LINK_POLICY_SETTINGS = 0x000f,
+ HCI_FLOW_SPECIFICATION = 0x0010,
+ HCI_SNIFF_SUBRATING = 0x0011
+};
+
+/* OGF 0x03 */
+#define OGF_SET_EVENT_MASK_COMMAND 0x03
+enum set_event_mask_command {
+ HCI_SET_EVENT_MASK = 0x0001,
+ HCI_RESET = 0x0003,
+ HCI_SET_EVENT_FILTER = 0x0005,
+ HCI_FLUSH = 0x0008,
+ HCI_READ_PIN_TYPE = 0x0009,
+ HCI_WRITE_PIN_TYPE = 0x000a,
+ HCI_CREATE_NEW_UNIT_KEY = 0x000b,
+ HCI_READ_STORED_LINK_KEY = 0x000d,
+ HCI_WRITE_STORED_LINK_KEY = 0x0011,
+ HCI_DELETE_STORED_LINK_KEY = 0x0012,
+ HCI_WRITE_LOCAL_NAME = 0x0013,
+ HCI_READ_LOCAL_NAME = 0x0014,
+ HCI_READ_CONNECTION_ACCEPT_TIMEOUT = 0x0015,
+ HCI_WRITE_CONNECTION_ACCEPT_TIMEOUT = 0x0016,
+ HCI_READ_PAGE_TIMEOUT = 0x0017,
+ HCI_WRITE_PAGE_TIMEOUT = 0x0018,
+ HCI_READ_SCAN_ENABLE = 0x0019,
+ HCI_WRITE_SCAN_ENABLE = 0x001a,
+ HCI_READ_PAGE_SCAN_ACTIVITY = 0x001b,
+ HCI_WRITE_PAGE_SCAN_ACTIVITY = 0x001c,
+ HCI_READ_INQUIRY_SCAN_ACTIVITY = 0x001d,
+ HCI_WRITE_INQUIRY_SCAN_ACTIVITY = 0x001e,
+ HCI_READ_AUTHENTICATION_ENABLE = 0x001f,
+ HCI_WRITE_AUTHENTICATION_ENABLE = 0x0020,
+ HCI_READ_CLASS_OF_DEVICE = 0x0023,
+ HCI_WRITE_CLASS_OF_DEVICE = 0x0024,
+ HCI_READ_VOICE_SETTING = 0x0025,
+ HCI_WRITE_VOICE_SETTING = 0x0026,
+ HCI_READ_AUTOMATIC_FLUSH_TIMEOUT = 0x0027,
+ HCI_WRITE_AUTOMATIC_FLUSH_TIMEOUT = 0x0028,
+ HCI_READ_NUM_BROADCAST_RETRANSMISSIONS = 0x0029,
+ HCI_WRITE_NUM_BROADCAST_RETRANSMISSIONS = 0x002a,
+ HCI_READ_HOLD_MODE_ACTIVITY = 0x002b,
+ HCI_WRITE_HOLD_MODE_ACTIVITY = 0x002c,
+ HCI_READ_SYNCHRONOUS_FLOW_CONTROL_ENABLE = 0x002e,
+ HCI_WRITE_SYNCHRONOUS_FLOW_CONTROL_ENABLE = 0x002f,
+ HCI_SET_CONTROLLER_TO_HOST_FLOW_CONTROL = 0x0031,
+ HCI_HOST_BUFFER_SIZE = 0x0033,
+ HCI_HOST_NUMBER_OF_COMPLETED_PACKETS = 0x0035,
+ HCI_READ_LINK_SUPERVISION_TIMEOUT = 0x0036,
+ HCI_WRITE_LINK_SUPERVISION_TIMEOUT = 0x0037,
+ HCI_READ_NUMBER_OF_SUPPORTED_IAC = 0x0038,
+ HCI_READ_CURRENT_IAC_LAP = 0x0039,
+ HCI_WRITE_CURRENT_IAC_LAP = 0x003a,
+ HCI_READ_PAGE_SCAN_MODE = 0x003d,
+ HCI_WRITE_PAGE_SCAN_MODE = 0x003e,
+ HCI_SET_AFH_HOST_CHANNEL_CLASSIFICATION = 0x003f,
+ HCI_READ_INQUIRY_SCAN_TYPE = 0x0042,
+ HCI_WRITE_INQUIRY_SCAN_TYPE = 0x0043,
+ HCI_READ_INQUIRY_MODE = 0x0044,
+ HCI_WRITE_INQUIRY_MODE = 0x0045,
+ HCI_READ_PAGE_SCAN_TYPE = 0x0046,
+ HCI_WRITE_PAGE_SCAN_TYPE = 0x0047,
+ HCI_READ_AFH_CHANNEL_ASSESSMENT_MODE = 0x0048,
+ HCI_WRITE_AFH_CHANNEL_ASSESSMENT_MODE = 0x0049,
+ HCI_READ_EXTENDED_INQUIRY_RESPONSE = 0x0051,
+ HCI_WRITE_EXTENDED_INQUIRY_RESPONSE = 0x0052,
+ HCI_REFRESH_ENCRYPTION_KEY = 0x0053,
+ HCI_READ_SIMPLE_PAIRING_MODE = 0x0055,
+ HCI_WRITE_SIMPLE_PAIRING_MODE = 0x0056,
+ HCI_READ_LOCAL_OOB_DATA = 0x0057,
+ HCI_READ_INQUIRY_RESPONSE_TRANSMIT_POWER_LEVEL = 0x0058,
+ HCI_WRITE_INQUIRY_TRANSMIT_POWER_LEVEL = 0x0059,
+ HCI_READ_DEFAULT_ERRONEOUS_DATA_REPORTING = 0x005a,
+ HCI_WRITE_DEFAULT_ERRONEOUS_DATA_REPORTING = 0x005b,
+ HCI_ENHANCED_FLUSH = 0x005f,
+ HCI_SEND_KEYPRESS_NOTIFICATION = 0x0060,
+ HCI_READ_LOGICAL_LINK_ACCEPT_TIMEOUT = 0x0061,
+ HCI_WRITE_LOGICAL_LINK_ACCEPT_TIMEOUT = 0x0062,
+ HCI_SET_EVENT_MASK_PAGE_2 = 0x0063,
+ HCI_READ_LOCATION_DATA = 0x0064,
+ HCI_WRITE_LOCATION_DATA = 0x0065,
+ HCI_READ_FLOW_CONTROL_MODE = 0x0066,
+ HCI_WRITE_FLOW_CONTROL_MODE = 0x0067,
+ HCI_READ_ENHANCE_TRANSMIT_POWER_LEVEL = 0x0068,
+ HCI_READ_BEST_EFFORT_FLUSH_TIMEOUT = 0x0069,
+ HCI_WRITE_BEST_EFFORT_FLUSH_TIMEOUT = 0x006a,
+ HCI_SHORT_RANGE_MODE = 0x006b
+};
+
+/* OGF 0x04 */
+#define OGF_INFORMATIONAL_PARAMETERS 0x04
+enum informational_params {
+ HCI_READ_LOCAL_VERSION_INFORMATION = 0x0001,
+ HCI_READ_LOCAL_SUPPORTED_COMMANDS = 0x0002,
+ HCI_READ_LOCAL_SUPPORTED_FEATURES = 0x0003,
+ HCI_READ_LOCAL_EXTENDED_FEATURES = 0x0004,
+ HCI_READ_BUFFER_SIZE = 0x0005,
+ HCI_READ_BD_ADDR = 0x0009,
+ HCI_READ_DATA_BLOCK_SIZE = 0x000a
+};
+
+/* OGF 0x05 */
+#define OGF_STATUS_PARAMETERS 0x05
+enum status_params {
+ HCI_READ_FAILED_CONTACT_COUNTER = 0x0001,
+ HCI_RESET_FAILED_CONTACT_COUNTER = 0x0002,
+ HCI_READ_LINK_QUALITY = 0x0003,
+ HCI_READ_RSSI = 0x0005,
+ HCI_READ_AFH_CHANNEL_MAP = 0x0006,
+ HCI_READ_CLOCK = 0x0007,
+ HCI_READ_ENCRYPTION_KEY_SIZE = 0x0008,
+ HCI_READ_LOCAL_AMP_INFO = 0x0009,
+ HCI_READ_LOCAL_AMP_ASSOC = 0x000a,
+ HCI_WRITE_REMOTE_AMP_ASSOC = 0x000b
+};
+
+/* OGF 0x06 */
+#define OGF_TESTING_COMMANDS 0x06
+enum testing_commands {
+ HCI_READ_LOOPBACK_MODE = 0x0001,
+ HCI_WRITE_LOOPBACK_MODE = 0x0002,
+ HCI_ENABLE_DEVICE_UNDER_TEST_MODE = 0x0003,
+ HCI_WRITE_SIMPLE_PAIRING_DEBUG_MODE = 0x0004,
+ HCI_ENABLE_AMP_RECEIVER_REPORTS = 0x0007,
+ HCI_AMP_TEST_END = 0x0008,
+ HCI_AMP_TEST_COMMAND = 0x0009
+};
+
+/* OGF 0x3f */
+#define OGF_EXTENSION 0X3f
+enum hci_extension_commands {
+ HCI_SET_ACL_LINK_DATA_FLOW_MODE = 0x0010,
+ HCI_SET_ACL_LINK_STATUS = 0x0020,
+ HCI_SET_SCO_LINK_STATUS = 0x0030,
+ HCI_SET_RSSI_VALUE = 0x0040,
+ HCI_SET_CURRENT_BLUETOOTH_STATUS = 0x0041,
+
+ /* The following is for RTK8723 */
+ HCI_EXTENSION_VERSION_NOTIFY = 0x0100,
+ HCI_LINK_STATUS_NOTIFY = 0x0101,
+ HCI_BT_OPERATION_NOTIFY = 0x0102,
+ HCI_ENABLE_WIFI_SCAN_NOTIFY = 0x0103,
+
+
+ /* The following is for IVT */
+ HCI_WIFI_CURRENT_CHANNEL = 0x0300,
+ HCI_WIFI_CURRENT_BANDWIDTH = 0x0301,
+ HCI_WIFI_CONNECTION_STATUS = 0x0302,
+};
+
+enum bt_spec {
+ BT_SPEC_1_0_b = 0x00,
+ BT_SPEC_1_1 = 0x01,
+ BT_SPEC_1_2 = 0x02,
+ BT_SPEC_2_0_EDR = 0x03,
+ BT_SPEC_2_1_EDR = 0x04,
+ BT_SPEC_3_0_HS = 0x05,
+ BT_SPEC_4_0 = 0x06
+};
+
+/* The following is for BT 3.0 + HS EVENTS */
+enum hci_event {
+ HCI_EVENT_INQUIRY_COMPLETE = 0x01,
+ HCI_EVENT_INQUIRY_RESULT = 0x02,
+ HCI_EVENT_CONNECTION_COMPLETE = 0x03,
+ HCI_EVENT_CONNECTION_REQUEST = 0x04,
+ HCI_EVENT_DISCONNECTION_COMPLETE = 0x05,
+ HCI_EVENT_AUTHENTICATION_COMPLETE = 0x06,
+ HCI_EVENT_REMOTE_NAME_REQUEST_COMPLETE = 0x07,
+ HCI_EVENT_ENCRYPTION_CHANGE = 0x08,
+ HCI_EVENT_CHANGE_LINK_KEY_COMPLETE = 0x09,
+ HCI_EVENT_MASTER_LINK_KEY_COMPLETE = 0x0a,
+ HCI_EVENT_READ_REMOTE_SUPPORT_FEATURES_COMPLETE = 0x0b,
+ HCI_EVENT_READ_REMOTE_VER_INFO_COMPLETE = 0x0c,
+ HCI_EVENT_QOS_SETUP_COMPLETE = 0x0d,
+ HCI_EVENT_COMMAND_COMPLETE = 0x0e,
+ HCI_EVENT_COMMAND_STATUS = 0x0f,
+ HCI_EVENT_HARDWARE_ERROR = 0x10,
+ HCI_EVENT_FLUSH_OCCRUED = 0x11,
+ HCI_EVENT_ROLE_CHANGE = 0x12,
+ HCI_EVENT_NUMBER_OF_COMPLETE_PACKETS = 0x13,
+ HCI_EVENT_MODE_CHANGE = 0x14,
+ HCI_EVENT_RETURN_LINK_KEYS = 0x15,
+ HCI_EVENT_PIN_CODE_REQUEST = 0x16,
+ HCI_EVENT_LINK_KEY_REQUEST = 0x17,
+ HCI_EVENT_LINK_KEY_NOTIFICATION = 0x18,
+ HCI_EVENT_LOOPBACK_COMMAND = 0x19,
+ HCI_EVENT_DATA_BUFFER_OVERFLOW = 0x1a,
+ HCI_EVENT_MAX_SLOTS_CHANGE = 0x1b,
+ HCI_EVENT_READ_CLOCK_OFFSET_COMPLETE = 0x1c,
+ HCI_EVENT_CONNECT_PACKET_TYPE_CHANGE = 0x1d,
+ HCI_EVENT_QOS_VIOLATION = 0x1e,
+ HCI_EVENT_PAGE_SCAN_REPETITION_MODE_CHANGE = 0x20,
+ HCI_EVENT_FLOW_SEPC_COMPLETE = 0x21,
+ HCI_EVENT_INQUIRY_RESULT_WITH_RSSI = 0x22,
+ HCI_EVENT_READ_REMOTE_EXT_FEATURES_COMPLETE = 0x23,
+ HCI_EVENT_SYNC_CONNECT_COMPLETE = 0x2c,
+ HCI_EVENT_SYNC_CONNECT_CHANGE = 0x2d,
+ HCI_EVENT_SNIFFER_SUBRATING = 0x2e,
+ HCI_EVENT_EXTENTED_INQUIRY_RESULT = 0x2f,
+ HCI_EVENT_ENCRYPTION_KEY_REFLASH_COMPLETE = 0x30,
+ HCI_EVENT_IO_CAPIBILITY_COMPLETE = 0x31,
+ HCI_EVENT_IO_CAPIBILITY_RESPONSE = 0x32,
+ HCI_EVENT_USER_CONFIRMTION_REQUEST = 0x33,
+ HCI_EVENT_USER_PASSKEY_REQUEST = 0x34,
+ HCI_EVENT_REMOTE_OOB_DATA_REQUEST = 0x35,
+ HCI_EVENT_SIMPLE_PAIRING_COMPLETE = 0x36,
+ HCI_EVENT_LINK_SUPERVISION_TIMEOUT_CHANGE = 0x38,
+ HCI_EVENT_ENHANCED_FLUSH_COMPLETE = 0x39,
+ HCI_EVENT_USER_PASSKEY_NOTIFICATION = 0x3b,
+ HCI_EVENT_KEYPRESS_NOTIFICATION = 0x3c,
+ HCI_EVENT_REMOTE_HOST_SUPPORT_FEATURES_NOTIFICATION = 0x3d,
+ HCI_EVENT_PHY_LINK_COMPLETE = 0x40,
+ HCI_EVENT_CHANNEL_SELECT = 0x41,
+ HCI_EVENT_DISCONNECT_PHY_LINK_COMPLETE = 0x42,
+ HCI_EVENT_PHY_LINK_LOSS_EARLY_WARNING = 0x43,
+ HCI_EVENT_PHY_LINK_RECOVER = 0x44,
+ HCI_EVENT_LOGICAL_LINK_COMPLETE = 0x45,
+ HCI_EVENT_DISCONNECT_LOGICAL_LINK_COMPLETE = 0x46,
+ HCI_EVENT_FLOW_SPEC_MODIFY_COMPLETE = 0x47,
+ HCI_EVENT_NUM_OF_COMPLETE_DATA_BLOCKS = 0x48,
+ HCI_EVENT_AMP_START_TEST = 0x49,
+ HCI_EVENT_AMP_TEST_END = 0x4a,
+ HCI_EVENT_AMP_RECEIVER_REPORT = 0x4b,
+ HCI_EVENT_SHORT_RANGE_MODE_CHANGE_COMPLETE = 0x4c,
+ HCI_EVENT_AMP_STATUS_CHANGE = 0x4d,
+ HCI_EVENT_EXTENSION_RTK = 0xfe,
+ HCI_EVENT_EXTENSION_MOTO = 0xff,
+};
+
+enum hci_extension_event_moto {
+ HCI_EVENT_GET_BT_RSSI = 0x01,
+};
+
+enum hci_extension_event {
+ HCI_EVENT_EXT_WIFI_SCAN_NOTIFY = 0x01,
+};
+
+enum hci_event_mask_page_2 {
+ EMP2_HCI_EVENT_PHY_LINK_COMPLETE = 0x0000000000000001,
+ EMP2_HCI_EVENT_CHANNEL_SELECT = 0x0000000000000002,
+ EMP2_HCI_EVENT_DISCONNECT_PHY_LINK_COMPLETE = 0x0000000000000004,
+ EMP2_HCI_EVENT_PHY_LINK_LOSS_EARLY_WARNING = 0x0000000000000008,
+ EMP2_HCI_EVENT_PHY_LINK_RECOVER = 0x0000000000000010,
+ EMP2_HCI_EVENT_LOGICAL_LINK_COMPLETE = 0x0000000000000020,
+ EMP2_HCI_EVENT_DISCONNECT_LOGICAL_LINK_COMPLETE = 0x0000000000000040,
+ EMP2_HCI_EVENT_FLOW_SPEC_MODIFY_COMPLETE = 0x0000000000000080,
+ EMP2_HCI_EVENT_NUM_OF_COMPLETE_DATA_BLOCKS = 0x0000000000000100,
+ EMP2_HCI_EVENT_AMP_START_TEST = 0x0000000000000200,
+ EMP2_HCI_EVENT_AMP_TEST_END = 0x0000000000000400,
+ EMP2_HCI_EVENT_AMP_RECEIVER_REPORT = 0x0000000000000800,
+ EMP2_HCI_EVENT_SHORT_RANGE_MODE_CHANGE_COMPLETE = 0x0000000000001000,
+ EMP2_HCI_EVENT_AMP_STATUS_CHANGE = 0x0000000000002000,
+};
+
+enum hci_state_machine {
+ HCI_STATE_STARTING = 0x01,
+ HCI_STATE_CONNECTING = 0x02,
+ HCI_STATE_AUTHENTICATING = 0x04,
+ HCI_STATE_CONNECTED = 0x08,
+ HCI_STATE_DISCONNECTING = 0x10,
+ HCI_STATE_DISCONNECTED = 0x20
+};
+
+enum amp_assoc_structure_type {
+ AMP_MAC_ADDR = 0x01,
+ AMP_PREFERRED_CHANNEL_LIST = 0x02,
+ AMP_CONNECTED_CHANNEL = 0x03,
+ AMP_80211_PAL_CAP_LIST = 0x04,
+ AMP_80211_PAL_VISION = 0x05,
+ AMP_RESERVED_FOR_TESTING = 0x33
+};
+
+enum amp_btap_type {
+ AMP_BTAP_NONE,
+ AMP_BTAP_CREATOR,
+ AMP_BTAP_JOINER
+};
+
+enum hci_state_with_cmd {
+ STATE_CMD_CREATE_PHY_LINK,
+ STATE_CMD_ACCEPT_PHY_LINK,
+ STATE_CMD_DISCONNECT_PHY_LINK,
+ STATE_CMD_CONNECT_ACCEPT_TIMEOUT,
+ STATE_CMD_MAC_START_COMPLETE,
+ STATE_CMD_MAC_START_FAILED,
+ STATE_CMD_MAC_CONNECT_COMPLETE,
+ STATE_CMD_MAC_CONNECT_FAILED,
+ STATE_CMD_MAC_DISCONNECT_INDICATE,
+ STATE_CMD_MAC_CONNECT_CANCEL_INDICATE,
+ STATE_CMD_4WAY_FAILED,
+ STATE_CMD_4WAY_SUCCESSED,
+ STATE_CMD_ENTER_STATE,
+ STATE_CMD_NO_SUCH_CMD,
+};
+
+enum hci_service_type {
+ SERVICE_NO_TRAFFIC,
+ SERVICE_BEST_EFFORT,
+ SERVICE_GUARANTEE
+};
+
+enum hci_traffic_mode {
+ TRAFFIC_MODE_BEST_EFFORT = 0x00,
+ TRAFFIC_MODE_GUARANTEED_LATENCY = 0x01,
+ TRAFFIC_MODE_GUARANTEED_BANDWIDTH = 0x02,
+ TRAFFIC_MODE_GUARANTEED_LATENCY_AND_BANDWIDTH = 0x03
+};
+
+#define HCIOPCODE(_OCF, _OGF) (_OGF<<10|_OCF)
+#define HCIOPCODELOW(_OCF, _OGF) (u8)(HCIOPCODE(_OCF, _OGF)&0x00ff)
+#define HCIOPCODEHIGHT(_OCF, _OGF) (u8)(HCIOPCODE(_OCF, _OGF)>>8)
+
+#define TWOBYTE_HIGHTBYTE(_DATA) (u8)(_DATA>>8)
+#define TWOBYTE_LOWBYTE(_DATA) (u8)(_DATA)
+
+enum amp_status {
+ AMP_STATUS_AVA_PHY_PWR_DWN = 0x0,
+ AMP_STATUS_BT_USE_ONLY = 0x1,
+ AMP_STATUS_NO_CAPACITY_FOR_BT = 0x2,
+ AMP_STATUS_LOW_CAPACITY_FOR_BT = 0x3,
+ AMP_STATUS_MEDIUM_CAPACITY_FOR_BT = 0x4,
+ AMP_STATUS_HIGH_CAPACITY_FOR_BT = 0x5,
+ AMP_STATUS_FULL_CAPACITY_FOR_BT = 0x6
+};
+
+enum bt_wpa_msg_type {
+ Type_BT_4way1st = 0,
+ Type_BT_4way2nd = 1,
+ Type_BT_4way3rd = 2,
+ Type_BT_4way4th = 3,
+ Type_BT_unknow = 4
+};
+
+enum bt_connect_type {
+ BT_CONNECT_AUTH_REQ = 0x00,
+ BT_CONNECT_AUTH_RSP = 0x01,
+ BT_CONNECT_ASOC_REQ = 0x02,
+ BT_CONNECT_ASOC_RSP = 0x03,
+ BT_DISCONNECT = 0x04
+};
+
+enum bt_ll_service_type {
+ BT_LL_BE = 0x01,
+ BT_LL_GU = 0x02
+};
+
+enum bt_ll_flowspec {
+ BT_TX_BE_FS, /* TX best effort flowspec */
+ BT_RX_BE_FS, /* RX best effort flowspec */
+ BT_TX_GU_FS, /* TX guaranteed latency flowspec */
+ BT_RX_GU_FS, /* RX guaranteed latency flowspec */
+ BT_TX_BE_AGG_FS, /* TX aggregated best effort flowspec */
+ BT_RX_BE_AGG_FS, /* RX aggregated best effort flowspec */
+ BT_TX_GU_BW_FS, /* TX guaranteed bandwidth flowspec */
+ BT_RX_GU_BW_FS, /* RX guaranteed bandwidth flowspec */
+ BT_TX_GU_LARGE_FS, /* TX guaranteed latency flowspec, for testing only */
+ BT_RX_GU_LARGE_FS, /* RX guaranteed latency flowspec, for testing only */
+};
+
+enum bt_traffic_mode {
+ BT_MOTOR_EXT_BE = 0x00, /* Best Effort. Default. for HCRP, PAN, SDP, RFCOMM-based profiles like FTP, OPP, SPP, DUN, etc. */
+ BT_MOTOR_EXT_GUL = 0x01, /* Guaranteed Latency. This type of traffic is used e.g. for HID and AVRCP. */
+ BT_MOTOR_EXT_GUB = 0X02, /* Guaranteed Bandwidth. */
+ BT_MOTOR_EXT_GULB = 0X03 /* Guaranteed Latency and Bandwidth. for A2DP and VDP. */
+};
+
+enum bt_traffic_mode_profile {
+ BT_PROFILE_NONE,
+ BT_PROFILE_A2DP,
+ BT_PROFILE_PAN,
+ BT_PROFILE_HID,
+ BT_PROFILE_SCO
+};
+
+enum bt_link_role {
+ BT_LINK_MASTER = 0,
+ BT_LINK_SLAVE = 1
+};
+
+enum bt_state_wpa_auth {
+ STATE_WPA_AUTH_UNINITIALIZED,
+ STATE_WPA_AUTH_WAIT_PACKET_1, /* Join */
+ STATE_WPA_AUTH_WAIT_PACKET_2, /* Creat */
+ STATE_WPA_AUTH_WAIT_PACKET_3,
+ STATE_WPA_AUTH_WAIT_PACKET_4,
+ STATE_WPA_AUTH_SUCCESSED
+};
+
+#define BT_WPA_AUTH_TIMEOUT_PERIOD 1000
+#define BTMaxWPAAuthReTransmitCoun 5
+
+#define MAX_AMP_ASSOC_FRAG_LEN 248
+#define TOTAL_ALLOCIATE_ASSOC_LEN 1000
+
+struct hci_flow_spec {
+ u8 Identifier;
+ u8 ServiceType;
+ u16 MaximumSDUSize;
+ u32 SDUInterArrivalTime;
+ u32 AccessLatency;
+ u32 FlushTimeout;
+};
+
+struct hci_log_link_cmd_data {
+ u8 BtPhyLinkhandle;
+ u16 BtLogLinkhandle;
+ u8 BtTxFlowSpecID;
+ struct hci_flow_spec Tx_Flow_Spec;
+ struct hci_flow_spec Rx_Flow_Spec;
+ u32 TxPacketCount;
+ u32 BestEffortFlushTimeout;
+
+ u8 bLLCompleteEventIsSet;
+
+ u8 bLLCancelCMDIsSetandComplete;
+};
+
+struct hci_phy_link_cmd_data {
+ /* Physical_Link_Handle */
+ u8 BtPhyLinkhandle;
+
+ u16 LinkSuperversionTimeout;
+
+ /* u16 SuperTimeOutCnt; */
+
+ /* Dedicated_AMP_Key_Length */
+ u8 BtAMPKeyLen;
+ /* Dedicated_AMP_Key_Type */
+ u8 BtAMPKeyType;
+ /* Dedicated_AMP_Key */
+ u8 BtAMPKey[PMK_LEN];
+};
+
+struct amp_assoc_structure {
+ /* TYPE ID */
+ u8 TypeID;
+ /* Length */
+ u16 Length;
+ /* Value */
+ u8 Data[1];
+};
+
+struct amp_pref_chnl_regulatory {
+ u8 reXId;
+ u8 regulatoryClass;
+ u8 coverageClass;
+};
+
+struct amp_assoc_cmd_data {
+ /* Physical_Link_Handle */
+ u8 BtPhyLinkhandle;
+ /* Length_So_Far */
+ u16 LenSoFar;
+
+ u16 MaxRemoteASSOCLen;
+ /* AMP_ASSOC_Remaining_Length */
+ u16 AMPAssocRemLen;
+ /* AMP_ASSOC_fragment */
+ void *AMPAssocfragment;
+};
+
+struct hci_link_info {
+ u16 ConnectHandle;
+ u8 IncomingTrafficMode;
+ u8 OutgoingTrafficMode;
+ u8 BTProfile;
+ u8 BTCoreSpec;
+ s8 BT_RSSI;
+ u8 TrafficProfile;
+ u8 linkRole;
+};
+
+struct hci_ext_config {
+ struct hci_link_info linkInfo[MAX_BT_ASOC_ENTRY_NUM];
+ u8 btOperationCode;
+ u16 CurrentConnectHandle;
+ u8 CurrentIncomingTrafficMode;
+ u8 CurrentOutgoingTrafficMode;
+ s8 MIN_BT_RSSI;
+ u8 NumberOfHandle;
+ u8 NumberOfSCO;
+ u8 CurrentBTStatus;
+ u16 HCIExtensionVer;
+
+ /* Bt coexist related */
+ u8 btProfileCase;
+ u8 btProfileAction;
+ u8 bManualControl;
+ u8 bBTBusy;
+ u8 bBTA2DPBusy;
+ u8 bEnableWifiScanNotify;
+
+ u8 bHoldForBtOperation;
+ u32 bHoldPeriodCnt;
+};
+
+struct hci_acl_packet_data {
+ u16 ACLDataPacketLen;
+ u8 SyncDataPacketLen;
+ u16 TotalNumACLDataPackets;
+ u16 TotalSyncNumDataPackets;
+};
+
+struct hci_phy_link_bss_info {
+ u16 bdCap; /* capability information */
+};
+
+struct packet_irp_hcicmd_data {
+ u16 OCF:10;
+ u16 OGF:6;
+ u8 Length;
+ u8 Data[20];
+};
+
+struct bt_asoc_entry {
+ u8 bUsed;
+ u8 mAssoc;
+ u8 b4waySuccess;
+ u8 Bssid[6];
+ struct hci_phy_link_cmd_data PhyLinkCmdData;
+
+ struct hci_log_link_cmd_data LogLinkCmdData[MAX_LOGICAL_LINK_NUM];
+
+ struct hci_acl_packet_data ACLPacketsData;
+
+ struct amp_assoc_cmd_data AmpAsocCmdData;
+ struct octet_string BTSsid;
+ u8 BTSsidBuf[33];
+
+ enum hci_status PhyLinkDisconnectReason;
+
+ u8 bSendSupervisionPacket;
+ /* u8 CurrentSuervisionPacketSendNum; */
+ /* u8 LastSuervisionPacketSendNum; */
+ u32 NoRxPktCnt;
+ /* Is Creator or Joiner */
+ enum amp_btap_type AMPRole;
+
+ /* BT current state */
+ u8 BtCurrentState;
+ /* BT next state */
+ u8 BtNextState;
+
+ u8 bNeedPhysLinkCompleteEvent;
+
+ enum hci_status PhysLinkCompleteStatus;
+
+ u8 BTRemoteMACAddr[6];
+
+ u32 BTCapability;
+
+ u8 SyncDataPacketLen;
+
+ u16 TotalSyncNumDataPackets;
+ u16 TotalNumACLDataPackets;
+
+ u8 ShortRangeMode;
+
+ u8 PTK[PTK_LEN_TKIP];
+ u8 GTK[GTK_LEN];
+ u8 ANonce[KEY_NONCE_LEN];
+ u8 SNonce[KEY_NONCE_LEN];
+ u64 KeyReplayCounter;
+ u8 WPAAuthReplayCount;
+ u8 AESKeyBuf[AESCCMP_BLK_SIZE_TOTAL];
+ u8 PMK[PMK_LEN];
+ enum bt_state_wpa_auth BTWPAAuthState;
+ s32 UndecoratedSmoothedPWDB;
+
+ /* Add for HW security !! */
+ u8 HwCAMIndex; /* Cam index */
+ u8 bPeerQosSta;
+
+ u32 rxSuvpPktCnt;
+};
+
+struct bt_traffic_statistics {
+ u8 bTxBusyTraffic;
+ u8 bRxBusyTraffic;
+ u8 bIdle;
+ u32 TxPktCntInPeriod;
+ u32 RxPktCntInPeriod;
+ u64 TxPktLenInPeriod;
+ u64 RxPktLenInPeriod;
+};
+
+struct bt_mgnt {
+ u8 bBTConnectInProgress;
+ u8 bLogLinkInProgress;
+ u8 bPhyLinkInProgress;
+ u8 bPhyLinkInProgressStartLL;
+ u8 BtCurrentPhyLinkhandle;
+ u16 BtCurrentLogLinkhandle;
+ u8 CurrentConnectEntryNum;
+ u8 DisconnectEntryNum;
+ u8 CurrentBTConnectionCnt;
+ enum bt_connect_type BTCurrentConnectType;
+ enum bt_connect_type BTReceiveConnectPkt;
+ u8 BTAuthCount;
+ u8 BTAsocCount;
+ u8 bStartSendSupervisionPkt;
+ u8 BtOperationOn;
+ u8 BTNeedAMPStatusChg;
+ u8 JoinerNeedSendAuth;
+ struct hci_phy_link_bss_info bssDesc;
+ struct hci_ext_config ExtConfig;
+ u8 bNeedNotifyAMPNoCap;
+ u8 bCreateSpportQos;
+ u8 bSupportProfile;
+ u8 BTChannel;
+ u8 CheckChnlIsSuit;
+ u8 bBtScan;
+ u8 btLogoTest;
+};
+
+struct bt_hci_dgb_info {
+ u32 hciCmdCnt;
+ u32 hciCmdCntUnknown;
+ u32 hciCmdCntCreatePhyLink;
+ u32 hciCmdCntAcceptPhyLink;
+ u32 hciCmdCntDisconnectPhyLink;
+ u32 hciCmdPhyLinkStatus;
+ u32 hciCmdCntCreateLogLink;
+ u32 hciCmdCntAcceptLogLink;
+ u32 hciCmdCntDisconnectLogLink;
+ u32 hciCmdCntReadLocalAmpAssoc;
+ u32 hciCmdCntWriteRemoteAmpAssoc;
+ u32 hciCmdCntSetAclLinkStatus;
+ u32 hciCmdCntSetScoLinkStatus;
+ u32 hciCmdCntExtensionVersionNotify;
+ u32 hciCmdCntLinkStatusNotify;
+};
+
+struct bt_irp_dgb_info {
+ u32 irpMJCreate;
+ /* Io Control */
+ u32 irpIoControl;
+ u32 irpIoCtrlHciCmd;
+ u32 irpIoCtrlHciEvent;
+ u32 irpIoCtrlHciTxData;
+ u32 irpIoCtrlHciRxData;
+ u32 irpIoCtrlUnknown;
+
+ u32 irpIoCtrlHciTxData1s;
+};
+
+struct bt_packet_dgb_info {
+ u32 btPktTxProbReq;
+ u32 btPktRxProbReq;
+ u32 btPktRxProbReqFail;
+ u32 btPktTxProbRsp;
+ u32 btPktRxProbRsp;
+ u32 btPktTxAuth;
+ u32 btPktRxAuth;
+ u32 btPktRxAuthButDrop;
+ u32 btPktTxAssocReq;
+ u32 btPktRxAssocReq;
+ u32 btPktRxAssocReqButDrop;
+ u32 btPktTxAssocRsp;
+ u32 btPktRxAssocRsp;
+ u32 btPktTxDisassoc;
+ u32 btPktRxDisassoc;
+ u32 btPktRxDeauth;
+ u32 btPktTx4way1st;
+ u32 btPktRx4way1st;
+ u32 btPktTx4way2nd;
+ u32 btPktRx4way2nd;
+ u32 btPktTx4way3rd;
+ u32 btPktRx4way3rd;
+ u32 btPktTx4way4th;
+ u32 btPktRx4way4th;
+ u32 btPktTxLinkSuperReq;
+ u32 btPktRxLinkSuperReq;
+ u32 btPktTxLinkSuperRsp;
+ u32 btPktRxLinkSuperRsp;
+ u32 btPktTxData;
+ u32 btPktRxData;
+};
+
+struct bt_dgb {
+ u8 dbgCtrl;
+ u32 dbgProfile;
+ struct bt_hci_dgb_info dbgHciInfo;
+ struct bt_irp_dgb_info dbgIrpInfo;
+ struct bt_packet_dgb_info dbgBtPkt;
+};
+
+struct bt_hci_info {
+ /* 802.11 Pal version specifier */
+ u8 BTPalVersion;
+ u16 BTPalCompanyID;
+ u16 BTPalsubversion;
+
+ /* Connected channel list */
+ u16 BTConnectChnlListLen;
+ u8 BTConnectChnllist[64];
+
+ /* Fail contact counter */
+ u16 FailContactCount;
+
+ /* Event mask */
+ u64 BTEventMask;
+ u64 BTEventMaskPage2;
+
+ /* timeout var */
+ u16 ConnAcceptTimeout;
+ u16 LogicalAcceptTimeout;
+ u16 PageTimeout;
+
+ u8 LocationDomainAware;
+ u16 LocationDomain;
+ u8 LocationDomainOptions;
+ u8 LocationOptions;
+
+ u8 FlowControlMode;
+
+ /* Preferred channel list */
+ u16 BtPreChnlListLen;
+ u8 BTPreChnllist[64];
+
+ u16 enFlush_LLH; /* enhanced flush handle */
+ u16 FLTO_LLH; /* enhanced flush handle */
+
+ /* */
+ /* Test command only. */
+ u8 bInTestMode;
+ u8 bTestIsEnd;
+ u8 bTestNeedReport;
+ u8 TestScenario;
+ u8 TestReportInterval;
+ u8 TestCtrType;
+ u32 TestEventType;
+ u16 TestNumOfFrame;
+ u16 TestNumOfErrFrame;
+ u16 TestNumOfBits;
+ u16 TestNumOfErrBits;
+ /* */
+};
+
+struct bt_traffic {
+ /* Add for check replay data */
+ u8 LastRxUniFragNum;
+ u16 LastRxUniSeqNum;
+
+ /* s32 EntryMaxUndecoratedSmoothedPWDB; */
+ /* s32 EntryMinUndecoratedSmoothedPWDB; */
+
+ struct bt_traffic_statistics Bt30TrafficStatistics;
+};
+
+#define RT_WORK_ITEM struct work_struct
+
+struct bt_security {
+ /* WPA auth state
+ * May need to remove to BTSecInfo ...
+ * enum bt_state_wpa_auth BTWPAAuthState;
+ */
+ struct octet_string RSNIE;
+ u8 RSNIEBuf[MAXRSNIELEN];
+ u8 bRegNoEncrypt;
+ u8 bUsedHwEncrypt;
+};
+
+struct bt_30info {
+ struct rtw_adapter *padapter;
+ struct bt_asoc_entry BtAsocEntry[MAX_BT_ASOC_ENTRY_NUM];
+ struct bt_mgnt BtMgnt;
+ struct bt_dgb BtDbg;
+ struct bt_hci_info BtHciInfo;
+ struct bt_traffic BtTraffic;
+ struct bt_security BtSec;
+ RT_WORK_ITEM HCICmdWorkItem;
+ struct timer_list BTHCICmdTimer;
+ RT_WORK_ITEM BTPsDisableWorkItem;
+ RT_WORK_ITEM BTConnectWorkItem;
+ struct timer_list BTHCIDiscardAclDataTimer;
+ struct timer_list BTHCIJoinTimeoutTimer;
+ struct timer_list BTTestSendPacketTimer;
+ struct timer_list BTDisconnectPhyLinkTimer;
+ struct timer_list BTBeaconTimer;
+ u8 BTBeaconTmrOn;
+
+ struct timer_list BTPsDisableTimer;
+
+ void * pBtChnlList;
+};
+
+struct packet_irp_acl_data {
+ u16 Handle:12;
+ u16 PB_Flag:2;
+ u16 BC_Flag:2;
+ u16 Length;
+ u8 Data[1];
+};
+
+struct packet_irp_hcievent_data {
+ u8 EventCode;
+ u8 Length;
+ u8 Data[20];
+};
+
+struct common_triple {
+ u8 byte_1st;
+ u8 byte_2nd;
+ u8 byte_3rd;
+};
+
+#define COUNTRY_STR_LEN 3 /* country string len = 3 */
+
+#define LOCAL_PMK 0
+
+enum hci_wifi_connect_status {
+ HCI_WIFI_NOT_CONNECTED = 0x0,
+ HCI_WIFI_CONNECTED = 0x1,
+ HCI_WIFI_CONNECT_IN_PROGRESS = 0x2,
+};
+
+enum hci_ext_bp_operation {
+ HCI_BT_OP_NONE = 0x0,
+ HCI_BT_OP_INQUIRY_START = 0x1,
+ HCI_BT_OP_INQUIRY_FINISH = 0x2,
+ HCI_BT_OP_PAGING_START = 0x3,
+ HCI_BT_OP_PAGING_SUCCESS = 0x4,
+ HCI_BT_OP_PAGING_UNSUCCESS = 0x5,
+ HCI_BT_OP_PAIRING_START = 0x6,
+ HCI_BT_OP_PAIRING_FINISH = 0x7,
+ HCI_BT_OP_BT_DEV_ENABLE = 0x8,
+ HCI_BT_OP_BT_DEV_DISABLE = 0x9,
+ HCI_BT_OP_MAX
+};
+
+#define BTHCI_SM_WITH_INFO(_Adapter, _StateToEnter, _StateCmd, _EntryNum) \
+{ \
+ RTPRINT(FIOCTL, IOCTL_STATE, ("[BT state change] caused by ""%s"", line =%d\n", __func__, __LINE__)); \
+ BTHCI_StateMachine(_Adapter, _StateToEnter, _StateCmd, _EntryNum);\
+}
+
+void BTHCI_EventParse(struct rtw_adapter *padapter, void *pEvntData,
+ u32 dataLen);
+#define BT_EventParse BTHCI_EventParse
+u8 BTHCI_HsConnectionEstablished(struct rtw_adapter *padapter);
+void BTHCI_UpdateBTProfileRTKToMoto(struct rtw_adapter *padapter);
+void BTHCI_WifiScanNotify(struct rtw_adapter *padapter, u8 scanType);
+void BTHCI_StateMachine(struct rtw_adapter *padapter, u8 StateToEnter,
+ enum hci_state_with_cmd StateCmd, u8 EntryNum);
+void BTHCI_DisconnectPeer(struct rtw_adapter *padapter, u8 EntryNum);
+void BTHCI_EventNumOfCompletedDataBlocks(struct rtw_adapter *padapter);
+void BTHCI_EventAMPStatusChange(struct rtw_adapter *padapter, u8 AMP_Status);
+void BTHCI_DisconnectAll(struct rtw_adapter *padapter);
+enum hci_status BTHCI_HandleHCICMD(struct rtw_adapter *padapter,
+ struct packet_irp_hcicmd_data *pHciCmd);
+
+/* ===== End of sync from SD7 driver COMMON/bt_hci.h ===== */
+
+/* ===== Below this line is sync from SD7 driver HAL/BTCoexist/HalBtc87231Ant.h ===== */
+#define GET_BT_INFO(padapter) (&GET_HAL_DATA(padapter)->BtInfo)
+
+#define BTC_FOR_SCAN_START 1
+#define BTC_FOR_SCAN_FINISH 0
+
+#define BT_TXRX_CNT_THRES_1 1200
+#define BT_TXRX_CNT_THRES_2 1400
+#define BT_TXRX_CNT_THRES_3 3000
+#define BT_TXRX_CNT_LEVEL_0 0 /* < 1200 */
+#define BT_TXRX_CNT_LEVEL_1 1 /* >= 1200 && < 1400 */
+#define BT_TXRX_CNT_LEVEL_2 2 /* >= 1400 */
+#define BT_TXRX_CNT_LEVEL_3 3 /* >= 3000 */
+
+enum bt_state_1ant {
+ BT_INFO_STATE_DISABLED = 0,
+ BT_INFO_STATE_NO_CONNECTION = 1,
+ BT_INFO_STATE_CONNECT_IDLE = 2,
+ BT_INFO_STATE_INQ_OR_PAG = 3,
+ BT_INFO_STATE_ACL_ONLY_BUSY = 4,
+ BT_INFO_STATE_SCO_ONLY_BUSY = 5,
+ BT_INFO_STATE_ACL_SCO_BUSY = 6,
+ BT_INFO_STATE_ACL_INQ_OR_PAG = 7,
+ BT_INFO_STATE_MAX = 8
+};
+
+struct btdm_8723a_1ant {
+ u8 prePsTdma;
+ u8 curPsTdma;
+ u8 psTdmaDuAdjType;
+ u8 bPrePsTdmaOn;
+ u8 bCurPsTdmaOn;
+ u8 preWifiPara;
+ u8 curWifiPara;
+ u8 preCoexWifiCon;
+ u8 curCoexWifiCon;
+ u8 wifiRssiThresh;
+
+ u32 psTdmaMonitorCnt;
+ u32 psTdmaGlobalCnt;
+
+ /* DurationAdjust For SCO */
+ u32 psTdmaMonitorCntForSCO;
+ u8 psTdmaDuAdjTypeForSCO;
+ u8 RSSI_WiFi_Last;
+ u8 RSSI_BT_Last;
+
+ u8 bWiFiHalt;
+ u8 bRAChanged;
+};
+
+void BTDM_1AntSignalCompensation(struct rtw_adapter *padapter,
+ u8 *rssi_wifi, u8 *rssi_bt);
+void BTDM_1AntForDhcp(struct rtw_adapter *padapter);
+void BTDM_1AntBtCoexist8723A(struct rtw_adapter *padapter);
+
+/* ===== End of sync from SD7 driver HAL/BTCoexist/HalBtc87231Ant.h ===== */
+
+/* ===== Below this line is sync from SD7 driver HAL/BTCoexist/HalBtc87232Ant.h ===== */
+enum bt_2ant_bt_status {
+ BT_2ANT_BT_STATUS_IDLE = 0x0,
+ BT_2ANT_BT_STATUS_CONNECTED_IDLE = 0x1,
+ BT_2ANT_BT_STATUS_NON_IDLE = 0x2,
+ BT_2ANT_BT_STATUS_MAX
+};
+
+enum bt_2ant_coex_algo {
+ BT_2ANT_COEX_ALGO_UNDEFINED = 0x0,
+ BT_2ANT_COEX_ALGO_SCO = 0x1,
+ BT_2ANT_COEX_ALGO_HID = 0x2,
+ BT_2ANT_COEX_ALGO_A2DP = 0x3,
+ BT_2ANT_COEX_ALGO_PANEDR = 0x4,
+ BT_2ANT_COEX_ALGO_PANHS = 0x5,
+ BT_2ANT_COEX_ALGO_PANEDR_A2DP = 0x6,
+ BT_2ANT_COEX_ALGO_PANEDR_HID = 0x7,
+ BT_2ANT_COEX_ALGO_HID_A2DP_PANEDR = 0x8,
+ BT_2ANT_COEX_ALGO_HID_A2DP = 0x9,
+ BT_2ANT_COEX_ALGO_HID_A2DP_PANHS = 0xA,
+ BT_2ANT_COEX_ALGO_MAX = 0xB,
+};
+
+struct btdm_8723a_2ant {
+ u8 bPreDecBtPwr;
+ u8 bCurDecBtPwr;
+
+ u8 preWlanActHi;
+ u8 curWlanActHi;
+ u8 preWlanActLo;
+ u8 curWlanActLo;
+
+ u8 preFwDacSwingLvl;
+ u8 curFwDacSwingLvl;
+
+ u8 bPreRfRxLpfShrink;
+ u8 bCurRfRxLpfShrink;
+
+ u8 bPreLowPenaltyRa;
+ u8 bCurLowPenaltyRa;
+
+ u8 preBtRetryIndex;
+ u8 curBtRetryIndex;
+
+ u8 bPreDacSwingOn;
+ u32 preDacSwingLvl;
+ u8 bCurDacSwingOn;
+ u32 curDacSwingLvl;
+
+ u8 bPreAdcBackOff;
+ u8 bCurAdcBackOff;
+
+ u8 bPreAgcTableEn;
+ u8 bCurAgcTableEn;
+
+ u32 preVal0x6c0;
+ u32 curVal0x6c0;
+ u32 preVal0x6c8;
+ u32 curVal0x6c8;
+ u8 preVal0x6cc;
+ u8 curVal0x6cc;
+
+ u8 bCurIgnoreWlanAct;
+ u8 bPreIgnoreWlanAct;
+
+ u8 prePsTdma;
+ u8 curPsTdma;
+ u8 psTdmaDuAdjType;
+ u8 bPrePsTdmaOn;
+ u8 bCurPsTdmaOn;
+
+ u8 preAlgorithm;
+ u8 curAlgorithm;
+ u8 bResetTdmaAdjust;
+
+ u8 btStatus;
+};
+
+void BTDM_2AntBtCoexist8723A(struct rtw_adapter *padapter);
+/* ===== End of sync from SD7 driver HAL/BTCoexist/HalBtc87232Ant.h ===== */
+
+/* ===== Below this line is sync from SD7 driver HAL/BTCoexist/HalBtc8723.h ===== */
+
+#define BT_Q_PKT_OFF 0
+#define BT_Q_PKT_ON 1
+
+#define BT_TX_PWR_OFF 0
+#define BT_TX_PWR_ON 1
+
+/* TDMA mode definition */
+#define TDMA_2ANT 0
+#define TDMA_1ANT 1
+#define TDMA_NAV_OFF 0
+#define TDMA_NAV_ON 1
+#define TDMA_DAC_SWING_OFF 0
+#define TDMA_DAC_SWING_ON 1
+
+#define BT_RSSI_LEVEL_H 0
+#define BT_RSSI_LEVEL_M 1
+#define BT_RSSI_LEVEL_L 2
+
+/* PTA mode related definition */
+#define BT_PTA_MODE_OFF 0
+#define BT_PTA_MODE_ON 1
+
+/* Penalty Tx Rate Adaptive */
+#define BT_TX_RATE_ADAPTIVE_NORMAL 0
+#define BT_TX_RATE_ADAPTIVE_LOW_PENALTY 1
+
+/* RF Corner */
+#define BT_RF_RX_LPF_CORNER_RESUME 0
+#define BT_RF_RX_LPF_CORNER_SHRINK 1
+
+#define BT_INFO_ACL BIT(0)
+#define BT_INFO_SCO BIT(1)
+#define BT_INFO_INQ_PAG BIT(2)
+#define BT_INFO_ACL_BUSY BIT(3)
+#define BT_INFO_SCO_BUSY BIT(4)
+#define BT_INFO_HID BIT(5)
+#define BT_INFO_A2DP BIT(6)
+#define BT_INFO_FTP BIT(7)
+
+
+
+struct bt_coexist_8723a {
+ u32 highPriorityTx;
+ u32 highPriorityRx;
+ u32 lowPriorityTx;
+ u32 lowPriorityRx;
+ u8 btRssi;
+ u8 TotalAntNum;
+ u8 bC2hBtInfoSupport;
+ u8 c2hBtInfo;
+ u8 c2hBtInfoOriginal;
+ u8 prec2hBtInfo; /* for 1Ant */
+ u8 bC2hBtInquiryPage;
+ unsigned long btInqPageStartTime; /* for 2Ant */
+ u8 c2hBtProfile; /* for 1Ant */
+ u8 btRetryCnt;
+ u8 btInfoExt;
+ u8 bC2hBtInfoReqSent;
+ u8 bForceFwBtInfo;
+ u8 bForceA2dpSink;
+ struct btdm_8723a_2ant btdm2Ant;
+ struct btdm_8723a_1ant btdm1Ant;
+};
+
+void BTDM_SetFwChnlInfo(struct rtw_adapter *padapter,
+ enum rt_media_status mstatus);
+u8 BTDM_IsWifiConnectionExist(struct rtw_adapter *padapter);
+void BTDM_SetFw3a(struct rtw_adapter *padapter, u8 byte1, u8 byte2, u8 byte3,
+ u8 byte4, u8 byte5);
+void BTDM_QueryBtInformation(struct rtw_adapter *padapter);
+void BTDM_SetSwRfRxLpfCorner(struct rtw_adapter *padapter, u8 type);
+void BTDM_SetSwPenaltyTxRateAdaptive(struct rtw_adapter *padapter, u8 raType);
+void BTDM_SetFwDecBtPwr(struct rtw_adapter *padapter, u8 bDecBtPwr);
+u8 BTDM_BtProfileSupport(struct rtw_adapter *padapter);
+void BTDM_LpsLeave(struct rtw_adapter *padapter);
+
+/* ===== End of sync from SD7 driver HAL/BTCoexist/HalBtc8723.h ===== */
+
+/* ===== Below this line is sync from SD7 driver HAL/BTCoexist/HalBtcCsr1Ant.h ===== */
+
+enum BT_A2DP_INDEX{
+ BT_A2DP_INDEX0 = 0, /* 32, 12; the most critical for BT */
+ BT_A2DP_INDEX1, /* 12, 24 */
+ BT_A2DP_INDEX2, /* 0, 0 */
+ BT_A2DP_INDEX_MAX
+};
+
+#define BT_A2DP_STATE_NOT_ENTERED 0
+#define BT_A2DP_STATE_DETECTING 1
+#define BT_A2DP_STATE_DETECTED 2
+
+#define BTDM_ANT_BT_IDLE 0
+#define BTDM_ANT_WIFI 1
+#define BTDM_ANT_BT 2
+
+
+void BTDM_SingleAnt(struct rtw_adapter *padapter, u8 bSingleAntOn,
+ u8 bInterruptOn, u8 bMultiNAVOn);
+void BTDM_CheckBTIdleChange1Ant(struct rtw_adapter *padapter);
+
+/* ===== End of sync from SD7 driver HAL/BTCoexist/HalBtcCsr1Ant.h ===== */
+
+/* ===== Below this line is sync from SD7 driver HAL/BTCoexist/HalBtcCsr2Ant.h ===== */
+
+/* */
+/* For old core stack before v251 */
+/* */
+#define BT_RSSI_STATE_NORMAL_POWER BIT(0)
+#define BT_RSSI_STATE_AMDPU_OFF BIT(1)
+#define BT_RSSI_STATE_SPECIAL_LOW BIT(2)
+#define BT_RSSI_STATE_BG_EDCA_LOW BIT(3)
+#define BT_RSSI_STATE_TXPOWER_LOW BIT(4)
+
+#define BT_DACSWING_OFF 0
+#define BT_DACSWING_M4 1
+#define BT_DACSWING_M7 2
+#define BT_DACSWING_M10 3
+
+void BTDM_DiminishWiFi(struct rtw_adapter *Adapter, u8 bDACOn, u8 bInterruptOn,
+ u8 DACSwingLevel, u8 bNAVOn);
+
+/* ===== End of sync from SD7 driver HAL/BTCoexist/HalBtcCsr2Ant.h ===== */
+
+/* HEADER/TypeDef.h */
+#define MAX_FW_SUPPORT_MACID_NUM 64
+
+/* ===== Below this line is sync from SD7 driver HAL/BTCoexist/HalBtCoexist.h ===== */
+
+#define FW_VER_BT_REG 62
+#define FW_VER_BT_REG1 74
+#define REG_BT_ACTIVE 0x444
+#define REG_BT_STATE 0x448
+#define REG_BT_POLLING1 0x44c
+#define REG_BT_POLLING 0x700
+
+#define REG_BT_ACTIVE_OLD 0x488
+#define REG_BT_STATE_OLD 0x48c
+#define REG_BT_POLLING_OLD 0x490
+
+/* The reg define is for 8723 */
+#define REG_HIGH_PRIORITY_TXRX 0x770
+#define REG_LOW_PRIORITY_TXRX 0x774
+
+#define BT_FW_COEX_THRESH_TOL 6
+#define BT_FW_COEX_THRESH_20 20
+#define BT_FW_COEX_THRESH_23 23
+#define BT_FW_COEX_THRESH_25 25
+#define BT_FW_COEX_THRESH_30 30
+#define BT_FW_COEX_THRESH_35 35
+#define BT_FW_COEX_THRESH_40 40
+#define BT_FW_COEX_THRESH_45 45
+#define BT_FW_COEX_THRESH_47 47
+#define BT_FW_COEX_THRESH_50 50
+#define BT_FW_COEX_THRESH_55 55
+#define BT_FW_COEX_THRESH_65 65
+
+#define BT_COEX_STATE_BT30 BIT(0)
+#define BT_COEX_STATE_WIFI_HT20 BIT(1)
+#define BT_COEX_STATE_WIFI_HT40 BIT(2)
+#define BT_COEX_STATE_WIFI_LEGACY BIT(3)
+
+#define BT_COEX_STATE_WIFI_RSSI_LOW BIT(4)
+#define BT_COEX_STATE_WIFI_RSSI_MEDIUM BIT(5)
+#define BT_COEX_STATE_WIFI_RSSI_HIGH BIT(6)
+#define BT_COEX_STATE_DEC_BT_POWER BIT(7)
+
+#define BT_COEX_STATE_WIFI_IDLE BIT(8)
+#define BT_COEX_STATE_WIFI_UPLINK BIT(9)
+#define BT_COEX_STATE_WIFI_DOWNLINK BIT(10)
+
+#define BT_COEX_STATE_BT_INQ_PAGE BIT(11)
+#define BT_COEX_STATE_BT_IDLE BIT(12)
+#define BT_COEX_STATE_BT_UPLINK BIT(13)
+#define BT_COEX_STATE_BT_DOWNLINK BIT(14)
+/* */
+/* Todo: Remove these definitions */
+#define BT_COEX_STATE_BT_PAN_IDLE BIT(15)
+#define BT_COEX_STATE_BT_PAN_UPLINK BIT(16)
+#define BT_COEX_STATE_BT_PAN_DOWNLINK BIT(17)
+#define BT_COEX_STATE_BT_A2DP_IDLE BIT(18)
+/* */
+#define BT_COEX_STATE_BT_RSSI_LOW BIT(19)
+
+#define BT_COEX_STATE_PROFILE_HID BIT(20)
+#define BT_COEX_STATE_PROFILE_A2DP BIT(21)
+#define BT_COEX_STATE_PROFILE_PAN BIT(22)
+#define BT_COEX_STATE_PROFILE_SCO BIT(23)
+
+#define BT_COEX_STATE_WIFI_RSSI_1_LOW BIT(24)
+#define BT_COEX_STATE_WIFI_RSSI_1_MEDIUM BIT(25)
+#define BT_COEX_STATE_WIFI_RSSI_1_HIGH BIT(26)
+
+#define BT_COEX_STATE_WIFI_RSSI_BEACON_LOW BIT(27)
+#define BT_COEX_STATE_WIFI_RSSI_BEACON_MEDIUM BIT(28)
+#define BT_COEX_STATE_WIFI_RSSI_BEACON_HIGH BIT(29)
+
+
+#define BT_COEX_STATE_BTINFO_COMMON BIT(30)
+#define BT_COEX_STATE_BTINFO_B_HID_SCOESCO BIT(31)
+#define BT_COEX_STATE_BTINFO_B_FTP_A2DP BIT(32)
+
+#define BT_COEX_STATE_BT_CNT_LEVEL_0 BIT(33)
+#define BT_COEX_STATE_BT_CNT_LEVEL_1 BIT(34)
+#define BT_COEX_STATE_BT_CNT_LEVEL_2 BIT(35)
+#define BT_COEX_STATE_BT_CNT_LEVEL_3 BIT(36)
+
+#define BT_RSSI_STATE_HIGH 0
+#define BT_RSSI_STATE_MEDIUM 1
+#define BT_RSSI_STATE_LOW 2
+#define BT_RSSI_STATE_STAY_HIGH 3
+#define BT_RSSI_STATE_STAY_MEDIUM 4
+#define BT_RSSI_STATE_STAY_LOW 5
+
+#define BT_AGCTABLE_OFF 0
+#define BT_AGCTABLE_ON 1
+
+#define BT_BB_BACKOFF_OFF 0
+#define BT_BB_BACKOFF_ON 1
+
+#define BT_FW_NAV_OFF 0
+#define BT_FW_NAV_ON 1
+
+#define BT_COEX_MECH_NONE 0
+#define BT_COEX_MECH_SCO 1
+#define BT_COEX_MECH_HID 2
+#define BT_COEX_MECH_A2DP 3
+#define BT_COEX_MECH_PAN 4
+#define BT_COEX_MECH_HID_A2DP 5
+#define BT_COEX_MECH_HID_PAN 6
+#define BT_COEX_MECH_PAN_A2DP 7
+#define BT_COEX_MECH_HID_SCO_ESCO 8
+#define BT_COEX_MECH_FTP_A2DP 9
+#define BT_COEX_MECH_COMMON 10
+#define BT_COEX_MECH_MAX 11
+/* BT Dbg Ctrl */
+#define BT_DBG_PROFILE_NONE 0
+#define BT_DBG_PROFILE_SCO 1
+#define BT_DBG_PROFILE_HID 2
+#define BT_DBG_PROFILE_A2DP 3
+#define BT_DBG_PROFILE_PAN 4
+#define BT_DBG_PROFILE_HID_A2DP 5
+#define BT_DBG_PROFILE_HID_PAN 6
+#define BT_DBG_PROFILE_PAN_A2DP 7
+#define BT_DBG_PROFILE_MAX 9
+
+struct bt_coexist_str {
+ u8 BluetoothCoexist;
+ u8 BT_Ant_Num;
+ u8 BT_CoexistType;
+ u8 BT_Ant_isolation; /* 0:good, 1:bad */
+ u8 bt_radiosharedtype;
+ u32 Ratio_Tx;
+ u32 Ratio_PRI;
+ u8 bInitlized;
+ u32 BtRfRegOrigin1E;
+ u32 BtRfRegOrigin1F;
+ u8 bBTBusyTraffic;
+ u8 bBTTrafficModeSet;
+ u8 bBTNonTrafficModeSet;
+ struct bt_traffic_statistics BT21TrafficStatistics;
+ u64 CurrentState;
+ u64 PreviousState;
+ u8 preRssiState;
+ u8 preRssiState1;
+ u8 preRssiStateBeacon;
+ u8 bFWCoexistAllOff;
+ u8 bSWCoexistAllOff;
+ u8 bHWCoexistAllOff;
+ u8 bBalanceOn;
+ u8 bSingleAntOn;
+ u8 bInterruptOn;
+ u8 bMultiNAVOn;
+ u8 PreWLANActH;
+ u8 PreWLANActL;
+ u8 WLANActH;
+ u8 WLANActL;
+ u8 A2DPState;
+ u8 AntennaState;
+ u32 lastBtEdca;
+ u16 last_aggr_num;
+ u8 bEDCAInitialized;
+ u8 exec_cnt;
+ u8 b8723aAgcTableOn;
+ u8 b92DAgcTableOn;
+ struct bt_coexist_8723a halCoex8723;
+ u8 btActiveZeroCnt;
+ u8 bCurBtDisabled;
+ u8 bPreBtDisabled;
+ u8 bNeedToRoamForBtDisableEnable;
+ u8 fw3aVal[5];
+};
+
+void BTDM_CheckAntSelMode(struct rtw_adapter *padapter);
+void BTDM_FwC2hBtRssi(struct rtw_adapter *padapter, u8 *tmpBuf);
+#define BT_FwC2hBtRssi BTDM_FwC2hBtRssi
+void BTDM_DisplayBtCoexInfo(struct rtw_adapter *padapter);
+#define BT_DisplayBtCoexInfo BTDM_DisplayBtCoexInfo
+void BTDM_RejectAPAggregatedPacket(struct rtw_adapter *padapter, u8 bReject);
+u8 BTDM_IsHT40(struct rtw_adapter *padapter);
+u8 BTDM_Legacy(struct rtw_adapter *padapter);
+void BTDM_CheckWiFiState(struct rtw_adapter *padapter);
+s32 BTDM_GetRxSS(struct rtw_adapter *padapter);
+u8 BTDM_CheckCoexBcnRssiState(struct rtw_adapter *padapter, u8 levelNum,
+ u8 RssiThresh, u8 RssiThresh1);
+u8 BTDM_CheckCoexRSSIState1(struct rtw_adapter *padapter, u8 levelNum,
+ u8 RssiThresh, u8 RssiThresh1);
+u8 BTDM_CheckCoexRSSIState(struct rtw_adapter *padapter, u8 levelNum,
+ u8 RssiThresh, u8 RssiThresh1);
+void BTDM_Balance(struct rtw_adapter *padapter, u8 bBalanceOn, u8 ms0, u8 ms1);
+void BTDM_AGCTable(struct rtw_adapter *padapter, u8 type);
+void BTDM_BBBackOffLevel(struct rtw_adapter *padapter, u8 type);
+void BTDM_FWCoexAllOff(struct rtw_adapter *padapter);
+void BTDM_SWCoexAllOff(struct rtw_adapter *padapter);
+void BTDM_HWCoexAllOff(struct rtw_adapter *padapter);
+void BTDM_CoexAllOff(struct rtw_adapter *padapter);
+void BTDM_TurnOffBtCoexistBeforeEnterIPS(struct rtw_adapter *padapter);
+void BTDM_SignalCompensation(struct rtw_adapter *padapter, u8 *rssi_wifi,
+ u8 *rssi_bt);
+void BTDM_UpdateCoexState(struct rtw_adapter *padapter);
+u8 BTDM_IsSameCoexistState(struct rtw_adapter *padapter);
+void BTDM_PWDBMonitor(struct rtw_adapter *padapter);
+u8 BTDM_IsBTBusy(struct rtw_adapter *padapter);
+#define BT_IsBtBusy BTDM_IsBTBusy
+u8 BTDM_IsWifiBusy(struct rtw_adapter *padapter);
+u8 BTDM_IsCoexistStateChanged(struct rtw_adapter *padapter);
+u8 BTDM_IsWifiUplink(struct rtw_adapter *padapter);
+u8 BTDM_IsWifiDownlink(struct rtw_adapter *padapter);
+u8 BTDM_IsBTHSMode(struct rtw_adapter *padapter);
+u8 BTDM_IsBTUplink(struct rtw_adapter *padapter);
+u8 BTDM_IsBTDownlink(struct rtw_adapter *padapter);
+void BTDM_AdjustForBtOperation(struct rtw_adapter *padapter);
+void BTDM_ForHalt(struct rtw_adapter *padapter);
+void BTDM_WifiScanNotify(struct rtw_adapter *padapter, u8 scanType);
+void BTDM_WifiAssociateNotify(struct rtw_adapter *padapter, u8 action);
+void BTDM_MediaStatusNotify(struct rtw_adapter *padapter,
+ enum rt_media_status mstatus);
+void BTDM_ForDhcp(struct rtw_adapter *padapter);
+void BTDM_ResetActionProfileState(struct rtw_adapter *padapter);
+void BTDM_SetBtCoexCurrAntNum(struct rtw_adapter *padapter, u8 antNum);
+#define BT_SetBtCoexCurrAntNum BTDM_SetBtCoexCurrAntNum
+u8 BTDM_IsActionSCO(struct rtw_adapter *padapter);
+u8 BTDM_IsActionHID(struct rtw_adapter *padapter);
+u8 BTDM_IsActionA2DP(struct rtw_adapter *padapter);
+u8 BTDM_IsActionPAN(struct rtw_adapter *padapter);
+u8 BTDM_IsActionHIDA2DP(struct rtw_adapter *padapter);
+u8 BTDM_IsActionHIDPAN(struct rtw_adapter *padapter);
+u8 BTDM_IsActionPANA2DP(struct rtw_adapter *padapter);
+u32 BTDM_BtTxRxCounterH(struct rtw_adapter *padapter);
+u32 BTDM_BtTxRxCounterL(struct rtw_adapter *padapter);
+
+/* ===== End of sync from SD7 driver HAL/BTCoexist/HalBtCoexist.h ===== */
+
+/* ===== Below this line is sync from SD7 driver HAL/HalBT.h ===== */
+
+#define RTS_CTS_NO_LEN_LIMIT 0
+
+u8 HALBT_GetPGAntNum(struct rtw_adapter *padapter);
+#define BT_GetPGAntNum HALBT_GetPGAntNum
+void HALBT_SetKey(struct rtw_adapter *padapter, u8 EntryNum);
+void HALBT_RemoveKey(struct rtw_adapter *padapter, u8 EntryNum);
+u8 HALBT_IsBTExist(struct rtw_adapter *padapter);
+#define BT_IsBtExist HALBT_IsBTExist
+u8 HALBT_BTChipType(struct rtw_adapter *padapter);
+void HALBT_SetRtsCtsNoLenLimit(struct rtw_adapter *padapter);
+
+/* ===== End of sync from SD7 driver HAL/HalBT.c ===== */
+
+#define _bt_dbg_off_ 0
+#define _bt_dbg_on_ 1
+
+extern u32 BTCoexDbgLevel;
+
+
+
+#endif /* __RTL8723A_BT_COEXIST_H__ */
diff --git a/drivers/staging/rtl8723au/include/rtl8723a_bt_intf.h b/drivers/staging/rtl8723au/include/rtl8723a_bt_intf.h
new file mode 100644
index 000000000..473355997
--- /dev/null
+++ b/drivers/staging/rtl8723au/include/rtl8723a_bt_intf.h
@@ -0,0 +1,69 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2012 Realtek Corporation. All rights reserved.
+ * Copyright(c) 2014, Jes Sorensen <Jes.Sorensen@redhat.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * 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.
+ *
+ ******************************************************************************/
+#ifndef __RTL8723A_BT_INTF_H__
+#define __RTL8723A_BT_INTF_H__
+
+#include <drv_types.h>
+
+#ifdef CONFIG_8723AU_BT_COEXIST
+enum rt_media_status;
+bool rtl8723a_BT_using_antenna_1(struct rtw_adapter *padapter);
+bool rtl8723a_BT_enabled(struct rtw_adapter *padapter);
+bool rtl8723a_BT_coexist(struct rtw_adapter *padapter);
+void rtl8723a_BT_do_coexist(struct rtw_adapter *padapter);
+void rtl8723a_BT_wifiscan_notify(struct rtw_adapter *padapter, u8 scanType);
+void rtl8723a_BT_mediastatus_notify(struct rtw_adapter *padapter,
+ enum rt_media_status mstatus);
+void rtl8723a_BT_specialpacket_notify(struct rtw_adapter *padapter);
+void rtl8723a_BT_lps_leave(struct rtw_adapter *padapter);
+void rtl8723a_BT_disable_coexist(struct rtw_adapter *padapter);
+bool rtl8723a_BT_disable_EDCA_turbo(struct rtw_adapter *padapter);
+void rtl8723a_dual_antenna_detection(struct rtw_adapter *padapter);
+void rtl8723a_BT_init_hwconfig(struct rtw_adapter *padapter);
+void rtl8723a_BT_wifiassociate_notify(struct rtw_adapter *padapter, u8 action);
+void rtl8723a_BT_init_hal_vars(struct rtw_adapter *padapter);
+void rtl8723a_fw_c2h_BT_info(struct rtw_adapter *padapter, u8 *tmpBuf, u8 length);
+#else
+static inline bool rtl8723a_BT_using_antenna_1(struct rtw_adapter *padapter)
+{
+ return false;
+}
+static inline bool rtl8723a_BT_enabled(struct rtw_adapter *padapter)
+{
+ return false;
+}
+static inline bool rtl8723a_BT_coexist(struct rtw_adapter *padapter)
+{
+ return false;
+}
+#define rtl8723a_BT_do_coexist(padapter) do {} while(0)
+#define rtl8723a_BT_wifiscan_notify(padapter, scanType) do {} while(0)
+#define rtl8723a_BT_mediastatus_notify(padapter, mstatus) do {} while(0)
+#define rtl8723a_BT_specialpacket_notify(padapter) do {} while(0)
+#define rtl8723a_BT_lps_leave(padapter) do {} while(0)
+#define rtl8723a_BT_disable_coexist(padapter) do {} while(0)
+static inline bool rtl8723a_BT_disable_EDCA_turbo(struct rtw_adapter *padapter)
+{
+ return false;
+}
+#define rtl8723a_dual_antenna_detection(padapter) do {} while(0)
+#define rtl8723a_BT_init_hwconfig(padapter) do {} while(0)
+#define rtl8723a_BT_wifiassociate_notify(padapter, action) do {} while(0)
+#define rtl8723a_BT_init_hal_vars(padapter) do {} while(0)
+#define rtl8723a_fw_c2h_BT_info(padapter, tmpBuf, length) do {} while(0)
+#endif
+
+#endif
diff --git a/drivers/staging/rtl8723au/include/rtl8723a_cmd.h b/drivers/staging/rtl8723au/include/rtl8723a_cmd.h
new file mode 100644
index 000000000..014c02edd
--- /dev/null
+++ b/drivers/staging/rtl8723au/include/rtl8723a_cmd.h
@@ -0,0 +1,158 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2012 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * 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.
+ *
+ ******************************************************************************/
+#ifndef __RTL8723A_CMD_H__
+#define __RTL8723A_CMD_H__
+
+
+#define H2C_BT_FW_PATCH_LEN 3
+#define H2C_BT_PWR_FORCE_LEN 3
+
+enum cmd_msg_element_id
+{
+ NONE_CMDMSG_EID,
+ AP_OFFLOAD_EID = 0,
+ SET_PWRMODE_EID = 1,
+ JOINBSS_RPT_EID = 2,
+ RSVD_PAGE_EID = 3,
+ RSSI_4_EID = 4,
+ RSSI_SETTING_EID = 5,
+ MACID_CONFIG_EID = 6,
+ MACID_PS_MODE_EID = 7,
+ P2P_PS_OFFLOAD_EID = 8,
+ SELECTIVE_SUSPEND_ROF_CMD = 9,
+ BT_QUEUE_PKT_EID = 17,
+ BT_ANT_TDMA_EID = 20,
+ BT_2ANT_HID_EID = 21,
+ P2P_PS_CTW_CMD_EID = 32,
+ FORCE_BT_TX_PWR_EID = 33,
+ SET_TDMA_WLAN_ACT_TIME_EID = 34,
+ SET_BT_TX_RETRY_INDEX_EID = 35,
+ HID_PROFILE_ENABLE_EID = 36,
+ BT_IGNORE_WLAN_ACT_EID = 37,
+ BT_PTA_MANAGER_UPDATE_ENABLE_EID = 38,
+ DAC_SWING_VALUE_EID = 41,
+ TRADITIONAL_TDMA_EN_EID = 51,
+ H2C_BT_FW_PATCH = 54,
+ B_TYPE_TDMA_EID = 58,
+ SCAN_EN_EID = 59,
+ LOWPWR_LPS_EID = 71,
+ H2C_RESET_TSF = 75,
+ MAX_CMDMSG_EID
+};
+
+struct cmd_msg_parm {
+ u8 eid; /* element id */
+ u8 sz; /* sz */
+ u8 buf[6];
+};
+
+struct setpwrmode_parm {
+ u8 Mode;
+ u8 SmartPS;
+ u8 AwakeInterval; /* unit: beacon interval */
+ u8 bAllQueueUAPSD;
+
+#define SETPM_LOWRXBCN BIT(0)
+#define SETPM_AUTOANTSWITCH BIT(1)
+#define SETPM_PSALLOWBTHIGHPRI BIT(2)
+ u8 BcnAntMode;
+} __packed;
+
+struct H2C_SS_RFOFF_PARAM{
+ u8 ROFOn; /* 1: on, 0:off */
+ u16 gpio_period; /* unit: 1024 us */
+}__attribute__ ((packed));
+
+
+struct joinbssrpt_parm {
+ u8 OpMode; /* enum rt_media_status */
+};
+
+struct rsvdpage_loc {
+ u8 LocProbeRsp;
+ u8 LocPsPoll;
+ u8 LocNullData;
+ u8 LocQosNull;
+ u8 LocBTQosNull;
+};
+
+struct P2P_PS_Offload_t {
+ u8 Offload_En:1;
+ u8 role:1; /* 1: Owner, 0: Client */
+ u8 CTWindow_En:1;
+ u8 NoA0_En:1;
+ u8 NoA1_En:1;
+ u8 AllStaSleep:1; /* Only valid in Owner */
+ u8 discovery:1;
+ u8 rsvd:1;
+};
+
+struct P2P_PS_CTWPeriod_t {
+ u8 CTWPeriod; /* TU */
+};
+
+#define B_TDMA_EN BIT(0)
+#define B_TDMA_FIXANTINBT BIT(1)
+#define B_TDMA_TXPSPOLL BIT(2)
+#define B_TDMA_VAL870 BIT(3)
+#define B_TDMA_AUTOWAKEUP BIT(4)
+#define B_TDMA_NOPS BIT(5)
+#define B_TDMA_WLANHIGHPRI BIT(6)
+
+struct b_type_tdma_parm {
+ u8 option;
+
+ u8 TBTTOnPeriod;
+ u8 MedPeriod;
+ u8 rsvd30;
+} __packed;
+
+struct scan_en_parm {
+ u8 En;
+} __packed;
+
+/* BT_PWR */
+#define SET_H2CCMD_BT_PWR_IDX(__pH2CCmd, __Value) SET_BITS_TO_LE_1BYTE_8BIT(__pH2CCmd, 0, 8, __Value)
+
+/* BT_FW_PATCH */
+#define SET_H2CCMD_BT_FW_PATCH_ENABLE(__pH2CCmd, __Value) SET_BITS_TO_LE_4BYTE(__pH2CCmd, 0, 8, __Value) /* SET_BITS_TO_LE_1BYTE(__pH2CCmd, 0, 8, __Value) */
+#define SET_H2CCMD_BT_FW_PATCH_SIZE(__pH2CCmd, __Value) SET_BITS_TO_LE_4BYTE(__pH2CCmd, 8, 16, __Value) /* SET_BITS_TO_LE_2BYTE((__pH2CCmd)+1, 0, 16, __Value) */
+
+struct lowpwr_lps_parm{
+ u8 bcn_count:4;
+ u8 tb_bcn_threshold:3;
+ u8 enable:1;
+ u8 bcn_interval;
+ u8 drop_threshold;
+ u8 max_early_period;
+ u8 max_bcn_timeout_period;
+} __packed;
+
+
+/* host message to firmware cmd */
+void rtl8723a_set_FwPwrMode_cmd(struct rtw_adapter *padapter, u8 Mode);
+void rtl8723a_set_FwJoinBssReport_cmd(struct rtw_adapter *padapter, u8 mstatus);
+#ifdef CONFIG_8723AU_BT_COEXIST
+void rtl8723a_set_BTCoex_AP_mode_FwRsvdPkt_cmd(struct rtw_adapter *padapter);
+#else
+#define rtl8723a_set_BTCoex_AP_mode_FwRsvdPkt_cmd(padapter) do {} while(0)
+#endif
+int rtl8723a_set_rssi_cmd(struct rtw_adapter *padapter, u8 *param);
+int rtl8723a_set_raid_cmd(struct rtw_adapter *padapter, u32 mask, u8 arg);
+void rtl8723a_add_rateatid(struct rtw_adapter *padapter, u32 bitmap, u8 arg, u8 rssi_level);
+
+int FillH2CCmd(struct rtw_adapter *padapter, u8 ElementID, u32 CmdLen, u8 *pCmdBuffer);
+
+#endif
diff --git a/drivers/staging/rtl8723au/include/rtl8723a_dm.h b/drivers/staging/rtl8723au/include/rtl8723a_dm.h
new file mode 100644
index 000000000..bf236e8e4
--- /dev/null
+++ b/drivers/staging/rtl8723au/include/rtl8723a_dm.h
@@ -0,0 +1,137 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2011 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * 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.
+ *
+ ******************************************************************************/
+#ifndef __RTL8723A_DM_H__
+#define __RTL8723A_DM_H__
+/* */
+/* Description: */
+/* */
+/* This file is for 8723A dynamic mechanism only */
+/* */
+/* */
+/* */
+#define DYNAMIC_FUNC_BT BIT(0)
+
+enum{
+ UP_LINK,
+ DOWN_LINK,
+};
+/* */
+/* structure and define */
+/* */
+
+/* duplicate code,will move to ODM ######### */
+#define IQK_MAC_REG_NUM 4
+#define IQK_ADDA_REG_NUM 16
+#define IQK_BB_REG_NUM 9
+#define HP_THERMAL_NUM 8
+/* duplicate code,will move to ODM ######### */
+struct dm_priv {
+ u32 InitODMFlag;
+
+ /* Upper and Lower Signal threshold for Rate Adaptive*/
+ int UndecoratedSmoothedPWDB;
+ int UndecoratedSmoothedCCK;
+ int EntryMinUndecoratedSmoothedPWDB;
+ int EntryMaxUndecoratedSmoothedPWDB;
+ int MinUndecoratedPWDBForDM;
+ int LastMinUndecoratedPWDBForDM;
+
+ s32 UndecoratedSmoothedBeacon;
+ #ifdef CONFIG_8723AU_BT_COEXIST
+ s32 BT_EntryMinUndecoratedSmoothedPWDB;
+ s32 BT_EntryMaxUndecoratedSmoothedPWDB;
+ #endif
+
+ /* for High Power */
+ u8 DynamicTxHighPowerLvl;/* Add by Jacken Tx Power Control for Near/Far Range 2008/03/06 */
+
+ /* for tx power tracking */
+ u8 bTXPowerTracking;
+ u8 TXPowercount;
+ u8 bTXPowerTrackingInit;
+ u8 TxPowerTrackControl; /* for mp mode, turn off txpwrtracking as default */
+ u8 TM_Trigger;
+
+ u8 ThermalMeter[2]; /* ThermalMeter, index 0 for RFIC0, and 1 for RFIC1 */
+ u8 ThermalValue;
+ u8 ThermalValue_LCK;
+ u8 ThermalValue_IQK;
+ u8 ThermalValue_DPK;
+
+ u8 bRfPiEnable;
+
+ /* for APK */
+ u32 APKoutput[2][2]; /* path A/B; output1_1a/output1_2a */
+ u8 bAPKdone;
+ u8 bAPKThermalMeterIgnore;
+ u8 bDPdone;
+ u8 bDPPathAOK;
+ u8 bDPPathBOK;
+
+ /* for IQK */
+ u32 RegC04;
+ u32 Reg874;
+ u32 RegC08;
+ u32 RegB68;
+ u32 RegB6C;
+ u32 Reg870;
+ u32 Reg860;
+ u32 Reg864;
+ u32 ADDA_backup[IQK_ADDA_REG_NUM];
+ u32 IQK_MAC_backup[IQK_MAC_REG_NUM];
+ u32 IQK_BB_backup_recover[9];
+ u32 IQK_BB_backup[IQK_BB_REG_NUM];
+ u8 PowerIndex_backup[6];
+
+ u8 bCCKinCH14;
+
+ u8 CCK_index;
+ u8 OFDM_index[2];
+
+ u8 bDoneTxpower;
+ u8 CCK_index_HP;
+ u8 OFDM_index_HP[2];
+ u8 ThermalValue_HP[HP_THERMAL_NUM];
+ u8 ThermalValue_HP_index;
+
+ /* for TxPwrTracking */
+ s32 RegE94;
+ s32 RegE9C;
+ s32 RegEB4;
+ s32 RegEBC;
+
+ u32 TXPowerTrackingCallbackCnt; /* cosa add for debug */
+
+ u32 prv_traffic_idx; /* edca turbo */
+
+ s32 OFDM_Pkt_Cnt;
+ u8 RSSI_Select;
+/* u8 DIG_Dynamic_MIN ; */
+/* duplicate code,will move to ODM ######### */
+ /* Add for Reading Initial Data Rate SEL Register 0x484 during watchdog. Using for fill tx desc. 2011.3.21 by Thomas */
+ u8 INIDATA_RATE[32];
+};
+
+
+/* */
+/* function prototype */
+/* */
+
+void rtl8723a_init_dm_priv(struct rtw_adapter *padapter);
+
+void rtl8723a_InitHalDm(struct rtw_adapter *padapter);
+void rtl8723a_HalDmWatchDog(struct rtw_adapter *padapter);
+
+#endif
diff --git a/drivers/staging/rtl8723au/include/rtl8723a_hal.h b/drivers/staging/rtl8723au/include/rtl8723a_hal.h
new file mode 100644
index 000000000..ad3a442bc
--- /dev/null
+++ b/drivers/staging/rtl8723au/include/rtl8723a_hal.h
@@ -0,0 +1,535 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2011 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * 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.
+ *
+ ******************************************************************************/
+#ifndef __RTL8723A_HAL_H__
+#define __RTL8723A_HAL_H__
+
+#include "rtl8723a_spec.h"
+#include "rtl8723a_pg.h"
+#include "Hal8723APhyReg.h"
+#include "Hal8723APhyCfg.h"
+#include "rtl8723a_rf.h"
+#include "rtl8723a_bt_intf.h"
+#ifdef CONFIG_8723AU_BT_COEXIST
+#include "rtl8723a_bt-coexist.h"
+#endif
+#include "rtl8723a_dm.h"
+#include "rtl8723a_recv.h"
+#include "rtl8723a_xmit.h"
+#include "rtl8723a_cmd.h"
+#include "rtl8723a_sreset.h"
+#include "rtw_efuse.h"
+#include "rtw_eeprom.h"
+
+#include "odm_precomp.h"
+#include "odm.h"
+
+
+/* 2TODO: We should define 8192S firmware related macro settings here!! */
+#define RTL819X_DEFAULT_RF_TYPE RF_1T2R
+#define RTL819X_TOTAL_RF_PATH 2
+
+/* */
+/* RTL8723S From header */
+/* */
+
+/* Fw Array */
+#define Rtl8723_FwImageArray Rtl8723UFwImgArray
+#define Rtl8723_FwUMCBCutImageArrayWithBT Rtl8723UFwUMCBCutImgArrayWithBT
+#define Rtl8723_FwUMCBCutImageArrayWithoutBT Rtl8723UFwUMCBCutImgArrayWithoutBT
+
+#define Rtl8723_ImgArrayLength Rtl8723UImgArrayLength
+#define Rtl8723_UMCBCutImgArrayWithBTLength Rtl8723UUMCBCutImgArrayWithBTLength
+#define Rtl8723_UMCBCutImgArrayWithoutBTLength Rtl8723UUMCBCutImgArrayWithoutBTLength
+
+#define Rtl8723_PHY_REG_Array_PG Rtl8723UPHY_REG_Array_PG
+#define Rtl8723_PHY_REG_Array_PGLength Rtl8723UPHY_REG_Array_PGLength
+
+#define Rtl8723_FwUMCBCutMPImageArray Rtl8723SFwUMCBCutMPImgAr
+#define Rtl8723_UMCBCutMPImgArrayLength Rtl8723SUMCBCutMPImgArrayLength
+
+#define DRVINFO_SZ 4 /* unit is 8bytes */
+#define PageNum_128(_Len) (u32)(((_Len)>>7) + ((_Len)&0x7F ? 1:0))
+
+#define FW_8723A_SIZE 0x8000
+#define FW_8723A_START_ADDRESS 0x1000
+#define FW_8723A_END_ADDRESS 0x1FFF /* 0x5FFF */
+
+#define MAX_PAGE_SIZE 4096 /* @ page : 4k bytes */
+
+#define IS_FW_HEADER_EXIST(_pFwHdr) ((le16_to_cpu(_pFwHdr->Signature)&0xFFF0) == 0x92C0 ||\
+ (le16_to_cpu(_pFwHdr->Signature)&0xFFF0) == 0x88C0 ||\
+ (le16_to_cpu(_pFwHdr->Signature)&0xFFF0) == 0x2300)
+
+/* */
+/* This structure must be cared byte-ordering */
+/* */
+/* Added by tynli. 2009.12.04. */
+struct rt_8723a_firmware_hdr {
+ /* 8-byte alinment required */
+
+ /* LONG WORD 0 ---- */
+ u16 Signature; /* 92C0: test chip; 92C, 88C0: test chip; 88C1: MP A-cut; 92C1: MP A-cut */
+ u8 Category; /* AP/NIC and USB/PCI */
+ u8 Function; /* Reserved for different FW function indcation, for further use when driver needs to download different FW in different conditions */
+ u16 Version; /* FW Version */
+ u8 Subversion; /* FW Subversion, default 0x00 */
+ u16 Rsvd1;
+
+
+ /* LONG WORD 1 ---- */
+ u8 Month; /* Release time Month field */
+ u8 Date; /* Release time Date field */
+ u8 Hour; /* Release time Hour field */
+ u8 Minute; /* Release time Minute field */
+ u16 RamCodeSize; /* The size of RAM code */
+ u16 Rsvd2;
+
+ /* LONG WORD 2 ---- */
+ u32 SvnIdx; /* The SVN entry index */
+ u32 Rsvd3;
+
+ /* LONG WORD 3 ---- */
+ u32 Rsvd4;
+ u32 Rsvd5;
+};
+
+#define DRIVER_EARLY_INT_TIME 0x05
+#define BCN_DMA_ATIME_INT_TIME 0x02
+
+
+/* BK, BE, VI, VO, HCCA, MANAGEMENT, COMMAND, HIGH, BEACON. */
+#define MAX_TX_QUEUE 9
+
+#define TX_SELE_HQ BIT(0) /* High Queue */
+#define TX_SELE_LQ BIT(1) /* Low Queue */
+#define TX_SELE_NQ BIT(2) /* Normal Queue */
+
+/* Note: We will divide number of page equally for each queue other than public queue! */
+#define TX_TOTAL_PAGE_NUMBER 0xF8
+#define TX_PAGE_BOUNDARY (TX_TOTAL_PAGE_NUMBER + 1)
+
+/* For Normal Chip Setting */
+/* (HPQ + LPQ + NPQ + PUBQ) shall be TX_TOTAL_PAGE_NUMBER */
+#define NORMAL_PAGE_NUM_PUBQ 0xE7
+#define NORMAL_PAGE_NUM_HPQ 0x0C
+#define NORMAL_PAGE_NUM_LPQ 0x02
+#define NORMAL_PAGE_NUM_NPQ 0x02
+
+/* For Test Chip Setting */
+/* (HPQ + LPQ + PUBQ) shall be TX_TOTAL_PAGE_NUMBER */
+#define TEST_PAGE_NUM_PUBQ 0x7E
+
+/* For Test Chip Setting */
+#define WMM_TEST_TX_TOTAL_PAGE_NUMBER 0xF5
+#define WMM_TEST_TX_PAGE_BOUNDARY (WMM_TEST_TX_TOTAL_PAGE_NUMBER + 1) /* F6 */
+
+#define WMM_TEST_PAGE_NUM_PUBQ 0xA3
+#define WMM_TEST_PAGE_NUM_HPQ 0x29
+#define WMM_TEST_PAGE_NUM_LPQ 0x29
+
+/* Note: For Normal Chip Setting, modify later */
+#define WMM_NORMAL_TX_TOTAL_PAGE_NUMBER 0xF5
+#define WMM_NORMAL_TX_PAGE_BOUNDARY (WMM_TEST_TX_TOTAL_PAGE_NUMBER + 1) /* F6 */
+
+#define WMM_NORMAL_PAGE_NUM_PUBQ 0xB0
+#define WMM_NORMAL_PAGE_NUM_HPQ 0x29
+#define WMM_NORMAL_PAGE_NUM_LPQ 0x1C
+#define WMM_NORMAL_PAGE_NUM_NPQ 0x1C
+
+
+/* */
+/* Chip specific */
+/* */
+#define CHIP_BONDING_IDENTIFIER(_value) (((_value)>>22)&0x3)
+#define CHIP_BONDING_92C_1T2R 0x1
+#define CHIP_BONDING_88C_USB_MCARD 0x2
+#define CHIP_BONDING_88C_USB_HP 0x1
+
+#include "HalVerDef.h"
+#include "hal_com.h"
+
+/* */
+/* Channel Plan */
+/* */
+enum ChannelPlan
+{
+ CHPL_FCC = 0,
+ CHPL_IC = 1,
+ CHPL_ETSI = 2,
+ CHPL_SPAIN = 3,
+ CHPL_FRANCE = 4,
+ CHPL_MKK = 5,
+ CHPL_MKK1 = 6,
+ CHPL_ISRAEL = 7,
+ CHPL_TELEC = 8,
+ CHPL_GLOBAL = 9,
+ CHPL_WORLD = 10,
+};
+
+#define EFUSE_REAL_CONTENT_LEN 512
+#define EFUSE_MAP_LEN 128
+#define EFUSE_MAX_SECTION 16
+#define EFUSE_IC_ID_OFFSET 506 /* For some inferiority IC purpose. added by Roger, 2009.09.02. */
+#define AVAILABLE_EFUSE_ADDR(addr) (addr < EFUSE_REAL_CONTENT_LEN)
+/* */
+/* <Roger_Notes> */
+/* To prevent out of boundary programming case, */
+/* leave 1byte and program full section */
+/* 9bytes + 1byt + 5bytes and pre 1byte. */
+/* For worst case: */
+/* | 1byte|----8bytes----|1byte|--5bytes--| */
+/* | | Reserved(14bytes) | */
+/* */
+
+/* PG data exclude header, dummy 6 bytes frome CP test and reserved 1byte. */
+#define EFUSE_OOB_PROTECT_BYTES 15
+
+#define EFUSE_REAL_CONTENT_LEN_8723A 512
+#define EFUSE_MAP_LEN_8723A 256
+#define EFUSE_MAX_SECTION_8723A 32
+
+/* */
+/* EFUSE for BT definition */
+/* */
+#define EFUSE_BT_REAL_BANK_CONTENT_LEN 512
+#define EFUSE_BT_REAL_CONTENT_LEN 1536 /* 512*3 */
+#define EFUSE_BT_MAP_LEN 1024 /* 1k bytes */
+#define EFUSE_BT_MAX_SECTION 128 /* 1024/8 */
+
+#define EFUSE_PROTECT_BYTES_BANK 16
+
+/* */
+/* <Roger_Notes> For RTL8723 WiFi/BT/GPS multi-function configuration. 2010.10.06. */
+/* */
+enum RT_MULTI_FUNC {
+ RT_MULTI_FUNC_NONE = 0x00,
+ RT_MULTI_FUNC_WIFI = 0x01,
+ RT_MULTI_FUNC_BT = 0x02,
+ RT_MULTI_FUNC_GPS = 0x04,
+};
+
+/* */
+/* <Roger_Notes> For RTL8723 WiFi PDn/GPIO polarity control configuration. 2010.10.08. */
+/* */
+enum RT_POLARITY_CTL {
+ RT_POLARITY_LOW_ACT = 0,
+ RT_POLARITY_HIGH_ACT = 1,
+};
+
+/* For RTL8723 regulator mode. by tynli. 2011.01.14. */
+enum RT_REGULATOR_MODE {
+ RT_SWITCHING_REGULATOR = 0,
+ RT_LDO_REGULATOR = 1,
+};
+
+/* Description: Determine the types of C2H events that are the same in driver and Fw. */
+/* Fisrt constructed by tynli. 2009.10.09. */
+enum {
+ C2H_DBG = 0,
+ C2H_TSF = 1,
+ C2H_AP_RPT_RSP = 2,
+ C2H_CCX_TX_RPT = 3, /* The FW notify the report of the specific tx packet. */
+ C2H_BT_RSSI = 4,
+ C2H_BT_OP_MODE = 5,
+ C2H_EXT_RA_RPT = 6,
+ C2H_HW_INFO_EXCH = 10,
+ C2H_C2H_H2C_TEST = 11,
+ C2H_BT_INFO = 12,
+ C2H_BT_MP_INFO = 15,
+ MAX_C2HEVENT
+};
+
+struct hal_data_8723a {
+ struct hal_version VersionID;
+ enum rt_customer_id CustomerID;
+
+ u16 FirmwareVersion;
+ u16 FirmwareVersionRev;
+ u16 FirmwareSubVersion;
+ u16 FirmwareSignature;
+
+ /* current WIFI_PHY values */
+ u32 ReceiveConfig;
+ enum WIRELESS_MODE CurrentWirelessMode;
+ enum ht_channel_width CurrentChannelBW;
+ u8 CurrentChannel;
+ u8 nCur40MhzPrimeSC;/* Control channel sub-carrier */
+
+ u16 BasicRateSet;
+
+ /* rf_ctrl */
+ u8 rf_type;
+ u8 NumTotalRFPath;
+
+ u8 BoardType;
+ u8 CrystalCap;
+ /* */
+ /* EEPROM setting. */
+ /* */
+ u8 EEPROMVersion;
+ u8 EEPROMCustomerID;
+ u8 EEPROMSubCustomerID;
+ u8 EEPROMRegulatory;
+ u8 EEPROMThermalMeter;
+ u8 EEPROMBluetoothCoexist;
+ u8 EEPROMBluetoothType;
+ u8 EEPROMBluetoothAntNum;
+ u8 EEPROMBluetoothAntIsolation;
+ u8 EEPROMBluetoothRadioShared;
+
+ u8 bTXPowerDataReadFromEEPORM;
+ u8 bAPKThermalMeterIgnore;
+
+ u8 bIQKInitialized;
+ u8 bAntennaDetected;
+
+ u8 TxPwrLevelCck[RF_PATH_MAX][CHANNEL_MAX_NUMBER];
+ u8 TxPwrLevelHT40_1S[RF_PATH_MAX][CHANNEL_MAX_NUMBER]; /* For HT 40MHZ pwr */
+ u8 TxPwrLevelHT40_2S[RF_PATH_MAX][CHANNEL_MAX_NUMBER]; /* For HT 40MHZ pwr */
+ u8 TxPwrHt20Diff[RF_PATH_MAX][CHANNEL_MAX_NUMBER];/* HT 20<->40 Pwr diff */
+ u8 TxPwrLegacyHtDiff[RF_PATH_MAX][CHANNEL_MAX_NUMBER];/* For HT<->legacy pwr diff */
+ /* For power group */
+ u8 PwrGroupHT20[RF_PATH_MAX][CHANNEL_MAX_NUMBER];
+ u8 PwrGroupHT40[RF_PATH_MAX][CHANNEL_MAX_NUMBER];
+
+ u8 LegacyHTTxPowerDiff;/* Legacy to HT rate power diff */
+
+ /* Read/write are allow for following hardware information variables */
+ u8 framesync;
+ u32 framesyncC34;
+ u8 framesyncMonitor;
+ u8 pwrGroupCnt;
+ u32 MCSTxPowerLevelOriginalOffset[7][16];
+ u32 CCKTxPowerLevelOriginalOffset;
+
+ u32 AntennaTxPath; /* Antenna path Tx */
+ u32 AntennaRxPath; /* Antenna path Rx */
+ u8 ExternalPA;
+
+ u8 bLedOpenDrain; /* Support Open-drain arrangement for controlling the LED. Added by Roger, 2009.10.16. */
+
+ u8 b1x1RecvCombine; /* for 1T1R receive combining */
+
+ /* For EDCA Turbo mode */
+
+ u32 AcParam_BE; /* Original parameter for BE, use for EDCA turbo. */
+
+ /* vivi, for tx power tracking, 20080407 */
+ /* u16 TSSI_13dBm; */
+ /* u32 Pwr_Track; */
+ /* The current Tx Power Level */
+ u8 CurrentCckTxPwrIdx;
+ u8 CurrentOfdm24GTxPwrIdx;
+
+ struct bb_reg_define PHYRegDef[4]; /* Radio A/B/C/D */
+
+ bool bRFPathRxEnable[4]; /* We support 4 RF path now. */
+
+ u32 RfRegChnlVal[2];
+
+ u8 bCckHighPower;
+
+ /* RDG enable */
+ bool bRDGEnable;
+
+ /* for host message to fw */
+ u8 LastHMEBoxNum;
+
+ u8 RegTxPause;
+ /* Beacon function related global variable. */
+ u8 RegFwHwTxQCtrl;
+ u8 RegReg542;
+
+ struct dm_priv dmpriv;
+ struct dm_odm_t odmpriv;
+ struct sreset_priv srestpriv;
+
+#ifdef CONFIG_8723AU_BT_COEXIST
+ u8 bBTMode;
+ /* BT only. */
+ struct bt_30info BtInfo;
+ /* For bluetooth co-existance */
+ struct bt_coexist_str bt_coexist;
+#endif
+
+ u8 bDumpRxPkt;/* for debug */
+ u8 FwRsvdPageStartOffset; /* 2010.06.23. Added by tynli. Reserve page start offset except beacon in TxQ. */
+
+ /* 2010/08/09 MH Add CU power down mode. */
+ u8 pwrdown;
+
+ u8 OutEpQueueSel;
+ u8 OutEpNumber;
+
+ /* */
+ /* Add For EEPROM Efuse switch and Efuse Shadow map Setting */
+ /* */
+ u8 EepromOrEfuse;
+ u16 EfuseUsedBytes;
+ u16 BTEfuseUsedBytes;
+
+ /* Interrupt relatd register information. */
+ u32 SysIntrStatus;
+ u32 SysIntrMask;
+
+ /* */
+ /* 2011/02/23 MH Add for 8723 mylti function definition. The define should be moved to an */
+ /* independent file in the future. */
+ /* */
+ /* 8723-----------------------------------------*/
+ enum RT_MULTI_FUNC MultiFunc; /* For multi-function consideration. */
+ enum RT_POLARITY_CTL PolarityCtl; /* For Wifi PDn Polarity control. */
+ enum RT_REGULATOR_MODE RegulatorMode; /* switching regulator or LDO */
+ /* 8723-----------------------------------------
+ * 2011/02/23 MH Add for 8723 mylti function definition. The define should be moved to an */
+ /* independent file in the future. */
+
+ /* Interrupt related register information. */
+ u32 IntArray[2];
+ u32 IntrMask[2];
+};
+
+#define GET_HAL_DATA(__pAdapter) ((struct hal_data_8723a *)((__pAdapter)->HalData))
+#define GET_RF_TYPE(priv) (GET_HAL_DATA(priv)->rf_type)
+
+#define INCLUDE_MULTI_FUNC_BT(_Adapter) (GET_HAL_DATA(_Adapter)->MultiFunc & RT_MULTI_FUNC_BT)
+#define INCLUDE_MULTI_FUNC_GPS(_Adapter) (GET_HAL_DATA(_Adapter)->MultiFunc & RT_MULTI_FUNC_GPS)
+
+struct rxreport_8723a {
+ u32 pktlen:14;
+ u32 crc32:1;
+ u32 icverr:1;
+ u32 drvinfosize:4;
+ u32 security:3;
+ u32 qos:1;
+ u32 shift:2;
+ u32 physt:1;
+ u32 swdec:1;
+ u32 ls:1;
+ u32 fs:1;
+ u32 eor:1;
+ u32 own:1;
+
+ u32 macid:5;
+ u32 tid:4;
+ u32 hwrsvd:4;
+ u32 amsdu:1;
+ u32 paggr:1;
+ u32 faggr:1;
+ u32 a1fit:4;
+ u32 a2fit:4;
+ u32 pam:1;
+ u32 pwr:1;
+ u32 md:1;
+ u32 mf:1;
+ u32 type:2;
+ u32 mc:1;
+ u32 bc:1;
+
+ u32 seq:12;
+ u32 frag:4;
+ u32 nextpktlen:14;
+ u32 nextind:1;
+ u32 rsvd0831:1;
+
+ u32 rxmcs:6;
+ u32 rxht:1;
+ u32 gf:1;
+ u32 splcp:1;
+ u32 bw:1;
+ u32 htc:1;
+ u32 eosp:1;
+ u32 bssidfit:2;
+ u32 rsvd1214:16;
+ u32 unicastwake:1;
+ u32 magicwake:1;
+
+ u32 pattern0match:1;
+ u32 pattern1match:1;
+ u32 pattern2match:1;
+ u32 pattern3match:1;
+ u32 pattern4match:1;
+ u32 pattern5match:1;
+ u32 pattern6match:1;
+ u32 pattern7match:1;
+ u32 pattern8match:1;
+ u32 pattern9match:1;
+ u32 patternamatch:1;
+ u32 patternbmatch:1;
+ u32 patterncmatch:1;
+ u32 rsvd1613:19;
+
+ u32 tsfl;
+
+ u32 bassn:12;
+ u32 bavld:1;
+ u32 rsvd2413:19;
+};
+
+/* rtl8723a_hal_init.c */
+s32 rtl8723a_FirmwareDownload(struct rtw_adapter *padapter);
+void rtl8723a_FirmwareSelfReset(struct rtw_adapter *padapter);
+void rtl8723a_InitializeFirmwareVars(struct rtw_adapter *padapter);
+
+void rtl8723a_InitAntenna_Selection(struct rtw_adapter *padapter);
+void rtl8723a_DeinitAntenna_Selection(struct rtw_adapter *padapter);
+void rtl8723a_CheckAntenna_Selection(struct rtw_adapter *padapter);
+void rtl8723a_init_default_value(struct rtw_adapter *padapter);
+
+s32 InitLLTTable23a(struct rtw_adapter *padapter, u32 boundary);
+
+s32 CardDisableHWSM(struct rtw_adapter *padapter, u8 resetMCU);
+s32 CardDisableWithoutHWSM(struct rtw_adapter *padapter);
+
+/* EFuse */
+u8 GetEEPROMSize8723A(struct rtw_adapter *padapter);
+void Hal_InitPGData(struct rtw_adapter *padapter, u8 *PROMContent);
+void Hal_EfuseParseIDCode(struct rtw_adapter *padapter, u8 *hwinfo);
+void Hal_EfuseParsetxpowerinfo_8723A(struct rtw_adapter *padapter, u8 *PROMContent, bool AutoLoadFail);
+void Hal_EfuseParseBTCoexistInfo_8723A(struct rtw_adapter *padapter, u8 *hwinfo, bool AutoLoadFail);
+void Hal_EfuseParseEEPROMVer(struct rtw_adapter *padapter, u8 *hwinfo, bool AutoLoadFail);
+void rtl8723a_EfuseParseChnlPlan(struct rtw_adapter *padapter, u8 *hwinfo, bool AutoLoadFail);
+void Hal_EfuseParseCustomerID(struct rtw_adapter *padapter, u8 *hwinfo, bool AutoLoadFail);
+void Hal_EfuseParseAntennaDiversity(struct rtw_adapter *padapter, u8 *hwinfo, bool AutoLoadFail);
+void Hal_EfuseParseRateIndicationOption(struct rtw_adapter *padapter, u8 *hwinfo, bool AutoLoadFail);
+void Hal_EfuseParseXtal_8723A(struct rtw_adapter *pAdapter, u8 *hwinfo, u8 AutoLoadFail);
+void Hal_EfuseParseThermalMeter_8723A(struct rtw_adapter *padapter, u8 *hwinfo, bool AutoLoadFail);
+
+/* register */
+void SetBcnCtrlReg23a(struct rtw_adapter *padapter, u8 SetBits, u8 ClearBits);
+void rtl8723a_InitBeaconParameters(struct rtw_adapter *padapter);
+
+void rtl8723a_start_thread(struct rtw_adapter *padapter);
+void rtl8723a_stop_thread(struct rtw_adapter *padapter);
+
+bool c2h_id_filter_ccx_8723a(u8 id);
+int c2h_handler_8723a(struct rtw_adapter *padapter, struct c2h_evt_hdr *c2h_evt);
+
+void rtl8723a_read_adapter_info(struct rtw_adapter *Adapter);
+void rtl8723a_read_chip_version(struct rtw_adapter *padapter);
+void rtl8723a_notch_filter(struct rtw_adapter *adapter, bool enable);
+void rtl8723a_SetBeaconRelatedRegisters(struct rtw_adapter *padapter);
+void rtl8723a_SetHalODMVar(struct rtw_adapter *Adapter,
+ enum hal_odm_variable eVariable,
+ void *pValue1, bool bSet);
+void
+rtl8723a_readefuse(struct rtw_adapter *padapter,
+ u8 efuseType, u16 _offset, u16 _size_byte, u8 *pbuf);
+u16 rtl8723a_EfuseGetCurrentSize_WiFi(struct rtw_adapter *padapter);
+u16 rtl8723a_EfuseGetCurrentSize_BT(struct rtw_adapter *padapter);
+void rtl8723a_update_ramask(struct rtw_adapter *padapter,
+ u32 mac_id, u8 rssi_level);
+
+#endif
diff --git a/drivers/staging/rtl8723au/include/rtl8723a_pg.h b/drivers/staging/rtl8723au/include/rtl8723a_pg.h
new file mode 100644
index 000000000..5c2ec448e
--- /dev/null
+++ b/drivers/staging/rtl8723au/include/rtl8723a_pg.h
@@ -0,0 +1,98 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2011 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * 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.
+ *
+ ******************************************************************************/
+#ifndef __RTL8723A_PG_H__
+#define __RTL8723A_PG_H__
+
+/* EEPROM/Efuse PG Offset for 8723E/8723U/8723S */
+#define EEPROM_CCK_TX_PWR_INX_8723A 0x10
+#define EEPROM_HT40_1S_TX_PWR_INX_8723A 0x16
+#define EEPROM_HT20_TX_PWR_INX_DIFF_8723A 0x1C
+#define EEPROM_OFDM_TX_PWR_INX_DIFF_8723A 0x1F
+#define EEPROM_HT40_MAX_PWR_OFFSET_8723A 0x22
+#define EEPROM_HT20_MAX_PWR_OFFSET_8723A 0x25
+
+#define EEPROM_ChannelPlan_8723A 0x28
+#define EEPROM_TSSI_A_8723A 0x29
+#define EEPROM_THERMAL_METER_8723A 0x2A
+#define RF_OPTION1_8723A 0x2B
+#define RF_OPTION2_8723A 0x2C
+#define RF_OPTION3_8723A 0x2D
+#define RF_OPTION4_8723A 0x2E
+#define EEPROM_VERSION_8723A 0x30
+#define EEPROM_CustomID_8723A 0x31
+#define EEPROM_SubCustomID_8723A 0x32
+#define EEPROM_XTAL_K_8723A 0x33
+#define EEPROM_Chipset_8723A 0x34
+
+/* RTL8723AE */
+#define EEPROM_VID_8723AE 0x49
+#define EEPROM_DID_8723AE 0x4B
+#define EEPROM_SVID_8723AE 0x4D
+#define EEPROM_SMID_8723AE 0x4F
+#define EEPROM_MAC_ADDR_8723AE 0x67
+
+/* RTL8723AU */
+#define EEPROM_MAC_ADDR_8723AU 0xC6
+#define EEPROM_VID_8723AU 0xB7
+#define EEPROM_PID_8723AU 0xB9
+
+/* RTL8723AS */
+#define EEPROM_MAC_ADDR_8723AS 0xAA
+
+/* EEPROM/Efuse Value Type */
+#define EETYPE_TX_PWR 0x0
+
+/* EEPROM/Efuse Default Value */
+#define EEPROM_Default_CrystalCap_8723A 0x20
+
+
+/* EEPROM/EFUSE data structure definition. */
+#define MAX_CHNL_GROUP 3+9
+
+struct txpowerinfo {
+ u8 CCKIndex[RF_PATH_MAX][MAX_CHNL_GROUP];
+ u8 HT40_1SIndex[RF_PATH_MAX][MAX_CHNL_GROUP];
+ u8 HT40_2SIndexDiff[RF_PATH_MAX][MAX_CHNL_GROUP];
+ u8 HT20IndexDiff[RF_PATH_MAX][MAX_CHNL_GROUP];
+ u8 OFDMIndexDiff[RF_PATH_MAX][MAX_CHNL_GROUP];
+ u8 HT40MaxOffset[RF_PATH_MAX][MAX_CHNL_GROUP];
+ u8 HT20MaxOffset[RF_PATH_MAX][MAX_CHNL_GROUP];
+ u8 TSSI_A[3];
+ u8 TSSI_B[3];
+ u8 TSSI_A_5G[3]; /* 5GL/5GM/5GH */
+ u8 TSSI_B_5G[3];
+};
+
+enum bt_ant_num {
+ Ant_x2 = 0,
+ Ant_x1 = 1
+};
+
+enum bt_cotype {
+ BT_2Wire = 0,
+ BT_ISSC_3Wire = 1,
+ BT_Accel = 2,
+ BT_CSR_BC4 = 3,
+ BT_CSR_BC8 = 4,
+ BT_RTL8756 = 5,
+ BT_RTL8723A = 6
+};
+
+enum bt_radioshared {
+ BT_Radio_Shared = 0,
+ BT_Radio_Individual = 1,
+};
+
+#endif
diff --git a/drivers/staging/rtl8723au/include/rtl8723a_recv.h b/drivers/staging/rtl8723au/include/rtl8723a_recv.h
new file mode 100644
index 000000000..875d37b3b
--- /dev/null
+++ b/drivers/staging/rtl8723au/include/rtl8723a_recv.h
@@ -0,0 +1,65 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2011 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * 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.
+ *
+ ******************************************************************************/
+#ifndef __RTL8723A_RECV_H__
+#define __RTL8723A_RECV_H__
+
+#include <osdep_service.h>
+#include <drv_types.h>
+
+#define NR_RECVBUFF 4
+
+#define NR_PREALLOC_RECV_SKB 8
+
+#define RECV_BLK_SZ 512
+#define RECV_BLK_CNT 16
+#define RECV_BLK_TH RECV_BLK_CNT
+
+#define MAX_RECVBUF_SZ 15360 /* 15k < 16k */
+
+#define PHY_RSSI_SLID_WIN_MAX 100
+#define PHY_LINKQUALITY_SLID_WIN_MAX 20
+
+
+struct phy_stat {
+ unsigned int phydw0;
+ unsigned int phydw1;
+ unsigned int phydw2;
+ unsigned int phydw3;
+ unsigned int phydw4;
+ unsigned int phydw5;
+ unsigned int phydw6;
+ unsigned int phydw7;
+};
+
+/* Rx smooth factor */
+#define Rx_Smooth_Factor 20
+
+struct interrupt_msg_format {
+ unsigned int C2H_MSG0;
+ unsigned int C2H_MSG1;
+ unsigned int C2H_MSG2;
+ unsigned int C2H_MSG3;
+ unsigned int HISR; /* from HISR Reg0x124, read to clear */
+ unsigned int HISRE;/* from HISRE Reg0x12c, read to clear */
+ unsigned int MSG_EX;
+};
+
+int rtl8723au_init_recv_priv(struct rtw_adapter *padapter);
+void rtl8723au_free_recv_priv(struct rtw_adapter *padapter);
+void rtl8723a_process_phy_info(struct rtw_adapter *padapter, void *prframe);
+void update_recvframe_attrib(struct recv_frame *precvframe, struct recv_stat *prxstat);
+void update_recvframe_phyinfo(struct recv_frame *precvframe, struct phy_stat *pphy_info);
+
+#endif
diff --git a/drivers/staging/rtl8723au/include/rtl8723a_rf.h b/drivers/staging/rtl8723au/include/rtl8723a_rf.h
new file mode 100644
index 000000000..0432799f5
--- /dev/null
+++ b/drivers/staging/rtl8723au/include/rtl8723a_rf.h
@@ -0,0 +1,58 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2011 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * 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.
+ *
+ ******************************************************************************/
+#ifndef __RTL8723A_RF_H__
+#define __RTL8723A_RF_H__
+
+/*--------------------------Define Parameters-------------------------------*/
+
+/* */
+/* For RF 6052 Series */
+/* */
+#define RF6052_MAX_TX_PWR 0x3F
+#define RF6052_MAX_REG 0x3F
+#define RF6052_MAX_PATH 2
+/*--------------------------Define Parameters-------------------------------*/
+
+
+/*------------------------------Define structure----------------------------*/
+
+/*------------------------------Define structure----------------------------*/
+
+
+/*------------------------Export global variable----------------------------*/
+/*------------------------Export global variable----------------------------*/
+
+/*------------------------Export Marco Definition---------------------------*/
+
+/*------------------------Export Marco Definition---------------------------*/
+
+
+/*--------------------------Exported Function prototype---------------------*/
+
+/* */
+/* RF RL6052 Series API */
+/* */
+void rtl8723a_phy_rf6052set_bw(struct rtw_adapter *Adapter,
+ enum ht_channel_width Bandwidth);
+void rtl823a_phy_rf6052setccktxpower(struct rtw_adapter *Adapter,
+ u8 *pPowerlevel);
+void rtl8723a_PHY_RF6052SetOFDMTxPower(struct rtw_adapter *Adapter,
+ u8 *pPowerLevel, u8 Channel);
+
+/*--------------------------Exported Function prototype---------------------*/
+
+int PHY_RF6052_Config8723A(struct rtw_adapter *Adapter);
+
+#endif
diff --git a/drivers/staging/rtl8723au/include/rtl8723a_spec.h b/drivers/staging/rtl8723au/include/rtl8723a_spec.h
new file mode 100644
index 000000000..2f186890d
--- /dev/null
+++ b/drivers/staging/rtl8723au/include/rtl8723a_spec.h
@@ -0,0 +1,2148 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2011 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * 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.
+ *
+ *******************************************************************************/
+#ifndef __RTL8723A_SPEC_H__
+#define __RTL8723A_SPEC_H__
+
+/* */
+/* */
+/* 0x0000h ~ 0x00FFh System Configuration */
+/* */
+/* */
+#define REG_SYS_ISO_CTRL 0x0000
+#define REG_SYS_FUNC_EN 0x0002
+#define REG_APS_FSMCO 0x0004
+#define REG_SYS_CLKR 0x0008
+#define REG_9346CR 0x000A
+#define REG_EE_VPD 0x000C
+#define REG_AFE_MISC 0x0010
+#define REG_SPS0_CTRL 0x0011
+#define REG_SPS_OCP_CFG 0x0018
+#define REG_RSV_CTRL 0x001C
+#define REG_RF_CTRL 0x001F
+#define REG_LDOA15_CTRL 0x0020
+#define REG_LDOV12D_CTRL 0x0021
+#define REG_LDOHCI12_CTRL 0x0022
+#define REG_LPLDO_CTRL 0x0023
+#define REG_AFE_XTAL_CTRL 0x0024
+#define REG_AFE_PLL_CTRL 0x0028
+#define REG_MAC_PHY_CTRL 0x002c
+#define REG_EFUSE_CTRL 0x0030
+#define REG_EFUSE_TEST 0x0034
+#define REG_PWR_DATA 0x0038
+#define REG_CAL_TIMER 0x003C
+#define REG_ACLK_MON 0x003E
+#define REG_GPIO_MUXCFG 0x0040
+#define REG_GPIO_IO_SEL 0x0042
+#define REG_MAC_PINMUX_CFG 0x0043
+#define REG_GPIO_PIN_CTRL 0x0044
+#define REG_GPIO_INTM 0x0048
+#define REG_LEDCFG0 0x004C
+#define REG_LEDCFG1 0x004D
+#define REG_LEDCFG2 0x004E
+#define REG_LEDCFG3 0x004F
+#define REG_LEDCFG REG_LEDCFG2
+#define REG_FSIMR 0x0050
+#define REG_FSISR 0x0054
+#define REG_HSIMR 0x0058
+#define REG_HSISR 0x005c
+ /* RTL8723 WIFI/BT/GPS Multi-Function GPIO Pin Control. */
+#define REG_GPIO_PIN_CTRL_2 0x0060
+ /* RTL8723 WIFI/BT/GPS Multi-Function GPIO Select. */
+#define REG_GPIO_IO_SEL_2 0x0062
+ /* RTL8723 WIFI/BT/GPS Multi-Function control source. */
+#define REG_MULTI_FUNC_CTRL 0x0068
+#define REG_MCUFWDL 0x0080
+#define REG_HMEBOX_EXT_0 0x0088
+#define REG_HMEBOX_EXT_1 0x008A
+#define REG_HMEBOX_EXT_2 0x008C
+#define REG_HMEBOX_EXT_3 0x008E
+ /* Host suspend counter on FPGA platform */
+#define REG_HOST_SUSP_CNT 0x00BC
+ /* Efuse access protection for RTL8723 */
+#define REG_EFUSE_ACCESS 0x00CF
+#define REG_BIST_SCAN 0x00D0
+#define REG_BIST_RPT 0x00D4
+#define REG_BIST_ROM_RPT 0x00D8
+#define REG_USB_SIE_INTF 0x00E0
+#define REG_PCIE_MIO_INTF 0x00E4
+#define REG_PCIE_MIO_INTD 0x00E8
+#define REG_HPON_FSM 0x00EC
+#define REG_SYS_CFG 0x00F0
+#define REG_GPIO_OUTSTS 0x00F4 /* For RTL8723 only. */
+
+/* */
+/* */
+/* 0x0100h ~ 0x01FFh MACTOP General Configuration */
+/* */
+/* */
+#define REG_CR 0x0100
+#define REG_PBP 0x0104
+#define REG_TRXDMA_CTRL 0x010C
+#define REG_TRXFF_BNDY 0x0114
+#define REG_TRXFF_STATUS 0x0118
+#define REG_RXFF_PTR 0x011C
+#define REG_HIMR 0x0120
+#define REG_HISR 0x0124
+#define REG_HIMRE 0x0128
+#define REG_HISRE 0x012C
+#define REG_CPWM 0x012F
+#define REG_FWIMR 0x0130
+#define REG_FWISR 0x0134
+#define REG_PKTBUF_DBG_CTRL 0x0140
+#define REG_PKTBUF_DBG_DATA_L 0x0144
+#define REG_PKTBUF_DBG_DATA_H 0x0148
+
+#define REG_TC0_CTRL 0x0150
+#define REG_TC1_CTRL 0x0154
+#define REG_TC2_CTRL 0x0158
+#define REG_TC3_CTRL 0x015C
+#define REG_TC4_CTRL 0x0160
+#define REG_TCUNIT_BASE 0x0164
+#define REG_MBIST_START 0x0174
+#define REG_MBIST_DONE 0x0178
+#define REG_MBIST_FAIL 0x017C
+#define REG_C2HEVT_MSG_NORMAL 0x01A0
+#define REG_C2HEVT_CLEAR 0x01AF
+#define REG_C2HEVT_MSG_TEST 0x01B8
+#define REG_MCUTST_1 0x01c0
+#define REG_FMETHR 0x01C8
+#define REG_HMETFR 0x01CC
+#define REG_HMEBOX_0 0x01D0
+#define REG_HMEBOX_1 0x01D4
+#define REG_HMEBOX_2 0x01D8
+#define REG_HMEBOX_3 0x01DC
+
+#define REG_LLT_INIT 0x01E0
+#define REG_BB_ACCEESS_CTRL 0x01E8
+#define REG_BB_ACCESS_DATA 0x01EC
+
+
+/* */
+/* */
+/* 0x0200h ~ 0x027Fh TXDMA Configuration */
+/* */
+/* */
+#define REG_RQPN 0x0200
+#define REG_FIFOPAGE 0x0204
+#define REG_TDECTRL 0x0208
+#define REG_TXDMA_OFFSET_CHK 0x020C
+#define REG_TXDMA_STATUS 0x0210
+#define REG_RQPN_NPQ 0x0214
+
+/* */
+/* */
+/* 0x0280h ~ 0x02FFh RXDMA Configuration */
+/* */
+/* */
+#define REG_RXDMA_AGG_PG_TH 0x0280
+#define REG_RXPKT_NUM 0x0284
+#define REG_RXDMA_STATUS 0x0288
+
+
+/* */
+/* */
+/* 0x0300h ~ 0x03FFh PCIe */
+/* */
+/* */
+#define REG_PCIE_CTRL_REG 0x0300
+#define REG_INT_MIG 0x0304 /* Interrupt Migration */
+ /* TX Beacon Descriptor Address */
+#define REG_BCNQ_DESA 0x0308
+ /* TX High Queue Descriptor Address */
+#define REG_HQ_DESA 0x0310
+ /* TX Manage Queue Descriptor Address */
+#define REG_MGQ_DESA 0x0318
+ /* TX VO Queue Descriptor Address */
+#define REG_VOQ_DESA 0x0320
+ /* TX VI Queue Descriptor Address */
+#define REG_VIQ_DESA 0x0328
+ /* TX BE Queue Descriptor Address */
+#define REG_BEQ_DESA 0x0330
+ /* TX BK Queue Descriptor Address */
+#define REG_BKQ_DESA 0x0338
+ /* RX Queue Descriptor Address */
+#define REG_RX_DESA 0x0340
+ /* Backdoor REG for Access Configuration */
+#define REG_DBI 0x0348
+ /* MDIO for Access PCIE PHY */
+#define REG_MDIO 0x0354
+ /* Debug Selection Register */
+#define REG_DBG_SEL 0x0360
+ /* PCIe RPWM */
+#define REG_PCIE_HRPWM 0x0361
+ /* PCIe CPWM */
+#define REG_PCIE_HCPWM 0x0363
+ /* UART Control */
+#define REG_UART_CTRL 0x0364
+ /* UART TX Descriptor Address */
+#define REG_UART_TX_DESA 0x0370
+ /* UART Rx Descriptor Address */
+#define REG_UART_RX_DESA 0x0378
+
+
+/* spec version 11 */
+/* */
+/* */
+/* 0x0400h ~ 0x047Fh Protocol Configuration */
+/* */
+/* */
+#define REG_VOQ_INFORMATION 0x0400
+#define REG_VIQ_INFORMATION 0x0404
+#define REG_BEQ_INFORMATION 0x0408
+#define REG_BKQ_INFORMATION 0x040C
+#define REG_MGQ_INFORMATION 0x0410
+#define REG_HGQ_INFORMATION 0x0414
+#define REG_BCNQ_INFORMATION 0x0418
+
+
+#define REG_CPU_MGQ_INFORMATION 0x041C
+#define REG_FWHW_TXQ_CTRL 0x0420
+#define REG_HWSEQ_CTRL 0x0423
+#define REG_TXPKTBUF_BCNQ_BDNY 0x0424
+#define REG_TXPKTBUF_MGQ_BDNY 0x0425
+#define REG_LIFETIME_EN 0x0426
+#define REG_MULTI_BCNQ_OFFSET 0x0427
+#define REG_SPEC_SIFS 0x0428
+#define REG_RL 0x042A
+#define REG_DARFRC 0x0430
+#define REG_RARFRC 0x0438
+#define REG_RRSR 0x0440
+#define REG_ARFR0 0x0444
+#define REG_ARFR1 0x0448
+#define REG_ARFR2 0x044C
+#define REG_ARFR3 0x0450
+#define REG_AGGLEN_LMT 0x0458
+#define REG_AMPDU_MIN_SPACE 0x045C
+#define REG_TXPKTBUF_WMAC_LBK_BF_HD 0x045D
+#define REG_FAST_EDCA_CTRL 0x0460
+#define REG_RD_RESP_PKT_TH 0x0463
+#define REG_INIRTS_RATE_SEL 0x0480
+#define REG_INIDATA_RATE_SEL 0x0484
+
+
+#define REG_POWER_STATUS 0x04A4
+#define REG_POWER_STAGE1 0x04B4
+#define REG_POWER_STAGE2 0x04B8
+#define REG_PKT_VO_VI_LIFE_TIME 0x04C0
+#define REG_PKT_BE_BK_LIFE_TIME 0x04C2
+#define REG_STBC_SETTING 0x04C4
+#define REG_PROT_MODE_CTRL 0x04C8
+#define REG_MAX_AGGR_NUM 0x04CA
+#define REG_RTS_MAX_AGGR_NUM 0x04CB
+#define REG_BAR_MODE_CTRL 0x04CC
+#define REG_RA_TRY_RATE_AGG_LMT 0x04CF
+#define REG_NQOS_SEQ 0x04DC
+#define REG_QOS_SEQ 0x04DE
+#define REG_NEED_CPU_HANDLE 0x04E0
+#define REG_PKT_LOSE_RPT 0x04E1
+#define REG_PTCL_ERR_STATUS 0x04E2
+#define REG_DUMMY 0x04FC
+
+
+
+/* */
+/* */
+/* 0x0500h ~ 0x05FFh EDCA Configuration */
+/* */
+/* */
+#define REG_EDCA_VO_PARAM 0x0500
+#define REG_EDCA_VI_PARAM 0x0504
+#define REG_EDCA_BE_PARAM 0x0508
+#define REG_EDCA_BK_PARAM 0x050C
+#define REG_BCNTCFG 0x0510
+#define REG_PIFS 0x0512
+#define REG_RDG_PIFS 0x0513
+#define REG_SIFS_CCK 0x0514
+#define REG_SIFS_OFDM 0x0516
+#define REG_SIFS_CTX 0x0514
+#define REG_SIFS_TRX 0x0516
+#define REG_TSFTR_SYN_OFFSET 0x0518
+#define REG_AGGR_BREAK_TIME 0x051A
+#define REG_SLOT 0x051B
+#define REG_TX_PTCL_CTRL 0x0520
+#define REG_TXPAUSE 0x0522
+#define REG_DIS_TXREQ_CLR 0x0523
+#define REG_RD_CTRL 0x0524
+#define REG_TBTT_PROHIBIT 0x0540
+#define REG_RD_NAV_NXT 0x0544
+#define REG_NAV_PROT_LEN 0x0546
+#define REG_BCN_CTRL 0x0550
+#define REG_BCN_CTRL_1 0x0551
+#define REG_MBID_NUM 0x0552
+#define REG_DUAL_TSF_RST 0x0553
+ /* The same as REG_MBSSID_BCN_SPACE */
+#define REG_BCN_INTERVAL 0x0554
+#define REG_MBSSID_BCN_SPACE 0x0554
+#define REG_DRVERLYINT 0x0558
+#define REG_BCNDMATIM 0x0559
+#define REG_ATIMWND 0x055A
+#define REG_BCN_MAX_ERR 0x055D
+#define REG_RXTSF_OFFSET_CCK 0x055E
+#define REG_RXTSF_OFFSET_OFDM 0x055F
+#define REG_TSFTR 0x0560
+#define REG_TSFTR1 0x0568
+#define REG_INIT_TSFTR 0x0564
+#define REG_ATIMWND_1 0x0570
+#define REG_PSTIMER 0x0580
+#define REG_TIMER0 0x0584
+#define REG_TIMER1 0x0588
+#define REG_ACMHWCTRL 0x05C0
+#define REG_ACMRSTCTRL 0x05C1
+#define REG_ACMAVG 0x05C2
+#define REG_VO_ADMTIME 0x05C4
+#define REG_VI_ADMTIME 0x05C6
+#define REG_BE_ADMTIME 0x05C8
+#define REG_EDCA_RANDOM_GEN 0x05CC
+#define REG_SCH_TXCMD 0x05D0
+
+/* define REG_FW_TSF_SYNC_CNT 0x04A0 */
+#define REG_FW_RESET_TSF_CNT_1 0x05FC
+#define REG_FW_RESET_TSF_CNT_0 0x05FD
+#define REG_FW_BCN_DIS_CNT 0x05FE
+
+/* */
+/* */
+/* 0x0600h ~ 0x07FFh WMAC Configuration */
+/* */
+/* */
+#define REG_APSD_CTRL 0x0600
+#define REG_BWOPMODE 0x0603
+#define REG_TCR 0x0604
+#define REG_RCR 0x0608
+#define REG_RX_PKT_LIMIT 0x060C
+#define REG_RX_DLK_TIME 0x060D
+#define REG_RX_DRVINFO_SZ 0x060F
+
+#define REG_MACID 0x0610
+#define REG_BSSID 0x0618
+#define REG_MAR 0x0620
+#define REG_MBIDCAMCFG 0x0628
+
+#define REG_USTIME_EDCA 0x0638
+#define REG_MAC_SPEC_SIFS 0x063A
+
+/* 20100719 Joseph: Hardware register definition change. (HW datasheet v54) */
+ /* [15:8]SIFS_R2T_OFDM, [7:0]SIFS_R2T_CCK */
+#define REG_R2T_SIFS 0x063C
+ /* [15:8]SIFS_T2T_OFDM, [7:0]SIFS_T2T_CCK */
+#define REG_T2T_SIFS 0x063E
+#define REG_ACKTO 0x0640
+#define REG_CTS2TO 0x0641
+#define REG_EIFS 0x0642
+
+/* WMA, BA, CCX */
+#define REG_NAV_CTRL 0x0650
+#define REG_BACAMCMD 0x0654
+#define REG_BACAMCONTENT 0x0658
+#define REG_LBDLY 0x0660
+#define REG_FWDLY 0x0661
+#define REG_RXERR_RPT 0x0664
+#define REG_WMAC_TRXPTCL_CTL 0x0668
+
+
+/* Security */
+#define REG_CAMCMD 0x0670
+#define REG_CAMWRITE 0x0674
+#define REG_CAMREAD 0x0678
+#define REG_CAMDBG 0x067C
+#define REG_SECCFG 0x0680
+
+/* Power */
+#define REG_WOW_CTRL 0x0690
+#define REG_PSSTATUS 0x0691
+#define REG_PS_RX_INFO 0x0692
+#define REG_LPNAV_CTRL 0x0694
+#define REG_WKFMCAM_CMD 0x0698
+#define REG_WKFMCAM_RWD 0x069C
+#define REG_RXFLTMAP0 0x06A0
+#define REG_RXFLTMAP1 0x06A2
+#define REG_RXFLTMAP2 0x06A4
+#define REG_BCN_PSR_RPT 0x06A8
+#define REG_CALB32K_CTRL 0x06AC
+#define REG_PKT_MON_CTRL 0x06B4
+#define REG_BT_COEX_TABLE 0x06C0
+#define REG_WMAC_RESP_TXINFO 0x06D8
+
+#define REG_MACID1 0x0700
+#define REG_BSSID1 0x0708
+
+
+/* */
+/* */
+/* 0xFE00h ~ 0xFE55h USB Configuration */
+/* */
+/* */
+#define REG_USB_INFO 0xFE17
+#define REG_USB_SPECIAL_OPTION 0xFE55
+#define REG_USB_DMA_AGG_TO 0xFE5B
+#define REG_USB_AGG_TO 0xFE5C
+#define REG_USB_AGG_TH 0xFE5D
+
+/* For test chip */
+#define REG_TEST_USB_TXQS 0xFE48
+#define REG_TEST_SIE_VID 0xFE60 /* 0xFE60~0xFE61 */
+#define REG_TEST_SIE_PID 0xFE62 /* 0xFE62~0xFE63 */
+#define REG_TEST_SIE_OPTIONAL 0xFE64
+#define REG_TEST_SIE_CHIRP_K 0xFE65
+#define REG_TEST_SIE_PHY 0xFE66 /* 0xFE66~0xFE6B */
+#define REG_TEST_SIE_MAC_ADDR 0xFE70 /* 0xFE70~0xFE75 */
+#define REG_TEST_SIE_STRING 0xFE80 /* 0xFE80~0xFEB9 */
+
+
+/* For normal chip */
+#define REG_NORMAL_SIE_VID 0xFE60 /* 0xFE60~0xFE61 */
+#define REG_NORMAL_SIE_PID 0xFE62 /* 0xFE62~0xFE63 */
+#define REG_NORMAL_SIE_OPTIONAL 0xFE64
+#define REG_NORMAL_SIE_EP 0xFE65 /* 0xFE65~0xFE67 */
+#define REG_NORMAL_SIE_PHY 0xFE68 /* 0xFE68~0xFE6B */
+#define REG_NORMAL_SIE_OPTIONAL2 0xFE6C
+#define REG_NORMAL_SIE_GPS_EP 0xFE6D /* RTL8723 only */
+#define REG_NORMAL_SIE_MAC_ADDR 0xFE70 /* 0xFE70~0xFE75 */
+#define REG_NORMAL_SIE_STRING 0xFE80 /* 0xFE80~0xFEDF */
+
+
+/* */
+/* */
+/* Redifine 8192C register definition for compatibility */
+/* */
+/* */
+
+/* TODO: use these definition when using REG_xxx naming rule. */
+/* NOTE: DO NOT Remove these definition. Use later. */
+
+ /* System Isolation Interface Control. */
+#define SYS_ISO_CTRL REG_SYS_ISO_CTRL
+ /* System Function Enable. */
+#define SYS_FUNC_EN REG_SYS_FUNC_EN
+#define SYS_CLK REG_SYS_CLKR
+ /* 93C46/93C56 Command Register. */
+#define CR9346 REG_9346CR
+ /* E-Fuse Control. */
+#define EFUSE_CTRL REG_EFUSE_CTRL
+ /* E-Fuse Test. */
+#define EFUSE_TEST REG_EFUSE_TEST
+ /* Media Status register */
+#define MSR (REG_CR + 2)
+#define ISR REG_HISR
+ /* Timing Sync Function Timer Register. */
+#define TSFR REG_TSFTR
+
+ /* MAC ID Register, Offset 0x0050-0x0053 */
+#define MACIDR0 REG_MACID
+ /* MAC ID Register, Offset 0x0054-0x0055 */
+#define MACIDR4 (REG_MACID + 4)
+
+#define PBP REG_PBP
+
+ /* Redifine MACID register, to compatible prior ICs. */
+#define IDR0 MACIDR0
+#define IDR4 MACIDR4
+
+
+/* */
+/* 9. Security Control Registers (Offset: ) */
+/* */
+ /* Software write CAM input content */
+#define WCAMI REG_CAMWRITE
+ /* Software read/write CAM config */
+#define RCAMO REG_CAMREAD
+#define CAMDBG REG_CAMDBG
+ /* Security Configuration Register */
+#define SECR REG_SECCFG
+
+/* Unused register */
+#define UnusedRegister 0x1BF
+#define DCAM UnusedRegister
+#define PSR UnusedRegister
+#define BBAddr UnusedRegister
+#define PhyDataR UnusedRegister
+
+#define InvalidBBRFValue 0x12345678
+
+/* Min Spacing related settings. */
+#define MAX_MSS_DENSITY_2T 0x13
+#define MAX_MSS_DENSITY_1T 0x0A
+
+/* */
+/* 8192C Cmd9346CR bits (Offset 0xA, 16bit) */
+/* */
+ /* EEPROM enable when set 1 */
+#define CmdEEPROM_En BIT(5)
+ /* System EEPROM select, 0: boot from E-FUSE,
+ 1: The EEPROM used is 9346 */
+#define CmdEERPOMSEL BIT(4)
+#define Cmd9346CR_9356SEL BIT(4)
+#define AutoLoadEEPROM (CmdEEPROM_En|CmdEERPOMSEL)
+#define AutoLoadEFUSE CmdEEPROM_En
+
+/* */
+/* 8192C GPIO MUX Configuration Register (offset 0x40, 4 byte) */
+/* */
+#define GPIOSEL_GPIO 0
+#define GPIOSEL_ENBT BIT(5)
+
+/* */
+/* 8192C GPIO PIN Control Register (offset 0x44, 4 byte) */
+/* */
+ /* GPIO pins input value */
+#define GPIO_IN REG_GPIO_PIN_CTRL
+ /* GPIO pins output value */
+#define GPIO_OUT (REG_GPIO_PIN_CTRL+1)
+ /* GPIO pins output enable when a bit is set to "1";
+ otherwise, input is configured. */
+#define GPIO_IO_SEL (REG_GPIO_PIN_CTRL+2)
+#define GPIO_MOD (REG_GPIO_PIN_CTRL+3)
+
+/* */
+/* 8192C (MSR) Media Status Register (Offset 0x4C, 8 bits) */
+/* */
+/*
+Network Type
+00: No link
+01: Link in ad hoc network
+10: Link in infrastructure network
+11: AP mode
+Default: 00b.
+*/
+#define MSR_NOLINK 0x00
+#define MSR_ADHOC 0x01
+#define MSR_INFRA 0x02
+#define MSR_AP 0x03
+
+/* */
+/* 6. Adaptive Control Registers (Offset: 0x0160 - 0x01CF) */
+/* */
+/* */
+/* 8192C Response Rate Set Register (offset 0x181, 24bits) */
+/* */
+#define RRSR_RSC_OFFSET 21
+#define RRSR_SHORT_OFFSET 23
+#define RRSR_RSC_BW_40M 0x600000
+#define RRSR_RSC_UPSUBCHNL 0x400000
+#define RRSR_RSC_LOWSUBCHNL 0x200000
+#define RRSR_SHORT 0x800000
+#define RRSR_1M BIT(0)
+#define RRSR_2M BIT(1)
+#define RRSR_5_5M BIT(2)
+#define RRSR_11M BIT(3)
+#define RRSR_6M BIT(4)
+#define RRSR_9M BIT(5)
+#define RRSR_12M BIT(6)
+#define RRSR_18M BIT(7)
+#define RRSR_24M BIT(8)
+#define RRSR_36M BIT(9)
+#define RRSR_48M BIT(10)
+#define RRSR_54M BIT(11)
+#define RRSR_MCS0 BIT(12)
+#define RRSR_MCS1 BIT(13)
+#define RRSR_MCS2 BIT(14)
+#define RRSR_MCS3 BIT(15)
+#define RRSR_MCS4 BIT(16)
+#define RRSR_MCS5 BIT(17)
+#define RRSR_MCS6 BIT(18)
+#define RRSR_MCS7 BIT(19)
+#define BRSR_AckShortPmb BIT(23)
+/* CCK ACK: use Short Preamble or not */
+
+/* */
+/* 8192C BW_OPMODE bits (Offset 0x203, 8bit) */
+/* */
+#define BW_OPMODE_20MHZ BIT(2)
+#define BW_OPMODE_5G BIT(1)
+#define BW_OPMODE_11J BIT(0)
+
+
+/* */
+/* 8192C CAM Config Setting (offset 0x250, 1 byte) */
+/* */
+#define CAM_VALID BIT(15)
+#define CAM_NOTVALID 0x0000
+#define CAM_USEDK BIT(5)
+
+#define CAM_CONTENT_COUNT 8
+
+#define CAM_NONE 0x0
+#define CAM_WEP40 0x01
+#define CAM_TKIP 0x02
+#define CAM_AES 0x04
+#define CAM_WEP104 0x05
+
+#define TOTAL_CAM_ENTRY 32
+#define HALF_CAM_ENTRY 16
+
+#define CAM_CONFIG_USEDK true
+#define CAM_CONFIG_NO_USEDK false
+
+#define CAM_WRITE BIT(16)
+#define CAM_READ 0x00000000
+#define CAM_POLLINIG BIT(31)
+
+#define SCR_UseDK 0x01
+#define SCR_TxSecEnable 0x02
+#define SCR_RxSecEnable 0x04
+
+
+/* */
+/* 12. Host Interrupt Status Registers (Offset: 0x0300 - 0x030F) */
+/* */
+/* */
+/* 8190 IMR/ISR bits (offset 0xfd, 8bits) */
+/* */
+#define IMR8190_DISABLED 0x0
+/* IMR DW0 Bit 0-31 */
+
+#define IMR_BCNDMAINT6 BIT(31) /* Beacon DMA Interrupt 6 */
+#define IMR_BCNDMAINT5 BIT(30) /* Beacon DMA Interrupt 5 */
+#define IMR_BCNDMAINT4 BIT(29) /* Beacon DMA Interrupt 4 */
+#define IMR_BCNDMAINT3 BIT(28) /* Beacon DMA Interrupt 3 */
+#define IMR_BCNDMAINT2 BIT(27) /* Beacon DMA Interrupt 2 */
+#define IMR_BCNDMAINT1 BIT(26) /* Beacon DMA Interrupt 1 */
+#define IMR_BCNDOK8 BIT(25) /* Beacon Queue DMA OK
+ Interrupt 8 */
+#define IMR_BCNDOK7 BIT(24) /* Beacon Queue DMA OK
+ Interrupt 7 */
+#define IMR_BCNDOK6 BIT(23) /* Beacon Queue DMA OK
+ Interrupt 6 */
+#define IMR_BCNDOK5 BIT(22) /* Beacon Queue DMA OK
+ Interrupt 5 */
+#define IMR_BCNDOK4 BIT(21) /* Beacon Queue DMA OK
+ Interrupt 4 */
+#define IMR_BCNDOK3 BIT(20) /* Beacon Queue DMA OK
+ Interrupt 3 */
+#define IMR_BCNDOK2 BIT(19) /* Beacon Queue DMA OK
+ Interrupt 2 */
+#define IMR_BCNDOK1 BIT(18) /* Beacon Queue DMA OK
+ Interrupt 1 */
+#define IMR_TIMEOUT2 BIT(17) /* Timeout interrupt 2 */
+#define IMR_TIMEOUT1 BIT(16) /* Timeout interrupt 1 */
+#define IMR_TXFOVW BIT(15) /* Transmit FIFO Overflow */
+#define IMR_PSTIMEOUT BIT(14) /* Power save time out
+ interrupt */
+#define IMR_BcnInt BIT(13) /* Beacon DMA Interrupt 0 */
+#define IMR_RXFOVW BIT(12) /* Receive FIFO Overflow */
+#define IMR_RDU BIT(11) /* Receive Descriptor
+ Unavailable */
+#define IMR_ATIMEND BIT(10) /* For 92C,ATIM Window
+ End Interrupt */
+#define IMR_BDOK BIT(9) /* Beacon Queue DMA OK
+ Interrup */
+#define IMR_HIGHDOK BIT(8) /* High Queue DMA OK
+ Interrupt */
+#define IMR_TBDOK BIT(7) /* Transmit Beacon OK
+ interrup */
+#define IMR_MGNTDOK BIT(6) /* Management Queue DMA OK
+ Interrupt */
+#define IMR_TBDER BIT(5) /* For 92C,Transmit Beacon
+ Error Interrupt */
+#define IMR_BKDOK BIT(4) /* AC_BK DMA OK Interrupt */
+#define IMR_BEDOK BIT(3) /* AC_BE DMA OK Interrupt */
+#define IMR_VIDOK BIT(2) /* AC_VI DMA OK Interrupt */
+#define IMR_VODOK BIT(1) /* AC_VO DMA Interrupt */
+#define IMR_ROK BIT(0) /* Receive DMA OK Interrupt */
+
+#define IMR_RX_MASK (IMR_ROK|IMR_RDU|IMR_RXFOVW)
+#define IMR_TX_MASK (IMR_VODOK|IMR_VIDOK|IMR_BEDOK| \
+ IMR_BKDOK|IMR_MGNTDOK|IMR_HIGHDOK| \
+ IMR_BDOK)
+
+/* 13. Host Interrupt Status Extension Register (Offset: 0x012C-012Eh) */
+#define IMR_BcnInt_E BIT(12)
+#define IMR_TXERR BIT(11)
+#define IMR_RXERR BIT(10)
+#define IMR_C2HCMD BIT(9)
+#define IMR_CPWM BIT(8)
+/* RSVD [2-7] */
+#define IMR_OCPINT BIT(1)
+#define IMR_WLANOFF BIT(0)
+
+
+/* 8192C EEPROM/EFUSE share register definition. */
+
+/* Default Value for EEPROM or EFUSE!!! */
+#define EEPROM_Default_TSSI 0x0
+#define EEPROM_Default_TxPowerDiff 0x0
+#define EEPROM_Default_CrystalCap 0x5
+ /* Default: 2X2, RTL8192CE(QFPN68) */
+#define EEPROM_Default_BoardType 0x02
+#define EEPROM_Default_TxPower 0x1010
+#define EEPROM_Default_HT2T_TxPwr 0x10
+
+#define EEPROM_Default_LegacyHTTxPowerDiff 0x3
+#define EEPROM_Default_ThermalMeter 0x12
+
+#define EEPROM_Default_AntTxPowerDiff 0x0
+#define EEPROM_Default_TxPwDiff_CrystalCap 0x5
+#define EEPROM_Default_TxPowerLevel 0x22
+#define EEPROM_Default_HT40_2SDiff 0x0
+ /* HT20<->40 default Tx Power Index Difference */
+#define EEPROM_Default_HT20_Diff 2
+#define EEPROM_Default_LegacyHTTxPowerDiff 0x3
+#define EEPROM_Default_HT40_PwrMaxOffset 0
+#define EEPROM_Default_HT20_PwrMaxOffset 0
+
+/* For debug */
+#define EEPROM_Default_PID 0x1234
+#define EEPROM_Default_VID 0x5678
+#define EEPROM_Default_CustomerID 0xAB
+#define EEPROM_Default_SubCustomerID 0xCD
+#define EEPROM_Default_Version 0
+
+#define EEPROM_CHANNEL_PLAN_FCC 0x0
+#define EEPROM_CHANNEL_PLAN_IC 0x1
+#define EEPROM_CHANNEL_PLAN_ETSI 0x2
+#define EEPROM_CHANNEL_PLAN_SPAIN 0x3
+#define EEPROM_CHANNEL_PLAN_FRANCE 0x4
+#define EEPROM_CHANNEL_PLAN_MKK 0x5
+#define EEPROM_CHANNEL_PLAN_MKK1 0x6
+#define EEPROM_CHANNEL_PLAN_ISRAEL 0x7
+#define EEPROM_CHANNEL_PLAN_TELEC 0x8
+#define EEPROM_CHANNEL_PLAN_GLOBAL_DOMAIN 0x9
+#define EEPROM_CHANNEL_PLAN_WORLD_WIDE_13 0xA
+#define EEPROM_CHANNEL_PLAN_NCC 0xB
+#define EEPROM_USB_OPTIONAL1 0xE
+#define EEPROM_CHANNEL_PLAN_BY_HW_MASK 0x80
+
+
+#define EEPROM_CID_DEFAULT 0x0
+#define EEPROM_CID_TOSHIBA 0x4
+ /* CCX test. By Bruce, 2009-02-25. */
+#define EEPROM_CID_CCX 0x10
+#define EEPROM_CID_QMI 0x0D
+ /* added by chiyoko for dtm, 20090108 */
+#define EEPROM_CID_WHQL 0xFE
+
+
+#define RTL_EEPROM_ID 0x8129
+
+#define SUPPORT_HW_RADIO_DETECT(pHalData) \
+ (pHalData->BoardType == BOARD_MINICARD || \
+ pHalData->BoardType == BOARD_USB_SOLO || \
+ pHalData->BoardType == BOARD_USB_COMBO)
+
+/* */
+/* EEPROM address for Test chip */
+/* */
+#define EEPROM_TEST_USB_OPT 0x0E
+#define EEPROM_TEST_CHIRP_K 0x0F
+#define EEPROM_TEST_EP_SETTING 0x0E
+#define EEPROM_TEST_USB_PHY 0x10
+
+
+/* */
+/* EEPROM address for Normal chip */
+/* */
+#define EEPROM_NORMAL_USB_OPT 0x0E
+#define EEPROM_NORMAL_CHIRP_K 0x0E /* Changed */
+#define EEPROM_NORMAL_EP_SETTING 0x0F /* Changed */
+#define EEPROM_NORMAL_USB_PHY 0x12 /* Changed */
+
+enum {
+ BOARD_USB_DONGLE = 0, /* USB dongle */
+ BOARD_USB_High_PA = 1, /* USB dongle with high power PA */
+ BOARD_MINICARD = 2, /* Minicard */
+ BOARD_USB_SOLO = 3, /* USB solo-Slim module */
+ BOARD_USB_COMBO = 4, /* USB Combo-Slim module */
+};
+
+/* Test chip and normal chip common define */
+/* */
+/* EEPROM address for both */
+/* */
+#define EEPROM_ID0 0x00
+#define EEPROM_ID1 0x01
+#define EEPROM_RTK_RSV1 0x02
+#define EEPROM_RTK_RSV2 0x03
+#define EEPROM_RTK_RSV3 0x04
+#define EEPROM_RTK_RSV4 0x05
+#define EEPROM_RTK_RSV5 0x06
+#define EEPROM_DBG_SEL 0x07
+#define EEPROM_RTK_RSV6 0x08
+#define EEPROM_VID 0x0A
+#define EEPROM_PID 0x0C
+
+#define EEPROM_MAC_ADDR 0x16
+#define EEPROM_STRING 0x1C
+#define EEPROM_SUBCUSTOMER_ID 0x59
+#define EEPROM_CCK_TX_PWR_INX 0x5A
+#define EEPROM_HT40_1S_TX_PWR_INX 0x60
+#define EEPROM_HT40_2S_TX_PWR_INX_DIFF 0x66
+#define EEPROM_HT20_TX_PWR_INX_DIFF 0x69
+#define EEPROM_OFDM_TX_PWR_INX_DIFF 0x6C
+#define EEPROM_HT40_MAX_PWR_OFFSET 0x6F
+#define EEPROM_HT20_MAX_PWR_OFFSET 0x72
+
+#define EEPROM_CHANNEL_PLAN 0x75
+#define EEPROM_TSSI_A 0x76
+#define EEPROM_TSSI_B 0x77
+#define EEPROM_THERMAL_METER 0x78
+#define EEPROM_RF_OPT1 0x79
+#define EEPROM_RF_OPT2 0x7A
+#define EEPROM_RF_OPT3 0x7B
+#define EEPROM_RF_OPT4 0x7C
+#define EEPROM_VERSION 0x7E
+#define EEPROM_CUSTOMER_ID 0x7F
+
+ /* 0x0: RTL8188SU, 0x1: RTL8191SU, 0x2: RTL8192SU, 0x3: RTL8191GU */
+#define EEPROM_BoardType 0x54
+ /* 0x5C-0x76, Tx Power index. */
+#define EEPROM_TxPwIndex 0x5C
+ /* Difference of gain index between legacy and high throughput OFDM. */
+#define EEPROM_PwDiff 0x67
+ /* CCK Tx Power */
+#define EEPROM_TxPowerCCK 0x5A
+
+/* 2009/02/09 Cosa Add for SD3 requirement */
+ /* HT20 Tx Power Index Difference */
+#define EEPROM_TX_PWR_HT20_DIFF 0x6e
+ /* HT20<->40 default Tx Power Index Difference */
+#define DEFAULT_HT20_TXPWR_DIFF 2
+ /* OFDM Tx Power Index Difference */
+#define EEPROM_TX_PWR_OFDM_DIFF 0x71
+
+ /* Power diff for channel group */
+#define EEPROM_TxPWRGroup 0x73
+ /* Check if power safety is need */
+#define EEPROM_Regulatory 0x79
+
+ /* 92cu, 0x7E[4] */
+#define EEPROM_BLUETOOTH_COEXIST 0x7E
+#define EEPROM_NORMAL_BoardType EEPROM_RF_OPT1 /* 7:5] */
+#define BOARD_TYPE_NORMAL_MASK 0xE0
+#define BOARD_TYPE_TEST_MASK 0x0F
+ /* BIT0 1 for build-in module, 0 for external dongle */
+#define EEPROM_EASY_REPLACEMENT 0x50
+/* */
+/* EPROM content definitions */
+/* */
+#define OS_LINK_SPEED BIT(5)
+
+#define BOARD_TYPE_MASK 0xF
+
+#define BT_COEXISTENCE BIT(4)
+#define BT_CO_SHIFT 4
+
+#define EP_NUMBER_MASK 0x30 /* bit 4:5 0Eh */
+#define EP_NUMBER_SHIFT 4
+
+
+#define USB_PHY_PARA_SIZE 5
+
+
+/* */
+/* EEPROM default value definitions */
+/* */
+/* Use 0xABCD instead of 0x8192 for debug */
+#define EEPROM_DEF_ID_0 0xCD /* Byte 0x00 */
+#define EEPROM_DEF_ID_1 0xAB /* Byte 0x01 */
+
+#define EEPROM_DEF_RTK_RSV_A3 0x74 /* Byte 0x03 */
+#define EEPROM_DEF_RTK_RSV_A4 0x6D /* Byte 0x04 */
+#define EEPROM_DEF_RTK_RSV_A8 0xFF /* Byte 0x08 */
+
+#define EEPROM_DEF_VID_0 0x0A /* Byte 0x0A */
+#define EEPROM_DEF_VID_1 0x0B
+
+#define EEPROM_DEF_PID_0 0x92 /* Byte 0x0C */
+#define EEPROM_DEF_PID_1 0x81
+
+
+#define EEPROM_TEST_DEF_USB_OPT 0x80 /* Byte 0x0E */
+#define EEPROM_NORMAL_DEF_USB_OPT 0x00 /* Byte 0x0E */
+
+#define EEPROM_DEF_CHIRPK 0x15 /* Byte 0x0F */
+
+#define EEPROM_DEF_USB_PHY_0 0x85 /* Byte 0x10 */
+#define EEPROM_DEF_USB_PHY_1 0x62 /* Byte 0x11 */
+#define EEPROM_DEF_USB_PHY_2 0x9E /* Byte 0x12 */
+#define EEPROM_DEF_USB_PHY_3 0x06 /* Byte 0x13 */
+
+#define EEPROM_DEF_TSSI_A 0x09 /* Byte 0x78 */
+#define EEPROM_DEF_TSSI_B 0x09 /* Byte 0x79 */
+
+
+#define EEPROM_DEF_THERMAL_METER 0x12 /* Byte 0x7A */
+
+ /* Check if power safety spec is need */
+#define RF_OPTION1 0x79
+#define RF_OPTION2 0x7A
+#define RF_OPTION3 0x7B
+#define RF_OPTION4 0x7C
+
+
+#define EEPROM_USB_SN BIT(0)
+#define EEPROM_USB_REMOTE_WAKEUP BIT(1)
+#define EEPROM_USB_DEVICE_PWR BIT(2)
+#define EEPROM_EP_NUMBER (BIT(3)|BIT(4))
+
+/*===================================================================
+=====================================================================
+Here the register defines are for 92C. When the define is as same with 92C,
+we will use the 92C's define for the consistency
+So the following defines for 92C is not entire!!!!!!
+=====================================================================
+=====================================================================*/
+/*
+Based on Datasheet V33---090401
+Register Summary
+Current IOREG MAP
+0x0000h ~ 0x00FFh System Configuration (256 Bytes)
+0x0100h ~ 0x01FFh MACTOP General Configuration (256 Bytes)
+0x0200h ~ 0x027Fh TXDMA Configuration (128 Bytes)
+0x0280h ~ 0x02FFh RXDMA Configuration (128 Bytes)
+0x0300h ~ 0x03FFh PCIE EMAC Reserved Region (256 Bytes)
+0x0400h ~ 0x04FFh Protocol Configuration (256 Bytes)
+0x0500h ~ 0x05FFh EDCA Configuration (256 Bytes)
+0x0600h ~ 0x07FFh WMAC Configuration (512 Bytes)
+0x2000h ~ 0x3FFFh 8051 FW Download Region (8196 Bytes)
+*/
+
+/* */
+/* 8192C (RCR) Receive Configuration Register (Offset 0x608, 32 bits) */
+/* */
+#define RCR_APPFCS BIT(31) /* WMAC append FCS after payload*/
+#define RCR_APP_MIC BIT(30)
+#define RCR_APP_PHYSTS BIT(28)
+#define RCR_APP_ICV BIT(29)
+#define RCR_APP_PHYST_RXFF BIT(28)
+#define RCR_APP_BA_SSN BIT(27) /* Accept BA SSN */
+#define RCR_ENMBID BIT(24) /* Enable Multiple BssId. */
+#define RCR_LSIGEN BIT(23)
+#define RCR_MFBEN BIT(22)
+#define RCR_HTC_LOC_CTRL BIT(14) /* MFC<--HTC=1 MFC-->HTC=0 */
+#define RCR_AMF BIT(13) /* Accept management type frame */
+#define RCR_ACF BIT(12) /* Accept control type frame */
+#define RCR_ADF BIT(11) /* Accept data type frame */
+#define RCR_AICV BIT(9) /* Accept ICV error packet */
+#define RCR_ACRC32 BIT(8) /* Accept CRC32 error packet */
+#define RCR_CBSSID_BCN BIT(7) /* Accept BSSID match packet
+ (Rx beacon, probe rsp) */
+#define RCR_CBSSID_DATA BIT(6) /* Accept BSSID match packet
+ (Data) */
+#define RCR_CBSSID RCR_CBSSID_DATA /* Accept BSSID match
+ packet */
+#define RCR_APWRMGT BIT(5) /* Accept power management
+ packet */
+#define RCR_ADD3 BIT(4) /* Accept address 3 match
+ packet */
+#define RCR_AB BIT(3) /* Accept broadcast packet */
+#define RCR_AM BIT(2) /* Accept multicast packet */
+#define RCR_APM BIT(1) /* Accept physical match packet */
+#define RCR_AAP BIT(0) /* Accept all unicast packet */
+#define RCR_MXDMA_OFFSET 8
+#define RCR_FIFO_OFFSET 13
+
+
+
+/* */
+/* 8192c USB specific Regsiter Offset and Content definition, */
+/* 2009.08.18, added by vivi. for merge 92c and 92C into one driver */
+/* */
+/* define APS_FSMCO 0x0004 same with 92Ce */
+#define RSV_CTRL 0x001C
+#define RD_CTRL 0x0524
+
+/* */
+/* */
+/* 0xFE00h ~ 0xFE55h USB Configuration */
+/* */
+/* */
+#define REG_USB_INFO 0xFE17
+#define REG_USB_SPECIAL_OPTION 0xFE55
+#define REG_USB_DMA_AGG_TO 0xFE5B
+#define REG_USB_AGG_TO 0xFE5C
+#define REG_USB_AGG_TH 0xFE5D
+
+#define REG_USB_VID 0xFE60
+#define REG_USB_PID 0xFE62
+#define REG_USB_OPTIONAL 0xFE64
+#define REG_USB_CHIRP_K 0xFE65
+#define REG_USB_PHY 0xFE66
+#define REG_USB_MAC_ADDR 0xFE70
+
+#define REG_USB_HRPWM 0xFE58
+#define REG_USB_HCPWM 0xFE57
+
+#define InvalidBBRFValue 0x12345678
+
+/* */
+/* 8192C Regsiter Bit and Content definition */
+/* */
+/* */
+/* */
+/* 0x0000h ~ 0x00FFh System Configuration */
+/* */
+/* */
+
+/* 2 SPS0_CTRL */
+#define SW18_FPWM BIT(3)
+
+
+/* 2 SYS_ISO_CTRL */
+#define ISO_MD2PP BIT(0)
+#define ISO_UA2USB BIT(1)
+#define ISO_UD2CORE BIT(2)
+#define ISO_PA2PCIE BIT(3)
+#define ISO_PD2CORE BIT(4)
+#define ISO_IP2MAC BIT(5)
+#define ISO_DIOP BIT(6)
+#define ISO_DIOE BIT(7)
+#define ISO_EB2CORE BIT(8)
+#define ISO_DIOR BIT(9)
+
+#define PWC_EV25V BIT(14)
+#define PWC_EV12V BIT(15)
+
+
+/* 2 SYS_FUNC_EN */
+#define FEN_BBRSTB BIT(0)
+#define FEN_BB_GLB_RSTn BIT(1)
+#define FEN_USBA BIT(2)
+#define FEN_UPLL BIT(3)
+#define FEN_USBD BIT(4)
+#define FEN_DIO_PCIE BIT(5)
+#define FEN_PCIEA BIT(6)
+#define FEN_PPLL BIT(7)
+#define FEN_PCIED BIT(8)
+#define FEN_DIOE BIT(9)
+#define FEN_CPUEN BIT(10)
+#define FEN_DCORE BIT(11)
+#define FEN_ELDR BIT(12)
+#define FEN_DIO_RF BIT(13)
+#define FEN_HWPDN BIT(14)
+#define FEN_MREGEN BIT(15)
+
+/* 2 APS_FSMCO */
+#define PFM_LDALL BIT(0)
+#define PFM_ALDN BIT(1)
+#define PFM_LDKP BIT(2)
+#define PFM_WOWL BIT(3)
+#define EnPDN BIT(4)
+#define PDN_PL BIT(5)
+#define APFM_ONMAC BIT(8)
+#define APFM_OFF BIT(9)
+#define APFM_RSM BIT(10)
+#define AFSM_HSUS BIT(11)
+#define AFSM_PCIE BIT(12)
+#define APDM_MAC BIT(13)
+#define APDM_HOST BIT(14)
+#define APDM_HPDN BIT(15)
+#define RDY_MACON BIT(16)
+#define SUS_HOST BIT(17)
+#define ROP_ALD BIT(20)
+#define ROP_PWR BIT(21)
+#define ROP_SPS BIT(22)
+#define SOP_MRST BIT(25)
+#define SOP_FUSE BIT(26)
+#define SOP_ABG BIT(27)
+#define SOP_AMB BIT(28)
+#define SOP_RCK BIT(29)
+#define SOP_A8M BIT(30)
+#define XOP_BTCK BIT(31)
+
+/* 2 SYS_CLKR */
+#define ANAD16V_EN BIT(0)
+#define ANA8M BIT(1)
+#define MACSLP BIT(4)
+#define LOADER_CLK_EN BIT(5)
+#define _80M_SSC_DIS BIT(7)
+#define _80M_SSC_EN_HO BIT(8)
+#define PHY_SSC_RSTB BIT(9)
+#define SEC_CLK_EN BIT(10)
+#define MAC_CLK_EN BIT(11)
+#define SYS_CLK_EN BIT(12)
+#define RING_CLK_EN BIT(13)
+
+
+/* 2 9346CR */
+
+
+#define EEDO BIT(0)
+#define EEDI BIT(1)
+#define EESK BIT(2)
+#define EECS BIT(3)
+/* define EERPROMSEL BIT(4) */
+/* define EEPROM_EN BIT(5) */
+#define BOOT_FROM_EEPROM BIT(4)
+#define EEPROM_EN BIT(5)
+#define EEM0 BIT(6)
+#define EEM1 BIT(7)
+
+
+/* 2 AFE_MISC */
+#define AFE_BGEN BIT(0)
+#define AFE_MBEN BIT(1)
+#define MAC_ID_EN BIT(7)
+
+
+/* 2 SPS0_CTRL */
+
+
+/* 2 SPS_OCP_CFG */
+
+
+/* 2 RSV_CTRL */
+#define WLOCK_ALL BIT(0)
+#define WLOCK_00 BIT(1)
+#define WLOCK_04 BIT(2)
+#define WLOCK_08 BIT(3)
+#define WLOCK_40 BIT(4)
+#define R_DIS_PRST_0 BIT(5)
+#define R_DIS_PRST_1 BIT(6)
+#define LOCK_ALL_EN BIT(7)
+
+/* 2 RF_CTRL */
+#define RF_EN BIT(0)
+#define RF_RSTB BIT(1)
+#define RF_SDMRSTB BIT(2)
+
+
+
+/* 2 LDOA15_CTRL */
+#define LDA15_EN BIT(0)
+#define LDA15_STBY BIT(1)
+#define LDA15_OBUF BIT(2)
+#define LDA15_REG_VOS BIT(3)
+#define _LDA15_VOADJ(x) (((x) & 0x7) << 4)
+
+
+
+/* 2 LDOV12D_CTRL */
+#define LDV12_EN BIT(0)
+#define LDV12_SDBY BIT(1)
+#define LPLDO_HSM BIT(2)
+#define LPLDO_LSM_DIS BIT(3)
+#define _LDV12_VADJ(x) (((x) & 0xF) << 4)
+
+
+/* 2 AFE_XTAL_CTRL */
+#define XTAL_EN BIT(0)
+#define XTAL_BSEL BIT(1)
+#define _XTAL_BOSC(x) (((x) & 0x3) << 2)
+#define _XTAL_CADJ(x) (((x) & 0xF) << 4)
+#define XTAL_GATE_USB BIT(8)
+#define _XTAL_USB_DRV(x) (((x) & 0x3) << 9)
+#define XTAL_GATE_AFE BIT(11)
+#define _XTAL_AFE_DRV(x) (((x) & 0x3) << 12)
+#define XTAL_RF_GATE BIT(14)
+#define _XTAL_RF_DRV(x) (((x) & 0x3) << 15)
+#define XTAL_GATE_DIG BIT(17)
+#define _XTAL_DIG_DRV(x) (((x) & 0x3) << 18)
+#define XTAL_BT_GATE BIT(20)
+#define _XTAL_BT_DRV(x) (((x) & 0x3) << 21)
+#define _XTAL_GPIO(x) (((x) & 0x7) << 23)
+
+
+#define CKDLY_AFE BIT(26)
+#define CKDLY_USB BIT(27)
+#define CKDLY_DIG BIT(28)
+#define CKDLY_BT BIT(29)
+
+
+/* 2 AFE_PLL_CTRL */
+#define APLL_EN BIT(0)
+#define APLL_320_EN BIT(1)
+#define APLL_FREF_SEL BIT(2)
+#define APLL_EDGE_SEL BIT(3)
+#define APLL_WDOGB BIT(4)
+#define APLL_LPFEN BIT(5)
+
+#define APLL_REF_CLK_13MHZ 0x1
+#define APLL_REF_CLK_19_2MHZ 0x2
+#define APLL_REF_CLK_20MHZ 0x3
+#define APLL_REF_CLK_25MHZ 0x4
+#define APLL_REF_CLK_26MHZ 0x5
+#define APLL_REF_CLK_38_4MHZ 0x6
+#define APLL_REF_CLK_40MHZ 0x7
+
+#define APLL_320EN BIT(14)
+#define APLL_80EN BIT(15)
+#define APLL_1MEN BIT(24)
+
+
+/* 2 EFUSE_CTRL */
+#define ALD_EN BIT(18)
+#define EF_PD BIT(19)
+#define EF_FLAG BIT(31)
+
+/* 2 EFUSE_TEST (For RTL8723 partially) */
+#define EF_TRPT BIT(7)
+ /* 00: Wifi Efuse, 01: BT Efuse0, 10: BT Efuse1, 11: BT Efuse2 */
+#define EF_CELL_SEL (BIT(8)|BIT(9))
+#define LDOE25_EN BIT(31)
+#define EFUSE_SEL(x) (((x) & 0x3) << 8)
+#define EFUSE_SEL_MASK 0x300
+#define EFUSE_WIFI_SEL_0 0x0
+#define EFUSE_BT_SEL_0 0x1
+#define EFUSE_BT_SEL_1 0x2
+#define EFUSE_BT_SEL_2 0x3
+
+#define EFUSE_ACCESS_ON 0x69 /* For RTL8723 only. */
+#define EFUSE_ACCESS_OFF 0x00 /* For RTL8723 only. */
+
+/* 2 PWR_DATA */
+
+/* 2 CAL_TIMER */
+
+/* 2 ACLK_MON */
+#define RSM_EN BIT(0)
+#define Timer_EN BIT(4)
+
+
+/* 2 GPIO_MUXCFG */
+#define TRSW0EN BIT(2)
+#define TRSW1EN BIT(3)
+#define EROM_EN BIT(4)
+#define EnBT BIT(5)
+#define EnUart BIT(8)
+#define Uart_910 BIT(9)
+#define EnPMAC BIT(10)
+#define SIC_SWRST BIT(11)
+#define EnSIC BIT(12)
+#define SIC_23 BIT(13)
+#define EnHDP BIT(14)
+#define SIC_LBK BIT(15)
+
+/* 2 GPIO_PIN_CTRL */
+
+/* GPIO BIT */
+#define HAL_8192C_HW_GPIO_WPS_BIT BIT(2)
+
+/* 2 GPIO_INTM */
+
+/* 2 LEDCFG */
+#define LED0PL BIT(4)
+#define LED0DIS BIT(7)
+#define LED1DIS BIT(15)
+#define LED1PL BIT(12)
+
+#define SECCAM_CLR BIT(30)
+
+
+/* 2 FSIMR */
+
+/* 2 FSISR */
+
+
+/* 2 8051FWDL */
+/* 2 MCUFWDL */
+#define MCUFWDL_EN BIT(0)
+#define MCUFWDL_RDY BIT(1)
+#define FWDL_ChkSum_rpt BIT(2)
+#define MACINI_RDY BIT(3)
+#define BBINI_RDY BIT(4)
+#define RFINI_RDY BIT(5)
+#define WINTINI_RDY BIT(6)
+#define CPRST BIT(23)
+
+/* 2REG_HPON_FSM */
+#define BOND92CE_1T2R_CFG BIT(22)
+
+
+/* 2 REG_SYS_CFG */
+#define XCLK_VLD BIT(0)
+#define ACLK_VLD BIT(1)
+#define UCLK_VLD BIT(2)
+#define PCLK_VLD BIT(3)
+#define PCIRSTB BIT(4)
+#define V15_VLD BIT(5)
+#define TRP_B15V_EN BIT(7)
+#define SIC_IDLE BIT(8)
+#define BD_MAC2 BIT(9)
+#define BD_MAC1 BIT(10)
+#define IC_MACPHY_MODE BIT(11)
+#define CHIP_VER (BIT(12)|BIT(13)|BIT(14)|BIT(15))
+#define BT_FUNC BIT(16)
+#define VENDOR_ID BIT(19)
+#define PAD_HWPD_IDN BIT(22)
+#define TRP_VAUX_EN BIT(23)
+#define TRP_BT_EN BIT(24)
+#define BD_PKG_SEL BIT(25)
+#define BD_HCI_SEL BIT(26)
+#define TYPE_ID BIT(27)
+
+#define CHIP_VER_RTL_MASK 0xF000 /* Bit 12 ~ 15 */
+#define CHIP_VER_RTL_SHIFT 12
+
+/* 2REG_GPIO_OUTSTS (For RTL8723 only) */
+#define EFS_HCI_SEL (BIT(0)|BIT(1))
+#define PAD_HCI_SEL (BIT(2)|BIT(3))
+#define HCI_SEL (BIT(4)|BIT(5))
+#define PKG_SEL_HCI BIT(6)
+#define FEN_GPS BIT(7)
+#define FEN_BT BIT(8)
+#define FEN_WL BIT(9)
+#define FEN_PCI BIT(10)
+#define FEN_USB BIT(11)
+#define BTRF_HWPDN_N BIT(12)
+#define WLRF_HWPDN_N BIT(13)
+#define PDN_BT_N BIT(14)
+#define PDN_GPS_N BIT(15)
+#define BT_CTL_HWPDN BIT(16)
+#define GPS_CTL_HWPDN BIT(17)
+#define PPHY_SUSB BIT(20)
+#define UPHY_SUSB BIT(21)
+#define PCI_SUSEN BIT(22)
+#define USB_SUSEN BIT(23)
+#define RF_RL_ID (BIT(31)|BIT(30)|BIT(29)|BIT(28))
+
+/* */
+/* */
+/* 0x0100h ~ 0x01FFh MACTOP General Configuration */
+/* */
+/* */
+
+
+/* 2 Function Enable Registers */
+/* 2 CR */
+
+#define REG_LBMODE (REG_CR + 3)
+
+
+#define HCI_TXDMA_EN BIT(0)
+#define HCI_RXDMA_EN BIT(1)
+#define TXDMA_EN BIT(2)
+#define RXDMA_EN BIT(3)
+#define PROTOCOL_EN BIT(4)
+#define SCHEDULE_EN BIT(5)
+#define MACTXEN BIT(6)
+#define MACRXEN BIT(7)
+#define ENSWBCN BIT(8)
+#define ENSEC BIT(9)
+
+#define _LBMODE(x) (((x) & 0xF) << 24)
+#define MASK_LBMODE 0xF000000
+#define LOOPBACK_NORMAL 0x0
+#define LOOPBACK_IMMEDIATELY 0xB
+#define LOOPBACK_MAC_DELAY 0x3
+#define LOOPBACK_PHY 0x1
+#define LOOPBACK_DMA 0x7
+
+
+/* 2 PBP - Page Size Register */
+#define GET_RX_PAGE_SIZE(value) ((value) & 0xF)
+#define GET_TX_PAGE_SIZE(value) (((value) & 0xF0) >> 4)
+#define _PSRX_MASK 0xF
+#define _PSTX_MASK 0xF0
+#define _PSRX(x) (x)
+#define _PSTX(x) ((x) << 4)
+
+#define PBP_64 0x0
+#define PBP_128 0x1
+#define PBP_256 0x2
+#define PBP_512 0x3
+#define PBP_1024 0x4
+
+
+/* 2 TX/RXDMA */
+#define RXDMA_ARBBW_EN BIT(0)
+#define RXSHFT_EN BIT(1)
+#define RXDMA_AGG_EN BIT(2)
+#define QS_VO_QUEUE BIT(8)
+#define QS_VI_QUEUE BIT(9)
+#define QS_BE_QUEUE BIT(10)
+#define QS_BK_QUEUE BIT(11)
+#define QS_MANAGER_QUEUE BIT(12)
+#define QS_HIGH_QUEUE BIT(13)
+
+#define HQSEL_VOQ BIT(0)
+#define HQSEL_VIQ BIT(1)
+#define HQSEL_BEQ BIT(2)
+#define HQSEL_BKQ BIT(3)
+#define HQSEL_MGTQ BIT(4)
+#define HQSEL_HIQ BIT(5)
+
+/* For normal driver, 0x10C */
+#define _TXDMA_HIQ_MAP(x) (((x)&0x3) << 14)
+#define _TXDMA_MGQ_MAP(x) (((x)&0x3) << 12)
+#define _TXDMA_BKQ_MAP(x) (((x)&0x3) << 10)
+#define _TXDMA_BEQ_MAP(x) (((x)&0x3) << 8)
+#define _TXDMA_VIQ_MAP(x) (((x)&0x3) << 6)
+#define _TXDMA_VOQ_MAP(x) (((x)&0x3) << 4)
+
+#define QUEUE_LOW 1
+#define QUEUE_NORMAL 2
+#define QUEUE_HIGH 3
+
+
+
+/* 2 TRXFF_BNDY */
+
+
+/* 2 LLT_INIT */
+#define _LLT_NO_ACTIVE 0x0
+#define _LLT_WRITE_ACCESS 0x1
+#define _LLT_READ_ACCESS 0x2
+
+#define _LLT_INIT_DATA(x) ((x) & 0xFF)
+#define _LLT_INIT_ADDR(x) (((x) & 0xFF) << 8)
+#define _LLT_OP(x) (((x) & 0x3) << 30)
+#define _LLT_OP_VALUE(x) (((x) >> 30) & 0x3)
+
+
+/* 2 BB_ACCESS_CTRL */
+#define BB_WRITE_READ_MASK (BIT(31) | BIT(30))
+#define BB_WRITE_EN BIT(30)
+#define BB_READ_EN BIT(31)
+/* define BB_ADDR_MASK 0xFFF */
+/* define _BB_ADDR(x) ((x) & BB_ADDR_MASK) */
+
+/* */
+/* */
+/* 0x0200h ~ 0x027Fh TXDMA Configuration */
+/* */
+/* */
+/* 2 RQPN */
+#define _HPQ(x) ((x) & 0xFF)
+#define _LPQ(x) (((x) & 0xFF) << 8)
+#define _PUBQ(x) (((x) & 0xFF) << 16)
+ /* NOTE: in RQPN_NPQ register */
+#define _NPQ(x) ((x) & 0xFF)
+
+
+#define HPQ_PUBLIC_DIS BIT(24)
+#define LPQ_PUBLIC_DIS BIT(25)
+#define LD_RQPN BIT(31)
+
+
+/* 2 TDECTRL */
+#define BCN_VALID BIT(16)
+#define BCN_HEAD(x) (((x) & 0xFF) << 8)
+#define BCN_HEAD_MASK 0xFF00
+
+/* 2 TDECTL */
+#define BLK_DESC_NUM_SHIFT 4
+#define BLK_DESC_NUM_MASK 0xF
+
+
+/* 2 TXDMA_OFFSET_CHK */
+#define DROP_DATA_EN BIT(9)
+
+/* */
+/* */
+/* 0x0400h ~ 0x047Fh Protocol Configuration */
+/* */
+/* */
+/* 2 FWHW_TXQ_CTRL */
+#define EN_AMPDU_RTY_NEW BIT(7)
+
+/* 2 INIRTSMCS_SEL */
+#define _INIRTSMCS_SEL(x) ((x) & 0x3F)
+
+
+/* 2 SPEC SIFS */
+#define _SPEC_SIFS_CCK(x) ((x) & 0xFF)
+#define _SPEC_SIFS_OFDM(x) (((x) & 0xFF) << 8)
+
+
+/* 2 RRSR */
+
+#define RATE_REG_BITMAP_ALL 0xFFFFF
+
+#define _RRSC_BITMAP(x) ((x) & 0xFFFFF)
+
+#define _RRSR_RSC(x) (((x) & 0x3) << 21)
+#define RRSR_RSC_RESERVED 0x0
+#define RRSR_RSC_UPPER_SUBCHANNEL 0x1
+#define RRSR_RSC_LOWER_SUBCHANNEL 0x2
+#define RRSR_RSC_DUPLICATE_MODE 0x3
+
+
+/* 2 ARFR */
+#define USE_SHORT_G1 BIT(20)
+
+/* 2 AGGLEN_LMT_L */
+#define _AGGLMT_MCS0(x) ((x) & 0xF)
+#define _AGGLMT_MCS1(x) (((x) & 0xF) << 4)
+#define _AGGLMT_MCS2(x) (((x) & 0xF) << 8)
+#define _AGGLMT_MCS3(x) (((x) & 0xF) << 12)
+#define _AGGLMT_MCS4(x) (((x) & 0xF) << 16)
+#define _AGGLMT_MCS5(x) (((x) & 0xF) << 20)
+#define _AGGLMT_MCS6(x) (((x) & 0xF) << 24)
+#define _AGGLMT_MCS7(x) (((x) & 0xF) << 28)
+
+
+/* 2 RL */
+#define RETRY_LIMIT_SHORT_SHIFT 8
+#define RETRY_LIMIT_LONG_SHIFT 0
+
+
+/* 2 DARFRC */
+#define _DARF_RC1(x) ((x) & 0x1F)
+#define _DARF_RC2(x) (((x) & 0x1F) << 8)
+#define _DARF_RC3(x) (((x) & 0x1F) << 16)
+#define _DARF_RC4(x) (((x) & 0x1F) << 24)
+/* NOTE: shift starting from address (DARFRC + 4) */
+#define _DARF_RC5(x) ((x) & 0x1F)
+#define _DARF_RC6(x) (((x) & 0x1F) << 8)
+#define _DARF_RC7(x) (((x) & 0x1F) << 16)
+#define _DARF_RC8(x) (((x) & 0x1F) << 24)
+
+
+/* 2 RARFRC */
+#define _RARF_RC1(x) ((x) & 0x1F)
+#define _RARF_RC2(x) (((x) & 0x1F) << 8)
+#define _RARF_RC3(x) (((x) & 0x1F) << 16)
+#define _RARF_RC4(x) (((x) & 0x1F) << 24)
+/* NOTE: shift starting from address (RARFRC + 4) */
+#define _RARF_RC5(x) ((x) & 0x1F)
+#define _RARF_RC6(x) (((x) & 0x1F) << 8)
+#define _RARF_RC7(x) (((x) & 0x1F) << 16)
+#define _RARF_RC8(x) (((x) & 0x1F) << 24)
+
+
+/* */
+/* */
+/* 0x0500h ~ 0x05FFh EDCA Configuration */
+/* */
+/* */
+
+
+
+/* 2 EDCA setting */
+#define AC_PARAM_TXOP_LIMIT_OFFSET 16
+#define AC_PARAM_ECW_MAX_OFFSET 12
+#define AC_PARAM_ECW_MIN_OFFSET 8
+#define AC_PARAM_AIFS_OFFSET 0
+
+
+/* 2 EDCA_VO_PARAM */
+#define _AIFS(x) (x)
+#define _ECW_MAX_MIN(x) ((x) << 8)
+#define _TXOP_LIMIT(x) ((x) << 16)
+
+
+#define _BCNIFS(x) ((x) & 0xFF)
+#define _BCNECW(x) (((x) & 0xF))<< 8)
+
+
+#define _LRL(x) ((x) & 0x3F)
+#define _SRL(x) (((x) & 0x3F) << 8)
+
+
+/* 2 SIFS_CCK */
+#define _SIFS_CCK_CTX(x) ((x) & 0xFF)
+#define _SIFS_CCK_TRX(x) (((x) & 0xFF) << 8);
+
+
+/* 2 SIFS_OFDM */
+#define _SIFS_OFDM_CTX(x) ((x) & 0xFF)
+#define _SIFS_OFDM_TRX(x) (((x) & 0xFF) << 8);
+
+
+/* 2 TBTT PROHIBIT */
+#define _TBTT_PROHIBIT_HOLD(x) (((x) & 0xFF) << 8)
+
+
+/* 2 REG_RD_CTRL */
+#define DIS_EDCA_CNT_DWN BIT(11)
+
+
+/* 2 BCN_CTRL */
+#define EN_MBSSID BIT(1)
+#define EN_TXBCN_RPT BIT(2)
+#define EN_BCN_FUNCTION BIT(3)
+#define DIS_TSF_UPDATE BIT(3)
+
+/* The same function but different bit field. */
+#define DIS_TSF_UDT0_NORMAL_CHIP BIT(4)
+#define DIS_TSF_UDT0_TEST_CHIP BIT(5)
+
+/* 2 ACMHWCTRL */
+#define AcmHw_HwEn BIT(0)
+#define AcmHw_BeqEn BIT(1)
+#define AcmHw_ViqEn BIT(2)
+#define AcmHw_VoqEn BIT(3)
+#define AcmHw_BeqStatus BIT(4)
+#define AcmHw_ViqStatus BIT(5)
+#define AcmHw_VoqStatus BIT(6)
+
+
+
+/* */
+/* */
+/* 0x0600h ~ 0x07FFh WMAC Configuration */
+/* */
+/* */
+
+/* 2 APSD_CTRL */
+#define APSDOFF BIT(6)
+#define APSDOFF_STATUS BIT(7)
+
+
+/* 2 BWOPMODE */
+#define BW_20MHZ BIT(2)
+
+
+#define RATE_BITMAP_ALL 0xFFFFF
+
+/* Only use CCK 1M rate for ACK */
+#define RATE_RRSR_CCK_ONLY_1M 0xFFFF1
+
+/* 2 TCR */
+#define TSFRST BIT(0)
+#define DIS_GCLK BIT(1)
+#define PAD_SEL BIT(2)
+#define PWR_ST BIT(6)
+#define PWRBIT_OW_EN BIT(7)
+#define ACRC BIT(8)
+#define CFENDFORM BIT(9)
+#define ICV BIT(10)
+
+
+
+/* 2 RCR */
+#define AAP BIT(0)
+#define APM BIT(1)
+#define AM BIT(2)
+#define AB BIT(3)
+#define ADD3 BIT(4)
+#define APWRMGT BIT(5)
+#define CBSSID BIT(6)
+#define CBSSID_BCN BIT(7)
+#define ACRC32 BIT(8)
+#define AICV BIT(9)
+#define ADF BIT(11)
+#define ACF BIT(12)
+#define AMF BIT(13)
+#define HTC_LOC_CTRL BIT(14)
+#define UC_DATA_EN BIT(16)
+#define BM_DATA_EN BIT(17)
+#define MFBEN BIT(22)
+#define LSIGEN BIT(23)
+#define EnMBID BIT(24)
+#define APP_BASSN BIT(27)
+#define APP_PHYSTS BIT(28)
+#define APP_ICV BIT(29)
+#define APP_MIC BIT(30)
+#define APP_FCS BIT(31)
+
+/* 2 RX_PKT_LIMIT */
+
+/* 2 RX_DLK_TIME */
+
+/* 2 MBIDCAMCFG */
+
+
+
+/* 2 AMPDU_MIN_SPACE */
+#define _MIN_SPACE(x) ((x) & 0x7)
+#define _SHORT_GI_PADDING(x) (((x) & 0x1F) << 3)
+
+
+/* 2 RXERR_RPT */
+#define RXERR_TYPE_OFDM_PPDU 0
+#define RXERR_TYPE_OFDMfalse_ALARM 1
+#define RXERR_TYPE_OFDM_MPDU_OK 2
+#define RXERR_TYPE_OFDM_MPDU_FAIL 3
+#define RXERR_TYPE_CCK_PPDU 4
+#define RXERR_TYPE_CCKfalse_ALARM 5
+#define RXERR_TYPE_CCK_MPDU_OK 6
+#define RXERR_TYPE_CCK_MPDU_FAIL 7
+#define RXERR_TYPE_HT_PPDU 8
+#define RXERR_TYPE_HTfalse_ALARM 9
+#define RXERR_TYPE_HT_MPDU_TOTAL 10
+#define RXERR_TYPE_HT_MPDU_OK 11
+#define RXERR_TYPE_HT_MPDU_FAIL 12
+#define RXERR_TYPE_RX_FULL_DROP 15
+
+#define RXERR_COUNTER_MASK 0xFFFFF
+#define RXERR_RPT_RST BIT(27)
+#define _RXERR_RPT_SEL(type) ((type) << 28)
+
+
+/* 2 SECCFG */
+#define SCR_TxUseDK BIT(0) /* Force Tx Use Default Key */
+#define SCR_RxUseDK BIT(1) /* Force Rx Use Default Key */
+#define SCR_TxEncEnable BIT(2) /* Enable Tx Encryption */
+#define SCR_RxDecEnable BIT(3) /* Enable Rx Decryption */
+#define SCR_SKByA2 BIT(4) /* Search kEY BY A2 */
+#define SCR_NoSKMC BIT(5) /* No Key Search Multicast */
+
+
+
+/* */
+/* */
+/* 0xFE00h ~ 0xFE55h USB Configuration */
+/* */
+/* */
+
+/* 2 USB Information (0xFE17) */
+#define USB_IS_HIGH_SPEED 0
+#define USB_IS_FULL_SPEED 1
+#define USB_SPEED_MASK BIT(5)
+
+#define USB_NORMAL_SIE_EP_MASK 0xF
+#define USB_NORMAL_SIE_EP_SHIFT 4
+
+#define USB_TEST_EP_MASK 0x30
+#define USB_TEST_EP_SHIFT 4
+
+/* 2 Special Option */
+#define USB_AGG_EN BIT(3)
+
+
+/* 2REG_C2HEVT_CLEAR */
+ /* Set by driver and notify FW that the driver has read the
+ C2H command message */
+#define C2H_EVT_HOST_CLOSE 0x00
+ /* Set by FW indicating that FW had set the C2H command message
+ and it's not yet read by driver. */
+#define C2H_EVT_FW_CLOSE 0xFF
+
+
+/* 2REG_MULTI_FUNC_CTRL(For RTL8723 Only) */
+ /* Enable GPIO[9] as WiFi HW PDn source */
+#define WL_HWPDN_EN BIT(0)
+ /* WiFi HW PDn polarity control */
+#define WL_HWPDN_SL BIT(1)
+ /* WiFi function enable */
+#define WL_FUNC_EN BIT(2)
+ /* Enable GPIO[9] as WiFi RF HW PDn source */
+#define WL_HWROF_EN BIT(3)
+ /* Enable GPIO[11] as BT HW PDn source */
+#define BT_HWPDN_EN BIT(16)
+ /* BT HW PDn polarity control */
+#define BT_HWPDN_SL BIT(17)
+ /* BT function enable */
+#define BT_FUNC_EN BIT(18)
+ /* Enable GPIO[11] as BT/GPS RF HW PDn source */
+#define BT_HWROF_EN BIT(19)
+ /* Enable GPIO[10] as GPS HW PDn source */
+#define GPS_HWPDN_EN BIT(20)
+ /* GPS HW PDn polarity control */
+#define GPS_HWPDN_SL BIT(21)
+ /* GPS function enable */
+#define GPS_FUNC_EN BIT(22)
+
+/* 3 REG_LIFECTRL_CTRL */
+#define HAL92C_EN_PKT_LIFE_TIME_BK BIT(3)
+#define HAL92C_EN_PKT_LIFE_TIME_BE BIT(2)
+#define HAL92C_EN_PKT_LIFE_TIME_VI BIT(1)
+#define HAL92C_EN_PKT_LIFE_TIME_VO BIT(0)
+
+#define HAL92C_MSDU_LIFE_TIME_UNIT 128 /* in us, said by Tim. */
+
+/* */
+/* General definitions */
+/* */
+
+#define LAST_ENTRY_OF_TX_PKT_BUFFER 255
+
+#define POLLING_LLT_THRESHOLD 20
+#define POLLING_READY_TIMEOUT_COUNT 1000
+
+/* Min Spacing related settings. */
+#define MAX_MSS_DENSITY_2T 0x13
+#define MAX_MSS_DENSITY_1T 0x0A
+
+/* */
+/* 8723A Regsiter offset definition */
+/* */
+#define HAL_8723A_NAV_UPPER_UNIT 128 /* micro-second */
+
+/* */
+/* */
+/* 0x0000h ~ 0x00FFh System Configuration */
+/* */
+/* */
+#define REG_SYSON_REG_LOCK 0x001C
+
+
+/* */
+/* */
+/* 0x0100h ~ 0x01FFh MACTOP General Configuration */
+/* */
+/* */
+#define REG_FTIMR 0x0138
+
+
+/* */
+/* */
+/* 0x0200h ~ 0x027Fh TXDMA Configuration */
+/* */
+/* */
+
+
+/* */
+/* */
+/* 0x0280h ~ 0x02FFh RXDMA Configuration */
+/* */
+/* */
+
+
+/* */
+/* */
+/* 0x0300h ~ 0x03FFh PCIe */
+/* */
+/* */
+
+
+/* */
+/* */
+/* 0x0400h ~ 0x047Fh Protocol Configuration */
+/* */
+/* */
+#define REG_EARLY_MODE_CONTROL 0x4D0
+
+
+/* */
+/* */
+/* 0x0500h ~ 0x05FFh EDCA Configuration */
+/* */
+/* */
+
+/* 2 BCN_CTRL */
+#define DIS_ATIM BIT(0)
+#define DIS_BCNQ_SUB BIT(1)
+#define DIS_TSF_UDT BIT(4)
+
+
+/* */
+/* */
+/* 0x0600h ~ 0x07FFh WMAC Configuration */
+/* */
+/* */
+/* */
+/* Note: */
+/* The NAV upper value is very important to WiFi 11n 5.2.3 NAV test.
+ * The default value is always too small, but the WiFi TestPlan test
+ * by 25,000 microseconds of NAV through sending CTS in the air. We
+ * must update this value greater than 25,000 microseconds to pass the
+ * item.
+* The offset of NAV_UPPER in 8192C Spec is incorrect, and the offset
+* should be 0x0652. Commented by SD1 Scott. */
+/* By Bruce, 2011-07-18. */
+/* */
+#define REG_NAV_UPPER 0x0652 /* unit of 128 */
+
+
+/* */
+/* 8723 Regsiter Bit and Content definition */
+/* */
+
+/* */
+/* */
+/* 0x0000h ~ 0x00FFh System Configuration */
+/* */
+/* */
+
+/* 2 SPS0_CTRL */
+
+/* 2 SYS_ISO_CTRL */
+
+/* 2 SYS_FUNC_EN */
+
+/* 2 APS_FSMCO */
+#define EN_WLON BIT(16)
+
+/* 2 SYS_CLKR */
+
+/* 2 9346CR */
+
+/* 2 AFE_MISC */
+
+/* 2 SPS0_CTRL */
+
+/* 2 SPS_OCP_CFG */
+
+/* 2 SYSON_REG_LOCK */
+#define WLOCK_ALL BIT(0)
+#define WLOCK_00 BIT(1)
+#define WLOCK_04 BIT(2)
+#define WLOCK_08 BIT(3)
+#define WLOCK_40 BIT(4)
+#define WLOCK_1C_B6 BIT(5)
+#define R_DIS_PRST_1 BIT(6)
+#define LOCK_ALL_EN BIT(7)
+
+/* 2 RF_CTRL */
+
+/* 2 LDOA15_CTRL */
+
+/* 2 LDOV12D_CTRL */
+
+/* 2 AFE_XTAL_CTRL */
+
+/* 2 AFE_PLL_CTRL */
+
+/* 2 EFUSE_CTRL */
+
+/* 2 EFUSE_TEST (For RTL8723 partially) */
+
+/* 2 PWR_DATA */
+
+/* 2 CAL_TIMER */
+
+/* 2 ACLK_MON */
+
+/* 2 GPIO_MUXCFG */
+
+/* 2 GPIO_PIN_CTRL */
+
+/* 2 GPIO_INTM */
+
+/* 2 LEDCFG */
+
+/* 2 FSIMR */
+
+/* 2 FSISR */
+
+/* 2 HSIMR */
+/* 8723 Host System Interrupt Mask Register (offset 0x58, 32 byte) */
+#define HSIMR_GPIO12_0_INT_EN BIT(0)
+#define HSIMR_SPS_OCP_INT_EN BIT(5)
+#define HSIMR_RON_INT_EN BIT(6)
+#define HSIMR_PDNINT_EN BIT(7)
+#define HSIMR_GPIO9_INT_EN BIT(25)
+
+/* 2 HSISR */
+/* 8723 Host System Interrupt Status Register (offset 0x5C, 32 byte) */
+#define HSISR_GPIO12_0_INT BIT(0)
+#define HSISR_SPS_OCP_INT BIT(5)
+#define HSISR_RON_INT BIT(6)
+#define HSISR_PDNINT BIT(7)
+#define HSISR_GPIO9_INT BIT(25)
+
+/* interrupt mask which needs to clear */
+#define MASK_HSISR_CLEAR (HSISR_GPIO12_0_INT | \
+ HSISR_SPS_OCP_INT | \
+ HSISR_RON_INT | \
+ HSISR_PDNINT | \
+ HSISR_GPIO9_INT)
+
+/* 2 MCUFWDL */
+#define RAM_DL_SEL BIT(7) /* 1:RAM, 0:ROM */
+
+/* 2 HPON_FSM */
+
+/* 2 SYS_CFG */
+#define RTL_ID BIT(23) /* TestChip ID,
+ 1:Test(RLE); 0:MP(RL) */
+#define SPS_SEL BIT(24) /* 1:LDO regulator mode;
+ 0:Switching regulator mode*/
+
+
+/* */
+/* */
+/* 0x0100h ~ 0x01FFh MACTOP General Configuration */
+/* */
+/* */
+
+/* 2 Function Enable Registers */
+
+/* 2 CR */
+#define CALTMR_EN BIT(10)
+
+/* 2 PBP - Page Size Register */
+
+/* 2 TX/RXDMA */
+
+/* 2 TRXFF_BNDY */
+
+/* 2 LLT_INIT */
+
+/* 2 BB_ACCESS_CTRL */
+
+
+/* */
+/* */
+/* 0x0200h ~ 0x027Fh TXDMA Configuration */
+/* */
+/* */
+
+/* 2 RQPN */
+
+/* 2 TDECTRL */
+
+/* 2 TDECTL */
+
+/* 2 TXDMA_OFFSET_CHK */
+
+
+/* */
+/* */
+/* 0x0400h ~ 0x047Fh Protocol Configuration */
+/* */
+/* */
+
+/* 2 FWHW_TXQ_CTRL */
+
+/* 2 INIRTSMCS_SEL */
+
+/* 2 SPEC SIFS */
+
+/* 2 RRSR */
+
+/* 2 ARFR */
+
+/* 2 AGGLEN_LMT_L */
+
+/* 2 RL */
+
+/* 2 DARFRC */
+
+/* 2 RARFRC */
+
+
+/* */
+/* */
+/* 0x0500h ~ 0x05FFh EDCA Configuration */
+/* */
+/* */
+
+/* 2 EDCA setting */
+
+/* 2 EDCA_VO_PARAM */
+
+/* 2 SIFS_CCK */
+
+/* 2 SIFS_OFDM */
+
+/* 2 TBTT PROHIBIT */
+
+/* 2 REG_RD_CTRL */
+
+/* 2 BCN_CTRL */
+
+/* 2 ACMHWCTRL */
+
+
+/* */
+/* */
+/* 0x0600h ~ 0x07FFh WMAC Configuration */
+/* */
+/* */
+
+/* 2 APSD_CTRL */
+
+/* 2 BWOPMODE */
+
+/* 2 TCR */
+
+/* 2 RCR */
+
+/* 2 RX_PKT_LIMIT */
+
+/* 2 RX_DLK_TIME */
+
+/* 2 MBIDCAMCFG */
+
+/* 2 AMPDU_MIN_SPACE */
+
+/* 2 RXERR_RPT */
+
+/* 2 SECCFG */
+
+
+/* */
+/* */
+/* 0xFE00h ~ 0xFE55h RTL8723 SDIO Configuration */
+/* */
+/* */
+
+/* I/O bus domain address mapping */
+#define WLAN_IOREG_BASE 0x10260000
+#define FIRMWARE_FIFO_BASE 0x10270000
+#define TX_HIQ_BASE 0x10310000
+#define TX_MIQ_BASE 0x10320000
+#define TX_LOQ_BASE 0x10330000
+#define RX_RX0FF_BASE 0x10340000
+
+/* SDIO host local register space mapping. */
+#define WLAN_IOREG_MSK 0x7FFF
+#define WLAN_FIFO_MSK 0x1FFF /* Aggregation Length[12:0] */
+#define WLAN_RX0FF_MSK 0x0003
+
+#define WLAN_RX0FF_DEVICE_ID 7 /* 0b[16], 111b[15:13] */
+#define WLAN_IOREG_DEVICE_ID 8 /* 1b[16] */
+
+/* 8723 EFUSE */
+#define HWSET_MAX_SIZE 256
+
+
+/* USB interrupt */
+#define UHIMR_TIMEOUT2 BIT(31)
+#define UHIMR_TIMEOUT1 BIT(30)
+#define UHIMR_PSTIMEOUT BIT(29)
+#define UHIMR_GTINT4 BIT(28)
+#define UHIMR_GTINT3 BIT(27)
+#define UHIMR_TXBCNERR BIT(26)
+#define UHIMR_TXBCNOK BIT(25)
+#define UHIMR_TSF_BIT32_TOGGLE BIT(24)
+#define UHIMR_BCNDMAINT3 BIT(23)
+#define UHIMR_BCNDMAINT2 BIT(22)
+#define UHIMR_BCNDMAINT1 BIT(21)
+#define UHIMR_BCNDMAINT0 BIT(20)
+#define UHIMR_BCNDOK3 BIT(19)
+#define UHIMR_BCNDOK2 BIT(18)
+#define UHIMR_BCNDOK1 BIT(17)
+#define UHIMR_BCNDOK0 BIT(16)
+#define UHIMR_HSISR_IND BIT(15)
+#define UHIMR_BCNDMAINT_E BIT(14)
+/* RSVD BIT(13) */
+#define UHIMR_CTW_END BIT(12)
+/* RSVD BIT(11) */
+#define UHIMR_C2HCMD BIT(10)
+#define UHIMR_CPWM2 BIT(9)
+#define UHIMR_CPWM BIT(8)
+#define UHIMR_HIGHDOK BIT(7) /* High Queue DMA OK
+ Interrupt */
+#define UHIMR_MGNTDOK BIT(6) /* Management Queue DMA OK
+ Interrupt */
+#define UHIMR_BKDOK BIT(5) /* AC_BK DMA OK Interrupt */
+#define UHIMR_BEDOK BIT(4) /* AC_BE DMA OK Interrupt */
+#define UHIMR_VIDOK BIT(3) /* AC_VI DMA OK Interrupt */
+#define UHIMR_VODOK BIT(2) /* AC_VO DMA Interrupt */
+#define UHIMR_RDU BIT(1) /* Receive Descriptor
+ Unavailable */
+#define UHIMR_ROK BIT(0) /* Receive DMA OK Interrupt */
+
+/* USB Host Interrupt Status Extension bit */
+#define UHIMR_BCNDMAINT7 BIT(23)
+#define UHIMR_BCNDMAINT6 BIT(22)
+#define UHIMR_BCNDMAINT5 BIT(21)
+#define UHIMR_BCNDMAINT4 BIT(20)
+#define UHIMR_BCNDOK7 BIT(19)
+#define UHIMR_BCNDOK6 BIT(18)
+#define UHIMR_BCNDOK5 BIT(17)
+#define UHIMR_BCNDOK4 BIT(16)
+/* bit14-15: RSVD */
+#define UHIMR_ATIMEND_E BIT(13)
+#define UHIMR_ATIMEND BIT(12)
+#define UHIMR_TXERR BIT(11)
+#define UHIMR_RXERR BIT(10)
+#define UHIMR_TXFOVW BIT(9)
+#define UHIMR_RXFOVW BIT(8)
+/* bit2-7: RSVD */
+#define UHIMR_OCPINT BIT(1)
+/* bit0: RSVD */
+
+#define REG_USB_HIMR 0xFE38
+#define REG_USB_HIMRE 0xFE3C
+#define REG_USB_HISR 0xFE78
+#define REG_USB_HISRE 0xFE7C
+
+#define USB_INTR_CPWM_OFFSET 16
+#define USB_INTR_CONTENT_HISR_OFFSET 48
+#define USB_INTR_CONTENT_HISRE_OFFSET 52
+#define USB_INTR_CONTENT_LENGTH 56
+#define USB_C2H_CMDID_OFFSET 0
+#define USB_C2H_SEQ_OFFSET 1
+#define USB_C2H_EVENT_OFFSET 2
+/* */
+/* General definitions */
+/* */
+
+#endif
diff --git a/drivers/staging/rtl8723au/include/rtl8723a_sreset.h b/drivers/staging/rtl8723au/include/rtl8723a_sreset.h
new file mode 100644
index 000000000..6197910a4
--- /dev/null
+++ b/drivers/staging/rtl8723au/include/rtl8723a_sreset.h
@@ -0,0 +1,24 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2011 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * 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.
+ *
+ ******************************************************************************/
+#ifndef _RTL8723A_SRESET_H_
+#define _RTL8723A_SRESET_H_
+
+#include <osdep_service.h>
+#include <drv_types.h>
+#include <rtw_sreset.h>
+
+void rtl8723a_sreset_xmit_status_check(struct rtw_adapter *padapter);
+
+#endif
diff --git a/drivers/staging/rtl8723au/include/rtl8723a_xmit.h b/drivers/staging/rtl8723au/include/rtl8723a_xmit.h
new file mode 100644
index 000000000..7db29f40a
--- /dev/null
+++ b/drivers/staging/rtl8723au/include/rtl8723a_xmit.h
@@ -0,0 +1,225 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2011 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * 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.
+ *
+ ******************************************************************************/
+#ifndef __RTL8723A_XMIT_H__
+#define __RTL8723A_XMIT_H__
+
+/* */
+/* Queue Select Value in TxDesc */
+/* */
+#define QSLT_BK 0x2/* 0x01 */
+#define QSLT_BE 0x0
+#define QSLT_VI 0x5/* 0x4 */
+#define QSLT_VO 0x7/* 0x6 */
+#define QSLT_BEACON 0x10
+#define QSLT_HIGH 0x11
+#define QSLT_MGNT 0x12
+#define QSLT_CMD 0x13
+
+/* */
+/* defined for TX DESC Operation */
+/* */
+
+#define MAX_TID (15)
+
+/* OFFSET 0 */
+#define OFFSET_SZ 0
+#define OFFSET_SHT 16
+#define BMC BIT(24)
+#define LSG BIT(26)
+#define FSG BIT(27)
+#define OWN BIT(31)
+
+
+/* OFFSET 4 */
+#define PKT_OFFSET_SZ 0
+#define BK BIT(6)
+#define QSEL_SHT 8
+#define Rate_ID_SHT 16
+#define NAVUSEHDR BIT(20)
+#define PKT_OFFSET_SHT 26
+#define HWPC BIT(31)
+
+/* OFFSET 8 */
+#define AGG_EN BIT(29)
+
+/* OFFSET 12 */
+#define SEQ_SHT 16
+
+/* OFFSET 16 */
+#define QoS BIT(6)
+#define HW_SEQ_EN BIT(7)
+#define USERATE BIT(8)
+#define DISDATAFB BIT(10)
+#define DATA_SHORT BIT(24)
+#define DATA_BW BIT(25)
+
+/* OFFSET 20 */
+#define SGI BIT(6)
+
+struct txdesc_8723a {
+ u32 pktlen:16;
+ u32 offset:8;
+ u32 bmc:1;
+ u32 htc:1;
+ u32 ls:1;
+ u32 fs:1;
+ u32 linip:1;
+ u32 noacm:1;
+ u32 gf:1;
+ u32 own:1;
+
+ u32 macid:5;
+ u32 agg_en:1;
+ u32 bk:1;
+ u32 rd_en:1;
+ u32 qsel:5;
+ u32 rd_nav_ext:1;
+ u32 lsig_txop_en:1;
+ u32 pifs:1;
+ u32 rate_id:4;
+ u32 navusehdr:1;
+ u32 en_desc_id:1;
+ u32 sectype:2;
+ u32 rsvd0424:2;
+ u32 pkt_offset:5; /* unit: 8 bytes */
+ u32 rsvd0431:1;
+
+ u32 rts_rc:6;
+ u32 data_rc:6;
+ u32 rsvd0812:2;
+ u32 bar_rty_th:2;
+ u32 rsvd0816:1;
+ u32 morefrag:1;
+ u32 raw:1;
+ u32 ccx:1;
+ u32 ampdu_density:3;
+ u32 bt_null:1;
+ u32 ant_sel_a:1;
+ u32 ant_sel_b:1;
+ u32 tx_ant_cck:2;
+ u32 tx_antl:2;
+ u32 tx_ant_ht:2;
+
+ u32 nextheadpage:8;
+ u32 tailpage:8;
+ u32 seq:12;
+ u32 cpu_handle:1;
+ u32 tag1:1;
+ u32 trigger_int:1;
+ u32 hwseq_en:1;
+
+ u32 rtsrate:5;
+ u32 ap_dcfe:1;
+ u32 hwseq_sel:2;
+ u32 userate:1;
+ u32 disrtsfb:1;
+ u32 disdatafb:1;
+ u32 cts2self:1;
+ u32 rtsen:1;
+ u32 hw_rts_en:1;
+ u32 port_id:1;
+ u32 rsvd1615:3;
+ u32 wait_dcts:1;
+ u32 cts2ap_en:1;
+ u32 data_sc:2;
+ u32 data_stbc:2;
+ u32 data_short:1;
+ u32 data_bw:1;
+ u32 rts_short:1;
+ u32 rts_bw:1;
+ u32 rts_sc:2;
+ u32 vcs_stbc:2;
+
+ u32 datarate:6;
+ u32 sgi:1;
+ u32 try_rate:1;
+ u32 data_ratefb_lmt:5;
+ u32 rts_ratefb_lmt:4;
+ u32 rty_lmt_en:1;
+ u32 data_rt_lmt:6;
+ u32 usb_txagg_num:8;
+
+ u32 txagg_a:5;
+ u32 txagg_b:5;
+ u32 use_max_len:1;
+ u32 max_agg_num:5;
+ u32 mcsg1_max_len:4;
+ u32 mcsg2_max_len:4;
+ u32 mcsg3_max_len:4;
+ u32 mcs7_sgi_max_len:4;
+
+ u32 checksum:16; /* TxBuffSize(PCIe)/CheckSum(USB) */
+ u32 mcsg4_max_len:4;
+ u32 mcsg5_max_len:4;
+ u32 mcsg6_max_len:4;
+ u32 mcs15_sgi_max_len:4;
+};
+
+#define txdesc_set_ccx_sw_8723a(txdesc, value) \
+ do { \
+ ((struct txdesc_8723a *)(txdesc))->mcsg4_max_len = (((value)>>8) & 0x0f); \
+ ((struct txdesc_8723a *)(txdesc))->mcs15_sgi_max_len= (((value)>>4) & 0x0f); \
+ ((struct txdesc_8723a *)(txdesc))->mcsg6_max_len = ((value) & 0x0f); \
+ } while (0)
+
+struct txrpt_ccx_8723a {
+ /* offset 0 */
+ u8 tag1:1;
+ u8 rsvd:4;
+ u8 int_bt:1;
+ u8 int_tri:1;
+ u8 int_ccx:1;
+
+ /* offset 1 */
+ u8 mac_id:5;
+ u8 pkt_drop:1;
+ u8 pkt_ok:1;
+ u8 bmc:1;
+
+ /* offset 2 */
+ u8 retry_cnt:6;
+ u8 lifetime_over:1;
+ u8 retry_over:1;
+
+ /* offset 3 */
+ u8 ccx_qtime0;
+ u8 ccx_qtime1;
+
+ /* offset 5 */
+ u8 final_data_rate;
+
+ /* offset 6 */
+ u8 sw1:4;
+ u8 qsel:4;
+
+ /* offset 7 */
+ u8 sw0;
+};
+
+#define txrpt_ccx_sw_8723a(txrpt_ccx) ((txrpt_ccx)->sw0 + ((txrpt_ccx)->sw1<<8))
+#define txrpt_ccx_qtime_8723a(txrpt_ccx) ((txrpt_ccx)->ccx_qtime0+((txrpt_ccx)->ccx_qtime1<<8))
+
+void handle_txrpt_ccx_8723a(struct rtw_adapter *adapter, void *buf);
+void rtl8723a_fill_fake_txdesc(struct rtw_adapter *padapter, u8 *pDesc, u32 BufferLen, u8 IsPsPoll, u8 IsBTQosNull);
+
+int rtl8723au_hal_xmitframe_enqueue(struct rtw_adapter *padapter, struct xmit_frame *pxmitframe);
+s32 rtl8723au_xmit_buf_handler(struct rtw_adapter *padapter);
+#define hal_xmit_handler rtl8723au_xmit_buf_handler
+bool rtl8723au_hal_xmit(struct rtw_adapter *padapter, struct xmit_frame *pxmitframe);
+int rtl8723au_mgnt_xmit(struct rtw_adapter *padapter, struct xmit_frame *pmgntframe);
+bool rtl8723au_xmitframe_complete(struct rtw_adapter *padapter, struct xmit_priv *pxmitpriv, struct xmit_buf *pxmitbuf);
+
+
+#endif
diff --git a/drivers/staging/rtl8723au/include/rtw_ap.h b/drivers/staging/rtl8723au/include/rtw_ap.h
new file mode 100644
index 000000000..9f8d235c9
--- /dev/null
+++ b/drivers/staging/rtl8723au/include/rtw_ap.h
@@ -0,0 +1,54 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2012 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * 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.
+ *
+ ******************************************************************************/
+#ifndef __RTW_AP_H_
+#define __RTW_AP_H_
+
+#include <osdep_service.h>
+#include <drv_types.h>
+
+
+#ifdef CONFIG_8723AU_AP_MODE
+
+/* external function */
+
+void init_mlme_ap_info23a(struct rtw_adapter *padapter);
+void free_mlme_ap_info23a(struct rtw_adapter *padapter);
+/* void update_BCNTIM(struct rtw_adapter *padapter); */
+void rtw_add_bcn_ie(struct rtw_adapter *padapter, struct wlan_bssid_ex *pnetwork, u8 index, u8 *data, u8 len);
+void rtw_remove_bcn_ie(struct rtw_adapter *padapter, struct wlan_bssid_ex *pnetwork, u8 index);
+void update_beacon23a(struct rtw_adapter *padapter, u8 ie_id, u8 *oui, u8 tx);
+void add_RATid23a(struct rtw_adapter *padapter, struct sta_info *psta, u8 rssi_level);
+void expire_timeout_chk23a(struct rtw_adapter *padapter);
+void update_sta_info23a_apmode23a(struct rtw_adapter *padapter, struct sta_info *psta);
+int rtw_check_beacon_data23a(struct rtw_adapter *padapter,
+ struct ieee80211_mgmt *mgmt, unsigned int len);
+void rtw_ap_restore_network(struct rtw_adapter *padapter);
+void rtw_set_macaddr_acl23a(struct rtw_adapter *padapter, int mode);
+int rtw_acl_add_sta23a(struct rtw_adapter *padapter, u8 *addr);
+int rtw_acl_remove_sta23a(struct rtw_adapter *padapter, u8 *addr);
+
+void associated_clients_update23a(struct rtw_adapter *padapter, u8 updated);
+void bss_cap_update_on_sta_join23a(struct rtw_adapter *padapter, struct sta_info *psta);
+u8 bss_cap_update_on_sta_leave23a(struct rtw_adapter *padapter, struct sta_info *psta);
+void sta_info_update23a(struct rtw_adapter *padapter, struct sta_info *psta);
+void ap_sta_info_defer_update23a(struct rtw_adapter *padapter, struct sta_info *psta);
+u8 ap_free_sta23a(struct rtw_adapter *padapter, struct sta_info *psta, bool active, u16 reason);
+int rtw_sta_flush23a(struct rtw_adapter *padapter);
+int rtw_ap_inform_ch_switch23a(struct rtw_adapter *padapter, u8 new_ch, u8 ch_offset);
+void start_ap_mode23a(struct rtw_adapter *padapter);
+void stop_ap_mode23a(struct rtw_adapter *padapter);
+#endif /* end of CONFIG_8723AU_AP_MODE */
+
+#endif
diff --git a/drivers/staging/rtl8723au/include/rtw_cmd.h b/drivers/staging/rtl8723au/include/rtw_cmd.h
new file mode 100644
index 000000000..775dcdc1e
--- /dev/null
+++ b/drivers/staging/rtl8723au/include/rtw_cmd.h
@@ -0,0 +1,817 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2011 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * 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.
+ *
+ ******************************************************************************/
+#ifndef __RTW_CMD_H_
+#define __RTW_CMD_H_
+
+#include <wlan_bssdef.h>
+#include <rtw_rf.h>
+
+#define C2H_MEM_SZ (16*1024)
+
+#include <osdep_service.h>
+#include <ieee80211.h> /* <ieee80211/ieee80211.h> */
+
+
+#define MAX_CMDSZ 1024
+#define MAX_RSPSZ 512
+#define MAX_EVTSZ 1024
+
+#define CMDBUFF_ALIGN_SZ 512
+
+struct cmd_obj {
+ struct work_struct work;
+ struct rtw_adapter *padapter;
+ u16 cmdcode;
+ int res;
+ u32 cmdsz;
+ u8 *parmbuf;
+ u8 *rsp;
+ u32 rspsz;
+};
+
+struct cmd_priv {
+ struct workqueue_struct *wq;
+ u32 cmd_issued_cnt;
+ u32 cmd_done_cnt;
+ u32 rsp_cnt;
+ struct rtw_adapter *padapter;
+};
+
+#define C2H_QUEUE_MAX_LEN 10
+
+struct evt_priv {
+ struct workqueue_struct *wq;
+ struct work_struct irq_wk;
+};
+
+#define init_h2fwcmd_w_parm_no_rsp(pcmd, pparm, code) \
+do {\
+ pcmd->cmdcode = code;\
+ pcmd->parmbuf = (u8 *)(pparm);\
+ pcmd->cmdsz = sizeof (*pparm);\
+ pcmd->rsp = NULL;\
+ pcmd->rspsz = 0;\
+} while(0)
+
+struct c2h_evt_hdr {
+ u8 id:4;
+ u8 plen:4;
+ u8 seq;
+ u8 payload[0];
+};
+
+/*
+ * Do not reorder - this allows for struct evt_work to be passed on to
+ * rtw_c2h_wk_cmd23a() as a 'struct c2h_evt_hdr *' without making an
+ * additional copy.
+ */
+struct evt_work {
+ union {
+ struct c2h_evt_hdr c2h_evt;
+ u8 buf[16];
+ } u;
+ struct work_struct work;
+ struct rtw_adapter *adapter;
+};
+
+#define c2h_evt_exist(c2h_evt) ((c2h_evt)->id || (c2h_evt)->plen)
+
+void rtw_evt_work(struct work_struct *work);
+
+int rtw_enqueue_cmd23a(struct cmd_priv *pcmdpriv, struct cmd_obj *obj);
+void rtw_free_cmd_obj23a(struct cmd_obj *pcmd);
+
+int rtw_cmd_thread23a(void *context);
+
+int rtw_init_cmd_priv23a(struct cmd_priv *pcmdpriv);
+
+u32 rtw_init_evt_priv23a (struct evt_priv *pevtpriv);
+void rtw_free_evt_priv23a (struct evt_priv *pevtpriv);
+void rtw_evt_notify_isr(struct evt_priv *pevtpriv);
+
+enum rtw_drvextra_cmd_id
+{
+ NONE_WK_CID,
+ DYNAMIC_CHK_WK_CID,
+ DM_CTRL_WK_CID,
+ PBC_POLLING_WK_CID,
+ POWER_SAVING_CTRL_WK_CID,/* IPS,AUTOSuspend */
+ LPS_CTRL_WK_CID,
+ ANT_SELECT_WK_CID,
+ P2P_PS_WK_CID,
+ P2P_PROTO_WK_CID,
+ CHECK_HIQ_WK_CID,/* for softap mode, check hi queue if empty */
+ C2H_WK_CID,
+ RTP_TIMER_CFG_WK_CID,
+ MAX_WK_CID
+};
+
+enum LPS_CTRL_TYPE
+{
+ LPS_CTRL_SCAN=0,
+ LPS_CTRL_JOINBSS=1,
+ LPS_CTRL_CONNECT=2,
+ LPS_CTRL_DISCONNECT=3,
+ LPS_CTRL_SPECIAL_PACKET=4,
+ LPS_CTRL_LEAVE=5,
+};
+
+enum RFINTFS {
+ SWSI,
+ HWSI,
+ HWPI,
+};
+
+/*
+Caller Mode: Infra, Ad-HoC(C)
+
+Notes: To enter USB suspend mode
+
+Command Mode
+
+*/
+struct usb_suspend_parm {
+ u32 action;/* 1: sleep, 0:resume */
+};
+
+/*
+Caller Mode: Infra, Ad-HoC
+
+Notes: To join a known BSS.
+
+Command-Event Mode
+
+*/
+
+/*
+Caller Mode: Infra, Ad-HoC(C)
+
+Notes: To disconnect the current associated BSS
+
+Command Mode
+
+*/
+struct disconnect_parm {
+ u32 deauth_timeout_ms;
+};
+
+struct setopmode_parm {
+ enum nl80211_iftype mode;
+};
+
+/*
+Caller Mode: AP, Ad-HoC, Infra
+
+Notes: To ask RTL8711 performing site-survey
+
+Command-Event Mode
+
+*/
+
+#define RTW_SSID_SCAN_AMOUNT 9 /* for WEXT_CSCAN_AMOUNT 9 */
+#define RTW_CHANNEL_SCAN_AMOUNT (14+37)
+struct sitesurvey_parm {
+ int scan_mode; /* active: 1, passive: 0 */
+ u8 ssid_num;
+ u8 ch_num;
+ struct cfg80211_ssid ssid[RTW_SSID_SCAN_AMOUNT];
+ struct rtw_ieee80211_channel ch[RTW_CHANNEL_SCAN_AMOUNT];
+};
+
+/*
+Caller Mode: Any
+
+Notes: To set the auth type of RTL8711. open/shared/802.1x
+
+Command Mode
+
+*/
+struct setauth_parm {
+ u8 mode; /* 0: legacy open, 1: legacy shared 2: 802.1x */
+ u8 _1x; /* 0: PSK, 1: TLS */
+ u8 rsvd[2];
+};
+
+/*
+Caller Mode: Infra
+
+a. algorithm: wep40, wep104, tkip & aes
+b. keytype: grp key/unicast key
+c. key contents
+
+when shared key ==> keyid is the camid
+when 802.1x ==> keyid [0:1] ==> grp key
+when 802.1x ==> keyid > 2 ==> unicast key
+
+*/
+struct setkey_parm {
+ u32 algorithm; /* encryption algorithm, could be none, wep40, TKIP, CCMP, wep104 */
+ u8 keyid;
+ u8 grpkey; /* 1: this is the grpkey for 802.1x. 0: this is the unicast key for 802.1x */
+ u8 set_tx; /* 1: main tx key for wep. 0: other key. */
+ u8 key[16]; /* this could be 40 or 104 */
+};
+
+/*
+When in AP or Ad-Hoc mode, this is used to
+allocate an sw/hw entry for a newly associated sta.
+
+Command
+
+when shared key ==> algorithm/keyid
+
+*/
+struct set_stakey_parm {
+ u8 addr[ETH_ALEN];
+ u8 id;/* currently for erasing cam entry if algorithm == _NO_PRIVACY_ */
+ u32 algorithm;
+ u8 key[16];
+};
+
+struct set_stakey_rsp {
+ u8 addr[ETH_ALEN];
+ u8 keyid;
+ u8 rsvd;
+};
+
+/*
+Caller Ad-Hoc/AP
+
+Command -Rsp(AID == CAMID) mode
+
+This is to force fw to add an sta_data entry per driver's request.
+
+FW will write an cam entry associated with it.
+
+*/
+struct set_assocsta_parm {
+ u8 addr[ETH_ALEN];
+};
+
+struct set_assocsta_rsp {
+ u8 cam_id;
+ u8 rsvd[3];
+};
+
+/*
+ Caller Ad-Hoc/AP
+
+ Command mode
+
+ This is to force fw to del an sta_data entry per driver's request
+
+ FW will invalidate the cam entry associated with it.
+
+*/
+struct del_assocsta_parm {
+ u8 addr[ETH_ALEN];
+};
+
+/*
+Caller Mode: AP/Ad-HoC(M)
+
+Notes: To notify fw that given staid has changed its power state
+
+Command Mode
+
+*/
+struct setstapwrstate_parm {
+ u8 staid;
+ u8 status;
+ u8 hwaddr[6];
+};
+
+/*
+Caller Mode: Any
+
+Notes: To setup the basic rate of RTL8711
+
+Command Mode
+
+*/
+struct setbasicrate_parm {
+ u8 basicrates[NumRates];
+};
+
+/*
+Caller Mode: Any
+
+Notes: To read the current basic rate
+
+Command-Rsp Mode
+
+*/
+struct getbasicrate_parm {
+ u32 rsvd;
+};
+
+struct getbasicrate_rsp {
+ u8 basicrates[NumRates];
+};
+
+/*
+Caller Mode: Any
+
+Notes: To setup the data rate of RTL8711
+
+Command Mode
+
+*/
+struct setdatarate_parm {
+ u8 mac_id;
+ u8 datarates[NumRates];
+};
+
+/*
+Caller Mode: Any
+
+Notes: To read the current data rate
+
+Command-Rsp Mode
+
+*/
+struct getdatarate_parm {
+ u32 rsvd;
+};
+
+struct getdatarate_rsp {
+ u8 datarates[NumRates];
+};
+
+
+/*
+Caller Mode: Any
+AP: AP can use the info for the contents of beacon frame
+Infra: STA can use the info when sitesurveying
+Ad-HoC(M): Like AP
+Ad-HoC(C): Like STA
+
+
+Notes: To set the phy capability of the NIC
+
+Command Mode
+
+*/
+
+struct setphyinfo_parm {
+ struct regulatory_class class_sets[NUM_REGULATORYS];
+ u8 status;
+};
+
+struct getphyinfo_parm {
+ u32 rsvd;
+};
+
+struct getphyinfo_rsp {
+ struct regulatory_class class_sets[NUM_REGULATORYS];
+ u8 status;
+};
+
+/*
+Caller Mode: Any
+
+Notes: To set the channel/modem/band
+This command will be used when channel/modem/band is changed.
+
+Command Mode
+
+*/
+struct setphy_parm {
+ u8 rfchannel;
+ u8 modem;
+};
+
+/*
+Caller Mode: Any
+
+Notes: To get the current setting of channel/modem/band
+
+Command-Rsp Mode
+
+*/
+struct getphy_parm {
+ u32 rsvd;
+};
+
+struct getphy_rsp {
+ u8 rfchannel;
+ u8 modem;
+};
+
+struct readBB_parm {
+ u8 offset;
+};
+
+struct readBB_rsp {
+ u8 value;
+};
+
+struct readTSSI_parm {
+ u8 offset;
+};
+
+struct readTSSI_rsp {
+ u8 value;
+};
+
+struct writeBB_parm {
+ u8 offset;
+ u8 value;
+};
+
+struct readRF_parm {
+ u8 offset;
+};
+
+struct readRF_rsp {
+ u32 value;
+};
+
+struct writeRF_parm {
+ u32 offset;
+ u32 value;
+};
+
+struct getrfintfs_parm {
+ u8 rfintfs;
+};
+
+struct Tx_Beacon_param {
+ struct wlan_bssid_ex network;
+};
+
+/* CMD param Formart for driver extra cmd handler */
+struct drvextra_cmd_parm {
+ int ec_id; /* extra cmd id */
+ int type_size; /* Can use this field as the type id or command size */
+ unsigned char *pbuf;
+};
+
+/*------------------- Below are used for RF/BB tunning ---------------------*/
+
+struct setantenna_parm {
+ u8 tx_antset;
+ u8 rx_antset;
+ u8 tx_antenna;
+ u8 rx_antenna;
+};
+
+struct enrateadaptive_parm {
+ u32 en;
+};
+
+struct settxagctbl_parm {
+ u32 txagc[MAX_RATES_LENGTH];
+};
+
+struct gettxagctbl_parm {
+ u32 rsvd;
+};
+
+struct gettxagctbl_rsp {
+ u32 txagc[MAX_RATES_LENGTH];
+};
+
+struct setagcctrl_parm {
+ u32 agcctrl; /* 0: pure hw, 1: fw */
+};
+
+struct setssup_parm {
+ u32 ss_ForceUp[MAX_RATES_LENGTH];
+};
+
+struct getssup_parm {
+ u32 rsvd;
+};
+
+struct getssup_rsp {
+ u8 ss_ForceUp[MAX_RATES_LENGTH];
+};
+
+struct setssdlevel_parm {
+ u8 ss_DLevel[MAX_RATES_LENGTH];
+};
+
+struct getssdlevel_parm {
+ u32 rsvd;
+};
+
+struct getssdlevel_rsp {
+ u8 ss_DLevel[MAX_RATES_LENGTH];
+};
+
+struct setssulevel_parm {
+ u8 ss_ULevel[MAX_RATES_LENGTH];
+};
+
+struct getssulevel_parm {
+ u32 rsvd;
+};
+
+struct getssulevel_rsp {
+ u8 ss_ULevel[MAX_RATES_LENGTH];
+};
+
+struct setcountjudge_parm {
+ u8 count_judge[MAX_RATES_LENGTH];
+};
+
+struct getcountjudge_parm {
+ u32 rsvd;
+};
+
+struct getcountjudge_rsp {
+ u8 count_judge[MAX_RATES_LENGTH];
+};
+
+struct setratable_parm {
+ u8 ss_ForceUp[NumRates];
+ u8 ss_ULevel[NumRates];
+ u8 ss_DLevel[NumRates];
+ u8 count_judge[NumRates];
+};
+
+struct getratable_parm {
+ uint rsvd;
+};
+
+struct getratable_rsp {
+ u8 ss_ForceUp[NumRates];
+ u8 ss_ULevel[NumRates];
+ u8 ss_DLevel[NumRates];
+ u8 count_judge[NumRates];
+};
+
+/* to get TX,RX retry count */
+struct gettxretrycnt_parm{
+ unsigned int rsvd;
+};
+struct gettxretrycnt_rsp{
+ unsigned long tx_retrycnt;
+};
+
+struct getrxretrycnt_parm{
+ unsigned int rsvd;
+};
+struct getrxretrycnt_rsp{
+ unsigned long rx_retrycnt;
+};
+
+/* to get BCNOK,BCNERR count */
+struct getbcnokcnt_parm{
+ unsigned int rsvd;
+};
+struct getbcnokcnt_rsp{
+ unsigned long bcnokcnt;
+};
+
+struct getbcnerrcnt_parm{
+ unsigned int rsvd;
+};
+struct getbcnerrcnt_rsp{
+ unsigned long bcnerrcnt;
+};
+
+/* to get current TX power level */
+struct getcurtxpwrlevel_parm{
+ unsigned int rsvd;
+};
+
+struct getcurtxpwrlevel_rsp{
+ unsigned short tx_power;
+};
+
+struct setprobereqextraie_parm {
+ unsigned char e_id;
+ unsigned char ie_len;
+ unsigned char ie[0];
+};
+
+struct setassocreqextraie_parm {
+ unsigned char e_id;
+ unsigned char ie_len;
+ unsigned char ie[0];
+};
+
+struct setproberspextraie_parm {
+ unsigned char e_id;
+ unsigned char ie_len;
+ unsigned char ie[0];
+};
+
+struct setassocrspextraie_parm {
+ unsigned char e_id;
+ unsigned char ie_len;
+ unsigned char ie[0];
+};
+
+struct addBaReq_parm {
+ unsigned int tid;
+ u8 addr[ETH_ALEN];
+};
+
+/*H2C Handler index: 46 */
+struct set_ch_parm {
+ u8 ch;
+ u8 bw;
+ u8 ch_offset;
+};
+
+/*H2C Handler index: 59 */
+struct SetChannelPlan_param {
+ u8 channel_plan;
+};
+
+/*H2C Handler index: 60 */
+struct LedBlink_param {
+ struct led_8723a *pLed;
+};
+
+/*H2C Handler index: 61 */
+struct SetChannelSwitch_param {
+ u8 new_ch_no;
+};
+
+/*H2C Handler index: 62 */
+struct TDLSoption_param {
+ u8 addr[ETH_ALEN];
+ u8 option;
+};
+
+#define GEN_CMD_CODE(cmd) cmd ## _CMD_
+
+
+/*
+
+Result:
+0x00: success
+0x01: sucess, and check Response.
+0x02: cmd ignored due to duplicated sequcne number
+0x03: cmd dropped due to invalid cmd code
+0x04: reserved.
+
+*/
+
+#define H2C_RSP_OFFSET 512
+
+#define H2C_SUCCESS 0x00
+#define H2C_SUCCESS_RSP 0x01
+#define H2C_DUPLICATED 0x02
+#define H2C_DROPPED 0x03
+#define H2C_PARAMETERS_ERROR 0x04
+#define H2C_REJECTED 0x05
+#define H2C_CMD_OVERFLOW 0x06
+#define H2C_RESERVED 0x07
+
+int rtw_setassocsta_cmd(struct rtw_adapter *padapter, u8 *mac_addr);
+int rtw_setstandby_cmd(struct rtw_adapter *padapter, uint action);
+int rtw_sitesurvey_cmd23a(struct rtw_adapter *padapter, struct cfg80211_ssid *ssid, int ssid_num, struct rtw_ieee80211_channel *ch, int ch_num);
+int rtw_createbss_cmd23a(struct rtw_adapter *padapter);
+int rtw_createbss_cmd23a_ex(struct rtw_adapter *padapter, unsigned char *pbss, unsigned int sz);
+int rtw_setphy_cmd(struct rtw_adapter *padapter, u8 modem, u8 ch);
+int rtw_setstakey_cmd23a(struct rtw_adapter *padapter, u8 *psta, u8 unicast_key);
+int rtw_clearstakey_cmd23a(struct rtw_adapter *padapter, u8 *psta, u8 entry, u8 enqueue);
+int rtw_joinbss_cmd23a(struct rtw_adapter *padapter, struct wlan_network* pnetwork);
+int rtw_disassoc_cmd23a(struct rtw_adapter *padapter, u32 deauth_timeout_ms, bool enqueue);
+int rtw_setopmode_cmd23a(struct rtw_adapter *padapter, enum nl80211_iftype ifmode);
+int rtw_setdatarate_cmd(struct rtw_adapter *padapter, u8 *rateset);
+int rtw_setbasicrate_cmd(struct rtw_adapter *padapter, u8 *rateset);
+int rtw_setbbreg_cmd(struct rtw_adapter *padapter, u8 offset, u8 val);
+int rtw_setrfreg_cmd(struct rtw_adapter *padapter, u8 offset, u32 val);
+int rtw_getbbreg_cmd(struct rtw_adapter *padapter, u8 offset, u8 *pval);
+int rtw_getrfreg_cmd(struct rtw_adapter *padapter, u8 offset, u8 *pval);
+int rtw_setrfintfs_cmd(struct rtw_adapter *padapter, u8 mode);
+int rtw_setrttbl_cmd(struct rtw_adapter *padapter, struct setratable_parm *prate_table);
+int rtw_getrttbl_cmd(struct rtw_adapter *padapter, struct getratable_rsp *pval);
+
+int rtw_gettssi_cmd(struct rtw_adapter *padapter, u8 offset, u8 *pval);
+int rtw_setfwdig_cmd(struct rtw_adapter*padapter, u8 type);
+int rtw_setfwra_cmd(struct rtw_adapter*padapter, u8 type);
+
+int rtw_addbareq_cmd23a(struct rtw_adapter*padapter, u8 tid, u8 *addr);
+
+int rtw_dynamic_chk_wk_cmd23a(struct rtw_adapter *adapter);
+
+int rtw_lps_ctrl_wk_cmd23a(struct rtw_adapter*padapter, u8 lps_ctrl_type, u8 enqueue);
+
+int rtw_ps_cmd23a(struct rtw_adapter*padapter);
+
+#ifdef CONFIG_8723AU_AP_MODE
+int rtw_chk_hi_queue_cmd23a(struct rtw_adapter*padapter);
+#endif
+
+int rtw_set_chplan_cmd(struct rtw_adapter*padapter, u8 chplan, u8 enqueue);
+int rtw_led_blink_cmd(struct rtw_adapter*padapter, struct led_8723a *pLed);
+int rtw_set_csa_cmd(struct rtw_adapter*padapter, u8 new_ch_no);
+
+int rtw_c2h_wk_cmd23a(struct rtw_adapter *padapter, u8 *c2h_evt);
+
+int rtw_drvextra_cmd_hdl23a(struct rtw_adapter *padapter, const u8 *pbuf);
+
+void rtw_survey_cmd_callback23a(struct rtw_adapter *padapter, struct cmd_obj *pcmd);
+void rtw_disassoc_cmd23a_callback(struct rtw_adapter *padapter, struct cmd_obj *pcmd);
+void rtw_joinbss_cmd23a_callback(struct rtw_adapter *padapter, struct cmd_obj *pcmd);
+void rtw_createbss_cmd23a_callback(struct rtw_adapter *padapter, struct cmd_obj *pcmd);
+void rtw_getbbrfreg_cmdrsp_callback23a(struct rtw_adapter *padapter, struct cmd_obj *pcmd);
+void rtw_readtssi_cmdrsp_callback(struct rtw_adapter* padapter, struct cmd_obj *pcmd);
+
+void rtw_setstaKey_cmdrsp_callback23a(struct rtw_adapter *padapter, struct cmd_obj *pcmd);
+void rtw_setassocsta_cmdrsp_callback23a(struct rtw_adapter *padapter, struct cmd_obj *pcmd);
+void rtw_getrttbl_cmdrsp_callback(struct rtw_adapter *padapter, struct cmd_obj *pcmd);
+
+struct _cmd_callback {
+ u32 cmd_code;
+ void (*callback)(struct rtw_adapter *padapter, struct cmd_obj *cmd);
+};
+
+enum rtw_h2c_cmd {
+ GEN_CMD_CODE(_Read_MACREG) , /*0*/
+ GEN_CMD_CODE(_Write_MACREG) ,
+ GEN_CMD_CODE(_Read_BBREG) ,
+ GEN_CMD_CODE(_Write_BBREG) ,
+ GEN_CMD_CODE(_Read_RFREG) ,
+ GEN_CMD_CODE(_Write_RFREG) , /*5*/
+ GEN_CMD_CODE(_Read_EEPROM) ,
+ GEN_CMD_CODE(_Write_EEPROM) ,
+ GEN_CMD_CODE(_Read_EFUSE) ,
+ GEN_CMD_CODE(_Write_EFUSE) ,
+
+ GEN_CMD_CODE(_Read_CAM) , /*10*/
+ GEN_CMD_CODE(_Write_CAM) ,
+ GEN_CMD_CODE(_setBCNITV),
+ GEN_CMD_CODE(_setMBIDCFG),
+ GEN_CMD_CODE(_JoinBss), /*14*/
+ GEN_CMD_CODE(_DisConnect) , /*15*/
+ GEN_CMD_CODE(_CreateBss) ,
+ GEN_CMD_CODE(_SetOpMode) ,
+ GEN_CMD_CODE(_SiteSurvey), /*18*/
+ GEN_CMD_CODE(_SetAuth) ,
+
+ GEN_CMD_CODE(_SetKey) , /*20*/
+ GEN_CMD_CODE(_SetStaKey) ,
+ GEN_CMD_CODE(_SetAssocSta) ,
+ GEN_CMD_CODE(_DelAssocSta) ,
+ GEN_CMD_CODE(_SetStaPwrState) ,
+ GEN_CMD_CODE(_SetBasicRate) , /*25*/
+ GEN_CMD_CODE(_GetBasicRate) ,
+ GEN_CMD_CODE(_SetDataRate) ,
+ GEN_CMD_CODE(_GetDataRate) ,
+ GEN_CMD_CODE(_SetPhyInfo) ,
+
+ GEN_CMD_CODE(_GetPhyInfo) , /*30*/
+ GEN_CMD_CODE(_SetPhy) ,
+ GEN_CMD_CODE(_GetPhy) ,
+ GEN_CMD_CODE(_readRssi) ,
+ GEN_CMD_CODE(_readGain) ,
+ GEN_CMD_CODE(_SetAtim) , /*35*/
+ GEN_CMD_CODE(_SetPwrMode) ,
+ GEN_CMD_CODE(_JoinbssRpt),
+ GEN_CMD_CODE(_SetRaTable) ,
+ GEN_CMD_CODE(_GetRaTable) ,
+
+ GEN_CMD_CODE(_GetCCXReport), /*40*/
+ GEN_CMD_CODE(_GetDTMReport),
+ GEN_CMD_CODE(_GetTXRateStatistics),
+ GEN_CMD_CODE(_SetUsbSuspend),
+ GEN_CMD_CODE(_SetH2cLbk),
+ GEN_CMD_CODE(_AddBAReq) , /*45*/
+ GEN_CMD_CODE(_SetChannel), /*46*/
+ GEN_CMD_CODE(_SetTxPower),
+ GEN_CMD_CODE(_SwitchAntenna),
+ GEN_CMD_CODE(_SetCrystalCap),
+ GEN_CMD_CODE(_SetSingleCarrierTx), /*50*/
+
+ GEN_CMD_CODE(_SetSingleToneTx),/*51*/
+ GEN_CMD_CODE(_SetCarrierSuppressionTx),
+ GEN_CMD_CODE(_SetContinuousTx),
+ GEN_CMD_CODE(_SwitchBandwidth), /*54*/
+ GEN_CMD_CODE(_TX_Beacon), /*55*/
+
+ GEN_CMD_CODE(_Set_MLME_EVT), /*56*/
+ GEN_CMD_CODE(_Set_Drv_Extra), /*57*/
+ GEN_CMD_CODE(_Set_H2C_MSG), /*58*/
+
+ GEN_CMD_CODE(_SetChannelPlan), /*59*/
+ GEN_CMD_CODE(_LedBlink), /*60*/
+
+ GEN_CMD_CODE(_SetChannelSwitch), /*61*/
+ GEN_CMD_CODE(_TDLS), /*62*/
+
+ MAX_H2CCMD
+};
+
+extern struct _cmd_callback rtw_cmd_callback[];
+
+#endif /* _CMD_H_ */
diff --git a/drivers/staging/rtl8723au/include/rtw_debug.h b/drivers/staging/rtl8723au/include/rtw_debug.h
new file mode 100644
index 000000000..159183e9c
--- /dev/null
+++ b/drivers/staging/rtl8723au/include/rtw_debug.h
@@ -0,0 +1,191 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2011 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * 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.
+ *
+ *
+ ******************************************************************************/
+#ifndef __RTW_DEBUG_H__
+#define __RTW_DEBUG_H__
+
+#include <osdep_service.h>
+#include <drv_types.h>
+
+#define _drv_always_ 1
+#define _drv_emerg_ 2
+#define _drv_alert_ 3
+#define _drv_err_ 4
+#define _drv_warning_ 5
+#define _drv_notice_ 6
+#define _drv_info_ 7
+#define _drv_debug_ 8
+
+#define _module_rtl871x_xmit_c_ BIT(0)
+#define _module_xmit_osdep_c_ BIT(1)
+#define _module_rtl871x_recv_c_ BIT(2)
+#define _module_recv_osdep_c_ BIT(3)
+#define _module_rtl871x_mlme_c_ BIT(4)
+#define _module_mlme_osdep_c_ BIT(5)
+#define _module_rtl871x_sta_mgt_c_ BIT(6)
+#define _module_rtl871x_cmd_c_ BIT(7)
+#define _module_cmd_osdep_c_ BIT(8)
+#define _module_rtl871x_io_c_ BIT(9)
+#define _module_io_osdep_c_ BIT(10)
+#define _module_os_intfs_c_ BIT(11)
+#define _module_rtl871x_security_c_ BIT(12)
+#define _module_rtl871x_eeprom_c_ BIT(13)
+#define _module_hal_init_c_ BIT(14)
+#define _module_hci_hal_init_c_ BIT(15)
+#define _module_rtl871x_ioctl_c_ BIT(16)
+#define _module_rtl871x_ioctl_set_c_ BIT(17)
+#define _module_rtl871x_ioctl_query_c_ BIT(18)
+#define _module_rtl871x_pwrctrl_c_ BIT(19)
+#define _module_hci_intfs_c_ BIT(20)
+#define _module_hci_ops_c_ BIT(21)
+#define _module_osdep_service_c_ BIT(22)
+#define _module_mp_ BIT(23)
+#define _module_hci_ops_os_c_ BIT(24)
+#define _module_rtl871x_ioctl_os_c BIT(25)
+#define _module_rtl8712_cmd_c_ BIT(26)
+#define _module_rtl8192c_xmit_c_ BIT(28)
+#define _module_hal_xmit_c_ BIT(28) /* duplication intentional */
+#define _module_efuse_ BIT(29)
+#define _module_rtl8712_recv_c_ BIT(30)
+#define _module_rtl8712_led_c_ BIT(31)
+
+#undef _MODULE_DEFINE_
+
+#if defined _RTW_XMIT_C_
+ #define _MODULE_DEFINE_ _module_rtl871x_xmit_c_
+#elif defined _XMIT_OSDEP_C_
+ #define _MODULE_DEFINE_ _module_xmit_osdep_c_
+#elif defined _RTW_RECV_C_
+ #define _MODULE_DEFINE_ _module_rtl871x_recv_c_
+#elif defined _RECV_OSDEP_C_
+ #define _MODULE_DEFINE_ _module_recv_osdep_c_
+#elif defined _RTW_MLME_C_
+ #define _MODULE_DEFINE_ _module_rtl871x_mlme_c_
+#elif defined _MLME_OSDEP_C_
+ #define _MODULE_DEFINE_ _module_mlme_osdep_c_
+#elif defined _RTW_MLME_EXT_C_
+ #define _MODULE_DEFINE_ 1
+#elif defined _RTW_STA_MGT_C_
+ #define _MODULE_DEFINE_ _module_rtl871x_sta_mgt_c_
+#elif defined _RTW_CMD_C_
+ #define _MODULE_DEFINE_ _module_rtl871x_cmd_c_
+#elif defined _CMD_OSDEP_C_
+ #define _MODULE_DEFINE_ _module_cmd_osdep_c_
+#elif defined _RTW_IO_C_
+ #define _MODULE_DEFINE_ _module_rtl871x_io_c_
+#elif defined _IO_OSDEP_C_
+ #define _MODULE_DEFINE_ _module_io_osdep_c_
+#elif defined _OS_INTFS_C_
+ #define _MODULE_DEFINE_ _module_os_intfs_c_
+#elif defined _RTW_SECURITY_C_
+ #define _MODULE_DEFINE_ _module_rtl871x_security_c_
+#elif defined _RTW_EEPROM_C_
+ #define _MODULE_DEFINE_ _module_rtl871x_eeprom_c_
+#elif defined _HAL_INTF_C_
+ #define _MODULE_DEFINE_ _module_hal_init_c_
+#elif (defined _HCI_HAL_INIT_C_) || (defined _SDIO_HALINIT_C_)
+ #define _MODULE_DEFINE_ _module_hci_hal_init_c_
+#elif defined _RTL871X_IOCTL_C_
+ #define _MODULE_DEFINE_ _module_rtl871x_ioctl_c_
+#elif defined _RTL871X_IOCTL_SET_C_
+ #define _MODULE_DEFINE_ _module_rtl871x_ioctl_set_c_
+#elif defined _RTL871X_IOCTL_QUERY_C_
+ #define _MODULE_DEFINE_ _module_rtl871x_ioctl_query_c_
+#elif defined _RTL871X_PWRCTRL_C_
+ #define _MODULE_DEFINE_ _module_rtl871x_pwrctrl_c_
+#elif defined _RTW_PWRCTRL_C_
+ #define _MODULE_DEFINE_ 1
+#elif defined _HCI_INTF_C_
+ #define _MODULE_DEFINE_ _module_hci_intfs_c_
+#elif defined _HCI_OPS_C_
+ #define _MODULE_DEFINE_ _module_hci_ops_c_
+#elif defined _SDIO_OPS_C_
+ #define _MODULE_DEFINE_ 1
+#elif defined _OSDEP_HCI_INTF_C_
+ #define _MODULE_DEFINE_ _module_hci_intfs_c_
+#elif defined _OSDEP_SERVICE_C_
+ #define _MODULE_DEFINE_ _module_osdep_service_c_
+#elif defined _HCI_OPS_OS_C_
+ #define _MODULE_DEFINE_ _module_hci_ops_os_c_
+#elif defined _RTL871X_IOCTL_LINUX_C_
+ #define _MODULE_DEFINE_ _module_rtl871x_ioctl_os_c
+#elif defined _RTL8712_CMD_C_
+ #define _MODULE_DEFINE_ _module_rtl8712_cmd_c_
+#elif defined _RTL8192C_XMIT_C_
+ #define _MODULE_DEFINE_ 1
+#elif defined _RTL8723AS_XMIT_C_
+ #define _MODULE_DEFINE_ 1
+#elif defined _RTL8712_RECV_C_
+ #define _MODULE_DEFINE_ _module_rtl8712_recv_c_
+#elif defined _RTL8192CU_RECV_C_
+ #define _MODULE_DEFINE_ _module_rtl8712_recv_c_
+#elif defined _RTL871X_MLME_EXT_C_
+ #define _MODULE_DEFINE_ _module_mlme_osdep_c_
+#elif defined _RTW_MP_C_
+ #define _MODULE_DEFINE_ _module_mp_
+#elif defined _RTW_MP_IOCTL_C_
+ #define _MODULE_DEFINE_ _module_mp_
+#elif defined _RTW_EFUSE_C_
+ #define _MODULE_DEFINE_ _module_efuse_
+#endif
+
+#define DRIVER_PREFIX "RTL8723AU: "
+#define DEBUG_LEVEL (_drv_err_)
+#define DBG_8723A_LEVEL(_level, fmt, arg...) \
+ do { \
+ if (_level <= GlobalDebugLevel23A) \
+ pr_info(DRIVER_PREFIX fmt, ##arg);\
+ } while (0)
+
+#define DBG_8723A(...) \
+ do { \
+ if (_drv_err_ <= GlobalDebugLevel23A) \
+ pr_info(DRIVER_PREFIX __VA_ARGS__); \
+ } while (0)
+
+#define MSG_8723A(...) \
+ do { \
+ if (_drv_err_ <= GlobalDebugLevel23A) \
+ pr_info(DRIVER_PREFIX __VA_ARGS__); \
+ } while (0)
+
+extern u32 GlobalDebugLevel23A;
+
+__printf(3, 4)
+void rt_trace(int comp, int level, const char *fmt, ...);
+
+#define RT_TRACE(_Comp, _Level, Fmt, ...) \
+do { \
+ if (_Level <= GlobalDebugLevel23A) \
+ rt_trace(_Comp, _Level, Fmt, ##__VA_ARGS__); \
+} while (0)
+
+#define RT_PRINT_DATA(_Comp, _Level, _TitleString, _HexData, \
+ _HexDataLen) \
+ if (_Level <= GlobalDebugLevel23A) { \
+ int __i; \
+ u8 *ptr = (u8 *)_HexData; \
+ pr_info("%s", DRIVER_PREFIX); \
+ pr_info(_TitleString); \
+ for (__i = 0; __i < (int)_HexDataLen; __i++) { \
+ printk("%02X%s", ptr[__i], \
+ (((__i + 1) % 4) == 0) ? " " : " "); \
+ if (((__i + 1) % 16) == 0) \
+ printk("\n"); \
+ } \
+ printk("\n"); \
+ }
+
+#endif /* __RTW_DEBUG_H__ */
diff --git a/drivers/staging/rtl8723au/include/rtw_eeprom.h b/drivers/staging/rtl8723au/include/rtw_eeprom.h
new file mode 100644
index 000000000..a86f36e49
--- /dev/null
+++ b/drivers/staging/rtl8723au/include/rtw_eeprom.h
@@ -0,0 +1,135 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2011 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * 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.
+ *
+ ******************************************************************************/
+#ifndef __RTW_EEPROM_H__
+#define __RTW_EEPROM_H__
+
+#include <osdep_service.h>
+#include <drv_types.h>
+
+#define RTL8712_EEPROM_ID 0x8712
+/* define EEPROM_MAX_SIZE 256 */
+
+#define HWSET_MAX_SIZE_512 512
+#define EEPROM_MAX_SIZE HWSET_MAX_SIZE_512
+
+#define CLOCK_RATE 50 /* 100us */
+
+/* EEPROM opcodes */
+#define EEPROM_READ_OPCODE 06
+#define EEPROM_WRITE_OPCODE 05
+#define EEPROM_ERASE_OPCODE 07
+#define EEPROM_EWEN_OPCODE 19 /* Erase/write enable */
+#define EEPROM_EWDS_OPCODE 16 /* Erase/write disable */
+
+/* Country codes */
+#define USA 0x555320
+#define EUROPE 0x1 /* temp, should be provided later */
+#define JAPAN 0x2 /* temp, should be provided later */
+
+#define EEPROM_CID_DEFAULT 0x0
+#define EEPROM_CID_ALPHA 0x1
+#define EEPROM_CID_Senao 0x3
+#define EEPROM_CID_NetCore 0x5
+#define EEPROM_CID_CAMEO 0X8
+#define EEPROM_CID_SITECOM 0x9
+#define EEPROM_CID_COREGA 0xB
+#define EEPROM_CID_EDIMAX_BELKIN 0xC
+#define EEPROM_CID_SERCOMM_BELKIN 0xE
+#define EEPROM_CID_CAMEO1 0xF
+#define EEPROM_CID_WNC_COREGA 0x12
+#define EEPROM_CID_CLEVO 0x13
+#define EEPROM_CID_WHQL 0xFE /* added by chiyoko for dtm, 20090108 */
+
+/* */
+/* Customer ID, note that: */
+/* This variable is initiailzed through EEPROM or registry, */
+/* however, its definition may be different with that in EEPROM for */
+/* EEPROM size consideration. So, we have to perform proper translation between them. */
+/* Besides, CustomerID of registry has precedence of that of EEPROM. */
+/* defined below. 060703, by rcnjko. */
+/* */
+enum rt_customer_id
+{
+ RT_CID_DEFAULT = 0,
+ RT_CID_8187_ALPHA0 = 1,
+ RT_CID_8187_SERCOMM_PS = 2,
+ RT_CID_8187_HW_LED = 3,
+ RT_CID_8187_NETGEAR = 4,
+ RT_CID_WHQL = 5,
+ RT_CID_819x_CAMEO = 6,
+ RT_CID_819x_RUNTOP = 7,
+ RT_CID_819x_Senao = 8,
+ RT_CID_TOSHIBA = 9, /* Merge by Jacken, 2008/01/31. */
+ RT_CID_819x_Netcore = 10,
+ RT_CID_Nettronix = 11,
+ RT_CID_DLINK = 12,
+ RT_CID_PRONET = 13,
+ RT_CID_COREGA = 14,
+ RT_CID_CHINA_MOBILE = 15,
+ RT_CID_819x_ALPHA = 16,
+ RT_CID_819x_Sitecom = 17,
+ RT_CID_CCX = 18, /* It's set under CCX logo test and isn't demanded for CCX functions, but for test behavior like retry limit and tx report. By Bruce, 2009-02-17. */
+ RT_CID_819x_Lenovo = 19,
+ RT_CID_819x_QMI = 20,
+ RT_CID_819x_Edimax_Belkin = 21,
+ RT_CID_819x_Sercomm_Belkin = 22,
+ RT_CID_819x_CAMEO1 = 23,
+ RT_CID_819x_MSI = 24,
+ RT_CID_819x_Acer = 25,
+ RT_CID_819x_AzWave_ASUS = 26,
+ RT_CID_819x_AzWave = 27, /* For AzWave in PCIe, The ID is AzWave use and not only Asus */
+ RT_CID_819x_HP = 28,
+ RT_CID_819x_WNC_COREGA = 29,
+ RT_CID_819x_Arcadyan_Belkin = 30,
+ RT_CID_819x_SAMSUNG = 31,
+ RT_CID_819x_CLEVO = 32,
+ RT_CID_819x_DELL = 33,
+ RT_CID_819x_PRONETS = 34,
+ RT_CID_819x_Edimax_ASUS = 35,
+ RT_CID_819x_CAMEO_NETGEAR = 36,
+ RT_CID_PLANEX = 37,
+ RT_CID_CC_C = 38,
+ RT_CID_819x_Xavi = 39,
+ RT_CID_819x_FUNAI_TV = 40,
+ RT_CID_819x_ALPHA_WD=41,
+};
+
+struct eeprom_priv {
+ u8 mac_addr[6]; /* PermanentAddress */
+ u8 bautoload_fail_flag;
+ u8 bloadfile_fail_flag;
+ u8 bloadmac_fail_flag;
+ /* u8 bempty; */
+ /* u8 sys_config; */
+ /* u8 config0; */
+ u16 channel_plan;
+ /* u8 country_string[3]; */
+ /* u8 tx_power_b[15]; */
+ /* u8 tx_power_g[15]; */
+ /* u8 tx_power_a[201]; */
+
+ u8 EepromOrEfuse;
+
+ u8 efuse_eeprom_data[HWSET_MAX_SIZE_512]; /* 92C:256bytes, 88E:512bytes, we use union set (512bytes) */
+};
+
+void eeprom_write16(struct rtw_adapter *padapter, u16 reg, u16 data);
+u16 eeprom_read16(struct rtw_adapter *padapter, u16 reg);
+void read_eeprom_content(struct rtw_adapter *padapter);
+void eeprom_read_sz(struct rtw_adapter *padapter, u16 reg, u8 *data, u32 sz);
+
+void read_eeprom_content_by_attrib(struct rtw_adapter *padapter);
+
+#endif /* __RTL871X_EEPROM_H__ */
diff --git a/drivers/staging/rtl8723au/include/rtw_efuse.h b/drivers/staging/rtl8723au/include/rtw_efuse.h
new file mode 100644
index 000000000..c577e260f
--- /dev/null
+++ b/drivers/staging/rtl8723au/include/rtw_efuse.h
@@ -0,0 +1,109 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2011 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * 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.
+ *
+ *
+ ******************************************************************************/
+#ifndef __RTW_EFUSE_H__
+#define __RTW_EFUSE_H__
+
+#include <osdep_service.h>
+
+#define EFUSE_ERROE_HANDLE 1
+
+#define PG_STATE_HEADER 0x01
+#define PG_STATE_WORD_0 0x02
+#define PG_STATE_WORD_1 0x04
+#define PG_STATE_WORD_2 0x08
+#define PG_STATE_WORD_3 0x10
+#define PG_STATE_DATA 0x20
+
+#define PG_SWBYTE_H 0x01
+#define PG_SWBYTE_L 0x02
+
+#define PGPKT_DATA_SIZE 8
+
+#define EFUSE_WIFI 0
+#define EFUSE_BT 1
+
+enum _EFUSE_DEF_TYPE {
+ TYPE_EFUSE_MAX_SECTION = 0,
+ TYPE_EFUSE_REAL_CONTENT_LEN = 1,
+ TYPE_AVAILABLE_EFUSE_BYTES_BANK = 2,
+ TYPE_AVAILABLE_EFUSE_BYTES_TOTAL = 3,
+ TYPE_EFUSE_MAP_LEN = 4,
+ TYPE_EFUSE_PROTECT_BYTES_BANK = 5,
+ TYPE_EFUSE_CONTENT_LEN_BANK = 6,
+};
+
+/* E-Fuse */
+#define EFUSE_MAP_SIZE 256
+
+#define EFUSE_MAX_SIZE 512
+/* end of E-Fuse */
+
+#define EFUSE_MAX_MAP_LEN 256
+#define EFUSE_MAX_HW_SIZE 512
+#define EFUSE_MAX_SECTION_BASE 16
+
+#define EXT_HEADER(header) ((header & 0x1F) == 0x0F)
+#define ALL_WORDS_DISABLED(wde) ((wde & 0x0F) == 0x0F)
+#define GET_HDR_OFFSET_2_0(header) ( (header & 0xE0) >> 5)
+
+#define EFUSE_REPEAT_THRESHOLD_ 3
+
+/* */
+/* The following is for BT Efuse definition */
+/* */
+#define EFUSE_BT_MAX_MAP_LEN 1024
+#define EFUSE_MAX_BANK 4
+#define EFUSE_MAX_BT_BANK (EFUSE_MAX_BANK-1)
+/* */
+/*--------------------------Define Parameters-------------------------------*/
+#define EFUSE_MAX_WORD_UNIT 4
+
+/*------------------------------Define structure----------------------------*/
+struct pg_pkt_struct {
+ u8 offset;
+ u8 word_en;
+ u8 data[8];
+ u8 word_cnts;
+};
+
+/*------------------------Export global variable----------------------------*/
+
+u16 efuse_GetMaxSize23a(struct rtw_adapter *padapter);
+int rtw_efuse_access23a(struct rtw_adapter *padapter, u8 bRead, u16 start_addr, u16 cnts, u8 *data);
+int rtw_efuse_map_read23a(struct rtw_adapter *padapter, u16 addr, u16 cnts, u8 *data);
+u8 rtw_efuse_map_write(struct rtw_adapter *padapter, u16 addr, u16 cnts, u8 *data);
+int rtw_BT_efuse_map_read23a(struct rtw_adapter *padapter, u16 addr, u16 cnts, u8 *data);
+u8 rtw_BT_efuse_map_write(struct rtw_adapter *padapter, u16 addr, u16 cnts, u8 *data);
+
+u16 Efuse_GetCurrentSize23a(struct rtw_adapter *pAdapter, u8 efuseType);
+u8 Efuse_CalculateWordCnts23a(u8 word_en);
+void ReadEFuseByte23a(struct rtw_adapter *Adapter, u16 _offset, u8 *pbuf);
+void EFUSE_GetEfuseDefinition23a(struct rtw_adapter *pAdapter, u8 efuseType, u8 type, void *pOut);
+int efuse_OneByteRead23a(struct rtw_adapter *pAdapter, u16 addr, u8 *data);
+int efuse_OneByteWrite23a(struct rtw_adapter *pAdapter, u16 addr, u8 data);
+
+void Efuse_PowerSwitch23a(struct rtw_adapter *pAdapter, u8 bWrite,
+ u8 PwrState);
+int Efuse_PgPacketRead23a(struct rtw_adapter *pAdapter, u8 offset, u8 *data);
+int Efuse_PgPacketWrite23a(struct rtw_adapter *pAdapter, u8 offset, u8 word_en, u8 *data);
+void efuse_WordEnableDataRead23a(u8 word_en, u8 *sourdata, u8 *targetdata);
+u8 Efuse_WordEnableDataWrite23a(struct rtw_adapter *pAdapter, u16 efuse_addr, u8 word_en, u8 *data);
+
+u8 EFUSE_Read1Byte23a(struct rtw_adapter *pAdapter, u16 Address);
+void EFUSE_ShadowMapUpdate23a(struct rtw_adapter *pAdapter, u8 efuseType);
+void EFUSE_ShadowRead23a(struct rtw_adapter *pAdapter, u8 Type, u16 Offset, u32 *Value);
+
+#endif
diff --git a/drivers/staging/rtl8723au/include/rtw_event.h b/drivers/staging/rtl8723au/include/rtw_event.h
new file mode 100644
index 000000000..4557aeccc
--- /dev/null
+++ b/drivers/staging/rtl8723au/include/rtw_event.h
@@ -0,0 +1,74 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2011 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * 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.
+ *
+ ******************************************************************************/
+#ifndef _RTW_EVENT_H_
+#define _RTW_EVENT_H_
+
+#include <osdep_service.h>
+#include <wlan_bssdef.h>
+
+/*
+Used to report a bss has been scanned
+*/
+struct survey_event {
+ struct wlan_bssid_ex *bss;
+};
+
+/*
+Used to report that the requested site survey has been done.
+bss_cnt indicates the number of bss that has been reported.
+*/
+struct surveydone_event {
+ unsigned int bss_cnt;
+};
+
+/*
+Used to report the link result of joinning the given bss
+join_res:
+-1: authentication fail
+-2: association fail
+> 0: TID
+*/
+struct joinbss_event {
+ struct wlan_network network;
+};
+
+/*
+Used to report a given STA has joinned the created BSS.
+It is used in AP/Ad-HoC(M) mode.
+*/
+struct stassoc_event {
+ unsigned char macaddr[6];
+ unsigned char rsvd[2];
+ int cam_id;
+};
+
+struct stadel_event {
+ unsigned char macaddr[6];
+ unsigned char rsvd[2]; /* for reason */
+ int mac_id;
+};
+
+struct addba_event {
+ unsigned int tid;
+};
+
+#define GEN_EVT_CODE(event) event ## _EVT_
+
+struct fwevent {
+ u32 parmsize;
+ void (*event_callback)(struct rtw_adapter *dev, const u8 *pbuf);
+};
+
+#endif /* _WLANEVENT_H_ */
diff --git a/drivers/staging/rtl8723au/include/rtw_ht.h b/drivers/staging/rtl8723au/include/rtw_ht.h
new file mode 100644
index 000000000..780eb8944
--- /dev/null
+++ b/drivers/staging/rtl8723au/include/rtw_ht.h
@@ -0,0 +1,42 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2011 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * 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.
+ *
+ ******************************************************************************/
+#ifndef _RTW_HT_H_
+#define _RTW_HT_H_
+
+#include <osdep_service.h>
+#include "linux/ieee80211.h"
+#include "wifi.h"
+
+struct ht_priv {
+ bool ht_option;
+ bool ampdu_enable;/* for enable Tx A-MPDU */
+ /* u8 baddbareq_issued[16]; */
+ u32 tx_amsdu_enable;/* for enable Tx A-MSDU */
+ u32 tx_amdsu_maxlen; /* 1: 8k, 0:4k ; default:8k, for tx */
+ u32 rx_ampdu_maxlen; /* for rx reordering ctrl win_sz, updated when join_callback. */
+
+ u8 bwmode;/* */
+ u8 ch_offset;/* PRIME_CHNL_OFFSET */
+ u8 sgi;/* short GI */
+
+ /* for processing Tx A-MPDU */
+ u16 agg_enable_bitmap;
+ /* u8 ADDBA_retry_count; */
+ u16 candidate_tid_bitmap;
+
+ struct ieee80211_ht_cap ht_cap;
+};
+
+#endif /* _RTL871X_HT_H_ */
diff --git a/drivers/staging/rtl8723au/include/rtw_io.h b/drivers/staging/rtl8723au/include/rtw_io.h
new file mode 100644
index 000000000..c8119e2c6
--- /dev/null
+++ b/drivers/staging/rtl8723au/include/rtw_io.h
@@ -0,0 +1,237 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2011 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * 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.
+ *
+ ******************************************************************************/
+
+#ifndef _RTW_IO_H_
+#define _RTW_IO_H_
+
+#include <osdep_service.h>
+#include <osdep_intf.h>
+
+#include <asm/byteorder.h>
+#include <linux/semaphore.h>
+#include <linux/list.h>
+/* include <linux/smp_lock.h> */
+#include <linux/spinlock.h>
+#include <asm/atomic.h>
+
+#include <linux/usb.h>
+#include <linux/usb/ch9.h>
+
+#define rtw_usb_buffer_alloc(dev, size, dma) usb_alloc_coherent((dev), (size), (in_interrupt() ? GFP_ATOMIC : GFP_KERNEL), (dma))
+#define rtw_usb_buffer_free(dev, size, addr, dma) usb_free_coherent((dev), (size), (addr), (dma))
+
+#define NUM_IOREQ 8
+
+#define MAX_PROT_SZ (64-16)
+
+#define _IOREADY 0
+#define _IO_WAIT_COMPLETE 1
+#define _IO_WAIT_RSP 2
+
+/* IO COMMAND TYPE */
+#define _IOSZ_MASK_ (0x7F)
+#define _IO_WRITE_ BIT(7)
+#define _IO_FIXED_ BIT(8)
+#define _IO_BURST_ BIT(9)
+#define _IO_BYTE_ BIT(10)
+#define _IO_HW_ BIT(11)
+#define _IO_WORD_ BIT(12)
+#define _IO_SYNC_ BIT(13)
+#define _IO_CMDMASK_ (0x1F80)
+
+
+/*
+ For prompt mode accessing, caller shall free io_req
+ Otherwise, io_handler will free io_req
+*/
+
+
+
+/* IO STATUS TYPE */
+#define _IO_ERR_ BIT(2)
+#define _IO_SUCCESS_ BIT(1)
+#define _IO_DONE_ BIT(0)
+
+
+#define IO_RD32 (_IO_SYNC_ | _IO_WORD_)
+#define IO_RD16 (_IO_SYNC_ | _IO_HW_)
+#define IO_RD8 (_IO_SYNC_ | _IO_BYTE_)
+
+#define IO_RD32_ASYNC (_IO_WORD_)
+#define IO_RD16_ASYNC (_IO_HW_)
+#define IO_RD8_ASYNC (_IO_BYTE_)
+
+#define IO_WR32 (_IO_WRITE_ | _IO_SYNC_ | _IO_WORD_)
+#define IO_WR16 (_IO_WRITE_ | _IO_SYNC_ | _IO_HW_)
+#define IO_WR8 (_IO_WRITE_ | _IO_SYNC_ | _IO_BYTE_)
+
+#define IO_WR32_ASYNC (_IO_WRITE_ | _IO_WORD_)
+#define IO_WR16_ASYNC (_IO_WRITE_ | _IO_HW_)
+#define IO_WR8_ASYNC (_IO_WRITE_ | _IO_BYTE_)
+
+/*
+
+ Only Sync. burst accessing is provided.
+
+*/
+
+#define IO_WR_BURST(x) (_IO_WRITE_ | _IO_SYNC_ | _IO_BURST_ | ( (x) & _IOSZ_MASK_))
+#define IO_RD_BURST(x) (_IO_SYNC_ | _IO_BURST_ | ( (x) & _IOSZ_MASK_))
+
+
+
+/* below is for the intf_option bit defition... */
+
+#define _INTF_ASYNC_ BIT(0) /* support async io */
+
+struct intf_priv;
+
+struct io_req {
+ struct list_head list;
+ u32 addr;
+ volatile u32 val;
+ u32 command;
+ u32 status;
+ u8 *pbuf;
+ struct semaphore sema;
+
+ void (*_async_io_callback)(struct rtw_adapter *padater, struct io_req *pio_req, u8 *cnxt);
+ u8 *cnxt;
+};
+
+struct reg_protocol_rd {
+
+#ifdef __LITTLE_ENDIAN
+
+ /* DW1 */
+ u32 NumOfTrans:4;
+ u32 Reserved1:4;
+ u32 Reserved2:24;
+ /* DW2 */
+ u32 ByteCount:7;
+ u32 WriteEnable:1; /* 0:read, 1:write */
+ u32 FixOrContinuous:1; /* 0:continuous, 1: Fix */
+ u32 BurstMode:1;
+ u32 Byte1Access:1;
+ u32 Byte2Access:1;
+ u32 Byte4Access:1;
+ u32 Reserved3:3;
+ u32 Reserved4:16;
+ /* DW3 */
+ u32 BusAddress;
+ /* DW4 */
+ /* u32 Value; */
+#else
+
+
+/* DW1 */
+ u32 Reserved1 :4;
+ u32 NumOfTrans :4;
+
+ u32 Reserved2 :24;
+
+ /* DW2 */
+ u32 WriteEnable : 1;
+ u32 ByteCount :7;
+
+
+ u32 Reserved3 : 3;
+ u32 Byte4Access : 1;
+
+ u32 Byte2Access : 1;
+ u32 Byte1Access : 1;
+ u32 BurstMode :1 ;
+ u32 FixOrContinuous : 1;
+
+ u32 Reserved4 : 16;
+
+ /* DW3 */
+ u32 BusAddress;
+
+ /* DW4 */
+ /* u32 Value; */
+
+#endif
+
+};
+
+
+struct reg_protocol_wt {
+
+
+#ifdef __LITTLE_ENDIAN
+
+ /* DW1 */
+ u32 NumOfTrans:4;
+ u32 Reserved1:4;
+ u32 Reserved2:24;
+ /* DW2 */
+ u32 ByteCount:7;
+ u32 WriteEnable:1; /* 0:read, 1:write */
+ u32 FixOrContinuous:1; /* 0:continuous, 1: Fix */
+ u32 BurstMode:1;
+ u32 Byte1Access:1;
+ u32 Byte2Access:1;
+ u32 Byte4Access:1;
+ u32 Reserved3:3;
+ u32 Reserved4:16;
+ /* DW3 */
+ u32 BusAddress;
+ /* DW4 */
+ u32 Value;
+
+#else
+ /* DW1 */
+ u32 Reserved1 :4;
+ u32 NumOfTrans :4;
+
+ u32 Reserved2 :24;
+
+ /* DW2 */
+ u32 WriteEnable : 1;
+ u32 ByteCount :7;
+
+ u32 Reserved3 : 3;
+ u32 Byte4Access : 1;
+
+ u32 Byte2Access : 1;
+ u32 Byte1Access : 1;
+ u32 BurstMode :1 ;
+ u32 FixOrContinuous : 1;
+
+ u32 Reserved4 : 16;
+
+ /* DW3 */
+ u32 BusAddress;
+
+ /* DW4 */
+ u32 Value;
+
+#endif
+
+};
+
+#define PlatformEFIOWrite1Byte(_a, _b, _c) \
+ rtl8723au_write8(_a, _b, _c)
+#define PlatformEFIOWrite2Byte(_a, _b, _c) \
+ rtl8723au_write16(_a, _b, _c)
+#define PlatformEFIOWrite4Byte(_a, _b, _c) \
+ rtl8723au_write32(_a, _b, _c)
+
+#define PlatformEFIORead1Byte(_a, _b) rtl8723au_read8(_a, _b)
+#define PlatformEFIORead2Byte(_a, _b) rtl8723au_read16(_a, _b)
+#define PlatformEFIORead4Byte(_a, _b) rtl8723au_read32(_a, _b)
+
+#endif /* _RTL8711_IO_H_ */
diff --git a/drivers/staging/rtl8723au/include/rtw_mlme.h b/drivers/staging/rtl8723au/include/rtw_mlme.h
new file mode 100644
index 000000000..a6751f138
--- /dev/null
+++ b/drivers/staging/rtl8723au/include/rtw_mlme.h
@@ -0,0 +1,340 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2011 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * 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.
+ *
+ *
+ ******************************************************************************/
+#ifndef __RTW_MLME_H_
+#define __RTW_MLME_H_
+
+#include <osdep_service.h>
+#include <mlme_osdep.h>
+#include <drv_types.h>
+#include <wlan_bssdef.h>
+
+#define MAX_BSS_CNT 128
+#define MAX_JOIN_TIMEOUT 6500
+
+/* Increase the scanning timeout because of increasing the SURVEY_TO value. */
+
+#define SCANNING_TIMEOUT 8000
+
+#define SCAN_INTERVAL (30) /* unit:2sec, 30*2 = 60sec */
+
+#define SCANQUEUE_LIFETIME 20 /* unit:sec */
+
+#define WIFI_NULL_STATE 0x00000000
+
+#define WIFI_ASOC_STATE 0x00000001 /* Under Linked state.*/
+#define WIFI_REASOC_STATE 0x00000002
+#define WIFI_SLEEP_STATE 0x00000004
+#define WIFI_STATION_STATE 0x00000008
+
+#define WIFI_AP_STATE 0x00000010
+#define WIFI_ADHOC_STATE 0x00000020
+#define WIFI_ADHOC_MASTER_STATE 0x00000040
+#define WIFI_UNDER_LINKING 0x00000080
+
+#define WIFI_UNDER_WPS 0x00000100
+#define WIFI_STA_ALIVE_CHK_STATE 0x00000400
+/* to indicate the station is under site surveying */
+#define WIFI_SITE_MONITOR 0x00000800
+
+#define WIFI_MP_STATE 0x00010000
+#define WIFI_MP_CTX_BACKGROUND 0x00020000 /* in continous tx background */
+#define WIFI_MP_CTX_ST 0x00040000 /* in continous tx with single-tone */
+#define WIFI_MP_CTX_BACKGROUND_PENDING 0x00080000 /* pending in continous tx background due to out of skb */
+#define WIFI_MP_CTX_CCK_HW 0x00100000 /* in continous tx */
+#define WIFI_MP_CTX_CCK_CS 0x00200000 /* in continous tx with carrier suppression */
+#define WIFI_MP_LPBK_STATE 0x00400000
+
+#define _FW_UNDER_LINKING WIFI_UNDER_LINKING
+#define _FW_LINKED WIFI_ASOC_STATE
+#define _FW_UNDER_SURVEY WIFI_SITE_MONITOR
+
+
+enum dot11AuthAlgrthmNum {
+ dot11AuthAlgrthm_Open = 0,
+ dot11AuthAlgrthm_Shared,
+ dot11AuthAlgrthm_8021X,
+ dot11AuthAlgrthm_Auto,
+ dot11AuthAlgrthm_MaxNum
+};
+
+/* Scan type including active and passive scan. */
+enum rt_scan_type {
+ SCAN_PASSIVE,
+ SCAN_ACTIVE,
+ SCAN_MIX,
+};
+
+enum {
+ GHZ24_50 = 0,
+ GHZ_50,
+ GHZ_24,
+};
+
+/*
+
+there are several "locks" in mlme_priv,
+since mlme_priv is a shared resource between many threads,
+like ISR/Call-Back functions, the OID handlers, and even timer functions.
+
+
+Each _queue has its own locks, already.
+Other items are protected by mlme_priv.lock.
+
+To avoid possible dead lock, any thread trying to modifiying mlme_priv
+SHALL not lock up more than one locks at a time!
+*/
+
+struct rt_link_detect {
+ u32 NumTxOkInPeriod;
+ u32 NumRxOkInPeriod;
+ u32 NumRxUnicastOkInPeriod;
+ bool bBusyTraffic;
+ bool bTxBusyTraffic;
+ bool bRxBusyTraffic;
+ bool bHigherBusyTraffic; /* For interrupt migration purpose. */
+ bool bHigherBusyRxTraffic; /* We may disable Tx interrupt according as Rx traffic. */
+ bool bHigherBusyTxTraffic; /* We may disable Tx interrupt according as Tx traffic. */
+};
+
+struct mlme_priv {
+ spinlock_t lock;
+ int fw_state;
+ u8 bScanInProcess;
+ u8 to_join; /* flag */
+ u8 to_roaming; /* roaming trying times */
+
+ struct rtw_adapter *nic_hdl;
+
+ u8 not_indic_disco;
+ struct rtw_queue scanned_queue;
+
+ struct cfg80211_ssid assoc_ssid;
+ u8 assoc_bssid[6];
+
+ struct wlan_network cur_network;
+
+ /* uint wireless_mode; no used, remove it */
+
+ u32 scan_interval;
+
+ struct timer_list assoc_timer;
+
+ uint assoc_by_bssid;
+ uint assoc_by_rssi;
+
+ struct timer_list scan_to_timer;
+
+ struct timer_list set_scan_deny_timer;
+ atomic_t set_scan_deny; /* 0: allowed, 1: deny */
+
+ unsigned int qos_option;
+
+ /* Number of non-HT AP/stations */
+ int num_sta_no_ht;
+
+ int num_FortyMHzIntolerant;
+
+ struct ht_priv htpriv;
+
+ struct rt_link_detect LinkDetectInfo;
+ struct timer_list dynamic_chk_timer; /* dynamic/periodic check timer */
+
+ u8 key_mask; /* use for ips to set wep key after ips_leave23a */
+ u8 acm_mask; /* for wmm acm mask */
+ u8 ChannelPlan;
+ enum rt_scan_type scan_mode; /* active: 1, passive: 0 */
+
+ u8 *wps_probe_req_ie;
+ u32 wps_probe_req_ie_len;
+ u8 *assoc_req;
+ u32 assoc_req_len;
+ u32 assoc_rsp_len;
+ u8 *assoc_rsp;
+
+#ifdef CONFIG_8723AU_AP_MODE
+ /* Number of associated Non-ERP stations (i.e., stations using 802.11b
+ * in 802.11g BSS) */
+ int num_sta_non_erp;
+
+ /* Number of associated stations that do not support Short Slot Time */
+ int num_sta_no_short_slot_time;
+
+ /* Number of associated stations that do not support Short Preamble */
+ int num_sta_no_short_preamble;
+
+ int olbc; /* Overlapping Legacy BSS Condition */
+
+ /* Number of HT associated stations that do not support greenfield */
+ int num_sta_ht_no_gf;
+
+ /* Number of associated non-HT stations */
+ /* int num_sta_no_ht; */
+
+ /* Number of HT associated stations 20 MHz */
+ int num_sta_ht_20mhz;
+
+ /* Overlapping BSS information */
+ int olbc_ht;
+
+ u16 ht_op_mode;
+
+ spinlock_t bcn_update_lock;
+ u8 update_bcn;
+
+#endif /* ifdef CONFIG_8723AU_AP_MODE */
+};
+
+void rtw_joinbss_event_prehandle23a(struct rtw_adapter *adapter, u8 *pbuf);
+void rtw_survey_event_cb23a(struct rtw_adapter *adapter, const u8 *pbuf);
+void rtw_surveydone_event_callback23a(struct rtw_adapter *adapter, const u8 *pbuf);
+void rtw23a_joinbss_event_cb(struct rtw_adapter *adapter, const u8 *pbuf);
+void rtw_stassoc_event_callback23a(struct rtw_adapter *adapter, const u8 *pbuf);
+void rtw_stadel_event_callback23a(struct rtw_adapter *adapter, const u8 *pbuf);
+
+int event_thread(void *context);
+void rtw23a_join_to_handler(unsigned long);
+
+void rtw_free_network_queue23a(struct rtw_adapter *adapter);
+int rtw_init_mlme_priv23a(struct rtw_adapter *adapter);
+
+void rtw_free_mlme_priv23a(struct mlme_priv *pmlmepriv);
+
+int rtw_do_join_adhoc(struct rtw_adapter *adapter);
+int rtw_do_join_network(struct rtw_adapter *adapter,
+ struct wlan_network *candidate);
+int rtw_select_and_join_from_scanned_queue23a(struct mlme_priv *pmlmepriv);
+int rtw_set_key23a(struct rtw_adapter *adapter,
+ struct security_priv *psecuritypriv, int keyid, u8 set_tx);
+int rtw_set_auth23a(struct rtw_adapter *adapter,
+ struct security_priv *psecuritypriv);
+
+static inline u8 *get_bssid(struct mlme_priv *pmlmepriv)
+{ /* if sta_mode:pmlmepriv->cur_network.network.MacAddress => bssid */
+ /* if adhoc_mode:pmlmepriv->cur_network.network.MacAddress => ibss mac address */
+ return pmlmepriv->cur_network.network.MacAddress;
+}
+
+static inline bool check_fwstate(struct mlme_priv *pmlmepriv, int state)
+{
+ if (pmlmepriv->fw_state & state)
+ return true;
+
+ return false;
+}
+
+static inline int get_fwstate(struct mlme_priv *pmlmepriv)
+{
+ return pmlmepriv->fw_state;
+}
+
+/*
+ * No Limit on the calling context,
+ * therefore set it to be the critical section...
+ *
+ * ### NOTE:#### (!!!!)
+ * MUST TAKE CARE THAT BEFORE CALLING THIS FUNC, YOU SHOULD HAVE LOCKED pmlmepriv->lock
+ */
+static inline void set_fwstate(struct mlme_priv *pmlmepriv, int state)
+{
+ pmlmepriv->fw_state |= state;
+ /* FOR HW integration */
+ if (_FW_UNDER_SURVEY == state)
+ pmlmepriv->bScanInProcess = true;
+}
+
+static inline void _clr_fwstate_(struct mlme_priv *pmlmepriv, int state)
+{
+ pmlmepriv->fw_state &= ~state;
+ /* FOR HW integration */
+ if (_FW_UNDER_SURVEY == state)
+ pmlmepriv->bScanInProcess = false;
+}
+
+/*
+ * No Limit on the calling context,
+ * therefore set it to be the critical section...
+ */
+static inline void clr_fwstate(struct mlme_priv *pmlmepriv, int state)
+{
+ spin_lock_bh(&pmlmepriv->lock);
+ if (check_fwstate(pmlmepriv, state))
+ pmlmepriv->fw_state ^= state;
+ spin_unlock_bh(&pmlmepriv->lock);
+}
+
+static inline void clr_fwstate_ex(struct mlme_priv *pmlmepriv, int state)
+{
+ spin_lock_bh(&pmlmepriv->lock);
+ _clr_fwstate_(pmlmepriv, state);
+ spin_unlock_bh(&pmlmepriv->lock);
+}
+
+void rtw_disconnect_hdl23a_under_linked(struct rtw_adapter *adapter,
+ struct sta_info *psta, u8 free_assoc);
+void rtw_generate_random_ibss23a(u8 *pibss);
+struct wlan_network *rtw_find_network23a(struct rtw_queue *scanned_queue, u8 *addr);
+struct wlan_network *rtw_get_oldest_wlan_network23a(struct rtw_queue *scanned_queue);
+
+void rtw_free_assoc_resources23a(struct rtw_adapter *adapter,
+ int lock_scanned_queue);
+void rtw_indicate_disconnect23a(struct rtw_adapter *adapter);
+void rtw_indicate_connect23a(struct rtw_adapter *adapter);
+void rtw_scan_abort23a(struct rtw_adapter *adapter);
+
+int rtw_restruct_sec_ie23a(struct rtw_adapter *adapter, u8 *in_ie, u8 *out_ie,
+ uint in_len);
+int rtw_restruct_wmm_ie23a(struct rtw_adapter *adapter, u8 *in_ie, u8 *out_ie,
+ uint in_len, uint initial_out_len);
+void rtw_init_registrypriv_dev_network23a(struct rtw_adapter *adapter);
+
+void rtw_update_registrypriv_dev_network23a(struct rtw_adapter *adapter);
+
+void rtw_scan_timeout_handler23a(unsigned long data);
+
+void rtw_dynamic_check_timer_handler(unsigned long data);
+bool rtw_is_scan_deny(struct rtw_adapter *adapter);
+void rtw_clear_scan_deny(struct rtw_adapter *adapter);
+void rtw_set_scan_deny_timer_hdl(unsigned long data);
+void rtw_set_scan_deny(struct rtw_adapter *adapter, u32 ms);
+
+void rtw23a_free_mlme_priv_ie_data(struct mlme_priv *pmlmepriv);
+
+void _rtw_free_mlme_priv23a(struct mlme_priv *pmlmepriv);
+
+struct wlan_network *rtw_alloc_network(struct mlme_priv *pmlmepriv, gfp_t gfp);
+
+int rtw_if_up23a(struct rtw_adapter *padapter);
+
+int rtw_linked_check(struct rtw_adapter *padapter);
+
+void rtw_joinbss_reset23a(struct rtw_adapter *padapter);
+
+bool rtw_restructure_ht_ie23a(struct rtw_adapter *padapter, u8 *in_ie,
+ u8 *out_ie, uint in_len, uint *pout_len);
+void rtw_update_ht_cap23a(struct rtw_adapter *padapter,
+ u8 *pie, uint ie_len);
+void rtw_issue_addbareq_cmd23a(struct rtw_adapter *padapter,
+ struct xmit_frame *pxmitframe);
+
+bool rtw_is_same_ibss23a(struct rtw_adapter *adapter,
+ struct wlan_network *pnetwork);
+int is_same_network23a(struct wlan_bssid_ex *src, struct wlan_bssid_ex *dst);
+
+void rtw23a_roaming(struct rtw_adapter *adapter,
+ struct wlan_network *tgt_network);
+void rtw_set_roaming(struct rtw_adapter *adapter, u8 to_roaming);
+
+#endif /* __RTL871X_MLME_H_ */
diff --git a/drivers/staging/rtl8723au/include/rtw_mlme_ext.h b/drivers/staging/rtl8723au/include/rtw_mlme_ext.h
new file mode 100644
index 000000000..ffb37b252
--- /dev/null
+++ b/drivers/staging/rtl8723au/include/rtw_mlme_ext.h
@@ -0,0 +1,685 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2011 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * 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.
+ *
+ ******************************************************************************/
+#ifndef __RTW_MLME_EXT_H_
+#define __RTW_MLME_EXT_H_
+
+#include <osdep_service.h>
+#include <drv_types.h>
+#include <wlan_bssdef.h>
+
+
+/* Commented by Albert 20101105 */
+/* Increase the SURVEY_TO value from 100 to 150 ( 100ms to 150ms ) */
+/* The Realtek 8188CE SoftAP will spend around 100ms to send the probe response after receiving the probe request. */
+/* So, this driver tried to extend the dwell time for each scanning channel. */
+/* This will increase the chance to receive the probe response from SoftAP. */
+
+#define SURVEY_TO (100)
+#define REAUTH_TO (300) /* 50) */
+#define REASSOC_TO (300) /* 50) */
+/* define DISCONNECT_TO (3000) */
+#define ADDBA_TO (2000)
+
+#define LINKED_TO (1) /* unit:2 sec, 1x2=2 sec */
+
+#define REAUTH_LIMIT (4)
+#define REASSOC_LIMIT (4)
+#define READDBA_LIMIT (2)
+
+#define ROAMING_LIMIT 8
+
+#define DYNAMIC_FUNC_DISABLE (0x0)
+
+/* ====== enum odm_ability ======== */
+/* BB ODM section BIT 0-15 */
+#define DYNAMIC_BB_DIG BIT(0)
+#define DYNAMIC_BB_RA_MASK BIT(1)
+#define DYNAMIC_BB_DYNAMIC_TXPWR BIT(2)
+#define DYNAMIC_BB_BB_FA_CNT BIT(3)
+
+#define DYNAMIC_BB_RSSI_MONITOR BIT(4)
+#define DYNAMIC_BB_CCK_PD BIT(5)
+#define DYNAMIC_BB_ANT_DIV BIT(6)
+#define DYNAMIC_BB_PWR_SAVE BIT(7)
+#define DYNAMIC_BB_PWR_TRAIN BIT(8)
+#define DYNAMIC_BB_RATE_ADAPTIVE BIT(9)
+#define DYNAMIC_BB_PATH_DIV BIT(10)
+#define DYNAMIC_BB_PSD BIT(11)
+
+/* MAC DM section BIT 16-23 */
+#define DYNAMIC_MAC_struct edca_turboURBO BIT(16)
+#define DYNAMIC_MAC_EARLY_MODE BIT(17)
+
+/* RF ODM section BIT 24-31 */
+#define DYNAMIC_RF_TX_PWR_TRACK BIT(24)
+#define DYNAMIC_RF_RX_GAIN_TRACK BIT(25)
+#define DYNAMIC_RF_CALIBRATION BIT(26)
+
+#define DYNAMIC_ALL_FUNC_ENABLE 0xFFFFFFF
+
+#define _HW_STATE_NOLINK_ 0x00
+#define _HW_STATE_ADHOC_ 0x01
+#define _HW_STATE_STATION_ 0x02
+#define _HW_STATE_AP_ 0x03
+
+
+#define _1M_RATE_ 0
+#define _2M_RATE_ 1
+#define _5M_RATE_ 2
+#define _11M_RATE_ 3
+#define _6M_RATE_ 4
+#define _9M_RATE_ 5
+#define _12M_RATE_ 6
+#define _18M_RATE_ 7
+#define _24M_RATE_ 8
+#define _36M_RATE_ 9
+#define _48M_RATE_ 10
+#define _54M_RATE_ 11
+
+
+extern unsigned char WMM_OUI23A[];
+extern unsigned char WPS_OUI23A[];
+extern unsigned char WFD_OUI23A[];
+extern unsigned char P2P_OUI23A[];
+
+extern unsigned char WMM_INFO_OUI23A[];
+extern unsigned char WMM_PARA_OUI23A[];
+
+
+/* */
+/* Channel Plan Type. */
+/* Note: */
+/* We just add new channel plan when the new channel plan is different from any of the following */
+/* channel plan. */
+/* If you just wnat to customize the acitions(scan period or join actions) about one of the channel plan, */
+/* customize them in struct rt_channel_info in the RT_CHANNEL_LIST. */
+/* */
+enum { /* _RT_CHANNEL_DOMAIN */
+ /* old channel plan mapping ===== */
+ RT_CHANNEL_DOMAIN_FCC = 0x00,
+ RT_CHANNEL_DOMAIN_IC = 0x01,
+ RT_CHANNEL_DOMAIN_ETSI = 0x02,
+ RT_CHANNEL_DOMAIN_SPAIN = 0x03,
+ RT_CHANNEL_DOMAIN_FRANCE = 0x04,
+ RT_CHANNEL_DOMAIN_MKK = 0x05,
+ RT_CHANNEL_DOMAIN_MKK1 = 0x06,
+ RT_CHANNEL_DOMAIN_ISRAEL = 0x07,
+ RT_CHANNEL_DOMAIN_TELEC = 0x08,
+ RT_CHANNEL_DOMAIN_GLOBAL_DOAMIN = 0x09,
+ RT_CHANNEL_DOMAIN_WORLD_WIDE_13 = 0x0A,
+ RT_CHANNEL_DOMAIN_TAIWAN = 0x0B,
+ RT_CHANNEL_DOMAIN_CHINA = 0x0C,
+ RT_CHANNEL_DOMAIN_SINGAPORE_INDIA_MEXICO = 0x0D,
+ RT_CHANNEL_DOMAIN_KOREA = 0x0E,
+ RT_CHANNEL_DOMAIN_TURKEY = 0x0F,
+ RT_CHANNEL_DOMAIN_JAPAN = 0x10,
+ RT_CHANNEL_DOMAIN_FCC_NO_DFS = 0x11,
+ RT_CHANNEL_DOMAIN_JAPAN_NO_DFS = 0x12,
+ RT_CHANNEL_DOMAIN_WORLD_WIDE_5G = 0x13,
+ RT_CHANNEL_DOMAIN_TAIWAN_NO_DFS = 0x14,
+
+ /* new channel plan mapping, (2GDOMAIN_5GDOMAIN) ===== */
+ RT_CHANNEL_DOMAIN_WORLD_NULL = 0x20,
+ RT_CHANNEL_DOMAIN_ETSI1_NULL = 0x21,
+ RT_CHANNEL_DOMAIN_FCC1_NULL = 0x22,
+ RT_CHANNEL_DOMAIN_MKK1_NULL = 0x23,
+ RT_CHANNEL_DOMAIN_ETSI2_NULL = 0x24,
+ RT_CHANNEL_DOMAIN_FCC1_FCC1 = 0x25,
+ RT_CHANNEL_DOMAIN_WORLD_ETSI1 = 0x26,
+ RT_CHANNEL_DOMAIN_MKK1_MKK1 = 0x27,
+ RT_CHANNEL_DOMAIN_WORLD_KCC1 = 0x28,
+ RT_CHANNEL_DOMAIN_WORLD_FCC2 = 0x29,
+ RT_CHANNEL_DOMAIN_WORLD_FCC3 = 0x30,
+ RT_CHANNEL_DOMAIN_WORLD_FCC4 = 0x31,
+ RT_CHANNEL_DOMAIN_WORLD_FCC5 = 0x32,
+ RT_CHANNEL_DOMAIN_WORLD_FCC6 = 0x33,
+ RT_CHANNEL_DOMAIN_FCC1_FCC7 = 0x34,
+ RT_CHANNEL_DOMAIN_WORLD_ETSI2 = 0x35,
+ RT_CHANNEL_DOMAIN_WORLD_ETSI3 = 0x36,
+ RT_CHANNEL_DOMAIN_MKK1_MKK2 = 0x37,
+ RT_CHANNEL_DOMAIN_MKK1_MKK3 = 0x38,
+ RT_CHANNEL_DOMAIN_FCC1_NCC1 = 0x39,
+ RT_CHANNEL_DOMAIN_FCC1_NCC2 = 0x40,
+ RT_CHANNEL_DOMAIN_GLOBAL_DOAMIN_2G = 0x41,
+ /* Add new channel plan above this line=============== */
+ RT_CHANNEL_DOMAIN_MAX,
+ RT_CHANNEL_DOMAIN_REALTEK_DEFINE = 0x7F,
+};
+
+enum { /* _RT_CHANNEL_DOMAIN_2G */
+ RT_CHANNEL_DOMAIN_2G_WORLD = 0x00, /* Worldwird 13 */
+ RT_CHANNEL_DOMAIN_2G_ETSI1 = 0x01, /* Europe */
+ RT_CHANNEL_DOMAIN_2G_FCC1 = 0x02, /* US */
+ RT_CHANNEL_DOMAIN_2G_MKK1 = 0x03, /* Japan */
+ RT_CHANNEL_DOMAIN_2G_ETSI2 = 0x04, /* France */
+ RT_CHANNEL_DOMAIN_2G_NULL = 0x05,
+ /* Add new channel plan above this line=============== */
+ RT_CHANNEL_DOMAIN_2G_MAX,
+};
+
+enum { /* _RT_CHANNEL_DOMAIN_5G */
+ RT_CHANNEL_DOMAIN_5G_NULL = 0x00,
+ RT_CHANNEL_DOMAIN_5G_ETSI1 = 0x01, /* Europe */
+ RT_CHANNEL_DOMAIN_5G_ETSI2 = 0x02, /* Australia, New Zealand */
+ RT_CHANNEL_DOMAIN_5G_ETSI3 = 0x03, /* Russia */
+ RT_CHANNEL_DOMAIN_5G_FCC1 = 0x04, /* US */
+ RT_CHANNEL_DOMAIN_5G_FCC2 = 0x05, /* FCC o/w DFS Channels */
+ RT_CHANNEL_DOMAIN_5G_FCC3 = 0x06, /* India, Mexico */
+ RT_CHANNEL_DOMAIN_5G_FCC4 = 0x07, /* Venezuela */
+ RT_CHANNEL_DOMAIN_5G_FCC5 = 0x08, /* China */
+ RT_CHANNEL_DOMAIN_5G_FCC6 = 0x09, /* Israel */
+ RT_CHANNEL_DOMAIN_5G_FCC7_IC1 = 0x0A, /* US, Canada */
+ RT_CHANNEL_DOMAIN_5G_KCC1 = 0x0B, /* Korea */
+ RT_CHANNEL_DOMAIN_5G_MKK1 = 0x0C, /* Japan */
+ RT_CHANNEL_DOMAIN_5G_MKK2 = 0x0D, /* Japan (W52, W53) */
+ RT_CHANNEL_DOMAIN_5G_MKK3 = 0x0E, /* Japan (W56) */
+ RT_CHANNEL_DOMAIN_5G_NCC1 = 0x0F, /* Taiwan */
+ RT_CHANNEL_DOMAIN_5G_NCC2 = 0x10, /* Taiwan o/w DFS */
+ /* Add new channel plan above this line=============== */
+ /* Driver Self Defined ===== */
+ RT_CHANNEL_DOMAIN_5G_FCC = 0x11,
+ RT_CHANNEL_DOMAIN_5G_JAPAN_NO_DFS = 0x12,
+ RT_CHANNEL_DOMAIN_5G_FCC4_NO_DFS = 0x13,
+ RT_CHANNEL_DOMAIN_5G_MAX,
+};
+
+#define rtw_is_channel_plan_valid(chplan) (chplan<RT_CHANNEL_DOMAIN_MAX || chplan == RT_CHANNEL_DOMAIN_REALTEK_DEFINE)
+
+struct rt_channel_plan {
+ unsigned char Channel[MAX_CHANNEL_NUM];
+ unsigned char Len;
+};
+
+struct rt_channel_plan_2g {
+ unsigned char Channel[MAX_CHANNEL_NUM_2G];
+ unsigned char Len;
+};
+
+struct rt_channel_plan_5g {
+ unsigned char Channel[MAX_CHANNEL_NUM_5G];
+ unsigned char Len;
+};
+
+struct rt_channel_plan_map {
+ unsigned char Index2G;
+ unsigned char Index5G;
+};
+
+enum Associated_AP {
+ atherosAP = 0,
+ broadcomAP = 1,
+ ciscoAP = 2,
+ marvellAP = 3,
+ ralinkAP = 4,
+ realtekAP = 5,
+ airgocapAP = 6,
+ unknownAP = 7,
+ maxAP,
+};
+
+enum { /* HT_IOT_PEER_E */
+ HT_IOT_PEER_UNKNOWN = 0,
+ HT_IOT_PEER_REALTEK = 1,
+ HT_IOT_PEER_REALTEK_92SE = 2,
+ HT_IOT_PEER_BROADCOM = 3,
+ HT_IOT_PEER_RALINK = 4,
+ HT_IOT_PEER_ATHEROS = 5,
+ HT_IOT_PEER_CISCO = 6,
+ HT_IOT_PEER_MERU = 7,
+ HT_IOT_PEER_MARVELL = 8,
+ HT_IOT_PEER_REALTEK_SOFTAP = 9,/* peer is RealTek SOFT_AP, by Bohn, 2009.12.17 */
+ HT_IOT_PEER_SELF_SOFTAP = 10, /* Self is SoftAP */
+ HT_IOT_PEER_AIRGO = 11,
+ HT_IOT_PEER_INTEL = 12,
+ HT_IOT_PEER_RTK_APCLIENT = 13,
+ HT_IOT_PEER_REALTEK_81XX = 14,
+ HT_IOT_PEER_REALTEK_WOW = 15,
+ HT_IOT_PEER_TENDA = 16,
+ HT_IOT_PEER_MAX = 17
+};
+
+enum SCAN_STATE {
+ SCAN_DISABLE = 0,
+ SCAN_START = 1,
+ SCAN_TXNULL = 2,
+ SCAN_PROCESS = 3,
+ SCAN_COMPLETE = 4,
+ SCAN_STATE_MAX,
+};
+
+struct mlme_handler {
+ char *str;
+ int (*func)(struct rtw_adapter *padapter, struct recv_frame *precv_frame);
+};
+
+struct action_handler {
+ unsigned int num;
+ char *str;
+ int (*func)(struct rtw_adapter *padapter, struct recv_frame *precv_frame);
+};
+
+struct ss_res {
+ int state;
+ int bss_cnt;
+ int channel_idx;
+ int scan_mode;
+ u8 ssid_num;
+ u8 ch_num;
+ struct cfg80211_ssid ssid[RTW_SSID_SCAN_AMOUNT];
+ struct rtw_ieee80211_channel ch[RTW_CHANNEL_SCAN_AMOUNT];
+};
+
+#define WIFI_FW_AUTH_NULL 0x00000100
+#define WIFI_FW_AUTH_STATE 0x00000200
+#define WIFI_FW_AUTH_SUCCESS 0x00000400
+
+#define WIFI_FW_ASSOC_STATE 0x00002000
+#define WIFI_FW_ASSOC_SUCCESS 0x00004000
+
+#define WIFI_FW_LINKING_STATE (WIFI_FW_AUTH_NULL | WIFI_FW_AUTH_STATE | WIFI_FW_AUTH_SUCCESS |WIFI_FW_ASSOC_STATE)
+
+struct FW_Sta_Info {
+ struct sta_info *psta;
+ u32 status;
+ u32 rx_pkt;
+ u32 retry;
+ unsigned char SupportedRates[NDIS_802_11_LENGTH_RATES_EX];
+};
+
+/*
+ * Usage:
+ * When one iface acted as AP mode and the other iface is STA mode and scanning,
+ * it should switch back to AP's operating channel periodically.
+ * Parameters info:
+ * When the driver scanned RTW_SCAN_NUM_OF_CH channels, it would switch back to AP's operating channel for
+ * RTW_STAY_AP_CH_MILLISECOND * SURVEY_TO milliseconds.
+ * Example:
+ * For chip supports 2.4G + 5GHz and AP mode is operating in channel 1,
+ * RTW_SCAN_NUM_OF_CH is 8, RTW_STAY_AP_CH_MILLISECOND is 3 and SURVEY_TO is 100.
+ * When it's STA mode gets set_scan command,
+ * it would
+ * 1. Doing the scan on channel 1.2.3.4.5.6.7.8
+ * 2. Back to channel 1 for 300 milliseconds
+ * 3. Go through doing site survey on channel 9.10.11.36.40.44.48.52
+ * 4. Back to channel 1 for 300 milliseconds
+ * 5. ... and so on, till survey done.
+ */
+
+struct mlme_ext_info {
+ u32 state;
+ u32 reauth_count;
+ u32 reassoc_count;
+ u32 link_count;
+ u32 auth_seq;
+ u32 auth_algo; /* 802.11 auth, could be open, shared, auto */
+ u32 authModeToggle;
+ u32 enc_algo;/* encrypt algorithm; */
+ u32 key_index; /* this is only valid for legendary wep, 0~3 for key id. */
+ u32 iv;
+ u8 chg_txt[128];
+ u16 aid;
+ u16 bcn_interval;
+ u16 capability;
+ u8 assoc_AP_vendor;
+ u8 slotTime;
+ u8 preamble_mode;
+ u8 WMM_enable;
+ u8 ERP_enable;
+ u8 ERP_IE;
+ u8 HT_enable;
+ u8 HT_caps_enable;
+ u8 HT_info_enable;
+ u8 HT_protection;
+ u8 turboMode_cts2self;
+ u8 turboMode_rtsen;
+ u8 SM_PS;
+ u8 ADDBA_retry_count;
+ u8 dialogToken;
+ /* Accept ADDBA Request */
+ bool bAcceptAddbaReq;
+ u8 bwmode_updated;
+ u8 hidden_ssid_mode;
+
+ struct ADDBA_request ADDBA_req;
+ struct WMM_para_element WMM_param;
+ struct ieee80211_ht_cap ht_cap;
+ struct ieee80211_ht_operation HT_info;
+ struct wlan_bssid_ex network;/* join network or bss_network, if in ap mode, it is the same to cur_network.network */
+ struct FW_Sta_Info FW_sta_info[NUM_STA];
+};
+
+/* The channel information about this channel including joining, scanning, and power constraints. */
+struct rt_channel_info {
+ u8 ChannelNum; /* The channel number. */
+ enum rt_scan_type ScanType; /* Scan type such as passive or active scan. */
+};
+
+int rtw_ch_set_search_ch23a(struct rt_channel_info *ch_set, const u32 ch);
+
+/* P2P_MAX_REG_CLASSES - Maximum number of regulatory classes */
+#define P2P_MAX_REG_CLASSES 10
+
+/* P2P_MAX_REG_CLASS_CHANNELS - Maximum number of channels per regulatory class */
+#define P2P_MAX_REG_CLASS_CHANNELS 20
+
+/* struct p2p_channels - List of supported channels */
+struct p2p_channels {
+ /* struct p2p_reg_class - Supported regulatory class */
+ struct p2p_reg_class {
+ /* reg_class - Regulatory class (IEEE 802.11-2007, Annex J) */
+ u8 reg_class;
+
+ /* channel - Supported channels */
+ u8 channel[P2P_MAX_REG_CLASS_CHANNELS];
+
+ /* channels - Number of channel entries in use */
+ size_t channels;
+ } reg_class[P2P_MAX_REG_CLASSES];
+
+ /* reg_classes - Number of reg_class entries in use */
+ size_t reg_classes;
+};
+
+struct p2p_oper_class_map {
+ enum hw_mode {IEEE80211G, IEEE80211A} mode;
+ u8 op_class;
+ u8 min_chan;
+ u8 max_chan;
+ u8 inc;
+ enum {
+ BW20, BW40PLUS, BW40MINUS
+ } bw;
+};
+
+struct mlme_ext_priv {
+ struct rtw_adapter *padapter;
+ u8 mlmeext_init;
+ atomic_t event_seq;
+ u16 mgnt_seq;
+
+ /* struct fw_priv fwpriv; */
+
+ unsigned char cur_channel;
+ unsigned char cur_bwmode;
+ unsigned char cur_ch_offset;/* PRIME_CHNL_OFFSET */
+ unsigned char cur_wireless_mode; /* NETWORK_TYPE */
+
+ unsigned char max_chan_nums;
+ struct rt_channel_info channel_set[MAX_CHANNEL_NUM];
+ struct p2p_channels channel_list;
+ unsigned char basicrate[NumRates];
+ unsigned char datarate[NumRates];
+
+ struct ss_res sitesurvey_res;
+ struct mlme_ext_info mlmext_info;/* for sta/adhoc mode, including current scanning/connecting/connected related info. */
+ /* for ap mode, network includes ap's cap_info */
+ struct timer_list survey_timer;
+ struct timer_list link_timer;
+ u16 chan_scan_time;
+
+ u8 scan_abort;
+ u8 tx_rate; /* TXRATE when USERATE is set. */
+
+ u32 retry; /* retry for issue probereq */
+
+ u64 TSFValue;
+
+ unsigned char bstart_bss;
+ u8 update_channel_plan_by_ap_done;
+ /* recv_decache check for Action_public frame */
+ u8 action_public_dialog_token;
+ u16 action_public_rxseq;
+ u8 active_keep_alive_check;
+};
+
+int init_mlme_ext_priv23a(struct rtw_adapter *padapter);
+int init_hw_mlme_ext23a(struct rtw_adapter *padapter);
+void free_mlme_ext_priv23a (struct mlme_ext_priv *pmlmeext);
+void init_mlme_ext_timer23a(struct rtw_adapter *padapter);
+void init_addba_retry_timer23a(struct sta_info *psta);
+struct xmit_frame *alloc_mgtxmitframe23a(struct xmit_priv *pxmitpriv);
+
+unsigned char networktype_to_raid23a(unsigned char network_type);
+u8 judge_network_type23a(struct rtw_adapter *padapter, unsigned char *rate,
+ int ratelen);
+void get_rate_set23a(struct rtw_adapter *padapter, unsigned char *pbssrate,
+ int *bssrate_len);
+void UpdateBrateTbl23a(struct rtw_adapter *padapter, u8 *mBratesOS);
+void Update23aTblForSoftAP(u8 *bssrateset, u32 bssratelen);
+
+u8 rtw_get_oper_ch23a(struct rtw_adapter *adapter);
+void rtw_set_oper_ch23a(struct rtw_adapter *adapter, u8 ch);
+u8 rtw_get_oper_bw23a(struct rtw_adapter *adapter);
+void rtw_set_oper_bw23a(struct rtw_adapter *adapter, u8 bw);
+u8 rtw_get_oper_ch23aoffset(struct rtw_adapter *adapter);
+void rtw_set_oper_ch23aoffset23a(struct rtw_adapter *adapter, u8 offset);
+
+void set_channel_bwmode23a(struct rtw_adapter *padapter, unsigned char channel,
+ unsigned char channel_offset, unsigned short bwmode);
+void SelectChannel23a(struct rtw_adapter *padapter, unsigned char channel);
+
+unsigned int decide_wait_for_beacon_timeout23a(unsigned int bcn_interval);
+
+void clear_cam_entry23a(struct rtw_adapter *padapter, u8 entry);
+
+void invalidate_cam_all23a(struct rtw_adapter *padapter);
+
+int allocate_fw_sta_entry23a(struct rtw_adapter *padapter);
+void flush_all_cam_entry23a(struct rtw_adapter *padapter);
+
+bool IsLegal5GChannel(struct rtw_adapter *Adapter, u8 channel);
+
+void update_network23a(struct wlan_bssid_ex *dst, struct wlan_bssid_ex *src,
+ struct rtw_adapter *padapter, bool update_ie);
+
+u8 *get_my_bssid23a(struct wlan_bssid_ex *pnetwork);
+
+bool is_client_associated_to_ap23a(struct rtw_adapter *padapter);
+bool is_client_associated_to_ibss23a(struct rtw_adapter *padapter);
+bool is_IBSS_empty23a(struct rtw_adapter *padapter);
+
+unsigned char check_assoc_AP23a(u8 *pframe, uint len);
+
+int WMM_param_handler23a(struct rtw_adapter *padapter, const u8 *p);
+void WMMOnAssocRsp23a(struct rtw_adapter *padapter);
+
+void HT_caps_handler23a(struct rtw_adapter *padapter, const u8 *p);
+void HT_info_handler23a(struct rtw_adapter *padapter, const u8 *p);
+void HTOnAssocRsp23a(struct rtw_adapter *padapter);
+
+void ERP_IE_handler23a(struct rtw_adapter *padapter, const u8 *p);
+void VCS_update23a(struct rtw_adapter *padapter, struct sta_info *psta);
+
+void update_beacon23a_info(struct rtw_adapter *padapter,
+ struct ieee80211_mgmt *mgmt, uint len,
+ struct sta_info *psta);
+int rtw_check_bcn_info23a(struct rtw_adapter *Adapter,
+ struct ieee80211_mgmt *mgmt, u32 packet_len);
+void update_IOT_info23a(struct rtw_adapter *padapter);
+void update_capinfo23a(struct rtw_adapter *Adapter, u16 updateCap);
+void update_wireless_mode23a(struct rtw_adapter *padapter);
+void update_tx_basic_rate23a(struct rtw_adapter *padapter, u8 modulation);
+void update_bmc_sta_support_rate23a(struct rtw_adapter *padapter, u32 mac_id);
+int update_sta_support_rate23a(struct rtw_adapter *padapter, u8 *pvar_ie,
+ uint var_ie_len, int cam_idx);
+
+/* for sta/adhoc mode */
+void update_sta_info23a(struct rtw_adapter *padapter, struct sta_info *psta);
+unsigned int update_basic_rate23a(unsigned char *ptn, unsigned int ptn_sz);
+unsigned int update_supported_rate23a(unsigned char *ptn, unsigned int ptn_sz);
+unsigned int update_MSC_rate23a(struct ieee80211_ht_cap *ht_cap);
+void Update_RA_Entry23a(struct rtw_adapter *padapter, struct sta_info *psta);
+void set_sta_rate23a(struct rtw_adapter *padapter, struct sta_info *psta);
+
+int receive_disconnect23a(struct rtw_adapter *padapter,
+ unsigned char *MacAddr, unsigned short reason);
+
+unsigned char get_highest_rate_idx23a(u32 mask);
+int support_short_GI23a(struct rtw_adapter *padapter,
+ struct ieee80211_ht_cap *ht_cap);
+bool is_ap_in_tkip23a(struct rtw_adapter *padapter);
+bool is_ap_in_wep23a(struct rtw_adapter *padapter);
+bool should_forbid_n_rate23a(struct rtw_adapter *padapter);
+
+void report_join_res23a(struct rtw_adapter *padapter, int res);
+void report_survey_event23a(struct rtw_adapter *padapter,
+ struct recv_frame *precv_frame);
+void report_surveydone_event23a(struct rtw_adapter *padapter);
+void report_del_sta_event23a(struct rtw_adapter *padapter,
+ unsigned char *MacAddr, unsigned short reason);
+void report_add_sta_event23a(struct rtw_adapter *padapter,
+ unsigned char *MacAddr, int cam_idx);
+
+int set_tx_beacon_cmd23a(struct rtw_adapter*padapter);
+unsigned int setup_beacon_frame(struct rtw_adapter *padapter,
+ unsigned char *beacon_frame);
+void update_mgnt_tx_rate23a(struct rtw_adapter *padapter, u8 rate);
+void update_mgntframe_attrib23a(struct rtw_adapter *padapter,
+ struct pkt_attrib *pattrib);
+void dump_mgntframe23a(struct rtw_adapter *padapter,
+ struct xmit_frame *pmgntframe);
+s32 dump_mgntframe23a_and_wait(struct rtw_adapter *padapter,
+ struct xmit_frame *pmgntframe, int timeout_ms);
+s32 dump_mgntframe23a_and_wait_ack23a(struct rtw_adapter *padapter,
+ struct xmit_frame *pmgntframe);
+
+void issue_beacon23a(struct rtw_adapter *padapter, int timeout_ms);
+int issue_nulldata23a(struct rtw_adapter *padapter, unsigned char *da,
+ unsigned int power_mode, int try_cnt, int wait_ms);
+int issue_qos_nulldata23a(struct rtw_adapter *padapter, unsigned char *da, u16 tid,
+ int try_cnt, int wait_ms);
+int issue_deauth23a(struct rtw_adapter *padapter, unsigned char *da,
+ unsigned short reason);
+void issue_action_spct_ch_switch23a(struct rtw_adapter *padapter, u8 *ra,
+ u8 new_ch, u8 ch_offset);
+void issue_action_BA23a(struct rtw_adapter *padapter,
+ const unsigned char *raddr,
+ unsigned char action, unsigned short status);
+int send_delba23a(struct rtw_adapter *padapter, u8 initiator, u8 *addr);
+int send_beacon23a(struct rtw_adapter *padapter);
+
+void mlmeext_joinbss_event_callback23a(struct rtw_adapter *padapter, int join_res);
+void mlmeext_sta_del_event_callback23a(struct rtw_adapter *padapter);
+void mlmeext_sta_add_event_callback23a(struct rtw_adapter *padapter, struct sta_info *psta);
+
+void linked_status_chk23a(struct rtw_adapter *padapter);
+
+#define set_survey_timer(mlmeext, ms) \
+ /*DBG_8723A("%s set_survey_timer(%p, %d)\n", __func__, (mlmeext), (ms));*/ \
+ mod_timer(&mlmeext->survey_timer, jiffies + msecs_to_jiffies(ms));
+
+#define set_link_timer(mlmeext, ms) \
+ /*DBG_8723A("%s set_link_timer(%p, %d)\n", __func__, (mlmeext), (ms));*/ \
+ mod_timer(&mlmeext->link_timer, jiffies + msecs_to_jiffies(ms));
+
+int cckrates_included23a(unsigned char *rate, int ratelen);
+int cckratesonly_included23a(unsigned char *rate, int ratelen);
+
+void process_addba_req23a(struct rtw_adapter *padapter, u8 *paddba_req, u8 *addr);
+
+void correct_TSF23a(struct rtw_adapter *padapter, struct mlme_ext_priv *pmlmeext);
+
+struct cmd_hdl {
+ uint parmsize;
+ int (*h2cfuns)(struct rtw_adapter *padapter, const u8 *pbuf);
+};
+
+
+int read_macreg_hdl(struct rtw_adapter *padapter, u8 *pbuf);
+int write_macreg_hdl(struct rtw_adapter *padapter, u8 *pbuf);
+int read_bbreg_hdl(struct rtw_adapter *padapter, u8 *pbuf);
+int write_bbreg_hdl(struct rtw_adapter *padapter, u8 *pbuf);
+int read_rfreg_hdl(struct rtw_adapter *padapter, u8 *pbuf);
+int write_rfreg_hdl(struct rtw_adapter *padapter, u8 *pbuf);
+
+
+int NULL_hdl23a(struct rtw_adapter *padapter, const u8 *pbuf);
+int join_cmd_hdl23a(struct rtw_adapter *padapter, const u8 *pbuf);
+int disconnect_hdl23a(struct rtw_adapter *padapter, const u8 *pbuf);
+int createbss_hdl23a(struct rtw_adapter *padapter, const u8 *pbuf);
+int setopmode_hdl23a(struct rtw_adapter *padapter, const u8 *pbuf);
+int sitesurvey_cmd_hdl23a(struct rtw_adapter *padapter, const u8 *pbuf);
+int setauth_hdl23a(struct rtw_adapter *padapter, const u8 *pbuf);
+int setkey_hdl23a(struct rtw_adapter *padapter, const u8 *pbuf);
+int set_stakey_hdl23a(struct rtw_adapter *padapter, const u8 *pbuf);
+int set_assocsta_hdl(struct rtw_adapter *padapter, const u8 *pbuf);
+int del_assocsta_hdl(struct rtw_adapter *padapter, const u8 *pbuf);
+int add_ba_hdl23a(struct rtw_adapter *padapter, const u8 *pbuf);
+
+int mlme_evt_hdl23a(struct rtw_adapter *padapter, const u8 *pbuf);
+int h2c_msg_hdl23a(struct rtw_adapter *padapter, const u8 *pbuf);
+int tx_beacon_hdl23a(struct rtw_adapter *padapter, const u8 *pbuf);
+int set_ch_hdl23a(struct rtw_adapter *padapter, const u8 *pbuf);
+int set_chplan_hdl23a(struct rtw_adapter *padapter, const u8 *pbuf);
+int led_blink_hdl23a(struct rtw_adapter *padapter, const u8 *pbuf);
+int set_csa_hdl23a(struct rtw_adapter *padapter, const u8 *pbuf); /* Kurt: Handling DFS channel switch announcement ie. */
+int tdls_hdl23a(struct rtw_adapter *padapter, const u8 *pbuf);
+
+#define GEN_DRV_CMD_HANDLER(size, cmd) {size, &cmd ## _hdl23a},
+#define GEN_MLME_EXT_HANDLER(size, cmd) {size, cmd},
+
+struct C2HEvent_Header {
+#ifdef __LITTLE_ENDIAN
+
+ unsigned int len:16;
+ unsigned int ID:8;
+ unsigned int seq:8;
+
+#elif defined(__BIG_ENDIAN)
+
+ unsigned int seq:8;
+ unsigned int ID:8;
+ unsigned int len:16;
+
+#else
+
+# error "Must be LITTLE or BIG Endian"
+
+#endif
+
+ unsigned int rsvd;
+};
+
+enum rtw_c2h_event {
+ GEN_EVT_CODE(_Read_MACREG) = 0, /*0*/
+ GEN_EVT_CODE(_Read_BBREG),
+ GEN_EVT_CODE(_Read_RFREG),
+ GEN_EVT_CODE(_Read_EEPROM),
+ GEN_EVT_CODE(_Read_EFUSE),
+ GEN_EVT_CODE(_Read_CAM), /*5*/
+ GEN_EVT_CODE(_Get_BasicRate),
+ GEN_EVT_CODE(_Get_DataRate),
+ GEN_EVT_CODE(_Survey), /*8*/
+ GEN_EVT_CODE(_SurveyDone), /*9*/
+
+ GEN_EVT_CODE(_JoinBss) , /*10*/
+ GEN_EVT_CODE(_AddSTA),
+ GEN_EVT_CODE(_DelSTA),
+ GEN_EVT_CODE(_AtimDone) ,
+ GEN_EVT_CODE(_TX_Report),
+ GEN_EVT_CODE(_CCX_Report), /*15*/
+ GEN_EVT_CODE(_DTM_Report),
+ GEN_EVT_CODE(_TX_Rate_Statistics),
+ GEN_EVT_CODE(_C2HLBK),
+ GEN_EVT_CODE(_FWDBG),
+ GEN_EVT_CODE(_C2HFEEDBACK), /*20*/
+ GEN_EVT_CODE(_ADDBA),
+ GEN_EVT_CODE(_C2HBCN),
+ GEN_EVT_CODE(_ReportPwrState), /* filen: only for PCIE, USB */
+ GEN_EVT_CODE(_CloseRF), /* filen: only for PCIE, work around ASPM */
+ MAX_C2HEVT
+};
+
+#endif
diff --git a/drivers/staging/rtl8723au/include/rtw_pwrctrl.h b/drivers/staging/rtl8723au/include/rtw_pwrctrl.h
new file mode 100644
index 000000000..599fed9b3
--- /dev/null
+++ b/drivers/staging/rtl8723au/include/rtw_pwrctrl.h
@@ -0,0 +1,241 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2012 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * 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.
+ *
+ ******************************************************************************/
+#ifndef __RTW_PWRCTRL_H_
+#define __RTW_PWRCTRL_H_
+
+#include <osdep_service.h>
+#include <drv_types.h>
+
+#define FW_PWR0 0
+#define FW_PWR1 1
+#define FW_PWR2 2
+#define FW_PWR3 3
+
+
+#define HW_PWR0 7
+#define HW_PWR1 6
+#define HW_PWR2 2
+#define HW_PWR3 0
+#define HW_PWR4 8
+
+#define FW_PWRMSK 0x7
+
+
+#define XMIT_ALIVE BIT(0)
+#define RECV_ALIVE BIT(1)
+#define CMD_ALIVE BIT(2)
+#define EVT_ALIVE BIT(3)
+
+enum Power_Mgnt {
+ PS_MODE_ACTIVE = 0,
+ PS_MODE_MIN,
+ PS_MODE_MAX,
+ PS_MODE_DTIM,
+ PS_MODE_VOIP,
+ PS_MODE_UAPSD_WMM,
+ PS_MODE_UAPSD,
+ PS_MODE_IBSS,
+ PS_MODE_WWLAN,
+ PM_Radio_Off,
+ PM_Card_Disable,
+ PS_MODE_NUM
+};
+
+
+/* BIT[2:0] = HW state
+ * BIT[3] = Protocol PS state, 0: active, 1: sleep state
+ * BIT[4] = sub-state
+ */
+
+#define PS_DPS BIT(0)
+#define PS_LCLK (PS_DPS)
+#define PS_RF_OFF BIT(1)
+#define PS_ALL_ON BIT(2)
+#define PS_ST_ACTIVE BIT(3)
+
+#define PS_ISR_ENABLE BIT(4)
+#define PS_IMR_ENABLE BIT(5)
+#define PS_ACK BIT(6)
+#define PS_TOGGLE BIT(7)
+
+#define PS_STATE_MASK (0x0F)
+#define PS_STATE_HW_MASK (0x07)
+#define PS_SEQ_MASK (0xc0)
+
+#define PS_STATE(x) (PS_STATE_MASK & (x))
+#define PS_STATE_HW(x) (PS_STATE_HW_MASK & (x))
+#define PS_SEQ(x) (PS_SEQ_MASK & (x))
+
+#define PS_STATE_S0 (PS_DPS)
+#define PS_STATE_S1 (PS_LCLK)
+#define PS_STATE_S2 (PS_RF_OFF)
+#define PS_STATE_S3 (PS_ALL_ON)
+#define PS_STATE_S4 ((PS_ST_ACTIVE) | (PS_ALL_ON))
+
+
+#define PS_IS_RF_ON(x) ((x) & (PS_ALL_ON))
+#define PS_IS_ACTIVE(x) ((x) & (PS_ST_ACTIVE))
+#define CLR_PS_STATE(x) ((x) = ((x) & (0xF0)))
+
+
+struct reportpwrstate_parm {
+ unsigned char mode;
+ unsigned char state; /* the CPWM value */
+ unsigned short rsvd;
+};
+
+#define LPS_DELAY_TIME (1*HZ) /* 1 sec */
+
+#define EXE_PWR_NONE 0x01
+#define EXE_PWR_IPS 0x02
+#define EXE_PWR_LPS 0x04
+
+/* RF state. */
+enum rt_rf_power_state {
+ rf_on, /* RF is on after RFSleep or RFOff */
+ rf_sleep, /* 802.11 Power Save mode */
+ rf_off, /* HW/SW Radio OFF or Inactive Power Save */
+ /* Add the new RF state above this line===== */
+ rf_max
+};
+
+/* RF Off Level for IPS or HW/SW radio off */
+#define RT_RF_OFF_LEVL_ASPM BIT(0) /* PCI ASPM */
+#define RT_RF_OFF_LEVL_CLK_REQ BIT(1) /* PCI clock request */
+#define RT_RF_OFF_LEVL_PCI_D3 BIT(2) /* PCI D3 mode */
+/* NIC halt, re-init hw params */
+#define RT_RF_OFF_LEVL_HALT_NIC BIT(3)
+/* FW free, re-download the FW */
+#define RT_RF_OFF_LEVL_FREE_FW BIT(4)
+#define RT_RF_OFF_LEVL_FW_32K BIT(5) /* FW in 32k */
+/* Always enable ASPM and Clock Req in initialization. */
+#define RT_RF_PS_LEVEL_ALWAYS_ASPM BIT(6)
+/* When LPS is on, disable 2R if no packet is received or transmittd. */
+#define RT_RF_LPS_DISALBE_2R BIT(30)
+#define RT_RF_LPS_LEVEL_ASPM BIT(31) /* LPS with ASPM */
+
+#define RT_IN_PS_LEVEL(ppsc, _PS_FLAG) \
+ ((ppsc->cur_ps_level & _PS_FLAG) ? true : false)
+#define RT_CLEAR_PS_LEVEL(ppsc, _PS_FLAG) \
+ (ppsc->cur_ps_level &= (~(_PS_FLAG)))
+#define RT_SET_PS_LEVEL(ppsc, _PS_FLAG) \
+ (ppsc->cur_ps_level |= _PS_FLAG)
+
+
+enum {
+ PSBBREG_RF0 = 0,
+ PSBBREG_RF1,
+ PSBBREG_RF2,
+ PSBBREG_AFE0,
+ PSBBREG_TOTALCNT
+};
+
+enum { /* for ips_mode */
+ IPS_NONE = 0,
+ IPS_NORMAL,
+ IPS_LEVEL_2,
+};
+
+struct pwrctrl_priv {
+ struct semaphore lock;
+ volatile u8 rpwm; /* requested power state for fw */
+ volatile u8 cpwm; /* fw current power state. updated when 1.
+ * read from HCPWM 2. driver lowers power level
+ */
+ volatile u8 tog; /* toggling */
+
+ u8 pwr_mode;
+ u8 smart_ps;
+ u8 bcn_ant_mode;
+
+ u8 bpower_saving;
+
+ u8 reg_rfoff;
+ u32 rfoff_reason;
+
+ /* RF OFF Level */
+ u32 cur_ps_level;
+ u32 reg_rfps_level;
+
+ uint ips_enter23a_cnts;
+ uint ips_leave23a_cnts;
+
+ u8 ips_mode;
+ u8 ips_mode_req; /* used to accept the mode setting request */
+ uint bips_processing;
+ unsigned long ips_deny_time; /* deny IPS when system time is smaller */
+ u8 ps_processing; /* used to mark whether in rtw_ps_processor23a */
+
+ u8 bLeisurePs;
+ u8 LpsIdleCount;
+ u8 power_mgnt;
+ u8 bFwCurrentInPSMode;
+ unsigned long DelayLPSLastTimeStamp;
+ u8 btcoex_rfon;
+
+ u8 bInSuspend;
+#ifdef CONFIG_8723AU_BT_COEXIST
+ u8 bAutoResume;
+ u8 autopm_cnt;
+#endif
+ u8 bSupportRemoteWakeup;
+ struct timer_list pwr_state_check_timer;
+ int pwr_state_check_interval;
+ u8 pwr_state_check_cnts;
+
+ enum rt_rf_power_state rf_pwrstate;/* cur power state */
+ enum rt_rf_power_state change_rfpwrstate;
+
+ u8 bkeepfwalive;
+ unsigned long PS_BBRegBackup[PSBBREG_TOTALCNT];
+};
+
+#define RTW_PWR_STATE_CHK_INTERVAL 2000
+
+#define _rtw_set_pwr_state_check_timer(pwrctrlpriv, ms) \
+ (mod_timer(&pwrctrlpriv->pwr_state_check_timer, jiffies + \
+ msecs_to_jiffies(ms)))
+
+#define rtw_set_pwr_state_check_timer(pwrctrlpriv) \
+ (_rtw_set_pwr_state_check_timer((pwrctrlpriv), \
+ (pwrctrlpriv)->pwr_state_check_interval))
+
+void rtw_init_pwrctrl_priv23a(struct rtw_adapter *adapter);
+void rtw_free_pwrctrl_priv(struct rtw_adapter *adapter);
+
+void rtw_set_ps_mode23a(struct rtw_adapter *padapter, u8 ps_mode,
+ u8 smart_ps, u8 bcn_ant_mode);
+void rtw_set_rpwm23a(struct rtw_adapter *padapter, u8 val8);
+void LeaveAllPowerSaveMode23a(struct rtw_adapter *adapter);
+void ips_enter23a(struct rtw_adapter *padapter);
+int ips_leave23a(struct rtw_adapter *padapter);
+
+void rtw_ps_processor23a(struct rtw_adapter *padapter);
+
+enum rt_rf_power_state RfOnOffDetect23a(struct rtw_adapter *adapter);
+
+s32 LPS_RF_ON_check23a(struct rtw_adapter *padapter, u32 delay_ms);
+void LPS_Enter23a(struct rtw_adapter *padapter);
+void LPS_Leave23a(struct rtw_adapter *padapter);
+
+void rtw_set_ips_deny23a(struct rtw_adapter *padapter, u32 ms);
+int _rtw_pwr_wakeup23a(struct rtw_adapter *padapter, u32 ips_deffer_ms,
+ const char *caller);
+#define rtw_pwr_wakeup(adapter) _rtw_pwr_wakeup23a(adapter, \
+ RTW_PWR_STATE_CHK_INTERVAL, __func__)
+int rtw_pm_set_ips23a(struct rtw_adapter *padapter, u8 mode);
+int rtw_pm_set_lps23a(struct rtw_adapter *padapter, u8 mode);
+
+#endif /* __RTL871X_PWRCTRL_H_ */
diff --git a/drivers/staging/rtl8723au/include/rtw_recv.h b/drivers/staging/rtl8723au/include/rtw_recv.h
new file mode 100644
index 000000000..dc784be3d
--- /dev/null
+++ b/drivers/staging/rtl8723au/include/rtw_recv.h
@@ -0,0 +1,307 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2012 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * 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.
+ *
+ ******************************************************************************/
+#ifndef _RTW_RECV_H_
+#define _RTW_RECV_H_
+
+#include <osdep_service.h>
+#include <drv_types.h>
+#include <Hal8723APhyCfg.h>
+
+#define NR_RECVFRAME 256
+
+#define MAX_RXFRAME_CNT 512
+#define MAX_RX_NUMBLKS (32)
+#define RECVFRAME_HDR_ALIGN 128
+
+#define SNAP_SIZE sizeof(struct ieee80211_snap_hdr)
+
+#define MAX_SUBFRAME_COUNT 64
+
+/* for Rx reordering buffer control */
+struct recv_reorder_ctrl {
+ struct rtw_adapter *padapter;
+ u8 enable;
+ u16 indicate_seq;/* wstart_b, init_value=0xffff */
+ u16 wend_b;
+ u8 wsize_b;
+ struct rtw_queue pending_recvframe_queue;
+ struct timer_list reordering_ctrl_timer;
+};
+
+struct stainfo_rxcache {
+ u16 tid_rxseq[16];
+/*
+ unsigned short tid0_rxseq;
+ unsigned short tid1_rxseq;
+ unsigned short tid2_rxseq;
+ unsigned short tid3_rxseq;
+ unsigned short tid4_rxseq;
+ unsigned short tid5_rxseq;
+ unsigned short tid6_rxseq;
+ unsigned short tid7_rxseq;
+ unsigned short tid8_rxseq;
+ unsigned short tid9_rxseq;
+ unsigned short tid10_rxseq;
+ unsigned short tid11_rxseq;
+ unsigned short tid12_rxseq;
+ unsigned short tid13_rxseq;
+ unsigned short tid14_rxseq;
+ unsigned short tid15_rxseq;
+*/
+};
+
+struct smooth_rssi_data {
+ u32 elements[100]; /* array to store values */
+ u32 index; /* index to current array to store */
+ u32 total_num; /* num of valid elements */
+ u32 total_val; /* sum of valid elements */
+};
+
+struct signal_stat {
+ u8 update_req; /* used to indicate */
+ u8 avg_val; /* avg of valid elements */
+ u32 total_num; /* num of valid elements */
+ u32 total_val; /* sum of valid elements */
+};
+
+struct phy_info {
+ u8 RxPWDBAll;
+ u8 SignalQuality; /* in 0-100 index. */
+ u8 RxMIMOSignalQuality[RF_PATH_MAX]; /* EVM */
+ u8 RxMIMOSignalStrength[RF_PATH_MAX];/* 0~100 */
+ s8 RxPower; /* in dBm Translate from PWdB */
+ /* Real power in dBm for this packet, no beautification and aggregation.
+ * Keep this raw info to be used for the other procedures.
+ */
+ s8 RecvSignalPower;
+ u8 BTRxRSSIPercentage;
+ u8 SignalStrength; /* in 0-100 index. */
+ u8 RxPwr[RF_PATH_MAX];/* per-path's pwdb */
+ u8 RxSNR[RF_PATH_MAX];/* per-path's SNR */
+};
+
+
+struct rx_pkt_attrib {
+ u16 pkt_len;
+ u8 physt;
+ u8 drvinfo_sz;
+ u8 shift_sz;
+ u8 hdrlen; /* the WLAN Header Len */
+ u8 amsdu;
+ u8 qos;
+ u8 priority;
+ u8 pw_save;
+ u8 mdata;
+ u16 seq_num;
+ u8 frag_num;
+ u8 mfrag;
+ u8 order;
+ u8 privacy; /* in frame_ctrl field */
+ u8 bdecrypted;
+ /* when 0 indicate no encrypt. when non-zero, indicate the algorith */
+ u32 encrypt;
+ u8 iv_len;
+ u8 icv_len;
+ u8 crc_err;
+ u8 icv_err;
+
+ u16 eth_type;
+
+ u8 dst[ETH_ALEN];
+ u8 src[ETH_ALEN];
+ u8 ta[ETH_ALEN];
+ u8 ra[ETH_ALEN];
+ u8 bssid[ETH_ALEN];
+
+ u8 ack_policy;
+
+ u8 tcpchk_valid; /* 0: invalid, 1: valid */
+ u8 ip_chkrpt; /* 0: incorrect, 1: correct */
+ u8 tcp_chkrpt; /* 0: incorrect, 1: correct */
+ u8 key_index;
+
+ u8 mcs_rate;
+ u8 rxht;
+ u8 sgi;
+ u8 pkt_rpt_type;
+ u32 MacIDValidEntry[2]; /* 64 bits present 64 entry. */
+ struct phy_info phy_info;
+};
+
+/* These definition is used for Rx packet reordering. */
+#define SN_LESS(a, b) (((a-b) & 0x800) != 0)
+#define SN_EQUAL(a, b) (a == b)
+#define REORDER_WAIT_TIME (50) /* (ms) */
+
+#define RECVBUFF_ALIGN_SZ 8
+
+#define RXDESC_SIZE 24
+#define RXDESC_OFFSET RXDESC_SIZE
+
+struct recv_stat {
+ __le32 rxdw0;
+ __le32 rxdw1;
+ __le32 rxdw2;
+ __le32 rxdw3;
+ __le32 rxdw4;
+ __le32 rxdw5;
+};
+
+/* accesser of recv_priv: rtw_recv_entry23a(dispatch / passive level); \
+ * recv_thread(passive) ; returnpkt(dispatch) ; halt(passive) ;
+ *
+ * using enter_critical section to protect
+ */
+struct recv_priv {
+ spinlock_t lock;
+
+ struct rtw_queue free_recv_queue;
+ struct rtw_queue recv_pending_queue;
+ struct rtw_queue uc_swdec_pending_queue;
+
+ int free_recvframe_cnt;
+
+ struct rtw_adapter *adapter;
+
+ u32 bIsAnyNonBEPkts;
+ u64 rx_bytes;
+ u64 rx_pkts;
+ u64 rx_drop;
+ u64 last_rx_bytes;
+
+ uint rx_icv_err;
+ uint rx_largepacket_crcerr;
+ uint rx_smallpacket_crcerr;
+ uint rx_middlepacket_crcerr;
+
+ /* u8 *pallocated_urb_buf; */
+ u8 rx_pending_cnt;
+
+ struct urb *int_in_urb;
+
+ u8 *int_in_buf;
+
+ struct tasklet_struct irq_prepare_beacon_tasklet;
+ struct tasklet_struct recv_tasklet;
+ struct sk_buff_head free_recv_skb_queue;
+ struct sk_buff_head rx_skb_queue;
+ u8 *precv_buf;
+
+ /* For display the phy informatiom */
+ s8 rxpwdb;
+ u8 signal_strength;
+ u8 signal_qual;
+ u8 noise;
+ int RxSNRdB[2];
+ s8 RxRssi[2];
+ int FalseAlmCnt_all;
+
+ struct timer_list signal_stat_timer;
+ u32 signal_stat_sampling_interval;
+ /* u32 signal_stat_converging_constant; */
+ struct signal_stat signal_qual_data;
+ struct signal_stat signal_strength_data;
+};
+
+#define rtw_set_signal_stat_timer(recvpriv) \
+ mod_timer(&(recvpriv)->signal_stat_timer, jiffies + \
+ msecs_to_jiffies((recvpriv)->signal_stat_sampling_interval))
+
+struct sta_recv_priv {
+ spinlock_t lock;
+ int option;
+
+ /* struct rtw_queue blk_strms[MAX_RX_NUMBLKS]; */
+ struct rtw_queue defrag_q; /* keeping the fragment frame until defrag */
+
+ struct stainfo_rxcache rxcache;
+
+ /* uint sta_rx_bytes; */
+ /* uint sta_rx_pkts; */
+ /* uint sta_rx_fail; */
+
+};
+
+
+struct recv_buf {
+ struct list_head list;
+
+ struct rtw_adapter *adapter;
+
+ struct urb *purb;
+ struct sk_buff *pskb;
+};
+
+/* head ----->
+ *
+ * data ----->
+ *
+ * payload
+ *
+ * tail ----->
+ *
+ * end ----->
+ *
+ * len = (unsigned int )(tail - data);
+ *
+ */
+struct recv_frame {
+ struct list_head list;
+ struct sk_buff *pkt;
+
+ struct rtw_adapter *adapter;
+
+ struct rx_pkt_attrib attrib;
+
+ struct sta_info *psta;
+
+ /* for A-MPDU Rx reordering buffer control */
+ struct recv_reorder_ctrl *preorder_ctrl;
+};
+
+/* get a free recv_frame from pfree_recv_queue */
+struct recv_frame *rtw_alloc_recvframe23a(struct rtw_queue *pfree_recv_queue);
+int rtw_free_recvframe23a(struct recv_frame *precvframe);
+
+int rtw_enqueue_recvframe23a(struct recv_frame *precvframe, struct rtw_queue *queue);
+
+u32 rtw_free_uc_swdec_pending_queue23a(struct rtw_adapter *adapter);
+
+int rtw_enqueue_recvbuf23a_to_head(struct recv_buf *precvbuf, struct rtw_queue *queue);
+int rtw_enqueue_recvbuf23a(struct recv_buf *precvbuf, struct rtw_queue *queue);
+struct recv_buf *rtw_dequeue_recvbuf23a(struct rtw_queue *queue);
+
+void rtw_reordering_ctrl_timeout_handler23a(unsigned long pcontext);
+
+static inline s32 translate_percentage_to_dbm(u32 SignalStrengthIndex)
+{
+ s32 SignalPower; /* in dBm. */
+
+ /* Translate to dBm (x=0.5y-95). */
+ SignalPower = (s32)((SignalStrengthIndex + 1) >> 1);
+ SignalPower -= 95;
+
+ return SignalPower;
+}
+
+
+struct sta_info;
+
+void _rtw_init_sta_recv_priv23a(struct sta_recv_priv *psta_recvpriv);
+
+void mgt_dispatcher23a(struct rtw_adapter *padapter,
+ struct recv_frame *precv_frame);
+
+#endif
diff --git a/drivers/staging/rtl8723au/include/rtw_rf.h b/drivers/staging/rtl8723au/include/rtw_rf.h
new file mode 100644
index 000000000..a7de71413
--- /dev/null
+++ b/drivers/staging/rtl8723au/include/rtw_rf.h
@@ -0,0 +1,102 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2011 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * 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.
+ *
+ ******************************************************************************/
+#ifndef __RTW_RF_H_
+#define __RTW_RF_H_
+
+#include <rtw_cmd.h>
+
+#define OFDM_PHY 1
+#define MIXED_PHY 2
+#define CCK_PHY 3
+
+#define NumRates (13)
+
+/* slot time for 11g */
+#define SHORT_SLOT_TIME 9
+#define NON_SHORT_SLOT_TIME 20
+
+/* We now define the max channels in each channel plan. */
+#define MAX_CHANNEL_NUM_2G 14
+#define MAX_CHANNEL_NUM_5G 24
+#define MAX_CHANNEL_NUM 38/* 14+24 */
+
+/* define NUM_REGULATORYS 21 */
+#define NUM_REGULATORYS 1
+
+/* Country codes */
+#define USA 0x555320
+#define EUROPE 0x1 /* temp, should be provided later */
+#define JAPAN 0x2 /* temp, should be provided later */
+
+struct regulatory_class {
+ u32 starting_freq; /* MHz, */
+ u8 channel_set[MAX_CHANNEL_NUM];
+ u8 channel_cck_power[MAX_CHANNEL_NUM];/* dbm */
+ u8 channel_ofdm_power[MAX_CHANNEL_NUM];/* dbm */
+ u8 txpower_limit; /* dbm */
+ u8 channel_spacing; /* MHz */
+ u8 modem;
+};
+
+enum {
+ cESS = 0x0001,
+ cIBSS = 0x0002,
+ cPollable = 0x0004,
+ cPollReq = 0x0008,
+ cPrivacy = 0x0010,
+ cShortPreamble = 0x0020,
+ cPBCC = 0x0040,
+ cChannelAgility = 0x0080,
+ cSpectrumMgnt = 0x0100,
+ cQos = 0x0200, /* For HCCA, use with CF-Pollable and CF-PollReq */
+ cShortSlotTime = 0x0400,
+ cAPSD = 0x0800,
+ cRM = 0x1000, /* RRM (Radio Request Measurement) */
+ cDSSS_OFDM = 0x2000,
+ cDelayedBA = 0x4000,
+ cImmediateBA = 0x8000,
+};
+
+enum {
+ PREAMBLE_LONG = 1,
+ PREAMBLE_AUTO = 2,
+ PREAMBLE_SHORT = 3,
+};
+
+/* Bandwidth Offset */
+#define HAL_PRIME_CHNL_OFFSET_DONT_CARE 0
+#define HAL_PRIME_CHNL_OFFSET_LOWER 1
+#define HAL_PRIME_CHNL_OFFSET_UPPER 2
+
+/* Represent Channel Width in HT Capabilities */
+enum ht_channel_width {
+ HT_CHANNEL_WIDTH_20 = 0,
+ HT_CHANNEL_WIDTH_40 = 1,
+ HT_CHANNEL_WIDTH_80 = 2,
+ HT_CHANNEL_WIDTH_160 = 3,
+ HT_CHANNEL_WIDTH_10 = 4,
+};
+
+/* 2007/11/15 MH Define different RF type. */
+enum {
+ RF_1T2R = 0,
+ RF_2T4R = 1,
+ RF_2T2R = 2,
+ RF_1T1R = 3,
+ RF_2T2R_GREEN = 4,
+ RF_819X_MAX_TYPE = 5,
+};
+
+#endif /* _RTL8711_RF_H_ */
diff --git a/drivers/staging/rtl8723au/include/rtw_security.h b/drivers/staging/rtl8723au/include/rtw_security.h
new file mode 100644
index 000000000..624a9d788
--- /dev/null
+++ b/drivers/staging/rtl8723au/include/rtw_security.h
@@ -0,0 +1,331 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2011 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * 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.
+ *
+ ******************************************************************************/
+#ifndef __RTW_SECURITY_H_
+#define __RTW_SECURITY_H_
+
+#include <osdep_service.h>
+#include <drv_types.h>
+#include <net/lib80211.h>
+
+
+#define is_wep_enc(alg) (alg == WLAN_CIPHER_SUITE_WEP40 || \
+ alg == WLAN_CIPHER_SUITE_WEP104)
+
+#define SHA256_MAC_LEN 32
+#define AES_BLOCK_SIZE 16
+#define AES_PRIV_SIZE (4 * 44)
+
+enum ENCRYP_PROTOCOL {
+ ENCRYP_PROTOCOL_OPENSYS, /* open system */
+ ENCRYP_PROTOCOL_WEP, /* WEP */
+ ENCRYP_PROTOCOL_WPA, /* WPA */
+ ENCRYP_PROTOCOL_WPA2, /* WPA2 */
+ ENCRYP_PROTOCOL_MAX
+};
+
+#ifndef Ndis802_11AuthModeWPA2
+#define Ndis802_11AuthModeWPA2 (Ndis802_11AuthModeWPANone + 1)
+#endif
+
+#ifndef Ndis802_11AuthModeWPA2PSK
+#define Ndis802_11AuthModeWPA2PSK (Ndis802_11AuthModeWPANone + 2)
+#endif
+
+union pn48 {
+ u64 val;
+
+#ifdef __LITTLE_ENDIAN
+
+struct {
+ u8 TSC0;
+ u8 TSC1;
+ u8 TSC2;
+ u8 TSC3;
+ u8 TSC4;
+ u8 TSC5;
+ u8 TSC6;
+ u8 TSC7;
+} _byte_;
+
+#elif defined(__BIG_ENDIAN)
+
+struct {
+ u8 TSC7;
+ u8 TSC6;
+ u8 TSC5;
+ u8 TSC4;
+ u8 TSC3;
+ u8 TSC2;
+ u8 TSC1;
+ u8 TSC0;
+} _byte_;
+#else
+#error Need BIG or LITTLE endian
+
+#endif
+
+};
+
+union Keytype {
+ u8 skey[16];
+ u32 lkey[4];
+};
+
+struct rtw_wep_key {
+ u8 key[WLAN_KEY_LEN_WEP104 + 1]; /* 14 */
+ u16 keylen;
+};
+
+struct rt_pmkid_list {
+ u8 bUsed;
+ u8 Bssid[6];
+ u8 PMKID[16];
+ u8 SsidBuf[33];
+ u8 *ssid_octet;
+ u16 ssid_length;
+};
+
+struct security_priv {
+ u32 dot11AuthAlgrthm; /* 802.11 auth, could be open, shared,
+ * 8021x and authswitch */
+ u32 dot11PrivacyAlgrthm; /* This specifies the privacy for
+ * shared auth. algorithm.
+ */
+ /* WEP */
+ u32 dot11PrivacyKeyIndex; /* this is only valid for legendary
+ * wep, 0~3 for key id. (tx key index)
+ */
+ struct rtw_wep_key wep_key[NUM_WEP_KEYS];
+
+ u32 dot118021XGrpPrivacy; /* specify the privacy algthm.
+ * used for Grp key
+ */
+ u32 dot118021XGrpKeyid; /* key id used for Grp Key
+ * (tx key index)
+ */
+ union Keytype dot118021XGrpKey[4];/* 802.1x Grp Key, inx0 and inx1 */
+ union Keytype dot118021XGrptxmickey[4];
+ union Keytype dot118021XGrprxmickey[4];
+ union pn48 dot11Grptxpn; /* PN48 used for Grp Key xmit.*/
+ union pn48 dot11Grprxpn; /* PN48 used for Grp Key recv.*/
+
+#ifdef CONFIG_8723AU_AP_MODE
+ /* extend security capabilities for AP_MODE */
+ unsigned int dot8021xalg;/* 0:disable, 1:psk, 2:802.1x */
+ unsigned int wpa_psk;/* 0:disable, bit(0): WPA, bit(1):WPA2 */
+ unsigned int wpa_group_cipher;
+ unsigned int wpa2_group_cipher;
+ unsigned int wpa_pairwise_cipher;
+ unsigned int wpa2_pairwise_cipher;
+#endif
+
+ u8 wps_ie[MAX_WPS_IE_LEN];/* added in assoc req */
+ int wps_ie_len;
+ unsigned int binstallGrpkey:1;
+ unsigned int busetkipkey:1;
+ unsigned int bcheck_grpkey:1;
+ unsigned int hw_decrypted:1;
+ u32 ndisauthtype; /* enum ndis_802_11_auth_mode */
+ u32 ndisencryptstatus; /* NDIS_802_11_ENCRYPTION_STATUS */
+ struct wlan_bssid_ex sec_bss; /* for joinbss (h2c buffer) usage */
+ u8 assoc_info[600];
+ u8 szofcapability[256]; /* for wpa2 usage */
+ u8 oidassociation[512]; /* for wpa/wpa2 usage */
+ u8 supplicant_ie[256]; /* store sta security information element */
+
+ /* for tkip countermeasure */
+ unsigned long last_mic_err_time;
+ u8 btkip_countermeasure;
+ u8 btkip_wait_report;
+ unsigned long btkip_countermeasure_time;
+
+ /* For WPA2 Pre-Authentication. */
+ struct rt_pmkid_list PMKIDList[NUM_PMKID_CACHE];
+ u8 PMKIDIndex;
+ u8 bWepDefaultKeyIdxSet;
+};
+
+struct sha256_state {
+ u64 length;
+ u32 state[8], curlen;
+ u8 buf[64];
+};
+
+#define GET_ENCRY_ALGO(psecuritypriv, psta, encry_algo, bmcst)\
+do {\
+ switch (psecuritypriv->dot11AuthAlgrthm) {\
+ case dot11AuthAlgrthm_Open:\
+ case dot11AuthAlgrthm_Shared:\
+ case dot11AuthAlgrthm_Auto:\
+ encry_algo = psecuritypriv->dot11PrivacyAlgrthm;\
+ break;\
+ case dot11AuthAlgrthm_8021X:\
+ if (bmcst)\
+ encry_algo = psecuritypriv->dot118021XGrpPrivacy;\
+ else\
+ encry_algo = psta->dot118021XPrivacy;\
+ break;\
+ } \
+} while (0)
+
+#define GET_TKIP_PN(iv, dot11txpn)\
+do {\
+ dot11txpn._byte_.TSC0 = iv[2];\
+ dot11txpn._byte_.TSC1 = iv[0];\
+ dot11txpn._byte_.TSC2 = iv[4];\
+ dot11txpn._byte_.TSC3 = iv[5];\
+ dot11txpn._byte_.TSC4 = iv[6];\
+ dot11txpn._byte_.TSC5 = iv[7];\
+} while (0)
+
+#define ROL32(A, n) (((A) << (n)) | (((A)>>(32-(n))) & ((1UL << (n)) - 1)))
+#define ROR32(A, n) ROL32((A), 32-(n))
+
+struct mic_data {
+ u32 K0, K1; /* Key */
+ u32 L, R; /* Current state */
+ u32 M; /* Message accumulator (single word) */
+ u32 nBytesInM; /* # bytes in M */
+};
+
+extern const u32 Te0[256];
+extern const u32 Te1[256];
+extern const u32 Te2[256];
+extern const u32 Te3[256];
+extern const u32 Te4[256];
+extern const u32 Td0[256];
+extern const u32 Td1[256];
+extern const u32 Td2[256];
+extern const u32 Td3[256];
+extern const u32 Td4[256];
+extern const u32 rcon[10];
+extern const u8 Td4s[256];
+extern const u8 rcons[10];
+
+#define RCON(i) (rcons[(i)] << 24)
+
+static inline u32 rotr(u32 val, int bits)
+{
+ return (val >> bits) | (val << (32 - bits));
+}
+
+#define TE0(i) Te0[((i) >> 24) & 0xff]
+#define TE1(i) rotr(Te0[((i) >> 16) & 0xff], 8)
+#define TE2(i) rotr(Te0[((i) >> 8) & 0xff], 16)
+#define TE3(i) rotr(Te0[(i) & 0xff], 24)
+#define TE41(i) ((Te0[((i) >> 24) & 0xff] << 8) & 0xff000000)
+#define TE42(i) (Te0[((i) >> 16) & 0xff] & 0x00ff0000)
+#define TE43(i) (Te0[((i) >> 8) & 0xff] & 0x0000ff00)
+#define TE44(i) ((Te0[(i) & 0xff] >> 8) & 0x000000ff)
+#define TE421(i) ((Te0[((i) >> 16) & 0xff] << 8) & 0xff000000)
+#define TE432(i) (Te0[((i) >> 8) & 0xff] & 0x00ff0000)
+#define TE443(i) (Te0[(i) & 0xff] & 0x0000ff00)
+#define TE414(i) ((Te0[((i) >> 24) & 0xff] >> 8) & 0x000000ff)
+#define TE4(i) ((Te0[(i)] >> 8) & 0x000000ff)
+
+#define TD0(i) Td0[((i) >> 24) & 0xff]
+#define TD1(i) rotr(Td0[((i) >> 16) & 0xff], 8)
+#define TD2(i) rotr(Td0[((i) >> 8) & 0xff], 16)
+#define TD3(i) rotr(Td0[(i) & 0xff], 24)
+#define TD41(i) (Td4s[((i) >> 24) & 0xff] << 24)
+#define TD42(i) (Td4s[((i) >> 16) & 0xff] << 16)
+#define TD43(i) (Td4s[((i) >> 8) & 0xff] << 8)
+#define TD44(i) (Td4s[(i) & 0xff])
+#define TD0_(i) Td0[(i) & 0xff]
+#define TD1_(i) rotr(Td0[(i) & 0xff], 8)
+#define TD2_(i) rotr(Td0[(i) & 0xff], 16)
+#define TD3_(i) rotr(Td0[(i) & 0xff], 24)
+
+#define GETU32(pt) (((u32)(pt)[0] << 24) ^ ((u32)(pt)[1] << 16) ^ \
+ ((u32)(pt)[2] << 8) ^ ((u32)(pt)[3]))
+
+#define PUTU32(ct, st) { \
+(ct)[0] = (u8)((st) >> 24); (ct)[1] = (u8)((st) >> 16); \
+(ct)[2] = (u8)((st) >> 8); (ct)[3] = (u8)(st); }
+
+#define WPA_GET_BE32(a) ((((u32) (a)[0]) << 24) | (((u32) (a)[1]) << 16) | \
+ (((u32) (a)[2]) << 8) | ((u32) (a)[3]))
+
+#define WPA_PUT_LE16(a, val) \
+ do { \
+ (a)[1] = ((u16) (val)) >> 8; \
+ (a)[0] = ((u16) (val)) & 0xff; \
+ } while (0)
+
+#define WPA_PUT_BE32(a, val) \
+ do { \
+ (a)[0] = (u8) ((((u32) (val)) >> 24) & 0xff); \
+ (a)[1] = (u8) ((((u32) (val)) >> 16) & 0xff); \
+ (a)[2] = (u8) ((((u32) (val)) >> 8) & 0xff); \
+ (a)[3] = (u8) (((u32) (val)) & 0xff); \
+ } while (0)
+
+#define WPA_PUT_BE64(a, val) \
+ do { \
+ (a)[0] = (u8) (((u64) (val)) >> 56); \
+ (a)[1] = (u8) (((u64) (val)) >> 48); \
+ (a)[2] = (u8) (((u64) (val)) >> 40); \
+ (a)[3] = (u8) (((u64) (val)) >> 32); \
+ (a)[4] = (u8) (((u64) (val)) >> 24); \
+ (a)[5] = (u8) (((u64) (val)) >> 16); \
+ (a)[6] = (u8) (((u64) (val)) >> 8); \
+ (a)[7] = (u8) (((u64) (val)) & 0xff); \
+ } while (0)
+
+/* ===== start - public domain SHA256 implementation ===== */
+
+/* This is based on SHA256 implementation in LibTomCrypt that was released into
+ * public domain by Tom St Denis. */
+
+/* the K array */
+static const unsigned long K[64] = {
+ 0x428a2f98UL, 0x71374491UL, 0xb5c0fbcfUL, 0xe9b5dba5UL, 0x3956c25bUL,
+ 0x59f111f1UL, 0x923f82a4UL, 0xab1c5ed5UL, 0xd807aa98UL, 0x12835b01UL,
+ 0x243185beUL, 0x550c7dc3UL, 0x72be5d74UL, 0x80deb1feUL, 0x9bdc06a7UL,
+ 0xc19bf174UL, 0xe49b69c1UL, 0xefbe4786UL, 0x0fc19dc6UL, 0x240ca1ccUL,
+ 0x2de92c6fUL, 0x4a7484aaUL, 0x5cb0a9dcUL, 0x76f988daUL, 0x983e5152UL,
+ 0xa831c66dUL, 0xb00327c8UL, 0xbf597fc7UL, 0xc6e00bf3UL, 0xd5a79147UL,
+ 0x06ca6351UL, 0x14292967UL, 0x27b70a85UL, 0x2e1b2138UL, 0x4d2c6dfcUL,
+ 0x53380d13UL, 0x650a7354UL, 0x766a0abbUL, 0x81c2c92eUL, 0x92722c85UL,
+ 0xa2bfe8a1UL, 0xa81a664bUL, 0xc24b8b70UL, 0xc76c51a3UL, 0xd192e819UL,
+ 0xd6990624UL, 0xf40e3585UL, 0x106aa070UL, 0x19a4c116UL, 0x1e376c08UL,
+ 0x2748774cUL, 0x34b0bcb5UL, 0x391c0cb3UL, 0x4ed8aa4aUL, 0x5b9cca4fUL,
+ 0x682e6ff3UL, 0x748f82eeUL, 0x78a5636fUL, 0x84c87814UL, 0x8cc70208UL,
+ 0x90befffaUL, 0xa4506cebUL, 0xbef9a3f7UL, 0xc67178f2UL
+};
+
+void rtw_secmicsetkey23a(struct mic_data *pmicdata, u8 *key);
+void rtw_secmicappend23abyte23a(struct mic_data *pmicdata, u8 b);
+void rtw_secmicappend23a(struct mic_data *pmicdata, u8 *src, u32 nbBytes);
+void rtw_secgetmic23a(struct mic_data *pmicdata, u8 *dst);
+
+void rtw_seccalctkipmic23a(u8 *key, u8 *header, u8 *data, u32 data_len,
+ u8 *Miccode, u8 priorityi);
+
+int rtw_aes_encrypt23a(struct rtw_adapter *padapter,
+ struct xmit_frame *pxmitframe);
+int rtw_tkip_encrypt23a(struct rtw_adapter *padapter,
+ struct xmit_frame *pxmitframe);
+void rtw_wep_encrypt23a(struct rtw_adapter *padapter,
+ struct xmit_frame *pxmitframe);
+int rtw_aes_decrypt23a(struct rtw_adapter *padapter,
+ struct recv_frame *precvframe);
+int rtw_tkip_decrypt23a(struct rtw_adapter *padapter,
+ struct recv_frame *precvframe);
+void rtw_wep_decrypt23a(struct rtw_adapter *padapter, struct recv_frame *precvframe);
+
+void rtw_use_tkipkey_handler23a(void *FunctionContext);
+
+#endif /* __RTL871X_SECURITY_H_ */
diff --git a/drivers/staging/rtl8723au/include/rtw_sreset.h b/drivers/staging/rtl8723au/include/rtw_sreset.h
new file mode 100644
index 000000000..60fa8296e
--- /dev/null
+++ b/drivers/staging/rtl8723au/include/rtw_sreset.h
@@ -0,0 +1,36 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2012 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * 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.
+ *
+ ******************************************************************************/
+#ifndef _RTW_SRESET_C_
+#define _RTW_SRESET_C_
+
+#include <osdep_service.h>
+#include <drv_types.h>
+
+struct sreset_priv {
+ struct mutex silentreset_mutex;
+ u8 silent_reset_inprogress;
+ unsigned long last_tx_time;
+ unsigned long last_tx_complete_time;
+};
+
+#include <rtl8723a_hal.h>
+
+void rtw_sreset_init(struct rtw_adapter *padapter);
+void rtw_sreset_reset_value(struct rtw_adapter *padapter);
+bool rtw_sreset_inprogress(struct rtw_adapter *padapter);
+void sreset_set_trigger_point(struct rtw_adapter *padapter, s32 tgp);
+void rtw_sreset_reset(struct rtw_adapter *active_adapter);
+
+#endif
diff --git a/drivers/staging/rtl8723au/include/rtw_version.h b/drivers/staging/rtl8723au/include/rtw_version.h
new file mode 100644
index 000000000..c947733a3
--- /dev/null
+++ b/drivers/staging/rtl8723au/include/rtw_version.h
@@ -0,0 +1 @@
+#define DRIVERVERSION "v4.1.6_7336.20130426"
diff --git a/drivers/staging/rtl8723au/include/rtw_xmit.h b/drivers/staging/rtl8723au/include/rtw_xmit.h
new file mode 100644
index 000000000..2b7d6d082
--- /dev/null
+++ b/drivers/staging/rtl8723au/include/rtw_xmit.h
@@ -0,0 +1,385 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2011 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * 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.
+ *
+ ******************************************************************************/
+#ifndef _RTW_XMIT_H_
+#define _RTW_XMIT_H_
+
+#include <osdep_service.h>
+#include <drv_types.h>
+
+#define MAX_XMITBUF_SZ 2048
+#define NR_XMITBUFF 4
+
+#define XMITBUF_ALIGN_SZ 512
+
+/* xmit extension buff defination */
+#define MAX_XMIT_EXTBUF_SZ 1536
+#define NR_XMIT_EXTBUFF 32
+
+#define MAX_NUMBLKS 1
+
+#define XMIT_VO_QUEUE 0
+#define XMIT_VI_QUEUE 1
+#define XMIT_BE_QUEUE 2
+#define XMIT_BK_QUEUE 3
+
+#define VO_QUEUE_INX 0
+#define VI_QUEUE_INX 1
+#define BE_QUEUE_INX 2
+#define BK_QUEUE_INX 3
+#define BCN_QUEUE_INX 4
+#define MGT_QUEUE_INX 5
+#define HIGH_QUEUE_INX 6
+#define TXCMD_QUEUE_INX 7
+
+#define HW_QUEUE_ENTRY 8
+
+#define WEP_IV(pattrib_iv, dot11txpn, keyidx) \
+do { \
+ pattrib_iv[0] = dot11txpn._byte_.TSC0; \
+ pattrib_iv[1] = dot11txpn._byte_.TSC1; \
+ pattrib_iv[2] = dot11txpn._byte_.TSC2; \
+ pattrib_iv[3] = ((keyidx & 0x3) << 6); \
+ dot11txpn.val = (dot11txpn.val == 0xffffff) ? 0 : \
+ (dot11txpn.val+1); \
+} while (0)
+
+#define TKIP_IV(pattrib_iv, dot11txpn, keyidx) \
+do { \
+ pattrib_iv[0] = dot11txpn._byte_.TSC1; \
+ pattrib_iv[1] = (dot11txpn._byte_.TSC1 | 0x20) & 0x7f; \
+ pattrib_iv[2] = dot11txpn._byte_.TSC0; \
+ pattrib_iv[3] = BIT(5) | ((keyidx & 0x3)<<6); \
+ pattrib_iv[4] = dot11txpn._byte_.TSC2; \
+ pattrib_iv[5] = dot11txpn._byte_.TSC3; \
+ pattrib_iv[6] = dot11txpn._byte_.TSC4; \
+ pattrib_iv[7] = dot11txpn._byte_.TSC5; \
+ dot11txpn.val = dot11txpn.val == 0xffffffffffffULL ? 0 : \
+ (dot11txpn.val+1); \
+} while (0)
+
+#define AES_IV(pattrib_iv, dot11txpn, keyidx)\
+do { \
+ pattrib_iv[0] = dot11txpn._byte_.TSC0; \
+ pattrib_iv[1] = dot11txpn._byte_.TSC1; \
+ pattrib_iv[2] = 0; \
+ pattrib_iv[3] = BIT(5) | ((keyidx & 0x3) << 6); \
+ pattrib_iv[4] = dot11txpn._byte_.TSC2; \
+ pattrib_iv[5] = dot11txpn._byte_.TSC3; \
+ pattrib_iv[6] = dot11txpn._byte_.TSC4; \
+ pattrib_iv[7] = dot11txpn._byte_.TSC5; \
+ dot11txpn.val = dot11txpn.val == 0xffffffffffffULL ? 0 : \
+ (dot11txpn.val+1); \
+} while (0)
+
+#define HWXMIT_ENTRY 4
+
+#define TXDESC_SIZE 32
+
+#define PACKET_OFFSET_SZ 8
+#define TXDESC_OFFSET (TXDESC_SIZE + PACKET_OFFSET_SZ)
+
+struct tx_desc {
+ /* DWORD 0 */
+ __le32 txdw0;
+ __le32 txdw1;
+ __le32 txdw2;
+ __le32 txdw3;
+ __le32 txdw4;
+ __le32 txdw5;
+ __le32 txdw6;
+ __le32 txdw7;
+};
+
+union txdesc {
+ struct tx_desc txdesc;
+ unsigned int value[TXDESC_SIZE>>2];
+};
+
+struct hw_xmit {
+ struct rtw_queue *sta_queue;
+ int accnt;
+};
+
+/* reduce size */
+struct pkt_attrib {
+ u16 type;
+ u8 bswenc;
+ u8 dhcp_pkt;
+ u16 ether_type;
+ u16 seqnum;
+ u16 pkt_hdrlen; /* the original 802.3 pkt header len */
+ u16 hdrlen; /* the WLAN Header Len */
+ u32 pktlen; /* the original 802.3 pkt raw_data len */
+ u32 last_txcmdsz;
+ u32 encrypt; /* when 0 indicate no encrypt. */
+ u8 nr_frags;
+ u8 iv_len;
+ u8 icv_len;
+ u8 iv[18];
+ u8 icv[16];
+ u8 priority;
+ u8 ack_policy;
+ u8 mac_id;
+ u8 vcs_mode; /* virtual carrier sense method */
+ u8 dst[ETH_ALEN];
+ u8 src[ETH_ALEN];
+ u8 ta[ETH_ALEN];
+ u8 ra[ETH_ALEN];
+ u8 key_idx;
+ u8 qos_en;
+ u8 ht_en;
+ u8 raid;/* rate adpative id */
+ u8 bwmode;
+ u8 ch_offset;/* PRIME_CHNL_OFFSET */
+ u8 sgi;/* short GI */
+ u8 ampdu_en;/* tx ampdu enable */
+ u8 mdata;/* more data bit */
+ u8 pctrl;/* per packet txdesc control enable */
+ u8 triggered;/* for ap mode handling Power Saving sta */
+ u8 qsel;
+ u8 eosp;
+ u8 rate;
+ u8 retry_ctrl;
+ struct sta_info *psta;
+};
+
+#define WLANHDR_OFFSET 64
+
+#define NULL_FRAMETAG 0x0
+#define DATA_FRAMETAG 0x01
+#define L2_FRAMETAG 0x02
+#define MGNT_FRAMETAG 0x03
+#define AMSDU_FRAMETAG 0x04
+
+#define EII_FRAMETAG 0x05
+#define IEEE8023_FRAMETAG 0x06
+
+#define MP_FRAMETAG 0x07
+
+#define TXAGG_FRAMETAG 0x08
+
+struct submit_ctx {
+ u32 timeout_ms; /* <0: not synchronous, 0: wait forever,
+ * >0: up to ms waiting
+ */
+ int status; /* status for operation */
+ struct completion done;
+};
+
+enum {
+ RTW_SCTX_SUBMITTED = -1,
+ RTW_SCTX_DONE_SUCCESS = 0,
+ RTW_SCTX_DONE_UNKNOWN,
+ RTW_SCTX_DONE_TIMEOUT,
+ RTW_SCTX_DONE_BUF_ALLOC,
+ RTW_SCTX_DONE_BUF_FREE,
+ RTW_SCTX_DONE_WRITE_PORT_ERR,
+ RTW_SCTX_DONE_TX_DESC_NA,
+ RTW_SCTX_DONE_TX_DENY,
+ RTW_SCTX_DONE_CCX_PKT_FAIL,
+ RTW_SCTX_DONE_DRV_STOP,
+ RTW_SCTX_DONE_DEV_REMOVE,
+};
+
+void rtw_sctx_init23a(struct submit_ctx *sctx, int timeout_ms);
+int rtw_sctx_wait23a(struct submit_ctx *sctx);
+void rtw23a_sctx_done_err(struct submit_ctx **sctx, int status);
+
+struct xmit_buf {
+ struct list_head list, list2;
+ struct rtw_adapter *padapter;
+
+ u8 *pallocated_buf;
+ u8 *pbuf;
+ void *priv_data;
+
+ u16 ext_tag; /* 0: Normal xmitbuf, 1: extension xmitbuf. */
+ u16 flags;
+ u32 alloc_sz;
+ u32 len;
+ struct submit_ctx *sctx;
+ u32 ff_hwaddr;
+ struct urb *pxmit_urb[8];
+ u8 bpending[8];
+ int last[8];
+#if defined(DBG_XMIT_BUF) || defined(DBG_XMIT_BUF_EXT)
+ u8 no;
+#endif
+};
+
+struct xmit_frame {
+ struct list_head list;
+ struct pkt_attrib attrib;
+ struct sk_buff *pkt;
+ int frame_tag;
+ struct rtw_adapter *padapter;
+ u8 *buf_addr;
+ struct xmit_buf *pxmitbuf;
+
+ s8 pkt_offset;
+
+ u8 ack_report;
+
+ u8 ext_tag; /* 0:data, 1:mgmt */
+};
+
+struct tx_servq {
+ struct list_head tx_pending;
+ struct rtw_queue sta_pending;
+ int qcnt;
+};
+
+struct sta_xmit_priv {
+ spinlock_t lock;
+ int option;
+ int apsd_setting; /* When bit mask is on, the associated edca
+ * queue supports APSD.
+ */
+ struct tx_servq be_q; /* priority == 0,3 */
+ struct tx_servq bk_q; /* priority == 1,2 */
+ struct tx_servq vi_q; /* priority == 4,5 */
+ struct tx_servq vo_q; /* priority == 6,7 */
+ struct list_head legacy_dz;
+ struct list_head apsd;
+ u16 txseq_tid[16];
+};
+
+struct hw_txqueue {
+ volatile int head;
+ volatile int tail;
+ volatile int free_sz; /* in units of 64 bytes */
+ volatile int free_cmdsz;
+ volatile int txsz[8];
+ uint ff_hwaddr;
+ uint cmd_hwaddr;
+ int ac_tag;
+};
+
+struct agg_pkt_info {
+ u16 offset;
+ u16 pkt_len;
+};
+
+struct xmit_priv {
+ spinlock_t lock;
+
+ struct semaphore xmit_sema;
+ struct semaphore terminate_xmitthread_sema;
+
+ struct rtw_queue be_pending;
+ struct rtw_queue bk_pending;
+ struct rtw_queue vi_pending;
+ struct rtw_queue vo_pending;
+ struct rtw_queue bm_pending;
+
+ int free_xmitframe_cnt;
+ struct rtw_queue free_xmit_queue;
+
+ int free_xframe_ext_cnt;
+ struct rtw_queue free_xframe_ext_queue;
+
+ uint frag_len;
+
+ struct rtw_adapter *adapter;
+
+ u64 tx_bytes;
+ u64 tx_pkts;
+ u64 tx_drop;
+ u64 last_tx_bytes;
+ u64 last_tx_pkts;
+
+ struct hw_xmit *hwxmits;
+ u8 hwxmit_entry;
+ u8 vcs;
+ u8 nqos_ssn;
+
+ u8 wmm_para_seq[4];/* sequence for wmm ac parameter strength from
+ * large to small. it's value is 0->vo, 1->vi,
+ * 2->be, 3->bk.
+ */
+
+ struct semaphore tx_retevt;/* all tx return event; */
+
+ struct tasklet_struct xmit_tasklet;
+
+ struct rtw_queue free_xmitbuf_queue;
+ struct list_head xmitbuf_list; /* track buffers for cleanup */
+ struct rtw_queue pending_xmitbuf_queue;
+ uint free_xmitbuf_cnt;
+
+ struct rtw_queue free_xmit_extbuf_queue;
+ struct list_head xmitextbuf_list; /* track buffers for cleanup */
+ uint free_xmit_extbuf_cnt;
+
+ int ack_tx;
+ struct mutex ack_tx_mutex;
+ struct submit_ctx ack_tx_ops;
+ spinlock_t lock_sctx;
+};
+
+struct xmit_buf *rtw_alloc_xmitbuf23a_ext(struct xmit_priv *pxmitpriv);
+s32 rtw_free_xmitbuf_ext23a(struct xmit_priv *pxmitpriv,
+ struct xmit_buf *pxmitbuf);
+
+struct xmit_buf *rtw_alloc_xmitbuf23a(struct xmit_priv *pxmitpriv);
+s32 rtw_free_xmitbuf23a(struct xmit_priv *pxmitpriv, struct xmit_buf *pxmitbuf);
+
+void rtw_count_tx_stats23a(struct rtw_adapter *padapter,
+ struct xmit_frame *pxmitframe, int sz);
+void rtw_update_protection23a(struct rtw_adapter *padapter, u8 *ie, uint ie_len);
+struct xmit_frame *rtw_alloc_xmitframe23a_ext(struct xmit_priv *pxmitpriv);
+struct xmit_frame *rtw_alloc_xmitframe23a_once(struct xmit_priv *pxmitpriv);
+s32 rtw_free_xmitframe23a(struct xmit_priv *pxmitpriv,
+ struct xmit_frame *pxmitframe);
+void rtw_free_xmitframe_queue23a(struct xmit_priv *pxmitpriv, struct rtw_queue *pframequeue);
+struct tx_servq *rtw_get_sta_pending23a(struct rtw_adapter *padapter,
+ struct sta_info *psta, int up, u8 *ac);
+s32 rtw_xmitframe_enqueue23a(struct rtw_adapter *padapter,
+ struct xmit_frame *pxmitframe);
+struct xmit_frame *rtw_dequeue_xframe23a(struct xmit_priv *pxmitpriv,
+ struct hw_xmit *phwxmit_i, int entry);
+s32 rtw_xmit23a_classifier(struct rtw_adapter *padapter,
+ struct xmit_frame *pxmitframe);
+s32 rtw_xmitframe_coalesce23a(struct rtw_adapter *padapter, struct sk_buff *pkt,
+ struct xmit_frame *pxmitframe);
+s32 _rtw_init_hw_txqueue(struct hw_txqueue *phw_txqueue, u8 ac_tag);
+void _rtw_init_sta_xmit_priv23a(struct sta_xmit_priv *psta_xmitpriv);
+
+s32 rtw_txframes_pending23a(struct rtw_adapter *padapter);
+s32 rtw_txframes_sta_ac_pending23a(struct rtw_adapter *padapter,
+ struct pkt_attrib *pattrib);
+void rtw_init_hwxmits23a(struct hw_xmit *phwxmit, int entry);
+int _rtw_init_xmit_priv23a(struct xmit_priv *pxmitpriv,
+ struct rtw_adapter *padapter);
+void _rtw_free_xmit_priv23a(struct xmit_priv *pxmitpriv);
+void rtw_alloc_hwxmits23a(struct rtw_adapter *padapter);
+void rtw_free_hwxmits23a(struct rtw_adapter *padapter);
+int rtw_xmit23a(struct rtw_adapter *padapter, struct sk_buff *pkt);
+#if defined(CONFIG_8723AU_AP_MODE)
+int xmitframe_enqueue_for_sleeping_sta23a(struct rtw_adapter *padapter,
+ struct xmit_frame *pxmitframe);
+void stop_sta_xmit23a(struct rtw_adapter *padapter, struct sta_info *psta);
+void wakeup_sta_to_xmit23a(struct rtw_adapter *padapter, struct sta_info *psta);
+void xmit_delivery_enabled_frames23a(struct rtw_adapter *padapter,
+ struct sta_info *psta);
+#endif
+u8 qos_acm23a(u8 acm_mask, u8 priority);
+u32 rtw_get_ff_hwaddr23a(struct xmit_frame *pxmitframe);
+int rtw_ack_tx_wait23a(struct xmit_priv *pxmitpriv, u32 timeout_ms);
+
+/* include after declaring struct xmit_buf, in order to avoid warning */
+#include <xmit_osdep.h>
+
+#endif /* _RTL871X_XMIT_H_ */
diff --git a/drivers/staging/rtl8723au/include/sta_info.h b/drivers/staging/rtl8723au/include/sta_info.h
new file mode 100644
index 000000000..c756b4f7f
--- /dev/null
+++ b/drivers/staging/rtl8723au/include/sta_info.h
@@ -0,0 +1,373 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2011 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * 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.
+ *
+ ******************************************************************************/
+#ifndef __STA_INFO_H_
+#define __STA_INFO_H_
+
+#include <osdep_service.h>
+#include <drv_types.h>
+#include <wifi.h>
+
+#define IBSS_START_MAC_ID 2
+#define NUM_STA 32
+#define NUM_ACL 16
+
+
+/* if mode ==0, then the sta is allowed once the addr is hit. */
+/* if mode ==1, then the sta is rejected once the addr is non-hit. */
+struct rtw_wlan_acl_node {
+ struct list_head list;
+ u8 addr[ETH_ALEN];
+ u8 valid;
+};
+
+/* mode=0, disable */
+/* mode=1, accept unless in deny list */
+/* mode=2, deny unless in accept list */
+struct wlan_acl_pool {
+ int mode;
+ int num;
+ struct rtw_wlan_acl_node aclnode[NUM_ACL];
+ struct rtw_queue acl_node_q;
+};
+
+struct rssi_sta {
+ s32 UndecoratedSmoothedPWDB;
+ s32 UndecoratedSmoothedCCK;
+ s32 UndecoratedSmoothedOFDM;
+ u64 PacketMap;
+ u8 ValidBit;
+};
+
+struct stainfo_stats {
+ u64 rx_mgnt_pkts;
+ u64 rx_beacon_pkts;
+ u64 rx_probereq_pkts;
+ u64 rx_probersp_pkts;
+ u64 rx_probersp_bm_pkts;
+ u64 rx_probersp_uo_pkts;
+ u64 rx_ctrl_pkts;
+ u64 rx_data_pkts;
+
+ u64 last_rx_mgnt_pkts;
+ u64 last_rx_beacon_pkts;
+ u64 last_rx_probereq_pkts;
+ u64 last_rx_probersp_pkts;
+ u64 last_rx_probersp_bm_pkts;
+ u64 last_rx_probersp_uo_pkts;
+ u64 last_rx_ctrl_pkts;
+ u64 last_rx_data_pkts;
+
+ u64 rx_bytes;
+ u64 rx_drops;
+
+ u64 tx_pkts;
+ u64 tx_bytes;
+ u64 tx_drops;
+
+};
+
+struct sta_info {
+ spinlock_t lock;
+ struct list_head list; /* free_sta_queue */
+ struct list_head hash_list; /* sta_hash */
+ struct rtw_adapter *padapter;
+
+ struct sta_xmit_priv sta_xmitpriv;
+ struct sta_recv_priv sta_recvpriv;
+
+ struct rtw_queue sleep_q;
+ unsigned int sleepq_len;
+
+ uint state;
+ uint aid;
+ uint mac_id;
+ uint qos_option;
+ u8 hwaddr[ETH_ALEN];
+
+ uint ieee8021x_blocked; /* 0: allowed, 1:blocked */
+ u32 dot118021XPrivacy; /* aes, tkip... */
+ union Keytype dot11tkiptxmickey;
+ union Keytype dot11tkiprxmickey;
+ union Keytype dot118021x_UncstKey;
+ union pn48 dot11txpn; /* PN48 used for Unicast xmit. */
+ union pn48 dot11rxpn; /* PN48 used for Unicast recv. */
+
+
+ u8 bssrateset[16];
+ u32 bssratelen;
+ s32 rssi;
+ s32 signal_quality;
+
+ u8 cts2self;
+ u8 rtsen;
+
+ u8 raid;
+ u8 init_rate;
+ u32 ra_mask;
+ u8 wireless_mode; /* NETWORK_TYPE */
+ struct stainfo_stats sta_stats;
+
+ /* for A-MPDU TX, ADDBA timeout check */
+ struct timer_list addba_retry_timer;
+
+ /* for A-MPDU Rx reordering buffer control */
+ struct recv_reorder_ctrl recvreorder_ctrl[16];
+
+ /* for A-MPDU Tx */
+ /* unsigned char ampdu_txen_bitmap; */
+ u16 BA_starting_seqctrl[16];
+
+ struct ht_priv htpriv;
+
+ /* Notes: */
+ /* STA_Mode: */
+ /* curr_network(mlme_priv/security_priv/qos/ht) + sta_info: (STA & AP) CAP/INFO */
+ /* scan_q: AP CAP/INFO */
+
+ /* AP_Mode: */
+ /* curr_network(mlme_priv/security_priv/qos/ht) : AP CAP/INFO */
+ /* sta_info: (AP & STA) CAP/INFO */
+
+ struct list_head asoc_list;
+ struct list_head auth_list;
+
+ unsigned int expire_to;
+ unsigned int auth_seq;
+ unsigned int authalg;
+ unsigned char chg_txt[128];
+
+ u16 capability;
+ int flags;
+
+ int dot8021xalg;/* 0:disable, 1:psk, 2:802.1x */
+ int wpa_psk;/* 0:disable, bit(0): WPA, bit(1):WPA2 */
+ int wpa_group_cipher;
+ int wpa2_group_cipher;
+ int wpa_pairwise_cipher;
+ int wpa2_pairwise_cipher;
+
+ u8 bpairwise_key_installed;
+
+ u8 wpa_ie[32];
+
+ u8 nonerp_set;
+ u8 no_short_slot_time_set;
+ u8 no_short_preamble_set;
+ u8 no_ht_gf_set;
+ u8 no_ht_set;
+ u8 ht_20mhz_set;
+
+ unsigned int tx_ra_bitmap;
+ u8 qos_info;
+
+ u8 max_sp_len;
+ u8 uapsd_bk;/* BIT(0): Delivery enabled, BIT(1): Trigger enabled */
+ u8 uapsd_be;
+ u8 uapsd_vi;
+ u8 uapsd_vo;
+
+ u8 has_legacy_ac;
+ unsigned int sleepq_ac_len;
+
+ /* p2p priv data */
+ u8 is_p2p_device;
+ u8 p2p_status_code;
+
+ u8 keep_alive_trycnt;
+
+ /* p2p client info */
+ u8 dev_addr[ETH_ALEN];
+ u8 dev_cap;
+ u16 config_methods;
+ u8 primary_dev_type[8];
+ u8 num_of_secdev_type;
+ u8 secdev_types_list[32];/* 32/8 == 4; */
+ u16 dev_name_len;
+ u8 dev_name[32];
+ u8 *passoc_req;
+ u32 assoc_req_len;
+
+ /* for DM */
+ struct rssi_sta rssi_stat;
+
+ /* */
+ /* ================ODM Relative Info======================= */
+ /* Please be care, dont declare too much structure here. It will cost memory * STA support num. */
+ /* */
+ /* */
+ /* 2011/10/20 MH Add for ODM STA info. */
+ /* */
+ /* Driver Write */
+ u8 bValid; /* record the sta status link or not? */
+ u8 rssi_level; /* for Refresh RA mask */
+ /* ODM Write */
+ /* 1 PHY_STATUS_INFO */
+ u8 RSSI_Path[4]; /* */
+ u8 RSSI_Ave;
+ u8 RXEVM[4];
+ u8 RXSNR[4];
+
+ /* ODM Write */
+ /* 1 TX_INFO (may changed by IC) */
+ /* ================ODM Relative Info======================= */
+ /* */
+
+ /* To store the sequence number of received management frame */
+ u16 RxMgmtFrameSeqNum;
+};
+
+#define sta_rx_pkts(sta) \
+ (sta->sta_stats.rx_mgnt_pkts \
+ + sta->sta_stats.rx_ctrl_pkts \
+ + sta->sta_stats.rx_data_pkts)
+
+#define sta_last_rx_pkts(sta) \
+ (sta->sta_stats.last_rx_mgnt_pkts \
+ + sta->sta_stats.last_rx_ctrl_pkts \
+ + sta->sta_stats.last_rx_data_pkts)
+
+#define sta_rx_data_pkts(sta) \
+ (sta->sta_stats.rx_data_pkts)
+
+#define sta_last_rx_data_pkts(sta) \
+ (sta->sta_stats.last_rx_data_pkts)
+
+#define sta_rx_mgnt_pkts(sta) \
+ (sta->sta_stats.rx_mgnt_pkts)
+
+#define sta_last_rx_mgnt_pkts(sta) \
+ (sta->sta_stats.last_rx_mgnt_pkts)
+
+#define sta_rx_beacon_pkts(sta) \
+ (sta->sta_stats.rx_beacon_pkts)
+
+#define sta_last_rx_beacon_pkts(sta) \
+ (sta->sta_stats.last_rx_beacon_pkts)
+
+#define sta_rx_probereq_pkts(sta) \
+ (sta->sta_stats.rx_probereq_pkts)
+
+#define sta_last_rx_probereq_pkts(sta) \
+ (sta->sta_stats.last_rx_probereq_pkts)
+
+#define sta_rx_probersp_pkts(sta) \
+ (sta->sta_stats.rx_probersp_pkts)
+
+#define sta_last_rx_probersp_pkts(sta) \
+ (sta->sta_stats.last_rx_probersp_pkts)
+
+#define sta_rx_probersp_bm_pkts(sta) \
+ (sta->sta_stats.rx_probersp_bm_pkts)
+
+#define sta_last_rx_probersp_bm_pkts(sta) \
+ (sta->sta_stats.last_rx_probersp_bm_pkts)
+
+#define sta_rx_probersp_uo_pkts(sta) \
+ (sta->sta_stats.rx_probersp_uo_pkts)
+
+#define sta_last_rx_probersp_uo_pkts(sta) \
+ (sta->sta_stats.last_rx_probersp_uo_pkts)
+
+#define sta_update_last_rx_pkts(sta) \
+ do { \
+ sta->sta_stats.last_rx_mgnt_pkts = sta->sta_stats.rx_mgnt_pkts; \
+ sta->sta_stats.last_rx_beacon_pkts = sta->sta_stats.rx_beacon_pkts; \
+ sta->sta_stats.last_rx_probereq_pkts = sta->sta_stats.rx_probereq_pkts; \
+ sta->sta_stats.last_rx_probersp_pkts = sta->sta_stats.rx_probersp_pkts; \
+ sta->sta_stats.last_rx_probersp_bm_pkts = sta->sta_stats.rx_probersp_bm_pkts; \
+ sta->sta_stats.last_rx_probersp_uo_pkts = sta->sta_stats.rx_probersp_uo_pkts; \
+ sta->sta_stats.last_rx_ctrl_pkts = sta->sta_stats.rx_ctrl_pkts; \
+ sta->sta_stats.last_rx_data_pkts = sta->sta_stats.rx_data_pkts; \
+ } while (0)
+
+#define STA_RX_PKTS_ARG(sta) \
+ sta->sta_stats.rx_mgnt_pkts \
+ , sta->sta_stats.rx_ctrl_pkts \
+ , sta->sta_stats.rx_data_pkts
+
+#define STA_LAST_RX_PKTS_ARG(sta) \
+ sta->sta_stats.last_rx_mgnt_pkts, \
+ sta->sta_stats.last_rx_ctrl_pkts, \
+ sta->sta_stats.last_rx_data_pkts
+
+#define STA_RX_PKTS_DIFF_ARG(sta) \
+ sta->sta_stats.rx_mgnt_pkts - sta->sta_stats.last_rx_mgnt_pkts, \
+ sta->sta_stats.rx_ctrl_pkts - sta->sta_stats.last_rx_ctrl_pkts, \
+ sta->sta_stats.rx_data_pkts - sta->sta_stats.last_rx_data_pkts
+
+#define STA_PKTS_FMT "(m:%llu, c:%llu, d:%llu)"
+
+struct sta_priv {
+ spinlock_t sta_hash_lock;
+ struct list_head sta_hash[NUM_STA];
+ int asoc_sta_count;
+
+ struct rtw_adapter *padapter;
+ struct list_head asoc_list;
+ struct list_head auth_list;
+ spinlock_t asoc_list_lock;
+ spinlock_t auth_list_lock;
+ u8 asoc_list_cnt;
+ u8 auth_list_cnt;
+
+ unsigned int auth_to; /* sec, time to expire in authenticating. */
+ unsigned int assoc_to; /* sec, time to expire before associating. */
+ unsigned int expire_to; /* sec , time to expire after associated. */
+
+ /* pointers to STA info; based on allocated AID or NULL if AID free
+ * AID is in the range 1-2007, so sta_aid[0] corresponders to AID 1
+ * and so on
+ */
+ struct sta_info *sta_aid[NUM_STA];
+
+ u16 sta_dz_bitmap;/* only support 15 stations, staion aid bitmap
+ * for sleeping sta. */
+ u16 tim_bitmap;/* only support 15 stations,
+ * aid=0~15 mapping bit0~bit15 */
+
+ u16 max_num_sta;
+
+ struct wlan_acl_pool acl_list;
+};
+
+static inline u32 wifi_mac_hash(const u8 *mac)
+{
+ u32 x;
+
+ x = mac[0];
+ x = (x << 2) ^ mac[1];
+ x = (x << 2) ^ mac[2];
+ x = (x << 2) ^ mac[3];
+ x = (x << 2) ^ mac[4];
+ x = (x << 2) ^ mac[5];
+
+ x ^= x >> 8;
+ x = x & (NUM_STA - 1);
+
+ return x;
+}
+
+int _rtw_init_sta_priv23a(struct sta_priv *pstapriv);
+int _rtw_free_sta_priv23a(struct sta_priv *pstapriv);
+
+struct sta_info *rtw_alloc_stainfo23a(struct sta_priv *pstapriv, const u8 *hwaddr, gfp_t gfp);
+int rtw_free_stainfo23a(struct rtw_adapter *padapter, struct sta_info *psta);
+void rtw_free_all_stainfo23a(struct rtw_adapter *padapter);
+struct sta_info *rtw_get_stainfo23a(struct sta_priv *pstapriv, const u8 *hwaddr);
+int rtw_init_bcmc_stainfo23a(struct rtw_adapter *padapter);
+struct sta_info *rtw_get_bcmc_stainfo23a(struct rtw_adapter *padapter);
+bool rtw_access_ctrl23a(struct rtw_adapter *padapter, u8 *mac_addr);
+
+#endif /* _STA_INFO_H_ */
diff --git a/drivers/staging/rtl8723au/include/usb_ops.h b/drivers/staging/rtl8723au/include/usb_ops.h
new file mode 100644
index 000000000..ff11e13b2
--- /dev/null
+++ b/drivers/staging/rtl8723au/include/usb_ops.h
@@ -0,0 +1,68 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2011 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * 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.
+ *
+ ******************************************************************************/
+#ifndef __USB_OPS_H_
+#define __USB_OPS_H_
+
+#include <osdep_service.h>
+#include <drv_types.h>
+#include <osdep_intf.h>
+#include <usb_ops_linux.h>
+
+#define REALTEK_USB_VENQT_READ 0xC0
+#define REALTEK_USB_VENQT_WRITE 0x40
+#define REALTEK_USB_VENQT_CMD_REQ 0x05
+#define REALTEK_USB_VENQT_CMD_IDX 0x00
+
+enum {
+ VENDOR_WRITE = 0x00,
+ VENDOR_READ = 0x01,
+};
+
+#define ALIGNMENT_UNIT 16
+#define MAX_VENDOR_REQ_CMD_SIZE 254 /* 8188cu SIE Support */
+#define MAX_USB_IO_CTL_SIZE (MAX_VENDOR_REQ_CMD_SIZE +ALIGNMENT_UNIT)
+
+void rtl8723au_set_hw_type(struct rtw_adapter *padapter);
+
+void rtl8723au_recv_tasklet(void *priv);
+
+void rtl8723au_xmit_tasklet(void *priv);
+
+/* Increase and check if the continual_urb_error of this @param dvobjprive is
+ * larger than MAX_CONTINUAL_URB_ERR. Return result
+ */
+static inline int rtw_inc_and_chk_continual_urb_error(struct dvobj_priv *dvobj)
+{
+ int ret = false;
+ int value;
+
+ value = atomic_inc_return(&dvobj->continual_urb_error);
+ if (value > MAX_CONTINUAL_URB_ERR) {
+ DBG_8723A("[dvobj:%p][ERROR] continual_urb_error:%d > %d\n",
+ dvobj, value, MAX_CONTINUAL_URB_ERR);
+ ret = true;
+ }
+ return ret;
+}
+
+/* Set the continual_urb_error of this @param dvobjprive to 0 */
+static inline void rtw_reset_continual_urb_error(struct dvobj_priv *dvobj)
+{
+ atomic_set(&dvobj->continual_urb_error, 0);
+}
+
+bool rtl8723au_chip_configure(struct rtw_adapter *padapter);
+
+#endif /* __USB_OPS_H_ */
diff --git a/drivers/staging/rtl8723au/include/usb_ops_linux.h b/drivers/staging/rtl8723au/include/usb_ops_linux.h
new file mode 100644
index 000000000..af2f14b8b
--- /dev/null
+++ b/drivers/staging/rtl8723au/include/usb_ops_linux.h
@@ -0,0 +1,41 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2011 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * 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.
+ *
+ ******************************************************************************/
+#ifndef __USB_OPS_LINUX_H__
+#define __USB_OPS_LINUX_H__
+
+#define VENDOR_CMD_MAX_DATA_LEN 254
+
+#define RTW_USB_CONTROL_MSG_TIMEOUT 500/* ms */
+
+#define MAX_USBCTRL_VENDORREQ_TIMES 10
+
+int rtl8723au_read_port(struct rtw_adapter *adapter, u32 cnt,
+ struct recv_buf *precvbuf);
+void rtl8723au_read_port_cancel(struct rtw_adapter *padapter);
+int rtl8723au_write_port(struct rtw_adapter *padapter, u32 addr, u32 cnt,
+ struct xmit_buf *pxmitbuf);
+void rtl8723au_write_port_cancel(struct rtw_adapter *padapter);
+int rtl8723au_read_interrupt(struct rtw_adapter *adapter);
+
+u8 rtl8723au_read8(struct rtw_adapter *padapter, u16 addr);
+u16 rtl8723au_read16(struct rtw_adapter *padapter, u16 addr);
+u32 rtl8723au_read32(struct rtw_adapter *padapter, u16 addr);
+int rtl8723au_write8(struct rtw_adapter *padapter, u16 addr, u8 val);
+int rtl8723au_write16(struct rtw_adapter *padapter, u16 addr, u16 val);
+int rtl8723au_write32(struct rtw_adapter *padapter, u16 addr, u32 val);
+int rtl8723au_writeN(struct rtw_adapter *padapter,
+ u16 addr, u16 length, u8 *pdata);
+
+#endif
diff --git a/drivers/staging/rtl8723au/include/wifi.h b/drivers/staging/rtl8723au/include/wifi.h
new file mode 100644
index 000000000..25d573c3e
--- /dev/null
+++ b/drivers/staging/rtl8723au/include/wifi.h
@@ -0,0 +1,84 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2012 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * 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.
+ *
+ ******************************************************************************/
+#ifndef _WIFI_H_
+#define _WIFI_H_
+
+/* This value is tested by WiFi 11n Test Plan 5.2.3.
+ * This test verifies the WLAN NIC can update the NAV through sending
+ * the CTS with large duration.
+ */
+#define WiFiNavUpperUs 30000 /* 30 ms */
+
+/*-----------------------------------------------------------------------------
+ Below is the definition for 802.11n
+------------------------------------------------------------------------------*/
+
+struct AC_param {
+ u8 ACI_AIFSN;
+ u8 CW;
+ __le16 TXOP_limit;
+} __packed;
+
+struct WMM_para_element {
+ unsigned char QoS_info;
+ unsigned char reserved;
+ struct AC_param ac_param[4];
+} __packed;
+
+struct ADDBA_request {
+ u8 dialog_token;
+ __le16 BA_para_set;
+ __le16 BA_timeout_value;
+ __le16 BA_starting_seqctrl;
+} __packed;
+
+
+/* ===============WPS Section=============== */
+/* WPS attribute ID */
+#define WPS_ATTR_VER1 0x104A
+#define WPS_ATTR_SIMPLE_CONF_STATE 0x1044
+#define WPS_ATTR_RESP_TYPE 0x103B
+#define WPS_ATTR_UUID_E 0x1047
+#define WPS_ATTR_MANUFACTURER 0x1021
+#define WPS_ATTR_MODEL_NAME 0x1023
+#define WPS_ATTR_MODEL_NUMBER 0x1024
+#define WPS_ATTR_SERIAL_NUMBER 0x1042
+#define WPS_ATTR_PRIMARY_DEV_TYPE 0x1054
+#define WPS_ATTR_SEC_DEV_TYPE_LIST 0x1055
+#define WPS_ATTR_DEVICE_NAME 0x1011
+#define WPS_ATTR_CONF_METHOD 0x1008
+#define WPS_ATTR_RF_BANDS 0x103C
+#define WPS_ATTR_DEVICE_PWID 0x1012
+#define WPS_ATTR_REQUEST_TYPE 0x103A
+#define WPS_ATTR_ASSOCIATION_STATE 0x1002
+#define WPS_ATTR_CONFIG_ERROR 0x1009
+#define WPS_ATTR_VENDOR_EXT 0x1049
+#define WPS_ATTR_SELECTED_REGISTRAR 0x1041
+
+/* WPS Configuration Method */
+#define WPS_CM_NONE 0x0000
+#define WPS_CM_LABEL 0x0004
+#define WPS_CM_DISPLYA 0x0008
+#define WPS_CM_EXTERNAL_NFC_TOKEN 0x0010
+#define WPS_CM_INTEGRATED_NFC_TOKEN 0x0020
+#define WPS_CM_NFC_INTERFACE 0x0040
+#define WPS_CM_PUSH_BUTTON 0x0080
+#define WPS_CM_KEYPAD 0x0100
+#define WPS_CM_SW_PUHS_BUTTON 0x0280
+#define WPS_CM_HW_PUHS_BUTTON 0x0480
+#define WPS_CM_SW_DISPLAY_PIN 0x2008
+#define WPS_CM_LCD_DISPLAY_PIN 0x4008
+
+#endif /* _WIFI_H_ */
diff --git a/drivers/staging/rtl8723au/include/wlan_bssdef.h b/drivers/staging/rtl8723au/include/wlan_bssdef.h
new file mode 100644
index 000000000..95b32e15a
--- /dev/null
+++ b/drivers/staging/rtl8723au/include/wlan_bssdef.h
@@ -0,0 +1,123 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2011 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * 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.
+ *
+ ******************************************************************************/
+#ifndef __WLAN_BSSDEF_H__
+#define __WLAN_BSSDEF_H__
+
+
+#define MAX_IE_SZ 768
+
+
+#define NDIS_802_11_LENGTH_RATES 8
+#define NDIS_802_11_LENGTH_RATES_EX 16
+
+/* Length is the 4 bytes multiples of the sum of
+ * sizeof(6 * sizeof(unsigned char)) + 2 + sizeof(struct ndis_802_11_ssid) +
+ * sizeof(u32) + sizeof(long) + sizeof(enum ndis_802_11_net_type) +
+ * sizeof(struct ndis_802_11_config) + sizeof(sizeof(unsigned char) *
+ * NDIS_802_11_LENGTH_RATES_EX) + IELength
+ *
+ * Except the IELength, all other fields are fixed length. Therefore,
+ * we can define a macro to present the partial sum.
+ */
+
+enum ndis_802_11_auth_mode {
+ Ndis802_11AuthModeOpen,
+ Ndis802_11AuthModeShared,
+ Ndis802_11AuthModeAutoSwitch,
+ Ndis802_11AuthModeWPA,
+ Ndis802_11AuthModeWPAPSK,
+ Ndis802_11AuthModeWPANone,
+ dis802_11AuthModeMax /* upper bound */
+};
+
+enum {
+ Ndis802_11WEPEnabled,
+ Ndis802_11Encryption1Enabled = Ndis802_11WEPEnabled,
+ Ndis802_11WEPDisabled,
+ Ndis802_11EncryptionDisabled = Ndis802_11WEPDisabled,
+ Ndis802_11WEPKeyAbsent,
+ Ndis802_11Encryption1KeyAbsent = Ndis802_11WEPKeyAbsent,
+ Ndis802_11WEPNotSupported,
+ Ndis802_11EncryptionNotSupported = Ndis802_11WEPNotSupported,
+ Ndis802_11Encryption2Enabled,
+ Ndis802_11Encryption2KeyAbsent,
+ Ndis802_11Encryption3Enabled,
+ Ndis802_11Encryption3KeyAbsent,
+};
+
+struct wlan_bcn_info {
+ /* these infor get from rtw_get_encrypt_info when
+ * * translate scan to UI */
+ u8 encryp_protocol;/* ENCRYP_PROTOCOL_E: OPEN/WEP/WPA/WPA2 */
+ int group_cipher; /* WPA/WPA2 group cipher */
+ int pairwise_cipher;/* WPA/WPA2/WEP pairwise cipher */
+ int is_8021x;
+
+ /* bwmode 20/40 and ch_offset UP/LOW */
+};
+
+struct wlan_bssid_ex {
+ u32 Length;
+ u8 MacAddress[ETH_ALEN];
+ u16 reserved;
+ struct cfg80211_ssid Ssid;
+ u32 Privacy;
+ long Rssi;/* in dBM, raw data , get from PHY) */
+ u16 beacon_interval;
+ u16 capability;
+ u64 tsf;
+ u32 ATIMWindow; /* units are Kusec */
+ u32 DSConfig; /* Frequency, units are kHz */
+ enum nl80211_iftype ifmode;
+ unsigned char SupportedRates[NDIS_802_11_LENGTH_RATES_EX];
+ u8 SignalStrength;/* in percentage */
+ u8 SignalQuality;/* in percentage */
+ u32 IELength;
+ u8 IEs[MAX_IE_SZ]; /* timestamp, beacon interval, and capability info*/
+} __packed;
+
+static inline uint get_wlan_bssid_ex_sz(struct wlan_bssid_ex *bss)
+{
+ return sizeof(struct wlan_bssid_ex) - MAX_IE_SZ + bss->IELength;
+}
+
+struct wlan_network {
+ struct list_head list;
+ int network_type; /* refer to ieee80211.h for 11A/B/G */
+ /* set to fixed when not to be removed as site-surveying */
+ int fixed;
+ unsigned long last_scanned; /* timestamp for the network */
+ int join_res;
+ struct wlan_bssid_ex network; /* must be the last item */
+ struct wlan_bcn_info BcnInfo;
+};
+
+enum VRTL_CARRIER_SENSE {
+ DISABLE_VCS,
+ ENABLE_VCS,
+ AUTO_VCS
+};
+
+enum VCS_TYPE {
+ NONE_VCS,
+ RTS_CTS,
+ CTS_TO_SELF
+};
+
+/* john */
+#define NUM_PRE_AUTH_KEY 16
+#define NUM_PMKID_CACHE NUM_PRE_AUTH_KEY
+
+#endif /* ifndef WLAN_BSSDEF_H_ */
diff --git a/drivers/staging/rtl8723au/include/xmit_osdep.h b/drivers/staging/rtl8723au/include/xmit_osdep.h
new file mode 100644
index 000000000..2be04c486
--- /dev/null
+++ b/drivers/staging/rtl8723au/include/xmit_osdep.h
@@ -0,0 +1,38 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2011 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * 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.
+ *
+ ******************************************************************************/
+#ifndef __XMIT_OSDEP_H_
+#define __XMIT_OSDEP_H_
+
+#include <osdep_service.h>
+#include <drv_types.h>
+
+
+#define NR_XMITFRAME 256
+
+int rtw_xmit23a_entry23a(struct sk_buff *pkt, struct net_device *pnetdev);
+
+void rtw_os_xmit_schedule23a(struct rtw_adapter *padapter);
+
+int rtw_os_xmit_resource_alloc23a(struct rtw_adapter *padapter,
+ struct xmit_buf *pxmitbuf, u32 alloc_sz);
+void rtw_os_xmit_resource_free23a(struct rtw_adapter *padapter,
+ struct xmit_buf *pxmitbuf);
+
+void rtw_os_pkt_complete23a(struct rtw_adapter *padapter, struct sk_buff *pkt);
+void rtw_os_xmit_complete23a(struct rtw_adapter *padapter,
+ struct xmit_frame *pxframe);
+int netdev_open23a(struct net_device *pnetdev);
+
+#endif /* __XMIT_OSDEP_H_ */
diff --git a/drivers/staging/rtl8723au/os_dep/ioctl_cfg80211.c b/drivers/staging/rtl8723au/os_dep/ioctl_cfg80211.c
new file mode 100644
index 000000000..bc95ce89a
--- /dev/null
+++ b/drivers/staging/rtl8723au/os_dep/ioctl_cfg80211.c
@@ -0,0 +1,3356 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2012 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ ******************************************************************************/
+#define _IOCTL_CFG80211_C_
+
+#include <osdep_service.h>
+#include <drv_types.h>
+#include <xmit_osdep.h>
+
+#include "ioctl_cfg80211.h"
+
+#define RTW_MAX_MGMT_TX_CNT 8
+
+#define RTW_MAX_REMAIN_ON_CHANNEL_DURATION 65535 /* ms */
+#define RTW_MAX_NUM_PMKIDS 4
+
+static const u32 rtw_cipher_suites[] = {
+ WLAN_CIPHER_SUITE_WEP40,
+ WLAN_CIPHER_SUITE_WEP104,
+ WLAN_CIPHER_SUITE_TKIP,
+ WLAN_CIPHER_SUITE_CCMP,
+};
+
+#define RATETAB_ENT(_rate, _rateid, _flags) { \
+ .bitrate = (_rate), \
+ .hw_value = (_rateid), \
+ .flags = (_flags), \
+}
+
+#define CHAN2G(_channel, _freq, _flags) { \
+ .band = IEEE80211_BAND_2GHZ, \
+ .center_freq = (_freq), \
+ .hw_value = (_channel), \
+ .flags = (_flags), \
+ .max_antenna_gain = 0, \
+ .max_power = 30, \
+}
+
+#define CHAN5G(_channel, _flags) { \
+ .band = IEEE80211_BAND_5GHZ, \
+ .center_freq = 5000 + (5 * (_channel)), \
+ .hw_value = (_channel), \
+ .flags = (_flags), \
+ .max_antenna_gain = 0, \
+ .max_power = 30, \
+}
+
+static struct ieee80211_rate rtw_rates[] = {
+ RATETAB_ENT(10, 0x1, 0),
+ RATETAB_ENT(20, 0x2, 0),
+ RATETAB_ENT(55, 0x4, 0),
+ RATETAB_ENT(110, 0x8, 0),
+ RATETAB_ENT(60, 0x10, 0),
+ RATETAB_ENT(90, 0x20, 0),
+ RATETAB_ENT(120, 0x40, 0),
+ RATETAB_ENT(180, 0x80, 0),
+ RATETAB_ENT(240, 0x100, 0),
+ RATETAB_ENT(360, 0x200, 0),
+ RATETAB_ENT(480, 0x400, 0),
+ RATETAB_ENT(540, 0x800, 0),
+};
+
+#define rtw_a_rates (rtw_rates + 4)
+#define RTW_A_RATES_NUM 8
+#define rtw_g_rates (rtw_rates + 0)
+#define RTW_G_RATES_NUM 12
+
+#define RTW_2G_CHANNELS_NUM 14
+#define RTW_5G_CHANNELS_NUM 37
+
+static struct ieee80211_channel rtw_2ghz_channels[] = {
+ CHAN2G(1, 2412, 0),
+ CHAN2G(2, 2417, 0),
+ CHAN2G(3, 2422, 0),
+ CHAN2G(4, 2427, 0),
+ CHAN2G(5, 2432, 0),
+ CHAN2G(6, 2437, 0),
+ CHAN2G(7, 2442, 0),
+ CHAN2G(8, 2447, 0),
+ CHAN2G(9, 2452, 0),
+ CHAN2G(10, 2457, 0),
+ CHAN2G(11, 2462, 0),
+ CHAN2G(12, 2467, 0),
+ CHAN2G(13, 2472, 0),
+ CHAN2G(14, 2484, 0),
+};
+
+static struct ieee80211_channel rtw_5ghz_a_channels[] = {
+ CHAN5G(34, 0), CHAN5G(36, 0),
+ CHAN5G(38, 0), CHAN5G(40, 0),
+ CHAN5G(42, 0), CHAN5G(44, 0),
+ CHAN5G(46, 0), CHAN5G(48, 0),
+ CHAN5G(52, 0), CHAN5G(56, 0),
+ CHAN5G(60, 0), CHAN5G(64, 0),
+ CHAN5G(100, 0), CHAN5G(104, 0),
+ CHAN5G(108, 0), CHAN5G(112, 0),
+ CHAN5G(116, 0), CHAN5G(120, 0),
+ CHAN5G(124, 0), CHAN5G(128, 0),
+ CHAN5G(132, 0), CHAN5G(136, 0),
+ CHAN5G(140, 0), CHAN5G(149, 0),
+ CHAN5G(153, 0), CHAN5G(157, 0),
+ CHAN5G(161, 0), CHAN5G(165, 0),
+ CHAN5G(184, 0), CHAN5G(188, 0),
+ CHAN5G(192, 0), CHAN5G(196, 0),
+ CHAN5G(200, 0), CHAN5G(204, 0),
+ CHAN5G(208, 0), CHAN5G(212, 0),
+ CHAN5G(216, 0),
+};
+
+static void rtw_2g_channels_init(struct ieee80211_channel *channels)
+{
+ memcpy((void *)channels, (void *)rtw_2ghz_channels,
+ sizeof(struct ieee80211_channel) * RTW_2G_CHANNELS_NUM);
+}
+
+static void rtw_5g_channels_init(struct ieee80211_channel *channels)
+{
+ memcpy((void *)channels, (void *)rtw_5ghz_a_channels,
+ sizeof(struct ieee80211_channel) * RTW_5G_CHANNELS_NUM);
+}
+
+static void rtw_2g_rates_init(struct ieee80211_rate *rates)
+{
+ memcpy(rates, rtw_g_rates,
+ sizeof(struct ieee80211_rate) * RTW_G_RATES_NUM);
+}
+
+static void rtw_5g_rates_init(struct ieee80211_rate *rates)
+{
+ memcpy(rates, rtw_a_rates,
+ sizeof(struct ieee80211_rate) * RTW_A_RATES_NUM);
+}
+
+static struct ieee80211_supported_band *
+rtw_spt_band_alloc(enum ieee80211_band band)
+{
+ struct ieee80211_supported_band *spt_band = NULL;
+ int n_channels, n_bitrates;
+
+ if (band == IEEE80211_BAND_2GHZ) {
+ n_channels = RTW_2G_CHANNELS_NUM;
+ n_bitrates = RTW_G_RATES_NUM;
+ } else if (band == IEEE80211_BAND_5GHZ) {
+ n_channels = RTW_5G_CHANNELS_NUM;
+ n_bitrates = RTW_A_RATES_NUM;
+ } else {
+ goto exit;
+ }
+ spt_band = kzalloc(sizeof(struct ieee80211_supported_band) +
+ sizeof(struct ieee80211_channel) * n_channels +
+ sizeof(struct ieee80211_rate) * n_bitrates,
+ GFP_KERNEL);
+ if (!spt_band)
+ goto exit;
+
+ spt_band->channels =
+ (struct ieee80211_channel *)(((u8 *) spt_band) +
+ sizeof(struct
+ ieee80211_supported_band));
+ spt_band->bitrates =
+ (struct ieee80211_rate *)(((u8 *) spt_band->channels) +
+ sizeof(struct ieee80211_channel) *
+ n_channels);
+ spt_band->band = band;
+ spt_band->n_channels = n_channels;
+ spt_band->n_bitrates = n_bitrates;
+
+ if (band == IEEE80211_BAND_2GHZ) {
+ rtw_2g_channels_init(spt_band->channels);
+ rtw_2g_rates_init(spt_band->bitrates);
+ } else if (band == IEEE80211_BAND_5GHZ) {
+ rtw_5g_channels_init(spt_band->channels);
+ rtw_5g_rates_init(spt_band->bitrates);
+ }
+
+ /* spt_band.ht_cap */
+
+exit:
+ return spt_band;
+}
+
+static const struct ieee80211_txrx_stypes
+rtw_cfg80211_default_mgmt_stypes[NUM_NL80211_IFTYPES] = {
+ [NL80211_IFTYPE_ADHOC] = {
+ .tx = 0xffff,
+ .rx = BIT(IEEE80211_STYPE_ACTION >> 4)
+ },
+ [NL80211_IFTYPE_STATION] = {
+ .tx = 0xffff,
+ .rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
+ BIT(IEEE80211_STYPE_PROBE_REQ >> 4)
+ },
+ [NL80211_IFTYPE_AP] = {
+ .tx = 0xffff,
+ .rx = BIT(IEEE80211_STYPE_ASSOC_REQ >> 4) |
+ BIT(IEEE80211_STYPE_REASSOC_REQ >> 4) |
+ BIT(IEEE80211_STYPE_PROBE_REQ >> 4) |
+ BIT(IEEE80211_STYPE_DISASSOC >> 4) |
+ BIT(IEEE80211_STYPE_AUTH >> 4) |
+ BIT(IEEE80211_STYPE_DEAUTH >> 4) |
+ BIT(IEEE80211_STYPE_ACTION >> 4)
+ },
+ [NL80211_IFTYPE_AP_VLAN] = {
+ /* copy AP */
+ .tx = 0xffff,
+ .rx = BIT(IEEE80211_STYPE_ASSOC_REQ >> 4) |
+ BIT(IEEE80211_STYPE_REASSOC_REQ >> 4) |
+ BIT(IEEE80211_STYPE_PROBE_REQ >> 4) |
+ BIT(IEEE80211_STYPE_DISASSOC >> 4) |
+ BIT(IEEE80211_STYPE_AUTH >> 4) |
+ BIT(IEEE80211_STYPE_DEAUTH >> 4) |
+ BIT(IEEE80211_STYPE_ACTION >> 4)
+ },
+ [NL80211_IFTYPE_P2P_CLIENT] = {
+ .tx = 0xffff,
+ .rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
+ BIT(IEEE80211_STYPE_PROBE_REQ >> 4)
+ },
+ [NL80211_IFTYPE_P2P_GO] = {
+ .tx = 0xffff,
+ .rx = BIT(IEEE80211_STYPE_ASSOC_REQ >> 4) |
+ BIT(IEEE80211_STYPE_REASSOC_REQ >> 4) |
+ BIT(IEEE80211_STYPE_PROBE_REQ >> 4) |
+ BIT(IEEE80211_STYPE_DISASSOC >> 4) |
+ BIT(IEEE80211_STYPE_AUTH >> 4) |
+ BIT(IEEE80211_STYPE_DEAUTH >> 4) |
+ BIT(IEEE80211_STYPE_ACTION >> 4)
+ },
+};
+
+static int rtw_cfg80211_inform_bss(struct rtw_adapter *padapter,
+ struct wlan_network *pnetwork)
+{
+ int ret = 0;
+ struct ieee80211_channel *notify_channel;
+ struct cfg80211_bss *bss;
+ u16 channel;
+ u32 freq;
+ u8 *notify_ie;
+ size_t notify_ielen;
+ s32 notify_signal;
+ struct wireless_dev *wdev = padapter->rtw_wdev;
+ struct wiphy *wiphy = wdev->wiphy;
+ struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
+
+ channel = pnetwork->network.DSConfig;
+ if (channel <= RTW_CH_MAX_2G_CHANNEL)
+ freq = ieee80211_channel_to_frequency(channel,
+ IEEE80211_BAND_2GHZ);
+ else
+ freq = ieee80211_channel_to_frequency(channel,
+ IEEE80211_BAND_5GHZ);
+
+ notify_channel = ieee80211_get_channel(wiphy, freq);
+
+ notify_ie = pnetwork->network.IEs;
+ notify_ielen = pnetwork->network.IELength;
+
+ /* We've set wiphy's signal_type as CFG80211_SIGNAL_TYPE_MBM:
+ * signal strength in mBm (100*dBm)
+ */
+ if (check_fwstate(pmlmepriv, _FW_LINKED) &&
+ is_same_network23a(&pmlmepriv->cur_network.network,
+ &pnetwork->network)) {
+ notify_signal = 100 * translate_percentage_to_dbm(padapter->recvpriv.signal_strength); /* dbm */
+ } else {
+ notify_signal = 100 * translate_percentage_to_dbm(
+ pnetwork->network.SignalStrength); /* dbm */
+ }
+
+ bss = cfg80211_inform_bss(wiphy, notify_channel,
+ CFG80211_BSS_FTYPE_UNKNOWN,
+ pnetwork->network.MacAddress,
+ pnetwork->network.tsf,
+ pnetwork->network.capability,
+ pnetwork->network.beacon_interval,
+ notify_ie, notify_ielen,
+ notify_signal, GFP_ATOMIC);
+
+ if (unlikely(!bss)) {
+ DBG_8723A("rtw_cfg80211_inform_bss error\n");
+ return -EINVAL;
+ }
+
+ cfg80211_put_bss(wiphy, bss);
+
+ return ret;
+}
+
+void rtw_cfg80211_indicate_connect(struct rtw_adapter *padapter)
+{
+ struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
+ struct wlan_network *cur_network = &pmlmepriv->cur_network;
+ struct wireless_dev *pwdev = padapter->rtw_wdev;
+
+ DBG_8723A("%s(padapter =%p)\n", __func__, padapter);
+
+ if (pwdev->iftype != NL80211_IFTYPE_STATION &&
+ pwdev->iftype != NL80211_IFTYPE_P2P_CLIENT)
+ return;
+
+ if (check_fwstate(pmlmepriv, WIFI_AP_STATE))
+ return;
+
+ if (padapter->mlmepriv.to_roaming > 0) {
+ struct wiphy *wiphy = pwdev->wiphy;
+ struct ieee80211_channel *notify_channel;
+ u32 freq;
+ u16 channel = cur_network->network.DSConfig;
+
+ if (channel <= RTW_CH_MAX_2G_CHANNEL)
+ freq =
+ ieee80211_channel_to_frequency(channel,
+ IEEE80211_BAND_2GHZ);
+ else
+ freq =
+ ieee80211_channel_to_frequency(channel,
+ IEEE80211_BAND_5GHZ);
+
+ notify_channel = ieee80211_get_channel(wiphy, freq);
+
+ DBG_8723A("%s call cfg80211_roamed\n", __func__);
+ cfg80211_roamed(padapter->pnetdev, notify_channel,
+ cur_network->network.MacAddress,
+ pmlmepriv->assoc_req +
+ sizeof(struct ieee80211_hdr_3addr) + 2,
+ pmlmepriv->assoc_req_len -
+ sizeof(struct ieee80211_hdr_3addr) - 2,
+ pmlmepriv->assoc_rsp +
+ sizeof(struct ieee80211_hdr_3addr) + 6,
+ pmlmepriv->assoc_rsp_len -
+ sizeof(struct ieee80211_hdr_3addr) - 6,
+ GFP_ATOMIC);
+ } else {
+ cfg80211_connect_result(padapter->pnetdev,
+ cur_network->network.MacAddress,
+ pmlmepriv->assoc_req +
+ sizeof(struct ieee80211_hdr_3addr) + 2,
+ pmlmepriv->assoc_req_len -
+ sizeof(struct ieee80211_hdr_3addr) - 2,
+ pmlmepriv->assoc_rsp +
+ sizeof(struct ieee80211_hdr_3addr) + 6,
+ pmlmepriv->assoc_rsp_len -
+ sizeof(struct ieee80211_hdr_3addr) - 6,
+ WLAN_STATUS_SUCCESS, GFP_ATOMIC);
+ }
+}
+
+void rtw_cfg80211_indicate_disconnect(struct rtw_adapter *padapter)
+{
+ struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
+ struct wireless_dev *pwdev = padapter->rtw_wdev;
+
+ DBG_8723A("%s(padapter =%p)\n", __func__, padapter);
+
+ if (pwdev->iftype != NL80211_IFTYPE_STATION &&
+ pwdev->iftype != NL80211_IFTYPE_P2P_CLIENT)
+ return;
+
+ if (check_fwstate(pmlmepriv, WIFI_AP_STATE))
+ return;
+
+ if (!padapter->mlmepriv.not_indic_disco) {
+ if (check_fwstate(&padapter->mlmepriv, WIFI_UNDER_LINKING)) {
+ cfg80211_connect_result(padapter->pnetdev, NULL, NULL,
+ 0, NULL, 0,
+ WLAN_STATUS_UNSPECIFIED_FAILURE,
+ GFP_ATOMIC);
+ } else {
+ cfg80211_disconnected(padapter->pnetdev, 0, NULL,
+ 0, GFP_ATOMIC);
+ }
+ }
+}
+
+#ifdef CONFIG_8723AU_AP_MODE
+static int set_pairwise_key(struct rtw_adapter *padapter, struct sta_info *psta)
+{
+ struct cmd_obj *ph2c;
+ struct set_stakey_parm *psetstakey_para;
+ struct cmd_priv *pcmdpriv = &padapter->cmdpriv;
+ int res = _SUCCESS;
+
+ ph2c = kzalloc(sizeof(struct cmd_obj), GFP_KERNEL);
+ if (ph2c == NULL) {
+ res = _FAIL;
+ goto exit;
+ }
+
+ psetstakey_para = kzalloc(sizeof(struct set_stakey_parm), GFP_KERNEL);
+ if (psetstakey_para == NULL) {
+ kfree(ph2c);
+ res = _FAIL;
+ goto exit;
+ }
+
+ init_h2fwcmd_w_parm_no_rsp(ph2c, psetstakey_para, _SetStaKey_CMD_);
+
+ psetstakey_para->algorithm = psta->dot118021XPrivacy;
+
+ ether_addr_copy(psetstakey_para->addr, psta->hwaddr);
+
+ memcpy(psetstakey_para->key, &psta->dot118021x_UncstKey, 16);
+
+ res = rtw_enqueue_cmd23a(pcmdpriv, ph2c);
+
+exit:
+ return res;
+}
+
+static int set_group_key(struct rtw_adapter *padapter, struct key_params *parms,
+ u32 alg, u8 keyid)
+{
+ struct cmd_obj *pcmd;
+ struct setkey_parm *psetkeyparm;
+ struct cmd_priv *pcmdpriv = &padapter->cmdpriv;
+ int res = _SUCCESS;
+
+ DBG_8723A("%s\n", __func__);
+
+ if (keyid >= 4) {
+ res = _FAIL;
+ goto exit;
+ }
+
+ pcmd = kzalloc(sizeof(struct cmd_obj), GFP_KERNEL);
+ if (!pcmd) {
+ res = _FAIL;
+ goto exit;
+ }
+ psetkeyparm = kzalloc(sizeof(struct setkey_parm), GFP_KERNEL);
+ if (!psetkeyparm) {
+ kfree(pcmd);
+ res = _FAIL;
+ goto exit;
+ }
+
+ psetkeyparm->keyid = keyid;
+ if (is_wep_enc(alg))
+ padapter->mlmepriv.key_mask |= BIT(psetkeyparm->keyid);
+
+ psetkeyparm->algorithm = alg;
+
+ psetkeyparm->set_tx = 1;
+
+ memcpy(&psetkeyparm->key, parms->key, parms->key_len);
+
+ pcmd->cmdcode = _SetKey_CMD_;
+ pcmd->parmbuf = (u8 *) psetkeyparm;
+ pcmd->cmdsz = (sizeof(struct setkey_parm));
+ pcmd->rsp = NULL;
+ pcmd->rspsz = 0;
+
+ res = rtw_enqueue_cmd23a(pcmdpriv, pcmd);
+
+exit:
+ return res;
+}
+
+static int rtw_cfg80211_ap_set_encryption(struct net_device *dev, u8 key_index,
+ int set_tx, const u8 *sta_addr,
+ struct key_params *keyparms)
+{
+ int key_len;
+ struct sta_info *psta = NULL, *pbcmc_sta = NULL;
+ struct rtw_adapter *padapter = netdev_priv(dev);
+ struct security_priv *psecuritypriv = &padapter->securitypriv;
+ struct sta_priv *pstapriv = &padapter->stapriv;
+
+ DBG_8723A("%s\n", __func__);
+
+ if (!is_broadcast_ether_addr(sta_addr)) {
+ psta = rtw_get_stainfo23a(pstapriv, sta_addr);
+ if (!psta) {
+ /* ret = -EINVAL; */
+ DBG_8723A("rtw_set_encryption(), sta has already "
+ "been removed or never been added\n");
+ goto exit;
+ }
+ }
+
+ key_len = keyparms->key_len;
+
+ if (!psta && (keyparms->cipher == WLAN_CIPHER_SUITE_WEP40 ||
+ keyparms->cipher == WLAN_CIPHER_SUITE_WEP104)) {
+ DBG_8723A("r871x_set_encryption, crypt.alg = WEP\n");
+
+ DBG_8723A("r871x_set_encryption, wep_key_idx =%d, len =%d\n",
+ key_index, key_len);
+
+ if (psecuritypriv->bWepDefaultKeyIdxSet == 0) {
+ /* wep default key has not been set, so use
+ this key index as default key. */
+
+ psecuritypriv->ndisencryptstatus =
+ Ndis802_11Encryption1Enabled;
+ psecuritypriv->dot11PrivacyAlgrthm = keyparms->cipher;
+ psecuritypriv->dot118021XGrpPrivacy = keyparms->cipher;
+
+ psecuritypriv->dot11PrivacyKeyIndex = key_index;
+ }
+
+ memcpy(&psecuritypriv->wep_key[key_index].key,
+ keyparms->key, key_len);
+
+ psecuritypriv->wep_key[key_index].keylen = key_len;
+
+ set_group_key(padapter, keyparms, keyparms->cipher, key_index);
+
+ goto exit;
+ }
+
+ if (!psta) { /* group key */
+ if (set_tx == 0) { /* group key */
+ if (keyparms->cipher == WLAN_CIPHER_SUITE_WEP40 ||
+ keyparms->cipher == WLAN_CIPHER_SUITE_WEP104) {
+ DBG_8723A("%s, set group_key, WEP\n", __func__);
+
+ memcpy(psecuritypriv->
+ dot118021XGrpKey[key_index].skey,
+ keyparms->key, key_len);
+
+ psecuritypriv->dot118021XGrpPrivacy =
+ keyparms->cipher;
+ } else if (keyparms->cipher == WLAN_CIPHER_SUITE_TKIP) {
+ DBG_8723A("%s, set group_key, TKIP\n",
+ __func__);
+
+ psecuritypriv->dot118021XGrpPrivacy =
+ WLAN_CIPHER_SUITE_TKIP;
+
+ memcpy(psecuritypriv->
+ dot118021XGrpKey[key_index].skey,
+ keyparms->key,
+ (key_len > 16 ? 16 : key_len));
+
+ /* set mic key */
+ memcpy(psecuritypriv->
+ dot118021XGrptxmickey[key_index].skey,
+ &keyparms->key[16], 8);
+ memcpy(psecuritypriv->
+ dot118021XGrprxmickey[key_index].skey,
+ &keyparms->key[24], 8);
+
+ psecuritypriv->busetkipkey = 1;
+
+ } else if (keyparms->cipher == WLAN_CIPHER_SUITE_CCMP) {
+ DBG_8723A("%s, set group_key, CCMP\n",
+ __func__);
+
+ psecuritypriv->dot118021XGrpPrivacy =
+ WLAN_CIPHER_SUITE_CCMP;
+
+ memcpy(psecuritypriv->
+ dot118021XGrpKey[key_index].skey,
+ keyparms->key,
+ (key_len > 16 ? 16 : key_len));
+ } else {
+ DBG_8723A("%s, set group_key, none\n",
+ __func__);
+
+ psecuritypriv->dot118021XGrpPrivacy = 0;
+ }
+
+ psecuritypriv->dot118021XGrpKeyid = key_index;
+
+ psecuritypriv->binstallGrpkey = 1;
+
+ psecuritypriv->dot11PrivacyAlgrthm =
+ psecuritypriv->dot118021XGrpPrivacy;
+
+ set_group_key(padapter, keyparms,
+ psecuritypriv->dot118021XGrpPrivacy,
+ key_index);
+
+ pbcmc_sta = rtw_get_bcmc_stainfo23a(padapter);
+ if (pbcmc_sta) {
+ pbcmc_sta->ieee8021x_blocked = false;
+ /* rx will use bmc_sta's dot118021XPrivacy */
+ pbcmc_sta->dot118021XPrivacy =
+ psecuritypriv->dot118021XGrpPrivacy;
+
+ }
+
+ }
+
+ goto exit;
+ }
+
+ if (psecuritypriv->dot11AuthAlgrthm == dot11AuthAlgrthm_8021X && psta) {
+ /* psk/802_1x */
+ if (set_tx == 1) {
+ /* pairwise key */
+ memcpy(psta->dot118021x_UncstKey.skey,
+ keyparms->key, (key_len > 16 ? 16 : key_len));
+
+ if (keyparms->cipher == WLAN_CIPHER_SUITE_WEP40 ||
+ keyparms->cipher == WLAN_CIPHER_SUITE_WEP104) {
+ DBG_8723A("%s, set pairwise key, WEP\n",
+ __func__);
+
+ psecuritypriv->dot118021XGrpPrivacy =
+ keyparms->cipher;
+ } else if (keyparms->cipher == WLAN_CIPHER_SUITE_TKIP) {
+ DBG_8723A("%s, set pairwise key, TKIP\n",
+ __func__);
+
+ psta->dot118021XPrivacy =
+ WLAN_CIPHER_SUITE_TKIP;
+
+ /* set mic key */
+ memcpy(psta->dot11tkiptxmickey.skey,
+ &keyparms->key[16], 8);
+ memcpy(psta->dot11tkiprxmickey.skey,
+ &keyparms->key[24], 8);
+
+ psecuritypriv->busetkipkey = 1;
+
+ } else if (keyparms->cipher == WLAN_CIPHER_SUITE_CCMP) {
+ DBG_8723A("%s, set pairwise key, CCMP\n",
+ __func__);
+
+ psta->dot118021XPrivacy =
+ WLAN_CIPHER_SUITE_CCMP;
+ } else {
+ DBG_8723A("%s, set pairwise key, none\n",
+ __func__);
+
+ psta->dot118021XPrivacy = 0;
+ }
+
+ set_pairwise_key(padapter, psta);
+
+ psta->ieee8021x_blocked = false;
+
+ psta->bpairwise_key_installed = true;
+ } else { /* group key??? */
+ if (keyparms->cipher == WLAN_CIPHER_SUITE_WEP40 ||
+ keyparms->cipher == WLAN_CIPHER_SUITE_WEP104) {
+ memcpy(psecuritypriv->
+ dot118021XGrpKey[key_index].skey,
+ keyparms->key, key_len);
+
+ psecuritypriv->dot118021XGrpPrivacy =
+ keyparms->cipher;
+ } else if (keyparms->cipher == WLAN_CIPHER_SUITE_TKIP) {
+ psecuritypriv->dot118021XGrpPrivacy =
+ WLAN_CIPHER_SUITE_TKIP;
+
+ memcpy(psecuritypriv->
+ dot118021XGrpKey[key_index].skey,
+ keyparms->key,
+ (key_len > 16 ? 16 : key_len));
+
+ /* set mic key */
+ memcpy(psecuritypriv->
+ dot118021XGrptxmickey[key_index].skey,
+ &keyparms->key[16], 8);
+ memcpy(psecuritypriv->
+ dot118021XGrprxmickey[key_index].skey,
+ &keyparms->key[24], 8);
+
+ psecuritypriv->busetkipkey = 1;
+ } else if (keyparms->cipher == WLAN_CIPHER_SUITE_CCMP) {
+ psecuritypriv->dot118021XGrpPrivacy =
+ WLAN_CIPHER_SUITE_CCMP;
+
+ memcpy(psecuritypriv->
+ dot118021XGrpKey[key_index].skey,
+ keyparms->key,
+ (key_len > 16 ? 16 : key_len));
+ } else {
+ psecuritypriv->dot118021XGrpPrivacy = 0;
+ }
+
+ psecuritypriv->dot118021XGrpKeyid = key_index;
+
+ psecuritypriv->binstallGrpkey = 1;
+
+ psecuritypriv->dot11PrivacyAlgrthm =
+ psecuritypriv->dot118021XGrpPrivacy;
+
+ set_group_key(padapter, keyparms,
+ psecuritypriv->dot118021XGrpPrivacy,
+ key_index);
+
+ pbcmc_sta = rtw_get_bcmc_stainfo23a(padapter);
+ if (pbcmc_sta) {
+ /* rx will use bmc_sta's
+ dot118021XPrivacy */
+ pbcmc_sta->ieee8021x_blocked = false;
+ pbcmc_sta->dot118021XPrivacy =
+ psecuritypriv->dot118021XGrpPrivacy;
+ }
+ }
+ }
+
+exit:
+
+ return 0;
+}
+#endif
+
+static int rtw_cfg80211_set_encryption(struct net_device *dev, u8 key_index,
+ int set_tx, const u8 *sta_addr,
+ struct key_params *keyparms)
+{
+ int ret = 0;
+ int key_len;
+ struct rtw_adapter *padapter = netdev_priv(dev);
+ struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
+ struct security_priv *psecuritypriv = &padapter->securitypriv;
+
+ DBG_8723A("%s\n", __func__);
+
+ key_len = keyparms->key_len;
+
+ if (keyparms->cipher == WLAN_CIPHER_SUITE_WEP40 ||
+ keyparms->cipher == WLAN_CIPHER_SUITE_WEP104) {
+ RT_TRACE(_module_rtl871x_ioctl_os_c, _drv_err_,
+ "wpa_set_encryption, crypt.alg = WEP\n");
+ DBG_8723A("wpa_set_encryption, crypt.alg = WEP\n");
+
+ if (psecuritypriv->bWepDefaultKeyIdxSet == 0) {
+ /* wep default key has not been set, so use this
+ key index as default key. */
+
+ psecuritypriv->ndisencryptstatus =
+ Ndis802_11Encryption1Enabled;
+ psecuritypriv->dot11PrivacyAlgrthm = keyparms->cipher;
+ psecuritypriv->dot118021XGrpPrivacy = keyparms->cipher;
+
+ psecuritypriv->dot11PrivacyKeyIndex = key_index;
+ }
+
+ memcpy(&psecuritypriv->wep_key[key_index].key,
+ keyparms->key, key_len);
+
+ psecuritypriv->wep_key[key_index].keylen = key_len;
+
+ rtw_set_key23a(padapter, psecuritypriv, key_index, 0);
+
+ goto exit;
+ }
+
+ if (padapter->securitypriv.dot11AuthAlgrthm ==
+ dot11AuthAlgrthm_8021X) { /* 802_1x */
+ struct sta_info *psta, *pbcmc_sta;
+ struct sta_priv *pstapriv = &padapter->stapriv;
+
+ if (check_fwstate(pmlmepriv,
+ WIFI_STATION_STATE | WIFI_MP_STATE)) {
+ /* sta mode */
+ psta = rtw_get_stainfo23a(pstapriv, get_bssid(pmlmepriv));
+ if (psta == NULL) {
+ DBG_8723A("%s, : Obtain Sta_info fail\n",
+ __func__);
+ } else {
+ /* Jeff: don't disable ieee8021x_blocked
+ while clearing key */
+ if (keyparms->cipher != IW_AUTH_CIPHER_NONE &&
+ keyparms->cipher != 0)
+ psta->ieee8021x_blocked = false;
+
+ if ((padapter->securitypriv.ndisencryptstatus ==
+ Ndis802_11Encryption2Enabled) ||
+ (padapter->securitypriv.ndisencryptstatus ==
+ Ndis802_11Encryption3Enabled)) {
+ psta->dot118021XPrivacy =
+ padapter->securitypriv.
+ dot11PrivacyAlgrthm;
+ }
+
+ if (set_tx == 1) {
+ /* pairwise key */
+ DBG_8723A("%s, : set_tx == 1\n",
+ __func__);
+
+ memcpy(psta->dot118021x_UncstKey.skey,
+ keyparms->key,
+ (key_len > 16 ? 16 : key_len));
+
+ if (keyparms->cipher ==
+ WLAN_CIPHER_SUITE_TKIP) {
+ memcpy(psta->dot11tkiptxmickey.
+ skey,
+ &keyparms->key[16], 8);
+ memcpy(psta->dot11tkiprxmickey.
+ skey,
+ &keyparms->key[24], 8);
+
+ padapter->securitypriv.
+ busetkipkey = 0;
+ }
+ DBG_8723A(" ~~~~set sta key:unicastkey\n");
+
+ rtw_setstakey_cmd23a(padapter,
+ (unsigned char *)psta,
+ true);
+ } else { /* group key */
+ memcpy(padapter->securitypriv.
+ dot118021XGrpKey[key_index].skey,
+ keyparms->key,
+ (key_len > 16 ? 16 : key_len));
+ memcpy(padapter->securitypriv.
+ dot118021XGrptxmickey[key_index].
+ skey, &keyparms->key[16], 8);
+ memcpy(padapter->securitypriv.
+ dot118021XGrprxmickey[key_index].
+ skey, &keyparms->key[24], 8);
+ padapter->securitypriv.binstallGrpkey =
+ 1;
+ DBG_8723A
+ (" ~~~~set sta key:groupkey\n");
+
+ padapter->securitypriv.
+ dot118021XGrpKeyid = key_index;
+
+ rtw_set_key23a(padapter,
+ &padapter->securitypriv,
+ key_index, 1);
+ }
+ }
+
+ pbcmc_sta = rtw_get_bcmc_stainfo23a(padapter);
+ if (pbcmc_sta) {
+ /* Jeff: don't disable ieee8021x_blocked
+ while clearing key */
+ if (keyparms->cipher != IW_AUTH_CIPHER_NONE &&
+ keyparms->cipher != 0)
+ pbcmc_sta->ieee8021x_blocked = false;
+
+ if ((padapter->securitypriv.ndisencryptstatus ==
+ Ndis802_11Encryption2Enabled) ||
+ (padapter->securitypriv.ndisencryptstatus ==
+ Ndis802_11Encryption3Enabled)) {
+ pbcmc_sta->dot118021XPrivacy =
+ padapter->securitypriv.
+ dot11PrivacyAlgrthm;
+ }
+ }
+ }
+ }
+
+exit:
+
+ DBG_8723A("%s, ret =%d\n", __func__, ret);
+
+
+
+ return ret;
+}
+
+static int cfg80211_rtw_add_key(struct wiphy *wiphy, struct net_device *ndev,
+ u8 key_index, bool pairwise,
+ const u8 *mac_addr, struct key_params *params)
+{
+ int set_tx, ret = 0;
+ struct wireless_dev *rtw_wdev = wiphy_to_wdev(wiphy);
+ struct rtw_adapter *padapter = wiphy_to_adapter(wiphy);
+ struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
+ u8 sta_addr[ETH_ALEN];
+
+ DBG_8723A("%s(%s): adding key for %pM\n", __func__, ndev->name,
+ mac_addr);
+ DBG_8723A("cipher = 0x%x\n", params->cipher);
+ DBG_8723A("key_len = 0x%x\n", params->key_len);
+ DBG_8723A("seq_len = 0x%x\n", params->seq_len);
+ DBG_8723A("key_index =%d\n", key_index);
+ DBG_8723A("pairwise =%d\n", pairwise);
+
+ switch (params->cipher) {
+ case IW_AUTH_CIPHER_NONE:
+ case WLAN_CIPHER_SUITE_WEP40:
+ if (params->key_len != WLAN_KEY_LEN_WEP40) {
+ ret = -EINVAL;
+ goto exit;
+ }
+ case WLAN_CIPHER_SUITE_WEP104:
+ if (params->key_len != WLAN_KEY_LEN_WEP104) {
+ ret = -EINVAL;
+ goto exit;
+ }
+ case WLAN_CIPHER_SUITE_TKIP:
+ case WLAN_CIPHER_SUITE_CCMP:
+ break;
+ default:
+ ret = -ENOTSUPP;
+ goto exit;
+ }
+
+ if (key_index >= WEP_KEYS || params->key_len < 0) {
+ ret = -EINVAL;
+ goto exit;
+ }
+
+ eth_broadcast_addr(sta_addr);
+
+ if (!mac_addr || is_broadcast_ether_addr(mac_addr))
+ set_tx = 0; /* for wpa/wpa2 group key */
+ else
+ set_tx = 1; /* for wpa/wpa2 pairwise key */
+
+ if (check_fwstate(pmlmepriv, WIFI_STATION_STATE)) {
+ ret = rtw_cfg80211_set_encryption(ndev, key_index, set_tx,
+ sta_addr, params);
+ } else if (check_fwstate(pmlmepriv, WIFI_AP_STATE)) {
+#ifdef CONFIG_8723AU_AP_MODE
+ if (mac_addr)
+ ether_addr_copy(sta_addr, mac_addr);
+
+ ret = rtw_cfg80211_ap_set_encryption(ndev, key_index, set_tx,
+ sta_addr, params);
+#endif
+ } else {
+ DBG_8723A("error! fw_state = 0x%x, iftype =%d\n",
+ pmlmepriv->fw_state, rtw_wdev->iftype);
+
+ }
+
+exit:
+ return ret;
+}
+
+static int
+cfg80211_rtw_get_key(struct wiphy *wiphy, struct net_device *ndev,
+ u8 key_index, bool pairwise, const u8 *mac_addr,
+ void *cookie,
+ void (*callback) (void *cookie, struct key_params *))
+{
+ DBG_8723A("%s(%s)\n", __func__, ndev->name);
+ return 0;
+}
+
+static int cfg80211_rtw_del_key(struct wiphy *wiphy, struct net_device *ndev,
+ u8 key_index, bool pairwise,
+ const u8 *mac_addr)
+{
+ struct rtw_adapter *padapter = netdev_priv(ndev);
+ struct security_priv *psecuritypriv = &padapter->securitypriv;
+
+ DBG_8723A("%s(%s): key_index =%d\n", __func__, ndev->name, key_index);
+
+ if (key_index == psecuritypriv->dot11PrivacyKeyIndex) {
+ /* clear the flag of wep default key set. */
+ psecuritypriv->bWepDefaultKeyIdxSet = 0;
+ }
+
+ return 0;
+}
+
+static int cfg80211_rtw_set_default_key(struct wiphy *wiphy,
+ struct net_device *ndev, u8 key_index,
+ bool unicast, bool multicast)
+{
+ struct rtw_adapter *padapter = netdev_priv(ndev);
+ struct security_priv *psecuritypriv = &padapter->securitypriv;
+
+ DBG_8723A("%s(%s): key_index =%d, unicast =%d, multicast =%d.\n",
+ __func__, ndev->name, key_index, unicast, multicast);
+
+ if (key_index < NUM_WEP_KEYS &&
+ (psecuritypriv->dot11PrivacyAlgrthm == WLAN_CIPHER_SUITE_WEP40 ||
+ psecuritypriv->dot11PrivacyAlgrthm == WLAN_CIPHER_SUITE_WEP104)) {
+ /* set wep default key */
+ psecuritypriv->ndisencryptstatus = Ndis802_11Encryption1Enabled;
+
+ psecuritypriv->dot11PrivacyKeyIndex = key_index;
+
+ psecuritypriv->dot11PrivacyAlgrthm = WLAN_CIPHER_SUITE_WEP40;
+ psecuritypriv->dot118021XGrpPrivacy = WLAN_CIPHER_SUITE_WEP40;
+ if (psecuritypriv->wep_key[key_index].keylen == 13) {
+ psecuritypriv->dot11PrivacyAlgrthm =
+ WLAN_CIPHER_SUITE_WEP104;
+ psecuritypriv->dot118021XGrpPrivacy =
+ WLAN_CIPHER_SUITE_WEP104;
+ }
+
+ /* set the flag to represent that wep default key
+ has been set */
+ psecuritypriv->bWepDefaultKeyIdxSet = 1;
+ }
+
+ return 0;
+}
+
+static u16 rtw_get_cur_max_rate(struct rtw_adapter *adapter)
+{
+ int i = 0;
+ const u8 *p;
+ u16 rate = 0, max_rate = 0;
+ struct mlme_ext_priv *pmlmeext = &adapter->mlmeextpriv;
+ struct mlme_ext_info *pmlmeinfo = &pmlmeext->mlmext_info;
+ struct registry_priv *pregistrypriv = &adapter->registrypriv;
+ struct mlme_priv *pmlmepriv = &adapter->mlmepriv;
+ struct wlan_bssid_ex *pcur_bss = &pmlmepriv->cur_network.network;
+ struct ieee80211_ht_cap *pht_capie;
+ u8 rf_type = 0;
+ u8 bw_40MHz = 0, short_GI_20 = 0, short_GI_40 = 0;
+ u16 mcs_rate = 0;
+
+ p = cfg80211_find_ie(WLAN_EID_HT_CAPABILITY,
+ pcur_bss->IEs, pcur_bss->IELength);
+ if (p && p[1] > 0) {
+ pht_capie = (struct ieee80211_ht_cap *)(p + 2);
+
+ memcpy(&mcs_rate, &pht_capie->mcs, 2);
+
+ /* bw_40MHz = (pht_capie->cap_info&
+ IEEE80211_HT_CAP_SUP_WIDTH_20_40) ? 1:0; */
+ /* cur_bwmod is updated by beacon, pmlmeinfo is
+ updated by association response */
+ bw_40MHz = (pmlmeext->cur_bwmode &&
+ (pmlmeinfo->HT_info.ht_param &
+ IEEE80211_HT_PARAM_CHAN_WIDTH_ANY)) ? 1:0;
+
+ /* short_GI = (pht_capie->cap_info & (IEEE80211_HT_CAP
+ _SGI_20|IEEE80211_HT_CAP_SGI_40)) ? 1 : 0; */
+ short_GI_20 = (pmlmeinfo->ht_cap.cap_info &
+ cpu_to_le16(IEEE80211_HT_CAP_SGI_20)) ? 1:0;
+ short_GI_40 = (pmlmeinfo->ht_cap.cap_info &
+ cpu_to_le16(IEEE80211_HT_CAP_SGI_40)) ? 1:0;
+
+ rf_type = rtl8723a_get_rf_type(adapter);
+ max_rate = rtw_mcs_rate23a(rf_type, bw_40MHz &
+ pregistrypriv->cbw40_enable,
+ short_GI_20, short_GI_40,
+ &pmlmeinfo->ht_cap.mcs);
+ } else {
+ while (pcur_bss->SupportedRates[i] != 0 &&
+ pcur_bss->SupportedRates[i] != 0xFF) {
+ rate = pcur_bss->SupportedRates[i] & 0x7F;
+ if (rate>max_rate)
+ max_rate = rate;
+ i++;
+ }
+
+ max_rate = max_rate * 10 / 2;
+ }
+
+ return max_rate;
+}
+
+static int cfg80211_rtw_get_station(struct wiphy *wiphy,
+ struct net_device *ndev,
+ const u8 *mac, struct station_info *sinfo)
+{
+ int ret = 0;
+ struct rtw_adapter *padapter = wiphy_to_adapter(wiphy);
+ struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
+ struct sta_info *psta = NULL;
+ struct sta_priv *pstapriv = &padapter->stapriv;
+
+ sinfo->filled = 0;
+
+ if (!mac) {
+ DBG_8723A("%s(%s): mac ==%p\n", __func__, ndev->name, mac);
+ ret = -ENOENT;
+ goto exit;
+ }
+
+ psta = rtw_get_stainfo23a(pstapriv, mac);
+ if (psta == NULL) {
+ DBG_8723A("%s, sta_info is null\n", __func__);
+ ret = -ENOENT;
+ goto exit;
+ }
+ DBG_8723A("%s(%s): mac=%pM\n", __func__, ndev->name, mac);
+
+ /* for infra./P2PClient mode */
+ if (check_fwstate(pmlmepriv, WIFI_STATION_STATE) &&
+ check_fwstate(pmlmepriv, _FW_LINKED)) {
+ struct wlan_network *cur_network = &pmlmepriv->cur_network;
+
+ if (!ether_addr_equal(mac, cur_network->network.MacAddress)) {
+ DBG_8723A("%s, mismatch bssid=%pM\n",
+ __func__, cur_network->network.MacAddress);
+ ret = -ENOENT;
+ goto exit;
+ }
+
+ sinfo->filled |= BIT(NL80211_STA_INFO_SIGNAL);
+ sinfo->signal = translate_percentage_to_dbm(padapter->recvpriv.
+ signal_strength);
+
+ sinfo->filled |= BIT(NL80211_STA_INFO_TX_BITRATE);
+ sinfo->txrate.legacy = rtw_get_cur_max_rate(padapter);
+
+ sinfo->filled |= BIT(NL80211_STA_INFO_RX_PACKETS);
+ sinfo->rx_packets = sta_rx_data_pkts(psta);
+
+ sinfo->filled |= BIT(NL80211_STA_INFO_TX_PACKETS);
+ sinfo->tx_packets = psta->sta_stats.tx_pkts;
+ }
+
+ /* for Ad-Hoc/AP mode */
+ if ((check_fwstate(pmlmepriv, WIFI_ADHOC_STATE) ||
+ check_fwstate(pmlmepriv, WIFI_ADHOC_MASTER_STATE) ||
+ check_fwstate(pmlmepriv, WIFI_AP_STATE)) &&
+ check_fwstate(pmlmepriv, _FW_LINKED)
+ ) {
+ /* TODO: should acquire station info... */
+ }
+
+exit:
+ return ret;
+}
+
+static int cfg80211_infrastructure_mode(struct rtw_adapter *padapter,
+ enum nl80211_iftype ifmode)
+{
+ struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
+ struct wlan_network *cur_network = &pmlmepriv->cur_network;
+ enum nl80211_iftype old_mode;
+
+ old_mode = cur_network->network.ifmode;
+
+ RT_TRACE(_module_rtl871x_ioctl_set_c_, _drv_notice_,
+ "+%s: old =%d new =%d fw_state = 0x%08x\n", __func__,
+ old_mode, ifmode, get_fwstate(pmlmepriv));
+
+ if (old_mode != ifmode) {
+ spin_lock_bh(&pmlmepriv->lock);
+
+ RT_TRACE(_module_rtl871x_ioctl_set_c_, _drv_info_,
+ "change mode!\n");
+
+ if (old_mode == NL80211_IFTYPE_AP ||
+ old_mode == NL80211_IFTYPE_P2P_GO) {
+ /* change to other mode from Ndis802_11APMode */
+ cur_network->join_res = -1;
+
+#ifdef CONFIG_8723AU_AP_MODE
+ stop_ap_mode23a(padapter);
+#endif
+ }
+
+ if (check_fwstate(pmlmepriv, _FW_LINKED) ||
+ old_mode == NL80211_IFTYPE_ADHOC)
+ rtw_disassoc_cmd23a(padapter, 0, true);
+
+ if (check_fwstate(pmlmepriv, _FW_LINKED) ||
+ check_fwstate(pmlmepriv, WIFI_ADHOC_MASTER_STATE))
+ rtw_free_assoc_resources23a(padapter, 1);
+
+ if (old_mode == NL80211_IFTYPE_STATION ||
+ old_mode == NL80211_IFTYPE_P2P_CLIENT ||
+ old_mode == NL80211_IFTYPE_ADHOC) {
+ if (check_fwstate(pmlmepriv, _FW_LINKED)) {
+ /* will clr Linked_state; before this function,
+ we must have chked whether issue
+ dis-assoc_cmd or not */
+ rtw_indicate_disconnect23a(padapter);
+ }
+ }
+
+ cur_network->network.ifmode = ifmode;
+
+ _clr_fwstate_(pmlmepriv, ~WIFI_NULL_STATE);
+
+ switch (ifmode) {
+ case NL80211_IFTYPE_ADHOC:
+ set_fwstate(pmlmepriv, WIFI_ADHOC_STATE);
+ break;
+
+ case NL80211_IFTYPE_P2P_CLIENT:
+ case NL80211_IFTYPE_STATION:
+ set_fwstate(pmlmepriv, WIFI_STATION_STATE);
+ break;
+
+ case NL80211_IFTYPE_P2P_GO:
+ case NL80211_IFTYPE_AP:
+ set_fwstate(pmlmepriv, WIFI_AP_STATE);
+#ifdef CONFIG_8723AU_AP_MODE
+ start_ap_mode23a(padapter);
+ /* rtw_indicate_connect23a(padapter); */
+#endif
+ break;
+
+ default:
+ break;
+ }
+
+ /* SecClearAllKeys(adapter); */
+
+ spin_unlock_bh(&pmlmepriv->lock);
+ }
+
+ return _SUCCESS;
+}
+
+static int cfg80211_rtw_change_iface(struct wiphy *wiphy,
+ struct net_device *ndev,
+ enum nl80211_iftype type, u32 *flags,
+ struct vif_params *params)
+{
+ enum nl80211_iftype old_type;
+ struct rtw_adapter *padapter = wiphy_to_adapter(wiphy);
+ struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv;
+ struct wireless_dev *rtw_wdev = wiphy_to_wdev(wiphy);
+ int ret = 0;
+
+ DBG_8723A("%s(%s): call netdev_open23a\n", __func__, ndev->name);
+
+ old_type = rtw_wdev->iftype;
+ DBG_8723A("%s(%s): old_iftype =%d, new_iftype =%d\n",
+ __func__, ndev->name, old_type, type);
+
+ if (old_type != type) {
+ pmlmeext->action_public_rxseq = 0xffff;
+ pmlmeext->action_public_dialog_token = 0xff;
+ }
+
+ switch (type) {
+ case NL80211_IFTYPE_ADHOC:
+ case NL80211_IFTYPE_P2P_CLIENT:
+ case NL80211_IFTYPE_STATION:
+ case NL80211_IFTYPE_P2P_GO:
+ case NL80211_IFTYPE_AP:
+ case NL80211_IFTYPE_UNSPECIFIED:
+ break;
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ rtw_wdev->iftype = type;
+
+ if (cfg80211_infrastructure_mode(padapter, type) != _SUCCESS) {
+ rtw_wdev->iftype = old_type;
+ ret = -EPERM;
+ goto exit;
+ }
+
+ rtw_setopmode_cmd23a(padapter, type);
+
+exit:
+ return ret;
+}
+
+void rtw_cfg80211_indicate_scan_done(struct rtw_wdev_priv *pwdev_priv,
+ bool aborted)
+{
+ spin_lock_bh(&pwdev_priv->scan_req_lock);
+ if (pwdev_priv->scan_request != NULL) {
+ DBG_8723A("%s with scan req\n", __func__);
+
+ if (pwdev_priv->scan_request->wiphy !=
+ pwdev_priv->rtw_wdev->wiphy)
+ DBG_8723A("error wiphy compare\n");
+ else
+ cfg80211_scan_done(pwdev_priv->scan_request, aborted);
+
+ pwdev_priv->scan_request = NULL;
+ } else {
+ DBG_8723A("%s without scan req\n", __func__);
+ }
+ spin_unlock_bh(&pwdev_priv->scan_req_lock);
+}
+
+void rtw_cfg80211_surveydone_event_callback(struct rtw_adapter *padapter)
+{
+ struct list_head *plist, *phead, *ptmp;
+ struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
+ struct rtw_queue *queue = &pmlmepriv->scanned_queue;
+ struct wlan_network *pnetwork;
+
+ spin_lock_bh(&pmlmepriv->scanned_queue.lock);
+
+ phead = get_list_head(queue);
+
+ list_for_each_safe(plist, ptmp, phead) {
+ pnetwork = container_of(plist, struct wlan_network, list);
+
+ /* report network only if the current channel set
+ contains the channel to which this network belongs */
+ if (rtw_ch_set_search_ch23a
+ (padapter->mlmeextpriv.channel_set,
+ pnetwork->network.DSConfig) >= 0)
+ rtw_cfg80211_inform_bss(padapter, pnetwork);
+ }
+
+ spin_unlock_bh(&pmlmepriv->scanned_queue.lock);
+
+ /* call this after other things have been done */
+ rtw_cfg80211_indicate_scan_done(wdev_to_priv(padapter->rtw_wdev),
+ false);
+}
+
+static int rtw_cfg80211_set_probe_req_wpsp2pie(struct rtw_adapter *padapter,
+ char *buf, int len)
+{
+ int ret = 0;
+ const u8 *wps_ie;
+ struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
+
+ DBG_8723A("%s, ielen =%d\n", __func__, len);
+
+ if (len > 0) {
+ wps_ie = cfg80211_find_vendor_ie(WLAN_OUI_MICROSOFT,
+ WLAN_OUI_TYPE_MICROSOFT_WPS,
+ buf, len);
+ if (wps_ie) {
+ DBG_8723A("probe_req_wps_ielen =%d\n", wps_ie[1]);
+
+ if (pmlmepriv->wps_probe_req_ie) {
+ pmlmepriv->wps_probe_req_ie_len = 0;
+ kfree(pmlmepriv->wps_probe_req_ie);
+ pmlmepriv->wps_probe_req_ie = NULL;
+ }
+
+ pmlmepriv->wps_probe_req_ie = kmemdup(wps_ie, wps_ie[1],
+ GFP_KERNEL);
+ if (pmlmepriv->wps_probe_req_ie == NULL) {
+ DBG_8723A("%s()-%d: kmalloc() ERROR!\n",
+ __func__, __LINE__);
+ return -EINVAL;
+ }
+ pmlmepriv->wps_probe_req_ie_len = wps_ie[1];
+ }
+ }
+
+ return ret;
+}
+
+static int cfg80211_rtw_scan(struct wiphy *wiphy,
+ struct cfg80211_scan_request *request)
+{
+ int i;
+ u8 _status = false;
+ int ret = 0;
+ struct rtw_adapter *padapter = wiphy_to_adapter(wiphy);
+ struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
+ struct cfg80211_ssid ssid[RTW_SSID_SCAN_AMOUNT];
+ struct rtw_ieee80211_channel ch[RTW_CHANNEL_SCAN_AMOUNT];
+ struct rtw_wdev_priv *pwdev_priv = wdev_to_priv(padapter->rtw_wdev);
+ struct cfg80211_ssid *ssids = request->ssids;
+ bool need_indicate_scan_done = false;
+
+ DBG_8723A("%s(%s)\n", __func__, padapter->pnetdev->name);
+
+ spin_lock_bh(&pwdev_priv->scan_req_lock);
+ pwdev_priv->scan_request = request;
+ spin_unlock_bh(&pwdev_priv->scan_req_lock);
+
+ if (check_fwstate(pmlmepriv, WIFI_AP_STATE)) {
+ DBG_8723A("%s under WIFI_AP_STATE\n", __func__);
+ /* need_indicate_scan_done = true; */
+ /* goto check_need_indicate_scan_done; */
+ }
+
+ if (rtw_pwr_wakeup(padapter) == _FAIL) {
+ need_indicate_scan_done = true;
+ goto check_need_indicate_scan_done;
+ }
+
+ if (request->ie && request->ie_len > 0) {
+ rtw_cfg80211_set_probe_req_wpsp2pie(padapter,
+ (u8 *) request->ie,
+ request->ie_len);
+ }
+
+ if (pmlmepriv->LinkDetectInfo.bBusyTraffic == true) {
+ DBG_8723A("%s, bBusyTraffic == true\n", __func__);
+ need_indicate_scan_done = true;
+ goto check_need_indicate_scan_done;
+ }
+ if (rtw_is_scan_deny(padapter)) {
+ DBG_8723A("%s(%s): scan deny\n", __func__,
+ padapter->pnetdev->name);
+ need_indicate_scan_done = true;
+ goto check_need_indicate_scan_done;
+ }
+
+ if (check_fwstate(pmlmepriv, _FW_UNDER_SURVEY | _FW_UNDER_LINKING) ==
+ true) {
+ DBG_8723A("%s, fwstate = 0x%x\n", __func__, pmlmepriv->fw_state);
+ need_indicate_scan_done = true;
+ goto check_need_indicate_scan_done;
+ }
+
+ memset(ssid, 0, sizeof(struct cfg80211_ssid) * RTW_SSID_SCAN_AMOUNT);
+ /* parsing request ssids, n_ssids */
+ for (i = 0; i < request->n_ssids && i < RTW_SSID_SCAN_AMOUNT; i++) {
+ DBG_8723A("ssid =%s, len =%d\n", ssids[i].ssid,
+ ssids[i].ssid_len);
+ memcpy(ssid[i].ssid, ssids[i].ssid, ssids[i].ssid_len);
+ ssid[i].ssid_len = ssids[i].ssid_len;
+ }
+
+ /* parsing channels, n_channels */
+ memset(ch, 0,
+ sizeof(struct rtw_ieee80211_channel) * RTW_CHANNEL_SCAN_AMOUNT);
+
+ if (request->n_channels == 1) {
+ for (i = 0; i < request->n_channels &&
+ i < RTW_CHANNEL_SCAN_AMOUNT; i++) {
+ DBG_8723A("%s:(%s):" CHAN_FMT "\n",
+ __func__, padapter->pnetdev->name,
+ CHAN_ARG(request->channels[i]));
+ ch[i].hw_value = request->channels[i]->hw_value;
+ ch[i].flags = request->channels[i]->flags;
+ }
+ }
+
+ spin_lock_bh(&pmlmepriv->lock);
+ if (request->n_channels == 1) {
+ memcpy(&ch[1], &ch[0], sizeof(struct rtw_ieee80211_channel));
+ memcpy(&ch[2], &ch[0], sizeof(struct rtw_ieee80211_channel));
+ _status = rtw_sitesurvey_cmd23a(padapter, ssid,
+ RTW_SSID_SCAN_AMOUNT, ch, 3);
+ } else {
+ _status = rtw_sitesurvey_cmd23a(padapter, ssid,
+ RTW_SSID_SCAN_AMOUNT, NULL, 0);
+ }
+ spin_unlock_bh(&pmlmepriv->lock);
+
+ if (_status == false)
+ ret = -1;
+
+check_need_indicate_scan_done:
+ if (need_indicate_scan_done)
+ rtw_cfg80211_surveydone_event_callback(padapter);
+ return ret;
+}
+
+static int cfg80211_rtw_set_wiphy_params(struct wiphy *wiphy, u32 changed)
+{
+ DBG_8723A("%s\n", __func__);
+ return 0;
+}
+
+static int cfg80211_rtw_join_ibss(struct wiphy *wiphy, struct net_device *ndev,
+ struct cfg80211_ibss_params *params)
+{
+ DBG_8723A("%s(%s)\n", __func__, ndev->name);
+ return 0;
+}
+
+static int cfg80211_rtw_leave_ibss(struct wiphy *wiphy, struct net_device *ndev)
+{
+ DBG_8723A("%s(%s)\n", __func__, ndev->name);
+ return 0;
+}
+
+static int rtw_cfg80211_set_wpa_version(struct security_priv *psecuritypriv,
+ u32 wpa_version)
+{
+ DBG_8723A("%s, wpa_version =%d\n", __func__, wpa_version);
+
+ if (!wpa_version) {
+ psecuritypriv->ndisauthtype = Ndis802_11AuthModeOpen;
+ return 0;
+ }
+
+ if (wpa_version & (NL80211_WPA_VERSION_1 | NL80211_WPA_VERSION_2))
+ psecuritypriv->ndisauthtype = Ndis802_11AuthModeWPAPSK;
+
+/*
+ if (wpa_version & NL80211_WPA_VERSION_2)
+ {
+ psecuritypriv->ndisauthtype = Ndis802_11AuthModeWPA2PSK;
+ }
+*/
+
+ return 0;
+}
+
+static int rtw_cfg80211_set_auth_type(struct security_priv *psecuritypriv,
+ enum nl80211_auth_type sme_auth_type)
+{
+ DBG_8723A("%s, nl80211_auth_type =%d\n", __func__, sme_auth_type);
+
+ switch (sme_auth_type) {
+ case NL80211_AUTHTYPE_AUTOMATIC:
+ psecuritypriv->dot11AuthAlgrthm = dot11AuthAlgrthm_Auto;
+
+ break;
+ case NL80211_AUTHTYPE_OPEN_SYSTEM:
+ psecuritypriv->dot11AuthAlgrthm = dot11AuthAlgrthm_Open;
+
+ if (psecuritypriv->ndisauthtype > Ndis802_11AuthModeWPA)
+ psecuritypriv->dot11AuthAlgrthm =
+ dot11AuthAlgrthm_8021X;
+ break;
+ case NL80211_AUTHTYPE_SHARED_KEY:
+ psecuritypriv->dot11AuthAlgrthm = dot11AuthAlgrthm_Shared;
+
+ psecuritypriv->ndisencryptstatus = Ndis802_11Encryption1Enabled;
+ break;
+ default:
+ psecuritypriv->dot11AuthAlgrthm = dot11AuthAlgrthm_Open;
+ /* return -ENOTSUPP; */
+ }
+
+ return 0;
+}
+
+static int rtw_cfg80211_set_cipher(struct security_priv *psecuritypriv,
+ u32 cipher, bool ucast)
+{
+ u32 ndisencryptstatus = Ndis802_11EncryptionDisabled;
+
+ u32 *profile_cipher = ucast ? &psecuritypriv->dot11PrivacyAlgrthm :
+ &psecuritypriv->dot118021XGrpPrivacy;
+
+ DBG_8723A("%s, ucast =%d, cipher = 0x%x\n", __func__, ucast, cipher);
+
+ if (!cipher) {
+ *profile_cipher = 0;
+ psecuritypriv->ndisencryptstatus = ndisencryptstatus;
+ return 0;
+ }
+
+ switch (cipher) {
+ case IW_AUTH_CIPHER_NONE:
+ *profile_cipher = 0;
+ ndisencryptstatus = Ndis802_11EncryptionDisabled;
+ break;
+ case WLAN_CIPHER_SUITE_WEP40:
+ *profile_cipher = WLAN_CIPHER_SUITE_WEP40;
+ ndisencryptstatus = Ndis802_11Encryption1Enabled;
+ break;
+ case WLAN_CIPHER_SUITE_WEP104:
+ *profile_cipher = WLAN_CIPHER_SUITE_WEP104;
+ ndisencryptstatus = Ndis802_11Encryption1Enabled;
+ break;
+ case WLAN_CIPHER_SUITE_TKIP:
+ *profile_cipher = WLAN_CIPHER_SUITE_TKIP;
+ ndisencryptstatus = Ndis802_11Encryption2Enabled;
+ break;
+ case WLAN_CIPHER_SUITE_CCMP:
+ *profile_cipher = WLAN_CIPHER_SUITE_CCMP;
+ ndisencryptstatus = Ndis802_11Encryption3Enabled;
+ break;
+ default:
+ DBG_8723A("Unsupported cipher: 0x%x\n", cipher);
+ return -ENOTSUPP;
+ }
+
+ if (ucast)
+ psecuritypriv->ndisencryptstatus = ndisencryptstatus;
+
+ return 0;
+}
+
+static int rtw_cfg80211_set_key_mgt(struct security_priv *psecuritypriv,
+ u32 key_mgt)
+{
+ DBG_8723A("%s, key_mgt = 0x%x\n", __func__, key_mgt);
+
+ if (key_mgt == WLAN_AKM_SUITE_8021X)
+ psecuritypriv->dot11AuthAlgrthm = dot11AuthAlgrthm_8021X;
+ else if (key_mgt == WLAN_AKM_SUITE_PSK)
+ psecuritypriv->dot11AuthAlgrthm = dot11AuthAlgrthm_8021X;
+ else
+ DBG_8723A("Invalid key mgt: 0x%x\n", key_mgt);
+
+ return 0;
+}
+
+static int rtw_cfg80211_set_wpa_ie(struct rtw_adapter *padapter, const u8 *pie,
+ size_t ielen)
+{
+ const u8 *wps_ie;
+ int group_cipher = 0, pairwise_cipher = 0;
+ int ret = 0;
+ const u8 *pwpa, *pwpa2;
+ int i;
+
+ if (!pie || !ielen) {
+ /* Treat this as normal case, but need to clear
+ WIFI_UNDER_WPS */
+ _clr_fwstate_(&padapter->mlmepriv, WIFI_UNDER_WPS);
+ goto exit;
+ }
+ if (ielen > MAX_WPA_IE_LEN + MAX_WPS_IE_LEN + MAX_P2P_IE_LEN) {
+ ret = -EINVAL;
+ goto exit;
+ }
+
+ /* dump */
+ DBG_8723A("set wpa_ie(length:%zu):\n", ielen);
+ for (i = 0; i < ielen; i = i + 8)
+ DBG_8723A("0x%.2x 0x%.2x 0x%.2x 0x%.2x "
+ "0x%.2x 0x%.2x 0x%.2x 0x%.2x\n",
+ pie[i], pie[i + 1], pie[i + 2], pie[i + 3],
+ pie[i + 4], pie[i + 5], pie[i + 6], pie[i + 7]);
+ if (ielen < RSN_HEADER_LEN) {
+ RT_TRACE(_module_rtl871x_ioctl_os_c, _drv_err_,
+ "Ie len too short %d\n", (int)ielen);
+ ret = -1;
+ goto exit;
+ }
+
+ pwpa = cfg80211_find_vendor_ie(WLAN_OUI_MICROSOFT,
+ WLAN_OUI_TYPE_MICROSOFT_WPA,
+ pie, ielen);
+ if (pwpa && pwpa[1] > 0) {
+ if (rtw_parse_wpa_ie23a(pwpa, pwpa[1] + 2, &group_cipher,
+ &pairwise_cipher, NULL) == _SUCCESS) {
+ padapter->securitypriv.dot11AuthAlgrthm =
+ dot11AuthAlgrthm_8021X;
+ padapter->securitypriv.ndisauthtype =
+ Ndis802_11AuthModeWPAPSK;
+ memcpy(padapter->securitypriv.supplicant_ie, pwpa,
+ pwpa[1] + 2);
+
+ DBG_8723A("got wpa_ie, wpa_ielen:%u\n", pwpa[1]);
+ }
+ }
+
+ pwpa2 = cfg80211_find_ie(WLAN_EID_RSN, pie, ielen);
+ if (pwpa2 && pwpa2[1] > 0) {
+ if (rtw_parse_wpa2_ie23a (pwpa2, pwpa2[1] + 2, &group_cipher,
+ &pairwise_cipher, NULL) == _SUCCESS) {
+ padapter->securitypriv.dot11AuthAlgrthm =
+ dot11AuthAlgrthm_8021X;
+ padapter->securitypriv.ndisauthtype =
+ Ndis802_11AuthModeWPA2PSK;
+ memcpy(padapter->securitypriv.supplicant_ie, pwpa2,
+ pwpa2[1] + 2);
+
+ DBG_8723A("got wpa2_ie, wpa2_ielen:%u\n", pwpa2[1]);
+ }
+ }
+
+ if (group_cipher == 0) {
+ group_cipher = WPA_CIPHER_NONE;
+ }
+ if (pairwise_cipher == 0) {
+ pairwise_cipher = WPA_CIPHER_NONE;
+ }
+
+ switch (group_cipher) {
+ case WPA_CIPHER_NONE:
+ padapter->securitypriv.dot118021XGrpPrivacy = 0;
+ padapter->securitypriv.ndisencryptstatus =
+ Ndis802_11EncryptionDisabled;
+ break;
+ case WPA_CIPHER_WEP40:
+ padapter->securitypriv.dot118021XGrpPrivacy = WLAN_CIPHER_SUITE_WEP40;
+ padapter->securitypriv.ndisencryptstatus =
+ Ndis802_11Encryption1Enabled;
+ break;
+ case WPA_CIPHER_TKIP:
+ padapter->securitypriv.dot118021XGrpPrivacy = WLAN_CIPHER_SUITE_TKIP;
+ padapter->securitypriv.ndisencryptstatus =
+ Ndis802_11Encryption2Enabled;
+ break;
+ case WPA_CIPHER_CCMP:
+ padapter->securitypriv.dot118021XGrpPrivacy = WLAN_CIPHER_SUITE_CCMP;
+ padapter->securitypriv.ndisencryptstatus =
+ Ndis802_11Encryption3Enabled;
+ break;
+ case WPA_CIPHER_WEP104:
+ padapter->securitypriv.dot118021XGrpPrivacy = WLAN_CIPHER_SUITE_WEP104;
+ padapter->securitypriv.ndisencryptstatus =
+ Ndis802_11Encryption1Enabled;
+ break;
+ }
+
+ switch (pairwise_cipher) {
+ case WPA_CIPHER_NONE:
+ padapter->securitypriv.dot11PrivacyAlgrthm = 0;
+ padapter->securitypriv.ndisencryptstatus =
+ Ndis802_11EncryptionDisabled;
+ break;
+ case WPA_CIPHER_WEP40:
+ padapter->securitypriv.dot11PrivacyAlgrthm = WLAN_CIPHER_SUITE_WEP40;
+ padapter->securitypriv.ndisencryptstatus =
+ Ndis802_11Encryption1Enabled;
+ break;
+ case WPA_CIPHER_TKIP:
+ padapter->securitypriv.dot11PrivacyAlgrthm = WLAN_CIPHER_SUITE_TKIP;
+ padapter->securitypriv.ndisencryptstatus =
+ Ndis802_11Encryption2Enabled;
+ break;
+ case WPA_CIPHER_CCMP:
+ padapter->securitypriv.dot11PrivacyAlgrthm = WLAN_CIPHER_SUITE_CCMP;
+ padapter->securitypriv.ndisencryptstatus =
+ Ndis802_11Encryption3Enabled;
+ break;
+ case WPA_CIPHER_WEP104:
+ padapter->securitypriv.dot11PrivacyAlgrthm = WLAN_CIPHER_SUITE_WEP104;
+ padapter->securitypriv.ndisencryptstatus =
+ Ndis802_11Encryption1Enabled;
+ break;
+ }
+
+ wps_ie = cfg80211_find_vendor_ie(WLAN_OUI_MICROSOFT,
+ WLAN_OUI_TYPE_MICROSOFT_WPS,
+ pie, ielen);
+ if (wps_ie && wps_ie[1] > 0) {
+ DBG_8723A("got wps_ie, wps_ielen:%u\n", wps_ie[1]);
+ padapter->securitypriv.wps_ie_len = wps_ie[1];
+ memcpy(padapter->securitypriv.wps_ie, wps_ie,
+ padapter->securitypriv.wps_ie_len);
+ set_fwstate(&padapter->mlmepriv, WIFI_UNDER_WPS);
+ } else {
+ _clr_fwstate_(&padapter->mlmepriv, WIFI_UNDER_WPS);
+ }
+
+ /* TKIP and AES disallow multicast packets until installing group key */
+ if (padapter->securitypriv.dot11PrivacyAlgrthm ==
+ WLAN_CIPHER_SUITE_TKIP ||
+ padapter->securitypriv.dot11PrivacyAlgrthm ==
+ WLAN_CIPHER_SUITE_CCMP)
+ /* WPS open need to enable multicast */
+ /* check_fwstate(&padapter->mlmepriv, WIFI_UNDER_WPS) == true)*/
+ rtl8723a_off_rcr_am(padapter);
+
+ RT_TRACE(_module_rtl871x_ioctl_os_c, _drv_info_,
+ "rtw_set_wpa_ie: pairwise_cipher = 0x%08x padapter->securitypriv.ndisencryptstatus =%d padapter->securitypriv.ndisauthtype =%d\n",
+ pairwise_cipher,
+ padapter->securitypriv.ndisencryptstatus,
+ padapter->securitypriv.ndisauthtype);
+
+exit:
+ if (ret)
+ _clr_fwstate_(&padapter->mlmepriv, WIFI_UNDER_WPS);
+ return ret;
+}
+
+static int rtw_cfg80211_add_wep(struct rtw_adapter *padapter,
+ struct rtw_wep_key *wep, u8 keyid)
+{
+ int res;
+ struct security_priv *psecuritypriv = &padapter->securitypriv;
+
+ if (keyid >= NUM_WEP_KEYS) {
+ RT_TRACE(_module_rtl871x_ioctl_set_c_, _drv_err_,
+ "%s:keyid>4 =>fail\n", __func__);
+ res = _FAIL;
+ goto exit;
+ }
+
+ switch (wep->keylen) {
+ case WLAN_KEY_LEN_WEP40:
+ psecuritypriv->dot11PrivacyAlgrthm = WLAN_CIPHER_SUITE_WEP40;
+ RT_TRACE(_module_rtl871x_ioctl_set_c_, _drv_info_,
+ "%s:wep->KeyLength = 5\n", __func__);
+ break;
+ case WLAN_KEY_LEN_WEP104:
+ psecuritypriv->dot11PrivacyAlgrthm = WLAN_CIPHER_SUITE_WEP104;
+ RT_TRACE(_module_rtl871x_ioctl_set_c_, _drv_info_,
+ "%s:wep->KeyLength = 13\n", __func__);
+ break;
+ default:
+ psecuritypriv->dot11PrivacyAlgrthm = 0;
+ RT_TRACE(_module_rtl871x_ioctl_set_c_, _drv_info_,
+ "%s:wep->KeyLength!= 5 or 13\n", __func__);
+ res = _FAIL;
+ goto exit;
+ }
+
+ RT_TRACE(_module_rtl871x_ioctl_set_c_, _drv_info_,
+ "%s:before memcpy, wep->KeyLength = 0x%x keyid =%x\n",
+ __func__, wep->keylen, keyid);
+
+ memcpy(&psecuritypriv->wep_key[keyid], wep, sizeof(struct rtw_wep_key));
+
+ psecuritypriv->dot11PrivacyKeyIndex = keyid;
+
+ RT_TRACE(_module_rtl871x_ioctl_set_c_, _drv_info_,
+ "%s:security key material : %x %x %x %x %x %x %x %x %x %x %x %x %x\n",
+ __func__,
+ psecuritypriv->wep_key[keyid].key[0],
+ psecuritypriv->wep_key[keyid].key[1],
+ psecuritypriv->wep_key[keyid].key[2],
+ psecuritypriv->wep_key[keyid].key[3],
+ psecuritypriv->wep_key[keyid].key[4],
+ psecuritypriv->wep_key[keyid].key[5],
+ psecuritypriv->wep_key[keyid].key[6],
+ psecuritypriv->wep_key[keyid].key[7],
+ psecuritypriv->wep_key[keyid].key[8],
+ psecuritypriv->wep_key[keyid].key[9],
+ psecuritypriv->wep_key[keyid].key[10],
+ psecuritypriv->wep_key[keyid].key[11],
+ psecuritypriv->wep_key[keyid].key[12]);
+
+ res = rtw_set_key23a(padapter, psecuritypriv, keyid, 1);
+
+exit:
+
+ return res;
+}
+
+static int rtw_set_ssid(struct rtw_adapter *padapter,
+ struct wlan_network *newnetwork)
+{
+ struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
+ struct wlan_network *pnetwork = &pmlmepriv->cur_network;
+ int status = _SUCCESS;
+ u32 cur_time = 0;
+
+ DBG_8723A_LEVEL(_drv_always_, "set ssid [%s] fw_state = 0x%08x\n",
+ newnetwork->network.Ssid.ssid, get_fwstate(pmlmepriv));
+
+ if (padapter->hw_init_completed == false) {
+ RT_TRACE(_module_rtl871x_ioctl_set_c_, _drv_err_,
+ "set_ssid: hw_init_completed == false =>exit!!!\n");
+ status = _FAIL;
+ goto exit;
+ }
+
+ spin_lock_bh(&pmlmepriv->lock);
+
+ DBG_8723A("Set SSID under fw_state = 0x%08x\n", get_fwstate(pmlmepriv));
+ if (check_fwstate(pmlmepriv, _FW_UNDER_SURVEY))
+ goto handle_tkip_countermeasure;
+
+ if (check_fwstate(pmlmepriv, _FW_LINKED|WIFI_ADHOC_MASTER_STATE)) {
+ RT_TRACE(_module_rtl871x_ioctl_set_c_, _drv_info_,
+ "set_ssid: _FW_LINKED||WIFI_ADHOC_MASTER_STATE\n");
+
+ if (pmlmepriv->assoc_ssid.ssid_len ==
+ newnetwork->network.Ssid.ssid_len &&
+ !memcmp(&pmlmepriv->assoc_ssid.ssid,
+ newnetwork->network.Ssid.ssid,
+ newnetwork->network.Ssid.ssid_len)) {
+ if (!check_fwstate(pmlmepriv, WIFI_STATION_STATE)) {
+ RT_TRACE(_module_rtl871x_ioctl_set_c_,
+ _drv_err_,
+ "New SSID is same SSID, fw_state = 0x%08x\n",
+ get_fwstate(pmlmepriv));
+
+ if (rtw_is_same_ibss23a(padapter, pnetwork)) {
+ /*
+ * it means driver is in
+ * WIFI_ADHOC_MASTER_STATE, we needn't
+ * create bss again.
+ */
+ goto release_mlme_lock;
+ }
+
+ /*
+ * if in WIFI_ADHOC_MASTER_STATE |
+ * WIFI_ADHOC_STATE, create bss or
+ * rejoin again
+ */
+ rtw_disassoc_cmd23a(padapter, 0, true);
+
+ if (check_fwstate(pmlmepriv, _FW_LINKED))
+ rtw_indicate_disconnect23a(padapter);
+
+ rtw_free_assoc_resources23a(padapter, 1);
+
+ if (check_fwstate(pmlmepriv,
+ WIFI_ADHOC_MASTER_STATE)) {
+ _clr_fwstate_(pmlmepriv,
+ WIFI_ADHOC_MASTER_STATE);
+ set_fwstate(pmlmepriv,
+ WIFI_ADHOC_STATE);
+ }
+ } else {
+ rtw_lps_ctrl_wk_cmd23a(padapter,
+ LPS_CTRL_JOINBSS, 1);
+ }
+ } else {
+ RT_TRACE(_module_rtl871x_ioctl_set_c_, _drv_info_,
+ "Set SSID not the same ssid\n");
+ RT_TRACE(_module_rtl871x_ioctl_set_c_, _drv_info_,
+ "set_ssid =[%s] len = 0x%x\n",
+ newnetwork->network.Ssid.ssid,
+ newnetwork->network.Ssid.ssid_len);
+ RT_TRACE(_module_rtl871x_ioctl_set_c_, _drv_info_,
+ "assoc_ssid =[%s] len = 0x%x\n",
+ pmlmepriv->assoc_ssid.ssid,
+ pmlmepriv->assoc_ssid.ssid_len);
+
+ rtw_disassoc_cmd23a(padapter, 0, true);
+
+ if (check_fwstate(pmlmepriv, _FW_LINKED))
+ rtw_indicate_disconnect23a(padapter);
+
+ rtw_free_assoc_resources23a(padapter, 1);
+
+ if (check_fwstate(pmlmepriv, WIFI_ADHOC_MASTER_STATE)) {
+ _clr_fwstate_(pmlmepriv, WIFI_ADHOC_MASTER_STATE);
+ set_fwstate(pmlmepriv, WIFI_ADHOC_STATE);
+ }
+ }
+ }
+
+handle_tkip_countermeasure:
+
+ if (padapter->securitypriv.btkip_countermeasure == true) {
+ cur_time = jiffies;
+
+ if ((cur_time -
+ padapter->securitypriv.btkip_countermeasure_time) >
+ 60 * HZ) {
+ padapter->securitypriv.btkip_countermeasure = false;
+ padapter->securitypriv.btkip_countermeasure_time = 0;
+ } else {
+ status = _FAIL;
+ goto release_mlme_lock;
+ }
+ }
+
+ memcpy(&pmlmepriv->assoc_ssid, &newnetwork->network.Ssid,
+ sizeof(struct cfg80211_ssid));
+
+ pmlmepriv->assoc_by_bssid = false;
+
+ pmlmepriv->to_join = true;
+
+ if (!check_fwstate(pmlmepriv, _FW_UNDER_SURVEY)) {
+ pmlmepriv->cur_network.join_res = -2;
+
+ status = rtw_do_join_network(padapter, newnetwork);
+ if (status == _SUCCESS) {
+ pmlmepriv->to_join = false;
+ } else {
+ if (check_fwstate(pmlmepriv, WIFI_ADHOC_STATE)) {
+ /* switch to ADHOC_MASTER */
+ status = rtw_do_join_adhoc(padapter);
+ if (status != _SUCCESS)
+ goto release_mlme_lock;
+ } else {
+ /* can't associate ; reset under-linking */
+ _clr_fwstate_(pmlmepriv, _FW_UNDER_LINKING);
+ status = _FAIL;
+ pmlmepriv->to_join = false;
+ }
+ }
+ }
+release_mlme_lock:
+ spin_unlock_bh(&pmlmepriv->lock);
+
+exit:
+ RT_TRACE(_module_rtl871x_ioctl_set_c_, _drv_err_,
+ "-%s: status =%d\n", __func__, status);
+
+ return status;
+}
+
+static int cfg80211_rtw_connect(struct wiphy *wiphy, struct net_device *ndev,
+ struct cfg80211_connect_params *sme)
+{
+ int ret = 0;
+ struct list_head *phead, *plist, *ptmp;
+ struct wlan_network *pnetwork = NULL;
+ /* u8 matched_by_bssid = false; */
+ /* u8 matched_by_ssid = false; */
+ u8 matched = false;
+ struct rtw_adapter *padapter = wiphy_to_adapter(wiphy);
+ struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
+ struct security_priv *psecuritypriv = &padapter->securitypriv;
+ struct rtw_queue *queue = &pmlmepriv->scanned_queue;
+
+ DBG_8723A("=>" "%s(%s)\n", __func__, ndev->name);
+ DBG_8723A("privacy =%d, key =%p, key_len =%d, key_idx =%d\n",
+ sme->privacy, sme->key, sme->key_len, sme->key_idx);
+
+ if (_FAIL == rtw_pwr_wakeup(padapter)) {
+ ret = -EPERM;
+ goto exit;
+ }
+
+ if (check_fwstate(pmlmepriv, WIFI_AP_STATE)) {
+ ret = -EPERM;
+ goto exit;
+ }
+
+ if (!sme->ssid || !sme->ssid_len ||
+ sme->ssid_len > IEEE80211_MAX_SSID_LEN) {
+ ret = -EINVAL;
+ goto exit;
+ }
+
+ DBG_8723A("ssid =%s, len =%zu\n", sme->ssid, sme->ssid_len);
+
+ if (sme->bssid)
+ DBG_8723A("bssid=%pM\n", sme->bssid);
+
+ if (check_fwstate(pmlmepriv, _FW_UNDER_LINKING)) {
+ ret = -EBUSY;
+ DBG_8723A("%s, fw_state = 0x%x, goto exit\n", __func__,
+ pmlmepriv->fw_state);
+ goto exit;
+ }
+ if (check_fwstate(pmlmepriv, _FW_UNDER_SURVEY)) {
+ rtw_scan_abort23a(padapter);
+ }
+
+ spin_lock_bh(&queue->lock);
+
+ phead = get_list_head(queue);
+
+ list_for_each_safe(plist, ptmp, phead) {
+ pnetwork = container_of(plist, struct wlan_network, list);
+
+ if (sme->bssid) {
+ if (!ether_addr_equal(pnetwork->network.MacAddress,
+ sme->bssid))
+ continue;
+ }
+
+ if (sme->ssid && sme->ssid_len) {
+ if (pnetwork->network.Ssid.ssid_len != sme->ssid_len ||
+ memcmp(pnetwork->network.Ssid.ssid, sme->ssid,
+ sme->ssid_len))
+ continue;
+ }
+
+ if (sme->bssid) {
+ if (ether_addr_equal(pnetwork->network.MacAddress,
+ sme->bssid)) {
+ DBG_8723A("matched by bssid\n");
+
+ matched = true;
+ break;
+ }
+ } else if (sme->ssid && sme->ssid_len) {
+ if (!memcmp(pnetwork->network.Ssid.ssid,
+ sme->ssid, sme->ssid_len) &&
+ pnetwork->network.Ssid.ssid_len == sme->ssid_len) {
+ DBG_8723A("matched by ssid\n");
+
+ matched = true;
+ break;
+ }
+ }
+ }
+
+ spin_unlock_bh(&queue->lock);
+
+ if (!matched || !pnetwork) {
+ ret = -ENOENT;
+ DBG_8723A("connect, matched == false, goto exit\n");
+ goto exit;
+ }
+
+ if (cfg80211_infrastructure_mode(
+ padapter, pnetwork->network.ifmode) != _SUCCESS) {
+ ret = -EPERM;
+ goto exit;
+ }
+
+ psecuritypriv->ndisencryptstatus = Ndis802_11EncryptionDisabled;
+ psecuritypriv->dot11PrivacyAlgrthm = 0;
+ psecuritypriv->dot118021XGrpPrivacy = 0;
+ psecuritypriv->dot11AuthAlgrthm = dot11AuthAlgrthm_Open;
+ psecuritypriv->ndisauthtype = Ndis802_11AuthModeOpen;
+
+ ret = rtw_cfg80211_set_wpa_version(psecuritypriv,
+ sme->crypto.wpa_versions);
+ if (ret < 0)
+ goto exit;
+
+ ret = rtw_cfg80211_set_auth_type(psecuritypriv, sme->auth_type);
+
+ if (ret < 0)
+ goto exit;
+
+ DBG_8723A("%s, ie_len =%zu\n", __func__, sme->ie_len);
+
+ ret = rtw_cfg80211_set_wpa_ie(padapter, sme->ie, sme->ie_len);
+ if (ret < 0)
+ goto exit;
+
+ if (sme->crypto.n_ciphers_pairwise) {
+ ret = rtw_cfg80211_set_cipher(psecuritypriv,
+ sme->crypto.ciphers_pairwise[0],
+ true);
+ if (ret < 0)
+ goto exit;
+ }
+
+ /* For WEP Shared auth */
+ if ((psecuritypriv->dot11AuthAlgrthm == dot11AuthAlgrthm_Shared ||
+ psecuritypriv->dot11AuthAlgrthm == dot11AuthAlgrthm_Auto) &&
+ sme->key) {
+ struct rtw_wep_key wep_key;
+ u8 wep_key_idx, wep_key_len;
+ DBG_8723A("%s(): Shared/Auto WEP\n", __func__);
+
+ wep_key_idx = sme->key_idx;
+ wep_key_len = sme->key_len;
+
+ if (wep_key_idx > WEP_KEYS || !wep_key_len ||
+ wep_key_len > WLAN_KEY_LEN_WEP104) {
+ ret = -EINVAL;
+ goto exit;
+ }
+
+ wep_key_len = wep_key_len <= 5 ? 5 : 13;
+
+ memset(&wep_key, 0, sizeof(struct rtw_wep_key));
+
+ wep_key.keylen = wep_key_len;
+
+ if (wep_key_len == 13) {
+ padapter->securitypriv.dot11PrivacyAlgrthm =
+ WLAN_CIPHER_SUITE_WEP104;
+ padapter->securitypriv.dot118021XGrpPrivacy =
+ WLAN_CIPHER_SUITE_WEP104;
+ } else {
+ padapter->securitypriv.dot11PrivacyAlgrthm =
+ WLAN_CIPHER_SUITE_WEP40;
+ padapter->securitypriv.dot118021XGrpPrivacy =
+ WLAN_CIPHER_SUITE_WEP40;
+ }
+
+ memcpy(wep_key.key, (void *)sme->key, wep_key.keylen);
+
+ if (rtw_cfg80211_add_wep(padapter, &wep_key, wep_key_idx) !=
+ _SUCCESS)
+ ret = -EOPNOTSUPP;
+
+ if (ret < 0)
+ goto exit;
+ }
+
+ ret = rtw_cfg80211_set_cipher(psecuritypriv,
+ sme->crypto.cipher_group, false);
+ if (ret < 0)
+ goto exit;
+
+ if (sme->crypto.n_akm_suites) {
+ ret = rtw_cfg80211_set_key_mgt(psecuritypriv,
+ sme->crypto.akm_suites[0]);
+ if (ret < 0)
+ goto exit;
+ }
+
+ if (psecuritypriv->ndisauthtype > 3)
+ psecuritypriv->dot11AuthAlgrthm = dot11AuthAlgrthm_8021X;
+
+ if (rtw_set_auth23a(padapter, psecuritypriv) != _SUCCESS) {
+ ret = -EBUSY;
+ goto exit;
+ }
+
+ /* rtw_set_802_11_encryption_mode(padapter,
+ padapter->securitypriv.ndisencryptstatus); */
+
+ if (rtw_set_ssid(padapter, pnetwork) != _SUCCESS) {
+ ret = -EBUSY;
+ goto exit;
+ }
+
+ DBG_8723A("set ssid:dot11AuthAlgrthm =%d, dot11PrivacyAlgrthm =%d, "
+ "dot118021XGrpPrivacy =%d\n", psecuritypriv->dot11AuthAlgrthm,
+ psecuritypriv->dot11PrivacyAlgrthm,
+ psecuritypriv->dot118021XGrpPrivacy);
+
+exit:
+
+ DBG_8723A("<=%s, ret %d\n", __func__, ret);
+
+ return ret;
+}
+
+static int cfg80211_rtw_disconnect(struct wiphy *wiphy, struct net_device *ndev,
+ u16 reason_code)
+{
+ struct rtw_adapter *padapter = wiphy_to_adapter(wiphy);
+
+ DBG_8723A("%s(%s)\n", __func__, ndev->name);
+
+ rtw_set_roaming(padapter, 0);
+
+ if (check_fwstate(&padapter->mlmepriv, _FW_LINKED)) {
+ rtw_scan_abort23a(padapter);
+ LeaveAllPowerSaveMode23a(padapter);
+ rtw_disassoc_cmd23a(padapter, 500, false);
+
+ DBG_8723A("%s...call rtw_indicate_disconnect23a\n", __func__);
+
+ padapter->mlmepriv.not_indic_disco = true;
+ rtw_indicate_disconnect23a(padapter);
+ padapter->mlmepriv.not_indic_disco = false;
+
+ rtw_free_assoc_resources23a(padapter, 1);
+ }
+
+ return 0;
+}
+
+static int cfg80211_rtw_set_txpower(struct wiphy *wiphy,
+ struct wireless_dev *wdev,
+ enum nl80211_tx_power_setting type, int mbm)
+{
+ DBG_8723A("%s\n", __func__);
+ return 0;
+}
+
+static int cfg80211_rtw_get_txpower(struct wiphy *wiphy,
+ struct wireless_dev *wdev, int *dbm)
+{
+ DBG_8723A("%s\n", __func__);
+ *dbm = (12);
+ return 0;
+}
+
+inline bool rtw_cfg80211_pwr_mgmt(struct rtw_adapter *adapter)
+{
+ struct rtw_wdev_priv *rtw_wdev_priv = wdev_to_priv(adapter->rtw_wdev);
+ return rtw_wdev_priv->power_mgmt;
+}
+
+static int cfg80211_rtw_set_power_mgmt(struct wiphy *wiphy,
+ struct net_device *ndev,
+ bool enabled, int timeout)
+{
+ struct rtw_adapter *padapter = wiphy_to_adapter(wiphy);
+ struct rtw_wdev_priv *rtw_wdev_priv = wdev_to_priv(padapter->rtw_wdev);
+
+ DBG_8723A("%s(%s): enabled:%u, timeout:%d\n",
+ __func__, ndev->name, enabled, timeout);
+
+ rtw_wdev_priv->power_mgmt = enabled;
+
+ if (!enabled)
+ LPS_Leave23a(padapter);
+
+ return 0;
+}
+
+static int cfg80211_rtw_set_pmksa(struct wiphy *wiphy,
+ struct net_device *netdev,
+ struct cfg80211_pmksa *pmksa)
+{
+ u8 index, blInserted = false;
+ struct rtw_adapter *padapter = wiphy_to_adapter(wiphy);
+ struct security_priv *psecuritypriv = &padapter->securitypriv;
+
+ DBG_8723A("%s(%s)\n", __func__, netdev->name);
+
+ if (is_zero_ether_addr(pmksa->bssid))
+ return -EINVAL;
+
+ blInserted = false;
+
+ /* overwrite PMKID */
+ for (index = 0; index < NUM_PMKID_CACHE; index++) {
+ if (ether_addr_equal(psecuritypriv->PMKIDList[index].Bssid,
+ pmksa->bssid)) {
+ /* BSSID is matched, the same AP => rewrite with
+ new PMKID. */
+ DBG_8723A("%s(%s): BSSID exists in the PMKList.\n",
+ __func__, netdev->name);
+
+ memcpy(psecuritypriv->PMKIDList[index].PMKID,
+ pmksa->pmkid, WLAN_PMKID_LEN);
+ psecuritypriv->PMKIDList[index].bUsed = true;
+ psecuritypriv->PMKIDIndex = index + 1;
+ blInserted = true;
+ break;
+ }
+ }
+
+ if (!blInserted) {
+ /* Find a new entry */
+ DBG_8723A("%s(%s): Use new entry index = %d for this PMKID\n",
+ __func__, netdev->name, psecuritypriv->PMKIDIndex);
+
+ ether_addr_copy(
+ psecuritypriv->PMKIDList[psecuritypriv->PMKIDIndex].
+ Bssid, pmksa->bssid);
+ memcpy(psecuritypriv->PMKIDList[psecuritypriv->PMKIDIndex].
+ PMKID, pmksa->pmkid, WLAN_PMKID_LEN);
+
+ psecuritypriv->PMKIDList[psecuritypriv->PMKIDIndex].bUsed =
+ true;
+ psecuritypriv->PMKIDIndex++;
+ if (psecuritypriv->PMKIDIndex == 16) {
+ psecuritypriv->PMKIDIndex = 0;
+ }
+ }
+
+ return 0;
+}
+
+static int cfg80211_rtw_del_pmksa(struct wiphy *wiphy,
+ struct net_device *netdev,
+ struct cfg80211_pmksa *pmksa)
+{
+ u8 index, bMatched = false;
+ struct rtw_adapter *padapter = wiphy_to_adapter(wiphy);
+ struct security_priv *psecuritypriv = &padapter->securitypriv;
+
+ DBG_8723A("%s(%s)\n", __func__, netdev->name);
+
+ for (index = 0; index < NUM_PMKID_CACHE; index++) {
+ if (ether_addr_equal(psecuritypriv->PMKIDList[index].Bssid,
+ pmksa->bssid)) {
+ /* BSSID is matched, the same AP => Remove this PMKID
+ information and reset it. */
+ eth_zero_addr(psecuritypriv->PMKIDList[index].Bssid);
+ memset(psecuritypriv->PMKIDList[index].PMKID, 0x00,
+ WLAN_PMKID_LEN);
+ psecuritypriv->PMKIDList[index].bUsed = false;
+ bMatched = true;
+ break;
+ }
+ }
+
+ if (false == bMatched) {
+ DBG_8723A("%s(%s): do not have matched BSSID\n", __func__,
+ netdev->name);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int cfg80211_rtw_flush_pmksa(struct wiphy *wiphy,
+ struct net_device *netdev)
+{
+ struct rtw_adapter *padapter = wiphy_to_adapter(wiphy);
+ struct security_priv *psecuritypriv = &padapter->securitypriv;
+
+ DBG_8723A("%s(%s)\n", __func__, netdev->name);
+
+ memset(&psecuritypriv->PMKIDList[0], 0x00,
+ sizeof(struct rt_pmkid_list) * NUM_PMKID_CACHE);
+ psecuritypriv->PMKIDIndex = 0;
+
+ return 0;
+}
+
+#ifdef CONFIG_8723AU_AP_MODE
+void rtw_cfg80211_indicate_sta_assoc(struct rtw_adapter *padapter,
+ u8 *pmgmt_frame, uint frame_len)
+{
+ s32 freq;
+ int channel;
+ struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv;
+ struct net_device *ndev = padapter->pnetdev;
+
+ DBG_8723A("%s(padapter =%p,%s)\n", __func__, padapter, ndev->name);
+
+#if defined(RTW_USE_CFG80211_STA_EVENT)
+ {
+ struct station_info sinfo;
+ u8 ie_offset;
+
+ if (ieee80211_is_assoc_req(hdr->frame_control))
+ ie_offset = offsetof(struct ieee80211_mgmt,
+ u.assoc_req.variable);
+ else /* WIFI_REASSOCREQ */
+ ie_offset = offsetof(struct ieee80211_mgmt,
+ u.reassoc_req.variable);
+
+ sinfo.filled = 0;
+ sinfo.assoc_req_ies = pmgmt_frame + ie_offset;
+ sinfo.assoc_req_ies_len = frame_len - ie_offset;
+ cfg80211_new_sta(ndev, hdr->addr2, &sinfo, GFP_ATOMIC);
+ }
+#else /* defined(RTW_USE_CFG80211_STA_EVENT) */
+ channel = pmlmeext->cur_channel;
+ if (channel <= RTW_CH_MAX_2G_CHANNEL)
+ freq = ieee80211_channel_to_frequency(channel,
+ IEEE80211_BAND_2GHZ);
+ else
+ freq = ieee80211_channel_to_frequency(channel,
+ IEEE80211_BAND_5GHZ);
+
+ cfg80211_rx_mgmt(padapter->rtw_wdev, freq, 0, pmgmt_frame, frame_len,
+ 0);
+#endif /* defined(RTW_USE_CFG80211_STA_EVENT) */
+}
+
+void rtw_cfg80211_indicate_sta_disassoc(struct rtw_adapter *padapter,
+ unsigned char *da,
+ unsigned short reason)
+{
+ s32 freq;
+ int channel;
+ uint frame_len;
+ struct ieee80211_mgmt mgmt;
+ struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv;
+ struct mlme_ext_info *pmlmeinfo = &pmlmeext->mlmext_info;
+ struct net_device *ndev = padapter->pnetdev;
+
+ DBG_8723A("%s(padapter =%p,%s)\n", __func__, padapter, ndev->name);
+
+ memset(&mgmt, 0, sizeof(struct ieee80211_mgmt));
+
+#if defined(RTW_USE_CFG80211_STA_EVENT)
+ cfg80211_del_sta(ndev, da, GFP_ATOMIC);
+#else /* defined(RTW_USE_CFG80211_STA_EVENT) */
+ channel = pmlmeext->cur_channel;
+ if (channel <= RTW_CH_MAX_2G_CHANNEL)
+ freq = ieee80211_channel_to_frequency(channel,
+ IEEE80211_BAND_2GHZ);
+ else
+ freq = ieee80211_channel_to_frequency(channel,
+ IEEE80211_BAND_5GHZ);
+
+ mgmt.frame_control =
+ cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_DEAUTH);
+
+ ether_addr_copy(mgmt.da, myid(&padapter->eeprompriv));
+ ether_addr_copy(mgmt.sa, da);
+ ether_addr_copy(mgmt.bssid, get_my_bssid23a(&pmlmeinfo->network));
+
+ mgmt.seq_ctrl = cpu_to_le16(IEEE80211_SN_TO_SEQ(pmlmeext->mgnt_seq));
+ pmlmeext->mgnt_seq++;
+
+ mgmt.u.disassoc.reason_code = cpu_to_le16(reason);
+
+ frame_len = sizeof(struct ieee80211_hdr_3addr) + 2;
+
+ cfg80211_rx_mgmt(padapter->rtw_wdev, freq, 0, (u8 *)&mgmt, frame_len,
+ 0);
+#endif /* defined(RTW_USE_CFG80211_STA_EVENT) */
+}
+
+static int rtw_cfg80211_monitor_if_open(struct net_device *ndev)
+{
+ DBG_8723A("%s\n", __func__);
+
+ return 0;
+}
+
+static int rtw_cfg80211_monitor_if_close(struct net_device *ndev)
+{
+ DBG_8723A("%s\n", __func__);
+
+ return 0;
+}
+
+static int rtw_cfg80211_monitor_if_xmit_entry(struct sk_buff *skb,
+ struct net_device *ndev)
+{
+ int ret = 0;
+ int rtap_len;
+ int qos_len = 0;
+ int dot11_hdr_len = 24;
+ int snap_len = 6;
+ unsigned char *pdata;
+ unsigned char src_mac_addr[6];
+ unsigned char dst_mac_addr[6];
+ struct ieee80211_hdr *dot11_hdr;
+ struct ieee80211_radiotap_header *rtap_hdr;
+ struct rtw_adapter *padapter = netdev_priv(ndev);
+
+ DBG_8723A("%s(%s)\n", __func__, ndev->name);
+
+ if (unlikely(skb->len < sizeof(struct ieee80211_radiotap_header)))
+ goto fail;
+
+ rtap_hdr = (struct ieee80211_radiotap_header *)skb->data;
+ if (unlikely(rtap_hdr->it_version))
+ goto fail;
+
+ rtap_len = ieee80211_get_radiotap_len(skb->data);
+ if (unlikely(skb->len < rtap_len))
+ goto fail;
+
+ if (rtap_len != 14) {
+ DBG_8723A("radiotap len (should be 14): %d\n", rtap_len);
+ goto fail;
+ }
+
+ /* Skip the ratio tap header */
+ skb_pull(skb, rtap_len);
+
+ dot11_hdr = (struct ieee80211_hdr *)skb->data;
+ /* Check if the QoS bit is set */
+ if (ieee80211_is_data(dot11_hdr->frame_control)) {
+ /* Check if this ia a Wireless Distribution System (WDS) frame
+ * which has 4 MAC addresses
+ */
+ if (ieee80211_is_data_qos(dot11_hdr->frame_control))
+ qos_len = IEEE80211_QOS_CTL_LEN;
+ if (ieee80211_has_a4(dot11_hdr->frame_control))
+ dot11_hdr_len += 6;
+
+ memcpy(dst_mac_addr, dot11_hdr->addr1, sizeof(dst_mac_addr));
+ memcpy(src_mac_addr, dot11_hdr->addr2, sizeof(src_mac_addr));
+
+ /*
+ * Skip the 802.11 header, QoS (if any) and SNAP,
+ * but leave spaces for two MAC addresses
+ */
+ skb_pull(skb, dot11_hdr_len + qos_len + snap_len -
+ ETH_ALEN * 2);
+ pdata = (unsigned char *)skb->data;
+ ether_addr_copy(pdata, dst_mac_addr);
+ ether_addr_copy(pdata + ETH_ALEN, src_mac_addr);
+
+ DBG_8723A("should be eapol packet\n");
+
+ /* Use the real net device to transmit the packet */
+ ret = rtw_xmit23a_entry23a(skb, padapter->pnetdev);
+
+ return ret;
+
+ } else if (ieee80211_is_action(dot11_hdr->frame_control)) {
+ struct ieee80211_mgmt *mgmt;
+ /* only for action frames */
+ struct xmit_frame *pmgntframe;
+ struct pkt_attrib *pattrib;
+ unsigned char *pframe;
+ /* u8 category, action, OUI_Subtype, dialogToken = 0; */
+ /* unsigned char *frame_body; */
+ struct xmit_priv *pxmitpriv = &padapter->xmitpriv;
+ struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv;
+ u32 len = skb->len;
+ u8 category, action;
+
+ mgmt = (struct ieee80211_mgmt *)dot11_hdr;
+
+ DBG_8723A("RTW_Tx:da=%pM via %s(%s)\n",
+ mgmt->da, __func__, ndev->name);
+ category = mgmt->u.action.category;
+ action = mgmt->u.action.u.wme_action.action_code;
+ DBG_8723A("RTW_Tx:category(%u), action(%u)\n",
+ category, action);
+
+ /* starting alloc mgmt frame to dump it */
+ pmgntframe = alloc_mgtxmitframe23a(pxmitpriv);
+ if (pmgntframe == NULL)
+ goto fail;
+
+ /* update attribute */
+ pattrib = &pmgntframe->attrib;
+ update_mgntframe_attrib23a(padapter, pattrib);
+ pattrib->retry_ctrl = false;
+
+ memset(pmgntframe->buf_addr, 0, WLANHDR_OFFSET + TXDESC_OFFSET);
+
+ pframe = (u8 *)(pmgntframe->buf_addr) + TXDESC_OFFSET;
+
+ memcpy(pframe, skb->data, len);
+ pattrib->pktlen = len;
+
+ /* update seq number */
+ pmlmeext->mgnt_seq = le16_to_cpu(dot11_hdr->seq_ctrl) >> 4;
+ pattrib->seqnum = pmlmeext->mgnt_seq;
+ pmlmeext->mgnt_seq++;
+
+ pattrib->last_txcmdsz = pattrib->pktlen;
+
+ dump_mgntframe23a(padapter, pmgntframe);
+ }
+
+fail:
+
+ dev_kfree_skb(skb);
+
+ return 0;
+}
+
+static int
+rtw_cfg80211_monitor_if_set_mac_address(struct net_device *ndev, void *addr)
+{
+ DBG_8723A("%s\n", __func__);
+
+ return 0;
+}
+
+static const struct net_device_ops rtw_cfg80211_monitor_if_ops = {
+ .ndo_open = rtw_cfg80211_monitor_if_open,
+ .ndo_stop = rtw_cfg80211_monitor_if_close,
+ .ndo_start_xmit = rtw_cfg80211_monitor_if_xmit_entry,
+ .ndo_set_mac_address = rtw_cfg80211_monitor_if_set_mac_address,
+};
+
+static int rtw_cfg80211_add_monitor_if(struct rtw_adapter *padapter, char *name,
+ unsigned char name_assign_type,
+ struct net_device **ndev)
+{
+ int ret = 0;
+ struct net_device *mon_ndev = NULL;
+ struct wireless_dev *mon_wdev = NULL;
+ struct rtw_wdev_priv *pwdev_priv = wdev_to_priv(padapter->rtw_wdev);
+
+ if (!name) {
+ DBG_8723A("%s(%s): without specific name\n",
+ __func__, padapter->pnetdev->name);
+ ret = -EINVAL;
+ goto out;
+ }
+
+ if (pwdev_priv->pmon_ndev) {
+ DBG_8723A("%s(%s): monitor interface exist: %s\n", __func__,
+ padapter->pnetdev->name, pwdev_priv->pmon_ndev->name);
+ ret = -EBUSY;
+ goto out;
+ }
+
+ mon_ndev = alloc_etherdev(sizeof(struct rtw_adapter));
+ if (!mon_ndev) {
+ DBG_8723A("%s(%s): allocate ndev fail\n", __func__,
+ padapter->pnetdev->name);
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ mon_ndev->type = ARPHRD_IEEE80211_RADIOTAP;
+ strncpy(mon_ndev->name, name, IFNAMSIZ);
+ mon_ndev->name[IFNAMSIZ - 1] = 0;
+ mon_ndev->name_assign_type = name_assign_type;
+ mon_ndev->destructor = rtw_ndev_destructor;
+
+ mon_ndev->netdev_ops = &rtw_cfg80211_monitor_if_ops;
+
+ /* wdev */
+ mon_wdev = kzalloc(sizeof(struct wireless_dev), GFP_KERNEL);
+ if (!mon_wdev) {
+ DBG_8723A("%s(%s): allocate mon_wdev fail\n", __func__,
+ padapter->pnetdev->name);
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ mon_wdev->wiphy = padapter->rtw_wdev->wiphy;
+ mon_wdev->netdev = mon_ndev;
+ mon_wdev->iftype = NL80211_IFTYPE_MONITOR;
+ mon_ndev->ieee80211_ptr = mon_wdev;
+
+ ret = register_netdevice(mon_ndev);
+ if (ret) {
+ goto out;
+ }
+
+ *ndev = pwdev_priv->pmon_ndev = mon_ndev;
+ memcpy(pwdev_priv->ifname_mon, name, IFNAMSIZ + 1);
+
+out:
+ if (ret) {
+ kfree(mon_wdev);
+ mon_wdev = NULL;
+ }
+
+ if (ret && mon_ndev) {
+ free_netdev(mon_ndev);
+ *ndev = mon_ndev = NULL;
+ }
+
+ return ret;
+}
+
+static struct wireless_dev *
+cfg80211_rtw_add_virtual_intf(struct wiphy *wiphy, const char *name,
+ unsigned char name_assign_type,
+ enum nl80211_iftype type, u32 *flags,
+ struct vif_params *params)
+{
+ int ret = 0;
+ struct net_device *ndev = NULL;
+ struct rtw_adapter *padapter = wiphy_to_adapter(wiphy);
+
+ DBG_8723A("%s(%s): wiphy:%s, name:%s, type:%d\n", __func__,
+ padapter->pnetdev->name, wiphy_name(wiphy), name, type);
+
+ switch (type) {
+ case NL80211_IFTYPE_ADHOC:
+ case NL80211_IFTYPE_AP_VLAN:
+ case NL80211_IFTYPE_WDS:
+ case NL80211_IFTYPE_MESH_POINT:
+ ret = -ENODEV;
+ break;
+ case NL80211_IFTYPE_MONITOR:
+ ret =
+ rtw_cfg80211_add_monitor_if(padapter, (char *)name,
+ name_assign_type, &ndev);
+ break;
+
+ case NL80211_IFTYPE_P2P_CLIENT:
+ case NL80211_IFTYPE_STATION:
+ ret = -ENODEV;
+ break;
+
+ case NL80211_IFTYPE_P2P_GO:
+ case NL80211_IFTYPE_AP:
+ ret = -ENODEV;
+ break;
+ default:
+ ret = -ENODEV;
+ DBG_8723A("Unsupported interface type\n");
+ break;
+ }
+
+ DBG_8723A("%s(%s): ndev:%p, ret:%d\n", __func__,
+ padapter->pnetdev->name,
+ ndev, ret);
+
+ return ndev ? ndev->ieee80211_ptr : ERR_PTR(ret);
+}
+
+static int cfg80211_rtw_del_virtual_intf(struct wiphy *wiphy,
+ struct wireless_dev *wdev)
+{
+ struct rtw_wdev_priv *pwdev_priv =
+ (struct rtw_wdev_priv *)wiphy_priv(wiphy);
+ struct net_device *ndev;
+ ndev = wdev ? wdev->netdev : NULL;
+
+ if (!ndev)
+ goto exit;
+
+ unregister_netdevice(ndev);
+
+ if (ndev == pwdev_priv->pmon_ndev) {
+ pwdev_priv->pmon_ndev = NULL;
+ pwdev_priv->ifname_mon[0] = '\0';
+ DBG_8723A("%s(%s): remove monitor interface\n",
+ __func__, ndev->name);
+ }
+
+exit:
+ return 0;
+}
+
+static int rtw_add_beacon(struct rtw_adapter *adapter, const u8 *head,
+ size_t head_len, const u8 *tail, size_t tail_len)
+{
+ int ret = 0;
+ u8 *pbuf;
+ uint len, ielen, wps_ielen = 0;
+ struct mlme_priv *pmlmepriv = &adapter->mlmepriv;
+ struct wlan_bssid_ex *bss = &pmlmepriv->cur_network.network;
+ const struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)head;
+ struct ieee80211_mgmt *tmpmgmt;
+ /* struct sta_priv *pstapriv = &padapter->stapriv; */
+
+ DBG_8723A("%s beacon_head_len =%zu, beacon_tail_len =%zu\n",
+ __func__, head_len, tail_len);
+
+ if (check_fwstate(pmlmepriv, WIFI_AP_STATE) != true)
+ return -EINVAL;
+
+ if (head_len < offsetof(struct ieee80211_mgmt, u.beacon.variable))
+ return -EINVAL;
+
+ pbuf = kzalloc(head_len + tail_len, GFP_KERNEL);
+ if (!pbuf)
+ return -ENOMEM;
+ tmpmgmt = (struct ieee80211_mgmt *)pbuf;
+
+ bss->beacon_interval = get_unaligned_le16(&mgmt->u.beacon.beacon_int);
+ bss->capability = get_unaligned_le16(&mgmt->u.beacon.capab_info);
+ bss->tsf = get_unaligned_le64(&mgmt->u.beacon.timestamp);
+
+ /* 24 = beacon header len. */
+ memcpy(pbuf, (void *)head, head_len);
+ memcpy(pbuf + head_len, (void *)tail, tail_len);
+
+ len = head_len + tail_len;
+ ielen = len - offsetof(struct ieee80211_mgmt, u.beacon.variable);
+ /* check wps ie if inclued */
+ if (cfg80211_find_vendor_ie(WLAN_OUI_MICROSOFT,
+ WLAN_OUI_TYPE_MICROSOFT_WPS,
+ tmpmgmt->u.beacon.variable, ielen))
+ DBG_8723A("add bcn, wps_ielen =%d\n", wps_ielen);
+
+ /* pbss_network->IEs will not include p2p_ie, wfd ie */
+ rtw_ies_remove_ie23a(tmpmgmt->u.beacon.variable, &ielen, 0,
+ WLAN_EID_VENDOR_SPECIFIC, P2P_OUI23A, 4);
+ rtw_ies_remove_ie23a(tmpmgmt->u.beacon.variable, &ielen, 0,
+ WLAN_EID_VENDOR_SPECIFIC, WFD_OUI23A, 4);
+
+ len = ielen + offsetof(struct ieee80211_mgmt, u.beacon.variable);
+ if (rtw_check_beacon_data23a(adapter, tmpmgmt, len) == _SUCCESS) {
+ ret = 0;
+ } else {
+ ret = -EINVAL;
+ }
+
+ kfree(pbuf);
+
+ return ret;
+}
+
+static int cfg80211_rtw_start_ap(struct wiphy *wiphy, struct net_device *ndev,
+ struct cfg80211_ap_settings *settings)
+{
+ int ret = 0;
+ struct rtw_adapter *adapter = wiphy_to_adapter(wiphy);
+
+ DBG_8723A("%s(%s): hidden_ssid:%d, auth_type:%d\n",
+ __func__, ndev->name, settings->hidden_ssid,
+ settings->auth_type);
+
+ ret = rtw_add_beacon(adapter, settings->beacon.head,
+ settings->beacon.head_len, settings->beacon.tail,
+ settings->beacon.tail_len);
+
+ adapter->mlmeextpriv.mlmext_info.hidden_ssid_mode =
+ settings->hidden_ssid;
+
+ if (settings->ssid && settings->ssid_len) {
+ struct wlan_bssid_ex *pbss_network =
+ &adapter->mlmepriv.cur_network.network;
+ struct wlan_bssid_ex *pbss_network_ext =
+ &adapter->mlmeextpriv.mlmext_info.network;
+
+ memcpy(pbss_network->Ssid.ssid, (void *)settings->ssid,
+ settings->ssid_len);
+ pbss_network->Ssid.ssid_len = settings->ssid_len;
+ memcpy(pbss_network_ext->Ssid.ssid, (void *)settings->ssid,
+ settings->ssid_len);
+ pbss_network_ext->Ssid.ssid_len = settings->ssid_len;
+ }
+
+ return ret;
+}
+
+static int cfg80211_rtw_change_beacon(struct wiphy *wiphy,
+ struct net_device *ndev,
+ struct cfg80211_beacon_data *info)
+{
+ int ret = 0;
+ struct rtw_adapter *adapter = wiphy_to_adapter(wiphy);
+
+ DBG_8723A("%s(%s)\n", __func__, ndev->name);
+
+ ret = rtw_add_beacon(adapter, info->head, info->head_len, info->tail,
+ info->tail_len);
+
+ return ret;
+}
+
+static int cfg80211_rtw_stop_ap(struct wiphy *wiphy, struct net_device *ndev)
+{
+ DBG_8723A("%s(%s)\n", __func__, ndev->name);
+ return 0;
+}
+
+static int cfg80211_rtw_add_station(struct wiphy *wiphy,
+ struct net_device *ndev, const u8 *mac,
+ struct station_parameters *params)
+{
+ DBG_8723A("%s(%s)\n", __func__, ndev->name);
+
+ return 0;
+}
+
+static int cfg80211_rtw_del_station(struct wiphy *wiphy,
+ struct net_device *ndev,
+ struct station_del_parameters *params)
+{
+ const u8 *mac = params->mac;
+ int ret = 0;
+ struct list_head *phead, *plist, *ptmp;
+ u8 updated = 0;
+ struct sta_info *psta;
+ struct rtw_adapter *padapter = netdev_priv(ndev);
+ struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
+ struct sta_priv *pstapriv = &padapter->stapriv;
+
+ DBG_8723A("+%s(%s)\n", __func__, ndev->name);
+
+ if (check_fwstate(pmlmepriv, (_FW_LINKED | WIFI_AP_STATE)) != true) {
+ DBG_8723A("%s, fw_state != FW_LINKED|WIFI_AP_STATE\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ if (!mac) {
+ DBG_8723A("flush all sta, and cam_entry\n");
+
+ flush_all_cam_entry23a(padapter); /* clear CAM */
+
+ ret = rtw_sta_flush23a(padapter);
+
+ return ret;
+ }
+
+ DBG_8723A("free sta macaddr=%pM\n", mac);
+
+ if (is_broadcast_ether_addr(mac))
+ return -EINVAL;
+
+ spin_lock_bh(&pstapriv->asoc_list_lock);
+
+ phead = &pstapriv->asoc_list;
+
+ /* check asoc_queue */
+ list_for_each_safe(plist, ptmp, phead) {
+ psta = container_of(plist, struct sta_info, asoc_list);
+
+ if (ether_addr_equal(mac, psta->hwaddr)) {
+ if (psta->dot8021xalg == 1 &&
+ psta->bpairwise_key_installed == false) {
+ DBG_8723A("%s, sta's dot8021xalg = 1 and "
+ "key_installed = false\n", __func__);
+ } else {
+ DBG_8723A("free psta =%p, aid =%d\n", psta,
+ psta->aid);
+
+ list_del_init(&psta->asoc_list);
+ pstapriv->asoc_list_cnt--;
+
+ /* spin_unlock_bh(&pstapriv->asoc_list_lock); */
+ updated =
+ ap_free_sta23a(padapter, psta, true,
+ WLAN_REASON_DEAUTH_LEAVING);
+ /* spin_lock_bh(&pstapriv->asoc_list_lock); */
+
+ psta = NULL;
+
+ break;
+ }
+ }
+ }
+
+ spin_unlock_bh(&pstapriv->asoc_list_lock);
+
+ associated_clients_update23a(padapter, updated);
+
+ DBG_8723A("-%s(%s)\n", __func__, ndev->name);
+
+ return ret;
+}
+
+static int cfg80211_rtw_change_station(struct wiphy *wiphy,
+ struct net_device *ndev, const u8 *mac,
+ struct station_parameters *params)
+{
+ DBG_8723A("%s(%s)\n", __func__, ndev->name);
+ return 0;
+}
+
+static int cfg80211_rtw_dump_station(struct wiphy *wiphy,
+ struct net_device *ndev, int idx, u8 *mac,
+ struct station_info *sinfo)
+{
+ DBG_8723A("%s(%s)\n", __func__, ndev->name);
+
+ /* TODO: dump scanned queue */
+
+ return -ENOENT;
+}
+
+static int cfg80211_rtw_change_bss(struct wiphy *wiphy, struct net_device *ndev,
+ struct bss_parameters *params)
+{
+ DBG_8723A("%s(%s)\n", __func__, ndev->name);
+ return 0;
+}
+#endif /* CONFIG_8723AU_AP_MODE */
+
+static int _cfg80211_rtw_mgmt_tx(struct rtw_adapter *padapter, u8 tx_ch,
+ const u8 *buf, size_t len)
+{
+ struct xmit_frame *pmgntframe;
+ struct pkt_attrib *pattrib;
+ unsigned char *pframe;
+ int ret = _FAIL;
+ struct ieee80211_hdr *pwlanhdr;
+ struct xmit_priv *pxmitpriv = &padapter->xmitpriv;
+ struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv;
+
+ if (_FAIL == rtw_pwr_wakeup(padapter)) {
+ ret = -EFAULT;
+ goto exit;
+ }
+
+ rtw_set_scan_deny(padapter, 1000);
+
+ rtw_scan_abort23a(padapter);
+
+ if (tx_ch != rtw_get_oper_ch23a(padapter)) {
+ if (!check_fwstate(&padapter->mlmepriv, _FW_LINKED))
+ pmlmeext->cur_channel = tx_ch;
+ set_channel_bwmode23a(padapter, tx_ch,
+ HAL_PRIME_CHNL_OFFSET_DONT_CARE,
+ HT_CHANNEL_WIDTH_20);
+ }
+
+ /* starting alloc mgmt frame to dump it */
+ pmgntframe = alloc_mgtxmitframe23a(pxmitpriv);
+ if (!pmgntframe) {
+ /* ret = -ENOMEM; */
+ ret = _FAIL;
+ goto exit;
+ }
+
+ /* update attribute */
+ pattrib = &pmgntframe->attrib;
+ update_mgntframe_attrib23a(padapter, pattrib);
+ pattrib->retry_ctrl = false;
+
+ memset(pmgntframe->buf_addr, 0, WLANHDR_OFFSET + TXDESC_OFFSET);
+
+ pframe = (u8 *) (pmgntframe->buf_addr) + TXDESC_OFFSET;
+
+ memcpy(pframe, (void *)buf, len);
+ pattrib->pktlen = len;
+
+ pwlanhdr = (struct ieee80211_hdr *)pframe;
+ /* update seq number */
+ pmlmeext->mgnt_seq = le16_to_cpu(pwlanhdr->seq_ctrl) >> 4;
+ pattrib->seqnum = pmlmeext->mgnt_seq;
+ pmlmeext->mgnt_seq++;
+
+ pattrib->last_txcmdsz = pattrib->pktlen;
+
+ ret = dump_mgntframe23a_and_wait_ack23a(padapter, pmgntframe);
+
+ if (ret != _SUCCESS)
+ DBG_8723A("%s, ack == false\n", __func__);
+ else
+ DBG_8723A("%s, ack == true\n", __func__);
+
+exit:
+
+ DBG_8723A("%s, ret =%d\n", __func__, ret);
+
+ return ret;
+}
+
+static int cfg80211_rtw_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
+ struct cfg80211_mgmt_tx_params *params,
+ u64 *cookie)
+{
+ struct rtw_adapter *padapter =
+ (struct rtw_adapter *)wiphy_to_adapter(wiphy);
+ int ret = 0;
+ int tx_ret;
+ u32 dump_limit = RTW_MAX_MGMT_TX_CNT;
+ u32 dump_cnt = 0;
+ bool ack = true;
+ u8 category, action;
+ unsigned long start = jiffies;
+ size_t len = params->len;
+ struct ieee80211_channel *chan = params->chan;
+ const u8 *buf = params->buf;
+ struct ieee80211_mgmt *hdr = (struct ieee80211_mgmt *)buf;
+ u8 tx_ch = (u8) ieee80211_frequency_to_channel(chan->center_freq);
+
+ if (!ieee80211_is_action(hdr->frame_control))
+ return -EINVAL;
+
+ /* cookie generation */
+ *cookie = (unsigned long)buf;
+
+ DBG_8723A("%s(%s): len =%zu, ch =%d\n", __func__,
+ padapter->pnetdev->name, len, tx_ch);
+
+ /* indicate ack before issue frame to avoid racing with rsp frame */
+ cfg80211_mgmt_tx_status(padapter->rtw_wdev, *cookie, buf, len, ack,
+ GFP_KERNEL);
+
+ DBG_8723A("RTW_Tx:tx_ch =%d, da =%pM\n", tx_ch, hdr->da);
+ category = hdr->u.action.category;
+ action = hdr->u.action.u.wme_action.action_code;
+ DBG_8723A("RTW_Tx:category(%u), action(%u)\n", category, action);
+
+ do {
+ dump_cnt++;
+ tx_ret = _cfg80211_rtw_mgmt_tx(padapter, tx_ch, buf, len);
+ } while (dump_cnt < dump_limit && tx_ret != _SUCCESS);
+
+ if (tx_ret != _SUCCESS || dump_cnt > 1) {
+ DBG_8723A("%s(%s): %s (%d/%d) in %d ms\n",
+ __func__, padapter->pnetdev->name,
+ tx_ret == _SUCCESS ? "OK" : "FAIL", dump_cnt,
+ dump_limit, jiffies_to_msecs(jiffies - start));
+ }
+
+ return ret;
+}
+
+static void cfg80211_rtw_mgmt_frame_register(struct wiphy *wiphy,
+ struct wireless_dev *wdev,
+ u16 frame_type, bool reg)
+{
+ if (frame_type != (IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_PROBE_REQ))
+ return;
+
+ return;
+}
+
+static struct cfg80211_ops rtw_cfg80211_ops = {
+ .change_virtual_intf = cfg80211_rtw_change_iface,
+ .add_key = cfg80211_rtw_add_key,
+ .get_key = cfg80211_rtw_get_key,
+ .del_key = cfg80211_rtw_del_key,
+ .set_default_key = cfg80211_rtw_set_default_key,
+ .get_station = cfg80211_rtw_get_station,
+ .scan = cfg80211_rtw_scan,
+ .set_wiphy_params = cfg80211_rtw_set_wiphy_params,
+ .connect = cfg80211_rtw_connect,
+ .disconnect = cfg80211_rtw_disconnect,
+ .join_ibss = cfg80211_rtw_join_ibss,
+ .leave_ibss = cfg80211_rtw_leave_ibss,
+ .set_tx_power = cfg80211_rtw_set_txpower,
+ .get_tx_power = cfg80211_rtw_get_txpower,
+ .set_power_mgmt = cfg80211_rtw_set_power_mgmt,
+ .set_pmksa = cfg80211_rtw_set_pmksa,
+ .del_pmksa = cfg80211_rtw_del_pmksa,
+ .flush_pmksa = cfg80211_rtw_flush_pmksa,
+
+#ifdef CONFIG_8723AU_AP_MODE
+ .add_virtual_intf = cfg80211_rtw_add_virtual_intf,
+ .del_virtual_intf = cfg80211_rtw_del_virtual_intf,
+
+ .start_ap = cfg80211_rtw_start_ap,
+ .change_beacon = cfg80211_rtw_change_beacon,
+ .stop_ap = cfg80211_rtw_stop_ap,
+
+ .add_station = cfg80211_rtw_add_station,
+ .del_station = cfg80211_rtw_del_station,
+ .change_station = cfg80211_rtw_change_station,
+ .dump_station = cfg80211_rtw_dump_station,
+ .change_bss = cfg80211_rtw_change_bss,
+#endif /* CONFIG_8723AU_AP_MODE */
+
+ .mgmt_tx = cfg80211_rtw_mgmt_tx,
+ .mgmt_frame_register = cfg80211_rtw_mgmt_frame_register,
+};
+
+static void rtw_cfg80211_init_ht_capab(struct ieee80211_sta_ht_cap *ht_cap,
+ enum ieee80211_band band, u8 rf_type)
+{
+
+#define MAX_BIT_RATE_40MHZ_MCS15 300 /* Mbps */
+#define MAX_BIT_RATE_40MHZ_MCS7 150 /* Mbps */
+
+ ht_cap->ht_supported = true;
+
+ ht_cap->cap = IEEE80211_HT_CAP_SUP_WIDTH_20_40 |
+ IEEE80211_HT_CAP_SGI_40 | IEEE80211_HT_CAP_SGI_20 |
+ IEEE80211_HT_CAP_DSSSCCK40 | IEEE80211_HT_CAP_MAX_AMSDU;
+
+ /*
+ *Maximum length of AMPDU that the STA can receive.
+ *Length = 2 ^ (13 + max_ampdu_length_exp) - 1 (octets)
+ */
+ ht_cap->ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K;
+
+ /*Minimum MPDU start spacing , */
+ ht_cap->ampdu_density = IEEE80211_HT_MPDU_DENSITY_16;
+
+ ht_cap->mcs.tx_params = IEEE80211_HT_MCS_TX_DEFINED;
+
+ /*
+ *hw->wiphy->bands[IEEE80211_BAND_2GHZ]
+ *base on ant_num
+ *rx_mask: RX mask
+ *if rx_ant = 1 rx_mask[0]= 0xff;==>MCS0-MCS7
+ *if rx_ant = 2 rx_mask[1]= 0xff;==>MCS8-MCS15
+ *if rx_ant >= 3 rx_mask[2]= 0xff;
+ *if BW_40 rx_mask[4]= 0x01;
+ *highest supported RX rate
+ */
+ if (rf_type == RF_1T1R) {
+ ht_cap->mcs.rx_mask[0] = 0xFF;
+ ht_cap->mcs.rx_mask[1] = 0x00;
+ ht_cap->mcs.rx_mask[4] = 0x01;
+
+ ht_cap->mcs.rx_highest = cpu_to_le16(MAX_BIT_RATE_40MHZ_MCS7);
+ } else if ((rf_type == RF_1T2R) || (rf_type == RF_2T2R)) {
+ ht_cap->mcs.rx_mask[0] = 0xFF;
+ ht_cap->mcs.rx_mask[1] = 0xFF;
+ ht_cap->mcs.rx_mask[4] = 0x01;
+
+ ht_cap->mcs.rx_highest = cpu_to_le16(MAX_BIT_RATE_40MHZ_MCS15);
+ } else {
+ DBG_8723A("%s, error rf_type =%d\n", __func__, rf_type);
+ }
+
+}
+
+void rtw_cfg80211_init_wiphy(struct rtw_adapter *padapter)
+{
+ u8 rf_type;
+ struct ieee80211_supported_band *bands;
+ struct wireless_dev *pwdev = padapter->rtw_wdev;
+ struct wiphy *wiphy = pwdev->wiphy;
+
+ rf_type = rtl8723a_get_rf_type(padapter);
+
+ DBG_8723A("%s:rf_type =%d\n", __func__, rf_type);
+
+ /* if (padapter->registrypriv.wireless_mode & WIRELESS_11G) */
+ {
+ bands = wiphy->bands[IEEE80211_BAND_2GHZ];
+ if (bands)
+ rtw_cfg80211_init_ht_capab(&bands->ht_cap,
+ IEEE80211_BAND_2GHZ,
+ rf_type);
+ }
+
+ /* if (padapter->registrypriv.wireless_mode & WIRELESS_11A) */
+ {
+ bands = wiphy->bands[IEEE80211_BAND_5GHZ];
+ if (bands)
+ rtw_cfg80211_init_ht_capab(&bands->ht_cap,
+ IEEE80211_BAND_5GHZ,
+ rf_type);
+ }
+}
+
+static void rtw_cfg80211_preinit_wiphy(struct rtw_adapter *padapter,
+ struct wiphy *wiphy)
+{
+ wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM;
+
+ wiphy->max_scan_ssids = RTW_SSID_SCAN_AMOUNT;
+ wiphy->max_scan_ie_len = IEEE80211_MAX_DATA_LEN;
+ wiphy->max_num_pmkids = RTW_MAX_NUM_PMKIDS;
+
+ wiphy->max_remain_on_channel_duration =
+ RTW_MAX_REMAIN_ON_CHANNEL_DURATION;
+
+ wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) |
+ BIT(NL80211_IFTYPE_ADHOC) |
+#ifdef CONFIG_8723AU_AP_MODE
+ BIT(NL80211_IFTYPE_AP) | BIT(NL80211_IFTYPE_MONITOR) |
+#endif
+ 0;
+
+#ifdef CONFIG_8723AU_AP_MODE
+ wiphy->mgmt_stypes = rtw_cfg80211_default_mgmt_stypes;
+#endif /* CONFIG_8723AU_AP_MODE */
+
+ wiphy->software_iftypes |= BIT(NL80211_IFTYPE_MONITOR);
+
+ /*
+ wiphy->iface_combinations = &rtw_combinations;
+ wiphy->n_iface_combinations = 1;
+ */
+
+ wiphy->cipher_suites = rtw_cipher_suites;
+ wiphy->n_cipher_suites = ARRAY_SIZE(rtw_cipher_suites);
+
+ /* if (padapter->registrypriv.wireless_mode & WIRELESS_11G) */
+ wiphy->bands[IEEE80211_BAND_2GHZ] =
+ rtw_spt_band_alloc(IEEE80211_BAND_2GHZ);
+ /* if (padapter->registrypriv.wireless_mode & WIRELESS_11A) */
+ wiphy->bands[IEEE80211_BAND_5GHZ] =
+ rtw_spt_band_alloc(IEEE80211_BAND_5GHZ);
+
+ wiphy->flags |= WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL;
+ wiphy->flags |= WIPHY_FLAG_OFFCHAN_TX | WIPHY_FLAG_HAVE_AP_SME;
+
+ if (padapter->registrypriv.power_mgnt != PS_MODE_ACTIVE)
+ wiphy->flags |= WIPHY_FLAG_PS_ON_BY_DEFAULT;
+ else
+ wiphy->flags &= ~WIPHY_FLAG_PS_ON_BY_DEFAULT;
+}
+
+int rtw_wdev_alloc(struct rtw_adapter *padapter, struct device *dev)
+{
+ int ret = 0;
+ struct wiphy *wiphy;
+ struct wireless_dev *wdev;
+ struct rtw_wdev_priv *pwdev_priv;
+ struct net_device *pnetdev = padapter->pnetdev;
+
+ DBG_8723A("%s(padapter =%p)\n", __func__, padapter);
+
+ /* wiphy */
+ wiphy = wiphy_new(&rtw_cfg80211_ops, sizeof(struct rtw_wdev_priv));
+ if (!wiphy) {
+ DBG_8723A("Couldn't allocate wiphy device\n");
+ ret = -ENOMEM;
+ goto exit;
+ }
+
+ /* wdev */
+ wdev = kzalloc(sizeof(struct wireless_dev), GFP_KERNEL);
+ if (!wdev) {
+ DBG_8723A("Couldn't allocate wireless device\n");
+ ret = -ENOMEM;
+ goto free_wiphy;
+ }
+
+ set_wiphy_dev(wiphy, dev);
+ rtw_cfg80211_preinit_wiphy(padapter, wiphy);
+
+ ret = wiphy_register(wiphy);
+ if (ret < 0) {
+ DBG_8723A("Couldn't register wiphy device\n");
+ goto free_wdev;
+ }
+
+ wdev->wiphy = wiphy;
+ wdev->netdev = pnetdev;
+ /* wdev->iftype = NL80211_IFTYPE_STATION; */
+ /* for rtw_setopmode_cmd23a() in cfg80211_rtw_change_iface() */
+ wdev->iftype = NL80211_IFTYPE_MONITOR;
+ padapter->rtw_wdev = wdev;
+ pnetdev->ieee80211_ptr = wdev;
+
+ /* init pwdev_priv */
+ pwdev_priv = wdev_to_priv(wdev);
+ pwdev_priv->rtw_wdev = wdev;
+ pwdev_priv->pmon_ndev = NULL;
+ pwdev_priv->ifname_mon[0] = '\0';
+ pwdev_priv->padapter = padapter;
+ pwdev_priv->scan_request = NULL;
+ spin_lock_init(&pwdev_priv->scan_req_lock);
+
+ pwdev_priv->p2p_enabled = false;
+
+ if (padapter->registrypriv.power_mgnt != PS_MODE_ACTIVE)
+ pwdev_priv->power_mgmt = true;
+ else
+ pwdev_priv->power_mgmt = false;
+
+ return ret;
+free_wdev:
+ kfree(wdev);
+free_wiphy:
+ wiphy_free(wiphy);
+exit:
+ return ret;
+}
+
+void rtw_wdev_free(struct wireless_dev *wdev)
+{
+ DBG_8723A("%s(wdev =%p)\n", __func__, wdev);
+
+ if (!wdev)
+ return;
+
+ kfree(wdev->wiphy->bands[IEEE80211_BAND_2GHZ]);
+ kfree(wdev->wiphy->bands[IEEE80211_BAND_5GHZ]);
+
+ wiphy_free(wdev->wiphy);
+
+ kfree(wdev);
+}
+
+void rtw_wdev_unregister(struct wireless_dev *wdev)
+{
+ struct rtw_wdev_priv *pwdev_priv;
+
+ DBG_8723A("%s(wdev =%p)\n", __func__, wdev);
+
+ if (!wdev)
+ return;
+
+ pwdev_priv = wdev_to_priv(wdev);
+
+ rtw_cfg80211_indicate_scan_done(pwdev_priv, true);
+
+ if (pwdev_priv->pmon_ndev) {
+ DBG_8723A("%s, unregister monitor interface\n", __func__);
+ unregister_netdev(pwdev_priv->pmon_ndev);
+ }
+
+ wiphy_unregister(wdev->wiphy);
+}
diff --git a/drivers/staging/rtl8723au/os_dep/mlme_linux.c b/drivers/staging/rtl8723au/os_dep/mlme_linux.c
new file mode 100644
index 000000000..ca24369f1
--- /dev/null
+++ b/drivers/staging/rtl8723au/os_dep/mlme_linux.c
@@ -0,0 +1,81 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2011 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ ******************************************************************************/
+
+#define _MLME_OSDEP_C_
+
+#include <osdep_service.h>
+#include <drv_types.h>
+#include <mlme_osdep.h>
+
+static struct rt_pmkid_list backupPMKIDList[NUM_PMKID_CACHE];
+
+void rtw_reset_securitypriv23a(struct rtw_adapter *adapter)
+{
+ u8 backupPMKIDIndex = 0;
+ u8 backupTKIPCountermeasure = 0x00;
+ unsigned long backupTKIPcountermeasure_time = 0;
+
+ if (adapter->securitypriv.dot11AuthAlgrthm ==
+ dot11AuthAlgrthm_8021X) { /* 802.1x */
+ /* We have to backup the PMK information for WiFi PMK
+ * Caching test item.
+ * Backup the btkip_countermeasure information.
+ * When the countermeasure is trigger, the driver have to
+ * disconnect with AP for 60 seconds.
+ */
+ memcpy(&backupPMKIDList[0], &adapter->securitypriv.PMKIDList[0],
+ sizeof(struct rt_pmkid_list) * NUM_PMKID_CACHE);
+ backupPMKIDIndex = adapter->securitypriv.PMKIDIndex;
+ backupTKIPCountermeasure = adapter->securitypriv.btkip_countermeasure;
+ backupTKIPcountermeasure_time = adapter->securitypriv.btkip_countermeasure_time;
+
+ memset((unsigned char *)&adapter->securitypriv, 0,
+ sizeof (struct security_priv));
+ /* Restore the PMK information to securitypriv structure
+ * for the following connection.
+ */
+ memcpy(&adapter->securitypriv.PMKIDList[0], &backupPMKIDList[0],
+ sizeof(struct rt_pmkid_list) * NUM_PMKID_CACHE);
+ adapter->securitypriv.PMKIDIndex = backupPMKIDIndex;
+ adapter->securitypriv.btkip_countermeasure = backupTKIPCountermeasure;
+ adapter->securitypriv.btkip_countermeasure_time = backupTKIPcountermeasure_time;
+
+ adapter->securitypriv.ndisauthtype = Ndis802_11AuthModeOpen;
+ adapter->securitypriv.ndisencryptstatus = Ndis802_11WEPDisabled;
+ } else { /* reset values in securitypriv */
+ struct security_priv *psec_priv = &adapter->securitypriv;
+
+ /* open system */
+ psec_priv->dot11AuthAlgrthm = dot11AuthAlgrthm_Open;
+ psec_priv->dot11PrivacyAlgrthm = 0;
+ psec_priv->dot11PrivacyKeyIndex = 0;
+
+ psec_priv->dot118021XGrpPrivacy = 0;
+ psec_priv->dot118021XGrpKeyid = 1;
+
+ psec_priv->ndisauthtype = Ndis802_11AuthModeOpen;
+ psec_priv->ndisencryptstatus = Ndis802_11WEPDisabled;
+ }
+}
+
+void rtw_os_indicate_disconnect23a(struct rtw_adapter *adapter)
+{
+ /* Do it first for tx broadcast pkt after disconnection issue! */
+ netif_carrier_off(adapter->pnetdev);
+
+ rtw_cfg80211_indicate_disconnect(adapter);
+
+ rtw_reset_securitypriv23a(adapter);
+}
diff --git a/drivers/staging/rtl8723au/os_dep/os_intfs.c b/drivers/staging/rtl8723au/os_dep/os_intfs.c
new file mode 100644
index 000000000..84d75d02a
--- /dev/null
+++ b/drivers/staging/rtl8723au/os_dep/os_intfs.c
@@ -0,0 +1,852 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2011 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ ******************************************************************************/
+#define _OS_INTFS_C_
+
+#include <osdep_service.h>
+#include <drv_types.h>
+#include <xmit_osdep.h>
+#include <recv_osdep.h>
+#include <hal_intf.h>
+#include <rtw_version.h>
+
+#include <rtl8723a_hal.h>
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Realtek Wireless Lan Driver");
+MODULE_AUTHOR("Realtek Semiconductor Corp.");
+MODULE_AUTHOR("Larry Finger <Larry.Finger@lwfinger.net>");
+MODULE_AUTHOR("Jes Sorensen <Jes.Sorensen@redhat.com>");
+MODULE_VERSION(DRIVERVERSION);
+/*(DEBLOBBED)*/
+
+/* module param defaults */
+static int rtw_chip_version;
+static int rtw_rfintfs = HWPI;
+static int rtw_debug = 1;
+
+static int rtw_channel = 1;/* ad-hoc support requirement */
+static int rtw_wireless_mode = WIRELESS_11BG_24N;
+static int rtw_vrtl_carrier_sense = AUTO_VCS;
+static int rtw_vcs_type = RTS_CTS;/* */
+static int rtw_rts_thresh = 2347;/* */
+static int rtw_frag_thresh = 2346;/* */
+static int rtw_preamble = PREAMBLE_LONG;/* long, short, auto */
+static int rtw_scan_mode = 1;/* active, passive */
+static int rtw_adhoc_tx_pwr = 1;
+static int rtw_soft_ap;
+static int rtw_power_mgnt = 1;
+static int rtw_ips_mode = IPS_NORMAL;
+
+static int rtw_smart_ps = 2;
+
+module_param(rtw_ips_mode, int, 0644);
+MODULE_PARM_DESC(rtw_ips_mode, "The default IPS mode");
+
+static int rtw_long_retry_lmt = 7;
+static int rtw_short_retry_lmt = 7;
+static int rtw_busy_thresh = 40;
+static int rtw_ack_policy = NORMAL_ACK;
+
+static int rtw_acm_method;/* 0:By SW 1:By HW. */
+
+static int rtw_wmm_enable = 1;/* default is set to enable the wmm. */
+static int rtw_uapsd_enable;
+
+static int rtw_ht_enable = 1;
+/* 0 :diable, bit(0): enable 2.4g, bit(1): enable 5g */
+static int rtw_cbw40_enable = 3;
+static int rtw_ampdu_enable = 1;/* for enable tx_ampdu */
+/* 0: disable, bit(0):enable 2.4g, bit(1):enable 5g, default is set to enable
+ * 2.4GHZ for IOT issue with bufflao's AP at 5GHZ
+ */
+static int rtw_rx_stbc = 1;
+static int rtw_ampdu_amsdu;/* 0: disabled, 1:enabled, 2:auto */
+
+/* Use 2 path Tx to transmit MCS0~7 and legacy mode */
+static int rtw_lowrate_two_xmit = 1;
+
+/* int rf_config = RF_1T2R; 1T2R */
+static int rtw_rf_config = RF_819X_MAX_TYPE; /* auto */
+static int rtw_low_power;
+static int rtw_wifi_spec;
+static int rtw_channel_plan = RT_CHANNEL_DOMAIN_MAX;
+
+#ifdef CONFIG_8723AU_BT_COEXIST
+static int rtw_btcoex_enable = 1;
+static int rtw_bt_iso = 2;/* 0:Low, 1:High, 2:From Efuse */
+/* 0:Idle, 1:None-SCO, 2:SCO, 3:From Counter, 4.Busy, 5.OtherBusy */
+static int rtw_bt_sco = 3;
+/* 0:Disable BT control A-MPDU, 1:Enable BT control A-MPDU. */
+static int rtw_bt_ampdu = 1;
+#endif
+
+/* 0:Reject AP's Add BA req, 1:Accept AP's Add BA req. */
+static int rtw_AcceptAddbaReq = true;
+
+static int rtw_antdiv_cfg = 2; /* 0:OFF , 1:ON, 2:decide by Efuse config */
+static int rtw_antdiv_type; /* 0:decide by efuse */
+
+static int rtw_enusbss;/* 0:disable, 1:enable */
+
+static int rtw_hwpdn_mode = 2;/* 0:disable, 1:enable, 2: by EFUSE config */
+
+static int rtw_hwpwrp_detect; /* HW power ping detect 0:disable , 1:enable */
+
+static int rtw_hw_wps_pbc = 1;
+
+static int rtw_80211d;
+
+static int rtw_regulatory_id = 0xff;/* Regulatory tab id, 0xff = follow efuse's setting */
+
+module_param(rtw_regulatory_id, int, 0644);
+
+static char *ifname = "wlan%d";
+module_param(ifname, charp, 0644);
+MODULE_PARM_DESC(ifname, "The default name to allocate for first interface");
+
+static char *if2name = "wlan%d";
+module_param(if2name, charp, 0644);
+MODULE_PARM_DESC(if2name, "The default name to allocate for second interface");
+
+module_param(rtw_channel_plan, int, 0644);
+module_param(rtw_chip_version, int, 0644);
+module_param(rtw_rfintfs, int, 0644);
+module_param(rtw_channel, int, 0644);
+module_param(rtw_wmm_enable, int, 0644);
+module_param(rtw_vrtl_carrier_sense, int, 0644);
+module_param(rtw_vcs_type, int, 0644);
+module_param(rtw_busy_thresh, int, 0644);
+module_param(rtw_ht_enable, int, 0644);
+module_param(rtw_cbw40_enable, int, 0644);
+module_param(rtw_ampdu_enable, int, 0644);
+module_param(rtw_rx_stbc, int, 0644);
+module_param(rtw_ampdu_amsdu, int, 0644);
+
+module_param(rtw_lowrate_two_xmit, int, 0644);
+
+module_param(rtw_rf_config, int, 0644);
+module_param(rtw_power_mgnt, int, 0644);
+module_param(rtw_smart_ps, int, 0644);
+module_param(rtw_low_power, int, 0644);
+module_param(rtw_wifi_spec, int, 0644);
+
+module_param(rtw_antdiv_cfg, int, 0644);
+
+module_param(rtw_enusbss, int, 0644);
+module_param(rtw_hwpdn_mode, int, 0644);
+module_param(rtw_hwpwrp_detect, int, 0644);
+
+module_param(rtw_hw_wps_pbc, int, 0644);
+
+static uint rtw_max_roaming_times = 2;
+module_param(rtw_max_roaming_times, uint, 0644);
+MODULE_PARM_DESC(rtw_max_roaming_times, "The max roaming times to try");
+
+module_param(rtw_80211d, int, 0644);
+MODULE_PARM_DESC(rtw_80211d, "Enable 802.11d mechanism");
+
+#ifdef CONFIG_8723AU_BT_COEXIST
+module_param(rtw_btcoex_enable, int, 0644);
+MODULE_PARM_DESC(rtw_btcoex_enable, "Enable BT co-existence mechanism");
+#endif
+
+static uint rtw_notch_filter;
+module_param(rtw_notch_filter, uint, 0644);
+MODULE_PARM_DESC(rtw_notch_filter, "0:Disable, 1:Enable, 2:Enable only for P2P");
+module_param_named(debug, rtw_debug, int, 0444);
+MODULE_PARM_DESC(debug, "Set debug level (1-9) (default 1)");
+
+static int netdev_close(struct net_device *pnetdev);
+
+static int loadparam(struct rtw_adapter *padapter, struct net_device *pnetdev)
+{
+ struct registry_priv *registry_par = &padapter->registrypriv;
+
+ GlobalDebugLevel23A = rtw_debug;
+ registry_par->chip_version = (u8)rtw_chip_version;
+ registry_par->rfintfs = (u8)rtw_rfintfs;
+ memcpy(registry_par->ssid.ssid, "ANY", 3);
+ registry_par->ssid.ssid_len = 3;
+ registry_par->channel = (u8)rtw_channel;
+ registry_par->wireless_mode = (u8)rtw_wireless_mode;
+ registry_par->vrtl_carrier_sense = (u8)rtw_vrtl_carrier_sense;
+ registry_par->vcs_type = (u8)rtw_vcs_type;
+ registry_par->rts_thresh = (u16)rtw_rts_thresh;
+ registry_par->frag_thresh = (u16)rtw_frag_thresh;
+ registry_par->preamble = (u8)rtw_preamble;
+ registry_par->scan_mode = (u8)rtw_scan_mode;
+ registry_par->adhoc_tx_pwr = (u8)rtw_adhoc_tx_pwr;
+ registry_par->soft_ap = (u8)rtw_soft_ap;
+ registry_par->smart_ps = (u8)rtw_smart_ps;
+ registry_par->power_mgnt = (u8)rtw_power_mgnt;
+ registry_par->ips_mode = (u8)rtw_ips_mode;
+ registry_par->long_retry_lmt = (u8)rtw_long_retry_lmt;
+ registry_par->short_retry_lmt = (u8)rtw_short_retry_lmt;
+ registry_par->busy_thresh = (u16)rtw_busy_thresh;
+ registry_par->ack_policy = (u8)rtw_ack_policy;
+ registry_par->acm_method = (u8)rtw_acm_method;
+ /* UAPSD */
+ registry_par->wmm_enable = (u8)rtw_wmm_enable;
+ registry_par->uapsd_enable = (u8)rtw_uapsd_enable;
+ registry_par->ht_enable = (u8)rtw_ht_enable;
+ registry_par->cbw40_enable = (u8)rtw_cbw40_enable;
+ registry_par->ampdu_enable = (u8)rtw_ampdu_enable;
+ registry_par->rx_stbc = (u8)rtw_rx_stbc;
+ registry_par->ampdu_amsdu = (u8)rtw_ampdu_amsdu;
+ registry_par->lowrate_two_xmit = (u8)rtw_lowrate_two_xmit;
+ registry_par->rf_config = (u8)rtw_rf_config;
+ registry_par->low_power = (u8)rtw_low_power;
+ registry_par->wifi_spec = (u8)rtw_wifi_spec;
+ registry_par->channel_plan = (u8)rtw_channel_plan;
+#ifdef CONFIG_8723AU_BT_COEXIST
+ registry_par->btcoex = (u8)rtw_btcoex_enable;
+ registry_par->bt_iso = (u8)rtw_bt_iso;
+ registry_par->bt_sco = (u8)rtw_bt_sco;
+ registry_par->bt_ampdu = (u8)rtw_bt_ampdu;
+#endif
+ registry_par->bAcceptAddbaReq = (u8)rtw_AcceptAddbaReq;
+ registry_par->antdiv_cfg = (u8)rtw_antdiv_cfg;
+ registry_par->antdiv_type = (u8)rtw_antdiv_type;
+
+ /* 0:disable, 1:enable, 2:by EFUSE config */
+ registry_par->hwpdn_mode = (u8)rtw_hwpdn_mode;
+ /* 0:disable, 1:enable */
+ registry_par->hwpwrp_detect = (u8)rtw_hwpwrp_detect;
+ registry_par->hw_wps_pbc = (u8)rtw_hw_wps_pbc;
+ registry_par->max_roaming_times = (u8)rtw_max_roaming_times;
+ registry_par->enable80211d = (u8)rtw_80211d;
+ snprintf(registry_par->ifname, 16, "%s", ifname);
+ snprintf(registry_par->if2name, 16, "%s", if2name);
+ registry_par->notch_filter = (u8)rtw_notch_filter;
+ registry_par->regulatory_tid = (u8)rtw_regulatory_id;
+ return _SUCCESS;
+}
+
+static int rtw_net_set_mac_address(struct net_device *pnetdev, void *p)
+{
+ struct rtw_adapter *padapter = netdev_priv(pnetdev);
+ struct sockaddr *addr = p;
+
+ if (!padapter->bup)
+ ether_addr_copy(padapter->eeprompriv.mac_addr, addr->sa_data);
+ return 0;
+}
+
+static struct net_device_stats *rtw_net_get_stats(struct net_device *pnetdev)
+{
+ struct rtw_adapter *padapter = netdev_priv(pnetdev);
+ struct xmit_priv *pxmitpriv = &padapter->xmitpriv;
+ struct recv_priv *precvpriv = &padapter->recvpriv;
+
+ padapter->stats.tx_packets = pxmitpriv->tx_pkts;
+ padapter->stats.rx_packets = precvpriv->rx_pkts;
+ padapter->stats.tx_dropped = pxmitpriv->tx_drop;
+ padapter->stats.rx_dropped = precvpriv->rx_drop;
+ padapter->stats.tx_bytes = pxmitpriv->tx_bytes;
+ padapter->stats.rx_bytes = precvpriv->rx_bytes;
+
+ return &padapter->stats;
+}
+
+/*
+ * AC to queue mapping
+ *
+ * AC_VO -> queue 0
+ * AC_VI -> queue 1
+ * AC_BE -> queue 2
+ * AC_BK -> queue 3
+ */
+static const u16 rtw_1d_to_queue[8] = { 2, 3, 3, 2, 1, 1, 0, 0 };
+
+/* Given a data frame determine the 802.1p/1d tag to use. */
+static u32 rtw_classify8021d(struct sk_buff *skb)
+{
+ u32 dscp;
+
+ /* skb->priority values from 256->263 are magic values to
+ * directly indicate a specific 802.1d priority. This is used
+ * to allow 802.1d priority to be passed directly in from VLAN
+ * tags, etc.
+ */
+ if (skb->priority >= 256 && skb->priority <= 263)
+ return skb->priority - 256;
+ switch (skb->protocol) {
+ case htons(ETH_P_IP):
+ dscp = ip_hdr(skb)->tos & 0xfc;
+ break;
+ default:
+ return 0;
+ }
+ return dscp >> 5;
+}
+
+static u16 rtw_select_queue(struct net_device *dev, struct sk_buff *skb,
+ void *accel_priv,
+ select_queue_fallback_t fallback)
+{
+ struct rtw_adapter *padapter = netdev_priv(dev);
+ struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
+
+ skb->priority = rtw_classify8021d(skb);
+
+ if (pmlmepriv->acm_mask != 0)
+ skb->priority = qos_acm23a(pmlmepriv->acm_mask, skb->priority);
+ return rtw_1d_to_queue[skb->priority];
+}
+
+u16 rtw_recv_select_queue23a(struct sk_buff *skb)
+{
+ struct iphdr *piphdr;
+ struct ethhdr *eth = (struct ethhdr *)skb->data;
+ unsigned int dscp;
+ u16 eth_type = get_unaligned_be16(&eth->h_proto);
+ u32 priority;
+ u8 *pdata = skb->data;
+
+ switch (eth_type) {
+ case ETH_P_IP:
+ piphdr = (struct iphdr *)(pdata + ETH_HLEN);
+ dscp = piphdr->tos & 0xfc;
+ priority = dscp >> 5;
+ break;
+ default:
+ priority = 0;
+ }
+ return rtw_1d_to_queue[priority];
+}
+
+static const struct net_device_ops rtw_netdev_ops = {
+ .ndo_open = netdev_open23a,
+ .ndo_stop = netdev_close,
+ .ndo_start_xmit = rtw_xmit23a_entry23a,
+ .ndo_select_queue = rtw_select_queue,
+ .ndo_set_mac_address = rtw_net_set_mac_address,
+ .ndo_get_stats = rtw_net_get_stats,
+};
+
+int rtw_init_netdev23a_name23a(struct net_device *pnetdev, const char *ifname)
+{
+ if (dev_alloc_name(pnetdev, ifname) < 0) {
+ RT_TRACE(_module_os_intfs_c_, _drv_err_,
+ "dev_alloc_name, fail!\n");
+ }
+ netif_carrier_off(pnetdev);
+ return 0;
+}
+
+static const struct device_type wlan_type = {
+ .name = "wlan",
+};
+
+struct net_device *rtw_init_netdev23a(struct rtw_adapter *old_padapter)
+{
+ struct rtw_adapter *padapter;
+ struct net_device *pnetdev;
+
+ RT_TRACE(_module_os_intfs_c_, _drv_info_, "+init_net_dev\n");
+
+ pnetdev = alloc_etherdev_mq(sizeof(struct rtw_adapter), 4);
+ if (!pnetdev)
+ return NULL;
+
+ pnetdev->dev.type = &wlan_type;
+ padapter = netdev_priv(pnetdev);
+ padapter->pnetdev = pnetdev;
+
+ DBG_8723A("register rtw_netdev_ops to netdev_ops\n");
+ pnetdev->netdev_ops = &rtw_netdev_ops;
+
+ pnetdev->watchdog_timeo = HZ*3; /* 3 second timeout */
+
+ /* step 2. */
+ loadparam(padapter, pnetdev);
+ return pnetdev;
+}
+
+static int rtw_init_default_value(struct rtw_adapter *padapter)
+{
+ struct registry_priv *pregistrypriv = &padapter->registrypriv;
+ struct xmit_priv *pxmitpriv = &padapter->xmitpriv;
+ struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
+ struct security_priv *psecuritypriv = &padapter->securitypriv;
+
+ /* xmit_priv */
+ pxmitpriv->vcs = pregistrypriv->vcs_type;
+ /* pxmitpriv->rts_thresh = pregistrypriv->rts_thresh; */
+ pxmitpriv->frag_len = pregistrypriv->frag_thresh;
+
+ /* mlme_priv */
+ pmlmepriv->scan_interval = SCAN_INTERVAL;/* 30*2 sec = 60sec */
+ pmlmepriv->scan_mode = SCAN_ACTIVE;
+
+ /* ht_priv */
+ pmlmepriv->htpriv.ampdu_enable = false;/* set to disabled */
+
+ /* security_priv */
+ psecuritypriv->binstallGrpkey = 0;
+
+ /* open system */
+ psecuritypriv->dot11AuthAlgrthm = dot11AuthAlgrthm_Open;
+ psecuritypriv->dot11PrivacyAlgrthm = 0;
+
+ psecuritypriv->dot11PrivacyKeyIndex = 0;
+
+ psecuritypriv->dot118021XGrpPrivacy = 0;
+ psecuritypriv->dot118021XGrpKeyid = 1;
+
+ psecuritypriv->ndisauthtype = Ndis802_11AuthModeOpen;
+ psecuritypriv->ndisencryptstatus = Ndis802_11WEPDisabled;
+
+ /* registry_priv */
+ rtw_init_registrypriv_dev_network23a(padapter);
+ rtw_update_registrypriv_dev_network23a(padapter);
+
+ /* hal_priv */
+ rtl8723a_init_default_value(padapter);
+
+ /* misc. */
+ padapter->bReadPortCancel = false;
+ padapter->bWritePortCancel = false;
+ return _SUCCESS;
+}
+
+int rtw_reset_drv_sw23a(struct rtw_adapter *padapter)
+{
+ struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
+ struct pwrctrl_priv *pwrctrlpriv = &padapter->pwrctrlpriv;
+
+ /* hal_priv */
+ rtl8723a_init_default_value(padapter);
+ padapter->bReadPortCancel = false;
+ padapter->bWritePortCancel = false;
+ pmlmepriv->scan_interval = SCAN_INTERVAL;/* 30*2 sec = 60sec */
+
+ padapter->xmitpriv.tx_pkts = 0;
+ padapter->recvpriv.rx_pkts = 0;
+
+ pmlmepriv->LinkDetectInfo.bBusyTraffic = false;
+
+ _clr_fwstate_(pmlmepriv, _FW_UNDER_SURVEY | _FW_UNDER_LINKING);
+
+ rtw_sreset_reset_value(padapter);
+ pwrctrlpriv->pwr_state_check_cnts = 0;
+
+ /* mlmeextpriv */
+ padapter->mlmeextpriv.sitesurvey_res.state = SCAN_DISABLE;
+
+ rtw_set_signal_stat_timer(&padapter->recvpriv);
+ return _SUCCESS;
+}
+
+int rtw_init_drv_sw23a(struct rtw_adapter *padapter)
+{
+ int ret8 = _SUCCESS;
+
+ RT_TRACE(_module_os_intfs_c_, _drv_info_, "+rtw_init_drv_sw23a\n");
+
+ if (rtw_init_cmd_priv23a(&padapter->cmdpriv) == _FAIL) {
+ RT_TRACE(_module_os_intfs_c_, _drv_err_,
+ "Can't init cmd_priv\n");
+ ret8 = _FAIL;
+ goto exit;
+ }
+
+ padapter->cmdpriv.padapter = padapter;
+
+ if (rtw_init_evt_priv23a(&padapter->evtpriv) == _FAIL) {
+ RT_TRACE(_module_os_intfs_c_, _drv_err_,
+ "Can't init evt_priv\n");
+ ret8 = _FAIL;
+ goto exit;
+ }
+
+ if (rtw_init_mlme_priv23a(padapter) == _FAIL) {
+ RT_TRACE(_module_os_intfs_c_, _drv_err_,
+ "Can't init mlme_priv\n");
+ ret8 = _FAIL;
+ goto exit;
+ }
+
+
+ if (init_mlme_ext_priv23a(padapter) == _FAIL) {
+ RT_TRACE(_module_os_intfs_c_, _drv_err_,
+ "Can't init mlme_ext_priv\n");
+ ret8 = _FAIL;
+ goto exit;
+ }
+
+ if (_rtw_init_xmit_priv23a(&padapter->xmitpriv, padapter) == _FAIL) {
+ DBG_8723A("Can't _rtw_init_xmit_priv23a\n");
+ ret8 = _FAIL;
+ goto exit;
+ }
+
+ if (_rtw_init_recv_priv23a(&padapter->recvpriv, padapter) == _FAIL) {
+ DBG_8723A("Can't _rtw_init_recv_priv23a\n");
+ ret8 = _FAIL;
+ goto exit;
+ }
+
+ if (_rtw_init_sta_priv23a(&padapter->stapriv) == _FAIL) {
+ DBG_8723A("Can't _rtw_init_sta_priv23a\n");
+ ret8 = _FAIL;
+ goto exit;
+ }
+
+ padapter->stapriv.padapter = padapter;
+ padapter->setband = GHZ24_50;
+ rtw_init_bcmc_stainfo23a(padapter);
+
+ rtw_init_pwrctrl_priv23a(padapter);
+
+ ret8 = rtw_init_default_value(padapter);
+
+ rtl8723a_init_dm_priv(padapter);
+
+ rtw_sreset_init(padapter);
+
+exit:
+
+ RT_TRACE(_module_os_intfs_c_, _drv_info_, "-rtw_init_drv_sw23a\n");
+ return ret8;
+}
+
+void rtw_cancel_all_timer23a(struct rtw_adapter *padapter)
+{
+ RT_TRACE(_module_os_intfs_c_, _drv_info_,
+ "+rtw_cancel_all_timer23a\n");
+
+ del_timer_sync(&padapter->mlmepriv.assoc_timer);
+ RT_TRACE(_module_os_intfs_c_, _drv_info_,
+ "%s:cancel association timer complete!\n", __func__);
+
+ del_timer_sync(&padapter->mlmepriv.scan_to_timer);
+ RT_TRACE(_module_os_intfs_c_, _drv_info_,
+ "%s:cancel scan_to_timer!\n", __func__);
+
+ del_timer_sync(&padapter->mlmepriv.dynamic_chk_timer);
+ RT_TRACE(_module_os_intfs_c_, _drv_info_,
+ "%s:cancel dynamic_chk_timer!\n", __func__);
+
+ del_timer_sync(&padapter->pwrctrlpriv.pwr_state_check_timer);
+
+ del_timer_sync(&padapter->mlmepriv.set_scan_deny_timer);
+ rtw_clear_scan_deny(padapter);
+ RT_TRACE(_module_os_intfs_c_, _drv_info_,
+ "%s:cancel set_scan_deny_timer!\n", __func__);
+
+ del_timer_sync(&padapter->recvpriv.signal_stat_timer);
+}
+
+int rtw_free_drv_sw23a(struct rtw_adapter *padapter)
+{
+ RT_TRACE(_module_os_intfs_c_, _drv_info_, "==>rtw_free_drv_sw23a\n");
+
+ free_mlme_ext_priv23a(&padapter->mlmeextpriv);
+
+ rtw_free_evt_priv23a(&padapter->evtpriv);
+
+ rtw_free_mlme_priv23a(&padapter->mlmepriv);
+
+ _rtw_free_xmit_priv23a(&padapter->xmitpriv);
+
+ /* will free bcmc_stainfo here */
+ _rtw_free_sta_priv23a(&padapter->stapriv);
+
+ _rtw_free_recv_priv23a(&padapter->recvpriv);
+
+ rtw_free_pwrctrl_priv(padapter);
+
+ kfree(padapter->HalData);
+ padapter->HalData = NULL;
+
+ RT_TRACE(_module_os_intfs_c_, _drv_info_, "-rtw_free_drv_sw23a\n");
+ return _SUCCESS;
+}
+
+static int _rtw_drv_register_netdev(struct rtw_adapter *padapter, char *name)
+{
+ struct net_device *pnetdev = padapter->pnetdev;
+ int ret = _SUCCESS;
+
+ /* alloc netdev name */
+ rtw_init_netdev23a_name23a(pnetdev, name);
+
+ ether_addr_copy(pnetdev->dev_addr, padapter->eeprompriv.mac_addr);
+
+ /* Tell the network stack we exist */
+ if (register_netdev(pnetdev)) {
+ DBG_8723A("%s(%s): Failed!\n", __func__, pnetdev->name);
+ ret = _FAIL;
+ goto error_register_netdev;
+ }
+ DBG_8723A("%s, MAC Address (if%d) = %pM\n",
+ __func__, padapter->iface_id + 1, pnetdev->dev_addr);
+ return ret;
+
+error_register_netdev:
+
+ if (padapter->iface_id > IFACE_ID0) {
+ rtw_free_drv_sw23a(padapter);
+
+ free_netdev(pnetdev);
+ }
+ return ret;
+}
+
+int rtw_drv_register_netdev(struct rtw_adapter *if1)
+{
+ struct dvobj_priv *dvobj = if1->dvobj;
+ int i, status = _SUCCESS;
+
+ if (dvobj->iface_nums >= IFACE_ID_MAX) {
+ status = _FAIL; /* -EINVAL */
+ goto exit;
+ }
+
+ for (i = 0; i < dvobj->iface_nums; i++) {
+ struct rtw_adapter *padapter = dvobj->padapters[i];
+
+ if (padapter) {
+ char *name;
+
+ if (padapter->iface_id == IFACE_ID0)
+ name = if1->registrypriv.ifname;
+ else if (padapter->iface_id == IFACE_ID1)
+ name = if1->registrypriv.if2name;
+ else
+ name = "wlan%d";
+ status = _rtw_drv_register_netdev(padapter, name);
+ if (status != _SUCCESS)
+ break;
+ }
+ }
+
+exit:
+ return status;
+}
+
+int netdev_open23a(struct net_device *pnetdev)
+{
+ struct rtw_adapter *padapter = netdev_priv(pnetdev);
+ struct pwrctrl_priv *pwrctrlpriv;
+ int ret = 0;
+ int status;
+
+ RT_TRACE(_module_os_intfs_c_, _drv_info_, "+871x_drv - dev_open\n");
+ DBG_8723A("+871x_drv - drv_open, bup =%d\n", padapter->bup);
+
+ mutex_lock(&adapter_to_dvobj(padapter)->hw_init_mutex);
+
+ pwrctrlpriv = &padapter->pwrctrlpriv;
+
+ if (!padapter->bup) {
+ padapter->bDriverStopped = false;
+ padapter->bSurpriseRemoved = false;
+ padapter->bCardDisableWOHSM = false;
+
+ status = rtl8723au_hal_init(padapter);
+ if (status == _FAIL) {
+ RT_TRACE(_module_os_intfs_c_, _drv_err_,
+ "rtl871x_hal_init(): Can't init h/w!\n");
+ goto netdev_open23a_error;
+ }
+
+ DBG_8723A("MAC Address = %pM\n", pnetdev->dev_addr);
+
+ if (init_hw_mlme_ext23a(padapter) == _FAIL) {
+ DBG_8723A("can't init mlme_ext_priv\n");
+ goto netdev_open23a_error;
+ }
+
+ rtl8723au_inirp_init(padapter);
+
+ rtw_cfg80211_init_wiphy(padapter);
+
+ padapter->bup = true;
+ }
+ padapter->net_closed = false;
+
+ mod_timer(&padapter->mlmepriv.dynamic_chk_timer,
+ jiffies + msecs_to_jiffies(2000));
+
+ padapter->pwrctrlpriv.bips_processing = false;
+ rtw_set_pwr_state_check_timer(&padapter->pwrctrlpriv);
+
+ /* netif_carrier_on(pnetdev);call this func when
+ rtw23a_joinbss_event_cb return success */
+ if (!rtw_netif_queue_stopped(pnetdev))
+ netif_tx_start_all_queues(pnetdev);
+ else
+ netif_tx_wake_all_queues(pnetdev);
+
+ RT_TRACE(_module_os_intfs_c_, _drv_info_, "-871x_drv - dev_open\n");
+ DBG_8723A("-871x_drv - drv_open, bup =%d\n", padapter->bup);
+exit:
+ mutex_unlock(&adapter_to_dvobj(padapter)->hw_init_mutex);
+ return ret;
+
+netdev_open23a_error:
+ padapter->bup = false;
+
+ netif_carrier_off(pnetdev);
+ netif_tx_stop_all_queues(pnetdev);
+
+ RT_TRACE(_module_os_intfs_c_, _drv_err_,
+ "-871x_drv - dev_open, fail!\n");
+ DBG_8723A("-871x_drv - drv_open fail, bup =%d\n", padapter->bup);
+
+ ret = -1;
+ goto exit;
+}
+
+static int ips_netdrv_open(struct rtw_adapter *padapter)
+{
+ int status = _SUCCESS;
+
+ padapter->net_closed = false;
+ DBG_8723A("===> %s.........\n", __func__);
+
+ padapter->bDriverStopped = false;
+ padapter->bSurpriseRemoved = false;
+ padapter->bCardDisableWOHSM = false;
+
+ status = rtl8723au_hal_init(padapter);
+ if (status == _FAIL) {
+ RT_TRACE(_module_os_intfs_c_, _drv_err_,
+ "ips_netdrv_open(): Can't init h/w!\n");
+ goto netdev_open23a_error;
+ }
+
+ rtl8723au_inirp_init(padapter);
+
+ rtw_set_pwr_state_check_timer(&padapter->pwrctrlpriv);
+ mod_timer(&padapter->mlmepriv.dynamic_chk_timer,
+ jiffies + msecs_to_jiffies(5000));
+
+ return _SUCCESS;
+
+netdev_open23a_error:
+ /* padapter->bup = false; */
+ DBG_8723A("-ips_netdrv_open - drv_open failure, bup =%d\n",
+ padapter->bup);
+
+ return _FAIL;
+}
+
+int rtw_ips_pwr_up23a(struct rtw_adapter *padapter)
+{
+ int result;
+ unsigned long start_time = jiffies;
+
+ DBG_8723A("===> rtw_ips_pwr_up23a..............\n");
+ rtw_reset_drv_sw23a(padapter);
+
+ result = ips_netdrv_open(padapter);
+
+ DBG_8723A("<=== rtw_ips_pwr_up23a.............. in %dms\n",
+ jiffies_to_msecs(jiffies - start_time));
+ return result;
+}
+
+void rtw_ips_pwr_down23a(struct rtw_adapter *padapter)
+{
+ unsigned long start_time = jiffies;
+
+ DBG_8723A("===> rtw_ips_pwr_down23a...................\n");
+
+ padapter->bCardDisableWOHSM = true;
+ padapter->net_closed = true;
+
+ rtw_ips_dev_unload23a(padapter);
+ padapter->bCardDisableWOHSM = false;
+ DBG_8723A("<=== rtw_ips_pwr_down23a..................... in %dms\n",
+ jiffies_to_msecs(jiffies - start_time));
+}
+
+void rtw_ips_dev_unload23a(struct rtw_adapter *padapter)
+{
+ rtl8723a_fifo_cleanup(padapter);
+
+ rtl8723a_usb_intf_stop(padapter);
+
+ /* s5. */
+ if (!padapter->bSurpriseRemoved)
+ rtl8723au_hal_deinit(padapter);
+}
+
+int pm_netdev_open23a(struct net_device *pnetdev, u8 bnormal)
+{
+ int status;
+
+ if (bnormal)
+ status = netdev_open23a(pnetdev);
+ else
+ status = (_SUCCESS == ips_netdrv_open(netdev_priv(pnetdev))) ?
+ (0) : (-1);
+
+ return status;
+}
+
+static int netdev_close(struct net_device *pnetdev)
+{
+ struct rtw_adapter *padapter = netdev_priv(pnetdev);
+
+ RT_TRACE(_module_os_intfs_c_, _drv_info_, "+871x_drv - drv_close\n");
+
+ padapter->net_closed = true;
+
+ if (padapter->pwrctrlpriv.rf_pwrstate == rf_on) {
+ DBG_8723A("(2)871x_drv - drv_close, bup =%d, "
+ "hw_init_completed =%d\n", padapter->bup,
+ padapter->hw_init_completed);
+
+ /* s1. */
+ if (pnetdev) {
+ if (!rtw_netif_queue_stopped(pnetdev))
+ netif_tx_stop_all_queues(pnetdev);
+ }
+
+ /* s2. */
+ LeaveAllPowerSaveMode23a(padapter);
+ rtw_disassoc_cmd23a(padapter, 500, false);
+ /* s2-2. indicate disconnect to os */
+ rtw_indicate_disconnect23a(padapter);
+ /* s2-3. */
+ rtw_free_assoc_resources23a(padapter, 1);
+ /* s2-4. */
+ rtw_free_network_queue23a(padapter);
+ }
+
+ rtw_scan_abort23a(padapter);
+
+ RT_TRACE(_module_os_intfs_c_, _drv_info_, "-871x_drv - drv_close\n");
+ DBG_8723A("-871x_drv - drv_close, bup =%d\n", padapter->bup);
+
+ return 0;
+}
+
+void rtw_ndev_destructor(struct net_device *ndev)
+{
+ DBG_8723A("%s(%s)\n", __func__, ndev->name);
+ kfree(ndev->ieee80211_ptr);
+ free_netdev(ndev);
+}
+
+void _rtw_init_queue23a(struct rtw_queue *pqueue)
+{
+ INIT_LIST_HEAD(&pqueue->queue);
+ spin_lock_init(&pqueue->lock);
+}
diff --git a/drivers/staging/rtl8723au/os_dep/recv_linux.c b/drivers/staging/rtl8723au/os_dep/recv_linux.c
new file mode 100644
index 000000000..084b506ae
--- /dev/null
+++ b/drivers/staging/rtl8723au/os_dep/recv_linux.c
@@ -0,0 +1,165 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2011 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ ******************************************************************************/
+#define _RECV_OSDEP_C_
+
+#include <osdep_service.h>
+#include <drv_types.h>
+
+#include <wifi.h>
+#include <recv_osdep.h>
+
+#include <osdep_intf.h>
+
+#include <usb_ops.h>
+
+void rtw_handle_tkip_mic_err23a(struct rtw_adapter *padapter, u8 bgroup)
+{
+ enum nl80211_key_type key_type = 0;
+ union iwreq_data wrqu;
+ struct iw_michaelmicfailure ev;
+ struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
+ struct security_priv *psecuritypriv = &padapter->securitypriv;
+ unsigned long cur_time;
+
+ if (psecuritypriv->last_mic_err_time == 0) {
+ psecuritypriv->last_mic_err_time = jiffies;
+ } else {
+ cur_time = jiffies;
+
+ if (cur_time - psecuritypriv->last_mic_err_time < 60*HZ) {
+ psecuritypriv->btkip_countermeasure = true;
+ psecuritypriv->last_mic_err_time = 0;
+ psecuritypriv->btkip_countermeasure_time = cur_time;
+ } else {
+ psecuritypriv->last_mic_err_time = jiffies;
+ }
+ }
+
+ if (bgroup)
+ key_type |= NL80211_KEYTYPE_GROUP;
+ else
+ key_type |= NL80211_KEYTYPE_PAIRWISE;
+
+ cfg80211_michael_mic_failure(padapter->pnetdev,
+ (u8 *)&pmlmepriv->assoc_bssid[0],
+ key_type, -1, NULL, GFP_ATOMIC);
+
+ memset(&ev, 0x00, sizeof(ev));
+ if (bgroup)
+ ev.flags |= IW_MICFAILURE_GROUP;
+ else
+ ev.flags |= IW_MICFAILURE_PAIRWISE;
+
+ ev.src_addr.sa_family = ARPHRD_ETHER;
+ ether_addr_copy(ev.src_addr.sa_data, &pmlmepriv->assoc_bssid[0]);
+
+ memset(&wrqu, 0x00, sizeof(wrqu));
+ wrqu.data.length = sizeof(ev);
+}
+
+int rtw_recv_indicatepkt23a(struct rtw_adapter *padapter,
+ struct recv_frame *precv_frame)
+{
+ struct recv_priv *precvpriv;
+ struct sk_buff *skb;
+ struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
+
+ precvpriv = &padapter->recvpriv;
+
+ skb = precv_frame->pkt;
+ if (!skb) {
+ RT_TRACE(_module_recv_osdep_c_, _drv_err_,
+ "rtw_recv_indicatepkt23a():skb == NULL!!!!\n");
+ goto _recv_indicatepkt_drop;
+ }
+
+ RT_TRACE(_module_recv_osdep_c_, _drv_info_,
+ "rtw_recv_indicatepkt23a():skb != NULL !!!\n");
+ RT_TRACE(_module_recv_osdep_c_, _drv_info_,
+ "rtw_recv_indicatepkt23a():precv_frame->hdr.rx_data =%p\n",
+ precv_frame->pkt->data);
+ RT_TRACE(_module_recv_osdep_c_, _drv_info_,
+ "skb->head =%p skb->data =%p skb->tail =%p skb->end =%p skb->len =%d\n",
+ skb->head, skb->data,
+ skb_tail_pointer(skb), skb_end_pointer(skb), skb->len);
+
+ if (check_fwstate(pmlmepriv, WIFI_AP_STATE) == true) {
+ struct sk_buff *pskb2 = NULL;
+ struct sta_info *psta = NULL;
+ struct sta_priv *pstapriv = &padapter->stapriv;
+ struct rx_pkt_attrib *pattrib = &precv_frame->attrib;
+ int bmcast = is_multicast_ether_addr(pattrib->dst);
+
+ /* DBG_8723A("bmcast =%d\n", bmcast); */
+
+ if (!ether_addr_equal(pattrib->dst,
+ myid(&padapter->eeprompriv))) {
+ /* DBG_8723A("not ap psta =%p, addr =%pM\n", psta, pattrib->dst); */
+ if (bmcast) {
+ psta = rtw_get_bcmc_stainfo23a(padapter);
+ pskb2 = skb_clone(skb, GFP_ATOMIC);
+ } else {
+ psta = rtw_get_stainfo23a(pstapriv, pattrib->dst);
+ }
+
+ if (psta) {
+ struct net_device *pnetdev = padapter->pnetdev;
+
+ /* DBG_8723A("directly forwarding to the rtw_xmit23a_entry23a\n"); */
+
+ /* skb->ip_summed = CHECKSUM_NONE; */
+ skb->dev = pnetdev;
+ skb_set_queue_mapping(skb, rtw_recv_select_queue23a(skb));
+
+ rtw_xmit23a_entry23a(skb, pnetdev);
+
+ if (bmcast)
+ skb = pskb2;
+ else
+ goto _recv_indicatepkt_end;
+ }
+ } else { /* to APself */
+ /* DBG_8723A("to APSelf\n"); */
+ }
+ }
+
+ skb->ip_summed = CHECKSUM_NONE;
+ skb->dev = padapter->pnetdev;
+ skb->protocol = eth_type_trans(skb, padapter->pnetdev);
+
+ netif_rx(skb);
+
+_recv_indicatepkt_end:
+
+ precv_frame->pkt = NULL; /* pointers to NULL before rtw_free_recvframe23a() */
+
+ rtw_free_recvframe23a(precv_frame);
+
+ RT_TRACE(_module_recv_osdep_c_, _drv_info_,
+ "rtw_recv_indicatepkt23a :after netif_rx!!!!\n");
+ return _SUCCESS;
+
+_recv_indicatepkt_drop:
+
+ rtw_free_recvframe23a(precv_frame);
+ return _FAIL;
+}
+
+void rtw_init_recv_timer23a(struct recv_reorder_ctrl *preorder_ctrl)
+{
+ setup_timer(&preorder_ctrl->reordering_ctrl_timer,
+ rtw_reordering_ctrl_timeout_handler23a,
+ (unsigned long)preorder_ctrl);
+}
diff --git a/drivers/staging/rtl8723au/os_dep/usb_intf.c b/drivers/staging/rtl8723au/os_dep/usb_intf.c
new file mode 100644
index 000000000..27b3a5b7d
--- /dev/null
+++ b/drivers/staging/rtl8723au/os_dep/usb_intf.c
@@ -0,0 +1,622 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2011 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ ******************************************************************************/
+#define _HCI_INTF_C_
+
+#include <osdep_service.h>
+#include <drv_types.h>
+#include <recv_osdep.h>
+#include <xmit_osdep.h>
+#include <hal_intf.h>
+#include <rtw_version.h>
+#include <osdep_intf.h>
+#include <usb_ops.h>
+#include <rtl8723a_hal.h>
+
+static int rtw_suspend(struct usb_interface *intf, pm_message_t message);
+static int rtw_resume(struct usb_interface *intf);
+static int rtw_drv_init(struct usb_interface *pusb_intf,
+ const struct usb_device_id *pdid);
+static void rtw_disconnect(struct usb_interface *pusb_intf);
+
+#define USB_VENDER_ID_REALTEK 0x0BDA
+
+#define RTL8723A_USB_IDS \
+ {USB_DEVICE_AND_INTERFACE_INFO(USB_VENDER_ID_REALTEK, 0x8724, \
+ 0xff, 0xff, 0xff)}, /* 8723AU 1*1 */ \
+ {USB_DEVICE_AND_INTERFACE_INFO(USB_VENDER_ID_REALTEK, 0x1724, \
+ 0xff, 0xff, 0xff)}, /* 8723AU 1*1 */ \
+ {USB_DEVICE_AND_INTERFACE_INFO(USB_VENDER_ID_REALTEK, 0x0724, \
+ 0xff, 0xff, 0xff)}, /* 8723AU 1*1 */
+
+static struct usb_device_id rtl8723a_usb_id_tbl[] = {
+ RTL8723A_USB_IDS
+ {} /* Terminating entry */
+};
+
+MODULE_DEVICE_TABLE(usb, rtl8723a_usb_id_tbl);
+
+static struct usb_driver rtl8723a_usb_drv = {
+ .name = (char *)"rtl8723au",
+ .probe = rtw_drv_init,
+ .disconnect = rtw_disconnect,
+ .id_table = rtl8723a_usb_id_tbl,
+ .suspend = rtw_suspend,
+ .resume = rtw_resume,
+ .reset_resume = rtw_resume,
+};
+
+static struct usb_driver *usb_drv = &rtl8723a_usb_drv;
+
+static int rtw_init_intf_priv(struct dvobj_priv *dvobj)
+{
+ mutex_init(&dvobj->usb_vendor_req_mutex);
+
+ return _SUCCESS;
+}
+
+static int rtw_deinit_intf_priv(struct dvobj_priv *dvobj)
+{
+ mutex_destroy(&dvobj->usb_vendor_req_mutex);
+
+ return _SUCCESS;
+}
+
+static struct dvobj_priv *usb_dvobj_init(struct usb_interface *usb_intf)
+{
+ struct dvobj_priv *pdvobjpriv;
+ struct usb_host_config *phost_conf;
+ struct usb_config_descriptor *pconf_desc;
+ struct usb_host_interface *phost_iface;
+ struct usb_interface_descriptor *piface_desc;
+ struct usb_endpoint_descriptor *pendp_desc;
+ struct usb_device *pusbd;
+ int i, status = _FAIL;
+
+ pdvobjpriv = kzalloc(sizeof(*pdvobjpriv), GFP_KERNEL);
+ if (!pdvobjpriv)
+ goto exit;
+
+ mutex_init(&pdvobjpriv->hw_init_mutex);
+ mutex_init(&pdvobjpriv->h2c_fwcmd_mutex);
+ mutex_init(&pdvobjpriv->setch_mutex);
+ mutex_init(&pdvobjpriv->setbw_mutex);
+
+ pdvobjpriv->pusbintf = usb_intf;
+ pusbd = interface_to_usbdev(usb_intf);
+ pdvobjpriv->pusbdev = pusbd;
+ usb_set_intfdata(usb_intf, pdvobjpriv);
+
+ pdvobjpriv->RtNumInPipes = 0;
+ pdvobjpriv->RtNumOutPipes = 0;
+
+ phost_conf = pusbd->actconfig;
+ pconf_desc = &phost_conf->desc;
+
+ phost_iface = &usb_intf->altsetting[0];
+ piface_desc = &phost_iface->desc;
+
+ pdvobjpriv->NumInterfaces = pconf_desc->bNumInterfaces;
+ pdvobjpriv->InterfaceNumber = piface_desc->bInterfaceNumber;
+ pdvobjpriv->nr_endpoint = piface_desc->bNumEndpoints;
+
+ for (i = 0; i < pdvobjpriv->nr_endpoint; i++) {
+ pendp_desc = &phost_iface->endpoint[i].desc;
+
+ DBG_8723A("\nusb_endpoint_descriptor(%d):\n", i);
+ DBG_8723A("bLength =%x\n", pendp_desc->bLength);
+ DBG_8723A("bDescriptorType =%x\n", pendp_desc->bDescriptorType);
+ DBG_8723A("bEndpointAddress =%x\n",
+ pendp_desc->bEndpointAddress);
+ DBG_8723A("wMaxPacketSize =%d\n",
+ le16_to_cpu(pendp_desc->wMaxPacketSize));
+ DBG_8723A("bInterval =%x\n", pendp_desc->bInterval);
+
+ if (usb_endpoint_is_bulk_in(pendp_desc)) {
+ DBG_8723A("usb_endpoint_is_bulk_in = %x\n",
+ usb_endpoint_num(pendp_desc));
+ pdvobjpriv->RtInPipe[pdvobjpriv->RtNumInPipes] =
+ usb_endpoint_num(pendp_desc);
+ pdvobjpriv->RtNumInPipes++;
+ } else if (usb_endpoint_is_int_in(pendp_desc)) {
+ DBG_8723A("usb_endpoint_is_int_in = %x, Interval = "
+ "%x\n", usb_endpoint_num(pendp_desc),
+ pendp_desc->bInterval);
+ pdvobjpriv->RtInPipe[pdvobjpriv->RtNumInPipes] =
+ usb_endpoint_num(pendp_desc);
+ pdvobjpriv->RtNumInPipes++;
+ } else if (usb_endpoint_is_bulk_out(pendp_desc)) {
+ DBG_8723A("usb_endpoint_is_bulk_out = %x\n",
+ usb_endpoint_num(pendp_desc));
+ pdvobjpriv->RtOutPipe[pdvobjpriv->RtNumOutPipes] =
+ usb_endpoint_num(pendp_desc);
+ pdvobjpriv->RtNumOutPipes++;
+ }
+ pdvobjpriv->ep_num[i] = usb_endpoint_num(pendp_desc);
+ }
+ DBG_8723A("nr_endpoint =%d, in_num =%d, out_num =%d\n\n",
+ pdvobjpriv->nr_endpoint, pdvobjpriv->RtNumInPipes,
+ pdvobjpriv->RtNumOutPipes);
+
+ if (pusbd->speed == USB_SPEED_HIGH) {
+ pdvobjpriv->ishighspeed = true;
+ DBG_8723A("USB_SPEED_HIGH\n");
+ } else {
+ pdvobjpriv->ishighspeed = false;
+ DBG_8723A("NON USB_SPEED_HIGH\n");
+ }
+
+ if (rtw_init_intf_priv(pdvobjpriv) == _FAIL) {
+ RT_TRACE(_module_os_intfs_c_, _drv_err_,
+ "Can't INIT rtw_init_intf_priv\n");
+ goto free_dvobj;
+ }
+ /* 3 misc */
+ rtw_reset_continual_urb_error(pdvobjpriv);
+ usb_get_dev(pusbd);
+ status = _SUCCESS;
+free_dvobj:
+ if (status != _SUCCESS && pdvobjpriv) {
+ usb_set_intfdata(usb_intf, NULL);
+ mutex_destroy(&pdvobjpriv->hw_init_mutex);
+ mutex_destroy(&pdvobjpriv->h2c_fwcmd_mutex);
+ mutex_destroy(&pdvobjpriv->setch_mutex);
+ mutex_destroy(&pdvobjpriv->setbw_mutex);
+ kfree(pdvobjpriv);
+ pdvobjpriv = NULL;
+ }
+exit:
+ return pdvobjpriv;
+}
+
+static void usb_dvobj_deinit(struct usb_interface *usb_intf)
+{
+ struct dvobj_priv *dvobj = usb_get_intfdata(usb_intf);
+
+ usb_set_intfdata(usb_intf, NULL);
+ if (dvobj) {
+ /* Modify condition for 92DU DMDP 2010.11.18, by Thomas */
+ if ((dvobj->NumInterfaces != 2 && dvobj->NumInterfaces != 3) ||
+ (dvobj->InterfaceNumber == 1)) {
+ if (interface_to_usbdev(usb_intf)->state !=
+ USB_STATE_NOTATTACHED) {
+ /* If we didn't unplug usb dongle and
+ * remove/insert module, driver fails on
+ * sitesurvey for the first time when
+ * device is up .
+ * Reset usb port for sitesurvey fail issue.
+ */
+ DBG_8723A("usb attached..., try to reset usb device\n");
+ usb_reset_device(interface_to_usbdev(usb_intf));
+ }
+ }
+ rtw_deinit_intf_priv(dvobj);
+ mutex_destroy(&dvobj->hw_init_mutex);
+ mutex_destroy(&dvobj->h2c_fwcmd_mutex);
+ mutex_destroy(&dvobj->setch_mutex);
+ mutex_destroy(&dvobj->setbw_mutex);
+ kfree(dvobj);
+ }
+ usb_put_dev(interface_to_usbdev(usb_intf));
+}
+
+void rtl8723a_usb_intf_stop(struct rtw_adapter *padapter)
+{
+ RT_TRACE(_module_hci_intfs_c_, _drv_err_, "+usb_intf_stop\n");
+
+ /* disable_hw_interrupt */
+ if (!padapter->bSurpriseRemoved) {
+ /* device still exists, so driver can do i/o operation
+ * TODO:
+ */
+ RT_TRACE(_module_hci_intfs_c_, _drv_err_,
+ "SurpriseRemoved == false\n");
+ }
+
+ /* cancel in irp */
+ rtl8723au_inirp_deinit(padapter);
+
+ /* cancel out irp */
+ rtl8723au_write_port_cancel(padapter);
+
+ /* todo:cancel other irps */
+ RT_TRACE(_module_hci_intfs_c_, _drv_err_, "-usb_intf_stop\n");
+}
+
+static void rtw_dev_unload(struct rtw_adapter *padapter)
+{
+ struct submit_ctx *pack_tx_ops = &padapter->xmitpriv.ack_tx_ops;
+
+ RT_TRACE(_module_hci_intfs_c_, _drv_err_, "+rtw_dev_unload\n");
+
+ if (padapter->bup) {
+ DBG_8723A("===> rtw_dev_unload\n");
+
+ padapter->bDriverStopped = true;
+ if (padapter->xmitpriv.ack_tx)
+ rtw23a_sctx_done_err(&pack_tx_ops,
+ RTW_SCTX_DONE_DRV_STOP);
+
+ /* s3. */
+ rtl8723a_usb_intf_stop(padapter);
+
+ /* s4. */
+ flush_workqueue(padapter->cmdpriv.wq);
+
+ /* s5. */
+ if (!padapter->bSurpriseRemoved) {
+ rtl8723au_hal_deinit(padapter);
+ padapter->bSurpriseRemoved = true;
+ }
+ padapter->bup = false;
+ } else {
+ RT_TRACE(_module_hci_intfs_c_, _drv_err_,
+ "r871x_dev_unload():padapter->bup == false\n");
+ }
+ DBG_8723A("<=== rtw_dev_unload\n");
+ RT_TRACE(_module_hci_intfs_c_, _drv_err_, "-rtw_dev_unload\n");
+}
+
+static int rtw_suspend(struct usb_interface *pusb_intf, pm_message_t message)
+{
+ struct dvobj_priv *dvobj = usb_get_intfdata(pusb_intf);
+ struct rtw_adapter *padapter = dvobj->if1;
+ struct net_device *pnetdev = padapter->pnetdev;
+ struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
+ struct pwrctrl_priv *pwrpriv = &padapter->pwrctrlpriv;
+ int ret = 0;
+ unsigned long start_time = jiffies;
+
+ DBG_8723A("==> %s (%s:%d)\n", __func__, current->comm, current->pid);
+
+ if ((!padapter->bup) || (padapter->bDriverStopped) ||
+ (padapter->bSurpriseRemoved)) {
+ DBG_8723A("padapter->bup =%d bDriverStopped =%d bSurpriseRemoved = %d\n",
+ padapter->bup, padapter->bDriverStopped,
+ padapter->bSurpriseRemoved);
+ goto exit;
+ }
+ pwrpriv->bInSuspend = true;
+ rtw_cancel_all_timer23a(padapter);
+ LeaveAllPowerSaveMode23a(padapter);
+
+ down(&pwrpriv->lock);
+ /* padapter->net_closed = true; */
+ /* s1. */
+ if (pnetdev) {
+ netif_carrier_off(pnetdev);
+ netif_tx_stop_all_queues(pnetdev);
+ }
+
+ /* s2. */
+ rtw_disassoc_cmd23a(padapter, 0, false);
+
+ if (check_fwstate(pmlmepriv, WIFI_STATION_STATE) &&
+ check_fwstate(pmlmepriv, _FW_LINKED)) {
+ DBG_8723A("%s:%d %s(%pM), length:%d assoc_ssid.length:%d\n",
+ __func__, __LINE__,
+ pmlmepriv->cur_network.network.Ssid.ssid,
+ pmlmepriv->cur_network.network.MacAddress,
+ pmlmepriv->cur_network.network.Ssid.ssid_len,
+ pmlmepriv->assoc_ssid.ssid_len);
+
+ rtw_set_roaming(padapter, 1);
+ }
+ /* s2-2. indicate disconnect to os */
+ rtw_indicate_disconnect23a(padapter);
+ /* s2-3. */
+ rtw_free_assoc_resources23a(padapter, 1);
+ /* s2-4. */
+ rtw_free_network_queue23a(padapter);
+
+ rtw_dev_unload(padapter);
+ up(&pwrpriv->lock);
+
+ if (check_fwstate(pmlmepriv, _FW_UNDER_SURVEY))
+ rtw_cfg80211_indicate_scan_done(
+ wdev_to_priv(padapter->rtw_wdev), true);
+
+ if (check_fwstate(pmlmepriv, _FW_UNDER_LINKING))
+ rtw_indicate_disconnect23a(padapter);
+
+exit:
+ DBG_8723A("<=== %s return %d.............. in %dms\n", __func__,
+ ret, jiffies_to_msecs(jiffies - start_time));
+
+ return ret;
+}
+
+static int rtw_resume(struct usb_interface *pusb_intf)
+{
+ struct dvobj_priv *dvobj = usb_get_intfdata(pusb_intf);
+ struct rtw_adapter *padapter = dvobj->if1;
+ struct net_device *pnetdev;
+ struct pwrctrl_priv *pwrpriv = NULL;
+ int ret = -1;
+ unsigned long start_time = jiffies;
+
+ DBG_8723A("==> %s (%s:%d)\n", __func__, current->comm, current->pid);
+
+ if (!padapter)
+ goto exit;
+ pnetdev = padapter->pnetdev;
+ pwrpriv = &padapter->pwrctrlpriv;
+
+ down(&pwrpriv->lock);
+ rtw_reset_drv_sw23a(padapter);
+ pwrpriv->bkeepfwalive = false;
+
+ DBG_8723A("bkeepfwalive(%x)\n", pwrpriv->bkeepfwalive);
+ if (pm_netdev_open23a(pnetdev, true) != 0) {
+ up(&pwrpriv->lock);
+ goto exit;
+ }
+
+ netif_device_attach(pnetdev);
+ netif_carrier_on(pnetdev);
+
+ up(&pwrpriv->lock);
+
+ if (padapter->pid[1] != 0) {
+ DBG_8723A("pid[1]:%d\n", padapter->pid[1]);
+ kill_pid(find_vpid(padapter->pid[1]), SIGUSR2, 1);
+ }
+
+ rtw23a_roaming(padapter, NULL);
+
+ ret = 0;
+exit:
+ if (pwrpriv)
+ pwrpriv->bInSuspend = false;
+ DBG_8723A("<=== %s return %d.............. in %dms\n", __func__,
+ ret, jiffies_to_msecs(jiffies - start_time));
+
+ return ret;
+}
+
+/*
+ * drv_init() - a device potentially for us
+ *
+ * notes: drv_init() is called when the bus driver has located a card
+ * for us to support.
+ * We accept the new device by returning 0.
+ */
+static struct rtw_adapter *rtw_usb_if1_init(struct dvobj_priv *dvobj,
+ struct usb_interface *pusb_intf,
+ const struct usb_device_id *pdid)
+{
+ struct rtw_adapter *padapter = NULL;
+ struct net_device *pnetdev = NULL;
+ int status = _FAIL;
+
+ pnetdev = rtw_init_netdev23a(padapter);
+ if (!pnetdev)
+ goto free_adapter;
+ padapter = netdev_priv(pnetdev);
+
+ padapter->dvobj = dvobj;
+ padapter->bDriverStopped = true;
+ dvobj->if1 = padapter;
+ dvobj->padapters[dvobj->iface_nums++] = padapter;
+ padapter->iface_id = IFACE_ID0;
+
+ rtl8723au_set_hw_type(padapter);
+
+ SET_NETDEV_DEV(pnetdev, dvobj_to_dev(dvobj));
+
+ if (rtw_wdev_alloc(padapter, dvobj_to_dev(dvobj)))
+ goto free_adapter;
+
+ /* step 2. allocate HalData */
+ padapter->HalData = kzalloc(sizeof(struct hal_data_8723a), GFP_KERNEL);
+ if (!padapter->HalData)
+ goto free_wdev;
+
+ /* step read_chip_version */
+ rtl8723a_read_chip_version(padapter);
+
+ /* step usb endpoint mapping */
+ if (!rtl8723au_chip_configure(padapter))
+ goto free_hal_data;
+
+ /* step read efuse/eeprom data and get mac_addr */
+ rtl8723a_read_adapter_info(padapter);
+
+ /* step 5. */
+ if (rtw_init_drv_sw23a(padapter) == _FAIL) {
+ RT_TRACE(_module_hci_intfs_c_, _drv_err_,
+ "Initialize driver software resource Failed!\n");
+ goto free_hal_data;
+ }
+
+#ifdef CONFIG_PM
+ if (padapter->pwrctrlpriv.bSupportRemoteWakeup) {
+ dvobj->pusbdev->do_remote_wakeup = 1;
+ pusb_intf->needs_remote_wakeup = 1;
+ device_init_wakeup(&pusb_intf->dev, 1);
+ DBG_8723A("\n padapter->pwrctrlpriv.bSupportRemoteWakeup~~~~~~\n");
+ DBG_8723A("\n padapter->pwrctrlpriv.bSupportRemoteWakeup~~~[%d]~~~\n",
+ device_may_wakeup(&pusb_intf->dev));
+ }
+#endif
+ /* 2012-07-11 Move here to prevent the 8723AS-VAU BT
+ * auto suspend influence
+ */
+ if (usb_autopm_get_interface(pusb_intf) < 0)
+ DBG_8723A("can't get autopm:\n");
+#ifdef CONFIG_8723AU_BT_COEXIST
+ padapter->pwrctrlpriv.autopm_cnt = 1;
+#endif
+
+ /* If the eeprom mac address is corrupted, assign a random address */
+ if (is_broadcast_ether_addr(padapter->eeprompriv.mac_addr) ||
+ is_zero_ether_addr(padapter->eeprompriv.mac_addr))
+ eth_random_addr(padapter->eeprompriv.mac_addr);
+
+ DBG_8723A("bDriverStopped:%d, bSurpriseRemoved:%d, bup:%d, hw_init_completed:%d\n",
+ padapter->bDriverStopped, padapter->bSurpriseRemoved,
+ padapter->bup, padapter->hw_init_completed
+ );
+ status = _SUCCESS;
+
+free_hal_data:
+ if (status != _SUCCESS)
+ kfree(padapter->HalData);
+free_wdev:
+ if (status != _SUCCESS) {
+ rtw_wdev_unregister(padapter->rtw_wdev);
+ rtw_wdev_free(padapter->rtw_wdev);
+ }
+free_adapter:
+ if (status != _SUCCESS) {
+ if (pnetdev)
+ free_netdev(pnetdev);
+ padapter = NULL;
+ }
+ return padapter;
+}
+
+static void rtw_usb_if1_deinit(struct rtw_adapter *if1)
+{
+ struct net_device *pnetdev = if1->pnetdev;
+ struct mlme_priv *pmlmepriv = &if1->mlmepriv;
+
+ if (check_fwstate(pmlmepriv, _FW_LINKED))
+ rtw_disassoc_cmd23a(if1, 0, false);
+
+#ifdef CONFIG_8723AU_AP_MODE
+ free_mlme_ap_info23a(if1);
+#endif
+
+ if (pnetdev)
+ unregister_netdev(pnetdev); /* will call netdev_close() */
+
+ rtw_cancel_all_timer23a(if1);
+
+ rtw_dev_unload(if1);
+
+ DBG_8723A("+r871xu_dev_remove, hw_init_completed =%d\n",
+ if1->hw_init_completed);
+
+ if (if1->rtw_wdev) {
+ rtw_wdev_unregister(if1->rtw_wdev);
+ rtw_wdev_free(if1->rtw_wdev);
+ }
+
+#ifdef CONFIG_8723AU_BT_COEXIST
+ if (1 == if1->pwrctrlpriv.autopm_cnt) {
+ usb_autopm_put_interface(adapter_to_dvobj(if1)->pusbintf);
+ if1->pwrctrlpriv.autopm_cnt--;
+ }
+#endif
+
+ rtw_free_drv_sw23a(if1);
+
+ if (pnetdev)
+ free_netdev(pnetdev);
+}
+
+static int rtw_drv_init(struct usb_interface *pusb_intf,
+ const struct usb_device_id *pdid)
+{
+ struct rtw_adapter *if1 = NULL;
+ struct dvobj_priv *dvobj;
+ int status = _FAIL;
+
+ RT_TRACE(_module_hci_intfs_c_, _drv_err_, "+rtw_drv_init\n");
+
+ /* Initialize dvobj_priv */
+ dvobj = usb_dvobj_init(pusb_intf);
+ if (!dvobj) {
+ RT_TRACE(_module_hci_intfs_c_, _drv_err_,
+ "initialize device object priv Failed!\n");
+ goto exit;
+ }
+
+ if1 = rtw_usb_if1_init(dvobj, pusb_intf, pdid);
+ if (!if1) {
+ DBG_8723A("rtw_init_primary_adapter Failed!\n");
+ goto free_dvobj;
+ }
+
+ /* dev_alloc_name && register_netdev */
+ status = rtw_drv_register_netdev(if1);
+ if (status != _SUCCESS)
+ goto free_if1;
+ RT_TRACE(_module_hci_intfs_c_, _drv_err_,
+ "-871x_drv - drv_init, success!\n");
+
+ status = _SUCCESS;
+
+free_if1:
+ if (status != _SUCCESS && if1)
+ rtw_usb_if1_deinit(if1);
+free_dvobj:
+ if (status != _SUCCESS)
+ usb_dvobj_deinit(pusb_intf);
+exit:
+ return status == _SUCCESS ? 0 : -ENODEV;
+}
+
+/* dev_remove() - our device is being removed */
+static void rtw_disconnect(struct usb_interface *pusb_intf)
+{
+ struct dvobj_priv *dvobj;
+ struct rtw_adapter *padapter;
+ struct net_device *pnetdev;
+ struct mlme_priv *pmlmepriv;
+
+ dvobj = usb_get_intfdata(pusb_intf);
+ if (!dvobj)
+ return;
+
+ padapter = dvobj->if1;
+ pnetdev = padapter->pnetdev;
+ pmlmepriv = &padapter->mlmepriv;
+
+ usb_set_intfdata(pusb_intf, NULL);
+
+ RT_TRACE(_module_hci_intfs_c_, _drv_err_, "+dev_remove()\n");
+
+ rtw_pm_set_ips23a(padapter, IPS_NONE);
+ rtw_pm_set_lps23a(padapter, PS_MODE_ACTIVE);
+
+ LeaveAllPowerSaveMode23a(padapter);
+
+ rtw_usb_if1_deinit(padapter);
+
+ usb_dvobj_deinit(pusb_intf);
+
+ RT_TRACE(_module_hci_intfs_c_, _drv_err_, "-dev_remove()\n");
+ DBG_8723A("-r871xu_dev_remove, done\n");
+}
+
+static int __init rtw_drv_entry(void)
+{
+ RT_TRACE(_module_hci_intfs_c_, _drv_err_, "+rtw_drv_entry\n");
+ return usb_register(usb_drv);
+}
+
+static void __exit rtw_drv_halt(void)
+{
+ RT_TRACE(_module_hci_intfs_c_, _drv_err_, "+rtw_drv_halt\n");
+ DBG_8723A("+rtw_drv_halt\n");
+
+ usb_deregister(usb_drv);
+
+ DBG_8723A("-rtw_drv_halt\n");
+}
+
+module_init(rtw_drv_entry);
+module_exit(rtw_drv_halt);
diff --git a/drivers/staging/rtl8723au/os_dep/usb_ops_linux.c b/drivers/staging/rtl8723au/os_dep/usb_ops_linux.c
new file mode 100644
index 000000000..0cdaef0a8
--- /dev/null
+++ b/drivers/staging/rtl8723au/os_dep/usb_ops_linux.c
@@ -0,0 +1,234 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2012 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ ******************************************************************************/
+#define _USB_OPS_LINUX_C_
+
+#include <drv_types.h>
+#include <usb_ops_linux.h>
+#include <rtw_sreset.h>
+
+void rtl8723au_read_port_cancel(struct rtw_adapter *padapter)
+{
+ struct recv_buf *precvbuf;
+ int i;
+
+ precvbuf = (struct recv_buf *)padapter->recvpriv.precv_buf;
+
+ DBG_8723A("%s\n", __func__);
+
+ padapter->bReadPortCancel = true;
+
+ for (i = 0; i < NR_RECVBUFF ; i++) {
+ if (precvbuf->purb)
+ usb_kill_urb(precvbuf->purb);
+ precvbuf++;
+ }
+ usb_kill_urb(padapter->recvpriv.int_in_urb);
+}
+
+static void usb_write_port23a_complete(struct urb *purb)
+{
+ struct xmit_buf *pxmitbuf = (struct xmit_buf *)purb->context;
+ struct rtw_adapter *padapter = pxmitbuf->padapter;
+ struct xmit_priv *pxmitpriv = &padapter->xmitpriv;
+ struct hal_data_8723a *phaldata;
+ unsigned long irqL;
+
+ switch (pxmitbuf->flags) {
+ case HIGH_QUEUE_INX:
+#ifdef CONFIG_8723AU_AP_MODE
+ rtw_chk_hi_queue_cmd23a(padapter);
+#endif
+ break;
+ default:
+ break;
+ }
+
+ if (padapter->bSurpriseRemoved || padapter->bDriverStopped ||
+ padapter->bWritePortCancel) {
+ RT_TRACE(_module_hci_ops_os_c_, _drv_err_,
+ "usb_write_port23a_complete:bDriverStopped(%d) OR bSurpriseRemoved(%d)\n",
+ padapter->bDriverStopped, padapter->bSurpriseRemoved);
+ DBG_8723A("%s(): TX Warning! bDriverStopped(%d) OR "
+ "bSurpriseRemoved(%d) bWritePortCancel(%d) "
+ "pxmitbuf->ext_tag(%x)\n", __func__,
+ padapter->bDriverStopped, padapter->bSurpriseRemoved,
+ padapter->bReadPortCancel, pxmitbuf->ext_tag);
+
+ goto check_completion;
+ }
+
+ if (purb->status) {
+ RT_TRACE(_module_hci_ops_os_c_, _drv_err_,
+ "usb_write_port23a_complete : purb->status(%d) != 0\n",
+ purb->status);
+ DBG_8723A("###=> urb_write_port_complete status(%d)\n",
+ purb->status);
+ if (purb->status == -EPIPE || purb->status == -EPROTO) {
+ } else if (purb->status == -EINPROGRESS) {
+ RT_TRACE(_module_hci_ops_os_c_, _drv_err_,
+ "usb_write_port23a_complete: EINPROGESS\n");
+ goto check_completion;
+ } else if (purb->status == -ENOENT) {
+ DBG_8723A("%s: -ENOENT\n", __func__);
+ goto check_completion;
+ } else if (purb->status == -ECONNRESET) {
+ DBG_8723A("%s: -ECONNRESET\n", __func__);
+ goto check_completion;
+ } else if (purb->status == -ESHUTDOWN) {
+ RT_TRACE(_module_hci_ops_os_c_, _drv_err_,
+ "usb_write_port23a_complete: ESHUTDOWN\n");
+ padapter->bDriverStopped = true;
+ RT_TRACE(_module_hci_ops_os_c_, _drv_err_,
+ "usb_write_port23a_complete:bDriverStopped = true\n");
+ goto check_completion;
+ } else {
+ padapter->bSurpriseRemoved = true;
+ DBG_8723A("bSurpriseRemoved = true\n");
+ RT_TRACE(_module_hci_ops_os_c_, _drv_err_,
+ "usb_write_port23a_complete:bSurpriseRemoved = true\n");
+ goto check_completion;
+ }
+ }
+ phaldata = GET_HAL_DATA(padapter);
+ phaldata->srestpriv.last_tx_complete_time = jiffies;
+
+check_completion:
+ spin_lock_irqsave(&pxmitpriv->lock_sctx, irqL);
+ rtw23a_sctx_done_err(&pxmitbuf->sctx,
+ purb->status ? RTW_SCTX_DONE_WRITE_PORT_ERR :
+ RTW_SCTX_DONE_SUCCESS);
+ spin_unlock_irqrestore(&pxmitpriv->lock_sctx, irqL);
+
+ rtw_free_xmitbuf23a(pxmitpriv, pxmitbuf);
+
+ tasklet_hi_schedule(&pxmitpriv->xmit_tasklet);
+}
+
+int rtl8723au_write_port(struct rtw_adapter *padapter, u32 addr, u32 cnt,
+ struct xmit_buf *pxmitbuf)
+{
+ struct urb *purb = NULL;
+ struct dvobj_priv *pdvobj = adapter_to_dvobj(padapter);
+ struct xmit_priv *pxmitpriv = &padapter->xmitpriv;
+ struct xmit_frame *pxmitframe;
+ struct usb_device *pusbd = pdvobj->pusbdev;
+ unsigned long irqL;
+ unsigned int pipe, ep_num;
+ int status;
+ int ret = _FAIL;
+
+ RT_TRACE(_module_hci_ops_os_c_, _drv_err_, "+usb_write_port23a\n");
+
+ if (padapter->bDriverStopped || padapter->bSurpriseRemoved) {
+ RT_TRACE(_module_hci_ops_os_c_, _drv_err_,
+ "%s:(padapter->bDriverStopped || padapter->bSurpriseRemoved)!!!\n",
+ __func__);
+ rtw23a_sctx_done_err(&pxmitbuf->sctx, RTW_SCTX_DONE_TX_DENY);
+ goto exit;
+ }
+
+ pxmitframe = (struct xmit_frame *)pxmitbuf->priv_data;
+ spin_lock_irqsave(&pxmitpriv->lock, irqL);
+
+ switch (addr) {
+ case VO_QUEUE_INX:
+ pxmitbuf->flags = VO_QUEUE_INX;
+ break;
+ case VI_QUEUE_INX:
+ pxmitbuf->flags = VI_QUEUE_INX;
+ break;
+ case BE_QUEUE_INX:
+ pxmitbuf->flags = BE_QUEUE_INX;
+ break;
+ case BK_QUEUE_INX:
+ pxmitbuf->flags = BK_QUEUE_INX;
+ break;
+ case HIGH_QUEUE_INX:
+ pxmitbuf->flags = HIGH_QUEUE_INX;
+ break;
+ default:
+ pxmitbuf->flags = MGT_QUEUE_INX;
+ break;
+ }
+
+ spin_unlock_irqrestore(&pxmitpriv->lock, irqL);
+
+ purb = pxmitbuf->pxmit_urb[0];
+
+ /* translate DMA FIFO addr to pipehandle */
+ ep_num = pdvobj->Queue2Pipe[addr];
+ pipe = usb_sndbulkpipe(pusbd, ep_num);
+
+ usb_fill_bulk_urb(purb, pusbd, pipe,
+ pxmitframe->buf_addr, /* pxmitbuf->pbuf */
+ cnt, usb_write_port23a_complete,
+ pxmitbuf);/* context is pxmitbuf */
+
+ status = usb_submit_urb(purb, GFP_ATOMIC);
+ if (!status) {
+ struct hal_data_8723a *phaldata = GET_HAL_DATA(padapter);
+ phaldata->srestpriv.last_tx_time = jiffies;
+ } else {
+ rtw23a_sctx_done_err(&pxmitbuf->sctx,
+ RTW_SCTX_DONE_WRITE_PORT_ERR);
+ DBG_8723A("usb_write_port23a, status =%d\n", status);
+ RT_TRACE(_module_hci_ops_os_c_, _drv_err_,
+ "usb_write_port23a(): usb_submit_urb, status =%x\n",
+ status);
+
+ switch (status) {
+ case -ENODEV:
+ padapter->bDriverStopped = true;
+ break;
+ default:
+ break;
+ }
+ goto exit;
+ }
+ ret = _SUCCESS;
+ RT_TRACE(_module_hci_ops_os_c_, _drv_err_, "-usb_write_port23a\n");
+
+exit:
+ if (ret != _SUCCESS)
+ rtw_free_xmitbuf23a(pxmitpriv, pxmitbuf);
+
+ return ret;
+}
+
+void rtl8723au_write_port_cancel(struct rtw_adapter *padapter)
+{
+ struct xmit_buf *pxmitbuf;
+ struct list_head *plist;
+ int j;
+
+ DBG_8723A("%s\n", __func__);
+
+ padapter->bWritePortCancel = true;
+
+ list_for_each(plist, &padapter->xmitpriv.xmitbuf_list) {
+ pxmitbuf = container_of(plist, struct xmit_buf, list2);
+ for (j = 0; j < 8; j++) {
+ if (pxmitbuf->pxmit_urb[j])
+ usb_kill_urb(pxmitbuf->pxmit_urb[j]);
+ }
+ }
+ list_for_each(plist, &padapter->xmitpriv.xmitextbuf_list) {
+ pxmitbuf = container_of(plist, struct xmit_buf, list2);
+ for (j = 0; j < 8; j++) {
+ if (pxmitbuf->pxmit_urb[j])
+ usb_kill_urb(pxmitbuf->pxmit_urb[j]);
+ }
+ }
+}
diff --git a/drivers/staging/rtl8723au/os_dep/xmit_linux.c b/drivers/staging/rtl8723au/os_dep/xmit_linux.c
new file mode 100644
index 000000000..9a14074ec
--- /dev/null
+++ b/drivers/staging/rtl8723au/os_dep/xmit_linux.c
@@ -0,0 +1,154 @@
+/******************************************************************************
+ *
+ * Copyright(c) 2007 - 2012 Realtek Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ ******************************************************************************/
+#define _XMIT_OSDEP_C_
+
+#include <osdep_service.h>
+#include <drv_types.h>
+
+#include <linux/if_ether.h>
+#include <linux/ip.h>
+#include <wifi.h>
+#include <mlme_osdep.h>
+#include <xmit_osdep.h>
+#include <osdep_intf.h>
+
+int rtw_os_xmit_resource_alloc23a(struct rtw_adapter *padapter,
+ struct xmit_buf *pxmitbuf, u32 alloc_sz)
+{
+ int i;
+
+ pxmitbuf->pallocated_buf = kzalloc(alloc_sz, GFP_KERNEL);
+ if (pxmitbuf->pallocated_buf == NULL)
+ return _FAIL;
+
+ pxmitbuf->pbuf = PTR_ALIGN(pxmitbuf->pallocated_buf, XMITBUF_ALIGN_SZ);
+
+ for (i = 0; i < 8; i++) {
+ pxmitbuf->pxmit_urb[i] = usb_alloc_urb(0, GFP_KERNEL);
+ if (pxmitbuf->pxmit_urb[i] == NULL) {
+ DBG_8723A("pxmitbuf->pxmit_urb[i]==NULL");
+ return _FAIL;
+ }
+ }
+ return _SUCCESS;
+}
+
+void rtw_os_xmit_resource_free23a(struct rtw_adapter *padapter,
+ struct xmit_buf *pxmitbuf)
+{
+ int i;
+
+ for (i = 0; i < 8; i++)
+ usb_free_urb(pxmitbuf->pxmit_urb[i]);
+ kfree(pxmitbuf->pallocated_buf);
+}
+
+#define WMM_XMIT_THRESHOLD (NR_XMITFRAME*2/5)
+
+void rtw_os_pkt_complete23a(struct rtw_adapter *padapter, struct sk_buff *pkt)
+{
+ struct xmit_priv *pxmitpriv = &padapter->xmitpriv;
+ u16 queue;
+
+ queue = skb_get_queue_mapping(pkt);
+ if (padapter->registrypriv.wifi_spec) {
+ if (__netif_subqueue_stopped(padapter->pnetdev, queue) &&
+ (pxmitpriv->hwxmits[queue].accnt < WMM_XMIT_THRESHOLD))
+ netif_wake_subqueue(padapter->pnetdev, queue);
+ } else {
+ if (__netif_subqueue_stopped(padapter->pnetdev, queue))
+ netif_wake_subqueue(padapter->pnetdev, queue);
+ }
+ dev_kfree_skb_any(pkt);
+}
+
+void rtw_os_xmit_complete23a(struct rtw_adapter *padapter,
+ struct xmit_frame *pxframe)
+{
+ if (pxframe->pkt)
+ rtw_os_pkt_complete23a(padapter, pxframe->pkt);
+
+ pxframe->pkt = NULL;
+}
+
+void rtw_os_xmit_schedule23a(struct rtw_adapter *padapter)
+{
+ struct xmit_priv *pxmitpriv;
+
+ if (!padapter)
+ return;
+ pxmitpriv = &padapter->xmitpriv;
+
+ spin_lock_bh(&pxmitpriv->lock);
+
+ if (rtw_txframes_pending23a(padapter))
+ tasklet_hi_schedule(&pxmitpriv->xmit_tasklet);
+ spin_unlock_bh(&pxmitpriv->lock);
+}
+
+static void rtw_check_xmit_resource(struct rtw_adapter *padapter,
+ struct sk_buff *pkt)
+{
+ struct xmit_priv *pxmitpriv = &padapter->xmitpriv;
+ u16 queue;
+
+ queue = skb_get_queue_mapping(pkt);
+ if (padapter->registrypriv.wifi_spec) {
+ /* No free space for Tx, tx_worker is too slow */
+ if (pxmitpriv->hwxmits[queue].accnt > WMM_XMIT_THRESHOLD)
+ netif_stop_subqueue(padapter->pnetdev, queue);
+ } else {
+ if (pxmitpriv->free_xmitframe_cnt <= 4) {
+ if (!netif_tx_queue_stopped(netdev_get_tx_queue(padapter->pnetdev, queue)))
+ netif_stop_subqueue(padapter->pnetdev, queue);
+ }
+ }
+}
+
+int rtw_xmit23a_entry23a(struct sk_buff *skb, struct net_device *pnetdev)
+{
+ struct rtw_adapter *padapter = netdev_priv(pnetdev);
+ struct xmit_priv *pxmitpriv = &padapter->xmitpriv;
+ int res = 0;
+
+ RT_TRACE(_module_rtl871x_mlme_c_, _drv_info_, "+xmit_enry\n");
+
+ if (!rtw_if_up23a(padapter)) {
+ RT_TRACE(_module_xmit_osdep_c_, _drv_err_,
+ "rtw_xmit23a_entry23a: rtw_if_up23a fail\n");
+ goto drop_packet;
+ }
+
+ rtw_check_xmit_resource(padapter, skb);
+
+ res = rtw_xmit23a(padapter, skb);
+ if (res < 0)
+ goto drop_packet;
+
+ pxmitpriv->tx_pkts++;
+ RT_TRACE(_module_xmit_osdep_c_, _drv_info_,
+ "rtw_xmit23a_entry23a: tx_pkts=%d\n",
+ (u32)pxmitpriv->tx_pkts);
+ goto exit;
+
+drop_packet:
+ pxmitpriv->tx_drop++;
+ dev_kfree_skb_any(skb);
+ RT_TRACE(_module_xmit_osdep_c_, _drv_notice_,
+ "rtw_xmit23a_entry23a: drop, tx_drop=%d\n",
+ (u32)pxmitpriv->tx_drop);
+exit:
+ return 0;
+}
diff --git a/drivers/staging/rts5208/Kconfig b/drivers/staging/rts5208/Kconfig
new file mode 100644
index 000000000..05c990f65
--- /dev/null
+++ b/drivers/staging/rts5208/Kconfig
@@ -0,0 +1,8 @@
+config RTS5208
+ tristate "Realtek PCI-E Card Reader RTS5208/5288 support"
+ depends on PCI && SCSI
+ help
+ Say Y here to include driver code to support the Realtek
+ PCI-E card reader rts5208/rts5288.
+
+ If this driver is compiled as a module, it will be named rts5208.
diff --git a/drivers/staging/rts5208/Makefile b/drivers/staging/rts5208/Makefile
new file mode 100644
index 000000000..f7fd03a94
--- /dev/null
+++ b/drivers/staging/rts5208/Makefile
@@ -0,0 +1,6 @@
+obj-$(CONFIG_RTS5208) := rts5208.o
+
+ccflags-y := -Idrivers/scsi
+
+rts5208-y := rtsx.o rtsx_chip.o rtsx_transport.o rtsx_scsi.o \
+ rtsx_card.o general.o sd.o xd.o ms.o spi.o trace.o
diff --git a/drivers/staging/rts5208/TODO b/drivers/staging/rts5208/TODO
new file mode 100644
index 000000000..57bcf5834
--- /dev/null
+++ b/drivers/staging/rts5208/TODO
@@ -0,0 +1,7 @@
+TODO:
+- use kernel coding style
+- checkpatch.pl fixes
+- We will use the stack in drivers/mmc to implement
+ rts5208/5288 in the future
+
+Micky Ching <micky_ching@realsil.com.cn> \ No newline at end of file
diff --git a/drivers/staging/rts5208/general.c b/drivers/staging/rts5208/general.c
new file mode 100644
index 000000000..79d245877
--- /dev/null
+++ b/drivers/staging/rts5208/general.c
@@ -0,0 +1,36 @@
+/* Driver for Realtek PCI-Express card reader
+ *
+ * Copyright(c) 2009-2013 Realtek Semiconductor Corp. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, 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, see <http://www.gnu.org/licenses/>.
+ *
+ * Author:
+ * Wei WANG (wei_wang@realsil.com.cn)
+ * Micky Ching (micky_ching@realsil.com.cn)
+ */
+
+#include "general.h"
+
+int bit1cnt_long(u32 data)
+{
+ int i, cnt = 0;
+
+ for (i = 0; i < 32; i++) {
+ if (data & 0x01)
+ cnt++;
+ data >>= 1;
+ }
+ return cnt;
+}
+
diff --git a/drivers/staging/rts5208/general.h b/drivers/staging/rts5208/general.h
new file mode 100644
index 000000000..90a1f9297
--- /dev/null
+++ b/drivers/staging/rts5208/general.h
@@ -0,0 +1,31 @@
+/* Driver for Realtek PCI-Express card reader
+ * Header file
+ *
+ * Copyright(c) 2009-2013 Realtek Semiconductor Corp. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, 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, see <http://www.gnu.org/licenses/>.
+ *
+ * Author:
+ * Wei WANG (wei_wang@realsil.com.cn)
+ * Micky Ching (micky_ching@realsil.com.cn)
+ */
+
+#ifndef __RTSX_GENERAL_H
+#define __RTSX_GENERAL_H
+
+#include "rtsx.h"
+
+int bit1cnt_long(u32 data);
+
+#endif /* __RTSX_GENERAL_H */
diff --git a/drivers/staging/rts5208/ms.c b/drivers/staging/rts5208/ms.c
new file mode 100644
index 000000000..ee818b0dc
--- /dev/null
+++ b/drivers/staging/rts5208/ms.c
@@ -0,0 +1,4810 @@
+/* Driver for Realtek PCI-Express card reader
+ *
+ * Copyright(c) 2009-2013 Realtek Semiconductor Corp. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, 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, see <http://www.gnu.org/licenses/>.
+ *
+ * Author:
+ * Wei WANG (wei_wang@realsil.com.cn)
+ * Micky Ching (micky_ching@realsil.com.cn)
+ */
+
+#include <linux/blkdev.h>
+#include <linux/kthread.h>
+#include <linux/sched.h>
+#include <linux/vmalloc.h>
+
+#include "rtsx.h"
+#include "ms.h"
+
+static inline void ms_set_err_code(struct rtsx_chip *chip, u8 err_code)
+{
+ struct ms_info *ms_card = &(chip->ms_card);
+
+ ms_card->err_code = err_code;
+}
+
+static inline int ms_check_err_code(struct rtsx_chip *chip, u8 err_code)
+{
+ struct ms_info *ms_card = &(chip->ms_card);
+
+ return (ms_card->err_code == err_code);
+}
+
+static int ms_parse_err_code(struct rtsx_chip *chip)
+{
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+}
+
+static int ms_transfer_tpc(struct rtsx_chip *chip, u8 trans_mode,
+ u8 tpc, u8 cnt, u8 cfg)
+{
+ struct ms_info *ms_card = &(chip->ms_card);
+ int retval;
+ u8 *ptr;
+
+ dev_dbg(rtsx_dev(chip), "%s: tpc = 0x%x\n", __func__, tpc);
+
+ rtsx_init_cmd(chip);
+
+ rtsx_add_cmd(chip, WRITE_REG_CMD, MS_TPC, 0xFF, tpc);
+ rtsx_add_cmd(chip, WRITE_REG_CMD, MS_BYTE_CNT, 0xFF, cnt);
+ rtsx_add_cmd(chip, WRITE_REG_CMD, MS_TRANS_CFG, 0xFF, cfg);
+ rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_DATA_SOURCE,
+ 0x01, PINGPONG_BUFFER);
+
+ rtsx_add_cmd(chip, WRITE_REG_CMD, MS_TRANSFER,
+ 0xFF, MS_TRANSFER_START | trans_mode);
+ rtsx_add_cmd(chip, CHECK_REG_CMD, MS_TRANSFER,
+ MS_TRANSFER_END, MS_TRANSFER_END);
+
+ rtsx_add_cmd(chip, READ_REG_CMD, MS_TRANS_CFG, 0, 0);
+
+ retval = rtsx_send_cmd(chip, MS_CARD, 5000);
+ if (retval < 0) {
+ rtsx_clear_ms_error(chip);
+ ms_set_err_code(chip, MS_TO_ERROR);
+ rtsx_trace(chip);
+ return ms_parse_err_code(chip);
+ }
+
+ ptr = rtsx_get_cmd_data(chip) + 1;
+
+ if (!(tpc & 0x08)) { /* Read Packet */
+ if (*ptr & MS_CRC16_ERR) {
+ ms_set_err_code(chip, MS_CRC16_ERROR);
+ rtsx_trace(chip);
+ return ms_parse_err_code(chip);
+ }
+ } else { /* Write Packet */
+ if (CHK_MSPRO(ms_card) && !(*ptr & 0x80)) {
+ if (*ptr & (MS_INT_ERR | MS_INT_CMDNK)) {
+ ms_set_err_code(chip, MS_CMD_NK);
+ rtsx_trace(chip);
+ return ms_parse_err_code(chip);
+ }
+ }
+ }
+
+ if (*ptr & MS_RDY_TIMEOUT) {
+ rtsx_clear_ms_error(chip);
+ ms_set_err_code(chip, MS_TO_ERROR);
+ rtsx_trace(chip);
+ return ms_parse_err_code(chip);
+ }
+
+ return STATUS_SUCCESS;
+}
+
+static int ms_transfer_data(struct rtsx_chip *chip, u8 trans_mode,
+ u8 tpc, u16 sec_cnt, u8 cfg, bool mode_2k,
+ int use_sg, void *buf, int buf_len)
+{
+ int retval;
+ u8 val, err_code = 0;
+ enum dma_data_direction dir;
+
+ if (!buf || !buf_len) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ if (trans_mode == MS_TM_AUTO_READ) {
+ dir = DMA_FROM_DEVICE;
+ err_code = MS_FLASH_READ_ERROR;
+ } else if (trans_mode == MS_TM_AUTO_WRITE) {
+ dir = DMA_TO_DEVICE;
+ err_code = MS_FLASH_WRITE_ERROR;
+ } else {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ rtsx_init_cmd(chip);
+
+ rtsx_add_cmd(chip, WRITE_REG_CMD, MS_TPC, 0xFF, tpc);
+ rtsx_add_cmd(chip, WRITE_REG_CMD,
+ MS_SECTOR_CNT_H, 0xFF, (u8)(sec_cnt >> 8));
+ rtsx_add_cmd(chip, WRITE_REG_CMD, MS_SECTOR_CNT_L, 0xFF, (u8)sec_cnt);
+ rtsx_add_cmd(chip, WRITE_REG_CMD, MS_TRANS_CFG, 0xFF, cfg);
+
+ if (mode_2k) {
+ rtsx_add_cmd(chip, WRITE_REG_CMD,
+ MS_CFG, MS_2K_SECTOR_MODE, MS_2K_SECTOR_MODE);
+ } else {
+ rtsx_add_cmd(chip, WRITE_REG_CMD, MS_CFG, MS_2K_SECTOR_MODE, 0);
+ }
+
+ trans_dma_enable(dir, chip, sec_cnt * 512, DMA_512);
+
+ rtsx_add_cmd(chip, WRITE_REG_CMD,
+ MS_TRANSFER, 0xFF, MS_TRANSFER_START | trans_mode);
+ rtsx_add_cmd(chip, CHECK_REG_CMD,
+ MS_TRANSFER, MS_TRANSFER_END, MS_TRANSFER_END);
+
+ rtsx_send_cmd_no_wait(chip);
+
+ retval = rtsx_transfer_data(chip, MS_CARD, buf, buf_len,
+ use_sg, dir, chip->mspro_timeout);
+ if (retval < 0) {
+ ms_set_err_code(chip, err_code);
+ if (retval == -ETIMEDOUT)
+ retval = STATUS_TIMEDOUT;
+ else
+ retval = STATUS_FAIL;
+
+ rtsx_trace(chip);
+ return retval;
+ }
+
+ retval = rtsx_read_register(chip, MS_TRANS_CFG, &val);
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+ if (val & (MS_INT_CMDNK | MS_INT_ERR | MS_CRC16_ERR | MS_RDY_TIMEOUT)) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ return STATUS_SUCCESS;
+}
+
+static int ms_write_bytes(struct rtsx_chip *chip,
+ u8 tpc, u8 cnt, u8 cfg, u8 *data, int data_len)
+{
+ struct ms_info *ms_card = &(chip->ms_card);
+ int retval, i;
+
+ if (!data || (data_len < cnt)) {
+ rtsx_trace(chip);
+ return STATUS_ERROR;
+ }
+
+ rtsx_init_cmd(chip);
+
+ for (i = 0; i < cnt; i++) {
+ rtsx_add_cmd(chip, WRITE_REG_CMD,
+ PPBUF_BASE2 + i, 0xFF, data[i]);
+ }
+ if (cnt % 2)
+ rtsx_add_cmd(chip, WRITE_REG_CMD, PPBUF_BASE2 + i, 0xFF, 0xFF);
+
+ rtsx_add_cmd(chip, WRITE_REG_CMD, MS_TPC, 0xFF, tpc);
+ rtsx_add_cmd(chip, WRITE_REG_CMD, MS_BYTE_CNT, 0xFF, cnt);
+ rtsx_add_cmd(chip, WRITE_REG_CMD, MS_TRANS_CFG, 0xFF, cfg);
+ rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_DATA_SOURCE,
+ 0x01, PINGPONG_BUFFER);
+
+ rtsx_add_cmd(chip, WRITE_REG_CMD,
+ MS_TRANSFER, 0xFF, MS_TRANSFER_START | MS_TM_WRITE_BYTES);
+ rtsx_add_cmd(chip, CHECK_REG_CMD,
+ MS_TRANSFER, MS_TRANSFER_END, MS_TRANSFER_END);
+
+ retval = rtsx_send_cmd(chip, MS_CARD, 5000);
+ if (retval < 0) {
+ u8 val = 0;
+
+ rtsx_read_register(chip, MS_TRANS_CFG, &val);
+ dev_dbg(rtsx_dev(chip), "MS_TRANS_CFG: 0x%02x\n", val);
+
+ rtsx_clear_ms_error(chip);
+
+ if (!(tpc & 0x08)) {
+ if (val & MS_CRC16_ERR) {
+ ms_set_err_code(chip, MS_CRC16_ERROR);
+ rtsx_trace(chip);
+ return ms_parse_err_code(chip);
+ }
+ } else {
+ if (CHK_MSPRO(ms_card) && !(val & 0x80)) {
+ if (val & (MS_INT_ERR | MS_INT_CMDNK)) {
+ ms_set_err_code(chip, MS_CMD_NK);
+ rtsx_trace(chip);
+ return ms_parse_err_code(chip);
+ }
+ }
+ }
+
+ if (val & MS_RDY_TIMEOUT) {
+ ms_set_err_code(chip, MS_TO_ERROR);
+ rtsx_trace(chip);
+ return ms_parse_err_code(chip);
+ }
+
+ ms_set_err_code(chip, MS_TO_ERROR);
+ rtsx_trace(chip);
+ return ms_parse_err_code(chip);
+ }
+
+ return STATUS_SUCCESS;
+}
+
+static int ms_read_bytes(struct rtsx_chip *chip,
+ u8 tpc, u8 cnt, u8 cfg, u8 *data, int data_len)
+{
+ struct ms_info *ms_card = &(chip->ms_card);
+ int retval, i;
+ u8 *ptr;
+
+ if (!data) {
+ rtsx_trace(chip);
+ return STATUS_ERROR;
+ }
+
+ rtsx_init_cmd(chip);
+
+ rtsx_add_cmd(chip, WRITE_REG_CMD, MS_TPC, 0xFF, tpc);
+ rtsx_add_cmd(chip, WRITE_REG_CMD, MS_BYTE_CNT, 0xFF, cnt);
+ rtsx_add_cmd(chip, WRITE_REG_CMD, MS_TRANS_CFG, 0xFF, cfg);
+ rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_DATA_SOURCE,
+ 0x01, PINGPONG_BUFFER);
+
+ rtsx_add_cmd(chip, WRITE_REG_CMD, MS_TRANSFER, 0xFF,
+ MS_TRANSFER_START | MS_TM_READ_BYTES);
+ rtsx_add_cmd(chip, CHECK_REG_CMD, MS_TRANSFER,
+ MS_TRANSFER_END, MS_TRANSFER_END);
+
+ for (i = 0; i < data_len - 1; i++)
+ rtsx_add_cmd(chip, READ_REG_CMD, PPBUF_BASE2 + i, 0, 0);
+
+ if (data_len % 2)
+ rtsx_add_cmd(chip, READ_REG_CMD, PPBUF_BASE2 + data_len, 0, 0);
+ else
+ rtsx_add_cmd(chip, READ_REG_CMD, PPBUF_BASE2 + data_len - 1,
+ 0, 0);
+
+ retval = rtsx_send_cmd(chip, MS_CARD, 5000);
+ if (retval < 0) {
+ u8 val = 0;
+
+ rtsx_read_register(chip, MS_TRANS_CFG, &val);
+ rtsx_clear_ms_error(chip);
+
+ if (!(tpc & 0x08)) {
+ if (val & MS_CRC16_ERR) {
+ ms_set_err_code(chip, MS_CRC16_ERROR);
+ rtsx_trace(chip);
+ return ms_parse_err_code(chip);
+ }
+ } else {
+ if (CHK_MSPRO(ms_card) && !(val & 0x80)) {
+ if (val & (MS_INT_ERR | MS_INT_CMDNK)) {
+ ms_set_err_code(chip, MS_CMD_NK);
+ rtsx_trace(chip);
+ return ms_parse_err_code(chip);
+ }
+ }
+ }
+
+ if (val & MS_RDY_TIMEOUT) {
+ ms_set_err_code(chip, MS_TO_ERROR);
+ rtsx_trace(chip);
+ return ms_parse_err_code(chip);
+ }
+
+ ms_set_err_code(chip, MS_TO_ERROR);
+ rtsx_trace(chip);
+ return ms_parse_err_code(chip);
+ }
+
+ ptr = rtsx_get_cmd_data(chip) + 1;
+
+ for (i = 0; i < data_len; i++)
+ data[i] = ptr[i];
+
+ if ((tpc == PRO_READ_SHORT_DATA) && (data_len == 8)) {
+ dev_dbg(rtsx_dev(chip), "Read format progress:\n");
+ print_hex_dump_bytes(KBUILD_MODNAME ": ", DUMP_PREFIX_NONE, ptr,
+ cnt);
+ }
+
+ return STATUS_SUCCESS;
+}
+
+static int ms_set_rw_reg_addr(struct rtsx_chip *chip,
+ u8 read_start, u8 read_cnt, u8 write_start, u8 write_cnt)
+{
+ int retval, i;
+ u8 data[4];
+
+ data[0] = read_start;
+ data[1] = read_cnt;
+ data[2] = write_start;
+ data[3] = write_cnt;
+
+ for (i = 0; i < MS_MAX_RETRY_COUNT; i++) {
+ retval = ms_write_bytes(chip, SET_RW_REG_ADRS, 4,
+ NO_WAIT_INT, data, 4);
+ if (retval == STATUS_SUCCESS)
+ return STATUS_SUCCESS;
+ rtsx_clear_ms_error(chip);
+ }
+
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+}
+
+static int ms_send_cmd(struct rtsx_chip *chip, u8 cmd, u8 cfg)
+{
+ u8 data[2];
+
+ data[0] = cmd;
+ data[1] = 0;
+
+ return ms_write_bytes(chip, PRO_SET_CMD, 1, cfg, data, 1);
+}
+
+static int ms_set_init_para(struct rtsx_chip *chip)
+{
+ struct ms_info *ms_card = &(chip->ms_card);
+ int retval;
+
+ if (CHK_HG8BIT(ms_card)) {
+ if (chip->asic_code)
+ ms_card->ms_clock = chip->asic_ms_hg_clk;
+ else
+ ms_card->ms_clock = chip->fpga_ms_hg_clk;
+
+ } else if (CHK_MSPRO(ms_card) || CHK_MS4BIT(ms_card)) {
+ if (chip->asic_code)
+ ms_card->ms_clock = chip->asic_ms_4bit_clk;
+ else
+ ms_card->ms_clock = chip->fpga_ms_4bit_clk;
+
+ } else {
+ if (chip->asic_code)
+ ms_card->ms_clock = chip->asic_ms_1bit_clk;
+ else
+ ms_card->ms_clock = chip->fpga_ms_1bit_clk;
+ }
+
+ retval = switch_clock(chip, ms_card->ms_clock);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ retval = select_card(chip, MS_CARD);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ return STATUS_SUCCESS;
+}
+
+static int ms_switch_clock(struct rtsx_chip *chip)
+{
+ struct ms_info *ms_card = &(chip->ms_card);
+ int retval;
+
+ retval = select_card(chip, MS_CARD);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ retval = switch_clock(chip, ms_card->ms_clock);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ return STATUS_SUCCESS;
+}
+
+static int ms_pull_ctl_disable(struct rtsx_chip *chip)
+{
+ int retval;
+
+ if (CHECK_PID(chip, 0x5208)) {
+ retval = rtsx_write_register(chip, CARD_PULL_CTL1, 0xFF,
+ MS_D1_PD | MS_D2_PD | MS_CLK_PD | MS_D6_PD);
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+ retval = rtsx_write_register(chip, CARD_PULL_CTL2, 0xFF,
+ MS_D3_PD | MS_D0_PD | MS_BS_PD | XD_D4_PD);
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+ retval = rtsx_write_register(chip, CARD_PULL_CTL3, 0xFF,
+ MS_D7_PD | XD_CE_PD | XD_CLE_PD | XD_CD_PU);
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+ retval = rtsx_write_register(chip, CARD_PULL_CTL4, 0xFF,
+ XD_RDY_PD | SD_D3_PD | SD_D2_PD | XD_ALE_PD);
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+ retval = rtsx_write_register(chip, CARD_PULL_CTL5, 0xFF,
+ MS_INS_PU | SD_WP_PD | SD_CD_PU | SD_CMD_PD);
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+ retval = rtsx_write_register(chip, CARD_PULL_CTL6, 0xFF,
+ MS_D5_PD | MS_D4_PD);
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+ } else if (CHECK_PID(chip, 0x5288)) {
+ if (CHECK_BARO_PKG(chip, QFN)) {
+ retval = rtsx_write_register(chip, CARD_PULL_CTL1,
+ 0xFF, 0x55);
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+ retval = rtsx_write_register(chip, CARD_PULL_CTL2,
+ 0xFF, 0x55);
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+ retval = rtsx_write_register(chip, CARD_PULL_CTL3,
+ 0xFF, 0x4B);
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+ retval = rtsx_write_register(chip, CARD_PULL_CTL4,
+ 0xFF, 0x69);
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+ }
+ }
+
+ return STATUS_SUCCESS;
+}
+
+static int ms_pull_ctl_enable(struct rtsx_chip *chip)
+{
+ int retval;
+
+ rtsx_init_cmd(chip);
+
+ if (CHECK_PID(chip, 0x5208)) {
+ rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_PULL_CTL1, 0xFF,
+ MS_D1_PD | MS_D2_PD | MS_CLK_NP | MS_D6_PD);
+ rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_PULL_CTL2, 0xFF,
+ MS_D3_PD | MS_D0_PD | MS_BS_NP | XD_D4_PD);
+ rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_PULL_CTL3, 0xFF,
+ MS_D7_PD | XD_CE_PD | XD_CLE_PD | XD_CD_PU);
+ rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_PULL_CTL4, 0xFF,
+ XD_RDY_PD | SD_D3_PD | SD_D2_PD | XD_ALE_PD);
+ rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_PULL_CTL5, 0xFF,
+ MS_INS_PU | SD_WP_PD | SD_CD_PU | SD_CMD_PD);
+ rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_PULL_CTL6, 0xFF,
+ MS_D5_PD | MS_D4_PD);
+ } else if (CHECK_PID(chip, 0x5288)) {
+ if (CHECK_BARO_PKG(chip, QFN)) {
+ rtsx_add_cmd(chip, WRITE_REG_CMD,
+ CARD_PULL_CTL1, 0xFF, 0x55);
+ rtsx_add_cmd(chip, WRITE_REG_CMD,
+ CARD_PULL_CTL2, 0xFF, 0x45);
+ rtsx_add_cmd(chip, WRITE_REG_CMD,
+ CARD_PULL_CTL3, 0xFF, 0x4B);
+ rtsx_add_cmd(chip, WRITE_REG_CMD,
+ CARD_PULL_CTL4, 0xFF, 0x29);
+ }
+ }
+
+ retval = rtsx_send_cmd(chip, MS_CARD, 100);
+ if (retval < 0) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ return STATUS_SUCCESS;
+}
+
+static int ms_prepare_reset(struct rtsx_chip *chip)
+{
+ struct ms_info *ms_card = &(chip->ms_card);
+ int retval;
+ u8 oc_mask = 0;
+
+ ms_card->ms_type = 0;
+ ms_card->check_ms_flow = 0;
+ ms_card->switch_8bit_fail = 0;
+ ms_card->delay_write.delay_write_flag = 0;
+
+ ms_card->pro_under_formatting = 0;
+
+ retval = ms_power_off_card3v3(chip);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ if (!chip->ft2_fast_mode)
+ wait_timeout(250);
+
+ retval = enable_card_clock(chip, MS_CARD);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ if (chip->asic_code) {
+ retval = ms_pull_ctl_enable(chip);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+ } else {
+ retval = rtsx_write_register(chip, FPGA_PULL_CTL,
+ FPGA_MS_PULL_CTL_BIT | 0x20, 0);
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+ }
+
+ if (!chip->ft2_fast_mode) {
+ retval = card_power_on(chip, MS_CARD);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ wait_timeout(150);
+
+#ifdef SUPPORT_OCP
+ if (CHECK_LUN_MODE(chip, SD_MS_2LUN))
+ oc_mask = MS_OC_NOW | MS_OC_EVER;
+ else
+ oc_mask = SD_OC_NOW | SD_OC_EVER;
+
+ if (chip->ocp_stat & oc_mask) {
+ dev_dbg(rtsx_dev(chip), "Over current, OCPSTAT is 0x%x\n",
+ chip->ocp_stat);
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+#endif
+ }
+
+ retval = rtsx_write_register(chip, CARD_OE, MS_OUTPUT_EN,
+ MS_OUTPUT_EN);
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+
+ if (chip->asic_code) {
+ retval = rtsx_write_register(chip, MS_CFG, 0xFF,
+ SAMPLE_TIME_RISING | PUSH_TIME_DEFAULT | NO_EXTEND_TOGGLE | MS_BUS_WIDTH_1);
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+ } else {
+ retval = rtsx_write_register(chip, MS_CFG, 0xFF,
+ SAMPLE_TIME_FALLING | PUSH_TIME_DEFAULT | NO_EXTEND_TOGGLE | MS_BUS_WIDTH_1);
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+ }
+ retval = rtsx_write_register(chip, MS_TRANS_CFG, 0xFF,
+ NO_WAIT_INT | NO_AUTO_READ_INT_REG);
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+ retval = rtsx_write_register(chip, CARD_STOP, MS_STOP | MS_CLR_ERR,
+ MS_STOP | MS_CLR_ERR);
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+
+ retval = ms_set_init_para(chip);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ return STATUS_SUCCESS;
+}
+
+static int ms_identify_media_type(struct rtsx_chip *chip, int switch_8bit_bus)
+{
+ struct ms_info *ms_card = &(chip->ms_card);
+ int retval, i;
+ u8 val;
+
+ retval = ms_set_rw_reg_addr(chip, Pro_StatusReg, 6, SystemParm, 1);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ for (i = 0; i < MS_MAX_RETRY_COUNT; i++) {
+ retval = ms_transfer_tpc(chip, MS_TM_READ_BYTES, READ_REG,
+ 6, NO_WAIT_INT);
+ if (retval == STATUS_SUCCESS)
+ break;
+ }
+ if (i == MS_MAX_RETRY_COUNT) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ retval = rtsx_read_register(chip, PPBUF_BASE2 + 2, &val);
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+ dev_dbg(rtsx_dev(chip), "Type register: 0x%x\n", val);
+ if (val != 0x01) {
+ if (val != 0x02)
+ ms_card->check_ms_flow = 1;
+
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ retval = rtsx_read_register(chip, PPBUF_BASE2 + 4, &val);
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+ dev_dbg(rtsx_dev(chip), "Category register: 0x%x\n", val);
+ if (val != 0) {
+ ms_card->check_ms_flow = 1;
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ retval = rtsx_read_register(chip, PPBUF_BASE2 + 5, &val);
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+ dev_dbg(rtsx_dev(chip), "Class register: 0x%x\n", val);
+ if (val == 0) {
+ retval = rtsx_read_register(chip, PPBUF_BASE2, &val);
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+ if (val & WRT_PRTCT)
+ chip->card_wp |= MS_CARD;
+ else
+ chip->card_wp &= ~MS_CARD;
+
+ } else if ((val == 0x01) || (val == 0x02) || (val == 0x03)) {
+ chip->card_wp |= MS_CARD;
+ } else {
+ ms_card->check_ms_flow = 1;
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ ms_card->ms_type |= TYPE_MSPRO;
+
+ retval = rtsx_read_register(chip, PPBUF_BASE2 + 3, &val);
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+ dev_dbg(rtsx_dev(chip), "IF Mode register: 0x%x\n", val);
+ if (val == 0) {
+ ms_card->ms_type &= 0x0F;
+ } else if (val == 7) {
+ if (switch_8bit_bus)
+ ms_card->ms_type |= MS_HG;
+ else
+ ms_card->ms_type &= 0x0F;
+
+ } else {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ return STATUS_SUCCESS;
+}
+
+static int ms_confirm_cpu_startup(struct rtsx_chip *chip)
+{
+ int retval, i, k;
+ u8 val;
+
+ /* Confirm CPU StartUp */
+ k = 0;
+ do {
+ if (detect_card_cd(chip, MS_CARD) != STATUS_SUCCESS) {
+ ms_set_err_code(chip, MS_NO_CARD);
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ for (i = 0; i < MS_MAX_RETRY_COUNT; i++) {
+ retval = ms_read_bytes(chip, GET_INT, 1,
+ NO_WAIT_INT, &val, 1);
+ if (retval == STATUS_SUCCESS)
+ break;
+ }
+ if (i == MS_MAX_RETRY_COUNT) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ if (k > 100) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ k++;
+ wait_timeout(100);
+ } while (!(val & INT_REG_CED));
+
+ for (i = 0; i < MS_MAX_RETRY_COUNT; i++) {
+ retval = ms_read_bytes(chip, GET_INT, 1, NO_WAIT_INT, &val, 1);
+ if (retval == STATUS_SUCCESS)
+ break;
+ }
+ if (i == MS_MAX_RETRY_COUNT) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ if (val & INT_REG_ERR) {
+ if (val & INT_REG_CMDNK)
+ chip->card_wp |= (MS_CARD);
+ else {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+ }
+ /* -- end confirm CPU startup */
+
+ return STATUS_SUCCESS;
+}
+
+static int ms_switch_parallel_bus(struct rtsx_chip *chip)
+{
+ int retval, i;
+ u8 data[2];
+
+ data[0] = PARALLEL_4BIT_IF;
+ data[1] = 0;
+ for (i = 0; i < MS_MAX_RETRY_COUNT; i++) {
+ retval = ms_write_bytes(chip, WRITE_REG, 1, NO_WAIT_INT,
+ data, 2);
+ if (retval == STATUS_SUCCESS)
+ break;
+ }
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ return STATUS_SUCCESS;
+}
+
+static int ms_switch_8bit_bus(struct rtsx_chip *chip)
+{
+ struct ms_info *ms_card = &(chip->ms_card);
+ int retval, i;
+ u8 data[2];
+
+ data[0] = PARALLEL_8BIT_IF;
+ data[1] = 0;
+ for (i = 0; i < MS_MAX_RETRY_COUNT; i++) {
+ retval = ms_write_bytes(chip, WRITE_REG, 1,
+ NO_WAIT_INT, data, 2);
+ if (retval == STATUS_SUCCESS)
+ break;
+ }
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ retval = rtsx_write_register(chip, MS_CFG, 0x98,
+ MS_BUS_WIDTH_8 | SAMPLE_TIME_FALLING);
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+ ms_card->ms_type |= MS_8BIT;
+ retval = ms_set_init_para(chip);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ for (i = 0; i < MS_MAX_RETRY_COUNT; i++) {
+ retval = ms_transfer_tpc(chip, MS_TM_READ_BYTES, GET_INT,
+ 1, NO_WAIT_INT);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+ }
+
+ return STATUS_SUCCESS;
+}
+
+static int ms_pro_reset_flow(struct rtsx_chip *chip, int switch_8bit_bus)
+{
+ struct ms_info *ms_card = &(chip->ms_card);
+ int retval, i;
+
+ for (i = 0; i < 3; i++) {
+ retval = ms_prepare_reset(chip);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ retval = ms_identify_media_type(chip, switch_8bit_bus);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ retval = ms_confirm_cpu_startup(chip);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ retval = ms_switch_parallel_bus(chip);
+ if (retval != STATUS_SUCCESS) {
+ if (detect_card_cd(chip, MS_CARD) != STATUS_SUCCESS) {
+ ms_set_err_code(chip, MS_NO_CARD);
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+ continue;
+ } else {
+ break;
+ }
+ }
+
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ /* Switch MS-PRO into Parallel mode */
+ retval = rtsx_write_register(chip, MS_CFG, 0x18, MS_BUS_WIDTH_4);
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+ retval = rtsx_write_register(chip, MS_CFG, PUSH_TIME_ODD,
+ PUSH_TIME_ODD);
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+
+ retval = ms_set_init_para(chip);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ /* If MSPro HG Card, We shall try to switch to 8-bit bus */
+ if (CHK_MSHG(ms_card) && chip->support_ms_8bit && switch_8bit_bus) {
+ retval = ms_switch_8bit_bus(chip);
+ if (retval != STATUS_SUCCESS) {
+ ms_card->switch_8bit_fail = 1;
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+ }
+
+ return STATUS_SUCCESS;
+}
+
+#ifdef XC_POWERCLASS
+static int msxc_change_power(struct rtsx_chip *chip, u8 mode)
+{
+ int retval;
+ u8 buf[6];
+
+ ms_cleanup_work(chip);
+
+ retval = ms_set_rw_reg_addr(chip, 0, 0, Pro_DataCount1, 6);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ buf[0] = 0;
+ buf[1] = mode;
+ buf[2] = 0;
+ buf[3] = 0;
+ buf[4] = 0;
+ buf[5] = 0;
+
+ retval = ms_write_bytes(chip, PRO_WRITE_REG, 6, NO_WAIT_INT, buf, 6);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ retval = ms_send_cmd(chip, XC_CHG_POWER, WAIT_INT);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ retval = rtsx_read_register(chip, MS_TRANS_CFG, buf);
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+ if (buf[0] & (MS_INT_CMDNK | MS_INT_ERR)) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ return STATUS_SUCCESS;
+}
+#endif
+
+static int ms_read_attribute_info(struct rtsx_chip *chip)
+{
+ struct ms_info *ms_card = &(chip->ms_card);
+ int retval, i;
+ u8 val, *buf, class_code, device_type, sub_class, data[16];
+ u16 total_blk = 0, blk_size = 0;
+#ifdef SUPPORT_MSXC
+ u32 xc_total_blk = 0, xc_blk_size = 0;
+#endif
+ u32 sys_info_addr = 0, sys_info_size;
+#ifdef SUPPORT_PCGL_1P18
+ u32 model_name_addr = 0, model_name_size;
+ int found_sys_info = 0, found_model_name = 0;
+#endif
+
+ retval = ms_set_rw_reg_addr(chip, Pro_IntReg, 2, Pro_SystemParm, 7);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ if (CHK_MS8BIT(ms_card))
+ data[0] = PARALLEL_8BIT_IF;
+ else
+ data[0] = PARALLEL_4BIT_IF;
+
+ data[1] = 0;
+
+ data[2] = 0x40;
+ data[3] = 0;
+ data[4] = 0;
+ data[5] = 0;
+ data[6] = 0;
+ data[7] = 0;
+
+ for (i = 0; i < MS_MAX_RETRY_COUNT; i++) {
+ retval = ms_write_bytes(chip, PRO_WRITE_REG, 7, NO_WAIT_INT,
+ data, 8);
+ if (retval == STATUS_SUCCESS)
+ break;
+ }
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ buf = kmalloc(64 * 512, GFP_KERNEL);
+ if (buf == NULL) {
+ rtsx_trace(chip);
+ return STATUS_ERROR;
+ }
+
+ for (i = 0; i < MS_MAX_RETRY_COUNT; i++) {
+ retval = ms_send_cmd(chip, PRO_READ_ATRB, WAIT_INT);
+ if (retval != STATUS_SUCCESS)
+ continue;
+
+ retval = rtsx_read_register(chip, MS_TRANS_CFG, &val);
+ if (retval != STATUS_SUCCESS) {
+ kfree(buf);
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+ if (!(val & MS_INT_BREQ)) {
+ kfree(buf);
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+ retval = ms_transfer_data(chip, MS_TM_AUTO_READ,
+ PRO_READ_LONG_DATA, 0x40, WAIT_INT,
+ 0, 0, buf, 64 * 512);
+ if (retval == STATUS_SUCCESS)
+ break;
+
+ rtsx_clear_ms_error(chip);
+ }
+ if (retval != STATUS_SUCCESS) {
+ kfree(buf);
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ i = 0;
+ do {
+ retval = rtsx_read_register(chip, MS_TRANS_CFG, &val);
+ if (retval != STATUS_SUCCESS) {
+ kfree(buf);
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ if ((val & MS_INT_CED) || !(val & MS_INT_BREQ))
+ break;
+
+ retval = ms_transfer_tpc(chip, MS_TM_NORMAL_READ,
+ PRO_READ_LONG_DATA, 0, WAIT_INT);
+ if (retval != STATUS_SUCCESS) {
+ kfree(buf);
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ i++;
+ } while (i < 1024);
+
+ if (retval != STATUS_SUCCESS) {
+ kfree(buf);
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ if ((buf[0] != 0xa5) && (buf[1] != 0xc3)) {
+ /* Signature code is wrong */
+ kfree(buf);
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ if ((buf[4] < 1) || (buf[4] > 12)) {
+ kfree(buf);
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ for (i = 0; i < buf[4]; i++) {
+ int cur_addr_off = 16 + i * 12;
+
+#ifdef SUPPORT_MSXC
+ if ((buf[cur_addr_off + 8] == 0x10) ||
+ (buf[cur_addr_off + 8] == 0x13))
+#else
+ if (buf[cur_addr_off + 8] == 0x10)
+#endif
+ {
+ sys_info_addr = ((u32)buf[cur_addr_off + 0] << 24) |
+ ((u32)buf[cur_addr_off + 1] << 16) |
+ ((u32)buf[cur_addr_off + 2] << 8) |
+ buf[cur_addr_off + 3];
+ sys_info_size = ((u32)buf[cur_addr_off + 4] << 24) |
+ ((u32)buf[cur_addr_off + 5] << 16) |
+ ((u32)buf[cur_addr_off + 6] << 8) |
+ buf[cur_addr_off + 7];
+ dev_dbg(rtsx_dev(chip), "sys_info_addr = 0x%x, sys_info_size = 0x%x\n",
+ sys_info_addr, sys_info_size);
+ if (sys_info_size != 96) {
+ kfree(buf);
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+ if (sys_info_addr < 0x1A0) {
+ kfree(buf);
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+ if ((sys_info_size + sys_info_addr) > 0x8000) {
+ kfree(buf);
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+#ifdef SUPPORT_MSXC
+ if (buf[cur_addr_off + 8] == 0x13)
+ ms_card->ms_type |= MS_XC;
+#endif
+#ifdef SUPPORT_PCGL_1P18
+ found_sys_info = 1;
+#else
+ break;
+#endif
+ }
+#ifdef SUPPORT_PCGL_1P18
+ if (buf[cur_addr_off + 8] == 0x15) {
+ model_name_addr = ((u32)buf[cur_addr_off + 0] << 24) |
+ ((u32)buf[cur_addr_off + 1] << 16) |
+ ((u32)buf[cur_addr_off + 2] << 8) |
+ buf[cur_addr_off + 3];
+ model_name_size = ((u32)buf[cur_addr_off + 4] << 24) |
+ ((u32)buf[cur_addr_off + 5] << 16) |
+ ((u32)buf[cur_addr_off + 6] << 8) |
+ buf[cur_addr_off + 7];
+ dev_dbg(rtsx_dev(chip), "model_name_addr = 0x%x, model_name_size = 0x%x\n",
+ model_name_addr, model_name_size);
+ if (model_name_size != 48) {
+ kfree(buf);
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+ if (model_name_addr < 0x1A0) {
+ kfree(buf);
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+ if ((model_name_size + model_name_addr) > 0x8000) {
+ kfree(buf);
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ found_model_name = 1;
+ }
+
+ if (found_sys_info && found_model_name)
+ break;
+#endif
+ }
+
+ if (i == buf[4]) {
+ kfree(buf);
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ class_code = buf[sys_info_addr + 0];
+ device_type = buf[sys_info_addr + 56];
+ sub_class = buf[sys_info_addr + 46];
+#ifdef SUPPORT_MSXC
+ if (CHK_MSXC(ms_card)) {
+ xc_total_blk = ((u32)buf[sys_info_addr + 6] << 24) |
+ ((u32)buf[sys_info_addr + 7] << 16) |
+ ((u32)buf[sys_info_addr + 8] << 8) |
+ buf[sys_info_addr + 9];
+ xc_blk_size = ((u32)buf[sys_info_addr + 32] << 24) |
+ ((u32)buf[sys_info_addr + 33] << 16) |
+ ((u32)buf[sys_info_addr + 34] << 8) |
+ buf[sys_info_addr + 35];
+ dev_dbg(rtsx_dev(chip), "xc_total_blk = 0x%x, xc_blk_size = 0x%x\n",
+ xc_total_blk, xc_blk_size);
+ } else {
+ total_blk = ((u16)buf[sys_info_addr + 6] << 8) |
+ buf[sys_info_addr + 7];
+ blk_size = ((u16)buf[sys_info_addr + 2] << 8) |
+ buf[sys_info_addr + 3];
+ dev_dbg(rtsx_dev(chip), "total_blk = 0x%x, blk_size = 0x%x\n",
+ total_blk, blk_size);
+ }
+#else
+ total_blk = ((u16)buf[sys_info_addr + 6] << 8) | buf[sys_info_addr + 7];
+ blk_size = ((u16)buf[sys_info_addr + 2] << 8) | buf[sys_info_addr + 3];
+ dev_dbg(rtsx_dev(chip), "total_blk = 0x%x, blk_size = 0x%x\n",
+ total_blk, blk_size);
+#endif
+
+ dev_dbg(rtsx_dev(chip), "class_code = 0x%x, device_type = 0x%x, sub_class = 0x%x\n",
+ class_code, device_type, sub_class);
+
+ memcpy(ms_card->raw_sys_info, buf + sys_info_addr, 96);
+#ifdef SUPPORT_PCGL_1P18
+ memcpy(ms_card->raw_model_name, buf + model_name_addr, 48);
+#endif
+
+ kfree(buf);
+
+#ifdef SUPPORT_MSXC
+ if (CHK_MSXC(ms_card)) {
+ if (class_code != 0x03) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+ } else {
+ if (class_code != 0x02) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+ }
+#else
+ if (class_code != 0x02) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+#endif
+
+ if (device_type != 0x00) {
+ if ((device_type == 0x01) || (device_type == 0x02) ||
+ (device_type == 0x03)) {
+ chip->card_wp |= MS_CARD;
+ } else {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+ }
+
+ if (sub_class & 0xC0) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ dev_dbg(rtsx_dev(chip), "class_code: 0x%x, device_type: 0x%x, sub_class: 0x%x\n",
+ class_code, device_type, sub_class);
+
+#ifdef SUPPORT_MSXC
+ if (CHK_MSXC(ms_card)) {
+ chip->capacity[chip->card2lun[MS_CARD]] =
+ ms_card->capacity = xc_total_blk * xc_blk_size;
+ } else {
+ chip->capacity[chip->card2lun[MS_CARD]] =
+ ms_card->capacity = total_blk * blk_size;
+ }
+#else
+ ms_card->capacity = total_blk * blk_size;
+ chip->capacity[chip->card2lun[MS_CARD]] = ms_card->capacity;
+#endif
+
+ return STATUS_SUCCESS;
+}
+
+#ifdef SUPPORT_MAGIC_GATE
+static int mg_set_tpc_para_sub(struct rtsx_chip *chip,
+ int type, u8 mg_entry_num);
+#endif
+
+static int reset_ms_pro(struct rtsx_chip *chip)
+{
+ struct ms_info *ms_card = &(chip->ms_card);
+ int retval;
+#ifdef XC_POWERCLASS
+ u8 change_power_class;
+
+ if (chip->ms_power_class_en & 0x02)
+ change_power_class = 2;
+ else if (chip->ms_power_class_en & 0x01)
+ change_power_class = 1;
+ else
+ change_power_class = 0;
+#endif
+
+#ifdef XC_POWERCLASS
+Retry:
+#endif
+ retval = ms_pro_reset_flow(chip, 1);
+ if (retval != STATUS_SUCCESS) {
+ if (ms_card->switch_8bit_fail) {
+ retval = ms_pro_reset_flow(chip, 0);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+ } else {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+ }
+
+ retval = ms_read_attribute_info(chip);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+#ifdef XC_POWERCLASS
+ if (CHK_HG8BIT(ms_card))
+ change_power_class = 0;
+
+ if (change_power_class && CHK_MSXC(ms_card)) {
+ u8 power_class_en = chip->ms_power_class_en;
+
+ dev_dbg(rtsx_dev(chip), "power_class_en = 0x%x\n",
+ power_class_en);
+ dev_dbg(rtsx_dev(chip), "change_power_class = %d\n",
+ change_power_class);
+
+ if (change_power_class)
+ power_class_en &= (1 << (change_power_class - 1));
+ else
+ power_class_en = 0;
+
+ if (power_class_en) {
+ u8 power_class_mode =
+ (ms_card->raw_sys_info[46] & 0x18) >> 3;
+ dev_dbg(rtsx_dev(chip), "power_class_mode = 0x%x",
+ power_class_mode);
+ if (change_power_class > power_class_mode)
+ change_power_class = power_class_mode;
+ if (change_power_class) {
+ retval = msxc_change_power(chip,
+ change_power_class);
+ if (retval != STATUS_SUCCESS) {
+ change_power_class--;
+ goto Retry;
+ }
+ }
+ }
+ }
+#endif
+
+#ifdef SUPPORT_MAGIC_GATE
+ retval = mg_set_tpc_para_sub(chip, 0, 0);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+#endif
+
+ if (CHK_HG8BIT(ms_card))
+ chip->card_bus_width[chip->card2lun[MS_CARD]] = 8;
+ else
+ chip->card_bus_width[chip->card2lun[MS_CARD]] = 4;
+
+ return STATUS_SUCCESS;
+}
+
+static int ms_read_status_reg(struct rtsx_chip *chip)
+{
+ int retval;
+ u8 val[2];
+
+ retval = ms_set_rw_reg_addr(chip, StatusReg0, 2, 0, 0);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ retval = ms_read_bytes(chip, READ_REG, 2, NO_WAIT_INT, val, 2);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ if (val[1] & (STS_UCDT | STS_UCEX | STS_UCFG)) {
+ ms_set_err_code(chip, MS_FLASH_READ_ERROR);
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ return STATUS_SUCCESS;
+}
+
+
+static int ms_read_extra_data(struct rtsx_chip *chip,
+ u16 block_addr, u8 page_num, u8 *buf, int buf_len)
+{
+ struct ms_info *ms_card = &(chip->ms_card);
+ int retval, i;
+ u8 val, data[10];
+
+ retval = ms_set_rw_reg_addr(chip, OverwriteFlag, MS_EXTRA_SIZE,
+ SystemParm, 6);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ if (CHK_MS4BIT(ms_card)) {
+ /* Parallel interface */
+ data[0] = 0x88;
+ } else {
+ /* Serial interface */
+ data[0] = 0x80;
+ }
+ data[1] = 0;
+ data[2] = (u8)(block_addr >> 8);
+ data[3] = (u8)block_addr;
+ data[4] = 0x40;
+ data[5] = page_num;
+
+ for (i = 0; i < MS_MAX_RETRY_COUNT; i++) {
+ retval = ms_write_bytes(chip, WRITE_REG, 6, NO_WAIT_INT,
+ data, 6);
+ if (retval == STATUS_SUCCESS)
+ break;
+ }
+ if (i == MS_MAX_RETRY_COUNT) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ ms_set_err_code(chip, MS_NO_ERROR);
+
+ for (i = 0; i < MS_MAX_RETRY_COUNT; i++) {
+ retval = ms_send_cmd(chip, BLOCK_READ, WAIT_INT);
+ if (retval == STATUS_SUCCESS)
+ break;
+ }
+ if (i == MS_MAX_RETRY_COUNT) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ ms_set_err_code(chip, MS_NO_ERROR);
+ retval = ms_read_bytes(chip, GET_INT, 1, NO_WAIT_INT, &val, 1);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ if (val & INT_REG_CMDNK) {
+ ms_set_err_code(chip, MS_CMD_NK);
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+ if (val & INT_REG_CED) {
+ if (val & INT_REG_ERR) {
+ retval = ms_read_status_reg(chip);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ retval = ms_set_rw_reg_addr(chip, OverwriteFlag,
+ MS_EXTRA_SIZE, SystemParm, 6);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+ }
+ }
+
+ retval = ms_read_bytes(chip, READ_REG, MS_EXTRA_SIZE, NO_WAIT_INT,
+ data, MS_EXTRA_SIZE);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ if (buf && buf_len) {
+ if (buf_len > MS_EXTRA_SIZE)
+ buf_len = MS_EXTRA_SIZE;
+ memcpy(buf, data, buf_len);
+ }
+
+ return STATUS_SUCCESS;
+}
+
+static int ms_write_extra_data(struct rtsx_chip *chip,
+ u16 block_addr, u8 page_num, u8 *buf, int buf_len)
+{
+ struct ms_info *ms_card = &(chip->ms_card);
+ int retval, i;
+ u8 val, data[16];
+
+ if (!buf || (buf_len < MS_EXTRA_SIZE)) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ retval = ms_set_rw_reg_addr(chip, OverwriteFlag, MS_EXTRA_SIZE,
+ SystemParm, 6 + MS_EXTRA_SIZE);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ if (CHK_MS4BIT(ms_card))
+ data[0] = 0x88;
+ else
+ data[0] = 0x80;
+
+ data[1] = 0;
+ data[2] = (u8)(block_addr >> 8);
+ data[3] = (u8)block_addr;
+ data[4] = 0x40;
+ data[5] = page_num;
+
+ for (i = 6; i < MS_EXTRA_SIZE + 6; i++)
+ data[i] = buf[i - 6];
+
+ retval = ms_write_bytes(chip, WRITE_REG, (6 + MS_EXTRA_SIZE),
+ NO_WAIT_INT, data, 16);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ retval = ms_send_cmd(chip, BLOCK_WRITE, WAIT_INT);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ ms_set_err_code(chip, MS_NO_ERROR);
+ retval = ms_read_bytes(chip, GET_INT, 1, NO_WAIT_INT, &val, 1);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ if (val & INT_REG_CMDNK) {
+ ms_set_err_code(chip, MS_CMD_NK);
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+ if (val & INT_REG_CED) {
+ if (val & INT_REG_ERR) {
+ ms_set_err_code(chip, MS_FLASH_WRITE_ERROR);
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+ }
+
+ return STATUS_SUCCESS;
+}
+
+
+static int ms_read_page(struct rtsx_chip *chip, u16 block_addr, u8 page_num)
+{
+ struct ms_info *ms_card = &(chip->ms_card);
+ int retval;
+ u8 val, data[6];
+
+ retval = ms_set_rw_reg_addr(chip, OverwriteFlag, MS_EXTRA_SIZE,
+ SystemParm, 6);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ if (CHK_MS4BIT(ms_card))
+ data[0] = 0x88;
+ else
+ data[0] = 0x80;
+
+ data[1] = 0;
+ data[2] = (u8)(block_addr >> 8);
+ data[3] = (u8)block_addr;
+ data[4] = 0x20;
+ data[5] = page_num;
+
+ retval = ms_write_bytes(chip, WRITE_REG, 6, NO_WAIT_INT, data, 6);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ retval = ms_send_cmd(chip, BLOCK_READ, WAIT_INT);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ ms_set_err_code(chip, MS_NO_ERROR);
+ retval = ms_read_bytes(chip, GET_INT, 1, NO_WAIT_INT, &val, 1);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ if (val & INT_REG_CMDNK) {
+ ms_set_err_code(chip, MS_CMD_NK);
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ if (val & INT_REG_CED) {
+ if (val & INT_REG_ERR) {
+ if (!(val & INT_REG_BREQ)) {
+ ms_set_err_code(chip, MS_FLASH_READ_ERROR);
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+ retval = ms_read_status_reg(chip);
+ if (retval != STATUS_SUCCESS)
+ ms_set_err_code(chip, MS_FLASH_WRITE_ERROR);
+
+ } else {
+ if (!(val & INT_REG_BREQ)) {
+ ms_set_err_code(chip, MS_BREQ_ERROR);
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+ }
+ }
+
+ retval = ms_transfer_tpc(chip, MS_TM_NORMAL_READ, READ_PAGE_DATA,
+ 0, NO_WAIT_INT);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ if (ms_check_err_code(chip, MS_FLASH_WRITE_ERROR)) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ return STATUS_SUCCESS;
+}
+
+
+static int ms_set_bad_block(struct rtsx_chip *chip, u16 phy_blk)
+{
+ struct ms_info *ms_card = &(chip->ms_card);
+ int retval;
+ u8 val, data[8], extra[MS_EXTRA_SIZE];
+
+ retval = ms_read_extra_data(chip, phy_blk, 0, extra, MS_EXTRA_SIZE);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ retval = ms_set_rw_reg_addr(chip, OverwriteFlag, MS_EXTRA_SIZE,
+ SystemParm, 7);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ ms_set_err_code(chip, MS_NO_ERROR);
+
+ if (CHK_MS4BIT(ms_card))
+ data[0] = 0x88;
+ else
+ data[0] = 0x80;
+
+ data[1] = 0;
+ data[2] = (u8)(phy_blk >> 8);
+ data[3] = (u8)phy_blk;
+ data[4] = 0x80;
+ data[5] = 0;
+ data[6] = extra[0] & 0x7F;
+ data[7] = 0xFF;
+
+ retval = ms_write_bytes(chip, WRITE_REG, 7, NO_WAIT_INT, data, 7);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ retval = ms_send_cmd(chip, BLOCK_WRITE, WAIT_INT);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ ms_set_err_code(chip, MS_NO_ERROR);
+ retval = ms_read_bytes(chip, GET_INT, 1, NO_WAIT_INT, &val, 1);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ if (val & INT_REG_CMDNK) {
+ ms_set_err_code(chip, MS_CMD_NK);
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ if (val & INT_REG_CED) {
+ if (val & INT_REG_ERR) {
+ ms_set_err_code(chip, MS_FLASH_WRITE_ERROR);
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+ }
+
+ return STATUS_SUCCESS;
+}
+
+
+static int ms_erase_block(struct rtsx_chip *chip, u16 phy_blk)
+{
+ struct ms_info *ms_card = &(chip->ms_card);
+ int retval, i = 0;
+ u8 val, data[6];
+
+ retval = ms_set_rw_reg_addr(chip, OverwriteFlag, MS_EXTRA_SIZE,
+ SystemParm, 6);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ ms_set_err_code(chip, MS_NO_ERROR);
+
+ if (CHK_MS4BIT(ms_card))
+ data[0] = 0x88;
+ else
+ data[0] = 0x80;
+
+ data[1] = 0;
+ data[2] = (u8)(phy_blk >> 8);
+ data[3] = (u8)phy_blk;
+ data[4] = 0;
+ data[5] = 0;
+
+ retval = ms_write_bytes(chip, WRITE_REG, 6, NO_WAIT_INT, data, 6);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ERASE_RTY:
+ retval = ms_send_cmd(chip, BLOCK_ERASE, WAIT_INT);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ ms_set_err_code(chip, MS_NO_ERROR);
+ retval = ms_read_bytes(chip, GET_INT, 1, NO_WAIT_INT, &val, 1);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ if (val & INT_REG_CMDNK) {
+ if (i < 3) {
+ i++;
+ goto ERASE_RTY;
+ }
+
+ ms_set_err_code(chip, MS_CMD_NK);
+ ms_set_bad_block(chip, phy_blk);
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ if (val & INT_REG_CED) {
+ if (val & INT_REG_ERR) {
+ ms_set_err_code(chip, MS_FLASH_WRITE_ERROR);
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+ }
+
+ return STATUS_SUCCESS;
+}
+
+
+static void ms_set_page_status(u16 log_blk, u8 type, u8 *extra, int extra_len)
+{
+ if (!extra || (extra_len < MS_EXTRA_SIZE))
+ return;
+
+ memset(extra, 0xFF, MS_EXTRA_SIZE);
+
+ if (type == setPS_NG) {
+ /* set page status as 1:NG,and block status keep 1:OK */
+ extra[0] = 0xB8;
+ } else {
+ /* set page status as 0:Data Error,and block status keep 1:OK */
+ extra[0] = 0x98;
+ }
+
+ extra[2] = (u8)(log_blk >> 8);
+ extra[3] = (u8)log_blk;
+}
+
+static int ms_init_page(struct rtsx_chip *chip, u16 phy_blk, u16 log_blk,
+ u8 start_page, u8 end_page)
+{
+ int retval;
+ u8 extra[MS_EXTRA_SIZE], i;
+
+ memset(extra, 0xff, MS_EXTRA_SIZE);
+
+ extra[0] = 0xf8; /* Block, page OK, data erased */
+ extra[1] = 0xff;
+ extra[2] = (u8)(log_blk >> 8);
+ extra[3] = (u8)log_blk;
+
+ for (i = start_page; i < end_page; i++) {
+ if (detect_card_cd(chip, MS_CARD) != STATUS_SUCCESS) {
+ ms_set_err_code(chip, MS_NO_CARD);
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ retval = ms_write_extra_data(chip, phy_blk, i,
+ extra, MS_EXTRA_SIZE);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+ }
+
+ return STATUS_SUCCESS;
+}
+
+static int ms_copy_page(struct rtsx_chip *chip, u16 old_blk, u16 new_blk,
+ u16 log_blk, u8 start_page, u8 end_page)
+{
+ struct ms_info *ms_card = &(chip->ms_card);
+ bool uncorrect_flag = false;
+ int retval, rty_cnt;
+ u8 extra[MS_EXTRA_SIZE], val, i, j, data[16];
+
+ dev_dbg(rtsx_dev(chip), "Copy page from 0x%x to 0x%x, logical block is 0x%x\n",
+ old_blk, new_blk, log_blk);
+ dev_dbg(rtsx_dev(chip), "start_page = %d, end_page = %d\n",
+ start_page, end_page);
+
+ retval = ms_read_extra_data(chip, new_blk, 0, extra, MS_EXTRA_SIZE);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ retval = ms_read_status_reg(chip);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ retval = rtsx_read_register(chip, PPBUF_BASE2, &val);
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+
+ if (val & BUF_FULL) {
+ retval = ms_send_cmd(chip, CLEAR_BUF, WAIT_INT);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ retval = ms_read_bytes(chip, GET_INT, 1, NO_WAIT_INT, &val, 1);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ if (!(val & INT_REG_CED)) {
+ ms_set_err_code(chip, MS_FLASH_WRITE_ERROR);
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+ }
+
+ for (i = start_page; i < end_page; i++) {
+ if (detect_card_cd(chip, MS_CARD) != STATUS_SUCCESS) {
+ ms_set_err_code(chip, MS_NO_CARD);
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ ms_read_extra_data(chip, old_blk, i, extra, MS_EXTRA_SIZE);
+
+ retval = ms_set_rw_reg_addr(chip, OverwriteFlag,
+ MS_EXTRA_SIZE, SystemParm, 6);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ ms_set_err_code(chip, MS_NO_ERROR);
+
+ if (CHK_MS4BIT(ms_card))
+ data[0] = 0x88;
+ else
+ data[0] = 0x80;
+
+ data[1] = 0;
+ data[2] = (u8)(old_blk >> 8);
+ data[3] = (u8)old_blk;
+ data[4] = 0x20;
+ data[5] = i;
+
+ retval = ms_write_bytes(chip, WRITE_REG, 6, NO_WAIT_INT,
+ data, 6);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ retval = ms_send_cmd(chip, BLOCK_READ, WAIT_INT);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ ms_set_err_code(chip, MS_NO_ERROR);
+ retval = ms_read_bytes(chip, GET_INT, 1, NO_WAIT_INT, &val, 1);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ if (val & INT_REG_CMDNK) {
+ ms_set_err_code(chip, MS_CMD_NK);
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ if (val & INT_REG_CED) {
+ if (val & INT_REG_ERR) {
+ retval = ms_read_status_reg(chip);
+ if (retval != STATUS_SUCCESS) {
+ uncorrect_flag = true;
+ dev_dbg(rtsx_dev(chip), "Uncorrectable error\n");
+ } else {
+ uncorrect_flag = false;
+ }
+
+ retval = ms_transfer_tpc(chip,
+ MS_TM_NORMAL_READ,
+ READ_PAGE_DATA,
+ 0, NO_WAIT_INT);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ if (uncorrect_flag) {
+ ms_set_page_status(log_blk, setPS_NG,
+ extra, MS_EXTRA_SIZE);
+ if (i == 0)
+ extra[0] &= 0xEF;
+
+ ms_write_extra_data(chip, old_blk, i,
+ extra, MS_EXTRA_SIZE);
+ dev_dbg(rtsx_dev(chip), "page %d : extra[0] = 0x%x\n",
+ i, extra[0]);
+ MS_SET_BAD_BLOCK_FLG(ms_card);
+
+ ms_set_page_status(log_blk, setPS_Error,
+ extra, MS_EXTRA_SIZE);
+ ms_write_extra_data(chip, new_blk, i,
+ extra, MS_EXTRA_SIZE);
+ continue;
+ }
+
+ for (rty_cnt = 0; rty_cnt < MS_MAX_RETRY_COUNT;
+ rty_cnt++) {
+ retval = ms_transfer_tpc(
+ chip,
+ MS_TM_NORMAL_WRITE,
+ WRITE_PAGE_DATA,
+ 0, NO_WAIT_INT);
+ if (retval == STATUS_SUCCESS)
+ break;
+ }
+ if (rty_cnt == MS_MAX_RETRY_COUNT) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+ }
+
+ if (!(val & INT_REG_BREQ)) {
+ ms_set_err_code(chip, MS_BREQ_ERROR);
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+ }
+
+ retval = ms_set_rw_reg_addr(chip, OverwriteFlag,
+ MS_EXTRA_SIZE, SystemParm, (6 + MS_EXTRA_SIZE));
+
+ ms_set_err_code(chip, MS_NO_ERROR);
+
+ if (CHK_MS4BIT(ms_card))
+ data[0] = 0x88;
+ else
+ data[0] = 0x80;
+
+ data[1] = 0;
+ data[2] = (u8)(new_blk >> 8);
+ data[3] = (u8)new_blk;
+ data[4] = 0x20;
+ data[5] = i;
+
+ if ((extra[0] & 0x60) != 0x60)
+ data[6] = extra[0];
+ else
+ data[6] = 0xF8;
+
+ data[6 + 1] = 0xFF;
+ data[6 + 2] = (u8)(log_blk >> 8);
+ data[6 + 3] = (u8)log_blk;
+
+ for (j = 4; j <= MS_EXTRA_SIZE; j++)
+ data[6 + j] = 0xFF;
+
+ retval = ms_write_bytes(chip, WRITE_REG, (6 + MS_EXTRA_SIZE),
+ NO_WAIT_INT, data, 16);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ retval = ms_send_cmd(chip, BLOCK_WRITE, WAIT_INT);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ ms_set_err_code(chip, MS_NO_ERROR);
+ retval = ms_read_bytes(chip, GET_INT, 1, NO_WAIT_INT, &val, 1);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ if (val & INT_REG_CMDNK) {
+ ms_set_err_code(chip, MS_CMD_NK);
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ if (val & INT_REG_CED) {
+ if (val & INT_REG_ERR) {
+ ms_set_err_code(chip, MS_FLASH_WRITE_ERROR);
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+ }
+
+ if (i == 0) {
+ retval = ms_set_rw_reg_addr(chip, OverwriteFlag,
+ MS_EXTRA_SIZE, SystemParm, 7);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ ms_set_err_code(chip, MS_NO_ERROR);
+
+ if (CHK_MS4BIT(ms_card))
+ data[0] = 0x88;
+ else
+ data[0] = 0x80;
+
+ data[1] = 0;
+ data[2] = (u8)(old_blk >> 8);
+ data[3] = (u8)old_blk;
+ data[4] = 0x80;
+ data[5] = 0;
+ data[6] = 0xEF;
+ data[7] = 0xFF;
+
+ retval = ms_write_bytes(chip, WRITE_REG, 7,
+ NO_WAIT_INT, data, 8);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ retval = ms_send_cmd(chip, BLOCK_WRITE, WAIT_INT);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ ms_set_err_code(chip, MS_NO_ERROR);
+ retval = ms_read_bytes(chip, GET_INT, 1,
+ NO_WAIT_INT, &val, 1);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ if (val & INT_REG_CMDNK) {
+ ms_set_err_code(chip, MS_CMD_NK);
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ if (val & INT_REG_CED) {
+ if (val & INT_REG_ERR) {
+ ms_set_err_code(chip,
+ MS_FLASH_WRITE_ERROR);
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+ }
+ }
+ }
+
+ return STATUS_SUCCESS;
+}
+
+
+static int reset_ms(struct rtsx_chip *chip)
+{
+ struct ms_info *ms_card = &(chip->ms_card);
+ int retval;
+ u16 i, reg_addr, block_size;
+ u8 val, extra[MS_EXTRA_SIZE], j, *ptr;
+#ifndef SUPPORT_MAGIC_GATE
+ u16 eblock_cnt;
+#endif
+
+ retval = ms_prepare_reset(chip);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ ms_card->ms_type |= TYPE_MS;
+
+ retval = ms_send_cmd(chip, MS_RESET, NO_WAIT_INT);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ retval = ms_read_status_reg(chip);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ retval = rtsx_read_register(chip, PPBUF_BASE2, &val);
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+ if (val & WRT_PRTCT)
+ chip->card_wp |= MS_CARD;
+ else
+ chip->card_wp &= ~MS_CARD;
+
+ i = 0;
+
+RE_SEARCH:
+ /* Search Boot Block */
+ while (i < (MAX_DEFECTIVE_BLOCK + 2)) {
+ if (detect_card_cd(chip, MS_CARD) != STATUS_SUCCESS) {
+ ms_set_err_code(chip, MS_NO_CARD);
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ retval = ms_read_extra_data(chip, i, 0, extra, MS_EXTRA_SIZE);
+ if (retval != STATUS_SUCCESS) {
+ i++;
+ continue;
+ }
+
+ if (extra[0] & BLOCK_OK) {
+ if (!(extra[1] & NOT_BOOT_BLOCK)) {
+ ms_card->boot_block = i;
+ break;
+ }
+ }
+ i++;
+ }
+
+ if (i == (MAX_DEFECTIVE_BLOCK + 2)) {
+ dev_dbg(rtsx_dev(chip), "No boot block found!");
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ for (j = 0; j < 3; j++) {
+ retval = ms_read_page(chip, ms_card->boot_block, j);
+ if (retval != STATUS_SUCCESS) {
+ if (ms_check_err_code(chip, MS_FLASH_WRITE_ERROR)) {
+ i = ms_card->boot_block + 1;
+ ms_set_err_code(chip, MS_NO_ERROR);
+ goto RE_SEARCH;
+ }
+ }
+ }
+
+ retval = ms_read_page(chip, ms_card->boot_block, 0);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ /* Read MS system information as sys_info */
+ rtsx_init_cmd(chip);
+
+ for (i = 0; i < 96; i++)
+ rtsx_add_cmd(chip, READ_REG_CMD, PPBUF_BASE2 + 0x1A0 + i, 0, 0);
+
+ retval = rtsx_send_cmd(chip, MS_CARD, 100);
+ if (retval < 0) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ ptr = rtsx_get_cmd_data(chip);
+ memcpy(ms_card->raw_sys_info, ptr, 96);
+
+ /* Read useful block contents */
+ rtsx_init_cmd(chip);
+
+ rtsx_add_cmd(chip, READ_REG_CMD, HEADER_ID0, 0, 0);
+ rtsx_add_cmd(chip, READ_REG_CMD, HEADER_ID1, 0, 0);
+
+ for (reg_addr = DISABLED_BLOCK0; reg_addr <= DISABLED_BLOCK3;
+ reg_addr++)
+ rtsx_add_cmd(chip, READ_REG_CMD, reg_addr, 0, 0);
+
+ for (reg_addr = BLOCK_SIZE_0; reg_addr <= PAGE_SIZE_1; reg_addr++)
+ rtsx_add_cmd(chip, READ_REG_CMD, reg_addr, 0, 0);
+
+ rtsx_add_cmd(chip, READ_REG_CMD, MS_Device_Type, 0, 0);
+ rtsx_add_cmd(chip, READ_REG_CMD, MS_4bit_Support, 0, 0);
+
+ retval = rtsx_send_cmd(chip, MS_CARD, 100);
+ if (retval < 0) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ ptr = rtsx_get_cmd_data(chip);
+
+ dev_dbg(rtsx_dev(chip), "Boot block data:\n");
+ dev_dbg(rtsx_dev(chip), "%*ph\n", 16, ptr);
+
+ /* Block ID error
+ * HEADER_ID0, HEADER_ID1
+ */
+ if (ptr[0] != 0x00 || ptr[1] != 0x01) {
+ i = ms_card->boot_block + 1;
+ goto RE_SEARCH;
+ }
+
+ /* Page size error
+ * PAGE_SIZE_0, PAGE_SIZE_1
+ */
+ if (ptr[12] != 0x02 || ptr[13] != 0x00) {
+ i = ms_card->boot_block + 1;
+ goto RE_SEARCH;
+ }
+
+ if ((ptr[14] == 1) || (ptr[14] == 3))
+ chip->card_wp |= MS_CARD;
+
+ /* BLOCK_SIZE_0, BLOCK_SIZE_1 */
+ block_size = ((u16)ptr[6] << 8) | ptr[7];
+ if (block_size == 0x0010) {
+ /* Block size 16KB */
+ ms_card->block_shift = 5;
+ ms_card->page_off = 0x1F;
+ } else if (block_size == 0x0008) {
+ /* Block size 8KB */
+ ms_card->block_shift = 4;
+ ms_card->page_off = 0x0F;
+ }
+
+ /* BLOCK_COUNT_0, BLOCK_COUNT_1 */
+ ms_card->total_block = ((u16)ptr[8] << 8) | ptr[9];
+
+#ifdef SUPPORT_MAGIC_GATE
+ j = ptr[10];
+
+ if (ms_card->block_shift == 4) { /* 4MB or 8MB */
+ if (j < 2) { /* Effective block for 4MB: 0x1F0 */
+ ms_card->capacity = 0x1EE0;
+ } else { /* Effective block for 8MB: 0x3E0 */
+ ms_card->capacity = 0x3DE0;
+ }
+ } else { /* 16MB, 32MB, 64MB or 128MB */
+ if (j < 5) { /* Effective block for 16MB: 0x3E0 */
+ ms_card->capacity = 0x7BC0;
+ } else if (j < 0xA) { /* Effective block for 32MB: 0x7C0 */
+ ms_card->capacity = 0xF7C0;
+ } else if (j < 0x11) { /* Effective block for 64MB: 0xF80 */
+ ms_card->capacity = 0x1EF80;
+ } else { /* Effective block for 128MB: 0x1F00 */
+ ms_card->capacity = 0x3DF00;
+ }
+ }
+#else
+ /* EBLOCK_COUNT_0, EBLOCK_COUNT_1 */
+ eblock_cnt = ((u16)ptr[10] << 8) | ptr[11];
+
+ ms_card->capacity = ((u32)eblock_cnt - 2) << ms_card->block_shift;
+#endif
+
+ chip->capacity[chip->card2lun[MS_CARD]] = ms_card->capacity;
+
+ /* Switch I/F Mode */
+ if (ptr[15]) {
+ retval = ms_set_rw_reg_addr(chip, 0, 0, SystemParm, 1);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ retval = rtsx_write_register(chip, PPBUF_BASE2, 0xFF, 0x88);
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+ retval = rtsx_write_register(chip, PPBUF_BASE2 + 1, 0xFF, 0);
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+
+ retval = ms_transfer_tpc(chip, MS_TM_WRITE_BYTES, WRITE_REG, 1,
+ NO_WAIT_INT);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ retval = rtsx_write_register(chip, MS_CFG,
+ 0x58 | MS_NO_CHECK_INT,
+ MS_BUS_WIDTH_4 | PUSH_TIME_ODD | MS_NO_CHECK_INT);
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+
+ ms_card->ms_type |= MS_4BIT;
+ }
+
+ if (CHK_MS4BIT(ms_card))
+ chip->card_bus_width[chip->card2lun[MS_CARD]] = 4;
+ else
+ chip->card_bus_width[chip->card2lun[MS_CARD]] = 1;
+
+ return STATUS_SUCCESS;
+}
+
+static int ms_init_l2p_tbl(struct rtsx_chip *chip)
+{
+ struct ms_info *ms_card = &(chip->ms_card);
+ int size, i, seg_no, retval;
+ u16 defect_block, reg_addr;
+ u8 val1, val2;
+
+ ms_card->segment_cnt = ms_card->total_block >> 9;
+ dev_dbg(rtsx_dev(chip), "ms_card->segment_cnt = %d\n",
+ ms_card->segment_cnt);
+
+ size = ms_card->segment_cnt * sizeof(struct zone_entry);
+ ms_card->segment = vzalloc(size);
+ if (ms_card->segment == NULL) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ retval = ms_read_page(chip, ms_card->boot_block, 1);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ goto INIT_FAIL;
+ }
+
+ reg_addr = PPBUF_BASE2;
+ for (i = 0; i < (((ms_card->total_block >> 9) * 10) + 1); i++) {
+ int block_no;
+
+ retval = rtsx_read_register(chip, reg_addr++, &val1);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ goto INIT_FAIL;
+ }
+
+ retval = rtsx_read_register(chip, reg_addr++, &val2);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ goto INIT_FAIL;
+ }
+
+ defect_block = ((u16)val1 << 8) | val2;
+ if (defect_block == 0xFFFF)
+ break;
+
+ seg_no = defect_block / 512;
+
+ block_no = ms_card->segment[seg_no].disable_count++;
+ ms_card->segment[seg_no].defect_list[block_no] = defect_block;
+ }
+
+ for (i = 0; i < ms_card->segment_cnt; i++) {
+ ms_card->segment[i].build_flag = 0;
+ ms_card->segment[i].l2p_table = NULL;
+ ms_card->segment[i].free_table = NULL;
+ ms_card->segment[i].get_index = 0;
+ ms_card->segment[i].set_index = 0;
+ ms_card->segment[i].unused_blk_cnt = 0;
+
+ dev_dbg(rtsx_dev(chip), "defective block count of segment %d is %d\n",
+ i, ms_card->segment[i].disable_count);
+ }
+
+ return STATUS_SUCCESS;
+
+INIT_FAIL:
+ if (ms_card->segment) {
+ vfree(ms_card->segment);
+ ms_card->segment = NULL;
+ }
+
+ return STATUS_FAIL;
+}
+
+static u16 ms_get_l2p_tbl(struct rtsx_chip *chip, int seg_no, u16 log_off)
+{
+ struct ms_info *ms_card = &(chip->ms_card);
+ struct zone_entry *segment;
+
+ if (ms_card->segment == NULL)
+ return 0xFFFF;
+
+ segment = &(ms_card->segment[seg_no]);
+
+ if (segment->l2p_table)
+ return segment->l2p_table[log_off];
+
+ return 0xFFFF;
+}
+
+static void ms_set_l2p_tbl(struct rtsx_chip *chip,
+ int seg_no, u16 log_off, u16 phy_blk)
+{
+ struct ms_info *ms_card = &(chip->ms_card);
+ struct zone_entry *segment;
+
+ if (ms_card->segment == NULL)
+ return;
+
+ segment = &(ms_card->segment[seg_no]);
+ if (segment->l2p_table)
+ segment->l2p_table[log_off] = phy_blk;
+}
+
+static void ms_set_unused_block(struct rtsx_chip *chip, u16 phy_blk)
+{
+ struct ms_info *ms_card = &(chip->ms_card);
+ struct zone_entry *segment;
+ int seg_no;
+
+ seg_no = (int)phy_blk >> 9;
+ segment = &(ms_card->segment[seg_no]);
+
+ segment->free_table[segment->set_index++] = phy_blk;
+ if (segment->set_index >= MS_FREE_TABLE_CNT)
+ segment->set_index = 0;
+
+ segment->unused_blk_cnt++;
+}
+
+static u16 ms_get_unused_block(struct rtsx_chip *chip, int seg_no)
+{
+ struct ms_info *ms_card = &(chip->ms_card);
+ struct zone_entry *segment;
+ u16 phy_blk;
+
+ segment = &(ms_card->segment[seg_no]);
+
+ if (segment->unused_blk_cnt <= 0)
+ return 0xFFFF;
+
+ phy_blk = segment->free_table[segment->get_index];
+ segment->free_table[segment->get_index++] = 0xFFFF;
+ if (segment->get_index >= MS_FREE_TABLE_CNT)
+ segment->get_index = 0;
+
+ segment->unused_blk_cnt--;
+
+ return phy_blk;
+}
+
+static const unsigned short ms_start_idx[] = {0, 494, 990, 1486, 1982, 2478,
+ 2974, 3470, 3966, 4462, 4958,
+ 5454, 5950, 6446, 6942, 7438,
+ 7934};
+
+static int ms_arbitrate_l2p(struct rtsx_chip *chip, u16 phy_blk,
+ u16 log_off, u8 us1, u8 us2)
+{
+ struct ms_info *ms_card = &(chip->ms_card);
+ struct zone_entry *segment;
+ int seg_no;
+ u16 tmp_blk;
+
+ seg_no = (int)phy_blk >> 9;
+ segment = &(ms_card->segment[seg_no]);
+ tmp_blk = segment->l2p_table[log_off];
+
+ if (us1 != us2) {
+ if (us1 == 0) {
+ if (!(chip->card_wp & MS_CARD))
+ ms_erase_block(chip, tmp_blk);
+
+ ms_set_unused_block(chip, tmp_blk);
+ segment->l2p_table[log_off] = phy_blk;
+ } else {
+ if (!(chip->card_wp & MS_CARD))
+ ms_erase_block(chip, phy_blk);
+
+ ms_set_unused_block(chip, phy_blk);
+ }
+ } else {
+ if (phy_blk < tmp_blk) {
+ if (!(chip->card_wp & MS_CARD))
+ ms_erase_block(chip, phy_blk);
+
+ ms_set_unused_block(chip, phy_blk);
+ } else {
+ if (!(chip->card_wp & MS_CARD))
+ ms_erase_block(chip, tmp_blk);
+
+ ms_set_unused_block(chip, tmp_blk);
+ segment->l2p_table[log_off] = phy_blk;
+ }
+ }
+
+ return STATUS_SUCCESS;
+}
+
+static int ms_build_l2p_tbl(struct rtsx_chip *chip, int seg_no)
+{
+ struct ms_info *ms_card = &(chip->ms_card);
+ struct zone_entry *segment;
+ bool defect_flag;
+ int retval, table_size, disable_cnt, i;
+ u16 start, end, phy_blk, log_blk, tmp_blk, idx;
+ u8 extra[MS_EXTRA_SIZE], us1, us2;
+
+ dev_dbg(rtsx_dev(chip), "ms_build_l2p_tbl: %d\n", seg_no);
+
+ if (ms_card->segment == NULL) {
+ retval = ms_init_l2p_tbl(chip);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return retval;
+ }
+ }
+
+ if (ms_card->segment[seg_no].build_flag) {
+ dev_dbg(rtsx_dev(chip), "l2p table of segment %d has been built\n",
+ seg_no);
+ return STATUS_SUCCESS;
+ }
+
+ if (seg_no == 0)
+ table_size = 494;
+ else
+ table_size = 496;
+
+ segment = &(ms_card->segment[seg_no]);
+
+ if (segment->l2p_table == NULL) {
+ segment->l2p_table = vmalloc(table_size * 2);
+ if (segment->l2p_table == NULL) {
+ rtsx_trace(chip);
+ goto BUILD_FAIL;
+ }
+ }
+ memset((u8 *)(segment->l2p_table), 0xff, table_size * 2);
+
+ if (segment->free_table == NULL) {
+ segment->free_table = vmalloc(MS_FREE_TABLE_CNT * 2);
+ if (segment->free_table == NULL) {
+ rtsx_trace(chip);
+ goto BUILD_FAIL;
+ }
+ }
+ memset((u8 *)(segment->free_table), 0xff, MS_FREE_TABLE_CNT * 2);
+
+ start = (u16)seg_no << 9;
+ end = (u16)(seg_no + 1) << 9;
+
+ disable_cnt = segment->disable_count;
+
+ segment->get_index = segment->set_index = 0;
+ segment->unused_blk_cnt = 0;
+
+ for (phy_blk = start; phy_blk < end; phy_blk++) {
+ if (disable_cnt) {
+ defect_flag = false;
+ for (i = 0; i < segment->disable_count; i++) {
+ if (phy_blk == segment->defect_list[i]) {
+ defect_flag = true;
+ break;
+ }
+ }
+ if (defect_flag) {
+ disable_cnt--;
+ continue;
+ }
+ }
+
+ retval = ms_read_extra_data(chip, phy_blk, 0,
+ extra, MS_EXTRA_SIZE);
+ if (retval != STATUS_SUCCESS) {
+ dev_dbg(rtsx_dev(chip), "read extra data fail\n");
+ ms_set_bad_block(chip, phy_blk);
+ continue;
+ }
+
+ if (seg_no == ms_card->segment_cnt - 1) {
+ if (!(extra[1] & NOT_TRANSLATION_TABLE)) {
+ if (!(chip->card_wp & MS_CARD)) {
+ retval = ms_erase_block(chip, phy_blk);
+ if (retval != STATUS_SUCCESS)
+ continue;
+ extra[2] = 0xff;
+ extra[3] = 0xff;
+ }
+ }
+ }
+
+ if (!(extra[0] & BLOCK_OK))
+ continue;
+ if (!(extra[1] & NOT_BOOT_BLOCK))
+ continue;
+ if ((extra[0] & PAGE_OK) != PAGE_OK)
+ continue;
+
+ log_blk = ((u16)extra[2] << 8) | extra[3];
+
+ if (log_blk == 0xFFFF) {
+ if (!(chip->card_wp & MS_CARD)) {
+ retval = ms_erase_block(chip, phy_blk);
+ if (retval != STATUS_SUCCESS)
+ continue;
+ }
+ ms_set_unused_block(chip, phy_blk);
+ continue;
+ }
+
+ if ((log_blk < ms_start_idx[seg_no]) ||
+ (log_blk >= ms_start_idx[seg_no+1])) {
+ if (!(chip->card_wp & MS_CARD)) {
+ retval = ms_erase_block(chip, phy_blk);
+ if (retval != STATUS_SUCCESS)
+ continue;
+ }
+ ms_set_unused_block(chip, phy_blk);
+ continue;
+ }
+
+ idx = log_blk - ms_start_idx[seg_no];
+
+ if (segment->l2p_table[idx] == 0xFFFF) {
+ segment->l2p_table[idx] = phy_blk;
+ continue;
+ }
+
+ us1 = extra[0] & 0x10;
+ tmp_blk = segment->l2p_table[idx];
+ retval = ms_read_extra_data(chip, tmp_blk, 0,
+ extra, MS_EXTRA_SIZE);
+ if (retval != STATUS_SUCCESS)
+ continue;
+ us2 = extra[0] & 0x10;
+
+ (void)ms_arbitrate_l2p(chip, phy_blk,
+ log_blk-ms_start_idx[seg_no], us1, us2);
+ continue;
+ }
+
+ segment->build_flag = 1;
+
+ dev_dbg(rtsx_dev(chip), "unused block count: %d\n",
+ segment->unused_blk_cnt);
+
+ /* Logical Address Confirmation Process */
+ if (seg_no == ms_card->segment_cnt - 1) {
+ if (segment->unused_blk_cnt < 2)
+ chip->card_wp |= MS_CARD;
+ } else {
+ if (segment->unused_blk_cnt < 1)
+ chip->card_wp |= MS_CARD;
+ }
+
+ if (chip->card_wp & MS_CARD)
+ return STATUS_SUCCESS;
+
+ for (log_blk = ms_start_idx[seg_no];
+ log_blk < ms_start_idx[seg_no + 1]; log_blk++) {
+ idx = log_blk - ms_start_idx[seg_no];
+ if (segment->l2p_table[idx] == 0xFFFF) {
+ phy_blk = ms_get_unused_block(chip, seg_no);
+ if (phy_blk == 0xFFFF) {
+ chip->card_wp |= MS_CARD;
+ return STATUS_SUCCESS;
+ }
+ retval = ms_init_page(chip, phy_blk, log_blk, 0, 1);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ goto BUILD_FAIL;
+ }
+
+ segment->l2p_table[idx] = phy_blk;
+ if (seg_no == ms_card->segment_cnt - 1) {
+ if (segment->unused_blk_cnt < 2) {
+ chip->card_wp |= MS_CARD;
+ return STATUS_SUCCESS;
+ }
+ } else {
+ if (segment->unused_blk_cnt < 1) {
+ chip->card_wp |= MS_CARD;
+ return STATUS_SUCCESS;
+ }
+ }
+ }
+ }
+
+ /* Make boot block be the first normal block */
+ if (seg_no == 0) {
+ for (log_blk = 0; log_blk < 494; log_blk++) {
+ tmp_blk = segment->l2p_table[log_blk];
+ if (tmp_blk < ms_card->boot_block) {
+ dev_dbg(rtsx_dev(chip), "Boot block is not the first normal block.\n");
+
+ if (chip->card_wp & MS_CARD)
+ break;
+
+ phy_blk = ms_get_unused_block(chip, 0);
+ retval = ms_copy_page(chip, tmp_blk, phy_blk,
+ log_blk, 0, ms_card->page_off + 1);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ segment->l2p_table[log_blk] = phy_blk;
+
+ retval = ms_set_bad_block(chip, tmp_blk);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+ }
+ }
+ }
+
+ return STATUS_SUCCESS;
+
+BUILD_FAIL:
+ segment->build_flag = 0;
+ if (segment->l2p_table) {
+ vfree(segment->l2p_table);
+ segment->l2p_table = NULL;
+ }
+ if (segment->free_table) {
+ vfree(segment->free_table);
+ segment->free_table = NULL;
+ }
+
+ return STATUS_FAIL;
+}
+
+
+int reset_ms_card(struct rtsx_chip *chip)
+{
+ struct ms_info *ms_card = &(chip->ms_card);
+ int retval;
+
+ memset(ms_card, 0, sizeof(struct ms_info));
+
+ retval = enable_card_clock(chip, MS_CARD);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ retval = select_card(chip, MS_CARD);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ ms_card->ms_type = 0;
+
+ retval = reset_ms_pro(chip);
+ if (retval != STATUS_SUCCESS) {
+ if (ms_card->check_ms_flow) {
+ retval = reset_ms(chip);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+ } else {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+ }
+
+ retval = ms_set_init_para(chip);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ if (!CHK_MSPRO(ms_card)) {
+ /* Build table for the last segment,
+ * to check if L2P table block exists, erasing it
+ */
+ retval = ms_build_l2p_tbl(chip, ms_card->total_block / 512 - 1);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+ }
+
+ dev_dbg(rtsx_dev(chip), "ms_card->ms_type = 0x%x\n", ms_card->ms_type);
+
+ return STATUS_SUCCESS;
+}
+
+static int mspro_set_rw_cmd(struct rtsx_chip *chip,
+ u32 start_sec, u16 sec_cnt, u8 cmd)
+{
+ int retval, i;
+ u8 data[8];
+
+ data[0] = cmd;
+ data[1] = (u8)(sec_cnt >> 8);
+ data[2] = (u8)sec_cnt;
+ data[3] = (u8)(start_sec >> 24);
+ data[4] = (u8)(start_sec >> 16);
+ data[5] = (u8)(start_sec >> 8);
+ data[6] = (u8)start_sec;
+ data[7] = 0;
+
+ for (i = 0; i < MS_MAX_RETRY_COUNT; i++) {
+ retval = ms_write_bytes(chip, PRO_EX_SET_CMD, 7,
+ WAIT_INT, data, 8);
+ if (retval == STATUS_SUCCESS)
+ break;
+ }
+ if (i == MS_MAX_RETRY_COUNT) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ return STATUS_SUCCESS;
+}
+
+
+void mspro_stop_seq_mode(struct rtsx_chip *chip)
+{
+ struct ms_info *ms_card = &(chip->ms_card);
+ int retval;
+
+ if (ms_card->seq_mode) {
+ retval = ms_switch_clock(chip);
+ if (retval != STATUS_SUCCESS)
+ return;
+
+ ms_card->seq_mode = 0;
+ ms_card->total_sec_cnt = 0;
+ ms_send_cmd(chip, PRO_STOP, WAIT_INT);
+
+ rtsx_write_register(chip, RBCTL, RB_FLUSH, RB_FLUSH);
+ }
+}
+
+static inline int ms_auto_tune_clock(struct rtsx_chip *chip)
+{
+ struct ms_info *ms_card = &(chip->ms_card);
+ int retval;
+
+ if (chip->asic_code) {
+ if (ms_card->ms_clock > 30)
+ ms_card->ms_clock -= 20;
+ } else {
+ if (ms_card->ms_clock == CLK_80)
+ ms_card->ms_clock = CLK_60;
+ else if (ms_card->ms_clock == CLK_60)
+ ms_card->ms_clock = CLK_40;
+ }
+
+ retval = ms_switch_clock(chip);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ return STATUS_SUCCESS;
+}
+
+static int mspro_rw_multi_sector(struct scsi_cmnd *srb,
+ struct rtsx_chip *chip, u32 start_sector,
+ u16 sector_cnt)
+{
+ struct ms_info *ms_card = &(chip->ms_card);
+ bool mode_2k = false;
+ int retval;
+ u16 count;
+ u8 val, trans_mode, rw_tpc, rw_cmd;
+
+ ms_set_err_code(chip, MS_NO_ERROR);
+
+ ms_card->cleanup_counter = 0;
+
+ if (CHK_MSHG(ms_card)) {
+ if ((start_sector % 4) || (sector_cnt % 4)) {
+ if (srb->sc_data_direction == DMA_FROM_DEVICE) {
+ rw_tpc = PRO_READ_LONG_DATA;
+ rw_cmd = PRO_READ_DATA;
+ } else {
+ rw_tpc = PRO_WRITE_LONG_DATA;
+ rw_cmd = PRO_WRITE_DATA;
+ }
+ } else {
+ if (srb->sc_data_direction == DMA_FROM_DEVICE) {
+ rw_tpc = PRO_READ_QUAD_DATA;
+ rw_cmd = PRO_READ_2K_DATA;
+ } else {
+ rw_tpc = PRO_WRITE_QUAD_DATA;
+ rw_cmd = PRO_WRITE_2K_DATA;
+ }
+ mode_2k = true;
+ }
+ } else {
+ if (srb->sc_data_direction == DMA_FROM_DEVICE) {
+ rw_tpc = PRO_READ_LONG_DATA;
+ rw_cmd = PRO_READ_DATA;
+ } else {
+ rw_tpc = PRO_WRITE_LONG_DATA;
+ rw_cmd = PRO_WRITE_DATA;
+ }
+ }
+
+ retval = ms_switch_clock(chip);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ if (srb->sc_data_direction == DMA_FROM_DEVICE)
+ trans_mode = MS_TM_AUTO_READ;
+ else
+ trans_mode = MS_TM_AUTO_WRITE;
+
+ retval = rtsx_read_register(chip, MS_TRANS_CFG, &val);
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+
+ if (ms_card->seq_mode) {
+ if ((ms_card->pre_dir != srb->sc_data_direction)
+ || ((ms_card->pre_sec_addr + ms_card->pre_sec_cnt) != start_sector)
+ || (mode_2k && (ms_card->seq_mode & MODE_512_SEQ))
+ || (!mode_2k && (ms_card->seq_mode & MODE_2K_SEQ))
+ || !(val & MS_INT_BREQ)
+ || ((ms_card->total_sec_cnt + sector_cnt) > 0xFE00)) {
+ ms_card->seq_mode = 0;
+ ms_card->total_sec_cnt = 0;
+ if (val & MS_INT_BREQ) {
+ retval = ms_send_cmd(chip, PRO_STOP, WAIT_INT);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ rtsx_write_register(chip, RBCTL, RB_FLUSH, RB_FLUSH);
+ }
+ }
+ }
+
+ if (!ms_card->seq_mode) {
+ ms_card->total_sec_cnt = 0;
+ if (sector_cnt >= SEQ_START_CRITERIA) {
+ if ((ms_card->capacity - start_sector) > 0xFE00)
+ count = 0xFE00;
+ else
+ count = (u16)(ms_card->capacity - start_sector);
+
+ if (count > sector_cnt) {
+ if (mode_2k)
+ ms_card->seq_mode = MODE_2K_SEQ;
+ else
+ ms_card->seq_mode = MODE_512_SEQ;
+ }
+ } else {
+ count = sector_cnt;
+ }
+ retval = mspro_set_rw_cmd(chip, start_sector, count, rw_cmd);
+ if (retval != STATUS_SUCCESS) {
+ ms_card->seq_mode = 0;
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+ }
+
+ retval = ms_transfer_data(chip, trans_mode, rw_tpc, sector_cnt,
+ WAIT_INT, mode_2k, scsi_sg_count(srb),
+ scsi_sglist(srb), scsi_bufflen(srb));
+ if (retval != STATUS_SUCCESS) {
+ ms_card->seq_mode = 0;
+ rtsx_read_register(chip, MS_TRANS_CFG, &val);
+ rtsx_clear_ms_error(chip);
+
+ if (detect_card_cd(chip, MS_CARD) != STATUS_SUCCESS) {
+ chip->rw_need_retry = 0;
+ dev_dbg(rtsx_dev(chip), "No card exist, exit mspro_rw_multi_sector\n");
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ if (val & MS_INT_BREQ)
+ ms_send_cmd(chip, PRO_STOP, WAIT_INT);
+
+ if (val & (MS_CRC16_ERR | MS_RDY_TIMEOUT)) {
+ dev_dbg(rtsx_dev(chip), "MSPro CRC error, tune clock!\n");
+ chip->rw_need_retry = 1;
+ ms_auto_tune_clock(chip);
+ }
+
+ rtsx_trace(chip);
+ return retval;
+ }
+
+ if (ms_card->seq_mode) {
+ ms_card->pre_sec_addr = start_sector;
+ ms_card->pre_sec_cnt = sector_cnt;
+ ms_card->pre_dir = srb->sc_data_direction;
+ ms_card->total_sec_cnt += sector_cnt;
+ }
+
+ return STATUS_SUCCESS;
+}
+
+static int mspro_read_format_progress(struct rtsx_chip *chip,
+ const int short_data_len)
+{
+ struct ms_info *ms_card = &(chip->ms_card);
+ int retval, i;
+ u32 total_progress, cur_progress;
+ u8 cnt, tmp;
+ u8 data[8];
+
+ dev_dbg(rtsx_dev(chip), "mspro_read_format_progress, short_data_len = %d\n",
+ short_data_len);
+
+ retval = ms_switch_clock(chip);
+ if (retval != STATUS_SUCCESS) {
+ ms_card->format_status = FORMAT_FAIL;
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ retval = rtsx_read_register(chip, MS_TRANS_CFG, &tmp);
+ if (retval != STATUS_SUCCESS) {
+ ms_card->format_status = FORMAT_FAIL;
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ if (!(tmp & MS_INT_BREQ)) {
+ if ((tmp & (MS_INT_CED | MS_INT_BREQ | MS_INT_CMDNK | MS_INT_ERR)) == MS_INT_CED) {
+ ms_card->format_status = FORMAT_SUCCESS;
+ return STATUS_SUCCESS;
+ }
+ ms_card->format_status = FORMAT_FAIL;
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ if (short_data_len >= 256)
+ cnt = 0;
+ else
+ cnt = (u8)short_data_len;
+
+ retval = rtsx_write_register(chip, MS_CFG, MS_NO_CHECK_INT,
+ MS_NO_CHECK_INT);
+ if (retval != STATUS_SUCCESS) {
+ ms_card->format_status = FORMAT_FAIL;
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ retval = ms_read_bytes(chip, PRO_READ_SHORT_DATA, cnt, WAIT_INT,
+ data, 8);
+ if (retval != STATUS_SUCCESS) {
+ ms_card->format_status = FORMAT_FAIL;
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ total_progress = (data[0] << 24) | (data[1] << 16) |
+ (data[2] << 8) | data[3];
+ cur_progress = (data[4] << 24) | (data[5] << 16) |
+ (data[6] << 8) | data[7];
+
+ dev_dbg(rtsx_dev(chip), "total_progress = %d, cur_progress = %d\n",
+ total_progress, cur_progress);
+
+ if (total_progress == 0) {
+ ms_card->progress = 0;
+ } else {
+ u64 ulltmp = (u64)cur_progress * (u64)65535;
+
+ do_div(ulltmp, total_progress);
+ ms_card->progress = (u16)ulltmp;
+ }
+ dev_dbg(rtsx_dev(chip), "progress = %d\n", ms_card->progress);
+
+ for (i = 0; i < 5000; i++) {
+ retval = rtsx_read_register(chip, MS_TRANS_CFG, &tmp);
+ if (retval != STATUS_SUCCESS) {
+ ms_card->format_status = FORMAT_FAIL;
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+ if (tmp & (MS_INT_CED | MS_INT_CMDNK |
+ MS_INT_BREQ | MS_INT_ERR))
+ break;
+
+ wait_timeout(1);
+ }
+
+ retval = rtsx_write_register(chip, MS_CFG, MS_NO_CHECK_INT, 0);
+ if (retval != STATUS_SUCCESS) {
+ ms_card->format_status = FORMAT_FAIL;
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ if (i == 5000) {
+ ms_card->format_status = FORMAT_FAIL;
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ if (tmp & (MS_INT_CMDNK | MS_INT_ERR)) {
+ ms_card->format_status = FORMAT_FAIL;
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ if (tmp & MS_INT_CED) {
+ ms_card->format_status = FORMAT_SUCCESS;
+ ms_card->pro_under_formatting = 0;
+ } else if (tmp & MS_INT_BREQ) {
+ ms_card->format_status = FORMAT_IN_PROGRESS;
+ } else {
+ ms_card->format_status = FORMAT_FAIL;
+ ms_card->pro_under_formatting = 0;
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ return STATUS_SUCCESS;
+}
+
+void mspro_polling_format_status(struct rtsx_chip *chip)
+{
+ struct ms_info *ms_card = &(chip->ms_card);
+ int i;
+
+ if (ms_card->pro_under_formatting &&
+ (rtsx_get_stat(chip) != RTSX_STAT_SS)) {
+ rtsx_set_stat(chip, RTSX_STAT_RUN);
+
+ for (i = 0; i < 65535; i++) {
+ mspro_read_format_progress(chip, MS_SHORT_DATA_LEN);
+ if (ms_card->format_status != FORMAT_IN_PROGRESS)
+ break;
+ }
+ }
+}
+
+int mspro_format(struct scsi_cmnd *srb, struct rtsx_chip *chip,
+ int short_data_len, bool quick_format)
+{
+ struct ms_info *ms_card = &(chip->ms_card);
+ int retval, i;
+ u8 buf[8], tmp;
+ u16 para;
+
+ retval = ms_switch_clock(chip);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ retval = ms_set_rw_reg_addr(chip, 0x00, 0x00, Pro_TPCParm, 0x01);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ memset(buf, 0, 2);
+ switch (short_data_len) {
+ case 32:
+ buf[0] = 0;
+ break;
+ case 64:
+ buf[0] = 1;
+ break;
+ case 128:
+ buf[0] = 2;
+ break;
+ case 256:
+ default:
+ buf[0] = 3;
+ break;
+ }
+
+ for (i = 0; i < MS_MAX_RETRY_COUNT; i++) {
+ retval = ms_write_bytes(chip, PRO_WRITE_REG, 1,
+ NO_WAIT_INT, buf, 2);
+ if (retval == STATUS_SUCCESS)
+ break;
+ }
+ if (i == MS_MAX_RETRY_COUNT) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ if (quick_format)
+ para = 0x0000;
+ else
+ para = 0x0001;
+
+ retval = mspro_set_rw_cmd(chip, 0, para, PRO_FORMAT);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ retval = rtsx_read_register(chip, MS_TRANS_CFG, &tmp);
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+
+ if (tmp & (MS_INT_CMDNK | MS_INT_ERR)) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ if ((tmp & (MS_INT_BREQ | MS_INT_CED)) == MS_INT_BREQ) {
+ ms_card->pro_under_formatting = 1;
+ ms_card->progress = 0;
+ ms_card->format_status = FORMAT_IN_PROGRESS;
+ return STATUS_SUCCESS;
+ }
+
+ if (tmp & MS_INT_CED) {
+ ms_card->pro_under_formatting = 0;
+ ms_card->progress = 0;
+ ms_card->format_status = FORMAT_SUCCESS;
+ set_sense_type(chip, SCSI_LUN(srb), SENSE_TYPE_NO_SENSE);
+ return STATUS_SUCCESS;
+ }
+
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+}
+
+
+static int ms_read_multiple_pages(struct rtsx_chip *chip, u16 phy_blk,
+ u16 log_blk, u8 start_page, u8 end_page,
+ u8 *buf, unsigned int *index,
+ unsigned int *offset)
+{
+ struct ms_info *ms_card = &(chip->ms_card);
+ int retval, i;
+ u8 extra[MS_EXTRA_SIZE], page_addr, val, trans_cfg, data[6];
+ u8 *ptr;
+
+ retval = ms_read_extra_data(chip, phy_blk, start_page,
+ extra, MS_EXTRA_SIZE);
+ if (retval == STATUS_SUCCESS) {
+ if ((extra[1] & 0x30) != 0x30) {
+ ms_set_err_code(chip, MS_FLASH_READ_ERROR);
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+ }
+
+ retval = ms_set_rw_reg_addr(chip, OverwriteFlag, MS_EXTRA_SIZE,
+ SystemParm, 6);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ if (CHK_MS4BIT(ms_card))
+ data[0] = 0x88;
+ else
+ data[0] = 0x80;
+
+ data[1] = 0;
+ data[2] = (u8)(phy_blk >> 8);
+ data[3] = (u8)phy_blk;
+ data[4] = 0;
+ data[5] = start_page;
+
+ for (i = 0; i < MS_MAX_RETRY_COUNT; i++) {
+ retval = ms_write_bytes(chip, WRITE_REG, 6, NO_WAIT_INT,
+ data, 6);
+ if (retval == STATUS_SUCCESS)
+ break;
+ }
+ if (i == MS_MAX_RETRY_COUNT) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ ms_set_err_code(chip, MS_NO_ERROR);
+
+ retval = ms_send_cmd(chip, BLOCK_READ, WAIT_INT);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ ptr = buf;
+
+ for (page_addr = start_page; page_addr < end_page; page_addr++) {
+ ms_set_err_code(chip, MS_NO_ERROR);
+
+ if (detect_card_cd(chip, MS_CARD) != STATUS_SUCCESS) {
+ ms_set_err_code(chip, MS_NO_CARD);
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ retval = ms_read_bytes(chip, GET_INT, 1, NO_WAIT_INT, &val, 1);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ if (val & INT_REG_CMDNK) {
+ ms_set_err_code(chip, MS_CMD_NK);
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+ if (val & INT_REG_ERR) {
+ if (val & INT_REG_BREQ) {
+ retval = ms_read_status_reg(chip);
+ if (retval != STATUS_SUCCESS) {
+ if (!(chip->card_wp & MS_CARD)) {
+ reset_ms(chip);
+ ms_set_page_status(log_blk, setPS_NG, extra, MS_EXTRA_SIZE);
+ ms_write_extra_data(chip, phy_blk,
+ page_addr, extra, MS_EXTRA_SIZE);
+ }
+ ms_set_err_code(chip, MS_FLASH_READ_ERROR);
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+ } else {
+ ms_set_err_code(chip, MS_FLASH_READ_ERROR);
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+ } else {
+ if (!(val & INT_REG_BREQ)) {
+ ms_set_err_code(chip, MS_BREQ_ERROR);
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+ }
+
+ if (page_addr == (end_page - 1)) {
+ if (!(val & INT_REG_CED)) {
+ retval = ms_send_cmd(chip, BLOCK_END, WAIT_INT);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+ }
+
+ retval = ms_read_bytes(chip, GET_INT, 1, NO_WAIT_INT,
+ &val, 1);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ if (!(val & INT_REG_CED)) {
+ ms_set_err_code(chip, MS_FLASH_READ_ERROR);
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ trans_cfg = NO_WAIT_INT;
+ } else {
+ trans_cfg = WAIT_INT;
+ }
+
+ rtsx_init_cmd(chip);
+
+ rtsx_add_cmd(chip, WRITE_REG_CMD, MS_TPC, 0xFF, READ_PAGE_DATA);
+ rtsx_add_cmd(chip, WRITE_REG_CMD, MS_TRANS_CFG,
+ 0xFF, trans_cfg);
+ rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_DATA_SOURCE,
+ 0x01, RING_BUFFER);
+
+ trans_dma_enable(DMA_FROM_DEVICE, chip, 512, DMA_512);
+
+ rtsx_add_cmd(chip, WRITE_REG_CMD, MS_TRANSFER, 0xFF,
+ MS_TRANSFER_START | MS_TM_NORMAL_READ);
+ rtsx_add_cmd(chip, CHECK_REG_CMD, MS_TRANSFER,
+ MS_TRANSFER_END, MS_TRANSFER_END);
+
+ rtsx_send_cmd_no_wait(chip);
+
+ retval = rtsx_transfer_data_partial(chip, MS_CARD, ptr,
+ 512, scsi_sg_count(chip->srb),
+ index, offset, DMA_FROM_DEVICE,
+ chip->ms_timeout);
+ if (retval < 0) {
+ if (retval == -ETIMEDOUT) {
+ ms_set_err_code(chip, MS_TO_ERROR);
+ rtsx_clear_ms_error(chip);
+ rtsx_trace(chip);
+ return STATUS_TIMEDOUT;
+ }
+
+ retval = rtsx_read_register(chip, MS_TRANS_CFG, &val);
+ if (retval != STATUS_SUCCESS) {
+ ms_set_err_code(chip, MS_TO_ERROR);
+ rtsx_clear_ms_error(chip);
+ rtsx_trace(chip);
+ return STATUS_TIMEDOUT;
+ }
+ if (val & (MS_CRC16_ERR | MS_RDY_TIMEOUT)) {
+ ms_set_err_code(chip, MS_CRC16_ERROR);
+ rtsx_clear_ms_error(chip);
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+ }
+
+ if (scsi_sg_count(chip->srb) == 0)
+ ptr += 512;
+ }
+
+ return STATUS_SUCCESS;
+}
+
+static int ms_write_multiple_pages(struct rtsx_chip *chip, u16 old_blk,
+ u16 new_blk, u16 log_blk, u8 start_page,
+ u8 end_page, u8 *buf, unsigned int *index,
+ unsigned int *offset)
+{
+ struct ms_info *ms_card = &(chip->ms_card);
+ int retval, i;
+ u8 page_addr, val, data[16];
+ u8 *ptr;
+
+ if (!start_page) {
+ retval = ms_set_rw_reg_addr(chip, OverwriteFlag, MS_EXTRA_SIZE,
+ SystemParm, 7);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ if (CHK_MS4BIT(ms_card))
+ data[0] = 0x88;
+ else
+ data[0] = 0x80;
+
+ data[1] = 0;
+ data[2] = (u8)(old_blk >> 8);
+ data[3] = (u8)old_blk;
+ data[4] = 0x80;
+ data[5] = 0;
+ data[6] = 0xEF;
+ data[7] = 0xFF;
+
+ retval = ms_write_bytes(chip, WRITE_REG, 7, NO_WAIT_INT,
+ data, 8);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ retval = ms_send_cmd(chip, BLOCK_WRITE, WAIT_INT);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ ms_set_err_code(chip, MS_NO_ERROR);
+ retval = ms_transfer_tpc(chip, MS_TM_READ_BYTES, GET_INT, 1,
+ NO_WAIT_INT);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+ }
+
+ retval = ms_set_rw_reg_addr(chip, OverwriteFlag, MS_EXTRA_SIZE,
+ SystemParm, (6 + MS_EXTRA_SIZE));
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ ms_set_err_code(chip, MS_NO_ERROR);
+
+ if (CHK_MS4BIT(ms_card))
+ data[0] = 0x88;
+ else
+ data[0] = 0x80;
+
+ data[1] = 0;
+ data[2] = (u8)(new_blk >> 8);
+ data[3] = (u8)new_blk;
+ if ((end_page - start_page) == 1)
+ data[4] = 0x20;
+ else
+ data[4] = 0;
+
+ data[5] = start_page;
+ data[6] = 0xF8;
+ data[7] = 0xFF;
+ data[8] = (u8)(log_blk >> 8);
+ data[9] = (u8)log_blk;
+
+ for (i = 0x0A; i < 0x10; i++)
+ data[i] = 0xFF;
+
+ for (i = 0; i < MS_MAX_RETRY_COUNT; i++) {
+ retval = ms_write_bytes(chip, WRITE_REG, 6 + MS_EXTRA_SIZE,
+ NO_WAIT_INT, data, 16);
+ if (retval == STATUS_SUCCESS)
+ break;
+ }
+ if (i == MS_MAX_RETRY_COUNT) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ for (i = 0; i < MS_MAX_RETRY_COUNT; i++) {
+ retval = ms_send_cmd(chip, BLOCK_WRITE, WAIT_INT);
+ if (retval == STATUS_SUCCESS)
+ break;
+ }
+ if (i == MS_MAX_RETRY_COUNT) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ retval = ms_read_bytes(chip, GET_INT, 1, NO_WAIT_INT, &val, 1);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ ptr = buf;
+ for (page_addr = start_page; page_addr < end_page; page_addr++) {
+ ms_set_err_code(chip, MS_NO_ERROR);
+
+ if (detect_card_cd(chip, MS_CARD) != STATUS_SUCCESS) {
+ ms_set_err_code(chip, MS_NO_CARD);
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ if (val & INT_REG_CMDNK) {
+ ms_set_err_code(chip, MS_CMD_NK);
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+ if (val & INT_REG_ERR) {
+ ms_set_err_code(chip, MS_FLASH_WRITE_ERROR);
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+ if (!(val & INT_REG_BREQ)) {
+ ms_set_err_code(chip, MS_BREQ_ERROR);
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ udelay(30);
+
+ rtsx_init_cmd(chip);
+
+ rtsx_add_cmd(chip, WRITE_REG_CMD, MS_TPC,
+ 0xFF, WRITE_PAGE_DATA);
+ rtsx_add_cmd(chip, WRITE_REG_CMD, MS_TRANS_CFG,
+ 0xFF, WAIT_INT);
+ rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_DATA_SOURCE,
+ 0x01, RING_BUFFER);
+
+ trans_dma_enable(DMA_TO_DEVICE, chip, 512, DMA_512);
+
+ rtsx_add_cmd(chip, WRITE_REG_CMD, MS_TRANSFER, 0xFF,
+ MS_TRANSFER_START | MS_TM_NORMAL_WRITE);
+ rtsx_add_cmd(chip, CHECK_REG_CMD, MS_TRANSFER,
+ MS_TRANSFER_END, MS_TRANSFER_END);
+
+ rtsx_send_cmd_no_wait(chip);
+
+ retval = rtsx_transfer_data_partial(chip, MS_CARD, ptr,
+ 512, scsi_sg_count(chip->srb),
+ index, offset, DMA_TO_DEVICE,
+ chip->ms_timeout);
+ if (retval < 0) {
+ ms_set_err_code(chip, MS_TO_ERROR);
+ rtsx_clear_ms_error(chip);
+
+ if (retval == -ETIMEDOUT) {
+ rtsx_trace(chip);
+ return STATUS_TIMEDOUT;
+ }
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ retval = ms_read_bytes(chip, GET_INT, 1, NO_WAIT_INT, &val, 1);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ if ((end_page - start_page) == 1) {
+ if (!(val & INT_REG_CED)) {
+ ms_set_err_code(chip, MS_FLASH_WRITE_ERROR);
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+ } else {
+ if (page_addr == (end_page - 1)) {
+ if (!(val & INT_REG_CED)) {
+ retval = ms_send_cmd(chip, BLOCK_END,
+ WAIT_INT);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+ }
+
+ retval = ms_read_bytes(chip, GET_INT, 1,
+ NO_WAIT_INT, &val, 1);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+ }
+
+ if ((page_addr == (end_page - 1)) ||
+ (page_addr == ms_card->page_off)) {
+ if (!(val & INT_REG_CED)) {
+ ms_set_err_code(chip,
+ MS_FLASH_WRITE_ERROR);
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+ }
+ }
+
+ if (scsi_sg_count(chip->srb) == 0)
+ ptr += 512;
+ }
+
+ return STATUS_SUCCESS;
+}
+
+
+static int ms_finish_write(struct rtsx_chip *chip, u16 old_blk, u16 new_blk,
+ u16 log_blk, u8 page_off)
+{
+ struct ms_info *ms_card = &(chip->ms_card);
+ int retval, seg_no;
+
+ retval = ms_copy_page(chip, old_blk, new_blk, log_blk,
+ page_off, ms_card->page_off + 1);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ seg_no = old_blk >> 9;
+
+ if (MS_TST_BAD_BLOCK_FLG(ms_card)) {
+ MS_CLR_BAD_BLOCK_FLG(ms_card);
+ ms_set_bad_block(chip, old_blk);
+ } else {
+ retval = ms_erase_block(chip, old_blk);
+ if (retval == STATUS_SUCCESS)
+ ms_set_unused_block(chip, old_blk);
+ }
+
+ ms_set_l2p_tbl(chip, seg_no, log_blk - ms_start_idx[seg_no], new_blk);
+
+ return STATUS_SUCCESS;
+}
+
+static int ms_prepare_write(struct rtsx_chip *chip, u16 old_blk, u16 new_blk,
+ u16 log_blk, u8 start_page)
+{
+ int retval;
+
+ if (start_page) {
+ retval = ms_copy_page(chip, old_blk, new_blk, log_blk,
+ 0, start_page);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+ }
+
+ return STATUS_SUCCESS;
+}
+
+#ifdef MS_DELAY_WRITE
+int ms_delay_write(struct rtsx_chip *chip)
+{
+ struct ms_info *ms_card = &(chip->ms_card);
+ struct ms_delay_write_tag *delay_write = &(ms_card->delay_write);
+ int retval;
+
+ if (delay_write->delay_write_flag) {
+ retval = ms_set_init_para(chip);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ delay_write->delay_write_flag = 0;
+ retval = ms_finish_write(chip,
+ delay_write->old_phyblock,
+ delay_write->new_phyblock,
+ delay_write->logblock,
+ delay_write->pageoff);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+ }
+
+ return STATUS_SUCCESS;
+}
+#endif
+
+static inline void ms_rw_fail(struct scsi_cmnd *srb, struct rtsx_chip *chip)
+{
+ if (srb->sc_data_direction == DMA_FROM_DEVICE)
+ set_sense_type(chip, SCSI_LUN(srb),
+ SENSE_TYPE_MEDIA_UNRECOVER_READ_ERR);
+ else
+ set_sense_type(chip, SCSI_LUN(srb), SENSE_TYPE_MEDIA_WRITE_ERR);
+}
+
+static int ms_rw_multi_sector(struct scsi_cmnd *srb, struct rtsx_chip *chip,
+ u32 start_sector, u16 sector_cnt)
+{
+ struct ms_info *ms_card = &(chip->ms_card);
+ unsigned int lun = SCSI_LUN(srb);
+ int retval, seg_no;
+ unsigned int index = 0, offset = 0;
+ u16 old_blk = 0, new_blk = 0, log_blk, total_sec_cnt = sector_cnt;
+ u8 start_page, end_page = 0, page_cnt;
+ u8 *ptr;
+#ifdef MS_DELAY_WRITE
+ struct ms_delay_write_tag *delay_write = &(ms_card->delay_write);
+#endif
+
+ ms_set_err_code(chip, MS_NO_ERROR);
+
+ ms_card->cleanup_counter = 0;
+
+ ptr = (u8 *)scsi_sglist(srb);
+
+ retval = ms_switch_clock(chip);
+ if (retval != STATUS_SUCCESS) {
+ ms_rw_fail(srb, chip);
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ log_blk = (u16)(start_sector >> ms_card->block_shift);
+ start_page = (u8)(start_sector & ms_card->page_off);
+
+ for (seg_no = 0; seg_no < ARRAY_SIZE(ms_start_idx) - 1; seg_no++) {
+ if (log_blk < ms_start_idx[seg_no+1])
+ break;
+ }
+
+ if (ms_card->segment[seg_no].build_flag == 0) {
+ retval = ms_build_l2p_tbl(chip, seg_no);
+ if (retval != STATUS_SUCCESS) {
+ chip->card_fail |= MS_CARD;
+ set_sense_type(chip, lun, SENSE_TYPE_MEDIA_NOT_PRESENT);
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+ }
+
+ if (srb->sc_data_direction == DMA_TO_DEVICE) {
+#ifdef MS_DELAY_WRITE
+ if (delay_write->delay_write_flag &&
+ (delay_write->logblock == log_blk) &&
+ (start_page > delay_write->pageoff)) {
+ delay_write->delay_write_flag = 0;
+ retval = ms_copy_page(chip,
+ delay_write->old_phyblock,
+ delay_write->new_phyblock, log_blk,
+ delay_write->pageoff, start_page);
+ if (retval != STATUS_SUCCESS) {
+ set_sense_type(chip, lun,
+ SENSE_TYPE_MEDIA_WRITE_ERR);
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+ old_blk = delay_write->old_phyblock;
+ new_blk = delay_write->new_phyblock;
+ } else if (delay_write->delay_write_flag &&
+ (delay_write->logblock == log_blk) &&
+ (start_page == delay_write->pageoff)) {
+ delay_write->delay_write_flag = 0;
+ old_blk = delay_write->old_phyblock;
+ new_blk = delay_write->new_phyblock;
+ } else {
+ retval = ms_delay_write(chip);
+ if (retval != STATUS_SUCCESS) {
+ set_sense_type(chip, lun,
+ SENSE_TYPE_MEDIA_WRITE_ERR);
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+#endif
+ old_blk = ms_get_l2p_tbl(chip, seg_no,
+ log_blk - ms_start_idx[seg_no]);
+ new_blk = ms_get_unused_block(chip, seg_no);
+ if ((old_blk == 0xFFFF) || (new_blk == 0xFFFF)) {
+ set_sense_type(chip, lun,
+ SENSE_TYPE_MEDIA_WRITE_ERR);
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ retval = ms_prepare_write(chip, old_blk, new_blk,
+ log_blk, start_page);
+ if (retval != STATUS_SUCCESS) {
+ if (detect_card_cd(chip, MS_CARD) != STATUS_SUCCESS) {
+ set_sense_type(chip, lun,
+ SENSE_TYPE_MEDIA_NOT_PRESENT);
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+ set_sense_type(chip, lun,
+ SENSE_TYPE_MEDIA_WRITE_ERR);
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+#ifdef MS_DELAY_WRITE
+ }
+#endif
+ } else {
+#ifdef MS_DELAY_WRITE
+ retval = ms_delay_write(chip);
+ if (retval != STATUS_SUCCESS) {
+ if (detect_card_cd(chip, MS_CARD) != STATUS_SUCCESS) {
+ set_sense_type(chip, lun,
+ SENSE_TYPE_MEDIA_NOT_PRESENT);
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+ set_sense_type(chip, lun,
+ SENSE_TYPE_MEDIA_UNRECOVER_READ_ERR);
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+#endif
+ old_blk = ms_get_l2p_tbl(chip, seg_no,
+ log_blk - ms_start_idx[seg_no]);
+ if (old_blk == 0xFFFF) {
+ set_sense_type(chip, lun,
+ SENSE_TYPE_MEDIA_UNRECOVER_READ_ERR);
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+ }
+
+ dev_dbg(rtsx_dev(chip), "seg_no = %d, old_blk = 0x%x, new_blk = 0x%x\n",
+ seg_no, old_blk, new_blk);
+
+ while (total_sec_cnt) {
+ if ((start_page + total_sec_cnt) > (ms_card->page_off + 1))
+ end_page = ms_card->page_off + 1;
+ else
+ end_page = start_page + (u8)total_sec_cnt;
+
+ page_cnt = end_page - start_page;
+
+ dev_dbg(rtsx_dev(chip), "start_page = %d, end_page = %d, page_cnt = %d\n",
+ start_page, end_page, page_cnt);
+
+ if (srb->sc_data_direction == DMA_FROM_DEVICE) {
+ retval = ms_read_multiple_pages(chip,
+ old_blk, log_blk, start_page, end_page,
+ ptr, &index, &offset);
+ } else {
+ retval = ms_write_multiple_pages(chip, old_blk,
+ new_blk, log_blk, start_page, end_page,
+ ptr, &index, &offset);
+ }
+
+ if (retval != STATUS_SUCCESS) {
+ toggle_gpio(chip, 1);
+ if (detect_card_cd(chip, MS_CARD) != STATUS_SUCCESS) {
+ set_sense_type(chip, lun,
+ SENSE_TYPE_MEDIA_NOT_PRESENT);
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+ ms_rw_fail(srb, chip);
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ if (srb->sc_data_direction == DMA_TO_DEVICE) {
+ if (end_page == (ms_card->page_off + 1)) {
+ retval = ms_erase_block(chip, old_blk);
+ if (retval == STATUS_SUCCESS)
+ ms_set_unused_block(chip, old_blk);
+
+ ms_set_l2p_tbl(chip, seg_no,
+ log_blk - ms_start_idx[seg_no],
+ new_blk);
+ }
+ }
+
+ total_sec_cnt -= page_cnt;
+ if (scsi_sg_count(srb) == 0)
+ ptr += page_cnt * 512;
+
+ if (total_sec_cnt == 0)
+ break;
+
+ log_blk++;
+
+ for (seg_no = 0; seg_no < ARRAY_SIZE(ms_start_idx) - 1;
+ seg_no++) {
+ if (log_blk < ms_start_idx[seg_no+1])
+ break;
+ }
+
+ if (ms_card->segment[seg_no].build_flag == 0) {
+ retval = ms_build_l2p_tbl(chip, seg_no);
+ if (retval != STATUS_SUCCESS) {
+ chip->card_fail |= MS_CARD;
+ set_sense_type(chip, lun,
+ SENSE_TYPE_MEDIA_NOT_PRESENT);
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+ }
+
+ old_blk = ms_get_l2p_tbl(chip, seg_no,
+ log_blk - ms_start_idx[seg_no]);
+ if (old_blk == 0xFFFF) {
+ ms_rw_fail(srb, chip);
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ if (srb->sc_data_direction == DMA_TO_DEVICE) {
+ new_blk = ms_get_unused_block(chip, seg_no);
+ if (new_blk == 0xFFFF) {
+ ms_rw_fail(srb, chip);
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+ }
+
+ dev_dbg(rtsx_dev(chip), "seg_no = %d, old_blk = 0x%x, new_blk = 0x%x\n",
+ seg_no, old_blk, new_blk);
+
+ start_page = 0;
+ }
+
+ if (srb->sc_data_direction == DMA_TO_DEVICE) {
+ if (end_page < (ms_card->page_off + 1)) {
+#ifdef MS_DELAY_WRITE
+ delay_write->delay_write_flag = 1;
+ delay_write->old_phyblock = old_blk;
+ delay_write->new_phyblock = new_blk;
+ delay_write->logblock = log_blk;
+ delay_write->pageoff = end_page;
+#else
+ retval = ms_finish_write(chip, old_blk, new_blk,
+ log_blk, end_page);
+ if (retval != STATUS_SUCCESS) {
+ if (detect_card_cd(chip, MS_CARD) != STATUS_SUCCESS) {
+ set_sense_type(chip, lun,
+ SENSE_TYPE_MEDIA_NOT_PRESENT);
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ ms_rw_fail(srb, chip);
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+#endif
+ }
+ }
+
+ scsi_set_resid(srb, 0);
+
+ return STATUS_SUCCESS;
+}
+
+int ms_rw(struct scsi_cmnd *srb, struct rtsx_chip *chip,
+ u32 start_sector, u16 sector_cnt)
+{
+ struct ms_info *ms_card = &(chip->ms_card);
+ int retval;
+
+ if (CHK_MSPRO(ms_card))
+ retval = mspro_rw_multi_sector(srb, chip, start_sector,
+ sector_cnt);
+ else
+ retval = ms_rw_multi_sector(srb, chip, start_sector,
+ sector_cnt);
+
+ return retval;
+}
+
+
+void ms_free_l2p_tbl(struct rtsx_chip *chip)
+{
+ struct ms_info *ms_card = &(chip->ms_card);
+ int i = 0;
+
+ if (ms_card->segment != NULL) {
+ for (i = 0; i < ms_card->segment_cnt; i++) {
+ if (ms_card->segment[i].l2p_table != NULL) {
+ vfree(ms_card->segment[i].l2p_table);
+ ms_card->segment[i].l2p_table = NULL;
+ }
+ if (ms_card->segment[i].free_table != NULL) {
+ vfree(ms_card->segment[i].free_table);
+ ms_card->segment[i].free_table = NULL;
+ }
+ }
+ vfree(ms_card->segment);
+ ms_card->segment = NULL;
+ }
+}
+
+#ifdef SUPPORT_MAGIC_GATE
+
+#ifdef READ_BYTES_WAIT_INT
+static int ms_poll_int(struct rtsx_chip *chip)
+{
+ int retval;
+ u8 val;
+
+ rtsx_init_cmd(chip);
+
+ rtsx_add_cmd(chip, CHECK_REG_CMD, MS_TRANS_CFG, MS_INT_CED, MS_INT_CED);
+
+ retval = rtsx_send_cmd(chip, MS_CARD, 5000);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ val = *rtsx_get_cmd_data(chip);
+ if (val & MS_INT_ERR) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ return STATUS_SUCCESS;
+}
+#endif
+
+#ifdef MS_SAMPLE_INT_ERR
+static int check_ms_err(struct rtsx_chip *chip)
+{
+ int retval;
+ u8 val;
+
+ retval = rtsx_read_register(chip, MS_TRANSFER, &val);
+ if (retval != STATUS_SUCCESS)
+ return 1;
+ if (val & MS_TRANSFER_ERR)
+ return 1;
+
+ retval = rtsx_read_register(chip, MS_TRANS_CFG, &val);
+ if (retval != STATUS_SUCCESS)
+ return 1;
+
+ if (val & (MS_INT_ERR | MS_INT_CMDNK))
+ return 1;
+
+ return 0;
+}
+#else
+static int check_ms_err(struct rtsx_chip *chip)
+{
+ int retval;
+ u8 val;
+
+ retval = rtsx_read_register(chip, MS_TRANSFER, &val);
+ if (retval != STATUS_SUCCESS)
+ return 1;
+ if (val & MS_TRANSFER_ERR)
+ return 1;
+
+ return 0;
+}
+#endif
+
+static int mg_send_ex_cmd(struct rtsx_chip *chip, u8 cmd, u8 entry_num)
+{
+ int retval, i;
+ u8 data[8];
+
+ data[0] = cmd;
+ data[1] = 0;
+ data[2] = 0;
+ data[3] = 0;
+ data[4] = 0;
+ data[5] = 0;
+ data[6] = entry_num;
+ data[7] = 0;
+
+ for (i = 0; i < MS_MAX_RETRY_COUNT; i++) {
+ retval = ms_write_bytes(chip, PRO_EX_SET_CMD, 7, WAIT_INT,
+ data, 8);
+ if (retval == STATUS_SUCCESS)
+ break;
+ }
+ if (i == MS_MAX_RETRY_COUNT) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ if (check_ms_err(chip)) {
+ rtsx_clear_ms_error(chip);
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ return STATUS_SUCCESS;
+}
+
+static int mg_set_tpc_para_sub(struct rtsx_chip *chip, int type,
+ u8 mg_entry_num)
+{
+ int retval;
+ u8 buf[6];
+
+ if (type == 0)
+ retval = ms_set_rw_reg_addr(chip, 0, 0, Pro_TPCParm, 1);
+ else
+ retval = ms_set_rw_reg_addr(chip, 0, 0, Pro_DataCount1, 6);
+
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ buf[0] = 0;
+ buf[1] = 0;
+ if (type == 1) {
+ buf[2] = 0;
+ buf[3] = 0;
+ buf[4] = 0;
+ buf[5] = mg_entry_num;
+ }
+ retval = ms_write_bytes(chip, PRO_WRITE_REG, (type == 0) ? 1 : 6,
+ NO_WAIT_INT, buf, 6);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ return STATUS_SUCCESS;
+}
+
+int mg_set_leaf_id(struct scsi_cmnd *srb, struct rtsx_chip *chip)
+{
+ int retval;
+ int i;
+ unsigned int lun = SCSI_LUN(srb);
+ u8 buf1[32], buf2[12];
+
+ if (scsi_bufflen(srb) < 12) {
+ set_sense_type(chip, lun, SENSE_TYPE_MEDIA_INVALID_CMD_FIELD);
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ ms_cleanup_work(chip);
+
+ retval = ms_switch_clock(chip);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ retval = mg_send_ex_cmd(chip, MG_SET_LID, 0);
+ if (retval != STATUS_SUCCESS) {
+ set_sense_type(chip, lun, SENSE_TYPE_MG_KEY_FAIL_NOT_ESTAB);
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ memset(buf1, 0, 32);
+ rtsx_stor_get_xfer_buf(buf2, min_t(int, 12, scsi_bufflen(srb)), srb);
+ for (i = 0; i < 8; i++)
+ buf1[8+i] = buf2[4+i];
+
+ retval = ms_write_bytes(chip, PRO_WRITE_SHORT_DATA, 32, WAIT_INT,
+ buf1, 32);
+ if (retval != STATUS_SUCCESS) {
+ set_sense_type(chip, lun, SENSE_TYPE_MG_KEY_FAIL_NOT_ESTAB);
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+ if (check_ms_err(chip)) {
+ set_sense_type(chip, lun, SENSE_TYPE_MG_KEY_FAIL_NOT_ESTAB);
+ rtsx_clear_ms_error(chip);
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ return STATUS_SUCCESS;
+}
+
+int mg_get_local_EKB(struct scsi_cmnd *srb, struct rtsx_chip *chip)
+{
+ int retval = STATUS_FAIL;
+ int bufflen;
+ unsigned int lun = SCSI_LUN(srb);
+ u8 *buf = NULL;
+
+ ms_cleanup_work(chip);
+
+ retval = ms_switch_clock(chip);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ buf = kmalloc(1540, GFP_KERNEL);
+ if (!buf) {
+ rtsx_trace(chip);
+ return STATUS_ERROR;
+ }
+
+ buf[0] = 0x04;
+ buf[1] = 0x1A;
+ buf[2] = 0x00;
+ buf[3] = 0x00;
+
+ retval = mg_send_ex_cmd(chip, MG_GET_LEKB, 0);
+ if (retval != STATUS_SUCCESS) {
+ set_sense_type(chip, lun, SENSE_TYPE_MG_KEY_FAIL_NOT_AUTHEN);
+ rtsx_trace(chip);
+ goto GetEKBFinish;
+ }
+
+ retval = ms_transfer_data(chip, MS_TM_AUTO_READ, PRO_READ_LONG_DATA,
+ 3, WAIT_INT, 0, 0, buf + 4, 1536);
+ if (retval != STATUS_SUCCESS) {
+ set_sense_type(chip, lun, SENSE_TYPE_MG_KEY_FAIL_NOT_AUTHEN);
+ rtsx_clear_ms_error(chip);
+ rtsx_trace(chip);
+ goto GetEKBFinish;
+ }
+ if (check_ms_err(chip)) {
+ set_sense_type(chip, lun, SENSE_TYPE_MG_KEY_FAIL_NOT_AUTHEN);
+ rtsx_clear_ms_error(chip);
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ bufflen = min_t(int, 1052, scsi_bufflen(srb));
+ rtsx_stor_set_xfer_buf(buf, bufflen, srb);
+
+GetEKBFinish:
+ kfree(buf);
+ return retval;
+}
+
+int mg_chg(struct scsi_cmnd *srb, struct rtsx_chip *chip)
+{
+ struct ms_info *ms_card = &(chip->ms_card);
+ int retval;
+ int bufflen;
+ int i;
+ unsigned int lun = SCSI_LUN(srb);
+ u8 buf[32];
+
+ ms_cleanup_work(chip);
+
+ retval = ms_switch_clock(chip);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ retval = mg_send_ex_cmd(chip, MG_GET_ID, 0);
+ if (retval != STATUS_SUCCESS) {
+ set_sense_type(chip, lun, SENSE_TYPE_MG_INCOMPATIBLE_MEDIUM);
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ retval = ms_read_bytes(chip, PRO_READ_SHORT_DATA, 32, WAIT_INT,
+ buf, 32);
+ if (retval != STATUS_SUCCESS) {
+ set_sense_type(chip, lun, SENSE_TYPE_MG_INCOMPATIBLE_MEDIUM);
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+ if (check_ms_err(chip)) {
+ set_sense_type(chip, lun, SENSE_TYPE_MG_INCOMPATIBLE_MEDIUM);
+ rtsx_clear_ms_error(chip);
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ memcpy(ms_card->magic_gate_id, buf, 16);
+
+#ifdef READ_BYTES_WAIT_INT
+ retval = ms_poll_int(chip);
+ if (retval != STATUS_SUCCESS) {
+ set_sense_type(chip, lun, SENSE_TYPE_MG_INCOMPATIBLE_MEDIUM);
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+#endif
+
+ retval = mg_send_ex_cmd(chip, MG_SET_RD, 0);
+ if (retval != STATUS_SUCCESS) {
+ set_sense_type(chip, lun, SENSE_TYPE_MG_INCOMPATIBLE_MEDIUM);
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ bufflen = min_t(int, 12, scsi_bufflen(srb));
+ rtsx_stor_get_xfer_buf(buf, bufflen, srb);
+
+ for (i = 0; i < 8; i++)
+ buf[i] = buf[4+i];
+
+ for (i = 0; i < 24; i++)
+ buf[8+i] = 0;
+
+ retval = ms_write_bytes(chip, PRO_WRITE_SHORT_DATA,
+ 32, WAIT_INT, buf, 32);
+ if (retval != STATUS_SUCCESS) {
+ set_sense_type(chip, lun, SENSE_TYPE_MG_INCOMPATIBLE_MEDIUM);
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+ if (check_ms_err(chip)) {
+ set_sense_type(chip, lun, SENSE_TYPE_MG_INCOMPATIBLE_MEDIUM);
+ rtsx_clear_ms_error(chip);
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ ms_card->mg_auth = 0;
+
+ return STATUS_SUCCESS;
+}
+
+int mg_get_rsp_chg(struct scsi_cmnd *srb, struct rtsx_chip *chip)
+{
+ struct ms_info *ms_card = &(chip->ms_card);
+ int retval;
+ int bufflen;
+ unsigned int lun = SCSI_LUN(srb);
+ u8 buf1[32], buf2[36];
+
+ ms_cleanup_work(chip);
+
+ retval = ms_switch_clock(chip);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ retval = mg_send_ex_cmd(chip, MG_MAKE_RMS, 0);
+ if (retval != STATUS_SUCCESS) {
+ set_sense_type(chip, lun, SENSE_TYPE_MG_KEY_FAIL_NOT_AUTHEN);
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ retval = ms_read_bytes(chip, PRO_READ_SHORT_DATA, 32, WAIT_INT,
+ buf1, 32);
+ if (retval != STATUS_SUCCESS) {
+ set_sense_type(chip, lun, SENSE_TYPE_MG_KEY_FAIL_NOT_AUTHEN);
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+ if (check_ms_err(chip)) {
+ set_sense_type(chip, lun, SENSE_TYPE_MG_KEY_FAIL_NOT_AUTHEN);
+ rtsx_clear_ms_error(chip);
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ buf2[0] = 0x00;
+ buf2[1] = 0x22;
+ buf2[2] = 0x00;
+ buf2[3] = 0x00;
+
+ memcpy(buf2 + 4, ms_card->magic_gate_id, 16);
+ memcpy(buf2 + 20, buf1, 16);
+
+ bufflen = min_t(int, 36, scsi_bufflen(srb));
+ rtsx_stor_set_xfer_buf(buf2, bufflen, srb);
+
+#ifdef READ_BYTES_WAIT_INT
+ retval = ms_poll_int(chip);
+ if (retval != STATUS_SUCCESS) {
+ set_sense_type(chip, lun, SENSE_TYPE_MG_KEY_FAIL_NOT_AUTHEN);
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+#endif
+
+ return STATUS_SUCCESS;
+}
+
+int mg_rsp(struct scsi_cmnd *srb, struct rtsx_chip *chip)
+{
+ struct ms_info *ms_card = &(chip->ms_card);
+ int retval;
+ int i;
+ int bufflen;
+ unsigned int lun = SCSI_LUN(srb);
+ u8 buf[32];
+
+ ms_cleanup_work(chip);
+
+ retval = ms_switch_clock(chip);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ retval = mg_send_ex_cmd(chip, MG_MAKE_KSE, 0);
+ if (retval != STATUS_SUCCESS) {
+ set_sense_type(chip, lun, SENSE_TYPE_MG_KEY_FAIL_NOT_AUTHEN);
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ bufflen = min_t(int, 12, scsi_bufflen(srb));
+ rtsx_stor_get_xfer_buf(buf, bufflen, srb);
+
+ for (i = 0; i < 8; i++)
+ buf[i] = buf[4+i];
+
+ for (i = 0; i < 24; i++)
+ buf[8+i] = 0;
+
+ retval = ms_write_bytes(chip, PRO_WRITE_SHORT_DATA, 32, WAIT_INT,
+ buf, 32);
+ if (retval != STATUS_SUCCESS) {
+ set_sense_type(chip, lun, SENSE_TYPE_MG_KEY_FAIL_NOT_AUTHEN);
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+ if (check_ms_err(chip)) {
+ set_sense_type(chip, lun, SENSE_TYPE_MG_KEY_FAIL_NOT_AUTHEN);
+ rtsx_clear_ms_error(chip);
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ ms_card->mg_auth = 1;
+
+ return STATUS_SUCCESS;
+}
+
+int mg_get_ICV(struct scsi_cmnd *srb, struct rtsx_chip *chip)
+{
+ struct ms_info *ms_card = &(chip->ms_card);
+ int retval;
+ int bufflen;
+ unsigned int lun = SCSI_LUN(srb);
+ u8 *buf = NULL;
+
+ ms_cleanup_work(chip);
+
+ retval = ms_switch_clock(chip);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ buf = kmalloc(1028, GFP_KERNEL);
+ if (!buf) {
+ rtsx_trace(chip);
+ return STATUS_ERROR;
+ }
+
+ buf[0] = 0x04;
+ buf[1] = 0x02;
+ buf[2] = 0x00;
+ buf[3] = 0x00;
+
+ retval = mg_send_ex_cmd(chip, MG_GET_IBD, ms_card->mg_entry_num);
+ if (retval != STATUS_SUCCESS) {
+ set_sense_type(chip, lun, SENSE_TYPE_MEDIA_UNRECOVER_READ_ERR);
+ rtsx_trace(chip);
+ goto GetICVFinish;
+ }
+
+ retval = ms_transfer_data(chip, MS_TM_AUTO_READ, PRO_READ_LONG_DATA,
+ 2, WAIT_INT, 0, 0, buf + 4, 1024);
+ if (retval != STATUS_SUCCESS) {
+ set_sense_type(chip, lun, SENSE_TYPE_MEDIA_UNRECOVER_READ_ERR);
+ rtsx_clear_ms_error(chip);
+ rtsx_trace(chip);
+ goto GetICVFinish;
+ }
+ if (check_ms_err(chip)) {
+ set_sense_type(chip, lun, SENSE_TYPE_MEDIA_UNRECOVER_READ_ERR);
+ rtsx_clear_ms_error(chip);
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ bufflen = min_t(int, 1028, scsi_bufflen(srb));
+ rtsx_stor_set_xfer_buf(buf, bufflen, srb);
+
+GetICVFinish:
+ kfree(buf);
+ return retval;
+}
+
+int mg_set_ICV(struct scsi_cmnd *srb, struct rtsx_chip *chip)
+{
+ struct ms_info *ms_card = &(chip->ms_card);
+ int retval;
+ int bufflen;
+#ifdef MG_SET_ICV_SLOW
+ int i;
+#endif
+ unsigned int lun = SCSI_LUN(srb);
+ u8 *buf = NULL;
+
+ ms_cleanup_work(chip);
+
+ retval = ms_switch_clock(chip);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ buf = kmalloc(1028, GFP_KERNEL);
+ if (!buf) {
+ rtsx_trace(chip);
+ return STATUS_ERROR;
+ }
+
+ bufflen = min_t(int, 1028, scsi_bufflen(srb));
+ rtsx_stor_get_xfer_buf(buf, bufflen, srb);
+
+ retval = mg_send_ex_cmd(chip, MG_SET_IBD, ms_card->mg_entry_num);
+ if (retval != STATUS_SUCCESS) {
+ if (ms_card->mg_auth == 0) {
+ if ((buf[5] & 0xC0) != 0)
+ set_sense_type(chip, lun,
+ SENSE_TYPE_MG_KEY_FAIL_NOT_ESTAB);
+ else
+ set_sense_type(chip, lun,
+ SENSE_TYPE_MG_WRITE_ERR);
+ } else {
+ set_sense_type(chip, lun, SENSE_TYPE_MG_WRITE_ERR);
+ }
+ rtsx_trace(chip);
+ goto SetICVFinish;
+ }
+
+#ifdef MG_SET_ICV_SLOW
+ for (i = 0; i < 2; i++) {
+ udelay(50);
+
+ rtsx_init_cmd(chip);
+
+ rtsx_add_cmd(chip, WRITE_REG_CMD, MS_TPC,
+ 0xFF, PRO_WRITE_LONG_DATA);
+ rtsx_add_cmd(chip, WRITE_REG_CMD, MS_TRANS_CFG, 0xFF, WAIT_INT);
+ rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_DATA_SOURCE,
+ 0x01, RING_BUFFER);
+
+ trans_dma_enable(DMA_TO_DEVICE, chip, 512, DMA_512);
+
+ rtsx_add_cmd(chip, WRITE_REG_CMD, MS_TRANSFER, 0xFF,
+ MS_TRANSFER_START | MS_TM_NORMAL_WRITE);
+ rtsx_add_cmd(chip, CHECK_REG_CMD, MS_TRANSFER,
+ MS_TRANSFER_END, MS_TRANSFER_END);
+
+ rtsx_send_cmd_no_wait(chip);
+
+ retval = rtsx_transfer_data(chip, MS_CARD, buf + 4 + i*512,
+ 512, 0, DMA_TO_DEVICE, 3000);
+ if ((retval < 0) || check_ms_err(chip)) {
+ rtsx_clear_ms_error(chip);
+ if (ms_card->mg_auth == 0) {
+ if ((buf[5] & 0xC0) != 0)
+ set_sense_type(chip, lun, SENSE_TYPE_MG_KEY_FAIL_NOT_ESTAB);
+ else
+ set_sense_type(chip, lun,
+ SENSE_TYPE_MG_WRITE_ERR);
+ } else {
+ set_sense_type(chip, lun,
+ SENSE_TYPE_MG_WRITE_ERR);
+ }
+ retval = STATUS_FAIL;
+ rtsx_trace(chip);
+ goto SetICVFinish;
+ }
+ }
+#else
+ retval = ms_transfer_data(chip, MS_TM_AUTO_WRITE, PRO_WRITE_LONG_DATA,
+ 2, WAIT_INT, 0, 0, buf + 4, 1024);
+ if ((retval != STATUS_SUCCESS) || check_ms_err(chip)) {
+ rtsx_clear_ms_error(chip);
+ if (ms_card->mg_auth == 0) {
+ if ((buf[5] & 0xC0) != 0)
+ set_sense_type(chip, lun,
+ SENSE_TYPE_MG_KEY_FAIL_NOT_ESTAB);
+ else
+ set_sense_type(chip, lun,
+ SENSE_TYPE_MG_WRITE_ERR);
+ } else {
+ set_sense_type(chip, lun, SENSE_TYPE_MG_WRITE_ERR);
+ }
+ rtsx_trace(chip);
+ goto SetICVFinish;
+ }
+#endif
+
+SetICVFinish:
+ kfree(buf);
+ return retval;
+}
+
+#endif /* SUPPORT_MAGIC_GATE */
+
+void ms_cleanup_work(struct rtsx_chip *chip)
+{
+ struct ms_info *ms_card = &(chip->ms_card);
+
+ if (CHK_MSPRO(ms_card)) {
+ if (ms_card->seq_mode) {
+ dev_dbg(rtsx_dev(chip), "MS Pro: stop transmission\n");
+ mspro_stop_seq_mode(chip);
+ ms_card->cleanup_counter = 0;
+ }
+ if (CHK_MSHG(ms_card)) {
+ rtsx_write_register(chip, MS_CFG,
+ MS_2K_SECTOR_MODE, 0x00);
+ }
+ }
+#ifdef MS_DELAY_WRITE
+ else if ((!CHK_MSPRO(ms_card)) && ms_card->delay_write.delay_write_flag) {
+ dev_dbg(rtsx_dev(chip), "MS: delay write\n");
+ ms_delay_write(chip);
+ ms_card->cleanup_counter = 0;
+ }
+#endif
+}
+
+int ms_power_off_card3v3(struct rtsx_chip *chip)
+{
+ int retval;
+
+ retval = disable_card_clock(chip, MS_CARD);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ if (chip->asic_code) {
+ retval = ms_pull_ctl_disable(chip);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+ } else {
+ retval = rtsx_write_register(chip, FPGA_PULL_CTL,
+ FPGA_MS_PULL_CTL_BIT | 0x20,
+ FPGA_MS_PULL_CTL_BIT);
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+ }
+ retval = rtsx_write_register(chip, CARD_OE, MS_OUTPUT_EN, 0);
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+ if (!chip->ft2_fast_mode) {
+ retval = card_power_off(chip, MS_CARD);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+ }
+
+ return STATUS_SUCCESS;
+}
+
+int release_ms_card(struct rtsx_chip *chip)
+{
+ struct ms_info *ms_card = &(chip->ms_card);
+ int retval;
+
+#ifdef MS_DELAY_WRITE
+ ms_card->delay_write.delay_write_flag = 0;
+#endif
+ ms_card->pro_under_formatting = 0;
+
+ chip->card_ready &= ~MS_CARD;
+ chip->card_fail &= ~MS_CARD;
+ chip->card_wp &= ~MS_CARD;
+
+ ms_free_l2p_tbl(chip);
+
+ memset(ms_card->raw_sys_info, 0, 96);
+#ifdef SUPPORT_PCGL_1P18
+ memset(ms_card->raw_model_name, 0, 48);
+#endif
+
+ retval = ms_power_off_card3v3(chip);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ return STATUS_SUCCESS;
+}
diff --git a/drivers/staging/rts5208/ms.h b/drivers/staging/rts5208/ms.h
new file mode 100644
index 000000000..d919170f2
--- /dev/null
+++ b/drivers/staging/rts5208/ms.h
@@ -0,0 +1,227 @@
+/* Driver for Realtek PCI-Express card reader
+ * Header file
+ *
+ * Copyright(c) 2009-2013 Realtek Semiconductor Corp. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, 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, see <http://www.gnu.org/licenses/>.
+ *
+ * Author:
+ * Wei WANG (wei_wang@realsil.com.cn)
+ * Micky Ching (micky_ching@realsil.com.cn)
+ */
+
+#ifndef __REALTEK_RTSX_MS_H
+#define __REALTEK_RTSX_MS_H
+
+#define MS_DELAY_WRITE
+
+#define MS_MAX_RETRY_COUNT 3
+
+#define MS_EXTRA_SIZE 0x9
+
+#define WRT_PRTCT 0x01
+
+/* Error Code */
+#define MS_NO_ERROR 0x00
+#define MS_CRC16_ERROR 0x80
+#define MS_TO_ERROR 0x40
+#define MS_NO_CARD 0x20
+#define MS_NO_MEMORY 0x10
+#define MS_CMD_NK 0x08
+#define MS_FLASH_READ_ERROR 0x04
+#define MS_FLASH_WRITE_ERROR 0x02
+#define MS_BREQ_ERROR 0x01
+#define MS_NOT_FOUND 0x03
+
+/* Transfer Protocol Command */
+#define READ_PAGE_DATA 0x02
+#define READ_REG 0x04
+#define GET_INT 0x07
+#define WRITE_PAGE_DATA 0x0D
+#define WRITE_REG 0x0B
+#define SET_RW_REG_ADRS 0x08
+#define SET_CMD 0x0E
+
+#define PRO_READ_LONG_DATA 0x02
+#define PRO_READ_SHORT_DATA 0x03
+#define PRO_READ_REG 0x04
+#define PRO_READ_QUAD_DATA 0x05
+#define PRO_GET_INT 0x07
+#define PRO_WRITE_LONG_DATA 0x0D
+#define PRO_WRITE_SHORT_DATA 0x0C
+#define PRO_WRITE_QUAD_DATA 0x0A
+#define PRO_WRITE_REG 0x0B
+#define PRO_SET_RW_REG_ADRS 0x08
+#define PRO_SET_CMD 0x0E
+#define PRO_EX_SET_CMD 0x09
+
+#ifdef SUPPORT_MAGIC_GATE
+
+#define MG_GET_ID 0x40
+#define MG_SET_LID 0x41
+#define MG_GET_LEKB 0x42
+#define MG_SET_RD 0x43
+#define MG_MAKE_RMS 0x44
+#define MG_MAKE_KSE 0x45
+#define MG_SET_IBD 0x46
+#define MG_GET_IBD 0x47
+
+#endif
+
+#ifdef XC_POWERCLASS
+#define XC_CHG_POWER 0x16
+#endif
+
+#define BLOCK_READ 0xAA
+#define BLOCK_WRITE 0x55
+#define BLOCK_END 0x33
+#define BLOCK_ERASE 0x99
+#define FLASH_STOP 0xCC
+
+#define SLEEP 0x5A
+#define CLEAR_BUF 0xC3
+#define MS_RESET 0x3C
+
+#define PRO_READ_DATA 0x20
+#define PRO_WRITE_DATA 0x21
+#define PRO_READ_ATRB 0x24
+#define PRO_STOP 0x25
+#define PRO_ERASE 0x26
+#define PRO_READ_2K_DATA 0x27
+#define PRO_WRITE_2K_DATA 0x28
+
+#define PRO_FORMAT 0x10
+#define PRO_SLEEP 0x11
+
+#define IntReg 0x01
+#define StatusReg0 0x02
+#define StatusReg1 0x03
+
+#define SystemParm 0x10
+#define BlockAdrs 0x11
+#define CMDParm 0x14
+#define PageAdrs 0x15
+
+#define OverwriteFlag 0x16
+#define ManagemenFlag 0x17
+#define LogicalAdrs 0x18
+#define ReserveArea 0x1A
+
+#define Pro_IntReg 0x01
+#define Pro_StatusReg 0x02
+#define Pro_TypeReg 0x04
+#define Pro_IFModeReg 0x05
+#define Pro_CatagoryReg 0x06
+#define Pro_ClassReg 0x07
+
+
+#define Pro_SystemParm 0x10
+#define Pro_DataCount1 0x11
+#define Pro_DataCount0 0x12
+#define Pro_DataAddr3 0x13
+#define Pro_DataAddr2 0x14
+#define Pro_DataAddr1 0x15
+#define Pro_DataAddr0 0x16
+
+#define Pro_TPCParm 0x17
+#define Pro_CMDParm 0x18
+
+#define INT_REG_CED 0x80
+#define INT_REG_ERR 0x40
+#define INT_REG_BREQ 0x20
+#define INT_REG_CMDNK 0x01
+
+#define BLOCK_BOOT 0xC0
+#define BLOCK_OK 0x80
+#define PAGE_OK 0x60
+#define DATA_COMPL 0x10
+
+#define NOT_BOOT_BLOCK 0x4
+#define NOT_TRANSLATION_TABLE 0x8
+
+#define HEADER_ID0 PPBUF_BASE2
+#define HEADER_ID1 (PPBUF_BASE2 + 1)
+#define DISABLED_BLOCK0 (PPBUF_BASE2 + 0x170 + 4)
+#define DISABLED_BLOCK1 (PPBUF_BASE2 + 0x170 + 5)
+#define DISABLED_BLOCK2 (PPBUF_BASE2 + 0x170 + 6)
+#define DISABLED_BLOCK3 (PPBUF_BASE2 + 0x170 + 7)
+#define BLOCK_SIZE_0 (PPBUF_BASE2 + 0x1a0 + 2)
+#define BLOCK_SIZE_1 (PPBUF_BASE2 + 0x1a0 + 3)
+#define BLOCK_COUNT_0 (PPBUF_BASE2 + 0x1a0 + 4)
+#define BLOCK_COUNT_1 (PPBUF_BASE2 + 0x1a0 + 5)
+#define EBLOCK_COUNT_0 (PPBUF_BASE2 + 0x1a0 + 6)
+#define EBLOCK_COUNT_1 (PPBUF_BASE2 + 0x1a0 + 7)
+#define PAGE_SIZE_0 (PPBUF_BASE2 + 0x1a0 + 8)
+#define PAGE_SIZE_1 (PPBUF_BASE2 + 0x1a0 + 9)
+
+#define MS_Device_Type (PPBUF_BASE2 + 0x1D8)
+
+#define MS_4bit_Support (PPBUF_BASE2 + 0x1D3)
+
+#define setPS_NG 1
+#define setPS_Error 0
+
+#define PARALLEL_8BIT_IF 0x40
+#define PARALLEL_4BIT_IF 0x00
+#define SERIAL_IF 0x80
+
+#define BUF_FULL 0x10
+#define BUF_EMPTY 0x20
+
+#define MEDIA_BUSY 0x80
+#define FLASH_BUSY 0x40
+#define DATA_ERROR 0x20
+#define STS_UCDT 0x10
+#define EXTRA_ERROR 0x08
+#define STS_UCEX 0x04
+#define FLAG_ERROR 0x02
+#define STS_UCFG 0x01
+
+#define MS_SHORT_DATA_LEN 32
+
+#define FORMAT_SUCCESS 0
+#define FORMAT_FAIL 1
+#define FORMAT_IN_PROGRESS 2
+
+#define MS_SET_BAD_BLOCK_FLG(ms_card) ((ms_card)->multi_flag |= 0x80)
+#define MS_CLR_BAD_BLOCK_FLG(ms_card) ((ms_card)->multi_flag &= 0x7F)
+#define MS_TST_BAD_BLOCK_FLG(ms_card) ((ms_card)->multi_flag & 0x80)
+
+void mspro_polling_format_status(struct rtsx_chip *chip);
+
+void mspro_stop_seq_mode(struct rtsx_chip *chip);
+int reset_ms_card(struct rtsx_chip *chip);
+int ms_rw(struct scsi_cmnd *srb, struct rtsx_chip *chip,
+ u32 start_sector, u16 sector_cnt);
+int mspro_format(struct scsi_cmnd *srb, struct rtsx_chip *chip,
+ int short_data_len, bool quick_format);
+void ms_free_l2p_tbl(struct rtsx_chip *chip);
+void ms_cleanup_work(struct rtsx_chip *chip);
+int ms_power_off_card3v3(struct rtsx_chip *chip);
+int release_ms_card(struct rtsx_chip *chip);
+#ifdef MS_DELAY_WRITE
+int ms_delay_write(struct rtsx_chip *chip);
+#endif
+
+#ifdef SUPPORT_MAGIC_GATE
+int mg_set_leaf_id(struct scsi_cmnd *srb, struct rtsx_chip *chip);
+int mg_get_local_EKB(struct scsi_cmnd *srb, struct rtsx_chip *chip);
+int mg_chg(struct scsi_cmnd *srb, struct rtsx_chip *chip);
+int mg_get_rsp_chg(struct scsi_cmnd *srb, struct rtsx_chip *chip);
+int mg_rsp(struct scsi_cmnd *srb, struct rtsx_chip *chip);
+int mg_get_ICV(struct scsi_cmnd *srb, struct rtsx_chip *chip);
+int mg_set_ICV(struct scsi_cmnd *srb, struct rtsx_chip *chip);
+#endif
+
+#endif /* __REALTEK_RTSX_MS_H */
diff --git a/drivers/staging/rts5208/rtsx.c b/drivers/staging/rts5208/rtsx.c
new file mode 100644
index 000000000..d64b6ed9c
--- /dev/null
+++ b/drivers/staging/rts5208/rtsx.c
@@ -0,0 +1,1045 @@
+/* Driver for Realtek PCI-Express card reader
+ *
+ * Copyright(c) 2009-2013 Realtek Semiconductor Corp. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, 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, see <http://www.gnu.org/licenses/>.
+ *
+ * Author:
+ * Wei WANG (wei_wang@realsil.com.cn)
+ * Micky Ching (micky_ching@realsil.com.cn)
+ */
+
+#include <linux/blkdev.h>
+#include <linux/kthread.h>
+#include <linux/sched.h>
+#include <linux/workqueue.h>
+
+#include "rtsx.h"
+#include "ms.h"
+#include "sd.h"
+#include "xd.h"
+
+MODULE_DESCRIPTION("Realtek PCI-Express card reader rts5208/rts5288 driver");
+MODULE_LICENSE("GPL");
+
+static unsigned int delay_use = 1;
+module_param(delay_use, uint, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(delay_use, "seconds to delay before using a new device");
+
+static int ss_en;
+module_param(ss_en, int, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(ss_en, "enable selective suspend");
+
+static int ss_interval = 50;
+module_param(ss_interval, int, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(ss_interval, "Interval to enter ss state in seconds");
+
+static int auto_delink_en;
+module_param(auto_delink_en, int, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(auto_delink_en, "enable auto delink");
+
+static unsigned char aspm_l0s_l1_en;
+module_param(aspm_l0s_l1_en, byte, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(aspm_l0s_l1_en, "enable device aspm");
+
+static int msi_en;
+module_param(msi_en, int, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(msi_en, "enable msi");
+
+static irqreturn_t rtsx_interrupt(int irq, void *dev_id);
+
+/***********************************************************************
+ * Host functions
+ ***********************************************************************/
+
+static const char *host_info(struct Scsi_Host *host)
+{
+ return "SCSI emulation for PCI-Express Mass Storage devices";
+}
+
+static int slave_alloc(struct scsi_device *sdev)
+{
+ /*
+ * Set the INQUIRY transfer length to 36. We don't use any of
+ * the extra data and many devices choke if asked for more or
+ * less than 36 bytes.
+ */
+ sdev->inquiry_len = 36;
+ return 0;
+}
+
+static int slave_configure(struct scsi_device *sdev)
+{
+ /* Scatter-gather buffers (all but the last) must have a length
+ * divisible by the bulk maxpacket size. Otherwise a data packet
+ * would end up being short, causing a premature end to the data
+ * transfer. Since high-speed bulk pipes have a maxpacket size
+ * of 512, we'll use that as the scsi device queue's DMA alignment
+ * mask. Guaranteeing proper alignment of the first buffer will
+ * have the desired effect because, except at the beginning and
+ * the end, scatter-gather buffers follow page boundaries. */
+ blk_queue_dma_alignment(sdev->request_queue, (512 - 1));
+
+ /* Set the SCSI level to at least 2. We'll leave it at 3 if that's
+ * what is originally reported. We need this to avoid confusing
+ * the SCSI layer with devices that report 0 or 1, but need 10-byte
+ * commands (ala ATAPI devices behind certain bridges, or devices
+ * which simply have broken INQUIRY data).
+ *
+ * NOTE: This means /dev/sg programs (ala cdrecord) will get the
+ * actual information. This seems to be the preference for
+ * programs like that.
+ *
+ * NOTE: This also means that /proc/scsi/scsi and sysfs may report
+ * the actual value or the modified one, depending on where the
+ * data comes from.
+ */
+ if (sdev->scsi_level < SCSI_2)
+ sdev->scsi_level = sdev->sdev_target->scsi_level = SCSI_2;
+
+ return 0;
+}
+
+
+/***********************************************************************
+ * /proc/scsi/ functions
+ ***********************************************************************/
+
+/* we use this macro to help us write into the buffer */
+#undef SPRINTF
+#define SPRINTF(args...) \
+ do { if (pos < buffer+length) pos += sprintf(pos, ## args); } while (0)
+
+/* queue a command */
+/* This is always called with scsi_lock(host) held */
+static int queuecommand_lck(struct scsi_cmnd *srb,
+ void (*done)(struct scsi_cmnd *))
+{
+ struct rtsx_dev *dev = host_to_rtsx(srb->device->host);
+ struct rtsx_chip *chip = dev->chip;
+
+ /* check for state-transition errors */
+ if (chip->srb != NULL) {
+ dev_err(&dev->pci->dev, "Error: chip->srb = %p\n",
+ chip->srb);
+ return SCSI_MLQUEUE_HOST_BUSY;
+ }
+
+ /* fail the command if we are disconnecting */
+ if (rtsx_chk_stat(chip, RTSX_STAT_DISCONNECT)) {
+ dev_info(&dev->pci->dev, "Fail command during disconnect\n");
+ srb->result = DID_NO_CONNECT << 16;
+ done(srb);
+ return 0;
+ }
+
+ /* enqueue the command and wake up the control thread */
+ srb->scsi_done = done;
+ chip->srb = srb;
+ complete(&dev->cmnd_ready);
+
+ return 0;
+}
+
+static DEF_SCSI_QCMD(queuecommand)
+
+/***********************************************************************
+ * Error handling functions
+ ***********************************************************************/
+
+/* Command timeout and abort */
+static int command_abort(struct scsi_cmnd *srb)
+{
+ struct Scsi_Host *host = srb->device->host;
+ struct rtsx_dev *dev = host_to_rtsx(host);
+ struct rtsx_chip *chip = dev->chip;
+
+ dev_info(&dev->pci->dev, "%s called\n", __func__);
+
+ scsi_lock(host);
+
+ /* Is this command still active? */
+ if (chip->srb != srb) {
+ scsi_unlock(host);
+ dev_info(&dev->pci->dev, "-- nothing to abort\n");
+ return FAILED;
+ }
+
+ rtsx_set_stat(chip, RTSX_STAT_ABORT);
+
+ scsi_unlock(host);
+
+ /* Wait for the aborted command to finish */
+ wait_for_completion(&dev->notify);
+
+ return SUCCESS;
+}
+
+/* This invokes the transport reset mechanism to reset the state of the
+ * device */
+static int device_reset(struct scsi_cmnd *srb)
+{
+ int result = 0;
+ struct rtsx_dev *dev = host_to_rtsx(srb->device->host);
+
+ dev_info(&dev->pci->dev, "%s called\n", __func__);
+
+ return result < 0 ? FAILED : SUCCESS;
+}
+
+/* Simulate a SCSI bus reset by resetting the device's USB port. */
+static int bus_reset(struct scsi_cmnd *srb)
+{
+ int result = 0;
+ struct rtsx_dev *dev = host_to_rtsx(srb->device->host);
+
+ dev_info(&dev->pci->dev, "%s called\n", __func__);
+
+ return result < 0 ? FAILED : SUCCESS;
+}
+
+
+/*
+ * this defines our host template, with which we'll allocate hosts
+ */
+
+static struct scsi_host_template rtsx_host_template = {
+ /* basic userland interface stuff */
+ .name = CR_DRIVER_NAME,
+ .proc_name = CR_DRIVER_NAME,
+ .info = host_info,
+
+ /* command interface -- queued only */
+ .queuecommand = queuecommand,
+
+ /* error and abort handlers */
+ .eh_abort_handler = command_abort,
+ .eh_device_reset_handler = device_reset,
+ .eh_bus_reset_handler = bus_reset,
+
+ /* queue commands only, only one command per LUN */
+ .can_queue = 1,
+ .cmd_per_lun = 1,
+
+ /* unknown initiator id */
+ .this_id = -1,
+
+ .slave_alloc = slave_alloc,
+ .slave_configure = slave_configure,
+
+ /* lots of sg segments can be handled */
+ .sg_tablesize = SG_ALL,
+
+ /* limit the total size of a transfer to 120 KB */
+ .max_sectors = 240,
+
+ /* merge commands... this seems to help performance, but
+ * periodically someone should test to see which setting is more
+ * optimal.
+ */
+ .use_clustering = 1,
+
+ /* emulated HBA */
+ .emulated = 1,
+
+ /* we do our own delay after a device or bus reset */
+ .skip_settle_delay = 1,
+
+ /* module management */
+ .module = THIS_MODULE
+};
+
+
+static int rtsx_acquire_irq(struct rtsx_dev *dev)
+{
+ struct rtsx_chip *chip = dev->chip;
+
+ dev_info(&dev->pci->dev, "%s: chip->msi_en = %d, pci->irq = %d\n",
+ __func__, chip->msi_en, dev->pci->irq);
+
+ if (request_irq(dev->pci->irq, rtsx_interrupt,
+ chip->msi_en ? 0 : IRQF_SHARED,
+ CR_DRIVER_NAME, dev)) {
+ dev_err(&dev->pci->dev,
+ "rtsx: unable to grab IRQ %d, disabling device\n",
+ dev->pci->irq);
+ return -1;
+ }
+
+ dev->irq = dev->pci->irq;
+ pci_intx(dev->pci, !chip->msi_en);
+
+ return 0;
+}
+
+
+int rtsx_read_pci_cfg_byte(u8 bus, u8 dev, u8 func, u8 offset, u8 *val)
+{
+ struct pci_dev *pdev;
+ u8 data;
+ u8 devfn = (dev << 3) | func;
+
+ pdev = pci_get_bus_and_slot(bus, devfn);
+ if (!pdev)
+ return -1;
+
+ pci_read_config_byte(pdev, offset, &data);
+ if (val)
+ *val = data;
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+/*
+ * power management
+ */
+static int rtsx_suspend(struct pci_dev *pci, pm_message_t state)
+{
+ struct rtsx_dev *dev = pci_get_drvdata(pci);
+ struct rtsx_chip *chip;
+
+ if (!dev)
+ return 0;
+
+ /* lock the device pointers */
+ mutex_lock(&(dev->dev_mutex));
+
+ chip = dev->chip;
+
+ rtsx_do_before_power_down(chip, PM_S3);
+
+ if (dev->irq >= 0) {
+ synchronize_irq(dev->irq);
+ free_irq(dev->irq, (void *)dev);
+ dev->irq = -1;
+ }
+
+ if (chip->msi_en)
+ pci_disable_msi(pci);
+
+ pci_save_state(pci);
+ pci_enable_wake(pci, pci_choose_state(pci, state), 1);
+ pci_disable_device(pci);
+ pci_set_power_state(pci, pci_choose_state(pci, state));
+
+ /* unlock the device pointers */
+ mutex_unlock(&dev->dev_mutex);
+
+ return 0;
+}
+
+static int rtsx_resume(struct pci_dev *pci)
+{
+ struct rtsx_dev *dev = pci_get_drvdata(pci);
+ struct rtsx_chip *chip;
+
+ if (!dev)
+ return 0;
+
+ chip = dev->chip;
+
+ /* lock the device pointers */
+ mutex_lock(&(dev->dev_mutex));
+
+ pci_set_power_state(pci, PCI_D0);
+ pci_restore_state(pci);
+ if (pci_enable_device(pci) < 0) {
+ dev_err(&dev->pci->dev,
+ "%s: pci_enable_device failed, disabling device\n",
+ CR_DRIVER_NAME);
+ /* unlock the device pointers */
+ mutex_unlock(&dev->dev_mutex);
+ return -EIO;
+ }
+ pci_set_master(pci);
+
+ if (chip->msi_en) {
+ if (pci_enable_msi(pci) < 0)
+ chip->msi_en = 0;
+ }
+
+ if (rtsx_acquire_irq(dev) < 0) {
+ /* unlock the device pointers */
+ mutex_unlock(&dev->dev_mutex);
+ return -EIO;
+ }
+
+ rtsx_write_register(chip, HOST_SLEEP_STATE, 0x03, 0x00);
+ rtsx_init_chip(chip);
+
+ /* unlock the device pointers */
+ mutex_unlock(&dev->dev_mutex);
+
+ return 0;
+}
+#endif /* CONFIG_PM */
+
+static void rtsx_shutdown(struct pci_dev *pci)
+{
+ struct rtsx_dev *dev = pci_get_drvdata(pci);
+ struct rtsx_chip *chip;
+
+ if (!dev)
+ return;
+
+ chip = dev->chip;
+
+ rtsx_do_before_power_down(chip, PM_S1);
+
+ if (dev->irq >= 0) {
+ synchronize_irq(dev->irq);
+ free_irq(dev->irq, (void *)dev);
+ dev->irq = -1;
+ }
+
+ if (chip->msi_en)
+ pci_disable_msi(pci);
+
+ pci_disable_device(pci);
+}
+
+static int rtsx_control_thread(void *__dev)
+{
+ struct rtsx_dev *dev = __dev;
+ struct rtsx_chip *chip = dev->chip;
+ struct Scsi_Host *host = rtsx_to_host(dev);
+
+ for (;;) {
+ if (wait_for_completion_interruptible(&dev->cmnd_ready))
+ break;
+
+ /* lock the device pointers */
+ mutex_lock(&(dev->dev_mutex));
+
+ /* if the device has disconnected, we are free to exit */
+ if (rtsx_chk_stat(chip, RTSX_STAT_DISCONNECT)) {
+ dev_info(&dev->pci->dev, "-- rtsx-control exiting\n");
+ mutex_unlock(&dev->dev_mutex);
+ break;
+ }
+
+ /* lock access to the state */
+ scsi_lock(host);
+
+ /* has the command aborted ? */
+ if (rtsx_chk_stat(chip, RTSX_STAT_ABORT)) {
+ chip->srb->result = DID_ABORT << 16;
+ goto SkipForAbort;
+ }
+
+ scsi_unlock(host);
+
+ /* reject the command if the direction indicator
+ * is UNKNOWN
+ */
+ if (chip->srb->sc_data_direction == DMA_BIDIRECTIONAL) {
+ dev_err(&dev->pci->dev, "UNKNOWN data direction\n");
+ chip->srb->result = DID_ERROR << 16;
+ }
+
+ /* reject if target != 0 or if LUN is higher than
+ * the maximum known LUN
+ */
+ else if (chip->srb->device->id) {
+ dev_err(&dev->pci->dev, "Bad target number (%d:%d)\n",
+ chip->srb->device->id,
+ (u8)chip->srb->device->lun);
+ chip->srb->result = DID_BAD_TARGET << 16;
+ }
+
+ else if (chip->srb->device->lun > chip->max_lun) {
+ dev_err(&dev->pci->dev, "Bad LUN (%d:%d)\n",
+ chip->srb->device->id,
+ (u8)chip->srb->device->lun);
+ chip->srb->result = DID_BAD_TARGET << 16;
+ }
+
+ /* we've got a command, let's do it! */
+ else {
+ scsi_show_command(chip);
+ rtsx_invoke_transport(chip->srb, chip);
+ }
+
+ /* lock access to the state */
+ scsi_lock(host);
+
+ /* did the command already complete because of a disconnect? */
+ if (!chip->srb)
+ ; /* nothing to do */
+
+ /* indicate that the command is done */
+ else if (chip->srb->result != DID_ABORT << 16) {
+ chip->srb->scsi_done(chip->srb);
+ } else {
+SkipForAbort:
+ dev_err(&dev->pci->dev, "scsi command aborted\n");
+ }
+
+ if (rtsx_chk_stat(chip, RTSX_STAT_ABORT)) {
+ complete(&(dev->notify));
+
+ rtsx_set_stat(chip, RTSX_STAT_IDLE);
+ }
+
+ /* finished working on this command */
+ chip->srb = NULL;
+ scsi_unlock(host);
+
+ /* unlock the device pointers */
+ mutex_unlock(&dev->dev_mutex);
+ } /* for (;;) */
+
+ /* notify the exit routine that we're actually exiting now
+ *
+ * complete()/wait_for_completion() is similar to up()/down(),
+ * except that complete() is safe in the case where the structure
+ * is getting deleted in a parallel mode of execution (i.e. just
+ * after the down() -- that's necessary for the thread-shutdown
+ * case.
+ *
+ * complete_and_exit() goes even further than this -- it is safe in
+ * the case that the thread of the caller is going away (not just
+ * the structure) -- this is necessary for the module-remove case.
+ * This is important in preemption kernels, which transfer the flow
+ * of execution immediately upon a complete().
+ */
+ complete_and_exit(&dev->control_exit, 0);
+}
+
+
+static int rtsx_polling_thread(void *__dev)
+{
+ struct rtsx_dev *dev = __dev;
+ struct rtsx_chip *chip = dev->chip;
+ struct sd_info *sd_card = &(chip->sd_card);
+ struct xd_info *xd_card = &(chip->xd_card);
+ struct ms_info *ms_card = &(chip->ms_card);
+
+ sd_card->cleanup_counter = 0;
+ xd_card->cleanup_counter = 0;
+ ms_card->cleanup_counter = 0;
+
+ /* Wait until SCSI scan finished */
+ wait_timeout((delay_use + 5) * 1000);
+
+ for (;;) {
+
+ set_current_state(TASK_INTERRUPTIBLE);
+ schedule_timeout(POLLING_INTERVAL);
+
+ /* lock the device pointers */
+ mutex_lock(&(dev->dev_mutex));
+
+ /* if the device has disconnected, we are free to exit */
+ if (rtsx_chk_stat(chip, RTSX_STAT_DISCONNECT)) {
+ dev_info(&dev->pci->dev, "-- rtsx-polling exiting\n");
+ mutex_unlock(&dev->dev_mutex);
+ break;
+ }
+
+ mutex_unlock(&dev->dev_mutex);
+
+ mspro_polling_format_status(chip);
+
+ /* lock the device pointers */
+ mutex_lock(&(dev->dev_mutex));
+
+ rtsx_polling_func(chip);
+
+ /* unlock the device pointers */
+ mutex_unlock(&dev->dev_mutex);
+ }
+
+ complete_and_exit(&dev->polling_exit, 0);
+}
+
+/*
+ * interrupt handler
+ */
+static irqreturn_t rtsx_interrupt(int irq, void *dev_id)
+{
+ struct rtsx_dev *dev = dev_id;
+ struct rtsx_chip *chip;
+ int retval;
+ u32 status;
+
+ if (dev)
+ chip = dev->chip;
+ else
+ return IRQ_NONE;
+
+ if (!chip)
+ return IRQ_NONE;
+
+ spin_lock(&dev->reg_lock);
+
+ retval = rtsx_pre_handle_interrupt(chip);
+ if (retval == STATUS_FAIL) {
+ spin_unlock(&dev->reg_lock);
+ if (chip->int_reg == 0xFFFFFFFF)
+ return IRQ_HANDLED;
+ return IRQ_NONE;
+ }
+
+ status = chip->int_reg;
+
+ if (dev->check_card_cd) {
+ if (!(dev->check_card_cd & status)) {
+ /* card not exist, return TRANS_RESULT_FAIL */
+ dev->trans_result = TRANS_RESULT_FAIL;
+ if (dev->done)
+ complete(dev->done);
+ goto Exit;
+ }
+ }
+
+ if (status & (NEED_COMPLETE_INT | DELINK_INT)) {
+ if (status & (TRANS_FAIL_INT | DELINK_INT)) {
+ if (status & DELINK_INT)
+ RTSX_SET_DELINK(chip);
+ dev->trans_result = TRANS_RESULT_FAIL;
+ if (dev->done)
+ complete(dev->done);
+ } else if (status & TRANS_OK_INT) {
+ dev->trans_result = TRANS_RESULT_OK;
+ if (dev->done)
+ complete(dev->done);
+ } else if (status & DATA_DONE_INT) {
+ dev->trans_result = TRANS_NOT_READY;
+ if (dev->done && (dev->trans_state == STATE_TRANS_SG))
+ complete(dev->done);
+ }
+ }
+
+Exit:
+ spin_unlock(&dev->reg_lock);
+ return IRQ_HANDLED;
+}
+
+
+/* Release all our dynamic resources */
+static void rtsx_release_resources(struct rtsx_dev *dev)
+{
+ dev_info(&dev->pci->dev, "-- %s\n", __func__);
+
+ /* Tell the control thread to exit. The SCSI host must
+ * already have been removed so it won't try to queue
+ * any more commands.
+ */
+ dev_info(&dev->pci->dev, "-- sending exit command to thread\n");
+ complete(&dev->cmnd_ready);
+ if (dev->ctl_thread)
+ wait_for_completion(&dev->control_exit);
+ if (dev->polling_thread)
+ wait_for_completion(&dev->polling_exit);
+
+ wait_timeout(200);
+
+ if (dev->rtsx_resv_buf) {
+ dma_free_coherent(&(dev->pci->dev), RTSX_RESV_BUF_LEN,
+ dev->rtsx_resv_buf, dev->rtsx_resv_buf_addr);
+ dev->chip->host_cmds_ptr = NULL;
+ dev->chip->host_sg_tbl_ptr = NULL;
+ }
+
+ if (dev->irq > 0)
+ free_irq(dev->irq, (void *)dev);
+ if (dev->chip->msi_en)
+ pci_disable_msi(dev->pci);
+ if (dev->remap_addr)
+ iounmap(dev->remap_addr);
+
+ pci_disable_device(dev->pci);
+ pci_release_regions(dev->pci);
+
+ rtsx_release_chip(dev->chip);
+ kfree(dev->chip);
+}
+
+/* First stage of disconnect processing: stop all commands and remove
+ * the host */
+static void quiesce_and_remove_host(struct rtsx_dev *dev)
+{
+ struct Scsi_Host *host = rtsx_to_host(dev);
+ struct rtsx_chip *chip = dev->chip;
+
+ /* Prevent new transfers, stop the current command, and
+ * interrupt a SCSI-scan or device-reset delay */
+ mutex_lock(&dev->dev_mutex);
+ scsi_lock(host);
+ rtsx_set_stat(chip, RTSX_STAT_DISCONNECT);
+ scsi_unlock(host);
+ mutex_unlock(&dev->dev_mutex);
+ wake_up(&dev->delay_wait);
+ wait_for_completion(&dev->scanning_done);
+
+ /* Wait some time to let other threads exist */
+ wait_timeout(100);
+
+ /* queuecommand won't accept any new commands and the control
+ * thread won't execute a previously-queued command. If there
+ * is such a command pending, complete it with an error. */
+ mutex_lock(&dev->dev_mutex);
+ if (chip->srb) {
+ chip->srb->result = DID_NO_CONNECT << 16;
+ scsi_lock(host);
+ chip->srb->scsi_done(dev->chip->srb);
+ chip->srb = NULL;
+ scsi_unlock(host);
+ }
+ mutex_unlock(&dev->dev_mutex);
+
+ /* Now we own no commands so it's safe to remove the SCSI host */
+ scsi_remove_host(host);
+}
+
+/* Second stage of disconnect processing: deallocate all resources */
+static void release_everything(struct rtsx_dev *dev)
+{
+ rtsx_release_resources(dev);
+
+ /* Drop our reference to the host; the SCSI core will free it
+ * when the refcount becomes 0. */
+ scsi_host_put(rtsx_to_host(dev));
+}
+
+/* Thread to carry out delayed SCSI-device scanning */
+static int rtsx_scan_thread(void *__dev)
+{
+ struct rtsx_dev *dev = (struct rtsx_dev *)__dev;
+ struct rtsx_chip *chip = dev->chip;
+
+ /* Wait for the timeout to expire or for a disconnect */
+ if (delay_use > 0) {
+ dev_info(&dev->pci->dev,
+ "%s: waiting for device to settle before scanning\n",
+ CR_DRIVER_NAME);
+ wait_event_interruptible_timeout(dev->delay_wait,
+ rtsx_chk_stat(chip, RTSX_STAT_DISCONNECT),
+ delay_use * HZ);
+ }
+
+ /* If the device is still connected, perform the scanning */
+ if (!rtsx_chk_stat(chip, RTSX_STAT_DISCONNECT)) {
+ scsi_scan_host(rtsx_to_host(dev));
+ dev_info(&dev->pci->dev, "%s: device scan complete\n",
+ CR_DRIVER_NAME);
+
+ /* Should we unbind if no devices were detected? */
+ }
+
+ complete_and_exit(&dev->scanning_done, 0);
+}
+
+static void rtsx_init_options(struct rtsx_chip *chip)
+{
+ chip->vendor_id = chip->rtsx->pci->vendor;
+ chip->product_id = chip->rtsx->pci->device;
+ chip->adma_mode = 1;
+ chip->lun_mc = 0;
+ chip->driver_first_load = 1;
+#ifdef HW_AUTO_SWITCH_SD_BUS
+ chip->sdio_in_charge = 0;
+#endif
+
+ chip->mspro_formatter_enable = 1;
+ chip->ignore_sd = 0;
+ chip->use_hw_setting = 0;
+ chip->lun_mode = DEFAULT_SINGLE;
+ chip->auto_delink_en = auto_delink_en;
+ chip->ss_en = ss_en;
+ chip->ss_idle_period = ss_interval * 1000;
+ chip->remote_wakeup_en = 0;
+ chip->aspm_l0s_l1_en = aspm_l0s_l1_en;
+ chip->dynamic_aspm = 1;
+ chip->fpga_sd_sdr104_clk = CLK_200;
+ chip->fpga_sd_ddr50_clk = CLK_100;
+ chip->fpga_sd_sdr50_clk = CLK_100;
+ chip->fpga_sd_hs_clk = CLK_100;
+ chip->fpga_mmc_52m_clk = CLK_80;
+ chip->fpga_ms_hg_clk = CLK_80;
+ chip->fpga_ms_4bit_clk = CLK_80;
+ chip->fpga_ms_1bit_clk = CLK_40;
+ chip->asic_sd_sdr104_clk = 203;
+ chip->asic_sd_sdr50_clk = 98;
+ chip->asic_sd_ddr50_clk = 98;
+ chip->asic_sd_hs_clk = 98;
+ chip->asic_mmc_52m_clk = 98;
+ chip->asic_ms_hg_clk = 117;
+ chip->asic_ms_4bit_clk = 78;
+ chip->asic_ms_1bit_clk = 39;
+ chip->ssc_depth_sd_sdr104 = SSC_DEPTH_2M;
+ chip->ssc_depth_sd_sdr50 = SSC_DEPTH_2M;
+ chip->ssc_depth_sd_ddr50 = SSC_DEPTH_1M;
+ chip->ssc_depth_sd_hs = SSC_DEPTH_1M;
+ chip->ssc_depth_mmc_52m = SSC_DEPTH_1M;
+ chip->ssc_depth_ms_hg = SSC_DEPTH_1M;
+ chip->ssc_depth_ms_4bit = SSC_DEPTH_512K;
+ chip->ssc_depth_low_speed = SSC_DEPTH_512K;
+ chip->ssc_en = 1;
+ chip->sd_speed_prior = 0x01040203;
+ chip->sd_current_prior = 0x00010203;
+ chip->sd_ctl = SD_PUSH_POINT_AUTO |
+ SD_SAMPLE_POINT_AUTO |
+ SUPPORT_MMC_DDR_MODE;
+ chip->sd_ddr_tx_phase = 0;
+ chip->mmc_ddr_tx_phase = 1;
+ chip->sd_default_tx_phase = 15;
+ chip->sd_default_rx_phase = 15;
+ chip->pmos_pwr_on_interval = 200;
+ chip->sd_voltage_switch_delay = 1000;
+ chip->ms_power_class_en = 3;
+
+ chip->sd_400mA_ocp_thd = 1;
+ chip->sd_800mA_ocp_thd = 5;
+ chip->ms_ocp_thd = 2;
+
+ chip->card_drive_sel = 0x55;
+ chip->sd30_drive_sel_1v8 = 0x03;
+ chip->sd30_drive_sel_3v3 = 0x01;
+
+ chip->do_delink_before_power_down = 1;
+ chip->auto_power_down = 1;
+ chip->polling_config = 0;
+
+ chip->force_clkreq_0 = 1;
+ chip->ft2_fast_mode = 0;
+
+ chip->sdio_retry_cnt = 1;
+
+ chip->xd_timeout = 2000;
+ chip->sd_timeout = 10000;
+ chip->ms_timeout = 2000;
+ chip->mspro_timeout = 15000;
+
+ chip->power_down_in_ss = 1;
+
+ chip->sdr104_en = 1;
+ chip->sdr50_en = 1;
+ chip->ddr50_en = 1;
+
+ chip->delink_stage1_step = 100;
+ chip->delink_stage2_step = 40;
+ chip->delink_stage3_step = 20;
+
+ chip->auto_delink_in_L1 = 1;
+ chip->blink_led = 1;
+ chip->msi_en = msi_en;
+ chip->hp_watch_bios_hotplug = 0;
+ chip->max_payload = 0;
+ chip->phy_voltage = 0;
+
+ chip->support_ms_8bit = 1;
+ chip->s3_pwr_off_delay = 1000;
+}
+
+static int rtsx_probe(struct pci_dev *pci,
+ const struct pci_device_id *pci_id)
+{
+ struct Scsi_Host *host;
+ struct rtsx_dev *dev;
+ int err = 0;
+ struct task_struct *th;
+
+ dev_dbg(&pci->dev, "Realtek PCI-E card reader detected\n");
+
+ err = pci_enable_device(pci);
+ if (err < 0) {
+ dev_err(&pci->dev, "PCI enable device failed!\n");
+ return err;
+ }
+
+ err = pci_request_regions(pci, CR_DRIVER_NAME);
+ if (err < 0) {
+ dev_err(&pci->dev, "PCI request regions for %s failed!\n",
+ CR_DRIVER_NAME);
+ pci_disable_device(pci);
+ return err;
+ }
+
+ /*
+ * Ask the SCSI layer to allocate a host structure, with extra
+ * space at the end for our private rtsx_dev structure.
+ */
+ host = scsi_host_alloc(&rtsx_host_template, sizeof(*dev));
+ if (!host) {
+ dev_err(&pci->dev, "Unable to allocate the scsi host\n");
+ pci_release_regions(pci);
+ pci_disable_device(pci);
+ return -ENOMEM;
+ }
+
+ dev = host_to_rtsx(host);
+ memset(dev, 0, sizeof(struct rtsx_dev));
+
+ dev->chip = kzalloc(sizeof(struct rtsx_chip), GFP_KERNEL);
+ if (dev->chip == NULL) {
+ err = -ENOMEM;
+ goto errout;
+ }
+
+ spin_lock_init(&dev->reg_lock);
+ mutex_init(&(dev->dev_mutex));
+ init_completion(&dev->cmnd_ready);
+ init_completion(&dev->control_exit);
+ init_completion(&dev->polling_exit);
+ init_completion(&(dev->notify));
+ init_completion(&dev->scanning_done);
+ init_waitqueue_head(&dev->delay_wait);
+
+ dev->pci = pci;
+ dev->irq = -1;
+
+ dev_info(&pci->dev, "Resource length: 0x%x\n",
+ (unsigned int)pci_resource_len(pci, 0));
+ dev->addr = pci_resource_start(pci, 0);
+ dev->remap_addr = ioremap_nocache(dev->addr, pci_resource_len(pci, 0));
+ if (dev->remap_addr == NULL) {
+ dev_err(&pci->dev, "ioremap error\n");
+ err = -ENXIO;
+ goto errout;
+ }
+
+ /*
+ * Using "unsigned long" cast here to eliminate gcc warning in
+ * 64-bit system
+ */
+ dev_info(&pci->dev, "Original address: 0x%lx, remapped address: 0x%lx\n",
+ (unsigned long)(dev->addr), (unsigned long)(dev->remap_addr));
+
+ dev->rtsx_resv_buf = dma_alloc_coherent(&(pci->dev), RTSX_RESV_BUF_LEN,
+ &(dev->rtsx_resv_buf_addr), GFP_KERNEL);
+ if (dev->rtsx_resv_buf == NULL) {
+ dev_err(&pci->dev, "alloc dma buffer fail\n");
+ err = -ENXIO;
+ goto errout;
+ }
+ dev->chip->host_cmds_ptr = dev->rtsx_resv_buf;
+ dev->chip->host_cmds_addr = dev->rtsx_resv_buf_addr;
+ dev->chip->host_sg_tbl_ptr = dev->rtsx_resv_buf + HOST_CMDS_BUF_LEN;
+ dev->chip->host_sg_tbl_addr = dev->rtsx_resv_buf_addr +
+ HOST_CMDS_BUF_LEN;
+
+ dev->chip->rtsx = dev;
+
+ rtsx_init_options(dev->chip);
+
+ dev_info(&pci->dev, "pci->irq = %d\n", pci->irq);
+
+ if (dev->chip->msi_en) {
+ if (pci_enable_msi(pci) < 0)
+ dev->chip->msi_en = 0;
+ }
+
+ if (rtsx_acquire_irq(dev) < 0) {
+ err = -EBUSY;
+ goto errout;
+ }
+
+ pci_set_master(pci);
+ synchronize_irq(dev->irq);
+
+ rtsx_init_chip(dev->chip);
+
+ /* set the supported max_lun and max_id for the scsi host
+ * NOTE: the minimal value of max_id is 1 */
+ host->max_id = 1;
+ host->max_lun = dev->chip->max_lun;
+
+ /* Start up our control thread */
+ th = kthread_run(rtsx_control_thread, dev, CR_DRIVER_NAME);
+ if (IS_ERR(th)) {
+ dev_err(&pci->dev, "Unable to start control thread\n");
+ err = PTR_ERR(th);
+ goto errout;
+ }
+ dev->ctl_thread = th;
+
+ err = scsi_add_host(host, &pci->dev);
+ if (err) {
+ dev_err(&pci->dev, "Unable to add the scsi host\n");
+ goto errout;
+ }
+
+ /* Start up the thread for delayed SCSI-device scanning */
+ th = kthread_run(rtsx_scan_thread, dev, "rtsx-scan");
+ if (IS_ERR(th)) {
+ dev_err(&pci->dev, "Unable to start the device-scanning thread\n");
+ complete(&dev->scanning_done);
+ quiesce_and_remove_host(dev);
+ err = PTR_ERR(th);
+ goto errout;
+ }
+
+ /* Start up the thread for polling thread */
+ th = kthread_run(rtsx_polling_thread, dev, "rtsx-polling");
+ if (IS_ERR(th)) {
+ dev_err(&pci->dev, "Unable to start the device-polling thread\n");
+ quiesce_and_remove_host(dev);
+ err = PTR_ERR(th);
+ goto errout;
+ }
+ dev->polling_thread = th;
+
+ pci_set_drvdata(pci, dev);
+
+ return 0;
+
+ /* We come here if there are any problems */
+errout:
+ dev_err(&pci->dev, "rtsx_probe() failed\n");
+ release_everything(dev);
+
+ return err;
+}
+
+
+static void rtsx_remove(struct pci_dev *pci)
+{
+ struct rtsx_dev *dev = pci_get_drvdata(pci);
+
+ dev_info(&pci->dev, "rtsx_remove() called\n");
+
+ quiesce_and_remove_host(dev);
+ release_everything(dev);
+
+ pci_set_drvdata(pci, NULL);
+}
+
+/* PCI IDs */
+static const struct pci_device_id rtsx_ids[] = {
+ { PCI_DEVICE(PCI_VENDOR_ID_REALTEK, 0x5208),
+ PCI_CLASS_OTHERS << 16, 0xFF0000 },
+ { PCI_DEVICE(PCI_VENDOR_ID_REALTEK, 0x5288),
+ PCI_CLASS_OTHERS << 16, 0xFF0000 },
+ { 0, },
+};
+
+MODULE_DEVICE_TABLE(pci, rtsx_ids);
+
+/* pci_driver definition */
+static struct pci_driver rtsx_driver = {
+ .name = CR_DRIVER_NAME,
+ .id_table = rtsx_ids,
+ .probe = rtsx_probe,
+ .remove = rtsx_remove,
+#ifdef CONFIG_PM
+ .suspend = rtsx_suspend,
+ .resume = rtsx_resume,
+#endif
+ .shutdown = rtsx_shutdown,
+};
+
+module_pci_driver(rtsx_driver);
diff --git a/drivers/staging/rts5208/rtsx.h b/drivers/staging/rts5208/rtsx.h
new file mode 100644
index 000000000..262441bcf
--- /dev/null
+++ b/drivers/staging/rts5208/rtsx.h
@@ -0,0 +1,191 @@
+/* Driver for Realtek PCI-Express card reader
+ * Header file
+ *
+ * Copyright(c) 2009-2013 Realtek Semiconductor Corp. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, 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, see <http://www.gnu.org/licenses/>.
+ *
+ * Author:
+ * Wei WANG (wei_wang@realsil.com.cn)
+ * Micky Ching (micky_ching@realsil.com.cn)
+ */
+
+#ifndef __REALTEK_RTSX_H
+#define __REALTEK_RTSX_H
+
+#include <linux/io.h>
+#include <linux/bitops.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/slab.h>
+#include <linux/pci.h>
+#include <linux/mutex.h>
+#include <linux/cdrom.h>
+#include <linux/workqueue.h>
+#include <linux/timer.h>
+#include <linux/time.h>
+
+#include <scsi/scsi.h>
+#include <scsi/scsi_cmnd.h>
+#include <scsi/scsi_device.h>
+#include <scsi/scsi_devinfo.h>
+#include <scsi/scsi_eh.h>
+#include <scsi/scsi_host.h>
+
+#define CR_DRIVER_NAME "rts5208"
+
+#define pci_get_bus_and_slot(bus, devfn) \
+ pci_get_domain_bus_and_slot(0, (bus), (devfn))
+
+/*
+ * macros for easy use
+ */
+#define rtsx_writel(chip, reg, value) \
+ iowrite32(value, (chip)->rtsx->remap_addr + reg)
+#define rtsx_readl(chip, reg) \
+ ioread32((chip)->rtsx->remap_addr + reg)
+#define rtsx_writew(chip, reg, value) \
+ iowrite16(value, (chip)->rtsx->remap_addr + reg)
+#define rtsx_readw(chip, reg) \
+ ioread16((chip)->rtsx->remap_addr + reg)
+#define rtsx_writeb(chip, reg, value) \
+ iowrite8(value, (chip)->rtsx->remap_addr + reg)
+#define rtsx_readb(chip, reg) \
+ ioread8((chip)->rtsx->remap_addr + reg)
+
+#define rtsx_read_config_byte(chip, where, val) \
+ pci_read_config_byte((chip)->rtsx->pci, where, val)
+
+#define rtsx_write_config_byte(chip, where, val) \
+ pci_write_config_byte((chip)->rtsx->pci, where, val)
+
+#define wait_timeout_x(task_state, msecs) \
+do { \
+ set_current_state((task_state)); \
+ schedule_timeout((msecs) * HZ / 1000); \
+} while (0)
+#define wait_timeout(msecs) wait_timeout_x(TASK_INTERRUPTIBLE, (msecs))
+
+
+#define STATE_TRANS_NONE 0
+#define STATE_TRANS_CMD 1
+#define STATE_TRANS_BUF 2
+#define STATE_TRANS_SG 3
+
+#define TRANS_NOT_READY 0
+#define TRANS_RESULT_OK 1
+#define TRANS_RESULT_FAIL 2
+
+#define SCSI_LUN(srb) ((srb)->device->lun)
+
+typedef unsigned long DELAY_PARA_T;
+
+struct rtsx_chip;
+
+struct rtsx_dev {
+ struct pci_dev *pci;
+
+ /* pci resources */
+ unsigned long addr;
+ void __iomem *remap_addr;
+ int irq;
+
+ /* locks */
+ spinlock_t reg_lock;
+
+ struct task_struct *ctl_thread; /* the control thread */
+ struct task_struct *polling_thread; /* the polling thread */
+
+ /* mutual exclusion and synchronization structures */
+ struct completion cmnd_ready; /* to sleep thread on */
+ struct completion control_exit; /* control thread exit */
+ struct completion polling_exit; /* polling thread exit */
+ struct completion notify; /* thread begin/end */
+ struct completion scanning_done; /* wait for scan thread */
+
+ wait_queue_head_t delay_wait; /* wait during scan, reset */
+ struct mutex dev_mutex;
+
+ /* host reserved buffer */
+ void *rtsx_resv_buf;
+ dma_addr_t rtsx_resv_buf_addr;
+
+ char trans_result;
+ char trans_state;
+
+ struct completion *done;
+ /* Whether interrupt handler should care card cd info */
+ u32 check_card_cd;
+
+ struct rtsx_chip *chip;
+};
+
+typedef struct rtsx_dev rtsx_dev_t;
+
+/* Convert between rtsx_dev and the corresponding Scsi_Host */
+static inline struct Scsi_Host *rtsx_to_host(struct rtsx_dev *dev)
+{
+ return container_of((void *) dev, struct Scsi_Host, hostdata);
+}
+static inline struct rtsx_dev *host_to_rtsx(struct Scsi_Host *host)
+{
+ return (struct rtsx_dev *) host->hostdata;
+}
+
+static inline void get_current_time(u8 *timeval_buf, int buf_len)
+{
+ struct timeval tv;
+
+ if (!timeval_buf || (buf_len < 8))
+ return;
+
+ do_gettimeofday(&tv);
+
+ timeval_buf[0] = (u8)(tv.tv_sec >> 24);
+ timeval_buf[1] = (u8)(tv.tv_sec >> 16);
+ timeval_buf[2] = (u8)(tv.tv_sec >> 8);
+ timeval_buf[3] = (u8)(tv.tv_sec);
+ timeval_buf[4] = (u8)(tv.tv_usec >> 24);
+ timeval_buf[5] = (u8)(tv.tv_usec >> 16);
+ timeval_buf[6] = (u8)(tv.tv_usec >> 8);
+ timeval_buf[7] = (u8)(tv.tv_usec);
+}
+
+/* The scsi_lock() and scsi_unlock() macros protect the sm_state and the
+ * single queue element srb for write access */
+#define scsi_unlock(host) spin_unlock_irq(host->host_lock)
+#define scsi_lock(host) spin_lock_irq(host->host_lock)
+
+#define lock_state(chip) spin_lock_irq(&((chip)->rtsx->reg_lock))
+#define unlock_state(chip) spin_unlock_irq(&((chip)->rtsx->reg_lock))
+
+/* struct scsi_cmnd transfer buffer access utilities */
+enum xfer_buf_dir {TO_XFER_BUF, FROM_XFER_BUF};
+
+int rtsx_read_pci_cfg_byte(u8 bus, u8 dev, u8 func, u8 offset, u8 *val);
+
+#define _MSG_TRACE
+
+#include "trace.h"
+#include "rtsx_chip.h"
+#include "rtsx_transport.h"
+#include "rtsx_scsi.h"
+#include "rtsx_card.h"
+#include "rtsx_sys.h"
+#include "general.h"
+
+#endif /* __REALTEK_RTSX_H */
diff --git a/drivers/staging/rts5208/rtsx_card.c b/drivers/staging/rts5208/rtsx_card.c
new file mode 100644
index 000000000..437436f5d
--- /dev/null
+++ b/drivers/staging/rts5208/rtsx_card.c
@@ -0,0 +1,1235 @@
+/* Driver for Realtek PCI-Express card reader
+ *
+ * Copyright(c) 2009-2013 Realtek Semiconductor Corp. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, 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, see <http://www.gnu.org/licenses/>.
+ *
+ * Author:
+ * Wei WANG (wei_wang@realsil.com.cn)
+ * Micky Ching (micky_ching@realsil.com.cn)
+ */
+
+#include <linux/blkdev.h>
+#include <linux/kthread.h>
+#include <linux/sched.h>
+#include <linux/workqueue.h>
+#include <linux/kernel.h>
+
+#include "rtsx.h"
+#include "sd.h"
+#include "xd.h"
+#include "ms.h"
+
+void do_remaining_work(struct rtsx_chip *chip)
+{
+ struct sd_info *sd_card = &(chip->sd_card);
+#ifdef XD_DELAY_WRITE
+ struct xd_info *xd_card = &(chip->xd_card);
+#endif
+ struct ms_info *ms_card = &(chip->ms_card);
+
+ if (chip->card_ready & SD_CARD) {
+ if (sd_card->seq_mode) {
+ rtsx_set_stat(chip, RTSX_STAT_RUN);
+ sd_card->cleanup_counter++;
+ } else {
+ sd_card->cleanup_counter = 0;
+ }
+ }
+
+#ifdef XD_DELAY_WRITE
+ if (chip->card_ready & XD_CARD) {
+ if (xd_card->delay_write.delay_write_flag) {
+ rtsx_set_stat(chip, RTSX_STAT_RUN);
+ xd_card->cleanup_counter++;
+ } else {
+ xd_card->cleanup_counter = 0;
+ }
+ }
+#endif
+
+ if (chip->card_ready & MS_CARD) {
+ if (CHK_MSPRO(ms_card)) {
+ if (ms_card->seq_mode) {
+ rtsx_set_stat(chip, RTSX_STAT_RUN);
+ ms_card->cleanup_counter++;
+ } else {
+ ms_card->cleanup_counter = 0;
+ }
+ } else {
+#ifdef MS_DELAY_WRITE
+ if (ms_card->delay_write.delay_write_flag) {
+ rtsx_set_stat(chip, RTSX_STAT_RUN);
+ ms_card->cleanup_counter++;
+ } else {
+ ms_card->cleanup_counter = 0;
+ }
+#endif
+ }
+ }
+
+ if (sd_card->cleanup_counter > POLLING_WAIT_CNT)
+ sd_cleanup_work(chip);
+
+ if (xd_card->cleanup_counter > POLLING_WAIT_CNT)
+ xd_cleanup_work(chip);
+
+ if (ms_card->cleanup_counter > POLLING_WAIT_CNT)
+ ms_cleanup_work(chip);
+}
+
+void try_to_switch_sdio_ctrl(struct rtsx_chip *chip)
+{
+ u8 reg1 = 0, reg2 = 0;
+
+ rtsx_read_register(chip, 0xFF34, &reg1);
+ rtsx_read_register(chip, 0xFF38, &reg2);
+ dev_dbg(rtsx_dev(chip), "reg 0xFF34: 0x%x, reg 0xFF38: 0x%x\n",
+ reg1, reg2);
+ if ((reg1 & 0xC0) && (reg2 & 0xC0)) {
+ chip->sd_int = 1;
+ rtsx_write_register(chip, SDIO_CTRL, 0xFF,
+ SDIO_BUS_CTRL | SDIO_CD_CTRL);
+ rtsx_write_register(chip, PWR_GATE_CTRL,
+ LDO3318_PWR_MASK, LDO_ON);
+ }
+}
+
+#ifdef SUPPORT_SDIO_ASPM
+void dynamic_configure_sdio_aspm(struct rtsx_chip *chip)
+{
+ u8 buf[12], reg;
+ int i;
+
+ for (i = 0; i < 12; i++)
+ rtsx_read_register(chip, 0xFF08 + i, &buf[i]);
+ rtsx_read_register(chip, 0xFF25, &reg);
+ if ((memcmp(buf, chip->sdio_raw_data, 12) != 0) || (reg & 0x03)) {
+ chip->sdio_counter = 0;
+ chip->sdio_idle = 0;
+ } else {
+ if (!chip->sdio_idle) {
+ chip->sdio_counter++;
+ if (chip->sdio_counter >= SDIO_IDLE_COUNT) {
+ chip->sdio_counter = 0;
+ chip->sdio_idle = 1;
+ }
+ }
+ }
+ memcpy(chip->sdio_raw_data, buf, 12);
+
+ if (chip->sdio_idle) {
+ if (!chip->sdio_aspm) {
+ dev_dbg(rtsx_dev(chip), "SDIO enter ASPM!\n");
+ rtsx_write_register(chip, ASPM_FORCE_CTL, 0xFC,
+ 0x30 | (chip->aspm_level[1] << 2));
+ chip->sdio_aspm = 1;
+ }
+ } else {
+ if (chip->sdio_aspm) {
+ dev_dbg(rtsx_dev(chip), "SDIO exit ASPM!\n");
+ rtsx_write_register(chip, ASPM_FORCE_CTL, 0xFC, 0x30);
+ chip->sdio_aspm = 0;
+ }
+ }
+}
+#endif
+
+void do_reset_sd_card(struct rtsx_chip *chip)
+{
+ int retval;
+
+ dev_dbg(rtsx_dev(chip), "%s: %d, card2lun = 0x%x\n", __func__,
+ chip->sd_reset_counter, chip->card2lun[SD_CARD]);
+
+ if (chip->card2lun[SD_CARD] >= MAX_ALLOWED_LUN_CNT) {
+ clear_bit(SD_NR, &(chip->need_reset));
+ chip->sd_reset_counter = 0;
+ chip->sd_show_cnt = 0;
+ return;
+ }
+
+ chip->rw_fail_cnt[chip->card2lun[SD_CARD]] = 0;
+
+ rtsx_set_stat(chip, RTSX_STAT_RUN);
+ rtsx_write_register(chip, SDIO_CTRL, 0xFF, 0);
+
+ retval = reset_sd_card(chip);
+ if (chip->need_release & SD_CARD)
+ return;
+ if (retval == STATUS_SUCCESS) {
+ clear_bit(SD_NR, &(chip->need_reset));
+ chip->sd_reset_counter = 0;
+ chip->sd_show_cnt = 0;
+ chip->card_ready |= SD_CARD;
+ chip->card_fail &= ~SD_CARD;
+ chip->rw_card[chip->card2lun[SD_CARD]] = sd_rw;
+ } else {
+ if (chip->sd_io || (chip->sd_reset_counter >= MAX_RESET_CNT)) {
+ clear_bit(SD_NR, &(chip->need_reset));
+ chip->sd_reset_counter = 0;
+ chip->sd_show_cnt = 0;
+ } else {
+ chip->sd_reset_counter++;
+ }
+ chip->card_ready &= ~SD_CARD;
+ chip->card_fail |= SD_CARD;
+ chip->capacity[chip->card2lun[SD_CARD]] = 0;
+ chip->rw_card[chip->card2lun[SD_CARD]] = NULL;
+
+ rtsx_write_register(chip, CARD_OE, SD_OUTPUT_EN, 0);
+ if (!chip->ft2_fast_mode)
+ card_power_off(chip, SD_CARD);
+ if (chip->sd_io) {
+ chip->sd_int = 0;
+ try_to_switch_sdio_ctrl(chip);
+ } else {
+ disable_card_clock(chip, SD_CARD);
+ }
+ }
+}
+
+void do_reset_xd_card(struct rtsx_chip *chip)
+{
+ int retval;
+
+ dev_dbg(rtsx_dev(chip), "%s: %d, card2lun = 0x%x\n", __func__,
+ chip->xd_reset_counter, chip->card2lun[XD_CARD]);
+
+ if (chip->card2lun[XD_CARD] >= MAX_ALLOWED_LUN_CNT) {
+ clear_bit(XD_NR, &(chip->need_reset));
+ chip->xd_reset_counter = 0;
+ chip->xd_show_cnt = 0;
+ return;
+ }
+
+ chip->rw_fail_cnt[chip->card2lun[XD_CARD]] = 0;
+
+ rtsx_set_stat(chip, RTSX_STAT_RUN);
+ rtsx_write_register(chip, SDIO_CTRL, 0xFF, 0);
+
+ retval = reset_xd_card(chip);
+ if (chip->need_release & XD_CARD)
+ return;
+ if (retval == STATUS_SUCCESS) {
+ clear_bit(XD_NR, &(chip->need_reset));
+ chip->xd_reset_counter = 0;
+ chip->card_ready |= XD_CARD;
+ chip->card_fail &= ~XD_CARD;
+ chip->rw_card[chip->card2lun[XD_CARD]] = xd_rw;
+ } else {
+ if (chip->xd_reset_counter >= MAX_RESET_CNT) {
+ clear_bit(XD_NR, &(chip->need_reset));
+ chip->xd_reset_counter = 0;
+ chip->xd_show_cnt = 0;
+ } else {
+ chip->xd_reset_counter++;
+ }
+ chip->card_ready &= ~XD_CARD;
+ chip->card_fail |= XD_CARD;
+ chip->capacity[chip->card2lun[XD_CARD]] = 0;
+ chip->rw_card[chip->card2lun[XD_CARD]] = NULL;
+
+ rtsx_write_register(chip, CARD_OE, XD_OUTPUT_EN, 0);
+ if (!chip->ft2_fast_mode)
+ card_power_off(chip, XD_CARD);
+ disable_card_clock(chip, XD_CARD);
+ }
+}
+
+void do_reset_ms_card(struct rtsx_chip *chip)
+{
+ int retval;
+
+ dev_dbg(rtsx_dev(chip), "%s: %d, card2lun = 0x%x\n", __func__,
+ chip->ms_reset_counter, chip->card2lun[MS_CARD]);
+
+ if (chip->card2lun[MS_CARD] >= MAX_ALLOWED_LUN_CNT) {
+ clear_bit(MS_NR, &(chip->need_reset));
+ chip->ms_reset_counter = 0;
+ chip->ms_show_cnt = 0;
+ return;
+ }
+
+ chip->rw_fail_cnt[chip->card2lun[MS_CARD]] = 0;
+
+ rtsx_set_stat(chip, RTSX_STAT_RUN);
+ rtsx_write_register(chip, SDIO_CTRL, 0xFF, 0);
+
+ retval = reset_ms_card(chip);
+ if (chip->need_release & MS_CARD)
+ return;
+ if (retval == STATUS_SUCCESS) {
+ clear_bit(MS_NR, &(chip->need_reset));
+ chip->ms_reset_counter = 0;
+ chip->card_ready |= MS_CARD;
+ chip->card_fail &= ~MS_CARD;
+ chip->rw_card[chip->card2lun[MS_CARD]] = ms_rw;
+ } else {
+ if (chip->ms_reset_counter >= MAX_RESET_CNT) {
+ clear_bit(MS_NR, &(chip->need_reset));
+ chip->ms_reset_counter = 0;
+ chip->ms_show_cnt = 0;
+ } else {
+ chip->ms_reset_counter++;
+ }
+ chip->card_ready &= ~MS_CARD;
+ chip->card_fail |= MS_CARD;
+ chip->capacity[chip->card2lun[MS_CARD]] = 0;
+ chip->rw_card[chip->card2lun[MS_CARD]] = NULL;
+
+ rtsx_write_register(chip, CARD_OE, MS_OUTPUT_EN, 0);
+ if (!chip->ft2_fast_mode)
+ card_power_off(chip, MS_CARD);
+ disable_card_clock(chip, MS_CARD);
+ }
+}
+
+static void release_sdio(struct rtsx_chip *chip)
+{
+ if (chip->sd_io) {
+ rtsx_write_register(chip, CARD_STOP, SD_STOP | SD_CLR_ERR,
+ SD_STOP | SD_CLR_ERR);
+
+ if (chip->chip_insert_with_sdio) {
+ chip->chip_insert_with_sdio = 0;
+
+ if (CHECK_PID(chip, 0x5288))
+ rtsx_write_register(chip, 0xFE5A, 0x08, 0x00);
+ else
+ rtsx_write_register(chip, 0xFE70, 0x80, 0x00);
+ }
+
+ rtsx_write_register(chip, SDIO_CTRL, SDIO_CD_CTRL, 0);
+ chip->sd_io = 0;
+ }
+}
+
+void rtsx_power_off_card(struct rtsx_chip *chip)
+{
+ if ((chip->card_ready & SD_CARD) || chip->sd_io) {
+ sd_cleanup_work(chip);
+ sd_power_off_card3v3(chip);
+ }
+
+ if (chip->card_ready & XD_CARD) {
+ xd_cleanup_work(chip);
+ xd_power_off_card3v3(chip);
+ }
+
+ if (chip->card_ready & MS_CARD) {
+ ms_cleanup_work(chip);
+ ms_power_off_card3v3(chip);
+ }
+}
+
+void rtsx_release_cards(struct rtsx_chip *chip)
+{
+ chip->int_reg = rtsx_readl(chip, RTSX_BIPR);
+
+ if ((chip->card_ready & SD_CARD) || chip->sd_io) {
+ if (chip->int_reg & SD_EXIST)
+ sd_cleanup_work(chip);
+ release_sd_card(chip);
+ }
+
+ if (chip->card_ready & XD_CARD) {
+ if (chip->int_reg & XD_EXIST)
+ xd_cleanup_work(chip);
+ release_xd_card(chip);
+ }
+
+ if (chip->card_ready & MS_CARD) {
+ if (chip->int_reg & MS_EXIST)
+ ms_cleanup_work(chip);
+ release_ms_card(chip);
+ }
+}
+
+void rtsx_reset_cards(struct rtsx_chip *chip)
+{
+ if (!chip->need_reset)
+ return;
+
+ rtsx_set_stat(chip, RTSX_STAT_RUN);
+
+ rtsx_force_power_on(chip, SSC_PDCTL | OC_PDCTL);
+
+ rtsx_disable_aspm(chip);
+
+ if ((chip->need_reset & SD_CARD) && chip->chip_insert_with_sdio)
+ clear_bit(SD_NR, &(chip->need_reset));
+
+ if (chip->need_reset & XD_CARD) {
+ chip->card_exist |= XD_CARD;
+
+ if (chip->xd_show_cnt >= MAX_SHOW_CNT)
+ do_reset_xd_card(chip);
+ else
+ chip->xd_show_cnt++;
+ }
+ if (CHECK_PID(chip, 0x5288) && CHECK_BARO_PKG(chip, QFN)) {
+ if (chip->card_exist & XD_CARD) {
+ clear_bit(SD_NR, &(chip->need_reset));
+ clear_bit(MS_NR, &(chip->need_reset));
+ }
+ }
+ if (chip->need_reset & SD_CARD) {
+ chip->card_exist |= SD_CARD;
+
+ if (chip->sd_show_cnt >= MAX_SHOW_CNT) {
+ rtsx_write_register(chip, RBCTL, RB_FLUSH, RB_FLUSH);
+ do_reset_sd_card(chip);
+ } else {
+ chip->sd_show_cnt++;
+ }
+ }
+ if (chip->need_reset & MS_CARD) {
+ chip->card_exist |= MS_CARD;
+
+ if (chip->ms_show_cnt >= MAX_SHOW_CNT)
+ do_reset_ms_card(chip);
+ else
+ chip->ms_show_cnt++;
+ }
+}
+
+void rtsx_reinit_cards(struct rtsx_chip *chip, int reset_chip)
+{
+ rtsx_set_stat(chip, RTSX_STAT_RUN);
+
+ rtsx_force_power_on(chip, SSC_PDCTL | OC_PDCTL);
+
+ if (reset_chip)
+ rtsx_reset_chip(chip);
+
+ chip->int_reg = rtsx_readl(chip, RTSX_BIPR);
+
+ if ((chip->int_reg & SD_EXIST) && (chip->need_reinit & SD_CARD)) {
+ release_sdio(chip);
+ release_sd_card(chip);
+
+ wait_timeout(100);
+
+ chip->card_exist |= SD_CARD;
+ do_reset_sd_card(chip);
+ }
+
+ if ((chip->int_reg & XD_EXIST) && (chip->need_reinit & XD_CARD)) {
+ release_xd_card(chip);
+
+ wait_timeout(100);
+
+ chip->card_exist |= XD_CARD;
+ do_reset_xd_card(chip);
+ }
+
+ if ((chip->int_reg & MS_EXIST) && (chip->need_reinit & MS_CARD)) {
+ release_ms_card(chip);
+
+ wait_timeout(100);
+
+ chip->card_exist |= MS_CARD;
+ do_reset_ms_card(chip);
+ }
+
+ chip->need_reinit = 0;
+}
+
+#ifdef DISABLE_CARD_INT
+void card_cd_debounce(struct rtsx_chip *chip, unsigned long *need_reset,
+ unsigned long *need_release)
+{
+ u8 release_map = 0, reset_map = 0;
+
+ chip->int_reg = rtsx_readl(chip, RTSX_BIPR);
+
+ if (chip->card_exist) {
+ if (chip->card_exist & XD_CARD) {
+ if (!(chip->int_reg & XD_EXIST))
+ release_map |= XD_CARD;
+ } else if (chip->card_exist & SD_CARD) {
+ if (!(chip->int_reg & SD_EXIST))
+ release_map |= SD_CARD;
+ } else if (chip->card_exist & MS_CARD) {
+ if (!(chip->int_reg & MS_EXIST))
+ release_map |= MS_CARD;
+ }
+ } else {
+ if (chip->int_reg & XD_EXIST)
+ reset_map |= XD_CARD;
+ else if (chip->int_reg & SD_EXIST)
+ reset_map |= SD_CARD;
+ else if (chip->int_reg & MS_EXIST)
+ reset_map |= MS_CARD;
+ }
+
+ if (reset_map) {
+ int xd_cnt = 0, sd_cnt = 0, ms_cnt = 0;
+ int i;
+
+ for (i = 0; i < (DEBOUNCE_CNT); i++) {
+ chip->int_reg = rtsx_readl(chip, RTSX_BIPR);
+
+ if (chip->int_reg & XD_EXIST)
+ xd_cnt++;
+ else
+ xd_cnt = 0;
+
+ if (chip->int_reg & SD_EXIST)
+ sd_cnt++;
+ else
+ sd_cnt = 0;
+
+ if (chip->int_reg & MS_EXIST)
+ ms_cnt++;
+ else
+ ms_cnt = 0;
+
+ wait_timeout(30);
+ }
+
+ reset_map = 0;
+ if (!(chip->card_exist & XD_CARD) &&
+ (xd_cnt > (DEBOUNCE_CNT-1)))
+ reset_map |= XD_CARD;
+ if (!(chip->card_exist & SD_CARD) &&
+ (sd_cnt > (DEBOUNCE_CNT-1)))
+ reset_map |= SD_CARD;
+ if (!(chip->card_exist & MS_CARD) &&
+ (ms_cnt > (DEBOUNCE_CNT-1)))
+ reset_map |= MS_CARD;
+ }
+
+ if (CHECK_PID(chip, 0x5288) && CHECK_BARO_PKG(chip, QFN))
+ rtsx_write_register(chip, HOST_SLEEP_STATE, 0xC0, 0x00);
+
+ if (need_reset)
+ *need_reset = reset_map;
+ if (need_release)
+ *need_release = release_map;
+}
+#endif
+
+void rtsx_init_cards(struct rtsx_chip *chip)
+{
+ if (RTSX_TST_DELINK(chip) && (rtsx_get_stat(chip) != RTSX_STAT_SS)) {
+ dev_dbg(rtsx_dev(chip), "Reset chip in polling thread!\n");
+ rtsx_reset_chip(chip);
+ RTSX_CLR_DELINK(chip);
+ }
+
+#ifdef DISABLE_CARD_INT
+ card_cd_debounce(chip, &(chip->need_reset), &(chip->need_release));
+#endif
+
+ if (chip->need_release) {
+ if (CHECK_PID(chip, 0x5288) && CHECK_BARO_PKG(chip, QFN)) {
+ if (chip->int_reg & XD_EXIST) {
+ clear_bit(SD_NR, &(chip->need_release));
+ clear_bit(MS_NR, &(chip->need_release));
+ }
+ }
+
+ if (!(chip->card_exist & SD_CARD) && !chip->sd_io)
+ clear_bit(SD_NR, &(chip->need_release));
+ if (!(chip->card_exist & XD_CARD))
+ clear_bit(XD_NR, &(chip->need_release));
+ if (!(chip->card_exist & MS_CARD))
+ clear_bit(MS_NR, &(chip->need_release));
+
+ dev_dbg(rtsx_dev(chip), "chip->need_release = 0x%x\n",
+ (unsigned int)(chip->need_release));
+
+#ifdef SUPPORT_OCP
+ if (chip->need_release) {
+ if (chip->ocp_stat & (CARD_OC_NOW | CARD_OC_EVER))
+ rtsx_write_register(chip, OCPCLR,
+ CARD_OC_INT_CLR | CARD_OC_CLR,
+ CARD_OC_INT_CLR | CARD_OC_CLR);
+ chip->ocp_stat = 0;
+ }
+#endif
+ if (chip->need_release) {
+ rtsx_set_stat(chip, RTSX_STAT_RUN);
+ rtsx_force_power_on(chip, SSC_PDCTL | OC_PDCTL);
+ }
+
+ if (chip->need_release & SD_CARD) {
+ clear_bit(SD_NR, &(chip->need_release));
+ chip->card_exist &= ~SD_CARD;
+ chip->card_ejected &= ~SD_CARD;
+ chip->card_fail &= ~SD_CARD;
+ CLR_BIT(chip->lun_mc, chip->card2lun[SD_CARD]);
+ chip->rw_fail_cnt[chip->card2lun[SD_CARD]] = 0;
+ rtsx_write_register(chip, RBCTL, RB_FLUSH, RB_FLUSH);
+
+ release_sdio(chip);
+ release_sd_card(chip);
+ }
+
+ if (chip->need_release & XD_CARD) {
+ clear_bit(XD_NR, &(chip->need_release));
+ chip->card_exist &= ~XD_CARD;
+ chip->card_ejected &= ~XD_CARD;
+ chip->card_fail &= ~XD_CARD;
+ CLR_BIT(chip->lun_mc, chip->card2lun[XD_CARD]);
+ chip->rw_fail_cnt[chip->card2lun[XD_CARD]] = 0;
+
+ release_xd_card(chip);
+
+ if (CHECK_PID(chip, 0x5288) &&
+ CHECK_BARO_PKG(chip, QFN))
+ rtsx_write_register(chip, HOST_SLEEP_STATE,
+ 0xC0, 0xC0);
+ }
+
+ if (chip->need_release & MS_CARD) {
+ clear_bit(MS_NR, &(chip->need_release));
+ chip->card_exist &= ~MS_CARD;
+ chip->card_ejected &= ~MS_CARD;
+ chip->card_fail &= ~MS_CARD;
+ CLR_BIT(chip->lun_mc, chip->card2lun[MS_CARD]);
+ chip->rw_fail_cnt[chip->card2lun[MS_CARD]] = 0;
+
+ release_ms_card(chip);
+ }
+
+ dev_dbg(rtsx_dev(chip), "chip->card_exist = 0x%x\n",
+ chip->card_exist);
+
+ if (!chip->card_exist)
+ turn_off_led(chip, LED_GPIO);
+ }
+
+ if (chip->need_reset) {
+ dev_dbg(rtsx_dev(chip), "chip->need_reset = 0x%x\n",
+ (unsigned int)(chip->need_reset));
+
+ rtsx_reset_cards(chip);
+ }
+
+ if (chip->need_reinit) {
+ dev_dbg(rtsx_dev(chip), "chip->need_reinit = 0x%x\n",
+ (unsigned int)(chip->need_reinit));
+
+ rtsx_reinit_cards(chip, 0);
+ }
+}
+
+static inline u8 double_depth(u8 depth)
+{
+ return (depth > 1) ? (depth - 1) : depth;
+}
+
+int switch_ssc_clock(struct rtsx_chip *chip, int clk)
+{
+ int retval;
+ u8 N = (u8)(clk - 2), min_N, max_N;
+ u8 mcu_cnt, div, max_div, ssc_depth, ssc_depth_mask;
+ int sd_vpclk_phase_reset = 0;
+
+ if (chip->cur_clk == clk)
+ return STATUS_SUCCESS;
+
+ min_N = 60;
+ max_N = 120;
+ max_div = CLK_DIV_4;
+
+ dev_dbg(rtsx_dev(chip), "Switch SSC clock to %dMHz (cur_clk = %d)\n",
+ clk, chip->cur_clk);
+
+ if ((clk <= 2) || (N > max_N)) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ mcu_cnt = (u8)(125/clk + 3);
+ if (mcu_cnt > 7)
+ mcu_cnt = 7;
+
+ div = CLK_DIV_1;
+ while ((N < min_N) && (div < max_div)) {
+ N = (N + 2) * 2 - 2;
+ div++;
+ }
+ dev_dbg(rtsx_dev(chip), "N = %d, div = %d\n", N, div);
+
+ if (chip->ssc_en) {
+ ssc_depth = 0x01;
+ N -= 2;
+ } else {
+ ssc_depth = 0;
+ }
+
+ ssc_depth_mask = 0x03;
+
+ dev_dbg(rtsx_dev(chip), "ssc_depth = %d\n", ssc_depth);
+
+ rtsx_init_cmd(chip);
+ rtsx_add_cmd(chip, WRITE_REG_CMD, CLK_CTL, CLK_LOW_FREQ, CLK_LOW_FREQ);
+ rtsx_add_cmd(chip, WRITE_REG_CMD, CLK_DIV, 0xFF, (div << 4) | mcu_cnt);
+ rtsx_add_cmd(chip, WRITE_REG_CMD, SSC_CTL1, SSC_RSTB, 0);
+ rtsx_add_cmd(chip, WRITE_REG_CMD, SSC_CTL2, ssc_depth_mask, ssc_depth);
+ rtsx_add_cmd(chip, WRITE_REG_CMD, SSC_DIV_N_0, 0xFF, N);
+ rtsx_add_cmd(chip, WRITE_REG_CMD, SSC_CTL1, SSC_RSTB, SSC_RSTB);
+ if (sd_vpclk_phase_reset) {
+ rtsx_add_cmd(chip, WRITE_REG_CMD, SD_VPCLK0_CTL,
+ PHASE_NOT_RESET, 0);
+ rtsx_add_cmd(chip, WRITE_REG_CMD, SD_VPCLK0_CTL,
+ PHASE_NOT_RESET, PHASE_NOT_RESET);
+ }
+
+ retval = rtsx_send_cmd(chip, 0, WAIT_TIME);
+ if (retval < 0) {
+ rtsx_trace(chip);
+ return STATUS_ERROR;
+ }
+
+ udelay(10);
+ retval = rtsx_write_register(chip, CLK_CTL, CLK_LOW_FREQ, 0);
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+
+ chip->cur_clk = clk;
+
+ return STATUS_SUCCESS;
+}
+
+int switch_normal_clock(struct rtsx_chip *chip, int clk)
+{
+ int retval;
+ u8 sel, div, mcu_cnt;
+ int sd_vpclk_phase_reset = 0;
+
+ if (chip->cur_clk == clk)
+ return STATUS_SUCCESS;
+
+ switch (clk) {
+ case CLK_20:
+ dev_dbg(rtsx_dev(chip), "Switch clock to 20MHz\n");
+ sel = SSC_80;
+ div = CLK_DIV_4;
+ mcu_cnt = 7;
+ break;
+
+ case CLK_30:
+ dev_dbg(rtsx_dev(chip), "Switch clock to 30MHz\n");
+ sel = SSC_120;
+ div = CLK_DIV_4;
+ mcu_cnt = 7;
+ break;
+
+ case CLK_40:
+ dev_dbg(rtsx_dev(chip), "Switch clock to 40MHz\n");
+ sel = SSC_80;
+ div = CLK_DIV_2;
+ mcu_cnt = 7;
+ break;
+
+ case CLK_50:
+ dev_dbg(rtsx_dev(chip), "Switch clock to 50MHz\n");
+ sel = SSC_100;
+ div = CLK_DIV_2;
+ mcu_cnt = 6;
+ break;
+
+ case CLK_60:
+ dev_dbg(rtsx_dev(chip), "Switch clock to 60MHz\n");
+ sel = SSC_120;
+ div = CLK_DIV_2;
+ mcu_cnt = 6;
+ break;
+
+ case CLK_80:
+ dev_dbg(rtsx_dev(chip), "Switch clock to 80MHz\n");
+ sel = SSC_80;
+ div = CLK_DIV_1;
+ mcu_cnt = 5;
+ break;
+
+ case CLK_100:
+ dev_dbg(rtsx_dev(chip), "Switch clock to 100MHz\n");
+ sel = SSC_100;
+ div = CLK_DIV_1;
+ mcu_cnt = 5;
+ break;
+
+ case CLK_120:
+ dev_dbg(rtsx_dev(chip), "Switch clock to 120MHz\n");
+ sel = SSC_120;
+ div = CLK_DIV_1;
+ mcu_cnt = 5;
+ break;
+
+ case CLK_150:
+ dev_dbg(rtsx_dev(chip), "Switch clock to 150MHz\n");
+ sel = SSC_150;
+ div = CLK_DIV_1;
+ mcu_cnt = 4;
+ break;
+
+ case CLK_200:
+ dev_dbg(rtsx_dev(chip), "Switch clock to 200MHz\n");
+ sel = SSC_200;
+ div = CLK_DIV_1;
+ mcu_cnt = 4;
+ break;
+
+ default:
+ dev_dbg(rtsx_dev(chip), "Try to switch to an illegal clock (%d)\n",
+ clk);
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ retval = rtsx_write_register(chip, CLK_CTL, 0xFF, CLK_LOW_FREQ);
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+ if (sd_vpclk_phase_reset) {
+ retval = rtsx_write_register(chip, SD_VPCLK0_CTL,
+ PHASE_NOT_RESET, 0);
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+ retval = rtsx_write_register(chip, SD_VPCLK1_CTL,
+ PHASE_NOT_RESET, 0);
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+ }
+ retval = rtsx_write_register(chip, CLK_DIV, 0xFF,
+ (div << 4) | mcu_cnt);
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+ retval = rtsx_write_register(chip, CLK_SEL, 0xFF, sel);
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+
+ if (sd_vpclk_phase_reset) {
+ udelay(200);
+ retval = rtsx_write_register(chip, SD_VPCLK0_CTL,
+ PHASE_NOT_RESET, PHASE_NOT_RESET);
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+ retval = rtsx_write_register(chip, SD_VPCLK1_CTL,
+ PHASE_NOT_RESET, PHASE_NOT_RESET);
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+ udelay(200);
+ }
+ retval = rtsx_write_register(chip, CLK_CTL, 0xFF, 0);
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+
+ chip->cur_clk = clk;
+
+ return STATUS_SUCCESS;
+}
+
+void trans_dma_enable(enum dma_data_direction dir, struct rtsx_chip *chip,
+ u32 byte_cnt, u8 pack_size)
+{
+ if (pack_size > DMA_1024)
+ pack_size = DMA_512;
+
+ rtsx_add_cmd(chip, WRITE_REG_CMD, IRQSTAT0, DMA_DONE_INT, DMA_DONE_INT);
+
+ rtsx_add_cmd(chip, WRITE_REG_CMD, DMATC3, 0xFF, (u8)(byte_cnt >> 24));
+ rtsx_add_cmd(chip, WRITE_REG_CMD, DMATC2, 0xFF, (u8)(byte_cnt >> 16));
+ rtsx_add_cmd(chip, WRITE_REG_CMD, DMATC1, 0xFF, (u8)(byte_cnt >> 8));
+ rtsx_add_cmd(chip, WRITE_REG_CMD, DMATC0, 0xFF, (u8)byte_cnt);
+
+ if (dir == DMA_FROM_DEVICE) {
+ rtsx_add_cmd(chip, WRITE_REG_CMD, DMACTL,
+ 0x03 | DMA_PACK_SIZE_MASK,
+ DMA_DIR_FROM_CARD | DMA_EN | pack_size);
+ } else {
+ rtsx_add_cmd(chip, WRITE_REG_CMD, DMACTL,
+ 0x03 | DMA_PACK_SIZE_MASK,
+ DMA_DIR_TO_CARD | DMA_EN | pack_size);
+ }
+
+ rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_DATA_SOURCE, 0x01, RING_BUFFER);
+}
+
+int enable_card_clock(struct rtsx_chip *chip, u8 card)
+{
+ int retval;
+ u8 clk_en = 0;
+
+ if (card & XD_CARD)
+ clk_en |= XD_CLK_EN;
+ if (card & SD_CARD)
+ clk_en |= SD_CLK_EN;
+ if (card & MS_CARD)
+ clk_en |= MS_CLK_EN;
+
+ retval = rtsx_write_register(chip, CARD_CLK_EN, clk_en, clk_en);
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+
+ return STATUS_SUCCESS;
+}
+
+int disable_card_clock(struct rtsx_chip *chip, u8 card)
+{
+ int retval;
+ u8 clk_en = 0;
+
+ if (card & XD_CARD)
+ clk_en |= XD_CLK_EN;
+ if (card & SD_CARD)
+ clk_en |= SD_CLK_EN;
+ if (card & MS_CARD)
+ clk_en |= MS_CLK_EN;
+
+ retval = rtsx_write_register(chip, CARD_CLK_EN, clk_en, 0);
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+
+ return STATUS_SUCCESS;
+}
+
+int card_power_on(struct rtsx_chip *chip, u8 card)
+{
+ int retval;
+ u8 mask, val1, val2;
+
+ if (CHECK_LUN_MODE(chip, SD_MS_2LUN) && (card == MS_CARD)) {
+ mask = MS_POWER_MASK;
+ val1 = MS_PARTIAL_POWER_ON;
+ val2 = MS_POWER_ON;
+ } else {
+ mask = SD_POWER_MASK;
+ val1 = SD_PARTIAL_POWER_ON;
+ val2 = SD_POWER_ON;
+ }
+
+ rtsx_init_cmd(chip);
+ rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_PWR_CTL, mask, val1);
+
+ retval = rtsx_send_cmd(chip, 0, 100);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ udelay(chip->pmos_pwr_on_interval);
+
+ rtsx_init_cmd(chip);
+ rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_PWR_CTL, mask, val2);
+
+ retval = rtsx_send_cmd(chip, 0, 100);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ return STATUS_SUCCESS;
+}
+
+int card_power_off(struct rtsx_chip *chip, u8 card)
+{
+ int retval;
+ u8 mask, val;
+
+ if (CHECK_LUN_MODE(chip, SD_MS_2LUN) && (card == MS_CARD)) {
+ mask = MS_POWER_MASK;
+ val = MS_POWER_OFF;
+ } else {
+ mask = SD_POWER_MASK;
+ val = SD_POWER_OFF;
+ }
+
+ retval = rtsx_write_register(chip, CARD_PWR_CTL, mask, val);
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+
+ return STATUS_SUCCESS;
+}
+
+int card_rw(struct scsi_cmnd *srb, struct rtsx_chip *chip,
+ u32 sec_addr, u16 sec_cnt)
+{
+ int retval;
+ unsigned int lun = SCSI_LUN(srb);
+ int i;
+
+ if (chip->rw_card[lun] == NULL) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ for (i = 0; i < 3; i++) {
+ chip->rw_need_retry = 0;
+
+ retval = chip->rw_card[lun](srb, chip, sec_addr, sec_cnt);
+ if (retval != STATUS_SUCCESS) {
+ if (rtsx_check_chip_exist(chip) != STATUS_SUCCESS) {
+ rtsx_release_chip(chip);
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+ if (detect_card_cd(chip, chip->cur_card) !=
+ STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ if (!chip->rw_need_retry) {
+ dev_dbg(rtsx_dev(chip), "RW fail, but no need to retry\n");
+ break;
+ }
+ } else {
+ chip->rw_need_retry = 0;
+ break;
+ }
+
+ dev_dbg(rtsx_dev(chip), "Retry RW, (i = %d)\n", i);
+ }
+
+ return retval;
+}
+
+int card_share_mode(struct rtsx_chip *chip, int card)
+{
+ int retval;
+ u8 mask, value;
+
+ if (CHECK_PID(chip, 0x5208)) {
+ mask = CARD_SHARE_MASK;
+ if (card == SD_CARD)
+ value = CARD_SHARE_48_SD;
+ else if (card == MS_CARD)
+ value = CARD_SHARE_48_MS;
+ else if (card == XD_CARD)
+ value = CARD_SHARE_48_XD;
+ else {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ } else if (CHECK_PID(chip, 0x5288)) {
+ mask = 0x03;
+ if (card == SD_CARD)
+ value = CARD_SHARE_BAROSSA_SD;
+ else if (card == MS_CARD)
+ value = CARD_SHARE_BAROSSA_MS;
+ else if (card == XD_CARD)
+ value = CARD_SHARE_BAROSSA_XD;
+ else {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ } else {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ retval = rtsx_write_register(chip, CARD_SHARE_MODE, mask, value);
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+
+ return STATUS_SUCCESS;
+}
+
+
+int select_card(struct rtsx_chip *chip, int card)
+{
+ int retval;
+
+ if (chip->cur_card != card) {
+ u8 mod;
+
+ if (card == SD_CARD)
+ mod = SD_MOD_SEL;
+ else if (card == MS_CARD)
+ mod = MS_MOD_SEL;
+ else if (card == XD_CARD)
+ mod = XD_MOD_SEL;
+ else if (card == SPI_CARD)
+ mod = SPI_MOD_SEL;
+ else {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ retval = rtsx_write_register(chip, CARD_SELECT, 0x07, mod);
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+ chip->cur_card = card;
+
+ retval = card_share_mode(chip, card);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+ }
+
+ return STATUS_SUCCESS;
+}
+
+void toggle_gpio(struct rtsx_chip *chip, u8 gpio)
+{
+ u8 temp_reg;
+
+ rtsx_read_register(chip, CARD_GPIO, &temp_reg);
+ temp_reg ^= (0x01 << gpio);
+ rtsx_write_register(chip, CARD_GPIO, 0xFF, temp_reg);
+}
+
+void turn_on_led(struct rtsx_chip *chip, u8 gpio)
+{
+ if (CHECK_PID(chip, 0x5288))
+ rtsx_write_register(chip, CARD_GPIO, (u8)(1 << gpio),
+ (u8)(1 << gpio));
+ else
+ rtsx_write_register(chip, CARD_GPIO, (u8)(1 << gpio), 0);
+}
+
+void turn_off_led(struct rtsx_chip *chip, u8 gpio)
+{
+ if (CHECK_PID(chip, 0x5288))
+ rtsx_write_register(chip, CARD_GPIO, (u8)(1 << gpio), 0);
+ else
+ rtsx_write_register(chip, CARD_GPIO, (u8)(1 << gpio),
+ (u8)(1 << gpio));
+}
+
+int detect_card_cd(struct rtsx_chip *chip, int card)
+{
+ u32 card_cd, status;
+
+ if (card == SD_CARD) {
+ card_cd = SD_EXIST;
+ } else if (card == MS_CARD) {
+ card_cd = MS_EXIST;
+ } else if (card == XD_CARD) {
+ card_cd = XD_EXIST;
+ } else {
+ dev_dbg(rtsx_dev(chip), "Wrong card type: 0x%x\n", card);
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ status = rtsx_readl(chip, RTSX_BIPR);
+ if (!(status & card_cd)) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ return STATUS_SUCCESS;
+}
+
+int check_card_exist(struct rtsx_chip *chip, unsigned int lun)
+{
+ if (chip->card_exist & chip->lun2card[lun])
+ return 1;
+
+ return 0;
+}
+
+int check_card_ready(struct rtsx_chip *chip, unsigned int lun)
+{
+ if (chip->card_ready & chip->lun2card[lun])
+ return 1;
+
+ return 0;
+}
+
+int check_card_wp(struct rtsx_chip *chip, unsigned int lun)
+{
+ if (chip->card_wp & chip->lun2card[lun])
+ return 1;
+
+ return 0;
+}
+
+int check_card_fail(struct rtsx_chip *chip, unsigned int lun)
+{
+ if (chip->card_fail & chip->lun2card[lun])
+ return 1;
+
+ return 0;
+}
+
+int check_card_ejected(struct rtsx_chip *chip, unsigned int lun)
+{
+ if (chip->card_ejected & chip->lun2card[lun])
+ return 1;
+
+ return 0;
+}
+
+u8 get_lun_card(struct rtsx_chip *chip, unsigned int lun)
+{
+ if ((chip->card_ready & chip->lun2card[lun]) == XD_CARD)
+ return (u8)XD_CARD;
+ else if ((chip->card_ready & chip->lun2card[lun]) == SD_CARD)
+ return (u8)SD_CARD;
+ else if ((chip->card_ready & chip->lun2card[lun]) == MS_CARD)
+ return (u8)MS_CARD;
+
+ return 0;
+}
+
+void eject_card(struct rtsx_chip *chip, unsigned int lun)
+{
+ do_remaining_work(chip);
+
+ if ((chip->card_ready & chip->lun2card[lun]) == SD_CARD) {
+ release_sd_card(chip);
+ chip->card_ejected |= SD_CARD;
+ chip->card_ready &= ~SD_CARD;
+ chip->capacity[lun] = 0;
+ } else if ((chip->card_ready & chip->lun2card[lun]) == XD_CARD) {
+ release_xd_card(chip);
+ chip->card_ejected |= XD_CARD;
+ chip->card_ready &= ~XD_CARD;
+ chip->capacity[lun] = 0;
+ } else if ((chip->card_ready & chip->lun2card[lun]) == MS_CARD) {
+ release_ms_card(chip);
+ chip->card_ejected |= MS_CARD;
+ chip->card_ready &= ~MS_CARD;
+ chip->capacity[lun] = 0;
+ }
+}
diff --git a/drivers/staging/rts5208/rtsx_card.h b/drivers/staging/rts5208/rtsx_card.h
new file mode 100644
index 000000000..8f2cf9a4e
--- /dev/null
+++ b/drivers/staging/rts5208/rtsx_card.h
@@ -0,0 +1,1103 @@
+/* Driver for Realtek PCI-Express card reader
+ * Header file
+ *
+ * Copyright(c) 2009-2013 Realtek Semiconductor Corp. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, 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, see <http://www.gnu.org/licenses/>.
+ *
+ * Author:
+ * Wei WANG (wei_wang@realsil.com.cn)
+ * Micky Ching (micky_ching@realsil.com.cn)
+ */
+
+#ifndef __REALTEK_RTSX_CARD_H
+#define __REALTEK_RTSX_CARD_H
+
+#include "rtsx.h"
+#include "rtsx_chip.h"
+#include "rtsx_transport.h"
+#include "sd.h"
+
+#define SSC_POWER_DOWN 0x01
+#define SD_OC_POWER_DOWN 0x02
+#define MS_OC_POWER_DOWN 0x04
+#define ALL_POWER_DOWN 0x07
+#define OC_POWER_DOWN 0x06
+
+#define PMOS_STRG_MASK 0x10
+#define PMOS_STRG_800mA 0x10
+#define PMOS_STRG_400mA 0x00
+
+#define POWER_OFF 0x03
+#define PARTIAL_POWER_ON 0x01
+#define POWER_ON 0x00
+
+#define MS_POWER_OFF 0x0C
+#define MS_PARTIAL_POWER_ON 0x04
+#define MS_POWER_ON 0x00
+#define MS_POWER_MASK 0x0C
+
+#define SD_POWER_OFF 0x03
+#define SD_PARTIAL_POWER_ON 0x01
+#define SD_POWER_ON 0x00
+#define SD_POWER_MASK 0x03
+
+#define XD_OUTPUT_EN 0x02
+#define SD_OUTPUT_EN 0x04
+#define MS_OUTPUT_EN 0x08
+#define SPI_OUTPUT_EN 0x10
+
+#define CLK_LOW_FREQ 0x01
+
+#define CLK_DIV_1 0x01
+#define CLK_DIV_2 0x02
+#define CLK_DIV_4 0x03
+#define CLK_DIV_8 0x04
+
+#define SSC_80 0
+#define SSC_100 1
+#define SSC_120 2
+#define SSC_150 3
+#define SSC_200 4
+
+#define XD_CLK_EN 0x02
+#define SD_CLK_EN 0x04
+#define MS_CLK_EN 0x08
+#define SPI_CLK_EN 0x10
+
+#define XD_MOD_SEL 1
+#define SD_MOD_SEL 2
+#define MS_MOD_SEL 3
+#define SPI_MOD_SEL 4
+
+#define CHANGE_CLK 0x01
+
+#define SD_CRC7_ERR 0x80
+#define SD_CRC16_ERR 0x40
+#define SD_CRC_WRITE_ERR 0x20
+#define SD_CRC_WRITE_ERR_MASK 0x1C
+#define GET_CRC_TIME_OUT 0x02
+#define SD_TUNING_COMPARE_ERR 0x01
+
+#define SD_RSP_80CLK_TIMEOUT 0x01
+
+#define SD_CLK_TOGGLE_EN 0x80
+#define SD_CLK_FORCE_STOP 0x40
+#define SD_DAT3_STATUS 0x10
+#define SD_DAT2_STATUS 0x08
+#define SD_DAT1_STATUS 0x04
+#define SD_DAT0_STATUS 0x02
+#define SD_CMD_STATUS 0x01
+
+#define SD_IO_USING_1V8 0x80
+#define SD_IO_USING_3V3 0x7F
+#define TYPE_A_DRIVING 0x00
+#define TYPE_B_DRIVING 0x01
+#define TYPE_C_DRIVING 0x02
+#define TYPE_D_DRIVING 0x03
+
+#define DDR_FIX_RX_DAT 0x00
+#define DDR_VAR_RX_DAT 0x80
+#define DDR_FIX_RX_DAT_EDGE 0x00
+#define DDR_FIX_RX_DAT_14_DELAY 0x40
+#define DDR_FIX_RX_CMD 0x00
+#define DDR_VAR_RX_CMD 0x20
+#define DDR_FIX_RX_CMD_POS_EDGE 0x00
+#define DDR_FIX_RX_CMD_14_DELAY 0x10
+#define SD20_RX_POS_EDGE 0x00
+#define SD20_RX_14_DELAY 0x08
+#define SD20_RX_SEL_MASK 0x08
+
+#define DDR_FIX_TX_CMD_DAT 0x00
+#define DDR_VAR_TX_CMD_DAT 0x80
+#define DDR_FIX_TX_DAT_14_TSU 0x00
+#define DDR_FIX_TX_DAT_12_TSU 0x40
+#define DDR_FIX_TX_CMD_NEG_EDGE 0x00
+#define DDR_FIX_TX_CMD_14_AHEAD 0x20
+#define SD20_TX_NEG_EDGE 0x00
+#define SD20_TX_14_AHEAD 0x10
+#define SD20_TX_SEL_MASK 0x10
+#define DDR_VAR_SDCLK_POL_SWAP 0x01
+
+#define SD_TRANSFER_START 0x80
+#define SD_TRANSFER_END 0x40
+#define SD_STAT_IDLE 0x20
+#define SD_TRANSFER_ERR 0x10
+#define SD_TM_NORMAL_WRITE 0x00
+#define SD_TM_AUTO_WRITE_3 0x01
+#define SD_TM_AUTO_WRITE_4 0x02
+#define SD_TM_AUTO_READ_3 0x05
+#define SD_TM_AUTO_READ_4 0x06
+#define SD_TM_CMD_RSP 0x08
+#define SD_TM_AUTO_WRITE_1 0x09
+#define SD_TM_AUTO_WRITE_2 0x0A
+#define SD_TM_NORMAL_READ 0x0C
+#define SD_TM_AUTO_READ_1 0x0D
+#define SD_TM_AUTO_READ_2 0x0E
+#define SD_TM_AUTO_TUNING 0x0F
+
+#define PHASE_CHANGE 0x80
+#define PHASE_NOT_RESET 0x40
+
+#define DCMPS_CHANGE 0x80
+#define DCMPS_CHANGE_DONE 0x40
+#define DCMPS_ERROR 0x20
+#define DCMPS_CURRENT_PHASE 0x1F
+
+#define SD_CLK_DIVIDE_0 0x00
+#define SD_CLK_DIVIDE_256 0xC0
+#define SD_CLK_DIVIDE_128 0x80
+#define SD_BUS_WIDTH_1 0x00
+#define SD_BUS_WIDTH_4 0x01
+#define SD_BUS_WIDTH_8 0x02
+#define SD_ASYNC_FIFO_NOT_RST 0x10
+#define SD_20_MODE 0x00
+#define SD_DDR_MODE 0x04
+#define SD_30_MODE 0x08
+
+#define SD_CLK_DIVIDE_MASK 0xC0
+
+#define SD_CMD_IDLE 0x80
+
+#define SD_DATA_IDLE 0x80
+
+#define DCM_RESET 0x08
+#define DCM_LOCKED 0x04
+#define DCM_208M 0x00
+#define DCM_TX 0x01
+#define DCM_RX 0x02
+
+#define DRP_START 0x80
+#define DRP_DONE 0x40
+
+#define DRP_WRITE 0x80
+#define DRP_READ 0x00
+#define DCM_WRITE_ADDRESS_50 0x50
+#define DCM_WRITE_ADDRESS_51 0x51
+#define DCM_READ_ADDRESS_00 0x00
+#define DCM_READ_ADDRESS_51 0x51
+
+#define SD_CALCULATE_CRC7 0x00
+#define SD_NO_CALCULATE_CRC7 0x80
+#define SD_CHECK_CRC16 0x00
+#define SD_NO_CHECK_CRC16 0x40
+#define SD_NO_CHECK_WAIT_CRC_TO 0x20
+#define SD_WAIT_BUSY_END 0x08
+#define SD_NO_WAIT_BUSY_END 0x00
+#define SD_CHECK_CRC7 0x00
+#define SD_NO_CHECK_CRC7 0x04
+#define SD_RSP_LEN_0 0x00
+#define SD_RSP_LEN_6 0x01
+#define SD_RSP_LEN_17 0x02
+#define SD_RSP_TYPE_R0 0x04
+#define SD_RSP_TYPE_R1 0x01
+#define SD_RSP_TYPE_R1b 0x09
+#define SD_RSP_TYPE_R2 0x02
+#define SD_RSP_TYPE_R3 0x05
+#define SD_RSP_TYPE_R4 0x05
+#define SD_RSP_TYPE_R5 0x01
+#define SD_RSP_TYPE_R6 0x01
+#define SD_RSP_TYPE_R7 0x01
+
+#define SD_RSP_80CLK_TIMEOUT_EN 0x01
+
+#define SAMPLE_TIME_RISING 0x00
+#define SAMPLE_TIME_FALLING 0x80
+#define PUSH_TIME_DEFAULT 0x00
+#define PUSH_TIME_ODD 0x40
+#define NO_EXTEND_TOGGLE 0x00
+#define EXTEND_TOGGLE_CHK 0x20
+#define MS_BUS_WIDTH_1 0x00
+#define MS_BUS_WIDTH_4 0x10
+#define MS_BUS_WIDTH_8 0x18
+#define MS_2K_SECTOR_MODE 0x04
+#define MS_512_SECTOR_MODE 0x00
+#define MS_TOGGLE_TIMEOUT_EN 0x00
+#define MS_TOGGLE_TIMEOUT_DISEN 0x01
+#define MS_NO_CHECK_INT 0x02
+
+#define WAIT_INT 0x80
+#define NO_WAIT_INT 0x00
+#define NO_AUTO_READ_INT_REG 0x00
+#define AUTO_READ_INT_REG 0x40
+#define MS_CRC16_ERR 0x20
+#define MS_RDY_TIMEOUT 0x10
+#define MS_INT_CMDNK 0x08
+#define MS_INT_BREQ 0x04
+#define MS_INT_ERR 0x02
+#define MS_INT_CED 0x01
+
+#define MS_TRANSFER_START 0x80
+#define MS_TRANSFER_END 0x40
+#define MS_TRANSFER_ERR 0x20
+#define MS_BS_STATE 0x10
+#define MS_TM_READ_BYTES 0x00
+#define MS_TM_NORMAL_READ 0x01
+#define MS_TM_WRITE_BYTES 0x04
+#define MS_TM_NORMAL_WRITE 0x05
+#define MS_TM_AUTO_READ 0x08
+#define MS_TM_AUTO_WRITE 0x0C
+
+#define CARD_SHARE_MASK 0x0F
+#define CARD_SHARE_MULTI_LUN 0x00
+#define CARD_SHARE_NORMAL 0x00
+#define CARD_SHARE_48_XD 0x02
+#define CARD_SHARE_48_SD 0x04
+#define CARD_SHARE_48_MS 0x08
+#define CARD_SHARE_BAROSSA_XD 0x00
+#define CARD_SHARE_BAROSSA_SD 0x01
+#define CARD_SHARE_BAROSSA_MS 0x02
+
+#define MS_DRIVE_8 0x00
+#define MS_DRIVE_4 0x40
+#define MS_DRIVE_12 0x80
+#define SD_DRIVE_8 0x00
+#define SD_DRIVE_4 0x10
+#define SD_DRIVE_12 0x20
+#define XD_DRIVE_8 0x00
+#define XD_DRIVE_4 0x04
+#define XD_DRIVE_12 0x08
+
+#define SPI_STOP 0x01
+#define XD_STOP 0x02
+#define SD_STOP 0x04
+#define MS_STOP 0x08
+#define SPI_CLR_ERR 0x10
+#define XD_CLR_ERR 0x20
+#define SD_CLR_ERR 0x40
+#define MS_CLR_ERR 0x80
+
+#define CRC_FIX_CLK (0x00 << 0)
+#define CRC_VAR_CLK0 (0x01 << 0)
+#define CRC_VAR_CLK1 (0x02 << 0)
+#define SD30_FIX_CLK (0x00 << 2)
+#define SD30_VAR_CLK0 (0x01 << 2)
+#define SD30_VAR_CLK1 (0x02 << 2)
+#define SAMPLE_FIX_CLK (0x00 << 4)
+#define SAMPLE_VAR_CLK0 (0x01 << 4)
+#define SAMPLE_VAR_CLK1 (0x02 << 4)
+
+#define SDIO_VER_20 0x80
+#define SDIO_VER_10 0x00
+#define SDIO_VER_CHG 0x40
+#define SDIO_BUS_AUTO_SWITCH 0x10
+
+#define PINGPONG_BUFFER 0x01
+#define RING_BUFFER 0x00
+
+#define RB_FLUSH 0x80
+
+#define DMA_DONE_INT_EN 0x80
+#define SUSPEND_INT_EN 0x40
+#define LINK_RDY_INT_EN 0x20
+#define LINK_DOWN_INT_EN 0x10
+
+#define DMA_DONE_INT 0x80
+#define SUSPEND_INT 0x40
+#define LINK_RDY_INT 0x20
+#define LINK_DOWN_INT 0x10
+
+#define MRD_ERR_INT_EN 0x40
+#define MWR_ERR_INT_EN 0x20
+#define SCSI_CMD_INT_EN 0x10
+#define TLP_RCV_INT_EN 0x08
+#define TLP_TRSMT_INT_EN 0x04
+#define MRD_COMPLETE_INT_EN 0x02
+#define MWR_COMPLETE_INT_EN 0x01
+
+#define MRD_ERR_INT 0x40
+#define MWR_ERR_INT 0x20
+#define SCSI_CMD_INT 0x10
+#define TLP_RX_INT 0x08
+#define TLP_TX_INT 0x04
+#define MRD_COMPLETE_INT 0x02
+#define MWR_COMPLETE_INT 0x01
+
+#define MSG_RX_INT_EN 0x08
+#define MRD_RX_INT_EN 0x04
+#define MWR_RX_INT_EN 0x02
+#define CPLD_RX_INT_EN 0x01
+
+#define MSG_RX_INT 0x08
+#define MRD_RX_INT 0x04
+#define MWR_RX_INT 0x02
+#define CPLD_RX_INT 0x01
+
+#define MSG_TX_INT_EN 0x08
+#define MRD_TX_INT_EN 0x04
+#define MWR_TX_INT_EN 0x02
+#define CPLD_TX_INT_EN 0x01
+
+#define MSG_TX_INT 0x08
+#define MRD_TX_INT 0x04
+#define MWR_TX_INT 0x02
+#define CPLD_TX_INT 0x01
+
+#define DMA_RST 0x80
+#define DMA_BUSY 0x04
+#define DMA_DIR_TO_CARD 0x00
+#define DMA_DIR_FROM_CARD 0x02
+#define DMA_EN 0x01
+#define DMA_128 (0 << 4)
+#define DMA_256 (1 << 4)
+#define DMA_512 (2 << 4)
+#define DMA_1024 (3 << 4)
+#define DMA_PACK_SIZE_MASK 0x30
+
+#define XD_PWR_OFF_DELAY0 0x00
+#define XD_PWR_OFF_DELAY1 0x02
+#define XD_PWR_OFF_DELAY2 0x04
+#define XD_PWR_OFF_DELAY3 0x06
+#define XD_AUTO_PWR_OFF_EN 0xF7
+#define XD_NO_AUTO_PWR_OFF 0x08
+
+#define XD_TIME_RWN_1 0x00
+#define XD_TIME_RWN_STEP 0x20
+#define XD_TIME_RW_1 0x00
+#define XD_TIME_RW_STEP 0x04
+#define XD_TIME_SETUP_1 0x00
+#define XD_TIME_SETUP_STEP 0x01
+
+#define XD_ECC2_UNCORRECTABLE 0x80
+#define XD_ECC2_ERROR 0x40
+#define XD_ECC1_UNCORRECTABLE 0x20
+#define XD_ECC1_ERROR 0x10
+#define XD_RDY 0x04
+#define XD_CE_EN 0xFD
+#define XD_CE_DISEN 0x02
+#define XD_WP_EN 0xFE
+#define XD_WP_DISEN 0x01
+
+#define XD_TRANSFER_START 0x80
+#define XD_TRANSFER_END 0x40
+#define XD_PPB_EMPTY 0x20
+#define XD_RESET 0x00
+#define XD_ERASE 0x01
+#define XD_READ_STATUS 0x02
+#define XD_READ_ID 0x03
+#define XD_READ_REDUNDANT 0x04
+#define XD_READ_PAGES 0x05
+#define XD_SET_CMD 0x06
+#define XD_NORMAL_READ 0x07
+#define XD_WRITE_PAGES 0x08
+#define XD_NORMAL_WRITE 0x09
+#define XD_WRITE_REDUNDANT 0x0A
+#define XD_SET_ADDR 0x0B
+
+#define XD_PPB_TO_SIE 0x80
+#define XD_TO_PPB_ONLY 0x00
+#define XD_BA_TRANSFORM 0x40
+#define XD_BA_NO_TRANSFORM 0x00
+#define XD_NO_CALC_ECC 0x20
+#define XD_CALC_ECC 0x00
+#define XD_IGNORE_ECC 0x10
+#define XD_CHECK_ECC 0x00
+#define XD_DIRECT_TO_RB 0x08
+#define XD_ADDR_LENGTH_0 0x00
+#define XD_ADDR_LENGTH_1 0x01
+#define XD_ADDR_LENGTH_2 0x02
+#define XD_ADDR_LENGTH_3 0x03
+#define XD_ADDR_LENGTH_4 0x04
+
+#define XD_GPG 0xFF
+#define XD_BPG 0x00
+
+#define XD_GBLK 0xFF
+#define XD_LATER_BBLK 0xF0
+
+#define XD_ECC2_ALL1 0x80
+#define XD_ECC1_ALL1 0x40
+#define XD_BA2_ALL0 0x20
+#define XD_BA1_ALL0 0x10
+#define XD_BA1_BA2_EQL 0x04
+#define XD_BA2_VALID 0x02
+#define XD_BA1_VALID 0x01
+
+#define XD_PGSTS_ZEROBIT_OVER4 0x00
+#define XD_PGSTS_NOT_FF 0x02
+#define XD_AUTO_CHK_DATA_STATUS 0x01
+
+#define RSTB_MODE_DETECT 0x80
+#define MODE_OUT_VLD 0x40
+#define MODE_OUT_0_NONE 0x00
+#define MODE_OUT_10_NONE 0x04
+#define MODE_OUT_10_47 0x05
+#define MODE_OUT_10_180 0x06
+#define MODE_OUT_10_680 0x07
+#define MODE_OUT_16_NONE 0x08
+#define MODE_OUT_16_47 0x09
+#define MODE_OUT_16_180 0x0A
+#define MODE_OUT_16_680 0x0B
+#define MODE_OUT_NONE_NONE 0x0C
+#define MODE_OUT_NONE_47 0x0D
+#define MODE_OUT_NONE_180 0x0E
+#define MODE_OUT_NONE_680 0x0F
+
+#define CARD_OC_INT_EN 0x20
+#define CARD_DETECT_EN 0x08
+
+#define MS_DETECT_EN 0x80
+#define MS_OCP_INT_EN 0x40
+#define MS_OCP_INT_CLR 0x20
+#define MS_OC_CLR 0x10
+#define SD_DETECT_EN 0x08
+#define SD_OCP_INT_EN 0x04
+#define SD_OCP_INT_CLR 0x02
+#define SD_OC_CLR 0x01
+
+#define CARD_OCP_DETECT 0x80
+#define CARD_OC_NOW 0x08
+#define CARD_OC_EVER 0x04
+
+#define MS_OCP_DETECT 0x80
+#define MS_OC_NOW 0x40
+#define MS_OC_EVER 0x20
+#define SD_OCP_DETECT 0x08
+#define SD_OC_NOW 0x04
+#define SD_OC_EVER 0x02
+
+#define CARD_OC_INT_CLR 0x08
+#define CARD_OC_CLR 0x02
+
+#define SD_OCP_GLITCH_MASK 0x07
+#define SD_OCP_GLITCH_6_4 0x00
+#define SD_OCP_GLITCH_64 0x01
+#define SD_OCP_GLITCH_640 0x02
+#define SD_OCP_GLITCH_1000 0x03
+#define SD_OCP_GLITCH_2000 0x04
+#define SD_OCP_GLITCH_4000 0x05
+#define SD_OCP_GLITCH_8000 0x06
+#define SD_OCP_GLITCH_10000 0x07
+
+#define MS_OCP_GLITCH_MASK 0x70
+#define MS_OCP_GLITCH_6_4 (0x00 << 4)
+#define MS_OCP_GLITCH_64 (0x01 << 4)
+#define MS_OCP_GLITCH_640 (0x02 << 4)
+#define MS_OCP_GLITCH_1000 (0x03 << 4)
+#define MS_OCP_GLITCH_2000 (0x04 << 4)
+#define MS_OCP_GLITCH_4000 (0x05 << 4)
+#define MS_OCP_GLITCH_8000 (0x06 << 4)
+#define MS_OCP_GLITCH_10000 (0x07 << 4)
+
+#define OCP_TIME_60 0x00
+#define OCP_TIME_100 (0x01 << 3)
+#define OCP_TIME_200 (0x02 << 3)
+#define OCP_TIME_400 (0x03 << 3)
+#define OCP_TIME_600 (0x04 << 3)
+#define OCP_TIME_800 (0x05 << 3)
+#define OCP_TIME_1100 (0x06 << 3)
+#define OCP_TIME_MASK 0x38
+
+#define MS_OCP_TIME_60 0x00
+#define MS_OCP_TIME_100 (0x01 << 4)
+#define MS_OCP_TIME_200 (0x02 << 4)
+#define MS_OCP_TIME_400 (0x03 << 4)
+#define MS_OCP_TIME_600 (0x04 << 4)
+#define MS_OCP_TIME_800 (0x05 << 4)
+#define MS_OCP_TIME_1100 (0x06 << 4)
+#define MS_OCP_TIME_MASK 0x70
+
+#define SD_OCP_TIME_60 0x00
+#define SD_OCP_TIME_100 0x01
+#define SD_OCP_TIME_200 0x02
+#define SD_OCP_TIME_400 0x03
+#define SD_OCP_TIME_600 0x04
+#define SD_OCP_TIME_800 0x05
+#define SD_OCP_TIME_1100 0x06
+#define SD_OCP_TIME_MASK 0x07
+
+#define OCP_THD_315_417 0x00
+#define OCP_THD_283_783 (0x01 << 6)
+#define OCP_THD_244_946 (0x02 << 6)
+#define OCP_THD_191_1080 (0x03 << 6)
+#define OCP_THD_MASK 0xC0
+
+#define MS_OCP_THD_450 0x00
+#define MS_OCP_THD_550 (0x01 << 4)
+#define MS_OCP_THD_650 (0x02 << 4)
+#define MS_OCP_THD_750 (0x03 << 4)
+#define MS_OCP_THD_850 (0x04 << 4)
+#define MS_OCP_THD_950 (0x05 << 4)
+#define MS_OCP_THD_1050 (0x06 << 4)
+#define MS_OCP_THD_1150 (0x07 << 4)
+#define MS_OCP_THD_MASK 0x70
+
+#define SD_OCP_THD_450 0x00
+#define SD_OCP_THD_550 0x01
+#define SD_OCP_THD_650 0x02
+#define SD_OCP_THD_750 0x03
+#define SD_OCP_THD_850 0x04
+#define SD_OCP_THD_950 0x05
+#define SD_OCP_THD_1050 0x06
+#define SD_OCP_THD_1150 0x07
+#define SD_OCP_THD_MASK 0x07
+
+#define FPGA_MS_PULL_CTL_EN 0xEF
+#define FPGA_SD_PULL_CTL_EN 0xF7
+#define FPGA_XD_PULL_CTL_EN1 0xFE
+#define FPGA_XD_PULL_CTL_EN2 0xFD
+#define FPGA_XD_PULL_CTL_EN3 0xFB
+
+#define FPGA_MS_PULL_CTL_BIT 0x10
+#define FPGA_SD_PULL_CTL_BIT 0x08
+
+#define BLINK_EN 0x08
+#define LED_GPIO0 (0 << 4)
+#define LED_GPIO1 (1 << 4)
+#define LED_GPIO2 (2 << 4)
+
+#define SDIO_BUS_CTRL 0x01
+#define SDIO_CD_CTRL 0x02
+
+#define SSC_RSTB 0x80
+#define SSC_8X_EN 0x40
+#define SSC_FIX_FRAC 0x20
+#define SSC_SEL_1M 0x00
+#define SSC_SEL_2M 0x08
+#define SSC_SEL_4M 0x10
+#define SSC_SEL_8M 0x18
+
+#define SSC_DEPTH_MASK 0x07
+#define SSC_DEPTH_DISALBE 0x00
+#define SSC_DEPTH_4M 0x01
+#define SSC_DEPTH_2M 0x02
+#define SSC_DEPTH_1M 0x03
+#define SSC_DEPTH_512K 0x04
+#define SSC_DEPTH_256K 0x05
+#define SSC_DEPTH_128K 0x06
+#define SSC_DEPTH_64K 0x07
+
+#define XD_D3_NP 0x00
+#define XD_D3_PD (0x01 << 6)
+#define XD_D3_PU (0x02 << 6)
+#define XD_D2_NP 0x00
+#define XD_D2_PD (0x01 << 4)
+#define XD_D2_PU (0x02 << 4)
+#define XD_D1_NP 0x00
+#define XD_D1_PD (0x01 << 2)
+#define XD_D1_PU (0x02 << 2)
+#define XD_D0_NP 0x00
+#define XD_D0_PD 0x01
+#define XD_D0_PU 0x02
+
+#define SD_D7_NP 0x00
+#define SD_D7_PD (0x01 << 4)
+#define SD_DAT7_PU (0x02 << 4)
+#define SD_CLK_NP 0x00
+#define SD_CLK_PD (0x01 << 2)
+#define SD_CLK_PU (0x02 << 2)
+#define SD_D5_NP 0x00
+#define SD_D5_PD 0x01
+#define SD_D5_PU 0x02
+
+#define MS_D1_NP 0x00
+#define MS_D1_PD (0x01 << 6)
+#define MS_D1_PU (0x02 << 6)
+#define MS_D2_NP 0x00
+#define MS_D2_PD (0x01 << 4)
+#define MS_D2_PU (0x02 << 4)
+#define MS_CLK_NP 0x00
+#define MS_CLK_PD (0x01 << 2)
+#define MS_CLK_PU (0x02 << 2)
+#define MS_D6_NP 0x00
+#define MS_D6_PD 0x01
+#define MS_D6_PU 0x02
+
+#define XD_D7_NP 0x00
+#define XD_D7_PD (0x01 << 6)
+#define XD_D7_PU (0x02 << 6)
+#define XD_D6_NP 0x00
+#define XD_D6_PD (0x01 << 4)
+#define XD_D6_PU (0x02 << 4)
+#define XD_D5_NP 0x00
+#define XD_D5_PD (0x01 << 2)
+#define XD_D5_PU (0x02 << 2)
+#define XD_D4_NP 0x00
+#define XD_D4_PD 0x01
+#define XD_D4_PU 0x02
+
+#define SD_D6_NP 0x00
+#define SD_D6_PD (0x01 << 6)
+#define SD_D6_PU (0x02 << 6)
+#define SD_D0_NP 0x00
+#define SD_D0_PD (0x01 << 4)
+#define SD_D0_PU (0x02 << 4)
+#define SD_D1_NP 0x00
+#define SD_D1_PD 0x01
+#define SD_D1_PU 0x02
+
+#define MS_D3_NP 0x00
+#define MS_D3_PD (0x01 << 6)
+#define MS_D3_PU (0x02 << 6)
+#define MS_D0_NP 0x00
+#define MS_D0_PD (0x01 << 4)
+#define MS_D0_PU (0x02 << 4)
+#define MS_BS_NP 0x00
+#define MS_BS_PD (0x01 << 2)
+#define MS_BS_PU (0x02 << 2)
+
+#define XD_WP_NP 0x00
+#define XD_WP_PD (0x01 << 6)
+#define XD_WP_PU (0x02 << 6)
+#define XD_CE_NP 0x00
+#define XD_CE_PD (0x01 << 3)
+#define XD_CE_PU (0x02 << 3)
+#define XD_CLE_NP 0x00
+#define XD_CLE_PD (0x01 << 1)
+#define XD_CLE_PU (0x02 << 1)
+#define XD_CD_PD 0x00
+#define XD_CD_PU 0x01
+
+#define SD_D4_NP 0x00
+#define SD_D4_PD (0x01 << 6)
+#define SD_D4_PU (0x02 << 6)
+
+#define MS_D7_NP 0x00
+#define MS_D7_PD (0x01 << 6)
+#define MS_D7_PU (0x02 << 6)
+
+#define XD_RDY_NP 0x00
+#define XD_RDY_PD (0x01 << 6)
+#define XD_RDY_PU (0x02 << 6)
+#define XD_WE_NP 0x00
+#define XD_WE_PD (0x01 << 4)
+#define XD_WE_PU (0x02 << 4)
+#define XD_RE_NP 0x00
+#define XD_RE_PD (0x01 << 2)
+#define XD_RE_PU (0x02 << 2)
+#define XD_ALE_NP 0x00
+#define XD_ALE_PD 0x01
+#define XD_ALE_PU 0x02
+
+#define SD_D3_NP 0x00
+#define SD_D3_PD (0x01 << 4)
+#define SD_D3_PU (0x02 << 4)
+#define SD_D2_NP 0x00
+#define SD_D2_PD (0x01 << 2)
+#define SD_D2_PU (0x02 << 2)
+
+#define MS_INS_PD 0x00
+#define MS_INS_PU (0x01 << 7)
+#define SD_WP_NP 0x00
+#define SD_WP_PD (0x01 << 5)
+#define SD_WP_PU (0x02 << 5)
+#define SD_CD_PD 0x00
+#define SD_CD_PU (0x01 << 4)
+#define SD_CMD_NP 0x00
+#define SD_CMD_PD (0x01 << 2)
+#define SD_CMD_PU (0x02 << 2)
+
+#define MS_D5_NP 0x00
+#define MS_D5_PD (0x01 << 2)
+#define MS_D5_PU (0x02 << 2)
+#define MS_D4_NP 0x00
+#define MS_D4_PD 0x01
+#define MS_D4_PU 0x02
+
+#define FORCE_PM_CLOCK 0x10
+#define EN_CLOCK_PM 0x01
+
+#define HOST_ENTER_S3 0x02
+#define HOST_ENTER_S1 0x01
+
+#define AUX_PWR_DETECTED 0x01
+
+#define PHY_DEBUG_MODE 0x01
+
+#define SPI_COMMAND_BIT_8 0xE0
+#define SPI_ADDRESS_BIT_24 0x17
+#define SPI_ADDRESS_BIT_32 0x1F
+
+#define SPI_TRANSFER0_START 0x80
+#define SPI_TRANSFER0_END 0x40
+#define SPI_C_MODE0 0x00
+#define SPI_CA_MODE0 0x01
+#define SPI_CDO_MODE0 0x02
+#define SPI_CDI_MODE0 0x03
+#define SPI_CADO_MODE0 0x04
+#define SPI_CADI_MODE0 0x05
+#define SPI_POLLING_MODE0 0x06
+
+#define SPI_TRANSFER1_START 0x80
+#define SPI_TRANSFER1_END 0x40
+#define SPI_DO_MODE1 0x00
+#define SPI_DI_MODE1 0x01
+
+#define CS_POLARITY_HIGH 0x40
+#define CS_POLARITY_LOW 0x00
+#define DTO_MSB_FIRST 0x00
+#define DTO_LSB_FIRST 0x20
+#define SPI_MASTER 0x00
+#define SPI_SLAVE 0x10
+#define SPI_MODE0 0x00
+#define SPI_MODE1 0x04
+#define SPI_MODE2 0x08
+#define SPI_MODE3 0x0C
+#define SPI_MANUAL 0x00
+#define SPI_HALF_AUTO 0x01
+#define SPI_AUTO 0x02
+#define SPI_EEPROM_AUTO 0x03
+
+#define EDO_TIMING_MASK 0x03
+#define SAMPLE_RISING 0x00
+#define SAMPLE_DELAY_HALF 0x01
+#define SAMPLE_DELAY_ONE 0x02
+#define SAPMLE_DELAY_ONE_HALF 0x03
+#define TCS_MASK 0x0C
+
+#define NOT_BYPASS_SD 0x02
+#define DISABLE_SDIO_FUNC 0x04
+#define SELECT_1LUN 0x08
+
+#define PWR_GATE_EN 0x01
+#define LDO3318_PWR_MASK 0x06
+#define LDO_ON 0x00
+#define LDO_SUSPEND 0x04
+#define LDO_OFF 0x06
+
+#define SD_CFG1 0xFDA0
+#define SD_CFG2 0xFDA1
+#define SD_CFG3 0xFDA2
+#define SD_STAT1 0xFDA3
+#define SD_STAT2 0xFDA4
+#define SD_BUS_STAT 0xFDA5
+#define SD_PAD_CTL 0xFDA6
+#define SD_SAMPLE_POINT_CTL 0xFDA7
+#define SD_PUSH_POINT_CTL 0xFDA8
+#define SD_CMD0 0xFDA9
+#define SD_CMD1 0xFDAA
+#define SD_CMD2 0xFDAB
+#define SD_CMD3 0xFDAC
+#define SD_CMD4 0xFDAD
+#define SD_CMD5 0xFDAE
+#define SD_BYTE_CNT_L 0xFDAF
+#define SD_BYTE_CNT_H 0xFDB0
+#define SD_BLOCK_CNT_L 0xFDB1
+#define SD_BLOCK_CNT_H 0xFDB2
+#define SD_TRANSFER 0xFDB3
+#define SD_CMD_STATE 0xFDB5
+#define SD_DATA_STATE 0xFDB6
+
+#define DCM_DRP_CTL 0xFC23
+#define DCM_DRP_TRIG 0xFC24
+#define DCM_DRP_CFG 0xFC25
+#define DCM_DRP_WR_DATA_L 0xFC26
+#define DCM_DRP_WR_DATA_H 0xFC27
+#define DCM_DRP_RD_DATA_L 0xFC28
+#define DCM_DRP_RD_DATA_H 0xFC29
+#define SD_VPCLK0_CTL 0xFC2A
+#define SD_VPCLK1_CTL 0xFC2B
+#define SD_DCMPS0_CTL 0xFC2C
+#define SD_DCMPS1_CTL 0xFC2D
+#define SD_VPTX_CTL SD_VPCLK0_CTL
+#define SD_VPRX_CTL SD_VPCLK1_CTL
+#define SD_DCMPS_TX_CTL SD_DCMPS0_CTL
+#define SD_DCMPS_RX_CTL SD_DCMPS1_CTL
+
+#define CARD_CLK_SOURCE 0xFC2E
+
+#define CARD_PWR_CTL 0xFD50
+#define CARD_CLK_SWITCH 0xFD51
+#define CARD_SHARE_MODE 0xFD52
+#define CARD_DRIVE_SEL 0xFD53
+#define CARD_STOP 0xFD54
+#define CARD_OE 0xFD55
+#define CARD_AUTO_BLINK 0xFD56
+#define CARD_GPIO_DIR 0xFD57
+#define CARD_GPIO 0xFD58
+
+#define CARD_DATA_SOURCE 0xFD5B
+#define CARD_SELECT 0xFD5C
+#define SD30_DRIVE_SEL 0xFD5E
+
+#define CARD_CLK_EN 0xFD69
+
+#define SDIO_CTRL 0xFD6B
+
+#define FPDCTL 0xFC00
+#define PDINFO 0xFC01
+
+#define CLK_CTL 0xFC02
+#define CLK_DIV 0xFC03
+#define CLK_SEL 0xFC04
+
+#define SSC_DIV_N_0 0xFC0F
+#define SSC_DIV_N_1 0xFC10
+
+#define RCCTL 0xFC14
+
+#define FPGA_PULL_CTL 0xFC1D
+
+#define CARD_PULL_CTL1 0xFD60
+#define CARD_PULL_CTL2 0xFD61
+#define CARD_PULL_CTL3 0xFD62
+#define CARD_PULL_CTL4 0xFD63
+#define CARD_PULL_CTL5 0xFD64
+#define CARD_PULL_CTL6 0xFD65
+
+#define IRQEN0 0xFE20
+#define IRQSTAT0 0xFE21
+#define IRQEN1 0xFE22
+#define IRQSTAT1 0xFE23
+#define TLPRIEN 0xFE24
+#define TLPRISTAT 0xFE25
+#define TLPTIEN 0xFE26
+#define TLPTISTAT 0xFE27
+#define DMATC0 0xFE28
+#define DMATC1 0xFE29
+#define DMATC2 0xFE2A
+#define DMATC3 0xFE2B
+#define DMACTL 0xFE2C
+#define BCTL 0xFE2D
+#define RBBC0 0xFE2E
+#define RBBC1 0xFE2F
+#define RBDAT 0xFE30
+#define RBCTL 0xFE34
+#define CFGADDR0 0xFE35
+#define CFGADDR1 0xFE36
+#define CFGDATA0 0xFE37
+#define CFGDATA1 0xFE38
+#define CFGDATA2 0xFE39
+#define CFGDATA3 0xFE3A
+#define CFGRWCTL 0xFE3B
+#define PHYRWCTL 0xFE3C
+#define PHYDATA0 0xFE3D
+#define PHYDATA1 0xFE3E
+#define PHYADDR 0xFE3F
+#define MSGRXDATA0 0xFE40
+#define MSGRXDATA1 0xFE41
+#define MSGRXDATA2 0xFE42
+#define MSGRXDATA3 0xFE43
+#define MSGTXDATA0 0xFE44
+#define MSGTXDATA1 0xFE45
+#define MSGTXDATA2 0xFE46
+#define MSGTXDATA3 0xFE47
+#define MSGTXCTL 0xFE48
+#define PETXCFG 0xFE49
+
+#define CDRESUMECTL 0xFE52
+#define WAKE_SEL_CTL 0xFE54
+#define PME_FORCE_CTL 0xFE56
+#define ASPM_FORCE_CTL 0xFE57
+#define PM_CLK_FORCE_CTL 0xFE58
+#define PERST_GLITCH_WIDTH 0xFE5C
+#define CHANGE_LINK_STATE 0xFE5B
+#define RESET_LOAD_REG 0xFE5E
+#define HOST_SLEEP_STATE 0xFE60
+#define MAIN_PWR_OFF_CTL 0xFE70 /* RTS5208 */
+
+#define NFTS_TX_CTRL 0xFE72
+
+#define PWR_GATE_CTRL 0xFE75
+#define PWD_SUSPEND_EN 0xFE76
+
+#define EFUSE_CONTENT 0xFE5F
+
+#define XD_INIT 0xFD10
+#define XD_DTCTL 0xFD11
+#define XD_CTL 0xFD12
+#define XD_TRANSFER 0xFD13
+#define XD_CFG 0xFD14
+#define XD_ADDRESS0 0xFD15
+#define XD_ADDRESS1 0xFD16
+#define XD_ADDRESS2 0xFD17
+#define XD_ADDRESS3 0xFD18
+#define XD_ADDRESS4 0xFD19
+#define XD_DAT 0xFD1A
+#define XD_PAGE_CNT 0xFD1B
+#define XD_PAGE_STATUS 0xFD1C
+#define XD_BLOCK_STATUS 0xFD1D
+#define XD_BLOCK_ADDR1_L 0xFD1E
+#define XD_BLOCK_ADDR1_H 0xFD1F
+#define XD_BLOCK_ADDR2_L 0xFD20
+#define XD_BLOCK_ADDR2_H 0xFD21
+#define XD_BYTE_CNT_L 0xFD22
+#define XD_BYTE_CNT_H 0xFD23
+#define XD_PARITY 0xFD24
+#define XD_ECC_BIT1 0xFD25
+#define XD_ECC_BYTE1 0xFD26
+#define XD_ECC_BIT2 0xFD27
+#define XD_ECC_BYTE2 0xFD28
+#define XD_RESERVED0 0xFD29
+#define XD_RESERVED1 0xFD2A
+#define XD_RESERVED2 0xFD2B
+#define XD_RESERVED3 0xFD2C
+#define XD_CHK_DATA_STATUS 0xFD2D
+#define XD_CATCTL 0xFD2E
+
+#define MS_CFG 0xFD40
+#define MS_TPC 0xFD41
+#define MS_TRANS_CFG 0xFD42
+#define MS_TRANSFER 0xFD43
+#define MS_INT_REG 0xFD44
+#define MS_BYTE_CNT 0xFD45
+#define MS_SECTOR_CNT_L 0xFD46
+#define MS_SECTOR_CNT_H 0xFD47
+#define MS_DBUS_H 0xFD48
+
+#define SSC_CTL1 0xFC11
+#define SSC_CTL2 0xFC12
+
+#define OCPCTL 0xFC15
+#define OCPSTAT 0xFC16
+#define OCPCLR 0xFC17 /* 5208 */
+#define OCPPARA1 0xFC18
+#define OCPPARA2 0xFC19
+
+#define EFUSE_OP 0xFC20
+#define EFUSE_CTRL 0xFC21
+#define EFUSE_DATA 0xFC22
+
+#define SPI_COMMAND 0xFD80
+#define SPI_ADDR0 0xFD81
+#define SPI_ADDR1 0xFD82
+#define SPI_ADDR2 0xFD83
+#define SPI_ADDR3 0xFD84
+#define SPI_CA_NUMBER 0xFD85
+#define SPI_LENGTH0 0xFD86
+#define SPI_LENGTH1 0xFD87
+#define SPI_DATA 0xFD88
+#define SPI_DATA_NUMBER 0xFD89
+#define SPI_TRANSFER0 0xFD90
+#define SPI_TRANSFER1 0xFD91
+#define SPI_CONTROL 0xFD92
+#define SPI_SIG 0xFD93
+#define SPI_TCTL 0xFD94
+#define SPI_SLAVE_NUM 0xFD95
+#define SPI_CLK_DIVIDER0 0xFD96
+#define SPI_CLK_DIVIDER1 0xFD97
+
+#define SRAM_BASE 0xE600
+#define RBUF_BASE 0xF400
+#define PPBUF_BASE1 0xF800
+#define PPBUF_BASE2 0xFA00
+#define IMAGE_FLAG_ADDR0 0xCE80
+#define IMAGE_FLAG_ADDR1 0xCE81
+
+#define READ_OP 1
+#define WRITE_OP 2
+
+#define LCTLR 0x80
+
+#define POLLING_WAIT_CNT 1
+#define IDLE_MAX_COUNT 10
+#define SDIO_IDLE_COUNT 10
+
+#define DEBOUNCE_CNT 5
+
+void do_remaining_work(struct rtsx_chip *chip);
+void try_to_switch_sdio_ctrl(struct rtsx_chip *chip);
+void do_reset_sd_card(struct rtsx_chip *chip);
+void do_reset_xd_card(struct rtsx_chip *chip);
+void do_reset_ms_card(struct rtsx_chip *chip);
+void rtsx_power_off_card(struct rtsx_chip *chip);
+void rtsx_release_cards(struct rtsx_chip *chip);
+void rtsx_reset_cards(struct rtsx_chip *chip);
+void rtsx_reinit_cards(struct rtsx_chip *chip, int reset_chip);
+void rtsx_init_cards(struct rtsx_chip *chip);
+int switch_ssc_clock(struct rtsx_chip *chip, int clk);
+int switch_normal_clock(struct rtsx_chip *chip, int clk);
+int enable_card_clock(struct rtsx_chip *chip, u8 card);
+int disable_card_clock(struct rtsx_chip *chip, u8 card);
+int card_rw(struct scsi_cmnd *srb, struct rtsx_chip *chip,
+ u32 sec_addr, u16 sec_cnt);
+void trans_dma_enable(enum dma_data_direction dir,
+ struct rtsx_chip *chip, u32 byte_cnt, u8 pack_size);
+void toggle_gpio(struct rtsx_chip *chip, u8 gpio);
+void turn_on_led(struct rtsx_chip *chip, u8 gpio);
+void turn_off_led(struct rtsx_chip *chip, u8 gpio);
+
+int card_share_mode(struct rtsx_chip *chip, int card);
+int select_card(struct rtsx_chip *chip, int card);
+int detect_card_cd(struct rtsx_chip *chip, int card);
+int check_card_exist(struct rtsx_chip *chip, unsigned int lun);
+int check_card_ready(struct rtsx_chip *chip, unsigned int lun);
+int check_card_wp(struct rtsx_chip *chip, unsigned int lun);
+int check_card_fail(struct rtsx_chip *chip, unsigned int lun);
+int check_card_ejected(struct rtsx_chip *chip, unsigned int lun);
+void eject_card(struct rtsx_chip *chip, unsigned int lun);
+u8 get_lun_card(struct rtsx_chip *chip, unsigned int lun);
+
+static inline u32 get_card_size(struct rtsx_chip *chip, unsigned int lun)
+{
+#ifdef SUPPORT_SD_LOCK
+ struct sd_info *sd_card = &(chip->sd_card);
+
+ if ((get_lun_card(chip, lun) == SD_CARD) &&
+ (sd_card->sd_lock_status & SD_LOCKED))
+ return 0;
+
+ return chip->capacity[lun];
+#else
+ return chip->capacity[lun];
+#endif
+}
+
+static inline int switch_clock(struct rtsx_chip *chip, int clk)
+{
+ int retval = 0;
+
+ if (chip->asic_code)
+ retval = switch_ssc_clock(chip, clk);
+ else
+ retval = switch_normal_clock(chip, clk);
+
+ return retval;
+}
+
+int card_power_on(struct rtsx_chip *chip, u8 card);
+int card_power_off(struct rtsx_chip *chip, u8 card);
+
+static inline int card_power_off_all(struct rtsx_chip *chip)
+{
+ int retval;
+
+ retval = rtsx_write_register(chip, CARD_PWR_CTL, 0x0F, 0x0F);
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+
+ return STATUS_SUCCESS;
+}
+
+static inline void rtsx_clear_xd_error(struct rtsx_chip *chip)
+{
+ rtsx_write_register(chip, CARD_STOP, XD_STOP | XD_CLR_ERR,
+ XD_STOP | XD_CLR_ERR);
+}
+
+static inline void rtsx_clear_sd_error(struct rtsx_chip *chip)
+{
+ rtsx_write_register(chip, CARD_STOP, SD_STOP | SD_CLR_ERR,
+ SD_STOP | SD_CLR_ERR);
+}
+
+static inline void rtsx_clear_ms_error(struct rtsx_chip *chip)
+{
+ rtsx_write_register(chip, CARD_STOP, MS_STOP | MS_CLR_ERR,
+ MS_STOP | MS_CLR_ERR);
+}
+
+static inline void rtsx_clear_spi_error(struct rtsx_chip *chip)
+{
+ rtsx_write_register(chip, CARD_STOP, SPI_STOP | SPI_CLR_ERR,
+ SPI_STOP | SPI_CLR_ERR);
+}
+
+#ifdef SUPPORT_SDIO_ASPM
+void dynamic_configure_sdio_aspm(struct rtsx_chip *chip);
+#endif
+
+#endif /* __REALTEK_RTSX_CARD_H */
diff --git a/drivers/staging/rts5208/rtsx_chip.c b/drivers/staging/rts5208/rtsx_chip.c
new file mode 100644
index 000000000..0c1716ebc
--- /dev/null
+++ b/drivers/staging/rts5208/rtsx_chip.c
@@ -0,0 +1,2437 @@
+/* Driver for Realtek PCI-Express card reader
+ *
+ * Copyright(c) 2009-2013 Realtek Semiconductor Corp. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, 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, see <http://www.gnu.org/licenses/>.
+ *
+ * Author:
+ * Wei WANG (wei_wang@realsil.com.cn)
+ * Micky Ching (micky_ching@realsil.com.cn)
+ */
+
+#include <linux/blkdev.h>
+#include <linux/kthread.h>
+#include <linux/sched.h>
+#include <linux/workqueue.h>
+#include <linux/vmalloc.h>
+
+#include "rtsx.h"
+#include "sd.h"
+#include "xd.h"
+#include "ms.h"
+
+static void rtsx_calibration(struct rtsx_chip *chip)
+{
+ rtsx_write_phy_register(chip, 0x1B, 0x135E);
+ wait_timeout(10);
+ rtsx_write_phy_register(chip, 0x00, 0x0280);
+ rtsx_write_phy_register(chip, 0x01, 0x7112);
+ rtsx_write_phy_register(chip, 0x01, 0x7110);
+ rtsx_write_phy_register(chip, 0x01, 0x7112);
+ rtsx_write_phy_register(chip, 0x01, 0x7113);
+ rtsx_write_phy_register(chip, 0x00, 0x0288);
+}
+
+void rtsx_disable_card_int(struct rtsx_chip *chip)
+{
+ u32 reg = rtsx_readl(chip, RTSX_BIER);
+
+ reg &= ~(XD_INT_EN | SD_INT_EN | MS_INT_EN);
+ rtsx_writel(chip, RTSX_BIER, reg);
+}
+
+void rtsx_enable_card_int(struct rtsx_chip *chip)
+{
+ u32 reg = rtsx_readl(chip, RTSX_BIER);
+ int i;
+
+ for (i = 0; i <= chip->max_lun; i++) {
+ if (chip->lun2card[i] & XD_CARD)
+ reg |= XD_INT_EN;
+ if (chip->lun2card[i] & SD_CARD)
+ reg |= SD_INT_EN;
+ if (chip->lun2card[i] & MS_CARD)
+ reg |= MS_INT_EN;
+ }
+ if (chip->hw_bypass_sd)
+ reg &= ~((u32)SD_INT_EN);
+
+ rtsx_writel(chip, RTSX_BIER, reg);
+}
+
+void rtsx_enable_bus_int(struct rtsx_chip *chip)
+{
+ u32 reg = 0;
+#ifndef DISABLE_CARD_INT
+ int i;
+#endif
+
+ reg = TRANS_OK_INT_EN | TRANS_FAIL_INT_EN;
+
+#ifndef DISABLE_CARD_INT
+ for (i = 0; i <= chip->max_lun; i++) {
+ dev_dbg(rtsx_dev(chip), "lun2card[%d] = 0x%02x\n",
+ i, chip->lun2card[i]);
+
+ if (chip->lun2card[i] & XD_CARD)
+ reg |= XD_INT_EN;
+ if (chip->lun2card[i] & SD_CARD)
+ reg |= SD_INT_EN;
+ if (chip->lun2card[i] & MS_CARD)
+ reg |= MS_INT_EN;
+ }
+ if (chip->hw_bypass_sd)
+ reg &= ~((u32)SD_INT_EN);
+#endif
+
+ if (chip->ic_version >= IC_VER_C)
+ reg |= DELINK_INT_EN;
+#ifdef SUPPORT_OCP
+ reg |= OC_INT_EN;
+#endif
+ if (!chip->adma_mode)
+ reg |= DATA_DONE_INT_EN;
+
+ /* Enable Bus Interrupt */
+ rtsx_writel(chip, RTSX_BIER, reg);
+
+ dev_dbg(rtsx_dev(chip), "RTSX_BIER: 0x%08x\n", reg);
+}
+
+void rtsx_disable_bus_int(struct rtsx_chip *chip)
+{
+ rtsx_writel(chip, RTSX_BIER, 0);
+}
+
+static int rtsx_pre_handle_sdio_old(struct rtsx_chip *chip)
+{
+ int retval;
+
+ if (chip->ignore_sd && CHK_SDIO_EXIST(chip)) {
+ if (chip->asic_code) {
+ retval = rtsx_write_register(chip, CARD_PULL_CTL5,
+ 0xFF,
+ MS_INS_PU | SD_WP_PU | SD_CD_PU | SD_CMD_PU);
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+ } else {
+ retval = rtsx_write_register(chip, FPGA_PULL_CTL,
+ 0xFF,
+ FPGA_SD_PULL_CTL_EN);
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+ }
+ retval = rtsx_write_register(chip, CARD_SHARE_MODE, 0xFF,
+ CARD_SHARE_48_SD);
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+
+ /* Enable SDIO internal clock */
+ retval = rtsx_write_register(chip, 0xFF2C, 0x01, 0x01);
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+
+ retval = rtsx_write_register(chip, SDIO_CTRL, 0xFF,
+ SDIO_BUS_CTRL | SDIO_CD_CTRL);
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+
+ chip->sd_int = 1;
+ chip->sd_io = 1;
+ } else {
+ chip->need_reset |= SD_CARD;
+ }
+
+ return STATUS_SUCCESS;
+}
+
+#ifdef HW_AUTO_SWITCH_SD_BUS
+static int rtsx_pre_handle_sdio_new(struct rtsx_chip *chip)
+{
+ u8 tmp;
+ bool sw_bypass_sd = false;
+ int retval;
+
+ if (chip->driver_first_load) {
+ if (CHECK_PID(chip, 0x5288)) {
+ retval = rtsx_read_register(chip, 0xFE5A, &tmp);
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+ if (tmp & 0x08)
+ sw_bypass_sd = true;
+ } else if (CHECK_PID(chip, 0x5208)) {
+ retval = rtsx_read_register(chip, 0xFE70, &tmp);
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+ if (tmp & 0x80)
+ sw_bypass_sd = true;
+ }
+ } else {
+ if (chip->sdio_in_charge)
+ sw_bypass_sd = true;
+ }
+ dev_dbg(rtsx_dev(chip), "chip->sdio_in_charge = %d\n",
+ chip->sdio_in_charge);
+ dev_dbg(rtsx_dev(chip), "chip->driver_first_load = %d\n",
+ chip->driver_first_load);
+ dev_dbg(rtsx_dev(chip), "sw_bypass_sd = %d\n",
+ sw_bypass_sd);
+
+ if (sw_bypass_sd) {
+ u8 cd_toggle_mask = 0;
+
+ retval = rtsx_read_register(chip, TLPTISTAT, &tmp);
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+ cd_toggle_mask = 0x08;
+
+ if (tmp & cd_toggle_mask) {
+ /* Disable sdio_bus_auto_switch */
+ if (CHECK_PID(chip, 0x5288)) {
+ retval = rtsx_write_register(chip, 0xFE5A,
+ 0x08, 0x00);
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+ } else if (CHECK_PID(chip, 0x5208)) {
+ retval = rtsx_write_register(chip, 0xFE70,
+ 0x80, 0x00);
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+ }
+
+ retval = rtsx_write_register(chip, TLPTISTAT, 0xFF,
+ tmp);
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+
+ chip->need_reset |= SD_CARD;
+ } else {
+ dev_dbg(rtsx_dev(chip), "Chip inserted with SDIO!\n");
+
+ if (chip->asic_code) {
+ retval = sd_pull_ctl_enable(chip);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+ } else {
+ retval = rtsx_write_register(chip,
+ FPGA_PULL_CTL,
+ FPGA_SD_PULL_CTL_BIT | 0x20,
+ 0);
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+ }
+ retval = card_share_mode(chip, SD_CARD);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ /* Enable sdio_bus_auto_switch */
+ if (CHECK_PID(chip, 0x5288)) {
+ retval = rtsx_write_register(chip, 0xFE5A,
+ 0x08, 0x08);
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+ } else if (CHECK_PID(chip, 0x5208)) {
+ retval = rtsx_write_register(chip, 0xFE70,
+ 0x80, 0x80);
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+ }
+
+ chip->chip_insert_with_sdio = 1;
+ chip->sd_io = 1;
+ }
+ } else {
+ retval = rtsx_write_register(chip, TLPTISTAT, 0x08, 0x08);
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+
+ chip->need_reset |= SD_CARD;
+ }
+
+ return STATUS_SUCCESS;
+}
+#endif
+
+static int rtsx_reset_aspm(struct rtsx_chip *chip)
+{
+ int ret;
+
+ if (chip->dynamic_aspm) {
+ if (!CHK_SDIO_EXIST(chip) || !CHECK_PID(chip, 0x5288))
+ return STATUS_SUCCESS;
+
+ ret = rtsx_write_cfg_dw(chip, 2, 0xC0, 0xFF,
+ chip->aspm_l0s_l1_en);
+ if (ret != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ return STATUS_SUCCESS;
+ }
+
+ if (CHECK_PID(chip, 0x5208)) {
+ ret = rtsx_write_register(chip, ASPM_FORCE_CTL, 0xFF, 0x3F);
+ if (ret) {
+ rtsx_trace(chip);
+ return ret;
+ }
+ }
+ ret = rtsx_write_config_byte(chip, LCTLR, chip->aspm_l0s_l1_en);
+ if (ret != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ chip->aspm_level[0] = chip->aspm_l0s_l1_en;
+ if (CHK_SDIO_EXIST(chip)) {
+ chip->aspm_level[1] = chip->aspm_l0s_l1_en;
+ ret = rtsx_write_cfg_dw(chip, CHECK_PID(chip, 0x5288) ? 2 : 1,
+ 0xC0, 0xFF, chip->aspm_l0s_l1_en);
+ if (ret != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+ }
+
+ chip->aspm_enabled = 1;
+
+ return STATUS_SUCCESS;
+}
+
+static int rtsx_enable_pcie_intr(struct rtsx_chip *chip)
+{
+ int ret;
+
+ if (!chip->asic_code || !CHECK_PID(chip, 0x5208)) {
+ rtsx_enable_bus_int(chip);
+ return STATUS_SUCCESS;
+ }
+
+ if (chip->phy_debug_mode) {
+ ret = rtsx_write_register(chip, CDRESUMECTL, 0x77, 0);
+ if (ret) {
+ rtsx_trace(chip);
+ return ret;
+ }
+ rtsx_disable_bus_int(chip);
+ } else {
+ rtsx_enable_bus_int(chip);
+ }
+
+ if (chip->ic_version >= IC_VER_D) {
+ u16 reg;
+
+ ret = rtsx_read_phy_register(chip, 0x00, &reg);
+ if (ret != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ reg &= 0xFE7F;
+ reg |= 0x80;
+ ret = rtsx_write_phy_register(chip, 0x00, reg);
+ if (ret != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ ret = rtsx_read_phy_register(chip, 0x1C, &reg);
+ if (ret != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ reg &= 0xFFF7;
+ ret = rtsx_write_phy_register(chip, 0x1C, reg);
+ if (ret != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+ }
+
+ if (chip->driver_first_load && (chip->ic_version < IC_VER_C))
+ rtsx_calibration(chip);
+
+ return STATUS_SUCCESS;
+}
+
+int rtsx_reset_chip(struct rtsx_chip *chip)
+{
+ int retval;
+
+ rtsx_writel(chip, RTSX_HCBAR, chip->host_cmds_addr);
+
+ rtsx_disable_aspm(chip);
+
+ retval = rtsx_write_register(chip, HOST_SLEEP_STATE, 0x03, 0x00);
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+
+ /* Disable card clock */
+ retval = rtsx_write_register(chip, CARD_CLK_EN, 0x1E, 0);
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+
+#ifdef SUPPORT_OCP
+ /* SSC power on, OCD power on */
+ if (CHECK_LUN_MODE(chip, SD_MS_2LUN)) {
+ retval = rtsx_write_register(chip, FPDCTL, OC_POWER_DOWN, 0);
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+ } else {
+ retval = rtsx_write_register(chip, FPDCTL, OC_POWER_DOWN,
+ MS_OC_POWER_DOWN);
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+ }
+
+ retval = rtsx_write_register(chip, OCPPARA1, OCP_TIME_MASK,
+ OCP_TIME_800);
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+ retval = rtsx_write_register(chip, OCPPARA2, OCP_THD_MASK,
+ OCP_THD_244_946);
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+ retval = rtsx_write_register(chip, OCPCTL, 0xFF,
+ CARD_OC_INT_EN | CARD_DETECT_EN);
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+#else
+ /* OC power down */
+ retval = rtsx_write_register(chip, FPDCTL, OC_POWER_DOWN,
+ OC_POWER_DOWN);
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+#endif
+
+ if (!CHECK_PID(chip, 0x5288)) {
+ retval = rtsx_write_register(chip, CARD_GPIO_DIR, 0xFF, 0x03);
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+ }
+
+ /* Turn off LED */
+ retval = rtsx_write_register(chip, CARD_GPIO, 0xFF, 0x03);
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+
+ /* Reset delink mode */
+ retval = rtsx_write_register(chip, CHANGE_LINK_STATE, 0x0A, 0);
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+
+ /* Card driving select */
+ retval = rtsx_write_register(chip, CARD_DRIVE_SEL, 0xFF,
+ chip->card_drive_sel);
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+
+#ifdef LED_AUTO_BLINK
+ retval = rtsx_write_register(chip, CARD_AUTO_BLINK, 0xFF,
+ LED_BLINK_SPEED | BLINK_EN | LED_GPIO0);
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+#endif
+
+ if (chip->asic_code) {
+ /* Enable SSC Clock */
+ retval = rtsx_write_register(chip, SSC_CTL1, 0xFF,
+ SSC_8X_EN | SSC_SEL_4M);
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+ retval = rtsx_write_register(chip, SSC_CTL2, 0xFF, 0x12);
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+ }
+
+ /* Disable cd_pwr_save (u_force_rst_core_en=0, u_cd_rst_core_en=0)
+ 0xFE5B
+ bit[1] u_cd_rst_core_en rst_value = 0
+ bit[2] u_force_rst_core_en rst_value = 0
+ bit[5] u_mac_phy_rst_n_dbg rst_value = 1
+ bit[4] u_non_sticky_rst_n_dbg rst_value = 0
+ */
+ retval = rtsx_write_register(chip, CHANGE_LINK_STATE, 0x16, 0x10);
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+
+ /* Enable ASPM */
+ if (chip->aspm_l0s_l1_en) {
+ retval = rtsx_reset_aspm(chip);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+ } else {
+ if (chip->asic_code && CHECK_PID(chip, 0x5208)) {
+ retval = rtsx_write_phy_register(chip, 0x07, 0x0129);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+ }
+ retval = rtsx_write_config_byte(chip, LCTLR,
+ chip->aspm_l0s_l1_en);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+ }
+
+ retval = rtsx_write_config_byte(chip, 0x81, 1);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ if (CHK_SDIO_EXIST(chip)) {
+ retval = rtsx_write_cfg_dw(chip,
+ CHECK_PID(chip, 0x5288) ? 2 : 1,
+ 0xC0, 0xFF00, 0x0100);
+
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+ }
+
+ if (CHECK_PID(chip, 0x5288) && !CHK_SDIO_EXIST(chip)) {
+ retval = rtsx_write_cfg_dw(chip, 2, 0xC0, 0xFFFF, 0x0103);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ retval = rtsx_write_cfg_dw(chip, 2, 0x84, 0xFF, 0x03);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+ }
+
+ retval = rtsx_write_register(chip, IRQSTAT0, LINK_RDY_INT,
+ LINK_RDY_INT);
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+
+ retval = rtsx_write_register(chip, PERST_GLITCH_WIDTH, 0xFF, 0x80);
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+
+ retval = rtsx_enable_pcie_intr(chip);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ chip->need_reset = 0;
+
+ chip->int_reg = rtsx_readl(chip, RTSX_BIPR);
+
+ if (chip->hw_bypass_sd)
+ goto nextcard;
+ dev_dbg(rtsx_dev(chip), "In %s, chip->int_reg = 0x%x\n", __func__,
+ chip->int_reg);
+ if (chip->int_reg & SD_EXIST) {
+#ifdef HW_AUTO_SWITCH_SD_BUS
+ if (CHECK_PID(chip, 0x5208) && (chip->ic_version < IC_VER_C))
+ retval = rtsx_pre_handle_sdio_old(chip);
+ else
+ retval = rtsx_pre_handle_sdio_new(chip);
+
+ dev_dbg(rtsx_dev(chip), "chip->need_reset = 0x%x (rtsx_reset_chip)\n",
+ (unsigned int)(chip->need_reset));
+#else /* HW_AUTO_SWITCH_SD_BUS */
+ retval = rtsx_pre_handle_sdio_old(chip);
+#endif /* HW_AUTO_SWITCH_SD_BUS */
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ } else {
+ chip->sd_io = 0;
+ retval = rtsx_write_register(chip, SDIO_CTRL,
+ SDIO_BUS_CTRL | SDIO_CD_CTRL, 0);
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+ }
+
+nextcard:
+ if (chip->int_reg & XD_EXIST)
+ chip->need_reset |= XD_CARD;
+ if (chip->int_reg & MS_EXIST)
+ chip->need_reset |= MS_CARD;
+ if (chip->int_reg & CARD_EXIST) {
+ retval = rtsx_write_register(chip, SSC_CTL1, SSC_RSTB,
+ SSC_RSTB);
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+ }
+
+ dev_dbg(rtsx_dev(chip), "In %s, chip->need_reset = 0x%x\n", __func__,
+ (unsigned int)(chip->need_reset));
+
+ retval = rtsx_write_register(chip, RCCTL, 0x01, 0x00);
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+
+ if (CHECK_PID(chip, 0x5208) || CHECK_PID(chip, 0x5288)) {
+ /* Turn off main power when entering S3/S4 state */
+ retval = rtsx_write_register(chip, MAIN_PWR_OFF_CTL, 0x03,
+ 0x03);
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+ }
+
+ if (chip->remote_wakeup_en && !chip->auto_delink_en) {
+ retval = rtsx_write_register(chip, WAKE_SEL_CTL, 0x07, 0x07);
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+ if (chip->aux_pwr_exist) {
+ retval = rtsx_write_register(chip, PME_FORCE_CTL,
+ 0xFF, 0x33);
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+ }
+ } else {
+ retval = rtsx_write_register(chip, WAKE_SEL_CTL, 0x07, 0x04);
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+ retval = rtsx_write_register(chip, PME_FORCE_CTL, 0xFF, 0x30);
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+ }
+
+ if (CHECK_PID(chip, 0x5208) && (chip->ic_version >= IC_VER_D)) {
+ retval = rtsx_write_register(chip, PETXCFG, 0x1C, 0x14);
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+ }
+
+ if (chip->asic_code && CHECK_PID(chip, 0x5208)) {
+ retval = rtsx_clr_phy_reg_bit(chip, 0x1C, 2);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+ }
+
+ if (chip->ft2_fast_mode) {
+ retval = rtsx_write_register(chip, CARD_PWR_CTL, 0xFF,
+ MS_PARTIAL_POWER_ON | SD_PARTIAL_POWER_ON);
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+ udelay(chip->pmos_pwr_on_interval);
+ retval = rtsx_write_register(chip, CARD_PWR_CTL, 0xFF,
+ MS_POWER_ON | SD_POWER_ON);
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+
+ wait_timeout(200);
+ }
+
+ /* Reset card */
+ rtsx_reset_detected_cards(chip, 0);
+
+ chip->driver_first_load = 0;
+
+ return STATUS_SUCCESS;
+}
+
+static inline int check_sd_speed_prior(u32 sd_speed_prior)
+{
+ bool fake_para = false;
+ int i;
+
+ for (i = 0; i < 4; i++) {
+ u8 tmp = (u8)(sd_speed_prior >> (i*8));
+
+ if ((tmp < 0x01) || (tmp > 0x04)) {
+ fake_para = true;
+ break;
+ }
+ }
+
+ return !fake_para;
+}
+
+static inline int check_sd_current_prior(u32 sd_current_prior)
+{
+ bool fake_para = false;
+ int i;
+
+ for (i = 0; i < 4; i++) {
+ u8 tmp = (u8)(sd_current_prior >> (i*8));
+
+ if (tmp > 0x03) {
+ fake_para = true;
+ break;
+ }
+ }
+
+ return !fake_para;
+}
+
+static int rts5208_init(struct rtsx_chip *chip)
+{
+ int retval;
+ u16 reg = 0;
+ u8 val = 0;
+
+ retval = rtsx_write_register(chip, CLK_SEL, 0x03, 0x03);
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+ retval = rtsx_read_register(chip, CLK_SEL, &val);
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+ chip->asic_code = val == 0 ? 1 : 0;
+
+ if (chip->asic_code) {
+ retval = rtsx_read_phy_register(chip, 0x1C, &reg);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ dev_dbg(rtsx_dev(chip), "Value of phy register 0x1C is 0x%x\n",
+ reg);
+ chip->ic_version = (reg >> 4) & 0x07;
+ chip->phy_debug_mode = reg & PHY_DEBUG_MODE ? 1 : 0;
+
+ } else {
+ retval = rtsx_read_register(chip, 0xFE80, &val);
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+ chip->ic_version = val;
+ chip->phy_debug_mode = 0;
+ }
+
+ retval = rtsx_read_register(chip, PDINFO, &val);
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+ dev_dbg(rtsx_dev(chip), "PDINFO: 0x%x\n", val);
+ chip->aux_pwr_exist = val & AUX_PWR_DETECTED ? 1 : 0;
+
+ retval = rtsx_read_register(chip, 0xFE50, &val);
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+ chip->hw_bypass_sd = val & 0x01 ? 1 : 0;
+
+ rtsx_read_config_byte(chip, 0x0E, &val);
+ if (val & 0x80)
+ SET_SDIO_EXIST(chip);
+ else
+ CLR_SDIO_EXIST(chip);
+
+ if (chip->use_hw_setting) {
+ retval = rtsx_read_register(chip, CHANGE_LINK_STATE, &val);
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+ chip->auto_delink_en = val & 0x80 ? 1 : 0;
+ }
+
+ return STATUS_SUCCESS;
+}
+
+static int rts5288_init(struct rtsx_chip *chip)
+{
+ int retval;
+ u8 val = 0, max_func;
+ u32 lval = 0;
+
+ retval = rtsx_write_register(chip, CLK_SEL, 0x03, 0x03);
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+ retval = rtsx_read_register(chip, CLK_SEL, &val);
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+ chip->asic_code = val == 0 ? 1 : 0;
+
+ chip->ic_version = 0;
+ chip->phy_debug_mode = 0;
+
+ retval = rtsx_read_register(chip, PDINFO, &val);
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+ dev_dbg(rtsx_dev(chip), "PDINFO: 0x%x\n", val);
+ chip->aux_pwr_exist = val & AUX_PWR_DETECTED ? 1 : 0;
+
+ retval = rtsx_read_register(chip, CARD_SHARE_MODE, &val);
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+ dev_dbg(rtsx_dev(chip), "CARD_SHARE_MODE: 0x%x\n", val);
+ chip->baro_pkg = val & 0x04 ? QFN : LQFP;
+
+ retval = rtsx_read_register(chip, 0xFE5A, &val);
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+ chip->hw_bypass_sd = val & 0x10 ? 1 : 0;
+
+ retval = rtsx_read_cfg_dw(chip, 0, 0x718, &lval);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ max_func = (u8)((lval >> 29) & 0x07);
+ dev_dbg(rtsx_dev(chip), "Max function number: %d\n", max_func);
+ if (max_func == 0x02)
+ SET_SDIO_EXIST(chip);
+ else
+ CLR_SDIO_EXIST(chip);
+
+ if (chip->use_hw_setting) {
+ retval = rtsx_read_register(chip, CHANGE_LINK_STATE, &val);
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+ chip->auto_delink_en = val & 0x80 ? 1 : 0;
+
+ if (CHECK_BARO_PKG(chip, LQFP))
+ chip->lun_mode = SD_MS_1LUN;
+ else
+ chip->lun_mode = DEFAULT_SINGLE;
+ }
+
+ return STATUS_SUCCESS;
+}
+
+int rtsx_init_chip(struct rtsx_chip *chip)
+{
+ struct sd_info *sd_card = &chip->sd_card;
+ struct xd_info *xd_card = &chip->xd_card;
+ struct ms_info *ms_card = &chip->ms_card;
+ int retval;
+ unsigned int i;
+
+ dev_dbg(rtsx_dev(chip), "Vendor ID: 0x%04x, Product ID: 0x%04x\n",
+ chip->vendor_id, chip->product_id);
+
+ chip->ic_version = 0;
+
+#ifdef _MSG_TRACE
+ chip->msg_idx = 0;
+#endif
+
+ memset(xd_card, 0, sizeof(struct xd_info));
+ memset(sd_card, 0, sizeof(struct sd_info));
+ memset(ms_card, 0, sizeof(struct ms_info));
+
+ chip->xd_reset_counter = 0;
+ chip->sd_reset_counter = 0;
+ chip->ms_reset_counter = 0;
+
+ chip->xd_show_cnt = MAX_SHOW_CNT;
+ chip->sd_show_cnt = MAX_SHOW_CNT;
+ chip->ms_show_cnt = MAX_SHOW_CNT;
+
+ chip->sd_io = 0;
+ chip->auto_delink_cnt = 0;
+ chip->auto_delink_allowed = 1;
+ rtsx_set_stat(chip, RTSX_STAT_INIT);
+
+ chip->aspm_enabled = 0;
+ chip->chip_insert_with_sdio = 0;
+ chip->sdio_aspm = 0;
+ chip->sdio_idle = 0;
+ chip->sdio_counter = 0;
+ chip->cur_card = 0;
+ chip->phy_debug_mode = 0;
+ chip->sdio_func_exist = 0;
+ memset(chip->sdio_raw_data, 0, 12);
+
+ for (i = 0; i < MAX_ALLOWED_LUN_CNT; i++) {
+ set_sense_type(chip, i, SENSE_TYPE_NO_SENSE);
+ chip->rw_fail_cnt[i] = 0;
+ }
+
+ if (!check_sd_speed_prior(chip->sd_speed_prior))
+ chip->sd_speed_prior = 0x01040203;
+
+ dev_dbg(rtsx_dev(chip), "sd_speed_prior = 0x%08x\n",
+ chip->sd_speed_prior);
+
+ if (!check_sd_current_prior(chip->sd_current_prior))
+ chip->sd_current_prior = 0x00010203;
+
+ dev_dbg(rtsx_dev(chip), "sd_current_prior = 0x%08x\n",
+ chip->sd_current_prior);
+
+ if ((chip->sd_ddr_tx_phase > 31) || (chip->sd_ddr_tx_phase < 0))
+ chip->sd_ddr_tx_phase = 0;
+
+ if ((chip->mmc_ddr_tx_phase > 31) || (chip->mmc_ddr_tx_phase < 0))
+ chip->mmc_ddr_tx_phase = 0;
+
+ retval = rtsx_write_register(chip, FPDCTL, SSC_POWER_DOWN, 0);
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+ wait_timeout(200);
+ retval = rtsx_write_register(chip, CLK_DIV, 0x07, 0x07);
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+ dev_dbg(rtsx_dev(chip), "chip->use_hw_setting = %d\n",
+ chip->use_hw_setting);
+
+ if (CHECK_PID(chip, 0x5208)) {
+ retval = rts5208_init(chip);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ } else if (CHECK_PID(chip, 0x5288)) {
+ retval = rts5288_init(chip);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+ }
+
+ if (chip->ss_en == 2)
+ chip->ss_en = 0;
+
+ dev_dbg(rtsx_dev(chip), "chip->asic_code = %d\n", chip->asic_code);
+ dev_dbg(rtsx_dev(chip), "chip->ic_version = 0x%x\n", chip->ic_version);
+ dev_dbg(rtsx_dev(chip), "chip->phy_debug_mode = %d\n",
+ chip->phy_debug_mode);
+ dev_dbg(rtsx_dev(chip), "chip->aux_pwr_exist = %d\n",
+ chip->aux_pwr_exist);
+ dev_dbg(rtsx_dev(chip), "chip->sdio_func_exist = %d\n",
+ chip->sdio_func_exist);
+ dev_dbg(rtsx_dev(chip), "chip->hw_bypass_sd = %d\n",
+ chip->hw_bypass_sd);
+ dev_dbg(rtsx_dev(chip), "chip->aspm_l0s_l1_en = %d\n",
+ chip->aspm_l0s_l1_en);
+ dev_dbg(rtsx_dev(chip), "chip->lun_mode = %d\n", chip->lun_mode);
+ dev_dbg(rtsx_dev(chip), "chip->auto_delink_en = %d\n",
+ chip->auto_delink_en);
+ dev_dbg(rtsx_dev(chip), "chip->ss_en = %d\n", chip->ss_en);
+ dev_dbg(rtsx_dev(chip), "chip->baro_pkg = %d\n", chip->baro_pkg);
+
+ if (CHECK_LUN_MODE(chip, SD_MS_2LUN)) {
+ chip->card2lun[SD_CARD] = 0;
+ chip->card2lun[MS_CARD] = 1;
+ chip->card2lun[XD_CARD] = 0xFF;
+ chip->lun2card[0] = SD_CARD;
+ chip->lun2card[1] = MS_CARD;
+ chip->max_lun = 1;
+ SET_SDIO_IGNORED(chip);
+ } else if (CHECK_LUN_MODE(chip, SD_MS_1LUN)) {
+ chip->card2lun[SD_CARD] = 0;
+ chip->card2lun[MS_CARD] = 0;
+ chip->card2lun[XD_CARD] = 0xFF;
+ chip->lun2card[0] = SD_CARD | MS_CARD;
+ chip->max_lun = 0;
+ } else {
+ chip->card2lun[XD_CARD] = 0;
+ chip->card2lun[SD_CARD] = 0;
+ chip->card2lun[MS_CARD] = 0;
+ chip->lun2card[0] = XD_CARD | SD_CARD | MS_CARD;
+ chip->max_lun = 0;
+ }
+
+ retval = rtsx_reset_chip(chip);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ return STATUS_SUCCESS;
+}
+
+void rtsx_release_chip(struct rtsx_chip *chip)
+{
+ xd_free_l2p_tbl(chip);
+ ms_free_l2p_tbl(chip);
+ chip->card_exist = 0;
+ chip->card_ready = 0;
+}
+
+#if !defined(LED_AUTO_BLINK) && defined(REGULAR_BLINK)
+static inline void rtsx_blink_led(struct rtsx_chip *chip)
+{
+ if (chip->card_exist && chip->blink_led) {
+ if (chip->led_toggle_counter < LED_TOGGLE_INTERVAL) {
+ chip->led_toggle_counter++;
+ } else {
+ chip->led_toggle_counter = 0;
+ toggle_gpio(chip, LED_GPIO);
+ }
+ }
+}
+#endif
+
+static void rtsx_monitor_aspm_config(struct rtsx_chip *chip)
+{
+ bool reg_changed, maybe_support_aspm;
+ u32 tmp = 0;
+ u8 reg0 = 0, reg1 = 0;
+
+ maybe_support_aspm = false;
+ reg_changed = false;
+ rtsx_read_config_byte(chip, LCTLR, &reg0);
+ if (chip->aspm_level[0] != reg0) {
+ reg_changed = true;
+ chip->aspm_level[0] = reg0;
+ }
+ if (CHK_SDIO_EXIST(chip) && !CHK_SDIO_IGNORED(chip)) {
+ rtsx_read_cfg_dw(chip, 1, 0xC0, &tmp);
+ reg1 = (u8)tmp;
+ if (chip->aspm_level[1] != reg1) {
+ reg_changed = true;
+ chip->aspm_level[1] = reg1;
+ }
+
+ if ((reg0 & 0x03) && (reg1 & 0x03))
+ maybe_support_aspm = true;
+
+ } else {
+ if (reg0 & 0x03)
+ maybe_support_aspm = true;
+ }
+
+ if (reg_changed) {
+ if (maybe_support_aspm)
+ chip->aspm_l0s_l1_en = 0x03;
+
+ dev_dbg(rtsx_dev(chip), "aspm_level[0] = 0x%02x, aspm_level[1] = 0x%02x\n",
+ chip->aspm_level[0], chip->aspm_level[1]);
+
+ if (chip->aspm_l0s_l1_en) {
+ chip->aspm_enabled = 1;
+ } else {
+ chip->aspm_enabled = 0;
+ chip->sdio_aspm = 0;
+ }
+ rtsx_write_register(chip, ASPM_FORCE_CTL, 0xFF,
+ 0x30 | chip->aspm_level[0] |
+ (chip->aspm_level[1] << 2));
+ }
+}
+
+void rtsx_polling_func(struct rtsx_chip *chip)
+{
+#ifdef SUPPORT_SD_LOCK
+ struct sd_info *sd_card = &chip->sd_card;
+#endif
+ bool ss_allowed;
+
+ if (rtsx_chk_stat(chip, RTSX_STAT_SUSPEND))
+ return;
+
+ if (rtsx_chk_stat(chip, RTSX_STAT_DELINK))
+ goto delink_stage;
+
+ if (chip->polling_config) {
+ u8 val;
+
+ rtsx_read_config_byte(chip, 0, &val);
+ }
+
+ if (rtsx_chk_stat(chip, RTSX_STAT_SS))
+ return;
+
+#ifdef SUPPORT_OCP
+ if (chip->ocp_int) {
+ rtsx_read_register(chip, OCPSTAT, &chip->ocp_stat);
+
+ if (chip->card_exist & SD_CARD)
+ sd_power_off_card3v3(chip);
+ else if (chip->card_exist & MS_CARD)
+ ms_power_off_card3v3(chip);
+ else if (chip->card_exist & XD_CARD)
+ xd_power_off_card3v3(chip);
+
+ chip->ocp_int = 0;
+ }
+#endif
+
+#ifdef SUPPORT_SD_LOCK
+ if (sd_card->sd_erase_status) {
+ if (chip->card_exist & SD_CARD) {
+ u8 val;
+
+ rtsx_read_register(chip, 0xFD30, &val);
+ if (val & 0x02) {
+ sd_card->sd_erase_status = SD_NOT_ERASE;
+ sd_card->sd_lock_notify = 1;
+ chip->need_reinit |= SD_CARD;
+ }
+ } else {
+ sd_card->sd_erase_status = SD_NOT_ERASE;
+ }
+ }
+#endif
+
+ rtsx_init_cards(chip);
+
+ if (chip->ss_en) {
+ ss_allowed = true;
+
+ if (CHECK_PID(chip, 0x5288)) {
+ ss_allowed = false;
+ } else {
+ if (CHK_SDIO_EXIST(chip) && !CHK_SDIO_IGNORED(chip)) {
+ u32 val;
+
+ rtsx_read_cfg_dw(chip, 1, 0x04, &val);
+ if (val & 0x07)
+ ss_allowed = false;
+ }
+ }
+ } else {
+ ss_allowed = false;
+ }
+
+ if (ss_allowed && !chip->sd_io) {
+ if (rtsx_get_stat(chip) != RTSX_STAT_IDLE) {
+ chip->ss_counter = 0;
+ } else {
+ if (chip->ss_counter <
+ (chip->ss_idle_period / POLLING_INTERVAL)) {
+ chip->ss_counter++;
+ } else {
+ rtsx_exclusive_enter_ss(chip);
+ return;
+ }
+ }
+ }
+
+ if (CHECK_PID(chip, 0x5208)) {
+ rtsx_monitor_aspm_config(chip);
+
+#ifdef SUPPORT_SDIO_ASPM
+ if (CHK_SDIO_EXIST(chip) && !CHK_SDIO_IGNORED(chip) &&
+ chip->aspm_l0s_l1_en && chip->dynamic_aspm) {
+ if (chip->sd_io) {
+ dynamic_configure_sdio_aspm(chip);
+ } else {
+ if (!chip->sdio_aspm) {
+ dev_dbg(rtsx_dev(chip), "SDIO enter ASPM!\n");
+ rtsx_write_register(chip,
+ ASPM_FORCE_CTL, 0xFC,
+ 0x30 |
+ (chip->aspm_level[1] << 2));
+ chip->sdio_aspm = 1;
+ }
+ }
+ }
+#endif
+ }
+
+ if (chip->idle_counter < IDLE_MAX_COUNT) {
+ chip->idle_counter++;
+ } else {
+ if (rtsx_get_stat(chip) != RTSX_STAT_IDLE) {
+ dev_dbg(rtsx_dev(chip), "Idle state!\n");
+ rtsx_set_stat(chip, RTSX_STAT_IDLE);
+
+#if !defined(LED_AUTO_BLINK) && defined(REGULAR_BLINK)
+ chip->led_toggle_counter = 0;
+#endif
+ rtsx_force_power_on(chip, SSC_PDCTL);
+
+ turn_off_led(chip, LED_GPIO);
+
+ if (chip->auto_power_down && !chip->card_ready &&
+ !chip->sd_io)
+ rtsx_force_power_down(chip,
+ SSC_PDCTL | OC_PDCTL);
+ }
+ }
+
+ switch (rtsx_get_stat(chip)) {
+ case RTSX_STAT_RUN:
+#if !defined(LED_AUTO_BLINK) && defined(REGULAR_BLINK)
+ rtsx_blink_led(chip);
+#endif
+ do_remaining_work(chip);
+ break;
+
+ case RTSX_STAT_IDLE:
+ if (chip->sd_io && !chip->sd_int)
+ try_to_switch_sdio_ctrl(chip);
+
+ rtsx_enable_aspm(chip);
+ break;
+
+ default:
+ break;
+ }
+
+#ifdef SUPPORT_OCP
+ if (CHECK_LUN_MODE(chip, SD_MS_2LUN)) {
+ if (chip->ocp_stat &
+ (SD_OC_NOW | SD_OC_EVER | MS_OC_NOW | MS_OC_EVER))
+ dev_dbg(rtsx_dev(chip), "Over current, OCPSTAT is 0x%x\n",
+ chip->ocp_stat);
+
+ if (chip->ocp_stat & (SD_OC_NOW | SD_OC_EVER)) {
+ if (chip->card_exist & SD_CARD) {
+ rtsx_write_register(chip, CARD_OE, SD_OUTPUT_EN,
+ 0);
+ card_power_off(chip, SD_CARD);
+ chip->card_fail |= SD_CARD;
+ }
+ }
+ if (chip->ocp_stat & (MS_OC_NOW | MS_OC_EVER)) {
+ if (chip->card_exist & MS_CARD) {
+ rtsx_write_register(chip, CARD_OE, MS_OUTPUT_EN,
+ 0);
+ card_power_off(chip, MS_CARD);
+ chip->card_fail |= MS_CARD;
+ }
+ }
+ } else {
+ if (chip->ocp_stat & (SD_OC_NOW | SD_OC_EVER)) {
+ dev_dbg(rtsx_dev(chip), "Over current, OCPSTAT is 0x%x\n",
+ chip->ocp_stat);
+ if (chip->card_exist & SD_CARD) {
+ rtsx_write_register(chip, CARD_OE, SD_OUTPUT_EN,
+ 0);
+ chip->card_fail |= SD_CARD;
+ } else if (chip->card_exist & MS_CARD) {
+ rtsx_write_register(chip, CARD_OE, MS_OUTPUT_EN,
+ 0);
+ chip->card_fail |= MS_CARD;
+ } else if (chip->card_exist & XD_CARD) {
+ rtsx_write_register(chip, CARD_OE, XD_OUTPUT_EN,
+ 0);
+ chip->card_fail |= XD_CARD;
+ }
+ card_power_off(chip, SD_CARD);
+ }
+ }
+#endif
+
+delink_stage:
+ if (chip->auto_delink_en && chip->auto_delink_allowed &&
+ !chip->card_ready && !chip->card_ejected && !chip->sd_io) {
+ int enter_L1 = chip->auto_delink_in_L1 && (
+ chip->aspm_l0s_l1_en || chip->ss_en);
+ int delink_stage1_cnt = chip->delink_stage1_step;
+ int delink_stage2_cnt = delink_stage1_cnt +
+ chip->delink_stage2_step;
+ int delink_stage3_cnt = delink_stage2_cnt +
+ chip->delink_stage3_step;
+
+ if (chip->auto_delink_cnt <= delink_stage3_cnt) {
+ if (chip->auto_delink_cnt == delink_stage1_cnt) {
+ rtsx_set_stat(chip, RTSX_STAT_DELINK);
+
+ if (chip->asic_code && CHECK_PID(chip, 0x5208))
+ rtsx_set_phy_reg_bit(chip, 0x1C, 2);
+
+ if (chip->card_exist) {
+ dev_dbg(rtsx_dev(chip), "False card inserted, do force delink\n");
+
+ if (enter_L1)
+ rtsx_write_register(chip,
+ HOST_SLEEP_STATE,
+ 0x03, 1);
+
+ rtsx_write_register(chip,
+ CHANGE_LINK_STATE,
+ 0x0A, 0x0A);
+
+ if (enter_L1)
+ rtsx_enter_L1(chip);
+
+ chip->auto_delink_cnt =
+ delink_stage3_cnt + 1;
+ } else {
+ dev_dbg(rtsx_dev(chip), "No card inserted, do delink\n");
+
+ if (enter_L1)
+ rtsx_write_register(chip,
+ HOST_SLEEP_STATE,
+ 0x03, 1);
+
+ rtsx_write_register(chip,
+ CHANGE_LINK_STATE,
+ 0x02, 0x02);
+
+ if (enter_L1)
+ rtsx_enter_L1(chip);
+ }
+ }
+
+ if (chip->auto_delink_cnt == delink_stage2_cnt) {
+ dev_dbg(rtsx_dev(chip), "Try to do force delink\n");
+
+ if (enter_L1)
+ rtsx_exit_L1(chip);
+
+ if (chip->asic_code && CHECK_PID(chip, 0x5208))
+ rtsx_set_phy_reg_bit(chip, 0x1C, 2);
+
+ rtsx_write_register(chip, CHANGE_LINK_STATE,
+ 0x0A, 0x0A);
+ }
+
+ chip->auto_delink_cnt++;
+ }
+ } else {
+ chip->auto_delink_cnt = 0;
+ }
+}
+
+void rtsx_undo_delink(struct rtsx_chip *chip)
+{
+ chip->auto_delink_allowed = 0;
+ rtsx_write_register(chip, CHANGE_LINK_STATE, 0x0A, 0x00);
+}
+
+/**
+ * rtsx_stop_cmd - stop command transfer and DMA transfer
+ * @chip: Realtek's card reader chip
+ * @card: flash card type
+ *
+ * Stop command transfer and DMA transfer.
+ * This function is called in error handler.
+ */
+void rtsx_stop_cmd(struct rtsx_chip *chip, int card)
+{
+ int i;
+
+ for (i = 0; i <= 8; i++) {
+ int addr = RTSX_HCBAR + i * 4;
+ u32 reg;
+
+ reg = rtsx_readl(chip, addr);
+ dev_dbg(rtsx_dev(chip), "BAR (0x%02x): 0x%08x\n", addr, reg);
+ }
+ rtsx_writel(chip, RTSX_HCBCTLR, STOP_CMD);
+ rtsx_writel(chip, RTSX_HDBCTLR, STOP_DMA);
+
+ for (i = 0; i < 16; i++) {
+ u16 addr = 0xFE20 + (u16)i;
+ u8 val;
+
+ rtsx_read_register(chip, addr, &val);
+ dev_dbg(rtsx_dev(chip), "0x%04X: 0x%02x\n", addr, val);
+ }
+
+ rtsx_write_register(chip, DMACTL, 0x80, 0x80);
+ rtsx_write_register(chip, RBCTL, 0x80, 0x80);
+}
+
+#define MAX_RW_REG_CNT 1024
+
+int rtsx_write_register(struct rtsx_chip *chip, u16 addr, u8 mask, u8 data)
+{
+ int i;
+ u32 val = 3 << 30;
+
+ val |= (u32)(addr & 0x3FFF) << 16;
+ val |= (u32)mask << 8;
+ val |= (u32)data;
+
+ rtsx_writel(chip, RTSX_HAIMR, val);
+
+ for (i = 0; i < MAX_RW_REG_CNT; i++) {
+ val = rtsx_readl(chip, RTSX_HAIMR);
+ if ((val & (1 << 31)) == 0) {
+ if (data != (u8)val) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ return STATUS_SUCCESS;
+ }
+ }
+
+ rtsx_trace(chip);
+ return STATUS_TIMEDOUT;
+}
+
+int rtsx_read_register(struct rtsx_chip *chip, u16 addr, u8 *data)
+{
+ u32 val = 2 << 30;
+ int i;
+
+ if (data)
+ *data = 0;
+
+ val |= (u32)(addr & 0x3FFF) << 16;
+
+ rtsx_writel(chip, RTSX_HAIMR, val);
+
+ for (i = 0; i < MAX_RW_REG_CNT; i++) {
+ val = rtsx_readl(chip, RTSX_HAIMR);
+ if ((val & (1 << 31)) == 0)
+ break;
+ }
+
+ if (i >= MAX_RW_REG_CNT) {
+ rtsx_trace(chip);
+ return STATUS_TIMEDOUT;
+ }
+
+ if (data)
+ *data = (u8)(val & 0xFF);
+
+ return STATUS_SUCCESS;
+}
+
+int rtsx_write_cfg_dw(struct rtsx_chip *chip, u8 func_no, u16 addr, u32 mask,
+ u32 val)
+{
+ int retval;
+ u8 mode = 0, tmp;
+ int i;
+
+ for (i = 0; i < 4; i++) {
+ if (mask & 0xFF) {
+ retval = rtsx_write_register(chip, CFGDATA0 + i,
+ 0xFF,
+ (u8)(val & mask & 0xFF));
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+ mode |= (1 << i);
+ }
+ mask >>= 8;
+ val >>= 8;
+ }
+
+ if (mode) {
+ retval = rtsx_write_register(chip, CFGADDR0, 0xFF, (u8)addr);
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+ retval = rtsx_write_register(chip, CFGADDR1, 0xFF,
+ (u8)(addr >> 8));
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+
+ retval = rtsx_write_register(chip, CFGRWCTL, 0xFF,
+ 0x80 | mode | ((func_no & 0x03) << 4));
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+
+ for (i = 0; i < MAX_RW_REG_CNT; i++) {
+ retval = rtsx_read_register(chip, CFGRWCTL, &tmp);
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+ if ((tmp & 0x80) == 0)
+ break;
+ }
+ }
+
+ return STATUS_SUCCESS;
+}
+
+int rtsx_read_cfg_dw(struct rtsx_chip *chip, u8 func_no, u16 addr, u32 *val)
+{
+ int retval;
+ int i;
+ u8 tmp;
+ u32 data = 0;
+
+ retval = rtsx_write_register(chip, CFGADDR0, 0xFF, (u8)addr);
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+ retval = rtsx_write_register(chip, CFGADDR1, 0xFF, (u8)(addr >> 8));
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+ retval = rtsx_write_register(chip, CFGRWCTL, 0xFF,
+ 0x80 | ((func_no & 0x03) << 4));
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+
+ for (i = 0; i < MAX_RW_REG_CNT; i++) {
+ retval = rtsx_read_register(chip, CFGRWCTL, &tmp);
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+ if ((tmp & 0x80) == 0)
+ break;
+ }
+
+ for (i = 0; i < 4; i++) {
+ retval = rtsx_read_register(chip, CFGDATA0 + i, &tmp);
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+ data |= (u32)tmp << (i * 8);
+ }
+
+ if (val)
+ *val = data;
+
+ return STATUS_SUCCESS;
+}
+
+int rtsx_write_cfg_seq(struct rtsx_chip *chip, u8 func, u16 addr, u8 *buf,
+ int len)
+{
+ u32 *data, *mask;
+ u16 offset = addr % 4;
+ u16 aligned_addr = addr - offset;
+ int dw_len, i, j;
+ int retval;
+
+ if (!buf) {
+ rtsx_trace(chip);
+ return STATUS_NOMEM;
+ }
+
+ if ((len + offset) % 4)
+ dw_len = (len + offset) / 4 + 1;
+ else
+ dw_len = (len + offset) / 4;
+
+ dev_dbg(rtsx_dev(chip), "dw_len = %d\n", dw_len);
+
+ data = vzalloc(dw_len * 4);
+ if (!data) {
+ rtsx_trace(chip);
+ return STATUS_NOMEM;
+ }
+
+ mask = vzalloc(dw_len * 4);
+ if (!mask) {
+ vfree(data);
+ rtsx_trace(chip);
+ return STATUS_NOMEM;
+ }
+
+ j = 0;
+ for (i = 0; i < len; i++) {
+ mask[j] |= 0xFF << (offset * 8);
+ data[j] |= buf[i] << (offset * 8);
+ if (++offset == 4) {
+ j++;
+ offset = 0;
+ }
+ }
+
+ print_hex_dump_bytes(KBUILD_MODNAME ": ", DUMP_PREFIX_NONE, mask,
+ dw_len * 4);
+ print_hex_dump_bytes(KBUILD_MODNAME ": ", DUMP_PREFIX_NONE, data,
+ dw_len * 4);
+
+ for (i = 0; i < dw_len; i++) {
+ retval = rtsx_write_cfg_dw(chip, func, aligned_addr + i * 4,
+ mask[i], data[i]);
+ if (retval != STATUS_SUCCESS) {
+ vfree(data);
+ vfree(mask);
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+ }
+
+ vfree(data);
+ vfree(mask);
+
+ return STATUS_SUCCESS;
+}
+
+int rtsx_read_cfg_seq(struct rtsx_chip *chip, u8 func, u16 addr, u8 *buf,
+ int len)
+{
+ u32 *data;
+ u16 offset = addr % 4;
+ u16 aligned_addr = addr - offset;
+ int dw_len, i, j;
+ int retval;
+
+ if ((len + offset) % 4)
+ dw_len = (len + offset) / 4 + 1;
+ else
+ dw_len = (len + offset) / 4;
+
+ dev_dbg(rtsx_dev(chip), "dw_len = %d\n", dw_len);
+
+ data = vmalloc(dw_len * 4);
+ if (!data) {
+ rtsx_trace(chip);
+ return STATUS_NOMEM;
+ }
+
+ for (i = 0; i < dw_len; i++) {
+ retval = rtsx_read_cfg_dw(chip, func, aligned_addr + i * 4,
+ data + i);
+ if (retval != STATUS_SUCCESS) {
+ vfree(data);
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+ }
+
+ if (buf) {
+ j = 0;
+
+ for (i = 0; i < len; i++) {
+ buf[i] = (u8)(data[j] >> (offset * 8));
+ if (++offset == 4) {
+ j++;
+ offset = 0;
+ }
+ }
+ }
+
+ vfree(data);
+
+ return STATUS_SUCCESS;
+}
+
+int rtsx_write_phy_register(struct rtsx_chip *chip, u8 addr, u16 val)
+{
+ int retval;
+ bool finished = false;
+ int i;
+ u8 tmp;
+
+ retval = rtsx_write_register(chip, PHYDATA0, 0xFF, (u8)val);
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+ retval = rtsx_write_register(chip, PHYDATA1, 0xFF, (u8)(val >> 8));
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+ retval = rtsx_write_register(chip, PHYADDR, 0xFF, addr);
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+ retval = rtsx_write_register(chip, PHYRWCTL, 0xFF, 0x81);
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+
+ for (i = 0; i < 100000; i++) {
+ retval = rtsx_read_register(chip, PHYRWCTL, &tmp);
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+ if (!(tmp & 0x80)) {
+ finished = true;
+ break;
+ }
+ }
+
+ if (!finished) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ return STATUS_SUCCESS;
+}
+
+int rtsx_read_phy_register(struct rtsx_chip *chip, u8 addr, u16 *val)
+{
+ int retval;
+ bool finished = false;
+ int i;
+ u16 data = 0;
+ u8 tmp;
+
+ retval = rtsx_write_register(chip, PHYADDR, 0xFF, addr);
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+ retval = rtsx_write_register(chip, PHYRWCTL, 0xFF, 0x80);
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+
+ for (i = 0; i < 100000; i++) {
+ retval = rtsx_read_register(chip, PHYRWCTL, &tmp);
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+ if (!(tmp & 0x80)) {
+ finished = true;
+ break;
+ }
+ }
+
+ if (!finished) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ retval = rtsx_read_register(chip, PHYDATA0, &tmp);
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+ data = tmp;
+ retval = rtsx_read_register(chip, PHYDATA1, &tmp);
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+ data |= (u16)tmp << 8;
+
+ if (val)
+ *val = data;
+
+ return STATUS_SUCCESS;
+}
+
+int rtsx_read_efuse(struct rtsx_chip *chip, u8 addr, u8 *val)
+{
+ int retval;
+ int i;
+ u8 data = 0;
+
+ retval = rtsx_write_register(chip, EFUSE_CTRL, 0xFF, 0x80 | addr);
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+
+ for (i = 0; i < 100; i++) {
+ retval = rtsx_read_register(chip, EFUSE_CTRL, &data);
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+ if (!(data & 0x80))
+ break;
+ udelay(1);
+ }
+
+ if (data & 0x80) {
+ rtsx_trace(chip);
+ return STATUS_TIMEDOUT;
+ }
+
+ retval = rtsx_read_register(chip, EFUSE_DATA, &data);
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+ if (val)
+ *val = data;
+
+ return STATUS_SUCCESS;
+}
+
+int rtsx_write_efuse(struct rtsx_chip *chip, u8 addr, u8 val)
+{
+ int retval;
+ int i, j;
+ u8 data = 0, tmp = 0xFF;
+
+ for (i = 0; i < 8; i++) {
+ if (val & (u8)(1 << i))
+ continue;
+
+ tmp &= (~(u8)(1 << i));
+ dev_dbg(rtsx_dev(chip), "Write 0x%x to 0x%x\n", tmp, addr);
+
+ retval = rtsx_write_register(chip, EFUSE_DATA, 0xFF, tmp);
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+ retval = rtsx_write_register(chip, EFUSE_CTRL, 0xFF,
+ 0xA0 | addr);
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+
+ for (j = 0; j < 100; j++) {
+ retval = rtsx_read_register(chip, EFUSE_CTRL, &data);
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+ if (!(data & 0x80))
+ break;
+ wait_timeout(3);
+ }
+
+ if (data & 0x80) {
+ rtsx_trace(chip);
+ return STATUS_TIMEDOUT;
+ }
+
+ wait_timeout(5);
+ }
+
+ return STATUS_SUCCESS;
+}
+
+int rtsx_clr_phy_reg_bit(struct rtsx_chip *chip, u8 reg, u8 bit)
+{
+ int retval;
+ u16 value;
+
+ retval = rtsx_read_phy_register(chip, reg, &value);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ if (value & (1 << bit)) {
+ value &= ~(1 << bit);
+ retval = rtsx_write_phy_register(chip, reg, value);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+ }
+
+ return STATUS_SUCCESS;
+}
+
+int rtsx_set_phy_reg_bit(struct rtsx_chip *chip, u8 reg, u8 bit)
+{
+ int retval;
+ u16 value;
+
+ retval = rtsx_read_phy_register(chip, reg, &value);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ if ((value & (1 << bit)) == 0) {
+ value |= (1 << bit);
+ retval = rtsx_write_phy_register(chip, reg, value);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+ }
+
+ return STATUS_SUCCESS;
+}
+
+int rtsx_check_link_ready(struct rtsx_chip *chip)
+{
+ int retval;
+ u8 val;
+
+ retval = rtsx_read_register(chip, IRQSTAT0, &val);
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+
+ dev_dbg(rtsx_dev(chip), "IRQSTAT0: 0x%x\n", val);
+ if (val & LINK_RDY_INT) {
+ dev_dbg(rtsx_dev(chip), "Delinked!\n");
+ rtsx_write_register(chip, IRQSTAT0, LINK_RDY_INT, LINK_RDY_INT);
+ return STATUS_FAIL;
+ }
+
+ return STATUS_SUCCESS;
+}
+
+static void rtsx_handle_pm_dstate(struct rtsx_chip *chip, u8 dstate)
+{
+ u32 ultmp;
+
+ dev_dbg(rtsx_dev(chip), "%04x set pm_dstate to %d\n",
+ chip->product_id, dstate);
+
+ if (CHK_SDIO_EXIST(chip)) {
+ u8 func_no;
+
+ if (CHECK_PID(chip, 0x5288))
+ func_no = 2;
+ else
+ func_no = 1;
+
+ rtsx_read_cfg_dw(chip, func_no, 0x84, &ultmp);
+ dev_dbg(rtsx_dev(chip), "pm_dstate of function %d: 0x%x\n",
+ (int)func_no, ultmp);
+ rtsx_write_cfg_dw(chip, func_no, 0x84, 0xFF, dstate);
+ }
+
+ rtsx_write_config_byte(chip, 0x44, dstate);
+ rtsx_write_config_byte(chip, 0x45, 0);
+}
+
+void rtsx_enter_L1(struct rtsx_chip *chip)
+{
+ rtsx_handle_pm_dstate(chip, 2);
+}
+
+void rtsx_exit_L1(struct rtsx_chip *chip)
+{
+ rtsx_write_config_byte(chip, 0x44, 0);
+ rtsx_write_config_byte(chip, 0x45, 0);
+}
+
+void rtsx_enter_ss(struct rtsx_chip *chip)
+{
+ dev_dbg(rtsx_dev(chip), "Enter Selective Suspend State!\n");
+
+ rtsx_write_register(chip, IRQSTAT0, LINK_RDY_INT, LINK_RDY_INT);
+
+ if (chip->power_down_in_ss) {
+ rtsx_power_off_card(chip);
+ rtsx_force_power_down(chip, SSC_PDCTL | OC_PDCTL);
+ }
+
+ if (CHK_SDIO_EXIST(chip))
+ rtsx_write_cfg_dw(chip, CHECK_PID(chip, 0x5288) ? 2 : 1,
+ 0xC0, 0xFF00, 0x0100);
+
+ if (chip->auto_delink_en) {
+ rtsx_write_register(chip, HOST_SLEEP_STATE, 0x01, 0x01);
+ } else {
+ if (!chip->phy_debug_mode) {
+ u32 tmp;
+
+ tmp = rtsx_readl(chip, RTSX_BIER);
+ tmp |= CARD_INT;
+ rtsx_writel(chip, RTSX_BIER, tmp);
+ }
+
+ rtsx_write_register(chip, CHANGE_LINK_STATE, 0x02, 0);
+ }
+
+ rtsx_enter_L1(chip);
+
+ RTSX_CLR_DELINK(chip);
+ rtsx_set_stat(chip, RTSX_STAT_SS);
+}
+
+void rtsx_exit_ss(struct rtsx_chip *chip)
+{
+ dev_dbg(rtsx_dev(chip), "Exit Selective Suspend State!\n");
+
+ rtsx_exit_L1(chip);
+
+ if (chip->power_down_in_ss) {
+ rtsx_force_power_on(chip, SSC_PDCTL | OC_PDCTL);
+ udelay(1000);
+ }
+
+ if (RTSX_TST_DELINK(chip)) {
+ chip->need_reinit = SD_CARD | MS_CARD | XD_CARD;
+ rtsx_reinit_cards(chip, 1);
+ RTSX_CLR_DELINK(chip);
+ } else if (chip->power_down_in_ss) {
+ chip->need_reinit = SD_CARD | MS_CARD | XD_CARD;
+ rtsx_reinit_cards(chip, 0);
+ }
+}
+
+int rtsx_pre_handle_interrupt(struct rtsx_chip *chip)
+{
+ u32 status, int_enable;
+ bool exit_ss = false;
+#ifdef SUPPORT_OCP
+ u32 ocp_int = 0;
+
+ ocp_int = OC_INT;
+#endif
+
+ if (chip->ss_en) {
+ chip->ss_counter = 0;
+ if (rtsx_get_stat(chip) == RTSX_STAT_SS) {
+ exit_ss = true;
+ rtsx_exit_L1(chip);
+ rtsx_set_stat(chip, RTSX_STAT_RUN);
+ }
+ }
+
+ int_enable = rtsx_readl(chip, RTSX_BIER);
+ chip->int_reg = rtsx_readl(chip, RTSX_BIPR);
+
+ if (((chip->int_reg & int_enable) == 0) ||
+ (chip->int_reg == 0xFFFFFFFF))
+ return STATUS_FAIL;
+
+ status = chip->int_reg &= (int_enable | 0x7FFFFF);
+
+ if (status & CARD_INT) {
+ chip->auto_delink_cnt = 0;
+
+ if (status & SD_INT) {
+ if (status & SD_EXIST) {
+ set_bit(SD_NR, &chip->need_reset);
+ } else {
+ set_bit(SD_NR, &chip->need_release);
+ chip->sd_reset_counter = 0;
+ chip->sd_show_cnt = 0;
+ clear_bit(SD_NR, &chip->need_reset);
+ }
+ } else {
+ /* If multi-luns, it's possible that
+ when plugging/unplugging one card
+ there is another card which still
+ exists in the slot. In this case,
+ all existed cards should be reset.
+ */
+ if (exit_ss && (status & SD_EXIST))
+ set_bit(SD_NR, &chip->need_reinit);
+ }
+ if (!CHECK_PID(chip, 0x5288) || CHECK_BARO_PKG(chip, QFN)) {
+ if (status & XD_INT) {
+ if (status & XD_EXIST) {
+ set_bit(XD_NR, &chip->need_reset);
+ } else {
+ set_bit(XD_NR, &chip->need_release);
+ chip->xd_reset_counter = 0;
+ chip->xd_show_cnt = 0;
+ clear_bit(XD_NR, &chip->need_reset);
+ }
+ } else {
+ if (exit_ss && (status & XD_EXIST))
+ set_bit(XD_NR, &chip->need_reinit);
+ }
+ }
+ if (status & MS_INT) {
+ if (status & MS_EXIST) {
+ set_bit(MS_NR, &chip->need_reset);
+ } else {
+ set_bit(MS_NR, &chip->need_release);
+ chip->ms_reset_counter = 0;
+ chip->ms_show_cnt = 0;
+ clear_bit(MS_NR, &chip->need_reset);
+ }
+ } else {
+ if (exit_ss && (status & MS_EXIST))
+ set_bit(MS_NR, &chip->need_reinit);
+ }
+ }
+
+#ifdef SUPPORT_OCP
+ chip->ocp_int = ocp_int & status;
+#endif
+
+ if (chip->sd_io && (chip->int_reg & DATA_DONE_INT))
+ chip->int_reg &= ~(u32)DATA_DONE_INT;
+
+ return STATUS_SUCCESS;
+}
+
+void rtsx_do_before_power_down(struct rtsx_chip *chip, int pm_stat)
+{
+ int retval;
+
+ dev_dbg(rtsx_dev(chip), "%s, pm_stat = %d\n", __func__, pm_stat);
+
+ rtsx_set_stat(chip, RTSX_STAT_SUSPEND);
+
+ retval = rtsx_force_power_on(chip, SSC_PDCTL);
+ if (retval != STATUS_SUCCESS)
+ return;
+
+ rtsx_release_cards(chip);
+ rtsx_disable_bus_int(chip);
+ turn_off_led(chip, LED_GPIO);
+
+#ifdef HW_AUTO_SWITCH_SD_BUS
+ if (chip->sd_io) {
+ chip->sdio_in_charge = 1;
+ if (CHECK_PID(chip, 0x5208)) {
+ rtsx_write_register(chip, TLPTISTAT, 0x08, 0x08);
+ /* Enable sdio_bus_auto_switch */
+ rtsx_write_register(chip, 0xFE70, 0x80, 0x80);
+ } else if (CHECK_PID(chip, 0x5288)) {
+ rtsx_write_register(chip, TLPTISTAT, 0x08, 0x08);
+ /* Enable sdio_bus_auto_switch */
+ rtsx_write_register(chip, 0xFE5A, 0x08, 0x08);
+ }
+ }
+#endif
+
+ if (CHECK_PID(chip, 0x5208) && (chip->ic_version >= IC_VER_D)) {
+ /* u_force_clkreq_0 */
+ rtsx_write_register(chip, PETXCFG, 0x08, 0x08);
+ }
+
+ if (pm_stat == PM_S1) {
+ dev_dbg(rtsx_dev(chip), "Host enter S1\n");
+ rtsx_write_register(chip, HOST_SLEEP_STATE, 0x03,
+ HOST_ENTER_S1);
+ } else if (pm_stat == PM_S3) {
+ if (chip->s3_pwr_off_delay > 0)
+ wait_timeout(chip->s3_pwr_off_delay);
+
+ dev_dbg(rtsx_dev(chip), "Host enter S3\n");
+ rtsx_write_register(chip, HOST_SLEEP_STATE, 0x03,
+ HOST_ENTER_S3);
+ }
+
+ if (chip->do_delink_before_power_down && chip->auto_delink_en)
+ rtsx_write_register(chip, CHANGE_LINK_STATE, 0x02, 2);
+
+ rtsx_force_power_down(chip, SSC_PDCTL | OC_PDCTL);
+
+ chip->cur_clk = 0;
+ chip->cur_card = 0;
+ chip->card_exist = 0;
+}
+
+void rtsx_enable_aspm(struct rtsx_chip *chip)
+{
+ if (chip->aspm_l0s_l1_en && chip->dynamic_aspm && !chip->aspm_enabled) {
+ dev_dbg(rtsx_dev(chip), "Try to enable ASPM\n");
+ chip->aspm_enabled = 1;
+
+ if (chip->asic_code && CHECK_PID(chip, 0x5208))
+ rtsx_write_phy_register(chip, 0x07, 0);
+ if (CHECK_PID(chip, 0x5208)) {
+ rtsx_write_register(chip, ASPM_FORCE_CTL, 0xF3,
+ 0x30 | chip->aspm_level[0]);
+ } else {
+ rtsx_write_config_byte(chip, LCTLR,
+ chip->aspm_l0s_l1_en);
+ }
+
+ if (CHK_SDIO_EXIST(chip)) {
+ u16 val = chip->aspm_l0s_l1_en | 0x0100;
+
+ rtsx_write_cfg_dw(chip, CHECK_PID(chip, 0x5288) ? 2 : 1,
+ 0xC0, 0xFFF, val);
+ }
+ }
+}
+
+void rtsx_disable_aspm(struct rtsx_chip *chip)
+{
+ if (CHECK_PID(chip, 0x5208))
+ rtsx_monitor_aspm_config(chip);
+
+ if (chip->aspm_l0s_l1_en && chip->dynamic_aspm && chip->aspm_enabled) {
+ dev_dbg(rtsx_dev(chip), "Try to disable ASPM\n");
+ chip->aspm_enabled = 0;
+
+ if (chip->asic_code && CHECK_PID(chip, 0x5208))
+ rtsx_write_phy_register(chip, 0x07, 0x0129);
+ if (CHECK_PID(chip, 0x5208))
+ rtsx_write_register(chip, ASPM_FORCE_CTL,
+ 0xF3, 0x30);
+ else
+ rtsx_write_config_byte(chip, LCTLR, 0x00);
+
+ wait_timeout(1);
+ }
+}
+
+int rtsx_read_ppbuf(struct rtsx_chip *chip, u8 *buf, int buf_len)
+{
+ int retval;
+ int i, j;
+ u16 reg_addr;
+ u8 *ptr;
+
+ if (!buf) {
+ rtsx_trace(chip);
+ return STATUS_ERROR;
+ }
+
+ ptr = buf;
+ reg_addr = PPBUF_BASE2;
+ for (i = 0; i < buf_len/256; i++) {
+ rtsx_init_cmd(chip);
+
+ for (j = 0; j < 256; j++)
+ rtsx_add_cmd(chip, READ_REG_CMD, reg_addr++, 0, 0);
+
+ retval = rtsx_send_cmd(chip, 0, 250);
+ if (retval < 0) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ memcpy(ptr, rtsx_get_cmd_data(chip), 256);
+ ptr += 256;
+ }
+
+ if (buf_len%256) {
+ rtsx_init_cmd(chip);
+
+ for (j = 0; j < buf_len%256; j++)
+ rtsx_add_cmd(chip, READ_REG_CMD, reg_addr++, 0, 0);
+
+ retval = rtsx_send_cmd(chip, 0, 250);
+ if (retval < 0) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+ }
+
+ memcpy(ptr, rtsx_get_cmd_data(chip), buf_len%256);
+
+ return STATUS_SUCCESS;
+}
+
+int rtsx_write_ppbuf(struct rtsx_chip *chip, u8 *buf, int buf_len)
+{
+ int retval;
+ int i, j;
+ u16 reg_addr;
+ u8 *ptr;
+
+ if (!buf) {
+ rtsx_trace(chip);
+ return STATUS_ERROR;
+ }
+
+ ptr = buf;
+ reg_addr = PPBUF_BASE2;
+ for (i = 0; i < buf_len/256; i++) {
+ rtsx_init_cmd(chip);
+
+ for (j = 0; j < 256; j++) {
+ rtsx_add_cmd(chip, WRITE_REG_CMD, reg_addr++, 0xFF,
+ *ptr);
+ ptr++;
+ }
+
+ retval = rtsx_send_cmd(chip, 0, 250);
+ if (retval < 0) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+ }
+
+ if (buf_len%256) {
+ rtsx_init_cmd(chip);
+
+ for (j = 0; j < buf_len%256; j++) {
+ rtsx_add_cmd(chip, WRITE_REG_CMD, reg_addr++, 0xFF,
+ *ptr);
+ ptr++;
+ }
+
+ retval = rtsx_send_cmd(chip, 0, 250);
+ if (retval < 0) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+ }
+
+ return STATUS_SUCCESS;
+}
+
+int rtsx_check_chip_exist(struct rtsx_chip *chip)
+{
+ if (rtsx_readl(chip, 0) == 0xFFFFFFFF) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ return STATUS_SUCCESS;
+}
+
+int rtsx_force_power_on(struct rtsx_chip *chip, u8 ctl)
+{
+ int retval;
+ u8 mask = 0;
+
+ if (ctl & SSC_PDCTL)
+ mask |= SSC_POWER_DOWN;
+
+#ifdef SUPPORT_OCP
+ if (ctl & OC_PDCTL) {
+ mask |= SD_OC_POWER_DOWN;
+ if (CHECK_LUN_MODE(chip, SD_MS_2LUN))
+ mask |= MS_OC_POWER_DOWN;
+ }
+#endif
+
+ if (mask) {
+ retval = rtsx_write_register(chip, FPDCTL, mask, 0);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ if (CHECK_PID(chip, 0x5288))
+ wait_timeout(200);
+ }
+
+ return STATUS_SUCCESS;
+}
+
+int rtsx_force_power_down(struct rtsx_chip *chip, u8 ctl)
+{
+ int retval;
+ u8 mask = 0, val = 0;
+
+ if (ctl & SSC_PDCTL)
+ mask |= SSC_POWER_DOWN;
+
+#ifdef SUPPORT_OCP
+ if (ctl & OC_PDCTL) {
+ mask |= SD_OC_POWER_DOWN;
+ if (CHECK_LUN_MODE(chip, SD_MS_2LUN))
+ mask |= MS_OC_POWER_DOWN;
+ }
+#endif
+
+ if (mask) {
+ val = mask;
+ retval = rtsx_write_register(chip, FPDCTL, mask, val);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+ }
+
+ return STATUS_SUCCESS;
+}
diff --git a/drivers/staging/rts5208/rtsx_chip.h b/drivers/staging/rts5208/rtsx_chip.h
new file mode 100644
index 000000000..c295b1eed
--- /dev/null
+++ b/drivers/staging/rts5208/rtsx_chip.h
@@ -0,0 +1,991 @@
+/* Driver for Realtek PCI-Express card reader
+ * Header file
+ *
+ * Copyright(c) 2009-2013 Realtek Semiconductor Corp. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, 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, see <http://www.gnu.org/licenses/>.
+ *
+ * Author:
+ * Wei WANG (wei_wang@realsil.com.cn)
+ * Micky Ching (micky_ching@realsil.com.cn)
+ */
+
+#ifndef __REALTEK_RTSX_CHIP_H
+#define __REALTEK_RTSX_CHIP_H
+
+#include "rtsx.h"
+
+#define SUPPORT_CPRM
+#define SUPPORT_OCP
+#define SUPPORT_SDIO_ASPM
+#define SUPPORT_MAGIC_GATE
+#define SUPPORT_MSXC
+#define SUPPORT_SD_LOCK
+/* Hardware switch bus_ctl and cd_ctl automatically */
+#define HW_AUTO_SWITCH_SD_BUS
+/* Enable hardware interrupt write clear */
+#define HW_INT_WRITE_CLR
+/* #define LED_AUTO_BLINK */
+/* #define DISABLE_CARD_INT */
+
+#ifdef SUPPORT_MAGIC_GATE
+ /* Using NORMAL_WRITE instead of AUTO_WRITE to set ICV */
+ #define MG_SET_ICV_SLOW
+ /* HW may miss ERR/CMDNK signal when sampling INT status. */
+ #define MS_SAMPLE_INT_ERR
+ /* HW DO NOT support Wait_INT function during READ_BYTES
+ * transfer mode */
+ #define READ_BYTES_WAIT_INT
+#endif
+
+#ifdef SUPPORT_MSXC
+#define XC_POWERCLASS
+#define SUPPORT_PCGL_1P18
+#endif
+
+#ifndef LED_AUTO_BLINK
+#define REGULAR_BLINK
+#endif
+
+#define LED_BLINK_SPEED 5
+#define LED_TOGGLE_INTERVAL 6
+#define GPIO_TOGGLE_THRESHOLD 1024
+#define LED_GPIO 0
+
+#define POLLING_INTERVAL 30
+
+#define TRACE_ITEM_CNT 64
+
+#ifndef STATUS_SUCCESS
+#define STATUS_SUCCESS 0
+#endif
+#ifndef STATUS_FAIL
+#define STATUS_FAIL 1
+#endif
+#ifndef STATUS_TIMEDOUT
+#define STATUS_TIMEDOUT 2
+#endif
+#ifndef STATUS_NOMEM
+#define STATUS_NOMEM 3
+#endif
+#ifndef STATUS_READ_FAIL
+#define STATUS_READ_FAIL 4
+#endif
+#ifndef STATUS_WRITE_FAIL
+#define STATUS_WRITE_FAIL 5
+#endif
+#ifndef STATUS_ERROR
+#define STATUS_ERROR 10
+#endif
+
+#define PM_S1 1
+#define PM_S3 3
+
+/*
+ * Transport return codes
+ */
+
+#define TRANSPORT_GOOD 0 /* Transport good, command good */
+#define TRANSPORT_FAILED 1 /* Transport good, command failed */
+#define TRANSPORT_NO_SENSE 2 /* Command failed, no auto-sense */
+#define TRANSPORT_ERROR 3 /* Transport bad (i.e. device dead) */
+
+
+/*-----------------------------------
+ Start-Stop-Unit
+-----------------------------------*/
+#define STOP_MEDIUM 0x00 /* access disable */
+#define MAKE_MEDIUM_READY 0x01 /* access enable */
+#define UNLOAD_MEDIUM 0x02 /* unload */
+#define LOAD_MEDIUM 0x03 /* load */
+
+/*-----------------------------------
+ STANDARD_INQUIRY
+-----------------------------------*/
+#define QULIFIRE 0x00
+#define AENC_FNC 0x00
+#define TRML_IOP 0x00
+#define REL_ADR 0x00
+#define WBUS_32 0x00
+#define WBUS_16 0x00
+#define SYNC 0x00
+#define LINKED 0x00
+#define CMD_QUE 0x00
+#define SFT_RE 0x00
+
+#define VEN_ID_LEN 8 /* Vendor ID Length */
+#define PRDCT_ID_LEN 16 /* Product ID Length */
+#define PRDCT_REV_LEN 4 /* Product LOT Length */
+
+/* Dynamic flag definitions: used in set_bit() etc. */
+#define RTSX_FLIDX_TRANS_ACTIVE 18 /* 0x00040000 transfer is active */
+#define RTSX_FLIDX_ABORTING 20 /* 0x00100000 abort is in
+ * progress */
+#define RTSX_FLIDX_DISCONNECTING 21 /* 0x00200000 disconnect
+ * in progress */
+#define ABORTING_OR_DISCONNECTING ((1UL << US_FLIDX_ABORTING) | \
+ (1UL << US_FLIDX_DISCONNECTING))
+#define RTSX_FLIDX_RESETTING 22 /* 0x00400000 device reset
+ * in progress */
+#define RTSX_FLIDX_TIMED_OUT 23 /* 0x00800000 SCSI
+ * midlayer timed out */
+
+#define DRCT_ACCESS_DEV 0x00 /* Direct Access Device */
+#define RMB_DISC 0x80 /* The Device is Removable */
+#define ANSI_SCSI2 0x02 /* Based on ANSI-SCSI2 */
+
+#define SCSI 0x00 /* Interface ID */
+
+#define WRITE_PROTECTED_MEDIA 0x07
+
+/*---- sense key ----*/
+#define ILI 0x20 /* ILI bit is on */
+
+#define NO_SENSE 0x00 /* not exist sense key */
+#define RECOVER_ERR 0x01 /* Target/Logical unit is recoverd */
+#define NOT_READY 0x02 /* Logical unit is not ready */
+#define MEDIA_ERR 0x03 /* medium/data error */
+#define HARDWARE_ERR 0x04 /* hardware error */
+#define ILGAL_REQ 0x05 /* CDB/parameter/identify msg error */
+#define UNIT_ATTENTION 0x06 /* unit attention condition occur */
+#define DAT_PRTCT 0x07 /* read/write is desable */
+#define BLNC_CHK 0x08 /* find blank/DOF in read */
+ /* write to unblank area */
+#define CPY_ABRT 0x0a /* Copy/Compare/Copy&Verify illgal */
+#define ABRT_CMD 0x0b /* Target make the command in error */
+#define EQUAL 0x0c /* Search Data end with Equal */
+#define VLM_OVRFLW 0x0d /* Some data are left in buffer */
+#define MISCMP 0x0e /* find inequality */
+
+#define READ_ERR -1
+#define WRITE_ERR -2
+
+#define FIRST_RESET 0x01
+#define USED_EXIST 0x02
+
+/*-----------------------------------
+ SENSE_DATA
+-----------------------------------*/
+/*---- valid ----*/
+#define SENSE_VALID 0x80 /* Sense data is valid as SCSI2 */
+#define SENSE_INVALID 0x00 /* Sense data is invalid as SCSI2 */
+
+/*---- error code ----*/
+#define CUR_ERR 0x70 /* current error */
+#define DEF_ERR 0x71 /* specific command error */
+
+/*---- sense key Information ----*/
+#define SNSKEYINFO_LEN 3 /* length of sense key information */
+
+#define SKSV 0x80
+#define CDB_ILLEGAL 0x40
+#define DAT_ILLEGAL 0x00
+#define BPV 0x08
+#define BIT_ILLEGAL0 0 /* bit0 is illegal */
+#define BIT_ILLEGAL1 1 /* bit1 is illegal */
+#define BIT_ILLEGAL2 2 /* bit2 is illegal */
+#define BIT_ILLEGAL3 3 /* bit3 is illegal */
+#define BIT_ILLEGAL4 4 /* bit4 is illegal */
+#define BIT_ILLEGAL5 5 /* bit5 is illegal */
+#define BIT_ILLEGAL6 6 /* bit6 is illegal */
+#define BIT_ILLEGAL7 7 /* bit7 is illegal */
+
+/*---- ASC ----*/
+#define ASC_NO_INFO 0x00
+#define ASC_MISCMP 0x1d
+#define ASC_INVLD_CDB 0x24
+#define ASC_INVLD_PARA 0x26
+#define ASC_LU_NOT_READY 0x04
+#define ASC_WRITE_ERR 0x0c
+#define ASC_READ_ERR 0x11
+#define ASC_LOAD_EJCT_ERR 0x53
+#define ASC_MEDIA_NOT_PRESENT 0x3A
+#define ASC_MEDIA_CHANGED 0x28
+#define ASC_MEDIA_IN_PROCESS 0x04
+#define ASC_WRITE_PROTECT 0x27
+#define ASC_LUN_NOT_SUPPORTED 0x25
+
+/*---- ASQC ----*/
+#define ASCQ_NO_INFO 0x00
+#define ASCQ_MEDIA_IN_PROCESS 0x01
+#define ASCQ_MISCMP 0x00
+#define ASCQ_INVLD_CDB 0x00
+#define ASCQ_INVLD_PARA 0x02
+#define ASCQ_LU_NOT_READY 0x02
+#define ASCQ_WRITE_ERR 0x02
+#define ASCQ_READ_ERR 0x00
+#define ASCQ_LOAD_EJCT_ERR 0x00
+#define ASCQ_WRITE_PROTECT 0x00
+
+
+struct sense_data_t {
+ unsigned char err_code; /* error code */
+ /* bit7 : valid */
+ /* (1 : SCSI2) */
+ /* (0 : Vendor * specific) */
+ /* bit6-0 : error * code */
+ /* (0x70 : current * error) */
+ /* (0x71 : specific command error) */
+ unsigned char seg_no; /* segment No. */
+ unsigned char sense_key; /* byte5 : ILI */
+ /* bit3-0 : sense key */
+ unsigned char info[4]; /* information */
+ unsigned char ad_sense_len; /* additional sense data length */
+ unsigned char cmd_info[4]; /* command specific information */
+ unsigned char asc; /* ASC */
+ unsigned char ascq; /* ASCQ */
+ unsigned char rfu; /* FRU */
+ unsigned char sns_key_info[3];/* sense key specific information */
+};
+
+/* PCI Operation Register Address */
+#define RTSX_HCBAR 0x00
+#define RTSX_HCBCTLR 0x04
+#define RTSX_HDBAR 0x08
+#define RTSX_HDBCTLR 0x0C
+#define RTSX_HAIMR 0x10
+#define RTSX_BIPR 0x14
+#define RTSX_BIER 0x18
+
+/* Host command buffer control register */
+#define STOP_CMD (0x01 << 28)
+
+/* Host data buffer control register */
+#define SDMA_MODE 0x00
+#define ADMA_MODE (0x02 << 26)
+#define STOP_DMA (0x01 << 28)
+#define TRIG_DMA (0x01 << 31)
+
+/* Bus interrupt pending register */
+#define CMD_DONE_INT (1 << 31)
+#define DATA_DONE_INT (1 << 30)
+#define TRANS_OK_INT (1 << 29)
+#define TRANS_FAIL_INT (1 << 28)
+#define XD_INT (1 << 27)
+#define MS_INT (1 << 26)
+#define SD_INT (1 << 25)
+#define GPIO0_INT (1 << 24)
+#define OC_INT (1 << 23)
+#define SD_WRITE_PROTECT (1 << 19)
+#define XD_EXIST (1 << 18)
+#define MS_EXIST (1 << 17)
+#define SD_EXIST (1 << 16)
+#define DELINK_INT GPIO0_INT
+#define MS_OC_INT (1 << 23)
+#define SD_OC_INT (1 << 22)
+
+#define CARD_INT (XD_INT | MS_INT | SD_INT)
+#define NEED_COMPLETE_INT (DATA_DONE_INT | TRANS_OK_INT | TRANS_FAIL_INT)
+#define RTSX_INT (CMD_DONE_INT | NEED_COMPLETE_INT | CARD_INT | GPIO0_INT | OC_INT)
+
+#define CARD_EXIST (XD_EXIST | MS_EXIST | SD_EXIST)
+
+/* Bus interrupt enable register */
+#define CMD_DONE_INT_EN (1 << 31)
+#define DATA_DONE_INT_EN (1 << 30)
+#define TRANS_OK_INT_EN (1 << 29)
+#define TRANS_FAIL_INT_EN (1 << 28)
+#define XD_INT_EN (1 << 27)
+#define MS_INT_EN (1 << 26)
+#define SD_INT_EN (1 << 25)
+#define GPIO0_INT_EN (1 << 24)
+#define OC_INT_EN (1 << 23)
+#define DELINK_INT_EN GPIO0_INT_EN
+#define MS_OC_INT_EN (1 << 23)
+#define SD_OC_INT_EN (1 << 22)
+
+
+#define READ_REG_CMD 0
+#define WRITE_REG_CMD 1
+#define CHECK_REG_CMD 2
+
+#define HOST_TO_DEVICE 0
+#define DEVICE_TO_HOST 1
+
+
+#define RTSX_RESV_BUF_LEN 4096
+#define HOST_CMDS_BUF_LEN 1024
+#define HOST_SG_TBL_BUF_LEN (RTSX_RESV_BUF_LEN - HOST_CMDS_BUF_LEN)
+
+#define SD_NR 2
+#define MS_NR 3
+#define XD_NR 4
+#define SPI_NR 7
+#define SD_CARD (1 << SD_NR)
+#define MS_CARD (1 << MS_NR)
+#define XD_CARD (1 << XD_NR)
+#define SPI_CARD (1 << SPI_NR)
+
+#define MAX_ALLOWED_LUN_CNT 8
+
+#define XD_FREE_TABLE_CNT 1200
+#define MS_FREE_TABLE_CNT 512
+
+
+/* Bit Operation */
+#define SET_BIT(data, idx) ((data) |= 1 << (idx))
+#define CLR_BIT(data, idx) ((data) &= ~(1 << (idx)))
+#define CHK_BIT(data, idx) ((data) & (1 << (idx)))
+
+/* SG descriptor */
+#define SG_INT 0x04
+#define SG_END 0x02
+#define SG_VALID 0x01
+
+#define SG_NO_OP 0x00
+#define SG_TRANS_DATA (0x02 << 4)
+#define SG_LINK_DESC (0x03 << 4)
+
+struct rtsx_chip;
+
+typedef int (*card_rw_func)(struct scsi_cmnd *srb, struct rtsx_chip *chip,
+ u32 sec_addr, u16 sec_cnt);
+
+/* Supported Clock */
+enum card_clock {CLK_20 = 1, CLK_30, CLK_40, CLK_50, CLK_60,
+ CLK_80, CLK_100, CLK_120, CLK_150, CLK_200};
+
+enum RTSX_STAT {RTSX_STAT_INIT, RTSX_STAT_IDLE, RTSX_STAT_RUN, RTSX_STAT_SS,
+ RTSX_STAT_DELINK, RTSX_STAT_SUSPEND,
+ RTSX_STAT_ABORT, RTSX_STAT_DISCONNECT};
+enum IC_VER {IC_VER_AB, IC_VER_C = 2, IC_VER_D = 3};
+
+#define MAX_RESET_CNT 3
+
+/* For MS Card */
+#define MAX_DEFECTIVE_BLOCK 10
+
+struct zone_entry {
+ u16 *l2p_table;
+ u16 *free_table;
+ u16 defect_list[MAX_DEFECTIVE_BLOCK]; /* For MS card only */
+ int set_index;
+ int get_index;
+ int unused_blk_cnt;
+ int disable_count;
+ /* To indicate whether the L2P table of this zone has been built. */
+ int build_flag;
+};
+
+#define TYPE_SD 0x0000
+#define TYPE_MMC 0x0001
+
+/* TYPE_SD */
+#define SD_HS 0x0100
+#define SD_SDR50 0x0200
+#define SD_DDR50 0x0400
+#define SD_SDR104 0x0800
+#define SD_HCXC 0x1000
+
+/* TYPE_MMC */
+#define MMC_26M 0x0100
+#define MMC_52M 0x0200
+#define MMC_4BIT 0x0400
+#define MMC_8BIT 0x0800
+#define MMC_SECTOR_MODE 0x1000
+#define MMC_DDR52 0x2000
+
+/* SD card */
+#define CHK_SD(sd_card) (((sd_card)->sd_type & 0xFF) == TYPE_SD)
+#define CHK_SD_HS(sd_card) (CHK_SD(sd_card) && ((sd_card)->sd_type & SD_HS))
+#define CHK_SD_SDR50(sd_card) (CHK_SD(sd_card) && ((sd_card)->sd_type & SD_SDR50))
+#define CHK_SD_DDR50(sd_card) (CHK_SD(sd_card) && ((sd_card)->sd_type & SD_DDR50))
+#define CHK_SD_SDR104(sd_card) (CHK_SD(sd_card) && ((sd_card)->sd_type & SD_SDR104))
+#define CHK_SD_HCXC(sd_card) (CHK_SD(sd_card) && ((sd_card)->sd_type & SD_HCXC))
+#define CHK_SD_HC(sd_card) (CHK_SD_HCXC(sd_card) && ((sd_card)->capacity <= 0x4000000))
+#define CHK_SD_XC(sd_card) (CHK_SD_HCXC(sd_card) && ((sd_card)->capacity > 0x4000000))
+#define CHK_SD30_SPEED(sd_card) (CHK_SD_SDR50(sd_card) || CHK_SD_DDR50(sd_card) || CHK_SD_SDR104(sd_card))
+
+#define SET_SD(sd_card) ((sd_card)->sd_type = TYPE_SD)
+#define SET_SD_HS(sd_card) ((sd_card)->sd_type |= SD_HS)
+#define SET_SD_SDR50(sd_card) ((sd_card)->sd_type |= SD_SDR50)
+#define SET_SD_DDR50(sd_card) ((sd_card)->sd_type |= SD_DDR50)
+#define SET_SD_SDR104(sd_card) ((sd_card)->sd_type |= SD_SDR104)
+#define SET_SD_HCXC(sd_card) ((sd_card)->sd_type |= SD_HCXC)
+
+#define CLR_SD_HS(sd_card) ((sd_card)->sd_type &= ~SD_HS)
+#define CLR_SD_SDR50(sd_card) ((sd_card)->sd_type &= ~SD_SDR50)
+#define CLR_SD_DDR50(sd_card) ((sd_card)->sd_type &= ~SD_DDR50)
+#define CLR_SD_SDR104(sd_card) ((sd_card)->sd_type &= ~SD_SDR104)
+#define CLR_SD_HCXC(sd_card) ((sd_card)->sd_type &= ~SD_HCXC)
+
+/* MMC card */
+#define CHK_MMC(sd_card) (((sd_card)->sd_type & 0xFF) == TYPE_MMC)
+#define CHK_MMC_26M(sd_card) (CHK_MMC(sd_card) && ((sd_card)->sd_type & MMC_26M))
+#define CHK_MMC_52M(sd_card) (CHK_MMC(sd_card) && ((sd_card)->sd_type & MMC_52M))
+#define CHK_MMC_4BIT(sd_card) (CHK_MMC(sd_card) && ((sd_card)->sd_type & MMC_4BIT))
+#define CHK_MMC_8BIT(sd_card) (CHK_MMC(sd_card) && ((sd_card)->sd_type & MMC_8BIT))
+#define CHK_MMC_SECTOR_MODE(sd_card) (CHK_MMC(sd_card) && ((sd_card)->sd_type & MMC_SECTOR_MODE))
+#define CHK_MMC_DDR52(sd_card) (CHK_MMC(sd_card) && ((sd_card)->sd_type & MMC_DDR52))
+
+#define SET_MMC(sd_card) ((sd_card)->sd_type = TYPE_MMC)
+#define SET_MMC_26M(sd_card) ((sd_card)->sd_type |= MMC_26M)
+#define SET_MMC_52M(sd_card) ((sd_card)->sd_type |= MMC_52M)
+#define SET_MMC_4BIT(sd_card) ((sd_card)->sd_type |= MMC_4BIT)
+#define SET_MMC_8BIT(sd_card) ((sd_card)->sd_type |= MMC_8BIT)
+#define SET_MMC_SECTOR_MODE(sd_card) ((sd_card)->sd_type |= MMC_SECTOR_MODE)
+#define SET_MMC_DDR52(sd_card) ((sd_card)->sd_type |= MMC_DDR52)
+
+#define CLR_MMC_26M(sd_card) ((sd_card)->sd_type &= ~MMC_26M)
+#define CLR_MMC_52M(sd_card) ((sd_card)->sd_type &= ~MMC_52M)
+#define CLR_MMC_4BIT(sd_card) ((sd_card)->sd_type &= ~MMC_4BIT)
+#define CLR_MMC_8BIT(sd_card) ((sd_card)->sd_type &= ~MMC_8BIT)
+#define CLR_MMC_SECTOR_MODE(sd_card) ((sd_card)->sd_type &= ~MMC_SECTOR_MODE)
+#define CLR_MMC_DDR52(sd_card) ((sd_card)->sd_type &= ~MMC_DDR52)
+
+#define CHK_MMC_HS(sd_card) (CHK_MMC_52M(sd_card) && CHK_MMC_26M(sd_card))
+#define CLR_MMC_HS(sd_card) \
+do { \
+ CLR_MMC_DDR52(sd_card); \
+ CLR_MMC_52M(sd_card); \
+ CLR_MMC_26M(sd_card); \
+} while (0)
+
+#define SD_SUPPORT_CLASS_TEN 0x01
+#define SD_SUPPORT_1V8 0x02
+
+#define SD_SET_CLASS_TEN(sd_card) ((sd_card)->sd_setting |= SD_SUPPORT_CLASS_TEN)
+#define SD_CHK_CLASS_TEN(sd_card) ((sd_card)->sd_setting & SD_SUPPORT_CLASS_TEN)
+#define SD_CLR_CLASS_TEN(sd_card) ((sd_card)->sd_setting &= ~SD_SUPPORT_CLASS_TEN)
+#define SD_SET_1V8(sd_card) ((sd_card)->sd_setting |= SD_SUPPORT_1V8)
+#define SD_CHK_1V8(sd_card) ((sd_card)->sd_setting & SD_SUPPORT_1V8)
+#define SD_CLR_1V8(sd_card) ((sd_card)->sd_setting &= ~SD_SUPPORT_1V8)
+
+struct sd_info {
+ u16 sd_type;
+ u8 err_code;
+ u8 sd_data_buf_ready;
+ u32 sd_addr;
+ u32 capacity;
+
+ u8 raw_csd[16];
+ u8 raw_scr[8];
+
+ /* Sequential RW */
+ int seq_mode;
+ enum dma_data_direction pre_dir;
+ u32 pre_sec_addr;
+ u16 pre_sec_cnt;
+
+ int cleanup_counter;
+
+ int sd_clock;
+
+ int mmc_dont_switch_bus;
+
+#ifdef SUPPORT_CPRM
+ int sd_pass_thru_en;
+ int pre_cmd_err;
+ u8 last_rsp_type;
+ u8 rsp[17];
+#endif
+
+ u8 func_group1_mask;
+ u8 func_group2_mask;
+ u8 func_group3_mask;
+ u8 func_group4_mask;
+
+ u8 sd_switch_fail;
+ u8 sd_read_phase;
+
+#ifdef SUPPORT_SD_LOCK
+ u8 sd_lock_status;
+ u8 sd_erase_status;
+ u8 sd_lock_notify;
+#endif
+ int need_retune;
+};
+
+struct xd_delay_write_tag {
+ u32 old_phyblock;
+ u32 new_phyblock;
+ u32 logblock;
+ u8 pageoff;
+ u8 delay_write_flag;
+};
+
+struct xd_info {
+ u8 maker_code;
+ u8 device_code;
+ u8 block_shift;
+ u8 page_off;
+ u8 addr_cycle;
+ u16 cis_block;
+ u8 multi_flag;
+ u8 err_code;
+ u32 capacity;
+
+ struct zone_entry *zone;
+ int zone_cnt;
+
+ struct xd_delay_write_tag delay_write;
+ int cleanup_counter;
+
+ int xd_clock;
+};
+
+#define MODE_512_SEQ 0x01
+#define MODE_2K_SEQ 0x02
+
+#define TYPE_MS 0x0000
+#define TYPE_MSPRO 0x0001
+
+#define MS_4BIT 0x0100
+#define MS_8BIT 0x0200
+#define MS_HG 0x0400
+#define MS_XC 0x0800
+
+#define HG8BIT (MS_HG | MS_8BIT)
+
+#define CHK_MSPRO(ms_card) (((ms_card)->ms_type & 0xFF) == TYPE_MSPRO)
+#define CHK_HG8BIT(ms_card) (CHK_MSPRO(ms_card) && (((ms_card)->ms_type & HG8BIT) == HG8BIT))
+#define CHK_MSXC(ms_card) (CHK_MSPRO(ms_card) && ((ms_card)->ms_type & MS_XC))
+#define CHK_MSHG(ms_card) (CHK_MSPRO(ms_card) && ((ms_card)->ms_type & MS_HG))
+
+#define CHK_MS8BIT(ms_card) (((ms_card)->ms_type & MS_8BIT))
+#define CHK_MS4BIT(ms_card) (((ms_card)->ms_type & MS_4BIT))
+
+struct ms_delay_write_tag {
+ u16 old_phyblock;
+ u16 new_phyblock;
+ u16 logblock;
+ u8 pageoff;
+ u8 delay_write_flag;
+};
+
+struct ms_info {
+ u16 ms_type;
+ u8 block_shift;
+ u8 page_off;
+ u16 total_block;
+ u16 boot_block;
+ u32 capacity;
+
+ u8 check_ms_flow;
+ u8 switch_8bit_fail;
+ u8 err_code;
+
+ struct zone_entry *segment;
+ int segment_cnt;
+
+ int pro_under_formatting;
+ int format_status;
+ u16 progress;
+ u8 raw_sys_info[96];
+#ifdef SUPPORT_PCGL_1P18
+ u8 raw_model_name[48];
+#endif
+
+ u8 multi_flag;
+
+ /* Sequential RW */
+ u8 seq_mode;
+ enum dma_data_direction pre_dir;
+ u32 pre_sec_addr;
+ u16 pre_sec_cnt;
+ u32 total_sec_cnt;
+
+ struct ms_delay_write_tag delay_write;
+
+ int cleanup_counter;
+
+ int ms_clock;
+
+#ifdef SUPPORT_MAGIC_GATE
+ u8 magic_gate_id[16];
+ u8 mg_entry_num;
+ int mg_auth; /* flag to indicate authentication process */
+#endif
+};
+
+struct spi_info {
+ u8 use_clk;
+ u8 write_en;
+ u16 clk_div;
+ u8 err_code;
+
+ int spi_clock;
+};
+
+
+#ifdef _MSG_TRACE
+struct trace_msg_t {
+ u16 line;
+#define MSG_FUNC_LEN 64
+ char func[MSG_FUNC_LEN];
+#define MSG_FILE_LEN 32
+ char file[MSG_FILE_LEN];
+#define TIME_VAL_LEN 16
+ u8 timeval_buf[TIME_VAL_LEN];
+ u8 valid;
+};
+#endif
+
+/************/
+/* LUN mode */
+/************/
+/* Single LUN, support xD/SD/MS */
+#define DEFAULT_SINGLE 0
+/* 2 LUN mode, support SD/MS */
+#define SD_MS_2LUN 1
+/* Single LUN, but only support SD/MS, for Barossa LQFP */
+#define SD_MS_1LUN 2
+
+#define LAST_LUN_MODE 2
+
+/* Barossa package */
+#define QFN 0
+#define LQFP 1
+
+/******************/
+/* sd_ctl bit map */
+/******************/
+/* SD push point control, bit 0, 1 */
+#define SD_PUSH_POINT_CTL_MASK 0x03
+#define SD_PUSH_POINT_DELAY 0x01
+#define SD_PUSH_POINT_AUTO 0x02
+/* SD sample point control, bit 2, 3 */
+#define SD_SAMPLE_POINT_CTL_MASK 0x0C
+#define SD_SAMPLE_POINT_DELAY 0x04
+#define SD_SAMPLE_POINT_AUTO 0x08
+/* SD DDR Tx phase set by user, bit 4 */
+#define SD_DDR_TX_PHASE_SET_BY_USER 0x10
+/* MMC DDR Tx phase set by user, bit 5 */
+#define MMC_DDR_TX_PHASE_SET_BY_USER 0x20
+/* Support MMC DDR mode, bit 6 */
+#define SUPPORT_MMC_DDR_MODE 0x40
+/* Reset MMC at first */
+#define RESET_MMC_FIRST 0x80
+
+#define SEQ_START_CRITERIA 0x20
+
+/* MS Power Class En */
+#define POWER_CLASS_2_EN 0x02
+#define POWER_CLASS_1_EN 0x01
+
+#define MAX_SHOW_CNT 10
+#define MAX_RESET_CNT 3
+
+#define SDIO_EXIST 0x01
+#define SDIO_IGNORED 0x02
+
+#define CHK_SDIO_EXIST(chip) ((chip)->sdio_func_exist & SDIO_EXIST)
+#define SET_SDIO_EXIST(chip) ((chip)->sdio_func_exist |= SDIO_EXIST)
+#define CLR_SDIO_EXIST(chip) ((chip)->sdio_func_exist &= ~SDIO_EXIST)
+
+#define CHK_SDIO_IGNORED(chip) ((chip)->sdio_func_exist & SDIO_IGNORED)
+#define SET_SDIO_IGNORED(chip) ((chip)->sdio_func_exist |= SDIO_IGNORED)
+#define CLR_SDIO_IGNORED(chip) ((chip)->sdio_func_exist &= ~SDIO_IGNORED)
+
+struct rtsx_chip {
+ rtsx_dev_t *rtsx;
+
+ u32 int_reg; /* Bus interrupt pending register */
+ char max_lun;
+ void *context;
+
+ void *host_cmds_ptr; /* host commands buffer pointer */
+ dma_addr_t host_cmds_addr;
+ int ci; /* Command Index */
+
+ void *host_sg_tbl_ptr; /* SG descriptor table */
+ dma_addr_t host_sg_tbl_addr;
+ int sgi; /* SG entry index */
+
+ struct scsi_cmnd *srb; /* current srb */
+ struct sense_data_t sense_buffer[MAX_ALLOWED_LUN_CNT];
+
+ int cur_clk; /* current card clock */
+
+ /* Current accessed card */
+ int cur_card;
+
+ unsigned long need_release; /* need release bit map */
+ unsigned long need_reset; /* need reset
+ * bit map */
+ /* Flag to indicate that this card is just resumed from SS state,
+ * and need released before being resetted
+ */
+ unsigned long need_reinit;
+
+ int rw_need_retry;
+
+#ifdef SUPPORT_OCP
+ u32 ocp_int;
+ u8 ocp_stat;
+#endif
+
+ u8 card_exist; /* card exist bit map (physical exist) */
+ u8 card_ready; /* card ready bit map (reset successfully) */
+ u8 card_fail; /* card reset fail bit map */
+ u8 card_ejected; /* card ejected bit map */
+ u8 card_wp; /* card write protected bit map */
+
+ u8 lun_mc; /* flag to indicate whether to answer
+ * MediaChange */
+
+#ifndef LED_AUTO_BLINK
+ int led_toggle_counter;
+#endif
+
+ int sd_reset_counter;
+ int xd_reset_counter;
+ int ms_reset_counter;
+
+ /* card bus width */
+ u8 card_bus_width[MAX_ALLOWED_LUN_CNT];
+ /* card capacity */
+ u32 capacity[MAX_ALLOWED_LUN_CNT];
+ /* read/write card function pointer */
+ card_rw_func rw_card[MAX_ALLOWED_LUN_CNT];
+ /* read/write capacity, used for GPIO Toggle */
+ u32 rw_cap[MAX_ALLOWED_LUN_CNT];
+ /* card to lun mapping table */
+ u8 card2lun[32];
+ /* lun to card mapping table */
+ u8 lun2card[MAX_ALLOWED_LUN_CNT];
+
+ int rw_fail_cnt[MAX_ALLOWED_LUN_CNT];
+
+ int sd_show_cnt;
+ int xd_show_cnt;
+ int ms_show_cnt;
+
+ /* card information */
+ struct sd_info sd_card;
+ struct xd_info xd_card;
+ struct ms_info ms_card;
+
+ struct spi_info spi;
+
+#ifdef _MSG_TRACE
+ struct trace_msg_t trace_msg[TRACE_ITEM_CNT];
+ int msg_idx;
+#endif
+
+ int auto_delink_cnt;
+ int auto_delink_allowed;
+
+ int aspm_enabled;
+
+ int sdio_aspm;
+ int sdio_idle;
+ int sdio_counter;
+ u8 sdio_raw_data[12];
+
+ u8 sd_io;
+ u8 sd_int;
+
+ u8 rtsx_flag;
+
+ int ss_counter;
+ int idle_counter;
+ enum RTSX_STAT rtsx_stat;
+
+ u16 vendor_id;
+ u16 product_id;
+ u8 ic_version;
+
+ int driver_first_load;
+
+#ifdef HW_AUTO_SWITCH_SD_BUS
+ int sdio_in_charge;
+#endif
+
+ u8 aspm_level[2];
+
+ int chip_insert_with_sdio;
+
+ /* Options */
+
+ int adma_mode;
+
+ int auto_delink_en;
+ int ss_en;
+ u8 lun_mode;
+ u8 aspm_l0s_l1_en;
+
+ int power_down_in_ss;
+
+ int sdr104_en;
+ int ddr50_en;
+ int sdr50_en;
+
+ int baro_pkg;
+
+ int asic_code;
+ int phy_debug_mode;
+ int hw_bypass_sd;
+ int sdio_func_exist;
+ int aux_pwr_exist;
+ u8 ms_power_class_en;
+
+ int mspro_formatter_enable;
+
+ int remote_wakeup_en;
+
+ int ignore_sd;
+ int use_hw_setting;
+
+ int ss_idle_period;
+
+ int dynamic_aspm;
+
+ int fpga_sd_sdr104_clk;
+ int fpga_sd_ddr50_clk;
+ int fpga_sd_sdr50_clk;
+ int fpga_sd_hs_clk;
+ int fpga_mmc_52m_clk;
+ int fpga_ms_hg_clk;
+ int fpga_ms_4bit_clk;
+ int fpga_ms_1bit_clk;
+
+ int asic_sd_sdr104_clk;
+ int asic_sd_ddr50_clk;
+ int asic_sd_sdr50_clk;
+ int asic_sd_hs_clk;
+ int asic_mmc_52m_clk;
+ int asic_ms_hg_clk;
+ int asic_ms_4bit_clk;
+ int asic_ms_1bit_clk;
+
+ u8 ssc_depth_sd_sdr104;
+ u8 ssc_depth_sd_ddr50;
+ u8 ssc_depth_sd_sdr50;
+ u8 ssc_depth_sd_hs;
+ u8 ssc_depth_mmc_52m;
+ u8 ssc_depth_ms_hg;
+ u8 ssc_depth_ms_4bit;
+ u8 ssc_depth_low_speed;
+
+ u8 card_drive_sel;
+ u8 sd30_drive_sel_1v8;
+ u8 sd30_drive_sel_3v3;
+
+ u8 sd_400mA_ocp_thd;
+ u8 sd_800mA_ocp_thd;
+ u8 ms_ocp_thd;
+
+ int ssc_en;
+ int msi_en;
+
+ int xd_timeout;
+ int sd_timeout;
+ int ms_timeout;
+ int mspro_timeout;
+
+ int auto_power_down;
+
+ int sd_ddr_tx_phase;
+ int mmc_ddr_tx_phase;
+ int sd_default_tx_phase;
+ int sd_default_rx_phase;
+
+ int pmos_pwr_on_interval;
+ int sd_voltage_switch_delay;
+ int s3_pwr_off_delay;
+
+ int force_clkreq_0;
+ int ft2_fast_mode;
+
+ int do_delink_before_power_down;
+ int polling_config;
+ int sdio_retry_cnt;
+
+ int delink_stage1_step;
+ int delink_stage2_step;
+ int delink_stage3_step;
+
+ int auto_delink_in_L1;
+ int hp_watch_bios_hotplug;
+ int support_ms_8bit;
+
+ u8 blink_led;
+ u8 phy_voltage;
+ u8 max_payload;
+
+ u32 sd_speed_prior;
+ u32 sd_current_prior;
+ u32 sd_ctl;
+};
+
+static inline struct device *rtsx_dev(const struct rtsx_chip *chip)
+{
+ return &chip->rtsx->pci->dev;
+}
+
+#define rtsx_set_stat(chip, stat) \
+do { \
+ if ((stat) != RTSX_STAT_IDLE) { \
+ (chip)->idle_counter = 0; \
+ } \
+ (chip)->rtsx_stat = (enum RTSX_STAT)(stat); \
+} while (0)
+#define rtsx_get_stat(chip) ((chip)->rtsx_stat)
+#define rtsx_chk_stat(chip, stat) ((chip)->rtsx_stat == (stat))
+
+#define RTSX_SET_DELINK(chip) ((chip)->rtsx_flag |= 0x01)
+#define RTSX_CLR_DELINK(chip) ((chip)->rtsx_flag &= 0xFE)
+#define RTSX_TST_DELINK(chip) ((chip)->rtsx_flag & 0x01)
+
+#define CHECK_PID(chip, pid) ((chip)->product_id == (pid))
+#define CHECK_BARO_PKG(chip, pkg) ((chip)->baro_pkg == (pkg))
+#define CHECK_LUN_MODE(chip, mode) ((chip)->lun_mode == (mode))
+
+/* Power down control */
+#define SSC_PDCTL 0x01
+#define OC_PDCTL 0x02
+
+int rtsx_force_power_on(struct rtsx_chip *chip, u8 ctl);
+int rtsx_force_power_down(struct rtsx_chip *chip, u8 ctl);
+
+void rtsx_disable_card_int(struct rtsx_chip *chip);
+void rtsx_enable_card_int(struct rtsx_chip *chip);
+void rtsx_enable_bus_int(struct rtsx_chip *chip);
+void rtsx_disable_bus_int(struct rtsx_chip *chip);
+int rtsx_reset_chip(struct rtsx_chip *chip);
+int rtsx_init_chip(struct rtsx_chip *chip);
+void rtsx_release_chip(struct rtsx_chip *chip);
+void rtsx_polling_func(struct rtsx_chip *chip);
+void rtsx_undo_delink(struct rtsx_chip *chip);
+void rtsx_stop_cmd(struct rtsx_chip *chip, int card);
+int rtsx_write_register(struct rtsx_chip *chip, u16 addr, u8 mask, u8 data);
+int rtsx_read_register(struct rtsx_chip *chip, u16 addr, u8 *data);
+int rtsx_write_cfg_dw(struct rtsx_chip *chip,
+ u8 func_no, u16 addr, u32 mask, u32 val);
+int rtsx_read_cfg_dw(struct rtsx_chip *chip, u8 func_no, u16 addr, u32 *val);
+int rtsx_write_cfg_seq(struct rtsx_chip *chip,
+ u8 func, u16 addr, u8 *buf, int len);
+int rtsx_read_cfg_seq(struct rtsx_chip *chip,
+ u8 func, u16 addr, u8 *buf, int len);
+int rtsx_write_phy_register(struct rtsx_chip *chip, u8 addr, u16 val);
+int rtsx_read_phy_register(struct rtsx_chip *chip, u8 addr, u16 *val);
+int rtsx_read_efuse(struct rtsx_chip *chip, u8 addr, u8 *val);
+int rtsx_write_efuse(struct rtsx_chip *chip, u8 addr, u8 val);
+int rtsx_clr_phy_reg_bit(struct rtsx_chip *chip, u8 reg, u8 bit);
+int rtsx_set_phy_reg_bit(struct rtsx_chip *chip, u8 reg, u8 bit);
+int rtsx_check_link_ready(struct rtsx_chip *chip);
+void rtsx_enter_ss(struct rtsx_chip *chip);
+void rtsx_exit_ss(struct rtsx_chip *chip);
+int rtsx_pre_handle_interrupt(struct rtsx_chip *chip);
+void rtsx_enter_L1(struct rtsx_chip *chip);
+void rtsx_exit_L1(struct rtsx_chip *chip);
+void rtsx_do_before_power_down(struct rtsx_chip *chip, int pm_stat);
+void rtsx_enable_aspm(struct rtsx_chip *chip);
+void rtsx_disable_aspm(struct rtsx_chip *chip);
+int rtsx_read_ppbuf(struct rtsx_chip *chip, u8 *buf, int buf_len);
+int rtsx_write_ppbuf(struct rtsx_chip *chip, u8 *buf, int buf_len);
+int rtsx_check_chip_exist(struct rtsx_chip *chip);
+
+#endif /* __REALTEK_RTSX_CHIP_H */
diff --git a/drivers/staging/rts5208/rtsx_scsi.c b/drivers/staging/rts5208/rtsx_scsi.c
new file mode 100644
index 000000000..8a5d6a8e7
--- /dev/null
+++ b/drivers/staging/rts5208/rtsx_scsi.c
@@ -0,0 +1,3544 @@
+/* Driver for Realtek PCI-Express card reader
+ *
+ * Copyright(c) 2009-2013 Realtek Semiconductor Corp. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, 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, see <http://www.gnu.org/licenses/>.
+ *
+ * Author:
+ * Wei WANG (wei_wang@realsil.com.cn)
+ * Micky Ching (micky_ching@realsil.com.cn)
+ */
+
+#include <linux/blkdev.h>
+#include <linux/kthread.h>
+#include <linux/sched.h>
+#include <linux/vmalloc.h>
+
+#include "rtsx.h"
+#include "sd.h"
+#include "ms.h"
+#include "spi.h"
+
+void scsi_show_command(struct rtsx_chip *chip)
+{
+ struct scsi_cmnd *srb = chip->srb;
+ char *what = NULL;
+ bool unknown_cmd = false;
+ int len;
+
+ switch (srb->cmnd[0]) {
+ case TEST_UNIT_READY:
+ what = "TEST_UNIT_READY";
+ break;
+ case REZERO_UNIT:
+ what = "REZERO_UNIT";
+ break;
+ case REQUEST_SENSE:
+ what = "REQUEST_SENSE";
+ break;
+ case FORMAT_UNIT:
+ what = "FORMAT_UNIT";
+ break;
+ case READ_BLOCK_LIMITS:
+ what = "READ_BLOCK_LIMITS";
+ break;
+ case REASSIGN_BLOCKS:
+ what = "REASSIGN_BLOCKS";
+ break;
+ case READ_6:
+ what = "READ_6";
+ break;
+ case WRITE_6:
+ what = "WRITE_6";
+ break;
+ case SEEK_6:
+ what = "SEEK_6";
+ break;
+ case READ_REVERSE:
+ what = "READ_REVERSE";
+ break;
+ case WRITE_FILEMARKS:
+ what = "WRITE_FILEMARKS";
+ break;
+ case SPACE:
+ what = "SPACE";
+ break;
+ case INQUIRY:
+ what = "INQUIRY";
+ break;
+ case RECOVER_BUFFERED_DATA:
+ what = "RECOVER_BUFFERED_DATA";
+ break;
+ case MODE_SELECT:
+ what = "MODE_SELECT";
+ break;
+ case RESERVE:
+ what = "RESERVE";
+ break;
+ case RELEASE:
+ what = "RELEASE";
+ break;
+ case COPY:
+ what = "COPY";
+ break;
+ case ERASE:
+ what = "ERASE";
+ break;
+ case MODE_SENSE:
+ what = "MODE_SENSE";
+ break;
+ case START_STOP:
+ what = "START_STOP";
+ break;
+ case RECEIVE_DIAGNOSTIC:
+ what = "RECEIVE_DIAGNOSTIC";
+ break;
+ case SEND_DIAGNOSTIC:
+ what = "SEND_DIAGNOSTIC";
+ break;
+ case ALLOW_MEDIUM_REMOVAL:
+ what = "ALLOW_MEDIUM_REMOVAL";
+ break;
+ case SET_WINDOW:
+ what = "SET_WINDOW";
+ break;
+ case READ_CAPACITY:
+ what = "READ_CAPACITY";
+ break;
+ case READ_10:
+ what = "READ_10";
+ break;
+ case WRITE_10:
+ what = "WRITE_10";
+ break;
+ case SEEK_10:
+ what = "SEEK_10";
+ break;
+ case WRITE_VERIFY:
+ what = "WRITE_VERIFY";
+ break;
+ case VERIFY:
+ what = "VERIFY";
+ break;
+ case SEARCH_HIGH:
+ what = "SEARCH_HIGH";
+ break;
+ case SEARCH_EQUAL:
+ what = "SEARCH_EQUAL";
+ break;
+ case SEARCH_LOW:
+ what = "SEARCH_LOW";
+ break;
+ case SET_LIMITS:
+ what = "SET_LIMITS";
+ break;
+ case READ_POSITION:
+ what = "READ_POSITION";
+ break;
+ case SYNCHRONIZE_CACHE:
+ what = "SYNCHRONIZE_CACHE";
+ break;
+ case LOCK_UNLOCK_CACHE:
+ what = "LOCK_UNLOCK_CACHE";
+ break;
+ case READ_DEFECT_DATA:
+ what = "READ_DEFECT_DATA";
+ break;
+ case MEDIUM_SCAN:
+ what = "MEDIUM_SCAN";
+ break;
+ case COMPARE:
+ what = "COMPARE";
+ break;
+ case COPY_VERIFY:
+ what = "COPY_VERIFY";
+ break;
+ case WRITE_BUFFER:
+ what = "WRITE_BUFFER";
+ break;
+ case READ_BUFFER:
+ what = "READ_BUFFER";
+ break;
+ case UPDATE_BLOCK:
+ what = "UPDATE_BLOCK";
+ break;
+ case READ_LONG:
+ what = "READ_LONG";
+ break;
+ case WRITE_LONG:
+ what = "WRITE_LONG";
+ break;
+ case CHANGE_DEFINITION:
+ what = "CHANGE_DEFINITION";
+ break;
+ case WRITE_SAME:
+ what = "WRITE_SAME";
+ break;
+ case GPCMD_READ_SUBCHANNEL:
+ what = "READ SUBCHANNEL";
+ break;
+ case READ_TOC:
+ what = "READ_TOC";
+ break;
+ case GPCMD_READ_HEADER:
+ what = "READ HEADER";
+ break;
+ case GPCMD_PLAY_AUDIO_10:
+ what = "PLAY AUDIO (10)";
+ break;
+ case GPCMD_PLAY_AUDIO_MSF:
+ what = "PLAY AUDIO MSF";
+ break;
+ case GPCMD_GET_EVENT_STATUS_NOTIFICATION:
+ what = "GET EVENT/STATUS NOTIFICATION";
+ break;
+ case GPCMD_PAUSE_RESUME:
+ what = "PAUSE/RESUME";
+ break;
+ case LOG_SELECT:
+ what = "LOG_SELECT";
+ break;
+ case LOG_SENSE:
+ what = "LOG_SENSE";
+ break;
+ case GPCMD_STOP_PLAY_SCAN:
+ what = "STOP PLAY/SCAN";
+ break;
+ case GPCMD_READ_DISC_INFO:
+ what = "READ DISC INFORMATION";
+ break;
+ case GPCMD_READ_TRACK_RZONE_INFO:
+ what = "READ TRACK INFORMATION";
+ break;
+ case GPCMD_RESERVE_RZONE_TRACK:
+ what = "RESERVE TRACK";
+ break;
+ case GPCMD_SEND_OPC:
+ what = "SEND OPC";
+ break;
+ case MODE_SELECT_10:
+ what = "MODE_SELECT_10";
+ break;
+ case GPCMD_REPAIR_RZONE_TRACK:
+ what = "REPAIR TRACK";
+ break;
+ case 0x59:
+ what = "READ MASTER CUE";
+ break;
+ case MODE_SENSE_10:
+ what = "MODE_SENSE_10";
+ break;
+ case GPCMD_CLOSE_TRACK:
+ what = "CLOSE TRACK/SESSION";
+ break;
+ case 0x5C:
+ what = "READ BUFFER CAPACITY";
+ break;
+ case 0x5D:
+ what = "SEND CUE SHEET";
+ break;
+ case GPCMD_BLANK:
+ what = "BLANK";
+ break;
+ case REPORT_LUNS:
+ what = "REPORT LUNS";
+ break;
+ case MOVE_MEDIUM:
+ what = "MOVE_MEDIUM or PLAY AUDIO (12)";
+ break;
+ case READ_12:
+ what = "READ_12";
+ break;
+ case WRITE_12:
+ what = "WRITE_12";
+ break;
+ case WRITE_VERIFY_12:
+ what = "WRITE_VERIFY_12";
+ break;
+ case SEARCH_HIGH_12:
+ what = "SEARCH_HIGH_12";
+ break;
+ case SEARCH_EQUAL_12:
+ what = "SEARCH_EQUAL_12";
+ break;
+ case SEARCH_LOW_12:
+ what = "SEARCH_LOW_12";
+ break;
+ case SEND_VOLUME_TAG:
+ what = "SEND_VOLUME_TAG";
+ break;
+ case READ_ELEMENT_STATUS:
+ what = "READ_ELEMENT_STATUS";
+ break;
+ case GPCMD_READ_CD_MSF:
+ what = "READ CD MSF";
+ break;
+ case GPCMD_SCAN:
+ what = "SCAN";
+ break;
+ case GPCMD_SET_SPEED:
+ what = "SET CD SPEED";
+ break;
+ case GPCMD_MECHANISM_STATUS:
+ what = "MECHANISM STATUS";
+ break;
+ case GPCMD_READ_CD:
+ what = "READ CD";
+ break;
+ case 0xE1:
+ what = "WRITE CONTINUE";
+ break;
+ case WRITE_LONG_2:
+ what = "WRITE_LONG_2";
+ break;
+ case VENDOR_CMND:
+ what = "Realtek's vendor command";
+ break;
+ default:
+ what = "(unknown command)";
+ unknown_cmd = true;
+ break;
+ }
+
+ if (srb->cmnd[0] != TEST_UNIT_READY)
+ dev_dbg(rtsx_dev(chip), "Command %s (%d bytes)\n",
+ what, srb->cmd_len);
+
+ if (unknown_cmd) {
+ len = min_t(unsigned short, srb->cmd_len, 16);
+ dev_dbg(rtsx_dev(chip), "%*ph\n", len, srb->cmnd);
+ }
+}
+
+void set_sense_type(struct rtsx_chip *chip, unsigned int lun, int sense_type)
+{
+ switch (sense_type) {
+ case SENSE_TYPE_MEDIA_CHANGE:
+ set_sense_data(chip, lun, CUR_ERR, 0x06, 0, 0x28, 0, 0, 0);
+ break;
+
+ case SENSE_TYPE_MEDIA_NOT_PRESENT:
+ set_sense_data(chip, lun, CUR_ERR, 0x02, 0, 0x3A, 0, 0, 0);
+ break;
+
+ case SENSE_TYPE_MEDIA_LBA_OVER_RANGE:
+ set_sense_data(chip, lun, CUR_ERR, 0x05, 0, 0x21, 0, 0, 0);
+ break;
+
+ case SENSE_TYPE_MEDIA_LUN_NOT_SUPPORT:
+ set_sense_data(chip, lun, CUR_ERR, 0x05, 0, 0x25, 0, 0, 0);
+ break;
+
+ case SENSE_TYPE_MEDIA_WRITE_PROTECT:
+ set_sense_data(chip, lun, CUR_ERR, 0x07, 0, 0x27, 0, 0, 0);
+ break;
+
+ case SENSE_TYPE_MEDIA_UNRECOVER_READ_ERR:
+ set_sense_data(chip, lun, CUR_ERR, 0x03, 0, 0x11, 0, 0, 0);
+ break;
+
+ case SENSE_TYPE_MEDIA_WRITE_ERR:
+ set_sense_data(chip, lun, CUR_ERR, 0x03, 0, 0x0C, 0x02, 0, 0);
+ break;
+
+ case SENSE_TYPE_MEDIA_INVALID_CMD_FIELD:
+ set_sense_data(chip, lun, CUR_ERR, ILGAL_REQ, 0,
+ ASC_INVLD_CDB, ASCQ_INVLD_CDB, CDB_ILLEGAL, 1);
+ break;
+
+ case SENSE_TYPE_FORMAT_IN_PROGRESS:
+ set_sense_data(chip, lun, CUR_ERR, 0x02, 0, 0x04, 0x04, 0, 0);
+ break;
+
+ case SENSE_TYPE_FORMAT_CMD_FAILED:
+ set_sense_data(chip, lun, CUR_ERR, 0x03, 0, 0x31, 0x01, 0, 0);
+ break;
+
+#ifdef SUPPORT_MAGIC_GATE
+ case SENSE_TYPE_MG_KEY_FAIL_NOT_ESTAB:
+ set_sense_data(chip, lun, CUR_ERR, 0x05, 0, 0x6F, 0x02, 0, 0);
+ break;
+
+ case SENSE_TYPE_MG_KEY_FAIL_NOT_AUTHEN:
+ set_sense_data(chip, lun, CUR_ERR, 0x05, 0, 0x6F, 0x00, 0, 0);
+ break;
+
+ case SENSE_TYPE_MG_INCOMPATIBLE_MEDIUM:
+ set_sense_data(chip, lun, CUR_ERR, 0x02, 0, 0x30, 0x00, 0, 0);
+ break;
+
+ case SENSE_TYPE_MG_WRITE_ERR:
+ set_sense_data(chip, lun, CUR_ERR, 0x03, 0, 0x0C, 0x00, 0, 0);
+ break;
+#endif
+
+#ifdef SUPPORT_SD_LOCK
+ case SENSE_TYPE_MEDIA_READ_FORBIDDEN:
+ set_sense_data(chip, lun, CUR_ERR, 0x07, 0, 0x11, 0x13, 0, 0);
+ break;
+#endif
+
+ case SENSE_TYPE_NO_SENSE:
+ default:
+ set_sense_data(chip, lun, CUR_ERR, 0, 0, 0, 0, 0, 0);
+ break;
+ }
+}
+
+void set_sense_data(struct rtsx_chip *chip, unsigned int lun, u8 err_code,
+ u8 sense_key, u32 info, u8 asc, u8 ascq, u8 sns_key_info0,
+ u16 sns_key_info1)
+{
+ struct sense_data_t *sense = &(chip->sense_buffer[lun]);
+
+ sense->err_code = err_code;
+ sense->sense_key = sense_key;
+ sense->info[0] = (u8)(info >> 24);
+ sense->info[1] = (u8)(info >> 16);
+ sense->info[2] = (u8)(info >> 8);
+ sense->info[3] = (u8)info;
+
+ sense->ad_sense_len = sizeof(struct sense_data_t) - 8;
+ sense->asc = asc;
+ sense->ascq = ascq;
+ if (sns_key_info0 != 0) {
+ sense->sns_key_info[0] = SKSV | sns_key_info0;
+ sense->sns_key_info[1] = (sns_key_info1 & 0xf0) >> 8;
+ sense->sns_key_info[2] = sns_key_info1 & 0x0f;
+ }
+}
+
+static int test_unit_ready(struct scsi_cmnd *srb, struct rtsx_chip *chip)
+{
+ unsigned int lun = SCSI_LUN(srb);
+
+ if (!check_card_ready(chip, lun)) {
+ set_sense_type(chip, lun, SENSE_TYPE_MEDIA_NOT_PRESENT);
+ return TRANSPORT_FAILED;
+ }
+
+ if (!(CHK_BIT(chip->lun_mc, lun))) {
+ SET_BIT(chip->lun_mc, lun);
+ set_sense_type(chip, lun, SENSE_TYPE_MEDIA_CHANGE);
+ return TRANSPORT_FAILED;
+ }
+
+#ifdef SUPPORT_SD_LOCK
+ if (get_lun_card(chip, SCSI_LUN(srb)) == SD_CARD) {
+ struct sd_info *sd_card = &(chip->sd_card);
+
+ if (sd_card->sd_lock_notify) {
+ sd_card->sd_lock_notify = 0;
+ set_sense_type(chip, lun, SENSE_TYPE_MEDIA_CHANGE);
+ return TRANSPORT_FAILED;
+ } else if (sd_card->sd_lock_status & SD_LOCKED) {
+ set_sense_type(chip, lun,
+ SENSE_TYPE_MEDIA_READ_FORBIDDEN);
+ return TRANSPORT_FAILED;
+ }
+ }
+#endif
+
+ return TRANSPORT_GOOD;
+}
+
+static unsigned char formatter_inquiry_str[20] = {
+ 'M', 'E', 'M', 'O', 'R', 'Y', 'S', 'T', 'I', 'C', 'K',
+#ifdef SUPPORT_MAGIC_GATE
+ '-', 'M', 'G', /* Byte[47:49] */
+#else
+ 0x20, 0x20, 0x20, /* Byte[47:49] */
+#endif
+
+#ifdef SUPPORT_MAGIC_GATE
+ 0x0B, /* Byte[50]: MG, MS, MSPro, MSXC */
+#else
+ 0x09, /* Byte[50]: MS, MSPro, MSXC */
+#endif
+ 0x00, /* Byte[51]: Category Specific Commands */
+ 0x00, /* Byte[52]: Access Control and feature */
+ 0x20, 0x20, 0x20, /* Byte[53:55] */
+};
+
+static int inquiry(struct scsi_cmnd *srb, struct rtsx_chip *chip)
+{
+ unsigned int lun = SCSI_LUN(srb);
+ char *inquiry_default = (char *)"Generic-xD/SD/M.S. 1.00 ";
+ char *inquiry_sdms = (char *)"Generic-SD/MemoryStick 1.00 ";
+ char *inquiry_sd = (char *)"Generic-SD/MMC 1.00 ";
+ char *inquiry_ms = (char *)"Generic-MemoryStick 1.00 ";
+ char *inquiry_string;
+ unsigned char sendbytes;
+ unsigned char *buf;
+ u8 card = get_lun_card(chip, lun);
+ bool pro_formatter_flag = false;
+ unsigned char inquiry_buf[] = {
+ QULIFIRE|DRCT_ACCESS_DEV,
+ RMB_DISC|0x0D,
+ 0x00,
+ 0x01,
+ 0x1f,
+ 0x02,
+ 0,
+ REL_ADR|WBUS_32|WBUS_16|SYNC|LINKED|CMD_QUE|SFT_RE,
+ };
+
+ if (CHECK_LUN_MODE(chip, SD_MS_2LUN)) {
+ if (chip->lun2card[lun] == SD_CARD)
+ inquiry_string = inquiry_sd;
+ else
+ inquiry_string = inquiry_ms;
+
+ } else if (CHECK_LUN_MODE(chip, SD_MS_1LUN)) {
+ inquiry_string = inquiry_sdms;
+ } else {
+ inquiry_string = inquiry_default;
+ }
+
+ buf = vmalloc(scsi_bufflen(srb));
+ if (buf == NULL) {
+ rtsx_trace(chip);
+ return TRANSPORT_ERROR;
+ }
+
+#ifdef SUPPORT_MAGIC_GATE
+ if ((chip->mspro_formatter_enable) &&
+ (chip->lun2card[lun] & MS_CARD))
+#else
+ if (chip->mspro_formatter_enable)
+#endif
+ if (!card || (card == MS_CARD))
+ pro_formatter_flag = true;
+
+ if (pro_formatter_flag) {
+ if (scsi_bufflen(srb) < 56)
+ sendbytes = (unsigned char)(scsi_bufflen(srb));
+ else
+ sendbytes = 56;
+
+ } else {
+ if (scsi_bufflen(srb) < 36)
+ sendbytes = (unsigned char)(scsi_bufflen(srb));
+ else
+ sendbytes = 36;
+ }
+
+ if (sendbytes > 8) {
+ memcpy(buf, inquiry_buf, 8);
+ memcpy(buf + 8, inquiry_string, sendbytes - 8);
+ if (pro_formatter_flag) {
+ /* Additional Length */
+ buf[4] = 0x33;
+ }
+ } else {
+ memcpy(buf, inquiry_buf, sendbytes);
+ }
+
+ if (pro_formatter_flag) {
+ if (sendbytes > 36)
+ memcpy(buf + 36, formatter_inquiry_str, sendbytes - 36);
+ }
+
+ scsi_set_resid(srb, 0);
+
+ rtsx_stor_set_xfer_buf(buf, scsi_bufflen(srb), srb);
+ vfree(buf);
+
+ return TRANSPORT_GOOD;
+}
+
+
+static int start_stop_unit(struct scsi_cmnd *srb, struct rtsx_chip *chip)
+{
+ unsigned int lun = SCSI_LUN(srb);
+
+ scsi_set_resid(srb, scsi_bufflen(srb));
+
+ if (srb->cmnd[1] == 1)
+ return TRANSPORT_GOOD;
+
+ switch (srb->cmnd[0x4]) {
+ case STOP_MEDIUM:
+ /* Media disabled */
+ return TRANSPORT_GOOD;
+
+ case UNLOAD_MEDIUM:
+ /* Media shall be unload */
+ if (check_card_ready(chip, lun))
+ eject_card(chip, lun);
+ return TRANSPORT_GOOD;
+
+ case MAKE_MEDIUM_READY:
+ case LOAD_MEDIUM:
+ if (check_card_ready(chip, lun))
+ return TRANSPORT_GOOD;
+ set_sense_type(chip, lun, SENSE_TYPE_MEDIA_NOT_PRESENT);
+ rtsx_trace(chip);
+ return TRANSPORT_FAILED;
+
+ break;
+ }
+
+ rtsx_trace(chip);
+ return TRANSPORT_ERROR;
+}
+
+
+static int allow_medium_removal(struct scsi_cmnd *srb, struct rtsx_chip *chip)
+{
+ int prevent;
+
+ prevent = srb->cmnd[4] & 0x1;
+
+ scsi_set_resid(srb, 0);
+
+ if (prevent) {
+ set_sense_type(chip, SCSI_LUN(srb),
+ SENSE_TYPE_MEDIA_INVALID_CMD_FIELD);
+ rtsx_trace(chip);
+ return TRANSPORT_FAILED;
+ }
+
+ return TRANSPORT_GOOD;
+}
+
+
+static int request_sense(struct scsi_cmnd *srb, struct rtsx_chip *chip)
+{
+ struct sense_data_t *sense;
+ unsigned int lun = SCSI_LUN(srb);
+ struct ms_info *ms_card = &(chip->ms_card);
+ unsigned char *tmp, *buf;
+
+ sense = &(chip->sense_buffer[lun]);
+
+ if ((get_lun_card(chip, lun) == MS_CARD) &&
+ ms_card->pro_under_formatting) {
+ if (ms_card->format_status == FORMAT_SUCCESS) {
+ set_sense_type(chip, lun, SENSE_TYPE_NO_SENSE);
+ ms_card->pro_under_formatting = 0;
+ ms_card->progress = 0;
+ } else if (ms_card->format_status == FORMAT_IN_PROGRESS) {
+ /* Logical Unit Not Ready Format in Progress */
+ set_sense_data(chip, lun, CUR_ERR, 0x02, 0, 0x04, 0x04,
+ 0, (u16)(ms_card->progress));
+ } else {
+ /* Format Command Failed */
+ set_sense_type(chip, lun, SENSE_TYPE_FORMAT_CMD_FAILED);
+ ms_card->pro_under_formatting = 0;
+ ms_card->progress = 0;
+ }
+
+ rtsx_set_stat(chip, RTSX_STAT_RUN);
+ }
+
+ buf = vmalloc(scsi_bufflen(srb));
+ if (buf == NULL) {
+ rtsx_trace(chip);
+ return TRANSPORT_ERROR;
+ }
+
+ tmp = (unsigned char *)sense;
+ memcpy(buf, tmp, scsi_bufflen(srb));
+
+ rtsx_stor_set_xfer_buf(buf, scsi_bufflen(srb), srb);
+ vfree(buf);
+
+ scsi_set_resid(srb, 0);
+ /* Reset Sense Data */
+ set_sense_type(chip, lun, SENSE_TYPE_NO_SENSE);
+ return TRANSPORT_GOOD;
+}
+
+static void ms_mode_sense(struct rtsx_chip *chip, u8 cmd,
+ int lun, u8 *buf, int buf_len)
+{
+ struct ms_info *ms_card = &(chip->ms_card);
+ int sys_info_offset;
+ int data_size = buf_len;
+ bool support_format = false;
+ int i = 0;
+
+ if (cmd == MODE_SENSE) {
+ sys_info_offset = 8;
+ if (data_size > 0x68)
+ data_size = 0x68;
+
+ buf[i++] = 0x67; /* Mode Data Length */
+ } else {
+ sys_info_offset = 12;
+ if (data_size > 0x6C)
+ data_size = 0x6C;
+
+ buf[i++] = 0x00; /* Mode Data Length (MSB) */
+ buf[i++] = 0x6A; /* Mode Data Length (LSB) */
+ }
+
+ /* Medium Type Code */
+ if (check_card_ready(chip, lun)) {
+ if (CHK_MSXC(ms_card)) {
+ support_format = true;
+ buf[i++] = 0x40;
+ } else if (CHK_MSPRO(ms_card)) {
+ support_format = true;
+ buf[i++] = 0x20;
+ } else {
+ buf[i++] = 0x10;
+ }
+
+ /* WP */
+ if (check_card_wp(chip, lun))
+ buf[i++] = 0x80;
+ else
+ buf[i++] = 0x00;
+
+ } else {
+ buf[i++] = 0x00; /* MediaType */
+ buf[i++] = 0x00; /* WP */
+ }
+
+ buf[i++] = 0x00; /* Reserved */
+
+ if (cmd == MODE_SENSE_10) {
+ buf[i++] = 0x00; /* Reserved */
+ buf[i++] = 0x00; /* Block descriptor length(MSB) */
+ buf[i++] = 0x00; /* Block descriptor length(LSB) */
+
+ /* The Following Data is the content of "Page 0x20" */
+ if (data_size >= 9)
+ buf[i++] = 0x20; /* Page Code */
+ if (data_size >= 10)
+ buf[i++] = 0x62; /* Page Length */
+ if (data_size >= 11)
+ buf[i++] = 0x00; /* No Access Control */
+ if (data_size >= 12) {
+ if (support_format)
+ buf[i++] = 0xC0; /* SF, SGM */
+ else
+ buf[i++] = 0x00;
+ }
+ } else {
+ /* The Following Data is the content of "Page 0x20" */
+ if (data_size >= 5)
+ buf[i++] = 0x20; /* Page Code */
+ if (data_size >= 6)
+ buf[i++] = 0x62; /* Page Length */
+ if (data_size >= 7)
+ buf[i++] = 0x00; /* No Access Control */
+ if (data_size >= 8) {
+ if (support_format)
+ buf[i++] = 0xC0; /* SF, SGM */
+ else
+ buf[i++] = 0x00;
+ }
+ }
+
+ if (data_size > sys_info_offset) {
+ /* 96 Bytes Attribute Data */
+ int len = data_size - sys_info_offset;
+
+ len = (len < 96) ? len : 96;
+
+ memcpy(buf + sys_info_offset, ms_card->raw_sys_info, len);
+ }
+}
+
+static int mode_sense(struct scsi_cmnd *srb, struct rtsx_chip *chip)
+{
+ unsigned int lun = SCSI_LUN(srb);
+ unsigned int dataSize;
+ int status;
+ bool pro_formatter_flag;
+ unsigned char pageCode, *buf;
+ u8 card = get_lun_card(chip, lun);
+
+#ifndef SUPPORT_MAGIC_GATE
+ if (!check_card_ready(chip, lun)) {
+ set_sense_type(chip, lun, SENSE_TYPE_MEDIA_NOT_PRESENT);
+ scsi_set_resid(srb, scsi_bufflen(srb));
+ rtsx_trace(chip);
+ return TRANSPORT_FAILED;
+ }
+#endif
+
+ pro_formatter_flag = false;
+ dataSize = 8;
+#ifdef SUPPORT_MAGIC_GATE
+ if ((chip->lun2card[lun] & MS_CARD)) {
+ if (!card || (card == MS_CARD)) {
+ dataSize = 108;
+ if (chip->mspro_formatter_enable)
+ pro_formatter_flag = true;
+ }
+ }
+#else
+ if (card == MS_CARD) {
+ if (chip->mspro_formatter_enable) {
+ pro_formatter_flag = true;
+ dataSize = 108;
+ }
+ }
+#endif
+
+ buf = kmalloc(dataSize, GFP_KERNEL);
+ if (buf == NULL) {
+ rtsx_trace(chip);
+ return TRANSPORT_ERROR;
+ }
+
+ pageCode = srb->cmnd[2] & 0x3f;
+
+ if ((pageCode == 0x3F) || (pageCode == 0x1C) ||
+ (pageCode == 0x00) ||
+ (pro_formatter_flag && (pageCode == 0x20))) {
+ if (srb->cmnd[0] == MODE_SENSE) {
+ if ((pageCode == 0x3F) || (pageCode == 0x20)) {
+ ms_mode_sense(chip, srb->cmnd[0],
+ lun, buf, dataSize);
+ } else {
+ dataSize = 4;
+ buf[0] = 0x03;
+ buf[1] = 0x00;
+ if (check_card_wp(chip, lun))
+ buf[2] = 0x80;
+ else
+ buf[2] = 0x00;
+
+ buf[3] = 0x00;
+ }
+ } else {
+ if ((pageCode == 0x3F) || (pageCode == 0x20)) {
+ ms_mode_sense(chip, srb->cmnd[0],
+ lun, buf, dataSize);
+ } else {
+ dataSize = 8;
+ buf[0] = 0x00;
+ buf[1] = 0x06;
+ buf[2] = 0x00;
+ if (check_card_wp(chip, lun))
+ buf[3] = 0x80;
+ else
+ buf[3] = 0x00;
+ buf[4] = 0x00;
+ buf[5] = 0x00;
+ buf[6] = 0x00;
+ buf[7] = 0x00;
+ }
+ }
+ status = TRANSPORT_GOOD;
+ } else {
+ set_sense_type(chip, lun, SENSE_TYPE_MEDIA_INVALID_CMD_FIELD);
+ scsi_set_resid(srb, scsi_bufflen(srb));
+ status = TRANSPORT_FAILED;
+ }
+
+ if (status == TRANSPORT_GOOD) {
+ unsigned int len = min_t(unsigned int, scsi_bufflen(srb),
+ dataSize);
+ rtsx_stor_set_xfer_buf(buf, len, srb);
+ scsi_set_resid(srb, scsi_bufflen(srb) - len);
+ }
+ kfree(buf);
+
+ return status;
+}
+
+static int read_write(struct scsi_cmnd *srb, struct rtsx_chip *chip)
+{
+#ifdef SUPPORT_SD_LOCK
+ struct sd_info *sd_card = &(chip->sd_card);
+#endif
+ unsigned int lun = SCSI_LUN(srb);
+ int retval;
+ u32 start_sec;
+ u16 sec_cnt;
+
+ rtsx_disable_aspm(chip);
+
+ if (chip->ss_en && (rtsx_get_stat(chip) == RTSX_STAT_SS)) {
+ rtsx_exit_ss(chip);
+ wait_timeout(100);
+ }
+ rtsx_set_stat(chip, RTSX_STAT_RUN);
+
+ if (!check_card_ready(chip, lun) || (get_card_size(chip, lun) == 0)) {
+ set_sense_type(chip, lun, SENSE_TYPE_MEDIA_NOT_PRESENT);
+ rtsx_trace(chip);
+ return TRANSPORT_FAILED;
+ }
+
+ if (!(CHK_BIT(chip->lun_mc, lun))) {
+ SET_BIT(chip->lun_mc, lun);
+ set_sense_type(chip, lun, SENSE_TYPE_MEDIA_CHANGE);
+ return TRANSPORT_FAILED;
+ }
+
+#ifdef SUPPORT_SD_LOCK
+ if (sd_card->sd_erase_status) {
+ /* Accessing to any card is forbidden
+ * until the erase procedure of SD is completed
+ */
+ dev_dbg(rtsx_dev(chip), "SD card being erased!\n");
+ set_sense_type(chip, lun, SENSE_TYPE_MEDIA_READ_FORBIDDEN);
+ rtsx_trace(chip);
+ return TRANSPORT_FAILED;
+ }
+
+ if (get_lun_card(chip, lun) == SD_CARD) {
+ if (sd_card->sd_lock_status & SD_LOCKED) {
+ dev_dbg(rtsx_dev(chip), "SD card locked!\n");
+ set_sense_type(chip, lun,
+ SENSE_TYPE_MEDIA_READ_FORBIDDEN);
+ rtsx_trace(chip);
+ return TRANSPORT_FAILED;
+ }
+ }
+#endif
+
+ if ((srb->cmnd[0] == READ_10) || (srb->cmnd[0] == WRITE_10)) {
+ start_sec = ((u32)srb->cmnd[2] << 24) |
+ ((u32)srb->cmnd[3] << 16) |
+ ((u32)srb->cmnd[4] << 8) | ((u32)srb->cmnd[5]);
+ sec_cnt = ((u16)(srb->cmnd[7]) << 8) | srb->cmnd[8];
+ } else if ((srb->cmnd[0] == READ_6) || (srb->cmnd[0] == WRITE_6)) {
+ start_sec = ((u32)(srb->cmnd[1] & 0x1F) << 16) |
+ ((u32)srb->cmnd[2] << 8) | ((u32)srb->cmnd[3]);
+ sec_cnt = srb->cmnd[4];
+ } else if ((srb->cmnd[0] == VENDOR_CMND) &&
+ (srb->cmnd[1] == SCSI_APP_CMD) &&
+ ((srb->cmnd[2] == PP_READ10) || (srb->cmnd[2] == PP_WRITE10))) {
+ start_sec = ((u32)srb->cmnd[4] << 24) |
+ ((u32)srb->cmnd[5] << 16) |
+ ((u32)srb->cmnd[6] << 8) | ((u32)srb->cmnd[7]);
+ sec_cnt = ((u16)(srb->cmnd[9]) << 8) | srb->cmnd[10];
+ } else {
+ set_sense_type(chip, lun, SENSE_TYPE_MEDIA_INVALID_CMD_FIELD);
+ rtsx_trace(chip);
+ return TRANSPORT_FAILED;
+ }
+
+ /* In some test, we will receive a start_sec like 0xFFFFFFFF.
+ * In this situation, start_sec + sec_cnt will overflow, so we
+ * need to judge start_sec at first
+ */
+ if ((start_sec > get_card_size(chip, lun)) ||
+ ((start_sec + sec_cnt) > get_card_size(chip, lun))) {
+ set_sense_type(chip, lun, SENSE_TYPE_MEDIA_LBA_OVER_RANGE);
+ rtsx_trace(chip);
+ return TRANSPORT_FAILED;
+ }
+
+ if (sec_cnt == 0) {
+ scsi_set_resid(srb, 0);
+ return TRANSPORT_GOOD;
+ }
+
+ if (chip->rw_fail_cnt[lun] == 3) {
+ dev_dbg(rtsx_dev(chip), "read/write fail three times in succession\n");
+ if (srb->sc_data_direction == DMA_FROM_DEVICE)
+ set_sense_type(chip, lun,
+ SENSE_TYPE_MEDIA_UNRECOVER_READ_ERR);
+ else
+ set_sense_type(chip, lun, SENSE_TYPE_MEDIA_WRITE_ERR);
+
+ rtsx_trace(chip);
+ return TRANSPORT_FAILED;
+ }
+
+ if (srb->sc_data_direction == DMA_TO_DEVICE) {
+ if (check_card_wp(chip, lun)) {
+ dev_dbg(rtsx_dev(chip), "Write protected card!\n");
+ set_sense_type(chip, lun,
+ SENSE_TYPE_MEDIA_WRITE_PROTECT);
+ rtsx_trace(chip);
+ return TRANSPORT_FAILED;
+ }
+ }
+
+ retval = card_rw(srb, chip, start_sec, sec_cnt);
+ if (retval != STATUS_SUCCESS) {
+ if (chip->need_release & chip->lun2card[lun]) {
+ chip->rw_fail_cnt[lun] = 0;
+ set_sense_type(chip, lun, SENSE_TYPE_MEDIA_NOT_PRESENT);
+ } else {
+ chip->rw_fail_cnt[lun]++;
+ if (srb->sc_data_direction == DMA_FROM_DEVICE)
+ set_sense_type(chip, lun,
+ SENSE_TYPE_MEDIA_UNRECOVER_READ_ERR);
+ else
+ set_sense_type(chip, lun,
+ SENSE_TYPE_MEDIA_WRITE_ERR);
+ }
+ retval = TRANSPORT_FAILED;
+ rtsx_trace(chip);
+ goto Exit;
+ } else {
+ chip->rw_fail_cnt[lun] = 0;
+ retval = TRANSPORT_GOOD;
+ }
+
+ scsi_set_resid(srb, 0);
+
+Exit:
+ return retval;
+}
+
+static int read_format_capacity(struct scsi_cmnd *srb, struct rtsx_chip *chip)
+{
+ unsigned char *buf;
+ unsigned int lun = SCSI_LUN(srb);
+ unsigned int buf_len;
+ u8 card = get_lun_card(chip, lun);
+ u32 card_size;
+ int desc_cnt;
+ int i = 0;
+
+ if (!check_card_ready(chip, lun)) {
+ if (!chip->mspro_formatter_enable) {
+ set_sense_type(chip, lun, SENSE_TYPE_MEDIA_NOT_PRESENT);
+ rtsx_trace(chip);
+ return TRANSPORT_FAILED;
+ }
+ }
+
+ buf_len = (scsi_bufflen(srb) > 12) ? 0x14 : 12;
+
+ buf = kmalloc(buf_len, GFP_KERNEL);
+ if (buf == NULL) {
+ rtsx_trace(chip);
+ return TRANSPORT_ERROR;
+ }
+
+ buf[i++] = 0;
+ buf[i++] = 0;
+ buf[i++] = 0;
+
+ /* Capacity List Length */
+ if ((buf_len > 12) && chip->mspro_formatter_enable &&
+ (chip->lun2card[lun] & MS_CARD) &&
+ (!card || (card == MS_CARD))) {
+ buf[i++] = 0x10;
+ desc_cnt = 2;
+ } else {
+ buf[i++] = 0x08;
+ desc_cnt = 1;
+ }
+
+ while (desc_cnt) {
+ if (check_card_ready(chip, lun)) {
+ card_size = get_card_size(chip, lun);
+ buf[i++] = (unsigned char)(card_size >> 24);
+ buf[i++] = (unsigned char)(card_size >> 16);
+ buf[i++] = (unsigned char)(card_size >> 8);
+ buf[i++] = (unsigned char)card_size;
+
+ if (desc_cnt == 2)
+ buf[i++] = 2;
+ else
+ buf[i++] = 0;
+ } else {
+ buf[i++] = 0xFF;
+ buf[i++] = 0xFF;
+ buf[i++] = 0xFF;
+ buf[i++] = 0xFF;
+
+ if (desc_cnt == 2)
+ buf[i++] = 3;
+ else
+ buf[i++] = 0;
+ }
+
+ buf[i++] = 0x00;
+ buf[i++] = 0x02;
+ buf[i++] = 0x00;
+
+ desc_cnt--;
+ }
+
+ buf_len = min_t(unsigned int, scsi_bufflen(srb), buf_len);
+ rtsx_stor_set_xfer_buf(buf, buf_len, srb);
+ kfree(buf);
+
+ scsi_set_resid(srb, scsi_bufflen(srb) - buf_len);
+
+ return TRANSPORT_GOOD;
+}
+
+static int read_capacity(struct scsi_cmnd *srb, struct rtsx_chip *chip)
+{
+ unsigned char *buf;
+ unsigned int lun = SCSI_LUN(srb);
+ u32 card_size;
+
+ if (!check_card_ready(chip, lun)) {
+ set_sense_type(chip, lun, SENSE_TYPE_MEDIA_NOT_PRESENT);
+ rtsx_trace(chip);
+ return TRANSPORT_FAILED;
+ }
+
+ if (!(CHK_BIT(chip->lun_mc, lun))) {
+ SET_BIT(chip->lun_mc, lun);
+ set_sense_type(chip, lun, SENSE_TYPE_MEDIA_CHANGE);
+ return TRANSPORT_FAILED;
+ }
+
+ buf = kmalloc(8, GFP_KERNEL);
+ if (buf == NULL) {
+ rtsx_trace(chip);
+ return TRANSPORT_ERROR;
+ }
+
+ card_size = get_card_size(chip, lun);
+ buf[0] = (unsigned char)((card_size - 1) >> 24);
+ buf[1] = (unsigned char)((card_size - 1) >> 16);
+ buf[2] = (unsigned char)((card_size - 1) >> 8);
+ buf[3] = (unsigned char)(card_size - 1);
+
+ buf[4] = 0x00;
+ buf[5] = 0x00;
+ buf[6] = 0x02;
+ buf[7] = 0x00;
+
+ rtsx_stor_set_xfer_buf(buf, scsi_bufflen(srb), srb);
+ kfree(buf);
+
+ scsi_set_resid(srb, 0);
+
+ return TRANSPORT_GOOD;
+}
+
+static int read_eeprom(struct scsi_cmnd *srb, struct rtsx_chip *chip)
+{
+ unsigned short len, i;
+ int retval;
+ u8 *buf;
+
+ rtsx_disable_aspm(chip);
+
+ if (chip->ss_en && (rtsx_get_stat(chip) == RTSX_STAT_SS)) {
+ rtsx_exit_ss(chip);
+ wait_timeout(100);
+ }
+ rtsx_set_stat(chip, RTSX_STAT_RUN);
+
+ len = ((u16)srb->cmnd[4] << 8) | srb->cmnd[5];
+
+ buf = vmalloc(len);
+ if (!buf) {
+ rtsx_trace(chip);
+ return TRANSPORT_ERROR;
+ }
+
+ retval = rtsx_force_power_on(chip, SSC_PDCTL);
+ if (retval != STATUS_SUCCESS) {
+ vfree(buf);
+ set_sense_type(chip, SCSI_LUN(srb),
+ SENSE_TYPE_MEDIA_UNRECOVER_READ_ERR);
+ rtsx_trace(chip);
+ return TRANSPORT_FAILED;
+ }
+
+ for (i = 0; i < len; i++) {
+ retval = spi_read_eeprom(chip, i, buf + i);
+ if (retval != STATUS_SUCCESS) {
+ vfree(buf);
+ set_sense_type(chip, SCSI_LUN(srb),
+ SENSE_TYPE_MEDIA_UNRECOVER_READ_ERR);
+ rtsx_trace(chip);
+ return TRANSPORT_FAILED;
+ }
+ }
+
+ len = (unsigned short)min_t(unsigned int, scsi_bufflen(srb), len);
+ rtsx_stor_set_xfer_buf(buf, len, srb);
+ scsi_set_resid(srb, scsi_bufflen(srb) - len);
+
+ vfree(buf);
+
+ return TRANSPORT_GOOD;
+}
+
+static int write_eeprom(struct scsi_cmnd *srb, struct rtsx_chip *chip)
+{
+ unsigned short len, i;
+ int retval;
+ u8 *buf;
+
+ rtsx_disable_aspm(chip);
+
+ if (chip->ss_en && (rtsx_get_stat(chip) == RTSX_STAT_SS)) {
+ rtsx_exit_ss(chip);
+ wait_timeout(100);
+ }
+ rtsx_set_stat(chip, RTSX_STAT_RUN);
+
+ len = ((u16)srb->cmnd[4] << 8) | srb->cmnd[5];
+
+ retval = rtsx_force_power_on(chip, SSC_PDCTL);
+ if (retval != STATUS_SUCCESS) {
+ set_sense_type(chip, SCSI_LUN(srb), SENSE_TYPE_MEDIA_WRITE_ERR);
+ rtsx_trace(chip);
+ return TRANSPORT_FAILED;
+ }
+
+ if (len == 511) {
+ retval = spi_erase_eeprom_chip(chip);
+ if (retval != STATUS_SUCCESS) {
+ set_sense_type(chip, SCSI_LUN(srb),
+ SENSE_TYPE_MEDIA_WRITE_ERR);
+ rtsx_trace(chip);
+ return TRANSPORT_FAILED;
+ }
+ } else {
+ len = (unsigned short)min_t(unsigned int, scsi_bufflen(srb),
+ len);
+ buf = vmalloc(len);
+ if (buf == NULL) {
+ rtsx_trace(chip);
+ return TRANSPORT_ERROR;
+ }
+
+ rtsx_stor_get_xfer_buf(buf, len, srb);
+ scsi_set_resid(srb, scsi_bufflen(srb) - len);
+
+ for (i = 0; i < len; i++) {
+ retval = spi_write_eeprom(chip, i, buf[i]);
+ if (retval != STATUS_SUCCESS) {
+ vfree(buf);
+ set_sense_type(chip, SCSI_LUN(srb),
+ SENSE_TYPE_MEDIA_WRITE_ERR);
+ rtsx_trace(chip);
+ return TRANSPORT_FAILED;
+ }
+ }
+
+ vfree(buf);
+ }
+
+ return TRANSPORT_GOOD;
+}
+
+static int read_mem(struct scsi_cmnd *srb, struct rtsx_chip *chip)
+{
+ unsigned short addr, len, i;
+ int retval;
+ u8 *buf;
+
+ rtsx_disable_aspm(chip);
+
+ if (chip->ss_en && (rtsx_get_stat(chip) == RTSX_STAT_SS)) {
+ rtsx_exit_ss(chip);
+ wait_timeout(100);
+ }
+ rtsx_set_stat(chip, RTSX_STAT_RUN);
+
+ addr = ((u16)srb->cmnd[2] << 8) | srb->cmnd[3];
+ len = ((u16)srb->cmnd[4] << 8) | srb->cmnd[5];
+
+ if (addr < 0xFC00) {
+ set_sense_type(chip, SCSI_LUN(srb),
+ SENSE_TYPE_MEDIA_INVALID_CMD_FIELD);
+ rtsx_trace(chip);
+ return TRANSPORT_FAILED;
+ }
+
+ buf = vmalloc(len);
+ if (!buf) {
+ rtsx_trace(chip);
+ return TRANSPORT_ERROR;
+ }
+
+ retval = rtsx_force_power_on(chip, SSC_PDCTL);
+ if (retval != STATUS_SUCCESS) {
+ vfree(buf);
+ set_sense_type(chip, SCSI_LUN(srb), SENSE_TYPE_MEDIA_WRITE_ERR);
+ rtsx_trace(chip);
+ return TRANSPORT_FAILED;
+ }
+
+ for (i = 0; i < len; i++) {
+ retval = rtsx_read_register(chip, addr + i, buf + i);
+ if (retval != STATUS_SUCCESS) {
+ vfree(buf);
+ set_sense_type(chip, SCSI_LUN(srb),
+ SENSE_TYPE_MEDIA_UNRECOVER_READ_ERR);
+ rtsx_trace(chip);
+ return TRANSPORT_FAILED;
+ }
+ }
+
+ len = (unsigned short)min_t(unsigned int, scsi_bufflen(srb), len);
+ rtsx_stor_set_xfer_buf(buf, len, srb);
+ scsi_set_resid(srb, scsi_bufflen(srb) - len);
+
+ vfree(buf);
+
+ return TRANSPORT_GOOD;
+}
+
+static int write_mem(struct scsi_cmnd *srb, struct rtsx_chip *chip)
+{
+ unsigned short addr, len, i;
+ int retval;
+ u8 *buf;
+
+ rtsx_disable_aspm(chip);
+
+ if (chip->ss_en && (rtsx_get_stat(chip) == RTSX_STAT_SS)) {
+ rtsx_exit_ss(chip);
+ wait_timeout(100);
+ }
+ rtsx_set_stat(chip, RTSX_STAT_RUN);
+
+ addr = ((u16)srb->cmnd[2] << 8) | srb->cmnd[3];
+ len = ((u16)srb->cmnd[4] << 8) | srb->cmnd[5];
+
+ if (addr < 0xFC00) {
+ set_sense_type(chip, SCSI_LUN(srb),
+ SENSE_TYPE_MEDIA_INVALID_CMD_FIELD);
+ rtsx_trace(chip);
+ return TRANSPORT_FAILED;
+ }
+
+ len = (unsigned short)min_t(unsigned int, scsi_bufflen(srb), len);
+ buf = vmalloc(len);
+ if (buf == NULL) {
+ rtsx_trace(chip);
+ return TRANSPORT_ERROR;
+ }
+
+ rtsx_stor_get_xfer_buf(buf, len, srb);
+ scsi_set_resid(srb, scsi_bufflen(srb) - len);
+
+ retval = rtsx_force_power_on(chip, SSC_PDCTL);
+ if (retval != STATUS_SUCCESS) {
+ vfree(buf);
+ set_sense_type(chip, SCSI_LUN(srb), SENSE_TYPE_MEDIA_WRITE_ERR);
+ rtsx_trace(chip);
+ return TRANSPORT_FAILED;
+ }
+
+ for (i = 0; i < len; i++) {
+ retval = rtsx_write_register(chip, addr + i, 0xFF, buf[i]);
+ if (retval != STATUS_SUCCESS) {
+ vfree(buf);
+ set_sense_type(chip, SCSI_LUN(srb),
+ SENSE_TYPE_MEDIA_WRITE_ERR);
+ rtsx_trace(chip);
+ return TRANSPORT_FAILED;
+ }
+ }
+
+ vfree(buf);
+
+ return TRANSPORT_GOOD;
+}
+
+static int get_sd_csd(struct scsi_cmnd *srb, struct rtsx_chip *chip)
+{
+ struct sd_info *sd_card = &(chip->sd_card);
+ unsigned int lun = SCSI_LUN(srb);
+
+ if (!check_card_ready(chip, lun)) {
+ set_sense_type(chip, lun, SENSE_TYPE_MEDIA_NOT_PRESENT);
+ rtsx_trace(chip);
+ return TRANSPORT_FAILED;
+ }
+
+ if (get_lun_card(chip, lun) != SD_CARD) {
+ set_sense_type(chip, lun, SENSE_TYPE_MEDIA_UNRECOVER_READ_ERR);
+ rtsx_trace(chip);
+ return TRANSPORT_FAILED;
+ }
+
+ scsi_set_resid(srb, 0);
+ rtsx_stor_set_xfer_buf(sd_card->raw_csd, scsi_bufflen(srb), srb);
+
+ return TRANSPORT_GOOD;
+}
+
+static int toggle_gpio_cmd(struct scsi_cmnd *srb, struct rtsx_chip *chip)
+{
+ u8 gpio = srb->cmnd[2];
+
+ rtsx_disable_aspm(chip);
+
+ if (chip->ss_en && (rtsx_get_stat(chip) == RTSX_STAT_SS)) {
+ rtsx_exit_ss(chip);
+ wait_timeout(100);
+ }
+ rtsx_set_stat(chip, RTSX_STAT_RUN);
+
+ if (gpio > 3)
+ gpio = 1;
+ toggle_gpio(chip, gpio);
+
+ return TRANSPORT_GOOD;
+}
+
+#ifdef _MSG_TRACE
+static int trace_msg_cmd(struct scsi_cmnd *srb, struct rtsx_chip *chip)
+{
+ unsigned char *ptr, *buf = NULL;
+ int i, msg_cnt;
+ u8 clear;
+ unsigned int buf_len;
+
+ buf_len = 4 + ((2 + MSG_FUNC_LEN + MSG_FILE_LEN + TIME_VAL_LEN) *
+ TRACE_ITEM_CNT);
+
+ if ((scsi_bufflen(srb) < buf_len) || (scsi_sglist(srb) == NULL)) {
+ set_sense_type(chip, SCSI_LUN(srb),
+ SENSE_TYPE_MEDIA_UNRECOVER_READ_ERR);
+ rtsx_trace(chip);
+ return TRANSPORT_FAILED;
+ }
+
+ clear = srb->cmnd[2];
+
+ buf = vmalloc(scsi_bufflen(srb));
+ if (buf == NULL) {
+ rtsx_trace(chip);
+ return TRANSPORT_ERROR;
+ }
+ ptr = buf;
+
+ if (chip->trace_msg[chip->msg_idx].valid)
+ msg_cnt = TRACE_ITEM_CNT;
+ else
+ msg_cnt = chip->msg_idx;
+
+ *(ptr++) = (u8)(msg_cnt >> 24);
+ *(ptr++) = (u8)(msg_cnt >> 16);
+ *(ptr++) = (u8)(msg_cnt >> 8);
+ *(ptr++) = (u8)msg_cnt;
+ dev_dbg(rtsx_dev(chip), "Trace message count is %d\n", msg_cnt);
+
+ for (i = 1; i <= msg_cnt; i++) {
+ int j, idx;
+
+ idx = chip->msg_idx - i;
+ if (idx < 0)
+ idx += TRACE_ITEM_CNT;
+
+ *(ptr++) = (u8)(chip->trace_msg[idx].line >> 8);
+ *(ptr++) = (u8)(chip->trace_msg[idx].line);
+ for (j = 0; j < MSG_FUNC_LEN; j++)
+ *(ptr++) = chip->trace_msg[idx].func[j];
+
+ for (j = 0; j < MSG_FILE_LEN; j++)
+ *(ptr++) = chip->trace_msg[idx].file[j];
+
+ for (j = 0; j < TIME_VAL_LEN; j++)
+ *(ptr++) = chip->trace_msg[idx].timeval_buf[j];
+ }
+
+ rtsx_stor_set_xfer_buf(buf, scsi_bufflen(srb), srb);
+ vfree(buf);
+
+ if (clear) {
+ chip->msg_idx = 0;
+ for (i = 0; i < TRACE_ITEM_CNT; i++)
+ chip->trace_msg[i].valid = 0;
+ }
+
+ scsi_set_resid(srb, 0);
+ return TRANSPORT_GOOD;
+}
+#endif
+
+static int read_host_reg(struct scsi_cmnd *srb, struct rtsx_chip *chip)
+{
+ u8 addr, buf[4];
+ u32 val;
+ unsigned int len;
+
+ rtsx_disable_aspm(chip);
+
+ if (chip->ss_en && (rtsx_get_stat(chip) == RTSX_STAT_SS)) {
+ rtsx_exit_ss(chip);
+ wait_timeout(100);
+ }
+ rtsx_set_stat(chip, RTSX_STAT_RUN);
+
+ addr = srb->cmnd[4];
+
+ val = rtsx_readl(chip, addr);
+ dev_dbg(rtsx_dev(chip), "Host register (0x%x): 0x%x\n", addr, val);
+
+ buf[0] = (u8)(val >> 24);
+ buf[1] = (u8)(val >> 16);
+ buf[2] = (u8)(val >> 8);
+ buf[3] = (u8)val;
+
+ len = min_t(unsigned int, scsi_bufflen(srb), 4);
+ rtsx_stor_set_xfer_buf(buf, len, srb);
+ scsi_set_resid(srb, scsi_bufflen(srb) - len);
+
+ return TRANSPORT_GOOD;
+}
+
+static int write_host_reg(struct scsi_cmnd *srb, struct rtsx_chip *chip)
+{
+ u8 addr, buf[4];
+ u32 val;
+ unsigned int len;
+
+ rtsx_disable_aspm(chip);
+
+ if (chip->ss_en && (rtsx_get_stat(chip) == RTSX_STAT_SS)) {
+ rtsx_exit_ss(chip);
+ wait_timeout(100);
+ }
+ rtsx_set_stat(chip, RTSX_STAT_RUN);
+
+ addr = srb->cmnd[4];
+
+ len = min_t(unsigned int, scsi_bufflen(srb), 4);
+ rtsx_stor_get_xfer_buf(buf, len, srb);
+ scsi_set_resid(srb, scsi_bufflen(srb) - len);
+
+ val = ((u32)buf[0] << 24) | ((u32)buf[1] << 16) | ((u32)buf[2]
+ << 8) | buf[3];
+
+ rtsx_writel(chip, addr, val);
+
+ return TRANSPORT_GOOD;
+}
+
+static int set_variable(struct scsi_cmnd *srb, struct rtsx_chip *chip)
+{
+ unsigned lun = SCSI_LUN(srb);
+
+ if (srb->cmnd[3] == 1) {
+ /* Variable Clock */
+ struct xd_info *xd_card = &(chip->xd_card);
+ struct sd_info *sd_card = &(chip->sd_card);
+ struct ms_info *ms_card = &(chip->ms_card);
+
+ switch (srb->cmnd[4]) {
+ case XD_CARD:
+ xd_card->xd_clock = srb->cmnd[5];
+ break;
+
+ case SD_CARD:
+ sd_card->sd_clock = srb->cmnd[5];
+ break;
+
+ case MS_CARD:
+ ms_card->ms_clock = srb->cmnd[5];
+ break;
+
+ default:
+ set_sense_type(chip, lun,
+ SENSE_TYPE_MEDIA_INVALID_CMD_FIELD);
+ rtsx_trace(chip);
+ return TRANSPORT_FAILED;
+ }
+ } else if (srb->cmnd[3] == 2) {
+ if (srb->cmnd[4]) {
+ chip->blink_led = 1;
+ } else {
+ int retval;
+
+ chip->blink_led = 0;
+
+ rtsx_disable_aspm(chip);
+
+ if (chip->ss_en &&
+ (rtsx_get_stat(chip) == RTSX_STAT_SS)) {
+ rtsx_exit_ss(chip);
+ wait_timeout(100);
+ }
+ rtsx_set_stat(chip, RTSX_STAT_RUN);
+
+ retval = rtsx_force_power_on(chip, SSC_PDCTL);
+ if (retval != STATUS_SUCCESS) {
+ set_sense_type(chip, SCSI_LUN(srb),
+ SENSE_TYPE_MEDIA_WRITE_ERR);
+ rtsx_trace(chip);
+ return TRANSPORT_FAILED;
+ }
+
+ turn_off_led(chip, LED_GPIO);
+ }
+ } else {
+ set_sense_type(chip, lun, SENSE_TYPE_MEDIA_INVALID_CMD_FIELD);
+ rtsx_trace(chip);
+ return TRANSPORT_FAILED;
+ }
+
+ return TRANSPORT_GOOD;
+}
+
+static int get_variable(struct scsi_cmnd *srb, struct rtsx_chip *chip)
+{
+ unsigned int lun = SCSI_LUN(srb);
+
+ if (srb->cmnd[3] == 1) {
+ struct xd_info *xd_card = &(chip->xd_card);
+ struct sd_info *sd_card = &(chip->sd_card);
+ struct ms_info *ms_card = &(chip->ms_card);
+ u8 tmp;
+
+ switch (srb->cmnd[4]) {
+ case XD_CARD:
+ tmp = (u8)(xd_card->xd_clock);
+ break;
+
+ case SD_CARD:
+ tmp = (u8)(sd_card->sd_clock);
+ break;
+
+ case MS_CARD:
+ tmp = (u8)(ms_card->ms_clock);
+ break;
+
+ default:
+ set_sense_type(chip, lun,
+ SENSE_TYPE_MEDIA_INVALID_CMD_FIELD);
+ rtsx_trace(chip);
+ return TRANSPORT_FAILED;
+ }
+
+ rtsx_stor_set_xfer_buf(&tmp, 1, srb);
+ } else if (srb->cmnd[3] == 2) {
+ u8 tmp = chip->blink_led;
+
+ rtsx_stor_set_xfer_buf(&tmp, 1, srb);
+ } else {
+ set_sense_type(chip, lun, SENSE_TYPE_MEDIA_INVALID_CMD_FIELD);
+ rtsx_trace(chip);
+ return TRANSPORT_FAILED;
+ }
+
+ return TRANSPORT_GOOD;
+}
+
+static int dma_access_ring_buffer(struct scsi_cmnd *srb, struct rtsx_chip *chip)
+{
+ int retval;
+ unsigned int lun = SCSI_LUN(srb);
+ u16 len;
+
+ rtsx_disable_aspm(chip);
+
+ if (chip->ss_en && (rtsx_get_stat(chip) == RTSX_STAT_SS)) {
+ rtsx_exit_ss(chip);
+ wait_timeout(100);
+ }
+ rtsx_set_stat(chip, RTSX_STAT_RUN);
+
+ len = ((u16)(srb->cmnd[4]) << 8) | srb->cmnd[5];
+ len = min_t(u16, len, scsi_bufflen(srb));
+
+ if (srb->sc_data_direction == DMA_FROM_DEVICE)
+ dev_dbg(rtsx_dev(chip), "Read from device\n");
+ else
+ dev_dbg(rtsx_dev(chip), "Write to device\n");
+
+ retval = rtsx_transfer_data(chip, 0, scsi_sglist(srb), len,
+ scsi_sg_count(srb), srb->sc_data_direction, 1000);
+ if (retval < 0) {
+ if (srb->sc_data_direction == DMA_FROM_DEVICE)
+ set_sense_type(chip, lun,
+ SENSE_TYPE_MEDIA_UNRECOVER_READ_ERR);
+ else
+ set_sense_type(chip, lun,
+ SENSE_TYPE_MEDIA_WRITE_ERR);
+
+ rtsx_trace(chip);
+ return TRANSPORT_FAILED;
+ }
+ scsi_set_resid(srb, 0);
+
+ return TRANSPORT_GOOD;
+}
+
+static int get_dev_status(struct scsi_cmnd *srb, struct rtsx_chip *chip)
+{
+ struct sd_info *sd_card = &(chip->sd_card);
+ struct ms_info *ms_card = &(chip->ms_card);
+ int buf_len;
+ unsigned int lun = SCSI_LUN(srb);
+ u8 card = get_lun_card(chip, lun);
+ u8 status[32];
+#ifdef SUPPORT_OCP
+ u8 oc_now_mask = 0, oc_ever_mask = 0;
+#endif
+
+ memset(status, 0, 32);
+
+ status[0] = (u8)(chip->product_id);
+ status[1] = chip->ic_version;
+
+ if (chip->auto_delink_en)
+ status[2] = 0x10;
+ else
+ status[2] = 0x00;
+
+ status[3] = 20;
+ status[4] = 10;
+ status[5] = 05;
+ status[6] = 21;
+
+ if (chip->card_wp)
+ status[7] = 0x20;
+ else
+ status[7] = 0x00;
+
+#ifdef SUPPORT_OCP
+ status[8] = 0;
+ if (CHECK_LUN_MODE(chip,
+ SD_MS_2LUN) && (chip->lun2card[lun] == MS_CARD)) {
+ oc_now_mask = MS_OC_NOW;
+ oc_ever_mask = MS_OC_EVER;
+ } else {
+ oc_now_mask = SD_OC_NOW;
+ oc_ever_mask = SD_OC_EVER;
+ }
+
+ if (chip->ocp_stat & oc_now_mask)
+ status[8] |= 0x02;
+
+ if (chip->ocp_stat & oc_ever_mask)
+ status[8] |= 0x01;
+#endif
+
+ if (card == SD_CARD) {
+ if (CHK_SD(sd_card)) {
+ if (CHK_SD_HCXC(sd_card)) {
+ if (sd_card->capacity > 0x4000000)
+ status[0x0E] = 0x02;
+ else
+ status[0x0E] = 0x01;
+ } else {
+ status[0x0E] = 0x00;
+ }
+
+ if (CHK_SD_SDR104(sd_card))
+ status[0x0F] = 0x03;
+ else if (CHK_SD_DDR50(sd_card))
+ status[0x0F] = 0x04;
+ else if (CHK_SD_SDR50(sd_card))
+ status[0x0F] = 0x02;
+ else if (CHK_SD_HS(sd_card))
+ status[0x0F] = 0x01;
+ else
+ status[0x0F] = 0x00;
+ } else {
+ if (CHK_MMC_SECTOR_MODE(sd_card))
+ status[0x0E] = 0x01;
+ else
+ status[0x0E] = 0x00;
+
+ if (CHK_MMC_DDR52(sd_card))
+ status[0x0F] = 0x03;
+ else if (CHK_MMC_52M(sd_card))
+ status[0x0F] = 0x02;
+ else if (CHK_MMC_26M(sd_card))
+ status[0x0F] = 0x01;
+ else
+ status[0x0F] = 0x00;
+ }
+ } else if (card == MS_CARD) {
+ if (CHK_MSPRO(ms_card)) {
+ if (CHK_MSXC(ms_card))
+ status[0x0E] = 0x01;
+ else
+ status[0x0E] = 0x00;
+
+ if (CHK_HG8BIT(ms_card))
+ status[0x0F] = 0x01;
+ else
+ status[0x0F] = 0x00;
+ }
+ }
+
+#ifdef SUPPORT_SD_LOCK
+ if (card == SD_CARD) {
+ status[0x17] = 0x80;
+ if (sd_card->sd_erase_status)
+ status[0x17] |= 0x01;
+ if (sd_card->sd_lock_status & SD_LOCKED) {
+ status[0x17] |= 0x02;
+ status[0x07] |= 0x40;
+ }
+ if (sd_card->sd_lock_status & SD_PWD_EXIST)
+ status[0x17] |= 0x04;
+ } else {
+ status[0x17] = 0x00;
+ }
+
+ dev_dbg(rtsx_dev(chip), "status[0x17] = 0x%x\n", status[0x17]);
+#endif
+
+ status[0x18] = 0x8A;
+ status[0x1A] = 0x28;
+#ifdef SUPPORT_SD_LOCK
+ status[0x1F] = 0x01;
+#endif
+
+ buf_len = min_t(unsigned int, scsi_bufflen(srb), sizeof(status));
+ rtsx_stor_set_xfer_buf(status, buf_len, srb);
+ scsi_set_resid(srb, scsi_bufflen(srb) - buf_len);
+
+ return TRANSPORT_GOOD;
+}
+
+static int set_chip_mode(struct scsi_cmnd *srb, struct rtsx_chip *chip)
+{
+ int phy_debug_mode;
+ int retval;
+ u16 reg;
+
+ if (!CHECK_PID(chip, 0x5208)) {
+ set_sense_type(chip, SCSI_LUN(srb),
+ SENSE_TYPE_MEDIA_INVALID_CMD_FIELD);
+ rtsx_trace(chip);
+ return TRANSPORT_FAILED;
+ }
+
+ phy_debug_mode = (int)(srb->cmnd[3]);
+
+ if (phy_debug_mode) {
+ chip->phy_debug_mode = 1;
+ retval = rtsx_write_register(chip, CDRESUMECTL, 0x77, 0);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return TRANSPORT_FAILED;
+ }
+
+ rtsx_disable_bus_int(chip);
+
+ retval = rtsx_read_phy_register(chip, 0x1C, &reg);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return TRANSPORT_FAILED;
+ }
+
+ reg |= 0x0001;
+ retval = rtsx_write_phy_register(chip, 0x1C, reg);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return TRANSPORT_FAILED;
+ }
+ } else {
+ chip->phy_debug_mode = 0;
+ retval = rtsx_write_register(chip, CDRESUMECTL, 0x77, 0x77);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return TRANSPORT_FAILED;
+ }
+
+ rtsx_enable_bus_int(chip);
+
+ retval = rtsx_read_phy_register(chip, 0x1C, &reg);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return TRANSPORT_FAILED;
+ }
+
+ reg &= 0xFFFE;
+ retval = rtsx_write_phy_register(chip, 0x1C, reg);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return TRANSPORT_FAILED;
+ }
+ }
+
+ return TRANSPORT_GOOD;
+}
+
+static int rw_mem_cmd_buf(struct scsi_cmnd *srb, struct rtsx_chip *chip)
+{
+ int retval = STATUS_SUCCESS;
+ unsigned int lun = SCSI_LUN(srb);
+ u8 cmd_type, mask, value, idx;
+ u16 addr;
+
+ rtsx_disable_aspm(chip);
+
+ if (chip->ss_en && (rtsx_get_stat(chip) == RTSX_STAT_SS)) {
+ rtsx_exit_ss(chip);
+ wait_timeout(100);
+ }
+ rtsx_set_stat(chip, RTSX_STAT_RUN);
+
+ switch (srb->cmnd[3]) {
+ case INIT_BATCHCMD:
+ rtsx_init_cmd(chip);
+ break;
+
+ case ADD_BATCHCMD:
+ cmd_type = srb->cmnd[4];
+ if (cmd_type > 2) {
+ set_sense_type(chip, lun,
+ SENSE_TYPE_MEDIA_INVALID_CMD_FIELD);
+ rtsx_trace(chip);
+ return TRANSPORT_FAILED;
+ }
+ addr = (srb->cmnd[5] << 8) | srb->cmnd[6];
+ mask = srb->cmnd[7];
+ value = srb->cmnd[8];
+ rtsx_add_cmd(chip, cmd_type, addr, mask, value);
+ break;
+
+ case SEND_BATCHCMD:
+ retval = rtsx_send_cmd(chip, 0, 1000);
+ break;
+
+ case GET_BATCHRSP:
+ idx = srb->cmnd[4];
+ value = *(rtsx_get_cmd_data(chip) + idx);
+ if (scsi_bufflen(srb) < 1) {
+ set_sense_type(chip, lun,
+ SENSE_TYPE_MEDIA_INVALID_CMD_FIELD);
+ rtsx_trace(chip);
+ return TRANSPORT_FAILED;
+ }
+ rtsx_stor_set_xfer_buf(&value, 1, srb);
+ scsi_set_resid(srb, 0);
+ break;
+
+ default:
+ set_sense_type(chip, lun, SENSE_TYPE_MEDIA_INVALID_CMD_FIELD);
+ rtsx_trace(chip);
+ return TRANSPORT_FAILED;
+ }
+
+ if (retval != STATUS_SUCCESS) {
+ set_sense_type(chip, lun, SENSE_TYPE_MEDIA_WRITE_ERR);
+ rtsx_trace(chip);
+ return TRANSPORT_FAILED;
+ }
+
+ return TRANSPORT_GOOD;
+}
+
+static int suit_cmd(struct scsi_cmnd *srb, struct rtsx_chip *chip)
+{
+ int result;
+
+ switch (srb->cmnd[3]) {
+ case INIT_BATCHCMD:
+ case ADD_BATCHCMD:
+ case SEND_BATCHCMD:
+ case GET_BATCHRSP:
+ result = rw_mem_cmd_buf(srb, chip);
+ break;
+ default:
+ result = TRANSPORT_ERROR;
+ }
+
+ return result;
+}
+
+static int read_phy_register(struct scsi_cmnd *srb, struct rtsx_chip *chip)
+{
+ unsigned short addr, len, i;
+ int retval;
+ u8 *buf;
+ u16 val;
+
+ rtsx_disable_aspm(chip);
+
+ if (chip->ss_en && (rtsx_get_stat(chip) == RTSX_STAT_SS)) {
+ rtsx_exit_ss(chip);
+ wait_timeout(100);
+ }
+ rtsx_set_stat(chip, RTSX_STAT_RUN);
+
+ addr = ((u16)srb->cmnd[4] << 8) | srb->cmnd[5];
+ len = ((u16)srb->cmnd[6] << 8) | srb->cmnd[7];
+
+ if (len % 2)
+ len -= len % 2;
+
+ if (len) {
+ buf = vmalloc(len);
+ if (!buf) {
+ rtsx_trace(chip);
+ return TRANSPORT_ERROR;
+ }
+
+ retval = rtsx_force_power_on(chip, SSC_PDCTL);
+ if (retval != STATUS_SUCCESS) {
+ vfree(buf);
+ set_sense_type(chip, SCSI_LUN(srb),
+ SENSE_TYPE_MEDIA_UNRECOVER_READ_ERR);
+ rtsx_trace(chip);
+ return TRANSPORT_FAILED;
+ }
+
+ for (i = 0; i < len / 2; i++) {
+ retval = rtsx_read_phy_register(chip, addr + i, &val);
+ if (retval != STATUS_SUCCESS) {
+ vfree(buf);
+ set_sense_type(chip, SCSI_LUN(srb),
+ SENSE_TYPE_MEDIA_UNRECOVER_READ_ERR);
+ rtsx_trace(chip);
+ return TRANSPORT_FAILED;
+ }
+
+ buf[2*i] = (u8)(val >> 8);
+ buf[2*i+1] = (u8)val;
+ }
+
+ len = (unsigned short)min_t(unsigned int, scsi_bufflen(srb),
+ len);
+ rtsx_stor_set_xfer_buf(buf, len, srb);
+ scsi_set_resid(srb, scsi_bufflen(srb) - len);
+
+ vfree(buf);
+ }
+
+ return TRANSPORT_GOOD;
+}
+
+static int write_phy_register(struct scsi_cmnd *srb, struct rtsx_chip *chip)
+{
+ unsigned short addr, len, i;
+ int retval;
+ u8 *buf;
+ u16 val;
+
+ rtsx_disable_aspm(chip);
+
+ if (chip->ss_en && (rtsx_get_stat(chip) == RTSX_STAT_SS)) {
+ rtsx_exit_ss(chip);
+ wait_timeout(100);
+ }
+ rtsx_set_stat(chip, RTSX_STAT_RUN);
+
+ addr = ((u16)srb->cmnd[4] << 8) | srb->cmnd[5];
+ len = ((u16)srb->cmnd[6] << 8) | srb->cmnd[7];
+
+ if (len % 2)
+ len -= len % 2;
+
+ if (len) {
+ len = (unsigned short)min_t(unsigned int, scsi_bufflen(srb),
+ len);
+
+ buf = vmalloc(len);
+ if (buf == NULL) {
+ rtsx_trace(chip);
+ return TRANSPORT_ERROR;
+ }
+
+ rtsx_stor_get_xfer_buf(buf, len, srb);
+ scsi_set_resid(srb, scsi_bufflen(srb) - len);
+
+ retval = rtsx_force_power_on(chip, SSC_PDCTL);
+ if (retval != STATUS_SUCCESS) {
+ vfree(buf);
+ set_sense_type(chip, SCSI_LUN(srb),
+ SENSE_TYPE_MEDIA_WRITE_ERR);
+ rtsx_trace(chip);
+ return TRANSPORT_FAILED;
+ }
+
+ for (i = 0; i < len / 2; i++) {
+ val = ((u16)buf[2*i] << 8) | buf[2*i+1];
+ retval = rtsx_write_phy_register(chip, addr + i, val);
+ if (retval != STATUS_SUCCESS) {
+ vfree(buf);
+ set_sense_type(chip, SCSI_LUN(srb),
+ SENSE_TYPE_MEDIA_WRITE_ERR);
+ rtsx_trace(chip);
+ return TRANSPORT_FAILED;
+ }
+ }
+
+ vfree(buf);
+ }
+
+ return TRANSPORT_GOOD;
+}
+
+static int erase_eeprom2(struct scsi_cmnd *srb, struct rtsx_chip *chip)
+{
+ unsigned short addr;
+ int retval;
+ u8 mode;
+
+ rtsx_disable_aspm(chip);
+
+ if (chip->ss_en && (rtsx_get_stat(chip) == RTSX_STAT_SS)) {
+ rtsx_exit_ss(chip);
+ wait_timeout(100);
+ }
+ rtsx_set_stat(chip, RTSX_STAT_RUN);
+
+ retval = rtsx_force_power_on(chip, SSC_PDCTL);
+ if (retval != STATUS_SUCCESS) {
+ set_sense_type(chip, SCSI_LUN(srb), SENSE_TYPE_MEDIA_WRITE_ERR);
+ rtsx_trace(chip);
+ return TRANSPORT_FAILED;
+ }
+
+ mode = srb->cmnd[3];
+ addr = ((u16)srb->cmnd[4] << 8) | srb->cmnd[5];
+
+ if (mode == 0) {
+ retval = spi_erase_eeprom_chip(chip);
+ if (retval != STATUS_SUCCESS) {
+ set_sense_type(chip, SCSI_LUN(srb),
+ SENSE_TYPE_MEDIA_WRITE_ERR);
+ rtsx_trace(chip);
+ return TRANSPORT_FAILED;
+ }
+ } else if (mode == 1) {
+ retval = spi_erase_eeprom_byte(chip, addr);
+ if (retval != STATUS_SUCCESS) {
+ set_sense_type(chip, SCSI_LUN(srb),
+ SENSE_TYPE_MEDIA_WRITE_ERR);
+ rtsx_trace(chip);
+ return TRANSPORT_FAILED;
+ }
+ } else {
+ set_sense_type(chip, SCSI_LUN(srb),
+ SENSE_TYPE_MEDIA_INVALID_CMD_FIELD);
+ rtsx_trace(chip);
+ return TRANSPORT_FAILED;
+ }
+
+ return TRANSPORT_GOOD;
+}
+
+static int read_eeprom2(struct scsi_cmnd *srb, struct rtsx_chip *chip)
+{
+ unsigned short addr, len, i;
+ int retval;
+ u8 *buf;
+
+ rtsx_disable_aspm(chip);
+
+ if (chip->ss_en && (rtsx_get_stat(chip) == RTSX_STAT_SS)) {
+ rtsx_exit_ss(chip);
+ wait_timeout(100);
+ }
+ rtsx_set_stat(chip, RTSX_STAT_RUN);
+
+ addr = ((u16)srb->cmnd[4] << 8) | srb->cmnd[5];
+ len = ((u16)srb->cmnd[6] << 8) | srb->cmnd[7];
+
+ buf = vmalloc(len);
+ if (!buf) {
+ rtsx_trace(chip);
+ return TRANSPORT_ERROR;
+ }
+
+ retval = rtsx_force_power_on(chip, SSC_PDCTL);
+ if (retval != STATUS_SUCCESS) {
+ vfree(buf);
+ set_sense_type(chip, SCSI_LUN(srb),
+ SENSE_TYPE_MEDIA_UNRECOVER_READ_ERR);
+ rtsx_trace(chip);
+ return TRANSPORT_FAILED;
+ }
+
+ for (i = 0; i < len; i++) {
+ retval = spi_read_eeprom(chip, addr + i, buf + i);
+ if (retval != STATUS_SUCCESS) {
+ vfree(buf);
+ set_sense_type(chip, SCSI_LUN(srb),
+ SENSE_TYPE_MEDIA_UNRECOVER_READ_ERR);
+ rtsx_trace(chip);
+ return TRANSPORT_FAILED;
+ }
+ }
+
+ len = (unsigned short)min_t(unsigned int, scsi_bufflen(srb), len);
+ rtsx_stor_set_xfer_buf(buf, len, srb);
+ scsi_set_resid(srb, scsi_bufflen(srb) - len);
+
+ vfree(buf);
+
+ return TRANSPORT_GOOD;
+}
+
+static int write_eeprom2(struct scsi_cmnd *srb, struct rtsx_chip *chip)
+{
+ unsigned short addr, len, i;
+ int retval;
+ u8 *buf;
+
+ rtsx_disable_aspm(chip);
+
+ if (chip->ss_en && (rtsx_get_stat(chip) == RTSX_STAT_SS)) {
+ rtsx_exit_ss(chip);
+ wait_timeout(100);
+ }
+ rtsx_set_stat(chip, RTSX_STAT_RUN);
+
+ addr = ((u16)srb->cmnd[4] << 8) | srb->cmnd[5];
+ len = ((u16)srb->cmnd[6] << 8) | srb->cmnd[7];
+
+ len = (unsigned short)min_t(unsigned int, scsi_bufflen(srb), len);
+ buf = vmalloc(len);
+ if (buf == NULL) {
+ rtsx_trace(chip);
+ return TRANSPORT_ERROR;
+ }
+
+ rtsx_stor_get_xfer_buf(buf, len, srb);
+ scsi_set_resid(srb, scsi_bufflen(srb) - len);
+
+ retval = rtsx_force_power_on(chip, SSC_PDCTL);
+ if (retval != STATUS_SUCCESS) {
+ vfree(buf);
+ set_sense_type(chip, SCSI_LUN(srb), SENSE_TYPE_MEDIA_WRITE_ERR);
+ rtsx_trace(chip);
+ return TRANSPORT_FAILED;
+ }
+
+ for (i = 0; i < len; i++) {
+ retval = spi_write_eeprom(chip, addr + i, buf[i]);
+ if (retval != STATUS_SUCCESS) {
+ vfree(buf);
+ set_sense_type(chip, SCSI_LUN(srb),
+ SENSE_TYPE_MEDIA_WRITE_ERR);
+ rtsx_trace(chip);
+ return TRANSPORT_FAILED;
+ }
+ }
+
+ vfree(buf);
+
+ return TRANSPORT_GOOD;
+}
+
+static int read_efuse(struct scsi_cmnd *srb, struct rtsx_chip *chip)
+{
+ int retval;
+ u8 addr, len, i;
+ u8 *buf;
+
+ rtsx_disable_aspm(chip);
+
+ if (chip->ss_en && (rtsx_get_stat(chip) == RTSX_STAT_SS)) {
+ rtsx_exit_ss(chip);
+ wait_timeout(100);
+ }
+ rtsx_set_stat(chip, RTSX_STAT_RUN);
+
+ addr = srb->cmnd[4];
+ len = srb->cmnd[5];
+
+ buf = vmalloc(len);
+ if (!buf) {
+ rtsx_trace(chip);
+ return TRANSPORT_ERROR;
+ }
+
+ retval = rtsx_force_power_on(chip, SSC_PDCTL);
+ if (retval != STATUS_SUCCESS) {
+ vfree(buf);
+ set_sense_type(chip, SCSI_LUN(srb),
+ SENSE_TYPE_MEDIA_UNRECOVER_READ_ERR);
+ rtsx_trace(chip);
+ return TRANSPORT_FAILED;
+ }
+
+ for (i = 0; i < len; i++) {
+ retval = rtsx_read_efuse(chip, addr + i, buf + i);
+ if (retval != STATUS_SUCCESS) {
+ vfree(buf);
+ set_sense_type(chip, SCSI_LUN(srb),
+ SENSE_TYPE_MEDIA_UNRECOVER_READ_ERR);
+ rtsx_trace(chip);
+ return TRANSPORT_FAILED;
+ }
+ }
+
+ len = (u8)min_t(unsigned int, scsi_bufflen(srb), len);
+ rtsx_stor_set_xfer_buf(buf, len, srb);
+ scsi_set_resid(srb, scsi_bufflen(srb) - len);
+
+ vfree(buf);
+
+ return TRANSPORT_GOOD;
+}
+
+static int write_efuse(struct scsi_cmnd *srb, struct rtsx_chip *chip)
+{
+ int retval, result = TRANSPORT_GOOD;
+ u16 val;
+ u8 addr, len, i;
+ u8 *buf;
+
+ rtsx_disable_aspm(chip);
+
+ if (chip->ss_en && (rtsx_get_stat(chip) == RTSX_STAT_SS)) {
+ rtsx_exit_ss(chip);
+ wait_timeout(100);
+ }
+ rtsx_set_stat(chip, RTSX_STAT_RUN);
+
+ addr = srb->cmnd[4];
+ len = srb->cmnd[5];
+
+ len = (u8)min_t(unsigned int, scsi_bufflen(srb), len);
+ buf = vmalloc(len);
+ if (buf == NULL) {
+ rtsx_trace(chip);
+ return TRANSPORT_ERROR;
+ }
+
+ rtsx_stor_get_xfer_buf(buf, len, srb);
+ scsi_set_resid(srb, scsi_bufflen(srb) - len);
+
+ retval = rtsx_force_power_on(chip, SSC_PDCTL);
+ if (retval != STATUS_SUCCESS) {
+ vfree(buf);
+ rtsx_trace(chip);
+ return TRANSPORT_ERROR;
+ }
+
+ if (chip->asic_code) {
+ retval = rtsx_read_phy_register(chip, 0x08, &val);
+ if (retval != STATUS_SUCCESS) {
+ vfree(buf);
+ rtsx_trace(chip);
+ return TRANSPORT_ERROR;
+ }
+
+ retval = rtsx_write_register(chip, PWR_GATE_CTRL,
+ LDO3318_PWR_MASK, LDO_OFF);
+ if (retval != STATUS_SUCCESS) {
+ vfree(buf);
+ rtsx_trace(chip);
+ return TRANSPORT_ERROR;
+ }
+
+ wait_timeout(600);
+
+ retval = rtsx_write_phy_register(chip, 0x08,
+ 0x4C00 | chip->phy_voltage);
+ if (retval != STATUS_SUCCESS) {
+ vfree(buf);
+ rtsx_trace(chip);
+ return TRANSPORT_ERROR;
+ }
+
+ retval = rtsx_write_register(chip, PWR_GATE_CTRL,
+ LDO3318_PWR_MASK, LDO_ON);
+ if (retval != STATUS_SUCCESS) {
+ vfree(buf);
+ rtsx_trace(chip);
+ return TRANSPORT_ERROR;
+ }
+
+ wait_timeout(600);
+ }
+
+ retval = card_power_on(chip, SPI_CARD);
+ if (retval != STATUS_SUCCESS) {
+ vfree(buf);
+ rtsx_trace(chip);
+ return TRANSPORT_ERROR;
+ }
+
+ wait_timeout(50);
+
+ for (i = 0; i < len; i++) {
+ retval = rtsx_write_efuse(chip, addr + i, buf[i]);
+ if (retval != STATUS_SUCCESS) {
+ set_sense_type(chip, SCSI_LUN(srb),
+ SENSE_TYPE_MEDIA_WRITE_ERR);
+ result = TRANSPORT_FAILED;
+ rtsx_trace(chip);
+ goto Exit;
+ }
+ }
+
+Exit:
+ vfree(buf);
+
+ retval = card_power_off(chip, SPI_CARD);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return TRANSPORT_ERROR;
+ }
+
+ if (chip->asic_code) {
+ retval = rtsx_write_register(chip, PWR_GATE_CTRL,
+ LDO3318_PWR_MASK, LDO_OFF);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return TRANSPORT_ERROR;
+ }
+
+ wait_timeout(600);
+
+ retval = rtsx_write_phy_register(chip, 0x08, val);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return TRANSPORT_ERROR;
+ }
+
+ retval = rtsx_write_register(chip, PWR_GATE_CTRL,
+ LDO3318_PWR_MASK, LDO_ON);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return TRANSPORT_ERROR;
+ }
+ }
+
+ return result;
+}
+
+static int read_cfg_byte(struct scsi_cmnd *srb, struct rtsx_chip *chip)
+{
+ int retval;
+ bool func_max;
+ u8 func;
+ u16 addr, len;
+ u8 *buf;
+
+ rtsx_disable_aspm(chip);
+
+ if (chip->ss_en && (rtsx_get_stat(chip) == RTSX_STAT_SS)) {
+ rtsx_exit_ss(chip);
+ wait_timeout(100);
+ }
+ rtsx_set_stat(chip, RTSX_STAT_RUN);
+
+ func = srb->cmnd[3];
+ addr = ((u16)(srb->cmnd[4]) << 8) | srb->cmnd[5];
+ len = ((u16)(srb->cmnd[6]) << 8) | srb->cmnd[7];
+
+ dev_dbg(rtsx_dev(chip), "%s: func = %d, addr = 0x%x, len = %d\n",
+ __func__, func, addr, len);
+
+ if (CHK_SDIO_EXIST(chip) && !CHK_SDIO_IGNORED(chip))
+ func_max = true;
+ else
+ func_max = false;
+
+ if (func > func_max) {
+ set_sense_type(chip, SCSI_LUN(srb),
+ SENSE_TYPE_MEDIA_INVALID_CMD_FIELD);
+ rtsx_trace(chip);
+ return TRANSPORT_FAILED;
+ }
+
+ buf = vmalloc(len);
+ if (!buf) {
+ rtsx_trace(chip);
+ return TRANSPORT_ERROR;
+ }
+
+ retval = rtsx_read_cfg_seq(chip, func, addr, buf, len);
+ if (retval != STATUS_SUCCESS) {
+ set_sense_type(chip, SCSI_LUN(srb),
+ SENSE_TYPE_MEDIA_UNRECOVER_READ_ERR);
+ vfree(buf);
+ rtsx_trace(chip);
+ return TRANSPORT_FAILED;
+ }
+
+ len = (u16)min_t(unsigned int, scsi_bufflen(srb), len);
+ rtsx_stor_set_xfer_buf(buf, len, srb);
+ scsi_set_resid(srb, scsi_bufflen(srb) - len);
+
+ vfree(buf);
+
+ return TRANSPORT_GOOD;
+}
+
+static int write_cfg_byte(struct scsi_cmnd *srb, struct rtsx_chip *chip)
+{
+ int retval;
+ bool func_max;
+ u8 func;
+ u16 addr, len;
+ u8 *buf;
+
+ rtsx_disable_aspm(chip);
+
+ if (chip->ss_en && (rtsx_get_stat(chip) == RTSX_STAT_SS)) {
+ rtsx_exit_ss(chip);
+ wait_timeout(100);
+ }
+ rtsx_set_stat(chip, RTSX_STAT_RUN);
+
+ func = srb->cmnd[3];
+ addr = ((u16)(srb->cmnd[4]) << 8) | srb->cmnd[5];
+ len = ((u16)(srb->cmnd[6]) << 8) | srb->cmnd[7];
+
+ dev_dbg(rtsx_dev(chip), "%s: func = %d, addr = 0x%x\n",
+ __func__, func, addr);
+
+ if (CHK_SDIO_EXIST(chip) && !CHK_SDIO_IGNORED(chip))
+ func_max = true;
+ else
+ func_max = false;
+
+ if (func > func_max) {
+ set_sense_type(chip, SCSI_LUN(srb),
+ SENSE_TYPE_MEDIA_INVALID_CMD_FIELD);
+ rtsx_trace(chip);
+ return TRANSPORT_FAILED;
+ }
+
+ len = (unsigned short)min_t(unsigned int, scsi_bufflen(srb), len);
+ buf = vmalloc(len);
+ if (!buf) {
+ rtsx_trace(chip);
+ return TRANSPORT_ERROR;
+ }
+
+ rtsx_stor_get_xfer_buf(buf, len, srb);
+ scsi_set_resid(srb, scsi_bufflen(srb) - len);
+
+ retval = rtsx_write_cfg_seq(chip, func, addr, buf, len);
+ if (retval != STATUS_SUCCESS) {
+ set_sense_type(chip, SCSI_LUN(srb), SENSE_TYPE_MEDIA_WRITE_ERR);
+ vfree(buf);
+ rtsx_trace(chip);
+ return TRANSPORT_FAILED;
+ }
+
+ vfree(buf);
+
+ return TRANSPORT_GOOD;
+}
+
+static int app_cmd(struct scsi_cmnd *srb, struct rtsx_chip *chip)
+{
+ int result;
+
+ switch (srb->cmnd[2]) {
+ case PP_READ10:
+ case PP_WRITE10:
+ result = read_write(srb, chip);
+ break;
+
+ case READ_HOST_REG:
+ result = read_host_reg(srb, chip);
+ break;
+
+ case WRITE_HOST_REG:
+ result = write_host_reg(srb, chip);
+ break;
+
+ case GET_VAR:
+ result = get_variable(srb, chip);
+ break;
+
+ case SET_VAR:
+ result = set_variable(srb, chip);
+ break;
+
+ case DMA_READ:
+ case DMA_WRITE:
+ result = dma_access_ring_buffer(srb, chip);
+ break;
+
+ case READ_PHY:
+ result = read_phy_register(srb, chip);
+ break;
+
+ case WRITE_PHY:
+ result = write_phy_register(srb, chip);
+ break;
+
+ case ERASE_EEPROM2:
+ result = erase_eeprom2(srb, chip);
+ break;
+
+ case READ_EEPROM2:
+ result = read_eeprom2(srb, chip);
+ break;
+
+ case WRITE_EEPROM2:
+ result = write_eeprom2(srb, chip);
+ break;
+
+ case READ_EFUSE:
+ result = read_efuse(srb, chip);
+ break;
+
+ case WRITE_EFUSE:
+ result = write_efuse(srb, chip);
+ break;
+
+ case READ_CFG:
+ result = read_cfg_byte(srb, chip);
+ break;
+
+ case WRITE_CFG:
+ result = write_cfg_byte(srb, chip);
+ break;
+
+ case SET_CHIP_MODE:
+ result = set_chip_mode(srb, chip);
+ break;
+
+ case SUIT_CMD:
+ result = suit_cmd(srb, chip);
+ break;
+
+ case GET_DEV_STATUS:
+ result = get_dev_status(srb, chip);
+ break;
+
+ default:
+ set_sense_type(chip, SCSI_LUN(srb),
+ SENSE_TYPE_MEDIA_INVALID_CMD_FIELD);
+ rtsx_trace(chip);
+ return TRANSPORT_FAILED;
+ }
+
+ return result;
+}
+
+
+static int read_status(struct scsi_cmnd *srb, struct rtsx_chip *chip)
+{
+ u8 rtsx_status[16];
+ int buf_len;
+ unsigned int lun = SCSI_LUN(srb);
+
+ rtsx_status[0] = (u8)(chip->vendor_id >> 8);
+ rtsx_status[1] = (u8)(chip->vendor_id);
+
+ rtsx_status[2] = (u8)(chip->product_id >> 8);
+ rtsx_status[3] = (u8)(chip->product_id);
+
+ rtsx_status[4] = (u8)lun;
+
+ if (CHECK_LUN_MODE(chip, SD_MS_2LUN)) {
+ if (chip->lun2card[lun] == SD_CARD)
+ rtsx_status[5] = 2;
+ else
+ rtsx_status[5] = 3;
+ } else {
+ if (chip->card_exist) {
+ if (chip->card_exist & XD_CARD)
+ rtsx_status[5] = 4;
+ else if (chip->card_exist & SD_CARD)
+ rtsx_status[5] = 2;
+ else if (chip->card_exist & MS_CARD)
+ rtsx_status[5] = 3;
+ else
+ rtsx_status[5] = 7;
+ } else {
+ rtsx_status[5] = 7;
+ }
+ }
+
+ if (CHECK_LUN_MODE(chip, SD_MS_2LUN))
+ rtsx_status[6] = 2;
+ else
+ rtsx_status[6] = 1;
+
+ rtsx_status[7] = (u8)(chip->product_id);
+ rtsx_status[8] = chip->ic_version;
+
+ if (check_card_exist(chip, lun))
+ rtsx_status[9] = 1;
+ else
+ rtsx_status[9] = 0;
+
+ if (CHECK_LUN_MODE(chip, SD_MS_2LUN))
+ rtsx_status[10] = 0;
+ else
+ rtsx_status[10] = 1;
+
+ if (CHECK_LUN_MODE(chip, SD_MS_2LUN)) {
+ if (chip->lun2card[lun] == SD_CARD)
+ rtsx_status[11] = SD_CARD;
+ else
+ rtsx_status[11] = MS_CARD;
+ } else {
+ rtsx_status[11] = XD_CARD | SD_CARD | MS_CARD;
+ }
+
+ if (check_card_ready(chip, lun))
+ rtsx_status[12] = 1;
+ else
+ rtsx_status[12] = 0;
+
+ if (get_lun_card(chip, lun) == XD_CARD) {
+ rtsx_status[13] = 0x40;
+ } else if (get_lun_card(chip, lun) == SD_CARD) {
+ struct sd_info *sd_card = &(chip->sd_card);
+
+ rtsx_status[13] = 0x20;
+ if (CHK_SD(sd_card)) {
+ if (CHK_SD_HCXC(sd_card))
+ rtsx_status[13] |= 0x04;
+ if (CHK_SD_HS(sd_card))
+ rtsx_status[13] |= 0x02;
+ } else {
+ rtsx_status[13] |= 0x08;
+ if (CHK_MMC_52M(sd_card))
+ rtsx_status[13] |= 0x02;
+ if (CHK_MMC_SECTOR_MODE(sd_card))
+ rtsx_status[13] |= 0x04;
+ }
+ } else if (get_lun_card(chip, lun) == MS_CARD) {
+ struct ms_info *ms_card = &(chip->ms_card);
+
+ if (CHK_MSPRO(ms_card)) {
+ rtsx_status[13] = 0x38;
+ if (CHK_HG8BIT(ms_card))
+ rtsx_status[13] |= 0x04;
+#ifdef SUPPORT_MSXC
+ if (CHK_MSXC(ms_card))
+ rtsx_status[13] |= 0x01;
+#endif
+ } else {
+ rtsx_status[13] = 0x30;
+ }
+ } else {
+ if (CHECK_LUN_MODE(chip, DEFAULT_SINGLE)) {
+#ifdef SUPPORT_SDIO
+ if (chip->sd_io && chip->sd_int)
+ rtsx_status[13] = 0x60;
+ else
+ rtsx_status[13] = 0x70;
+#else
+ rtsx_status[13] = 0x70;
+#endif
+ } else {
+ if (chip->lun2card[lun] == SD_CARD)
+ rtsx_status[13] = 0x20;
+ else
+ rtsx_status[13] = 0x30;
+ }
+ }
+
+ rtsx_status[14] = 0x78;
+ if (CHK_SDIO_EXIST(chip) && !CHK_SDIO_IGNORED(chip))
+ rtsx_status[15] = 0x83;
+ else
+ rtsx_status[15] = 0x82;
+
+ buf_len = min_t(unsigned int, scsi_bufflen(srb), sizeof(rtsx_status));
+ rtsx_stor_set_xfer_buf(rtsx_status, buf_len, srb);
+ scsi_set_resid(srb, scsi_bufflen(srb) - buf_len);
+
+ return TRANSPORT_GOOD;
+}
+
+static int get_card_bus_width(struct scsi_cmnd *srb, struct rtsx_chip *chip)
+{
+ unsigned int lun = SCSI_LUN(srb);
+ u8 card, bus_width;
+
+ if (!check_card_ready(chip, lun)) {
+ set_sense_type(chip, lun, SENSE_TYPE_MEDIA_NOT_PRESENT);
+ rtsx_trace(chip);
+ return TRANSPORT_FAILED;
+ }
+
+ card = get_lun_card(chip, lun);
+ if ((card == SD_CARD) || (card == MS_CARD)) {
+ bus_width = chip->card_bus_width[lun];
+ } else {
+ set_sense_type(chip, lun, SENSE_TYPE_MEDIA_UNRECOVER_READ_ERR);
+ rtsx_trace(chip);
+ return TRANSPORT_FAILED;
+ }
+
+ scsi_set_resid(srb, 0);
+ rtsx_stor_set_xfer_buf(&bus_width, scsi_bufflen(srb), srb);
+
+ return TRANSPORT_GOOD;
+}
+
+static int spi_vendor_cmd(struct scsi_cmnd *srb, struct rtsx_chip *chip)
+{
+ int result;
+ unsigned int lun = SCSI_LUN(srb);
+ u8 gpio_dir;
+
+ if (CHECK_PID(chip, 0x5208) || CHECK_PID(chip, 0x5288)) {
+ set_sense_type(chip, lun, SENSE_TYPE_MEDIA_INVALID_CMD_FIELD);
+ rtsx_trace(chip);
+ return TRANSPORT_FAILED;
+ }
+
+ rtsx_disable_aspm(chip);
+
+ if (chip->ss_en && (rtsx_get_stat(chip) == RTSX_STAT_SS)) {
+ rtsx_exit_ss(chip);
+ wait_timeout(100);
+ }
+ rtsx_set_stat(chip, RTSX_STAT_RUN);
+
+ rtsx_force_power_on(chip, SSC_PDCTL);
+
+ rtsx_read_register(chip, CARD_GPIO_DIR, &gpio_dir);
+ rtsx_write_register(chip, CARD_GPIO_DIR, 0x07, gpio_dir & 0x06);
+
+ switch (srb->cmnd[2]) {
+ case SCSI_SPI_GETSTATUS:
+ result = spi_get_status(srb, chip);
+ break;
+
+ case SCSI_SPI_SETPARAMETER:
+ result = spi_set_parameter(srb, chip);
+ break;
+
+ case SCSI_SPI_READFALSHID:
+ result = spi_read_flash_id(srb, chip);
+ break;
+
+ case SCSI_SPI_READFLASH:
+ result = spi_read_flash(srb, chip);
+ break;
+
+ case SCSI_SPI_WRITEFLASH:
+ result = spi_write_flash(srb, chip);
+ break;
+
+ case SCSI_SPI_WRITEFLASHSTATUS:
+ result = spi_write_flash_status(srb, chip);
+ break;
+
+ case SCSI_SPI_ERASEFLASH:
+ result = spi_erase_flash(srb, chip);
+ break;
+
+ default:
+ rtsx_write_register(chip, CARD_GPIO_DIR, 0x07, gpio_dir);
+
+ set_sense_type(chip, lun, SENSE_TYPE_MEDIA_INVALID_CMD_FIELD);
+ rtsx_trace(chip);
+ return TRANSPORT_FAILED;
+ }
+
+ rtsx_write_register(chip, CARD_GPIO_DIR, 0x07, gpio_dir);
+
+ if (result != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return TRANSPORT_FAILED;
+ }
+
+ return TRANSPORT_GOOD;
+}
+
+static int vendor_cmnd(struct scsi_cmnd *srb, struct rtsx_chip *chip)
+{
+ int result;
+
+ switch (srb->cmnd[1]) {
+ case READ_STATUS:
+ result = read_status(srb, chip);
+ break;
+
+ case READ_MEM:
+ result = read_mem(srb, chip);
+ break;
+
+ case WRITE_MEM:
+ result = write_mem(srb, chip);
+ break;
+
+ case READ_EEPROM:
+ result = read_eeprom(srb, chip);
+ break;
+
+ case WRITE_EEPROM:
+ result = write_eeprom(srb, chip);
+ break;
+
+ case TOGGLE_GPIO:
+ result = toggle_gpio_cmd(srb, chip);
+ break;
+
+ case GET_SD_CSD:
+ result = get_sd_csd(srb, chip);
+ break;
+
+ case GET_BUS_WIDTH:
+ result = get_card_bus_width(srb, chip);
+ break;
+
+#ifdef _MSG_TRACE
+ case TRACE_MSG:
+ result = trace_msg_cmd(srb, chip);
+ break;
+#endif
+
+ case SCSI_APP_CMD:
+ result = app_cmd(srb, chip);
+ break;
+
+ case SPI_VENDOR_COMMAND:
+ result = spi_vendor_cmd(srb, chip);
+ break;
+
+ default:
+ set_sense_type(chip, SCSI_LUN(srb),
+ SENSE_TYPE_MEDIA_INVALID_CMD_FIELD);
+ rtsx_trace(chip);
+ return TRANSPORT_FAILED;
+ }
+
+ return result;
+}
+
+#if !defined(LED_AUTO_BLINK) && !defined(REGULAR_BLINK)
+void led_shine(struct scsi_cmnd *srb, struct rtsx_chip *chip)
+{
+ unsigned int lun = SCSI_LUN(srb);
+ u16 sec_cnt;
+
+ if ((srb->cmnd[0] == READ_10) || (srb->cmnd[0] == WRITE_10))
+ sec_cnt = ((u16)(srb->cmnd[7]) << 8) | srb->cmnd[8];
+ else if ((srb->cmnd[0] == READ_6) || (srb->cmnd[0] == WRITE_6))
+ sec_cnt = srb->cmnd[4];
+ else
+ return;
+
+ if (chip->rw_cap[lun] >= GPIO_TOGGLE_THRESHOLD) {
+ toggle_gpio(chip, LED_GPIO);
+ chip->rw_cap[lun] = 0;
+ } else {
+ chip->rw_cap[lun] += sec_cnt;
+ }
+}
+#endif
+
+static int ms_format_cmnd(struct scsi_cmnd *srb, struct rtsx_chip *chip)
+{
+ struct ms_info *ms_card = &(chip->ms_card);
+ unsigned int lun = SCSI_LUN(srb);
+ bool quick_format;
+ int retval;
+
+ if (get_lun_card(chip, lun) != MS_CARD) {
+ set_sense_type(chip, lun, SENSE_TYPE_MEDIA_LUN_NOT_SUPPORT);
+ rtsx_trace(chip);
+ return TRANSPORT_FAILED;
+ }
+
+ if ((srb->cmnd[3] != 0x4D) || (srb->cmnd[4] != 0x47) ||
+ (srb->cmnd[5] != 0x66) || (srb->cmnd[6] != 0x6D) ||
+ (srb->cmnd[7] != 0x74)) {
+ set_sense_type(chip, lun, SENSE_TYPE_MEDIA_INVALID_CMD_FIELD);
+ rtsx_trace(chip);
+ return TRANSPORT_FAILED;
+ }
+
+ rtsx_disable_aspm(chip);
+
+ if (chip->ss_en && (rtsx_get_stat(chip) == RTSX_STAT_SS)) {
+ rtsx_exit_ss(chip);
+ wait_timeout(100);
+
+ if (!check_card_ready(chip, lun) ||
+ (get_card_size(chip, lun) == 0)) {
+ set_sense_type(chip, lun, SENSE_TYPE_MEDIA_NOT_PRESENT);
+ rtsx_trace(chip);
+ return TRANSPORT_FAILED;
+ }
+ }
+ rtsx_set_stat(chip, RTSX_STAT_RUN);
+
+ if (srb->cmnd[8] & 0x01)
+ quick_format = false;
+ else
+ quick_format = true;
+
+ if (!(chip->card_ready & MS_CARD)) {
+ set_sense_type(chip, lun, SENSE_TYPE_MEDIA_NOT_PRESENT);
+ rtsx_trace(chip);
+ return TRANSPORT_FAILED;
+ }
+
+ if (chip->card_wp & MS_CARD) {
+ set_sense_type(chip, lun, SENSE_TYPE_MEDIA_WRITE_PROTECT);
+ rtsx_trace(chip);
+ return TRANSPORT_FAILED;
+ }
+
+ if (!CHK_MSPRO(ms_card)) {
+ set_sense_type(chip, lun, SENSE_TYPE_MEDIA_LUN_NOT_SUPPORT);
+ rtsx_trace(chip);
+ return TRANSPORT_FAILED;
+ }
+
+ retval = mspro_format(srb, chip, MS_SHORT_DATA_LEN, quick_format);
+ if (retval != STATUS_SUCCESS) {
+ set_sense_type(chip, lun, SENSE_TYPE_FORMAT_CMD_FAILED);
+ rtsx_trace(chip);
+ return TRANSPORT_FAILED;
+ }
+
+ scsi_set_resid(srb, 0);
+ return TRANSPORT_GOOD;
+}
+
+#ifdef SUPPORT_PCGL_1P18
+static int get_ms_information(struct scsi_cmnd *srb, struct rtsx_chip *chip)
+{
+ struct ms_info *ms_card = &(chip->ms_card);
+ unsigned int lun = SCSI_LUN(srb);
+ u8 dev_info_id, data_len;
+ u8 *buf;
+ unsigned int buf_len;
+ int i;
+
+ if (!check_card_ready(chip, lun)) {
+ set_sense_type(chip, lun, SENSE_TYPE_MEDIA_NOT_PRESENT);
+ rtsx_trace(chip);
+ return TRANSPORT_FAILED;
+ }
+ if (get_lun_card(chip, lun) != MS_CARD) {
+ set_sense_type(chip, lun, SENSE_TYPE_MEDIA_LUN_NOT_SUPPORT);
+ rtsx_trace(chip);
+ return TRANSPORT_FAILED;
+ }
+
+ if ((srb->cmnd[2] != 0xB0) || (srb->cmnd[4] != 0x4D) ||
+ (srb->cmnd[5] != 0x53) || (srb->cmnd[6] != 0x49) ||
+ (srb->cmnd[7] != 0x44)) {
+ set_sense_type(chip, lun, SENSE_TYPE_MEDIA_INVALID_CMD_FIELD);
+ rtsx_trace(chip);
+ return TRANSPORT_FAILED;
+ }
+
+ dev_info_id = srb->cmnd[3];
+ if ((CHK_MSXC(ms_card) && (dev_info_id == 0x10)) ||
+ (!CHK_MSXC(ms_card) && (dev_info_id == 0x13)) ||
+ !CHK_MSPRO(ms_card)) {
+ set_sense_type(chip, lun, SENSE_TYPE_MEDIA_INVALID_CMD_FIELD);
+ rtsx_trace(chip);
+ return TRANSPORT_FAILED;
+ }
+
+ if (dev_info_id == 0x15)
+ buf_len = data_len = 0x3A;
+ else
+ buf_len = data_len = 0x6A;
+
+ buf = kmalloc(buf_len, GFP_KERNEL);
+ if (!buf) {
+ rtsx_trace(chip);
+ return TRANSPORT_ERROR;
+ }
+
+ i = 0;
+ /* GET Memory Stick Media Information Response Header */
+ buf[i++] = 0x00; /* Data length MSB */
+ buf[i++] = data_len; /* Data length LSB */
+ /* Device Information Type Code */
+ if (CHK_MSXC(ms_card))
+ buf[i++] = 0x03;
+ else
+ buf[i++] = 0x02;
+
+ /* SGM bit */
+ buf[i++] = 0x01;
+ /* Reserved */
+ buf[i++] = 0x00;
+ buf[i++] = 0x00;
+ buf[i++] = 0x00;
+ /* Number of Device Information */
+ buf[i++] = 0x01;
+
+ /* Device Information Body */
+
+ /* Device Information ID Number */
+ buf[i++] = dev_info_id;
+ /* Device Information Length */
+ if (dev_info_id == 0x15)
+ data_len = 0x31;
+ else
+ data_len = 0x61;
+
+ buf[i++] = 0x00; /* Data length MSB */
+ buf[i++] = data_len; /* Data length LSB */
+ /* Valid Bit */
+ buf[i++] = 0x80;
+ if ((dev_info_id == 0x10) || (dev_info_id == 0x13)) {
+ /* System Information */
+ memcpy(buf+i, ms_card->raw_sys_info, 96);
+ } else {
+ /* Model Name */
+ memcpy(buf+i, ms_card->raw_model_name, 48);
+ }
+
+ rtsx_stor_set_xfer_buf(buf, buf_len, srb);
+
+ if (dev_info_id == 0x15)
+ scsi_set_resid(srb, scsi_bufflen(srb)-0x3C);
+ else
+ scsi_set_resid(srb, scsi_bufflen(srb)-0x6C);
+
+ kfree(buf);
+ return STATUS_SUCCESS;
+}
+#endif
+
+static int ms_sp_cmnd(struct scsi_cmnd *srb, struct rtsx_chip *chip)
+{
+ int retval = TRANSPORT_ERROR;
+
+ if (srb->cmnd[2] == MS_FORMAT)
+ retval = ms_format_cmnd(srb, chip);
+#ifdef SUPPORT_PCGL_1P18
+ else if (srb->cmnd[2] == GET_MS_INFORMATION)
+ retval = get_ms_information(srb, chip);
+#endif
+
+ return retval;
+}
+
+#ifdef SUPPORT_CPRM
+static int sd_extention_cmnd(struct scsi_cmnd *srb, struct rtsx_chip *chip)
+{
+ unsigned int lun = SCSI_LUN(srb);
+ int result;
+
+ rtsx_disable_aspm(chip);
+
+ if (chip->ss_en && (rtsx_get_stat(chip) == RTSX_STAT_SS)) {
+ rtsx_exit_ss(chip);
+ wait_timeout(100);
+ }
+ rtsx_set_stat(chip, RTSX_STAT_RUN);
+
+ sd_cleanup_work(chip);
+
+ if (!check_card_ready(chip, lun)) {
+ set_sense_type(chip, lun, SENSE_TYPE_MEDIA_NOT_PRESENT);
+ rtsx_trace(chip);
+ return TRANSPORT_FAILED;
+ }
+ if (get_lun_card(chip, lun) != SD_CARD) {
+ set_sense_type(chip, lun, SENSE_TYPE_MEDIA_LUN_NOT_SUPPORT);
+ rtsx_trace(chip);
+ return TRANSPORT_FAILED;
+ }
+
+ switch (srb->cmnd[0]) {
+ case SD_PASS_THRU_MODE:
+ result = sd_pass_thru_mode(srb, chip);
+ break;
+
+ case SD_EXECUTE_NO_DATA:
+ result = sd_execute_no_data(srb, chip);
+ break;
+
+ case SD_EXECUTE_READ:
+ result = sd_execute_read_data(srb, chip);
+ break;
+
+ case SD_EXECUTE_WRITE:
+ result = sd_execute_write_data(srb, chip);
+ break;
+
+ case SD_GET_RSP:
+ result = sd_get_cmd_rsp(srb, chip);
+ break;
+
+ case SD_HW_RST:
+ result = sd_hw_rst(srb, chip);
+ break;
+
+ default:
+ set_sense_type(chip, lun, SENSE_TYPE_MEDIA_INVALID_CMD_FIELD);
+ rtsx_trace(chip);
+ return TRANSPORT_FAILED;
+ }
+
+ return result;
+}
+#endif
+
+#ifdef SUPPORT_MAGIC_GATE
+static int mg_report_key(struct scsi_cmnd *srb, struct rtsx_chip *chip)
+{
+ struct ms_info *ms_card = &(chip->ms_card);
+ unsigned int lun = SCSI_LUN(srb);
+ int retval;
+ u8 key_format;
+
+ rtsx_disable_aspm(chip);
+
+ if (chip->ss_en && (rtsx_get_stat(chip) == RTSX_STAT_SS)) {
+ rtsx_exit_ss(chip);
+ wait_timeout(100);
+ }
+ rtsx_set_stat(chip, RTSX_STAT_RUN);
+
+ ms_cleanup_work(chip);
+
+ if (!check_card_ready(chip, lun)) {
+ set_sense_type(chip, lun, SENSE_TYPE_MEDIA_NOT_PRESENT);
+ rtsx_trace(chip);
+ return TRANSPORT_FAILED;
+ }
+ if (get_lun_card(chip, lun) != MS_CARD) {
+ set_sense_type(chip, lun, SENSE_TYPE_MEDIA_LUN_NOT_SUPPORT);
+ rtsx_trace(chip);
+ return TRANSPORT_FAILED;
+ }
+
+ if (srb->cmnd[7] != KC_MG_R_PRO) {
+ set_sense_type(chip, lun, SENSE_TYPE_MEDIA_INVALID_CMD_FIELD);
+ rtsx_trace(chip);
+ return TRANSPORT_FAILED;
+ }
+
+ if (!CHK_MSPRO(ms_card)) {
+ set_sense_type(chip, lun, SENSE_TYPE_MG_INCOMPATIBLE_MEDIUM);
+ rtsx_trace(chip);
+ return TRANSPORT_FAILED;
+ }
+
+ key_format = srb->cmnd[10] & 0x3F;
+ dev_dbg(rtsx_dev(chip), "key_format = 0x%x\n", key_format);
+
+ switch (key_format) {
+ case KF_GET_LOC_EKB:
+ if ((scsi_bufflen(srb) == 0x41C) &&
+ (srb->cmnd[8] == 0x04) &&
+ (srb->cmnd[9] == 0x1C)) {
+ retval = mg_get_local_EKB(srb, chip);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return TRANSPORT_FAILED;
+ }
+
+ } else {
+ set_sense_type(chip, lun,
+ SENSE_TYPE_MEDIA_INVALID_CMD_FIELD);
+ rtsx_trace(chip);
+ return TRANSPORT_FAILED;
+ }
+ break;
+
+ case KF_RSP_CHG:
+ if ((scsi_bufflen(srb) == 0x24) &&
+ (srb->cmnd[8] == 0x00) &&
+ (srb->cmnd[9] == 0x24)) {
+ retval = mg_get_rsp_chg(srb, chip);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return TRANSPORT_FAILED;
+ }
+
+ } else {
+ set_sense_type(chip, lun,
+ SENSE_TYPE_MEDIA_INVALID_CMD_FIELD);
+ rtsx_trace(chip);
+ return TRANSPORT_FAILED;
+ }
+ break;
+
+ case KF_GET_ICV:
+ ms_card->mg_entry_num = srb->cmnd[5];
+ if ((scsi_bufflen(srb) == 0x404) &&
+ (srb->cmnd[8] == 0x04) &&
+ (srb->cmnd[9] == 0x04) &&
+ (srb->cmnd[2] == 0x00) &&
+ (srb->cmnd[3] == 0x00) &&
+ (srb->cmnd[4] == 0x00) &&
+ (srb->cmnd[5] < 32)) {
+ retval = mg_get_ICV(srb, chip);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return TRANSPORT_FAILED;
+ }
+
+ } else {
+ set_sense_type(chip, lun,
+ SENSE_TYPE_MEDIA_INVALID_CMD_FIELD);
+ rtsx_trace(chip);
+ return TRANSPORT_FAILED;
+ }
+ break;
+
+ default:
+ set_sense_type(chip, lun, SENSE_TYPE_MEDIA_INVALID_CMD_FIELD);
+ rtsx_trace(chip);
+ return TRANSPORT_FAILED;
+ }
+
+ scsi_set_resid(srb, 0);
+ return TRANSPORT_GOOD;
+}
+
+static int mg_send_key(struct scsi_cmnd *srb, struct rtsx_chip *chip)
+{
+ struct ms_info *ms_card = &(chip->ms_card);
+ unsigned int lun = SCSI_LUN(srb);
+ int retval;
+ u8 key_format;
+
+ rtsx_disable_aspm(chip);
+
+ if (chip->ss_en && (rtsx_get_stat(chip) == RTSX_STAT_SS)) {
+ rtsx_exit_ss(chip);
+ wait_timeout(100);
+ }
+ rtsx_set_stat(chip, RTSX_STAT_RUN);
+
+ ms_cleanup_work(chip);
+
+ if (!check_card_ready(chip, lun)) {
+ set_sense_type(chip, lun, SENSE_TYPE_MEDIA_NOT_PRESENT);
+ rtsx_trace(chip);
+ return TRANSPORT_FAILED;
+ }
+ if (check_card_wp(chip, lun)) {
+ set_sense_type(chip, lun, SENSE_TYPE_MEDIA_WRITE_PROTECT);
+ rtsx_trace(chip);
+ return TRANSPORT_FAILED;
+ }
+ if (get_lun_card(chip, lun) != MS_CARD) {
+ set_sense_type(chip, lun, SENSE_TYPE_MEDIA_LUN_NOT_SUPPORT);
+ rtsx_trace(chip);
+ return TRANSPORT_FAILED;
+ }
+
+ if (srb->cmnd[7] != KC_MG_R_PRO) {
+ set_sense_type(chip, lun, SENSE_TYPE_MEDIA_INVALID_CMD_FIELD);
+ rtsx_trace(chip);
+ return TRANSPORT_FAILED;
+ }
+
+ if (!CHK_MSPRO(ms_card)) {
+ set_sense_type(chip, lun, SENSE_TYPE_MG_INCOMPATIBLE_MEDIUM);
+ rtsx_trace(chip);
+ return TRANSPORT_FAILED;
+ }
+
+ key_format = srb->cmnd[10] & 0x3F;
+ dev_dbg(rtsx_dev(chip), "key_format = 0x%x\n", key_format);
+
+ switch (key_format) {
+ case KF_SET_LEAF_ID:
+ if ((scsi_bufflen(srb) == 0x0C) &&
+ (srb->cmnd[8] == 0x00) &&
+ (srb->cmnd[9] == 0x0C)) {
+ retval = mg_set_leaf_id(srb, chip);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return TRANSPORT_FAILED;
+ }
+
+ } else {
+ set_sense_type(chip, lun,
+ SENSE_TYPE_MEDIA_INVALID_CMD_FIELD);
+ rtsx_trace(chip);
+ return TRANSPORT_FAILED;
+ }
+ break;
+
+ case KF_CHG_HOST:
+ if ((scsi_bufflen(srb) == 0x0C) &&
+ (srb->cmnd[8] == 0x00) &&
+ (srb->cmnd[9] == 0x0C)) {
+ retval = mg_chg(srb, chip);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return TRANSPORT_FAILED;
+ }
+
+ } else {
+ set_sense_type(chip, lun,
+ SENSE_TYPE_MEDIA_INVALID_CMD_FIELD);
+ rtsx_trace(chip);
+ return TRANSPORT_FAILED;
+ }
+ break;
+
+ case KF_RSP_HOST:
+ if ((scsi_bufflen(srb) == 0x0C) &&
+ (srb->cmnd[8] == 0x00) &&
+ (srb->cmnd[9] == 0x0C)) {
+ retval = mg_rsp(srb, chip);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return TRANSPORT_FAILED;
+ }
+
+ } else {
+ set_sense_type(chip, lun,
+ SENSE_TYPE_MEDIA_INVALID_CMD_FIELD);
+ rtsx_trace(chip);
+ return TRANSPORT_FAILED;
+ }
+ break;
+
+ case KF_SET_ICV:
+ ms_card->mg_entry_num = srb->cmnd[5];
+ if ((scsi_bufflen(srb) == 0x404) &&
+ (srb->cmnd[8] == 0x04) &&
+ (srb->cmnd[9] == 0x04) &&
+ (srb->cmnd[2] == 0x00) &&
+ (srb->cmnd[3] == 0x00) &&
+ (srb->cmnd[4] == 0x00) &&
+ (srb->cmnd[5] < 32)) {
+ retval = mg_set_ICV(srb, chip);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return TRANSPORT_FAILED;
+ }
+
+ } else {
+ set_sense_type(chip, lun,
+ SENSE_TYPE_MEDIA_INVALID_CMD_FIELD);
+ rtsx_trace(chip);
+ return TRANSPORT_FAILED;
+ }
+ break;
+
+ default:
+ set_sense_type(chip, lun, SENSE_TYPE_MEDIA_INVALID_CMD_FIELD);
+ rtsx_trace(chip);
+ return TRANSPORT_FAILED;
+ }
+
+ scsi_set_resid(srb, 0);
+ return TRANSPORT_GOOD;
+}
+#endif
+
+int rtsx_scsi_handler(struct scsi_cmnd *srb, struct rtsx_chip *chip)
+{
+#ifdef SUPPORT_SD_LOCK
+ struct sd_info *sd_card = &(chip->sd_card);
+#endif
+ struct ms_info *ms_card = &(chip->ms_card);
+ unsigned int lun = SCSI_LUN(srb);
+ int result;
+
+#ifdef SUPPORT_SD_LOCK
+ if (sd_card->sd_erase_status) {
+ /* Block all SCSI command except for
+ * REQUEST_SENSE and rs_ppstatus
+ */
+ if (!((srb->cmnd[0] == VENDOR_CMND) &&
+ (srb->cmnd[1] == SCSI_APP_CMD) &&
+ (srb->cmnd[2] == GET_DEV_STATUS)) &&
+ (srb->cmnd[0] != REQUEST_SENSE)) {
+ /* Logical Unit Not Ready Format in Progress */
+ set_sense_data(chip, lun, CUR_ERR,
+ 0x02, 0, 0x04, 0x04, 0, 0);
+ rtsx_trace(chip);
+ return TRANSPORT_FAILED;
+ }
+ }
+#endif
+
+ if ((get_lun_card(chip, lun) == MS_CARD) &&
+ (ms_card->format_status == FORMAT_IN_PROGRESS)) {
+ if ((srb->cmnd[0] != REQUEST_SENSE) &&
+ (srb->cmnd[0] != INQUIRY)) {
+ /* Logical Unit Not Ready Format in Progress */
+ set_sense_data(chip, lun, CUR_ERR, 0x02, 0, 0x04, 0x04,
+ 0, (u16)(ms_card->progress));
+ rtsx_trace(chip);
+ return TRANSPORT_FAILED;
+ }
+ }
+
+ switch (srb->cmnd[0]) {
+ case READ_10:
+ case WRITE_10:
+ case READ_6:
+ case WRITE_6:
+ result = read_write(srb, chip);
+#if !defined(LED_AUTO_BLINK) && !defined(REGULAR_BLINK)
+ led_shine(srb, chip);
+#endif
+ break;
+
+ case TEST_UNIT_READY:
+ result = test_unit_ready(srb, chip);
+ break;
+
+ case INQUIRY:
+ result = inquiry(srb, chip);
+ break;
+
+ case READ_CAPACITY:
+ result = read_capacity(srb, chip);
+ break;
+
+ case START_STOP:
+ result = start_stop_unit(srb, chip);
+ break;
+
+ case ALLOW_MEDIUM_REMOVAL:
+ result = allow_medium_removal(srb, chip);
+ break;
+
+ case REQUEST_SENSE:
+ result = request_sense(srb, chip);
+ break;
+
+ case MODE_SENSE:
+ case MODE_SENSE_10:
+ result = mode_sense(srb, chip);
+ break;
+
+ case 0x23:
+ result = read_format_capacity(srb, chip);
+ break;
+
+ case VENDOR_CMND:
+ result = vendor_cmnd(srb, chip);
+ break;
+
+ case MS_SP_CMND:
+ result = ms_sp_cmnd(srb, chip);
+ break;
+
+#ifdef SUPPORT_CPRM
+ case SD_PASS_THRU_MODE:
+ case SD_EXECUTE_NO_DATA:
+ case SD_EXECUTE_READ:
+ case SD_EXECUTE_WRITE:
+ case SD_GET_RSP:
+ case SD_HW_RST:
+ result = sd_extention_cmnd(srb, chip);
+ break;
+#endif
+
+#ifdef SUPPORT_MAGIC_GATE
+ case CMD_MSPRO_MG_RKEY:
+ result = mg_report_key(srb, chip);
+ break;
+
+ case CMD_MSPRO_MG_SKEY:
+ result = mg_send_key(srb, chip);
+ break;
+#endif
+
+ case FORMAT_UNIT:
+ case MODE_SELECT:
+ case VERIFY:
+ result = TRANSPORT_GOOD;
+ break;
+
+ default:
+ set_sense_type(chip, lun, SENSE_TYPE_MEDIA_INVALID_CMD_FIELD);
+ result = TRANSPORT_FAILED;
+ }
+
+ return result;
+}
diff --git a/drivers/staging/rts5208/rtsx_scsi.h b/drivers/staging/rts5208/rtsx_scsi.h
new file mode 100644
index 000000000..03dd76d6c
--- /dev/null
+++ b/drivers/staging/rts5208/rtsx_scsi.h
@@ -0,0 +1,143 @@
+/* Driver for Realtek PCI-Express card reader
+ * Header file
+ *
+ * Copyright(c) 2009-2013 Realtek Semiconductor Corp. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, 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, see <http://www.gnu.org/licenses/>.
+ *
+ * Author:
+ * Wei WANG (wei_wang@realsil.com.cn)
+ * Micky Ching (micky_ching@realsil.com.cn)
+ */
+
+#ifndef __REALTEK_RTSX_SCSI_H
+#define __REALTEK_RTSX_SCSI_H
+
+#include "rtsx.h"
+#include "rtsx_chip.h"
+
+#define MS_SP_CMND 0xFA
+#define MS_FORMAT 0xA0
+#define GET_MS_INFORMATION 0xB0
+
+#define VENDOR_CMND 0xF0
+
+#define READ_STATUS 0x09
+
+#define READ_EEPROM 0x04
+#define WRITE_EEPROM 0x05
+#define READ_MEM 0x0D
+#define WRITE_MEM 0x0E
+#define GET_BUS_WIDTH 0x13
+#define GET_SD_CSD 0x14
+#define TOGGLE_GPIO 0x15
+#define TRACE_MSG 0x18
+
+#define SCSI_APP_CMD 0x10
+
+#define PP_READ10 0x1A
+#define PP_WRITE10 0x0A
+#define READ_HOST_REG 0x1D
+#define WRITE_HOST_REG 0x0D
+#define SET_VAR 0x05
+#define GET_VAR 0x15
+#define DMA_READ 0x16
+#define DMA_WRITE 0x06
+#define GET_DEV_STATUS 0x10
+#define SET_CHIP_MODE 0x27
+#define SUIT_CMD 0xE0
+#define WRITE_PHY 0x07
+#define READ_PHY 0x17
+#define WRITE_EEPROM2 0x03
+#define READ_EEPROM2 0x13
+#define ERASE_EEPROM2 0x23
+#define WRITE_EFUSE 0x04
+#define READ_EFUSE 0x14
+#define WRITE_CFG 0x0E
+#define READ_CFG 0x1E
+
+#define SPI_VENDOR_COMMAND 0x1C
+
+#define SCSI_SPI_GETSTATUS 0x00
+#define SCSI_SPI_SETPARAMETER 0x01
+#define SCSI_SPI_READFALSHID 0x02
+#define SCSI_SPI_READFLASH 0x03
+#define SCSI_SPI_WRITEFLASH 0x04
+#define SCSI_SPI_WRITEFLASHSTATUS 0x05
+#define SCSI_SPI_ERASEFLASH 0x06
+
+#define INIT_BATCHCMD 0x41
+#define ADD_BATCHCMD 0x42
+#define SEND_BATCHCMD 0x43
+#define GET_BATCHRSP 0x44
+
+#define CHIP_NORMALMODE 0x00
+#define CHIP_DEBUGMODE 0x01
+
+/* SD Pass Through Command Extension */
+#define SD_PASS_THRU_MODE 0xD0
+#define SD_EXECUTE_NO_DATA 0xD1
+#define SD_EXECUTE_READ 0xD2
+#define SD_EXECUTE_WRITE 0xD3
+#define SD_GET_RSP 0xD4
+#define SD_HW_RST 0xD6
+
+#ifdef SUPPORT_MAGIC_GATE
+#define CMD_MSPRO_MG_RKEY 0xA4 /* Report Key Command */
+#define CMD_MSPRO_MG_SKEY 0xA3 /* Send Key Command */
+
+/* CBWCB field: key class */
+#define KC_MG_R_PRO 0xBE /* MG-R PRO*/
+
+/* CBWCB field: key format */
+#define KF_SET_LEAF_ID 0x31 /* Set Leaf ID */
+#define KF_GET_LOC_EKB 0x32 /* Get Local EKB */
+#define KF_CHG_HOST 0x33 /* Challenge (host) */
+#define KF_RSP_CHG 0x34 /* Response and Challenge (device) */
+#define KF_RSP_HOST 0x35 /* Response (host) */
+#define KF_GET_ICV 0x36 /* Get ICV */
+#define KF_SET_ICV 0x37 /* SSet ICV */
+#endif
+
+/* Sense type */
+#define SENSE_TYPE_NO_SENSE 0
+#define SENSE_TYPE_MEDIA_CHANGE 1
+#define SENSE_TYPE_MEDIA_NOT_PRESENT 2
+#define SENSE_TYPE_MEDIA_LBA_OVER_RANGE 3
+#define SENSE_TYPE_MEDIA_LUN_NOT_SUPPORT 4
+#define SENSE_TYPE_MEDIA_WRITE_PROTECT 5
+#define SENSE_TYPE_MEDIA_INVALID_CMD_FIELD 6
+#define SENSE_TYPE_MEDIA_UNRECOVER_READ_ERR 7
+#define SENSE_TYPE_MEDIA_WRITE_ERR 8
+#define SENSE_TYPE_FORMAT_IN_PROGRESS 9
+#define SENSE_TYPE_FORMAT_CMD_FAILED 10
+#ifdef SUPPORT_MAGIC_GATE
+#define SENSE_TYPE_MG_KEY_FAIL_NOT_ESTAB 0x0b
+#define SENSE_TYPE_MG_KEY_FAIL_NOT_AUTHEN 0x0c
+#define SENSE_TYPE_MG_INCOMPATIBLE_MEDIUM 0x0d
+#define SENSE_TYPE_MG_WRITE_ERR 0x0e
+#endif
+#ifdef SUPPORT_SD_LOCK
+/* FOR Locked SD card*/
+#define SENSE_TYPE_MEDIA_READ_FORBIDDEN 0x10
+#endif
+
+void scsi_show_command(struct rtsx_chip *chip);
+void set_sense_type(struct rtsx_chip *chip, unsigned int lun, int sense_type);
+void set_sense_data(struct rtsx_chip *chip, unsigned int lun, u8 err_code,
+ u8 sense_key, u32 info, u8 asc, u8 ascq,
+ u8 sns_key_info0, u16 sns_key_info1);
+int rtsx_scsi_handler(struct scsi_cmnd *srb, struct rtsx_chip *chip);
+
+#endif /* __REALTEK_RTSX_SCSI_H */
diff --git a/drivers/staging/rts5208/rtsx_sys.h b/drivers/staging/rts5208/rtsx_sys.h
new file mode 100644
index 000000000..0b6b4d4f1
--- /dev/null
+++ b/drivers/staging/rts5208/rtsx_sys.h
@@ -0,0 +1,50 @@
+/* Driver for Realtek PCI-Express card reader
+ * Header file
+ *
+ * Copyright(c) 2009-2013 Realtek Semiconductor Corp. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, 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, see <http://www.gnu.org/licenses/>.
+ *
+ * Author:
+ * Wei WANG (wei_wang@realsil.com.cn)
+ * Micky Ching (micky_ching@realsil.com.cn)
+ */
+
+#ifndef __RTSX_SYS_H
+#define __RTSX_SYS_H
+
+#include "rtsx.h"
+#include "rtsx_chip.h"
+#include "rtsx_card.h"
+
+typedef dma_addr_t ULONG_PTR;
+
+static inline void rtsx_exclusive_enter_ss(struct rtsx_chip *chip)
+{
+ struct rtsx_dev *dev = chip->rtsx;
+
+ spin_lock(&(dev->reg_lock));
+ rtsx_enter_ss(chip);
+ spin_unlock(&(dev->reg_lock));
+}
+
+static inline void rtsx_reset_detected_cards(struct rtsx_chip *chip, int flag)
+{
+ rtsx_reset_cards(chip);
+}
+
+#define RTSX_MSG_IN_INT(x)
+
+#endif /* __RTSX_SYS_H */
+
diff --git a/drivers/staging/rts5208/rtsx_transport.c b/drivers/staging/rts5208/rtsx_transport.c
new file mode 100644
index 000000000..f27491e80
--- /dev/null
+++ b/drivers/staging/rts5208/rtsx_transport.c
@@ -0,0 +1,775 @@
+/* Driver for Realtek PCI-Express card reader
+ *
+ * Copyright(c) 2009-2013 Realtek Semiconductor Corp. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, 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, see <http://www.gnu.org/licenses/>.
+ *
+ * Author:
+ * Wei WANG (wei_wang@realsil.com.cn)
+ * Micky Ching (micky_ching@realsil.com.cn)
+ */
+
+#include <linux/blkdev.h>
+#include <linux/kthread.h>
+#include <linux/sched.h>
+
+#include "rtsx.h"
+
+/***********************************************************************
+ * Scatter-gather transfer buffer access routines
+ ***********************************************************************/
+
+/* Copy a buffer of length buflen to/from the srb's transfer buffer.
+ * (Note: for scatter-gather transfers (srb->use_sg > 0), srb->request_buffer
+ * points to a list of s-g entries and we ignore srb->request_bufflen.
+ * For non-scatter-gather transfers, srb->request_buffer points to the
+ * transfer buffer itself and srb->request_bufflen is the buffer's length.)
+ * Update the *index and *offset variables so that the next copy will
+ * pick up from where this one left off. */
+
+unsigned int rtsx_stor_access_xfer_buf(unsigned char *buffer,
+ unsigned int buflen, struct scsi_cmnd *srb, unsigned int *index,
+ unsigned int *offset, enum xfer_buf_dir dir)
+{
+ unsigned int cnt;
+
+ /* If not using scatter-gather, just transfer the data directly.
+ * Make certain it will fit in the available buffer space. */
+ if (scsi_sg_count(srb) == 0) {
+ if (*offset >= scsi_bufflen(srb))
+ return 0;
+ cnt = min(buflen, scsi_bufflen(srb) - *offset);
+ if (dir == TO_XFER_BUF)
+ memcpy((unsigned char *) scsi_sglist(srb) + *offset,
+ buffer, cnt);
+ else
+ memcpy(buffer, (unsigned char *) scsi_sglist(srb) +
+ *offset, cnt);
+ *offset += cnt;
+
+ /* Using scatter-gather. We have to go through the list one entry
+ * at a time. Each s-g entry contains some number of pages, and
+ * each page has to be kmap()'ed separately. If the page is already
+ * in kernel-addressable memory then kmap() will return its address.
+ * If the page is not directly accessible -- such as a user buffer
+ * located in high memory -- then kmap() will map it to a temporary
+ * position in the kernel's virtual address space. */
+ } else {
+ struct scatterlist *sg =
+ (struct scatterlist *) scsi_sglist(srb)
+ + *index;
+
+ /* This loop handles a single s-g list entry, which may
+ * include multiple pages. Find the initial page structure
+ * and the starting offset within the page, and update
+ * the *offset and *index values for the next loop. */
+ cnt = 0;
+ while (cnt < buflen && *index < scsi_sg_count(srb)) {
+ struct page *page = sg_page(sg) +
+ ((sg->offset + *offset) >> PAGE_SHIFT);
+ unsigned int poff =
+ (sg->offset + *offset) & (PAGE_SIZE-1);
+ unsigned int sglen = sg->length - *offset;
+
+ if (sglen > buflen - cnt) {
+
+ /* Transfer ends within this s-g entry */
+ sglen = buflen - cnt;
+ *offset += sglen;
+ } else {
+
+ /* Transfer continues to next s-g entry */
+ *offset = 0;
+ ++*index;
+ ++sg;
+ }
+
+ /* Transfer the data for all the pages in this
+ * s-g entry. For each page: call kmap(), do the
+ * transfer, and call kunmap() immediately after. */
+ while (sglen > 0) {
+ unsigned int plen = min(sglen, (unsigned int)
+ PAGE_SIZE - poff);
+ unsigned char *ptr = kmap(page);
+
+ if (dir == TO_XFER_BUF)
+ memcpy(ptr + poff, buffer + cnt, plen);
+ else
+ memcpy(buffer + cnt, ptr + poff, plen);
+ kunmap(page);
+
+ /* Start at the beginning of the next page */
+ poff = 0;
+ ++page;
+ cnt += plen;
+ sglen -= plen;
+ }
+ }
+ }
+
+ /* Return the amount actually transferred */
+ return cnt;
+}
+
+/* Store the contents of buffer into srb's transfer buffer and set the
+* SCSI residue. */
+void rtsx_stor_set_xfer_buf(unsigned char *buffer,
+ unsigned int buflen, struct scsi_cmnd *srb)
+{
+ unsigned int index = 0, offset = 0;
+
+ rtsx_stor_access_xfer_buf(buffer, buflen, srb, &index, &offset,
+ TO_XFER_BUF);
+ if (buflen < scsi_bufflen(srb))
+ scsi_set_resid(srb, scsi_bufflen(srb) - buflen);
+}
+
+void rtsx_stor_get_xfer_buf(unsigned char *buffer,
+ unsigned int buflen, struct scsi_cmnd *srb)
+{
+ unsigned int index = 0, offset = 0;
+
+ rtsx_stor_access_xfer_buf(buffer, buflen, srb, &index, &offset,
+ FROM_XFER_BUF);
+ if (buflen < scsi_bufflen(srb))
+ scsi_set_resid(srb, scsi_bufflen(srb) - buflen);
+}
+
+
+/***********************************************************************
+ * Transport routines
+ ***********************************************************************/
+
+/* Invoke the transport and basic error-handling/recovery methods
+ *
+ * This is used to send the message to the device and receive the response.
+ */
+void rtsx_invoke_transport(struct scsi_cmnd *srb, struct rtsx_chip *chip)
+{
+ int result;
+
+ result = rtsx_scsi_handler(srb, chip);
+
+ /* if the command gets aborted by the higher layers, we need to
+ * short-circuit all other processing
+ */
+ if (rtsx_chk_stat(chip, RTSX_STAT_ABORT)) {
+ dev_dbg(rtsx_dev(chip), "-- command was aborted\n");
+ srb->result = DID_ABORT << 16;
+ goto Handle_Errors;
+ }
+
+ /* if there is a transport error, reset and don't auto-sense */
+ if (result == TRANSPORT_ERROR) {
+ dev_dbg(rtsx_dev(chip), "-- transport indicates error, resetting\n");
+ srb->result = DID_ERROR << 16;
+ goto Handle_Errors;
+ }
+
+ srb->result = SAM_STAT_GOOD;
+
+ /*
+ * If we have a failure, we're going to do a REQUEST_SENSE
+ * automatically. Note that we differentiate between a command
+ * "failure" and an "error" in the transport mechanism.
+ */
+ if (result == TRANSPORT_FAILED) {
+ /* set the result so the higher layers expect this data */
+ srb->result = SAM_STAT_CHECK_CONDITION;
+ memcpy(srb->sense_buffer,
+ (unsigned char *)&(chip->sense_buffer[SCSI_LUN(srb)]),
+ sizeof(struct sense_data_t));
+ }
+
+ return;
+
+ /* Error and abort processing: try to resynchronize with the device
+ * by issuing a port reset. If that fails, try a class-specific
+ * device reset. */
+Handle_Errors:
+ return;
+}
+
+void rtsx_add_cmd(struct rtsx_chip *chip,
+ u8 cmd_type, u16 reg_addr, u8 mask, u8 data)
+{
+ u32 *cb = (u32 *)(chip->host_cmds_ptr);
+ u32 val = 0;
+
+ val |= (u32)(cmd_type & 0x03) << 30;
+ val |= (u32)(reg_addr & 0x3FFF) << 16;
+ val |= (u32)mask << 8;
+ val |= (u32)data;
+
+ spin_lock_irq(&chip->rtsx->reg_lock);
+ if (chip->ci < (HOST_CMDS_BUF_LEN / 4))
+ cb[(chip->ci)++] = cpu_to_le32(val);
+
+ spin_unlock_irq(&chip->rtsx->reg_lock);
+}
+
+void rtsx_send_cmd_no_wait(struct rtsx_chip *chip)
+{
+ u32 val = 1 << 31;
+
+ rtsx_writel(chip, RTSX_HCBAR, chip->host_cmds_addr);
+
+ val |= (u32)(chip->ci * 4) & 0x00FFFFFF;
+ /* Hardware Auto Response */
+ val |= 0x40000000;
+ rtsx_writel(chip, RTSX_HCBCTLR, val);
+}
+
+int rtsx_send_cmd(struct rtsx_chip *chip, u8 card, int timeout)
+{
+ struct rtsx_dev *rtsx = chip->rtsx;
+ struct completion trans_done;
+ u32 val = 1 << 31;
+ long timeleft;
+ int err = 0;
+
+ if (card == SD_CARD)
+ rtsx->check_card_cd = SD_EXIST;
+ else if (card == MS_CARD)
+ rtsx->check_card_cd = MS_EXIST;
+ else if (card == XD_CARD)
+ rtsx->check_card_cd = XD_EXIST;
+ else
+ rtsx->check_card_cd = 0;
+
+ spin_lock_irq(&rtsx->reg_lock);
+
+ /* set up data structures for the wakeup system */
+ rtsx->done = &trans_done;
+ rtsx->trans_result = TRANS_NOT_READY;
+ init_completion(&trans_done);
+ rtsx->trans_state = STATE_TRANS_CMD;
+
+ rtsx_writel(chip, RTSX_HCBAR, chip->host_cmds_addr);
+
+ val |= (u32)(chip->ci * 4) & 0x00FFFFFF;
+ /* Hardware Auto Response */
+ val |= 0x40000000;
+ rtsx_writel(chip, RTSX_HCBCTLR, val);
+
+ spin_unlock_irq(&rtsx->reg_lock);
+
+ /* Wait for TRANS_OK_INT */
+ timeleft = wait_for_completion_interruptible_timeout(
+ &trans_done, msecs_to_jiffies(timeout));
+ if (timeleft <= 0) {
+ dev_dbg(rtsx_dev(chip), "chip->int_reg = 0x%x\n",
+ chip->int_reg);
+ err = -ETIMEDOUT;
+ rtsx_trace(chip);
+ goto finish_send_cmd;
+ }
+
+ spin_lock_irq(&rtsx->reg_lock);
+ if (rtsx->trans_result == TRANS_RESULT_FAIL)
+ err = -EIO;
+ else if (rtsx->trans_result == TRANS_RESULT_OK)
+ err = 0;
+
+ spin_unlock_irq(&rtsx->reg_lock);
+
+finish_send_cmd:
+ rtsx->done = NULL;
+ rtsx->trans_state = STATE_TRANS_NONE;
+
+ if (err < 0)
+ rtsx_stop_cmd(chip, card);
+
+ return err;
+}
+
+static inline void rtsx_add_sg_tbl(
+ struct rtsx_chip *chip, u32 addr, u32 len, u8 option)
+{
+ u64 *sgb = (u64 *)(chip->host_sg_tbl_ptr);
+ u64 val = 0;
+ u32 temp_len = 0;
+ u8 temp_opt = 0;
+
+ do {
+ if (len > 0x80000) {
+ temp_len = 0x80000;
+ temp_opt = option & (~SG_END);
+ } else {
+ temp_len = len;
+ temp_opt = option;
+ }
+ val = ((u64)addr << 32) | ((u64)temp_len << 12) | temp_opt;
+
+ if (chip->sgi < (HOST_SG_TBL_BUF_LEN / 8))
+ sgb[(chip->sgi)++] = cpu_to_le64(val);
+
+ len -= temp_len;
+ addr += temp_len;
+ } while (len);
+}
+
+static int rtsx_transfer_sglist_adma_partial(struct rtsx_chip *chip, u8 card,
+ struct scatterlist *sg, int num_sg, unsigned int *index,
+ unsigned int *offset, int size,
+ enum dma_data_direction dma_dir, int timeout)
+{
+ struct rtsx_dev *rtsx = chip->rtsx;
+ struct completion trans_done;
+ u8 dir;
+ int sg_cnt, i, resid;
+ int err = 0;
+ long timeleft;
+ struct scatterlist *sg_ptr;
+ u32 val = TRIG_DMA;
+
+ if ((sg == NULL) || (num_sg <= 0) || !offset || !index)
+ return -EIO;
+
+ if (dma_dir == DMA_TO_DEVICE)
+ dir = HOST_TO_DEVICE;
+ else if (dma_dir == DMA_FROM_DEVICE)
+ dir = DEVICE_TO_HOST;
+ else
+ return -ENXIO;
+
+ if (card == SD_CARD)
+ rtsx->check_card_cd = SD_EXIST;
+ else if (card == MS_CARD)
+ rtsx->check_card_cd = MS_EXIST;
+ else if (card == XD_CARD)
+ rtsx->check_card_cd = XD_EXIST;
+ else
+ rtsx->check_card_cd = 0;
+
+ spin_lock_irq(&rtsx->reg_lock);
+
+ /* set up data structures for the wakeup system */
+ rtsx->done = &trans_done;
+
+ rtsx->trans_state = STATE_TRANS_SG;
+ rtsx->trans_result = TRANS_NOT_READY;
+
+ spin_unlock_irq(&rtsx->reg_lock);
+
+ sg_cnt = dma_map_sg(&(rtsx->pci->dev), sg, num_sg, dma_dir);
+
+ resid = size;
+ sg_ptr = sg;
+ chip->sgi = 0;
+ /* Usually the next entry will be @sg@ + 1, but if this sg element
+ * is part of a chained scatterlist, it could jump to the start of
+ * a new scatterlist array. So here we use sg_next to move to
+ * the proper sg
+ */
+ for (i = 0; i < *index; i++)
+ sg_ptr = sg_next(sg_ptr);
+ for (i = *index; i < sg_cnt; i++) {
+ dma_addr_t addr;
+ unsigned int len;
+ u8 option;
+
+ addr = sg_dma_address(sg_ptr);
+ len = sg_dma_len(sg_ptr);
+
+ dev_dbg(rtsx_dev(chip), "DMA addr: 0x%x, Len: 0x%x\n",
+ (unsigned int)addr, len);
+ dev_dbg(rtsx_dev(chip), "*index = %d, *offset = %d\n",
+ *index, *offset);
+
+ addr += *offset;
+
+ if ((len - *offset) > resid) {
+ *offset += resid;
+ len = resid;
+ resid = 0;
+ } else {
+ resid -= (len - *offset);
+ len -= *offset;
+ *offset = 0;
+ *index = *index + 1;
+ }
+ if ((i == (sg_cnt - 1)) || !resid)
+ option = SG_VALID | SG_END | SG_TRANS_DATA;
+ else
+ option = SG_VALID | SG_TRANS_DATA;
+
+ rtsx_add_sg_tbl(chip, (u32)addr, (u32)len, option);
+
+ if (!resid)
+ break;
+
+ sg_ptr = sg_next(sg_ptr);
+ }
+
+ dev_dbg(rtsx_dev(chip), "SG table count = %d\n", chip->sgi);
+
+ val |= (u32)(dir & 0x01) << 29;
+ val |= ADMA_MODE;
+
+ spin_lock_irq(&rtsx->reg_lock);
+
+ init_completion(&trans_done);
+
+ rtsx_writel(chip, RTSX_HDBAR, chip->host_sg_tbl_addr);
+ rtsx_writel(chip, RTSX_HDBCTLR, val);
+
+ spin_unlock_irq(&rtsx->reg_lock);
+
+ timeleft = wait_for_completion_interruptible_timeout(
+ &trans_done, msecs_to_jiffies(timeout));
+ if (timeleft <= 0) {
+ dev_dbg(rtsx_dev(chip), "Timeout (%s %d)\n",
+ __func__, __LINE__);
+ dev_dbg(rtsx_dev(chip), "chip->int_reg = 0x%x\n",
+ chip->int_reg);
+ err = -ETIMEDOUT;
+ goto out;
+ }
+
+ spin_lock_irq(&rtsx->reg_lock);
+ if (rtsx->trans_result == TRANS_RESULT_FAIL) {
+ err = -EIO;
+ spin_unlock_irq(&rtsx->reg_lock);
+ goto out;
+ }
+ spin_unlock_irq(&rtsx->reg_lock);
+
+ /* Wait for TRANS_OK_INT */
+ spin_lock_irq(&rtsx->reg_lock);
+ if (rtsx->trans_result == TRANS_NOT_READY) {
+ init_completion(&trans_done);
+ spin_unlock_irq(&rtsx->reg_lock);
+ timeleft = wait_for_completion_interruptible_timeout(
+ &trans_done, msecs_to_jiffies(timeout));
+ if (timeleft <= 0) {
+ dev_dbg(rtsx_dev(chip), "Timeout (%s %d)\n",
+ __func__, __LINE__);
+ dev_dbg(rtsx_dev(chip), "chip->int_reg = 0x%x\n",
+ chip->int_reg);
+ err = -ETIMEDOUT;
+ goto out;
+ }
+ } else {
+ spin_unlock_irq(&rtsx->reg_lock);
+ }
+
+ spin_lock_irq(&rtsx->reg_lock);
+ if (rtsx->trans_result == TRANS_RESULT_FAIL)
+ err = -EIO;
+ else if (rtsx->trans_result == TRANS_RESULT_OK)
+ err = 0;
+
+ spin_unlock_irq(&rtsx->reg_lock);
+
+out:
+ rtsx->done = NULL;
+ rtsx->trans_state = STATE_TRANS_NONE;
+ dma_unmap_sg(&(rtsx->pci->dev), sg, num_sg, dma_dir);
+
+ if (err < 0)
+ rtsx_stop_cmd(chip, card);
+
+ return err;
+}
+
+static int rtsx_transfer_sglist_adma(struct rtsx_chip *chip, u8 card,
+ struct scatterlist *sg, int num_sg,
+ enum dma_data_direction dma_dir, int timeout)
+{
+ struct rtsx_dev *rtsx = chip->rtsx;
+ struct completion trans_done;
+ u8 dir;
+ int buf_cnt, i;
+ int err = 0;
+ long timeleft;
+ struct scatterlist *sg_ptr;
+
+ if ((sg == NULL) || (num_sg <= 0))
+ return -EIO;
+
+ if (dma_dir == DMA_TO_DEVICE)
+ dir = HOST_TO_DEVICE;
+ else if (dma_dir == DMA_FROM_DEVICE)
+ dir = DEVICE_TO_HOST;
+ else
+ return -ENXIO;
+
+ if (card == SD_CARD)
+ rtsx->check_card_cd = SD_EXIST;
+ else if (card == MS_CARD)
+ rtsx->check_card_cd = MS_EXIST;
+ else if (card == XD_CARD)
+ rtsx->check_card_cd = XD_EXIST;
+ else
+ rtsx->check_card_cd = 0;
+
+ spin_lock_irq(&rtsx->reg_lock);
+
+ /* set up data structures for the wakeup system */
+ rtsx->done = &trans_done;
+
+ rtsx->trans_state = STATE_TRANS_SG;
+ rtsx->trans_result = TRANS_NOT_READY;
+
+ spin_unlock_irq(&rtsx->reg_lock);
+
+ buf_cnt = dma_map_sg(&(rtsx->pci->dev), sg, num_sg, dma_dir);
+
+ sg_ptr = sg;
+
+ for (i = 0; i <= buf_cnt / (HOST_SG_TBL_BUF_LEN / 8); i++) {
+ u32 val = TRIG_DMA;
+ int sg_cnt, j;
+
+ if (i == buf_cnt / (HOST_SG_TBL_BUF_LEN / 8))
+ sg_cnt = buf_cnt % (HOST_SG_TBL_BUF_LEN / 8);
+ else
+ sg_cnt = HOST_SG_TBL_BUF_LEN / 8;
+
+ chip->sgi = 0;
+ for (j = 0; j < sg_cnt; j++) {
+ dma_addr_t addr = sg_dma_address(sg_ptr);
+ unsigned int len = sg_dma_len(sg_ptr);
+ u8 option;
+
+ dev_dbg(rtsx_dev(chip), "DMA addr: 0x%x, Len: 0x%x\n",
+ (unsigned int)addr, len);
+
+ if (j == (sg_cnt - 1))
+ option = SG_VALID | SG_END | SG_TRANS_DATA;
+ else
+ option = SG_VALID | SG_TRANS_DATA;
+
+ rtsx_add_sg_tbl(chip, (u32)addr, (u32)len, option);
+
+ sg_ptr = sg_next(sg_ptr);
+ }
+
+ dev_dbg(rtsx_dev(chip), "SG table count = %d\n", chip->sgi);
+
+ val |= (u32)(dir & 0x01) << 29;
+ val |= ADMA_MODE;
+
+ spin_lock_irq(&rtsx->reg_lock);
+
+ init_completion(&trans_done);
+
+ rtsx_writel(chip, RTSX_HDBAR, chip->host_sg_tbl_addr);
+ rtsx_writel(chip, RTSX_HDBCTLR, val);
+
+ spin_unlock_irq(&rtsx->reg_lock);
+
+ timeleft = wait_for_completion_interruptible_timeout(
+ &trans_done, msecs_to_jiffies(timeout));
+ if (timeleft <= 0) {
+ dev_dbg(rtsx_dev(chip), "Timeout (%s %d)\n",
+ __func__, __LINE__);
+ dev_dbg(rtsx_dev(chip), "chip->int_reg = 0x%x\n",
+ chip->int_reg);
+ err = -ETIMEDOUT;
+ goto out;
+ }
+
+ spin_lock_irq(&rtsx->reg_lock);
+ if (rtsx->trans_result == TRANS_RESULT_FAIL) {
+ err = -EIO;
+ spin_unlock_irq(&rtsx->reg_lock);
+ goto out;
+ }
+ spin_unlock_irq(&rtsx->reg_lock);
+
+ sg_ptr += sg_cnt;
+ }
+
+ /* Wait for TRANS_OK_INT */
+ spin_lock_irq(&rtsx->reg_lock);
+ if (rtsx->trans_result == TRANS_NOT_READY) {
+ init_completion(&trans_done);
+ spin_unlock_irq(&rtsx->reg_lock);
+ timeleft = wait_for_completion_interruptible_timeout(
+ &trans_done, msecs_to_jiffies(timeout));
+ if (timeleft <= 0) {
+ dev_dbg(rtsx_dev(chip), "Timeout (%s %d)\n",
+ __func__, __LINE__);
+ dev_dbg(rtsx_dev(chip), "chip->int_reg = 0x%x\n",
+ chip->int_reg);
+ err = -ETIMEDOUT;
+ goto out;
+ }
+ } else {
+ spin_unlock_irq(&rtsx->reg_lock);
+ }
+
+ spin_lock_irq(&rtsx->reg_lock);
+ if (rtsx->trans_result == TRANS_RESULT_FAIL)
+ err = -EIO;
+ else if (rtsx->trans_result == TRANS_RESULT_OK)
+ err = 0;
+
+ spin_unlock_irq(&rtsx->reg_lock);
+
+out:
+ rtsx->done = NULL;
+ rtsx->trans_state = STATE_TRANS_NONE;
+ dma_unmap_sg(&(rtsx->pci->dev), sg, num_sg, dma_dir);
+
+ if (err < 0)
+ rtsx_stop_cmd(chip, card);
+
+ return err;
+}
+
+static int rtsx_transfer_buf(struct rtsx_chip *chip, u8 card, void *buf,
+ size_t len, enum dma_data_direction dma_dir, int timeout)
+{
+ struct rtsx_dev *rtsx = chip->rtsx;
+ struct completion trans_done;
+ dma_addr_t addr;
+ u8 dir;
+ int err = 0;
+ u32 val = 1 << 31;
+ long timeleft;
+
+ if ((buf == NULL) || (len <= 0))
+ return -EIO;
+
+ if (dma_dir == DMA_TO_DEVICE)
+ dir = HOST_TO_DEVICE;
+ else if (dma_dir == DMA_FROM_DEVICE)
+ dir = DEVICE_TO_HOST;
+ else
+ return -ENXIO;
+
+ addr = dma_map_single(&(rtsx->pci->dev), buf, len, dma_dir);
+ if (!addr)
+ return -ENOMEM;
+
+ if (card == SD_CARD)
+ rtsx->check_card_cd = SD_EXIST;
+ else if (card == MS_CARD)
+ rtsx->check_card_cd = MS_EXIST;
+ else if (card == XD_CARD)
+ rtsx->check_card_cd = XD_EXIST;
+ else
+ rtsx->check_card_cd = 0;
+
+ val |= (u32)(dir & 0x01) << 29;
+ val |= (u32)(len & 0x00FFFFFF);
+
+ spin_lock_irq(&rtsx->reg_lock);
+
+ /* set up data structures for the wakeup system */
+ rtsx->done = &trans_done;
+
+ init_completion(&trans_done);
+
+ rtsx->trans_state = STATE_TRANS_BUF;
+ rtsx->trans_result = TRANS_NOT_READY;
+
+ rtsx_writel(chip, RTSX_HDBAR, addr);
+ rtsx_writel(chip, RTSX_HDBCTLR, val);
+
+ spin_unlock_irq(&rtsx->reg_lock);
+
+ /* Wait for TRANS_OK_INT */
+ timeleft = wait_for_completion_interruptible_timeout(
+ &trans_done, msecs_to_jiffies(timeout));
+ if (timeleft <= 0) {
+ dev_dbg(rtsx_dev(chip), "Timeout (%s %d)\n",
+ __func__, __LINE__);
+ dev_dbg(rtsx_dev(chip), "chip->int_reg = 0x%x\n",
+ chip->int_reg);
+ err = -ETIMEDOUT;
+ goto out;
+ }
+
+ spin_lock_irq(&rtsx->reg_lock);
+ if (rtsx->trans_result == TRANS_RESULT_FAIL)
+ err = -EIO;
+ else if (rtsx->trans_result == TRANS_RESULT_OK)
+ err = 0;
+
+ spin_unlock_irq(&rtsx->reg_lock);
+
+out:
+ rtsx->done = NULL;
+ rtsx->trans_state = STATE_TRANS_NONE;
+ dma_unmap_single(&(rtsx->pci->dev), addr, len, dma_dir);
+
+ if (err < 0)
+ rtsx_stop_cmd(chip, card);
+
+ return err;
+}
+
+int rtsx_transfer_data_partial(struct rtsx_chip *chip, u8 card,
+ void *buf, size_t len, int use_sg, unsigned int *index,
+ unsigned int *offset, enum dma_data_direction dma_dir,
+ int timeout)
+{
+ int err = 0;
+
+ /* don't transfer data during abort processing */
+ if (rtsx_chk_stat(chip, RTSX_STAT_ABORT))
+ return -EIO;
+
+ if (use_sg)
+ err = rtsx_transfer_sglist_adma_partial(chip, card,
+ (struct scatterlist *)buf, use_sg,
+ index, offset, (int)len, dma_dir, timeout);
+ else
+ err = rtsx_transfer_buf(chip, card,
+ buf, len, dma_dir, timeout);
+ if (err < 0) {
+ if (RTSX_TST_DELINK(chip)) {
+ RTSX_CLR_DELINK(chip);
+ chip->need_reinit = SD_CARD | MS_CARD | XD_CARD;
+ rtsx_reinit_cards(chip, 1);
+ }
+ }
+
+ return err;
+}
+
+int rtsx_transfer_data(struct rtsx_chip *chip, u8 card, void *buf, size_t len,
+ int use_sg, enum dma_data_direction dma_dir, int timeout)
+{
+ int err = 0;
+
+ dev_dbg(rtsx_dev(chip), "use_sg = %d\n", use_sg);
+
+ /* don't transfer data during abort processing */
+ if (rtsx_chk_stat(chip, RTSX_STAT_ABORT))
+ return -EIO;
+
+ if (use_sg) {
+ err = rtsx_transfer_sglist_adma(chip, card,
+ (struct scatterlist *)buf,
+ use_sg, dma_dir, timeout);
+ } else {
+ err = rtsx_transfer_buf(chip, card, buf, len, dma_dir, timeout);
+ }
+
+ if (err < 0) {
+ if (RTSX_TST_DELINK(chip)) {
+ RTSX_CLR_DELINK(chip);
+ chip->need_reinit = SD_CARD | MS_CARD | XD_CARD;
+ rtsx_reinit_cards(chip, 1);
+ }
+ }
+
+ return err;
+}
+
diff --git a/drivers/staging/rts5208/rtsx_transport.h b/drivers/staging/rts5208/rtsx_transport.h
new file mode 100644
index 000000000..899bc2079
--- /dev/null
+++ b/drivers/staging/rts5208/rtsx_transport.h
@@ -0,0 +1,66 @@
+/* Driver for Realtek PCI-Express card reader
+ * Header file
+ *
+ * Copyright(c) 2009-2013 Realtek Semiconductor Corp. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, 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, see <http://www.gnu.org/licenses/>.
+ *
+ * Author:
+ * Wei WANG (wei_wang@realsil.com.cn)
+ * Micky Ching (micky_ching@realsil.com.cn)
+ */
+
+#ifndef __REALTEK_RTSX_TRANSPORT_H
+#define __REALTEK_RTSX_TRANSPORT_H
+
+#include "rtsx.h"
+#include "rtsx_chip.h"
+
+#define WAIT_TIME 2000
+
+unsigned int rtsx_stor_access_xfer_buf(unsigned char *buffer,
+ unsigned int buflen, struct scsi_cmnd *srb, unsigned int *index,
+ unsigned int *offset, enum xfer_buf_dir dir);
+void rtsx_stor_set_xfer_buf(unsigned char *buffer,
+ unsigned int buflen, struct scsi_cmnd *srb);
+void rtsx_stor_get_xfer_buf(unsigned char *buffer,
+ unsigned int buflen, struct scsi_cmnd *srb);
+void rtsx_invoke_transport(struct scsi_cmnd *srb, struct rtsx_chip *chip);
+
+
+#define rtsx_init_cmd(chip) ((chip)->ci = 0)
+
+void rtsx_add_cmd(struct rtsx_chip *chip,
+ u8 cmd_type, u16 reg_addr, u8 mask, u8 data);
+void rtsx_send_cmd_no_wait(struct rtsx_chip *chip);
+int rtsx_send_cmd(struct rtsx_chip *chip, u8 card, int timeout);
+
+static inline u8 *rtsx_get_cmd_data(struct rtsx_chip *chip)
+{
+#ifdef CMD_USING_SG
+ return (u8 *)(chip->host_sg_tbl_ptr);
+#else
+ return (u8 *)(chip->host_cmds_ptr);
+#endif
+}
+
+int rtsx_transfer_data(struct rtsx_chip *chip, u8 card, void *buf, size_t len,
+ int use_sg, enum dma_data_direction dma_dir, int timeout);
+
+int rtsx_transfer_data_partial(struct rtsx_chip *chip, u8 card,
+ void *buf, size_t len,
+ int use_sg, unsigned int *index, unsigned int *offset,
+ enum dma_data_direction dma_dir, int timeout);
+
+#endif /* __REALTEK_RTSX_TRANSPORT_H */
diff --git a/drivers/staging/rts5208/sd.c b/drivers/staging/rts5208/sd.c
new file mode 100644
index 000000000..a8d657bb5
--- /dev/null
+++ b/drivers/staging/rts5208/sd.c
@@ -0,0 +1,5316 @@
+/* Driver for Realtek PCI-Express card reader
+ *
+ * Copyright(c) 2009-2013 Realtek Semiconductor Corp. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, 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, see <http://www.gnu.org/licenses/>.
+ *
+ * Author:
+ * Wei WANG (wei_wang@realsil.com.cn)
+ * Micky Ching (micky_ching@realsil.com.cn)
+ */
+
+#include <linux/blkdev.h>
+#include <linux/kthread.h>
+#include <linux/sched.h>
+
+#include "rtsx.h"
+#include "sd.h"
+
+#define SD_MAX_RETRY_COUNT 3
+
+static u16 REG_SD_CFG1;
+static u16 REG_SD_CFG2;
+static u16 REG_SD_CFG3;
+static u16 REG_SD_STAT1;
+static u16 REG_SD_STAT2;
+static u16 REG_SD_BUS_STAT;
+static u16 REG_SD_PAD_CTL;
+static u16 REG_SD_SAMPLE_POINT_CTL;
+static u16 REG_SD_PUSH_POINT_CTL;
+static u16 REG_SD_CMD0;
+static u16 REG_SD_CMD1;
+static u16 REG_SD_CMD2;
+static u16 REG_SD_CMD3;
+static u16 REG_SD_CMD4;
+static u16 REG_SD_CMD5;
+static u16 REG_SD_BYTE_CNT_L;
+static u16 REG_SD_BYTE_CNT_H;
+static u16 REG_SD_BLOCK_CNT_L;
+static u16 REG_SD_BLOCK_CNT_H;
+static u16 REG_SD_TRANSFER;
+static u16 REG_SD_VPCLK0_CTL;
+static u16 REG_SD_VPCLK1_CTL;
+static u16 REG_SD_DCMPS0_CTL;
+static u16 REG_SD_DCMPS1_CTL;
+
+static inline void sd_set_err_code(struct rtsx_chip *chip, u8 err_code)
+{
+ struct sd_info *sd_card = &(chip->sd_card);
+
+ sd_card->err_code |= err_code;
+}
+
+static inline void sd_clr_err_code(struct rtsx_chip *chip)
+{
+ struct sd_info *sd_card = &(chip->sd_card);
+
+ sd_card->err_code = 0;
+}
+
+static inline int sd_check_err_code(struct rtsx_chip *chip, u8 err_code)
+{
+ struct sd_info *sd_card = &(chip->sd_card);
+
+ return sd_card->err_code & err_code;
+}
+
+static void sd_init_reg_addr(struct rtsx_chip *chip)
+{
+ REG_SD_CFG1 = 0xFD31;
+ REG_SD_CFG2 = 0xFD33;
+ REG_SD_CFG3 = 0xFD3E;
+ REG_SD_STAT1 = 0xFD30;
+ REG_SD_STAT2 = 0;
+ REG_SD_BUS_STAT = 0;
+ REG_SD_PAD_CTL = 0;
+ REG_SD_SAMPLE_POINT_CTL = 0;
+ REG_SD_PUSH_POINT_CTL = 0;
+ REG_SD_CMD0 = 0xFD34;
+ REG_SD_CMD1 = 0xFD35;
+ REG_SD_CMD2 = 0xFD36;
+ REG_SD_CMD3 = 0xFD37;
+ REG_SD_CMD4 = 0xFD38;
+ REG_SD_CMD5 = 0xFD5A;
+ REG_SD_BYTE_CNT_L = 0xFD39;
+ REG_SD_BYTE_CNT_H = 0xFD3A;
+ REG_SD_BLOCK_CNT_L = 0xFD3B;
+ REG_SD_BLOCK_CNT_H = 0xFD3C;
+ REG_SD_TRANSFER = 0xFD32;
+ REG_SD_VPCLK0_CTL = 0;
+ REG_SD_VPCLK1_CTL = 0;
+ REG_SD_DCMPS0_CTL = 0;
+ REG_SD_DCMPS1_CTL = 0;
+}
+
+static int sd_check_data0_status(struct rtsx_chip *chip)
+{
+ int retval;
+ u8 stat;
+
+ retval = rtsx_read_register(chip, REG_SD_STAT1, &stat);
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+
+ if (!(stat & SD_DAT0_STATUS)) {
+ sd_set_err_code(chip, SD_BUSY);
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ return STATUS_SUCCESS;
+}
+
+static int sd_send_cmd_get_rsp(struct rtsx_chip *chip, u8 cmd_idx,
+ u32 arg, u8 rsp_type, u8 *rsp, int rsp_len)
+{
+ struct sd_info *sd_card = &(chip->sd_card);
+ int retval;
+ int timeout = 100;
+ u16 reg_addr;
+ u8 *ptr;
+ int stat_idx = 0;
+ int rty_cnt = 0;
+
+ sd_clr_err_code(chip);
+
+ dev_dbg(rtsx_dev(chip), "SD/MMC CMD %d, arg = 0x%08x\n", cmd_idx, arg);
+
+ if (rsp_type == SD_RSP_TYPE_R1b)
+ timeout = 3000;
+
+RTY_SEND_CMD:
+
+ rtsx_init_cmd(chip);
+
+ rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_CMD0, 0xFF, 0x40 | cmd_idx);
+ rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_CMD1, 0xFF, (u8)(arg >> 24));
+ rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_CMD2, 0xFF, (u8)(arg >> 16));
+ rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_CMD3, 0xFF, (u8)(arg >> 8));
+ rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_CMD4, 0xFF, (u8)arg);
+
+ rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_CFG2, 0xFF, rsp_type);
+ rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_DATA_SOURCE,
+ 0x01, PINGPONG_BUFFER);
+ rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_TRANSFER,
+ 0xFF, SD_TM_CMD_RSP | SD_TRANSFER_START);
+ rtsx_add_cmd(chip, CHECK_REG_CMD, REG_SD_TRANSFER,
+ SD_TRANSFER_END | SD_STAT_IDLE, SD_TRANSFER_END | SD_STAT_IDLE);
+
+ if (rsp_type == SD_RSP_TYPE_R2) {
+ for (reg_addr = PPBUF_BASE2; reg_addr < PPBUF_BASE2 + 16;
+ reg_addr++)
+ rtsx_add_cmd(chip, READ_REG_CMD, reg_addr, 0, 0);
+
+ stat_idx = 16;
+ } else if (rsp_type != SD_RSP_TYPE_R0) {
+ for (reg_addr = REG_SD_CMD0; reg_addr <= REG_SD_CMD4;
+ reg_addr++)
+ rtsx_add_cmd(chip, READ_REG_CMD, reg_addr, 0, 0);
+
+ stat_idx = 5;
+ }
+
+ rtsx_add_cmd(chip, READ_REG_CMD, REG_SD_STAT1, 0, 0);
+
+ retval = rtsx_send_cmd(chip, SD_CARD, timeout);
+ if (retval < 0) {
+ u8 val;
+
+ rtsx_read_register(chip, REG_SD_STAT1, &val);
+ dev_dbg(rtsx_dev(chip), "SD_STAT1: 0x%x\n", val);
+
+ rtsx_read_register(chip, REG_SD_CFG3, &val);
+ dev_dbg(rtsx_dev(chip), "SD_CFG3: 0x%x\n", val);
+
+ if (retval == -ETIMEDOUT) {
+ if (rsp_type & SD_WAIT_BUSY_END) {
+ retval = sd_check_data0_status(chip);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_clear_sd_error(chip);
+ rtsx_trace(chip);
+ return retval;
+ }
+ } else {
+ sd_set_err_code(chip, SD_TO_ERR);
+ }
+ retval = STATUS_TIMEDOUT;
+ } else {
+ retval = STATUS_FAIL;
+ }
+ rtsx_clear_sd_error(chip);
+
+ rtsx_trace(chip);
+ return retval;
+ }
+
+ if (rsp_type == SD_RSP_TYPE_R0)
+ return STATUS_SUCCESS;
+
+ ptr = rtsx_get_cmd_data(chip) + 1;
+
+ if ((ptr[0] & 0xC0) != 0) {
+ sd_set_err_code(chip, SD_STS_ERR);
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ if (!(rsp_type & SD_NO_CHECK_CRC7)) {
+ if (ptr[stat_idx] & SD_CRC7_ERR) {
+ if (cmd_idx == WRITE_MULTIPLE_BLOCK) {
+ sd_set_err_code(chip, SD_CRC_ERR);
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+ if (rty_cnt < SD_MAX_RETRY_COUNT) {
+ wait_timeout(20);
+ rty_cnt++;
+ goto RTY_SEND_CMD;
+ } else {
+ sd_set_err_code(chip, SD_CRC_ERR);
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+ }
+ }
+
+ if ((rsp_type == SD_RSP_TYPE_R1) || (rsp_type == SD_RSP_TYPE_R1b)) {
+ if ((cmd_idx != SEND_RELATIVE_ADDR) &&
+ (cmd_idx != SEND_IF_COND)) {
+ if (cmd_idx != STOP_TRANSMISSION) {
+ if (ptr[1] & 0x80) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+ }
+#ifdef SUPPORT_SD_LOCK
+ if (ptr[1] & 0x7D)
+#else
+ if (ptr[1] & 0x7F)
+#endif
+ {
+ dev_dbg(rtsx_dev(chip), "ptr[1]: 0x%02x\n",
+ ptr[1]);
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+ if (ptr[2] & 0xFF) {
+ dev_dbg(rtsx_dev(chip), "ptr[2]: 0x%02x\n",
+ ptr[2]);
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+ if (ptr[3] & 0x80) {
+ dev_dbg(rtsx_dev(chip), "ptr[3]: 0x%02x\n",
+ ptr[3]);
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+ if (ptr[3] & 0x01)
+ sd_card->sd_data_buf_ready = 1;
+ else
+ sd_card->sd_data_buf_ready = 0;
+ }
+ }
+
+ if (rsp && rsp_len)
+ memcpy(rsp, ptr, rsp_len);
+
+ return STATUS_SUCCESS;
+}
+
+static int sd_read_data(struct rtsx_chip *chip,
+ u8 trans_mode, u8 *cmd, int cmd_len, u16 byte_cnt,
+ u16 blk_cnt, u8 bus_width, u8 *buf, int buf_len,
+ int timeout)
+{
+ struct sd_info *sd_card = &(chip->sd_card);
+ int retval;
+ int i;
+
+ sd_clr_err_code(chip);
+
+ if (!buf)
+ buf_len = 0;
+
+ if (buf_len > 512) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ rtsx_init_cmd(chip);
+
+ if (cmd_len) {
+ dev_dbg(rtsx_dev(chip), "SD/MMC CMD %d\n", cmd[0] - 0x40);
+ for (i = 0; i < (cmd_len < 6 ? cmd_len : 6); i++)
+ rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_CMD0 + i,
+ 0xFF, cmd[i]);
+ }
+ rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_BYTE_CNT_L, 0xFF,
+ (u8)byte_cnt);
+ rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_BYTE_CNT_H, 0xFF,
+ (u8)(byte_cnt >> 8));
+ rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_BLOCK_CNT_L, 0xFF,
+ (u8)blk_cnt);
+ rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_BLOCK_CNT_H, 0xFF,
+ (u8)(blk_cnt >> 8));
+
+ rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_CFG1, 0x03, bus_width);
+
+ rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_CFG2, 0xFF,
+ SD_CALCULATE_CRC7 | SD_CHECK_CRC16 | SD_NO_WAIT_BUSY_END|
+ SD_CHECK_CRC7 | SD_RSP_LEN_6);
+ if (trans_mode != SD_TM_AUTO_TUNING)
+ rtsx_add_cmd(chip, WRITE_REG_CMD,
+ CARD_DATA_SOURCE, 0x01, PINGPONG_BUFFER);
+
+ rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_TRANSFER, 0xFF,
+ trans_mode | SD_TRANSFER_START);
+ rtsx_add_cmd(chip, CHECK_REG_CMD, REG_SD_TRANSFER, SD_TRANSFER_END,
+ SD_TRANSFER_END);
+
+ retval = rtsx_send_cmd(chip, SD_CARD, timeout);
+ if (retval < 0) {
+ if (retval == -ETIMEDOUT) {
+ sd_send_cmd_get_rsp(chip, SEND_STATUS, sd_card->sd_addr,
+ SD_RSP_TYPE_R1, NULL, 0);
+ }
+
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ if (buf && buf_len) {
+ retval = rtsx_read_ppbuf(chip, buf, buf_len);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+ }
+
+ return STATUS_SUCCESS;
+}
+
+static int sd_write_data(struct rtsx_chip *chip, u8 trans_mode,
+ u8 *cmd, int cmd_len, u16 byte_cnt, u16 blk_cnt, u8 bus_width,
+ u8 *buf, int buf_len, int timeout)
+{
+ struct sd_info *sd_card = &(chip->sd_card);
+ int retval;
+ int i;
+
+ sd_clr_err_code(chip);
+
+ if (!buf)
+ buf_len = 0;
+
+ if (buf_len > 512) {
+ /* This function can't write data more than one page */
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ if (buf && buf_len) {
+ retval = rtsx_write_ppbuf(chip, buf, buf_len);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+ }
+
+ rtsx_init_cmd(chip);
+
+ if (cmd_len) {
+ dev_dbg(rtsx_dev(chip), "SD/MMC CMD %d\n", cmd[0] - 0x40);
+ for (i = 0; i < (cmd_len < 6 ? cmd_len : 6); i++) {
+ rtsx_add_cmd(chip, WRITE_REG_CMD,
+ REG_SD_CMD0 + i, 0xFF, cmd[i]);
+ }
+ }
+ rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_BYTE_CNT_L, 0xFF,
+ (u8)byte_cnt);
+ rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_BYTE_CNT_H, 0xFF,
+ (u8)(byte_cnt >> 8));
+ rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_BLOCK_CNT_L, 0xFF,
+ (u8)blk_cnt);
+ rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_BLOCK_CNT_H, 0xFF,
+ (u8)(blk_cnt >> 8));
+
+ rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_CFG1, 0x03, bus_width);
+
+ rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_CFG2, 0xFF,
+ SD_CALCULATE_CRC7 | SD_CHECK_CRC16 | SD_NO_WAIT_BUSY_END |
+ SD_CHECK_CRC7 | SD_RSP_LEN_6);
+
+ rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_TRANSFER, 0xFF,
+ trans_mode | SD_TRANSFER_START);
+ rtsx_add_cmd(chip, CHECK_REG_CMD, REG_SD_TRANSFER, SD_TRANSFER_END,
+ SD_TRANSFER_END);
+
+ retval = rtsx_send_cmd(chip, SD_CARD, timeout);
+ if (retval < 0) {
+ if (retval == -ETIMEDOUT) {
+ sd_send_cmd_get_rsp(chip, SEND_STATUS,
+ sd_card->sd_addr, SD_RSP_TYPE_R1, NULL, 0);
+ }
+
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ return STATUS_SUCCESS;
+}
+
+static int sd_check_csd(struct rtsx_chip *chip, char check_wp)
+{
+ struct sd_info *sd_card = &(chip->sd_card);
+ int retval;
+ int i;
+ u8 csd_ver, trans_speed;
+ u8 rsp[16];
+
+ for (i = 0; i < 6; i++) {
+ if (detect_card_cd(chip, SD_CARD) != STATUS_SUCCESS) {
+ sd_set_err_code(chip, SD_NO_CARD);
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ retval = sd_send_cmd_get_rsp(chip, SEND_CSD, sd_card->sd_addr,
+ SD_RSP_TYPE_R2, rsp, 16);
+ if (retval == STATUS_SUCCESS)
+ break;
+ }
+
+ if (i == 6) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ memcpy(sd_card->raw_csd, rsp + 1, 15);
+
+ dev_dbg(rtsx_dev(chip), "CSD Response:\n");
+ dev_dbg(rtsx_dev(chip), "%*ph\n", 16, sd_card->raw_csd);
+
+ csd_ver = (rsp[1] & 0xc0) >> 6;
+ dev_dbg(rtsx_dev(chip), "csd_ver = %d\n", csd_ver);
+
+ trans_speed = rsp[4];
+ if ((trans_speed & 0x07) == 0x02) {
+ if ((trans_speed & 0xf8) >= 0x30) {
+ if (chip->asic_code)
+ sd_card->sd_clock = 47;
+ else
+ sd_card->sd_clock = CLK_50;
+
+ } else if ((trans_speed & 0xf8) == 0x28) {
+ if (chip->asic_code)
+ sd_card->sd_clock = 39;
+ else
+ sd_card->sd_clock = CLK_40;
+
+ } else if ((trans_speed & 0xf8) == 0x20) {
+ if (chip->asic_code)
+ sd_card->sd_clock = 29;
+ else
+ sd_card->sd_clock = CLK_30;
+
+ } else if ((trans_speed & 0xf8) >= 0x10) {
+ if (chip->asic_code)
+ sd_card->sd_clock = 23;
+ else
+ sd_card->sd_clock = CLK_20;
+
+ } else if ((trans_speed & 0x08) >= 0x08) {
+ if (chip->asic_code)
+ sd_card->sd_clock = 19;
+ else
+ sd_card->sd_clock = CLK_20;
+ } else {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+ } else {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ if (CHK_MMC_SECTOR_MODE(sd_card)) {
+ sd_card->capacity = 0;
+ } else {
+ if ((!CHK_SD_HCXC(sd_card)) || (csd_ver == 0)) {
+ u8 blk_size, c_size_mult;
+ u16 c_size;
+
+ blk_size = rsp[6] & 0x0F;
+ c_size = ((u16)(rsp[7] & 0x03) << 10)
+ + ((u16)rsp[8] << 2)
+ + ((u16)(rsp[9] & 0xC0) >> 6);
+ c_size_mult = (u8)((rsp[10] & 0x03) << 1);
+ c_size_mult += (rsp[11] & 0x80) >> 7;
+ sd_card->capacity = (((u32)(c_size + 1)) *
+ (1 << (c_size_mult + 2)))
+ << (blk_size - 9);
+ } else {
+ u32 total_sector = 0;
+
+ total_sector = (((u32)rsp[8] & 0x3f) << 16) |
+ ((u32)rsp[9] << 8) | (u32)rsp[10];
+ sd_card->capacity = (total_sector + 1) << 10;
+ }
+ }
+
+ if (check_wp) {
+ if (rsp[15] & 0x30)
+ chip->card_wp |= SD_CARD;
+
+ dev_dbg(rtsx_dev(chip), "CSD WP Status: 0x%x\n", rsp[15]);
+ }
+
+ return STATUS_SUCCESS;
+}
+
+static int sd_set_sample_push_timing(struct rtsx_chip *chip)
+{
+ int retval;
+ struct sd_info *sd_card = &(chip->sd_card);
+ u8 val = 0;
+
+ if ((chip->sd_ctl & SD_PUSH_POINT_CTL_MASK) == SD_PUSH_POINT_DELAY)
+ val |= 0x10;
+
+ if ((chip->sd_ctl & SD_SAMPLE_POINT_CTL_MASK) == SD_SAMPLE_POINT_AUTO) {
+ if (chip->asic_code) {
+ if (CHK_SD_HS(sd_card) || CHK_MMC_52M(sd_card)) {
+ if (val & 0x10)
+ val |= 0x04;
+ else
+ val |= 0x08;
+ }
+ } else {
+ if (val & 0x10)
+ val |= 0x04;
+ else
+ val |= 0x08;
+ }
+ } else if ((chip->sd_ctl & SD_SAMPLE_POINT_CTL_MASK) ==
+ SD_SAMPLE_POINT_DELAY) {
+ if (val & 0x10)
+ val |= 0x04;
+ else
+ val |= 0x08;
+ }
+
+ retval = rtsx_write_register(chip, REG_SD_CFG1, 0x1C, val);
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+
+ return STATUS_SUCCESS;
+}
+
+static void sd_choose_proper_clock(struct rtsx_chip *chip)
+{
+ struct sd_info *sd_card = &(chip->sd_card);
+
+ if (CHK_SD_SDR104(sd_card)) {
+ if (chip->asic_code)
+ sd_card->sd_clock = chip->asic_sd_sdr104_clk;
+ else
+ sd_card->sd_clock = chip->fpga_sd_sdr104_clk;
+
+ } else if (CHK_SD_DDR50(sd_card)) {
+ if (chip->asic_code)
+ sd_card->sd_clock = chip->asic_sd_ddr50_clk;
+ else
+ sd_card->sd_clock = chip->fpga_sd_ddr50_clk;
+
+ } else if (CHK_SD_SDR50(sd_card)) {
+ if (chip->asic_code)
+ sd_card->sd_clock = chip->asic_sd_sdr50_clk;
+ else
+ sd_card->sd_clock = chip->fpga_sd_sdr50_clk;
+
+ } else if (CHK_SD_HS(sd_card)) {
+ if (chip->asic_code)
+ sd_card->sd_clock = chip->asic_sd_hs_clk;
+ else
+ sd_card->sd_clock = chip->fpga_sd_hs_clk;
+
+ } else if (CHK_MMC_52M(sd_card) || CHK_MMC_DDR52(sd_card)) {
+ if (chip->asic_code)
+ sd_card->sd_clock = chip->asic_mmc_52m_clk;
+ else
+ sd_card->sd_clock = chip->fpga_mmc_52m_clk;
+
+ } else if (CHK_MMC_26M(sd_card)) {
+ if (chip->asic_code)
+ sd_card->sd_clock = 48;
+ else
+ sd_card->sd_clock = CLK_50;
+ }
+}
+
+static int sd_set_clock_divider(struct rtsx_chip *chip, u8 clk_div)
+{
+ int retval;
+ u8 mask = 0, val = 0;
+
+ mask = 0x60;
+ if (clk_div == SD_CLK_DIVIDE_0)
+ val = 0x00;
+ else if (clk_div == SD_CLK_DIVIDE_128)
+ val = 0x40;
+ else if (clk_div == SD_CLK_DIVIDE_256)
+ val = 0x20;
+
+ retval = rtsx_write_register(chip, REG_SD_CFG1, mask, val);
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+
+ return STATUS_SUCCESS;
+}
+
+static int sd_set_init_para(struct rtsx_chip *chip)
+{
+ struct sd_info *sd_card = &(chip->sd_card);
+ int retval;
+
+ retval = sd_set_sample_push_timing(chip);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ sd_choose_proper_clock(chip);
+
+ retval = switch_clock(chip, sd_card->sd_clock);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ return STATUS_SUCCESS;
+}
+
+int sd_select_card(struct rtsx_chip *chip, int select)
+{
+ struct sd_info *sd_card = &(chip->sd_card);
+ int retval;
+ u8 cmd_idx, cmd_type;
+ u32 addr;
+
+ if (select) {
+ cmd_idx = SELECT_CARD;
+ cmd_type = SD_RSP_TYPE_R1;
+ addr = sd_card->sd_addr;
+ } else {
+ cmd_idx = DESELECT_CARD;
+ cmd_type = SD_RSP_TYPE_R0;
+ addr = 0;
+ }
+
+ retval = sd_send_cmd_get_rsp(chip, cmd_idx, addr, cmd_type, NULL, 0);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ return STATUS_SUCCESS;
+}
+
+#ifdef SUPPORT_SD_LOCK
+static int sd_update_lock_status(struct rtsx_chip *chip)
+{
+ struct sd_info *sd_card = &(chip->sd_card);
+ int retval;
+ u8 rsp[5];
+
+ retval = sd_send_cmd_get_rsp(chip, SEND_STATUS, sd_card->sd_addr,
+ SD_RSP_TYPE_R1, rsp, 5);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ if (rsp[1] & 0x02)
+ sd_card->sd_lock_status |= SD_LOCKED;
+ else
+ sd_card->sd_lock_status &= ~SD_LOCKED;
+
+ dev_dbg(rtsx_dev(chip), "sd_card->sd_lock_status = 0x%x\n",
+ sd_card->sd_lock_status);
+
+ if (rsp[1] & 0x01) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ return STATUS_SUCCESS;
+}
+#endif
+
+static int sd_wait_state_data_ready(struct rtsx_chip *chip, u8 state,
+ u8 data_ready, int polling_cnt)
+{
+ struct sd_info *sd_card = &(chip->sd_card);
+ int retval, i;
+ u8 rsp[5];
+
+ for (i = 0; i < polling_cnt; i++) {
+ retval = sd_send_cmd_get_rsp(chip, SEND_STATUS,
+ sd_card->sd_addr, SD_RSP_TYPE_R1, rsp,
+ 5);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ if (((rsp[3] & 0x1E) == state) &&
+ ((rsp[3] & 0x01) == data_ready))
+ return STATUS_SUCCESS;
+ }
+
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+}
+
+static int sd_change_bank_voltage(struct rtsx_chip *chip, u8 voltage)
+{
+ int retval;
+
+ if (voltage == SD_IO_3V3) {
+ if (chip->asic_code) {
+ retval = rtsx_write_phy_register(chip, 0x08,
+ 0x4FC0 |
+ chip->phy_voltage);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+ } else {
+ retval = rtsx_write_register(chip, SD_PAD_CTL,
+ SD_IO_USING_1V8, 0);
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+ }
+ } else if (voltage == SD_IO_1V8) {
+ if (chip->asic_code) {
+ retval = rtsx_write_phy_register(chip, 0x08,
+ 0x4C40 |
+ chip->phy_voltage);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+ } else {
+ retval = rtsx_write_register(chip, SD_PAD_CTL,
+ SD_IO_USING_1V8,
+ SD_IO_USING_1V8);
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+ }
+ } else {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ return STATUS_SUCCESS;
+}
+
+static int sd_voltage_switch(struct rtsx_chip *chip)
+{
+ int retval;
+ u8 stat;
+
+ retval = rtsx_write_register(chip, SD_BUS_STAT,
+ SD_CLK_TOGGLE_EN | SD_CLK_FORCE_STOP,
+ SD_CLK_TOGGLE_EN);
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+
+ retval = sd_send_cmd_get_rsp(chip, VOLTAGE_SWITCH, 0, SD_RSP_TYPE_R1,
+ NULL, 0);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ udelay(chip->sd_voltage_switch_delay);
+
+ retval = rtsx_read_register(chip, SD_BUS_STAT, &stat);
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+ if (stat & (SD_CMD_STATUS | SD_DAT3_STATUS | SD_DAT2_STATUS |
+ SD_DAT1_STATUS | SD_DAT0_STATUS)) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ retval = rtsx_write_register(chip, SD_BUS_STAT, 0xFF,
+ SD_CLK_FORCE_STOP);
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+ retval = sd_change_bank_voltage(chip, SD_IO_1V8);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ wait_timeout(50);
+
+ retval = rtsx_write_register(chip, SD_BUS_STAT, 0xFF,
+ SD_CLK_TOGGLE_EN);
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+ wait_timeout(10);
+
+ retval = rtsx_read_register(chip, SD_BUS_STAT, &stat);
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+ if ((stat & (SD_CMD_STATUS | SD_DAT3_STATUS | SD_DAT2_STATUS |
+ SD_DAT1_STATUS | SD_DAT0_STATUS)) !=
+ (SD_CMD_STATUS | SD_DAT3_STATUS | SD_DAT2_STATUS |
+ SD_DAT1_STATUS | SD_DAT0_STATUS)) {
+ dev_dbg(rtsx_dev(chip), "SD_BUS_STAT: 0x%x\n", stat);
+ rtsx_write_register(chip, SD_BUS_STAT,
+ SD_CLK_TOGGLE_EN | SD_CLK_FORCE_STOP, 0);
+ rtsx_write_register(chip, CARD_CLK_EN, 0xFF, 0);
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ retval = rtsx_write_register(chip, SD_BUS_STAT,
+ SD_CLK_TOGGLE_EN | SD_CLK_FORCE_STOP, 0);
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+
+ return STATUS_SUCCESS;
+}
+
+static int sd_reset_dcm(struct rtsx_chip *chip, u8 tune_dir)
+{
+ int retval;
+
+ if (tune_dir == TUNE_RX) {
+ retval = rtsx_write_register(chip, DCM_DRP_CTL, 0xFF,
+ DCM_RESET | DCM_RX);
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+ retval = rtsx_write_register(chip, DCM_DRP_CTL, 0xFF, DCM_RX);
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+ } else {
+ retval = rtsx_write_register(chip, DCM_DRP_CTL, 0xFF,
+ DCM_RESET | DCM_TX);
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+ retval = rtsx_write_register(chip, DCM_DRP_CTL, 0xFF, DCM_TX);
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+ }
+
+ return STATUS_SUCCESS;
+}
+
+static int sd_change_phase(struct rtsx_chip *chip, u8 sample_point, u8 tune_dir)
+{
+ struct sd_info *sd_card = &(chip->sd_card);
+ u16 SD_VP_CTL, SD_DCMPS_CTL;
+ u8 val;
+ int retval;
+ bool ddr_rx = false;
+
+ dev_dbg(rtsx_dev(chip), "sd_change_phase (sample_point = %d, tune_dir = %d)\n",
+ sample_point, tune_dir);
+
+ if (tune_dir == TUNE_RX) {
+ SD_VP_CTL = SD_VPRX_CTL;
+ SD_DCMPS_CTL = SD_DCMPS_RX_CTL;
+ if (CHK_SD_DDR50(sd_card))
+ ddr_rx = true;
+ } else {
+ SD_VP_CTL = SD_VPTX_CTL;
+ SD_DCMPS_CTL = SD_DCMPS_TX_CTL;
+ }
+
+ if (chip->asic_code) {
+ retval = rtsx_write_register(chip, CLK_CTL, CHANGE_CLK,
+ CHANGE_CLK);
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+ retval = rtsx_write_register(chip, SD_VP_CTL, 0x1F,
+ sample_point);
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+ retval = rtsx_write_register(chip, SD_VPCLK0_CTL,
+ PHASE_NOT_RESET, 0);
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+ retval = rtsx_write_register(chip, SD_VPCLK0_CTL,
+ PHASE_NOT_RESET, PHASE_NOT_RESET);
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+ retval = rtsx_write_register(chip, CLK_CTL, CHANGE_CLK, 0);
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+ } else {
+ rtsx_read_register(chip, SD_VP_CTL, &val);
+ dev_dbg(rtsx_dev(chip), "SD_VP_CTL: 0x%x\n", val);
+ rtsx_read_register(chip, SD_DCMPS_CTL, &val);
+ dev_dbg(rtsx_dev(chip), "SD_DCMPS_CTL: 0x%x\n", val);
+
+ if (ddr_rx) {
+ retval = rtsx_write_register(chip, SD_VP_CTL,
+ PHASE_CHANGE,
+ PHASE_CHANGE);
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+ udelay(50);
+ retval = rtsx_write_register(chip, SD_VP_CTL, 0xFF,
+ PHASE_CHANGE | PHASE_NOT_RESET | sample_point);
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+ } else {
+ retval = rtsx_write_register(chip, CLK_CTL,
+ CHANGE_CLK, CHANGE_CLK);
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+ udelay(50);
+ retval = rtsx_write_register(chip, SD_VP_CTL, 0xFF,
+ PHASE_NOT_RESET | sample_point);
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+ }
+ udelay(100);
+
+ rtsx_init_cmd(chip);
+ rtsx_add_cmd(chip, WRITE_REG_CMD, SD_DCMPS_CTL, DCMPS_CHANGE,
+ DCMPS_CHANGE);
+ rtsx_add_cmd(chip, CHECK_REG_CMD, SD_DCMPS_CTL,
+ DCMPS_CHANGE_DONE, DCMPS_CHANGE_DONE);
+ retval = rtsx_send_cmd(chip, SD_CARD, 100);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ goto Fail;
+ }
+
+ val = *rtsx_get_cmd_data(chip);
+ if (val & DCMPS_ERROR) {
+ rtsx_trace(chip);
+ goto Fail;
+ }
+
+ if ((val & DCMPS_CURRENT_PHASE) != sample_point) {
+ rtsx_trace(chip);
+ goto Fail;
+ }
+
+ retval = rtsx_write_register(chip, SD_DCMPS_CTL,
+ DCMPS_CHANGE, 0);
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+ if (ddr_rx) {
+ retval = rtsx_write_register(chip, SD_VP_CTL,
+ PHASE_CHANGE, 0);
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+ } else {
+ retval = rtsx_write_register(chip, CLK_CTL,
+ CHANGE_CLK, 0);
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+ }
+
+ udelay(50);
+ }
+
+ retval = rtsx_write_register(chip, SD_CFG1, SD_ASYNC_FIFO_NOT_RST, 0);
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+
+ return STATUS_SUCCESS;
+
+Fail:
+ rtsx_read_register(chip, SD_VP_CTL, &val);
+ dev_dbg(rtsx_dev(chip), "SD_VP_CTL: 0x%x\n", val);
+ rtsx_read_register(chip, SD_DCMPS_CTL, &val);
+ dev_dbg(rtsx_dev(chip), "SD_DCMPS_CTL: 0x%x\n", val);
+
+ rtsx_write_register(chip, SD_DCMPS_CTL, DCMPS_CHANGE, 0);
+ rtsx_write_register(chip, SD_VP_CTL, PHASE_CHANGE, 0);
+ wait_timeout(10);
+ sd_reset_dcm(chip, tune_dir);
+ return STATUS_FAIL;
+}
+
+static int sd_check_spec(struct rtsx_chip *chip, u8 bus_width)
+{
+ struct sd_info *sd_card = &(chip->sd_card);
+ int retval;
+ u8 cmd[5], buf[8];
+
+ retval = sd_send_cmd_get_rsp(chip, APP_CMD, sd_card->sd_addr,
+ SD_RSP_TYPE_R1, NULL, 0);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ cmd[0] = 0x40 | SEND_SCR;
+ cmd[1] = 0;
+ cmd[2] = 0;
+ cmd[3] = 0;
+ cmd[4] = 0;
+
+ retval = sd_read_data(chip, SD_TM_NORMAL_READ, cmd, 5, 8, 1, bus_width,
+ buf, 8, 250);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_clear_sd_error(chip);
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ memcpy(sd_card->raw_scr, buf, 8);
+
+ if ((buf[0] & 0x0F) == 0) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ return STATUS_SUCCESS;
+}
+
+static int sd_query_switch_result(struct rtsx_chip *chip, u8 func_group,
+ u8 func_to_switch, u8 *buf, int buf_len)
+{
+ u8 support_mask = 0, query_switch = 0, switch_busy = 0;
+ int support_offset = 0, query_switch_offset = 0, check_busy_offset = 0;
+
+ if (func_group == SD_FUNC_GROUP_1) {
+ support_offset = FUNCTION_GROUP1_SUPPORT_OFFSET;
+ query_switch_offset = FUNCTION_GROUP1_QUERY_SWITCH_OFFSET;
+ check_busy_offset = FUNCTION_GROUP1_CHECK_BUSY_OFFSET;
+
+ switch (func_to_switch) {
+ case HS_SUPPORT:
+ support_mask = HS_SUPPORT_MASK;
+ query_switch = HS_QUERY_SWITCH_OK;
+ switch_busy = HS_SWITCH_BUSY;
+ break;
+
+ case SDR50_SUPPORT:
+ support_mask = SDR50_SUPPORT_MASK;
+ query_switch = SDR50_QUERY_SWITCH_OK;
+ switch_busy = SDR50_SWITCH_BUSY;
+ break;
+
+ case SDR104_SUPPORT:
+ support_mask = SDR104_SUPPORT_MASK;
+ query_switch = SDR104_QUERY_SWITCH_OK;
+ switch_busy = SDR104_SWITCH_BUSY;
+ break;
+
+ case DDR50_SUPPORT:
+ support_mask = DDR50_SUPPORT_MASK;
+ query_switch = DDR50_QUERY_SWITCH_OK;
+ switch_busy = DDR50_SWITCH_BUSY;
+ break;
+
+ default:
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+ } else if (func_group == SD_FUNC_GROUP_3) {
+ support_offset = FUNCTION_GROUP3_SUPPORT_OFFSET;
+ query_switch_offset = FUNCTION_GROUP3_QUERY_SWITCH_OFFSET;
+ check_busy_offset = FUNCTION_GROUP3_CHECK_BUSY_OFFSET;
+
+ switch (func_to_switch) {
+ case DRIVING_TYPE_A:
+ support_mask = DRIVING_TYPE_A_MASK;
+ query_switch = TYPE_A_QUERY_SWITCH_OK;
+ switch_busy = TYPE_A_SWITCH_BUSY;
+ break;
+
+ case DRIVING_TYPE_C:
+ support_mask = DRIVING_TYPE_C_MASK;
+ query_switch = TYPE_C_QUERY_SWITCH_OK;
+ switch_busy = TYPE_C_SWITCH_BUSY;
+ break;
+
+ case DRIVING_TYPE_D:
+ support_mask = DRIVING_TYPE_D_MASK;
+ query_switch = TYPE_D_QUERY_SWITCH_OK;
+ switch_busy = TYPE_D_SWITCH_BUSY;
+ break;
+
+ default:
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+ } else if (func_group == SD_FUNC_GROUP_4) {
+ support_offset = FUNCTION_GROUP4_SUPPORT_OFFSET;
+ query_switch_offset = FUNCTION_GROUP4_QUERY_SWITCH_OFFSET;
+ check_busy_offset = FUNCTION_GROUP4_CHECK_BUSY_OFFSET;
+
+ switch (func_to_switch) {
+ case CURRENT_LIMIT_400:
+ support_mask = CURRENT_LIMIT_400_MASK;
+ query_switch = CURRENT_LIMIT_400_QUERY_SWITCH_OK;
+ switch_busy = CURRENT_LIMIT_400_SWITCH_BUSY;
+ break;
+
+ case CURRENT_LIMIT_600:
+ support_mask = CURRENT_LIMIT_600_MASK;
+ query_switch = CURRENT_LIMIT_600_QUERY_SWITCH_OK;
+ switch_busy = CURRENT_LIMIT_600_SWITCH_BUSY;
+ break;
+
+ case CURRENT_LIMIT_800:
+ support_mask = CURRENT_LIMIT_800_MASK;
+ query_switch = CURRENT_LIMIT_800_QUERY_SWITCH_OK;
+ switch_busy = CURRENT_LIMIT_800_SWITCH_BUSY;
+ break;
+
+ default:
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+ } else {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ if (func_group == SD_FUNC_GROUP_1) {
+ if (!(buf[support_offset] & support_mask) ||
+ ((buf[query_switch_offset] & 0x0F) != query_switch)) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+ }
+
+ /* Check 'Busy Status' */
+ if ((buf[DATA_STRUCTURE_VER_OFFSET] == 0x01) &&
+ ((buf[check_busy_offset] & switch_busy) == switch_busy)) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ return STATUS_SUCCESS;
+}
+
+static int sd_check_switch_mode(struct rtsx_chip *chip, u8 mode,
+ u8 func_group, u8 func_to_switch, u8 bus_width)
+{
+ struct sd_info *sd_card = &(chip->sd_card);
+ int retval;
+ u8 cmd[5], buf[64];
+
+ dev_dbg(rtsx_dev(chip), "sd_check_switch_mode (mode = %d, func_group = %d, func_to_switch = %d)\n",
+ mode, func_group, func_to_switch);
+
+ cmd[0] = 0x40 | SWITCH;
+ cmd[1] = mode;
+
+ if (func_group == SD_FUNC_GROUP_1) {
+ cmd[2] = 0xFF;
+ cmd[3] = 0xFF;
+ cmd[4] = 0xF0 + func_to_switch;
+ } else if (func_group == SD_FUNC_GROUP_3) {
+ cmd[2] = 0xFF;
+ cmd[3] = 0xF0 + func_to_switch;
+ cmd[4] = 0xFF;
+ } else if (func_group == SD_FUNC_GROUP_4) {
+ cmd[2] = 0xFF;
+ cmd[3] = 0x0F + (func_to_switch << 4);
+ cmd[4] = 0xFF;
+ } else {
+ cmd[1] = SD_CHECK_MODE;
+ cmd[2] = 0xFF;
+ cmd[3] = 0xFF;
+ cmd[4] = 0xFF;
+ }
+
+ retval = sd_read_data(chip, SD_TM_NORMAL_READ, cmd, 5, 64, 1, bus_width,
+ buf, 64, 250);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_clear_sd_error(chip);
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ dev_dbg(rtsx_dev(chip), "%*ph\n", 64, buf);
+
+ if (func_group == NO_ARGUMENT) {
+ sd_card->func_group1_mask = buf[0x0D];
+ sd_card->func_group2_mask = buf[0x0B];
+ sd_card->func_group3_mask = buf[0x09];
+ sd_card->func_group4_mask = buf[0x07];
+
+ dev_dbg(rtsx_dev(chip), "func_group1_mask = 0x%02x\n",
+ buf[0x0D]);
+ dev_dbg(rtsx_dev(chip), "func_group2_mask = 0x%02x\n",
+ buf[0x0B]);
+ dev_dbg(rtsx_dev(chip), "func_group3_mask = 0x%02x\n",
+ buf[0x09]);
+ dev_dbg(rtsx_dev(chip), "func_group4_mask = 0x%02x\n",
+ buf[0x07]);
+ } else {
+ /* Maximum current consumption, check whether current is
+ * acceptable; bit[511:496] = 0x0000 means some error happened.
+ */
+ u16 cc = ((u16)buf[0] << 8) | buf[1];
+
+ dev_dbg(rtsx_dev(chip), "Maximum current consumption: %dmA\n",
+ cc);
+ if ((cc == 0) || (cc > 800)) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ retval = sd_query_switch_result(chip, func_group,
+ func_to_switch, buf, 64);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ if ((cc > 400) || (func_to_switch > CURRENT_LIMIT_400)) {
+ retval = rtsx_write_register(chip, OCPPARA2,
+ SD_OCP_THD_MASK,
+ chip->sd_800mA_ocp_thd);
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+ retval = rtsx_write_register(chip, CARD_PWR_CTL,
+ PMOS_STRG_MASK,
+ PMOS_STRG_800mA);
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+ }
+ }
+
+ return STATUS_SUCCESS;
+}
+
+static u8 downgrade_switch_mode(u8 func_group, u8 func_to_switch)
+{
+ if (func_group == SD_FUNC_GROUP_1) {
+ if (func_to_switch > HS_SUPPORT)
+ func_to_switch--;
+
+ } else if (func_group == SD_FUNC_GROUP_4) {
+ if (func_to_switch > CURRENT_LIMIT_200)
+ func_to_switch--;
+ }
+
+ return func_to_switch;
+}
+
+static int sd_check_switch(struct rtsx_chip *chip,
+ u8 func_group, u8 func_to_switch, u8 bus_width)
+{
+ int retval;
+ int i;
+ bool switch_good = false;
+
+ for (i = 0; i < 3; i++) {
+ if (detect_card_cd(chip, SD_CARD) != STATUS_SUCCESS) {
+ sd_set_err_code(chip, SD_NO_CARD);
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ retval = sd_check_switch_mode(chip, SD_CHECK_MODE, func_group,
+ func_to_switch, bus_width);
+ if (retval == STATUS_SUCCESS) {
+ u8 stat;
+
+ retval = sd_check_switch_mode(chip, SD_SWITCH_MODE,
+ func_group, func_to_switch, bus_width);
+ if (retval == STATUS_SUCCESS) {
+ switch_good = true;
+ break;
+ }
+
+ retval = rtsx_read_register(chip, SD_STAT1, &stat);
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+ if (stat & SD_CRC16_ERR) {
+ dev_dbg(rtsx_dev(chip), "SD CRC16 error when switching mode\n");
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+ }
+
+ func_to_switch = downgrade_switch_mode(func_group,
+ func_to_switch);
+
+ wait_timeout(20);
+ }
+
+ if (!switch_good) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ return STATUS_SUCCESS;
+}
+
+static int sd_switch_function(struct rtsx_chip *chip, u8 bus_width)
+{
+ struct sd_info *sd_card = &(chip->sd_card);
+ int retval;
+ int i;
+ u8 func_to_switch = 0;
+
+ /* Get supported functions */
+ retval = sd_check_switch_mode(chip, SD_CHECK_MODE,
+ NO_ARGUMENT, NO_ARGUMENT, bus_width);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ sd_card->func_group1_mask &= ~(sd_card->sd_switch_fail);
+
+ /* Function Group 1: Access Mode */
+ for (i = 0; i < 4; i++) {
+ switch ((u8)(chip->sd_speed_prior >> (i*8))) {
+ case SDR104_SUPPORT:
+ if ((sd_card->func_group1_mask & SDR104_SUPPORT_MASK)
+ && chip->sdr104_en) {
+ func_to_switch = SDR104_SUPPORT;
+ }
+ break;
+
+ case DDR50_SUPPORT:
+ if ((sd_card->func_group1_mask & DDR50_SUPPORT_MASK)
+ && chip->ddr50_en) {
+ func_to_switch = DDR50_SUPPORT;
+ }
+ break;
+
+ case SDR50_SUPPORT:
+ if ((sd_card->func_group1_mask & SDR50_SUPPORT_MASK)
+ && chip->sdr50_en) {
+ func_to_switch = SDR50_SUPPORT;
+ }
+ break;
+
+ case HS_SUPPORT:
+ if (sd_card->func_group1_mask & HS_SUPPORT_MASK)
+ func_to_switch = HS_SUPPORT;
+
+ break;
+
+ default:
+ continue;
+ }
+
+
+ if (func_to_switch)
+ break;
+
+ }
+ dev_dbg(rtsx_dev(chip), "SD_FUNC_GROUP_1: func_to_switch = 0x%02x",
+ func_to_switch);
+
+#ifdef SUPPORT_SD_LOCK
+ if ((sd_card->sd_lock_status & SD_SDR_RST)
+ && (DDR50_SUPPORT == func_to_switch)
+ && (sd_card->func_group1_mask & SDR50_SUPPORT_MASK)) {
+ func_to_switch = SDR50_SUPPORT;
+ dev_dbg(rtsx_dev(chip), "Using SDR50 instead of DDR50 for SD Lock\n");
+ }
+#endif
+
+ if (func_to_switch) {
+ retval = sd_check_switch(chip, SD_FUNC_GROUP_1, func_to_switch,
+ bus_width);
+ if (retval != STATUS_SUCCESS) {
+ if (func_to_switch == SDR104_SUPPORT) {
+ sd_card->sd_switch_fail = SDR104_SUPPORT_MASK;
+ } else if (func_to_switch == DDR50_SUPPORT) {
+ sd_card->sd_switch_fail = SDR104_SUPPORT_MASK |
+ DDR50_SUPPORT_MASK;
+ } else if (func_to_switch == SDR50_SUPPORT) {
+ sd_card->sd_switch_fail = SDR104_SUPPORT_MASK |
+ DDR50_SUPPORT_MASK | SDR50_SUPPORT_MASK;
+ }
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ if (func_to_switch == SDR104_SUPPORT)
+ SET_SD_SDR104(sd_card);
+ else if (func_to_switch == DDR50_SUPPORT)
+ SET_SD_DDR50(sd_card);
+ else if (func_to_switch == SDR50_SUPPORT)
+ SET_SD_SDR50(sd_card);
+ else
+ SET_SD_HS(sd_card);
+ }
+
+ if (CHK_SD_DDR50(sd_card)) {
+ retval = rtsx_write_register(chip, SD_PUSH_POINT_CTL, 0x06,
+ 0x04);
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+ retval = sd_set_sample_push_timing(chip);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+ }
+
+ if (!func_to_switch || (func_to_switch == HS_SUPPORT)) {
+ /* Do not try to switch current limit if the card doesn't
+ * support UHS mode or we don't want it to support UHS mode
+ */
+ return STATUS_SUCCESS;
+ }
+
+ /* Function Group 4: Current Limit */
+ func_to_switch = 0xFF;
+
+ for (i = 0; i < 4; i++) {
+ switch ((u8)(chip->sd_current_prior >> (i*8))) {
+ case CURRENT_LIMIT_800:
+ if (sd_card->func_group4_mask & CURRENT_LIMIT_800_MASK)
+ func_to_switch = CURRENT_LIMIT_800;
+
+ break;
+
+ case CURRENT_LIMIT_600:
+ if (sd_card->func_group4_mask & CURRENT_LIMIT_600_MASK)
+ func_to_switch = CURRENT_LIMIT_600;
+
+ break;
+
+ case CURRENT_LIMIT_400:
+ if (sd_card->func_group4_mask & CURRENT_LIMIT_400_MASK)
+ func_to_switch = CURRENT_LIMIT_400;
+
+ break;
+
+ case CURRENT_LIMIT_200:
+ if (sd_card->func_group4_mask & CURRENT_LIMIT_200_MASK)
+ func_to_switch = CURRENT_LIMIT_200;
+
+ break;
+
+ default:
+ continue;
+ }
+
+ if (func_to_switch != 0xFF)
+ break;
+ }
+
+ dev_dbg(rtsx_dev(chip), "SD_FUNC_GROUP_4: func_to_switch = 0x%02x",
+ func_to_switch);
+
+ if (func_to_switch <= CURRENT_LIMIT_800) {
+ retval = sd_check_switch(chip, SD_FUNC_GROUP_4, func_to_switch,
+ bus_width);
+ if (retval != STATUS_SUCCESS) {
+ if (sd_check_err_code(chip, SD_NO_CARD)) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+ }
+ dev_dbg(rtsx_dev(chip), "Switch current limit finished! (%d)\n",
+ retval);
+ }
+
+ if (CHK_SD_DDR50(sd_card)) {
+ retval = rtsx_write_register(chip, SD_PUSH_POINT_CTL, 0x06, 0);
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+ }
+
+ return STATUS_SUCCESS;
+}
+
+static int sd_wait_data_idle(struct rtsx_chip *chip)
+{
+ int retval = STATUS_TIMEDOUT;
+ int i;
+ u8 val = 0;
+
+ for (i = 0; i < 100; i++) {
+ retval = rtsx_read_register(chip, SD_DATA_STATE, &val);
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+ if (val & SD_DATA_IDLE) {
+ retval = STATUS_SUCCESS;
+ break;
+ }
+ udelay(100);
+ }
+ dev_dbg(rtsx_dev(chip), "SD_DATA_STATE: 0x%02x\n", val);
+
+ return retval;
+}
+
+static int sd_sdr_tuning_rx_cmd(struct rtsx_chip *chip, u8 sample_point)
+{
+ int retval;
+ u8 cmd[5];
+
+ retval = sd_change_phase(chip, sample_point, TUNE_RX);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ cmd[0] = 0x40 | SEND_TUNING_PATTERN;
+ cmd[1] = 0;
+ cmd[2] = 0;
+ cmd[3] = 0;
+ cmd[4] = 0;
+
+ retval = sd_read_data(chip, SD_TM_AUTO_TUNING,
+ cmd, 5, 0x40, 1, SD_BUS_WIDTH_4, NULL, 0, 100);
+ if (retval != STATUS_SUCCESS) {
+ (void)sd_wait_data_idle(chip);
+
+ rtsx_clear_sd_error(chip);
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ return STATUS_SUCCESS;
+}
+
+static int sd_ddr_tuning_rx_cmd(struct rtsx_chip *chip, u8 sample_point)
+{
+ struct sd_info *sd_card = &(chip->sd_card);
+ int retval;
+ u8 cmd[5];
+
+ retval = sd_change_phase(chip, sample_point, TUNE_RX);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ dev_dbg(rtsx_dev(chip), "sd ddr tuning rx\n");
+
+ retval = sd_send_cmd_get_rsp(chip, APP_CMD, sd_card->sd_addr,
+ SD_RSP_TYPE_R1, NULL, 0);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ cmd[0] = 0x40 | SD_STATUS;
+ cmd[1] = 0;
+ cmd[2] = 0;
+ cmd[3] = 0;
+ cmd[4] = 0;
+
+ retval = sd_read_data(chip, SD_TM_NORMAL_READ,
+ cmd, 5, 64, 1, SD_BUS_WIDTH_4, NULL, 0, 100);
+ if (retval != STATUS_SUCCESS) {
+ (void)sd_wait_data_idle(chip);
+
+ rtsx_clear_sd_error(chip);
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ return STATUS_SUCCESS;
+}
+
+static int mmc_ddr_tunning_rx_cmd(struct rtsx_chip *chip, u8 sample_point)
+{
+ struct sd_info *sd_card = &(chip->sd_card);
+ int retval;
+ u8 cmd[5], bus_width;
+
+ if (CHK_MMC_8BIT(sd_card))
+ bus_width = SD_BUS_WIDTH_8;
+ else if (CHK_MMC_4BIT(sd_card))
+ bus_width = SD_BUS_WIDTH_4;
+ else
+ bus_width = SD_BUS_WIDTH_1;
+
+ retval = sd_change_phase(chip, sample_point, TUNE_RX);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ dev_dbg(rtsx_dev(chip), "mmc ddr tuning rx\n");
+
+ cmd[0] = 0x40 | SEND_EXT_CSD;
+ cmd[1] = 0;
+ cmd[2] = 0;
+ cmd[3] = 0;
+ cmd[4] = 0;
+
+ retval = sd_read_data(chip, SD_TM_NORMAL_READ,
+ cmd, 5, 0x200, 1, bus_width, NULL, 0, 100);
+ if (retval != STATUS_SUCCESS) {
+ (void)sd_wait_data_idle(chip);
+
+ rtsx_clear_sd_error(chip);
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ return STATUS_SUCCESS;
+}
+
+static int sd_sdr_tuning_tx_cmd(struct rtsx_chip *chip, u8 sample_point)
+{
+ struct sd_info *sd_card = &(chip->sd_card);
+ int retval;
+
+ retval = sd_change_phase(chip, sample_point, TUNE_TX);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ retval = rtsx_write_register(chip, SD_CFG3, SD_RSP_80CLK_TIMEOUT_EN,
+ SD_RSP_80CLK_TIMEOUT_EN);
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+
+ retval = sd_send_cmd_get_rsp(chip, SEND_STATUS, sd_card->sd_addr,
+ SD_RSP_TYPE_R1, NULL, 0);
+ if (retval != STATUS_SUCCESS) {
+ if (sd_check_err_code(chip, SD_RSP_TIMEOUT)) {
+ rtsx_write_register(chip, SD_CFG3,
+ SD_RSP_80CLK_TIMEOUT_EN, 0);
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+ }
+
+ retval = rtsx_write_register(chip, SD_CFG3, SD_RSP_80CLK_TIMEOUT_EN,
+ 0);
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+
+ return STATUS_SUCCESS;
+}
+
+static int sd_ddr_tuning_tx_cmd(struct rtsx_chip *chip, u8 sample_point)
+{
+ struct sd_info *sd_card = &(chip->sd_card);
+ int retval;
+ u8 cmd[5], bus_width;
+
+ retval = sd_change_phase(chip, sample_point, TUNE_TX);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ if (CHK_SD(sd_card)) {
+ bus_width = SD_BUS_WIDTH_4;
+ } else {
+ if (CHK_MMC_8BIT(sd_card))
+ bus_width = SD_BUS_WIDTH_8;
+ else if (CHK_MMC_4BIT(sd_card))
+ bus_width = SD_BUS_WIDTH_4;
+ else
+ bus_width = SD_BUS_WIDTH_1;
+ }
+
+ retval = sd_wait_state_data_ready(chip, 0x08, 1, 1000);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ retval = rtsx_write_register(chip, SD_CFG3, SD_RSP_80CLK_TIMEOUT_EN,
+ SD_RSP_80CLK_TIMEOUT_EN);
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+
+ cmd[0] = 0x40 | PROGRAM_CSD;
+ cmd[1] = 0;
+ cmd[2] = 0;
+ cmd[3] = 0;
+ cmd[4] = 0;
+
+ retval = sd_write_data(chip, SD_TM_AUTO_WRITE_2,
+ cmd, 5, 16, 1, bus_width, sd_card->raw_csd, 16, 100);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_clear_sd_error(chip);
+ rtsx_write_register(chip, SD_CFG3, SD_RSP_80CLK_TIMEOUT_EN, 0);
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ retval = rtsx_write_register(chip, SD_CFG3, SD_RSP_80CLK_TIMEOUT_EN,
+ 0);
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+
+ sd_send_cmd_get_rsp(chip, SEND_STATUS, sd_card->sd_addr, SD_RSP_TYPE_R1,
+ NULL, 0);
+
+ return STATUS_SUCCESS;
+}
+
+static u8 sd_search_final_phase(struct rtsx_chip *chip, u32 phase_map,
+ u8 tune_dir)
+{
+ struct sd_info *sd_card = &(chip->sd_card);
+ struct timing_phase_path path[MAX_PHASE + 1];
+ int i, j, cont_path_cnt;
+ bool new_block;
+ int max_len, final_path_idx;
+ u8 final_phase = 0xFF;
+
+ if (phase_map == 0xFFFFFFFF) {
+ if (tune_dir == TUNE_RX)
+ final_phase = (u8)chip->sd_default_rx_phase;
+ else
+ final_phase = (u8)chip->sd_default_tx_phase;
+
+ goto Search_Finish;
+ }
+
+ cont_path_cnt = 0;
+ new_block = true;
+ j = 0;
+ for (i = 0; i < MAX_PHASE + 1; i++) {
+ if (phase_map & (1 << i)) {
+ if (new_block) {
+ new_block = false;
+ j = cont_path_cnt++;
+ path[j].start = i;
+ path[j].end = i;
+ } else {
+ path[j].end = i;
+ }
+ } else {
+ new_block = true;
+ if (cont_path_cnt) {
+ int idx = cont_path_cnt - 1;
+
+ path[idx].len = path[idx].end -
+ path[idx].start + 1;
+ path[idx].mid = path[idx].start +
+ path[idx].len / 2;
+ }
+ }
+ }
+
+ if (cont_path_cnt == 0) {
+ dev_dbg(rtsx_dev(chip), "No continuous phase path\n");
+ goto Search_Finish;
+ } else {
+ int idx = cont_path_cnt - 1;
+
+ path[idx].len = path[idx].end - path[idx].start + 1;
+ path[idx].mid = path[idx].start + path[idx].len / 2;
+ }
+
+ if ((path[0].start == 0) &&
+ (path[cont_path_cnt - 1].end == MAX_PHASE)) {
+ path[0].start = path[cont_path_cnt - 1].start - MAX_PHASE - 1;
+ path[0].len += path[cont_path_cnt - 1].len;
+ path[0].mid = path[0].start + path[0].len / 2;
+ if (path[0].mid < 0)
+ path[0].mid += MAX_PHASE + 1;
+
+ cont_path_cnt--;
+ }
+
+ max_len = 0;
+ final_phase = 0;
+ final_path_idx = 0;
+ for (i = 0; i < cont_path_cnt; i++) {
+ if (path[i].len > max_len) {
+ max_len = path[i].len;
+ final_phase = (u8)path[i].mid;
+ final_path_idx = i;
+ }
+
+ dev_dbg(rtsx_dev(chip), "path[%d].start = %d\n",
+ i, path[i].start);
+ dev_dbg(rtsx_dev(chip), "path[%d].end = %d\n", i, path[i].end);
+ dev_dbg(rtsx_dev(chip), "path[%d].len = %d\n", i, path[i].len);
+ dev_dbg(rtsx_dev(chip), "path[%d].mid = %d\n", i, path[i].mid);
+ dev_dbg(rtsx_dev(chip), "\n");
+ }
+
+ if (tune_dir == TUNE_TX) {
+ if (CHK_SD_SDR104(sd_card)) {
+ if (max_len > 15) {
+ int temp_mid = (max_len - 16) / 2;
+ int temp_final_phase =
+ path[final_path_idx].end -
+ (max_len - (6 + temp_mid));
+
+ if (temp_final_phase < 0)
+ final_phase = (u8)(temp_final_phase +
+ MAX_PHASE + 1);
+ else
+ final_phase = (u8)temp_final_phase;
+ }
+ } else if (CHK_SD_SDR50(sd_card)) {
+ if (max_len > 12) {
+ int temp_mid = (max_len - 13) / 2;
+ int temp_final_phase =
+ path[final_path_idx].end -
+ (max_len - (3 + temp_mid));
+
+ if (temp_final_phase < 0)
+ final_phase = (u8)(temp_final_phase +
+ MAX_PHASE + 1);
+ else
+ final_phase = (u8)temp_final_phase;
+ }
+ }
+ }
+
+Search_Finish:
+ dev_dbg(rtsx_dev(chip), "Final chosen phase: %d\n", final_phase);
+ return final_phase;
+}
+
+static int sd_tuning_rx(struct rtsx_chip *chip)
+{
+ struct sd_info *sd_card = &(chip->sd_card);
+ int retval;
+ int i, j;
+ u32 raw_phase_map[3], phase_map;
+ u8 final_phase;
+ int (*tuning_cmd)(struct rtsx_chip *chip, u8 sample_point);
+
+ if (CHK_SD(sd_card)) {
+ if (CHK_SD_DDR50(sd_card))
+ tuning_cmd = sd_ddr_tuning_rx_cmd;
+ else
+ tuning_cmd = sd_sdr_tuning_rx_cmd;
+
+ } else {
+ if (CHK_MMC_DDR52(sd_card))
+ tuning_cmd = mmc_ddr_tunning_rx_cmd;
+ else {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+ }
+
+ for (i = 0; i < 3; i++) {
+ raw_phase_map[i] = 0;
+ for (j = MAX_PHASE; j >= 0; j--) {
+ if (detect_card_cd(chip, SD_CARD) != STATUS_SUCCESS) {
+ sd_set_err_code(chip, SD_NO_CARD);
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ retval = tuning_cmd(chip, (u8)j);
+ if (retval == STATUS_SUCCESS)
+ raw_phase_map[i] |= 1 << j;
+ }
+ }
+
+ phase_map = raw_phase_map[0] & raw_phase_map[1] & raw_phase_map[2];
+ for (i = 0; i < 3; i++)
+ dev_dbg(rtsx_dev(chip), "RX raw_phase_map[%d] = 0x%08x\n",
+ i, raw_phase_map[i]);
+
+ dev_dbg(rtsx_dev(chip), "RX phase_map = 0x%08x\n", phase_map);
+
+ final_phase = sd_search_final_phase(chip, phase_map, TUNE_RX);
+ if (final_phase == 0xFF) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ retval = sd_change_phase(chip, final_phase, TUNE_RX);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ return STATUS_SUCCESS;
+}
+
+static int sd_ddr_pre_tuning_tx(struct rtsx_chip *chip)
+{
+ struct sd_info *sd_card = &(chip->sd_card);
+ int retval;
+ int i;
+ u32 phase_map;
+ u8 final_phase;
+
+ retval = rtsx_write_register(chip, SD_CFG3, SD_RSP_80CLK_TIMEOUT_EN,
+ SD_RSP_80CLK_TIMEOUT_EN);
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+
+ phase_map = 0;
+ for (i = MAX_PHASE; i >= 0; i--) {
+ if (detect_card_cd(chip, SD_CARD) != STATUS_SUCCESS) {
+ sd_set_err_code(chip, SD_NO_CARD);
+ rtsx_write_register(chip, SD_CFG3,
+ SD_RSP_80CLK_TIMEOUT_EN, 0);
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ retval = sd_change_phase(chip, (u8)i, TUNE_TX);
+ if (retval != STATUS_SUCCESS)
+ continue;
+
+ retval = sd_send_cmd_get_rsp(chip, SEND_STATUS,
+ sd_card->sd_addr, SD_RSP_TYPE_R1, NULL,
+ 0);
+ if ((retval == STATUS_SUCCESS) ||
+ !sd_check_err_code(chip, SD_RSP_TIMEOUT))
+ phase_map |= 1 << i;
+ }
+
+ retval = rtsx_write_register(chip, SD_CFG3, SD_RSP_80CLK_TIMEOUT_EN,
+ 0);
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+
+ dev_dbg(rtsx_dev(chip), "DDR TX pre tune phase_map = 0x%08x\n",
+ phase_map);
+
+ final_phase = sd_search_final_phase(chip, phase_map, TUNE_TX);
+ if (final_phase == 0xFF) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ retval = sd_change_phase(chip, final_phase, TUNE_TX);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ dev_dbg(rtsx_dev(chip), "DDR TX pre tune phase: %d\n",
+ (int)final_phase);
+
+ return STATUS_SUCCESS;
+}
+
+static int sd_tuning_tx(struct rtsx_chip *chip)
+{
+ struct sd_info *sd_card = &(chip->sd_card);
+ int retval;
+ int i, j;
+ u32 raw_phase_map[3], phase_map;
+ u8 final_phase;
+ int (*tuning_cmd)(struct rtsx_chip *chip, u8 sample_point);
+
+ if (CHK_SD(sd_card)) {
+ if (CHK_SD_DDR50(sd_card))
+ tuning_cmd = sd_ddr_tuning_tx_cmd;
+ else
+ tuning_cmd = sd_sdr_tuning_tx_cmd;
+
+ } else {
+ if (CHK_MMC_DDR52(sd_card))
+ tuning_cmd = sd_ddr_tuning_tx_cmd;
+ else {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+ }
+
+ for (i = 0; i < 3; i++) {
+ raw_phase_map[i] = 0;
+ for (j = MAX_PHASE; j >= 0; j--) {
+ if (detect_card_cd(chip, SD_CARD) != STATUS_SUCCESS) {
+ sd_set_err_code(chip, SD_NO_CARD);
+ rtsx_write_register(chip, SD_CFG3,
+ SD_RSP_80CLK_TIMEOUT_EN, 0);
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ retval = tuning_cmd(chip, (u8)j);
+ if (retval == STATUS_SUCCESS)
+ raw_phase_map[i] |= 1 << j;
+ }
+ }
+
+ phase_map = raw_phase_map[0] & raw_phase_map[1] & raw_phase_map[2];
+ for (i = 0; i < 3; i++)
+ dev_dbg(rtsx_dev(chip), "TX raw_phase_map[%d] = 0x%08x\n",
+ i, raw_phase_map[i]);
+
+ dev_dbg(rtsx_dev(chip), "TX phase_map = 0x%08x\n", phase_map);
+
+ final_phase = sd_search_final_phase(chip, phase_map, TUNE_TX);
+ if (final_phase == 0xFF) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ retval = sd_change_phase(chip, final_phase, TUNE_TX);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ return STATUS_SUCCESS;
+}
+
+static int sd_sdr_tuning(struct rtsx_chip *chip)
+{
+ int retval;
+
+ retval = sd_tuning_tx(chip);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ retval = sd_tuning_rx(chip);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ return STATUS_SUCCESS;
+}
+
+static int sd_ddr_tuning(struct rtsx_chip *chip)
+{
+ int retval;
+
+ if (!(chip->sd_ctl & SD_DDR_TX_PHASE_SET_BY_USER)) {
+ retval = sd_ddr_pre_tuning_tx(chip);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+ } else {
+ retval = sd_change_phase(chip, (u8)chip->sd_ddr_tx_phase,
+ TUNE_TX);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+ }
+
+ retval = sd_tuning_rx(chip);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ if (!(chip->sd_ctl & SD_DDR_TX_PHASE_SET_BY_USER)) {
+ retval = sd_tuning_tx(chip);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+ }
+
+ return STATUS_SUCCESS;
+}
+
+static int mmc_ddr_tuning(struct rtsx_chip *chip)
+{
+ int retval;
+
+ if (!(chip->sd_ctl & MMC_DDR_TX_PHASE_SET_BY_USER)) {
+ retval = sd_ddr_pre_tuning_tx(chip);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+ } else {
+ retval = sd_change_phase(chip, (u8)chip->mmc_ddr_tx_phase,
+ TUNE_TX);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+ }
+
+ retval = sd_tuning_rx(chip);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ if (!(chip->sd_ctl & MMC_DDR_TX_PHASE_SET_BY_USER)) {
+ retval = sd_tuning_tx(chip);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+ }
+
+ return STATUS_SUCCESS;
+}
+
+int sd_switch_clock(struct rtsx_chip *chip)
+{
+ struct sd_info *sd_card = &(chip->sd_card);
+ int retval;
+ int re_tuning = 0;
+
+ retval = select_card(chip, SD_CARD);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ retval = switch_clock(chip, sd_card->sd_clock);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ if (re_tuning) {
+ if (CHK_SD(sd_card)) {
+ if (CHK_SD_DDR50(sd_card))
+ retval = sd_ddr_tuning(chip);
+ else
+ retval = sd_sdr_tuning(chip);
+ } else {
+ if (CHK_MMC_DDR52(sd_card))
+ retval = mmc_ddr_tuning(chip);
+ }
+
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+ }
+
+ return STATUS_SUCCESS;
+}
+
+static int sd_prepare_reset(struct rtsx_chip *chip)
+{
+ struct sd_info *sd_card = &(chip->sd_card);
+ int retval;
+
+ if (chip->asic_code)
+ sd_card->sd_clock = 29;
+ else
+ sd_card->sd_clock = CLK_30;
+
+ sd_card->sd_type = 0;
+ sd_card->seq_mode = 0;
+ sd_card->sd_data_buf_ready = 0;
+ sd_card->capacity = 0;
+
+#ifdef SUPPORT_SD_LOCK
+ sd_card->sd_lock_status = 0;
+ sd_card->sd_erase_status = 0;
+#endif
+
+ chip->capacity[chip->card2lun[SD_CARD]] = 0;
+ chip->sd_io = 0;
+
+ retval = sd_set_init_para(chip);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return retval;
+ }
+
+ retval = rtsx_write_register(chip, REG_SD_CFG1, 0xFF, 0x40);
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+
+ retval = rtsx_write_register(chip, CARD_STOP, SD_STOP | SD_CLR_ERR,
+ SD_STOP | SD_CLR_ERR);
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+
+ retval = select_card(chip, SD_CARD);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ return STATUS_SUCCESS;
+}
+
+static int sd_pull_ctl_disable(struct rtsx_chip *chip)
+{
+ int retval;
+
+ if (CHECK_PID(chip, 0x5208)) {
+ retval = rtsx_write_register(chip, CARD_PULL_CTL1, 0xFF,
+ XD_D3_PD | SD_D7_PD | SD_CLK_PD | SD_D5_PD);
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+ retval = rtsx_write_register(chip, CARD_PULL_CTL2, 0xFF,
+ SD_D6_PD | SD_D0_PD | SD_D1_PD | XD_D5_PD);
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+ retval = rtsx_write_register(chip, CARD_PULL_CTL3, 0xFF,
+ SD_D4_PD | XD_CE_PD | XD_CLE_PD | XD_CD_PU);
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+ retval = rtsx_write_register(chip, CARD_PULL_CTL4, 0xFF,
+ XD_RDY_PD | SD_D3_PD | SD_D2_PD | XD_ALE_PD);
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+ retval = rtsx_write_register(chip, CARD_PULL_CTL5, 0xFF,
+ MS_INS_PU | SD_WP_PD | SD_CD_PU | SD_CMD_PD);
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+ retval = rtsx_write_register(chip, CARD_PULL_CTL6, 0xFF,
+ MS_D5_PD | MS_D4_PD);
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+ } else if (CHECK_PID(chip, 0x5288)) {
+ if (CHECK_BARO_PKG(chip, QFN)) {
+ retval = rtsx_write_register(chip, CARD_PULL_CTL1,
+ 0xFF, 0x55);
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+ retval = rtsx_write_register(chip, CARD_PULL_CTL2,
+ 0xFF, 0x55);
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+ retval = rtsx_write_register(chip, CARD_PULL_CTL3,
+ 0xFF, 0x4B);
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+ retval = rtsx_write_register(chip, CARD_PULL_CTL4,
+ 0xFF, 0x69);
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+ }
+ }
+
+ return STATUS_SUCCESS;
+}
+
+int sd_pull_ctl_enable(struct rtsx_chip *chip)
+{
+ int retval;
+
+ rtsx_init_cmd(chip);
+
+ if (CHECK_PID(chip, 0x5208)) {
+ rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_PULL_CTL1, 0xFF,
+ XD_D3_PD | SD_DAT7_PU | SD_CLK_NP | SD_D5_PU);
+ rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_PULL_CTL2, 0xFF,
+ SD_D6_PU | SD_D0_PU | SD_D1_PU | XD_D5_PD);
+ rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_PULL_CTL3, 0xFF,
+ SD_D4_PU | XD_CE_PD | XD_CLE_PD | XD_CD_PU);
+ rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_PULL_CTL4, 0xFF,
+ XD_RDY_PD | SD_D3_PU | SD_D2_PU | XD_ALE_PD);
+ rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_PULL_CTL5, 0xFF,
+ MS_INS_PU | SD_WP_PU | SD_CD_PU | SD_CMD_PU);
+ rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_PULL_CTL6, 0xFF,
+ MS_D5_PD | MS_D4_PD);
+ } else if (CHECK_PID(chip, 0x5288)) {
+ if (CHECK_BARO_PKG(chip, QFN)) {
+ rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_PULL_CTL1, 0xFF,
+ 0xA8);
+ rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_PULL_CTL2, 0xFF,
+ 0x5A);
+ rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_PULL_CTL3, 0xFF,
+ 0x95);
+ rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_PULL_CTL4, 0xFF,
+ 0xAA);
+ }
+ }
+
+ retval = rtsx_send_cmd(chip, SD_CARD, 100);
+ if (retval < 0) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ return STATUS_SUCCESS;
+}
+
+static int sd_init_power(struct rtsx_chip *chip)
+{
+ int retval;
+
+ retval = sd_power_off_card3v3(chip);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ if (!chip->ft2_fast_mode)
+ wait_timeout(250);
+
+ retval = enable_card_clock(chip, SD_CARD);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ if (chip->asic_code) {
+ retval = sd_pull_ctl_enable(chip);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+ } else {
+ retval = rtsx_write_register(chip, FPGA_PULL_CTL,
+ FPGA_SD_PULL_CTL_BIT | 0x20, 0);
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+ }
+
+ if (!chip->ft2_fast_mode) {
+ retval = card_power_on(chip, SD_CARD);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ wait_timeout(260);
+
+#ifdef SUPPORT_OCP
+ if (chip->ocp_stat & (SD_OC_NOW | SD_OC_EVER)) {
+ dev_dbg(rtsx_dev(chip), "Over current, OCPSTAT is 0x%x\n",
+ chip->ocp_stat);
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+#endif
+ }
+
+ retval = rtsx_write_register(chip, CARD_OE, SD_OUTPUT_EN,
+ SD_OUTPUT_EN);
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+
+ return STATUS_SUCCESS;
+}
+
+static int sd_dummy_clock(struct rtsx_chip *chip)
+{
+ int retval;
+
+ retval = rtsx_write_register(chip, REG_SD_CFG3, 0x01, 0x01);
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+ wait_timeout(5);
+ retval = rtsx_write_register(chip, REG_SD_CFG3, 0x01, 0);
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+
+ return STATUS_SUCCESS;
+}
+
+static int sd_read_lba0(struct rtsx_chip *chip)
+{
+ struct sd_info *sd_card = &(chip->sd_card);
+ int retval;
+ u8 cmd[5], bus_width;
+
+ cmd[0] = 0x40 | READ_SINGLE_BLOCK;
+ cmd[1] = 0;
+ cmd[2] = 0;
+ cmd[3] = 0;
+ cmd[4] = 0;
+
+ if (CHK_SD(sd_card)) {
+ bus_width = SD_BUS_WIDTH_4;
+ } else {
+ if (CHK_MMC_8BIT(sd_card))
+ bus_width = SD_BUS_WIDTH_8;
+ else if (CHK_MMC_4BIT(sd_card))
+ bus_width = SD_BUS_WIDTH_4;
+ else
+ bus_width = SD_BUS_WIDTH_1;
+ }
+
+ retval = sd_read_data(chip, SD_TM_NORMAL_READ, cmd,
+ 5, 512, 1, bus_width, NULL, 0, 100);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_clear_sd_error(chip);
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ return STATUS_SUCCESS;
+}
+
+static int sd_check_wp_state(struct rtsx_chip *chip)
+{
+ struct sd_info *sd_card = &(chip->sd_card);
+ int retval;
+ u32 val;
+ u16 sd_card_type;
+ u8 cmd[5], buf[64];
+
+ retval = sd_send_cmd_get_rsp(chip, APP_CMD,
+ sd_card->sd_addr, SD_RSP_TYPE_R1, NULL, 0);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ cmd[0] = 0x40 | SD_STATUS;
+ cmd[1] = 0;
+ cmd[2] = 0;
+ cmd[3] = 0;
+ cmd[4] = 0;
+
+ retval = sd_read_data(chip, SD_TM_NORMAL_READ, cmd, 5, 64, 1,
+ SD_BUS_WIDTH_4, buf, 64, 250);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_clear_sd_error(chip);
+
+ sd_send_cmd_get_rsp(chip, SEND_STATUS, sd_card->sd_addr,
+ SD_RSP_TYPE_R1, NULL, 0);
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ dev_dbg(rtsx_dev(chip), "ACMD13:\n");
+ dev_dbg(rtsx_dev(chip), "%*ph\n", 64, buf);
+
+ sd_card_type = ((u16)buf[2] << 8) | buf[3];
+ dev_dbg(rtsx_dev(chip), "sd_card_type = 0x%04x\n", sd_card_type);
+ if ((sd_card_type == 0x0001) || (sd_card_type == 0x0002)) {
+ /* ROM card or OTP */
+ chip->card_wp |= SD_CARD;
+ }
+
+ /* Check SD Machanical Write-Protect Switch */
+ val = rtsx_readl(chip, RTSX_BIPR);
+ if (val & SD_WRITE_PROTECT)
+ chip->card_wp |= SD_CARD;
+
+ return STATUS_SUCCESS;
+}
+
+static int reset_sd(struct rtsx_chip *chip)
+{
+ struct sd_info *sd_card = &(chip->sd_card);
+ bool hi_cap_flow = false;
+ int retval, i = 0, j = 0, k = 0;
+ bool sd_dont_switch = false;
+ bool support_1v8 = false;
+ bool try_sdio = true;
+ u8 rsp[16];
+ u8 switch_bus_width;
+ u32 voltage = 0;
+ bool sd20_mode = false;
+
+ SET_SD(sd_card);
+
+Switch_Fail:
+
+ i = 0;
+ j = 0;
+ k = 0;
+ hi_cap_flow = false;
+
+#ifdef SUPPORT_SD_LOCK
+ if (sd_card->sd_lock_status & SD_UNLOCK_POW_ON)
+ goto SD_UNLOCK_ENTRY;
+#endif
+
+ retval = sd_prepare_reset(chip);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ retval = sd_dummy_clock(chip);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ if (CHK_SDIO_EXIST(chip) && !CHK_SDIO_IGNORED(chip) && try_sdio) {
+ int rty_cnt = 0;
+
+ for (; rty_cnt < chip->sdio_retry_cnt; rty_cnt++) {
+ if (detect_card_cd(chip, SD_CARD) != STATUS_SUCCESS) {
+ sd_set_err_code(chip, SD_NO_CARD);
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ retval = sd_send_cmd_get_rsp(chip, IO_SEND_OP_COND, 0,
+ SD_RSP_TYPE_R4, rsp, 5);
+ if (retval == STATUS_SUCCESS) {
+ int func_num = (rsp[1] >> 4) & 0x07;
+
+ if (func_num) {
+ dev_dbg(rtsx_dev(chip), "SD_IO card (Function number: %d)!\n",
+ func_num);
+ chip->sd_io = 1;
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ break;
+ }
+
+ sd_init_power(chip);
+
+ sd_dummy_clock(chip);
+ }
+
+ dev_dbg(rtsx_dev(chip), "Normal card!\n");
+ }
+
+ /* Start Initialization Process of SD Card */
+RTY_SD_RST:
+ retval = sd_send_cmd_get_rsp(chip, GO_IDLE_STATE, 0, SD_RSP_TYPE_R0,
+ NULL, 0);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ wait_timeout(20);
+
+ retval = sd_send_cmd_get_rsp(chip, SEND_IF_COND, 0x000001AA,
+ SD_RSP_TYPE_R7, rsp, 5);
+ if (retval == STATUS_SUCCESS) {
+ if ((rsp[4] == 0xAA) && ((rsp[3] & 0x0f) == 0x01)) {
+ hi_cap_flow = true;
+ voltage = SUPPORT_VOLTAGE | 0x40000000;
+ }
+ }
+
+ if (!hi_cap_flow) {
+ voltage = SUPPORT_VOLTAGE;
+
+ retval = sd_send_cmd_get_rsp(chip, GO_IDLE_STATE, 0,
+ SD_RSP_TYPE_R0, NULL, 0);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ wait_timeout(20);
+ }
+
+ do {
+ retval = sd_send_cmd_get_rsp(chip, APP_CMD, 0, SD_RSP_TYPE_R1,
+ NULL, 0);
+ if (retval != STATUS_SUCCESS) {
+ if (detect_card_cd(chip, SD_CARD) != STATUS_SUCCESS) {
+ sd_set_err_code(chip, SD_NO_CARD);
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ j++;
+ if (j < 3)
+ goto RTY_SD_RST;
+ else {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+ }
+
+ retval = sd_send_cmd_get_rsp(chip, SD_APP_OP_COND, voltage,
+ SD_RSP_TYPE_R3, rsp, 5);
+ if (retval != STATUS_SUCCESS) {
+ k++;
+ if (k < 3)
+ goto RTY_SD_RST;
+ else {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+ }
+
+ i++;
+ wait_timeout(20);
+ } while (!(rsp[1] & 0x80) && (i < 255));
+
+ if (i == 255) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ if (hi_cap_flow) {
+ if (rsp[1] & 0x40)
+ SET_SD_HCXC(sd_card);
+ else
+ CLR_SD_HCXC(sd_card);
+
+ support_1v8 = false;
+ } else {
+ CLR_SD_HCXC(sd_card);
+ support_1v8 = false;
+ }
+ dev_dbg(rtsx_dev(chip), "support_1v8 = %d\n", support_1v8);
+
+ if (support_1v8) {
+ retval = sd_voltage_switch(chip);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+ }
+
+ retval = sd_send_cmd_get_rsp(chip, ALL_SEND_CID, 0, SD_RSP_TYPE_R2,
+ NULL, 0);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ for (i = 0; i < 3; i++) {
+ retval = sd_send_cmd_get_rsp(chip, SEND_RELATIVE_ADDR, 0,
+ SD_RSP_TYPE_R6, rsp, 5);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ sd_card->sd_addr = (u32)rsp[1] << 24;
+ sd_card->sd_addr += (u32)rsp[2] << 16;
+
+ if (sd_card->sd_addr)
+ break;
+ }
+
+ retval = sd_check_csd(chip, 1);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ retval = sd_select_card(chip, 1);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+#ifdef SUPPORT_SD_LOCK
+SD_UNLOCK_ENTRY:
+ retval = sd_update_lock_status(chip);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ if (sd_card->sd_lock_status & SD_LOCKED) {
+ sd_card->sd_lock_status |= (SD_LOCK_1BIT_MODE | SD_PWD_EXIST);
+ return STATUS_SUCCESS;
+ } else if (!(sd_card->sd_lock_status & SD_UNLOCK_POW_ON)) {
+ sd_card->sd_lock_status &= ~SD_PWD_EXIST;
+ }
+#endif
+
+ retval = sd_send_cmd_get_rsp(chip, APP_CMD, sd_card->sd_addr,
+ SD_RSP_TYPE_R1, NULL, 0);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ retval = sd_send_cmd_get_rsp(chip, SET_CLR_CARD_DETECT, 0,
+ SD_RSP_TYPE_R1, NULL, 0);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ if (support_1v8) {
+ retval = sd_send_cmd_get_rsp(chip, APP_CMD, sd_card->sd_addr,
+ SD_RSP_TYPE_R1, NULL, 0);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ retval = sd_send_cmd_get_rsp(chip, SET_BUS_WIDTH, 2,
+ SD_RSP_TYPE_R1, NULL, 0);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ switch_bus_width = SD_BUS_WIDTH_4;
+ } else {
+ switch_bus_width = SD_BUS_WIDTH_1;
+ }
+
+ retval = sd_send_cmd_get_rsp(chip, SET_BLOCKLEN, 0x200, SD_RSP_TYPE_R1,
+ NULL, 0);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ retval = sd_set_clock_divider(chip, SD_CLK_DIVIDE_0);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ if (!(sd_card->raw_csd[4] & 0x40))
+ sd_dont_switch = true;
+
+ if (!sd_dont_switch) {
+ if (sd20_mode) {
+ /* Set sd_switch_fail here, because we needn't
+ * switch to UHS mode
+ */
+ sd_card->sd_switch_fail = SDR104_SUPPORT_MASK |
+ DDR50_SUPPORT_MASK | SDR50_SUPPORT_MASK;
+ }
+
+ /* Check the card whether follow SD1.1 spec or higher */
+ retval = sd_check_spec(chip, switch_bus_width);
+ if (retval == STATUS_SUCCESS) {
+ retval = sd_switch_function(chip, switch_bus_width);
+ if (retval != STATUS_SUCCESS) {
+ sd_init_power(chip);
+ sd_dont_switch = true;
+ try_sdio = false;
+
+ goto Switch_Fail;
+ }
+ } else {
+ if (support_1v8) {
+ sd_init_power(chip);
+ sd_dont_switch = true;
+ try_sdio = false;
+
+ goto Switch_Fail;
+ }
+ }
+ }
+
+ if (!support_1v8) {
+ retval = sd_send_cmd_get_rsp(chip, APP_CMD, sd_card->sd_addr,
+ SD_RSP_TYPE_R1, NULL, 0);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ retval = sd_send_cmd_get_rsp(chip, SET_BUS_WIDTH, 2,
+ SD_RSP_TYPE_R1, NULL, 0);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+ }
+
+#ifdef SUPPORT_SD_LOCK
+ sd_card->sd_lock_status &= ~SD_LOCK_1BIT_MODE;
+#endif
+
+ if (!sd20_mode && CHK_SD30_SPEED(sd_card)) {
+ int read_lba0 = 1;
+
+ retval = rtsx_write_register(chip, SD30_DRIVE_SEL, 0x07,
+ chip->sd30_drive_sel_1v8);
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+
+ retval = sd_set_init_para(chip);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ if (CHK_SD_DDR50(sd_card))
+ retval = sd_ddr_tuning(chip);
+ else
+ retval = sd_sdr_tuning(chip);
+
+ if (retval != STATUS_SUCCESS) {
+ if (sd20_mode) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ } else {
+ retval = sd_init_power(chip);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ try_sdio = false;
+ sd20_mode = true;
+ goto Switch_Fail;
+ }
+ }
+
+ sd_send_cmd_get_rsp(chip, SEND_STATUS, sd_card->sd_addr,
+ SD_RSP_TYPE_R1, NULL, 0);
+
+ if (CHK_SD_DDR50(sd_card)) {
+ retval = sd_wait_state_data_ready(chip, 0x08, 1, 1000);
+ if (retval != STATUS_SUCCESS)
+ read_lba0 = 0;
+ }
+
+ if (read_lba0) {
+ retval = sd_read_lba0(chip);
+ if (retval != STATUS_SUCCESS) {
+ if (sd20_mode) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ } else {
+ retval = sd_init_power(chip);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ try_sdio = false;
+ sd20_mode = true;
+ goto Switch_Fail;
+ }
+ }
+ }
+ }
+
+ retval = sd_check_wp_state(chip);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ chip->card_bus_width[chip->card2lun[SD_CARD]] = 4;
+
+#ifdef SUPPORT_SD_LOCK
+ if (sd_card->sd_lock_status & SD_UNLOCK_POW_ON) {
+ retval = rtsx_write_register(chip, REG_SD_BLOCK_CNT_H, 0xFF,
+ 0x02);
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+ retval = rtsx_write_register(chip, REG_SD_BLOCK_CNT_L, 0xFF,
+ 0x00);
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+ }
+#endif
+
+ return STATUS_SUCCESS;
+}
+
+
+static int mmc_test_switch_bus(struct rtsx_chip *chip, u8 width)
+{
+ struct sd_info *sd_card = &(chip->sd_card);
+ int retval;
+ u8 buf[8] = {0}, bus_width, *ptr;
+ u16 byte_cnt;
+ int len;
+
+ retval = sd_send_cmd_get_rsp(chip, BUSTEST_W, 0, SD_RSP_TYPE_R1, NULL,
+ 0);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return SWITCH_FAIL;
+ }
+
+ if (width == MMC_8BIT_BUS) {
+ buf[0] = 0x55;
+ buf[1] = 0xAA;
+ len = 8;
+ byte_cnt = 8;
+ bus_width = SD_BUS_WIDTH_8;
+ } else {
+ buf[0] = 0x5A;
+ len = 4;
+ byte_cnt = 4;
+ bus_width = SD_BUS_WIDTH_4;
+ }
+
+ retval = rtsx_write_register(chip, REG_SD_CFG3, 0x02, 0x02);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return SWITCH_ERR;
+ }
+
+ retval = sd_write_data(chip, SD_TM_AUTO_WRITE_3,
+ NULL, 0, byte_cnt, 1, bus_width, buf, len, 100);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_clear_sd_error(chip);
+ rtsx_write_register(chip, REG_SD_CFG3, 0x02, 0);
+ rtsx_trace(chip);
+ return SWITCH_ERR;
+ }
+
+ retval = rtsx_write_register(chip, REG_SD_CFG3, 0x02, 0);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return SWITCH_ERR;
+ }
+
+ dev_dbg(rtsx_dev(chip), "SD/MMC CMD %d\n", BUSTEST_R);
+
+ rtsx_init_cmd(chip);
+
+ rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_CMD0, 0xFF, 0x40 | BUSTEST_R);
+
+ if (width == MMC_8BIT_BUS)
+ rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_BYTE_CNT_L,
+ 0xFF, 0x08);
+ else
+ rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_BYTE_CNT_L,
+ 0xFF, 0x04);
+
+ rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_BLOCK_CNT_L, 0xFF, 1);
+ rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_BLOCK_CNT_H, 0xFF, 0);
+
+ rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_CFG2, 0xFF,
+ SD_CALCULATE_CRC7 | SD_NO_CHECK_CRC16 | SD_NO_WAIT_BUSY_END|
+ SD_CHECK_CRC7 | SD_RSP_LEN_6);
+ rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_DATA_SOURCE, 0x01,
+ PINGPONG_BUFFER);
+ rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_TRANSFER, 0xFF,
+ SD_TM_NORMAL_READ | SD_TRANSFER_START);
+ rtsx_add_cmd(chip, CHECK_REG_CMD, REG_SD_TRANSFER, SD_TRANSFER_END,
+ SD_TRANSFER_END);
+
+ rtsx_add_cmd(chip, READ_REG_CMD, PPBUF_BASE2, 0, 0);
+ if (width == MMC_8BIT_BUS)
+ rtsx_add_cmd(chip, READ_REG_CMD, PPBUF_BASE2 + 1, 0, 0);
+
+ retval = rtsx_send_cmd(chip, SD_CARD, 100);
+ if (retval < 0) {
+ rtsx_clear_sd_error(chip);
+ rtsx_trace(chip);
+ return SWITCH_ERR;
+ }
+
+ ptr = rtsx_get_cmd_data(chip) + 1;
+
+ if (width == MMC_8BIT_BUS) {
+ dev_dbg(rtsx_dev(chip), "BUSTEST_R [8bits]: 0x%02x 0x%02x\n",
+ ptr[0], ptr[1]);
+ if ((ptr[0] == 0xAA) && (ptr[1] == 0x55)) {
+ u8 rsp[5];
+ u32 arg;
+
+ if (CHK_MMC_DDR52(sd_card))
+ arg = 0x03B70600;
+ else
+ arg = 0x03B70200;
+
+ retval = sd_send_cmd_get_rsp(chip, SWITCH, arg,
+ SD_RSP_TYPE_R1b, rsp, 5);
+ if ((retval == STATUS_SUCCESS) &&
+ !(rsp[4] & MMC_SWITCH_ERR))
+ return SWITCH_SUCCESS;
+ }
+ } else {
+ dev_dbg(rtsx_dev(chip), "BUSTEST_R [4bits]: 0x%02x\n", ptr[0]);
+ if (ptr[0] == 0xA5) {
+ u8 rsp[5];
+ u32 arg;
+
+ if (CHK_MMC_DDR52(sd_card))
+ arg = 0x03B70500;
+ else
+ arg = 0x03B70100;
+
+ retval = sd_send_cmd_get_rsp(chip, SWITCH, arg,
+ SD_RSP_TYPE_R1b, rsp, 5);
+ if ((retval == STATUS_SUCCESS) &&
+ !(rsp[4] & MMC_SWITCH_ERR))
+ return SWITCH_SUCCESS;
+ }
+ }
+
+ rtsx_trace(chip);
+ return SWITCH_FAIL;
+}
+
+
+static int mmc_switch_timing_bus(struct rtsx_chip *chip, bool switch_ddr)
+{
+ struct sd_info *sd_card = &(chip->sd_card);
+ int retval;
+ u8 *ptr, card_type, card_type_mask = 0;
+
+ CLR_MMC_HS(sd_card);
+
+ dev_dbg(rtsx_dev(chip), "SD/MMC CMD %d\n", SEND_EXT_CSD);
+
+ rtsx_init_cmd(chip);
+
+ rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_CMD0, 0xFF,
+ 0x40 | SEND_EXT_CSD);
+ rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_CMD1, 0xFF, 0);
+ rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_CMD2, 0xFF, 0);
+ rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_CMD3, 0xFF, 0);
+ rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_CMD4, 0xFF, 0);
+
+ rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_BYTE_CNT_L, 0xFF, 0);
+ rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_BYTE_CNT_H, 0xFF, 2);
+ rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_BLOCK_CNT_L, 0xFF, 1);
+ rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_BLOCK_CNT_H, 0xFF, 0);
+
+ rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_CFG2, 0xFF,
+ SD_CALCULATE_CRC7 | SD_CHECK_CRC16 | SD_NO_WAIT_BUSY_END|
+ SD_CHECK_CRC7 | SD_RSP_LEN_6);
+ rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_DATA_SOURCE, 0x01,
+ PINGPONG_BUFFER);
+ rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_TRANSFER, 0xFF,
+ SD_TM_NORMAL_READ | SD_TRANSFER_START);
+ rtsx_add_cmd(chip, CHECK_REG_CMD, REG_SD_TRANSFER, SD_TRANSFER_END,
+ SD_TRANSFER_END);
+
+ rtsx_add_cmd(chip, READ_REG_CMD, PPBUF_BASE2 + 196, 0xFF, 0);
+ rtsx_add_cmd(chip, READ_REG_CMD, PPBUF_BASE2 + 212, 0xFF, 0);
+ rtsx_add_cmd(chip, READ_REG_CMD, PPBUF_BASE2 + 213, 0xFF, 0);
+ rtsx_add_cmd(chip, READ_REG_CMD, PPBUF_BASE2 + 214, 0xFF, 0);
+ rtsx_add_cmd(chip, READ_REG_CMD, PPBUF_BASE2 + 215, 0xFF, 0);
+
+ retval = rtsx_send_cmd(chip, SD_CARD, 1000);
+ if (retval < 0) {
+ if (retval == -ETIMEDOUT) {
+ rtsx_clear_sd_error(chip);
+ sd_send_cmd_get_rsp(chip, SEND_STATUS, sd_card->sd_addr,
+ SD_RSP_TYPE_R1, NULL, 0);
+ }
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ ptr = rtsx_get_cmd_data(chip);
+ if (ptr[0] & SD_TRANSFER_ERR) {
+ sd_send_cmd_get_rsp(chip, SEND_STATUS, sd_card->sd_addr,
+ SD_RSP_TYPE_R1, NULL, 0);
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ if (CHK_MMC_SECTOR_MODE(sd_card)) {
+ sd_card->capacity = ((u32)ptr[5] << 24) | ((u32)ptr[4] << 16) |
+ ((u32)ptr[3] << 8) | ((u32)ptr[2]);
+ }
+
+ card_type_mask = 0x03;
+ card_type = ptr[1] & card_type_mask;
+ if (card_type) {
+ u8 rsp[5];
+
+ if (card_type & 0x04) {
+ if (switch_ddr)
+ SET_MMC_DDR52(sd_card);
+ else
+ SET_MMC_52M(sd_card);
+ } else if (card_type & 0x02) {
+ SET_MMC_52M(sd_card);
+ } else {
+ SET_MMC_26M(sd_card);
+ }
+
+ retval = sd_send_cmd_get_rsp(chip, SWITCH,
+ 0x03B90100, SD_RSP_TYPE_R1b, rsp, 5);
+ if ((retval != STATUS_SUCCESS) || (rsp[4] & MMC_SWITCH_ERR))
+ CLR_MMC_HS(sd_card);
+ }
+
+ sd_choose_proper_clock(chip);
+ retval = switch_clock(chip, sd_card->sd_clock);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ /* Test Bus Procedure */
+ retval = mmc_test_switch_bus(chip, MMC_8BIT_BUS);
+ if (retval == SWITCH_SUCCESS) {
+ SET_MMC_8BIT(sd_card);
+ chip->card_bus_width[chip->card2lun[SD_CARD]] = 8;
+#ifdef SUPPORT_SD_LOCK
+ sd_card->sd_lock_status &= ~SD_LOCK_1BIT_MODE;
+#endif
+ } else if (retval == SWITCH_FAIL) {
+ retval = mmc_test_switch_bus(chip, MMC_4BIT_BUS);
+ if (retval == SWITCH_SUCCESS) {
+ SET_MMC_4BIT(sd_card);
+ chip->card_bus_width[chip->card2lun[SD_CARD]] = 4;
+#ifdef SUPPORT_SD_LOCK
+ sd_card->sd_lock_status &= ~SD_LOCK_1BIT_MODE;
+#endif
+ } else if (retval == SWITCH_FAIL) {
+ CLR_MMC_8BIT(sd_card);
+ CLR_MMC_4BIT(sd_card);
+ } else {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+ } else {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ return STATUS_SUCCESS;
+}
+
+
+static int reset_mmc(struct rtsx_chip *chip)
+{
+ struct sd_info *sd_card = &(chip->sd_card);
+ int retval, i = 0, j = 0, k = 0;
+ bool switch_ddr = true;
+ u8 rsp[16];
+ u8 spec_ver = 0;
+ u32 temp;
+
+#ifdef SUPPORT_SD_LOCK
+ if (sd_card->sd_lock_status & SD_UNLOCK_POW_ON)
+ goto MMC_UNLOCK_ENTRY;
+#endif
+
+Switch_Fail:
+ retval = sd_prepare_reset(chip);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return retval;
+ }
+
+ SET_MMC(sd_card);
+
+RTY_MMC_RST:
+ retval = sd_send_cmd_get_rsp(chip, GO_IDLE_STATE, 0, SD_RSP_TYPE_R0,
+ NULL, 0);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ do {
+ if (detect_card_cd(chip, SD_CARD) != STATUS_SUCCESS) {
+ sd_set_err_code(chip, SD_NO_CARD);
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ retval = sd_send_cmd_get_rsp(chip, SEND_OP_COND,
+ (SUPPORT_VOLTAGE | 0x40000000),
+ SD_RSP_TYPE_R3, rsp, 5);
+ if (retval != STATUS_SUCCESS) {
+ if (sd_check_err_code(chip, SD_BUSY) ||
+ sd_check_err_code(chip, SD_TO_ERR)) {
+ k++;
+ if (k < 20) {
+ sd_clr_err_code(chip);
+ goto RTY_MMC_RST;
+ } else {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+ } else {
+ j++;
+ if (j < 100) {
+ sd_clr_err_code(chip);
+ goto RTY_MMC_RST;
+ } else {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+ }
+ }
+
+ wait_timeout(20);
+ i++;
+ } while (!(rsp[1] & 0x80) && (i < 255));
+
+ if (i == 255) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ if ((rsp[1] & 0x60) == 0x40)
+ SET_MMC_SECTOR_MODE(sd_card);
+ else
+ CLR_MMC_SECTOR_MODE(sd_card);
+
+ retval = sd_send_cmd_get_rsp(chip, ALL_SEND_CID, 0, SD_RSP_TYPE_R2,
+ NULL, 0);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ sd_card->sd_addr = 0x00100000;
+ retval = sd_send_cmd_get_rsp(chip, SET_RELATIVE_ADDR, sd_card->sd_addr,
+ SD_RSP_TYPE_R6, rsp, 5);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ retval = sd_check_csd(chip, 1);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ spec_ver = (sd_card->raw_csd[0] & 0x3C) >> 2;
+
+ retval = sd_select_card(chip, 1);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ retval = sd_send_cmd_get_rsp(chip, SET_BLOCKLEN, 0x200, SD_RSP_TYPE_R1,
+ NULL, 0);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+#ifdef SUPPORT_SD_LOCK
+MMC_UNLOCK_ENTRY:
+ retval = sd_update_lock_status(chip);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+#endif
+
+ retval = sd_set_clock_divider(chip, SD_CLK_DIVIDE_0);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ chip->card_bus_width[chip->card2lun[SD_CARD]] = 1;
+
+ if (!sd_card->mmc_dont_switch_bus) {
+ if (spec_ver == 4) {
+ /* MMC 4.x Cards */
+ retval = mmc_switch_timing_bus(chip, switch_ddr);
+ if (retval != STATUS_SUCCESS) {
+ retval = sd_init_power(chip);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+ sd_card->mmc_dont_switch_bus = 1;
+ rtsx_trace(chip);
+ goto Switch_Fail;
+ }
+ }
+
+ if (CHK_MMC_SECTOR_MODE(sd_card) && (sd_card->capacity == 0)) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ if (switch_ddr && CHK_MMC_DDR52(sd_card)) {
+ retval = sd_set_init_para(chip);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ retval = mmc_ddr_tuning(chip);
+ if (retval != STATUS_SUCCESS) {
+ retval = sd_init_power(chip);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ switch_ddr = false;
+ rtsx_trace(chip);
+ goto Switch_Fail;
+ }
+
+ retval = sd_wait_state_data_ready(chip, 0x08, 1, 1000);
+ if (retval == STATUS_SUCCESS) {
+ retval = sd_read_lba0(chip);
+ if (retval != STATUS_SUCCESS) {
+ retval = sd_init_power(chip);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ switch_ddr = false;
+ rtsx_trace(chip);
+ goto Switch_Fail;
+ }
+ }
+ }
+ }
+
+#ifdef SUPPORT_SD_LOCK
+ if (sd_card->sd_lock_status & SD_UNLOCK_POW_ON) {
+ retval = rtsx_write_register(chip, REG_SD_BLOCK_CNT_H, 0xFF,
+ 0x02);
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+ retval = rtsx_write_register(chip, REG_SD_BLOCK_CNT_L, 0xFF,
+ 0x00);
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+ }
+#endif
+
+ temp = rtsx_readl(chip, RTSX_BIPR);
+ if (temp & SD_WRITE_PROTECT)
+ chip->card_wp |= SD_CARD;
+
+ return STATUS_SUCCESS;
+}
+
+int reset_sd_card(struct rtsx_chip *chip)
+{
+ struct sd_info *sd_card = &(chip->sd_card);
+ int retval;
+
+ sd_init_reg_addr(chip);
+
+ memset(sd_card, 0, sizeof(struct sd_info));
+ chip->capacity[chip->card2lun[SD_CARD]] = 0;
+
+ retval = enable_card_clock(chip, SD_CARD);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ if (chip->ignore_sd && CHK_SDIO_EXIST(chip) &&
+ !CHK_SDIO_IGNORED(chip)) {
+ if (chip->asic_code) {
+ retval = sd_pull_ctl_enable(chip);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+ } else {
+ retval = rtsx_write_register(chip, FPGA_PULL_CTL,
+ FPGA_SD_PULL_CTL_BIT | 0x20, 0);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+ }
+ retval = card_share_mode(chip, SD_CARD);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ chip->sd_io = 1;
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ retval = sd_init_power(chip);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ if (chip->sd_ctl & RESET_MMC_FIRST) {
+ retval = reset_mmc(chip);
+ if (retval != STATUS_SUCCESS) {
+ if (sd_check_err_code(chip, SD_NO_CARD)) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ retval = reset_sd(chip);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+ }
+ } else {
+ retval = reset_sd(chip);
+ if (retval != STATUS_SUCCESS) {
+ if (sd_check_err_code(chip, SD_NO_CARD)) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ if (chip->sd_io) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ } else {
+ retval = reset_mmc(chip);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+ }
+ }
+ }
+
+ retval = sd_set_clock_divider(chip, SD_CLK_DIVIDE_0);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ retval = rtsx_write_register(chip, REG_SD_BYTE_CNT_L, 0xFF, 0);
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+ retval = rtsx_write_register(chip, REG_SD_BYTE_CNT_H, 0xFF, 2);
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+
+ chip->capacity[chip->card2lun[SD_CARD]] = sd_card->capacity;
+
+ retval = sd_set_init_para(chip);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ dev_dbg(rtsx_dev(chip), "sd_card->sd_type = 0x%x\n", sd_card->sd_type);
+
+ return STATUS_SUCCESS;
+}
+
+static int reset_mmc_only(struct rtsx_chip *chip)
+{
+ struct sd_info *sd_card = &(chip->sd_card);
+ int retval;
+
+ sd_card->sd_type = 0;
+ sd_card->seq_mode = 0;
+ sd_card->sd_data_buf_ready = 0;
+ sd_card->capacity = 0;
+ sd_card->sd_switch_fail = 0;
+
+#ifdef SUPPORT_SD_LOCK
+ sd_card->sd_lock_status = 0;
+ sd_card->sd_erase_status = 0;
+#endif
+
+ chip->capacity[chip->card2lun[SD_CARD]] = sd_card->capacity = 0;
+
+ retval = enable_card_clock(chip, SD_CARD);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ retval = sd_init_power(chip);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ retval = reset_mmc(chip);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ retval = sd_set_clock_divider(chip, SD_CLK_DIVIDE_0);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ retval = rtsx_write_register(chip, REG_SD_BYTE_CNT_L, 0xFF, 0);
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+ retval = rtsx_write_register(chip, REG_SD_BYTE_CNT_H, 0xFF, 2);
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+
+ chip->capacity[chip->card2lun[SD_CARD]] = sd_card->capacity;
+
+ retval = sd_set_init_para(chip);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ dev_dbg(rtsx_dev(chip), "In reset_mmc_only, sd_card->sd_type = 0x%x\n",
+ sd_card->sd_type);
+
+ return STATUS_SUCCESS;
+}
+
+#define WAIT_DATA_READY_RTY_CNT 255
+
+static int wait_data_buf_ready(struct rtsx_chip *chip)
+{
+ struct sd_info *sd_card = &(chip->sd_card);
+ int i, retval;
+
+ for (i = 0; i < WAIT_DATA_READY_RTY_CNT; i++) {
+ if (detect_card_cd(chip, SD_CARD) != STATUS_SUCCESS) {
+ sd_set_err_code(chip, SD_NO_CARD);
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ sd_card->sd_data_buf_ready = 0;
+
+ retval = sd_send_cmd_get_rsp(chip, SEND_STATUS,
+ sd_card->sd_addr, SD_RSP_TYPE_R1, NULL, 0);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ if (sd_card->sd_data_buf_ready) {
+ return sd_send_cmd_get_rsp(chip, SEND_STATUS,
+ sd_card->sd_addr, SD_RSP_TYPE_R1, NULL, 0);
+ }
+ }
+
+ sd_set_err_code(chip, SD_TO_ERR);
+
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+}
+
+void sd_stop_seq_mode(struct rtsx_chip *chip)
+{
+ struct sd_info *sd_card = &(chip->sd_card);
+ int retval;
+
+ if (sd_card->seq_mode) {
+ retval = sd_switch_clock(chip);
+ if (retval != STATUS_SUCCESS)
+ return;
+
+ retval = sd_send_cmd_get_rsp(chip, STOP_TRANSMISSION, 0,
+ SD_RSP_TYPE_R1b, NULL, 0);
+ if (retval != STATUS_SUCCESS)
+ sd_set_err_code(chip, SD_STS_ERR);
+
+ retval = sd_wait_state_data_ready(chip, 0x08, 1, 1000);
+ if (retval != STATUS_SUCCESS)
+ sd_set_err_code(chip, SD_STS_ERR);
+
+ sd_card->seq_mode = 0;
+
+ rtsx_write_register(chip, RBCTL, RB_FLUSH, RB_FLUSH);
+ }
+}
+
+static inline int sd_auto_tune_clock(struct rtsx_chip *chip)
+{
+ struct sd_info *sd_card = &(chip->sd_card);
+ int retval;
+
+ if (chip->asic_code) {
+ if (sd_card->sd_clock > 30)
+ sd_card->sd_clock -= 20;
+ } else {
+ switch (sd_card->sd_clock) {
+ case CLK_200:
+ sd_card->sd_clock = CLK_150;
+ break;
+
+ case CLK_150:
+ sd_card->sd_clock = CLK_120;
+ break;
+
+ case CLK_120:
+ sd_card->sd_clock = CLK_100;
+ break;
+
+ case CLK_100:
+ sd_card->sd_clock = CLK_80;
+ break;
+
+ case CLK_80:
+ sd_card->sd_clock = CLK_60;
+ break;
+
+ case CLK_60:
+ sd_card->sd_clock = CLK_50;
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ retval = sd_switch_clock(chip);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ return STATUS_SUCCESS;
+}
+
+int sd_rw(struct scsi_cmnd *srb, struct rtsx_chip *chip, u32 start_sector,
+ u16 sector_cnt)
+{
+ struct sd_info *sd_card = &(chip->sd_card);
+ u32 data_addr;
+ u8 cfg2;
+ int retval;
+
+ if (srb->sc_data_direction == DMA_FROM_DEVICE) {
+ dev_dbg(rtsx_dev(chip), "sd_rw: Read %d %s from 0x%x\n",
+ sector_cnt, (sector_cnt > 1) ? "sectors" : "sector",
+ start_sector);
+ } else {
+ dev_dbg(rtsx_dev(chip), "sd_rw: Write %d %s to 0x%x\n",
+ sector_cnt, (sector_cnt > 1) ? "sectors" : "sector",
+ start_sector);
+ }
+
+ sd_card->cleanup_counter = 0;
+
+ if (!(chip->card_ready & SD_CARD)) {
+ sd_card->seq_mode = 0;
+
+ retval = reset_sd_card(chip);
+ if (retval == STATUS_SUCCESS) {
+ chip->card_ready |= SD_CARD;
+ chip->card_fail &= ~SD_CARD;
+ } else {
+ chip->card_ready &= ~SD_CARD;
+ chip->card_fail |= SD_CARD;
+ chip->capacity[chip->card2lun[SD_CARD]] = 0;
+ chip->rw_need_retry = 1;
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+ }
+
+ if (!CHK_SD_HCXC(sd_card) && !CHK_MMC_SECTOR_MODE(sd_card))
+ data_addr = start_sector << 9;
+ else
+ data_addr = start_sector;
+
+ sd_clr_err_code(chip);
+
+ retval = sd_switch_clock(chip);
+ if (retval != STATUS_SUCCESS) {
+ sd_set_err_code(chip, SD_IO_ERR);
+ rtsx_trace(chip);
+ goto RW_FAIL;
+ }
+
+ if (sd_card->seq_mode &&
+ ((sd_card->pre_dir != srb->sc_data_direction) ||
+ ((sd_card->pre_sec_addr + sd_card->pre_sec_cnt) !=
+ start_sector))) {
+ if ((sd_card->pre_sec_cnt < 0x80)
+ && (sd_card->pre_dir == DMA_FROM_DEVICE)
+ && !CHK_SD30_SPEED(sd_card)
+ && !CHK_SD_HS(sd_card)
+ && !CHK_MMC_HS(sd_card)) {
+ sd_send_cmd_get_rsp(chip, SEND_STATUS, sd_card->sd_addr,
+ SD_RSP_TYPE_R1, NULL, 0);
+ }
+
+ retval = sd_send_cmd_get_rsp(chip, STOP_TRANSMISSION,
+ 0, SD_RSP_TYPE_R1b, NULL, 0);
+ if (retval != STATUS_SUCCESS) {
+ chip->rw_need_retry = 1;
+ sd_set_err_code(chip, SD_STS_ERR);
+ rtsx_trace(chip);
+ goto RW_FAIL;
+ }
+
+ sd_card->seq_mode = 0;
+
+ retval = rtsx_write_register(chip, RBCTL, RB_FLUSH, RB_FLUSH);
+ if (retval != STATUS_SUCCESS) {
+ sd_set_err_code(chip, SD_IO_ERR);
+ rtsx_trace(chip);
+ goto RW_FAIL;
+ }
+
+ if ((sd_card->pre_sec_cnt < 0x80)
+ && !CHK_SD30_SPEED(sd_card)
+ && !CHK_SD_HS(sd_card)
+ && !CHK_MMC_HS(sd_card)) {
+ sd_send_cmd_get_rsp(chip, SEND_STATUS, sd_card->sd_addr,
+ SD_RSP_TYPE_R1, NULL, 0);
+ }
+ }
+
+ rtsx_init_cmd(chip);
+
+ rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_BYTE_CNT_L, 0xFF, 0x00);
+ rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_BYTE_CNT_H, 0xFF, 0x02);
+ rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_BLOCK_CNT_L, 0xFF,
+ (u8)sector_cnt);
+ rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_BLOCK_CNT_H, 0xFF,
+ (u8)(sector_cnt >> 8));
+
+ rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_DATA_SOURCE, 0x01, RING_BUFFER);
+
+ if (CHK_MMC_8BIT(sd_card))
+ rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_CFG1,
+ 0x03, SD_BUS_WIDTH_8);
+ else if (CHK_MMC_4BIT(sd_card) || CHK_SD(sd_card))
+ rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_CFG1,
+ 0x03, SD_BUS_WIDTH_4);
+ else
+ rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_CFG1,
+ 0x03, SD_BUS_WIDTH_1);
+
+ if (sd_card->seq_mode) {
+ cfg2 = SD_NO_CALCULATE_CRC7 | SD_CHECK_CRC16|
+ SD_NO_WAIT_BUSY_END | SD_NO_CHECK_CRC7 |
+ SD_RSP_LEN_0;
+ rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_CFG2, 0xFF, cfg2);
+
+ trans_dma_enable(srb->sc_data_direction, chip, sector_cnt * 512,
+ DMA_512);
+
+ if (srb->sc_data_direction == DMA_FROM_DEVICE) {
+ rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_TRANSFER, 0xFF,
+ SD_TM_AUTO_READ_3 | SD_TRANSFER_START);
+ } else {
+ rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_TRANSFER, 0xFF,
+ SD_TM_AUTO_WRITE_3 | SD_TRANSFER_START);
+ }
+
+ rtsx_add_cmd(chip, CHECK_REG_CMD, REG_SD_TRANSFER,
+ SD_TRANSFER_END, SD_TRANSFER_END);
+
+ rtsx_send_cmd_no_wait(chip);
+ } else {
+ if (srb->sc_data_direction == DMA_FROM_DEVICE) {
+ dev_dbg(rtsx_dev(chip), "SD/MMC CMD %d\n",
+ READ_MULTIPLE_BLOCK);
+ rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_CMD0, 0xFF,
+ 0x40 | READ_MULTIPLE_BLOCK);
+ rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_CMD1, 0xFF,
+ (u8)(data_addr >> 24));
+ rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_CMD2, 0xFF,
+ (u8)(data_addr >> 16));
+ rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_CMD3, 0xFF,
+ (u8)(data_addr >> 8));
+ rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_CMD4, 0xFF,
+ (u8)data_addr);
+
+ cfg2 = SD_CALCULATE_CRC7 | SD_CHECK_CRC16 |
+ SD_NO_WAIT_BUSY_END | SD_CHECK_CRC7 |
+ SD_RSP_LEN_6;
+ rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_CFG2, 0xFF,
+ cfg2);
+
+ trans_dma_enable(srb->sc_data_direction, chip,
+ sector_cnt * 512, DMA_512);
+
+ rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_TRANSFER, 0xFF,
+ SD_TM_AUTO_READ_2 | SD_TRANSFER_START);
+ rtsx_add_cmd(chip, CHECK_REG_CMD, REG_SD_TRANSFER,
+ SD_TRANSFER_END, SD_TRANSFER_END);
+
+ rtsx_send_cmd_no_wait(chip);
+ } else {
+ retval = rtsx_send_cmd(chip, SD_CARD, 50);
+ if (retval < 0) {
+ rtsx_clear_sd_error(chip);
+
+ chip->rw_need_retry = 1;
+ sd_set_err_code(chip, SD_TO_ERR);
+ rtsx_trace(chip);
+ goto RW_FAIL;
+ }
+
+ retval = wait_data_buf_ready(chip);
+ if (retval != STATUS_SUCCESS) {
+ chip->rw_need_retry = 1;
+ sd_set_err_code(chip, SD_TO_ERR);
+ rtsx_trace(chip);
+ goto RW_FAIL;
+ }
+
+ retval = sd_send_cmd_get_rsp(chip, WRITE_MULTIPLE_BLOCK,
+ data_addr, SD_RSP_TYPE_R1, NULL, 0);
+ if (retval != STATUS_SUCCESS) {
+ chip->rw_need_retry = 1;
+ rtsx_trace(chip);
+ goto RW_FAIL;
+ }
+
+ rtsx_init_cmd(chip);
+
+ cfg2 = SD_NO_CALCULATE_CRC7 | SD_CHECK_CRC16 |
+ SD_NO_WAIT_BUSY_END |
+ SD_NO_CHECK_CRC7 | SD_RSP_LEN_0;
+ rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_CFG2, 0xFF,
+ cfg2);
+
+ trans_dma_enable(srb->sc_data_direction, chip,
+ sector_cnt * 512, DMA_512);
+
+ rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_TRANSFER, 0xFF,
+ SD_TM_AUTO_WRITE_3 | SD_TRANSFER_START);
+ rtsx_add_cmd(chip, CHECK_REG_CMD, REG_SD_TRANSFER,
+ SD_TRANSFER_END, SD_TRANSFER_END);
+
+ rtsx_send_cmd_no_wait(chip);
+ }
+
+ sd_card->seq_mode = 1;
+ }
+
+ retval = rtsx_transfer_data(chip, SD_CARD, scsi_sglist(srb),
+ scsi_bufflen(srb), scsi_sg_count(srb),
+ srb->sc_data_direction, chip->sd_timeout);
+ if (retval < 0) {
+ u8 stat = 0;
+ int err;
+
+ sd_card->seq_mode = 0;
+
+ if (retval == -ETIMEDOUT)
+ err = STATUS_TIMEDOUT;
+ else
+ err = STATUS_FAIL;
+
+ rtsx_read_register(chip, REG_SD_STAT1, &stat);
+ rtsx_clear_sd_error(chip);
+ if (detect_card_cd(chip, SD_CARD) != STATUS_SUCCESS) {
+ chip->rw_need_retry = 0;
+ dev_dbg(rtsx_dev(chip), "No card exist, exit sd_rw\n");
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ chip->rw_need_retry = 1;
+
+ retval = sd_send_cmd_get_rsp(chip, STOP_TRANSMISSION, 0,
+ SD_RSP_TYPE_R1b, NULL, 0);
+ if (retval != STATUS_SUCCESS) {
+ sd_set_err_code(chip, SD_STS_ERR);
+ rtsx_trace(chip);
+ goto RW_FAIL;
+ }
+
+ if (stat & (SD_CRC7_ERR | SD_CRC16_ERR | SD_CRC_WRITE_ERR)) {
+ dev_dbg(rtsx_dev(chip), "SD CRC error, tune clock!\n");
+ sd_set_err_code(chip, SD_CRC_ERR);
+ rtsx_trace(chip);
+ goto RW_FAIL;
+ }
+
+ if (err == STATUS_TIMEDOUT) {
+ sd_set_err_code(chip, SD_TO_ERR);
+ rtsx_trace(chip);
+ goto RW_FAIL;
+ }
+
+ rtsx_trace(chip);
+ return err;
+ }
+
+ sd_card->pre_sec_addr = start_sector;
+ sd_card->pre_sec_cnt = sector_cnt;
+ sd_card->pre_dir = srb->sc_data_direction;
+
+ return STATUS_SUCCESS;
+
+RW_FAIL:
+ sd_card->seq_mode = 0;
+
+ if (detect_card_cd(chip, SD_CARD) != STATUS_SUCCESS) {
+ chip->rw_need_retry = 0;
+ dev_dbg(rtsx_dev(chip), "No card exist, exit sd_rw\n");
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ if (sd_check_err_code(chip, SD_CRC_ERR)) {
+ if (CHK_MMC_4BIT(sd_card) || CHK_MMC_8BIT(sd_card)) {
+ sd_card->mmc_dont_switch_bus = 1;
+ reset_mmc_only(chip);
+ sd_card->mmc_dont_switch_bus = 0;
+ } else {
+ sd_card->need_retune = 1;
+ sd_auto_tune_clock(chip);
+ }
+ } else if (sd_check_err_code(chip, SD_TO_ERR | SD_STS_ERR)) {
+ retval = reset_sd_card(chip);
+ if (retval != STATUS_SUCCESS) {
+ chip->card_ready &= ~SD_CARD;
+ chip->card_fail |= SD_CARD;
+ chip->capacity[chip->card2lun[SD_CARD]] = 0;
+ }
+ }
+
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+}
+
+#ifdef SUPPORT_CPRM
+int soft_reset_sd_card(struct rtsx_chip *chip)
+{
+ return reset_sd(chip);
+}
+
+int ext_sd_send_cmd_get_rsp(struct rtsx_chip *chip, u8 cmd_idx,
+ u32 arg, u8 rsp_type, u8 *rsp, int rsp_len, bool special_check)
+{
+ int retval;
+ int timeout = 100;
+ u16 reg_addr;
+ u8 *ptr;
+ int stat_idx = 0;
+ int rty_cnt = 0;
+
+ dev_dbg(rtsx_dev(chip), "EXT SD/MMC CMD %d\n", cmd_idx);
+
+ if (rsp_type == SD_RSP_TYPE_R1b)
+ timeout = 3000;
+
+RTY_SEND_CMD:
+
+ rtsx_init_cmd(chip);
+
+ rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_CMD0, 0xFF, 0x40 | cmd_idx);
+ rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_CMD1, 0xFF, (u8)(arg >> 24));
+ rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_CMD2, 0xFF, (u8)(arg >> 16));
+ rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_CMD3, 0xFF, (u8)(arg >> 8));
+ rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_CMD4, 0xFF, (u8)arg);
+
+ rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_CFG2, 0xFF, rsp_type);
+ rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_DATA_SOURCE,
+ 0x01, PINGPONG_BUFFER);
+ rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_TRANSFER,
+ 0xFF, SD_TM_CMD_RSP | SD_TRANSFER_START);
+ rtsx_add_cmd(chip, CHECK_REG_CMD, REG_SD_TRANSFER, SD_TRANSFER_END,
+ SD_TRANSFER_END);
+
+ if (rsp_type == SD_RSP_TYPE_R2) {
+ for (reg_addr = PPBUF_BASE2; reg_addr < PPBUF_BASE2 + 16;
+ reg_addr++)
+ rtsx_add_cmd(chip, READ_REG_CMD, reg_addr, 0, 0);
+
+ stat_idx = 17;
+ } else if (rsp_type != SD_RSP_TYPE_R0) {
+ for (reg_addr = REG_SD_CMD0; reg_addr <= REG_SD_CMD4;
+ reg_addr++)
+ rtsx_add_cmd(chip, READ_REG_CMD, reg_addr, 0, 0);
+
+ stat_idx = 6;
+ }
+ rtsx_add_cmd(chip, READ_REG_CMD, REG_SD_CMD5, 0, 0);
+
+ rtsx_add_cmd(chip, READ_REG_CMD, REG_SD_STAT1, 0, 0);
+
+ retval = rtsx_send_cmd(chip, SD_CARD, timeout);
+ if (retval < 0) {
+ if (retval == -ETIMEDOUT) {
+ rtsx_clear_sd_error(chip);
+
+ if (rsp_type & SD_WAIT_BUSY_END) {
+ retval = sd_check_data0_status(chip);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return retval;
+ }
+ } else {
+ sd_set_err_code(chip, SD_TO_ERR);
+ }
+ }
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ if (rsp_type == SD_RSP_TYPE_R0)
+ return STATUS_SUCCESS;
+
+ ptr = rtsx_get_cmd_data(chip) + 1;
+
+ if ((ptr[0] & 0xC0) != 0) {
+ sd_set_err_code(chip, SD_STS_ERR);
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ if (!(rsp_type & SD_NO_CHECK_CRC7)) {
+ if (ptr[stat_idx] & SD_CRC7_ERR) {
+ if (cmd_idx == WRITE_MULTIPLE_BLOCK) {
+ sd_set_err_code(chip, SD_CRC_ERR);
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+ if (rty_cnt < SD_MAX_RETRY_COUNT) {
+ wait_timeout(20);
+ rty_cnt++;
+ goto RTY_SEND_CMD;
+ } else {
+ sd_set_err_code(chip, SD_CRC_ERR);
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+ }
+ }
+
+ if ((cmd_idx == SELECT_CARD) || (cmd_idx == APP_CMD) ||
+ (cmd_idx == SEND_STATUS) || (cmd_idx == STOP_TRANSMISSION)) {
+ if ((cmd_idx != STOP_TRANSMISSION) && !special_check) {
+ if (ptr[1] & 0x80) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+ }
+#ifdef SUPPORT_SD_LOCK
+ if (ptr[1] & 0x7D)
+#else
+ if (ptr[1] & 0x7F)
+#endif
+ {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+ if (ptr[2] & 0xF8) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ if (cmd_idx == SELECT_CARD) {
+ if (rsp_type == SD_RSP_TYPE_R2) {
+ if ((ptr[3] & 0x1E) != 0x04) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ } else if (rsp_type == SD_RSP_TYPE_R0) {
+ if ((ptr[3] & 0x1E) != 0x03) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+ }
+ }
+ }
+
+ if (rsp && rsp_len)
+ memcpy(rsp, ptr, rsp_len);
+
+ return STATUS_SUCCESS;
+}
+
+int ext_sd_get_rsp(struct rtsx_chip *chip, int len, u8 *rsp, u8 rsp_type)
+{
+ int retval, rsp_len;
+ u16 reg_addr;
+
+ if (rsp_type == SD_RSP_TYPE_R0)
+ return STATUS_SUCCESS;
+
+ rtsx_init_cmd(chip);
+
+ if (rsp_type == SD_RSP_TYPE_R2) {
+ for (reg_addr = PPBUF_BASE2; reg_addr < PPBUF_BASE2 + 16;
+ reg_addr++)
+ rtsx_add_cmd(chip, READ_REG_CMD, reg_addr, 0xFF, 0);
+
+ rsp_len = 17;
+ } else if (rsp_type != SD_RSP_TYPE_R0) {
+ for (reg_addr = REG_SD_CMD0; reg_addr <= REG_SD_CMD4;
+ reg_addr++)
+ rtsx_add_cmd(chip, READ_REG_CMD, reg_addr, 0xFF, 0);
+
+ rsp_len = 6;
+ }
+ rtsx_add_cmd(chip, READ_REG_CMD, REG_SD_CMD5, 0xFF, 0);
+
+ retval = rtsx_send_cmd(chip, SD_CARD, 100);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ if (rsp) {
+ int min_len = (rsp_len < len) ? rsp_len : len;
+
+ memcpy(rsp, rtsx_get_cmd_data(chip), min_len);
+
+ dev_dbg(rtsx_dev(chip), "min_len = %d\n", min_len);
+ dev_dbg(rtsx_dev(chip), "Response in cmd buf: 0x%x 0x%x 0x%x 0x%x\n",
+ rsp[0], rsp[1], rsp[2], rsp[3]);
+ }
+
+ return STATUS_SUCCESS;
+}
+
+int sd_pass_thru_mode(struct scsi_cmnd *srb, struct rtsx_chip *chip)
+{
+ struct sd_info *sd_card = &(chip->sd_card);
+ unsigned int lun = SCSI_LUN(srb);
+ int len;
+ u8 buf[18] = {
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x0E,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x53,
+ 0x44,
+ 0x20,
+ 0x43,
+ 0x61,
+ 0x72,
+ 0x64,
+ 0x00,
+ 0x00,
+ 0x00,
+ };
+
+ sd_card->pre_cmd_err = 0;
+
+ if (!(CHK_BIT(chip->lun_mc, lun))) {
+ SET_BIT(chip->lun_mc, lun);
+ set_sense_type(chip, lun, SENSE_TYPE_MEDIA_CHANGE);
+ rtsx_trace(chip);
+ return TRANSPORT_FAILED;
+ }
+
+ if ((0x53 != srb->cmnd[2]) || (0x44 != srb->cmnd[3]) ||
+ (0x20 != srb->cmnd[4]) || (0x43 != srb->cmnd[5]) ||
+ (0x61 != srb->cmnd[6]) || (0x72 != srb->cmnd[7]) ||
+ (0x64 != srb->cmnd[8])) {
+ set_sense_type(chip, lun, SENSE_TYPE_MEDIA_INVALID_CMD_FIELD);
+ rtsx_trace(chip);
+ return TRANSPORT_FAILED;
+ }
+
+ switch (srb->cmnd[1] & 0x0F) {
+ case 0:
+ sd_card->sd_pass_thru_en = 0;
+ break;
+
+ case 1:
+ sd_card->sd_pass_thru_en = 1;
+ break;
+
+ default:
+ set_sense_type(chip, lun, SENSE_TYPE_MEDIA_INVALID_CMD_FIELD);
+ rtsx_trace(chip);
+ return TRANSPORT_FAILED;
+ }
+
+ buf[5] = (1 == CHK_SD(sd_card)) ? 0x01 : 0x02;
+ if (chip->card_wp & SD_CARD)
+ buf[5] |= 0x80;
+
+ buf[6] = (u8)(sd_card->sd_addr >> 16);
+ buf[7] = (u8)(sd_card->sd_addr >> 24);
+
+ buf[15] = chip->max_lun;
+
+ len = min_t(int, 18, scsi_bufflen(srb));
+ rtsx_stor_set_xfer_buf(buf, len, srb);
+
+ return TRANSPORT_GOOD;
+}
+
+static inline int get_rsp_type(struct scsi_cmnd *srb, u8 *rsp_type,
+ int *rsp_len)
+{
+ if (!rsp_type || !rsp_len)
+ return STATUS_FAIL;
+
+ switch (srb->cmnd[10]) {
+ case 0x03:
+ *rsp_type = SD_RSP_TYPE_R0;
+ *rsp_len = 0;
+ break;
+
+ case 0x04:
+ *rsp_type = SD_RSP_TYPE_R1;
+ *rsp_len = 6;
+ break;
+
+ case 0x05:
+ *rsp_type = SD_RSP_TYPE_R1b;
+ *rsp_len = 6;
+ break;
+
+ case 0x06:
+ *rsp_type = SD_RSP_TYPE_R2;
+ *rsp_len = 17;
+ break;
+
+ case 0x07:
+ *rsp_type = SD_RSP_TYPE_R3;
+ *rsp_len = 6;
+ break;
+
+ default:
+ return STATUS_FAIL;
+ }
+
+ return STATUS_SUCCESS;
+}
+
+int sd_execute_no_data(struct scsi_cmnd *srb, struct rtsx_chip *chip)
+{
+ struct sd_info *sd_card = &(chip->sd_card);
+ unsigned int lun = SCSI_LUN(srb);
+ int retval, rsp_len;
+ u8 cmd_idx, rsp_type;
+ bool standby = false, acmd = false;
+ u32 arg;
+
+ if (!sd_card->sd_pass_thru_en) {
+ set_sense_type(chip, lun, SENSE_TYPE_MEDIA_INVALID_CMD_FIELD);
+ rtsx_trace(chip);
+ return TRANSPORT_FAILED;
+ }
+
+ retval = sd_switch_clock(chip);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return TRANSPORT_FAILED;
+ }
+
+ if (sd_card->pre_cmd_err) {
+ sd_card->pre_cmd_err = 0;
+ set_sense_type(chip, lun, SENSE_TYPE_MEDIA_CHANGE);
+ rtsx_trace(chip);
+ return TRANSPORT_FAILED;
+ }
+
+ cmd_idx = srb->cmnd[2] & 0x3F;
+ if (srb->cmnd[1] & 0x02)
+ standby = true;
+
+ if (srb->cmnd[1] & 0x01)
+ acmd = true;
+
+ arg = ((u32)srb->cmnd[3] << 24) | ((u32)srb->cmnd[4] << 16) |
+ ((u32)srb->cmnd[5] << 8) | srb->cmnd[6];
+
+ retval = get_rsp_type(srb, &rsp_type, &rsp_len);
+ if (retval != STATUS_SUCCESS) {
+ set_sense_type(chip, lun, SENSE_TYPE_MEDIA_INVALID_CMD_FIELD);
+ rtsx_trace(chip);
+ return TRANSPORT_FAILED;
+ }
+ sd_card->last_rsp_type = rsp_type;
+
+ retval = sd_switch_clock(chip);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return TRANSPORT_FAILED;
+ }
+
+#ifdef SUPPORT_SD_LOCK
+ if ((sd_card->sd_lock_status & SD_LOCK_1BIT_MODE) == 0) {
+ if (CHK_MMC_8BIT(sd_card)) {
+ retval = rtsx_write_register(chip, REG_SD_CFG1, 0x03,
+ SD_BUS_WIDTH_8);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return TRANSPORT_FAILED;
+ }
+
+ } else if (CHK_SD(sd_card) || CHK_MMC_4BIT(sd_card)) {
+ retval = rtsx_write_register(chip, REG_SD_CFG1, 0x03,
+ SD_BUS_WIDTH_4);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return TRANSPORT_FAILED;
+ }
+ }
+ }
+#else
+ retval = rtsx_write_register(chip, REG_SD_CFG1, 0x03, SD_BUS_WIDTH_4);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return TRANSPORT_FAILED;
+ }
+#endif
+
+ if (standby) {
+ retval = sd_select_card(chip, 0);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ goto SD_Execute_Cmd_Failed;
+ }
+ }
+
+ if (acmd) {
+ retval = ext_sd_send_cmd_get_rsp(chip, APP_CMD,
+ sd_card->sd_addr,
+ SD_RSP_TYPE_R1, NULL, 0, false);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ goto SD_Execute_Cmd_Failed;
+ }
+ }
+
+ retval = ext_sd_send_cmd_get_rsp(chip, cmd_idx, arg, rsp_type,
+ sd_card->rsp, rsp_len, false);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ goto SD_Execute_Cmd_Failed;
+ }
+
+ if (standby) {
+ retval = sd_select_card(chip, 1);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ goto SD_Execute_Cmd_Failed;
+ }
+ }
+
+#ifdef SUPPORT_SD_LOCK
+ retval = sd_update_lock_status(chip);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ goto SD_Execute_Cmd_Failed;
+ }
+#endif
+
+ scsi_set_resid(srb, 0);
+ return TRANSPORT_GOOD;
+
+SD_Execute_Cmd_Failed:
+ sd_card->pre_cmd_err = 1;
+ set_sense_type(chip, lun, SENSE_TYPE_NO_SENSE);
+ release_sd_card(chip);
+ do_reset_sd_card(chip);
+ if (!(chip->card_ready & SD_CARD))
+ set_sense_type(chip, lun, SENSE_TYPE_MEDIA_NOT_PRESENT);
+
+ rtsx_trace(chip);
+ return TRANSPORT_FAILED;
+}
+
+int sd_execute_read_data(struct scsi_cmnd *srb, struct rtsx_chip *chip)
+{
+ struct sd_info *sd_card = &(chip->sd_card);
+ unsigned int lun = SCSI_LUN(srb);
+ int retval, rsp_len, i;
+ bool read_err = false, cmd13_checkbit = false;
+ u8 cmd_idx, rsp_type, bus_width;
+ bool standby = false, send_cmd12 = false, acmd = false;
+ u32 data_len;
+
+ if (!sd_card->sd_pass_thru_en) {
+ set_sense_type(chip, lun, SENSE_TYPE_MEDIA_INVALID_CMD_FIELD);
+ rtsx_trace(chip);
+ return TRANSPORT_FAILED;
+ }
+
+ if (sd_card->pre_cmd_err) {
+ sd_card->pre_cmd_err = 0;
+ set_sense_type(chip, lun, SENSE_TYPE_MEDIA_CHANGE);
+ rtsx_trace(chip);
+ return TRANSPORT_FAILED;
+ }
+
+ retval = sd_switch_clock(chip);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return TRANSPORT_FAILED;
+ }
+
+ cmd_idx = srb->cmnd[2] & 0x3F;
+ if (srb->cmnd[1] & 0x04)
+ send_cmd12 = true;
+
+ if (srb->cmnd[1] & 0x02)
+ standby = true;
+
+ if (srb->cmnd[1] & 0x01)
+ acmd = true;
+
+ data_len = ((u32)srb->cmnd[7] << 16) | ((u32)srb->cmnd[8]
+ << 8) | srb->cmnd[9];
+
+ retval = get_rsp_type(srb, &rsp_type, &rsp_len);
+ if (retval != STATUS_SUCCESS) {
+ set_sense_type(chip, lun, SENSE_TYPE_MEDIA_INVALID_CMD_FIELD);
+ rtsx_trace(chip);
+ return TRANSPORT_FAILED;
+ }
+ sd_card->last_rsp_type = rsp_type;
+
+ retval = sd_switch_clock(chip);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return TRANSPORT_FAILED;
+ }
+
+#ifdef SUPPORT_SD_LOCK
+ if ((sd_card->sd_lock_status & SD_LOCK_1BIT_MODE) == 0) {
+ if (CHK_MMC_8BIT(sd_card))
+ bus_width = SD_BUS_WIDTH_8;
+ else if (CHK_SD(sd_card) || CHK_MMC_4BIT(sd_card))
+ bus_width = SD_BUS_WIDTH_4;
+ else
+ bus_width = SD_BUS_WIDTH_1;
+ } else {
+ bus_width = SD_BUS_WIDTH_4;
+ }
+ dev_dbg(rtsx_dev(chip), "bus_width = %d\n", bus_width);
+#else
+ bus_width = SD_BUS_WIDTH_4;
+#endif
+
+ if (data_len < 512) {
+ retval = ext_sd_send_cmd_get_rsp(chip, SET_BLOCKLEN, data_len,
+ SD_RSP_TYPE_R1, NULL, 0, false);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ goto SD_Execute_Read_Cmd_Failed;
+ }
+ }
+
+ if (standby) {
+ retval = sd_select_card(chip, 0);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ goto SD_Execute_Read_Cmd_Failed;
+ }
+ }
+
+ if (acmd) {
+ retval = ext_sd_send_cmd_get_rsp(chip, APP_CMD,
+ sd_card->sd_addr,
+ SD_RSP_TYPE_R1, NULL, 0, false);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ goto SD_Execute_Read_Cmd_Failed;
+ }
+ }
+
+ if (data_len <= 512) {
+ int min_len;
+ u8 *buf;
+ u16 byte_cnt, blk_cnt;
+ u8 cmd[5];
+
+ byte_cnt = ((u16)(srb->cmnd[8] & 0x03) << 8) | srb->cmnd[9];
+ blk_cnt = 1;
+
+ cmd[0] = 0x40 | cmd_idx;
+ cmd[1] = srb->cmnd[3];
+ cmd[2] = srb->cmnd[4];
+ cmd[3] = srb->cmnd[5];
+ cmd[4] = srb->cmnd[6];
+
+ buf = kmalloc(data_len, GFP_KERNEL);
+ if (buf == NULL) {
+ rtsx_trace(chip);
+ return TRANSPORT_ERROR;
+ }
+
+ retval = sd_read_data(chip, SD_TM_NORMAL_READ, cmd, 5, byte_cnt,
+ blk_cnt, bus_width, buf, data_len, 2000);
+ if (retval != STATUS_SUCCESS) {
+ read_err = true;
+ kfree(buf);
+ rtsx_clear_sd_error(chip);
+ rtsx_trace(chip);
+ goto SD_Execute_Read_Cmd_Failed;
+ }
+
+ min_len = min(data_len, scsi_bufflen(srb));
+ rtsx_stor_set_xfer_buf(buf, min_len, srb);
+
+ kfree(buf);
+ } else if (!(data_len & 0x1FF)) {
+ rtsx_init_cmd(chip);
+
+ trans_dma_enable(DMA_FROM_DEVICE, chip, data_len, DMA_512);
+
+ rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_BYTE_CNT_H, 0xFF,
+ 0x02);
+ rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_BYTE_CNT_L, 0xFF,
+ 0x00);
+ rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_BLOCK_CNT_H,
+ 0xFF, (srb->cmnd[7] & 0xFE) >> 1);
+ rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_BLOCK_CNT_L,
+ 0xFF, (u8)((data_len & 0x0001FE00) >> 9));
+
+ rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_CMD0, 0xFF,
+ 0x40 | cmd_idx);
+ rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_CMD1, 0xFF,
+ srb->cmnd[3]);
+ rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_CMD2, 0xFF,
+ srb->cmnd[4]);
+ rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_CMD3, 0xFF,
+ srb->cmnd[5]);
+ rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_CMD4, 0xFF,
+ srb->cmnd[6]);
+
+ rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_CFG1, 0x03, bus_width);
+ rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_CFG2, 0xFF, rsp_type);
+
+ rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_TRANSFER,
+ 0xFF, SD_TM_AUTO_READ_2 | SD_TRANSFER_START);
+ rtsx_add_cmd(chip, CHECK_REG_CMD, REG_SD_TRANSFER,
+ SD_TRANSFER_END, SD_TRANSFER_END);
+
+ rtsx_send_cmd_no_wait(chip);
+
+ retval = rtsx_transfer_data(chip, SD_CARD, scsi_sglist(srb),
+ scsi_bufflen(srb), scsi_sg_count(srb),
+ DMA_FROM_DEVICE, 10000);
+ if (retval < 0) {
+ read_err = true;
+ rtsx_clear_sd_error(chip);
+ rtsx_trace(chip);
+ goto SD_Execute_Read_Cmd_Failed;
+ }
+
+ } else {
+ rtsx_trace(chip);
+ goto SD_Execute_Read_Cmd_Failed;
+ }
+
+ retval = ext_sd_get_rsp(chip, rsp_len, sd_card->rsp, rsp_type);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ goto SD_Execute_Read_Cmd_Failed;
+ }
+
+ if (standby) {
+ retval = sd_select_card(chip, 1);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ goto SD_Execute_Read_Cmd_Failed;
+ }
+ }
+
+ if (send_cmd12) {
+ retval = ext_sd_send_cmd_get_rsp(chip, STOP_TRANSMISSION,
+ 0, SD_RSP_TYPE_R1b, NULL, 0, false);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ goto SD_Execute_Read_Cmd_Failed;
+ }
+ }
+
+ if (data_len < 512) {
+ retval = ext_sd_send_cmd_get_rsp(chip, SET_BLOCKLEN, 0x200,
+ SD_RSP_TYPE_R1, NULL, 0, false);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ goto SD_Execute_Read_Cmd_Failed;
+ }
+
+ retval = rtsx_write_register(chip, SD_BYTE_CNT_H, 0xFF, 0x02);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ goto SD_Execute_Read_Cmd_Failed;
+ }
+
+ retval = rtsx_write_register(chip, SD_BYTE_CNT_L, 0xFF, 0x00);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ goto SD_Execute_Read_Cmd_Failed;
+ }
+ }
+
+ if ((srb->cmnd[1] & 0x02) || (srb->cmnd[1] & 0x04))
+ cmd13_checkbit = true;
+
+ for (i = 0; i < 3; i++) {
+ retval = ext_sd_send_cmd_get_rsp(chip, SEND_STATUS,
+ sd_card->sd_addr,
+ SD_RSP_TYPE_R1, NULL, 0,
+ cmd13_checkbit);
+ if (retval == STATUS_SUCCESS)
+ break;
+ }
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ goto SD_Execute_Read_Cmd_Failed;
+ }
+
+ scsi_set_resid(srb, 0);
+ return TRANSPORT_GOOD;
+
+SD_Execute_Read_Cmd_Failed:
+ sd_card->pre_cmd_err = 1;
+ set_sense_type(chip, lun, SENSE_TYPE_NO_SENSE);
+ if (read_err)
+ set_sense_type(chip, lun, SENSE_TYPE_MEDIA_UNRECOVER_READ_ERR);
+
+ release_sd_card(chip);
+ do_reset_sd_card(chip);
+ if (!(chip->card_ready & SD_CARD))
+ set_sense_type(chip, lun, SENSE_TYPE_MEDIA_NOT_PRESENT);
+
+ rtsx_trace(chip);
+ return TRANSPORT_FAILED;
+}
+
+int sd_execute_write_data(struct scsi_cmnd *srb, struct rtsx_chip *chip)
+{
+ struct sd_info *sd_card = &(chip->sd_card);
+ unsigned int lun = SCSI_LUN(srb);
+ int retval, rsp_len, i;
+ bool write_err = false, cmd13_checkbit = false;
+ u8 cmd_idx, rsp_type;
+ bool standby = false, send_cmd12 = false, acmd = false;
+ u32 data_len, arg;
+#ifdef SUPPORT_SD_LOCK
+ int lock_cmd_fail = 0;
+ u8 sd_lock_state = 0;
+ u8 lock_cmd_type = 0;
+#endif
+
+ if (!sd_card->sd_pass_thru_en) {
+ set_sense_type(chip, lun, SENSE_TYPE_MEDIA_INVALID_CMD_FIELD);
+ rtsx_trace(chip);
+ return TRANSPORT_FAILED;
+ }
+
+ if (sd_card->pre_cmd_err) {
+ sd_card->pre_cmd_err = 0;
+ set_sense_type(chip, lun, SENSE_TYPE_MEDIA_CHANGE);
+ rtsx_trace(chip);
+ return TRANSPORT_FAILED;
+ }
+
+ retval = sd_switch_clock(chip);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return TRANSPORT_FAILED;
+ }
+
+ cmd_idx = srb->cmnd[2] & 0x3F;
+ if (srb->cmnd[1] & 0x04)
+ send_cmd12 = true;
+
+ if (srb->cmnd[1] & 0x02)
+ standby = true;
+
+ if (srb->cmnd[1] & 0x01)
+ acmd = true;
+
+ data_len = ((u32)srb->cmnd[7] << 16) | ((u32)srb->cmnd[8]
+ << 8) | srb->cmnd[9];
+ arg = ((u32)srb->cmnd[3] << 24) | ((u32)srb->cmnd[4] << 16) |
+ ((u32)srb->cmnd[5] << 8) | srb->cmnd[6];
+
+#ifdef SUPPORT_SD_LOCK
+ if (cmd_idx == LOCK_UNLOCK) {
+ sd_lock_state = sd_card->sd_lock_status;
+ sd_lock_state &= SD_LOCKED;
+ }
+#endif
+
+ retval = get_rsp_type(srb, &rsp_type, &rsp_len);
+ if (retval != STATUS_SUCCESS) {
+ set_sense_type(chip, lun, SENSE_TYPE_MEDIA_INVALID_CMD_FIELD);
+ rtsx_trace(chip);
+ return TRANSPORT_FAILED;
+ }
+ sd_card->last_rsp_type = rsp_type;
+
+ retval = sd_switch_clock(chip);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return TRANSPORT_FAILED;
+ }
+
+#ifdef SUPPORT_SD_LOCK
+ if ((sd_card->sd_lock_status & SD_LOCK_1BIT_MODE) == 0) {
+ if (CHK_MMC_8BIT(sd_card)) {
+ retval = rtsx_write_register(chip, REG_SD_CFG1, 0x03,
+ SD_BUS_WIDTH_8);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return TRANSPORT_FAILED;
+ }
+
+ } else if (CHK_SD(sd_card) || CHK_MMC_4BIT(sd_card)) {
+ retval = rtsx_write_register(chip, REG_SD_CFG1, 0x03,
+ SD_BUS_WIDTH_4);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return TRANSPORT_FAILED;
+ }
+ }
+ }
+#else
+ retval = rtsx_write_register(chip, REG_SD_CFG1, 0x03, SD_BUS_WIDTH_4);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return TRANSPORT_FAILED;
+ }
+#endif
+
+ if (data_len < 512) {
+ retval = ext_sd_send_cmd_get_rsp(chip, SET_BLOCKLEN, data_len,
+ SD_RSP_TYPE_R1, NULL, 0, false);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ goto SD_Execute_Write_Cmd_Failed;
+ }
+ }
+
+ if (standby) {
+ retval = sd_select_card(chip, 0);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ goto SD_Execute_Write_Cmd_Failed;
+ }
+ }
+
+ if (acmd) {
+ retval = ext_sd_send_cmd_get_rsp(chip, APP_CMD,
+ sd_card->sd_addr,
+ SD_RSP_TYPE_R1, NULL, 0, false);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ goto SD_Execute_Write_Cmd_Failed;
+ }
+ }
+
+ retval = ext_sd_send_cmd_get_rsp(chip, cmd_idx, arg, rsp_type,
+ sd_card->rsp, rsp_len, false);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ goto SD_Execute_Write_Cmd_Failed;
+ }
+
+ if (data_len <= 512) {
+ u16 i;
+ u8 *buf;
+
+ buf = kmalloc(data_len, GFP_KERNEL);
+ if (buf == NULL) {
+ rtsx_trace(chip);
+ return TRANSPORT_ERROR;
+ }
+
+ rtsx_stor_get_xfer_buf(buf, data_len, srb);
+
+#ifdef SUPPORT_SD_LOCK
+ if (cmd_idx == LOCK_UNLOCK)
+ lock_cmd_type = buf[0] & 0x0F;
+#endif
+
+ if (data_len > 256) {
+ rtsx_init_cmd(chip);
+ for (i = 0; i < 256; i++) {
+ rtsx_add_cmd(chip, WRITE_REG_CMD,
+ PPBUF_BASE2 + i, 0xFF, buf[i]);
+ }
+ retval = rtsx_send_cmd(chip, 0, 250);
+ if (retval != STATUS_SUCCESS) {
+ kfree(buf);
+ rtsx_trace(chip);
+ goto SD_Execute_Write_Cmd_Failed;
+ }
+
+ rtsx_init_cmd(chip);
+ for (i = 256; i < data_len; i++) {
+ rtsx_add_cmd(chip, WRITE_REG_CMD,
+ PPBUF_BASE2 + i, 0xFF, buf[i]);
+ }
+ retval = rtsx_send_cmd(chip, 0, 250);
+ if (retval != STATUS_SUCCESS) {
+ kfree(buf);
+ rtsx_trace(chip);
+ goto SD_Execute_Write_Cmd_Failed;
+ }
+ } else {
+ rtsx_init_cmd(chip);
+ for (i = 0; i < data_len; i++) {
+ rtsx_add_cmd(chip, WRITE_REG_CMD,
+ PPBUF_BASE2 + i, 0xFF, buf[i]);
+ }
+ retval = rtsx_send_cmd(chip, 0, 250);
+ if (retval != STATUS_SUCCESS) {
+ kfree(buf);
+ rtsx_trace(chip);
+ goto SD_Execute_Write_Cmd_Failed;
+ }
+ }
+
+ kfree(buf);
+
+ rtsx_init_cmd(chip);
+
+ rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_BYTE_CNT_H, 0xFF,
+ srb->cmnd[8] & 0x03);
+ rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_BYTE_CNT_L, 0xFF,
+ srb->cmnd[9]);
+ rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_BLOCK_CNT_H, 0xFF,
+ 0x00);
+ rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_BLOCK_CNT_L, 0xFF,
+ 0x01);
+ rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_DATA_SOURCE, 0x01,
+ PINGPONG_BUFFER);
+
+ rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_TRANSFER, 0xFF,
+ SD_TM_AUTO_WRITE_3 | SD_TRANSFER_START);
+ rtsx_add_cmd(chip, CHECK_REG_CMD, REG_SD_TRANSFER,
+ SD_TRANSFER_END, SD_TRANSFER_END);
+
+ retval = rtsx_send_cmd(chip, SD_CARD, 250);
+ } else if (!(data_len & 0x1FF)) {
+ rtsx_init_cmd(chip);
+
+ trans_dma_enable(DMA_TO_DEVICE, chip, data_len, DMA_512);
+
+ rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_BYTE_CNT_H, 0xFF,
+ 0x02);
+ rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_BYTE_CNT_L, 0xFF,
+ 0x00);
+ rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_BLOCK_CNT_H,
+ 0xFF, (srb->cmnd[7] & 0xFE) >> 1);
+ rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_BLOCK_CNT_L,
+ 0xFF, (u8)((data_len & 0x0001FE00) >> 9));
+
+ rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_TRANSFER, 0xFF,
+ SD_TM_AUTO_WRITE_3 | SD_TRANSFER_START);
+ rtsx_add_cmd(chip, CHECK_REG_CMD, REG_SD_TRANSFER,
+ SD_TRANSFER_END, SD_TRANSFER_END);
+
+ rtsx_send_cmd_no_wait(chip);
+
+ retval = rtsx_transfer_data(chip, SD_CARD, scsi_sglist(srb),
+ scsi_bufflen(srb), scsi_sg_count(srb),
+ DMA_TO_DEVICE, 10000);
+
+ } else {
+ rtsx_trace(chip);
+ goto SD_Execute_Write_Cmd_Failed;
+ }
+
+ if (retval < 0) {
+ write_err = true;
+ rtsx_clear_sd_error(chip);
+ rtsx_trace(chip);
+ goto SD_Execute_Write_Cmd_Failed;
+ }
+
+#ifdef SUPPORT_SD_LOCK
+ if (cmd_idx == LOCK_UNLOCK) {
+ if (lock_cmd_type == SD_ERASE) {
+ sd_card->sd_erase_status = SD_UNDER_ERASING;
+ scsi_set_resid(srb, 0);
+ return TRANSPORT_GOOD;
+ }
+
+ rtsx_init_cmd(chip);
+ rtsx_add_cmd(chip, CHECK_REG_CMD, 0xFD30, 0x02, 0x02);
+
+ rtsx_send_cmd(chip, SD_CARD, 250);
+
+ retval = sd_update_lock_status(chip);
+ if (retval != STATUS_SUCCESS) {
+ dev_dbg(rtsx_dev(chip), "Lock command fail!\n");
+ lock_cmd_fail = 1;
+ }
+ }
+#endif /* SUPPORT_SD_LOCK */
+
+ if (standby) {
+ retval = sd_select_card(chip, 1);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ goto SD_Execute_Write_Cmd_Failed;
+ }
+ }
+
+ if (send_cmd12) {
+ retval = ext_sd_send_cmd_get_rsp(chip, STOP_TRANSMISSION,
+ 0, SD_RSP_TYPE_R1b, NULL, 0, false);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ goto SD_Execute_Write_Cmd_Failed;
+ }
+ }
+
+ if (data_len < 512) {
+ retval = ext_sd_send_cmd_get_rsp(chip, SET_BLOCKLEN, 0x200,
+ SD_RSP_TYPE_R1, NULL, 0, false);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ goto SD_Execute_Write_Cmd_Failed;
+ }
+
+ retval = rtsx_write_register(chip, SD_BYTE_CNT_H, 0xFF, 0x02);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ goto SD_Execute_Write_Cmd_Failed;
+ }
+
+ rtsx_write_register(chip, SD_BYTE_CNT_L, 0xFF, 0x00);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ goto SD_Execute_Write_Cmd_Failed;
+ }
+ }
+
+ if ((srb->cmnd[1] & 0x02) || (srb->cmnd[1] & 0x04))
+ cmd13_checkbit = true;
+
+ for (i = 0; i < 3; i++) {
+ retval = ext_sd_send_cmd_get_rsp(chip, SEND_STATUS,
+ sd_card->sd_addr,
+ SD_RSP_TYPE_R1, NULL, 0,
+ cmd13_checkbit);
+ if (retval == STATUS_SUCCESS)
+ break;
+ }
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ goto SD_Execute_Write_Cmd_Failed;
+ }
+
+#ifdef SUPPORT_SD_LOCK
+ if (cmd_idx == LOCK_UNLOCK) {
+ if (!lock_cmd_fail) {
+ dev_dbg(rtsx_dev(chip), "lock_cmd_type = 0x%x\n",
+ lock_cmd_type);
+ if (lock_cmd_type & SD_CLR_PWD)
+ sd_card->sd_lock_status &= ~SD_PWD_EXIST;
+
+ if (lock_cmd_type & SD_SET_PWD)
+ sd_card->sd_lock_status |= SD_PWD_EXIST;
+ }
+
+ dev_dbg(rtsx_dev(chip), "sd_lock_state = 0x%x, sd_card->sd_lock_status = 0x%x\n",
+ sd_lock_state, sd_card->sd_lock_status);
+ if (sd_lock_state ^ (sd_card->sd_lock_status & SD_LOCKED)) {
+ sd_card->sd_lock_notify = 1;
+ if (sd_lock_state) {
+ if (sd_card->sd_lock_status & SD_LOCK_1BIT_MODE) {
+ sd_card->sd_lock_status |= (
+ SD_UNLOCK_POW_ON | SD_SDR_RST);
+ if (CHK_SD(sd_card)) {
+ retval = reset_sd(chip);
+ if (retval != STATUS_SUCCESS) {
+ sd_card->sd_lock_status &= ~(SD_UNLOCK_POW_ON | SD_SDR_RST);
+ rtsx_trace(chip);
+ goto SD_Execute_Write_Cmd_Failed;
+ }
+ }
+
+ sd_card->sd_lock_status &= ~(SD_UNLOCK_POW_ON | SD_SDR_RST);
+ }
+ }
+ }
+ }
+
+ if (lock_cmd_fail) {
+ scsi_set_resid(srb, 0);
+ set_sense_type(chip, lun, SENSE_TYPE_NO_SENSE);
+ rtsx_trace(chip);
+ return TRANSPORT_FAILED;
+ }
+#endif /* SUPPORT_SD_LOCK */
+
+ scsi_set_resid(srb, 0);
+ return TRANSPORT_GOOD;
+
+SD_Execute_Write_Cmd_Failed:
+ sd_card->pre_cmd_err = 1;
+ set_sense_type(chip, lun, SENSE_TYPE_NO_SENSE);
+ if (write_err)
+ set_sense_type(chip, lun, SENSE_TYPE_MEDIA_WRITE_ERR);
+
+ release_sd_card(chip);
+ do_reset_sd_card(chip);
+ if (!(chip->card_ready & SD_CARD))
+ set_sense_type(chip, lun, SENSE_TYPE_MEDIA_NOT_PRESENT);
+
+ rtsx_trace(chip);
+ return TRANSPORT_FAILED;
+}
+
+int sd_get_cmd_rsp(struct scsi_cmnd *srb, struct rtsx_chip *chip)
+{
+ struct sd_info *sd_card = &(chip->sd_card);
+ unsigned int lun = SCSI_LUN(srb);
+ int count;
+ u16 data_len;
+
+ if (!sd_card->sd_pass_thru_en) {
+ set_sense_type(chip, lun, SENSE_TYPE_MEDIA_INVALID_CMD_FIELD);
+ rtsx_trace(chip);
+ return TRANSPORT_FAILED;
+ }
+
+ if (sd_card->pre_cmd_err) {
+ sd_card->pre_cmd_err = 0;
+ set_sense_type(chip, lun, SENSE_TYPE_MEDIA_CHANGE);
+ rtsx_trace(chip);
+ return TRANSPORT_FAILED;
+ }
+
+ data_len = ((u16)srb->cmnd[7] << 8) | srb->cmnd[8];
+
+ if (sd_card->last_rsp_type == SD_RSP_TYPE_R0) {
+ set_sense_type(chip, lun, SENSE_TYPE_MEDIA_INVALID_CMD_FIELD);
+ rtsx_trace(chip);
+ return TRANSPORT_FAILED;
+ } else if (sd_card->last_rsp_type == SD_RSP_TYPE_R2) {
+ count = (data_len < 17) ? data_len : 17;
+ } else {
+ count = (data_len < 6) ? data_len : 6;
+ }
+ rtsx_stor_set_xfer_buf(sd_card->rsp, count, srb);
+
+ dev_dbg(rtsx_dev(chip), "Response length: %d\n", data_len);
+ dev_dbg(rtsx_dev(chip), "Response: 0x%x 0x%x 0x%x 0x%x\n",
+ sd_card->rsp[0], sd_card->rsp[1],
+ sd_card->rsp[2], sd_card->rsp[3]);
+
+ scsi_set_resid(srb, 0);
+ return TRANSPORT_GOOD;
+}
+
+int sd_hw_rst(struct scsi_cmnd *srb, struct rtsx_chip *chip)
+{
+ struct sd_info *sd_card = &(chip->sd_card);
+ unsigned int lun = SCSI_LUN(srb);
+ int retval;
+
+ if (!sd_card->sd_pass_thru_en) {
+ set_sense_type(chip, lun, SENSE_TYPE_MEDIA_INVALID_CMD_FIELD);
+ rtsx_trace(chip);
+ return TRANSPORT_FAILED;
+ }
+
+ if (sd_card->pre_cmd_err) {
+ sd_card->pre_cmd_err = 0;
+ set_sense_type(chip, lun, SENSE_TYPE_MEDIA_CHANGE);
+ rtsx_trace(chip);
+ return TRANSPORT_FAILED;
+ }
+
+ if ((0x53 != srb->cmnd[2]) || (0x44 != srb->cmnd[3]) ||
+ (0x20 != srb->cmnd[4]) || (0x43 != srb->cmnd[5]) ||
+ (0x61 != srb->cmnd[6]) || (0x72 != srb->cmnd[7]) ||
+ (0x64 != srb->cmnd[8])) {
+ set_sense_type(chip, lun, SENSE_TYPE_MEDIA_INVALID_CMD_FIELD);
+ rtsx_trace(chip);
+ return TRANSPORT_FAILED;
+ }
+
+ switch (srb->cmnd[1] & 0x0F) {
+ case 0:
+#ifdef SUPPORT_SD_LOCK
+ if (0x64 == srb->cmnd[9])
+ sd_card->sd_lock_status |= SD_SDR_RST;
+#endif
+ retval = reset_sd_card(chip);
+ if (retval != STATUS_SUCCESS) {
+#ifdef SUPPORT_SD_LOCK
+ sd_card->sd_lock_status &= ~SD_SDR_RST;
+#endif
+ set_sense_type(chip, lun, SENSE_TYPE_MEDIA_NOT_PRESENT);
+ sd_card->pre_cmd_err = 1;
+ rtsx_trace(chip);
+ return TRANSPORT_FAILED;
+ }
+#ifdef SUPPORT_SD_LOCK
+ sd_card->sd_lock_status &= ~SD_SDR_RST;
+#endif
+ break;
+
+ case 1:
+ retval = soft_reset_sd_card(chip);
+ if (retval != STATUS_SUCCESS) {
+ set_sense_type(chip, lun, SENSE_TYPE_MEDIA_NOT_PRESENT);
+ sd_card->pre_cmd_err = 1;
+ rtsx_trace(chip);
+ return TRANSPORT_FAILED;
+ }
+ break;
+
+ default:
+ set_sense_type(chip, lun, SENSE_TYPE_MEDIA_INVALID_CMD_FIELD);
+ rtsx_trace(chip);
+ return TRANSPORT_FAILED;
+ }
+
+ scsi_set_resid(srb, 0);
+ return TRANSPORT_GOOD;
+}
+#endif
+
+void sd_cleanup_work(struct rtsx_chip *chip)
+{
+ struct sd_info *sd_card = &(chip->sd_card);
+
+ if (sd_card->seq_mode) {
+ dev_dbg(rtsx_dev(chip), "SD: stop transmission\n");
+ sd_stop_seq_mode(chip);
+ sd_card->cleanup_counter = 0;
+ }
+}
+
+int sd_power_off_card3v3(struct rtsx_chip *chip)
+{
+ int retval;
+
+ retval = disable_card_clock(chip, SD_CARD);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ retval = rtsx_write_register(chip, CARD_OE, SD_OUTPUT_EN, 0);
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+
+ if (!chip->ft2_fast_mode) {
+ retval = card_power_off(chip, SD_CARD);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ wait_timeout(50);
+ }
+
+ if (chip->asic_code) {
+ retval = sd_pull_ctl_disable(chip);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+ } else {
+ retval = rtsx_write_register(chip, FPGA_PULL_CTL,
+ FPGA_SD_PULL_CTL_BIT | 0x20,
+ FPGA_SD_PULL_CTL_BIT);
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+ }
+
+ return STATUS_SUCCESS;
+}
+
+int release_sd_card(struct rtsx_chip *chip)
+{
+ struct sd_info *sd_card = &(chip->sd_card);
+ int retval;
+
+ chip->card_ready &= ~SD_CARD;
+ chip->card_fail &= ~SD_CARD;
+ chip->card_wp &= ~SD_CARD;
+
+ chip->sd_io = 0;
+ chip->sd_int = 0;
+
+#ifdef SUPPORT_SD_LOCK
+ sd_card->sd_lock_status = 0;
+ sd_card->sd_erase_status = 0;
+#endif
+
+ memset(sd_card->raw_csd, 0, 16);
+ memset(sd_card->raw_scr, 0, 8);
+
+ retval = sd_power_off_card3v3(chip);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ return STATUS_SUCCESS;
+}
diff --git a/drivers/staging/rts5208/sd.h b/drivers/staging/rts5208/sd.h
new file mode 100644
index 000000000..60b79280f
--- /dev/null
+++ b/drivers/staging/rts5208/sd.h
@@ -0,0 +1,301 @@
+/* Driver for Realtek PCI-Express card reader
+ * Header file
+ *
+ * Copyright(c) 2009-2013 Realtek Semiconductor Corp. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, 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, see <http://www.gnu.org/licenses/>.
+ *
+ * Author:
+ * Wei WANG (wei_wang@realsil.com.cn)
+ * Micky Ching (micky_ching@realsil.com.cn)
+ */
+
+#ifndef __REALTEK_RTSX_SD_H
+#define __REALTEK_RTSX_SD_H
+
+#include "rtsx_chip.h"
+
+#define SUPPORT_VOLTAGE 0x003C0000
+
+/* Error Code */
+#define SD_NO_ERROR 0x0
+#define SD_CRC_ERR 0x80
+#define SD_TO_ERR 0x40
+#define SD_NO_CARD 0x20
+#define SD_BUSY 0x10
+#define SD_STS_ERR 0x08
+#define SD_RSP_TIMEOUT 0x04
+#define SD_IO_ERR 0x02
+
+/* Return code for MMC switch bus */
+#define SWITCH_SUCCESS 0
+#define SWITCH_ERR 1
+#define SWITCH_FAIL 2
+
+/* MMC/SD Command Index */
+/* Basic command (class 0) */
+#define GO_IDLE_STATE 0
+#define SEND_OP_COND 1
+#define ALL_SEND_CID 2
+#define SET_RELATIVE_ADDR 3
+#define SEND_RELATIVE_ADDR 3
+#define SET_DSR 4
+#define IO_SEND_OP_COND 5
+#define SWITCH 6
+#define SELECT_CARD 7
+#define DESELECT_CARD 7
+/* CMD8 is "SEND_EXT_CSD" for MMC4.x Spec
+ * while is "SEND_IF_COND" for SD 2.0
+ */
+#define SEND_EXT_CSD 8
+#define SEND_IF_COND 8
+
+#define SEND_CSD 9
+#define SEND_CID 10
+#define VOLTAGE_SWITCH 11
+#define READ_DAT_UTIL_STOP 11
+#define STOP_TRANSMISSION 12
+#define SEND_STATUS 13
+#define GO_INACTIVE_STATE 15
+
+#define SET_BLOCKLEN 16
+#define READ_SINGLE_BLOCK 17
+#define READ_MULTIPLE_BLOCK 18
+#define SEND_TUNING_PATTERN 19
+
+#define BUSTEST_R 14
+#define BUSTEST_W 19
+
+#define WRITE_BLOCK 24
+#define WRITE_MULTIPLE_BLOCK 25
+#define PROGRAM_CSD 27
+
+#define ERASE_WR_BLK_START 32
+#define ERASE_WR_BLK_END 33
+#define ERASE_CMD 38
+
+#define LOCK_UNLOCK 42
+#define IO_RW_DIRECT 52
+
+#define APP_CMD 55
+#define GEN_CMD 56
+
+#define SET_BUS_WIDTH 6
+#define SD_STATUS 13
+#define SEND_NUM_WR_BLOCKS 22
+#define SET_WR_BLK_ERASE_COUNT 23
+#define SD_APP_OP_COND 41
+#define SET_CLR_CARD_DETECT 42
+#define SEND_SCR 51
+
+#define SD_READ_COMPLETE 0x00
+#define SD_READ_TO 0x01
+#define SD_READ_ADVENCE 0x02
+
+#define SD_CHECK_MODE 0x00
+#define SD_SWITCH_MODE 0x80
+#define SD_FUNC_GROUP_1 0x01
+#define SD_FUNC_GROUP_2 0x02
+#define SD_FUNC_GROUP_3 0x03
+#define SD_FUNC_GROUP_4 0x04
+#define SD_CHECK_SPEC_V1_1 0xFF
+
+#define NO_ARGUMENT 0x00
+#define CHECK_PATTERN 0x000000AA
+#define VOLTAGE_SUPPLY_RANGE 0x00000100
+#define SUPPORT_HIGH_AND_EXTENDED_CAPACITY 0x40000000
+#define SUPPORT_MAX_POWER_PERMANCE 0x10000000
+#define SUPPORT_1V8 0x01000000
+
+#define SWTICH_NO_ERR 0x00
+#define CARD_NOT_EXIST 0x01
+#define SPEC_NOT_SUPPORT 0x02
+#define CHECK_MODE_ERR 0x03
+#define CHECK_NOT_READY 0x04
+#define SWITCH_CRC_ERR 0x05
+#define SWITCH_MODE_ERR 0x06
+#define SWITCH_PASS 0x07
+
+#ifdef SUPPORT_SD_LOCK
+#define SD_ERASE 0x08
+#define SD_LOCK 0x04
+#define SD_UNLOCK 0x00
+#define SD_CLR_PWD 0x02
+#define SD_SET_PWD 0x01
+
+#define SD_PWD_LEN 0x10
+
+#define SD_LOCKED 0x80
+#define SD_LOCK_1BIT_MODE 0x40
+#define SD_PWD_EXIST 0x20
+#define SD_UNLOCK_POW_ON 0x01
+#define SD_SDR_RST 0x02
+
+#define SD_NOT_ERASE 0x00
+#define SD_UNDER_ERASING 0x01
+#define SD_COMPLETE_ERASE 0x02
+
+#define SD_RW_FORBIDDEN 0x0F
+
+#endif
+
+#define HS_SUPPORT 0x01
+#define SDR50_SUPPORT 0x02
+#define SDR104_SUPPORT 0x03
+#define DDR50_SUPPORT 0x04
+
+#define HS_SUPPORT_MASK 0x02
+#define SDR50_SUPPORT_MASK 0x04
+#define SDR104_SUPPORT_MASK 0x08
+#define DDR50_SUPPORT_MASK 0x10
+
+#define HS_QUERY_SWITCH_OK 0x01
+#define SDR50_QUERY_SWITCH_OK 0x02
+#define SDR104_QUERY_SWITCH_OK 0x03
+#define DDR50_QUERY_SWITCH_OK 0x04
+
+#define HS_SWITCH_BUSY 0x02
+#define SDR50_SWITCH_BUSY 0x04
+#define SDR104_SWITCH_BUSY 0x08
+#define DDR50_SWITCH_BUSY 0x10
+
+#define FUNCTION_GROUP1_SUPPORT_OFFSET 0x0D
+#define FUNCTION_GROUP1_QUERY_SWITCH_OFFSET 0x10
+#define FUNCTION_GROUP1_CHECK_BUSY_OFFSET 0x1D
+
+#define DRIVING_TYPE_A 0x01
+#define DRIVING_TYPE_B 0x00
+#define DRIVING_TYPE_C 0x02
+#define DRIVING_TYPE_D 0x03
+
+#define DRIVING_TYPE_A_MASK 0x02
+#define DRIVING_TYPE_B_MASK 0x01
+#define DRIVING_TYPE_C_MASK 0x04
+#define DRIVING_TYPE_D_MASK 0x08
+
+#define TYPE_A_QUERY_SWITCH_OK 0x01
+#define TYPE_B_QUERY_SWITCH_OK 0x00
+#define TYPE_C_QUERY_SWITCH_OK 0x02
+#define TYPE_D_QUERY_SWITCH_OK 0x03
+
+#define TYPE_A_SWITCH_BUSY 0x02
+#define TYPE_B_SWITCH_BUSY 0x01
+#define TYPE_C_SWITCH_BUSY 0x04
+#define TYPE_D_SWITCH_BUSY 0x08
+
+#define FUNCTION_GROUP3_SUPPORT_OFFSET 0x09
+#define FUNCTION_GROUP3_QUERY_SWITCH_OFFSET 0x0F
+#define FUNCTION_GROUP3_CHECK_BUSY_OFFSET 0x19
+
+#define CURRENT_LIMIT_200 0x00
+#define CURRENT_LIMIT_400 0x01
+#define CURRENT_LIMIT_600 0x02
+#define CURRENT_LIMIT_800 0x03
+
+#define CURRENT_LIMIT_200_MASK 0x01
+#define CURRENT_LIMIT_400_MASK 0x02
+#define CURRENT_LIMIT_600_MASK 0x04
+#define CURRENT_LIMIT_800_MASK 0x08
+
+#define CURRENT_LIMIT_200_QUERY_SWITCH_OK 0x00
+#define CURRENT_LIMIT_400_QUERY_SWITCH_OK 0x01
+#define CURRENT_LIMIT_600_QUERY_SWITCH_OK 0x02
+#define CURRENT_LIMIT_800_QUERY_SWITCH_OK 0x03
+
+#define CURRENT_LIMIT_200_SWITCH_BUSY 0x01
+#define CURRENT_LIMIT_400_SWITCH_BUSY 0x02
+#define CURRENT_LIMIT_600_SWITCH_BUSY 0x04
+#define CURRENT_LIMIT_800_SWITCH_BUSY 0x08
+
+#define FUNCTION_GROUP4_SUPPORT_OFFSET 0x07
+#define FUNCTION_GROUP4_QUERY_SWITCH_OFFSET 0x0F
+#define FUNCTION_GROUP4_CHECK_BUSY_OFFSET 0x17
+
+#define DATA_STRUCTURE_VER_OFFSET 0x11
+
+#define MAX_PHASE 31
+
+#define MMC_8BIT_BUS 0x0010
+#define MMC_4BIT_BUS 0x0020
+
+#define MMC_SWITCH_ERR 0x80
+
+#define SD_IO_3V3 0
+#define SD_IO_1V8 1
+
+#define TUNE_TX 0x00
+#define TUNE_RX 0x01
+
+#define CHANGE_TX 0x00
+#define CHANGE_RX 0x01
+
+#define DCM_HIGH_FREQUENCY_MODE 0x00
+#define DCM_LOW_FREQUENCY_MODE 0x01
+
+#define DCM_HIGH_FREQUENCY_MODE_SET 0x0C
+#define DCM_Low_FREQUENCY_MODE_SET 0x00
+
+#define MULTIPLY_BY_1 0x00
+#define MULTIPLY_BY_2 0x01
+#define MULTIPLY_BY_3 0x02
+#define MULTIPLY_BY_4 0x03
+#define MULTIPLY_BY_5 0x04
+#define MULTIPLY_BY_6 0x05
+#define MULTIPLY_BY_7 0x06
+#define MULTIPLY_BY_8 0x07
+#define MULTIPLY_BY_9 0x08
+#define MULTIPLY_BY_10 0x09
+
+#define DIVIDE_BY_2 0x01
+#define DIVIDE_BY_3 0x02
+#define DIVIDE_BY_4 0x03
+#define DIVIDE_BY_5 0x04
+#define DIVIDE_BY_6 0x05
+#define DIVIDE_BY_7 0x06
+#define DIVIDE_BY_8 0x07
+#define DIVIDE_BY_9 0x08
+#define DIVIDE_BY_10 0x09
+
+struct timing_phase_path {
+ int start;
+ int end;
+ int mid;
+ int len;
+};
+
+int sd_select_card(struct rtsx_chip *chip, int select);
+int sd_pull_ctl_enable(struct rtsx_chip *chip);
+int reset_sd_card(struct rtsx_chip *chip);
+int sd_switch_clock(struct rtsx_chip *chip);
+void sd_stop_seq_mode(struct rtsx_chip *chip);
+int sd_rw(struct scsi_cmnd *srb, struct rtsx_chip *chip,
+ u32 start_sector, u16 sector_cnt);
+void sd_cleanup_work(struct rtsx_chip *chip);
+int sd_power_off_card3v3(struct rtsx_chip *chip);
+int release_sd_card(struct rtsx_chip *chip);
+#ifdef SUPPORT_CPRM
+int soft_reset_sd_card(struct rtsx_chip *chip);
+int ext_sd_send_cmd_get_rsp(struct rtsx_chip *chip, u8 cmd_idx,
+ u32 arg, u8 rsp_type, u8 *rsp, int rsp_len, bool special_check);
+int ext_sd_get_rsp(struct rtsx_chip *chip, int len, u8 *rsp, u8 rsp_type);
+
+int sd_pass_thru_mode(struct scsi_cmnd *srb, struct rtsx_chip *chip);
+int sd_execute_no_data(struct scsi_cmnd *srb, struct rtsx_chip *chip);
+int sd_execute_read_data(struct scsi_cmnd *srb, struct rtsx_chip *chip);
+int sd_execute_write_data(struct scsi_cmnd *srb, struct rtsx_chip *chip);
+int sd_get_cmd_rsp(struct scsi_cmnd *srb, struct rtsx_chip *chip);
+int sd_hw_rst(struct scsi_cmnd *srb, struct rtsx_chip *chip);
+#endif
+
+#endif /* __REALTEK_RTSX_SD_H */
diff --git a/drivers/staging/rts5208/spi.c b/drivers/staging/rts5208/spi.c
new file mode 100644
index 000000000..e67e7ecc2
--- /dev/null
+++ b/drivers/staging/rts5208/spi.c
@@ -0,0 +1,1036 @@
+/* Driver for Realtek PCI-Express card reader
+ *
+ * Copyright(c) 2009-2013 Realtek Semiconductor Corp. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, 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, see <http://www.gnu.org/licenses/>.
+ *
+ * Author:
+ * Wei WANG (wei_wang@realsil.com.cn)
+ * Micky Ching (micky_ching@realsil.com.cn)
+ */
+
+#include <linux/blkdev.h>
+#include <linux/kthread.h>
+#include <linux/sched.h>
+
+#include "rtsx.h"
+#include "spi.h"
+
+static inline void spi_set_err_code(struct rtsx_chip *chip, u8 err_code)
+{
+ struct spi_info *spi = &(chip->spi);
+
+ spi->err_code = err_code;
+}
+
+static int spi_init(struct rtsx_chip *chip)
+{
+ int retval;
+
+ retval = rtsx_write_register(chip, SPI_CONTROL, 0xFF,
+ CS_POLARITY_LOW | DTO_MSB_FIRST | SPI_MASTER | SPI_MODE0 | SPI_AUTO);
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+ retval = rtsx_write_register(chip, SPI_TCTL, EDO_TIMING_MASK,
+ SAMPLE_DELAY_HALF);
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+
+ return STATUS_SUCCESS;
+}
+
+static int spi_set_init_para(struct rtsx_chip *chip)
+{
+ struct spi_info *spi = &(chip->spi);
+ int retval;
+
+ retval = rtsx_write_register(chip, SPI_CLK_DIVIDER1, 0xFF,
+ (u8)(spi->clk_div >> 8));
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+ retval = rtsx_write_register(chip, SPI_CLK_DIVIDER0, 0xFF,
+ (u8)(spi->clk_div));
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+
+ retval = switch_clock(chip, spi->spi_clock);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ retval = select_card(chip, SPI_CARD);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ retval = rtsx_write_register(chip, CARD_CLK_EN, SPI_CLK_EN,
+ SPI_CLK_EN);
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+ retval = rtsx_write_register(chip, CARD_OE, SPI_OUTPUT_EN,
+ SPI_OUTPUT_EN);
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+
+ wait_timeout(10);
+
+ retval = spi_init(chip);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ return STATUS_SUCCESS;
+}
+
+static int sf_polling_status(struct rtsx_chip *chip, int msec)
+{
+ int retval;
+
+ rtsx_init_cmd(chip);
+
+ rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_COMMAND, 0xFF, SPI_RDSR);
+ rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_TRANSFER0, 0xFF,
+ SPI_TRANSFER0_START | SPI_POLLING_MODE0);
+ rtsx_add_cmd(chip, CHECK_REG_CMD, SPI_TRANSFER0, SPI_TRANSFER0_END,
+ SPI_TRANSFER0_END);
+
+ retval = rtsx_send_cmd(chip, 0, msec);
+ if (retval < 0) {
+ rtsx_clear_spi_error(chip);
+ spi_set_err_code(chip, SPI_BUSY_ERR);
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ return STATUS_SUCCESS;
+}
+
+static int sf_enable_write(struct rtsx_chip *chip, u8 ins)
+{
+ struct spi_info *spi = &(chip->spi);
+ int retval;
+
+ if (!spi->write_en)
+ return STATUS_SUCCESS;
+
+ rtsx_init_cmd(chip);
+
+ rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_COMMAND, 0xFF, ins);
+ rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_CA_NUMBER, 0xFF,
+ SPI_COMMAND_BIT_8 | SPI_ADDRESS_BIT_24);
+ rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_TRANSFER0, 0xFF,
+ SPI_TRANSFER0_START | SPI_C_MODE0);
+ rtsx_add_cmd(chip, CHECK_REG_CMD, SPI_TRANSFER0, SPI_TRANSFER0_END,
+ SPI_TRANSFER0_END);
+
+ retval = rtsx_send_cmd(chip, 0, 100);
+ if (retval < 0) {
+ rtsx_clear_spi_error(chip);
+ spi_set_err_code(chip, SPI_HW_ERR);
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ return STATUS_SUCCESS;
+}
+
+static int sf_disable_write(struct rtsx_chip *chip, u8 ins)
+{
+ struct spi_info *spi = &(chip->spi);
+ int retval;
+
+ if (!spi->write_en)
+ return STATUS_SUCCESS;
+
+ rtsx_init_cmd(chip);
+
+ rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_COMMAND, 0xFF, ins);
+ rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_CA_NUMBER, 0xFF,
+ SPI_COMMAND_BIT_8 | SPI_ADDRESS_BIT_24);
+ rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_TRANSFER0, 0xFF,
+ SPI_TRANSFER0_START | SPI_C_MODE0);
+ rtsx_add_cmd(chip, CHECK_REG_CMD, SPI_TRANSFER0, SPI_TRANSFER0_END,
+ SPI_TRANSFER0_END);
+
+ retval = rtsx_send_cmd(chip, 0, 100);
+ if (retval < 0) {
+ rtsx_clear_spi_error(chip);
+ spi_set_err_code(chip, SPI_HW_ERR);
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ return STATUS_SUCCESS;
+}
+
+static void sf_program(struct rtsx_chip *chip, u8 ins, u8 addr_mode, u32 addr,
+ u16 len)
+{
+ rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_COMMAND, 0xFF, ins);
+ rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_CA_NUMBER, 0xFF,
+ SPI_COMMAND_BIT_8 | SPI_ADDRESS_BIT_24);
+ rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_LENGTH0, 0xFF, (u8)len);
+ rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_LENGTH1, 0xFF, (u8)(len >> 8));
+ if (addr_mode) {
+ rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_ADDR0, 0xFF, (u8)addr);
+ rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_ADDR1, 0xFF,
+ (u8)(addr >> 8));
+ rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_ADDR2, 0xFF,
+ (u8)(addr >> 16));
+ rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_TRANSFER0, 0xFF,
+ SPI_TRANSFER0_START | SPI_CADO_MODE0);
+ } else {
+ rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_TRANSFER0, 0xFF,
+ SPI_TRANSFER0_START | SPI_CDO_MODE0);
+ }
+ rtsx_add_cmd(chip, CHECK_REG_CMD, SPI_TRANSFER0, SPI_TRANSFER0_END,
+ SPI_TRANSFER0_END);
+}
+
+static int sf_erase(struct rtsx_chip *chip, u8 ins, u8 addr_mode, u32 addr)
+{
+ int retval;
+
+ rtsx_init_cmd(chip);
+
+ rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_COMMAND, 0xFF, ins);
+ rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_CA_NUMBER, 0xFF,
+ SPI_COMMAND_BIT_8 | SPI_ADDRESS_BIT_24);
+ if (addr_mode) {
+ rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_ADDR0, 0xFF, (u8)addr);
+ rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_ADDR1, 0xFF,
+ (u8)(addr >> 8));
+ rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_ADDR2, 0xFF,
+ (u8)(addr >> 16));
+ rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_TRANSFER0, 0xFF,
+ SPI_TRANSFER0_START | SPI_CA_MODE0);
+ } else {
+ rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_TRANSFER0, 0xFF,
+ SPI_TRANSFER0_START | SPI_C_MODE0);
+ }
+ rtsx_add_cmd(chip, CHECK_REG_CMD, SPI_TRANSFER0, SPI_TRANSFER0_END,
+ SPI_TRANSFER0_END);
+
+ retval = rtsx_send_cmd(chip, 0, 100);
+ if (retval < 0) {
+ rtsx_clear_spi_error(chip);
+ spi_set_err_code(chip, SPI_HW_ERR);
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ return STATUS_SUCCESS;
+}
+
+static int spi_init_eeprom(struct rtsx_chip *chip)
+{
+ int retval;
+ int clk;
+
+ if (chip->asic_code)
+ clk = 30;
+ else
+ clk = CLK_30;
+
+ retval = rtsx_write_register(chip, SPI_CLK_DIVIDER1, 0xFF, 0x00);
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+ retval = rtsx_write_register(chip, SPI_CLK_DIVIDER0, 0xFF, 0x27);
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+
+ retval = switch_clock(chip, clk);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ retval = select_card(chip, SPI_CARD);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ retval = rtsx_write_register(chip, CARD_CLK_EN, SPI_CLK_EN,
+ SPI_CLK_EN);
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+ retval = rtsx_write_register(chip, CARD_OE, SPI_OUTPUT_EN,
+ SPI_OUTPUT_EN);
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+
+ wait_timeout(10);
+
+ retval = rtsx_write_register(chip, SPI_CONTROL, 0xFF,
+ CS_POLARITY_HIGH | SPI_EEPROM_AUTO);
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+ retval = rtsx_write_register(chip, SPI_TCTL, EDO_TIMING_MASK,
+ SAMPLE_DELAY_HALF);
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+
+ return STATUS_SUCCESS;
+}
+
+static int spi_eeprom_program_enable(struct rtsx_chip *chip)
+{
+ int retval;
+
+ rtsx_init_cmd(chip);
+
+ rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_CA_NUMBER, 0xFF, 0x86);
+ rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_COMMAND, 0xFF, 0x13);
+ rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_TRANSFER0, 0xFF,
+ SPI_TRANSFER0_START | SPI_CA_MODE0);
+ rtsx_add_cmd(chip, CHECK_REG_CMD, SPI_TRANSFER0, SPI_TRANSFER0_END,
+ SPI_TRANSFER0_END);
+
+ retval = rtsx_send_cmd(chip, 0, 100);
+ if (retval < 0) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ return STATUS_SUCCESS;
+}
+
+int spi_erase_eeprom_chip(struct rtsx_chip *chip)
+{
+ int retval;
+
+ retval = spi_init_eeprom(chip);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ retval = spi_eeprom_program_enable(chip);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ rtsx_init_cmd(chip);
+
+ rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_GPIO_DIR, 0x01, 0);
+ rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_DATA_SOURCE, 0x01, RING_BUFFER);
+ rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_COMMAND, 0xFF, 0x12);
+ rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_CA_NUMBER, 0xFF, 0x84);
+ rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_TRANSFER0, 0xFF,
+ SPI_TRANSFER0_START | SPI_CA_MODE0);
+ rtsx_add_cmd(chip, CHECK_REG_CMD, SPI_TRANSFER0, SPI_TRANSFER0_END,
+ SPI_TRANSFER0_END);
+
+ retval = rtsx_send_cmd(chip, 0, 100);
+ if (retval < 0) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ retval = rtsx_write_register(chip, CARD_GPIO_DIR, 0x01, 0x01);
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+
+ return STATUS_SUCCESS;
+}
+
+int spi_erase_eeprom_byte(struct rtsx_chip *chip, u16 addr)
+{
+ int retval;
+
+ retval = spi_init_eeprom(chip);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ retval = spi_eeprom_program_enable(chip);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ rtsx_init_cmd(chip);
+
+ rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_GPIO_DIR, 0x01, 0);
+ rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_DATA_SOURCE, 0x01, RING_BUFFER);
+ rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_COMMAND, 0xFF, 0x07);
+ rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_ADDR0, 0xFF, (u8)addr);
+ rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_ADDR1, 0xFF, (u8)(addr >> 8));
+ rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_CA_NUMBER, 0xFF, 0x46);
+ rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_TRANSFER0, 0xFF,
+ SPI_TRANSFER0_START | SPI_CA_MODE0);
+ rtsx_add_cmd(chip, CHECK_REG_CMD, SPI_TRANSFER0, SPI_TRANSFER0_END,
+ SPI_TRANSFER0_END);
+
+ retval = rtsx_send_cmd(chip, 0, 100);
+ if (retval < 0) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ retval = rtsx_write_register(chip, CARD_GPIO_DIR, 0x01, 0x01);
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+
+ return STATUS_SUCCESS;
+}
+
+
+int spi_read_eeprom(struct rtsx_chip *chip, u16 addr, u8 *val)
+{
+ int retval;
+ u8 data;
+
+ retval = spi_init_eeprom(chip);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ rtsx_init_cmd(chip);
+
+ rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_GPIO_DIR, 0x01, 0);
+ rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_DATA_SOURCE, 0x01, RING_BUFFER);
+ rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_COMMAND, 0xFF, 0x06);
+ rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_ADDR0, 0xFF, (u8)addr);
+ rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_ADDR1, 0xFF, (u8)(addr >> 8));
+ rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_CA_NUMBER, 0xFF, 0x46);
+ rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_LENGTH0, 0xFF, 1);
+ rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_TRANSFER0, 0xFF,
+ SPI_TRANSFER0_START | SPI_CADI_MODE0);
+ rtsx_add_cmd(chip, CHECK_REG_CMD, SPI_TRANSFER0, SPI_TRANSFER0_END,
+ SPI_TRANSFER0_END);
+
+ retval = rtsx_send_cmd(chip, 0, 100);
+ if (retval < 0) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ wait_timeout(5);
+ retval = rtsx_read_register(chip, SPI_DATA, &data);
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+
+ if (val)
+ *val = data;
+
+ retval = rtsx_write_register(chip, CARD_GPIO_DIR, 0x01, 0x01);
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+
+ return STATUS_SUCCESS;
+}
+
+int spi_write_eeprom(struct rtsx_chip *chip, u16 addr, u8 val)
+{
+ int retval;
+
+ retval = spi_init_eeprom(chip);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ retval = spi_eeprom_program_enable(chip);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ rtsx_init_cmd(chip);
+
+ rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_GPIO_DIR, 0x01, 0);
+ rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_DATA_SOURCE, 0x01, RING_BUFFER);
+ rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_COMMAND, 0xFF, 0x05);
+ rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_ADDR0, 0xFF, val);
+ rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_ADDR1, 0xFF, (u8)addr);
+ rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_ADDR2, 0xFF, (u8)(addr >> 8));
+ rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_CA_NUMBER, 0xFF, 0x4E);
+ rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_TRANSFER0, 0xFF,
+ SPI_TRANSFER0_START | SPI_CA_MODE0);
+ rtsx_add_cmd(chip, CHECK_REG_CMD, SPI_TRANSFER0, SPI_TRANSFER0_END,
+ SPI_TRANSFER0_END);
+
+ retval = rtsx_send_cmd(chip, 0, 100);
+ if (retval < 0) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ retval = rtsx_write_register(chip, CARD_GPIO_DIR, 0x01, 0x01);
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+
+ return STATUS_SUCCESS;
+}
+
+
+int spi_get_status(struct scsi_cmnd *srb, struct rtsx_chip *chip)
+{
+ struct spi_info *spi = &(chip->spi);
+
+ dev_dbg(rtsx_dev(chip), "spi_get_status: err_code = 0x%x\n",
+ spi->err_code);
+ rtsx_stor_set_xfer_buf(&(spi->err_code),
+ min_t(int, scsi_bufflen(srb), 1), srb);
+ scsi_set_resid(srb, scsi_bufflen(srb) - 1);
+
+ return STATUS_SUCCESS;
+}
+
+int spi_set_parameter(struct scsi_cmnd *srb, struct rtsx_chip *chip)
+{
+ struct spi_info *spi = &(chip->spi);
+
+ spi_set_err_code(chip, SPI_NO_ERR);
+
+ if (chip->asic_code)
+ spi->spi_clock = ((u16)(srb->cmnd[8]) << 8) | srb->cmnd[9];
+ else
+ spi->spi_clock = srb->cmnd[3];
+
+ spi->clk_div = ((u16)(srb->cmnd[4]) << 8) | srb->cmnd[5];
+ spi->write_en = srb->cmnd[6];
+
+ dev_dbg(rtsx_dev(chip), "spi_set_parameter: spi_clock = %d, clk_div = %d, write_en = %d\n",
+ spi->spi_clock, spi->clk_div, spi->write_en);
+
+ return STATUS_SUCCESS;
+}
+
+int spi_read_flash_id(struct scsi_cmnd *srb, struct rtsx_chip *chip)
+{
+ int retval;
+ u16 len;
+ u8 *buf;
+
+ spi_set_err_code(chip, SPI_NO_ERR);
+
+ len = ((u16)(srb->cmnd[7]) << 8) | srb->cmnd[8];
+ if (len > 512) {
+ spi_set_err_code(chip, SPI_INVALID_COMMAND);
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ retval = spi_set_init_para(chip);
+ if (retval != STATUS_SUCCESS) {
+ spi_set_err_code(chip, SPI_HW_ERR);
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ rtsx_init_cmd(chip);
+
+ rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_DATA_SOURCE, 0x01,
+ PINGPONG_BUFFER);
+
+ rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_COMMAND, 0xFF, srb->cmnd[3]);
+ rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_ADDR2, 0xFF, srb->cmnd[4]);
+ rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_ADDR1, 0xFF, srb->cmnd[5]);
+ rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_ADDR0, 0xFF, srb->cmnd[6]);
+ rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_CA_NUMBER, 0xFF,
+ SPI_COMMAND_BIT_8 | SPI_ADDRESS_BIT_24);
+ rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_LENGTH1, 0xFF, srb->cmnd[7]);
+ rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_LENGTH0, 0xFF, srb->cmnd[8]);
+
+ if (len == 0) {
+ if (srb->cmnd[9]) {
+ rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_TRANSFER0,
+ 0xFF, SPI_TRANSFER0_START | SPI_CA_MODE0);
+ } else {
+ rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_TRANSFER0,
+ 0xFF, SPI_TRANSFER0_START | SPI_C_MODE0);
+ }
+ } else {
+ if (srb->cmnd[9]) {
+ rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_TRANSFER0, 0xFF,
+ SPI_TRANSFER0_START | SPI_CADI_MODE0);
+ } else {
+ rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_TRANSFER0, 0xFF,
+ SPI_TRANSFER0_START | SPI_CDI_MODE0);
+ }
+ }
+
+ rtsx_add_cmd(chip, CHECK_REG_CMD, SPI_TRANSFER0, SPI_TRANSFER0_END,
+ SPI_TRANSFER0_END);
+
+ retval = rtsx_send_cmd(chip, 0, 100);
+ if (retval < 0) {
+ rtsx_clear_spi_error(chip);
+ spi_set_err_code(chip, SPI_HW_ERR);
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ if (len) {
+ buf = kmalloc(len, GFP_KERNEL);
+ if (!buf) {
+ rtsx_trace(chip);
+ return STATUS_ERROR;
+ }
+
+ retval = rtsx_read_ppbuf(chip, buf, len);
+ if (retval != STATUS_SUCCESS) {
+ spi_set_err_code(chip, SPI_READ_ERR);
+ kfree(buf);
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ rtsx_stor_set_xfer_buf(buf, scsi_bufflen(srb), srb);
+ scsi_set_resid(srb, 0);
+
+ kfree(buf);
+ }
+
+ return STATUS_SUCCESS;
+}
+
+int spi_read_flash(struct scsi_cmnd *srb, struct rtsx_chip *chip)
+{
+ int retval;
+ unsigned int index = 0, offset = 0;
+ u8 ins, slow_read;
+ u32 addr;
+ u16 len;
+ u8 *buf;
+
+ spi_set_err_code(chip, SPI_NO_ERR);
+
+ ins = srb->cmnd[3];
+ addr = ((u32)(srb->cmnd[4]) << 16) | ((u32)(srb->cmnd[5])
+ << 8) | srb->cmnd[6];
+ len = ((u16)(srb->cmnd[7]) << 8) | srb->cmnd[8];
+ slow_read = srb->cmnd[9];
+
+ retval = spi_set_init_para(chip);
+ if (retval != STATUS_SUCCESS) {
+ spi_set_err_code(chip, SPI_HW_ERR);
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ buf = kmalloc(SF_PAGE_LEN, GFP_KERNEL);
+ if (buf == NULL) {
+ rtsx_trace(chip);
+ return STATUS_ERROR;
+ }
+
+ while (len) {
+ u16 pagelen = SF_PAGE_LEN - (u8)addr;
+
+ if (pagelen > len)
+ pagelen = len;
+
+ rtsx_init_cmd(chip);
+
+ trans_dma_enable(DMA_FROM_DEVICE, chip, 256, DMA_256);
+
+ rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_COMMAND, 0xFF, ins);
+
+ if (slow_read) {
+ rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_ADDR0, 0xFF,
+ (u8)addr);
+ rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_ADDR1, 0xFF,
+ (u8)(addr >> 8));
+ rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_ADDR2, 0xFF,
+ (u8)(addr >> 16));
+ rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_CA_NUMBER, 0xFF,
+ SPI_COMMAND_BIT_8 | SPI_ADDRESS_BIT_24);
+ } else {
+ rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_ADDR1, 0xFF,
+ (u8)addr);
+ rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_ADDR2, 0xFF,
+ (u8)(addr >> 8));
+ rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_ADDR3, 0xFF,
+ (u8)(addr >> 16));
+ rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_CA_NUMBER, 0xFF,
+ SPI_COMMAND_BIT_8 | SPI_ADDRESS_BIT_32);
+ }
+
+ rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_LENGTH1, 0xFF,
+ (u8)(pagelen >> 8));
+ rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_LENGTH0, 0xFF,
+ (u8)pagelen);
+
+ rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_TRANSFER0, 0xFF,
+ SPI_TRANSFER0_START | SPI_CADI_MODE0);
+ rtsx_add_cmd(chip, CHECK_REG_CMD, SPI_TRANSFER0,
+ SPI_TRANSFER0_END, SPI_TRANSFER0_END);
+
+ rtsx_send_cmd_no_wait(chip);
+
+ retval = rtsx_transfer_data(chip, 0, buf, pagelen, 0,
+ DMA_FROM_DEVICE, 10000);
+ if (retval < 0) {
+ kfree(buf);
+ rtsx_clear_spi_error(chip);
+ spi_set_err_code(chip, SPI_HW_ERR);
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ rtsx_stor_access_xfer_buf(buf, pagelen, srb, &index, &offset,
+ TO_XFER_BUF);
+
+ addr += pagelen;
+ len -= pagelen;
+ }
+
+ scsi_set_resid(srb, 0);
+ kfree(buf);
+
+ return STATUS_SUCCESS;
+}
+
+int spi_write_flash(struct scsi_cmnd *srb, struct rtsx_chip *chip)
+{
+ int retval;
+ u8 ins, program_mode;
+ u32 addr;
+ u16 len;
+ u8 *buf;
+ unsigned int index = 0, offset = 0;
+
+ spi_set_err_code(chip, SPI_NO_ERR);
+
+ ins = srb->cmnd[3];
+ addr = ((u32)(srb->cmnd[4]) << 16) | ((u32)(srb->cmnd[5])
+ << 8) | srb->cmnd[6];
+ len = ((u16)(srb->cmnd[7]) << 8) | srb->cmnd[8];
+ program_mode = srb->cmnd[9];
+
+ retval = spi_set_init_para(chip);
+ if (retval != STATUS_SUCCESS) {
+ spi_set_err_code(chip, SPI_HW_ERR);
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ if (program_mode == BYTE_PROGRAM) {
+ buf = kmalloc(4, GFP_KERNEL);
+ if (!buf) {
+ rtsx_trace(chip);
+ return STATUS_ERROR;
+ }
+
+ while (len) {
+ retval = sf_enable_write(chip, SPI_WREN);
+ if (retval != STATUS_SUCCESS) {
+ kfree(buf);
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ rtsx_stor_access_xfer_buf(buf, 1, srb, &index, &offset,
+ FROM_XFER_BUF);
+
+ rtsx_init_cmd(chip);
+
+ rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_DATA_SOURCE,
+ 0x01, PINGPONG_BUFFER);
+ rtsx_add_cmd(chip, WRITE_REG_CMD, PPBUF_BASE2, 0xFF,
+ buf[0]);
+ sf_program(chip, ins, 1, addr, 1);
+
+ retval = rtsx_send_cmd(chip, 0, 100);
+ if (retval < 0) {
+ kfree(buf);
+ rtsx_clear_spi_error(chip);
+ spi_set_err_code(chip, SPI_HW_ERR);
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ retval = sf_polling_status(chip, 100);
+ if (retval != STATUS_SUCCESS) {
+ kfree(buf);
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ addr++;
+ len--;
+ }
+
+ kfree(buf);
+
+ } else if (program_mode == AAI_PROGRAM) {
+ int first_byte = 1;
+
+ retval = sf_enable_write(chip, SPI_WREN);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ buf = kmalloc(4, GFP_KERNEL);
+ if (!buf) {
+ rtsx_trace(chip);
+ return STATUS_ERROR;
+ }
+
+ while (len) {
+ rtsx_stor_access_xfer_buf(buf, 1, srb, &index, &offset,
+ FROM_XFER_BUF);
+
+ rtsx_init_cmd(chip);
+
+ rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_DATA_SOURCE,
+ 0x01, PINGPONG_BUFFER);
+ rtsx_add_cmd(chip, WRITE_REG_CMD, PPBUF_BASE2, 0xFF,
+ buf[0]);
+ if (first_byte) {
+ sf_program(chip, ins, 1, addr, 1);
+ first_byte = 0;
+ } else {
+ sf_program(chip, ins, 0, 0, 1);
+ }
+
+ retval = rtsx_send_cmd(chip, 0, 100);
+ if (retval < 0) {
+ kfree(buf);
+ rtsx_clear_spi_error(chip);
+ spi_set_err_code(chip, SPI_HW_ERR);
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ retval = sf_polling_status(chip, 100);
+ if (retval != STATUS_SUCCESS) {
+ kfree(buf);
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ len--;
+ }
+
+ kfree(buf);
+
+ retval = sf_disable_write(chip, SPI_WRDI);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ retval = sf_polling_status(chip, 100);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+ } else if (program_mode == PAGE_PROGRAM) {
+ buf = kmalloc(SF_PAGE_LEN, GFP_KERNEL);
+ if (!buf) {
+ rtsx_trace(chip);
+ return STATUS_NOMEM;
+ }
+
+ while (len) {
+ u16 pagelen = SF_PAGE_LEN - (u8)addr;
+
+ if (pagelen > len)
+ pagelen = len;
+
+ retval = sf_enable_write(chip, SPI_WREN);
+ if (retval != STATUS_SUCCESS) {
+ kfree(buf);
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ rtsx_init_cmd(chip);
+
+ trans_dma_enable(DMA_TO_DEVICE, chip, 256, DMA_256);
+ sf_program(chip, ins, 1, addr, pagelen);
+
+ rtsx_send_cmd_no_wait(chip);
+
+ rtsx_stor_access_xfer_buf(buf, pagelen, srb, &index,
+ &offset, FROM_XFER_BUF);
+
+ retval = rtsx_transfer_data(chip, 0, buf, pagelen, 0,
+ DMA_TO_DEVICE, 100);
+ if (retval < 0) {
+ kfree(buf);
+ rtsx_clear_spi_error(chip);
+ spi_set_err_code(chip, SPI_HW_ERR);
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ retval = sf_polling_status(chip, 100);
+ if (retval != STATUS_SUCCESS) {
+ kfree(buf);
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ addr += pagelen;
+ len -= pagelen;
+ }
+
+ kfree(buf);
+ } else {
+ spi_set_err_code(chip, SPI_INVALID_COMMAND);
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ return STATUS_SUCCESS;
+}
+
+int spi_erase_flash(struct scsi_cmnd *srb, struct rtsx_chip *chip)
+{
+ int retval;
+ u8 ins, erase_mode;
+ u32 addr;
+
+ spi_set_err_code(chip, SPI_NO_ERR);
+
+ ins = srb->cmnd[3];
+ addr = ((u32)(srb->cmnd[4]) << 16) | ((u32)(srb->cmnd[5])
+ << 8) | srb->cmnd[6];
+ erase_mode = srb->cmnd[9];
+
+ retval = spi_set_init_para(chip);
+ if (retval != STATUS_SUCCESS) {
+ spi_set_err_code(chip, SPI_HW_ERR);
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ if (erase_mode == PAGE_ERASE) {
+ retval = sf_enable_write(chip, SPI_WREN);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ retval = sf_erase(chip, ins, 1, addr);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+ } else if (erase_mode == CHIP_ERASE) {
+ retval = sf_enable_write(chip, SPI_WREN);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ retval = sf_erase(chip, ins, 0, 0);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+ } else {
+ spi_set_err_code(chip, SPI_INVALID_COMMAND);
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ return STATUS_SUCCESS;
+}
+
+int spi_write_flash_status(struct scsi_cmnd *srb, struct rtsx_chip *chip)
+{
+ int retval;
+ u8 ins, status, ewsr;
+
+ ins = srb->cmnd[3];
+ status = srb->cmnd[4];
+ ewsr = srb->cmnd[5];
+
+ retval = spi_set_init_para(chip);
+ if (retval != STATUS_SUCCESS) {
+ spi_set_err_code(chip, SPI_HW_ERR);
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ retval = sf_enable_write(chip, ewsr);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ rtsx_init_cmd(chip);
+
+ rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_DATA_SOURCE, 0x01,
+ PINGPONG_BUFFER);
+
+ rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_COMMAND, 0xFF, ins);
+ rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_CA_NUMBER, 0xFF,
+ SPI_COMMAND_BIT_8 | SPI_ADDRESS_BIT_24);
+ rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_LENGTH1, 0xFF, 0);
+ rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_LENGTH0, 0xFF, 1);
+ rtsx_add_cmd(chip, WRITE_REG_CMD, PPBUF_BASE2, 0xFF, status);
+ rtsx_add_cmd(chip, WRITE_REG_CMD, SPI_TRANSFER0, 0xFF,
+ SPI_TRANSFER0_START | SPI_CDO_MODE0);
+ rtsx_add_cmd(chip, CHECK_REG_CMD, SPI_TRANSFER0, SPI_TRANSFER0_END,
+ SPI_TRANSFER0_END);
+
+ retval = rtsx_send_cmd(chip, 0, 100);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_clear_spi_error(chip);
+ spi_set_err_code(chip, SPI_HW_ERR);
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ return STATUS_SUCCESS;
+}
diff --git a/drivers/staging/rts5208/spi.h b/drivers/staging/rts5208/spi.h
new file mode 100644
index 000000000..fc824b5d8
--- /dev/null
+++ b/drivers/staging/rts5208/spi.h
@@ -0,0 +1,65 @@
+/* Driver for Realtek PCI-Express card reader
+ * Header file
+ *
+ * Copyright(c) 2009-2013 Realtek Semiconductor Corp. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, 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, see <http://www.gnu.org/licenses/>.
+ *
+ * Author:
+ * Wei WANG (wei_wang@realsil.com.cn)
+ * Micky Ching (micky_ching@realsil.com.cn)
+ */
+
+#ifndef __REALTEK_RTSX_SPI_H
+#define __REALTEK_RTSX_SPI_H
+
+/* SPI operation error */
+#define SPI_NO_ERR 0x00
+#define SPI_HW_ERR 0x01
+#define SPI_INVALID_COMMAND 0x02
+#define SPI_READ_ERR 0x03
+#define SPI_WRITE_ERR 0x04
+#define SPI_ERASE_ERR 0x05
+#define SPI_BUSY_ERR 0x06
+
+/* Serial flash instruction */
+#define SPI_READ 0x03
+#define SPI_FAST_READ 0x0B
+#define SPI_WREN 0x06
+#define SPI_WRDI 0x04
+#define SPI_RDSR 0x05
+
+#define SF_PAGE_LEN 256
+
+#define BYTE_PROGRAM 0
+#define AAI_PROGRAM 1
+#define PAGE_PROGRAM 2
+
+#define PAGE_ERASE 0
+#define CHIP_ERASE 1
+
+int spi_erase_eeprom_chip(struct rtsx_chip *chip);
+int spi_erase_eeprom_byte(struct rtsx_chip *chip, u16 addr);
+int spi_read_eeprom(struct rtsx_chip *chip, u16 addr, u8 *val);
+int spi_write_eeprom(struct rtsx_chip *chip, u16 addr, u8 val);
+int spi_get_status(struct scsi_cmnd *srb, struct rtsx_chip *chip);
+int spi_set_parameter(struct scsi_cmnd *srb, struct rtsx_chip *chip);
+int spi_read_flash_id(struct scsi_cmnd *srb, struct rtsx_chip *chip);
+int spi_read_flash(struct scsi_cmnd *srb, struct rtsx_chip *chip);
+int spi_write_flash(struct scsi_cmnd *srb, struct rtsx_chip *chip);
+int spi_erase_flash(struct scsi_cmnd *srb, struct rtsx_chip *chip);
+int spi_write_flash_status(struct scsi_cmnd *srb, struct rtsx_chip *chip);
+
+
+#endif /* __REALTEK_RTSX_SPI_H */
diff --git a/drivers/staging/rts5208/trace.c b/drivers/staging/rts5208/trace.c
new file mode 100644
index 000000000..1bddbdf34
--- /dev/null
+++ b/drivers/staging/rts5208/trace.c
@@ -0,0 +1,26 @@
+#include <linux/kernel.h>
+#include <linux/string.h>
+
+#include "rtsx.h"
+
+#ifdef _MSG_TRACE
+
+void _rtsx_trace(struct rtsx_chip *chip, const char *file, const char *func,
+ int line)
+{
+ struct trace_msg_t *msg = &chip->trace_msg[chip->msg_idx];
+
+ file = kbasename(file);
+ dev_dbg(rtsx_dev(chip), "[%s][%s]:[%d]\n", file, func, line);
+
+ strncpy(msg->file, file, MSG_FILE_LEN - 1);
+ strncpy(msg->func, func, MSG_FUNC_LEN - 1);
+ msg->line = (u16)line;
+ get_current_time(msg->timeval_buf, TIME_VAL_LEN);
+ msg->valid = 1;
+
+ chip->msg_idx++;
+ if (chip->msg_idx >= TRACE_ITEM_CNT)
+ chip->msg_idx = 0;
+}
+#endif
diff --git a/drivers/staging/rts5208/trace.h b/drivers/staging/rts5208/trace.h
new file mode 100644
index 000000000..5b807874c
--- /dev/null
+++ b/drivers/staging/rts5208/trace.h
@@ -0,0 +1,40 @@
+/* Driver for Realtek PCI-Express card reader
+ * Header file
+ *
+ * Copyright(c) 2009-2013 Realtek Semiconductor Corp. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, 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, see <http://www.gnu.org/licenses/>.
+ *
+ * Author:
+ * Wei WANG (wei_wang@realsil.com.cn)
+ * Micky Ching (micky_ching@realsil.com.cn)
+ */
+
+#ifndef __REALTEK_RTSX_TRACE_H
+#define __REALTEK_RTSX_TRACE_H
+
+struct rtsx_chip;
+
+#ifdef _MSG_TRACE
+void _rtsx_trace(struct rtsx_chip *chip, const char *file, const char *func,
+ int line);
+#define rtsx_trace(chip) \
+ _rtsx_trace(chip, __FILE__, __func__, __LINE__)
+#else
+static inline void rtsx_trace(struct rtsx_chip *chip)
+{
+}
+#endif
+
+#endif /* __REALTEK_RTSX_TRACE_H */
diff --git a/drivers/staging/rts5208/xd.c b/drivers/staging/rts5208/xd.c
new file mode 100644
index 000000000..8fd108e50
--- /dev/null
+++ b/drivers/staging/rts5208/xd.c
@@ -0,0 +1,2345 @@
+/* Driver for Realtek PCI-Express card reader
+ *
+ * Copyright(c) 2009-2013 Realtek Semiconductor Corp. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, 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, see <http://www.gnu.org/licenses/>.
+ *
+ * Author:
+ * Wei WANG (wei_wang@realsil.com.cn)
+ * Micky Ching (micky_ching@realsil.com.cn)
+ */
+
+#include <linux/blkdev.h>
+#include <linux/kthread.h>
+#include <linux/sched.h>
+#include <linux/vmalloc.h>
+
+#include "rtsx.h"
+#include "rtsx_transport.h"
+#include "rtsx_scsi.h"
+#include "rtsx_card.h"
+#include "xd.h"
+
+static int xd_build_l2p_tbl(struct rtsx_chip *chip, int zone_no);
+static int xd_init_page(struct rtsx_chip *chip, u32 phy_blk, u16 logoff,
+ u8 start_page, u8 end_page);
+
+static inline void xd_set_err_code(struct rtsx_chip *chip, u8 err_code)
+{
+ struct xd_info *xd_card = &(chip->xd_card);
+
+ xd_card->err_code = err_code;
+}
+
+static inline int xd_check_err_code(struct rtsx_chip *chip, u8 err_code)
+{
+ struct xd_info *xd_card = &(chip->xd_card);
+
+ return (xd_card->err_code == err_code);
+}
+
+static int xd_set_init_para(struct rtsx_chip *chip)
+{
+ struct xd_info *xd_card = &(chip->xd_card);
+ int retval;
+
+ if (chip->asic_code)
+ xd_card->xd_clock = 47;
+ else
+ xd_card->xd_clock = CLK_50;
+
+ retval = switch_clock(chip, xd_card->xd_clock);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ return STATUS_SUCCESS;
+}
+
+static int xd_switch_clock(struct rtsx_chip *chip)
+{
+ struct xd_info *xd_card = &(chip->xd_card);
+ int retval;
+
+ retval = select_card(chip, XD_CARD);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ retval = switch_clock(chip, xd_card->xd_clock);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ return STATUS_SUCCESS;
+}
+
+static int xd_read_id(struct rtsx_chip *chip, u8 id_cmd, u8 *id_buf, u8 buf_len)
+{
+ int retval, i;
+ u8 *ptr;
+
+ rtsx_init_cmd(chip);
+
+ rtsx_add_cmd(chip, WRITE_REG_CMD, XD_DAT, 0xFF, id_cmd);
+ rtsx_add_cmd(chip, WRITE_REG_CMD, XD_TRANSFER, 0xFF,
+ XD_TRANSFER_START | XD_READ_ID);
+ rtsx_add_cmd(chip, CHECK_REG_CMD, XD_TRANSFER, XD_TRANSFER_END,
+ XD_TRANSFER_END);
+
+ for (i = 0; i < 4; i++)
+ rtsx_add_cmd(chip, READ_REG_CMD, (u16)(XD_ADDRESS1 + i), 0, 0);
+
+ retval = rtsx_send_cmd(chip, XD_CARD, 20);
+ if (retval < 0) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ ptr = rtsx_get_cmd_data(chip) + 1;
+ if (id_buf && buf_len) {
+ if (buf_len > 4)
+ buf_len = 4;
+ memcpy(id_buf, ptr, buf_len);
+ }
+
+ return STATUS_SUCCESS;
+}
+
+static void xd_assign_phy_addr(struct rtsx_chip *chip, u32 addr, u8 mode)
+{
+ struct xd_info *xd_card = &(chip->xd_card);
+
+ switch (mode) {
+ case XD_RW_ADDR:
+ rtsx_add_cmd(chip, WRITE_REG_CMD, XD_ADDRESS0, 0xFF, 0);
+ rtsx_add_cmd(chip, WRITE_REG_CMD, XD_ADDRESS1, 0xFF, (u8)addr);
+ rtsx_add_cmd(chip, WRITE_REG_CMD, XD_ADDRESS2,
+ 0xFF, (u8)(addr >> 8));
+ rtsx_add_cmd(chip, WRITE_REG_CMD, XD_ADDRESS3,
+ 0xFF, (u8)(addr >> 16));
+ rtsx_add_cmd(chip, WRITE_REG_CMD, XD_CFG, 0xFF,
+ xd_card->addr_cycle | XD_CALC_ECC | XD_BA_NO_TRANSFORM);
+ break;
+
+ case XD_ERASE_ADDR:
+ rtsx_add_cmd(chip, WRITE_REG_CMD, XD_ADDRESS0, 0xFF, (u8)addr);
+ rtsx_add_cmd(chip, WRITE_REG_CMD, XD_ADDRESS1,
+ 0xFF, (u8)(addr >> 8));
+ rtsx_add_cmd(chip, WRITE_REG_CMD, XD_ADDRESS2,
+ 0xFF, (u8)(addr >> 16));
+ rtsx_add_cmd(chip, WRITE_REG_CMD, XD_CFG, 0xFF,
+ (xd_card->addr_cycle - 1) | XD_CALC_ECC |
+ XD_BA_NO_TRANSFORM);
+ break;
+
+ default:
+ break;
+ }
+}
+
+static int xd_read_redundant(struct rtsx_chip *chip, u32 page_addr,
+ u8 *buf, int buf_len)
+{
+ int retval, i;
+
+ rtsx_init_cmd(chip);
+
+ xd_assign_phy_addr(chip, page_addr, XD_RW_ADDR);
+
+ rtsx_add_cmd(chip, WRITE_REG_CMD, XD_TRANSFER,
+ 0xFF, XD_TRANSFER_START | XD_READ_REDUNDANT);
+ rtsx_add_cmd(chip, CHECK_REG_CMD, XD_TRANSFER,
+ XD_TRANSFER_END, XD_TRANSFER_END);
+
+ for (i = 0; i < 6; i++)
+ rtsx_add_cmd(chip, READ_REG_CMD, (u16)(XD_PAGE_STATUS + i),
+ 0, 0);
+ for (i = 0; i < 4; i++)
+ rtsx_add_cmd(chip, READ_REG_CMD, (u16)(XD_RESERVED0 + i),
+ 0, 0);
+ rtsx_add_cmd(chip, READ_REG_CMD, XD_PARITY, 0, 0);
+
+ retval = rtsx_send_cmd(chip, XD_CARD, 500);
+ if (retval < 0) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ if (buf && buf_len) {
+ u8 *ptr = rtsx_get_cmd_data(chip) + 1;
+
+ if (buf_len > 11)
+ buf_len = 11;
+ memcpy(buf, ptr, buf_len);
+ }
+
+ return STATUS_SUCCESS;
+}
+
+static int xd_read_data_from_ppb(struct rtsx_chip *chip, int offset,
+ u8 *buf, int buf_len)
+{
+ int retval, i;
+
+ if (!buf || (buf_len < 0)) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ rtsx_init_cmd(chip);
+
+ for (i = 0; i < buf_len; i++)
+ rtsx_add_cmd(chip, READ_REG_CMD, PPBUF_BASE2 + offset + i,
+ 0, 0);
+
+ retval = rtsx_send_cmd(chip, 0, 250);
+ if (retval < 0) {
+ rtsx_clear_xd_error(chip);
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ memcpy(buf, rtsx_get_cmd_data(chip), buf_len);
+
+ return STATUS_SUCCESS;
+}
+
+static int xd_read_cis(struct rtsx_chip *chip, u32 page_addr, u8 *buf,
+ int buf_len)
+{
+ int retval;
+ u8 reg;
+
+ if (!buf || (buf_len < 10)) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ rtsx_init_cmd(chip);
+
+ xd_assign_phy_addr(chip, page_addr, XD_RW_ADDR);
+
+ rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_DATA_SOURCE,
+ 0x01, PINGPONG_BUFFER);
+ rtsx_add_cmd(chip, WRITE_REG_CMD, XD_PAGE_CNT, 0xFF, 1);
+ rtsx_add_cmd(chip, WRITE_REG_CMD, XD_CHK_DATA_STATUS,
+ XD_AUTO_CHK_DATA_STATUS, XD_AUTO_CHK_DATA_STATUS);
+
+ rtsx_add_cmd(chip, WRITE_REG_CMD, XD_TRANSFER, 0xFF,
+ XD_TRANSFER_START | XD_READ_PAGES);
+ rtsx_add_cmd(chip, CHECK_REG_CMD, XD_TRANSFER, XD_TRANSFER_END,
+ XD_TRANSFER_END);
+
+ retval = rtsx_send_cmd(chip, XD_CARD, 250);
+ if (retval == -ETIMEDOUT) {
+ rtsx_clear_xd_error(chip);
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ retval = rtsx_read_register(chip, XD_PAGE_STATUS, &reg);
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+ if (reg != XD_GPG) {
+ rtsx_clear_xd_error(chip);
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ retval = rtsx_read_register(chip, XD_CTL, &reg);
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+ if (!(reg & XD_ECC1_ERROR) || !(reg & XD_ECC1_UNCORRECTABLE)) {
+ retval = xd_read_data_from_ppb(chip, 0, buf, buf_len);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+ if (reg & XD_ECC1_ERROR) {
+ u8 ecc_bit, ecc_byte;
+
+ retval = rtsx_read_register(chip, XD_ECC_BIT1,
+ &ecc_bit);
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+ retval = rtsx_read_register(chip, XD_ECC_BYTE1,
+ &ecc_byte);
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+
+ dev_dbg(rtsx_dev(chip), "ECC_BIT1 = 0x%x, ECC_BYTE1 = 0x%x\n",
+ ecc_bit, ecc_byte);
+ if (ecc_byte < buf_len) {
+ dev_dbg(rtsx_dev(chip), "Before correct: 0x%x\n",
+ buf[ecc_byte]);
+ buf[ecc_byte] ^= (1 << ecc_bit);
+ dev_dbg(rtsx_dev(chip), "After correct: 0x%x\n",
+ buf[ecc_byte]);
+ }
+ }
+ } else if (!(reg & XD_ECC2_ERROR) || !(reg & XD_ECC2_UNCORRECTABLE)) {
+ rtsx_clear_xd_error(chip);
+
+ retval = xd_read_data_from_ppb(chip, 256, buf, buf_len);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+ if (reg & XD_ECC2_ERROR) {
+ u8 ecc_bit, ecc_byte;
+
+ retval = rtsx_read_register(chip, XD_ECC_BIT2,
+ &ecc_bit);
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+ retval = rtsx_read_register(chip, XD_ECC_BYTE2,
+ &ecc_byte);
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+
+ dev_dbg(rtsx_dev(chip), "ECC_BIT2 = 0x%x, ECC_BYTE2 = 0x%x\n",
+ ecc_bit, ecc_byte);
+ if (ecc_byte < buf_len) {
+ dev_dbg(rtsx_dev(chip), "Before correct: 0x%x\n",
+ buf[ecc_byte]);
+ buf[ecc_byte] ^= (1 << ecc_bit);
+ dev_dbg(rtsx_dev(chip), "After correct: 0x%x\n",
+ buf[ecc_byte]);
+ }
+ }
+ } else {
+ rtsx_clear_xd_error(chip);
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ return STATUS_SUCCESS;
+}
+
+static void xd_fill_pull_ctl_disable(struct rtsx_chip *chip)
+{
+ if (CHECK_PID(chip, 0x5208)) {
+ rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_PULL_CTL1, 0xFF,
+ XD_D3_PD | XD_D2_PD | XD_D1_PD | XD_D0_PD);
+ rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_PULL_CTL2, 0xFF,
+ XD_D7_PD | XD_D6_PD | XD_D5_PD | XD_D4_PD);
+ rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_PULL_CTL3, 0xFF,
+ XD_WP_PD | XD_CE_PD | XD_CLE_PD | XD_CD_PU);
+ rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_PULL_CTL4, 0xFF,
+ XD_RDY_PD | XD_WE_PD | XD_RE_PD | XD_ALE_PD);
+ rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_PULL_CTL5, 0xFF,
+ MS_INS_PU | SD_WP_PD | SD_CD_PU | SD_CMD_PD);
+ rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_PULL_CTL6, 0xFF,
+ MS_D5_PD | MS_D4_PD);
+ } else if (CHECK_PID(chip, 0x5288)) {
+ if (CHECK_BARO_PKG(chip, QFN)) {
+ rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_PULL_CTL1,
+ 0xFF, 0x55);
+ rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_PULL_CTL2,
+ 0xFF, 0x55);
+ rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_PULL_CTL3,
+ 0xFF, 0x4B);
+ rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_PULL_CTL4,
+ 0xFF, 0x69);
+ }
+ }
+}
+
+static void xd_fill_pull_ctl_stage1_barossa(struct rtsx_chip *chip)
+{
+ if (CHECK_BARO_PKG(chip, QFN)) {
+ rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_PULL_CTL1, 0xFF, 0x55);
+ rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_PULL_CTL2, 0xFF, 0x55);
+ rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_PULL_CTL3, 0xFF, 0x4B);
+ rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_PULL_CTL4, 0xFF, 0x55);
+ }
+}
+
+static void xd_fill_pull_ctl_enable(struct rtsx_chip *chip)
+{
+ if (CHECK_PID(chip, 0x5208)) {
+ rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_PULL_CTL1, 0xFF,
+ XD_D3_PD | XD_D2_PD | XD_D1_PD | XD_D0_PD);
+ rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_PULL_CTL2, 0xFF,
+ XD_D7_PD | XD_D6_PD | XD_D5_PD | XD_D4_PD);
+ rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_PULL_CTL3, 0xFF,
+ XD_WP_PD | XD_CE_PU | XD_CLE_PD | XD_CD_PU);
+ rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_PULL_CTL4, 0xFF,
+ XD_RDY_PU | XD_WE_PU | XD_RE_PU | XD_ALE_PD);
+ rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_PULL_CTL5, 0xFF,
+ MS_INS_PU | SD_WP_PD | SD_CD_PU | SD_CMD_PD);
+ rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_PULL_CTL6, 0xFF,
+ MS_D5_PD | MS_D4_PD);
+ } else if (CHECK_PID(chip, 0x5288)) {
+ if (CHECK_BARO_PKG(chip, QFN)) {
+ rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_PULL_CTL1,
+ 0xFF, 0x55);
+ rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_PULL_CTL2,
+ 0xFF, 0x55);
+ rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_PULL_CTL3,
+ 0xFF, 0x53);
+ rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_PULL_CTL4,
+ 0xFF, 0xA9);
+ }
+ }
+}
+
+static int xd_pull_ctl_disable(struct rtsx_chip *chip)
+{
+ int retval;
+
+ if (CHECK_PID(chip, 0x5208)) {
+ retval = rtsx_write_register(chip, CARD_PULL_CTL1, 0xFF,
+ XD_D3_PD | XD_D2_PD | XD_D1_PD | XD_D0_PD);
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+ retval = rtsx_write_register(chip, CARD_PULL_CTL2, 0xFF,
+ XD_D7_PD | XD_D6_PD | XD_D5_PD | XD_D4_PD);
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+ retval = rtsx_write_register(chip, CARD_PULL_CTL3, 0xFF,
+ XD_WP_PD | XD_CE_PD | XD_CLE_PD | XD_CD_PU);
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+ retval = rtsx_write_register(chip, CARD_PULL_CTL4, 0xFF,
+ XD_RDY_PD | XD_WE_PD | XD_RE_PD | XD_ALE_PD);
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+ retval = rtsx_write_register(chip, CARD_PULL_CTL5, 0xFF,
+ MS_INS_PU | SD_WP_PD | SD_CD_PU | SD_CMD_PD);
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+ retval = rtsx_write_register(chip, CARD_PULL_CTL6, 0xFF,
+ MS_D5_PD | MS_D4_PD);
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+ } else if (CHECK_PID(chip, 0x5288)) {
+ if (CHECK_BARO_PKG(chip, QFN)) {
+ retval = rtsx_write_register(chip, CARD_PULL_CTL1,
+ 0xFF, 0x55);
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+ retval = rtsx_write_register(chip, CARD_PULL_CTL2,
+ 0xFF, 0x55);
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+ retval = rtsx_write_register(chip, CARD_PULL_CTL3,
+ 0xFF, 0x4B);
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+ retval = rtsx_write_register(chip, CARD_PULL_CTL4,
+ 0xFF, 0x69);
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+ }
+ }
+
+ return STATUS_SUCCESS;
+}
+
+static int reset_xd(struct rtsx_chip *chip)
+{
+ struct xd_info *xd_card = &(chip->xd_card);
+ int retval, i, j;
+ u8 *ptr, id_buf[4], redunt[11];
+
+ retval = select_card(chip, XD_CARD);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ rtsx_init_cmd(chip);
+
+ rtsx_add_cmd(chip, WRITE_REG_CMD, XD_CHK_DATA_STATUS, 0xFF,
+ XD_PGSTS_NOT_FF);
+ if (chip->asic_code) {
+ if (!CHECK_PID(chip, 0x5288))
+ xd_fill_pull_ctl_disable(chip);
+ else
+ xd_fill_pull_ctl_stage1_barossa(chip);
+ } else {
+ rtsx_add_cmd(chip, WRITE_REG_CMD, FPGA_PULL_CTL, 0xFF,
+ (FPGA_XD_PULL_CTL_EN1 & FPGA_XD_PULL_CTL_EN3) | 0x20);
+ }
+
+ if (!chip->ft2_fast_mode)
+ rtsx_add_cmd(chip, WRITE_REG_CMD, XD_INIT,
+ XD_NO_AUTO_PWR_OFF, 0);
+
+ rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_OE, XD_OUTPUT_EN, 0);
+
+ retval = rtsx_send_cmd(chip, XD_CARD, 100);
+ if (retval < 0) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ if (!chip->ft2_fast_mode) {
+ retval = card_power_off(chip, XD_CARD);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ wait_timeout(250);
+
+ rtsx_init_cmd(chip);
+
+ if (chip->asic_code) {
+ xd_fill_pull_ctl_enable(chip);
+ } else {
+ rtsx_add_cmd(chip, WRITE_REG_CMD, FPGA_PULL_CTL, 0xFF,
+ (FPGA_XD_PULL_CTL_EN1 & FPGA_XD_PULL_CTL_EN2) |
+ 0x20);
+ }
+
+ retval = rtsx_send_cmd(chip, XD_CARD, 100);
+ if (retval < 0) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ retval = card_power_on(chip, XD_CARD);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+#ifdef SUPPORT_OCP
+ wait_timeout(50);
+ if (chip->ocp_stat & (SD_OC_NOW | SD_OC_EVER)) {
+ dev_dbg(rtsx_dev(chip), "Over current, OCPSTAT is 0x%x\n",
+ chip->ocp_stat);
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+#endif
+ }
+
+ rtsx_init_cmd(chip);
+
+ if (chip->ft2_fast_mode) {
+ if (chip->asic_code) {
+ xd_fill_pull_ctl_enable(chip);
+ } else {
+ rtsx_add_cmd(chip, WRITE_REG_CMD, FPGA_PULL_CTL, 0xFF,
+ (FPGA_XD_PULL_CTL_EN1 & FPGA_XD_PULL_CTL_EN2) |
+ 0x20);
+ }
+ }
+
+ rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_OE, XD_OUTPUT_EN, XD_OUTPUT_EN);
+ rtsx_add_cmd(chip, WRITE_REG_CMD, XD_CTL, XD_CE_DISEN, XD_CE_DISEN);
+
+ retval = rtsx_send_cmd(chip, XD_CARD, 100);
+ if (retval < 0) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ if (!chip->ft2_fast_mode)
+ wait_timeout(200);
+
+ retval = xd_set_init_para(chip);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ /* Read ID to check if the timing setting is right */
+ for (i = 0; i < 4; i++) {
+ rtsx_init_cmd(chip);
+
+ rtsx_add_cmd(chip, WRITE_REG_CMD, XD_DTCTL, 0xFF,
+ XD_TIME_SETUP_STEP * 3 +
+ XD_TIME_RW_STEP * (2 + i) + XD_TIME_RWN_STEP * i);
+ rtsx_add_cmd(chip, WRITE_REG_CMD, XD_CATCTL, 0xFF,
+ XD_TIME_SETUP_STEP * 3 + XD_TIME_RW_STEP * (4 + i) +
+ XD_TIME_RWN_STEP * (3 + i));
+
+ rtsx_add_cmd(chip, WRITE_REG_CMD, XD_TRANSFER, 0xFF,
+ XD_TRANSFER_START | XD_RESET);
+ rtsx_add_cmd(chip, CHECK_REG_CMD, XD_TRANSFER,
+ XD_TRANSFER_END, XD_TRANSFER_END);
+
+ rtsx_add_cmd(chip, READ_REG_CMD, XD_DAT, 0, 0);
+ rtsx_add_cmd(chip, READ_REG_CMD, XD_CTL, 0, 0);
+
+ retval = rtsx_send_cmd(chip, XD_CARD, 100);
+ if (retval < 0) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ ptr = rtsx_get_cmd_data(chip) + 1;
+
+ dev_dbg(rtsx_dev(chip), "XD_DAT: 0x%x, XD_CTL: 0x%x\n",
+ ptr[0], ptr[1]);
+
+ if (((ptr[0] & READY_FLAG) != READY_STATE) ||
+ !(ptr[1] & XD_RDY))
+ continue;
+
+ retval = xd_read_id(chip, READ_ID, id_buf, 4);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ dev_dbg(rtsx_dev(chip), "READ_ID: 0x%x 0x%x 0x%x 0x%x\n",
+ id_buf[0], id_buf[1], id_buf[2], id_buf[3]);
+
+ xd_card->device_code = id_buf[1];
+
+ /* Check if the xD card is supported */
+ switch (xd_card->device_code) {
+ case XD_4M_X8_512_1:
+ case XD_4M_X8_512_2:
+ xd_card->block_shift = 4;
+ xd_card->page_off = 0x0F;
+ xd_card->addr_cycle = 3;
+ xd_card->zone_cnt = 1;
+ xd_card->capacity = 8000;
+ XD_SET_4MB(xd_card);
+ break;
+ case XD_8M_X8_512:
+ xd_card->block_shift = 4;
+ xd_card->page_off = 0x0F;
+ xd_card->addr_cycle = 3;
+ xd_card->zone_cnt = 1;
+ xd_card->capacity = 16000;
+ break;
+ case XD_16M_X8_512:
+ XD_PAGE_512(xd_card);
+ xd_card->addr_cycle = 3;
+ xd_card->zone_cnt = 1;
+ xd_card->capacity = 32000;
+ break;
+ case XD_32M_X8_512:
+ XD_PAGE_512(xd_card);
+ xd_card->addr_cycle = 3;
+ xd_card->zone_cnt = 2;
+ xd_card->capacity = 64000;
+ break;
+ case XD_64M_X8_512:
+ XD_PAGE_512(xd_card);
+ xd_card->addr_cycle = 4;
+ xd_card->zone_cnt = 4;
+ xd_card->capacity = 128000;
+ break;
+ case XD_128M_X8_512:
+ XD_PAGE_512(xd_card);
+ xd_card->addr_cycle = 4;
+ xd_card->zone_cnt = 8;
+ xd_card->capacity = 256000;
+ break;
+ case XD_256M_X8_512:
+ XD_PAGE_512(xd_card);
+ xd_card->addr_cycle = 4;
+ xd_card->zone_cnt = 16;
+ xd_card->capacity = 512000;
+ break;
+ case XD_512M_X8:
+ XD_PAGE_512(xd_card);
+ xd_card->addr_cycle = 4;
+ xd_card->zone_cnt = 32;
+ xd_card->capacity = 1024000;
+ break;
+ case xD_1G_X8_512:
+ XD_PAGE_512(xd_card);
+ xd_card->addr_cycle = 4;
+ xd_card->zone_cnt = 64;
+ xd_card->capacity = 2048000;
+ break;
+ case xD_2G_X8_512:
+ XD_PAGE_512(xd_card);
+ xd_card->addr_cycle = 4;
+ xd_card->zone_cnt = 128;
+ xd_card->capacity = 4096000;
+ break;
+ default:
+ continue;
+ }
+
+ /* Confirm timing setting */
+ for (j = 0; j < 10; j++) {
+ retval = xd_read_id(chip, READ_ID, id_buf, 4);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ if (id_buf[1] != xd_card->device_code)
+ break;
+ }
+
+ if (j == 10)
+ break;
+ }
+
+ if (i == 4) {
+ xd_card->block_shift = 0;
+ xd_card->page_off = 0;
+ xd_card->addr_cycle = 0;
+ xd_card->capacity = 0;
+
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ retval = xd_read_id(chip, READ_xD_ID, id_buf, 4);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+ dev_dbg(rtsx_dev(chip), "READ_xD_ID: 0x%x 0x%x 0x%x 0x%x\n",
+ id_buf[0], id_buf[1], id_buf[2], id_buf[3]);
+ if (id_buf[2] != XD_ID_CODE) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ /* Search CIS block */
+ for (i = 0; i < 24; i++) {
+ u32 page_addr;
+
+ if (detect_card_cd(chip, XD_CARD) != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ page_addr = (u32)i << xd_card->block_shift;
+
+ for (j = 0; j < 3; j++) {
+ retval = xd_read_redundant(chip, page_addr, redunt, 11);
+ if (retval == STATUS_SUCCESS)
+ break;
+ }
+ if (j == 3)
+ continue;
+
+ if (redunt[BLOCK_STATUS] != XD_GBLK)
+ continue;
+
+ j = 0;
+ if (redunt[PAGE_STATUS] != XD_GPG) {
+ for (j = 1; j <= 8; j++) {
+ retval = xd_read_redundant(chip, page_addr + j,
+ redunt, 11);
+ if (retval == STATUS_SUCCESS) {
+ if (redunt[PAGE_STATUS] == XD_GPG)
+ break;
+ }
+ }
+
+ if (j == 9)
+ break;
+ }
+
+ /* Check CIS data */
+ if ((redunt[BLOCK_STATUS] == XD_GBLK) &&
+ (redunt[PARITY] & XD_BA1_ALL0)) {
+ u8 buf[10];
+
+ page_addr += j;
+
+ retval = xd_read_cis(chip, page_addr, buf, 10);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ if ((buf[0] == 0x01) && (buf[1] == 0x03) &&
+ (buf[2] == 0xD9)
+ && (buf[3] == 0x01) && (buf[4] == 0xFF)
+ && (buf[5] == 0x18) && (buf[6] == 0x02)
+ && (buf[7] == 0xDF) && (buf[8] == 0x01)
+ && (buf[9] == 0x20)) {
+ xd_card->cis_block = (u16)i;
+ }
+ }
+
+ break;
+ }
+
+ dev_dbg(rtsx_dev(chip), "CIS block: 0x%x\n", xd_card->cis_block);
+ if (xd_card->cis_block == 0xFFFF) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ chip->capacity[chip->card2lun[XD_CARD]] = xd_card->capacity;
+
+ return STATUS_SUCCESS;
+}
+
+static int xd_check_data_blank(u8 *redunt)
+{
+ int i;
+
+ for (i = 0; i < 6; i++) {
+ if (redunt[PAGE_STATUS + i] != 0xFF)
+ return 0;
+ }
+
+ if ((redunt[PARITY] & (XD_ECC1_ALL1 | XD_ECC2_ALL1))
+ != (XD_ECC1_ALL1 | XD_ECC2_ALL1))
+ return 0;
+
+
+ for (i = 0; i < 4; i++) {
+ if (redunt[RESERVED0 + i] != 0xFF)
+ return 0;
+ }
+
+ return 1;
+}
+
+static u16 xd_load_log_block_addr(u8 *redunt)
+{
+ u16 addr = 0xFFFF;
+
+ if (redunt[PARITY] & XD_BA1_BA2_EQL)
+ addr = ((u16)redunt[BLOCK_ADDR1_H] << 8) |
+ redunt[BLOCK_ADDR1_L];
+ else if (redunt[PARITY] & XD_BA1_VALID)
+ addr = ((u16)redunt[BLOCK_ADDR1_H] << 8) |
+ redunt[BLOCK_ADDR1_L];
+ else if (redunt[PARITY] & XD_BA2_VALID)
+ addr = ((u16)redunt[BLOCK_ADDR2_H] << 8) |
+ redunt[BLOCK_ADDR2_L];
+
+ return addr;
+}
+
+static int xd_init_l2p_tbl(struct rtsx_chip *chip)
+{
+ struct xd_info *xd_card = &(chip->xd_card);
+ int size, i;
+
+ dev_dbg(rtsx_dev(chip), "xd_init_l2p_tbl: zone_cnt = %d\n",
+ xd_card->zone_cnt);
+
+ if (xd_card->zone_cnt < 1) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ size = xd_card->zone_cnt * sizeof(struct zone_entry);
+ dev_dbg(rtsx_dev(chip), "Buffer size for l2p table is %d\n", size);
+
+ xd_card->zone = vmalloc(size);
+ if (!xd_card->zone) {
+ rtsx_trace(chip);
+ return STATUS_ERROR;
+ }
+
+ for (i = 0; i < xd_card->zone_cnt; i++) {
+ xd_card->zone[i].build_flag = 0;
+ xd_card->zone[i].l2p_table = NULL;
+ xd_card->zone[i].free_table = NULL;
+ xd_card->zone[i].get_index = 0;
+ xd_card->zone[i].set_index = 0;
+ xd_card->zone[i].unused_blk_cnt = 0;
+ }
+
+ return STATUS_SUCCESS;
+}
+
+static inline void free_zone(struct zone_entry *zone)
+{
+ if (!zone)
+ return;
+
+ zone->build_flag = 0;
+ zone->set_index = 0;
+ zone->get_index = 0;
+ zone->unused_blk_cnt = 0;
+ if (zone->l2p_table) {
+ vfree(zone->l2p_table);
+ zone->l2p_table = NULL;
+ }
+ if (zone->free_table) {
+ vfree(zone->free_table);
+ zone->free_table = NULL;
+ }
+}
+
+static void xd_set_unused_block(struct rtsx_chip *chip, u32 phy_blk)
+{
+ struct xd_info *xd_card = &(chip->xd_card);
+ struct zone_entry *zone;
+ int zone_no;
+
+ zone_no = (int)phy_blk >> 10;
+ if (zone_no >= xd_card->zone_cnt) {
+ dev_dbg(rtsx_dev(chip), "Set unused block to invalid zone (zone_no = %d, zone_cnt = %d)\n",
+ zone_no, xd_card->zone_cnt);
+ return;
+ }
+ zone = &(xd_card->zone[zone_no]);
+
+ if (zone->free_table == NULL) {
+ if (xd_build_l2p_tbl(chip, zone_no) != STATUS_SUCCESS)
+ return;
+ }
+
+ if ((zone->set_index >= XD_FREE_TABLE_CNT)
+ || (zone->set_index < 0)) {
+ free_zone(zone);
+ dev_dbg(rtsx_dev(chip), "Set unused block fail, invalid set_index\n");
+ return;
+ }
+
+ dev_dbg(rtsx_dev(chip), "Set unused block to index %d\n",
+ zone->set_index);
+
+ zone->free_table[zone->set_index++] = (u16) (phy_blk & 0x3ff);
+ if (zone->set_index >= XD_FREE_TABLE_CNT)
+ zone->set_index = 0;
+ zone->unused_blk_cnt++;
+}
+
+static u32 xd_get_unused_block(struct rtsx_chip *chip, int zone_no)
+{
+ struct xd_info *xd_card = &(chip->xd_card);
+ struct zone_entry *zone;
+ u32 phy_blk;
+
+ if (zone_no >= xd_card->zone_cnt) {
+ dev_dbg(rtsx_dev(chip), "Get unused block from invalid zone (zone_no = %d, zone_cnt = %d)\n",
+ zone_no, xd_card->zone_cnt);
+ return BLK_NOT_FOUND;
+ }
+ zone = &(xd_card->zone[zone_no]);
+
+ if ((zone->unused_blk_cnt == 0) ||
+ (zone->set_index == zone->get_index)) {
+ free_zone(zone);
+ dev_dbg(rtsx_dev(chip), "Get unused block fail, no unused block available\n");
+ return BLK_NOT_FOUND;
+ }
+ if ((zone->get_index >= XD_FREE_TABLE_CNT) || (zone->get_index < 0)) {
+ free_zone(zone);
+ dev_dbg(rtsx_dev(chip), "Get unused block fail, invalid get_index\n");
+ return BLK_NOT_FOUND;
+ }
+
+ dev_dbg(rtsx_dev(chip), "Get unused block from index %d\n",
+ zone->get_index);
+
+ phy_blk = zone->free_table[zone->get_index];
+ zone->free_table[zone->get_index++] = 0xFFFF;
+ if (zone->get_index >= XD_FREE_TABLE_CNT)
+ zone->get_index = 0;
+ zone->unused_blk_cnt--;
+
+ phy_blk += ((u32)(zone_no) << 10);
+ return phy_blk;
+}
+
+static void xd_set_l2p_tbl(struct rtsx_chip *chip,
+ int zone_no, u16 log_off, u16 phy_off)
+{
+ struct xd_info *xd_card = &(chip->xd_card);
+ struct zone_entry *zone;
+
+ zone = &(xd_card->zone[zone_no]);
+ zone->l2p_table[log_off] = phy_off;
+}
+
+static u32 xd_get_l2p_tbl(struct rtsx_chip *chip, int zone_no, u16 log_off)
+{
+ struct xd_info *xd_card = &(chip->xd_card);
+ struct zone_entry *zone;
+ int retval;
+
+ zone = &(xd_card->zone[zone_no]);
+ if (zone->l2p_table[log_off] == 0xFFFF) {
+ u32 phy_blk = 0;
+ int i;
+
+#ifdef XD_DELAY_WRITE
+ retval = xd_delay_write(chip);
+ if (retval != STATUS_SUCCESS) {
+ dev_dbg(rtsx_dev(chip), "In xd_get_l2p_tbl, delay write fail!\n");
+ return BLK_NOT_FOUND;
+ }
+#endif
+
+ if (zone->unused_blk_cnt <= 0) {
+ dev_dbg(rtsx_dev(chip), "No unused block!\n");
+ return BLK_NOT_FOUND;
+ }
+
+ for (i = 0; i < zone->unused_blk_cnt; i++) {
+ phy_blk = xd_get_unused_block(chip, zone_no);
+ if (phy_blk == BLK_NOT_FOUND) {
+ dev_dbg(rtsx_dev(chip), "No unused block available!\n");
+ return BLK_NOT_FOUND;
+ }
+
+ retval = xd_init_page(chip, phy_blk, log_off,
+ 0, xd_card->page_off + 1);
+ if (retval == STATUS_SUCCESS)
+ break;
+ }
+ if (i >= zone->unused_blk_cnt) {
+ dev_dbg(rtsx_dev(chip), "No good unused block available!\n");
+ return BLK_NOT_FOUND;
+ }
+
+ xd_set_l2p_tbl(chip, zone_no, log_off, (u16)(phy_blk & 0x3FF));
+ return phy_blk;
+ }
+
+ return (u32)zone->l2p_table[log_off] + ((u32)(zone_no) << 10);
+}
+
+int reset_xd_card(struct rtsx_chip *chip)
+{
+ struct xd_info *xd_card = &(chip->xd_card);
+ int retval;
+
+ memset(xd_card, 0, sizeof(struct xd_info));
+
+ xd_card->block_shift = 0;
+ xd_card->page_off = 0;
+ xd_card->addr_cycle = 0;
+ xd_card->capacity = 0;
+ xd_card->zone_cnt = 0;
+ xd_card->cis_block = 0xFFFF;
+ xd_card->delay_write.delay_write_flag = 0;
+
+ retval = enable_card_clock(chip, XD_CARD);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ retval = reset_xd(chip);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ retval = xd_init_l2p_tbl(chip);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ return STATUS_SUCCESS;
+}
+
+static int xd_mark_bad_block(struct rtsx_chip *chip, u32 phy_blk)
+{
+ struct xd_info *xd_card = &(chip->xd_card);
+ int retval;
+ u32 page_addr;
+ u8 reg = 0;
+
+ dev_dbg(rtsx_dev(chip), "mark block 0x%x as bad block\n", phy_blk);
+
+ if (phy_blk == BLK_NOT_FOUND) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ rtsx_init_cmd(chip);
+
+ rtsx_add_cmd(chip, WRITE_REG_CMD, XD_PAGE_STATUS, 0xFF, XD_GPG);
+ rtsx_add_cmd(chip, WRITE_REG_CMD, XD_BLOCK_STATUS, 0xFF, XD_LATER_BBLK);
+ rtsx_add_cmd(chip, WRITE_REG_CMD, XD_BLOCK_ADDR1_H, 0xFF, 0xFF);
+ rtsx_add_cmd(chip, WRITE_REG_CMD, XD_BLOCK_ADDR1_L, 0xFF, 0xFF);
+ rtsx_add_cmd(chip, WRITE_REG_CMD, XD_BLOCK_ADDR2_H, 0xFF, 0xFF);
+ rtsx_add_cmd(chip, WRITE_REG_CMD, XD_BLOCK_ADDR2_L, 0xFF, 0xFF);
+ rtsx_add_cmd(chip, WRITE_REG_CMD, XD_RESERVED0, 0xFF, 0xFF);
+ rtsx_add_cmd(chip, WRITE_REG_CMD, XD_RESERVED1, 0xFF, 0xFF);
+ rtsx_add_cmd(chip, WRITE_REG_CMD, XD_RESERVED2, 0xFF, 0xFF);
+ rtsx_add_cmd(chip, WRITE_REG_CMD, XD_RESERVED3, 0xFF, 0xFF);
+
+ page_addr = phy_blk << xd_card->block_shift;
+
+ xd_assign_phy_addr(chip, page_addr, XD_RW_ADDR);
+
+ rtsx_add_cmd(chip, WRITE_REG_CMD, XD_PAGE_CNT, 0xFF,
+ xd_card->page_off + 1);
+
+ rtsx_add_cmd(chip, WRITE_REG_CMD, XD_TRANSFER, 0xFF,
+ XD_TRANSFER_START | XD_WRITE_REDUNDANT);
+ rtsx_add_cmd(chip, CHECK_REG_CMD, XD_TRANSFER,
+ XD_TRANSFER_END, XD_TRANSFER_END);
+
+ retval = rtsx_send_cmd(chip, XD_CARD, 500);
+ if (retval < 0) {
+ rtsx_clear_xd_error(chip);
+ rtsx_read_register(chip, XD_DAT, &reg);
+ if (reg & PROGRAM_ERROR)
+ xd_set_err_code(chip, XD_PRG_ERROR);
+ else
+ xd_set_err_code(chip, XD_TO_ERROR);
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ return STATUS_SUCCESS;
+}
+
+static int xd_init_page(struct rtsx_chip *chip, u32 phy_blk,
+ u16 logoff, u8 start_page, u8 end_page)
+{
+ struct xd_info *xd_card = &(chip->xd_card);
+ int retval;
+ u32 page_addr;
+ u8 reg = 0;
+
+ dev_dbg(rtsx_dev(chip), "Init block 0x%x\n", phy_blk);
+
+ if (start_page > end_page) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+ if (phy_blk == BLK_NOT_FOUND) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ rtsx_init_cmd(chip);
+
+ rtsx_add_cmd(chip, WRITE_REG_CMD, XD_PAGE_STATUS, 0xFF, 0xFF);
+ rtsx_add_cmd(chip, WRITE_REG_CMD, XD_BLOCK_STATUS, 0xFF, 0xFF);
+ rtsx_add_cmd(chip, WRITE_REG_CMD, XD_BLOCK_ADDR1_H,
+ 0xFF, (u8)(logoff >> 8));
+ rtsx_add_cmd(chip, WRITE_REG_CMD, XD_BLOCK_ADDR1_L, 0xFF, (u8)logoff);
+
+ page_addr = (phy_blk << xd_card->block_shift) + start_page;
+
+ xd_assign_phy_addr(chip, page_addr, XD_RW_ADDR);
+
+ rtsx_add_cmd(chip, WRITE_REG_CMD, XD_CFG,
+ XD_BA_TRANSFORM, XD_BA_TRANSFORM);
+
+ rtsx_add_cmd(chip, WRITE_REG_CMD, XD_PAGE_CNT,
+ 0xFF, (end_page - start_page));
+
+ rtsx_add_cmd(chip, WRITE_REG_CMD, XD_TRANSFER,
+ 0xFF, XD_TRANSFER_START | XD_WRITE_REDUNDANT);
+ rtsx_add_cmd(chip, CHECK_REG_CMD, XD_TRANSFER,
+ XD_TRANSFER_END, XD_TRANSFER_END);
+
+ retval = rtsx_send_cmd(chip, XD_CARD, 500);
+ if (retval < 0) {
+ rtsx_clear_xd_error(chip);
+ rtsx_read_register(chip, XD_DAT, &reg);
+ if (reg & PROGRAM_ERROR) {
+ xd_mark_bad_block(chip, phy_blk);
+ xd_set_err_code(chip, XD_PRG_ERROR);
+ } else {
+ xd_set_err_code(chip, XD_TO_ERROR);
+ }
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ return STATUS_SUCCESS;
+}
+
+static int xd_copy_page(struct rtsx_chip *chip, u32 old_blk, u32 new_blk,
+ u8 start_page, u8 end_page)
+{
+ struct xd_info *xd_card = &(chip->xd_card);
+ u32 old_page, new_page;
+ u8 i, reg = 0;
+ int retval;
+
+ dev_dbg(rtsx_dev(chip), "Copy page from block 0x%x to block 0x%x\n",
+ old_blk, new_blk);
+
+ if (start_page > end_page) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ if ((old_blk == BLK_NOT_FOUND) || (new_blk == BLK_NOT_FOUND)) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ old_page = (old_blk << xd_card->block_shift) + start_page;
+ new_page = (new_blk << xd_card->block_shift) + start_page;
+
+ XD_CLR_BAD_NEWBLK(xd_card);
+
+ retval = rtsx_write_register(chip, CARD_DATA_SOURCE, 0x01,
+ PINGPONG_BUFFER);
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+
+ for (i = start_page; i < end_page; i++) {
+ if (detect_card_cd(chip, XD_CARD) != STATUS_SUCCESS) {
+ rtsx_clear_xd_error(chip);
+ xd_set_err_code(chip, XD_NO_CARD);
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ rtsx_init_cmd(chip);
+
+ xd_assign_phy_addr(chip, old_page, XD_RW_ADDR);
+
+ rtsx_add_cmd(chip, WRITE_REG_CMD, XD_PAGE_CNT, 0xFF, 1);
+ rtsx_add_cmd(chip, WRITE_REG_CMD, XD_CHK_DATA_STATUS,
+ XD_AUTO_CHK_DATA_STATUS, 0);
+ rtsx_add_cmd(chip, WRITE_REG_CMD, XD_TRANSFER, 0xFF,
+ XD_TRANSFER_START | XD_READ_PAGES);
+ rtsx_add_cmd(chip, CHECK_REG_CMD, XD_TRANSFER,
+ XD_TRANSFER_END, XD_TRANSFER_END);
+
+ retval = rtsx_send_cmd(chip, XD_CARD, 500);
+ if (retval < 0) {
+ rtsx_clear_xd_error(chip);
+ reg = 0;
+ rtsx_read_register(chip, XD_CTL, &reg);
+ if (reg & (XD_ECC1_ERROR | XD_ECC2_ERROR)) {
+ wait_timeout(100);
+
+ if (detect_card_cd(chip,
+ XD_CARD) != STATUS_SUCCESS) {
+ xd_set_err_code(chip, XD_NO_CARD);
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ if (((reg & (XD_ECC1_ERROR | XD_ECC1_UNCORRECTABLE)) ==
+ (XD_ECC1_ERROR | XD_ECC1_UNCORRECTABLE))
+ || ((reg & (XD_ECC2_ERROR | XD_ECC2_UNCORRECTABLE)) ==
+ (XD_ECC2_ERROR | XD_ECC2_UNCORRECTABLE))) {
+ rtsx_write_register(chip,
+ XD_PAGE_STATUS, 0xFF,
+ XD_BPG);
+ rtsx_write_register(chip,
+ XD_BLOCK_STATUS, 0xFF,
+ XD_GBLK);
+ XD_SET_BAD_OLDBLK(xd_card);
+ dev_dbg(rtsx_dev(chip), "old block 0x%x ecc error\n",
+ old_blk);
+ }
+ } else {
+ xd_set_err_code(chip, XD_TO_ERROR);
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+ }
+
+ if (XD_CHK_BAD_OLDBLK(xd_card))
+ rtsx_clear_xd_error(chip);
+
+ rtsx_init_cmd(chip);
+
+ xd_assign_phy_addr(chip, new_page, XD_RW_ADDR);
+ rtsx_add_cmd(chip, WRITE_REG_CMD, XD_PAGE_CNT, 0xFF, 1);
+ rtsx_add_cmd(chip, WRITE_REG_CMD, XD_TRANSFER, 0xFF,
+ XD_TRANSFER_START | XD_WRITE_PAGES);
+ rtsx_add_cmd(chip, CHECK_REG_CMD, XD_TRANSFER,
+ XD_TRANSFER_END, XD_TRANSFER_END);
+
+ retval = rtsx_send_cmd(chip, XD_CARD, 300);
+ if (retval < 0) {
+ rtsx_clear_xd_error(chip);
+ reg = 0;
+ rtsx_read_register(chip, XD_DAT, &reg);
+ if (reg & PROGRAM_ERROR) {
+ xd_mark_bad_block(chip, new_blk);
+ xd_set_err_code(chip, XD_PRG_ERROR);
+ XD_SET_BAD_NEWBLK(xd_card);
+ } else {
+ xd_set_err_code(chip, XD_TO_ERROR);
+ }
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ old_page++;
+ new_page++;
+ }
+
+ return STATUS_SUCCESS;
+}
+
+static int xd_reset_cmd(struct rtsx_chip *chip)
+{
+ int retval;
+ u8 *ptr;
+
+ rtsx_init_cmd(chip);
+
+ rtsx_add_cmd(chip, WRITE_REG_CMD, XD_TRANSFER,
+ 0xFF, XD_TRANSFER_START | XD_RESET);
+ rtsx_add_cmd(chip, CHECK_REG_CMD, XD_TRANSFER,
+ XD_TRANSFER_END, XD_TRANSFER_END);
+ rtsx_add_cmd(chip, READ_REG_CMD, XD_DAT, 0, 0);
+ rtsx_add_cmd(chip, READ_REG_CMD, XD_CTL, 0, 0);
+
+ retval = rtsx_send_cmd(chip, XD_CARD, 100);
+ if (retval < 0) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ ptr = rtsx_get_cmd_data(chip) + 1;
+ if (((ptr[0] & READY_FLAG) == READY_STATE) && (ptr[1] & XD_RDY))
+ return STATUS_SUCCESS;
+
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+}
+
+static int xd_erase_block(struct rtsx_chip *chip, u32 phy_blk)
+{
+ struct xd_info *xd_card = &(chip->xd_card);
+ u32 page_addr;
+ u8 reg = 0, *ptr;
+ int i, retval;
+
+ if (phy_blk == BLK_NOT_FOUND) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ page_addr = phy_blk << xd_card->block_shift;
+
+ for (i = 0; i < 3; i++) {
+ rtsx_init_cmd(chip);
+
+ xd_assign_phy_addr(chip, page_addr, XD_ERASE_ADDR);
+
+ rtsx_add_cmd(chip, WRITE_REG_CMD, XD_TRANSFER, 0xFF,
+ XD_TRANSFER_START | XD_ERASE);
+ rtsx_add_cmd(chip, CHECK_REG_CMD, XD_TRANSFER,
+ XD_TRANSFER_END, XD_TRANSFER_END);
+ rtsx_add_cmd(chip, READ_REG_CMD, XD_DAT, 0, 0);
+
+ retval = rtsx_send_cmd(chip, XD_CARD, 250);
+ if (retval < 0) {
+ rtsx_clear_xd_error(chip);
+ rtsx_read_register(chip, XD_DAT, &reg);
+ if (reg & PROGRAM_ERROR) {
+ xd_mark_bad_block(chip, phy_blk);
+ xd_set_err_code(chip, XD_PRG_ERROR);
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ } else {
+ xd_set_err_code(chip, XD_ERASE_FAIL);
+ }
+ retval = xd_reset_cmd(chip);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+ continue;
+ }
+
+ ptr = rtsx_get_cmd_data(chip) + 1;
+ if (*ptr & PROGRAM_ERROR) {
+ xd_mark_bad_block(chip, phy_blk);
+ xd_set_err_code(chip, XD_PRG_ERROR);
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ return STATUS_SUCCESS;
+ }
+
+ xd_mark_bad_block(chip, phy_blk);
+ xd_set_err_code(chip, XD_ERASE_FAIL);
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+}
+
+
+static int xd_build_l2p_tbl(struct rtsx_chip *chip, int zone_no)
+{
+ struct xd_info *xd_card = &(chip->xd_card);
+ struct zone_entry *zone;
+ int retval;
+ u32 start, end, i;
+ u16 max_logoff, cur_fst_page_logoff;
+ u16 cur_lst_page_logoff, ent_lst_page_logoff;
+ u8 redunt[11];
+
+ dev_dbg(rtsx_dev(chip), "xd_build_l2p_tbl: %d\n", zone_no);
+
+ if (xd_card->zone == NULL) {
+ retval = xd_init_l2p_tbl(chip);
+ if (retval != STATUS_SUCCESS)
+ return retval;
+ }
+
+ if (xd_card->zone[zone_no].build_flag) {
+ dev_dbg(rtsx_dev(chip), "l2p table of zone %d has been built\n",
+ zone_no);
+ return STATUS_SUCCESS;
+ }
+
+ zone = &(xd_card->zone[zone_no]);
+
+ if (zone->l2p_table == NULL) {
+ zone->l2p_table = vmalloc(2000);
+ if (zone->l2p_table == NULL) {
+ rtsx_trace(chip);
+ goto Build_Fail;
+ }
+ }
+ memset((u8 *)(zone->l2p_table), 0xff, 2000);
+
+ if (zone->free_table == NULL) {
+ zone->free_table = vmalloc(XD_FREE_TABLE_CNT * 2);
+ if (zone->free_table == NULL) {
+ rtsx_trace(chip);
+ goto Build_Fail;
+ }
+ }
+ memset((u8 *)(zone->free_table), 0xff, XD_FREE_TABLE_CNT * 2);
+
+ if (zone_no == 0) {
+ if (xd_card->cis_block == 0xFFFF)
+ start = 0;
+ else
+ start = xd_card->cis_block + 1;
+ if (XD_CHK_4MB(xd_card)) {
+ end = 0x200;
+ max_logoff = 499;
+ } else {
+ end = 0x400;
+ max_logoff = 999;
+ }
+ } else {
+ start = (u32)(zone_no) << 10;
+ end = (u32)(zone_no + 1) << 10;
+ max_logoff = 999;
+ }
+
+ dev_dbg(rtsx_dev(chip), "start block 0x%x, end block 0x%x\n",
+ start, end);
+
+ zone->set_index = zone->get_index = 0;
+ zone->unused_blk_cnt = 0;
+
+ for (i = start; i < end; i++) {
+ u32 page_addr = i << xd_card->block_shift;
+ u32 phy_block;
+
+ retval = xd_read_redundant(chip, page_addr, redunt, 11);
+ if (retval != STATUS_SUCCESS)
+ continue;
+
+ if (redunt[BLOCK_STATUS] != 0xFF) {
+ dev_dbg(rtsx_dev(chip), "bad block\n");
+ continue;
+ }
+
+ if (xd_check_data_blank(redunt)) {
+ dev_dbg(rtsx_dev(chip), "blank block\n");
+ xd_set_unused_block(chip, i);
+ continue;
+ }
+
+ cur_fst_page_logoff = xd_load_log_block_addr(redunt);
+ if ((cur_fst_page_logoff == 0xFFFF) ||
+ (cur_fst_page_logoff > max_logoff)) {
+ retval = xd_erase_block(chip, i);
+ if (retval == STATUS_SUCCESS)
+ xd_set_unused_block(chip, i);
+ continue;
+ }
+
+ if ((zone_no == 0) && (cur_fst_page_logoff == 0) &&
+ (redunt[PAGE_STATUS] != XD_GPG))
+ XD_SET_MBR_FAIL(xd_card);
+
+ if (zone->l2p_table[cur_fst_page_logoff] == 0xFFFF) {
+ zone->l2p_table[cur_fst_page_logoff] = (u16)(i & 0x3FF);
+ continue;
+ }
+
+ phy_block = zone->l2p_table[cur_fst_page_logoff] +
+ ((u32)((zone_no) << 10));
+
+ page_addr = ((i + 1) << xd_card->block_shift) - 1;
+
+ retval = xd_read_redundant(chip, page_addr, redunt, 11);
+ if (retval != STATUS_SUCCESS)
+ continue;
+
+ cur_lst_page_logoff = xd_load_log_block_addr(redunt);
+ if (cur_lst_page_logoff == cur_fst_page_logoff) {
+ int m;
+
+ page_addr = ((phy_block + 1) <<
+ xd_card->block_shift) - 1;
+
+ for (m = 0; m < 3; m++) {
+ retval = xd_read_redundant(chip, page_addr,
+ redunt, 11);
+ if (retval == STATUS_SUCCESS)
+ break;
+ }
+
+ if (m == 3) {
+ zone->l2p_table[cur_fst_page_logoff] =
+ (u16)(i & 0x3FF);
+ retval = xd_erase_block(chip, phy_block);
+ if (retval == STATUS_SUCCESS)
+ xd_set_unused_block(chip, phy_block);
+ continue;
+ }
+
+ ent_lst_page_logoff = xd_load_log_block_addr(redunt);
+ if (ent_lst_page_logoff != cur_fst_page_logoff) {
+ zone->l2p_table[cur_fst_page_logoff] =
+ (u16)(i & 0x3FF);
+ retval = xd_erase_block(chip, phy_block);
+ if (retval == STATUS_SUCCESS)
+ xd_set_unused_block(chip, phy_block);
+ continue;
+ } else {
+ retval = xd_erase_block(chip, i);
+ if (retval == STATUS_SUCCESS)
+ xd_set_unused_block(chip, i);
+ }
+ } else {
+ retval = xd_erase_block(chip, i);
+ if (retval == STATUS_SUCCESS)
+ xd_set_unused_block(chip, i);
+ }
+ }
+
+ if (XD_CHK_4MB(xd_card))
+ end = 500;
+ else
+ end = 1000;
+
+ i = 0;
+ for (start = 0; start < end; start++) {
+ if (zone->l2p_table[start] == 0xFFFF)
+ i++;
+ }
+
+ dev_dbg(rtsx_dev(chip), "Block count %d, invalid L2P entry %d\n",
+ end, i);
+ dev_dbg(rtsx_dev(chip), "Total unused block: %d\n",
+ zone->unused_blk_cnt);
+
+ if ((zone->unused_blk_cnt - i) < 1)
+ chip->card_wp |= XD_CARD;
+
+ zone->build_flag = 1;
+
+ return STATUS_SUCCESS;
+
+Build_Fail:
+ if (zone->l2p_table) {
+ vfree(zone->l2p_table);
+ zone->l2p_table = NULL;
+ }
+ if (zone->free_table) {
+ vfree(zone->free_table);
+ zone->free_table = NULL;
+ }
+
+ return STATUS_FAIL;
+}
+
+static int xd_send_cmd(struct rtsx_chip *chip, u8 cmd)
+{
+ int retval;
+
+ rtsx_init_cmd(chip);
+
+ rtsx_add_cmd(chip, WRITE_REG_CMD, XD_DAT, 0xFF, cmd);
+ rtsx_add_cmd(chip, WRITE_REG_CMD, XD_TRANSFER, 0xFF,
+ XD_TRANSFER_START | XD_SET_CMD);
+ rtsx_add_cmd(chip, CHECK_REG_CMD, XD_TRANSFER,
+ XD_TRANSFER_END, XD_TRANSFER_END);
+
+ retval = rtsx_send_cmd(chip, XD_CARD, 200);
+ if (retval < 0) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ return STATUS_SUCCESS;
+}
+
+static int xd_read_multiple_pages(struct rtsx_chip *chip, u32 phy_blk,
+ u32 log_blk, u8 start_page, u8 end_page,
+ u8 *buf, unsigned int *index,
+ unsigned int *offset)
+{
+ struct xd_info *xd_card = &(chip->xd_card);
+ u32 page_addr, new_blk;
+ u16 log_off;
+ u8 reg_val, page_cnt;
+ int zone_no, retval, i;
+
+ if (start_page > end_page) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ page_cnt = end_page - start_page;
+ zone_no = (int)(log_blk / 1000);
+ log_off = (u16)(log_blk % 1000);
+
+ if ((phy_blk & 0x3FF) == 0x3FF) {
+ for (i = 0; i < 256; i++) {
+ page_addr = ((u32)i) << xd_card->block_shift;
+
+ retval = xd_read_redundant(chip, page_addr, NULL, 0);
+ if (retval == STATUS_SUCCESS)
+ break;
+
+ if (detect_card_cd(chip, XD_CARD) != STATUS_SUCCESS) {
+ xd_set_err_code(chip, XD_NO_CARD);
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+ }
+ }
+
+ page_addr = (phy_blk << xd_card->block_shift) + start_page;
+
+ rtsx_init_cmd(chip);
+
+ xd_assign_phy_addr(chip, page_addr, XD_RW_ADDR);
+ rtsx_add_cmd(chip, WRITE_REG_CMD, XD_CFG, XD_PPB_TO_SIE, XD_PPB_TO_SIE);
+ rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_DATA_SOURCE, 0x01, RING_BUFFER);
+ rtsx_add_cmd(chip, WRITE_REG_CMD, XD_PAGE_CNT, 0xFF, page_cnt);
+ rtsx_add_cmd(chip, WRITE_REG_CMD, XD_CHK_DATA_STATUS,
+ XD_AUTO_CHK_DATA_STATUS, XD_AUTO_CHK_DATA_STATUS);
+
+ trans_dma_enable(chip->srb->sc_data_direction, chip,
+ page_cnt * 512, DMA_512);
+
+ rtsx_add_cmd(chip, WRITE_REG_CMD, XD_TRANSFER, 0xFF,
+ XD_TRANSFER_START | XD_READ_PAGES);
+ rtsx_add_cmd(chip, CHECK_REG_CMD, XD_TRANSFER,
+ XD_TRANSFER_END | XD_PPB_EMPTY, XD_TRANSFER_END | XD_PPB_EMPTY);
+
+ rtsx_send_cmd_no_wait(chip);
+
+ retval = rtsx_transfer_data_partial(chip, XD_CARD, buf, page_cnt * 512,
+ scsi_sg_count(chip->srb),
+ index, offset, DMA_FROM_DEVICE,
+ chip->xd_timeout);
+ if (retval < 0) {
+ rtsx_clear_xd_error(chip);
+
+ if (retval == -ETIMEDOUT) {
+ xd_set_err_code(chip, XD_TO_ERROR);
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ } else {
+ rtsx_trace(chip);
+ goto Fail;
+ }
+ }
+
+ return STATUS_SUCCESS;
+
+Fail:
+ retval = rtsx_read_register(chip, XD_PAGE_STATUS, &reg_val);
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+
+ if (reg_val != XD_GPG)
+ xd_set_err_code(chip, XD_PRG_ERROR);
+
+ retval = rtsx_read_register(chip, XD_CTL, &reg_val);
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+
+ if (((reg_val & (XD_ECC1_ERROR | XD_ECC1_UNCORRECTABLE))
+ == (XD_ECC1_ERROR | XD_ECC1_UNCORRECTABLE))
+ || ((reg_val & (XD_ECC2_ERROR | XD_ECC2_UNCORRECTABLE))
+ == (XD_ECC2_ERROR | XD_ECC2_UNCORRECTABLE))) {
+ wait_timeout(100);
+
+ if (detect_card_cd(chip, XD_CARD) != STATUS_SUCCESS) {
+ xd_set_err_code(chip, XD_NO_CARD);
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ xd_set_err_code(chip, XD_ECC_ERROR);
+
+ new_blk = xd_get_unused_block(chip, zone_no);
+ if (new_blk == NO_NEW_BLK) {
+ XD_CLR_BAD_OLDBLK(xd_card);
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ retval = xd_copy_page(chip, phy_blk, new_blk, 0,
+ xd_card->page_off + 1);
+ if (retval != STATUS_SUCCESS) {
+ if (!XD_CHK_BAD_NEWBLK(xd_card)) {
+ retval = xd_erase_block(chip, new_blk);
+ if (retval == STATUS_SUCCESS)
+ xd_set_unused_block(chip, new_blk);
+ } else {
+ XD_CLR_BAD_NEWBLK(xd_card);
+ }
+ XD_CLR_BAD_OLDBLK(xd_card);
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+ xd_set_l2p_tbl(chip, zone_no, log_off, (u16)(new_blk & 0x3FF));
+ xd_erase_block(chip, phy_blk);
+ xd_mark_bad_block(chip, phy_blk);
+ XD_CLR_BAD_OLDBLK(xd_card);
+ }
+
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+}
+
+static int xd_finish_write(struct rtsx_chip *chip,
+ u32 old_blk, u32 new_blk, u32 log_blk, u8 page_off)
+{
+ struct xd_info *xd_card = &(chip->xd_card);
+ int retval, zone_no;
+ u16 log_off;
+
+ dev_dbg(rtsx_dev(chip), "xd_finish_write, old_blk = 0x%x, new_blk = 0x%x, log_blk = 0x%x\n",
+ old_blk, new_blk, log_blk);
+
+ if (page_off > xd_card->page_off) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ zone_no = (int)(log_blk / 1000);
+ log_off = (u16)(log_blk % 1000);
+
+ if (old_blk == BLK_NOT_FOUND) {
+ retval = xd_init_page(chip, new_blk, log_off,
+ page_off, xd_card->page_off + 1);
+ if (retval != STATUS_SUCCESS) {
+ retval = xd_erase_block(chip, new_blk);
+ if (retval == STATUS_SUCCESS)
+ xd_set_unused_block(chip, new_blk);
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+ } else {
+ retval = xd_copy_page(chip, old_blk, new_blk,
+ page_off, xd_card->page_off + 1);
+ if (retval != STATUS_SUCCESS) {
+ if (!XD_CHK_BAD_NEWBLK(xd_card)) {
+ retval = xd_erase_block(chip, new_blk);
+ if (retval == STATUS_SUCCESS)
+ xd_set_unused_block(chip, new_blk);
+ }
+ XD_CLR_BAD_NEWBLK(xd_card);
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ retval = xd_erase_block(chip, old_blk);
+ if (retval == STATUS_SUCCESS) {
+ if (XD_CHK_BAD_OLDBLK(xd_card)) {
+ xd_mark_bad_block(chip, old_blk);
+ XD_CLR_BAD_OLDBLK(xd_card);
+ } else {
+ xd_set_unused_block(chip, old_blk);
+ }
+ } else {
+ xd_set_err_code(chip, XD_NO_ERROR);
+ XD_CLR_BAD_OLDBLK(xd_card);
+ }
+ }
+
+ xd_set_l2p_tbl(chip, zone_no, log_off, (u16)(new_blk & 0x3FF));
+
+ return STATUS_SUCCESS;
+}
+
+static int xd_prepare_write(struct rtsx_chip *chip,
+ u32 old_blk, u32 new_blk, u32 log_blk, u8 page_off)
+{
+ int retval;
+
+ dev_dbg(rtsx_dev(chip), "%s, old_blk = 0x%x, new_blk = 0x%x, log_blk = 0x%x, page_off = %d\n",
+ __func__, old_blk, new_blk, log_blk, (int)page_off);
+
+ if (page_off) {
+ retval = xd_copy_page(chip, old_blk, new_blk, 0, page_off);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+ }
+
+ return STATUS_SUCCESS;
+}
+
+
+static int xd_write_multiple_pages(struct rtsx_chip *chip, u32 old_blk,
+ u32 new_blk, u32 log_blk, u8 start_page,
+ u8 end_page, u8 *buf, unsigned int *index,
+ unsigned int *offset)
+{
+ struct xd_info *xd_card = &(chip->xd_card);
+ u32 page_addr;
+ int zone_no, retval;
+ u16 log_off;
+ u8 page_cnt, reg_val;
+
+ dev_dbg(rtsx_dev(chip), "%s, old_blk = 0x%x, new_blk = 0x%x, log_blk = 0x%x\n",
+ __func__, old_blk, new_blk, log_blk);
+
+ if (start_page > end_page) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ page_cnt = end_page - start_page;
+ zone_no = (int)(log_blk / 1000);
+ log_off = (u16)(log_blk % 1000);
+
+ page_addr = (new_blk << xd_card->block_shift) + start_page;
+
+ retval = xd_send_cmd(chip, READ1_1);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ rtsx_init_cmd(chip);
+
+ rtsx_add_cmd(chip, WRITE_REG_CMD, XD_BLOCK_ADDR1_H,
+ 0xFF, (u8)(log_off >> 8));
+ rtsx_add_cmd(chip, WRITE_REG_CMD, XD_BLOCK_ADDR1_L, 0xFF, (u8)log_off);
+ rtsx_add_cmd(chip, WRITE_REG_CMD, XD_BLOCK_STATUS, 0xFF, XD_GBLK);
+ rtsx_add_cmd(chip, WRITE_REG_CMD, XD_PAGE_STATUS, 0xFF, XD_GPG);
+
+ xd_assign_phy_addr(chip, page_addr, XD_RW_ADDR);
+
+ rtsx_add_cmd(chip, WRITE_REG_CMD, XD_CFG, XD_BA_TRANSFORM,
+ XD_BA_TRANSFORM);
+ rtsx_add_cmd(chip, WRITE_REG_CMD, XD_PAGE_CNT, 0xFF, page_cnt);
+ rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_DATA_SOURCE, 0x01, RING_BUFFER);
+
+ trans_dma_enable(chip->srb->sc_data_direction, chip,
+ page_cnt * 512, DMA_512);
+
+ rtsx_add_cmd(chip, WRITE_REG_CMD, XD_TRANSFER,
+ 0xFF, XD_TRANSFER_START | XD_WRITE_PAGES);
+ rtsx_add_cmd(chip, CHECK_REG_CMD, XD_TRANSFER,
+ XD_TRANSFER_END, XD_TRANSFER_END);
+
+ rtsx_send_cmd_no_wait(chip);
+
+ retval = rtsx_transfer_data_partial(chip, XD_CARD, buf, page_cnt * 512,
+ scsi_sg_count(chip->srb),
+ index, offset, DMA_TO_DEVICE, chip->xd_timeout);
+ if (retval < 0) {
+ rtsx_clear_xd_error(chip);
+
+ if (retval == -ETIMEDOUT) {
+ xd_set_err_code(chip, XD_TO_ERROR);
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ } else {
+ rtsx_trace(chip);
+ goto Fail;
+ }
+ }
+
+ if (end_page == (xd_card->page_off + 1)) {
+ xd_card->delay_write.delay_write_flag = 0;
+
+ if (old_blk != BLK_NOT_FOUND) {
+ retval = xd_erase_block(chip, old_blk);
+ if (retval == STATUS_SUCCESS) {
+ if (XD_CHK_BAD_OLDBLK(xd_card)) {
+ xd_mark_bad_block(chip, old_blk);
+ XD_CLR_BAD_OLDBLK(xd_card);
+ } else {
+ xd_set_unused_block(chip, old_blk);
+ }
+ } else {
+ xd_set_err_code(chip, XD_NO_ERROR);
+ XD_CLR_BAD_OLDBLK(xd_card);
+ }
+ }
+ xd_set_l2p_tbl(chip, zone_no, log_off, (u16)(new_blk & 0x3FF));
+ }
+
+ return STATUS_SUCCESS;
+
+Fail:
+ retval = rtsx_read_register(chip, XD_DAT, &reg_val);
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+ if (reg_val & PROGRAM_ERROR) {
+ xd_set_err_code(chip, XD_PRG_ERROR);
+ xd_mark_bad_block(chip, new_blk);
+ }
+
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+}
+
+#ifdef XD_DELAY_WRITE
+int xd_delay_write(struct rtsx_chip *chip)
+{
+ struct xd_info *xd_card = &(chip->xd_card);
+ struct xd_delay_write_tag *delay_write = &(xd_card->delay_write);
+ int retval;
+
+ if (delay_write->delay_write_flag) {
+ dev_dbg(rtsx_dev(chip), "xd_delay_write\n");
+ retval = xd_switch_clock(chip);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ delay_write->delay_write_flag = 0;
+ retval = xd_finish_write(chip,
+ delay_write->old_phyblock,
+ delay_write->new_phyblock,
+ delay_write->logblock, delay_write->pageoff);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+ }
+
+ return STATUS_SUCCESS;
+}
+#endif
+
+int xd_rw(struct scsi_cmnd *srb, struct rtsx_chip *chip,
+ u32 start_sector, u16 sector_cnt)
+{
+ struct xd_info *xd_card = &(chip->xd_card);
+ unsigned int lun = SCSI_LUN(srb);
+#ifdef XD_DELAY_WRITE
+ struct xd_delay_write_tag *delay_write = &(xd_card->delay_write);
+#endif
+ int retval, zone_no;
+ unsigned int index = 0, offset = 0;
+ u32 log_blk, old_blk = 0, new_blk = 0;
+ u16 log_off, total_sec_cnt = sector_cnt;
+ u8 start_page, end_page = 0, page_cnt;
+ u8 *ptr;
+
+ xd_set_err_code(chip, XD_NO_ERROR);
+
+ xd_card->cleanup_counter = 0;
+
+ dev_dbg(rtsx_dev(chip), "xd_rw: scsi_sg_count = %d\n",
+ scsi_sg_count(srb));
+
+ ptr = (u8 *)scsi_sglist(srb);
+
+ retval = xd_switch_clock(chip);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+
+ if (detect_card_cd(chip, XD_CARD) != STATUS_SUCCESS) {
+ chip->card_fail |= XD_CARD;
+ set_sense_type(chip, lun, SENSE_TYPE_MEDIA_NOT_PRESENT);
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ log_blk = start_sector >> xd_card->block_shift;
+ start_page = (u8)start_sector & xd_card->page_off;
+ zone_no = (int)(log_blk / 1000);
+ log_off = (u16)(log_blk % 1000);
+
+ if (xd_card->zone[zone_no].build_flag == 0) {
+ retval = xd_build_l2p_tbl(chip, zone_no);
+ if (retval != STATUS_SUCCESS) {
+ chip->card_fail |= XD_CARD;
+ set_sense_type(chip, lun, SENSE_TYPE_MEDIA_NOT_PRESENT);
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+ }
+
+ if (srb->sc_data_direction == DMA_TO_DEVICE) {
+#ifdef XD_DELAY_WRITE
+ if (delay_write->delay_write_flag &&
+ (delay_write->logblock == log_blk) &&
+ (start_page > delay_write->pageoff)) {
+ delay_write->delay_write_flag = 0;
+ if (delay_write->old_phyblock != BLK_NOT_FOUND) {
+ retval = xd_copy_page(chip,
+ delay_write->old_phyblock,
+ delay_write->new_phyblock,
+ delay_write->pageoff, start_page);
+ if (retval != STATUS_SUCCESS) {
+ set_sense_type(chip, lun,
+ SENSE_TYPE_MEDIA_WRITE_ERR);
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+ }
+ old_blk = delay_write->old_phyblock;
+ new_blk = delay_write->new_phyblock;
+ } else if (delay_write->delay_write_flag &&
+ (delay_write->logblock == log_blk) &&
+ (start_page == delay_write->pageoff)) {
+ delay_write->delay_write_flag = 0;
+ old_blk = delay_write->old_phyblock;
+ new_blk = delay_write->new_phyblock;
+ } else {
+ retval = xd_delay_write(chip);
+ if (retval != STATUS_SUCCESS) {
+ set_sense_type(chip, lun,
+ SENSE_TYPE_MEDIA_WRITE_ERR);
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+#endif
+ old_blk = xd_get_l2p_tbl(chip, zone_no, log_off);
+ new_blk = xd_get_unused_block(chip, zone_no);
+ if ((old_blk == BLK_NOT_FOUND) ||
+ (new_blk == BLK_NOT_FOUND)) {
+ set_sense_type(chip, lun,
+ SENSE_TYPE_MEDIA_WRITE_ERR);
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ retval = xd_prepare_write(chip, old_blk, new_blk,
+ log_blk, start_page);
+ if (retval != STATUS_SUCCESS) {
+ if (detect_card_cd(chip, XD_CARD) !=
+ STATUS_SUCCESS) {
+ set_sense_type(chip, lun,
+ SENSE_TYPE_MEDIA_NOT_PRESENT);
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+ set_sense_type(chip, lun,
+ SENSE_TYPE_MEDIA_WRITE_ERR);
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+#ifdef XD_DELAY_WRITE
+ }
+#endif
+ } else {
+#ifdef XD_DELAY_WRITE
+ retval = xd_delay_write(chip);
+ if (retval != STATUS_SUCCESS) {
+ if (detect_card_cd(chip, XD_CARD) != STATUS_SUCCESS) {
+ set_sense_type(chip, lun,
+ SENSE_TYPE_MEDIA_NOT_PRESENT);
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+ set_sense_type(chip, lun,
+ SENSE_TYPE_MEDIA_UNRECOVER_READ_ERR);
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+#endif
+
+ old_blk = xd_get_l2p_tbl(chip, zone_no, log_off);
+ if (old_blk == BLK_NOT_FOUND) {
+ set_sense_type(chip, lun,
+ SENSE_TYPE_MEDIA_UNRECOVER_READ_ERR);
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+ }
+
+ dev_dbg(rtsx_dev(chip), "old_blk = 0x%x\n", old_blk);
+
+ while (total_sec_cnt) {
+ if (detect_card_cd(chip, XD_CARD) != STATUS_SUCCESS) {
+ chip->card_fail |= XD_CARD;
+ set_sense_type(chip, lun, SENSE_TYPE_MEDIA_NOT_PRESENT);
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ if ((start_page + total_sec_cnt) > (xd_card->page_off + 1))
+ end_page = xd_card->page_off + 1;
+ else
+ end_page = start_page + (u8)total_sec_cnt;
+
+ page_cnt = end_page - start_page;
+ if (srb->sc_data_direction == DMA_FROM_DEVICE) {
+ retval = xd_read_multiple_pages(chip, old_blk, log_blk,
+ start_page, end_page, ptr,
+ &index, &offset);
+ if (retval != STATUS_SUCCESS) {
+ set_sense_type(chip, lun,
+ SENSE_TYPE_MEDIA_UNRECOVER_READ_ERR);
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+ } else {
+ retval = xd_write_multiple_pages(chip, old_blk,
+ new_blk, log_blk,
+ start_page, end_page, ptr,
+ &index, &offset);
+ if (retval != STATUS_SUCCESS) {
+ set_sense_type(chip, lun,
+ SENSE_TYPE_MEDIA_WRITE_ERR);
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+ }
+
+ total_sec_cnt -= page_cnt;
+ if (scsi_sg_count(srb) == 0)
+ ptr += page_cnt * 512;
+
+ if (total_sec_cnt == 0)
+ break;
+
+ log_blk++;
+ zone_no = (int)(log_blk / 1000);
+ log_off = (u16)(log_blk % 1000);
+
+ if (xd_card->zone[zone_no].build_flag == 0) {
+ retval = xd_build_l2p_tbl(chip, zone_no);
+ if (retval != STATUS_SUCCESS) {
+ chip->card_fail |= XD_CARD;
+ set_sense_type(chip, lun,
+ SENSE_TYPE_MEDIA_NOT_PRESENT);
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+ }
+
+ old_blk = xd_get_l2p_tbl(chip, zone_no, log_off);
+ if (old_blk == BLK_NOT_FOUND) {
+ if (srb->sc_data_direction == DMA_FROM_DEVICE)
+ set_sense_type(chip, lun,
+ SENSE_TYPE_MEDIA_UNRECOVER_READ_ERR);
+ else
+ set_sense_type(chip, lun,
+ SENSE_TYPE_MEDIA_WRITE_ERR);
+
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ if (srb->sc_data_direction == DMA_TO_DEVICE) {
+ new_blk = xd_get_unused_block(chip, zone_no);
+ if (new_blk == BLK_NOT_FOUND) {
+ set_sense_type(chip, lun,
+ SENSE_TYPE_MEDIA_WRITE_ERR);
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+ }
+
+ start_page = 0;
+ }
+
+ if ((srb->sc_data_direction == DMA_TO_DEVICE) &&
+ (end_page != (xd_card->page_off + 1))) {
+#ifdef XD_DELAY_WRITE
+ delay_write->delay_write_flag = 1;
+ delay_write->old_phyblock = old_blk;
+ delay_write->new_phyblock = new_blk;
+ delay_write->logblock = log_blk;
+ delay_write->pageoff = end_page;
+#else
+ if (detect_card_cd(chip, XD_CARD) != STATUS_SUCCESS) {
+ chip->card_fail |= XD_CARD;
+ set_sense_type(chip, lun, SENSE_TYPE_MEDIA_NOT_PRESENT);
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ retval = xd_finish_write(chip, old_blk, new_blk,
+ log_blk, end_page);
+ if (retval != STATUS_SUCCESS) {
+ if (detect_card_cd(chip, XD_CARD) != STATUS_SUCCESS) {
+ set_sense_type(chip, lun,
+ SENSE_TYPE_MEDIA_NOT_PRESENT);
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+ set_sense_type(chip, lun, SENSE_TYPE_MEDIA_WRITE_ERR);
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+#endif
+ }
+
+ scsi_set_resid(srb, 0);
+
+ return STATUS_SUCCESS;
+}
+
+void xd_free_l2p_tbl(struct rtsx_chip *chip)
+{
+ struct xd_info *xd_card = &(chip->xd_card);
+ int i = 0;
+
+ if (xd_card->zone != NULL) {
+ for (i = 0; i < xd_card->zone_cnt; i++) {
+ if (xd_card->zone[i].l2p_table != NULL) {
+ vfree(xd_card->zone[i].l2p_table);
+ xd_card->zone[i].l2p_table = NULL;
+ }
+ if (xd_card->zone[i].free_table != NULL) {
+ vfree(xd_card->zone[i].free_table);
+ xd_card->zone[i].free_table = NULL;
+ }
+ }
+ vfree(xd_card->zone);
+ xd_card->zone = NULL;
+ }
+}
+
+void xd_cleanup_work(struct rtsx_chip *chip)
+{
+#ifdef XD_DELAY_WRITE
+ struct xd_info *xd_card = &(chip->xd_card);
+
+ if (xd_card->delay_write.delay_write_flag) {
+ dev_dbg(rtsx_dev(chip), "xD: delay write\n");
+ xd_delay_write(chip);
+ xd_card->cleanup_counter = 0;
+ }
+#endif
+}
+
+int xd_power_off_card3v3(struct rtsx_chip *chip)
+{
+ int retval;
+
+ retval = disable_card_clock(chip, XD_CARD);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ retval = rtsx_write_register(chip, CARD_OE, XD_OUTPUT_EN, 0);
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+
+ if (!chip->ft2_fast_mode) {
+ retval = card_power_off(chip, XD_CARD);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ wait_timeout(50);
+ }
+
+ if (chip->asic_code) {
+ retval = xd_pull_ctl_disable(chip);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+ } else {
+ retval = rtsx_write_register(chip, FPGA_PULL_CTL, 0xFF, 0xDF);
+ if (retval) {
+ rtsx_trace(chip);
+ return retval;
+ }
+ }
+
+ return STATUS_SUCCESS;
+}
+
+int release_xd_card(struct rtsx_chip *chip)
+{
+ struct xd_info *xd_card = &(chip->xd_card);
+ int retval;
+
+ chip->card_ready &= ~XD_CARD;
+ chip->card_fail &= ~XD_CARD;
+ chip->card_wp &= ~XD_CARD;
+
+ xd_card->delay_write.delay_write_flag = 0;
+
+ xd_free_l2p_tbl(chip);
+
+ retval = xd_power_off_card3v3(chip);
+ if (retval != STATUS_SUCCESS) {
+ rtsx_trace(chip);
+ return STATUS_FAIL;
+ }
+
+ return STATUS_SUCCESS;
+}
diff --git a/drivers/staging/rts5208/xd.h b/drivers/staging/rts5208/xd.h
new file mode 100644
index 000000000..938138c50
--- /dev/null
+++ b/drivers/staging/rts5208/xd.h
@@ -0,0 +1,188 @@
+/* Driver for Realtek PCI-Express card reader
+ * Header file
+ *
+ * Copyright(c) 2009-2013 Realtek Semiconductor Corp. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, 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, see <http://www.gnu.org/licenses/>.
+ *
+ * Author:
+ * Wei WANG (wei_wang@realsil.com.cn)
+ * Micky Ching (micky_ching@realsil.com.cn)
+ */
+
+#ifndef __REALTEK_RTSX_XD_H
+#define __REALTEK_RTSX_XD_H
+
+#define XD_DELAY_WRITE
+
+/* Error Codes */
+#define XD_NO_ERROR 0x00
+#define XD_NO_MEMORY 0x80
+#define XD_PRG_ERROR 0x40
+#define XD_NO_CARD 0x20
+#define XD_READ_FAIL 0x10
+#define XD_ERASE_FAIL 0x08
+#define XD_WRITE_FAIL 0x04
+#define XD_ECC_ERROR 0x02
+#define XD_TO_ERROR 0x01
+
+/* XD Commands */
+#define READ1_1 0x00
+#define READ1_2 0x01
+#define READ2 0x50
+#define READ_ID 0x90
+#define RESET 0xff
+#define PAGE_PRG_1 0x80
+#define PAGE_PRG_2 0x10
+#define BLK_ERASE_1 0x60
+#define BLK_ERASE_2 0xD0
+#define READ_STS 0x70
+#define READ_xD_ID 0x9A
+#define COPY_BACK_512 0x8A
+#define COPY_BACK_2K 0x85
+#define READ1_1_2 0x30
+#define READ1_1_3 0x35
+#define CHG_DAT_OUT_1 0x05
+#define RDM_DAT_OUT_1 0x05
+#define CHG_DAT_OUT_2 0xE0
+#define RDM_DAT_OUT_2 0xE0
+#define CHG_DAT_OUT_2 0xE0
+#define CHG_DAT_IN_1 0x85
+#define CACHE_PRG 0x15
+
+/* Redundant Area Related */
+#define XD_EXTRA_SIZE 0x10
+#define XD_2K_EXTRA_SIZE 0x40
+
+#define NOT_WRITE_PROTECTED 0x80
+#define READY_STATE 0x40
+#define PROGRAM_ERROR 0x01
+#define PROGRAM_ERROR_N_1 0x02
+#define INTERNAL_READY 0x20
+#define READY_FLAG 0x5F
+
+#define XD_8M_X8_512 0xE6
+#define XD_16M_X8_512 0x73
+#define XD_32M_X8_512 0x75
+#define XD_64M_X8_512 0x76
+#define XD_128M_X8_512 0x79
+#define XD_256M_X8_512 0x71
+#define XD_128M_X8_2048 0xF1
+#define XD_256M_X8_2048 0xDA
+#define XD_512M_X8 0xDC
+#define XD_128M_X16_2048 0xC1
+#define XD_4M_X8_512_1 0xE3
+#define XD_4M_X8_512_2 0xE5
+#define xD_1G_X8_512 0xD3
+#define xD_2G_X8_512 0xD5
+
+#define XD_ID_CODE 0xB5
+
+#define VENDOR_BLOCK 0xEFFF
+#define CIS_BLOCK 0xDFFF
+
+#define BLK_NOT_FOUND 0xFFFFFFFF
+
+#define NO_NEW_BLK 0xFFFFFFFF
+
+#define PAGE_CORRECTABLE 0x0
+#define PAGE_NOTCORRECTABLE 0x1
+
+#define NO_OFFSET 0x0
+#define WITH_OFFSET 0x1
+
+#define Sect_Per_Page 4
+#define XD_ADDR_MODE_2C XD_ADDR_MODE_2A
+
+#define ZONE0_BAD_BLOCK 23
+#define NOT_ZONE0_BAD_BLOCK 24
+
+#define XD_RW_ADDR 0x01
+#define XD_ERASE_ADDR 0x02
+
+#define XD_PAGE_512(xd_card) \
+do { \
+ (xd_card)->block_shift = 5; \
+ (xd_card)->page_off = 0x1F; \
+} while (0)
+
+#define XD_SET_BAD_NEWBLK(xd_card) ((xd_card)->multi_flag |= 0x01)
+#define XD_CLR_BAD_NEWBLK(xd_card) ((xd_card)->multi_flag &= ~0x01)
+#define XD_CHK_BAD_NEWBLK(xd_card) ((xd_card)->multi_flag & 0x01)
+
+#define XD_SET_BAD_OLDBLK(xd_card) ((xd_card)->multi_flag |= 0x02)
+#define XD_CLR_BAD_OLDBLK(xd_card) ((xd_card)->multi_flag &= ~0x02)
+#define XD_CHK_BAD_OLDBLK(xd_card) ((xd_card)->multi_flag & 0x02)
+
+#define XD_SET_MBR_FAIL(xd_card) ((xd_card)->multi_flag |= 0x04)
+#define XD_CLR_MBR_FAIL(xd_card) ((xd_card)->multi_flag &= ~0x04)
+#define XD_CHK_MBR_FAIL(xd_card) ((xd_card)->multi_flag & 0x04)
+
+#define XD_SET_ECC_FLD_ERR(xd_card) ((xd_card)->multi_flag |= 0x08)
+#define XD_CLR_ECC_FLD_ERR(xd_card) ((xd_card)->multi_flag &= ~0x08)
+#define XD_CHK_ECC_FLD_ERR(xd_card) ((xd_card)->multi_flag & 0x08)
+
+#define XD_SET_4MB(xd_card) ((xd_card)->multi_flag |= 0x10)
+#define XD_CLR_4MB(xd_card) ((xd_card)->multi_flag &= ~0x10)
+#define XD_CHK_4MB(xd_card) ((xd_card)->multi_flag & 0x10)
+
+#define XD_SET_ECC_ERR(xd_card) ((xd_card)->multi_flag |= 0x40)
+#define XD_CLR_ECC_ERR(xd_card) ((xd_card)->multi_flag &= ~0x40)
+#define XD_CHK_ECC_ERR(xd_card) ((xd_card)->multi_flag & 0x40)
+
+#define PAGE_STATUS 0
+#define BLOCK_STATUS 1
+#define BLOCK_ADDR1_L 2
+#define BLOCK_ADDR1_H 3
+#define BLOCK_ADDR2_L 4
+#define BLOCK_ADDR2_H 5
+#define RESERVED0 6
+#define RESERVED1 7
+#define RESERVED2 8
+#define RESERVED3 9
+#define PARITY 10
+
+#define CIS0_0 0
+#define CIS0_1 1
+#define CIS0_2 2
+#define CIS0_3 3
+#define CIS0_4 4
+#define CIS0_5 5
+#define CIS0_6 6
+#define CIS0_7 7
+#define CIS0_8 8
+#define CIS0_9 9
+#define CIS1_0 256
+#define CIS1_1 (256 + 1)
+#define CIS1_2 (256 + 2)
+#define CIS1_3 (256 + 3)
+#define CIS1_4 (256 + 4)
+#define CIS1_5 (256 + 5)
+#define CIS1_6 (256 + 6)
+#define CIS1_7 (256 + 7)
+#define CIS1_8 (256 + 8)
+#define CIS1_9 (256 + 9)
+
+int reset_xd_card(struct rtsx_chip *chip);
+#ifdef XD_DELAY_WRITE
+int xd_delay_write(struct rtsx_chip *chip);
+#endif
+int xd_rw(struct scsi_cmnd *srb, struct rtsx_chip *chip,
+ u32 start_sector, u16 sector_cnt);
+void xd_free_l2p_tbl(struct rtsx_chip *chip);
+void xd_cleanup_work(struct rtsx_chip *chip);
+int xd_power_off_card3v3(struct rtsx_chip *chip);
+int release_xd_card(struct rtsx_chip *chip);
+
+#endif /* __REALTEK_RTSX_XD_H */
diff --git a/drivers/staging/skein/Kconfig b/drivers/staging/skein/Kconfig
new file mode 100644
index 000000000..012a82333
--- /dev/null
+++ b/drivers/staging/skein/Kconfig
@@ -0,0 +1,16 @@
+config CRYPTO_SKEIN
+ tristate "Skein digest algorithm"
+ depends on (X86 || UML_X86) && 64BIT && CRYPTO
+ select CRYPTO_HASH
+ select CRYPTO_ALGAPI
+ help
+ Skein secure hash algorithm is one of 5 finalists from the NIST SHA3
+ competition.
+
+ Skein is optimized for modern, 64bit processors and is highly
+ customizable. See:
+
+ http://www.skein-hash.info/sites/default/files/skein1.3.pdf
+
+ for more information. This module also contains the threefish block
+ cipher algorithm.
diff --git a/drivers/staging/skein/Makefile b/drivers/staging/skein/Makefile
new file mode 100644
index 000000000..b7f947fb9
--- /dev/null
+++ b/drivers/staging/skein/Makefile
@@ -0,0 +1,10 @@
+#
+# Makefile for the skein secure hash algorithm
+#
+obj-$(CONFIG_CRYPTO_SKEIN) += skein.o
+skein-y := skein_base.o \
+ skein_api.o \
+ skein_block.o \
+ threefish_block.o \
+ threefish_api.o \
+ skein_generic.o
diff --git a/drivers/staging/skein/TODO b/drivers/staging/skein/TODO
new file mode 100644
index 000000000..cd3508dd9
--- /dev/null
+++ b/drivers/staging/skein/TODO
@@ -0,0 +1,8 @@
+skein/threefish TODO
+
+ - move macros into appropriate header files
+ - add / pass test vectors
+ - module support
+
+Please send patches to Jason Cooper <jason@lakedaemon.net> in addition to the
+staging tree mailinglist.
diff --git a/drivers/staging/skein/skein_api.c b/drivers/staging/skein/skein_api.c
new file mode 100644
index 000000000..5bfce076f
--- /dev/null
+++ b/drivers/staging/skein/skein_api.c
@@ -0,0 +1,239 @@
+/*
+Copyright (c) 2010 Werner Dittmann
+
+Permission is hereby granted, free of charge, to any person
+obtaining a copy of this software and associated documentation
+files (the "Software"), to deal in the Software without
+restriction, including without limitation the rights to use,
+copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following
+conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
+
+*/
+
+#include <linux/string.h>
+#include "skein_api.h"
+
+int skein_ctx_prepare(struct skein_ctx *ctx, enum skein_size size)
+{
+ skein_assert_ret(ctx && size, SKEIN_FAIL);
+
+ memset(ctx, 0, sizeof(struct skein_ctx));
+ ctx->skein_size = size;
+
+ return SKEIN_SUCCESS;
+}
+
+int skein_init(struct skein_ctx *ctx, size_t hash_bit_len)
+{
+ int ret = SKEIN_FAIL;
+ size_t x_len = 0;
+ u64 *x = NULL;
+ u64 tree_info = SKEIN_CFG_TREE_INFO_SEQUENTIAL;
+
+ skein_assert_ret(ctx, SKEIN_FAIL);
+ /*
+ * The following two lines rely of the fact that the real Skein
+ * contexts are a union in out context and thus have tha maximum
+ * memory available. The beauty of C :-) .
+ */
+ x = ctx->m.s256.x;
+ x_len = ctx->skein_size/8;
+ /*
+ * If size is the same and hash bit length is zero then reuse
+ * the save chaining variables.
+ */
+ switch (ctx->skein_size) {
+ case SKEIN_256:
+ ret = skein_256_init_ext(&ctx->m.s256, hash_bit_len,
+ tree_info, NULL, 0);
+ break;
+ case SKEIN_512:
+ ret = skein_512_init_ext(&ctx->m.s512, hash_bit_len,
+ tree_info, NULL, 0);
+ break;
+ case SKEIN_1024:
+ ret = skein_1024_init_ext(&ctx->m.s1024, hash_bit_len,
+ tree_info, NULL, 0);
+ break;
+ }
+
+ if (ret == SKEIN_SUCCESS) {
+ /*
+ * Save chaining variables for this combination of size and
+ * hash_bit_len
+ */
+ memcpy(ctx->x_save, x, x_len);
+ }
+ return ret;
+}
+
+int skein_mac_init(struct skein_ctx *ctx, const u8 *key, size_t key_len,
+ size_t hash_bit_len)
+{
+ int ret = SKEIN_FAIL;
+ u64 *x = NULL;
+ size_t x_len = 0;
+ u64 tree_info = SKEIN_CFG_TREE_INFO_SEQUENTIAL;
+
+ skein_assert_ret(ctx, SKEIN_FAIL);
+
+ x = ctx->m.s256.x;
+ x_len = ctx->skein_size/8;
+
+ skein_assert_ret(hash_bit_len, SKEIN_BAD_HASHLEN);
+
+ switch (ctx->skein_size) {
+ case SKEIN_256:
+ ret = skein_256_init_ext(&ctx->m.s256, hash_bit_len,
+ tree_info,
+ (const u8 *)key, key_len);
+
+ break;
+ case SKEIN_512:
+ ret = skein_512_init_ext(&ctx->m.s512, hash_bit_len,
+ tree_info,
+ (const u8 *)key, key_len);
+ break;
+ case SKEIN_1024:
+ ret = skein_1024_init_ext(&ctx->m.s1024, hash_bit_len,
+ tree_info,
+ (const u8 *)key, key_len);
+
+ break;
+ }
+ if (ret == SKEIN_SUCCESS) {
+ /*
+ * Save chaining variables for this combination of key,
+ * key_len, hash_bit_len
+ */
+ memcpy(ctx->x_save, x, x_len);
+ }
+ return ret;
+}
+
+void skein_reset(struct skein_ctx *ctx)
+{
+ size_t x_len = 0;
+ u64 *x = NULL;
+
+ /*
+ * The following two lines rely of the fact that the real Skein
+ * contexts are a union in out context and thus have tha maximum
+ * memory available. The beautiy of C :-) .
+ */
+ x = ctx->m.s256.x;
+ x_len = ctx->skein_size/8;
+ /* Restore the chaing variable, reset byte counter */
+ memcpy(x, ctx->x_save, x_len);
+
+ /* Setup context to process the message */
+ skein_start_new_type(&ctx->m, MSG);
+}
+
+int skein_update(struct skein_ctx *ctx, const u8 *msg,
+ size_t msg_byte_cnt)
+{
+ int ret = SKEIN_FAIL;
+
+ skein_assert_ret(ctx, SKEIN_FAIL);
+
+ switch (ctx->skein_size) {
+ case SKEIN_256:
+ ret = skein_256_update(&ctx->m.s256, (const u8 *)msg,
+ msg_byte_cnt);
+ break;
+ case SKEIN_512:
+ ret = skein_512_update(&ctx->m.s512, (const u8 *)msg,
+ msg_byte_cnt);
+ break;
+ case SKEIN_1024:
+ ret = skein_1024_update(&ctx->m.s1024, (const u8 *)msg,
+ msg_byte_cnt);
+ break;
+ }
+ return ret;
+
+}
+
+int skein_update_bits(struct skein_ctx *ctx, const u8 *msg,
+ size_t msg_bit_cnt)
+{
+ /*
+ * I've used the bit pad implementation from skein_test.c (see NIST CD)
+ * and modified it to use the convenience functions and added some
+ * pointer arithmetic.
+ */
+ size_t length;
+ u8 mask;
+ u8 *up;
+
+ /*
+ * only the final Update() call is allowed do partial bytes, else
+ * assert an error
+ */
+ skein_assert_ret((ctx->m.h.T[1] & SKEIN_T1_FLAG_BIT_PAD) == 0 ||
+ msg_bit_cnt == 0, SKEIN_FAIL);
+
+ /* if number of bits is a multiple of bytes - that's easy */
+ if ((msg_bit_cnt & 0x7) == 0)
+ return skein_update(ctx, msg, msg_bit_cnt >> 3);
+
+ skein_update(ctx, msg, (msg_bit_cnt >> 3) + 1);
+
+ /*
+ * The next line rely on the fact that the real Skein contexts
+ * are a union in our context. After the addition the pointer points to
+ * Skein's real partial block buffer.
+ * If this layout ever changes we have to adapt this as well.
+ */
+ up = (u8 *)ctx->m.s256.x + ctx->skein_size / 8;
+
+ /* set tweak flag for the skein_final call */
+ skein_set_bit_pad_flag(ctx->m.h);
+
+ /* now "pad" the final partial byte the way NIST likes */
+ /* get the b_cnt value (same location for all block sizes) */
+ length = ctx->m.h.b_cnt;
+ /* internal sanity check: there IS a partial byte in the buffer! */
+ skein_assert(length != 0);
+ /* partial byte bit mask */
+ mask = (u8) (1u << (7 - (msg_bit_cnt & 7)));
+ /* apply bit padding on final byte (in the buffer) */
+ up[length-1] = (u8)((up[length-1] & (0-mask))|mask);
+
+ return SKEIN_SUCCESS;
+}
+
+int skein_final(struct skein_ctx *ctx, u8 *hash)
+{
+ int ret = SKEIN_FAIL;
+
+ skein_assert_ret(ctx, SKEIN_FAIL);
+
+ switch (ctx->skein_size) {
+ case SKEIN_256:
+ ret = skein_256_final(&ctx->m.s256, (u8 *)hash);
+ break;
+ case SKEIN_512:
+ ret = skein_512_final(&ctx->m.s512, (u8 *)hash);
+ break;
+ case SKEIN_1024:
+ ret = skein_1024_final(&ctx->m.s1024, (u8 *)hash);
+ break;
+ }
+ return ret;
+}
diff --git a/drivers/staging/skein/skein_api.h b/drivers/staging/skein/skein_api.h
new file mode 100644
index 000000000..171b87549
--- /dev/null
+++ b/drivers/staging/skein/skein_api.h
@@ -0,0 +1,230 @@
+/*
+Copyright (c) 2010 Werner Dittmann
+
+Permission is hereby granted, free of charge, to any person
+obtaining a copy of this software and associated documentation
+files (the "Software"), to deal in the Software without
+restriction, including without limitation the rights to use,
+copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following
+conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
+
+*/
+
+#ifndef SKEINAPI_H
+#define SKEINAPI_H
+
+/**
+ * @file skein_api.h
+ * @brief A Skein API and its functions.
+ * @{
+ *
+ * This API and the functions that implement this API simplify the usage
+ * of Skein. The design and the way to use the functions follow the openSSL
+ * design but at the same time take care of some Skein specific behaviour
+ * and possibilities.
+ *
+ * The functions enable applications to create a normal Skein hashes and
+ * message authentication codes (MAC).
+ *
+ * Using these functions is simple and straight forward:
+ *
+ * @code
+ *
+ * #include "skein_api.h"
+ *
+ * ...
+ * struct skein_ctx ctx; // a Skein hash or MAC context
+ *
+ * // prepare context, here for a Skein with a state size of 512 bits.
+ * skein_ctx_prepare(&ctx, SKEIN_512);
+ *
+ * // Initialize the context to set the requested hash length in bits
+ * // here request a output hash size of 31 bits (Skein supports variable
+ * // output sizes even very strange sizes)
+ * skein_init(&ctx, 31);
+ *
+ * // Now update Skein with any number of message bits. A function that
+ * // takes a number of bytes is also available.
+ * skein_update_bits(&ctx, message, msg_length);
+ *
+ * // Now get the result of the Skein hash. The output buffer must be
+ * // large enough to hold the request number of output bits. The application
+ * // may now extract the bits.
+ * skein_final(&ctx, result);
+ * ...
+ * @endcode
+ *
+ * An application may use @c skein_reset to reset a Skein context and use
+ * it for creation of another hash with the same Skein state size and output
+ * bit length. In this case the API implementation restores some internal
+ * internal state data and saves a full Skein initialization round.
+ *
+ * To create a MAC the application just uses @c skein_mac_init instead of
+ * @c skein_init. All other functions calls remain the same.
+ *
+ */
+
+#include <linux/types.h>
+#include "skein_base.h"
+
+/**
+ * Which Skein size to use
+ */
+enum skein_size {
+ SKEIN_256 = 256, /*!< Skein with 256 bit state */
+ SKEIN_512 = 512, /*!< Skein with 512 bit state */
+ SKEIN_1024 = 1024 /*!< Skein with 1024 bit state */
+};
+
+/**
+ * Context for Skein.
+ *
+ * This structure was setup with some know-how of the internal
+ * Skein structures, in particular ordering of header and size dependent
+ * variables. If Skein implementation changes this, then adapt these
+ * structures as well.
+ */
+struct skein_ctx {
+ u64 skein_size;
+ u64 x_save[SKEIN_MAX_STATE_WORDS]; /* save area for state variables */
+ union {
+ struct skein_ctx_hdr h;
+ struct skein_256_ctx s256;
+ struct skein_512_ctx s512;
+ struct skein_1024_ctx s1024;
+ } m;
+};
+
+/**
+ * Prepare a Skein context.
+ *
+ * An application must call this function before it can use the Skein
+ * context. The functions clears memory and initializes size dependent
+ * variables.
+ *
+ * @param ctx
+ * Pointer to a Skein context.
+ * @param size
+ * Which Skein size to use.
+ * @return
+ * SKEIN_SUCESS of SKEIN_FAIL
+ */
+int skein_ctx_prepare(struct skein_ctx *ctx, enum skein_size size);
+
+/**
+ * Initialize a Skein context.
+ *
+ * Initializes the context with this data and saves the resulting Skein
+ * state variables for further use.
+ *
+ * @param ctx
+ * Pointer to a Skein context.
+ * @param hash_bit_len
+ * Number of MAC hash bits to compute
+ * @return
+ * SKEIN_SUCESS of SKEIN_FAIL
+ * @see skein_reset
+ */
+int skein_init(struct skein_ctx *ctx, size_t hash_bit_len);
+
+/**
+ * Resets a Skein context for further use.
+ *
+ * Restores the saved chaining variables to reset the Skein context.
+ * Thus applications can reuse the same setup to process several
+ * messages. This saves a complete Skein initialization cycle.
+ *
+ * @param ctx
+ * Pointer to a pre-initialized Skein MAC context
+ */
+void skein_reset(struct skein_ctx *ctx);
+
+/**
+ * Initializes a Skein context for MAC usage.
+ *
+ * Initializes the context with this data and saves the resulting Skein
+ * state variables for further use.
+ *
+ * Applications call the normal Skein functions to update the MAC and
+ * get the final result.
+ *
+ * @param ctx
+ * Pointer to an empty or preinitialized Skein MAC context
+ * @param key
+ * Pointer to key bytes or NULL
+ * @param key_len
+ * Length of the key in bytes or zero
+ * @param hash_bit_len
+ * Number of MAC hash bits to compute
+ * @return
+ * SKEIN_SUCESS of SKEIN_FAIL
+ */
+int skein_mac_init(struct skein_ctx *ctx, const u8 *key, size_t key_len,
+ size_t hash_bit_len);
+
+/**
+ * Update Skein with the next part of the message.
+ *
+ * @param ctx
+ * Pointer to initialized Skein context
+ * @param msg
+ * Pointer to the message.
+ * @param msg_byte_cnt
+ * Length of the message in @b bytes
+ * @return
+ * Success or error code.
+ */
+int skein_update(struct skein_ctx *ctx, const u8 *msg,
+ size_t msg_byte_cnt);
+
+/**
+ * Update the hash with a message bit string.
+ *
+ * Skein can handle data not only as bytes but also as bit strings of
+ * arbitrary length (up to its maximum design size).
+ *
+ * @param ctx
+ * Pointer to initialized Skein context
+ * @param msg
+ * Pointer to the message.
+ * @param msg_bit_cnt
+ * Length of the message in @b bits.
+ */
+int skein_update_bits(struct skein_ctx *ctx, const u8 *msg,
+ size_t msg_bit_cnt);
+
+/**
+ * Finalize Skein and return the hash.
+ *
+ * Before an application can reuse a Skein setup the application must
+ * reset the Skein context.
+ *
+ * @param ctx
+ * Pointer to initialized Skein context
+ * @param hash
+ * Pointer to buffer that receives the hash. The buffer must be large
+ * enough to store @c hash_bit_len bits.
+ * @return
+ * Success or error code.
+ * @see skein_reset
+ */
+int skein_final(struct skein_ctx *ctx, u8 *hash);
+
+/**
+ * @}
+ */
+#endif
diff --git a/drivers/staging/skein/skein_base.c b/drivers/staging/skein/skein_base.c
new file mode 100644
index 000000000..7e700a6b5
--- /dev/null
+++ b/drivers/staging/skein/skein_base.c
@@ -0,0 +1,864 @@
+/***********************************************************************
+**
+** Implementation of the Skein hash function.
+**
+** Source code author: Doug Whiting, 2008.
+**
+** This algorithm and source code is released to the public domain.
+**
+************************************************************************/
+
+#include <linux/string.h> /* get the memcpy/memset functions */
+#include <linux/export.h>
+#include "skein_base.h" /* get the Skein API definitions */
+#include "skein_iv.h" /* get precomputed IVs */
+#include "skein_block.h"
+
+/*****************************************************************/
+/* 256-bit Skein */
+/*****************************************************************/
+
+/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
+/* init the context for a straight hashing operation */
+int skein_256_init(struct skein_256_ctx *ctx, size_t hash_bit_len)
+{
+ union {
+ u8 b[SKEIN_256_STATE_BYTES];
+ u64 w[SKEIN_256_STATE_WORDS];
+ } cfg; /* config block */
+
+ skein_assert_ret(hash_bit_len > 0, SKEIN_BAD_HASHLEN);
+ ctx->h.hash_bit_len = hash_bit_len; /* output hash bit count */
+
+ switch (hash_bit_len) { /* use pre-computed values, where available */
+ case 256:
+ memcpy(ctx->x, SKEIN_256_IV_256, sizeof(ctx->x));
+ break;
+ case 224:
+ memcpy(ctx->x, SKEIN_256_IV_224, sizeof(ctx->x));
+ break;
+ case 160:
+ memcpy(ctx->x, SKEIN_256_IV_160, sizeof(ctx->x));
+ break;
+ case 128:
+ memcpy(ctx->x, SKEIN_256_IV_128, sizeof(ctx->x));
+ break;
+ default:
+ /* here if there is no precomputed IV value available */
+ /*
+ * build/process the config block, type == CONFIG (could be
+ * precomputed)
+ */
+ /* set tweaks: T0=0; T1=CFG | FINAL */
+ skein_start_new_type(ctx, CFG_FINAL);
+
+ /* set the schema, version */
+ cfg.w[0] = skein_swap64(SKEIN_SCHEMA_VER);
+ /* hash result length in bits */
+ cfg.w[1] = skein_swap64(hash_bit_len);
+ cfg.w[2] = skein_swap64(SKEIN_CFG_TREE_INFO_SEQUENTIAL);
+ /* zero pad config block */
+ memset(&cfg.w[3], 0, sizeof(cfg) - 3*sizeof(cfg.w[0]));
+
+ /* compute the initial chaining values from config block */
+ /* zero the chaining variables */
+ memset(ctx->x, 0, sizeof(ctx->x));
+ skein_256_process_block(ctx, cfg.b, 1, SKEIN_CFG_STR_LEN);
+ break;
+ }
+ /* The chaining vars ctx->x are now initialized for hash_bit_len. */
+ /* Set up to process the data message portion of the hash (default) */
+ skein_start_new_type(ctx, MSG); /* T0=0, T1= MSG type */
+
+ return SKEIN_SUCCESS;
+}
+
+/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
+/* init the context for a MAC and/or tree hash operation */
+/* [identical to skein_256_init() when key_bytes == 0 && \
+ * tree_info == SKEIN_CFG_TREE_INFO_SEQUENTIAL] */
+int skein_256_init_ext(struct skein_256_ctx *ctx, size_t hash_bit_len,
+ u64 tree_info, const u8 *key, size_t key_bytes)
+{
+ union {
+ u8 b[SKEIN_256_STATE_BYTES];
+ u64 w[SKEIN_256_STATE_WORDS];
+ } cfg; /* config block */
+
+ skein_assert_ret(hash_bit_len > 0, SKEIN_BAD_HASHLEN);
+ skein_assert_ret(key_bytes == 0 || key != NULL, SKEIN_FAIL);
+
+ /* compute the initial chaining values ctx->x[], based on key */
+ if (key_bytes == 0) { /* is there a key? */
+ /* no key: use all zeroes as key for config block */
+ memset(ctx->x, 0, sizeof(ctx->x));
+ } else { /* here to pre-process a key */
+ skein_assert(sizeof(cfg.b) >= sizeof(ctx->x));
+ /* do a mini-Init right here */
+ /* set output hash bit count = state size */
+ ctx->h.hash_bit_len = 8*sizeof(ctx->x);
+ /* set tweaks: T0 = 0; T1 = KEY type */
+ skein_start_new_type(ctx, KEY);
+ /* zero the initial chaining variables */
+ memset(ctx->x, 0, sizeof(ctx->x));
+ /* hash the key */
+ skein_256_update(ctx, key, key_bytes);
+ /* put result into cfg.b[] */
+ skein_256_final_pad(ctx, cfg.b);
+ /* copy over into ctx->x[] */
+ memcpy(ctx->x, cfg.b, sizeof(cfg.b));
+ }
+ /*
+ * build/process the config block, type == CONFIG (could be
+ * precomputed for each key)
+ */
+ /* output hash bit count */
+ ctx->h.hash_bit_len = hash_bit_len;
+ skein_start_new_type(ctx, CFG_FINAL);
+
+ /* pre-pad cfg.w[] with zeroes */
+ memset(&cfg.w, 0, sizeof(cfg.w));
+ cfg.w[0] = skein_swap64(SKEIN_SCHEMA_VER);
+ /* hash result length in bits */
+ cfg.w[1] = skein_swap64(hash_bit_len);
+ /* tree hash config info (or SKEIN_CFG_TREE_INFO_SEQUENTIAL) */
+ cfg.w[2] = skein_swap64(tree_info);
+
+ /* compute the initial chaining values from config block */
+ skein_256_process_block(ctx, cfg.b, 1, SKEIN_CFG_STR_LEN);
+
+ /* The chaining vars ctx->x are now initialized */
+ /* Set up to process the data message portion of the hash (default) */
+ skein_start_new_type(ctx, MSG);
+
+ return SKEIN_SUCCESS;
+}
+
+/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
+/* process the input bytes */
+int skein_256_update(struct skein_256_ctx *ctx, const u8 *msg,
+ size_t msg_byte_cnt)
+{
+ size_t n;
+
+ /* catch uninitialized context */
+ skein_assert_ret(ctx->h.b_cnt <= SKEIN_256_BLOCK_BYTES, SKEIN_FAIL);
+
+ /* process full blocks, if any */
+ if (msg_byte_cnt + ctx->h.b_cnt > SKEIN_256_BLOCK_BYTES) {
+ /* finish up any buffered message data */
+ if (ctx->h.b_cnt) {
+ /* # bytes free in buffer b[] */
+ n = SKEIN_256_BLOCK_BYTES - ctx->h.b_cnt;
+ if (n) {
+ /* check on our logic here */
+ skein_assert(n < msg_byte_cnt);
+ memcpy(&ctx->b[ctx->h.b_cnt], msg, n);
+ msg_byte_cnt -= n;
+ msg += n;
+ ctx->h.b_cnt += n;
+ }
+ skein_assert(ctx->h.b_cnt == SKEIN_256_BLOCK_BYTES);
+ skein_256_process_block(ctx, ctx->b, 1,
+ SKEIN_256_BLOCK_BYTES);
+ ctx->h.b_cnt = 0;
+ }
+ /*
+ * now process any remaining full blocks, directly from input
+ * message data
+ */
+ if (msg_byte_cnt > SKEIN_256_BLOCK_BYTES) {
+ /* number of full blocks to process */
+ n = (msg_byte_cnt-1) / SKEIN_256_BLOCK_BYTES;
+ skein_256_process_block(ctx, msg, n,
+ SKEIN_256_BLOCK_BYTES);
+ msg_byte_cnt -= n * SKEIN_256_BLOCK_BYTES;
+ msg += n * SKEIN_256_BLOCK_BYTES;
+ }
+ skein_assert(ctx->h.b_cnt == 0);
+ }
+
+ /* copy any remaining source message data bytes into b[] */
+ if (msg_byte_cnt) {
+ skein_assert(msg_byte_cnt + ctx->h.b_cnt <=
+ SKEIN_256_BLOCK_BYTES);
+ memcpy(&ctx->b[ctx->h.b_cnt], msg, msg_byte_cnt);
+ ctx->h.b_cnt += msg_byte_cnt;
+ }
+
+ return SKEIN_SUCCESS;
+}
+
+/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
+/* finalize the hash computation and output the result */
+int skein_256_final(struct skein_256_ctx *ctx, u8 *hash_val)
+{
+ size_t i, n, byte_cnt;
+ u64 x[SKEIN_256_STATE_WORDS];
+ /* catch uninitialized context */
+ skein_assert_ret(ctx->h.b_cnt <= SKEIN_256_BLOCK_BYTES, SKEIN_FAIL);
+
+ /* tag as the final block */
+ ctx->h.tweak[1] |= SKEIN_T1_FLAG_FINAL;
+ /* zero pad b[] if necessary */
+ if (ctx->h.b_cnt < SKEIN_256_BLOCK_BYTES)
+ memset(&ctx->b[ctx->h.b_cnt], 0,
+ SKEIN_256_BLOCK_BYTES - ctx->h.b_cnt);
+
+ /* process the final block */
+ skein_256_process_block(ctx, ctx->b, 1, ctx->h.b_cnt);
+
+ /* now output the result */
+ /* total number of output bytes */
+ byte_cnt = (ctx->h.hash_bit_len + 7) >> 3;
+
+ /* run Threefish in "counter mode" to generate output */
+ /* zero out b[], so it can hold the counter */
+ memset(ctx->b, 0, sizeof(ctx->b));
+ /* keep a local copy of counter mode "key" */
+ memcpy(x, ctx->x, sizeof(x));
+ for (i = 0; i*SKEIN_256_BLOCK_BYTES < byte_cnt; i++) {
+ /* build the counter block */
+ ((u64 *)ctx->b)[0] = skein_swap64((u64) i);
+ skein_start_new_type(ctx, OUT_FINAL);
+ /* run "counter mode" */
+ skein_256_process_block(ctx, ctx->b, 1, sizeof(u64));
+ /* number of output bytes left to go */
+ n = byte_cnt - i*SKEIN_256_BLOCK_BYTES;
+ if (n >= SKEIN_256_BLOCK_BYTES)
+ n = SKEIN_256_BLOCK_BYTES;
+ /* "output" the ctr mode bytes */
+ skein_put64_lsb_first(hash_val+i*SKEIN_256_BLOCK_BYTES, ctx->x,
+ n);
+ /* restore the counter mode key for next time */
+ memcpy(ctx->x, x, sizeof(x));
+ }
+ return SKEIN_SUCCESS;
+}
+
+/*****************************************************************/
+/* 512-bit Skein */
+/*****************************************************************/
+
+/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
+/* init the context for a straight hashing operation */
+int skein_512_init(struct skein_512_ctx *ctx, size_t hash_bit_len)
+{
+ union {
+ u8 b[SKEIN_512_STATE_BYTES];
+ u64 w[SKEIN_512_STATE_WORDS];
+ } cfg; /* config block */
+
+ skein_assert_ret(hash_bit_len > 0, SKEIN_BAD_HASHLEN);
+ ctx->h.hash_bit_len = hash_bit_len; /* output hash bit count */
+
+ switch (hash_bit_len) { /* use pre-computed values, where available */
+ case 512:
+ memcpy(ctx->x, SKEIN_512_IV_512, sizeof(ctx->x));
+ break;
+ case 384:
+ memcpy(ctx->x, SKEIN_512_IV_384, sizeof(ctx->x));
+ break;
+ case 256:
+ memcpy(ctx->x, SKEIN_512_IV_256, sizeof(ctx->x));
+ break;
+ case 224:
+ memcpy(ctx->x, SKEIN_512_IV_224, sizeof(ctx->x));
+ break;
+ default:
+ /* here if there is no precomputed IV value available */
+ /*
+ * build/process the config block, type == CONFIG (could be
+ * precomputed)
+ */
+ /* set tweaks: T0=0; T1=CFG | FINAL */
+ skein_start_new_type(ctx, CFG_FINAL);
+
+ /* set the schema, version */
+ cfg.w[0] = skein_swap64(SKEIN_SCHEMA_VER);
+ /* hash result length in bits */
+ cfg.w[1] = skein_swap64(hash_bit_len);
+ cfg.w[2] = skein_swap64(SKEIN_CFG_TREE_INFO_SEQUENTIAL);
+ /* zero pad config block */
+ memset(&cfg.w[3], 0, sizeof(cfg) - 3*sizeof(cfg.w[0]));
+
+ /* compute the initial chaining values from config block */
+ /* zero the chaining variables */
+ memset(ctx->x, 0, sizeof(ctx->x));
+ skein_512_process_block(ctx, cfg.b, 1, SKEIN_CFG_STR_LEN);
+ break;
+ }
+
+ /*
+ * The chaining vars ctx->x are now initialized for the given
+ * hash_bit_len.
+ */
+ /* Set up to process the data message portion of the hash (default) */
+ skein_start_new_type(ctx, MSG); /* T0=0, T1= MSG type */
+
+ return SKEIN_SUCCESS;
+}
+
+/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
+/* init the context for a MAC and/or tree hash operation */
+/* [identical to skein_512_init() when key_bytes == 0 && \
+ * tree_info == SKEIN_CFG_TREE_INFO_SEQUENTIAL] */
+int skein_512_init_ext(struct skein_512_ctx *ctx, size_t hash_bit_len,
+ u64 tree_info, const u8 *key, size_t key_bytes)
+{
+ union {
+ u8 b[SKEIN_512_STATE_BYTES];
+ u64 w[SKEIN_512_STATE_WORDS];
+ } cfg; /* config block */
+
+ skein_assert_ret(hash_bit_len > 0, SKEIN_BAD_HASHLEN);
+ skein_assert_ret(key_bytes == 0 || key != NULL, SKEIN_FAIL);
+
+ /* compute the initial chaining values ctx->x[], based on key */
+ if (key_bytes == 0) { /* is there a key? */
+ /* no key: use all zeroes as key for config block */
+ memset(ctx->x, 0, sizeof(ctx->x));
+ } else { /* here to pre-process a key */
+ skein_assert(sizeof(cfg.b) >= sizeof(ctx->x));
+ /* do a mini-Init right here */
+ /* set output hash bit count = state size */
+ ctx->h.hash_bit_len = 8*sizeof(ctx->x);
+ /* set tweaks: T0 = 0; T1 = KEY type */
+ skein_start_new_type(ctx, KEY);
+ /* zero the initial chaining variables */
+ memset(ctx->x, 0, sizeof(ctx->x));
+ /* hash the key */
+ skein_512_update(ctx, key, key_bytes);
+ /* put result into cfg.b[] */
+ skein_512_final_pad(ctx, cfg.b);
+ /* copy over into ctx->x[] */
+ memcpy(ctx->x, cfg.b, sizeof(cfg.b));
+ }
+ /*
+ * build/process the config block, type == CONFIG (could be
+ * precomputed for each key)
+ */
+ ctx->h.hash_bit_len = hash_bit_len; /* output hash bit count */
+ skein_start_new_type(ctx, CFG_FINAL);
+
+ /* pre-pad cfg.w[] with zeroes */
+ memset(&cfg.w, 0, sizeof(cfg.w));
+ cfg.w[0] = skein_swap64(SKEIN_SCHEMA_VER);
+ /* hash result length in bits */
+ cfg.w[1] = skein_swap64(hash_bit_len);
+ /* tree hash config info (or SKEIN_CFG_TREE_INFO_SEQUENTIAL) */
+ cfg.w[2] = skein_swap64(tree_info);
+
+ /* compute the initial chaining values from config block */
+ skein_512_process_block(ctx, cfg.b, 1, SKEIN_CFG_STR_LEN);
+
+ /* The chaining vars ctx->x are now initialized */
+ /* Set up to process the data message portion of the hash (default) */
+ skein_start_new_type(ctx, MSG);
+
+ return SKEIN_SUCCESS;
+}
+
+/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
+/* process the input bytes */
+int skein_512_update(struct skein_512_ctx *ctx, const u8 *msg,
+ size_t msg_byte_cnt)
+{
+ size_t n;
+
+ /* catch uninitialized context */
+ skein_assert_ret(ctx->h.b_cnt <= SKEIN_512_BLOCK_BYTES, SKEIN_FAIL);
+
+ /* process full blocks, if any */
+ if (msg_byte_cnt + ctx->h.b_cnt > SKEIN_512_BLOCK_BYTES) {
+ /* finish up any buffered message data */
+ if (ctx->h.b_cnt) {
+ /* # bytes free in buffer b[] */
+ n = SKEIN_512_BLOCK_BYTES - ctx->h.b_cnt;
+ if (n) {
+ /* check on our logic here */
+ skein_assert(n < msg_byte_cnt);
+ memcpy(&ctx->b[ctx->h.b_cnt], msg, n);
+ msg_byte_cnt -= n;
+ msg += n;
+ ctx->h.b_cnt += n;
+ }
+ skein_assert(ctx->h.b_cnt == SKEIN_512_BLOCK_BYTES);
+ skein_512_process_block(ctx, ctx->b, 1,
+ SKEIN_512_BLOCK_BYTES);
+ ctx->h.b_cnt = 0;
+ }
+ /*
+ * now process any remaining full blocks, directly from input
+ * message data
+ */
+ if (msg_byte_cnt > SKEIN_512_BLOCK_BYTES) {
+ /* number of full blocks to process */
+ n = (msg_byte_cnt-1) / SKEIN_512_BLOCK_BYTES;
+ skein_512_process_block(ctx, msg, n,
+ SKEIN_512_BLOCK_BYTES);
+ msg_byte_cnt -= n * SKEIN_512_BLOCK_BYTES;
+ msg += n * SKEIN_512_BLOCK_BYTES;
+ }
+ skein_assert(ctx->h.b_cnt == 0);
+ }
+
+ /* copy any remaining source message data bytes into b[] */
+ if (msg_byte_cnt) {
+ skein_assert(msg_byte_cnt + ctx->h.b_cnt <=
+ SKEIN_512_BLOCK_BYTES);
+ memcpy(&ctx->b[ctx->h.b_cnt], msg, msg_byte_cnt);
+ ctx->h.b_cnt += msg_byte_cnt;
+ }
+
+ return SKEIN_SUCCESS;
+}
+
+/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
+/* finalize the hash computation and output the result */
+int skein_512_final(struct skein_512_ctx *ctx, u8 *hash_val)
+{
+ size_t i, n, byte_cnt;
+ u64 x[SKEIN_512_STATE_WORDS];
+ /* catch uninitialized context */
+ skein_assert_ret(ctx->h.b_cnt <= SKEIN_512_BLOCK_BYTES, SKEIN_FAIL);
+
+ /* tag as the final block */
+ ctx->h.tweak[1] |= SKEIN_T1_FLAG_FINAL;
+ /* zero pad b[] if necessary */
+ if (ctx->h.b_cnt < SKEIN_512_BLOCK_BYTES)
+ memset(&ctx->b[ctx->h.b_cnt], 0,
+ SKEIN_512_BLOCK_BYTES - ctx->h.b_cnt);
+
+ /* process the final block */
+ skein_512_process_block(ctx, ctx->b, 1, ctx->h.b_cnt);
+
+ /* now output the result */
+ /* total number of output bytes */
+ byte_cnt = (ctx->h.hash_bit_len + 7) >> 3;
+
+ /* run Threefish in "counter mode" to generate output */
+ /* zero out b[], so it can hold the counter */
+ memset(ctx->b, 0, sizeof(ctx->b));
+ /* keep a local copy of counter mode "key" */
+ memcpy(x, ctx->x, sizeof(x));
+ for (i = 0; i*SKEIN_512_BLOCK_BYTES < byte_cnt; i++) {
+ /* build the counter block */
+ ((u64 *)ctx->b)[0] = skein_swap64((u64) i);
+ skein_start_new_type(ctx, OUT_FINAL);
+ /* run "counter mode" */
+ skein_512_process_block(ctx, ctx->b, 1, sizeof(u64));
+ /* number of output bytes left to go */
+ n = byte_cnt - i*SKEIN_512_BLOCK_BYTES;
+ if (n >= SKEIN_512_BLOCK_BYTES)
+ n = SKEIN_512_BLOCK_BYTES;
+ /* "output" the ctr mode bytes */
+ skein_put64_lsb_first(hash_val+i*SKEIN_512_BLOCK_BYTES, ctx->x,
+ n);
+ /* restore the counter mode key for next time */
+ memcpy(ctx->x, x, sizeof(x));
+ }
+ return SKEIN_SUCCESS;
+}
+
+/*****************************************************************/
+/* 1024-bit Skein */
+/*****************************************************************/
+
+/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
+/* init the context for a straight hashing operation */
+int skein_1024_init(struct skein_1024_ctx *ctx, size_t hash_bit_len)
+{
+ union {
+ u8 b[SKEIN_1024_STATE_BYTES];
+ u64 w[SKEIN_1024_STATE_WORDS];
+ } cfg; /* config block */
+
+ skein_assert_ret(hash_bit_len > 0, SKEIN_BAD_HASHLEN);
+ ctx->h.hash_bit_len = hash_bit_len; /* output hash bit count */
+
+ switch (hash_bit_len) { /* use pre-computed values, where available */
+ case 512:
+ memcpy(ctx->x, SKEIN_1024_IV_512, sizeof(ctx->x));
+ break;
+ case 384:
+ memcpy(ctx->x, SKEIN_1024_IV_384, sizeof(ctx->x));
+ break;
+ case 1024:
+ memcpy(ctx->x, SKEIN_1024_IV_1024, sizeof(ctx->x));
+ break;
+ default:
+ /* here if there is no precomputed IV value available */
+ /*
+ * build/process the config block, type == CONFIG
+ * (could be precomputed)
+ */
+ /* set tweaks: T0=0; T1=CFG | FINAL */
+ skein_start_new_type(ctx, CFG_FINAL);
+
+ /* set the schema, version */
+ cfg.w[0] = skein_swap64(SKEIN_SCHEMA_VER);
+ /* hash result length in bits */
+ cfg.w[1] = skein_swap64(hash_bit_len);
+ cfg.w[2] = skein_swap64(SKEIN_CFG_TREE_INFO_SEQUENTIAL);
+ /* zero pad config block */
+ memset(&cfg.w[3], 0, sizeof(cfg) - 3*sizeof(cfg.w[0]));
+
+ /* compute the initial chaining values from config block */
+ /* zero the chaining variables */
+ memset(ctx->x, 0, sizeof(ctx->x));
+ skein_1024_process_block(ctx, cfg.b, 1, SKEIN_CFG_STR_LEN);
+ break;
+ }
+
+ /* The chaining vars ctx->x are now initialized for the hash_bit_len. */
+ /* Set up to process the data message portion of the hash (default) */
+ skein_start_new_type(ctx, MSG); /* T0=0, T1= MSG type */
+
+ return SKEIN_SUCCESS;
+}
+
+/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
+/* init the context for a MAC and/or tree hash operation */
+/* [identical to skein_1024_init() when key_bytes == 0 && \
+ * tree_info == SKEIN_CFG_TREE_INFO_SEQUENTIAL] */
+int skein_1024_init_ext(struct skein_1024_ctx *ctx, size_t hash_bit_len,
+ u64 tree_info, const u8 *key, size_t key_bytes)
+{
+ union {
+ u8 b[SKEIN_1024_STATE_BYTES];
+ u64 w[SKEIN_1024_STATE_WORDS];
+ } cfg; /* config block */
+
+ skein_assert_ret(hash_bit_len > 0, SKEIN_BAD_HASHLEN);
+ skein_assert_ret(key_bytes == 0 || key != NULL, SKEIN_FAIL);
+
+ /* compute the initial chaining values ctx->x[], based on key */
+ if (key_bytes == 0) { /* is there a key? */
+ /* no key: use all zeroes as key for config block */
+ memset(ctx->x, 0, sizeof(ctx->x));
+ } else { /* here to pre-process a key */
+ skein_assert(sizeof(cfg.b) >= sizeof(ctx->x));
+ /* do a mini-Init right here */
+ /* set output hash bit count = state size */
+ ctx->h.hash_bit_len = 8*sizeof(ctx->x);
+ /* set tweaks: T0 = 0; T1 = KEY type */
+ skein_start_new_type(ctx, KEY);
+ /* zero the initial chaining variables */
+ memset(ctx->x, 0, sizeof(ctx->x));
+ /* hash the key */
+ skein_1024_update(ctx, key, key_bytes);
+ /* put result into cfg.b[] */
+ skein_1024_final_pad(ctx, cfg.b);
+ /* copy over into ctx->x[] */
+ memcpy(ctx->x, cfg.b, sizeof(cfg.b));
+ }
+ /*
+ * build/process the config block, type == CONFIG (could be
+ * precomputed for each key)
+ */
+ /* output hash bit count */
+ ctx->h.hash_bit_len = hash_bit_len;
+ skein_start_new_type(ctx, CFG_FINAL);
+
+ /* pre-pad cfg.w[] with zeroes */
+ memset(&cfg.w, 0, sizeof(cfg.w));
+ cfg.w[0] = skein_swap64(SKEIN_SCHEMA_VER);
+ /* hash result length in bits */
+ cfg.w[1] = skein_swap64(hash_bit_len);
+ /* tree hash config info (or SKEIN_CFG_TREE_INFO_SEQUENTIAL) */
+ cfg.w[2] = skein_swap64(tree_info);
+
+ /* compute the initial chaining values from config block */
+ skein_1024_process_block(ctx, cfg.b, 1, SKEIN_CFG_STR_LEN);
+
+ /* The chaining vars ctx->x are now initialized */
+ /* Set up to process the data message portion of the hash (default) */
+ skein_start_new_type(ctx, MSG);
+
+ return SKEIN_SUCCESS;
+}
+
+/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
+/* process the input bytes */
+int skein_1024_update(struct skein_1024_ctx *ctx, const u8 *msg,
+ size_t msg_byte_cnt)
+{
+ size_t n;
+
+ /* catch uninitialized context */
+ skein_assert_ret(ctx->h.b_cnt <= SKEIN_1024_BLOCK_BYTES, SKEIN_FAIL);
+
+ /* process full blocks, if any */
+ if (msg_byte_cnt + ctx->h.b_cnt > SKEIN_1024_BLOCK_BYTES) {
+ /* finish up any buffered message data */
+ if (ctx->h.b_cnt) {
+ /* # bytes free in buffer b[] */
+ n = SKEIN_1024_BLOCK_BYTES - ctx->h.b_cnt;
+ if (n) {
+ /* check on our logic here */
+ skein_assert(n < msg_byte_cnt);
+ memcpy(&ctx->b[ctx->h.b_cnt], msg, n);
+ msg_byte_cnt -= n;
+ msg += n;
+ ctx->h.b_cnt += n;
+ }
+ skein_assert(ctx->h.b_cnt == SKEIN_1024_BLOCK_BYTES);
+ skein_1024_process_block(ctx, ctx->b, 1,
+ SKEIN_1024_BLOCK_BYTES);
+ ctx->h.b_cnt = 0;
+ }
+ /*
+ * now process any remaining full blocks, directly from input
+ * message data
+ */
+ if (msg_byte_cnt > SKEIN_1024_BLOCK_BYTES) {
+ /* number of full blocks to process */
+ n = (msg_byte_cnt-1) / SKEIN_1024_BLOCK_BYTES;
+ skein_1024_process_block(ctx, msg, n,
+ SKEIN_1024_BLOCK_BYTES);
+ msg_byte_cnt -= n * SKEIN_1024_BLOCK_BYTES;
+ msg += n * SKEIN_1024_BLOCK_BYTES;
+ }
+ skein_assert(ctx->h.b_cnt == 0);
+ }
+
+ /* copy any remaining source message data bytes into b[] */
+ if (msg_byte_cnt) {
+ skein_assert(msg_byte_cnt + ctx->h.b_cnt <=
+ SKEIN_1024_BLOCK_BYTES);
+ memcpy(&ctx->b[ctx->h.b_cnt], msg, msg_byte_cnt);
+ ctx->h.b_cnt += msg_byte_cnt;
+ }
+
+ return SKEIN_SUCCESS;
+}
+
+/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
+/* finalize the hash computation and output the result */
+int skein_1024_final(struct skein_1024_ctx *ctx, u8 *hash_val)
+{
+ size_t i, n, byte_cnt;
+ u64 x[SKEIN_1024_STATE_WORDS];
+ /* catch uninitialized context */
+ skein_assert_ret(ctx->h.b_cnt <= SKEIN_1024_BLOCK_BYTES, SKEIN_FAIL);
+
+ /* tag as the final block */
+ ctx->h.tweak[1] |= SKEIN_T1_FLAG_FINAL;
+ /* zero pad b[] if necessary */
+ if (ctx->h.b_cnt < SKEIN_1024_BLOCK_BYTES)
+ memset(&ctx->b[ctx->h.b_cnt], 0,
+ SKEIN_1024_BLOCK_BYTES - ctx->h.b_cnt);
+
+ /* process the final block */
+ skein_1024_process_block(ctx, ctx->b, 1, ctx->h.b_cnt);
+
+ /* now output the result */
+ /* total number of output bytes */
+ byte_cnt = (ctx->h.hash_bit_len + 7) >> 3;
+
+ /* run Threefish in "counter mode" to generate output */
+ /* zero out b[], so it can hold the counter */
+ memset(ctx->b, 0, sizeof(ctx->b));
+ /* keep a local copy of counter mode "key" */
+ memcpy(x, ctx->x, sizeof(x));
+ for (i = 0; i*SKEIN_1024_BLOCK_BYTES < byte_cnt; i++) {
+ /* build the counter block */
+ ((u64 *)ctx->b)[0] = skein_swap64((u64) i);
+ skein_start_new_type(ctx, OUT_FINAL);
+ /* run "counter mode" */
+ skein_1024_process_block(ctx, ctx->b, 1, sizeof(u64));
+ /* number of output bytes left to go */
+ n = byte_cnt - i*SKEIN_1024_BLOCK_BYTES;
+ if (n >= SKEIN_1024_BLOCK_BYTES)
+ n = SKEIN_1024_BLOCK_BYTES;
+ /* "output" the ctr mode bytes */
+ skein_put64_lsb_first(hash_val+i*SKEIN_1024_BLOCK_BYTES, ctx->x,
+ n);
+ /* restore the counter mode key for next time */
+ memcpy(ctx->x, x, sizeof(x));
+ }
+ return SKEIN_SUCCESS;
+}
+
+/**************** Functions to support MAC/tree hashing ***************/
+/* (this code is identical for Optimized and Reference versions) */
+
+/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
+/* finalize the hash computation and output the block, no OUTPUT stage */
+int skein_256_final_pad(struct skein_256_ctx *ctx, u8 *hash_val)
+{
+ /* catch uninitialized context */
+ skein_assert_ret(ctx->h.b_cnt <= SKEIN_256_BLOCK_BYTES, SKEIN_FAIL);
+
+ /* tag as the final block */
+ ctx->h.tweak[1] |= SKEIN_T1_FLAG_FINAL;
+ /* zero pad b[] if necessary */
+ if (ctx->h.b_cnt < SKEIN_256_BLOCK_BYTES)
+ memset(&ctx->b[ctx->h.b_cnt], 0,
+ SKEIN_256_BLOCK_BYTES - ctx->h.b_cnt);
+ /* process the final block */
+ skein_256_process_block(ctx, ctx->b, 1, ctx->h.b_cnt);
+
+ /* "output" the state bytes */
+ skein_put64_lsb_first(hash_val, ctx->x, SKEIN_256_BLOCK_BYTES);
+
+ return SKEIN_SUCCESS;
+}
+
+/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
+/* finalize the hash computation and output the block, no OUTPUT stage */
+int skein_512_final_pad(struct skein_512_ctx *ctx, u8 *hash_val)
+{
+ /* catch uninitialized context */
+ skein_assert_ret(ctx->h.b_cnt <= SKEIN_512_BLOCK_BYTES, SKEIN_FAIL);
+
+ /* tag as the final block */
+ ctx->h.tweak[1] |= SKEIN_T1_FLAG_FINAL;
+ /* zero pad b[] if necessary */
+ if (ctx->h.b_cnt < SKEIN_512_BLOCK_BYTES)
+ memset(&ctx->b[ctx->h.b_cnt], 0,
+ SKEIN_512_BLOCK_BYTES - ctx->h.b_cnt);
+ /* process the final block */
+ skein_512_process_block(ctx, ctx->b, 1, ctx->h.b_cnt);
+
+ /* "output" the state bytes */
+ skein_put64_lsb_first(hash_val, ctx->x, SKEIN_512_BLOCK_BYTES);
+
+ return SKEIN_SUCCESS;
+}
+
+/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
+/* finalize the hash computation and output the block, no OUTPUT stage */
+int skein_1024_final_pad(struct skein_1024_ctx *ctx, u8 *hash_val)
+{
+ /* catch uninitialized context */
+ skein_assert_ret(ctx->h.b_cnt <= SKEIN_1024_BLOCK_BYTES, SKEIN_FAIL);
+
+ /* tag as the final block */
+ ctx->h.tweak[1] |= SKEIN_T1_FLAG_FINAL;
+ /* zero pad b[] if necessary */
+ if (ctx->h.b_cnt < SKEIN_1024_BLOCK_BYTES)
+ memset(&ctx->b[ctx->h.b_cnt], 0,
+ SKEIN_1024_BLOCK_BYTES - ctx->h.b_cnt);
+ /* process the final block */
+ skein_1024_process_block(ctx, ctx->b, 1, ctx->h.b_cnt);
+
+ /* "output" the state bytes */
+ skein_put64_lsb_first(hash_val, ctx->x, SKEIN_1024_BLOCK_BYTES);
+
+ return SKEIN_SUCCESS;
+}
+
+#if SKEIN_TREE_HASH
+/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
+/* just do the OUTPUT stage */
+int skein_256_output(struct skein_256_ctx *ctx, u8 *hash_val)
+{
+ size_t i, n, byte_cnt;
+ u64 x[SKEIN_256_STATE_WORDS];
+ /* catch uninitialized context */
+ skein_assert_ret(ctx->h.b_cnt <= SKEIN_256_BLOCK_BYTES, SKEIN_FAIL);
+
+ /* now output the result */
+ /* total number of output bytes */
+ byte_cnt = (ctx->h.hash_bit_len + 7) >> 3;
+
+ /* run Threefish in "counter mode" to generate output */
+ /* zero out b[], so it can hold the counter */
+ memset(ctx->b, 0, sizeof(ctx->b));
+ /* keep a local copy of counter mode "key" */
+ memcpy(x, ctx->x, sizeof(x));
+ for (i = 0; i*SKEIN_256_BLOCK_BYTES < byte_cnt; i++) {
+ /* build the counter block */
+ ((u64 *)ctx->b)[0] = skein_swap64((u64) i);
+ skein_start_new_type(ctx, OUT_FINAL);
+ /* run "counter mode" */
+ skein_256_process_block(ctx, ctx->b, 1, sizeof(u64));
+ /* number of output bytes left to go */
+ n = byte_cnt - i*SKEIN_256_BLOCK_BYTES;
+ if (n >= SKEIN_256_BLOCK_BYTES)
+ n = SKEIN_256_BLOCK_BYTES;
+ /* "output" the ctr mode bytes */
+ skein_put64_lsb_first(hash_val+i*SKEIN_256_BLOCK_BYTES, ctx->x,
+ n);
+ /* restore the counter mode key for next time */
+ memcpy(ctx->x, x, sizeof(x));
+ }
+ return SKEIN_SUCCESS;
+}
+
+/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
+/* just do the OUTPUT stage */
+int skein_512_output(struct skein_512_ctx *ctx, u8 *hash_val)
+{
+ size_t i, n, byte_cnt;
+ u64 x[SKEIN_512_STATE_WORDS];
+ /* catch uninitialized context */
+ skein_assert_ret(ctx->h.b_cnt <= SKEIN_512_BLOCK_BYTES, SKEIN_FAIL);
+
+ /* now output the result */
+ /* total number of output bytes */
+ byte_cnt = (ctx->h.hash_bit_len + 7) >> 3;
+
+ /* run Threefish in "counter mode" to generate output */
+ /* zero out b[], so it can hold the counter */
+ memset(ctx->b, 0, sizeof(ctx->b));
+ /* keep a local copy of counter mode "key" */
+ memcpy(x, ctx->x, sizeof(x));
+ for (i = 0; i*SKEIN_512_BLOCK_BYTES < byte_cnt; i++) {
+ /* build the counter block */
+ ((u64 *)ctx->b)[0] = skein_swap64((u64) i);
+ skein_start_new_type(ctx, OUT_FINAL);
+ /* run "counter mode" */
+ skein_512_process_block(ctx, ctx->b, 1, sizeof(u64));
+ /* number of output bytes left to go */
+ n = byte_cnt - i*SKEIN_512_BLOCK_BYTES;
+ if (n >= SKEIN_512_BLOCK_BYTES)
+ n = SKEIN_512_BLOCK_BYTES;
+ /* "output" the ctr mode bytes */
+ skein_put64_lsb_first(hash_val+i*SKEIN_512_BLOCK_BYTES, ctx->x,
+ n);
+ /* restore the counter mode key for next time */
+ memcpy(ctx->x, x, sizeof(x));
+ }
+ return SKEIN_SUCCESS;
+}
+
+/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
+/* just do the OUTPUT stage */
+int skein_1024_output(struct skein_1024_ctx *ctx, u8 *hash_val)
+{
+ size_t i, n, byte_cnt;
+ u64 x[SKEIN_1024_STATE_WORDS];
+ /* catch uninitialized context */
+ skein_assert_ret(ctx->h.b_cnt <= SKEIN_1024_BLOCK_BYTES, SKEIN_FAIL);
+
+ /* now output the result */
+ /* total number of output bytes */
+ byte_cnt = (ctx->h.hash_bit_len + 7) >> 3;
+
+ /* run Threefish in "counter mode" to generate output */
+ /* zero out b[], so it can hold the counter */
+ memset(ctx->b, 0, sizeof(ctx->b));
+ /* keep a local copy of counter mode "key" */
+ memcpy(x, ctx->x, sizeof(x));
+ for (i = 0; i*SKEIN_1024_BLOCK_BYTES < byte_cnt; i++) {
+ /* build the counter block */
+ ((u64 *)ctx->b)[0] = skein_swap64((u64) i);
+ skein_start_new_type(ctx, OUT_FINAL);
+ /* run "counter mode" */
+ skein_1024_process_block(ctx, ctx->b, 1, sizeof(u64));
+ /* number of output bytes left to go */
+ n = byte_cnt - i*SKEIN_1024_BLOCK_BYTES;
+ if (n >= SKEIN_1024_BLOCK_BYTES)
+ n = SKEIN_1024_BLOCK_BYTES;
+ /* "output" the ctr mode bytes */
+ skein_put64_lsb_first(hash_val+i*SKEIN_1024_BLOCK_BYTES, ctx->x,
+ n);
+ /* restore the counter mode key for next time */
+ memcpy(ctx->x, x, sizeof(x));
+ }
+ return SKEIN_SUCCESS;
+}
+#endif
diff --git a/drivers/staging/skein/skein_base.h b/drivers/staging/skein/skein_base.h
new file mode 100644
index 000000000..3c7f8ad36
--- /dev/null
+++ b/drivers/staging/skein/skein_base.h
@@ -0,0 +1,335 @@
+#ifndef _SKEIN_H_
+#define _SKEIN_H_ 1
+/**************************************************************************
+**
+** Interface declarations and internal definitions for Skein hashing.
+**
+** Source code author: Doug Whiting, 2008.
+**
+** This algorithm and source code is released to the public domain.
+**
+***************************************************************************
+**
+** The following compile-time switches may be defined to control some
+** tradeoffs between speed, code size, error checking, and security.
+**
+** The "default" note explains what happens when the switch is not defined.
+**
+** SKEIN_ERR_CHECK -- how error checking is handled inside Skein
+** code. If not defined, most error checking
+** is disabled (for performance). Otherwise,
+** the switch value is interpreted as:
+** 0: use assert() to flag errors
+** 1: return SKEIN_FAIL to flag errors
+**
+***************************************************************************/
+
+/*Skein digest sizes for crypto api*/
+#define SKEIN256_DIGEST_BIT_SIZE 256
+#define SKEIN512_DIGEST_BIT_SIZE 512
+#define SKEIN1024_DIGEST_BIT_SIZE 1024
+
+/* below two prototype assume we are handed aligned data */
+#define skein_put64_lsb_first(dst08, src64, b_cnt) memcpy(dst08, src64, b_cnt)
+#define skein_get64_lsb_first(dst64, src08, w_cnt) \
+ memcpy(dst64, src08, 8*(w_cnt))
+#define skein_swap64(w64) (w64)
+
+enum {
+ SKEIN_SUCCESS = 0, /* return codes from Skein calls */
+ SKEIN_FAIL = 1,
+ SKEIN_BAD_HASHLEN = 2
+};
+
+#define SKEIN_MODIFIER_WORDS 2 /* number of modifier (tweak) words */
+
+#define SKEIN_256_STATE_WORDS 4
+#define SKEIN_512_STATE_WORDS 8
+#define SKEIN_1024_STATE_WORDS 16
+#define SKEIN_MAX_STATE_WORDS 16
+
+#define SKEIN_256_STATE_BYTES (8*SKEIN_256_STATE_WORDS)
+#define SKEIN_512_STATE_BYTES (8*SKEIN_512_STATE_WORDS)
+#define SKEIN_1024_STATE_BYTES (8*SKEIN_1024_STATE_WORDS)
+
+#define SKEIN_256_STATE_BITS (64*SKEIN_256_STATE_WORDS)
+#define SKEIN_512_STATE_BITS (64*SKEIN_512_STATE_WORDS)
+#define SKEIN_1024_STATE_BITS (64*SKEIN_1024_STATE_WORDS)
+
+#define SKEIN_256_BLOCK_BYTES (8*SKEIN_256_STATE_WORDS)
+#define SKEIN_512_BLOCK_BYTES (8*SKEIN_512_STATE_WORDS)
+#define SKEIN_1024_BLOCK_BYTES (8*SKEIN_1024_STATE_WORDS)
+
+struct skein_ctx_hdr {
+ size_t hash_bit_len; /* size of hash result, in bits */
+ size_t b_cnt; /* current byte count in buffer b[] */
+ u64 tweak[SKEIN_MODIFIER_WORDS]; /* tweak[0]=byte cnt, tweak[1]=flags */
+};
+
+struct skein_256_ctx { /* 256-bit Skein hash context structure */
+ struct skein_ctx_hdr h; /* common header context variables */
+ u64 x[SKEIN_256_STATE_WORDS]; /* chaining variables */
+ u8 b[SKEIN_256_BLOCK_BYTES]; /* partial block buf (8-byte aligned) */
+};
+
+struct skein_512_ctx { /* 512-bit Skein hash context structure */
+ struct skein_ctx_hdr h; /* common header context variables */
+ u64 x[SKEIN_512_STATE_WORDS]; /* chaining variables */
+ u8 b[SKEIN_512_BLOCK_BYTES]; /* partial block buf (8-byte aligned) */
+};
+
+struct skein_1024_ctx { /* 1024-bit Skein hash context structure */
+ struct skein_ctx_hdr h; /* common header context variables */
+ u64 x[SKEIN_1024_STATE_WORDS]; /* chaining variables */
+ u8 b[SKEIN_1024_BLOCK_BYTES]; /* partial block buf (8-byte aligned) */
+};
+
+static inline u64 rotl_64(u64 x, u8 N)
+{
+ return (x << N) | (x >> (64 - N));
+}
+
+/* Skein APIs for (incremental) "straight hashing" */
+int skein_256_init(struct skein_256_ctx *ctx, size_t hash_bit_len);
+int skein_512_init(struct skein_512_ctx *ctx, size_t hash_bit_len);
+int skein_1024_init(struct skein_1024_ctx *ctx, size_t hash_bit_len);
+
+int skein_256_update(struct skein_256_ctx *ctx, const u8 *msg,
+ size_t msg_byte_cnt);
+int skein_512_update(struct skein_512_ctx *ctx, const u8 *msg,
+ size_t msg_byte_cnt);
+int skein_1024_update(struct skein_1024_ctx *ctx, const u8 *msg,
+ size_t msg_byte_cnt);
+
+int skein_256_final(struct skein_256_ctx *ctx, u8 *hash_val);
+int skein_512_final(struct skein_512_ctx *ctx, u8 *hash_val);
+int skein_1024_final(struct skein_1024_ctx *ctx, u8 *hash_val);
+
+/*
+** Skein APIs for "extended" initialization: MAC keys, tree hashing.
+** After an init_ext() call, just use update/final calls as with init().
+**
+** Notes: Same parameters as _init() calls, plus tree_info/key/key_bytes.
+** When key_bytes == 0 and tree_info == SKEIN_SEQUENTIAL,
+** the results of init_ext() are identical to calling init().
+** The function init() may be called once to "precompute" the IV for
+** a given hash_bit_len value, then by saving a copy of the context
+** the IV computation may be avoided in later calls.
+** Similarly, the function init_ext() may be called once per MAC key
+** to precompute the MAC IV, then a copy of the context saved and
+** reused for each new MAC computation.
+**/
+int skein_256_init_ext(struct skein_256_ctx *ctx, size_t hash_bit_len,
+ u64 tree_info, const u8 *key, size_t key_bytes);
+int skein_512_init_ext(struct skein_512_ctx *ctx, size_t hash_bit_len,
+ u64 tree_info, const u8 *key, size_t key_bytes);
+int skein_1024_init_ext(struct skein_1024_ctx *ctx, size_t hash_bit_len,
+ u64 tree_info, const u8 *key, size_t key_bytes);
+
+/*
+** Skein APIs for MAC and tree hash:
+** final_pad: pad, do final block, but no OUTPUT type
+** output: do just the output stage
+*/
+int skein_256_final_pad(struct skein_256_ctx *ctx, u8 *hash_val);
+int skein_512_final_pad(struct skein_512_ctx *ctx, u8 *hash_val);
+int skein_1024_final_pad(struct skein_1024_ctx *ctx, u8 *hash_val);
+
+#ifndef SKEIN_TREE_HASH
+#define SKEIN_TREE_HASH (1)
+#endif
+#if SKEIN_TREE_HASH
+int skein_256_output(struct skein_256_ctx *ctx, u8 *hash_val);
+int skein_512_output(struct skein_512_ctx *ctx, u8 *hash_val);
+int skein_1024_output(struct skein_1024_ctx *ctx, u8 *hash_val);
+#endif
+
+/*****************************************************************
+** "Internal" Skein definitions
+** -- not needed for sequential hashing API, but will be
+** helpful for other uses of Skein (e.g., tree hash mode).
+** -- included here so that they can be shared between
+** reference and optimized code.
+******************************************************************/
+
+/* tweak word tweak[1]: bit field starting positions */
+#define SKEIN_T1_BIT(BIT) ((BIT) - 64) /* second word */
+
+#define SKEIN_T1_POS_TREE_LVL SKEIN_T1_BIT(112) /* 112..118 hash tree level */
+#define SKEIN_T1_POS_BIT_PAD SKEIN_T1_BIT(119) /* 119 part. final in byte */
+#define SKEIN_T1_POS_BLK_TYPE SKEIN_T1_BIT(120) /* 120..125 type field `*/
+#define SKEIN_T1_POS_FIRST SKEIN_T1_BIT(126) /* 126 first blk flag */
+#define SKEIN_T1_POS_FINAL SKEIN_T1_BIT(127) /* 127 final blk flag */
+
+/* tweak word tweak[1]: flag bit definition(s) */
+#define SKEIN_T1_FLAG_FIRST (((u64) 1) << SKEIN_T1_POS_FIRST)
+#define SKEIN_T1_FLAG_FINAL (((u64) 1) << SKEIN_T1_POS_FINAL)
+#define SKEIN_T1_FLAG_BIT_PAD (((u64) 1) << SKEIN_T1_POS_BIT_PAD)
+
+/* tweak word tweak[1]: tree level bit field mask */
+#define SKEIN_T1_TREE_LVL_MASK (((u64)0x7F) << SKEIN_T1_POS_TREE_LVL)
+#define SKEIN_T1_TREE_LEVEL(n) (((u64) (n)) << SKEIN_T1_POS_TREE_LVL)
+
+/* tweak word tweak[1]: block type field */
+#define SKEIN_BLK_TYPE_KEY (0) /* key, for MAC and KDF */
+#define SKEIN_BLK_TYPE_CFG (4) /* configuration block */
+#define SKEIN_BLK_TYPE_PERS (8) /* personalization string */
+#define SKEIN_BLK_TYPE_PK (12) /* pubkey (for digital sigs) */
+#define SKEIN_BLK_TYPE_KDF (16) /* key identifier for KDF */
+#define SKEIN_BLK_TYPE_NONCE (20) /* nonce for PRNG */
+#define SKEIN_BLK_TYPE_MSG (48) /* message processing */
+#define SKEIN_BLK_TYPE_OUT (63) /* output stage */
+#define SKEIN_BLK_TYPE_MASK (63) /* bit field mask */
+
+#define SKEIN_T1_BLK_TYPE(T) (((u64) (SKEIN_BLK_TYPE_##T)) << \
+ SKEIN_T1_POS_BLK_TYPE)
+#define SKEIN_T1_BLK_TYPE_KEY SKEIN_T1_BLK_TYPE(KEY) /* for MAC and KDF */
+#define SKEIN_T1_BLK_TYPE_CFG SKEIN_T1_BLK_TYPE(CFG) /* config block */
+#define SKEIN_T1_BLK_TYPE_PERS SKEIN_T1_BLK_TYPE(PERS) /* personalization */
+#define SKEIN_T1_BLK_TYPE_PK SKEIN_T1_BLK_TYPE(PK) /* pubkey (for sigs) */
+#define SKEIN_T1_BLK_TYPE_KDF SKEIN_T1_BLK_TYPE(KDF) /* key ident for KDF */
+#define SKEIN_T1_BLK_TYPE_NONCE SKEIN_T1_BLK_TYPE(NONCE)/* nonce for PRNG */
+#define SKEIN_T1_BLK_TYPE_MSG SKEIN_T1_BLK_TYPE(MSG) /* message processing */
+#define SKEIN_T1_BLK_TYPE_OUT SKEIN_T1_BLK_TYPE(OUT) /* output stage */
+#define SKEIN_T1_BLK_TYPE_MASK SKEIN_T1_BLK_TYPE(MASK) /* field bit mask */
+
+#define SKEIN_T1_BLK_TYPE_CFG_FINAL (SKEIN_T1_BLK_TYPE_CFG | \
+ SKEIN_T1_FLAG_FINAL)
+#define SKEIN_T1_BLK_TYPE_OUT_FINAL (SKEIN_T1_BLK_TYPE_OUT | \
+ SKEIN_T1_FLAG_FINAL)
+
+#define SKEIN_VERSION (1)
+
+#ifndef SKEIN_ID_STRING_LE /* allow compile-time personalization */
+#define SKEIN_ID_STRING_LE (0x33414853) /* "SHA3" (little-endian)*/
+#endif
+
+#define SKEIN_MK_64(hi32, lo32) ((lo32) + (((u64) (hi32)) << 32))
+#define SKEIN_SCHEMA_VER SKEIN_MK_64(SKEIN_VERSION, SKEIN_ID_STRING_LE)
+#define SKEIN_KS_PARITY SKEIN_MK_64(0x1BD11BDA, 0xA9FC1A22)
+
+#define SKEIN_CFG_STR_LEN (4*8)
+
+/* bit field definitions in config block tree_info word */
+#define SKEIN_CFG_TREE_LEAF_SIZE_POS (0)
+#define SKEIN_CFG_TREE_NODE_SIZE_POS (8)
+#define SKEIN_CFG_TREE_MAX_LEVEL_POS (16)
+
+#define SKEIN_CFG_TREE_LEAF_SIZE_MSK (((u64)0xFF) << \
+ SKEIN_CFG_TREE_LEAF_SIZE_POS)
+#define SKEIN_CFG_TREE_NODE_SIZE_MSK (((u64)0xFF) << \
+ SKEIN_CFG_TREE_NODE_SIZE_POS)
+#define SKEIN_CFG_TREE_MAX_LEVEL_MSK (((u64)0xFF) << \
+ SKEIN_CFG_TREE_MAX_LEVEL_POS)
+
+#define SKEIN_CFG_TREE_INFO(leaf, node, max_lvl) \
+ ((((u64)(leaf)) << SKEIN_CFG_TREE_LEAF_SIZE_POS) | \
+ (((u64)(node)) << SKEIN_CFG_TREE_NODE_SIZE_POS) | \
+ (((u64)(max_lvl)) << SKEIN_CFG_TREE_MAX_LEVEL_POS))
+
+/* use as tree_info in InitExt() call for sequential processing */
+#define SKEIN_CFG_TREE_INFO_SEQUENTIAL SKEIN_CFG_TREE_INFO(0, 0, 0)
+
+/*
+** Skein macros for getting/setting tweak words, etc.
+** These are useful for partial input bytes, hash tree init/update, etc.
+**/
+#define skein_get_tweak(ctx_ptr, TWK_NUM) ((ctx_ptr)->h.tweak[TWK_NUM])
+#define skein_set_tweak(ctx_ptr, TWK_NUM, t_val) { \
+ (ctx_ptr)->h.tweak[TWK_NUM] = (t_val); \
+ }
+
+#define skein_get_T0(ctx_ptr) skein_get_tweak(ctx_ptr, 0)
+#define skein_get_T1(ctx_ptr) skein_get_tweak(ctx_ptr, 1)
+#define skein_set_T0(ctx_ptr, T0) skein_set_tweak(ctx_ptr, 0, T0)
+#define skein_set_T1(ctx_ptr, T1) skein_set_tweak(ctx_ptr, 1, T1)
+
+/* set both tweak words at once */
+#define skein_set_T0_T1(ctx_ptr, T0, T1) \
+ { \
+ skein_set_T0(ctx_ptr, (T0)); \
+ skein_set_T1(ctx_ptr, (T1)); \
+ }
+
+#define skein_set_type(ctx_ptr, BLK_TYPE) \
+ skein_set_T1(ctx_ptr, SKEIN_T1_BLK_TYPE_##BLK_TYPE)
+
+/*
+ * setup for starting with a new type:
+ * h.tweak[0]=0; h.tweak[1] = NEW_TYPE; h.b_cnt=0;
+ */
+#define skein_start_new_type(ctx_ptr, BLK_TYPE) { \
+ skein_set_T0_T1(ctx_ptr, 0, SKEIN_T1_FLAG_FIRST | \
+ SKEIN_T1_BLK_TYPE_##BLK_TYPE); \
+ (ctx_ptr)->h.b_cnt = 0; \
+ }
+
+#define skein_clear_first_flag(hdr) { \
+ (hdr).tweak[1] &= ~SKEIN_T1_FLAG_FIRST; \
+ }
+#define skein_set_bit_pad_flag(hdr) { \
+ (hdr).tweak[1] |= SKEIN_T1_FLAG_BIT_PAD; \
+ }
+
+#define skein_set_tree_level(hdr, height) { \
+ (hdr).tweak[1] |= SKEIN_T1_TREE_LEVEL(height); \
+ }
+
+/* ignore all asserts, for performance */
+#define skein_assert_ret(x, ret_code)
+#define skein_assert(x)
+
+/*****************************************************************
+** Skein block function constants (shared across Ref and Opt code)
+******************************************************************/
+enum {
+ /* SKEIN_256 round rotation constants */
+ R_256_0_0 = 14, R_256_0_1 = 16,
+ R_256_1_0 = 52, R_256_1_1 = 57,
+ R_256_2_0 = 23, R_256_2_1 = 40,
+ R_256_3_0 = 5, R_256_3_1 = 37,
+ R_256_4_0 = 25, R_256_4_1 = 33,
+ R_256_5_0 = 46, R_256_5_1 = 12,
+ R_256_6_0 = 58, R_256_6_1 = 22,
+ R_256_7_0 = 32, R_256_7_1 = 32,
+
+ /* SKEIN_512 round rotation constants */
+ R_512_0_0 = 46, R_512_0_1 = 36, R_512_0_2 = 19, R_512_0_3 = 37,
+ R_512_1_0 = 33, R_512_1_1 = 27, R_512_1_2 = 14, R_512_1_3 = 42,
+ R_512_2_0 = 17, R_512_2_1 = 49, R_512_2_2 = 36, R_512_2_3 = 39,
+ R_512_3_0 = 44, R_512_3_1 = 9, R_512_3_2 = 54, R_512_3_3 = 56,
+ R_512_4_0 = 39, R_512_4_1 = 30, R_512_4_2 = 34, R_512_4_3 = 24,
+ R_512_5_0 = 13, R_512_5_1 = 50, R_512_5_2 = 10, R_512_5_3 = 17,
+ R_512_6_0 = 25, R_512_6_1 = 29, R_512_6_2 = 39, R_512_6_3 = 43,
+ R_512_7_0 = 8, R_512_7_1 = 35, R_512_7_2 = 56, R_512_7_3 = 22,
+
+ /* SKEIN_1024 round rotation constants */
+ R1024_0_0 = 24, R1024_0_1 = 13, R1024_0_2 = 8, R1024_0_3 = 47,
+ R1024_0_4 = 8, R1024_0_5 = 17, R1024_0_6 = 22, R1024_0_7 = 37,
+ R1024_1_0 = 38, R1024_1_1 = 19, R1024_1_2 = 10, R1024_1_3 = 55,
+ R1024_1_4 = 49, R1024_1_5 = 18, R1024_1_6 = 23, R1024_1_7 = 52,
+ R1024_2_0 = 33, R1024_2_1 = 4, R1024_2_2 = 51, R1024_2_3 = 13,
+ R1024_2_4 = 34, R1024_2_5 = 41, R1024_2_6 = 59, R1024_2_7 = 17,
+ R1024_3_0 = 5, R1024_3_1 = 20, R1024_3_2 = 48, R1024_3_3 = 41,
+ R1024_3_4 = 47, R1024_3_5 = 28, R1024_3_6 = 16, R1024_3_7 = 25,
+ R1024_4_0 = 41, R1024_4_1 = 9, R1024_4_2 = 37, R1024_4_3 = 31,
+ R1024_4_4 = 12, R1024_4_5 = 47, R1024_4_6 = 44, R1024_4_7 = 30,
+ R1024_5_0 = 16, R1024_5_1 = 34, R1024_5_2 = 56, R1024_5_3 = 51,
+ R1024_5_4 = 4, R1024_5_5 = 53, R1024_5_6 = 42, R1024_5_7 = 41,
+ R1024_6_0 = 31, R1024_6_1 = 44, R1024_6_2 = 47, R1024_6_3 = 46,
+ R1024_6_4 = 19, R1024_6_5 = 42, R1024_6_6 = 44, R1024_6_7 = 25,
+ R1024_7_0 = 9, R1024_7_1 = 48, R1024_7_2 = 35, R1024_7_3 = 52,
+ R1024_7_4 = 23, R1024_7_5 = 31, R1024_7_6 = 37, R1024_7_7 = 20
+};
+
+#ifndef SKEIN_ROUNDS
+#define SKEIN_256_ROUNDS_TOTAL (72) /* # rounds for diff block sizes */
+#define SKEIN_512_ROUNDS_TOTAL (72)
+#define SKEIN_1024_ROUNDS_TOTAL (80)
+#else /* allow command-line define in range 8*(5..14) */
+#define SKEIN_256_ROUNDS_TOTAL (8*((((SKEIN_ROUNDS/100) + 5) % 10) + 5))
+#define SKEIN_512_ROUNDS_TOTAL (8*((((SKEIN_ROUNDS/10) + 5) % 10) + 5))
+#define SKEIN_1024_ROUNDS_TOTAL (8*((((SKEIN_ROUNDS) + 5) % 10) + 5))
+#endif
+
+#endif /* ifndef _SKEIN_H_ */
diff --git a/drivers/staging/skein/skein_block.c b/drivers/staging/skein/skein_block.c
new file mode 100644
index 000000000..b0cd93573
--- /dev/null
+++ b/drivers/staging/skein/skein_block.c
@@ -0,0 +1,782 @@
+/***********************************************************************
+**
+** Implementation of the Skein block functions.
+**
+** Source code author: Doug Whiting, 2008.
+**
+** This algorithm and source code is released to the public domain.
+**
+** Compile-time switches:
+**
+** SKEIN_USE_ASM -- set bits (256/512/1024) to select which
+** versions use ASM code for block processing
+** [default: use C for all block sizes]
+**
+************************************************************************/
+
+#include <linux/string.h>
+#include "skein_base.h"
+#include "skein_block.h"
+
+#ifndef SKEIN_USE_ASM
+#define SKEIN_USE_ASM (0) /* default is all C code (no ASM) */
+#endif
+
+#ifndef SKEIN_LOOP
+#define SKEIN_LOOP 001 /* default: unroll 256 and 512, but not 1024 */
+#endif
+
+#define BLK_BITS (WCNT * 64) /* some useful definitions for code here */
+#define KW_TWK_BASE (0)
+#define KW_KEY_BASE (3)
+#define ks (kw + KW_KEY_BASE)
+#define ts (kw + KW_TWK_BASE)
+
+#ifdef SKEIN_DEBUG
+#define debug_save_tweak(ctx) \
+{ \
+ ctx->h.tweak[0] = ts[0]; \
+ ctx->h.tweak[1] = ts[1]; \
+}
+#else
+#define debug_save_tweak(ctx)
+#endif
+
+#if !(SKEIN_USE_ASM & 256)
+#undef RCNT
+#define RCNT (SKEIN_256_ROUNDS_TOTAL / 8)
+#ifdef SKEIN_LOOP /* configure how much to unroll the loop */
+#define SKEIN_UNROLL_256 (((SKEIN_LOOP) / 100) % 10)
+#else
+#define SKEIN_UNROLL_256 (0)
+#endif
+
+#if SKEIN_UNROLL_256
+#if (RCNT % SKEIN_UNROLL_256)
+#error "Invalid SKEIN_UNROLL_256" /* sanity check on unroll count */
+#endif
+#endif
+#define ROUND256(p0, p1, p2, p3, ROT, r_num) \
+do { \
+ X##p0 += X##p1; \
+ X##p1 = rotl_64(X##p1, ROT##_0); \
+ X##p1 ^= X##p0; \
+ X##p2 += X##p3; \
+ X##p3 = rotl_64(X##p3, ROT##_1); \
+ X##p3 ^= X##p2; \
+} while (0)
+
+#if SKEIN_UNROLL_256 == 0
+#define R256(p0, p1, p2, p3, ROT, r_num) /* fully unrolled */ \
+ ROUND256(p0, p1, p2, p3, ROT, r_num)
+
+#define I256(R) \
+do { \
+ /* inject the key schedule value */ \
+ X0 += ks[((R) + 1) % 5]; \
+ X1 += ks[((R) + 2) % 5] + ts[((R) + 1) % 3]; \
+ X2 += ks[((R) + 3) % 5] + ts[((R) + 2) % 3]; \
+ X3 += ks[((R) + 4) % 5] + (R) + 1; \
+} while (0)
+#else
+/* looping version */
+#define R256(p0, p1, p2, p3, ROT, r_num) ROUND256(p0, p1, p2, p3, ROT, r_num)
+
+#define I256(R) \
+do { \
+ /* inject the key schedule value */ \
+ X0 += ks[r + (R) + 0]; \
+ X1 += ks[r + (R) + 1] + ts[r + (R) + 0]; \
+ X2 += ks[r + (R) + 2] + ts[r + (R) + 1]; \
+ X3 += ks[r + (R) + 3] + r + (R); \
+ /* rotate key schedule */ \
+ ks[r + (R) + 4] = ks[r + (R) - 1]; \
+ ts[r + (R) + 2] = ts[r + (R) - 1]; \
+} while (0)
+#endif
+#define R256_8_ROUNDS(R) \
+do { \
+ R256(0, 1, 2, 3, R_256_0, 8 * (R) + 1); \
+ R256(0, 3, 2, 1, R_256_1, 8 * (R) + 2); \
+ R256(0, 1, 2, 3, R_256_2, 8 * (R) + 3); \
+ R256(0, 3, 2, 1, R_256_3, 8 * (R) + 4); \
+ I256(2 * (R)); \
+ R256(0, 1, 2, 3, R_256_4, 8 * (R) + 5); \
+ R256(0, 3, 2, 1, R_256_5, 8 * (R) + 6); \
+ R256(0, 1, 2, 3, R_256_6, 8 * (R) + 7); \
+ R256(0, 3, 2, 1, R_256_7, 8 * (R) + 8); \
+ I256(2 * (R) + 1); \
+} while (0)
+
+#define R256_UNROLL_R(NN) \
+ ((SKEIN_UNROLL_256 == 0 && \
+ SKEIN_256_ROUNDS_TOTAL / 8 > (NN)) || \
+ (SKEIN_UNROLL_256 > (NN)))
+
+#if (SKEIN_UNROLL_256 > 14)
+#error "need more unrolling in skein_256_process_block"
+#endif
+#endif
+
+#if !(SKEIN_USE_ASM & 512)
+#undef RCNT
+#define RCNT (SKEIN_512_ROUNDS_TOTAL/8)
+
+#ifdef SKEIN_LOOP /* configure how much to unroll the loop */
+#define SKEIN_UNROLL_512 (((SKEIN_LOOP)/10)%10)
+#else
+#define SKEIN_UNROLL_512 (0)
+#endif
+
+#if SKEIN_UNROLL_512
+#if (RCNT % SKEIN_UNROLL_512)
+#error "Invalid SKEIN_UNROLL_512" /* sanity check on unroll count */
+#endif
+#endif
+#define ROUND512(p0, p1, p2, p3, p4, p5, p6, p7, ROT, r_num) \
+do { \
+ X##p0 += X##p1; \
+ X##p1 = rotl_64(X##p1, ROT##_0); \
+ X##p1 ^= X##p0; \
+ X##p2 += X##p3; \
+ X##p3 = rotl_64(X##p3, ROT##_1); \
+ X##p3 ^= X##p2; \
+ X##p4 += X##p5; \
+ X##p5 = rotl_64(X##p5, ROT##_2); \
+ X##p5 ^= X##p4; \
+ X##p6 += X##p7; X##p7 = rotl_64(X##p7, ROT##_3); \
+ X##p7 ^= X##p6; \
+} while (0)
+
+#if SKEIN_UNROLL_512 == 0
+#define R512(p0, p1, p2, p3, p4, p5, p6, p7, ROT, r_num) /* unrolled */ \
+ ROUND512(p0, p1, p2, p3, p4, p5, p6, p7, ROT, r_num)
+
+#define I512(R) \
+do { \
+ /* inject the key schedule value */ \
+ X0 += ks[((R) + 1) % 9]; \
+ X1 += ks[((R) + 2) % 9]; \
+ X2 += ks[((R) + 3) % 9]; \
+ X3 += ks[((R) + 4) % 9]; \
+ X4 += ks[((R) + 5) % 9]; \
+ X5 += ks[((R) + 6) % 9] + ts[((R) + 1) % 3]; \
+ X6 += ks[((R) + 7) % 9] + ts[((R) + 2) % 3]; \
+ X7 += ks[((R) + 8) % 9] + (R) + 1; \
+} while (0)
+
+#else /* looping version */
+#define R512(p0, p1, p2, p3, p4, p5, p6, p7, ROT, r_num) \
+ ROUND512(p0, p1, p2, p3, p4, p5, p6, p7, ROT, r_num) \
+
+#define I512(R) \
+do { \
+ /* inject the key schedule value */ \
+ X0 += ks[r + (R) + 0]; \
+ X1 += ks[r + (R) + 1]; \
+ X2 += ks[r + (R) + 2]; \
+ X3 += ks[r + (R) + 3]; \
+ X4 += ks[r + (R) + 4]; \
+ X5 += ks[r + (R) + 5] + ts[r + (R) + 0]; \
+ X6 += ks[r + (R) + 6] + ts[r + (R) + 1]; \
+ X7 += ks[r + (R) + 7] + r + (R); \
+ /* rotate key schedule */ \
+ ks[r + (R) + 8] = ks[r + (R) - 1]; \
+ ts[r + (R) + 2] = ts[r + (R) - 1]; \
+} while (0)
+#endif /* end of looped code definitions */
+#define R512_8_ROUNDS(R) /* do 8 full rounds */ \
+do { \
+ R512(0, 1, 2, 3, 4, 5, 6, 7, R_512_0, 8 * (R) + 1); \
+ R512(2, 1, 4, 7, 6, 5, 0, 3, R_512_1, 8 * (R) + 2); \
+ R512(4, 1, 6, 3, 0, 5, 2, 7, R_512_2, 8 * (R) + 3); \
+ R512(6, 1, 0, 7, 2, 5, 4, 3, R_512_3, 8 * (R) + 4); \
+ I512(2 * (R)); \
+ R512(0, 1, 2, 3, 4, 5, 6, 7, R_512_4, 8 * (R) + 5); \
+ R512(2, 1, 4, 7, 6, 5, 0, 3, R_512_5, 8 * (R) + 6); \
+ R512(4, 1, 6, 3, 0, 5, 2, 7, R_512_6, 8 * (R) + 7); \
+ R512(6, 1, 0, 7, 2, 5, 4, 3, R_512_7, 8 * (R) + 8); \
+ I512(2 * (R) + 1); /* and key injection */ \
+} while (0)
+#define R512_UNROLL_R(NN) \
+ ((SKEIN_UNROLL_512 == 0 && \
+ SKEIN_512_ROUNDS_TOTAL/8 > (NN)) || \
+ (SKEIN_UNROLL_512 > (NN)))
+
+#if (SKEIN_UNROLL_512 > 14)
+#error "need more unrolling in skein_512_process_block"
+#endif
+#endif
+
+#if !(SKEIN_USE_ASM & 1024)
+#undef RCNT
+#define RCNT (SKEIN_1024_ROUNDS_TOTAL/8)
+#ifdef SKEIN_LOOP /* configure how much to unroll the loop */
+#define SKEIN_UNROLL_1024 ((SKEIN_LOOP)%10)
+#else
+#define SKEIN_UNROLL_1024 (0)
+#endif
+
+#if (SKEIN_UNROLL_1024 != 0)
+#if (RCNT % SKEIN_UNROLL_1024)
+#error "Invalid SKEIN_UNROLL_1024" /* sanity check on unroll count */
+#endif
+#endif
+#define ROUND1024(p0, p1, p2, p3, p4, p5, p6, p7, p8, p9, pA, pB, pC, pD, pE, \
+ pF, ROT, r_num) \
+do { \
+ X##p0 += X##p1; \
+ X##p1 = rotl_64(X##p1, ROT##_0); \
+ X##p1 ^= X##p0; \
+ X##p2 += X##p3; \
+ X##p3 = rotl_64(X##p3, ROT##_1); \
+ X##p3 ^= X##p2; \
+ X##p4 += X##p5; \
+ X##p5 = rotl_64(X##p5, ROT##_2); \
+ X##p5 ^= X##p4; \
+ X##p6 += X##p7; \
+ X##p7 = rotl_64(X##p7, ROT##_3); \
+ X##p7 ^= X##p6; \
+ X##p8 += X##p9; \
+ X##p9 = rotl_64(X##p9, ROT##_4); \
+ X##p9 ^= X##p8; \
+ X##pA += X##pB; \
+ X##pB = rotl_64(X##pB, ROT##_5); \
+ X##pB ^= X##pA; \
+ X##pC += X##pD; \
+ X##pD = rotl_64(X##pD, ROT##_6); \
+ X##pD ^= X##pC; \
+ X##pE += X##pF; \
+ X##pF = rotl_64(X##pF, ROT##_7); \
+ X##pF ^= X##pE; \
+} while (0)
+
+#if SKEIN_UNROLL_1024 == 0
+#define R1024(p0, p1, p2, p3, p4, p5, p6, p7, p8, p9, pA, pB, pC, pD, pE, pF, \
+ ROT, rn) \
+ ROUND1024(p0, p1, p2, p3, p4, p5, p6, p7, p8, p9, pA, pB, pC, pD, pE, \
+ pF, ROT, rn) \
+
+#define I1024(R) \
+do { \
+ /* inject the key schedule value */ \
+ X00 += ks[((R) + 1) % 17]; \
+ X01 += ks[((R) + 2) % 17]; \
+ X02 += ks[((R) + 3) % 17]; \
+ X03 += ks[((R) + 4) % 17]; \
+ X04 += ks[((R) + 5) % 17]; \
+ X05 += ks[((R) + 6) % 17]; \
+ X06 += ks[((R) + 7) % 17]; \
+ X07 += ks[((R) + 8) % 17]; \
+ X08 += ks[((R) + 9) % 17]; \
+ X09 += ks[((R) + 10) % 17]; \
+ X10 += ks[((R) + 11) % 17]; \
+ X11 += ks[((R) + 12) % 17]; \
+ X12 += ks[((R) + 13) % 17]; \
+ X13 += ks[((R) + 14) % 17] + ts[((R) + 1) % 3]; \
+ X14 += ks[((R) + 15) % 17] + ts[((R) + 2) % 3]; \
+ X15 += ks[((R) + 16) % 17] + (R) + 1; \
+} while (0)
+#else /* looping version */
+#define R1024(p0, p1, p2, p3, p4, p5, p6, p7, p8, p9, pA, pB, pC, pD, pE, pF, \
+ ROT, rn) \
+ ROUND1024(p0, p1, p2, p3, p4, p5, p6, p7, p8, p9, pA, pB, pC, pD, pE, \
+ pF, ROT, rn) \
+
+#define I1024(R) \
+do { \
+ /* inject the key schedule value */ \
+ X00 += ks[r + (R) + 0]; \
+ X01 += ks[r + (R) + 1]; \
+ X02 += ks[r + (R) + 2]; \
+ X03 += ks[r + (R) + 3]; \
+ X04 += ks[r + (R) + 4]; \
+ X05 += ks[r + (R) + 5]; \
+ X06 += ks[r + (R) + 6]; \
+ X07 += ks[r + (R) + 7]; \
+ X08 += ks[r + (R) + 8]; \
+ X09 += ks[r + (R) + 9]; \
+ X10 += ks[r + (R) + 10]; \
+ X11 += ks[r + (R) + 11]; \
+ X12 += ks[r + (R) + 12]; \
+ X13 += ks[r + (R) + 13] + ts[r + (R) + 0]; \
+ X14 += ks[r + (R) + 14] + ts[r + (R) + 1]; \
+ X15 += ks[r + (R) + 15] + r + (R); \
+ /* rotate key schedule */ \
+ ks[r + (R) + 16] = ks[r + (R) - 1]; \
+ ts[r + (R) + 2] = ts[r + (R) - 1]; \
+} while (0)
+
+#endif
+#define R1024_8_ROUNDS(R) \
+do { \
+ R1024(00, 01, 02, 03, 04, 05, 06, 07, 08, 09, 10, 11, 12, 13, 14, 15, \
+ R1024_0, 8*(R) + 1); \
+ R1024(00, 09, 02, 13, 06, 11, 04, 15, 10, 07, 12, 03, 14, 05, 08, 01, \
+ R1024_1, 8*(R) + 2); \
+ R1024(00, 07, 02, 05, 04, 03, 06, 01, 12, 15, 14, 13, 08, 11, 10, 09, \
+ R1024_2, 8*(R) + 3); \
+ R1024(00, 15, 02, 11, 06, 13, 04, 09, 14, 01, 08, 05, 10, 03, 12, 07, \
+ R1024_3, 8*(R) + 4); \
+ I1024(2*(R)); \
+ R1024(00, 01, 02, 03, 04, 05, 06, 07, 08, 09, 10, 11, 12, 13, 14, 15, \
+ R1024_4, 8*(R) + 5); \
+ R1024(00, 09, 02, 13, 06, 11, 04, 15, 10, 07, 12, 03, 14, 05, 08, 01, \
+ R1024_5, 8*(R) + 6); \
+ R1024(00, 07, 02, 05, 04, 03, 06, 01, 12, 15, 14, 13, 08, 11, 10, 09, \
+ R1024_6, 8*(R) + 7); \
+ R1024(00, 15, 02, 11, 06, 13, 04, 09, 14, 01, 08, 05, 10, 03, 12, 07, \
+ R1024_7, 8*(R) + 8); \
+ I1024(2*(R)+1); \
+} while (0)
+
+#define R1024_UNROLL_R(NN) \
+ ((SKEIN_UNROLL_1024 == 0 && \
+ SKEIN_1024_ROUNDS_TOTAL/8 > (NN)) || \
+ (SKEIN_UNROLL_1024 > (NN)))
+
+#if (SKEIN_UNROLL_1024 > 14)
+#error "need more unrolling in Skein_1024_Process_Block"
+#endif
+#endif
+
+/***************************** SKEIN_256 ******************************/
+#if !(SKEIN_USE_ASM & 256)
+void skein_256_process_block(struct skein_256_ctx *ctx, const u8 *blk_ptr,
+ size_t blk_cnt, size_t byte_cnt_add)
+{ /* do it in C */
+ enum {
+ WCNT = SKEIN_256_STATE_WORDS
+ };
+ size_t r;
+#if SKEIN_UNROLL_256
+ /* key schedule: chaining vars + tweak + "rot"*/
+ u64 kw[WCNT+4+RCNT*2];
+#else
+ /* key schedule words : chaining vars + tweak */
+ u64 kw[WCNT+4];
+#endif
+ u64 X0, X1, X2, X3; /* local copy of context vars, for speed */
+ u64 w[WCNT]; /* local copy of input block */
+#ifdef SKEIN_DEBUG
+ const u64 *X_ptr[4]; /* use for debugging (help cc put Xn in regs) */
+
+ X_ptr[0] = &X0;
+ X_ptr[1] = &X1;
+ X_ptr[2] = &X2;
+ X_ptr[3] = &X3;
+#endif
+ skein_assert(blk_cnt != 0); /* never call with blk_cnt == 0! */
+ ts[0] = ctx->h.tweak[0];
+ ts[1] = ctx->h.tweak[1];
+ do {
+ /*
+ * this implementation only supports 2**64 input bytes
+ * (no carry out here)
+ */
+ ts[0] += byte_cnt_add; /* update processed length */
+
+ /* precompute the key schedule for this block */
+ ks[0] = ctx->x[0];
+ ks[1] = ctx->x[1];
+ ks[2] = ctx->x[2];
+ ks[3] = ctx->x[3];
+ ks[4] = ks[0] ^ ks[1] ^ ks[2] ^ ks[3] ^ SKEIN_KS_PARITY;
+
+ ts[2] = ts[0] ^ ts[1];
+
+ /* get input block in little-endian format */
+ skein_get64_lsb_first(w, blk_ptr, WCNT);
+ debug_save_tweak(ctx);
+
+ /* do the first full key injection */
+ X0 = w[0] + ks[0];
+ X1 = w[1] + ks[1] + ts[0];
+ X2 = w[2] + ks[2] + ts[1];
+ X3 = w[3] + ks[3];
+
+ blk_ptr += SKEIN_256_BLOCK_BYTES;
+
+ /* run the rounds */
+ for (r = 1;
+ r < (SKEIN_UNROLL_256 ? 2 * RCNT : 2);
+ r += (SKEIN_UNROLL_256 ? 2 * SKEIN_UNROLL_256 : 1)) {
+ R256_8_ROUNDS(0);
+#if R256_UNROLL_R(1)
+ R256_8_ROUNDS(1);
+#endif
+#if R256_UNROLL_R(2)
+ R256_8_ROUNDS(2);
+#endif
+#if R256_UNROLL_R(3)
+ R256_8_ROUNDS(3);
+#endif
+#if R256_UNROLL_R(4)
+ R256_8_ROUNDS(4);
+#endif
+#if R256_UNROLL_R(5)
+ R256_8_ROUNDS(5);
+#endif
+#if R256_UNROLL_R(6)
+ R256_8_ROUNDS(6);
+#endif
+#if R256_UNROLL_R(7)
+ R256_8_ROUNDS(7);
+#endif
+#if R256_UNROLL_R(8)
+ R256_8_ROUNDS(8);
+#endif
+#if R256_UNROLL_R(9)
+ R256_8_ROUNDS(9);
+#endif
+#if R256_UNROLL_R(10)
+ R256_8_ROUNDS(10);
+#endif
+#if R256_UNROLL_R(11)
+ R256_8_ROUNDS(11);
+#endif
+#if R256_UNROLL_R(12)
+ R256_8_ROUNDS(12);
+#endif
+#if R256_UNROLL_R(13)
+ R256_8_ROUNDS(13);
+#endif
+#if R256_UNROLL_R(14)
+ R256_8_ROUNDS(14);
+#endif
+ }
+ /* do the final "feedforward" xor, update context chaining */
+ ctx->x[0] = X0 ^ w[0];
+ ctx->x[1] = X1 ^ w[1];
+ ctx->x[2] = X2 ^ w[2];
+ ctx->x[3] = X3 ^ w[3];
+
+ ts[1] &= ~SKEIN_T1_FLAG_FIRST;
+ } while (--blk_cnt);
+ ctx->h.tweak[0] = ts[0];
+ ctx->h.tweak[1] = ts[1];
+}
+
+#if defined(SKEIN_CODE_SIZE) || defined(SKEIN_PERF)
+size_t skein_256_process_block_code_size(void)
+{
+ return ((u8 *) skein_256_process_block_code_size) -
+ ((u8 *) skein_256_process_block);
+}
+unsigned int skein_256_unroll_cnt(void)
+{
+ return SKEIN_UNROLL_256;
+}
+#endif
+#endif
+
+/***************************** SKEIN_512 ******************************/
+#if !(SKEIN_USE_ASM & 512)
+void skein_512_process_block(struct skein_512_ctx *ctx, const u8 *blk_ptr,
+ size_t blk_cnt, size_t byte_cnt_add)
+{ /* do it in C */
+ enum {
+ WCNT = SKEIN_512_STATE_WORDS
+ };
+ size_t r;
+#if SKEIN_UNROLL_512
+ u64 kw[WCNT+4+RCNT*2]; /* key sched: chaining vars + tweak + "rot"*/
+#else
+ u64 kw[WCNT+4]; /* key schedule words : chaining vars + tweak */
+#endif
+ u64 X0, X1, X2, X3, X4, X5, X6, X7; /* local copies, for speed */
+ u64 w[WCNT]; /* local copy of input block */
+#ifdef SKEIN_DEBUG
+ const u64 *X_ptr[8]; /* use for debugging (help cc put Xn in regs) */
+
+ X_ptr[0] = &X0;
+ X_ptr[1] = &X1;
+ X_ptr[2] = &X2;
+ X_ptr[3] = &X3;
+ X_ptr[4] = &X4;
+ X_ptr[5] = &X5;
+ X_ptr[6] = &X6;
+ X_ptr[7] = &X7;
+#endif
+
+ skein_assert(blk_cnt != 0); /* never call with blk_cnt == 0! */
+ ts[0] = ctx->h.tweak[0];
+ ts[1] = ctx->h.tweak[1];
+ do {
+ /*
+ * this implementation only supports 2**64 input bytes
+ * (no carry out here)
+ */
+ ts[0] += byte_cnt_add; /* update processed length */
+
+ /* precompute the key schedule for this block */
+ ks[0] = ctx->x[0];
+ ks[1] = ctx->x[1];
+ ks[2] = ctx->x[2];
+ ks[3] = ctx->x[3];
+ ks[4] = ctx->x[4];
+ ks[5] = ctx->x[5];
+ ks[6] = ctx->x[6];
+ ks[7] = ctx->x[7];
+ ks[8] = ks[0] ^ ks[1] ^ ks[2] ^ ks[3] ^
+ ks[4] ^ ks[5] ^ ks[6] ^ ks[7] ^ SKEIN_KS_PARITY;
+
+ ts[2] = ts[0] ^ ts[1];
+
+ /* get input block in little-endian format */
+ skein_get64_lsb_first(w, blk_ptr, WCNT);
+ debug_save_tweak(ctx);
+
+ /* do the first full key injection */
+ X0 = w[0] + ks[0];
+ X1 = w[1] + ks[1];
+ X2 = w[2] + ks[2];
+ X3 = w[3] + ks[3];
+ X4 = w[4] + ks[4];
+ X5 = w[5] + ks[5] + ts[0];
+ X6 = w[6] + ks[6] + ts[1];
+ X7 = w[7] + ks[7];
+
+ blk_ptr += SKEIN_512_BLOCK_BYTES;
+
+ /* run the rounds */
+ for (r = 1;
+ r < (SKEIN_UNROLL_512 ? 2 * RCNT : 2);
+ r += (SKEIN_UNROLL_512 ? 2 * SKEIN_UNROLL_512 : 1)) {
+
+ R512_8_ROUNDS(0);
+
+#if R512_UNROLL_R(1)
+ R512_8_ROUNDS(1);
+#endif
+#if R512_UNROLL_R(2)
+ R512_8_ROUNDS(2);
+#endif
+#if R512_UNROLL_R(3)
+ R512_8_ROUNDS(3);
+#endif
+#if R512_UNROLL_R(4)
+ R512_8_ROUNDS(4);
+#endif
+#if R512_UNROLL_R(5)
+ R512_8_ROUNDS(5);
+#endif
+#if R512_UNROLL_R(6)
+ R512_8_ROUNDS(6);
+#endif
+#if R512_UNROLL_R(7)
+ R512_8_ROUNDS(7);
+#endif
+#if R512_UNROLL_R(8)
+ R512_8_ROUNDS(8);
+#endif
+#if R512_UNROLL_R(9)
+ R512_8_ROUNDS(9);
+#endif
+#if R512_UNROLL_R(10)
+ R512_8_ROUNDS(10);
+#endif
+#if R512_UNROLL_R(11)
+ R512_8_ROUNDS(11);
+#endif
+#if R512_UNROLL_R(12)
+ R512_8_ROUNDS(12);
+#endif
+#if R512_UNROLL_R(13)
+ R512_8_ROUNDS(13);
+#endif
+#if R512_UNROLL_R(14)
+ R512_8_ROUNDS(14);
+#endif
+ }
+
+ /* do the final "feedforward" xor, update context chaining */
+ ctx->x[0] = X0 ^ w[0];
+ ctx->x[1] = X1 ^ w[1];
+ ctx->x[2] = X2 ^ w[2];
+ ctx->x[3] = X3 ^ w[3];
+ ctx->x[4] = X4 ^ w[4];
+ ctx->x[5] = X5 ^ w[5];
+ ctx->x[6] = X6 ^ w[6];
+ ctx->x[7] = X7 ^ w[7];
+
+ ts[1] &= ~SKEIN_T1_FLAG_FIRST;
+ } while (--blk_cnt);
+ ctx->h.tweak[0] = ts[0];
+ ctx->h.tweak[1] = ts[1];
+}
+
+#if defined(SKEIN_CODE_SIZE) || defined(SKEIN_PERF)
+size_t skein_512_process_block_code_size(void)
+{
+ return ((u8 *) skein_512_process_block_code_size) -
+ ((u8 *) skein_512_process_block);
+}
+unsigned int skein_512_unroll_cnt(void)
+{
+ return SKEIN_UNROLL_512;
+}
+#endif
+#endif
+
+/***************************** SKEIN_1024 ******************************/
+#if !(SKEIN_USE_ASM & 1024)
+void skein_1024_process_block(struct skein_1024_ctx *ctx, const u8 *blk_ptr,
+ size_t blk_cnt, size_t byte_cnt_add)
+{ /* do it in C, always looping (unrolled is bigger AND slower!) */
+ enum {
+ WCNT = SKEIN_1024_STATE_WORDS
+ };
+ size_t r;
+#if (SKEIN_UNROLL_1024 != 0)
+ u64 kw[WCNT+4+RCNT*2]; /* key sched: chaining vars + tweak + "rot" */
+#else
+ u64 kw[WCNT+4]; /* key schedule words : chaining vars + tweak */
+#endif
+
+ /* local copy of vars, for speed */
+ u64 X00, X01, X02, X03, X04, X05, X06, X07,
+ X08, X09, X10, X11, X12, X13, X14, X15;
+ u64 w[WCNT]; /* local copy of input block */
+
+ skein_assert(blk_cnt != 0); /* never call with blk_cnt == 0! */
+ ts[0] = ctx->h.tweak[0];
+ ts[1] = ctx->h.tweak[1];
+ do {
+ /*
+ * this implementation only supports 2**64 input bytes
+ * (no carry out here)
+ */
+ ts[0] += byte_cnt_add; /* update processed length */
+
+ /* precompute the key schedule for this block */
+ ks[0] = ctx->x[0];
+ ks[1] = ctx->x[1];
+ ks[2] = ctx->x[2];
+ ks[3] = ctx->x[3];
+ ks[4] = ctx->x[4];
+ ks[5] = ctx->x[5];
+ ks[6] = ctx->x[6];
+ ks[7] = ctx->x[7];
+ ks[8] = ctx->x[8];
+ ks[9] = ctx->x[9];
+ ks[10] = ctx->x[10];
+ ks[11] = ctx->x[11];
+ ks[12] = ctx->x[12];
+ ks[13] = ctx->x[13];
+ ks[14] = ctx->x[14];
+ ks[15] = ctx->x[15];
+ ks[16] = ks[0] ^ ks[1] ^ ks[2] ^ ks[3] ^
+ ks[4] ^ ks[5] ^ ks[6] ^ ks[7] ^
+ ks[8] ^ ks[9] ^ ks[10] ^ ks[11] ^
+ ks[12] ^ ks[13] ^ ks[14] ^ ks[15] ^ SKEIN_KS_PARITY;
+
+ ts[2] = ts[0] ^ ts[1];
+
+ /* get input block in little-endian format */
+ skein_get64_lsb_first(w, blk_ptr, WCNT);
+ debug_save_tweak(ctx);
+
+ /* do the first full key injection */
+ X00 = w[0] + ks[0];
+ X01 = w[1] + ks[1];
+ X02 = w[2] + ks[2];
+ X03 = w[3] + ks[3];
+ X04 = w[4] + ks[4];
+ X05 = w[5] + ks[5];
+ X06 = w[6] + ks[6];
+ X07 = w[7] + ks[7];
+ X08 = w[8] + ks[8];
+ X09 = w[9] + ks[9];
+ X10 = w[10] + ks[10];
+ X11 = w[11] + ks[11];
+ X12 = w[12] + ks[12];
+ X13 = w[13] + ks[13] + ts[0];
+ X14 = w[14] + ks[14] + ts[1];
+ X15 = w[15] + ks[15];
+
+ for (r = 1;
+ r < (SKEIN_UNROLL_1024 ? 2 * RCNT : 2);
+ r += (SKEIN_UNROLL_1024 ? 2 * SKEIN_UNROLL_1024 : 1)) {
+ R1024_8_ROUNDS(0);
+#if R1024_UNROLL_R(1)
+ R1024_8_ROUNDS(1);
+#endif
+#if R1024_UNROLL_R(2)
+ R1024_8_ROUNDS(2);
+#endif
+#if R1024_UNROLL_R(3)
+ R1024_8_ROUNDS(3);
+#endif
+#if R1024_UNROLL_R(4)
+ R1024_8_ROUNDS(4);
+#endif
+#if R1024_UNROLL_R(5)
+ R1024_8_ROUNDS(5);
+#endif
+#if R1024_UNROLL_R(6)
+ R1024_8_ROUNDS(6);
+#endif
+#if R1024_UNROLL_R(7)
+ R1024_8_ROUNDS(7);
+#endif
+#if R1024_UNROLL_R(8)
+ R1024_8_ROUNDS(8);
+#endif
+#if R1024_UNROLL_R(9)
+ R1024_8_ROUNDS(9);
+#endif
+#if R1024_UNROLL_R(10)
+ R1024_8_ROUNDS(10);
+#endif
+#if R1024_UNROLL_R(11)
+ R1024_8_ROUNDS(11);
+#endif
+#if R1024_UNROLL_R(12)
+ R1024_8_ROUNDS(12);
+#endif
+#if R1024_UNROLL_R(13)
+ R1024_8_ROUNDS(13);
+#endif
+#if R1024_UNROLL_R(14)
+ R1024_8_ROUNDS(14);
+#endif
+ }
+ /* do the final "feedforward" xor, update context chaining */
+
+ ctx->x[0] = X00 ^ w[0];
+ ctx->x[1] = X01 ^ w[1];
+ ctx->x[2] = X02 ^ w[2];
+ ctx->x[3] = X03 ^ w[3];
+ ctx->x[4] = X04 ^ w[4];
+ ctx->x[5] = X05 ^ w[5];
+ ctx->x[6] = X06 ^ w[6];
+ ctx->x[7] = X07 ^ w[7];
+ ctx->x[8] = X08 ^ w[8];
+ ctx->x[9] = X09 ^ w[9];
+ ctx->x[10] = X10 ^ w[10];
+ ctx->x[11] = X11 ^ w[11];
+ ctx->x[12] = X12 ^ w[12];
+ ctx->x[13] = X13 ^ w[13];
+ ctx->x[14] = X14 ^ w[14];
+ ctx->x[15] = X15 ^ w[15];
+
+ ts[1] &= ~SKEIN_T1_FLAG_FIRST;
+ blk_ptr += SKEIN_1024_BLOCK_BYTES;
+ } while (--blk_cnt);
+ ctx->h.tweak[0] = ts[0];
+ ctx->h.tweak[1] = ts[1];
+}
+
+#if defined(SKEIN_CODE_SIZE) || defined(SKEIN_PERF)
+size_t skein_1024_process_block_code_size(void)
+{
+ return ((u8 *) skein_1024_process_block_code_size) -
+ ((u8 *) skein_1024_process_block);
+}
+unsigned int skein_1024_unroll_cnt(void)
+{
+ return SKEIN_UNROLL_1024;
+}
+#endif
+#endif
diff --git a/drivers/staging/skein/skein_block.h b/drivers/staging/skein/skein_block.h
new file mode 100644
index 000000000..9d40f4a52
--- /dev/null
+++ b/drivers/staging/skein/skein_block.h
@@ -0,0 +1,22 @@
+/***********************************************************************
+**
+** Implementation of the Skein hash function.
+**
+** Source code author: Doug Whiting, 2008.
+**
+** This algorithm and source code is released to the public domain.
+**
+************************************************************************/
+#ifndef _SKEIN_BLOCK_H_
+#define _SKEIN_BLOCK_H_
+
+#include "skein_base.h" /* get the Skein API definitions */
+
+void skein_256_process_block(struct skein_256_ctx *ctx, const u8 *blk_ptr,
+ size_t blk_cnt, size_t byte_cnt_add);
+void skein_512_process_block(struct skein_512_ctx *ctx, const u8 *blk_ptr,
+ size_t blk_cnt, size_t byte_cnt_add);
+void skein_1024_process_block(struct skein_1024_ctx *ctx, const u8 *blk_ptr,
+ size_t blk_cnt, size_t byte_cnt_add);
+
+#endif
diff --git a/drivers/staging/skein/skein_generic.c b/drivers/staging/skein/skein_generic.c
new file mode 100644
index 000000000..899078f1b
--- /dev/null
+++ b/drivers/staging/skein/skein_generic.c
@@ -0,0 +1,215 @@
+/*
+ * Cryptographic API.
+ *
+ * Skein256 Hash Algorithm.
+ *
+ * Derived from cryptoapi implementation, adapted for in-place
+ * scatterlist interface.
+ *
+ * Copyright (c) Eric Rost <eric.rost@mybabylon.net>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ */
+#include <linux/types.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <crypto/internal/hash.h>
+#include "skein_base.h"
+
+
+static int skein256_init(struct shash_desc *desc)
+{
+ return skein_256_init((struct skein_256_ctx *) shash_desc_ctx(desc),
+ SKEIN256_DIGEST_BIT_SIZE);
+}
+
+static int skein256_update(struct shash_desc *desc, const u8 *data,
+ unsigned int len)
+{
+ return skein_256_update((struct skein_256_ctx *)shash_desc_ctx(desc),
+ data, len);
+}
+
+static int skein256_final(struct shash_desc *desc, u8 *out)
+{
+ return skein_256_final((struct skein_256_ctx *)shash_desc_ctx(desc),
+ out);
+}
+
+static int skein256_export(struct shash_desc *desc, void *out)
+{
+ struct skein_256_ctx *sctx = shash_desc_ctx(desc);
+
+ memcpy(out, sctx, sizeof(*sctx));
+ return 0;
+}
+
+static int skein256_import(struct shash_desc *desc, const void *in)
+{
+ struct skein_256_ctx *sctx = shash_desc_ctx(desc);
+
+ memcpy(sctx, in, sizeof(*sctx));
+ return 0;
+}
+
+static int skein512_init(struct shash_desc *desc)
+{
+ return skein_512_init((struct skein_512_ctx *)shash_desc_ctx(desc),
+ SKEIN512_DIGEST_BIT_SIZE);
+}
+
+static int skein512_update(struct shash_desc *desc, const u8 *data,
+ unsigned int len)
+{
+ return skein_512_update((struct skein_512_ctx *)shash_desc_ctx(desc),
+ data, len);
+}
+
+static int skein512_final(struct shash_desc *desc, u8 *out)
+{
+ return skein_512_final((struct skein_512_ctx *)shash_desc_ctx(desc),
+ out);
+}
+
+static int skein512_export(struct shash_desc *desc, void *out)
+{
+ struct skein_512_ctx *sctx = shash_desc_ctx(desc);
+
+ memcpy(out, sctx, sizeof(*sctx));
+ return 0;
+}
+
+static int skein512_import(struct shash_desc *desc, const void *in)
+{
+ struct skein_512_ctx *sctx = shash_desc_ctx(desc);
+
+ memcpy(sctx, in, sizeof(*sctx));
+ return 0;
+}
+
+static int skein1024_init(struct shash_desc *desc)
+{
+ return skein_1024_init((struct skein_1024_ctx *)shash_desc_ctx(desc),
+ SKEIN1024_DIGEST_BIT_SIZE);
+}
+
+static int skein1024_update(struct shash_desc *desc, const u8 *data,
+ unsigned int len)
+{
+ return skein_1024_update((struct skein_1024_ctx *)shash_desc_ctx(desc),
+ data, len);
+}
+
+static int skein1024_final(struct shash_desc *desc, u8 *out)
+{
+ return skein_1024_final((struct skein_1024_ctx *)shash_desc_ctx(desc),
+ out);
+}
+
+static int skein1024_export(struct shash_desc *desc, void *out)
+{
+ struct skein_1024_ctx *sctx = shash_desc_ctx(desc);
+
+ memcpy(out, sctx, sizeof(*sctx));
+ return 0;
+}
+
+static int skein1024_import(struct shash_desc *desc, const void *in)
+{
+ struct skein_1024_ctx *sctx = shash_desc_ctx(desc);
+
+ memcpy(sctx, in, sizeof(*sctx));
+ return 0;
+}
+
+static struct shash_alg alg256 = {
+ .digestsize = (SKEIN256_DIGEST_BIT_SIZE / 8),
+ .init = skein256_init,
+ .update = skein256_update,
+ .final = skein256_final,
+ .export = skein256_export,
+ .import = skein256_import,
+ .descsize = sizeof(struct skein_256_ctx),
+ .statesize = sizeof(struct skein_256_ctx),
+ .base = {
+ .cra_name = "skein256",
+ .cra_driver_name = "skein",
+ .cra_flags = CRYPTO_ALG_TYPE_SHASH,
+ .cra_blocksize = SKEIN_256_BLOCK_BYTES,
+ .cra_module = THIS_MODULE,
+ }
+};
+
+static struct shash_alg alg512 = {
+ .digestsize = (SKEIN512_DIGEST_BIT_SIZE / 8),
+ .init = skein512_init,
+ .update = skein512_update,
+ .final = skein512_final,
+ .export = skein512_export,
+ .import = skein512_import,
+ .descsize = sizeof(struct skein_512_ctx),
+ .statesize = sizeof(struct skein_512_ctx),
+ .base = {
+ .cra_name = "skein512",
+ .cra_driver_name = "skein",
+ .cra_flags = CRYPTO_ALG_TYPE_SHASH,
+ .cra_blocksize = SKEIN_512_BLOCK_BYTES,
+ .cra_module = THIS_MODULE,
+ }
+};
+
+static struct shash_alg alg1024 = {
+ .digestsize = (SKEIN1024_DIGEST_BIT_SIZE / 8),
+ .init = skein1024_init,
+ .update = skein1024_update,
+ .final = skein1024_final,
+ .export = skein1024_export,
+ .import = skein1024_import,
+ .descsize = sizeof(struct skein_1024_ctx),
+ .statesize = sizeof(struct skein_1024_ctx),
+ .base = {
+ .cra_name = "skein1024",
+ .cra_driver_name = "skein",
+ .cra_flags = CRYPTO_ALG_TYPE_SHASH,
+ .cra_blocksize = SKEIN_1024_BLOCK_BYTES,
+ .cra_module = THIS_MODULE,
+ }
+};
+
+static int __init skein_generic_init(void)
+{
+ if (crypto_register_shash(&alg256))
+ goto out;
+ if (crypto_register_shash(&alg512))
+ goto unreg256;
+ if (crypto_register_shash(&alg1024))
+ goto unreg512;
+
+ return 0;
+
+unreg512:
+ crypto_unregister_shash(&alg512);
+unreg256:
+ crypto_unregister_shash(&alg256);
+out:
+ return -1;
+}
+
+static void __exit skein_generic_fini(void)
+{
+ crypto_unregister_shash(&alg256);
+ crypto_unregister_shash(&alg512);
+ crypto_unregister_shash(&alg1024);
+}
+
+module_init(skein_generic_init);
+module_exit(skein_generic_fini);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Skein Hash Algorithm");
+
+MODULE_ALIAS("skein");
diff --git a/drivers/staging/skein/skein_iv.h b/drivers/staging/skein/skein_iv.h
new file mode 100644
index 000000000..8a06314d0
--- /dev/null
+++ b/drivers/staging/skein/skein_iv.h
@@ -0,0 +1,186 @@
+#ifndef _SKEIN_IV_H_
+#define _SKEIN_IV_H_
+
+#include "skein_base.h" /* get Skein macros and types */
+
+/*
+***************** Pre-computed Skein IVs *******************
+**
+** NOTE: these values are not "magic" constants, but
+** are generated using the Threefish block function.
+** They are pre-computed here only for speed; i.e., to
+** avoid the need for a Threefish call during Init().
+**
+** The IV for any fixed hash length may be pre-computed.
+** Only the most common values are included here.
+**
+************************************************************
+**/
+
+#define MK_64 SKEIN_MK_64
+
+/* blkSize = 256 bits. hashSize = 128 bits */
+static const u64 SKEIN_256_IV_128[] = {
+ MK_64(0xE1111906, 0x964D7260),
+ MK_64(0x883DAAA7, 0x7C8D811C),
+ MK_64(0x10080DF4, 0x91960F7A),
+ MK_64(0xCCF7DDE5, 0xB45BC1C2)
+};
+
+/* blkSize = 256 bits. hashSize = 160 bits */
+static const u64 SKEIN_256_IV_160[] = {
+ MK_64(0x14202314, 0x72825E98),
+ MK_64(0x2AC4E9A2, 0x5A77E590),
+ MK_64(0xD47A5856, 0x8838D63E),
+ MK_64(0x2DD2E496, 0x8586AB7D)
+};
+
+/* blkSize = 256 bits. hashSize = 224 bits */
+static const u64 SKEIN_256_IV_224[] = {
+ MK_64(0xC6098A8C, 0x9AE5EA0B),
+ MK_64(0x876D5686, 0x08C5191C),
+ MK_64(0x99CB88D7, 0xD7F53884),
+ MK_64(0x384BDDB1, 0xAEDDB5DE)
+};
+
+/* blkSize = 256 bits. hashSize = 256 bits */
+static const u64 SKEIN_256_IV_256[] = {
+ MK_64(0xFC9DA860, 0xD048B449),
+ MK_64(0x2FCA6647, 0x9FA7D833),
+ MK_64(0xB33BC389, 0x6656840F),
+ MK_64(0x6A54E920, 0xFDE8DA69)
+};
+
+/* blkSize = 512 bits. hashSize = 128 bits */
+static const u64 SKEIN_512_IV_128[] = {
+ MK_64(0xA8BC7BF3, 0x6FBF9F52),
+ MK_64(0x1E9872CE, 0xBD1AF0AA),
+ MK_64(0x309B1790, 0xB32190D3),
+ MK_64(0xBCFBB854, 0x3F94805C),
+ MK_64(0x0DA61BCD, 0x6E31B11B),
+ MK_64(0x1A18EBEA, 0xD46A32E3),
+ MK_64(0xA2CC5B18, 0xCE84AA82),
+ MK_64(0x6982AB28, 0x9D46982D)
+};
+
+/* blkSize = 512 bits. hashSize = 160 bits */
+static const u64 SKEIN_512_IV_160[] = {
+ MK_64(0x28B81A2A, 0xE013BD91),
+ MK_64(0xC2F11668, 0xB5BDF78F),
+ MK_64(0x1760D8F3, 0xF6A56F12),
+ MK_64(0x4FB74758, 0x8239904F),
+ MK_64(0x21EDE07F, 0x7EAF5056),
+ MK_64(0xD908922E, 0x63ED70B8),
+ MK_64(0xB8EC76FF, 0xECCB52FA),
+ MK_64(0x01A47BB8, 0xA3F27A6E)
+};
+
+/* blkSize = 512 bits. hashSize = 224 bits */
+static const u64 SKEIN_512_IV_224[] = {
+ MK_64(0xCCD06162, 0x48677224),
+ MK_64(0xCBA65CF3, 0xA92339EF),
+ MK_64(0x8CCD69D6, 0x52FF4B64),
+ MK_64(0x398AED7B, 0x3AB890B4),
+ MK_64(0x0F59D1B1, 0x457D2BD0),
+ MK_64(0x6776FE65, 0x75D4EB3D),
+ MK_64(0x99FBC70E, 0x997413E9),
+ MK_64(0x9E2CFCCF, 0xE1C41EF7)
+};
+
+/* blkSize = 512 bits. hashSize = 256 bits */
+static const u64 SKEIN_512_IV_256[] = {
+ MK_64(0xCCD044A1, 0x2FDB3E13),
+ MK_64(0xE8359030, 0x1A79A9EB),
+ MK_64(0x55AEA061, 0x4F816E6F),
+ MK_64(0x2A2767A4, 0xAE9B94DB),
+ MK_64(0xEC06025E, 0x74DD7683),
+ MK_64(0xE7A436CD, 0xC4746251),
+ MK_64(0xC36FBAF9, 0x393AD185),
+ MK_64(0x3EEDBA18, 0x33EDFC13)
+};
+
+/* blkSize = 512 bits. hashSize = 384 bits */
+static const u64 SKEIN_512_IV_384[] = {
+ MK_64(0xA3F6C6BF, 0x3A75EF5F),
+ MK_64(0xB0FEF9CC, 0xFD84FAA4),
+ MK_64(0x9D77DD66, 0x3D770CFE),
+ MK_64(0xD798CBF3, 0xB468FDDA),
+ MK_64(0x1BC4A666, 0x8A0E4465),
+ MK_64(0x7ED7D434, 0xE5807407),
+ MK_64(0x548FC1AC, 0xD4EC44D6),
+ MK_64(0x266E1754, 0x6AA18FF8)
+};
+
+/* blkSize = 512 bits. hashSize = 512 bits */
+static const u64 SKEIN_512_IV_512[] = {
+ MK_64(0x4903ADFF, 0x749C51CE),
+ MK_64(0x0D95DE39, 0x9746DF03),
+ MK_64(0x8FD19341, 0x27C79BCE),
+ MK_64(0x9A255629, 0xFF352CB1),
+ MK_64(0x5DB62599, 0xDF6CA7B0),
+ MK_64(0xEABE394C, 0xA9D5C3F4),
+ MK_64(0x991112C7, 0x1A75B523),
+ MK_64(0xAE18A40B, 0x660FCC33)
+};
+
+/* blkSize = 1024 bits. hashSize = 384 bits */
+static const u64 SKEIN_1024_IV_384[] = {
+ MK_64(0x5102B6B8, 0xC1894A35),
+ MK_64(0xFEEBC9E3, 0xFE8AF11A),
+ MK_64(0x0C807F06, 0xE32BED71),
+ MK_64(0x60C13A52, 0xB41A91F6),
+ MK_64(0x9716D35D, 0xD4917C38),
+ MK_64(0xE780DF12, 0x6FD31D3A),
+ MK_64(0x797846B6, 0xC898303A),
+ MK_64(0xB172C2A8, 0xB3572A3B),
+ MK_64(0xC9BC8203, 0xA6104A6C),
+ MK_64(0x65909338, 0xD75624F4),
+ MK_64(0x94BCC568, 0x4B3F81A0),
+ MK_64(0x3EBBF51E, 0x10ECFD46),
+ MK_64(0x2DF50F0B, 0xEEB08542),
+ MK_64(0x3B5A6530, 0x0DBC6516),
+ MK_64(0x484B9CD2, 0x167BBCE1),
+ MK_64(0x2D136947, 0xD4CBAFEA)
+};
+
+/* blkSize = 1024 bits. hashSize = 512 bits */
+static const u64 SKEIN_1024_IV_512[] = {
+ MK_64(0xCAEC0E5D, 0x7C1B1B18),
+ MK_64(0xA01B0E04, 0x5F03E802),
+ MK_64(0x33840451, 0xED912885),
+ MK_64(0x374AFB04, 0xEAEC2E1C),
+ MK_64(0xDF25A0E2, 0x813581F7),
+ MK_64(0xE4004093, 0x8B12F9D2),
+ MK_64(0xA662D539, 0xC2ED39B6),
+ MK_64(0xFA8B85CF, 0x45D8C75A),
+ MK_64(0x8316ED8E, 0x29EDE796),
+ MK_64(0x053289C0, 0x2E9F91B8),
+ MK_64(0xC3F8EF1D, 0x6D518B73),
+ MK_64(0xBDCEC3C4, 0xD5EF332E),
+ MK_64(0x549A7E52, 0x22974487),
+ MK_64(0x67070872, 0x5B749816),
+ MK_64(0xB9CD28FB, 0xF0581BD1),
+ MK_64(0x0E2940B8, 0x15804974)
+};
+
+/* blkSize = 1024 bits. hashSize = 1024 bits */
+static const u64 SKEIN_1024_IV_1024[] = {
+ MK_64(0xD593DA07, 0x41E72355),
+ MK_64(0x15B5E511, 0xAC73E00C),
+ MK_64(0x5180E5AE, 0xBAF2C4F0),
+ MK_64(0x03BD41D3, 0xFCBCAFAF),
+ MK_64(0x1CAEC6FD, 0x1983A898),
+ MK_64(0x6E510B8B, 0xCDD0589F),
+ MK_64(0x77E2BDFD, 0xC6394ADA),
+ MK_64(0xC11E1DB5, 0x24DCB0A3),
+ MK_64(0xD6D14AF9, 0xC6329AB5),
+ MK_64(0x6A9B0BFC, 0x6EB67E0D),
+ MK_64(0x9243C60D, 0xCCFF1332),
+ MK_64(0x1A1F1DDE, 0x743F02D4),
+ MK_64(0x0996753C, 0x10ED0BB8),
+ MK_64(0x6572DD22, 0xF2B4969A),
+ MK_64(0x61FD3062, 0xD00A579A),
+ MK_64(0x1DE0536E, 0x8682E539)
+};
+
+#endif /* _SKEIN_IV_H_ */
diff --git a/drivers/staging/skein/threefish_api.c b/drivers/staging/skein/threefish_api.c
new file mode 100644
index 000000000..2b649abb7
--- /dev/null
+++ b/drivers/staging/skein/threefish_api.c
@@ -0,0 +1,77 @@
+#include <linux/string.h>
+#include "threefish_api.h"
+
+void threefish_set_key(struct threefish_key *key_ctx,
+ enum threefish_size state_size,
+ u64 *key_data, u64 *tweak)
+{
+ int key_words = state_size / 64;
+ int i;
+ u64 parity = KEY_SCHEDULE_CONST;
+
+ key_ctx->tweak[0] = tweak[0];
+ key_ctx->tweak[1] = tweak[1];
+ key_ctx->tweak[2] = tweak[0] ^ tweak[1];
+
+ for (i = 0; i < key_words; i++) {
+ key_ctx->key[i] = key_data[i];
+ parity ^= key_data[i];
+ }
+ key_ctx->key[i] = parity;
+ key_ctx->state_size = state_size;
+}
+
+void threefish_encrypt_block_bytes(struct threefish_key *key_ctx, u8 *in,
+ u8 *out)
+{
+ u64 plain[SKEIN_MAX_STATE_WORDS]; /* max number of words*/
+ u64 cipher[SKEIN_MAX_STATE_WORDS];
+
+ skein_get64_lsb_first(plain, in, key_ctx->state_size / 64);
+ threefish_encrypt_block_words(key_ctx, plain, cipher);
+ skein_put64_lsb_first(out, cipher, key_ctx->state_size / 8);
+}
+
+void threefish_encrypt_block_words(struct threefish_key *key_ctx, u64 *in,
+ u64 *out)
+{
+ switch (key_ctx->state_size) {
+ case THREEFISH_256:
+ threefish_encrypt_256(key_ctx, in, out);
+ break;
+ case THREEFISH_512:
+ threefish_encrypt_512(key_ctx, in, out);
+ break;
+ case THREEFISH_1024:
+ threefish_encrypt_1024(key_ctx, in, out);
+ break;
+ }
+}
+
+void threefish_decrypt_block_bytes(struct threefish_key *key_ctx, u8 *in,
+ u8 *out)
+{
+ u64 plain[SKEIN_MAX_STATE_WORDS]; /* max number of words*/
+ u64 cipher[SKEIN_MAX_STATE_WORDS];
+
+ skein_get64_lsb_first(cipher, in, key_ctx->state_size / 64);
+ threefish_decrypt_block_words(key_ctx, cipher, plain);
+ skein_put64_lsb_first(out, plain, key_ctx->state_size / 8);
+}
+
+void threefish_decrypt_block_words(struct threefish_key *key_ctx, u64 *in,
+ u64 *out)
+{
+ switch (key_ctx->state_size) {
+ case THREEFISH_256:
+ threefish_decrypt_256(key_ctx, in, out);
+ break;
+ case THREEFISH_512:
+ threefish_decrypt_512(key_ctx, in, out);
+ break;
+ case THREEFISH_1024:
+ threefish_decrypt_1024(key_ctx, in, out);
+ break;
+ }
+}
+
diff --git a/drivers/staging/skein/threefish_api.h b/drivers/staging/skein/threefish_api.h
new file mode 100644
index 000000000..8e0a0b77e
--- /dev/null
+++ b/drivers/staging/skein/threefish_api.h
@@ -0,0 +1,170 @@
+
+#ifndef THREEFISHAPI_H
+#define THREEFISHAPI_H
+
+/**
+ * @file threefish_api.h
+ * @brief A Threefish cipher API and its functions.
+ * @{
+ *
+ * This API and the functions that implement this API simplify the usage
+ * of the Threefish cipher. The design and the way to use the functions
+ * follow the openSSL design but at the same time take care of some Threefish
+ * specific behaviour and possibilities.
+ *
+ * These are the low level functions that deal with Threefish blocks only.
+ * Implementations for cipher modes such as ECB, CFB, or CBC may use these
+ * functions.
+ *
+@code
+ // Threefish cipher context data
+ struct threefish_key key_ctx;
+
+ // Initialize the context
+ threefish_set_key(&key_ctx, THREEFISH_512, key, tweak);
+
+ // Encrypt
+ threefish_encrypt_block_bytes(&key_ctx, input, cipher);
+@endcode
+ */
+
+#include <linux/types.h>
+#include "skein_base.h"
+
+#define KEY_SCHEDULE_CONST 0x1BD11BDAA9FC1A22L
+
+/**
+ * Which Threefish size to use
+ */
+enum threefish_size {
+ THREEFISH_256 = 256, /*!< Skein with 256 bit state */
+ THREEFISH_512 = 512, /*!< Skein with 512 bit state */
+ THREEFISH_1024 = 1024 /*!< Skein with 1024 bit state */
+};
+
+/**
+ * Context for Threefish key and tweak words.
+ *
+ * This structure was setup with some know-how of the internal
+ * Skein structures, in particular ordering of header and size dependent
+ * variables. If Skein implementation changes this, the adapt these
+ * structures as well.
+ */
+struct threefish_key {
+ u64 state_size;
+ u64 key[SKEIN_MAX_STATE_WORDS+1]; /* max number of key words*/
+ u64 tweak[3];
+};
+
+/**
+ * Set Threefish key and tweak data.
+ *
+ * This function sets the key and tweak data for the Threefish cipher of
+ * the given size. The key data must have the same length (number of bits)
+ * as the state size
+ *
+ * @param key_ctx
+ * Pointer to a Threefish key structure.
+ * @param size
+ * Which Skein size to use.
+ * @param key_data
+ * Pointer to the key words (word has 64 bits).
+ * @param tweak
+ * Pointer to the two tweak words (word has 64 bits).
+ */
+void threefish_set_key(struct threefish_key *key_ctx,
+ enum threefish_size state_size,
+ u64 *key_data, u64 *tweak);
+
+/**
+ * Encrypt Threefish block (bytes).
+ *
+ * The buffer must have at least the same length (number of bits) as the
+ * state size for this key. The function uses the first @c state_size bits
+ * of the input buffer, encrypts them and stores the result in the output
+ * buffer.
+ *
+ * @param key_ctx
+ * Pointer to a Threefish key structure.
+ * @param in
+ * Poionter to plaintext data buffer.
+ * @param out
+ * Pointer to cipher buffer.
+ */
+void threefish_encrypt_block_bytes(struct threefish_key *key_ctx, u8 *in,
+ u8 *out);
+
+/**
+ * Encrypt Threefish block (words).
+ *
+ * The buffer must have at least the same length (number of bits) as the
+ * state size for this key. The function uses the first @c state_size bits
+ * of the input buffer, encrypts them and stores the result in the output
+ * buffer.
+ *
+ * The wordsize ist set to 64 bits.
+ *
+ * @param key_ctx
+ * Pointer to a Threefish key structure.
+ * @param in
+ * Poionter to plaintext data buffer.
+ * @param out
+ * Pointer to cipher buffer.
+ */
+void threefish_encrypt_block_words(struct threefish_key *key_ctx, u64 *in,
+ u64 *out);
+
+/**
+ * Decrypt Threefish block (bytes).
+ *
+ * The buffer must have at least the same length (number of bits) as the
+ * state size for this key. The function uses the first @c state_size bits
+ * of the input buffer, decrypts them and stores the result in the output
+ * buffer
+ *
+ * @param key_ctx
+ * Pointer to a Threefish key structure.
+ * @param in
+ * Poionter to cipher data buffer.
+ * @param out
+ * Pointer to plaintext buffer.
+ */
+void threefish_decrypt_block_bytes(struct threefish_key *key_ctx, u8 *in,
+ u8 *out);
+
+/**
+ * Decrypt Threefish block (words).
+ *
+ * The buffer must have at least the same length (number of bits) as the
+ * state size for this key. The function uses the first @c state_size bits
+ * of the input buffer, encrypts them and stores the result in the output
+ * buffer.
+ *
+ * The wordsize ist set to 64 bits.
+ *
+ * @param key_ctx
+ * Pointer to a Threefish key structure.
+ * @param in
+ * Poionter to cipher data buffer.
+ * @param out
+ * Pointer to plaintext buffer.
+ */
+void threefish_decrypt_block_words(struct threefish_key *key_ctx, u64 *in,
+ u64 *out);
+
+void threefish_encrypt_256(struct threefish_key *key_ctx, u64 *input,
+ u64 *output);
+void threefish_encrypt_512(struct threefish_key *key_ctx, u64 *input,
+ u64 *output);
+void threefish_encrypt_1024(struct threefish_key *key_ctx, u64 *input,
+ u64 *output);
+void threefish_decrypt_256(struct threefish_key *key_ctx, u64 *input,
+ u64 *output);
+void threefish_decrypt_512(struct threefish_key *key_ctx, u64 *input,
+ u64 *output);
+void threefish_decrypt_1024(struct threefish_key *key_ctx, u64 *input,
+ u64 *output);
+/**
+ * @}
+ */
+#endif
diff --git a/drivers/staging/skein/threefish_block.c b/drivers/staging/skein/threefish_block.c
new file mode 100644
index 000000000..bd1e15caa
--- /dev/null
+++ b/drivers/staging/skein/threefish_block.c
@@ -0,0 +1,8258 @@
+#include "threefish_api.h"
+
+void threefish_encrypt_256(struct threefish_key *key_ctx, u64 *input,
+ u64 *output)
+{
+ u64 b0 = input[0], b1 = input[1],
+ b2 = input[2], b3 = input[3];
+ u64 k0 = key_ctx->key[0], k1 = key_ctx->key[1],
+ k2 = key_ctx->key[2], k3 = key_ctx->key[3],
+ k4 = key_ctx->key[4];
+ u64 t0 = key_ctx->tweak[0], t1 = key_ctx->tweak[1],
+ t2 = key_ctx->tweak[2];
+
+ b1 += k1 + t0;
+ b0 += b1 + k0;
+ b1 = ((b1 << 14) | (b1 >> (64 - 14))) ^ b0;
+
+ b3 += k3;
+ b2 += b3 + k2 + t1;
+ b3 = ((b3 << 16) | (b3 >> (64 - 16))) ^ b2;
+
+ b0 += b3;
+ b3 = ((b3 << 52) | (b3 >> (64 - 52))) ^ b0;
+
+ b2 += b1;
+ b1 = ((b1 << 57) | (b1 >> (64 - 57))) ^ b2;
+
+ b0 += b1;
+ b1 = ((b1 << 23) | (b1 >> (64 - 23))) ^ b0;
+
+ b2 += b3;
+ b3 = ((b3 << 40) | (b3 >> (64 - 40))) ^ b2;
+
+ b0 += b3;
+ b3 = ((b3 << 5) | (b3 >> (64 - 5))) ^ b0;
+
+ b2 += b1;
+ b1 = ((b1 << 37) | (b1 >> (64 - 37))) ^ b2;
+
+ b1 += k2 + t1;
+ b0 += b1 + k1;
+ b1 = ((b1 << 25) | (b1 >> (64 - 25))) ^ b0;
+
+ b3 += k4 + 1;
+ b2 += b3 + k3 + t2;
+ b3 = ((b3 << 33) | (b3 >> (64 - 33))) ^ b2;
+
+ b0 += b3;
+ b3 = ((b3 << 46) | (b3 >> (64 - 46))) ^ b0;
+
+ b2 += b1;
+ b1 = ((b1 << 12) | (b1 >> (64 - 12))) ^ b2;
+
+ b0 += b1;
+ b1 = ((b1 << 58) | (b1 >> (64 - 58))) ^ b0;
+
+ b2 += b3;
+ b3 = ((b3 << 22) | (b3 >> (64 - 22))) ^ b2;
+
+ b0 += b3;
+ b3 = ((b3 << 32) | (b3 >> (64 - 32))) ^ b0;
+
+ b2 += b1;
+ b1 = ((b1 << 32) | (b1 >> (64 - 32))) ^ b2;
+
+
+ b1 += k3 + t2;
+ b0 += b1 + k2;
+ b1 = ((b1 << 14) | (b1 >> (64 - 14))) ^ b0;
+
+ b3 += k0 + 2;
+ b2 += b3 + k4 + t0;
+ b3 = ((b3 << 16) | (b3 >> (64 - 16))) ^ b2;
+
+ b0 += b3;
+ b3 = ((b3 << 52) | (b3 >> (64 - 52))) ^ b0;
+
+ b2 += b1;
+ b1 = ((b1 << 57) | (b1 >> (64 - 57))) ^ b2;
+
+ b0 += b1;
+ b1 = ((b1 << 23) | (b1 >> (64 - 23))) ^ b0;
+
+ b2 += b3;
+ b3 = ((b3 << 40) | (b3 >> (64 - 40))) ^ b2;
+
+ b0 += b3;
+ b3 = ((b3 << 5) | (b3 >> (64 - 5))) ^ b0;
+
+ b2 += b1;
+ b1 = ((b1 << 37) | (b1 >> (64 - 37))) ^ b2;
+
+ b1 += k4 + t0;
+ b0 += b1 + k3;
+ b1 = ((b1 << 25) | (b1 >> (64 - 25))) ^ b0;
+
+ b3 += k1 + 3;
+ b2 += b3 + k0 + t1;
+ b3 = ((b3 << 33) | (b3 >> (64 - 33))) ^ b2;
+
+ b0 += b3;
+ b3 = ((b3 << 46) | (b3 >> (64 - 46))) ^ b0;
+
+ b2 += b1;
+ b1 = ((b1 << 12) | (b1 >> (64 - 12))) ^ b2;
+
+ b0 += b1;
+ b1 = ((b1 << 58) | (b1 >> (64 - 58))) ^ b0;
+
+ b2 += b3;
+ b3 = ((b3 << 22) | (b3 >> (64 - 22))) ^ b2;
+
+ b0 += b3;
+ b3 = ((b3 << 32) | (b3 >> (64 - 32))) ^ b0;
+
+ b2 += b1;
+ b1 = ((b1 << 32) | (b1 >> (64 - 32))) ^ b2;
+
+
+ b1 += k0 + t1;
+ b0 += b1 + k4;
+ b1 = ((b1 << 14) | (b1 >> (64 - 14))) ^ b0;
+
+ b3 += k2 + 4;
+ b2 += b3 + k1 + t2;
+ b3 = ((b3 << 16) | (b3 >> (64 - 16))) ^ b2;
+
+ b0 += b3;
+ b3 = ((b3 << 52) | (b3 >> (64 - 52))) ^ b0;
+
+ b2 += b1;
+ b1 = ((b1 << 57) | (b1 >> (64 - 57))) ^ b2;
+
+ b0 += b1;
+ b1 = ((b1 << 23) | (b1 >> (64 - 23))) ^ b0;
+
+ b2 += b3;
+ b3 = ((b3 << 40) | (b3 >> (64 - 40))) ^ b2;
+
+ b0 += b3;
+ b3 = ((b3 << 5) | (b3 >> (64 - 5))) ^ b0;
+
+ b2 += b1;
+ b1 = ((b1 << 37) | (b1 >> (64 - 37))) ^ b2;
+
+ b1 += k1 + t2;
+ b0 += b1 + k0;
+ b1 = ((b1 << 25) | (b1 >> (64 - 25))) ^ b0;
+
+ b3 += k3 + 5;
+ b2 += b3 + k2 + t0;
+ b3 = ((b3 << 33) | (b3 >> (64 - 33))) ^ b2;
+
+ b0 += b3;
+ b3 = ((b3 << 46) | (b3 >> (64 - 46))) ^ b0;
+
+ b2 += b1;
+ b1 = ((b1 << 12) | (b1 >> (64 - 12))) ^ b2;
+
+ b0 += b1;
+ b1 = ((b1 << 58) | (b1 >> (64 - 58))) ^ b0;
+
+ b2 += b3;
+ b3 = ((b3 << 22) | (b3 >> (64 - 22))) ^ b2;
+
+ b0 += b3;
+ b3 = ((b3 << 32) | (b3 >> (64 - 32))) ^ b0;
+
+ b2 += b1;
+ b1 = ((b1 << 32) | (b1 >> (64 - 32))) ^ b2;
+
+
+ b1 += k2 + t0;
+ b0 += b1 + k1;
+ b1 = ((b1 << 14) | (b1 >> (64 - 14))) ^ b0;
+
+ b3 += k4 + 6;
+ b2 += b3 + k3 + t1;
+ b3 = ((b3 << 16) | (b3 >> (64 - 16))) ^ b2;
+
+ b0 += b3;
+ b3 = ((b3 << 52) | (b3 >> (64 - 52))) ^ b0;
+
+ b2 += b1;
+ b1 = ((b1 << 57) | (b1 >> (64 - 57))) ^ b2;
+
+ b0 += b1;
+ b1 = ((b1 << 23) | (b1 >> (64 - 23))) ^ b0;
+
+ b2 += b3;
+ b3 = ((b3 << 40) | (b3 >> (64 - 40))) ^ b2;
+
+ b0 += b3;
+ b3 = ((b3 << 5) | (b3 >> (64 - 5))) ^ b0;
+
+ b2 += b1;
+ b1 = ((b1 << 37) | (b1 >> (64 - 37))) ^ b2;
+
+ b1 += k3 + t1;
+ b0 += b1 + k2;
+ b1 = ((b1 << 25) | (b1 >> (64 - 25))) ^ b0;
+
+ b3 += k0 + 7;
+ b2 += b3 + k4 + t2;
+ b3 = ((b3 << 33) | (b3 >> (64 - 33))) ^ b2;
+
+ b0 += b3;
+ b3 = ((b3 << 46) | (b3 >> (64 - 46))) ^ b0;
+
+ b2 += b1;
+ b1 = ((b1 << 12) | (b1 >> (64 - 12))) ^ b2;
+
+ b0 += b1;
+ b1 = ((b1 << 58) | (b1 >> (64 - 58))) ^ b0;
+
+ b2 += b3;
+ b3 = ((b3 << 22) | (b3 >> (64 - 22))) ^ b2;
+
+ b0 += b3;
+ b3 = ((b3 << 32) | (b3 >> (64 - 32))) ^ b0;
+
+ b2 += b1;
+ b1 = ((b1 << 32) | (b1 >> (64 - 32))) ^ b2;
+
+
+ b1 += k4 + t2;
+ b0 += b1 + k3;
+ b1 = ((b1 << 14) | (b1 >> (64 - 14))) ^ b0;
+
+ b3 += k1 + 8;
+ b2 += b3 + k0 + t0;
+ b3 = ((b3 << 16) | (b3 >> (64 - 16))) ^ b2;
+
+ b0 += b3;
+ b3 = ((b3 << 52) | (b3 >> (64 - 52))) ^ b0;
+
+ b2 += b1;
+ b1 = ((b1 << 57) | (b1 >> (64 - 57))) ^ b2;
+
+ b0 += b1;
+ b1 = ((b1 << 23) | (b1 >> (64 - 23))) ^ b0;
+
+ b2 += b3;
+ b3 = ((b3 << 40) | (b3 >> (64 - 40))) ^ b2;
+
+ b0 += b3;
+ b3 = ((b3 << 5) | (b3 >> (64 - 5))) ^ b0;
+
+ b2 += b1;
+ b1 = ((b1 << 37) | (b1 >> (64 - 37))) ^ b2;
+
+ b1 += k0 + t0;
+ b0 += b1 + k4;
+ b1 = ((b1 << 25) | (b1 >> (64 - 25))) ^ b0;
+
+ b3 += k2 + 9;
+ b2 += b3 + k1 + t1;
+ b3 = ((b3 << 33) | (b3 >> (64 - 33))) ^ b2;
+
+ b0 += b3;
+ b3 = ((b3 << 46) | (b3 >> (64 - 46))) ^ b0;
+
+ b2 += b1;
+ b1 = ((b1 << 12) | (b1 >> (64 - 12))) ^ b2;
+
+ b0 += b1;
+ b1 = ((b1 << 58) | (b1 >> (64 - 58))) ^ b0;
+
+ b2 += b3;
+ b3 = ((b3 << 22) | (b3 >> (64 - 22))) ^ b2;
+
+ b0 += b3;
+ b3 = ((b3 << 32) | (b3 >> (64 - 32))) ^ b0;
+
+ b2 += b1;
+ b1 = ((b1 << 32) | (b1 >> (64 - 32))) ^ b2;
+
+
+ b1 += k1 + t1;
+ b0 += b1 + k0;
+ b1 = ((b1 << 14) | (b1 >> (64 - 14))) ^ b0;
+
+ b3 += k3 + 10;
+ b2 += b3 + k2 + t2;
+ b3 = ((b3 << 16) | (b3 >> (64 - 16))) ^ b2;
+
+ b0 += b3;
+ b3 = ((b3 << 52) | (b3 >> (64 - 52))) ^ b0;
+
+ b2 += b1;
+ b1 = ((b1 << 57) | (b1 >> (64 - 57))) ^ b2;
+
+ b0 += b1;
+ b1 = ((b1 << 23) | (b1 >> (64 - 23))) ^ b0;
+
+ b2 += b3;
+ b3 = ((b3 << 40) | (b3 >> (64 - 40))) ^ b2;
+
+ b0 += b3;
+ b3 = ((b3 << 5) | (b3 >> (64 - 5))) ^ b0;
+
+ b2 += b1;
+ b1 = ((b1 << 37) | (b1 >> (64 - 37))) ^ b2;
+
+ b1 += k2 + t2;
+ b0 += b1 + k1;
+ b1 = ((b1 << 25) | (b1 >> (64 - 25))) ^ b0;
+
+ b3 += k4 + 11;
+ b2 += b3 + k3 + t0;
+ b3 = ((b3 << 33) | (b3 >> (64 - 33))) ^ b2;
+
+ b0 += b3;
+ b3 = ((b3 << 46) | (b3 >> (64 - 46))) ^ b0;
+
+ b2 += b1;
+ b1 = ((b1 << 12) | (b1 >> (64 - 12))) ^ b2;
+
+ b0 += b1;
+ b1 = ((b1 << 58) | (b1 >> (64 - 58))) ^ b0;
+
+ b2 += b3;
+ b3 = ((b3 << 22) | (b3 >> (64 - 22))) ^ b2;
+
+ b0 += b3;
+ b3 = ((b3 << 32) | (b3 >> (64 - 32))) ^ b0;
+
+ b2 += b1;
+ b1 = ((b1 << 32) | (b1 >> (64 - 32))) ^ b2;
+
+
+ b1 += k3 + t0;
+ b0 += b1 + k2;
+ b1 = ((b1 << 14) | (b1 >> (64 - 14))) ^ b0;
+
+ b3 += k0 + 12;
+ b2 += b3 + k4 + t1;
+ b3 = ((b3 << 16) | (b3 >> (64 - 16))) ^ b2;
+
+ b0 += b3;
+ b3 = ((b3 << 52) | (b3 >> (64 - 52))) ^ b0;
+
+ b2 += b1;
+ b1 = ((b1 << 57) | (b1 >> (64 - 57))) ^ b2;
+
+ b0 += b1;
+ b1 = ((b1 << 23) | (b1 >> (64 - 23))) ^ b0;
+
+ b2 += b3;
+ b3 = ((b3 << 40) | (b3 >> (64 - 40))) ^ b2;
+
+ b0 += b3;
+ b3 = ((b3 << 5) | (b3 >> (64 - 5))) ^ b0;
+
+ b2 += b1;
+ b1 = ((b1 << 37) | (b1 >> (64 - 37))) ^ b2;
+
+ b1 += k4 + t1;
+ b0 += b1 + k3;
+ b1 = ((b1 << 25) | (b1 >> (64 - 25))) ^ b0;
+
+ b3 += k1 + 13;
+ b2 += b3 + k0 + t2;
+ b3 = ((b3 << 33) | (b3 >> (64 - 33))) ^ b2;
+
+ b0 += b3;
+ b3 = ((b3 << 46) | (b3 >> (64 - 46))) ^ b0;
+
+ b2 += b1;
+ b1 = ((b1 << 12) | (b1 >> (64 - 12))) ^ b2;
+
+ b0 += b1;
+ b1 = ((b1 << 58) | (b1 >> (64 - 58))) ^ b0;
+
+ b2 += b3;
+ b3 = ((b3 << 22) | (b3 >> (64 - 22))) ^ b2;
+
+ b0 += b3;
+ b3 = ((b3 << 32) | (b3 >> (64 - 32))) ^ b0;
+
+ b2 += b1;
+ b1 = ((b1 << 32) | (b1 >> (64 - 32))) ^ b2;
+
+
+ b1 += k0 + t2;
+ b0 += b1 + k4;
+ b1 = ((b1 << 14) | (b1 >> (64 - 14))) ^ b0;
+
+ b3 += k2 + 14;
+ b2 += b3 + k1 + t0;
+ b3 = ((b3 << 16) | (b3 >> (64 - 16))) ^ b2;
+
+ b0 += b3;
+ b3 = ((b3 << 52) | (b3 >> (64 - 52))) ^ b0;
+
+ b2 += b1;
+ b1 = ((b1 << 57) | (b1 >> (64 - 57))) ^ b2;
+
+ b0 += b1;
+ b1 = ((b1 << 23) | (b1 >> (64 - 23))) ^ b0;
+
+ b2 += b3;
+ b3 = ((b3 << 40) | (b3 >> (64 - 40))) ^ b2;
+
+ b0 += b3;
+ b3 = ((b3 << 5) | (b3 >> (64 - 5))) ^ b0;
+
+ b2 += b1;
+ b1 = ((b1 << 37) | (b1 >> (64 - 37))) ^ b2;
+
+ b1 += k1 + t0;
+ b0 += b1 + k0;
+ b1 = ((b1 << 25) | (b1 >> (64 - 25))) ^ b0;
+
+ b3 += k3 + 15;
+ b2 += b3 + k2 + t1;
+ b3 = ((b3 << 33) | (b3 >> (64 - 33))) ^ b2;
+
+ b0 += b3;
+ b3 = ((b3 << 46) | (b3 >> (64 - 46))) ^ b0;
+
+ b2 += b1;
+ b1 = ((b1 << 12) | (b1 >> (64 - 12))) ^ b2;
+
+ b0 += b1;
+ b1 = ((b1 << 58) | (b1 >> (64 - 58))) ^ b0;
+
+ b2 += b3;
+ b3 = ((b3 << 22) | (b3 >> (64 - 22))) ^ b2;
+
+ b0 += b3;
+ b3 = ((b3 << 32) | (b3 >> (64 - 32))) ^ b0;
+
+ b2 += b1;
+ b1 = ((b1 << 32) | (b1 >> (64 - 32))) ^ b2;
+
+
+ b1 += k2 + t1;
+ b0 += b1 + k1;
+ b1 = ((b1 << 14) | (b1 >> (64 - 14))) ^ b0;
+
+ b3 += k4 + 16;
+ b2 += b3 + k3 + t2;
+ b3 = ((b3 << 16) | (b3 >> (64 - 16))) ^ b2;
+
+ b0 += b3;
+ b3 = ((b3 << 52) | (b3 >> (64 - 52))) ^ b0;
+
+ b2 += b1;
+ b1 = ((b1 << 57) | (b1 >> (64 - 57))) ^ b2;
+
+ b0 += b1;
+ b1 = ((b1 << 23) | (b1 >> (64 - 23))) ^ b0;
+
+ b2 += b3;
+ b3 = ((b3 << 40) | (b3 >> (64 - 40))) ^ b2;
+
+ b0 += b3;
+ b3 = ((b3 << 5) | (b3 >> (64 - 5))) ^ b0;
+
+ b2 += b1;
+ b1 = ((b1 << 37) | (b1 >> (64 - 37))) ^ b2;
+
+ b1 += k3 + t2;
+ b0 += b1 + k2;
+ b1 = ((b1 << 25) | (b1 >> (64 - 25))) ^ b0;
+
+ b3 += k0 + 17;
+ b2 += b3 + k4 + t0;
+ b3 = ((b3 << 33) | (b3 >> (64 - 33))) ^ b2;
+
+ b0 += b3;
+ b3 = ((b3 << 46) | (b3 >> (64 - 46))) ^ b0;
+
+ b2 += b1;
+ b1 = ((b1 << 12) | (b1 >> (64 - 12))) ^ b2;
+
+ b0 += b1;
+ b1 = ((b1 << 58) | (b1 >> (64 - 58))) ^ b0;
+
+ b2 += b3;
+ b3 = ((b3 << 22) | (b3 >> (64 - 22))) ^ b2;
+
+ b0 += b3;
+ b3 = ((b3 << 32) | (b3 >> (64 - 32))) ^ b0;
+
+ b2 += b1;
+ b1 = ((b1 << 32) | (b1 >> (64 - 32))) ^ b2;
+
+ output[0] = b0 + k3;
+ output[1] = b1 + k4 + t0;
+ output[2] = b2 + k0 + t1;
+ output[3] = b3 + k1 + 18;
+}
+
+void threefish_decrypt_256(struct threefish_key *key_ctx, u64 *input,
+ u64 *output)
+{
+ u64 b0 = input[0], b1 = input[1],
+ b2 = input[2], b3 = input[3];
+ u64 k0 = key_ctx->key[0], k1 = key_ctx->key[1],
+ k2 = key_ctx->key[2], k3 = key_ctx->key[3],
+ k4 = key_ctx->key[4];
+ u64 t0 = key_ctx->tweak[0], t1 = key_ctx->tweak[1],
+ t2 = key_ctx->tweak[2];
+
+ u64 tmp;
+
+ b0 -= k3;
+ b1 -= k4 + t0;
+ b2 -= k0 + t1;
+ b3 -= k1 + 18;
+ tmp = b3 ^ b0;
+ b3 = (tmp >> 32) | (tmp << (64 - 32));
+ b0 -= b3;
+
+ tmp = b1 ^ b2;
+ b1 = (tmp >> 32) | (tmp << (64 - 32));
+ b2 -= b1;
+
+ tmp = b1 ^ b0;
+ b1 = (tmp >> 58) | (tmp << (64 - 58));
+ b0 -= b1;
+
+ tmp = b3 ^ b2;
+ b3 = (tmp >> 22) | (tmp << (64 - 22));
+ b2 -= b3;
+
+ tmp = b3 ^ b0;
+ b3 = (tmp >> 46) | (tmp << (64 - 46));
+ b0 -= b3;
+
+ tmp = b1 ^ b2;
+ b1 = (tmp >> 12) | (tmp << (64 - 12));
+ b2 -= b1;
+
+ tmp = b1 ^ b0;
+ b1 = (tmp >> 25) | (tmp << (64 - 25));
+ b0 -= b1 + k2;
+ b1 -= k3 + t2;
+
+ tmp = b3 ^ b2;
+ b3 = (tmp >> 33) | (tmp << (64 - 33));
+ b2 -= b3 + k4 + t0;
+ b3 -= k0 + 17;
+
+ tmp = b3 ^ b0;
+ b3 = (tmp >> 5) | (tmp << (64 - 5));
+ b0 -= b3;
+
+ tmp = b1 ^ b2;
+ b1 = (tmp >> 37) | (tmp << (64 - 37));
+ b2 -= b1;
+
+ tmp = b1 ^ b0;
+ b1 = (tmp >> 23) | (tmp << (64 - 23));
+ b0 -= b1;
+
+ tmp = b3 ^ b2;
+ b3 = (tmp >> 40) | (tmp << (64 - 40));
+ b2 -= b3;
+
+ tmp = b3 ^ b0;
+ b3 = (tmp >> 52) | (tmp << (64 - 52));
+ b0 -= b3;
+
+ tmp = b1 ^ b2;
+ b1 = (tmp >> 57) | (tmp << (64 - 57));
+ b2 -= b1;
+
+ tmp = b1 ^ b0;
+ b1 = (tmp >> 14) | (tmp << (64 - 14));
+ b0 -= b1 + k1;
+ b1 -= k2 + t1;
+
+ tmp = b3 ^ b2;
+ b3 = (tmp >> 16) | (tmp << (64 - 16));
+ b2 -= b3 + k3 + t2;
+ b3 -= k4 + 16;
+
+
+ tmp = b3 ^ b0;
+ b3 = (tmp >> 32) | (tmp << (64 - 32));
+ b0 -= b3;
+
+ tmp = b1 ^ b2;
+ b1 = (tmp >> 32) | (tmp << (64 - 32));
+ b2 -= b1;
+
+ tmp = b1 ^ b0;
+ b1 = (tmp >> 58) | (tmp << (64 - 58));
+ b0 -= b1;
+
+ tmp = b3 ^ b2;
+ b3 = (tmp >> 22) | (tmp << (64 - 22));
+ b2 -= b3;
+
+ tmp = b3 ^ b0;
+ b3 = (tmp >> 46) | (tmp << (64 - 46));
+ b0 -= b3;
+
+ tmp = b1 ^ b2;
+ b1 = (tmp >> 12) | (tmp << (64 - 12));
+ b2 -= b1;
+
+ tmp = b1 ^ b0;
+ b1 = (tmp >> 25) | (tmp << (64 - 25));
+ b0 -= b1 + k0;
+ b1 -= k1 + t0;
+
+ tmp = b3 ^ b2;
+ b3 = (tmp >> 33) | (tmp << (64 - 33));
+ b2 -= b3 + k2 + t1;
+ b3 -= k3 + 15;
+
+ tmp = b3 ^ b0;
+ b3 = (tmp >> 5) | (tmp << (64 - 5));
+ b0 -= b3;
+
+ tmp = b1 ^ b2;
+ b1 = (tmp >> 37) | (tmp << (64 - 37));
+ b2 -= b1;
+
+ tmp = b1 ^ b0;
+ b1 = (tmp >> 23) | (tmp << (64 - 23));
+ b0 -= b1;
+
+ tmp = b3 ^ b2;
+ b3 = (tmp >> 40) | (tmp << (64 - 40));
+ b2 -= b3;
+
+ tmp = b3 ^ b0;
+ b3 = (tmp >> 52) | (tmp << (64 - 52));
+ b0 -= b3;
+
+ tmp = b1 ^ b2;
+ b1 = (tmp >> 57) | (tmp << (64 - 57));
+ b2 -= b1;
+
+ tmp = b1 ^ b0;
+ b1 = (tmp >> 14) | (tmp << (64 - 14));
+ b0 -= b1 + k4;
+ b1 -= k0 + t2;
+
+ tmp = b3 ^ b2;
+ b3 = (tmp >> 16) | (tmp << (64 - 16));
+ b2 -= b3 + k1 + t0;
+ b3 -= k2 + 14;
+
+
+ tmp = b3 ^ b0;
+ b3 = (tmp >> 32) | (tmp << (64 - 32));
+ b0 -= b3;
+
+ tmp = b1 ^ b2;
+ b1 = (tmp >> 32) | (tmp << (64 - 32));
+ b2 -= b1;
+
+ tmp = b1 ^ b0;
+ b1 = (tmp >> 58) | (tmp << (64 - 58));
+ b0 -= b1;
+
+ tmp = b3 ^ b2;
+ b3 = (tmp >> 22) | (tmp << (64 - 22));
+ b2 -= b3;
+
+ tmp = b3 ^ b0;
+ b3 = (tmp >> 46) | (tmp << (64 - 46));
+ b0 -= b3;
+
+ tmp = b1 ^ b2;
+ b1 = (tmp >> 12) | (tmp << (64 - 12));
+ b2 -= b1;
+
+ tmp = b1 ^ b0;
+ b1 = (tmp >> 25) | (tmp << (64 - 25));
+ b0 -= b1 + k3;
+ b1 -= k4 + t1;
+
+ tmp = b3 ^ b2;
+ b3 = (tmp >> 33) | (tmp << (64 - 33));
+ b2 -= b3 + k0 + t2;
+ b3 -= k1 + 13;
+
+ tmp = b3 ^ b0;
+ b3 = (tmp >> 5) | (tmp << (64 - 5));
+ b0 -= b3;
+
+ tmp = b1 ^ b2;
+ b1 = (tmp >> 37) | (tmp << (64 - 37));
+ b2 -= b1;
+
+ tmp = b1 ^ b0;
+ b1 = (tmp >> 23) | (tmp << (64 - 23));
+ b0 -= b1;
+
+ tmp = b3 ^ b2;
+ b3 = (tmp >> 40) | (tmp << (64 - 40));
+ b2 -= b3;
+
+ tmp = b3 ^ b0;
+ b3 = (tmp >> 52) | (tmp << (64 - 52));
+ b0 -= b3;
+
+ tmp = b1 ^ b2;
+ b1 = (tmp >> 57) | (tmp << (64 - 57));
+ b2 -= b1;
+
+ tmp = b1 ^ b0;
+ b1 = (tmp >> 14) | (tmp << (64 - 14));
+ b0 -= b1 + k2;
+ b1 -= k3 + t0;
+
+ tmp = b3 ^ b2;
+ b3 = (tmp >> 16) | (tmp << (64 - 16));
+ b2 -= b3 + k4 + t1;
+ b3 -= k0 + 12;
+
+
+ tmp = b3 ^ b0;
+ b3 = (tmp >> 32) | (tmp << (64 - 32));
+ b0 -= b3;
+
+ tmp = b1 ^ b2;
+ b1 = (tmp >> 32) | (tmp << (64 - 32));
+ b2 -= b1;
+
+ tmp = b1 ^ b0;
+ b1 = (tmp >> 58) | (tmp << (64 - 58));
+ b0 -= b1;
+
+ tmp = b3 ^ b2;
+ b3 = (tmp >> 22) | (tmp << (64 - 22));
+ b2 -= b3;
+
+ tmp = b3 ^ b0;
+ b3 = (tmp >> 46) | (tmp << (64 - 46));
+ b0 -= b3;
+
+ tmp = b1 ^ b2;
+ b1 = (tmp >> 12) | (tmp << (64 - 12));
+ b2 -= b1;
+
+ tmp = b1 ^ b0;
+ b1 = (tmp >> 25) | (tmp << (64 - 25));
+ b0 -= b1 + k1;
+ b1 -= k2 + t2;
+
+ tmp = b3 ^ b2;
+ b3 = (tmp >> 33) | (tmp << (64 - 33));
+ b2 -= b3 + k3 + t0;
+ b3 -= k4 + 11;
+
+ tmp = b3 ^ b0;
+ b3 = (tmp >> 5) | (tmp << (64 - 5));
+ b0 -= b3;
+
+ tmp = b1 ^ b2;
+ b1 = (tmp >> 37) | (tmp << (64 - 37));
+ b2 -= b1;
+
+ tmp = b1 ^ b0;
+ b1 = (tmp >> 23) | (tmp << (64 - 23));
+ b0 -= b1;
+
+ tmp = b3 ^ b2;
+ b3 = (tmp >> 40) | (tmp << (64 - 40));
+ b2 -= b3;
+
+ tmp = b3 ^ b0;
+ b3 = (tmp >> 52) | (tmp << (64 - 52));
+ b0 -= b3;
+
+ tmp = b1 ^ b2;
+ b1 = (tmp >> 57) | (tmp << (64 - 57));
+ b2 -= b1;
+
+ tmp = b1 ^ b0;
+ b1 = (tmp >> 14) | (tmp << (64 - 14));
+ b0 -= b1 + k0;
+ b1 -= k1 + t1;
+
+ tmp = b3 ^ b2;
+ b3 = (tmp >> 16) | (tmp << (64 - 16));
+ b2 -= b3 + k2 + t2;
+ b3 -= k3 + 10;
+
+
+ tmp = b3 ^ b0;
+ b3 = (tmp >> 32) | (tmp << (64 - 32));
+ b0 -= b3;
+
+ tmp = b1 ^ b2;
+ b1 = (tmp >> 32) | (tmp << (64 - 32));
+ b2 -= b1;
+
+ tmp = b1 ^ b0;
+ b1 = (tmp >> 58) | (tmp << (64 - 58));
+ b0 -= b1;
+
+ tmp = b3 ^ b2;
+ b3 = (tmp >> 22) | (tmp << (64 - 22));
+ b2 -= b3;
+
+ tmp = b3 ^ b0;
+ b3 = (tmp >> 46) | (tmp << (64 - 46));
+ b0 -= b3;
+
+ tmp = b1 ^ b2;
+ b1 = (tmp >> 12) | (tmp << (64 - 12));
+ b2 -= b1;
+
+ tmp = b1 ^ b0;
+ b1 = (tmp >> 25) | (tmp << (64 - 25));
+ b0 -= b1 + k4;
+ b1 -= k0 + t0;
+
+ tmp = b3 ^ b2;
+ b3 = (tmp >> 33) | (tmp << (64 - 33));
+ b2 -= b3 + k1 + t1;
+ b3 -= k2 + 9;
+
+ tmp = b3 ^ b0;
+ b3 = (tmp >> 5) | (tmp << (64 - 5));
+ b0 -= b3;
+
+ tmp = b1 ^ b2;
+ b1 = (tmp >> 37) | (tmp << (64 - 37));
+ b2 -= b1;
+
+ tmp = b1 ^ b0;
+ b1 = (tmp >> 23) | (tmp << (64 - 23));
+ b0 -= b1;
+
+ tmp = b3 ^ b2;
+ b3 = (tmp >> 40) | (tmp << (64 - 40));
+ b2 -= b3;
+
+ tmp = b3 ^ b0;
+ b3 = (tmp >> 52) | (tmp << (64 - 52));
+ b0 -= b3;
+
+ tmp = b1 ^ b2;
+ b1 = (tmp >> 57) | (tmp << (64 - 57));
+ b2 -= b1;
+
+ tmp = b1 ^ b0;
+ b1 = (tmp >> 14) | (tmp << (64 - 14));
+ b0 -= b1 + k3;
+ b1 -= k4 + t2;
+
+ tmp = b3 ^ b2;
+ b3 = (tmp >> 16) | (tmp << (64 - 16));
+ b2 -= b3 + k0 + t0;
+ b3 -= k1 + 8;
+
+
+ tmp = b3 ^ b0;
+ b3 = (tmp >> 32) | (tmp << (64 - 32));
+ b0 -= b3;
+
+ tmp = b1 ^ b2;
+ b1 = (tmp >> 32) | (tmp << (64 - 32));
+ b2 -= b1;
+
+ tmp = b1 ^ b0;
+ b1 = (tmp >> 58) | (tmp << (64 - 58));
+ b0 -= b1;
+
+ tmp = b3 ^ b2;
+ b3 = (tmp >> 22) | (tmp << (64 - 22));
+ b2 -= b3;
+
+ tmp = b3 ^ b0;
+ b3 = (tmp >> 46) | (tmp << (64 - 46));
+ b0 -= b3;
+
+ tmp = b1 ^ b2;
+ b1 = (tmp >> 12) | (tmp << (64 - 12));
+ b2 -= b1;
+
+ tmp = b1 ^ b0;
+ b1 = (tmp >> 25) | (tmp << (64 - 25));
+ b0 -= b1 + k2;
+ b1 -= k3 + t1;
+
+ tmp = b3 ^ b2;
+ b3 = (tmp >> 33) | (tmp << (64 - 33));
+ b2 -= b3 + k4 + t2;
+ b3 -= k0 + 7;
+
+ tmp = b3 ^ b0;
+ b3 = (tmp >> 5) | (tmp << (64 - 5));
+ b0 -= b3;
+
+ tmp = b1 ^ b2;
+ b1 = (tmp >> 37) | (tmp << (64 - 37));
+ b2 -= b1;
+
+ tmp = b1 ^ b0;
+ b1 = (tmp >> 23) | (tmp << (64 - 23));
+ b0 -= b1;
+
+ tmp = b3 ^ b2;
+ b3 = (tmp >> 40) | (tmp << (64 - 40));
+ b2 -= b3;
+
+ tmp = b3 ^ b0;
+ b3 = (tmp >> 52) | (tmp << (64 - 52));
+ b0 -= b3;
+
+ tmp = b1 ^ b2;
+ b1 = (tmp >> 57) | (tmp << (64 - 57));
+ b2 -= b1;
+
+ tmp = b1 ^ b0;
+ b1 = (tmp >> 14) | (tmp << (64 - 14));
+ b0 -= b1 + k1;
+ b1 -= k2 + t0;
+
+ tmp = b3 ^ b2;
+ b3 = (tmp >> 16) | (tmp << (64 - 16));
+ b2 -= b3 + k3 + t1;
+ b3 -= k4 + 6;
+
+
+ tmp = b3 ^ b0;
+ b3 = (tmp >> 32) | (tmp << (64 - 32));
+ b0 -= b3;
+
+ tmp = b1 ^ b2;
+ b1 = (tmp >> 32) | (tmp << (64 - 32));
+ b2 -= b1;
+
+ tmp = b1 ^ b0;
+ b1 = (tmp >> 58) | (tmp << (64 - 58));
+ b0 -= b1;
+
+ tmp = b3 ^ b2;
+ b3 = (tmp >> 22) | (tmp << (64 - 22));
+ b2 -= b3;
+
+ tmp = b3 ^ b0;
+ b3 = (tmp >> 46) | (tmp << (64 - 46));
+ b0 -= b3;
+
+ tmp = b1 ^ b2;
+ b1 = (tmp >> 12) | (tmp << (64 - 12));
+ b2 -= b1;
+
+ tmp = b1 ^ b0;
+ b1 = (tmp >> 25) | (tmp << (64 - 25));
+ b0 -= b1 + k0;
+ b1 -= k1 + t2;
+
+ tmp = b3 ^ b2;
+ b3 = (tmp >> 33) | (tmp << (64 - 33));
+ b2 -= b3 + k2 + t0;
+ b3 -= k3 + 5;
+
+ tmp = b3 ^ b0;
+ b3 = (tmp >> 5) | (tmp << (64 - 5));
+ b0 -= b3;
+
+ tmp = b1 ^ b2;
+ b1 = (tmp >> 37) | (tmp << (64 - 37));
+ b2 -= b1;
+
+ tmp = b1 ^ b0;
+ b1 = (tmp >> 23) | (tmp << (64 - 23));
+ b0 -= b1;
+
+ tmp = b3 ^ b2;
+ b3 = (tmp >> 40) | (tmp << (64 - 40));
+ b2 -= b3;
+
+ tmp = b3 ^ b0;
+ b3 = (tmp >> 52) | (tmp << (64 - 52));
+ b0 -= b3;
+
+ tmp = b1 ^ b2;
+ b1 = (tmp >> 57) | (tmp << (64 - 57));
+ b2 -= b1;
+
+ tmp = b1 ^ b0;
+ b1 = (tmp >> 14) | (tmp << (64 - 14));
+ b0 -= b1 + k4;
+ b1 -= k0 + t1;
+
+ tmp = b3 ^ b2;
+ b3 = (tmp >> 16) | (tmp << (64 - 16));
+ b2 -= b3 + k1 + t2;
+ b3 -= k2 + 4;
+
+
+ tmp = b3 ^ b0;
+ b3 = (tmp >> 32) | (tmp << (64 - 32));
+ b0 -= b3;
+
+ tmp = b1 ^ b2;
+ b1 = (tmp >> 32) | (tmp << (64 - 32));
+ b2 -= b1;
+
+ tmp = b1 ^ b0;
+ b1 = (tmp >> 58) | (tmp << (64 - 58));
+ b0 -= b1;
+
+ tmp = b3 ^ b2;
+ b3 = (tmp >> 22) | (tmp << (64 - 22));
+ b2 -= b3;
+
+ tmp = b3 ^ b0;
+ b3 = (tmp >> 46) | (tmp << (64 - 46));
+ b0 -= b3;
+
+ tmp = b1 ^ b2;
+ b1 = (tmp >> 12) | (tmp << (64 - 12));
+ b2 -= b1;
+
+ tmp = b1 ^ b0;
+ b1 = (tmp >> 25) | (tmp << (64 - 25));
+ b0 -= b1 + k3;
+ b1 -= k4 + t0;
+
+ tmp = b3 ^ b2;
+ b3 = (tmp >> 33) | (tmp << (64 - 33));
+ b2 -= b3 + k0 + t1;
+ b3 -= k1 + 3;
+
+ tmp = b3 ^ b0;
+ b3 = (tmp >> 5) | (tmp << (64 - 5));
+ b0 -= b3;
+
+ tmp = b1 ^ b2;
+ b1 = (tmp >> 37) | (tmp << (64 - 37));
+ b2 -= b1;
+
+ tmp = b1 ^ b0;
+ b1 = (tmp >> 23) | (tmp << (64 - 23));
+ b0 -= b1;
+
+ tmp = b3 ^ b2;
+ b3 = (tmp >> 40) | (tmp << (64 - 40));
+ b2 -= b3;
+
+ tmp = b3 ^ b0;
+ b3 = (tmp >> 52) | (tmp << (64 - 52));
+ b0 -= b3;
+
+ tmp = b1 ^ b2;
+ b1 = (tmp >> 57) | (tmp << (64 - 57));
+ b2 -= b1;
+
+ tmp = b1 ^ b0;
+ b1 = (tmp >> 14) | (tmp << (64 - 14));
+ b0 -= b1 + k2;
+ b1 -= k3 + t2;
+
+ tmp = b3 ^ b2;
+ b3 = (tmp >> 16) | (tmp << (64 - 16));
+ b2 -= b3 + k4 + t0;
+ b3 -= k0 + 2;
+
+
+ tmp = b3 ^ b0;
+ b3 = (tmp >> 32) | (tmp << (64 - 32));
+ b0 -= b3;
+
+ tmp = b1 ^ b2;
+ b1 = (tmp >> 32) | (tmp << (64 - 32));
+ b2 -= b1;
+
+ tmp = b1 ^ b0;
+ b1 = (tmp >> 58) | (tmp << (64 - 58));
+ b0 -= b1;
+
+ tmp = b3 ^ b2;
+ b3 = (tmp >> 22) | (tmp << (64 - 22));
+ b2 -= b3;
+
+ tmp = b3 ^ b0;
+ b3 = (tmp >> 46) | (tmp << (64 - 46));
+ b0 -= b3;
+
+ tmp = b1 ^ b2;
+ b1 = (tmp >> 12) | (tmp << (64 - 12));
+ b2 -= b1;
+
+ tmp = b1 ^ b0;
+ b1 = (tmp >> 25) | (tmp << (64 - 25));
+ b0 -= b1 + k1;
+ b1 -= k2 + t1;
+
+ tmp = b3 ^ b2;
+ b3 = (tmp >> 33) | (tmp << (64 - 33));
+ b2 -= b3 + k3 + t2;
+ b3 -= k4 + 1;
+
+ tmp = b3 ^ b0;
+ b3 = (tmp >> 5) | (tmp << (64 - 5));
+ b0 -= b3;
+
+ tmp = b1 ^ b2;
+ b1 = (tmp >> 37) | (tmp << (64 - 37));
+ b2 -= b1;
+
+ tmp = b1 ^ b0;
+ b1 = (tmp >> 23) | (tmp << (64 - 23));
+ b0 -= b1;
+
+ tmp = b3 ^ b2;
+ b3 = (tmp >> 40) | (tmp << (64 - 40));
+ b2 -= b3;
+
+ tmp = b3 ^ b0;
+ b3 = (tmp >> 52) | (tmp << (64 - 52));
+ b0 -= b3;
+
+ tmp = b1 ^ b2;
+ b1 = (tmp >> 57) | (tmp << (64 - 57));
+ b2 -= b1;
+
+ tmp = b1 ^ b0;
+ b1 = (tmp >> 14) | (tmp << (64 - 14));
+ b0 -= b1 + k0;
+ b1 -= k1 + t0;
+
+ tmp = b3 ^ b2;
+ b3 = (tmp >> 16) | (tmp << (64 - 16));
+ b2 -= b3 + k2 + t1;
+ b3 -= k3;
+
+ output[0] = b0;
+ output[1] = b1;
+ output[2] = b2;
+ output[3] = b3;
+}
+
+void threefish_encrypt_512(struct threefish_key *key_ctx, u64 *input,
+ u64 *output)
+{
+ u64 b0 = input[0], b1 = input[1],
+ b2 = input[2], b3 = input[3],
+ b4 = input[4], b5 = input[5],
+ b6 = input[6], b7 = input[7];
+ u64 k0 = key_ctx->key[0], k1 = key_ctx->key[1],
+ k2 = key_ctx->key[2], k3 = key_ctx->key[3],
+ k4 = key_ctx->key[4], k5 = key_ctx->key[5],
+ k6 = key_ctx->key[6], k7 = key_ctx->key[7],
+ k8 = key_ctx->key[8];
+ u64 t0 = key_ctx->tweak[0], t1 = key_ctx->tweak[1],
+ t2 = key_ctx->tweak[2];
+
+ b1 += k1;
+ b0 += b1 + k0;
+ b1 = ((b1 << 46) | (b1 >> (64 - 46))) ^ b0;
+
+ b3 += k3;
+ b2 += b3 + k2;
+ b3 = ((b3 << 36) | (b3 >> (64 - 36))) ^ b2;
+
+ b5 += k5 + t0;
+ b4 += b5 + k4;
+ b5 = ((b5 << 19) | (b5 >> (64 - 19))) ^ b4;
+
+ b7 += k7;
+ b6 += b7 + k6 + t1;
+ b7 = ((b7 << 37) | (b7 >> (64 - 37))) ^ b6;
+
+ b2 += b1;
+ b1 = ((b1 << 33) | (b1 >> (64 - 33))) ^ b2;
+
+ b4 += b7;
+ b7 = ((b7 << 27) | (b7 >> (64 - 27))) ^ b4;
+
+ b6 += b5;
+ b5 = ((b5 << 14) | (b5 >> (64 - 14))) ^ b6;
+
+ b0 += b3;
+ b3 = ((b3 << 42) | (b3 >> (64 - 42))) ^ b0;
+
+ b4 += b1;
+ b1 = ((b1 << 17) | (b1 >> (64 - 17))) ^ b4;
+
+ b6 += b3;
+ b3 = ((b3 << 49) | (b3 >> (64 - 49))) ^ b6;
+
+ b0 += b5;
+ b5 = ((b5 << 36) | (b5 >> (64 - 36))) ^ b0;
+
+ b2 += b7;
+ b7 = ((b7 << 39) | (b7 >> (64 - 39))) ^ b2;
+
+ b6 += b1;
+ b1 = ((b1 << 44) | (b1 >> (64 - 44))) ^ b6;
+
+ b0 += b7;
+ b7 = ((b7 << 9) | (b7 >> (64 - 9))) ^ b0;
+
+ b2 += b5;
+ b5 = ((b5 << 54) | (b5 >> (64 - 54))) ^ b2;
+
+ b4 += b3;
+ b3 = ((b3 << 56) | (b3 >> (64 - 56))) ^ b4;
+
+ b1 += k2;
+ b0 += b1 + k1;
+ b1 = ((b1 << 39) | (b1 >> (64 - 39))) ^ b0;
+
+ b3 += k4;
+ b2 += b3 + k3;
+ b3 = ((b3 << 30) | (b3 >> (64 - 30))) ^ b2;
+
+ b5 += k6 + t1;
+ b4 += b5 + k5;
+ b5 = ((b5 << 34) | (b5 >> (64 - 34))) ^ b4;
+
+ b7 += k8 + 1;
+ b6 += b7 + k7 + t2;
+ b7 = ((b7 << 24) | (b7 >> (64 - 24))) ^ b6;
+
+ b2 += b1;
+ b1 = ((b1 << 13) | (b1 >> (64 - 13))) ^ b2;
+
+ b4 += b7;
+ b7 = ((b7 << 50) | (b7 >> (64 - 50))) ^ b4;
+
+ b6 += b5;
+ b5 = ((b5 << 10) | (b5 >> (64 - 10))) ^ b6;
+
+ b0 += b3;
+ b3 = ((b3 << 17) | (b3 >> (64 - 17))) ^ b0;
+
+ b4 += b1;
+ b1 = ((b1 << 25) | (b1 >> (64 - 25))) ^ b4;
+
+ b6 += b3;
+ b3 = ((b3 << 29) | (b3 >> (64 - 29))) ^ b6;
+
+ b0 += b5;
+ b5 = ((b5 << 39) | (b5 >> (64 - 39))) ^ b0;
+
+ b2 += b7;
+ b7 = ((b7 << 43) | (b7 >> (64 - 43))) ^ b2;
+
+ b6 += b1;
+ b1 = ((b1 << 8) | (b1 >> (64 - 8))) ^ b6;
+
+ b0 += b7;
+ b7 = ((b7 << 35) | (b7 >> (64 - 35))) ^ b0;
+
+ b2 += b5;
+ b5 = ((b5 << 56) | (b5 >> (64 - 56))) ^ b2;
+
+ b4 += b3;
+ b3 = ((b3 << 22) | (b3 >> (64 - 22))) ^ b4;
+
+ b1 += k3;
+ b0 += b1 + k2;
+ b1 = ((b1 << 46) | (b1 >> (64 - 46))) ^ b0;
+
+ b3 += k5;
+ b2 += b3 + k4;
+ b3 = ((b3 << 36) | (b3 >> (64 - 36))) ^ b2;
+
+ b5 += k7 + t2;
+ b4 += b5 + k6;
+ b5 = ((b5 << 19) | (b5 >> (64 - 19))) ^ b4;
+
+ b7 += k0 + 2;
+ b6 += b7 + k8 + t0;
+ b7 = ((b7 << 37) | (b7 >> (64 - 37))) ^ b6;
+
+ b2 += b1;
+ b1 = ((b1 << 33) | (b1 >> (64 - 33))) ^ b2;
+
+ b4 += b7;
+ b7 = ((b7 << 27) | (b7 >> (64 - 27))) ^ b4;
+
+ b6 += b5;
+ b5 = ((b5 << 14) | (b5 >> (64 - 14))) ^ b6;
+
+ b0 += b3;
+ b3 = ((b3 << 42) | (b3 >> (64 - 42))) ^ b0;
+
+ b4 += b1;
+ b1 = ((b1 << 17) | (b1 >> (64 - 17))) ^ b4;
+
+ b6 += b3;
+ b3 = ((b3 << 49) | (b3 >> (64 - 49))) ^ b6;
+
+ b0 += b5;
+ b5 = ((b5 << 36) | (b5 >> (64 - 36))) ^ b0;
+
+ b2 += b7;
+ b7 = ((b7 << 39) | (b7 >> (64 - 39))) ^ b2;
+
+ b6 += b1;
+ b1 = ((b1 << 44) | (b1 >> (64 - 44))) ^ b6;
+
+ b0 += b7;
+ b7 = ((b7 << 9) | (b7 >> (64 - 9))) ^ b0;
+
+ b2 += b5;
+ b5 = ((b5 << 54) | (b5 >> (64 - 54))) ^ b2;
+
+ b4 += b3;
+ b3 = ((b3 << 56) | (b3 >> (64 - 56))) ^ b4;
+
+ b1 += k4;
+ b0 += b1 + k3;
+ b1 = ((b1 << 39) | (b1 >> (64 - 39))) ^ b0;
+
+ b3 += k6;
+ b2 += b3 + k5;
+ b3 = ((b3 << 30) | (b3 >> (64 - 30))) ^ b2;
+
+ b5 += k8 + t0;
+ b4 += b5 + k7;
+ b5 = ((b5 << 34) | (b5 >> (64 - 34))) ^ b4;
+
+ b7 += k1 + 3;
+ b6 += b7 + k0 + t1;
+ b7 = ((b7 << 24) | (b7 >> (64 - 24))) ^ b6;
+
+ b2 += b1;
+ b1 = ((b1 << 13) | (b1 >> (64 - 13))) ^ b2;
+
+ b4 += b7;
+ b7 = ((b7 << 50) | (b7 >> (64 - 50))) ^ b4;
+
+ b6 += b5;
+ b5 = ((b5 << 10) | (b5 >> (64 - 10))) ^ b6;
+
+ b0 += b3;
+ b3 = ((b3 << 17) | (b3 >> (64 - 17))) ^ b0;
+
+ b4 += b1;
+ b1 = ((b1 << 25) | (b1 >> (64 - 25))) ^ b4;
+
+ b6 += b3;
+ b3 = ((b3 << 29) | (b3 >> (64 - 29))) ^ b6;
+
+ b0 += b5;
+ b5 = ((b5 << 39) | (b5 >> (64 - 39))) ^ b0;
+
+ b2 += b7;
+ b7 = ((b7 << 43) | (b7 >> (64 - 43))) ^ b2;
+
+ b6 += b1;
+ b1 = ((b1 << 8) | (b1 >> (64 - 8))) ^ b6;
+
+ b0 += b7;
+ b7 = ((b7 << 35) | (b7 >> (64 - 35))) ^ b0;
+
+ b2 += b5;
+ b5 = ((b5 << 56) | (b5 >> (64 - 56))) ^ b2;
+
+ b4 += b3;
+ b3 = ((b3 << 22) | (b3 >> (64 - 22))) ^ b4;
+
+ b1 += k5;
+ b0 += b1 + k4;
+ b1 = ((b1 << 46) | (b1 >> (64 - 46))) ^ b0;
+
+ b3 += k7;
+ b2 += b3 + k6;
+ b3 = ((b3 << 36) | (b3 >> (64 - 36))) ^ b2;
+
+ b5 += k0 + t1;
+ b4 += b5 + k8;
+ b5 = ((b5 << 19) | (b5 >> (64 - 19))) ^ b4;
+
+ b7 += k2 + 4;
+ b6 += b7 + k1 + t2;
+ b7 = ((b7 << 37) | (b7 >> (64 - 37))) ^ b6;
+
+ b2 += b1;
+ b1 = ((b1 << 33) | (b1 >> (64 - 33))) ^ b2;
+
+ b4 += b7;
+ b7 = ((b7 << 27) | (b7 >> (64 - 27))) ^ b4;
+
+ b6 += b5;
+ b5 = ((b5 << 14) | (b5 >> (64 - 14))) ^ b6;
+
+ b0 += b3;
+ b3 = ((b3 << 42) | (b3 >> (64 - 42))) ^ b0;
+
+ b4 += b1;
+ b1 = ((b1 << 17) | (b1 >> (64 - 17))) ^ b4;
+
+ b6 += b3;
+ b3 = ((b3 << 49) | (b3 >> (64 - 49))) ^ b6;
+
+ b0 += b5;
+ b5 = ((b5 << 36) | (b5 >> (64 - 36))) ^ b0;
+
+ b2 += b7;
+ b7 = ((b7 << 39) | (b7 >> (64 - 39))) ^ b2;
+
+ b6 += b1;
+ b1 = ((b1 << 44) | (b1 >> (64 - 44))) ^ b6;
+
+ b0 += b7;
+ b7 = ((b7 << 9) | (b7 >> (64 - 9))) ^ b0;
+
+ b2 += b5;
+ b5 = ((b5 << 54) | (b5 >> (64 - 54))) ^ b2;
+
+ b4 += b3;
+ b3 = ((b3 << 56) | (b3 >> (64 - 56))) ^ b4;
+
+ b1 += k6;
+ b0 += b1 + k5;
+ b1 = ((b1 << 39) | (b1 >> (64 - 39))) ^ b0;
+
+ b3 += k8;
+ b2 += b3 + k7;
+ b3 = ((b3 << 30) | (b3 >> (64 - 30))) ^ b2;
+
+ b5 += k1 + t2;
+ b4 += b5 + k0;
+ b5 = ((b5 << 34) | (b5 >> (64 - 34))) ^ b4;
+
+ b7 += k3 + 5;
+ b6 += b7 + k2 + t0;
+ b7 = ((b7 << 24) | (b7 >> (64 - 24))) ^ b6;
+
+ b2 += b1;
+ b1 = ((b1 << 13) | (b1 >> (64 - 13))) ^ b2;
+
+ b4 += b7;
+ b7 = ((b7 << 50) | (b7 >> (64 - 50))) ^ b4;
+
+ b6 += b5;
+ b5 = ((b5 << 10) | (b5 >> (64 - 10))) ^ b6;
+
+ b0 += b3;
+ b3 = ((b3 << 17) | (b3 >> (64 - 17))) ^ b0;
+
+ b4 += b1;
+ b1 = ((b1 << 25) | (b1 >> (64 - 25))) ^ b4;
+
+ b6 += b3;
+ b3 = ((b3 << 29) | (b3 >> (64 - 29))) ^ b6;
+
+ b0 += b5;
+ b5 = ((b5 << 39) | (b5 >> (64 - 39))) ^ b0;
+
+ b2 += b7;
+ b7 = ((b7 << 43) | (b7 >> (64 - 43))) ^ b2;
+
+ b6 += b1;
+ b1 = ((b1 << 8) | (b1 >> (64 - 8))) ^ b6;
+
+ b0 += b7;
+ b7 = ((b7 << 35) | (b7 >> (64 - 35))) ^ b0;
+
+ b2 += b5;
+ b5 = ((b5 << 56) | (b5 >> (64 - 56))) ^ b2;
+
+ b4 += b3;
+ b3 = ((b3 << 22) | (b3 >> (64 - 22))) ^ b4;
+
+ b1 += k7;
+ b0 += b1 + k6;
+ b1 = ((b1 << 46) | (b1 >> (64 - 46))) ^ b0;
+
+ b3 += k0;
+ b2 += b3 + k8;
+ b3 = ((b3 << 36) | (b3 >> (64 - 36))) ^ b2;
+
+ b5 += k2 + t0;
+ b4 += b5 + k1;
+ b5 = ((b5 << 19) | (b5 >> (64 - 19))) ^ b4;
+
+ b7 += k4 + 6;
+ b6 += b7 + k3 + t1;
+ b7 = ((b7 << 37) | (b7 >> (64 - 37))) ^ b6;
+
+ b2 += b1;
+ b1 = ((b1 << 33) | (b1 >> (64 - 33))) ^ b2;
+
+ b4 += b7;
+ b7 = ((b7 << 27) | (b7 >> (64 - 27))) ^ b4;
+
+ b6 += b5;
+ b5 = ((b5 << 14) | (b5 >> (64 - 14))) ^ b6;
+
+ b0 += b3;
+ b3 = ((b3 << 42) | (b3 >> (64 - 42))) ^ b0;
+
+ b4 += b1;
+ b1 = ((b1 << 17) | (b1 >> (64 - 17))) ^ b4;
+
+ b6 += b3;
+ b3 = ((b3 << 49) | (b3 >> (64 - 49))) ^ b6;
+
+ b0 += b5;
+ b5 = ((b5 << 36) | (b5 >> (64 - 36))) ^ b0;
+
+ b2 += b7;
+ b7 = ((b7 << 39) | (b7 >> (64 - 39))) ^ b2;
+
+ b6 += b1;
+ b1 = ((b1 << 44) | (b1 >> (64 - 44))) ^ b6;
+
+ b0 += b7;
+ b7 = ((b7 << 9) | (b7 >> (64 - 9))) ^ b0;
+
+ b2 += b5;
+ b5 = ((b5 << 54) | (b5 >> (64 - 54))) ^ b2;
+
+ b4 += b3;
+ b3 = ((b3 << 56) | (b3 >> (64 - 56))) ^ b4;
+
+ b1 += k8;
+ b0 += b1 + k7;
+ b1 = ((b1 << 39) | (b1 >> (64 - 39))) ^ b0;
+
+ b3 += k1;
+ b2 += b3 + k0;
+ b3 = ((b3 << 30) | (b3 >> (64 - 30))) ^ b2;
+
+ b5 += k3 + t1;
+ b4 += b5 + k2;
+ b5 = ((b5 << 34) | (b5 >> (64 - 34))) ^ b4;
+
+ b7 += k5 + 7;
+ b6 += b7 + k4 + t2;
+ b7 = ((b7 << 24) | (b7 >> (64 - 24))) ^ b6;
+
+ b2 += b1;
+ b1 = ((b1 << 13) | (b1 >> (64 - 13))) ^ b2;
+
+ b4 += b7;
+ b7 = ((b7 << 50) | (b7 >> (64 - 50))) ^ b4;
+
+ b6 += b5;
+ b5 = ((b5 << 10) | (b5 >> (64 - 10))) ^ b6;
+
+ b0 += b3;
+ b3 = ((b3 << 17) | (b3 >> (64 - 17))) ^ b0;
+
+ b4 += b1;
+ b1 = ((b1 << 25) | (b1 >> (64 - 25))) ^ b4;
+
+ b6 += b3;
+ b3 = ((b3 << 29) | (b3 >> (64 - 29))) ^ b6;
+
+ b0 += b5;
+ b5 = ((b5 << 39) | (b5 >> (64 - 39))) ^ b0;
+
+ b2 += b7;
+ b7 = ((b7 << 43) | (b7 >> (64 - 43))) ^ b2;
+
+ b6 += b1;
+ b1 = ((b1 << 8) | (b1 >> (64 - 8))) ^ b6;
+
+ b0 += b7;
+ b7 = ((b7 << 35) | (b7 >> (64 - 35))) ^ b0;
+
+ b2 += b5;
+ b5 = ((b5 << 56) | (b5 >> (64 - 56))) ^ b2;
+
+ b4 += b3;
+ b3 = ((b3 << 22) | (b3 >> (64 - 22))) ^ b4;
+
+ b1 += k0;
+ b0 += b1 + k8;
+ b1 = ((b1 << 46) | (b1 >> (64 - 46))) ^ b0;
+
+ b3 += k2;
+ b2 += b3 + k1;
+ b3 = ((b3 << 36) | (b3 >> (64 - 36))) ^ b2;
+
+ b5 += k4 + t2;
+ b4 += b5 + k3;
+ b5 = ((b5 << 19) | (b5 >> (64 - 19))) ^ b4;
+
+ b7 += k6 + 8;
+ b6 += b7 + k5 + t0;
+ b7 = ((b7 << 37) | (b7 >> (64 - 37))) ^ b6;
+
+ b2 += b1;
+ b1 = ((b1 << 33) | (b1 >> (64 - 33))) ^ b2;
+
+ b4 += b7;
+ b7 = ((b7 << 27) | (b7 >> (64 - 27))) ^ b4;
+
+ b6 += b5;
+ b5 = ((b5 << 14) | (b5 >> (64 - 14))) ^ b6;
+
+ b0 += b3;
+ b3 = ((b3 << 42) | (b3 >> (64 - 42))) ^ b0;
+
+ b4 += b1;
+ b1 = ((b1 << 17) | (b1 >> (64 - 17))) ^ b4;
+
+ b6 += b3;
+ b3 = ((b3 << 49) | (b3 >> (64 - 49))) ^ b6;
+
+ b0 += b5;
+ b5 = ((b5 << 36) | (b5 >> (64 - 36))) ^ b0;
+
+ b2 += b7;
+ b7 = ((b7 << 39) | (b7 >> (64 - 39))) ^ b2;
+
+ b6 += b1;
+ b1 = ((b1 << 44) | (b1 >> (64 - 44))) ^ b6;
+
+ b0 += b7;
+ b7 = ((b7 << 9) | (b7 >> (64 - 9))) ^ b0;
+
+ b2 += b5;
+ b5 = ((b5 << 54) | (b5 >> (64 - 54))) ^ b2;
+
+ b4 += b3;
+ b3 = ((b3 << 56) | (b3 >> (64 - 56))) ^ b4;
+
+ b1 += k1;
+ b0 += b1 + k0;
+ b1 = ((b1 << 39) | (b1 >> (64 - 39))) ^ b0;
+
+ b3 += k3;
+ b2 += b3 + k2;
+ b3 = ((b3 << 30) | (b3 >> (64 - 30))) ^ b2;
+
+ b5 += k5 + t0;
+ b4 += b5 + k4;
+ b5 = ((b5 << 34) | (b5 >> (64 - 34))) ^ b4;
+
+ b7 += k7 + 9;
+ b6 += b7 + k6 + t1;
+ b7 = ((b7 << 24) | (b7 >> (64 - 24))) ^ b6;
+
+ b2 += b1;
+ b1 = ((b1 << 13) | (b1 >> (64 - 13))) ^ b2;
+
+ b4 += b7;
+ b7 = ((b7 << 50) | (b7 >> (64 - 50))) ^ b4;
+
+ b6 += b5;
+ b5 = ((b5 << 10) | (b5 >> (64 - 10))) ^ b6;
+
+ b0 += b3;
+ b3 = ((b3 << 17) | (b3 >> (64 - 17))) ^ b0;
+
+ b4 += b1;
+ b1 = ((b1 << 25) | (b1 >> (64 - 25))) ^ b4;
+
+ b6 += b3;
+ b3 = ((b3 << 29) | (b3 >> (64 - 29))) ^ b6;
+
+ b0 += b5;
+ b5 = ((b5 << 39) | (b5 >> (64 - 39))) ^ b0;
+
+ b2 += b7;
+ b7 = ((b7 << 43) | (b7 >> (64 - 43))) ^ b2;
+
+ b6 += b1;
+ b1 = ((b1 << 8) | (b1 >> (64 - 8))) ^ b6;
+
+ b0 += b7;
+ b7 = ((b7 << 35) | (b7 >> (64 - 35))) ^ b0;
+
+ b2 += b5;
+ b5 = ((b5 << 56) | (b5 >> (64 - 56))) ^ b2;
+
+ b4 += b3;
+ b3 = ((b3 << 22) | (b3 >> (64 - 22))) ^ b4;
+
+ b1 += k2;
+ b0 += b1 + k1;
+ b1 = ((b1 << 46) | (b1 >> (64 - 46))) ^ b0;
+
+ b3 += k4;
+ b2 += b3 + k3;
+ b3 = ((b3 << 36) | (b3 >> (64 - 36))) ^ b2;
+
+ b5 += k6 + t1;
+ b4 += b5 + k5;
+ b5 = ((b5 << 19) | (b5 >> (64 - 19))) ^ b4;
+
+ b7 += k8 + 10;
+ b6 += b7 + k7 + t2;
+ b7 = ((b7 << 37) | (b7 >> (64 - 37))) ^ b6;
+
+ b2 += b1;
+ b1 = ((b1 << 33) | (b1 >> (64 - 33))) ^ b2;
+
+ b4 += b7;
+ b7 = ((b7 << 27) | (b7 >> (64 - 27))) ^ b4;
+
+ b6 += b5;
+ b5 = ((b5 << 14) | (b5 >> (64 - 14))) ^ b6;
+
+ b0 += b3;
+ b3 = ((b3 << 42) | (b3 >> (64 - 42))) ^ b0;
+
+ b4 += b1;
+ b1 = ((b1 << 17) | (b1 >> (64 - 17))) ^ b4;
+
+ b6 += b3;
+ b3 = ((b3 << 49) | (b3 >> (64 - 49))) ^ b6;
+
+ b0 += b5;
+ b5 = ((b5 << 36) | (b5 >> (64 - 36))) ^ b0;
+
+ b2 += b7;
+ b7 = ((b7 << 39) | (b7 >> (64 - 39))) ^ b2;
+
+ b6 += b1;
+ b1 = ((b1 << 44) | (b1 >> (64 - 44))) ^ b6;
+
+ b0 += b7;
+ b7 = ((b7 << 9) | (b7 >> (64 - 9))) ^ b0;
+
+ b2 += b5;
+ b5 = ((b5 << 54) | (b5 >> (64 - 54))) ^ b2;
+
+ b4 += b3;
+ b3 = ((b3 << 56) | (b3 >> (64 - 56))) ^ b4;
+
+ b1 += k3;
+ b0 += b1 + k2;
+ b1 = ((b1 << 39) | (b1 >> (64 - 39))) ^ b0;
+
+ b3 += k5;
+ b2 += b3 + k4;
+ b3 = ((b3 << 30) | (b3 >> (64 - 30))) ^ b2;
+
+ b5 += k7 + t2;
+ b4 += b5 + k6;
+ b5 = ((b5 << 34) | (b5 >> (64 - 34))) ^ b4;
+
+ b7 += k0 + 11;
+ b6 += b7 + k8 + t0;
+ b7 = ((b7 << 24) | (b7 >> (64 - 24))) ^ b6;
+
+ b2 += b1;
+ b1 = ((b1 << 13) | (b1 >> (64 - 13))) ^ b2;
+
+ b4 += b7;
+ b7 = ((b7 << 50) | (b7 >> (64 - 50))) ^ b4;
+
+ b6 += b5;
+ b5 = ((b5 << 10) | (b5 >> (64 - 10))) ^ b6;
+
+ b0 += b3;
+ b3 = ((b3 << 17) | (b3 >> (64 - 17))) ^ b0;
+
+ b4 += b1;
+ b1 = ((b1 << 25) | (b1 >> (64 - 25))) ^ b4;
+
+ b6 += b3;
+ b3 = ((b3 << 29) | (b3 >> (64 - 29))) ^ b6;
+
+ b0 += b5;
+ b5 = ((b5 << 39) | (b5 >> (64 - 39))) ^ b0;
+
+ b2 += b7;
+ b7 = ((b7 << 43) | (b7 >> (64 - 43))) ^ b2;
+
+ b6 += b1;
+ b1 = ((b1 << 8) | (b1 >> (64 - 8))) ^ b6;
+
+ b0 += b7;
+ b7 = ((b7 << 35) | (b7 >> (64 - 35))) ^ b0;
+
+ b2 += b5;
+ b5 = ((b5 << 56) | (b5 >> (64 - 56))) ^ b2;
+
+ b4 += b3;
+ b3 = ((b3 << 22) | (b3 >> (64 - 22))) ^ b4;
+
+ b1 += k4;
+ b0 += b1 + k3;
+ b1 = ((b1 << 46) | (b1 >> (64 - 46))) ^ b0;
+
+ b3 += k6;
+ b2 += b3 + k5;
+ b3 = ((b3 << 36) | (b3 >> (64 - 36))) ^ b2;
+
+ b5 += k8 + t0;
+ b4 += b5 + k7;
+ b5 = ((b5 << 19) | (b5 >> (64 - 19))) ^ b4;
+
+ b7 += k1 + 12;
+ b6 += b7 + k0 + t1;
+ b7 = ((b7 << 37) | (b7 >> (64 - 37))) ^ b6;
+
+ b2 += b1;
+ b1 = ((b1 << 33) | (b1 >> (64 - 33))) ^ b2;
+
+ b4 += b7;
+ b7 = ((b7 << 27) | (b7 >> (64 - 27))) ^ b4;
+
+ b6 += b5;
+ b5 = ((b5 << 14) | (b5 >> (64 - 14))) ^ b6;
+
+ b0 += b3;
+ b3 = ((b3 << 42) | (b3 >> (64 - 42))) ^ b0;
+
+ b4 += b1;
+ b1 = ((b1 << 17) | (b1 >> (64 - 17))) ^ b4;
+
+ b6 += b3;
+ b3 = ((b3 << 49) | (b3 >> (64 - 49))) ^ b6;
+
+ b0 += b5;
+ b5 = ((b5 << 36) | (b5 >> (64 - 36))) ^ b0;
+
+ b2 += b7;
+ b7 = ((b7 << 39) | (b7 >> (64 - 39))) ^ b2;
+
+ b6 += b1;
+ b1 = ((b1 << 44) | (b1 >> (64 - 44))) ^ b6;
+
+ b0 += b7;
+ b7 = ((b7 << 9) | (b7 >> (64 - 9))) ^ b0;
+
+ b2 += b5;
+ b5 = ((b5 << 54) | (b5 >> (64 - 54))) ^ b2;
+
+ b4 += b3;
+ b3 = ((b3 << 56) | (b3 >> (64 - 56))) ^ b4;
+
+ b1 += k5;
+ b0 += b1 + k4;
+ b1 = ((b1 << 39) | (b1 >> (64 - 39))) ^ b0;
+
+ b3 += k7;
+ b2 += b3 + k6;
+ b3 = ((b3 << 30) | (b3 >> (64 - 30))) ^ b2;
+
+ b5 += k0 + t1;
+ b4 += b5 + k8;
+ b5 = ((b5 << 34) | (b5 >> (64 - 34))) ^ b4;
+
+ b7 += k2 + 13;
+ b6 += b7 + k1 + t2;
+ b7 = ((b7 << 24) | (b7 >> (64 - 24))) ^ b6;
+
+ b2 += b1;
+ b1 = ((b1 << 13) | (b1 >> (64 - 13))) ^ b2;
+
+ b4 += b7;
+ b7 = ((b7 << 50) | (b7 >> (64 - 50))) ^ b4;
+
+ b6 += b5;
+ b5 = ((b5 << 10) | (b5 >> (64 - 10))) ^ b6;
+
+ b0 += b3;
+ b3 = ((b3 << 17) | (b3 >> (64 - 17))) ^ b0;
+
+ b4 += b1;
+ b1 = ((b1 << 25) | (b1 >> (64 - 25))) ^ b4;
+
+ b6 += b3;
+ b3 = ((b3 << 29) | (b3 >> (64 - 29))) ^ b6;
+
+ b0 += b5;
+ b5 = ((b5 << 39) | (b5 >> (64 - 39))) ^ b0;
+
+ b2 += b7;
+ b7 = ((b7 << 43) | (b7 >> (64 - 43))) ^ b2;
+
+ b6 += b1;
+ b1 = ((b1 << 8) | (b1 >> (64 - 8))) ^ b6;
+
+ b0 += b7;
+ b7 = ((b7 << 35) | (b7 >> (64 - 35))) ^ b0;
+
+ b2 += b5;
+ b5 = ((b5 << 56) | (b5 >> (64 - 56))) ^ b2;
+
+ b4 += b3;
+ b3 = ((b3 << 22) | (b3 >> (64 - 22))) ^ b4;
+
+ b1 += k6;
+ b0 += b1 + k5;
+ b1 = ((b1 << 46) | (b1 >> (64 - 46))) ^ b0;
+
+ b3 += k8;
+ b2 += b3 + k7;
+ b3 = ((b3 << 36) | (b3 >> (64 - 36))) ^ b2;
+
+ b5 += k1 + t2;
+ b4 += b5 + k0;
+ b5 = ((b5 << 19) | (b5 >> (64 - 19))) ^ b4;
+
+ b7 += k3 + 14;
+ b6 += b7 + k2 + t0;
+ b7 = ((b7 << 37) | (b7 >> (64 - 37))) ^ b6;
+
+ b2 += b1;
+ b1 = ((b1 << 33) | (b1 >> (64 - 33))) ^ b2;
+
+ b4 += b7;
+ b7 = ((b7 << 27) | (b7 >> (64 - 27))) ^ b4;
+
+ b6 += b5;
+ b5 = ((b5 << 14) | (b5 >> (64 - 14))) ^ b6;
+
+ b0 += b3;
+ b3 = ((b3 << 42) | (b3 >> (64 - 42))) ^ b0;
+
+ b4 += b1;
+ b1 = ((b1 << 17) | (b1 >> (64 - 17))) ^ b4;
+
+ b6 += b3;
+ b3 = ((b3 << 49) | (b3 >> (64 - 49))) ^ b6;
+
+ b0 += b5;
+ b5 = ((b5 << 36) | (b5 >> (64 - 36))) ^ b0;
+
+ b2 += b7;
+ b7 = ((b7 << 39) | (b7 >> (64 - 39))) ^ b2;
+
+ b6 += b1;
+ b1 = ((b1 << 44) | (b1 >> (64 - 44))) ^ b6;
+
+ b0 += b7;
+ b7 = ((b7 << 9) | (b7 >> (64 - 9))) ^ b0;
+
+ b2 += b5;
+ b5 = ((b5 << 54) | (b5 >> (64 - 54))) ^ b2;
+
+ b4 += b3;
+ b3 = ((b3 << 56) | (b3 >> (64 - 56))) ^ b4;
+
+ b1 += k7;
+ b0 += b1 + k6;
+ b1 = ((b1 << 39) | (b1 >> (64 - 39))) ^ b0;
+
+ b3 += k0;
+ b2 += b3 + k8;
+ b3 = ((b3 << 30) | (b3 >> (64 - 30))) ^ b2;
+
+ b5 += k2 + t0;
+ b4 += b5 + k1;
+ b5 = ((b5 << 34) | (b5 >> (64 - 34))) ^ b4;
+
+ b7 += k4 + 15;
+ b6 += b7 + k3 + t1;
+ b7 = ((b7 << 24) | (b7 >> (64 - 24))) ^ b6;
+
+ b2 += b1;
+ b1 = ((b1 << 13) | (b1 >> (64 - 13))) ^ b2;
+
+ b4 += b7;
+ b7 = ((b7 << 50) | (b7 >> (64 - 50))) ^ b4;
+
+ b6 += b5;
+ b5 = ((b5 << 10) | (b5 >> (64 - 10))) ^ b6;
+
+ b0 += b3;
+ b3 = ((b3 << 17) | (b3 >> (64 - 17))) ^ b0;
+
+ b4 += b1;
+ b1 = ((b1 << 25) | (b1 >> (64 - 25))) ^ b4;
+
+ b6 += b3;
+ b3 = ((b3 << 29) | (b3 >> (64 - 29))) ^ b6;
+
+ b0 += b5;
+ b5 = ((b5 << 39) | (b5 >> (64 - 39))) ^ b0;
+
+ b2 += b7;
+ b7 = ((b7 << 43) | (b7 >> (64 - 43))) ^ b2;
+
+ b6 += b1;
+ b1 = ((b1 << 8) | (b1 >> (64 - 8))) ^ b6;
+
+ b0 += b7;
+ b7 = ((b7 << 35) | (b7 >> (64 - 35))) ^ b0;
+
+ b2 += b5;
+ b5 = ((b5 << 56) | (b5 >> (64 - 56))) ^ b2;
+
+ b4 += b3;
+ b3 = ((b3 << 22) | (b3 >> (64 - 22))) ^ b4;
+
+ b1 += k8;
+ b0 += b1 + k7;
+ b1 = ((b1 << 46) | (b1 >> (64 - 46))) ^ b0;
+
+ b3 += k1;
+ b2 += b3 + k0;
+ b3 = ((b3 << 36) | (b3 >> (64 - 36))) ^ b2;
+
+ b5 += k3 + t1;
+ b4 += b5 + k2;
+ b5 = ((b5 << 19) | (b5 >> (64 - 19))) ^ b4;
+
+ b7 += k5 + 16;
+ b6 += b7 + k4 + t2;
+ b7 = ((b7 << 37) | (b7 >> (64 - 37))) ^ b6;
+
+ b2 += b1;
+ b1 = ((b1 << 33) | (b1 >> (64 - 33))) ^ b2;
+
+ b4 += b7;
+ b7 = ((b7 << 27) | (b7 >> (64 - 27))) ^ b4;
+
+ b6 += b5;
+ b5 = ((b5 << 14) | (b5 >> (64 - 14))) ^ b6;
+
+ b0 += b3;
+ b3 = ((b3 << 42) | (b3 >> (64 - 42))) ^ b0;
+
+ b4 += b1;
+ b1 = ((b1 << 17) | (b1 >> (64 - 17))) ^ b4;
+
+ b6 += b3;
+ b3 = ((b3 << 49) | (b3 >> (64 - 49))) ^ b6;
+
+ b0 += b5;
+ b5 = ((b5 << 36) | (b5 >> (64 - 36))) ^ b0;
+
+ b2 += b7;
+ b7 = ((b7 << 39) | (b7 >> (64 - 39))) ^ b2;
+
+ b6 += b1;
+ b1 = ((b1 << 44) | (b1 >> (64 - 44))) ^ b6;
+
+ b0 += b7;
+ b7 = ((b7 << 9) | (b7 >> (64 - 9))) ^ b0;
+
+ b2 += b5;
+ b5 = ((b5 << 54) | (b5 >> (64 - 54))) ^ b2;
+
+ b4 += b3;
+ b3 = ((b3 << 56) | (b3 >> (64 - 56))) ^ b4;
+
+ b1 += k0;
+ b0 += b1 + k8;
+ b1 = ((b1 << 39) | (b1 >> (64 - 39))) ^ b0;
+
+ b3 += k2;
+ b2 += b3 + k1;
+ b3 = ((b3 << 30) | (b3 >> (64 - 30))) ^ b2;
+
+ b5 += k4 + t2;
+ b4 += b5 + k3;
+ b5 = ((b5 << 34) | (b5 >> (64 - 34))) ^ b4;
+
+ b7 += k6 + 17;
+ b6 += b7 + k5 + t0;
+ b7 = ((b7 << 24) | (b7 >> (64 - 24))) ^ b6;
+
+ b2 += b1;
+ b1 = ((b1 << 13) | (b1 >> (64 - 13))) ^ b2;
+
+ b4 += b7;
+ b7 = ((b7 << 50) | (b7 >> (64 - 50))) ^ b4;
+
+ b6 += b5;
+ b5 = ((b5 << 10) | (b5 >> (64 - 10))) ^ b6;
+
+ b0 += b3;
+ b3 = ((b3 << 17) | (b3 >> (64 - 17))) ^ b0;
+
+ b4 += b1;
+ b1 = ((b1 << 25) | (b1 >> (64 - 25))) ^ b4;
+
+ b6 += b3;
+ b3 = ((b3 << 29) | (b3 >> (64 - 29))) ^ b6;
+
+ b0 += b5;
+ b5 = ((b5 << 39) | (b5 >> (64 - 39))) ^ b0;
+
+ b2 += b7;
+ b7 = ((b7 << 43) | (b7 >> (64 - 43))) ^ b2;
+
+ b6 += b1;
+ b1 = ((b1 << 8) | (b1 >> (64 - 8))) ^ b6;
+
+ b0 += b7;
+ b7 = ((b7 << 35) | (b7 >> (64 - 35))) ^ b0;
+
+ b2 += b5;
+ b5 = ((b5 << 56) | (b5 >> (64 - 56))) ^ b2;
+
+ b4 += b3;
+ b3 = ((b3 << 22) | (b3 >> (64 - 22))) ^ b4;
+
+ output[0] = b0 + k0;
+ output[1] = b1 + k1;
+ output[2] = b2 + k2;
+ output[3] = b3 + k3;
+ output[4] = b4 + k4;
+ output[5] = b5 + k5 + t0;
+ output[6] = b6 + k6 + t1;
+ output[7] = b7 + k7 + 18;
+}
+
+void threefish_decrypt_512(struct threefish_key *key_ctx, u64 *input,
+ u64 *output)
+{
+ u64 b0 = input[0], b1 = input[1],
+ b2 = input[2], b3 = input[3],
+ b4 = input[4], b5 = input[5],
+ b6 = input[6], b7 = input[7];
+ u64 k0 = key_ctx->key[0], k1 = key_ctx->key[1],
+ k2 = key_ctx->key[2], k3 = key_ctx->key[3],
+ k4 = key_ctx->key[4], k5 = key_ctx->key[5],
+ k6 = key_ctx->key[6], k7 = key_ctx->key[7],
+ k8 = key_ctx->key[8];
+ u64 t0 = key_ctx->tweak[0], t1 = key_ctx->tweak[1],
+ t2 = key_ctx->tweak[2];
+
+ u64 tmp;
+
+ b0 -= k0;
+ b1 -= k1;
+ b2 -= k2;
+ b3 -= k3;
+ b4 -= k4;
+ b5 -= k5 + t0;
+ b6 -= k6 + t1;
+ b7 -= k7 + 18;
+
+ tmp = b3 ^ b4;
+ b3 = (tmp >> 22) | (tmp << (64 - 22));
+ b4 -= b3;
+
+ tmp = b5 ^ b2;
+ b5 = (tmp >> 56) | (tmp << (64 - 56));
+ b2 -= b5;
+
+ tmp = b7 ^ b0;
+ b7 = (tmp >> 35) | (tmp << (64 - 35));
+ b0 -= b7;
+
+ tmp = b1 ^ b6;
+ b1 = (tmp >> 8) | (tmp << (64 - 8));
+ b6 -= b1;
+
+ tmp = b7 ^ b2;
+ b7 = (tmp >> 43) | (tmp << (64 - 43));
+ b2 -= b7;
+
+ tmp = b5 ^ b0;
+ b5 = (tmp >> 39) | (tmp << (64 - 39));
+ b0 -= b5;
+
+ tmp = b3 ^ b6;
+ b3 = (tmp >> 29) | (tmp << (64 - 29));
+ b6 -= b3;
+
+ tmp = b1 ^ b4;
+ b1 = (tmp >> 25) | (tmp << (64 - 25));
+ b4 -= b1;
+
+ tmp = b3 ^ b0;
+ b3 = (tmp >> 17) | (tmp << (64 - 17));
+ b0 -= b3;
+
+ tmp = b5 ^ b6;
+ b5 = (tmp >> 10) | (tmp << (64 - 10));
+ b6 -= b5;
+
+ tmp = b7 ^ b4;
+ b7 = (tmp >> 50) | (tmp << (64 - 50));
+ b4 -= b7;
+
+ tmp = b1 ^ b2;
+ b1 = (tmp >> 13) | (tmp << (64 - 13));
+ b2 -= b1;
+
+ tmp = b7 ^ b6;
+ b7 = (tmp >> 24) | (tmp << (64 - 24));
+ b6 -= b7 + k5 + t0;
+ b7 -= k6 + 17;
+
+ tmp = b5 ^ b4;
+ b5 = (tmp >> 34) | (tmp << (64 - 34));
+ b4 -= b5 + k3;
+ b5 -= k4 + t2;
+
+ tmp = b3 ^ b2;
+ b3 = (tmp >> 30) | (tmp << (64 - 30));
+ b2 -= b3 + k1;
+ b3 -= k2;
+
+ tmp = b1 ^ b0;
+ b1 = (tmp >> 39) | (tmp << (64 - 39));
+ b0 -= b1 + k8;
+ b1 -= k0;
+
+ tmp = b3 ^ b4;
+ b3 = (tmp >> 56) | (tmp << (64 - 56));
+ b4 -= b3;
+
+ tmp = b5 ^ b2;
+ b5 = (tmp >> 54) | (tmp << (64 - 54));
+ b2 -= b5;
+
+ tmp = b7 ^ b0;
+ b7 = (tmp >> 9) | (tmp << (64 - 9));
+ b0 -= b7;
+
+ tmp = b1 ^ b6;
+ b1 = (tmp >> 44) | (tmp << (64 - 44));
+ b6 -= b1;
+
+ tmp = b7 ^ b2;
+ b7 = (tmp >> 39) | (tmp << (64 - 39));
+ b2 -= b7;
+
+ tmp = b5 ^ b0;
+ b5 = (tmp >> 36) | (tmp << (64 - 36));
+ b0 -= b5;
+
+ tmp = b3 ^ b6;
+ b3 = (tmp >> 49) | (tmp << (64 - 49));
+ b6 -= b3;
+
+ tmp = b1 ^ b4;
+ b1 = (tmp >> 17) | (tmp << (64 - 17));
+ b4 -= b1;
+
+ tmp = b3 ^ b0;
+ b3 = (tmp >> 42) | (tmp << (64 - 42));
+ b0 -= b3;
+
+ tmp = b5 ^ b6;
+ b5 = (tmp >> 14) | (tmp << (64 - 14));
+ b6 -= b5;
+
+ tmp = b7 ^ b4;
+ b7 = (tmp >> 27) | (tmp << (64 - 27));
+ b4 -= b7;
+
+ tmp = b1 ^ b2;
+ b1 = (tmp >> 33) | (tmp << (64 - 33));
+ b2 -= b1;
+
+ tmp = b7 ^ b6;
+ b7 = (tmp >> 37) | (tmp << (64 - 37));
+ b6 -= b7 + k4 + t2;
+ b7 -= k5 + 16;
+
+ tmp = b5 ^ b4;
+ b5 = (tmp >> 19) | (tmp << (64 - 19));
+ b4 -= b5 + k2;
+ b5 -= k3 + t1;
+
+ tmp = b3 ^ b2;
+ b3 = (tmp >> 36) | (tmp << (64 - 36));
+ b2 -= b3 + k0;
+ b3 -= k1;
+
+ tmp = b1 ^ b0;
+ b1 = (tmp >> 46) | (tmp << (64 - 46));
+ b0 -= b1 + k7;
+ b1 -= k8;
+
+ tmp = b3 ^ b4;
+ b3 = (tmp >> 22) | (tmp << (64 - 22));
+ b4 -= b3;
+
+ tmp = b5 ^ b2;
+ b5 = (tmp >> 56) | (tmp << (64 - 56));
+ b2 -= b5;
+
+ tmp = b7 ^ b0;
+ b7 = (tmp >> 35) | (tmp << (64 - 35));
+ b0 -= b7;
+
+ tmp = b1 ^ b6;
+ b1 = (tmp >> 8) | (tmp << (64 - 8));
+ b6 -= b1;
+
+ tmp = b7 ^ b2;
+ b7 = (tmp >> 43) | (tmp << (64 - 43));
+ b2 -= b7;
+
+ tmp = b5 ^ b0;
+ b5 = (tmp >> 39) | (tmp << (64 - 39));
+ b0 -= b5;
+
+ tmp = b3 ^ b6;
+ b3 = (tmp >> 29) | (tmp << (64 - 29));
+ b6 -= b3;
+
+ tmp = b1 ^ b4;
+ b1 = (tmp >> 25) | (tmp << (64 - 25));
+ b4 -= b1;
+
+ tmp = b3 ^ b0;
+ b3 = (tmp >> 17) | (tmp << (64 - 17));
+ b0 -= b3;
+
+ tmp = b5 ^ b6;
+ b5 = (tmp >> 10) | (tmp << (64 - 10));
+ b6 -= b5;
+
+ tmp = b7 ^ b4;
+ b7 = (tmp >> 50) | (tmp << (64 - 50));
+ b4 -= b7;
+
+ tmp = b1 ^ b2;
+ b1 = (tmp >> 13) | (tmp << (64 - 13));
+ b2 -= b1;
+
+ tmp = b7 ^ b6;
+ b7 = (tmp >> 24) | (tmp << (64 - 24));
+ b6 -= b7 + k3 + t1;
+ b7 -= k4 + 15;
+
+ tmp = b5 ^ b4;
+ b5 = (tmp >> 34) | (tmp << (64 - 34));
+ b4 -= b5 + k1;
+ b5 -= k2 + t0;
+
+ tmp = b3 ^ b2;
+ b3 = (tmp >> 30) | (tmp << (64 - 30));
+ b2 -= b3 + k8;
+ b3 -= k0;
+
+ tmp = b1 ^ b0;
+ b1 = (tmp >> 39) | (tmp << (64 - 39));
+ b0 -= b1 + k6;
+ b1 -= k7;
+
+ tmp = b3 ^ b4;
+ b3 = (tmp >> 56) | (tmp << (64 - 56));
+ b4 -= b3;
+
+ tmp = b5 ^ b2;
+ b5 = (tmp >> 54) | (tmp << (64 - 54));
+ b2 -= b5;
+
+ tmp = b7 ^ b0;
+ b7 = (tmp >> 9) | (tmp << (64 - 9));
+ b0 -= b7;
+
+ tmp = b1 ^ b6;
+ b1 = (tmp >> 44) | (tmp << (64 - 44));
+ b6 -= b1;
+
+ tmp = b7 ^ b2;
+ b7 = (tmp >> 39) | (tmp << (64 - 39));
+ b2 -= b7;
+
+ tmp = b5 ^ b0;
+ b5 = (tmp >> 36) | (tmp << (64 - 36));
+ b0 -= b5;
+
+ tmp = b3 ^ b6;
+ b3 = (tmp >> 49) | (tmp << (64 - 49));
+ b6 -= b3;
+
+ tmp = b1 ^ b4;
+ b1 = (tmp >> 17) | (tmp << (64 - 17));
+ b4 -= b1;
+
+ tmp = b3 ^ b0;
+ b3 = (tmp >> 42) | (tmp << (64 - 42));
+ b0 -= b3;
+
+ tmp = b5 ^ b6;
+ b5 = (tmp >> 14) | (tmp << (64 - 14));
+ b6 -= b5;
+
+ tmp = b7 ^ b4;
+ b7 = (tmp >> 27) | (tmp << (64 - 27));
+ b4 -= b7;
+
+ tmp = b1 ^ b2;
+ b1 = (tmp >> 33) | (tmp << (64 - 33));
+ b2 -= b1;
+
+ tmp = b7 ^ b6;
+ b7 = (tmp >> 37) | (tmp << (64 - 37));
+ b6 -= b7 + k2 + t0;
+ b7 -= k3 + 14;
+
+ tmp = b5 ^ b4;
+ b5 = (tmp >> 19) | (tmp << (64 - 19));
+ b4 -= b5 + k0;
+ b5 -= k1 + t2;
+
+ tmp = b3 ^ b2;
+ b3 = (tmp >> 36) | (tmp << (64 - 36));
+ b2 -= b3 + k7;
+ b3 -= k8;
+
+ tmp = b1 ^ b0;
+ b1 = (tmp >> 46) | (tmp << (64 - 46));
+ b0 -= b1 + k5;
+ b1 -= k6;
+
+ tmp = b3 ^ b4;
+ b3 = (tmp >> 22) | (tmp << (64 - 22));
+ b4 -= b3;
+
+ tmp = b5 ^ b2;
+ b5 = (tmp >> 56) | (tmp << (64 - 56));
+ b2 -= b5;
+
+ tmp = b7 ^ b0;
+ b7 = (tmp >> 35) | (tmp << (64 - 35));
+ b0 -= b7;
+
+ tmp = b1 ^ b6;
+ b1 = (tmp >> 8) | (tmp << (64 - 8));
+ b6 -= b1;
+
+ tmp = b7 ^ b2;
+ b7 = (tmp >> 43) | (tmp << (64 - 43));
+ b2 -= b7;
+
+ tmp = b5 ^ b0;
+ b5 = (tmp >> 39) | (tmp << (64 - 39));
+ b0 -= b5;
+
+ tmp = b3 ^ b6;
+ b3 = (tmp >> 29) | (tmp << (64 - 29));
+ b6 -= b3;
+
+ tmp = b1 ^ b4;
+ b1 = (tmp >> 25) | (tmp << (64 - 25));
+ b4 -= b1;
+
+ tmp = b3 ^ b0;
+ b3 = (tmp >> 17) | (tmp << (64 - 17));
+ b0 -= b3;
+
+ tmp = b5 ^ b6;
+ b5 = (tmp >> 10) | (tmp << (64 - 10));
+ b6 -= b5;
+
+ tmp = b7 ^ b4;
+ b7 = (tmp >> 50) | (tmp << (64 - 50));
+ b4 -= b7;
+
+ tmp = b1 ^ b2;
+ b1 = (tmp >> 13) | (tmp << (64 - 13));
+ b2 -= b1;
+
+ tmp = b7 ^ b6;
+ b7 = (tmp >> 24) | (tmp << (64 - 24));
+ b6 -= b7 + k1 + t2;
+ b7 -= k2 + 13;
+
+ tmp = b5 ^ b4;
+ b5 = (tmp >> 34) | (tmp << (64 - 34));
+ b4 -= b5 + k8;
+ b5 -= k0 + t1;
+
+ tmp = b3 ^ b2;
+ b3 = (tmp >> 30) | (tmp << (64 - 30));
+ b2 -= b3 + k6;
+ b3 -= k7;
+
+ tmp = b1 ^ b0;
+ b1 = (tmp >> 39) | (tmp << (64 - 39));
+ b0 -= b1 + k4;
+ b1 -= k5;
+
+ tmp = b3 ^ b4;
+ b3 = (tmp >> 56) | (tmp << (64 - 56));
+ b4 -= b3;
+
+ tmp = b5 ^ b2;
+ b5 = (tmp >> 54) | (tmp << (64 - 54));
+ b2 -= b5;
+
+ tmp = b7 ^ b0;
+ b7 = (tmp >> 9) | (tmp << (64 - 9));
+ b0 -= b7;
+
+ tmp = b1 ^ b6;
+ b1 = (tmp >> 44) | (tmp << (64 - 44));
+ b6 -= b1;
+
+ tmp = b7 ^ b2;
+ b7 = (tmp >> 39) | (tmp << (64 - 39));
+ b2 -= b7;
+
+ tmp = b5 ^ b0;
+ b5 = (tmp >> 36) | (tmp << (64 - 36));
+ b0 -= b5;
+
+ tmp = b3 ^ b6;
+ b3 = (tmp >> 49) | (tmp << (64 - 49));
+ b6 -= b3;
+
+ tmp = b1 ^ b4;
+ b1 = (tmp >> 17) | (tmp << (64 - 17));
+ b4 -= b1;
+
+ tmp = b3 ^ b0;
+ b3 = (tmp >> 42) | (tmp << (64 - 42));
+ b0 -= b3;
+
+ tmp = b5 ^ b6;
+ b5 = (tmp >> 14) | (tmp << (64 - 14));
+ b6 -= b5;
+
+ tmp = b7 ^ b4;
+ b7 = (tmp >> 27) | (tmp << (64 - 27));
+ b4 -= b7;
+
+ tmp = b1 ^ b2;
+ b1 = (tmp >> 33) | (tmp << (64 - 33));
+ b2 -= b1;
+
+ tmp = b7 ^ b6;
+ b7 = (tmp >> 37) | (tmp << (64 - 37));
+ b6 -= b7 + k0 + t1;
+ b7 -= k1 + 12;
+
+ tmp = b5 ^ b4;
+ b5 = (tmp >> 19) | (tmp << (64 - 19));
+ b4 -= b5 + k7;
+ b5 -= k8 + t0;
+
+ tmp = b3 ^ b2;
+ b3 = (tmp >> 36) | (tmp << (64 - 36));
+ b2 -= b3 + k5;
+ b3 -= k6;
+
+ tmp = b1 ^ b0;
+ b1 = (tmp >> 46) | (tmp << (64 - 46));
+ b0 -= b1 + k3;
+ b1 -= k4;
+
+ tmp = b3 ^ b4;
+ b3 = (tmp >> 22) | (tmp << (64 - 22));
+ b4 -= b3;
+
+ tmp = b5 ^ b2;
+ b5 = (tmp >> 56) | (tmp << (64 - 56));
+ b2 -= b5;
+
+ tmp = b7 ^ b0;
+ b7 = (tmp >> 35) | (tmp << (64 - 35));
+ b0 -= b7;
+
+ tmp = b1 ^ b6;
+ b1 = (tmp >> 8) | (tmp << (64 - 8));
+ b6 -= b1;
+
+ tmp = b7 ^ b2;
+ b7 = (tmp >> 43) | (tmp << (64 - 43));
+ b2 -= b7;
+
+ tmp = b5 ^ b0;
+ b5 = (tmp >> 39) | (tmp << (64 - 39));
+ b0 -= b5;
+
+ tmp = b3 ^ b6;
+ b3 = (tmp >> 29) | (tmp << (64 - 29));
+ b6 -= b3;
+
+ tmp = b1 ^ b4;
+ b1 = (tmp >> 25) | (tmp << (64 - 25));
+ b4 -= b1;
+
+ tmp = b3 ^ b0;
+ b3 = (tmp >> 17) | (tmp << (64 - 17));
+ b0 -= b3;
+
+ tmp = b5 ^ b6;
+ b5 = (tmp >> 10) | (tmp << (64 - 10));
+ b6 -= b5;
+
+ tmp = b7 ^ b4;
+ b7 = (tmp >> 50) | (tmp << (64 - 50));
+ b4 -= b7;
+
+ tmp = b1 ^ b2;
+ b1 = (tmp >> 13) | (tmp << (64 - 13));
+ b2 -= b1;
+
+ tmp = b7 ^ b6;
+ b7 = (tmp >> 24) | (tmp << (64 - 24));
+ b6 -= b7 + k8 + t0;
+ b7 -= k0 + 11;
+
+ tmp = b5 ^ b4;
+ b5 = (tmp >> 34) | (tmp << (64 - 34));
+ b4 -= b5 + k6;
+ b5 -= k7 + t2;
+
+ tmp = b3 ^ b2;
+ b3 = (tmp >> 30) | (tmp << (64 - 30));
+ b2 -= b3 + k4;
+ b3 -= k5;
+
+ tmp = b1 ^ b0;
+ b1 = (tmp >> 39) | (tmp << (64 - 39));
+ b0 -= b1 + k2;
+ b1 -= k3;
+
+ tmp = b3 ^ b4;
+ b3 = (tmp >> 56) | (tmp << (64 - 56));
+ b4 -= b3;
+
+ tmp = b5 ^ b2;
+ b5 = (tmp >> 54) | (tmp << (64 - 54));
+ b2 -= b5;
+
+ tmp = b7 ^ b0;
+ b7 = (tmp >> 9) | (tmp << (64 - 9));
+ b0 -= b7;
+
+ tmp = b1 ^ b6;
+ b1 = (tmp >> 44) | (tmp << (64 - 44));
+ b6 -= b1;
+
+ tmp = b7 ^ b2;
+ b7 = (tmp >> 39) | (tmp << (64 - 39));
+ b2 -= b7;
+
+ tmp = b5 ^ b0;
+ b5 = (tmp >> 36) | (tmp << (64 - 36));
+ b0 -= b5;
+
+ tmp = b3 ^ b6;
+ b3 = (tmp >> 49) | (tmp << (64 - 49));
+ b6 -= b3;
+
+ tmp = b1 ^ b4;
+ b1 = (tmp >> 17) | (tmp << (64 - 17));
+ b4 -= b1;
+
+ tmp = b3 ^ b0;
+ b3 = (tmp >> 42) | (tmp << (64 - 42));
+ b0 -= b3;
+
+ tmp = b5 ^ b6;
+ b5 = (tmp >> 14) | (tmp << (64 - 14));
+ b6 -= b5;
+
+ tmp = b7 ^ b4;
+ b7 = (tmp >> 27) | (tmp << (64 - 27));
+ b4 -= b7;
+
+ tmp = b1 ^ b2;
+ b1 = (tmp >> 33) | (tmp << (64 - 33));
+ b2 -= b1;
+
+ tmp = b7 ^ b6;
+ b7 = (tmp >> 37) | (tmp << (64 - 37));
+ b6 -= b7 + k7 + t2;
+ b7 -= k8 + 10;
+
+ tmp = b5 ^ b4;
+ b5 = (tmp >> 19) | (tmp << (64 - 19));
+ b4 -= b5 + k5;
+ b5 -= k6 + t1;
+
+ tmp = b3 ^ b2;
+ b3 = (tmp >> 36) | (tmp << (64 - 36));
+ b2 -= b3 + k3;
+ b3 -= k4;
+
+ tmp = b1 ^ b0;
+ b1 = (tmp >> 46) | (tmp << (64 - 46));
+ b0 -= b1 + k1;
+ b1 -= k2;
+
+ tmp = b3 ^ b4;
+ b3 = (tmp >> 22) | (tmp << (64 - 22));
+ b4 -= b3;
+
+ tmp = b5 ^ b2;
+ b5 = (tmp >> 56) | (tmp << (64 - 56));
+ b2 -= b5;
+
+ tmp = b7 ^ b0;
+ b7 = (tmp >> 35) | (tmp << (64 - 35));
+ b0 -= b7;
+
+ tmp = b1 ^ b6;
+ b1 = (tmp >> 8) | (tmp << (64 - 8));
+ b6 -= b1;
+
+ tmp = b7 ^ b2;
+ b7 = (tmp >> 43) | (tmp << (64 - 43));
+ b2 -= b7;
+
+ tmp = b5 ^ b0;
+ b5 = (tmp >> 39) | (tmp << (64 - 39));
+ b0 -= b5;
+
+ tmp = b3 ^ b6;
+ b3 = (tmp >> 29) | (tmp << (64 - 29));
+ b6 -= b3;
+
+ tmp = b1 ^ b4;
+ b1 = (tmp >> 25) | (tmp << (64 - 25));
+ b4 -= b1;
+
+ tmp = b3 ^ b0;
+ b3 = (tmp >> 17) | (tmp << (64 - 17));
+ b0 -= b3;
+
+ tmp = b5 ^ b6;
+ b5 = (tmp >> 10) | (tmp << (64 - 10));
+ b6 -= b5;
+
+ tmp = b7 ^ b4;
+ b7 = (tmp >> 50) | (tmp << (64 - 50));
+ b4 -= b7;
+
+ tmp = b1 ^ b2;
+ b1 = (tmp >> 13) | (tmp << (64 - 13));
+ b2 -= b1;
+
+ tmp = b7 ^ b6;
+ b7 = (tmp >> 24) | (tmp << (64 - 24));
+ b6 -= b7 + k6 + t1;
+ b7 -= k7 + 9;
+
+ tmp = b5 ^ b4;
+ b5 = (tmp >> 34) | (tmp << (64 - 34));
+ b4 -= b5 + k4;
+ b5 -= k5 + t0;
+
+ tmp = b3 ^ b2;
+ b3 = (tmp >> 30) | (tmp << (64 - 30));
+ b2 -= b3 + k2;
+ b3 -= k3;
+
+ tmp = b1 ^ b0;
+ b1 = (tmp >> 39) | (tmp << (64 - 39));
+ b0 -= b1 + k0;
+ b1 -= k1;
+
+ tmp = b3 ^ b4;
+ b3 = (tmp >> 56) | (tmp << (64 - 56));
+ b4 -= b3;
+
+ tmp = b5 ^ b2;
+ b5 = (tmp >> 54) | (tmp << (64 - 54));
+ b2 -= b5;
+
+ tmp = b7 ^ b0;
+ b7 = (tmp >> 9) | (tmp << (64 - 9));
+ b0 -= b7;
+
+ tmp = b1 ^ b6;
+ b1 = (tmp >> 44) | (tmp << (64 - 44));
+ b6 -= b1;
+
+ tmp = b7 ^ b2;
+ b7 = (tmp >> 39) | (tmp << (64 - 39));
+ b2 -= b7;
+
+ tmp = b5 ^ b0;
+ b5 = (tmp >> 36) | (tmp << (64 - 36));
+ b0 -= b5;
+
+ tmp = b3 ^ b6;
+ b3 = (tmp >> 49) | (tmp << (64 - 49));
+ b6 -= b3;
+
+ tmp = b1 ^ b4;
+ b1 = (tmp >> 17) | (tmp << (64 - 17));
+ b4 -= b1;
+
+ tmp = b3 ^ b0;
+ b3 = (tmp >> 42) | (tmp << (64 - 42));
+ b0 -= b3;
+
+ tmp = b5 ^ b6;
+ b5 = (tmp >> 14) | (tmp << (64 - 14));
+ b6 -= b5;
+
+ tmp = b7 ^ b4;
+ b7 = (tmp >> 27) | (tmp << (64 - 27));
+ b4 -= b7;
+
+ tmp = b1 ^ b2;
+ b1 = (tmp >> 33) | (tmp << (64 - 33));
+ b2 -= b1;
+
+ tmp = b7 ^ b6;
+ b7 = (tmp >> 37) | (tmp << (64 - 37));
+ b6 -= b7 + k5 + t0;
+ b7 -= k6 + 8;
+
+ tmp = b5 ^ b4;
+ b5 = (tmp >> 19) | (tmp << (64 - 19));
+ b4 -= b5 + k3;
+ b5 -= k4 + t2;
+
+ tmp = b3 ^ b2;
+ b3 = (tmp >> 36) | (tmp << (64 - 36));
+ b2 -= b3 + k1;
+ b3 -= k2;
+
+ tmp = b1 ^ b0;
+ b1 = (tmp >> 46) | (tmp << (64 - 46));
+ b0 -= b1 + k8;
+ b1 -= k0;
+
+ tmp = b3 ^ b4;
+ b3 = (tmp >> 22) | (tmp << (64 - 22));
+ b4 -= b3;
+
+ tmp = b5 ^ b2;
+ b5 = (tmp >> 56) | (tmp << (64 - 56));
+ b2 -= b5;
+
+ tmp = b7 ^ b0;
+ b7 = (tmp >> 35) | (tmp << (64 - 35));
+ b0 -= b7;
+
+ tmp = b1 ^ b6;
+ b1 = (tmp >> 8) | (tmp << (64 - 8));
+ b6 -= b1;
+
+ tmp = b7 ^ b2;
+ b7 = (tmp >> 43) | (tmp << (64 - 43));
+ b2 -= b7;
+
+ tmp = b5 ^ b0;
+ b5 = (tmp >> 39) | (tmp << (64 - 39));
+ b0 -= b5;
+
+ tmp = b3 ^ b6;
+ b3 = (tmp >> 29) | (tmp << (64 - 29));
+ b6 -= b3;
+
+ tmp = b1 ^ b4;
+ b1 = (tmp >> 25) | (tmp << (64 - 25));
+ b4 -= b1;
+
+ tmp = b3 ^ b0;
+ b3 = (tmp >> 17) | (tmp << (64 - 17));
+ b0 -= b3;
+
+ tmp = b5 ^ b6;
+ b5 = (tmp >> 10) | (tmp << (64 - 10));
+ b6 -= b5;
+
+ tmp = b7 ^ b4;
+ b7 = (tmp >> 50) | (tmp << (64 - 50));
+ b4 -= b7;
+
+ tmp = b1 ^ b2;
+ b1 = (tmp >> 13) | (tmp << (64 - 13));
+ b2 -= b1;
+
+ tmp = b7 ^ b6;
+ b7 = (tmp >> 24) | (tmp << (64 - 24));
+ b6 -= b7 + k4 + t2;
+ b7 -= k5 + 7;
+
+ tmp = b5 ^ b4;
+ b5 = (tmp >> 34) | (tmp << (64 - 34));
+ b4 -= b5 + k2;
+ b5 -= k3 + t1;
+
+ tmp = b3 ^ b2;
+ b3 = (tmp >> 30) | (tmp << (64 - 30));
+ b2 -= b3 + k0;
+ b3 -= k1;
+
+ tmp = b1 ^ b0;
+ b1 = (tmp >> 39) | (tmp << (64 - 39));
+ b0 -= b1 + k7;
+ b1 -= k8;
+
+ tmp = b3 ^ b4;
+ b3 = (tmp >> 56) | (tmp << (64 - 56));
+ b4 -= b3;
+
+ tmp = b5 ^ b2;
+ b5 = (tmp >> 54) | (tmp << (64 - 54));
+ b2 -= b5;
+
+ tmp = b7 ^ b0;
+ b7 = (tmp >> 9) | (tmp << (64 - 9));
+ b0 -= b7;
+
+ tmp = b1 ^ b6;
+ b1 = (tmp >> 44) | (tmp << (64 - 44));
+ b6 -= b1;
+
+ tmp = b7 ^ b2;
+ b7 = (tmp >> 39) | (tmp << (64 - 39));
+ b2 -= b7;
+
+ tmp = b5 ^ b0;
+ b5 = (tmp >> 36) | (tmp << (64 - 36));
+ b0 -= b5;
+
+ tmp = b3 ^ b6;
+ b3 = (tmp >> 49) | (tmp << (64 - 49));
+ b6 -= b3;
+
+ tmp = b1 ^ b4;
+ b1 = (tmp >> 17) | (tmp << (64 - 17));
+ b4 -= b1;
+
+ tmp = b3 ^ b0;
+ b3 = (tmp >> 42) | (tmp << (64 - 42));
+ b0 -= b3;
+
+ tmp = b5 ^ b6;
+ b5 = (tmp >> 14) | (tmp << (64 - 14));
+ b6 -= b5;
+
+ tmp = b7 ^ b4;
+ b7 = (tmp >> 27) | (tmp << (64 - 27));
+ b4 -= b7;
+
+ tmp = b1 ^ b2;
+ b1 = (tmp >> 33) | (tmp << (64 - 33));
+ b2 -= b1;
+
+ tmp = b7 ^ b6;
+ b7 = (tmp >> 37) | (tmp << (64 - 37));
+ b6 -= b7 + k3 + t1;
+ b7 -= k4 + 6;
+
+ tmp = b5 ^ b4;
+ b5 = (tmp >> 19) | (tmp << (64 - 19));
+ b4 -= b5 + k1;
+ b5 -= k2 + t0;
+
+ tmp = b3 ^ b2;
+ b3 = (tmp >> 36) | (tmp << (64 - 36));
+ b2 -= b3 + k8;
+ b3 -= k0;
+
+ tmp = b1 ^ b0;
+ b1 = (tmp >> 46) | (tmp << (64 - 46));
+ b0 -= b1 + k6;
+ b1 -= k7;
+
+ tmp = b3 ^ b4;
+ b3 = (tmp >> 22) | (tmp << (64 - 22));
+ b4 -= b3;
+
+ tmp = b5 ^ b2;
+ b5 = (tmp >> 56) | (tmp << (64 - 56));
+ b2 -= b5;
+
+ tmp = b7 ^ b0;
+ b7 = (tmp >> 35) | (tmp << (64 - 35));
+ b0 -= b7;
+
+ tmp = b1 ^ b6;
+ b1 = (tmp >> 8) | (tmp << (64 - 8));
+ b6 -= b1;
+
+ tmp = b7 ^ b2;
+ b7 = (tmp >> 43) | (tmp << (64 - 43));
+ b2 -= b7;
+
+ tmp = b5 ^ b0;
+ b5 = (tmp >> 39) | (tmp << (64 - 39));
+ b0 -= b5;
+
+ tmp = b3 ^ b6;
+ b3 = (tmp >> 29) | (tmp << (64 - 29));
+ b6 -= b3;
+
+ tmp = b1 ^ b4;
+ b1 = (tmp >> 25) | (tmp << (64 - 25));
+ b4 -= b1;
+
+ tmp = b3 ^ b0;
+ b3 = (tmp >> 17) | (tmp << (64 - 17));
+ b0 -= b3;
+
+ tmp = b5 ^ b6;
+ b5 = (tmp >> 10) | (tmp << (64 - 10));
+ b6 -= b5;
+
+ tmp = b7 ^ b4;
+ b7 = (tmp >> 50) | (tmp << (64 - 50));
+ b4 -= b7;
+
+ tmp = b1 ^ b2;
+ b1 = (tmp >> 13) | (tmp << (64 - 13));
+ b2 -= b1;
+
+ tmp = b7 ^ b6;
+ b7 = (tmp >> 24) | (tmp << (64 - 24));
+ b6 -= b7 + k2 + t0;
+ b7 -= k3 + 5;
+
+ tmp = b5 ^ b4;
+ b5 = (tmp >> 34) | (tmp << (64 - 34));
+ b4 -= b5 + k0;
+ b5 -= k1 + t2;
+
+ tmp = b3 ^ b2;
+ b3 = (tmp >> 30) | (tmp << (64 - 30));
+ b2 -= b3 + k7;
+ b3 -= k8;
+
+ tmp = b1 ^ b0;
+ b1 = (tmp >> 39) | (tmp << (64 - 39));
+ b0 -= b1 + k5;
+ b1 -= k6;
+
+ tmp = b3 ^ b4;
+ b3 = (tmp >> 56) | (tmp << (64 - 56));
+ b4 -= b3;
+
+ tmp = b5 ^ b2;
+ b5 = (tmp >> 54) | (tmp << (64 - 54));
+ b2 -= b5;
+
+ tmp = b7 ^ b0;
+ b7 = (tmp >> 9) | (tmp << (64 - 9));
+ b0 -= b7;
+
+ tmp = b1 ^ b6;
+ b1 = (tmp >> 44) | (tmp << (64 - 44));
+ b6 -= b1;
+
+ tmp = b7 ^ b2;
+ b7 = (tmp >> 39) | (tmp << (64 - 39));
+ b2 -= b7;
+
+ tmp = b5 ^ b0;
+ b5 = (tmp >> 36) | (tmp << (64 - 36));
+ b0 -= b5;
+
+ tmp = b3 ^ b6;
+ b3 = (tmp >> 49) | (tmp << (64 - 49));
+ b6 -= b3;
+
+ tmp = b1 ^ b4;
+ b1 = (tmp >> 17) | (tmp << (64 - 17));
+ b4 -= b1;
+
+ tmp = b3 ^ b0;
+ b3 = (tmp >> 42) | (tmp << (64 - 42));
+ b0 -= b3;
+
+ tmp = b5 ^ b6;
+ b5 = (tmp >> 14) | (tmp << (64 - 14));
+ b6 -= b5;
+
+ tmp = b7 ^ b4;
+ b7 = (tmp >> 27) | (tmp << (64 - 27));
+ b4 -= b7;
+
+ tmp = b1 ^ b2;
+ b1 = (tmp >> 33) | (tmp << (64 - 33));
+ b2 -= b1;
+
+ tmp = b7 ^ b6;
+ b7 = (tmp >> 37) | (tmp << (64 - 37));
+ b6 -= b7 + k1 + t2;
+ b7 -= k2 + 4;
+
+ tmp = b5 ^ b4;
+ b5 = (tmp >> 19) | (tmp << (64 - 19));
+ b4 -= b5 + k8;
+ b5 -= k0 + t1;
+
+ tmp = b3 ^ b2;
+ b3 = (tmp >> 36) | (tmp << (64 - 36));
+ b2 -= b3 + k6;
+ b3 -= k7;
+
+ tmp = b1 ^ b0;
+ b1 = (tmp >> 46) | (tmp << (64 - 46));
+ b0 -= b1 + k4;
+ b1 -= k5;
+
+ tmp = b3 ^ b4;
+ b3 = (tmp >> 22) | (tmp << (64 - 22));
+ b4 -= b3;
+
+ tmp = b5 ^ b2;
+ b5 = (tmp >> 56) | (tmp << (64 - 56));
+ b2 -= b5;
+
+ tmp = b7 ^ b0;
+ b7 = (tmp >> 35) | (tmp << (64 - 35));
+ b0 -= b7;
+
+ tmp = b1 ^ b6;
+ b1 = (tmp >> 8) | (tmp << (64 - 8));
+ b6 -= b1;
+
+ tmp = b7 ^ b2;
+ b7 = (tmp >> 43) | (tmp << (64 - 43));
+ b2 -= b7;
+
+ tmp = b5 ^ b0;
+ b5 = (tmp >> 39) | (tmp << (64 - 39));
+ b0 -= b5;
+
+ tmp = b3 ^ b6;
+ b3 = (tmp >> 29) | (tmp << (64 - 29));
+ b6 -= b3;
+
+ tmp = b1 ^ b4;
+ b1 = (tmp >> 25) | (tmp << (64 - 25));
+ b4 -= b1;
+
+ tmp = b3 ^ b0;
+ b3 = (tmp >> 17) | (tmp << (64 - 17));
+ b0 -= b3;
+
+ tmp = b5 ^ b6;
+ b5 = (tmp >> 10) | (tmp << (64 - 10));
+ b6 -= b5;
+
+ tmp = b7 ^ b4;
+ b7 = (tmp >> 50) | (tmp << (64 - 50));
+ b4 -= b7;
+
+ tmp = b1 ^ b2;
+ b1 = (tmp >> 13) | (tmp << (64 - 13));
+ b2 -= b1;
+
+ tmp = b7 ^ b6;
+ b7 = (tmp >> 24) | (tmp << (64 - 24));
+ b6 -= b7 + k0 + t1;
+ b7 -= k1 + 3;
+
+ tmp = b5 ^ b4;
+ b5 = (tmp >> 34) | (tmp << (64 - 34));
+ b4 -= b5 + k7;
+ b5 -= k8 + t0;
+
+ tmp = b3 ^ b2;
+ b3 = (tmp >> 30) | (tmp << (64 - 30));
+ b2 -= b3 + k5;
+ b3 -= k6;
+
+ tmp = b1 ^ b0;
+ b1 = (tmp >> 39) | (tmp << (64 - 39));
+ b0 -= b1 + k3;
+ b1 -= k4;
+
+ tmp = b3 ^ b4;
+ b3 = (tmp >> 56) | (tmp << (64 - 56));
+ b4 -= b3;
+
+ tmp = b5 ^ b2;
+ b5 = (tmp >> 54) | (tmp << (64 - 54));
+ b2 -= b5;
+
+ tmp = b7 ^ b0;
+ b7 = (tmp >> 9) | (tmp << (64 - 9));
+ b0 -= b7;
+
+ tmp = b1 ^ b6;
+ b1 = (tmp >> 44) | (tmp << (64 - 44));
+ b6 -= b1;
+
+ tmp = b7 ^ b2;
+ b7 = (tmp >> 39) | (tmp << (64 - 39));
+ b2 -= b7;
+
+ tmp = b5 ^ b0;
+ b5 = (tmp >> 36) | (tmp << (64 - 36));
+ b0 -= b5;
+
+ tmp = b3 ^ b6;
+ b3 = (tmp >> 49) | (tmp << (64 - 49));
+ b6 -= b3;
+
+ tmp = b1 ^ b4;
+ b1 = (tmp >> 17) | (tmp << (64 - 17));
+ b4 -= b1;
+
+ tmp = b3 ^ b0;
+ b3 = (tmp >> 42) | (tmp << (64 - 42));
+ b0 -= b3;
+
+ tmp = b5 ^ b6;
+ b5 = (tmp >> 14) | (tmp << (64 - 14));
+ b6 -= b5;
+
+ tmp = b7 ^ b4;
+ b7 = (tmp >> 27) | (tmp << (64 - 27));
+ b4 -= b7;
+
+ tmp = b1 ^ b2;
+ b1 = (tmp >> 33) | (tmp << (64 - 33));
+ b2 -= b1;
+
+ tmp = b7 ^ b6;
+ b7 = (tmp >> 37) | (tmp << (64 - 37));
+ b6 -= b7 + k8 + t0;
+ b7 -= k0 + 2;
+
+ tmp = b5 ^ b4;
+ b5 = (tmp >> 19) | (tmp << (64 - 19));
+ b4 -= b5 + k6;
+ b5 -= k7 + t2;
+
+ tmp = b3 ^ b2;
+ b3 = (tmp >> 36) | (tmp << (64 - 36));
+ b2 -= b3 + k4;
+ b3 -= k5;
+
+ tmp = b1 ^ b0;
+ b1 = (tmp >> 46) | (tmp << (64 - 46));
+ b0 -= b1 + k2;
+ b1 -= k3;
+
+ tmp = b3 ^ b4;
+ b3 = (tmp >> 22) | (tmp << (64 - 22));
+ b4 -= b3;
+
+ tmp = b5 ^ b2;
+ b5 = (tmp >> 56) | (tmp << (64 - 56));
+ b2 -= b5;
+
+ tmp = b7 ^ b0;
+ b7 = (tmp >> 35) | (tmp << (64 - 35));
+ b0 -= b7;
+
+ tmp = b1 ^ b6;
+ b1 = (tmp >> 8) | (tmp << (64 - 8));
+ b6 -= b1;
+
+ tmp = b7 ^ b2;
+ b7 = (tmp >> 43) | (tmp << (64 - 43));
+ b2 -= b7;
+
+ tmp = b5 ^ b0;
+ b5 = (tmp >> 39) | (tmp << (64 - 39));
+ b0 -= b5;
+
+ tmp = b3 ^ b6;
+ b3 = (tmp >> 29) | (tmp << (64 - 29));
+ b6 -= b3;
+
+ tmp = b1 ^ b4;
+ b1 = (tmp >> 25) | (tmp << (64 - 25));
+ b4 -= b1;
+
+ tmp = b3 ^ b0;
+ b3 = (tmp >> 17) | (tmp << (64 - 17));
+ b0 -= b3;
+
+ tmp = b5 ^ b6;
+ b5 = (tmp >> 10) | (tmp << (64 - 10));
+ b6 -= b5;
+
+ tmp = b7 ^ b4;
+ b7 = (tmp >> 50) | (tmp << (64 - 50));
+ b4 -= b7;
+
+ tmp = b1 ^ b2;
+ b1 = (tmp >> 13) | (tmp << (64 - 13));
+ b2 -= b1;
+
+ tmp = b7 ^ b6;
+ b7 = (tmp >> 24) | (tmp << (64 - 24));
+ b6 -= b7 + k7 + t2;
+ b7 -= k8 + 1;
+
+ tmp = b5 ^ b4;
+ b5 = (tmp >> 34) | (tmp << (64 - 34));
+ b4 -= b5 + k5;
+ b5 -= k6 + t1;
+
+ tmp = b3 ^ b2;
+ b3 = (tmp >> 30) | (tmp << (64 - 30));
+ b2 -= b3 + k3;
+ b3 -= k4;
+
+ tmp = b1 ^ b0;
+ b1 = (tmp >> 39) | (tmp << (64 - 39));
+ b0 -= b1 + k1;
+ b1 -= k2;
+
+ tmp = b3 ^ b4;
+ b3 = (tmp >> 56) | (tmp << (64 - 56));
+ b4 -= b3;
+
+ tmp = b5 ^ b2;
+ b5 = (tmp >> 54) | (tmp << (64 - 54));
+ b2 -= b5;
+
+ tmp = b7 ^ b0;
+ b7 = (tmp >> 9) | (tmp << (64 - 9));
+ b0 -= b7;
+
+ tmp = b1 ^ b6;
+ b1 = (tmp >> 44) | (tmp << (64 - 44));
+ b6 -= b1;
+
+ tmp = b7 ^ b2;
+ b7 = (tmp >> 39) | (tmp << (64 - 39));
+ b2 -= b7;
+
+ tmp = b5 ^ b0;
+ b5 = (tmp >> 36) | (tmp << (64 - 36));
+ b0 -= b5;
+
+ tmp = b3 ^ b6;
+ b3 = (tmp >> 49) | (tmp << (64 - 49));
+ b6 -= b3;
+
+ tmp = b1 ^ b4;
+ b1 = (tmp >> 17) | (tmp << (64 - 17));
+ b4 -= b1;
+
+ tmp = b3 ^ b0;
+ b3 = (tmp >> 42) | (tmp << (64 - 42));
+ b0 -= b3;
+
+ tmp = b5 ^ b6;
+ b5 = (tmp >> 14) | (tmp << (64 - 14));
+ b6 -= b5;
+
+ tmp = b7 ^ b4;
+ b7 = (tmp >> 27) | (tmp << (64 - 27));
+ b4 -= b7;
+
+ tmp = b1 ^ b2;
+ b1 = (tmp >> 33) | (tmp << (64 - 33));
+ b2 -= b1;
+
+ tmp = b7 ^ b6;
+ b7 = (tmp >> 37) | (tmp << (64 - 37));
+ b6 -= b7 + k6 + t1;
+ b7 -= k7;
+
+ tmp = b5 ^ b4;
+ b5 = (tmp >> 19) | (tmp << (64 - 19));
+ b4 -= b5 + k4;
+ b5 -= k5 + t0;
+
+ tmp = b3 ^ b2;
+ b3 = (tmp >> 36) | (tmp << (64 - 36));
+ b2 -= b3 + k2;
+ b3 -= k3;
+
+ tmp = b1 ^ b0;
+ b1 = (tmp >> 46) | (tmp << (64 - 46));
+ b0 -= b1 + k0;
+ b1 -= k1;
+
+ output[0] = b0;
+ output[1] = b1;
+ output[2] = b2;
+ output[3] = b3;
+
+ output[7] = b7;
+ output[6] = b6;
+ output[5] = b5;
+ output[4] = b4;
+}
+
+void threefish_encrypt_1024(struct threefish_key *key_ctx, u64 *input,
+ u64 *output)
+{
+ u64 b0 = input[0], b1 = input[1],
+ b2 = input[2], b3 = input[3],
+ b4 = input[4], b5 = input[5],
+ b6 = input[6], b7 = input[7],
+ b8 = input[8], b9 = input[9],
+ b10 = input[10], b11 = input[11],
+ b12 = input[12], b13 = input[13],
+ b14 = input[14], b15 = input[15];
+ u64 k0 = key_ctx->key[0], k1 = key_ctx->key[1],
+ k2 = key_ctx->key[2], k3 = key_ctx->key[3],
+ k4 = key_ctx->key[4], k5 = key_ctx->key[5],
+ k6 = key_ctx->key[6], k7 = key_ctx->key[7],
+ k8 = key_ctx->key[8], k9 = key_ctx->key[9],
+ k10 = key_ctx->key[10], k11 = key_ctx->key[11],
+ k12 = key_ctx->key[12], k13 = key_ctx->key[13],
+ k14 = key_ctx->key[14], k15 = key_ctx->key[15],
+ k16 = key_ctx->key[16];
+ u64 t0 = key_ctx->tweak[0], t1 = key_ctx->tweak[1],
+ t2 = key_ctx->tweak[2];
+
+ b1 += k1;
+ b0 += b1 + k0;
+ b1 = ((b1 << 24) | (b1 >> (64 - 24))) ^ b0;
+
+ b3 += k3;
+ b2 += b3 + k2;
+ b3 = ((b3 << 13) | (b3 >> (64 - 13))) ^ b2;
+
+ b5 += k5;
+ b4 += b5 + k4;
+ b5 = ((b5 << 8) | (b5 >> (64 - 8))) ^ b4;
+
+ b7 += k7;
+ b6 += b7 + k6;
+ b7 = ((b7 << 47) | (b7 >> (64 - 47))) ^ b6;
+
+ b9 += k9;
+ b8 += b9 + k8;
+ b9 = ((b9 << 8) | (b9 >> (64 - 8))) ^ b8;
+
+ b11 += k11;
+ b10 += b11 + k10;
+ b11 = ((b11 << 17) | (b11 >> (64 - 17))) ^ b10;
+
+ b13 += k13 + t0;
+ b12 += b13 + k12;
+ b13 = ((b13 << 22) | (b13 >> (64 - 22))) ^ b12;
+
+ b15 += k15;
+ b14 += b15 + k14 + t1;
+ b15 = ((b15 << 37) | (b15 >> (64 - 37))) ^ b14;
+
+ b0 += b9;
+ b9 = ((b9 << 38) | (b9 >> (64 - 38))) ^ b0;
+
+ b2 += b13;
+ b13 = ((b13 << 19) | (b13 >> (64 - 19))) ^ b2;
+
+ b6 += b11;
+ b11 = ((b11 << 10) | (b11 >> (64 - 10))) ^ b6;
+
+ b4 += b15;
+ b15 = ((b15 << 55) | (b15 >> (64 - 55))) ^ b4;
+
+ b10 += b7;
+ b7 = ((b7 << 49) | (b7 >> (64 - 49))) ^ b10;
+
+ b12 += b3;
+ b3 = ((b3 << 18) | (b3 >> (64 - 18))) ^ b12;
+
+ b14 += b5;
+ b5 = ((b5 << 23) | (b5 >> (64 - 23))) ^ b14;
+
+ b8 += b1;
+ b1 = ((b1 << 52) | (b1 >> (64 - 52))) ^ b8;
+
+ b0 += b7;
+ b7 = ((b7 << 33) | (b7 >> (64 - 33))) ^ b0;
+
+ b2 += b5;
+ b5 = ((b5 << 4) | (b5 >> (64 - 4))) ^ b2;
+
+ b4 += b3;
+ b3 = ((b3 << 51) | (b3 >> (64 - 51))) ^ b4;
+
+ b6 += b1;
+ b1 = ((b1 << 13) | (b1 >> (64 - 13))) ^ b6;
+
+ b12 += b15;
+ b15 = ((b15 << 34) | (b15 >> (64 - 34))) ^ b12;
+
+ b14 += b13;
+ b13 = ((b13 << 41) | (b13 >> (64 - 41))) ^ b14;
+
+ b8 += b11;
+ b11 = ((b11 << 59) | (b11 >> (64 - 59))) ^ b8;
+
+ b10 += b9;
+ b9 = ((b9 << 17) | (b9 >> (64 - 17))) ^ b10;
+
+ b0 += b15;
+ b15 = ((b15 << 5) | (b15 >> (64 - 5))) ^ b0;
+
+ b2 += b11;
+ b11 = ((b11 << 20) | (b11 >> (64 - 20))) ^ b2;
+
+ b6 += b13;
+ b13 = ((b13 << 48) | (b13 >> (64 - 48))) ^ b6;
+
+ b4 += b9;
+ b9 = ((b9 << 41) | (b9 >> (64 - 41))) ^ b4;
+
+ b14 += b1;
+ b1 = ((b1 << 47) | (b1 >> (64 - 47))) ^ b14;
+
+ b8 += b5;
+ b5 = ((b5 << 28) | (b5 >> (64 - 28))) ^ b8;
+
+ b10 += b3;
+ b3 = ((b3 << 16) | (b3 >> (64 - 16))) ^ b10;
+
+ b12 += b7;
+ b7 = ((b7 << 25) | (b7 >> (64 - 25))) ^ b12;
+
+ b1 += k2;
+ b0 += b1 + k1;
+ b1 = ((b1 << 41) | (b1 >> (64 - 41))) ^ b0;
+
+ b3 += k4;
+ b2 += b3 + k3;
+ b3 = ((b3 << 9) | (b3 >> (64 - 9))) ^ b2;
+
+ b5 += k6;
+ b4 += b5 + k5;
+ b5 = ((b5 << 37) | (b5 >> (64 - 37))) ^ b4;
+
+ b7 += k8;
+ b6 += b7 + k7;
+ b7 = ((b7 << 31) | (b7 >> (64 - 31))) ^ b6;
+
+ b9 += k10;
+ b8 += b9 + k9;
+ b9 = ((b9 << 12) | (b9 >> (64 - 12))) ^ b8;
+
+ b11 += k12;
+ b10 += b11 + k11;
+ b11 = ((b11 << 47) | (b11 >> (64 - 47))) ^ b10;
+
+ b13 += k14 + t1;
+ b12 += b13 + k13;
+ b13 = ((b13 << 44) | (b13 >> (64 - 44))) ^ b12;
+
+ b15 += k16 + 1;
+ b14 += b15 + k15 + t2;
+ b15 = ((b15 << 30) | (b15 >> (64 - 30))) ^ b14;
+
+ b0 += b9;
+ b9 = ((b9 << 16) | (b9 >> (64 - 16))) ^ b0;
+
+ b2 += b13;
+ b13 = ((b13 << 34) | (b13 >> (64 - 34))) ^ b2;
+
+ b6 += b11;
+ b11 = ((b11 << 56) | (b11 >> (64 - 56))) ^ b6;
+
+ b4 += b15;
+ b15 = ((b15 << 51) | (b15 >> (64 - 51))) ^ b4;
+
+ b10 += b7;
+ b7 = ((b7 << 4) | (b7 >> (64 - 4))) ^ b10;
+
+ b12 += b3;
+ b3 = ((b3 << 53) | (b3 >> (64 - 53))) ^ b12;
+
+ b14 += b5;
+ b5 = ((b5 << 42) | (b5 >> (64 - 42))) ^ b14;
+
+ b8 += b1;
+ b1 = ((b1 << 41) | (b1 >> (64 - 41))) ^ b8;
+
+ b0 += b7;
+ b7 = ((b7 << 31) | (b7 >> (64 - 31))) ^ b0;
+
+ b2 += b5;
+ b5 = ((b5 << 44) | (b5 >> (64 - 44))) ^ b2;
+
+ b4 += b3;
+ b3 = ((b3 << 47) | (b3 >> (64 - 47))) ^ b4;
+
+ b6 += b1;
+ b1 = ((b1 << 46) | (b1 >> (64 - 46))) ^ b6;
+
+ b12 += b15;
+ b15 = ((b15 << 19) | (b15 >> (64 - 19))) ^ b12;
+
+ b14 += b13;
+ b13 = ((b13 << 42) | (b13 >> (64 - 42))) ^ b14;
+
+ b8 += b11;
+ b11 = ((b11 << 44) | (b11 >> (64 - 44))) ^ b8;
+
+ b10 += b9;
+ b9 = ((b9 << 25) | (b9 >> (64 - 25))) ^ b10;
+
+ b0 += b15;
+ b15 = ((b15 << 9) | (b15 >> (64 - 9))) ^ b0;
+
+ b2 += b11;
+ b11 = ((b11 << 48) | (b11 >> (64 - 48))) ^ b2;
+
+ b6 += b13;
+ b13 = ((b13 << 35) | (b13 >> (64 - 35))) ^ b6;
+
+ b4 += b9;
+ b9 = ((b9 << 52) | (b9 >> (64 - 52))) ^ b4;
+
+ b14 += b1;
+ b1 = ((b1 << 23) | (b1 >> (64 - 23))) ^ b14;
+
+ b8 += b5;
+ b5 = ((b5 << 31) | (b5 >> (64 - 31))) ^ b8;
+
+ b10 += b3;
+ b3 = ((b3 << 37) | (b3 >> (64 - 37))) ^ b10;
+
+ b12 += b7;
+ b7 = ((b7 << 20) | (b7 >> (64 - 20))) ^ b12;
+
+ b1 += k3;
+ b0 += b1 + k2;
+ b1 = ((b1 << 24) | (b1 >> (64 - 24))) ^ b0;
+
+ b3 += k5;
+ b2 += b3 + k4;
+ b3 = ((b3 << 13) | (b3 >> (64 - 13))) ^ b2;
+
+ b5 += k7;
+ b4 += b5 + k6;
+ b5 = ((b5 << 8) | (b5 >> (64 - 8))) ^ b4;
+
+ b7 += k9;
+ b6 += b7 + k8;
+ b7 = ((b7 << 47) | (b7 >> (64 - 47))) ^ b6;
+
+ b9 += k11;
+ b8 += b9 + k10;
+ b9 = ((b9 << 8) | (b9 >> (64 - 8))) ^ b8;
+
+ b11 += k13;
+ b10 += b11 + k12;
+ b11 = ((b11 << 17) | (b11 >> (64 - 17))) ^ b10;
+
+ b13 += k15 + t2;
+ b12 += b13 + k14;
+ b13 = ((b13 << 22) | (b13 >> (64 - 22))) ^ b12;
+
+ b15 += k0 + 2;
+ b14 += b15 + k16 + t0;
+ b15 = ((b15 << 37) | (b15 >> (64 - 37))) ^ b14;
+
+ b0 += b9;
+ b9 = ((b9 << 38) | (b9 >> (64 - 38))) ^ b0;
+
+ b2 += b13;
+ b13 = ((b13 << 19) | (b13 >> (64 - 19))) ^ b2;
+
+ b6 += b11;
+ b11 = ((b11 << 10) | (b11 >> (64 - 10))) ^ b6;
+
+ b4 += b15;
+ b15 = ((b15 << 55) | (b15 >> (64 - 55))) ^ b4;
+
+ b10 += b7;
+ b7 = ((b7 << 49) | (b7 >> (64 - 49))) ^ b10;
+
+ b12 += b3;
+ b3 = ((b3 << 18) | (b3 >> (64 - 18))) ^ b12;
+
+ b14 += b5;
+ b5 = ((b5 << 23) | (b5 >> (64 - 23))) ^ b14;
+
+ b8 += b1;
+ b1 = ((b1 << 52) | (b1 >> (64 - 52))) ^ b8;
+
+ b0 += b7;
+ b7 = ((b7 << 33) | (b7 >> (64 - 33))) ^ b0;
+
+ b2 += b5;
+ b5 = ((b5 << 4) | (b5 >> (64 - 4))) ^ b2;
+
+ b4 += b3;
+ b3 = ((b3 << 51) | (b3 >> (64 - 51))) ^ b4;
+
+ b6 += b1;
+ b1 = ((b1 << 13) | (b1 >> (64 - 13))) ^ b6;
+
+ b12 += b15;
+ b15 = ((b15 << 34) | (b15 >> (64 - 34))) ^ b12;
+
+ b14 += b13;
+ b13 = ((b13 << 41) | (b13 >> (64 - 41))) ^ b14;
+
+ b8 += b11;
+ b11 = ((b11 << 59) | (b11 >> (64 - 59))) ^ b8;
+
+ b10 += b9;
+ b9 = ((b9 << 17) | (b9 >> (64 - 17))) ^ b10;
+
+ b0 += b15;
+ b15 = ((b15 << 5) | (b15 >> (64 - 5))) ^ b0;
+
+ b2 += b11;
+ b11 = ((b11 << 20) | (b11 >> (64 - 20))) ^ b2;
+
+ b6 += b13;
+ b13 = ((b13 << 48) | (b13 >> (64 - 48))) ^ b6;
+
+ b4 += b9;
+ b9 = ((b9 << 41) | (b9 >> (64 - 41))) ^ b4;
+
+ b14 += b1;
+ b1 = ((b1 << 47) | (b1 >> (64 - 47))) ^ b14;
+
+ b8 += b5;
+ b5 = ((b5 << 28) | (b5 >> (64 - 28))) ^ b8;
+
+ b10 += b3;
+ b3 = ((b3 << 16) | (b3 >> (64 - 16))) ^ b10;
+
+ b12 += b7;
+ b7 = ((b7 << 25) | (b7 >> (64 - 25))) ^ b12;
+
+ b1 += k4;
+ b0 += b1 + k3;
+ b1 = ((b1 << 41) | (b1 >> (64 - 41))) ^ b0;
+
+ b3 += k6;
+ b2 += b3 + k5;
+ b3 = ((b3 << 9) | (b3 >> (64 - 9))) ^ b2;
+
+ b5 += k8;
+ b4 += b5 + k7;
+ b5 = ((b5 << 37) | (b5 >> (64 - 37))) ^ b4;
+
+ b7 += k10;
+ b6 += b7 + k9;
+ b7 = ((b7 << 31) | (b7 >> (64 - 31))) ^ b6;
+
+ b9 += k12;
+ b8 += b9 + k11;
+ b9 = ((b9 << 12) | (b9 >> (64 - 12))) ^ b8;
+
+ b11 += k14;
+ b10 += b11 + k13;
+ b11 = ((b11 << 47) | (b11 >> (64 - 47))) ^ b10;
+
+ b13 += k16 + t0;
+ b12 += b13 + k15;
+ b13 = ((b13 << 44) | (b13 >> (64 - 44))) ^ b12;
+
+ b15 += k1 + 3;
+ b14 += b15 + k0 + t1;
+ b15 = ((b15 << 30) | (b15 >> (64 - 30))) ^ b14;
+
+ b0 += b9;
+ b9 = ((b9 << 16) | (b9 >> (64 - 16))) ^ b0;
+
+ b2 += b13;
+ b13 = ((b13 << 34) | (b13 >> (64 - 34))) ^ b2;
+
+ b6 += b11;
+ b11 = ((b11 << 56) | (b11 >> (64 - 56))) ^ b6;
+
+ b4 += b15;
+ b15 = ((b15 << 51) | (b15 >> (64 - 51))) ^ b4;
+
+ b10 += b7;
+ b7 = ((b7 << 4) | (b7 >> (64 - 4))) ^ b10;
+
+ b12 += b3;
+ b3 = ((b3 << 53) | (b3 >> (64 - 53))) ^ b12;
+
+ b14 += b5;
+ b5 = ((b5 << 42) | (b5 >> (64 - 42))) ^ b14;
+
+ b8 += b1;
+ b1 = ((b1 << 41) | (b1 >> (64 - 41))) ^ b8;
+
+ b0 += b7;
+ b7 = ((b7 << 31) | (b7 >> (64 - 31))) ^ b0;
+
+ b2 += b5;
+ b5 = ((b5 << 44) | (b5 >> (64 - 44))) ^ b2;
+
+ b4 += b3;
+ b3 = ((b3 << 47) | (b3 >> (64 - 47))) ^ b4;
+
+ b6 += b1;
+ b1 = ((b1 << 46) | (b1 >> (64 - 46))) ^ b6;
+
+ b12 += b15;
+ b15 = ((b15 << 19) | (b15 >> (64 - 19))) ^ b12;
+
+ b14 += b13;
+ b13 = ((b13 << 42) | (b13 >> (64 - 42))) ^ b14;
+
+ b8 += b11;
+ b11 = ((b11 << 44) | (b11 >> (64 - 44))) ^ b8;
+
+ b10 += b9;
+ b9 = ((b9 << 25) | (b9 >> (64 - 25))) ^ b10;
+
+ b0 += b15;
+ b15 = ((b15 << 9) | (b15 >> (64 - 9))) ^ b0;
+
+ b2 += b11;
+ b11 = ((b11 << 48) | (b11 >> (64 - 48))) ^ b2;
+
+ b6 += b13;
+ b13 = ((b13 << 35) | (b13 >> (64 - 35))) ^ b6;
+
+ b4 += b9;
+ b9 = ((b9 << 52) | (b9 >> (64 - 52))) ^ b4;
+
+ b14 += b1;
+ b1 = ((b1 << 23) | (b1 >> (64 - 23))) ^ b14;
+
+ b8 += b5;
+ b5 = ((b5 << 31) | (b5 >> (64 - 31))) ^ b8;
+
+ b10 += b3;
+ b3 = ((b3 << 37) | (b3 >> (64 - 37))) ^ b10;
+
+ b12 += b7;
+ b7 = ((b7 << 20) | (b7 >> (64 - 20))) ^ b12;
+
+ b1 += k5;
+ b0 += b1 + k4;
+ b1 = ((b1 << 24) | (b1 >> (64 - 24))) ^ b0;
+
+ b3 += k7;
+ b2 += b3 + k6;
+ b3 = ((b3 << 13) | (b3 >> (64 - 13))) ^ b2;
+
+ b5 += k9;
+ b4 += b5 + k8;
+ b5 = ((b5 << 8) | (b5 >> (64 - 8))) ^ b4;
+
+ b7 += k11;
+ b6 += b7 + k10;
+ b7 = ((b7 << 47) | (b7 >> (64 - 47))) ^ b6;
+
+ b9 += k13;
+ b8 += b9 + k12;
+ b9 = ((b9 << 8) | (b9 >> (64 - 8))) ^ b8;
+
+ b11 += k15;
+ b10 += b11 + k14;
+ b11 = ((b11 << 17) | (b11 >> (64 - 17))) ^ b10;
+
+ b13 += k0 + t1;
+ b12 += b13 + k16;
+ b13 = ((b13 << 22) | (b13 >> (64 - 22))) ^ b12;
+
+ b15 += k2 + 4;
+ b14 += b15 + k1 + t2;
+ b15 = ((b15 << 37) | (b15 >> (64 - 37))) ^ b14;
+
+ b0 += b9;
+ b9 = ((b9 << 38) | (b9 >> (64 - 38))) ^ b0;
+
+ b2 += b13;
+ b13 = ((b13 << 19) | (b13 >> (64 - 19))) ^ b2;
+
+ b6 += b11;
+ b11 = ((b11 << 10) | (b11 >> (64 - 10))) ^ b6;
+
+ b4 += b15;
+ b15 = ((b15 << 55) | (b15 >> (64 - 55))) ^ b4;
+
+ b10 += b7;
+ b7 = ((b7 << 49) | (b7 >> (64 - 49))) ^ b10;
+
+ b12 += b3;
+ b3 = ((b3 << 18) | (b3 >> (64 - 18))) ^ b12;
+
+ b14 += b5;
+ b5 = ((b5 << 23) | (b5 >> (64 - 23))) ^ b14;
+
+ b8 += b1;
+ b1 = ((b1 << 52) | (b1 >> (64 - 52))) ^ b8;
+
+ b0 += b7;
+ b7 = ((b7 << 33) | (b7 >> (64 - 33))) ^ b0;
+
+ b2 += b5;
+ b5 = ((b5 << 4) | (b5 >> (64 - 4))) ^ b2;
+
+ b4 += b3;
+ b3 = ((b3 << 51) | (b3 >> (64 - 51))) ^ b4;
+
+ b6 += b1;
+ b1 = ((b1 << 13) | (b1 >> (64 - 13))) ^ b6;
+
+ b12 += b15;
+ b15 = ((b15 << 34) | (b15 >> (64 - 34))) ^ b12;
+
+ b14 += b13;
+ b13 = ((b13 << 41) | (b13 >> (64 - 41))) ^ b14;
+
+ b8 += b11;
+ b11 = ((b11 << 59) | (b11 >> (64 - 59))) ^ b8;
+
+ b10 += b9;
+ b9 = ((b9 << 17) | (b9 >> (64 - 17))) ^ b10;
+
+ b0 += b15;
+ b15 = ((b15 << 5) | (b15 >> (64 - 5))) ^ b0;
+
+ b2 += b11;
+ b11 = ((b11 << 20) | (b11 >> (64 - 20))) ^ b2;
+
+ b6 += b13;
+ b13 = ((b13 << 48) | (b13 >> (64 - 48))) ^ b6;
+
+ b4 += b9;
+ b9 = ((b9 << 41) | (b9 >> (64 - 41))) ^ b4;
+
+ b14 += b1;
+ b1 = ((b1 << 47) | (b1 >> (64 - 47))) ^ b14;
+
+ b8 += b5;
+ b5 = ((b5 << 28) | (b5 >> (64 - 28))) ^ b8;
+
+ b10 += b3;
+ b3 = ((b3 << 16) | (b3 >> (64 - 16))) ^ b10;
+
+ b12 += b7;
+ b7 = ((b7 << 25) | (b7 >> (64 - 25))) ^ b12;
+
+ b1 += k6;
+ b0 += b1 + k5;
+ b1 = ((b1 << 41) | (b1 >> (64 - 41))) ^ b0;
+
+ b3 += k8;
+ b2 += b3 + k7;
+ b3 = ((b3 << 9) | (b3 >> (64 - 9))) ^ b2;
+
+ b5 += k10;
+ b4 += b5 + k9;
+ b5 = ((b5 << 37) | (b5 >> (64 - 37))) ^ b4;
+
+ b7 += k12;
+ b6 += b7 + k11;
+ b7 = ((b7 << 31) | (b7 >> (64 - 31))) ^ b6;
+
+ b9 += k14;
+ b8 += b9 + k13;
+ b9 = ((b9 << 12) | (b9 >> (64 - 12))) ^ b8;
+
+ b11 += k16;
+ b10 += b11 + k15;
+ b11 = ((b11 << 47) | (b11 >> (64 - 47))) ^ b10;
+
+ b13 += k1 + t2;
+ b12 += b13 + k0;
+ b13 = ((b13 << 44) | (b13 >> (64 - 44))) ^ b12;
+
+ b15 += k3 + 5;
+ b14 += b15 + k2 + t0;
+ b15 = ((b15 << 30) | (b15 >> (64 - 30))) ^ b14;
+
+ b0 += b9;
+ b9 = ((b9 << 16) | (b9 >> (64 - 16))) ^ b0;
+
+ b2 += b13;
+ b13 = ((b13 << 34) | (b13 >> (64 - 34))) ^ b2;
+
+ b6 += b11;
+ b11 = ((b11 << 56) | (b11 >> (64 - 56))) ^ b6;
+
+ b4 += b15;
+ b15 = ((b15 << 51) | (b15 >> (64 - 51))) ^ b4;
+
+ b10 += b7;
+ b7 = ((b7 << 4) | (b7 >> (64 - 4))) ^ b10;
+
+ b12 += b3;
+ b3 = ((b3 << 53) | (b3 >> (64 - 53))) ^ b12;
+
+ b14 += b5;
+ b5 = ((b5 << 42) | (b5 >> (64 - 42))) ^ b14;
+
+ b8 += b1;
+ b1 = ((b1 << 41) | (b1 >> (64 - 41))) ^ b8;
+
+ b0 += b7;
+ b7 = ((b7 << 31) | (b7 >> (64 - 31))) ^ b0;
+
+ b2 += b5;
+ b5 = ((b5 << 44) | (b5 >> (64 - 44))) ^ b2;
+
+ b4 += b3;
+ b3 = ((b3 << 47) | (b3 >> (64 - 47))) ^ b4;
+
+ b6 += b1;
+ b1 = ((b1 << 46) | (b1 >> (64 - 46))) ^ b6;
+
+ b12 += b15;
+ b15 = ((b15 << 19) | (b15 >> (64 - 19))) ^ b12;
+
+ b14 += b13;
+ b13 = ((b13 << 42) | (b13 >> (64 - 42))) ^ b14;
+
+ b8 += b11;
+ b11 = ((b11 << 44) | (b11 >> (64 - 44))) ^ b8;
+
+ b10 += b9;
+ b9 = ((b9 << 25) | (b9 >> (64 - 25))) ^ b10;
+
+ b0 += b15;
+ b15 = ((b15 << 9) | (b15 >> (64 - 9))) ^ b0;
+
+ b2 += b11;
+ b11 = ((b11 << 48) | (b11 >> (64 - 48))) ^ b2;
+
+ b6 += b13;
+ b13 = ((b13 << 35) | (b13 >> (64 - 35))) ^ b6;
+
+ b4 += b9;
+ b9 = ((b9 << 52) | (b9 >> (64 - 52))) ^ b4;
+
+ b14 += b1;
+ b1 = ((b1 << 23) | (b1 >> (64 - 23))) ^ b14;
+
+ b8 += b5;
+ b5 = ((b5 << 31) | (b5 >> (64 - 31))) ^ b8;
+
+ b10 += b3;
+ b3 = ((b3 << 37) | (b3 >> (64 - 37))) ^ b10;
+
+ b12 += b7;
+ b7 = ((b7 << 20) | (b7 >> (64 - 20))) ^ b12;
+
+ b1 += k7;
+ b0 += b1 + k6;
+ b1 = ((b1 << 24) | (b1 >> (64 - 24))) ^ b0;
+
+ b3 += k9;
+ b2 += b3 + k8;
+ b3 = ((b3 << 13) | (b3 >> (64 - 13))) ^ b2;
+
+ b5 += k11;
+ b4 += b5 + k10;
+ b5 = ((b5 << 8) | (b5 >> (64 - 8))) ^ b4;
+
+ b7 += k13;
+ b6 += b7 + k12;
+ b7 = ((b7 << 47) | (b7 >> (64 - 47))) ^ b6;
+
+ b9 += k15;
+ b8 += b9 + k14;
+ b9 = ((b9 << 8) | (b9 >> (64 - 8))) ^ b8;
+
+ b11 += k0;
+ b10 += b11 + k16;
+ b11 = ((b11 << 17) | (b11 >> (64 - 17))) ^ b10;
+
+ b13 += k2 + t0;
+ b12 += b13 + k1;
+ b13 = ((b13 << 22) | (b13 >> (64 - 22))) ^ b12;
+
+ b15 += k4 + 6;
+ b14 += b15 + k3 + t1;
+ b15 = ((b15 << 37) | (b15 >> (64 - 37))) ^ b14;
+
+ b0 += b9;
+ b9 = ((b9 << 38) | (b9 >> (64 - 38))) ^ b0;
+
+ b2 += b13;
+ b13 = ((b13 << 19) | (b13 >> (64 - 19))) ^ b2;
+
+ b6 += b11;
+ b11 = ((b11 << 10) | (b11 >> (64 - 10))) ^ b6;
+
+ b4 += b15;
+ b15 = ((b15 << 55) | (b15 >> (64 - 55))) ^ b4;
+
+ b10 += b7;
+ b7 = ((b7 << 49) | (b7 >> (64 - 49))) ^ b10;
+
+ b12 += b3;
+ b3 = ((b3 << 18) | (b3 >> (64 - 18))) ^ b12;
+
+ b14 += b5;
+ b5 = ((b5 << 23) | (b5 >> (64 - 23))) ^ b14;
+
+ b8 += b1;
+ b1 = ((b1 << 52) | (b1 >> (64 - 52))) ^ b8;
+
+ b0 += b7;
+ b7 = ((b7 << 33) | (b7 >> (64 - 33))) ^ b0;
+
+ b2 += b5;
+ b5 = ((b5 << 4) | (b5 >> (64 - 4))) ^ b2;
+
+ b4 += b3;
+ b3 = ((b3 << 51) | (b3 >> (64 - 51))) ^ b4;
+
+ b6 += b1;
+ b1 = ((b1 << 13) | (b1 >> (64 - 13))) ^ b6;
+
+ b12 += b15;
+ b15 = ((b15 << 34) | (b15 >> (64 - 34))) ^ b12;
+
+ b14 += b13;
+ b13 = ((b13 << 41) | (b13 >> (64 - 41))) ^ b14;
+
+ b8 += b11;
+ b11 = ((b11 << 59) | (b11 >> (64 - 59))) ^ b8;
+
+ b10 += b9;
+ b9 = ((b9 << 17) | (b9 >> (64 - 17))) ^ b10;
+
+ b0 += b15;
+ b15 = ((b15 << 5) | (b15 >> (64 - 5))) ^ b0;
+
+ b2 += b11;
+ b11 = ((b11 << 20) | (b11 >> (64 - 20))) ^ b2;
+
+ b6 += b13;
+ b13 = ((b13 << 48) | (b13 >> (64 - 48))) ^ b6;
+
+ b4 += b9;
+ b9 = ((b9 << 41) | (b9 >> (64 - 41))) ^ b4;
+
+ b14 += b1;
+ b1 = ((b1 << 47) | (b1 >> (64 - 47))) ^ b14;
+
+ b8 += b5;
+ b5 = ((b5 << 28) | (b5 >> (64 - 28))) ^ b8;
+
+ b10 += b3;
+ b3 = ((b3 << 16) | (b3 >> (64 - 16))) ^ b10;
+
+ b12 += b7;
+ b7 = ((b7 << 25) | (b7 >> (64 - 25))) ^ b12;
+
+ b1 += k8;
+ b0 += b1 + k7;
+ b1 = ((b1 << 41) | (b1 >> (64 - 41))) ^ b0;
+
+ b3 += k10;
+ b2 += b3 + k9;
+ b3 = ((b3 << 9) | (b3 >> (64 - 9))) ^ b2;
+
+ b5 += k12;
+ b4 += b5 + k11;
+ b5 = ((b5 << 37) | (b5 >> (64 - 37))) ^ b4;
+
+ b7 += k14;
+ b6 += b7 + k13;
+ b7 = ((b7 << 31) | (b7 >> (64 - 31))) ^ b6;
+
+ b9 += k16;
+ b8 += b9 + k15;
+ b9 = ((b9 << 12) | (b9 >> (64 - 12))) ^ b8;
+
+ b11 += k1;
+ b10 += b11 + k0;
+ b11 = ((b11 << 47) | (b11 >> (64 - 47))) ^ b10;
+
+ b13 += k3 + t1;
+ b12 += b13 + k2;
+ b13 = ((b13 << 44) | (b13 >> (64 - 44))) ^ b12;
+
+ b15 += k5 + 7;
+ b14 += b15 + k4 + t2;
+ b15 = ((b15 << 30) | (b15 >> (64 - 30))) ^ b14;
+
+ b0 += b9;
+ b9 = ((b9 << 16) | (b9 >> (64 - 16))) ^ b0;
+
+ b2 += b13;
+ b13 = ((b13 << 34) | (b13 >> (64 - 34))) ^ b2;
+
+ b6 += b11;
+ b11 = ((b11 << 56) | (b11 >> (64 - 56))) ^ b6;
+
+ b4 += b15;
+ b15 = ((b15 << 51) | (b15 >> (64 - 51))) ^ b4;
+
+ b10 += b7;
+ b7 = ((b7 << 4) | (b7 >> (64 - 4))) ^ b10;
+
+ b12 += b3;
+ b3 = ((b3 << 53) | (b3 >> (64 - 53))) ^ b12;
+
+ b14 += b5;
+ b5 = ((b5 << 42) | (b5 >> (64 - 42))) ^ b14;
+
+ b8 += b1;
+ b1 = ((b1 << 41) | (b1 >> (64 - 41))) ^ b8;
+
+ b0 += b7;
+ b7 = ((b7 << 31) | (b7 >> (64 - 31))) ^ b0;
+
+ b2 += b5;
+ b5 = ((b5 << 44) | (b5 >> (64 - 44))) ^ b2;
+
+ b4 += b3;
+ b3 = ((b3 << 47) | (b3 >> (64 - 47))) ^ b4;
+
+ b6 += b1;
+ b1 = ((b1 << 46) | (b1 >> (64 - 46))) ^ b6;
+
+ b12 += b15;
+ b15 = ((b15 << 19) | (b15 >> (64 - 19))) ^ b12;
+
+ b14 += b13;
+ b13 = ((b13 << 42) | (b13 >> (64 - 42))) ^ b14;
+
+ b8 += b11;
+ b11 = ((b11 << 44) | (b11 >> (64 - 44))) ^ b8;
+
+ b10 += b9;
+ b9 = ((b9 << 25) | (b9 >> (64 - 25))) ^ b10;
+
+ b0 += b15;
+ b15 = ((b15 << 9) | (b15 >> (64 - 9))) ^ b0;
+
+ b2 += b11;
+ b11 = ((b11 << 48) | (b11 >> (64 - 48))) ^ b2;
+
+ b6 += b13;
+ b13 = ((b13 << 35) | (b13 >> (64 - 35))) ^ b6;
+
+ b4 += b9;
+ b9 = ((b9 << 52) | (b9 >> (64 - 52))) ^ b4;
+
+ b14 += b1;
+ b1 = ((b1 << 23) | (b1 >> (64 - 23))) ^ b14;
+
+ b8 += b5;
+ b5 = ((b5 << 31) | (b5 >> (64 - 31))) ^ b8;
+
+ b10 += b3;
+ b3 = ((b3 << 37) | (b3 >> (64 - 37))) ^ b10;
+
+ b12 += b7;
+ b7 = ((b7 << 20) | (b7 >> (64 - 20))) ^ b12;
+
+ b1 += k9;
+ b0 += b1 + k8;
+ b1 = ((b1 << 24) | (b1 >> (64 - 24))) ^ b0;
+
+ b3 += k11;
+ b2 += b3 + k10;
+ b3 = ((b3 << 13) | (b3 >> (64 - 13))) ^ b2;
+
+ b5 += k13;
+ b4 += b5 + k12;
+ b5 = ((b5 << 8) | (b5 >> (64 - 8))) ^ b4;
+
+ b7 += k15;
+ b6 += b7 + k14;
+ b7 = ((b7 << 47) | (b7 >> (64 - 47))) ^ b6;
+
+ b9 += k0;
+ b8 += b9 + k16;
+ b9 = ((b9 << 8) | (b9 >> (64 - 8))) ^ b8;
+
+ b11 += k2;
+ b10 += b11 + k1;
+ b11 = ((b11 << 17) | (b11 >> (64 - 17))) ^ b10;
+
+ b13 += k4 + t2;
+ b12 += b13 + k3;
+ b13 = ((b13 << 22) | (b13 >> (64 - 22))) ^ b12;
+
+ b15 += k6 + 8;
+ b14 += b15 + k5 + t0;
+ b15 = ((b15 << 37) | (b15 >> (64 - 37))) ^ b14;
+
+ b0 += b9;
+ b9 = ((b9 << 38) | (b9 >> (64 - 38))) ^ b0;
+
+ b2 += b13;
+ b13 = ((b13 << 19) | (b13 >> (64 - 19))) ^ b2;
+
+ b6 += b11;
+ b11 = ((b11 << 10) | (b11 >> (64 - 10))) ^ b6;
+
+ b4 += b15;
+ b15 = ((b15 << 55) | (b15 >> (64 - 55))) ^ b4;
+
+ b10 += b7;
+ b7 = ((b7 << 49) | (b7 >> (64 - 49))) ^ b10;
+
+ b12 += b3;
+ b3 = ((b3 << 18) | (b3 >> (64 - 18))) ^ b12;
+
+ b14 += b5;
+ b5 = ((b5 << 23) | (b5 >> (64 - 23))) ^ b14;
+
+ b8 += b1;
+ b1 = ((b1 << 52) | (b1 >> (64 - 52))) ^ b8;
+
+ b0 += b7;
+ b7 = ((b7 << 33) | (b7 >> (64 - 33))) ^ b0;
+
+ b2 += b5;
+ b5 = ((b5 << 4) | (b5 >> (64 - 4))) ^ b2;
+
+ b4 += b3;
+ b3 = ((b3 << 51) | (b3 >> (64 - 51))) ^ b4;
+
+ b6 += b1;
+ b1 = ((b1 << 13) | (b1 >> (64 - 13))) ^ b6;
+
+ b12 += b15;
+ b15 = ((b15 << 34) | (b15 >> (64 - 34))) ^ b12;
+
+ b14 += b13;
+ b13 = ((b13 << 41) | (b13 >> (64 - 41))) ^ b14;
+
+ b8 += b11;
+ b11 = ((b11 << 59) | (b11 >> (64 - 59))) ^ b8;
+
+ b10 += b9;
+ b9 = ((b9 << 17) | (b9 >> (64 - 17))) ^ b10;
+
+ b0 += b15;
+ b15 = ((b15 << 5) | (b15 >> (64 - 5))) ^ b0;
+
+ b2 += b11;
+ b11 = ((b11 << 20) | (b11 >> (64 - 20))) ^ b2;
+
+ b6 += b13;
+ b13 = ((b13 << 48) | (b13 >> (64 - 48))) ^ b6;
+
+ b4 += b9;
+ b9 = ((b9 << 41) | (b9 >> (64 - 41))) ^ b4;
+
+ b14 += b1;
+ b1 = ((b1 << 47) | (b1 >> (64 - 47))) ^ b14;
+
+ b8 += b5;
+ b5 = ((b5 << 28) | (b5 >> (64 - 28))) ^ b8;
+
+ b10 += b3;
+ b3 = ((b3 << 16) | (b3 >> (64 - 16))) ^ b10;
+
+ b12 += b7;
+ b7 = ((b7 << 25) | (b7 >> (64 - 25))) ^ b12;
+
+ b1 += k10;
+ b0 += b1 + k9;
+ b1 = ((b1 << 41) | (b1 >> (64 - 41))) ^ b0;
+
+ b3 += k12;
+ b2 += b3 + k11;
+ b3 = ((b3 << 9) | (b3 >> (64 - 9))) ^ b2;
+
+ b5 += k14;
+ b4 += b5 + k13;
+ b5 = ((b5 << 37) | (b5 >> (64 - 37))) ^ b4;
+
+ b7 += k16;
+ b6 += b7 + k15;
+ b7 = ((b7 << 31) | (b7 >> (64 - 31))) ^ b6;
+
+ b9 += k1;
+ b8 += b9 + k0;
+ b9 = ((b9 << 12) | (b9 >> (64 - 12))) ^ b8;
+
+ b11 += k3;
+ b10 += b11 + k2;
+ b11 = ((b11 << 47) | (b11 >> (64 - 47))) ^ b10;
+
+ b13 += k5 + t0;
+ b12 += b13 + k4;
+ b13 = ((b13 << 44) | (b13 >> (64 - 44))) ^ b12;
+
+ b15 += k7 + 9;
+ b14 += b15 + k6 + t1;
+ b15 = ((b15 << 30) | (b15 >> (64 - 30))) ^ b14;
+
+ b0 += b9;
+ b9 = ((b9 << 16) | (b9 >> (64 - 16))) ^ b0;
+
+ b2 += b13;
+ b13 = ((b13 << 34) | (b13 >> (64 - 34))) ^ b2;
+
+ b6 += b11;
+ b11 = ((b11 << 56) | (b11 >> (64 - 56))) ^ b6;
+
+ b4 += b15;
+ b15 = ((b15 << 51) | (b15 >> (64 - 51))) ^ b4;
+
+ b10 += b7;
+ b7 = ((b7 << 4) | (b7 >> (64 - 4))) ^ b10;
+
+ b12 += b3;
+ b3 = ((b3 << 53) | (b3 >> (64 - 53))) ^ b12;
+
+ b14 += b5;
+ b5 = ((b5 << 42) | (b5 >> (64 - 42))) ^ b14;
+
+ b8 += b1;
+ b1 = ((b1 << 41) | (b1 >> (64 - 41))) ^ b8;
+
+ b0 += b7;
+ b7 = ((b7 << 31) | (b7 >> (64 - 31))) ^ b0;
+
+ b2 += b5;
+ b5 = ((b5 << 44) | (b5 >> (64 - 44))) ^ b2;
+
+ b4 += b3;
+ b3 = ((b3 << 47) | (b3 >> (64 - 47))) ^ b4;
+
+ b6 += b1;
+ b1 = ((b1 << 46) | (b1 >> (64 - 46))) ^ b6;
+
+ b12 += b15;
+ b15 = ((b15 << 19) | (b15 >> (64 - 19))) ^ b12;
+
+ b14 += b13;
+ b13 = ((b13 << 42) | (b13 >> (64 - 42))) ^ b14;
+
+ b8 += b11;
+ b11 = ((b11 << 44) | (b11 >> (64 - 44))) ^ b8;
+
+ b10 += b9;
+ b9 = ((b9 << 25) | (b9 >> (64 - 25))) ^ b10;
+
+ b0 += b15;
+ b15 = ((b15 << 9) | (b15 >> (64 - 9))) ^ b0;
+
+ b2 += b11;
+ b11 = ((b11 << 48) | (b11 >> (64 - 48))) ^ b2;
+
+ b6 += b13;
+ b13 = ((b13 << 35) | (b13 >> (64 - 35))) ^ b6;
+
+ b4 += b9;
+ b9 = ((b9 << 52) | (b9 >> (64 - 52))) ^ b4;
+
+ b14 += b1;
+ b1 = ((b1 << 23) | (b1 >> (64 - 23))) ^ b14;
+
+ b8 += b5;
+ b5 = ((b5 << 31) | (b5 >> (64 - 31))) ^ b8;
+
+ b10 += b3;
+ b3 = ((b3 << 37) | (b3 >> (64 - 37))) ^ b10;
+
+ b12 += b7;
+ b7 = ((b7 << 20) | (b7 >> (64 - 20))) ^ b12;
+
+ b1 += k11;
+ b0 += b1 + k10;
+ b1 = ((b1 << 24) | (b1 >> (64 - 24))) ^ b0;
+
+ b3 += k13;
+ b2 += b3 + k12;
+ b3 = ((b3 << 13) | (b3 >> (64 - 13))) ^ b2;
+
+ b5 += k15;
+ b4 += b5 + k14;
+ b5 = ((b5 << 8) | (b5 >> (64 - 8))) ^ b4;
+
+ b7 += k0;
+ b6 += b7 + k16;
+ b7 = ((b7 << 47) | (b7 >> (64 - 47))) ^ b6;
+
+ b9 += k2;
+ b8 += b9 + k1;
+ b9 = ((b9 << 8) | (b9 >> (64 - 8))) ^ b8;
+
+ b11 += k4;
+ b10 += b11 + k3;
+ b11 = ((b11 << 17) | (b11 >> (64 - 17))) ^ b10;
+
+ b13 += k6 + t1;
+ b12 += b13 + k5;
+ b13 = ((b13 << 22) | (b13 >> (64 - 22))) ^ b12;
+
+ b15 += k8 + 10;
+ b14 += b15 + k7 + t2;
+ b15 = ((b15 << 37) | (b15 >> (64 - 37))) ^ b14;
+
+ b0 += b9;
+ b9 = ((b9 << 38) | (b9 >> (64 - 38))) ^ b0;
+
+ b2 += b13;
+ b13 = ((b13 << 19) | (b13 >> (64 - 19))) ^ b2;
+
+ b6 += b11;
+ b11 = ((b11 << 10) | (b11 >> (64 - 10))) ^ b6;
+
+ b4 += b15;
+ b15 = ((b15 << 55) | (b15 >> (64 - 55))) ^ b4;
+
+ b10 += b7;
+ b7 = ((b7 << 49) | (b7 >> (64 - 49))) ^ b10;
+
+ b12 += b3;
+ b3 = ((b3 << 18) | (b3 >> (64 - 18))) ^ b12;
+
+ b14 += b5;
+ b5 = ((b5 << 23) | (b5 >> (64 - 23))) ^ b14;
+
+ b8 += b1;
+ b1 = ((b1 << 52) | (b1 >> (64 - 52))) ^ b8;
+
+ b0 += b7;
+ b7 = ((b7 << 33) | (b7 >> (64 - 33))) ^ b0;
+
+ b2 += b5;
+ b5 = ((b5 << 4) | (b5 >> (64 - 4))) ^ b2;
+
+ b4 += b3;
+ b3 = ((b3 << 51) | (b3 >> (64 - 51))) ^ b4;
+
+ b6 += b1;
+ b1 = ((b1 << 13) | (b1 >> (64 - 13))) ^ b6;
+
+ b12 += b15;
+ b15 = ((b15 << 34) | (b15 >> (64 - 34))) ^ b12;
+
+ b14 += b13;
+ b13 = ((b13 << 41) | (b13 >> (64 - 41))) ^ b14;
+
+ b8 += b11;
+ b11 = ((b11 << 59) | (b11 >> (64 - 59))) ^ b8;
+
+ b10 += b9;
+ b9 = ((b9 << 17) | (b9 >> (64 - 17))) ^ b10;
+
+ b0 += b15;
+ b15 = ((b15 << 5) | (b15 >> (64 - 5))) ^ b0;
+
+ b2 += b11;
+ b11 = ((b11 << 20) | (b11 >> (64 - 20))) ^ b2;
+
+ b6 += b13;
+ b13 = ((b13 << 48) | (b13 >> (64 - 48))) ^ b6;
+
+ b4 += b9;
+ b9 = ((b9 << 41) | (b9 >> (64 - 41))) ^ b4;
+
+ b14 += b1;
+ b1 = ((b1 << 47) | (b1 >> (64 - 47))) ^ b14;
+
+ b8 += b5;
+ b5 = ((b5 << 28) | (b5 >> (64 - 28))) ^ b8;
+
+ b10 += b3;
+ b3 = ((b3 << 16) | (b3 >> (64 - 16))) ^ b10;
+
+ b12 += b7;
+ b7 = ((b7 << 25) | (b7 >> (64 - 25))) ^ b12;
+
+ b1 += k12;
+ b0 += b1 + k11;
+ b1 = ((b1 << 41) | (b1 >> (64 - 41))) ^ b0;
+
+ b3 += k14;
+ b2 += b3 + k13;
+ b3 = ((b3 << 9) | (b3 >> (64 - 9))) ^ b2;
+
+ b5 += k16;
+ b4 += b5 + k15;
+ b5 = ((b5 << 37) | (b5 >> (64 - 37))) ^ b4;
+
+ b7 += k1;
+ b6 += b7 + k0;
+ b7 = ((b7 << 31) | (b7 >> (64 - 31))) ^ b6;
+
+ b9 += k3;
+ b8 += b9 + k2;
+ b9 = ((b9 << 12) | (b9 >> (64 - 12))) ^ b8;
+
+ b11 += k5;
+ b10 += b11 + k4;
+ b11 = ((b11 << 47) | (b11 >> (64 - 47))) ^ b10;
+
+ b13 += k7 + t2;
+ b12 += b13 + k6;
+ b13 = ((b13 << 44) | (b13 >> (64 - 44))) ^ b12;
+
+ b15 += k9 + 11;
+ b14 += b15 + k8 + t0;
+ b15 = ((b15 << 30) | (b15 >> (64 - 30))) ^ b14;
+
+ b0 += b9;
+ b9 = ((b9 << 16) | (b9 >> (64 - 16))) ^ b0;
+
+ b2 += b13;
+ b13 = ((b13 << 34) | (b13 >> (64 - 34))) ^ b2;
+
+ b6 += b11;
+ b11 = ((b11 << 56) | (b11 >> (64 - 56))) ^ b6;
+
+ b4 += b15;
+ b15 = ((b15 << 51) | (b15 >> (64 - 51))) ^ b4;
+
+ b10 += b7;
+ b7 = ((b7 << 4) | (b7 >> (64 - 4))) ^ b10;
+
+ b12 += b3;
+ b3 = ((b3 << 53) | (b3 >> (64 - 53))) ^ b12;
+
+ b14 += b5;
+ b5 = ((b5 << 42) | (b5 >> (64 - 42))) ^ b14;
+
+ b8 += b1;
+ b1 = ((b1 << 41) | (b1 >> (64 - 41))) ^ b8;
+
+ b0 += b7;
+ b7 = ((b7 << 31) | (b7 >> (64 - 31))) ^ b0;
+
+ b2 += b5;
+ b5 = ((b5 << 44) | (b5 >> (64 - 44))) ^ b2;
+
+ b4 += b3;
+ b3 = ((b3 << 47) | (b3 >> (64 - 47))) ^ b4;
+
+ b6 += b1;
+ b1 = ((b1 << 46) | (b1 >> (64 - 46))) ^ b6;
+
+ b12 += b15;
+ b15 = ((b15 << 19) | (b15 >> (64 - 19))) ^ b12;
+
+ b14 += b13;
+ b13 = ((b13 << 42) | (b13 >> (64 - 42))) ^ b14;
+
+ b8 += b11;
+ b11 = ((b11 << 44) | (b11 >> (64 - 44))) ^ b8;
+
+ b10 += b9;
+ b9 = ((b9 << 25) | (b9 >> (64 - 25))) ^ b10;
+
+ b0 += b15;
+ b15 = ((b15 << 9) | (b15 >> (64 - 9))) ^ b0;
+
+ b2 += b11;
+ b11 = ((b11 << 48) | (b11 >> (64 - 48))) ^ b2;
+
+ b6 += b13;
+ b13 = ((b13 << 35) | (b13 >> (64 - 35))) ^ b6;
+
+ b4 += b9;
+ b9 = ((b9 << 52) | (b9 >> (64 - 52))) ^ b4;
+
+ b14 += b1;
+ b1 = ((b1 << 23) | (b1 >> (64 - 23))) ^ b14;
+
+ b8 += b5;
+ b5 = ((b5 << 31) | (b5 >> (64 - 31))) ^ b8;
+
+ b10 += b3;
+ b3 = ((b3 << 37) | (b3 >> (64 - 37))) ^ b10;
+
+ b12 += b7;
+ b7 = ((b7 << 20) | (b7 >> (64 - 20))) ^ b12;
+
+ b1 += k13;
+ b0 += b1 + k12;
+ b1 = ((b1 << 24) | (b1 >> (64 - 24))) ^ b0;
+
+ b3 += k15;
+ b2 += b3 + k14;
+ b3 = ((b3 << 13) | (b3 >> (64 - 13))) ^ b2;
+
+ b5 += k0;
+ b4 += b5 + k16;
+ b5 = ((b5 << 8) | (b5 >> (64 - 8))) ^ b4;
+
+ b7 += k2;
+ b6 += b7 + k1;
+ b7 = ((b7 << 47) | (b7 >> (64 - 47))) ^ b6;
+
+ b9 += k4;
+ b8 += b9 + k3;
+ b9 = ((b9 << 8) | (b9 >> (64 - 8))) ^ b8;
+
+ b11 += k6;
+ b10 += b11 + k5;
+ b11 = ((b11 << 17) | (b11 >> (64 - 17))) ^ b10;
+
+ b13 += k8 + t0;
+ b12 += b13 + k7;
+ b13 = ((b13 << 22) | (b13 >> (64 - 22))) ^ b12;
+
+ b15 += k10 + 12;
+ b14 += b15 + k9 + t1;
+ b15 = ((b15 << 37) | (b15 >> (64 - 37))) ^ b14;
+
+ b0 += b9;
+ b9 = ((b9 << 38) | (b9 >> (64 - 38))) ^ b0;
+
+ b2 += b13;
+ b13 = ((b13 << 19) | (b13 >> (64 - 19))) ^ b2;
+
+ b6 += b11;
+ b11 = ((b11 << 10) | (b11 >> (64 - 10))) ^ b6;
+
+ b4 += b15;
+ b15 = ((b15 << 55) | (b15 >> (64 - 55))) ^ b4;
+
+ b10 += b7;
+ b7 = ((b7 << 49) | (b7 >> (64 - 49))) ^ b10;
+
+ b12 += b3;
+ b3 = ((b3 << 18) | (b3 >> (64 - 18))) ^ b12;
+
+ b14 += b5;
+ b5 = ((b5 << 23) | (b5 >> (64 - 23))) ^ b14;
+
+ b8 += b1;
+ b1 = ((b1 << 52) | (b1 >> (64 - 52))) ^ b8;
+
+ b0 += b7;
+ b7 = ((b7 << 33) | (b7 >> (64 - 33))) ^ b0;
+
+ b2 += b5;
+ b5 = ((b5 << 4) | (b5 >> (64 - 4))) ^ b2;
+
+ b4 += b3;
+ b3 = ((b3 << 51) | (b3 >> (64 - 51))) ^ b4;
+
+ b6 += b1;
+ b1 = ((b1 << 13) | (b1 >> (64 - 13))) ^ b6;
+
+ b12 += b15;
+ b15 = ((b15 << 34) | (b15 >> (64 - 34))) ^ b12;
+
+ b14 += b13;
+ b13 = ((b13 << 41) | (b13 >> (64 - 41))) ^ b14;
+
+ b8 += b11;
+ b11 = ((b11 << 59) | (b11 >> (64 - 59))) ^ b8;
+
+ b10 += b9;
+ b9 = ((b9 << 17) | (b9 >> (64 - 17))) ^ b10;
+
+ b0 += b15;
+ b15 = ((b15 << 5) | (b15 >> (64 - 5))) ^ b0;
+
+ b2 += b11;
+ b11 = ((b11 << 20) | (b11 >> (64 - 20))) ^ b2;
+
+ b6 += b13;
+ b13 = ((b13 << 48) | (b13 >> (64 - 48))) ^ b6;
+
+ b4 += b9;
+ b9 = ((b9 << 41) | (b9 >> (64 - 41))) ^ b4;
+
+ b14 += b1;
+ b1 = ((b1 << 47) | (b1 >> (64 - 47))) ^ b14;
+
+ b8 += b5;
+ b5 = ((b5 << 28) | (b5 >> (64 - 28))) ^ b8;
+
+ b10 += b3;
+ b3 = ((b3 << 16) | (b3 >> (64 - 16))) ^ b10;
+
+ b12 += b7;
+ b7 = ((b7 << 25) | (b7 >> (64 - 25))) ^ b12;
+
+ b1 += k14;
+ b0 += b1 + k13;
+ b1 = ((b1 << 41) | (b1 >> (64 - 41))) ^ b0;
+
+ b3 += k16;
+ b2 += b3 + k15;
+ b3 = ((b3 << 9) | (b3 >> (64 - 9))) ^ b2;
+
+ b5 += k1;
+ b4 += b5 + k0;
+ b5 = ((b5 << 37) | (b5 >> (64 - 37))) ^ b4;
+
+ b7 += k3;
+ b6 += b7 + k2;
+ b7 = ((b7 << 31) | (b7 >> (64 - 31))) ^ b6;
+
+ b9 += k5;
+ b8 += b9 + k4;
+ b9 = ((b9 << 12) | (b9 >> (64 - 12))) ^ b8;
+
+ b11 += k7;
+ b10 += b11 + k6;
+ b11 = ((b11 << 47) | (b11 >> (64 - 47))) ^ b10;
+
+ b13 += k9 + t1;
+ b12 += b13 + k8;
+ b13 = ((b13 << 44) | (b13 >> (64 - 44))) ^ b12;
+
+ b15 += k11 + 13;
+ b14 += b15 + k10 + t2;
+ b15 = ((b15 << 30) | (b15 >> (64 - 30))) ^ b14;
+
+ b0 += b9;
+ b9 = ((b9 << 16) | (b9 >> (64 - 16))) ^ b0;
+
+ b2 += b13;
+ b13 = ((b13 << 34) | (b13 >> (64 - 34))) ^ b2;
+
+ b6 += b11;
+ b11 = ((b11 << 56) | (b11 >> (64 - 56))) ^ b6;
+
+ b4 += b15;
+ b15 = ((b15 << 51) | (b15 >> (64 - 51))) ^ b4;
+
+ b10 += b7;
+ b7 = ((b7 << 4) | (b7 >> (64 - 4))) ^ b10;
+
+ b12 += b3;
+ b3 = ((b3 << 53) | (b3 >> (64 - 53))) ^ b12;
+
+ b14 += b5;
+ b5 = ((b5 << 42) | (b5 >> (64 - 42))) ^ b14;
+
+ b8 += b1;
+ b1 = ((b1 << 41) | (b1 >> (64 - 41))) ^ b8;
+
+ b0 += b7;
+ b7 = ((b7 << 31) | (b7 >> (64 - 31))) ^ b0;
+
+ b2 += b5;
+ b5 = ((b5 << 44) | (b5 >> (64 - 44))) ^ b2;
+
+ b4 += b3;
+ b3 = ((b3 << 47) | (b3 >> (64 - 47))) ^ b4;
+
+ b6 += b1;
+ b1 = ((b1 << 46) | (b1 >> (64 - 46))) ^ b6;
+
+ b12 += b15;
+ b15 = ((b15 << 19) | (b15 >> (64 - 19))) ^ b12;
+
+ b14 += b13;
+ b13 = ((b13 << 42) | (b13 >> (64 - 42))) ^ b14;
+
+ b8 += b11;
+ b11 = ((b11 << 44) | (b11 >> (64 - 44))) ^ b8;
+
+ b10 += b9;
+ b9 = ((b9 << 25) | (b9 >> (64 - 25))) ^ b10;
+
+ b0 += b15;
+ b15 = ((b15 << 9) | (b15 >> (64 - 9))) ^ b0;
+
+ b2 += b11;
+ b11 = ((b11 << 48) | (b11 >> (64 - 48))) ^ b2;
+
+ b6 += b13;
+ b13 = ((b13 << 35) | (b13 >> (64 - 35))) ^ b6;
+
+ b4 += b9;
+ b9 = ((b9 << 52) | (b9 >> (64 - 52))) ^ b4;
+
+ b14 += b1;
+ b1 = ((b1 << 23) | (b1 >> (64 - 23))) ^ b14;
+
+ b8 += b5;
+ b5 = ((b5 << 31) | (b5 >> (64 - 31))) ^ b8;
+
+ b10 += b3;
+ b3 = ((b3 << 37) | (b3 >> (64 - 37))) ^ b10;
+
+ b12 += b7;
+ b7 = ((b7 << 20) | (b7 >> (64 - 20))) ^ b12;
+
+ b1 += k15;
+ b0 += b1 + k14;
+ b1 = ((b1 << 24) | (b1 >> (64 - 24))) ^ b0;
+
+ b3 += k0;
+ b2 += b3 + k16;
+ b3 = ((b3 << 13) | (b3 >> (64 - 13))) ^ b2;
+
+ b5 += k2;
+ b4 += b5 + k1;
+ b5 = ((b5 << 8) | (b5 >> (64 - 8))) ^ b4;
+
+ b7 += k4;
+ b6 += b7 + k3;
+ b7 = ((b7 << 47) | (b7 >> (64 - 47))) ^ b6;
+
+ b9 += k6;
+ b8 += b9 + k5;
+ b9 = ((b9 << 8) | (b9 >> (64 - 8))) ^ b8;
+
+ b11 += k8;
+ b10 += b11 + k7;
+ b11 = ((b11 << 17) | (b11 >> (64 - 17))) ^ b10;
+
+ b13 += k10 + t2;
+ b12 += b13 + k9;
+ b13 = ((b13 << 22) | (b13 >> (64 - 22))) ^ b12;
+
+ b15 += k12 + 14;
+ b14 += b15 + k11 + t0;
+ b15 = ((b15 << 37) | (b15 >> (64 - 37))) ^ b14;
+
+ b0 += b9;
+ b9 = ((b9 << 38) | (b9 >> (64 - 38))) ^ b0;
+
+ b2 += b13;
+ b13 = ((b13 << 19) | (b13 >> (64 - 19))) ^ b2;
+
+ b6 += b11;
+ b11 = ((b11 << 10) | (b11 >> (64 - 10))) ^ b6;
+
+ b4 += b15;
+ b15 = ((b15 << 55) | (b15 >> (64 - 55))) ^ b4;
+
+ b10 += b7;
+ b7 = ((b7 << 49) | (b7 >> (64 - 49))) ^ b10;
+
+ b12 += b3;
+ b3 = ((b3 << 18) | (b3 >> (64 - 18))) ^ b12;
+
+ b14 += b5;
+ b5 = ((b5 << 23) | (b5 >> (64 - 23))) ^ b14;
+
+ b8 += b1;
+ b1 = ((b1 << 52) | (b1 >> (64 - 52))) ^ b8;
+
+ b0 += b7;
+ b7 = ((b7 << 33) | (b7 >> (64 - 33))) ^ b0;
+
+ b2 += b5;
+ b5 = ((b5 << 4) | (b5 >> (64 - 4))) ^ b2;
+
+ b4 += b3;
+ b3 = ((b3 << 51) | (b3 >> (64 - 51))) ^ b4;
+
+ b6 += b1;
+ b1 = ((b1 << 13) | (b1 >> (64 - 13))) ^ b6;
+
+ b12 += b15;
+ b15 = ((b15 << 34) | (b15 >> (64 - 34))) ^ b12;
+
+ b14 += b13;
+ b13 = ((b13 << 41) | (b13 >> (64 - 41))) ^ b14;
+
+ b8 += b11;
+ b11 = ((b11 << 59) | (b11 >> (64 - 59))) ^ b8;
+
+ b10 += b9;
+ b9 = ((b9 << 17) | (b9 >> (64 - 17))) ^ b10;
+
+ b0 += b15;
+ b15 = ((b15 << 5) | (b15 >> (64 - 5))) ^ b0;
+
+ b2 += b11;
+ b11 = ((b11 << 20) | (b11 >> (64 - 20))) ^ b2;
+
+ b6 += b13;
+ b13 = ((b13 << 48) | (b13 >> (64 - 48))) ^ b6;
+
+ b4 += b9;
+ b9 = ((b9 << 41) | (b9 >> (64 - 41))) ^ b4;
+
+ b14 += b1;
+ b1 = ((b1 << 47) | (b1 >> (64 - 47))) ^ b14;
+
+ b8 += b5;
+ b5 = ((b5 << 28) | (b5 >> (64 - 28))) ^ b8;
+
+ b10 += b3;
+ b3 = ((b3 << 16) | (b3 >> (64 - 16))) ^ b10;
+
+ b12 += b7;
+ b7 = ((b7 << 25) | (b7 >> (64 - 25))) ^ b12;
+
+ b1 += k16;
+ b0 += b1 + k15;
+ b1 = ((b1 << 41) | (b1 >> (64 - 41))) ^ b0;
+
+ b3 += k1;
+ b2 += b3 + k0;
+ b3 = ((b3 << 9) | (b3 >> (64 - 9))) ^ b2;
+
+ b5 += k3;
+ b4 += b5 + k2;
+ b5 = ((b5 << 37) | (b5 >> (64 - 37))) ^ b4;
+
+ b7 += k5;
+ b6 += b7 + k4;
+ b7 = ((b7 << 31) | (b7 >> (64 - 31))) ^ b6;
+
+ b9 += k7;
+ b8 += b9 + k6;
+ b9 = ((b9 << 12) | (b9 >> (64 - 12))) ^ b8;
+
+ b11 += k9;
+ b10 += b11 + k8;
+ b11 = ((b11 << 47) | (b11 >> (64 - 47))) ^ b10;
+
+ b13 += k11 + t0;
+ b12 += b13 + k10;
+ b13 = ((b13 << 44) | (b13 >> (64 - 44))) ^ b12;
+
+ b15 += k13 + 15;
+ b14 += b15 + k12 + t1;
+ b15 = ((b15 << 30) | (b15 >> (64 - 30))) ^ b14;
+
+ b0 += b9;
+ b9 = ((b9 << 16) | (b9 >> (64 - 16))) ^ b0;
+
+ b2 += b13;
+ b13 = ((b13 << 34) | (b13 >> (64 - 34))) ^ b2;
+
+ b6 += b11;
+ b11 = ((b11 << 56) | (b11 >> (64 - 56))) ^ b6;
+
+ b4 += b15;
+ b15 = ((b15 << 51) | (b15 >> (64 - 51))) ^ b4;
+
+ b10 += b7;
+ b7 = ((b7 << 4) | (b7 >> (64 - 4))) ^ b10;
+
+ b12 += b3;
+ b3 = ((b3 << 53) | (b3 >> (64 - 53))) ^ b12;
+
+ b14 += b5;
+ b5 = ((b5 << 42) | (b5 >> (64 - 42))) ^ b14;
+
+ b8 += b1;
+ b1 = ((b1 << 41) | (b1 >> (64 - 41))) ^ b8;
+
+ b0 += b7;
+ b7 = ((b7 << 31) | (b7 >> (64 - 31))) ^ b0;
+
+ b2 += b5;
+ b5 = ((b5 << 44) | (b5 >> (64 - 44))) ^ b2;
+
+ b4 += b3;
+ b3 = ((b3 << 47) | (b3 >> (64 - 47))) ^ b4;
+
+ b6 += b1;
+ b1 = ((b1 << 46) | (b1 >> (64 - 46))) ^ b6;
+
+ b12 += b15;
+ b15 = ((b15 << 19) | (b15 >> (64 - 19))) ^ b12;
+
+ b14 += b13;
+ b13 = ((b13 << 42) | (b13 >> (64 - 42))) ^ b14;
+
+ b8 += b11;
+ b11 = ((b11 << 44) | (b11 >> (64 - 44))) ^ b8;
+
+ b10 += b9;
+ b9 = ((b9 << 25) | (b9 >> (64 - 25))) ^ b10;
+
+ b0 += b15;
+ b15 = ((b15 << 9) | (b15 >> (64 - 9))) ^ b0;
+
+ b2 += b11;
+ b11 = ((b11 << 48) | (b11 >> (64 - 48))) ^ b2;
+
+ b6 += b13;
+ b13 = ((b13 << 35) | (b13 >> (64 - 35))) ^ b6;
+
+ b4 += b9;
+ b9 = ((b9 << 52) | (b9 >> (64 - 52))) ^ b4;
+
+ b14 += b1;
+ b1 = ((b1 << 23) | (b1 >> (64 - 23))) ^ b14;
+
+ b8 += b5;
+ b5 = ((b5 << 31) | (b5 >> (64 - 31))) ^ b8;
+
+ b10 += b3;
+ b3 = ((b3 << 37) | (b3 >> (64 - 37))) ^ b10;
+
+ b12 += b7;
+ b7 = ((b7 << 20) | (b7 >> (64 - 20))) ^ b12;
+
+ b1 += k0;
+ b0 += b1 + k16;
+ b1 = ((b1 << 24) | (b1 >> (64 - 24))) ^ b0;
+
+ b3 += k2;
+ b2 += b3 + k1;
+ b3 = ((b3 << 13) | (b3 >> (64 - 13))) ^ b2;
+
+ b5 += k4;
+ b4 += b5 + k3;
+ b5 = ((b5 << 8) | (b5 >> (64 - 8))) ^ b4;
+
+ b7 += k6;
+ b6 += b7 + k5;
+ b7 = ((b7 << 47) | (b7 >> (64 - 47))) ^ b6;
+
+ b9 += k8;
+ b8 += b9 + k7;
+ b9 = ((b9 << 8) | (b9 >> (64 - 8))) ^ b8;
+
+ b11 += k10;
+ b10 += b11 + k9;
+ b11 = ((b11 << 17) | (b11 >> (64 - 17))) ^ b10;
+
+ b13 += k12 + t1;
+ b12 += b13 + k11;
+ b13 = ((b13 << 22) | (b13 >> (64 - 22))) ^ b12;
+
+ b15 += k14 + 16;
+ b14 += b15 + k13 + t2;
+ b15 = ((b15 << 37) | (b15 >> (64 - 37))) ^ b14;
+
+ b0 += b9;
+ b9 = ((b9 << 38) | (b9 >> (64 - 38))) ^ b0;
+
+ b2 += b13;
+ b13 = ((b13 << 19) | (b13 >> (64 - 19))) ^ b2;
+
+ b6 += b11;
+ b11 = ((b11 << 10) | (b11 >> (64 - 10))) ^ b6;
+
+ b4 += b15;
+ b15 = ((b15 << 55) | (b15 >> (64 - 55))) ^ b4;
+
+ b10 += b7;
+ b7 = ((b7 << 49) | (b7 >> (64 - 49))) ^ b10;
+
+ b12 += b3;
+ b3 = ((b3 << 18) | (b3 >> (64 - 18))) ^ b12;
+
+ b14 += b5;
+ b5 = ((b5 << 23) | (b5 >> (64 - 23))) ^ b14;
+
+ b8 += b1;
+ b1 = ((b1 << 52) | (b1 >> (64 - 52))) ^ b8;
+
+ b0 += b7;
+ b7 = ((b7 << 33) | (b7 >> (64 - 33))) ^ b0;
+
+ b2 += b5;
+ b5 = ((b5 << 4) | (b5 >> (64 - 4))) ^ b2;
+
+ b4 += b3;
+ b3 = ((b3 << 51) | (b3 >> (64 - 51))) ^ b4;
+
+ b6 += b1;
+ b1 = ((b1 << 13) | (b1 >> (64 - 13))) ^ b6;
+
+ b12 += b15;
+ b15 = ((b15 << 34) | (b15 >> (64 - 34))) ^ b12;
+
+ b14 += b13;
+ b13 = ((b13 << 41) | (b13 >> (64 - 41))) ^ b14;
+
+ b8 += b11;
+ b11 = ((b11 << 59) | (b11 >> (64 - 59))) ^ b8;
+
+ b10 += b9;
+ b9 = ((b9 << 17) | (b9 >> (64 - 17))) ^ b10;
+
+ b0 += b15;
+ b15 = ((b15 << 5) | (b15 >> (64 - 5))) ^ b0;
+
+ b2 += b11;
+ b11 = ((b11 << 20) | (b11 >> (64 - 20))) ^ b2;
+
+ b6 += b13;
+ b13 = ((b13 << 48) | (b13 >> (64 - 48))) ^ b6;
+
+ b4 += b9;
+ b9 = ((b9 << 41) | (b9 >> (64 - 41))) ^ b4;
+
+ b14 += b1;
+ b1 = ((b1 << 47) | (b1 >> (64 - 47))) ^ b14;
+
+ b8 += b5;
+ b5 = ((b5 << 28) | (b5 >> (64 - 28))) ^ b8;
+
+ b10 += b3;
+ b3 = ((b3 << 16) | (b3 >> (64 - 16))) ^ b10;
+
+ b12 += b7;
+ b7 = ((b7 << 25) | (b7 >> (64 - 25))) ^ b12;
+
+ b1 += k1;
+ b0 += b1 + k0;
+ b1 = ((b1 << 41) | (b1 >> (64 - 41))) ^ b0;
+
+ b3 += k3;
+ b2 += b3 + k2;
+ b3 = ((b3 << 9) | (b3 >> (64 - 9))) ^ b2;
+
+ b5 += k5;
+ b4 += b5 + k4;
+ b5 = ((b5 << 37) | (b5 >> (64 - 37))) ^ b4;
+
+ b7 += k7;
+ b6 += b7 + k6;
+ b7 = ((b7 << 31) | (b7 >> (64 - 31))) ^ b6;
+
+ b9 += k9;
+ b8 += b9 + k8;
+ b9 = ((b9 << 12) | (b9 >> (64 - 12))) ^ b8;
+
+ b11 += k11;
+ b10 += b11 + k10;
+ b11 = ((b11 << 47) | (b11 >> (64 - 47))) ^ b10;
+
+ b13 += k13 + t2;
+ b12 += b13 + k12;
+ b13 = ((b13 << 44) | (b13 >> (64 - 44))) ^ b12;
+
+ b15 += k15 + 17;
+ b14 += b15 + k14 + t0;
+ b15 = ((b15 << 30) | (b15 >> (64 - 30))) ^ b14;
+
+ b0 += b9;
+ b9 = ((b9 << 16) | (b9 >> (64 - 16))) ^ b0;
+
+ b2 += b13;
+ b13 = ((b13 << 34) | (b13 >> (64 - 34))) ^ b2;
+
+ b6 += b11;
+ b11 = ((b11 << 56) | (b11 >> (64 - 56))) ^ b6;
+
+ b4 += b15;
+ b15 = ((b15 << 51) | (b15 >> (64 - 51))) ^ b4;
+
+ b10 += b7;
+ b7 = ((b7 << 4) | (b7 >> (64 - 4))) ^ b10;
+
+ b12 += b3;
+ b3 = ((b3 << 53) | (b3 >> (64 - 53))) ^ b12;
+
+ b14 += b5;
+ b5 = ((b5 << 42) | (b5 >> (64 - 42))) ^ b14;
+
+ b8 += b1;
+ b1 = ((b1 << 41) | (b1 >> (64 - 41))) ^ b8;
+
+ b0 += b7;
+ b7 = ((b7 << 31) | (b7 >> (64 - 31))) ^ b0;
+
+ b2 += b5;
+ b5 = ((b5 << 44) | (b5 >> (64 - 44))) ^ b2;
+
+ b4 += b3;
+ b3 = ((b3 << 47) | (b3 >> (64 - 47))) ^ b4;
+
+ b6 += b1;
+ b1 = ((b1 << 46) | (b1 >> (64 - 46))) ^ b6;
+
+ b12 += b15;
+ b15 = ((b15 << 19) | (b15 >> (64 - 19))) ^ b12;
+
+ b14 += b13;
+ b13 = ((b13 << 42) | (b13 >> (64 - 42))) ^ b14;
+
+ b8 += b11;
+ b11 = ((b11 << 44) | (b11 >> (64 - 44))) ^ b8;
+
+ b10 += b9;
+ b9 = ((b9 << 25) | (b9 >> (64 - 25))) ^ b10;
+
+ b0 += b15;
+ b15 = ((b15 << 9) | (b15 >> (64 - 9))) ^ b0;
+
+ b2 += b11;
+ b11 = ((b11 << 48) | (b11 >> (64 - 48))) ^ b2;
+
+ b6 += b13;
+ b13 = ((b13 << 35) | (b13 >> (64 - 35))) ^ b6;
+
+ b4 += b9;
+ b9 = ((b9 << 52) | (b9 >> (64 - 52))) ^ b4;
+
+ b14 += b1;
+ b1 = ((b1 << 23) | (b1 >> (64 - 23))) ^ b14;
+
+ b8 += b5;
+ b5 = ((b5 << 31) | (b5 >> (64 - 31))) ^ b8;
+
+ b10 += b3;
+ b3 = ((b3 << 37) | (b3 >> (64 - 37))) ^ b10;
+
+ b12 += b7;
+ b7 = ((b7 << 20) | (b7 >> (64 - 20))) ^ b12;
+
+ b1 += k2;
+ b0 += b1 + k1;
+ b1 = ((b1 << 24) | (b1 >> (64 - 24))) ^ b0;
+
+ b3 += k4;
+ b2 += b3 + k3;
+ b3 = ((b3 << 13) | (b3 >> (64 - 13))) ^ b2;
+
+ b5 += k6;
+ b4 += b5 + k5;
+ b5 = ((b5 << 8) | (b5 >> (64 - 8))) ^ b4;
+
+ b7 += k8;
+ b6 += b7 + k7;
+ b7 = ((b7 << 47) | (b7 >> (64 - 47))) ^ b6;
+
+ b9 += k10;
+ b8 += b9 + k9;
+ b9 = ((b9 << 8) | (b9 >> (64 - 8))) ^ b8;
+
+ b11 += k12;
+ b10 += b11 + k11;
+ b11 = ((b11 << 17) | (b11 >> (64 - 17))) ^ b10;
+
+ b13 += k14 + t0;
+ b12 += b13 + k13;
+ b13 = ((b13 << 22) | (b13 >> (64 - 22))) ^ b12;
+
+ b15 += k16 + 18;
+ b14 += b15 + k15 + t1;
+ b15 = ((b15 << 37) | (b15 >> (64 - 37))) ^ b14;
+
+ b0 += b9;
+ b9 = ((b9 << 38) | (b9 >> (64 - 38))) ^ b0;
+
+ b2 += b13;
+ b13 = ((b13 << 19) | (b13 >> (64 - 19))) ^ b2;
+
+ b6 += b11;
+ b11 = ((b11 << 10) | (b11 >> (64 - 10))) ^ b6;
+
+ b4 += b15;
+ b15 = ((b15 << 55) | (b15 >> (64 - 55))) ^ b4;
+
+ b10 += b7;
+ b7 = ((b7 << 49) | (b7 >> (64 - 49))) ^ b10;
+
+ b12 += b3;
+ b3 = ((b3 << 18) | (b3 >> (64 - 18))) ^ b12;
+
+ b14 += b5;
+ b5 = ((b5 << 23) | (b5 >> (64 - 23))) ^ b14;
+
+ b8 += b1;
+ b1 = ((b1 << 52) | (b1 >> (64 - 52))) ^ b8;
+
+ b0 += b7;
+ b7 = ((b7 << 33) | (b7 >> (64 - 33))) ^ b0;
+
+ b2 += b5;
+ b5 = ((b5 << 4) | (b5 >> (64 - 4))) ^ b2;
+
+ b4 += b3;
+ b3 = ((b3 << 51) | (b3 >> (64 - 51))) ^ b4;
+
+ b6 += b1;
+ b1 = ((b1 << 13) | (b1 >> (64 - 13))) ^ b6;
+
+ b12 += b15;
+ b15 = ((b15 << 34) | (b15 >> (64 - 34))) ^ b12;
+
+ b14 += b13;
+ b13 = ((b13 << 41) | (b13 >> (64 - 41))) ^ b14;
+
+ b8 += b11;
+ b11 = ((b11 << 59) | (b11 >> (64 - 59))) ^ b8;
+
+ b10 += b9;
+ b9 = ((b9 << 17) | (b9 >> (64 - 17))) ^ b10;
+
+ b0 += b15;
+ b15 = ((b15 << 5) | (b15 >> (64 - 5))) ^ b0;
+
+ b2 += b11;
+ b11 = ((b11 << 20) | (b11 >> (64 - 20))) ^ b2;
+
+ b6 += b13;
+ b13 = ((b13 << 48) | (b13 >> (64 - 48))) ^ b6;
+
+ b4 += b9;
+ b9 = ((b9 << 41) | (b9 >> (64 - 41))) ^ b4;
+
+ b14 += b1;
+ b1 = ((b1 << 47) | (b1 >> (64 - 47))) ^ b14;
+
+ b8 += b5;
+ b5 = ((b5 << 28) | (b5 >> (64 - 28))) ^ b8;
+
+ b10 += b3;
+ b3 = ((b3 << 16) | (b3 >> (64 - 16))) ^ b10;
+
+ b12 += b7;
+ b7 = ((b7 << 25) | (b7 >> (64 - 25))) ^ b12;
+
+ b1 += k3;
+ b0 += b1 + k2;
+ b1 = ((b1 << 41) | (b1 >> (64 - 41))) ^ b0;
+
+ b3 += k5;
+ b2 += b3 + k4;
+ b3 = ((b3 << 9) | (b3 >> (64 - 9))) ^ b2;
+
+ b5 += k7;
+ b4 += b5 + k6;
+ b5 = ((b5 << 37) | (b5 >> (64 - 37))) ^ b4;
+
+ b7 += k9;
+ b6 += b7 + k8;
+ b7 = ((b7 << 31) | (b7 >> (64 - 31))) ^ b6;
+
+ b9 += k11;
+ b8 += b9 + k10;
+ b9 = ((b9 << 12) | (b9 >> (64 - 12))) ^ b8;
+
+ b11 += k13;
+ b10 += b11 + k12;
+ b11 = ((b11 << 47) | (b11 >> (64 - 47))) ^ b10;
+
+ b13 += k15 + t1;
+ b12 += b13 + k14;
+ b13 = ((b13 << 44) | (b13 >> (64 - 44))) ^ b12;
+
+ b15 += k0 + 19;
+ b14 += b15 + k16 + t2;
+ b15 = ((b15 << 30) | (b15 >> (64 - 30))) ^ b14;
+
+ b0 += b9;
+ b9 = ((b9 << 16) | (b9 >> (64 - 16))) ^ b0;
+
+ b2 += b13;
+ b13 = ((b13 << 34) | (b13 >> (64 - 34))) ^ b2;
+
+ b6 += b11;
+ b11 = ((b11 << 56) | (b11 >> (64 - 56))) ^ b6;
+
+ b4 += b15;
+ b15 = ((b15 << 51) | (b15 >> (64 - 51))) ^ b4;
+
+ b10 += b7;
+ b7 = ((b7 << 4) | (b7 >> (64 - 4))) ^ b10;
+
+ b12 += b3;
+ b3 = ((b3 << 53) | (b3 >> (64 - 53))) ^ b12;
+
+ b14 += b5;
+ b5 = ((b5 << 42) | (b5 >> (64 - 42))) ^ b14;
+
+ b8 += b1;
+ b1 = ((b1 << 41) | (b1 >> (64 - 41))) ^ b8;
+
+ b0 += b7;
+ b7 = ((b7 << 31) | (b7 >> (64 - 31))) ^ b0;
+
+ b2 += b5;
+ b5 = ((b5 << 44) | (b5 >> (64 - 44))) ^ b2;
+
+ b4 += b3;
+ b3 = ((b3 << 47) | (b3 >> (64 - 47))) ^ b4;
+
+ b6 += b1;
+ b1 = ((b1 << 46) | (b1 >> (64 - 46))) ^ b6;
+
+ b12 += b15;
+ b15 = ((b15 << 19) | (b15 >> (64 - 19))) ^ b12;
+
+ b14 += b13;
+ b13 = ((b13 << 42) | (b13 >> (64 - 42))) ^ b14;
+
+ b8 += b11;
+ b11 = ((b11 << 44) | (b11 >> (64 - 44))) ^ b8;
+
+ b10 += b9;
+ b9 = ((b9 << 25) | (b9 >> (64 - 25))) ^ b10;
+
+ b0 += b15;
+ b15 = ((b15 << 9) | (b15 >> (64 - 9))) ^ b0;
+
+ b2 += b11;
+ b11 = ((b11 << 48) | (b11 >> (64 - 48))) ^ b2;
+
+ b6 += b13;
+ b13 = ((b13 << 35) | (b13 >> (64 - 35))) ^ b6;
+
+ b4 += b9;
+ b9 = ((b9 << 52) | (b9 >> (64 - 52))) ^ b4;
+
+ b14 += b1;
+ b1 = ((b1 << 23) | (b1 >> (64 - 23))) ^ b14;
+
+ b8 += b5;
+ b5 = ((b5 << 31) | (b5 >> (64 - 31))) ^ b8;
+
+ b10 += b3;
+ b3 = ((b3 << 37) | (b3 >> (64 - 37))) ^ b10;
+
+ b12 += b7;
+ b7 = ((b7 << 20) | (b7 >> (64 - 20))) ^ b12;
+
+ output[0] = b0 + k3;
+ output[1] = b1 + k4;
+ output[2] = b2 + k5;
+ output[3] = b3 + k6;
+ output[4] = b4 + k7;
+ output[5] = b5 + k8;
+ output[6] = b6 + k9;
+ output[7] = b7 + k10;
+ output[8] = b8 + k11;
+ output[9] = b9 + k12;
+ output[10] = b10 + k13;
+ output[11] = b11 + k14;
+ output[12] = b12 + k15;
+ output[13] = b13 + k16 + t2;
+ output[14] = b14 + k0 + t0;
+ output[15] = b15 + k1 + 20;
+}
+
+void threefish_decrypt_1024(struct threefish_key *key_ctx, u64 *input,
+ u64 *output)
+{
+ u64 b0 = input[0], b1 = input[1],
+ b2 = input[2], b3 = input[3],
+ b4 = input[4], b5 = input[5],
+ b6 = input[6], b7 = input[7],
+ b8 = input[8], b9 = input[9],
+ b10 = input[10], b11 = input[11],
+ b12 = input[12], b13 = input[13],
+ b14 = input[14], b15 = input[15];
+ u64 k0 = key_ctx->key[0], k1 = key_ctx->key[1],
+ k2 = key_ctx->key[2], k3 = key_ctx->key[3],
+ k4 = key_ctx->key[4], k5 = key_ctx->key[5],
+ k6 = key_ctx->key[6], k7 = key_ctx->key[7],
+ k8 = key_ctx->key[8], k9 = key_ctx->key[9],
+ k10 = key_ctx->key[10], k11 = key_ctx->key[11],
+ k12 = key_ctx->key[12], k13 = key_ctx->key[13],
+ k14 = key_ctx->key[14], k15 = key_ctx->key[15],
+ k16 = key_ctx->key[16];
+ u64 t0 = key_ctx->tweak[0], t1 = key_ctx->tweak[1],
+ t2 = key_ctx->tweak[2];
+ u64 tmp;
+
+ b0 -= k3;
+ b1 -= k4;
+ b2 -= k5;
+ b3 -= k6;
+ b4 -= k7;
+ b5 -= k8;
+ b6 -= k9;
+ b7 -= k10;
+ b8 -= k11;
+ b9 -= k12;
+ b10 -= k13;
+ b11 -= k14;
+ b12 -= k15;
+ b13 -= k16 + t2;
+ b14 -= k0 + t0;
+ b15 -= k1 + 20;
+ tmp = b7 ^ b12;
+ b7 = (tmp >> 20) | (tmp << (64 - 20));
+ b12 -= b7;
+
+ tmp = b3 ^ b10;
+ b3 = (tmp >> 37) | (tmp << (64 - 37));
+ b10 -= b3;
+
+ tmp = b5 ^ b8;
+ b5 = (tmp >> 31) | (tmp << (64 - 31));
+ b8 -= b5;
+
+ tmp = b1 ^ b14;
+ b1 = (tmp >> 23) | (tmp << (64 - 23));
+ b14 -= b1;
+
+ tmp = b9 ^ b4;
+ b9 = (tmp >> 52) | (tmp << (64 - 52));
+ b4 -= b9;
+
+ tmp = b13 ^ b6;
+ b13 = (tmp >> 35) | (tmp << (64 - 35));
+ b6 -= b13;
+
+ tmp = b11 ^ b2;
+ b11 = (tmp >> 48) | (tmp << (64 - 48));
+ b2 -= b11;
+
+ tmp = b15 ^ b0;
+ b15 = (tmp >> 9) | (tmp << (64 - 9));
+ b0 -= b15;
+
+ tmp = b9 ^ b10;
+ b9 = (tmp >> 25) | (tmp << (64 - 25));
+ b10 -= b9;
+
+ tmp = b11 ^ b8;
+ b11 = (tmp >> 44) | (tmp << (64 - 44));
+ b8 -= b11;
+
+ tmp = b13 ^ b14;
+ b13 = (tmp >> 42) | (tmp << (64 - 42));
+ b14 -= b13;
+
+ tmp = b15 ^ b12;
+ b15 = (tmp >> 19) | (tmp << (64 - 19));
+ b12 -= b15;
+
+ tmp = b1 ^ b6;
+ b1 = (tmp >> 46) | (tmp << (64 - 46));
+ b6 -= b1;
+
+ tmp = b3 ^ b4;
+ b3 = (tmp >> 47) | (tmp << (64 - 47));
+ b4 -= b3;
+
+ tmp = b5 ^ b2;
+ b5 = (tmp >> 44) | (tmp << (64 - 44));
+ b2 -= b5;
+
+ tmp = b7 ^ b0;
+ b7 = (tmp >> 31) | (tmp << (64 - 31));
+ b0 -= b7;
+
+ tmp = b1 ^ b8;
+ b1 = (tmp >> 41) | (tmp << (64 - 41));
+ b8 -= b1;
+
+ tmp = b5 ^ b14;
+ b5 = (tmp >> 42) | (tmp << (64 - 42));
+ b14 -= b5;
+
+ tmp = b3 ^ b12;
+ b3 = (tmp >> 53) | (tmp << (64 - 53));
+ b12 -= b3;
+
+ tmp = b7 ^ b10;
+ b7 = (tmp >> 4) | (tmp << (64 - 4));
+ b10 -= b7;
+
+ tmp = b15 ^ b4;
+ b15 = (tmp >> 51) | (tmp << (64 - 51));
+ b4 -= b15;
+
+ tmp = b11 ^ b6;
+ b11 = (tmp >> 56) | (tmp << (64 - 56));
+ b6 -= b11;
+
+ tmp = b13 ^ b2;
+ b13 = (tmp >> 34) | (tmp << (64 - 34));
+ b2 -= b13;
+
+ tmp = b9 ^ b0;
+ b9 = (tmp >> 16) | (tmp << (64 - 16));
+ b0 -= b9;
+
+ tmp = b15 ^ b14;
+ b15 = (tmp >> 30) | (tmp << (64 - 30));
+ b14 -= b15 + k16 + t2;
+ b15 -= k0 + 19;
+
+ tmp = b13 ^ b12;
+ b13 = (tmp >> 44) | (tmp << (64 - 44));
+ b12 -= b13 + k14;
+ b13 -= k15 + t1;
+
+ tmp = b11 ^ b10;
+ b11 = (tmp >> 47) | (tmp << (64 - 47));
+ b10 -= b11 + k12;
+ b11 -= k13;
+
+ tmp = b9 ^ b8;
+ b9 = (tmp >> 12) | (tmp << (64 - 12));
+ b8 -= b9 + k10;
+ b9 -= k11;
+
+ tmp = b7 ^ b6;
+ b7 = (tmp >> 31) | (tmp << (64 - 31));
+ b6 -= b7 + k8;
+ b7 -= k9;
+
+ tmp = b5 ^ b4;
+ b5 = (tmp >> 37) | (tmp << (64 - 37));
+ b4 -= b5 + k6;
+ b5 -= k7;
+
+ tmp = b3 ^ b2;
+ b3 = (tmp >> 9) | (tmp << (64 - 9));
+ b2 -= b3 + k4;
+ b3 -= k5;
+
+ tmp = b1 ^ b0;
+ b1 = (tmp >> 41) | (tmp << (64 - 41));
+ b0 -= b1 + k2;
+ b1 -= k3;
+
+ tmp = b7 ^ b12;
+ b7 = (tmp >> 25) | (tmp << (64 - 25));
+ b12 -= b7;
+
+ tmp = b3 ^ b10;
+ b3 = (tmp >> 16) | (tmp << (64 - 16));
+ b10 -= b3;
+
+ tmp = b5 ^ b8;
+ b5 = (tmp >> 28) | (tmp << (64 - 28));
+ b8 -= b5;
+
+ tmp = b1 ^ b14;
+ b1 = (tmp >> 47) | (tmp << (64 - 47));
+ b14 -= b1;
+
+ tmp = b9 ^ b4;
+ b9 = (tmp >> 41) | (tmp << (64 - 41));
+ b4 -= b9;
+
+ tmp = b13 ^ b6;
+ b13 = (tmp >> 48) | (tmp << (64 - 48));
+ b6 -= b13;
+
+ tmp = b11 ^ b2;
+ b11 = (tmp >> 20) | (tmp << (64 - 20));
+ b2 -= b11;
+
+ tmp = b15 ^ b0;
+ b15 = (tmp >> 5) | (tmp << (64 - 5));
+ b0 -= b15;
+
+ tmp = b9 ^ b10;
+ b9 = (tmp >> 17) | (tmp << (64 - 17));
+ b10 -= b9;
+
+ tmp = b11 ^ b8;
+ b11 = (tmp >> 59) | (tmp << (64 - 59));
+ b8 -= b11;
+
+ tmp = b13 ^ b14;
+ b13 = (tmp >> 41) | (tmp << (64 - 41));
+ b14 -= b13;
+
+ tmp = b15 ^ b12;
+ b15 = (tmp >> 34) | (tmp << (64 - 34));
+ b12 -= b15;
+
+ tmp = b1 ^ b6;
+ b1 = (tmp >> 13) | (tmp << (64 - 13));
+ b6 -= b1;
+
+ tmp = b3 ^ b4;
+ b3 = (tmp >> 51) | (tmp << (64 - 51));
+ b4 -= b3;
+
+ tmp = b5 ^ b2;
+ b5 = (tmp >> 4) | (tmp << (64 - 4));
+ b2 -= b5;
+
+ tmp = b7 ^ b0;
+ b7 = (tmp >> 33) | (tmp << (64 - 33));
+ b0 -= b7;
+
+ tmp = b1 ^ b8;
+ b1 = (tmp >> 52) | (tmp << (64 - 52));
+ b8 -= b1;
+
+ tmp = b5 ^ b14;
+ b5 = (tmp >> 23) | (tmp << (64 - 23));
+ b14 -= b5;
+
+ tmp = b3 ^ b12;
+ b3 = (tmp >> 18) | (tmp << (64 - 18));
+ b12 -= b3;
+
+ tmp = b7 ^ b10;
+ b7 = (tmp >> 49) | (tmp << (64 - 49));
+ b10 -= b7;
+
+ tmp = b15 ^ b4;
+ b15 = (tmp >> 55) | (tmp << (64 - 55));
+ b4 -= b15;
+
+ tmp = b11 ^ b6;
+ b11 = (tmp >> 10) | (tmp << (64 - 10));
+ b6 -= b11;
+
+ tmp = b13 ^ b2;
+ b13 = (tmp >> 19) | (tmp << (64 - 19));
+ b2 -= b13;
+
+ tmp = b9 ^ b0;
+ b9 = (tmp >> 38) | (tmp << (64 - 38));
+ b0 -= b9;
+
+ tmp = b15 ^ b14;
+ b15 = (tmp >> 37) | (tmp << (64 - 37));
+ b14 -= b15 + k15 + t1;
+ b15 -= k16 + 18;
+
+ tmp = b13 ^ b12;
+ b13 = (tmp >> 22) | (tmp << (64 - 22));
+ b12 -= b13 + k13;
+ b13 -= k14 + t0;
+
+ tmp = b11 ^ b10;
+ b11 = (tmp >> 17) | (tmp << (64 - 17));
+ b10 -= b11 + k11;
+ b11 -= k12;
+
+ tmp = b9 ^ b8;
+ b9 = (tmp >> 8) | (tmp << (64 - 8));
+ b8 -= b9 + k9;
+ b9 -= k10;
+
+ tmp = b7 ^ b6;
+ b7 = (tmp >> 47) | (tmp << (64 - 47));
+ b6 -= b7 + k7;
+ b7 -= k8;
+
+ tmp = b5 ^ b4;
+ b5 = (tmp >> 8) | (tmp << (64 - 8));
+ b4 -= b5 + k5;
+ b5 -= k6;
+
+ tmp = b3 ^ b2;
+ b3 = (tmp >> 13) | (tmp << (64 - 13));
+ b2 -= b3 + k3;
+ b3 -= k4;
+
+ tmp = b1 ^ b0;
+ b1 = (tmp >> 24) | (tmp << (64 - 24));
+ b0 -= b1 + k1;
+ b1 -= k2;
+
+ tmp = b7 ^ b12;
+ b7 = (tmp >> 20) | (tmp << (64 - 20));
+ b12 -= b7;
+
+ tmp = b3 ^ b10;
+ b3 = (tmp >> 37) | (tmp << (64 - 37));
+ b10 -= b3;
+
+ tmp = b5 ^ b8;
+ b5 = (tmp >> 31) | (tmp << (64 - 31));
+ b8 -= b5;
+
+ tmp = b1 ^ b14;
+ b1 = (tmp >> 23) | (tmp << (64 - 23));
+ b14 -= b1;
+
+ tmp = b9 ^ b4;
+ b9 = (tmp >> 52) | (tmp << (64 - 52));
+ b4 -= b9;
+
+ tmp = b13 ^ b6;
+ b13 = (tmp >> 35) | (tmp << (64 - 35));
+ b6 -= b13;
+
+ tmp = b11 ^ b2;
+ b11 = (tmp >> 48) | (tmp << (64 - 48));
+ b2 -= b11;
+
+ tmp = b15 ^ b0;
+ b15 = (tmp >> 9) | (tmp << (64 - 9));
+ b0 -= b15;
+
+ tmp = b9 ^ b10;
+ b9 = (tmp >> 25) | (tmp << (64 - 25));
+ b10 -= b9;
+
+ tmp = b11 ^ b8;
+ b11 = (tmp >> 44) | (tmp << (64 - 44));
+ b8 -= b11;
+
+ tmp = b13 ^ b14;
+ b13 = (tmp >> 42) | (tmp << (64 - 42));
+ b14 -= b13;
+
+ tmp = b15 ^ b12;
+ b15 = (tmp >> 19) | (tmp << (64 - 19));
+ b12 -= b15;
+
+ tmp = b1 ^ b6;
+ b1 = (tmp >> 46) | (tmp << (64 - 46));
+ b6 -= b1;
+
+ tmp = b3 ^ b4;
+ b3 = (tmp >> 47) | (tmp << (64 - 47));
+ b4 -= b3;
+
+ tmp = b5 ^ b2;
+ b5 = (tmp >> 44) | (tmp << (64 - 44));
+ b2 -= b5;
+
+ tmp = b7 ^ b0;
+ b7 = (tmp >> 31) | (tmp << (64 - 31));
+ b0 -= b7;
+
+ tmp = b1 ^ b8;
+ b1 = (tmp >> 41) | (tmp << (64 - 41));
+ b8 -= b1;
+
+ tmp = b5 ^ b14;
+ b5 = (tmp >> 42) | (tmp << (64 - 42));
+ b14 -= b5;
+
+ tmp = b3 ^ b12;
+ b3 = (tmp >> 53) | (tmp << (64 - 53));
+ b12 -= b3;
+
+ tmp = b7 ^ b10;
+ b7 = (tmp >> 4) | (tmp << (64 - 4));
+ b10 -= b7;
+
+ tmp = b15 ^ b4;
+ b15 = (tmp >> 51) | (tmp << (64 - 51));
+ b4 -= b15;
+
+ tmp = b11 ^ b6;
+ b11 = (tmp >> 56) | (tmp << (64 - 56));
+ b6 -= b11;
+
+ tmp = b13 ^ b2;
+ b13 = (tmp >> 34) | (tmp << (64 - 34));
+ b2 -= b13;
+
+ tmp = b9 ^ b0;
+ b9 = (tmp >> 16) | (tmp << (64 - 16));
+ b0 -= b9;
+
+ tmp = b15 ^ b14;
+ b15 = (tmp >> 30) | (tmp << (64 - 30));
+ b14 -= b15 + k14 + t0;
+ b15 -= k15 + 17;
+
+ tmp = b13 ^ b12;
+ b13 = (tmp >> 44) | (tmp << (64 - 44));
+ b12 -= b13 + k12;
+ b13 -= k13 + t2;
+
+ tmp = b11 ^ b10;
+ b11 = (tmp >> 47) | (tmp << (64 - 47));
+ b10 -= b11 + k10;
+ b11 -= k11;
+
+ tmp = b9 ^ b8;
+ b9 = (tmp >> 12) | (tmp << (64 - 12));
+ b8 -= b9 + k8;
+ b9 -= k9;
+
+ tmp = b7 ^ b6;
+ b7 = (tmp >> 31) | (tmp << (64 - 31));
+ b6 -= b7 + k6;
+ b7 -= k7;
+
+ tmp = b5 ^ b4;
+ b5 = (tmp >> 37) | (tmp << (64 - 37));
+ b4 -= b5 + k4;
+ b5 -= k5;
+
+ tmp = b3 ^ b2;
+ b3 = (tmp >> 9) | (tmp << (64 - 9));
+ b2 -= b3 + k2;
+ b3 -= k3;
+
+ tmp = b1 ^ b0;
+ b1 = (tmp >> 41) | (tmp << (64 - 41));
+ b0 -= b1 + k0;
+ b1 -= k1;
+
+ tmp = b7 ^ b12;
+ b7 = (tmp >> 25) | (tmp << (64 - 25));
+ b12 -= b7;
+
+ tmp = b3 ^ b10;
+ b3 = (tmp >> 16) | (tmp << (64 - 16));
+ b10 -= b3;
+
+ tmp = b5 ^ b8;
+ b5 = (tmp >> 28) | (tmp << (64 - 28));
+ b8 -= b5;
+
+ tmp = b1 ^ b14;
+ b1 = (tmp >> 47) | (tmp << (64 - 47));
+ b14 -= b1;
+
+ tmp = b9 ^ b4;
+ b9 = (tmp >> 41) | (tmp << (64 - 41));
+ b4 -= b9;
+
+ tmp = b13 ^ b6;
+ b13 = (tmp >> 48) | (tmp << (64 - 48));
+ b6 -= b13;
+
+ tmp = b11 ^ b2;
+ b11 = (tmp >> 20) | (tmp << (64 - 20));
+ b2 -= b11;
+
+ tmp = b15 ^ b0;
+ b15 = (tmp >> 5) | (tmp << (64 - 5));
+ b0 -= b15;
+
+ tmp = b9 ^ b10;
+ b9 = (tmp >> 17) | (tmp << (64 - 17));
+ b10 -= b9;
+
+ tmp = b11 ^ b8;
+ b11 = (tmp >> 59) | (tmp << (64 - 59));
+ b8 -= b11;
+
+ tmp = b13 ^ b14;
+ b13 = (tmp >> 41) | (tmp << (64 - 41));
+ b14 -= b13;
+
+ tmp = b15 ^ b12;
+ b15 = (tmp >> 34) | (tmp << (64 - 34));
+ b12 -= b15;
+
+ tmp = b1 ^ b6;
+ b1 = (tmp >> 13) | (tmp << (64 - 13));
+ b6 -= b1;
+
+ tmp = b3 ^ b4;
+ b3 = (tmp >> 51) | (tmp << (64 - 51));
+ b4 -= b3;
+
+ tmp = b5 ^ b2;
+ b5 = (tmp >> 4) | (tmp << (64 - 4));
+ b2 -= b5;
+
+ tmp = b7 ^ b0;
+ b7 = (tmp >> 33) | (tmp << (64 - 33));
+ b0 -= b7;
+
+ tmp = b1 ^ b8;
+ b1 = (tmp >> 52) | (tmp << (64 - 52));
+ b8 -= b1;
+
+ tmp = b5 ^ b14;
+ b5 = (tmp >> 23) | (tmp << (64 - 23));
+ b14 -= b5;
+
+ tmp = b3 ^ b12;
+ b3 = (tmp >> 18) | (tmp << (64 - 18));
+ b12 -= b3;
+
+ tmp = b7 ^ b10;
+ b7 = (tmp >> 49) | (tmp << (64 - 49));
+ b10 -= b7;
+
+ tmp = b15 ^ b4;
+ b15 = (tmp >> 55) | (tmp << (64 - 55));
+ b4 -= b15;
+
+ tmp = b11 ^ b6;
+ b11 = (tmp >> 10) | (tmp << (64 - 10));
+ b6 -= b11;
+
+ tmp = b13 ^ b2;
+ b13 = (tmp >> 19) | (tmp << (64 - 19));
+ b2 -= b13;
+
+ tmp = b9 ^ b0;
+ b9 = (tmp >> 38) | (tmp << (64 - 38));
+ b0 -= b9;
+
+ tmp = b15 ^ b14;
+ b15 = (tmp >> 37) | (tmp << (64 - 37));
+ b14 -= b15 + k13 + t2;
+ b15 -= k14 + 16;
+
+ tmp = b13 ^ b12;
+ b13 = (tmp >> 22) | (tmp << (64 - 22));
+ b12 -= b13 + k11;
+ b13 -= k12 + t1;
+
+ tmp = b11 ^ b10;
+ b11 = (tmp >> 17) | (tmp << (64 - 17));
+ b10 -= b11 + k9;
+ b11 -= k10;
+
+ tmp = b9 ^ b8;
+ b9 = (tmp >> 8) | (tmp << (64 - 8));
+ b8 -= b9 + k7;
+ b9 -= k8;
+
+ tmp = b7 ^ b6;
+ b7 = (tmp >> 47) | (tmp << (64 - 47));
+ b6 -= b7 + k5;
+ b7 -= k6;
+
+ tmp = b5 ^ b4;
+ b5 = (tmp >> 8) | (tmp << (64 - 8));
+ b4 -= b5 + k3;
+ b5 -= k4;
+
+ tmp = b3 ^ b2;
+ b3 = (tmp >> 13) | (tmp << (64 - 13));
+ b2 -= b3 + k1;
+ b3 -= k2;
+
+ tmp = b1 ^ b0;
+ b1 = (tmp >> 24) | (tmp << (64 - 24));
+ b0 -= b1 + k16;
+ b1 -= k0;
+
+ tmp = b7 ^ b12;
+ b7 = (tmp >> 20) | (tmp << (64 - 20));
+ b12 -= b7;
+
+ tmp = b3 ^ b10;
+ b3 = (tmp >> 37) | (tmp << (64 - 37));
+ b10 -= b3;
+
+ tmp = b5 ^ b8;
+ b5 = (tmp >> 31) | (tmp << (64 - 31));
+ b8 -= b5;
+
+ tmp = b1 ^ b14;
+ b1 = (tmp >> 23) | (tmp << (64 - 23));
+ b14 -= b1;
+
+ tmp = b9 ^ b4;
+ b9 = (tmp >> 52) | (tmp << (64 - 52));
+ b4 -= b9;
+
+ tmp = b13 ^ b6;
+ b13 = (tmp >> 35) | (tmp << (64 - 35));
+ b6 -= b13;
+
+ tmp = b11 ^ b2;
+ b11 = (tmp >> 48) | (tmp << (64 - 48));
+ b2 -= b11;
+
+ tmp = b15 ^ b0;
+ b15 = (tmp >> 9) | (tmp << (64 - 9));
+ b0 -= b15;
+
+ tmp = b9 ^ b10;
+ b9 = (tmp >> 25) | (tmp << (64 - 25));
+ b10 -= b9;
+
+ tmp = b11 ^ b8;
+ b11 = (tmp >> 44) | (tmp << (64 - 44));
+ b8 -= b11;
+
+ tmp = b13 ^ b14;
+ b13 = (tmp >> 42) | (tmp << (64 - 42));
+ b14 -= b13;
+
+ tmp = b15 ^ b12;
+ b15 = (tmp >> 19) | (tmp << (64 - 19));
+ b12 -= b15;
+
+ tmp = b1 ^ b6;
+ b1 = (tmp >> 46) | (tmp << (64 - 46));
+ b6 -= b1;
+
+ tmp = b3 ^ b4;
+ b3 = (tmp >> 47) | (tmp << (64 - 47));
+ b4 -= b3;
+
+ tmp = b5 ^ b2;
+ b5 = (tmp >> 44) | (tmp << (64 - 44));
+ b2 -= b5;
+
+ tmp = b7 ^ b0;
+ b7 = (tmp >> 31) | (tmp << (64 - 31));
+ b0 -= b7;
+
+ tmp = b1 ^ b8;
+ b1 = (tmp >> 41) | (tmp << (64 - 41));
+ b8 -= b1;
+
+ tmp = b5 ^ b14;
+ b5 = (tmp >> 42) | (tmp << (64 - 42));
+ b14 -= b5;
+
+ tmp = b3 ^ b12;
+ b3 = (tmp >> 53) | (tmp << (64 - 53));
+ b12 -= b3;
+
+ tmp = b7 ^ b10;
+ b7 = (tmp >> 4) | (tmp << (64 - 4));
+ b10 -= b7;
+
+ tmp = b15 ^ b4;
+ b15 = (tmp >> 51) | (tmp << (64 - 51));
+ b4 -= b15;
+
+ tmp = b11 ^ b6;
+ b11 = (tmp >> 56) | (tmp << (64 - 56));
+ b6 -= b11;
+
+ tmp = b13 ^ b2;
+ b13 = (tmp >> 34) | (tmp << (64 - 34));
+ b2 -= b13;
+
+ tmp = b9 ^ b0;
+ b9 = (tmp >> 16) | (tmp << (64 - 16));
+ b0 -= b9;
+
+ tmp = b15 ^ b14;
+ b15 = (tmp >> 30) | (tmp << (64 - 30));
+ b14 -= b15 + k12 + t1;
+ b15 -= k13 + 15;
+
+ tmp = b13 ^ b12;
+ b13 = (tmp >> 44) | (tmp << (64 - 44));
+ b12 -= b13 + k10;
+ b13 -= k11 + t0;
+
+ tmp = b11 ^ b10;
+ b11 = (tmp >> 47) | (tmp << (64 - 47));
+ b10 -= b11 + k8;
+ b11 -= k9;
+
+ tmp = b9 ^ b8;
+ b9 = (tmp >> 12) | (tmp << (64 - 12));
+ b8 -= b9 + k6;
+ b9 -= k7;
+
+ tmp = b7 ^ b6;
+ b7 = (tmp >> 31) | (tmp << (64 - 31));
+ b6 -= b7 + k4;
+ b7 -= k5;
+
+ tmp = b5 ^ b4;
+ b5 = (tmp >> 37) | (tmp << (64 - 37));
+ b4 -= b5 + k2;
+ b5 -= k3;
+
+ tmp = b3 ^ b2;
+ b3 = (tmp >> 9) | (tmp << (64 - 9));
+ b2 -= b3 + k0;
+ b3 -= k1;
+
+ tmp = b1 ^ b0;
+ b1 = (tmp >> 41) | (tmp << (64 - 41));
+ b0 -= b1 + k15;
+ b1 -= k16;
+
+ tmp = b7 ^ b12;
+ b7 = (tmp >> 25) | (tmp << (64 - 25));
+ b12 -= b7;
+
+ tmp = b3 ^ b10;
+ b3 = (tmp >> 16) | (tmp << (64 - 16));
+ b10 -= b3;
+
+ tmp = b5 ^ b8;
+ b5 = (tmp >> 28) | (tmp << (64 - 28));
+ b8 -= b5;
+
+ tmp = b1 ^ b14;
+ b1 = (tmp >> 47) | (tmp << (64 - 47));
+ b14 -= b1;
+
+ tmp = b9 ^ b4;
+ b9 = (tmp >> 41) | (tmp << (64 - 41));
+ b4 -= b9;
+
+ tmp = b13 ^ b6;
+ b13 = (tmp >> 48) | (tmp << (64 - 48));
+ b6 -= b13;
+
+ tmp = b11 ^ b2;
+ b11 = (tmp >> 20) | (tmp << (64 - 20));
+ b2 -= b11;
+
+ tmp = b15 ^ b0;
+ b15 = (tmp >> 5) | (tmp << (64 - 5));
+ b0 -= b15;
+
+ tmp = b9 ^ b10;
+ b9 = (tmp >> 17) | (tmp << (64 - 17));
+ b10 -= b9;
+
+ tmp = b11 ^ b8;
+ b11 = (tmp >> 59) | (tmp << (64 - 59));
+ b8 -= b11;
+
+ tmp = b13 ^ b14;
+ b13 = (tmp >> 41) | (tmp << (64 - 41));
+ b14 -= b13;
+
+ tmp = b15 ^ b12;
+ b15 = (tmp >> 34) | (tmp << (64 - 34));
+ b12 -= b15;
+
+ tmp = b1 ^ b6;
+ b1 = (tmp >> 13) | (tmp << (64 - 13));
+ b6 -= b1;
+
+ tmp = b3 ^ b4;
+ b3 = (tmp >> 51) | (tmp << (64 - 51));
+ b4 -= b3;
+
+ tmp = b5 ^ b2;
+ b5 = (tmp >> 4) | (tmp << (64 - 4));
+ b2 -= b5;
+
+ tmp = b7 ^ b0;
+ b7 = (tmp >> 33) | (tmp << (64 - 33));
+ b0 -= b7;
+
+ tmp = b1 ^ b8;
+ b1 = (tmp >> 52) | (tmp << (64 - 52));
+ b8 -= b1;
+
+ tmp = b5 ^ b14;
+ b5 = (tmp >> 23) | (tmp << (64 - 23));
+ b14 -= b5;
+
+ tmp = b3 ^ b12;
+ b3 = (tmp >> 18) | (tmp << (64 - 18));
+ b12 -= b3;
+
+ tmp = b7 ^ b10;
+ b7 = (tmp >> 49) | (tmp << (64 - 49));
+ b10 -= b7;
+
+ tmp = b15 ^ b4;
+ b15 = (tmp >> 55) | (tmp << (64 - 55));
+ b4 -= b15;
+
+ tmp = b11 ^ b6;
+ b11 = (tmp >> 10) | (tmp << (64 - 10));
+ b6 -= b11;
+
+ tmp = b13 ^ b2;
+ b13 = (tmp >> 19) | (tmp << (64 - 19));
+ b2 -= b13;
+
+ tmp = b9 ^ b0;
+ b9 = (tmp >> 38) | (tmp << (64 - 38));
+ b0 -= b9;
+
+ tmp = b15 ^ b14;
+ b15 = (tmp >> 37) | (tmp << (64 - 37));
+ b14 -= b15 + k11 + t0;
+ b15 -= k12 + 14;
+
+ tmp = b13 ^ b12;
+ b13 = (tmp >> 22) | (tmp << (64 - 22));
+ b12 -= b13 + k9;
+ b13 -= k10 + t2;
+
+ tmp = b11 ^ b10;
+ b11 = (tmp >> 17) | (tmp << (64 - 17));
+ b10 -= b11 + k7;
+ b11 -= k8;
+
+ tmp = b9 ^ b8;
+ b9 = (tmp >> 8) | (tmp << (64 - 8));
+ b8 -= b9 + k5;
+ b9 -= k6;
+
+ tmp = b7 ^ b6;
+ b7 = (tmp >> 47) | (tmp << (64 - 47));
+ b6 -= b7 + k3;
+ b7 -= k4;
+
+ tmp = b5 ^ b4;
+ b5 = (tmp >> 8) | (tmp << (64 - 8));
+ b4 -= b5 + k1;
+ b5 -= k2;
+
+ tmp = b3 ^ b2;
+ b3 = (tmp >> 13) | (tmp << (64 - 13));
+ b2 -= b3 + k16;
+ b3 -= k0;
+
+ tmp = b1 ^ b0;
+ b1 = (tmp >> 24) | (tmp << (64 - 24));
+ b0 -= b1 + k14;
+ b1 -= k15;
+
+ tmp = b7 ^ b12;
+ b7 = (tmp >> 20) | (tmp << (64 - 20));
+ b12 -= b7;
+
+ tmp = b3 ^ b10;
+ b3 = (tmp >> 37) | (tmp << (64 - 37));
+ b10 -= b3;
+
+ tmp = b5 ^ b8;
+ b5 = (tmp >> 31) | (tmp << (64 - 31));
+ b8 -= b5;
+
+ tmp = b1 ^ b14;
+ b1 = (tmp >> 23) | (tmp << (64 - 23));
+ b14 -= b1;
+
+ tmp = b9 ^ b4;
+ b9 = (tmp >> 52) | (tmp << (64 - 52));
+ b4 -= b9;
+
+ tmp = b13 ^ b6;
+ b13 = (tmp >> 35) | (tmp << (64 - 35));
+ b6 -= b13;
+
+ tmp = b11 ^ b2;
+ b11 = (tmp >> 48) | (tmp << (64 - 48));
+ b2 -= b11;
+
+ tmp = b15 ^ b0;
+ b15 = (tmp >> 9) | (tmp << (64 - 9));
+ b0 -= b15;
+
+ tmp = b9 ^ b10;
+ b9 = (tmp >> 25) | (tmp << (64 - 25));
+ b10 -= b9;
+
+ tmp = b11 ^ b8;
+ b11 = (tmp >> 44) | (tmp << (64 - 44));
+ b8 -= b11;
+
+ tmp = b13 ^ b14;
+ b13 = (tmp >> 42) | (tmp << (64 - 42));
+ b14 -= b13;
+
+ tmp = b15 ^ b12;
+ b15 = (tmp >> 19) | (tmp << (64 - 19));
+ b12 -= b15;
+
+ tmp = b1 ^ b6;
+ b1 = (tmp >> 46) | (tmp << (64 - 46));
+ b6 -= b1;
+
+ tmp = b3 ^ b4;
+ b3 = (tmp >> 47) | (tmp << (64 - 47));
+ b4 -= b3;
+
+ tmp = b5 ^ b2;
+ b5 = (tmp >> 44) | (tmp << (64 - 44));
+ b2 -= b5;
+
+ tmp = b7 ^ b0;
+ b7 = (tmp >> 31) | (tmp << (64 - 31));
+ b0 -= b7;
+
+ tmp = b1 ^ b8;
+ b1 = (tmp >> 41) | (tmp << (64 - 41));
+ b8 -= b1;
+
+ tmp = b5 ^ b14;
+ b5 = (tmp >> 42) | (tmp << (64 - 42));
+ b14 -= b5;
+
+ tmp = b3 ^ b12;
+ b3 = (tmp >> 53) | (tmp << (64 - 53));
+ b12 -= b3;
+
+ tmp = b7 ^ b10;
+ b7 = (tmp >> 4) | (tmp << (64 - 4));
+ b10 -= b7;
+
+ tmp = b15 ^ b4;
+ b15 = (tmp >> 51) | (tmp << (64 - 51));
+ b4 -= b15;
+
+ tmp = b11 ^ b6;
+ b11 = (tmp >> 56) | (tmp << (64 - 56));
+ b6 -= b11;
+
+ tmp = b13 ^ b2;
+ b13 = (tmp >> 34) | (tmp << (64 - 34));
+ b2 -= b13;
+
+ tmp = b9 ^ b0;
+ b9 = (tmp >> 16) | (tmp << (64 - 16));
+ b0 -= b9;
+
+ tmp = b15 ^ b14;
+ b15 = (tmp >> 30) | (tmp << (64 - 30));
+ b14 -= b15 + k10 + t2;
+ b15 -= k11 + 13;
+
+ tmp = b13 ^ b12;
+ b13 = (tmp >> 44) | (tmp << (64 - 44));
+ b12 -= b13 + k8;
+ b13 -= k9 + t1;
+
+ tmp = b11 ^ b10;
+ b11 = (tmp >> 47) | (tmp << (64 - 47));
+ b10 -= b11 + k6;
+ b11 -= k7;
+
+ tmp = b9 ^ b8;
+ b9 = (tmp >> 12) | (tmp << (64 - 12));
+ b8 -= b9 + k4;
+ b9 -= k5;
+
+ tmp = b7 ^ b6;
+ b7 = (tmp >> 31) | (tmp << (64 - 31));
+ b6 -= b7 + k2;
+ b7 -= k3;
+
+ tmp = b5 ^ b4;
+ b5 = (tmp >> 37) | (tmp << (64 - 37));
+ b4 -= b5 + k0;
+ b5 -= k1;
+
+ tmp = b3 ^ b2;
+ b3 = (tmp >> 9) | (tmp << (64 - 9));
+ b2 -= b3 + k15;
+ b3 -= k16;
+
+ tmp = b1 ^ b0;
+ b1 = (tmp >> 41) | (tmp << (64 - 41));
+ b0 -= b1 + k13;
+ b1 -= k14;
+
+ tmp = b7 ^ b12;
+ b7 = (tmp >> 25) | (tmp << (64 - 25));
+ b12 -= b7;
+
+ tmp = b3 ^ b10;
+ b3 = (tmp >> 16) | (tmp << (64 - 16));
+ b10 -= b3;
+
+ tmp = b5 ^ b8;
+ b5 = (tmp >> 28) | (tmp << (64 - 28));
+ b8 -= b5;
+
+ tmp = b1 ^ b14;
+ b1 = (tmp >> 47) | (tmp << (64 - 47));
+ b14 -= b1;
+
+ tmp = b9 ^ b4;
+ b9 = (tmp >> 41) | (tmp << (64 - 41));
+ b4 -= b9;
+
+ tmp = b13 ^ b6;
+ b13 = (tmp >> 48) | (tmp << (64 - 48));
+ b6 -= b13;
+
+ tmp = b11 ^ b2;
+ b11 = (tmp >> 20) | (tmp << (64 - 20));
+ b2 -= b11;
+
+ tmp = b15 ^ b0;
+ b15 = (tmp >> 5) | (tmp << (64 - 5));
+ b0 -= b15;
+
+ tmp = b9 ^ b10;
+ b9 = (tmp >> 17) | (tmp << (64 - 17));
+ b10 -= b9;
+
+ tmp = b11 ^ b8;
+ b11 = (tmp >> 59) | (tmp << (64 - 59));
+ b8 -= b11;
+
+ tmp = b13 ^ b14;
+ b13 = (tmp >> 41) | (tmp << (64 - 41));
+ b14 -= b13;
+
+ tmp = b15 ^ b12;
+ b15 = (tmp >> 34) | (tmp << (64 - 34));
+ b12 -= b15;
+
+ tmp = b1 ^ b6;
+ b1 = (tmp >> 13) | (tmp << (64 - 13));
+ b6 -= b1;
+
+ tmp = b3 ^ b4;
+ b3 = (tmp >> 51) | (tmp << (64 - 51));
+ b4 -= b3;
+
+ tmp = b5 ^ b2;
+ b5 = (tmp >> 4) | (tmp << (64 - 4));
+ b2 -= b5;
+
+ tmp = b7 ^ b0;
+ b7 = (tmp >> 33) | (tmp << (64 - 33));
+ b0 -= b7;
+
+ tmp = b1 ^ b8;
+ b1 = (tmp >> 52) | (tmp << (64 - 52));
+ b8 -= b1;
+
+ tmp = b5 ^ b14;
+ b5 = (tmp >> 23) | (tmp << (64 - 23));
+ b14 -= b5;
+
+ tmp = b3 ^ b12;
+ b3 = (tmp >> 18) | (tmp << (64 - 18));
+ b12 -= b3;
+
+ tmp = b7 ^ b10;
+ b7 = (tmp >> 49) | (tmp << (64 - 49));
+ b10 -= b7;
+
+ tmp = b15 ^ b4;
+ b15 = (tmp >> 55) | (tmp << (64 - 55));
+ b4 -= b15;
+
+ tmp = b11 ^ b6;
+ b11 = (tmp >> 10) | (tmp << (64 - 10));
+ b6 -= b11;
+
+ tmp = b13 ^ b2;
+ b13 = (tmp >> 19) | (tmp << (64 - 19));
+ b2 -= b13;
+
+ tmp = b9 ^ b0;
+ b9 = (tmp >> 38) | (tmp << (64 - 38));
+ b0 -= b9;
+
+ tmp = b15 ^ b14;
+ b15 = (tmp >> 37) | (tmp << (64 - 37));
+ b14 -= b15 + k9 + t1;
+ b15 -= k10 + 12;
+
+ tmp = b13 ^ b12;
+ b13 = (tmp >> 22) | (tmp << (64 - 22));
+ b12 -= b13 + k7;
+ b13 -= k8 + t0;
+
+ tmp = b11 ^ b10;
+ b11 = (tmp >> 17) | (tmp << (64 - 17));
+ b10 -= b11 + k5;
+ b11 -= k6;
+
+ tmp = b9 ^ b8;
+ b9 = (tmp >> 8) | (tmp << (64 - 8));
+ b8 -= b9 + k3;
+ b9 -= k4;
+
+ tmp = b7 ^ b6;
+ b7 = (tmp >> 47) | (tmp << (64 - 47));
+ b6 -= b7 + k1;
+ b7 -= k2;
+
+ tmp = b5 ^ b4;
+ b5 = (tmp >> 8) | (tmp << (64 - 8));
+ b4 -= b5 + k16;
+ b5 -= k0;
+
+ tmp = b3 ^ b2;
+ b3 = (tmp >> 13) | (tmp << (64 - 13));
+ b2 -= b3 + k14;
+ b3 -= k15;
+
+ tmp = b1 ^ b0;
+ b1 = (tmp >> 24) | (tmp << (64 - 24));
+ b0 -= b1 + k12;
+ b1 -= k13;
+
+ tmp = b7 ^ b12;
+ b7 = (tmp >> 20) | (tmp << (64 - 20));
+ b12 -= b7;
+
+ tmp = b3 ^ b10;
+ b3 = (tmp >> 37) | (tmp << (64 - 37));
+ b10 -= b3;
+
+ tmp = b5 ^ b8;
+ b5 = (tmp >> 31) | (tmp << (64 - 31));
+ b8 -= b5;
+
+ tmp = b1 ^ b14;
+ b1 = (tmp >> 23) | (tmp << (64 - 23));
+ b14 -= b1;
+
+ tmp = b9 ^ b4;
+ b9 = (tmp >> 52) | (tmp << (64 - 52));
+ b4 -= b9;
+
+ tmp = b13 ^ b6;
+ b13 = (tmp >> 35) | (tmp << (64 - 35));
+ b6 -= b13;
+
+ tmp = b11 ^ b2;
+ b11 = (tmp >> 48) | (tmp << (64 - 48));
+ b2 -= b11;
+
+ tmp = b15 ^ b0;
+ b15 = (tmp >> 9) | (tmp << (64 - 9));
+ b0 -= b15;
+
+ tmp = b9 ^ b10;
+ b9 = (tmp >> 25) | (tmp << (64 - 25));
+ b10 -= b9;
+
+ tmp = b11 ^ b8;
+ b11 = (tmp >> 44) | (tmp << (64 - 44));
+ b8 -= b11;
+
+ tmp = b13 ^ b14;
+ b13 = (tmp >> 42) | (tmp << (64 - 42));
+ b14 -= b13;
+
+ tmp = b15 ^ b12;
+ b15 = (tmp >> 19) | (tmp << (64 - 19));
+ b12 -= b15;
+
+ tmp = b1 ^ b6;
+ b1 = (tmp >> 46) | (tmp << (64 - 46));
+ b6 -= b1;
+
+ tmp = b3 ^ b4;
+ b3 = (tmp >> 47) | (tmp << (64 - 47));
+ b4 -= b3;
+
+ tmp = b5 ^ b2;
+ b5 = (tmp >> 44) | (tmp << (64 - 44));
+ b2 -= b5;
+
+ tmp = b7 ^ b0;
+ b7 = (tmp >> 31) | (tmp << (64 - 31));
+ b0 -= b7;
+
+ tmp = b1 ^ b8;
+ b1 = (tmp >> 41) | (tmp << (64 - 41));
+ b8 -= b1;
+
+ tmp = b5 ^ b14;
+ b5 = (tmp >> 42) | (tmp << (64 - 42));
+ b14 -= b5;
+
+ tmp = b3 ^ b12;
+ b3 = (tmp >> 53) | (tmp << (64 - 53));
+ b12 -= b3;
+
+ tmp = b7 ^ b10;
+ b7 = (tmp >> 4) | (tmp << (64 - 4));
+ b10 -= b7;
+
+ tmp = b15 ^ b4;
+ b15 = (tmp >> 51) | (tmp << (64 - 51));
+ b4 -= b15;
+
+ tmp = b11 ^ b6;
+ b11 = (tmp >> 56) | (tmp << (64 - 56));
+ b6 -= b11;
+
+ tmp = b13 ^ b2;
+ b13 = (tmp >> 34) | (tmp << (64 - 34));
+ b2 -= b13;
+
+ tmp = b9 ^ b0;
+ b9 = (tmp >> 16) | (tmp << (64 - 16));
+ b0 -= b9;
+
+ tmp = b15 ^ b14;
+ b15 = (tmp >> 30) | (tmp << (64 - 30));
+ b14 -= b15 + k8 + t0;
+ b15 -= k9 + 11;
+
+ tmp = b13 ^ b12;
+ b13 = (tmp >> 44) | (tmp << (64 - 44));
+ b12 -= b13 + k6;
+ b13 -= k7 + t2;
+
+ tmp = b11 ^ b10;
+ b11 = (tmp >> 47) | (tmp << (64 - 47));
+ b10 -= b11 + k4;
+ b11 -= k5;
+
+ tmp = b9 ^ b8;
+ b9 = (tmp >> 12) | (tmp << (64 - 12));
+ b8 -= b9 + k2;
+ b9 -= k3;
+
+ tmp = b7 ^ b6;
+ b7 = (tmp >> 31) | (tmp << (64 - 31));
+ b6 -= b7 + k0;
+ b7 -= k1;
+
+ tmp = b5 ^ b4;
+ b5 = (tmp >> 37) | (tmp << (64 - 37));
+ b4 -= b5 + k15;
+ b5 -= k16;
+
+ tmp = b3 ^ b2;
+ b3 = (tmp >> 9) | (tmp << (64 - 9));
+ b2 -= b3 + k13;
+ b3 -= k14;
+
+ tmp = b1 ^ b0;
+ b1 = (tmp >> 41) | (tmp << (64 - 41));
+ b0 -= b1 + k11;
+ b1 -= k12;
+
+ tmp = b7 ^ b12;
+ b7 = (tmp >> 25) | (tmp << (64 - 25));
+ b12 -= b7;
+
+ tmp = b3 ^ b10;
+ b3 = (tmp >> 16) | (tmp << (64 - 16));
+ b10 -= b3;
+
+ tmp = b5 ^ b8;
+ b5 = (tmp >> 28) | (tmp << (64 - 28));
+ b8 -= b5;
+
+ tmp = b1 ^ b14;
+ b1 = (tmp >> 47) | (tmp << (64 - 47));
+ b14 -= b1;
+
+ tmp = b9 ^ b4;
+ b9 = (tmp >> 41) | (tmp << (64 - 41));
+ b4 -= b9;
+
+ tmp = b13 ^ b6;
+ b13 = (tmp >> 48) | (tmp << (64 - 48));
+ b6 -= b13;
+
+ tmp = b11 ^ b2;
+ b11 = (tmp >> 20) | (tmp << (64 - 20));
+ b2 -= b11;
+
+ tmp = b15 ^ b0;
+ b15 = (tmp >> 5) | (tmp << (64 - 5));
+ b0 -= b15;
+
+ tmp = b9 ^ b10;
+ b9 = (tmp >> 17) | (tmp << (64 - 17));
+ b10 -= b9;
+
+ tmp = b11 ^ b8;
+ b11 = (tmp >> 59) | (tmp << (64 - 59));
+ b8 -= b11;
+
+ tmp = b13 ^ b14;
+ b13 = (tmp >> 41) | (tmp << (64 - 41));
+ b14 -= b13;
+
+ tmp = b15 ^ b12;
+ b15 = (tmp >> 34) | (tmp << (64 - 34));
+ b12 -= b15;
+
+ tmp = b1 ^ b6;
+ b1 = (tmp >> 13) | (tmp << (64 - 13));
+ b6 -= b1;
+
+ tmp = b3 ^ b4;
+ b3 = (tmp >> 51) | (tmp << (64 - 51));
+ b4 -= b3;
+
+ tmp = b5 ^ b2;
+ b5 = (tmp >> 4) | (tmp << (64 - 4));
+ b2 -= b5;
+
+ tmp = b7 ^ b0;
+ b7 = (tmp >> 33) | (tmp << (64 - 33));
+ b0 -= b7;
+
+ tmp = b1 ^ b8;
+ b1 = (tmp >> 52) | (tmp << (64 - 52));
+ b8 -= b1;
+
+ tmp = b5 ^ b14;
+ b5 = (tmp >> 23) | (tmp << (64 - 23));
+ b14 -= b5;
+
+ tmp = b3 ^ b12;
+ b3 = (tmp >> 18) | (tmp << (64 - 18));
+ b12 -= b3;
+
+ tmp = b7 ^ b10;
+ b7 = (tmp >> 49) | (tmp << (64 - 49));
+ b10 -= b7;
+
+ tmp = b15 ^ b4;
+ b15 = (tmp >> 55) | (tmp << (64 - 55));
+ b4 -= b15;
+
+ tmp = b11 ^ b6;
+ b11 = (tmp >> 10) | (tmp << (64 - 10));
+ b6 -= b11;
+
+ tmp = b13 ^ b2;
+ b13 = (tmp >> 19) | (tmp << (64 - 19));
+ b2 -= b13;
+
+ tmp = b9 ^ b0;
+ b9 = (tmp >> 38) | (tmp << (64 - 38));
+ b0 -= b9;
+
+ tmp = b15 ^ b14;
+ b15 = (tmp >> 37) | (tmp << (64 - 37));
+ b14 -= b15 + k7 + t2;
+ b15 -= k8 + 10;
+
+ tmp = b13 ^ b12;
+ b13 = (tmp >> 22) | (tmp << (64 - 22));
+ b12 -= b13 + k5;
+ b13 -= k6 + t1;
+
+ tmp = b11 ^ b10;
+ b11 = (tmp >> 17) | (tmp << (64 - 17));
+ b10 -= b11 + k3;
+ b11 -= k4;
+
+ tmp = b9 ^ b8;
+ b9 = (tmp >> 8) | (tmp << (64 - 8));
+ b8 -= b9 + k1;
+ b9 -= k2;
+
+ tmp = b7 ^ b6;
+ b7 = (tmp >> 47) | (tmp << (64 - 47));
+ b6 -= b7 + k16;
+ b7 -= k0;
+
+ tmp = b5 ^ b4;
+ b5 = (tmp >> 8) | (tmp << (64 - 8));
+ b4 -= b5 + k14;
+ b5 -= k15;
+
+ tmp = b3 ^ b2;
+ b3 = (tmp >> 13) | (tmp << (64 - 13));
+ b2 -= b3 + k12;
+ b3 -= k13;
+
+ tmp = b1 ^ b0;
+ b1 = (tmp >> 24) | (tmp << (64 - 24));
+ b0 -= b1 + k10;
+ b1 -= k11;
+
+ tmp = b7 ^ b12;
+ b7 = (tmp >> 20) | (tmp << (64 - 20));
+ b12 -= b7;
+
+ tmp = b3 ^ b10;
+ b3 = (tmp >> 37) | (tmp << (64 - 37));
+ b10 -= b3;
+
+ tmp = b5 ^ b8;
+ b5 = (tmp >> 31) | (tmp << (64 - 31));
+ b8 -= b5;
+
+ tmp = b1 ^ b14;
+ b1 = (tmp >> 23) | (tmp << (64 - 23));
+ b14 -= b1;
+
+ tmp = b9 ^ b4;
+ b9 = (tmp >> 52) | (tmp << (64 - 52));
+ b4 -= b9;
+
+ tmp = b13 ^ b6;
+ b13 = (tmp >> 35) | (tmp << (64 - 35));
+ b6 -= b13;
+
+ tmp = b11 ^ b2;
+ b11 = (tmp >> 48) | (tmp << (64 - 48));
+ b2 -= b11;
+
+ tmp = b15 ^ b0;
+ b15 = (tmp >> 9) | (tmp << (64 - 9));
+ b0 -= b15;
+
+ tmp = b9 ^ b10;
+ b9 = (tmp >> 25) | (tmp << (64 - 25));
+ b10 -= b9;
+
+ tmp = b11 ^ b8;
+ b11 = (tmp >> 44) | (tmp << (64 - 44));
+ b8 -= b11;
+
+ tmp = b13 ^ b14;
+ b13 = (tmp >> 42) | (tmp << (64 - 42));
+ b14 -= b13;
+
+ tmp = b15 ^ b12;
+ b15 = (tmp >> 19) | (tmp << (64 - 19));
+ b12 -= b15;
+
+ tmp = b1 ^ b6;
+ b1 = (tmp >> 46) | (tmp << (64 - 46));
+ b6 -= b1;
+
+ tmp = b3 ^ b4;
+ b3 = (tmp >> 47) | (tmp << (64 - 47));
+ b4 -= b3;
+
+ tmp = b5 ^ b2;
+ b5 = (tmp >> 44) | (tmp << (64 - 44));
+ b2 -= b5;
+
+ tmp = b7 ^ b0;
+ b7 = (tmp >> 31) | (tmp << (64 - 31));
+ b0 -= b7;
+
+ tmp = b1 ^ b8;
+ b1 = (tmp >> 41) | (tmp << (64 - 41));
+ b8 -= b1;
+
+ tmp = b5 ^ b14;
+ b5 = (tmp >> 42) | (tmp << (64 - 42));
+ b14 -= b5;
+
+ tmp = b3 ^ b12;
+ b3 = (tmp >> 53) | (tmp << (64 - 53));
+ b12 -= b3;
+
+ tmp = b7 ^ b10;
+ b7 = (tmp >> 4) | (tmp << (64 - 4));
+ b10 -= b7;
+
+ tmp = b15 ^ b4;
+ b15 = (tmp >> 51) | (tmp << (64 - 51));
+ b4 -= b15;
+
+ tmp = b11 ^ b6;
+ b11 = (tmp >> 56) | (tmp << (64 - 56));
+ b6 -= b11;
+
+ tmp = b13 ^ b2;
+ b13 = (tmp >> 34) | (tmp << (64 - 34));
+ b2 -= b13;
+
+ tmp = b9 ^ b0;
+ b9 = (tmp >> 16) | (tmp << (64 - 16));
+ b0 -= b9;
+
+ tmp = b15 ^ b14;
+ b15 = (tmp >> 30) | (tmp << (64 - 30));
+ b14 -= b15 + k6 + t1;
+ b15 -= k7 + 9;
+
+ tmp = b13 ^ b12;
+ b13 = (tmp >> 44) | (tmp << (64 - 44));
+ b12 -= b13 + k4;
+ b13 -= k5 + t0;
+
+ tmp = b11 ^ b10;
+ b11 = (tmp >> 47) | (tmp << (64 - 47));
+ b10 -= b11 + k2;
+ b11 -= k3;
+
+ tmp = b9 ^ b8;
+ b9 = (tmp >> 12) | (tmp << (64 - 12));
+ b8 -= b9 + k0;
+ b9 -= k1;
+
+ tmp = b7 ^ b6;
+ b7 = (tmp >> 31) | (tmp << (64 - 31));
+ b6 -= b7 + k15;
+ b7 -= k16;
+
+ tmp = b5 ^ b4;
+ b5 = (tmp >> 37) | (tmp << (64 - 37));
+ b4 -= b5 + k13;
+ b5 -= k14;
+
+ tmp = b3 ^ b2;
+ b3 = (tmp >> 9) | (tmp << (64 - 9));
+ b2 -= b3 + k11;
+ b3 -= k12;
+
+ tmp = b1 ^ b0;
+ b1 = (tmp >> 41) | (tmp << (64 - 41));
+ b0 -= b1 + k9;
+ b1 -= k10;
+
+ tmp = b7 ^ b12;
+ b7 = (tmp >> 25) | (tmp << (64 - 25));
+ b12 -= b7;
+
+ tmp = b3 ^ b10;
+ b3 = (tmp >> 16) | (tmp << (64 - 16));
+ b10 -= b3;
+
+ tmp = b5 ^ b8;
+ b5 = (tmp >> 28) | (tmp << (64 - 28));
+ b8 -= b5;
+
+ tmp = b1 ^ b14;
+ b1 = (tmp >> 47) | (tmp << (64 - 47));
+ b14 -= b1;
+
+ tmp = b9 ^ b4;
+ b9 = (tmp >> 41) | (tmp << (64 - 41));
+ b4 -= b9;
+
+ tmp = b13 ^ b6;
+ b13 = (tmp >> 48) | (tmp << (64 - 48));
+ b6 -= b13;
+
+ tmp = b11 ^ b2;
+ b11 = (tmp >> 20) | (tmp << (64 - 20));
+ b2 -= b11;
+
+ tmp = b15 ^ b0;
+ b15 = (tmp >> 5) | (tmp << (64 - 5));
+ b0 -= b15;
+
+ tmp = b9 ^ b10;
+ b9 = (tmp >> 17) | (tmp << (64 - 17));
+ b10 -= b9;
+
+ tmp = b11 ^ b8;
+ b11 = (tmp >> 59) | (tmp << (64 - 59));
+ b8 -= b11;
+
+ tmp = b13 ^ b14;
+ b13 = (tmp >> 41) | (tmp << (64 - 41));
+ b14 -= b13;
+
+ tmp = b15 ^ b12;
+ b15 = (tmp >> 34) | (tmp << (64 - 34));
+ b12 -= b15;
+
+ tmp = b1 ^ b6;
+ b1 = (tmp >> 13) | (tmp << (64 - 13));
+ b6 -= b1;
+
+ tmp = b3 ^ b4;
+ b3 = (tmp >> 51) | (tmp << (64 - 51));
+ b4 -= b3;
+
+ tmp = b5 ^ b2;
+ b5 = (tmp >> 4) | (tmp << (64 - 4));
+ b2 -= b5;
+
+ tmp = b7 ^ b0;
+ b7 = (tmp >> 33) | (tmp << (64 - 33));
+ b0 -= b7;
+
+ tmp = b1 ^ b8;
+ b1 = (tmp >> 52) | (tmp << (64 - 52));
+ b8 -= b1;
+
+ tmp = b5 ^ b14;
+ b5 = (tmp >> 23) | (tmp << (64 - 23));
+ b14 -= b5;
+
+ tmp = b3 ^ b12;
+ b3 = (tmp >> 18) | (tmp << (64 - 18));
+ b12 -= b3;
+
+ tmp = b7 ^ b10;
+ b7 = (tmp >> 49) | (tmp << (64 - 49));
+ b10 -= b7;
+
+ tmp = b15 ^ b4;
+ b15 = (tmp >> 55) | (tmp << (64 - 55));
+ b4 -= b15;
+
+ tmp = b11 ^ b6;
+ b11 = (tmp >> 10) | (tmp << (64 - 10));
+ b6 -= b11;
+
+ tmp = b13 ^ b2;
+ b13 = (tmp >> 19) | (tmp << (64 - 19));
+ b2 -= b13;
+
+ tmp = b9 ^ b0;
+ b9 = (tmp >> 38) | (tmp << (64 - 38));
+ b0 -= b9;
+
+ tmp = b15 ^ b14;
+ b15 = (tmp >> 37) | (tmp << (64 - 37));
+ b14 -= b15 + k5 + t0;
+ b15 -= k6 + 8;
+
+ tmp = b13 ^ b12;
+ b13 = (tmp >> 22) | (tmp << (64 - 22));
+ b12 -= b13 + k3;
+ b13 -= k4 + t2;
+
+ tmp = b11 ^ b10;
+ b11 = (tmp >> 17) | (tmp << (64 - 17));
+ b10 -= b11 + k1;
+ b11 -= k2;
+
+ tmp = b9 ^ b8;
+ b9 = (tmp >> 8) | (tmp << (64 - 8));
+ b8 -= b9 + k16;
+ b9 -= k0;
+
+ tmp = b7 ^ b6;
+ b7 = (tmp >> 47) | (tmp << (64 - 47));
+ b6 -= b7 + k14;
+ b7 -= k15;
+
+ tmp = b5 ^ b4;
+ b5 = (tmp >> 8) | (tmp << (64 - 8));
+ b4 -= b5 + k12;
+ b5 -= k13;
+
+ tmp = b3 ^ b2;
+ b3 = (tmp >> 13) | (tmp << (64 - 13));
+ b2 -= b3 + k10;
+ b3 -= k11;
+
+ tmp = b1 ^ b0;
+ b1 = (tmp >> 24) | (tmp << (64 - 24));
+ b0 -= b1 + k8;
+ b1 -= k9;
+
+ tmp = b7 ^ b12;
+ b7 = (tmp >> 20) | (tmp << (64 - 20));
+ b12 -= b7;
+
+ tmp = b3 ^ b10;
+ b3 = (tmp >> 37) | (tmp << (64 - 37));
+ b10 -= b3;
+
+ tmp = b5 ^ b8;
+ b5 = (tmp >> 31) | (tmp << (64 - 31));
+ b8 -= b5;
+
+ tmp = b1 ^ b14;
+ b1 = (tmp >> 23) | (tmp << (64 - 23));
+ b14 -= b1;
+
+ tmp = b9 ^ b4;
+ b9 = (tmp >> 52) | (tmp << (64 - 52));
+ b4 -= b9;
+
+ tmp = b13 ^ b6;
+ b13 = (tmp >> 35) | (tmp << (64 - 35));
+ b6 -= b13;
+
+ tmp = b11 ^ b2;
+ b11 = (tmp >> 48) | (tmp << (64 - 48));
+ b2 -= b11;
+
+ tmp = b15 ^ b0;
+ b15 = (tmp >> 9) | (tmp << (64 - 9));
+ b0 -= b15;
+
+ tmp = b9 ^ b10;
+ b9 = (tmp >> 25) | (tmp << (64 - 25));
+ b10 -= b9;
+
+ tmp = b11 ^ b8;
+ b11 = (tmp >> 44) | (tmp << (64 - 44));
+ b8 -= b11;
+
+ tmp = b13 ^ b14;
+ b13 = (tmp >> 42) | (tmp << (64 - 42));
+ b14 -= b13;
+
+ tmp = b15 ^ b12;
+ b15 = (tmp >> 19) | (tmp << (64 - 19));
+ b12 -= b15;
+
+ tmp = b1 ^ b6;
+ b1 = (tmp >> 46) | (tmp << (64 - 46));
+ b6 -= b1;
+
+ tmp = b3 ^ b4;
+ b3 = (tmp >> 47) | (tmp << (64 - 47));
+ b4 -= b3;
+
+ tmp = b5 ^ b2;
+ b5 = (tmp >> 44) | (tmp << (64 - 44));
+ b2 -= b5;
+
+ tmp = b7 ^ b0;
+ b7 = (tmp >> 31) | (tmp << (64 - 31));
+ b0 -= b7;
+
+ tmp = b1 ^ b8;
+ b1 = (tmp >> 41) | (tmp << (64 - 41));
+ b8 -= b1;
+
+ tmp = b5 ^ b14;
+ b5 = (tmp >> 42) | (tmp << (64 - 42));
+ b14 -= b5;
+
+ tmp = b3 ^ b12;
+ b3 = (tmp >> 53) | (tmp << (64 - 53));
+ b12 -= b3;
+
+ tmp = b7 ^ b10;
+ b7 = (tmp >> 4) | (tmp << (64 - 4));
+ b10 -= b7;
+
+ tmp = b15 ^ b4;
+ b15 = (tmp >> 51) | (tmp << (64 - 51));
+ b4 -= b15;
+
+ tmp = b11 ^ b6;
+ b11 = (tmp >> 56) | (tmp << (64 - 56));
+ b6 -= b11;
+
+ tmp = b13 ^ b2;
+ b13 = (tmp >> 34) | (tmp << (64 - 34));
+ b2 -= b13;
+
+ tmp = b9 ^ b0;
+ b9 = (tmp >> 16) | (tmp << (64 - 16));
+ b0 -= b9;
+
+ tmp = b15 ^ b14;
+ b15 = (tmp >> 30) | (tmp << (64 - 30));
+ b14 -= b15 + k4 + t2;
+ b15 -= k5 + 7;
+
+ tmp = b13 ^ b12;
+ b13 = (tmp >> 44) | (tmp << (64 - 44));
+ b12 -= b13 + k2;
+ b13 -= k3 + t1;
+
+ tmp = b11 ^ b10;
+ b11 = (tmp >> 47) | (tmp << (64 - 47));
+ b10 -= b11 + k0;
+ b11 -= k1;
+
+ tmp = b9 ^ b8;
+ b9 = (tmp >> 12) | (tmp << (64 - 12));
+ b8 -= b9 + k15;
+ b9 -= k16;
+
+ tmp = b7 ^ b6;
+ b7 = (tmp >> 31) | (tmp << (64 - 31));
+ b6 -= b7 + k13;
+ b7 -= k14;
+
+ tmp = b5 ^ b4;
+ b5 = (tmp >> 37) | (tmp << (64 - 37));
+ b4 -= b5 + k11;
+ b5 -= k12;
+
+ tmp = b3 ^ b2;
+ b3 = (tmp >> 9) | (tmp << (64 - 9));
+ b2 -= b3 + k9;
+ b3 -= k10;
+
+ tmp = b1 ^ b0;
+ b1 = (tmp >> 41) | (tmp << (64 - 41));
+ b0 -= b1 + k7;
+ b1 -= k8;
+
+ tmp = b7 ^ b12;
+ b7 = (tmp >> 25) | (tmp << (64 - 25));
+ b12 -= b7;
+
+ tmp = b3 ^ b10;
+ b3 = (tmp >> 16) | (tmp << (64 - 16));
+ b10 -= b3;
+
+ tmp = b5 ^ b8;
+ b5 = (tmp >> 28) | (tmp << (64 - 28));
+ b8 -= b5;
+
+ tmp = b1 ^ b14;
+ b1 = (tmp >> 47) | (tmp << (64 - 47));
+ b14 -= b1;
+
+ tmp = b9 ^ b4;
+ b9 = (tmp >> 41) | (tmp << (64 - 41));
+ b4 -= b9;
+
+ tmp = b13 ^ b6;
+ b13 = (tmp >> 48) | (tmp << (64 - 48));
+ b6 -= b13;
+
+ tmp = b11 ^ b2;
+ b11 = (tmp >> 20) | (tmp << (64 - 20));
+ b2 -= b11;
+
+ tmp = b15 ^ b0;
+ b15 = (tmp >> 5) | (tmp << (64 - 5));
+ b0 -= b15;
+
+ tmp = b9 ^ b10;
+ b9 = (tmp >> 17) | (tmp << (64 - 17));
+ b10 -= b9;
+
+ tmp = b11 ^ b8;
+ b11 = (tmp >> 59) | (tmp << (64 - 59));
+ b8 -= b11;
+
+ tmp = b13 ^ b14;
+ b13 = (tmp >> 41) | (tmp << (64 - 41));
+ b14 -= b13;
+
+ tmp = b15 ^ b12;
+ b15 = (tmp >> 34) | (tmp << (64 - 34));
+ b12 -= b15;
+
+ tmp = b1 ^ b6;
+ b1 = (tmp >> 13) | (tmp << (64 - 13));
+ b6 -= b1;
+
+ tmp = b3 ^ b4;
+ b3 = (tmp >> 51) | (tmp << (64 - 51));
+ b4 -= b3;
+
+ tmp = b5 ^ b2;
+ b5 = (tmp >> 4) | (tmp << (64 - 4));
+ b2 -= b5;
+
+ tmp = b7 ^ b0;
+ b7 = (tmp >> 33) | (tmp << (64 - 33));
+ b0 -= b7;
+
+ tmp = b1 ^ b8;
+ b1 = (tmp >> 52) | (tmp << (64 - 52));
+ b8 -= b1;
+
+ tmp = b5 ^ b14;
+ b5 = (tmp >> 23) | (tmp << (64 - 23));
+ b14 -= b5;
+
+ tmp = b3 ^ b12;
+ b3 = (tmp >> 18) | (tmp << (64 - 18));
+ b12 -= b3;
+
+ tmp = b7 ^ b10;
+ b7 = (tmp >> 49) | (tmp << (64 - 49));
+ b10 -= b7;
+
+ tmp = b15 ^ b4;
+ b15 = (tmp >> 55) | (tmp << (64 - 55));
+ b4 -= b15;
+
+ tmp = b11 ^ b6;
+ b11 = (tmp >> 10) | (tmp << (64 - 10));
+ b6 -= b11;
+
+ tmp = b13 ^ b2;
+ b13 = (tmp >> 19) | (tmp << (64 - 19));
+ b2 -= b13;
+
+ tmp = b9 ^ b0;
+ b9 = (tmp >> 38) | (tmp << (64 - 38));
+ b0 -= b9;
+
+ tmp = b15 ^ b14;
+ b15 = (tmp >> 37) | (tmp << (64 - 37));
+ b14 -= b15 + k3 + t1;
+ b15 -= k4 + 6;
+
+ tmp = b13 ^ b12;
+ b13 = (tmp >> 22) | (tmp << (64 - 22));
+ b12 -= b13 + k1;
+ b13 -= k2 + t0;
+
+ tmp = b11 ^ b10;
+ b11 = (tmp >> 17) | (tmp << (64 - 17));
+ b10 -= b11 + k16;
+ b11 -= k0;
+
+ tmp = b9 ^ b8;
+ b9 = (tmp >> 8) | (tmp << (64 - 8));
+ b8 -= b9 + k14;
+ b9 -= k15;
+
+ tmp = b7 ^ b6;
+ b7 = (tmp >> 47) | (tmp << (64 - 47));
+ b6 -= b7 + k12;
+ b7 -= k13;
+
+ tmp = b5 ^ b4;
+ b5 = (tmp >> 8) | (tmp << (64 - 8));
+ b4 -= b5 + k10;
+ b5 -= k11;
+
+ tmp = b3 ^ b2;
+ b3 = (tmp >> 13) | (tmp << (64 - 13));
+ b2 -= b3 + k8;
+ b3 -= k9;
+
+ tmp = b1 ^ b0;
+ b1 = (tmp >> 24) | (tmp << (64 - 24));
+ b0 -= b1 + k6;
+ b1 -= k7;
+
+ tmp = b7 ^ b12;
+ b7 = (tmp >> 20) | (tmp << (64 - 20));
+ b12 -= b7;
+
+ tmp = b3 ^ b10;
+ b3 = (tmp >> 37) | (tmp << (64 - 37));
+ b10 -= b3;
+
+ tmp = b5 ^ b8;
+ b5 = (tmp >> 31) | (tmp << (64 - 31));
+ b8 -= b5;
+
+ tmp = b1 ^ b14;
+ b1 = (tmp >> 23) | (tmp << (64 - 23));
+ b14 -= b1;
+
+ tmp = b9 ^ b4;
+ b9 = (tmp >> 52) | (tmp << (64 - 52));
+ b4 -= b9;
+
+ tmp = b13 ^ b6;
+ b13 = (tmp >> 35) | (tmp << (64 - 35));
+ b6 -= b13;
+
+ tmp = b11 ^ b2;
+ b11 = (tmp >> 48) | (tmp << (64 - 48));
+ b2 -= b11;
+
+ tmp = b15 ^ b0;
+ b15 = (tmp >> 9) | (tmp << (64 - 9));
+ b0 -= b15;
+
+ tmp = b9 ^ b10;
+ b9 = (tmp >> 25) | (tmp << (64 - 25));
+ b10 -= b9;
+
+ tmp = b11 ^ b8;
+ b11 = (tmp >> 44) | (tmp << (64 - 44));
+ b8 -= b11;
+
+ tmp = b13 ^ b14;
+ b13 = (tmp >> 42) | (tmp << (64 - 42));
+ b14 -= b13;
+
+ tmp = b15 ^ b12;
+ b15 = (tmp >> 19) | (tmp << (64 - 19));
+ b12 -= b15;
+
+ tmp = b1 ^ b6;
+ b1 = (tmp >> 46) | (tmp << (64 - 46));
+ b6 -= b1;
+
+ tmp = b3 ^ b4;
+ b3 = (tmp >> 47) | (tmp << (64 - 47));
+ b4 -= b3;
+
+ tmp = b5 ^ b2;
+ b5 = (tmp >> 44) | (tmp << (64 - 44));
+ b2 -= b5;
+
+ tmp = b7 ^ b0;
+ b7 = (tmp >> 31) | (tmp << (64 - 31));
+ b0 -= b7;
+
+ tmp = b1 ^ b8;
+ b1 = (tmp >> 41) | (tmp << (64 - 41));
+ b8 -= b1;
+
+ tmp = b5 ^ b14;
+ b5 = (tmp >> 42) | (tmp << (64 - 42));
+ b14 -= b5;
+
+ tmp = b3 ^ b12;
+ b3 = (tmp >> 53) | (tmp << (64 - 53));
+ b12 -= b3;
+
+ tmp = b7 ^ b10;
+ b7 = (tmp >> 4) | (tmp << (64 - 4));
+ b10 -= b7;
+
+ tmp = b15 ^ b4;
+ b15 = (tmp >> 51) | (tmp << (64 - 51));
+ b4 -= b15;
+
+ tmp = b11 ^ b6;
+ b11 = (tmp >> 56) | (tmp << (64 - 56));
+ b6 -= b11;
+
+ tmp = b13 ^ b2;
+ b13 = (tmp >> 34) | (tmp << (64 - 34));
+ b2 -= b13;
+
+ tmp = b9 ^ b0;
+ b9 = (tmp >> 16) | (tmp << (64 - 16));
+ b0 -= b9;
+
+ tmp = b15 ^ b14;
+ b15 = (tmp >> 30) | (tmp << (64 - 30));
+ b14 -= b15 + k2 + t0;
+ b15 -= k3 + 5;
+
+ tmp = b13 ^ b12;
+ b13 = (tmp >> 44) | (tmp << (64 - 44));
+ b12 -= b13 + k0;
+ b13 -= k1 + t2;
+
+ tmp = b11 ^ b10;
+ b11 = (tmp >> 47) | (tmp << (64 - 47));
+ b10 -= b11 + k15;
+ b11 -= k16;
+
+ tmp = b9 ^ b8;
+ b9 = (tmp >> 12) | (tmp << (64 - 12));
+ b8 -= b9 + k13;
+ b9 -= k14;
+
+ tmp = b7 ^ b6;
+ b7 = (tmp >> 31) | (tmp << (64 - 31));
+ b6 -= b7 + k11;
+ b7 -= k12;
+
+ tmp = b5 ^ b4;
+ b5 = (tmp >> 37) | (tmp << (64 - 37));
+ b4 -= b5 + k9;
+ b5 -= k10;
+
+ tmp = b3 ^ b2;
+ b3 = (tmp >> 9) | (tmp << (64 - 9));
+ b2 -= b3 + k7;
+ b3 -= k8;
+
+ tmp = b1 ^ b0;
+ b1 = (tmp >> 41) | (tmp << (64 - 41));
+ b0 -= b1 + k5;
+ b1 -= k6;
+
+ tmp = b7 ^ b12;
+ b7 = (tmp >> 25) | (tmp << (64 - 25));
+ b12 -= b7;
+
+ tmp = b3 ^ b10;
+ b3 = (tmp >> 16) | (tmp << (64 - 16));
+ b10 -= b3;
+
+ tmp = b5 ^ b8;
+ b5 = (tmp >> 28) | (tmp << (64 - 28));
+ b8 -= b5;
+
+ tmp = b1 ^ b14;
+ b1 = (tmp >> 47) | (tmp << (64 - 47));
+ b14 -= b1;
+
+ tmp = b9 ^ b4;
+ b9 = (tmp >> 41) | (tmp << (64 - 41));
+ b4 -= b9;
+
+ tmp = b13 ^ b6;
+ b13 = (tmp >> 48) | (tmp << (64 - 48));
+ b6 -= b13;
+
+ tmp = b11 ^ b2;
+ b11 = (tmp >> 20) | (tmp << (64 - 20));
+ b2 -= b11;
+
+ tmp = b15 ^ b0;
+ b15 = (tmp >> 5) | (tmp << (64 - 5));
+ b0 -= b15;
+
+ tmp = b9 ^ b10;
+ b9 = (tmp >> 17) | (tmp << (64 - 17));
+ b10 -= b9;
+
+ tmp = b11 ^ b8;
+ b11 = (tmp >> 59) | (tmp << (64 - 59));
+ b8 -= b11;
+
+ tmp = b13 ^ b14;
+ b13 = (tmp >> 41) | (tmp << (64 - 41));
+ b14 -= b13;
+
+ tmp = b15 ^ b12;
+ b15 = (tmp >> 34) | (tmp << (64 - 34));
+ b12 -= b15;
+
+ tmp = b1 ^ b6;
+ b1 = (tmp >> 13) | (tmp << (64 - 13));
+ b6 -= b1;
+
+ tmp = b3 ^ b4;
+ b3 = (tmp >> 51) | (tmp << (64 - 51));
+ b4 -= b3;
+
+ tmp = b5 ^ b2;
+ b5 = (tmp >> 4) | (tmp << (64 - 4));
+ b2 -= b5;
+
+ tmp = b7 ^ b0;
+ b7 = (tmp >> 33) | (tmp << (64 - 33));
+ b0 -= b7;
+
+ tmp = b1 ^ b8;
+ b1 = (tmp >> 52) | (tmp << (64 - 52));
+ b8 -= b1;
+
+ tmp = b5 ^ b14;
+ b5 = (tmp >> 23) | (tmp << (64 - 23));
+ b14 -= b5;
+
+ tmp = b3 ^ b12;
+ b3 = (tmp >> 18) | (tmp << (64 - 18));
+ b12 -= b3;
+
+ tmp = b7 ^ b10;
+ b7 = (tmp >> 49) | (tmp << (64 - 49));
+ b10 -= b7;
+
+ tmp = b15 ^ b4;
+ b15 = (tmp >> 55) | (tmp << (64 - 55));
+ b4 -= b15;
+
+ tmp = b11 ^ b6;
+ b11 = (tmp >> 10) | (tmp << (64 - 10));
+ b6 -= b11;
+
+ tmp = b13 ^ b2;
+ b13 = (tmp >> 19) | (tmp << (64 - 19));
+ b2 -= b13;
+
+ tmp = b9 ^ b0;
+ b9 = (tmp >> 38) | (tmp << (64 - 38));
+ b0 -= b9;
+
+ tmp = b15 ^ b14;
+ b15 = (tmp >> 37) | (tmp << (64 - 37));
+ b14 -= b15 + k1 + t2;
+ b15 -= k2 + 4;
+
+ tmp = b13 ^ b12;
+ b13 = (tmp >> 22) | (tmp << (64 - 22));
+ b12 -= b13 + k16;
+ b13 -= k0 + t1;
+
+ tmp = b11 ^ b10;
+ b11 = (tmp >> 17) | (tmp << (64 - 17));
+ b10 -= b11 + k14;
+ b11 -= k15;
+
+ tmp = b9 ^ b8;
+ b9 = (tmp >> 8) | (tmp << (64 - 8));
+ b8 -= b9 + k12;
+ b9 -= k13;
+
+ tmp = b7 ^ b6;
+ b7 = (tmp >> 47) | (tmp << (64 - 47));
+ b6 -= b7 + k10;
+ b7 -= k11;
+
+ tmp = b5 ^ b4;
+ b5 = (tmp >> 8) | (tmp << (64 - 8));
+ b4 -= b5 + k8;
+ b5 -= k9;
+
+ tmp = b3 ^ b2;
+ b3 = (tmp >> 13) | (tmp << (64 - 13));
+ b2 -= b3 + k6;
+ b3 -= k7;
+
+ tmp = b1 ^ b0;
+ b1 = (tmp >> 24) | (tmp << (64 - 24));
+ b0 -= b1 + k4;
+ b1 -= k5;
+
+ tmp = b7 ^ b12;
+ b7 = (tmp >> 20) | (tmp << (64 - 20));
+ b12 -= b7;
+
+ tmp = b3 ^ b10;
+ b3 = (tmp >> 37) | (tmp << (64 - 37));
+ b10 -= b3;
+
+ tmp = b5 ^ b8;
+ b5 = (tmp >> 31) | (tmp << (64 - 31));
+ b8 -= b5;
+
+ tmp = b1 ^ b14;
+ b1 = (tmp >> 23) | (tmp << (64 - 23));
+ b14 -= b1;
+
+ tmp = b9 ^ b4;
+ b9 = (tmp >> 52) | (tmp << (64 - 52));
+ b4 -= b9;
+
+ tmp = b13 ^ b6;
+ b13 = (tmp >> 35) | (tmp << (64 - 35));
+ b6 -= b13;
+
+ tmp = b11 ^ b2;
+ b11 = (tmp >> 48) | (tmp << (64 - 48));
+ b2 -= b11;
+
+ tmp = b15 ^ b0;
+ b15 = (tmp >> 9) | (tmp << (64 - 9));
+ b0 -= b15;
+
+ tmp = b9 ^ b10;
+ b9 = (tmp >> 25) | (tmp << (64 - 25));
+ b10 -= b9;
+
+ tmp = b11 ^ b8;
+ b11 = (tmp >> 44) | (tmp << (64 - 44));
+ b8 -= b11;
+
+ tmp = b13 ^ b14;
+ b13 = (tmp >> 42) | (tmp << (64 - 42));
+ b14 -= b13;
+
+ tmp = b15 ^ b12;
+ b15 = (tmp >> 19) | (tmp << (64 - 19));
+ b12 -= b15;
+
+ tmp = b1 ^ b6;
+ b1 = (tmp >> 46) | (tmp << (64 - 46));
+ b6 -= b1;
+
+ tmp = b3 ^ b4;
+ b3 = (tmp >> 47) | (tmp << (64 - 47));
+ b4 -= b3;
+
+ tmp = b5 ^ b2;
+ b5 = (tmp >> 44) | (tmp << (64 - 44));
+ b2 -= b5;
+
+ tmp = b7 ^ b0;
+ b7 = (tmp >> 31) | (tmp << (64 - 31));
+ b0 -= b7;
+
+ tmp = b1 ^ b8;
+ b1 = (tmp >> 41) | (tmp << (64 - 41));
+ b8 -= b1;
+
+ tmp = b5 ^ b14;
+ b5 = (tmp >> 42) | (tmp << (64 - 42));
+ b14 -= b5;
+
+ tmp = b3 ^ b12;
+ b3 = (tmp >> 53) | (tmp << (64 - 53));
+ b12 -= b3;
+
+ tmp = b7 ^ b10;
+ b7 = (tmp >> 4) | (tmp << (64 - 4));
+ b10 -= b7;
+
+ tmp = b15 ^ b4;
+ b15 = (tmp >> 51) | (tmp << (64 - 51));
+ b4 -= b15;
+
+ tmp = b11 ^ b6;
+ b11 = (tmp >> 56) | (tmp << (64 - 56));
+ b6 -= b11;
+
+ tmp = b13 ^ b2;
+ b13 = (tmp >> 34) | (tmp << (64 - 34));
+ b2 -= b13;
+
+ tmp = b9 ^ b0;
+ b9 = (tmp >> 16) | (tmp << (64 - 16));
+ b0 -= b9;
+
+ tmp = b15 ^ b14;
+ b15 = (tmp >> 30) | (tmp << (64 - 30));
+ b14 -= b15 + k0 + t1;
+ b15 -= k1 + 3;
+
+ tmp = b13 ^ b12;
+ b13 = (tmp >> 44) | (tmp << (64 - 44));
+ b12 -= b13 + k15;
+ b13 -= k16 + t0;
+
+ tmp = b11 ^ b10;
+ b11 = (tmp >> 47) | (tmp << (64 - 47));
+ b10 -= b11 + k13;
+ b11 -= k14;
+
+ tmp = b9 ^ b8;
+ b9 = (tmp >> 12) | (tmp << (64 - 12));
+ b8 -= b9 + k11;
+ b9 -= k12;
+
+ tmp = b7 ^ b6;
+ b7 = (tmp >> 31) | (tmp << (64 - 31));
+ b6 -= b7 + k9;
+ b7 -= k10;
+
+ tmp = b5 ^ b4;
+ b5 = (tmp >> 37) | (tmp << (64 - 37));
+ b4 -= b5 + k7;
+ b5 -= k8;
+
+ tmp = b3 ^ b2;
+ b3 = (tmp >> 9) | (tmp << (64 - 9));
+ b2 -= b3 + k5;
+ b3 -= k6;
+
+ tmp = b1 ^ b0;
+ b1 = (tmp >> 41) | (tmp << (64 - 41));
+ b0 -= b1 + k3;
+ b1 -= k4;
+
+ tmp = b7 ^ b12;
+ b7 = (tmp >> 25) | (tmp << (64 - 25));
+ b12 -= b7;
+
+ tmp = b3 ^ b10;
+ b3 = (tmp >> 16) | (tmp << (64 - 16));
+ b10 -= b3;
+
+ tmp = b5 ^ b8;
+ b5 = (tmp >> 28) | (tmp << (64 - 28));
+ b8 -= b5;
+
+ tmp = b1 ^ b14;
+ b1 = (tmp >> 47) | (tmp << (64 - 47));
+ b14 -= b1;
+
+ tmp = b9 ^ b4;
+ b9 = (tmp >> 41) | (tmp << (64 - 41));
+ b4 -= b9;
+
+ tmp = b13 ^ b6;
+ b13 = (tmp >> 48) | (tmp << (64 - 48));
+ b6 -= b13;
+
+ tmp = b11 ^ b2;
+ b11 = (tmp >> 20) | (tmp << (64 - 20));
+ b2 -= b11;
+
+ tmp = b15 ^ b0;
+ b15 = (tmp >> 5) | (tmp << (64 - 5));
+ b0 -= b15;
+
+ tmp = b9 ^ b10;
+ b9 = (tmp >> 17) | (tmp << (64 - 17));
+ b10 -= b9;
+
+ tmp = b11 ^ b8;
+ b11 = (tmp >> 59) | (tmp << (64 - 59));
+ b8 -= b11;
+
+ tmp = b13 ^ b14;
+ b13 = (tmp >> 41) | (tmp << (64 - 41));
+ b14 -= b13;
+
+ tmp = b15 ^ b12;
+ b15 = (tmp >> 34) | (tmp << (64 - 34));
+ b12 -= b15;
+
+ tmp = b1 ^ b6;
+ b1 = (tmp >> 13) | (tmp << (64 - 13));
+ b6 -= b1;
+
+ tmp = b3 ^ b4;
+ b3 = (tmp >> 51) | (tmp << (64 - 51));
+ b4 -= b3;
+
+ tmp = b5 ^ b2;
+ b5 = (tmp >> 4) | (tmp << (64 - 4));
+ b2 -= b5;
+
+ tmp = b7 ^ b0;
+ b7 = (tmp >> 33) | (tmp << (64 - 33));
+ b0 -= b7;
+
+ tmp = b1 ^ b8;
+ b1 = (tmp >> 52) | (tmp << (64 - 52));
+ b8 -= b1;
+
+ tmp = b5 ^ b14;
+ b5 = (tmp >> 23) | (tmp << (64 - 23));
+ b14 -= b5;
+
+ tmp = b3 ^ b12;
+ b3 = (tmp >> 18) | (tmp << (64 - 18));
+ b12 -= b3;
+
+ tmp = b7 ^ b10;
+ b7 = (tmp >> 49) | (tmp << (64 - 49));
+ b10 -= b7;
+
+ tmp = b15 ^ b4;
+ b15 = (tmp >> 55) | (tmp << (64 - 55));
+ b4 -= b15;
+
+ tmp = b11 ^ b6;
+ b11 = (tmp >> 10) | (tmp << (64 - 10));
+ b6 -= b11;
+
+ tmp = b13 ^ b2;
+ b13 = (tmp >> 19) | (tmp << (64 - 19));
+ b2 -= b13;
+
+ tmp = b9 ^ b0;
+ b9 = (tmp >> 38) | (tmp << (64 - 38));
+ b0 -= b9;
+
+ tmp = b15 ^ b14;
+ b15 = (tmp >> 37) | (tmp << (64 - 37));
+ b14 -= b15 + k16 + t0;
+ b15 -= k0 + 2;
+
+ tmp = b13 ^ b12;
+ b13 = (tmp >> 22) | (tmp << (64 - 22));
+ b12 -= b13 + k14;
+ b13 -= k15 + t2;
+
+ tmp = b11 ^ b10;
+ b11 = (tmp >> 17) | (tmp << (64 - 17));
+ b10 -= b11 + k12;
+ b11 -= k13;
+
+ tmp = b9 ^ b8;
+ b9 = (tmp >> 8) | (tmp << (64 - 8));
+ b8 -= b9 + k10;
+ b9 -= k11;
+
+ tmp = b7 ^ b6;
+ b7 = (tmp >> 47) | (tmp << (64 - 47));
+ b6 -= b7 + k8;
+ b7 -= k9;
+
+ tmp = b5 ^ b4;
+ b5 = (tmp >> 8) | (tmp << (64 - 8));
+ b4 -= b5 + k6;
+ b5 -= k7;
+
+ tmp = b3 ^ b2;
+ b3 = (tmp >> 13) | (tmp << (64 - 13));
+ b2 -= b3 + k4;
+ b3 -= k5;
+
+ tmp = b1 ^ b0;
+ b1 = (tmp >> 24) | (tmp << (64 - 24));
+ b0 -= b1 + k2;
+ b1 -= k3;
+
+ tmp = b7 ^ b12;
+ b7 = (tmp >> 20) | (tmp << (64 - 20));
+ b12 -= b7;
+
+ tmp = b3 ^ b10;
+ b3 = (tmp >> 37) | (tmp << (64 - 37));
+ b10 -= b3;
+
+ tmp = b5 ^ b8;
+ b5 = (tmp >> 31) | (tmp << (64 - 31));
+ b8 -= b5;
+
+ tmp = b1 ^ b14;
+ b1 = (tmp >> 23) | (tmp << (64 - 23));
+ b14 -= b1;
+
+ tmp = b9 ^ b4;
+ b9 = (tmp >> 52) | (tmp << (64 - 52));
+ b4 -= b9;
+
+ tmp = b13 ^ b6;
+ b13 = (tmp >> 35) | (tmp << (64 - 35));
+ b6 -= b13;
+
+ tmp = b11 ^ b2;
+ b11 = (tmp >> 48) | (tmp << (64 - 48));
+ b2 -= b11;
+
+ tmp = b15 ^ b0;
+ b15 = (tmp >> 9) | (tmp << (64 - 9));
+ b0 -= b15;
+
+ tmp = b9 ^ b10;
+ b9 = (tmp >> 25) | (tmp << (64 - 25));
+ b10 -= b9;
+
+ tmp = b11 ^ b8;
+ b11 = (tmp >> 44) | (tmp << (64 - 44));
+ b8 -= b11;
+
+ tmp = b13 ^ b14;
+ b13 = (tmp >> 42) | (tmp << (64 - 42));
+ b14 -= b13;
+
+ tmp = b15 ^ b12;
+ b15 = (tmp >> 19) | (tmp << (64 - 19));
+ b12 -= b15;
+
+ tmp = b1 ^ b6;
+ b1 = (tmp >> 46) | (tmp << (64 - 46));
+ b6 -= b1;
+
+ tmp = b3 ^ b4;
+ b3 = (tmp >> 47) | (tmp << (64 - 47));
+ b4 -= b3;
+
+ tmp = b5 ^ b2;
+ b5 = (tmp >> 44) | (tmp << (64 - 44));
+ b2 -= b5;
+
+ tmp = b7 ^ b0;
+ b7 = (tmp >> 31) | (tmp << (64 - 31));
+ b0 -= b7;
+
+ tmp = b1 ^ b8;
+ b1 = (tmp >> 41) | (tmp << (64 - 41));
+ b8 -= b1;
+
+ tmp = b5 ^ b14;
+ b5 = (tmp >> 42) | (tmp << (64 - 42));
+ b14 -= b5;
+
+ tmp = b3 ^ b12;
+ b3 = (tmp >> 53) | (tmp << (64 - 53));
+ b12 -= b3;
+
+ tmp = b7 ^ b10;
+ b7 = (tmp >> 4) | (tmp << (64 - 4));
+ b10 -= b7;
+
+ tmp = b15 ^ b4;
+ b15 = (tmp >> 51) | (tmp << (64 - 51));
+ b4 -= b15;
+
+ tmp = b11 ^ b6;
+ b11 = (tmp >> 56) | (tmp << (64 - 56));
+ b6 -= b11;
+
+ tmp = b13 ^ b2;
+ b13 = (tmp >> 34) | (tmp << (64 - 34));
+ b2 -= b13;
+
+ tmp = b9 ^ b0;
+ b9 = (tmp >> 16) | (tmp << (64 - 16));
+ b0 -= b9;
+
+ tmp = b15 ^ b14;
+ b15 = (tmp >> 30) | (tmp << (64 - 30));
+ b14 -= b15 + k15 + t2;
+ b15 -= k16 + 1;
+
+ tmp = b13 ^ b12;
+ b13 = (tmp >> 44) | (tmp << (64 - 44));
+ b12 -= b13 + k13;
+ b13 -= k14 + t1;
+
+ tmp = b11 ^ b10;
+ b11 = (tmp >> 47) | (tmp << (64 - 47));
+ b10 -= b11 + k11;
+ b11 -= k12;
+
+ tmp = b9 ^ b8;
+ b9 = (tmp >> 12) | (tmp << (64 - 12));
+ b8 -= b9 + k9;
+ b9 -= k10;
+
+ tmp = b7 ^ b6;
+ b7 = (tmp >> 31) | (tmp << (64 - 31));
+ b6 -= b7 + k7;
+ b7 -= k8;
+
+ tmp = b5 ^ b4;
+ b5 = (tmp >> 37) | (tmp << (64 - 37));
+ b4 -= b5 + k5;
+ b5 -= k6;
+
+ tmp = b3 ^ b2;
+ b3 = (tmp >> 9) | (tmp << (64 - 9));
+ b2 -= b3 + k3;
+ b3 -= k4;
+
+ tmp = b1 ^ b0;
+ b1 = (tmp >> 41) | (tmp << (64 - 41));
+ b0 -= b1 + k1;
+ b1 -= k2;
+
+ tmp = b7 ^ b12;
+ b7 = (tmp >> 25) | (tmp << (64 - 25));
+ b12 -= b7;
+
+ tmp = b3 ^ b10;
+ b3 = (tmp >> 16) | (tmp << (64 - 16));
+ b10 -= b3;
+
+ tmp = b5 ^ b8;
+ b5 = (tmp >> 28) | (tmp << (64 - 28));
+ b8 -= b5;
+
+ tmp = b1 ^ b14;
+ b1 = (tmp >> 47) | (tmp << (64 - 47));
+ b14 -= b1;
+
+ tmp = b9 ^ b4;
+ b9 = (tmp >> 41) | (tmp << (64 - 41));
+ b4 -= b9;
+
+ tmp = b13 ^ b6;
+ b13 = (tmp >> 48) | (tmp << (64 - 48));
+ b6 -= b13;
+
+ tmp = b11 ^ b2;
+ b11 = (tmp >> 20) | (tmp << (64 - 20));
+ b2 -= b11;
+
+ tmp = b15 ^ b0;
+ b15 = (tmp >> 5) | (tmp << (64 - 5));
+ b0 -= b15;
+
+ tmp = b9 ^ b10;
+ b9 = (tmp >> 17) | (tmp << (64 - 17));
+ b10 -= b9;
+
+ tmp = b11 ^ b8;
+ b11 = (tmp >> 59) | (tmp << (64 - 59));
+ b8 -= b11;
+
+ tmp = b13 ^ b14;
+ b13 = (tmp >> 41) | (tmp << (64 - 41));
+ b14 -= b13;
+
+ tmp = b15 ^ b12;
+ b15 = (tmp >> 34) | (tmp << (64 - 34));
+ b12 -= b15;
+
+ tmp = b1 ^ b6;
+ b1 = (tmp >> 13) | (tmp << (64 - 13));
+ b6 -= b1;
+
+ tmp = b3 ^ b4;
+ b3 = (tmp >> 51) | (tmp << (64 - 51));
+ b4 -= b3;
+
+ tmp = b5 ^ b2;
+ b5 = (tmp >> 4) | (tmp << (64 - 4));
+ b2 -= b5;
+
+ tmp = b7 ^ b0;
+ b7 = (tmp >> 33) | (tmp << (64 - 33));
+ b0 -= b7;
+
+ tmp = b1 ^ b8;
+ b1 = (tmp >> 52) | (tmp << (64 - 52));
+ b8 -= b1;
+
+ tmp = b5 ^ b14;
+ b5 = (tmp >> 23) | (tmp << (64 - 23));
+ b14 -= b5;
+
+ tmp = b3 ^ b12;
+ b3 = (tmp >> 18) | (tmp << (64 - 18));
+ b12 -= b3;
+
+ tmp = b7 ^ b10;
+ b7 = (tmp >> 49) | (tmp << (64 - 49));
+ b10 -= b7;
+
+ tmp = b15 ^ b4;
+ b15 = (tmp >> 55) | (tmp << (64 - 55));
+ b4 -= b15;
+
+ tmp = b11 ^ b6;
+ b11 = (tmp >> 10) | (tmp << (64 - 10));
+ b6 -= b11;
+
+ tmp = b13 ^ b2;
+ b13 = (tmp >> 19) | (tmp << (64 - 19));
+ b2 -= b13;
+
+ tmp = b9 ^ b0;
+ b9 = (tmp >> 38) | (tmp << (64 - 38));
+ b0 -= b9;
+
+ tmp = b15 ^ b14;
+ b15 = (tmp >> 37) | (tmp << (64 - 37));
+ b14 -= b15 + k14 + t1;
+ b15 -= k15;
+
+ tmp = b13 ^ b12;
+ b13 = (tmp >> 22) | (tmp << (64 - 22));
+ b12 -= b13 + k12;
+ b13 -= k13 + t0;
+
+ tmp = b11 ^ b10;
+ b11 = (tmp >> 17) | (tmp << (64 - 17));
+ b10 -= b11 + k10;
+ b11 -= k11;
+
+ tmp = b9 ^ b8;
+ b9 = (tmp >> 8) | (tmp << (64 - 8));
+ b8 -= b9 + k8;
+ b9 -= k9;
+
+ tmp = b7 ^ b6;
+ b7 = (tmp >> 47) | (tmp << (64 - 47));
+ b6 -= b7 + k6;
+ b7 -= k7;
+
+ tmp = b5 ^ b4;
+ b5 = (tmp >> 8) | (tmp << (64 - 8));
+ b4 -= b5 + k4;
+ b5 -= k5;
+
+ tmp = b3 ^ b2;
+ b3 = (tmp >> 13) | (tmp << (64 - 13));
+ b2 -= b3 + k2;
+ b3 -= k3;
+
+ tmp = b1 ^ b0;
+ b1 = (tmp >> 24) | (tmp << (64 - 24));
+ b0 -= b1 + k0;
+ b1 -= k1;
+
+ output[15] = b15;
+ output[14] = b14;
+ output[13] = b13;
+ output[12] = b12;
+ output[11] = b11;
+ output[10] = b10;
+ output[9] = b9;
+ output[8] = b8;
+ output[7] = b7;
+ output[6] = b6;
+ output[5] = b5;
+ output[4] = b4;
+ output[3] = b3;
+ output[2] = b2;
+ output[1] = b1;
+ output[0] = b0;
+}
diff --git a/drivers/staging/slicoss/Kconfig b/drivers/staging/slicoss/Kconfig
new file mode 100644
index 000000000..5c2a15b42
--- /dev/null
+++ b/drivers/staging/slicoss/Kconfig
@@ -0,0 +1,14 @@
+config SLICOSS
+ tristate "Alacritech Gigabit IS-NIC support"
+ depends on PCI && X86 && NET
+ default n
+ help
+ This driver supports Alacritech's IS-NIC gigabit ethernet cards.
+
+ This includes the following devices:
+ Mojave cards (single port PCI Gigabit) both copper and fiber
+ Oasis cards (single and dual port PCI-x Gigabit) copper and fiber
+ Kalahari cards (dual and quad port PCI-e Gigabit) copper and fiber
+
+ To compile this driver as a module, choose M here: the module
+ will be called slicoss.
diff --git a/drivers/staging/slicoss/Makefile b/drivers/staging/slicoss/Makefile
new file mode 100644
index 000000000..7bc9e9b9d
--- /dev/null
+++ b/drivers/staging/slicoss/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_SLICOSS) += slicoss.o
diff --git a/drivers/staging/slicoss/README b/drivers/staging/slicoss/README
new file mode 100644
index 000000000..4fa50e73c
--- /dev/null
+++ b/drivers/staging/slicoss/README
@@ -0,0 +1,7 @@
+This driver is supposed to support:
+
+ Mojave cards (single port PCI Gigabit) both copper and fiber
+ Oasis cards (single and dual port PCI-x Gigabit) copper and fiber
+ Kalahari cards (dual and quad port PCI-e Gigabit) copper and fiber
+
+The driver was actually tested on Oasis and Kalahari cards.
diff --git a/drivers/staging/slicoss/TODO b/drivers/staging/slicoss/TODO
new file mode 100644
index 000000000..20cc9abdc
--- /dev/null
+++ b/drivers/staging/slicoss/TODO
@@ -0,0 +1,37 @@
+TODO:
+ - move firmware loading to request_firmware()
+ - remove direct memory access of structures
+ - any remaining sparse and checkpatch.pl warnings
+
+ - use net_device_ops
+ - use dev->stats rather than adapter->stats
+ - don't cast netdev_priv it is already void
+ - GET RID OF MACROS
+ - work on all architectures
+ - without CONFIG_X86_64 confusion
+ - do 64 bit correctly
+ - don't depend on order of union
+ - get rid of ASSERT(), use BUG() instead but only where necessary
+ looks like most aren't really useful
+ - no new SIOCDEVPRIVATE ioctl allowed
+ - don't use module_param for configuring interrupt mitigation
+ use ethtool instead
+ - reorder code to elminate use of forward declarations
+ - don't keep private linked list of drivers.
+ - use PCI_DEVICE()
+ - do ethtool correctly using ethtool_ops
+ - NAPI?
+ - wasted overhead of extra stats
+ - state variables for things that are
+ easily available and shouldn't be kept in card structure, cardnum, ...
+ slotnumber, events, ...
+ - get rid of slic_spinlock wrapper
+ - volatile == bad design => bad code
+ - locking too fine grained, not designed just throw more locks
+ at problem
+
+Please send patches to:
+ Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+and Cc: Lior Dotan <liodot@gmail.com> and Christopher Harrer
+<charrer@alacritech.com> as well as they are also able to test out any
+changes.
diff --git a/drivers/staging/slicoss/slic.h b/drivers/staging/slicoss/slic.h
new file mode 100644
index 000000000..3a5aa882b
--- /dev/null
+++ b/drivers/staging/slicoss/slic.h
@@ -0,0 +1,527 @@
+/**************************************************************************
+ *
+ * Copyright (c) 2000-2002 Alacritech, Inc. All rights reserved.
+ *
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY ALACRITECH, INC. ``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 ALACRITECH, INC. OR
+ * CONTRIBUTORS 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.
+ *
+ * The views and conclusions contained in the software and documentation
+ * are those of the authors and should not be interpreted as representing
+ * official policies, either expressed or implied, of Alacritech, Inc.
+ *
+ **************************************************************************/
+
+/*
+ * FILENAME: slic.h
+ *
+ * This is the base set of header definitions for the SLICOSS driver.
+ */
+#ifndef __SLIC_DRIVER_H__
+#define __SLIC_DRIVER_H__
+
+/* firmware stuff */
+#define OASIS_UCODE_VERS_STRING "1.2"
+#define OASIS_UCODE_VERS_DATE "2006/03/27 15:10:37"
+#define OASIS_UCODE_HOSTIF_ID 3
+
+#define MOJAVE_UCODE_VERS_STRING "1.2"
+#define MOJAVE_UCODE_VERS_DATE "2006/03/27 15:12:22"
+#define MOJAVE_UCODE_HOSTIF_ID 3
+
+#define GB_RCVUCODE_VERS_STRING "1.2"
+#define GB_RCVUCODE_VERS_DATE "2006/03/27 15:12:15"
+static u32 OasisRcvUCodeLen = 512;
+static u32 GBRcvUCodeLen = 512;
+#define SECTION_SIZE 65536
+
+struct slic_spinlock {
+ spinlock_t lock;
+ unsigned long flags;
+};
+
+#define SLIC_RSPQ_PAGES_GB 10
+#define SLIC_RSPQ_BUFSINPAGE (PAGE_SIZE / SLIC_RSPBUF_SIZE)
+
+struct slic_rspqueue {
+ u32 offset;
+ u32 pageindex;
+ u32 num_pages;
+ struct slic_rspbuf *rspbuf;
+ u32 *vaddr[SLIC_RSPQ_PAGES_GB];
+ dma_addr_t paddr[SLIC_RSPQ_PAGES_GB];
+};
+
+#define SLIC_RCVQ_EXPANSION 1
+#define SLIC_RCVQ_ENTRIES (256 * SLIC_RCVQ_EXPANSION)
+#define SLIC_RCVQ_MINENTRIES (SLIC_RCVQ_ENTRIES / 2)
+#define SLIC_RCVQ_MAX_PROCESS_ISR ((SLIC_RCVQ_ENTRIES * 4))
+#define SLIC_RCVQ_RCVBUFSIZE 2048
+#define SLIC_RCVQ_FILLENTRIES (16 * SLIC_RCVQ_EXPANSION)
+#define SLIC_RCVQ_FILLTHRESH (SLIC_RCVQ_ENTRIES - SLIC_RCVQ_FILLENTRIES)
+
+struct slic_rcvqueue {
+ struct sk_buff *head;
+ struct sk_buff *tail;
+ u32 count;
+ u32 size;
+ u32 errors;
+};
+
+struct slic_rcvbuf_info {
+ u32 id;
+ u32 starttime;
+ u32 stoptime;
+ u32 slicworld;
+ u32 lasttime;
+ u32 lastid;
+};
+/*
+ SLIC Handle structure. Used to restrict handle values to
+ 32 bits by using an index rather than an address.
+ Simplifies ucode in 64-bit systems
+*/
+struct slic_handle_word {
+ union {
+ struct {
+ ushort index;
+ ushort bottombits; /* to denote num bufs to card */
+ } parts;
+ u32 whole;
+ } handle;
+};
+
+struct slic_handle {
+ struct slic_handle_word token; /* token passed between host and card*/
+ ushort type;
+ void *address; /* actual address of the object*/
+ ushort offset;
+ struct slic_handle *other_handle;
+ struct slic_handle *next;
+};
+
+#define SLIC_HANDLE_FREE 0x0000
+#define SLIC_HANDLE_DATA 0x0001
+#define SLIC_HANDLE_CMD 0x0002
+#define SLIC_HANDLE_CONTEXT 0x0003
+#define SLIC_HANDLE_TEAM 0x0004
+
+#define handle_index handle.parts.index
+#define handle_bottom handle.parts.bottombits
+#define handle_token handle.whole
+
+#define SLIC_HOSTCMD_SIZE 512
+
+struct slic_hostcmd {
+ struct slic_host64_cmd cmd64;
+ u32 type;
+ struct sk_buff *skb;
+ u32 paddrl;
+ u32 paddrh;
+ u32 busy;
+ u32 cmdsize;
+ ushort numbufs;
+ struct slic_handle *pslic_handle;/* handle associated with command */
+ struct slic_hostcmd *next;
+ struct slic_hostcmd *next_all;
+};
+
+#define SLIC_CMDQ_CMDSINPAGE (PAGE_SIZE / SLIC_HOSTCMD_SIZE)
+#define SLIC_CMD_DUMB 3
+#define SLIC_CMDQ_INITCMDS 256
+#define SLIC_CMDQ_MAXCMDS 256
+#define SLIC_CMDQ_MAXOUTSTAND SLIC_CMDQ_MAXCMDS
+#define SLIC_CMDQ_MAXPAGES (SLIC_CMDQ_MAXCMDS / SLIC_CMDQ_CMDSINPAGE)
+#define SLIC_CMDQ_INITPAGES (SLIC_CMDQ_INITCMDS / SLIC_CMDQ_CMDSINPAGE)
+
+struct slic_cmdqmem {
+ int pagecnt;
+ u32 *pages[SLIC_CMDQ_MAXPAGES];
+ dma_addr_t dma_pages[SLIC_CMDQ_MAXPAGES];
+};
+
+struct slic_cmdqueue {
+ struct slic_hostcmd *head;
+ struct slic_hostcmd *tail;
+ int count;
+ struct slic_spinlock lock;
+};
+
+#define SLIC_MAX_CARDS 32
+#define SLIC_MAX_PORTS 4 /* Max # of ports per card */
+
+
+struct mcast_address {
+ unsigned char address[6];
+ struct mcast_address *next;
+};
+
+#define CARD_DOWN 0x00000000
+#define CARD_UP 0x00000001
+#define CARD_FAIL 0x00000002
+#define CARD_DIAG 0x00000003
+#define CARD_SLEEP 0x00000004
+
+#define ADAPT_DOWN 0x00
+#define ADAPT_UP 0x01
+#define ADAPT_FAIL 0x02
+#define ADAPT_RESET 0x03
+#define ADAPT_SLEEP 0x04
+
+#define ADAPT_FLAGS_BOOTTIME 0x0001
+#define ADAPT_FLAGS_IS64BIT 0x0002
+#define ADAPT_FLAGS_PENDINGLINKDOWN 0x0004
+#define ADAPT_FLAGS_FIBERMEDIA 0x0008
+#define ADAPT_FLAGS_LOCKS_ALLOCED 0x0010
+#define ADAPT_FLAGS_INT_REGISTERED 0x0020
+#define ADAPT_FLAGS_LOAD_TIMER_SET 0x0040
+#define ADAPT_FLAGS_STATS_TIMER_SET 0x0080
+#define ADAPT_FLAGS_RESET_TIMER_SET 0x0100
+
+#define LINK_DOWN 0x00
+#define LINK_CONFIG 0x01
+#define LINK_UP 0x02
+
+#define LINK_10MB 0x00
+#define LINK_100MB 0x01
+#define LINK_AUTOSPEED 0x02
+#define LINK_1000MB 0x03
+#define LINK_10000MB 0x04
+
+#define LINK_HALFD 0x00
+#define LINK_FULLD 0x01
+#define LINK_AUTOD 0x02
+
+#define MAC_DIRECTED 0x00000001
+#define MAC_BCAST 0x00000002
+#define MAC_MCAST 0x00000004
+#define MAC_PROMISC 0x00000008
+#define MAC_LOOPBACK 0x00000010
+#define MAC_ALLMCAST 0x00000020
+
+#define SLIC_DUPLEX(x) ((x == LINK_FULLD) ? "FDX" : "HDX")
+#define SLIC_SPEED(x) ((x == LINK_100MB) ? "100Mb" : ((x == LINK_1000MB) ?\
+ "1000Mb" : " 10Mb"))
+#define SLIC_LINKSTATE(x) ((x == LINK_DOWN) ? "Down" : "Up ")
+#define SLIC_ADAPTER_STATE(x) ((x == ADAPT_UP) ? "UP" : "Down")
+#define SLIC_CARD_STATE(x) ((x == CARD_UP) ? "UP" : "Down")
+
+struct slic_iface_stats {
+ /*
+ * Stats
+ */
+ u64 xmt_bytes;
+ u64 xmt_ucast;
+ u64 xmt_mcast;
+ u64 xmt_bcast;
+ u64 xmt_errors;
+ u64 xmt_discards;
+ u64 xmit_collisions;
+ u64 xmit_excess_xmit_collisions;
+ u64 rcv_bytes;
+ u64 rcv_ucast;
+ u64 rcv_mcast;
+ u64 rcv_bcast;
+ u64 rcv_errors;
+ u64 rcv_discards;
+};
+
+struct sliccp_stats {
+ u64 xmit_tcp_segs;
+ u64 xmit_tcp_bytes;
+ u64 rcv_tcp_segs;
+ u64 rcv_tcp_bytes;
+};
+
+struct slicnet_stats {
+ struct sliccp_stats tcp;
+ struct slic_iface_stats iface;
+};
+
+#define SLIC_LOADTIMER_PERIOD 1
+#define SLIC_INTAGG_DEFAULT 200
+#define SLIC_LOAD_0 0
+#define SLIC_INTAGG_0 0
+#define SLIC_LOAD_1 8000
+#define SLIC_LOAD_2 10000
+#define SLIC_LOAD_3 12000
+#define SLIC_LOAD_4 14000
+#define SLIC_LOAD_5 16000
+#define SLIC_INTAGG_1 50
+#define SLIC_INTAGG_2 100
+#define SLIC_INTAGG_3 150
+#define SLIC_INTAGG_4 200
+#define SLIC_INTAGG_5 250
+#define SLIC_LOAD_1GB 3000
+#define SLIC_LOAD_2GB 6000
+#define SLIC_LOAD_3GB 12000
+#define SLIC_LOAD_4GB 24000
+#define SLIC_LOAD_5GB 48000
+#define SLIC_INTAGG_1GB 50
+#define SLIC_INTAGG_2GB 75
+#define SLIC_INTAGG_3GB 100
+#define SLIC_INTAGG_4GB 100
+#define SLIC_INTAGG_5GB 100
+
+struct ether_header {
+ unsigned char ether_dhost[6];
+ unsigned char ether_shost[6];
+ ushort ether_type;
+};
+
+struct sliccard {
+ uint busnumber;
+ uint slotnumber;
+ uint state;
+ uint cardnum;
+ uint card_size;
+ uint adapters_activated;
+ uint adapters_allocated;
+ uint adapters_sleeping;
+ uint gennumber;
+ u32 events;
+ u32 loadlevel_current;
+ u32 load;
+ uint reset_in_progress;
+ u32 pingstatus;
+ u32 bad_pingstatus;
+ struct timer_list loadtimer;
+ u32 loadtimerset;
+ uint config_set;
+ struct slic_config config;
+ struct adapter *master;
+ struct adapter *adapter[SLIC_MAX_PORTS];
+ struct sliccard *next;
+ u32 error_interrupts;
+ u32 error_rmiss_interrupts;
+ u32 rcv_interrupts;
+ u32 xmit_interrupts;
+ u32 num_isrs;
+ u32 false_interrupts;
+ u32 max_isr_rcvs;
+ u32 max_isr_xmits;
+ u32 rcv_interrupt_yields;
+ u32 tx_packets;
+ u32 debug_ix;
+ ushort reg_type[32];
+ ushort reg_offset[32];
+ u32 reg_value[32];
+ u32 reg_valueh[32];
+};
+
+#define NUM_CFG_SPACES 2
+#define NUM_CFG_REGS 64
+#define NUM_CFG_REG_ULONGS (NUM_CFG_REGS / sizeof(u32))
+
+struct physcard {
+ struct adapter *adapter[SLIC_MAX_PORTS];
+ struct physcard *next;
+ uint adapters_allocd;
+
+/* the following is not currently needed
+ u32 bridge_busnum;
+ u32 bridge_cfg[NUM_CFG_SPACES][NUM_CFG_REG_ULONGS];
+*/
+};
+
+struct base_driver {
+ struct slic_spinlock driver_lock;
+ u32 num_slic_cards;
+ u32 num_slic_ports;
+ u32 num_slic_ports_active;
+ u32 dynamic_intagg;
+ struct sliccard *slic_card;
+ struct physcard *phys_card;
+ uint cardnuminuse[SLIC_MAX_CARDS];
+};
+
+struct slic_shmem {
+ volatile u32 isr;
+ volatile u32 linkstatus;
+ volatile struct slic_stats inicstats;
+};
+
+struct slic_upr {
+ uint adapter;
+ u32 upr_request;
+ u32 upr_data;
+ u32 upr_data_h;
+ u32 upr_buffer;
+ u32 upr_buffer_h;
+ struct slic_upr *next;
+};
+
+struct slic_ifevents {
+ uint oflow802;
+ uint uflow802;
+ uint Tprtoflow;
+ uint rcvearly;
+ uint Bufov;
+ uint Carre;
+ uint Longe;
+ uint Invp;
+ uint Crc;
+ uint Drbl;
+ uint Code;
+ uint IpHlen;
+ uint IpLen;
+ uint IpCsum;
+ uint TpCsum;
+ uint TpHlen;
+};
+
+struct adapter {
+ void *ifp;
+ struct sliccard *card;
+ uint port;
+ struct physcard *physcard;
+ uint physport;
+ uint cardindex;
+ uint card_size;
+ uint chipid;
+ struct net_device *netdev;
+ struct slic_spinlock adapter_lock;
+ struct slic_spinlock reset_lock;
+ struct pci_dev *pcidev;
+ uint busnumber;
+ uint slotnumber;
+ uint functionnumber;
+ ushort vendid;
+ ushort devid;
+ ushort subsysid;
+ u32 irq;
+ u32 drambase;
+ u32 dramlength;
+ uint queues_initialized;
+ uint allocated;
+ uint activated;
+ u32 intrregistered;
+ uint isp_initialized;
+ uint gennumber;
+ u32 curaddrupper;
+ struct slic_shmem *pshmem;
+ dma_addr_t phys_shmem;
+ u32 isrcopy;
+ __iomem struct slic_regs *slic_regs;
+ unsigned char state;
+ unsigned char linkstate;
+ unsigned char linkspeed;
+ unsigned char linkduplex;
+ uint flags;
+ unsigned char macaddr[6];
+ unsigned char currmacaddr[6];
+ u32 macopts;
+ ushort devflags_prev;
+ u64 mcastmask;
+ struct mcast_address *mcastaddrs;
+ struct slic_upr *upr_list;
+ uint upr_busy;
+ struct timer_list pingtimer;
+ u32 pingtimerset;
+ struct timer_list loadtimer;
+ u32 loadtimerset;
+ struct slic_spinlock upr_lock;
+ struct slic_spinlock bit64reglock;
+ struct slic_rspqueue rspqueue;
+ struct slic_rcvqueue rcvqueue;
+ struct slic_cmdqueue cmdq_free;
+ struct slic_cmdqueue cmdq_done;
+ struct slic_cmdqueue cmdq_all;
+ struct slic_cmdqmem cmdqmem;
+ /*
+ * SLIC Handles
+ */
+ /* Object handles*/
+ struct slic_handle slic_handles[SLIC_CMDQ_MAXCMDS+1];
+ /* Free object handles*/
+ struct slic_handle *pfree_slic_handles;
+ /* Object handle list lock*/
+ struct slic_spinlock handle_lock;
+ ushort slic_handle_ix;
+
+ u32 xmitq_full;
+ u32 all_reg_writes;
+ u32 icr_reg_writes;
+ u32 isr_reg_writes;
+ u32 error_interrupts;
+ u32 error_rmiss_interrupts;
+ u32 rx_errors;
+ u32 rcv_drops;
+ u32 rcv_interrupts;
+ u32 xmit_interrupts;
+ u32 linkevent_interrupts;
+ u32 upr_interrupts;
+ u32 num_isrs;
+ u32 false_interrupts;
+ u32 tx_packets;
+ u32 xmit_completes;
+ u32 tx_drops;
+ u32 rcv_broadcasts;
+ u32 rcv_multicasts;
+ u32 rcv_unicasts;
+ u32 max_isr_rcvs;
+ u32 max_isr_xmits;
+ u32 rcv_interrupt_yields;
+ u32 intagg_period;
+ struct inicpm_state *inicpm_info;
+ void *pinicpm_info;
+ struct slic_ifevents if_events;
+ struct slic_stats inicstats_prev;
+ struct slicnet_stats slic_stats;
+};
+
+
+#define UPDATE_STATS(largestat, newstat, oldstat) \
+{ \
+ if ((newstat) < (oldstat)) \
+ (largestat) += ((newstat) + (0xFFFFFFFF - oldstat + 1)); \
+ else \
+ (largestat) += ((newstat) - (oldstat)); \
+}
+
+#define UPDATE_STATS_GB(largestat, newstat, oldstat) \
+{ \
+ (largestat) += ((newstat) - (oldstat)); \
+}
+
+#if BITS_PER_LONG == 64
+#define SLIC_GET_ADDR_LOW(_addr) (u32)((u64)(_addr) & \
+ 0x00000000FFFFFFFF)
+#define SLIC_GET_ADDR_HIGH(_addr) (u32)(((u64)(_addr) >> 32) & \
+ 0x00000000FFFFFFFF)
+#elif BITS_PER_LONG == 32
+#define SLIC_GET_ADDR_LOW(_addr) (u32)(_addr)
+#define SLIC_GET_ADDR_HIGH(_addr) (u32)0
+#else
+#error BITS_PER_LONG must be 32 or 64
+#endif
+
+#define FLUSH true
+#define DONT_FLUSH false
+
+#define SIOCSLICDUMPCARD (SIOCDEVPRIVATE+9)
+#define SIOCSLICSETINTAGG (SIOCDEVPRIVATE+10)
+#define SIOCSLICTRACEDUMP (SIOCDEVPRIVATE+11)
+
+#endif /* __SLIC_DRIVER_H__ */
diff --git a/drivers/staging/slicoss/slichw.h b/drivers/staging/slicoss/slichw.h
new file mode 100644
index 000000000..21cd02b8b
--- /dev/null
+++ b/drivers/staging/slicoss/slichw.h
@@ -0,0 +1,827 @@
+/**************************************************************************
+ *
+ * Copyright (c) 2000-2002 Alacritech, Inc. All rights reserved.
+ *
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY ALACRITECH, INC. ``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 ALACRITECH, INC. OR
+ * CONTRIBUTORS 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.
+ *
+ * The views and conclusions contained in the software and documentation
+ * are those of the authors and should not be interpreted as representing
+ * official policies, either expressed or implied, of Alacritech, Inc.
+ *
+ **************************************************************************/
+
+/*
+ * FILENAME: slichw.h
+ *
+ * This header file contains definitions that are common to our hardware.
+ */
+#ifndef __SLICHW_H__
+#define __SLICHW_H__
+
+#define PCI_VENDOR_ID_ALACRITECH 0x139A
+#define SLIC_1GB_DEVICE_ID 0x0005
+#define SLIC_2GB_DEVICE_ID 0x0007 /* Oasis Device ID */
+
+#define SLIC_1GB_CICADA_SUBSYS_ID 0x0008
+
+#define SLIC_NBR_MACS 4
+
+#define SLIC_RCVBUF_SIZE 2048
+#define SLIC_RCVBUF_HEADSIZE 34
+#define SLIC_RCVBUF_TAILSIZE 0
+#define SLIC_RCVBUF_DATASIZE (SLIC_RCVBUF_SIZE - \
+ (SLIC_RCVBUF_HEADSIZE + \
+ SLIC_RCVBUF_TAILSIZE))
+
+#define VGBSTAT_XPERR 0x40000000
+#define VGBSTAT_XERRSHFT 25
+#define VGBSTAT_XCSERR 0x23
+#define VGBSTAT_XUFLOW 0x22
+#define VGBSTAT_XHLEN 0x20
+#define VGBSTAT_NETERR 0x01000000
+#define VGBSTAT_NERRSHFT 16
+#define VGBSTAT_NERRMSK 0x1ff
+#define VGBSTAT_NCSERR 0x103
+#define VGBSTAT_NUFLOW 0x102
+#define VGBSTAT_NHLEN 0x100
+#define VGBSTAT_LNKERR 0x00000080
+#define VGBSTAT_LERRMSK 0xff
+#define VGBSTAT_LDEARLY 0x86
+#define VGBSTAT_LBOFLO 0x85
+#define VGBSTAT_LCODERR 0x84
+#define VGBSTAT_LDBLNBL 0x83
+#define VGBSTAT_LCRCERR 0x82
+#define VGBSTAT_LOFLO 0x81
+#define VGBSTAT_LUFLO 0x80
+#define IRHDDR_FLEN_MSK 0x0000ffff
+#define IRHDDR_SVALID 0x80000000
+#define IRHDDR_ERR 0x10000000
+#define VRHSTAT_802OE 0x80000000
+#define VRHSTAT_TPOFLO 0x10000000
+#define VRHSTATB_802UE 0x80000000
+#define VRHSTATB_RCVE 0x40000000
+#define VRHSTATB_BUFF 0x20000000
+#define VRHSTATB_CARRE 0x08000000
+#define VRHSTATB_LONGE 0x02000000
+#define VRHSTATB_PREA 0x01000000
+#define VRHSTATB_CRC 0x00800000
+#define VRHSTATB_DRBL 0x00400000
+#define VRHSTATB_CODE 0x00200000
+#define VRHSTATB_TPCSUM 0x00100000
+#define VRHSTATB_TPHLEN 0x00080000
+#define VRHSTATB_IPCSUM 0x00040000
+#define VRHSTATB_IPLERR 0x00020000
+#define VRHSTATB_IPHERR 0x00010000
+#define SLIC_MAX64_BCNT 23
+#define SLIC_MAX32_BCNT 26
+#define IHCMD_XMT_REQ 0x01
+#define IHFLG_IFSHFT 2
+#define SLIC_RSPBUF_SIZE 32
+
+#define SLIC_RESET_MAGIC 0xDEAD
+#define ICR_INT_OFF 0
+#define ICR_INT_ON 1
+#define ICR_INT_MASK 2
+
+#define ISR_ERR 0x80000000
+#define ISR_RCV 0x40000000
+#define ISR_CMD 0x20000000
+#define ISR_IO 0x60000000
+#define ISR_UPC 0x10000000
+#define ISR_LEVENT 0x08000000
+#define ISR_RMISS 0x02000000
+#define ISR_UPCERR 0x01000000
+#define ISR_XDROP 0x00800000
+#define ISR_UPCBSY 0x00020000
+#define ISR_EVMSK 0xffff0000
+#define ISR_PINGMASK 0x00700000
+#define ISR_PINGDSMASK 0x00710000
+#define ISR_UPCMASK 0x11000000
+#define SLIC_WCS_START 0x80000000
+#define SLIC_WCS_COMPARE 0x40000000
+#define SLIC_RCVWCS_BEGIN 0x40000000
+#define SLIC_RCVWCS_FINISH 0x80000000
+#define SLIC_PM_MAXPATTERNS 6
+#define SLIC_PM_PATTERNSIZE 128
+#define SLIC_PMCAPS_WAKEONLAN 0x00000001
+#define MIICR_REG_PCR 0x00000000
+#define MIICR_REG_4 0x00040000
+#define MIICR_REG_9 0x00090000
+#define MIICR_REG_16 0x00100000
+#define PCR_RESET 0x8000
+#define PCR_POWERDOWN 0x0800
+#define PCR_SPEED_100 0x2000
+#define PCR_SPEED_1000 0x0040
+#define PCR_AUTONEG 0x1000
+#define PCR_AUTONEG_RST 0x0200
+#define PCR_DUPLEX_FULL 0x0100
+#define PSR_LINKUP 0x0004
+
+#define PAR_ADV100FD 0x0100
+#define PAR_ADV100HD 0x0080
+#define PAR_ADV10FD 0x0040
+#define PAR_ADV10HD 0x0020
+#define PAR_ASYMPAUSE 0x0C00
+#define PAR_802_3 0x0001
+
+#define PAR_ADV1000XFD 0x0020
+#define PAR_ADV1000XHD 0x0040
+#define PAR_ASYMPAUSE_FIBER 0x0180
+
+#define PGC_ADV1000FD 0x0200
+#define PGC_ADV1000HD 0x0100
+#define SEEQ_LINKFAIL 0x4000
+#define SEEQ_SPEED 0x0080
+#define SEEQ_DUPLEX 0x0040
+#define TDK_DUPLEX 0x0800
+#define TDK_SPEED 0x0400
+#define MRV_REG16_XOVERON 0x0068
+#define MRV_REG16_XOVEROFF 0x0008
+#define MRV_SPEED_1000 0x8000
+#define MRV_SPEED_100 0x4000
+#define MRV_SPEED_10 0x0000
+#define MRV_FULLDUPLEX 0x2000
+#define MRV_LINKUP 0x0400
+
+#define GIG_LINKUP 0x0001
+#define GIG_FULLDUPLEX 0x0002
+#define GIG_SPEED_MASK 0x000C
+#define GIG_SPEED_1000 0x0008
+#define GIG_SPEED_100 0x0004
+#define GIG_SPEED_10 0x0000
+
+#define MCR_RESET 0x80000000
+#define MCR_CRCEN 0x40000000
+#define MCR_FULLD 0x10000000
+#define MCR_PAD 0x02000000
+#define MCR_RETRYLATE 0x01000000
+#define MCR_BOL_SHIFT 21
+#define MCR_IPG1_SHIFT 14
+#define MCR_IPG2_SHIFT 7
+#define MCR_IPG3_SHIFT 0
+#define GMCR_RESET 0x80000000
+#define GMCR_GBIT 0x20000000
+#define GMCR_FULLD 0x10000000
+#define GMCR_GAPBB_SHIFT 14
+#define GMCR_GAPR1_SHIFT 7
+#define GMCR_GAPR2_SHIFT 0
+#define GMCR_GAPBB_1000 0x60
+#define GMCR_GAPR1_1000 0x2C
+#define GMCR_GAPR2_1000 0x40
+#define GMCR_GAPBB_100 0x70
+#define GMCR_GAPR1_100 0x2C
+#define GMCR_GAPR2_100 0x40
+#define XCR_RESET 0x80000000
+#define XCR_XMTEN 0x40000000
+#define XCR_PAUSEEN 0x20000000
+#define XCR_LOADRNG 0x10000000
+#define RCR_RESET 0x80000000
+#define RCR_RCVEN 0x40000000
+#define RCR_RCVALL 0x20000000
+#define RCR_RCVBAD 0x10000000
+#define RCR_CTLEN 0x08000000
+#define RCR_ADDRAEN 0x02000000
+#define GXCR_RESET 0x80000000
+#define GXCR_XMTEN 0x40000000
+#define GXCR_PAUSEEN 0x20000000
+#define GRCR_RESET 0x80000000
+#define GRCR_RCVEN 0x40000000
+#define GRCR_RCVALL 0x20000000
+#define GRCR_RCVBAD 0x10000000
+#define GRCR_CTLEN 0x08000000
+#define GRCR_ADDRAEN 0x02000000
+#define GRCR_HASHSIZE_SHIFT 17
+#define GRCR_HASHSIZE 14
+
+#define SLIC_EEPROM_ID 0xA5A5
+#define SLIC_SRAM_SIZE2GB (64 * 1024)
+#define SLIC_SRAM_SIZE1GB (32 * 1024)
+#define SLIC_HOSTID_DEFAULT 0xFFFF /* uninitialized hostid */
+#define SLIC_NBR_MACS 4
+
+struct slic_rcvbuf {
+ u8 pad1[6];
+ u16 pad2;
+ u32 pad3;
+ u32 pad4;
+ u32 buffer;
+ u32 length;
+ u32 status;
+ u32 pad5;
+ u16 pad6;
+ u8 data[SLIC_RCVBUF_DATASIZE];
+};
+
+struct slic_hddr_wds {
+ union {
+ struct {
+ u32 frame_status;
+ u32 frame_status_b;
+ u32 time_stamp;
+ u32 checksum;
+ } hdrs_14port;
+ struct {
+ u32 frame_status;
+ u16 ByteCnt;
+ u16 TpChksum;
+ u16 CtxHash;
+ u16 MacHash;
+ u32 BufLnk;
+ } hdrs_gbit;
+ } u0;
+};
+
+#define frame_status14 u0.hdrs_14port.frame_status
+#define frame_status_b14 u0.hdrs_14port.frame_status_b
+#define frame_statusGB u0.hdrs_gbit.frame_status
+
+struct slic_host64sg {
+ u32 paddrl;
+ u32 paddrh;
+ u32 length;
+};
+
+struct slic_host64_cmd {
+ u32 hosthandle;
+ u32 RSVD;
+ u8 command;
+ u8 flags;
+ union {
+ u16 rsv1;
+ u16 rsv2;
+ } u0;
+ union {
+ struct {
+ u32 totlen;
+ struct slic_host64sg bufs[SLIC_MAX64_BCNT];
+ } slic_buffers;
+ } u;
+};
+
+struct slic_rspbuf {
+ u32 hosthandle;
+ u32 pad0;
+ u32 pad1;
+ u32 status;
+ u32 pad2[4];
+};
+
+struct slic_regs {
+ u32 slic_reset; /* Reset Register */
+ u32 pad0;
+
+ u32 slic_icr; /* Interrupt Control Register */
+ u32 pad2;
+#define SLIC_ICR 0x0008
+
+ u32 slic_isp; /* Interrupt status pointer */
+ u32 pad1;
+#define SLIC_ISP 0x0010
+
+ u32 slic_isr; /* Interrupt status */
+ u32 pad3;
+#define SLIC_ISR 0x0018
+
+ u32 slic_hbar; /* Header buffer address reg */
+ u32 pad4;
+ /* 31-8 - phy addr of set of contiguous hdr buffers
+ 7-0 - number of buffers passed
+ Buffers are 256 bytes long on 256-byte boundaries. */
+#define SLIC_HBAR 0x0020
+#define SLIC_HBAR_CNT_MSK 0x000000FF
+
+ u32 slic_dbar; /* Data buffer handle & address reg */
+ u32 pad5;
+
+ /* 4 sets of registers; Buffers are 2K bytes long 2 per 4K page. */
+#define SLIC_DBAR 0x0028
+#define SLIC_DBAR_SIZE 2048
+
+ u32 slic_cbar; /* Xmt Cmd buf addr regs.*/
+ /* 1 per XMT interface
+ 31-5 - phy addr of host command buffer
+ 4-0 - length of cmd in multiples of 32 bytes
+ Buffers are 32 bytes up to 512 bytes long */
+#define SLIC_CBAR 0x0030
+#define SLIC_CBAR_LEN_MSK 0x0000001F
+#define SLIC_CBAR_ALIGN 0x00000020
+
+ u32 slic_wcs; /* write control store*/
+#define SLIC_WCS 0x0034
+#define SLIC_WCS_START 0x80000000 /*Start the SLIC (Jump to WCS)*/
+#define SLIC_WCS_COMPARE 0x40000000 /* Compare with value in WCS*/
+
+ u32 slic_rbar; /* Response buffer address reg.*/
+ u32 pad7;
+ /*31-8 - phy addr of set of contiguous response buffers
+ 7-0 - number of buffers passed
+ Buffers are 32 bytes long on 32-byte boundaries.*/
+#define SLIC_RBAR 0x0038
+#define SLIC_RBAR_CNT_MSK 0x000000FF
+#define SLIC_RBAR_SIZE 32
+
+ u32 slic_stats; /* read statistics (UPR) */
+ u32 pad8;
+#define SLIC_RSTAT 0x0040
+
+ u32 slic_rlsr; /* read link status */
+ u32 pad9;
+#define SLIC_LSTAT 0x0048
+
+ u32 slic_wmcfg; /* Write Mac Config */
+ u32 pad10;
+#define SLIC_WMCFG 0x0050
+
+ u32 slic_wphy; /* Write phy register */
+ u32 pad11;
+#define SLIC_WPHY 0x0058
+
+ u32 slic_rcbar; /* Rcv Cmd buf addr reg */
+ u32 pad12;
+#define SLIC_RCBAR 0x0060
+
+ u32 slic_rconfig; /* Read SLIC Config*/
+ u32 pad13;
+#define SLIC_RCONFIG 0x0068
+
+ u32 slic_intagg; /* Interrupt aggregation time */
+ u32 pad14;
+#define SLIC_INTAGG 0x0070
+
+ u32 slic_wxcfg; /* Write XMIT config reg*/
+ u32 pad16;
+#define SLIC_WXCFG 0x0078
+
+ u32 slic_wrcfg; /* Write RCV config reg*/
+ u32 pad17;
+#define SLIC_WRCFG 0x0080
+
+ u32 slic_wraddral; /* Write rcv addr a low*/
+ u32 pad18;
+#define SLIC_WRADDRAL 0x0088
+
+ u32 slic_wraddrah; /* Write rcv addr a high*/
+ u32 pad19;
+#define SLIC_WRADDRAH 0x0090
+
+ u32 slic_wraddrbl; /* Write rcv addr b low*/
+ u32 pad20;
+#define SLIC_WRADDRBL 0x0098
+
+ u32 slic_wraddrbh; /* Write rcv addr b high*/
+ u32 pad21;
+#define SLIC_WRADDRBH 0x00a0
+
+ u32 slic_mcastlow; /* Low bits of mcast mask*/
+ u32 pad22;
+#define SLIC_MCASTLOW 0x00a8
+
+ u32 slic_mcasthigh; /* High bits of mcast mask*/
+ u32 pad23;
+#define SLIC_MCASTHIGH 0x00b0
+
+ u32 slic_ping; /* Ping the card*/
+ u32 pad24;
+#define SLIC_PING 0x00b8
+
+ u32 slic_dump_cmd; /* Dump command */
+ u32 pad25;
+#define SLIC_DUMP_CMD 0x00c0
+
+ u32 slic_dump_data; /* Dump data pointer */
+ u32 pad26;
+#define SLIC_DUMP_DATA 0x00c8
+
+ u32 slic_pcistatus; /* Read card's pci_status register */
+ u32 pad27;
+#define SLIC_PCISTATUS 0x00d0
+
+ u32 slic_wrhostid; /* Write hostid field */
+ u32 pad28;
+#define SLIC_WRHOSTID 0x00d8
+#define SLIC_RDHOSTID_1GB 0x1554
+#define SLIC_RDHOSTID_2GB 0x1554
+
+ u32 slic_low_power; /* Put card in a low power state */
+ u32 pad29;
+#define SLIC_LOW_POWER 0x00e0
+
+ u32 slic_quiesce; /* force slic into quiescent state
+ before soft reset */
+ u32 pad30;
+#define SLIC_QUIESCE 0x00e8
+
+ u32 slic_reset_iface;/* reset interface queues */
+ u32 pad31;
+#define SLIC_RESET_IFACE 0x00f0
+
+ u32 slic_addr_upper;/* Bits 63-32 for host i/f addrs */
+ u32 pad32;
+#define SLIC_ADDR_UPPER 0x00f8 /*Register is only written when it has changed*/
+
+ u32 slic_hbar64; /* 64 bit Header buffer address reg */
+ u32 pad33;
+#define SLIC_HBAR64 0x0100
+
+ u32 slic_dbar64; /* 64 bit Data buffer handle & address reg */
+ u32 pad34;
+#define SLIC_DBAR64 0x0108
+
+ u32 slic_cbar64; /* 64 bit Xmt Cmd buf addr regs. */
+ u32 pad35;
+#define SLIC_CBAR64 0x0110
+
+ u32 slic_rbar64; /* 64 bit Response buffer address reg.*/
+ u32 pad36;
+#define SLIC_RBAR64 0x0118
+
+ u32 slic_rcbar64; /* 64 bit Rcv Cmd buf addr reg*/
+ u32 pad37;
+#define SLIC_RCBAR64 0x0120
+
+ u32 slic_stats64; /* read statistics (64 bit UPR) */
+ u32 pad38;
+#define SLIC_RSTAT64 0x0128
+
+ u32 slic_rcv_wcs; /*Download Gigabit RCV sequencer ucode*/
+ u32 pad39;
+#define SLIC_RCV_WCS 0x0130
+#define SLIC_RCVWCS_BEGIN 0x40000000
+#define SLIC_RCVWCS_FINISH 0x80000000
+
+ u32 slic_wrvlanid; /* Write VlanId field */
+ u32 pad40;
+#define SLIC_WRVLANID 0x0138
+
+ u32 slic_read_xf_info; /* Read Transformer info */
+ u32 pad41;
+#define SLIC_READ_XF_INFO 0x0140
+
+ u32 slic_write_xf_info; /* Write Transformer info */
+ u32 pad42;
+#define SLIC_WRITE_XF_INFO 0x0148
+
+ u32 RSVD1; /* TOE Only */
+ u32 pad43;
+
+ u32 RSVD2; /* TOE Only */
+ u32 pad44;
+
+ u32 RSVD3; /* TOE Only */
+ u32 pad45;
+
+ u32 RSVD4; /* TOE Only */
+ u32 pad46;
+
+ u32 slic_ticks_per_sec; /* Write card ticks per second */
+ u32 pad47;
+#define SLIC_TICKS_PER_SEC 0x0170
+};
+
+enum UPR_REQUEST {
+ SLIC_UPR_STATS,
+ SLIC_UPR_RLSR,
+ SLIC_UPR_WCFG,
+ SLIC_UPR_RCONFIG,
+ SLIC_UPR_RPHY,
+ SLIC_UPR_ENLB,
+ SLIC_UPR_ENCT,
+ SLIC_UPR_PDWN,
+ SLIC_UPR_PING,
+ SLIC_UPR_DUMP,
+};
+
+struct inicpm_wakepattern {
+ u32 patternlength;
+ u8 pattern[SLIC_PM_PATTERNSIZE];
+ u8 mask[SLIC_PM_PATTERNSIZE];
+};
+
+struct inicpm_state {
+ u32 powercaps;
+ u32 powerstate;
+ u32 wake_linkstatus;
+ u32 wake_magicpacket;
+ u32 wake_framepattern;
+ struct inicpm_wakepattern wakepattern[SLIC_PM_MAXPATTERNS];
+};
+
+struct slicpm_packet_pattern {
+ u32 priority;
+ u32 reserved;
+ u32 masksize;
+ u32 patternoffset;
+ u32 patternsize;
+ u32 patternflags;
+};
+
+enum slicpm_power_state {
+ slicpm_state_unspecified = 0,
+ slicpm_state_d0,
+ slicpm_state_d1,
+ slicpm_state_d2,
+ slicpm_state_d3,
+ slicpm_state_maximum
+};
+
+struct slicpm_wakeup_capabilities {
+ enum slicpm_power_state min_magic_packet_wakeup;
+ enum slicpm_power_state min_pattern_wakeup;
+ enum slicpm_power_state min_link_change_wakeup;
+};
+
+struct slic_pnp_capabilities {
+ u32 flags;
+ struct slicpm_wakeup_capabilities wakeup_capabilities;
+};
+
+struct xmt_stats {
+ u32 xmit_tcp_bytes;
+ u32 xmit_tcp_segs;
+ u32 xmit_bytes;
+ u32 xmit_collisions;
+ u32 xmit_unicasts;
+ u32 xmit_other_error;
+ u32 xmit_excess_collisions;
+};
+
+struct rcv_stats {
+ u32 rcv_tcp_bytes;
+ u32 rcv_tcp_segs;
+ u32 rcv_bytes;
+ u32 rcv_unicasts;
+ u32 rcv_other_error;
+ u32 rcv_drops;
+};
+
+struct xmt_statsgb {
+ u64 xmit_tcp_bytes;
+ u64 xmit_tcp_segs;
+ u64 xmit_bytes;
+ u64 xmit_collisions;
+ u64 xmit_unicasts;
+ u64 xmit_other_error;
+ u64 xmit_excess_collisions;
+};
+
+struct rcv_statsgb {
+ u64 rcv_tcp_bytes;
+ u64 rcv_tcp_segs;
+ u64 rcv_bytes;
+ u64 rcv_unicasts;
+ u64 rcv_other_error;
+ u64 rcv_drops;
+};
+
+struct slic_stats {
+ union {
+ struct {
+ struct xmt_stats xmt100;
+ struct rcv_stats rcv100;
+ } stats_100;
+ struct {
+ struct xmt_statsgb xmtGB;
+ struct rcv_statsgb rcvGB;
+ } stats_GB;
+ } u;
+};
+
+#define xmit_tcp_segs100 u.stats_100.xmt100.xmit_tcp_segs
+#define xmit_tcp_bytes100 u.stats_100.xmt100.xmit_tcp_bytes
+#define xmit_bytes100 u.stats_100.xmt100.xmit_bytes
+#define xmit_collisions100 u.stats_100.xmt100.xmit_collisions
+#define xmit_unicasts100 u.stats_100.xmt100.xmit_unicasts
+#define xmit_other_error100 u.stats_100.xmt100.xmit_other_error
+#define xmit_excess_collisions100 u.stats_100.xmt100.xmit_excess_collisions
+#define rcv_tcp_segs100 u.stats_100.rcv100.rcv_tcp_segs
+#define rcv_tcp_bytes100 u.stats_100.rcv100.rcv_tcp_bytes
+#define rcv_bytes100 u.stats_100.rcv100.rcv_bytes
+#define rcv_unicasts100 u.stats_100.rcv100.rcv_unicasts
+#define rcv_other_error100 u.stats_100.rcv100.rcv_other_error
+#define rcv_drops100 u.stats_100.rcv100.rcv_drops
+#define xmit_tcp_segs_gb u.stats_GB.xmtGB.xmit_tcp_segs
+#define xmit_tcp_bytes_gb u.stats_GB.xmtGB.xmit_tcp_bytes
+#define xmit_bytes_gb u.stats_GB.xmtGB.xmit_bytes
+#define xmit_collisions_gb u.stats_GB.xmtGB.xmit_collisions
+#define xmit_unicasts_gb u.stats_GB.xmtGB.xmit_unicasts
+#define xmit_other_error_gb u.stats_GB.xmtGB.xmit_other_error
+#define xmit_excess_collisions_gb u.stats_GB.xmtGB.xmit_excess_collisions
+
+#define rcv_tcp_segs_gb u.stats_GB.rcvGB.rcv_tcp_segs
+#define rcv_tcp_bytes_gb u.stats_GB.rcvGB.rcv_tcp_bytes
+#define rcv_bytes_gb u.stats_GB.rcvGB.rcv_bytes
+#define rcv_unicasts_gb u.stats_GB.rcvGB.rcv_unicasts
+#define rcv_other_error_gb u.stats_GB.rcvGB.rcv_other_error
+#define rcv_drops_gb u.stats_GB.rcvGB.rcv_drops
+
+struct slic_config_mac {
+ u8 macaddrA[6];
+};
+
+#define ATK_FRU_FORMAT 0x00
+#define VENDOR1_FRU_FORMAT 0x01
+#define VENDOR2_FRU_FORMAT 0x02
+#define VENDOR3_FRU_FORMAT 0x03
+#define VENDOR4_FRU_FORMAT 0x04
+#define NO_FRU_FORMAT 0xFF
+
+struct atk_fru {
+ u8 assembly[6];
+ u8 revision[2];
+ u8 serial[14];
+ u8 pad[3];
+};
+
+struct vendor1_fru {
+ u8 commodity;
+ u8 assembly[4];
+ u8 revision[2];
+ u8 supplier[2];
+ u8 date[2];
+ u8 sequence[3];
+ u8 pad[13];
+};
+
+struct vendor2_fru {
+ u8 part[8];
+ u8 supplier[5];
+ u8 date[3];
+ u8 sequence[4];
+ u8 pad[7];
+};
+
+struct vendor3_fru {
+ u8 assembly[6];
+ u8 revision[2];
+ u8 serial[14];
+ u8 pad[3];
+};
+
+struct vendor4_fru {
+ u8 number[8];
+ u8 part[8];
+ u8 version[8];
+ u8 pad[3];
+};
+
+union oemfru {
+ struct vendor1_fru vendor1_fru;
+ struct vendor2_fru vendor2_fru;
+ struct vendor3_fru vendor3_fru;
+ struct vendor4_fru vendor4_fru;
+};
+
+/*
+ * SLIC EEPROM structure for Mojave
+ */
+struct slic_eeprom {
+ u16 Id; /* 00 EEPROM/FLASH Magic code 'A5A5'*/
+ u16 EecodeSize; /* 01 Size of EEPROM Codes (bytes * 4)*/
+ u16 FlashSize; /* 02 Flash size */
+ u16 EepromSize; /* 03 EEPROM Size */
+ u16 VendorId; /* 04 Vendor ID */
+ u16 DeviceId; /* 05 Device ID */
+ u8 RevisionId; /* 06 Revision ID */
+ u8 ClassCode[3]; /* 07 Class Code */
+ u8 DbgIntPin; /* 08 Debug Interrupt pin */
+ u8 NetIntPin0; /* Network Interrupt Pin */
+ u8 MinGrant; /* 09 Minimum grant */
+ u8 MaxLat; /* Maximum Latency */
+ u16 PciStatus; /* 10 PCI Status */
+ u16 SubSysVId; /* 11 Subsystem Vendor Id */
+ u16 SubSysId; /* 12 Subsystem ID */
+ u16 DbgDevId; /* 13 Debug Device Id */
+ u16 DramRomFn; /* 14 Dram/Rom function */
+ u16 DSize2Pci; /* 15 DRAM size to PCI (bytes * 64K) */
+ u16 RSize2Pci; /* 16 ROM extension size to PCI (bytes * 4k) */
+ u8 NetIntPin1; /* 17 Network Interface Pin 1
+ (simba/leone only) */
+ u8 NetIntPin2; /* Network Interface Pin 2 (simba/leone only)*/
+ union {
+ u8 NetIntPin3; /* 18 Network Interface Pin 3 (simba only) */
+ u8 FreeTime; /* FreeTime setting (leone/mojave only) */
+ } u1;
+ u8 TBIctl; /* 10-bit interface control (Mojave only) */
+ u16 DramSize; /* 19 DRAM size (bytes * 64k) */
+ union {
+ struct {
+ /* Mac Interface Specific portions */
+ struct slic_config_mac MacInfo[SLIC_NBR_MACS];
+ } mac; /* MAC access for all boards */
+ struct {
+ /* use above struct for MAC access */
+ struct slic_config_mac pad[SLIC_NBR_MACS - 1];
+ u16 DeviceId2; /* Device ID for 2nd PCI function */
+ u8 IntPin2; /* Interrupt pin for 2nd PCI function */
+ u8 ClassCode2[3]; /* Class Code for 2nd PCI function */
+ } mojave; /* 2nd function access for gigabit board */
+ } u2;
+ u16 CfgByte6; /* Config Byte 6 */
+ u16 PMECapab; /* Power Mgment capabilities */
+ u16 NwClkCtrls; /* NetworkClockControls */
+ u8 FruFormat; /* Alacritech FRU format type */
+ struct atk_fru AtkFru; /* Alacritech FRU information */
+ u8 OemFruFormat; /* optional OEM FRU format type */
+ union oemfru OemFru; /* optional OEM FRU information */
+ u8 Pad[4]; /* Pad to 128 bytes - includes 2 cksum bytes
+ * (if OEM FRU info exists) and two unusable
+ * bytes at the end */
+};
+
+/* SLIC EEPROM structure for Oasis */
+struct oslic_eeprom {
+ u16 Id; /* 00 EEPROM/FLASH Magic code 'A5A5' */
+ u16 EecodeSize; /* 01 Size of EEPROM Codes (bytes * 4)*/
+ u16 FlashConfig0; /* 02 Flash Config for SPI device 0 */
+ u16 FlashConfig1; /* 03 Flash Config for SPI device 1 */
+ u16 VendorId; /* 04 Vendor ID */
+ u16 DeviceId; /* 05 Device ID (function 0) */
+ u8 RevisionId; /* 06 Revision ID */
+ u8 ClassCode[3]; /* 07 Class Code for PCI function 0 */
+ u8 IntPin1; /* 08 Interrupt pin for PCI function 1*/
+ u8 ClassCode2[3]; /* 09 Class Code for PCI function 1 */
+ u8 IntPin2; /* 10 Interrupt pin for PCI function 2*/
+ u8 IntPin0; /* Interrupt pin for PCI function 0*/
+ u8 MinGrant; /* 11 Minimum grant */
+ u8 MaxLat; /* Maximum Latency */
+ u16 SubSysVId; /* 12 Subsystem Vendor Id */
+ u16 SubSysId; /* 13 Subsystem ID */
+ u16 FlashSize; /* 14 Flash size (bytes / 4K) */
+ u16 DSize2Pci; /* 15 DRAM size to PCI (bytes / 64K) */
+ u16 RSize2Pci; /* 16 Flash (ROM extension) size to PCI
+ (bytes / 4K) */
+ u16 DeviceId1; /* 17 Device Id (function 1) */
+ u16 DeviceId2; /* 18 Device Id (function 2) */
+ u16 CfgByte6; /* 19 Device Status Config Bytes 6-7 */
+ u16 PMECapab; /* 20 Power Mgment capabilities */
+ u8 MSICapab; /* 21 MSI capabilities */
+ u8 ClockDivider; /* Clock divider */
+ u16 PciStatusLow; /* 22 PCI Status bits 15:0 */
+ u16 PciStatusHigh; /* 23 PCI Status bits 31:16 */
+ u16 DramConfigLow; /* 24 DRAM Configuration bits 15:0 */
+ u16 DramConfigHigh; /* 25 DRAM Configuration bits 31:16 */
+ u16 DramSize; /* 26 DRAM size (bytes / 64K) */
+ u16 GpioTbiCtl; /* 27 GPIO/TBI controls for functions 1/0 */
+ u16 EepromSize; /* 28 EEPROM Size */
+ struct slic_config_mac MacInfo[2]; /* 29 MAC addresses (2 ports) */
+ u8 FruFormat; /* 35 Alacritech FRU format type */
+ struct atk_fru AtkFru; /* Alacritech FRU information */
+ u8 OemFruFormat; /* optional OEM FRU format type */
+ union oemfru OemFru; /* optional OEM FRU information */
+ u8 Pad[4]; /* Pad to 128 bytes - includes 2 checksum bytes
+ * (if OEM FRU info exists) and two unusable
+ * bytes at the end
+ */
+};
+
+#define MAX_EECODE_SIZE sizeof(struct slic_eeprom)
+#define MIN_EECODE_SIZE 0x62 /* code size without optional OEM FRU stuff */
+
+/*
+ * SLIC CONFIG structure
+ *
+ * This structure lives in the CARD structure and is valid for all board types.
+ * It is filled in from the appropriate EEPROM structure by
+ * SlicGetConfigData()
+ */
+struct slic_config {
+ bool EepromValid; /* Valid EEPROM flag (checksum good?) */
+ u16 DramSize; /* DRAM size (bytes / 64K) */
+ struct slic_config_mac MacInfo[SLIC_NBR_MACS]; /* MAC addresses */
+ u8 FruFormat; /* Alacritech FRU format type */
+ struct atk_fru AtkFru; /* Alacritech FRU information */
+ u8 OemFruFormat; /* optional OEM FRU format type */
+ union {
+ struct vendor1_fru vendor1_fru;
+ struct vendor2_fru vendor2_fru;
+ struct vendor3_fru vendor3_fru;
+ struct vendor4_fru vendor4_fru;
+ } OemFru;
+};
+
+#pragma pack()
+
+#endif
diff --git a/drivers/staging/slicoss/slicoss.c b/drivers/staging/slicoss/slicoss.c
new file mode 100644
index 000000000..68f1be127
--- /dev/null
+++ b/drivers/staging/slicoss/slicoss.c
@@ -0,0 +1,3176 @@
+/**************************************************************************
+ *
+ * Copyright 2000-2006 Alacritech, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED BY ALACRITECH, INC. ``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 ALACRITECH, INC. OR
+ * CONTRIBUTORS 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.
+ *
+ * The views and conclusions contained in the software and documentation
+ * are those of the authors and should not be interpreted as representing
+ * official policies, either expressed or implied, of Alacritech, Inc.
+ *
+ **************************************************************************/
+
+/*
+ * FILENAME: slicoss.c
+ *
+ * The SLICOSS driver for Alacritech's IS-NIC products.
+ *
+ * This driver is supposed to support:
+ *
+ * Mojave cards (single port PCI Gigabit) both copper and fiber
+ * Oasis cards (single and dual port PCI-x Gigabit) copper and fiber
+ * Kalahari cards (dual and quad port PCI-e Gigabit) copper and fiber
+ *
+ * The driver was actually tested on Oasis and Kalahari cards.
+ *
+ *
+ * NOTE: This is the standard, non-accelerated version of Alacritech's
+ * IS-NIC driver.
+ */
+
+
+#define KLUDGE_FOR_4GB_BOUNDARY 1
+#define DEBUG_MICROCODE 1
+#define DBG 1
+#define SLIC_INTERRUPT_PROCESS_LIMIT 1
+#define SLIC_OFFLOAD_IP_CHECKSUM 1
+#define STATS_TIMER_INTERVAL 2
+#define PING_TIMER_INTERVAL 1
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/ioport.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/timer.h>
+#include <linux/pci.h>
+#include <linux/spinlock.h>
+#include <linux/init.h>
+#include <linux/bitops.h>
+#include <linux/io.h>
+#include <linux/netdevice.h>
+#include <linux/crc32.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/delay.h>
+#include <linux/seq_file.h>
+#include <linux/kthread.h>
+#include <linux/module.h>
+
+#include <linux/firmware.h>
+#include <linux/types.h>
+#include <linux/dma-mapping.h>
+#include <linux/mii.h>
+#include <linux/if_vlan.h>
+#include <asm/unaligned.h>
+
+#include <linux/ethtool.h>
+#include <linux/uaccess.h>
+#include "slichw.h"
+#include "slic.h"
+
+static uint slic_first_init = 1;
+static char *slic_banner = "Alacritech SLIC Technology(tm) Server and Storage Accelerator (Non-Accelerated)";
+
+static char *slic_proc_version = "2.0.351 2006/07/14 12:26:00";
+
+static struct base_driver slic_global = { {}, 0, 0, 0, 1, NULL, NULL };
+static int intagg_delay = 100;
+static u32 dynamic_intagg;
+static unsigned int rcv_count;
+
+#define DRV_NAME "slicoss"
+#define DRV_VERSION "2.0.1"
+#define DRV_AUTHOR "Alacritech, Inc. Engineering"
+#define DRV_DESCRIPTION "Alacritech SLIC Techonology(tm) "\
+ "Non-Accelerated Driver"
+#define DRV_COPYRIGHT "Copyright 2000-2006 Alacritech, Inc. "\
+ "All rights reserved."
+#define PFX DRV_NAME " "
+
+MODULE_AUTHOR(DRV_AUTHOR);
+MODULE_DESCRIPTION(DRV_DESCRIPTION);
+MODULE_LICENSE("Dual BSD/GPL");
+
+module_param(dynamic_intagg, int, 0);
+MODULE_PARM_DESC(dynamic_intagg, "Dynamic Interrupt Aggregation Setting");
+module_param(intagg_delay, int, 0);
+MODULE_PARM_DESC(intagg_delay, "uSec Interrupt Aggregation Delay");
+
+static const struct pci_device_id slic_pci_tbl[] = {
+ { PCI_DEVICE(PCI_VENDOR_ID_ALACRITECH, SLIC_1GB_DEVICE_ID) },
+ { PCI_DEVICE(PCI_VENDOR_ID_ALACRITECH, SLIC_2GB_DEVICE_ID) },
+ { 0 }
+};
+
+MODULE_DEVICE_TABLE(pci, slic_pci_tbl);
+
+static inline void slic_reg32_write(void __iomem *reg, u32 value, bool flush)
+{
+ writel(value, reg);
+ if (flush)
+ mb();
+}
+
+static inline void slic_reg64_write(struct adapter *adapter, void __iomem *reg,
+ u32 value, void __iomem *regh, u32 paddrh,
+ bool flush)
+{
+ spin_lock_irqsave(&adapter->bit64reglock.lock,
+ adapter->bit64reglock.flags);
+ if (paddrh != adapter->curaddrupper) {
+ adapter->curaddrupper = paddrh;
+ writel(paddrh, regh);
+ }
+ writel(value, reg);
+ if (flush)
+ mb();
+ spin_unlock_irqrestore(&adapter->bit64reglock.lock,
+ adapter->bit64reglock.flags);
+}
+
+static void slic_mcast_set_bit(struct adapter *adapter, char *address)
+{
+ unsigned char crcpoly;
+
+ /* Get the CRC polynomial for the mac address */
+ /* we use bits 1-8 (lsb), bitwise reversed,
+ * msb (= lsb bit 0 before bitrev) is automatically discarded */
+ crcpoly = ether_crc(ETH_ALEN, address)>>23;
+
+ /* We only have space on the SLIC for 64 entries. Lop
+ * off the top two bits. (2^6 = 64)
+ */
+ crcpoly &= 0x3F;
+
+ /* OR in the new bit into our 64 bit mask. */
+ adapter->mcastmask |= (u64) 1 << crcpoly;
+}
+
+static void slic_mcast_set_mask(struct adapter *adapter)
+{
+ __iomem struct slic_regs *slic_regs = adapter->slic_regs;
+
+ if (adapter->macopts & (MAC_ALLMCAST | MAC_PROMISC)) {
+ /* Turn on all multicast addresses. We have to do this for
+ * promiscuous mode as well as ALLMCAST mode. It saves the
+ * Microcode from having to keep state about the MAC
+ * configuration.
+ */
+ slic_reg32_write(&slic_regs->slic_mcastlow, 0xFFFFFFFF, FLUSH);
+ slic_reg32_write(&slic_regs->slic_mcasthigh, 0xFFFFFFFF,
+ FLUSH);
+ } else {
+ /* Commit our multicast mast to the SLIC by writing to the
+ * multicast address mask registers
+ */
+ slic_reg32_write(&slic_regs->slic_mcastlow,
+ (u32)(adapter->mcastmask & 0xFFFFFFFF), FLUSH);
+ slic_reg32_write(&slic_regs->slic_mcasthigh,
+ (u32)((adapter->mcastmask >> 32) & 0xFFFFFFFF), FLUSH);
+ }
+}
+
+static void slic_timer_ping(ulong dev)
+{
+ struct adapter *adapter;
+ struct sliccard *card;
+
+ adapter = netdev_priv((struct net_device *)dev);
+ card = adapter->card;
+
+ adapter->pingtimer.expires = jiffies + (PING_TIMER_INTERVAL * HZ);
+ add_timer(&adapter->pingtimer);
+}
+
+static void slic_unmap_mmio_space(struct adapter *adapter)
+{
+ if (adapter->slic_regs)
+ iounmap(adapter->slic_regs);
+ adapter->slic_regs = NULL;
+}
+
+/*
+ * slic_link_config
+ *
+ * Write phy control to configure link duplex/speed
+ *
+ */
+static void slic_link_config(struct adapter *adapter,
+ u32 linkspeed, u32 linkduplex)
+{
+ u32 __iomem *wphy;
+ u32 speed;
+ u32 duplex;
+ u32 phy_config;
+ u32 phy_advreg;
+ u32 phy_gctlreg;
+
+ if (adapter->state != ADAPT_UP)
+ return;
+
+ if (linkspeed > LINK_1000MB)
+ linkspeed = LINK_AUTOSPEED;
+ if (linkduplex > LINK_AUTOD)
+ linkduplex = LINK_AUTOD;
+
+ wphy = &adapter->slic_regs->slic_wphy;
+
+ if ((linkspeed == LINK_AUTOSPEED) || (linkspeed == LINK_1000MB)) {
+ if (adapter->flags & ADAPT_FLAGS_FIBERMEDIA) {
+ /* We've got a fiber gigabit interface, and register
+ * 4 is different in fiber mode than in copper mode
+ */
+
+ /* advertise FD only @1000 Mb */
+ phy_advreg = (MIICR_REG_4 | (PAR_ADV1000XFD));
+ /* enable PAUSE frames */
+ phy_advreg |= PAR_ASYMPAUSE_FIBER;
+ slic_reg32_write(wphy, phy_advreg, FLUSH);
+
+ if (linkspeed == LINK_AUTOSPEED) {
+ /* reset phy, enable auto-neg */
+ phy_config =
+ (MIICR_REG_PCR |
+ (PCR_RESET | PCR_AUTONEG |
+ PCR_AUTONEG_RST));
+ slic_reg32_write(wphy, phy_config, FLUSH);
+ } else { /* forced 1000 Mb FD*/
+ /* power down phy to break link
+ this may not work) */
+ phy_config = (MIICR_REG_PCR | PCR_POWERDOWN);
+ slic_reg32_write(wphy, phy_config, FLUSH);
+ /* wait, Marvell says 1 sec,
+ try to get away with 10 ms */
+ mdelay(10);
+
+ /* disable auto-neg, set speed/duplex,
+ soft reset phy, powerup */
+ phy_config =
+ (MIICR_REG_PCR |
+ (PCR_RESET | PCR_SPEED_1000 |
+ PCR_DUPLEX_FULL));
+ slic_reg32_write(wphy, phy_config, FLUSH);
+ }
+ } else { /* copper gigabit */
+
+ /* Auto-Negotiate or 1000 Mb must be auto negotiated
+ * We've got a copper gigabit interface, and
+ * register 4 is different in copper mode than
+ * in fiber mode
+ */
+ if (linkspeed == LINK_AUTOSPEED) {
+ /* advertise 10/100 Mb modes */
+ phy_advreg =
+ (MIICR_REG_4 |
+ (PAR_ADV100FD | PAR_ADV100HD | PAR_ADV10FD
+ | PAR_ADV10HD));
+ } else {
+ /* linkspeed == LINK_1000MB -
+ don't advertise 10/100 Mb modes */
+ phy_advreg = MIICR_REG_4;
+ }
+ /* enable PAUSE frames */
+ phy_advreg |= PAR_ASYMPAUSE;
+ /* required by the Cicada PHY */
+ phy_advreg |= PAR_802_3;
+ slic_reg32_write(wphy, phy_advreg, FLUSH);
+ /* advertise FD only @1000 Mb */
+ phy_gctlreg = (MIICR_REG_9 | (PGC_ADV1000FD));
+ slic_reg32_write(wphy, phy_gctlreg, FLUSH);
+
+ if (adapter->subsysid != SLIC_1GB_CICADA_SUBSYS_ID) {
+ /* if a Marvell PHY
+ enable auto crossover */
+ phy_config =
+ (MIICR_REG_16 | (MRV_REG16_XOVERON));
+ slic_reg32_write(wphy, phy_config, FLUSH);
+
+ /* reset phy, enable auto-neg */
+ phy_config =
+ (MIICR_REG_PCR |
+ (PCR_RESET | PCR_AUTONEG |
+ PCR_AUTONEG_RST));
+ slic_reg32_write(wphy, phy_config, FLUSH);
+ } else { /* it's a Cicada PHY */
+ /* enable and restart auto-neg (don't reset) */
+ phy_config =
+ (MIICR_REG_PCR |
+ (PCR_AUTONEG | PCR_AUTONEG_RST));
+ slic_reg32_write(wphy, phy_config, FLUSH);
+ }
+ }
+ } else {
+ /* Forced 10/100 */
+ if (linkspeed == LINK_10MB)
+ speed = 0;
+ else
+ speed = PCR_SPEED_100;
+ if (linkduplex == LINK_HALFD)
+ duplex = 0;
+ else
+ duplex = PCR_DUPLEX_FULL;
+
+ if (adapter->subsysid != SLIC_1GB_CICADA_SUBSYS_ID) {
+ /* if a Marvell PHY
+ disable auto crossover */
+ phy_config = (MIICR_REG_16 | (MRV_REG16_XOVEROFF));
+ slic_reg32_write(wphy, phy_config, FLUSH);
+ }
+
+ /* power down phy to break link (this may not work) */
+ phy_config = (MIICR_REG_PCR | (PCR_POWERDOWN | speed | duplex));
+ slic_reg32_write(wphy, phy_config, FLUSH);
+
+ /* wait, Marvell says 1 sec, try to get away with 10 ms */
+ mdelay(10);
+
+ if (adapter->subsysid != SLIC_1GB_CICADA_SUBSYS_ID) {
+ /* if a Marvell PHY
+ disable auto-neg, set speed,
+ soft reset phy, powerup */
+ phy_config =
+ (MIICR_REG_PCR | (PCR_RESET | speed | duplex));
+ slic_reg32_write(wphy, phy_config, FLUSH);
+ } else { /* it's a Cicada PHY */
+ /* disable auto-neg, set speed, powerup */
+ phy_config = (MIICR_REG_PCR | (speed | duplex));
+ slic_reg32_write(wphy, phy_config, FLUSH);
+ }
+ }
+}
+
+static int slic_card_download_gbrcv(struct adapter *adapter)
+{
+ const struct firmware *fw;
+ const char *file = "";
+ int ret;
+ __iomem struct slic_regs *slic_regs = adapter->slic_regs;
+ u32 codeaddr;
+ u32 instruction;
+ int index = 0;
+ u32 rcvucodelen = 0;
+
+ switch (adapter->devid) {
+ case SLIC_2GB_DEVICE_ID:
+ file = "/*(DEBLOBBED)*/";
+ break;
+ case SLIC_1GB_DEVICE_ID:
+ file = "/*(DEBLOBBED)*/";
+ break;
+ default:
+ return -ENOENT;
+ }
+
+ ret = reject_firmware(&fw, file, &adapter->pcidev->dev);
+ if (ret) {
+ dev_err(&adapter->pcidev->dev,
+ "Failed to load firmware %s\n", file);
+ return ret;
+ }
+
+ rcvucodelen = *(u32 *)(fw->data + index);
+ index += 4;
+ switch (adapter->devid) {
+ case SLIC_2GB_DEVICE_ID:
+ if (rcvucodelen != OasisRcvUCodeLen) {
+ release_firmware(fw);
+ return -EINVAL;
+ }
+ break;
+ case SLIC_1GB_DEVICE_ID:
+ if (rcvucodelen != GBRcvUCodeLen) {
+ release_firmware(fw);
+ return -EINVAL;
+ }
+ break;
+ }
+ /* start download */
+ slic_reg32_write(&slic_regs->slic_rcv_wcs, SLIC_RCVWCS_BEGIN, FLUSH);
+ /* download the rcv sequencer ucode */
+ for (codeaddr = 0; codeaddr < rcvucodelen; codeaddr++) {
+ /* write out instruction address */
+ slic_reg32_write(&slic_regs->slic_rcv_wcs, codeaddr, FLUSH);
+
+ instruction = *(u32 *)(fw->data + index);
+ index += 4;
+ /* write out the instruction data low addr */
+ slic_reg32_write(&slic_regs->slic_rcv_wcs, instruction, FLUSH);
+
+ instruction = *(u8 *)(fw->data + index);
+ index++;
+ /* write out the instruction data high addr */
+ slic_reg32_write(&slic_regs->slic_rcv_wcs, (u8)instruction,
+ FLUSH);
+ }
+
+ /* download finished */
+ release_firmware(fw);
+ slic_reg32_write(&slic_regs->slic_rcv_wcs, SLIC_RCVWCS_FINISH, FLUSH);
+ return 0;
+}
+
+/*(DEBLOBBED)*/
+
+static int slic_card_download(struct adapter *adapter)
+{
+ const struct firmware *fw;
+ const char *file = "";
+ int ret;
+ u32 section;
+ int thissectionsize;
+ int codeaddr;
+ __iomem struct slic_regs *slic_regs = adapter->slic_regs;
+ u32 instruction;
+ u32 baseaddress;
+ u32 i;
+ u32 numsects = 0;
+ u32 sectsize[3];
+ u32 sectstart[3];
+ int ucode_start, index = 0;
+
+ switch (adapter->devid) {
+ case SLIC_2GB_DEVICE_ID:
+ file = "/*(DEBLOBBED)*/";
+ break;
+ case SLIC_1GB_DEVICE_ID:
+ file = "/*(DEBLOBBED)*/";
+ break;
+ default:
+ return -ENOENT;
+ }
+ ret = reject_firmware(&fw, file, &adapter->pcidev->dev);
+ if (ret) {
+ dev_err(&adapter->pcidev->dev,
+ "Failed to load firmware %s\n", file);
+ return ret;
+ }
+ numsects = *(u32 *)(fw->data + index);
+ index += 4;
+ for (i = 0; i < numsects; i++) {
+ sectsize[i] = *(u32 *)(fw->data + index);
+ index += 4;
+ }
+ for (i = 0; i < numsects; i++) {
+ sectstart[i] = *(u32 *)(fw->data + index);
+ index += 4;
+ }
+ ucode_start = index;
+ instruction = *(u32 *)(fw->data + index);
+ index += 4;
+ for (section = 0; section < numsects; section++) {
+ baseaddress = sectstart[section];
+ thissectionsize = sectsize[section] >> 3;
+
+ for (codeaddr = 0; codeaddr < thissectionsize; codeaddr++) {
+ /* Write out instruction address */
+ slic_reg32_write(&slic_regs->slic_wcs,
+ baseaddress + codeaddr, FLUSH);
+ /* Write out instruction to low addr */
+ slic_reg32_write(&slic_regs->slic_wcs,
+ instruction, FLUSH);
+ instruction = *(u32 *)(fw->data + index);
+ index += 4;
+
+ /* Write out instruction to high addr */
+ slic_reg32_write(&slic_regs->slic_wcs,
+ instruction, FLUSH);
+ instruction = *(u32 *)(fw->data + index);
+ index += 4;
+ }
+ }
+ index = ucode_start;
+ for (section = 0; section < numsects; section++) {
+ instruction = *(u32 *)(fw->data + index);
+ baseaddress = sectstart[section];
+ if (baseaddress < 0x8000)
+ continue;
+ thissectionsize = sectsize[section] >> 3;
+
+ for (codeaddr = 0; codeaddr < thissectionsize; codeaddr++) {
+ /* Write out instruction address */
+ slic_reg32_write(&slic_regs->slic_wcs,
+ SLIC_WCS_COMPARE | (baseaddress + codeaddr),
+ FLUSH);
+ /* Write out instruction to low addr */
+ slic_reg32_write(&slic_regs->slic_wcs, instruction,
+ FLUSH);
+ instruction = *(u32 *)(fw->data + index);
+ index += 4;
+ /* Write out instruction to high addr */
+ slic_reg32_write(&slic_regs->slic_wcs, instruction,
+ FLUSH);
+ instruction = *(u32 *)(fw->data + index);
+ index += 4;
+
+ /* Check SRAM location zero. If it is non-zero. Abort.*/
+/* failure = readl((u32 __iomem *)&slic_regs->slic_reset);
+ if (failure) {
+ release_firmware(fw);
+ return -EIO;
+ }*/
+ }
+ }
+ release_firmware(fw);
+ /* Everything OK, kick off the card */
+ mdelay(10);
+ slic_reg32_write(&slic_regs->slic_wcs, SLIC_WCS_START, FLUSH);
+
+ /* stall for 20 ms, long enough for ucode to init card
+ and reach mainloop */
+ mdelay(20);
+
+ return 0;
+}
+
+/*(DEBLOBBED)*/
+
+static void slic_adapter_set_hwaddr(struct adapter *adapter)
+{
+ struct sliccard *card = adapter->card;
+
+ if ((adapter->card) && (card->config_set)) {
+ memcpy(adapter->macaddr,
+ card->config.MacInfo[adapter->functionnumber].macaddrA,
+ sizeof(struct slic_config_mac));
+ if (is_zero_ether_addr(adapter->currmacaddr))
+ memcpy(adapter->currmacaddr, adapter->macaddr,
+ ETH_ALEN);
+ if (adapter->netdev)
+ memcpy(adapter->netdev->dev_addr, adapter->currmacaddr,
+ ETH_ALEN);
+ }
+}
+
+static void slic_intagg_set(struct adapter *adapter, u32 value)
+{
+ slic_reg32_write(&adapter->slic_regs->slic_intagg, value, FLUSH);
+ adapter->card->loadlevel_current = value;
+}
+
+static void slic_soft_reset(struct adapter *adapter)
+{
+ if (adapter->card->state == CARD_UP) {
+ slic_reg32_write(&adapter->slic_regs->slic_quiesce, 0, FLUSH);
+ mdelay(1);
+ }
+
+ slic_reg32_write(&adapter->slic_regs->slic_reset, SLIC_RESET_MAGIC,
+ FLUSH);
+ mdelay(1);
+}
+
+static void slic_mac_address_config(struct adapter *adapter)
+{
+ u32 value;
+ u32 value2;
+ __iomem struct slic_regs *slic_regs = adapter->slic_regs;
+
+ value = ntohl(*(__be32 *) &adapter->currmacaddr[2]);
+ slic_reg32_write(&slic_regs->slic_wraddral, value, FLUSH);
+ slic_reg32_write(&slic_regs->slic_wraddrbl, value, FLUSH);
+
+ value2 = (u32) ((adapter->currmacaddr[0] << 8 |
+ adapter->currmacaddr[1]) & 0xFFFF);
+
+ slic_reg32_write(&slic_regs->slic_wraddrah, value2, FLUSH);
+ slic_reg32_write(&slic_regs->slic_wraddrbh, value2, FLUSH);
+
+ /* Write our multicast mask out to the card. This is done */
+ /* here in addition to the slic_mcast_addr_set routine */
+ /* because ALL_MCAST may have been enabled or disabled */
+ slic_mcast_set_mask(adapter);
+}
+
+static void slic_mac_config(struct adapter *adapter)
+{
+ u32 value;
+ __iomem struct slic_regs *slic_regs = adapter->slic_regs;
+
+ /* Setup GMAC gaps */
+ if (adapter->linkspeed == LINK_1000MB) {
+ value = ((GMCR_GAPBB_1000 << GMCR_GAPBB_SHIFT) |
+ (GMCR_GAPR1_1000 << GMCR_GAPR1_SHIFT) |
+ (GMCR_GAPR2_1000 << GMCR_GAPR2_SHIFT));
+ } else {
+ value = ((GMCR_GAPBB_100 << GMCR_GAPBB_SHIFT) |
+ (GMCR_GAPR1_100 << GMCR_GAPR1_SHIFT) |
+ (GMCR_GAPR2_100 << GMCR_GAPR2_SHIFT));
+ }
+
+ /* enable GMII */
+ if (adapter->linkspeed == LINK_1000MB)
+ value |= GMCR_GBIT;
+
+ /* enable fullduplex */
+ if ((adapter->linkduplex == LINK_FULLD)
+ || (adapter->macopts & MAC_LOOPBACK)) {
+ value |= GMCR_FULLD;
+ }
+
+ /* write mac config */
+ slic_reg32_write(&slic_regs->slic_wmcfg, value, FLUSH);
+
+ /* setup mac addresses */
+ slic_mac_address_config(adapter);
+}
+
+static void slic_config_set(struct adapter *adapter, bool linkchange)
+{
+ u32 value;
+ u32 RcrReset;
+ __iomem struct slic_regs *slic_regs = adapter->slic_regs;
+
+ if (linkchange) {
+ /* Setup MAC */
+ slic_mac_config(adapter);
+ RcrReset = GRCR_RESET;
+ } else {
+ slic_mac_address_config(adapter);
+ RcrReset = 0;
+ }
+
+ if (adapter->linkduplex == LINK_FULLD) {
+ /* setup xmtcfg */
+ value = (GXCR_RESET | /* Always reset */
+ GXCR_XMTEN | /* Enable transmit */
+ GXCR_PAUSEEN); /* Enable pause */
+
+ slic_reg32_write(&slic_regs->slic_wxcfg, value, FLUSH);
+
+ /* Setup rcvcfg last */
+ value = (RcrReset | /* Reset, if linkchange */
+ GRCR_CTLEN | /* Enable CTL frames */
+ GRCR_ADDRAEN | /* Address A enable */
+ GRCR_RCVBAD | /* Rcv bad frames */
+ (GRCR_HASHSIZE << GRCR_HASHSIZE_SHIFT));
+ } else {
+ /* setup xmtcfg */
+ value = (GXCR_RESET | /* Always reset */
+ GXCR_XMTEN); /* Enable transmit */
+
+ slic_reg32_write(&slic_regs->slic_wxcfg, value, FLUSH);
+
+ /* Setup rcvcfg last */
+ value = (RcrReset | /* Reset, if linkchange */
+ GRCR_ADDRAEN | /* Address A enable */
+ GRCR_RCVBAD | /* Rcv bad frames */
+ (GRCR_HASHSIZE << GRCR_HASHSIZE_SHIFT));
+ }
+
+ if (adapter->state != ADAPT_DOWN) {
+ /* Only enable receive if we are restarting or running */
+ value |= GRCR_RCVEN;
+ }
+
+ if (adapter->macopts & MAC_PROMISC)
+ value |= GRCR_RCVALL;
+
+ slic_reg32_write(&slic_regs->slic_wrcfg, value, FLUSH);
+}
+
+/*
+ * Turn off RCV and XMT, power down PHY
+ */
+static void slic_config_clear(struct adapter *adapter)
+{
+ u32 value;
+ u32 phy_config;
+ __iomem struct slic_regs *slic_regs = adapter->slic_regs;
+
+ /* Setup xmtcfg */
+ value = (GXCR_RESET | /* Always reset */
+ GXCR_PAUSEEN); /* Enable pause */
+
+ slic_reg32_write(&slic_regs->slic_wxcfg, value, FLUSH);
+
+ value = (GRCR_RESET | /* Always reset */
+ GRCR_CTLEN | /* Enable CTL frames */
+ GRCR_ADDRAEN | /* Address A enable */
+ (GRCR_HASHSIZE << GRCR_HASHSIZE_SHIFT));
+
+ slic_reg32_write(&slic_regs->slic_wrcfg, value, FLUSH);
+
+ /* power down phy */
+ phy_config = (MIICR_REG_PCR | (PCR_POWERDOWN));
+ slic_reg32_write(&slic_regs->slic_wphy, phy_config, FLUSH);
+}
+
+static bool slic_mac_filter(struct adapter *adapter,
+ struct ether_header *ether_frame)
+{
+ struct net_device *netdev = adapter->netdev;
+ u32 opts = adapter->macopts;
+
+ if (opts & MAC_PROMISC)
+ return true;
+
+ if (is_broadcast_ether_addr(ether_frame->ether_dhost)) {
+ if (opts & MAC_BCAST) {
+ adapter->rcv_broadcasts++;
+ return true;
+ }
+
+ return false;
+ }
+
+ if (is_multicast_ether_addr(ether_frame->ether_dhost)) {
+ if (opts & MAC_ALLMCAST) {
+ adapter->rcv_multicasts++;
+ netdev->stats.multicast++;
+ return true;
+ }
+ if (opts & MAC_MCAST) {
+ struct mcast_address *mcaddr = adapter->mcastaddrs;
+
+ while (mcaddr) {
+ if (ether_addr_equal(mcaddr->address,
+ ether_frame->ether_dhost)) {
+ adapter->rcv_multicasts++;
+ netdev->stats.multicast++;
+ return true;
+ }
+ mcaddr = mcaddr->next;
+ }
+
+ return false;
+ }
+
+ return false;
+ }
+ if (opts & MAC_DIRECTED) {
+ adapter->rcv_unicasts++;
+ return true;
+ }
+ return false;
+
+}
+
+static int slic_mac_set_address(struct net_device *dev, void *ptr)
+{
+ struct adapter *adapter = netdev_priv(dev);
+ struct sockaddr *addr = ptr;
+
+ if (netif_running(dev))
+ return -EBUSY;
+ if (!adapter)
+ return -EBUSY;
+
+ if (!is_valid_ether_addr(addr->sa_data))
+ return -EINVAL;
+
+ memcpy(dev->dev_addr, addr->sa_data, dev->addr_len);
+ memcpy(adapter->currmacaddr, addr->sa_data, dev->addr_len);
+
+ slic_config_set(adapter, true);
+ return 0;
+}
+
+static void slic_timer_load_check(ulong cardaddr)
+{
+ struct sliccard *card = (struct sliccard *)cardaddr;
+ struct adapter *adapter = card->master;
+ u32 __iomem *intagg;
+ u32 load = card->events;
+ u32 level = 0;
+
+ if ((adapter) && (adapter->state == ADAPT_UP) &&
+ (card->state == CARD_UP) && (slic_global.dynamic_intagg)) {
+ intagg = &adapter->slic_regs->slic_intagg;
+ if (adapter->devid == SLIC_1GB_DEVICE_ID) {
+ if (adapter->linkspeed == LINK_1000MB)
+ level = 100;
+ else {
+ if (load > SLIC_LOAD_5)
+ level = SLIC_INTAGG_5;
+ else if (load > SLIC_LOAD_4)
+ level = SLIC_INTAGG_4;
+ else if (load > SLIC_LOAD_3)
+ level = SLIC_INTAGG_3;
+ else if (load > SLIC_LOAD_2)
+ level = SLIC_INTAGG_2;
+ else if (load > SLIC_LOAD_1)
+ level = SLIC_INTAGG_1;
+ else
+ level = SLIC_INTAGG_0;
+ }
+ if (card->loadlevel_current != level) {
+ card->loadlevel_current = level;
+ slic_reg32_write(intagg, level, FLUSH);
+ }
+ } else {
+ if (load > SLIC_LOAD_5)
+ level = SLIC_INTAGG_5;
+ else if (load > SLIC_LOAD_4)
+ level = SLIC_INTAGG_4;
+ else if (load > SLIC_LOAD_3)
+ level = SLIC_INTAGG_3;
+ else if (load > SLIC_LOAD_2)
+ level = SLIC_INTAGG_2;
+ else if (load > SLIC_LOAD_1)
+ level = SLIC_INTAGG_1;
+ else
+ level = SLIC_INTAGG_0;
+ if (card->loadlevel_current != level) {
+ card->loadlevel_current = level;
+ slic_reg32_write(intagg, level, FLUSH);
+ }
+ }
+ }
+ card->events = 0;
+ card->loadtimer.expires = jiffies + (SLIC_LOADTIMER_PERIOD * HZ);
+ add_timer(&card->loadtimer);
+}
+
+static int slic_upr_queue_request(struct adapter *adapter,
+ u32 upr_request,
+ u32 upr_data,
+ u32 upr_data_h,
+ u32 upr_buffer, u32 upr_buffer_h)
+{
+ struct slic_upr *upr;
+ struct slic_upr *uprqueue;
+
+ upr = kmalloc(sizeof(struct slic_upr), GFP_ATOMIC);
+ if (!upr)
+ return -ENOMEM;
+
+ upr->adapter = adapter->port;
+ upr->upr_request = upr_request;
+ upr->upr_data = upr_data;
+ upr->upr_buffer = upr_buffer;
+ upr->upr_data_h = upr_data_h;
+ upr->upr_buffer_h = upr_buffer_h;
+ upr->next = NULL;
+ if (adapter->upr_list) {
+ uprqueue = adapter->upr_list;
+
+ while (uprqueue->next)
+ uprqueue = uprqueue->next;
+ uprqueue->next = upr;
+ } else {
+ adapter->upr_list = upr;
+ }
+ return 0;
+}
+
+static void slic_upr_start(struct adapter *adapter)
+{
+ struct slic_upr *upr;
+ __iomem struct slic_regs *slic_regs = adapter->slic_regs;
+/*
+ char * ptr1;
+ char * ptr2;
+ uint cmdoffset;
+*/
+ upr = adapter->upr_list;
+ if (!upr)
+ return;
+ if (adapter->upr_busy)
+ return;
+ adapter->upr_busy = 1;
+
+ switch (upr->upr_request) {
+ case SLIC_UPR_STATS:
+ if (upr->upr_data_h == 0) {
+ slic_reg32_write(&slic_regs->slic_stats, upr->upr_data,
+ FLUSH);
+ } else {
+ slic_reg64_write(adapter, &slic_regs->slic_stats64,
+ upr->upr_data,
+ &slic_regs->slic_addr_upper,
+ upr->upr_data_h, FLUSH);
+ }
+ break;
+
+ case SLIC_UPR_RLSR:
+ slic_reg64_write(adapter, &slic_regs->slic_rlsr, upr->upr_data,
+ &slic_regs->slic_addr_upper, upr->upr_data_h,
+ FLUSH);
+ break;
+
+ case SLIC_UPR_RCONFIG:
+ slic_reg64_write(adapter, &slic_regs->slic_rconfig,
+ upr->upr_data, &slic_regs->slic_addr_upper,
+ upr->upr_data_h, FLUSH);
+ break;
+ case SLIC_UPR_PING:
+ slic_reg32_write(&slic_regs->slic_ping, 1, FLUSH);
+ break;
+ }
+}
+
+static int slic_upr_request(struct adapter *adapter,
+ u32 upr_request,
+ u32 upr_data,
+ u32 upr_data_h,
+ u32 upr_buffer, u32 upr_buffer_h)
+{
+ int rc;
+
+ spin_lock_irqsave(&adapter->upr_lock.lock, adapter->upr_lock.flags);
+ rc = slic_upr_queue_request(adapter,
+ upr_request,
+ upr_data,
+ upr_data_h, upr_buffer, upr_buffer_h);
+ if (rc)
+ goto err_unlock_irq;
+
+ slic_upr_start(adapter);
+err_unlock_irq:
+ spin_unlock_irqrestore(&adapter->upr_lock.lock,
+ adapter->upr_lock.flags);
+ return rc;
+}
+
+static void slic_link_upr_complete(struct adapter *adapter, u32 isr)
+{
+ u32 linkstatus = adapter->pshmem->linkstatus;
+ uint linkup;
+ unsigned char linkspeed;
+ unsigned char linkduplex;
+
+ if ((isr & ISR_UPCERR) || (isr & ISR_UPCBSY)) {
+ struct slic_shmem *pshmem;
+
+ pshmem = (struct slic_shmem *)(unsigned long)
+ adapter->phys_shmem;
+#if BITS_PER_LONG == 64
+ slic_upr_queue_request(adapter,
+ SLIC_UPR_RLSR,
+ SLIC_GET_ADDR_LOW(&pshmem->linkstatus),
+ SLIC_GET_ADDR_HIGH(&pshmem->linkstatus),
+ 0, 0);
+#else
+ slic_upr_queue_request(adapter,
+ SLIC_UPR_RLSR,
+ (u32) &pshmem->linkstatus,
+ SLIC_GET_ADDR_HIGH(pshmem), 0, 0);
+#endif
+ return;
+ }
+ if (adapter->state != ADAPT_UP)
+ return;
+
+ linkup = linkstatus & GIG_LINKUP ? LINK_UP : LINK_DOWN;
+ if (linkstatus & GIG_SPEED_1000)
+ linkspeed = LINK_1000MB;
+ else if (linkstatus & GIG_SPEED_100)
+ linkspeed = LINK_100MB;
+ else
+ linkspeed = LINK_10MB;
+
+ if (linkstatus & GIG_FULLDUPLEX)
+ linkduplex = LINK_FULLD;
+ else
+ linkduplex = LINK_HALFD;
+
+ if ((adapter->linkstate == LINK_DOWN) && (linkup == LINK_DOWN))
+ return;
+
+ /* link up event, but nothing has changed */
+ if ((adapter->linkstate == LINK_UP) &&
+ (linkup == LINK_UP) &&
+ (adapter->linkspeed == linkspeed) &&
+ (adapter->linkduplex == linkduplex))
+ return;
+
+ /* link has changed at this point */
+
+ /* link has gone from up to down */
+ if (linkup == LINK_DOWN) {
+ adapter->linkstate = LINK_DOWN;
+ return;
+ }
+
+ /* link has gone from down to up */
+ adapter->linkspeed = linkspeed;
+ adapter->linkduplex = linkduplex;
+
+ if (adapter->linkstate != LINK_UP) {
+ /* setup the mac */
+ slic_config_set(adapter, true);
+ adapter->linkstate = LINK_UP;
+ netif_start_queue(adapter->netdev);
+ }
+}
+
+static void slic_upr_request_complete(struct adapter *adapter, u32 isr)
+{
+ struct sliccard *card = adapter->card;
+ struct slic_upr *upr;
+
+ spin_lock_irqsave(&adapter->upr_lock.lock, adapter->upr_lock.flags);
+ upr = adapter->upr_list;
+ if (!upr) {
+ spin_unlock_irqrestore(&adapter->upr_lock.lock,
+ adapter->upr_lock.flags);
+ return;
+ }
+ adapter->upr_list = upr->next;
+ upr->next = NULL;
+ adapter->upr_busy = 0;
+ switch (upr->upr_request) {
+ case SLIC_UPR_STATS:
+ {
+ struct slic_stats *slicstats =
+ (struct slic_stats *) &adapter->pshmem->inicstats;
+ struct slic_stats *newstats = slicstats;
+ struct slic_stats *old = &adapter->inicstats_prev;
+ struct slicnet_stats *stst = &adapter->slic_stats;
+
+ if (isr & ISR_UPCERR) {
+ dev_err(&adapter->netdev->dev,
+ "SLIC_UPR_STATS command failed isr[%x]\n",
+ isr);
+
+ break;
+ }
+ UPDATE_STATS_GB(stst->tcp.xmit_tcp_segs,
+ newstats->xmit_tcp_segs_gb,
+ old->xmit_tcp_segs_gb);
+
+ UPDATE_STATS_GB(stst->tcp.xmit_tcp_bytes,
+ newstats->xmit_tcp_bytes_gb,
+ old->xmit_tcp_bytes_gb);
+
+ UPDATE_STATS_GB(stst->tcp.rcv_tcp_segs,
+ newstats->rcv_tcp_segs_gb,
+ old->rcv_tcp_segs_gb);
+
+ UPDATE_STATS_GB(stst->tcp.rcv_tcp_bytes,
+ newstats->rcv_tcp_bytes_gb,
+ old->rcv_tcp_bytes_gb);
+
+ UPDATE_STATS_GB(stst->iface.xmt_bytes,
+ newstats->xmit_bytes_gb,
+ old->xmit_bytes_gb);
+
+ UPDATE_STATS_GB(stst->iface.xmt_ucast,
+ newstats->xmit_unicasts_gb,
+ old->xmit_unicasts_gb);
+
+ UPDATE_STATS_GB(stst->iface.rcv_bytes,
+ newstats->rcv_bytes_gb,
+ old->rcv_bytes_gb);
+
+ UPDATE_STATS_GB(stst->iface.rcv_ucast,
+ newstats->rcv_unicasts_gb,
+ old->rcv_unicasts_gb);
+
+ UPDATE_STATS_GB(stst->iface.xmt_errors,
+ newstats->xmit_collisions_gb,
+ old->xmit_collisions_gb);
+
+ UPDATE_STATS_GB(stst->iface.xmt_errors,
+ newstats->xmit_excess_collisions_gb,
+ old->xmit_excess_collisions_gb);
+
+ UPDATE_STATS_GB(stst->iface.xmt_errors,
+ newstats->xmit_other_error_gb,
+ old->xmit_other_error_gb);
+
+ UPDATE_STATS_GB(stst->iface.rcv_errors,
+ newstats->rcv_other_error_gb,
+ old->rcv_other_error_gb);
+
+ UPDATE_STATS_GB(stst->iface.rcv_discards,
+ newstats->rcv_drops_gb,
+ old->rcv_drops_gb);
+
+ if (newstats->rcv_drops_gb > old->rcv_drops_gb) {
+ adapter->rcv_drops +=
+ (newstats->rcv_drops_gb -
+ old->rcv_drops_gb);
+ }
+ memcpy(old, newstats, sizeof(struct slic_stats));
+ break;
+ }
+ case SLIC_UPR_RLSR:
+ slic_link_upr_complete(adapter, isr);
+ break;
+ case SLIC_UPR_RCONFIG:
+ break;
+ case SLIC_UPR_PING:
+ card->pingstatus |= (isr & ISR_PINGDSMASK);
+ break;
+ }
+ kfree(upr);
+ slic_upr_start(adapter);
+ spin_unlock_irqrestore(&adapter->upr_lock.lock,
+ adapter->upr_lock.flags);
+}
+
+static int slic_config_get(struct adapter *adapter, u32 config, u32 config_h)
+{
+ return slic_upr_request(adapter, SLIC_UPR_RCONFIG, config, config_h,
+ 0, 0);
+}
+
+/*
+ * Compute a checksum of the EEPROM according to RFC 1071.
+ */
+static u16 slic_eeprom_cksum(void *eeprom, unsigned len)
+{
+ u16 *wp = eeprom;
+ u32 checksum = 0;
+
+ while (len > 1) {
+ checksum += *(wp++);
+ len -= 2;
+ }
+
+ if (len > 0)
+ checksum += *(u8 *) wp;
+
+
+ while (checksum >> 16)
+ checksum = (checksum & 0xFFFF) + ((checksum >> 16) & 0xFFFF);
+
+ return ~checksum;
+}
+
+static void slic_rspqueue_free(struct adapter *adapter)
+{
+ int i;
+ struct slic_rspqueue *rspq = &adapter->rspqueue;
+
+ for (i = 0; i < rspq->num_pages; i++) {
+ if (rspq->vaddr[i]) {
+ pci_free_consistent(adapter->pcidev, PAGE_SIZE,
+ rspq->vaddr[i], rspq->paddr[i]);
+ }
+ rspq->vaddr[i] = NULL;
+ rspq->paddr[i] = 0;
+ }
+ rspq->offset = 0;
+ rspq->pageindex = 0;
+ rspq->rspbuf = NULL;
+}
+
+static int slic_rspqueue_init(struct adapter *adapter)
+{
+ int i;
+ struct slic_rspqueue *rspq = &adapter->rspqueue;
+ __iomem struct slic_regs *slic_regs = adapter->slic_regs;
+ u32 paddrh = 0;
+
+ memset(rspq, 0, sizeof(struct slic_rspqueue));
+
+ rspq->num_pages = SLIC_RSPQ_PAGES_GB;
+
+ for (i = 0; i < rspq->num_pages; i++) {
+ rspq->vaddr[i] = pci_zalloc_consistent(adapter->pcidev,
+ PAGE_SIZE,
+ &rspq->paddr[i]);
+ if (!rspq->vaddr[i]) {
+ dev_err(&adapter->pcidev->dev,
+ "pci_alloc_consistent failed\n");
+ slic_rspqueue_free(adapter);
+ return -ENOMEM;
+ }
+
+ if (paddrh == 0) {
+ slic_reg32_write(&slic_regs->slic_rbar,
+ (rspq->paddr[i] | SLIC_RSPQ_BUFSINPAGE),
+ DONT_FLUSH);
+ } else {
+ slic_reg64_write(adapter, &slic_regs->slic_rbar64,
+ (rspq->paddr[i] | SLIC_RSPQ_BUFSINPAGE),
+ &slic_regs->slic_addr_upper,
+ paddrh, DONT_FLUSH);
+ }
+ }
+ rspq->offset = 0;
+ rspq->pageindex = 0;
+ rspq->rspbuf = (struct slic_rspbuf *)rspq->vaddr[0];
+ return 0;
+}
+
+static struct slic_rspbuf *slic_rspqueue_getnext(struct adapter *adapter)
+{
+ struct slic_rspqueue *rspq = &adapter->rspqueue;
+ struct slic_rspbuf *buf;
+
+ if (!(rspq->rspbuf->status))
+ return NULL;
+
+ buf = rspq->rspbuf;
+ if (++rspq->offset < SLIC_RSPQ_BUFSINPAGE) {
+ rspq->rspbuf++;
+ } else {
+ slic_reg64_write(adapter, &adapter->slic_regs->slic_rbar64,
+ (rspq->paddr[rspq->pageindex] | SLIC_RSPQ_BUFSINPAGE),
+ &adapter->slic_regs->slic_addr_upper, 0, DONT_FLUSH);
+ rspq->pageindex = (rspq->pageindex + 1) % rspq->num_pages;
+ rspq->offset = 0;
+ rspq->rspbuf = (struct slic_rspbuf *)
+ rspq->vaddr[rspq->pageindex];
+ }
+
+ return buf;
+}
+
+static void slic_cmdqmem_free(struct adapter *adapter)
+{
+ struct slic_cmdqmem *cmdqmem = &adapter->cmdqmem;
+ int i;
+
+ for (i = 0; i < SLIC_CMDQ_MAXPAGES; i++) {
+ if (cmdqmem->pages[i]) {
+ pci_free_consistent(adapter->pcidev,
+ PAGE_SIZE,
+ (void *) cmdqmem->pages[i],
+ cmdqmem->dma_pages[i]);
+ }
+ }
+ memset(cmdqmem, 0, sizeof(struct slic_cmdqmem));
+}
+
+static u32 *slic_cmdqmem_addpage(struct adapter *adapter)
+{
+ struct slic_cmdqmem *cmdqmem = &adapter->cmdqmem;
+ u32 *pageaddr;
+
+ if (cmdqmem->pagecnt >= SLIC_CMDQ_MAXPAGES)
+ return NULL;
+ pageaddr = pci_alloc_consistent(adapter->pcidev,
+ PAGE_SIZE,
+ &cmdqmem->dma_pages[cmdqmem->pagecnt]);
+ if (!pageaddr)
+ return NULL;
+
+ cmdqmem->pages[cmdqmem->pagecnt] = pageaddr;
+ cmdqmem->pagecnt++;
+ return pageaddr;
+}
+
+static void slic_cmdq_free(struct adapter *adapter)
+{
+ struct slic_hostcmd *cmd;
+
+ cmd = adapter->cmdq_all.head;
+ while (cmd) {
+ if (cmd->busy) {
+ struct sk_buff *tempskb;
+
+ tempskb = cmd->skb;
+ if (tempskb) {
+ cmd->skb = NULL;
+ dev_kfree_skb_irq(tempskb);
+ }
+ }
+ cmd = cmd->next_all;
+ }
+ memset(&adapter->cmdq_all, 0, sizeof(struct slic_cmdqueue));
+ memset(&adapter->cmdq_free, 0, sizeof(struct slic_cmdqueue));
+ memset(&adapter->cmdq_done, 0, sizeof(struct slic_cmdqueue));
+ slic_cmdqmem_free(adapter);
+}
+
+static void slic_cmdq_addcmdpage(struct adapter *adapter, u32 *page)
+{
+ struct slic_hostcmd *cmd;
+ struct slic_hostcmd *prev;
+ struct slic_hostcmd *tail;
+ struct slic_cmdqueue *cmdq;
+ int cmdcnt;
+ void *cmdaddr;
+ ulong phys_addr;
+ u32 phys_addrl;
+ u32 phys_addrh;
+ struct slic_handle *pslic_handle;
+
+ cmdaddr = page;
+ cmd = (struct slic_hostcmd *)cmdaddr;
+ cmdcnt = 0;
+
+ phys_addr = virt_to_bus((void *)page);
+ phys_addrl = SLIC_GET_ADDR_LOW(phys_addr);
+ phys_addrh = SLIC_GET_ADDR_HIGH(phys_addr);
+
+ prev = NULL;
+ tail = cmd;
+ while ((cmdcnt < SLIC_CMDQ_CMDSINPAGE) &&
+ (adapter->slic_handle_ix < 256)) {
+ /* Allocate and initialize a SLIC_HANDLE for this command */
+ spin_lock_irqsave(&adapter->handle_lock.lock,
+ adapter->handle_lock.flags);
+ pslic_handle = adapter->pfree_slic_handles;
+ adapter->pfree_slic_handles = pslic_handle->next;
+ spin_unlock_irqrestore(&adapter->handle_lock.lock,
+ adapter->handle_lock.flags);
+ pslic_handle->type = SLIC_HANDLE_CMD;
+ pslic_handle->address = (void *) cmd;
+ pslic_handle->offset = (ushort) adapter->slic_handle_ix++;
+ pslic_handle->other_handle = NULL;
+ pslic_handle->next = NULL;
+
+ cmd->pslic_handle = pslic_handle;
+ cmd->cmd64.hosthandle = pslic_handle->token.handle_token;
+ cmd->busy = false;
+ cmd->paddrl = phys_addrl;
+ cmd->paddrh = phys_addrh;
+ cmd->next_all = prev;
+ cmd->next = prev;
+ prev = cmd;
+ phys_addrl += SLIC_HOSTCMD_SIZE;
+ cmdaddr += SLIC_HOSTCMD_SIZE;
+
+ cmd = (struct slic_hostcmd *)cmdaddr;
+ cmdcnt++;
+ }
+
+ cmdq = &adapter->cmdq_all;
+ cmdq->count += cmdcnt; /* SLIC_CMDQ_CMDSINPAGE; mooktodo */
+ tail->next_all = cmdq->head;
+ cmdq->head = prev;
+ cmdq = &adapter->cmdq_free;
+ spin_lock_irqsave(&cmdq->lock.lock, cmdq->lock.flags);
+ cmdq->count += cmdcnt; /* SLIC_CMDQ_CMDSINPAGE; mooktodo */
+ tail->next = cmdq->head;
+ cmdq->head = prev;
+ spin_unlock_irqrestore(&cmdq->lock.lock, cmdq->lock.flags);
+}
+
+static int slic_cmdq_init(struct adapter *adapter)
+{
+ int i;
+ u32 *pageaddr;
+
+ memset(&adapter->cmdq_all, 0, sizeof(struct slic_cmdqueue));
+ memset(&adapter->cmdq_free, 0, sizeof(struct slic_cmdqueue));
+ memset(&adapter->cmdq_done, 0, sizeof(struct slic_cmdqueue));
+ spin_lock_init(&adapter->cmdq_all.lock.lock);
+ spin_lock_init(&adapter->cmdq_free.lock.lock);
+ spin_lock_init(&adapter->cmdq_done.lock.lock);
+ memset(&adapter->cmdqmem, 0, sizeof(struct slic_cmdqmem));
+ adapter->slic_handle_ix = 1;
+ for (i = 0; i < SLIC_CMDQ_INITPAGES; i++) {
+ pageaddr = slic_cmdqmem_addpage(adapter);
+ if (!pageaddr) {
+ slic_cmdq_free(adapter);
+ return -ENOMEM;
+ }
+ slic_cmdq_addcmdpage(adapter, pageaddr);
+ }
+ adapter->slic_handle_ix = 1;
+
+ return 0;
+}
+
+static void slic_cmdq_reset(struct adapter *adapter)
+{
+ struct slic_hostcmd *hcmd;
+ struct sk_buff *skb;
+ u32 outstanding;
+
+ spin_lock_irqsave(&adapter->cmdq_free.lock.lock,
+ adapter->cmdq_free.lock.flags);
+ spin_lock_irqsave(&adapter->cmdq_done.lock.lock,
+ adapter->cmdq_done.lock.flags);
+ outstanding = adapter->cmdq_all.count - adapter->cmdq_done.count;
+ outstanding -= adapter->cmdq_free.count;
+ hcmd = adapter->cmdq_all.head;
+ while (hcmd) {
+ if (hcmd->busy) {
+ skb = hcmd->skb;
+ hcmd->busy = 0;
+ hcmd->skb = NULL;
+ dev_kfree_skb_irq(skb);
+ }
+ hcmd = hcmd->next_all;
+ }
+ adapter->cmdq_free.count = 0;
+ adapter->cmdq_free.head = NULL;
+ adapter->cmdq_free.tail = NULL;
+ adapter->cmdq_done.count = 0;
+ adapter->cmdq_done.head = NULL;
+ adapter->cmdq_done.tail = NULL;
+ adapter->cmdq_free.head = adapter->cmdq_all.head;
+ hcmd = adapter->cmdq_all.head;
+ while (hcmd) {
+ adapter->cmdq_free.count++;
+ hcmd->next = hcmd->next_all;
+ hcmd = hcmd->next_all;
+ }
+ if (adapter->cmdq_free.count != adapter->cmdq_all.count) {
+ dev_err(&adapter->netdev->dev,
+ "free_count %d != all count %d\n",
+ adapter->cmdq_free.count, adapter->cmdq_all.count);
+ }
+ spin_unlock_irqrestore(&adapter->cmdq_done.lock.lock,
+ adapter->cmdq_done.lock.flags);
+ spin_unlock_irqrestore(&adapter->cmdq_free.lock.lock,
+ adapter->cmdq_free.lock.flags);
+}
+
+static void slic_cmdq_getdone(struct adapter *adapter)
+{
+ struct slic_cmdqueue *done_cmdq = &adapter->cmdq_done;
+ struct slic_cmdqueue *free_cmdq = &adapter->cmdq_free;
+
+ spin_lock_irqsave(&done_cmdq->lock.lock, done_cmdq->lock.flags);
+
+ free_cmdq->head = done_cmdq->head;
+ free_cmdq->count = done_cmdq->count;
+ done_cmdq->head = NULL;
+ done_cmdq->tail = NULL;
+ done_cmdq->count = 0;
+ spin_unlock_irqrestore(&done_cmdq->lock.lock, done_cmdq->lock.flags);
+}
+
+static struct slic_hostcmd *slic_cmdq_getfree(struct adapter *adapter)
+{
+ struct slic_cmdqueue *cmdq = &adapter->cmdq_free;
+ struct slic_hostcmd *cmd = NULL;
+
+lock_and_retry:
+ spin_lock_irqsave(&cmdq->lock.lock, cmdq->lock.flags);
+retry:
+ cmd = cmdq->head;
+ if (cmd) {
+ cmdq->head = cmd->next;
+ cmdq->count--;
+ spin_unlock_irqrestore(&cmdq->lock.lock, cmdq->lock.flags);
+ } else {
+ slic_cmdq_getdone(adapter);
+ cmd = cmdq->head;
+ if (cmd) {
+ goto retry;
+ } else {
+ u32 *pageaddr;
+
+ spin_unlock_irqrestore(&cmdq->lock.lock,
+ cmdq->lock.flags);
+ pageaddr = slic_cmdqmem_addpage(adapter);
+ if (pageaddr) {
+ slic_cmdq_addcmdpage(adapter, pageaddr);
+ goto lock_and_retry;
+ }
+ }
+ }
+ return cmd;
+}
+
+static void slic_cmdq_putdone_irq(struct adapter *adapter,
+ struct slic_hostcmd *cmd)
+{
+ struct slic_cmdqueue *cmdq = &adapter->cmdq_done;
+
+ spin_lock(&cmdq->lock.lock);
+ cmd->busy = 0;
+ cmd->next = cmdq->head;
+ cmdq->head = cmd;
+ cmdq->count++;
+ if ((adapter->xmitq_full) && (cmdq->count > 10))
+ netif_wake_queue(adapter->netdev);
+ spin_unlock(&cmdq->lock.lock);
+}
+
+static int slic_rcvqueue_fill(struct adapter *adapter)
+{
+ void *paddr;
+ u32 paddrl;
+ u32 paddrh;
+ struct slic_rcvqueue *rcvq = &adapter->rcvqueue;
+ int i = 0;
+ struct device *dev = &adapter->netdev->dev;
+
+ while (i < SLIC_RCVQ_FILLENTRIES) {
+ struct slic_rcvbuf *rcvbuf;
+ struct sk_buff *skb;
+#ifdef KLUDGE_FOR_4GB_BOUNDARY
+retry_rcvqfill:
+#endif
+ skb = alloc_skb(SLIC_RCVQ_RCVBUFSIZE, GFP_ATOMIC);
+ if (skb) {
+ paddr = (void *)(unsigned long)
+ pci_map_single(adapter->pcidev,
+ skb->data,
+ SLIC_RCVQ_RCVBUFSIZE,
+ PCI_DMA_FROMDEVICE);
+ paddrl = SLIC_GET_ADDR_LOW(paddr);
+ paddrh = SLIC_GET_ADDR_HIGH(paddr);
+
+ skb->len = SLIC_RCVBUF_HEADSIZE;
+ rcvbuf = (struct slic_rcvbuf *)skb->head;
+ rcvbuf->status = 0;
+ skb->next = NULL;
+#ifdef KLUDGE_FOR_4GB_BOUNDARY
+ if (paddrl == 0) {
+ dev_err(dev, "%s: LOW 32bits PHYSICAL ADDRESS == 0\n",
+ __func__);
+ dev_err(dev, "skb[%p] PROBLEM\n", skb);
+ dev_err(dev, " skbdata[%p]\n",
+ skb->data);
+ dev_err(dev, " skblen[%x]\n", skb->len);
+ dev_err(dev, " paddr[%p]\n", paddr);
+ dev_err(dev, " paddrl[%x]\n", paddrl);
+ dev_err(dev, " paddrh[%x]\n", paddrh);
+ dev_err(dev, " rcvq->head[%p]\n",
+ rcvq->head);
+ dev_err(dev, " rcvq->tail[%p]\n",
+ rcvq->tail);
+ dev_err(dev, " rcvq->count[%x]\n",
+ rcvq->count);
+ dev_err(dev, "SKIP THIS SKB!!!!!!!!\n");
+ goto retry_rcvqfill;
+ }
+#else
+ if (paddrl == 0) {
+ dev_err(dev, "%s: LOW 32bits PHYSICAL ADDRESS == 0\n",
+ __func__);
+ dev_err(dev, "skb[%p] PROBLEM\n", skb);
+ dev_err(dev, " skbdata[%p]\n",
+ skb->data);
+ dev_err(dev, " skblen[%x]\n", skb->len);
+ dev_err(dev, " paddr[%p]\n", paddr);
+ dev_err(dev, " paddrl[%x]\n", paddrl);
+ dev_err(dev, " paddrh[%x]\n", paddrh);
+ dev_err(dev, " rcvq->head[%p]\n",
+ rcvq->head);
+ dev_err(dev, " rcvq->tail[%p]\n",
+ rcvq->tail);
+ dev_err(dev, " rcvq->count[%x]\n",
+ rcvq->count);
+ dev_err(dev, "GIVE TO CARD ANYWAY\n");
+ }
+#endif
+ if (paddrh == 0) {
+ slic_reg32_write(&adapter->slic_regs->slic_hbar,
+ (u32)paddrl, DONT_FLUSH);
+ } else {
+ slic_reg64_write(adapter,
+ &adapter->slic_regs->slic_hbar64,
+ paddrl,
+ &adapter->slic_regs->slic_addr_upper,
+ paddrh, DONT_FLUSH);
+ }
+ if (rcvq->head)
+ rcvq->tail->next = skb;
+ else
+ rcvq->head = skb;
+ rcvq->tail = skb;
+ rcvq->count++;
+ i++;
+ } else {
+ dev_err(&adapter->netdev->dev,
+ "slic_rcvqueue_fill could only get [%d] skbuffs\n",
+ i);
+ break;
+ }
+ }
+ return i;
+}
+
+static void slic_rcvqueue_free(struct adapter *adapter)
+{
+ struct slic_rcvqueue *rcvq = &adapter->rcvqueue;
+ struct sk_buff *skb;
+
+ while (rcvq->head) {
+ skb = rcvq->head;
+ rcvq->head = rcvq->head->next;
+ dev_kfree_skb(skb);
+ }
+ rcvq->tail = NULL;
+ rcvq->head = NULL;
+ rcvq->count = 0;
+}
+
+static int slic_rcvqueue_init(struct adapter *adapter)
+{
+ int i, count;
+ struct slic_rcvqueue *rcvq = &adapter->rcvqueue;
+
+ rcvq->tail = NULL;
+ rcvq->head = NULL;
+ rcvq->size = SLIC_RCVQ_ENTRIES;
+ rcvq->errors = 0;
+ rcvq->count = 0;
+ i = SLIC_RCVQ_ENTRIES / SLIC_RCVQ_FILLENTRIES;
+ count = 0;
+ while (i) {
+ count += slic_rcvqueue_fill(adapter);
+ i--;
+ }
+ if (rcvq->count < SLIC_RCVQ_MINENTRIES) {
+ slic_rcvqueue_free(adapter);
+ return -ENOMEM;
+ }
+ return 0;
+}
+
+static struct sk_buff *slic_rcvqueue_getnext(struct adapter *adapter)
+{
+ struct slic_rcvqueue *rcvq = &adapter->rcvqueue;
+ struct sk_buff *skb;
+ struct slic_rcvbuf *rcvbuf;
+ int count;
+
+ if (rcvq->count) {
+ skb = rcvq->head;
+ rcvbuf = (struct slic_rcvbuf *)skb->head;
+
+ if (rcvbuf->status & IRHDDR_SVALID) {
+ rcvq->head = rcvq->head->next;
+ skb->next = NULL;
+ rcvq->count--;
+ } else {
+ skb = NULL;
+ }
+ } else {
+ dev_err(&adapter->netdev->dev,
+ "RcvQ Empty!! rcvq[%p] count[%x]\n", rcvq, rcvq->count);
+ skb = NULL;
+ }
+ while (rcvq->count < SLIC_RCVQ_FILLTHRESH) {
+ count = slic_rcvqueue_fill(adapter);
+ if (!count)
+ break;
+ }
+ if (skb)
+ rcvq->errors = 0;
+ return skb;
+}
+
+static u32 slic_rcvqueue_reinsert(struct adapter *adapter, struct sk_buff *skb)
+{
+ struct slic_rcvqueue *rcvq = &adapter->rcvqueue;
+ void *paddr;
+ u32 paddrl;
+ u32 paddrh;
+ struct slic_rcvbuf *rcvbuf = (struct slic_rcvbuf *)skb->head;
+ struct device *dev;
+
+ paddr = (void *)(unsigned long)
+ pci_map_single(adapter->pcidev, skb->head,
+ SLIC_RCVQ_RCVBUFSIZE, PCI_DMA_FROMDEVICE);
+ rcvbuf->status = 0;
+ skb->next = NULL;
+
+ paddrl = SLIC_GET_ADDR_LOW(paddr);
+ paddrh = SLIC_GET_ADDR_HIGH(paddr);
+
+ if (paddrl == 0) {
+ dev = &adapter->netdev->dev;
+ dev_err(dev, "%s: LOW 32bits PHYSICAL ADDRESS == 0\n",
+ __func__);
+ dev_err(dev, "skb[%p] PROBLEM\n", skb);
+ dev_err(dev, " skbdata[%p]\n", skb->data);
+ dev_err(dev, " skblen[%x]\n", skb->len);
+ dev_err(dev, " paddr[%p]\n", paddr);
+ dev_err(dev, " paddrl[%x]\n", paddrl);
+ dev_err(dev, " paddrh[%x]\n", paddrh);
+ dev_err(dev, " rcvq->head[%p]\n", rcvq->head);
+ dev_err(dev, " rcvq->tail[%p]\n", rcvq->tail);
+ dev_err(dev, " rcvq->count[%x]\n", rcvq->count);
+ }
+ if (paddrh == 0) {
+ slic_reg32_write(&adapter->slic_regs->slic_hbar, (u32)paddrl,
+ DONT_FLUSH);
+ } else {
+ slic_reg64_write(adapter, &adapter->slic_regs->slic_hbar64,
+ paddrl, &adapter->slic_regs->slic_addr_upper,
+ paddrh, DONT_FLUSH);
+ }
+ if (rcvq->head)
+ rcvq->tail->next = skb;
+ else
+ rcvq->head = skb;
+ rcvq->tail = skb;
+ rcvq->count++;
+ return rcvq->count;
+}
+
+/*
+ * slic_link_event_handler -
+ *
+ * Initiate a link configuration sequence. The link configuration begins
+ * by issuing a READ_LINK_STATUS command to the Utility Processor on the
+ * SLIC. Since the command finishes asynchronously, the slic_upr_comlete
+ * routine will follow it up witha UP configuration write command, which
+ * will also complete asynchronously.
+ *
+ */
+static void slic_link_event_handler(struct adapter *adapter)
+{
+ int status;
+ struct slic_shmem *pshmem;
+
+ if (adapter->state != ADAPT_UP) {
+ /* Adapter is not operational. Ignore. */
+ return;
+ }
+
+ pshmem = (struct slic_shmem *)(unsigned long)adapter->phys_shmem;
+
+#if BITS_PER_LONG == 64
+ status = slic_upr_request(adapter,
+ SLIC_UPR_RLSR,
+ SLIC_GET_ADDR_LOW(&pshmem->linkstatus),
+ SLIC_GET_ADDR_HIGH(&pshmem->linkstatus),
+ 0, 0);
+#else
+ status = slic_upr_request(adapter, SLIC_UPR_RLSR,
+ (u32) &pshmem->linkstatus, /* no 4GB wrap guaranteed */
+ 0, 0, 0);
+#endif
+}
+
+static void slic_init_cleanup(struct adapter *adapter)
+{
+ if (adapter->intrregistered) {
+ adapter->intrregistered = 0;
+ free_irq(adapter->netdev->irq, adapter->netdev);
+
+ }
+ if (adapter->pshmem) {
+ pci_free_consistent(adapter->pcidev,
+ sizeof(struct slic_shmem),
+ adapter->pshmem, adapter->phys_shmem);
+ adapter->pshmem = NULL;
+ adapter->phys_shmem = (dma_addr_t)(unsigned long)NULL;
+ }
+
+ if (adapter->pingtimerset) {
+ adapter->pingtimerset = 0;
+ del_timer(&adapter->pingtimer);
+ }
+
+ slic_rspqueue_free(adapter);
+ slic_cmdq_free(adapter);
+ slic_rcvqueue_free(adapter);
+}
+
+/*
+ * Allocate a mcast_address structure to hold the multicast address.
+ * Link it in.
+ */
+static int slic_mcast_add_list(struct adapter *adapter, char *address)
+{
+ struct mcast_address *mcaddr, *mlist;
+
+ /* Check to see if it already exists */
+ mlist = adapter->mcastaddrs;
+ while (mlist) {
+ if (ether_addr_equal(mlist->address, address))
+ return 0;
+ mlist = mlist->next;
+ }
+
+ /* Doesn't already exist. Allocate a structure to hold it */
+ mcaddr = kmalloc(sizeof(struct mcast_address), GFP_ATOMIC);
+ if (mcaddr == NULL)
+ return 1;
+
+ ether_addr_copy(mcaddr->address, address);
+
+ mcaddr->next = adapter->mcastaddrs;
+ adapter->mcastaddrs = mcaddr;
+
+ return 0;
+}
+
+static void slic_mcast_set_list(struct net_device *dev)
+{
+ struct adapter *adapter = netdev_priv(dev);
+ int status = 0;
+ char *addresses;
+ struct netdev_hw_addr *ha;
+
+ netdev_for_each_mc_addr(ha, dev) {
+ addresses = (char *) &ha->addr;
+ status = slic_mcast_add_list(adapter, addresses);
+ if (status != 0)
+ break;
+ slic_mcast_set_bit(adapter, addresses);
+ }
+
+ if (adapter->devflags_prev != dev->flags) {
+ adapter->macopts = MAC_DIRECTED;
+ if (dev->flags) {
+ if (dev->flags & IFF_BROADCAST)
+ adapter->macopts |= MAC_BCAST;
+ if (dev->flags & IFF_PROMISC)
+ adapter->macopts |= MAC_PROMISC;
+ if (dev->flags & IFF_ALLMULTI)
+ adapter->macopts |= MAC_ALLMCAST;
+ if (dev->flags & IFF_MULTICAST)
+ adapter->macopts |= MAC_MCAST;
+ }
+ adapter->devflags_prev = dev->flags;
+ slic_config_set(adapter, true);
+ } else {
+ if (status == 0)
+ slic_mcast_set_mask(adapter);
+ }
+}
+
+#define XMIT_FAIL_LINK_STATE 1
+#define XMIT_FAIL_ZERO_LENGTH 2
+#define XMIT_FAIL_HOSTCMD_FAIL 3
+
+static void slic_xmit_build_request(struct adapter *adapter,
+ struct slic_hostcmd *hcmd, struct sk_buff *skb)
+{
+ struct slic_host64_cmd *ihcmd;
+ ulong phys_addr;
+
+ ihcmd = &hcmd->cmd64;
+
+ ihcmd->flags = adapter->port << IHFLG_IFSHFT;
+ ihcmd->command = IHCMD_XMT_REQ;
+ ihcmd->u.slic_buffers.totlen = skb->len;
+ phys_addr = pci_map_single(adapter->pcidev, skb->data, skb->len,
+ PCI_DMA_TODEVICE);
+ ihcmd->u.slic_buffers.bufs[0].paddrl = SLIC_GET_ADDR_LOW(phys_addr);
+ ihcmd->u.slic_buffers.bufs[0].paddrh = SLIC_GET_ADDR_HIGH(phys_addr);
+ ihcmd->u.slic_buffers.bufs[0].length = skb->len;
+#if BITS_PER_LONG == 64
+ hcmd->cmdsize = (u32) ((((u64)&ihcmd->u.slic_buffers.bufs[1] -
+ (u64) hcmd) + 31) >> 5);
+#else
+ hcmd->cmdsize = (((u32)&ihcmd->u.slic_buffers.bufs[1] -
+ (u32)hcmd) + 31) >> 5;
+#endif
+}
+
+static void slic_xmit_fail(struct adapter *adapter,
+ struct sk_buff *skb,
+ void *cmd, u32 skbtype, u32 status)
+{
+ if (adapter->xmitq_full)
+ netif_stop_queue(adapter->netdev);
+ if ((cmd == NULL) && (status <= XMIT_FAIL_HOSTCMD_FAIL)) {
+ switch (status) {
+ case XMIT_FAIL_LINK_STATE:
+ dev_err(&adapter->netdev->dev,
+ "reject xmit skb[%p: %x] linkstate[%s] adapter[%s:%d] card[%s:%d]\n",
+ skb, skb->pkt_type,
+ SLIC_LINKSTATE(adapter->linkstate),
+ SLIC_ADAPTER_STATE(adapter->state),
+ adapter->state,
+ SLIC_CARD_STATE(adapter->card->state),
+ adapter->card->state);
+ break;
+ case XMIT_FAIL_ZERO_LENGTH:
+ dev_err(&adapter->netdev->dev,
+ "xmit_start skb->len == 0 skb[%p] type[%x]\n",
+ skb, skb->pkt_type);
+ break;
+ case XMIT_FAIL_HOSTCMD_FAIL:
+ dev_err(&adapter->netdev->dev,
+ "xmit_start skb[%p] type[%x] No host commands available\n",
+ skb, skb->pkt_type);
+ break;
+ }
+ }
+ dev_kfree_skb(skb);
+ adapter->netdev->stats.tx_dropped++;
+}
+
+static void slic_rcv_handle_error(struct adapter *adapter,
+ struct slic_rcvbuf *rcvbuf)
+{
+ struct slic_hddr_wds *hdr = (struct slic_hddr_wds *)rcvbuf->data;
+ struct net_device *netdev = adapter->netdev;
+
+ if (adapter->devid != SLIC_1GB_DEVICE_ID) {
+ if (hdr->frame_status14 & VRHSTAT_802OE)
+ adapter->if_events.oflow802++;
+ if (hdr->frame_status14 & VRHSTAT_TPOFLO)
+ adapter->if_events.Tprtoflow++;
+ if (hdr->frame_status_b14 & VRHSTATB_802UE)
+ adapter->if_events.uflow802++;
+ if (hdr->frame_status_b14 & VRHSTATB_RCVE) {
+ adapter->if_events.rcvearly++;
+ netdev->stats.rx_fifo_errors++;
+ }
+ if (hdr->frame_status_b14 & VRHSTATB_BUFF) {
+ adapter->if_events.Bufov++;
+ netdev->stats.rx_over_errors++;
+ }
+ if (hdr->frame_status_b14 & VRHSTATB_CARRE) {
+ adapter->if_events.Carre++;
+ netdev->stats.tx_carrier_errors++;
+ }
+ if (hdr->frame_status_b14 & VRHSTATB_LONGE)
+ adapter->if_events.Longe++;
+ if (hdr->frame_status_b14 & VRHSTATB_PREA)
+ adapter->if_events.Invp++;
+ if (hdr->frame_status_b14 & VRHSTATB_CRC) {
+ adapter->if_events.Crc++;
+ netdev->stats.rx_crc_errors++;
+ }
+ if (hdr->frame_status_b14 & VRHSTATB_DRBL)
+ adapter->if_events.Drbl++;
+ if (hdr->frame_status_b14 & VRHSTATB_CODE)
+ adapter->if_events.Code++;
+ if (hdr->frame_status_b14 & VRHSTATB_TPCSUM)
+ adapter->if_events.TpCsum++;
+ if (hdr->frame_status_b14 & VRHSTATB_TPHLEN)
+ adapter->if_events.TpHlen++;
+ if (hdr->frame_status_b14 & VRHSTATB_IPCSUM)
+ adapter->if_events.IpCsum++;
+ if (hdr->frame_status_b14 & VRHSTATB_IPLERR)
+ adapter->if_events.IpLen++;
+ if (hdr->frame_status_b14 & VRHSTATB_IPHERR)
+ adapter->if_events.IpHlen++;
+ } else {
+ if (hdr->frame_statusGB & VGBSTAT_XPERR) {
+ u32 xerr = hdr->frame_statusGB >> VGBSTAT_XERRSHFT;
+
+ if (xerr == VGBSTAT_XCSERR)
+ adapter->if_events.TpCsum++;
+ if (xerr == VGBSTAT_XUFLOW)
+ adapter->if_events.Tprtoflow++;
+ if (xerr == VGBSTAT_XHLEN)
+ adapter->if_events.TpHlen++;
+ }
+ if (hdr->frame_statusGB & VGBSTAT_NETERR) {
+ u32 nerr =
+ (hdr->
+ frame_statusGB >> VGBSTAT_NERRSHFT) &
+ VGBSTAT_NERRMSK;
+ if (nerr == VGBSTAT_NCSERR)
+ adapter->if_events.IpCsum++;
+ if (nerr == VGBSTAT_NUFLOW)
+ adapter->if_events.IpLen++;
+ if (nerr == VGBSTAT_NHLEN)
+ adapter->if_events.IpHlen++;
+ }
+ if (hdr->frame_statusGB & VGBSTAT_LNKERR) {
+ u32 lerr = hdr->frame_statusGB & VGBSTAT_LERRMSK;
+
+ if (lerr == VGBSTAT_LDEARLY)
+ adapter->if_events.rcvearly++;
+ if (lerr == VGBSTAT_LBOFLO)
+ adapter->if_events.Bufov++;
+ if (lerr == VGBSTAT_LCODERR)
+ adapter->if_events.Code++;
+ if (lerr == VGBSTAT_LDBLNBL)
+ adapter->if_events.Drbl++;
+ if (lerr == VGBSTAT_LCRCERR)
+ adapter->if_events.Crc++;
+ if (lerr == VGBSTAT_LOFLO)
+ adapter->if_events.oflow802++;
+ if (lerr == VGBSTAT_LUFLO)
+ adapter->if_events.uflow802++;
+ }
+ }
+}
+
+#define TCP_OFFLOAD_FRAME_PUSHFLAG 0x10000000
+#define M_FAST_PATH 0x0040
+
+static void slic_rcv_handler(struct adapter *adapter)
+{
+ struct net_device *netdev = adapter->netdev;
+ struct sk_buff *skb;
+ struct slic_rcvbuf *rcvbuf;
+ u32 frames = 0;
+
+ while ((skb = slic_rcvqueue_getnext(adapter))) {
+ u32 rx_bytes;
+
+ rcvbuf = (struct slic_rcvbuf *)skb->head;
+ adapter->card->events++;
+ if (rcvbuf->status & IRHDDR_ERR) {
+ adapter->rx_errors++;
+ slic_rcv_handle_error(adapter, rcvbuf);
+ slic_rcvqueue_reinsert(adapter, skb);
+ continue;
+ }
+
+ if (!slic_mac_filter(adapter, (struct ether_header *)
+ rcvbuf->data)) {
+ slic_rcvqueue_reinsert(adapter, skb);
+ continue;
+ }
+ skb_pull(skb, SLIC_RCVBUF_HEADSIZE);
+ rx_bytes = (rcvbuf->length & IRHDDR_FLEN_MSK);
+ skb_put(skb, rx_bytes);
+ netdev->stats.rx_packets++;
+ netdev->stats.rx_bytes += rx_bytes;
+#if SLIC_OFFLOAD_IP_CHECKSUM
+ skb->ip_summed = CHECKSUM_UNNECESSARY;
+#endif
+
+ skb->dev = adapter->netdev;
+ skb->protocol = eth_type_trans(skb, skb->dev);
+ netif_rx(skb);
+
+ ++frames;
+#if SLIC_INTERRUPT_PROCESS_LIMIT
+ if (frames >= SLIC_RCVQ_MAX_PROCESS_ISR) {
+ adapter->rcv_interrupt_yields++;
+ break;
+ }
+#endif
+ }
+ adapter->max_isr_rcvs = max(adapter->max_isr_rcvs, frames);
+}
+
+static void slic_xmit_complete(struct adapter *adapter)
+{
+ struct slic_hostcmd *hcmd;
+ struct slic_rspbuf *rspbuf;
+ u32 frames = 0;
+ struct slic_handle_word slic_handle_word;
+
+ do {
+ rspbuf = slic_rspqueue_getnext(adapter);
+ if (!rspbuf)
+ break;
+ adapter->xmit_completes++;
+ adapter->card->events++;
+ /*
+ Get the complete host command buffer
+ */
+ slic_handle_word.handle_token = rspbuf->hosthandle;
+ hcmd =
+ (struct slic_hostcmd *)
+ adapter->slic_handles[slic_handle_word.handle_index].
+ address;
+/* hcmd = (struct slic_hostcmd *) rspbuf->hosthandle; */
+ if (hcmd->type == SLIC_CMD_DUMB) {
+ if (hcmd->skb)
+ dev_kfree_skb_irq(hcmd->skb);
+ slic_cmdq_putdone_irq(adapter, hcmd);
+ }
+ rspbuf->status = 0;
+ rspbuf->hosthandle = 0;
+ frames++;
+ } while (1);
+ adapter->max_isr_xmits = max(adapter->max_isr_xmits, frames);
+}
+
+static void slic_interrupt_card_up(u32 isr, struct adapter *adapter,
+ struct net_device *dev)
+{
+ if (isr & ~ISR_IO) {
+ if (isr & ISR_ERR) {
+ adapter->error_interrupts++;
+ if (isr & ISR_RMISS) {
+ int count;
+ int pre_count;
+ int errors;
+
+ struct slic_rcvqueue *rcvq =
+ &adapter->rcvqueue;
+
+ adapter->error_rmiss_interrupts++;
+
+ if (!rcvq->errors)
+ rcv_count = rcvq->count;
+ pre_count = rcvq->count;
+ errors = rcvq->errors;
+
+ while (rcvq->count < SLIC_RCVQ_FILLTHRESH) {
+ count = slic_rcvqueue_fill(adapter);
+ if (!count)
+ break;
+ }
+ } else if (isr & ISR_XDROP) {
+ dev_err(&dev->dev,
+ "isr & ISR_ERR [%x] ISR_XDROP\n",
+ isr);
+ } else {
+ dev_err(&dev->dev,
+ "isr & ISR_ERR [%x]\n",
+ isr);
+ }
+ }
+
+ if (isr & ISR_LEVENT) {
+ adapter->linkevent_interrupts++;
+ slic_link_event_handler(adapter);
+ }
+
+ if ((isr & ISR_UPC) || (isr & ISR_UPCERR) ||
+ (isr & ISR_UPCBSY)) {
+ adapter->upr_interrupts++;
+ slic_upr_request_complete(adapter, isr);
+ }
+ }
+
+ if (isr & ISR_RCV) {
+ adapter->rcv_interrupts++;
+ slic_rcv_handler(adapter);
+ }
+
+ if (isr & ISR_CMD) {
+ adapter->xmit_interrupts++;
+ slic_xmit_complete(adapter);
+ }
+}
+
+
+static irqreturn_t slic_interrupt(int irq, void *dev_id)
+{
+ struct net_device *dev = (struct net_device *)dev_id;
+ struct adapter *adapter = netdev_priv(dev);
+ u32 isr;
+
+ if ((adapter->pshmem) && (adapter->pshmem->isr)) {
+ slic_reg32_write(&adapter->slic_regs->slic_icr,
+ ICR_INT_MASK, FLUSH);
+ isr = adapter->isrcopy = adapter->pshmem->isr;
+ adapter->pshmem->isr = 0;
+ adapter->num_isrs++;
+ switch (adapter->card->state) {
+ case CARD_UP:
+ slic_interrupt_card_up(isr, adapter, dev);
+ break;
+
+ case CARD_DOWN:
+ if ((isr & ISR_UPC) ||
+ (isr & ISR_UPCERR) || (isr & ISR_UPCBSY)) {
+ adapter->upr_interrupts++;
+ slic_upr_request_complete(adapter, isr);
+ }
+ break;
+ }
+
+ adapter->isrcopy = 0;
+ adapter->all_reg_writes += 2;
+ adapter->isr_reg_writes++;
+ slic_reg32_write(&adapter->slic_regs->slic_isr, 0, FLUSH);
+ } else {
+ adapter->false_interrupts++;
+ }
+ return IRQ_HANDLED;
+}
+
+#define NORMAL_ETHFRAME 0
+
+static netdev_tx_t slic_xmit_start(struct sk_buff *skb, struct net_device *dev)
+{
+ struct sliccard *card;
+ struct adapter *adapter = netdev_priv(dev);
+ struct slic_hostcmd *hcmd = NULL;
+ u32 status = 0;
+ void *offloadcmd = NULL;
+
+ card = adapter->card;
+ if ((adapter->linkstate != LINK_UP) ||
+ (adapter->state != ADAPT_UP) || (card->state != CARD_UP)) {
+ status = XMIT_FAIL_LINK_STATE;
+ goto xmit_fail;
+
+ } else if (skb->len == 0) {
+ status = XMIT_FAIL_ZERO_LENGTH;
+ goto xmit_fail;
+ }
+
+ hcmd = slic_cmdq_getfree(adapter);
+ if (!hcmd) {
+ adapter->xmitq_full = 1;
+ status = XMIT_FAIL_HOSTCMD_FAIL;
+ goto xmit_fail;
+ }
+ hcmd->skb = skb;
+ hcmd->busy = 1;
+ hcmd->type = SLIC_CMD_DUMB;
+ slic_xmit_build_request(adapter, hcmd, skb);
+ dev->stats.tx_packets++;
+ dev->stats.tx_bytes += skb->len;
+
+#ifdef DEBUG_DUMP
+ if (adapter->kill_card) {
+ struct slic_host64_cmd ihcmd;
+
+ ihcmd = &hcmd->cmd64;
+
+ ihcmd->flags |= 0x40;
+ adapter->kill_card = 0; /* only do this once */
+ }
+#endif
+ if (hcmd->paddrh == 0) {
+ slic_reg32_write(&adapter->slic_regs->slic_cbar,
+ (hcmd->paddrl | hcmd->cmdsize), DONT_FLUSH);
+ } else {
+ slic_reg64_write(adapter, &adapter->slic_regs->slic_cbar64,
+ (hcmd->paddrl | hcmd->cmdsize),
+ &adapter->slic_regs->slic_addr_upper,
+ hcmd->paddrh, DONT_FLUSH);
+ }
+xmit_done:
+ return NETDEV_TX_OK;
+xmit_fail:
+ slic_xmit_fail(adapter, skb, offloadcmd, NORMAL_ETHFRAME, status);
+ goto xmit_done;
+}
+
+
+static void slic_adapter_freeresources(struct adapter *adapter)
+{
+ slic_init_cleanup(adapter);
+ adapter->error_interrupts = 0;
+ adapter->rcv_interrupts = 0;
+ adapter->xmit_interrupts = 0;
+ adapter->linkevent_interrupts = 0;
+ adapter->upr_interrupts = 0;
+ adapter->num_isrs = 0;
+ adapter->xmit_completes = 0;
+ adapter->rcv_broadcasts = 0;
+ adapter->rcv_multicasts = 0;
+ adapter->rcv_unicasts = 0;
+}
+
+static int slic_adapter_allocresources(struct adapter *adapter)
+{
+ if (!adapter->intrregistered) {
+ int retval;
+
+ spin_unlock_irqrestore(&slic_global.driver_lock.lock,
+ slic_global.driver_lock.flags);
+
+ retval = request_irq(adapter->netdev->irq,
+ &slic_interrupt,
+ IRQF_SHARED,
+ adapter->netdev->name, adapter->netdev);
+
+ spin_lock_irqsave(&slic_global.driver_lock.lock,
+ slic_global.driver_lock.flags);
+
+ if (retval) {
+ dev_err(&adapter->netdev->dev,
+ "request_irq (%s) FAILED [%x]\n",
+ adapter->netdev->name, retval);
+ return retval;
+ }
+ adapter->intrregistered = 1;
+ }
+ return 0;
+}
+
+/*
+ * slic_if_init
+ *
+ * Perform initialization of our slic interface.
+ *
+ */
+static int slic_if_init(struct adapter *adapter)
+{
+ struct sliccard *card = adapter->card;
+ struct net_device *dev = adapter->netdev;
+ __iomem struct slic_regs *slic_regs = adapter->slic_regs;
+ struct slic_shmem *pshmem;
+ int rc;
+
+ /* adapter should be down at this point */
+ if (adapter->state != ADAPT_DOWN) {
+ dev_err(&dev->dev, "%s: adapter->state != ADAPT_DOWN\n",
+ __func__);
+ rc = -EIO;
+ goto err;
+ }
+
+ adapter->devflags_prev = dev->flags;
+ adapter->macopts = MAC_DIRECTED;
+ if (dev->flags) {
+ if (dev->flags & IFF_BROADCAST)
+ adapter->macopts |= MAC_BCAST;
+ if (dev->flags & IFF_PROMISC)
+ adapter->macopts |= MAC_PROMISC;
+ if (dev->flags & IFF_ALLMULTI)
+ adapter->macopts |= MAC_ALLMCAST;
+ if (dev->flags & IFF_MULTICAST)
+ adapter->macopts |= MAC_MCAST;
+ }
+ rc = slic_adapter_allocresources(adapter);
+ if (rc) {
+ dev_err(&dev->dev, "slic_adapter_allocresources FAILED %x\n",
+ rc);
+ slic_adapter_freeresources(adapter);
+ goto err;
+ }
+
+ if (!adapter->queues_initialized) {
+ rc = slic_rspqueue_init(adapter);
+ if (rc)
+ goto err;
+ rc = slic_cmdq_init(adapter);
+ if (rc)
+ goto err;
+ rc = slic_rcvqueue_init(adapter);
+ if (rc)
+ goto err;
+ adapter->queues_initialized = 1;
+ }
+
+ slic_reg32_write(&slic_regs->slic_icr, ICR_INT_OFF, FLUSH);
+ mdelay(1);
+
+ if (!adapter->isp_initialized) {
+ pshmem = (struct slic_shmem *)(unsigned long)
+ adapter->phys_shmem;
+
+ spin_lock_irqsave(&adapter->bit64reglock.lock,
+ adapter->bit64reglock.flags);
+
+#if BITS_PER_LONG == 64
+ slic_reg32_write(&slic_regs->slic_addr_upper,
+ SLIC_GET_ADDR_HIGH(&pshmem->isr), DONT_FLUSH);
+ slic_reg32_write(&slic_regs->slic_isp,
+ SLIC_GET_ADDR_LOW(&pshmem->isr), FLUSH);
+#else
+ slic_reg32_write(&slic_regs->slic_addr_upper, 0, DONT_FLUSH);
+ slic_reg32_write(&slic_regs->slic_isp, (u32)&pshmem->isr,
+ FLUSH);
+#endif
+ spin_unlock_irqrestore(&adapter->bit64reglock.lock,
+ adapter->bit64reglock.flags);
+ adapter->isp_initialized = 1;
+ }
+
+ adapter->state = ADAPT_UP;
+ if (!card->loadtimerset) {
+ setup_timer(&card->loadtimer, &slic_timer_load_check,
+ (ulong)card);
+ card->loadtimer.expires =
+ jiffies + (SLIC_LOADTIMER_PERIOD * HZ);
+ add_timer(&card->loadtimer);
+
+ card->loadtimerset = 1;
+ }
+
+ if (!adapter->pingtimerset) {
+ setup_timer(&adapter->pingtimer, &slic_timer_ping, (ulong)dev);
+ adapter->pingtimer.expires =
+ jiffies + (PING_TIMER_INTERVAL * HZ);
+ add_timer(&adapter->pingtimer);
+ adapter->pingtimerset = 1;
+ adapter->card->pingstatus = ISR_PINGMASK;
+ }
+
+ /*
+ * clear any pending events, then enable interrupts
+ */
+ adapter->isrcopy = 0;
+ adapter->pshmem->isr = 0;
+ slic_reg32_write(&slic_regs->slic_isr, 0, FLUSH);
+ slic_reg32_write(&slic_regs->slic_icr, ICR_INT_ON, FLUSH);
+
+ slic_link_config(adapter, LINK_AUTOSPEED, LINK_AUTOD);
+ slic_link_event_handler(adapter);
+
+err:
+ return rc;
+}
+
+static int slic_entry_open(struct net_device *dev)
+{
+ struct adapter *adapter = netdev_priv(dev);
+ struct sliccard *card = adapter->card;
+ int status;
+
+ netif_stop_queue(adapter->netdev);
+
+ spin_lock_irqsave(&slic_global.driver_lock.lock,
+ slic_global.driver_lock.flags);
+ if (!adapter->activated) {
+ card->adapters_activated++;
+ slic_global.num_slic_ports_active++;
+ adapter->activated = 1;
+ }
+ status = slic_if_init(adapter);
+
+ if (status != 0) {
+ if (adapter->activated) {
+ card->adapters_activated--;
+ slic_global.num_slic_ports_active--;
+ adapter->activated = 0;
+ }
+ goto spin_unlock;
+ }
+ if (!card->master)
+ card->master = adapter;
+
+spin_unlock:
+ spin_unlock_irqrestore(&slic_global.driver_lock.lock,
+ slic_global.driver_lock.flags);
+ return status;
+}
+
+static void slic_card_cleanup(struct sliccard *card)
+{
+ if (card->loadtimerset) {
+ card->loadtimerset = 0;
+ del_timer_sync(&card->loadtimer);
+ }
+
+ kfree(card);
+}
+
+static void slic_entry_remove(struct pci_dev *pcidev)
+{
+ struct net_device *dev = pci_get_drvdata(pcidev);
+ struct adapter *adapter = netdev_priv(dev);
+ struct sliccard *card;
+ struct mcast_address *mcaddr, *mlist;
+
+ unregister_netdev(dev);
+
+ slic_adapter_freeresources(adapter);
+ slic_unmap_mmio_space(adapter);
+
+ /* free multicast addresses */
+ mlist = adapter->mcastaddrs;
+ while (mlist) {
+ mcaddr = mlist;
+ mlist = mlist->next;
+ kfree(mcaddr);
+ }
+ card = adapter->card;
+ card->adapters_allocated--;
+ adapter->allocated = 0;
+ if (!card->adapters_allocated) {
+ struct sliccard *curr_card = slic_global.slic_card;
+
+ if (curr_card == card) {
+ slic_global.slic_card = card->next;
+ } else {
+ while (curr_card->next != card)
+ curr_card = curr_card->next;
+ curr_card->next = card->next;
+ }
+ slic_global.num_slic_cards--;
+ slic_card_cleanup(card);
+ }
+ free_netdev(dev);
+ pci_release_regions(pcidev);
+ pci_disable_device(pcidev);
+}
+
+static int slic_entry_halt(struct net_device *dev)
+{
+ struct adapter *adapter = netdev_priv(dev);
+ struct sliccard *card = adapter->card;
+ __iomem struct slic_regs *slic_regs = adapter->slic_regs;
+
+ spin_lock_irqsave(&slic_global.driver_lock.lock,
+ slic_global.driver_lock.flags);
+ netif_stop_queue(adapter->netdev);
+ adapter->state = ADAPT_DOWN;
+ adapter->linkstate = LINK_DOWN;
+ adapter->upr_list = NULL;
+ adapter->upr_busy = 0;
+ adapter->devflags_prev = 0;
+ slic_reg32_write(&slic_regs->slic_icr, ICR_INT_OFF, FLUSH);
+ adapter->all_reg_writes++;
+ adapter->icr_reg_writes++;
+ slic_config_clear(adapter);
+ if (adapter->activated) {
+ card->adapters_activated--;
+ slic_global.num_slic_ports_active--;
+ adapter->activated = 0;
+ }
+#ifdef AUTOMATIC_RESET
+ slic_reg32_write(&slic_regs->slic_reset_iface, 0, FLUSH);
+#endif
+ /*
+ * Reset the adapter's cmd queues
+ */
+ slic_cmdq_reset(adapter);
+
+#ifdef AUTOMATIC_RESET
+ if (!card->adapters_activated)
+ slic_card_init(card, adapter);
+#endif
+
+ spin_unlock_irqrestore(&slic_global.driver_lock.lock,
+ slic_global.driver_lock.flags);
+ return 0;
+}
+
+static struct net_device_stats *slic_get_stats(struct net_device *dev)
+{
+ struct adapter *adapter = netdev_priv(dev);
+
+ dev->stats.collisions = adapter->slic_stats.iface.xmit_collisions;
+ dev->stats.rx_errors = adapter->slic_stats.iface.rcv_errors;
+ dev->stats.tx_errors = adapter->slic_stats.iface.xmt_errors;
+ dev->stats.rx_missed_errors = adapter->slic_stats.iface.rcv_discards;
+ dev->stats.tx_heartbeat_errors = 0;
+ dev->stats.tx_aborted_errors = 0;
+ dev->stats.tx_window_errors = 0;
+ dev->stats.tx_fifo_errors = 0;
+ dev->stats.rx_frame_errors = 0;
+ dev->stats.rx_length_errors = 0;
+
+ return &dev->stats;
+}
+
+static int slic_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
+{
+ struct adapter *adapter = netdev_priv(dev);
+ struct ethtool_cmd edata;
+ struct ethtool_cmd ecmd;
+ u32 data[7];
+ u32 intagg;
+
+ switch (cmd) {
+ case SIOCSLICSETINTAGG:
+ if (copy_from_user(data, rq->ifr_data, 28))
+ return -EFAULT;
+ intagg = data[0];
+ dev_err(&dev->dev, "set interrupt aggregation to %d\n",
+ intagg);
+ slic_intagg_set(adapter, intagg);
+ return 0;
+
+ case SIOCETHTOOL:
+ if (copy_from_user(&ecmd, rq->ifr_data, sizeof(ecmd)))
+ return -EFAULT;
+
+ if (ecmd.cmd == ETHTOOL_GSET) {
+ memset(&edata, 0, sizeof(edata));
+ edata.supported = (SUPPORTED_10baseT_Half |
+ SUPPORTED_10baseT_Full |
+ SUPPORTED_100baseT_Half |
+ SUPPORTED_100baseT_Full |
+ SUPPORTED_Autoneg | SUPPORTED_MII);
+ edata.port = PORT_MII;
+ edata.transceiver = XCVR_INTERNAL;
+ edata.phy_address = 0;
+ if (adapter->linkspeed == LINK_100MB)
+ edata.speed = SPEED_100;
+ else if (adapter->linkspeed == LINK_10MB)
+ edata.speed = SPEED_10;
+ else
+ edata.speed = 0;
+
+ if (adapter->linkduplex == LINK_FULLD)
+ edata.duplex = DUPLEX_FULL;
+ else
+ edata.duplex = DUPLEX_HALF;
+
+ edata.autoneg = AUTONEG_ENABLE;
+ edata.maxtxpkt = 1;
+ edata.maxrxpkt = 1;
+ if (copy_to_user(rq->ifr_data, &edata, sizeof(edata)))
+ return -EFAULT;
+
+ } else if (ecmd.cmd == ETHTOOL_SSET) {
+ if (!capable(CAP_NET_ADMIN))
+ return -EPERM;
+
+ if (adapter->linkspeed == LINK_100MB)
+ edata.speed = SPEED_100;
+ else if (adapter->linkspeed == LINK_10MB)
+ edata.speed = SPEED_10;
+ else
+ edata.speed = 0;
+
+ if (adapter->linkduplex == LINK_FULLD)
+ edata.duplex = DUPLEX_FULL;
+ else
+ edata.duplex = DUPLEX_HALF;
+
+ edata.autoneg = AUTONEG_ENABLE;
+ edata.maxtxpkt = 1;
+ edata.maxrxpkt = 1;
+ if ((ecmd.speed != edata.speed) ||
+ (ecmd.duplex != edata.duplex)) {
+ u32 speed;
+ u32 duplex;
+
+ if (ecmd.speed == SPEED_10)
+ speed = 0;
+ else
+ speed = PCR_SPEED_100;
+ if (ecmd.duplex == DUPLEX_FULL)
+ duplex = PCR_DUPLEX_FULL;
+ else
+ duplex = 0;
+ slic_link_config(adapter, speed, duplex);
+ slic_link_event_handler(adapter);
+ }
+ }
+ return 0;
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+static void slic_config_pci(struct pci_dev *pcidev)
+{
+ u16 pci_command;
+ u16 new_command;
+
+ pci_read_config_word(pcidev, PCI_COMMAND, &pci_command);
+
+ new_command = pci_command | PCI_COMMAND_MASTER
+ | PCI_COMMAND_MEMORY
+ | PCI_COMMAND_INVALIDATE
+ | PCI_COMMAND_PARITY | PCI_COMMAND_SERR | PCI_COMMAND_FAST_BACK;
+ if (pci_command != new_command)
+ pci_write_config_word(pcidev, PCI_COMMAND, new_command);
+}
+
+static int slic_card_init(struct sliccard *card, struct adapter *adapter)
+{
+ __iomem struct slic_regs *slic_regs = adapter->slic_regs;
+ struct slic_eeprom *peeprom;
+ struct oslic_eeprom *pOeeprom;
+ dma_addr_t phys_config;
+ u32 phys_configh;
+ u32 phys_configl;
+ u32 i = 0;
+ struct slic_shmem *pshmem;
+ int status;
+ uint macaddrs = card->card_size;
+ ushort eecodesize;
+ ushort dramsize;
+ ushort ee_chksum;
+ ushort calc_chksum;
+ struct slic_config_mac *pmac;
+ unsigned char fruformat;
+ unsigned char oemfruformat;
+ struct atk_fru *patkfru;
+ union oemfru *poemfru;
+
+ /* Reset everything except PCI configuration space */
+ slic_soft_reset(adapter);
+
+ /* Download the microcode */
+ status = slic_card_download(adapter);
+ if (status)
+ return status;
+
+ if (!card->config_set) {
+ peeprom = pci_alloc_consistent(adapter->pcidev,
+ sizeof(struct slic_eeprom),
+ &phys_config);
+
+ phys_configl = SLIC_GET_ADDR_LOW(phys_config);
+ phys_configh = SLIC_GET_ADDR_HIGH(phys_config);
+
+ if (!peeprom) {
+ dev_err(&adapter->pcidev->dev,
+ "Failed to allocate DMA memory for EEPROM.\n");
+ return -ENOMEM;
+ }
+
+ memset(peeprom, 0, sizeof(struct slic_eeprom));
+
+ slic_reg32_write(&slic_regs->slic_icr, ICR_INT_OFF, FLUSH);
+ mdelay(1);
+ pshmem = (struct slic_shmem *)(unsigned long)
+ adapter->phys_shmem;
+
+ spin_lock_irqsave(&adapter->bit64reglock.lock,
+ adapter->bit64reglock.flags);
+ slic_reg32_write(&slic_regs->slic_addr_upper,
+ SLIC_GET_ADDR_HIGH(&pshmem->isr), DONT_FLUSH);
+ slic_reg32_write(&slic_regs->slic_isp,
+ SLIC_GET_ADDR_LOW(&pshmem->isr), FLUSH);
+ spin_unlock_irqrestore(&adapter->bit64reglock.lock,
+ adapter->bit64reglock.flags);
+
+ status = slic_config_get(adapter, phys_configl, phys_configh);
+ if (status) {
+ dev_err(&adapter->pcidev->dev,
+ "Failed to fetch config data from device.\n");
+ goto card_init_err;
+ }
+
+ for (;;) {
+ if (adapter->pshmem->isr) {
+ if (adapter->pshmem->isr & ISR_UPC) {
+ adapter->pshmem->isr = 0;
+ slic_reg64_write(adapter,
+ &slic_regs->slic_isp, 0,
+ &slic_regs->slic_addr_upper,
+ 0, FLUSH);
+ slic_reg32_write(&slic_regs->slic_isr,
+ 0, FLUSH);
+
+ slic_upr_request_complete(adapter, 0);
+ break;
+ }
+
+ adapter->pshmem->isr = 0;
+ slic_reg32_write(&slic_regs->slic_isr,
+ 0, FLUSH);
+ } else {
+ mdelay(1);
+ i++;
+ if (i > 5000) {
+ dev_err(&adapter->pcidev->dev,
+ "Fetch of config data timed out.\n");
+ slic_reg64_write(adapter,
+ &slic_regs->slic_isp, 0,
+ &slic_regs->slic_addr_upper,
+ 0, FLUSH);
+ status = -EINVAL;
+ goto card_init_err;
+ }
+ }
+ }
+
+ switch (adapter->devid) {
+ /* Oasis card */
+ case SLIC_2GB_DEVICE_ID:
+ /* extract EEPROM data and pointers to EEPROM data */
+ pOeeprom = (struct oslic_eeprom *) peeprom;
+ eecodesize = pOeeprom->EecodeSize;
+ dramsize = pOeeprom->DramSize;
+ pmac = pOeeprom->MacInfo;
+ fruformat = pOeeprom->FruFormat;
+ patkfru = &pOeeprom->AtkFru;
+ oemfruformat = pOeeprom->OemFruFormat;
+ poemfru = &pOeeprom->OemFru;
+ macaddrs = 2;
+ /* Minor kludge for Oasis card
+ get 2 MAC addresses from the
+ EEPROM to ensure that function 1
+ gets the Port 1 MAC address */
+ break;
+ default:
+ /* extract EEPROM data and pointers to EEPROM data */
+ eecodesize = peeprom->EecodeSize;
+ dramsize = peeprom->DramSize;
+ pmac = peeprom->u2.mac.MacInfo;
+ fruformat = peeprom->FruFormat;
+ patkfru = &peeprom->AtkFru;
+ oemfruformat = peeprom->OemFruFormat;
+ poemfru = &peeprom->OemFru;
+ break;
+ }
+
+ card->config.EepromValid = false;
+
+ /* see if the EEPROM is valid by checking it's checksum */
+ if ((eecodesize <= MAX_EECODE_SIZE) &&
+ (eecodesize >= MIN_EECODE_SIZE)) {
+
+ ee_chksum =
+ *(u16 *) ((char *) peeprom + (eecodesize - 2));
+ /*
+ calculate the EEPROM checksum
+ */
+ calc_chksum = slic_eeprom_cksum(peeprom,
+ eecodesize - 2);
+ /*
+ if the ucdoe chksum flag bit worked,
+ we wouldn't need this
+ */
+ if (ee_chksum == calc_chksum)
+ card->config.EepromValid = true;
+ }
+ /* copy in the DRAM size */
+ card->config.DramSize = dramsize;
+
+ /* copy in the MAC address(es) */
+ for (i = 0; i < macaddrs; i++) {
+ memcpy(&card->config.MacInfo[i],
+ &pmac[i], sizeof(struct slic_config_mac));
+ }
+
+ /* copy the Alacritech FRU information */
+ card->config.FruFormat = fruformat;
+ memcpy(&card->config.AtkFru, patkfru,
+ sizeof(struct atk_fru));
+
+ pci_free_consistent(adapter->pcidev,
+ sizeof(struct slic_eeprom),
+ peeprom, phys_config);
+
+ if (!card->config.EepromValid) {
+ slic_reg64_write(adapter, &slic_regs->slic_isp, 0,
+ &slic_regs->slic_addr_upper,
+ 0, FLUSH);
+ dev_err(&adapter->pcidev->dev, "EEPROM invalid.\n");
+ return -EINVAL;
+ }
+
+ card->config_set = 1;
+ }
+
+ status = slic_card_download_gbrcv(adapter);
+ if (status)
+ return status;
+
+ if (slic_global.dynamic_intagg)
+ slic_intagg_set(adapter, 0);
+ else
+ slic_intagg_set(adapter, intagg_delay);
+
+ /*
+ * Initialize ping status to "ok"
+ */
+ card->pingstatus = ISR_PINGMASK;
+
+ /*
+ * Lastly, mark our card state as up and return success
+ */
+ card->state = CARD_UP;
+ card->reset_in_progress = 0;
+
+ return 0;
+
+card_init_err:
+ pci_free_consistent(adapter->pcidev, sizeof(struct slic_eeprom),
+ peeprom, phys_config);
+ return status;
+}
+
+static void slic_init_driver(void)
+{
+ if (slic_first_init) {
+ slic_first_init = 0;
+ spin_lock_init(&slic_global.driver_lock.lock);
+ }
+}
+
+static void slic_init_adapter(struct net_device *netdev,
+ struct pci_dev *pcidev,
+ const struct pci_device_id *pci_tbl_entry,
+ void __iomem *memaddr, int chip_idx)
+{
+ ushort index;
+ struct slic_handle *pslic_handle;
+ struct adapter *adapter = netdev_priv(netdev);
+
+/* adapter->pcidev = pcidev;*/
+ adapter->vendid = pci_tbl_entry->vendor;
+ adapter->devid = pci_tbl_entry->device;
+ adapter->subsysid = pci_tbl_entry->subdevice;
+ adapter->busnumber = pcidev->bus->number;
+ adapter->slotnumber = ((pcidev->devfn >> 3) & 0x1F);
+ adapter->functionnumber = (pcidev->devfn & 0x7);
+ adapter->slic_regs = (__iomem struct slic_regs *)memaddr;
+ adapter->irq = pcidev->irq;
+/* adapter->netdev = netdev;*/
+ adapter->chipid = chip_idx;
+ adapter->port = 0; /*adapter->functionnumber;*/
+ adapter->cardindex = adapter->port;
+ spin_lock_init(&adapter->upr_lock.lock);
+ spin_lock_init(&adapter->bit64reglock.lock);
+ spin_lock_init(&adapter->adapter_lock.lock);
+ spin_lock_init(&adapter->reset_lock.lock);
+ spin_lock_init(&adapter->handle_lock.lock);
+
+ adapter->card_size = 1;
+ /*
+ Initialize slic_handle array
+ */
+ /*
+ Start with 1. 0 is an invalid host handle.
+ */
+ for (index = 1, pslic_handle = &adapter->slic_handles[1];
+ index < SLIC_CMDQ_MAXCMDS; index++, pslic_handle++) {
+
+ pslic_handle->token.handle_index = index;
+ pslic_handle->type = SLIC_HANDLE_FREE;
+ pslic_handle->next = adapter->pfree_slic_handles;
+ adapter->pfree_slic_handles = pslic_handle;
+ }
+ adapter->pshmem = (struct slic_shmem *)
+ pci_alloc_consistent(adapter->pcidev,
+ sizeof(struct slic_shmem),
+ &adapter->
+ phys_shmem);
+ if (adapter->pshmem)
+ memset(adapter->pshmem, 0, sizeof(struct slic_shmem));
+}
+
+static const struct net_device_ops slic_netdev_ops = {
+ .ndo_open = slic_entry_open,
+ .ndo_stop = slic_entry_halt,
+ .ndo_start_xmit = slic_xmit_start,
+ .ndo_do_ioctl = slic_ioctl,
+ .ndo_set_mac_address = slic_mac_set_address,
+ .ndo_get_stats = slic_get_stats,
+ .ndo_set_rx_mode = slic_mcast_set_list,
+ .ndo_validate_addr = eth_validate_addr,
+ .ndo_change_mtu = eth_change_mtu,
+};
+
+static u32 slic_card_locate(struct adapter *adapter)
+{
+ struct sliccard *card = slic_global.slic_card;
+ struct physcard *physcard = slic_global.phys_card;
+ ushort card_hostid;
+ u16 __iomem *hostid_reg;
+ uint i;
+ uint rdhostid_offset = 0;
+
+ switch (adapter->devid) {
+ case SLIC_2GB_DEVICE_ID:
+ rdhostid_offset = SLIC_RDHOSTID_2GB;
+ break;
+ case SLIC_1GB_DEVICE_ID:
+ rdhostid_offset = SLIC_RDHOSTID_1GB;
+ break;
+ default:
+ return -ENODEV;
+ }
+
+ hostid_reg =
+ (u16 __iomem *) (((u8 __iomem *) (adapter->slic_regs)) +
+ rdhostid_offset);
+
+ /* read the 16 bit hostid from SRAM */
+ card_hostid = (ushort) readw(hostid_reg);
+
+ /* Initialize a new card structure if need be */
+ if (card_hostid == SLIC_HOSTID_DEFAULT) {
+ card = kzalloc(sizeof(struct sliccard), GFP_KERNEL);
+ if (card == NULL)
+ return -ENOMEM;
+
+ card->next = slic_global.slic_card;
+ slic_global.slic_card = card;
+ card->busnumber = adapter->busnumber;
+ card->slotnumber = adapter->slotnumber;
+
+ /* Find an available cardnum */
+ for (i = 0; i < SLIC_MAX_CARDS; i++) {
+ if (slic_global.cardnuminuse[i] == 0) {
+ slic_global.cardnuminuse[i] = 1;
+ card->cardnum = i;
+ break;
+ }
+ }
+ slic_global.num_slic_cards++;
+ } else {
+ /* Card exists, find the card this adapter belongs to */
+ while (card) {
+ if (card->cardnum == card_hostid)
+ break;
+ card = card->next;
+ }
+ }
+
+ if (!card)
+ return -ENXIO;
+ /* Put the adapter in the card's adapter list */
+ if (!card->adapter[adapter->port]) {
+ card->adapter[adapter->port] = adapter;
+ adapter->card = card;
+ }
+
+ card->card_size = 1; /* one port per *logical* card */
+
+ while (physcard) {
+ for (i = 0; i < SLIC_MAX_PORTS; i++) {
+ if (physcard->adapter[i])
+ break;
+ }
+ if (i == SLIC_MAX_PORTS)
+ break;
+
+ if (physcard->adapter[i]->slotnumber == adapter->slotnumber)
+ break;
+ physcard = physcard->next;
+ }
+ if (!physcard) {
+ /* no structure allocated for this physical card yet */
+ physcard = kzalloc(sizeof(struct physcard), GFP_ATOMIC);
+ if (!physcard) {
+ if (card_hostid == SLIC_HOSTID_DEFAULT)
+ kfree(card);
+ return -ENOMEM;
+ }
+
+ physcard->next = slic_global.phys_card;
+ slic_global.phys_card = physcard;
+ physcard->adapters_allocd = 1;
+ } else {
+ physcard->adapters_allocd++;
+ }
+ /* Note - this is ZERO relative */
+ adapter->physport = physcard->adapters_allocd - 1;
+
+ physcard->adapter[adapter->physport] = adapter;
+ adapter->physcard = physcard;
+
+ return 0;
+}
+
+static int slic_entry_probe(struct pci_dev *pcidev,
+ const struct pci_device_id *pci_tbl_entry)
+{
+ static int cards_found;
+ static int did_version;
+ int err = -ENODEV;
+ struct net_device *netdev;
+ struct adapter *adapter;
+ void __iomem *memmapped_ioaddr = NULL;
+ ulong mmio_start = 0;
+ ulong mmio_len = 0;
+ struct sliccard *card = NULL;
+ int pci_using_dac = 0;
+
+ slic_global.dynamic_intagg = dynamic_intagg;
+
+ err = pci_enable_device(pcidev);
+
+ if (err)
+ return err;
+
+ if (did_version++ == 0) {
+ dev_info(&pcidev->dev, "%s\n", slic_banner);
+ dev_info(&pcidev->dev, "%s\n", slic_proc_version);
+ }
+
+ if (!pci_set_dma_mask(pcidev, DMA_BIT_MASK(64))) {
+ pci_using_dac = 1;
+ err = pci_set_consistent_dma_mask(pcidev, DMA_BIT_MASK(64));
+ if (err) {
+ dev_err(&pcidev->dev, "unable to obtain 64-bit DMA for consistent allocations\n");
+ goto err_out_disable_pci;
+ }
+ } else {
+ err = pci_set_dma_mask(pcidev, DMA_BIT_MASK(32));
+ if (err) {
+ dev_err(&pcidev->dev, "no usable DMA configuration\n");
+ goto err_out_disable_pci;
+ }
+ pci_using_dac = 0;
+ pci_set_consistent_dma_mask(pcidev, DMA_BIT_MASK(32));
+ }
+
+ err = pci_request_regions(pcidev, DRV_NAME);
+ if (err) {
+ dev_err(&pcidev->dev, "can't obtain PCI resources\n");
+ goto err_out_disable_pci;
+ }
+
+ pci_set_master(pcidev);
+
+ netdev = alloc_etherdev(sizeof(struct adapter));
+ if (!netdev) {
+ err = -ENOMEM;
+ goto err_out_exit_slic_probe;
+ }
+
+ SET_NETDEV_DEV(netdev, &pcidev->dev);
+
+ pci_set_drvdata(pcidev, netdev);
+ adapter = netdev_priv(netdev);
+ adapter->netdev = netdev;
+ adapter->pcidev = pcidev;
+ if (pci_using_dac)
+ netdev->features |= NETIF_F_HIGHDMA;
+
+ mmio_start = pci_resource_start(pcidev, 0);
+ mmio_len = pci_resource_len(pcidev, 0);
+
+
+/* memmapped_ioaddr = (u32)ioremap_nocache(mmio_start, mmio_len);*/
+ memmapped_ioaddr = ioremap(mmio_start, mmio_len);
+ if (!memmapped_ioaddr) {
+ dev_err(&pcidev->dev, "cannot remap MMIO region %lx @ %lx\n",
+ mmio_len, mmio_start);
+ err = -ENOMEM;
+ goto err_out_free_netdev;
+ }
+
+ slic_config_pci(pcidev);
+
+ slic_init_driver();
+
+ slic_init_adapter(netdev,
+ pcidev, pci_tbl_entry, memmapped_ioaddr, cards_found);
+
+ err = slic_card_locate(adapter);
+ if (err) {
+ dev_err(&pcidev->dev, "cannot locate card\n");
+ goto err_out_unmap;
+ }
+
+ card = adapter->card;
+
+ if (!adapter->allocated) {
+ card->adapters_allocated++;
+ adapter->allocated = 1;
+ }
+
+ err = slic_card_init(card, adapter);
+ if (err)
+ goto err_out_unmap;
+
+ slic_adapter_set_hwaddr(adapter);
+
+ netdev->base_addr = (unsigned long) memmapped_ioaddr;
+ netdev->irq = adapter->irq;
+ netdev->netdev_ops = &slic_netdev_ops;
+
+ strcpy(netdev->name, "eth%d");
+ err = register_netdev(netdev);
+ if (err) {
+ dev_err(&pcidev->dev, "Cannot register net device, aborting.\n");
+ goto err_out_unmap;
+ }
+
+ cards_found++;
+
+ return 0;
+
+err_out_unmap:
+ iounmap(memmapped_ioaddr);
+err_out_free_netdev:
+ free_netdev(netdev);
+err_out_exit_slic_probe:
+ pci_release_regions(pcidev);
+err_out_disable_pci:
+ pci_disable_device(pcidev);
+ return err;
+}
+
+static struct pci_driver slic_driver = {
+ .name = DRV_NAME,
+ .id_table = slic_pci_tbl,
+ .probe = slic_entry_probe,
+ .remove = slic_entry_remove,
+};
+
+static int __init slic_module_init(void)
+{
+ slic_init_driver();
+
+ return pci_register_driver(&slic_driver);
+}
+
+static void __exit slic_module_cleanup(void)
+{
+ pci_unregister_driver(&slic_driver);
+}
+
+module_init(slic_module_init);
+module_exit(slic_module_cleanup);
diff --git a/drivers/staging/sm750fb/Kconfig b/drivers/staging/sm750fb/Kconfig
new file mode 100644
index 000000000..c40d088a4
--- /dev/null
+++ b/drivers/staging/sm750fb/Kconfig
@@ -0,0 +1,10 @@
+config FB_SM750
+ tristate "Silicon Motion SM750 framebuffer support"
+ depends on FB && PCI
+ help
+ Frame buffer driver for the Silicon Motion SM750 chip
+ with 2D accelearion and dual head support.
+
+ This driver is also available as a module. The module will be
+ called sm750fb. If you want to compile it as a module, say M
+ here and read <file:Documentation/kbuild/modules.txt>.
diff --git a/drivers/staging/sm750fb/Makefile b/drivers/staging/sm750fb/Makefile
new file mode 100644
index 000000000..dcce3f487
--- /dev/null
+++ b/drivers/staging/sm750fb/Makefile
@@ -0,0 +1,4 @@
+obj-$(CONFIG_FB_SM750) += sm750fb.o
+
+sm750fb-objs := sm750.o sm750_hw.o sm750_accel.o sm750_cursor.o ddk750_chip.o ddk750_power.o ddk750_mode.o
+sm750fb-objs += ddk750_display.o ddk750_help.o ddk750_swi2c.o ddk750_sii164.o ddk750_dvi.o ddk750_hwi2c.o
diff --git a/drivers/staging/sm750fb/TODO b/drivers/staging/sm750fb/TODO
new file mode 100644
index 000000000..bc1617249
--- /dev/null
+++ b/drivers/staging/sm750fb/TODO
@@ -0,0 +1,15 @@
+TODO:
+- lots of clechpatch cleanup
+- use kernel coding style
+- refine the code and remove unused code
+- check on hardware effects of removal of USE_HW_I2C and USE_DVICHIP (these two
+ are supposed to be sample code which is given here if someone wants to
+ use those functionalities)
+- move it to drivers/video/fbdev
+- modify the code for drm framework
+
+Please send any patches to
+ Greg Kroah-Hartman <greg@kroah.com>
+ Sudip Mukherjee <sudipm.mukherjee@gmail.com>
+ Teddy Wang <teddy.wang@siliconmotion.com>
+ Sudip Mukherjee <sudip@vectorindia.org>
diff --git a/drivers/staging/sm750fb/ddk750.h b/drivers/staging/sm750fb/ddk750.h
new file mode 100644
index 000000000..2c10a08ed
--- /dev/null
+++ b/drivers/staging/sm750fb/ddk750.h
@@ -0,0 +1,24 @@
+#ifndef DDK750_H__
+#define DDK750_H__
+/*******************************************************************
+*
+* Copyright (c) 2007 by Silicon Motion, Inc. (SMI)
+*
+* All rights are reserved. Reproduction or in part is prohibited
+* without the written consent of the copyright owner.
+*
+* RegSC.h --- SM718 SDK
+* This file contains the definitions for the System Configuration registers.
+*
+*******************************************************************/
+#include "ddk750_reg.h"
+#include "ddk750_mode.h"
+#include "ddk750_chip.h"
+#include "ddk750_display.h"
+#include "ddk750_power.h"
+#include "ddk750_help.h"
+#ifdef USE_HW_I2C
+#include "ddk750_hwi2c.h"
+#endif
+#include "ddk750_swi2c.h"
+#endif
diff --git a/drivers/staging/sm750fb/ddk750_chip.c b/drivers/staging/sm750fb/ddk750_chip.c
new file mode 100644
index 000000000..7b28328c9
--- /dev/null
+++ b/drivers/staging/sm750fb/ddk750_chip.c
@@ -0,0 +1,622 @@
+#include "ddk750_help.h"
+#include "ddk750_reg.h"
+#include "ddk750_chip.h"
+#include "ddk750_power.h"
+typedef struct _pllcalparam {
+ unsigned char power;/* d : 0~ 6*/
+ unsigned char pod;
+ unsigned char od;
+ unsigned char value;/* value of 2 power d (2^d) */
+}
+pllcalparam;
+
+
+logical_chip_type_t getChipType(void)
+{
+ unsigned short physicalID;
+ char physicalRev;
+ logical_chip_type_t chip;
+
+ physicalID = devId750;//either 0x718 or 0x750
+ physicalRev = revId750;
+
+ if (physicalID == 0x718)
+ chip = SM718;
+ else if (physicalID == 0x750) {
+ chip = SM750;
+ /* SM750 and SM750LE are different in their revision ID only. */
+ if (physicalRev == SM750LE_REVISION_ID)
+ chip = SM750LE;
+ } else
+ chip = SM_UNKNOWN;
+
+ return chip;
+}
+
+
+inline unsigned int twoToPowerOfx(unsigned long x)
+{
+ unsigned long i;
+ unsigned long result = 1;
+
+ for (i = 1; i <= x; i++)
+ result *= 2;
+ return result;
+}
+
+inline unsigned int calcPLL(pll_value_t *pPLL)
+{
+ return (pPLL->inputFreq * pPLL->M / pPLL->N / twoToPowerOfx(pPLL->OD) / twoToPowerOfx(pPLL->POD));
+}
+
+unsigned int getPllValue(clock_type_t clockType, pll_value_t *pPLL)
+{
+ unsigned int ulPllReg = 0;
+
+ pPLL->inputFreq = DEFAULT_INPUT_CLOCK;
+ pPLL->clockType = clockType;
+
+ switch (clockType) {
+ case MXCLK_PLL:
+ ulPllReg = PEEK32(MXCLK_PLL_CTRL);
+ break;
+ case PRIMARY_PLL:
+ ulPllReg = PEEK32(PANEL_PLL_CTRL);
+ break;
+ case SECONDARY_PLL:
+ ulPllReg = PEEK32(CRT_PLL_CTRL);
+ break;
+ case VGA0_PLL:
+ ulPllReg = PEEK32(VGA_PLL0_CTRL);
+ break;
+ case VGA1_PLL:
+ ulPllReg = PEEK32(VGA_PLL1_CTRL);
+ break;
+ }
+
+ pPLL->M = FIELD_GET(ulPllReg, PANEL_PLL_CTRL, M);
+ pPLL->N = FIELD_GET(ulPllReg, PANEL_PLL_CTRL, N);
+ pPLL->OD = FIELD_GET(ulPllReg, PANEL_PLL_CTRL, OD);
+ pPLL->POD = FIELD_GET(ulPllReg, PANEL_PLL_CTRL, POD);
+
+ return calcPLL(pPLL);
+}
+
+
+unsigned int getChipClock(void)
+{
+ pll_value_t pll;
+#if 1
+ if (getChipType() == SM750LE)
+ return MHz(130);
+#endif
+
+ return getPllValue(MXCLK_PLL, &pll);
+}
+
+
+/*
+ * This function set up the main chip clock.
+ *
+ * Input: Frequency to be set.
+ */
+void setChipClock(unsigned int frequency)
+{
+ pll_value_t pll;
+ unsigned int ulActualMxClk;
+#if 1
+ /* Cheok_0509: For SM750LE, the chip clock is fixed. Nothing to set. */
+ if (getChipType() == SM750LE)
+ return;
+#endif
+
+ if (frequency) {
+ /*
+ * Set up PLL, a structure to hold the value to be set in clocks.
+ */
+ pll.inputFreq = DEFAULT_INPUT_CLOCK; /* Defined in CLOCK.H */
+ pll.clockType = MXCLK_PLL;
+
+ /*
+ * Call calcPllValue() to fill up the other fields for PLL structure.
+ * Sometime, the chip cannot set up the exact clock required by User.
+ * Return value from calcPllValue() gives the actual possible clock.
+ */
+ ulActualMxClk = calcPllValue(frequency, &pll);
+
+ /* Master Clock Control: MXCLK_PLL */
+ POKE32(MXCLK_PLL_CTRL, formatPllReg(&pll));
+ }
+}
+
+
+
+void setMemoryClock(unsigned int frequency)
+{
+ unsigned int ulReg, divisor;
+ #if 1
+ /* Cheok_0509: For SM750LE, the memory clock is fixed. Nothing to set. */
+ if (getChipType() == SM750LE)
+ return;
+#endif
+ if (frequency) {
+ /* Set the frequency to the maximum frequency that the DDR Memory can take
+ which is 336MHz. */
+ if (frequency > MHz(336))
+ frequency = MHz(336);
+
+ /* Calculate the divisor */
+ divisor = (unsigned int) roundedDiv(getChipClock(), frequency);
+
+ /* Set the corresponding divisor in the register. */
+ ulReg = PEEK32(CURRENT_GATE);
+ switch (divisor) {
+ default:
+ case 1:
+ ulReg = FIELD_SET(ulReg, CURRENT_GATE, M2XCLK, DIV_1);
+ break;
+ case 2:
+ ulReg = FIELD_SET(ulReg, CURRENT_GATE, M2XCLK, DIV_2);
+ break;
+ case 3:
+ ulReg = FIELD_SET(ulReg, CURRENT_GATE, M2XCLK, DIV_3);
+ break;
+ case 4:
+ ulReg = FIELD_SET(ulReg, CURRENT_GATE, M2XCLK, DIV_4);
+ break;
+ }
+
+ setCurrentGate(ulReg);
+ }
+}
+
+
+/*
+ * This function set up the master clock (MCLK).
+ *
+ * Input: Frequency to be set.
+ *
+ * NOTE:
+ * The maximum frequency the engine can run is 168MHz.
+ */
+void setMasterClock(unsigned int frequency)
+{
+ unsigned int ulReg, divisor;
+#if 1
+ /* Cheok_0509: For SM750LE, the memory clock is fixed. Nothing to set. */
+ if (getChipType() == SM750LE)
+ return;
+#endif
+ if (frequency) {
+ /* Set the frequency to the maximum frequency that the SM750 engine can
+ run, which is about 190 MHz. */
+ if (frequency > MHz(190))
+ frequency = MHz(190);
+
+ /* Calculate the divisor */
+ divisor = (unsigned int) roundedDiv(getChipClock(), frequency);
+
+ /* Set the corresponding divisor in the register. */
+ ulReg = PEEK32(CURRENT_GATE);
+ switch (divisor) {
+ default:
+ case 3:
+ ulReg = FIELD_SET(ulReg, CURRENT_GATE, MCLK, DIV_3);
+ break;
+ case 4:
+ ulReg = FIELD_SET(ulReg, CURRENT_GATE, MCLK, DIV_4);
+ break;
+ case 6:
+ ulReg = FIELD_SET(ulReg, CURRENT_GATE, MCLK, DIV_6);
+ break;
+ case 8:
+ ulReg = FIELD_SET(ulReg, CURRENT_GATE, MCLK, DIV_8);
+ break;
+ }
+
+ setCurrentGate(ulReg);
+ }
+}
+
+
+unsigned int ddk750_getVMSize(void)
+{
+ unsigned int reg;
+ unsigned int data;
+
+ /* sm750le only use 64 mb memory*/
+ if (getChipType() == SM750LE)
+ return MB(64);
+
+ /* for 750,always use power mode0*/
+ reg = PEEK32(MODE0_GATE);
+ reg = FIELD_SET(reg, MODE0_GATE, GPIO, ON);
+ POKE32(MODE0_GATE, reg);
+
+ /* get frame buffer size from GPIO */
+ reg = FIELD_GET(PEEK32(MISC_CTRL), MISC_CTRL, LOCALMEM_SIZE);
+ switch (reg) {
+ case MISC_CTRL_LOCALMEM_SIZE_8M:
+ data = MB(8); break; /* 8 Mega byte */
+ case MISC_CTRL_LOCALMEM_SIZE_16M:
+ data = MB(16); break; /* 16 Mega byte */
+ case MISC_CTRL_LOCALMEM_SIZE_32M:
+ data = MB(32); break; /* 32 Mega byte */
+ case MISC_CTRL_LOCALMEM_SIZE_64M:
+ data = MB(64); break; /* 64 Mega byte */
+ default:
+ data = 0;
+ break;
+ }
+ return data;
+
+}
+
+int ddk750_initHw(initchip_param_t *pInitParam)
+{
+
+ unsigned int ulReg;
+#if 0
+ //move the code to map regiter function.
+ if (getChipType() == SM718) {
+ /* turn on big endian bit*/
+ ulReg = PEEK32(0x74);
+ /* now consider register definition in a big endian pattern*/
+ POKE32(0x74, ulReg|0x80000000);
+ }
+
+#endif
+
+
+ if (pInitParam->powerMode != 0 )
+ pInitParam->powerMode = 0;
+ setPowerMode(pInitParam->powerMode);
+
+ /* Enable display power gate & LOCALMEM power gate*/
+ ulReg = PEEK32(CURRENT_GATE);
+ ulReg = FIELD_SET(ulReg, CURRENT_GATE, DISPLAY, ON);
+ ulReg = FIELD_SET(ulReg, CURRENT_GATE, LOCALMEM, ON);
+ setCurrentGate(ulReg);
+
+ if (getChipType() != SM750LE) {
+ /* set panel pll and graphic mode via mmio_88 */
+ ulReg = PEEK32(VGA_CONFIGURATION);
+ ulReg = FIELD_SET(ulReg, VGA_CONFIGURATION, PLL, PANEL);
+ ulReg = FIELD_SET(ulReg, VGA_CONFIGURATION, MODE, GRAPHIC);
+ POKE32(VGA_CONFIGURATION, ulReg);
+ } else {
+#if defined(__i386__) || defined( __x86_64__)
+ /* set graphic mode via IO method */
+ outb_p(0x88, 0x3d4);
+ outb_p(0x06, 0x3d5);
+#endif
+ }
+
+ /* Set the Main Chip Clock */
+ setChipClock(MHz((unsigned int)pInitParam->chipClock));
+
+ /* Set up memory clock. */
+ setMemoryClock(MHz(pInitParam->memClock));
+
+ /* Set up master clock */
+ setMasterClock(MHz(pInitParam->masterClock));
+
+
+ /* Reset the memory controller. If the memory controller is not reset in SM750,
+ the system might hang when sw accesses the memory.
+ The memory should be resetted after changing the MXCLK.
+ */
+ if (pInitParam->resetMemory == 1) {
+ ulReg = PEEK32(MISC_CTRL);
+ ulReg = FIELD_SET(ulReg, MISC_CTRL, LOCALMEM_RESET, RESET);
+ POKE32(MISC_CTRL, ulReg);
+
+ ulReg = FIELD_SET(ulReg, MISC_CTRL, LOCALMEM_RESET, NORMAL);
+ POKE32(MISC_CTRL, ulReg);
+ }
+
+ if (pInitParam->setAllEngOff == 1) {
+ enable2DEngine(0);
+
+ /* Disable Overlay, if a former application left it on */
+ ulReg = PEEK32(VIDEO_DISPLAY_CTRL);
+ ulReg = FIELD_SET(ulReg, VIDEO_DISPLAY_CTRL, PLANE, DISABLE);
+ POKE32(VIDEO_DISPLAY_CTRL, ulReg);
+
+ /* Disable video alpha, if a former application left it on */
+ ulReg = PEEK32(VIDEO_ALPHA_DISPLAY_CTRL);
+ ulReg = FIELD_SET(ulReg, VIDEO_ALPHA_DISPLAY_CTRL, PLANE, DISABLE);
+ POKE32(VIDEO_ALPHA_DISPLAY_CTRL, ulReg);
+
+ /* Disable alpha plane, if a former application left it on */
+ ulReg = PEEK32(ALPHA_DISPLAY_CTRL);
+ ulReg = FIELD_SET(ulReg, ALPHA_DISPLAY_CTRL, PLANE, DISABLE);
+ POKE32(ALPHA_DISPLAY_CTRL, ulReg);
+
+#if 0
+ /* Disable LCD hardware cursor, if a former application left it on */
+ ulReg = PEEK32(PANEL_HWC_ADDRESS);
+ ulReg = FIELD_SET(ulReg, PANEL_HWC_ADDRESS, ENABLE, DISABLE);
+ POKE32(PANEL_HWC_ADDRESS, ulReg);
+
+ /* Disable CRT hardware cursor, if a former application left it on */
+ ulReg = PEEK32(CRT_HWC_ADDRESS);
+ ulReg = FIELD_SET(ulReg, CRT_HWC_ADDRESS, ENABLE, DISABLE);
+ POKE32(CRT_HWC_ADDRESS, ulReg);
+
+ /* Disable ZV Port 0, if a former application left it on */
+ ulReg = PEEK32(ZV0_CAPTURE_CTRL);
+ ulReg = FIELD_SET(ulReg, ZV0_CAPTURE_CTRL, CAP, DISABLE);
+ POKE32(ZV0_CAPTURE_CTRL, ulReg);
+
+ /* Disable ZV Port 1, if a former application left it on */
+ ulReg = PEEK32(ZV1_CAPTURE_CTRL);
+ ulReg = FIELD_SET(ulReg, ZV1_CAPTURE_CTRL, CAP, DISABLE);
+ POKE32(ZV1_CAPTURE_CTRL, ulReg);
+
+ /* Disable ZV Port Power, if a former application left it on */
+ enableZVPort(0);
+ /* Disable DMA Channel, if a former application left it on */
+ ulReg = PEEK32(DMA_ABORT_INTERRUPT);
+ ulReg = FIELD_SET(ulReg, DMA_ABORT_INTERRUPT, ABORT_1, ABORT);
+ POKE32(DMA_ABORT_INTERRUPT, ulReg);
+
+ /* Disable i2c */
+ enableI2C(0);
+#endif
+ /* Disable DMA Channel, if a former application left it on */
+ ulReg = PEEK32(DMA_ABORT_INTERRUPT);
+ ulReg = FIELD_SET(ulReg, DMA_ABORT_INTERRUPT, ABORT_1, ABORT);
+ POKE32(DMA_ABORT_INTERRUPT, ulReg);
+
+ /* Disable DMA Power, if a former application left it on */
+ enableDMA(0);
+ }
+
+ /* We can add more initialization as needed. */
+
+ return 0;
+}
+
+#if 0
+
+unsigned int absDiff(unsigned int a, unsigned int b)
+{
+ if ( a > b )
+ return(a - b);
+ else
+ return(b - a);
+}
+
+#endif
+/*
+ monk liu @ 4/6/2011:
+ re-write the calculatePLL function of ddk750.
+ the original version function does not use some mathematics tricks and shortcut
+ when it doing the calculation of the best N,M,D combination
+ I think this version gives a little upgrade in speed
+
+ 750 pll clock formular:
+ Request Clock = (Input Clock * M )/(N * X)
+
+ Input Clock = 14318181 hz
+ X = 2 power D
+ D ={0,1,2,3,4,5,6}
+ M = {1,...,255}
+ N = {2,...,15}
+*/
+unsigned int calcPllValue(unsigned int request_orig, pll_value_t *pll)
+{
+ /* used for primary and secondary channel pixel clock pll */
+ static pllcalparam xparm_PIXEL[] = {
+ /* 2^0 = 1*/ {0, 0, 0, 1},
+ /* 2^ 1 =2*/ {1, 0, 1, 2},
+ /* 2^ 2 = 4*/ {2, 0, 2, 4},
+ {3, 0, 3, 8},
+ {4, 1, 3, 16},
+ {5, 2, 3, 32},
+ /* 2^6 = 64 */ {6, 3, 3, 64},
+ };
+
+ /* used for MXCLK (chip clock) */
+ static pllcalparam xparm_MXCLK[] = {
+ /* 2^0 = 1*/ {0, 0, 0, 1},
+ /* 2^ 1 =2*/ {1, 0, 1, 2},
+ /* 2^ 2 = 4*/ {2, 0, 2, 4},
+ {3, 0, 3, 8},
+ };
+
+ /* as sm750 register definition, N located in 2,15 and M located in 1,255 */
+ int N, M, X, d;
+ int xcnt;
+ int miniDiff;
+ unsigned int RN, quo, rem, fl_quo;
+ unsigned int input, request;
+ unsigned int tmpClock, ret;
+ pllcalparam * xparm;
+
+#if 1
+ if (getChipType() == SM750LE) {
+ /* SM750LE don't have prgrammable PLL and M/N values to work on.
+ Just return the requested clock. */
+ return request_orig;
+ }
+#endif
+
+ ret = 0;
+ miniDiff = ~0;
+ request = request_orig / 1000;
+ input = pll->inputFreq / 1000;
+
+ /* for MXCLK register , no POD provided, so need be treated differently */
+
+ if (pll->clockType != MXCLK_PLL) {
+ xparm = &xparm_PIXEL[0];
+ xcnt = sizeof(xparm_PIXEL)/sizeof(xparm_PIXEL[0]);
+ } else {
+ xparm = &xparm_MXCLK[0];
+ xcnt = sizeof(xparm_MXCLK)/sizeof(xparm_MXCLK[0]);
+ }
+
+
+ for (N = 15; N > 1; N--) {
+ /* RN will not exceed maximum long if @request <= 285 MHZ (for 32bit cpu) */
+ RN = N * request;
+ quo = RN / input;
+ rem = RN % input;/* rem always small than 14318181 */
+ fl_quo = (rem * 10000 /input);
+
+ for (d = xcnt - 1; d >= 0; d--) {
+ X = xparm[d].value;
+ M = quo*X;
+ M += fl_quo * X / 10000;
+ /* round step */
+ M += (fl_quo*X % 10000)>5000?1:0;
+ if (M < 256 && M > 0) {
+ unsigned int diff;
+ tmpClock = pll->inputFreq *M / N / X;
+ diff = absDiff(tmpClock, request_orig);
+ if (diff < miniDiff) {
+ pll->M = M;
+ pll->N = N;
+ pll->OD = xparm[d].od;
+ pll->POD = xparm[d].pod;
+ miniDiff = diff;
+ ret = tmpClock;
+ }
+ }
+ }
+ }
+
+ //printk("Finally: pll->n[%lu],m[%lu],od[%lu],pod[%lu]\n",pll->N,pll->M,pll->OD,pll->POD);
+ return ret;
+}
+
+unsigned int calcPllValue2(
+unsigned int ulRequestClk, /* Required pixel clock in Hz unit */
+pll_value_t *pPLL /* Structure to hold the value to be set in PLL */
+)
+{
+ unsigned int M, N, OD, POD = 0, diff, pllClk, odPower, podPower;
+ unsigned int bestDiff = 0xffffffff; /* biggest 32 bit unsigned number */
+ unsigned int ret;
+ /* Init PLL structure to know states */
+ pPLL->M = 0;
+ pPLL->N = 0;
+ pPLL->OD = 0;
+ pPLL->POD = 0;
+
+ /* Sanity check: None at the moment */
+
+ /* Convert everything in Khz range in order to avoid calculation overflow */
+ pPLL->inputFreq /= 1000;
+ ulRequestClk /= 1000;
+
+#ifndef VALIDATION_CHIP
+ /* The maximum of post divider is 8. */
+ for (POD = 0; POD <= 3; POD++)
+#endif
+ {
+
+#ifndef VALIDATION_CHIP
+ /* MXCLK_PLL does not have post divider. */
+ if ((POD > 0) && (pPLL->clockType == MXCLK_PLL))
+ break;
+#endif
+
+ /* Work out 2 to the power of POD */
+ podPower = twoToPowerOfx(POD);
+
+ /* OD has only 2 bits [15:14] and its value must between 0 to 3 */
+ for (OD = 0; OD <= 3; OD++) {
+ /* Work out 2 to the power of OD */
+ odPower = twoToPowerOfx(OD);
+
+#ifdef VALIDATION_CHIP
+ if (odPower > 4)
+ podPower = 4;
+ else
+ podPower = odPower;
+#endif
+
+ /* N has 4 bits [11:8] and its value must between 2 and 15.
+ The N == 1 will behave differently --> Result is not correct. */
+ for (N = 2; N <= 15; N++) {
+ /* The formula for PLL is ulRequestClk = inputFreq * M / N / (2^OD)
+ In the following steps, we try to work out a best M value given the others are known.
+ To avoid decimal calculation, we use 1000 as multiplier for up to 3 decimal places of accuracy.
+ */
+ M = ulRequestClk * N * odPower * 1000 / pPLL->inputFreq;
+ M = roundedDiv(M, 1000);
+
+ /* M field has only 8 bits, reject value bigger than 8 bits */
+ if (M < 256) {
+ /* Calculate the actual clock for a given M & N */
+ pllClk = pPLL->inputFreq * M / N / odPower / podPower;
+
+ /* How much are we different from the requirement */
+ diff = absDiff(pllClk, ulRequestClk);
+
+ if (diff < bestDiff) {
+ bestDiff = diff;
+
+ /* Store M and N values */
+ pPLL->M = M;
+ pPLL->N = N;
+ pPLL->OD = OD;
+
+#ifdef VALIDATION_CHIP
+ if (OD > 2)
+ POD = 2;
+ else
+ POD = OD;
+#endif
+
+ pPLL->POD = POD;
+ }
+ }
+ }
+ }
+ }
+
+ /* Restore input frequency from Khz to hz unit */
+// pPLL->inputFreq *= 1000;
+ ulRequestClk *= 1000;
+ pPLL->inputFreq = DEFAULT_INPUT_CLOCK; /* Default reference clock */
+
+ /* Output debug information */
+ //DDKDEBUGPRINT((DISPLAY_LEVEL, "calcPllValue: Requested Frequency = %d\n", ulRequestClk));
+ //DDKDEBUGPRINT((DISPLAY_LEVEL, "calcPllValue: Input CLK = %dHz, M=%d, N=%d, OD=%d, POD=%d\n", pPLL->inputFreq, pPLL->M, pPLL->N, pPLL->OD, pPLL->POD));
+
+ /* Return actual frequency that the PLL can set */
+ ret = calcPLL(pPLL);
+ return ret;
+}
+
+
+
+
+
+unsigned int formatPllReg(pll_value_t *pPLL)
+{
+ unsigned int ulPllReg = 0;
+
+ /* Note that all PLL's have the same format. Here, we just use Panel PLL parameter
+ to work out the bit fields in the register.
+ On returning a 32 bit number, the value can be applied to any PLL in the calling function.
+ */
+ ulPllReg =
+ FIELD_SET( 0, PANEL_PLL_CTRL, BYPASS, OFF)
+ | FIELD_SET( 0, PANEL_PLL_CTRL, POWER, ON)
+ | FIELD_SET( 0, PANEL_PLL_CTRL, INPUT, OSC)
+#ifndef VALIDATION_CHIP
+ | FIELD_VALUE(0, PANEL_PLL_CTRL, POD, pPLL->POD)
+#endif
+ | FIELD_VALUE(0, PANEL_PLL_CTRL, OD, pPLL->OD)
+ | FIELD_VALUE(0, PANEL_PLL_CTRL, N, pPLL->N)
+ | FIELD_VALUE(0, PANEL_PLL_CTRL, M, pPLL->M);
+
+ return ulPllReg;
+}
+
+
diff --git a/drivers/staging/sm750fb/ddk750_chip.h b/drivers/staging/sm750fb/ddk750_chip.h
new file mode 100644
index 000000000..04cb0d559
--- /dev/null
+++ b/drivers/staging/sm750fb/ddk750_chip.h
@@ -0,0 +1,87 @@
+#ifndef DDK750_CHIP_H__
+#define DDK750_CHIP_H__
+#define DEFAULT_INPUT_CLOCK 14318181 /* Default reference clock */
+#ifndef SM750LE_REVISION_ID
+#define SM750LE_REVISION_ID ((unsigned char)0xfe)
+#endif
+
+#include <linux/io.h>
+
+/* This is all the chips recognized by this library */
+typedef enum _logical_chip_type_t
+{
+ SM_UNKNOWN,
+ SM718,
+ SM750,
+ SM750LE,
+}
+logical_chip_type_t;
+
+
+typedef enum _clock_type_t
+{
+ MXCLK_PLL,
+ PRIMARY_PLL,
+ SECONDARY_PLL,
+ VGA0_PLL,
+ VGA1_PLL,
+}
+clock_type_t;
+
+typedef struct _pll_value_t
+{
+ clock_type_t clockType;
+ unsigned long inputFreq; /* Input clock frequency to the PLL */
+
+ /* Use this when clockType = PANEL_PLL */
+ unsigned long M;
+ unsigned long N;
+ unsigned long OD;
+ unsigned long POD;
+}
+pll_value_t;
+
+/* input struct to initChipParam() function */
+typedef struct _initchip_param_t
+{
+ unsigned short powerMode; /* Use power mode 0 or 1 */
+ unsigned short chipClock; /* Speed of main chip clock in MHz unit
+ 0 = keep the current clock setting
+ Others = the new main chip clock
+ */
+ unsigned short memClock; /* Speed of memory clock in MHz unit
+ 0 = keep the current clock setting
+ Others = the new memory clock
+ */
+ unsigned short masterClock; /* Speed of master clock in MHz unit
+ 0 = keep the current clock setting
+ Others = the new master clock
+ */
+ unsigned short setAllEngOff; /* 0 = leave all engine state untouched.
+ 1 = make sure they are off: 2D, Overlay,
+ video alpha, alpha, hardware cursors
+ */
+ unsigned char resetMemory; /* 0 = Do not reset the memory controller
+ 1 = Reset the memory controller
+ */
+
+ /* More initialization parameter can be added if needed */
+}
+initchip_param_t;
+
+
+logical_chip_type_t getChipType(void);
+unsigned int calcPllValue(unsigned int request,pll_value_t *pll);
+unsigned int calcPllValue2(unsigned int,pll_value_t *);
+unsigned int formatPllReg(pll_value_t *pPLL);
+void ddk750_set_mmio(void __iomem *,unsigned short,char);
+unsigned int ddk750_getVMSize(void);
+int ddk750_initHw(initchip_param_t *);
+unsigned int getPllValue(clock_type_t clockType, pll_value_t *pPLL);
+unsigned int getChipClock(void);
+void setChipClock(unsigned int);
+void setMemoryClock(unsigned int frequency);
+void setMasterClock(unsigned int frequency);
+
+
+#endif
diff --git a/drivers/staging/sm750fb/ddk750_display.c b/drivers/staging/sm750fb/ddk750_display.c
new file mode 100644
index 000000000..c84196ac0
--- /dev/null
+++ b/drivers/staging/sm750fb/ddk750_display.c
@@ -0,0 +1,307 @@
+#include "ddk750_reg.h"
+#include "ddk750_help.h"
+#include "ddk750_display.h"
+#include "ddk750_power.h"
+#include "ddk750_dvi.h"
+
+#define primaryWaitVerticalSync(delay) waitNextVerticalSync(0,delay)
+
+static void setDisplayControl(int ctrl,int dispState)
+{
+ /* state != 0 means turn on both timing & plane en_bit */
+ unsigned long ulDisplayCtrlReg, ulReservedBits;
+ int cnt;
+
+ cnt = 0;
+
+ /* Set the primary display control */
+ if (!ctrl)
+ {
+ ulDisplayCtrlReg = PEEK32(PANEL_DISPLAY_CTRL);
+ /* Turn on/off the Panel display control */
+ if (dispState)
+ {
+ /* Timing should be enabled first before enabling the plane
+ * because changing at the same time does not guarantee that
+ * the plane will also enabled or disabled.
+ */
+ ulDisplayCtrlReg = FIELD_SET(ulDisplayCtrlReg,
+ PANEL_DISPLAY_CTRL, TIMING, ENABLE);
+ POKE32(PANEL_DISPLAY_CTRL, ulDisplayCtrlReg);
+
+ ulDisplayCtrlReg = FIELD_SET(ulDisplayCtrlReg,
+ PANEL_DISPLAY_CTRL, PLANE, ENABLE);
+
+ /* Added some masks to mask out the reserved bits.
+ * Sometimes, the reserved bits are set/reset randomly when
+ * writing to the PRIMARY_DISPLAY_CTRL, therefore, the register
+ * reserved bits are needed to be masked out.
+ */
+ ulReservedBits = FIELD_SET(0, PANEL_DISPLAY_CTRL, RESERVED_1_MASK, ENABLE) |
+ FIELD_SET(0, PANEL_DISPLAY_CTRL, RESERVED_2_MASK, ENABLE) |
+ FIELD_SET(0, PANEL_DISPLAY_CTRL, RESERVED_3_MASK, ENABLE);
+
+ /* Somehow the register value on the plane is not set
+ * until a few delay. Need to write
+ * and read it a couple times
+ */
+ do
+ {
+ cnt++;
+ POKE32(PANEL_DISPLAY_CTRL, ulDisplayCtrlReg);
+ } while((PEEK32(PANEL_DISPLAY_CTRL) & ~ulReservedBits) !=
+ (ulDisplayCtrlReg & ~ulReservedBits));
+ printk("Set Panel Plane enbit:after tried %d times\n",cnt);
+ }
+ else
+ {
+ /* When turning off, there is no rule on the programming
+ * sequence since whenever the clock is off, then it does not
+ * matter whether the plane is enabled or disabled.
+ * Note: Modifying the plane bit will take effect on the
+ * next vertical sync. Need to find out if it is necessary to
+ * wait for 1 vsync before modifying the timing enable bit.
+ * */
+ ulDisplayCtrlReg = FIELD_SET(ulDisplayCtrlReg,
+ PANEL_DISPLAY_CTRL, PLANE, DISABLE);
+ POKE32(PANEL_DISPLAY_CTRL, ulDisplayCtrlReg);
+
+ ulDisplayCtrlReg = FIELD_SET(ulDisplayCtrlReg,
+ PANEL_DISPLAY_CTRL, TIMING, DISABLE);
+ POKE32(PANEL_DISPLAY_CTRL, ulDisplayCtrlReg);
+ }
+
+ }
+ /* Set the secondary display control */
+ else
+ {
+ ulDisplayCtrlReg = PEEK32(CRT_DISPLAY_CTRL);
+
+ if (dispState)
+ {
+ /* Timing should be enabled first before enabling the plane because changing at the
+ same time does not guarantee that the plane will also enabled or disabled.
+ */
+ ulDisplayCtrlReg = FIELD_SET(ulDisplayCtrlReg,
+ CRT_DISPLAY_CTRL, TIMING, ENABLE);
+ POKE32(CRT_DISPLAY_CTRL, ulDisplayCtrlReg);
+
+ ulDisplayCtrlReg = FIELD_SET(ulDisplayCtrlReg,
+ CRT_DISPLAY_CTRL, PLANE, ENABLE);
+
+ /* Added some masks to mask out the reserved bits.
+ * Sometimes, the reserved bits are set/reset randomly when
+ * writing to the PRIMARY_DISPLAY_CTRL, therefore, the register
+ * reserved bits are needed to be masked out.
+ */
+
+ ulReservedBits = FIELD_SET(0, CRT_DISPLAY_CTRL, RESERVED_1_MASK, ENABLE) |
+ FIELD_SET(0, CRT_DISPLAY_CTRL, RESERVED_2_MASK, ENABLE) |
+ FIELD_SET(0, CRT_DISPLAY_CTRL, RESERVED_3_MASK, ENABLE) |
+ FIELD_SET(0, CRT_DISPLAY_CTRL, RESERVED_4_MASK, ENABLE);
+
+ do
+ {
+ cnt++;
+ POKE32(CRT_DISPLAY_CTRL, ulDisplayCtrlReg);
+ } while((PEEK32(CRT_DISPLAY_CTRL) & ~ulReservedBits) !=
+ (ulDisplayCtrlReg & ~ulReservedBits));
+ printk("Set Crt Plane enbit:after tried %d times\n",cnt);
+ }
+ else
+ {
+ /* When turning off, there is no rule on the programming
+ * sequence since whenever the clock is off, then it does not
+ * matter whether the plane is enabled or disabled.
+ * Note: Modifying the plane bit will take effect on the next
+ * vertical sync. Need to find out if it is necessary to
+ * wait for 1 vsync before modifying the timing enable bit.
+ */
+ ulDisplayCtrlReg = FIELD_SET(ulDisplayCtrlReg,
+ CRT_DISPLAY_CTRL, PLANE, DISABLE);
+ POKE32(CRT_DISPLAY_CTRL, ulDisplayCtrlReg);
+
+ ulDisplayCtrlReg = FIELD_SET(ulDisplayCtrlReg,
+ CRT_DISPLAY_CTRL, TIMING, DISABLE);
+ POKE32(CRT_DISPLAY_CTRL, ulDisplayCtrlReg);
+ }
+ }
+}
+
+
+static void waitNextVerticalSync(int ctrl,int delay)
+{
+ unsigned int status;
+ if(!ctrl){
+ /* primary controller */
+
+ /* Do not wait when the Primary PLL is off or display control is already off.
+ This will prevent the software to wait forever. */
+ if ((FIELD_GET(PEEK32(PANEL_PLL_CTRL), PANEL_PLL_CTRL, POWER) ==
+ PANEL_PLL_CTRL_POWER_OFF) ||
+ (FIELD_GET(PEEK32(PANEL_DISPLAY_CTRL), PANEL_DISPLAY_CTRL, TIMING) ==
+ PANEL_DISPLAY_CTRL_TIMING_DISABLE))
+ {
+ return;
+ }
+
+ while (delay-- > 0)
+ {
+ /* Wait for end of vsync. */
+ do
+ {
+ status = FIELD_GET(PEEK32(SYSTEM_CTRL),
+ SYSTEM_CTRL,
+ PANEL_VSYNC);
+ }
+ while (status == SYSTEM_CTRL_PANEL_VSYNC_ACTIVE);
+
+ /* Wait for start of vsync. */
+ do
+ {
+ status = FIELD_GET(PEEK32(SYSTEM_CTRL),
+ SYSTEM_CTRL,
+ PANEL_VSYNC);
+ }
+ while (status == SYSTEM_CTRL_PANEL_VSYNC_INACTIVE);
+ }
+
+ }else{
+
+ /* Do not wait when the Primary PLL is off or display control is already off.
+ This will prevent the software to wait forever. */
+ if ((FIELD_GET(PEEK32(CRT_PLL_CTRL), CRT_PLL_CTRL, POWER) ==
+ CRT_PLL_CTRL_POWER_OFF) ||
+ (FIELD_GET(PEEK32(CRT_DISPLAY_CTRL), CRT_DISPLAY_CTRL, TIMING) ==
+ CRT_DISPLAY_CTRL_TIMING_DISABLE))
+ {
+ return;
+ }
+
+ while (delay-- > 0)
+ {
+ /* Wait for end of vsync. */
+ do
+ {
+ status = FIELD_GET(PEEK32(SYSTEM_CTRL),
+ SYSTEM_CTRL,
+ CRT_VSYNC);
+ }
+ while (status == SYSTEM_CTRL_CRT_VSYNC_ACTIVE);
+
+ /* Wait for start of vsync. */
+ do
+ {
+ status = FIELD_GET(PEEK32(SYSTEM_CTRL),
+ SYSTEM_CTRL,
+ CRT_VSYNC);
+ }
+ while (status == SYSTEM_CTRL_CRT_VSYNC_INACTIVE);
+ }
+ }
+}
+
+static void swPanelPowerSequence(int disp,int delay)
+{
+ unsigned int reg;
+
+ /* disp should be 1 to open sequence */
+ reg = PEEK32(PANEL_DISPLAY_CTRL);
+ reg = FIELD_VALUE(reg,PANEL_DISPLAY_CTRL,FPEN,disp);
+ POKE32(PANEL_DISPLAY_CTRL,reg);
+ primaryWaitVerticalSync(delay);
+
+
+ reg = PEEK32(PANEL_DISPLAY_CTRL);
+ reg = FIELD_VALUE(reg,PANEL_DISPLAY_CTRL,DATA,disp);
+ POKE32(PANEL_DISPLAY_CTRL,reg);
+ primaryWaitVerticalSync(delay);
+
+ reg = PEEK32(PANEL_DISPLAY_CTRL);
+ reg = FIELD_VALUE(reg,PANEL_DISPLAY_CTRL,VBIASEN,disp);
+ POKE32(PANEL_DISPLAY_CTRL,reg);
+ primaryWaitVerticalSync(delay);
+
+
+ reg = PEEK32(PANEL_DISPLAY_CTRL);
+ reg = FIELD_VALUE(reg,PANEL_DISPLAY_CTRL,FPEN,disp);
+ POKE32(PANEL_DISPLAY_CTRL,reg);
+ primaryWaitVerticalSync(delay);
+
+}
+
+void ddk750_setLogicalDispOut(disp_output_t output)
+{
+ unsigned int reg;
+ if(output & PNL_2_USAGE){
+ /* set panel path controller select */
+ reg = PEEK32(PANEL_DISPLAY_CTRL);
+ reg = FIELD_VALUE(reg,PANEL_DISPLAY_CTRL,SELECT,(output & PNL_2_MASK)>>PNL_2_OFFSET);
+ POKE32(PANEL_DISPLAY_CTRL,reg);
+ }
+
+ if(output & CRT_2_USAGE){
+ /* set crt path controller select */
+ reg = PEEK32(CRT_DISPLAY_CTRL);
+ reg = FIELD_VALUE(reg,CRT_DISPLAY_CTRL,SELECT,(output & CRT_2_MASK)>>CRT_2_OFFSET);
+ /*se blank off */
+ reg = FIELD_SET(reg,CRT_DISPLAY_CTRL,BLANK,OFF);
+ POKE32(CRT_DISPLAY_CTRL,reg);
+
+ }
+
+ if(output & PRI_TP_USAGE){
+ /* set primary timing and plane en_bit */
+ setDisplayControl(0,(output&PRI_TP_MASK)>>PRI_TP_OFFSET);
+ }
+
+ if(output & SEC_TP_USAGE){
+ /* set secondary timing and plane en_bit*/
+ setDisplayControl(1,(output&SEC_TP_MASK)>>SEC_TP_OFFSET);
+ }
+
+ if(output & PNL_SEQ_USAGE){
+ /* set panel sequence */
+ swPanelPowerSequence((output&PNL_SEQ_MASK)>>PNL_SEQ_OFFSET,4);
+ }
+
+ if(output & DAC_USAGE)
+ setDAC((output & DAC_MASK)>>DAC_OFFSET);
+
+ if(output & DPMS_USAGE)
+ ddk750_setDPMS((output & DPMS_MASK) >> DPMS_OFFSET);
+}
+
+
+int ddk750_initDVIDisp(void)
+{
+ /* Initialize DVI. If the dviInit fail and the VendorID or the DeviceID are
+ not zeroed, then set the failure flag. If it is zeroe, it might mean
+ that the system is in Dual CRT Monitor configuration. */
+
+ /* De-skew enabled with default 111b value.
+ This will fix some artifacts problem in some mode on board 2.2.
+ Somehow this fix does not affect board 2.1.
+ */
+ if ((dviInit(1, /* Select Rising Edge */
+ 1, /* Select 24-bit bus */
+ 0, /* Select Single Edge clock */
+ 1, /* Enable HSync as is */
+ 1, /* Enable VSync as is */
+ 1, /* Enable De-skew */
+ 7, /* Set the de-skew setting to maximum setup */
+ 1, /* Enable continuous Sync */
+ 1, /* Enable PLL Filter */
+ 4 /* Use the recommended value for PLL Filter value */
+ ) != 0) && (dviGetVendorID() != 0x0000) && (dviGetDeviceID() != 0x0000))
+ {
+ return (-1);
+ }
+
+ /* TODO: Initialize other display component */
+
+ /* Success */
+ return 0;
+
+}
+
diff --git a/drivers/staging/sm750fb/ddk750_display.h b/drivers/staging/sm750fb/ddk750_display.h
new file mode 100644
index 000000000..ae0f84c68
--- /dev/null
+++ b/drivers/staging/sm750fb/ddk750_display.h
@@ -0,0 +1,160 @@
+#ifndef DDK750_DISPLAY_H__
+#define DDK750_DISPLAY_H__
+
+/* panel path select
+ 80000[29:28]
+*/
+
+#define PNL_2_OFFSET 0
+#define PNL_2_MASK (3 << PNL_2_OFFSET)
+#define PNL_2_USAGE (PNL_2_MASK << 16)
+#define PNL_2_PRI ((0 << PNL_2_OFFSET)|PNL_2_USAGE)
+#define PNL_2_SEC ((2 << PNL_2_OFFSET)|PNL_2_USAGE)
+
+
+/* primary timing & plane enable bit
+ 1: 80000[8] & 80000[2] on
+ 0: both off
+*/
+#define PRI_TP_OFFSET 4
+#define PRI_TP_MASK (1 << PRI_TP_OFFSET)
+#define PRI_TP_USAGE (PRI_TP_MASK << 16)
+#define PRI_TP_ON ((0x1 << PRI_TP_OFFSET)|PRI_TP_USAGE)
+#define PRI_TP_OFF ((0x0 << PRI_TP_OFFSET)|PRI_TP_USAGE)
+
+
+/* panel sequency status
+ 80000[27:24]
+*/
+#define PNL_SEQ_OFFSET 6
+#define PNL_SEQ_MASK (1 << PNL_SEQ_OFFSET)
+#define PNL_SEQ_USAGE (PNL_SEQ_MASK << 16)
+#define PNL_SEQ_ON ((1 << PNL_SEQ_OFFSET)|PNL_SEQ_USAGE)
+#define PNL_SEQ_OFF ((0 << PNL_SEQ_OFFSET)|PNL_SEQ_USAGE)
+
+/* dual digital output
+ 80000[19]
+*/
+#define DUAL_TFT_OFFSET 8
+#define DUAL_TFT_MASK (1 << DUAL_TFT_OFFSET)
+#define DUAL_TFT_USAGE (DUAL_TFT_MASK << 16)
+#define DUAL_TFT_ON ((1 << DUAL_TFT_OFFSET)|DUAL_TFT_USAGE)
+#define DUAL_TFT_OFF ((0 << DUAL_TFT_OFFSET)|DUAL_TFT_USAGE)
+
+/* secondary timing & plane enable bit
+ 1:80200[8] & 80200[2] on
+ 0: both off
+*/
+#define SEC_TP_OFFSET 5
+#define SEC_TP_MASK (1<< SEC_TP_OFFSET)
+#define SEC_TP_USAGE (SEC_TP_MASK << 16)
+#define SEC_TP_ON ((0x1 << SEC_TP_OFFSET)|SEC_TP_USAGE)
+#define SEC_TP_OFF ((0x0 << SEC_TP_OFFSET)|SEC_TP_USAGE)
+
+/* crt path select
+ 80200[19:18]
+*/
+#define CRT_2_OFFSET 2
+#define CRT_2_MASK (3 << CRT_2_OFFSET)
+#define CRT_2_USAGE (CRT_2_MASK << 16)
+#define CRT_2_PRI ((0x0 << CRT_2_OFFSET)|CRT_2_USAGE)
+#define CRT_2_SEC ((0x2 << CRT_2_OFFSET)|CRT_2_USAGE)
+
+
+/* DAC affect both DVI and DSUB
+ 4[20]
+*/
+#define DAC_OFFSET 7
+#define DAC_MASK (1 << DAC_OFFSET)
+#define DAC_USAGE (DAC_MASK << 16)
+#define DAC_ON ((0x0<< DAC_OFFSET)|DAC_USAGE)
+#define DAC_OFF ((0x1 << DAC_OFFSET)|DAC_USAGE)
+
+/* DPMS only affect D-SUB head
+ 0[31:30]
+*/
+#define DPMS_OFFSET 9
+#define DPMS_MASK (3 << DPMS_OFFSET)
+#define DPMS_USAGE (DPMS_MASK << 16)
+#define DPMS_OFF ((3 << DPMS_OFFSET)|DPMS_USAGE)
+#define DPMS_ON ((0 << DPMS_OFFSET)|DPMS_USAGE)
+
+
+
+/*
+ LCD1 means panel path TFT1 & panel path DVI (so enable DAC)
+ CRT means crt path DSUB
+*/
+#if 0
+typedef enum _disp_output_t
+{
+ NO_DISPLAY = DPMS_OFF,
+
+ LCD1_PRI = PNL_2_PRI|PRI_TP_ON|PNL_SEQ_ON|DPMS_OFF|DAC_ON,
+ LCD1_SEC = PNL_2_SEC|SEC_TP_ON|PNL_SEQ_ON|DPMS_OFF|DAC_ON,
+
+ LCD2_PRI = CRT_2_PRI|PRI_TP_ON|DUAL_TFT_ON|DPMS_OFF,
+ LCD2_SEC = CRT_2_SEC|SEC_TP_ON|DUAL_TFT_ON|DPMS_OFF,
+
+ DSUB_PRI = CRT_2_PRI|PRI_TP_ON|DAC_ON,
+ DSUB_SEC = CRT_2_SEC|SEC_TP_ON|DAC_ON,
+
+ LCD1_DSUB_PRI = PNL_2_PRI|PRI_TP_ON|PNL_SEQ_ON|
+ CRT_2_PRI|SEC_TP_OFF|DAC_ON,
+
+ LCD1_DSUB_SEC = PNL_2_SEC|SEC_TP_ON|PNL_SEQ_ON|
+ CRT_2_SEC|PRI_TP_OFF|DAC_ON,
+
+ /* LCD1 show primary and DSUB show secondary */
+ LCD1_DSUB_DUAL = PNL_2_PRI|PRI_TP_ON|PNL_SEQ_ON|
+ CRT_2_SEC|SEC_TP_ON|DAC_ON,
+
+ /* LCD1 show secondary and DSUB show primary */
+ LCD1_DSUB_DUAL_SWAP = PNL_2_SEC|SEC_TP_ON|PNL_SEQ_ON|
+ CRT_2_PRI|PRI_TP_ON|DAC_ON,
+
+ LCD1_LCD2_PRI = PNL_2_PRI|PRI_TP_ON|PNL_SEQ_ON|
+ CRT_2_PRI|SEC_TP_OFF|DPMS_OFF|DUAL_TFT_ON,
+
+ LCD1_LCD2_SEC = PNL_2_SEC|SEC_TP_ON|PNL_SEQ_ON|
+ CRT_2_SEC|PRI_TP_OFF|DPMS_OFF|DUAL_TFT_ON,
+
+ LCD1_LCD2_DSUB_PRI = PNL_2_PRI|PRI_TP_ON|PNL_SEQ_ON|DAC_ON|
+ CRT_2_PRI|SEC_TP_OFF|DPMS_ON|DUAL_TFT_ON,
+
+ LCD1_LCD2_DSUB_SEC = PNL_2_SEC|SEC_TP_ON|PNL_SEQ_ON|DAC_ON|
+ CRT_2_SEC|PRI_TP_OFF|DPMS_ON|DUAL_TFT_ON,
+
+
+}
+disp_output_t;
+#else
+typedef enum _disp_output_t{
+ do_LCD1_PRI = PNL_2_PRI|PRI_TP_ON|PNL_SEQ_ON|DAC_ON,
+ do_LCD1_SEC = PNL_2_SEC|SEC_TP_ON|PNL_SEQ_ON|DAC_ON,
+#if 0
+ do_LCD2_PRI = CRT_2_PRI|PRI_TP_ON,
+ do_LCD2_SEC = CRT_2_SEC|SEC_TP_ON,
+#else
+ do_LCD2_PRI = CRT_2_PRI|PRI_TP_ON|DUAL_TFT_ON,
+ do_LCD2_SEC = CRT_2_SEC|SEC_TP_ON|DUAL_TFT_ON,
+#endif
+ /*
+ do_DSUB_PRI = CRT_2_PRI|PRI_TP_ON|DPMS_ON|DAC_ON,
+ do_DSUB_SEC = CRT_2_SEC|SEC_TP_ON|DPMS_ON|DAC_ON,
+ */
+#if 0
+ do_CRT_PRI = CRT_2_PRI|PRI_TP_ON,
+ do_CRT_SEC = CRT_2_SEC|SEC_TP_ON,
+#else
+ do_CRT_PRI = CRT_2_PRI|PRI_TP_ON|DPMS_ON|DAC_ON,
+ do_CRT_SEC = CRT_2_SEC|SEC_TP_ON|DPMS_ON|DAC_ON,
+#endif
+}
+disp_output_t;
+#endif
+
+void ddk750_setLogicalDispOut(disp_output_t);
+int ddk750_initDVIDisp(void);
+
+#endif
diff --git a/drivers/staging/sm750fb/ddk750_dvi.c b/drivers/staging/sm750fb/ddk750_dvi.c
new file mode 100644
index 000000000..f5932bbf1
--- /dev/null
+++ b/drivers/staging/sm750fb/ddk750_dvi.c
@@ -0,0 +1,99 @@
+#define USE_DVICHIP
+#ifdef USE_DVICHIP
+#include "ddk750_help.h"
+#include "ddk750_reg.h"
+#include "ddk750_dvi.h"
+#include "ddk750_sii164.h"
+
+
+/* This global variable contains all the supported driver and its corresponding
+ function API. Please set the function pointer to NULL whenever the function
+ is not supported. */
+static dvi_ctrl_device_t g_dcftSupportedDviController[] =
+{
+#ifdef DVI_CTRL_SII164
+ {
+ .pfnInit = sii164InitChip,
+ .pfnGetVendorId = sii164GetVendorID,
+ .pfnGetDeviceId = sii164GetDeviceID,
+#ifdef SII164_FULL_FUNCTIONS
+ .pfnResetChip = sii164ResetChip,
+ .pfnGetChipString = sii164GetChipString,
+ .pfnSetPower = sii164SetPower,
+ .pfnEnableHotPlugDetection = sii164EnableHotPlugDetection,
+ .pfnIsConnected = sii164IsConnected,
+ .pfnCheckInterrupt = sii164CheckInterrupt,
+ .pfnClearInterrupt = sii164ClearInterrupt,
+#endif
+ },
+#endif
+};
+
+
+int dviInit(
+ unsigned char edgeSelect,
+ unsigned char busSelect,
+ unsigned char dualEdgeClkSelect,
+ unsigned char hsyncEnable,
+ unsigned char vsyncEnable,
+ unsigned char deskewEnable,
+ unsigned char deskewSetting,
+ unsigned char continuousSyncEnable,
+ unsigned char pllFilterEnable,
+ unsigned char pllFilterValue
+ )
+{
+ dvi_ctrl_device_t *pCurrentDviCtrl;
+ pCurrentDviCtrl = g_dcftSupportedDviController;
+ if(pCurrentDviCtrl->pfnInit != NULL)
+ {
+ return pCurrentDviCtrl->pfnInit(edgeSelect, busSelect, dualEdgeClkSelect, hsyncEnable,
+ vsyncEnable, deskewEnable, deskewSetting, continuousSyncEnable,
+ pllFilterEnable, pllFilterValue);
+ }
+ return -1;//error
+}
+
+
+/*
+ * dviGetVendorID
+ * This function gets the vendor ID of the DVI controller chip.
+ *
+ * Output:
+ * Vendor ID
+ */
+unsigned short dviGetVendorID(void)
+{
+ dvi_ctrl_device_t *pCurrentDviCtrl;
+
+ //pCurrentDviCtrl = getDviCtrl();
+ pCurrentDviCtrl = g_dcftSupportedDviController;
+ if (pCurrentDviCtrl != (dvi_ctrl_device_t *)0)
+ return pCurrentDviCtrl->pfnGetVendorId();
+
+ return 0x0000;
+}
+
+
+/*
+ * dviGetDeviceID
+ * This function gets the device ID of the DVI controller chip.
+ *
+ * Output:
+ * Device ID
+ */
+unsigned short dviGetDeviceID(void)
+{
+ dvi_ctrl_device_t *pCurrentDviCtrl;
+
+// pCurrentDviCtrl = getDviCtrl();
+ pCurrentDviCtrl = g_dcftSupportedDviController;
+ if (pCurrentDviCtrl != (dvi_ctrl_device_t *)0)
+ return pCurrentDviCtrl->pfnGetDeviceId();
+
+ return 0x0000;
+}
+
+#endif
+
+
diff --git a/drivers/staging/sm750fb/ddk750_dvi.h b/drivers/staging/sm750fb/ddk750_dvi.h
new file mode 100644
index 000000000..50bcec29b
--- /dev/null
+++ b/drivers/staging/sm750fb/ddk750_dvi.h
@@ -0,0 +1,67 @@
+#ifndef DDK750_DVI_H__
+#define DDK750_DVI_H__
+
+/* dvi chip stuffs structros */
+
+typedef long (*PFN_DVICTRL_INIT)(
+ unsigned char edgeSelect,
+ unsigned char busSelect,
+ unsigned char dualEdgeClkSelect,
+ unsigned char hsyncEnable,
+ unsigned char vsyncEnable,
+ unsigned char deskewEnable,
+ unsigned char deskewSetting,
+ unsigned char continuousSyncEnable,
+ unsigned char pllFilterEnable,
+ unsigned char pllFilterValue);
+typedef void (*PFN_DVICTRL_RESETCHIP)(void);
+typedef char* (*PFN_DVICTRL_GETCHIPSTRING)(void);
+typedef unsigned short (*PFN_DVICTRL_GETVENDORID)(void);
+typedef unsigned short (*PFN_DVICTRL_GETDEVICEID)(void);
+typedef void (*PFN_DVICTRL_SETPOWER)(unsigned char powerUp);
+typedef void (*PFN_DVICTRL_HOTPLUGDETECTION)(unsigned char enableHotPlug);
+typedef unsigned char (*PFN_DVICTRL_ISCONNECTED)(void);
+typedef unsigned char (*PFN_DVICTRL_CHECKINTERRUPT)(void);
+typedef void (*PFN_DVICTRL_CLEARINTERRUPT)(void);
+
+
+
+/* Structure to hold all the function pointer to the DVI Controller. */
+typedef struct _dvi_ctrl_device_t
+{
+ PFN_DVICTRL_INIT pfnInit;
+ PFN_DVICTRL_RESETCHIP pfnResetChip;
+ PFN_DVICTRL_GETCHIPSTRING pfnGetChipString;
+ PFN_DVICTRL_GETVENDORID pfnGetVendorId;
+ PFN_DVICTRL_GETDEVICEID pfnGetDeviceId;
+ PFN_DVICTRL_SETPOWER pfnSetPower;
+ PFN_DVICTRL_HOTPLUGDETECTION pfnEnableHotPlugDetection;
+ PFN_DVICTRL_ISCONNECTED pfnIsConnected;
+ PFN_DVICTRL_CHECKINTERRUPT pfnCheckInterrupt;
+ PFN_DVICTRL_CLEARINTERRUPT pfnClearInterrupt;
+} dvi_ctrl_device_t;
+#define DVI_CTRL_SII164
+
+
+
+/* dvi functions prototype */
+int dviInit(
+ unsigned char edgeSelect,
+ unsigned char busSelect,
+ unsigned char dualEdgeClkSelect,
+ unsigned char hsyncEnable,
+ unsigned char vsyncEnable,
+ unsigned char deskewEnable,
+ unsigned char deskewSetting,
+ unsigned char continuousSyncEnable,
+ unsigned char pllFilterEnable,
+ unsigned char pllFilterValue
+);
+
+unsigned short dviGetVendorID(void);
+unsigned short dviGetDeviceID(void);
+
+
+
+#endif
+
diff --git a/drivers/staging/sm750fb/ddk750_help.c b/drivers/staging/sm750fb/ddk750_help.c
new file mode 100644
index 000000000..c68ff3b57
--- /dev/null
+++ b/drivers/staging/sm750fb/ddk750_help.c
@@ -0,0 +1,19 @@
+//#include "ddk750_reg.h"
+//#include "ddk750_chip.h"
+#include "ddk750_help.h"
+
+void __iomem * mmio750 = NULL;
+char revId750 = 0;
+unsigned short devId750 = 0;
+
+/* after driver mapped io registers, use this function first */
+void ddk750_set_mmio(void __iomem * addr,unsigned short devId,char revId)
+{
+ mmio750 = addr;
+ devId750 = devId;
+ revId750 = revId;
+ if(revId == 0xfe)
+ printk("found sm750le\n");
+}
+
+
diff --git a/drivers/staging/sm750fb/ddk750_help.h b/drivers/staging/sm750fb/ddk750_help.h
new file mode 100644
index 000000000..07c8264fa
--- /dev/null
+++ b/drivers/staging/sm750fb/ddk750_help.h
@@ -0,0 +1,29 @@
+#ifndef DDK750_HELP_H__
+#define DDK750_HELP_H__
+#include "ddk750_chip.h"
+#ifndef USE_INTERNAL_REGISTER_ACCESS
+
+#include <linux/ioport.h>
+#include <asm/io.h>
+#include <asm/uaccess.h>
+#include "sm750_help.h"
+
+
+#if 0
+/* if 718 big endian turned on,be aware that don't use this driver for general use,only for ppc big-endian */
+#warning "big endian on target cpu and enable nature big endian support of 718 capability !"
+#define PEEK32(addr) __raw_readl(mmio750 + addr)
+#define POKE32(addr,data) __raw_writel(data, mmio750 + addr)
+#else /* software control endianess */
+#define PEEK32(addr) readl(addr + mmio750)
+#define POKE32(addr,data) writel(data, addr + mmio750)
+#endif
+
+extern void __iomem * mmio750;
+extern char revId750;
+extern unsigned short devId750;
+#else
+/* implement if you want use it*/
+#endif
+
+#endif
diff --git a/drivers/staging/sm750fb/ddk750_hwi2c.c b/drivers/staging/sm750fb/ddk750_hwi2c.c
new file mode 100644
index 000000000..7826376ed
--- /dev/null
+++ b/drivers/staging/sm750fb/ddk750_hwi2c.c
@@ -0,0 +1,271 @@
+#define USE_HW_I2C
+#ifdef USE_HW_I2C
+#include "ddk750_help.h"
+#include "ddk750_reg.h"
+#include "ddk750_hwi2c.h"
+#include "ddk750_power.h"
+
+#define MAX_HWI2C_FIFO 16
+#define HWI2C_WAIT_TIMEOUT 0xF0000
+
+
+int hwI2CInit(
+ unsigned char busSpeedMode
+)
+{
+ unsigned int value;
+
+ /* Enable GPIO 30 & 31 as IIC clock & data */
+ value = PEEK32(GPIO_MUX);
+
+ value = FIELD_SET(value, GPIO_MUX, 30, I2C) |
+ FIELD_SET(0, GPIO_MUX, 31, I2C);
+ POKE32(GPIO_MUX, value);
+
+ /* Enable Hardware I2C power.
+ TODO: Check if we need to enable GPIO power?
+ */
+ enableI2C(1);
+
+ /* Enable the I2C Controller and set the bus speed mode */
+ value = PEEK32(I2C_CTRL);
+ if (busSpeedMode == 0)
+ value = FIELD_SET(value, I2C_CTRL, MODE, STANDARD);
+ else
+ value = FIELD_SET(value, I2C_CTRL, MODE, FAST);
+ value = FIELD_SET(value, I2C_CTRL, EN, ENABLE);
+ POKE32(I2C_CTRL, value);
+
+ return 0;
+}
+
+
+void hwI2CClose(void)
+{
+ unsigned int value;
+
+ /* Disable I2C controller */
+ value = PEEK32(I2C_CTRL);
+ value = FIELD_SET(value, I2C_CTRL, EN, DISABLE);
+ POKE32(I2C_CTRL, value);
+
+ /* Disable I2C Power */
+ enableI2C(0);
+
+ /* Set GPIO 30 & 31 back as GPIO pins */
+ value = PEEK32(GPIO_MUX);
+ value = FIELD_SET(value, GPIO_MUX, 30, GPIO);
+ value = FIELD_SET(value, GPIO_MUX, 31, GPIO);
+ POKE32(GPIO_MUX, value);
+}
+
+
+static long hwI2CWaitTXDone(void)
+{
+ unsigned int timeout;
+
+ /* Wait until the transfer is completed. */
+ timeout = HWI2C_WAIT_TIMEOUT;
+ while ((FIELD_GET(PEEK32(I2C_STATUS), I2C_STATUS, TX) != I2C_STATUS_TX_COMPLETED) &&
+ (timeout != 0))
+ timeout--;
+
+ if (timeout == 0)
+ return (-1);
+
+ return 0;
+}
+
+
+
+/*
+ * This function writes data to the i2c slave device registers.
+ *
+ * Parameters:
+ * deviceAddress - i2c Slave device address
+ * length - Total number of bytes to be written to the device
+ * pBuffer - The buffer that contains the data to be written to the
+ * i2c device.
+ *
+ * Return Value:
+ * Total number of bytes those are actually written.
+ */
+static unsigned int hwI2CWriteData(
+ unsigned char deviceAddress,
+ unsigned int length,
+ unsigned char *pBuffer
+)
+{
+ unsigned char count, i;
+ unsigned int totalBytes = 0;
+
+ /* Set the Device Address */
+ POKE32(I2C_SLAVE_ADDRESS, deviceAddress & ~0x01);
+
+ /* Write data.
+ * Note:
+ * Only 16 byte can be accessed per i2c start instruction.
+ */
+ do
+ {
+ /* Reset I2C by writing 0 to I2C_RESET register to clear the previous status. */
+ POKE32(I2C_RESET, 0);
+
+ /* Set the number of bytes to be written */
+ if (length < MAX_HWI2C_FIFO)
+ count = length - 1;
+ else
+ count = MAX_HWI2C_FIFO - 1;
+ POKE32(I2C_BYTE_COUNT, count);
+
+ /* Move the data to the I2C data register */
+ for (i = 0; i <= count; i++)
+ POKE32(I2C_DATA0 + i, *pBuffer++);
+
+ /* Start the I2C */
+ POKE32(I2C_CTRL, FIELD_SET(PEEK32(I2C_CTRL), I2C_CTRL, CTRL, START));
+
+ /* Wait until the transfer is completed. */
+ if (hwI2CWaitTXDone() != 0)
+ break;
+
+ /* Substract length */
+ length -= (count + 1);
+
+ /* Total byte written */
+ totalBytes += (count + 1);
+
+ } while (length > 0);
+
+ return totalBytes;
+}
+
+
+
+
+/*
+ * This function reads data from the slave device and stores them
+ * in the given buffer
+ *
+ * Parameters:
+ * deviceAddress - i2c Slave device address
+ * length - Total number of bytes to be read
+ * pBuffer - Pointer to a buffer to be filled with the data read
+ * from the slave device. It has to be the same size as the
+ * length to make sure that it can keep all the data read.
+ *
+ * Return Value:
+ * Total number of actual bytes read from the slave device
+ */
+static unsigned int hwI2CReadData(
+ unsigned char deviceAddress,
+ unsigned int length,
+ unsigned char *pBuffer
+)
+{
+ unsigned char count, i;
+ unsigned int totalBytes = 0;
+
+ /* Set the Device Address */
+ POKE32(I2C_SLAVE_ADDRESS, deviceAddress | 0x01);
+
+ /* Read data and save them to the buffer.
+ * Note:
+ * Only 16 byte can be accessed per i2c start instruction.
+ */
+ do
+ {
+ /* Reset I2C by writing 0 to I2C_RESET register to clear all the status. */
+ POKE32(I2C_RESET, 0);
+
+ /* Set the number of bytes to be read */
+ if (length <= MAX_HWI2C_FIFO)
+ count = length - 1;
+ else
+ count = MAX_HWI2C_FIFO - 1;
+ POKE32(I2C_BYTE_COUNT, count);
+
+ /* Start the I2C */
+ POKE32(I2C_CTRL, FIELD_SET(PEEK32(I2C_CTRL), I2C_CTRL, CTRL, START));
+
+ /* Wait until transaction done. */
+ if (hwI2CWaitTXDone() != 0)
+ break;
+
+ /* Save the data to the given buffer */
+ for (i = 0; i <= count; i++)
+ *pBuffer++ = PEEK32(I2C_DATA0 + i);
+
+ /* Substract length by 16 */
+ length -= (count + 1);
+
+ /* Number of bytes read. */
+ totalBytes += (count + 1);
+
+ } while (length > 0);
+
+ return totalBytes;
+}
+
+
+
+
+/*
+ * This function reads the slave device's register
+ *
+ * Parameters:
+ * deviceAddress - i2c Slave device address which register
+ * to be read from
+ * registerIndex - Slave device's register to be read
+ *
+ * Return Value:
+ * Register value
+ */
+unsigned char hwI2CReadReg(
+ unsigned char deviceAddress,
+ unsigned char registerIndex
+)
+{
+ unsigned char value = (0xFF);
+
+ if (hwI2CWriteData(deviceAddress, 1, &registerIndex) == 1)
+ hwI2CReadData(deviceAddress, 1, &value);
+
+ return value;
+}
+
+
+
+
+
+/*
+ * This function writes a value to the slave device's register
+ *
+ * Parameters:
+ * deviceAddress - i2c Slave device address which register
+ * to be written
+ * registerIndex - Slave device's register to be written
+ * data - Data to be written to the register
+ *
+ * Result:
+ * 0 - Success
+ * -1 - Fail
+ */
+int hwI2CWriteReg(
+ unsigned char deviceAddress,
+ unsigned char registerIndex,
+ unsigned char data
+)
+{
+ unsigned char value[2];
+
+ value[0] = registerIndex;
+ value[1] = data;
+ if (hwI2CWriteData(deviceAddress, 2, value) == 2)
+ return 0;
+
+ return (-1);
+}
+
+
+#endif
diff --git a/drivers/staging/sm750fb/ddk750_hwi2c.h b/drivers/staging/sm750fb/ddk750_hwi2c.h
new file mode 100644
index 000000000..ad311493c
--- /dev/null
+++ b/drivers/staging/sm750fb/ddk750_hwi2c.h
@@ -0,0 +1,10 @@
+#ifndef DDK750_HWI2C_H__
+#define DDK750_HWI2C_H__
+
+/* hwi2c functions */
+int hwI2CInit(unsigned char busSpeedMode);
+void hwI2CClose(void);
+
+unsigned char hwI2CReadReg(unsigned char deviceAddress,unsigned char registerIndex);
+int hwI2CWriteReg(unsigned char deviceAddress,unsigned char registerIndex,unsigned char data);
+#endif
diff --git a/drivers/staging/sm750fb/ddk750_mode.c b/drivers/staging/sm750fb/ddk750_mode.c
new file mode 100644
index 000000000..2e418fb6f
--- /dev/null
+++ b/drivers/staging/sm750fb/ddk750_mode.c
@@ -0,0 +1,205 @@
+
+#include "ddk750_help.h"
+#include "ddk750_reg.h"
+#include "ddk750_mode.h"
+#include "ddk750_chip.h"
+
+/*
+ SM750LE only:
+ This function takes care extra registers and bit fields required to set
+ up a mode in SM750LE
+
+ Explanation about Display Control register:
+ HW only supports 7 predefined pixel clocks, and clock select is
+ in bit 29:27 of Display Control register.
+*/
+static unsigned long displayControlAdjust_SM750LE(mode_parameter_t *pModeParam, unsigned long dispControl)
+{
+ unsigned long x, y;
+
+ x = pModeParam->horizontal_display_end;
+ y = pModeParam->vertical_display_end;
+
+ /* SM750LE has to set up the top-left and bottom-right
+ registers as well.
+ Note that normal SM750/SM718 only use those two register for
+ auto-centering mode.
+ */
+ POKE32(CRT_AUTO_CENTERING_TL,
+ FIELD_VALUE(0, CRT_AUTO_CENTERING_TL, TOP, 0)
+ | FIELD_VALUE(0, CRT_AUTO_CENTERING_TL, LEFT, 0));
+
+ POKE32(CRT_AUTO_CENTERING_BR,
+ FIELD_VALUE(0, CRT_AUTO_CENTERING_BR, BOTTOM, y-1)
+ | FIELD_VALUE(0, CRT_AUTO_CENTERING_BR, RIGHT, x-1));
+
+ /* Assume common fields in dispControl have been properly set before
+ calling this function.
+ This function only sets the extra fields in dispControl.
+ */
+
+ /* Clear bit 29:27 of display control register */
+ dispControl &= FIELD_CLEAR(CRT_DISPLAY_CTRL, CLK);
+
+ /* Set bit 29:27 of display control register for the right clock */
+ /* Note that SM750LE only need to supported 7 resoluitons. */
+ if ( x == 800 && y == 600 )
+ dispControl = FIELD_SET(dispControl, CRT_DISPLAY_CTRL, CLK, PLL41);
+ else if (x == 1024 && y == 768)
+ dispControl = FIELD_SET(dispControl, CRT_DISPLAY_CTRL, CLK, PLL65);
+ else if (x == 1152 && y == 864)
+ dispControl = FIELD_SET(dispControl, CRT_DISPLAY_CTRL, CLK, PLL80);
+ else if (x == 1280 && y == 768)
+ dispControl = FIELD_SET(dispControl, CRT_DISPLAY_CTRL, CLK, PLL80);
+ else if (x == 1280 && y == 720)
+ dispControl = FIELD_SET(dispControl, CRT_DISPLAY_CTRL, CLK, PLL74);
+ else if (x == 1280 && y == 960)
+ dispControl = FIELD_SET(dispControl, CRT_DISPLAY_CTRL, CLK, PLL108);
+ else if (x == 1280 && y == 1024)
+ dispControl = FIELD_SET(dispControl, CRT_DISPLAY_CTRL, CLK, PLL108);
+ else /* default to VGA clock */
+ dispControl = FIELD_SET(dispControl, CRT_DISPLAY_CTRL, CLK, PLL25);
+
+ /* Set bit 25:24 of display controller */
+ dispControl = FIELD_SET(dispControl, CRT_DISPLAY_CTRL, CRTSELECT, CRT);
+ dispControl = FIELD_SET(dispControl, CRT_DISPLAY_CTRL, RGBBIT, 24BIT);
+
+ /* Set bit 14 of display controller */
+ dispControl = FIELD_SET(dispControl, CRT_DISPLAY_CTRL, CLOCK_PHASE, ACTIVE_LOW);
+
+ POKE32(CRT_DISPLAY_CTRL, dispControl);
+
+ return dispControl;
+}
+
+
+
+/* only timing related registers will be programed */
+static int programModeRegisters(mode_parameter_t * pModeParam,pll_value_t * pll)
+{
+ int ret = 0;
+ int cnt = 0;
+ unsigned int ulTmpValue,ulReg;
+ if(pll->clockType == SECONDARY_PLL)
+ {
+ /* programe secondary pixel clock */
+ POKE32(CRT_PLL_CTRL,formatPllReg(pll));
+ POKE32(CRT_HORIZONTAL_TOTAL,
+ FIELD_VALUE(0, CRT_HORIZONTAL_TOTAL, TOTAL, pModeParam->horizontal_total - 1)
+ | FIELD_VALUE(0, CRT_HORIZONTAL_TOTAL, DISPLAY_END, pModeParam->horizontal_display_end - 1));
+
+ POKE32(CRT_HORIZONTAL_SYNC,
+ FIELD_VALUE(0, CRT_HORIZONTAL_SYNC, WIDTH, pModeParam->horizontal_sync_width)
+ | FIELD_VALUE(0, CRT_HORIZONTAL_SYNC, START, pModeParam->horizontal_sync_start - 1));
+
+ POKE32(CRT_VERTICAL_TOTAL,
+ FIELD_VALUE(0, CRT_VERTICAL_TOTAL, TOTAL, pModeParam->vertical_total - 1)
+ | FIELD_VALUE(0, CRT_VERTICAL_TOTAL, DISPLAY_END, pModeParam->vertical_display_end - 1));
+
+ POKE32(CRT_VERTICAL_SYNC,
+ FIELD_VALUE(0, CRT_VERTICAL_SYNC, HEIGHT, pModeParam->vertical_sync_height)
+ | FIELD_VALUE(0, CRT_VERTICAL_SYNC, START, pModeParam->vertical_sync_start - 1));
+
+
+ ulTmpValue = FIELD_VALUE(0,CRT_DISPLAY_CTRL,VSYNC_PHASE,pModeParam->vertical_sync_polarity)|
+ FIELD_VALUE(0,CRT_DISPLAY_CTRL,HSYNC_PHASE,pModeParam->horizontal_sync_polarity)|
+ FIELD_SET(0,CRT_DISPLAY_CTRL,TIMING,ENABLE)|
+ FIELD_SET(0,CRT_DISPLAY_CTRL,PLANE,ENABLE);
+
+
+ if(getChipType() == SM750LE){
+ displayControlAdjust_SM750LE(pModeParam,ulTmpValue);
+ }else{
+ ulReg = PEEK32(CRT_DISPLAY_CTRL)
+ & FIELD_CLEAR(CRT_DISPLAY_CTRL,VSYNC_PHASE)
+ & FIELD_CLEAR(CRT_DISPLAY_CTRL,HSYNC_PHASE)
+ & FIELD_CLEAR(CRT_DISPLAY_CTRL,TIMING)
+ & FIELD_CLEAR(CRT_DISPLAY_CTRL,PLANE);
+
+ POKE32(CRT_DISPLAY_CTRL,ulTmpValue|ulReg);
+ }
+
+ }
+ else if(pll->clockType == PRIMARY_PLL)
+ {
+ unsigned int ulReservedBits;
+ POKE32(PANEL_PLL_CTRL,formatPllReg(pll));
+
+ POKE32(PANEL_HORIZONTAL_TOTAL,
+ FIELD_VALUE(0, PANEL_HORIZONTAL_TOTAL, TOTAL, pModeParam->horizontal_total - 1)
+ | FIELD_VALUE(0, PANEL_HORIZONTAL_TOTAL, DISPLAY_END, pModeParam->horizontal_display_end - 1));
+
+ POKE32(PANEL_HORIZONTAL_SYNC,
+ FIELD_VALUE(0, PANEL_HORIZONTAL_SYNC, WIDTH, pModeParam->horizontal_sync_width)
+ | FIELD_VALUE(0, PANEL_HORIZONTAL_SYNC, START, pModeParam->horizontal_sync_start - 1));
+
+ POKE32(PANEL_VERTICAL_TOTAL,
+ FIELD_VALUE(0, PANEL_VERTICAL_TOTAL, TOTAL, pModeParam->vertical_total - 1)
+ | FIELD_VALUE(0, PANEL_VERTICAL_TOTAL, DISPLAY_END, pModeParam->vertical_display_end - 1));
+
+ POKE32(PANEL_VERTICAL_SYNC,
+ FIELD_VALUE(0, PANEL_VERTICAL_SYNC, HEIGHT, pModeParam->vertical_sync_height)
+ | FIELD_VALUE(0, PANEL_VERTICAL_SYNC, START, pModeParam->vertical_sync_start - 1));
+
+ ulTmpValue = FIELD_VALUE(0,PANEL_DISPLAY_CTRL,VSYNC_PHASE,pModeParam->vertical_sync_polarity)|
+ FIELD_VALUE(0,PANEL_DISPLAY_CTRL,HSYNC_PHASE,pModeParam->horizontal_sync_polarity)|
+ FIELD_VALUE(0,PANEL_DISPLAY_CTRL,CLOCK_PHASE,pModeParam->clock_phase_polarity)|
+ FIELD_SET(0,PANEL_DISPLAY_CTRL,TIMING,ENABLE)|
+ FIELD_SET(0,PANEL_DISPLAY_CTRL,PLANE,ENABLE);
+
+ ulReservedBits = FIELD_SET(0, PANEL_DISPLAY_CTRL, RESERVED_1_MASK, ENABLE) |
+ FIELD_SET(0, PANEL_DISPLAY_CTRL, RESERVED_2_MASK, ENABLE) |
+ FIELD_SET(0, PANEL_DISPLAY_CTRL, RESERVED_3_MASK, ENABLE)|
+ FIELD_SET(0,PANEL_DISPLAY_CTRL,VSYNC,ACTIVE_LOW);
+
+ ulReg = (PEEK32(PANEL_DISPLAY_CTRL) & ~ulReservedBits)
+ & FIELD_CLEAR(PANEL_DISPLAY_CTRL, CLOCK_PHASE)
+ & FIELD_CLEAR(PANEL_DISPLAY_CTRL, VSYNC_PHASE)
+ & FIELD_CLEAR(PANEL_DISPLAY_CTRL, HSYNC_PHASE)
+ & FIELD_CLEAR(PANEL_DISPLAY_CTRL, TIMING)
+ & FIELD_CLEAR(PANEL_DISPLAY_CTRL, PLANE);
+
+
+ /* May a hardware bug or just my test chip (not confirmed).
+ * PANEL_DISPLAY_CTRL register seems requiring few writes
+ * before a value can be succesfully written in.
+ * Added some masks to mask out the reserved bits.
+ * Note: This problem happens by design. The hardware will wait for the
+ * next vertical sync to turn on/off the plane.
+ */
+
+ POKE32(PANEL_DISPLAY_CTRL,ulTmpValue|ulReg);
+#if 1
+ while((PEEK32(PANEL_DISPLAY_CTRL) & ~ulReservedBits) != (ulTmpValue|ulReg))
+ {
+ cnt++;
+ if(cnt > 1000)
+ break;
+ POKE32(PANEL_DISPLAY_CTRL,ulTmpValue|ulReg);
+ }
+#endif
+ }
+ else{
+ ret = -1;
+ }
+ return ret;
+}
+
+int ddk750_setModeTiming(mode_parameter_t * parm,clock_type_t clock)
+{
+ pll_value_t pll;
+ unsigned int uiActualPixelClk;
+ pll.inputFreq = DEFAULT_INPUT_CLOCK;
+ pll.clockType = clock;
+
+ uiActualPixelClk = calcPllValue(parm->pixel_clock,&pll);
+ if(getChipType() == SM750LE){
+ /* set graphic mode via IO method */
+ outb_p(0x88,0x3d4);
+ outb_p(0x06,0x3d5);
+ }
+ programModeRegisters(parm,&pll);
+ return 0;
+}
+
+
diff --git a/drivers/staging/sm750fb/ddk750_mode.h b/drivers/staging/sm750fb/ddk750_mode.h
new file mode 100644
index 000000000..6f8df96a8
--- /dev/null
+++ b/drivers/staging/sm750fb/ddk750_mode.h
@@ -0,0 +1,43 @@
+#ifndef DDK750_MODE_H__
+#define DDK750_MODE_H__
+
+#include "ddk750_chip.h"
+
+typedef enum _spolarity_t
+{
+ POS = 0, /* positive */
+ NEG, /* negative */
+}
+spolarity_t;
+
+
+typedef struct _mode_parameter_t
+{
+ /* Horizontal timing. */
+ unsigned long horizontal_total;
+ unsigned long horizontal_display_end;
+ unsigned long horizontal_sync_start;
+ unsigned long horizontal_sync_width;
+ spolarity_t horizontal_sync_polarity;
+
+ /* Vertical timing. */
+ unsigned long vertical_total;
+ unsigned long vertical_display_end;
+ unsigned long vertical_sync_start;
+ unsigned long vertical_sync_height;
+ spolarity_t vertical_sync_polarity;
+
+ /* Refresh timing. */
+ unsigned long pixel_clock;
+ unsigned long horizontal_frequency;
+ unsigned long vertical_frequency;
+
+ /* Clock Phase. This clock phase only applies to Panel. */
+ spolarity_t clock_phase_polarity;
+}
+mode_parameter_t;
+
+int ddk750_setModeTiming(mode_parameter_t *,clock_type_t);
+
+
+#endif
diff --git a/drivers/staging/sm750fb/ddk750_power.c b/drivers/staging/sm750fb/ddk750_power.c
new file mode 100644
index 000000000..cbb97676b
--- /dev/null
+++ b/drivers/staging/sm750fb/ddk750_power.c
@@ -0,0 +1,239 @@
+#include "ddk750_help.h"
+#include "ddk750_reg.h"
+#include "ddk750_power.h"
+
+void ddk750_setDPMS(DPMS_t state)
+{
+ unsigned int value;
+ if(getChipType() == SM750LE){
+ value = PEEK32(CRT_DISPLAY_CTRL);
+ POKE32(CRT_DISPLAY_CTRL,FIELD_VALUE(value,CRT_DISPLAY_CTRL,DPMS,state));
+ }else{
+ value = PEEK32(SYSTEM_CTRL);
+ value= FIELD_VALUE(value,SYSTEM_CTRL,DPMS,state);
+ POKE32(SYSTEM_CTRL, value);
+ }
+}
+
+unsigned int getPowerMode(void)
+{
+ if(getChipType() == SM750LE)
+ return 0;
+ return (FIELD_GET(PEEK32(POWER_MODE_CTRL), POWER_MODE_CTRL, MODE));
+}
+
+
+/*
+ * SM50x can operate in one of three modes: 0, 1 or Sleep.
+ * On hardware reset, power mode 0 is default.
+ */
+void setPowerMode(unsigned int powerMode)
+{
+ unsigned int control_value = 0;
+
+ control_value = PEEK32(POWER_MODE_CTRL);
+
+ if(getChipType() == SM750LE)
+ return;
+
+ switch (powerMode)
+ {
+ case POWER_MODE_CTRL_MODE_MODE0:
+ control_value = FIELD_SET(control_value, POWER_MODE_CTRL, MODE, MODE0);
+ break;
+
+ case POWER_MODE_CTRL_MODE_MODE1:
+ control_value = FIELD_SET(control_value, POWER_MODE_CTRL, MODE, MODE1);
+ break;
+
+ case POWER_MODE_CTRL_MODE_SLEEP:
+ control_value = FIELD_SET(control_value, POWER_MODE_CTRL, MODE, SLEEP);
+ break;
+
+ default:
+ break;
+ }
+
+ /* Set up other fields in Power Control Register */
+ if (powerMode == POWER_MODE_CTRL_MODE_SLEEP)
+ {
+ control_value =
+#ifdef VALIDATION_CHIP
+ FIELD_SET( control_value, POWER_MODE_CTRL, 336CLK, OFF) |
+#endif
+ FIELD_SET( control_value, POWER_MODE_CTRL, OSC_INPUT, OFF);
+ }
+ else
+ {
+ control_value =
+#ifdef VALIDATION_CHIP
+ FIELD_SET( control_value, POWER_MODE_CTRL, 336CLK, ON) |
+#endif
+ FIELD_SET( control_value, POWER_MODE_CTRL, OSC_INPUT, ON);
+ }
+
+ /* Program new power mode. */
+ POKE32(POWER_MODE_CTRL, control_value);
+}
+
+void setCurrentGate(unsigned int gate)
+{
+ unsigned int gate_reg;
+ unsigned int mode;
+
+ /* Get current power mode. */
+ mode = getPowerMode();
+
+ switch (mode)
+ {
+ case POWER_MODE_CTRL_MODE_MODE0:
+ gate_reg = MODE0_GATE;
+ break;
+
+ case POWER_MODE_CTRL_MODE_MODE1:
+ gate_reg = MODE1_GATE;
+ break;
+
+ default:
+ gate_reg = MODE0_GATE;
+ break;
+ }
+ POKE32(gate_reg, gate);
+}
+
+
+
+/*
+ * This function enable/disable the 2D engine.
+ */
+void enable2DEngine(unsigned int enable)
+{
+ uint32_t gate;
+
+ gate = PEEK32(CURRENT_GATE);
+ if (enable)
+ {
+ gate = FIELD_SET(gate, CURRENT_GATE, DE, ON);
+ gate = FIELD_SET(gate, CURRENT_GATE, CSC, ON);
+ }
+ else
+ {
+ gate = FIELD_SET(gate, CURRENT_GATE, DE, OFF);
+ gate = FIELD_SET(gate, CURRENT_GATE, CSC, OFF);
+ }
+
+ setCurrentGate(gate);
+}
+
+
+/*
+ * This function enable/disable the ZV Port.
+ */
+void enableZVPort(unsigned int enable)
+{
+ uint32_t gate;
+
+ /* Enable ZV Port Gate */
+ gate = PEEK32(CURRENT_GATE);
+ if (enable)
+ {
+ gate = FIELD_SET(gate, CURRENT_GATE, ZVPORT, ON);
+#if 1
+ /* Using Software I2C */
+ gate = FIELD_SET(gate, CURRENT_GATE, GPIO, ON);
+#else
+ /* Using Hardware I2C */
+ gate = FIELD_SET(gate, CURRENT_GATE, I2C, ON);
+#endif
+ }
+ else
+ {
+ /* Disable ZV Port Gate. There is no way to know whether the GPIO pins are being used
+ or not. Therefore, do not disable the GPIO gate. */
+ gate = FIELD_SET(gate, CURRENT_GATE, ZVPORT, OFF);
+ }
+
+ setCurrentGate(gate);
+}
+
+
+void enableSSP(unsigned int enable)
+{
+ uint32_t gate;
+
+ /* Enable SSP Gate */
+ gate = PEEK32(CURRENT_GATE);
+ if (enable)
+ gate = FIELD_SET(gate, CURRENT_GATE, SSP, ON);
+ else
+ gate = FIELD_SET(gate, CURRENT_GATE, SSP, OFF);
+
+ setCurrentGate(gate);
+}
+
+void enableDMA(unsigned int enable)
+{
+ uint32_t gate;
+
+ /* Enable DMA Gate */
+ gate = PEEK32(CURRENT_GATE);
+ if (enable)
+ gate = FIELD_SET(gate, CURRENT_GATE, DMA, ON);
+ else
+ gate = FIELD_SET(gate, CURRENT_GATE, DMA, OFF);
+
+ setCurrentGate(gate);
+}
+
+/*
+ * This function enable/disable the GPIO Engine
+ */
+void enableGPIO(unsigned int enable)
+{
+ uint32_t gate;
+
+ /* Enable GPIO Gate */
+ gate = PEEK32(CURRENT_GATE);
+ if (enable)
+ gate = FIELD_SET(gate, CURRENT_GATE, GPIO, ON);
+ else
+ gate = FIELD_SET(gate, CURRENT_GATE, GPIO, OFF);
+
+ setCurrentGate(gate);
+}
+
+/*
+ * This function enable/disable the PWM Engine
+ */
+void enablePWM(unsigned int enable)
+{
+ uint32_t gate;
+
+ /* Enable PWM Gate */
+ gate = PEEK32(CURRENT_GATE);
+ if (enable)
+ gate = FIELD_SET(gate, CURRENT_GATE, PWM, ON);
+ else
+ gate = FIELD_SET(gate, CURRENT_GATE, PWM, OFF);
+
+ setCurrentGate(gate);
+}
+
+/*
+ * This function enable/disable the I2C Engine
+ */
+void enableI2C(unsigned int enable)
+{
+ uint32_t gate;
+
+ /* Enable I2C Gate */
+ gate = PEEK32(CURRENT_GATE);
+ if (enable)
+ gate = FIELD_SET(gate, CURRENT_GATE, I2C, ON);
+ else
+ gate = FIELD_SET(gate, CURRENT_GATE, I2C, OFF);
+
+ setCurrentGate(gate);
+}
+
+
diff --git a/drivers/staging/sm750fb/ddk750_power.h b/drivers/staging/sm750fb/ddk750_power.h
new file mode 100644
index 000000000..71dc7f980
--- /dev/null
+++ b/drivers/staging/sm750fb/ddk750_power.h
@@ -0,0 +1,71 @@
+#ifndef DDK750_POWER_H__
+#define DDK750_POWER_H__
+
+typedef enum _DPMS_t
+{
+ crtDPMS_ON = 0x0,
+ crtDPMS_STANDBY = 0x1,
+ crtDPMS_SUSPEND = 0x2,
+ crtDPMS_OFF = 0x3,
+}
+DPMS_t;
+
+#define setDAC(off) \
+ { \
+ POKE32(MISC_CTRL,FIELD_VALUE(PEEK32(MISC_CTRL), \
+ MISC_CTRL, \
+ DAC_POWER, \
+ off)); \
+ }
+
+void ddk750_setDPMS(DPMS_t);
+
+unsigned int getPowerMode(void);
+
+/*
+ * This function sets the current power mode
+ */
+void setPowerMode(unsigned int powerMode);
+
+/*
+ * This function sets current gate
+ */
+void setCurrentGate(unsigned int gate);
+
+/*
+ * This function enable/disable the 2D engine.
+ */
+void enable2DEngine(unsigned int enable);
+
+/*
+ * This function enable/disable the ZV Port
+ */
+void enableZVPort(unsigned int enable);
+
+/*
+ * This function enable/disable the DMA Engine
+ */
+void enableDMA(unsigned int enable);
+
+/*
+ * This function enable/disable the GPIO Engine
+ */
+void enableGPIO(unsigned int enable);
+
+/*
+ * This function enable/disable the PWM Engine
+ */
+void enablePWM(unsigned int enable);
+
+/*
+ * This function enable/disable the I2C Engine
+ */
+void enableI2C(unsigned int enable);
+
+/*
+ * This function enable/disable the SSP.
+ */
+void enableSSP(unsigned int enable);
+
+
+#endif
diff --git a/drivers/staging/sm750fb/ddk750_reg.h b/drivers/staging/sm750fb/ddk750_reg.h
new file mode 100644
index 000000000..2016f97d2
--- /dev/null
+++ b/drivers/staging/sm750fb/ddk750_reg.h
@@ -0,0 +1,2616 @@
+#ifndef DDK750_REG_H__
+#define DDK750_REG_H__
+
+/* New register for SM750LE */
+#define DE_STATE1 0x100054
+#define DE_STATE1_DE_ABORT 0:0
+#define DE_STATE1_DE_ABORT_OFF 0
+#define DE_STATE1_DE_ABORT_ON 1
+
+#define DE_STATE2 0x100058
+#define DE_STATE2_DE_FIFO 3:3
+#define DE_STATE2_DE_FIFO_NOTEMPTY 0
+#define DE_STATE2_DE_FIFO_EMPTY 1
+#define DE_STATE2_DE_STATUS 2:2
+#define DE_STATE2_DE_STATUS_IDLE 0
+#define DE_STATE2_DE_STATUS_BUSY 1
+#define DE_STATE2_DE_MEM_FIFO 1:1
+#define DE_STATE2_DE_MEM_FIFO_NOTEMPTY 0
+#define DE_STATE2_DE_MEM_FIFO_EMPTY 1
+#define DE_STATE2_DE_RESERVED 0:0
+
+
+
+#define SYSTEM_CTRL 0x000000
+#define SYSTEM_CTRL_DPMS 31:30
+#define SYSTEM_CTRL_DPMS_VPHP 0
+#define SYSTEM_CTRL_DPMS_VPHN 1
+#define SYSTEM_CTRL_DPMS_VNHP 2
+#define SYSTEM_CTRL_DPMS_VNHN 3
+#define SYSTEM_CTRL_PCI_BURST 29:29
+#define SYSTEM_CTRL_PCI_BURST_OFF 0
+#define SYSTEM_CTRL_PCI_BURST_ON 1
+#define SYSTEM_CTRL_PCI_MASTER 25:25
+#define SYSTEM_CTRL_PCI_MASTER_OFF 0
+#define SYSTEM_CTRL_PCI_MASTER_ON 1
+#define SYSTEM_CTRL_LATENCY_TIMER 24:24
+#define SYSTEM_CTRL_LATENCY_TIMER_ON 0
+#define SYSTEM_CTRL_LATENCY_TIMER_OFF 1
+#define SYSTEM_CTRL_DE_FIFO 23:23
+#define SYSTEM_CTRL_DE_FIFO_NOTEMPTY 0
+#define SYSTEM_CTRL_DE_FIFO_EMPTY 1
+#define SYSTEM_CTRL_DE_STATUS 22:22
+#define SYSTEM_CTRL_DE_STATUS_IDLE 0
+#define SYSTEM_CTRL_DE_STATUS_BUSY 1
+#define SYSTEM_CTRL_DE_MEM_FIFO 21:21
+#define SYSTEM_CTRL_DE_MEM_FIFO_NOTEMPTY 0
+#define SYSTEM_CTRL_DE_MEM_FIFO_EMPTY 1
+#define SYSTEM_CTRL_CSC_STATUS 20:20
+#define SYSTEM_CTRL_CSC_STATUS_IDLE 0
+#define SYSTEM_CTRL_CSC_STATUS_BUSY 1
+#define SYSTEM_CTRL_CRT_VSYNC 19:19
+#define SYSTEM_CTRL_CRT_VSYNC_INACTIVE 0
+#define SYSTEM_CTRL_CRT_VSYNC_ACTIVE 1
+#define SYSTEM_CTRL_PANEL_VSYNC 18:18
+#define SYSTEM_CTRL_PANEL_VSYNC_INACTIVE 0
+#define SYSTEM_CTRL_PANEL_VSYNC_ACTIVE 1
+#define SYSTEM_CTRL_CURRENT_BUFFER 17:17
+#define SYSTEM_CTRL_CURRENT_BUFFER_NORMAL 0
+#define SYSTEM_CTRL_CURRENT_BUFFER_FLIP_PENDING 1
+#define SYSTEM_CTRL_DMA_STATUS 16:16
+#define SYSTEM_CTRL_DMA_STATUS_IDLE 0
+#define SYSTEM_CTRL_DMA_STATUS_BUSY 1
+#define SYSTEM_CTRL_PCI_BURST_READ 15:15
+#define SYSTEM_CTRL_PCI_BURST_READ_OFF 0
+#define SYSTEM_CTRL_PCI_BURST_READ_ON 1
+#define SYSTEM_CTRL_DE_ABORT 13:13
+#define SYSTEM_CTRL_DE_ABORT_OFF 0
+#define SYSTEM_CTRL_DE_ABORT_ON 1
+#define SYSTEM_CTRL_PCI_SUBSYS_ID_LOCK 11:11
+#define SYSTEM_CTRL_PCI_SUBSYS_ID_LOCK_OFF 0
+#define SYSTEM_CTRL_PCI_SUBSYS_ID_LOCK_ON 1
+#define SYSTEM_CTRL_PCI_RETRY 7:7
+#define SYSTEM_CTRL_PCI_RETRY_ON 0
+#define SYSTEM_CTRL_PCI_RETRY_OFF 1
+#define SYSTEM_CTRL_PCI_SLAVE_BURST_READ_SIZE 5:4
+#define SYSTEM_CTRL_PCI_SLAVE_BURST_READ_SIZE_1 0
+#define SYSTEM_CTRL_PCI_SLAVE_BURST_READ_SIZE_2 1
+#define SYSTEM_CTRL_PCI_SLAVE_BURST_READ_SIZE_4 2
+#define SYSTEM_CTRL_PCI_SLAVE_BURST_READ_SIZE_8 3
+#define SYSTEM_CTRL_CRT_TRISTATE 3:3
+#define SYSTEM_CTRL_CRT_TRISTATE_OFF 0
+#define SYSTEM_CTRL_CRT_TRISTATE_ON 1
+#define SYSTEM_CTRL_PCIMEM_TRISTATE 2:2
+#define SYSTEM_CTRL_PCIMEM_TRISTATE_OFF 0
+#define SYSTEM_CTRL_PCIMEM_TRISTATE_ON 1
+#define SYSTEM_CTRL_LOCALMEM_TRISTATE 1:1
+#define SYSTEM_CTRL_LOCALMEM_TRISTATE_OFF 0
+#define SYSTEM_CTRL_LOCALMEM_TRISTATE_ON 1
+#define SYSTEM_CTRL_PANEL_TRISTATE 0:0
+#define SYSTEM_CTRL_PANEL_TRISTATE_OFF 0
+#define SYSTEM_CTRL_PANEL_TRISTATE_ON 1
+
+#define MISC_CTRL 0x000004
+#define MISC_CTRL_DRAM_RERESH_COUNT 27:27
+#define MISC_CTRL_DRAM_RERESH_COUNT_1ROW 0
+#define MISC_CTRL_DRAM_RERESH_COUNT_3ROW 1
+#define MISC_CTRL_DRAM_REFRESH_TIME 26:25
+#define MISC_CTRL_DRAM_REFRESH_TIME_8 0
+#define MISC_CTRL_DRAM_REFRESH_TIME_16 1
+#define MISC_CTRL_DRAM_REFRESH_TIME_32 2
+#define MISC_CTRL_DRAM_REFRESH_TIME_64 3
+#define MISC_CTRL_INT_OUTPUT 24:24
+#define MISC_CTRL_INT_OUTPUT_NORMAL 0
+#define MISC_CTRL_INT_OUTPUT_INVERT 1
+#define MISC_CTRL_PLL_CLK_COUNT 23:23
+#define MISC_CTRL_PLL_CLK_COUNT_OFF 0
+#define MISC_CTRL_PLL_CLK_COUNT_ON 1
+#define MISC_CTRL_DAC_POWER 20:20
+#define MISC_CTRL_DAC_POWER_ON 0
+#define MISC_CTRL_DAC_POWER_OFF 1
+#define MISC_CTRL_CLK_SELECT 16:16
+#define MISC_CTRL_CLK_SELECT_OSC 0
+#define MISC_CTRL_CLK_SELECT_TESTCLK 1
+#define MISC_CTRL_DRAM_COLUMN_SIZE 15:14
+#define MISC_CTRL_DRAM_COLUMN_SIZE_256 0
+#define MISC_CTRL_DRAM_COLUMN_SIZE_512 1
+#define MISC_CTRL_DRAM_COLUMN_SIZE_1024 2
+#define MISC_CTRL_LOCALMEM_SIZE 13:12
+#define MISC_CTRL_LOCALMEM_SIZE_8M 3
+#define MISC_CTRL_LOCALMEM_SIZE_16M 0
+#define MISC_CTRL_LOCALMEM_SIZE_32M 1
+#define MISC_CTRL_LOCALMEM_SIZE_64M 2
+#define MISC_CTRL_DRAM_TWTR 11:11
+#define MISC_CTRL_DRAM_TWTR_2CLK 0
+#define MISC_CTRL_DRAM_TWTR_1CLK 1
+#define MISC_CTRL_DRAM_TWR 10:10
+#define MISC_CTRL_DRAM_TWR_3CLK 0
+#define MISC_CTRL_DRAM_TWR_2CLK 1
+#define MISC_CTRL_DRAM_TRP 9:9
+#define MISC_CTRL_DRAM_TRP_3CLK 0
+#define MISC_CTRL_DRAM_TRP_4CLK 1
+#define MISC_CTRL_DRAM_TRFC 8:8
+#define MISC_CTRL_DRAM_TRFC_12CLK 0
+#define MISC_CTRL_DRAM_TRFC_14CLK 1
+#define MISC_CTRL_DRAM_TRAS 7:7
+#define MISC_CTRL_DRAM_TRAS_7CLK 0
+#define MISC_CTRL_DRAM_TRAS_8CLK 1
+#define MISC_CTRL_LOCALMEM_RESET 6:6
+#define MISC_CTRL_LOCALMEM_RESET_RESET 0
+#define MISC_CTRL_LOCALMEM_RESET_NORMAL 1
+#define MISC_CTRL_LOCALMEM_STATE 5:5
+#define MISC_CTRL_LOCALMEM_STATE_ACTIVE 0
+#define MISC_CTRL_LOCALMEM_STATE_INACTIVE 1
+#define MISC_CTRL_CPU_CAS_LATENCY 4:4
+#define MISC_CTRL_CPU_CAS_LATENCY_2CLK 0
+#define MISC_CTRL_CPU_CAS_LATENCY_3CLK 1
+#define MISC_CTRL_DLL 3:3
+#define MISC_CTRL_DLL_ON 0
+#define MISC_CTRL_DLL_OFF 1
+#define MISC_CTRL_DRAM_OUTPUT 2:2
+#define MISC_CTRL_DRAM_OUTPUT_LOW 0
+#define MISC_CTRL_DRAM_OUTPUT_HIGH 1
+#define MISC_CTRL_LOCALMEM_BUS_SIZE 1:1
+#define MISC_CTRL_LOCALMEM_BUS_SIZE_32 0
+#define MISC_CTRL_LOCALMEM_BUS_SIZE_64 1
+#define MISC_CTRL_EMBEDDED_LOCALMEM 0:0
+#define MISC_CTRL_EMBEDDED_LOCALMEM_ON 0
+#define MISC_CTRL_EMBEDDED_LOCALMEM_OFF 1
+
+#define GPIO_MUX 0x000008
+#define GPIO_MUX_31 31:31
+#define GPIO_MUX_31_GPIO 0
+#define GPIO_MUX_31_I2C 1
+#define GPIO_MUX_30 30:30
+#define GPIO_MUX_30_GPIO 0
+#define GPIO_MUX_30_I2C 1
+#define GPIO_MUX_29 29:29
+#define GPIO_MUX_29_GPIO 0
+#define GPIO_MUX_29_SSP1 1
+#define GPIO_MUX_28 28:28
+#define GPIO_MUX_28_GPIO 0
+#define GPIO_MUX_28_SSP1 1
+#define GPIO_MUX_27 27:27
+#define GPIO_MUX_27_GPIO 0
+#define GPIO_MUX_27_SSP1 1
+#define GPIO_MUX_26 26:26
+#define GPIO_MUX_26_GPIO 0
+#define GPIO_MUX_26_SSP1 1
+#define GPIO_MUX_25 25:25
+#define GPIO_MUX_25_GPIO 0
+#define GPIO_MUX_25_SSP1 1
+#define GPIO_MUX_24 24:24
+#define GPIO_MUX_24_GPIO 0
+#define GPIO_MUX_24_SSP0 1
+#define GPIO_MUX_23 23:23
+#define GPIO_MUX_23_GPIO 0
+#define GPIO_MUX_23_SSP0 1
+#define GPIO_MUX_22 22:22
+#define GPIO_MUX_22_GPIO 0
+#define GPIO_MUX_22_SSP0 1
+#define GPIO_MUX_21 21:21
+#define GPIO_MUX_21_GPIO 0
+#define GPIO_MUX_21_SSP0 1
+#define GPIO_MUX_20 20:20
+#define GPIO_MUX_20_GPIO 0
+#define GPIO_MUX_20_SSP0 1
+#define GPIO_MUX_19 19:19
+#define GPIO_MUX_19_GPIO 0
+#define GPIO_MUX_19_PWM 1
+#define GPIO_MUX_18 18:18
+#define GPIO_MUX_18_GPIO 0
+#define GPIO_MUX_18_PWM 1
+#define GPIO_MUX_17 17:17
+#define GPIO_MUX_17_GPIO 0
+#define GPIO_MUX_17_PWM 1
+#define GPIO_MUX_16 16:16
+#define GPIO_MUX_16_GPIO_ZVPORT 0
+#define GPIO_MUX_16_TEST_DATA 1
+#define GPIO_MUX_15 15:15
+#define GPIO_MUX_15_GPIO_ZVPORT 0
+#define GPIO_MUX_15_TEST_DATA 1
+#define GPIO_MUX_14 14:14
+#define GPIO_MUX_14_GPIO_ZVPORT 0
+#define GPIO_MUX_14_TEST_DATA 1
+#define GPIO_MUX_13 13:13
+#define GPIO_MUX_13_GPIO_ZVPORT 0
+#define GPIO_MUX_13_TEST_DATA 1
+#define GPIO_MUX_12 12:12
+#define GPIO_MUX_12_GPIO_ZVPORT 0
+#define GPIO_MUX_12_TEST_DATA 1
+#define GPIO_MUX_11 11:11
+#define GPIO_MUX_11_GPIO_ZVPORT 0
+#define GPIO_MUX_11_TEST_DATA 1
+#define GPIO_MUX_10 10:10
+#define GPIO_MUX_10_GPIO_ZVPORT 0
+#define GPIO_MUX_10_TEST_DATA 1
+#define GPIO_MUX_9 9:9
+#define GPIO_MUX_9_GPIO_ZVPORT 0
+#define GPIO_MUX_9_TEST_DATA 1
+#define GPIO_MUX_8 8:8
+#define GPIO_MUX_8_GPIO_ZVPORT 0
+#define GPIO_MUX_8_TEST_DATA 1
+#define GPIO_MUX_7 7:7
+#define GPIO_MUX_7_GPIO_ZVPORT 0
+#define GPIO_MUX_7_TEST_DATA 1
+#define GPIO_MUX_6 6:6
+#define GPIO_MUX_6_GPIO_ZVPORT 0
+#define GPIO_MUX_6_TEST_DATA 1
+#define GPIO_MUX_5 5:5
+#define GPIO_MUX_5_GPIO_ZVPORT 0
+#define GPIO_MUX_5_TEST_DATA 1
+#define GPIO_MUX_4 4:4
+#define GPIO_MUX_4_GPIO_ZVPORT 0
+#define GPIO_MUX_4_TEST_DATA 1
+#define GPIO_MUX_3 3:3
+#define GPIO_MUX_3_GPIO_ZVPORT 0
+#define GPIO_MUX_3_TEST_DATA 1
+#define GPIO_MUX_2 2:2
+#define GPIO_MUX_2_GPIO_ZVPORT 0
+#define GPIO_MUX_2_TEST_DATA 1
+#define GPIO_MUX_1 1:1
+#define GPIO_MUX_1_GPIO_ZVPORT 0
+#define GPIO_MUX_1_TEST_DATA 1
+#define GPIO_MUX_0 0:0
+#define GPIO_MUX_0_GPIO_ZVPORT 0
+#define GPIO_MUX_0_TEST_DATA 1
+
+#define LOCALMEM_ARBITRATION 0x00000C
+#define LOCALMEM_ARBITRATION_ROTATE 28:28
+#define LOCALMEM_ARBITRATION_ROTATE_OFF 0
+#define LOCALMEM_ARBITRATION_ROTATE_ON 1
+#define LOCALMEM_ARBITRATION_VGA 26:24
+#define LOCALMEM_ARBITRATION_VGA_OFF 0
+#define LOCALMEM_ARBITRATION_VGA_PRIORITY_1 1
+#define LOCALMEM_ARBITRATION_VGA_PRIORITY_2 2
+#define LOCALMEM_ARBITRATION_VGA_PRIORITY_3 3
+#define LOCALMEM_ARBITRATION_VGA_PRIORITY_4 4
+#define LOCALMEM_ARBITRATION_VGA_PRIORITY_5 5
+#define LOCALMEM_ARBITRATION_VGA_PRIORITY_6 6
+#define LOCALMEM_ARBITRATION_VGA_PRIORITY_7 7
+#define LOCALMEM_ARBITRATION_DMA 22:20
+#define LOCALMEM_ARBITRATION_DMA_OFF 0
+#define LOCALMEM_ARBITRATION_DMA_PRIORITY_1 1
+#define LOCALMEM_ARBITRATION_DMA_PRIORITY_2 2
+#define LOCALMEM_ARBITRATION_DMA_PRIORITY_3 3
+#define LOCALMEM_ARBITRATION_DMA_PRIORITY_4 4
+#define LOCALMEM_ARBITRATION_DMA_PRIORITY_5 5
+#define LOCALMEM_ARBITRATION_DMA_PRIORITY_6 6
+#define LOCALMEM_ARBITRATION_DMA_PRIORITY_7 7
+#define LOCALMEM_ARBITRATION_ZVPORT1 18:16
+#define LOCALMEM_ARBITRATION_ZVPORT1_OFF 0
+#define LOCALMEM_ARBITRATION_ZVPORT1_PRIORITY_1 1
+#define LOCALMEM_ARBITRATION_ZVPORT1_PRIORITY_2 2
+#define LOCALMEM_ARBITRATION_ZVPORT1_PRIORITY_3 3
+#define LOCALMEM_ARBITRATION_ZVPORT1_PRIORITY_4 4
+#define LOCALMEM_ARBITRATION_ZVPORT1_PRIORITY_5 5
+#define LOCALMEM_ARBITRATION_ZVPORT1_PRIORITY_6 6
+#define LOCALMEM_ARBITRATION_ZVPORT1_PRIORITY_7 7
+#define LOCALMEM_ARBITRATION_ZVPORT0 14:12
+#define LOCALMEM_ARBITRATION_ZVPORT0_OFF 0
+#define LOCALMEM_ARBITRATION_ZVPORT0_PRIORITY_1 1
+#define LOCALMEM_ARBITRATION_ZVPORT0_PRIORITY_2 2
+#define LOCALMEM_ARBITRATION_ZVPORT0_PRIORITY_3 3
+#define LOCALMEM_ARBITRATION_ZVPORT0_PRIORITY_4 4
+#define LOCALMEM_ARBITRATION_ZVPORT0_PRIORITY_5 5
+#define LOCALMEM_ARBITRATION_ZVPORT0_PRIORITY_6 6
+#define LOCALMEM_ARBITRATION_ZVPORT0_PRIORITY_7 7
+#define LOCALMEM_ARBITRATION_VIDEO 10:8
+#define LOCALMEM_ARBITRATION_VIDEO_OFF 0
+#define LOCALMEM_ARBITRATION_VIDEO_PRIORITY_1 1
+#define LOCALMEM_ARBITRATION_VIDEO_PRIORITY_2 2
+#define LOCALMEM_ARBITRATION_VIDEO_PRIORITY_3 3
+#define LOCALMEM_ARBITRATION_VIDEO_PRIORITY_4 4
+#define LOCALMEM_ARBITRATION_VIDEO_PRIORITY_5 5
+#define LOCALMEM_ARBITRATION_VIDEO_PRIORITY_6 6
+#define LOCALMEM_ARBITRATION_VIDEO_PRIORITY_7 7
+#define LOCALMEM_ARBITRATION_PANEL 6:4
+#define LOCALMEM_ARBITRATION_PANEL_OFF 0
+#define LOCALMEM_ARBITRATION_PANEL_PRIORITY_1 1
+#define LOCALMEM_ARBITRATION_PANEL_PRIORITY_2 2
+#define LOCALMEM_ARBITRATION_PANEL_PRIORITY_3 3
+#define LOCALMEM_ARBITRATION_PANEL_PRIORITY_4 4
+#define LOCALMEM_ARBITRATION_PANEL_PRIORITY_5 5
+#define LOCALMEM_ARBITRATION_PANEL_PRIORITY_6 6
+#define LOCALMEM_ARBITRATION_PANEL_PRIORITY_7 7
+#define LOCALMEM_ARBITRATION_CRT 2:0
+#define LOCALMEM_ARBITRATION_CRT_OFF 0
+#define LOCALMEM_ARBITRATION_CRT_PRIORITY_1 1
+#define LOCALMEM_ARBITRATION_CRT_PRIORITY_2 2
+#define LOCALMEM_ARBITRATION_CRT_PRIORITY_3 3
+#define LOCALMEM_ARBITRATION_CRT_PRIORITY_4 4
+#define LOCALMEM_ARBITRATION_CRT_PRIORITY_5 5
+#define LOCALMEM_ARBITRATION_CRT_PRIORITY_6 6
+#define LOCALMEM_ARBITRATION_CRT_PRIORITY_7 7
+
+#define PCIMEM_ARBITRATION 0x000010
+#define PCIMEM_ARBITRATION_ROTATE 28:28
+#define PCIMEM_ARBITRATION_ROTATE_OFF 0
+#define PCIMEM_ARBITRATION_ROTATE_ON 1
+#define PCIMEM_ARBITRATION_VGA 26:24
+#define PCIMEM_ARBITRATION_VGA_OFF 0
+#define PCIMEM_ARBITRATION_VGA_PRIORITY_1 1
+#define PCIMEM_ARBITRATION_VGA_PRIORITY_2 2
+#define PCIMEM_ARBITRATION_VGA_PRIORITY_3 3
+#define PCIMEM_ARBITRATION_VGA_PRIORITY_4 4
+#define PCIMEM_ARBITRATION_VGA_PRIORITY_5 5
+#define PCIMEM_ARBITRATION_VGA_PRIORITY_6 6
+#define PCIMEM_ARBITRATION_VGA_PRIORITY_7 7
+#define PCIMEM_ARBITRATION_DMA 22:20
+#define PCIMEM_ARBITRATION_DMA_OFF 0
+#define PCIMEM_ARBITRATION_DMA_PRIORITY_1 1
+#define PCIMEM_ARBITRATION_DMA_PRIORITY_2 2
+#define PCIMEM_ARBITRATION_DMA_PRIORITY_3 3
+#define PCIMEM_ARBITRATION_DMA_PRIORITY_4 4
+#define PCIMEM_ARBITRATION_DMA_PRIORITY_5 5
+#define PCIMEM_ARBITRATION_DMA_PRIORITY_6 6
+#define PCIMEM_ARBITRATION_DMA_PRIORITY_7 7
+#define PCIMEM_ARBITRATION_ZVPORT1 18:16
+#define PCIMEM_ARBITRATION_ZVPORT1_OFF 0
+#define PCIMEM_ARBITRATION_ZVPORT1_PRIORITY_1 1
+#define PCIMEM_ARBITRATION_ZVPORT1_PRIORITY_2 2
+#define PCIMEM_ARBITRATION_ZVPORT1_PRIORITY_3 3
+#define PCIMEM_ARBITRATION_ZVPORT1_PRIORITY_4 4
+#define PCIMEM_ARBITRATION_ZVPORT1_PRIORITY_5 5
+#define PCIMEM_ARBITRATION_ZVPORT1_PRIORITY_6 6
+#define PCIMEM_ARBITRATION_ZVPORT1_PRIORITY_7 7
+#define PCIMEM_ARBITRATION_ZVPORT0 14:12
+#define PCIMEM_ARBITRATION_ZVPORT0_OFF 0
+#define PCIMEM_ARBITRATION_ZVPORT0_PRIORITY_1 1
+#define PCIMEM_ARBITRATION_ZVPORT0_PRIORITY_2 2
+#define PCIMEM_ARBITRATION_ZVPORT0_PRIORITY_3 3
+#define PCIMEM_ARBITRATION_ZVPORT0_PRIORITY_4 4
+#define PCIMEM_ARBITRATION_ZVPORT0_PRIORITY_5 5
+#define PCIMEM_ARBITRATION_ZVPORT0_PRIORITY_6 6
+#define PCIMEM_ARBITRATION_ZVPORT0_PRIORITY_7 7
+#define PCIMEM_ARBITRATION_VIDEO 10:8
+#define PCIMEM_ARBITRATION_VIDEO_OFF 0
+#define PCIMEM_ARBITRATION_VIDEO_PRIORITY_1 1
+#define PCIMEM_ARBITRATION_VIDEO_PRIORITY_2 2
+#define PCIMEM_ARBITRATION_VIDEO_PRIORITY_3 3
+#define PCIMEM_ARBITRATION_VIDEO_PRIORITY_4 4
+#define PCIMEM_ARBITRATION_VIDEO_PRIORITY_5 5
+#define PCIMEM_ARBITRATION_VIDEO_PRIORITY_6 6
+#define PCIMEM_ARBITRATION_VIDEO_PRIORITY_7 7
+#define PCIMEM_ARBITRATION_PANEL 6:4
+#define PCIMEM_ARBITRATION_PANEL_OFF 0
+#define PCIMEM_ARBITRATION_PANEL_PRIORITY_1 1
+#define PCIMEM_ARBITRATION_PANEL_PRIORITY_2 2
+#define PCIMEM_ARBITRATION_PANEL_PRIORITY_3 3
+#define PCIMEM_ARBITRATION_PANEL_PRIORITY_4 4
+#define PCIMEM_ARBITRATION_PANEL_PRIORITY_5 5
+#define PCIMEM_ARBITRATION_PANEL_PRIORITY_6 6
+#define PCIMEM_ARBITRATION_PANEL_PRIORITY_7 7
+#define PCIMEM_ARBITRATION_CRT 2:0
+#define PCIMEM_ARBITRATION_CRT_OFF 0
+#define PCIMEM_ARBITRATION_CRT_PRIORITY_1 1
+#define PCIMEM_ARBITRATION_CRT_PRIORITY_2 2
+#define PCIMEM_ARBITRATION_CRT_PRIORITY_3 3
+#define PCIMEM_ARBITRATION_CRT_PRIORITY_4 4
+#define PCIMEM_ARBITRATION_CRT_PRIORITY_5 5
+#define PCIMEM_ARBITRATION_CRT_PRIORITY_6 6
+#define PCIMEM_ARBITRATION_CRT_PRIORITY_7 7
+
+#define RAW_INT 0x000020
+#define RAW_INT_ZVPORT1_VSYNC 4:4
+#define RAW_INT_ZVPORT1_VSYNC_INACTIVE 0
+#define RAW_INT_ZVPORT1_VSYNC_ACTIVE 1
+#define RAW_INT_ZVPORT1_VSYNC_CLEAR 1
+#define RAW_INT_ZVPORT0_VSYNC 3:3
+#define RAW_INT_ZVPORT0_VSYNC_INACTIVE 0
+#define RAW_INT_ZVPORT0_VSYNC_ACTIVE 1
+#define RAW_INT_ZVPORT0_VSYNC_CLEAR 1
+#define RAW_INT_CRT_VSYNC 2:2
+#define RAW_INT_CRT_VSYNC_INACTIVE 0
+#define RAW_INT_CRT_VSYNC_ACTIVE 1
+#define RAW_INT_CRT_VSYNC_CLEAR 1
+#define RAW_INT_PANEL_VSYNC 1:1
+#define RAW_INT_PANEL_VSYNC_INACTIVE 0
+#define RAW_INT_PANEL_VSYNC_ACTIVE 1
+#define RAW_INT_PANEL_VSYNC_CLEAR 1
+#define RAW_INT_VGA_VSYNC 0:0
+#define RAW_INT_VGA_VSYNC_INACTIVE 0
+#define RAW_INT_VGA_VSYNC_ACTIVE 1
+#define RAW_INT_VGA_VSYNC_CLEAR 1
+
+#define INT_STATUS 0x000024
+#define INT_STATUS_GPIO31 31:31
+#define INT_STATUS_GPIO31_INACTIVE 0
+#define INT_STATUS_GPIO31_ACTIVE 1
+#define INT_STATUS_GPIO30 30:30
+#define INT_STATUS_GPIO30_INACTIVE 0
+#define INT_STATUS_GPIO30_ACTIVE 1
+#define INT_STATUS_GPIO29 29:29
+#define INT_STATUS_GPIO29_INACTIVE 0
+#define INT_STATUS_GPIO29_ACTIVE 1
+#define INT_STATUS_GPIO28 28:28
+#define INT_STATUS_GPIO28_INACTIVE 0
+#define INT_STATUS_GPIO28_ACTIVE 1
+#define INT_STATUS_GPIO27 27:27
+#define INT_STATUS_GPIO27_INACTIVE 0
+#define INT_STATUS_GPIO27_ACTIVE 1
+#define INT_STATUS_GPIO26 26:26
+#define INT_STATUS_GPIO26_INACTIVE 0
+#define INT_STATUS_GPIO26_ACTIVE 1
+#define INT_STATUS_GPIO25 25:25
+#define INT_STATUS_GPIO25_INACTIVE 0
+#define INT_STATUS_GPIO25_ACTIVE 1
+#define INT_STATUS_I2C 12:12
+#define INT_STATUS_I2C_INACTIVE 0
+#define INT_STATUS_I2C_ACTIVE 1
+#define INT_STATUS_PWM 11:11
+#define INT_STATUS_PWM_INACTIVE 0
+#define INT_STATUS_PWM_ACTIVE 1
+#define INT_STATUS_DMA1 10:10
+#define INT_STATUS_DMA1_INACTIVE 0
+#define INT_STATUS_DMA1_ACTIVE 1
+#define INT_STATUS_DMA0 9:9
+#define INT_STATUS_DMA0_INACTIVE 0
+#define INT_STATUS_DMA0_ACTIVE 1
+#define INT_STATUS_PCI 8:8
+#define INT_STATUS_PCI_INACTIVE 0
+#define INT_STATUS_PCI_ACTIVE 1
+#define INT_STATUS_SSP1 7:7
+#define INT_STATUS_SSP1_INACTIVE 0
+#define INT_STATUS_SSP1_ACTIVE 1
+#define INT_STATUS_SSP0 6:6
+#define INT_STATUS_SSP0_INACTIVE 0
+#define INT_STATUS_SSP0_ACTIVE 1
+#define INT_STATUS_DE 5:5
+#define INT_STATUS_DE_INACTIVE 0
+#define INT_STATUS_DE_ACTIVE 1
+#define INT_STATUS_ZVPORT1_VSYNC 4:4
+#define INT_STATUS_ZVPORT1_VSYNC_INACTIVE 0
+#define INT_STATUS_ZVPORT1_VSYNC_ACTIVE 1
+#define INT_STATUS_ZVPORT0_VSYNC 3:3
+#define INT_STATUS_ZVPORT0_VSYNC_INACTIVE 0
+#define INT_STATUS_ZVPORT0_VSYNC_ACTIVE 1
+#define INT_STATUS_CRT_VSYNC 2:2
+#define INT_STATUS_CRT_VSYNC_INACTIVE 0
+#define INT_STATUS_CRT_VSYNC_ACTIVE 1
+#define INT_STATUS_PANEL_VSYNC 1:1
+#define INT_STATUS_PANEL_VSYNC_INACTIVE 0
+#define INT_STATUS_PANEL_VSYNC_ACTIVE 1
+#define INT_STATUS_VGA_VSYNC 0:0
+#define INT_STATUS_VGA_VSYNC_INACTIVE 0
+#define INT_STATUS_VGA_VSYNC_ACTIVE 1
+
+#define INT_MASK 0x000028
+#define INT_MASK_GPIO31 31:31
+#define INT_MASK_GPIO31_DISABLE 0
+#define INT_MASK_GPIO31_ENABLE 1
+#define INT_MASK_GPIO30 30:30
+#define INT_MASK_GPIO30_DISABLE 0
+#define INT_MASK_GPIO30_ENABLE 1
+#define INT_MASK_GPIO29 29:29
+#define INT_MASK_GPIO29_DISABLE 0
+#define INT_MASK_GPIO29_ENABLE 1
+#define INT_MASK_GPIO28 28:28
+#define INT_MASK_GPIO28_DISABLE 0
+#define INT_MASK_GPIO28_ENABLE 1
+#define INT_MASK_GPIO27 27:27
+#define INT_MASK_GPIO27_DISABLE 0
+#define INT_MASK_GPIO27_ENABLE 1
+#define INT_MASK_GPIO26 26:26
+#define INT_MASK_GPIO26_DISABLE 0
+#define INT_MASK_GPIO26_ENABLE 1
+#define INT_MASK_GPIO25 25:25
+#define INT_MASK_GPIO25_DISABLE 0
+#define INT_MASK_GPIO25_ENABLE 1
+#define INT_MASK_I2C 12:12
+#define INT_MASK_I2C_DISABLE 0
+#define INT_MASK_I2C_ENABLE 1
+#define INT_MASK_PWM 11:11
+#define INT_MASK_PWM_DISABLE 0
+#define INT_MASK_PWM_ENABLE 1
+#define INT_MASK_DMA1 10:10
+#define INT_MASK_DMA1_DISABLE 0
+#define INT_MASK_DMA1_ENABLE 1
+#define INT_MASK_DMA 9:9
+#define INT_MASK_DMA_DISABLE 0
+#define INT_MASK_DMA_ENABLE 1
+#define INT_MASK_PCI 8:8
+#define INT_MASK_PCI_DISABLE 0
+#define INT_MASK_PCI_ENABLE 1
+#define INT_MASK_SSP1 7:7
+#define INT_MASK_SSP1_DISABLE 0
+#define INT_MASK_SSP1_ENABLE 1
+#define INT_MASK_SSP0 6:6
+#define INT_MASK_SSP0_DISABLE 0
+#define INT_MASK_SSP0_ENABLE 1
+#define INT_MASK_DE 5:5
+#define INT_MASK_DE_DISABLE 0
+#define INT_MASK_DE_ENABLE 1
+#define INT_MASK_ZVPORT1_VSYNC 4:4
+#define INT_MASK_ZVPORT1_VSYNC_DISABLE 0
+#define INT_MASK_ZVPORT1_VSYNC_ENABLE 1
+#define INT_MASK_ZVPORT0_VSYNC 3:3
+#define INT_MASK_ZVPORT0_VSYNC_DISABLE 0
+#define INT_MASK_ZVPORT0_VSYNC_ENABLE 1
+#define INT_MASK_CRT_VSYNC 2:2
+#define INT_MASK_CRT_VSYNC_DISABLE 0
+#define INT_MASK_CRT_VSYNC_ENABLE 1
+#define INT_MASK_PANEL_VSYNC 1:1
+#define INT_MASK_PANEL_VSYNC_DISABLE 0
+#define INT_MASK_PANEL_VSYNC_ENABLE 1
+#define INT_MASK_VGA_VSYNC 0:0
+#define INT_MASK_VGA_VSYNC_DISABLE 0
+#define INT_MASK_VGA_VSYNC_ENABLE 1
+
+#define CURRENT_GATE 0x000040
+#define CURRENT_GATE_MCLK 15:14
+#ifdef VALIDATION_CHIP
+ #define CURRENT_GATE_MCLK_112MHZ 0
+ #define CURRENT_GATE_MCLK_84MHZ 1
+ #define CURRENT_GATE_MCLK_56MHZ 2
+ #define CURRENT_GATE_MCLK_42MHZ 3
+#else
+ #define CURRENT_GATE_MCLK_DIV_3 0
+ #define CURRENT_GATE_MCLK_DIV_4 1
+ #define CURRENT_GATE_MCLK_DIV_6 2
+ #define CURRENT_GATE_MCLK_DIV_8 3
+#endif
+#define CURRENT_GATE_M2XCLK 13:12
+#ifdef VALIDATION_CHIP
+ #define CURRENT_GATE_M2XCLK_336MHZ 0
+ #define CURRENT_GATE_M2XCLK_168MHZ 1
+ #define CURRENT_GATE_M2XCLK_112MHZ 2
+ #define CURRENT_GATE_M2XCLK_84MHZ 3
+#else
+ #define CURRENT_GATE_M2XCLK_DIV_1 0
+ #define CURRENT_GATE_M2XCLK_DIV_2 1
+ #define CURRENT_GATE_M2XCLK_DIV_3 2
+ #define CURRENT_GATE_M2XCLK_DIV_4 3
+#endif
+#define CURRENT_GATE_VGA 10:10
+#define CURRENT_GATE_VGA_OFF 0
+#define CURRENT_GATE_VGA_ON 1
+#define CURRENT_GATE_PWM 9:9
+#define CURRENT_GATE_PWM_OFF 0
+#define CURRENT_GATE_PWM_ON 1
+#define CURRENT_GATE_I2C 8:8
+#define CURRENT_GATE_I2C_OFF 0
+#define CURRENT_GATE_I2C_ON 1
+#define CURRENT_GATE_SSP 7:7
+#define CURRENT_GATE_SSP_OFF 0
+#define CURRENT_GATE_SSP_ON 1
+#define CURRENT_GATE_GPIO 6:6
+#define CURRENT_GATE_GPIO_OFF 0
+#define CURRENT_GATE_GPIO_ON 1
+#define CURRENT_GATE_ZVPORT 5:5
+#define CURRENT_GATE_ZVPORT_OFF 0
+#define CURRENT_GATE_ZVPORT_ON 1
+#define CURRENT_GATE_CSC 4:4
+#define CURRENT_GATE_CSC_OFF 0
+#define CURRENT_GATE_CSC_ON 1
+#define CURRENT_GATE_DE 3:3
+#define CURRENT_GATE_DE_OFF 0
+#define CURRENT_GATE_DE_ON 1
+#define CURRENT_GATE_DISPLAY 2:2
+#define CURRENT_GATE_DISPLAY_OFF 0
+#define CURRENT_GATE_DISPLAY_ON 1
+#define CURRENT_GATE_LOCALMEM 1:1
+#define CURRENT_GATE_LOCALMEM_OFF 0
+#define CURRENT_GATE_LOCALMEM_ON 1
+#define CURRENT_GATE_DMA 0:0
+#define CURRENT_GATE_DMA_OFF 0
+#define CURRENT_GATE_DMA_ON 1
+
+#define MODE0_GATE 0x000044
+#define MODE0_GATE_MCLK 15:14
+#define MODE0_GATE_MCLK_112MHZ 0
+#define MODE0_GATE_MCLK_84MHZ 1
+#define MODE0_GATE_MCLK_56MHZ 2
+#define MODE0_GATE_MCLK_42MHZ 3
+#define MODE0_GATE_M2XCLK 13:12
+#define MODE0_GATE_M2XCLK_336MHZ 0
+#define MODE0_GATE_M2XCLK_168MHZ 1
+#define MODE0_GATE_M2XCLK_112MHZ 2
+#define MODE0_GATE_M2XCLK_84MHZ 3
+#define MODE0_GATE_VGA 10:10
+#define MODE0_GATE_VGA_OFF 0
+#define MODE0_GATE_VGA_ON 1
+#define MODE0_GATE_PWM 9:9
+#define MODE0_GATE_PWM_OFF 0
+#define MODE0_GATE_PWM_ON 1
+#define MODE0_GATE_I2C 8:8
+#define MODE0_GATE_I2C_OFF 0
+#define MODE0_GATE_I2C_ON 1
+#define MODE0_GATE_SSP 7:7
+#define MODE0_GATE_SSP_OFF 0
+#define MODE0_GATE_SSP_ON 1
+#define MODE0_GATE_GPIO 6:6
+#define MODE0_GATE_GPIO_OFF 0
+#define MODE0_GATE_GPIO_ON 1
+#define MODE0_GATE_ZVPORT 5:5
+#define MODE0_GATE_ZVPORT_OFF 0
+#define MODE0_GATE_ZVPORT_ON 1
+#define MODE0_GATE_CSC 4:4
+#define MODE0_GATE_CSC_OFF 0
+#define MODE0_GATE_CSC_ON 1
+#define MODE0_GATE_DE 3:3
+#define MODE0_GATE_DE_OFF 0
+#define MODE0_GATE_DE_ON 1
+#define MODE0_GATE_DISPLAY 2:2
+#define MODE0_GATE_DISPLAY_OFF 0
+#define MODE0_GATE_DISPLAY_ON 1
+#define MODE0_GATE_LOCALMEM 1:1
+#define MODE0_GATE_LOCALMEM_OFF 0
+#define MODE0_GATE_LOCALMEM_ON 1
+#define MODE0_GATE_DMA 0:0
+#define MODE0_GATE_DMA_OFF 0
+#define MODE0_GATE_DMA_ON 1
+
+#define MODE1_GATE 0x000048
+#define MODE1_GATE_MCLK 15:14
+#define MODE1_GATE_MCLK_112MHZ 0
+#define MODE1_GATE_MCLK_84MHZ 1
+#define MODE1_GATE_MCLK_56MHZ 2
+#define MODE1_GATE_MCLK_42MHZ 3
+#define MODE1_GATE_M2XCLK 13:12
+#define MODE1_GATE_M2XCLK_336MHZ 0
+#define MODE1_GATE_M2XCLK_168MHZ 1
+#define MODE1_GATE_M2XCLK_112MHZ 2
+#define MODE1_GATE_M2XCLK_84MHZ 3
+#define MODE1_GATE_VGA 10:10
+#define MODE1_GATE_VGA_OFF 0
+#define MODE1_GATE_VGA_ON 1
+#define MODE1_GATE_PWM 9:9
+#define MODE1_GATE_PWM_OFF 0
+#define MODE1_GATE_PWM_ON 1
+#define MODE1_GATE_I2C 8:8
+#define MODE1_GATE_I2C_OFF 0
+#define MODE1_GATE_I2C_ON 1
+#define MODE1_GATE_SSP 7:7
+#define MODE1_GATE_SSP_OFF 0
+#define MODE1_GATE_SSP_ON 1
+#define MODE1_GATE_GPIO 6:6
+#define MODE1_GATE_GPIO_OFF 0
+#define MODE1_GATE_GPIO_ON 1
+#define MODE1_GATE_ZVPORT 5:5
+#define MODE1_GATE_ZVPORT_OFF 0
+#define MODE1_GATE_ZVPORT_ON 1
+#define MODE1_GATE_CSC 4:4
+#define MODE1_GATE_CSC_OFF 0
+#define MODE1_GATE_CSC_ON 1
+#define MODE1_GATE_DE 3:3
+#define MODE1_GATE_DE_OFF 0
+#define MODE1_GATE_DE_ON 1
+#define MODE1_GATE_DISPLAY 2:2
+#define MODE1_GATE_DISPLAY_OFF 0
+#define MODE1_GATE_DISPLAY_ON 1
+#define MODE1_GATE_LOCALMEM 1:1
+#define MODE1_GATE_LOCALMEM_OFF 0
+#define MODE1_GATE_LOCALMEM_ON 1
+#define MODE1_GATE_DMA 0:0
+#define MODE1_GATE_DMA_OFF 0
+#define MODE1_GATE_DMA_ON 1
+
+#define POWER_MODE_CTRL 0x00004C
+#ifdef VALIDATION_CHIP
+ #define POWER_MODE_CTRL_336CLK 4:4
+ #define POWER_MODE_CTRL_336CLK_OFF 0
+ #define POWER_MODE_CTRL_336CLK_ON 1
+#endif
+#define POWER_MODE_CTRL_OSC_INPUT 3:3
+#define POWER_MODE_CTRL_OSC_INPUT_OFF 0
+#define POWER_MODE_CTRL_OSC_INPUT_ON 1
+#define POWER_MODE_CTRL_ACPI 2:2
+#define POWER_MODE_CTRL_ACPI_OFF 0
+#define POWER_MODE_CTRL_ACPI_ON 1
+#define POWER_MODE_CTRL_MODE 1:0
+#define POWER_MODE_CTRL_MODE_MODE0 0
+#define POWER_MODE_CTRL_MODE_MODE1 1
+#define POWER_MODE_CTRL_MODE_SLEEP 2
+
+#define PCI_MASTER_BASE 0x000050
+#define PCI_MASTER_BASE_ADDRESS 7:0
+
+#define DEVICE_ID 0x000054
+#define DEVICE_ID_DEVICE_ID 31:16
+#define DEVICE_ID_REVISION_ID 7:0
+
+#define PLL_CLK_COUNT 0x000058
+#define PLL_CLK_COUNT_COUNTER 15:0
+
+#define PANEL_PLL_CTRL 0x00005C
+#define PANEL_PLL_CTRL_BYPASS 18:18
+#define PANEL_PLL_CTRL_BYPASS_OFF 0
+#define PANEL_PLL_CTRL_BYPASS_ON 1
+#define PANEL_PLL_CTRL_POWER 17:17
+#define PANEL_PLL_CTRL_POWER_OFF 0
+#define PANEL_PLL_CTRL_POWER_ON 1
+#define PANEL_PLL_CTRL_INPUT 16:16
+#define PANEL_PLL_CTRL_INPUT_OSC 0
+#define PANEL_PLL_CTRL_INPUT_TESTCLK 1
+#ifdef VALIDATION_CHIP
+ #define PANEL_PLL_CTRL_OD 15:14
+#else
+ #define PANEL_PLL_CTRL_POD 15:14
+ #define PANEL_PLL_CTRL_OD 13:12
+#endif
+#define PANEL_PLL_CTRL_N 11:8
+#define PANEL_PLL_CTRL_M 7:0
+
+#define CRT_PLL_CTRL 0x000060
+#define CRT_PLL_CTRL_BYPASS 18:18
+#define CRT_PLL_CTRL_BYPASS_OFF 0
+#define CRT_PLL_CTRL_BYPASS_ON 1
+#define CRT_PLL_CTRL_POWER 17:17
+#define CRT_PLL_CTRL_POWER_OFF 0
+#define CRT_PLL_CTRL_POWER_ON 1
+#define CRT_PLL_CTRL_INPUT 16:16
+#define CRT_PLL_CTRL_INPUT_OSC 0
+#define CRT_PLL_CTRL_INPUT_TESTCLK 1
+#ifdef VALIDATION_CHIP
+ #define CRT_PLL_CTRL_OD 15:14
+#else
+ #define CRT_PLL_CTRL_POD 15:14
+ #define CRT_PLL_CTRL_OD 13:12
+#endif
+#define CRT_PLL_CTRL_N 11:8
+#define CRT_PLL_CTRL_M 7:0
+
+#define VGA_PLL0_CTRL 0x000064
+#define VGA_PLL0_CTRL_BYPASS 18:18
+#define VGA_PLL0_CTRL_BYPASS_OFF 0
+#define VGA_PLL0_CTRL_BYPASS_ON 1
+#define VGA_PLL0_CTRL_POWER 17:17
+#define VGA_PLL0_CTRL_POWER_OFF 0
+#define VGA_PLL0_CTRL_POWER_ON 1
+#define VGA_PLL0_CTRL_INPUT 16:16
+#define VGA_PLL0_CTRL_INPUT_OSC 0
+#define VGA_PLL0_CTRL_INPUT_TESTCLK 1
+#ifdef VALIDATION_CHIP
+ #define VGA_PLL0_CTRL_OD 15:14
+#else
+ #define VGA_PLL0_CTRL_POD 15:14
+ #define VGA_PLL0_CTRL_OD 13:12
+#endif
+#define VGA_PLL0_CTRL_N 11:8
+#define VGA_PLL0_CTRL_M 7:0
+
+#define VGA_PLL1_CTRL 0x000068
+#define VGA_PLL1_CTRL_BYPASS 18:18
+#define VGA_PLL1_CTRL_BYPASS_OFF 0
+#define VGA_PLL1_CTRL_BYPASS_ON 1
+#define VGA_PLL1_CTRL_POWER 17:17
+#define VGA_PLL1_CTRL_POWER_OFF 0
+#define VGA_PLL1_CTRL_POWER_ON 1
+#define VGA_PLL1_CTRL_INPUT 16:16
+#define VGA_PLL1_CTRL_INPUT_OSC 0
+#define VGA_PLL1_CTRL_INPUT_TESTCLK 1
+#ifdef VALIDATION_CHIP
+ #define VGA_PLL1_CTRL_OD 15:14
+#else
+ #define VGA_PLL1_CTRL_POD 15:14
+ #define VGA_PLL1_CTRL_OD 13:12
+#endif
+#define VGA_PLL1_CTRL_N 11:8
+#define VGA_PLL1_CTRL_M 7:0
+
+#define SCRATCH_DATA 0x00006c
+
+#ifndef VALIDATION_CHIP
+
+#define MXCLK_PLL_CTRL 0x000070
+#define MXCLK_PLL_CTRL_BYPASS 18:18
+#define MXCLK_PLL_CTRL_BYPASS_OFF 0
+#define MXCLK_PLL_CTRL_BYPASS_ON 1
+#define MXCLK_PLL_CTRL_POWER 17:17
+#define MXCLK_PLL_CTRL_POWER_OFF 0
+#define MXCLK_PLL_CTRL_POWER_ON 1
+#define MXCLK_PLL_CTRL_INPUT 16:16
+#define MXCLK_PLL_CTRL_INPUT_OSC 0
+#define MXCLK_PLL_CTRL_INPUT_TESTCLK 1
+#define MXCLK_PLL_CTRL_POD 15:14
+#define MXCLK_PLL_CTRL_OD 13:12
+#define MXCLK_PLL_CTRL_N 11:8
+#define MXCLK_PLL_CTRL_M 7:0
+
+#define VGA_CONFIGURATION 0x000088
+#define VGA_CONFIGURATION_USER_DEFINE 5:4
+#define VGA_CONFIGURATION_PLL 2:2
+#define VGA_CONFIGURATION_PLL_VGA 0
+#define VGA_CONFIGURATION_PLL_PANEL 1
+#define VGA_CONFIGURATION_MODE 1:1
+#define VGA_CONFIGURATION_MODE_TEXT 0
+#define VGA_CONFIGURATION_MODE_GRAPHIC 1
+
+#endif
+
+#define GPIO_DATA 0x010000
+#define GPIO_DATA_31 31:31
+#define GPIO_DATA_30 30:30
+#define GPIO_DATA_29 29:29
+#define GPIO_DATA_28 28:28
+#define GPIO_DATA_27 27:27
+#define GPIO_DATA_26 26:26
+#define GPIO_DATA_25 25:25
+#define GPIO_DATA_24 24:24
+#define GPIO_DATA_23 23:23
+#define GPIO_DATA_22 22:22
+#define GPIO_DATA_21 21:21
+#define GPIO_DATA_20 20:20
+#define GPIO_DATA_19 19:19
+#define GPIO_DATA_18 18:18
+#define GPIO_DATA_17 17:17
+#define GPIO_DATA_16 16:16
+#define GPIO_DATA_15 15:15
+#define GPIO_DATA_14 14:14
+#define GPIO_DATA_13 13:13
+#define GPIO_DATA_12 12:12
+#define GPIO_DATA_11 11:11
+#define GPIO_DATA_10 10:10
+#define GPIO_DATA_9 9:9
+#define GPIO_DATA_8 8:8
+#define GPIO_DATA_7 7:7
+#define GPIO_DATA_6 6:6
+#define GPIO_DATA_5 5:5
+#define GPIO_DATA_4 4:4
+#define GPIO_DATA_3 3:3
+#define GPIO_DATA_2 2:2
+#define GPIO_DATA_1 1:1
+#define GPIO_DATA_0 0:0
+
+#define GPIO_DATA_DIRECTION 0x010004
+#define GPIO_DATA_DIRECTION_31 31:31
+#define GPIO_DATA_DIRECTION_31_INPUT 0
+#define GPIO_DATA_DIRECTION_31_OUTPUT 1
+#define GPIO_DATA_DIRECTION_30 30:30
+#define GPIO_DATA_DIRECTION_30_INPUT 0
+#define GPIO_DATA_DIRECTION_30_OUTPUT 1
+#define GPIO_DATA_DIRECTION_29 29:29
+#define GPIO_DATA_DIRECTION_29_INPUT 0
+#define GPIO_DATA_DIRECTION_29_OUTPUT 1
+#define GPIO_DATA_DIRECTION_28 28:28
+#define GPIO_DATA_DIRECTION_28_INPUT 0
+#define GPIO_DATA_DIRECTION_28_OUTPUT 1
+#define GPIO_DATA_DIRECTION_27 27:27
+#define GPIO_DATA_DIRECTION_27_INPUT 0
+#define GPIO_DATA_DIRECTION_27_OUTPUT 1
+#define GPIO_DATA_DIRECTION_26 26:26
+#define GPIO_DATA_DIRECTION_26_INPUT 0
+#define GPIO_DATA_DIRECTION_26_OUTPUT 1
+#define GPIO_DATA_DIRECTION_25 25:25
+#define GPIO_DATA_DIRECTION_25_INPUT 0
+#define GPIO_DATA_DIRECTION_25_OUTPUT 1
+#define GPIO_DATA_DIRECTION_24 24:24
+#define GPIO_DATA_DIRECTION_24_INPUT 0
+#define GPIO_DATA_DIRECTION_24_OUTPUT 1
+#define GPIO_DATA_DIRECTION_23 23:23
+#define GPIO_DATA_DIRECTION_23_INPUT 0
+#define GPIO_DATA_DIRECTION_23_OUTPUT 1
+#define GPIO_DATA_DIRECTION_22 22:22
+#define GPIO_DATA_DIRECTION_22_INPUT 0
+#define GPIO_DATA_DIRECTION_22_OUTPUT 1
+#define GPIO_DATA_DIRECTION_21 21:21
+#define GPIO_DATA_DIRECTION_21_INPUT 0
+#define GPIO_DATA_DIRECTION_21_OUTPUT 1
+#define GPIO_DATA_DIRECTION_20 20:20
+#define GPIO_DATA_DIRECTION_20_INPUT 0
+#define GPIO_DATA_DIRECTION_20_OUTPUT 1
+#define GPIO_DATA_DIRECTION_19 19:19
+#define GPIO_DATA_DIRECTION_19_INPUT 0
+#define GPIO_DATA_DIRECTION_19_OUTPUT 1
+#define GPIO_DATA_DIRECTION_18 18:18
+#define GPIO_DATA_DIRECTION_18_INPUT 0
+#define GPIO_DATA_DIRECTION_18_OUTPUT 1
+#define GPIO_DATA_DIRECTION_17 17:17
+#define GPIO_DATA_DIRECTION_17_INPUT 0
+#define GPIO_DATA_DIRECTION_17_OUTPUT 1
+#define GPIO_DATA_DIRECTION_16 16:16
+#define GPIO_DATA_DIRECTION_16_INPUT 0
+#define GPIO_DATA_DIRECTION_16_OUTPUT 1
+#define GPIO_DATA_DIRECTION_15 15:15
+#define GPIO_DATA_DIRECTION_15_INPUT 0
+#define GPIO_DATA_DIRECTION_15_OUTPUT 1
+#define GPIO_DATA_DIRECTION_14 14:14
+#define GPIO_DATA_DIRECTION_14_INPUT 0
+#define GPIO_DATA_DIRECTION_14_OUTPUT 1
+#define GPIO_DATA_DIRECTION_13 13:13
+#define GPIO_DATA_DIRECTION_13_INPUT 0
+#define GPIO_DATA_DIRECTION_13_OUTPUT 1
+#define GPIO_DATA_DIRECTION_12 12:12
+#define GPIO_DATA_DIRECTION_12_INPUT 0
+#define GPIO_DATA_DIRECTION_12_OUTPUT 1
+#define GPIO_DATA_DIRECTION_11 11:11
+#define GPIO_DATA_DIRECTION_11_INPUT 0
+#define GPIO_DATA_DIRECTION_11_OUTPUT 1
+#define GPIO_DATA_DIRECTION_10 10:10
+#define GPIO_DATA_DIRECTION_10_INPUT 0
+#define GPIO_DATA_DIRECTION_10_OUTPUT 1
+#define GPIO_DATA_DIRECTION_9 9:9
+#define GPIO_DATA_DIRECTION_9_INPUT 0
+#define GPIO_DATA_DIRECTION_9_OUTPUT 1
+#define GPIO_DATA_DIRECTION_8 8:8
+#define GPIO_DATA_DIRECTION_8_INPUT 0
+#define GPIO_DATA_DIRECTION_8_OUTPUT 1
+#define GPIO_DATA_DIRECTION_7 7:7
+#define GPIO_DATA_DIRECTION_7_INPUT 0
+#define GPIO_DATA_DIRECTION_7_OUTPUT 1
+#define GPIO_DATA_DIRECTION_6 6:6
+#define GPIO_DATA_DIRECTION_6_INPUT 0
+#define GPIO_DATA_DIRECTION_6_OUTPUT 1
+#define GPIO_DATA_DIRECTION_5 5:5
+#define GPIO_DATA_DIRECTION_5_INPUT 0
+#define GPIO_DATA_DIRECTION_5_OUTPUT 1
+#define GPIO_DATA_DIRECTION_4 4:4
+#define GPIO_DATA_DIRECTION_4_INPUT 0
+#define GPIO_DATA_DIRECTION_4_OUTPUT 1
+#define GPIO_DATA_DIRECTION_3 3:3
+#define GPIO_DATA_DIRECTION_3_INPUT 0
+#define GPIO_DATA_DIRECTION_3_OUTPUT 1
+#define GPIO_DATA_DIRECTION_2 2:2
+#define GPIO_DATA_DIRECTION_2_INPUT 0
+#define GPIO_DATA_DIRECTION_2_OUTPUT 1
+#define GPIO_DATA_DIRECTION_1 131
+#define GPIO_DATA_DIRECTION_1_INPUT 0
+#define GPIO_DATA_DIRECTION_1_OUTPUT 1
+#define GPIO_DATA_DIRECTION_0 0:0
+#define GPIO_DATA_DIRECTION_0_INPUT 0
+#define GPIO_DATA_DIRECTION_0_OUTPUT 1
+
+#define GPIO_INTERRUPT_SETUP 0x010008
+#define GPIO_INTERRUPT_SETUP_TRIGGER_31 22:22
+#define GPIO_INTERRUPT_SETUP_TRIGGER_31_EDGE 0
+#define GPIO_INTERRUPT_SETUP_TRIGGER_31_LEVEL 1
+#define GPIO_INTERRUPT_SETUP_TRIGGER_30 21:21
+#define GPIO_INTERRUPT_SETUP_TRIGGER_30_EDGE 0
+#define GPIO_INTERRUPT_SETUP_TRIGGER_30_LEVEL 1
+#define GPIO_INTERRUPT_SETUP_TRIGGER_29 20:20
+#define GPIO_INTERRUPT_SETUP_TRIGGER_29_EDGE 0
+#define GPIO_INTERRUPT_SETUP_TRIGGER_29_LEVEL 1
+#define GPIO_INTERRUPT_SETUP_TRIGGER_28 19:19
+#define GPIO_INTERRUPT_SETUP_TRIGGER_28_EDGE 0
+#define GPIO_INTERRUPT_SETUP_TRIGGER_28_LEVEL 1
+#define GPIO_INTERRUPT_SETUP_TRIGGER_27 18:18
+#define GPIO_INTERRUPT_SETUP_TRIGGER_27_EDGE 0
+#define GPIO_INTERRUPT_SETUP_TRIGGER_27_LEVEL 1
+#define GPIO_INTERRUPT_SETUP_TRIGGER_26 17:17
+#define GPIO_INTERRUPT_SETUP_TRIGGER_26_EDGE 0
+#define GPIO_INTERRUPT_SETUP_TRIGGER_26_LEVEL 1
+#define GPIO_INTERRUPT_SETUP_TRIGGER_25 16:16
+#define GPIO_INTERRUPT_SETUP_TRIGGER_25_EDGE 0
+#define GPIO_INTERRUPT_SETUP_TRIGGER_25_LEVEL 1
+#define GPIO_INTERRUPT_SETUP_ACTIVE_31 14:14
+#define GPIO_INTERRUPT_SETUP_ACTIVE_31_LOW 0
+#define GPIO_INTERRUPT_SETUP_ACTIVE_31_HIGH 1
+#define GPIO_INTERRUPT_SETUP_ACTIVE_30 13:13
+#define GPIO_INTERRUPT_SETUP_ACTIVE_30_LOW 0
+#define GPIO_INTERRUPT_SETUP_ACTIVE_30_HIGH 1
+#define GPIO_INTERRUPT_SETUP_ACTIVE_29 12:12
+#define GPIO_INTERRUPT_SETUP_ACTIVE_29_LOW 0
+#define GPIO_INTERRUPT_SETUP_ACTIVE_29_HIGH 1
+#define GPIO_INTERRUPT_SETUP_ACTIVE_28 11:11
+#define GPIO_INTERRUPT_SETUP_ACTIVE_28_LOW 0
+#define GPIO_INTERRUPT_SETUP_ACTIVE_28_HIGH 1
+#define GPIO_INTERRUPT_SETUP_ACTIVE_27 10:10
+#define GPIO_INTERRUPT_SETUP_ACTIVE_27_LOW 0
+#define GPIO_INTERRUPT_SETUP_ACTIVE_27_HIGH 1
+#define GPIO_INTERRUPT_SETUP_ACTIVE_26 9:9
+#define GPIO_INTERRUPT_SETUP_ACTIVE_26_LOW 0
+#define GPIO_INTERRUPT_SETUP_ACTIVE_26_HIGH 1
+#define GPIO_INTERRUPT_SETUP_ACTIVE_25 8:8
+#define GPIO_INTERRUPT_SETUP_ACTIVE_25_LOW 0
+#define GPIO_INTERRUPT_SETUP_ACTIVE_25_HIGH 1
+#define GPIO_INTERRUPT_SETUP_ENABLE_31 6:6
+#define GPIO_INTERRUPT_SETUP_ENABLE_31_GPIO 0
+#define GPIO_INTERRUPT_SETUP_ENABLE_31_INTERRUPT 1
+#define GPIO_INTERRUPT_SETUP_ENABLE_30 5:5
+#define GPIO_INTERRUPT_SETUP_ENABLE_30_GPIO 0
+#define GPIO_INTERRUPT_SETUP_ENABLE_30_INTERRUPT 1
+#define GPIO_INTERRUPT_SETUP_ENABLE_29 4:4
+#define GPIO_INTERRUPT_SETUP_ENABLE_29_GPIO 0
+#define GPIO_INTERRUPT_SETUP_ENABLE_29_INTERRUPT 1
+#define GPIO_INTERRUPT_SETUP_ENABLE_28 3:3
+#define GPIO_INTERRUPT_SETUP_ENABLE_28_GPIO 0
+#define GPIO_INTERRUPT_SETUP_ENABLE_28_INTERRUPT 1
+#define GPIO_INTERRUPT_SETUP_ENABLE_27 2:2
+#define GPIO_INTERRUPT_SETUP_ENABLE_27_GPIO 0
+#define GPIO_INTERRUPT_SETUP_ENABLE_27_INTERRUPT 1
+#define GPIO_INTERRUPT_SETUP_ENABLE_26 1:1
+#define GPIO_INTERRUPT_SETUP_ENABLE_26_GPIO 0
+#define GPIO_INTERRUPT_SETUP_ENABLE_26_INTERRUPT 1
+#define GPIO_INTERRUPT_SETUP_ENABLE_25 0:0
+#define GPIO_INTERRUPT_SETUP_ENABLE_25_GPIO 0
+#define GPIO_INTERRUPT_SETUP_ENABLE_25_INTERRUPT 1
+
+#define GPIO_INTERRUPT_STATUS 0x01000C
+#define GPIO_INTERRUPT_STATUS_31 22:22
+#define GPIO_INTERRUPT_STATUS_31_INACTIVE 0
+#define GPIO_INTERRUPT_STATUS_31_ACTIVE 1
+#define GPIO_INTERRUPT_STATUS_31_RESET 1
+#define GPIO_INTERRUPT_STATUS_30 21:21
+#define GPIO_INTERRUPT_STATUS_30_INACTIVE 0
+#define GPIO_INTERRUPT_STATUS_30_ACTIVE 1
+#define GPIO_INTERRUPT_STATUS_30_RESET 1
+#define GPIO_INTERRUPT_STATUS_29 20:20
+#define GPIO_INTERRUPT_STATUS_29_INACTIVE 0
+#define GPIO_INTERRUPT_STATUS_29_ACTIVE 1
+#define GPIO_INTERRUPT_STATUS_29_RESET 1
+#define GPIO_INTERRUPT_STATUS_28 19:19
+#define GPIO_INTERRUPT_STATUS_28_INACTIVE 0
+#define GPIO_INTERRUPT_STATUS_28_ACTIVE 1
+#define GPIO_INTERRUPT_STATUS_28_RESET 1
+#define GPIO_INTERRUPT_STATUS_27 18:18
+#define GPIO_INTERRUPT_STATUS_27_INACTIVE 0
+#define GPIO_INTERRUPT_STATUS_27_ACTIVE 1
+#define GPIO_INTERRUPT_STATUS_27_RESET 1
+#define GPIO_INTERRUPT_STATUS_26 17:17
+#define GPIO_INTERRUPT_STATUS_26_INACTIVE 0
+#define GPIO_INTERRUPT_STATUS_26_ACTIVE 1
+#define GPIO_INTERRUPT_STATUS_26_RESET 1
+#define GPIO_INTERRUPT_STATUS_25 16:16
+#define GPIO_INTERRUPT_STATUS_25_INACTIVE 0
+#define GPIO_INTERRUPT_STATUS_25_ACTIVE 1
+#define GPIO_INTERRUPT_STATUS_25_RESET 1
+
+
+#define PANEL_DISPLAY_CTRL 0x080000
+#define PANEL_DISPLAY_CTRL_RESERVED_1_MASK 31:30
+#define PANEL_DISPLAY_CTRL_RESERVED_1_MASK_DISABLE 0
+#define PANEL_DISPLAY_CTRL_RESERVED_1_MASK_ENABLE 3
+#define PANEL_DISPLAY_CTRL_SELECT 29:28
+#define PANEL_DISPLAY_CTRL_SELECT_PANEL 0
+#define PANEL_DISPLAY_CTRL_SELECT_VGA 1
+#define PANEL_DISPLAY_CTRL_SELECT_CRT 2
+#define PANEL_DISPLAY_CTRL_FPEN 27:27
+#define PANEL_DISPLAY_CTRL_FPEN_LOW 0
+#define PANEL_DISPLAY_CTRL_FPEN_HIGH 1
+#define PANEL_DISPLAY_CTRL_VBIASEN 26:26
+#define PANEL_DISPLAY_CTRL_VBIASEN_LOW 0
+#define PANEL_DISPLAY_CTRL_VBIASEN_HIGH 1
+#define PANEL_DISPLAY_CTRL_DATA 25:25
+#define PANEL_DISPLAY_CTRL_DATA_DISABLE 0
+#define PANEL_DISPLAY_CTRL_DATA_ENABLE 1
+#define PANEL_DISPLAY_CTRL_FPVDDEN 24:24
+#define PANEL_DISPLAY_CTRL_FPVDDEN_LOW 0
+#define PANEL_DISPLAY_CTRL_FPVDDEN_HIGH 1
+#define PANEL_DISPLAY_CTRL_RESERVED_2_MASK 23:20
+#define PANEL_DISPLAY_CTRL_RESERVED_2_MASK_DISABLE 0
+#define PANEL_DISPLAY_CTRL_RESERVED_2_MASK_ENABLE 15
+
+#define PANEL_DISPLAY_CTRL_TFT_DISP 19:18
+#define PANEL_DISPLAY_CTRL_TFT_DISP_24 0
+#define PANEL_DISPLAY_CTRL_TFT_DISP_36 1
+#define PANEL_DISPLAY_CTRL_TFT_DISP_18 2
+
+
+#define PANEL_DISPLAY_CTRL_DUAL_DISPLAY 19:19
+#define PANEL_DISPLAY_CTRL_DUAL_DISPLAY_DISABLE 0
+#define PANEL_DISPLAY_CTRL_DUAL_DISPLAY_ENABLE 1
+#define PANEL_DISPLAY_CTRL_DOUBLE_PIXEL 18:18
+#define PANEL_DISPLAY_CTRL_DOUBLE_PIXEL_DISABLE 0
+#define PANEL_DISPLAY_CTRL_DOUBLE_PIXEL_ENABLE 1
+#define PANEL_DISPLAY_CTRL_FIFO 17:16
+#define PANEL_DISPLAY_CTRL_FIFO_1 0
+#define PANEL_DISPLAY_CTRL_FIFO_3 1
+#define PANEL_DISPLAY_CTRL_FIFO_7 2
+#define PANEL_DISPLAY_CTRL_FIFO_11 3
+#define PANEL_DISPLAY_CTRL_RESERVED_3_MASK 15:15
+#define PANEL_DISPLAY_CTRL_RESERVED_3_MASK_DISABLE 0
+#define PANEL_DISPLAY_CTRL_RESERVED_3_MASK_ENABLE 1
+#define PANEL_DISPLAY_CTRL_CLOCK_PHASE 14:14
+#define PANEL_DISPLAY_CTRL_CLOCK_PHASE_ACTIVE_HIGH 0
+#define PANEL_DISPLAY_CTRL_CLOCK_PHASE_ACTIVE_LOW 1
+#define PANEL_DISPLAY_CTRL_VSYNC_PHASE 13:13
+#define PANEL_DISPLAY_CTRL_VSYNC_PHASE_ACTIVE_HIGH 0
+#define PANEL_DISPLAY_CTRL_VSYNC_PHASE_ACTIVE_LOW 1
+#define PANEL_DISPLAY_CTRL_HSYNC_PHASE 12:12
+#define PANEL_DISPLAY_CTRL_HSYNC_PHASE_ACTIVE_HIGH 0
+#define PANEL_DISPLAY_CTRL_HSYNC_PHASE_ACTIVE_LOW 1
+#define PANEL_DISPLAY_CTRL_VSYNC 11:11
+#define PANEL_DISPLAY_CTRL_VSYNC_ACTIVE_HIGH 0
+#define PANEL_DISPLAY_CTRL_VSYNC_ACTIVE_LOW 1
+#define PANEL_DISPLAY_CTRL_CAPTURE_TIMING 10:10
+#define PANEL_DISPLAY_CTRL_CAPTURE_TIMING_DISABLE 0
+#define PANEL_DISPLAY_CTRL_CAPTURE_TIMING_ENABLE 1
+#define PANEL_DISPLAY_CTRL_COLOR_KEY 9:9
+#define PANEL_DISPLAY_CTRL_COLOR_KEY_DISABLE 0
+#define PANEL_DISPLAY_CTRL_COLOR_KEY_ENABLE 1
+#define PANEL_DISPLAY_CTRL_TIMING 8:8
+#define PANEL_DISPLAY_CTRL_TIMING_DISABLE 0
+#define PANEL_DISPLAY_CTRL_TIMING_ENABLE 1
+#define PANEL_DISPLAY_CTRL_VERTICAL_PAN_DIR 7:7
+#define PANEL_DISPLAY_CTRL_VERTICAL_PAN_DIR_DOWN 0
+#define PANEL_DISPLAY_CTRL_VERTICAL_PAN_DIR_UP 1
+#define PANEL_DISPLAY_CTRL_VERTICAL_PAN 6:6
+#define PANEL_DISPLAY_CTRL_VERTICAL_PAN_DISABLE 0
+#define PANEL_DISPLAY_CTRL_VERTICAL_PAN_ENABLE 1
+#define PANEL_DISPLAY_CTRL_HORIZONTAL_PAN_DIR 5:5
+#define PANEL_DISPLAY_CTRL_HORIZONTAL_PAN_DIR_RIGHT 0
+#define PANEL_DISPLAY_CTRL_HORIZONTAL_PAN_DIR_LEFT 1
+#define PANEL_DISPLAY_CTRL_HORIZONTAL_PAN 4:4
+#define PANEL_DISPLAY_CTRL_HORIZONTAL_PAN_DISABLE 0
+#define PANEL_DISPLAY_CTRL_HORIZONTAL_PAN_ENABLE 1
+#define PANEL_DISPLAY_CTRL_GAMMA 3:3
+#define PANEL_DISPLAY_CTRL_GAMMA_DISABLE 0
+#define PANEL_DISPLAY_CTRL_GAMMA_ENABLE 1
+#define PANEL_DISPLAY_CTRL_PLANE 2:2
+#define PANEL_DISPLAY_CTRL_PLANE_DISABLE 0
+#define PANEL_DISPLAY_CTRL_PLANE_ENABLE 1
+#define PANEL_DISPLAY_CTRL_FORMAT 1:0
+#define PANEL_DISPLAY_CTRL_FORMAT_8 0
+#define PANEL_DISPLAY_CTRL_FORMAT_16 1
+#define PANEL_DISPLAY_CTRL_FORMAT_32 2
+
+#define PANEL_PAN_CTRL 0x080004
+#define PANEL_PAN_CTRL_VERTICAL_PAN 31:24
+#define PANEL_PAN_CTRL_VERTICAL_VSYNC 21:16
+#define PANEL_PAN_CTRL_HORIZONTAL_PAN 15:8
+#define PANEL_PAN_CTRL_HORIZONTAL_VSYNC 5:0
+
+#define PANEL_COLOR_KEY 0x080008
+#define PANEL_COLOR_KEY_MASK 31:16
+#define PANEL_COLOR_KEY_VALUE 15:0
+
+#define PANEL_FB_ADDRESS 0x08000C
+#define PANEL_FB_ADDRESS_STATUS 31:31
+#define PANEL_FB_ADDRESS_STATUS_CURRENT 0
+#define PANEL_FB_ADDRESS_STATUS_PENDING 1
+#define PANEL_FB_ADDRESS_EXT 27:27
+#define PANEL_FB_ADDRESS_EXT_LOCAL 0
+#define PANEL_FB_ADDRESS_EXT_EXTERNAL 1
+#define PANEL_FB_ADDRESS_ADDRESS 25:0
+
+#define PANEL_FB_WIDTH 0x080010
+#define PANEL_FB_WIDTH_WIDTH 29:16
+#define PANEL_FB_WIDTH_OFFSET 13:0
+
+#define PANEL_WINDOW_WIDTH 0x080014
+#define PANEL_WINDOW_WIDTH_WIDTH 27:16
+#define PANEL_WINDOW_WIDTH_X 11:0
+
+#define PANEL_WINDOW_HEIGHT 0x080018
+#define PANEL_WINDOW_HEIGHT_HEIGHT 27:16
+#define PANEL_WINDOW_HEIGHT_Y 11:0
+
+#define PANEL_PLANE_TL 0x08001C
+#define PANEL_PLANE_TL_TOP 26:16
+#define PANEL_PLANE_TL_LEFT 10:0
+
+#define PANEL_PLANE_BR 0x080020
+#define PANEL_PLANE_BR_BOTTOM 26:16
+#define PANEL_PLANE_BR_RIGHT 10:0
+
+#define PANEL_HORIZONTAL_TOTAL 0x080024
+#define PANEL_HORIZONTAL_TOTAL_TOTAL 27:16
+#define PANEL_HORIZONTAL_TOTAL_DISPLAY_END 11:0
+
+#define PANEL_HORIZONTAL_SYNC 0x080028
+#define PANEL_HORIZONTAL_SYNC_WIDTH 23:16
+#define PANEL_HORIZONTAL_SYNC_START 11:0
+
+#define PANEL_VERTICAL_TOTAL 0x08002C
+#define PANEL_VERTICAL_TOTAL_TOTAL 26:16
+#define PANEL_VERTICAL_TOTAL_DISPLAY_END 10:0
+
+#define PANEL_VERTICAL_SYNC 0x080030
+#define PANEL_VERTICAL_SYNC_HEIGHT 21:16
+#define PANEL_VERTICAL_SYNC_START 10:0
+
+#define PANEL_CURRENT_LINE 0x080034
+#define PANEL_CURRENT_LINE_LINE 10:0
+
+/* Video Control */
+
+#define VIDEO_DISPLAY_CTRL 0x080040
+#define VIDEO_DISPLAY_CTRL_LINE_BUFFER 18:18
+#define VIDEO_DISPLAY_CTRL_LINE_BUFFER_DISABLE 0
+#define VIDEO_DISPLAY_CTRL_LINE_BUFFER_ENABLE 1
+#define VIDEO_DISPLAY_CTRL_FIFO 17:16
+#define VIDEO_DISPLAY_CTRL_FIFO_1 0
+#define VIDEO_DISPLAY_CTRL_FIFO_3 1
+#define VIDEO_DISPLAY_CTRL_FIFO_7 2
+#define VIDEO_DISPLAY_CTRL_FIFO_11 3
+#define VIDEO_DISPLAY_CTRL_BUFFER 15:15
+#define VIDEO_DISPLAY_CTRL_BUFFER_0 0
+#define VIDEO_DISPLAY_CTRL_BUFFER_1 1
+#define VIDEO_DISPLAY_CTRL_CAPTURE 14:14
+#define VIDEO_DISPLAY_CTRL_CAPTURE_DISABLE 0
+#define VIDEO_DISPLAY_CTRL_CAPTURE_ENABLE 1
+#define VIDEO_DISPLAY_CTRL_DOUBLE_BUFFER 13:13
+#define VIDEO_DISPLAY_CTRL_DOUBLE_BUFFER_DISABLE 0
+#define VIDEO_DISPLAY_CTRL_DOUBLE_BUFFER_ENABLE 1
+#define VIDEO_DISPLAY_CTRL_BYTE_SWAP 12:12
+#define VIDEO_DISPLAY_CTRL_BYTE_SWAP_DISABLE 0
+#define VIDEO_DISPLAY_CTRL_BYTE_SWAP_ENABLE 1
+#define VIDEO_DISPLAY_CTRL_VERTICAL_SCALE 11:11
+#define VIDEO_DISPLAY_CTRL_VERTICAL_SCALE_NORMAL 0
+#define VIDEO_DISPLAY_CTRL_VERTICAL_SCALE_HALF 1
+#define VIDEO_DISPLAY_CTRL_HORIZONTAL_SCALE 10:10
+#define VIDEO_DISPLAY_CTRL_HORIZONTAL_SCALE_NORMAL 0
+#define VIDEO_DISPLAY_CTRL_HORIZONTAL_SCALE_HALF 1
+#define VIDEO_DISPLAY_CTRL_VERTICAL_MODE 9:9
+#define VIDEO_DISPLAY_CTRL_VERTICAL_MODE_REPLICATE 0
+#define VIDEO_DISPLAY_CTRL_VERTICAL_MODE_INTERPOLATE 1
+#define VIDEO_DISPLAY_CTRL_HORIZONTAL_MODE 8:8
+#define VIDEO_DISPLAY_CTRL_HORIZONTAL_MODE_REPLICATE 0
+#define VIDEO_DISPLAY_CTRL_HORIZONTAL_MODE_INTERPOLATE 1
+#define VIDEO_DISPLAY_CTRL_PIXEL 7:4
+#define VIDEO_DISPLAY_CTRL_GAMMA 3:3
+#define VIDEO_DISPLAY_CTRL_GAMMA_DISABLE 0
+#define VIDEO_DISPLAY_CTRL_GAMMA_ENABLE 1
+#define VIDEO_DISPLAY_CTRL_PLANE 2:2
+#define VIDEO_DISPLAY_CTRL_PLANE_DISABLE 0
+#define VIDEO_DISPLAY_CTRL_PLANE_ENABLE 1
+#define VIDEO_DISPLAY_CTRL_FORMAT 1:0
+#define VIDEO_DISPLAY_CTRL_FORMAT_8 0
+#define VIDEO_DISPLAY_CTRL_FORMAT_16 1
+#define VIDEO_DISPLAY_CTRL_FORMAT_32 2
+#define VIDEO_DISPLAY_CTRL_FORMAT_YUV 3
+
+#define VIDEO_FB_0_ADDRESS 0x080044
+#define VIDEO_FB_0_ADDRESS_STATUS 31:31
+#define VIDEO_FB_0_ADDRESS_STATUS_CURRENT 0
+#define VIDEO_FB_0_ADDRESS_STATUS_PENDING 1
+#define VIDEO_FB_0_ADDRESS_EXT 27:27
+#define VIDEO_FB_0_ADDRESS_EXT_LOCAL 0
+#define VIDEO_FB_0_ADDRESS_EXT_EXTERNAL 1
+#define VIDEO_FB_0_ADDRESS_ADDRESS 25:0
+
+#define VIDEO_FB_WIDTH 0x080048
+#define VIDEO_FB_WIDTH_WIDTH 29:16
+#define VIDEO_FB_WIDTH_OFFSET 13:0
+
+#define VIDEO_FB_0_LAST_ADDRESS 0x08004C
+#define VIDEO_FB_0_LAST_ADDRESS_EXT 27:27
+#define VIDEO_FB_0_LAST_ADDRESS_EXT_LOCAL 0
+#define VIDEO_FB_0_LAST_ADDRESS_EXT_EXTERNAL 1
+#define VIDEO_FB_0_LAST_ADDRESS_ADDRESS 25:0
+
+#define VIDEO_PLANE_TL 0x080050
+#define VIDEO_PLANE_TL_TOP 26:16
+#define VIDEO_PLANE_TL_LEFT 10:0
+
+#define VIDEO_PLANE_BR 0x080054
+#define VIDEO_PLANE_BR_BOTTOM 26:16
+#define VIDEO_PLANE_BR_RIGHT 10:0
+
+#define VIDEO_SCALE 0x080058
+#define VIDEO_SCALE_VERTICAL_MODE 31:31
+#define VIDEO_SCALE_VERTICAL_MODE_EXPAND 0
+#define VIDEO_SCALE_VERTICAL_MODE_SHRINK 1
+#define VIDEO_SCALE_VERTICAL_SCALE 27:16
+#define VIDEO_SCALE_HORIZONTAL_MODE 15:15
+#define VIDEO_SCALE_HORIZONTAL_MODE_EXPAND 0
+#define VIDEO_SCALE_HORIZONTAL_MODE_SHRINK 1
+#define VIDEO_SCALE_HORIZONTAL_SCALE 11:0
+
+#define VIDEO_INITIAL_SCALE 0x08005C
+#define VIDEO_INITIAL_SCALE_FB_1 27:16
+#define VIDEO_INITIAL_SCALE_FB_0 11:0
+
+#define VIDEO_YUV_CONSTANTS 0x080060
+#define VIDEO_YUV_CONSTANTS_Y 31:24
+#define VIDEO_YUV_CONSTANTS_R 23:16
+#define VIDEO_YUV_CONSTANTS_G 15:8
+#define VIDEO_YUV_CONSTANTS_B 7:0
+
+#define VIDEO_FB_1_ADDRESS 0x080064
+#define VIDEO_FB_1_ADDRESS_STATUS 31:31
+#define VIDEO_FB_1_ADDRESS_STATUS_CURRENT 0
+#define VIDEO_FB_1_ADDRESS_STATUS_PENDING 1
+#define VIDEO_FB_1_ADDRESS_EXT 27:27
+#define VIDEO_FB_1_ADDRESS_EXT_LOCAL 0
+#define VIDEO_FB_1_ADDRESS_EXT_EXTERNAL 1
+#define VIDEO_FB_1_ADDRESS_ADDRESS 25:0
+
+#define VIDEO_FB_1_LAST_ADDRESS 0x080068
+#define VIDEO_FB_1_LAST_ADDRESS_EXT 27:27
+#define VIDEO_FB_1_LAST_ADDRESS_EXT_LOCAL 0
+#define VIDEO_FB_1_LAST_ADDRESS_EXT_EXTERNAL 1
+#define VIDEO_FB_1_LAST_ADDRESS_ADDRESS 25:0
+
+/* Video Alpha Control */
+
+#define VIDEO_ALPHA_DISPLAY_CTRL 0x080080
+#define VIDEO_ALPHA_DISPLAY_CTRL_SELECT 28:28
+#define VIDEO_ALPHA_DISPLAY_CTRL_SELECT_PER_PIXEL 0
+#define VIDEO_ALPHA_DISPLAY_CTRL_SELECT_ALPHA 1
+#define VIDEO_ALPHA_DISPLAY_CTRL_ALPHA 27:24
+#define VIDEO_ALPHA_DISPLAY_CTRL_FIFO 17:16
+#define VIDEO_ALPHA_DISPLAY_CTRL_FIFO_1 0
+#define VIDEO_ALPHA_DISPLAY_CTRL_FIFO_3 1
+#define VIDEO_ALPHA_DISPLAY_CTRL_FIFO_7 2
+#define VIDEO_ALPHA_DISPLAY_CTRL_FIFO_11 3
+#define VIDEO_ALPHA_DISPLAY_CTRL_VERT_SCALE 11:11
+#define VIDEO_ALPHA_DISPLAY_CTRL_VERT_SCALE_NORMAL 0
+#define VIDEO_ALPHA_DISPLAY_CTRL_VERT_SCALE_HALF 1
+#define VIDEO_ALPHA_DISPLAY_CTRL_HORZ_SCALE 10:10
+#define VIDEO_ALPHA_DISPLAY_CTRL_HORZ_SCALE_NORMAL 0
+#define VIDEO_ALPHA_DISPLAY_CTRL_HORZ_SCALE_HALF 1
+#define VIDEO_ALPHA_DISPLAY_CTRL_VERT_MODE 9:9
+#define VIDEO_ALPHA_DISPLAY_CTRL_VERT_MODE_REPLICATE 0
+#define VIDEO_ALPHA_DISPLAY_CTRL_VERT_MODE_INTERPOLATE 1
+#define VIDEO_ALPHA_DISPLAY_CTRL_HORZ_MODE 8:8
+#define VIDEO_ALPHA_DISPLAY_CTRL_HORZ_MODE_REPLICATE 0
+#define VIDEO_ALPHA_DISPLAY_CTRL_HORZ_MODE_INTERPOLATE 1
+#define VIDEO_ALPHA_DISPLAY_CTRL_PIXEL 7:4
+#define VIDEO_ALPHA_DISPLAY_CTRL_CHROMA_KEY 3:3
+#define VIDEO_ALPHA_DISPLAY_CTRL_CHROMA_KEY_DISABLE 0
+#define VIDEO_ALPHA_DISPLAY_CTRL_CHROMA_KEY_ENABLE 1
+#define VIDEO_ALPHA_DISPLAY_CTRL_PLANE 2:2
+#define VIDEO_ALPHA_DISPLAY_CTRL_PLANE_DISABLE 0
+#define VIDEO_ALPHA_DISPLAY_CTRL_PLANE_ENABLE 1
+#define VIDEO_ALPHA_DISPLAY_CTRL_FORMAT 1:0
+#define VIDEO_ALPHA_DISPLAY_CTRL_FORMAT_8 0
+#define VIDEO_ALPHA_DISPLAY_CTRL_FORMAT_16 1
+#define VIDEO_ALPHA_DISPLAY_CTRL_FORMAT_ALPHA_4_4 2
+#define VIDEO_ALPHA_DISPLAY_CTRL_FORMAT_ALPHA_4_4_4_4 3
+
+#define VIDEO_ALPHA_FB_ADDRESS 0x080084
+#define VIDEO_ALPHA_FB_ADDRESS_STATUS 31:31
+#define VIDEO_ALPHA_FB_ADDRESS_STATUS_CURRENT 0
+#define VIDEO_ALPHA_FB_ADDRESS_STATUS_PENDING 1
+#define VIDEO_ALPHA_FB_ADDRESS_EXT 27:27
+#define VIDEO_ALPHA_FB_ADDRESS_EXT_LOCAL 0
+#define VIDEO_ALPHA_FB_ADDRESS_EXT_EXTERNAL 1
+#define VIDEO_ALPHA_FB_ADDRESS_ADDRESS 25:0
+
+#define VIDEO_ALPHA_FB_WIDTH 0x080088
+#define VIDEO_ALPHA_FB_WIDTH_WIDTH 29:16
+#define VIDEO_ALPHA_FB_WIDTH_OFFSET 13:0
+
+#define VIDEO_ALPHA_FB_LAST_ADDRESS 0x08008C
+#define VIDEO_ALPHA_FB_LAST_ADDRESS_EXT 27:27
+#define VIDEO_ALPHA_FB_LAST_ADDRESS_EXT_LOCAL 0
+#define VIDEO_ALPHA_FB_LAST_ADDRESS_EXT_EXTERNAL 1
+#define VIDEO_ALPHA_FB_LAST_ADDRESS_ADDRESS 25:0
+
+#define VIDEO_ALPHA_PLANE_TL 0x080090
+#define VIDEO_ALPHA_PLANE_TL_TOP 26:16
+#define VIDEO_ALPHA_PLANE_TL_LEFT 10:0
+
+#define VIDEO_ALPHA_PLANE_BR 0x080094
+#define VIDEO_ALPHA_PLANE_BR_BOTTOM 26:16
+#define VIDEO_ALPHA_PLANE_BR_RIGHT 10:0
+
+#define VIDEO_ALPHA_SCALE 0x080098
+#define VIDEO_ALPHA_SCALE_VERTICAL_MODE 31:31
+#define VIDEO_ALPHA_SCALE_VERTICAL_MODE_EXPAND 0
+#define VIDEO_ALPHA_SCALE_VERTICAL_MODE_SHRINK 1
+#define VIDEO_ALPHA_SCALE_VERTICAL_SCALE 27:16
+#define VIDEO_ALPHA_SCALE_HORIZONTAL_MODE 15:15
+#define VIDEO_ALPHA_SCALE_HORIZONTAL_MODE_EXPAND 0
+#define VIDEO_ALPHA_SCALE_HORIZONTAL_MODE_SHRINK 1
+#define VIDEO_ALPHA_SCALE_HORIZONTAL_SCALE 11:0
+
+#define VIDEO_ALPHA_INITIAL_SCALE 0x08009C
+#define VIDEO_ALPHA_INITIAL_SCALE_VERTICAL 27:16
+#define VIDEO_ALPHA_INITIAL_SCALE_HORIZONTAL 11:0
+
+#define VIDEO_ALPHA_CHROMA_KEY 0x0800A0
+#define VIDEO_ALPHA_CHROMA_KEY_MASK 31:16
+#define VIDEO_ALPHA_CHROMA_KEY_VALUE 15:0
+
+#define VIDEO_ALPHA_COLOR_LOOKUP_01 0x0800A4
+#define VIDEO_ALPHA_COLOR_LOOKUP_01_1 31:16
+#define VIDEO_ALPHA_COLOR_LOOKUP_01_1_RED 31:27
+#define VIDEO_ALPHA_COLOR_LOOKUP_01_1_GREEN 26:21
+#define VIDEO_ALPHA_COLOR_LOOKUP_01_1_BLUE 20:16
+#define VIDEO_ALPHA_COLOR_LOOKUP_01_0 15:0
+#define VIDEO_ALPHA_COLOR_LOOKUP_01_0_RED 15:11
+#define VIDEO_ALPHA_COLOR_LOOKUP_01_0_GREEN 10:5
+#define VIDEO_ALPHA_COLOR_LOOKUP_01_0_BLUE 4:0
+
+#define VIDEO_ALPHA_COLOR_LOOKUP_23 0x0800A8
+#define VIDEO_ALPHA_COLOR_LOOKUP_23_3 31:16
+#define VIDEO_ALPHA_COLOR_LOOKUP_23_3_RED 31:27
+#define VIDEO_ALPHA_COLOR_LOOKUP_23_3_GREEN 26:21
+#define VIDEO_ALPHA_COLOR_LOOKUP_23_3_BLUE 20:16
+#define VIDEO_ALPHA_COLOR_LOOKUP_23_2 15:0
+#define VIDEO_ALPHA_COLOR_LOOKUP_23_2_RED 15:11
+#define VIDEO_ALPHA_COLOR_LOOKUP_23_2_GREEN 10:5
+#define VIDEO_ALPHA_COLOR_LOOKUP_23_2_BLUE 4:0
+
+#define VIDEO_ALPHA_COLOR_LOOKUP_45 0x0800AC
+#define VIDEO_ALPHA_COLOR_LOOKUP_45_5 31:16
+#define VIDEO_ALPHA_COLOR_LOOKUP_45_5_RED 31:27
+#define VIDEO_ALPHA_COLOR_LOOKUP_45_5_GREEN 26:21
+#define VIDEO_ALPHA_COLOR_LOOKUP_45_5_BLUE 20:16
+#define VIDEO_ALPHA_COLOR_LOOKUP_45_4 15:0
+#define VIDEO_ALPHA_COLOR_LOOKUP_45_4_RED 15:11
+#define VIDEO_ALPHA_COLOR_LOOKUP_45_4_GREEN 10:5
+#define VIDEO_ALPHA_COLOR_LOOKUP_45_4_BLUE 4:0
+
+#define VIDEO_ALPHA_COLOR_LOOKUP_67 0x0800B0
+#define VIDEO_ALPHA_COLOR_LOOKUP_67_7 31:16
+#define VIDEO_ALPHA_COLOR_LOOKUP_67_7_RED 31:27
+#define VIDEO_ALPHA_COLOR_LOOKUP_67_7_GREEN 26:21
+#define VIDEO_ALPHA_COLOR_LOOKUP_67_7_BLUE 20:16
+#define VIDEO_ALPHA_COLOR_LOOKUP_67_6 15:0
+#define VIDEO_ALPHA_COLOR_LOOKUP_67_6_RED 15:11
+#define VIDEO_ALPHA_COLOR_LOOKUP_67_6_GREEN 10:5
+#define VIDEO_ALPHA_COLOR_LOOKUP_67_6_BLUE 4:0
+
+#define VIDEO_ALPHA_COLOR_LOOKUP_89 0x0800B4
+#define VIDEO_ALPHA_COLOR_LOOKUP_89_9 31:16
+#define VIDEO_ALPHA_COLOR_LOOKUP_89_9_RED 31:27
+#define VIDEO_ALPHA_COLOR_LOOKUP_89_9_GREEN 26:21
+#define VIDEO_ALPHA_COLOR_LOOKUP_89_9_BLUE 20:16
+#define VIDEO_ALPHA_COLOR_LOOKUP_89_8 15:0
+#define VIDEO_ALPHA_COLOR_LOOKUP_89_8_RED 15:11
+#define VIDEO_ALPHA_COLOR_LOOKUP_89_8_GREEN 10:5
+#define VIDEO_ALPHA_COLOR_LOOKUP_89_8_BLUE 4:0
+
+#define VIDEO_ALPHA_COLOR_LOOKUP_AB 0x0800B8
+#define VIDEO_ALPHA_COLOR_LOOKUP_AB_B 31:16
+#define VIDEO_ALPHA_COLOR_LOOKUP_AB_B_RED 31:27
+#define VIDEO_ALPHA_COLOR_LOOKUP_AB_B_GREEN 26:21
+#define VIDEO_ALPHA_COLOR_LOOKUP_AB_B_BLUE 20:16
+#define VIDEO_ALPHA_COLOR_LOOKUP_AB_A 15:0
+#define VIDEO_ALPHA_COLOR_LOOKUP_AB_A_RED 15:11
+#define VIDEO_ALPHA_COLOR_LOOKUP_AB_A_GREEN 10:5
+#define VIDEO_ALPHA_COLOR_LOOKUP_AB_A_BLUE 4:0
+
+#define VIDEO_ALPHA_COLOR_LOOKUP_CD 0x0800BC
+#define VIDEO_ALPHA_COLOR_LOOKUP_CD_D 31:16
+#define VIDEO_ALPHA_COLOR_LOOKUP_CD_D_RED 31:27
+#define VIDEO_ALPHA_COLOR_LOOKUP_CD_D_GREEN 26:21
+#define VIDEO_ALPHA_COLOR_LOOKUP_CD_D_BLUE 20:16
+#define VIDEO_ALPHA_COLOR_LOOKUP_CD_C 15:0
+#define VIDEO_ALPHA_COLOR_LOOKUP_CD_C_RED 15:11
+#define VIDEO_ALPHA_COLOR_LOOKUP_CD_C_GREEN 10:5
+#define VIDEO_ALPHA_COLOR_LOOKUP_CD_C_BLUE 4:0
+
+#define VIDEO_ALPHA_COLOR_LOOKUP_EF 0x0800C0
+#define VIDEO_ALPHA_COLOR_LOOKUP_EF_F 31:16
+#define VIDEO_ALPHA_COLOR_LOOKUP_EF_F_RED 31:27
+#define VIDEO_ALPHA_COLOR_LOOKUP_EF_F_GREEN 26:21
+#define VIDEO_ALPHA_COLOR_LOOKUP_EF_F_BLUE 20:16
+#define VIDEO_ALPHA_COLOR_LOOKUP_EF_E 15:0
+#define VIDEO_ALPHA_COLOR_LOOKUP_EF_E_RED 15:11
+#define VIDEO_ALPHA_COLOR_LOOKUP_EF_E_GREEN 10:5
+#define VIDEO_ALPHA_COLOR_LOOKUP_EF_E_BLUE 4:0
+
+/* Panel Cursor Control */
+
+#define PANEL_HWC_ADDRESS 0x0800F0
+#define PANEL_HWC_ADDRESS_ENABLE 31:31
+#define PANEL_HWC_ADDRESS_ENABLE_DISABLE 0
+#define PANEL_HWC_ADDRESS_ENABLE_ENABLE 1
+#define PANEL_HWC_ADDRESS_EXT 27:27
+#define PANEL_HWC_ADDRESS_EXT_LOCAL 0
+#define PANEL_HWC_ADDRESS_EXT_EXTERNAL 1
+#define PANEL_HWC_ADDRESS_ADDRESS 25:0
+
+#define PANEL_HWC_LOCATION 0x0800F4
+#define PANEL_HWC_LOCATION_TOP 27:27
+#define PANEL_HWC_LOCATION_TOP_INSIDE 0
+#define PANEL_HWC_LOCATION_TOP_OUTSIDE 1
+#define PANEL_HWC_LOCATION_Y 26:16
+#define PANEL_HWC_LOCATION_LEFT 11:11
+#define PANEL_HWC_LOCATION_LEFT_INSIDE 0
+#define PANEL_HWC_LOCATION_LEFT_OUTSIDE 1
+#define PANEL_HWC_LOCATION_X 10:0
+
+#define PANEL_HWC_COLOR_12 0x0800F8
+#define PANEL_HWC_COLOR_12_2_RGB565 31:16
+#define PANEL_HWC_COLOR_12_1_RGB565 15:0
+
+#define PANEL_HWC_COLOR_3 0x0800FC
+#define PANEL_HWC_COLOR_3_RGB565 15:0
+
+/* Old Definitions +++ */
+#define PANEL_HWC_COLOR_01 0x0800F8
+#define PANEL_HWC_COLOR_01_1_RED 31:27
+#define PANEL_HWC_COLOR_01_1_GREEN 26:21
+#define PANEL_HWC_COLOR_01_1_BLUE 20:16
+#define PANEL_HWC_COLOR_01_0_RED 15:11
+#define PANEL_HWC_COLOR_01_0_GREEN 10:5
+#define PANEL_HWC_COLOR_01_0_BLUE 4:0
+
+#define PANEL_HWC_COLOR_2 0x0800FC
+#define PANEL_HWC_COLOR_2_RED 15:11
+#define PANEL_HWC_COLOR_2_GREEN 10:5
+#define PANEL_HWC_COLOR_2_BLUE 4:0
+/* Old Definitions --- */
+
+/* Alpha Control */
+
+#define ALPHA_DISPLAY_CTRL 0x080100
+#define ALPHA_DISPLAY_CTRL_SELECT 28:28
+#define ALPHA_DISPLAY_CTRL_SELECT_PER_PIXEL 0
+#define ALPHA_DISPLAY_CTRL_SELECT_ALPHA 1
+#define ALPHA_DISPLAY_CTRL_ALPHA 27:24
+#define ALPHA_DISPLAY_CTRL_FIFO 17:16
+#define ALPHA_DISPLAY_CTRL_FIFO_1 0
+#define ALPHA_DISPLAY_CTRL_FIFO_3 1
+#define ALPHA_DISPLAY_CTRL_FIFO_7 2
+#define ALPHA_DISPLAY_CTRL_FIFO_11 3
+#define ALPHA_DISPLAY_CTRL_PIXEL 7:4
+#define ALPHA_DISPLAY_CTRL_CHROMA_KEY 3:3
+#define ALPHA_DISPLAY_CTRL_CHROMA_KEY_DISABLE 0
+#define ALPHA_DISPLAY_CTRL_CHROMA_KEY_ENABLE 1
+#define ALPHA_DISPLAY_CTRL_PLANE 2:2
+#define ALPHA_DISPLAY_CTRL_PLANE_DISABLE 0
+#define ALPHA_DISPLAY_CTRL_PLANE_ENABLE 1
+#define ALPHA_DISPLAY_CTRL_FORMAT 1:0
+#define ALPHA_DISPLAY_CTRL_FORMAT_16 1
+#define ALPHA_DISPLAY_CTRL_FORMAT_ALPHA_4_4 2
+#define ALPHA_DISPLAY_CTRL_FORMAT_ALPHA_4_4_4_4 3
+
+#define ALPHA_FB_ADDRESS 0x080104
+#define ALPHA_FB_ADDRESS_STATUS 31:31
+#define ALPHA_FB_ADDRESS_STATUS_CURRENT 0
+#define ALPHA_FB_ADDRESS_STATUS_PENDING 1
+#define ALPHA_FB_ADDRESS_EXT 27:27
+#define ALPHA_FB_ADDRESS_EXT_LOCAL 0
+#define ALPHA_FB_ADDRESS_EXT_EXTERNAL 1
+#define ALPHA_FB_ADDRESS_ADDRESS 25:0
+
+#define ALPHA_FB_WIDTH 0x080108
+#define ALPHA_FB_WIDTH_WIDTH 29:16
+#define ALPHA_FB_WIDTH_OFFSET 13:0
+
+#define ALPHA_PLANE_TL 0x08010C
+#define ALPHA_PLANE_TL_TOP 26:16
+#define ALPHA_PLANE_TL_LEFT 10:0
+
+#define ALPHA_PLANE_BR 0x080110
+#define ALPHA_PLANE_BR_BOTTOM 26:16
+#define ALPHA_PLANE_BR_RIGHT 10:0
+
+#define ALPHA_CHROMA_KEY 0x080114
+#define ALPHA_CHROMA_KEY_MASK 31:16
+#define ALPHA_CHROMA_KEY_VALUE 15:0
+
+#define ALPHA_COLOR_LOOKUP_01 0x080118
+#define ALPHA_COLOR_LOOKUP_01_1 31:16
+#define ALPHA_COLOR_LOOKUP_01_1_RED 31:27
+#define ALPHA_COLOR_LOOKUP_01_1_GREEN 26:21
+#define ALPHA_COLOR_LOOKUP_01_1_BLUE 20:16
+#define ALPHA_COLOR_LOOKUP_01_0 15:0
+#define ALPHA_COLOR_LOOKUP_01_0_RED 15:11
+#define ALPHA_COLOR_LOOKUP_01_0_GREEN 10:5
+#define ALPHA_COLOR_LOOKUP_01_0_BLUE 4:0
+
+#define ALPHA_COLOR_LOOKUP_23 0x08011C
+#define ALPHA_COLOR_LOOKUP_23_3 31:16
+#define ALPHA_COLOR_LOOKUP_23_3_RED 31:27
+#define ALPHA_COLOR_LOOKUP_23_3_GREEN 26:21
+#define ALPHA_COLOR_LOOKUP_23_3_BLUE 20:16
+#define ALPHA_COLOR_LOOKUP_23_2 15:0
+#define ALPHA_COLOR_LOOKUP_23_2_RED 15:11
+#define ALPHA_COLOR_LOOKUP_23_2_GREEN 10:5
+#define ALPHA_COLOR_LOOKUP_23_2_BLUE 4:0
+
+#define ALPHA_COLOR_LOOKUP_45 0x080120
+#define ALPHA_COLOR_LOOKUP_45_5 31:16
+#define ALPHA_COLOR_LOOKUP_45_5_RED 31:27
+#define ALPHA_COLOR_LOOKUP_45_5_GREEN 26:21
+#define ALPHA_COLOR_LOOKUP_45_5_BLUE 20:16
+#define ALPHA_COLOR_LOOKUP_45_4 15:0
+#define ALPHA_COLOR_LOOKUP_45_4_RED 15:11
+#define ALPHA_COLOR_LOOKUP_45_4_GREEN 10:5
+#define ALPHA_COLOR_LOOKUP_45_4_BLUE 4:0
+
+#define ALPHA_COLOR_LOOKUP_67 0x080124
+#define ALPHA_COLOR_LOOKUP_67_7 31:16
+#define ALPHA_COLOR_LOOKUP_67_7_RED 31:27
+#define ALPHA_COLOR_LOOKUP_67_7_GREEN 26:21
+#define ALPHA_COLOR_LOOKUP_67_7_BLUE 20:16
+#define ALPHA_COLOR_LOOKUP_67_6 15:0
+#define ALPHA_COLOR_LOOKUP_67_6_RED 15:11
+#define ALPHA_COLOR_LOOKUP_67_6_GREEN 10:5
+#define ALPHA_COLOR_LOOKUP_67_6_BLUE 4:0
+
+#define ALPHA_COLOR_LOOKUP_89 0x080128
+#define ALPHA_COLOR_LOOKUP_89_9 31:16
+#define ALPHA_COLOR_LOOKUP_89_9_RED 31:27
+#define ALPHA_COLOR_LOOKUP_89_9_GREEN 26:21
+#define ALPHA_COLOR_LOOKUP_89_9_BLUE 20:16
+#define ALPHA_COLOR_LOOKUP_89_8 15:0
+#define ALPHA_COLOR_LOOKUP_89_8_RED 15:11
+#define ALPHA_COLOR_LOOKUP_89_8_GREEN 10:5
+#define ALPHA_COLOR_LOOKUP_89_8_BLUE 4:0
+
+#define ALPHA_COLOR_LOOKUP_AB 0x08012C
+#define ALPHA_COLOR_LOOKUP_AB_B 31:16
+#define ALPHA_COLOR_LOOKUP_AB_B_RED 31:27
+#define ALPHA_COLOR_LOOKUP_AB_B_GREEN 26:21
+#define ALPHA_COLOR_LOOKUP_AB_B_BLUE 20:16
+#define ALPHA_COLOR_LOOKUP_AB_A 15:0
+#define ALPHA_COLOR_LOOKUP_AB_A_RED 15:11
+#define ALPHA_COLOR_LOOKUP_AB_A_GREEN 10:5
+#define ALPHA_COLOR_LOOKUP_AB_A_BLUE 4:0
+
+#define ALPHA_COLOR_LOOKUP_CD 0x080130
+#define ALPHA_COLOR_LOOKUP_CD_D 31:16
+#define ALPHA_COLOR_LOOKUP_CD_D_RED 31:27
+#define ALPHA_COLOR_LOOKUP_CD_D_GREEN 26:21
+#define ALPHA_COLOR_LOOKUP_CD_D_BLUE 20:16
+#define ALPHA_COLOR_LOOKUP_CD_C 15:0
+#define ALPHA_COLOR_LOOKUP_CD_C_RED 15:11
+#define ALPHA_COLOR_LOOKUP_CD_C_GREEN 10:5
+#define ALPHA_COLOR_LOOKUP_CD_C_BLUE 4:0
+
+#define ALPHA_COLOR_LOOKUP_EF 0x080134
+#define ALPHA_COLOR_LOOKUP_EF_F 31:16
+#define ALPHA_COLOR_LOOKUP_EF_F_RED 31:27
+#define ALPHA_COLOR_LOOKUP_EF_F_GREEN 26:21
+#define ALPHA_COLOR_LOOKUP_EF_F_BLUE 20:16
+#define ALPHA_COLOR_LOOKUP_EF_E 15:0
+#define ALPHA_COLOR_LOOKUP_EF_E_RED 15:11
+#define ALPHA_COLOR_LOOKUP_EF_E_GREEN 10:5
+#define ALPHA_COLOR_LOOKUP_EF_E_BLUE 4:0
+
+/* CRT Graphics Control */
+
+#define CRT_DISPLAY_CTRL 0x080200
+#define CRT_DISPLAY_CTRL_RESERVED_1_MASK 31:27
+#define CRT_DISPLAY_CTRL_RESERVED_1_MASK_DISABLE 0
+#define CRT_DISPLAY_CTRL_RESERVED_1_MASK_ENABLE 0x1F
+
+/* SM750LE definition */
+#define CRT_DISPLAY_CTRL_DPMS 31:30
+#define CRT_DISPLAY_CTRL_DPMS_0 0
+#define CRT_DISPLAY_CTRL_DPMS_1 1
+#define CRT_DISPLAY_CTRL_DPMS_2 2
+#define CRT_DISPLAY_CTRL_DPMS_3 3
+#define CRT_DISPLAY_CTRL_CLK 29:27
+#define CRT_DISPLAY_CTRL_CLK_PLL25 0
+#define CRT_DISPLAY_CTRL_CLK_PLL41 1
+#define CRT_DISPLAY_CTRL_CLK_PLL62 2
+#define CRT_DISPLAY_CTRL_CLK_PLL65 3
+#define CRT_DISPLAY_CTRL_CLK_PLL74 4
+#define CRT_DISPLAY_CTRL_CLK_PLL80 5
+#define CRT_DISPLAY_CTRL_CLK_PLL108 6
+#define CRT_DISPLAY_CTRL_CLK_RESERVED 7
+#define CRT_DISPLAY_CTRL_SHIFT_VGA_DAC 26:26
+#define CRT_DISPLAY_CTRL_SHIFT_VGA_DAC_DISABLE 1
+#define CRT_DISPLAY_CTRL_SHIFT_VGA_DAC_ENABLE 0
+
+
+#define CRT_DISPLAY_CTRL_RESERVED_2_MASK 25:24
+#define CRT_DISPLAY_CTRL_RESERVED_2_MASK_ENABLE 3
+#define CRT_DISPLAY_CTRL_RESERVED_2_MASK_DISABLE 0
+
+/* SM750LE definition */
+#define CRT_DISPLAY_CTRL_CRTSELECT 25:25
+#define CRT_DISPLAY_CTRL_CRTSELECT_VGA 0
+#define CRT_DISPLAY_CTRL_CRTSELECT_CRT 1
+#define CRT_DISPLAY_CTRL_RGBBIT 24:24
+#define CRT_DISPLAY_CTRL_RGBBIT_24BIT 0
+#define CRT_DISPLAY_CTRL_RGBBIT_12BIT 1
+
+
+#define CRT_DISPLAY_CTRL_RESERVED_3_MASK 15:15
+#define CRT_DISPLAY_CTRL_RESERVED_3_MASK_DISABLE 0
+#define CRT_DISPLAY_CTRL_RESERVED_3_MASK_ENABLE 1
+
+#define CRT_DISPLAY_CTRL_RESERVED_4_MASK 9:9
+#define CRT_DISPLAY_CTRL_RESERVED_4_MASK_DISABLE 0
+#define CRT_DISPLAY_CTRL_RESERVED_4_MASK_ENABLE 1
+
+#ifndef VALIDATION_CHIP
+ #define CRT_DISPLAY_CTRL_SHIFT_VGA_DAC 26:26
+ #define CRT_DISPLAY_CTRL_SHIFT_VGA_DAC_DISABLE 1
+ #define CRT_DISPLAY_CTRL_SHIFT_VGA_DAC_ENABLE 0
+ #define CRT_DISPLAY_CTRL_CENTERING 24:24
+ #define CRT_DISPLAY_CTRL_CENTERING_DISABLE 0
+ #define CRT_DISPLAY_CTRL_CENTERING_ENABLE 1
+#endif
+#define CRT_DISPLAY_CTRL_LOCK_TIMING 23:23
+#define CRT_DISPLAY_CTRL_LOCK_TIMING_DISABLE 0
+#define CRT_DISPLAY_CTRL_LOCK_TIMING_ENABLE 1
+#define CRT_DISPLAY_CTRL_EXPANSION 22:22
+#define CRT_DISPLAY_CTRL_EXPANSION_DISABLE 0
+#define CRT_DISPLAY_CTRL_EXPANSION_ENABLE 1
+#define CRT_DISPLAY_CTRL_VERTICAL_MODE 21:21
+#define CRT_DISPLAY_CTRL_VERTICAL_MODE_REPLICATE 0
+#define CRT_DISPLAY_CTRL_VERTICAL_MODE_INTERPOLATE 1
+#define CRT_DISPLAY_CTRL_HORIZONTAL_MODE 20:20
+#define CRT_DISPLAY_CTRL_HORIZONTAL_MODE_REPLICATE 0
+#define CRT_DISPLAY_CTRL_HORIZONTAL_MODE_INTERPOLATE 1
+#define CRT_DISPLAY_CTRL_SELECT 19:18
+#define CRT_DISPLAY_CTRL_SELECT_PANEL 0
+#define CRT_DISPLAY_CTRL_SELECT_VGA 1
+#define CRT_DISPLAY_CTRL_SELECT_CRT 2
+#define CRT_DISPLAY_CTRL_FIFO 17:16
+#define CRT_DISPLAY_CTRL_FIFO_1 0
+#define CRT_DISPLAY_CTRL_FIFO_3 1
+#define CRT_DISPLAY_CTRL_FIFO_7 2
+#define CRT_DISPLAY_CTRL_FIFO_11 3
+#define CRT_DISPLAY_CTRL_CLOCK_PHASE 14:14
+#define CRT_DISPLAY_CTRL_CLOCK_PHASE_ACTIVE_HIGH 0
+#define CRT_DISPLAY_CTRL_CLOCK_PHASE_ACTIVE_LOW 1
+#define CRT_DISPLAY_CTRL_VSYNC_PHASE 13:13
+#define CRT_DISPLAY_CTRL_VSYNC_PHASE_ACTIVE_HIGH 0
+#define CRT_DISPLAY_CTRL_VSYNC_PHASE_ACTIVE_LOW 1
+#define CRT_DISPLAY_CTRL_HSYNC_PHASE 12:12
+#define CRT_DISPLAY_CTRL_HSYNC_PHASE_ACTIVE_HIGH 0
+#define CRT_DISPLAY_CTRL_HSYNC_PHASE_ACTIVE_LOW 1
+#define CRT_DISPLAY_CTRL_BLANK 10:10
+#define CRT_DISPLAY_CTRL_BLANK_OFF 0
+#define CRT_DISPLAY_CTRL_BLANK_ON 1
+#define CRT_DISPLAY_CTRL_TIMING 8:8
+#define CRT_DISPLAY_CTRL_TIMING_DISABLE 0
+#define CRT_DISPLAY_CTRL_TIMING_ENABLE 1
+#define CRT_DISPLAY_CTRL_PIXEL 7:4
+#define CRT_DISPLAY_CTRL_GAMMA 3:3
+#define CRT_DISPLAY_CTRL_GAMMA_DISABLE 0
+#define CRT_DISPLAY_CTRL_GAMMA_ENABLE 1
+#define CRT_DISPLAY_CTRL_PLANE 2:2
+#define CRT_DISPLAY_CTRL_PLANE_DISABLE 0
+#define CRT_DISPLAY_CTRL_PLANE_ENABLE 1
+#define CRT_DISPLAY_CTRL_FORMAT 1:0
+#define CRT_DISPLAY_CTRL_FORMAT_8 0
+#define CRT_DISPLAY_CTRL_FORMAT_16 1
+#define CRT_DISPLAY_CTRL_FORMAT_32 2
+#define CRT_DISPLAY_CTRL_RESERVED_BITS_MASK 0xFF000200
+
+#define CRT_FB_ADDRESS 0x080204
+#define CRT_FB_ADDRESS_STATUS 31:31
+#define CRT_FB_ADDRESS_STATUS_CURRENT 0
+#define CRT_FB_ADDRESS_STATUS_PENDING 1
+#define CRT_FB_ADDRESS_EXT 27:27
+#define CRT_FB_ADDRESS_EXT_LOCAL 0
+#define CRT_FB_ADDRESS_EXT_EXTERNAL 1
+#define CRT_FB_ADDRESS_ADDRESS 25:0
+
+#define CRT_FB_WIDTH 0x080208
+#define CRT_FB_WIDTH_WIDTH 29:16
+#define CRT_FB_WIDTH_OFFSET 13:0
+
+#define CRT_HORIZONTAL_TOTAL 0x08020C
+#define CRT_HORIZONTAL_TOTAL_TOTAL 27:16
+#define CRT_HORIZONTAL_TOTAL_DISPLAY_END 11:0
+
+#define CRT_HORIZONTAL_SYNC 0x080210
+#define CRT_HORIZONTAL_SYNC_WIDTH 23:16
+#define CRT_HORIZONTAL_SYNC_START 11:0
+
+#define CRT_VERTICAL_TOTAL 0x080214
+#define CRT_VERTICAL_TOTAL_TOTAL 26:16
+#define CRT_VERTICAL_TOTAL_DISPLAY_END 10:0
+
+#define CRT_VERTICAL_SYNC 0x080218
+#define CRT_VERTICAL_SYNC_HEIGHT 21:16
+#define CRT_VERTICAL_SYNC_START 10:0
+
+#define CRT_SIGNATURE_ANALYZER 0x08021C
+#define CRT_SIGNATURE_ANALYZER_STATUS 31:16
+#define CRT_SIGNATURE_ANALYZER_ENABLE 3:3
+#define CRT_SIGNATURE_ANALYZER_ENABLE_DISABLE 0
+#define CRT_SIGNATURE_ANALYZER_ENABLE_ENABLE 1
+#define CRT_SIGNATURE_ANALYZER_RESET 2:2
+#define CRT_SIGNATURE_ANALYZER_RESET_NORMAL 0
+#define CRT_SIGNATURE_ANALYZER_RESET_RESET 1
+#define CRT_SIGNATURE_ANALYZER_SOURCE 1:0
+#define CRT_SIGNATURE_ANALYZER_SOURCE_RED 0
+#define CRT_SIGNATURE_ANALYZER_SOURCE_GREEN 1
+#define CRT_SIGNATURE_ANALYZER_SOURCE_BLUE 2
+
+#define CRT_CURRENT_LINE 0x080220
+#define CRT_CURRENT_LINE_LINE 10:0
+
+#define CRT_MONITOR_DETECT 0x080224
+#define CRT_MONITOR_DETECT_VALUE 25:25
+#define CRT_MONITOR_DETECT_VALUE_DISABLE 0
+#define CRT_MONITOR_DETECT_VALUE_ENABLE 1
+#define CRT_MONITOR_DETECT_ENABLE 24:24
+#define CRT_MONITOR_DETECT_ENABLE_DISABLE 0
+#define CRT_MONITOR_DETECT_ENABLE_ENABLE 1
+#define CRT_MONITOR_DETECT_RED 23:16
+#define CRT_MONITOR_DETECT_GREEN 15:8
+#define CRT_MONITOR_DETECT_BLUE 7:0
+
+#define CRT_SCALE 0x080228
+#define CRT_SCALE_VERTICAL_MODE 31:31
+#define CRT_SCALE_VERTICAL_MODE_EXPAND 0
+#define CRT_SCALE_VERTICAL_MODE_SHRINK 1
+#define CRT_SCALE_VERTICAL_SCALE 27:16
+#define CRT_SCALE_HORIZONTAL_MODE 15:15
+#define CRT_SCALE_HORIZONTAL_MODE_EXPAND 0
+#define CRT_SCALE_HORIZONTAL_MODE_SHRINK 1
+#define CRT_SCALE_HORIZONTAL_SCALE 11:0
+
+/* CRT Cursor Control */
+
+#define CRT_HWC_ADDRESS 0x080230
+#define CRT_HWC_ADDRESS_ENABLE 31:31
+#define CRT_HWC_ADDRESS_ENABLE_DISABLE 0
+#define CRT_HWC_ADDRESS_ENABLE_ENABLE 1
+#define CRT_HWC_ADDRESS_EXT 27:27
+#define CRT_HWC_ADDRESS_EXT_LOCAL 0
+#define CRT_HWC_ADDRESS_EXT_EXTERNAL 1
+#define CRT_HWC_ADDRESS_ADDRESS 25:0
+
+#define CRT_HWC_LOCATION 0x080234
+#define CRT_HWC_LOCATION_TOP 27:27
+#define CRT_HWC_LOCATION_TOP_INSIDE 0
+#define CRT_HWC_LOCATION_TOP_OUTSIDE 1
+#define CRT_HWC_LOCATION_Y 26:16
+#define CRT_HWC_LOCATION_LEFT 11:11
+#define CRT_HWC_LOCATION_LEFT_INSIDE 0
+#define CRT_HWC_LOCATION_LEFT_OUTSIDE 1
+#define CRT_HWC_LOCATION_X 10:0
+
+#define CRT_HWC_COLOR_12 0x080238
+#define CRT_HWC_COLOR_12_2_RGB565 31:16
+#define CRT_HWC_COLOR_12_1_RGB565 15:0
+
+#define CRT_HWC_COLOR_3 0x08023C
+#define CRT_HWC_COLOR_3_RGB565 15:0
+
+/* Old Definitions +++. Need to be removed if no application use it. */
+#if 0
+ #define CRT_HWC_COLOR_01 0x080238
+ #define CRT_HWC_COLOR_01_1_RED 31:27
+ #define CRT_HWC_COLOR_01_1_GREEN 26:21
+ #define CRT_HWC_COLOR_01_1_BLUE 20:16
+ #define CRT_HWC_COLOR_01_0_RED 15:11
+ #define CRT_HWC_COLOR_01_0_GREEN 10:5
+ #define CRT_HWC_COLOR_01_0_BLUE 4:0
+
+ #define CRT_HWC_COLOR_2 0x08023C
+ #define CRT_HWC_COLOR_2_RED 15:11
+ #define CRT_HWC_COLOR_2_GREEN 10:5
+ #define CRT_HWC_COLOR_2_BLUE 4:0
+#endif
+/* Old Definitions --- */
+
+/* This vertical expansion below start at 0x080240 ~ 0x080264 */
+#define CRT_VERTICAL_EXPANSION 0x080240
+#ifndef VALIDATION_CHIP
+ #define CRT_VERTICAL_CENTERING_VALUE 31:24
+#endif
+#define CRT_VERTICAL_EXPANSION_COMPARE_VALUE 23:16
+#define CRT_VERTICAL_EXPANSION_LINE_BUFFER 15:12
+#define CRT_VERTICAL_EXPANSION_SCALE_FACTOR 11:0
+
+/* This horizontal expansion below start at 0x080268 ~ 0x08027C */
+#define CRT_HORIZONTAL_EXPANSION 0x080268
+#ifndef VALIDATION_CHIP
+ #define CRT_HORIZONTAL_CENTERING_VALUE 31:24
+#endif
+#define CRT_HORIZONTAL_EXPANSION_COMPARE_VALUE 23:16
+#define CRT_HORIZONTAL_EXPANSION_SCALE_FACTOR 11:0
+
+#ifndef VALIDATION_CHIP
+ /* Auto Centering */
+ #define CRT_AUTO_CENTERING_TL 0x080280
+ #define CRT_AUTO_CENTERING_TL_TOP 26:16
+ #define CRT_AUTO_CENTERING_TL_LEFT 10:0
+
+ #define CRT_AUTO_CENTERING_BR 0x080284
+ #define CRT_AUTO_CENTERING_BR_BOTTOM 26:16
+ #define CRT_AUTO_CENTERING_BR_RIGHT 10:0
+#endif
+
+/* sm750le new register to control panel output */
+#define DISPLAY_CONTROL_750LE 0x80288
+/* Palette RAM */
+
+/* Panel Pallete register starts at 0x080400 ~ 0x0807FC */
+#define PANEL_PALETTE_RAM 0x080400
+
+/* Panel Pallete register starts at 0x080C00 ~ 0x080FFC */
+#define CRT_PALETTE_RAM 0x080C00
+
+/* 2D registers
+ * move their defination into general lynx_accel.h file
+ * because all smi graphic chip share the same drawing engine
+ * register format */
+#if 0
+#define DE_SOURCE 0x100000
+#define DE_SOURCE_WRAP 31:31
+#define DE_SOURCE_WRAP_DISABLE 0
+#define DE_SOURCE_WRAP_ENABLE 1
+
+/*
+ * The following definitions are used in different setting
+ */
+
+/* Use these definitions in XY addressing mode or linear addressing mode. */
+#define DE_SOURCE_X_K1 27:16
+#define DE_SOURCE_Y_K2 11:0
+
+/* Use this definition in host write mode for mono. The Y_K2 is not used
+ in host write mode. */
+#define DE_SOURCE_X_K1_MONO 20:16
+
+/* Use these definitions in Bresenham line drawing mode. */
+#define DE_SOURCE_X_K1_LINE 29:16
+#define DE_SOURCE_Y_K2_LINE 13:0
+
+#define DE_DESTINATION 0x100004
+#define DE_DESTINATION_WRAP 31:31
+#define DE_DESTINATION_WRAP_DISABLE 0
+#define DE_DESTINATION_WRAP_ENABLE 1
+#if 1
+ #define DE_DESTINATION_X 27:16
+ #define DE_DESTINATION_Y 11:0
+#else
+ #define DE_DESTINATION_X 28:16
+ #define DE_DESTINATION_Y 15:0
+#endif
+
+#define DE_DIMENSION 0x100008
+#define DE_DIMENSION_X 28:16
+#define DE_DIMENSION_Y_ET 15:0
+
+#define DE_CONTROL 0x10000C
+#define DE_CONTROL_STATUS 31:31
+#define DE_CONTROL_STATUS_STOP 0
+#define DE_CONTROL_STATUS_START 1
+#define DE_CONTROL_PATTERN 30:30
+#define DE_CONTROL_PATTERN_MONO 0
+#define DE_CONTROL_PATTERN_COLOR 1
+#define DE_CONTROL_UPDATE_DESTINATION_X 29:29
+#define DE_CONTROL_UPDATE_DESTINATION_X_DISABLE 0
+#define DE_CONTROL_UPDATE_DESTINATION_X_ENABLE 1
+#define DE_CONTROL_QUICK_START 28:28
+#define DE_CONTROL_QUICK_START_DISABLE 0
+#define DE_CONTROL_QUICK_START_ENABLE 1
+#define DE_CONTROL_DIRECTION 27:27
+#define DE_CONTROL_DIRECTION_LEFT_TO_RIGHT 0
+#define DE_CONTROL_DIRECTION_RIGHT_TO_LEFT 1
+#define DE_CONTROL_MAJOR 26:26
+#define DE_CONTROL_MAJOR_X 0
+#define DE_CONTROL_MAJOR_Y 1
+#define DE_CONTROL_STEP_X 25:25
+#define DE_CONTROL_STEP_X_POSITIVE 0
+#define DE_CONTROL_STEP_X_NEGATIVE 1
+#define DE_CONTROL_STEP_Y 24:24
+#define DE_CONTROL_STEP_Y_POSITIVE 0
+#define DE_CONTROL_STEP_Y_NEGATIVE 1
+#define DE_CONTROL_STRETCH 23:23
+#define DE_CONTROL_STRETCH_DISABLE 0
+#define DE_CONTROL_STRETCH_ENABLE 1
+#define DE_CONTROL_HOST 22:22
+#define DE_CONTROL_HOST_COLOR 0
+#define DE_CONTROL_HOST_MONO 1
+#define DE_CONTROL_LAST_PIXEL 21:21
+#define DE_CONTROL_LAST_PIXEL_OFF 0
+#define DE_CONTROL_LAST_PIXEL_ON 1
+#define DE_CONTROL_COMMAND 20:16
+#define DE_CONTROL_COMMAND_BITBLT 0
+#define DE_CONTROL_COMMAND_RECTANGLE_FILL 1
+#define DE_CONTROL_COMMAND_DE_TILE 2
+#define DE_CONTROL_COMMAND_TRAPEZOID_FILL 3
+#define DE_CONTROL_COMMAND_ALPHA_BLEND 4
+#define DE_CONTROL_COMMAND_RLE_STRIP 5
+#define DE_CONTROL_COMMAND_SHORT_STROKE 6
+#define DE_CONTROL_COMMAND_LINE_DRAW 7
+#define DE_CONTROL_COMMAND_HOST_WRITE 8
+#define DE_CONTROL_COMMAND_HOST_READ 9
+#define DE_CONTROL_COMMAND_HOST_WRITE_BOTTOM_UP 10
+#define DE_CONTROL_COMMAND_ROTATE 11
+#define DE_CONTROL_COMMAND_FONT 12
+#define DE_CONTROL_COMMAND_TEXTURE_LOAD 15
+#define DE_CONTROL_ROP_SELECT 15:15
+#define DE_CONTROL_ROP_SELECT_ROP3 0
+#define DE_CONTROL_ROP_SELECT_ROP2 1
+#define DE_CONTROL_ROP2_SOURCE 14:14
+#define DE_CONTROL_ROP2_SOURCE_BITMAP 0
+#define DE_CONTROL_ROP2_SOURCE_PATTERN 1
+#define DE_CONTROL_MONO_DATA 13:12
+#define DE_CONTROL_MONO_DATA_NOT_PACKED 0
+#define DE_CONTROL_MONO_DATA_8_PACKED 1
+#define DE_CONTROL_MONO_DATA_16_PACKED 2
+#define DE_CONTROL_MONO_DATA_32_PACKED 3
+#define DE_CONTROL_REPEAT_ROTATE 11:11
+#define DE_CONTROL_REPEAT_ROTATE_DISABLE 0
+#define DE_CONTROL_REPEAT_ROTATE_ENABLE 1
+#define DE_CONTROL_TRANSPARENCY_MATCH 10:10
+#define DE_CONTROL_TRANSPARENCY_MATCH_OPAQUE 0
+#define DE_CONTROL_TRANSPARENCY_MATCH_TRANSPARENT 1
+#define DE_CONTROL_TRANSPARENCY_SELECT 9:9
+#define DE_CONTROL_TRANSPARENCY_SELECT_SOURCE 0
+#define DE_CONTROL_TRANSPARENCY_SELECT_DESTINATION 1
+#define DE_CONTROL_TRANSPARENCY 8:8
+#define DE_CONTROL_TRANSPARENCY_DISABLE 0
+#define DE_CONTROL_TRANSPARENCY_ENABLE 1
+#define DE_CONTROL_ROP 7:0
+
+/* Pseudo fields. */
+
+#define DE_CONTROL_SHORT_STROKE_DIR 27:24
+#define DE_CONTROL_SHORT_STROKE_DIR_225 0
+#define DE_CONTROL_SHORT_STROKE_DIR_135 1
+#define DE_CONTROL_SHORT_STROKE_DIR_315 2
+#define DE_CONTROL_SHORT_STROKE_DIR_45 3
+#define DE_CONTROL_SHORT_STROKE_DIR_270 4
+#define DE_CONTROL_SHORT_STROKE_DIR_90 5
+#define DE_CONTROL_SHORT_STROKE_DIR_180 8
+#define DE_CONTROL_SHORT_STROKE_DIR_0 10
+#define DE_CONTROL_ROTATION 25:24
+#define DE_CONTROL_ROTATION_0 0
+#define DE_CONTROL_ROTATION_270 1
+#define DE_CONTROL_ROTATION_90 2
+#define DE_CONTROL_ROTATION_180 3
+
+#define DE_PITCH 0x100010
+#define DE_PITCH_DESTINATION 28:16
+#define DE_PITCH_SOURCE 12:0
+
+#define DE_FOREGROUND 0x100014
+#define DE_FOREGROUND_COLOR 31:0
+
+#define DE_BACKGROUND 0x100018
+#define DE_BACKGROUND_COLOR 31:0
+
+#define DE_STRETCH_FORMAT 0x10001C
+#define DE_STRETCH_FORMAT_PATTERN_XY 30:30
+#define DE_STRETCH_FORMAT_PATTERN_XY_NORMAL 0
+#define DE_STRETCH_FORMAT_PATTERN_XY_OVERWRITE 1
+#define DE_STRETCH_FORMAT_PATTERN_Y 29:27
+#define DE_STRETCH_FORMAT_PATTERN_X 25:23
+#define DE_STRETCH_FORMAT_PIXEL_FORMAT 21:20
+#define DE_STRETCH_FORMAT_PIXEL_FORMAT_8 0
+#define DE_STRETCH_FORMAT_PIXEL_FORMAT_16 1
+#define DE_STRETCH_FORMAT_PIXEL_FORMAT_32 2
+#define DE_STRETCH_FORMAT_ADDRESSING 19:16
+#define DE_STRETCH_FORMAT_ADDRESSING_XY 0
+#define DE_STRETCH_FORMAT_ADDRESSING_LINEAR 15
+#define DE_STRETCH_FORMAT_SOURCE_HEIGHT 11:0
+
+#define DE_COLOR_COMPARE 0x100020
+#define DE_COLOR_COMPARE_COLOR 23:0
+
+#define DE_COLOR_COMPARE_MASK 0x100024
+#define DE_COLOR_COMPARE_MASK_MASKS 23:0
+
+#define DE_MASKS 0x100028
+#define DE_MASKS_BYTE_MASK 31:16
+#define DE_MASKS_BIT_MASK 15:0
+
+#define DE_CLIP_TL 0x10002C
+#define DE_CLIP_TL_TOP 31:16
+#define DE_CLIP_TL_STATUS 13:13
+#define DE_CLIP_TL_STATUS_DISABLE 0
+#define DE_CLIP_TL_STATUS_ENABLE 1
+#define DE_CLIP_TL_INHIBIT 12:12
+#define DE_CLIP_TL_INHIBIT_OUTSIDE 0
+#define DE_CLIP_TL_INHIBIT_INSIDE 1
+#define DE_CLIP_TL_LEFT 11:0
+
+#define DE_CLIP_BR 0x100030
+#define DE_CLIP_BR_BOTTOM 31:16
+#define DE_CLIP_BR_RIGHT 12:0
+
+#define DE_MONO_PATTERN_LOW 0x100034
+#define DE_MONO_PATTERN_LOW_PATTERN 31:0
+
+#define DE_MONO_PATTERN_HIGH 0x100038
+#define DE_MONO_PATTERN_HIGH_PATTERN 31:0
+
+#define DE_WINDOW_WIDTH 0x10003C
+#define DE_WINDOW_WIDTH_DESTINATION 28:16
+#define DE_WINDOW_WIDTH_SOURCE 12:0
+
+#define DE_WINDOW_SOURCE_BASE 0x100040
+#define DE_WINDOW_SOURCE_BASE_EXT 27:27
+#define DE_WINDOW_SOURCE_BASE_EXT_LOCAL 0
+#define DE_WINDOW_SOURCE_BASE_EXT_EXTERNAL 1
+#define DE_WINDOW_SOURCE_BASE_CS 26:26
+#define DE_WINDOW_SOURCE_BASE_CS_0 0
+#define DE_WINDOW_SOURCE_BASE_CS_1 1
+#define DE_WINDOW_SOURCE_BASE_ADDRESS 25:0
+
+#define DE_WINDOW_DESTINATION_BASE 0x100044
+#define DE_WINDOW_DESTINATION_BASE_EXT 27:27
+#define DE_WINDOW_DESTINATION_BASE_EXT_LOCAL 0
+#define DE_WINDOW_DESTINATION_BASE_EXT_EXTERNAL 1
+#define DE_WINDOW_DESTINATION_BASE_CS 26:26
+#define DE_WINDOW_DESTINATION_BASE_CS_0 0
+#define DE_WINDOW_DESTINATION_BASE_CS_1 1
+#define DE_WINDOW_DESTINATION_BASE_ADDRESS 25:0
+
+#define DE_ALPHA 0x100048
+#define DE_ALPHA_VALUE 7:0
+
+#define DE_WRAP 0x10004C
+#define DE_WRAP_X 31:16
+#define DE_WRAP_Y 15:0
+
+#define DE_STATUS 0x100050
+#define DE_STATUS_CSC 1:1
+#define DE_STATUS_CSC_CLEAR 0
+#define DE_STATUS_CSC_NOT_ACTIVE 0
+#define DE_STATUS_CSC_ACTIVE 1
+#define DE_STATUS_2D 0:0
+#define DE_STATUS_2D_CLEAR 0
+#define DE_STATUS_2D_NOT_ACTIVE 0
+#define DE_STATUS_2D_ACTIVE 1
+#endif
+/* Color Space Conversion registers. */
+
+#define CSC_Y_SOURCE_BASE 0x1000C8
+#define CSC_Y_SOURCE_BASE_EXT 27:27
+#define CSC_Y_SOURCE_BASE_EXT_LOCAL 0
+#define CSC_Y_SOURCE_BASE_EXT_EXTERNAL 1
+#define CSC_Y_SOURCE_BASE_CS 26:26
+#define CSC_Y_SOURCE_BASE_CS_0 0
+#define CSC_Y_SOURCE_BASE_CS_1 1
+#define CSC_Y_SOURCE_BASE_ADDRESS 25:0
+
+#define CSC_CONSTANTS 0x1000CC
+#define CSC_CONSTANTS_Y 31:24
+#define CSC_CONSTANTS_R 23:16
+#define CSC_CONSTANTS_G 15:8
+#define CSC_CONSTANTS_B 7:0
+
+#define CSC_Y_SOURCE_X 0x1000D0
+#define CSC_Y_SOURCE_X_INTEGER 26:16
+#define CSC_Y_SOURCE_X_FRACTION 15:3
+
+#define CSC_Y_SOURCE_Y 0x1000D4
+#define CSC_Y_SOURCE_Y_INTEGER 27:16
+#define CSC_Y_SOURCE_Y_FRACTION 15:3
+
+#define CSC_U_SOURCE_BASE 0x1000D8
+#define CSC_U_SOURCE_BASE_EXT 27:27
+#define CSC_U_SOURCE_BASE_EXT_LOCAL 0
+#define CSC_U_SOURCE_BASE_EXT_EXTERNAL 1
+#define CSC_U_SOURCE_BASE_CS 26:26
+#define CSC_U_SOURCE_BASE_CS_0 0
+#define CSC_U_SOURCE_BASE_CS_1 1
+#define CSC_U_SOURCE_BASE_ADDRESS 25:0
+
+#define CSC_V_SOURCE_BASE 0x1000DC
+#define CSC_V_SOURCE_BASE_EXT 27:27
+#define CSC_V_SOURCE_BASE_EXT_LOCAL 0
+#define CSC_V_SOURCE_BASE_EXT_EXTERNAL 1
+#define CSC_V_SOURCE_BASE_CS 26:26
+#define CSC_V_SOURCE_BASE_CS_0 0
+#define CSC_V_SOURCE_BASE_CS_1 1
+#define CSC_V_SOURCE_BASE_ADDRESS 25:0
+
+#define CSC_SOURCE_DIMENSION 0x1000E0
+#define CSC_SOURCE_DIMENSION_X 31:16
+#define CSC_SOURCE_DIMENSION_Y 15:0
+
+#define CSC_SOURCE_PITCH 0x1000E4
+#define CSC_SOURCE_PITCH_Y 31:16
+#define CSC_SOURCE_PITCH_UV 15:0
+
+#define CSC_DESTINATION 0x1000E8
+#define CSC_DESTINATION_WRAP 31:31
+#define CSC_DESTINATION_WRAP_DISABLE 0
+#define CSC_DESTINATION_WRAP_ENABLE 1
+#define CSC_DESTINATION_X 27:16
+#define CSC_DESTINATION_Y 11:0
+
+#define CSC_DESTINATION_DIMENSION 0x1000EC
+#define CSC_DESTINATION_DIMENSION_X 31:16
+#define CSC_DESTINATION_DIMENSION_Y 15:0
+
+#define CSC_DESTINATION_PITCH 0x1000F0
+#define CSC_DESTINATION_PITCH_X 31:16
+#define CSC_DESTINATION_PITCH_Y 15:0
+
+#define CSC_SCALE_FACTOR 0x1000F4
+#define CSC_SCALE_FACTOR_HORIZONTAL 31:16
+#define CSC_SCALE_FACTOR_VERTICAL 15:0
+
+#define CSC_DESTINATION_BASE 0x1000F8
+#define CSC_DESTINATION_BASE_EXT 27:27
+#define CSC_DESTINATION_BASE_EXT_LOCAL 0
+#define CSC_DESTINATION_BASE_EXT_EXTERNAL 1
+#define CSC_DESTINATION_BASE_CS 26:26
+#define CSC_DESTINATION_BASE_CS_0 0
+#define CSC_DESTINATION_BASE_CS_1 1
+#define CSC_DESTINATION_BASE_ADDRESS 25:0
+
+#define CSC_CONTROL 0x1000FC
+#define CSC_CONTROL_STATUS 31:31
+#define CSC_CONTROL_STATUS_STOP 0
+#define CSC_CONTROL_STATUS_START 1
+#define CSC_CONTROL_SOURCE_FORMAT 30:28
+#define CSC_CONTROL_SOURCE_FORMAT_YUV422 0
+#define CSC_CONTROL_SOURCE_FORMAT_YUV420I 1
+#define CSC_CONTROL_SOURCE_FORMAT_YUV420 2
+#define CSC_CONTROL_SOURCE_FORMAT_YVU9 3
+#define CSC_CONTROL_SOURCE_FORMAT_IYU1 4
+#define CSC_CONTROL_SOURCE_FORMAT_IYU2 5
+#define CSC_CONTROL_SOURCE_FORMAT_RGB565 6
+#define CSC_CONTROL_SOURCE_FORMAT_RGB8888 7
+#define CSC_CONTROL_DESTINATION_FORMAT 27:26
+#define CSC_CONTROL_DESTINATION_FORMAT_RGB565 0
+#define CSC_CONTROL_DESTINATION_FORMAT_RGB8888 1
+#define CSC_CONTROL_HORIZONTAL_FILTER 25:25
+#define CSC_CONTROL_HORIZONTAL_FILTER_DISABLE 0
+#define CSC_CONTROL_HORIZONTAL_FILTER_ENABLE 1
+#define CSC_CONTROL_VERTICAL_FILTER 24:24
+#define CSC_CONTROL_VERTICAL_FILTER_DISABLE 0
+#define CSC_CONTROL_VERTICAL_FILTER_ENABLE 1
+#define CSC_CONTROL_BYTE_ORDER 23:23
+#define CSC_CONTROL_BYTE_ORDER_YUYV 0
+#define CSC_CONTROL_BYTE_ORDER_UYVY 1
+
+#define DE_DATA_PORT 0x110000
+
+#define I2C_BYTE_COUNT 0x010040
+#define I2C_BYTE_COUNT_COUNT 3:0
+
+#define I2C_CTRL 0x010041
+#define I2C_CTRL_INT 4:4
+#define I2C_CTRL_INT_DISABLE 0
+#define I2C_CTRL_INT_ENABLE 1
+#define I2C_CTRL_DIR 3:3
+#define I2C_CTRL_DIR_WR 0
+#define I2C_CTRL_DIR_RD 1
+#define I2C_CTRL_CTRL 2:2
+#define I2C_CTRL_CTRL_STOP 0
+#define I2C_CTRL_CTRL_START 1
+#define I2C_CTRL_MODE 1:1
+#define I2C_CTRL_MODE_STANDARD 0
+#define I2C_CTRL_MODE_FAST 1
+#define I2C_CTRL_EN 0:0
+#define I2C_CTRL_EN_DISABLE 0
+#define I2C_CTRL_EN_ENABLE 1
+
+#define I2C_STATUS 0x010042
+#define I2C_STATUS_TX 3:3
+#define I2C_STATUS_TX_PROGRESS 0
+#define I2C_STATUS_TX_COMPLETED 1
+#define I2C_TX_DONE 0x08
+#define I2C_STATUS_ERR 2:2
+#define I2C_STATUS_ERR_NORMAL 0
+#define I2C_STATUS_ERR_ERROR 1
+#define I2C_STATUS_ERR_CLEAR 0
+#define I2C_STATUS_ACK 1:1
+#define I2C_STATUS_ACK_RECEIVED 0
+#define I2C_STATUS_ACK_NOT 1
+#define I2C_STATUS_BSY 0:0
+#define I2C_STATUS_BSY_IDLE 0
+#define I2C_STATUS_BSY_BUSY 1
+
+#define I2C_RESET 0x010042
+#define I2C_RESET_BUS_ERROR 2:2
+#define I2C_RESET_BUS_ERROR_CLEAR 0
+
+#define I2C_SLAVE_ADDRESS 0x010043
+#define I2C_SLAVE_ADDRESS_ADDRESS 7:1
+#define I2C_SLAVE_ADDRESS_RW 0:0
+#define I2C_SLAVE_ADDRESS_RW_W 0
+#define I2C_SLAVE_ADDRESS_RW_R 1
+
+#define I2C_DATA0 0x010044
+#define I2C_DATA1 0x010045
+#define I2C_DATA2 0x010046
+#define I2C_DATA3 0x010047
+#define I2C_DATA4 0x010048
+#define I2C_DATA5 0x010049
+#define I2C_DATA6 0x01004A
+#define I2C_DATA7 0x01004B
+#define I2C_DATA8 0x01004C
+#define I2C_DATA9 0x01004D
+#define I2C_DATA10 0x01004E
+#define I2C_DATA11 0x01004F
+#define I2C_DATA12 0x010050
+#define I2C_DATA13 0x010051
+#define I2C_DATA14 0x010052
+#define I2C_DATA15 0x010053
+
+
+#define ZV0_CAPTURE_CTRL 0x090000
+#define ZV0_CAPTURE_CTRL_FIELD_INPUT 27:27
+#define ZV0_CAPTURE_CTRL_FIELD_INPUT_EVEN_FIELD 0
+#define ZV0_CAPTURE_CTRL_FIELD_INPUT_ODD_FIELD 1
+#define ZV0_CAPTURE_CTRL_SCAN 26:26
+#define ZV0_CAPTURE_CTRL_SCAN_PROGRESSIVE 0
+#define ZV0_CAPTURE_CTRL_SCAN_INTERLACE 1
+#define ZV0_CAPTURE_CTRL_CURRENT_BUFFER 25:25
+#define ZV0_CAPTURE_CTRL_CURRENT_BUFFER_0 0
+#define ZV0_CAPTURE_CTRL_CURRENT_BUFFER_1 1
+#define ZV0_CAPTURE_CTRL_VERTICAL_SYNC 24:24
+#define ZV0_CAPTURE_CTRL_VERTICAL_SYNC_INACTIVE 0
+#define ZV0_CAPTURE_CTRL_VERTICAL_SYNC_ACTIVE 1
+#define ZV0_CAPTURE_CTRL_ADJ 19:19
+#define ZV0_CAPTURE_CTRL_ADJ_NORMAL 0
+#define ZV0_CAPTURE_CTRL_ADJ_DELAY 1
+#define ZV0_CAPTURE_CTRL_HA 18:18
+#define ZV0_CAPTURE_CTRL_HA_DISABLE 0
+#define ZV0_CAPTURE_CTRL_HA_ENABLE 1
+#define ZV0_CAPTURE_CTRL_VSK 17:17
+#define ZV0_CAPTURE_CTRL_VSK_DISABLE 0
+#define ZV0_CAPTURE_CTRL_VSK_ENABLE 1
+#define ZV0_CAPTURE_CTRL_HSK 16:16
+#define ZV0_CAPTURE_CTRL_HSK_DISABLE 0
+#define ZV0_CAPTURE_CTRL_HSK_ENABLE 1
+#define ZV0_CAPTURE_CTRL_FD 15:15
+#define ZV0_CAPTURE_CTRL_FD_RISING 0
+#define ZV0_CAPTURE_CTRL_FD_FALLING 1
+#define ZV0_CAPTURE_CTRL_VP 14:14
+#define ZV0_CAPTURE_CTRL_VP_HIGH 0
+#define ZV0_CAPTURE_CTRL_VP_LOW 1
+#define ZV0_CAPTURE_CTRL_HP 13:13
+#define ZV0_CAPTURE_CTRL_HP_HIGH 0
+#define ZV0_CAPTURE_CTRL_HP_LOW 1
+#define ZV0_CAPTURE_CTRL_CP 12:12
+#define ZV0_CAPTURE_CTRL_CP_HIGH 0
+#define ZV0_CAPTURE_CTRL_CP_LOW 1
+#define ZV0_CAPTURE_CTRL_UVS 11:11
+#define ZV0_CAPTURE_CTRL_UVS_DISABLE 0
+#define ZV0_CAPTURE_CTRL_UVS_ENABLE 1
+#define ZV0_CAPTURE_CTRL_BS 10:10
+#define ZV0_CAPTURE_CTRL_BS_DISABLE 0
+#define ZV0_CAPTURE_CTRL_BS_ENABLE 1
+#define ZV0_CAPTURE_CTRL_CS 9:9
+#define ZV0_CAPTURE_CTRL_CS_16 0
+#define ZV0_CAPTURE_CTRL_CS_8 1
+#define ZV0_CAPTURE_CTRL_CF 8:8
+#define ZV0_CAPTURE_CTRL_CF_YUV 0
+#define ZV0_CAPTURE_CTRL_CF_RGB 1
+#define ZV0_CAPTURE_CTRL_FS 7:7
+#define ZV0_CAPTURE_CTRL_FS_DISABLE 0
+#define ZV0_CAPTURE_CTRL_FS_ENABLE 1
+#define ZV0_CAPTURE_CTRL_WEAVE 6:6
+#define ZV0_CAPTURE_CTRL_WEAVE_DISABLE 0
+#define ZV0_CAPTURE_CTRL_WEAVE_ENABLE 1
+#define ZV0_CAPTURE_CTRL_BOB 5:5
+#define ZV0_CAPTURE_CTRL_BOB_DISABLE 0
+#define ZV0_CAPTURE_CTRL_BOB_ENABLE 1
+#define ZV0_CAPTURE_CTRL_DB 4:4
+#define ZV0_CAPTURE_CTRL_DB_DISABLE 0
+#define ZV0_CAPTURE_CTRL_DB_ENABLE 1
+#define ZV0_CAPTURE_CTRL_CC 3:3
+#define ZV0_CAPTURE_CTRL_CC_CONTINUE 0
+#define ZV0_CAPTURE_CTRL_CC_CONDITION 1
+#define ZV0_CAPTURE_CTRL_RGB 2:2
+#define ZV0_CAPTURE_CTRL_RGB_DISABLE 0
+#define ZV0_CAPTURE_CTRL_RGB_ENABLE 1
+#define ZV0_CAPTURE_CTRL_656 1:1
+#define ZV0_CAPTURE_CTRL_656_DISABLE 0
+#define ZV0_CAPTURE_CTRL_656_ENABLE 1
+#define ZV0_CAPTURE_CTRL_CAP 0:0
+#define ZV0_CAPTURE_CTRL_CAP_DISABLE 0
+#define ZV0_CAPTURE_CTRL_CAP_ENABLE 1
+
+#define ZV0_CAPTURE_CLIP 0x090004
+#define ZV0_CAPTURE_CLIP_YCLIP_EVEN_FIELD 25:16
+#define ZV0_CAPTURE_CLIP_YCLIP 25:16
+#define ZV0_CAPTURE_CLIP_XCLIP 9:0
+
+#define ZV0_CAPTURE_SIZE 0x090008
+#define ZV0_CAPTURE_SIZE_HEIGHT 26:16
+#define ZV0_CAPTURE_SIZE_WIDTH 10:0
+
+#define ZV0_CAPTURE_BUF0_ADDRESS 0x09000C
+#define ZV0_CAPTURE_BUF0_ADDRESS_STATUS 31:31
+#define ZV0_CAPTURE_BUF0_ADDRESS_STATUS_CURRENT 0
+#define ZV0_CAPTURE_BUF0_ADDRESS_STATUS_PENDING 1
+#define ZV0_CAPTURE_BUF0_ADDRESS_EXT 27:27
+#define ZV0_CAPTURE_BUF0_ADDRESS_EXT_LOCAL 0
+#define ZV0_CAPTURE_BUF0_ADDRESS_EXT_EXTERNAL 1
+#define ZV0_CAPTURE_BUF0_ADDRESS_CS 26:26
+#define ZV0_CAPTURE_BUF0_ADDRESS_CS_0 0
+#define ZV0_CAPTURE_BUF0_ADDRESS_CS_1 1
+#define ZV0_CAPTURE_BUF0_ADDRESS_ADDRESS 25:0
+
+#define ZV0_CAPTURE_BUF1_ADDRESS 0x090010
+#define ZV0_CAPTURE_BUF1_ADDRESS_STATUS 31:31
+#define ZV0_CAPTURE_BUF1_ADDRESS_STATUS_CURRENT 0
+#define ZV0_CAPTURE_BUF1_ADDRESS_STATUS_PENDING 1
+#define ZV0_CAPTURE_BUF1_ADDRESS_EXT 27:27
+#define ZV0_CAPTURE_BUF1_ADDRESS_EXT_LOCAL 0
+#define ZV0_CAPTURE_BUF1_ADDRESS_EXT_EXTERNAL 1
+#define ZV0_CAPTURE_BUF1_ADDRESS_CS 26:26
+#define ZV0_CAPTURE_BUF1_ADDRESS_CS_0 0
+#define ZV0_CAPTURE_BUF1_ADDRESS_CS_1 1
+#define ZV0_CAPTURE_BUF1_ADDRESS_ADDRESS 25:0
+
+#define ZV0_CAPTURE_BUF_OFFSET 0x090014
+#ifndef VALIDATION_CHIP
+ #define ZV0_CAPTURE_BUF_OFFSET_YCLIP_ODD_FIELD 25:16
+#endif
+#define ZV0_CAPTURE_BUF_OFFSET_OFFSET 15:0
+
+#define ZV0_CAPTURE_FIFO_CTRL 0x090018
+#define ZV0_CAPTURE_FIFO_CTRL_FIFO 2:0
+#define ZV0_CAPTURE_FIFO_CTRL_FIFO_0 0
+#define ZV0_CAPTURE_FIFO_CTRL_FIFO_1 1
+#define ZV0_CAPTURE_FIFO_CTRL_FIFO_2 2
+#define ZV0_CAPTURE_FIFO_CTRL_FIFO_3 3
+#define ZV0_CAPTURE_FIFO_CTRL_FIFO_4 4
+#define ZV0_CAPTURE_FIFO_CTRL_FIFO_5 5
+#define ZV0_CAPTURE_FIFO_CTRL_FIFO_6 6
+#define ZV0_CAPTURE_FIFO_CTRL_FIFO_7 7
+
+#define ZV0_CAPTURE_YRGB_CONST 0x09001C
+#define ZV0_CAPTURE_YRGB_CONST_Y 31:24
+#define ZV0_CAPTURE_YRGB_CONST_R 23:16
+#define ZV0_CAPTURE_YRGB_CONST_G 15:8
+#define ZV0_CAPTURE_YRGB_CONST_B 7:0
+
+#define ZV0_CAPTURE_LINE_COMP 0x090020
+#define ZV0_CAPTURE_LINE_COMP_LC 10:0
+
+/* ZV1 */
+
+#define ZV1_CAPTURE_CTRL 0x098000
+#define ZV1_CAPTURE_CTRL_FIELD_INPUT 27:27
+#define ZV1_CAPTURE_CTRL_FIELD_INPUT_EVEN_FIELD 0
+#define ZV1_CAPTURE_CTRL_FIELD_INPUT_ODD_FIELD 0
+#define ZV1_CAPTURE_CTRL_SCAN 26:26
+#define ZV1_CAPTURE_CTRL_SCAN_PROGRESSIVE 0
+#define ZV1_CAPTURE_CTRL_SCAN_INTERLACE 1
+#define ZV1_CAPTURE_CTRL_CURRENT_BUFFER 25:25
+#define ZV1_CAPTURE_CTRL_CURRENT_BUFFER_0 0
+#define ZV1_CAPTURE_CTRL_CURRENT_BUFFER_1 1
+#define ZV1_CAPTURE_CTRL_VERTICAL_SYNC 24:24
+#define ZV1_CAPTURE_CTRL_VERTICAL_SYNC_INACTIVE 0
+#define ZV1_CAPTURE_CTRL_VERTICAL_SYNC_ACTIVE 1
+#define ZV1_CAPTURE_CTRL_PANEL 20:20
+#define ZV1_CAPTURE_CTRL_PANEL_DISABLE 0
+#define ZV1_CAPTURE_CTRL_PANEL_ENABLE 1
+#define ZV1_CAPTURE_CTRL_ADJ 19:19
+#define ZV1_CAPTURE_CTRL_ADJ_NORMAL 0
+#define ZV1_CAPTURE_CTRL_ADJ_DELAY 1
+#define ZV1_CAPTURE_CTRL_HA 18:18
+#define ZV1_CAPTURE_CTRL_HA_DISABLE 0
+#define ZV1_CAPTURE_CTRL_HA_ENABLE 1
+#define ZV1_CAPTURE_CTRL_VSK 17:17
+#define ZV1_CAPTURE_CTRL_VSK_DISABLE 0
+#define ZV1_CAPTURE_CTRL_VSK_ENABLE 1
+#define ZV1_CAPTURE_CTRL_HSK 16:16
+#define ZV1_CAPTURE_CTRL_HSK_DISABLE 0
+#define ZV1_CAPTURE_CTRL_HSK_ENABLE 1
+#define ZV1_CAPTURE_CTRL_FD 15:15
+#define ZV1_CAPTURE_CTRL_FD_RISING 0
+#define ZV1_CAPTURE_CTRL_FD_FALLING 1
+#define ZV1_CAPTURE_CTRL_VP 14:14
+#define ZV1_CAPTURE_CTRL_VP_HIGH 0
+#define ZV1_CAPTURE_CTRL_VP_LOW 1
+#define ZV1_CAPTURE_CTRL_HP 13:13
+#define ZV1_CAPTURE_CTRL_HP_HIGH 0
+#define ZV1_CAPTURE_CTRL_HP_LOW 1
+#define ZV1_CAPTURE_CTRL_CP 12:12
+#define ZV1_CAPTURE_CTRL_CP_HIGH 0
+#define ZV1_CAPTURE_CTRL_CP_LOW 1
+#define ZV1_CAPTURE_CTRL_UVS 11:11
+#define ZV1_CAPTURE_CTRL_UVS_DISABLE 0
+#define ZV1_CAPTURE_CTRL_UVS_ENABLE 1
+#define ZV1_CAPTURE_CTRL_BS 10:10
+#define ZV1_CAPTURE_CTRL_BS_DISABLE 0
+#define ZV1_CAPTURE_CTRL_BS_ENABLE 1
+#define ZV1_CAPTURE_CTRL_CS 9:9
+#define ZV1_CAPTURE_CTRL_CS_16 0
+#define ZV1_CAPTURE_CTRL_CS_8 1
+#define ZV1_CAPTURE_CTRL_CF 8:8
+#define ZV1_CAPTURE_CTRL_CF_YUV 0
+#define ZV1_CAPTURE_CTRL_CF_RGB 1
+#define ZV1_CAPTURE_CTRL_FS 7:7
+#define ZV1_CAPTURE_CTRL_FS_DISABLE 0
+#define ZV1_CAPTURE_CTRL_FS_ENABLE 1
+#define ZV1_CAPTURE_CTRL_WEAVE 6:6
+#define ZV1_CAPTURE_CTRL_WEAVE_DISABLE 0
+#define ZV1_CAPTURE_CTRL_WEAVE_ENABLE 1
+#define ZV1_CAPTURE_CTRL_BOB 5:5
+#define ZV1_CAPTURE_CTRL_BOB_DISABLE 0
+#define ZV1_CAPTURE_CTRL_BOB_ENABLE 1
+#define ZV1_CAPTURE_CTRL_DB 4:4
+#define ZV1_CAPTURE_CTRL_DB_DISABLE 0
+#define ZV1_CAPTURE_CTRL_DB_ENABLE 1
+#define ZV1_CAPTURE_CTRL_CC 3:3
+#define ZV1_CAPTURE_CTRL_CC_CONTINUE 0
+#define ZV1_CAPTURE_CTRL_CC_CONDITION 1
+#define ZV1_CAPTURE_CTRL_RGB 2:2
+#define ZV1_CAPTURE_CTRL_RGB_DISABLE 0
+#define ZV1_CAPTURE_CTRL_RGB_ENABLE 1
+#define ZV1_CAPTURE_CTRL_656 1:1
+#define ZV1_CAPTURE_CTRL_656_DISABLE 0
+#define ZV1_CAPTURE_CTRL_656_ENABLE 1
+#define ZV1_CAPTURE_CTRL_CAP 0:0
+#define ZV1_CAPTURE_CTRL_CAP_DISABLE 0
+#define ZV1_CAPTURE_CTRL_CAP_ENABLE 1
+
+#define ZV1_CAPTURE_CLIP 0x098004
+#define ZV1_CAPTURE_CLIP_YCLIP 25:16
+#define ZV1_CAPTURE_CLIP_XCLIP 9:0
+
+#define ZV1_CAPTURE_SIZE 0x098008
+#define ZV1_CAPTURE_SIZE_HEIGHT 26:16
+#define ZV1_CAPTURE_SIZE_WIDTH 10:0
+
+#define ZV1_CAPTURE_BUF0_ADDRESS 0x09800C
+#define ZV1_CAPTURE_BUF0_ADDRESS_STATUS 31:31
+#define ZV1_CAPTURE_BUF0_ADDRESS_STATUS_CURRENT 0
+#define ZV1_CAPTURE_BUF0_ADDRESS_STATUS_PENDING 1
+#define ZV1_CAPTURE_BUF0_ADDRESS_EXT 27:27
+#define ZV1_CAPTURE_BUF0_ADDRESS_EXT_LOCAL 0
+#define ZV1_CAPTURE_BUF0_ADDRESS_EXT_EXTERNAL 1
+#define ZV1_CAPTURE_BUF0_ADDRESS_CS 26:26
+#define ZV1_CAPTURE_BUF0_ADDRESS_CS_0 0
+#define ZV1_CAPTURE_BUF0_ADDRESS_CS_1 1
+#define ZV1_CAPTURE_BUF0_ADDRESS_ADDRESS 25:0
+
+#define ZV1_CAPTURE_BUF1_ADDRESS 0x098010
+#define ZV1_CAPTURE_BUF1_ADDRESS_STATUS 31:31
+#define ZV1_CAPTURE_BUF1_ADDRESS_STATUS_CURRENT 0
+#define ZV1_CAPTURE_BUF1_ADDRESS_STATUS_PENDING 1
+#define ZV1_CAPTURE_BUF1_ADDRESS_EXT 27:27
+#define ZV1_CAPTURE_BUF1_ADDRESS_EXT_LOCAL 0
+#define ZV1_CAPTURE_BUF1_ADDRESS_EXT_EXTERNAL 1
+#define ZV1_CAPTURE_BUF1_ADDRESS_CS 26:26
+#define ZV1_CAPTURE_BUF1_ADDRESS_CS_0 0
+#define ZV1_CAPTURE_BUF1_ADDRESS_CS_1 1
+#define ZV1_CAPTURE_BUF1_ADDRESS_ADDRESS 25:0
+
+#define ZV1_CAPTURE_BUF_OFFSET 0x098014
+#define ZV1_CAPTURE_BUF_OFFSET_OFFSET 15:0
+
+#define ZV1_CAPTURE_FIFO_CTRL 0x098018
+#define ZV1_CAPTURE_FIFO_CTRL_FIFO 2:0
+#define ZV1_CAPTURE_FIFO_CTRL_FIFO_0 0
+#define ZV1_CAPTURE_FIFO_CTRL_FIFO_1 1
+#define ZV1_CAPTURE_FIFO_CTRL_FIFO_2 2
+#define ZV1_CAPTURE_FIFO_CTRL_FIFO_3 3
+#define ZV1_CAPTURE_FIFO_CTRL_FIFO_4 4
+#define ZV1_CAPTURE_FIFO_CTRL_FIFO_5 5
+#define ZV1_CAPTURE_FIFO_CTRL_FIFO_6 6
+#define ZV1_CAPTURE_FIFO_CTRL_FIFO_7 7
+
+#define ZV1_CAPTURE_YRGB_CONST 0x09801C
+#define ZV1_CAPTURE_YRGB_CONST_Y 31:24
+#define ZV1_CAPTURE_YRGB_CONST_R 23:16
+#define ZV1_CAPTURE_YRGB_CONST_G 15:8
+#define ZV1_CAPTURE_YRGB_CONST_B 7:0
+
+#define DMA_1_SOURCE 0x0D0010
+#define DMA_1_SOURCE_ADDRESS_EXT 27:27
+#define DMA_1_SOURCE_ADDRESS_EXT_LOCAL 0
+#define DMA_1_SOURCE_ADDRESS_EXT_EXTERNAL 1
+#define DMA_1_SOURCE_ADDRESS_CS 26:26
+#define DMA_1_SOURCE_ADDRESS_CS_0 0
+#define DMA_1_SOURCE_ADDRESS_CS_1 1
+#define DMA_1_SOURCE_ADDRESS 25:0
+
+#define DMA_1_DESTINATION 0x0D0014
+#define DMA_1_DESTINATION_ADDRESS_EXT 27:27
+#define DMA_1_DESTINATION_ADDRESS_EXT_LOCAL 0
+#define DMA_1_DESTINATION_ADDRESS_EXT_EXTERNAL 1
+#define DMA_1_DESTINATION_ADDRESS_CS 26:26
+#define DMA_1_DESTINATION_ADDRESS_CS_0 0
+#define DMA_1_DESTINATION_ADDRESS_CS_1 1
+#define DMA_1_DESTINATION_ADDRESS 25:0
+
+#define DMA_1_SIZE_CONTROL 0x0D0018
+#define DMA_1_SIZE_CONTROL_STATUS 31:31
+#define DMA_1_SIZE_CONTROL_STATUS_IDLE 0
+#define DMA_1_SIZE_CONTROL_STATUS_ACTIVE 1
+#define DMA_1_SIZE_CONTROL_SIZE 23:0
+
+#define DMA_ABORT_INTERRUPT 0x0D0020
+#define DMA_ABORT_INTERRUPT_ABORT_1 5:5
+#define DMA_ABORT_INTERRUPT_ABORT_1_ENABLE 0
+#define DMA_ABORT_INTERRUPT_ABORT_1_ABORT 1
+#define DMA_ABORT_INTERRUPT_ABORT_0 4:4
+#define DMA_ABORT_INTERRUPT_ABORT_0_ENABLE 0
+#define DMA_ABORT_INTERRUPT_ABORT_0_ABORT 1
+#define DMA_ABORT_INTERRUPT_INT_1 1:1
+#define DMA_ABORT_INTERRUPT_INT_1_CLEAR 0
+#define DMA_ABORT_INTERRUPT_INT_1_FINISHED 1
+#define DMA_ABORT_INTERRUPT_INT_0 0:0
+#define DMA_ABORT_INTERRUPT_INT_0_CLEAR 0
+#define DMA_ABORT_INTERRUPT_INT_0_FINISHED 1
+
+
+
+
+
+/* Default i2c CLK and Data GPIO. These are the default i2c pins */
+#define DEFAULT_I2C_SCL 30
+#define DEFAULT_I2C_SDA 31
+
+
+#define GPIO_DATA_SM750LE 0x020018
+#define GPIO_DATA_SM750LE_1 1:1
+#define GPIO_DATA_SM750LE_0 0:0
+
+#define GPIO_DATA_DIRECTION_SM750LE 0x02001C
+#define GPIO_DATA_DIRECTION_SM750LE_1 1:1
+#define GPIO_DATA_DIRECTION_SM750LE_1_INPUT 0
+#define GPIO_DATA_DIRECTION_SM750LE_1_OUTPUT 1
+#define GPIO_DATA_DIRECTION_SM750LE_0 0:0
+#define GPIO_DATA_DIRECTION_SM750LE_0_INPUT 0
+#define GPIO_DATA_DIRECTION_SM750LE_0_OUTPUT 1
+
+
+#endif
diff --git a/drivers/staging/sm750fb/ddk750_sii164.c b/drivers/staging/sm750fb/ddk750_sii164.c
new file mode 100644
index 000000000..3d224d6a7
--- /dev/null
+++ b/drivers/staging/sm750fb/ddk750_sii164.c
@@ -0,0 +1,425 @@
+#define USE_DVICHIP
+#ifdef USE_DVICHIP
+
+#include "ddk750_sii164.h"
+#include "ddk750_hwi2c.h"
+
+/* I2C Address of each SII164 chip */
+#define SII164_I2C_ADDRESS 0x70
+
+/* Define this definition to use hardware i2c. */
+#define USE_HW_I2C
+
+#ifdef USE_HW_I2C
+ #define i2cWriteReg hwI2CWriteReg
+ #define i2cReadReg hwI2CReadReg
+#else
+ #define i2cWriteReg swI2CWriteReg
+ #define i2cReadReg swI2CReadReg
+#endif
+
+/* SII164 Vendor and Device ID */
+#define SII164_VENDOR_ID 0x0001
+#define SII164_DEVICE_ID 0x0006
+
+#ifdef SII164_FULL_FUNCTIONS
+/* Name of the DVI Controller chip */
+static char *gDviCtrlChipName = "Silicon Image SiI 164";
+#endif
+
+/*
+ * sii164GetVendorID
+ * This function gets the vendor ID of the DVI controller chip.
+ *
+ * Output:
+ * Vendor ID
+ */
+unsigned short sii164GetVendorID(void)
+{
+ unsigned short vendorID;
+
+ vendorID = ((unsigned short) i2cReadReg(SII164_I2C_ADDRESS, SII164_VENDOR_ID_HIGH) << 8) |
+ (unsigned short) i2cReadReg(SII164_I2C_ADDRESS, SII164_VENDOR_ID_LOW);
+
+ return vendorID;
+}
+
+/*
+ * sii164GetDeviceID
+ * This function gets the device ID of the DVI controller chip.
+ *
+ * Output:
+ * Device ID
+ */
+unsigned short sii164GetDeviceID(void)
+{
+ unsigned short deviceID;
+
+ deviceID = ((unsigned short) i2cReadReg(SII164_I2C_ADDRESS, SII164_DEVICE_ID_HIGH) << 8) |
+ (unsigned short) i2cReadReg(SII164_I2C_ADDRESS, SII164_DEVICE_ID_LOW);
+
+ return deviceID;
+}
+
+
+
+/* DVI.C will handle all SiI164 chip stuffs and try it best to make code minimal and useful */
+
+/*
+ * sii164InitChip
+ * This function initialize and detect the DVI controller chip.
+ *
+ * Input:
+ * edgeSelect - Edge Select:
+ * 0 = Input data is falling edge latched (falling edge
+ * latched first in dual edge mode)
+ * 1 = Input data is rising edge latched (rising edge
+ * latched first in dual edge mode)
+ * busSelect - Input Bus Select:
+ * 0 = Input data bus is 12-bits wide
+ * 1 = Input data bus is 24-bits wide
+ * dualEdgeClkSelect - Dual Edge Clock Select
+ * 0 = Input data is single edge latched
+ * 1 = Input data is dual edge latched
+ * hsyncEnable - Horizontal Sync Enable:
+ * 0 = HSYNC input is transmitted as fixed LOW
+ * 1 = HSYNC input is transmitted as is
+ * vsyncEnable - Vertical Sync Enable:
+ * 0 = VSYNC input is transmitted as fixed LOW
+ * 1 = VSYNC input is transmitted as is
+ * deskewEnable - De-skewing Enable:
+ * 0 = De-skew disabled
+ * 1 = De-skew enabled
+ * deskewSetting - De-skewing Setting (increment of 260psec)
+ * 0 = 1 step --> minimum setup / maximum hold
+ * 1 = 2 step
+ * 2 = 3 step
+ * 3 = 4 step
+ * 4 = 5 step
+ * 5 = 6 step
+ * 6 = 7 step
+ * 7 = 8 step --> maximum setup / minimum hold
+ * continuousSyncEnable- SYNC Continuous:
+ * 0 = Disable
+ * 1 = Enable
+ * pllFilterEnable - PLL Filter Enable
+ * 0 = Disable PLL Filter
+ * 1 = Enable PLL Filter
+ * pllFilterValue - PLL Filter characteristics:
+ * 0~7 (recommended value is 4)
+ *
+ * Output:
+ * 0 - Success
+ * -1 - Fail.
+ */
+long sii164InitChip(
+ unsigned char edgeSelect,
+ unsigned char busSelect,
+ unsigned char dualEdgeClkSelect,
+ unsigned char hsyncEnable,
+ unsigned char vsyncEnable,
+ unsigned char deskewEnable,
+ unsigned char deskewSetting,
+ unsigned char continuousSyncEnable,
+ unsigned char pllFilterEnable,
+ unsigned char pllFilterValue
+)
+{
+ //unsigned char ucRegIndex, ucRegValue;
+ //unsigned char ucDeviceAddress,
+ unsigned char config;
+ //unsigned long delayCount;
+
+ /* Initialize the i2c bus */
+#ifdef USE_HW_I2C
+ /* Use fast mode. */
+ hwI2CInit(1);
+#else
+ swI2CInit(DEFAULT_I2C_SCL, DEFAULT_I2C_SDA);
+#endif
+
+ /* Check if SII164 Chip exists */
+ if ((sii164GetVendorID() == SII164_VENDOR_ID) && (sii164GetDeviceID() == SII164_DEVICE_ID))
+ {
+
+#ifdef DDKDEBUG
+ //sii164PrintRegisterValues();
+#endif
+ /*
+ * Initialize SII164 controller chip.
+ */
+
+ /* Select the edge */
+ if (edgeSelect == 0)
+ config = SII164_CONFIGURATION_LATCH_FALLING;
+ else
+ config = SII164_CONFIGURATION_LATCH_RISING;
+
+ /* Select bus wide */
+ if (busSelect == 0)
+ config |= SII164_CONFIGURATION_BUS_12BITS;
+ else
+ config |= SII164_CONFIGURATION_BUS_24BITS;
+
+ /* Select Dual/Single Edge Clock */
+ if (dualEdgeClkSelect == 0)
+ config |= SII164_CONFIGURATION_CLOCK_SINGLE;
+ else
+ config |= SII164_CONFIGURATION_CLOCK_DUAL;
+
+ /* Select HSync Enable */
+ if (hsyncEnable == 0)
+ config |= SII164_CONFIGURATION_HSYNC_FORCE_LOW;
+ else
+ config |= SII164_CONFIGURATION_HSYNC_AS_IS;
+
+ /* Select VSync Enable */
+ if (vsyncEnable == 0)
+ config |= SII164_CONFIGURATION_VSYNC_FORCE_LOW;
+ else
+ config |= SII164_CONFIGURATION_VSYNC_AS_IS;
+
+ i2cWriteReg(SII164_I2C_ADDRESS, SII164_CONFIGURATION, config);
+
+ /* De-skew enabled with default 111b value.
+ This will fix some artifacts problem in some mode on board 2.2.
+ Somehow this fix does not affect board 2.1.
+ */
+ if (deskewEnable == 0)
+ config = SII164_DESKEW_DISABLE;
+ else
+ config = SII164_DESKEW_ENABLE;
+
+ switch (deskewSetting)
+ {
+ case 0:
+ config |= SII164_DESKEW_1_STEP;
+ break;
+ case 1:
+ config |= SII164_DESKEW_2_STEP;
+ break;
+ case 2:
+ config |= SII164_DESKEW_3_STEP;
+ break;
+ case 3:
+ config |= SII164_DESKEW_4_STEP;
+ break;
+ case 4:
+ config |= SII164_DESKEW_5_STEP;
+ break;
+ case 5:
+ config |= SII164_DESKEW_6_STEP;
+ break;
+ case 6:
+ config |= SII164_DESKEW_7_STEP;
+ break;
+ case 7:
+ config |= SII164_DESKEW_8_STEP;
+ break;
+ }
+ i2cWriteReg(SII164_I2C_ADDRESS, SII164_DESKEW, config);
+
+ /* Enable/Disable Continuous Sync. */
+ if (continuousSyncEnable == 0)
+ config = SII164_PLL_FILTER_SYNC_CONTINUOUS_DISABLE;
+ else
+ config = SII164_PLL_FILTER_SYNC_CONTINUOUS_ENABLE;
+
+ /* Enable/Disable PLL Filter */
+ if (pllFilterEnable == 0)
+ config |= SII164_PLL_FILTER_DISABLE;
+ else
+ config |= SII164_PLL_FILTER_ENABLE;
+
+ /* Set the PLL Filter value */
+ config |= ((pllFilterValue & 0x07) << 1);
+
+ i2cWriteReg(SII164_I2C_ADDRESS, SII164_PLL, config);
+
+ /* Recover from Power Down and enable output. */
+ config = i2cReadReg(SII164_I2C_ADDRESS, SII164_CONFIGURATION);
+ config |= SII164_CONFIGURATION_POWER_NORMAL;
+ i2cWriteReg(SII164_I2C_ADDRESS, SII164_CONFIGURATION, config);
+
+#ifdef DDKDEBUG
+ //sii164PrintRegisterValues();
+#endif
+
+ return 0;
+ }
+
+ /* Return -1 if initialization fails. */
+ return (-1);
+}
+
+
+
+
+
+/* below sii164 function is not neccessary */
+
+#ifdef SII164_FULL_FUNCTIONS
+
+/*
+ * sii164ResetChip
+ * This function resets the DVI Controller Chip.
+ */
+void sii164ResetChip(void)
+{
+ /* Power down */
+ sii164SetPower(0);
+ sii164SetPower(1);
+}
+
+
+/*
+ * sii164GetChipString
+ * This function returns a char string name of the current DVI Controller chip.
+ * It's convenient for application need to display the chip name.
+ */
+char *sii164GetChipString(void)
+{
+ return gDviCtrlChipName;
+}
+
+
+/*
+ * sii164SetPower
+ * This function sets the power configuration of the DVI Controller Chip.
+ *
+ * Input:
+ * powerUp - Flag to set the power down or up
+ */
+void sii164SetPower(
+ unsigned char powerUp
+)
+{
+ unsigned char config;
+
+ config = i2cReadReg(SII164_I2C_ADDRESS, SII164_CONFIGURATION);
+ if (powerUp == 1)
+ {
+ /* Power up the chip */
+ config &= ~SII164_CONFIGURATION_POWER_MASK;
+ config |= SII164_CONFIGURATION_POWER_NORMAL;
+ i2cWriteReg(SII164_I2C_ADDRESS, SII164_CONFIGURATION, config);
+ }
+ else
+ {
+ /* Power down the chip */
+ config &= ~SII164_CONFIGURATION_POWER_MASK;
+ config |= SII164_CONFIGURATION_POWER_DOWN;
+ i2cWriteReg(SII164_I2C_ADDRESS, SII164_CONFIGURATION, config);
+ }
+}
+
+
+/*
+ * sii164SelectHotPlugDetectionMode
+ * This function selects the mode of the hot plug detection.
+ */
+static void sii164SelectHotPlugDetectionMode(
+ sii164_hot_plug_mode_t hotPlugMode
+)
+{
+ unsigned char detectReg;
+
+ detectReg = i2cReadReg(SII164_I2C_ADDRESS, SII164_DETECT) & ~SII164_DETECT_MONITOR_SENSE_OUTPUT_FLAG;
+ switch (hotPlugMode)
+ {
+ case SII164_HOTPLUG_DISABLE:
+ detectReg |= SII164_DETECT_MONITOR_SENSE_OUTPUT_HIGH;
+ break;
+ case SII164_HOTPLUG_USE_MDI:
+ detectReg &= ~SII164_DETECT_INTERRUPT_MASK;
+ detectReg |= SII164_DETECT_INTERRUPT_BY_HTPLG_PIN;
+ detectReg |= SII164_DETECT_MONITOR_SENSE_OUTPUT_MDI;
+ break;
+ case SII164_HOTPLUG_USE_RSEN:
+ detectReg |= SII164_DETECT_MONITOR_SENSE_OUTPUT_RSEN;
+ break;
+ case SII164_HOTPLUG_USE_HTPLG:
+ detectReg |= SII164_DETECT_MONITOR_SENSE_OUTPUT_HTPLG;
+ break;
+ }
+
+ i2cWriteReg(SII164_I2C_ADDRESS, SII164_DETECT, detectReg);
+}
+
+/*
+ * sii164EnableHotPlugDetection
+ * This function enables the Hot Plug detection.
+ *
+ * enableHotPlug - Enable (=1) / disable (=0) Hot Plug detection
+ */
+void sii164EnableHotPlugDetection(
+ unsigned char enableHotPlug
+)
+{
+ unsigned char detectReg;
+ detectReg = i2cReadReg(SII164_I2C_ADDRESS, SII164_DETECT);
+
+ /* Depending on each DVI controller, need to enable the hot plug based on each
+ individual chip design. */
+ if (enableHotPlug != 0)
+ sii164SelectHotPlugDetectionMode(SII164_HOTPLUG_USE_MDI);
+ else
+ sii164SelectHotPlugDetectionMode(SII164_HOTPLUG_DISABLE);
+}
+
+/*
+ * sii164IsConnected
+ * Check if the DVI Monitor is connected.
+ *
+ * Output:
+ * 0 - Not Connected
+ * 1 - Connected
+ */
+unsigned char sii164IsConnected(void)
+{
+ unsigned char hotPlugValue;
+
+ hotPlugValue = i2cReadReg(SII164_I2C_ADDRESS, SII164_DETECT) & SII164_DETECT_HOT_PLUG_STATUS_MASK;
+ if (hotPlugValue == SII164_DETECT_HOT_PLUG_STATUS_ON)
+ return 1;
+ else
+ return 0;
+}
+
+/*
+ * sii164CheckInterrupt
+ * Checks if interrupt has occured.
+ *
+ * Output:
+ * 0 - No interrupt
+ * 1 - Interrupt occurs
+ */
+unsigned char sii164CheckInterrupt(void)
+{
+ unsigned char detectReg;
+
+ detectReg = i2cReadReg(SII164_I2C_ADDRESS, SII164_DETECT) & SII164_DETECT_MONITOR_STATE_MASK;
+ if (detectReg == SII164_DETECT_MONITOR_STATE_CHANGE)
+ return 1;
+ else
+ return 0;
+}
+
+/*
+ * sii164ClearInterrupt
+ * Clear the hot plug interrupt.
+ */
+void sii164ClearInterrupt(void)
+{
+ unsigned char detectReg;
+
+ /* Clear the MDI interrupt */
+ detectReg = i2cReadReg(SII164_I2C_ADDRESS, SII164_DETECT);
+ i2cWriteReg(SII164_I2C_ADDRESS, SII164_DETECT, detectReg | SII164_DETECT_MONITOR_STATE_CLEAR);
+}
+
+#endif
+
+#endif
+
+
diff --git a/drivers/staging/sm750fb/ddk750_sii164.h b/drivers/staging/sm750fb/ddk750_sii164.h
new file mode 100644
index 000000000..2b4c7d338
--- /dev/null
+++ b/drivers/staging/sm750fb/ddk750_sii164.h
@@ -0,0 +1,172 @@
+#ifndef DDK750_SII164_H__
+#define DDK750_SII164_H__
+
+#define USE_DVICHIP
+
+/* Hot Plug detection mode structure */
+typedef enum _sii164_hot_plug_mode_t
+{
+ SII164_HOTPLUG_DISABLE = 0, /* Disable Hot Plug output bit (always high). */
+ SII164_HOTPLUG_USE_MDI, /* Use Monitor Detect Interrupt bit. */
+ SII164_HOTPLUG_USE_RSEN, /* Use Receiver Sense detect bit. */
+ SII164_HOTPLUG_USE_HTPLG /* Use Hot Plug detect bit. */
+} sii164_hot_plug_mode_t;
+
+
+/* Silicon Image SiI164 chip prototype */
+long sii164InitChip(
+ unsigned char edgeSelect,
+ unsigned char busSelect,
+ unsigned char dualEdgeClkSelect,
+ unsigned char hsyncEnable,
+ unsigned char vsyncEnable,
+ unsigned char deskewEnable,
+ unsigned char deskewSetting,
+ unsigned char continuousSyncEnable,
+ unsigned char pllFilterEnable,
+ unsigned char pllFilterValue
+);
+
+unsigned short sii164GetVendorID(void);
+unsigned short sii164GetDeviceID(void);
+
+
+#ifdef SII164_FULL_FUNCTIONS
+void sii164ResetChip(void);
+char *sii164GetChipString(void);
+void sii164SetPower(unsigned char powerUp);
+void sii164EnableHotPlugDetection(unsigned char enableHotPlug);
+unsigned char sii164IsConnected(void);
+unsigned char sii164CheckInterrupt(void);
+void sii164ClearInterrupt(void);
+#endif
+/* below register definination is used for Silicon Image SiI164 DVI controller chip */
+/*
+ * Vendor ID registers
+ */
+#define SII164_VENDOR_ID_LOW 0x00
+#define SII164_VENDOR_ID_HIGH 0x01
+
+/*
+ * Device ID registers
+ */
+#define SII164_DEVICE_ID_LOW 0x02
+#define SII164_DEVICE_ID_HIGH 0x03
+
+/*
+ * Device Revision
+ */
+#define SII164_DEVICE_REVISION 0x04
+
+/*
+ * Frequency Limitation registers
+ */
+#define SII164_FREQUENCY_LIMIT_LOW 0x06
+#define SII164_FREQUENCY_LIMIT_HIGH 0x07
+
+/*
+ * Power Down and Input Signal Configuration registers
+ */
+#define SII164_CONFIGURATION 0x08
+
+/* Power down (PD) */
+#define SII164_CONFIGURATION_POWER_DOWN 0x00
+#define SII164_CONFIGURATION_POWER_NORMAL 0x01
+#define SII164_CONFIGURATION_POWER_MASK 0x01
+
+/* Input Edge Latch Select (EDGE) */
+#define SII164_CONFIGURATION_LATCH_FALLING 0x00
+#define SII164_CONFIGURATION_LATCH_RISING 0x02
+
+/* Bus Select (BSEL) */
+#define SII164_CONFIGURATION_BUS_12BITS 0x00
+#define SII164_CONFIGURATION_BUS_24BITS 0x04
+
+/* Dual Edge Clock Select (DSEL) */
+#define SII164_CONFIGURATION_CLOCK_SINGLE 0x00
+#define SII164_CONFIGURATION_CLOCK_DUAL 0x08
+
+/* Horizontal Sync Enable (HEN) */
+#define SII164_CONFIGURATION_HSYNC_FORCE_LOW 0x00
+#define SII164_CONFIGURATION_HSYNC_AS_IS 0x10
+
+/* Vertical Sync Enable (VEN) */
+#define SII164_CONFIGURATION_VSYNC_FORCE_LOW 0x00
+#define SII164_CONFIGURATION_VSYNC_AS_IS 0x20
+
+/*
+ * Detection registers
+ */
+#define SII164_DETECT 0x09
+
+/* Monitor Detect Interrupt (MDI) */
+#define SII164_DETECT_MONITOR_STATE_CHANGE 0x00
+#define SII164_DETECT_MONITOR_STATE_NO_CHANGE 0x01
+#define SII164_DETECT_MONITOR_STATE_CLEAR 0x01
+#define SII164_DETECT_MONITOR_STATE_MASK 0x01
+
+/* Hot Plug detect Input (HTPLG) */
+#define SII164_DETECT_HOT_PLUG_STATUS_OFF 0x00
+#define SII164_DETECT_HOT_PLUG_STATUS_ON 0x02
+#define SII164_DETECT_HOT_PLUG_STATUS_MASK 0x02
+
+/* Receiver Sense (RSEN) */
+#define SII164_DETECT_RECEIVER_SENSE_NOT_DETECTED 0x00
+#define SII164_DETECT_RECEIVER_SENSE_DETECTED 0x04
+
+/* Interrupt Generation Method (TSEL) */
+#define SII164_DETECT_INTERRUPT_BY_RSEN_PIN 0x00
+#define SII164_DETECT_INTERRUPT_BY_HTPLG_PIN 0x08
+#define SII164_DETECT_INTERRUPT_MASK 0x08
+
+/* Monitor Sense Output (MSEN) */
+#define SII164_DETECT_MONITOR_SENSE_OUTPUT_HIGH 0x00
+#define SII164_DETECT_MONITOR_SENSE_OUTPUT_MDI 0x10
+#define SII164_DETECT_MONITOR_SENSE_OUTPUT_RSEN 0x20
+#define SII164_DETECT_MONITOR_SENSE_OUTPUT_HTPLG 0x30
+#define SII164_DETECT_MONITOR_SENSE_OUTPUT_FLAG 0x30
+
+/*
+ * Skewing registers
+ */
+#define SII164_DESKEW 0x0A
+
+/* General Purpose Input (CTL[3:1]) */
+#define SII164_DESKEW_GENERAL_PURPOSE_INPUT_MASK 0x0E
+
+/* De-skewing Enable bit (DKEN) */
+#define SII164_DESKEW_DISABLE 0x00
+#define SII164_DESKEW_ENABLE 0x10
+
+/* De-skewing Setting (DK[3:1])*/
+#define SII164_DESKEW_1_STEP 0x00
+#define SII164_DESKEW_2_STEP 0x20
+#define SII164_DESKEW_3_STEP 0x40
+#define SII164_DESKEW_4_STEP 0x60
+#define SII164_DESKEW_5_STEP 0x80
+#define SII164_DESKEW_6_STEP 0xA0
+#define SII164_DESKEW_7_STEP 0xC0
+#define SII164_DESKEW_8_STEP 0xE0
+
+/*
+ * User Configuration Data registers (CFG 7:0)
+ */
+#define SII164_USER_CONFIGURATION 0x0B
+
+/*
+ * PLL registers
+ */
+#define SII164_PLL 0x0C
+
+/* PLL Filter Value (PLLF) */
+#define SII164_PLL_FILTER_VALUE_MASK 0x0E
+
+/* PLL Filter Enable (PFEN) */
+#define SII164_PLL_FILTER_DISABLE 0x00
+#define SII164_PLL_FILTER_ENABLE 0x01
+
+/* Sync Continuous (SCNT) */
+#define SII164_PLL_FILTER_SYNC_CONTINUOUS_DISABLE 0x00
+#define SII164_PLL_FILTER_SYNC_CONTINUOUS_ENABLE 0x80
+
+#endif
diff --git a/drivers/staging/sm750fb/ddk750_swi2c.c b/drivers/staging/sm750fb/ddk750_swi2c.c
new file mode 100644
index 000000000..901b3737f
--- /dev/null
+++ b/drivers/staging/sm750fb/ddk750_swi2c.c
@@ -0,0 +1,522 @@
+/*******************************************************************
+*
+* Copyright (c) 2007 by Silicon Motion, Inc. (SMI)
+*
+* All rights are reserved. Reproduction or in part is prohibited
+* without the written consent of the copyright owner.
+*
+* swi2c.c --- SM750/SM718 DDK
+* This file contains the source code for I2C using software
+* implementation.
+*
+*******************************************************************/
+#include "ddk750_help.h"
+#include "ddk750_reg.h"
+#include "ddk750_swi2c.h"
+#include "ddk750_power.h"
+
+
+/*******************************************************************
+ * I2C Software Master Driver:
+ * ===========================
+ * Each i2c cycle is split into 4 sections. Each of these section marks
+ * a point in time where the SCL or SDA may be changed.
+ *
+ * 1 Cycle == | Section I. | Section 2. | Section 3. | Section 4. |
+ * +-------------+-------------+-------------+-------------+
+ * | SCL set LOW |SCL no change| SCL set HIGH|SCL no change|
+ *
+ * ____________ _____________
+ * SCL == XXXX _____________ ____________ /
+ *
+ * I.e. the SCL may only be changed in section 1. and section 3. while
+ * the SDA may only be changed in section 2. and section 4. The table
+ * below gives the changes for these 2 lines in the varios sections.
+ *
+ * Section changes Table:
+ * ======================
+ * blank = no change, L = set bit LOW, H = set bit HIGH
+ *
+ * | 1.| 2.| 3.| 4.|
+ * ---------------+---+---+---+---+
+ * Tx Start SDA | | H | | L |
+ * SCL | L | | H | |
+ * ---------------+---+---+---+---+
+ * Tx Stop SDA | | L | | H |
+ * SCL | L | | H | |
+ * ---------------+---+---+---+---+
+ * Tx bit H SDA | | H | | |
+ * SCL | L | | H | |
+ * ---------------+---+---+---+---+
+ * Tx bit L SDA | | L | | |
+ * SCL | L | | H | |
+ * ---------------+---+---+---+---+
+ *
+ ******************************************************************/
+
+/* GPIO pins used for this I2C. It ranges from 0 to 63. */
+static unsigned char g_i2cClockGPIO = DEFAULT_I2C_SCL;
+static unsigned char g_i2cDataGPIO = DEFAULT_I2C_SDA;
+
+/*
+ * Below is the variable declaration for the GPIO pin register usage
+ * for the i2c Clock and i2c Data.
+ *
+ * Note:
+ * Notice that the GPIO usage for the i2c clock and i2c Data are
+ * separated. This is to make this code flexible enough when
+ * two separate GPIO pins for the clock and data are located
+ * in two different GPIO register set (worst case).
+ */
+
+/* i2c Clock GPIO Register usage */
+static unsigned long g_i2cClkGPIOMuxReg = GPIO_MUX;
+static unsigned long g_i2cClkGPIODataReg = GPIO_DATA;
+static unsigned long g_i2cClkGPIODataDirReg = GPIO_DATA_DIRECTION;
+
+/* i2c Data GPIO Register usage */
+static unsigned long g_i2cDataGPIOMuxReg = GPIO_MUX;
+static unsigned long g_i2cDataGPIODataReg = GPIO_DATA;
+static unsigned long g_i2cDataGPIODataDirReg = GPIO_DATA_DIRECTION;
+
+/*
+ * This function puts a delay between command
+ */
+static void swI2CWait(void)
+{
+ /* find a bug:
+ * peekIO method works well before suspend/resume
+ * but after suspend, peekIO(0x3ce,0x61) & 0x10
+ * always be non-zero,which makes the while loop
+ * never finish.
+ * use non-ultimate for loop below is safe
+ * */
+#if 0
+ /* Change wait algorithm to use PCI bus clock,
+ it's more reliable than counter loop ..
+ write 0x61 to 0x3ce and read from 0x3cf
+ */
+ while(peekIO(0x3ce,0x61) & 0x10);
+#else
+ int i, Temp;
+
+ for(i=0; i<600; i++)
+ {
+ Temp = i;
+ Temp += i;
+ }
+#endif
+}
+
+/*
+ * This function set/reset the SCL GPIO pin
+ *
+ * Parameters:
+ * value - Bit value to set to the SCL or SDA (0 = low, 1 = high)
+ *
+ * Notes:
+ * When setting SCL to high, just set the GPIO as input where the pull up
+ * resistor will pull the signal up. Do not use software to pull up the
+ * signal because the i2c will fail when other device try to drive the
+ * signal due to SM50x will drive the signal to always high.
+ */
+void swI2CSCL(unsigned char value)
+{
+ unsigned long ulGPIOData;
+ unsigned long ulGPIODirection;
+
+ ulGPIODirection = PEEK32(g_i2cClkGPIODataDirReg);
+ if (value) /* High */
+ {
+ /* Set direction as input. This will automatically pull the signal up. */
+ ulGPIODirection &= ~(1 << g_i2cClockGPIO);
+ POKE32(g_i2cClkGPIODataDirReg, ulGPIODirection);
+ }
+ else /* Low */
+ {
+ /* Set the signal down */
+ ulGPIOData = PEEK32(g_i2cClkGPIODataReg);
+ ulGPIOData &= ~(1 << g_i2cClockGPIO);
+ POKE32(g_i2cClkGPIODataReg, ulGPIOData);
+
+ /* Set direction as output */
+ ulGPIODirection |= (1 << g_i2cClockGPIO);
+ POKE32(g_i2cClkGPIODataDirReg, ulGPIODirection);
+ }
+}
+
+/*
+ * This function set/reset the SDA GPIO pin
+ *
+ * Parameters:
+ * value - Bit value to set to the SCL or SDA (0 = low, 1 = high)
+ *
+ * Notes:
+ * When setting SCL to high, just set the GPIO as input where the pull up
+ * resistor will pull the signal up. Do not use software to pull up the
+ * signal because the i2c will fail when other device try to drive the
+ * signal due to SM50x will drive the signal to always high.
+ */
+void swI2CSDA(unsigned char value)
+{
+ unsigned long ulGPIOData;
+ unsigned long ulGPIODirection;
+
+ ulGPIODirection = PEEK32(g_i2cDataGPIODataDirReg);
+ if (value) /* High */
+ {
+ /* Set direction as input. This will automatically pull the signal up. */
+ ulGPIODirection &= ~(1 << g_i2cDataGPIO);
+ POKE32(g_i2cDataGPIODataDirReg, ulGPIODirection);
+ }
+ else /* Low */
+ {
+ /* Set the signal down */
+ ulGPIOData = PEEK32(g_i2cDataGPIODataReg);
+ ulGPIOData &= ~(1 << g_i2cDataGPIO);
+ POKE32(g_i2cDataGPIODataReg, ulGPIOData);
+
+ /* Set direction as output */
+ ulGPIODirection |= (1 << g_i2cDataGPIO);
+ POKE32(g_i2cDataGPIODataDirReg, ulGPIODirection);
+ }
+}
+
+/*
+ * This function read the data from the SDA GPIO pin
+ *
+ * Return Value:
+ * The SDA data bit sent by the Slave
+ */
+static unsigned char swI2CReadSDA(void)
+{
+ unsigned long ulGPIODirection;
+ unsigned long ulGPIOData;
+
+ /* Make sure that the direction is input (High) */
+ ulGPIODirection = PEEK32(g_i2cDataGPIODataDirReg);
+ if ((ulGPIODirection & (1 << g_i2cDataGPIO)) != (~(1 << g_i2cDataGPIO)))
+ {
+ ulGPIODirection &= ~(1 << g_i2cDataGPIO);
+ POKE32(g_i2cDataGPIODataDirReg, ulGPIODirection);
+ }
+
+ /* Now read the SDA line */
+ ulGPIOData = PEEK32(g_i2cDataGPIODataReg);
+ if (ulGPIOData & (1 << g_i2cDataGPIO))
+ return 1;
+ else
+ return 0;
+}
+
+/*
+ * This function sends ACK signal
+ */
+static void swI2CAck(void)
+{
+ return; /* Single byte read is ok without it. */
+}
+
+/*
+ * This function sends the start command to the slave device
+ */
+static void swI2CStart(void)
+{
+ /* Start I2C */
+ swI2CSDA(1);
+ swI2CSCL(1);
+ swI2CSDA(0);
+}
+
+/*
+ * This function sends the stop command to the slave device
+ */
+static void swI2CStop(void)
+{
+ /* Stop the I2C */
+ swI2CSCL(1);
+ swI2CSDA(0);
+ swI2CSDA(1);
+}
+
+/*
+ * This function writes one byte to the slave device
+ *
+ * Parameters:
+ * data - Data to be write to the slave device
+ *
+ * Return Value:
+ * 0 - Success
+ * -1 - Fail to write byte
+ */
+static long swI2CWriteByte(unsigned char data)
+{
+ unsigned char value = data;
+ int i;
+
+ /* Sending the data bit by bit */
+ for (i=0; i<8; i++)
+ {
+ /* Set SCL to low */
+ swI2CSCL(0);
+
+ /* Send data bit */
+ if ((value & 0x80) != 0)
+ swI2CSDA(1);
+ else
+ swI2CSDA(0);
+
+ swI2CWait();
+
+ /* Toggle clk line to one */
+ swI2CSCL(1);
+ swI2CWait();
+
+ /* Shift byte to be sent */
+ value = value << 1;
+ }
+
+ /* Set the SCL Low and SDA High (prepare to get input) */
+ swI2CSCL(0);
+ swI2CSDA(1);
+
+ /* Set the SCL High for ack */
+ swI2CWait();
+ swI2CSCL(1);
+ swI2CWait();
+
+ /* Read SDA, until SDA==0 */
+ for(i=0; i<0xff; i++)
+ {
+ if (!swI2CReadSDA())
+ break;
+
+ swI2CSCL(0);
+ swI2CWait();
+ swI2CSCL(1);
+ swI2CWait();
+ }
+
+ /* Set the SCL Low and SDA High */
+ swI2CSCL(0);
+ swI2CSDA(1);
+
+ if (i<0xff)
+ return 0;
+ else
+ return -1;
+}
+
+/*
+ * This function reads one byte from the slave device
+ *
+ * Parameters:
+ * ack - Flag to indicate either to send the acknowledge
+ * message to the slave device or not
+ *
+ * Return Value:
+ * One byte data read from the Slave device
+ */
+static unsigned char swI2CReadByte(unsigned char ack)
+{
+ int i;
+ unsigned char data = 0;
+
+ for(i=7; i>=0; i--)
+ {
+ /* Set the SCL to Low and SDA to High (Input) */
+ swI2CSCL(0);
+ swI2CSDA(1);
+ swI2CWait();
+
+ /* Set the SCL High */
+ swI2CSCL(1);
+ swI2CWait();
+
+ /* Read data bits from SDA */
+ data |= (swI2CReadSDA() << i);
+ }
+
+ if (ack)
+ swI2CAck();
+
+ /* Set the SCL Low and SDA High */
+ swI2CSCL(0);
+ swI2CSDA(1);
+
+ return data;
+}
+
+/*
+ * This function initializes GPIO port for SW I2C communication.
+ *
+ * Parameters:
+ * i2cClkGPIO - The GPIO pin to be used as i2c SCL
+ * i2cDataGPIO - The GPIO pin to be used as i2c SDA
+ *
+ * Return Value:
+ * -1 - Fail to initialize the i2c
+ * 0 - Success
+ */
+static long swI2CInit_SM750LE(unsigned char i2cClkGPIO,
+ unsigned char i2cDataGPIO)
+{
+ int i;
+
+ /* Initialize the GPIO pin for the i2c Clock Register */
+ g_i2cClkGPIODataReg = GPIO_DATA_SM750LE;
+ g_i2cClkGPIODataDirReg = GPIO_DATA_DIRECTION_SM750LE;
+
+ /* Initialize the Clock GPIO Offset */
+ g_i2cClockGPIO = i2cClkGPIO;
+
+ /* Initialize the GPIO pin for the i2c Data Register */
+ g_i2cDataGPIODataReg = GPIO_DATA_SM750LE;
+ g_i2cDataGPIODataDirReg = GPIO_DATA_DIRECTION_SM750LE;
+
+ /* Initialize the Data GPIO Offset */
+ g_i2cDataGPIO = i2cDataGPIO;
+
+ /* Note that SM750LE don't have GPIO MUX and power is always on */
+
+ /* Clear the i2c lines. */
+ for(i=0; i<9; i++)
+ swI2CStop();
+
+ return 0;
+}
+
+/*
+ * This function initializes the i2c attributes and bus
+ *
+ * Parameters:
+ * i2cClkGPIO - The GPIO pin to be used as i2c SCL
+ * i2cDataGPIO - The GPIO pin to be used as i2c SDA
+ *
+ * Return Value:
+ * -1 - Fail to initialize the i2c
+ * 0 - Success
+ */
+long swI2CInit(
+ unsigned char i2cClkGPIO,
+ unsigned char i2cDataGPIO
+)
+{
+ int i;
+
+ /* Return 0 if the GPIO pins to be used is out of range. The range is only from [0..63] */
+ if ((i2cClkGPIO > 31) || (i2cDataGPIO > 31))
+ return -1;
+
+ if (getChipType() == SM750LE)
+ return swI2CInit_SM750LE(i2cClkGPIO, i2cDataGPIO);
+
+ /* Initialize the GPIO pin for the i2c Clock Register */
+ g_i2cClkGPIOMuxReg = GPIO_MUX;
+ g_i2cClkGPIODataReg = GPIO_DATA;
+ g_i2cClkGPIODataDirReg = GPIO_DATA_DIRECTION;
+
+ /* Initialize the Clock GPIO Offset */
+ g_i2cClockGPIO = i2cClkGPIO;
+
+ /* Initialize the GPIO pin for the i2c Data Register */
+ g_i2cDataGPIOMuxReg = GPIO_MUX;
+ g_i2cDataGPIODataReg = GPIO_DATA;
+ g_i2cDataGPIODataDirReg = GPIO_DATA_DIRECTION;
+
+ /* Initialize the Data GPIO Offset */
+ g_i2cDataGPIO = i2cDataGPIO;
+
+ /* Enable the GPIO pins for the i2c Clock and Data (GPIO MUX) */
+ POKE32(g_i2cClkGPIOMuxReg,
+ PEEK32(g_i2cClkGPIOMuxReg) & ~(1 << g_i2cClockGPIO));
+ POKE32(g_i2cDataGPIOMuxReg,
+ PEEK32(g_i2cDataGPIOMuxReg) & ~(1 << g_i2cDataGPIO));
+
+ /* Enable GPIO power */
+ enableGPIO(1);
+
+ /* Clear the i2c lines. */
+ for(i=0; i<9; i++)
+ swI2CStop();
+
+ return 0;
+}
+
+/*
+ * This function reads the slave device's register
+ *
+ * Parameters:
+ * deviceAddress - i2c Slave device address which register
+ * to be read from
+ * registerIndex - Slave device's register to be read
+ *
+ * Return Value:
+ * Register value
+ */
+unsigned char swI2CReadReg(
+ unsigned char deviceAddress,
+ unsigned char registerIndex
+)
+{
+ unsigned char data;
+
+ /* Send the Start signal */
+ swI2CStart();
+
+ /* Send the device address */
+ swI2CWriteByte(deviceAddress);
+
+ /* Send the register index */
+ swI2CWriteByte(registerIndex);
+
+ /* Get the bus again and get the data from the device read address */
+ swI2CStart();
+ swI2CWriteByte(deviceAddress + 1);
+ data = swI2CReadByte(1);
+
+ /* Stop swI2C and release the bus */
+ swI2CStop();
+
+ return data;
+}
+
+/*
+ * This function writes a value to the slave device's register
+ *
+ * Parameters:
+ * deviceAddress - i2c Slave device address which register
+ * to be written
+ * registerIndex - Slave device's register to be written
+ * data - Data to be written to the register
+ *
+ * Result:
+ * 0 - Success
+ * -1 - Fail
+ */
+long swI2CWriteReg(
+ unsigned char deviceAddress,
+ unsigned char registerIndex,
+ unsigned char data
+)
+{
+ long returnValue = 0;
+
+ /* Send the Start signal */
+ swI2CStart();
+
+ /* Send the device address and read the data. All should return success
+ in order for the writing processed to be successful
+ */
+ if ((swI2CWriteByte(deviceAddress) != 0) ||
+ (swI2CWriteByte(registerIndex) != 0) ||
+ (swI2CWriteByte(data) != 0))
+ {
+ returnValue = -1;
+ }
+
+ /* Stop i2c and release the bus */
+ swI2CStop();
+
+ return returnValue;
+}
diff --git a/drivers/staging/sm750fb/ddk750_swi2c.h b/drivers/staging/sm750fb/ddk750_swi2c.h
new file mode 100644
index 000000000..ec5463b98
--- /dev/null
+++ b/drivers/staging/sm750fb/ddk750_swi2c.h
@@ -0,0 +1,92 @@
+/*******************************************************************
+*
+* Copyright (c) 2007 by Silicon Motion, Inc. (SMI)
+*
+* All rights are reserved. Reproduction or in part is prohibited
+* without the written consent of the copyright owner.
+*
+* swi2c.h --- SM750/SM718 DDK
+* This file contains the definitions for i2c using software
+* implementation.
+*
+*******************************************************************/
+#ifndef _SWI2C_H_
+#define _SWI2C_H_
+
+/* Default i2c CLK and Data GPIO. These are the default i2c pins */
+#define DEFAULT_I2C_SCL 30
+#define DEFAULT_I2C_SDA 31
+
+/*
+ * This function initializes the i2c attributes and bus
+ *
+ * Parameters:
+ * i2cClkGPIO - The GPIO pin to be used as i2c SCL
+ * i2cDataGPIO - The GPIO pin to be used as i2c SDA
+ *
+ * Return Value:
+ * -1 - Fail to initialize the i2c
+ * 0 - Success
+ */
+long swI2CInit(
+ unsigned char i2cClkGPIO,
+ unsigned char i2cDataGPIO
+);
+
+/*
+ * This function reads the slave device's register
+ *
+ * Parameters:
+ * deviceAddress - i2c Slave device address which register
+ * to be read from
+ * registerIndex - Slave device's register to be read
+ *
+ * Return Value:
+ * Register value
+ */
+unsigned char swI2CReadReg(
+ unsigned char deviceAddress,
+ unsigned char registerIndex
+);
+
+/*
+ * This function writes a value to the slave device's register
+ *
+ * Parameters:
+ * deviceAddress - i2c Slave device address which register
+ * to be written
+ * registerIndex - Slave device's register to be written
+ * data - Data to be written to the register
+ *
+ * Result:
+ * 0 - Success
+ * -1 - Fail
+ */
+long swI2CWriteReg(
+ unsigned char deviceAddress,
+ unsigned char registerIndex,
+ unsigned char data
+);
+
+/*
+ * These two functions are used to toggle the data on the SCL and SDA I2C lines.
+ * The used of these two functions are not recommended unless it is necessary.
+ */
+
+/*
+ * This function set/reset the SCL GPIO pin
+ *
+ * Parameters:
+ * value - Bit value to set to the SCL or SDA (0 = low, 1 = high)
+ */
+void swI2CSCL(unsigned char value);
+
+/*
+ * This function set/reset the SDA GPIO pin
+ *
+ * Parameters:
+ * value - Bit value to set to the SCL or SDA (0 = low, 1 = high)
+ */
+void swI2CSDA(unsigned char value);
+
+#endif /* _SWI2C_H_ */
diff --git a/drivers/staging/sm750fb/modedb.h b/drivers/staging/sm750fb/modedb.h
new file mode 100644
index 000000000..c5275c6ff
--- /dev/null
+++ b/drivers/staging/sm750fb/modedb.h
@@ -0,0 +1,221 @@
+
+static const struct fb_videomode modedb2[] = {
+ {
+ /* 640x400 @ 70 Hz, 31.5 kHz hsync */
+ NULL, 70, 640, 400, 39721, 40, 24, 39, 9, 96, 2,
+ 0, FB_VMODE_NONINTERLACED
+ }, {
+ /* 640x480 @ 60 Hz, 31.5 kHz hsync */
+ NULL, 60, 640, 480, 39721, 40, 24, 32, 11, 96, 2,
+ 0, FB_VMODE_NONINTERLACED
+ }, {
+ /* 800x600 @ 56 Hz, 35.15 kHz hsync */
+ NULL, 56, 800, 600, 27777, 128, 24, 22, 1, 72, 2,
+ 0, FB_VMODE_NONINTERLACED
+ }, {
+ /* 1024x768 @ 87 Hz interlaced, 35.5 kHz hsync */
+ NULL, 87, 1024, 768, 22271, 56, 24, 33, 8, 160, 8,
+ 0, FB_VMODE_INTERLACED
+ }, {
+ /* 640x400 @ 85 Hz, 37.86 kHz hsync */
+ NULL, 85, 640, 400, 31746, 96, 32, 41, 1, 64, 3,
+ FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED
+ }, {
+ /* 640x480 @ 72 Hz, 36.5 kHz hsync */
+ NULL, 72, 640, 480, 31746, 144, 40, 30, 8, 40, 3,
+ 0, FB_VMODE_NONINTERLACED
+ }, {
+ /* 640x480 @ 75 Hz, 37.50 kHz hsync */
+ NULL, 75, 640, 480, 31746, 120, 16, 16, 1, 64, 3,
+ 0, FB_VMODE_NONINTERLACED
+ }, {
+ /* 800x600 @ 60 Hz, 37.8 kHz hsync */
+ NULL, 60, 800, 600, 25000, 88, 40, 23, 1, 128, 4,
+ FB_SYNC_HOR_HIGH_ACT|FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED
+ }, {
+ /* 640x480 @ 85 Hz, 43.27 kHz hsync */
+ NULL, 85, 640, 480, 27777, 80, 56, 25, 1, 56, 3,
+ 0, FB_VMODE_NONINTERLACED
+ }, {
+ /* 1152x864 @ 89 Hz interlaced, 44 kHz hsync */
+ NULL, 69, 1152, 864, 15384, 96, 16, 110, 1, 216, 10,
+ 0, FB_VMODE_INTERLACED
+ }, {
+ /* 800x600 @ 72 Hz, 48.0 kHz hsync */
+ NULL, 72, 800, 600, 20000, 64, 56, 23, 37, 120, 6,
+ FB_SYNC_HOR_HIGH_ACT|FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED
+ }, {
+ /* 1024x768 @ 60 Hz, 48.4 kHz hsync */
+ NULL, 60, 1024, 768, 15384, 168, 8, 29, 3, 144, 6,
+ 0, FB_VMODE_NONINTERLACED
+ }, {
+ /* 640x480 @ 100 Hz, 53.01 kHz hsync */
+ NULL, 100, 640, 480, 21834, 96, 32, 36, 8, 96, 6,
+ 0, FB_VMODE_NONINTERLACED
+ }, {
+ /* 1152x864 @ 60 Hz, 53.5 kHz hsync */
+ NULL, 60, 1152, 864, 11123, 208, 64, 16, 4, 256, 8,
+ 0, FB_VMODE_NONINTERLACED
+ }, {
+ /* 800x600 @ 85 Hz, 55.84 kHz hsync */
+ NULL, 85, 800, 600, 16460, 160, 64, 36, 16, 64, 5,
+ 0, FB_VMODE_NONINTERLACED
+ }, {
+ /* 1024x768 @ 70 Hz, 56.5 kHz hsync */
+ NULL, 70, 1024, 768, 13333, 144, 24, 29, 3, 136, 6,
+ 0, FB_VMODE_NONINTERLACED
+ }, {
+ /* 1280x960-60 VESA */
+ NULL, 60, 1280, 960, 9259, 312, 96, 36, 1, 112, 3,
+ FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA
+ }, {
+ /* 1280x1024-60 VESA */
+ NULL, 60, 1280, 1024, 9259, 248, 48, 38, 1, 112, 3,
+ FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA
+ }, {
+ /* 1280x1024 @ 87 Hz interlaced, 51 kHz hsync */
+ NULL, 87, 1280, 1024, 12500, 56, 16, 128, 1, 216, 12,
+ 0, FB_VMODE_INTERLACED
+ }, {
+ /* 800x600 @ 100 Hz, 64.02 kHz hsync */
+ NULL, 100, 800, 600, 14357, 160, 64, 30, 4, 64, 6,
+ 0, FB_VMODE_NONINTERLACED
+ }, {
+ /* 1024x768 @ 76 Hz, 62.5 kHz hsync */
+ NULL, 76, 1024, 768, 11764, 208, 8, 36, 16, 120, 3,
+ 0, FB_VMODE_NONINTERLACED
+ }, {
+ /* 1152x864 @ 70 Hz, 62.4 kHz hsync */
+ NULL, 70, 1152, 864, 10869, 106, 56, 20, 1, 160, 10,
+ 0, FB_VMODE_NONINTERLACED
+ }, {
+ /* 1280x1024 @ 61 Hz, 64.2 kHz hsync */
+ NULL, 61, 1280, 1024, 9090, 200, 48, 26, 1, 184, 3,
+ 0, FB_VMODE_NONINTERLACED
+ }, {
+ /* 1400x1050 @ 60Hz, 63.9 kHz hsync */
+ NULL, 68, 1400, 1050, 9259, 136, 40, 13, 1, 112, 3,
+ 0, FB_VMODE_NONINTERLACED
+ }, {
+ /* 1400x1050 @ 75,107 Hz, 82,392 kHz +hsync +vsync*/
+ NULL, 75, 1400, 1050, 9271, 120, 56, 13, 0, 112, 3,
+ FB_SYNC_HOR_HIGH_ACT|FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED
+ }, {
+ /* 1400x1050 @ 60 Hz, ? kHz +hsync +vsync*/
+ NULL, 60, 1400, 1050, 9259, 128, 40, 12, 0, 112, 3,
+ FB_SYNC_HOR_HIGH_ACT|FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED
+ }, {
+ /* 1024x768 @ 85 Hz, 70.24 kHz hsync */
+ NULL, 85, 1024, 768, 10111, 192, 32, 34, 14, 160, 6,
+ 0, FB_VMODE_NONINTERLACED
+ }, {
+ /* 1152x864 @ 78 Hz, 70.8 kHz hsync */
+ NULL, 78, 1152, 864, 9090, 228, 88, 32, 0, 84, 12,
+ 0, FB_VMODE_NONINTERLACED
+ }, {
+ /* 1280x1024 @ 70 Hz, 74.59 kHz hsync */
+ NULL, 70, 1280, 1024, 7905, 224, 32, 28, 8, 160, 8,
+ 0, FB_VMODE_NONINTERLACED
+ }, {
+ /* 1600x1200 @ 60Hz, 75.00 kHz hsync */
+ NULL, 60, 1600, 1200, 6172, 304, 64, 46, 1, 192, 3,
+ FB_SYNC_HOR_HIGH_ACT|FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED
+ }, {
+ /* 1152x864 @ 84 Hz, 76.0 kHz hsync */
+ NULL, 84, 1152, 864, 7407, 184, 312, 32, 0, 128, 12,
+ 0, FB_VMODE_NONINTERLACED
+ }, {
+ /* 1280x1024 @ 74 Hz, 78.85 kHz hsync */
+ NULL, 74, 1280, 1024, 7407, 256, 32, 34, 3, 144, 3,
+ 0, FB_VMODE_NONINTERLACED
+ }, {
+ /* 1024x768 @ 100Hz, 80.21 kHz hsync */
+ NULL, 100, 1024, 768, 8658, 192, 32, 21, 3, 192, 10,
+ 0, FB_VMODE_NONINTERLACED
+ }, {
+ /* 1280x1024 @ 76 Hz, 81.13 kHz hsync */
+ NULL, 76, 1280, 1024, 7407, 248, 32, 34, 3, 104, 3,
+ 0, FB_VMODE_NONINTERLACED
+ }, {
+ /* 1600x1200 @ 70 Hz, 87.50 kHz hsync */
+ NULL, 70, 1600, 1200, 5291, 304, 64, 46, 1, 192, 3,
+ 0, FB_VMODE_NONINTERLACED
+ }, {
+ /* 1152x864 @ 100 Hz, 89.62 kHz hsync */
+ NULL, 100, 1152, 864, 7264, 224, 32, 17, 2, 128, 19,
+ 0, FB_VMODE_NONINTERLACED
+ }, {
+ /* 1280x1024 @ 85 Hz, 91.15 kHz hsync */
+ NULL, 85, 1280, 1024, 6349, 224, 64, 44, 1, 160, 3,
+ FB_SYNC_HOR_HIGH_ACT|FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED
+ }, {
+ /* 1600x1200 @ 75 Hz, 93.75 kHz hsync */
+ NULL, 75, 1600, 1200, 4938, 304, 64, 46, 1, 192, 3,
+ FB_SYNC_HOR_HIGH_ACT|FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED
+ }, {
+ /* 1600x1200 @ 85 Hz, 105.77 kHz hsync */
+ NULL, 85, 1600, 1200, 4545, 272, 16, 37, 4, 192, 3,
+ FB_SYNC_HOR_HIGH_ACT|FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED
+ }, {
+ /* 1280x1024 @ 100 Hz, 107.16 kHz hsync */
+ NULL, 100, 1280, 1024, 5502, 256, 32, 26, 7, 128, 15,
+ 0, FB_VMODE_NONINTERLACED
+ }, {
+ /* 1800x1440 @ 64Hz, 96.15 kHz hsync */
+ NULL, 64, 1800, 1440, 4347, 304, 96, 46, 1, 192, 3,
+ FB_SYNC_HOR_HIGH_ACT|FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED
+ }, {
+ /* 1800x1440 @ 70Hz, 104.52 kHz hsync */
+ NULL, 70, 1800, 1440, 4000, 304, 96, 46, 1, 192, 3,
+ FB_SYNC_HOR_HIGH_ACT|FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED
+ }, {
+ /* 512x384 @ 78 Hz, 31.50 kHz hsync */
+ NULL, 78, 512, 384, 49603, 48, 16, 16, 1, 64, 3,
+ 0, FB_VMODE_NONINTERLACED
+ }, {
+ /* 512x384 @ 85 Hz, 34.38 kHz hsync */
+ NULL, 85, 512, 384, 45454, 48, 16, 16, 1, 64, 3,
+ 0, FB_VMODE_NONINTERLACED
+ }, {
+ /* 320x200 @ 70 Hz, 31.5 kHz hsync, 8:5 aspect ratio */
+ NULL, 70, 320, 200, 79440, 16, 16, 20, 4, 48, 1,
+ 0, FB_VMODE_DOUBLE
+ }, {
+ /* 320x240 @ 60 Hz, 31.5 kHz hsync, 4:3 aspect ratio */
+ NULL, 60, 320, 240, 79440, 16, 16, 16, 5, 48, 1,
+ 0, FB_VMODE_DOUBLE
+ }, {
+ /* 320x240 @ 72 Hz, 36.5 kHz hsync */
+ NULL, 72, 320, 240, 63492, 16, 16, 16, 4, 48, 2,
+ 0, FB_VMODE_DOUBLE
+ }, {
+ /* 400x300 @ 56 Hz, 35.2 kHz hsync, 4:3 aspect ratio */
+ NULL, 56, 400, 300, 55555, 64, 16, 10, 1, 32, 1,
+ 0, FB_VMODE_DOUBLE
+ }, {
+ /* 400x300 @ 60 Hz, 37.8 kHz hsync */
+ NULL, 60, 400, 300, 50000, 48, 16, 11, 1, 64, 2,
+ 0, FB_VMODE_DOUBLE
+ }, {
+ /* 400x300 @ 72 Hz, 48.0 kHz hsync */
+ NULL, 72, 400, 300, 40000, 32, 24, 11, 19, 64, 3,
+ 0, FB_VMODE_DOUBLE
+ }, {
+ /* 480x300 @ 56 Hz, 35.2 kHz hsync, 8:5 aspect ratio */
+ NULL, 56, 480, 300, 46176, 80, 16, 10, 1, 40, 1,
+ 0, FB_VMODE_DOUBLE
+ }, {
+ /* 480x300 @ 60 Hz, 37.8 kHz hsync */
+ NULL, 60, 480, 300, 41858, 56, 16, 11, 1, 80, 2,
+ 0, FB_VMODE_DOUBLE
+ }, {
+ /* 480x300 @ 63 Hz, 39.6 kHz hsync */
+ NULL, 63, 480, 300, 40000, 56, 16, 11, 1, 80, 2,
+ 0, FB_VMODE_DOUBLE
+ }, {
+ /* 480x300 @ 72 Hz, 48.0 kHz hsync */
+ NULL, 72, 480, 300, 33386, 40, 24, 11, 19, 80, 3,
+ 0, FB_VMODE_DOUBLE
+ },
+};
+static const int nmodedb2 = sizeof(modedb2);
diff --git a/drivers/staging/sm750fb/readme b/drivers/staging/sm750fb/readme
new file mode 100644
index 000000000..ab9af7916
--- /dev/null
+++ b/drivers/staging/sm750fb/readme
@@ -0,0 +1,38 @@
+Introduction:
+ SM750 of Silicon MOtion is pci express display controller device.
+ The SM750 embedded graphics features include:
+ - dual display
+ - 2D acceleration
+ - 16MB integrated video memory
+
+About the kernel module paramter of driver:
+
+ Use 1280,8bpp index color and 60 hz mode:
+ insmod ./sm750fb.ko g_option="1280x1024-8@60"
+
+ Disable MTRR,Disable 2d acceleration,Disable hardware cursor,
+ and use a 800x600 mode :
+ insmod ./sm750fb.ko g_option="noaccel:nomtrr:nohwc:800x600"
+
+ dual frame buffer for driver with "dual" parameter
+ insmod ./sm750fb.ko g_option="dual,800x600:1024x768"
+ it will create fb0 and fb1 (or fb1,fb2 if fb0 already exist) under /dev
+ and user can use con2fb to link fbX and ttyX
+
+ Notes:
+ 1) if you build the driver with built-in method, the paramter
+ you edited in the grub config file will be also the
+ same format as above modular method,but additionaly add
+ "video=sm750fb:"
+ ahead of parameters,so,it looks like:
+ video=sm750fb:noaccel,1280x1024@60,otherparam,etc...
+ it equal to modular method with below command:
+ insmod ./sm750fb.ko g_option="noaccel:1280x1024@60:otherparm:etc..."
+
+ 2) if you put 800x600 into the paramter without bpp and
+ refresh rate, kernel driver will defaulty use 16bpp and 60hz
+
+Important:
+ if you have vesafb enabled in your config then /dev/fb0 will be created by vesafb
+ and this driver will use fb1, fb2. In that case, you need to configure your X-server
+ to use fb1. Another simple althernative is to disable vesafb from your config.
diff --git a/drivers/staging/sm750fb/sm750.c b/drivers/staging/sm750fb/sm750.c
new file mode 100644
index 000000000..dbbb2f879
--- /dev/null
+++ b/drivers/staging/sm750fb/sm750.c
@@ -0,0 +1,1403 @@
+#include<linux/kernel.h>
+#include<linux/module.h>
+#include<linux/errno.h>
+#include<linux/string.h>
+#include<linux/mm.h>
+#include<linux/slab.h>
+#include<linux/delay.h>
+#include<linux/fb.h>
+#include<linux/ioport.h>
+#include<linux/init.h>
+#include<linux/pci.h>
+#include<linux/mm_types.h>
+#include<linux/vmalloc.h>
+#include<linux/pagemap.h>
+#include<linux/screen_info.h>
+#include<linux/vmalloc.h>
+#include<linux/pagemap.h>
+#include <linux/console.h>
+#ifdef CONFIG_MTRR
+#include <asm/mtrr.h>
+#endif
+#include <asm/fb.h>
+#include "sm750.h"
+#include "sm750_hw.h"
+#include "sm750_accel.h"
+#include "sm750_cursor.h"
+
+#include "modedb.h"
+
+int smi_indent = 0;
+
+
+/*
+ * #ifdef __BIG_ENDIAN
+ * ssize_t lynxfb_ops_write(struct fb_info *info, const char __user *buf,
+ * size_t count, loff_t *ppos);
+ * ssize_t lynxfb_ops_read(struct fb_info *info, char __user *buf,
+ * size_t count, loff_t *ppos);
+ * #endif
+ */
+
+typedef void (*PROC_SPEC_SETUP)(struct lynx_share*, char *);
+typedef int (*PROC_SPEC_MAP)(struct lynx_share*, struct pci_dev*);
+typedef int (*PROC_SPEC_INITHW)(struct lynx_share*, struct pci_dev*);
+
+
+/* common var for all device */
+static int g_hwcursor = 1;
+static int g_noaccel;
+#ifdef CONFIG_MTRR
+static int g_nomtrr;
+#endif
+static const char *g_fbmode[] = {NULL, NULL};
+static const char *g_def_fbmode = "800x600-16@60";
+static char *g_settings = NULL;
+static int g_dualview;
+static char *g_option = NULL;
+
+
+static const struct fb_videomode lynx750_ext[] = {
+ /* 1024x600-60 VESA [1.71:1] */
+ {NULL, 60, 1024, 600, 20423, 144, 40, 18, 1, 104, 3,
+ FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
+ FB_VMODE_NONINTERLACED},
+
+ /* 1024x600-70 VESA */
+ {NULL, 70, 1024, 600, 17211, 152, 48, 21, 1, 104, 3,
+ FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
+ FB_VMODE_NONINTERLACED},
+
+ /* 1024x600-75 VESA */
+ {NULL, 75, 1024, 600, 15822, 160, 56, 23, 1, 104, 3,
+ FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
+ FB_VMODE_NONINTERLACED},
+
+ /* 1024x600-85 VESA */
+ {NULL, 85, 1024, 600, 13730, 168, 56, 26, 1, 112, 3,
+ FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
+ FB_VMODE_NONINTERLACED},
+
+ /* 720x480 */
+ {NULL, 60, 720, 480, 37427, 88, 16, 13, 1, 72, 3,
+ FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
+ FB_VMODE_NONINTERLACED},
+
+ /* 1280x720 [1.78:1] */
+ {NULL, 60, 1280, 720, 13426, 162, 86, 22, 1, 136, 3,
+ FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
+ FB_VMODE_NONINTERLACED},
+
+ /* 1280x768@60 */
+ {NULL, 60, 1280, 768, 12579, 192, 64, 20, 3, 128, 7,
+ FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
+ FB_VMODE_NONINTERLACED},
+
+ {NULL, 60, 1360, 768, 11804, 208, 64, 23, 1, 144, 3,
+ FB_SYNC_HOR_HIGH_ACT|FB_VMODE_NONINTERLACED},
+
+ /* 1360 x 768 [1.77083:1] */
+ {NULL, 60, 1360, 768, 11804, 208, 64, 23, 1, 144, 3,
+ FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
+ FB_VMODE_NONINTERLACED},
+
+ /* 1368 x 768 [1.78:1] */
+ {NULL, 60, 1368, 768, 11647, 216, 72, 23, 1, 144, 3,
+ FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
+ FB_VMODE_NONINTERLACED},
+
+ /* 1440 x 900 [16:10] */
+ {NULL, 60, 1440, 900, 9392, 232, 80, 28, 1, 152, 3,
+ FB_SYNC_VERT_HIGH_ACT,
+ FB_VMODE_NONINTERLACED},
+
+ /* 1440x960 [15:10] */
+ {NULL, 60, 1440, 960, 8733, 240, 88, 30, 1, 152, 3,
+ FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
+ FB_VMODE_NONINTERLACED},
+
+ /* 1920x1080 [16:9] */
+ {NULL, 60, 1920, 1080, 6734, 148, 88, 41, 1, 44, 3,
+ FB_SYNC_VERT_HIGH_ACT,
+ FB_VMODE_NONINTERLACED},
+};
+
+
+
+
+/* no hardware cursor supported under version 2.6.10, kernel bug */
+static int lynxfb_ops_cursor(struct fb_info *info, struct fb_cursor *fbcursor)
+{
+ struct lynxfb_par *par;
+ struct lynxfb_crtc *crtc;
+ struct lynx_cursor *cursor;
+
+ par = info->par;
+ crtc = &par->crtc;
+ cursor = &crtc->cursor;
+
+ if (fbcursor->image.width > cursor->maxW ||
+ fbcursor->image.height > cursor->maxH ||
+ fbcursor->image.depth > 1) {
+ return -ENXIO;
+ }
+
+ cursor->disable(cursor);
+ if (fbcursor->set & FB_CUR_SETSIZE)
+ cursor->setSize(cursor,
+ fbcursor->image.width,
+ fbcursor->image.height);
+
+ if (fbcursor->set & FB_CUR_SETPOS)
+ cursor->setPos(cursor,
+ fbcursor->image.dx - info->var.xoffset,
+ fbcursor->image.dy - info->var.yoffset);
+
+ if (fbcursor->set & FB_CUR_SETCMAP) {
+ /* get the 16bit color of kernel means */
+ u16 fg, bg;
+
+ fg = ((info->cmap.red[fbcursor->image.fg_color] & 0xf800))|
+ ((info->cmap.green[fbcursor->image.fg_color] & 0xfc00) >> 5)|
+ ((info->cmap.blue[fbcursor->image.fg_color] & 0xf800) >> 11);
+
+ bg = ((info->cmap.red[fbcursor->image.bg_color] & 0xf800))|
+ ((info->cmap.green[fbcursor->image.bg_color] & 0xfc00) >> 5)|
+ ((info->cmap.blue[fbcursor->image.bg_color] & 0xf800) >> 11);
+
+ cursor->setColor(cursor, fg, bg);
+ }
+
+
+ if (fbcursor->set & (FB_CUR_SETSHAPE | FB_CUR_SETIMAGE)) {
+ cursor->setData(cursor,
+ fbcursor->rop,
+ fbcursor->image.data,
+ fbcursor->mask);
+ }
+
+ if (fbcursor->enable)
+ cursor->enable(cursor);
+
+ return 0;
+}
+
+static void lynxfb_ops_fillrect(struct fb_info *info,
+ const struct fb_fillrect *region)
+{
+ struct lynxfb_par *par;
+ struct lynx_share *share;
+ unsigned int base, pitch, Bpp, rop;
+ u32 color;
+
+ if (info->state != FBINFO_STATE_RUNNING)
+ return;
+
+ par = info->par;
+ share = par->share;
+
+ /* each time 2d function begin to work,below three variable always need
+ * be set, seems we can put them together in some place */
+ base = par->crtc.oScreen;
+ pitch = info->fix.line_length;
+ Bpp = info->var.bits_per_pixel >> 3;
+
+ color = (Bpp == 1)?region->color:((u32 *)info->pseudo_palette)[region->color];
+ rop = (region->rop != ROP_COPY) ? HW_ROP2_XOR:HW_ROP2_COPY;
+
+ /*
+ * If not use spin_lock,system will die if user load driver
+ * and immediatly unload driver frequently (dual)
+ */
+ if (share->dual)
+ spin_lock(&share->slock);
+
+ share->accel.de_fillrect(&share->accel,
+ base, pitch, Bpp,
+ region->dx, region->dy,
+ region->width, region->height,
+ color, rop);
+ if (share->dual)
+ spin_unlock(&share->slock);
+}
+
+static void lynxfb_ops_copyarea(struct fb_info *info,
+ const struct fb_copyarea *region)
+{
+ struct lynxfb_par *par;
+ struct lynx_share *share;
+ unsigned int base, pitch, Bpp;
+
+ par = info->par;
+ share = par->share;
+
+ /* each time 2d function begin to work,below three variable always need
+ * be set, seems we can put them together in some place */
+ base = par->crtc.oScreen;
+ pitch = info->fix.line_length;
+ Bpp = info->var.bits_per_pixel >> 3;
+
+ /*
+ * If not use spin_lock, system will die if user load driver
+ * and immediatly unload driver frequently (dual)
+ */
+ if (share->dual)
+ spin_lock(&share->slock);
+
+ share->accel.de_copyarea(&share->accel,
+ base, pitch, region->sx, region->sy,
+ base, pitch, Bpp, region->dx, region->dy,
+ region->width, region->height, HW_ROP2_COPY);
+ if (share->dual)
+ spin_unlock(&share->slock);
+}
+
+static void lynxfb_ops_imageblit(struct fb_info *info,
+ const struct fb_image *image)
+{
+ unsigned int base, pitch, Bpp;
+ unsigned int fgcol, bgcol;
+ struct lynxfb_par *par;
+ struct lynx_share *share;
+
+ par = info->par;
+ share = par->share;
+ /* each time 2d function begin to work,below three variable always need
+ * be set, seems we can put them together in some place */
+ base = par->crtc.oScreen;
+ pitch = info->fix.line_length;
+ Bpp = info->var.bits_per_pixel >> 3;
+
+ if (image->depth == 1) {
+ if (info->fix.visual == FB_VISUAL_TRUECOLOR ||
+ info->fix.visual == FB_VISUAL_DIRECTCOLOR) {
+ fgcol = ((u32 *)info->pseudo_palette)[image->fg_color];
+ bgcol = ((u32 *)info->pseudo_palette)[image->bg_color];
+ } else {
+ fgcol = image->fg_color;
+ bgcol = image->bg_color;
+ }
+ goto _do_work;
+ }
+ return;
+_do_work:
+ /*
+ * If not use spin_lock, system will die if user load driver
+ * and immediatly unload driver frequently (dual)
+ */
+ if (share->dual)
+ spin_lock(&share->slock);
+
+ share->accel.de_imageblit(&share->accel,
+ image->data, image->width>>3, 0,
+ base, pitch, Bpp,
+ image->dx, image->dy,
+ image->width, image->height,
+ fgcol, bgcol, HW_ROP2_COPY);
+ if (share->dual)
+ spin_unlock(&share->slock);
+}
+
+static int lynxfb_ops_pan_display(struct fb_var_screeninfo *var,
+ struct fb_info *info)
+{
+ struct lynxfb_par *par;
+ struct lynxfb_crtc *crtc;
+ int ret;
+
+
+ if (!info)
+ return -EINVAL;
+
+ ret = 0;
+ par = info->par;
+ crtc = &par->crtc;
+ ret = crtc->proc_panDisplay(crtc, var, info);
+
+ return ret;
+}
+
+static int lynxfb_ops_set_par(struct fb_info *info)
+{
+ struct lynxfb_par *par;
+ struct lynx_share *share;
+ struct lynxfb_crtc *crtc;
+ struct lynxfb_output *output;
+ struct fb_var_screeninfo *var;
+ struct fb_fix_screeninfo *fix;
+ int ret;
+ unsigned int line_length;
+
+ if (!info)
+ return -EINVAL;
+
+ ret = 0;
+ par = info->par;
+ share = par->share;
+ crtc = &par->crtc;
+ output = &par->output;
+ var = &info->var;
+ fix = &info->fix;
+
+ /* fix structur is not so FIX ... */
+ line_length = var->xres_virtual * var->bits_per_pixel / 8;
+ line_length = PADDING(crtc->line_pad, line_length);
+ fix->line_length = line_length;
+ pr_err("fix->line_length = %d\n", fix->line_length);
+
+ /* var->red,green,blue,transp are need to be set by driver
+ * and these data should be set before setcolreg routine
+ * */
+
+ switch (var->bits_per_pixel) {
+ case 8:
+ fix->visual = FB_VISUAL_PSEUDOCOLOR;
+ var->red.offset = 0;
+ var->red.length = 8;
+ var->green.offset = 0;
+ var->green.length = 8;
+ var->blue.offset = 0;
+ var->blue.length = 8;
+ var->transp.length = 0;
+ var->transp.offset = 0;
+ break;
+ case 16:
+ var->red.offset = 11;
+ var->red.length = 5;
+ var->green.offset = 5;
+ var->green.length = 6;
+ var->blue.offset = 0;
+ var->blue.length = 5;
+ var->transp.length = 0;
+ var->transp.offset = 0;
+ fix->visual = FB_VISUAL_TRUECOLOR;
+ break;
+ case 24:
+ case 32:
+ var->red.offset = 16;
+ var->red.length = 8;
+ var->green.offset = 8;
+ var->green.length = 8;
+ var->blue.offset = 0;
+ var->blue.length = 8;
+ fix->visual = FB_VISUAL_TRUECOLOR;
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+ var->height = var->width = -1;
+ var->accel_flags = 0;/*FB_ACCELF_TEXT;*/
+
+ if (ret) {
+ pr_err("pixel bpp format not satisfied\n.");
+ return ret;
+ }
+ ret = crtc->proc_setMode(crtc, var, fix);
+ if (!ret)
+ ret = output->proc_setMode(output, var, fix);
+ return ret;
+}
+
+static inline unsigned int chan_to_field(unsigned int chan,
+ struct fb_bitfield *bf)
+{
+ chan &= 0xffff;
+ chan >>= 16 - bf->length;
+ return chan << bf->offset;
+}
+
+#ifdef CONFIG_PM
+static int lynxfb_suspend(struct pci_dev *pdev, pm_message_t mesg)
+{
+ struct fb_info *info;
+ struct lynx_share *share;
+ int ret;
+
+ if (mesg.event == pdev->dev.power.power_state.event)
+ return 0;
+
+ ret = 0;
+ share = pci_get_drvdata(pdev);
+ switch (mesg.event) {
+ case PM_EVENT_FREEZE:
+ case PM_EVENT_PRETHAW:
+ pdev->dev.power.power_state = mesg;
+ return 0;
+ }
+
+ console_lock();
+ if (mesg.event & PM_EVENT_SLEEP) {
+ info = share->fbinfo[0];
+ if (info)
+ /* 1 means do suspend */
+ fb_set_suspend(info, 1);
+ info = share->fbinfo[1];
+ if (info)
+ /* 1 means do suspend */
+ fb_set_suspend(info, 1);
+
+ ret = pci_save_state(pdev);
+ if (ret) {
+ pr_err("error:%d occurred in pci_save_state\n", ret);
+ return ret;
+ }
+
+ /* set chip to sleep mode */
+ if (share->suspend)
+ (*share->suspend)(share);
+
+ pci_disable_device(pdev);
+ ret = pci_set_power_state(pdev, pci_choose_state(pdev, mesg));
+ if (ret) {
+ pr_err("error:%d occurred in pci_set_power_state\n", ret);
+ return ret;
+ }
+ }
+
+ pdev->dev.power.power_state = mesg;
+ console_unlock();
+ return ret;
+}
+
+static int lynxfb_resume(struct pci_dev *pdev)
+{
+ struct fb_info *info;
+ struct lynx_share *share;
+
+ struct lynxfb_par *par;
+ struct lynxfb_crtc *crtc;
+ struct lynx_cursor *cursor;
+
+ int ret;
+
+
+ ret = 0;
+ share = pci_get_drvdata(pdev);
+
+ console_lock();
+
+ ret = pci_set_power_state(pdev, PCI_D0);
+ if (ret) {
+ pr_err("error:%d occured in pci_set_power_state\n", ret);
+ return ret;
+ }
+
+
+ if (pdev->dev.power.power_state.event != PM_EVENT_FREEZE) {
+ pci_restore_state(pdev);
+ ret = pci_enable_device(pdev);
+ if (ret) {
+ pr_err("error:%d occured in pci_enable_device\n", ret);
+ return ret;
+ }
+ pci_set_master(pdev);
+ }
+ if (share->resume)
+ (*share->resume)(share);
+
+ hw_sm750_inithw(share, pdev);
+
+
+ info = share->fbinfo[0];
+
+ if (info) {
+ par = info->par;
+ crtc = &par->crtc;
+ cursor = &crtc->cursor;
+ memset_io(cursor->vstart, 0x0, cursor->size);
+ memset_io(crtc->vScreen, 0x0, crtc->vidmem_size);
+ lynxfb_ops_set_par(info);
+ fb_set_suspend(info, 0);
+ }
+
+ info = share->fbinfo[1];
+
+ if (info) {
+ par = info->par;
+ crtc = &par->crtc;
+ cursor = &crtc->cursor;
+ memset_io(cursor->vstart, 0x0, cursor->size);
+ memset_io(crtc->vScreen, 0x0, crtc->vidmem_size);
+ lynxfb_ops_set_par(info);
+ fb_set_suspend(info, 0);
+ }
+
+
+ console_unlock();
+ return ret;
+}
+#endif
+
+static int lynxfb_ops_check_var(struct fb_var_screeninfo *var,
+ struct fb_info *info)
+{
+ struct lynxfb_par *par;
+ struct lynxfb_crtc *crtc;
+ struct lynxfb_output *output;
+ struct lynx_share *share;
+ int ret;
+ resource_size_t request;
+
+
+ par = info->par;
+ crtc = &par->crtc;
+ output = &par->output;
+ share = par->share;
+ ret = 0;
+
+ pr_debug("check var:%dx%d-%d\n",
+ var->xres,
+ var->yres,
+ var->bits_per_pixel);
+
+
+ switch (var->bits_per_pixel) {
+ case 8:
+ case 16:
+ case 24: /* support 24 bpp for only lynx712/722/720 */
+ case 32:
+ break;
+ default:
+ pr_err("bpp %d not supported\n", var->bits_per_pixel);
+ ret = -EINVAL;
+ goto exit;
+ }
+
+ switch (var->bits_per_pixel) {
+ case 8:
+ info->fix.visual = FB_VISUAL_PSEUDOCOLOR;
+ var->red.offset = 0;
+ var->red.length = 8;
+ var->green.offset = 0;
+ var->green.length = 8;
+ var->blue.offset = 0;
+ var->blue.length = 8;
+ var->transp.length = 0;
+ var->transp.offset = 0;
+ break;
+ case 16:
+ var->red.offset = 11;
+ var->red.length = 5;
+ var->green.offset = 5;
+ var->green.length = 6;
+ var->blue.offset = 0;
+ var->blue.length = 5;
+ var->transp.length = 0;
+ var->transp.offset = 0;
+ info->fix.visual = FB_VISUAL_TRUECOLOR;
+ break;
+ case 24:
+ case 32:
+ var->red.offset = 16;
+ var->red.length = 8;
+ var->green.offset = 8;
+ var->green.length = 8;
+ var->blue.offset = 0;
+ var->blue.length = 8;
+ info->fix.visual = FB_VISUAL_TRUECOLOR;
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+ var->height = var->width = -1;
+ var->accel_flags = 0;/* FB_ACCELF_TEXT; */
+
+ /* check if current fb's video memory big enought to hold the onscreen*/
+ request = var->xres_virtual * (var->bits_per_pixel >> 3);
+ /* defaulty crtc->channel go with par->index */
+
+ request = PADDING(crtc->line_pad, request);
+ request = request * var->yres_virtual;
+ if (crtc->vidmem_size < request) {
+ pr_err("not enough video memory for mode\n");
+ return -ENOMEM;
+ }
+
+ ret = output->proc_checkMode(output, var);
+ if (!ret)
+ ret = crtc->proc_checkMode(crtc, var);
+exit:
+ return ret;
+}
+
+
+static int lynxfb_ops_setcolreg(unsigned regno,
+ unsigned red,
+ unsigned green,
+ unsigned blue,
+ unsigned transp,
+ struct fb_info *info)
+{
+ struct lynxfb_par *par;
+ struct lynxfb_crtc *crtc;
+ struct fb_var_screeninfo *var;
+ int ret;
+
+ par = info->par;
+ crtc = &par->crtc;
+ var = &info->var;
+ ret = 0;
+
+ if (regno > 256) {
+ pr_err("regno = %d\n", regno);
+ return -EINVAL;
+ }
+
+ if (info->var.grayscale)
+ red = green = blue = (red * 77 + green * 151 + blue * 28) >> 8;
+
+ if (var->bits_per_pixel == 8 &&
+ info->fix.visual == FB_VISUAL_PSEUDOCOLOR) {
+ red >>= 8;
+ green >>= 8;
+ blue >>= 8;
+ ret = crtc->proc_setColReg(crtc, regno, red, green, blue);
+ goto exit;
+ }
+
+
+ if (info->fix.visual == FB_VISUAL_TRUECOLOR && regno < 256) {
+ u32 val;
+
+ if (var->bits_per_pixel == 16 ||
+ var->bits_per_pixel == 32 ||
+ var->bits_per_pixel == 24) {
+ val = chan_to_field(red, &var->red);
+ val |= chan_to_field(green, &var->green);
+ val |= chan_to_field(blue, &var->blue);
+ par->pseudo_palette[regno] = val;
+ goto exit;
+ }
+ }
+
+ ret = -EINVAL;
+
+exit:
+ return ret;
+}
+
+static int lynxfb_ops_blank(int blank, struct fb_info *info)
+{
+ struct lynxfb_par *par;
+ struct lynxfb_output *output;
+
+ pr_debug("blank = %d.\n", blank);
+ par = info->par;
+ output = &par->output;
+ return output->proc_setBLANK(output, blank);
+}
+
+static int sm750fb_set_drv(struct lynxfb_par *par)
+{
+ int ret;
+ struct lynx_share *share;
+ struct sm750_share *spec_share;
+ struct lynxfb_output *output;
+ struct lynxfb_crtc *crtc;
+
+ ret = 0;
+
+ share = par->share;
+ spec_share = container_of(share, struct sm750_share, share);
+ output = &par->output;
+ crtc = &par->crtc;
+
+ crtc->vidmem_size = (share->dual)?share->vidmem_size>>1:share->vidmem_size;
+ /* setup crtc and output member */
+ spec_share->hwCursor = g_hwcursor;
+
+ crtc->proc_setMode = hw_sm750_crtc_setMode;
+ crtc->proc_checkMode = hw_sm750_crtc_checkMode;
+ crtc->proc_setColReg = hw_sm750_setColReg;
+ crtc->proc_panDisplay = hw_sm750_pan_display;
+ crtc->clear = hw_sm750_crtc_clear;
+ crtc->line_pad = 16;
+ crtc->xpanstep = 8;
+ crtc->ypanstep = 1;
+ crtc->ywrapstep = 0;
+
+ output->proc_setMode = hw_sm750_output_setMode;
+ output->proc_checkMode = hw_sm750_output_checkMode;
+
+ output->proc_setBLANK = (share->revid == SM750LE_REVISION_ID)?hw_sm750le_setBLANK:hw_sm750_setBLANK;
+ output->clear = hw_sm750_output_clear;
+ /* chip specific phase */
+ share->accel.de_wait = (share->revid == SM750LE_REVISION_ID)?hw_sm750le_deWait : hw_sm750_deWait;
+ switch (spec_share->state.dataflow) {
+ case sm750_simul_pri:
+ output->paths = sm750_pnc;
+ crtc->channel = sm750_primary;
+ crtc->oScreen = 0;
+ crtc->vScreen = share->pvMem;
+ pr_info("use simul primary mode\n");
+ break;
+ case sm750_simul_sec:
+ output->paths = sm750_pnc;
+ crtc->channel = sm750_secondary;
+ crtc->oScreen = 0;
+ crtc->vScreen = share->pvMem;
+ break;
+ case sm750_dual_normal:
+ if (par->index == 0) {
+ output->paths = sm750_panel;
+ crtc->channel = sm750_primary;
+ crtc->oScreen = 0;
+ crtc->vScreen = share->pvMem;
+ } else {
+ output->paths = sm750_crt;
+ crtc->channel = sm750_secondary;
+ /* not consider of padding stuffs for oScreen,need fix */
+ crtc->oScreen = (share->vidmem_size >> 1);
+ crtc->vScreen = share->pvMem + crtc->oScreen;
+ }
+ break;
+ case sm750_dual_swap:
+ if (par->index == 0) {
+ output->paths = sm750_panel;
+ crtc->channel = sm750_secondary;
+ crtc->oScreen = 0;
+ crtc->vScreen = share->pvMem;
+ } else {
+ output->paths = sm750_crt;
+ crtc->channel = sm750_primary;
+ /* not consider of padding stuffs for oScreen,need fix */
+ crtc->oScreen = (share->vidmem_size >> 1);
+ crtc->vScreen = share->pvMem + crtc->oScreen;
+ }
+ break;
+ default:
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+static struct fb_ops lynxfb_ops = {
+ .owner = THIS_MODULE,
+ .fb_check_var = lynxfb_ops_check_var,
+ .fb_set_par = lynxfb_ops_set_par,
+ .fb_setcolreg = lynxfb_ops_setcolreg,
+ .fb_blank = lynxfb_ops_blank,
+ .fb_fillrect = cfb_fillrect,
+ .fb_imageblit = cfb_imageblit,
+ .fb_copyarea = cfb_copyarea,
+ /* cursor */
+ .fb_cursor = lynxfb_ops_cursor,
+};
+
+
+static int lynxfb_set_fbinfo(struct fb_info *info, int index)
+{
+ int i;
+ struct lynxfb_par *par;
+ struct lynx_share *share;
+ struct lynxfb_crtc *crtc;
+ struct lynxfb_output *output;
+ struct fb_var_screeninfo *var;
+ struct fb_fix_screeninfo *fix;
+
+ const struct fb_videomode *pdb[] = {
+ lynx750_ext, NULL, vesa_modes,
+ };
+ int cdb[] = {ARRAY_SIZE(lynx750_ext), 0, VESA_MODEDB_SIZE};
+ static const char *mdb_desc[] = {
+ "driver prepared modes",
+ "kernel prepared default modedb",
+ "kernel HELPERS prepared vesa_modes",
+ };
+
+
+ static const char *fixId[2] = {
+ "sm750_fb1", "sm750_fb2",
+ };
+
+ int ret, line_length;
+
+ ret = 0;
+ par = (struct lynxfb_par *)info->par;
+ share = par->share;
+ crtc = &par->crtc;
+ output = &par->output;
+ var = &info->var;
+ fix = &info->fix;
+
+ /* set index */
+ par->index = index;
+ output->channel = &crtc->channel;
+ sm750fb_set_drv(par);
+ lynxfb_ops.fb_pan_display = lynxfb_ops_pan_display;
+
+
+ /* set current cursor variable and proc pointer,
+ * must be set after crtc member initialized */
+ crtc->cursor.offset = crtc->oScreen + crtc->vidmem_size - 1024;
+ crtc->cursor.mmio = share->pvReg + 0x800f0 + (int)crtc->channel * 0x140;
+
+ pr_info("crtc->cursor.mmio = %p\n", crtc->cursor.mmio);
+ crtc->cursor.maxH = crtc->cursor.maxW = 64;
+ crtc->cursor.size = crtc->cursor.maxH*crtc->cursor.maxW*2/8;
+ crtc->cursor.disable = hw_cursor_disable;
+ crtc->cursor.enable = hw_cursor_enable;
+ crtc->cursor.setColor = hw_cursor_setColor;
+ crtc->cursor.setPos = hw_cursor_setPos;
+ crtc->cursor.setSize = hw_cursor_setSize;
+ crtc->cursor.setData = hw_cursor_setData;
+ crtc->cursor.vstart = share->pvMem + crtc->cursor.offset;
+
+
+ crtc->cursor.share = share;
+ memset_io(crtc->cursor.vstart, 0, crtc->cursor.size);
+ if (!g_hwcursor) {
+ lynxfb_ops.fb_cursor = NULL;
+ crtc->cursor.disable(&crtc->cursor);
+ }
+
+
+ /* set info->fbops, must be set before fb_find_mode */
+ if (!share->accel_off) {
+ /* use 2d acceleration */
+ lynxfb_ops.fb_fillrect = lynxfb_ops_fillrect;
+ lynxfb_ops.fb_copyarea = lynxfb_ops_copyarea;
+ lynxfb_ops.fb_imageblit = lynxfb_ops_imageblit;
+ }
+ info->fbops = &lynxfb_ops;
+
+ if (!g_fbmode[index]) {
+ g_fbmode[index] = g_def_fbmode;
+ if (index)
+ g_fbmode[index] = g_fbmode[0];
+ }
+
+
+ for (i = 0; i < 3; i++) {
+
+ ret = fb_find_mode(var, info, g_fbmode[index],
+ pdb[i], cdb[i], NULL, 8);
+
+ if (ret == 1) {
+ pr_info("success! use specified mode:%s in %s\n",
+ g_fbmode[index],
+ mdb_desc[i]);
+ break;
+ } else if (ret == 2) {
+ pr_warn("use specified mode:%s in %s,with an ignored refresh rate\n",
+ g_fbmode[index],
+ mdb_desc[i]);
+ break;
+ } else if (ret == 3) {
+ pr_warn("wanna use default mode\n");
+ /*break;*/
+ } else if (ret == 4) {
+ pr_warn("fall back to any valid mode\n");
+ } else {
+ pr_warn("ret = %d,fb_find_mode failed,with %s\n",
+ ret,
+ mdb_desc[i]);
+ }
+ }
+
+ /* some member of info->var had been set by fb_find_mode */
+
+ pr_info("Member of info->var is :\n\
+ xres=%d\n\
+ yres=%d\n\
+ xres_virtual=%d\n\
+ yres_virtual=%d\n\
+ xoffset=%d\n\
+ yoffset=%d\n\
+ bits_per_pixel=%d\n \
+ ...\n",
+ var->xres,
+ var->yres,
+ var->xres_virtual,
+ var->yres_virtual,
+ var->xoffset,
+ var->yoffset,
+ var->bits_per_pixel);
+
+ /* set par */
+ par->info = info;
+
+ /* set info */
+ line_length = PADDING(crtc->line_pad,
+ (var->xres_virtual * var->bits_per_pixel/8));
+
+ info->pseudo_palette = &par->pseudo_palette[0];
+ info->screen_base = crtc->vScreen;
+ pr_debug("screen_base vaddr = %p\n", info->screen_base);
+ info->screen_size = line_length * var->yres_virtual;
+ info->flags = FBINFO_FLAG_DEFAULT|0;
+
+ /* set info->fix */
+ fix->type = FB_TYPE_PACKED_PIXELS;
+ fix->type_aux = 0;
+ fix->xpanstep = crtc->xpanstep;
+ fix->ypanstep = crtc->ypanstep;
+ fix->ywrapstep = crtc->ywrapstep;
+ fix->accel = FB_ACCEL_SMI;
+
+ strlcpy(fix->id, fixId[index], sizeof(fix->id));
+
+
+ fix->smem_start = crtc->oScreen + share->vidmem_start;
+ pr_info("fix->smem_start = %lx\n", fix->smem_start);
+ /* according to mmap experiment from user space application,
+ * fix->mmio_len should not larger than virtual size
+ * (xres_virtual x yres_virtual x ByPP)
+ * Below line maybe buggy when user mmap fb dev node and write
+ * data into the bound over virtual size
+ * */
+ fix->smem_len = crtc->vidmem_size;
+ pr_info("fix->smem_len = %x\n", fix->smem_len);
+ info->screen_size = fix->smem_len;
+ fix->line_length = line_length;
+ fix->mmio_start = share->vidreg_start;
+ pr_info("fix->mmio_start = %lx\n", fix->mmio_start);
+ fix->mmio_len = share->vidreg_size;
+ pr_info("fix->mmio_len = %x\n", fix->mmio_len);
+ switch (var->bits_per_pixel) {
+ case 8:
+ fix->visual = FB_VISUAL_PSEUDOCOLOR;
+ break;
+ case 16:
+ case 32:
+ fix->visual = FB_VISUAL_TRUECOLOR;
+ break;
+ }
+
+ /* set var */
+ var->activate = FB_ACTIVATE_NOW;
+ var->accel_flags = 0;
+ var->vmode = FB_VMODE_NONINTERLACED;
+
+ pr_debug("#1 show info->cmap : \nstart=%d,len=%d,red=%p,green=%p,blue=%p,transp=%p\n",
+ info->cmap.start, info->cmap.len,
+ info->cmap.red, info->cmap.green, info->cmap.blue,
+ info->cmap.transp);
+
+ ret = fb_alloc_cmap(&info->cmap, 256, 0);
+ if (ret < 0) {
+ pr_err("Could not allcate memory for cmap.\n");
+ goto exit;
+ }
+
+ pr_debug("#2 show info->cmap :\nstart=%d,len=%d,red=%p,green=%p,blue=%p,transp=%p\n",
+ info->cmap.start, info->cmap.len,
+ info->cmap.red, info->cmap.green, info->cmap.blue,
+ info->cmap.transp);
+
+exit:
+ lynxfb_ops_check_var(var, info);
+ return ret;
+}
+
+/* chip specific g_option configuration routine */
+static void sm750fb_setup(struct lynx_share *share, char *src)
+{
+ struct sm750_share *spec_share;
+ char *opt;
+#ifdef CAP_EXPENSION
+ char *exp_res;
+#endif
+ int swap;
+
+
+ spec_share = container_of(share, struct sm750_share, share);
+#ifdef CAP_EXPENSIION
+ exp_res = NULL;
+#endif
+ swap = 0;
+
+ spec_share->state.initParm.chip_clk = 0;
+ spec_share->state.initParm.mem_clk = 0;
+ spec_share->state.initParm.master_clk = 0;
+ spec_share->state.initParm.powerMode = 0;
+ spec_share->state.initParm.setAllEngOff = 0;
+ spec_share->state.initParm.resetMemory = 1;
+
+ /* defaultly turn g_hwcursor on for both view */
+ g_hwcursor = 3;
+
+ if (!src || !*src) {
+ pr_warn("no specific g_option.\n");
+ goto NO_PARAM;
+ }
+
+ while ((opt = strsep(&src, ":")) != NULL && *opt != 0) {
+ pr_err("opt=%s\n", opt);
+ pr_err("src=%s\n", src);
+
+ if (!strncmp(opt, "swap", strlen("swap")))
+ swap = 1;
+ else if (!strncmp(opt, "nocrt", strlen("nocrt")))
+ spec_share->state.nocrt = 1;
+ else if (!strncmp(opt, "36bit", strlen("36bit")))
+ spec_share->state.pnltype = sm750_doubleTFT;
+ else if (!strncmp(opt, "18bit", strlen("18bit")))
+ spec_share->state.pnltype = sm750_dualTFT;
+ else if (!strncmp(opt, "24bit", strlen("24bit")))
+ spec_share->state.pnltype = sm750_24TFT;
+#ifdef CAP_EXPANSION
+ else if (!strncmp(opt, "exp:", strlen("exp:")))
+ exp_res = opt + strlen("exp:");
+#endif
+ else if (!strncmp(opt, "nohwc0", strlen("nohwc0")))
+ g_hwcursor &= ~0x1;
+ else if (!strncmp(opt, "nohwc1", strlen("nohwc1")))
+ g_hwcursor &= ~0x2;
+ else if (!strncmp(opt, "nohwc", strlen("nohwc")))
+ g_hwcursor = 0;
+ else {
+ if (!g_fbmode[0]) {
+ g_fbmode[0] = opt;
+ pr_info("find fbmode0 : %s\n", g_fbmode[0]);
+ } else if (!g_fbmode[1]) {
+ g_fbmode[1] = opt;
+ pr_info("find fbmode1 : %s\n", g_fbmode[1]);
+ } else {
+ pr_warn("How many view you wann set?\n");
+ }
+ }
+ }
+#ifdef CAP_EXPANSION
+ if (getExpRes(exp_res,
+ &spec_share->state.xLCD,
+ &spec_share->state.yLCD)) {
+ /* seems exp_res is not valid */
+ spec_share->state.xLCD = spec_share->state.yLCD = 0;
+ }
+#endif
+
+NO_PARAM:
+ if (share->revid != SM750LE_REVISION_ID) {
+ if (share->dual) {
+ if (swap)
+ spec_share->state.dataflow = sm750_dual_swap;
+ else
+ spec_share->state.dataflow = sm750_dual_normal;
+ } else {
+ if (swap)
+ spec_share->state.dataflow = sm750_simul_sec;
+ else
+ spec_share->state.dataflow = sm750_simul_pri;
+ }
+ } else {
+ /* SM750LE only have one crt channel */
+ spec_share->state.dataflow = sm750_simul_sec;
+ /* sm750le do not have complex attributes */
+ spec_share->state.nocrt = 0;
+ }
+}
+
+static int lynxfb_pci_probe(struct pci_dev *pdev,
+ const struct pci_device_id * ent)
+{
+ struct fb_info *info[] = {NULL, NULL};
+ struct lynx_share *share = NULL;
+
+ struct sm750_share *spec_share = NULL;
+ size_t spec_offset = 0;
+ int fbidx;
+
+
+ /* enable device */
+ if (pci_enable_device(pdev)) {
+ pr_err("can not enable device.\n");
+ goto err_enable;
+ }
+
+ /* though offset of share in sm750_share is 0,
+ * we use this marcro as the same */
+ spec_offset = offsetof(struct sm750_share, share);
+
+ spec_share = kzalloc(sizeof(*spec_share), GFP_KERNEL);
+ if (!spec_share) {
+ pr_err("Could not allocate memory for share.\n");
+ goto err_share;
+ }
+
+ /* setting share structure */
+ share = (struct lynx_share *)(&(spec_share->share));
+ share->fbinfo[0] = share->fbinfo[1] = NULL;
+ share->devid = pdev->device;
+ share->revid = pdev->revision;
+
+ pr_info("share->revid = %02x\n", share->revid);
+ share->pdev = pdev;
+#ifdef CONFIG_MTRR
+ share->mtrr_off = g_nomtrr;
+ share->mtrr.vram = 0;
+ share->mtrr.vram_added = 0;
+#endif
+ share->accel_off = g_noaccel;
+ share->dual = g_dualview;
+ spin_lock_init(&share->slock);
+
+ if (!share->accel_off) {
+ /* hook deInit and 2d routines, notes that below hw_xxx
+ * routine can work on most of lynx chips
+ * if some chip need specific function,
+ * please hook it in smXXX_set_drv routine */
+ share->accel.de_init = hw_de_init;
+ share->accel.de_fillrect = hw_fillrect;
+ share->accel.de_copyarea = hw_copyarea;
+ share->accel.de_imageblit = hw_imageblit;
+ pr_info("enable 2d acceleration\n");
+ } else {
+ pr_info("disable 2d acceleration\n");
+ }
+
+ /* call chip specific setup routine */
+ sm750fb_setup(share, g_settings);
+
+ /* call chip specific mmap routine */
+ if (hw_sm750_map(share, pdev)) {
+ pr_err("Memory map failed\n");
+ goto err_map;
+ }
+
+#ifdef CONFIG_MTRR
+ if (!share->mtrr_off) {
+ pr_info("enable mtrr\n");
+ share->mtrr.vram = mtrr_add(share->vidmem_start,
+ share->vidmem_size,
+ MTRR_TYPE_WRCOMB, 1);
+
+ if (share->mtrr.vram < 0) {
+ /* don't block driver with the failure of MTRR */
+ pr_err("Unable to setup MTRR.\n");
+ } else {
+ share->mtrr.vram_added = 1;
+ pr_info("MTRR added succesfully\n");
+ }
+ }
+#endif
+
+ memset_io(share->pvMem, 0, share->vidmem_size);
+
+ pr_info("sm%3x mmio address = %p\n", share->devid, share->pvReg);
+
+ pci_set_drvdata(pdev, share);
+
+ /* call chipInit routine */
+ hw_sm750_inithw(share, pdev);
+
+ /* allocate frame buffer info structor according to g_dualview */
+ fbidx = 0;
+ALLOC_FB:
+ info[fbidx] = framebuffer_alloc(sizeof(struct lynxfb_par), &pdev->dev);
+ if (!info[fbidx]) {
+ pr_err("Could not allocate framebuffer #%d.\n", fbidx);
+ if (fbidx == 0)
+ goto err_info0_alloc;
+ else
+ goto err_info1_alloc;
+ } else {
+ struct lynxfb_par *par;
+ int errno;
+
+ pr_info("framebuffer #%d alloc okay\n", fbidx);
+ share->fbinfo[fbidx] = info[fbidx];
+ par = info[fbidx]->par;
+ par->share = share;
+
+ /* set fb_info structure */
+ if (lynxfb_set_fbinfo(info[fbidx], fbidx)) {
+ pr_err("Failed to initial fb_info #%d.\n", fbidx);
+ if (fbidx == 0)
+ goto err_info0_set;
+ else
+ goto err_info1_set;
+ }
+
+ /* register frame buffer */
+ pr_info("Ready to register framebuffer #%d.\n", fbidx);
+ errno = register_framebuffer(info[fbidx]);
+ if (errno < 0) {
+ pr_err("Failed to register fb_info #%d. err %d\n",
+ fbidx,
+ errno);
+ if (fbidx == 0)
+ goto err_register0;
+ else
+ goto err_register1;
+ }
+ pr_info("Accomplished register framebuffer #%d.\n", fbidx);
+ }
+
+ /* no dual view by far */
+ fbidx++;
+ if (share->dual && fbidx < 2)
+ goto ALLOC_FB;
+
+ return 0;
+
+err_register1:
+err_info1_set:
+ framebuffer_release(info[1]);
+err_info1_alloc:
+ unregister_framebuffer(info[0]);
+err_register0:
+err_info0_set:
+ framebuffer_release(info[0]);
+err_info0_alloc:
+err_map:
+ kfree(spec_share);
+err_share:
+err_enable:
+ return -ENODEV;
+}
+
+static void lynxfb_pci_remove(struct pci_dev *pdev)
+{
+ struct fb_info *info;
+ struct lynx_share *share;
+ void *spec_share;
+ struct lynxfb_par *par;
+ int cnt;
+
+ cnt = 2;
+ share = pci_get_drvdata(pdev);
+
+ while (cnt-- > 0) {
+ info = share->fbinfo[cnt];
+ if (!info)
+ continue;
+ par = info->par;
+
+ unregister_framebuffer(info);
+ /* clean crtc & output allocations */
+ par->crtc.clear(&par->crtc);
+ par->output.clear(&par->output);
+ /* release frame buffer */
+ framebuffer_release(info);
+ }
+#ifdef CONFIG_MTRR
+ if (share->mtrr.vram_added)
+ mtrr_del(share->mtrr.vram,
+ share->vidmem_start,
+ share->vidmem_size);
+#endif
+
+ iounmap(share->pvReg);
+ iounmap(share->pvMem);
+ spec_share = container_of(share, struct sm750_share, share);
+ kfree(g_settings);
+ kfree(spec_share);
+ pci_set_drvdata(pdev, NULL);
+}
+
+static int __init lynxfb_setup(char *options)
+{
+ int len;
+ char *opt, *tmp;
+
+
+ if (!options || !*options) {
+ pr_warn("no options.\n");
+ return 0;
+ }
+
+ pr_info("options:%s\n", options);
+
+ len = strlen(options) + 1;
+ g_settings = kzalloc(len, GFP_KERNEL);
+ if (!g_settings)
+ return -ENOMEM;
+
+ tmp = g_settings;
+
+ /* Notes:
+ char * strsep(char **s,const char * ct);
+ @s: the string to be searched
+ @ct :the characters to search for
+
+ strsep() updates @options to pointer after the first found token
+ it also returns the pointer ahead the token.
+ */
+ while ((opt = strsep(&options, ":")) != NULL) {
+ /* options that mean for any lynx chips are configured here */
+ if (!strncmp(opt, "noaccel", strlen("noaccel")))
+ g_noaccel = 1;
+#ifdef CONFIG_MTRR
+ else if (!strncmp(opt, "nomtrr", strlen("nomtrr")))
+ g_nomtrr = 1;
+#endif
+ else if (!strncmp(opt, "dual", strlen("dual")))
+ g_dualview = 1;
+ else {
+ strcat(tmp, opt);
+ tmp += strlen(opt);
+ if (options != NULL)
+ *tmp++ = ':';
+ else
+ *tmp++ = 0;
+ }
+ }
+
+ /* misc g_settings are transport to chip specific routines */
+ pr_info("parameter left for chip specific analysis:%s\n", g_settings);
+ return 0;
+}
+
+static struct pci_device_id smi_pci_table[] = {
+ { PCI_DEVICE(0x126f, 0x0750), },
+ {0,}
+};
+
+MODULE_DEVICE_TABLE(pci, smi_pci_table);
+
+static struct pci_driver lynxfb_driver = {
+ .name = "sm750fb",
+ .id_table = smi_pci_table,
+ .probe = lynxfb_pci_probe,
+ .remove = lynxfb_pci_remove,
+#ifdef CONFIG_PM
+ .suspend = lynxfb_suspend,
+ .resume = lynxfb_resume,
+#endif
+};
+
+
+static int __init lynxfb_init(void)
+{
+ char *option;
+ int ret;
+
+#ifdef MODULE
+ option = g_option;
+#else
+ if (fb_get_options("sm750fb", &option))
+ return -ENODEV;
+#endif
+
+ lynxfb_setup(option);
+ ret = pci_register_driver(&lynxfb_driver);
+ return ret;
+}
+module_init(lynxfb_init);
+
+static void __exit lynxfb_exit(void)
+{
+ pci_unregister_driver(&lynxfb_driver);
+}
+module_exit(lynxfb_exit);
+
+module_param(g_option, charp, S_IRUGO);
+
+MODULE_PARM_DESC(g_option,
+ "\n\t\tCommon options:\n"
+ "\t\tnoaccel:disable 2d capabilities\n"
+ "\t\tnomtrr:disable MTRR attribute for video memory\n"
+ "\t\tdualview:dual frame buffer feature enabled\n"
+ "\t\tnohwc:disable hardware cursor\n"
+ "\t\tUsual example:\n"
+ "\t\tinsmod ./sm750fb.ko g_option=\"noaccel,nohwc,1280x1024-8@60\"\n"
+ );
+
+MODULE_AUTHOR("monk liu <monk.liu@siliconmotion.com>");
+MODULE_AUTHOR("Sudip Mukherjee <sudip@vectorindia.org>");
+MODULE_DESCRIPTION("Frame buffer driver for SM750 chipset");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/staging/sm750fb/sm750.h b/drivers/staging/sm750fb/sm750.h
new file mode 100644
index 000000000..0847d2bd9
--- /dev/null
+++ b/drivers/staging/sm750fb/sm750.h
@@ -0,0 +1,186 @@
+#ifndef LYNXDRV_H_
+#define LYNXDRV_H_
+
+
+
+#define FB_ACCEL_SMI 0xab
+/* please use revision id to distinguish sm750le and sm750*/
+#define SPC_SM750 0
+
+//#define SPC_SM750LE 8
+
+#define MB(x) ((x)<<20)
+#define MHZ(x) ((x) * 1000000)
+/* align should be 2,4,8,16 */
+#define PADDING(align,data) (((data)+(align)-1)&(~((align) -1)))
+extern int smi_indent;
+
+
+struct lynx_accel{
+ /* base virtual address of DPR registers */
+ volatile unsigned char __iomem * dprBase;
+ /* base virtual address of de data port */
+ volatile unsigned char __iomem * dpPortBase;
+
+ /* function fointers */
+ void (*de_init)(struct lynx_accel *);
+
+ int (*de_wait)(void);/* see if hardware ready to work */
+
+ int (*de_fillrect)(struct lynx_accel *,u32,u32,u32,
+ u32,u32,u32,u32,u32,u32);
+
+ int (*de_copyarea)(struct lynx_accel *,u32,u32,u32,u32,
+ u32,u32,u32,u32,
+ u32,u32,u32,u32);
+
+ int (*de_imageblit)(struct lynx_accel *,const char *,u32,u32,u32,
+ u32,u32,u32,u32,u32,u32,u32,u32,u32);
+
+};
+
+/* lynx_share stands for a presentation of two frame buffer
+ that use one smi adaptor , it is similar to a basic class of C++
+*/
+struct lynx_share{
+ /* common members */
+ u16 devid;
+ u8 revid;
+ struct pci_dev * pdev;
+ struct fb_info * fbinfo[2];
+ struct lynx_accel accel;
+ int accel_off;
+ int dual;
+#ifdef CONFIG_MTRR
+ int mtrr_off;
+ struct{
+ int vram;
+ int vram_added;
+ }mtrr;
+#endif
+ /* all smi graphic adaptor got below attributes */
+ unsigned long vidmem_start;
+ unsigned long vidreg_start;
+ __u32 vidmem_size;
+ __u32 vidreg_size;
+ void __iomem * pvReg;
+ unsigned char __iomem * pvMem;
+ /* locks*/
+ spinlock_t slock;
+ /* function pointers */
+ void (*suspend)(struct lynx_share*);
+ void (*resume)(struct lynx_share*);
+};
+
+struct lynx_cursor{
+ /* cursor width ,height and size */
+ int w;
+ int h;
+ int size;
+ /* hardware limitation */
+ int maxW;
+ int maxH;
+ /* base virtual address and offset of cursor image */
+ char __iomem * vstart;
+ int offset;
+ /* mmio addr of hw cursor */
+ volatile char __iomem * mmio;
+ /* the lynx_share of this adaptor */
+ struct lynx_share * share;
+ /* proc_routines */
+ void (*enable)(struct lynx_cursor *);
+ void (*disable)(struct lynx_cursor *);
+ void (*setSize)(struct lynx_cursor *,int,int);
+ void (*setPos)(struct lynx_cursor *,int,int);
+ void (*setColor)(struct lynx_cursor *,u32,u32);
+ void (*setData)(struct lynx_cursor *,u16,const u8*,const u8*);
+};
+
+struct lynxfb_crtc{
+ unsigned char __iomem * vCursor;//virtual address of cursor
+ unsigned char __iomem * vScreen;//virtual address of on_screen
+ int oCursor;//cursor address offset in vidmem
+ int oScreen;//onscreen address offset in vidmem
+ int channel;/* which channel this crtc stands for*/
+ resource_size_t vidmem_size;/* this view's video memory max size */
+
+ /* below attributes belong to info->fix, their value depends on specific adaptor*/
+ u16 line_pad;/* padding information:0,1,2,4,8,16,... */
+ u16 xpanstep;
+ u16 ypanstep;
+ u16 ywrapstep;
+
+ void * priv;
+
+ int(*proc_setMode)(struct lynxfb_crtc*,
+ struct fb_var_screeninfo*,
+ struct fb_fix_screeninfo*);
+
+ int(*proc_checkMode)(struct lynxfb_crtc*,struct fb_var_screeninfo*);
+ int(*proc_setColReg)(struct lynxfb_crtc*,ushort,ushort,ushort,ushort);
+ void (*clear)(struct lynxfb_crtc*);
+ /* pan display */
+ int (*proc_panDisplay)(struct lynxfb_crtc *,
+ const struct fb_var_screeninfo *,
+ const struct fb_info *);
+ /* cursor information */
+ struct lynx_cursor cursor;
+};
+
+struct lynxfb_output{
+ int dpms;
+ int paths;
+ /* which paths(s) this output stands for,for sm750:
+ paths=1:means output for panel paths
+ paths=2:means output for crt paths
+ paths=3:means output for both panel and crt paths
+ */
+
+ int * channel;
+ /* which channel these outputs linked with,for sm750:
+ *channel=0 means primary channel
+ *channel=1 means secondary channel
+ output->channel ==> &crtc->channel
+ */
+ void * priv;
+
+ int(*proc_setMode)(struct lynxfb_output*,
+ struct fb_var_screeninfo*,
+ struct fb_fix_screeninfo*);
+
+ int(*proc_checkMode)(struct lynxfb_output*,struct fb_var_screeninfo*);
+ int(*proc_setBLANK)(struct lynxfb_output*,int);
+ void (*clear)(struct lynxfb_output*);
+};
+
+struct lynxfb_par{
+ /* either 0 or 1 for dual head adaptor,0 is the older one registered */
+ int index;
+ unsigned int pseudo_palette[256];
+ struct lynxfb_crtc crtc;
+ struct lynxfb_output output;
+ struct fb_info * info;
+ struct lynx_share * share;
+};
+
+#ifndef offsetof
+#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
+#endif
+
+
+#define PS_TO_HZ(ps) \
+ ({ \
+ unsigned long long hz = 1000*1000*1000*1000ULL; \
+ do_div(hz,ps); \
+ (unsigned long)hz;})
+
+static inline unsigned long ps_to_hz(unsigned int psvalue)
+{
+ unsigned long long numerator=1000*1000*1000*1000ULL;
+ /* 10^12 / picosecond period gives frequency in Hz */
+ do_div(numerator, psvalue);
+ return (unsigned long)numerator;
+}
+
+
+#endif
diff --git a/drivers/staging/sm750fb/sm750_accel.c b/drivers/staging/sm750fb/sm750_accel.c
new file mode 100644
index 000000000..c5a372690
--- /dev/null
+++ b/drivers/staging/sm750fb/sm750_accel.c
@@ -0,0 +1,437 @@
+#include<linux/module.h>
+#include<linux/kernel.h>
+#include<linux/errno.h>
+#include<linux/string.h>
+#include<linux/mm.h>
+#include<linux/slab.h>
+#include<linux/delay.h>
+#include<linux/fb.h>
+#include<linux/ioport.h>
+#include<linux/init.h>
+#include<linux/pci.h>
+#include<linux/vmalloc.h>
+#include<linux/pagemap.h>
+#include <linux/console.h>
+#include<linux/platform_device.h>
+#include<linux/screen_info.h>
+
+#include "sm750.h"
+#include "sm750_accel.h"
+#include "sm750_help.h"
+static inline void write_dpr(struct lynx_accel * accel,int offset,u32 regValue)
+{
+ writel(regValue,accel->dprBase + offset);
+}
+
+static inline u32 read_dpr(struct lynx_accel * accel,int offset)
+{
+ return readl(accel->dprBase + offset);
+}
+
+static inline void write_dpPort(struct lynx_accel * accel,u32 data)
+{
+ writel(data,accel->dpPortBase);
+}
+
+void hw_de_init(struct lynx_accel * accel)
+{
+ /* setup 2d engine registers */
+ u32 reg,clr;
+
+ write_dpr(accel,DE_MASKS,0xFFFFFFFF);
+
+ /* dpr1c */
+ reg = FIELD_SET(0,DE_STRETCH_FORMAT,PATTERN_XY,NORMAL)|
+ FIELD_VALUE(0,DE_STRETCH_FORMAT,PATTERN_Y,0)|
+ FIELD_VALUE(0,DE_STRETCH_FORMAT,PATTERN_X,0)|
+ FIELD_SET(0,DE_STRETCH_FORMAT,ADDRESSING,XY)|
+ FIELD_VALUE(0,DE_STRETCH_FORMAT,SOURCE_HEIGHT,3);
+
+ clr = FIELD_CLEAR(DE_STRETCH_FORMAT,PATTERN_XY)&
+ FIELD_CLEAR(DE_STRETCH_FORMAT,PATTERN_Y)&
+ FIELD_CLEAR(DE_STRETCH_FORMAT,PATTERN_X)&
+ FIELD_CLEAR(DE_STRETCH_FORMAT,ADDRESSING)&
+ FIELD_CLEAR(DE_STRETCH_FORMAT,SOURCE_HEIGHT);
+
+ /* DE_STRETCH bpp format need be initilized in setMode routine */
+ write_dpr(accel,DE_STRETCH_FORMAT,(read_dpr(accel,DE_STRETCH_FORMAT) & clr) | reg);
+
+ /* disable clipping and transparent */
+ write_dpr(accel,DE_CLIP_TL,0);//dpr2c
+ write_dpr(accel,DE_CLIP_BR,0);//dpr30
+
+ write_dpr(accel,DE_COLOR_COMPARE_MASK,0);//dpr24
+ write_dpr(accel,DE_COLOR_COMPARE,0);
+
+ reg = FIELD_SET(0,DE_CONTROL,TRANSPARENCY,DISABLE)|
+ FIELD_SET(0,DE_CONTROL,TRANSPARENCY_MATCH,OPAQUE)|
+ FIELD_SET(0,DE_CONTROL,TRANSPARENCY_SELECT,SOURCE);
+
+ clr = FIELD_CLEAR(DE_CONTROL,TRANSPARENCY)&
+ FIELD_CLEAR(DE_CONTROL,TRANSPARENCY_MATCH)&
+ FIELD_CLEAR(DE_CONTROL,TRANSPARENCY_SELECT);
+
+ /* dpr0c */
+ write_dpr(accel,DE_CONTROL,(read_dpr(accel,DE_CONTROL)&clr)|reg);
+}
+
+/* set2dformat only be called from setmode functions
+ * but if you need dual framebuffer driver,need call set2dformat
+ * every time you use 2d function */
+
+void hw_set2dformat(struct lynx_accel * accel,int fmt)
+{
+ u32 reg;
+
+ /* fmt=0,1,2 for 8,16,32,bpp on sm718/750/502 */
+ reg = read_dpr(accel,DE_STRETCH_FORMAT);
+ reg = FIELD_VALUE(reg,DE_STRETCH_FORMAT,PIXEL_FORMAT,fmt);
+ write_dpr(accel,DE_STRETCH_FORMAT,reg);
+}
+
+int hw_fillrect(struct lynx_accel * accel,
+ u32 base,u32 pitch,u32 Bpp,
+ u32 x,u32 y,u32 width,u32 height,
+ u32 color,u32 rop)
+{
+ u32 deCtrl;
+
+ if(accel->de_wait() != 0)
+ {
+ /* int time wait and always busy,seems hardware
+ * got something error */
+ pr_debug("%s:De engine always bussy\n",__func__);
+ return -1;
+ }
+
+ write_dpr(accel,DE_WINDOW_DESTINATION_BASE,base);//dpr40
+ write_dpr(accel,DE_PITCH,
+ FIELD_VALUE(0,DE_PITCH,DESTINATION,pitch/Bpp)|
+ FIELD_VALUE(0,DE_PITCH,SOURCE,pitch/Bpp));//dpr10
+
+ write_dpr(accel,DE_WINDOW_WIDTH,
+ FIELD_VALUE(0,DE_WINDOW_WIDTH,DESTINATION,pitch/Bpp)|
+ FIELD_VALUE(0,DE_WINDOW_WIDTH,SOURCE,pitch/Bpp));//dpr44
+
+ write_dpr(accel,DE_FOREGROUND,color);//DPR14
+
+ write_dpr(accel,DE_DESTINATION,
+ FIELD_SET(0,DE_DESTINATION,WRAP,DISABLE)|
+ FIELD_VALUE(0,DE_DESTINATION,X,x)|
+ FIELD_VALUE(0,DE_DESTINATION,Y,y));//dpr4
+
+ write_dpr(accel,DE_DIMENSION,
+ FIELD_VALUE(0,DE_DIMENSION,X,width)|
+ FIELD_VALUE(0,DE_DIMENSION,Y_ET,height));//dpr8
+
+ deCtrl =
+ FIELD_SET(0,DE_CONTROL,STATUS,START)|
+ FIELD_SET(0,DE_CONTROL,DIRECTION,LEFT_TO_RIGHT)|
+ FIELD_SET(0,DE_CONTROL,LAST_PIXEL,ON)|
+ FIELD_SET(0,DE_CONTROL,COMMAND,RECTANGLE_FILL)|
+ FIELD_SET(0,DE_CONTROL,ROP_SELECT,ROP2)|
+ FIELD_VALUE(0,DE_CONTROL,ROP,rop);//dpr0xc
+
+ write_dpr(accel,DE_CONTROL,deCtrl);
+ return 0;
+}
+
+int hw_copyarea(
+struct lynx_accel * accel,
+unsigned int sBase, /* Address of source: offset in frame buffer */
+unsigned int sPitch, /* Pitch value of source surface in BYTE */
+unsigned int sx,
+unsigned int sy, /* Starting coordinate of source surface */
+unsigned int dBase, /* Address of destination: offset in frame buffer */
+unsigned int dPitch, /* Pitch value of destination surface in BYTE */
+unsigned int Bpp, /* Color depth of destination surface */
+unsigned int dx,
+unsigned int dy, /* Starting coordinate of destination surface */
+unsigned int width,
+unsigned int height, /* width and height of rectangle in pixel value */
+unsigned int rop2) /* ROP value */
+{
+ unsigned int nDirection, de_ctrl;
+ int opSign;
+ nDirection = LEFT_TO_RIGHT;
+ /* Direction of ROP2 operation: 1 = Left to Right, (-1) = Right to Left */
+ opSign = 1;
+ de_ctrl = 0;
+
+ /* If source and destination are the same surface, need to check for overlay cases */
+ if (sBase == dBase && sPitch == dPitch)
+ {
+ /* Determine direction of operation */
+ if (sy < dy)
+ {
+ /* +----------+
+ |S |
+ | +----------+
+ | | | |
+ | | | |
+ +---|------+ |
+ | D|
+ +----------+ */
+
+ nDirection = BOTTOM_TO_TOP;
+ }
+ else if (sy > dy)
+ {
+ /* +----------+
+ |D |
+ | +----------+
+ | | | |
+ | | | |
+ +---|------+ |
+ | S|
+ +----------+ */
+
+ nDirection = TOP_TO_BOTTOM;
+ }
+ else
+ {
+ /* sy == dy */
+
+ if (sx <= dx)
+ {
+ /* +------+---+------+
+ |S | | D|
+ | | | |
+ | | | |
+ | | | |
+ +------+---+------+ */
+
+ nDirection = RIGHT_TO_LEFT;
+ }
+ else
+ {
+ /* sx > dx */
+
+ /* +------+---+------+
+ |D | | S|
+ | | | |
+ | | | |
+ | | | |
+ +------+---+------+ */
+
+ nDirection = LEFT_TO_RIGHT;
+ }
+ }
+ }
+
+ if ((nDirection == BOTTOM_TO_TOP) || (nDirection == RIGHT_TO_LEFT))
+ {
+ sx += width - 1;
+ sy += height - 1;
+ dx += width - 1;
+ dy += height - 1;
+ opSign = (-1);
+ }
+
+ /* Note:
+ DE_FOREGROUND are DE_BACKGROUND are don't care.
+ DE_COLOR_COMPARE and DE_COLOR_COMPARE_MAKS are set by set deSetTransparency().
+ */
+
+ /* 2D Source Base.
+ It is an address offset (128 bit aligned) from the beginning of frame buffer.
+ */
+ write_dpr(accel,DE_WINDOW_SOURCE_BASE, sBase);//dpr40
+
+ /* 2D Destination Base.
+ It is an address offset (128 bit aligned) from the beginning of frame buffer.
+ */
+ write_dpr(accel,DE_WINDOW_DESTINATION_BASE, dBase);//dpr44
+
+#if 0
+ /* Program pitch (distance between the 1st points of two adjacent lines).
+ Note that input pitch is BYTE value, but the 2D Pitch register uses
+ pixel values. Need Byte to pixel convertion.
+ */
+ if(Bpp == 3){
+ sx *= 3;
+ dx *= 3;
+ width *= 3;
+ write_dpr(accel,DE_PITCH,
+ FIELD_VALUE(0, DE_PITCH, DESTINATION, dPitch) |
+ FIELD_VALUE(0, DE_PITCH, SOURCE, sPitch));//dpr10
+ }
+ else
+#endif
+ {
+ write_dpr(accel,DE_PITCH,
+ FIELD_VALUE(0, DE_PITCH, DESTINATION, (dPitch/Bpp)) |
+ FIELD_VALUE(0, DE_PITCH, SOURCE, (sPitch/Bpp)));//dpr10
+ }
+
+ /* Screen Window width in Pixels.
+ 2D engine uses this value to calculate the linear address in frame buffer for a given point.
+ */
+ write_dpr(accel,DE_WINDOW_WIDTH,
+ FIELD_VALUE(0, DE_WINDOW_WIDTH, DESTINATION, (dPitch/Bpp)) |
+ FIELD_VALUE(0, DE_WINDOW_WIDTH, SOURCE, (sPitch/Bpp)));//dpr3c
+
+ if (accel->de_wait() != 0){
+ return -1;
+ }
+
+ {
+
+ write_dpr(accel,DE_SOURCE,
+ FIELD_SET (0, DE_SOURCE, WRAP, DISABLE) |
+ FIELD_VALUE(0, DE_SOURCE, X_K1, sx) |
+ FIELD_VALUE(0, DE_SOURCE, Y_K2, sy));//dpr0
+ write_dpr(accel,DE_DESTINATION,
+ FIELD_SET (0, DE_DESTINATION, WRAP, DISABLE) |
+ FIELD_VALUE(0, DE_DESTINATION, X, dx) |
+ FIELD_VALUE(0, DE_DESTINATION, Y, dy));//dpr04
+ write_dpr(accel,DE_DIMENSION,
+ FIELD_VALUE(0, DE_DIMENSION, X, width) |
+ FIELD_VALUE(0, DE_DIMENSION, Y_ET, height));//dpr08
+
+ de_ctrl =
+ FIELD_VALUE(0, DE_CONTROL, ROP, rop2) |
+ FIELD_SET(0, DE_CONTROL, ROP_SELECT, ROP2) |
+ FIELD_SET(0, DE_CONTROL, COMMAND, BITBLT) |
+ ((nDirection == RIGHT_TO_LEFT) ?
+ FIELD_SET(0, DE_CONTROL, DIRECTION, RIGHT_TO_LEFT)
+ : FIELD_SET(0, DE_CONTROL, DIRECTION, LEFT_TO_RIGHT)) |
+ FIELD_SET(0, DE_CONTROL, STATUS, START);
+ write_dpr(accel,DE_CONTROL,de_ctrl);//dpr0c
+ }
+
+ return 0;
+}
+
+static unsigned int deGetTransparency(struct lynx_accel * accel)
+{
+ unsigned int de_ctrl;
+
+ de_ctrl = read_dpr(accel,DE_CONTROL);
+
+ de_ctrl &=
+ FIELD_MASK(DE_CONTROL_TRANSPARENCY_MATCH) |
+ FIELD_MASK(DE_CONTROL_TRANSPARENCY_SELECT)|
+ FIELD_MASK(DE_CONTROL_TRANSPARENCY);
+
+ return de_ctrl;
+}
+
+int hw_imageblit(struct lynx_accel *accel,
+ const char *pSrcbuf, /* pointer to start of source buffer in system memory */
+ u32 srcDelta, /* Pitch value (in bytes) of the source buffer, +ive means top down and -ive mean button up */
+ u32 startBit, /* Mono data can start at any bit in a byte, this value should be 0 to 7 */
+ u32 dBase, /* Address of destination: offset in frame buffer */
+ u32 dPitch, /* Pitch value of destination surface in BYTE */
+ u32 bytePerPixel, /* Color depth of destination surface */
+ u32 dx,
+ u32 dy, /* Starting coordinate of destination surface */
+ u32 width,
+ u32 height, /* width and height of rectange in pixel value */
+ u32 fColor, /* Foreground color (corresponding to a 1 in the monochrome data */
+ u32 bColor, /* Background color (corresponding to a 0 in the monochrome data */
+ u32 rop2) /* ROP value */
+{
+ unsigned int ulBytesPerScan;
+ unsigned int ul4BytesPerScan;
+ unsigned int ulBytesRemain;
+ unsigned int de_ctrl = 0;
+ unsigned char ajRemain[4];
+ int i, j;
+
+ startBit &= 7; /* Just make sure the start bit is within legal range */
+ ulBytesPerScan = (width + startBit + 7) / 8;
+ ul4BytesPerScan = ulBytesPerScan & ~3;
+ ulBytesRemain = ulBytesPerScan & 3;
+
+ if(accel->de_wait() != 0)
+ {
+// inf_msg("*** ImageBlit return -1 ***\n");
+ return -1;
+ }
+
+ /* 2D Source Base.
+ Use 0 for HOST Blt.
+ */
+ write_dpr(accel,DE_WINDOW_SOURCE_BASE, 0);
+
+ /* 2D Destination Base.
+ It is an address offset (128 bit aligned) from the beginning of frame buffer.
+ */
+ write_dpr(accel,DE_WINDOW_DESTINATION_BASE, dBase);
+#if 0
+ /* Program pitch (distance between the 1st points of two adjacent lines).
+ Note that input pitch is BYTE value, but the 2D Pitch register uses
+ pixel values. Need Byte to pixel convertion.
+ */
+ if(bytePerPixel == 3 ){
+ dx *= 3;
+ width *= 3;
+ startBit *= 3;
+ write_dpr(accel,DE_PITCH,
+ FIELD_VALUE(0, DE_PITCH, DESTINATION, dPitch) |
+ FIELD_VALUE(0, DE_PITCH, SOURCE, dPitch));//dpr10
+
+ }
+ else
+#endif
+ {
+ write_dpr(accel,DE_PITCH,
+ FIELD_VALUE(0, DE_PITCH, DESTINATION, dPitch/bytePerPixel) |
+ FIELD_VALUE(0, DE_PITCH, SOURCE, dPitch/bytePerPixel));//dpr10
+ }
+
+ /* Screen Window width in Pixels.
+ 2D engine uses this value to calculate the linear address in frame buffer for a given point.
+ */
+ write_dpr(accel,DE_WINDOW_WIDTH,
+ FIELD_VALUE(0, DE_WINDOW_WIDTH, DESTINATION, (dPitch/bytePerPixel)) |
+ FIELD_VALUE(0, DE_WINDOW_WIDTH, SOURCE, (dPitch/bytePerPixel)));
+
+ /* Note: For 2D Source in Host Write, only X_K1_MONO field is needed, and Y_K2 field is not used.
+ For mono bitmap, use startBit for X_K1. */
+ write_dpr(accel,DE_SOURCE,
+ FIELD_SET (0, DE_SOURCE, WRAP, DISABLE) |
+ FIELD_VALUE(0, DE_SOURCE, X_K1_MONO, startBit));//dpr00
+
+ write_dpr(accel,DE_DESTINATION,
+ FIELD_SET (0, DE_DESTINATION, WRAP, DISABLE) |
+ FIELD_VALUE(0, DE_DESTINATION, X, dx) |
+ FIELD_VALUE(0, DE_DESTINATION, Y, dy));//dpr04
+
+ write_dpr(accel,DE_DIMENSION,
+ FIELD_VALUE(0, DE_DIMENSION, X, width) |
+ FIELD_VALUE(0, DE_DIMENSION, Y_ET, height));//dpr08
+
+ write_dpr(accel,DE_FOREGROUND, fColor);
+ write_dpr(accel,DE_BACKGROUND, bColor);
+
+ de_ctrl = FIELD_VALUE(0, DE_CONTROL, ROP, rop2) |
+ FIELD_SET(0, DE_CONTROL, ROP_SELECT, ROP2) |
+ FIELD_SET(0, DE_CONTROL, COMMAND, HOST_WRITE) |
+ FIELD_SET(0, DE_CONTROL, HOST, MONO) |
+ FIELD_SET(0, DE_CONTROL, STATUS, START);
+
+ write_dpr(accel,DE_CONTROL, de_ctrl | deGetTransparency(accel));
+
+ /* Write MONO data (line by line) to 2D Engine data port */
+ for (i=0; i<height; i++)
+ {
+ /* For each line, send the data in chunks of 4 bytes */
+ for (j=0; j<(ul4BytesPerScan/4); j++)
+ {
+ write_dpPort(accel, *(unsigned int *)(pSrcbuf + (j * 4)));
+ }
+
+ if (ulBytesRemain)
+ {
+ memcpy(ajRemain, pSrcbuf+ul4BytesPerScan, ulBytesRemain);
+ write_dpPort(accel, *(unsigned int *)ajRemain);
+ }
+
+ pSrcbuf += srcDelta;
+ }
+
+ return 0;
+}
+
diff --git a/drivers/staging/sm750fb/sm750_accel.h b/drivers/staging/sm750fb/sm750_accel.h
new file mode 100644
index 000000000..3ee0bd892
--- /dev/null
+++ b/drivers/staging/sm750fb/sm750_accel.h
@@ -0,0 +1,275 @@
+#ifndef ACCEL_H__
+#define ACCEL_H__
+
+#define HW_ROP2_COPY 0xc
+#define HW_ROP2_XOR 0x6
+
+/* notes: below address are the offset value from de_base_address (0x100000)*/
+
+/* for sm718/750/502 de_base is at mmreg_1mb*/
+#define DE_BASE_ADDR_TYPE1 0x100000
+/* for sm712,de_base is at mmreg_32kb */
+#define DE_BASE_ADDR_TYPE2 0x8000
+/* for sm722,de_base is at mmreg_0 */
+#define DE_BASE_ADDR_TYPE3 0
+
+/* type1 data port address is at mmreg_0x110000*/
+#define DE_PORT_ADDR_TYPE1 0x110000
+/* for sm712,data port address is at mmreg_0 */
+#define DE_PORT_ADDR_TYPE2 0x100000
+/* for sm722,data port address is at mmreg_1mb */
+#define DE_PORT_ADDR_TYPE3 0x100000
+
+#define DE_SOURCE 0x0
+#define DE_SOURCE_WRAP 31:31
+#define DE_SOURCE_WRAP_DISABLE 0
+#define DE_SOURCE_WRAP_ENABLE 1
+#define DE_SOURCE_X_K1 29:16
+#define DE_SOURCE_Y_K2 15:0
+#define DE_SOURCE_X_K1_MONO 20:16
+
+#define DE_DESTINATION 0x4
+#define DE_DESTINATION_WRAP 31:31
+#define DE_DESTINATION_WRAP_DISABLE 0
+#define DE_DESTINATION_WRAP_ENABLE 1
+#define DE_DESTINATION_X 28:16
+#define DE_DESTINATION_Y 15:0
+
+#define DE_DIMENSION 0x8
+#define DE_DIMENSION_X 28:16
+#define DE_DIMENSION_Y_ET 15:0
+
+#define DE_CONTROL 0xC
+#define DE_CONTROL_STATUS 31:31
+#define DE_CONTROL_STATUS_STOP 0
+#define DE_CONTROL_STATUS_START 1
+#define DE_CONTROL_PATTERN 30:30
+#define DE_CONTROL_PATTERN_MONO 0
+#define DE_CONTROL_PATTERN_COLOR 1
+#define DE_CONTROL_UPDATE_DESTINATION_X 29:29
+#define DE_CONTROL_UPDATE_DESTINATION_X_DISABLE 0
+#define DE_CONTROL_UPDATE_DESTINATION_X_ENABLE 1
+#define DE_CONTROL_QUICK_START 28:28
+#define DE_CONTROL_QUICK_START_DISABLE 0
+#define DE_CONTROL_QUICK_START_ENABLE 1
+#define DE_CONTROL_DIRECTION 27:27
+#define DE_CONTROL_DIRECTION_LEFT_TO_RIGHT 0
+#define DE_CONTROL_DIRECTION_RIGHT_TO_LEFT 1
+#define DE_CONTROL_MAJOR 26:26
+#define DE_CONTROL_MAJOR_X 0
+#define DE_CONTROL_MAJOR_Y 1
+#define DE_CONTROL_STEP_X 25:25
+#define DE_CONTROL_STEP_X_POSITIVE 1
+#define DE_CONTROL_STEP_X_NEGATIVE 0
+#define DE_CONTROL_STEP_Y 24:24
+#define DE_CONTROL_STEP_Y_POSITIVE 1
+#define DE_CONTROL_STEP_Y_NEGATIVE 0
+#define DE_CONTROL_STRETCH 23:23
+#define DE_CONTROL_STRETCH_DISABLE 0
+#define DE_CONTROL_STRETCH_ENABLE 1
+#define DE_CONTROL_HOST 22:22
+#define DE_CONTROL_HOST_COLOR 0
+#define DE_CONTROL_HOST_MONO 1
+#define DE_CONTROL_LAST_PIXEL 21:21
+#define DE_CONTROL_LAST_PIXEL_OFF 0
+#define DE_CONTROL_LAST_PIXEL_ON 1
+#define DE_CONTROL_COMMAND 20:16
+#define DE_CONTROL_COMMAND_BITBLT 0
+#define DE_CONTROL_COMMAND_RECTANGLE_FILL 1
+#define DE_CONTROL_COMMAND_DE_TILE 2
+#define DE_CONTROL_COMMAND_TRAPEZOID_FILL 3
+#define DE_CONTROL_COMMAND_ALPHA_BLEND 4
+#define DE_CONTROL_COMMAND_RLE_STRIP 5
+#define DE_CONTROL_COMMAND_SHORT_STROKE 6
+#define DE_CONTROL_COMMAND_LINE_DRAW 7
+#define DE_CONTROL_COMMAND_HOST_WRITE 8
+#define DE_CONTROL_COMMAND_HOST_READ 9
+#define DE_CONTROL_COMMAND_HOST_WRITE_BOTTOM_UP 10
+#define DE_CONTROL_COMMAND_ROTATE 11
+#define DE_CONTROL_COMMAND_FONT 12
+#define DE_CONTROL_COMMAND_TEXTURE_LOAD 15
+#define DE_CONTROL_ROP_SELECT 15:15
+#define DE_CONTROL_ROP_SELECT_ROP3 0
+#define DE_CONTROL_ROP_SELECT_ROP2 1
+#define DE_CONTROL_ROP2_SOURCE 14:14
+#define DE_CONTROL_ROP2_SOURCE_BITMAP 0
+#define DE_CONTROL_ROP2_SOURCE_PATTERN 1
+#define DE_CONTROL_MONO_DATA 13:12
+#define DE_CONTROL_MONO_DATA_NOT_PACKED 0
+#define DE_CONTROL_MONO_DATA_8_PACKED 1
+#define DE_CONTROL_MONO_DATA_16_PACKED 2
+#define DE_CONTROL_MONO_DATA_32_PACKED 3
+#define DE_CONTROL_REPEAT_ROTATE 11:11
+#define DE_CONTROL_REPEAT_ROTATE_DISABLE 0
+#define DE_CONTROL_REPEAT_ROTATE_ENABLE 1
+#define DE_CONTROL_TRANSPARENCY_MATCH 10:10
+#define DE_CONTROL_TRANSPARENCY_MATCH_OPAQUE 0
+#define DE_CONTROL_TRANSPARENCY_MATCH_TRANSPARENT 1
+#define DE_CONTROL_TRANSPARENCY_SELECT 9:9
+#define DE_CONTROL_TRANSPARENCY_SELECT_SOURCE 0
+#define DE_CONTROL_TRANSPARENCY_SELECT_DESTINATION 1
+#define DE_CONTROL_TRANSPARENCY 8:8
+#define DE_CONTROL_TRANSPARENCY_DISABLE 0
+#define DE_CONTROL_TRANSPARENCY_ENABLE 1
+#define DE_CONTROL_ROP 7:0
+
+// Pseudo fields.
+
+#define DE_CONTROL_SHORT_STROKE_DIR 27:24
+#define DE_CONTROL_SHORT_STROKE_DIR_225 0
+#define DE_CONTROL_SHORT_STROKE_DIR_135 1
+#define DE_CONTROL_SHORT_STROKE_DIR_315 2
+#define DE_CONTROL_SHORT_STROKE_DIR_45 3
+#define DE_CONTROL_SHORT_STROKE_DIR_270 4
+#define DE_CONTROL_SHORT_STROKE_DIR_90 5
+#define DE_CONTROL_SHORT_STROKE_DIR_180 8
+#define DE_CONTROL_SHORT_STROKE_DIR_0 10
+#define DE_CONTROL_ROTATION 25:24
+#define DE_CONTROL_ROTATION_0 0
+#define DE_CONTROL_ROTATION_270 1
+#define DE_CONTROL_ROTATION_90 2
+#define DE_CONTROL_ROTATION_180 3
+
+#define DE_PITCH 0x000010
+#define DE_PITCH_DESTINATION 28:16
+#define DE_PITCH_SOURCE 12:0
+
+#define DE_FOREGROUND 0x000014
+#define DE_FOREGROUND_COLOR 31:0
+
+#define DE_BACKGROUND 0x000018
+#define DE_BACKGROUND_COLOR 31:0
+
+#define DE_STRETCH_FORMAT 0x00001C
+#define DE_STRETCH_FORMAT_PATTERN_XY 30:30
+#define DE_STRETCH_FORMAT_PATTERN_XY_NORMAL 0
+#define DE_STRETCH_FORMAT_PATTERN_XY_OVERWRITE 1
+#define DE_STRETCH_FORMAT_PATTERN_Y 29:27
+#define DE_STRETCH_FORMAT_PATTERN_X 25:23
+#define DE_STRETCH_FORMAT_PIXEL_FORMAT 21:20
+#define DE_STRETCH_FORMAT_PIXEL_FORMAT_8 0
+#define DE_STRETCH_FORMAT_PIXEL_FORMAT_16 1
+#define DE_STRETCH_FORMAT_PIXEL_FORMAT_32 2
+#define DE_STRETCH_FORMAT_PIXEL_FORMAT_24 3
+
+#define DE_STRETCH_FORMAT_ADDRESSING 19:16
+#define DE_STRETCH_FORMAT_ADDRESSING_XY 0
+#define DE_STRETCH_FORMAT_ADDRESSING_LINEAR 15
+#define DE_STRETCH_FORMAT_SOURCE_HEIGHT 11:0
+
+#define DE_COLOR_COMPARE 0x000020
+#define DE_COLOR_COMPARE_COLOR 23:0
+
+#define DE_COLOR_COMPARE_MASK 0x000024
+#define DE_COLOR_COMPARE_MASK_MASKS 23:0
+
+#define DE_MASKS 0x000028
+#define DE_MASKS_BYTE_MASK 31:16
+#define DE_MASKS_BIT_MASK 15:0
+
+#define DE_CLIP_TL 0x00002C
+#define DE_CLIP_TL_TOP 31:16
+#define DE_CLIP_TL_STATUS 13:13
+#define DE_CLIP_TL_STATUS_DISABLE 0
+#define DE_CLIP_TL_STATUS_ENABLE 1
+#define DE_CLIP_TL_INHIBIT 12:12
+#define DE_CLIP_TL_INHIBIT_OUTSIDE 0
+#define DE_CLIP_TL_INHIBIT_INSIDE 1
+#define DE_CLIP_TL_LEFT 11:0
+
+#define DE_CLIP_BR 0x000030
+#define DE_CLIP_BR_BOTTOM 31:16
+#define DE_CLIP_BR_RIGHT 12:0
+
+#define DE_MONO_PATTERN_LOW 0x000034
+#define DE_MONO_PATTERN_LOW_PATTERN 31:0
+
+#define DE_MONO_PATTERN_HIGH 0x000038
+#define DE_MONO_PATTERN_HIGH_PATTERN 31:0
+
+#define DE_WINDOW_WIDTH 0x00003C
+#define DE_WINDOW_WIDTH_DESTINATION 28:16
+#define DE_WINDOW_WIDTH_SOURCE 12:0
+
+#define DE_WINDOW_SOURCE_BASE 0x000040
+#define DE_WINDOW_SOURCE_BASE_EXT 27:27
+#define DE_WINDOW_SOURCE_BASE_EXT_LOCAL 0
+#define DE_WINDOW_SOURCE_BASE_EXT_EXTERNAL 1
+#define DE_WINDOW_SOURCE_BASE_CS 26:26
+#define DE_WINDOW_SOURCE_BASE_CS_0 0
+#define DE_WINDOW_SOURCE_BASE_CS_1 1
+#define DE_WINDOW_SOURCE_BASE_ADDRESS 25:0
+
+#define DE_WINDOW_DESTINATION_BASE 0x000044
+#define DE_WINDOW_DESTINATION_BASE_EXT 27:27
+#define DE_WINDOW_DESTINATION_BASE_EXT_LOCAL 0
+#define DE_WINDOW_DESTINATION_BASE_EXT_EXTERNAL 1
+#define DE_WINDOW_DESTINATION_BASE_CS 26:26
+#define DE_WINDOW_DESTINATION_BASE_CS_0 0
+#define DE_WINDOW_DESTINATION_BASE_CS_1 1
+#define DE_WINDOW_DESTINATION_BASE_ADDRESS 25:0
+
+#define DE_ALPHA 0x000048
+#define DE_ALPHA_VALUE 7:0
+
+#define DE_WRAP 0x00004C
+#define DE_WRAP_X 31:16
+#define DE_WRAP_Y 15:0
+
+#define DE_STATUS 0x000050
+#define DE_STATUS_CSC 1:1
+#define DE_STATUS_CSC_CLEAR 0
+#define DE_STATUS_CSC_NOT_ACTIVE 0
+#define DE_STATUS_CSC_ACTIVE 1
+#define DE_STATUS_2D 0:0
+#define DE_STATUS_2D_CLEAR 0
+#define DE_STATUS_2D_NOT_ACTIVE 0
+#define DE_STATUS_2D_ACTIVE 1
+
+
+
+/* blt direction */
+#define TOP_TO_BOTTOM 0
+#define LEFT_TO_RIGHT 0
+#define BOTTOM_TO_TOP 1
+#define RIGHT_TO_LEFT 1
+
+void hw_set2dformat(struct lynx_accel * accel,int fmt);
+
+void hw_de_init(struct lynx_accel * accel);
+
+int hw_fillrect(struct lynx_accel * accel,
+ u32 base,u32 pitch,u32 Bpp,
+ u32 x,u32 y,u32 width,u32 height,
+ u32 color,u32 rop);
+
+int hw_copyarea(
+struct lynx_accel * accel,
+unsigned int sBase, /* Address of source: offset in frame buffer */
+unsigned int sPitch, /* Pitch value of source surface in BYTE */
+unsigned int sx,
+unsigned int sy, /* Starting coordinate of source surface */
+unsigned int dBase, /* Address of destination: offset in frame buffer */
+unsigned int dPitch, /* Pitch value of destination surface in BYTE */
+unsigned int bpp, /* Color depth of destination surface */
+unsigned int dx,
+unsigned int dy, /* Starting coordinate of destination surface */
+unsigned int width,
+unsigned int height, /* width and height of rectangle in pixel value */
+unsigned int rop2);
+
+int hw_imageblit(struct lynx_accel *accel,
+ const char *pSrcbuf, /* pointer to start of source buffer in system memory */
+ u32 srcDelta, /* Pitch value (in bytes) of the source buffer, +ive means top down and -ive mean button up */
+ u32 startBit, /* Mono data can start at any bit in a byte, this value should be 0 to 7 */
+ u32 dBase, /* Address of destination: offset in frame buffer */
+ u32 dPitch, /* Pitch value of destination surface in BYTE */
+ u32 bytePerPixel, /* Color depth of destination surface */
+ u32 dx,
+ u32 dy, /* Starting coordinate of destination surface */
+ u32 width,
+ u32 height, /* width and height of rectange in pixel value */
+ u32 fColor, /* Foreground color (corresponding to a 1 in the monochrome data */
+ u32 bColor, /* Background color (corresponding to a 0 in the monochrome data */
+ u32 rop2);
+#endif
diff --git a/drivers/staging/sm750fb/sm750_cursor.c b/drivers/staging/sm750fb/sm750_cursor.c
new file mode 100644
index 000000000..68d5cbc3e
--- /dev/null
+++ b/drivers/staging/sm750fb/sm750_cursor.c
@@ -0,0 +1,251 @@
+#include<linux/module.h>
+#include<linux/kernel.h>
+#include<linux/errno.h>
+#include<linux/string.h>
+#include<linux/mm.h>
+#include<linux/slab.h>
+#include<linux/delay.h>
+#include<linux/fb.h>
+#include<linux/ioport.h>
+#include<linux/init.h>
+#include<linux/pci.h>
+#include<linux/vmalloc.h>
+#include<linux/pagemap.h>
+#include <linux/console.h>
+#include<linux/platform_device.h>
+#include<linux/screen_info.h>
+
+#include "sm750.h"
+#include "sm750_help.h"
+#include "sm750_cursor.h"
+
+
+#define PEEK32(addr) \
+readl(cursor->mmio + (addr))
+
+#define POKE32(addr,data) \
+writel((data),cursor->mmio + (addr))
+
+/* cursor control for voyager and 718/750*/
+#define HWC_ADDRESS 0x0
+#define HWC_ADDRESS_ENABLE 31:31
+#define HWC_ADDRESS_ENABLE_DISABLE 0
+#define HWC_ADDRESS_ENABLE_ENABLE 1
+#define HWC_ADDRESS_EXT 27:27
+#define HWC_ADDRESS_EXT_LOCAL 0
+#define HWC_ADDRESS_EXT_EXTERNAL 1
+#define HWC_ADDRESS_CS 26:26
+#define HWC_ADDRESS_CS_0 0
+#define HWC_ADDRESS_CS_1 1
+#define HWC_ADDRESS_ADDRESS 25:0
+
+#define HWC_LOCATION 0x4
+#define HWC_LOCATION_TOP 27:27
+#define HWC_LOCATION_TOP_INSIDE 0
+#define HWC_LOCATION_TOP_OUTSIDE 1
+#define HWC_LOCATION_Y 26:16
+#define HWC_LOCATION_LEFT 11:11
+#define HWC_LOCATION_LEFT_INSIDE 0
+#define HWC_LOCATION_LEFT_OUTSIDE 1
+#define HWC_LOCATION_X 10:0
+
+#define HWC_COLOR_12 0x8
+#define HWC_COLOR_12_2_RGB565 31:16
+#define HWC_COLOR_12_1_RGB565 15:0
+
+#define HWC_COLOR_3 0xC
+#define HWC_COLOR_3_RGB565 15:0
+
+
+/* hw_cursor_xxx works for voyager,718 and 750 */
+void hw_cursor_enable(struct lynx_cursor * cursor)
+{
+ u32 reg;
+ reg = FIELD_VALUE(0,HWC_ADDRESS,ADDRESS,cursor->offset)|
+ FIELD_SET(0,HWC_ADDRESS,EXT,LOCAL)|
+ FIELD_SET(0,HWC_ADDRESS,ENABLE,ENABLE);
+ POKE32(HWC_ADDRESS,reg);
+}
+void hw_cursor_disable(struct lynx_cursor * cursor)
+{
+ POKE32(HWC_ADDRESS,0);
+}
+
+void hw_cursor_setSize(struct lynx_cursor * cursor,
+ int w,int h)
+{
+ cursor->w = w;
+ cursor->h = h;
+}
+void hw_cursor_setPos(struct lynx_cursor * cursor,
+ int x,int y)
+{
+ u32 reg;
+ reg = FIELD_VALUE(0,HWC_LOCATION,Y,y)|
+ FIELD_VALUE(0,HWC_LOCATION,X,x);
+ POKE32(HWC_LOCATION,reg);
+}
+void hw_cursor_setColor(struct lynx_cursor * cursor,
+ u32 fg,u32 bg)
+{
+ POKE32(HWC_COLOR_12,(fg<<16)|(bg&0xffff));
+ POKE32(HWC_COLOR_3,0xffe0);
+}
+
+void hw_cursor_setData(struct lynx_cursor * cursor,
+ u16 rop,const u8* pcol,const u8* pmsk)
+{
+ int i,j,count,pitch,offset;
+ u8 color,mask,opr;
+ u16 data;
+ void __iomem *pbuffer, *pstart;
+
+ /* in byte*/
+ pitch = cursor->w >> 3;
+
+ /* in byte */
+ count = pitch * cursor->h;
+
+ /* in byte */
+ offset = cursor->maxW * 2 / 8;
+
+ data = 0;
+ pstart = cursor->vstart;
+ pbuffer = pstart;
+
+/*
+ if(odd &1){
+ hw_cursor_setData2(cursor,rop,pcol,pmsk);
+ }
+ odd++;
+ if(odd > 0xfffffff0)
+ odd=0;
+*/
+
+ for(i=0;i<count;i++)
+ {
+ color = *pcol++;
+ mask = *pmsk++;
+ data = 0;
+
+ /* either method below works well,
+ * but method 2 shows no lag
+ * and method 1 seems a bit wrong*/
+#if 0
+ if(rop == ROP_XOR)
+ opr = mask ^ color;
+ else
+ opr = mask & color;
+
+ for(j=0;j<8;j++)
+ {
+
+ if(opr & (0x80 >> j))
+ { //use fg color,id = 2
+ data |= 2 << (j*2);
+ }else{
+ //use bg color,id = 1
+ data |= 1 << (j*2);
+ }
+ }
+#else
+ for(j=0;j<8;j++){
+ if(mask & (0x80>>j)){
+ if(rop == ROP_XOR)
+ opr = mask ^ color;
+ else
+ opr = mask & color;
+
+ /* 2 stands for forecolor and 1 for backcolor */
+ data |= ((opr & (0x80>>j))?2:1)<<(j*2);
+ }
+ }
+#endif
+ iowrite16(data, pbuffer);
+
+ /* assume pitch is 1,2,4,8,...*/
+#if 0
+ if(!((i+1)&(pitch-1))) /* below line equal to is line */
+#else
+ if((i+1) % pitch == 0)
+#endif
+ {
+ /* need a return */
+ pstart += offset;
+ pbuffer = pstart;
+ }else{
+ pbuffer += sizeof(u16);
+ }
+
+ }
+
+
+}
+
+
+void hw_cursor_setData2(struct lynx_cursor * cursor,
+ u16 rop,const u8* pcol,const u8* pmsk)
+{
+ int i,j,count,pitch,offset;
+ u8 color, mask;
+ u16 data;
+ void __iomem *pbuffer, *pstart;
+
+ /* in byte*/
+ pitch = cursor->w >> 3;
+
+ /* in byte */
+ count = pitch * cursor->h;
+
+ /* in byte */
+ offset = cursor->maxW * 2 / 8;
+
+ data = 0;
+ pstart = cursor->vstart;
+ pbuffer = pstart;
+
+ for(i=0;i<count;i++)
+ {
+ color = *pcol++;
+ mask = *pmsk++;
+ data = 0;
+
+ /* either method below works well, but method 2 shows no lag */
+#if 0
+ if(rop == ROP_XOR)
+ opr = mask ^ color;
+ else
+ opr = mask & color;
+
+ for(j=0;j<8;j++)
+ {
+
+ if(opr & (0x80 >> j))
+ { //use fg color,id = 2
+ data |= 2 << (j*2);
+ }else{
+ //use bg color,id = 1
+ data |= 1 << (j*2);
+ }
+ }
+#else
+ for(j=0;j<8;j++){
+ if(mask & (1<<j))
+ data |= ((color & (1<<j))?1:2)<<(j*2);
+ }
+#endif
+ iowrite16(data, pbuffer);
+
+ /* assume pitch is 1,2,4,8,...*/
+ if(!(i&(pitch-1)))
+ //if((i+1) % pitch == 0)
+ {
+ /* need a return */
+ pstart += offset;
+ pbuffer = pstart;
+ }else{
+ pbuffer += sizeof(u16);
+ }
+
+ }
+}
diff --git a/drivers/staging/sm750fb/sm750_cursor.h b/drivers/staging/sm750fb/sm750_cursor.h
new file mode 100644
index 000000000..8cede0721
--- /dev/null
+++ b/drivers/staging/sm750fb/sm750_cursor.h
@@ -0,0 +1,17 @@
+#ifndef LYNX_CURSOR_H__
+#define LYNX_CURSOR_H__
+
+/* hw_cursor_xxx works for voyager,718 and 750 */
+void hw_cursor_enable(struct lynx_cursor * cursor);
+void hw_cursor_disable(struct lynx_cursor * cursor);
+void hw_cursor_setSize(struct lynx_cursor * cursor,
+ int w,int h);
+void hw_cursor_setPos(struct lynx_cursor * cursor,
+ int x,int y);
+void hw_cursor_setColor(struct lynx_cursor * cursor,
+ u32 fg,u32 bg);
+void hw_cursor_setData(struct lynx_cursor * cursor,
+ u16 rop,const u8* data,const u8* mask);
+void hw_cursor_setData2(struct lynx_cursor * cursor,
+ u16 rop,const u8* data,const u8* mask);
+#endif
diff --git a/drivers/staging/sm750fb/sm750_help.h b/drivers/staging/sm750fb/sm750_help.h
new file mode 100644
index 000000000..e0128d2a9
--- /dev/null
+++ b/drivers/staging/sm750fb/sm750_help.h
@@ -0,0 +1,111 @@
+#ifndef LYNX_HELP_H__
+#define LYNX_HELP_H__
+/*****************************************************************************\
+ * FIELD MACROS *
+\*****************************************************************************/
+
+#define _LSB(f) (0 ? f)
+#define _MSB(f) (1 ? f)
+#define _COUNT(f) (_MSB(f) - _LSB(f) + 1)
+
+#define RAW_MASK(f) (0xFFFFFFFF >> (32 - _COUNT(f)))
+#define GET_MASK(f) (RAW_MASK(f) << _LSB(f))
+#define GET_FIELD(d,f) (((d) >> _LSB(f)) & RAW_MASK(f))
+#define TEST_FIELD(d,f,v) (GET_FIELD(d,f) == f ## _ ## v)
+#define SET_FIELD(d,f,v) (((d) & ~GET_MASK(f)) | \
+ (((f ## _ ## v) & RAW_MASK(f)) << _LSB(f)))
+#define SET_FIELDV(d,f,v) (((d) & ~GET_MASK(f)) | \
+ (((v) & RAW_MASK(f)) << _LSB(f)))
+
+
+////////////////////////////////////////////////////////////////////////////////
+// //
+// Internal macros //
+// //
+////////////////////////////////////////////////////////////////////////////////
+
+#define _F_START(f) (0 ? f)
+#define _F_END(f) (1 ? f)
+#define _F_SIZE(f) (1 + _F_END(f) - _F_START(f))
+#define _F_MASK(f) (((1 << _F_SIZE(f)) - 1) << _F_START(f))
+#define _F_NORMALIZE(v, f) (((v) & _F_MASK(f)) >> _F_START(f))
+#define _F_DENORMALIZE(v, f) (((v) << _F_START(f)) & _F_MASK(f))
+
+
+////////////////////////////////////////////////////////////////////////////////
+// //
+// Global macros //
+// //
+////////////////////////////////////////////////////////////////////////////////
+
+#define FIELD_GET(x, reg, field) \
+( \
+ _F_NORMALIZE((x), reg ## _ ## field) \
+)
+
+#define FIELD_SET(x, reg, field, value) \
+( \
+ (x & ~_F_MASK(reg ## _ ## field)) \
+ | _F_DENORMALIZE(reg ## _ ## field ## _ ## value, reg ## _ ## field) \
+)
+
+#define FIELD_VALUE(x, reg, field, value) \
+( \
+ (x & ~_F_MASK(reg ## _ ## field)) \
+ | _F_DENORMALIZE(value, reg ## _ ## field) \
+)
+
+#define FIELD_CLEAR(reg, field) \
+( \
+ ~ _F_MASK(reg ## _ ## field) \
+)
+
+
+////////////////////////////////////////////////////////////////////////////////
+// //
+// Field Macros //
+// //
+////////////////////////////////////////////////////////////////////////////////
+
+#define FIELD_START(field) (0 ? field)
+#define FIELD_END(field) (1 ? field)
+#define FIELD_SIZE(field) (1 + FIELD_END(field) - FIELD_START(field))
+#define FIELD_MASK(field) (((1 << (FIELD_SIZE(field)-1)) | ((1 << (FIELD_SIZE(field)-1)) - 1)) << FIELD_START(field))
+#define FIELD_NORMALIZE(reg, field) (((reg) & FIELD_MASK(field)) >> FIELD_START(field))
+#define FIELD_DENORMALIZE(field, value) (((value) << FIELD_START(field)) & FIELD_MASK(field))
+
+#define FIELD_INIT(reg, field, value) FIELD_DENORMALIZE(reg ## _ ## field, \
+ reg ## _ ## field ## _ ## value)
+#define FIELD_INIT_VAL(reg, field, value) \
+ (FIELD_DENORMALIZE(reg ## _ ## field, value))
+#define FIELD_VAL_SET(x, r, f, v) x = x & ~FIELD_MASK(r ## _ ## f) \
+ | FIELD_DENORMALIZE(r ## _ ## f, r ## _ ## f ## _ ## v)
+
+#define RGB(r, g, b) \
+( \
+ (unsigned long) (((r) << 16) | ((g) << 8) | (b)) \
+)
+
+#define RGB16(r, g, b) \
+( \
+ (unsigned short) ((((r) & 0xF8) << 8) | (((g) & 0xFC) << 3) | (((b) & 0xF8) >> 3)) \
+)
+
+static inline unsigned int absDiff(unsigned int a,unsigned int b)
+{
+ if(a<b)
+ return b-a;
+ else
+ return a-b;
+}
+
+/* n / d + 1 / 2 = (2n + d) / 2d */
+#define roundedDiv(num,denom) ((2 * (num) + (denom)) / (2 * (denom)))
+#define MB(x) ((x)<<20)
+#define KB(x) ((x)<<10)
+#define MHz(x) ((x) * 1000000)
+
+
+
+
+#endif
diff --git a/drivers/staging/sm750fb/sm750_hw.c b/drivers/staging/sm750fb/sm750_hw.c
new file mode 100644
index 000000000..9f0d06da1
--- /dev/null
+++ b/drivers/staging/sm750fb/sm750_hw.c
@@ -0,0 +1,642 @@
+#include <linux/version.h>
+#include<linux/module.h>
+#include<linux/kernel.h>
+#include<linux/errno.h>
+#include<linux/string.h>
+#include<linux/mm.h>
+#include<linux/slab.h>
+#include<linux/delay.h>
+#include<linux/fb.h>
+#include<linux/ioport.h>
+#include<linux/init.h>
+#include<linux/pci.h>
+#include<linux/vmalloc.h>
+#include<linux/pagemap.h>
+#include <linux/console.h>
+#ifdef CONFIG_MTRR
+#include <asm/mtrr.h>
+#endif
+#include<linux/platform_device.h>
+#include<linux/screen_info.h>
+
+#include "sm750.h"
+#include "sm750_hw.h"
+#include "ddk750.h"
+#include "sm750_accel.h"
+
+int hw_sm750_map(struct lynx_share* share, struct pci_dev* pdev)
+{
+ int ret;
+ struct sm750_share * spec_share;
+
+
+ spec_share = container_of(share, struct sm750_share,share);
+ ret = 0;
+
+ share->vidreg_start = pci_resource_start(pdev, 1);
+ share->vidreg_size = MB(2);
+
+ pr_info("mmio phyAddr = %lx\n", share->vidreg_start);
+
+ /* reserve the vidreg space of smi adaptor
+ * if you do this, u need to add release region code
+ * in lynxfb_remove, or memory will not be mapped again
+ * successfully
+ * */
+
+ if((ret = pci_request_region(pdev, 1, "sm750fb")))
+ {
+ pr_err("Can not request PCI regions.\n");
+ goto exit;
+ }
+
+ /* now map mmio and vidmem*/
+ share->pvReg = ioremap_nocache(share->vidreg_start, share->vidreg_size);
+ if(!share->pvReg){
+ pr_err("mmio failed\n");
+ ret = -EFAULT;
+ goto exit;
+ }else{
+ pr_info("mmio virtual addr = %p\n", share->pvReg);
+ }
+
+
+ share->accel.dprBase = share->pvReg + DE_BASE_ADDR_TYPE1;
+ share->accel.dpPortBase = share->pvReg + DE_PORT_ADDR_TYPE1;
+
+ ddk750_set_mmio(share->pvReg,share->devid, share->revid);
+
+ share->vidmem_start = pci_resource_start(pdev, 0);
+ /* don't use pdev_resource[x].end - resource[x].start to
+ * calculate the resource size,its only the maximum available
+ * size but not the actual size,use
+ * @hw_sm750_getVMSize function can be safe.
+ * */
+ share->vidmem_size = hw_sm750_getVMSize(share);
+ pr_info("video memory phyAddr = %lx, size = %u bytes\n",
+ share->vidmem_start, share->vidmem_size);
+
+ /* reserve the vidmem space of smi adaptor */
+#if 0
+ if((ret = pci_request_region(pdev,0,_moduleName_)))
+ {
+ pr_err("Can not request PCI regions.\n");
+ goto exit;
+ }
+#endif
+
+ share->pvMem = ioremap(share->vidmem_start,
+ share->vidmem_size);
+
+ if(!share->pvMem){
+ pr_err("Map video memory failed\n");
+ ret = -EFAULT;
+ goto exit;
+ }else{
+ pr_info("video memory vaddr = %p\n", share->pvMem);
+ }
+exit:
+ return ret;
+}
+
+
+
+int hw_sm750_inithw(struct lynx_share* share, struct pci_dev * pdev)
+{
+ struct sm750_share * spec_share;
+ struct init_status * parm;
+
+ spec_share = container_of(share, struct sm750_share,share);
+ parm = &spec_share->state.initParm;
+ if(parm->chip_clk == 0)
+ parm->chip_clk = (getChipType() == SM750LE)?
+ DEFAULT_SM750LE_CHIP_CLOCK :
+ DEFAULT_SM750_CHIP_CLOCK;
+
+ if(parm->mem_clk == 0)
+ parm->mem_clk = parm->chip_clk;
+ if(parm->master_clk == 0)
+ parm->master_clk = parm->chip_clk/3;
+
+ ddk750_initHw((initchip_param_t *)&spec_share->state.initParm);
+ /* for sm718,open pci burst */
+ if(share->devid == 0x718){
+ POKE32(SYSTEM_CTRL,
+ FIELD_SET(PEEK32(SYSTEM_CTRL), SYSTEM_CTRL, PCI_BURST, ON));
+ }
+
+ /* sm750 use sii164, it can be setup with default value
+ * by on power, so initDVIDisp can be skipped */
+#if 0
+ ddk750_initDVIDisp();
+#endif
+
+ if(getChipType() != SM750LE)
+ {
+ /* does user need CRT ?*/
+ if(spec_share->state.nocrt){
+ POKE32(MISC_CTRL,
+ FIELD_SET(PEEK32(MISC_CTRL),
+ MISC_CTRL,
+ DAC_POWER, OFF));
+ /* shut off dpms */
+ POKE32(SYSTEM_CTRL,
+ FIELD_SET(PEEK32(SYSTEM_CTRL),
+ SYSTEM_CTRL,
+ DPMS, VNHN));
+ }else{
+ POKE32(MISC_CTRL,
+ FIELD_SET(PEEK32(MISC_CTRL),
+ MISC_CTRL,
+ DAC_POWER, ON));
+ /* turn on dpms */
+ POKE32(SYSTEM_CTRL,
+ FIELD_SET(PEEK32(SYSTEM_CTRL),
+ SYSTEM_CTRL,
+ DPMS, VPHP));
+ }
+
+ switch (spec_share->state.pnltype){
+ case sm750_doubleTFT:
+ case sm750_24TFT:
+ case sm750_dualTFT:
+ POKE32(PANEL_DISPLAY_CTRL,
+ FIELD_VALUE(PEEK32(PANEL_DISPLAY_CTRL),
+ PANEL_DISPLAY_CTRL,
+ TFT_DISP,
+ spec_share->state.pnltype));
+ break;
+ }
+ }else{
+ /* for 750LE ,no DVI chip initilization makes Monitor no signal */
+ /* Set up GPIO for software I2C to program DVI chip in the
+ Xilinx SP605 board, in order to have video signal.
+ */
+ swI2CInit(0,1);
+
+
+ /* Customer may NOT use CH7301 DVI chip, which has to be
+ initialized differently.
+ */
+ if (swI2CReadReg(0xec, 0x4a) == 0x95)
+ {
+ /* The following register values for CH7301 are from
+ Chrontel app note and our experiment.
+ */
+ pr_info("yes,CH7301 DVI chip found\n");
+ swI2CWriteReg(0xec, 0x1d, 0x16);
+ swI2CWriteReg(0xec, 0x21, 0x9);
+ swI2CWriteReg(0xec, 0x49, 0xC0);
+ pr_info("okay,CH7301 DVI chip setup done\n");
+ }
+ }
+
+ /* init 2d engine */
+ if(!share->accel_off){
+ hw_sm750_initAccel(share);
+// share->accel.de_wait = hw_sm750_deWait;
+ }
+
+ return 0;
+}
+
+
+resource_size_t hw_sm750_getVMSize(struct lynx_share * share)
+{
+ resource_size_t ret;
+
+ ret = ddk750_getVMSize();
+ return ret;
+}
+
+
+
+int hw_sm750_output_checkMode(struct lynxfb_output* output, struct fb_var_screeninfo* var)
+{
+
+ return 0;
+}
+
+
+int hw_sm750_output_setMode(struct lynxfb_output* output,
+ struct fb_var_screeninfo* var, struct fb_fix_screeninfo* fix)
+{
+ int ret;
+ disp_output_t dispSet;
+ int channel;
+
+ ret = 0;
+ dispSet = 0;
+ channel = *output->channel;
+
+
+ if(getChipType() != SM750LE){
+ if(channel == sm750_primary){
+ pr_info("primary channel\n");
+ if(output->paths & sm750_panel)
+ dispSet |= do_LCD1_PRI;
+ if(output->paths & sm750_crt)
+ dispSet |= do_CRT_PRI;
+
+ }else{
+ pr_info("secondary channel\n");
+ if(output->paths & sm750_panel)
+ dispSet |= do_LCD1_SEC;
+ if(output->paths & sm750_crt)
+ dispSet |= do_CRT_SEC;
+
+ }
+ ddk750_setLogicalDispOut(dispSet);
+ }else{
+ /* just open DISPLAY_CONTROL_750LE register bit 3:0*/
+ u32 reg;
+ reg = PEEK32(DISPLAY_CONTROL_750LE);
+ reg |= 0xf;
+ POKE32(DISPLAY_CONTROL_750LE, reg);
+ }
+
+ pr_info("ddk setlogicdispout done \n");
+ return ret;
+}
+
+void hw_sm750_output_clear(struct lynxfb_output* output)
+{
+
+ return;
+}
+
+int hw_sm750_crtc_checkMode(struct lynxfb_crtc* crtc, struct fb_var_screeninfo* var)
+{
+ struct lynx_share * share;
+
+
+ share = container_of(crtc, struct lynxfb_par,crtc)->share;
+
+ switch (var->bits_per_pixel){
+ case 8:
+ case 16:
+ break;
+ case 32:
+ if (share->revid == SM750LE_REVISION_ID) {
+ pr_debug("750le do not support 32bpp\n");
+ return -EINVAL;
+ }
+ break;
+ default:
+ return -EINVAL;
+
+ }
+
+ return 0;
+}
+
+
+/*
+ set the controller's mode for @crtc charged with @var and @fix parameters
+*/
+int hw_sm750_crtc_setMode(struct lynxfb_crtc* crtc,
+ struct fb_var_screeninfo* var,
+ struct fb_fix_screeninfo* fix)
+{
+ int ret,fmt;
+ u32 reg;
+ mode_parameter_t modparm;
+ clock_type_t clock;
+ struct lynx_share * share;
+ struct lynxfb_par * par;
+
+
+ ret = 0;
+ par = container_of(crtc, struct lynxfb_par, crtc);
+ share = par->share;
+#if 1
+ if(!share->accel_off){
+ /* set 2d engine pixel format according to mode bpp */
+ switch(var->bits_per_pixel){
+ case 8:
+ fmt = 0;
+ break;
+ case 16:
+ fmt = 1;
+ break;
+ case 32:
+ default:
+ fmt = 2;
+ break;
+ }
+ hw_set2dformat(&share->accel, fmt);
+ }
+#endif
+
+ /* set timing */
+// modparm.pixel_clock = PS_TO_HZ(var->pixclock);
+ modparm.pixel_clock = ps_to_hz(var->pixclock);
+ modparm.vertical_sync_polarity = (var->sync & FB_SYNC_HOR_HIGH_ACT) ? POS:NEG;
+ modparm.horizontal_sync_polarity = (var->sync & FB_SYNC_VERT_HIGH_ACT) ? POS:NEG;
+ modparm.clock_phase_polarity = (var->sync& FB_SYNC_COMP_HIGH_ACT) ? POS:NEG;
+ modparm.horizontal_display_end = var->xres;
+ modparm.horizontal_sync_width = var->hsync_len;
+ modparm.horizontal_sync_start = var->xres + var->right_margin;
+ modparm.horizontal_total = var->xres + var->left_margin + var->right_margin + var->hsync_len;
+ modparm.vertical_display_end = var->yres;
+ modparm.vertical_sync_height = var->vsync_len;
+ modparm.vertical_sync_start = var->yres + var->lower_margin;
+ modparm.vertical_total = var->yres + var->upper_margin + var->lower_margin + var->vsync_len;
+
+ /* choose pll */
+ if(crtc->channel != sm750_secondary)
+ clock = PRIMARY_PLL;
+ else
+ clock = SECONDARY_PLL;
+
+ pr_debug("Request pixel clock = %lu\n", modparm.pixel_clock);
+ ret = ddk750_setModeTiming(&modparm, clock);
+ if(ret){
+ pr_err("Set mode timing failed\n");
+ goto exit;
+ }
+
+ if(crtc->channel != sm750_secondary){
+ /* set pitch, offset ,width,start address ,etc... */
+ POKE32(PANEL_FB_ADDRESS,
+ FIELD_SET(0, PANEL_FB_ADDRESS, STATUS, CURRENT)|
+ FIELD_SET(0, PANEL_FB_ADDRESS, EXT, LOCAL)|
+ FIELD_VALUE(0, PANEL_FB_ADDRESS, ADDRESS, crtc->oScreen));
+
+ reg = var->xres * (var->bits_per_pixel >> 3);
+ /* crtc->channel is not equal to par->index on numeric,be aware of that */
+ reg = PADDING(crtc->line_pad,reg);
+
+ POKE32(PANEL_FB_WIDTH,
+ FIELD_VALUE(0, PANEL_FB_WIDTH, WIDTH, reg)|
+ FIELD_VALUE(0, PANEL_FB_WIDTH, OFFSET, fix->line_length));
+
+ POKE32(PANEL_WINDOW_WIDTH,
+ FIELD_VALUE(0, PANEL_WINDOW_WIDTH, WIDTH, var->xres -1)|
+ FIELD_VALUE(0, PANEL_WINDOW_WIDTH, X, var->xoffset));
+
+ POKE32(PANEL_WINDOW_HEIGHT,
+ FIELD_VALUE(0, PANEL_WINDOW_HEIGHT, HEIGHT, var->yres_virtual - 1)|
+ FIELD_VALUE(0, PANEL_WINDOW_HEIGHT, Y, var->yoffset));
+
+ POKE32(PANEL_PLANE_TL, 0);
+
+ POKE32(PANEL_PLANE_BR,
+ FIELD_VALUE(0, PANEL_PLANE_BR, BOTTOM, var->yres - 1)|
+ FIELD_VALUE(0, PANEL_PLANE_BR,RIGHT, var->xres - 1));
+
+ /* set pixel format */
+ reg = PEEK32(PANEL_DISPLAY_CTRL);
+ POKE32(PANEL_DISPLAY_CTRL,
+ FIELD_VALUE(reg,
+ PANEL_DISPLAY_CTRL, FORMAT,
+ (var->bits_per_pixel >> 4)
+ ));
+ }else{
+ /* not implemented now */
+ POKE32(CRT_FB_ADDRESS, crtc->oScreen);
+ reg = var->xres * (var->bits_per_pixel >> 3);
+ /* crtc->channel is not equal to par->index on numeric,be aware of that */
+ reg = PADDING(crtc->line_pad, reg);
+
+ POKE32(CRT_FB_WIDTH,
+ FIELD_VALUE(0, CRT_FB_WIDTH, WIDTH, reg)|
+ FIELD_VALUE(0, CRT_FB_WIDTH, OFFSET, fix->line_length));
+
+ /* SET PIXEL FORMAT */
+ reg = PEEK32(CRT_DISPLAY_CTRL);
+ reg = FIELD_VALUE(reg, CRT_DISPLAY_CTRL, FORMAT, var->bits_per_pixel >> 4);
+ POKE32(CRT_DISPLAY_CTRL, reg);
+
+ }
+
+
+exit:
+ return ret;
+}
+
+void hw_sm750_crtc_clear(struct lynxfb_crtc* crtc)
+{
+
+ return;
+}
+
+int hw_sm750_setColReg(struct lynxfb_crtc* crtc, ushort index,
+ ushort red, ushort green, ushort blue)
+{
+ static unsigned int add[]={PANEL_PALETTE_RAM,CRT_PALETTE_RAM};
+ POKE32(add[crtc->channel] + index*4, (red<<16)|(green<<8)|blue);
+ return 0;
+}
+
+int hw_sm750le_setBLANK(struct lynxfb_output * output, int blank){
+ int dpms,crtdb;
+
+ switch(blank)
+ {
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,10)
+ case FB_BLANK_UNBLANK:
+#else
+ case VESA_NO_BLANKING:
+#endif
+ dpms = CRT_DISPLAY_CTRL_DPMS_0;
+ crtdb = CRT_DISPLAY_CTRL_BLANK_OFF;
+ break;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,10)
+ case FB_BLANK_NORMAL:
+ dpms = CRT_DISPLAY_CTRL_DPMS_0;
+ crtdb = CRT_DISPLAY_CTRL_BLANK_ON;
+ break;
+#endif
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,10)
+ case FB_BLANK_VSYNC_SUSPEND:
+#else
+ case VESA_VSYNC_SUSPEND:
+#endif
+ dpms = CRT_DISPLAY_CTRL_DPMS_2;
+ crtdb = CRT_DISPLAY_CTRL_BLANK_ON;
+ break;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,10)
+ case FB_BLANK_HSYNC_SUSPEND:
+#else
+ case VESA_HSYNC_SUSPEND:
+#endif
+ dpms = CRT_DISPLAY_CTRL_DPMS_1;
+ crtdb = CRT_DISPLAY_CTRL_BLANK_ON;
+ break;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,10)
+ case FB_BLANK_POWERDOWN:
+#else
+ case VESA_POWERDOWN:
+#endif
+ dpms = CRT_DISPLAY_CTRL_DPMS_3;
+ crtdb = CRT_DISPLAY_CTRL_BLANK_ON;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if(output->paths & sm750_crt){
+ POKE32(CRT_DISPLAY_CTRL, FIELD_VALUE(PEEK32(CRT_DISPLAY_CTRL), CRT_DISPLAY_CTRL, DPMS, dpms));
+ POKE32(CRT_DISPLAY_CTRL, FIELD_VALUE(PEEK32(CRT_DISPLAY_CTRL), CRT_DISPLAY_CTRL, BLANK, crtdb));
+ }
+ return 0;
+}
+
+int hw_sm750_setBLANK(struct lynxfb_output* output,int blank)
+{
+ unsigned int dpms, pps, crtdb;
+
+ dpms = pps = crtdb = 0;
+
+ switch (blank)
+ {
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,10)
+ case FB_BLANK_UNBLANK:
+#else
+ case VESA_NO_BLANKING:
+#endif
+ pr_info("flag = FB_BLANK_UNBLANK \n");
+ dpms = SYSTEM_CTRL_DPMS_VPHP;
+ pps = PANEL_DISPLAY_CTRL_DATA_ENABLE;
+ crtdb = CRT_DISPLAY_CTRL_BLANK_OFF;
+ break;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,10)
+ case FB_BLANK_NORMAL:
+ pr_info("flag = FB_BLANK_NORMAL \n");
+ dpms = SYSTEM_CTRL_DPMS_VPHP;
+ pps = PANEL_DISPLAY_CTRL_DATA_DISABLE;
+ crtdb = CRT_DISPLAY_CTRL_BLANK_ON;
+ break;
+#endif
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,10)
+ case FB_BLANK_VSYNC_SUSPEND:
+#else
+ case VESA_VSYNC_SUSPEND:
+#endif
+ dpms = SYSTEM_CTRL_DPMS_VNHP;
+ pps = PANEL_DISPLAY_CTRL_DATA_DISABLE;
+ crtdb = CRT_DISPLAY_CTRL_BLANK_ON;
+ break;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,10)
+ case FB_BLANK_HSYNC_SUSPEND:
+#else
+ case VESA_HSYNC_SUSPEND:
+#endif
+ dpms = SYSTEM_CTRL_DPMS_VPHN;
+ pps = PANEL_DISPLAY_CTRL_DATA_DISABLE;
+ crtdb = CRT_DISPLAY_CTRL_BLANK_ON;
+ break;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,10)
+ case FB_BLANK_POWERDOWN:
+#else
+ case VESA_POWERDOWN:
+#endif
+ dpms = SYSTEM_CTRL_DPMS_VNHN;
+ pps = PANEL_DISPLAY_CTRL_DATA_DISABLE;
+ crtdb = CRT_DISPLAY_CTRL_BLANK_ON;
+ break;
+ }
+
+ if(output->paths & sm750_crt){
+
+ POKE32(SYSTEM_CTRL,FIELD_VALUE(PEEK32(SYSTEM_CTRL), SYSTEM_CTRL, DPMS, dpms));
+ POKE32(CRT_DISPLAY_CTRL,FIELD_VALUE(PEEK32(CRT_DISPLAY_CTRL), CRT_DISPLAY_CTRL,BLANK, crtdb));
+ }
+
+ if(output->paths & sm750_panel){
+ POKE32(PANEL_DISPLAY_CTRL, FIELD_VALUE(PEEK32(PANEL_DISPLAY_CTRL), PANEL_DISPLAY_CTRL, DATA, pps));
+ }
+
+ return 0;
+}
+
+
+void hw_sm750_initAccel(struct lynx_share * share)
+{
+ u32 reg;
+ enable2DEngine(1);
+
+ if(getChipType() == SM750LE){
+ reg = PEEK32(DE_STATE1);
+ reg = FIELD_SET(reg, DE_STATE1, DE_ABORT,ON);
+ POKE32(DE_STATE1,reg);
+
+ reg = PEEK32(DE_STATE1);
+ reg = FIELD_SET(reg, DE_STATE1, DE_ABORT,OFF);
+ POKE32(DE_STATE1, reg);
+
+ }else{
+ /* engine reset */
+ reg = PEEK32(SYSTEM_CTRL);
+ reg = FIELD_SET(reg, SYSTEM_CTRL, DE_ABORT,ON);
+ POKE32(SYSTEM_CTRL, reg);
+
+ reg = PEEK32(SYSTEM_CTRL);
+ reg = FIELD_SET(reg, SYSTEM_CTRL, DE_ABORT,OFF);
+ POKE32(SYSTEM_CTRL, reg);
+ }
+
+ /* call 2d init */
+ share->accel.de_init(&share->accel);
+}
+
+int hw_sm750le_deWait(void)
+{
+ int i=0x10000000;
+ while(i--){
+ unsigned int dwVal = PEEK32(DE_STATE2);
+ if((FIELD_GET(dwVal, DE_STATE2, DE_STATUS) == DE_STATE2_DE_STATUS_IDLE) &&
+ (FIELD_GET(dwVal, DE_STATE2, DE_FIFO) == DE_STATE2_DE_FIFO_EMPTY) &&
+ (FIELD_GET(dwVal, DE_STATE2, DE_MEM_FIFO) == DE_STATE2_DE_MEM_FIFO_EMPTY))
+ {
+ return 0;
+ }
+ }
+ /* timeout error */
+ return -1;
+}
+
+
+int hw_sm750_deWait(void)
+{
+ int i=0x10000000;
+ while(i--){
+ unsigned int dwVal = PEEK32(SYSTEM_CTRL);
+ if((FIELD_GET(dwVal,SYSTEM_CTRL,DE_STATUS) == SYSTEM_CTRL_DE_STATUS_IDLE) &&
+ (FIELD_GET(dwVal,SYSTEM_CTRL,DE_FIFO) == SYSTEM_CTRL_DE_FIFO_EMPTY) &&
+ (FIELD_GET(dwVal,SYSTEM_CTRL,DE_MEM_FIFO) == SYSTEM_CTRL_DE_MEM_FIFO_EMPTY))
+ {
+ return 0;
+ }
+ }
+ /* timeout error */
+ return -1;
+}
+
+int hw_sm750_pan_display(struct lynxfb_crtc *crtc,
+ const struct fb_var_screeninfo *var,
+ const struct fb_info *info)
+{
+ uint32_t total;
+ //check params
+ if ((var->xoffset + var->xres > var->xres_virtual) ||
+ (var->yoffset + var->yres > var->yres_virtual)) {
+ return -EINVAL;
+ }
+
+ total = var->yoffset * info->fix.line_length +
+ ((var->xoffset * var->bits_per_pixel) >> 3);
+ total += crtc->oScreen;
+ if (crtc->channel == sm750_primary) {
+ POKE32(PANEL_FB_ADDRESS,
+ FIELD_VALUE(PEEK32(PANEL_FB_ADDRESS),
+ PANEL_FB_ADDRESS, ADDRESS, total));
+ } else {
+ POKE32(CRT_FB_ADDRESS,
+ FIELD_VALUE(PEEK32(CRT_FB_ADDRESS),
+ CRT_FB_ADDRESS, ADDRESS, total));
+ }
+ return 0;
+}
+
diff --git a/drivers/staging/sm750fb/sm750_hw.h b/drivers/staging/sm750fb/sm750_hw.h
new file mode 100644
index 000000000..b05be5e99
--- /dev/null
+++ b/drivers/staging/sm750fb/sm750_hw.h
@@ -0,0 +1,104 @@
+#ifndef LYNX_HW750_H__
+#define LYNX_HW750_H__
+
+
+#define DEFAULT_SM750_CHIP_CLOCK 290
+#define DEFAULT_SM750LE_CHIP_CLOCK 333
+#ifndef SM750LE_REVISION_ID
+#define SM750LE_REVISION_ID (unsigned char)0xfe
+#endif
+
+//#define DEFAULT_MEM_CLOCK (DEFAULT_SM750_CHIP_CLOCK/1)
+//#define DEFAULT_MASTER_CLOCK (DEFAULT_SM750_CHIP_CLOCK/3)
+
+
+enum sm750_pnltype{
+
+ sm750_24TFT = 0,/* 24bit tft */
+
+ sm750_dualTFT = 2,/* dual 18 bit tft */
+
+ sm750_doubleTFT = 1,/* 36 bit double pixel tft */
+};
+
+/* vga channel is not concerned */
+enum sm750_dataflow{
+ sm750_simul_pri,/* primary => all head */
+
+ sm750_simul_sec,/* secondary => all head */
+
+ sm750_dual_normal,/* primary => panel head and secondary => crt */
+
+ sm750_dual_swap,/* primary => crt head and secondary => panel */
+};
+
+
+enum sm750_channel{
+ sm750_primary = 0,
+ /* enum value equal to the register filed data */
+ sm750_secondary = 1,
+};
+
+enum sm750_path{
+ sm750_panel = 1,
+ sm750_crt = 2,
+ sm750_pnc = 3,/* panel and crt */
+};
+
+struct init_status{
+ ushort powerMode;
+ /* below three clocks are in unit of MHZ*/
+ ushort chip_clk;
+ ushort mem_clk;
+ ushort master_clk;
+ ushort setAllEngOff;
+ ushort resetMemory;
+};
+
+struct sm750_state{
+ struct init_status initParm;
+ enum sm750_pnltype pnltype;
+ enum sm750_dataflow dataflow;
+ int nocrt;
+ int xLCD;
+ int yLCD;
+};
+
+/* sm750_share stands for a presentation of two frame buffer
+ that use one sm750 adaptor, it is similiar to the super class of lynx_share
+ in C++
+*/
+
+struct sm750_share{
+ /* it's better to put lynx_share struct to the first place of sm750_share */
+ struct lynx_share share;
+ struct sm750_state state;
+ int hwCursor;
+ /* 0: no hardware cursor
+ 1: primary crtc hw cursor enabled,
+ 2: secondary crtc hw cursor enabled
+ 3: both ctrc hw cursor enabled
+ */
+};
+
+int hw_sm750_map(struct lynx_share* share,struct pci_dev* pdev);
+int hw_sm750_inithw(struct lynx_share*,struct pci_dev *);
+void hw_sm750_initAccel(struct lynx_share *);
+int hw_sm750_deWait(void);
+int hw_sm750le_deWait(void);
+
+resource_size_t hw_sm750_getVMSize(struct lynx_share *);
+int hw_sm750_output_checkMode(struct lynxfb_output*,struct fb_var_screeninfo*);
+int hw_sm750_output_setMode(struct lynxfb_output*,struct fb_var_screeninfo*,struct fb_fix_screeninfo*);
+int hw_sm750_crtc_checkMode(struct lynxfb_crtc*,struct fb_var_screeninfo*);
+int hw_sm750_crtc_setMode(struct lynxfb_crtc*,struct fb_var_screeninfo*,struct fb_fix_screeninfo*);
+int hw_sm750_setColReg(struct lynxfb_crtc*,ushort,ushort,ushort,ushort);
+int hw_sm750_setBLANK(struct lynxfb_output*,int);
+int hw_sm750le_setBLANK(struct lynxfb_output*,int);
+void hw_sm750_crtc_clear(struct lynxfb_crtc*);
+void hw_sm750_output_clear(struct lynxfb_output*);
+int hw_sm750_pan_display(struct lynxfb_crtc *crtc,
+ const struct fb_var_screeninfo *var,
+ const struct fb_info *info);
+
+#endif
diff --git a/drivers/staging/sm7xxfb/Kconfig b/drivers/staging/sm7xxfb/Kconfig
new file mode 100644
index 000000000..e2922ae3a
--- /dev/null
+++ b/drivers/staging/sm7xxfb/Kconfig
@@ -0,0 +1,13 @@
+config FB_SM7XX
+ tristate "Silicon Motion SM7XX framebuffer support"
+ depends on FB && PCI
+ select FB_CFB_FILLRECT
+ select FB_CFB_COPYAREA
+ select FB_CFB_IMAGEBLIT
+ help
+ Frame buffer driver for the Silicon Motion SM710, SM712, SM721
+ and SM722 chips.
+
+ This driver is also available as a module. The module will be
+ called sm7xxfb. If you want to compile it as a module, say M
+ here and read <file:Documentation/kbuild/modules.txt>.
diff --git a/drivers/staging/sm7xxfb/Makefile b/drivers/staging/sm7xxfb/Makefile
new file mode 100644
index 000000000..48f471cf9
--- /dev/null
+++ b/drivers/staging/sm7xxfb/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_FB_SM7XX) += sm7xxfb.o
diff --git a/drivers/staging/sm7xxfb/TODO b/drivers/staging/sm7xxfb/TODO
new file mode 100644
index 000000000..7cb0b242f
--- /dev/null
+++ b/drivers/staging/sm7xxfb/TODO
@@ -0,0 +1,12 @@
+TODO:
+- Dual head support
+- 2D acceleration support
+- use kernel coding style
+- refine the code and remove unused code
+- move it to drivers/video/fbdev/sm7xxfb.c
+
+Please send any patches to
+ Greg Kroah-Hartman <greg@kroah.com>
+ Sudip Mukherjee <sudipm.mukherjee@gmail.com>
+ Teddy Wang <teddy.wang@siliconmotion.com>
+ Sudip Mukherjee <sudip@vectorindia.org>
diff --git a/drivers/staging/sm7xxfb/sm7xx.h b/drivers/staging/sm7xxfb/sm7xx.h
new file mode 100644
index 000000000..c5d62534e
--- /dev/null
+++ b/drivers/staging/sm7xxfb/sm7xx.h
@@ -0,0 +1,779 @@
+/*
+ * Silicon Motion SM712 frame buffer device
+ *
+ * Copyright (C) 2006 Silicon Motion Technology Corp.
+ * Authors: Ge Wang, gewang@siliconmotion.com
+ * Boyod boyod.yang@siliconmotion.com.cn
+ *
+ * Copyright (C) 2009 Lemote, Inc.
+ * Author: Wu Zhangjin, wuzhangjin@gmail.com
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file COPYING in the main directory of this archive for
+ * more details.
+ */
+
+#define NR_PALETTE 256
+
+#define FB_ACCEL_SMI_LYNX 88
+
+#define SCREEN_X_RES 1024
+#define SCREEN_Y_RES 600
+#define SCREEN_BPP 16
+
+/*Assume SM712 graphics chip has 4MB VRAM */
+#define SM712_VIDEOMEMORYSIZE 0x00400000
+/*Assume SM722 graphics chip has 8MB VRAM */
+#define SM722_VIDEOMEMORYSIZE 0x00800000
+
+#define dac_reg (0x3c8)
+#define dac_val (0x3c9)
+
+extern void __iomem *smtc_regbaseaddress;
+#define smtc_mmiowb(dat, reg) writeb(dat, smtc_regbaseaddress + reg)
+#define smtc_mmioww(dat, reg) writew(dat, smtc_regbaseaddress + reg)
+#define smtc_mmiowl(dat, reg) writel(dat, smtc_regbaseaddress + reg)
+
+#define smtc_mmiorb(reg) readb(smtc_regbaseaddress + reg)
+#define smtc_mmiorw(reg) readw(smtc_regbaseaddress + reg)
+#define smtc_mmiorl(reg) readl(smtc_regbaseaddress + reg)
+
+#define SIZE_SR00_SR04 (0x04 - 0x00 + 1)
+#define SIZE_SR10_SR24 (0x24 - 0x10 + 1)
+#define SIZE_SR30_SR75 (0x75 - 0x30 + 1)
+#define SIZE_SR80_SR93 (0x93 - 0x80 + 1)
+#define SIZE_SRA0_SRAF (0xAF - 0xA0 + 1)
+#define SIZE_GR00_GR08 (0x08 - 0x00 + 1)
+#define SIZE_AR00_AR14 (0x14 - 0x00 + 1)
+#define SIZE_CR00_CR18 (0x18 - 0x00 + 1)
+#define SIZE_CR30_CR4D (0x4D - 0x30 + 1)
+#define SIZE_CR90_CRA7 (0xA7 - 0x90 + 1)
+#define SIZE_VPR (0x6C + 1)
+#define SIZE_DPR (0x44 + 1)
+
+static inline void smtc_crtcw(int reg, int val)
+{
+ smtc_mmiowb(reg, 0x3d4);
+ smtc_mmiowb(val, 0x3d5);
+}
+
+static inline unsigned int smtc_crtcr(int reg)
+{
+ smtc_mmiowb(reg, 0x3d4);
+ return smtc_mmiorb(0x3d5);
+}
+
+static inline void smtc_grphw(int reg, int val)
+{
+ smtc_mmiowb(reg, 0x3ce);
+ smtc_mmiowb(val, 0x3cf);
+}
+
+static inline unsigned int smtc_grphr(int reg)
+{
+ smtc_mmiowb(reg, 0x3ce);
+ return smtc_mmiorb(0x3cf);
+}
+
+static inline void smtc_attrw(int reg, int val)
+{
+ smtc_mmiorb(0x3da);
+ smtc_mmiowb(reg, 0x3c0);
+ smtc_mmiorb(0x3c1);
+ smtc_mmiowb(val, 0x3c0);
+}
+
+static inline void smtc_seqw(int reg, int val)
+{
+ smtc_mmiowb(reg, 0x3c4);
+ smtc_mmiowb(val, 0x3c5);
+}
+
+static inline unsigned int smtc_seqr(int reg)
+{
+ smtc_mmiowb(reg, 0x3c4);
+ return smtc_mmiorb(0x3c5);
+}
+
+/* The next structure holds all information relevant for a specific video mode.
+ */
+
+struct ModeInit {
+ int mmsizex;
+ int mmsizey;
+ int bpp;
+ int hz;
+ unsigned char init_misc;
+ unsigned char init_sr00_sr04[SIZE_SR00_SR04];
+ unsigned char init_sr10_sr24[SIZE_SR10_SR24];
+ unsigned char init_sr30_sr75[SIZE_SR30_SR75];
+ unsigned char init_sr80_sr93[SIZE_SR80_SR93];
+ unsigned char init_sra0_sraf[SIZE_SRA0_SRAF];
+ unsigned char init_gr00_gr08[SIZE_GR00_GR08];
+ unsigned char init_ar00_ar14[SIZE_AR00_AR14];
+ unsigned char init_cr00_cr18[SIZE_CR00_CR18];
+ unsigned char init_cr30_cr4d[SIZE_CR30_CR4D];
+ unsigned char init_cr90_cra7[SIZE_CR90_CRA7];
+};
+
+/**********************************************************************
+ SM712 Mode table.
+ **********************************************************************/
+static struct ModeInit vgamode[] = {
+ {
+ /* mode#0: 640 x 480 16Bpp 60Hz */
+ 640, 480, 16, 60,
+ /* Init_MISC */
+ 0xE3,
+ { /* Init_SR0_SR4 */
+ 0x03, 0x01, 0x0F, 0x00, 0x0E,
+ },
+ { /* Init_SR10_SR24 */
+ 0xFF, 0xBE, 0xEF, 0xFF, 0x00, 0x0E, 0x17, 0x2C,
+ 0x99, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xC4, 0x30, 0x02, 0x01, 0x01,
+ },
+ { /* Init_SR30_SR75 */
+ 0x32, 0x03, 0xA0, 0x09, 0xC0, 0x32, 0x32, 0x32,
+ 0x32, 0x32, 0x32, 0x32, 0x00, 0x00, 0x03, 0xFF,
+ 0x00, 0xFC, 0x00, 0x00, 0x20, 0x18, 0x00, 0xFC,
+ 0x20, 0x0C, 0x44, 0x20, 0x00, 0x32, 0x32, 0x32,
+ 0x04, 0x24, 0x63, 0x4F, 0x52, 0x0B, 0xDF, 0xEA,
+ 0x04, 0x50, 0x19, 0x32, 0x32, 0x00, 0x00, 0x32,
+ 0x01, 0x80, 0x7E, 0x1A, 0x1A, 0x00, 0x00, 0x00,
+ 0x50, 0x03, 0x74, 0x14, 0x07, 0x82, 0x07, 0x04,
+ 0x00, 0x45, 0x30, 0x30, 0x40, 0x30,
+ },
+ { /* Init_SR80_SR93 */
+ 0xFF, 0x07, 0x00, 0x6F, 0x7F, 0x7F, 0xFF, 0x32,
+ 0xF7, 0x00, 0x00, 0x00, 0xEF, 0xFF, 0x32, 0x32,
+ 0x00, 0x00, 0x00, 0x00,
+ },
+ { /* Init_SRA0_SRAF */
+ 0x00, 0xFF, 0xBF, 0xFF, 0xFF, 0xED, 0xED, 0xED,
+ 0x7B, 0xFF, 0xFF, 0xFF, 0xBF, 0xEF, 0xFF, 0xDF,
+ },
+ { /* Init_GR00_GR08 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x05, 0x0F,
+ 0xFF,
+ },
+ { /* Init_AR00_AR14 */
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
+ 0x41, 0x00, 0x0F, 0x00, 0x00,
+ },
+ { /* Init_CR00_CR18 */
+ 0x5F, 0x4F, 0x4F, 0x00, 0x53, 0x1F, 0x0B, 0x3E,
+ 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xEA, 0x0C, 0xDF, 0x50, 0x40, 0xDF, 0x00, 0xE3,
+ 0xFF,
+ },
+ { /* Init_CR30_CR4D */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x55, 0x03, 0x20,
+ 0x00, 0x00, 0x00, 0x40, 0x00, 0xE7, 0xFF, 0xFD,
+ 0x5F, 0x4F, 0x00, 0x54, 0x00, 0x0B, 0xDF, 0x00,
+ 0xEA, 0x0C, 0x2E, 0x00, 0x4F, 0xDF,
+ },
+ { /* Init_CR90_CRA7 */
+ 0x56, 0xDD, 0x5E, 0xEA, 0x87, 0x44, 0x8F, 0x55,
+ 0x0A, 0x8F, 0x55, 0x0A, 0x00, 0x00, 0x18, 0x00,
+ 0x11, 0x10, 0x0B, 0x0A, 0x0A, 0x0A, 0x0A, 0x00,
+ },
+ },
+ {
+ /* mode#1: 640 x 480 24Bpp 60Hz */
+ 640, 480, 24, 60,
+ /* Init_MISC */
+ 0xE3,
+ { /* Init_SR0_SR4 */
+ 0x03, 0x01, 0x0F, 0x00, 0x0E,
+ },
+ { /* Init_SR10_SR24 */
+ 0xFF, 0xBE, 0xEF, 0xFF, 0x00, 0x0E, 0x17, 0x2C,
+ 0x99, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xC4, 0x30, 0x02, 0x01, 0x01,
+ },
+ { /* Init_SR30_SR75 */
+ 0x32, 0x03, 0xA0, 0x09, 0xC0, 0x32, 0x32, 0x32,
+ 0x32, 0x32, 0x32, 0x32, 0x00, 0x00, 0x03, 0xFF,
+ 0x00, 0xFC, 0x00, 0x00, 0x20, 0x18, 0x00, 0xFC,
+ 0x20, 0x0C, 0x44, 0x20, 0x00, 0x32, 0x32, 0x32,
+ 0x04, 0x24, 0x63, 0x4F, 0x52, 0x0B, 0xDF, 0xEA,
+ 0x04, 0x50, 0x19, 0x32, 0x32, 0x00, 0x00, 0x32,
+ 0x01, 0x80, 0x7E, 0x1A, 0x1A, 0x00, 0x00, 0x00,
+ 0x50, 0x03, 0x74, 0x14, 0x07, 0x82, 0x07, 0x04,
+ 0x00, 0x45, 0x30, 0x30, 0x40, 0x30,
+ },
+ { /* Init_SR80_SR93 */
+ 0xFF, 0x07, 0x00, 0x6F, 0x7F, 0x7F, 0xFF, 0x32,
+ 0xF7, 0x00, 0x00, 0x00, 0xEF, 0xFF, 0x32, 0x32,
+ 0x00, 0x00, 0x00, 0x00,
+ },
+ { /* Init_SRA0_SRAF */
+ 0x00, 0xFF, 0xBF, 0xFF, 0xFF, 0xED, 0xED, 0xED,
+ 0x7B, 0xFF, 0xFF, 0xFF, 0xBF, 0xEF, 0xFF, 0xDF,
+ },
+ { /* Init_GR00_GR08 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x05, 0x0F,
+ 0xFF,
+ },
+ { /* Init_AR00_AR14 */
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
+ 0x41, 0x00, 0x0F, 0x00, 0x00,
+ },
+ { /* Init_CR00_CR18 */
+ 0x5F, 0x4F, 0x4F, 0x00, 0x53, 0x1F, 0x0B, 0x3E,
+ 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xEA, 0x0C, 0xDF, 0x50, 0x40, 0xDF, 0x00, 0xE3,
+ 0xFF,
+ },
+ { /* Init_CR30_CR4D */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x55, 0x03, 0x20,
+ 0x00, 0x00, 0x00, 0x40, 0x00, 0xE7, 0xFF, 0xFD,
+ 0x5F, 0x4F, 0x00, 0x54, 0x00, 0x0B, 0xDF, 0x00,
+ 0xEA, 0x0C, 0x2E, 0x00, 0x4F, 0xDF,
+ },
+ { /* Init_CR90_CRA7 */
+ 0x56, 0xDD, 0x5E, 0xEA, 0x87, 0x44, 0x8F, 0x55,
+ 0x0A, 0x8F, 0x55, 0x0A, 0x00, 0x00, 0x18, 0x00,
+ 0x11, 0x10, 0x0B, 0x0A, 0x0A, 0x0A, 0x0A, 0x00,
+ },
+ },
+ {
+ /* mode#0: 640 x 480 32Bpp 60Hz */
+ 640, 480, 32, 60,
+ /* Init_MISC */
+ 0xE3,
+ { /* Init_SR0_SR4 */
+ 0x03, 0x01, 0x0F, 0x00, 0x0E,
+ },
+ { /* Init_SR10_SR24 */
+ 0xFF, 0xBE, 0xEF, 0xFF, 0x00, 0x0E, 0x17, 0x2C,
+ 0x99, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xC4, 0x30, 0x02, 0x01, 0x01,
+ },
+ { /* Init_SR30_SR75 */
+ 0x32, 0x03, 0xA0, 0x09, 0xC0, 0x32, 0x32, 0x32,
+ 0x32, 0x32, 0x32, 0x32, 0x00, 0x00, 0x03, 0xFF,
+ 0x00, 0xFC, 0x00, 0x00, 0x20, 0x18, 0x00, 0xFC,
+ 0x20, 0x0C, 0x44, 0x20, 0x00, 0x32, 0x32, 0x32,
+ 0x04, 0x24, 0x63, 0x4F, 0x52, 0x0B, 0xDF, 0xEA,
+ 0x04, 0x50, 0x19, 0x32, 0x32, 0x00, 0x00, 0x32,
+ 0x01, 0x80, 0x7E, 0x1A, 0x1A, 0x00, 0x00, 0x00,
+ 0x50, 0x03, 0x74, 0x14, 0x07, 0x82, 0x07, 0x04,
+ 0x00, 0x45, 0x30, 0x30, 0x40, 0x30,
+ },
+ { /* Init_SR80_SR93 */
+ 0xFF, 0x07, 0x00, 0x6F, 0x7F, 0x7F, 0xFF, 0x32,
+ 0xF7, 0x00, 0x00, 0x00, 0xEF, 0xFF, 0x32, 0x32,
+ 0x00, 0x00, 0x00, 0x00,
+ },
+ { /* Init_SRA0_SRAF */
+ 0x00, 0xFF, 0xBF, 0xFF, 0xFF, 0xED, 0xED, 0xED,
+ 0x7B, 0xFF, 0xFF, 0xFF, 0xBF, 0xEF, 0xFF, 0xDF,
+ },
+ { /* Init_GR00_GR08 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x05, 0x0F,
+ 0xFF,
+ },
+ { /* Init_AR00_AR14 */
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
+ 0x41, 0x00, 0x0F, 0x00, 0x00,
+ },
+ { /* Init_CR00_CR18 */
+ 0x5F, 0x4F, 0x4F, 0x00, 0x53, 0x1F, 0x0B, 0x3E,
+ 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xEA, 0x0C, 0xDF, 0x50, 0x40, 0xDF, 0x00, 0xE3,
+ 0xFF,
+ },
+ { /* Init_CR30_CR4D */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x55, 0x03, 0x20,
+ 0x00, 0x00, 0x00, 0x40, 0x00, 0xE7, 0xFF, 0xFD,
+ 0x5F, 0x4F, 0x00, 0x54, 0x00, 0x0B, 0xDF, 0x00,
+ 0xEA, 0x0C, 0x2E, 0x00, 0x4F, 0xDF,
+ },
+ { /* Init_CR90_CRA7 */
+ 0x56, 0xDD, 0x5E, 0xEA, 0x87, 0x44, 0x8F, 0x55,
+ 0x0A, 0x8F, 0x55, 0x0A, 0x00, 0x00, 0x18, 0x00,
+ 0x11, 0x10, 0x0B, 0x0A, 0x0A, 0x0A, 0x0A, 0x00,
+ },
+ },
+
+ { /* mode#2: 800 x 600 16Bpp 60Hz */
+ 800, 600, 16, 60,
+ /* Init_MISC */
+ 0x2B,
+ { /* Init_SR0_SR4 */
+ 0x03, 0x01, 0x0F, 0x03, 0x0E,
+ },
+ { /* Init_SR10_SR24 */
+ 0xFF, 0xBE, 0xEE, 0xFF, 0x00, 0x0E, 0x17, 0x2C,
+ 0x99, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xC4, 0x30, 0x02, 0x01, 0x01,
+ },
+ { /* Init_SR30_SR75 */
+ 0x34, 0x03, 0x20, 0x09, 0xC0, 0x24, 0x24, 0x24,
+ 0x24, 0x24, 0x24, 0x24, 0x00, 0x00, 0x03, 0xFF,
+ 0x00, 0xFC, 0x00, 0x00, 0x20, 0x38, 0x00, 0xFC,
+ 0x20, 0x0C, 0x44, 0x20, 0x00, 0x24, 0x24, 0x24,
+ 0x04, 0x48, 0x83, 0x63, 0x68, 0x72, 0x57, 0x58,
+ 0x04, 0x55, 0x59, 0x24, 0x24, 0x00, 0x00, 0x24,
+ 0x01, 0x80, 0x7A, 0x1A, 0x1A, 0x00, 0x00, 0x00,
+ 0x50, 0x03, 0x74, 0x14, 0x1C, 0x85, 0x35, 0x13,
+ 0x02, 0x45, 0x30, 0x35, 0x40, 0x20,
+ },
+ { /* Init_SR80_SR93 */
+ 0x00, 0x00, 0x00, 0x6F, 0x7F, 0x7F, 0xFF, 0x24,
+ 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x24, 0x24,
+ 0x00, 0x00, 0x00, 0x00,
+ },
+ { /* Init_SRA0_SRAF */
+ 0x00, 0xFF, 0xBF, 0xFF, 0xFF, 0xED, 0xED, 0xED,
+ 0x7B, 0xFF, 0xFF, 0xFF, 0xBF, 0xEF, 0xBF, 0xDF,
+ },
+ { /* Init_GR00_GR08 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x05, 0x0F,
+ 0xFF,
+ },
+ { /* Init_AR00_AR14 */
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
+ 0x41, 0x00, 0x0F, 0x00, 0x00,
+ },
+ { /* Init_CR00_CR18 */
+ 0x7F, 0x63, 0x63, 0x00, 0x68, 0x18, 0x72, 0xF0,
+ 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x58, 0x0C, 0x57, 0x64, 0x40, 0x57, 0x00, 0xE3,
+ 0xFF,
+ },
+ { /* Init_CR30_CR4D */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x33, 0x03, 0x20,
+ 0x00, 0x00, 0x00, 0x40, 0x00, 0xE7, 0xBF, 0xFD,
+ 0x7F, 0x63, 0x00, 0x69, 0x18, 0x72, 0x57, 0x00,
+ 0x58, 0x0C, 0xE0, 0x20, 0x63, 0x57,
+ },
+ { /* Init_CR90_CRA7 */
+ 0x56, 0x4B, 0x5E, 0x55, 0x86, 0x9D, 0x8E, 0xAA,
+ 0xDB, 0x2A, 0xDF, 0x33, 0x00, 0x00, 0x18, 0x00,
+ 0x20, 0x1F, 0x1A, 0x19, 0x0F, 0x0F, 0x0F, 0x00,
+ },
+ },
+ { /* mode#3: 800 x 600 24Bpp 60Hz */
+ 800, 600, 24, 60,
+ 0x2B,
+ { /* Init_SR0_SR4 */
+ 0x03, 0x01, 0x0F, 0x03, 0x0E,
+ },
+ { /* Init_SR10_SR24 */
+ 0xFF, 0xBE, 0xEE, 0xFF, 0x00, 0x0E, 0x17, 0x2C,
+ 0x99, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xC4, 0x30, 0x02, 0x01, 0x01,
+ },
+ { /* Init_SR30_SR75 */
+ 0x36, 0x03, 0x20, 0x09, 0xC0, 0x36, 0x36, 0x36,
+ 0x36, 0x36, 0x36, 0x36, 0x00, 0x00, 0x03, 0xFF,
+ 0x00, 0xFC, 0x00, 0x00, 0x20, 0x18, 0x00, 0xFC,
+ 0x20, 0x0C, 0x44, 0x20, 0x00, 0x36, 0x36, 0x36,
+ 0x04, 0x48, 0x83, 0x63, 0x68, 0x72, 0x57, 0x58,
+ 0x04, 0x55, 0x59, 0x36, 0x36, 0x00, 0x00, 0x36,
+ 0x01, 0x80, 0x7E, 0x1A, 0x1A, 0x00, 0x00, 0x00,
+ 0x50, 0x03, 0x74, 0x14, 0x1C, 0x85, 0x35, 0x13,
+ 0x02, 0x45, 0x30, 0x30, 0x40, 0x20,
+ },
+ { /* Init_SR80_SR93 */
+ 0xFF, 0x07, 0x00, 0x6F, 0x7F, 0x7F, 0xFF, 0x36,
+ 0xF7, 0x00, 0x00, 0x00, 0xEF, 0xFF, 0x36, 0x36,
+ 0x00, 0x00, 0x00, 0x00,
+ },
+ { /* Init_SRA0_SRAF */
+ 0x00, 0xFF, 0xBF, 0xFF, 0xFF, 0xED, 0xED, 0xED,
+ 0x7B, 0xFF, 0xFF, 0xFF, 0xBF, 0xEF, 0xBF, 0xDF,
+ },
+ { /* Init_GR00_GR08 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x05, 0x0F,
+ 0xFF,
+ },
+ { /* Init_AR00_AR14 */
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
+ 0x41, 0x00, 0x0F, 0x00, 0x00,
+ },
+ { /* Init_CR00_CR18 */
+ 0x7F, 0x63, 0x63, 0x00, 0x68, 0x18, 0x72, 0xF0,
+ 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x58, 0x0C, 0x57, 0x64, 0x40, 0x57, 0x00, 0xE3,
+ 0xFF,
+ },
+ { /* Init_CR30_CR4D */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x33, 0x03, 0x20,
+ 0x00, 0x00, 0x00, 0x40, 0x00, 0xE7, 0xBF, 0xFD,
+ 0x7F, 0x63, 0x00, 0x69, 0x18, 0x72, 0x57, 0x00,
+ 0x58, 0x0C, 0xE0, 0x20, 0x63, 0x57,
+ },
+ { /* Init_CR90_CRA7 */
+ 0x56, 0x4B, 0x5E, 0x55, 0x86, 0x9D, 0x8E, 0xAA,
+ 0xDB, 0x2A, 0xDF, 0x33, 0x00, 0x00, 0x18, 0x00,
+ 0x20, 0x1F, 0x1A, 0x19, 0x0F, 0x0F, 0x0F, 0x00,
+ },
+ },
+ { /* mode#7: 800 x 600 32Bpp 60Hz */
+ 800, 600, 32, 60,
+ /* Init_MISC */
+ 0x2B,
+ { /* Init_SR0_SR4 */
+ 0x03, 0x01, 0x0F, 0x03, 0x0E,
+ },
+ { /* Init_SR10_SR24 */
+ 0xFF, 0xBE, 0xEE, 0xFF, 0x00, 0x0E, 0x17, 0x2C,
+ 0x99, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xC4, 0x30, 0x02, 0x01, 0x01,
+ },
+ { /* Init_SR30_SR75 */
+ 0x34, 0x03, 0x20, 0x09, 0xC0, 0x24, 0x24, 0x24,
+ 0x24, 0x24, 0x24, 0x24, 0x00, 0x00, 0x03, 0xFF,
+ 0x00, 0xFC, 0x00, 0x00, 0x20, 0x38, 0x00, 0xFC,
+ 0x20, 0x0C, 0x44, 0x20, 0x00, 0x24, 0x24, 0x24,
+ 0x04, 0x48, 0x83, 0x63, 0x68, 0x72, 0x57, 0x58,
+ 0x04, 0x55, 0x59, 0x24, 0x24, 0x00, 0x00, 0x24,
+ 0x01, 0x80, 0x7A, 0x1A, 0x1A, 0x00, 0x00, 0x00,
+ 0x50, 0x03, 0x74, 0x14, 0x1C, 0x85, 0x35, 0x13,
+ 0x02, 0x45, 0x30, 0x35, 0x40, 0x20,
+ },
+ { /* Init_SR80_SR93 */
+ 0x00, 0x00, 0x00, 0x6F, 0x7F, 0x7F, 0xFF, 0x24,
+ 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x24, 0x24,
+ 0x00, 0x00, 0x00, 0x00,
+ },
+ { /* Init_SRA0_SRAF */
+ 0x00, 0xFF, 0xBF, 0xFF, 0xFF, 0xED, 0xED, 0xED,
+ 0x7B, 0xFF, 0xFF, 0xFF, 0xBF, 0xEF, 0xBF, 0xDF,
+ },
+ { /* Init_GR00_GR08 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x05, 0x0F,
+ 0xFF,
+ },
+ { /* Init_AR00_AR14 */
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
+ 0x41, 0x00, 0x0F, 0x00, 0x00,
+ },
+ { /* Init_CR00_CR18 */
+ 0x7F, 0x63, 0x63, 0x00, 0x68, 0x18, 0x72, 0xF0,
+ 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x58, 0x0C, 0x57, 0x64, 0x40, 0x57, 0x00, 0xE3,
+ 0xFF,
+ },
+ { /* Init_CR30_CR4D */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x33, 0x03, 0x20,
+ 0x00, 0x00, 0x00, 0x40, 0x00, 0xE7, 0xBF, 0xFD,
+ 0x7F, 0x63, 0x00, 0x69, 0x18, 0x72, 0x57, 0x00,
+ 0x58, 0x0C, 0xE0, 0x20, 0x63, 0x57,
+ },
+ { /* Init_CR90_CRA7 */
+ 0x56, 0x4B, 0x5E, 0x55, 0x86, 0x9D, 0x8E, 0xAA,
+ 0xDB, 0x2A, 0xDF, 0x33, 0x00, 0x00, 0x18, 0x00,
+ 0x20, 0x1F, 0x1A, 0x19, 0x0F, 0x0F, 0x0F, 0x00,
+ },
+ },
+ /* We use 1024x768 table to light 1024x600 panel for lemote */
+ { /* mode#4: 1024 x 600 16Bpp 60Hz */
+ 1024, 600, 16, 60,
+ /* Init_MISC */
+ 0xEB,
+ { /* Init_SR0_SR4 */
+ 0x03, 0x01, 0x0F, 0x00, 0x0E,
+ },
+ { /* Init_SR10_SR24 */
+ 0xC8, 0x40, 0x14, 0x60, 0x00, 0x0A, 0x17, 0x20,
+ 0x51, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xC4, 0x30, 0x02, 0x00, 0x01,
+ },
+ { /* Init_SR30_SR75 */
+ 0x22, 0x03, 0x24, 0x09, 0xC0, 0x22, 0x22, 0x22,
+ 0x22, 0x22, 0x22, 0x22, 0x00, 0x00, 0x03, 0xFF,
+ 0x00, 0xFC, 0x00, 0x00, 0x20, 0x18, 0x00, 0xFC,
+ 0x20, 0x0C, 0x44, 0x20, 0x00, 0x22, 0x22, 0x22,
+ 0x06, 0x68, 0xA7, 0x7F, 0x83, 0x24, 0xFF, 0x03,
+ 0x00, 0x60, 0x59, 0x22, 0x22, 0x00, 0x00, 0x22,
+ 0x01, 0x80, 0x7A, 0x1A, 0x1A, 0x00, 0x00, 0x00,
+ 0x50, 0x03, 0x16, 0x02, 0x0D, 0x82, 0x09, 0x02,
+ 0x04, 0x45, 0x3F, 0x30, 0x40, 0x20,
+ },
+ { /* Init_SR80_SR93 */
+ 0xFF, 0x07, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x3A,
+ 0xF7, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x3A, 0x3A,
+ 0x00, 0x00, 0x00, 0x00,
+ },
+ { /* Init_SRA0_SRAF */
+ 0x00, 0xFB, 0x9F, 0x01, 0x00, 0xED, 0xED, 0xED,
+ 0x7B, 0xFB, 0xFF, 0xFF, 0x97, 0xEF, 0xBF, 0xDF,
+ },
+ { /* Init_GR00_GR08 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x05, 0x0F,
+ 0xFF,
+ },
+ { /* Init_AR00_AR14 */
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
+ 0x41, 0x00, 0x0F, 0x00, 0x00,
+ },
+ { /* Init_CR00_CR18 */
+ 0xA3, 0x7F, 0x7F, 0x00, 0x85, 0x16, 0x24, 0xF5,
+ 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x03, 0x09, 0xFF, 0x80, 0x40, 0xFF, 0x00, 0xE3,
+ 0xFF,
+ },
+ { /* Init_CR30_CR4D */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x02, 0x20,
+ 0x00, 0x00, 0x00, 0x40, 0x00, 0xFF, 0xBF, 0xFF,
+ 0xA3, 0x7F, 0x00, 0x82, 0x0b, 0x6f, 0x57, 0x00,
+ 0x5c, 0x0f, 0xE0, 0xe0, 0x7F, 0x57,
+ },
+ { /* Init_CR90_CRA7 */
+ 0x55, 0xD9, 0x5D, 0xE1, 0x86, 0x1B, 0x8E, 0x26,
+ 0xDA, 0x8D, 0xDE, 0x94, 0x00, 0x00, 0x18, 0x00,
+ 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x15, 0x03,
+ },
+ },
+ { /* mode#5: 1024 x 768 24Bpp 60Hz */
+ 1024, 768, 24, 60,
+ /* Init_MISC */
+ 0xEB,
+ { /* Init_SR0_SR4 */
+ 0x03, 0x01, 0x0F, 0x03, 0x0E,
+ },
+ { /* Init_SR10_SR24 */
+ 0xF3, 0xB6, 0xC0, 0xDD, 0x00, 0x0E, 0x17, 0x2C,
+ 0x99, 0x02, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xC4, 0x30, 0x02, 0x01, 0x01,
+ },
+ { /* Init_SR30_SR75 */
+ 0x38, 0x03, 0x20, 0x09, 0xC0, 0x3A, 0x3A, 0x3A,
+ 0x3A, 0x3A, 0x3A, 0x3A, 0x00, 0x00, 0x03, 0xFF,
+ 0x00, 0xFC, 0x00, 0x00, 0x20, 0x18, 0x00, 0xFC,
+ 0x20, 0x0C, 0x44, 0x20, 0x00, 0x00, 0x00, 0x3A,
+ 0x06, 0x68, 0xA7, 0x7F, 0x83, 0x24, 0xFF, 0x03,
+ 0x00, 0x60, 0x59, 0x3A, 0x3A, 0x00, 0x00, 0x3A,
+ 0x01, 0x80, 0x7E, 0x1A, 0x1A, 0x00, 0x00, 0x00,
+ 0x50, 0x03, 0x74, 0x14, 0x3B, 0x0D, 0x09, 0x02,
+ 0x04, 0x45, 0x30, 0x30, 0x40, 0x20,
+ },
+ { /* Init_SR80_SR93 */
+ 0xFF, 0x07, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x3A,
+ 0xF7, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x3A, 0x3A,
+ 0x00, 0x00, 0x00, 0x00,
+ },
+ { /* Init_SRA0_SRAF */
+ 0x00, 0xFB, 0x9F, 0x01, 0x00, 0xED, 0xED, 0xED,
+ 0x7B, 0xFB, 0xFF, 0xFF, 0x97, 0xEF, 0xBF, 0xDF,
+ },
+ { /* Init_GR00_GR08 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x05, 0x0F,
+ 0xFF,
+ },
+ { /* Init_AR00_AR14 */
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
+ 0x41, 0x00, 0x0F, 0x00, 0x00,
+ },
+ { /* Init_CR00_CR18 */
+ 0xA3, 0x7F, 0x7F, 0x00, 0x85, 0x16, 0x24, 0xF5,
+ 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x03, 0x09, 0xFF, 0x80, 0x40, 0xFF, 0x00, 0xE3,
+ 0xFF,
+ },
+ { /* Init_CR30_CR4D */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x02, 0x20,
+ 0x00, 0x00, 0x00, 0x40, 0x00, 0xFF, 0xBF, 0xFF,
+ 0xA3, 0x7F, 0x00, 0x86, 0x15, 0x24, 0xFF, 0x00,
+ 0x01, 0x07, 0xE5, 0x20, 0x7F, 0xFF,
+ },
+ { /* Init_CR90_CRA7 */
+ 0x55, 0xD9, 0x5D, 0xE1, 0x86, 0x1B, 0x8E, 0x26,
+ 0xDA, 0x8D, 0xDE, 0x94, 0x00, 0x00, 0x18, 0x00,
+ 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x15, 0x03,
+ },
+ },
+ { /* mode#4: 1024 x 768 32Bpp 60Hz */
+ 1024, 768, 32, 60,
+ /* Init_MISC */
+ 0xEB,
+ { /* Init_SR0_SR4 */
+ 0x03, 0x01, 0x0F, 0x03, 0x0E,
+ },
+ { /* Init_SR10_SR24 */
+ 0xF3, 0xB6, 0xC0, 0xDD, 0x00, 0x0E, 0x17, 0x2C,
+ 0x99, 0x02, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xC4, 0x32, 0x02, 0x01, 0x01,
+ },
+ { /* Init_SR30_SR75 */
+ 0x38, 0x03, 0x20, 0x09, 0xC0, 0x3A, 0x3A, 0x3A,
+ 0x3A, 0x3A, 0x3A, 0x3A, 0x00, 0x00, 0x03, 0xFF,
+ 0x00, 0xFC, 0x00, 0x00, 0x20, 0x18, 0x00, 0xFC,
+ 0x20, 0x0C, 0x44, 0x20, 0x00, 0x00, 0x00, 0x3A,
+ 0x06, 0x68, 0xA7, 0x7F, 0x83, 0x24, 0xFF, 0x03,
+ 0x00, 0x60, 0x59, 0x3A, 0x3A, 0x00, 0x00, 0x3A,
+ 0x01, 0x80, 0x7E, 0x1A, 0x1A, 0x00, 0x00, 0x00,
+ 0x50, 0x03, 0x74, 0x14, 0x3B, 0x0D, 0x09, 0x02,
+ 0x04, 0x45, 0x30, 0x30, 0x40, 0x20,
+ },
+ { /* Init_SR80_SR93 */
+ 0xFF, 0x07, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x3A,
+ 0xF7, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x3A, 0x3A,
+ 0x00, 0x00, 0x00, 0x00,
+ },
+ { /* Init_SRA0_SRAF */
+ 0x00, 0xFB, 0x9F, 0x01, 0x00, 0xED, 0xED, 0xED,
+ 0x7B, 0xFB, 0xFF, 0xFF, 0x97, 0xEF, 0xBF, 0xDF,
+ },
+ { /* Init_GR00_GR08 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x05, 0x0F,
+ 0xFF,
+ },
+ { /* Init_AR00_AR14 */
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
+ 0x41, 0x00, 0x0F, 0x00, 0x00,
+ },
+ { /* Init_CR00_CR18 */
+ 0xA3, 0x7F, 0x7F, 0x00, 0x85, 0x16, 0x24, 0xF5,
+ 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x03, 0x09, 0xFF, 0x80, 0x40, 0xFF, 0x00, 0xE3,
+ 0xFF,
+ },
+ { /* Init_CR30_CR4D */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x02, 0x20,
+ 0x00, 0x00, 0x00, 0x40, 0x00, 0xFF, 0xBF, 0xFF,
+ 0xA3, 0x7F, 0x00, 0x86, 0x15, 0x24, 0xFF, 0x00,
+ 0x01, 0x07, 0xE5, 0x20, 0x7F, 0xFF,
+ },
+ { /* Init_CR90_CRA7 */
+ 0x55, 0xD9, 0x5D, 0xE1, 0x86, 0x1B, 0x8E, 0x26,
+ 0xDA, 0x8D, 0xDE, 0x94, 0x00, 0x00, 0x18, 0x00,
+ 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x15, 0x03,
+ },
+ },
+ { /* mode#6: 320 x 240 16Bpp 60Hz */
+ 320, 240, 16, 60,
+ /* Init_MISC */
+ 0xEB,
+ { /* Init_SR0_SR4 */
+ 0x03, 0x01, 0x0F, 0x03, 0x0E,
+ },
+ { /* Init_SR10_SR24 */
+ 0xF3, 0xB6, 0xC0, 0xDD, 0x00, 0x0E, 0x17, 0x2C,
+ 0x99, 0x02, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xC4, 0x32, 0x02, 0x01, 0x01,
+ },
+ { /* Init_SR30_SR75 */
+ 0x38, 0x03, 0x20, 0x09, 0xC0, 0x3A, 0x3A, 0x3A,
+ 0x3A, 0x3A, 0x3A, 0x3A, 0x00, 0x00, 0x03, 0xFF,
+ 0x00, 0xFC, 0x00, 0x00, 0x20, 0x18, 0x00, 0xFC,
+ 0x20, 0x0C, 0x44, 0x20, 0x00, 0x00, 0x00, 0x3A,
+ 0x06, 0x68, 0xA7, 0x7F, 0x83, 0x24, 0xFF, 0x03,
+ 0x00, 0x60, 0x59, 0x3A, 0x3A, 0x00, 0x00, 0x3A,
+ 0x01, 0x80, 0x7E, 0x1A, 0x1A, 0x00, 0x00, 0x00,
+ 0x50, 0x03, 0x74, 0x14, 0x08, 0x43, 0x08, 0x43,
+ 0x04, 0x45, 0x30, 0x30, 0x40, 0x20,
+ },
+ { /* Init_SR80_SR93 */
+ 0xFF, 0x07, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x3A,
+ 0xF7, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x3A, 0x3A,
+ 0x00, 0x00, 0x00, 0x00,
+ },
+ { /* Init_SRA0_SRAF */
+ 0x00, 0xFB, 0x9F, 0x01, 0x00, 0xED, 0xED, 0xED,
+ 0x7B, 0xFB, 0xFF, 0xFF, 0x97, 0xEF, 0xBF, 0xDF,
+ },
+ { /* Init_GR00_GR08 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x05, 0x0F,
+ 0xFF,
+ },
+ { /* Init_AR00_AR14 */
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
+ 0x41, 0x00, 0x0F, 0x00, 0x00,
+ },
+ { /* Init_CR00_CR18 */
+ 0xA3, 0x7F, 0x7F, 0x00, 0x85, 0x16, 0x24, 0xF5,
+ 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x03, 0x09, 0xFF, 0x80, 0x40, 0xFF, 0x00, 0xE3,
+ 0xFF,
+ },
+ { /* Init_CR30_CR4D */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x02, 0x20,
+ 0x00, 0x00, 0x30, 0x40, 0x00, 0xFF, 0xBF, 0xFF,
+ 0x2E, 0x27, 0x00, 0x2b, 0x0c, 0x0F, 0xEF, 0x00,
+ 0xFe, 0x0f, 0x01, 0xC0, 0x27, 0xEF,
+ },
+ { /* Init_CR90_CRA7 */
+ 0x55, 0xD9, 0x5D, 0xE1, 0x86, 0x1B, 0x8E, 0x26,
+ 0xDA, 0x8D, 0xDE, 0x94, 0x00, 0x00, 0x18, 0x00,
+ 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x15, 0x03,
+ },
+ },
+
+ { /* mode#8: 320 x 240 32Bpp 60Hz */
+ 320, 240, 32, 60,
+ /* Init_MISC */
+ 0xEB,
+ { /* Init_SR0_SR4 */
+ 0x03, 0x01, 0x0F, 0x03, 0x0E,
+ },
+ { /* Init_SR10_SR24 */
+ 0xF3, 0xB6, 0xC0, 0xDD, 0x00, 0x0E, 0x17, 0x2C,
+ 0x99, 0x02, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xC4, 0x32, 0x02, 0x01, 0x01,
+ },
+ { /* Init_SR30_SR75 */
+ 0x38, 0x03, 0x20, 0x09, 0xC0, 0x3A, 0x3A, 0x3A,
+ 0x3A, 0x3A, 0x3A, 0x3A, 0x00, 0x00, 0x03, 0xFF,
+ 0x00, 0xFC, 0x00, 0x00, 0x20, 0x18, 0x00, 0xFC,
+ 0x20, 0x0C, 0x44, 0x20, 0x00, 0x00, 0x00, 0x3A,
+ 0x06, 0x68, 0xA7, 0x7F, 0x83, 0x24, 0xFF, 0x03,
+ 0x00, 0x60, 0x59, 0x3A, 0x3A, 0x00, 0x00, 0x3A,
+ 0x01, 0x80, 0x7E, 0x1A, 0x1A, 0x00, 0x00, 0x00,
+ 0x50, 0x03, 0x74, 0x14, 0x08, 0x43, 0x08, 0x43,
+ 0x04, 0x45, 0x30, 0x30, 0x40, 0x20,
+ },
+ { /* Init_SR80_SR93 */
+ 0xFF, 0x07, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x3A,
+ 0xF7, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x3A, 0x3A,
+ 0x00, 0x00, 0x00, 0x00,
+ },
+ { /* Init_SRA0_SRAF */
+ 0x00, 0xFB, 0x9F, 0x01, 0x00, 0xED, 0xED, 0xED,
+ 0x7B, 0xFB, 0xFF, 0xFF, 0x97, 0xEF, 0xBF, 0xDF,
+ },
+ { /* Init_GR00_GR08 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x05, 0x0F,
+ 0xFF,
+ },
+ { /* Init_AR00_AR14 */
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
+ 0x41, 0x00, 0x0F, 0x00, 0x00,
+ },
+ { /* Init_CR00_CR18 */
+ 0xA3, 0x7F, 0x7F, 0x00, 0x85, 0x16, 0x24, 0xF5,
+ 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x03, 0x09, 0xFF, 0x80, 0x40, 0xFF, 0x00, 0xE3,
+ 0xFF,
+ },
+ { /* Init_CR30_CR4D */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x02, 0x20,
+ 0x00, 0x00, 0x30, 0x40, 0x00, 0xFF, 0xBF, 0xFF,
+ 0x2E, 0x27, 0x00, 0x2b, 0x0c, 0x0F, 0xEF, 0x00,
+ 0xFe, 0x0f, 0x01, 0xC0, 0x27, 0xEF,
+ },
+ { /* Init_CR90_CRA7 */
+ 0x55, 0xD9, 0x5D, 0xE1, 0x86, 0x1B, 0x8E, 0x26,
+ 0xDA, 0x8D, 0xDE, 0x94, 0x00, 0x00, 0x18, 0x00,
+ 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x15, 0x03,
+ },
+ },
+};
+
+#define numvgamodes ARRAY_SIZE(vgamode)
diff --git a/drivers/staging/sm7xxfb/sm7xxfb.c b/drivers/staging/sm7xxfb/sm7xxfb.c
new file mode 100644
index 000000000..77f51a075
--- /dev/null
+++ b/drivers/staging/sm7xxfb/sm7xxfb.c
@@ -0,0 +1,1058 @@
+/*
+ * Silicon Motion SM7XX frame buffer device
+ *
+ * Copyright (C) 2006 Silicon Motion Technology Corp.
+ * Authors: Ge Wang, gewang@siliconmotion.com
+ * Boyod boyod.yang@siliconmotion.com.cn
+ *
+ * Copyright (C) 2009 Lemote, Inc.
+ * Author: Wu Zhangjin, wuzhangjin@gmail.com
+ *
+ * Copyright (C) 2011 Igalia, S.L.
+ * Author: Javier M. Mellid <jmunhoz@igalia.com>
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file COPYING in the main directory of this archive for
+ * more details.
+ *
+ * Framebuffer driver for Silicon Motion SM710, SM712, SM721 and SM722 chips
+ */
+
+#include <linux/io.h>
+#include <linux/fb.h>
+#include <linux/pci.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+#include <linux/module.h>
+#include <linux/console.h>
+#include <linux/screen_info.h>
+
+#ifdef CONFIG_PM
+#include <linux/pm.h>
+#endif
+
+#include "sm7xx.h"
+
+/*
+* Private structure
+*/
+struct smtcfb_info {
+ struct pci_dev *pdev;
+ struct fb_info fb;
+ u16 chip_id;
+ u8 chip_rev_id;
+
+ void __iomem *lfb; /* linear frame buffer */
+ void __iomem *dp_regs; /* drawing processor control regs */
+ void __iomem *vp_regs; /* video processor control regs */
+ void __iomem *cp_regs; /* capture processor control regs */
+ void __iomem *mmio; /* memory map IO port */
+
+ u_int width;
+ u_int height;
+ u_int hz;
+
+ u32 colreg[17];
+};
+
+void __iomem *smtc_regbaseaddress; /* Memory Map IO starting address */
+
+static struct fb_var_screeninfo smtcfb_var = {
+ .xres = 1024,
+ .yres = 600,
+ .xres_virtual = 1024,
+ .yres_virtual = 600,
+ .bits_per_pixel = 16,
+ .red = {16, 8, 0},
+ .green = {8, 8, 0},
+ .blue = {0, 8, 0},
+ .activate = FB_ACTIVATE_NOW,
+ .height = -1,
+ .width = -1,
+ .vmode = FB_VMODE_NONINTERLACED,
+ .nonstd = 0,
+ .accel_flags = FB_ACCELF_TEXT,
+};
+
+static struct fb_fix_screeninfo smtcfb_fix = {
+ .id = "smXXXfb",
+ .type = FB_TYPE_PACKED_PIXELS,
+ .visual = FB_VISUAL_TRUECOLOR,
+ .line_length = 800 * 3,
+ .accel = FB_ACCEL_SMI_LYNX,
+ .type_aux = 0,
+ .xpanstep = 0,
+ .ypanstep = 0,
+ .ywrapstep = 0,
+};
+
+struct vesa_mode {
+ char index[6];
+ u16 lfb_width;
+ u16 lfb_height;
+ u16 lfb_depth;
+};
+
+static struct vesa_mode vesa_mode_table[] = {
+ {"0x301", 640, 480, 8},
+ {"0x303", 800, 600, 8},
+ {"0x305", 1024, 768, 8},
+ {"0x307", 1280, 1024, 8},
+
+ {"0x311", 640, 480, 16},
+ {"0x314", 800, 600, 16},
+ {"0x317", 1024, 768, 16},
+ {"0x31A", 1280, 1024, 16},
+
+ {"0x312", 640, 480, 24},
+ {"0x315", 800, 600, 24},
+ {"0x318", 1024, 768, 24},
+ {"0x31B", 1280, 1024, 24},
+};
+
+static struct screen_info smtc_scr_info;
+
+static char *mode_option;
+
+/* process command line options, get vga parameter */
+static void __init sm7xx_vga_setup(char *options)
+{
+ int i;
+
+ if (!options || !*options)
+ return;
+
+ smtc_scr_info.lfb_width = 0;
+ smtc_scr_info.lfb_height = 0;
+ smtc_scr_info.lfb_depth = 0;
+
+ pr_debug("sm7xx_vga_setup = %s\n", options);
+
+ for (i = 0; i < ARRAY_SIZE(vesa_mode_table); i++) {
+ if (strstr(options, vesa_mode_table[i].index)) {
+ smtc_scr_info.lfb_width = vesa_mode_table[i].lfb_width;
+ smtc_scr_info.lfb_height =
+ vesa_mode_table[i].lfb_height;
+ smtc_scr_info.lfb_depth = vesa_mode_table[i].lfb_depth;
+ return;
+ }
+ }
+}
+
+static void sm712_setpalette(int regno, unsigned red, unsigned green,
+ unsigned blue, struct fb_info *info)
+{
+ /* set bit 5:4 = 01 (write LCD RAM only) */
+ smtc_seqw(0x66, (smtc_seqr(0x66) & 0xC3) | 0x10);
+
+ smtc_mmiowb(regno, dac_reg);
+ smtc_mmiowb(red >> 10, dac_val);
+ smtc_mmiowb(green >> 10, dac_val);
+ smtc_mmiowb(blue >> 10, dac_val);
+}
+
+/* chan_to_field
+ *
+ * convert a colour value into a field position
+ *
+ * from pxafb.c
+ */
+
+static inline unsigned int chan_to_field(unsigned int chan,
+ struct fb_bitfield *bf)
+{
+ chan &= 0xffff;
+ chan >>= 16 - bf->length;
+ return chan << bf->offset;
+}
+
+static int smtc_blank(int blank_mode, struct fb_info *info)
+{
+ /* clear DPMS setting */
+ switch (blank_mode) {
+ case FB_BLANK_UNBLANK:
+ /* Screen On: HSync: On, VSync : On */
+ smtc_seqw(0x01, (smtc_seqr(0x01) & (~0x20)));
+ smtc_seqw(0x6a, 0x16);
+ smtc_seqw(0x6b, 0x02);
+ smtc_seqw(0x21, (smtc_seqr(0x21) & 0x77));
+ smtc_seqw(0x22, (smtc_seqr(0x22) & (~0x30)));
+ smtc_seqw(0x23, (smtc_seqr(0x23) & (~0xc0)));
+ smtc_seqw(0x24, (smtc_seqr(0x24) | 0x01));
+ smtc_seqw(0x31, (smtc_seqr(0x31) | 0x03));
+ break;
+ case FB_BLANK_NORMAL:
+ /* Screen Off: HSync: On, VSync : On Soft blank */
+ smtc_seqw(0x01, (smtc_seqr(0x01) & (~0x20)));
+ smtc_seqw(0x6a, 0x16);
+ smtc_seqw(0x6b, 0x02);
+ smtc_seqw(0x22, (smtc_seqr(0x22) & (~0x30)));
+ smtc_seqw(0x23, (smtc_seqr(0x23) & (~0xc0)));
+ smtc_seqw(0x24, (smtc_seqr(0x24) | 0x01));
+ smtc_seqw(0x31, ((smtc_seqr(0x31) & (~0x07)) | 0x00));
+ break;
+ case FB_BLANK_VSYNC_SUSPEND:
+ /* Screen On: HSync: On, VSync : Off */
+ smtc_seqw(0x01, (smtc_seqr(0x01) | 0x20));
+ smtc_seqw(0x20, (smtc_seqr(0x20) & (~0xB0)));
+ smtc_seqw(0x6a, 0x0c);
+ smtc_seqw(0x6b, 0x02);
+ smtc_seqw(0x21, (smtc_seqr(0x21) | 0x88));
+ smtc_seqw(0x22, ((smtc_seqr(0x22) & (~0x30)) | 0x20));
+ smtc_seqw(0x23, ((smtc_seqr(0x23) & (~0xc0)) | 0x20));
+ smtc_seqw(0x24, (smtc_seqr(0x24) & (~0x01)));
+ smtc_seqw(0x31, ((smtc_seqr(0x31) & (~0x07)) | 0x00));
+ smtc_seqw(0x34, (smtc_seqr(0x34) | 0x80));
+ break;
+ case FB_BLANK_HSYNC_SUSPEND:
+ /* Screen On: HSync: Off, VSync : On */
+ smtc_seqw(0x01, (smtc_seqr(0x01) | 0x20));
+ smtc_seqw(0x20, (smtc_seqr(0x20) & (~0xB0)));
+ smtc_seqw(0x6a, 0x0c);
+ smtc_seqw(0x6b, 0x02);
+ smtc_seqw(0x21, (smtc_seqr(0x21) | 0x88));
+ smtc_seqw(0x22, ((smtc_seqr(0x22) & (~0x30)) | 0x10));
+ smtc_seqw(0x23, ((smtc_seqr(0x23) & (~0xc0)) | 0xD8));
+ smtc_seqw(0x24, (smtc_seqr(0x24) & (~0x01)));
+ smtc_seqw(0x31, ((smtc_seqr(0x31) & (~0x07)) | 0x00));
+ smtc_seqw(0x34, (smtc_seqr(0x34) | 0x80));
+ break;
+ case FB_BLANK_POWERDOWN:
+ /* Screen On: HSync: Off, VSync : Off */
+ smtc_seqw(0x01, (smtc_seqr(0x01) | 0x20));
+ smtc_seqw(0x20, (smtc_seqr(0x20) & (~0xB0)));
+ smtc_seqw(0x6a, 0x0c);
+ smtc_seqw(0x6b, 0x02);
+ smtc_seqw(0x21, (smtc_seqr(0x21) | 0x88));
+ smtc_seqw(0x22, ((smtc_seqr(0x22) & (~0x30)) | 0x30));
+ smtc_seqw(0x23, ((smtc_seqr(0x23) & (~0xc0)) | 0xD8));
+ smtc_seqw(0x24, (smtc_seqr(0x24) & (~0x01)));
+ smtc_seqw(0x31, ((smtc_seqr(0x31) & (~0x07)) | 0x00));
+ smtc_seqw(0x34, (smtc_seqr(0x34) | 0x80));
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int smtc_setcolreg(unsigned regno, unsigned red, unsigned green,
+ unsigned blue, unsigned trans, struct fb_info *info)
+{
+ struct smtcfb_info *sfb;
+ u32 val;
+
+ sfb = info->par;
+
+ if (regno > 255)
+ return 1;
+
+ switch (sfb->fb.fix.visual) {
+ case FB_VISUAL_DIRECTCOLOR:
+ case FB_VISUAL_TRUECOLOR:
+ /*
+ * 16/32 bit true-colour, use pseudo-palette for 16 base color
+ */
+ if (regno < 16) {
+ if (sfb->fb.var.bits_per_pixel == 16) {
+ u32 *pal = sfb->fb.pseudo_palette;
+
+ val = chan_to_field(red, &sfb->fb.var.red);
+ val |= chan_to_field(green, &sfb->fb.var.green);
+ val |= chan_to_field(blue, &sfb->fb.var.blue);
+#ifdef __BIG_ENDIAN
+ pal[regno] =
+ ((red & 0xf800) >> 8) |
+ ((green & 0xe000) >> 13) |
+ ((green & 0x1c00) << 3) |
+ ((blue & 0xf800) >> 3);
+#else
+ pal[regno] = val;
+#endif
+ } else {
+ u32 *pal = sfb->fb.pseudo_palette;
+
+ val = chan_to_field(red, &sfb->fb.var.red);
+ val |= chan_to_field(green, &sfb->fb.var.green);
+ val |= chan_to_field(blue, &sfb->fb.var.blue);
+#ifdef __BIG_ENDIAN
+ val =
+ (val & 0xff00ff00 >> 8) |
+ (val & 0x00ff00ff << 8);
+#endif
+ pal[regno] = val;
+ }
+ }
+ break;
+
+ case FB_VISUAL_PSEUDOCOLOR:
+ /* color depth 8 bit */
+ sm712_setpalette(regno, red, green, blue, info);
+ break;
+
+ default:
+ return 1; /* unknown type */
+ }
+
+ return 0;
+}
+
+#ifdef __BIG_ENDIAN
+static ssize_t smtcfb_read(struct fb_info *info, char __user *buf, size_t
+ count, loff_t *ppos)
+{
+ unsigned long p = *ppos;
+
+ u32 *buffer, *dst;
+ u32 __iomem *src;
+ int c, i, cnt = 0, err = 0;
+ unsigned long total_size;
+
+ if (!info || !info->screen_base)
+ return -ENODEV;
+
+ if (info->state != FBINFO_STATE_RUNNING)
+ return -EPERM;
+
+ total_size = info->screen_size;
+
+ if (total_size == 0)
+ total_size = info->fix.smem_len;
+
+ if (p >= total_size)
+ return 0;
+
+ if (count >= total_size)
+ count = total_size;
+
+ if (count + p > total_size)
+ count = total_size - p;
+
+ buffer = kmalloc((count > PAGE_SIZE) ? PAGE_SIZE : count, GFP_KERNEL);
+ if (!buffer)
+ return -ENOMEM;
+
+ src = (u32 __iomem *) (info->screen_base + p);
+
+ if (info->fbops->fb_sync)
+ info->fbops->fb_sync(info);
+
+ while (count) {
+ c = (count > PAGE_SIZE) ? PAGE_SIZE : count;
+ dst = buffer;
+ for (i = c >> 2; i--;) {
+ *dst = fb_readl(src++);
+ *dst =
+ (*dst & 0xff00ff00 >> 8) |
+ (*dst & 0x00ff00ff << 8);
+ dst++;
+ }
+ if (c & 3) {
+ u8 *dst8 = (u8 *)dst;
+ u8 __iomem *src8 = (u8 __iomem *)src;
+
+ for (i = c & 3; i--;) {
+ if (i & 1) {
+ *dst8++ = fb_readb(++src8);
+ } else {
+ *dst8++ = fb_readb(--src8);
+ src8 += 2;
+ }
+ }
+ src = (u32 __iomem *)src8;
+ }
+
+ if (copy_to_user(buf, buffer, c)) {
+ err = -EFAULT;
+ break;
+ }
+ *ppos += c;
+ buf += c;
+ cnt += c;
+ count -= c;
+ }
+
+ kfree(buffer);
+
+ return (err) ? err : cnt;
+}
+
+static ssize_t
+smtcfb_write(struct fb_info *info, const char __user *buf, size_t count,
+ loff_t *ppos)
+{
+ unsigned long p = *ppos;
+
+ u32 *buffer, *src;
+ u32 __iomem *dst;
+ int c, i, cnt = 0, err = 0;
+ unsigned long total_size;
+
+ if (!info || !info->screen_base)
+ return -ENODEV;
+
+ if (info->state != FBINFO_STATE_RUNNING)
+ return -EPERM;
+
+ total_size = info->screen_size;
+
+ if (total_size == 0)
+ total_size = info->fix.smem_len;
+
+ if (p > total_size)
+ return -EFBIG;
+
+ if (count > total_size) {
+ err = -EFBIG;
+ count = total_size;
+ }
+
+ if (count + p > total_size) {
+ if (!err)
+ err = -ENOSPC;
+
+ count = total_size - p;
+ }
+
+ buffer = kmalloc((count > PAGE_SIZE) ? PAGE_SIZE : count, GFP_KERNEL);
+ if (!buffer)
+ return -ENOMEM;
+
+ dst = (u32 __iomem *) (info->screen_base + p);
+
+ if (info->fbops->fb_sync)
+ info->fbops->fb_sync(info);
+
+ while (count) {
+ c = (count > PAGE_SIZE) ? PAGE_SIZE : count;
+ src = buffer;
+
+ if (copy_from_user(src, buf, c)) {
+ err = -EFAULT;
+ break;
+ }
+
+ for (i = c >> 2; i--;) {
+ fb_writel((*src & 0xff00ff00 >> 8) |
+ (*src & 0x00ff00ff << 8), dst++);
+ src++;
+ }
+ if (c & 3) {
+ u8 *src8 = (u8 *)src;
+ u8 __iomem *dst8 = (u8 __iomem *)dst;
+
+ for (i = c & 3; i--;) {
+ if (i & 1) {
+ fb_writeb(*src8++, ++dst8);
+ } else {
+ fb_writeb(*src8++, --dst8);
+ dst8 += 2;
+ }
+ }
+ dst = (u32 __iomem *)dst8;
+ }
+
+ *ppos += c;
+ buf += c;
+ cnt += c;
+ count -= c;
+ }
+
+ kfree(buffer);
+
+ return (cnt) ? cnt : err;
+}
+#endif /* ! __BIG_ENDIAN */
+
+static void sm7xx_set_timing(struct smtcfb_info *sfb)
+{
+ int i = 0, j = 0;
+ u32 m_nscreenstride;
+
+ dev_dbg(&sfb->pdev->dev,
+ "sfb->width=%d sfb->height=%d sfb->fb.var.bits_per_pixel=%d sfb->hz=%d\n",
+ sfb->width, sfb->height, sfb->fb.var.bits_per_pixel, sfb->hz);
+
+ for (j = 0; j < numvgamodes; j++) {
+ if (vgamode[j].mmsizex == sfb->width &&
+ vgamode[j].mmsizey == sfb->height &&
+ vgamode[j].bpp == sfb->fb.var.bits_per_pixel &&
+ vgamode[j].hz == sfb->hz) {
+ dev_dbg(&sfb->pdev->dev,
+ "vgamode[j].mmsizex=%d vgamode[j].mmSizeY=%d vgamode[j].bpp=%d vgamode[j].hz=%d\n",
+ vgamode[j].mmsizex, vgamode[j].mmsizey,
+ vgamode[j].bpp, vgamode[j].hz);
+
+ dev_dbg(&sfb->pdev->dev, "vgamode index=%d\n", j);
+
+ smtc_mmiowb(0x0, 0x3c6);
+
+ smtc_seqw(0, 0x1);
+
+ smtc_mmiowb(vgamode[j].init_misc, 0x3c2);
+
+ /* init SEQ register SR00 - SR04 */
+ for (i = 0; i < SIZE_SR00_SR04; i++)
+ smtc_seqw(i, vgamode[j].init_sr00_sr04[i]);
+
+ /* init SEQ register SR10 - SR24 */
+ for (i = 0; i < SIZE_SR10_SR24; i++)
+ smtc_seqw(i + 0x10,
+ vgamode[j].init_sr10_sr24[i]);
+
+ /* init SEQ register SR30 - SR75 */
+ for (i = 0; i < SIZE_SR30_SR75; i++)
+ if ((i + 0x30) != 0x62 &&
+ (i + 0x30) != 0x6a &&
+ (i + 0x30) != 0x6b)
+ smtc_seqw(i + 0x30,
+ vgamode[j].init_sr30_sr75[i]);
+
+ /* init SEQ register SR80 - SR93 */
+ for (i = 0; i < SIZE_SR80_SR93; i++)
+ smtc_seqw(i + 0x80,
+ vgamode[j].init_sr80_sr93[i]);
+
+ /* init SEQ register SRA0 - SRAF */
+ for (i = 0; i < SIZE_SRA0_SRAF; i++)
+ smtc_seqw(i + 0xa0,
+ vgamode[j].init_sra0_sraf[i]);
+
+ /* init Graphic register GR00 - GR08 */
+ for (i = 0; i < SIZE_GR00_GR08; i++)
+ smtc_grphw(i, vgamode[j].init_gr00_gr08[i]);
+
+ /* init Attribute register AR00 - AR14 */
+ for (i = 0; i < SIZE_AR00_AR14; i++)
+ smtc_attrw(i, vgamode[j].init_ar00_ar14[i]);
+
+ /* init CRTC register CR00 - CR18 */
+ for (i = 0; i < SIZE_CR00_CR18; i++)
+ smtc_crtcw(i, vgamode[j].init_cr00_cr18[i]);
+
+ /* init CRTC register CR30 - CR4D */
+ for (i = 0; i < SIZE_CR30_CR4D; i++)
+ smtc_crtcw(i + 0x30,
+ vgamode[j].init_cr30_cr4d[i]);
+
+ /* init CRTC register CR90 - CRA7 */
+ for (i = 0; i < SIZE_CR90_CRA7; i++)
+ smtc_crtcw(i + 0x90,
+ vgamode[j].init_cr90_cra7[i]);
+ }
+ }
+ smtc_mmiowb(0x67, 0x3c2);
+
+ /* set VPR registers */
+ writel(0x0, sfb->vp_regs + 0x0C);
+ writel(0x0, sfb->vp_regs + 0x40);
+
+ /* set data width */
+ m_nscreenstride =
+ (sfb->width * sfb->fb.var.bits_per_pixel) / 64;
+ switch (sfb->fb.var.bits_per_pixel) {
+ case 8:
+ writel(0x0, sfb->vp_regs + 0x0);
+ break;
+ case 16:
+ writel(0x00020000, sfb->vp_regs + 0x0);
+ break;
+ case 24:
+ writel(0x00040000, sfb->vp_regs + 0x0);
+ break;
+ case 32:
+ writel(0x00030000, sfb->vp_regs + 0x0);
+ break;
+ }
+ writel((u32) (((m_nscreenstride + 2) << 16) | m_nscreenstride),
+ sfb->vp_regs + 0x10);
+}
+
+static void smtc_set_timing(struct smtcfb_info *sfb)
+{
+ switch (sfb->chip_id) {
+ case 0x710:
+ case 0x712:
+ case 0x720:
+ sm7xx_set_timing(sfb);
+ break;
+ }
+}
+
+static void smtcfb_setmode(struct smtcfb_info *sfb)
+{
+ switch (sfb->fb.var.bits_per_pixel) {
+ case 32:
+ sfb->fb.fix.visual = FB_VISUAL_TRUECOLOR;
+ sfb->fb.fix.line_length = sfb->fb.var.xres * 4;
+ sfb->fb.var.red.length = 8;
+ sfb->fb.var.green.length = 8;
+ sfb->fb.var.blue.length = 8;
+ sfb->fb.var.red.offset = 16;
+ sfb->fb.var.green.offset = 8;
+ sfb->fb.var.blue.offset = 0;
+ break;
+ case 24:
+ sfb->fb.fix.visual = FB_VISUAL_TRUECOLOR;
+ sfb->fb.fix.line_length = sfb->fb.var.xres * 3;
+ sfb->fb.var.red.length = 8;
+ sfb->fb.var.green.length = 8;
+ sfb->fb.var.blue.length = 8;
+ sfb->fb.var.red.offset = 16;
+ sfb->fb.var.green.offset = 8;
+ sfb->fb.var.blue.offset = 0;
+ break;
+ case 8:
+ sfb->fb.fix.visual = FB_VISUAL_PSEUDOCOLOR;
+ sfb->fb.fix.line_length = sfb->fb.var.xres;
+ sfb->fb.var.red.length = 3;
+ sfb->fb.var.green.length = 3;
+ sfb->fb.var.blue.length = 2;
+ sfb->fb.var.red.offset = 5;
+ sfb->fb.var.green.offset = 2;
+ sfb->fb.var.blue.offset = 0;
+ break;
+ case 16:
+ default:
+ sfb->fb.fix.visual = FB_VISUAL_TRUECOLOR;
+ sfb->fb.fix.line_length = sfb->fb.var.xres * 2;
+ sfb->fb.var.red.length = 5;
+ sfb->fb.var.green.length = 6;
+ sfb->fb.var.blue.length = 5;
+ sfb->fb.var.red.offset = 11;
+ sfb->fb.var.green.offset = 5;
+ sfb->fb.var.blue.offset = 0;
+ break;
+ }
+
+ sfb->width = sfb->fb.var.xres;
+ sfb->height = sfb->fb.var.yres;
+ sfb->hz = 60;
+ smtc_set_timing(sfb);
+}
+
+static int smtc_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
+{
+ /* sanity checks */
+ if (var->xres_virtual < var->xres)
+ var->xres_virtual = var->xres;
+
+ if (var->yres_virtual < var->yres)
+ var->yres_virtual = var->yres;
+
+ /* set valid default bpp */
+ if ((var->bits_per_pixel != 8) && (var->bits_per_pixel != 16) &&
+ (var->bits_per_pixel != 24) && (var->bits_per_pixel != 32))
+ var->bits_per_pixel = 16;
+
+ return 0;
+}
+
+static int smtc_set_par(struct fb_info *info)
+{
+ smtcfb_setmode(info->par);
+
+ return 0;
+}
+
+static struct fb_ops smtcfb_ops = {
+ .owner = THIS_MODULE,
+ .fb_check_var = smtc_check_var,
+ .fb_set_par = smtc_set_par,
+ .fb_setcolreg = smtc_setcolreg,
+ .fb_blank = smtc_blank,
+ .fb_fillrect = cfb_fillrect,
+ .fb_imageblit = cfb_imageblit,
+ .fb_copyarea = cfb_copyarea,
+#ifdef __BIG_ENDIAN
+ .fb_read = smtcfb_read,
+ .fb_write = smtcfb_write,
+#endif
+};
+
+/*
+ * alloc struct smtcfb_info and assign default values
+ */
+static struct smtcfb_info *smtc_alloc_fb_info(struct pci_dev *pdev)
+{
+ struct smtcfb_info *sfb;
+
+ sfb = kzalloc(sizeof(*sfb), GFP_KERNEL);
+
+ if (!sfb)
+ return NULL;
+
+ sfb->pdev = pdev;
+
+ sfb->fb.flags = FBINFO_FLAG_DEFAULT;
+ sfb->fb.fbops = &smtcfb_ops;
+ sfb->fb.fix = smtcfb_fix;
+ sfb->fb.var = smtcfb_var;
+ sfb->fb.pseudo_palette = sfb->colreg;
+ sfb->fb.par = sfb;
+
+ return sfb;
+}
+
+/*
+ * free struct smtcfb_info
+ */
+static void smtc_free_fb_info(struct smtcfb_info *sfb)
+{
+ kfree(sfb);
+}
+
+/*
+ * Unmap in the memory mapped IO registers
+ */
+
+static void smtc_unmap_mmio(struct smtcfb_info *sfb)
+{
+ if (sfb && smtc_regbaseaddress)
+ smtc_regbaseaddress = NULL;
+}
+
+/*
+ * Map in the screen memory
+ */
+
+static int smtc_map_smem(struct smtcfb_info *sfb,
+ struct pci_dev *pdev, u_long smem_len)
+{
+ sfb->fb.fix.smem_start = pci_resource_start(pdev, 0);
+
+#ifdef __BIG_ENDIAN
+ if (sfb->fb.var.bits_per_pixel == 32)
+ sfb->fb.fix.smem_start += 0x800000;
+#endif
+
+ sfb->fb.fix.smem_len = smem_len;
+
+ sfb->fb.screen_base = sfb->lfb;
+
+ if (!sfb->fb.screen_base) {
+ dev_err(&pdev->dev,
+ "%s: unable to map screen memory\n", sfb->fb.fix.id);
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+/*
+ * Unmap in the screen memory
+ *
+ */
+static void smtc_unmap_smem(struct smtcfb_info *sfb)
+{
+ if (sfb && sfb->fb.screen_base) {
+ iounmap(sfb->fb.screen_base);
+ sfb->fb.screen_base = NULL;
+ }
+}
+
+/*
+ * We need to wake up the device and make sure its in linear memory mode.
+ */
+static inline void sm7xx_init_hw(void)
+{
+ outb_p(0x18, 0x3c4);
+ outb_p(0x11, 0x3c5);
+}
+
+static int smtcfb_pci_probe(struct pci_dev *pdev,
+ const struct pci_device_id *ent)
+{
+ struct smtcfb_info *sfb;
+ u_long smem_size = 0x00800000; /* default 8MB */
+ int err;
+ unsigned long mmio_base;
+
+ dev_info(&pdev->dev, "Silicon Motion display driver.");
+
+ err = pci_enable_device(pdev); /* enable SMTC chip */
+ if (err)
+ return err;
+
+ err = pci_request_region(pdev, 0, "sm7xxfb");
+ if (err < 0) {
+ dev_err(&pdev->dev, "cannot reserve framebuffer region\n");
+ goto failed_regions;
+ }
+
+ sprintf(smtcfb_fix.id, "sm%Xfb", ent->device);
+
+ sfb = smtc_alloc_fb_info(pdev);
+
+ if (!sfb) {
+ err = -ENOMEM;
+ goto failed_free;
+ }
+
+ sfb->chip_id = ent->device;
+
+ pci_set_drvdata(pdev, sfb);
+
+ sm7xx_init_hw();
+
+ /* get mode parameter from smtc_scr_info */
+ if (smtc_scr_info.lfb_width != 0) {
+ sfb->fb.var.xres = smtc_scr_info.lfb_width;
+ sfb->fb.var.yres = smtc_scr_info.lfb_height;
+ sfb->fb.var.bits_per_pixel = smtc_scr_info.lfb_depth;
+ } else {
+ /* default resolution 1024x600 16bit mode */
+ sfb->fb.var.xres = SCREEN_X_RES;
+ sfb->fb.var.yres = SCREEN_Y_RES;
+ sfb->fb.var.bits_per_pixel = SCREEN_BPP;
+ }
+
+#ifdef __BIG_ENDIAN
+ if (sfb->fb.var.bits_per_pixel == 24)
+ sfb->fb.var.bits_per_pixel = (smtc_scr_info.lfb_depth = 32);
+#endif
+ /* Map address and memory detection */
+ mmio_base = pci_resource_start(pdev, 0);
+ pci_read_config_byte(pdev, PCI_REVISION_ID, &sfb->chip_rev_id);
+
+ switch (sfb->chip_id) {
+ case 0x710:
+ case 0x712:
+ sfb->fb.fix.mmio_start = mmio_base + 0x00400000;
+ sfb->fb.fix.mmio_len = 0x00400000;
+ smem_size = SM712_VIDEOMEMORYSIZE;
+#ifdef __BIG_ENDIAN
+ sfb->lfb = ioremap(mmio_base, 0x00c00000);
+#else
+ sfb->lfb = ioremap(mmio_base, 0x00800000);
+#endif
+ sfb->mmio = (smtc_regbaseaddress =
+ sfb->lfb + 0x00700000);
+ sfb->dp_regs = sfb->lfb + 0x00408000;
+ sfb->vp_regs = sfb->lfb + 0x0040c000;
+#ifdef __BIG_ENDIAN
+ if (sfb->fb.var.bits_per_pixel == 32) {
+ sfb->lfb += 0x800000;
+ dev_info(&pdev->dev, "sfb->lfb=%p", sfb->lfb);
+ }
+#endif
+ if (!smtc_regbaseaddress) {
+ dev_err(&pdev->dev,
+ "%s: unable to map memory mapped IO!",
+ sfb->fb.fix.id);
+ err = -ENOMEM;
+ goto failed_fb;
+ }
+
+ /* set MCLK = 14.31818 * (0x16 / 0x2) */
+ smtc_seqw(0x6a, 0x16);
+ smtc_seqw(0x6b, 0x02);
+ smtc_seqw(0x62, 0x3e);
+ /* enable PCI burst */
+ smtc_seqw(0x17, 0x20);
+ /* enable word swap */
+#ifdef __BIG_ENDIAN
+ if (sfb->fb.var.bits_per_pixel == 32)
+ smtc_seqw(0x17, 0x30);
+#endif
+ break;
+ case 0x720:
+ sfb->fb.fix.mmio_start = mmio_base;
+ sfb->fb.fix.mmio_len = 0x00200000;
+ smem_size = SM722_VIDEOMEMORYSIZE;
+ sfb->dp_regs = ioremap(mmio_base, 0x00a00000);
+ sfb->lfb = sfb->dp_regs + 0x00200000;
+ sfb->mmio = (smtc_regbaseaddress =
+ sfb->dp_regs + 0x000c0000);
+ sfb->vp_regs = sfb->dp_regs + 0x800;
+
+ smtc_seqw(0x62, 0xff);
+ smtc_seqw(0x6a, 0x0d);
+ smtc_seqw(0x6b, 0x02);
+ break;
+ default:
+ dev_err(&pdev->dev,
+ "No valid Silicon Motion display chip was detected!");
+
+ goto failed_fb;
+ }
+
+ /* can support 32 bpp */
+ if (15 == sfb->fb.var.bits_per_pixel)
+ sfb->fb.var.bits_per_pixel = 16;
+
+ sfb->fb.var.xres_virtual = sfb->fb.var.xres;
+ sfb->fb.var.yres_virtual = sfb->fb.var.yres;
+ err = smtc_map_smem(sfb, pdev, smem_size);
+ if (err)
+ goto failed;
+
+ smtcfb_setmode(sfb);
+
+ err = register_framebuffer(&sfb->fb);
+ if (err < 0)
+ goto failed;
+
+ dev_info(&pdev->dev,
+ "Silicon Motion SM%X Rev%X primary display mode %dx%d-%d Init Complete.",
+ sfb->chip_id, sfb->chip_rev_id, sfb->fb.var.xres,
+ sfb->fb.var.yres, sfb->fb.var.bits_per_pixel);
+
+ return 0;
+
+failed:
+ dev_err(&pdev->dev, "Silicon Motion, Inc. primary display init fail.");
+
+ smtc_unmap_smem(sfb);
+ smtc_unmap_mmio(sfb);
+failed_fb:
+ smtc_free_fb_info(sfb);
+
+failed_free:
+ pci_release_region(pdev, 0);
+
+failed_regions:
+ pci_disable_device(pdev);
+
+ return err;
+}
+
+/*
+ * 0x710 (LynxEM)
+ * 0x712 (LynxEM+)
+ * 0x720 (Lynx3DM, Lynx3DM+)
+ */
+static const struct pci_device_id smtcfb_pci_table[] = {
+ { PCI_DEVICE(0x126f, 0x710), },
+ { PCI_DEVICE(0x126f, 0x712), },
+ { PCI_DEVICE(0x126f, 0x720), },
+ {0,}
+};
+
+MODULE_DEVICE_TABLE(pci, smtcfb_pci_table);
+
+static void smtcfb_pci_remove(struct pci_dev *pdev)
+{
+ struct smtcfb_info *sfb;
+
+ sfb = pci_get_drvdata(pdev);
+ smtc_unmap_smem(sfb);
+ smtc_unmap_mmio(sfb);
+ unregister_framebuffer(&sfb->fb);
+ smtc_free_fb_info(sfb);
+ pci_release_region(pdev, 0);
+ pci_disable_device(pdev);
+}
+
+#ifdef CONFIG_PM
+static int smtcfb_pci_suspend(struct device *device)
+{
+ struct pci_dev *pdev = to_pci_dev(device);
+ struct smtcfb_info *sfb;
+
+ sfb = pci_get_drvdata(pdev);
+
+ /* set the hw in sleep mode use external clock and self memory refresh
+ * so that we can turn off internal PLLs later on
+ */
+ smtc_seqw(0x20, (smtc_seqr(0x20) | 0xc0));
+ smtc_seqw(0x69, (smtc_seqr(0x69) & 0xf7));
+
+ console_lock();
+ fb_set_suspend(&sfb->fb, 1);
+ console_unlock();
+
+ /* additionally turn off all function blocks including internal PLLs */
+ smtc_seqw(0x21, 0xff);
+
+ return 0;
+}
+
+static int smtcfb_pci_resume(struct device *device)
+{
+ struct pci_dev *pdev = to_pci_dev(device);
+ struct smtcfb_info *sfb;
+
+ sfb = pci_get_drvdata(pdev);
+
+ /* reinit hardware */
+ sm7xx_init_hw();
+ switch (sfb->chip_id) {
+ case 0x710:
+ case 0x712:
+ /* set MCLK = 14.31818 * (0x16 / 0x2) */
+ smtc_seqw(0x6a, 0x16);
+ smtc_seqw(0x6b, 0x02);
+ smtc_seqw(0x62, 0x3e);
+ /* enable PCI burst */
+ smtc_seqw(0x17, 0x20);
+#ifdef __BIG_ENDIAN
+ if (sfb->fb.var.bits_per_pixel == 32)
+ smtc_seqw(0x17, 0x30);
+#endif
+ break;
+ case 0x720:
+ smtc_seqw(0x62, 0xff);
+ smtc_seqw(0x6a, 0x0d);
+ smtc_seqw(0x6b, 0x02);
+ break;
+ }
+
+ smtc_seqw(0x34, (smtc_seqr(0x34) | 0xc0));
+ smtc_seqw(0x33, ((smtc_seqr(0x33) | 0x08) & 0xfb));
+
+ smtcfb_setmode(sfb);
+
+ console_lock();
+ fb_set_suspend(&sfb->fb, 0);
+ console_unlock();
+
+ return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(sm7xx_pm_ops, smtcfb_pci_suspend, smtcfb_pci_resume);
+#define SM7XX_PM_OPS (&sm7xx_pm_ops)
+
+#else /* !CONFIG_PM */
+
+#define SM7XX_PM_OPS NULL
+
+#endif /* !CONFIG_PM */
+
+static struct pci_driver smtcfb_driver = {
+ .name = "smtcfb",
+ .id_table = smtcfb_pci_table,
+ .probe = smtcfb_pci_probe,
+ .remove = smtcfb_pci_remove,
+ .driver.pm = SM7XX_PM_OPS,
+};
+
+static int __init sm712fb_init(void)
+{
+#ifndef MODULE
+ char *option = NULL;
+
+ if (fb_get_options("sm712fb", &option))
+ return -ENODEV;
+ if (option && *option)
+ mode_option = option;
+#endif
+ sm7xx_vga_setup(mode_option);
+
+ return pci_register_driver(&smtcfb_driver);
+}
+
+module_init(sm712fb_init);
+
+static void __exit sm712fb_exit(void)
+{
+ pci_unregister_driver(&smtcfb_driver);
+}
+
+module_exit(sm712fb_exit);
+
+MODULE_AUTHOR("Siliconmotion ");
+MODULE_DESCRIPTION("Framebuffer driver for SMI Graphic Cards");
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/speakup/DefaultKeyAssignments b/drivers/staging/speakup/DefaultKeyAssignments
new file mode 100644
index 000000000..101c803b2
--- /dev/null
+++ b/drivers/staging/speakup/DefaultKeyAssignments
@@ -0,0 +1,46 @@
+This file is intended to give you an overview of the default keys used
+by speakup for it's review functions. You may change them to be
+anything you want but that will take some familiarity with key
+mapping.
+
+We have remapped the insert or zero key on the keypad to act as a
+shift key. Well, actually as an altgr key. So in the following list
+InsKeyPad-period means hold down the insert key like a shift key and
+hit the keypad period.
+
+KeyPad-8 Say current Line
+InsKeyPad-8 say from top of screen to reading cursor.
+KeyPad-7 Say Previous Line (UP one line)
+KeyPad-9 Say Next Line (down one line)
+KeyPad-5 Say Current Word
+InsKeyPad-5 Spell Current Word
+KeyPad-4 Say Previous Word (left one word)
+InsKeyPad-4 say from left edge of line to reading cursor.
+KeyPad-6 Say Next Word (right one word)
+InsKeyPad-6 Say from reading cursor to right edge of line.
+KeyPad-2 Say Current Letter
+InsKeyPad-2 say current letter phonetically
+KeyPad-1 Say Previous Character (left one letter)
+KeyPad-3 Say Next Character (right one letter)
+KeyPad-plus Say Entire Screen
+InsKeyPad-plus Say from reading cursor line to bottom of screen.
+KeyPad-Minus Park reading cursor (toggle)
+InsKeyPad-minus Say character hex and decimal value.
+KeyPad-period Say Position (current line, position and console)
+InsKeyPad-period say colour attributes of current position.
+InsKeyPad-9 Move reading cursor to top of screen (insert pgup)
+InsKeyPad-3 Move reading cursor to bottom of screen (insert pgdn)
+InsKeyPad-7 Move reading cursor to left edge of screen (insert home)
+InsKeyPad-1 Move reading cursor to right edge of screen (insert end)
+ControlKeyPad-1 Move reading cursor to last character on current line.
+KeyPad-Enter Shut Up (until another key is hit) and sync reading cursor
+InsKeyPad-Enter Shut Up (until toggled back on).
+InsKeyPad-star n<x|y> go to line (y) or column (x). Where 'n' is any
+ allowed value for the row or column for your current screen.
+KeyPad-/ Mark and Cut screen region.
+InsKeyPad-/ Paste screen region into any console.
+
+Hitting any key while speakup is outputting speech will quiet the
+synth until it has caught up with what is being printed on the
+console.
+
diff --git a/drivers/staging/speakup/Kconfig b/drivers/staging/speakup/Kconfig
new file mode 100644
index 000000000..efd6f4560
--- /dev/null
+++ b/drivers/staging/speakup/Kconfig
@@ -0,0 +1,199 @@
+menu "Speakup console speech"
+
+config SPEAKUP
+ depends on VT
+ tristate "Speakup core"
+ ---help---
+ This is the Speakup screen reader. Think of it as a
+ video console for blind people. If built in to the
+ kernel, it can speak everything on the text console from
+ boot up to shutdown. For more information on Speakup,
+ point your browser at <http://www.linux-speakup.org/>.
+ There is also a mailing list at the above url that you
+ can subscribe to.
+
+ Supported synthesizers are accent sa, accent pc,
+ appollo II., Auddapter, Braille 'n Speak, Dectalk
+ external (old), Dectalk PC (full length isa board),
+ Dectalk express, Doubletalk, Doubletalk LT or
+ Litetalk, Keynote Gold internal PC, software
+ synthesizers, Speakout, transport, and a dummy module
+ that can be used with a plain text terminal.
+
+ Speakup can either be built in or compiled as a module
+ by answering y or m. If you answer y here, then you
+ must answer either y or m to at least one of the
+ synthesizer drivers below. If you answer m here, then
+ the synthesizer drivers below can only be built as
+ modules.
+
+ These drivers are not standalone drivers, but must be
+ used in conjunction with Speakup. Think of them as
+ video cards for blind people.
+
+
+ The Dectalk pc driver can only be built as a module, and
+ requires software to be pre-loaded on to the card before
+ the module can be loaded. See the decpc choice below
+ for more details.
+
+ If you are not a blind person, or don't have access to
+ one of the listed synthesizers, you should say n.
+
+if SPEAKUP
+config SPEAKUP_SYNTH_ACNTSA
+ tristate "Accent SA synthesizer support"
+ ---help---
+ This is the Speakup driver for the Accent SA
+ synthesizer. You can say y to build it into the kernel,
+ or m to build it as a module. See the configuration
+ help on the Speakup choice above for more info.
+
+config SPEAKUP_SYNTH_ACNTPC
+ tristate "Accent PC synthesizer support"
+ depends on ISA || COMPILE_TEST
+ ---help---
+ This is the Speakup driver for the accent pc
+ synthesizer. You can say y to build it into the kernel,
+ or m to build it as a module. See the configuration
+ help on the Speakup choice above for more info.
+
+config SPEAKUP_SYNTH_APOLLO
+ tristate "Apollo II synthesizer support"
+ ---help---
+ This is the Speakup driver for the Apollo II
+ synthesizer. You can say y to build it into the kernel,
+ or m to build it as a module. See the configuration
+ help on the Speakup choice above for more info.
+
+config SPEAKUP_SYNTH_AUDPTR
+ tristate "Audapter synthesizer support"
+ ---help---
+ This is the Speakup driver for the Audapter synthesizer.
+ You can say y to build it into the kernel, or m to
+ build it as a module. See the configuration help on the
+ Speakup choice above for more info.
+
+config SPEAKUP_SYNTH_BNS
+ tristate "Braille 'n' Speak synthesizer support"
+ ---help---
+ This is the Speakup driver for the Braille 'n' Speak
+ synthesizer. You can say y to build it into the kernel,
+ or m to build it as a module. See the configuration
+ help on the Speakup choice above for more info.
+
+config SPEAKUP_SYNTH_DECTLK
+ tristate "DECtalk Express synthesizer support"
+ ---help---
+
+ This is the Speakup driver for the DecTalk Express
+ synthesizer. You can say y to build it into the kernel,
+ or m to build it as a module. See the configuration
+ help on the Speakup choice above for more info.
+
+config SPEAKUP_SYNTH_DECEXT
+ tristate "DECtalk External (old) synthesizer support"
+ ---help---
+
+ This is the Speakup driver for the DecTalk External
+ (old) synthesizer. You can say y to build it into the
+ kernel, or m to build it as a module. See the
+ configuration help on the Speakup choice above for more
+ info.
+
+config SPEAKUP_SYNTH_DECPC
+ depends on m
+ depends on ISA || COMPILE_TEST
+ tristate "DECtalk PC (big ISA card) synthesizer support"
+ ---help---
+
+ This is the Speakup driver for the DecTalk PC (full
+ length ISA) synthesizer. You can say m to build it as
+ a module. See the configuration help on the Speakup
+ choice above for more info.
+
+ In order to use the DecTalk PC driver, you must download
+ the dec_pc.tgz file from linux-speakup.org. It is in
+ the pub/linux/goodies directory. The dec_pc.tgz file
+ contains the software which must be pre-loaded on to the
+ DecTalk PC board in order to use it with this driver.
+ This driver must be built as a module, and can not be
+ loaded until the file system is mounted and the DecTalk
+ PC software has been pre-loaded on to the board.
+
+ See the README file in the dec_pc.tgz file for more
+ details.
+
+config SPEAKUP_SYNTH_DTLK
+ tristate "DoubleTalk PC synthesizer support"
+ depends on ISA || COMPILE_TEST
+ ---help---
+
+ This is the Speakup driver for the internal DoubleTalk
+ PC synthesizer. You can say y to build it into the
+ kernel, or m to build it as a module. See the
+ configuration help on the Speakup choice above for more
+ info.
+
+config SPEAKUP_SYNTH_KEYPC
+ tristate "Keynote Gold PC synthesizer support"
+ depends on ISA || COMPILE_TEST
+ ---help---
+
+ This is the Speakup driver for the Keynote Gold
+ PC synthesizer. You can say y to build it into the
+ kernel, or m to build it as a module. See the
+ configuration help on the Speakup choice above for more
+ info.
+
+config SPEAKUP_SYNTH_LTLK
+ tristate "DoubleTalk LT/LiteTalk synthesizer support"
+---help---
+
+ This is the Speakup driver for the LiteTalk/DoubleTalk
+ LT synthesizer. You can say y to build it into the
+ kernel, or m to build it as a module. See the
+ configuration help on the Speakup choice above for more
+ info.
+
+config SPEAKUP_SYNTH_SOFT
+ tristate "Userspace software synthesizer support"
+ ---help---
+
+ This is the software synthesizer device node. It will
+ register a device /dev/softsynth which midware programs
+ and speech daemons may open and read to provide kernel
+ output to software synths such as espeak, festival,
+ flite and so forth. You can select 'y' or 'm' to have
+ it built-in to the kernel or loaded as a module.
+
+config SPEAKUP_SYNTH_SPKOUT
+ tristate "Speak Out synthesizer support"
+ ---help---
+
+ This is the Speakup driver for the Speakout synthesizer.
+ You can say y to build it into the kernel, or m to
+ build it as a module. See the configuration help on the
+ Speakup choice above for more info.
+
+config SPEAKUP_SYNTH_TXPRT
+ tristate "Transport synthesizer support"
+ ---help---
+
+ This is the Speakup driver for the Transport
+ synthesizer. You can say y to build it into the kernel,
+ or m to build it as a module. See the configuration
+ help on the Speakup choice above for more info.
+
+config SPEAKUP_SYNTH_DUMMY
+ tristate "Dummy synthesizer driver (for testing)"
+ ---help---
+
+ This is a dummy Speakup driver for plugging a mere serial
+ terminal. This is handy if you want to test speakup but
+ don't have the hardware. You can say y to build it into
+ the kernel, or m to build it as a module. See the
+ configuration help on the Speakup choice above for more info.
+
+endif # SPEAKUP
+endmenu
diff --git a/drivers/staging/speakup/Makefile b/drivers/staging/speakup/Makefile
new file mode 100644
index 000000000..c5e43a598
--- /dev/null
+++ b/drivers/staging/speakup/Makefile
@@ -0,0 +1,30 @@
+obj-$(CONFIG_SPEAKUP_SYNTH_ACNTSA) += speakup_acntsa.o
+obj-$(CONFIG_SPEAKUP_SYNTH_ACNTPC) += speakup_acntpc.o
+obj-$(CONFIG_SPEAKUP_SYNTH_APOLLO) += speakup_apollo.o
+obj-$(CONFIG_SPEAKUP_SYNTH_AUDPTR) += speakup_audptr.o
+obj-$(CONFIG_SPEAKUP_SYNTH_BNS) += speakup_bns.o
+obj-$(CONFIG_SPEAKUP_SYNTH_DECTLK) += speakup_dectlk.o
+obj-$(CONFIG_SPEAKUP_SYNTH_DECEXT) += speakup_decext.o
+obj-$(CONFIG_SPEAKUP_SYNTH_DECPC) += speakup_decpc.o
+obj-$(CONFIG_SPEAKUP_SYNTH_DTLK) += speakup_dtlk.o
+obj-$(CONFIG_SPEAKUP_SYNTH_KEYPC) += speakup_keypc.o
+obj-$(CONFIG_SPEAKUP_SYNTH_LTLK) += speakup_ltlk.o
+obj-$(CONFIG_SPEAKUP_SYNTH_SOFT) += speakup_soft.o
+obj-$(CONFIG_SPEAKUP_SYNTH_SPKOUT) += speakup_spkout.o
+obj-$(CONFIG_SPEAKUP_SYNTH_TXPRT) += speakup_txprt.o
+obj-$(CONFIG_SPEAKUP_SYNTH_DUMMY) += speakup_dummy.o
+
+obj-$(CONFIG_SPEAKUP) += speakup.o
+speakup-y := \
+ buffers.o \
+ devsynth.o \
+ i18n.o \
+ fakekey.o \
+ main.o \
+ keyhelp.o \
+ kobjects.o \
+ selection.o \
+ serialio.o \
+ synth.o \
+ thread.o \
+ varhandlers.o
diff --git a/drivers/staging/speakup/TODO b/drivers/staging/speakup/TODO
new file mode 100644
index 000000000..3094799cf
--- /dev/null
+++ b/drivers/staging/speakup/TODO
@@ -0,0 +1,47 @@
+Speakup project home: http://www.linux-speakup.org
+
+Mailing List: speakup@linux-speakup.org
+
+Speakup is a kernel based screen review package for the linux operating
+system. It allows blind users to interact with applications on the
+linux console by means of synthetic speech.
+
+Currently, speakup has several issues we know of.
+
+The first issue has to do with the way speakup communicates with serial
+ports. Currently, we communicate directly with the hardware
+ports. This however conflicts with the standard serial port drivers,
+which poses various problems. This is also not working for modern hardware
+such as PCI-based serial ports. Also, there is not a way we can
+communicate with USB devices. The current serial port handling code is
+in serialio.c in this directory.
+
+Some places are currently using in_atomic() because speakup functions
+are called in various contexts, and a couple of things can't happen
+in these cases. Pushing work to some worker thread would probably help,
+as was already done for the serial port driving part.
+
+There is a duplication of the selection functions in selections.c. These
+functions should get exported from drivers/char/selection.c (clear_selection
+notably) and used from there instead.
+
+The kobjects may have to move to a more proper place in /sys. The
+discussion on lkml resulted to putting speech synthesizers in the
+"speech" class, and the speakup screen reader itself into
+/sys/class/vtconsole/vtcon0/speakup, the nasty path being handled by
+userland tools.
+
+Another issue seems to only happen on SMP systems. It seems
+that text in the output buffer gets garbled because a lock is not set.
+This bug happens regularly, but no one has been able to find a situation
+which produces it consistently.
+
+Patches, suggestions, corrections, etc, are definitely welcome.
+
+We prefer that you contact us on the mailing list; however, if you do
+not want to subscribe to a mailing list, send your email to all of the
+following:
+
+w.d.hubbs@gmail.com, chris@the-brannons.com, kirk@braille.uwo.ca and
+samuel.thibault@ens-lyon.org.
+
diff --git a/drivers/staging/speakup/buffers.c b/drivers/staging/speakup/buffers.c
new file mode 100644
index 000000000..d45c8afb0
--- /dev/null
+++ b/drivers/staging/speakup/buffers.c
@@ -0,0 +1,105 @@
+#include <linux/console.h>
+#include <linux/types.h>
+#include <linux/wait.h>
+
+#include "speakup.h"
+#include "spk_priv.h"
+
+#define SYNTH_BUF_SIZE 8192 /* currently 8K bytes */
+
+static u_char synth_buffer[SYNTH_BUF_SIZE]; /* guess what this is for! */
+static u_char *buff_in = synth_buffer;
+static u_char *buff_out = synth_buffer;
+static u_char *buffer_end = synth_buffer + SYNTH_BUF_SIZE - 1;
+
+/* These try to throttle applications by stopping the TTYs
+ * Note: we need to make sure that we will restart them eventually, which is
+ * usually not possible to do from the notifiers. TODO: it should be possible
+ * starting from linux 2.6.26.
+ *
+ * So we only stop when we know alive == 1 (else we discard the data anyway),
+ * and the alive synth will eventually call start_ttys from the thread context.
+ */
+void speakup_start_ttys(void)
+{
+ int i;
+
+ for (i = 0; i < MAX_NR_CONSOLES; i++) {
+ if (speakup_console[i] && speakup_console[i]->tty_stopped)
+ continue;
+ if ((vc_cons[i].d != NULL) && (vc_cons[i].d->port.tty != NULL))
+ start_tty(vc_cons[i].d->port.tty);
+ }
+}
+EXPORT_SYMBOL_GPL(speakup_start_ttys);
+
+static void speakup_stop_ttys(void)
+{
+ int i;
+
+ for (i = 0; i < MAX_NR_CONSOLES; i++)
+ if ((vc_cons[i].d != NULL) && (vc_cons[i].d->port.tty != NULL))
+ stop_tty(vc_cons[i].d->port.tty);
+}
+
+static int synth_buffer_free(void)
+{
+ int bytes_free;
+
+ if (buff_in >= buff_out)
+ bytes_free = SYNTH_BUF_SIZE - (buff_in - buff_out);
+ else
+ bytes_free = buff_out - buff_in;
+ return bytes_free;
+}
+
+int synth_buffer_empty(void)
+{
+ return (buff_in == buff_out);
+}
+EXPORT_SYMBOL_GPL(synth_buffer_empty);
+
+void synth_buffer_add(char ch)
+{
+ if (!synth->alive) {
+ /* This makes sure that we won't stop TTYs if there is no synth
+ * to restart them */
+ return;
+ }
+ if (synth_buffer_free() <= 100) {
+ synth_start();
+ speakup_stop_ttys();
+ }
+ if (synth_buffer_free() <= 1)
+ return;
+ *buff_in++ = ch;
+ if (buff_in > buffer_end)
+ buff_in = synth_buffer;
+}
+
+char synth_buffer_getc(void)
+{
+ char ch;
+
+ if (buff_out == buff_in)
+ return 0;
+ ch = *buff_out++;
+ if (buff_out > buffer_end)
+ buff_out = synth_buffer;
+ return ch;
+}
+EXPORT_SYMBOL_GPL(synth_buffer_getc);
+
+char synth_buffer_peek(void)
+{
+ if (buff_out == buff_in)
+ return 0;
+ return *buff_out;
+}
+EXPORT_SYMBOL_GPL(synth_buffer_peek);
+
+void synth_buffer_clear(void)
+{
+ buff_in = buff_out = synth_buffer;
+}
+EXPORT_SYMBOL_GPL(synth_buffer_clear);
diff --git a/drivers/staging/speakup/devsynth.c b/drivers/staging/speakup/devsynth.c
new file mode 100644
index 000000000..71c728acf
--- /dev/null
+++ b/drivers/staging/speakup/devsynth.c
@@ -0,0 +1,95 @@
+#include <linux/errno.h>
+#include <linux/miscdevice.h> /* for misc_register, and SYNTH_MINOR */
+#include <linux/types.h>
+#include <linux/uaccess.h>
+
+#include "speakup.h"
+#include "spk_priv.h"
+
+#ifndef SYNTH_MINOR
+#define SYNTH_MINOR 25
+#endif
+
+static int misc_registered;
+static int dev_opened;
+
+static ssize_t speakup_file_write(struct file *fp, const char __user *buffer,
+ size_t nbytes, loff_t *ppos)
+{
+ size_t count = nbytes;
+ const char __user *ptr = buffer;
+ size_t bytes;
+ unsigned long flags;
+ u_char buf[256];
+
+ if (synth == NULL)
+ return -ENODEV;
+ while (count > 0) {
+ bytes = min(count, sizeof(buf));
+ if (copy_from_user(buf, ptr, bytes))
+ return -EFAULT;
+ count -= bytes;
+ ptr += bytes;
+ spin_lock_irqsave(&speakup_info.spinlock, flags);
+ synth_write(buf, bytes);
+ spin_unlock_irqrestore(&speakup_info.spinlock, flags);
+ }
+ return (ssize_t) nbytes;
+}
+
+static ssize_t speakup_file_read(struct file *fp, char __user *buf,
+ size_t nbytes, loff_t *ppos)
+{
+ return 0;
+}
+
+static int speakup_file_open(struct inode *ip, struct file *fp)
+{
+ if (synth == NULL)
+ return -ENODEV;
+ if (xchg(&dev_opened, 1))
+ return -EBUSY;
+ return 0;
+}
+
+static int speakup_file_release(struct inode *ip, struct file *fp)
+{
+ dev_opened = 0;
+ return 0;
+}
+
+static const struct file_operations synth_fops = {
+ .read = speakup_file_read,
+ .write = speakup_file_write,
+ .open = speakup_file_open,
+ .release = speakup_file_release,
+};
+
+static struct miscdevice synth_device = {
+ .minor = SYNTH_MINOR,
+ .name = "synth",
+ .fops = &synth_fops,
+};
+
+void speakup_register_devsynth(void)
+{
+ if (misc_registered != 0)
+ return;
+/* zero it so if register fails, deregister will not ref invalid ptrs */
+ if (misc_register(&synth_device))
+ pr_warn("Couldn't initialize miscdevice /dev/synth.\n");
+ else {
+ pr_info("initialized device: /dev/synth, node (MAJOR %d, MINOR %d)\n",
+ MISC_MAJOR, SYNTH_MINOR);
+ misc_registered = 1;
+ }
+}
+
+void speakup_unregister_devsynth(void)
+{
+ if (!misc_registered)
+ return;
+ pr_info("speakup: unregistering synth device /dev/synth\n");
+ misc_deregister(&synth_device);
+ misc_registered = 0;
+}
diff --git a/drivers/staging/speakup/fakekey.c b/drivers/staging/speakup/fakekey.c
new file mode 100644
index 000000000..4299cf45f
--- /dev/null
+++ b/drivers/staging/speakup/fakekey.c
@@ -0,0 +1,99 @@
+/* fakekey.c
+ * Functions for simulating keypresses.
+ *
+ * Copyright (C) 2010 the Speakup Team
+ *
+ * This program is free software; you can redistribute it and/or 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/types.h>
+#include <linux/slab.h>
+#include <linux/preempt.h>
+#include <linux/percpu.h>
+#include <linux/input.h>
+
+#include "speakup.h"
+
+#define PRESSED 1
+#define RELEASED 0
+
+static DEFINE_PER_CPU(bool, reporting_keystroke);
+
+static struct input_dev *virt_keyboard;
+
+int speakup_add_virtual_keyboard(void)
+{
+ int err;
+
+ virt_keyboard = input_allocate_device();
+
+ if (!virt_keyboard)
+ return -ENOMEM;
+
+ virt_keyboard->name = "Speakup";
+ virt_keyboard->id.bustype = BUS_VIRTUAL;
+ virt_keyboard->phys = "speakup/input0";
+ virt_keyboard->dev.parent = NULL;
+
+ __set_bit(EV_KEY, virt_keyboard->evbit);
+ __set_bit(KEY_DOWN, virt_keyboard->keybit);
+
+ err = input_register_device(virt_keyboard);
+ if (err) {
+ input_free_device(virt_keyboard);
+ virt_keyboard = NULL;
+ }
+
+ return err;
+}
+
+void speakup_remove_virtual_keyboard(void)
+{
+ if (virt_keyboard != NULL) {
+ input_unregister_device(virt_keyboard);
+ virt_keyboard = NULL;
+ }
+}
+
+/*
+ * Send a simulated down-arrow to the application.
+ */
+void speakup_fake_down_arrow(void)
+{
+ unsigned long flags;
+
+ /* disable keyboard interrupts */
+ local_irq_save(flags);
+ /* don't change CPU */
+ preempt_disable();
+
+ __this_cpu_write(reporting_keystroke, true);
+ input_report_key(virt_keyboard, KEY_DOWN, PRESSED);
+ input_report_key(virt_keyboard, KEY_DOWN, RELEASED);
+ __this_cpu_write(reporting_keystroke, false);
+
+ /* reenable preemption */
+ preempt_enable();
+ /* reenable keyboard interrupts */
+ local_irq_restore(flags);
+}
+
+/*
+ * Are we handling a simulated keypress on the current CPU?
+ * Returns a boolean.
+ */
+bool speakup_fake_key_pressed(void)
+{
+ return this_cpu_read(reporting_keystroke);
+}
diff --git a/drivers/staging/speakup/i18n.c b/drivers/staging/speakup/i18n.c
new file mode 100644
index 000000000..9ea16c5b4
--- /dev/null
+++ b/drivers/staging/speakup/i18n.c
@@ -0,0 +1,629 @@
+/* Internationalization implementation. Includes definitions of English
+ * string arrays, and the i18n pointer. */
+
+#include <linux/slab.h> /* For kmalloc. */
+#include <linux/ctype.h>
+#include <linux/module.h>
+#include <linux/string.h>
+#include "speakup.h"
+#include "spk_priv.h"
+
+static char *speakup_msgs[MSG_LAST_INDEX];
+static char *speakup_default_msgs[MSG_LAST_INDEX] = {
+ [MSG_BLANK] = "blank",
+ [MSG_IAM_ALIVE] = "I'm aLive!",
+ [MSG_YOU_KILLED_SPEAKUP] = "You killed speakup!",
+ [MSG_HEY_THATS_BETTER] = "hey. That's better!",
+ [MSG_YOU_TURNED_ME_OFF] = "You turned me off!",
+ [MSG_PARKED] = "parked!",
+ [MSG_UNPARKED] = "unparked!",
+ [MSG_MARK] = "mark",
+ [MSG_CUT] = "cut",
+ [MSG_MARK_CLEARED] = "mark, cleared",
+ [MSG_PASTE] = "paste",
+ [MSG_BRIGHT] = "bright",
+ [MSG_ON_BLINKING] = "on blinking",
+ [MSG_OFF] = "off",
+ [MSG_ON] = "on",
+ [MSG_NO_WINDOW] = "no window",
+ [MSG_CURSORING_OFF] = "cursoring off",
+ [MSG_CURSORING_ON] = "cursoring on",
+ [MSG_HIGHLIGHT_TRACKING] = "highlight tracking",
+ [MSG_READ_WINDOW] = "read windo",
+ [MSG_READ_ALL] = "read all",
+ [MSG_EDIT_DONE] = "edit done",
+ [MSG_WINDOW_ALREADY_SET] = "window already set, clear then reset",
+ [MSG_END_BEFORE_START] = "error end before start",
+ [MSG_WINDOW_CLEARED] = "window cleared",
+ [MSG_WINDOW_SILENCED] = "window silenced",
+ [MSG_WINDOW_SILENCE_DISABLED] = "window silence disabled",
+ [MSG_ERROR] = "error",
+ [MSG_GOTO_CANCELED] = "goto canceled",
+ [MSG_GOTO] = "go to?",
+ [MSG_LEAVING_HELP] = "leaving help",
+ [MSG_IS_UNASSIGNED] = "is unassigned",
+ [MSG_HELP_INFO] =
+ "press space to exit, up or down to scroll, or a letter to go to a command",
+ [MSG_EDGE_TOP] = "top,",
+ [MSG_EDGE_BOTTOM] = "bottom,",
+ [MSG_EDGE_LEFT] = "left,",
+ [MSG_EDGE_RIGHT] = "right,",
+ [MSG_NUMBER] = "number",
+ [MSG_SPACE] = "space",
+ [MSG_START] = "start",
+ [MSG_END] = "end",
+ [MSG_CTRL] = "control-",
+ [MSG_DISJUNCTION] = "or",
+
+/* Messages with embedded format specifiers. */
+ [MSG_POS_INFO] = "line %ld, col %ld, t t y %d",
+ [MSG_CHAR_INFO] = "hex %02x, decimal %d",
+ [MSG_REPEAT_DESC] = "times %d .",
+ [MSG_REPEAT_DESC2] = "repeated %d .",
+ [MSG_WINDOW_LINE] = "window is line %d",
+ [MSG_WINDOW_BOUNDARY] = "%s at line %d, column %d",
+ [MSG_EDIT_PROMPT] = "edit %s, press space when done",
+ [MSG_NO_COMMAND] = "no commands for %c",
+ [MSG_KEYDESC] = "is %s",
+
+ /* Control keys. */
+ /* Most of these duplicate the entries in state names. */
+ [MSG_CTL_SHIFT] = "shift",
+ [MSG_CTL_ALTGR] = "altgr",
+ [MSG_CTL_CONTROL] = "control",
+ [MSG_CTL_ALT] = "alt",
+ [MSG_CTL_LSHIFT] = "l shift",
+ [MSG_CTL_SPEAKUP] = "speakup",
+ [MSG_CTL_LCONTROL] = "l control",
+ [MSG_CTL_RCONTROL] = "r control",
+ [MSG_CTL_CAPSSHIFT] = "caps shift",
+
+ /* Color names. */
+ [MSG_COLOR_BLACK] = "black",
+ [MSG_COLOR_BLUE] = "blue",
+ [MSG_COLOR_GREEN] = "green",
+ [MSG_COLOR_CYAN] = "cyan",
+ [MSG_COLOR_RED] = "red",
+ [MSG_COLOR_MAGENTA] = "magenta",
+ [MSG_COLOR_YELLOW] = "yellow",
+ [MSG_COLOR_WHITE] = "white",
+ [MSG_COLOR_GREY] = "grey",
+
+ /* Names of key states. */
+ [MSG_STATE_DOUBLE] = "double",
+ [MSG_STATE_SPEAKUP] = "speakup",
+ [MSG_STATE_ALT] = "alt",
+ [MSG_STATE_CONTROL] = "ctrl",
+ [MSG_STATE_ALTGR] = "altgr",
+ [MSG_STATE_SHIFT] = "shift",
+
+ /* Key names. */
+ [MSG_KEYNAME_ESC] = "escape",
+ [MSG_KEYNAME_1] = "1",
+ [MSG_KEYNAME_2] = "2",
+ [MSG_KEYNAME_3] = "3",
+ [MSG_KEYNAME_4] = "4",
+ [MSG_KEYNAME_5] = "5",
+ [MSG_KEYNAME_6] = "6",
+ [MSG_KEYNAME_7] = "7",
+ [MSG_KEYNAME_8] = "8",
+ [MSG_KEYNAME_9] = "9",
+ [MSG_KEYNAME_0] = "0",
+ [MSG_KEYNAME_DASH] = "minus",
+ [MSG_KEYNAME_EQUAL] = "equal",
+ [MSG_KEYNAME_BS] = "back space",
+ [MSG_KEYNAME_TAB] = "tab",
+ [MSG_KEYNAME_Q] = "q",
+ [MSG_KEYNAME_W] = "w",
+ [MSG_KEYNAME_E] = "e",
+ [MSG_KEYNAME_R] = "r",
+ [MSG_KEYNAME_T] = "t",
+ [MSG_KEYNAME_Y] = "y",
+ [MSG_KEYNAME_U] = "u",
+ [MSG_KEYNAME_I] = "i",
+ [MSG_KEYNAME_O] = "o",
+ [MSG_KEYNAME_P] = "p",
+ [MSG_KEYNAME_LEFTBRACE] = "left brace",
+ [MSG_KEYNAME_RIGHTBRACE] = "right brace",
+ [MSG_KEYNAME_ENTER] = "enter",
+ [MSG_KEYNAME_LEFTCTRL] = "left control",
+ [MSG_KEYNAME_A] = "a",
+ [MSG_KEYNAME_S] = "s",
+ [MSG_KEYNAME_D] = "d",
+ [MSG_KEYNAME_F] = "f",
+ [MSG_KEYNAME_G] = "g",
+ [MSG_KEYNAME_H] = "h",
+ [MSG_KEYNAME_J] = "j",
+ [MSG_KEYNAME_K] = "k",
+ [MSG_KEYNAME_L] = "l",
+ [MSG_KEYNAME_SEMICOLON] = "semicolon",
+ [MSG_KEYNAME_SINGLEQUOTE] = "apostrophe",
+ [MSG_KEYNAME_GRAVE] = "accent",
+ [MSG_KEYNAME_LEFTSHFT] = "left shift",
+ [MSG_KEYNAME_BACKSLASH] = "back slash",
+ [MSG_KEYNAME_Z] = "z",
+ [MSG_KEYNAME_X] = "x",
+ [MSG_KEYNAME_C] = "c",
+ [MSG_KEYNAME_V] = "v",
+ [MSG_KEYNAME_B] = "b",
+ [MSG_KEYNAME_N] = "n",
+ [MSG_KEYNAME_M] = "m",
+ [MSG_KEYNAME_COMMA] = "comma",
+ [MSG_KEYNAME_DOT] = "dot",
+ [MSG_KEYNAME_SLASH] = "slash",
+ [MSG_KEYNAME_RIGHTSHFT] = "right shift",
+ [MSG_KEYNAME_KPSTAR] = "keypad asterisk",
+ [MSG_KEYNAME_LEFTALT] = "left alt",
+ [MSG_KEYNAME_SPACE] = "space",
+ [MSG_KEYNAME_CAPSLOCK] = "caps lock",
+ [MSG_KEYNAME_F1] = "f1",
+ [MSG_KEYNAME_F2] = "f2",
+ [MSG_KEYNAME_F3] = "f3",
+ [MSG_KEYNAME_F4] = "f4",
+ [MSG_KEYNAME_F5] = "f5",
+ [MSG_KEYNAME_F6] = "f6",
+ [MSG_KEYNAME_F7] = "f7",
+ [MSG_KEYNAME_F8] = "f8",
+ [MSG_KEYNAME_F9] = "f9",
+ [MSG_KEYNAME_F10] = "f10",
+ [MSG_KEYNAME_NUMLOCK] = "num lock",
+ [MSG_KEYNAME_SCROLLLOCK] = "scroll lock",
+ [MSG_KEYNAME_KP7] = "keypad 7",
+ [MSG_KEYNAME_KP8] = "keypad 8",
+ [MSG_KEYNAME_KP9] = "keypad 9",
+ [MSG_KEYNAME_KPMINUS] = "keypad minus",
+ [MSG_KEYNAME_KP4] = "keypad 4",
+ [MSG_KEYNAME_KP5] = "keypad 5",
+ [MSG_KEYNAME_KP6] = "keypad 6",
+ [MSG_KEYNAME_KPPLUS] = "keypad plus",
+ [MSG_KEYNAME_KP1] = "keypad 1",
+ [MSG_KEYNAME_KP2] = "keypad 2",
+ [MSG_KEYNAME_KP3] = "keypad 3",
+ [MSG_KEYNAME_KP0] = "keypad 0",
+ [MSG_KEYNAME_KPDOT] = "keypad dot",
+ [MSG_KEYNAME_103RD] = "103rd",
+ [MSG_KEYNAME_F13] = "f13",
+ [MSG_KEYNAME_102ND] = "102nd",
+ [MSG_KEYNAME_F11] = "f11",
+ [MSG_KEYNAME_F12] = "f12",
+ [MSG_KEYNAME_F14] = "f14",
+ [MSG_KEYNAME_F15] = "f15",
+ [MSG_KEYNAME_F16] = "f16",
+ [MSG_KEYNAME_F17] = "f17",
+ [MSG_KEYNAME_F18] = "f18",
+ [MSG_KEYNAME_F19] = "f19",
+ [MSG_KEYNAME_F20] = "f20",
+ [MSG_KEYNAME_KPENTER] = "keypad enter",
+ [MSG_KEYNAME_RIGHTCTRL] = "right control",
+ [MSG_KEYNAME_KPSLASH] = "keypad slash",
+ [MSG_KEYNAME_SYSRQ] = "sysrq",
+ [MSG_KEYNAME_RIGHTALT] = "right alt",
+ [MSG_KEYNAME_LF] = "line feed",
+ [MSG_KEYNAME_HOME] = "home",
+ [MSG_KEYNAME_UP] = "up",
+ [MSG_KEYNAME_PGUP] = "page up",
+ [MSG_KEYNAME_LEFT] = "left",
+ [MSG_KEYNAME_RIGHT] = "right",
+ [MSG_KEYNAME_END] = "end",
+ [MSG_KEYNAME_DOWN] = "down",
+ [MSG_KEYNAME_PGDN] = "page down",
+ [MSG_KEYNAME_INS] = "insert",
+ [MSG_KEYNAME_DEL] = "delete",
+ [MSG_KEYNAME_MACRO] = "macro",
+ [MSG_KEYNAME_MUTE] = "mute",
+ [MSG_KEYNAME_VOLDOWN] = "volume down",
+ [MSG_KEYNAME_VOLUP] = "volume up",
+ [MSG_KEYNAME_POWER] = "power",
+ [MSG_KEYNAME_KPEQUAL] = "keypad equal",
+ [MSG_KEYNAME_KPPLUSDASH] = "keypad plusminus",
+ [MSG_KEYNAME_PAUSE] = "pause",
+ [MSG_KEYNAME_F21] = "f21",
+ [MSG_KEYNAME_F22] = "f22",
+ [MSG_KEYNAME_F23] = "f23",
+ [MSG_KEYNAME_F24] = "f24",
+ [MSG_KEYNAME_KPCOMMA] = "keypad comma",
+ [MSG_KEYNAME_LEFTMETA] = "left meta",
+ [MSG_KEYNAME_RIGHTMETA] = "right meta",
+ [MSG_KEYNAME_COMPOSE] = "compose",
+ [MSG_KEYNAME_STOP] = "stop",
+ [MSG_KEYNAME_AGAIN] = "again",
+ [MSG_KEYNAME_PROPS] = "props",
+ [MSG_KEYNAME_UNDO] = "undo",
+ [MSG_KEYNAME_FRONT] = "front",
+ [MSG_KEYNAME_COPY] = "copy",
+ [MSG_KEYNAME_OPEN] = "open",
+ [MSG_KEYNAME_PASTE] = "paste",
+ [MSG_KEYNAME_FIND] = "find",
+ [MSG_KEYNAME_CUT] = "cut",
+ [MSG_KEYNAME_HELP] = "help",
+ [MSG_KEYNAME_MENU] = "menu",
+ [MSG_KEYNAME_CALC] = "calc",
+ [MSG_KEYNAME_SETUP] = "setup",
+ [MSG_KEYNAME_SLEEP] = "sleep",
+ [MSG_KEYNAME_WAKEUP] = "wakeup",
+ [MSG_KEYNAME_FILE] = "file",
+ [MSG_KEYNAME_SENDFILE] = "send file",
+ [MSG_KEYNAME_DELFILE] = "delete file",
+ [MSG_KEYNAME_XFER] = "transfer",
+ [MSG_KEYNAME_PROG1] = "prog1",
+ [MSG_KEYNAME_PROG2] = "prog2",
+ [MSG_KEYNAME_WWW] = "www",
+ [MSG_KEYNAME_MSDOS] = "msdos",
+ [MSG_KEYNAME_COFFEE] = "coffee",
+ [MSG_KEYNAME_DIRECTION] = "direction",
+ [MSG_KEYNAME_CYCLEWINDOWS] = "cycle windows",
+ [MSG_KEYNAME_MAIL] = "mail",
+ [MSG_KEYNAME_BOOKMARKS] = "bookmarks",
+ [MSG_KEYNAME_COMPUTER] = "computer",
+ [MSG_KEYNAME_BACK] = "back",
+ [MSG_KEYNAME_FORWARD] = "forward",
+ [MSG_KEYNAME_CLOSECD] = "close cd",
+ [MSG_KEYNAME_EJECTCD] = "eject cd",
+ [MSG_KEYNAME_EJECTCLOSE] = "eject close cd",
+ [MSG_KEYNAME_NEXTSONG] = "next song",
+ [MSG_KEYNAME_PLAYPAUSE] = "play pause",
+ [MSG_KEYNAME_PREVSONG] = "previous song",
+ [MSG_KEYNAME_STOPCD] = "stop cd",
+ [MSG_KEYNAME_RECORD] = "record",
+ [MSG_KEYNAME_REWIND] = "rewind",
+ [MSG_KEYNAME_PHONE] = "phone",
+ [MSG_KEYNAME_ISO] = "iso",
+ [MSG_KEYNAME_CONFIG] = "config",
+ [MSG_KEYNAME_HOMEPG] = "home page",
+ [MSG_KEYNAME_REFRESH] = "refresh",
+ [MSG_KEYNAME_EXIT] = "exit",
+ [MSG_KEYNAME_MOVE] = "move",
+ [MSG_KEYNAME_EDIT] = "edit",
+ [MSG_KEYNAME_SCROLLUP] = "scroll up",
+ [MSG_KEYNAME_SCROLLDN] = "scroll down",
+ [MSG_KEYNAME_KPLEFTPAR] = "keypad left paren",
+ [MSG_KEYNAME_KPRIGHTPAR] = "keypad right paren",
+
+ /* Function names. */
+ [MSG_FUNCNAME_ATTRIB_BLEEP_DEC] = "attribute bleep decrement",
+ [MSG_FUNCNAME_ATTRIB_BLEEP_INC] = "attribute bleep increment",
+ [MSG_FUNCNAME_BLEEPS_DEC] = "bleeps decrement",
+ [MSG_FUNCNAME_BLEEPS_INC] = "bleeps increment",
+ [MSG_FUNCNAME_CHAR_FIRST] = "character, first",
+ [MSG_FUNCNAME_CHAR_LAST] = "character, last",
+ [MSG_FUNCNAME_CHAR_CURRENT] = "character, say current",
+ [MSG_FUNCNAME_CHAR_HEX_AND_DEC] = "character, say hex and decimal",
+ [MSG_FUNCNAME_CHAR_NEXT] = "character, say next",
+ [MSG_FUNCNAME_CHAR_PHONETIC] = "character, say phonetic",
+ [MSG_FUNCNAME_CHAR_PREVIOUS] = "character, say previous",
+ [MSG_FUNCNAME_CURSOR_PARK] = "cursor park",
+ [MSG_FUNCNAME_CUT] = "cut",
+ [MSG_FUNCNAME_EDIT_DELIM] = "edit delimiters",
+ [MSG_FUNCNAME_EDIT_EXNUM] = "edit exnum",
+ [MSG_FUNCNAME_EDIT_MOST] = "edit most",
+ [MSG_FUNCNAME_EDIT_REPEATS] = "edit repeats",
+ [MSG_FUNCNAME_EDIT_SOME] = "edit some",
+ [MSG_FUNCNAME_GOTO] = "go to",
+ [MSG_FUNCNAME_GOTO_BOTTOM] = "go to bottom edge",
+ [MSG_FUNCNAME_GOTO_LEFT] = "go to left edge",
+ [MSG_FUNCNAME_GOTO_RIGHT] = "go to right edge",
+ [MSG_FUNCNAME_GOTO_TOP] = "go to top edge",
+ [MSG_FUNCNAME_HELP] = "help",
+ [MSG_FUNCNAME_LINE_SAY_CURRENT] = "line, say current",
+ [MSG_FUNCNAME_LINE_SAY_NEXT] = "line, say next",
+ [MSG_FUNCNAME_LINE_SAY_PREVIOUS] = "line, say previous",
+ [MSG_FUNCNAME_LINE_SAY_WITH_INDENT] = "line, say with indent",
+ [MSG_FUNCNAME_PASTE] = "paste",
+ [MSG_FUNCNAME_PITCH_DEC] = "pitch decrement",
+ [MSG_FUNCNAME_PITCH_INC] = "pitch increment",
+ [MSG_FUNCNAME_PUNC_DEC] = "punctuation decrement",
+ [MSG_FUNCNAME_PUNC_INC] = "punctuation increment",
+ [MSG_FUNCNAME_PUNC_LEVEL_DEC] = "punc level decrement",
+ [MSG_FUNCNAME_PUNC_LEVEL_INC] = "punc level increment",
+ [MSG_FUNCNAME_QUIET] = "quiet",
+ [MSG_FUNCNAME_RATE_DEC] = "rate decrement",
+ [MSG_FUNCNAME_RATE_INC] = "rate increment",
+ [MSG_FUNCNAME_READING_PUNC_DEC] = "reading punctuation decrement",
+ [MSG_FUNCNAME_READING_PUNC_INC] = "reading punctuation increment",
+ [MSG_FUNCNAME_SAY_ATTRIBUTES] = "say attributes",
+ [MSG_FUNCNAME_SAY_FROM_LEFT] = "say from left",
+ [MSG_FUNCNAME_SAY_FROM_TOP] = "say from top",
+ [MSG_FUNCNAME_SAY_POSITION] = "say position",
+ [MSG_FUNCNAME_SAY_SCREEN] = "say screen",
+ [MSG_FUNCNAME_SAY_TO_BOTTOM] = "say to bottom",
+ [MSG_FUNCNAME_SAY_TO_RIGHT] = "say to right",
+ [MSG_FUNCNAME_SPEAKUP] = "speakup",
+ [MSG_FUNCNAME_SPEAKUP_LOCK] = "speakup lock",
+ [MSG_FUNCNAME_SPEAKUP_OFF] = "speakup off",
+ [MSG_FUNCNAME_SPEECH_KILL] = "speech kill",
+ [MSG_FUNCNAME_SPELL_DELAY_DEC] = "spell delay decrement",
+ [MSG_FUNCNAME_SPELL_DELAY_INC] = "spell delay increment",
+ [MSG_FUNCNAME_SPELL_WORD] = "spell word",
+ [MSG_FUNCNAME_SPELL_WORD_PHONETICALLY] = "spell word phoneticly",
+ [MSG_FUNCNAME_TONE_DEC] = "tone decrement",
+ [MSG_FUNCNAME_TONE_INC] = "tone increment",
+ [MSG_FUNCNAME_VOICE_DEC] = "voice decrement",
+ [MSG_FUNCNAME_VOICE_INC] = "voice increment",
+ [MSG_FUNCNAME_VOLUME_DEC] = "volume decrement",
+ [MSG_FUNCNAME_VOLUME_INC] = "volume increment",
+ [MSG_FUNCNAME_WINDOW_CLEAR] = "window, clear",
+ [MSG_FUNCNAME_WINDOW_SAY] = "window, say",
+ [MSG_FUNCNAME_WINDOW_SET] = "window, set",
+ [MSG_FUNCNAME_WINDOW_SILENCE] = "window, silence",
+ [MSG_FUNCNAME_WORD_SAY_CURRENT] = "word, say current",
+ [MSG_FUNCNAME_WORD_SAY_NEXT] = "word, say next",
+ [MSG_FUNCNAME_WORD_SAY_PREVIOUS] = "word, say previous",
+};
+
+static struct msg_group_t all_groups[] = {
+ {
+ .name = "ctl_keys",
+ .start = MSG_CTL_START,
+ .end = MSG_CTL_END,
+ },
+ {
+ .name = "colors",
+ .start = MSG_COLORS_START,
+ .end = MSG_COLORS_END,
+ },
+ {
+ .name = "formatted",
+ .start = MSG_FORMATTED_START,
+ .end = MSG_FORMATTED_END,
+ },
+ {
+ .name = "function_names",
+ .start = MSG_FUNCNAMES_START,
+ .end = MSG_FUNCNAMES_END,
+ },
+ {
+ .name = "key_names",
+ .start = MSG_KEYNAMES_START,
+ .end = MSG_KEYNAMES_END,
+ },
+ {
+ .name = "announcements",
+ .start = MSG_ANNOUNCEMENTS_START,
+ .end = MSG_ANNOUNCEMENTS_END,
+ },
+ {
+ .name = "states",
+ .start = MSG_STATES_START,
+ .end = MSG_STATES_END,
+ },
+};
+
+static const int num_groups = sizeof(all_groups) / sizeof(struct msg_group_t);
+
+char *spk_msg_get(enum msg_index_t index)
+{
+ char *ch;
+
+ ch = speakup_msgs[index];
+ return ch;
+}
+
+/*
+ * Function: next_specifier
+ * Finds the start of the next format specifier in the argument string.
+ * Return value: pointer to start of format
+ * specifier, or NULL if no specifier exists.
+*/
+static char *next_specifier(char *input)
+{
+ int found = 0;
+ char *next_percent = input;
+
+ while ((next_percent != NULL) && !found) {
+ next_percent = strchr(next_percent, '%');
+ if (next_percent != NULL) {
+ /* skip over doubled percent signs */
+ while ((next_percent[0] == '%')
+ && (next_percent[1] == '%'))
+ next_percent += 2;
+ if (*next_percent == '%')
+ found = 1;
+ else if (*next_percent == '\0')
+ next_percent = NULL;
+ }
+ }
+
+ return next_percent;
+}
+
+/* Skip over 0 or more flags. */
+static char *skip_flags(char *input)
+{
+ while ((*input != '\0') && strchr(" 0+-#", *input))
+ input++;
+ return input;
+}
+
+/* Skip over width.precision, if it exists. */
+static char *skip_width(char *input)
+{
+ while (isdigit(*input))
+ input++;
+ if (*input == '.') {
+ input++;
+ while (isdigit(*input))
+ input++;
+ }
+ return input;
+}
+
+/*
+ * Skip past the end of the conversion part.
+ * Note that this code only accepts a handful of conversion specifiers:
+ * c d s x and ld. Not accidental; these are exactly the ones used in
+ * the default group of formatted messages.
+*/
+static char *skip_conversion(char *input)
+{
+ if ((input[0] == 'l') && (input[1] == 'd'))
+ input += 2;
+ else if ((*input != '\0') && strchr("cdsx", *input))
+ input++;
+ return input;
+}
+
+/*
+ * Function: find_specifier_end
+ * Return a pointer to the end of the format specifier.
+*/
+static char *find_specifier_end(char *input)
+{
+ input++; /* Advance over %. */
+ input = skip_flags(input);
+ input = skip_width(input);
+ input = skip_conversion(input);
+ return input;
+}
+
+/*
+ * Function: compare_specifiers
+ * Compare the format specifiers pointed to by *input1 and *input2.
+ * Return 1 if they are the same, 0 otherwise. Advance *input1 and *input2
+ * so that they point to the character following the end of the specifier.
+*/
+static int compare_specifiers(char **input1, char **input2)
+{
+ int same = 0;
+ char *end1 = find_specifier_end(*input1);
+ char *end2 = find_specifier_end(*input2);
+ size_t length1 = end1 - *input1;
+ size_t length2 = end2 - *input2;
+
+ if ((length1 == length2) && !memcmp(*input1, *input2, length1))
+ same = 1;
+
+ *input1 = end1;
+ *input2 = end2;
+ return same;
+}
+
+/*
+ * Function: fmt_validate
+ * Check that two format strings contain the same number of format specifiers,
+ * and that the order of specifiers is the same in both strings.
+ * Return 1 if the condition holds, 0 if it doesn't.
+*/
+static int fmt_validate(char *template, char *user)
+{
+ int valid = 1;
+ int still_comparing = 1;
+ char *template_ptr = template;
+ char *user_ptr = user;
+
+ while (still_comparing && valid) {
+ template_ptr = next_specifier(template_ptr);
+ user_ptr = next_specifier(user_ptr);
+ if (template_ptr && user_ptr) {
+ /* Both have at least one more specifier. */
+ valid = compare_specifiers(&template_ptr, &user_ptr);
+ } else {
+ /* No more format specifiers in one or both strings. */
+ still_comparing = 0;
+ /* See if one has more specifiers than the other. */
+ if (template_ptr || user_ptr)
+ valid = 0;
+ }
+ }
+ return valid;
+}
+
+/*
+ * Function: msg_set
+ * Description: Add a user-supplied message to the user_messages array.
+ * The message text is copied to a memory area allocated with kmalloc.
+ * If the function fails, then user_messages is untouched.
+ * Arguments:
+ * - index: a message number, as found in i18n.h.
+ * - text: text of message. Not NUL-terminated.
+ * - length: number of bytes in text.
+ * Failure conditions:
+ * -EINVAL - Invalid format specifiers in formatted message or illegal index.
+ * -ENOMEM - Unable to allocate memory.
+*/
+ssize_t spk_msg_set(enum msg_index_t index, char *text, size_t length)
+{
+ int rc = 0;
+ char *newstr = NULL;
+ unsigned long flags;
+
+ if ((index >= MSG_FIRST_INDEX) && (index < MSG_LAST_INDEX)) {
+ newstr = kmalloc(length + 1, GFP_KERNEL);
+ if (newstr) {
+ memcpy(newstr, text, length);
+ newstr[length] = '\0';
+ if ((index >= MSG_FORMATTED_START
+ && index <= MSG_FORMATTED_END)
+ && !fmt_validate(speakup_default_msgs[index],
+ newstr)) {
+ kfree(newstr);
+ return -EINVAL;
+ }
+ spin_lock_irqsave(&speakup_info.spinlock, flags);
+ if (speakup_msgs[index] != speakup_default_msgs[index])
+ kfree(speakup_msgs[index]);
+ speakup_msgs[index] = newstr;
+ spin_unlock_irqrestore(&speakup_info.spinlock, flags);
+ } else {
+ rc = -ENOMEM;
+ }
+ } else {
+ rc = -EINVAL;
+ }
+ return rc;
+}
+
+/*
+ * Find a message group, given its name. Return a pointer to the structure
+ * if found, or NULL otherwise.
+*/
+struct msg_group_t *spk_find_msg_group(const char *group_name)
+{
+ struct msg_group_t *group = NULL;
+ int i;
+
+ for (i = 0; i < num_groups; i++) {
+ if (!strcmp(all_groups[i].name, group_name)) {
+ group = &all_groups[i];
+ break;
+ }
+ }
+ return group;
+}
+
+void spk_reset_msg_group(struct msg_group_t *group)
+{
+ unsigned long flags;
+ enum msg_index_t i;
+
+ spin_lock_irqsave(&speakup_info.spinlock, flags);
+
+ for (i = group->start; i <= group->end; i++) {
+ if (speakup_msgs[i] != speakup_default_msgs[i])
+ kfree(speakup_msgs[i]);
+ speakup_msgs[i] = speakup_default_msgs[i];
+ }
+ spin_unlock_irqrestore(&speakup_info.spinlock, flags);
+}
+
+/* Called at initialization time, to establish default messages. */
+void spk_initialize_msgs(void)
+{
+ memcpy(speakup_msgs, speakup_default_msgs,
+ sizeof(speakup_default_msgs));
+}
+
+/* Free user-supplied strings when module is unloaded: */
+void spk_free_user_msgs(void)
+{
+ enum msg_index_t index;
+ unsigned long flags;
+
+ spin_lock_irqsave(&speakup_info.spinlock, flags);
+ for (index = MSG_FIRST_INDEX; index < MSG_LAST_INDEX; index++) {
+ if (speakup_msgs[index] != speakup_default_msgs[index]) {
+ kfree(speakup_msgs[index]);
+ speakup_msgs[index] = speakup_default_msgs[index];
+ }
+ }
+ spin_unlock_irqrestore(&speakup_info.spinlock, flags);
+}
diff --git a/drivers/staging/speakup/i18n.h b/drivers/staging/speakup/i18n.h
new file mode 100644
index 000000000..326d086f9
--- /dev/null
+++ b/drivers/staging/speakup/i18n.h
@@ -0,0 +1,234 @@
+#ifndef I18N_H
+#define I18N_H
+/* Internationalization declarations */
+
+enum msg_index_t {
+ MSG_FIRST_INDEX,
+ MSG_ANNOUNCEMENTS_START = MSG_FIRST_INDEX,
+ MSG_BLANK = MSG_ANNOUNCEMENTS_START,
+ MSG_IAM_ALIVE,
+ MSG_YOU_KILLED_SPEAKUP,
+ MSG_HEY_THATS_BETTER,
+ MSG_YOU_TURNED_ME_OFF,
+ MSG_PARKED,
+ MSG_UNPARKED,
+ MSG_MARK,
+ MSG_CUT,
+ MSG_MARK_CLEARED,
+ MSG_PASTE,
+ MSG_BRIGHT,
+ MSG_ON_BLINKING,
+ MSG_STATUS_START,
+ MSG_OFF = MSG_STATUS_START,
+ MSG_ON,
+ MSG_NO_WINDOW,
+ MSG_CURSOR_MSGS_START,
+ MSG_CURSORING_OFF = MSG_CURSOR_MSGS_START,
+ MSG_CURSORING_ON,
+ MSG_HIGHLIGHT_TRACKING,
+ MSG_READ_WINDOW,
+ MSG_READ_ALL,
+ MSG_EDIT_DONE,
+ MSG_WINDOW_ALREADY_SET,
+ MSG_END_BEFORE_START,
+ MSG_WINDOW_CLEARED,
+ MSG_WINDOW_SILENCED,
+ MSG_WINDOW_SILENCE_DISABLED,
+ MSG_ERROR,
+ MSG_GOTO_CANCELED,
+ MSG_GOTO,
+ MSG_LEAVING_HELP,
+ MSG_IS_UNASSIGNED,
+ MSG_HELP_INFO,
+ MSG_EDGE_MSGS_START,
+ MSG_EDGE_TOP = MSG_EDGE_MSGS_START,
+ MSG_EDGE_BOTTOM,
+ MSG_EDGE_LEFT,
+ MSG_EDGE_RIGHT,
+ MSG_NUMBER,
+ MSG_SPACE,
+ MSG_START, /* A little confusing, given our convention. */
+ MSG_END, /* A little confusing, given our convention. */
+ MSG_CTRL,
+
+/* A message containing the single word "or". */
+ MSG_DISJUNCTION,
+ MSG_ANNOUNCEMENTS_END = MSG_DISJUNCTION,
+
+/* Messages with format specifiers. */
+ MSG_FORMATTED_START,
+ MSG_POS_INFO = MSG_FORMATTED_START,
+ MSG_CHAR_INFO,
+ MSG_REPEAT_DESC,
+ MSG_REPEAT_DESC2,
+ MSG_WINDOW_LINE,
+ MSG_WINDOW_BOUNDARY,
+ MSG_EDIT_PROMPT,
+ MSG_NO_COMMAND,
+ MSG_KEYDESC,
+ MSG_FORMATTED_END = MSG_KEYDESC,
+
+ /* Control keys. */
+ MSG_CTL_START,
+ MSG_CTL_SHIFT = MSG_CTL_START,
+ MSG_CTL_ALTGR,
+ MSG_CTL_CONTROL,
+ MSG_CTL_ALT,
+ MSG_CTL_LSHIFT,
+ MSG_CTL_SPEAKUP,
+ MSG_CTL_LCONTROL,
+ MSG_CTL_RCONTROL,
+ MSG_CTL_CAPSSHIFT,
+ MSG_CTL_END = MSG_CTL_CAPSSHIFT,
+
+ /* Colors. */
+ MSG_COLORS_START,
+ MSG_COLOR_BLACK = MSG_COLORS_START,
+ MSG_COLOR_BLUE,
+ MSG_COLOR_GREEN,
+ MSG_COLOR_CYAN,
+ MSG_COLOR_RED,
+ MSG_COLOR_MAGENTA,
+ MSG_COLOR_YELLOW,
+ MSG_COLOR_WHITE,
+ MSG_COLOR_GREY,
+ MSG_COLORS_END = MSG_COLOR_GREY,
+
+ MSG_STATES_START,
+ MSG_STATE_DOUBLE = MSG_STATES_START,
+ MSG_STATE_SPEAKUP,
+ MSG_STATE_ALT,
+ MSG_STATE_CONTROL,
+ MSG_STATE_ALTGR,
+ MSG_STATE_SHIFT,
+ MSG_STATES_END = MSG_STATE_SHIFT,
+
+ MSG_KEYNAMES_START,
+ MSG_KEYNAME_ESC = MSG_KEYNAMES_START,
+ MSG_KEYNAME_1, MSG_KEYNAME_2, MSG_KEYNAME_3, MSG_KEYNAME_4,
+ MSG_KEYNAME_5, MSG_KEYNAME_6, MSG_KEYNAME_7, MSG_KEYNAME_8,
+ MSG_KEYNAME_9,
+ MSG_KEYNAME_0, MSG_KEYNAME_DASH, MSG_KEYNAME_EQUAL, MSG_KEYNAME_BS,
+ MSG_KEYNAME_TAB,
+ MSG_KEYNAME_Q, MSG_KEYNAME_W, MSG_KEYNAME_E, MSG_KEYNAME_R,
+ MSG_KEYNAME_T, MSG_KEYNAME_Y, MSG_KEYNAME_U, MSG_KEYNAME_I,
+ MSG_KEYNAME_O, MSG_KEYNAME_P,
+ MSG_KEYNAME_LEFTBRACE, MSG_KEYNAME_RIGHTBRACE, MSG_KEYNAME_ENTER,
+ MSG_KEYNAME_LEFTCTRL, MSG_KEYNAME_A,
+ MSG_KEYNAME_S, MSG_KEYNAME_D, MSG_KEYNAME_F, MSG_KEYNAME_G,
+ MSG_KEYNAME_H, MSG_KEYNAME_J, MSG_KEYNAME_K, MSG_KEYNAME_L,
+ MSG_KEYNAME_SEMICOLON,
+ MSG_KEYNAME_SINGLEQUOTE, MSG_KEYNAME_GRAVE,
+ MSG_KEYNAME_LEFTSHFT, MSG_KEYNAME_BACKSLASH, MSG_KEYNAME_Z,
+ MSG_KEYNAME_X, MSG_KEYNAME_C, MSG_KEYNAME_V, MSG_KEYNAME_B,
+ MSG_KEYNAME_N, MSG_KEYNAME_M, MSG_KEYNAME_COMMA, MSG_KEYNAME_DOT,
+ MSG_KEYNAME_SLASH, MSG_KEYNAME_RIGHTSHFT,
+ MSG_KEYNAME_KPSTAR,
+ MSG_KEYNAME_LEFTALT, MSG_KEYNAME_SPACE, MSG_KEYNAME_CAPSLOCK,
+ MSG_KEYNAME_F1, MSG_KEYNAME_F2,
+ MSG_KEYNAME_F3, MSG_KEYNAME_F4, MSG_KEYNAME_F5, MSG_KEYNAME_F6,
+ MSG_KEYNAME_F7,
+ MSG_KEYNAME_F8, MSG_KEYNAME_F9, MSG_KEYNAME_F10, MSG_KEYNAME_NUMLOCK,
+ MSG_KEYNAME_SCROLLLOCK,
+ MSG_KEYNAME_KP7, MSG_KEYNAME_KP8, MSG_KEYNAME_KP9, MSG_KEYNAME_KPMINUS,
+ MSG_KEYNAME_KP4,
+ MSG_KEYNAME_KP5, MSG_KEYNAME_KP6, MSG_KEYNAME_KPPLUS, MSG_KEYNAME_KP1,
+ MSG_KEYNAME_KP2,
+ MSG_KEYNAME_KP3, MSG_KEYNAME_KP0, MSG_KEYNAME_KPDOT, MSG_KEYNAME_103RD,
+ MSG_KEYNAME_F13,
+ MSG_KEYNAME_102ND, MSG_KEYNAME_F11, MSG_KEYNAME_F12, MSG_KEYNAME_F14,
+ MSG_KEYNAME_F15,
+ MSG_KEYNAME_F16, MSG_KEYNAME_F17, MSG_KEYNAME_F18, MSG_KEYNAME_F19,
+ MSG_KEYNAME_F20,
+ MSG_KEYNAME_KPENTER, MSG_KEYNAME_RIGHTCTRL, MSG_KEYNAME_KPSLASH,
+ MSG_KEYNAME_SYSRQ, MSG_KEYNAME_RIGHTALT,
+ MSG_KEYNAME_LF, MSG_KEYNAME_HOME, MSG_KEYNAME_UP, MSG_KEYNAME_PGUP,
+ MSG_KEYNAME_LEFT,
+ MSG_KEYNAME_RIGHT, MSG_KEYNAME_END, MSG_KEYNAME_DOWN, MSG_KEYNAME_PGDN,
+ MSG_KEYNAME_INS,
+ MSG_KEYNAME_DEL, MSG_KEYNAME_MACRO, MSG_KEYNAME_MUTE,
+ MSG_KEYNAME_VOLDOWN, MSG_KEYNAME_VOLUP,
+ MSG_KEYNAME_POWER, MSG_KEYNAME_KPEQUAL, MSG_KEYNAME_KPPLUSDASH,
+ MSG_KEYNAME_PAUSE, MSG_KEYNAME_F21, MSG_KEYNAME_F22, MSG_KEYNAME_F23,
+ MSG_KEYNAME_F24, MSG_KEYNAME_KPCOMMA, MSG_KEYNAME_LEFTMETA,
+ MSG_KEYNAME_RIGHTMETA, MSG_KEYNAME_COMPOSE, MSG_KEYNAME_STOP,
+ MSG_KEYNAME_AGAIN, MSG_KEYNAME_PROPS,
+ MSG_KEYNAME_UNDO, MSG_KEYNAME_FRONT, MSG_KEYNAME_COPY, MSG_KEYNAME_OPEN,
+ MSG_KEYNAME_PASTE,
+ MSG_KEYNAME_FIND, MSG_KEYNAME_CUT, MSG_KEYNAME_HELP, MSG_KEYNAME_MENU,
+ MSG_KEYNAME_CALC,
+ MSG_KEYNAME_SETUP, MSG_KEYNAME_SLEEP, MSG_KEYNAME_WAKEUP,
+ MSG_KEYNAME_FILE, MSG_KEYNAME_SENDFILE,
+ MSG_KEYNAME_DELFILE, MSG_KEYNAME_XFER, MSG_KEYNAME_PROG1,
+ MSG_KEYNAME_PROG2, MSG_KEYNAME_WWW,
+ MSG_KEYNAME_MSDOS, MSG_KEYNAME_COFFEE, MSG_KEYNAME_DIRECTION,
+ MSG_KEYNAME_CYCLEWINDOWS, MSG_KEYNAME_MAIL,
+ MSG_KEYNAME_BOOKMARKS, MSG_KEYNAME_COMPUTER, MSG_KEYNAME_BACK,
+ MSG_KEYNAME_FORWARD, MSG_KEYNAME_CLOSECD,
+ MSG_KEYNAME_EJECTCD, MSG_KEYNAME_EJECTCLOSE, MSG_KEYNAME_NEXTSONG,
+ MSG_KEYNAME_PLAYPAUSE, MSG_KEYNAME_PREVSONG,
+ MSG_KEYNAME_STOPCD, MSG_KEYNAME_RECORD, MSG_KEYNAME_REWIND,
+ MSG_KEYNAME_PHONE, MSG_KEYNAME_ISO,
+ MSG_KEYNAME_CONFIG, MSG_KEYNAME_HOMEPG, MSG_KEYNAME_REFRESH,
+ MSG_KEYNAME_EXIT, MSG_KEYNAME_MOVE,
+ MSG_KEYNAME_EDIT, MSG_KEYNAME_SCROLLUP, MSG_KEYNAME_SCROLLDN,
+ MSG_KEYNAME_KPLEFTPAR, MSG_KEYNAME_KPRIGHTPAR,
+ MSG_KEYNAMES_END = MSG_KEYNAME_KPRIGHTPAR,
+
+ MSG_FUNCNAMES_START,
+ MSG_FUNCNAME_ATTRIB_BLEEP_DEC = MSG_FUNCNAMES_START,
+ MSG_FUNCNAME_ATTRIB_BLEEP_INC,
+ MSG_FUNCNAME_BLEEPS_DEC, MSG_FUNCNAME_BLEEPS_INC,
+ MSG_FUNCNAME_CHAR_FIRST, MSG_FUNCNAME_CHAR_LAST,
+ MSG_FUNCNAME_CHAR_CURRENT, MSG_FUNCNAME_CHAR_HEX_AND_DEC,
+ MSG_FUNCNAME_CHAR_NEXT,
+ MSG_FUNCNAME_CHAR_PHONETIC, MSG_FUNCNAME_CHAR_PREVIOUS,
+ MSG_FUNCNAME_CURSOR_PARK, MSG_FUNCNAME_CUT,
+ MSG_FUNCNAME_EDIT_DELIM, MSG_FUNCNAME_EDIT_EXNUM,
+ MSG_FUNCNAME_EDIT_MOST, MSG_FUNCNAME_EDIT_REPEATS,
+ MSG_FUNCNAME_EDIT_SOME,
+ MSG_FUNCNAME_GOTO, MSG_FUNCNAME_GOTO_BOTTOM, MSG_FUNCNAME_GOTO_LEFT,
+ MSG_FUNCNAME_GOTO_RIGHT, MSG_FUNCNAME_GOTO_TOP, MSG_FUNCNAME_HELP,
+ MSG_FUNCNAME_LINE_SAY_CURRENT, MSG_FUNCNAME_LINE_SAY_NEXT,
+ MSG_FUNCNAME_LINE_SAY_PREVIOUS, MSG_FUNCNAME_LINE_SAY_WITH_INDENT,
+ MSG_FUNCNAME_PASTE, MSG_FUNCNAME_PITCH_DEC, MSG_FUNCNAME_PITCH_INC,
+ MSG_FUNCNAME_PUNC_DEC, MSG_FUNCNAME_PUNC_INC,
+ MSG_FUNCNAME_PUNC_LEVEL_DEC, MSG_FUNCNAME_PUNC_LEVEL_INC,
+ MSG_FUNCNAME_QUIET,
+ MSG_FUNCNAME_RATE_DEC, MSG_FUNCNAME_RATE_INC,
+ MSG_FUNCNAME_READING_PUNC_DEC, MSG_FUNCNAME_READING_PUNC_INC,
+ MSG_FUNCNAME_SAY_ATTRIBUTES,
+ MSG_FUNCNAME_SAY_FROM_LEFT, MSG_FUNCNAME_SAY_FROM_TOP,
+ MSG_FUNCNAME_SAY_POSITION, MSG_FUNCNAME_SAY_SCREEN,
+ MSG_FUNCNAME_SAY_TO_BOTTOM, MSG_FUNCNAME_SAY_TO_RIGHT,
+ MSG_FUNCNAME_SPEAKUP, MSG_FUNCNAME_SPEAKUP_LOCK,
+ MSG_FUNCNAME_SPEAKUP_OFF, MSG_FUNCNAME_SPEECH_KILL,
+ MSG_FUNCNAME_SPELL_DELAY_DEC, MSG_FUNCNAME_SPELL_DELAY_INC,
+ MSG_FUNCNAME_SPELL_WORD, MSG_FUNCNAME_SPELL_WORD_PHONETICALLY,
+ MSG_FUNCNAME_TONE_DEC, MSG_FUNCNAME_TONE_INC,
+ MSG_FUNCNAME_VOICE_DEC, MSG_FUNCNAME_VOICE_INC,
+ MSG_FUNCNAME_VOLUME_DEC, MSG_FUNCNAME_VOLUME_INC,
+ MSG_FUNCNAME_WINDOW_CLEAR, MSG_FUNCNAME_WINDOW_SAY,
+ MSG_FUNCNAME_WINDOW_SET, MSG_FUNCNAME_WINDOW_SILENCE,
+ MSG_FUNCNAME_WORD_SAY_CURRENT, MSG_FUNCNAME_WORD_SAY_NEXT,
+ MSG_FUNCNAME_WORD_SAY_PREVIOUS,
+ MSG_FUNCNAMES_END = MSG_FUNCNAME_WORD_SAY_PREVIOUS,
+
+ /* all valid indices must be above this */
+ MSG_LAST_INDEX
+};
+
+struct msg_group_t {
+ char *name;
+ enum msg_index_t start;
+ enum msg_index_t end;
+};
+
+extern char *spk_msg_get(enum msg_index_t index);
+extern ssize_t spk_msg_set(enum msg_index_t index, char *text, size_t length);
+extern struct msg_group_t *spk_find_msg_group(const char *group_name);
+extern void spk_reset_msg_group(struct msg_group_t *group);
+extern void spk_initialize_msgs(void);
+extern void spk_free_user_msgs(void);
+
+#endif
diff --git a/drivers/staging/speakup/keyhelp.c b/drivers/staging/speakup/keyhelp.c
new file mode 100644
index 000000000..947567421
--- /dev/null
+++ b/drivers/staging/speakup/keyhelp.c
@@ -0,0 +1,224 @@
+/* speakup_keyhelp.c
+ * help module for speakup
+ *
+ *written by David Borowski.
+ *
+ * Copyright (C) 2003 David Borowski.
+ *
+ * This program is free software; you can redistribute it and/or 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/keyboard.h>
+#include "spk_priv.h"
+#include "speakup.h"
+
+#define MAXFUNCS 130
+#define MAXKEYS 256
+static const int num_key_names = MSG_KEYNAMES_END - MSG_KEYNAMES_START + 1;
+static u_short key_offsets[MAXFUNCS], key_data[MAXKEYS];
+static u_short masks[] = { 32, 16, 8, 4, 2, 1 };
+
+static short letter_offsets[26] = {
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1 };
+
+static u_char funcvals[] = {
+ ATTRIB_BLEEP_DEC, ATTRIB_BLEEP_INC, BLEEPS_DEC, BLEEPS_INC,
+ SAY_FIRST_CHAR, SAY_LAST_CHAR, SAY_CHAR, SAY_CHAR_NUM,
+ SAY_NEXT_CHAR, SAY_PHONETIC_CHAR, SAY_PREV_CHAR, SPEAKUP_PARKED,
+ SPEAKUP_CUT, EDIT_DELIM, EDIT_EXNUM, EDIT_MOST,
+ EDIT_REPEAT, EDIT_SOME, SPEAKUP_GOTO, BOTTOM_EDGE,
+ LEFT_EDGE, RIGHT_EDGE, TOP_EDGE, SPEAKUP_HELP,
+ SAY_LINE, SAY_NEXT_LINE, SAY_PREV_LINE, SAY_LINE_INDENT,
+ SPEAKUP_PASTE, PITCH_DEC, PITCH_INC, PUNCT_DEC,
+ PUNCT_INC, PUNC_LEVEL_DEC, PUNC_LEVEL_INC, SPEAKUP_QUIET,
+ RATE_DEC, RATE_INC, READING_PUNC_DEC, READING_PUNC_INC,
+ SAY_ATTRIBUTES, SAY_FROM_LEFT, SAY_FROM_TOP, SAY_POSITION,
+ SAY_SCREEN, SAY_TO_BOTTOM, SAY_TO_RIGHT, SPK_KEY,
+ SPK_LOCK, SPEAKUP_OFF, SPEECH_KILL, SPELL_DELAY_DEC,
+ SPELL_DELAY_INC, SPELL_WORD, SPELL_PHONETIC, TONE_DEC,
+ TONE_INC, VOICE_DEC, VOICE_INC, VOL_DEC,
+ VOL_INC, CLEAR_WIN, SAY_WIN, SET_WIN,
+ ENABLE_WIN, SAY_WORD, SAY_NEXT_WORD, SAY_PREV_WORD, 0
+};
+
+static u_char *state_tbl;
+static int cur_item, nstates;
+
+static void build_key_data(void)
+{
+ u_char *kp, counters[MAXFUNCS], ch, ch1;
+ u_short *p_key = key_data, key;
+ int i, offset = 1;
+
+ nstates = (int)(state_tbl[-1]);
+ memset(counters, 0, sizeof(counters));
+ memset(key_offsets, 0, sizeof(key_offsets));
+ kp = state_tbl + nstates + 1;
+ while (*kp++) {
+ /* count occurrences of each function */
+ for (i = 0; i < nstates; i++, kp++) {
+ if (!*kp)
+ continue;
+ if ((state_tbl[i]&16) != 0 && *kp == SPK_KEY)
+ continue;
+ counters[*kp]++;
+ }
+ }
+ for (i = 0; i < MAXFUNCS; i++) {
+ if (counters[i] == 0)
+ continue;
+ key_offsets[i] = offset;
+ offset += (counters[i]+1);
+ if (offset >= MAXKEYS)
+ break;
+ }
+/* leave counters set so high keycodes come first.
+ * this is done so num pad and other extended keys maps are spoken before
+ * the alpha with speakup type mapping.
+ */
+ kp = state_tbl + nstates + 1;
+ while ((ch = *kp++)) {
+ for (i = 0; i < nstates; i++) {
+ ch1 = *kp++;
+ if (!ch1)
+ continue;
+ if ((state_tbl[i]&16) != 0 && ch1 == SPK_KEY)
+ continue;
+ key = (state_tbl[i] << 8) + ch;
+ counters[ch1]--;
+ offset = key_offsets[ch1];
+ if (!offset)
+ continue;
+ p_key = key_data + offset + counters[ch1];
+ *p_key = key;
+ }
+ }
+}
+
+static void say_key(int key)
+{
+ int i, state = key >> 8;
+
+ key &= 0xff;
+ for (i = 0; i < 6; i++) {
+ if (state & masks[i])
+ synth_printf(" %s", spk_msg_get(MSG_STATES_START + i));
+ }
+ if ((key > 0) && (key <= num_key_names))
+ synth_printf(" %s\n",
+ spk_msg_get(MSG_KEYNAMES_START + (key - 1)));
+}
+
+static int help_init(void)
+{
+ char start = SPACE;
+ int i;
+ int num_funcs = MSG_FUNCNAMES_END - MSG_FUNCNAMES_START + 1;
+
+ state_tbl = spk_our_keys[0]+SHIFT_TBL_SIZE+2;
+ for (i = 0; i < num_funcs; i++) {
+ char *cur_funcname = spk_msg_get(MSG_FUNCNAMES_START + i);
+
+ if (start == *cur_funcname)
+ continue;
+ start = *cur_funcname;
+ letter_offsets[(start&31)-1] = i;
+ }
+ return 0;
+}
+
+int spk_handle_help(struct vc_data *vc, u_char type, u_char ch, u_short key)
+{
+ int i, n;
+ char *name;
+ u_char func, *kp;
+ u_short *p_keys, val;
+
+ if (letter_offsets[0] == -1)
+ help_init();
+ if (type == KT_LATIN) {
+ if (ch == SPACE) {
+ spk_special_handler = NULL;
+ synth_printf("%s\n", spk_msg_get(MSG_LEAVING_HELP));
+ return 1;
+ }
+ ch |= 32; /* lower case */
+ if (ch < 'a' || ch > 'z')
+ return -1;
+ if (letter_offsets[ch-'a'] == -1) {
+ synth_printf(spk_msg_get(MSG_NO_COMMAND), ch);
+ synth_printf("\n");
+ return 1;
+ }
+ cur_item = letter_offsets[ch-'a'];
+ } else if (type == KT_CUR) {
+ if (ch == 0
+ && (MSG_FUNCNAMES_START + cur_item + 1) <=
+ MSG_FUNCNAMES_END)
+ cur_item++;
+ else if (ch == 3 && cur_item > 0)
+ cur_item--;
+ else
+ return -1;
+ } else if (type == KT_SPKUP
+ && ch == SPEAKUP_HELP
+ && !spk_special_handler) {
+ spk_special_handler = spk_handle_help;
+ synth_printf("%s\n", spk_msg_get(MSG_HELP_INFO));
+ build_key_data(); /* rebuild each time in case new mapping */
+ return 1;
+ } else {
+ name = NULL;
+ if ((type != KT_SPKUP) && (key > 0) && (key <= num_key_names)) {
+ synth_printf("%s\n",
+ spk_msg_get(MSG_KEYNAMES_START + key-1));
+ return 1;
+ }
+ for (i = 0; funcvals[i] != 0 && !name; i++) {
+ if (ch == funcvals[i])
+ name = spk_msg_get(MSG_FUNCNAMES_START + i);
+ }
+ if (!name)
+ return -1;
+ kp = spk_our_keys[key]+1;
+ for (i = 0; i < nstates; i++) {
+ if (ch == kp[i])
+ break;
+ }
+ key += (state_tbl[i] << 8);
+ say_key(key);
+ synth_printf(spk_msg_get(MSG_KEYDESC), name);
+ synth_printf("\n");
+ return 1;
+ }
+ name = spk_msg_get(MSG_FUNCNAMES_START + cur_item);
+ func = funcvals[cur_item];
+ synth_printf("%s", name);
+ if (key_offsets[func] == 0) {
+ synth_printf(" %s\n", spk_msg_get(MSG_IS_UNASSIGNED));
+ return 1;
+ }
+ p_keys = key_data + key_offsets[func];
+ for (n = 0; p_keys[n]; n++) {
+ val = p_keys[n];
+ if (n > 0)
+ synth_printf("%s ", spk_msg_get(MSG_DISJUNCTION));
+ say_key(val);
+ }
+ return 1;
+}
diff --git a/drivers/staging/speakup/kobjects.c b/drivers/staging/speakup/kobjects.c
new file mode 100644
index 000000000..0211df600
--- /dev/null
+++ b/drivers/staging/speakup/kobjects.c
@@ -0,0 +1,1039 @@
+/*
+ * Speakup kobject implementation
+ *
+ * Copyright (C) 2009 William Hubbs
+ *
+ * This code is based on kobject-example.c, which came with linux 2.6.x.
+ *
+ * Copyright (C) 2004-2007 Greg Kroah-Hartman <greg@kroah.com>
+ * Copyright (C) 2007 Novell Inc.
+ *
+ * Released under the GPL version 2 only.
+ *
+ */
+#include <linux/slab.h> /* For kmalloc. */
+#include <linux/kernel.h>
+#include <linux/kobject.h>
+#include <linux/string.h>
+#include <linux/string_helpers.h>
+#include <linux/sysfs.h>
+#include <linux/ctype.h>
+
+#include "speakup.h"
+#include "spk_priv.h"
+
+/*
+ * This is called when a user reads the characters or chartab sys file.
+ */
+static ssize_t chars_chartab_show(struct kobject *kobj,
+ struct kobj_attribute *attr, char *buf)
+{
+ int i;
+ int len = 0;
+ char *cp;
+ char *buf_pointer = buf;
+ size_t bufsize = PAGE_SIZE;
+ unsigned long flags;
+
+ spin_lock_irqsave(&speakup_info.spinlock, flags);
+ *buf_pointer = '\0';
+ for (i = 0; i < 256; i++) {
+ if (bufsize <= 1)
+ break;
+ if (strcmp("characters", attr->attr.name) == 0) {
+ len = scnprintf(buf_pointer, bufsize, "%d\t%s\n",
+ i, spk_characters[i]);
+ } else { /* show chartab entry */
+ if (IS_TYPE(i, B_CTL))
+ cp = "B_CTL";
+ else if (IS_TYPE(i, WDLM))
+ cp = "WDLM";
+ else if (IS_TYPE(i, A_PUNC))
+ cp = "A_PUNC";
+ else if (IS_TYPE(i, PUNC))
+ cp = "PUNC";
+ else if (IS_TYPE(i, NUM))
+ cp = "NUM";
+ else if (IS_TYPE(i, A_CAP))
+ cp = "A_CAP";
+ else if (IS_TYPE(i, ALPHA))
+ cp = "ALPHA";
+ else if (IS_TYPE(i, B_CAPSYM))
+ cp = "B_CAPSYM";
+ else if (IS_TYPE(i, B_SYM))
+ cp = "B_SYM";
+ else
+ cp = "0";
+ len =
+ scnprintf(buf_pointer, bufsize, "%d\t%s\n", i, cp);
+ }
+ bufsize -= len;
+ buf_pointer += len;
+ }
+ spin_unlock_irqrestore(&speakup_info.spinlock, flags);
+ return buf_pointer - buf;
+}
+
+/*
+ * Print informational messages or warnings after updating
+ * character descriptions or chartab entries.
+ */
+static void report_char_chartab_status(int reset, int received, int used,
+ int rejected, int do_characters)
+{
+ static char const *object_type[] = {
+ "character class entries",
+ "character descriptions",
+ };
+ int len;
+ char buf[80];
+
+ if (reset) {
+ pr_info("%s reset to defaults\n", object_type[do_characters]);
+ } else if (received) {
+ len = snprintf(buf, sizeof(buf),
+ " updated %d of %d %s\n",
+ used, received, object_type[do_characters]);
+ if (rejected)
+ snprintf(buf + (len - 1), sizeof(buf) - (len - 1),
+ " with %d reject%s\n",
+ rejected, rejected > 1 ? "s" : "");
+ printk(buf);
+ }
+}
+
+/*
+ * This is called when a user changes the characters or chartab parameters.
+ */
+static ssize_t chars_chartab_store(struct kobject *kobj,
+ struct kobj_attribute *attr, const char *buf, size_t count)
+{
+ char *cp = (char *) buf;
+ char *end = cp + count; /* the null at the end of the buffer */
+ char *linefeed = NULL;
+ char keyword[MAX_DESC_LEN + 1];
+ char *outptr = NULL; /* Will hold keyword or desc. */
+ char *temp = NULL;
+ char *desc = NULL;
+ ssize_t retval = count;
+ unsigned long flags;
+ unsigned long index = 0;
+ int charclass = 0;
+ int received = 0;
+ int used = 0;
+ int rejected = 0;
+ int reset = 0;
+ int do_characters = !strcmp(attr->attr.name, "characters");
+ size_t desc_length = 0;
+ int i;
+
+ spin_lock_irqsave(&speakup_info.spinlock, flags);
+ while (cp < end) {
+
+ while ((cp < end) && (*cp == ' ' || *cp == '\t'))
+ cp++;
+
+ if (cp == end)
+ break;
+ if ((*cp == '\n') || strchr("dDrR", *cp)) {
+ reset = 1;
+ break;
+ }
+ received++;
+
+ linefeed = strchr(cp, '\n');
+ if (!linefeed) {
+ rejected++;
+ break;
+ }
+
+ if (!isdigit(*cp)) {
+ rejected++;
+ cp = linefeed + 1;
+ continue;
+ }
+
+ index = simple_strtoul(cp, &temp, 10);
+ if (index > 255) {
+ rejected++;
+ cp = linefeed + 1;
+ continue;
+ }
+
+ while ((temp < linefeed) && (*temp == ' ' || *temp == '\t'))
+ temp++;
+
+ desc_length = linefeed - temp;
+ if (desc_length > MAX_DESC_LEN) {
+ rejected++;
+ cp = linefeed + 1;
+ continue;
+ }
+ if (do_characters) {
+ desc = kmalloc(desc_length + 1, GFP_ATOMIC);
+ if (!desc) {
+ retval = -ENOMEM;
+ reset = 1; /* just reset on error. */
+ break;
+ }
+ outptr = desc;
+ } else {
+ outptr = keyword;
+ }
+
+ for (i = 0; i < desc_length; i++)
+ outptr[i] = temp[i];
+ outptr[desc_length] = '\0';
+
+ if (do_characters) {
+ if (spk_characters[index] != spk_default_chars[index])
+ kfree(spk_characters[index]);
+ spk_characters[index] = desc;
+ used++;
+ } else {
+ charclass = spk_chartab_get_value(keyword);
+ if (charclass == 0) {
+ rejected++;
+ cp = linefeed + 1;
+ continue;
+ }
+ if (charclass != spk_chartab[index]) {
+ spk_chartab[index] = charclass;
+ used++;
+ }
+ }
+ cp = linefeed + 1;
+ }
+
+ if (reset) {
+ if (do_characters)
+ spk_reset_default_chars();
+ else
+ spk_reset_default_chartab();
+ }
+
+ spin_unlock_irqrestore(&speakup_info.spinlock, flags);
+ report_char_chartab_status(reset, received, used, rejected,
+ do_characters);
+ return retval;
+}
+
+/*
+ * This is called when a user reads the keymap parameter.
+ */
+static ssize_t keymap_show(struct kobject *kobj, struct kobj_attribute *attr,
+ char *buf)
+{
+ char *cp = buf;
+ int i;
+ int n;
+ int num_keys;
+ int nstates;
+ u_char *cp1;
+ u_char ch;
+ unsigned long flags;
+
+ spin_lock_irqsave(&speakup_info.spinlock, flags);
+ cp1 = spk_key_buf + SHIFT_TBL_SIZE;
+ num_keys = (int)(*cp1);
+ nstates = (int)cp1[1];
+ cp += sprintf(cp, "%d, %d, %d,\n", KEY_MAP_VER, num_keys, nstates);
+ cp1 += 2; /* now pointing at shift states */
+ /* dump num_keys+1 as first row is shift states + flags,
+ * each subsequent row is key + states */
+ for (n = 0; n <= num_keys; n++) {
+ for (i = 0; i <= nstates; i++) {
+ ch = *cp1++;
+ cp += sprintf(cp, "%d,", (int)ch);
+ *cp++ = (i < nstates) ? SPACE : '\n';
+ }
+ }
+ cp += sprintf(cp, "0, %d\n", KEY_MAP_VER);
+ spin_unlock_irqrestore(&speakup_info.spinlock, flags);
+ return (int)(cp-buf);
+}
+
+/*
+ * This is called when a user changes the keymap parameter.
+ */
+static ssize_t keymap_store(struct kobject *kobj, struct kobj_attribute *attr,
+ const char *buf, size_t count)
+{
+ int i;
+ ssize_t ret = count;
+ char *in_buff = NULL;
+ char *cp;
+ u_char *cp1;
+ unsigned long flags;
+
+ spin_lock_irqsave(&speakup_info.spinlock, flags);
+ in_buff = kmemdup(buf, count + 1, GFP_ATOMIC);
+ if (!in_buff) {
+ spin_unlock_irqrestore(&speakup_info.spinlock, flags);
+ return -ENOMEM;
+ }
+ if (strchr("dDrR", *in_buff)) {
+ spk_set_key_info(spk_key_defaults, spk_key_buf);
+ pr_info("keymap set to default values\n");
+ kfree(in_buff);
+ spin_unlock_irqrestore(&speakup_info.spinlock, flags);
+ return count;
+ }
+ if (in_buff[count - 1] == '\n')
+ in_buff[count - 1] = '\0';
+ cp = in_buff;
+ cp1 = (u_char *)in_buff;
+ for (i = 0; i < 3; i++) {
+ cp = spk_s2uchar(cp, cp1);
+ cp1++;
+ }
+ i = (int)cp1[-2]+1;
+ i *= (int)cp1[-1]+1;
+ i += 2; /* 0 and last map ver */
+ if (cp1[-3] != KEY_MAP_VER || cp1[-1] > 10 ||
+ i+SHIFT_TBL_SIZE+4 >= sizeof(spk_key_buf)) {
+ pr_warn("i %d %d %d %d\n", i,
+ (int)cp1[-3], (int)cp1[-2], (int)cp1[-1]);
+ kfree(in_buff);
+ spin_unlock_irqrestore(&speakup_info.spinlock, flags);
+ return -EINVAL;
+ }
+ while (--i >= 0) {
+ cp = spk_s2uchar(cp, cp1);
+ cp1++;
+ if (!(*cp))
+ break;
+ }
+ if (i != 0 || cp1[-1] != KEY_MAP_VER || cp1[-2] != 0) {
+ ret = -EINVAL;
+ pr_warn("end %d %d %d %d\n", i,
+ (int)cp1[-3], (int)cp1[-2], (int)cp1[-1]);
+ } else {
+ if (spk_set_key_info(in_buff, spk_key_buf)) {
+ spk_set_key_info(spk_key_defaults, spk_key_buf);
+ ret = -EINVAL;
+ pr_warn("set key failed\n");
+ }
+ }
+ kfree(in_buff);
+ spin_unlock_irqrestore(&speakup_info.spinlock, flags);
+ return ret;
+}
+
+/*
+ * This is called when a user changes the value of the silent parameter.
+ */
+static ssize_t silent_store(struct kobject *kobj, struct kobj_attribute *attr,
+ const char *buf, size_t count)
+{
+ int len;
+ struct vc_data *vc = vc_cons[fg_console].d;
+ char ch = 0;
+ char shut;
+ unsigned long flags;
+
+ len = strlen(buf);
+ if (len > 0 && len < 3) {
+ ch = buf[0];
+ if (ch == '\n')
+ ch = '0';
+ }
+ if (ch < '0' || ch > '7') {
+ pr_warn("silent value '%c' not in range (0,7)\n", ch);
+ return -EINVAL;
+ }
+ spin_lock_irqsave(&speakup_info.spinlock, flags);
+ if (ch&2) {
+ shut = 1;
+ spk_do_flush();
+ } else {
+ shut = 0;
+ }
+ if (ch&4)
+ shut |= 0x40;
+ if (ch&1)
+ spk_shut_up |= shut;
+ else
+ spk_shut_up &= ~shut;
+ spin_unlock_irqrestore(&speakup_info.spinlock, flags);
+ return count;
+}
+
+/*
+ * This is called when a user reads the synth setting.
+ */
+static ssize_t synth_show(struct kobject *kobj, struct kobj_attribute *attr,
+ char *buf)
+{
+ int rv;
+
+ if (synth == NULL)
+ rv = sprintf(buf, "%s\n", "none");
+ else
+ rv = sprintf(buf, "%s\n", synth->name);
+ return rv;
+}
+
+/*
+ * This is called when a user requests to change synthesizers.
+ */
+static ssize_t synth_store(struct kobject *kobj, struct kobj_attribute *attr,
+ const char *buf, size_t count)
+{
+ int len;
+ char new_synth_name[10];
+
+ len = strlen(buf);
+ if (len < 2 || len > 9)
+ return -EINVAL;
+ strncpy(new_synth_name, buf, len);
+ if (new_synth_name[len - 1] == '\n')
+ len--;
+ new_synth_name[len] = '\0';
+ spk_strlwr(new_synth_name);
+ if ((synth != NULL) && (!strcmp(new_synth_name, synth->name))) {
+ pr_warn("%s already in use\n", new_synth_name);
+ } else if (synth_init(new_synth_name) != 0) {
+ pr_warn("failed to init synth %s\n", new_synth_name);
+ return -ENODEV;
+ }
+ return count;
+}
+
+/*
+ * This is called when text is sent to the synth via the synth_direct file.
+ */
+static ssize_t synth_direct_store(struct kobject *kobj,
+ struct kobj_attribute *attr, const char *buf, size_t count)
+{
+ u_char tmp[256];
+ int len;
+ int bytes;
+ const char *ptr = buf;
+
+ if (!synth)
+ return -EPERM;
+
+ len = strlen(buf);
+ while (len > 0) {
+ bytes = min_t(size_t, len, 250);
+ strncpy(tmp, ptr, bytes);
+ tmp[bytes] = '\0';
+ string_unescape_any_inplace(tmp);
+ synth_printf("%s", tmp);
+ ptr += bytes;
+ len -= bytes;
+ }
+ return count;
+}
+
+/*
+ * This function is called when a user reads the version.
+ */
+static ssize_t version_show(struct kobject *kobj, struct kobj_attribute *attr,
+ char *buf)
+{
+ char *cp;
+
+ cp = buf;
+ cp += sprintf(cp, "Speakup version %s\n", SPEAKUP_VERSION);
+ if (synth)
+ cp += sprintf(cp, "%s synthesizer driver version %s\n",
+ synth->name, synth->version);
+ return cp - buf;
+}
+
+/*
+ * This is called when a user reads the punctuation settings.
+ */
+static ssize_t punc_show(struct kobject *kobj, struct kobj_attribute *attr,
+ char *buf)
+{
+ int i;
+ char *cp = buf;
+ struct st_var_header *p_header;
+ struct punc_var_t *var;
+ struct st_bits_data *pb;
+ short mask;
+ unsigned long flags;
+
+ p_header = spk_var_header_by_name(attr->attr.name);
+ if (p_header == NULL) {
+ pr_warn("p_header is null, attr->attr.name is %s\n",
+ attr->attr.name);
+ return -EINVAL;
+ }
+
+ var = spk_get_punc_var(p_header->var_id);
+ if (var == NULL) {
+ pr_warn("var is null, p_header->var_id is %i\n",
+ p_header->var_id);
+ return -EINVAL;
+ }
+
+ spin_lock_irqsave(&speakup_info.spinlock, flags);
+ pb = (struct st_bits_data *) &spk_punc_info[var->value];
+ mask = pb->mask;
+ for (i = 33; i < 128; i++) {
+ if (!(spk_chartab[i]&mask))
+ continue;
+ *cp++ = (char)i;
+ }
+ spin_unlock_irqrestore(&speakup_info.spinlock, flags);
+ return cp-buf;
+}
+
+/*
+ * This is called when a user changes the punctuation settings.
+ */
+static ssize_t punc_store(struct kobject *kobj, struct kobj_attribute *attr,
+ const char *buf, size_t count)
+{
+ int x;
+ struct st_var_header *p_header;
+ struct punc_var_t *var;
+ char punc_buf[100];
+ unsigned long flags;
+
+ x = strlen(buf);
+ if (x < 1 || x > 99)
+ return -EINVAL;
+
+ p_header = spk_var_header_by_name(attr->attr.name);
+ if (p_header == NULL) {
+ pr_warn("p_header is null, attr->attr.name is %s\n",
+ attr->attr.name);
+ return -EINVAL;
+ }
+
+ var = spk_get_punc_var(p_header->var_id);
+ if (var == NULL) {
+ pr_warn("var is null, p_header->var_id is %i\n",
+ p_header->var_id);
+ return -EINVAL;
+ }
+
+ strncpy(punc_buf, buf, x);
+
+ while (x && punc_buf[x - 1] == '\n')
+ x--;
+ punc_buf[x] = '\0';
+
+ spin_lock_irqsave(&speakup_info.spinlock, flags);
+
+ if (*punc_buf == 'd' || *punc_buf == 'r')
+ x = spk_set_mask_bits(NULL, var->value, 3);
+ else
+ x = spk_set_mask_bits(punc_buf, var->value, 3);
+
+ spin_unlock_irqrestore(&speakup_info.spinlock, flags);
+ return count;
+}
+
+/*
+ * This function is called when a user reads one of the variable parameters.
+ */
+ssize_t spk_var_show(struct kobject *kobj, struct kobj_attribute *attr,
+ char *buf)
+{
+ int rv = 0;
+ struct st_var_header *param;
+ struct var_t *var;
+ char *cp1;
+ char *cp;
+ char ch;
+ unsigned long flags;
+
+ param = spk_var_header_by_name(attr->attr.name);
+ if (param == NULL)
+ return -EINVAL;
+
+ spin_lock_irqsave(&speakup_info.spinlock, flags);
+ var = (struct var_t *) param->data;
+ switch (param->var_type) {
+ case VAR_NUM:
+ case VAR_TIME:
+ if (var)
+ rv = sprintf(buf, "%i\n", var->u.n.value);
+ else
+ rv = sprintf(buf, "0\n");
+ break;
+ case VAR_STRING:
+ if (var) {
+ cp1 = buf;
+ *cp1++ = '"';
+ for (cp = (char *)param->p_val; (ch = *cp); cp++) {
+ if (ch >= ' ' && ch < '~')
+ *cp1++ = ch;
+ else
+ cp1 += sprintf(cp1, "\\x%02x", ch);
+ }
+ *cp1++ = '"';
+ *cp1++ = '\n';
+ *cp1 = '\0';
+ rv = cp1-buf;
+ } else {
+ rv = sprintf(buf, "\"\"\n");
+ }
+ break;
+ default:
+ rv = sprintf(buf, "Bad parameter %s, type %i\n",
+ param->name, param->var_type);
+ break;
+ }
+ spin_unlock_irqrestore(&speakup_info.spinlock, flags);
+ return rv;
+}
+EXPORT_SYMBOL_GPL(spk_var_show);
+
+/*
+ * Used to reset either default_pitch or default_vol.
+ */
+static inline void spk_reset_default_value(char *header_name,
+ int *synth_default_value, int idx)
+{
+ struct st_var_header *param;
+
+ if (synth && synth_default_value) {
+ param = spk_var_header_by_name(header_name);
+ if (param) {
+ spk_set_num_var(synth_default_value[idx],
+ param, E_NEW_DEFAULT);
+ spk_set_num_var(0, param, E_DEFAULT);
+ pr_info("%s reset to default value\n", param->name);
+ }
+ }
+}
+
+/*
+ * This function is called when a user echos a value to one of the
+ * variable parameters.
+ */
+ssize_t spk_var_store(struct kobject *kobj, struct kobj_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct st_var_header *param;
+ int ret;
+ int len;
+ char *cp;
+ struct var_t *var_data;
+ long value;
+ unsigned long flags;
+
+ param = spk_var_header_by_name(attr->attr.name);
+ if (param == NULL)
+ return -EINVAL;
+ if (param->data == NULL)
+ return 0;
+ ret = 0;
+ cp = (char *)buf;
+ string_unescape_any_inplace(cp);
+
+ spin_lock_irqsave(&speakup_info.spinlock, flags);
+ switch (param->var_type) {
+ case VAR_NUM:
+ case VAR_TIME:
+ if (*cp == 'd' || *cp == 'r' || *cp == '\0')
+ len = E_DEFAULT;
+ else if (*cp == '+' || *cp == '-')
+ len = E_INC;
+ else
+ len = E_SET;
+ if (kstrtol(cp, 10, &value) == 0)
+ ret = spk_set_num_var(value, param, len);
+ else
+ pr_warn("overflow or parsing error has occurred");
+ if (ret == -ERANGE) {
+ var_data = param->data;
+ pr_warn("value for %s out of range, expect %d to %d\n",
+ param->name,
+ var_data->u.n.low, var_data->u.n.high);
+ }
+
+ /*
+ * If voice was just changed, we might need to reset our default
+ * pitch and volume.
+ */
+ if (param->var_id == VOICE && synth &&
+ (ret == 0 || ret == -ERESTART)) {
+ var_data = param->data;
+ value = var_data->u.n.value;
+ spk_reset_default_value("pitch", synth->default_pitch,
+ value);
+ spk_reset_default_value("vol", synth->default_vol,
+ value);
+ }
+ break;
+ case VAR_STRING:
+ len = strlen(cp);
+ if ((len >= 1) && (cp[len - 1] == '\n'))
+ --len;
+ if ((len >= 2) && (cp[0] == '"') && (cp[len - 1] == '"')) {
+ ++cp;
+ len -= 2;
+ }
+ cp[len] = '\0';
+ ret = spk_set_string_var(cp, param, len);
+ if (ret == -E2BIG)
+ pr_warn("value too long for %s\n",
+ param->name);
+ break;
+ default:
+ pr_warn("%s unknown type %d\n",
+ param->name, (int)param->var_type);
+ break;
+ }
+ spin_unlock_irqrestore(&speakup_info.spinlock, flags);
+
+ if (ret == -ERESTART)
+ pr_info("%s reset to default value\n", param->name);
+ return count;
+}
+EXPORT_SYMBOL_GPL(spk_var_store);
+
+/*
+ * Functions for reading and writing lists of i18n messages. Incomplete.
+ */
+
+static ssize_t message_show_helper(char *buf, enum msg_index_t first,
+ enum msg_index_t last)
+{
+ size_t bufsize = PAGE_SIZE;
+ char *buf_pointer = buf;
+ int printed;
+ enum msg_index_t cursor;
+ int index = 0;
+ *buf_pointer = '\0'; /* buf_pointer always looking at a NUL byte. */
+
+ for (cursor = first; cursor <= last; cursor++, index++) {
+ if (bufsize <= 1)
+ break;
+ printed = scnprintf(buf_pointer, bufsize, "%d\t%s\n",
+ index, spk_msg_get(cursor));
+ buf_pointer += printed;
+ bufsize -= printed;
+ }
+
+ return buf_pointer - buf;
+}
+
+static void report_msg_status(int reset, int received, int used,
+ int rejected, char *groupname)
+{
+ int len;
+ char buf[160];
+
+ if (reset) {
+ pr_info("i18n messages from group %s reset to defaults\n",
+ groupname);
+ } else if (received) {
+ len = snprintf(buf, sizeof(buf),
+ " updated %d of %d i18n messages from group %s\n",
+ used, received, groupname);
+ if (rejected)
+ snprintf(buf + (len - 1), sizeof(buf) - (len - 1),
+ " with %d reject%s\n",
+ rejected, rejected > 1 ? "s" : "");
+ printk(buf);
+ }
+}
+
+static ssize_t message_store_helper(const char *buf, size_t count,
+ struct msg_group_t *group)
+{
+ char *cp = (char *) buf;
+ char *end = cp + count;
+ char *linefeed = NULL;
+ char *temp = NULL;
+ ssize_t msg_stored = 0;
+ ssize_t retval = count;
+ size_t desc_length = 0;
+ unsigned long index = 0;
+ int received = 0;
+ int used = 0;
+ int rejected = 0;
+ int reset = 0;
+ enum msg_index_t firstmessage = group->start;
+ enum msg_index_t lastmessage = group->end;
+ enum msg_index_t curmessage;
+
+ while (cp < end) {
+
+ while ((cp < end) && (*cp == ' ' || *cp == '\t'))
+ cp++;
+
+ if (cp == end)
+ break;
+ if (strchr("dDrR", *cp)) {
+ reset = 1;
+ break;
+ }
+ received++;
+
+ linefeed = strchr(cp, '\n');
+ if (!linefeed) {
+ rejected++;
+ break;
+ }
+
+ if (!isdigit(*cp)) {
+ rejected++;
+ cp = linefeed + 1;
+ continue;
+ }
+
+ index = simple_strtoul(cp, &temp, 10);
+
+ while ((temp < linefeed) && (*temp == ' ' || *temp == '\t'))
+ temp++;
+
+ desc_length = linefeed - temp;
+ curmessage = firstmessage + index;
+
+ /*
+ * Note the check (curmessage < firstmessage). It is not
+ * redundant. Suppose that the user gave us an index
+ * equal to ULONG_MAX - 1. If firstmessage > 1, then
+ * firstmessage + index < firstmessage!
+ */
+
+ if ((curmessage < firstmessage) || (curmessage > lastmessage)) {
+ rejected++;
+ cp = linefeed + 1;
+ continue;
+ }
+
+ msg_stored = spk_msg_set(curmessage, temp, desc_length);
+ if (msg_stored < 0) {
+ retval = msg_stored;
+ if (msg_stored == -ENOMEM)
+ reset = 1;
+ break;
+ }
+
+ used++;
+
+ cp = linefeed + 1;
+ }
+
+ if (reset)
+ spk_reset_msg_group(group);
+
+ report_msg_status(reset, received, used, rejected, group->name);
+ return retval;
+}
+
+static ssize_t message_show(struct kobject *kobj,
+ struct kobj_attribute *attr, char *buf)
+{
+ ssize_t retval = 0;
+ struct msg_group_t *group = spk_find_msg_group(attr->attr.name);
+ unsigned long flags;
+
+ BUG_ON(!group);
+ spin_lock_irqsave(&speakup_info.spinlock, flags);
+ retval = message_show_helper(buf, group->start, group->end);
+ spin_unlock_irqrestore(&speakup_info.spinlock, flags);
+ return retval;
+}
+
+static ssize_t message_store(struct kobject *kobj, struct kobj_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct msg_group_t *group = spk_find_msg_group(attr->attr.name);
+
+ BUG_ON(!group);
+ return message_store_helper(buf, count, group);
+}
+
+/*
+ * Declare the attributes.
+ */
+static struct kobj_attribute keymap_attribute =
+ __ATTR_RW(keymap);
+static struct kobj_attribute silent_attribute =
+ __ATTR_WO(silent);
+static struct kobj_attribute synth_attribute =
+ __ATTR_RW(synth);
+static struct kobj_attribute synth_direct_attribute =
+ __ATTR_WO(synth_direct);
+static struct kobj_attribute version_attribute =
+ __ATTR_RO(version);
+
+static struct kobj_attribute delimiters_attribute =
+ __ATTR(delimiters, S_IWUSR|S_IRUGO, punc_show, punc_store);
+static struct kobj_attribute ex_num_attribute =
+ __ATTR(ex_num, S_IWUSR|S_IRUGO, punc_show, punc_store);
+static struct kobj_attribute punc_all_attribute =
+ __ATTR(punc_all, S_IWUSR|S_IRUGO, punc_show, punc_store);
+static struct kobj_attribute punc_most_attribute =
+ __ATTR(punc_most, S_IWUSR|S_IRUGO, punc_show, punc_store);
+static struct kobj_attribute punc_some_attribute =
+ __ATTR(punc_some, S_IWUSR|S_IRUGO, punc_show, punc_store);
+static struct kobj_attribute repeats_attribute =
+ __ATTR(repeats, S_IWUSR|S_IRUGO, punc_show, punc_store);
+
+static struct kobj_attribute attrib_bleep_attribute =
+ __ATTR(attrib_bleep, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
+static struct kobj_attribute bell_pos_attribute =
+ __ATTR(bell_pos, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
+static struct kobj_attribute bleep_time_attribute =
+ __ATTR(bleep_time, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
+static struct kobj_attribute bleeps_attribute =
+ __ATTR(bleeps, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
+static struct kobj_attribute cursor_time_attribute =
+ __ATTR(cursor_time, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
+static struct kobj_attribute key_echo_attribute =
+ __ATTR(key_echo, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
+static struct kobj_attribute no_interrupt_attribute =
+ __ATTR(no_interrupt, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
+static struct kobj_attribute punc_level_attribute =
+ __ATTR(punc_level, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
+static struct kobj_attribute reading_punc_attribute =
+ __ATTR(reading_punc, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
+static struct kobj_attribute say_control_attribute =
+ __ATTR(say_control, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
+static struct kobj_attribute say_word_ctl_attribute =
+ __ATTR(say_word_ctl, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
+static struct kobj_attribute spell_delay_attribute =
+ __ATTR(spell_delay, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
+
+/*
+ * These attributes are i18n related.
+ */
+static struct kobj_attribute announcements_attribute =
+ __ATTR(announcements, S_IWUSR|S_IRUGO, message_show, message_store);
+static struct kobj_attribute characters_attribute =
+ __ATTR(characters, S_IWUSR|S_IRUGO, chars_chartab_show,
+ chars_chartab_store);
+static struct kobj_attribute chartab_attribute =
+ __ATTR(chartab, S_IWUSR|S_IRUGO, chars_chartab_show,
+ chars_chartab_store);
+static struct kobj_attribute ctl_keys_attribute =
+ __ATTR(ctl_keys, S_IWUSR|S_IRUGO, message_show, message_store);
+static struct kobj_attribute colors_attribute =
+ __ATTR(colors, S_IWUSR|S_IRUGO, message_show, message_store);
+static struct kobj_attribute formatted_attribute =
+ __ATTR(formatted, S_IWUSR|S_IRUGO, message_show, message_store);
+static struct kobj_attribute function_names_attribute =
+ __ATTR(function_names, S_IWUSR|S_IRUGO, message_show, message_store);
+static struct kobj_attribute key_names_attribute =
+ __ATTR(key_names, S_IWUSR|S_IRUGO, message_show, message_store);
+static struct kobj_attribute states_attribute =
+ __ATTR(states, S_IWUSR|S_IRUGO, message_show, message_store);
+
+/*
+ * Create groups of attributes so that we can create and destroy them all
+ * at once.
+ */
+static struct attribute *main_attrs[] = {
+ &keymap_attribute.attr,
+ &silent_attribute.attr,
+ &synth_attribute.attr,
+ &synth_direct_attribute.attr,
+ &version_attribute.attr,
+ &delimiters_attribute.attr,
+ &ex_num_attribute.attr,
+ &punc_all_attribute.attr,
+ &punc_most_attribute.attr,
+ &punc_some_attribute.attr,
+ &repeats_attribute.attr,
+ &attrib_bleep_attribute.attr,
+ &bell_pos_attribute.attr,
+ &bleep_time_attribute.attr,
+ &bleeps_attribute.attr,
+ &cursor_time_attribute.attr,
+ &key_echo_attribute.attr,
+ &no_interrupt_attribute.attr,
+ &punc_level_attribute.attr,
+ &reading_punc_attribute.attr,
+ &say_control_attribute.attr,
+ &say_word_ctl_attribute.attr,
+ &spell_delay_attribute.attr,
+ NULL,
+};
+
+static struct attribute *i18n_attrs[] = {
+ &announcements_attribute.attr,
+ &characters_attribute.attr,
+ &chartab_attribute.attr,
+ &ctl_keys_attribute.attr,
+ &colors_attribute.attr,
+ &formatted_attribute.attr,
+ &function_names_attribute.attr,
+ &key_names_attribute.attr,
+ &states_attribute.attr,
+ NULL,
+};
+
+/*
+ * An unnamed attribute group will put all of the attributes directly in
+ * the kobject directory. If we specify a name, a subdirectory will be
+ * created for the attributes with the directory being the name of the
+ * attribute group.
+ */
+static struct attribute_group main_attr_group = {
+ .attrs = main_attrs,
+};
+
+static struct attribute_group i18n_attr_group = {
+ .attrs = i18n_attrs,
+ .name = "i18n",
+};
+
+static struct kobject *accessibility_kobj;
+struct kobject *speakup_kobj;
+
+int speakup_kobj_init(void)
+{
+ int retval;
+
+ /*
+ * Create a simple kobject with the name of "accessibility",
+ * located under /sys/
+ *
+ * As this is a simple directory, no uevent will be sent to
+ * userspace. That is why this function should not be used for
+ * any type of dynamic kobjects, where the name and number are
+ * not known ahead of time.
+ */
+ accessibility_kobj = kobject_create_and_add("accessibility", NULL);
+ if (!accessibility_kobj) {
+ retval = -ENOMEM;
+ goto out;
+ }
+
+ speakup_kobj = kobject_create_and_add("speakup", accessibility_kobj);
+ if (!speakup_kobj) {
+ retval = -ENOMEM;
+ goto err_acc;
+ }
+
+ /* Create the files associated with this kobject */
+ retval = sysfs_create_group(speakup_kobj, &main_attr_group);
+ if (retval)
+ goto err_speakup;
+
+ retval = sysfs_create_group(speakup_kobj, &i18n_attr_group);
+ if (retval)
+ goto err_group;
+
+ goto out;
+
+err_group:
+ sysfs_remove_group(speakup_kobj, &main_attr_group);
+err_speakup:
+ kobject_put(speakup_kobj);
+err_acc:
+ kobject_put(accessibility_kobj);
+out:
+ return retval;
+}
+
+void speakup_kobj_exit(void)
+{
+ sysfs_remove_group(speakup_kobj, &i18n_attr_group);
+ sysfs_remove_group(speakup_kobj, &main_attr_group);
+ kobject_put(speakup_kobj);
+ kobject_put(accessibility_kobj);
+}
diff --git a/drivers/staging/speakup/main.c b/drivers/staging/speakup/main.c
new file mode 100644
index 000000000..c95597641
--- /dev/null
+++ b/drivers/staging/speakup/main.c
@@ -0,0 +1,2409 @@
+/* speakup.c
+ * review functions for the speakup screen review package.
+ * originally written by: Kirk Reiser and Andy Berdan.
+ *
+ * extensively modified by David Borowski.
+ *
+ ** Copyright (C) 1998 Kirk Reiser.
+ * Copyright (C) 2003 David Borowski.
+ *
+ * This program is free software; you can redistribute it and/or 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/kernel.h>
+#include <linux/vt.h>
+#include <linux/tty.h>
+#include <linux/mm.h> /* __get_free_page() and friends */
+#include <linux/vt_kern.h>
+#include <linux/ctype.h>
+#include <linux/selection.h>
+#include <linux/unistd.h>
+#include <linux/jiffies.h>
+#include <linux/kthread.h>
+#include <linux/keyboard.h> /* for KT_SHIFT */
+#include <linux/kbd_kern.h> /* for vc_kbd_* and friends */
+#include <linux/input.h>
+#include <linux/kmod.h>
+
+/* speakup_*_selection */
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <linux/consolemap.h>
+
+#include <linux/spinlock.h>
+#include <linux/notifier.h>
+
+#include <linux/uaccess.h> /* copy_from|to|user() and others */
+
+#include "spk_priv.h"
+#include "speakup.h"
+
+#define MAX_DELAY msecs_to_jiffies(500)
+#define MINECHOCHAR SPACE
+
+MODULE_AUTHOR("Kirk Reiser <kirk@braille.uwo.ca>");
+MODULE_AUTHOR("Daniel Drake <dsd@gentoo.org>");
+MODULE_DESCRIPTION("Speakup console speech");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(SPEAKUP_VERSION);
+
+char *synth_name;
+module_param_named(synth, synth_name, charp, S_IRUGO);
+module_param_named(quiet, spk_quiet_boot, bool, S_IRUGO);
+
+MODULE_PARM_DESC(synth, "Synth to start if speakup is built in.");
+MODULE_PARM_DESC(quiet, "Do not announce when the synthesizer is found.");
+
+special_func spk_special_handler;
+
+short spk_pitch_shift, synth_flags;
+static char buf[256];
+int spk_attrib_bleep, spk_bleeps, spk_bleep_time = 10;
+int spk_no_intr, spk_spell_delay;
+int spk_key_echo, spk_say_word_ctl;
+int spk_say_ctrl, spk_bell_pos;
+short spk_punc_mask;
+int spk_punc_level, spk_reading_punc;
+char spk_str_caps_start[MAXVARLEN + 1] = "\0";
+char spk_str_caps_stop[MAXVARLEN + 1] = "\0";
+const struct st_bits_data spk_punc_info[] = {
+ {"none", "", 0},
+ {"some", "/$%&@", SOME},
+ {"most", "$%&#()=+*/@^<>|\\", MOST},
+ {"all", "!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~", PUNC},
+ {"delimiters", "", B_WDLM},
+ {"repeats", "()", CH_RPT},
+ {"extended numeric", "", B_EXNUM},
+ {"symbols", "", B_SYM},
+ {NULL, NULL}
+};
+
+static char mark_cut_flag;
+#define MAX_KEY 160
+static u_char *spk_shift_table;
+u_char *spk_our_keys[MAX_KEY];
+u_char spk_key_buf[600];
+const u_char spk_key_defaults[] = {
+#include "speakupmap.h"
+};
+
+/* Speakup Cursor Track Variables */
+static int cursor_track = 1, prev_cursor_track = 1;
+
+/* cursor track modes, must be ordered same as cursor_msgs */
+enum {
+ CT_Off = 0,
+ CT_On,
+ CT_Highlight,
+ CT_Window,
+ CT_Max
+};
+#define read_all_mode CT_Max
+
+static struct tty_struct *tty;
+
+static void spkup_write(const char *in_buf, int count);
+
+static char *phonetic[] = {
+ "alfa", "bravo", "charlie", "delta", "echo", "foxtrot", "golf", "hotel",
+ "india", "juliett", "keelo", "leema", "mike", "november", "oscar",
+ "papa",
+ "keh beck", "romeo", "sierra", "tango", "uniform", "victer", "whiskey",
+ "x ray", "yankee", "zulu"
+};
+
+/* array of 256 char pointers (one for each character description)
+ * initialized to default_chars and user selectable via
+ * /proc/speakup/characters */
+char *spk_characters[256];
+
+char *spk_default_chars[256] = {
+/*000*/ "null", "^a", "^b", "^c", "^d", "^e", "^f", "^g",
+/*008*/ "^h", "^i", "^j", "^k", "^l", "^m", "^n", "^o",
+/*016*/ "^p", "^q", "^r", "^s", "^t", "^u", "^v", "^w",
+/*024*/ "^x", "^y", "^z", "control", "control", "control", "control",
+ "control",
+/*032*/ "space", "bang!", "quote", "number", "dollar", "percent", "and",
+ "tick",
+/*040*/ "left paren", "right paren", "star", "plus", "comma", "dash",
+ "dot",
+ "slash",
+/*048*/ "zero", "one", "two", "three", "four", "five", "six", "seven",
+ "eight", "nine",
+/*058*/ "colon", "semmy", "less", "equals", "greater", "question", "at",
+/*065*/ "EIGH", "B", "C", "D", "E", "F", "G",
+/*072*/ "H", "I", "J", "K", "L", "M", "N", "O",
+/*080*/ "P", "Q", "R", "S", "T", "U", "V", "W", "X",
+/*089*/ "Y", "ZED", "left bracket", "backslash", "right bracket",
+ "caret",
+ "line",
+/*096*/ "accent", "a", "b", "c", "d", "e", "f", "g",
+/*104*/ "h", "i", "j", "k", "l", "m", "n", "o",
+/*112*/ "p", "q", "r", "s", "t", "u", "v", "w",
+/*120*/ "x", "y", "zed", "left brace", "bar", "right brace", "tihlduh",
+/*127*/ "del", "control", "control", "control", "control", "control",
+ "control", "control", "control", "control", "control",
+/*138*/ "control", "control", "control", "control", "control",
+ "control", "control", "control", "control", "control",
+ "control", "control",
+/*150*/ "control", "control", "control", "control", "control",
+ "control", "control", "control", "control", "control",
+/*160*/ "nbsp", "inverted bang",
+/*162*/ "cents", "pounds", "currency", "yen", "broken bar", "section",
+/*168*/ "diaeresis", "copyright", "female ordinal", "double left angle",
+/*172*/ "not", "soft hyphen", "registered", "macron",
+/*176*/ "degrees", "plus or minus", "super two", "super three",
+/*180*/ "acute accent", "micro", "pilcrow", "middle dot",
+/*184*/ "cedilla", "super one", "male ordinal", "double right angle",
+/*188*/ "one quarter", "one half", "three quarters",
+ "inverted question",
+/*192*/ "A GRAVE", "A ACUTE", "A CIRCUMFLEX", "A TILDE", "A OOMLAUT",
+ "A RING",
+/*198*/ "AE", "C CIDELLA", "E GRAVE", "E ACUTE", "E CIRCUMFLEX",
+ "E OOMLAUT",
+/*204*/ "I GRAVE", "I ACUTE", "I CIRCUMFLEX", "I OOMLAUT", "ETH",
+ "N TILDE",
+/*210*/ "O GRAVE", "O ACUTE", "O CIRCUMFLEX", "O TILDE", "O OOMLAUT",
+/*215*/ "multiplied by", "O STROKE", "U GRAVE", "U ACUTE",
+ "U CIRCUMFLEX",
+/*220*/ "U OOMLAUT", "Y ACUTE", "THORN", "sharp s", "a grave",
+/*225*/ "a acute", "a circumflex", "a tilde", "a oomlaut", "a ring",
+/*230*/ "ae", "c cidella", "e grave", "e acute",
+/*234*/ "e circumflex", "e oomlaut", "i grave", "i acute",
+ "i circumflex",
+/*239*/ "i oomlaut", "eth", "n tilde", "o grave", "o acute",
+ "o circumflex",
+/*245*/ "o tilde", "o oomlaut", "divided by", "o stroke", "u grave",
+ "u acute",
+/* 251 */ "u circumflex", "u oomlaut", "y acute", "thorn", "y oomlaut"
+};
+
+/* array of 256 u_short (one for each character)
+ * initialized to default_chartab and user selectable via
+ * /sys/module/speakup/parameters/chartab */
+u_short spk_chartab[256];
+
+static u_short default_chartab[256] = {
+ B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, /* 0-7 */
+ B_CTL, B_CTL, A_CTL, B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, /* 8-15 */
+ B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, /*16-23 */
+ B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, B_CTL, /* 24-31 */
+ WDLM, A_PUNC, PUNC, PUNC, PUNC, PUNC, PUNC, A_PUNC, /* !"#$%&' */
+ PUNC, PUNC, PUNC, PUNC, A_PUNC, A_PUNC, A_PUNC, PUNC, /* ()*+, -./ */
+ NUM, NUM, NUM, NUM, NUM, NUM, NUM, NUM, /* 01234567 */
+ NUM, NUM, A_PUNC, PUNC, PUNC, PUNC, PUNC, A_PUNC, /* 89:;<=>? */
+ PUNC, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, /* @ABCDEFG */
+ A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, /* HIJKLMNO */
+ A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, /* PQRSTUVW */
+ A_CAP, A_CAP, A_CAP, PUNC, PUNC, PUNC, PUNC, PUNC, /* XYZ[\]^_ */
+ PUNC, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, /* `abcdefg */
+ ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, /* hijklmno */
+ ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, /* pqrstuvw */
+ ALPHA, ALPHA, ALPHA, PUNC, PUNC, PUNC, PUNC, 0, /* xyz{|}~ */
+ B_CAPSYM, B_CAPSYM, B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, /* 128-134 */
+ B_SYM, /* 135 */
+ B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, /* 136-142 */
+ B_CAPSYM, /* 143 */
+ B_CAPSYM, B_CAPSYM, B_SYM, B_CAPSYM, B_SYM, B_SYM, B_SYM, /* 144-150 */
+ B_SYM, /* 151 */
+ B_SYM, B_SYM, B_CAPSYM, B_CAPSYM, B_SYM, B_SYM, B_SYM, /*152-158 */
+ B_SYM, /* 159 */
+ WDLM, B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, B_CAPSYM, /* 160-166 */
+ B_SYM, /* 167 */
+ B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, /* 168-175 */
+ B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, /* 176-183 */
+ B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, B_SYM, /* 184-191 */
+ A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, /* 192-199 */
+ A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, /* 200-207 */
+ A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, B_SYM, /* 208-215 */
+ A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, A_CAP, ALPHA, /* 216-223 */
+ ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, /* 224-231 */
+ ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, /* 232-239 */
+ ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, B_SYM, /* 240-247 */
+ ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA /* 248-255 */
+};
+
+struct task_struct *speakup_task;
+struct bleep spk_unprocessed_sound;
+static int spk_keydown;
+static u_char spk_lastkey, spk_close_press, keymap_flags;
+static u_char last_keycode, this_speakup_key;
+static u_long last_spk_jiffy;
+
+struct st_spk_t *speakup_console[MAX_NR_CONSOLES];
+
+DEFINE_MUTEX(spk_mutex);
+
+static int keyboard_notifier_call(struct notifier_block *,
+ unsigned long code, void *param);
+
+static struct notifier_block keyboard_notifier_block = {
+ .notifier_call = keyboard_notifier_call,
+};
+
+static int vt_notifier_call(struct notifier_block *,
+ unsigned long code, void *param);
+
+static struct notifier_block vt_notifier_block = {
+ .notifier_call = vt_notifier_call,
+};
+
+static unsigned char get_attributes(u16 *pos)
+{
+ return (u_char) (scr_readw(pos) >> 8);
+}
+
+static void speakup_date(struct vc_data *vc)
+{
+ spk_x = spk_cx = vc->vc_x;
+ spk_y = spk_cy = vc->vc_y;
+ spk_pos = spk_cp = vc->vc_pos;
+ spk_old_attr = spk_attr;
+ spk_attr = get_attributes((u_short *) spk_pos);
+}
+
+static void bleep(u_short val)
+{
+ static const short vals[] = {
+ 350, 370, 392, 414, 440, 466, 491, 523, 554, 587, 619, 659
+ };
+ short freq;
+ int time = spk_bleep_time;
+
+ freq = vals[val % 12];
+ if (val > 11)
+ freq *= (1 << (val / 12));
+ spk_unprocessed_sound.freq = freq;
+ spk_unprocessed_sound.jiffies = msecs_to_jiffies(time);
+ spk_unprocessed_sound.active = 1;
+ /* We can only have 1 active sound at a time. */
+}
+
+static void speakup_shut_up(struct vc_data *vc)
+{
+ if (spk_killed)
+ return;
+ spk_shut_up |= 0x01;
+ spk_parked &= 0xfe;
+ speakup_date(vc);
+ if (synth != NULL)
+ spk_do_flush();
+}
+
+static void speech_kill(struct vc_data *vc)
+{
+ char val = synth->is_alive(synth);
+
+ if (val == 0)
+ return;
+
+ /* re-enables synth, if disabled */
+ if (val == 2 || spk_killed) {
+ /* dead */
+ spk_shut_up &= ~0x40;
+ synth_printf("%s\n", spk_msg_get(MSG_IAM_ALIVE));
+ } else {
+ synth_printf("%s\n", spk_msg_get(MSG_YOU_KILLED_SPEAKUP));
+ spk_shut_up |= 0x40;
+ }
+}
+
+static void speakup_off(struct vc_data *vc)
+{
+ if (spk_shut_up & 0x80) {
+ spk_shut_up &= 0x7f;
+ synth_printf("%s\n", spk_msg_get(MSG_HEY_THATS_BETTER));
+ } else {
+ spk_shut_up |= 0x80;
+ synth_printf("%s\n", spk_msg_get(MSG_YOU_TURNED_ME_OFF));
+ }
+ speakup_date(vc);
+}
+
+static void speakup_parked(struct vc_data *vc)
+{
+ if (spk_parked & 0x80) {
+ spk_parked = 0;
+ synth_printf("%s\n", spk_msg_get(MSG_UNPARKED));
+ } else {
+ spk_parked |= 0x80;
+ synth_printf("%s\n", spk_msg_get(MSG_PARKED));
+ }
+}
+
+static void speakup_cut(struct vc_data *vc)
+{
+ static const char err_buf[] = "set selection failed";
+ int ret;
+
+ if (!mark_cut_flag) {
+ mark_cut_flag = 1;
+ spk_xs = (u_short) spk_x;
+ spk_ys = (u_short) spk_y;
+ spk_sel_cons = vc;
+ synth_printf("%s\n", spk_msg_get(MSG_MARK));
+ return;
+ }
+ spk_xe = (u_short) spk_x;
+ spk_ye = (u_short) spk_y;
+ mark_cut_flag = 0;
+ synth_printf("%s\n", spk_msg_get(MSG_CUT));
+
+ speakup_clear_selection();
+ ret = speakup_set_selection(tty);
+
+ switch (ret) {
+ case 0:
+ break; /* no error */
+ case -EFAULT:
+ pr_warn("%sEFAULT\n", err_buf);
+ break;
+ case -EINVAL:
+ pr_warn("%sEINVAL\n", err_buf);
+ break;
+ case -ENOMEM:
+ pr_warn("%sENOMEM\n", err_buf);
+ break;
+ }
+}
+
+static void speakup_paste(struct vc_data *vc)
+{
+ if (mark_cut_flag) {
+ mark_cut_flag = 0;
+ synth_printf("%s\n", spk_msg_get(MSG_MARK_CLEARED));
+ } else {
+ synth_printf("%s\n", spk_msg_get(MSG_PASTE));
+ speakup_paste_selection(tty);
+ }
+}
+
+static void say_attributes(struct vc_data *vc)
+{
+ int fg = spk_attr & 0x0f;
+ int bg = spk_attr >> 4;
+
+ if (fg > 8) {
+ synth_printf("%s ", spk_msg_get(MSG_BRIGHT));
+ fg -= 8;
+ }
+ synth_printf("%s", spk_msg_get(MSG_COLORS_START + fg));
+ if (bg > 7) {
+ synth_printf(" %s ", spk_msg_get(MSG_ON_BLINKING));
+ bg -= 8;
+ } else
+ synth_printf(" %s ", spk_msg_get(MSG_ON));
+ synth_printf("%s\n", spk_msg_get(MSG_COLORS_START + bg));
+}
+
+enum {
+ edge_top = 1,
+ edge_bottom,
+ edge_left,
+ edge_right,
+ edge_quiet
+};
+
+static void announce_edge(struct vc_data *vc, int msg_id)
+{
+ if (spk_bleeps & 1)
+ bleep(spk_y);
+ if ((spk_bleeps & 2) && (msg_id < edge_quiet))
+ synth_printf("%s\n",
+ spk_msg_get(MSG_EDGE_MSGS_START + msg_id - 1));
+}
+
+static void speak_char(u_char ch)
+{
+ char *cp = spk_characters[ch];
+ struct var_t *direct = spk_get_var(DIRECT);
+
+ if (direct && direct->u.n.value) {
+ if (IS_CHAR(ch, B_CAP)) {
+ spk_pitch_shift++;
+ synth_printf("%s", spk_str_caps_start);
+ }
+ synth_printf("%c", ch);
+ if (IS_CHAR(ch, B_CAP))
+ synth_printf("%s", spk_str_caps_stop);
+ return;
+ }
+ if (cp == NULL) {
+ pr_info("speak_char: cp == NULL!\n");
+ return;
+ }
+ synth_buffer_add(SPACE);
+ if (IS_CHAR(ch, B_CAP)) {
+ spk_pitch_shift++;
+ synth_printf("%s", spk_str_caps_start);
+ synth_printf("%s", cp);
+ synth_printf("%s", spk_str_caps_stop);
+ } else {
+ if (*cp == '^') {
+ synth_printf("%s", spk_msg_get(MSG_CTRL));
+ cp++;
+ }
+ synth_printf("%s", cp);
+ }
+ synth_buffer_add(SPACE);
+}
+
+static u16 get_char(struct vc_data *vc, u16 *pos, u_char *attribs)
+{
+ u16 ch = ' ';
+
+ if (vc && pos) {
+ u16 w = scr_readw(pos);
+ u16 c = w & 0xff;
+
+ if (w & vc->vc_hi_font_mask)
+ c |= 0x100;
+
+ ch = inverse_translate(vc, c, 0);
+ *attribs = (w & 0xff00) >> 8;
+ }
+ return ch;
+}
+
+static void say_char(struct vc_data *vc)
+{
+ u_short ch;
+
+ spk_old_attr = spk_attr;
+ ch = get_char(vc, (u_short *) spk_pos, &spk_attr);
+ if (spk_attr != spk_old_attr) {
+ if (spk_attrib_bleep & 1)
+ bleep(spk_y);
+ if (spk_attrib_bleep & 2)
+ say_attributes(vc);
+ }
+ speak_char(ch & 0xff);
+}
+
+static void say_phonetic_char(struct vc_data *vc)
+{
+ u_short ch;
+
+ spk_old_attr = spk_attr;
+ ch = get_char(vc, (u_short *) spk_pos, &spk_attr);
+ if (isascii(ch) && isalpha(ch)) {
+ ch &= 0x1f;
+ synth_printf("%s\n", phonetic[--ch]);
+ } else {
+ if (IS_CHAR(ch, B_NUM))
+ synth_printf("%s ", spk_msg_get(MSG_NUMBER));
+ speak_char(ch);
+ }
+}
+
+static void say_prev_char(struct vc_data *vc)
+{
+ spk_parked |= 0x01;
+ if (spk_x == 0) {
+ announce_edge(vc, edge_left);
+ return;
+ }
+ spk_x--;
+ spk_pos -= 2;
+ say_char(vc);
+}
+
+static void say_next_char(struct vc_data *vc)
+{
+ spk_parked |= 0x01;
+ if (spk_x == vc->vc_cols - 1) {
+ announce_edge(vc, edge_right);
+ return;
+ }
+ spk_x++;
+ spk_pos += 2;
+ say_char(vc);
+}
+
+/* get_word - will first check to see if the character under the
+ * reading cursor is a space and if spk_say_word_ctl is true it will
+ * return the word space. If spk_say_word_ctl is not set it will check to
+ * see if there is a word starting on the next position to the right
+ * and return that word if it exists. If it does not exist it will
+ * move left to the beginning of any previous word on the line or the
+ * beginning off the line whichever comes first.. */
+
+static u_long get_word(struct vc_data *vc)
+{
+ u_long cnt = 0, tmpx = spk_x, tmp_pos = spk_pos;
+ char ch;
+ u_short attr_ch;
+ u_char temp;
+
+ spk_old_attr = spk_attr;
+ ch = (char)get_char(vc, (u_short *) tmp_pos, &temp);
+
+/* decided to take out the sayword if on a space (mis-information */
+ if (spk_say_word_ctl && ch == SPACE) {
+ *buf = '\0';
+ synth_printf("%s\n", spk_msg_get(MSG_SPACE));
+ return 0;
+ } else if ((tmpx < vc->vc_cols - 2)
+ && (ch == SPACE || ch == 0 || IS_WDLM(ch))
+ && ((char)get_char(vc, (u_short *) &tmp_pos + 1, &temp) >
+ SPACE)) {
+ tmp_pos += 2;
+ tmpx++;
+ } else
+ while (tmpx > 0) {
+ ch = (char)get_char(vc, (u_short *) tmp_pos - 1, &temp);
+ if ((ch == SPACE || ch == 0 || IS_WDLM(ch))
+ && ((char)get_char(vc, (u_short *) tmp_pos, &temp) >
+ SPACE))
+ break;
+ tmp_pos -= 2;
+ tmpx--;
+ }
+ attr_ch = get_char(vc, (u_short *) tmp_pos, &spk_attr);
+ buf[cnt++] = attr_ch & 0xff;
+ while (tmpx < vc->vc_cols - 1) {
+ tmp_pos += 2;
+ tmpx++;
+ ch = (char)get_char(vc, (u_short *) tmp_pos, &temp);
+ if ((ch == SPACE) || ch == 0
+ || (IS_WDLM(buf[cnt - 1]) && (ch > SPACE)))
+ break;
+ buf[cnt++] = ch;
+ }
+ buf[cnt] = '\0';
+ return cnt;
+}
+
+static void say_word(struct vc_data *vc)
+{
+ u_long cnt = get_word(vc);
+ u_short saved_punc_mask = spk_punc_mask;
+
+ if (cnt == 0)
+ return;
+ spk_punc_mask = PUNC;
+ buf[cnt++] = SPACE;
+ spkup_write(buf, cnt);
+ spk_punc_mask = saved_punc_mask;
+}
+
+static void say_prev_word(struct vc_data *vc)
+{
+ u_char temp;
+ char ch;
+ u_short edge_said = 0, last_state = 0, state = 0;
+
+ spk_parked |= 0x01;
+
+ if (spk_x == 0) {
+ if (spk_y == 0) {
+ announce_edge(vc, edge_top);
+ return;
+ }
+ spk_y--;
+ spk_x = vc->vc_cols;
+ edge_said = edge_quiet;
+ }
+ while (1) {
+ if (spk_x == 0) {
+ if (spk_y == 0) {
+ edge_said = edge_top;
+ break;
+ }
+ if (edge_said != edge_quiet)
+ edge_said = edge_left;
+ if (state > 0)
+ break;
+ spk_y--;
+ spk_x = vc->vc_cols - 1;
+ } else
+ spk_x--;
+ spk_pos -= 2;
+ ch = (char)get_char(vc, (u_short *) spk_pos, &temp);
+ if (ch == SPACE || ch == 0)
+ state = 0;
+ else if (IS_WDLM(ch))
+ state = 1;
+ else
+ state = 2;
+ if (state < last_state) {
+ spk_pos += 2;
+ spk_x++;
+ break;
+ }
+ last_state = state;
+ }
+ if (spk_x == 0 && edge_said == edge_quiet)
+ edge_said = edge_left;
+ if (edge_said > 0 && edge_said < edge_quiet)
+ announce_edge(vc, edge_said);
+ say_word(vc);
+}
+
+static void say_next_word(struct vc_data *vc)
+{
+ u_char temp;
+ char ch;
+ u_short edge_said = 0, last_state = 2, state = 0;
+
+ spk_parked |= 0x01;
+ if (spk_x == vc->vc_cols - 1 && spk_y == vc->vc_rows - 1) {
+ announce_edge(vc, edge_bottom);
+ return;
+ }
+ while (1) {
+ ch = (char)get_char(vc, (u_short *) spk_pos, &temp);
+ if (ch == SPACE || ch == 0)
+ state = 0;
+ else if (IS_WDLM(ch))
+ state = 1;
+ else
+ state = 2;
+ if (state > last_state)
+ break;
+ if (spk_x >= vc->vc_cols - 1) {
+ if (spk_y == vc->vc_rows - 1) {
+ edge_said = edge_bottom;
+ break;
+ }
+ state = 0;
+ spk_y++;
+ spk_x = 0;
+ edge_said = edge_right;
+ } else
+ spk_x++;
+ spk_pos += 2;
+ last_state = state;
+ }
+ if (edge_said > 0)
+ announce_edge(vc, edge_said);
+ say_word(vc);
+}
+
+static void spell_word(struct vc_data *vc)
+{
+ static char const *delay_str[] = { "", ",", ".", ". .", ". . ." };
+ char *cp = buf, *str_cap = spk_str_caps_stop;
+ char *cp1, *last_cap = spk_str_caps_stop;
+ u_char ch;
+
+ if (!get_word(vc))
+ return;
+ while ((ch = (u_char) *cp)) {
+ if (cp != buf)
+ synth_printf(" %s ", delay_str[spk_spell_delay]);
+ if (IS_CHAR(ch, B_CAP)) {
+ str_cap = spk_str_caps_start;
+ if (*spk_str_caps_stop)
+ spk_pitch_shift++;
+ else /* synth has no pitch */
+ last_cap = spk_str_caps_stop;
+ } else
+ str_cap = spk_str_caps_stop;
+ if (str_cap != last_cap) {
+ synth_printf("%s", str_cap);
+ last_cap = str_cap;
+ }
+ if (this_speakup_key == SPELL_PHONETIC
+ && (isascii(ch) && isalpha(ch))) {
+ ch &= 31;
+ cp1 = phonetic[--ch];
+ } else {
+ cp1 = spk_characters[ch];
+ if (*cp1 == '^') {
+ synth_printf("%s", spk_msg_get(MSG_CTRL));
+ cp1++;
+ }
+ }
+ synth_printf("%s", cp1);
+ cp++;
+ }
+ if (str_cap != spk_str_caps_stop)
+ synth_printf("%s", spk_str_caps_stop);
+}
+
+static int get_line(struct vc_data *vc)
+{
+ u_long tmp = spk_pos - (spk_x * 2);
+ int i = 0;
+ u_char tmp2;
+
+ spk_old_attr = spk_attr;
+ spk_attr = get_attributes((u_short *) spk_pos);
+ for (i = 0; i < vc->vc_cols; i++) {
+ buf[i] = (u_char) get_char(vc, (u_short *) tmp, &tmp2);
+ tmp += 2;
+ }
+ for (--i; i >= 0; i--)
+ if (buf[i] != SPACE)
+ break;
+ return ++i;
+}
+
+static void say_line(struct vc_data *vc)
+{
+ int i = get_line(vc);
+ char *cp;
+ u_short saved_punc_mask = spk_punc_mask;
+
+ if (i == 0) {
+ synth_printf("%s\n", spk_msg_get(MSG_BLANK));
+ return;
+ }
+ buf[i++] = '\n';
+ if (this_speakup_key == SAY_LINE_INDENT) {
+ cp = buf;
+ while (*cp == SPACE)
+ cp++;
+ synth_printf("%d, ", (cp - buf) + 1);
+ }
+ spk_punc_mask = spk_punc_masks[spk_reading_punc];
+ spkup_write(buf, i);
+ spk_punc_mask = saved_punc_mask;
+}
+
+static void say_prev_line(struct vc_data *vc)
+{
+ spk_parked |= 0x01;
+ if (spk_y == 0) {
+ announce_edge(vc, edge_top);
+ return;
+ }
+ spk_y--;
+ spk_pos -= vc->vc_size_row;
+ say_line(vc);
+}
+
+static void say_next_line(struct vc_data *vc)
+{
+ spk_parked |= 0x01;
+ if (spk_y == vc->vc_rows - 1) {
+ announce_edge(vc, edge_bottom);
+ return;
+ }
+ spk_y++;
+ spk_pos += vc->vc_size_row;
+ say_line(vc);
+}
+
+static int say_from_to(struct vc_data *vc, u_long from, u_long to,
+ int read_punc)
+{
+ int i = 0;
+ u_char tmp;
+ u_short saved_punc_mask = spk_punc_mask;
+
+ spk_old_attr = spk_attr;
+ spk_attr = get_attributes((u_short *) from);
+ while (from < to) {
+ buf[i++] = (char)get_char(vc, (u_short *) from, &tmp);
+ from += 2;
+ if (i >= vc->vc_size_row)
+ break;
+ }
+ for (--i; i >= 0; i--)
+ if (buf[i] != SPACE)
+ break;
+ buf[++i] = SPACE;
+ buf[++i] = '\0';
+ if (i < 1)
+ return i;
+ if (read_punc)
+ spk_punc_mask = spk_punc_info[spk_reading_punc].mask;
+ spkup_write(buf, i);
+ if (read_punc)
+ spk_punc_mask = saved_punc_mask;
+ return i - 1;
+}
+
+static void say_line_from_to(struct vc_data *vc, u_long from, u_long to,
+ int read_punc)
+{
+ u_long start = vc->vc_origin + (spk_y * vc->vc_size_row);
+ u_long end = start + (to * 2);
+
+ start += from * 2;
+ if (say_from_to(vc, start, end, read_punc) <= 0)
+ if (cursor_track != read_all_mode)
+ synth_printf("%s\n", spk_msg_get(MSG_BLANK));
+}
+
+/* Sentence Reading Commands */
+
+static int currsentence;
+static int numsentences[2];
+static char *sentbufend[2];
+static char *sentmarks[2][10];
+static int currbuf;
+static int bn;
+static char sentbuf[2][256];
+
+static int say_sentence_num(int num, int prev)
+{
+ bn = currbuf;
+ currsentence = num + 1;
+ if (prev && --bn == -1)
+ bn = 1;
+
+ if (num > numsentences[bn])
+ return 0;
+
+ spkup_write(sentmarks[bn][num], sentbufend[bn] - sentmarks[bn][num]);
+ return 1;
+}
+
+static int get_sentence_buf(struct vc_data *vc, int read_punc)
+{
+ u_long start, end;
+ int i, bn;
+ u_char tmp;
+
+ currbuf++;
+ if (currbuf == 2)
+ currbuf = 0;
+ bn = currbuf;
+ start = vc->vc_origin + ((spk_y) * vc->vc_size_row);
+ end = vc->vc_origin + ((spk_y) * vc->vc_size_row) + vc->vc_cols * 2;
+
+ numsentences[bn] = 0;
+ sentmarks[bn][0] = &sentbuf[bn][0];
+ i = 0;
+ spk_old_attr = spk_attr;
+ spk_attr = get_attributes((u_short *) start);
+
+ while (start < end) {
+ sentbuf[bn][i] = (char)get_char(vc, (u_short *) start, &tmp);
+ if (i > 0) {
+ if (sentbuf[bn][i] == SPACE && sentbuf[bn][i - 1] == '.'
+ && numsentences[bn] < 9) {
+ /* Sentence Marker */
+ numsentences[bn]++;
+ sentmarks[bn][numsentences[bn]] =
+ &sentbuf[bn][i];
+ }
+ }
+ i++;
+ start += 2;
+ if (i >= vc->vc_size_row)
+ break;
+ }
+
+ for (--i; i >= 0; i--)
+ if (sentbuf[bn][i] != SPACE)
+ break;
+
+ if (i < 1)
+ return -1;
+
+ sentbuf[bn][++i] = SPACE;
+ sentbuf[bn][++i] = '\0';
+
+ sentbufend[bn] = &sentbuf[bn][i];
+ return numsentences[bn];
+}
+
+static void say_screen_from_to(struct vc_data *vc, u_long from, u_long to)
+{
+ u_long start = vc->vc_origin, end;
+
+ if (from > 0)
+ start += from * vc->vc_size_row;
+ if (to > vc->vc_rows)
+ to = vc->vc_rows;
+ end = vc->vc_origin + (to * vc->vc_size_row);
+ for (from = start; from < end; from = to) {
+ to = from + vc->vc_size_row;
+ say_from_to(vc, from, to, 1);
+ }
+}
+
+static void say_screen(struct vc_data *vc)
+{
+ say_screen_from_to(vc, 0, vc->vc_rows);
+}
+
+static void speakup_win_say(struct vc_data *vc)
+{
+ u_long start, end, from, to;
+
+ if (win_start < 2) {
+ synth_printf("%s\n", spk_msg_get(MSG_NO_WINDOW));
+ return;
+ }
+ start = vc->vc_origin + (win_top * vc->vc_size_row);
+ end = vc->vc_origin + (win_bottom * vc->vc_size_row);
+ while (start <= end) {
+ from = start + (win_left * 2);
+ to = start + (win_right * 2);
+ say_from_to(vc, from, to, 1);
+ start += vc->vc_size_row;
+ }
+}
+
+static void top_edge(struct vc_data *vc)
+{
+ spk_parked |= 0x01;
+ spk_pos = vc->vc_origin + 2 * spk_x;
+ spk_y = 0;
+ say_line(vc);
+}
+
+static void bottom_edge(struct vc_data *vc)
+{
+ spk_parked |= 0x01;
+ spk_pos += (vc->vc_rows - spk_y - 1) * vc->vc_size_row;
+ spk_y = vc->vc_rows - 1;
+ say_line(vc);
+}
+
+static void left_edge(struct vc_data *vc)
+{
+ spk_parked |= 0x01;
+ spk_pos -= spk_x * 2;
+ spk_x = 0;
+ say_char(vc);
+}
+
+static void right_edge(struct vc_data *vc)
+{
+ spk_parked |= 0x01;
+ spk_pos += (vc->vc_cols - spk_x - 1) * 2;
+ spk_x = vc->vc_cols - 1;
+ say_char(vc);
+}
+
+static void say_first_char(struct vc_data *vc)
+{
+ int i, len = get_line(vc);
+ u_char ch;
+
+ spk_parked |= 0x01;
+ if (len == 0) {
+ synth_printf("%s\n", spk_msg_get(MSG_BLANK));
+ return;
+ }
+ for (i = 0; i < len; i++)
+ if (buf[i] != SPACE)
+ break;
+ ch = buf[i];
+ spk_pos -= (spk_x - i) * 2;
+ spk_x = i;
+ synth_printf("%d, ", ++i);
+ speak_char(ch);
+}
+
+static void say_last_char(struct vc_data *vc)
+{
+ int len = get_line(vc);
+ u_char ch;
+
+ spk_parked |= 0x01;
+ if (len == 0) {
+ synth_printf("%s\n", spk_msg_get(MSG_BLANK));
+ return;
+ }
+ ch = buf[--len];
+ spk_pos -= (spk_x - len) * 2;
+ spk_x = len;
+ synth_printf("%d, ", ++len);
+ speak_char(ch);
+}
+
+static void say_position(struct vc_data *vc)
+{
+ synth_printf(spk_msg_get(MSG_POS_INFO), spk_y + 1, spk_x + 1,
+ vc->vc_num + 1);
+ synth_printf("\n");
+}
+
+/* Added by brianb */
+static void say_char_num(struct vc_data *vc)
+{
+ u_char tmp;
+ u_short ch = get_char(vc, (u_short *) spk_pos, &tmp);
+
+ ch &= 0xff;
+ synth_printf(spk_msg_get(MSG_CHAR_INFO), ch, ch);
+}
+
+/* these are stub functions to keep keyboard.c happy. */
+
+static void say_from_top(struct vc_data *vc)
+{
+ say_screen_from_to(vc, 0, spk_y);
+}
+
+static void say_to_bottom(struct vc_data *vc)
+{
+ say_screen_from_to(vc, spk_y, vc->vc_rows);
+}
+
+static void say_from_left(struct vc_data *vc)
+{
+ say_line_from_to(vc, 0, spk_x, 1);
+}
+
+static void say_to_right(struct vc_data *vc)
+{
+ say_line_from_to(vc, spk_x, vc->vc_cols, 1);
+}
+
+/* end of stub functions. */
+
+static void spkup_write(const char *in_buf, int count)
+{
+ static int rep_count;
+ static u_char ch = '\0', old_ch = '\0';
+ static u_short char_type, last_type;
+ int in_count = count;
+
+ spk_keydown = 0;
+ while (count--) {
+ if (cursor_track == read_all_mode) {
+ /* Insert Sentence Index */
+ if ((in_buf == sentmarks[bn][currsentence]) &&
+ (currsentence <= numsentences[bn]))
+ synth_insert_next_index(currsentence++);
+ }
+ ch = (u_char) *in_buf++;
+ char_type = spk_chartab[ch];
+ if (ch == old_ch && !(char_type & B_NUM)) {
+ if (++rep_count > 2)
+ continue;
+ } else {
+ if ((last_type & CH_RPT) && rep_count > 2) {
+ synth_printf(" ");
+ synth_printf(spk_msg_get(MSG_REPEAT_DESC),
+ ++rep_count);
+ synth_printf(" ");
+ }
+ rep_count = 0;
+ }
+ if (ch == spk_lastkey) {
+ rep_count = 0;
+ if (spk_key_echo == 1 && ch >= MINECHOCHAR)
+ speak_char(ch);
+ } else if (char_type & B_ALPHA) {
+ if ((synth_flags & SF_DEC) && (last_type & PUNC))
+ synth_buffer_add(SPACE);
+ synth_printf("%c", ch);
+ } else if (char_type & B_NUM) {
+ rep_count = 0;
+ synth_printf("%c", ch);
+ } else if (char_type & spk_punc_mask) {
+ speak_char(ch);
+ char_type &= ~PUNC; /* for dec nospell processing */
+ } else if (char_type & SYNTH_OK) {
+ /* these are usually puncts like . and , which synth
+ * needs for expression.
+ * suppress multiple to get rid of long pauses and
+ * clear repeat count
+ * so if someone has
+ * repeats on you don't get nothing repeated count */
+ if (ch != old_ch)
+ synth_printf("%c", ch);
+ else
+ rep_count = 0;
+ } else {
+/* send space and record position, if next is num overwrite space */
+ if (old_ch != ch)
+ synth_buffer_add(SPACE);
+ else
+ rep_count = 0;
+ }
+ old_ch = ch;
+ last_type = char_type;
+ }
+ spk_lastkey = 0;
+ if (in_count > 2 && rep_count > 2) {
+ if (last_type & CH_RPT) {
+ synth_printf(" ");
+ synth_printf(spk_msg_get(MSG_REPEAT_DESC2),
+ ++rep_count);
+ synth_printf(" ");
+ }
+ rep_count = 0;
+ }
+}
+
+static const int NUM_CTL_LABELS = (MSG_CTL_END - MSG_CTL_START + 1);
+
+static void read_all_doc(struct vc_data *vc);
+static void cursor_done(u_long data);
+static DEFINE_TIMER(cursor_timer, cursor_done, 0, 0);
+
+static void do_handle_shift(struct vc_data *vc, u_char value, char up_flag)
+{
+ unsigned long flags;
+
+ if (synth == NULL || up_flag || spk_killed)
+ return;
+ spin_lock_irqsave(&speakup_info.spinlock, flags);
+ if (cursor_track == read_all_mode) {
+ switch (value) {
+ case KVAL(K_SHIFT):
+ del_timer(&cursor_timer);
+ spk_shut_up &= 0xfe;
+ spk_do_flush();
+ read_all_doc(vc);
+ break;
+ case KVAL(K_CTRL):
+ del_timer(&cursor_timer);
+ cursor_track = prev_cursor_track;
+ spk_shut_up &= 0xfe;
+ spk_do_flush();
+ break;
+ }
+ } else {
+ spk_shut_up &= 0xfe;
+ spk_do_flush();
+ }
+ if (spk_say_ctrl && value < NUM_CTL_LABELS)
+ synth_printf("%s", spk_msg_get(MSG_CTL_START + value));
+ spin_unlock_irqrestore(&speakup_info.spinlock, flags);
+}
+
+static void do_handle_latin(struct vc_data *vc, u_char value, char up_flag)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&speakup_info.spinlock, flags);
+ if (up_flag) {
+ spk_lastkey = spk_keydown = 0;
+ spin_unlock_irqrestore(&speakup_info.spinlock, flags);
+ return;
+ }
+ if (synth == NULL || spk_killed) {
+ spin_unlock_irqrestore(&speakup_info.spinlock, flags);
+ return;
+ }
+ spk_shut_up &= 0xfe;
+ spk_lastkey = value;
+ spk_keydown++;
+ spk_parked &= 0xfe;
+ if (spk_key_echo == 2 && value >= MINECHOCHAR)
+ speak_char(value);
+ spin_unlock_irqrestore(&speakup_info.spinlock, flags);
+}
+
+int spk_set_key_info(const u_char *key_info, u_char *k_buffer)
+{
+ int i = 0, states, key_data_len;
+ const u_char *cp = key_info;
+ u_char *cp1 = k_buffer;
+ u_char ch, version, num_keys;
+
+ version = *cp++;
+ if (version != KEY_MAP_VER)
+ return -1;
+ num_keys = *cp;
+ states = (int)cp[1];
+ key_data_len = (states + 1) * (num_keys + 1);
+ if (key_data_len + SHIFT_TBL_SIZE + 4 >= sizeof(spk_key_buf))
+ return -2;
+ memset(k_buffer, 0, SHIFT_TBL_SIZE);
+ memset(spk_our_keys, 0, sizeof(spk_our_keys));
+ spk_shift_table = k_buffer;
+ spk_our_keys[0] = spk_shift_table;
+ cp1 += SHIFT_TBL_SIZE;
+ memcpy(cp1, cp, key_data_len + 3);
+ /* get num_keys, states and data */
+ cp1 += 2; /* now pointing at shift states */
+ for (i = 1; i <= states; i++) {
+ ch = *cp1++;
+ if (ch >= SHIFT_TBL_SIZE)
+ return -3;
+ spk_shift_table[ch] = i;
+ }
+ keymap_flags = *cp1++;
+ while ((ch = *cp1)) {
+ if (ch >= MAX_KEY)
+ return -4;
+ spk_our_keys[ch] = cp1;
+ cp1 += states + 1;
+ }
+ return 0;
+}
+
+static struct var_t spk_vars[] = {
+ /* bell must be first to set high limit */
+ {BELL_POS, .u.n = {NULL, 0, 0, 0, 0, 0, NULL} },
+ {SPELL_DELAY, .u.n = {NULL, 0, 0, 4, 0, 0, NULL} },
+ {ATTRIB_BLEEP, .u.n = {NULL, 1, 0, 3, 0, 0, NULL} },
+ {BLEEPS, .u.n = {NULL, 3, 0, 3, 0, 0, NULL} },
+ {BLEEP_TIME, .u.n = {NULL, 30, 1, 200, 0, 0, NULL} },
+ {PUNC_LEVEL, .u.n = {NULL, 1, 0, 4, 0, 0, NULL} },
+ {READING_PUNC, .u.n = {NULL, 1, 0, 4, 0, 0, NULL} },
+ {CURSOR_TIME, .u.n = {NULL, 120, 50, 600, 0, 0, NULL} },
+ {SAY_CONTROL, TOGGLE_0},
+ {SAY_WORD_CTL, TOGGLE_0},
+ {NO_INTERRUPT, TOGGLE_0},
+ {KEY_ECHO, .u.n = {NULL, 1, 0, 2, 0, 0, NULL} },
+ V_LAST_VAR
+};
+
+static void toggle_cursoring(struct vc_data *vc)
+{
+ if (cursor_track == read_all_mode)
+ cursor_track = prev_cursor_track;
+ if (++cursor_track >= CT_Max)
+ cursor_track = 0;
+ synth_printf("%s\n", spk_msg_get(MSG_CURSOR_MSGS_START + cursor_track));
+}
+
+void spk_reset_default_chars(void)
+{
+ int i;
+
+ /* First, free any non-default */
+ for (i = 0; i < 256; i++) {
+ if ((spk_characters[i] != NULL)
+ && (spk_characters[i] != spk_default_chars[i]))
+ kfree(spk_characters[i]);
+ }
+
+ memcpy(spk_characters, spk_default_chars, sizeof(spk_default_chars));
+}
+
+void spk_reset_default_chartab(void)
+{
+ memcpy(spk_chartab, default_chartab, sizeof(default_chartab));
+}
+
+static const struct st_bits_data *pb_edit;
+
+static int edit_bits(struct vc_data *vc, u_char type, u_char ch, u_short key)
+{
+ short mask = pb_edit->mask, ch_type = spk_chartab[ch];
+
+ if (type != KT_LATIN || (ch_type & B_NUM) || ch < SPACE)
+ return -1;
+ if (ch == SPACE) {
+ synth_printf("%s\n", spk_msg_get(MSG_EDIT_DONE));
+ spk_special_handler = NULL;
+ return 1;
+ }
+ if (mask < PUNC && !(ch_type & PUNC))
+ return -1;
+ spk_chartab[ch] ^= mask;
+ speak_char(ch);
+ synth_printf(" %s\n",
+ (spk_chartab[ch] & mask) ? spk_msg_get(MSG_ON) :
+ spk_msg_get(MSG_OFF));
+ return 1;
+}
+
+/* Allocation concurrency is protected by the console semaphore */
+static int speakup_allocate(struct vc_data *vc)
+{
+ int vc_num;
+
+ vc_num = vc->vc_num;
+ if (speakup_console[vc_num] == NULL) {
+ speakup_console[vc_num] = kzalloc(sizeof(*speakup_console[0]),
+ GFP_ATOMIC);
+ if (speakup_console[vc_num] == NULL)
+ return -ENOMEM;
+ speakup_date(vc);
+ } else if (!spk_parked)
+ speakup_date(vc);
+
+ return 0;
+}
+
+static void speakup_deallocate(struct vc_data *vc)
+{
+ int vc_num;
+
+ vc_num = vc->vc_num;
+ kfree(speakup_console[vc_num]);
+ speakup_console[vc_num] = NULL;
+}
+
+static u_char is_cursor;
+static u_long old_cursor_pos, old_cursor_x, old_cursor_y;
+static int cursor_con;
+
+static void reset_highlight_buffers(struct vc_data *);
+
+static int read_all_key;
+
+static void start_read_all_timer(struct vc_data *vc, int command);
+
+enum {
+ RA_NOTHING,
+ RA_NEXT_SENT,
+ RA_PREV_LINE,
+ RA_NEXT_LINE,
+ RA_PREV_SENT,
+ RA_DOWN_ARROW,
+ RA_TIMER,
+ RA_FIND_NEXT_SENT,
+ RA_FIND_PREV_SENT,
+};
+
+static void kbd_fakekey2(struct vc_data *vc, int command)
+{
+ del_timer(&cursor_timer);
+ speakup_fake_down_arrow();
+ start_read_all_timer(vc, command);
+}
+
+static void read_all_doc(struct vc_data *vc)
+{
+ if ((vc->vc_num != fg_console) || synth == NULL || spk_shut_up)
+ return;
+ if (!synth_supports_indexing())
+ return;
+ if (cursor_track != read_all_mode)
+ prev_cursor_track = cursor_track;
+ cursor_track = read_all_mode;
+ spk_reset_index_count(0);
+ if (get_sentence_buf(vc, 0) == -1)
+ kbd_fakekey2(vc, RA_DOWN_ARROW);
+ else {
+ say_sentence_num(0, 0);
+ synth_insert_next_index(0);
+ start_read_all_timer(vc, RA_TIMER);
+ }
+}
+
+static void stop_read_all(struct vc_data *vc)
+{
+ del_timer(&cursor_timer);
+ cursor_track = prev_cursor_track;
+ spk_shut_up &= 0xfe;
+ spk_do_flush();
+}
+
+static void start_read_all_timer(struct vc_data *vc, int command)
+{
+ struct var_t *cursor_timeout;
+
+ cursor_con = vc->vc_num;
+ read_all_key = command;
+ cursor_timeout = spk_get_var(CURSOR_TIME);
+ mod_timer(&cursor_timer,
+ jiffies + msecs_to_jiffies(cursor_timeout->u.n.value));
+}
+
+static void handle_cursor_read_all(struct vc_data *vc, int command)
+{
+ int indcount, sentcount, rv, sn;
+
+ switch (command) {
+ case RA_NEXT_SENT:
+ /* Get Current Sentence */
+ spk_get_index_count(&indcount, &sentcount);
+ /*printk("%d %d ", indcount, sentcount); */
+ spk_reset_index_count(sentcount + 1);
+ if (indcount == 1) {
+ if (!say_sentence_num(sentcount + 1, 0)) {
+ kbd_fakekey2(vc, RA_FIND_NEXT_SENT);
+ return;
+ }
+ synth_insert_next_index(0);
+ } else {
+ sn = 0;
+ if (!say_sentence_num(sentcount + 1, 1)) {
+ sn = 1;
+ spk_reset_index_count(sn);
+ } else
+ synth_insert_next_index(0);
+ if (!say_sentence_num(sn, 0)) {
+ kbd_fakekey2(vc, RA_FIND_NEXT_SENT);
+ return;
+ }
+ synth_insert_next_index(0);
+ }
+ start_read_all_timer(vc, RA_TIMER);
+ break;
+ case RA_PREV_SENT:
+ break;
+ case RA_NEXT_LINE:
+ read_all_doc(vc);
+ break;
+ case RA_PREV_LINE:
+ break;
+ case RA_DOWN_ARROW:
+ if (get_sentence_buf(vc, 0) == -1) {
+ kbd_fakekey2(vc, RA_DOWN_ARROW);
+ } else {
+ say_sentence_num(0, 0);
+ synth_insert_next_index(0);
+ start_read_all_timer(vc, RA_TIMER);
+ }
+ break;
+ case RA_FIND_NEXT_SENT:
+ rv = get_sentence_buf(vc, 0);
+ if (rv == -1)
+ read_all_doc(vc);
+ if (rv == 0)
+ kbd_fakekey2(vc, RA_FIND_NEXT_SENT);
+ else {
+ say_sentence_num(1, 0);
+ synth_insert_next_index(0);
+ start_read_all_timer(vc, RA_TIMER);
+ }
+ break;
+ case RA_FIND_PREV_SENT:
+ break;
+ case RA_TIMER:
+ spk_get_index_count(&indcount, &sentcount);
+ if (indcount < 2)
+ kbd_fakekey2(vc, RA_DOWN_ARROW);
+ else
+ start_read_all_timer(vc, RA_TIMER);
+ break;
+ }
+}
+
+static int pre_handle_cursor(struct vc_data *vc, u_char value, char up_flag)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&speakup_info.spinlock, flags);
+ if (cursor_track == read_all_mode) {
+ spk_parked &= 0xfe;
+ if (synth == NULL || up_flag || spk_shut_up) {
+ spin_unlock_irqrestore(&speakup_info.spinlock, flags);
+ return NOTIFY_STOP;
+ }
+ del_timer(&cursor_timer);
+ spk_shut_up &= 0xfe;
+ spk_do_flush();
+ start_read_all_timer(vc, value + 1);
+ spin_unlock_irqrestore(&speakup_info.spinlock, flags);
+ return NOTIFY_STOP;
+ }
+ spin_unlock_irqrestore(&speakup_info.spinlock, flags);
+ return NOTIFY_OK;
+}
+
+static void do_handle_cursor(struct vc_data *vc, u_char value, char up_flag)
+{
+ unsigned long flags;
+ struct var_t *cursor_timeout;
+
+ spin_lock_irqsave(&speakup_info.spinlock, flags);
+ spk_parked &= 0xfe;
+ if (synth == NULL || up_flag || spk_shut_up || cursor_track == CT_Off) {
+ spin_unlock_irqrestore(&speakup_info.spinlock, flags);
+ return;
+ }
+ spk_shut_up &= 0xfe;
+ if (spk_no_intr)
+ spk_do_flush();
+/* the key press flushes if !no_inter but we want to flush on cursor
+ * moves regardless of no_inter state */
+ is_cursor = value + 1;
+ old_cursor_pos = vc->vc_pos;
+ old_cursor_x = vc->vc_x;
+ old_cursor_y = vc->vc_y;
+ speakup_console[vc->vc_num]->ht.cy = vc->vc_y;
+ cursor_con = vc->vc_num;
+ if (cursor_track == CT_Highlight)
+ reset_highlight_buffers(vc);
+ cursor_timeout = spk_get_var(CURSOR_TIME);
+ mod_timer(&cursor_timer,
+ jiffies + msecs_to_jiffies(cursor_timeout->u.n.value));
+ spin_unlock_irqrestore(&speakup_info.spinlock, flags);
+}
+
+static void update_color_buffer(struct vc_data *vc, const char *ic, int len)
+{
+ int i, bi, hi;
+ int vc_num = vc->vc_num;
+
+ bi = (vc->vc_attr & 0x70) >> 4;
+ hi = speakup_console[vc_num]->ht.highsize[bi];
+
+ i = 0;
+ if (speakup_console[vc_num]->ht.highsize[bi] == 0) {
+ speakup_console[vc_num]->ht.rpos[bi] = vc->vc_pos;
+ speakup_console[vc_num]->ht.rx[bi] = vc->vc_x;
+ speakup_console[vc_num]->ht.ry[bi] = vc->vc_y;
+ }
+ while ((hi < COLOR_BUFFER_SIZE) && (i < len)) {
+ if ((ic[i] > 32) && (ic[i] < 127)) {
+ speakup_console[vc_num]->ht.highbuf[bi][hi] = ic[i];
+ hi++;
+ } else if ((ic[i] == 32) && (hi != 0)) {
+ if (speakup_console[vc_num]->ht.highbuf[bi][hi - 1] !=
+ 32) {
+ speakup_console[vc_num]->ht.highbuf[bi][hi] =
+ ic[i];
+ hi++;
+ }
+ }
+ i++;
+ }
+ speakup_console[vc_num]->ht.highsize[bi] = hi;
+}
+
+static void reset_highlight_buffers(struct vc_data *vc)
+{
+ int i;
+ int vc_num = vc->vc_num;
+
+ for (i = 0; i < 8; i++)
+ speakup_console[vc_num]->ht.highsize[i] = 0;
+}
+
+static int count_highlight_color(struct vc_data *vc)
+{
+ int i, bg;
+ int cc;
+ int vc_num = vc->vc_num;
+ u16 ch;
+ u16 *start = (u16 *) vc->vc_origin;
+
+ for (i = 0; i < 8; i++)
+ speakup_console[vc_num]->ht.bgcount[i] = 0;
+
+ for (i = 0; i < vc->vc_rows; i++) {
+ u16 *end = start + vc->vc_cols * 2;
+ u16 *ptr;
+
+ for (ptr = start; ptr < end; ptr++) {
+ ch = get_attributes(ptr);
+ bg = (ch & 0x70) >> 4;
+ speakup_console[vc_num]->ht.bgcount[bg]++;
+ }
+ start += vc->vc_size_row;
+ }
+
+ cc = 0;
+ for (i = 0; i < 8; i++)
+ if (speakup_console[vc_num]->ht.bgcount[i] > 0)
+ cc++;
+ return cc;
+}
+
+static int get_highlight_color(struct vc_data *vc)
+{
+ int i, j;
+ unsigned int cptr[8], tmp;
+ int vc_num = vc->vc_num;
+
+ for (i = 0; i < 8; i++)
+ cptr[i] = i;
+
+ for (i = 0; i < 7; i++)
+ for (j = i + 1; j < 8; j++)
+ if (speakup_console[vc_num]->ht.bgcount[cptr[i]] >
+ speakup_console[vc_num]->ht.bgcount[cptr[j]]) {
+ tmp = cptr[i];
+ cptr[i] = cptr[j];
+ cptr[j] = tmp;
+ }
+
+ for (i = 0; i < 8; i++)
+ if (speakup_console[vc_num]->ht.bgcount[cptr[i]] != 0)
+ if (speakup_console[vc_num]->ht.highsize[cptr[i]] > 0)
+ return cptr[i];
+ return -1;
+}
+
+static int speak_highlight(struct vc_data *vc)
+{
+ int hc, d;
+ int vc_num = vc->vc_num;
+
+ if (count_highlight_color(vc) == 1)
+ return 0;
+ hc = get_highlight_color(vc);
+ if (hc != -1) {
+ d = vc->vc_y - speakup_console[vc_num]->ht.cy;
+ if ((d == 1) || (d == -1))
+ if (speakup_console[vc_num]->ht.ry[hc] != vc->vc_y)
+ return 0;
+ spk_parked |= 0x01;
+ spk_do_flush();
+ spkup_write(speakup_console[vc_num]->ht.highbuf[hc],
+ speakup_console[vc_num]->ht.highsize[hc]);
+ spk_pos = spk_cp = speakup_console[vc_num]->ht.rpos[hc];
+ spk_x = spk_cx = speakup_console[vc_num]->ht.rx[hc];
+ spk_y = spk_cy = speakup_console[vc_num]->ht.ry[hc];
+ return 1;
+ }
+ return 0;
+}
+
+static void cursor_done(u_long data)
+{
+ struct vc_data *vc = vc_cons[cursor_con].d;
+ unsigned long flags;
+
+ del_timer(&cursor_timer);
+ spin_lock_irqsave(&speakup_info.spinlock, flags);
+ if (cursor_con != fg_console) {
+ is_cursor = 0;
+ goto out;
+ }
+ speakup_date(vc);
+ if (win_enabled) {
+ if (vc->vc_x >= win_left && vc->vc_x <= win_right &&
+ vc->vc_y >= win_top && vc->vc_y <= win_bottom) {
+ spk_keydown = is_cursor = 0;
+ goto out;
+ }
+ }
+ if (cursor_track == read_all_mode) {
+ handle_cursor_read_all(vc, read_all_key);
+ goto out;
+ }
+ if (cursor_track == CT_Highlight) {
+ if (speak_highlight(vc)) {
+ spk_keydown = is_cursor = 0;
+ goto out;
+ }
+ }
+ if (cursor_track == CT_Window)
+ speakup_win_say(vc);
+ else if (is_cursor == 1 || is_cursor == 4)
+ say_line_from_to(vc, 0, vc->vc_cols, 0);
+ else
+ say_char(vc);
+ spk_keydown = is_cursor = 0;
+out:
+ spin_unlock_irqrestore(&speakup_info.spinlock, flags);
+}
+
+/* called by: vt_notifier_call() */
+static void speakup_bs(struct vc_data *vc)
+{
+ unsigned long flags;
+
+ if (!speakup_console[vc->vc_num])
+ return;
+ if (!spin_trylock_irqsave(&speakup_info.spinlock, flags))
+ /* Speakup output, discard */
+ return;
+ if (!spk_parked)
+ speakup_date(vc);
+ if (spk_shut_up || synth == NULL) {
+ spin_unlock_irqrestore(&speakup_info.spinlock, flags);
+ return;
+ }
+ if (vc->vc_num == fg_console && spk_keydown) {
+ spk_keydown = 0;
+ if (!is_cursor)
+ say_char(vc);
+ }
+ spin_unlock_irqrestore(&speakup_info.spinlock, flags);
+}
+
+/* called by: vt_notifier_call() */
+static void speakup_con_write(struct vc_data *vc, const char *str, int len)
+{
+ unsigned long flags;
+
+ if ((vc->vc_num != fg_console) || spk_shut_up || synth == NULL)
+ return;
+ if (!spin_trylock_irqsave(&speakup_info.spinlock, flags))
+ /* Speakup output, discard */
+ return;
+ if (spk_bell_pos && spk_keydown && (vc->vc_x == spk_bell_pos - 1))
+ bleep(3);
+ if ((is_cursor) || (cursor_track == read_all_mode)) {
+ if (cursor_track == CT_Highlight)
+ update_color_buffer(vc, str, len);
+ spin_unlock_irqrestore(&speakup_info.spinlock, flags);
+ return;
+ }
+ if (win_enabled) {
+ if (vc->vc_x >= win_left && vc->vc_x <= win_right &&
+ vc->vc_y >= win_top && vc->vc_y <= win_bottom) {
+ spin_unlock_irqrestore(&speakup_info.spinlock, flags);
+ return;
+ }
+ }
+
+ spkup_write(str, len);
+ spin_unlock_irqrestore(&speakup_info.spinlock, flags);
+}
+
+static void speakup_con_update(struct vc_data *vc)
+{
+ unsigned long flags;
+
+ if (speakup_console[vc->vc_num] == NULL || spk_parked)
+ return;
+ if (!spin_trylock_irqsave(&speakup_info.spinlock, flags))
+ /* Speakup output, discard */
+ return;
+ speakup_date(vc);
+ spin_unlock_irqrestore(&speakup_info.spinlock, flags);
+}
+
+static void do_handle_spec(struct vc_data *vc, u_char value, char up_flag)
+{
+ unsigned long flags;
+ int on_off = 2;
+ char *label;
+
+ if (synth == NULL || up_flag || spk_killed)
+ return;
+ spin_lock_irqsave(&speakup_info.spinlock, flags);
+ spk_shut_up &= 0xfe;
+ if (spk_no_intr)
+ spk_do_flush();
+ switch (value) {
+ case KVAL(K_CAPS):
+ label = spk_msg_get(MSG_KEYNAME_CAPSLOCK);
+ on_off = vt_get_leds(fg_console, VC_CAPSLOCK);
+ break;
+ case KVAL(K_NUM):
+ label = spk_msg_get(MSG_KEYNAME_NUMLOCK);
+ on_off = vt_get_leds(fg_console, VC_NUMLOCK);
+ break;
+ case KVAL(K_HOLD):
+ label = spk_msg_get(MSG_KEYNAME_SCROLLLOCK);
+ on_off = vt_get_leds(fg_console, VC_SCROLLOCK);
+ if (speakup_console[vc->vc_num])
+ speakup_console[vc->vc_num]->tty_stopped = on_off;
+ break;
+ default:
+ spk_parked &= 0xfe;
+ spin_unlock_irqrestore(&speakup_info.spinlock, flags);
+ return;
+ }
+ if (on_off < 2)
+ synth_printf("%s %s\n",
+ label, spk_msg_get(MSG_STATUS_START + on_off));
+ spin_unlock_irqrestore(&speakup_info.spinlock, flags);
+}
+
+static int inc_dec_var(u_char value)
+{
+ struct st_var_header *p_header;
+ struct var_t *var_data;
+ char num_buf[32];
+ char *cp = num_buf;
+ char *pn;
+ int var_id = (int)value - VAR_START;
+ int how = (var_id & 1) ? E_INC : E_DEC;
+
+ var_id = var_id / 2 + FIRST_SET_VAR;
+ p_header = spk_get_var_header(var_id);
+ if (p_header == NULL)
+ return -1;
+ if (p_header->var_type != VAR_NUM)
+ return -1;
+ var_data = p_header->data;
+ if (spk_set_num_var(1, p_header, how) != 0)
+ return -1;
+ if (!spk_close_press) {
+ for (pn = p_header->name; *pn; pn++) {
+ if (*pn == '_')
+ *cp = SPACE;
+ else
+ *cp++ = *pn;
+ }
+ }
+ snprintf(cp, sizeof(num_buf) - (cp - num_buf), " %d ",
+ var_data->u.n.value);
+ synth_printf("%s", num_buf);
+ return 0;
+}
+
+static void speakup_win_set(struct vc_data *vc)
+{
+ char info[40];
+
+ if (win_start > 1) {
+ synth_printf("%s\n", spk_msg_get(MSG_WINDOW_ALREADY_SET));
+ return;
+ }
+ if (spk_x < win_left || spk_y < win_top) {
+ synth_printf("%s\n", spk_msg_get(MSG_END_BEFORE_START));
+ return;
+ }
+ if (win_start && spk_x == win_left && spk_y == win_top) {
+ win_left = 0;
+ win_right = vc->vc_cols - 1;
+ win_bottom = spk_y;
+ snprintf(info, sizeof(info), spk_msg_get(MSG_WINDOW_LINE),
+ (int)win_top + 1);
+ } else {
+ if (!win_start) {
+ win_top = spk_y;
+ win_left = spk_x;
+ } else {
+ win_bottom = spk_y;
+ win_right = spk_x;
+ }
+ snprintf(info, sizeof(info), spk_msg_get(MSG_WINDOW_BOUNDARY),
+ (win_start) ?
+ spk_msg_get(MSG_END) : spk_msg_get(MSG_START),
+ (int)spk_y + 1, (int)spk_x + 1);
+ }
+ synth_printf("%s\n", info);
+ win_start++;
+}
+
+static void speakup_win_clear(struct vc_data *vc)
+{
+ win_top = win_bottom = 0;
+ win_left = win_right = 0;
+ win_start = 0;
+ synth_printf("%s\n", spk_msg_get(MSG_WINDOW_CLEARED));
+}
+
+static void speakup_win_enable(struct vc_data *vc)
+{
+ if (win_start < 2) {
+ synth_printf("%s\n", spk_msg_get(MSG_NO_WINDOW));
+ return;
+ }
+ win_enabled ^= 1;
+ if (win_enabled)
+ synth_printf("%s\n", spk_msg_get(MSG_WINDOW_SILENCED));
+ else
+ synth_printf("%s\n", spk_msg_get(MSG_WINDOW_SILENCE_DISABLED));
+}
+
+static void speakup_bits(struct vc_data *vc)
+{
+ int val = this_speakup_key - (FIRST_EDIT_BITS - 1);
+
+ if (spk_special_handler != NULL || val < 1 || val > 6) {
+ synth_printf("%s\n", spk_msg_get(MSG_ERROR));
+ return;
+ }
+ pb_edit = &spk_punc_info[val];
+ synth_printf(spk_msg_get(MSG_EDIT_PROMPT), pb_edit->name);
+ spk_special_handler = edit_bits;
+}
+
+static int handle_goto(struct vc_data *vc, u_char type, u_char ch, u_short key)
+{
+ static u_char goto_buf[8];
+ static int num;
+ int maxlen;
+ char *cp;
+
+ if (type == KT_SPKUP && ch == SPEAKUP_GOTO)
+ goto do_goto;
+ if (type == KT_LATIN && ch == '\n')
+ goto do_goto;
+ if (type != 0)
+ goto oops;
+ if (ch == 8) {
+ if (num == 0)
+ return -1;
+ ch = goto_buf[--num];
+ goto_buf[num] = '\0';
+ spkup_write(&ch, 1);
+ return 1;
+ }
+ if (ch < '+' || ch > 'y')
+ goto oops;
+ goto_buf[num++] = ch;
+ goto_buf[num] = '\0';
+ spkup_write(&ch, 1);
+ maxlen = (*goto_buf >= '0') ? 3 : 4;
+ if ((ch == '+' || ch == '-') && num == 1)
+ return 1;
+ if (ch >= '0' && ch <= '9' && num < maxlen)
+ return 1;
+ if (num < maxlen - 1 || num > maxlen)
+ goto oops;
+ if (ch < 'x' || ch > 'y') {
+oops:
+ if (!spk_killed)
+ synth_printf(" %s\n", spk_msg_get(MSG_GOTO_CANCELED));
+ goto_buf[num = 0] = '\0';
+ spk_special_handler = NULL;
+ return 1;
+ }
+
+ goto_pos = simple_strtoul(goto_buf, &cp, 10);
+
+ if (*cp == 'x') {
+ if (*goto_buf < '0')
+ goto_pos += spk_x;
+ else if (goto_pos > 0)
+ goto_pos--;
+
+ if (goto_pos >= vc->vc_cols)
+ goto_pos = vc->vc_cols - 1;
+ goto_x = 1;
+ } else {
+ if (*goto_buf < '0')
+ goto_pos += spk_y;
+ else if (goto_pos > 0)
+ goto_pos--;
+
+ if (goto_pos >= vc->vc_rows)
+ goto_pos = vc->vc_rows - 1;
+ goto_x = 0;
+ }
+ goto_buf[num = 0] = '\0';
+do_goto:
+ spk_special_handler = NULL;
+ spk_parked |= 0x01;
+ if (goto_x) {
+ spk_pos -= spk_x * 2;
+ spk_x = goto_pos;
+ spk_pos += goto_pos * 2;
+ say_word(vc);
+ } else {
+ spk_y = goto_pos;
+ spk_pos = vc->vc_origin + (goto_pos * vc->vc_size_row);
+ say_line(vc);
+ }
+ return 1;
+}
+
+static void speakup_goto(struct vc_data *vc)
+{
+ if (spk_special_handler != NULL) {
+ synth_printf("%s\n", spk_msg_get(MSG_ERROR));
+ return;
+ }
+ synth_printf("%s\n", spk_msg_get(MSG_GOTO));
+ spk_special_handler = handle_goto;
+}
+
+static void speakup_help(struct vc_data *vc)
+{
+ spk_handle_help(vc, KT_SPKUP, SPEAKUP_HELP, 0);
+}
+
+static void do_nothing(struct vc_data *vc)
+{
+ return; /* flush done in do_spkup */
+}
+
+static u_char key_speakup, spk_key_locked;
+
+static void speakup_lock(struct vc_data *vc)
+{
+ if (!spk_key_locked)
+ spk_key_locked = key_speakup = 16;
+ else
+ spk_key_locked = key_speakup = 0;
+}
+
+typedef void (*spkup_hand) (struct vc_data *);
+static spkup_hand spkup_handler[] = {
+ /* must be ordered same as defines in speakup.h */
+ do_nothing, speakup_goto, speech_kill, speakup_shut_up,
+ speakup_cut, speakup_paste, say_first_char, say_last_char,
+ say_char, say_prev_char, say_next_char,
+ say_word, say_prev_word, say_next_word,
+ say_line, say_prev_line, say_next_line,
+ top_edge, bottom_edge, left_edge, right_edge,
+ spell_word, spell_word, say_screen,
+ say_position, say_attributes,
+ speakup_off, speakup_parked, say_line, /* this is for indent */
+ say_from_top, say_to_bottom,
+ say_from_left, say_to_right,
+ say_char_num, speakup_bits, speakup_bits, say_phonetic_char,
+ speakup_bits, speakup_bits, speakup_bits,
+ speakup_win_set, speakup_win_clear, speakup_win_enable, speakup_win_say,
+ speakup_lock, speakup_help, toggle_cursoring, read_all_doc, NULL
+};
+
+static void do_spkup(struct vc_data *vc, u_char value)
+{
+ if (spk_killed && value != SPEECH_KILL)
+ return;
+ spk_keydown = 0;
+ spk_lastkey = 0;
+ spk_shut_up &= 0xfe;
+ this_speakup_key = value;
+ if (value < SPKUP_MAX_FUNC && spkup_handler[value]) {
+ spk_do_flush();
+ (*spkup_handler[value]) (vc);
+ } else {
+ if (inc_dec_var(value) < 0)
+ bleep(9);
+ }
+}
+
+static const char *pad_chars = "0123456789+-*/\015,.?()";
+
+static int
+speakup_key(struct vc_data *vc, int shift_state, int keycode, u_short keysym,
+ int up_flag)
+{
+ unsigned long flags;
+ int kh;
+ u_char *key_info;
+ u_char type = KTYP(keysym), value = KVAL(keysym), new_key = 0;
+ u_char shift_info, offset;
+ int ret = 0;
+
+ if (synth == NULL)
+ return 0;
+
+ spin_lock_irqsave(&speakup_info.spinlock, flags);
+ tty = vc->port.tty;
+ if (type >= 0xf0)
+ type -= 0xf0;
+ if (type == KT_PAD
+ && (vt_get_leds(fg_console, VC_NUMLOCK))) {
+ if (up_flag) {
+ spk_keydown = 0;
+ goto out;
+ }
+ value = spk_lastkey = pad_chars[value];
+ spk_keydown++;
+ spk_parked &= 0xfe;
+ goto no_map;
+ }
+ if (keycode >= MAX_KEY)
+ goto no_map;
+ key_info = spk_our_keys[keycode];
+ if (!key_info)
+ goto no_map;
+ /* Check valid read all mode keys */
+ if ((cursor_track == read_all_mode) && (!up_flag)) {
+ switch (value) {
+ case KVAL(K_DOWN):
+ case KVAL(K_UP):
+ case KVAL(K_LEFT):
+ case KVAL(K_RIGHT):
+ case KVAL(K_PGUP):
+ case KVAL(K_PGDN):
+ break;
+ default:
+ stop_read_all(vc);
+ break;
+ }
+ }
+ shift_info = (shift_state & 0x0f) + key_speakup;
+ offset = spk_shift_table[shift_info];
+ if (offset) {
+ new_key = key_info[offset];
+ if (new_key) {
+ ret = 1;
+ if (new_key == SPK_KEY) {
+ if (!spk_key_locked)
+ key_speakup = (up_flag) ? 0 : 16;
+ if (up_flag || spk_killed)
+ goto out;
+ spk_shut_up &= 0xfe;
+ spk_do_flush();
+ goto out;
+ }
+ if (up_flag)
+ goto out;
+ if (last_keycode == keycode &&
+ time_after(last_spk_jiffy + MAX_DELAY, jiffies)) {
+ spk_close_press = 1;
+ offset = spk_shift_table[shift_info + 32];
+ /* double press? */
+ if (offset && key_info[offset])
+ new_key = key_info[offset];
+ }
+ last_keycode = keycode;
+ last_spk_jiffy = jiffies;
+ type = KT_SPKUP;
+ value = new_key;
+ }
+ }
+no_map:
+ if (type == KT_SPKUP && spk_special_handler == NULL) {
+ do_spkup(vc, new_key);
+ spk_close_press = 0;
+ ret = 1;
+ goto out;
+ }
+ if (up_flag || spk_killed || type == KT_SHIFT)
+ goto out;
+ spk_shut_up &= 0xfe;
+ kh = (value == KVAL(K_DOWN))
+ || (value == KVAL(K_UP))
+ || (value == KVAL(K_LEFT))
+ || (value == KVAL(K_RIGHT));
+ if ((cursor_track != read_all_mode) || !kh)
+ if (!spk_no_intr)
+ spk_do_flush();
+ if (spk_special_handler) {
+ if (type == KT_SPEC && value == 1) {
+ value = '\n';
+ type = KT_LATIN;
+ } else if (type == KT_LETTER)
+ type = KT_LATIN;
+ else if (value == 0x7f)
+ value = 8; /* make del = backspace */
+ ret = (*spk_special_handler) (vc, type, value, keycode);
+ spk_close_press = 0;
+ if (ret < 0)
+ bleep(9);
+ goto out;
+ }
+ last_keycode = 0;
+out:
+ spin_unlock_irqrestore(&speakup_info.spinlock, flags);
+ return ret;
+}
+
+static int keyboard_notifier_call(struct notifier_block *nb,
+ unsigned long code, void *_param)
+{
+ struct keyboard_notifier_param *param = _param;
+ struct vc_data *vc = param->vc;
+ int up = !param->down;
+ int ret = NOTIFY_OK;
+ static int keycode; /* to hold the current keycode */
+
+ if (vc->vc_mode == KD_GRAPHICS)
+ return ret;
+
+ /*
+ * First, determine whether we are handling a fake keypress on
+ * the current processor. If we are, then return NOTIFY_OK,
+ * to pass the keystroke up the chain. This prevents us from
+ * trying to take the Speakup lock while it is held by the
+ * processor on which the simulated keystroke was generated.
+ * Also, the simulated keystrokes should be ignored by Speakup.
+ */
+
+ if (speakup_fake_key_pressed())
+ return ret;
+
+ switch (code) {
+ case KBD_KEYCODE:
+ /* speakup requires keycode and keysym currently */
+ keycode = param->value;
+ break;
+ case KBD_UNBOUND_KEYCODE:
+ /* not used yet */
+ break;
+ case KBD_UNICODE:
+ /* not used yet */
+ break;
+ case KBD_KEYSYM:
+ if (speakup_key(vc, param->shift, keycode, param->value, up))
+ ret = NOTIFY_STOP;
+ else if (KTYP(param->value) == KT_CUR)
+ ret = pre_handle_cursor(vc, KVAL(param->value), up);
+ break;
+ case KBD_POST_KEYSYM:{
+ unsigned char type = KTYP(param->value) - 0xf0;
+ unsigned char val = KVAL(param->value);
+
+ switch (type) {
+ case KT_SHIFT:
+ do_handle_shift(vc, val, up);
+ break;
+ case KT_LATIN:
+ case KT_LETTER:
+ do_handle_latin(vc, val, up);
+ break;
+ case KT_CUR:
+ do_handle_cursor(vc, val, up);
+ break;
+ case KT_SPEC:
+ do_handle_spec(vc, val, up);
+ break;
+ }
+ break;
+ }
+ }
+ return ret;
+}
+
+static int vt_notifier_call(struct notifier_block *nb,
+ unsigned long code, void *_param)
+{
+ struct vt_notifier_param *param = _param;
+ struct vc_data *vc = param->vc;
+
+ switch (code) {
+ case VT_ALLOCATE:
+ if (vc->vc_mode == KD_TEXT)
+ speakup_allocate(vc);
+ break;
+ case VT_DEALLOCATE:
+ speakup_deallocate(vc);
+ break;
+ case VT_WRITE:
+ if (param->c == '\b')
+ speakup_bs(vc);
+ else if (param->c < 0x100) {
+ char d = param->c;
+
+ speakup_con_write(vc, &d, 1);
+ }
+ break;
+ case VT_UPDATE:
+ speakup_con_update(vc);
+ break;
+ }
+ return NOTIFY_OK;
+}
+
+/* called by: module_exit() */
+static void __exit speakup_exit(void)
+{
+ int i;
+
+ unregister_keyboard_notifier(&keyboard_notifier_block);
+ unregister_vt_notifier(&vt_notifier_block);
+ speakup_unregister_devsynth();
+ speakup_cancel_paste();
+ del_timer(&cursor_timer);
+ kthread_stop(speakup_task);
+ speakup_task = NULL;
+ mutex_lock(&spk_mutex);
+ synth_release();
+ mutex_unlock(&spk_mutex);
+
+ speakup_kobj_exit();
+
+ for (i = 0; i < MAX_NR_CONSOLES; i++)
+ kfree(speakup_console[i]);
+
+ speakup_remove_virtual_keyboard();
+
+ for (i = 0; i < MAXVARS; i++)
+ speakup_unregister_var(i);
+
+ for (i = 0; i < 256; i++) {
+ if (spk_characters[i] != spk_default_chars[i])
+ kfree(spk_characters[i]);
+ }
+
+ spk_free_user_msgs();
+}
+
+/* call by: module_init() */
+static int __init speakup_init(void)
+{
+ int i;
+ long err = 0;
+ struct st_spk_t *first_console;
+ struct vc_data *vc = vc_cons[fg_console].d;
+ struct var_t *var;
+
+ /* These first few initializations cannot fail. */
+ spk_initialize_msgs(); /* Initialize arrays for i18n. */
+ spk_reset_default_chars();
+ spk_reset_default_chartab();
+ spk_strlwr(synth_name);
+ spk_vars[0].u.n.high = vc->vc_cols;
+ for (var = spk_vars; var->var_id != MAXVARS; var++)
+ speakup_register_var(var);
+ for (var = synth_time_vars;
+ (var->var_id >= 0) && (var->var_id < MAXVARS); var++)
+ speakup_register_var(var);
+ for (i = 1; spk_punc_info[i].mask != 0; i++)
+ spk_set_mask_bits(NULL, i, 2);
+
+ spk_set_key_info(spk_key_defaults, spk_key_buf);
+
+ /* From here on out, initializations can fail. */
+ err = speakup_add_virtual_keyboard();
+ if (err)
+ goto error_virtkeyboard;
+
+ first_console = kzalloc(sizeof(*first_console), GFP_KERNEL);
+ if (!first_console) {
+ err = -ENOMEM;
+ goto error_alloc;
+ }
+
+ speakup_console[vc->vc_num] = first_console;
+ speakup_date(vc);
+
+ for (i = 0; i < MAX_NR_CONSOLES; i++)
+ if (vc_cons[i].d) {
+ err = speakup_allocate(vc_cons[i].d);
+ if (err)
+ goto error_kobjects;
+ }
+
+ if (spk_quiet_boot)
+ spk_shut_up |= 0x01;
+
+ err = speakup_kobj_init();
+ if (err)
+ goto error_kobjects;
+
+ synth_init(synth_name);
+ speakup_register_devsynth();
+ /*
+ * register_devsynth might fail, but this error is not fatal.
+ * /dev/synth is an extra feature; the rest of Speakup
+ * will work fine without it.
+ */
+
+ err = register_keyboard_notifier(&keyboard_notifier_block);
+ if (err)
+ goto error_kbdnotifier;
+ err = register_vt_notifier(&vt_notifier_block);
+ if (err)
+ goto error_vtnotifier;
+
+ speakup_task = kthread_create(speakup_thread, NULL, "speakup");
+
+ if (IS_ERR(speakup_task)) {
+ err = PTR_ERR(speakup_task);
+ goto error_task;
+ }
+
+ set_user_nice(speakup_task, 10);
+ wake_up_process(speakup_task);
+
+ pr_info("speakup %s: initialized\n", SPEAKUP_VERSION);
+ pr_info("synth name on entry is: %s\n", synth_name);
+ goto out;
+
+error_task:
+ unregister_vt_notifier(&vt_notifier_block);
+
+error_vtnotifier:
+ unregister_keyboard_notifier(&keyboard_notifier_block);
+ del_timer(&cursor_timer);
+
+error_kbdnotifier:
+ speakup_unregister_devsynth();
+ mutex_lock(&spk_mutex);
+ synth_release();
+ mutex_unlock(&spk_mutex);
+ speakup_kobj_exit();
+
+error_kobjects:
+ for (i = 0; i < MAX_NR_CONSOLES; i++)
+ kfree(speakup_console[i]);
+
+error_alloc:
+ speakup_remove_virtual_keyboard();
+
+error_virtkeyboard:
+ for (i = 0; i < MAXVARS; i++)
+ speakup_unregister_var(i);
+
+ for (i = 0; i < 256; i++) {
+ if (spk_characters[i] != spk_default_chars[i])
+ kfree(spk_characters[i]);
+ }
+
+ spk_free_user_msgs();
+
+out:
+ return err;
+}
+
+module_init(speakup_init);
+module_exit(speakup_exit);
diff --git a/drivers/staging/speakup/selection.c b/drivers/staging/speakup/selection.c
new file mode 100644
index 000000000..a0315701c
--- /dev/null
+++ b/drivers/staging/speakup/selection.c
@@ -0,0 +1,186 @@
+#include <linux/slab.h> /* for kmalloc */
+#include <linux/consolemap.h>
+#include <linux/interrupt.h>
+#include <linux/sched.h>
+#include <linux/device.h> /* for dev_warn */
+#include <linux/selection.h>
+#include <linux/workqueue.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <asm/cmpxchg.h>
+
+#include "speakup.h"
+
+/* ------ cut and paste ----- */
+/* Don't take this from <ctype.h>: 011-015 on the screen aren't spaces */
+#define ishardspace(c) ((c) == ' ')
+
+unsigned short spk_xs, spk_ys, spk_xe, spk_ye; /* our region points */
+
+/* Variables for selection control. */
+/* must not be deallocated */
+struct vc_data *spk_sel_cons;
+/* cleared by clear_selection */
+static int sel_start = -1;
+static int sel_end;
+static int sel_buffer_lth;
+static char *sel_buffer;
+
+static unsigned char sel_pos(int n)
+{
+ return inverse_translate(spk_sel_cons,
+ screen_glyph(spk_sel_cons, n), 0);
+}
+
+void speakup_clear_selection(void)
+{
+ sel_start = -1;
+}
+
+/* does screen address p correspond to character at LH/RH edge of screen? */
+static int atedge(const int p, int size_row)
+{
+ return !(p % size_row) || !((p + 2) % size_row);
+}
+
+/* constrain v such that v <= u */
+static unsigned short limit(const unsigned short v, const unsigned short u)
+{
+ return (v > u) ? u : v;
+}
+
+int speakup_set_selection(struct tty_struct *tty)
+{
+ int new_sel_start, new_sel_end;
+ char *bp, *obp;
+ int i, ps, pe;
+ struct vc_data *vc = vc_cons[fg_console].d;
+
+ spk_xs = limit(spk_xs, vc->vc_cols - 1);
+ spk_ys = limit(spk_ys, vc->vc_rows - 1);
+ spk_xe = limit(spk_xe, vc->vc_cols - 1);
+ spk_ye = limit(spk_ye, vc->vc_rows - 1);
+ ps = spk_ys * vc->vc_size_row + (spk_xs << 1);
+ pe = spk_ye * vc->vc_size_row + (spk_xe << 1);
+
+ if (ps > pe) {
+ /* make sel_start <= sel_end */
+ int tmp = ps;
+
+ ps = pe;
+ pe = tmp;
+ }
+
+ if (spk_sel_cons != vc_cons[fg_console].d) {
+ speakup_clear_selection();
+ spk_sel_cons = vc_cons[fg_console].d;
+ dev_warn(tty->dev,
+ "Selection: mark console not the same as cut\n");
+ return -EINVAL;
+ }
+
+ new_sel_start = ps;
+ new_sel_end = pe;
+
+ /* select to end of line if on trailing space */
+ if (new_sel_end > new_sel_start &&
+ !atedge(new_sel_end, vc->vc_size_row) &&
+ ishardspace(sel_pos(new_sel_end))) {
+ for (pe = new_sel_end + 2; ; pe += 2)
+ if (!ishardspace(sel_pos(pe)) ||
+ atedge(pe, vc->vc_size_row))
+ break;
+ if (ishardspace(sel_pos(pe)))
+ new_sel_end = pe;
+ }
+ if ((new_sel_start == sel_start) && (new_sel_end == sel_end))
+ return 0; /* no action required */
+
+ sel_start = new_sel_start;
+ sel_end = new_sel_end;
+ /* Allocate a new buffer before freeing the old one ... */
+ bp = kmalloc((sel_end-sel_start)/2+1, GFP_ATOMIC);
+ if (!bp) {
+ speakup_clear_selection();
+ return -ENOMEM;
+ }
+ kfree(sel_buffer);
+ sel_buffer = bp;
+
+ obp = bp;
+ for (i = sel_start; i <= sel_end; i += 2) {
+ *bp = sel_pos(i);
+ if (!ishardspace(*bp++))
+ obp = bp;
+ if (!((i + 2) % vc->vc_size_row)) {
+ /* strip trailing blanks from line and add newline,
+ unless non-space at end of line. */
+ if (obp != bp) {
+ bp = obp;
+ *bp++ = '\r';
+ }
+ obp = bp;
+ }
+ }
+ sel_buffer_lth = bp - sel_buffer;
+ return 0;
+}
+
+struct speakup_paste_work {
+ struct work_struct work;
+ struct tty_struct *tty;
+};
+
+static void __speakup_paste_selection(struct work_struct *work)
+{
+ struct speakup_paste_work *spw =
+ container_of(work, struct speakup_paste_work, work);
+ struct tty_struct *tty = xchg(&spw->tty, NULL);
+ struct vc_data *vc = (struct vc_data *) tty->driver_data;
+ int pasted = 0, count;
+ struct tty_ldisc *ld;
+ DECLARE_WAITQUEUE(wait, current);
+
+ ld = tty_ldisc_ref_wait(tty);
+ tty_buffer_lock_exclusive(&vc->port);
+
+ add_wait_queue(&vc->paste_wait, &wait);
+ while (sel_buffer && sel_buffer_lth > pasted) {
+ set_current_state(TASK_INTERRUPTIBLE);
+ if (test_bit(TTY_THROTTLED, &tty->flags)) {
+ schedule();
+ continue;
+ }
+ count = sel_buffer_lth - pasted;
+ count = tty_ldisc_receive_buf(ld, sel_buffer + pasted, NULL,
+ count);
+ pasted += count;
+ }
+ remove_wait_queue(&vc->paste_wait, &wait);
+ __set_current_state(TASK_RUNNING);
+
+ tty_buffer_unlock_exclusive(&vc->port);
+ tty_ldisc_deref(ld);
+ tty_kref_put(tty);
+}
+
+static struct speakup_paste_work speakup_paste_work = {
+ .work = __WORK_INITIALIZER(speakup_paste_work.work,
+ __speakup_paste_selection)
+};
+
+int speakup_paste_selection(struct tty_struct *tty)
+{
+ if (cmpxchg(&speakup_paste_work.tty, NULL, tty) != NULL)
+ return -EBUSY;
+
+ tty_kref_get(tty);
+ schedule_work_on(WORK_CPU_UNBOUND, &speakup_paste_work.work);
+ return 0;
+}
+
+void speakup_cancel_paste(void)
+{
+ cancel_work_sync(&speakup_paste_work.work);
+ tty_kref_put(speakup_paste_work.tty);
+}
diff --git a/drivers/staging/speakup/serialio.c b/drivers/staging/speakup/serialio.c
new file mode 100644
index 000000000..1d9d51bdf
--- /dev/null
+++ b/drivers/staging/speakup/serialio.c
@@ -0,0 +1,220 @@
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+
+#include "spk_types.h"
+#include "speakup.h"
+#include "spk_priv.h"
+#include "serialio.h"
+
+#ifndef SERIAL_PORT_DFNS
+#define SERIAL_PORT_DFNS
+#endif
+
+static void start_serial_interrupt(int irq);
+
+static const struct old_serial_port rs_table[] = {
+ SERIAL_PORT_DFNS
+};
+static const struct old_serial_port *serstate;
+static int timeouts;
+
+const struct old_serial_port *spk_serial_init(int index)
+{
+ int baud = 9600, quot = 0;
+ unsigned int cval = 0;
+ int cflag = CREAD | HUPCL | CLOCAL | B9600 | CS8;
+ const struct old_serial_port *ser = rs_table + index;
+ int err;
+
+ /* Divisor, bytesize and parity */
+ quot = ser->baud_base / baud;
+ cval = cflag & (CSIZE | CSTOPB);
+#if defined(__powerpc__) || defined(__alpha__)
+ cval >>= 8;
+#else /* !__powerpc__ && !__alpha__ */
+ cval >>= 4;
+#endif /* !__powerpc__ && !__alpha__ */
+ if (cflag & PARENB)
+ cval |= UART_LCR_PARITY;
+ if (!(cflag & PARODD))
+ cval |= UART_LCR_EPAR;
+ if (synth_request_region(ser->port, 8)) {
+ /* try to take it back. */
+ pr_info("Ports not available, trying to steal them\n");
+ __release_region(&ioport_resource, ser->port, 8);
+ err = synth_request_region(ser->port, 8);
+ if (err) {
+ pr_warn("Unable to allocate port at %x, errno %i",
+ ser->port, err);
+ return NULL;
+ }
+ }
+
+ /* Disable UART interrupts, set DTR and RTS high
+ * and set speed. */
+ outb(cval | UART_LCR_DLAB, ser->port + UART_LCR); /* set DLAB */
+ outb(quot & 0xff, ser->port + UART_DLL); /* LS of divisor */
+ outb(quot >> 8, ser->port + UART_DLM); /* MS of divisor */
+ outb(cval, ser->port + UART_LCR); /* reset DLAB */
+
+ /* Turn off Interrupts */
+ outb(0, ser->port + UART_IER);
+ outb(UART_MCR_DTR | UART_MCR_RTS, ser->port + UART_MCR);
+
+ /* If we read 0xff from the LSR, there is no UART here. */
+ if (inb(ser->port + UART_LSR) == 0xff) {
+ synth_release_region(ser->port, 8);
+ serstate = NULL;
+ return NULL;
+ }
+
+ mdelay(1);
+ speakup_info.port_tts = ser->port;
+ serstate = ser;
+
+ start_serial_interrupt(ser->irq);
+
+ return ser;
+}
+
+static irqreturn_t synth_readbuf_handler(int irq, void *dev_id)
+{
+ unsigned long flags;
+/*printk(KERN_ERR "in irq\n"); */
+/*pr_warn("in IRQ\n"); */
+ int c;
+
+ spin_lock_irqsave(&speakup_info.spinlock, flags);
+ while (inb_p(speakup_info.port_tts + UART_LSR) & UART_LSR_DR) {
+
+ c = inb_p(speakup_info.port_tts+UART_RX);
+ synth->read_buff_add((u_char) c);
+/*printk(KERN_ERR "c = %d\n", c); */
+/*pr_warn("C = %d\n", c); */
+ }
+ spin_unlock_irqrestore(&speakup_info.spinlock, flags);
+ return IRQ_HANDLED;
+}
+
+static void start_serial_interrupt(int irq)
+{
+ int rv;
+
+ if (synth->read_buff_add == NULL)
+ return;
+
+ rv = request_irq(irq, synth_readbuf_handler, IRQF_SHARED,
+ "serial", (void *) synth_readbuf_handler);
+
+ if (rv)
+ pr_err("Unable to request Speakup serial I R Q\n");
+ /* Set MCR */
+ outb(UART_MCR_DTR | UART_MCR_RTS | UART_MCR_OUT2,
+ speakup_info.port_tts + UART_MCR);
+ /* Turn on Interrupts */
+ outb(UART_IER_MSI|UART_IER_RLSI|UART_IER_RDI,
+ speakup_info.port_tts + UART_IER);
+ inb(speakup_info.port_tts+UART_LSR);
+ inb(speakup_info.port_tts+UART_RX);
+ inb(speakup_info.port_tts+UART_IIR);
+ inb(speakup_info.port_tts+UART_MSR);
+ outb(1, speakup_info.port_tts + UART_FCR); /* Turn FIFO On */
+}
+
+void spk_stop_serial_interrupt(void)
+{
+ if (speakup_info.port_tts == 0)
+ return;
+
+ if (synth->read_buff_add == NULL)
+ return;
+
+ /* Turn off interrupts */
+ outb(0, speakup_info.port_tts+UART_IER);
+ /* Free IRQ */
+ free_irq(serstate->irq, (void *) synth_readbuf_handler);
+}
+
+int spk_wait_for_xmitr(void)
+{
+ int tmout = SPK_XMITR_TIMEOUT;
+
+ if ((synth->alive) && (timeouts >= NUM_DISABLE_TIMEOUTS)) {
+ pr_warn("%s: too many timeouts, deactivating speakup\n",
+ synth->long_name);
+ synth->alive = 0;
+ /* No synth any more, so nobody will restart TTYs, and we thus
+ * need to do it ourselves. Now that there is no synth we can
+ * let application flood anyway */
+ speakup_start_ttys();
+ timeouts = 0;
+ return 0;
+ }
+ while (spk_serial_tx_busy()) {
+ if (--tmout == 0) {
+ pr_warn("%s: timed out (tx busy)\n", synth->long_name);
+ timeouts++;
+ return 0;
+ }
+ udelay(1);
+ }
+ tmout = SPK_CTS_TIMEOUT;
+ while (!((inb_p(speakup_info.port_tts + UART_MSR)) & UART_MSR_CTS)) {
+ /* CTS */
+ if (--tmout == 0) {
+ /* pr_warn("%s: timed out (cts)\n",
+ * synth->long_name); */
+ timeouts++;
+ return 0;
+ }
+ udelay(1);
+ }
+ timeouts = 0;
+ return 1;
+}
+
+unsigned char spk_serial_in(void)
+{
+ int tmout = SPK_SERIAL_TIMEOUT;
+
+ while (!(inb_p(speakup_info.port_tts + UART_LSR) & UART_LSR_DR)) {
+ if (--tmout == 0) {
+ pr_warn("time out while waiting for input.\n");
+ return 0xff;
+ }
+ udelay(1);
+ }
+ return inb_p(speakup_info.port_tts + UART_RX);
+}
+EXPORT_SYMBOL_GPL(spk_serial_in);
+
+unsigned char spk_serial_in_nowait(void)
+{
+ unsigned char lsr;
+
+ lsr = inb_p(speakup_info.port_tts + UART_LSR);
+ if (!(lsr & UART_LSR_DR))
+ return 0;
+ return inb_p(speakup_info.port_tts + UART_RX);
+}
+EXPORT_SYMBOL_GPL(spk_serial_in_nowait);
+
+int spk_serial_out(const char ch)
+{
+ if (synth->alive && spk_wait_for_xmitr()) {
+ outb_p(ch, speakup_info.port_tts);
+ return 1;
+ }
+ return 0;
+}
+EXPORT_SYMBOL_GPL(spk_serial_out);
+
+void spk_serial_release(void)
+{
+ if (speakup_info.port_tts == 0)
+ return;
+ synth_release_region(speakup_info.port_tts, 8);
+ speakup_info.port_tts = 0;
+}
+EXPORT_SYMBOL_GPL(spk_serial_release);
+
diff --git a/drivers/staging/speakup/serialio.h b/drivers/staging/speakup/serialio.h
new file mode 100644
index 000000000..1b399214e
--- /dev/null
+++ b/drivers/staging/speakup/serialio.h
@@ -0,0 +1,40 @@
+#ifndef _SPEAKUP_SERIAL_H
+#define _SPEAKUP_SERIAL_H
+
+#include <linux/serial.h> /* for rs_table, serial constants */
+#include <linux/serial_reg.h> /* for more serial constants */
+#ifndef __sparc__
+#include <linux/serial.h>
+#endif
+
+/*
+ * this is cut&paste from 8250.h. Get rid of the structure, the definitions
+ * and this whole broken driver.
+ */
+struct old_serial_port {
+ unsigned int uart; /* unused */
+ unsigned int baud_base;
+ unsigned int port;
+ unsigned int irq;
+ unsigned int flags; /* unused */
+};
+
+/* countdown values for serial timeouts in us */
+#define SPK_SERIAL_TIMEOUT 100000
+/* countdown values transmitter/dsr timeouts in us */
+#define SPK_XMITR_TIMEOUT 100000
+/* countdown values cts timeouts in us */
+#define SPK_CTS_TIMEOUT 100000
+/* check ttyS0 ... ttyS3 */
+#define SPK_LO_TTY 0
+#define SPK_HI_TTY 3
+/* # of timeouts permitted before disable */
+#define NUM_DISABLE_TIMEOUTS 3
+/* buffer timeout in ms */
+#define SPK_TIMEOUT 100
+#define BOTH_EMPTY (UART_LSR_TEMT | UART_LSR_THRE)
+
+#define spk_serial_tx_busy() \
+ ((inb(speakup_info.port_tts + UART_LSR) & BOTH_EMPTY) != BOTH_EMPTY)
+
+#endif
diff --git a/drivers/staging/speakup/speakup.h b/drivers/staging/speakup/speakup.h
new file mode 100644
index 000000000..a7f496242
--- /dev/null
+++ b/drivers/staging/speakup/speakup.h
@@ -0,0 +1,123 @@
+#ifndef _SPEAKUP_H
+#define _SPEAKUP_H
+
+#include "spk_types.h"
+#include "i18n.h"
+
+#define SPEAKUP_VERSION "3.1.6"
+#define KEY_MAP_VER 119
+#define SHIFT_TBL_SIZE 64
+#define MAX_DESC_LEN 72
+
+/* proc permissions */
+#define USER_R (S_IFREG|S_IRUGO)
+#define USER_W (S_IFREG|S_IWUGO)
+
+#define TOGGLE_0 .u.n = {NULL, 0, 0, 1, 0, 0, NULL }
+#define TOGGLE_1 .u.n = {NULL, 1, 0, 1, 0, 0, NULL }
+#define MAXVARLEN 15
+
+#define SYNTH_OK 0x0001
+#define B_ALPHA 0x0002
+#define ALPHA 0x0003
+#define B_CAP 0x0004
+#define A_CAP 0x0007
+#define B_NUM 0x0008
+#define NUM 0x0009
+#define ALPHANUM (B_ALPHA|B_NUM)
+#define SOME 0x0010
+#define MOST 0x0020
+#define PUNC 0x0040
+#define A_PUNC 0x0041
+#define B_WDLM 0x0080
+#define WDLM 0x0081
+#define B_EXNUM 0x0100
+#define CH_RPT 0x0200
+#define B_CTL 0x0400
+#define A_CTL (B_CTL+SYNTH_OK)
+#define B_SYM 0x0800
+#define B_CAPSYM (B_CAP|B_SYM)
+
+#define IS_WDLM(x) (spk_chartab[((u_char)x)]&B_WDLM)
+#define IS_CHAR(x, type) (spk_chartab[((u_char)x)]&type)
+#define IS_TYPE(x, type) ((spk_chartab[((u_char)x)]&type) == type)
+
+extern int speakup_thread(void *data);
+extern void spk_reset_default_chars(void);
+extern void spk_reset_default_chartab(void);
+extern void synth_start(void);
+void synth_insert_next_index(int sent_num);
+void spk_reset_index_count(int sc);
+void spk_get_index_count(int *linecount, int *sentcount);
+extern int spk_set_key_info(const u_char *key_info, u_char *k_buffer);
+extern char *spk_strlwr(char *s);
+extern char *spk_s2uchar(char *start, char *dest);
+extern int speakup_kobj_init(void);
+extern void speakup_kobj_exit(void);
+extern int spk_chartab_get_value(char *keyword);
+extern void speakup_register_var(struct var_t *var);
+extern void speakup_unregister_var(enum var_id_t var_id);
+extern struct st_var_header *spk_get_var_header(enum var_id_t var_id);
+extern struct st_var_header *spk_var_header_by_name(const char *name);
+extern struct punc_var_t *spk_get_punc_var(enum var_id_t var_id);
+extern int spk_set_num_var(int val, struct st_var_header *var, int how);
+extern int spk_set_string_var(const char *page, struct st_var_header *var,
+ int len);
+extern int spk_set_mask_bits(const char *input, const int which, const int how);
+extern special_func spk_special_handler;
+extern int spk_handle_help(struct vc_data *vc, u_char type, u_char ch,
+ u_short key);
+extern int synth_init(char *name);
+extern void synth_release(void);
+
+extern void spk_do_flush(void);
+extern void speakup_start_ttys(void);
+extern void synth_buffer_add(char ch);
+extern void synth_buffer_clear(void);
+extern void speakup_clear_selection(void);
+extern int speakup_set_selection(struct tty_struct *tty);
+extern int speakup_paste_selection(struct tty_struct *tty);
+extern void speakup_cancel_paste(void);
+extern void speakup_register_devsynth(void);
+extern void speakup_unregister_devsynth(void);
+extern void synth_write(const char *buf, size_t count);
+extern int synth_supports_indexing(void);
+
+extern struct vc_data *spk_sel_cons;
+extern unsigned short spk_xs, spk_ys, spk_xe, spk_ye; /* our region points */
+
+extern wait_queue_head_t speakup_event;
+extern struct kobject *speakup_kobj;
+extern struct task_struct *speakup_task;
+extern const u_char spk_key_defaults[];
+
+/* Protect speakup synthesizer list */
+extern struct mutex spk_mutex;
+extern struct st_spk_t *speakup_console[];
+extern struct spk_synth *synth;
+extern char spk_pitch_buff[];
+extern u_char *spk_our_keys[];
+extern short spk_punc_masks[];
+extern char spk_str_caps_start[], spk_str_caps_stop[];
+extern const struct st_bits_data spk_punc_info[];
+extern u_char spk_key_buf[600];
+extern char *spk_characters[];
+extern char *spk_default_chars[];
+extern u_short spk_chartab[];
+extern int spk_no_intr, spk_say_ctrl, spk_say_word_ctl, spk_punc_level;
+extern int spk_reading_punc, spk_attrib_bleep, spk_bleeps;
+extern int spk_bleep_time, spk_bell_pos;
+extern int spk_spell_delay, spk_key_echo;
+extern short spk_punc_mask;
+extern short spk_pitch_shift, synth_flags;
+extern bool spk_quiet_boot;
+extern char *synth_name;
+extern struct bleep spk_unprocessed_sound;
+
+/* Prototypes from fakekey.c. */
+int speakup_add_virtual_keyboard(void);
+void speakup_remove_virtual_keyboard(void);
+void speakup_fake_down_arrow(void);
+bool speakup_fake_key_pressed(void);
+
+#endif
diff --git a/drivers/staging/speakup/speakup_acnt.h b/drivers/staging/speakup/speakup_acnt.h
new file mode 100644
index 000000000..6376fca9e
--- /dev/null
+++ b/drivers/staging/speakup/speakup_acnt.h
@@ -0,0 +1,16 @@
+/* speakup_acntpc.h - header file for speakups Accent-PC driver. */
+
+#define SYNTH_IO_EXTENT 0x02
+
+#define SYNTH_CLEAR 0x18 /* stops speech */
+
+ /* Port Status Flags */
+#define SYNTH_READABLE 0x01 /* mask for bit which is nonzero if a
+ byte can be read from the data port */
+#define SYNTH_WRITABLE 0x02 /* mask for RDY bit, which when set to
+ 1, indicates the data port is ready
+ to accept a byte of data. */
+#define SYNTH_QUIET 'S' /* synth is not speaking */
+#define SYNTH_FULL 'F' /* synth is full. */
+#define SYNTH_ALMOST_EMPTY 'M' /* synth has less than 2 seconds of text left */
+#define SYNTH_SPEAKING 's' /* synth is speaking and has a fare way to go */
diff --git a/drivers/staging/speakup/speakup_acntpc.c b/drivers/staging/speakup/speakup_acntpc.c
new file mode 100644
index 000000000..f41889392
--- /dev/null
+++ b/drivers/staging/speakup/speakup_acntpc.c
@@ -0,0 +1,328 @@
+/*
+ * written by: Kirk Reiser <kirk@braille.uwo.ca>
+ * this version considerably modified by David Borowski, david575@rogers.com
+ *
+ * Copyright (C) 1998-99 Kirk Reiser.
+ * Copyright (C) 2003 David Borowski.
+ *
+ * This program is free software; you can redistribute it and/or 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 code is specificly written as a driver for the speakup screenreview
+ * package and is not a general device driver.
+ * This driver is for the Aicom Acent PC internal synthesizer.
+ */
+
+#include <linux/jiffies.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/kthread.h>
+
+#include "spk_priv.h"
+#include "serialio.h"
+#include "speakup.h"
+#include "speakup_acnt.h" /* local header file for Accent values */
+
+#define DRV_VERSION "2.10"
+#define PROCSPEECH '\r'
+
+static int synth_probe(struct spk_synth *synth);
+static void accent_release(void);
+static const char *synth_immediate(struct spk_synth *synth, const char *buf);
+static void do_catch_up(struct spk_synth *synth);
+static void synth_flush(struct spk_synth *synth);
+
+static int synth_port_control;
+static int port_forced;
+static unsigned int synth_portlist[] = { 0x2a8, 0 };
+
+static struct var_t vars[] = {
+ { CAPS_START, .u.s = {"\033P8" } },
+ { CAPS_STOP, .u.s = {"\033P5" } },
+ { RATE, .u.n = {"\033R%c", 9, 0, 17, 0, 0, "0123456789abcdefgh" } },
+ { PITCH, .u.n = {"\033P%d", 5, 0, 9, 0, 0, NULL } },
+ { VOL, .u.n = {"\033A%d", 5, 0, 9, 0, 0, NULL } },
+ { TONE, .u.n = {"\033V%d", 5, 0, 9, 0, 0, NULL } },
+ { DIRECT, .u.n = {NULL, 0, 0, 1, 0, 0, NULL } },
+ V_LAST_VAR
+};
+
+/*
+ * These attributes will appear in /sys/accessibility/speakup/acntpc.
+ */
+static struct kobj_attribute caps_start_attribute =
+ __ATTR(caps_start, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
+static struct kobj_attribute caps_stop_attribute =
+ __ATTR(caps_stop, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
+static struct kobj_attribute pitch_attribute =
+ __ATTR(pitch, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
+static struct kobj_attribute rate_attribute =
+ __ATTR(rate, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
+static struct kobj_attribute tone_attribute =
+ __ATTR(tone, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
+static struct kobj_attribute vol_attribute =
+ __ATTR(vol, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
+
+static struct kobj_attribute delay_time_attribute =
+ __ATTR(delay_time, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
+static struct kobj_attribute direct_attribute =
+ __ATTR(direct, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
+static struct kobj_attribute full_time_attribute =
+ __ATTR(full_time, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
+static struct kobj_attribute jiffy_delta_attribute =
+ __ATTR(jiffy_delta, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
+static struct kobj_attribute trigger_time_attribute =
+ __ATTR(trigger_time, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
+
+/*
+ * Create a group of attributes so that we can create and destroy them all
+ * at once.
+ */
+static struct attribute *synth_attrs[] = {
+ &caps_start_attribute.attr,
+ &caps_stop_attribute.attr,
+ &pitch_attribute.attr,
+ &rate_attribute.attr,
+ &tone_attribute.attr,
+ &vol_attribute.attr,
+ &delay_time_attribute.attr,
+ &direct_attribute.attr,
+ &full_time_attribute.attr,
+ &jiffy_delta_attribute.attr,
+ &trigger_time_attribute.attr,
+ NULL, /* need to NULL terminate the list of attributes */
+};
+
+static struct spk_synth synth_acntpc = {
+ .name = "acntpc",
+ .version = DRV_VERSION,
+ .long_name = "Accent PC",
+ .init = "\033=X \033Oi\033T2\033=M\033N1\n",
+ .procspeech = PROCSPEECH,
+ .clear = SYNTH_CLEAR,
+ .delay = 500,
+ .trigger = 50,
+ .jiffies = 50,
+ .full = 1000,
+ .startup = SYNTH_START,
+ .checkval = SYNTH_CHECK,
+ .vars = vars,
+ .probe = synth_probe,
+ .release = accent_release,
+ .synth_immediate = synth_immediate,
+ .catch_up = do_catch_up,
+ .flush = synth_flush,
+ .is_alive = spk_synth_is_alive_nop,
+ .synth_adjust = NULL,
+ .read_buff_add = NULL,
+ .get_index = NULL,
+ .indexing = {
+ .command = NULL,
+ .lowindex = 0,
+ .highindex = 0,
+ .currindex = 0,
+ },
+ .attributes = {
+ .attrs = synth_attrs,
+ .name = "acntpc",
+ },
+};
+
+static inline bool synth_writable(void)
+{
+ return inb_p(synth_port_control) & SYNTH_WRITABLE;
+}
+
+static inline bool synth_full(void)
+{
+ return inb_p(speakup_info.port_tts + UART_RX) == 'F';
+}
+
+static const char *synth_immediate(struct spk_synth *synth, const char *buf)
+{
+ u_char ch;
+
+ while ((ch = *buf)) {
+ int timeout = SPK_XMITR_TIMEOUT;
+
+ if (ch == '\n')
+ ch = PROCSPEECH;
+ if (synth_full())
+ return buf;
+ while (synth_writable()) {
+ if (!--timeout)
+ return buf;
+ udelay(1);
+ }
+ outb_p(ch, speakup_info.port_tts);
+ buf++;
+ }
+ return NULL;
+}
+
+static void do_catch_up(struct spk_synth *synth)
+{
+ u_char ch;
+ unsigned long flags;
+ unsigned long jiff_max;
+ int timeout;
+ int delay_time_val;
+ int jiffy_delta_val;
+ int full_time_val;
+ struct var_t *delay_time;
+ struct var_t *full_time;
+ struct var_t *jiffy_delta;
+
+ jiffy_delta = spk_get_var(JIFFY);
+ delay_time = spk_get_var(DELAY);
+ full_time = spk_get_var(FULL);
+
+ spin_lock_irqsave(&speakup_info.spinlock, flags);
+ jiffy_delta_val = jiffy_delta->u.n.value;
+ spin_unlock_irqrestore(&speakup_info.spinlock, flags);
+
+ jiff_max = jiffies + jiffy_delta_val;
+ while (!kthread_should_stop()) {
+ spin_lock_irqsave(&speakup_info.spinlock, flags);
+ if (speakup_info.flushing) {
+ speakup_info.flushing = 0;
+ spin_unlock_irqrestore(&speakup_info.spinlock, flags);
+ synth->flush(synth);
+ continue;
+ }
+ if (synth_buffer_empty()) {
+ spin_unlock_irqrestore(&speakup_info.spinlock, flags);
+ break;
+ }
+ set_current_state(TASK_INTERRUPTIBLE);
+ full_time_val = full_time->u.n.value;
+ spin_unlock_irqrestore(&speakup_info.spinlock, flags);
+ if (synth_full()) {
+ schedule_timeout(msecs_to_jiffies(full_time_val));
+ continue;
+ }
+ set_current_state(TASK_RUNNING);
+ timeout = SPK_XMITR_TIMEOUT;
+ while (synth_writable()) {
+ if (!--timeout)
+ break;
+ udelay(1);
+ }
+ spin_lock_irqsave(&speakup_info.spinlock, flags);
+ ch = synth_buffer_getc();
+ spin_unlock_irqrestore(&speakup_info.spinlock, flags);
+ if (ch == '\n')
+ ch = PROCSPEECH;
+ outb_p(ch, speakup_info.port_tts);
+ if (time_after_eq(jiffies, jiff_max) && ch == SPACE) {
+ timeout = SPK_XMITR_TIMEOUT;
+ while (synth_writable()) {
+ if (!--timeout)
+ break;
+ udelay(1);
+ }
+ outb_p(PROCSPEECH, speakup_info.port_tts);
+ spin_lock_irqsave(&speakup_info.spinlock, flags);
+ jiffy_delta_val = jiffy_delta->u.n.value;
+ delay_time_val = delay_time->u.n.value;
+ spin_unlock_irqrestore(&speakup_info.spinlock, flags);
+ schedule_timeout(msecs_to_jiffies(delay_time_val));
+ jiff_max = jiffies+jiffy_delta_val;
+ }
+ }
+ timeout = SPK_XMITR_TIMEOUT;
+ while (synth_writable()) {
+ if (!--timeout)
+ break;
+ udelay(1);
+ }
+ outb_p(PROCSPEECH, speakup_info.port_tts);
+}
+
+static void synth_flush(struct spk_synth *synth)
+{
+ outb_p(SYNTH_CLEAR, speakup_info.port_tts);
+}
+
+static int synth_probe(struct spk_synth *synth)
+{
+ unsigned int port_val = 0;
+ int i = 0;
+
+ pr_info("Probing for %s.\n", synth->long_name);
+ if (port_forced) {
+ speakup_info.port_tts = port_forced;
+ pr_info("probe forced to %x by kernel command line\n",
+ speakup_info.port_tts);
+ if (synth_request_region(speakup_info.port_tts-1,
+ SYNTH_IO_EXTENT)) {
+ pr_warn("sorry, port already reserved\n");
+ return -EBUSY;
+ }
+ port_val = inw(speakup_info.port_tts-1);
+ synth_port_control = speakup_info.port_tts-1;
+ } else {
+ for (i = 0; synth_portlist[i]; i++) {
+ if (synth_request_region(synth_portlist[i],
+ SYNTH_IO_EXTENT)) {
+ pr_warn
+ ("request_region: failed with 0x%x, %d\n",
+ synth_portlist[i], SYNTH_IO_EXTENT);
+ continue;
+ }
+ port_val = inw(synth_portlist[i]) & 0xfffc;
+ if (port_val == 0x53fc) {
+ /* 'S' and out&input bits */
+ synth_port_control = synth_portlist[i];
+ speakup_info.port_tts = synth_port_control+1;
+ break;
+ }
+ }
+ }
+ port_val &= 0xfffc;
+ if (port_val != 0x53fc) {
+ /* 'S' and out&input bits */
+ pr_info("%s: not found\n", synth->long_name);
+ synth_release_region(synth_port_control, SYNTH_IO_EXTENT);
+ synth_port_control = 0;
+ return -ENODEV;
+ }
+ pr_info("%s: %03x-%03x, driver version %s,\n", synth->long_name,
+ synth_port_control, synth_port_control+SYNTH_IO_EXTENT-1,
+ synth->version);
+ synth->alive = 1;
+ return 0;
+}
+
+static void accent_release(void)
+{
+ if (speakup_info.port_tts)
+ synth_release_region(speakup_info.port_tts-1, SYNTH_IO_EXTENT);
+ speakup_info.port_tts = 0;
+}
+
+module_param_named(port, port_forced, int, S_IRUGO);
+module_param_named(start, synth_acntpc.startup, short, S_IRUGO);
+
+MODULE_PARM_DESC(port, "Set the port for the synthesizer (override probing).");
+MODULE_PARM_DESC(start, "Start the synthesizer once it is loaded.");
+
+module_spk_synth(synth_acntpc);
+
+MODULE_AUTHOR("Kirk Reiser <kirk@braille.uwo.ca>");
+MODULE_AUTHOR("David Borowski");
+MODULE_DESCRIPTION("Speakup support for Accent PC synthesizer");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(DRV_VERSION);
+
diff --git a/drivers/staging/speakup/speakup_acntsa.c b/drivers/staging/speakup/speakup_acntsa.c
new file mode 100644
index 000000000..af2690f38
--- /dev/null
+++ b/drivers/staging/speakup/speakup_acntsa.c
@@ -0,0 +1,153 @@
+/*
+ * originally written by: Kirk Reiser <kirk@braille.uwo.ca>
+* this version considerably modified by David Borowski, david575@rogers.com
+ *
+ * Copyright (C) 1998-99 Kirk Reiser.
+ * Copyright (C) 2003 David Borowski.
+ *
+ * This program is free software; you can redistribute it and/or 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 code is specificly written as a driver for the speakup screenreview
+ * package and is not a general device driver.
+ */
+
+#include "spk_priv.h"
+#include "speakup.h"
+#include "speakup_acnt.h" /* local header file for Accent values */
+
+#define DRV_VERSION "2.11"
+#define PROCSPEECH '\r'
+
+static int synth_probe(struct spk_synth *synth);
+
+static struct var_t vars[] = {
+ { CAPS_START, .u.s = {"\033P8" } },
+ { CAPS_STOP, .u.s = {"\033P5" } },
+ { RATE, .u.n = {"\033R%c", 9, 0, 17, 0, 0, "0123456789abcdefgh" } },
+ { PITCH, .u.n = {"\033P%d", 5, 0, 9, 0, 0, NULL } },
+ { VOL, .u.n = {"\033A%d", 9, 0, 9, 0, 0, NULL } },
+ { TONE, .u.n = {"\033V%d", 5, 0, 9, 0, 0, NULL } },
+ { DIRECT, .u.n = {NULL, 0, 0, 1, 0, 0, NULL } },
+ V_LAST_VAR
+};
+
+/*
+ * These attributes will appear in /sys/accessibility/speakup/acntsa.
+ */
+static struct kobj_attribute caps_start_attribute =
+ __ATTR(caps_start, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
+static struct kobj_attribute caps_stop_attribute =
+ __ATTR(caps_stop, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
+static struct kobj_attribute pitch_attribute =
+ __ATTR(pitch, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
+static struct kobj_attribute rate_attribute =
+ __ATTR(rate, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
+static struct kobj_attribute tone_attribute =
+ __ATTR(tone, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
+static struct kobj_attribute vol_attribute =
+ __ATTR(vol, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
+
+static struct kobj_attribute delay_time_attribute =
+ __ATTR(delay_time, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
+static struct kobj_attribute direct_attribute =
+ __ATTR(direct, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
+static struct kobj_attribute full_time_attribute =
+ __ATTR(full_time, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
+static struct kobj_attribute jiffy_delta_attribute =
+ __ATTR(jiffy_delta, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
+static struct kobj_attribute trigger_time_attribute =
+ __ATTR(trigger_time, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
+
+/*
+ * Create a group of attributes so that we can create and destroy them all
+ * at once.
+ */
+static struct attribute *synth_attrs[] = {
+ &caps_start_attribute.attr,
+ &caps_stop_attribute.attr,
+ &pitch_attribute.attr,
+ &rate_attribute.attr,
+ &tone_attribute.attr,
+ &vol_attribute.attr,
+ &delay_time_attribute.attr,
+ &direct_attribute.attr,
+ &full_time_attribute.attr,
+ &jiffy_delta_attribute.attr,
+ &trigger_time_attribute.attr,
+ NULL, /* need to NULL terminate the list of attributes */
+};
+
+static struct spk_synth synth_acntsa = {
+ .name = "acntsa",
+ .version = DRV_VERSION,
+ .long_name = "Accent-SA",
+ .init = "\033T2\033=M\033Oi\033N1\n",
+ .procspeech = PROCSPEECH,
+ .clear = SYNTH_CLEAR,
+ .delay = 400,
+ .trigger = 50,
+ .jiffies = 30,
+ .full = 40000,
+ .startup = SYNTH_START,
+ .checkval = SYNTH_CHECK,
+ .vars = vars,
+ .probe = synth_probe,
+ .release = spk_serial_release,
+ .synth_immediate = spk_synth_immediate,
+ .catch_up = spk_do_catch_up,
+ .flush = spk_synth_flush,
+ .is_alive = spk_synth_is_alive_restart,
+ .synth_adjust = NULL,
+ .read_buff_add = NULL,
+ .get_index = NULL,
+ .indexing = {
+ .command = NULL,
+ .lowindex = 0,
+ .highindex = 0,
+ .currindex = 0,
+ },
+ .attributes = {
+ .attrs = synth_attrs,
+ .name = "acntsa",
+ },
+};
+
+static int synth_probe(struct spk_synth *synth)
+{
+ int failed;
+
+ failed = spk_serial_synth_probe(synth);
+ if (failed == 0) {
+ spk_synth_immediate(synth, "\033=R\r");
+ mdelay(100);
+ }
+ synth->alive = !failed;
+ return failed;
+}
+
+module_param_named(ser, synth_acntsa.ser, int, S_IRUGO);
+module_param_named(start, synth_acntsa.startup, short, S_IRUGO);
+
+MODULE_PARM_DESC(ser, "Set the serial port for the synthesizer (0-based).");
+MODULE_PARM_DESC(start, "Start the synthesizer once it is loaded.");
+
+module_spk_synth(synth_acntsa);
+
+MODULE_AUTHOR("Kirk Reiser <kirk@braille.uwo.ca>");
+MODULE_AUTHOR("David Borowski");
+MODULE_DESCRIPTION("Speakup support for Accent SA synthesizer");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(DRV_VERSION);
+
diff --git a/drivers/staging/speakup/speakup_apollo.c b/drivers/staging/speakup/speakup_apollo.c
new file mode 100644
index 000000000..51788f7d4
--- /dev/null
+++ b/drivers/staging/speakup/speakup_apollo.c
@@ -0,0 +1,217 @@
+/*
+ * originally written by: Kirk Reiser <kirk@braille.uwo.ca>
+* this version considerably modified by David Borowski, david575@rogers.com
+ *
+ * Copyright (C) 1998-99 Kirk Reiser.
+ * Copyright (C) 2003 David Borowski.
+ *
+ * This program is free software; you can redistribute it and/or 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 code is specificly written as a driver for the speakup screenreview
+ * package and is not a general device driver.
+ */
+#include <linux/jiffies.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/kthread.h>
+
+#include "spk_priv.h"
+#include "serialio.h"
+#include "speakup.h"
+
+#define DRV_VERSION "2.21"
+#define SYNTH_CLEAR 0x18
+#define PROCSPEECH '\r'
+
+static void do_catch_up(struct spk_synth *synth);
+
+static struct var_t vars[] = {
+ { CAPS_START, .u.s = {"cap, " } },
+ { CAPS_STOP, .u.s = {"" } },
+ { RATE, .u.n = {"@W%d", 6, 1, 9, 0, 0, NULL } },
+ { PITCH, .u.n = {"@F%x", 10, 0, 15, 0, 0, NULL } },
+ { VOL, .u.n = {"@A%x", 10, 0, 15, 0, 0, NULL } },
+ { VOICE, .u.n = {"@V%d", 1, 1, 6, 0, 0, NULL } },
+ { LANG, .u.n = {"@=%d,", 1, 1, 4, 0, 0, NULL } },
+ { DIRECT, .u.n = {NULL, 0, 0, 1, 0, 0, NULL } },
+ V_LAST_VAR
+};
+
+/*
+ * These attributes will appear in /sys/accessibility/speakup/apollo.
+ */
+static struct kobj_attribute caps_start_attribute =
+ __ATTR(caps_start, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
+static struct kobj_attribute caps_stop_attribute =
+ __ATTR(caps_stop, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
+static struct kobj_attribute lang_attribute =
+ __ATTR(lang, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
+static struct kobj_attribute pitch_attribute =
+ __ATTR(pitch, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
+static struct kobj_attribute rate_attribute =
+ __ATTR(rate, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
+static struct kobj_attribute voice_attribute =
+ __ATTR(voice, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
+static struct kobj_attribute vol_attribute =
+ __ATTR(vol, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
+
+static struct kobj_attribute delay_time_attribute =
+ __ATTR(delay_time, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
+static struct kobj_attribute direct_attribute =
+ __ATTR(direct, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
+static struct kobj_attribute full_time_attribute =
+ __ATTR(full_time, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
+static struct kobj_attribute jiffy_delta_attribute =
+ __ATTR(jiffy_delta, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
+static struct kobj_attribute trigger_time_attribute =
+ __ATTR(trigger_time, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
+
+/*
+ * Create a group of attributes so that we can create and destroy them all
+ * at once.
+ */
+static struct attribute *synth_attrs[] = {
+ &caps_start_attribute.attr,
+ &caps_stop_attribute.attr,
+ &lang_attribute.attr,
+ &pitch_attribute.attr,
+ &rate_attribute.attr,
+ &voice_attribute.attr,
+ &vol_attribute.attr,
+ &delay_time_attribute.attr,
+ &direct_attribute.attr,
+ &full_time_attribute.attr,
+ &jiffy_delta_attribute.attr,
+ &trigger_time_attribute.attr,
+ NULL, /* need to NULL terminate the list of attributes */
+};
+
+static struct spk_synth synth_apollo = {
+ .name = "apollo",
+ .version = DRV_VERSION,
+ .long_name = "Apollo",
+ .init = "@R3@D0@K1\r",
+ .procspeech = PROCSPEECH,
+ .clear = SYNTH_CLEAR,
+ .delay = 500,
+ .trigger = 50,
+ .jiffies = 50,
+ .full = 40000,
+ .startup = SYNTH_START,
+ .checkval = SYNTH_CHECK,
+ .vars = vars,
+ .probe = spk_serial_synth_probe,
+ .release = spk_serial_release,
+ .synth_immediate = spk_synth_immediate,
+ .catch_up = do_catch_up,
+ .flush = spk_synth_flush,
+ .is_alive = spk_synth_is_alive_restart,
+ .synth_adjust = NULL,
+ .read_buff_add = NULL,
+ .get_index = NULL,
+ .indexing = {
+ .command = NULL,
+ .lowindex = 0,
+ .highindex = 0,
+ .currindex = 0,
+ },
+ .attributes = {
+ .attrs = synth_attrs,
+ .name = "apollo",
+ },
+};
+
+static void do_catch_up(struct spk_synth *synth)
+{
+ u_char ch;
+ unsigned long flags;
+ unsigned long jiff_max;
+ struct var_t *jiffy_delta;
+ struct var_t *delay_time;
+ struct var_t *full_time;
+ int full_time_val = 0;
+ int delay_time_val = 0;
+ int jiffy_delta_val = 0;
+
+ jiffy_delta = spk_get_var(JIFFY);
+ delay_time = spk_get_var(DELAY);
+ full_time = spk_get_var(FULL);
+ spin_lock_irqsave(&speakup_info.spinlock, flags);
+ jiffy_delta_val = jiffy_delta->u.n.value;
+ spin_unlock_irqrestore(&speakup_info.spinlock, flags);
+ jiff_max = jiffies + jiffy_delta_val;
+
+ while (!kthread_should_stop()) {
+ spin_lock_irqsave(&speakup_info.spinlock, flags);
+ jiffy_delta_val = jiffy_delta->u.n.value;
+ full_time_val = full_time->u.n.value;
+ delay_time_val = delay_time->u.n.value;
+ if (speakup_info.flushing) {
+ speakup_info.flushing = 0;
+ spin_unlock_irqrestore(&speakup_info.spinlock, flags);
+ synth->flush(synth);
+ continue;
+ }
+ if (synth_buffer_empty()) {
+ spin_unlock_irqrestore(&speakup_info.spinlock, flags);
+ break;
+ }
+ ch = synth_buffer_peek();
+ set_current_state(TASK_INTERRUPTIBLE);
+ full_time_val = full_time->u.n.value;
+ spin_unlock_irqrestore(&speakup_info.spinlock, flags);
+ if (!spk_serial_out(ch)) {
+ outb(UART_MCR_DTR, speakup_info.port_tts + UART_MCR);
+ outb(UART_MCR_DTR | UART_MCR_RTS,
+ speakup_info.port_tts + UART_MCR);
+ schedule_timeout(msecs_to_jiffies(full_time_val));
+ continue;
+ }
+ if (time_after_eq(jiffies, jiff_max) && (ch == SPACE)) {
+ spin_lock_irqsave(&speakup_info.spinlock, flags);
+ jiffy_delta_val = jiffy_delta->u.n.value;
+ full_time_val = full_time->u.n.value;
+ delay_time_val = delay_time->u.n.value;
+ spin_unlock_irqrestore(&speakup_info.spinlock, flags);
+ if (spk_serial_out(synth->procspeech))
+ schedule_timeout(msecs_to_jiffies
+ (delay_time_val));
+ else
+ schedule_timeout(msecs_to_jiffies
+ (full_time_val));
+ jiff_max = jiffies + jiffy_delta_val;
+ }
+ set_current_state(TASK_RUNNING);
+ spin_lock_irqsave(&speakup_info.spinlock, flags);
+ synth_buffer_getc();
+ spin_unlock_irqrestore(&speakup_info.spinlock, flags);
+ }
+ spk_serial_out(PROCSPEECH);
+}
+
+module_param_named(ser, synth_apollo.ser, int, S_IRUGO);
+module_param_named(start, synth_apollo.startup, short, S_IRUGO);
+
+MODULE_PARM_DESC(ser, "Set the serial port for the synthesizer (0-based).");
+MODULE_PARM_DESC(start, "Start the synthesizer once it is loaded.");
+
+module_spk_synth(synth_apollo);
+
+MODULE_AUTHOR("Kirk Reiser <kirk@braille.uwo.ca>");
+MODULE_AUTHOR("David Borowski");
+MODULE_DESCRIPTION("Speakup support for Apollo II synthesizer");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(DRV_VERSION);
+
diff --git a/drivers/staging/speakup/speakup_audptr.c b/drivers/staging/speakup/speakup_audptr.c
new file mode 100644
index 000000000..ea89e36ec
--- /dev/null
+++ b/drivers/staging/speakup/speakup_audptr.c
@@ -0,0 +1,187 @@
+/*
+ * originally written by: Kirk Reiser <kirk@braille.uwo.ca>
+ * this version considerably modified by David Borowski, david575@rogers.com
+ *
+ * Copyright (C) 1998-99 Kirk Reiser.
+ * Copyright (C) 2003 David Borowski.
+ *
+ * This program is free software; you can redistribute it and/or 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
+ *
+ * specificly written as a driver for the speakup screenreview
+ * s not a general device driver.
+ */
+#include "spk_priv.h"
+#include "speakup.h"
+#include "serialio.h"
+
+#define DRV_VERSION "2.11"
+#define SYNTH_CLEAR 0x18 /* flush synth buffer */
+#define PROCSPEECH '\r' /* start synth processing speech char */
+
+static int synth_probe(struct spk_synth *synth);
+static void synth_flush(struct spk_synth *synth);
+
+static struct var_t vars[] = {
+ { CAPS_START, .u.s = {"\x05[f99]" } },
+ { CAPS_STOP, .u.s = {"\x05[f80]" } },
+ { RATE, .u.n = {"\x05[r%d]", 10, 0, 20, 100, -10, NULL } },
+ { PITCH, .u.n = {"\x05[f%d]", 80, 39, 4500, 0, 0, NULL } },
+ { VOL, .u.n = {"\x05[g%d]", 21, 0, 40, 0, 0, NULL } },
+ { TONE, .u.n = {"\x05[s%d]", 9, 0, 63, 0, 0, NULL } },
+ { PUNCT, .u.n = {"\x05[A%c]", 0, 0, 3, 0, 0, "nmsa" } },
+ { DIRECT, .u.n = {NULL, 0, 0, 1, 0, 0, NULL } },
+ V_LAST_VAR
+};
+
+/*
+ * These attributes will appear in /sys/accessibility/speakup/audptr.
+ */
+static struct kobj_attribute caps_start_attribute =
+ __ATTR(caps_start, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
+static struct kobj_attribute caps_stop_attribute =
+ __ATTR(caps_stop, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
+static struct kobj_attribute pitch_attribute =
+ __ATTR(pitch, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
+static struct kobj_attribute punct_attribute =
+ __ATTR(punct, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
+static struct kobj_attribute rate_attribute =
+ __ATTR(rate, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
+static struct kobj_attribute tone_attribute =
+ __ATTR(tone, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
+static struct kobj_attribute vol_attribute =
+ __ATTR(vol, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
+
+static struct kobj_attribute delay_time_attribute =
+ __ATTR(delay_time, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
+static struct kobj_attribute direct_attribute =
+ __ATTR(direct, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
+static struct kobj_attribute full_time_attribute =
+ __ATTR(full_time, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
+static struct kobj_attribute jiffy_delta_attribute =
+ __ATTR(jiffy_delta, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
+static struct kobj_attribute trigger_time_attribute =
+ __ATTR(trigger_time, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
+
+/*
+ * Create a group of attributes so that we can create and destroy them all
+ * at once.
+ */
+static struct attribute *synth_attrs[] = {
+ &caps_start_attribute.attr,
+ &caps_stop_attribute.attr,
+ &pitch_attribute.attr,
+ &punct_attribute.attr,
+ &rate_attribute.attr,
+ &tone_attribute.attr,
+ &vol_attribute.attr,
+ &delay_time_attribute.attr,
+ &direct_attribute.attr,
+ &full_time_attribute.attr,
+ &jiffy_delta_attribute.attr,
+ &trigger_time_attribute.attr,
+ NULL, /* need to NULL terminate the list of attributes */
+};
+
+static struct spk_synth synth_audptr = {
+ .name = "audptr",
+ .version = DRV_VERSION,
+ .long_name = "Audapter",
+ .init = "\x05[D1]\x05[Ol]",
+ .procspeech = PROCSPEECH,
+ .clear = SYNTH_CLEAR,
+ .delay = 400,
+ .trigger = 50,
+ .jiffies = 30,
+ .full = 18000,
+ .startup = SYNTH_START,
+ .checkval = SYNTH_CHECK,
+ .vars = vars,
+ .probe = synth_probe,
+ .release = spk_serial_release,
+ .synth_immediate = spk_synth_immediate,
+ .catch_up = spk_do_catch_up,
+ .flush = synth_flush,
+ .is_alive = spk_synth_is_alive_restart,
+ .synth_adjust = NULL,
+ .read_buff_add = NULL,
+ .get_index = NULL,
+ .indexing = {
+ .command = NULL,
+ .lowindex = 0,
+ .highindex = 0,
+ .currindex = 0,
+ },
+ .attributes = {
+ .attrs = synth_attrs,
+ .name = "audptr",
+ },
+};
+
+static void synth_flush(struct spk_synth *synth)
+{
+ int timeout = SPK_XMITR_TIMEOUT;
+
+ while (spk_serial_tx_busy()) {
+ if (!--timeout)
+ break;
+ udelay(1);
+ }
+ outb(SYNTH_CLEAR, speakup_info.port_tts);
+ spk_serial_out(PROCSPEECH);
+}
+
+static void synth_version(struct spk_synth *synth)
+{
+ unsigned char test = 0;
+ char synth_id[40] = "";
+
+ spk_synth_immediate(synth, "\x05[Q]");
+ synth_id[test] = spk_serial_in();
+ if (synth_id[test] == 'A') {
+ do {
+ /* read version string from synth */
+ synth_id[++test] = spk_serial_in();
+ } while (synth_id[test] != '\n' && test < 32);
+ synth_id[++test] = 0x00;
+ }
+ if (synth_id[0] == 'A')
+ pr_info("%s version: %s", synth->long_name, synth_id);
+}
+
+static int synth_probe(struct spk_synth *synth)
+{
+ int failed = 0;
+
+ failed = spk_serial_synth_probe(synth);
+ if (failed == 0)
+ synth_version(synth);
+ synth->alive = !failed;
+ return 0;
+}
+
+module_param_named(ser, synth_audptr.ser, int, S_IRUGO);
+module_param_named(start, synth_audptr.startup, short, S_IRUGO);
+
+MODULE_PARM_DESC(ser, "Set the serial port for the synthesizer (0-based).");
+MODULE_PARM_DESC(start, "Start the synthesizer once it is loaded.");
+
+module_spk_synth(synth_audptr);
+
+MODULE_AUTHOR("Kirk Reiser <kirk@braille.uwo.ca>");
+MODULE_AUTHOR("David Borowski");
+MODULE_DESCRIPTION("Speakup support for Audapter synthesizer");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(DRV_VERSION);
+
diff --git a/drivers/staging/speakup/speakup_bns.c b/drivers/staging/speakup/speakup_bns.c
new file mode 100644
index 000000000..80f8358d4
--- /dev/null
+++ b/drivers/staging/speakup/speakup_bns.c
@@ -0,0 +1,137 @@
+/*
+ * originally written by: Kirk Reiser <kirk@braille.uwo.ca>
+* this version considerably modified by David Borowski, david575@rogers.com
+ *
+ * Copyright (C) 1998-99 Kirk Reiser.
+ * Copyright (C) 2003 David Borowski.
+ *
+ * This program is free software; you can redistribute it and/or 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 code is specificly written as a driver for the speakup screenreview
+ * package and is not a general device driver.
+ */
+#include "spk_priv.h"
+#include "speakup.h"
+
+#define DRV_VERSION "2.11"
+#define SYNTH_CLEAR 0x18
+#define PROCSPEECH '\r'
+
+static struct var_t vars[] = {
+ { CAPS_START, .u.s = {"\x05\x31\x32P" } },
+ { CAPS_STOP, .u.s = {"\x05\x38P" } },
+ { RATE, .u.n = {"\x05%dE", 8, 1, 16, 0, 0, NULL } },
+ { PITCH, .u.n = {"\x05%dP", 8, 0, 16, 0, 0, NULL } },
+ { VOL, .u.n = {"\x05%dV", 8, 0, 16, 0, 0, NULL } },
+ { TONE, .u.n = {"\x05%dT", 8, 0, 16, 0, 0, NULL } },
+ { DIRECT, .u.n = {NULL, 0, 0, 1, 0, 0, NULL } },
+ V_LAST_VAR
+};
+
+/*
+ * These attributes will appear in /sys/accessibility/speakup/bns.
+ */
+static struct kobj_attribute caps_start_attribute =
+ __ATTR(caps_start, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
+static struct kobj_attribute caps_stop_attribute =
+ __ATTR(caps_stop, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
+static struct kobj_attribute pitch_attribute =
+ __ATTR(pitch, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
+static struct kobj_attribute rate_attribute =
+ __ATTR(rate, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
+static struct kobj_attribute tone_attribute =
+ __ATTR(tone, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
+static struct kobj_attribute vol_attribute =
+ __ATTR(vol, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
+
+static struct kobj_attribute delay_time_attribute =
+ __ATTR(delay_time, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
+static struct kobj_attribute direct_attribute =
+ __ATTR(direct, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
+static struct kobj_attribute full_time_attribute =
+ __ATTR(full_time, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
+static struct kobj_attribute jiffy_delta_attribute =
+ __ATTR(jiffy_delta, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
+static struct kobj_attribute trigger_time_attribute =
+ __ATTR(trigger_time, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
+
+/*
+ * Create a group of attributes so that we can create and destroy them all
+ * at once.
+ */
+static struct attribute *synth_attrs[] = {
+ &caps_start_attribute.attr,
+ &caps_stop_attribute.attr,
+ &pitch_attribute.attr,
+ &rate_attribute.attr,
+ &tone_attribute.attr,
+ &vol_attribute.attr,
+ &delay_time_attribute.attr,
+ &direct_attribute.attr,
+ &full_time_attribute.attr,
+ &jiffy_delta_attribute.attr,
+ &trigger_time_attribute.attr,
+ NULL, /* need to NULL terminate the list of attributes */
+};
+
+static struct spk_synth synth_bns = {
+ .name = "bns",
+ .version = DRV_VERSION,
+ .long_name = "Braille 'N Speak",
+ .init = "\x05Z\x05\x43",
+ .procspeech = PROCSPEECH,
+ .clear = SYNTH_CLEAR,
+ .delay = 500,
+ .trigger = 50,
+ .jiffies = 50,
+ .full = 40000,
+ .startup = SYNTH_START,
+ .checkval = SYNTH_CHECK,
+ .vars = vars,
+ .probe = spk_serial_synth_probe,
+ .release = spk_serial_release,
+ .synth_immediate = spk_synth_immediate,
+ .catch_up = spk_do_catch_up,
+ .flush = spk_synth_flush,
+ .is_alive = spk_synth_is_alive_restart,
+ .synth_adjust = NULL,
+ .read_buff_add = NULL,
+ .get_index = NULL,
+ .indexing = {
+ .command = NULL,
+ .lowindex = 0,
+ .highindex = 0,
+ .currindex = 0,
+ },
+ .attributes = {
+ .attrs = synth_attrs,
+ .name = "bns",
+ },
+};
+
+module_param_named(ser, synth_bns.ser, int, S_IRUGO);
+module_param_named(start, synth_bns.startup, short, S_IRUGO);
+
+MODULE_PARM_DESC(ser, "Set the serial port for the synthesizer (0-based).");
+MODULE_PARM_DESC(start, "Start the synthesizer once it is loaded.");
+
+module_spk_synth(synth_bns);
+
+MODULE_AUTHOR("Kirk Reiser <kirk@braille.uwo.ca>");
+MODULE_AUTHOR("David Borowski");
+MODULE_DESCRIPTION("Speakup support for Braille 'n Speak synthesizers");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(DRV_VERSION);
+
diff --git a/drivers/staging/speakup/speakup_decext.c b/drivers/staging/speakup/speakup_decext.c
new file mode 100644
index 000000000..e0b5db9bb
--- /dev/null
+++ b/drivers/staging/speakup/speakup_decext.c
@@ -0,0 +1,246 @@
+/*
+ * originally written by: Kirk Reiser <kirk@braille.uwo.ca>
+* this version considerably modified by David Borowski, david575@rogers.com
+ *
+ * Copyright (C) 1998-99 Kirk Reiser.
+ * Copyright (C) 2003 David Borowski.
+ *
+ * This program is free software; you can redistribute it and/or 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
+ *
+ * specificly written as a driver for the speakup screenreview
+ * s not a general device driver.
+ */
+#include <linux/jiffies.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/kthread.h>
+
+#include "spk_priv.h"
+#include "serialio.h"
+#include "speakup.h"
+
+#define DRV_VERSION "2.14"
+#define SYNTH_CLEAR 0x03
+#define PROCSPEECH 0x0b
+static unsigned char last_char;
+
+static inline u_char get_last_char(void)
+{
+ u_char avail = inb_p(speakup_info.port_tts + UART_LSR) & UART_LSR_DR;
+
+ if (avail)
+ last_char = inb_p(speakup_info.port_tts + UART_RX);
+ return last_char;
+}
+
+static inline bool synth_full(void)
+{
+ return get_last_char() == 0x13;
+}
+
+static void do_catch_up(struct spk_synth *synth);
+static void synth_flush(struct spk_synth *synth);
+
+static int in_escape;
+
+static struct var_t vars[] = {
+ { CAPS_START, .u.s = {"[:dv ap 222]" } },
+ { CAPS_STOP, .u.s = {"[:dv ap 100]" } },
+ { RATE, .u.n = {"[:ra %d]", 7, 0, 9, 150, 25, NULL } },
+ { PITCH, .u.n = {"[:dv ap %d]", 100, 0, 100, 0, 0, NULL } },
+ { VOL, .u.n = {"[:dv gv %d]", 13, 0, 16, 0, 5, NULL } },
+ { PUNCT, .u.n = {"[:pu %c]", 0, 0, 2, 0, 0, "nsa" } },
+ { VOICE, .u.n = {"[:n%c]", 0, 0, 9, 0, 0, "phfdburwkv" } },
+ { DIRECT, .u.n = {NULL, 0, 0, 1, 0, 0, NULL } },
+ V_LAST_VAR
+};
+
+/*
+ * These attributes will appear in /sys/accessibility/speakup/decext.
+ */
+static struct kobj_attribute caps_start_attribute =
+ __ATTR(caps_start, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
+static struct kobj_attribute caps_stop_attribute =
+ __ATTR(caps_stop, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
+static struct kobj_attribute pitch_attribute =
+ __ATTR(pitch, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
+static struct kobj_attribute punct_attribute =
+ __ATTR(punct, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
+static struct kobj_attribute rate_attribute =
+ __ATTR(rate, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
+static struct kobj_attribute voice_attribute =
+ __ATTR(voice, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
+static struct kobj_attribute vol_attribute =
+ __ATTR(vol, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
+
+static struct kobj_attribute delay_time_attribute =
+ __ATTR(delay_time, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
+static struct kobj_attribute direct_attribute =
+ __ATTR(direct, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
+static struct kobj_attribute full_time_attribute =
+ __ATTR(full_time, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
+static struct kobj_attribute jiffy_delta_attribute =
+ __ATTR(jiffy_delta, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
+static struct kobj_attribute trigger_time_attribute =
+ __ATTR(trigger_time, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
+
+/*
+ * Create a group of attributes so that we can create and destroy them all
+ * at once.
+ */
+static struct attribute *synth_attrs[] = {
+ &caps_start_attribute.attr,
+ &caps_stop_attribute.attr,
+ &pitch_attribute.attr,
+ &punct_attribute.attr,
+ &rate_attribute.attr,
+ &voice_attribute.attr,
+ &vol_attribute.attr,
+ &delay_time_attribute.attr,
+ &direct_attribute.attr,
+ &full_time_attribute.attr,
+ &jiffy_delta_attribute.attr,
+ &trigger_time_attribute.attr,
+ NULL, /* need to NULL terminate the list of attributes */
+};
+
+static struct spk_synth synth_decext = {
+ .name = "decext",
+ .version = DRV_VERSION,
+ .long_name = "Dectalk External",
+ .init = "[:pe -380]",
+ .procspeech = PROCSPEECH,
+ .clear = SYNTH_CLEAR,
+ .delay = 500,
+ .trigger = 50,
+ .jiffies = 50,
+ .full = 40000,
+ .flags = SF_DEC,
+ .startup = SYNTH_START,
+ .checkval = SYNTH_CHECK,
+ .vars = vars,
+ .probe = spk_serial_synth_probe,
+ .release = spk_serial_release,
+ .synth_immediate = spk_synth_immediate,
+ .catch_up = do_catch_up,
+ .flush = synth_flush,
+ .is_alive = spk_synth_is_alive_restart,
+ .synth_adjust = NULL,
+ .read_buff_add = NULL,
+ .get_index = NULL,
+ .indexing = {
+ .command = NULL,
+ .lowindex = 0,
+ .highindex = 0,
+ .currindex = 0,
+ },
+ .attributes = {
+ .attrs = synth_attrs,
+ .name = "decext",
+ },
+};
+
+static void do_catch_up(struct spk_synth *synth)
+{
+ u_char ch;
+ static u_char last = '\0';
+ unsigned long flags;
+ unsigned long jiff_max;
+ struct var_t *jiffy_delta;
+ struct var_t *delay_time;
+ int jiffy_delta_val = 0;
+ int delay_time_val = 0;
+
+ jiffy_delta = spk_get_var(JIFFY);
+ delay_time = spk_get_var(DELAY);
+
+ spin_lock_irqsave(&speakup_info.spinlock, flags);
+ jiffy_delta_val = jiffy_delta->u.n.value;
+ spin_unlock_irqrestore(&speakup_info.spinlock, flags);
+ jiff_max = jiffies + jiffy_delta_val;
+
+ while (!kthread_should_stop()) {
+ spin_lock_irqsave(&speakup_info.spinlock, flags);
+ if (speakup_info.flushing) {
+ speakup_info.flushing = 0;
+ spin_unlock_irqrestore(&speakup_info.spinlock, flags);
+ synth->flush(synth);
+ continue;
+ }
+ if (synth_buffer_empty()) {
+ spin_unlock_irqrestore(&speakup_info.spinlock, flags);
+ break;
+ }
+ ch = synth_buffer_peek();
+ set_current_state(TASK_INTERRUPTIBLE);
+ delay_time_val = delay_time->u.n.value;
+ spin_unlock_irqrestore(&speakup_info.spinlock, flags);
+ if (ch == '\n')
+ ch = 0x0D;
+ if (synth_full() || !spk_serial_out(ch)) {
+ schedule_timeout(msecs_to_jiffies(delay_time_val));
+ continue;
+ }
+ set_current_state(TASK_RUNNING);
+ spin_lock_irqsave(&speakup_info.spinlock, flags);
+ synth_buffer_getc();
+ spin_unlock_irqrestore(&speakup_info.spinlock, flags);
+ if (ch == '[')
+ in_escape = 1;
+ else if (ch == ']')
+ in_escape = 0;
+ else if (ch <= SPACE) {
+ if (!in_escape && strchr(",.!?;:", last))
+ spk_serial_out(PROCSPEECH);
+ if (time_after_eq(jiffies, jiff_max)) {
+ if (!in_escape)
+ spk_serial_out(PROCSPEECH);
+ spin_lock_irqsave(&speakup_info.spinlock,
+ flags);
+ jiffy_delta_val = jiffy_delta->u.n.value;
+ delay_time_val = delay_time->u.n.value;
+ spin_unlock_irqrestore(&speakup_info.spinlock,
+ flags);
+ schedule_timeout(msecs_to_jiffies
+ (delay_time_val));
+ jiff_max = jiffies + jiffy_delta_val;
+ }
+ }
+ last = ch;
+ }
+ if (!in_escape)
+ spk_serial_out(PROCSPEECH);
+}
+
+static void synth_flush(struct spk_synth *synth)
+{
+ in_escape = 0;
+ spk_synth_immediate(synth, "\033P;10z\033\\");
+}
+
+module_param_named(ser, synth_decext.ser, int, S_IRUGO);
+module_param_named(start, synth_decext.startup, short, S_IRUGO);
+
+MODULE_PARM_DESC(ser, "Set the serial port for the synthesizer (0-based).");
+MODULE_PARM_DESC(start, "Start the synthesizer once it is loaded.");
+
+module_spk_synth(synth_decext);
+
+MODULE_AUTHOR("Kirk Reiser <kirk@braille.uwo.ca>");
+MODULE_AUTHOR("David Borowski");
+MODULE_DESCRIPTION("Speakup support for DECtalk External synthesizers");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(DRV_VERSION);
+
diff --git a/drivers/staging/speakup/speakup_decpc.c b/drivers/staging/speakup/speakup_decpc.c
new file mode 100644
index 000000000..437e13a85
--- /dev/null
+++ b/drivers/staging/speakup/speakup_decpc.c
@@ -0,0 +1,503 @@
+/*
+ * This is the DECtalk PC speakup driver
+ *
+ * Some constants from DEC's DOS driver:
+ * Copyright (c) by Digital Equipment Corp.
+ *
+ * 386BSD DECtalk PC driver:
+ * Copyright (c) 1996 Brian Buhrow <buhrow@lothlorien.nfbcal.org>
+ *
+ * Linux DECtalk PC driver:
+ * Copyright (c) 1997 Nicolas Pitre <nico@cam.org>
+ *
+ * speakup DECtalk PC Internal driver:
+ * Copyright (c) 2003 David Borowski <david575@golden.net>
+ *
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for 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/jiffies.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/kthread.h>
+
+#include "spk_priv.h"
+#include "speakup.h"
+
+#define MODULE_init 0x0dec /* module in boot code */
+#define MODULE_self_test 0x8800 /* module in self-test */
+#define MODULE_reset 0xffff /* reinit the whole module */
+
+#define MODE_mask 0xf000 /* mode bits in high nibble */
+#define MODE_null 0x0000
+#define MODE_test 0x2000 /* in testing mode */
+#define MODE_status 0x8000
+#define STAT_int 0x0001 /* running in interrupt mode */
+#define STAT_tr_char 0x0002 /* character data to transmit */
+#define STAT_rr_char 0x0004 /* ready to receive char data */
+#define STAT_cmd_ready 0x0008 /* ready to accept commands */
+#define STAT_dma_ready 0x0010 /* dma command ready */
+#define STAT_digitized 0x0020 /* spc in digitized mode */
+#define STAT_new_index 0x0040 /* new last index ready */
+#define STAT_new_status 0x0080 /* new status posted */
+#define STAT_dma_state 0x0100 /* dma state toggle */
+#define STAT_index_valid 0x0200 /* indexs are valid */
+#define STAT_flushing 0x0400 /* flush in progress */
+#define STAT_self_test 0x0800 /* module in self test */
+#define MODE_ready 0xc000 /* module ready for next phase */
+#define READY_boot 0x0000
+#define READY_kernel 0x0001
+#define MODE_error 0xf000
+
+#define CMD_mask 0xf000 /* mask for command nibble */
+#define CMD_null 0x0000 /* post status */
+#define CMD_control 0x1000 /* hard control command */
+#define CTRL_mask 0x0F00 /* mask off control nibble */
+#define CTRL_data 0x00FF /* mask to get data byte */
+#define CTRL_null 0x0000 /* null control */
+#define CTRL_vol_up 0x0100 /* increase volume */
+#define CTRL_vol_down 0x0200 /* decrease volume */
+#define CTRL_vol_set 0x0300 /* set volume */
+#define CTRL_pause 0x0400 /* pause spc */
+#define CTRL_resume 0x0500 /* resume spc clock */
+#define CTRL_resume_spc 0x0001 /* resume spc soft pause */
+#define CTRL_flush 0x0600 /* flush all buffers */
+#define CTRL_int_enable 0x0700 /* enable status change ints */
+#define CTRL_buff_free 0x0800 /* buffer remain count */
+#define CTRL_buff_used 0x0900 /* buffer in use */
+#define CTRL_speech 0x0a00 /* immediate speech change */
+#define CTRL_SP_voice 0x0001 /* voice change */
+#define CTRL_SP_rate 0x0002 /* rate change */
+#define CTRL_SP_comma 0x0003 /* comma pause change */
+#define CTRL_SP_period 0x0004 /* period pause change */
+#define CTRL_SP_rate_delta 0x0005 /* delta rate change */
+#define CTRL_SP_get_param 0x0006 /* return the desired parameter */
+#define CTRL_last_index 0x0b00 /* get last index spoken */
+#define CTRL_io_priority 0x0c00 /* change i/o priority */
+#define CTRL_free_mem 0x0d00 /* get free paragraphs on module */
+#define CTRL_get_lang 0x0e00 /* return bit mask of loaded
+ * languages */
+#define CMD_test 0x2000 /* self-test request */
+#define TEST_mask 0x0F00 /* isolate test field */
+#define TEST_null 0x0000 /* no test requested */
+#define TEST_isa_int 0x0100 /* assert isa irq */
+#define TEST_echo 0x0200 /* make data in == data out */
+#define TEST_seg 0x0300 /* set peek/poke segment */
+#define TEST_off 0x0400 /* set peek/poke offset */
+#define TEST_peek 0x0500 /* data out == *peek */
+#define TEST_poke 0x0600 /* *peek == data in */
+#define TEST_sub_code 0x00FF /* user defined test sub codes */
+#define CMD_id 0x3000 /* return software id */
+#define ID_null 0x0000 /* null id */
+#define ID_kernel 0x0100 /* kernel code executing */
+#define ID_boot 0x0200 /* boot code executing */
+#define CMD_dma 0x4000 /* force a dma start */
+#define CMD_reset 0x5000 /* reset module status */
+#define CMD_sync 0x6000 /* kernel sync command */
+#define CMD_char_in 0x7000 /* single character send */
+#define CMD_char_out 0x8000 /* single character get */
+#define CHAR_count_1 0x0100 /* one char in cmd_low */
+#define CHAR_count_2 0x0200 /* the second in data_low */
+#define CHAR_count_3 0x0300 /* the third in data_high */
+#define CMD_spc_mode 0x9000 /* change spc mode */
+#define CMD_spc_to_text 0x0100 /* set to text mode */
+#define CMD_spc_to_digit 0x0200 /* set to digital mode */
+#define CMD_spc_rate 0x0400 /* change spc data rate */
+#define CMD_error 0xf000 /* severe error */
+
+enum { PRIMARY_DIC = 0, USER_DIC, COMMAND_DIC, ABBREV_DIC };
+
+#define DMA_single_in 0x01
+#define DMA_single_out 0x02
+#define DMA_buff_in 0x03
+#define DMA_buff_out 0x04
+#define DMA_control 0x05
+#define DT_MEM_ALLOC 0x03
+#define DT_SET_DIC 0x04
+#define DT_START_TASK 0x05
+#define DT_LOAD_MEM 0x06
+#define DT_READ_MEM 0x07
+#define DT_DIGITAL_IN 0x08
+#define DMA_sync 0x06
+#define DMA_sync_char 0x07
+
+#define DRV_VERSION "2.12"
+#define PROCSPEECH 0x0b
+#define SYNTH_IO_EXTENT 8
+
+static int synth_probe(struct spk_synth *synth);
+static void dtpc_release(void);
+static const char *synth_immediate(struct spk_synth *synth, const char *buf);
+static void do_catch_up(struct spk_synth *synth);
+static void synth_flush(struct spk_synth *synth);
+
+static int synth_portlist[] = { 0x340, 0x350, 0x240, 0x250, 0 };
+static int in_escape, is_flushing;
+static int dt_stat, dma_state;
+
+static struct var_t vars[] = {
+ { CAPS_START, .u.s = {"[:dv ap 200]" } },
+ { CAPS_STOP, .u.s = {"[:dv ap 100]" } },
+ { RATE, .u.n = {"[:ra %d]", 9, 0, 18, 150, 25, NULL } },
+ { PITCH, .u.n = {"[:dv ap %d]", 80, 0, 100, 20, 0, NULL } },
+ { VOL, .u.n = {"[:vo se %d]", 5, 0, 9, 5, 10, NULL } },
+ { PUNCT, .u.n = {"[:pu %c]", 0, 0, 2, 0, 0, "nsa" } },
+ { VOICE, .u.n = {"[:n%c]", 0, 0, 9, 0, 0, "phfdburwkv" } },
+ { DIRECT, .u.n = {NULL, 0, 0, 1, 0, 0, NULL } },
+ V_LAST_VAR
+};
+
+/*
+ * These attributes will appear in /sys/accessibility/speakup/decpc.
+ */
+static struct kobj_attribute caps_start_attribute =
+ __ATTR(caps_start, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
+static struct kobj_attribute caps_stop_attribute =
+ __ATTR(caps_stop, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
+static struct kobj_attribute pitch_attribute =
+ __ATTR(pitch, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
+static struct kobj_attribute punct_attribute =
+ __ATTR(punct, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
+static struct kobj_attribute rate_attribute =
+ __ATTR(rate, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
+static struct kobj_attribute voice_attribute =
+ __ATTR(voice, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
+static struct kobj_attribute vol_attribute =
+ __ATTR(vol, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
+
+static struct kobj_attribute delay_time_attribute =
+ __ATTR(delay_time, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
+static struct kobj_attribute direct_attribute =
+ __ATTR(direct, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
+static struct kobj_attribute full_time_attribute =
+ __ATTR(full_time, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
+static struct kobj_attribute jiffy_delta_attribute =
+ __ATTR(jiffy_delta, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
+static struct kobj_attribute trigger_time_attribute =
+ __ATTR(trigger_time, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
+
+/*
+ * Create a group of attributes so that we can create and destroy them all
+ * at once.
+ */
+static struct attribute *synth_attrs[] = {
+ &caps_start_attribute.attr,
+ &caps_stop_attribute.attr,
+ &pitch_attribute.attr,
+ &punct_attribute.attr,
+ &rate_attribute.attr,
+ &voice_attribute.attr,
+ &vol_attribute.attr,
+ &delay_time_attribute.attr,
+ &direct_attribute.attr,
+ &full_time_attribute.attr,
+ &jiffy_delta_attribute.attr,
+ &trigger_time_attribute.attr,
+ NULL, /* need to NULL terminate the list of attributes */
+};
+
+static struct spk_synth synth_dec_pc = {
+ .name = "decpc",
+ .version = DRV_VERSION,
+ .long_name = "Dectalk PC",
+ .init = "[:pe -380]",
+ .procspeech = PROCSPEECH,
+ .delay = 500,
+ .trigger = 50,
+ .jiffies = 50,
+ .full = 1000,
+ .flags = SF_DEC,
+ .startup = SYNTH_START,
+ .checkval = SYNTH_CHECK,
+ .vars = vars,
+ .probe = synth_probe,
+ .release = dtpc_release,
+ .synth_immediate = synth_immediate,
+ .catch_up = do_catch_up,
+ .flush = synth_flush,
+ .is_alive = spk_synth_is_alive_nop,
+ .synth_adjust = NULL,
+ .read_buff_add = NULL,
+ .get_index = NULL,
+ .indexing = {
+ .command = NULL,
+ .lowindex = 0,
+ .highindex = 0,
+ .currindex = 0,
+ },
+ .attributes = {
+ .attrs = synth_attrs,
+ .name = "decpc",
+ },
+};
+
+static int dt_getstatus(void)
+{
+ dt_stat = inb_p(speakup_info.port_tts) |
+ (inb_p(speakup_info.port_tts + 1) << 8);
+ return dt_stat;
+}
+
+static void dt_sendcmd(u_int cmd)
+{
+ outb_p(cmd & 0xFF, speakup_info.port_tts);
+ outb_p((cmd >> 8) & 0xFF, speakup_info.port_tts+1);
+}
+
+static int dt_waitbit(int bit)
+{
+ int timeout = 100;
+
+ while (--timeout > 0) {
+ if ((dt_getstatus() & bit) == bit)
+ return 1;
+ udelay(50);
+ }
+ return 0;
+}
+
+static int dt_wait_dma(void)
+{
+ int timeout = 100, state = dma_state;
+
+ if (!dt_waitbit(STAT_dma_ready))
+ return 0;
+ while (--timeout > 0) {
+ if ((dt_getstatus()&STAT_dma_state) == state)
+ return 1;
+ udelay(50);
+ }
+ dma_state = dt_getstatus() & STAT_dma_state;
+ return 1;
+}
+
+static int dt_ctrl(u_int cmd)
+{
+ int timeout = 10;
+
+ if (!dt_waitbit(STAT_cmd_ready))
+ return -1;
+ outb_p(0, speakup_info.port_tts+2);
+ outb_p(0, speakup_info.port_tts+3);
+ dt_getstatus();
+ dt_sendcmd(CMD_control|cmd);
+ outb_p(0, speakup_info.port_tts+6);
+ while (dt_getstatus() & STAT_cmd_ready) {
+ udelay(20);
+ if (--timeout == 0)
+ break;
+ }
+ dt_sendcmd(CMD_null);
+ return 0;
+}
+
+static void synth_flush(struct spk_synth *synth)
+{
+ int timeout = 10;
+
+ if (is_flushing)
+ return;
+ is_flushing = 4;
+ in_escape = 0;
+ while (dt_ctrl(CTRL_flush)) {
+ if (--timeout == 0)
+ break;
+udelay(50);
+ }
+ for (timeout = 0; timeout < 10; timeout++) {
+ if (dt_waitbit(STAT_dma_ready))
+ break;
+udelay(50);
+ }
+ outb_p(DMA_sync, speakup_info.port_tts+4);
+ outb_p(0, speakup_info.port_tts+4);
+ udelay(100);
+ for (timeout = 0; timeout < 10; timeout++) {
+ if (!(dt_getstatus() & STAT_flushing))
+ break;
+udelay(50);
+ }
+ dma_state = dt_getstatus() & STAT_dma_state;
+ dma_state ^= STAT_dma_state;
+ is_flushing = 0;
+}
+
+static int dt_sendchar(char ch)
+{
+ if (!dt_wait_dma())
+ return -1;
+ if (!(dt_stat & STAT_rr_char))
+ return -2;
+ outb_p(DMA_single_in, speakup_info.port_tts+4);
+ outb_p(ch, speakup_info.port_tts+4);
+ dma_state ^= STAT_dma_state;
+ return 0;
+}
+
+static int testkernel(void)
+{
+ int status = 0;
+
+ if (dt_getstatus() == 0xffff) {
+ status = -1;
+ goto oops;
+ }
+ dt_sendcmd(CMD_sync);
+ if (!dt_waitbit(STAT_cmd_ready))
+ status = -2;
+ else if (dt_stat&0x8000)
+ return 0;
+ else if (dt_stat == 0x0dec)
+ pr_warn("dec_pc at 0x%x, software not loaded\n",
+ speakup_info.port_tts);
+ status = -3;
+oops: synth_release_region(speakup_info.port_tts, SYNTH_IO_EXTENT);
+ speakup_info.port_tts = 0;
+ return status;
+}
+
+static void do_catch_up(struct spk_synth *synth)
+{
+ u_char ch;
+ static u_char last;
+ unsigned long flags;
+ unsigned long jiff_max;
+ struct var_t *jiffy_delta;
+ struct var_t *delay_time;
+ int jiffy_delta_val;
+ int delay_time_val;
+
+ jiffy_delta = spk_get_var(JIFFY);
+ delay_time = spk_get_var(DELAY);
+ spin_lock_irqsave(&speakup_info.spinlock, flags);
+ jiffy_delta_val = jiffy_delta->u.n.value;
+ spin_unlock_irqrestore(&speakup_info.spinlock, flags);
+ jiff_max = jiffies + jiffy_delta_val;
+
+ while (!kthread_should_stop()) {
+ spin_lock_irqsave(&speakup_info.spinlock, flags);
+ if (speakup_info.flushing) {
+ speakup_info.flushing = 0;
+ spin_unlock_irqrestore(&speakup_info.spinlock, flags);
+ synth->flush(synth);
+ continue;
+ }
+ if (synth_buffer_empty()) {
+ spin_unlock_irqrestore(&speakup_info.spinlock, flags);
+ break;
+ }
+ ch = synth_buffer_peek();
+ set_current_state(TASK_INTERRUPTIBLE);
+ delay_time_val = delay_time->u.n.value;
+ spin_unlock_irqrestore(&speakup_info.spinlock, flags);
+ if (ch == '\n')
+ ch = 0x0D;
+ if (dt_sendchar(ch)) {
+ schedule_timeout(msecs_to_jiffies(delay_time_val));
+ continue;
+ }
+ set_current_state(TASK_RUNNING);
+ spin_lock_irqsave(&speakup_info.spinlock, flags);
+ synth_buffer_getc();
+ spin_unlock_irqrestore(&speakup_info.spinlock, flags);
+ if (ch == '[')
+ in_escape = 1;
+ else if (ch == ']')
+ in_escape = 0;
+ else if (ch <= SPACE) {
+ if (!in_escape && strchr(",.!?;:", last))
+ dt_sendchar(PROCSPEECH);
+ if (time_after_eq(jiffies, jiff_max)) {
+ if (!in_escape)
+ dt_sendchar(PROCSPEECH);
+ spin_lock_irqsave(&speakup_info.spinlock,
+ flags);
+ jiffy_delta_val = jiffy_delta->u.n.value;
+ delay_time_val = delay_time->u.n.value;
+ spin_unlock_irqrestore(&speakup_info.spinlock,
+ flags);
+ schedule_timeout(msecs_to_jiffies
+ (delay_time_val));
+ jiff_max = jiffies + jiffy_delta_val;
+ }
+ }
+ last = ch;
+ ch = 0;
+ }
+ if (!in_escape)
+ dt_sendchar(PROCSPEECH);
+}
+
+static const char *synth_immediate(struct spk_synth *synth, const char *buf)
+{
+ u_char ch;
+
+ while ((ch = *buf)) {
+ if (ch == '\n')
+ ch = PROCSPEECH;
+ if (dt_sendchar(ch))
+ return buf;
+ buf++;
+ }
+ return NULL;
+}
+
+static int synth_probe(struct spk_synth *synth)
+{
+ int i = 0, failed = 0;
+
+ pr_info("Probing for %s.\n", synth->long_name);
+ for (i = 0; synth_portlist[i]; i++) {
+ if (synth_request_region(synth_portlist[i], SYNTH_IO_EXTENT)) {
+ pr_warn("request_region: failed with 0x%x, %d\n",
+ synth_portlist[i], SYNTH_IO_EXTENT);
+ continue;
+ }
+ speakup_info.port_tts = synth_portlist[i];
+ failed = testkernel();
+ if (failed == 0)
+ break;
+ }
+ if (failed) {
+ pr_info("%s: not found\n", synth->long_name);
+ return -ENODEV;
+ }
+ pr_info("%s: %03x-%03x, Driver Version %s,\n", synth->long_name,
+ speakup_info.port_tts, speakup_info.port_tts + 7,
+ synth->version);
+ synth->alive = 1;
+ return 0;
+}
+
+static void dtpc_release(void)
+{
+ if (speakup_info.port_tts)
+ synth_release_region(speakup_info.port_tts, SYNTH_IO_EXTENT);
+ speakup_info.port_tts = 0;
+}
+
+module_param_named(start, synth_dec_pc.startup, short, S_IRUGO);
+
+MODULE_PARM_DESC(start, "Start the synthesizer once it is loaded.");
+
+module_spk_synth(synth_dec_pc);
+
+MODULE_AUTHOR("Kirk Reiser <kirk@braille.uwo.ca>");
+MODULE_AUTHOR("David Borowski");
+MODULE_DESCRIPTION("Speakup support for DECtalk PC synthesizers");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(DRV_VERSION);
+
diff --git a/drivers/staging/speakup/speakup_dectlk.c b/drivers/staging/speakup/speakup_dectlk.c
new file mode 100644
index 000000000..b5a23d42f
--- /dev/null
+++ b/drivers/staging/speakup/speakup_dectlk.c
@@ -0,0 +1,316 @@
+/*
+ * originally written by: Kirk Reiser <kirk@braille.uwo.ca>
+ * this version considerably modified by David Borowski, david575@rogers.com
+ *
+ * Copyright (C) 1998-99 Kirk Reiser.
+ * Copyright (C) 2003 David Borowski.
+ *
+ * This program is free software; you can redistribute it and/or 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
+ *
+ * specificly written as a driver for the speakup screenreview
+ * s not a general device driver.
+ */
+#include <linux/unistd.h>
+#include <linux/proc_fs.h>
+#include <linux/jiffies.h>
+#include <linux/spinlock.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/kthread.h>
+#include "speakup.h"
+#include "spk_priv.h"
+#include "serialio.h"
+
+#define DRV_VERSION "2.20"
+#define SYNTH_CLEAR 0x03
+#define PROCSPEECH 0x0b
+static int xoff;
+
+static inline int synth_full(void)
+{
+ return xoff;
+}
+
+static void do_catch_up(struct spk_synth *synth);
+static void synth_flush(struct spk_synth *synth);
+static void read_buff_add(u_char c);
+static unsigned char get_index(void);
+
+static int in_escape;
+static int is_flushing;
+
+static spinlock_t flush_lock;
+static DECLARE_WAIT_QUEUE_HEAD(flush);
+
+static struct var_t vars[] = {
+ { CAPS_START, .u.s = {"[:dv ap 160] " } },
+ { CAPS_STOP, .u.s = {"[:dv ap 100 ] " } },
+ { RATE, .u.n = {"[:ra %d] ", 180, 75, 650, 0, 0, NULL } },
+ { PITCH, .u.n = {"[:dv ap %d] ", 122, 50, 350, 0, 0, NULL } },
+ { VOL, .u.n = {"[:dv g5 %d] ", 86, 60, 86, 0, 0, NULL } },
+ { PUNCT, .u.n = {"[:pu %c] ", 0, 0, 2, 0, 0, "nsa" } },
+ { VOICE, .u.n = {"[:n%c] ", 0, 0, 9, 0, 0, "phfdburwkv" } },
+ { DIRECT, .u.n = {NULL, 0, 0, 1, 0, 0, NULL } },
+ V_LAST_VAR
+};
+
+/*
+ * These attributes will appear in /sys/accessibility/speakup/dectlk.
+ */
+static struct kobj_attribute caps_start_attribute =
+ __ATTR(caps_start, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
+static struct kobj_attribute caps_stop_attribute =
+ __ATTR(caps_stop, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
+static struct kobj_attribute pitch_attribute =
+ __ATTR(pitch, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
+static struct kobj_attribute punct_attribute =
+ __ATTR(punct, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
+static struct kobj_attribute rate_attribute =
+ __ATTR(rate, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
+static struct kobj_attribute voice_attribute =
+ __ATTR(voice, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
+static struct kobj_attribute vol_attribute =
+ __ATTR(vol, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
+
+static struct kobj_attribute delay_time_attribute =
+ __ATTR(delay_time, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
+static struct kobj_attribute direct_attribute =
+ __ATTR(direct, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
+static struct kobj_attribute full_time_attribute =
+ __ATTR(full_time, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
+static struct kobj_attribute jiffy_delta_attribute =
+ __ATTR(jiffy_delta, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
+static struct kobj_attribute trigger_time_attribute =
+ __ATTR(trigger_time, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
+
+/*
+ * Create a group of attributes so that we can create and destroy them all
+ * at once.
+ */
+static struct attribute *synth_attrs[] = {
+ &caps_start_attribute.attr,
+ &caps_stop_attribute.attr,
+ &pitch_attribute.attr,
+ &punct_attribute.attr,
+ &rate_attribute.attr,
+ &voice_attribute.attr,
+ &vol_attribute.attr,
+ &delay_time_attribute.attr,
+ &direct_attribute.attr,
+ &full_time_attribute.attr,
+ &jiffy_delta_attribute.attr,
+ &trigger_time_attribute.attr,
+ NULL, /* need to NULL terminate the list of attributes */
+};
+
+static int ap_defaults[] = {122, 89, 155, 110, 208, 240, 200, 106, 306};
+static int g5_defaults[] = {86, 81, 86, 84, 81, 80, 83, 83, 73};
+
+static struct spk_synth synth_dectlk = {
+ .name = "dectlk",
+ .version = DRV_VERSION,
+ .long_name = "Dectalk Express",
+ .init = "[:error sp :name paul :rate 180 :tsr off] ",
+ .procspeech = PROCSPEECH,
+ .clear = SYNTH_CLEAR,
+ .delay = 500,
+ .trigger = 50,
+ .jiffies = 50,
+ .full = 40000,
+ .startup = SYNTH_START,
+ .checkval = SYNTH_CHECK,
+ .vars = vars,
+ .default_pitch = ap_defaults,
+ .default_vol = g5_defaults,
+ .probe = spk_serial_synth_probe,
+ .release = spk_serial_release,
+ .synth_immediate = spk_synth_immediate,
+ .catch_up = do_catch_up,
+ .flush = synth_flush,
+ .is_alive = spk_synth_is_alive_restart,
+ .synth_adjust = NULL,
+ .read_buff_add = read_buff_add,
+ .get_index = get_index,
+ .indexing = {
+ .command = "[:in re %d ] ",
+ .lowindex = 1,
+ .highindex = 8,
+ .currindex = 1,
+ },
+ .attributes = {
+ .attrs = synth_attrs,
+ .name = "dectlk",
+ },
+};
+
+static int is_indnum(u_char *ch)
+{
+ if ((*ch >= '0') && (*ch <= '9')) {
+ *ch = *ch - '0';
+ return 1;
+ }
+ return 0;
+}
+
+static u_char lastind;
+
+static unsigned char get_index(void)
+{
+ u_char rv;
+
+ rv = lastind;
+ lastind = 0;
+ return rv;
+}
+
+static void read_buff_add(u_char c)
+{
+ static int ind = -1;
+
+ if (c == 0x01) {
+ unsigned long flags;
+
+ spin_lock_irqsave(&flush_lock, flags);
+ is_flushing = 0;
+ wake_up_interruptible(&flush);
+ spin_unlock_irqrestore(&flush_lock, flags);
+ } else if (c == 0x13) {
+ xoff = 1;
+ } else if (c == 0x11) {
+ xoff = 0;
+ } else if (is_indnum(&c)) {
+ if (ind == -1)
+ ind = c;
+ else
+ ind = ind * 10 + c;
+ } else if ((c > 31) && (c < 127)) {
+ if (ind != -1)
+ lastind = (u_char)ind;
+ ind = -1;
+ }
+}
+
+static void do_catch_up(struct spk_synth *synth)
+{
+ int synth_full_val = 0;
+ static u_char ch;
+ static u_char last = '\0';
+ unsigned long flags;
+ unsigned long jiff_max;
+ unsigned long timeout = msecs_to_jiffies(4000);
+ DEFINE_WAIT(wait);
+ struct var_t *jiffy_delta;
+ struct var_t *delay_time;
+ int jiffy_delta_val;
+ int delay_time_val;
+
+ jiffy_delta = spk_get_var(JIFFY);
+ delay_time = spk_get_var(DELAY);
+ spin_lock_irqsave(&speakup_info.spinlock, flags);
+ jiffy_delta_val = jiffy_delta->u.n.value;
+ spin_unlock_irqrestore(&speakup_info.spinlock, flags);
+ jiff_max = jiffies + jiffy_delta_val;
+
+ while (!kthread_should_stop()) {
+ /* if no ctl-a in 4, send data anyway */
+ spin_lock_irqsave(&flush_lock, flags);
+ while (is_flushing && timeout) {
+ prepare_to_wait(&flush, &wait, TASK_INTERRUPTIBLE);
+ spin_unlock_irqrestore(&flush_lock, flags);
+ timeout = schedule_timeout(timeout);
+ spin_lock_irqsave(&flush_lock, flags);
+ }
+ finish_wait(&flush, &wait);
+ is_flushing = 0;
+ spin_unlock_irqrestore(&flush_lock, flags);
+
+ spin_lock_irqsave(&speakup_info.spinlock, flags);
+ if (speakup_info.flushing) {
+ speakup_info.flushing = 0;
+ spin_unlock_irqrestore(&speakup_info.spinlock, flags);
+ synth->flush(synth);
+ continue;
+ }
+ if (synth_buffer_empty()) {
+ spin_unlock_irqrestore(&speakup_info.spinlock, flags);
+ break;
+ }
+ ch = synth_buffer_peek();
+ set_current_state(TASK_INTERRUPTIBLE);
+ delay_time_val = delay_time->u.n.value;
+ synth_full_val = synth_full();
+ spin_unlock_irqrestore(&speakup_info.spinlock, flags);
+ if (ch == '\n')
+ ch = 0x0D;
+ if (synth_full_val || !spk_serial_out(ch)) {
+ schedule_timeout(msecs_to_jiffies(delay_time_val));
+ continue;
+ }
+ set_current_state(TASK_RUNNING);
+ spin_lock_irqsave(&speakup_info.spinlock, flags);
+ synth_buffer_getc();
+ spin_unlock_irqrestore(&speakup_info.spinlock, flags);
+ if (ch == '[')
+ in_escape = 1;
+ else if (ch == ']')
+ in_escape = 0;
+ else if (ch <= SPACE) {
+ if (!in_escape && strchr(",.!?;:", last))
+ spk_serial_out(PROCSPEECH);
+ if (time_after_eq(jiffies, jiff_max)) {
+ if (!in_escape)
+ spk_serial_out(PROCSPEECH);
+ spin_lock_irqsave(&speakup_info.spinlock,
+ flags);
+ jiffy_delta_val = jiffy_delta->u.n.value;
+ delay_time_val = delay_time->u.n.value;
+ spin_unlock_irqrestore(&speakup_info.spinlock,
+ flags);
+ schedule_timeout(msecs_to_jiffies
+ (delay_time_val));
+ jiff_max = jiffies + jiffy_delta_val;
+ }
+ }
+ last = ch;
+ }
+ if (!in_escape)
+ spk_serial_out(PROCSPEECH);
+}
+
+static void synth_flush(struct spk_synth *synth)
+{
+ if (in_escape) {
+ /* if in command output ']' so we don't get an error */
+ spk_serial_out(']');
+ }
+ in_escape = 0;
+ is_flushing = 1;
+ spk_serial_out(SYNTH_CLEAR);
+}
+
+module_param_named(ser, synth_dectlk.ser, int, S_IRUGO);
+module_param_named(start, synth_dectlk.startup, short, S_IRUGO);
+
+MODULE_PARM_DESC(ser, "Set the serial port for the synthesizer (0-based).");
+MODULE_PARM_DESC(start, "Start the synthesizer once it is loaded.");
+
+module_spk_synth(synth_dectlk);
+
+MODULE_AUTHOR("Kirk Reiser <kirk@braille.uwo.ca>");
+MODULE_AUTHOR("David Borowski");
+MODULE_DESCRIPTION("Speakup support for DECtalk Express synthesizers");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(DRV_VERSION);
+
diff --git a/drivers/staging/speakup/speakup_dtlk.c b/drivers/staging/speakup/speakup_dtlk.c
new file mode 100644
index 000000000..345efd334
--- /dev/null
+++ b/drivers/staging/speakup/speakup_dtlk.c
@@ -0,0 +1,398 @@
+/*
+ * originally written by: Kirk Reiser <kirk@braille.uwo.ca>
+* this version considerably modified by David Borowski, david575@rogers.com
+ *
+ * Copyright (C) 1998-99 Kirk Reiser.
+ * Copyright (C) 2003 David Borowski.
+ *
+ * This program is free software; you can redistribute it and/or 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
+ *
+ * specificly written as a driver for the speakup screenreview
+ * package it's not a general device driver.
+ * This driver is for the RC Systems DoubleTalk PC internal synthesizer.
+ */
+#include <linux/jiffies.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/kthread.h>
+
+#include "spk_priv.h"
+#include "serialio.h"
+#include "speakup_dtlk.h" /* local header file for DoubleTalk values */
+#include "speakup.h"
+
+#define DRV_VERSION "2.10"
+#define PROCSPEECH 0x00
+
+static int synth_probe(struct spk_synth *synth);
+static void dtlk_release(void);
+static const char *synth_immediate(struct spk_synth *synth, const char *buf);
+static void do_catch_up(struct spk_synth *synth);
+static void synth_flush(struct spk_synth *synth);
+
+static int synth_lpc;
+static int port_forced;
+static unsigned int synth_portlist[] = {
+ 0x25e, 0x29e, 0x2de, 0x31e, 0x35e, 0x39e, 0
+};
+static u_char synth_status;
+
+static struct var_t vars[] = {
+ { CAPS_START, .u.s = {"\x01+35p" } },
+ { CAPS_STOP, .u.s = {"\x01-35p" } },
+ { RATE, .u.n = {"\x01%ds", 8, 0, 9, 0, 0, NULL } },
+ { PITCH, .u.n = {"\x01%dp", 50, 0, 99, 0, 0, NULL } },
+ { VOL, .u.n = {"\x01%dv", 5, 0, 9, 0, 0, NULL } },
+ { TONE, .u.n = {"\x01%dx", 1, 0, 2, 0, 0, NULL } },
+ { PUNCT, .u.n = {"\x01%db", 7, 0, 15, 0, 0, NULL } },
+ { VOICE, .u.n = {"\x01%do", 0, 0, 7, 0, 0, NULL } },
+ { FREQUENCY, .u.n = {"\x01%df", 5, 0, 9, 0, 0, NULL } },
+ { DIRECT, .u.n = {NULL, 0, 0, 1, 0, 0, NULL } },
+ V_LAST_VAR
+};
+
+/*
+ * These attributes will appear in /sys/accessibility/speakup/dtlk.
+ */
+static struct kobj_attribute caps_start_attribute =
+ __ATTR(caps_start, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
+static struct kobj_attribute caps_stop_attribute =
+ __ATTR(caps_stop, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
+static struct kobj_attribute freq_attribute =
+ __ATTR(freq, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
+static struct kobj_attribute pitch_attribute =
+ __ATTR(pitch, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
+static struct kobj_attribute punct_attribute =
+ __ATTR(punct, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
+static struct kobj_attribute rate_attribute =
+ __ATTR(rate, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
+static struct kobj_attribute tone_attribute =
+ __ATTR(tone, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
+static struct kobj_attribute voice_attribute =
+ __ATTR(voice, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
+static struct kobj_attribute vol_attribute =
+ __ATTR(vol, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
+
+static struct kobj_attribute delay_time_attribute =
+ __ATTR(delay_time, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
+static struct kobj_attribute direct_attribute =
+ __ATTR(direct, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
+static struct kobj_attribute full_time_attribute =
+ __ATTR(full_time, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
+static struct kobj_attribute jiffy_delta_attribute =
+ __ATTR(jiffy_delta, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
+static struct kobj_attribute trigger_time_attribute =
+ __ATTR(trigger_time, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
+
+/*
+ * Create a group of attributes so that we can create and destroy them all
+ * at once.
+ */
+static struct attribute *synth_attrs[] = {
+ &caps_start_attribute.attr,
+ &caps_stop_attribute.attr,
+ &freq_attribute.attr,
+ &pitch_attribute.attr,
+ &punct_attribute.attr,
+ &rate_attribute.attr,
+ &tone_attribute.attr,
+ &voice_attribute.attr,
+ &vol_attribute.attr,
+ &delay_time_attribute.attr,
+ &direct_attribute.attr,
+ &full_time_attribute.attr,
+ &jiffy_delta_attribute.attr,
+ &trigger_time_attribute.attr,
+ NULL, /* need to NULL terminate the list of attributes */
+};
+
+static struct spk_synth synth_dtlk = {
+ .name = "dtlk",
+ .version = DRV_VERSION,
+ .long_name = "DoubleTalk PC",
+ .init = "\x01@\x01\x31y",
+ .procspeech = PROCSPEECH,
+ .clear = SYNTH_CLEAR,
+ .delay = 500,
+ .trigger = 30,
+ .jiffies = 50,
+ .full = 1000,
+ .startup = SYNTH_START,
+ .checkval = SYNTH_CHECK,
+ .vars = vars,
+ .probe = synth_probe,
+ .release = dtlk_release,
+ .synth_immediate = synth_immediate,
+ .catch_up = do_catch_up,
+ .flush = synth_flush,
+ .is_alive = spk_synth_is_alive_nop,
+ .synth_adjust = NULL,
+ .read_buff_add = NULL,
+ .get_index = spk_serial_in_nowait,
+ .indexing = {
+ .command = "\x01%di",
+ .lowindex = 1,
+ .highindex = 5,
+ .currindex = 1,
+ },
+ .attributes = {
+ .attrs = synth_attrs,
+ .name = "dtlk",
+ },
+};
+
+static inline bool synth_readable(void)
+{
+ synth_status = inb_p(speakup_info.port_tts + UART_RX);
+ return (synth_status & TTS_READABLE) != 0;
+}
+
+static inline bool synth_writable(void)
+{
+ synth_status = inb_p(speakup_info.port_tts + UART_RX);
+ return (synth_status & TTS_WRITABLE) != 0;
+}
+
+static inline bool synth_full(void)
+{
+ synth_status = inb_p(speakup_info.port_tts + UART_RX);
+ return (synth_status & TTS_ALMOST_FULL) != 0;
+}
+
+static void spk_out(const char ch)
+{
+ int timeout = SPK_XMITR_TIMEOUT;
+
+ while (!synth_writable()) {
+ if (!--timeout)
+ break;
+ udelay(1);
+ }
+ outb_p(ch, speakup_info.port_tts);
+ timeout = SPK_XMITR_TIMEOUT;
+ while (synth_writable()) {
+ if (!--timeout)
+ break;
+ udelay(1);
+ }
+}
+
+static void do_catch_up(struct spk_synth *synth)
+{
+ u_char ch;
+ unsigned long flags;
+ unsigned long jiff_max;
+ struct var_t *jiffy_delta;
+ struct var_t *delay_time;
+ int jiffy_delta_val;
+ int delay_time_val;
+
+ jiffy_delta = spk_get_var(JIFFY);
+ delay_time = spk_get_var(DELAY);
+ spin_lock_irqsave(&speakup_info.spinlock, flags);
+ jiffy_delta_val = jiffy_delta->u.n.value;
+ spin_unlock_irqrestore(&speakup_info.spinlock, flags);
+ jiff_max = jiffies + jiffy_delta_val;
+ while (!kthread_should_stop()) {
+ spin_lock_irqsave(&speakup_info.spinlock, flags);
+ if (speakup_info.flushing) {
+ speakup_info.flushing = 0;
+ spin_unlock_irqrestore(&speakup_info.spinlock, flags);
+ synth->flush(synth);
+ continue;
+ }
+ if (synth_buffer_empty()) {
+ spin_unlock_irqrestore(&speakup_info.spinlock, flags);
+ break;
+ }
+ set_current_state(TASK_INTERRUPTIBLE);
+ delay_time_val = delay_time->u.n.value;
+ spin_unlock_irqrestore(&speakup_info.spinlock, flags);
+ if (synth_full()) {
+ schedule_timeout(msecs_to_jiffies(delay_time_val));
+ continue;
+ }
+ set_current_state(TASK_RUNNING);
+ spin_lock_irqsave(&speakup_info.spinlock, flags);
+ ch = synth_buffer_getc();
+ spin_unlock_irqrestore(&speakup_info.spinlock, flags);
+ if (ch == '\n')
+ ch = PROCSPEECH;
+ spk_out(ch);
+ if (time_after_eq(jiffies, jiff_max) && (ch == SPACE)) {
+ spk_out(PROCSPEECH);
+ spin_lock_irqsave(&speakup_info.spinlock, flags);
+ delay_time_val = delay_time->u.n.value;
+ jiffy_delta_val = jiffy_delta->u.n.value;
+ spin_unlock_irqrestore(&speakup_info.spinlock, flags);
+ schedule_timeout(msecs_to_jiffies(delay_time_val));
+ jiff_max = jiffies + jiffy_delta_val;
+ }
+ }
+ spk_out(PROCSPEECH);
+}
+
+static const char *synth_immediate(struct spk_synth *synth, const char *buf)
+{
+ u_char ch;
+
+ while ((ch = (u_char)*buf)) {
+ if (synth_full())
+ return buf;
+ if (ch == '\n')
+ ch = PROCSPEECH;
+ spk_out(ch);
+ buf++;
+ }
+ return NULL;
+}
+
+static void synth_flush(struct spk_synth *synth)
+{
+ outb_p(SYNTH_CLEAR, speakup_info.port_tts);
+ while (synth_writable())
+ cpu_relax();
+}
+
+static char synth_read_tts(void)
+{
+ u_char ch;
+
+ while (!synth_readable())
+ cpu_relax();
+ ch = synth_status & 0x7f;
+ outb_p(ch, speakup_info.port_tts);
+ while (synth_readable())
+ cpu_relax();
+ return (char) ch;
+}
+
+/* interrogate the DoubleTalk PC and return its settings */
+static struct synth_settings *synth_interrogate(struct spk_synth *synth)
+{
+ u_char *t;
+ static char buf[sizeof(struct synth_settings) + 1];
+ int total, i;
+ static struct synth_settings status;
+
+ synth_immediate(synth, "\x18\x01?");
+ for (total = 0, i = 0; i < 50; i++) {
+ buf[total] = synth_read_tts();
+ if (total > 2 && buf[total] == 0x7f)
+ break;
+ if (total < sizeof(struct synth_settings))
+ total++;
+ }
+ t = buf;
+ /* serial number is little endian */
+ status.serial_number = t[0] + t[1]*256;
+ t += 2;
+ for (i = 0; *t != '\r'; t++) {
+ status.rom_version[i] = *t;
+ if (i < sizeof(status.rom_version)-1)
+ i++;
+ }
+ status.rom_version[i] = 0;
+ t++;
+ status.mode = *t++;
+ status.punc_level = *t++;
+ status.formant_freq = *t++;
+ status.pitch = *t++;
+ status.speed = *t++;
+ status.volume = *t++;
+ status.tone = *t++;
+ status.expression = *t++;
+ status.ext_dict_loaded = *t++;
+ status.ext_dict_status = *t++;
+ status.free_ram = *t++;
+ status.articulation = *t++;
+ status.reverb = *t++;
+ status.eob = *t++;
+ return &status;
+}
+
+static int synth_probe(struct spk_synth *synth)
+{
+ unsigned int port_val = 0;
+ int i = 0;
+ struct synth_settings *sp;
+
+ pr_info("Probing for DoubleTalk.\n");
+ if (port_forced) {
+ speakup_info.port_tts = port_forced;
+ pr_info("probe forced to %x by kernel command line\n",
+ speakup_info.port_tts);
+ if ((port_forced & 0xf) != 0xf)
+ pr_info("warning: port base should probably end with f\n");
+ if (synth_request_region(speakup_info.port_tts-1,
+ SYNTH_IO_EXTENT)) {
+ pr_warn("sorry, port already reserved\n");
+ return -EBUSY;
+ }
+ port_val = inw(speakup_info.port_tts-1);
+ synth_lpc = speakup_info.port_tts-1;
+ } else {
+ for (i = 0; synth_portlist[i]; i++) {
+ if (synth_request_region(synth_portlist[i],
+ SYNTH_IO_EXTENT))
+ continue;
+ port_val = inw(synth_portlist[i]) & 0xfbff;
+ if (port_val == 0x107f) {
+ synth_lpc = synth_portlist[i];
+ speakup_info.port_tts = synth_lpc+1;
+ break;
+ }
+ synth_release_region(synth_portlist[i],
+ SYNTH_IO_EXTENT);
+ }
+ }
+ port_val &= 0xfbff;
+ if (port_val != 0x107f) {
+ pr_info("DoubleTalk PC: not found\n");
+ if (synth_lpc)
+ synth_release_region(synth_lpc, SYNTH_IO_EXTENT);
+ return -ENODEV;
+ }
+ while (inw_p(synth_lpc) != 0x147f)
+ cpu_relax(); /* wait until it's ready */
+ sp = synth_interrogate(synth);
+ pr_info("%s: %03x-%03x, ROM ver %s, s/n %u, driver: %s\n",
+ synth->long_name, synth_lpc, synth_lpc+SYNTH_IO_EXTENT - 1,
+ sp->rom_version, sp->serial_number, synth->version);
+ synth->alive = 1;
+ return 0;
+}
+
+static void dtlk_release(void)
+{
+ if (speakup_info.port_tts)
+ synth_release_region(speakup_info.port_tts-1, SYNTH_IO_EXTENT);
+ speakup_info.port_tts = 0;
+}
+
+module_param_named(port, port_forced, int, S_IRUGO);
+module_param_named(start, synth_dtlk.startup, short, S_IRUGO);
+
+MODULE_PARM_DESC(port, "Set the port for the synthesizer (override probing).");
+MODULE_PARM_DESC(start, "Start the synthesizer once it is loaded.");
+
+module_spk_synth(synth_dtlk);
+
+MODULE_AUTHOR("Kirk Reiser <kirk@braille.uwo.ca>");
+MODULE_AUTHOR("David Borowski");
+MODULE_DESCRIPTION("Speakup support for DoubleTalk PC synthesizers");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(DRV_VERSION);
+
diff --git a/drivers/staging/speakup/speakup_dtlk.h b/drivers/staging/speakup/speakup_dtlk.h
new file mode 100644
index 000000000..d951d18c5
--- /dev/null
+++ b/drivers/staging/speakup/speakup_dtlk.h
@@ -0,0 +1,54 @@
+/* speakup_dtlk.h - header file for speakups DoubleTalk driver. */
+
+#define SYNTH_IO_EXTENT 0x02
+#define SYNTH_CLEAR 0x18 /* stops speech */
+ /* TTS Port Status Flags */
+#define TTS_READABLE 0x80 /* mask for bit which is nonzero if a
+ byte can be read from the TTS port */
+#define TTS_SPEAKING 0x40 /* mask for SYNC bit, which is nonzero
+ while DoubleTalk is producing
+ output with TTS, PCM or CVSD
+ synthesizers or tone generators
+ (that is, all but LPC) */
+#define TTS_SPEAKING2 0x20 /* mask for SYNC2 bit,
+ which falls to zero up to 0.4 sec
+ before speech stops */
+#define TTS_WRITABLE 0x10 /* mask for RDY bit, which when set to
+ 1, indicates the TTS port is ready
+ to accept a byte of data. The RDY
+ bit goes zero 2-3 usec after
+ writing, and goes 1 again 180-190
+ usec later. */
+#define TTS_ALMOST_FULL 0x08 /* mask for AF bit: When set to 1,
+ indicates that less than 300 bytes
+ are available in the TTS input
+ buffer. AF is always 0 in the PCM,
+ TGN and CVSD modes. */
+#define TTS_ALMOST_EMPTY 0x04 /* mask for AE bit: When set to 1,
+ indicates that less than 300 bytes
+ are remaining in DoubleTalk's input
+ (TTS or PCM) buffer. AE is always 1
+ in the TGN and CVSD modes. */
+
+ /* data returned by Interrogate command */
+struct synth_settings {
+ u_short serial_number; /* 0-7Fh:0-7Fh */
+ u_char rom_version[24]; /* null terminated string */
+ u_char mode; /* 0=Character; 1=Phoneme; 2=Text */
+ u_char punc_level; /* nB; 0-7 */
+ u_char formant_freq; /* nF; 0-9 */
+ u_char pitch; /* nP; 0-99 */
+ u_char speed; /* nS; 0-9 */
+ u_char volume; /* nV; 0-9 */
+ u_char tone; /* nX; 0-2 */
+ u_char expression; /* nE; 0-9 */
+ u_char ext_dict_loaded; /* 1=exception dictionary loaded */
+ u_char ext_dict_status; /* 1=exception dictionary enabled */
+ u_char free_ram; /* # pages (truncated) remaining for
+ * text buffer */
+ u_char articulation; /* nA; 0-9 */
+ u_char reverb; /* nR; 0-9 */
+ u_char eob; /* 7Fh value indicating end of
+ * parameter block */
+ u_char has_indexing; /* nonzero if indexing is implemented */
+};
diff --git a/drivers/staging/speakup/speakup_dummy.c b/drivers/staging/speakup/speakup_dummy.c
new file mode 100644
index 000000000..f66811269
--- /dev/null
+++ b/drivers/staging/speakup/speakup_dummy.c
@@ -0,0 +1,138 @@
+/*
+ * originally written by: Kirk Reiser <kirk@braille.uwo.ca>
+ * this version considerably modified by David Borowski, david575@rogers.com
+ * eventually modified by Samuel Thibault <samuel.thibault@ens-lyon.org>
+ *
+ * Copyright (C) 1998-99 Kirk Reiser.
+ * Copyright (C) 2003 David Borowski.
+ * Copyright (C) 2007 Samuel Thibault.
+ *
+ * This program is free software; you can redistribute it and/or 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
+ *
+ * specificly written as a driver for the speakup screenreview
+ * s not a general device driver.
+ */
+#include "spk_priv.h"
+#include "speakup.h"
+
+#define PROCSPEECH '\n'
+#define DRV_VERSION "2.11"
+#define SYNTH_CLEAR '!'
+
+static struct var_t vars[] = {
+ { CAPS_START, .u.s = {"CAPS_START\n" } },
+ { CAPS_STOP, .u.s = {"CAPS_STOP\n" } },
+ { RATE, .u.n = {"RATE %d\n", 8, 1, 16, 0, 0, NULL } },
+ { PITCH, .u.n = {"PITCH %d\n", 8, 0, 16, 0, 0, NULL } },
+ { VOL, .u.n = {"VOL %d\n", 8, 0, 16, 0, 0, NULL } },
+ { TONE, .u.n = {"TONE %d\n", 8, 0, 16, 0, 0, NULL } },
+ { DIRECT, .u.n = {NULL, 0, 0, 1, 0, 0, NULL } },
+ V_LAST_VAR
+};
+
+/*
+ * These attributes will appear in /sys/accessibility/speakup/dummy.
+ */
+static struct kobj_attribute caps_start_attribute =
+ __ATTR(caps_start, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
+static struct kobj_attribute caps_stop_attribute =
+ __ATTR(caps_stop, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
+static struct kobj_attribute pitch_attribute =
+ __ATTR(pitch, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
+static struct kobj_attribute rate_attribute =
+ __ATTR(rate, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
+static struct kobj_attribute tone_attribute =
+ __ATTR(tone, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
+static struct kobj_attribute vol_attribute =
+ __ATTR(vol, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
+
+static struct kobj_attribute delay_time_attribute =
+ __ATTR(delay_time, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
+static struct kobj_attribute direct_attribute =
+ __ATTR(direct, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
+static struct kobj_attribute full_time_attribute =
+ __ATTR(full_time, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
+static struct kobj_attribute jiffy_delta_attribute =
+ __ATTR(jiffy_delta, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
+static struct kobj_attribute trigger_time_attribute =
+ __ATTR(trigger_time, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
+
+/*
+ * Create a group of attributes so that we can create and destroy them all
+ * at once.
+ */
+static struct attribute *synth_attrs[] = {
+ &caps_start_attribute.attr,
+ &caps_stop_attribute.attr,
+ &pitch_attribute.attr,
+ &rate_attribute.attr,
+ &tone_attribute.attr,
+ &vol_attribute.attr,
+ &delay_time_attribute.attr,
+ &direct_attribute.attr,
+ &full_time_attribute.attr,
+ &jiffy_delta_attribute.attr,
+ &trigger_time_attribute.attr,
+ NULL, /* need to NULL terminate the list of attributes */
+};
+
+static struct spk_synth synth_dummy = {
+ .name = "dummy",
+ .version = DRV_VERSION,
+ .long_name = "Dummy",
+ .init = "Speakup\n",
+ .procspeech = PROCSPEECH,
+ .clear = SYNTH_CLEAR,
+ .delay = 500,
+ .trigger = 50,
+ .jiffies = 50,
+ .full = 40000,
+ .startup = SYNTH_START,
+ .checkval = SYNTH_CHECK,
+ .vars = vars,
+ .probe = spk_serial_synth_probe,
+ .release = spk_serial_release,
+ .synth_immediate = spk_synth_immediate,
+ .catch_up = spk_do_catch_up,
+ .flush = spk_synth_flush,
+ .is_alive = spk_synth_is_alive_restart,
+ .synth_adjust = NULL,
+ .read_buff_add = NULL,
+ .get_index = NULL,
+ .indexing = {
+ .command = NULL,
+ .lowindex = 0,
+ .highindex = 0,
+ .currindex = 0,
+ },
+ .attributes = {
+ .attrs = synth_attrs,
+ .name = "dummy",
+ },
+};
+
+module_param_named(ser, synth_dummy.ser, int, S_IRUGO);
+module_param_named(start, synth_dummy.startup, short, S_IRUGO);
+
+MODULE_PARM_DESC(ser, "Set the serial port for the synthesizer (0-based).");
+MODULE_PARM_DESC(start, "Start the synthesizer once it is loaded.");
+
+module_spk_synth(synth_dummy);
+
+MODULE_AUTHOR("Samuel Thibault <samuel.thibault@ens-lyon.org>");
+MODULE_DESCRIPTION("Speakup support for text console");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(DRV_VERSION);
+
diff --git a/drivers/staging/speakup/speakup_keypc.c b/drivers/staging/speakup/speakup_keypc.c
new file mode 100644
index 000000000..6ea027365
--- /dev/null
+++ b/drivers/staging/speakup/speakup_keypc.c
@@ -0,0 +1,328 @@
+/*
+ * written by David Borowski
+ *
+ * Copyright (C) 2003 David Borowski.
+ *
+ * This program is free software; you can redistribute it and/or 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
+ *
+ * specificly written as a driver for the speakup screenreview
+ * package it's not a general device driver.
+ * This driver is for the Keynote Gold internal synthesizer.
+ */
+#include <linux/jiffies.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/kthread.h>
+#include <linux/serial_reg.h>
+
+#include "spk_priv.h"
+#include "speakup.h"
+
+#define DRV_VERSION "2.10"
+#define SYNTH_IO_EXTENT 0x04
+#define SWAIT udelay(70)
+#define PROCSPEECH 0x1f
+#define SYNTH_CLEAR 0x03
+
+static int synth_probe(struct spk_synth *synth);
+static void keynote_release(void);
+static const char *synth_immediate(struct spk_synth *synth, const char *buf);
+static void do_catch_up(struct spk_synth *synth);
+static void synth_flush(struct spk_synth *synth);
+
+static int synth_port;
+static int port_forced;
+static unsigned int synth_portlist[] = { 0x2a8, 0 };
+
+static struct var_t vars[] = {
+ { CAPS_START, .u.s = {"[f130]" } },
+ { CAPS_STOP, .u.s = {"[f90]" } },
+ { RATE, .u.n = {"\04%c ", 8, 0, 10, 81, -8, NULL } },
+ { PITCH, .u.n = {"[f%d]", 5, 0, 9, 40, 10, NULL } },
+ { DIRECT, .u.n = {NULL, 0, 0, 1, 0, 0, NULL } },
+ V_LAST_VAR
+};
+
+/*
+ * These attributes will appear in /sys/accessibility/speakup/keypc.
+ */
+static struct kobj_attribute caps_start_attribute =
+ __ATTR(caps_start, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
+static struct kobj_attribute caps_stop_attribute =
+ __ATTR(caps_stop, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
+static struct kobj_attribute pitch_attribute =
+ __ATTR(pitch, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
+static struct kobj_attribute rate_attribute =
+ __ATTR(rate, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
+
+static struct kobj_attribute delay_time_attribute =
+ __ATTR(delay_time, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
+static struct kobj_attribute direct_attribute =
+ __ATTR(direct, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
+static struct kobj_attribute full_time_attribute =
+ __ATTR(full_time, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
+static struct kobj_attribute jiffy_delta_attribute =
+ __ATTR(jiffy_delta, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
+static struct kobj_attribute trigger_time_attribute =
+ __ATTR(trigger_time, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
+
+/*
+ * Create a group of attributes so that we can create and destroy them all
+ * at once.
+ */
+static struct attribute *synth_attrs[] = {
+ &caps_start_attribute.attr,
+ &caps_stop_attribute.attr,
+ &pitch_attribute.attr,
+ &rate_attribute.attr,
+ &delay_time_attribute.attr,
+ &direct_attribute.attr,
+ &full_time_attribute.attr,
+ &jiffy_delta_attribute.attr,
+ &trigger_time_attribute.attr,
+ NULL, /* need to NULL terminate the list of attributes */
+};
+
+static struct spk_synth synth_keypc = {
+ .name = "keypc",
+ .version = DRV_VERSION,
+ .long_name = "Keynote PC",
+ .init = "[t][n7,1][n8,0]",
+ .procspeech = PROCSPEECH,
+ .clear = SYNTH_CLEAR,
+ .delay = 500,
+ .trigger = 50,
+ .jiffies = 50,
+ .full = 1000,
+ .startup = SYNTH_START,
+ .checkval = SYNTH_CHECK,
+ .vars = vars,
+ .probe = synth_probe,
+ .release = keynote_release,
+ .synth_immediate = synth_immediate,
+ .catch_up = do_catch_up,
+ .flush = synth_flush,
+ .is_alive = spk_synth_is_alive_nop,
+ .synth_adjust = NULL,
+ .read_buff_add = NULL,
+ .get_index = NULL,
+ .indexing = {
+ .command = NULL,
+ .lowindex = 0,
+ .highindex = 0,
+ .currindex = 0,
+ },
+ .attributes = {
+ .attrs = synth_attrs,
+ .name = "keypc",
+ },
+};
+
+static inline bool synth_writable(void)
+{
+ return (inb_p(synth_port + UART_RX) & 0x10) != 0;
+}
+
+static inline bool synth_full(void)
+{
+ return (inb_p(synth_port + UART_RX) & 0x80) == 0;
+}
+
+static char *oops(void)
+{
+ int s1, s2, s3, s4;
+
+ s1 = inb_p(synth_port);
+ s2 = inb_p(synth_port+1);
+ s3 = inb_p(synth_port+2);
+ s4 = inb_p(synth_port+3);
+ pr_warn("synth timeout %d %d %d %d\n", s1, s2, s3, s4);
+ return NULL;
+}
+
+static const char *synth_immediate(struct spk_synth *synth, const char *buf)
+{
+ u_char ch;
+ int timeout;
+
+ while ((ch = *buf)) {
+ if (ch == '\n')
+ ch = PROCSPEECH;
+ if (synth_full())
+ return buf;
+ timeout = 1000;
+ while (synth_writable())
+ if (--timeout <= 0)
+ return oops();
+ outb_p(ch, synth_port);
+ udelay(70);
+ buf++;
+ }
+ return NULL;
+}
+
+static void do_catch_up(struct spk_synth *synth)
+{
+ u_char ch;
+ int timeout;
+ unsigned long flags;
+ unsigned long jiff_max;
+ struct var_t *jiffy_delta;
+ struct var_t *delay_time;
+ struct var_t *full_time;
+ int delay_time_val;
+ int full_time_val;
+ int jiffy_delta_val;
+
+ jiffy_delta = spk_get_var(JIFFY);
+ delay_time = spk_get_var(DELAY);
+ full_time = spk_get_var(FULL);
+spin_lock_irqsave(&speakup_info.spinlock, flags);
+ jiffy_delta_val = jiffy_delta->u.n.value;
+ spin_unlock_irqrestore(&speakup_info.spinlock, flags);
+
+ jiff_max = jiffies + jiffy_delta_val;
+ while (!kthread_should_stop()) {
+ spin_lock_irqsave(&speakup_info.spinlock, flags);
+ if (speakup_info.flushing) {
+ speakup_info.flushing = 0;
+ spin_unlock_irqrestore(&speakup_info.spinlock, flags);
+ synth->flush(synth);
+ continue;
+ }
+ if (synth_buffer_empty()) {
+ spin_unlock_irqrestore(&speakup_info.spinlock, flags);
+ break;
+ }
+ set_current_state(TASK_INTERRUPTIBLE);
+ full_time_val = full_time->u.n.value;
+ spin_unlock_irqrestore(&speakup_info.spinlock, flags);
+ if (synth_full()) {
+ schedule_timeout(msecs_to_jiffies(full_time_val));
+ continue;
+ }
+ set_current_state(TASK_RUNNING);
+ timeout = 1000;
+ while (synth_writable())
+ if (--timeout <= 0)
+ break;
+ if (timeout <= 0) {
+ oops();
+ break;
+ }
+ spin_lock_irqsave(&speakup_info.spinlock, flags);
+ ch = synth_buffer_getc();
+ spin_unlock_irqrestore(&speakup_info.spinlock, flags);
+ if (ch == '\n')
+ ch = PROCSPEECH;
+ outb_p(ch, synth_port);
+ SWAIT;
+ if (time_after_eq(jiffies, jiff_max) && (ch == SPACE)) {
+ timeout = 1000;
+ while (synth_writable())
+ if (--timeout <= 0)
+ break;
+ if (timeout <= 0) {
+ oops();
+ break;
+ }
+ outb_p(PROCSPEECH, synth_port);
+ spin_lock_irqsave(&speakup_info.spinlock, flags);
+ jiffy_delta_val = jiffy_delta->u.n.value;
+ delay_time_val = delay_time->u.n.value;
+ spin_unlock_irqrestore(&speakup_info.spinlock, flags);
+ schedule_timeout(msecs_to_jiffies(delay_time_val));
+ jiff_max = jiffies+jiffy_delta_val;
+ }
+ }
+ timeout = 1000;
+ while (synth_writable())
+ if (--timeout <= 0)
+ break;
+ if (timeout <= 0)
+ oops();
+ else
+ outb_p(PROCSPEECH, synth_port);
+}
+
+static void synth_flush(struct spk_synth *synth)
+{
+ outb_p(SYNTH_CLEAR, synth_port);
+}
+
+static int synth_probe(struct spk_synth *synth)
+{
+ unsigned int port_val = 0;
+ int i = 0;
+
+ pr_info("Probing for %s.\n", synth->long_name);
+ if (port_forced) {
+ synth_port = port_forced;
+ pr_info("probe forced to %x by kernel command line\n",
+ synth_port);
+ if (synth_request_region(synth_port-1, SYNTH_IO_EXTENT)) {
+ pr_warn("sorry, port already reserved\n");
+ return -EBUSY;
+ }
+ port_val = inb(synth_port);
+ } else {
+ for (i = 0; synth_portlist[i]; i++) {
+ if (synth_request_region(synth_portlist[i],
+ SYNTH_IO_EXTENT)) {
+ pr_warn
+ ("request_region: failed with 0x%x, %d\n",
+ synth_portlist[i], SYNTH_IO_EXTENT);
+ continue;
+ }
+ port_val = inb(synth_portlist[i]);
+ if (port_val == 0x80) {
+ synth_port = synth_portlist[i];
+ break;
+ }
+ }
+ }
+ if (port_val != 0x80) {
+ pr_info("%s: not found\n", synth->long_name);
+ synth_release_region(synth_port, SYNTH_IO_EXTENT);
+ synth_port = 0;
+ return -ENODEV;
+ }
+ pr_info("%s: %03x-%03x, driver version %s,\n", synth->long_name,
+ synth_port, synth_port+SYNTH_IO_EXTENT-1,
+ synth->version);
+ synth->alive = 1;
+ return 0;
+}
+
+static void keynote_release(void)
+{
+ if (synth_port)
+ synth_release_region(synth_port, SYNTH_IO_EXTENT);
+ synth_port = 0;
+}
+
+module_param_named(port, port_forced, int, S_IRUGO);
+module_param_named(start, synth_keypc.startup, short, S_IRUGO);
+
+MODULE_PARM_DESC(port, "Set the port for the synthesizer (override probing).");
+MODULE_PARM_DESC(start, "Start the synthesizer once it is loaded.");
+
+module_spk_synth(synth_keypc);
+
+MODULE_AUTHOR("David Borowski");
+MODULE_DESCRIPTION("Speakup support for Keynote Gold PC synthesizers");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(DRV_VERSION);
+
diff --git a/drivers/staging/speakup/speakup_ltlk.c b/drivers/staging/speakup/speakup_ltlk.c
new file mode 100644
index 000000000..cc4806be8
--- /dev/null
+++ b/drivers/staging/speakup/speakup_ltlk.c
@@ -0,0 +1,185 @@
+/*
+ * originally written by: Kirk Reiser <kirk@braille.uwo.ca>
+* this version considerably modified by David Borowski, david575@rogers.com
+ *
+ * Copyright (C) 1998-99 Kirk Reiser.
+ * Copyright (C) 2003 David Borowski.
+ *
+ * This program is free software; you can redistribute it and/or 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
+ *
+ * specificly written as a driver for the speakup screenreview
+ * s not a general device driver.
+ */
+#include "speakup.h"
+#include "spk_priv.h"
+#include "serialio.h"
+#include "speakup_dtlk.h" /* local header file for LiteTalk values */
+
+#define DRV_VERSION "2.11"
+#define PROCSPEECH 0x0d
+
+static int synth_probe(struct spk_synth *synth);
+
+static struct var_t vars[] = {
+ { CAPS_START, .u.s = {"\x01+35p" } },
+ { CAPS_STOP, .u.s = {"\x01-35p" } },
+ { RATE, .u.n = {"\x01%ds", 8, 0, 9, 0, 0, NULL } },
+ { PITCH, .u.n = {"\x01%dp", 50, 0, 99, 0, 0, NULL } },
+ { VOL, .u.n = {"\x01%dv", 5, 0, 9, 0, 0, NULL } },
+ { TONE, .u.n = {"\x01%dx", 1, 0, 2, 0, 0, NULL } },
+ { PUNCT, .u.n = {"\x01%db", 7, 0, 15, 0, 0, NULL } },
+ { VOICE, .u.n = {"\x01%do", 0, 0, 7, 0, 0, NULL } },
+ { FREQUENCY, .u.n = {"\x01%df", 5, 0, 9, 0, 0, NULL } },
+ { DIRECT, .u.n = {NULL, 0, 0, 1, 0, 0, NULL } },
+ V_LAST_VAR
+};
+
+/*
+ * These attributes will appear in /sys/accessibility/speakup/ltlk.
+ */
+static struct kobj_attribute caps_start_attribute =
+ __ATTR(caps_start, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
+static struct kobj_attribute caps_stop_attribute =
+ __ATTR(caps_stop, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
+static struct kobj_attribute freq_attribute =
+ __ATTR(freq, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
+static struct kobj_attribute pitch_attribute =
+ __ATTR(pitch, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
+static struct kobj_attribute punct_attribute =
+ __ATTR(punct, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
+static struct kobj_attribute rate_attribute =
+ __ATTR(rate, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
+static struct kobj_attribute tone_attribute =
+ __ATTR(tone, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
+static struct kobj_attribute voice_attribute =
+ __ATTR(voice, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
+static struct kobj_attribute vol_attribute =
+ __ATTR(vol, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
+
+static struct kobj_attribute delay_time_attribute =
+ __ATTR(delay_time, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
+static struct kobj_attribute direct_attribute =
+ __ATTR(direct, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
+static struct kobj_attribute full_time_attribute =
+ __ATTR(full_time, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
+static struct kobj_attribute jiffy_delta_attribute =
+ __ATTR(jiffy_delta, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
+static struct kobj_attribute trigger_time_attribute =
+ __ATTR(trigger_time, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
+
+/*
+ * Create a group of attributes so that we can create and destroy them all
+ * at once.
+ */
+static struct attribute *synth_attrs[] = {
+ &caps_start_attribute.attr,
+ &caps_stop_attribute.attr,
+ &freq_attribute.attr,
+ &pitch_attribute.attr,
+ &punct_attribute.attr,
+ &rate_attribute.attr,
+ &tone_attribute.attr,
+ &voice_attribute.attr,
+ &vol_attribute.attr,
+ &delay_time_attribute.attr,
+ &direct_attribute.attr,
+ &full_time_attribute.attr,
+ &jiffy_delta_attribute.attr,
+ &trigger_time_attribute.attr,
+ NULL, /* need to NULL terminate the list of attributes */
+};
+
+static struct spk_synth synth_ltlk = {
+ .name = "ltlk",
+ .version = DRV_VERSION,
+ .long_name = "LiteTalk",
+ .init = "\01@\x01\x31y\n\0",
+ .procspeech = PROCSPEECH,
+ .clear = SYNTH_CLEAR,
+ .delay = 500,
+ .trigger = 50,
+ .jiffies = 50,
+ .full = 40000,
+ .startup = SYNTH_START,
+ .checkval = SYNTH_CHECK,
+ .vars = vars,
+ .probe = synth_probe,
+ .release = spk_serial_release,
+ .synth_immediate = spk_synth_immediate,
+ .catch_up = spk_do_catch_up,
+ .flush = spk_synth_flush,
+ .is_alive = spk_synth_is_alive_restart,
+ .synth_adjust = NULL,
+ .read_buff_add = NULL,
+ .get_index = spk_serial_in_nowait,
+ .indexing = {
+ .command = "\x01%di",
+ .lowindex = 1,
+ .highindex = 5,
+ .currindex = 1,
+ },
+ .attributes = {
+ .attrs = synth_attrs,
+ .name = "ltlk",
+ },
+};
+
+/* interrogate the LiteTalk and print its settings */
+static void synth_interrogate(struct spk_synth *synth)
+{
+ unsigned char *t, i;
+ unsigned char buf[50], rom_v[20];
+
+ spk_synth_immediate(synth, "\x18\x01?");
+ for (i = 0; i < 50; i++) {
+ buf[i] = spk_serial_in();
+ if (i > 2 && buf[i] == 0x7f)
+ break;
+ }
+ t = buf+2;
+ for (i = 0; *t != '\r'; t++) {
+ rom_v[i] = *t;
+ if (++i >= 19)
+ break;
+ }
+ rom_v[i] = 0;
+ pr_info("%s: ROM version: %s\n", synth->long_name, rom_v);
+}
+
+static int synth_probe(struct spk_synth *synth)
+{
+ int failed = 0;
+
+ failed = spk_serial_synth_probe(synth);
+ if (failed == 0)
+ synth_interrogate(synth);
+ synth->alive = !failed;
+ return failed;
+}
+
+module_param_named(ser, synth_ltlk.ser, int, S_IRUGO);
+module_param_named(start, synth_ltlk.startup, short, S_IRUGO);
+
+MODULE_PARM_DESC(ser, "Set the serial port for the synthesizer (0-based).");
+MODULE_PARM_DESC(start, "Start the synthesizer once it is loaded.");
+
+module_spk_synth(synth_ltlk);
+
+MODULE_AUTHOR("Kirk Reiser <kirk@braille.uwo.ca>");
+MODULE_AUTHOR("David Borowski");
+MODULE_DESCRIPTION("Speakup support for DoubleTalk LT/LiteTalk synthesizers");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(DRV_VERSION);
+
diff --git a/drivers/staging/speakup/speakup_soft.c b/drivers/staging/speakup/speakup_soft.c
new file mode 100644
index 000000000..fb31bb95d
--- /dev/null
+++ b/drivers/staging/speakup/speakup_soft.c
@@ -0,0 +1,359 @@
+/* speakup_soft.c - speakup driver to register and make available
+ * a user space device for software synthesizers. written by: Kirk
+ * Reiser <kirk@braille.uwo.ca>
+ *
+ * Copyright (C) 2003 Kirk Reiser.
+ *
+ * This program is free software; you can redistribute it and/or 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 code is specificly written as a driver for the speakup screenreview
+ * package and is not a general device driver. */
+
+#include <linux/unistd.h>
+#include <linux/miscdevice.h> /* for misc_register, and SYNTH_MINOR */
+#include <linux/poll.h> /* for poll_wait() */
+#include <linux/sched.h> /* schedule(), signal_pending(), TASK_INTERRUPTIBLE */
+
+#include "spk_priv.h"
+#include "speakup.h"
+
+#define DRV_VERSION "2.6"
+#define SOFTSYNTH_MINOR 26 /* might as well give it one more than /dev/synth */
+#define PROCSPEECH 0x0d
+#define CLEAR_SYNTH 0x18
+
+static int softsynth_probe(struct spk_synth *synth);
+static void softsynth_release(void);
+static int softsynth_is_alive(struct spk_synth *synth);
+static unsigned char get_index(void);
+
+static struct miscdevice synth_device;
+static int init_pos;
+static int misc_registered;
+
+static struct var_t vars[] = {
+ { CAPS_START, .u.s = {"\x01+3p" } },
+ { CAPS_STOP, .u.s = {"\x01-3p" } },
+ { RATE, .u.n = {"\x01%ds", 2, 0, 9, 0, 0, NULL } },
+ { PITCH, .u.n = {"\x01%dp", 5, 0, 9, 0, 0, NULL } },
+ { VOL, .u.n = {"\x01%dv", 5, 0, 9, 0, 0, NULL } },
+ { TONE, .u.n = {"\x01%dx", 1, 0, 2, 0, 0, NULL } },
+ { PUNCT, .u.n = {"\x01%db", 0, 0, 2, 0, 0, NULL } },
+ { VOICE, .u.n = {"\x01%do", 0, 0, 7, 0, 0, NULL } },
+ { FREQUENCY, .u.n = {"\x01%df", 5, 0, 9, 0, 0, NULL } },
+ { DIRECT, .u.n = {NULL, 0, 0, 1, 0, 0, NULL } },
+ V_LAST_VAR
+};
+
+/*
+ * These attributes will appear in /sys/accessibility/speakup/soft.
+ */
+static struct kobj_attribute caps_start_attribute =
+ __ATTR(caps_start, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
+static struct kobj_attribute caps_stop_attribute =
+ __ATTR(caps_stop, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
+static struct kobj_attribute freq_attribute =
+ __ATTR(freq, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
+static struct kobj_attribute pitch_attribute =
+ __ATTR(pitch, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
+static struct kobj_attribute punct_attribute =
+ __ATTR(punct, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
+static struct kobj_attribute rate_attribute =
+ __ATTR(rate, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
+static struct kobj_attribute tone_attribute =
+ __ATTR(tone, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
+static struct kobj_attribute voice_attribute =
+ __ATTR(voice, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
+static struct kobj_attribute vol_attribute =
+ __ATTR(vol, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
+
+/*
+ * We should uncomment the following definition, when we agree on a
+ * method of passing a language designation to the software synthesizer.
+ * static struct kobj_attribute lang_attribute =
+ * __ATTR(lang, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
+ */
+
+static struct kobj_attribute delay_time_attribute =
+ __ATTR(delay_time, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
+static struct kobj_attribute direct_attribute =
+ __ATTR(direct, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
+static struct kobj_attribute full_time_attribute =
+ __ATTR(full_time, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
+static struct kobj_attribute jiffy_delta_attribute =
+ __ATTR(jiffy_delta, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
+static struct kobj_attribute trigger_time_attribute =
+ __ATTR(trigger_time, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
+
+/*
+ * Create a group of attributes so that we can create and destroy them all
+ * at once.
+ */
+static struct attribute *synth_attrs[] = {
+ &caps_start_attribute.attr,
+ &caps_stop_attribute.attr,
+ &freq_attribute.attr,
+/* &lang_attribute.attr, */
+ &pitch_attribute.attr,
+ &punct_attribute.attr,
+ &rate_attribute.attr,
+ &tone_attribute.attr,
+ &voice_attribute.attr,
+ &vol_attribute.attr,
+ &delay_time_attribute.attr,
+ &direct_attribute.attr,
+ &full_time_attribute.attr,
+ &jiffy_delta_attribute.attr,
+ &trigger_time_attribute.attr,
+ NULL, /* need to NULL terminate the list of attributes */
+};
+
+static struct spk_synth synth_soft = {
+ .name = "soft",
+ .version = DRV_VERSION,
+ .long_name = "software synth",
+ .init = "\01@\x01\x31y\n",
+ .procspeech = PROCSPEECH,
+ .delay = 0,
+ .trigger = 0,
+ .jiffies = 0,
+ .full = 0,
+ .startup = SYNTH_START,
+ .checkval = SYNTH_CHECK,
+ .vars = vars,
+ .probe = softsynth_probe,
+ .release = softsynth_release,
+ .synth_immediate = NULL,
+ .catch_up = NULL,
+ .flush = NULL,
+ .is_alive = softsynth_is_alive,
+ .synth_adjust = NULL,
+ .read_buff_add = NULL,
+ .get_index = get_index,
+ .indexing = {
+ .command = "\x01%di",
+ .lowindex = 1,
+ .highindex = 5,
+ .currindex = 1,
+ },
+ .attributes = {
+ .attrs = synth_attrs,
+ .name = "soft",
+ },
+};
+
+static char *get_initstring(void)
+{
+ static char buf[40];
+ char *cp;
+ struct var_t *var;
+
+ memset(buf, 0, sizeof(buf));
+ cp = buf;
+ var = synth_soft.vars;
+ while (var->var_id != MAXVARS) {
+ if (var->var_id != CAPS_START && var->var_id != CAPS_STOP
+ && var->var_id != DIRECT)
+ cp = cp + sprintf(cp, var->u.n.synth_fmt,
+ var->u.n.value);
+ var++;
+ }
+ cp = cp + sprintf(cp, "\n");
+ return buf;
+}
+
+static int softsynth_open(struct inode *inode, struct file *fp)
+{
+ unsigned long flags;
+ /*if ((fp->f_flags & O_ACCMODE) != O_RDONLY) */
+ /* return -EPERM; */
+ spin_lock_irqsave(&speakup_info.spinlock, flags);
+ if (synth_soft.alive) {
+ spin_unlock_irqrestore(&speakup_info.spinlock, flags);
+ return -EBUSY;
+ }
+ synth_soft.alive = 1;
+ spin_unlock_irqrestore(&speakup_info.spinlock, flags);
+ return 0;
+}
+
+static int softsynth_close(struct inode *inode, struct file *fp)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&speakup_info.spinlock, flags);
+ synth_soft.alive = 0;
+ init_pos = 0;
+ spin_unlock_irqrestore(&speakup_info.spinlock, flags);
+ /* Make sure we let applications go before leaving */
+ speakup_start_ttys();
+ return 0;
+}
+
+static ssize_t softsynth_read(struct file *fp, char __user *buf, size_t count,
+ loff_t *pos)
+{
+ int chars_sent = 0;
+ char __user *cp;
+ char *init;
+ char ch;
+ int empty;
+ unsigned long flags;
+ DEFINE_WAIT(wait);
+
+ spin_lock_irqsave(&speakup_info.spinlock, flags);
+ while (1) {
+ prepare_to_wait(&speakup_event, &wait, TASK_INTERRUPTIBLE);
+ if (!synth_buffer_empty() || speakup_info.flushing)
+ break;
+ spin_unlock_irqrestore(&speakup_info.spinlock, flags);
+ if (fp->f_flags & O_NONBLOCK) {
+ finish_wait(&speakup_event, &wait);
+ return -EAGAIN;
+ }
+ if (signal_pending(current)) {
+ finish_wait(&speakup_event, &wait);
+ return -ERESTARTSYS;
+ }
+ schedule();
+ spin_lock_irqsave(&speakup_info.spinlock, flags);
+ }
+ finish_wait(&speakup_event, &wait);
+
+ cp = buf;
+ init = get_initstring();
+ while (chars_sent < count) {
+ if (speakup_info.flushing) {
+ speakup_info.flushing = 0;
+ ch = '\x18';
+ } else if (synth_buffer_empty()) {
+ break;
+ } else if (init[init_pos]) {
+ ch = init[init_pos++];
+ } else {
+ ch = synth_buffer_getc();
+ }
+ spin_unlock_irqrestore(&speakup_info.spinlock, flags);
+ if (copy_to_user(cp, &ch, 1))
+ return -EFAULT;
+ spin_lock_irqsave(&speakup_info.spinlock, flags);
+ chars_sent++;
+ cp++;
+ }
+ *pos += chars_sent;
+ empty = synth_buffer_empty();
+ spin_unlock_irqrestore(&speakup_info.spinlock, flags);
+ if (empty) {
+ speakup_start_ttys();
+ *pos = 0;
+ }
+ return chars_sent;
+}
+
+static int last_index;
+
+static ssize_t softsynth_write(struct file *fp, const char __user *buf,
+ size_t count, loff_t *pos)
+{
+ unsigned long supplied_index = 0;
+ int converted;
+
+ converted = kstrtoul_from_user(buf, count, 0, &supplied_index);
+
+ if (converted < 0)
+ return converted;
+
+ last_index = supplied_index;
+ return count;
+}
+
+static unsigned int softsynth_poll(struct file *fp,
+ struct poll_table_struct *wait)
+{
+ unsigned long flags;
+ int ret = 0;
+
+ poll_wait(fp, &speakup_event, wait);
+
+ spin_lock_irqsave(&speakup_info.spinlock, flags);
+ if (!synth_buffer_empty() || speakup_info.flushing)
+ ret = POLLIN | POLLRDNORM;
+ spin_unlock_irqrestore(&speakup_info.spinlock, flags);
+ return ret;
+}
+
+static unsigned char get_index(void)
+{
+ int rv;
+
+ rv = last_index;
+ last_index = 0;
+ return rv;
+}
+
+static const struct file_operations softsynth_fops = {
+ .owner = THIS_MODULE,
+ .poll = softsynth_poll,
+ .read = softsynth_read,
+ .write = softsynth_write,
+ .open = softsynth_open,
+ .release = softsynth_close,
+};
+
+
+static int softsynth_probe(struct spk_synth *synth)
+{
+
+ if (misc_registered != 0)
+ return 0;
+ memset(&synth_device, 0, sizeof(synth_device));
+ synth_device.minor = SOFTSYNTH_MINOR;
+ synth_device.name = "softsynth";
+ synth_device.fops = &softsynth_fops;
+ if (misc_register(&synth_device)) {
+ pr_warn("Couldn't initialize miscdevice /dev/softsynth.\n");
+ return -ENODEV;
+ }
+
+ misc_registered = 1;
+ pr_info("initialized device: /dev/softsynth, node (MAJOR 10, MINOR 26)\n");
+ return 0;
+}
+
+static void softsynth_release(void)
+{
+ misc_deregister(&synth_device);
+ misc_registered = 0;
+ pr_info("unregistered /dev/softsynth\n");
+}
+
+static int softsynth_is_alive(struct spk_synth *synth)
+{
+ if (synth_soft.alive)
+ return 1;
+ return 0;
+}
+
+module_param_named(start, synth_soft.startup, short, S_IRUGO);
+
+MODULE_PARM_DESC(start, "Start the synthesizer once it is loaded.");
+
+module_spk_synth(synth_soft);
+
+MODULE_AUTHOR("Kirk Reiser <kirk@braille.uwo.ca>");
+MODULE_DESCRIPTION("Speakup userspace software synthesizer support");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(DRV_VERSION);
+
diff --git a/drivers/staging/speakup/speakup_spkout.c b/drivers/staging/speakup/speakup_spkout.c
new file mode 100644
index 000000000..1007a6168
--- /dev/null
+++ b/drivers/staging/speakup/speakup_spkout.c
@@ -0,0 +1,156 @@
+/*
+ * originally written by: Kirk Reiser <kirk@braille.uwo.ca>
+* this version considerably modified by David Borowski, david575@rogers.com
+ *
+ * Copyright (C) 1998-99 Kirk Reiser.
+ * Copyright (C) 2003 David Borowski.
+ *
+ * This program is free software; you can redistribute it and/or 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
+ *
+ * specificly written as a driver for the speakup screenreview
+ * s not a general device driver.
+ */
+#include "spk_priv.h"
+#include "speakup.h"
+#include "serialio.h"
+
+#define DRV_VERSION "2.11"
+#define SYNTH_CLEAR 0x18
+#define PROCSPEECH '\r'
+
+static void synth_flush(struct spk_synth *synth);
+
+static struct var_t vars[] = {
+ { CAPS_START, .u.s = {"\x05P+" } },
+ { CAPS_STOP, .u.s = {"\x05P-" } },
+ { RATE, .u.n = {"\x05R%d", 7, 0, 9, 0, 0, NULL } },
+ { PITCH, .u.n = {"\x05P%d", 3, 0, 9, 0, 0, NULL } },
+ { VOL, .u.n = {"\x05V%d", 9, 0, 9, 0, 0, NULL } },
+ { TONE, .u.n = {"\x05T%c", 8, 0, 25, 65, 0, NULL } },
+ { PUNCT, .u.n = {"\x05M%c", 0, 0, 3, 0, 0, "nsma" } },
+ { DIRECT, .u.n = {NULL, 0, 0, 1, 0, 0, NULL } },
+ V_LAST_VAR
+};
+
+/*
+ * These attributes will appear in /sys/accessibility/speakup/spkout.
+ */
+static struct kobj_attribute caps_start_attribute =
+ __ATTR(caps_start, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
+static struct kobj_attribute caps_stop_attribute =
+ __ATTR(caps_stop, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
+static struct kobj_attribute pitch_attribute =
+ __ATTR(pitch, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
+static struct kobj_attribute punct_attribute =
+ __ATTR(punct, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
+static struct kobj_attribute rate_attribute =
+ __ATTR(rate, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
+static struct kobj_attribute tone_attribute =
+ __ATTR(tone, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
+static struct kobj_attribute vol_attribute =
+ __ATTR(vol, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
+
+static struct kobj_attribute delay_time_attribute =
+ __ATTR(delay_time, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
+static struct kobj_attribute direct_attribute =
+ __ATTR(direct, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
+static struct kobj_attribute full_time_attribute =
+ __ATTR(full_time, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
+static struct kobj_attribute jiffy_delta_attribute =
+ __ATTR(jiffy_delta, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
+static struct kobj_attribute trigger_time_attribute =
+ __ATTR(trigger_time, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
+
+/*
+ * Create a group of attributes so that we can create and destroy them all
+ * at once.
+ */
+static struct attribute *synth_attrs[] = {
+ &caps_start_attribute.attr,
+ &caps_stop_attribute.attr,
+ &pitch_attribute.attr,
+ &punct_attribute.attr,
+ &rate_attribute.attr,
+ &tone_attribute.attr,
+ &vol_attribute.attr,
+ &delay_time_attribute.attr,
+ &direct_attribute.attr,
+ &full_time_attribute.attr,
+ &jiffy_delta_attribute.attr,
+ &trigger_time_attribute.attr,
+ NULL, /* need to NULL terminate the list of attributes */
+};
+
+static struct spk_synth synth_spkout = {
+ .name = "spkout",
+ .version = DRV_VERSION,
+ .long_name = "Speakout",
+ .init = "\005W1\005I2\005C3",
+ .procspeech = PROCSPEECH,
+ .clear = SYNTH_CLEAR,
+ .delay = 500,
+ .trigger = 50,
+ .jiffies = 50,
+ .full = 40000,
+ .startup = SYNTH_START,
+ .checkval = SYNTH_CHECK,
+ .vars = vars,
+ .probe = spk_serial_synth_probe,
+ .release = spk_serial_release,
+ .synth_immediate = spk_synth_immediate,
+ .catch_up = spk_do_catch_up,
+ .flush = synth_flush,
+ .is_alive = spk_synth_is_alive_restart,
+ .synth_adjust = NULL,
+ .read_buff_add = NULL,
+ .get_index = spk_serial_in_nowait,
+ .indexing = {
+ .command = "\x05[%c",
+ .lowindex = 1,
+ .highindex = 5,
+ .currindex = 1,
+ },
+ .attributes = {
+ .attrs = synth_attrs,
+ .name = "spkout",
+ },
+};
+
+static void synth_flush(struct spk_synth *synth)
+{
+ int timeout = SPK_XMITR_TIMEOUT;
+
+ while (spk_serial_tx_busy()) {
+ if (!--timeout)
+ break;
+ udelay(1);
+ }
+ outb(SYNTH_CLEAR, speakup_info.port_tts);
+}
+
+module_param_named(ser, synth_spkout.ser, int, S_IRUGO);
+module_param_named(start, synth_spkout.startup, short, S_IRUGO);
+
+MODULE_PARM_DESC(ser, "Set the serial port for the synthesizer (0-based).");
+MODULE_PARM_DESC(start, "Start the synthesizer once it is loaded.");
+
+module_spk_synth(synth_spkout);
+
+MODULE_AUTHOR("Kirk Reiser <kirk@braille.uwo.ca>");
+MODULE_AUTHOR("David Borowski");
+MODULE_DESCRIPTION("Speakup support for Speak Out synthesizers");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(DRV_VERSION);
+
diff --git a/drivers/staging/speakup/speakup_txprt.c b/drivers/staging/speakup/speakup_txprt.c
new file mode 100644
index 000000000..6c21e7112
--- /dev/null
+++ b/drivers/staging/speakup/speakup_txprt.c
@@ -0,0 +1,137 @@
+/*
+ * originally written by: Kirk Reiser <kirk@braille.uwo.ca>
+* this version considerably modified by David Borowski, david575@rogers.com
+ *
+ * Copyright (C) 1998-99 Kirk Reiser.
+ * Copyright (C) 2003 David Borowski.
+ *
+ * This program is free software; you can redistribute it and/or 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
+ *
+ * specificly written as a driver for the speakup screenreview
+ * s not a general device driver.
+ */
+#include "spk_priv.h"
+#include "speakup.h"
+
+#define DRV_VERSION "2.11"
+#define SYNTH_CLEAR 0x18
+#define PROCSPEECH '\r' /* process speech char */
+
+static struct var_t vars[] = {
+ { CAPS_START, .u.s = {"\x05P8" } },
+ { CAPS_STOP, .u.s = {"\x05P5" } },
+ { RATE, .u.n = {"\x05R%d", 5, 0, 9, 0, 0, NULL } },
+ { PITCH, .u.n = {"\x05P%d", 5, 0, 9, 0, 0, NULL } },
+ { VOL, .u.n = {"\x05V%d", 5, 0, 9, 0, 0, NULL } },
+ { TONE, .u.n = {"\x05T%c", 12, 0, 25, 61, 0, NULL } },
+ { DIRECT, .u.n = {NULL, 0, 0, 1, 0, 0, NULL } },
+ V_LAST_VAR
+ };
+
+/*
+ * These attributes will appear in /sys/accessibility/speakup/txprt.
+ */
+static struct kobj_attribute caps_start_attribute =
+ __ATTR(caps_start, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
+static struct kobj_attribute caps_stop_attribute =
+ __ATTR(caps_stop, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
+static struct kobj_attribute pitch_attribute =
+ __ATTR(pitch, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
+static struct kobj_attribute rate_attribute =
+ __ATTR(rate, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
+static struct kobj_attribute tone_attribute =
+ __ATTR(tone, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
+static struct kobj_attribute vol_attribute =
+ __ATTR(vol, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
+
+static struct kobj_attribute delay_time_attribute =
+ __ATTR(delay_time, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
+static struct kobj_attribute direct_attribute =
+ __ATTR(direct, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
+static struct kobj_attribute full_time_attribute =
+ __ATTR(full_time, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
+static struct kobj_attribute jiffy_delta_attribute =
+ __ATTR(jiffy_delta, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
+static struct kobj_attribute trigger_time_attribute =
+ __ATTR(trigger_time, S_IWUSR|S_IRUGO, spk_var_show, spk_var_store);
+
+/*
+ * Create a group of attributes so that we can create and destroy them all
+ * at once.
+ */
+static struct attribute *synth_attrs[] = {
+ &caps_start_attribute.attr,
+ &caps_stop_attribute.attr,
+ &pitch_attribute.attr,
+ &rate_attribute.attr,
+ &tone_attribute.attr,
+ &vol_attribute.attr,
+ &delay_time_attribute.attr,
+ &direct_attribute.attr,
+ &full_time_attribute.attr,
+ &jiffy_delta_attribute.attr,
+ &trigger_time_attribute.attr,
+ NULL, /* need to NULL terminate the list of attributes */
+};
+
+static struct spk_synth synth_txprt = {
+ .name = "txprt",
+ .version = DRV_VERSION,
+ .long_name = "Transport",
+ .init = "\x05N1",
+ .procspeech = PROCSPEECH,
+ .clear = SYNTH_CLEAR,
+ .delay = 500,
+ .trigger = 50,
+ .jiffies = 50,
+ .full = 40000,
+ .startup = SYNTH_START,
+ .checkval = SYNTH_CHECK,
+ .vars = vars,
+ .probe = spk_serial_synth_probe,
+ .release = spk_serial_release,
+ .synth_immediate = spk_synth_immediate,
+ .catch_up = spk_do_catch_up,
+ .flush = spk_synth_flush,
+ .is_alive = spk_synth_is_alive_restart,
+ .synth_adjust = NULL,
+ .read_buff_add = NULL,
+ .get_index = NULL,
+ .indexing = {
+ .command = NULL,
+ .lowindex = 0,
+ .highindex = 0,
+ .currindex = 0,
+ },
+ .attributes = {
+ .attrs = synth_attrs,
+ .name = "txprt",
+ },
+};
+
+module_param_named(ser, synth_txprt.ser, int, S_IRUGO);
+module_param_named(start, synth_txprt.startup, short, S_IRUGO);
+
+MODULE_PARM_DESC(ser, "Set the serial port for the synthesizer (0-based).");
+MODULE_PARM_DESC(start, "Start the synthesizer once it is loaded.");
+
+module_spk_synth(synth_txprt);
+
+MODULE_AUTHOR("Kirk Reiser <kirk@braille.uwo.ca>");
+MODULE_AUTHOR("David Borowski");
+MODULE_DESCRIPTION("Speakup support for Transport synthesizers");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(DRV_VERSION);
+
diff --git a/drivers/staging/speakup/speakupmap.h b/drivers/staging/speakup/speakupmap.h
new file mode 100644
index 000000000..f1c0dd3b2
--- /dev/null
+++ b/drivers/staging/speakup/speakupmap.h
@@ -0,0 +1,65 @@
+ 119, 62, 6,
+ 0, 16, 20, 17, 32, 48, 0,
+ 2, 0, 78, 0, 0, 0, 0,
+ 3, 0, 79, 0, 0, 0, 0,
+ 4, 0, 76, 0, 0, 0, 0,
+ 5, 0, 77, 0, 0, 0, 0,
+ 6, 0, 74, 0, 0, 0, 0,
+ 7, 0, 75, 0, 0, 0, 0,
+ 9, 0, 5, 46, 0, 0, 0,
+ 10, 0, 4, 0, 0, 0, 0,
+ 11, 0, 0, 1, 0, 0, 0,
+ 12, 0, 27, 0, 33, 0, 0,
+ 19, 0, 47, 0, 0, 0, 0,
+ 21, 0, 29, 17, 0, 0, 0,
+ 22, 0, 15, 0, 0, 0, 0,
+ 23, 0, 14, 0, 0, 0, 28,
+ 24, 0, 16, 0, 0, 0, 0,
+ 25, 0, 30, 18, 0, 0, 0,
+ 28, 0, 3, 26, 0, 0, 0,
+ 35, 0, 31, 0, 0, 0, 0,
+ 36, 0, 12, 0, 0, 0, 0,
+ 37, 0, 11, 0, 0, 0, 22,
+ 38, 0, 13, 0, 0, 0, 0,
+ 39, 0, 32, 7, 0, 0, 0,
+ 40, 0, 23, 0, 0, 0, 0,
+ 44, 0, 44, 0, 0, 0, 0,
+ 49, 0, 24, 0, 0, 0, 0,
+ 50, 0, 9, 19, 6, 0, 0,
+ 51, 0, 8, 0, 0, 0, 36,
+ 52, 0, 10, 20, 0, 0, 0,
+ 53, 0, 25, 0, 0, 0, 0,
+ 55, 46, 1, 0, 0, 0, 0,
+ 58, 128, 128, 0, 0, 0, 0,
+ 59, 0, 45, 0, 0, 0, 0,
+ 60, 0, 40, 0, 0, 0, 0,
+ 61, 0, 41, 0, 0, 0, 0,
+ 62, 0, 42, 0, 0, 0, 0,
+ 63, 0, 34, 0, 0, 0, 0,
+ 64, 0, 35, 0, 0, 0, 0,
+ 65, 0, 37, 0, 0, 0, 0,
+ 66, 0, 38, 0, 0, 0, 0,
+ 67, 0, 66, 0, 39, 0, 0,
+ 68, 0, 67, 0, 0, 0, 0,
+ 71, 15, 19, 0, 0, 0, 0,
+ 72, 14, 29, 0, 0, 28, 0,
+ 73, 16, 17, 0, 0, 0, 0,
+ 74, 27, 33, 0, 0, 0, 0,
+ 75, 12, 31, 0, 0, 0, 0,
+ 76, 11, 21, 0, 0, 22, 0,
+ 77, 13, 32, 0, 0, 0, 0,
+ 78, 23, 43, 0, 0, 0, 0,
+ 79, 9, 20, 0, 0, 0, 0,
+ 80, 8, 30, 0, 0, 36, 0,
+ 81, 10, 18, 0, 0, 0, 0,
+ 82, 128, 128, 0, 0, 0, 0,
+ 83, 24, 25, 0, 0, 0, 0,
+ 87, 0, 68, 0, 0, 0, 0,
+ 88, 0, 69, 0, 0, 0, 0,
+ 96, 3, 26, 0, 0, 0, 0,
+ 98, 4, 5, 0, 0, 0, 0,
+ 99, 2, 0, 0, 0, 0, 0,
+ 104, 0, 6, 0, 0, 0, 0,
+ 109, 0, 7, 0, 0, 0, 0,
+ 125, 128, 128, 0, 0, 0, 0,
+ 0, 119
diff --git a/drivers/staging/speakup/speakupmap.map b/drivers/staging/speakup/speakupmap.map
new file mode 100644
index 000000000..f10d44cf5
--- /dev/null
+++ b/drivers/staging/speakup/speakupmap.map
@@ -0,0 +1,93 @@
+spk key_f9 = punc_level_dec
+spk key_f10 = punc_level_inc
+spk key_f11 = reading_punc_dec
+spk key_f12 = reading_punc_inc
+spk key_1 = vol_dec
+spk key_2 = vol_inc
+spk key_3 = pitch_dec
+spk key_4 = pitch_inc
+spk key_5 = rate_dec
+spk key_6 = rate_inc
+key_kpasterisk = toggle_cursoring
+ctrl spk key_8 = toggle_cursoring
+spk key_kpasterisk = speakup_goto
+spk key_f1 = speakup_help
+spk key_f2 = set_win
+spk key_f3 = clear_win
+spk key_f4 = enable_win
+spk key_f5 = edit_some
+spk key_f6 = edit_most
+spk key_f7 = edit_delim
+spk key_f8 = edit_repeat
+shift spk key_f9 = edit_exnum
+ key_kp7 = say_prev_line
+spk key_kp7 = left_edge
+ key_kp8 = say_line
+double key_kp8 = say_line_indent
+spk key_kp8 = say_from_top
+ key_kp9 = say_next_line
+spk key_kp9 = top_edge
+ key_kpminus = speakup_parked
+spk key_kpminus = say_char_num
+ key_kp4 = say_prev_word
+spk key_kp4 = say_from_left
+ key_kp5 = say_word
+double key_kp5 = spell_word
+spk key_kp5 = spell_phonetic
+ key_kp6 = say_next_word
+spk key_kp6 = say_to_right
+ key_kpplus = say_screen
+spk key_kpplus = say_win
+ key_kp1 = say_prev_char
+spk key_kp1 = right_edge
+ key_kp2 = say_char
+spk key_kp2 = say_to_bottom
+double key_kp2 = say_phonetic_char
+ key_kp3 = say_next_char
+spk key_kp3 = bottom_edge
+ key_kp0 = spk_key
+ key_kpdot = say_position
+spk key_kpdot = say_attributes
+key_kpenter = speakup_quiet
+spk key_kpenter = speakup_off
+key_sysrq = speech_kill
+ key_kpslash = speakup_cut
+spk key_kpslash = speakup_paste
+spk key_pageup = say_first_char
+spk key_pagedown = say_last_char
+key_capslock = spk_key
+ spk key_z = spk_lock
+key_leftmeta = spk_key
+ctrl spk key_0 = speakup_goto
+spk key_u = say_prev_line
+spk key_i = say_line
+double spk key_i = say_line_indent
+spk key_o = say_next_line
+spk key_minus = speakup_parked
+shift spk key_minus = say_char_num
+spk key_j = say_prev_word
+spk key_k = say_word
+double spk key_k = spell_word
+spk key_l = say_next_word
+spk key_m = say_prev_char
+spk key_comma = say_char
+double spk key_comma = say_phonetic_char
+spk key_dot = say_next_char
+spk key_n = say_position
+ ctrl spk key_m = left_edge
+ ctrl spk key_y = top_edge
+ ctrl spk key_dot = right_edge
+ctrl spk key_p = bottom_edge
+spk key_apostrophe = say_screen
+spk key_h = say_from_left
+spk key_y = say_from_top
+spk key_semicolon = say_to_right
+spk key_p = say_to_bottom
+spk key_slash = say_attributes
+ spk key_enter = speakup_quiet
+ ctrl spk key_enter = speakup_off
+ spk key_9 = speakup_cut
+spk key_8 = speakup_paste
+shift spk key_m = say_first_char
+ ctrl spk key_semicolon = say_last_char
+spk key_r = read_all_doc
diff --git a/drivers/staging/speakup/spk_priv.h b/drivers/staging/speakup/spk_priv.h
new file mode 100644
index 000000000..1ef3795b8
--- /dev/null
+++ b/drivers/staging/speakup/spk_priv.h
@@ -0,0 +1,80 @@
+/* spk_priv.h
+ review functions for the speakup screen review package.
+ originally written by: Kirk Reiser and Andy Berdan.
+
+ extensively modified by David Borowski.
+
+ Copyright (C) 1998 Kirk Reiser.
+ Copyright (C) 2003 David Borowski.
+
+ This program is free software; you can redistribute it and/or 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 _SPEAKUP_PRIVATE_H
+#define _SPEAKUP_PRIVATE_H
+
+#include "spk_types.h"
+#include "spk_priv_keyinfo.h"
+
+#ifndef pr_warn
+#define pr_warn(fmt, arg...) printk(KERN_WARNING fmt, ##arg)
+#endif
+
+#define V_LAST_VAR { MAXVARS }
+#define SPACE 0x20
+#define SYNTH_CHECK 20030716 /* today's date ought to do for check value */
+/* synth flags, for odd synths */
+#define SF_DEC 1 /* to fiddle puncs in alpha strings so it doesn't spell */
+#ifdef MODULE
+#define SYNTH_START 1
+#else
+#define SYNTH_START 0
+#endif
+
+#define KT_SPKUP 15
+
+const struct old_serial_port *spk_serial_init(int index);
+void spk_stop_serial_interrupt(void);
+int spk_wait_for_xmitr(void);
+unsigned char spk_serial_in(void);
+unsigned char spk_serial_in_nowait(void);
+int spk_serial_out(const char ch);
+void spk_serial_release(void);
+
+char synth_buffer_getc(void);
+char synth_buffer_peek(void);
+int synth_buffer_empty(void);
+struct var_t *spk_get_var(enum var_id_t var_id);
+ssize_t spk_var_show(struct kobject *kobj, struct kobj_attribute *attr,
+ char *buf);
+ssize_t spk_var_store(struct kobject *kobj, struct kobj_attribute *attr,
+ const char *buf, size_t count);
+
+int spk_serial_synth_probe(struct spk_synth *synth);
+const char *spk_synth_immediate(struct spk_synth *synth, const char *buff);
+void spk_do_catch_up(struct spk_synth *synth);
+void spk_synth_flush(struct spk_synth *synth);
+int spk_synth_is_alive_nop(struct spk_synth *synth);
+int spk_synth_is_alive_restart(struct spk_synth *synth);
+void synth_printf(const char *buf, ...);
+int synth_request_region(u_long, u_long);
+int synth_release_region(u_long, u_long);
+int synth_add(struct spk_synth *in_synth);
+void synth_remove(struct spk_synth *in_synth);
+
+extern struct speakup_info_t speakup_info;
+
+extern struct var_t synth_time_vars[];
+
+#endif
diff --git a/drivers/staging/speakup/spk_priv_keyinfo.h b/drivers/staging/speakup/spk_priv_keyinfo.h
new file mode 100644
index 000000000..95c473a7e
--- /dev/null
+++ b/drivers/staging/speakup/spk_priv_keyinfo.h
@@ -0,0 +1,110 @@
+/* spk_priv.h
+ review functions for the speakup screen review package.
+ originally written by: Kirk Reiser and Andy Berdan.
+
+ extensively modified by David Borowski.
+
+ Copyright (C) 1998 Kirk Reiser.
+ Copyright (C) 2003 David Borowski.
+
+ This program is free software; you can redistribute it and/or 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 _SPEAKUP_KEYINFO_H
+#define _SPEAKUP_KEYINFO_H
+
+#define FIRST_SYNTH_VAR RATE
+/* 0 is reserved for no remap */
+#define SPEAKUP_GOTO 0x01
+#define SPEECH_KILL 0x02
+#define SPEAKUP_QUIET 0x03
+#define SPEAKUP_CUT 0x04
+#define SPEAKUP_PASTE 0x05
+#define SAY_FIRST_CHAR 0x06
+#define SAY_LAST_CHAR 0x07
+#define SAY_CHAR 0x08
+#define SAY_PREV_CHAR 0x09
+#define SAY_NEXT_CHAR 0x0a
+#define SAY_WORD 0x0b
+#define SAY_PREV_WORD 0x0c
+#define SAY_NEXT_WORD 0x0d
+#define SAY_LINE 0x0e
+#define SAY_PREV_LINE 0x0f
+#define SAY_NEXT_LINE 0x10
+#define TOP_EDGE 0x11
+#define BOTTOM_EDGE 0x12
+#define LEFT_EDGE 0x13
+#define RIGHT_EDGE 0x14
+#define SPELL_PHONETIC 0x15
+#define SPELL_WORD 0x16
+#define SAY_SCREEN 0x17
+#define SAY_POSITION 0x18
+#define SAY_ATTRIBUTES 0x19
+#define SPEAKUP_OFF 0x1a
+#define SPEAKUP_PARKED 0x1b
+#define SAY_LINE_INDENT 0x1c
+#define SAY_FROM_TOP 0x1d
+#define SAY_TO_BOTTOM 0x1e
+#define SAY_FROM_LEFT 0x1f
+#define SAY_TO_RIGHT 0x20
+#define SAY_CHAR_NUM 0x21
+#define EDIT_SOME 0x22
+#define EDIT_MOST 0x23
+#define SAY_PHONETIC_CHAR 0x24
+#define EDIT_DELIM 0x25
+#define EDIT_REPEAT 0x26
+#define EDIT_EXNUM 0x27
+#define SET_WIN 0x28
+#define CLEAR_WIN 0x29
+#define ENABLE_WIN 0x2a
+#define SAY_WIN 0x2b
+#define SPK_LOCK 0x2c
+#define SPEAKUP_HELP 0x2d
+#define TOGGLE_CURSORING 0x2e
+#define READ_ALL_DOC 0x2f
+#define SPKUP_MAX_FUNC 0x30 /* one greater than the last func handler */
+
+#define SPK_KEY 0x80
+#define FIRST_EDIT_BITS 0x22
+
+#define FIRST_SET_VAR SPELL_DELAY
+#define VAR_START 0x40 /* increase if adding more than 0x3f functions */
+
+/* keys for setting variables, must be ordered same as the enum for var_ids */
+/* with dec being even and inc being 1 greater */
+#define SPELL_DELAY_DEC (VAR_START+0)
+#define SPELL_DELAY_INC (SPELL_DELAY_DEC+1)
+#define PUNC_LEVEL_DEC (SPELL_DELAY_DEC+2)
+#define PUNC_LEVEL_INC (PUNC_LEVEL_DEC+1)
+#define READING_PUNC_DEC (PUNC_LEVEL_DEC+2)
+#define READING_PUNC_INC (READING_PUNC_DEC+1)
+#define ATTRIB_BLEEP_DEC (READING_PUNC_DEC+2)
+#define ATTRIB_BLEEP_INC (ATTRIB_BLEEP_DEC+1)
+#define BLEEPS_DEC (ATTRIB_BLEEP_DEC+2)
+#define BLEEPS_INC (BLEEPS_DEC+1)
+#define RATE_DEC (BLEEPS_DEC+2)
+#define RATE_INC (RATE_DEC+1)
+#define PITCH_DEC (RATE_DEC+2)
+#define PITCH_INC (PITCH_DEC+1)
+#define VOL_DEC (PITCH_DEC+2)
+#define VOL_INC (VOL_DEC+1)
+#define TONE_DEC (VOL_DEC+2)
+#define TONE_INC (TONE_DEC+1)
+#define PUNCT_DEC (TONE_DEC+2)
+#define PUNCT_INC (PUNCT_DEC+1)
+#define VOICE_DEC (PUNCT_DEC+2)
+#define VOICE_INC (VOICE_DEC+1)
+
+#endif
diff --git a/drivers/staging/speakup/spk_types.h b/drivers/staging/speakup/spk_types.h
new file mode 100644
index 000000000..e8ff5d7d6
--- /dev/null
+++ b/drivers/staging/speakup/spk_types.h
@@ -0,0 +1,205 @@
+#ifndef SPEAKUP_TYPES_H
+#define SPEAKUP_TYPES_H
+
+/*
+ * This file includes all of the typedefs and structs used in speakup.
+ */
+
+#include <linux/types.h>
+#include <linux/fs.h>
+#include <linux/errno.h>
+#include <linux/delay.h>
+#include <linux/wait.h> /* for wait_queue */
+#include <linux/init.h> /* for __init */
+#include <linux/module.h>
+#include <linux/vt_kern.h>
+#include <linux/spinlock.h>
+#include <linux/mutex.h>
+#include <linux/io.h> /* for inb_p, outb_p, inb, outb, etc... */
+#include <linux/device.h>
+
+enum var_type_t {
+ VAR_NUM = 0,
+ VAR_TIME,
+ VAR_STRING,
+ VAR_PROC
+};
+
+enum {
+ E_DEFAULT = 0,
+ E_SET,
+ E_INC,
+ E_DEC,
+ E_NEW_DEFAULT,
+};
+
+enum var_id_t {
+ VERSION = 0, SYNTH, SILENT, SYNTH_DIRECT,
+ KEYMAP, CHARS,
+ PUNC_SOME, PUNC_MOST, PUNC_ALL,
+ DELIM, REPEATS, EXNUMBER,
+ DELAY, TRIGGER, JIFFY, FULL, /* all timers must be together */
+ BLEEP_TIME, CURSOR_TIME, BELL_POS,
+ SAY_CONTROL, SAY_WORD_CTL, NO_INTERRUPT, KEY_ECHO,
+ SPELL_DELAY, PUNC_LEVEL, READING_PUNC,
+ ATTRIB_BLEEP, BLEEPS,
+ RATE, PITCH, VOL, TONE, PUNCT, VOICE, FREQUENCY, LANG, DIRECT,
+ CAPS_START, CAPS_STOP, CHARTAB,
+ MAXVARS
+};
+
+typedef int (*special_func)(struct vc_data *vc, u_char type, u_char ch,
+ u_short key);
+
+#define COLOR_BUFFER_SIZE 160
+
+struct spk_highlight_color_track {
+ /* Count of each background color */
+ unsigned int bgcount[8];
+ /* Buffer for characters drawn with each background color */
+ char highbuf[8][COLOR_BUFFER_SIZE];
+ /* Current index into highbuf */
+ unsigned int highsize[8];
+ /* Reading Position for each color */
+ u_long rpos[8], rx[8], ry[8];
+ /* Real Cursor Y Position */
+ ulong cy;
+};
+
+struct st_spk_t {
+ u_long reading_x, cursor_x;
+ u_long reading_y, cursor_y;
+ u_long reading_pos, cursor_pos;
+ u_long go_x, go_pos;
+ u_long w_top, w_bottom, w_left, w_right;
+ u_char w_start, w_enabled;
+ u_char reading_attr, old_attr;
+ char parked, shut_up;
+ struct spk_highlight_color_track ht;
+ int tty_stopped;
+};
+
+/* now some defines to make these easier to use. */
+#define spk_shut_up (speakup_console[vc->vc_num]->shut_up)
+#define spk_killed (speakup_console[vc->vc_num]->shut_up & 0x40)
+#define spk_x (speakup_console[vc->vc_num]->reading_x)
+#define spk_cx (speakup_console[vc->vc_num]->cursor_x)
+#define spk_y (speakup_console[vc->vc_num]->reading_y)
+#define spk_cy (speakup_console[vc->vc_num]->cursor_y)
+#define spk_pos (speakup_console[vc->vc_num]->reading_pos)
+#define spk_cp (speakup_console[vc->vc_num]->cursor_pos)
+#define goto_pos (speakup_console[vc->vc_num]->go_pos)
+#define goto_x (speakup_console[vc->vc_num]->go_x)
+#define win_top (speakup_console[vc->vc_num]->w_top)
+#define win_bottom (speakup_console[vc->vc_num]->w_bottom)
+#define win_left (speakup_console[vc->vc_num]->w_left)
+#define win_right (speakup_console[vc->vc_num]->w_right)
+#define win_start (speakup_console[vc->vc_num]->w_start)
+#define win_enabled (speakup_console[vc->vc_num]->w_enabled)
+#define spk_attr (speakup_console[vc->vc_num]->reading_attr)
+#define spk_old_attr (speakup_console[vc->vc_num]->old_attr)
+#define spk_parked (speakup_console[vc->vc_num]->parked)
+
+struct st_var_header {
+ char *name;
+ enum var_id_t var_id;
+ enum var_type_t var_type;
+ void *p_val; /* ptr to programs variable to store value */
+ void *data; /* ptr to the vars data */
+};
+
+struct num_var_t {
+ char *synth_fmt;
+ int default_val;
+ int low;
+ int high;
+ short offset, multiplier; /* for fiddling rates etc. */
+ char *out_str; /* if synth needs char representation of number */
+ int value; /* current value */
+};
+
+struct punc_var_t {
+ enum var_id_t var_id;
+ short value;
+};
+
+struct string_var_t {
+ char *default_val;
+};
+
+struct var_t {
+ enum var_id_t var_id;
+ union {
+ struct num_var_t n;
+ struct string_var_t s;
+ } u;
+};
+
+struct st_bits_data { /* punc, repeats, word delim bits */
+ char *name;
+ char *value;
+ short mask;
+};
+
+struct synth_indexing {
+ char *command;
+ unsigned char lowindex;
+ unsigned char highindex;
+ unsigned char currindex;
+};
+
+struct spk_synth {
+ const char *name;
+ const char *version;
+ const char *long_name;
+ const char *init;
+ char procspeech;
+ char clear;
+ int delay;
+ int trigger;
+ int jiffies;
+ int full;
+ int ser;
+ short flags;
+ short startup;
+ const int checkval; /* for validating a proper synth module */
+ struct var_t *vars;
+ int *default_pitch;
+ int *default_vol;
+ int (*probe)(struct spk_synth *synth);
+ void (*release)(void);
+ const char *(*synth_immediate)(struct spk_synth *synth,
+ const char *buff);
+ void (*catch_up)(struct spk_synth *synth);
+ void (*flush)(struct spk_synth *synth);
+ int (*is_alive)(struct spk_synth *synth);
+ int (*synth_adjust)(struct st_var_header *var);
+ void (*read_buff_add)(u_char);
+ unsigned char (*get_index)(void);
+ struct synth_indexing indexing;
+ int alive;
+ struct attribute_group attributes;
+};
+
+/**
+ * module_spk_synth() - Helper macro for registering a speakup driver
+ * @__spk_synth: spk_synth struct
+ * Helper macro for speakup drivers which do not do anything special in module
+ * init/exit. This eliminates a lot of boilerplate. Each module may only
+ * use this macro once, and calling it replaces module_init() and module_exit()
+ */
+#define module_spk_synth(__spk_synth) \
+ module_driver(__spk_synth, synth_add, synth_remove)
+
+struct speakup_info_t {
+ spinlock_t spinlock;
+ int port_tts;
+ int flushing;
+};
+
+struct bleep {
+ short freq;
+ unsigned long jiffies;
+ int active;
+};
+#endif
diff --git a/drivers/staging/speakup/spkguide.txt b/drivers/staging/speakup/spkguide.txt
new file mode 100644
index 000000000..b699de3c6
--- /dev/null
+++ b/drivers/staging/speakup/spkguide.txt
@@ -0,0 +1,1575 @@
+
+The Speakup User's Guide
+For Speakup 3.1.2 and Later
+By Gene Collins
+Updated by others
+Last modified on Mon Sep 27 14:26:31 2010
+Document version 1.3
+
+Copyright (c) 2005 Gene Collins
+Copyright (c) 2008 Samuel Thibault
+Copyright (c) 2009, 2010 the Speakup Team
+
+Permission is granted to copy, distribute and/or modify this document
+under the terms of the GNU Free Documentation License, Version 1.2 or
+any later version published by the Free Software Foundation; with no
+Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts. A
+copy of the license is included in the section entitled "GNU Free
+Documentation License".
+
+Preface
+
+The purpose of this document is to familiarize users with the user
+interface to Speakup, a Linux Screen Reader. If you need instructions
+for installing or obtaining Speakup, visit the web site at
+http://linux-speakup.org/. Speakup is a set of patches to the standard
+Linux kernel source tree. It can be built as a series of modules, or as
+a part of a monolithic kernel. These details are beyond the scope of
+this manual, but the user may need to be aware of the module
+capabilities, depending on how your system administrator has installed
+Speakup. If Speakup is built as a part of a monolithic kernel, and the
+user is using a hardware synthesizer, then Speakup will be able to
+provide speech access from the time the kernel is loaded, until the time
+the system is shutdown. This means that if you have obtained Linux
+installation media for a distribution which includes Speakup as a part
+of its kernel, you will be able, as a blind person, to install Linux
+with speech access unaided by a sighted person. Again, these details
+are beyond the scope of this manual, but the user should be aware of
+them. See the web site mentioned above for further details.
+
+1. Starting Speakup
+
+If your system administrator has installed Speakup to work with your
+specific synthesizer by default, then all you need to do to use Speakup
+is to boot your system, and Speakup should come up talking. This
+assumes of course that your synthesizer is a supported hardware
+synthesizer, and that it is either installed in or connected to your
+system, and is if necessary powered on.
+
+It is possible, however, that Speakup may have been compiled into the
+kernel with no default synthesizer. It is even possible that your
+kernel has been compiled with support for some of the supported
+synthesizers and not others. If you find that this is the case, and
+your synthesizer is supported but not available, complain to the person
+who compiled and installed your kernel. Or better yet, go to the web
+site, and learn how to patch Speakup into your own kernel source, and
+build and install your own kernel.
+
+If your kernel has been compiled with Speakup, and has no default
+synthesizer set, or you would like to use a different synthesizer than
+the default one, then you may issue the following command at the boot
+prompt of your boot loader.
+
+linux speakup.synth=ltlk
+
+This command would tell Speakup to look for and use a LiteTalk or
+DoubleTalk LT at boot up. You may replace the ltlk synthesizer keyword
+with the keyword for whatever synthesizer you wish to use. The
+speakup.synth parameter will accept the following keywords, provided
+that support for the related synthesizers has been built into the
+kernel.
+
+acntsa -- Accent SA
+acntpc -- Accent PC
+apollo -- Apollo
+audptr -- Audapter
+bns -- Braille 'n Speak
+dectlk -- DecTalk Express (old and new, db9 serial only)
+decext -- DecTalk (old) External
+dtlk -- DoubleTalk PC
+keypc -- Keynote Gold PC
+ltlk -- DoubleTalk LT, LiteTalk, or external Tripletalk (db9 serial only)
+spkout -- Speak Out
+txprt -- Transport
+dummy -- Plain text terminal
+
+Note: Speakup does * NOT * support usb connections! Speakup also does *
+NOT * support the internal Tripletalk!
+
+Speakup does support two other synthesizers, but because they work in
+conjunction with other software, they must be loaded as modules after
+their related software is loaded, and so are not available at boot up.
+These are as follows:
+
+decpc -- DecTalk PC (not available at boot up)
+soft -- One of several software synthesizers (not available at boot up)
+
+See the sections on loading modules and software synthesizers later in
+this manual for further details. It should be noted here that the
+speakup.synth boot parameter will have no effect if Speakup has been
+compiled as modules. In order for Speakup modules to be loaded during
+the boot process, such action must be configured by your system
+administrator. This will mean that you will hear some, but not all, of
+the bootup messages.
+
+2. Basic operation
+
+Once you have booted the system, and if necessary, have supplied the
+proper bootup parameter for your synthesizer, Speakup will begin
+talking as soon as the kernel is loaded. In fact, it will talk a lot!
+It will speak all the boot up messages that the kernel prints on the
+screen during the boot process. This is because Speakup is not a
+separate screen reader, but is actually built into the operating
+system. Since almost all console applications must print text on the
+screen using the kernel, and must get their keyboard input through the
+kernel, they are automatically handled properly by Speakup. There are a
+few exceptions, but we'll come to those later.
+
+Note: In this guide I will refer to the numeric keypad as the keypad.
+This is done because the speakupmap.map file referred to later in this
+manual uses the term keypad instead of numeric keypad. Also I'm lazy
+and would rather only type one word. So keypad it is. Got it? Good.
+
+Most of the Speakup review keys are located on the keypad at the far
+right of the keyboard. The numlock key should be off, in order for these
+to work. If you toggle the numlock on, the keypad will produce numbers,
+which is exactly what you want for spreadsheets and such. For the
+purposes of this guide, you should have the numlock turned off, which is
+its default state at bootup.
+
+You probably won't want to listen to all the bootup messages every time
+you start your system, though it's a good idea to listen to them at
+least once, just so you'll know what kind of information is available to
+you during the boot process. You can always review these messages after
+bootup with the command:
+
+dmesg | more
+
+In order to speed the boot process, and to silence the speaking of the
+bootup messages, just press the keypad enter key. This key is located
+in the bottom right corner of the keypad. Speakup will shut up and stay
+that way, until you press another key.
+
+You can check to see if the boot process has completed by pressing the 8
+key on the keypad, which reads the current line. This also has the
+effect of starting Speakup talking again, so you can press keypad enter
+to silence it again if the boot process has not completed.
+
+When the boot process is complete, you will arrive at a "login" prompt.
+At this point, you'll need to type in your user id and password, as
+provided by your system administrator. You will hear Speakup speak the
+letters of your user id as you type it, but not the password. This is
+because the password is not displayed on the screen for security
+reasons. This has nothing to do with Speakup, it's a Linux security
+feature.
+
+Once you've logged in, you can run any Linux command or program which is
+allowed by your user id. Normal users will not be able to run programs
+which require root privileges.
+
+When you are running a program or command, Speakup will automatically
+speak new text as it arrives on the screen. You can at any time silence
+the speech with keypad enter, or use any of the Speakup review keys.
+
+Here are some basic Speakup review keys, and a short description of what
+they do.
+
+keypad 1 -- read previous character
+keypad 2 -- read current character (pressing keypad 2 twice rapidly will speak
+ the current character phonetically)
+keypad 3 -- read next character
+keypad 4 -- read previous word
+keypad 5 -- read current word (press twice rapidly to spell the current word)
+keypad 6 -- read next word
+keypad 7 -- read previous line
+keypad 8 -- read current line (press twice rapidly to hear how much the
+ text on the current line is indented)
+keypad 9 -- read next line
+keypad period -- speak current cursor position and announce current
+ virtual console
+
+It's also worth noting that the insert key on the keypad is mapped
+as the speakup key. Instead of pressing and releasing this key, as you
+do under DOS or Windows, you hold it like a shift key, and press other
+keys in combination with it. For example, repeatedly holding keypad
+insert, from now on called speakup, and keypad enter will toggle the
+speaking of new text on the screen on and off. This is not the same as
+just pressing keypad enter by itself, which just silences the speech
+until you hit another key. When you hit speakup plus keypad enter,
+Speakup will say, "You turned me off.", or "Hey, that's better." When
+Speakup is turned off, no new text on the screen will be spoken. You
+can still use the reading controls to review the screen however.
+
+3. Using the Speakup Help System
+
+In order to enter the Speakup help system, press and hold the speakup
+key (remember that this is the keypad insert key), and press the f1 key.
+You will hear the message:
+
+"Press space to leave help, cursor up or down to scroll, or a letter to
+go to commands in list."
+
+When you press the spacebar to leave the help system, you will hear:
+
+"Leaving help."
+
+While you are in the Speakup help system, you can scroll up or down
+through the list of available commands using the cursor keys. The list
+of commands is arranged in alphabetical order. If you wish to jump to
+commands in a specific part of the alphabet, you may press the letter of
+the alphabet you wish to jump to.
+
+You can also just explore by typing keyboard keys. Pressing keys will
+cause Speakup to speak the command associated with that key. For
+example, if you press the keypad 8 key, you will hear:
+
+"Keypad 8 is line, say current."
+
+You'll notice that some commands do not have keys assigned to them.
+This is because they are very infrequently used commands, and are also
+accessible through the sys system. We'll discuss the sys system later
+in this manual.
+
+You'll also notice that some commands have two keys assigned to them.
+This is because Speakup has a built in set of alternative key bindings
+for laptop users. The alternate speakup key is the caps lock key. You
+can press and hold the caps lock key, while pressing an alternate
+speakup command key to activate the command. On most laptops, the
+numeric keypad is defined as the keys in the j k l area of the keyboard.
+
+There is usually a function key which turns this keypad function on and
+off, and some other key which controls the numlock state. Toggling the
+keypad functionality on and off can become a royal pain. So, Speakup
+gives you a simple way to get at an alternative set of key mappings for
+your laptop. These are also available by default on desktop systems,
+because Speakup does not know whether it is running on a desktop or
+laptop. So you may choose which set of Speakup keys to use. Some
+system administrators may have chosen to compile Speakup for a desktop
+system without this set of alternate key bindings, but these details are
+beyond the scope of this manual. To use the caps lock for its normal
+purpose, hold the shift key while toggling the caps lock on and off. We
+should note here, that holding the caps lock key and pressing the z key
+will toggle the alternate j k l keypad on and off.
+
+4. Keys and Their Assigned Commands
+
+In this section, we'll go through a list of all the speakup keys and
+commands. You can also get a list of commands and assigned keys from
+the help system.
+
+The following list was taken from the speakupmap.map file. Key
+assignments are on the left of the equal sign, and the associated
+Speakup commands are on the right. The designation "spk" means to press
+and hold the speakup key, a.k.a. keypad insert, a.k.a. caps lock, while
+pressing the other specified key.
+
+spk key_f9 = punc_level_dec
+spk key_f10 = punc_level_inc
+spk key_f11 = reading_punc_dec
+spk key_f12 = reading_punc_inc
+spk key_1 = vol_dec
+spk key_2 = vol_inc
+spk key_3 = pitch_dec
+spk key_4 = pitch_inc
+spk key_5 = rate_dec
+spk key_6 = rate_inc
+key_kpasterisk = toggle_cursoring
+spk key_kpasterisk = speakup_goto
+spk key_f1 = speakup_help
+spk key_f2 = set_win
+spk key_f3 = clear_win
+spk key_f4 = enable_win
+spk key_f5 = edit_some
+spk key_f6 = edit_most
+spk key_f7 = edit_delim
+spk key_f8 = edit_repeat
+shift spk key_f9 = edit_exnum
+ key_kp7 = say_prev_line
+spk key_kp7 = left_edge
+ key_kp8 = say_line
+double key_kp8 = say_line_indent
+spk key_kp8 = say_from_top
+ key_kp9 = say_next_line
+spk key_kp9 = top_edge
+ key_kpminus = speakup_parked
+spk key_kpminus = say_char_num
+ key_kp4 = say_prev_word
+spk key_kp4 = say_from_left
+ key_kp5 = say_word
+double key_kp5 = spell_word
+spk key_kp5 = spell_phonetic
+ key_kp6 = say_next_word
+spk key_kp6 = say_to_right
+ key_kpplus = say_screen
+spk key_kpplus = say_win
+ key_kp1 = say_prev_char
+spk key_kp1 = right_edge
+ key_kp2 = say_char
+spk key_kp2 = say_to_bottom
+double key_kp2 = say_phonetic_char
+ key_kp3 = say_next_char
+spk key_kp3 = bottom_edge
+ key_kp0 = spk_key
+ key_kpdot = say_position
+spk key_kpdot = say_attributes
+key_kpenter = speakup_quiet
+spk key_kpenter = speakup_off
+key_sysrq = speech_kill
+ key_kpslash = speakup_cut
+spk key_kpslash = speakup_paste
+spk key_pageup = say_first_char
+spk key_pagedown = say_last_char
+key_capslock = spk_key
+ spk key_z = spk_lock
+key_leftmeta = spk_key
+ctrl spk key_0 = speakup_goto
+spk key_u = say_prev_line
+spk key_i = say_line
+double spk key_i = say_line_indent
+spk key_o = say_next_line
+spk key_minus = speakup_parked
+shift spk key_minus = say_char_num
+spk key_j = say_prev_word
+spk key_k = say_word
+double spk key_k = spell_word
+spk key_l = say_next_word
+spk key_m = say_prev_char
+spk key_comma = say_char
+double spk key_comma = say_phonetic_char
+spk key_dot = say_next_char
+spk key_n = say_position
+ ctrl spk key_m = left_edge
+ ctrl spk key_y = top_edge
+ ctrl spk key_dot = right_edge
+ctrl spk key_p = bottom_edge
+spk key_apostrophe = say_screen
+spk key_h = say_from_left
+spk key_y = say_from_top
+spk key_semicolon = say_to_right
+spk key_p = say_to_bottom
+spk key_slash = say_attributes
+ spk key_enter = speakup_quiet
+ ctrl spk key_enter = speakup_off
+ spk key_9 = speakup_cut
+spk key_8 = speakup_paste
+shift spk key_m = say_first_char
+ ctrl spk key_semicolon = say_last_char
+
+5. The Speakup Sys System
+
+The Speakup screen reader also creates a speakup subdirectory as a part
+of the sys system.
+
+As a convenience, run as root
+
+ln -s /sys/accessibility/speakup /speakup
+
+to directly access speakup parameters from /speakup.
+You can see these entries by typing the command:
+
+ls -1 /speakup/*
+
+If you issue the above ls command, you will get back something like
+this:
+
+/speakup/attrib_bleep
+/speakup/bell_pos
+/speakup/bleep_time
+/speakup/bleeps
+/speakup/cursor_time
+/speakup/delimiters
+/speakup/ex_num
+/speakup/key_echo
+/speakup/keymap
+/speakup/no_interrupt
+/speakup/punc_all
+/speakup/punc_level
+/speakup/punc_most
+/speakup/punc_some
+/speakup/reading_punc
+/speakup/repeats
+/speakup/say_control
+/speakup/say_word_ctl
+/speakup/silent
+/speakup/spell_delay
+/speakup/synth
+/speakup/synth_direct
+/speakup/version
+
+/speakup/i18n:
+announcements
+characters
+chartab
+colors
+ctl_keys
+formatted
+function_names
+key_names
+states
+
+/speakup/soft:
+caps_start
+caps_stop
+delay_time
+direct
+freq
+full_time
+jiffy_delta
+pitch
+punct
+rate
+tone
+trigger_time
+voice
+vol
+
+Notice the two subdirectories of /speakup: /speakup/i18n and
+/speakup/soft.
+The i18n subdirectory is described in a later section.
+The files under /speakup/soft represent settings that are specific to the
+driver for the software synthesizer. If you use the LiteTalk, your
+synthesizer-specific settings would be found in /speakup/ltlk. In other words,
+a subdirectory named /speakup/KWD is created to hold parameters specific
+to the device whose keyword is KWD.
+These parameters include volume, rate, pitch, and others.
+
+In addition to using the Speakup hot keys to change such things as
+volume, pitch, and rate, you can also echo values to the appropriate
+entry in the /speakup directory. This is very useful, since it
+lets you control Speakup parameters from within a script. How you
+would write such scripts is somewhat beyond the scope of this manual,
+but I will include a couple of simple examples here to give you a
+general idea of what such scripts can do.
+
+Suppose for example, that you wanted to control both the punctuation
+level and the reading punctuation level at the same time. For
+simplicity, we'll call them punc0, punc1, punc2, and punc3. The scripts
+might look something like this:
+
+#!/bin/bash
+# punc0
+# set punc and reading punc levels to 0
+echo 0 >/speakup/punc_level
+echo 0 >/speakup/reading_punc
+echo Punctuation level set to 0.
+
+#!/bin/bash
+# punc1
+# set punc and reading punc levels to 1
+echo 1 >/speakup/punc_level
+echo 1 >/speakup/reading_punc
+echo Punctuation level set to 1.
+
+#!/bin/bash
+# punc2
+# set punc and reading punc levels to 2
+echo 2 >/speakup/punc_level
+echo 2 >/speakup/reading_punc
+echo Punctuation level set to 2.
+
+#!/bin/bash
+# punc3
+# set punc and reading punc levels to 3
+echo 3 >/speakup/punc_level
+echo 3 >/speakup/reading_punc
+echo Punctuation level set to 3.
+
+If you were to store these four small scripts in a directory in your
+path, perhaps /usr/local/bin, and set the permissions to 755 with the
+chmod command, then you could change the default reading punc and
+punctuation levels at the same time by issuing just one command. For
+example, if you were to execute the punc3 command at your shell prompt,
+then the reading punc and punc level would both get set to 3.
+
+I should note that the above scripts were written to work with bash, but
+regardless of which shell you use, you should be able to do something
+similar.
+
+The Speakup sys system also has another interesting use. You can echo
+Speakup parameters into the sys system in a script during system
+startup, and speakup will return to your preferred parameters every time
+the system is rebooted.
+
+Most of the Speakup sys parameters can be manipulated by a regular user
+on the system. However, there are a few parameters that are dangerous
+enough that they should only be manipulated by the root user on your
+system. There are even some parameters that are read only, and cannot
+be written to at all. For example, the version entry in the Speakup
+sys system is read only. This is because there is no reason for a user
+to tamper with the version number which is reported by Speakup. Doing
+an ls -l on /speakup/version will return this:
+
+-r--r--r-- 1 root root 0 Mar 21 13:46 /speakup/version
+
+As you can see, the version entry in the Speakup sys system is read
+only, is owned by root, and belongs to the root group. Doing a cat of
+/speakup/version will display the Speakup version number, like
+this:
+
+cat /speakup/version
+Speakup v-2.00 CVS: Thu Oct 21 10:38:21 EDT 2004
+synth dtlk version 1.1
+
+The display shows the Speakup version number, along with the version
+number of the driver for the current synthesizer.
+
+Looking at entries in the Speakup sys system can be useful in many
+ways. For example, you might wish to know what level your volume is set
+at. You could type:
+
+cat /speakup/KWD/vol
+# Replace KWD with the keyword for your synthesizer, E.G., ltlk for LiteTalk.
+5
+
+The number five which comes back is the level at which the synthesizer
+volume is set at.
+
+All the entries in the Speakup sys system are readable, some are
+writable by root only, and some are writable by everyone. Unless you
+know what you are doing, you should probably leave the ones that are
+writable by root only alone. Most of the names are self explanatory.
+Vol for controlling volume, pitch for pitch, rate for controlling speaking
+rate, etc. If you find one you aren't sure about, you can post a query
+on the Speakup list.
+
+6. Changing Synthesizers
+
+It is possible to change to a different synthesizer while speakup is
+running. In other words, it is not necessary to reboot the system
+in order to use a different synthesizer. You can simply echo the
+synthesizer keyword to the /speakup/synth sys entry.
+Depending on your situation, you may wish to echo none to the synth
+sys entry, to disable speech while one synthesizer is disconnected and
+a second one is connected in its place. Then echo the keyword for the
+new synthesizer into the synth sys entry in order to start speech
+with the newly connected synthesizer. See the list of synthesizer
+keywords in section 1 to find the keyword which matches your synth.
+
+7. Loading modules
+
+As mentioned earlier, Speakup can either be completely compiled into the
+kernel, with the exception of the help module, or it can be compiled as
+a series of modules. When compiled as modules, Speakup will only be
+able to speak some of the bootup messages if your system administrator
+has configured the system to load the modules at boo time. The modules
+can be loaded after the file systems have been checked and mounted, or
+from an initrd. There is a third possibility. Speakup can be compiled
+with some components built into the kernel, and others as modules. As
+we'll see in the next section, this is particularly useful when you are
+working with software synthesizers.
+
+If Speakup is completely compiled as modules, then you must use the
+modprobe command to load Speakup. You do this by loading the module for
+the synthesizer driver you wish to use. The driver modules are all
+named speakup_<keyword>, where <keyword> is the keyword for the
+synthesizer you want. So, in order to load the driver for the DecTalk
+Express, you would type the following command:
+
+modprobe speakup_dectlk
+
+Issuing this command would load the DecTalk Express driver and all other
+related Speakup modules necessary to get Speakup up and running.
+
+To completely unload Speakup, again presuming that it is entirely built
+as modules, you would give the command:
+
+modprobe -r speakup_dectlk
+
+The above command assumes you were running a DecTalk Express. If you
+were using a different synth, then you would substitute its keyword in
+place of dectlk.
+
+If you have multiple drivers loaded, you need to unload all of them, in
+order to completely unload Speakup.
+For example, if you have loaded both the dectlk and ltlk drivers, use the
+command:
+modprobe -r speakup_dectlk speakup_ltlk
+
+You cannot unload the driver for software synthesizers when a user-space
+daemon is using /dev/softsynth. First, kill the daemon. Next, remove
+the driver with the command:
+modprobe -r speakup_soft
+
+Now, suppose we have a situation where the main Speakup component
+is built into the kernel, and some or all of the drivers are built as
+modules. Since the main part of Speakup is compiled into the kernel, a
+partial Speakup sys system has been created which we can take advantage
+of by simply echoing the synthesizer keyword into the
+/speakup/synth sys entry. This will cause the kernel to
+automatically load the appropriate driver module, and start Speakup
+talking. To switch to another synth, just echo a new keyword to the
+synth sys entry. For example, to load the DoubleTalk LT driver,
+you would type:
+
+echo ltlk >/speakup/synth
+
+You can use the modprobe -r command to unload driver modules, regardless
+of whether the main part of Speakup has been built into the kernel or
+not.
+
+8. Using Software Synthesizers
+
+Using a software synthesizer requires that some other software be
+installed and running on your system. For this reason, software
+synthesizers are not available for use at bootup, or during a system
+installation process.
+There are two freely-available solutions for software speech: Espeakup and
+Speech Dispatcher.
+These are described in subsections 8.1 and 8.2, respectively.
+
+During the rest of this section, we assume that speakup_soft is either
+built in to your kernel, or loaded as a module.
+
+If your system does not have udev installed , before you can use a
+software synthesizer, you must have created the /dev/softsynth device.
+If you have not already done so, issue the following commands as root:
+
+cd /dev
+mknod softsynth c 10 26
+
+While we are at it, we might just as well create the /dev/synth device,
+which can be used to let user space programs send information to your
+synthesizer. To create /dev/synth, change to the /dev directory, and
+issue the following command as root:
+
+mknod synth c 10 25
+
+of both.
+
+8.1. Espeakup
+
+Espeakup is a connector between Speakup and the eSpeak software synthesizer.
+Espeakup may already be available as a package for your distribution
+of Linux. If it is not packaged, you need to install it manually.
+You can find it in the contrib/ subdirectory of the Speakup sources.
+The filename is espeakup-$VERSION.tar.bz2, where $VERSION
+depends on the current release of Espeakup. The Speakup 3.1.2 source
+ships with version 0.71 of Espeakup.
+The README file included with the Espeakup sources describes the process
+of manual installation.
+
+Assuming that Espeakup is installed, either by the user or by the distributor,
+follow these steps to use it.
+
+Tell Speakup to use the "soft driver:
+echo soft > /speakup/synth
+
+Finally, start the espeakup program. There are two ways to do it.
+Both require root privileges.
+
+If Espeakup was installed as a package for your Linux distribution,
+you probably have a distribution-specific script that controls the operation
+of the daemon. Look for a file named espeakup under /etc/init.d or
+/etc/rc.d. Execute the following command with root privileges:
+/etc/init.d/espeakup start
+Replace init.d with rc.d, if your distribution uses scripts located under
+/etc/rc.d.
+Your distribution will also have a procedure for starting daemons at
+boot-time, so it is possible to have software speech as soon as user-space
+daemons are started by the bootup scripts.
+These procedures are not described in this document.
+
+If you built Espeakup manually, the "make install" step placed the binary
+under /usr/bin.
+Run the following command as root:
+/usr/bin/espeakup
+Espeakup should start speaking.
+
+8.2. Speech Dispatcher
+
+For this option, you must have a package called
+Speech Dispatcher running on your system, and it must be configured to
+work with one of its supported software synthesizers.
+
+Two open source synthesizers you might use are Flite and Festival. You
+might also choose to purchase the Software DecTalk from Fonix Sales Inc.
+If you run a google search for Fonix, you'll find their web site.
+
+You can obtain a copy of Speech Dispatcher from free(b)soft at
+http://www.freebsoft.org/. Follow the installation instructions that
+come with Speech Dispatcher in order to install and configure Speech
+Dispatcher. You can check out the web site for your Linux distribution
+in order to get a copy of either Flite or Festival. Your Linux
+distribution may also have a precompiled Speech Dispatcher package.
+
+Once you've installed, configured, and tested Speech Dispatcher with your
+chosen software synthesizer, you still need one more piece of software
+in order to make things work. You need a package called speechd-up.
+You get it from the free(b)soft web site mentioned above. After you've
+compiled and installed speechd-up, you are almost ready to begin using
+your software synthesizer.
+
+Now you can begin using your software synthesizer. In order to do so,
+echo the soft keyword to the synth sys entry like this:
+
+echo soft >/speakup/synth
+
+Next run the speechd_up command like this:
+
+speechd_up &
+
+Your synth should now start talking, and you should be able to adjust
+the pitch, rate, etc.
+
+9. Using The DecTalk PC Card
+
+The DecTalk PC card is an ISA card that is inserted into one of the ISA
+slots in your computer. It requires that the DecTalk PC software be
+installed on your computer, and that the software be loaded onto the
+Dectalk PC card before it can be used.
+
+You can get the dec_pc.tgz file from the linux-speakup.org site. The
+dec_pc.tgz file is in the ~ftp/pub/linux/speakup directory.
+
+After you have downloaded the dec_pc.tgz file, untar it in your home
+directory, and read the Readme file in the newly created dec_pc
+directory.
+
+The easiest way to get the software working is to copy the entire dec_pc
+directory into /user/local/lib. To do this, su to root in your home
+directory, and issue the command:
+
+cp dec_pc /usr/local/lib
+
+You will need to copy the dtload command from the dec_pc directory to a
+directory in your path. Either /usr/bin or /usr/local/bin is a good
+choice.
+
+You can now run the dtload command in order to load the DecTalk PC
+software onto the card. After you have done this, echo the decpc
+keyword to the synth entry in the sys system like this:
+
+echo decpc >/speakup/synth
+
+Your DecTalk PC should start talking, and then you can adjust the pitch,
+rate, volume, voice, etc. The voice entry in the Speakup sys system
+will accept a number from 0 through 7 for the DecTalk PC synthesizer,
+which will give you access to some of the DecTalk voices.
+
+10. Using Cursor Tracking
+
+In Speakup version 2.0 and later, cursor tracking is turned on by
+default. This means that when you are using an editor, Speakup will
+automatically speak characters as you move left and right with the
+cursor keys, and lines as you move up and down with the cursor keys.
+This is the traditional sort of cursor tracking.
+Recent versions of Speakup provide two additional ways to control the
+text that is spoken when the cursor is moved:
+"highlight tracking" and "read window."
+They are described later in this section.
+Sometimes, these modes get in your way, so you can disable cursor tracking
+altogether.
+
+You may select among the various forms of cursor tracking using the keypad
+asterisk key.
+Each time you press this key, a new mode is selected, and Speakup speaks
+the name of the new mode. The names for the four possible states of cursor
+tracking are: "cursoring on", "highlight tracking", "read window",
+and "cursoring off." The keypad asterisk key moves through the list of
+modes in a circular fashion.
+
+If highlight tracking is enabled, Speakup tracks highlighted text,
+rather than the cursor itself. When you move the cursor with the arrow keys,
+Speakup speaks the currently highlighted information.
+This is useful when moving through various menus and dialog boxes.
+If cursor tracking isn't helping you while navigating a menu,
+try highlight tracking.
+
+With the "read window" variety of cursor tracking, you can limit the text
+that Speakup speaks by specifying a window of interest on the screen.
+See section 15 for a description of the process of defining windows.
+When you move the cursor via the arrow keys, Speakup only speaks
+the contents of the window. This is especially helpful when you are hearing
+superfluous speech. Consider the following example.
+
+Suppose that you are at a shell prompt. You use bash, and you want to
+explore your command history using the up and down arrow keys. If you
+have enabled cursor tracking, you will hear two pieces of information.
+Speakup speaks both your shell prompt and the current entry from the
+command history. You may not want to hear the prompt repeated
+each time you move, so you can silence it by specifying a window. Find
+the last line of text on the screen. Clear the current window by pressing
+the key combination speakup f3. Use the review cursor to find the first
+character that follows your shell prompt. Press speakup + f2 twice, to
+define a one-line window. The boundaries of the window are the
+character following the shell prompt and the end of the line. Now, cycle
+through the cursor tracking modes using keypad asterisk, until Speakup
+says "read window." Move through your history using your arrow keys.
+You will notice that Speakup no longer speaks the redundant prompt.
+
+Some folks like to turn cursor tracking off while they are using the
+lynx web browser. You definitely want to turn cursor tracking off when
+you are using the alsamixer application. Otherwise, you won't be able
+to hear your mixer settings while you are using the arrow keys.
+
+11. Cut and Paste
+
+One of Speakup's more useful features is the ability to cut and paste
+text on the screen. This means that you can capture information from a
+program, and paste that captured text into a different place in the
+program, or into an entirely different program, which may even be
+running on a different console.
+
+For example, in this manual, we have made references to several web
+sites. It would be nice if you could cut and paste these urls into your
+web browser. Speakup does this quite nicely. Suppose you wanted to
+past the following url into your browser:
+
+http://linux-speakup.org/
+
+Use the speakup review keys to position the reading cursor on the first
+character of the above url. When the reading cursor is in position,
+press the keypad slash key once. Speakup will say, "mark". Next,
+position the reading cursor on the rightmost character of the above
+url. Press the keypad slash key once again to actually cut the text
+from the screen. Speakup will say, "cut". Although we call this
+cutting, Speakup does not actually delete the cut text from the screen.
+It makes a copy of the text in a special buffer for later pasting.
+
+Now that you have the url cut from the screen, you can paste it into
+your browser, or even paste the url on a command line as an argument to
+your browser.
+
+Suppose you want to start lynx and go to the Speakup site.
+
+You can switch to a different console with the alt left and right
+arrows, or you can switch to a specific console by typing alt and a
+function key. These are not Speakup commands, just standard Linux
+console capabilities.
+
+Once you've changed to an appropriate console, and are at a shell prompt,
+type the word lynx, followed by a space. Now press and hold the speakup
+key, while you type the keypad slash character. The url will be pasted
+onto the command line, just as though you had typed it in. Press the
+enter key to execute the command.
+
+The paste buffer will continue to hold the cut information, until a new
+mark and cut operation is carried out. This means you can paste the cut
+information as many times as you like before doing another cut
+operation.
+
+You are not limited to cutting and pasting only one line on the screen.
+You can also cut and paste rectangular regions of the screen. Just
+position the reading cursor at the top left corner of the text to be
+cut, mark it with the keypad slash key, then position the reading cursor
+at the bottom right corner of the region to be cut, and cut it with the
+keypad slash key.
+
+12. Changing the Pronunciation of Characters
+
+Through the /speakup/i18n/characters sys entry, Speakup gives you the
+ability to change how Speakup pronounces a given character. You could,
+for example, change how some punctuation characters are spoken. You can
+even change how Speakup will pronounce certain letters.
+
+You may, for example, wish to change how Speakup pronounces the z
+character. The author of Speakup, Kirk Reiser, is Canadian, and thus
+believes that the z should be pronounced zed. If you are an American,
+you might wish to use the zee pronunciation instead of zed. You can
+change the pronunciation of both the upper and lower case z with the
+following two commands:
+
+echo 90 zee >/speakup/characters
+echo 122 zee >/speakup/characters
+
+Let's examine the parts of the two previous commands. They are issued
+at the shell prompt, and could be placed in a startup script.
+
+The word echo tells the shell that you want to have it display the
+string of characters that follow the word echo. If you were to just
+type:
+
+echo hello.
+
+You would get the word hello printed on your screen as soon as you
+pressed the enter key. In this case, we are echoing strings that we
+want to be redirected into the sys system.
+
+The numbers 90 and 122 in the above echo commands are the ascii numeric
+values for the upper and lower case z, the characters we wish to change.
+
+The string zee is the pronunciation that we want Speakup to use for the
+upper and lower case z.
+
+The > symbol redirects the output of the echo command to a file, just
+like in DOS, or at the Windows command prompt.
+
+And finally, /speakup/i18n/characters is the file entry in the sys system
+where we want the output to be directed. Speakup looks at the numeric
+value of the character we want to change, and inserts the pronunciation
+string into an internal table.
+
+You can look at the whole table with the following command:
+
+cat /speakup/i18n/characters
+
+Speakup will then print out the entire character pronunciation table. I
+won't display it here, but leave you to look at it at your convenience.
+
+13. Mapping Keys
+
+Speakup has the capability of allowing you to assign or "map" keys to
+internal Speakup commands. This section necessarily assumes you have a
+Linux kernel source tree installed, and that it has been patched and
+configured with Speakup. How you do this is beyond the scope of this
+manual. For this information, visit the Speakup web site at
+http://linux-speakup.org/. The reason you'll need the kernel source
+tree patched with Speakup is that the genmap utility you'll need for
+processing keymaps is in the
+/usr/src/linux-<version_number>/drivers/char/speakup directory. The
+<version_number> in the above directory path is the version number of
+the Linux source tree you are working with.
+
+So ok, you've gone off and gotten your kernel source tree, and patched
+and configured it. Now you can start manipulating keymaps.
+
+You can either use the
+/usr/src/linux-<version_number>/drivers/char/speakup/speakupmap.map file
+included with the Speakup source, or you can cut and paste the copy in
+section 4 into a separate file. If you use the one in the Speakup
+source tree, make sure you make a backup of it before you start making
+changes. You have been warned!
+
+Suppose that you want to swap the key assignments for the Speakup
+say_last_char and the Speakup say_first_char commands. The
+speakupmap.map lists the key mappings for these two commands as follows:
+
+spk key_pageup = say_first_char
+spk key_pagedown = say_last_char
+
+You can edit your copy of the speakupmap.map file and swap the command
+names on the right side of the = (equals) sign. You did make a backup,
+right? The new keymap lines would look like this:
+
+spk key_pageup = say_last_char
+spk key_pagedown = say_first_char
+
+After you edit your copy of the speakupmap.map file, save it under a new
+file name, perhaps newmap.map. Then exit your editor and return to the
+shell prompt.
+
+You are now ready to load your keymap with your swapped key assignments.
+ Assuming that you saved your new keymap as the file newmap.map, you
+would load your keymap into the sys system like this:
+
+/usr/src/linux-<version_number>/drivers/char/speakup/genmap newmap.map
+>/speakup/keymap
+
+Remember to substitute your kernel version number for the
+<version_number> in the above command. Also note that although the
+above command wrapped onto two lines in this document, you should type
+it all on one line.
+
+Your say first and say last characters should now be swapped. Pressing
+speakup pagedown should read you the first non-whitespace character on
+the line your reading cursor is in, and pressing speakup pageup should
+read you the last character on the line your reading cursor is in.
+
+You should note that these new mappings will only stay in effect until
+you reboot, or until you load another keymap.
+
+One final warning. If you try to load a partial map, you will quickly
+find that all the mappings you didn't include in your file got deleted
+from the working map. Be extremely careful, and always make a backup!
+You have been warned!
+
+14. Internationalizing Speakup
+
+Speakup indicates various conditions to the user by speaking messages.
+For instance, when you move to the left edge of the screen with the
+review keys, Speakup says, "left."
+Prior to version 3.1.0 of Speakup, all of these messages were in English,
+and they could not be changed. If you used a non-English synthesizer,
+you still heard English messages, such as "left" and "cursoring on."
+In version 3.1.0 or higher, one may load translations for the various
+messages via the /sys filesystem.
+
+The directory /speakup/i18n contains several collections of messages.
+Each group of messages is stored in its own file.
+The following section lists all of these files, along with a brief description
+of each.
+
+14.1. Files Under the i18n Subdirectory
+
+* announcements:
+This file contains various general announcements, most of which cannot
+be categorized. You will find messages such as "You killed Speakup",
+"I'm alive", "leaving help", "parked", "unparked", and others.
+You will also find the names of the screen edges and cursor tracking modes
+here.
+
+* characters:
+See section 12 for a description of this file.
+
+* chartab:
+See section 12. Unlike the rest of the files in the i18n subdirectory,
+this one does not contain messages to be spoken.
+
+* colors:
+When you use the "say attributes" function, Speakup says the name of the
+foreground and background colors. These names come from the i18n/colors
+file.
+
+* ctl_keys:
+Here, you will find names of control keys. These are used with Speakup's
+say_control feature.
+
+* formatted:
+This group of messages contains embedded formatting codes, to specify
+the type and width of displayed data. If you change these, you must
+preserve all of the formatting codes, and they must appear in the order
+used by the default messages.
+
+* function_names:
+Here, you will find a list of names for Speakup functions. These are used
+by the help system. For example, suppose that you have activated help mode,
+and you pressed keypad 3. Speakup says:
+"keypad 3 is character, say next."
+The message "character, say next" names a Speakup function, and it
+comes from this function_names file.
+
+* key_names:
+Again, key_names is used by Speakup's help system. In the previous
+example, Speakup said that you pressed "keypad 3."
+This name came from the key_names file.
+
+* states:
+This file contains names for key states.
+Again, these are part of the help system. For instance, if you had pressed
+speakup + keypad 3, you would hear:
+"speakup keypad 3 is go to bottom edge."
+The speakup key is depressed, so the name of the key state is speakup.
+This part of the message comes from the states collection.
+
+14.2. Loading Your Own Messages
+
+The files under the i18n subdirectory all follow the same format.
+They consist of lines, with one message per line.
+Each message is represented by a number, followed by the text of the message.
+The number is the position of the message in the given collection.
+For example, if you view the file /speakup/i18n/colors, you will see the
+following list:
+
+0 black
+1 blue
+2 green
+3 cyan
+4 red
+5 magenta
+6 yellow
+7 white
+8 grey
+
+You can change one message, or you can change a whole group.
+To load a whole collection of messages from a new source, simply use
+the cp command:
+cp ~/my_colors /speakup/i18n/colors
+You can change an individual message with the echo command,
+as shown in the following example.
+
+The Spanish name for the color blue is azul.
+Looking at the colors file, we see that the name "blue" is at position 1
+within the colors group. Let's change blue to azul:
+echo '1 azul' > /speakup/i18n/colors
+The next time that Speakup says message 1 from the colors group, it will
+say "azul", rather than "blue."
+
+In the future, translations into various languages will be made available,
+and most users will just load the files necessary for their language.
+
+14.3. No Support for Non-Western-European Languages
+
+As of the current release, Speakup only supports Western European languages.
+Support for the extended characters used by languages outside of the Western
+European family of languages is a work in progress.
+
+15. Using Speakup's Windowing Capability
+
+Speakup has the capability of defining and manipulating windows on the
+screen. Speakup uses the term "Window", to mean a user defined area of
+the screen. The key strokes for defining and manipulating Speakup
+windows are as follows:
+
+speakup + f2 -- Set the bounds of the window.
+Speakup + f3 -- clear the current window definition.
+speakup + f4 -- Toggle window silence on and off.
+speakup + keypad plus -- Say the currently defined window.
+
+These capabilities are useful for tracking a certain part of the screen
+without rereading the whole screen, or for silencing a part of the
+screen that is constantly changing, such as a clock or status line.
+
+There is no way to save these window settings, and you can only have one
+window defined for each virtual console. There is also no way to have
+windows automatically defined for specific applications.
+
+In order to define a window, use the review keys to move your reading
+cursor to the beginning of the area you want to define. Then press
+speakup + f2. Speakup will tell you that the window starts at the
+indicated row and column position. Then move the reading cursor to the
+end of the area to be defined as a window, and press speakup + f2 again.
+ If there is more than one line in the window, Speakup will tell you
+that the window ends at the indicated row and column position. If there
+is only one line in the window, then Speakup will tell you that the
+window is the specified line on the screen. If you are only defining a
+one line window, you can just press speakup + f2 twice after placing the
+reading cursor on the line you want to define as a window. It is not
+necessary to position the reading cursor at the end of the line in order
+to define the whole line as a window.
+
+16. Tools for Controlling Speakup
+
+The speakup distribution includes extra tools (in the tools directory)
+which were written to make speakup easier to use. This section will
+briefly describe the use of these tools.
+
+16.1. Speakupconf
+
+speakupconf began life as a contribution from Steve Holmes, a member of
+the speakup community. We would like to thank him for his work on the
+early versions of this project.
+
+This script may be installed as part of your linux distribution, but if
+it isn't, the recommended places to put it are /usr/local/bin or
+/usr/bin. This script can be run by any user, so it does not require
+root privileges.
+
+Speakupconf allows you to save and load your Speakup settings. It works
+by reading and writing the /sys files described above.
+
+The directory that speakupconf uses to store your settings depends on
+whether it is run from the root account. If you execute speakupconf as
+root, it uses the directory /etc/speakup. Otherwise, it uses the directory
+~/.speakup, where ~ is your home directory.
+Anyone who needs to use Speakup from your console can load his own custom
+settings with this script.
+
+speakupconf takes one required argument: load or save.
+Use the command
+speakupconf save
+to save your Speakup settings, and
+speakupconf load
+to load them into Speakup.
+A second argument may be specified to use an alternate directory to
+load or save the speakup parameters.
+
+16.2. Talkwith
+
+Charles Hallenbeck, another member of the speakup community, wrote the
+initial versions of this script, and we would also like to thank him for
+his work on it.
+
+This script needs root privileges to run, so if it is not installed as
+part of your linux distribution, the recommended places to install it
+are /usr/local/sbin or /usr/sbin.
+
+Talkwith allows you to switch synthesizers on the fly. It takes a synthesizer
+name as an argument. For instance,
+talkwith dectlk
+causes Speakup to use the DecTalk Express. If you wish to switch to a
+software synthesizer, you must also indicate which daemon you wish to
+use. There are two possible choices:
+spd and espeakup. spd is an abbreviation for speechd-up.
+If you wish to use espeakup for software synthesis, give the command
+talkwith soft espeakup
+To use speechd-up, type:
+talkwith soft spd
+Any arguments that follow the name of the daemon are passed to the daemon
+when it is invoked. For instance:
+talkwith espeakup --default-voice=fr
+causes espeakup to use the French voice.
+Note that talkwith must always be executed with root privileges.
+
+Talkwith does not attempt to load your settings after the new
+synthesizer is activated. You can use speakupconf to load your settings
+if desired.
+
+ GNU Free Documentation License
+ Version 1.2, November 2002
+
+
+ Copyright (C) 2000,2001,2002 Free Software Foundation, Inc.
+ 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+
+0. PREAMBLE
+
+The purpose of this License is to make a manual, textbook, or other
+functional and useful document "free" in the sense of freedom: to
+assure everyone the effective freedom to copy and redistribute it,
+with or without modifying it, either commercially or noncommercially.
+Secondarily, this License preserves for the author and publisher a way
+to get credit for their work, while not being considered responsible
+for modifications made by others.
+
+This License is a kind of "copyleft", which means that derivative
+works of the document must themselves be free in the same sense. It
+complements the GNU General Public License, which is a copyleft
+license designed for free software.
+
+We have designed this License in order to use it for manuals for free
+software, because free software needs free documentation: a free
+program should come with manuals providing the same freedoms that the
+software does. But this License is not limited to software manuals;
+it can be used for any textual work, regardless of subject matter or
+whether it is published as a printed book. We recommend this License
+principally for works whose purpose is instruction or reference.
+
+
+1. APPLICABILITY AND DEFINITIONS
+
+This License applies to any manual or other work, in any medium, that
+contains a notice placed by the copyright holder saying it can be
+distributed under the terms of this License. Such a notice grants a
+world-wide, royalty-free license, unlimited in duration, to use that
+work under the conditions stated herein. The "Document", below,
+refers to any such manual or work. Any member of the public is a
+licensee, and is addressed as "you". You accept the license if you
+copy, modify or distribute the work in a way requiring permission
+under copyright law.
+
+A "Modified Version" of the Document means any work containing the
+Document or a portion of it, either copied verbatim, or with
+modifications and/or translated into another language.
+
+A "Secondary Section" is a named appendix or a front-matter section of
+the Document that deals exclusively with the relationship of the
+publishers or authors of the Document to the Document's overall subject
+(or to related matters) and contains nothing that could fall directly
+within that overall subject. (Thus, if the Document is in part a
+textbook of mathematics, a Secondary Section may not explain any
+mathematics.) The relationship could be a matter of historical
+connection with the subject or with related matters, or of legal,
+commercial, philosophical, ethical or political position regarding
+them.
+
+The "Invariant Sections" are certain Secondary Sections whose titles
+are designated, as being those of Invariant Sections, in the notice
+that says that the Document is released under this License. If a
+section does not fit the above definition of Secondary then it is not
+allowed to be designated as Invariant. The Document may contain zero
+Invariant Sections. If the Document does not identify any Invariant
+Sections then there are none.
+
+The "Cover Texts" are certain short passages of text that are listed,
+as Front-Cover Texts or Back-Cover Texts, in the notice that says that
+the Document is released under this License. A Front-Cover Text may
+be at most 5 words, and a Back-Cover Text may be at most 25 words.
+
+A "Transparent" copy of the Document means a machine-readable copy,
+represented in a format whose specification is available to the
+general public, that is suitable for revising the document
+straightforwardly with generic text editors or (for images composed of
+pixels) generic paint programs or (for drawings) some widely available
+drawing editor, and that is suitable for input to text formatters or
+for automatic translation to a variety of formats suitable for input
+to text formatters. A copy made in an otherwise Transparent file
+format whose markup, or absence of markup, has been arranged to thwart
+or discourage subsequent modification by readers is not Transparent.
+An image format is not Transparent if used for any substantial amount
+of text. A copy that is not "Transparent" is called "Opaque".
+
+Examples of suitable formats for Transparent copies include plain
+ASCII without markup, Texinfo input format, LaTeX input format, SGML
+or XML using a publicly available DTD, and standard-conforming simple
+HTML, PostScript or PDF designed for human modification. Examples of
+transparent image formats include PNG, XCF and JPG. Opaque formats
+include proprietary formats that can be read and edited only by
+proprietary word processors, SGML or XML for which the DTD and/or
+processing tools are not generally available, and the
+machine-generated HTML, PostScript or PDF produced by some word
+processors for output purposes only.
+
+The "Title Page" means, for a printed book, the title page itself,
+plus such following pages as are needed to hold, legibly, the material
+this License requires to appear in the title page. For works in
+formats which do not have any title page as such, "Title Page" means
+the text near the most prominent appearance of the work's title,
+preceding the beginning of the body of the text.
+
+A section "Entitled XYZ" means a named subunit of the Document whose
+title either is precisely XYZ or contains XYZ in parentheses following
+text that translates XYZ in another language. (Here XYZ stands for a
+specific section name mentioned below, such as "Acknowledgements",
+"Dedications", "Endorsements", or "History".) To "Preserve the Title"
+of such a section when you modify the Document means that it remains a
+section "Entitled XYZ" according to this definition.
+
+The Document may include Warranty Disclaimers next to the notice which
+states that this License applies to the Document. These Warranty
+Disclaimers are considered to be included by reference in this
+License, but only as regards disclaiming warranties: any other
+implication that these Warranty Disclaimers may have is void and has
+no effect on the meaning of this License.
+
+
+2. VERBATIM COPYING
+
+You may copy and distribute the Document in any medium, either
+commercially or noncommercially, provided that this License, the
+copyright notices, and the license notice saying this License applies
+to the Document are reproduced in all copies, and that you add no other
+conditions whatsoever to those of this License. You may not use
+technical measures to obstruct or control the reading or further
+copying of the copies you make or distribute. However, you may accept
+compensation in exchange for copies. If you distribute a large enough
+number of copies you must also follow the conditions in section 3.
+
+You may also lend copies, under the same conditions stated above, and
+you may publicly display copies.
+
+
+3. COPYING IN QUANTITY
+
+If you publish printed copies (or copies in media that commonly have
+printed covers) of the Document, numbering more than 100, and the
+Document's license notice requires Cover Texts, you must enclose the
+copies in covers that carry, clearly and legibly, all these Cover
+Texts: Front-Cover Texts on the front cover, and Back-Cover Texts on
+the back cover. Both covers must also clearly and legibly identify
+you as the publisher of these copies. The front cover must present
+the full title with all words of the title equally prominent and
+visible. You may add other material on the covers in addition.
+Copying with changes limited to the covers, as long as they preserve
+the title of the Document and satisfy these conditions, can be treated
+as verbatim copying in other respects.
+
+If the required texts for either cover are too voluminous to fit
+legibly, you should put the first ones listed (as many as fit
+reasonably) on the actual cover, and continue the rest onto adjacent
+pages.
+
+If you publish or distribute Opaque copies of the Document numbering
+more than 100, you must either include a machine-readable Transparent
+copy along with each Opaque copy, or state in or with each Opaque copy
+a computer-network location from which the general network-using
+public has access to download using public-standard network protocols
+a complete Transparent copy of the Document, free of added material.
+If you use the latter option, you must take reasonably prudent steps,
+when you begin distribution of Opaque copies in quantity, to ensure
+that this Transparent copy will remain thus accessible at the stated
+location until at least one year after the last time you distribute an
+Opaque copy (directly or through your agents or retailers) of that
+edition to the public.
+
+It is requested, but not required, that you contact the authors of the
+Document well before redistributing any large number of copies, to give
+them a chance to provide you with an updated version of the Document.
+
+
+4. MODIFICATIONS
+
+You may copy and distribute a Modified Version of the Document under
+the conditions of sections 2 and 3 above, provided that you release
+the Modified Version under precisely this License, with the Modified
+Version filling the role of the Document, thus licensing distribution
+and modification of the Modified Version to whoever possesses a copy
+of it. In addition, you must do these things in the Modified Version:
+
+A. Use in the Title Page (and on the covers, if any) a title distinct
+ from that of the Document, and from those of previous versions
+ (which should, if there were any, be listed in the History section
+ of the Document). You may use the same title as a previous version
+ if the original publisher of that version gives permission.
+B. List on the Title Page, as authors, one or more persons or entities
+ responsible for authorship of the modifications in the Modified
+ Version, together with at least five of the principal authors of the
+ Document (all of its principal authors, if it has fewer than five),
+ unless they release you from this requirement.
+C. State on the Title page the name of the publisher of the
+ Modified Version, as the publisher.
+D. Preserve all the copyright notices of the Document.
+E. Add an appropriate copyright notice for your modifications
+ adjacent to the other copyright notices.
+F. Include, immediately after the copyright notices, a license notice
+ giving the public permission to use the Modified Version under the
+ terms of this License, in the form shown in the Addendum below.
+G. Preserve in that license notice the full lists of Invariant Sections
+ and required Cover Texts given in the Document's license notice.
+H. Include an unaltered copy of this License.
+I. Preserve the section Entitled "History", Preserve its Title, and add
+ to it an item stating at least the title, year, new authors, and
+ publisher of the Modified Version as given on the Title Page. If
+ there is no section Entitled "History" in the Document, create one
+ stating the title, year, authors, and publisher of the Document as
+ given on its Title Page, then add an item describing the Modified
+ Version as stated in the previous sentence.
+J. Preserve the network location, if any, given in the Document for
+ public access to a Transparent copy of the Document, and likewise
+ the network locations given in the Document for previous versions
+ it was based on. These may be placed in the "History" section.
+ You may omit a network location for a work that was published at
+ least four years before the Document itself, or if the original
+ publisher of the version it refers to gives permission.
+K. For any section Entitled "Acknowledgements" or "Dedications",
+ Preserve the Title of the section, and preserve in the section all
+ the substance and tone of each of the contributor acknowledgements
+ and/or dedications given therein.
+L. Preserve all the Invariant Sections of the Document,
+ unaltered in their text and in their titles. Section numbers
+ or the equivalent are not considered part of the section titles.
+M. Delete any section Entitled "Endorsements". Such a section
+ may not be included in the Modified Version.
+N. Do not retitle any existing section to be Entitled "Endorsements"
+ or to conflict in title with any Invariant Section.
+O. Preserve any Warranty Disclaimers.
+
+If the Modified Version includes new front-matter sections or
+appendices that qualify as Secondary Sections and contain no material
+copied from the Document, you may at your option designate some or all
+of these sections as invariant. To do this, add their titles to the
+list of Invariant Sections in the Modified Version's license notice.
+These titles must be distinct from any other section titles.
+
+You may add a section Entitled "Endorsements", provided it contains
+nothing but endorsements of your Modified Version by various
+parties--for example, statements of peer review or that the text has
+been approved by an organization as the authoritative definition of a
+standard.
+
+You may add a passage of up to five words as a Front-Cover Text, and a
+passage of up to 25 words as a Back-Cover Text, to the end of the list
+of Cover Texts in the Modified Version. Only one passage of
+Front-Cover Text and one of Back-Cover Text may be added by (or
+through arrangements made by) any one entity. If the Document already
+includes a cover text for the same cover, previously added by you or
+by arrangement made by the same entity you are acting on behalf of,
+you may not add another; but you may replace the old one, on explicit
+permission from the previous publisher that added the old one.
+
+The author(s) and publisher(s) of the Document do not by this License
+give permission to use their names for publicity for or to assert or
+imply endorsement of any Modified Version.
+
+
+5. COMBINING DOCUMENTS
+
+You may combine the Document with other documents released under this
+License, under the terms defined in section 4 above for modified
+versions, provided that you include in the combination all of the
+Invariant Sections of all of the original documents, unmodified, and
+list them all as Invariant Sections of your combined work in its
+license notice, and that you preserve all their Warranty Disclaimers.
+
+The combined work need only contain one copy of this License, and
+multiple identical Invariant Sections may be replaced with a single
+copy. If there are multiple Invariant Sections with the same name but
+different contents, make the title of each such section unique by
+adding at the end of it, in parentheses, the name of the original
+author or publisher of that section if known, or else a unique number.
+Make the same adjustment to the section titles in the list of
+Invariant Sections in the license notice of the combined work.
+
+In the combination, you must combine any sections Entitled "History"
+in the various original documents, forming one section Entitled
+"History"; likewise combine any sections Entitled "Acknowledgements",
+and any sections Entitled "Dedications". You must delete all sections
+Entitled "Endorsements".
+
+
+6. COLLECTIONS OF DOCUMENTS
+
+You may make a collection consisting of the Document and other documents
+released under this License, and replace the individual copies of this
+License in the various documents with a single copy that is included in
+the collection, provided that you follow the rules of this License for
+verbatim copying of each of the documents in all other respects.
+
+You may extract a single document from such a collection, and distribute
+it individually under this License, provided you insert a copy of this
+License into the extracted document, and follow this License in all
+other respects regarding verbatim copying of that document.
+
+
+7. AGGREGATION WITH INDEPENDENT WORKS
+
+A compilation of the Document or its derivatives with other separate
+and independent documents or works, in or on a volume of a storage or
+distribution medium, is called an "aggregate" if the copyright
+resulting from the compilation is not used to limit the legal rights
+of the compilation's users beyond what the individual works permit.
+When the Document is included in an aggregate, this License does not
+apply to the other works in the aggregate which are not themselves
+derivative works of the Document.
+
+If the Cover Text requirement of section 3 is applicable to these
+copies of the Document, then if the Document is less than one half of
+the entire aggregate, the Document's Cover Texts may be placed on
+covers that bracket the Document within the aggregate, or the
+electronic equivalent of covers if the Document is in electronic form.
+Otherwise they must appear on printed covers that bracket the whole
+aggregate.
+
+
+8. TRANSLATION
+
+Translation is considered a kind of modification, so you may
+distribute translations of the Document under the terms of section 4.
+Replacing Invariant Sections with translations requires special
+permission from their copyright holders, but you may include
+translations of some or all Invariant Sections in addition to the
+original versions of these Invariant Sections. You may include a
+translation of this License, and all the license notices in the
+Document, and any Warranty Disclaimers, provided that you also include
+the original English version of this License and the original versions
+of those notices and disclaimers. In case of a disagreement between
+the translation and the original version of this License or a notice
+or disclaimer, the original version will prevail.
+
+If a section in the Document is Entitled "Acknowledgements",
+"Dedications", or "History", the requirement (section 4) to Preserve
+its Title (section 1) will typically require changing the actual
+title.
+
+
+9. TERMINATION
+
+You may not copy, modify, sublicense, or distribute the Document except
+as expressly provided for under this License. Any other attempt to
+copy, modify, sublicense or distribute the Document is void, and will
+automatically terminate your rights under this License. However,
+parties who have received copies, or rights, from you under this
+License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+
+10. FUTURE REVISIONS OF THIS LICENSE
+
+The Free Software Foundation may publish new, revised versions
+of the GNU Free Documentation License from time to time. Such new
+versions will be similar in spirit to the present version, but may
+differ in detail to address new problems or concerns. See
+http://www.gnu.org/copyleft/.
+
+Each version of the License is given a distinguishing version number.
+If the Document specifies that a particular numbered version of this
+License "or any later version" applies to it, you have the option of
+following the terms and conditions either of that specified version or
+of any later version that has been published (not as a draft) by the
+Free Software Foundation. If the Document does not specify a version
+number of this License, you may choose any version ever published (not
+as a draft) by the Free Software Foundation.
+
+
+ADDENDUM: How to use this License for your documents
+
+To use this License in a document you have written, include a copy of
+the License in the document and put the following copyright and
+license notices just after the title page:
+
+ Copyright (c) YEAR YOUR NAME.
+ Permission is granted to copy, distribute and/or modify this document
+ under the terms of the GNU Free Documentation License, Version 1.2
+ or any later version published by the Free Software Foundation;
+ with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts.
+ A copy of the license is included in the section entitled "GNU
+ Free Documentation License".
+
+If you have Invariant Sections, Front-Cover Texts and Back-Cover Texts,
+replace the "with...Texts." line with this:
+
+ with the Invariant Sections being LIST THEIR TITLES, with the
+ Front-Cover Texts being LIST, and with the Back-Cover Texts being LIST.
+
+If you have Invariant Sections without Cover Texts, or some other
+combination of the three, merge those two alternatives to suit the
+situation.
+
+If your document contains nontrivial examples of program code, we
+recommend releasing these examples in parallel under your choice of
+free software license, such as the GNU General Public License,
+to permit their use in free software.
+
+The End.
diff --git a/drivers/staging/speakup/synth.c b/drivers/staging/speakup/synth.c
new file mode 100644
index 000000000..01eddab93
--- /dev/null
+++ b/drivers/staging/speakup/synth.c
@@ -0,0 +1,485 @@
+#include <linux/types.h>
+#include <linux/ctype.h> /* for isdigit() and friends */
+#include <linux/fs.h>
+#include <linux/mm.h> /* for verify_area */
+#include <linux/errno.h> /* for -EBUSY */
+#include <linux/ioport.h> /* for check_region, request_region */
+#include <linux/interrupt.h>
+#include <linux/delay.h> /* for loops_per_sec */
+#include <linux/kmod.h>
+#include <linux/jiffies.h>
+#include <linux/uaccess.h> /* for copy_from_user */
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/kthread.h>
+
+#include "spk_priv.h"
+#include "speakup.h"
+#include "serialio.h"
+
+#define MAXSYNTHS 16 /* Max number of synths in array. */
+static struct spk_synth *synths[MAXSYNTHS];
+struct spk_synth *synth;
+char spk_pitch_buff[32] = "";
+static int module_status;
+bool spk_quiet_boot;
+
+struct speakup_info_t speakup_info = {
+ /*
+ * This spinlock is used to protect the entire speakup machinery, and
+ * must be taken at each kernel->speakup transition and released at
+ * each corresponding speakup->kernel transition.
+ *
+ * The progression thread only interferes with the speakup machinery
+ * through the synth buffer, so only needs to take the lock
+ * while tinkering with the buffer.
+ *
+ * We use spin_lock/trylock_irqsave and spin_unlock_irqrestore with this
+ * spinlock because speakup needs to disable the keyboard IRQ.
+ */
+ .spinlock = __SPIN_LOCK_UNLOCKED(speakup_info.spinlock),
+ .flushing = 0,
+};
+EXPORT_SYMBOL_GPL(speakup_info);
+
+static int do_synth_init(struct spk_synth *in_synth);
+
+int spk_serial_synth_probe(struct spk_synth *synth)
+{
+ const struct old_serial_port *ser;
+ int failed = 0;
+
+ if ((synth->ser >= SPK_LO_TTY) && (synth->ser <= SPK_HI_TTY)) {
+ ser = spk_serial_init(synth->ser);
+ if (ser == NULL) {
+ failed = -1;
+ } else {
+ outb_p(0, ser->port);
+ mdelay(1);
+ outb_p('\r', ser->port);
+ }
+ } else {
+ failed = -1;
+ pr_warn("ttyS%i is an invalid port\n", synth->ser);
+ }
+ if (failed) {
+ pr_info("%s: not found\n", synth->long_name);
+ return -ENODEV;
+ }
+ pr_info("%s: ttyS%i, Driver Version %s\n",
+ synth->long_name, synth->ser, synth->version);
+ synth->alive = 1;
+ return 0;
+}
+EXPORT_SYMBOL_GPL(spk_serial_synth_probe);
+
+/* Main loop of the progression thread: keep eating from the buffer
+ * and push to the serial port, waiting as needed
+ *
+ * For devices that have a "full" notification mechanism, the driver can
+ * adapt the loop the way they prefer.
+ */
+void spk_do_catch_up(struct spk_synth *synth)
+{
+ u_char ch;
+ unsigned long flags;
+ unsigned long jiff_max;
+ struct var_t *delay_time;
+ struct var_t *full_time;
+ struct var_t *jiffy_delta;
+ int jiffy_delta_val;
+ int delay_time_val;
+ int full_time_val;
+
+ jiffy_delta = spk_get_var(JIFFY);
+ full_time = spk_get_var(FULL);
+ delay_time = spk_get_var(DELAY);
+
+ spin_lock_irqsave(&speakup_info.spinlock, flags);
+ jiffy_delta_val = jiffy_delta->u.n.value;
+ spin_unlock_irqrestore(&speakup_info.spinlock, flags);
+
+ jiff_max = jiffies + jiffy_delta_val;
+ while (!kthread_should_stop()) {
+ spin_lock_irqsave(&speakup_info.spinlock, flags);
+ if (speakup_info.flushing) {
+ speakup_info.flushing = 0;
+ spin_unlock_irqrestore(&speakup_info.spinlock, flags);
+ synth->flush(synth);
+ continue;
+ }
+ if (synth_buffer_empty()) {
+ spin_unlock_irqrestore(&speakup_info.spinlock, flags);
+ break;
+ }
+ ch = synth_buffer_peek();
+ set_current_state(TASK_INTERRUPTIBLE);
+ full_time_val = full_time->u.n.value;
+ spin_unlock_irqrestore(&speakup_info.spinlock, flags);
+ if (ch == '\n')
+ ch = synth->procspeech;
+ if (!spk_serial_out(ch)) {
+ schedule_timeout(msecs_to_jiffies(full_time_val));
+ continue;
+ }
+ if (time_after_eq(jiffies, jiff_max) && (ch == SPACE)) {
+ spin_lock_irqsave(&speakup_info.spinlock, flags);
+ jiffy_delta_val = jiffy_delta->u.n.value;
+ delay_time_val = delay_time->u.n.value;
+ full_time_val = full_time->u.n.value;
+ spin_unlock_irqrestore(&speakup_info.spinlock, flags);
+ if (spk_serial_out(synth->procspeech))
+ schedule_timeout(
+ msecs_to_jiffies(delay_time_val));
+ else
+ schedule_timeout(
+ msecs_to_jiffies(full_time_val));
+ jiff_max = jiffies + jiffy_delta_val;
+ }
+ set_current_state(TASK_RUNNING);
+ spin_lock_irqsave(&speakup_info.spinlock, flags);
+ synth_buffer_getc();
+ spin_unlock_irqrestore(&speakup_info.spinlock, flags);
+ }
+ spk_serial_out(synth->procspeech);
+}
+EXPORT_SYMBOL_GPL(spk_do_catch_up);
+
+const char *spk_synth_immediate(struct spk_synth *synth, const char *buff)
+{
+ u_char ch;
+
+ while ((ch = *buff)) {
+ if (ch == '\n')
+ ch = synth->procspeech;
+ if (spk_wait_for_xmitr())
+ outb(ch, speakup_info.port_tts);
+ else
+ return buff;
+ buff++;
+ }
+ return NULL;
+}
+EXPORT_SYMBOL_GPL(spk_synth_immediate);
+
+void spk_synth_flush(struct spk_synth *synth)
+{
+ spk_serial_out(synth->clear);
+}
+EXPORT_SYMBOL_GPL(spk_synth_flush);
+
+int spk_synth_is_alive_nop(struct spk_synth *synth)
+{
+ synth->alive = 1;
+ return 1;
+}
+EXPORT_SYMBOL_GPL(spk_synth_is_alive_nop);
+
+int spk_synth_is_alive_restart(struct spk_synth *synth)
+{
+ if (synth->alive)
+ return 1;
+ if (!synth->alive && spk_wait_for_xmitr() > 0) {
+ /* restart */
+ synth->alive = 1;
+ synth_printf("%s", synth->init);
+ return 2; /* reenabled */
+ }
+ pr_warn("%s: can't restart synth\n", synth->long_name);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(spk_synth_is_alive_restart);
+
+static void thread_wake_up(u_long data)
+{
+ wake_up_interruptible_all(&speakup_event);
+}
+
+static DEFINE_TIMER(thread_timer, thread_wake_up, 0, 0);
+
+void synth_start(void)
+{
+ struct var_t *trigger_time;
+
+ if (!synth->alive) {
+ synth_buffer_clear();
+ return;
+ }
+ trigger_time = spk_get_var(TRIGGER);
+ if (!timer_pending(&thread_timer))
+ mod_timer(&thread_timer, jiffies +
+ msecs_to_jiffies(trigger_time->u.n.value));
+}
+
+void spk_do_flush(void)
+{
+ if (!synth)
+ return;
+
+ speakup_info.flushing = 1;
+ synth_buffer_clear();
+ if (synth->alive) {
+ if (spk_pitch_shift) {
+ synth_printf("%s", spk_pitch_buff);
+ spk_pitch_shift = 0;
+ }
+ }
+ wake_up_interruptible_all(&speakup_event);
+ wake_up_process(speakup_task);
+}
+
+void synth_write(const char *buf, size_t count)
+{
+ while (count--)
+ synth_buffer_add(*buf++);
+ synth_start();
+}
+
+void synth_printf(const char *fmt, ...)
+{
+ va_list args;
+ unsigned char buf[160], *p;
+ int r;
+
+ va_start(args, fmt);
+ r = vsnprintf(buf, sizeof(buf), fmt, args);
+ va_end(args);
+ if (r > sizeof(buf) - 1)
+ r = sizeof(buf) - 1;
+
+ p = buf;
+ while (r--)
+ synth_buffer_add(*p++);
+ synth_start();
+}
+EXPORT_SYMBOL_GPL(synth_printf);
+
+static int index_count;
+static int sentence_count;
+
+void spk_reset_index_count(int sc)
+{
+ static int first = 1;
+
+ if (first)
+ first = 0;
+ else
+ synth->get_index();
+ index_count = 0;
+ sentence_count = sc;
+}
+
+int synth_supports_indexing(void)
+{
+ if (synth->get_index != NULL)
+ return 1;
+ return 0;
+}
+
+void synth_insert_next_index(int sent_num)
+{
+ int out;
+
+ if (synth->alive) {
+ if (sent_num == 0) {
+ synth->indexing.currindex++;
+ index_count++;
+ if (synth->indexing.currindex >
+ synth->indexing.highindex)
+ synth->indexing.currindex =
+ synth->indexing.lowindex;
+ }
+
+ out = synth->indexing.currindex * 10 + sent_num;
+ synth_printf(synth->indexing.command, out, out);
+ }
+}
+
+void spk_get_index_count(int *linecount, int *sentcount)
+{
+ int ind = synth->get_index();
+
+ if (ind) {
+ sentence_count = ind % 10;
+
+ if ((ind / 10) <= synth->indexing.currindex)
+ index_count = synth->indexing.currindex-(ind/10);
+ else
+ index_count = synth->indexing.currindex
+ -synth->indexing.lowindex
+ + synth->indexing.highindex-(ind/10)+1;
+
+ }
+ *sentcount = sentence_count;
+ *linecount = index_count;
+}
+
+static struct resource synth_res;
+
+int synth_request_region(unsigned long start, unsigned long n)
+{
+ struct resource *parent = &ioport_resource;
+
+ memset(&synth_res, 0, sizeof(synth_res));
+ synth_res.name = synth->name;
+ synth_res.start = start;
+ synth_res.end = start + n - 1;
+ synth_res.flags = IORESOURCE_BUSY;
+ return request_resource(parent, &synth_res);
+}
+EXPORT_SYMBOL_GPL(synth_request_region);
+
+int synth_release_region(unsigned long start, unsigned long n)
+{
+ return release_resource(&synth_res);
+}
+EXPORT_SYMBOL_GPL(synth_release_region);
+
+struct var_t synth_time_vars[] = {
+ { DELAY, .u.n = {NULL, 100, 100, 2000, 0, 0, NULL } },
+ { TRIGGER, .u.n = {NULL, 20, 10, 2000, 0, 0, NULL } },
+ { JIFFY, .u.n = {NULL, 50, 20, 200, 0, 0, NULL } },
+ { FULL, .u.n = {NULL, 400, 200, 60000, 0, 0, NULL } },
+ V_LAST_VAR
+};
+
+/* called by: speakup_init() */
+int synth_init(char *synth_name)
+{
+ int i;
+ int ret = 0;
+ struct spk_synth *synth = NULL;
+
+ if (synth_name == NULL)
+ return 0;
+
+ if (strcmp(synth_name, "none") == 0) {
+ mutex_lock(&spk_mutex);
+ synth_release();
+ mutex_unlock(&spk_mutex);
+ return 0;
+ }
+
+ mutex_lock(&spk_mutex);
+ /* First, check if we already have it loaded. */
+ for (i = 0; i < MAXSYNTHS && synths[i] != NULL; i++)
+ if (strcmp(synths[i]->name, synth_name) == 0)
+ synth = synths[i];
+
+ /* If we got one, initialize it now. */
+ if (synth)
+ ret = do_synth_init(synth);
+ else
+ ret = -ENODEV;
+ mutex_unlock(&spk_mutex);
+
+ return ret;
+}
+
+/* called by: synth_add() */
+static int do_synth_init(struct spk_synth *in_synth)
+{
+ struct var_t *var;
+
+ synth_release();
+ if (in_synth->checkval != SYNTH_CHECK)
+ return -EINVAL;
+ synth = in_synth;
+ synth->alive = 0;
+ pr_warn("synth probe\n");
+ if (synth->probe(synth) < 0) {
+ pr_warn("%s: device probe failed\n", in_synth->name);
+ synth = NULL;
+ return -ENODEV;
+ }
+ synth_time_vars[0].u.n.value =
+ synth_time_vars[0].u.n.default_val = synth->delay;
+ synth_time_vars[1].u.n.value =
+ synth_time_vars[1].u.n.default_val = synth->trigger;
+ synth_time_vars[2].u.n.value =
+ synth_time_vars[2].u.n.default_val = synth->jiffies;
+ synth_time_vars[3].u.n.value =
+ synth_time_vars[3].u.n.default_val = synth->full;
+ synth_printf("%s", synth->init);
+ for (var = synth->vars;
+ (var->var_id >= 0) && (var->var_id < MAXVARS); var++)
+ speakup_register_var(var);
+ if (!spk_quiet_boot)
+ synth_printf("%s found\n", synth->long_name);
+ if (synth->attributes.name
+ && sysfs_create_group(speakup_kobj, &(synth->attributes)) < 0)
+ return -ENOMEM;
+ synth_flags = synth->flags;
+ wake_up_interruptible_all(&speakup_event);
+ if (speakup_task)
+ wake_up_process(speakup_task);
+ return 0;
+}
+
+void synth_release(void)
+{
+ struct var_t *var;
+ unsigned long flags;
+
+ if (synth == NULL)
+ return;
+ spin_lock_irqsave(&speakup_info.spinlock, flags);
+ pr_info("releasing synth %s\n", synth->name);
+ synth->alive = 0;
+ del_timer(&thread_timer);
+ spin_unlock_irqrestore(&speakup_info.spinlock, flags);
+ if (synth->attributes.name)
+ sysfs_remove_group(speakup_kobj, &(synth->attributes));
+ for (var = synth->vars; var->var_id != MAXVARS; var++)
+ speakup_unregister_var(var->var_id);
+ spk_stop_serial_interrupt();
+ synth->release();
+ synth = NULL;
+}
+
+/* called by: all_driver_init() */
+int synth_add(struct spk_synth *in_synth)
+{
+ int i;
+ int status = 0;
+
+ mutex_lock(&spk_mutex);
+ for (i = 0; i < MAXSYNTHS && synths[i] != NULL; i++)
+ /* synth_remove() is responsible for rotating the array down */
+ if (in_synth == synths[i]) {
+ mutex_unlock(&spk_mutex);
+ return 0;
+ }
+ if (i == MAXSYNTHS) {
+ pr_warn("Error: attempting to add a synth past end of array\n");
+ mutex_unlock(&spk_mutex);
+ return -1;
+ }
+ synths[i++] = in_synth;
+ synths[i] = NULL;
+ if (in_synth->startup)
+ status = do_synth_init(in_synth);
+ mutex_unlock(&spk_mutex);
+ return status;
+}
+EXPORT_SYMBOL_GPL(synth_add);
+
+void synth_remove(struct spk_synth *in_synth)
+{
+ int i;
+
+ mutex_lock(&spk_mutex);
+ if (synth == in_synth)
+ synth_release();
+ for (i = 0; synths[i] != NULL; i++) {
+ if (in_synth == synths[i])
+ break;
+ }
+ for ( ; synths[i] != NULL; i++) /* compress table */
+ synths[i] = synths[i+1];
+ module_status = 0;
+ mutex_unlock(&spk_mutex);
+}
+EXPORT_SYMBOL_GPL(synth_remove);
+
+short spk_punc_masks[] = { 0, SOME, MOST, PUNC, PUNC|B_SYM };
diff --git a/drivers/staging/speakup/thread.c b/drivers/staging/speakup/thread.c
new file mode 100644
index 000000000..d95efb702
--- /dev/null
+++ b/drivers/staging/speakup/thread.c
@@ -0,0 +1,59 @@
+#include <linux/kthread.h>
+#include <linux/wait.h>
+
+#include "spk_types.h"
+#include "speakup.h"
+#include "spk_priv.h"
+
+DECLARE_WAIT_QUEUE_HEAD(speakup_event);
+EXPORT_SYMBOL_GPL(speakup_event);
+
+int speakup_thread(void *data)
+{
+ unsigned long flags;
+ int should_break;
+ struct bleep our_sound;
+
+ our_sound.active = 0;
+ our_sound.freq = 0;
+ our_sound.jiffies = 0;
+
+ mutex_lock(&spk_mutex);
+ while (1) {
+ DEFINE_WAIT(wait);
+
+ while (1) {
+ spin_lock_irqsave(&speakup_info.spinlock, flags);
+ our_sound = spk_unprocessed_sound;
+ spk_unprocessed_sound.active = 0;
+ prepare_to_wait(&speakup_event, &wait,
+ TASK_INTERRUPTIBLE);
+ should_break = kthread_should_stop() ||
+ our_sound.active ||
+ (synth && synth->catch_up && synth->alive &&
+ (speakup_info.flushing ||
+ !synth_buffer_empty()));
+ spin_unlock_irqrestore(&speakup_info.spinlock, flags);
+ if (should_break)
+ break;
+ mutex_unlock(&spk_mutex);
+ schedule();
+ mutex_lock(&spk_mutex);
+ }
+ finish_wait(&speakup_event, &wait);
+ if (kthread_should_stop())
+ break;
+
+ if (our_sound.active)
+ kd_mksound(our_sound.freq, our_sound.jiffies);
+ if (synth && synth->catch_up && synth->alive) {
+ /* It is up to the callee to take the lock, so that it
+ * can sleep whenever it likes */
+ synth->catch_up(synth);
+ }
+
+ speakup_start_ttys();
+ }
+ mutex_unlock(&spk_mutex);
+ return 0;
+}
diff --git a/drivers/staging/speakup/varhandlers.c b/drivers/staging/speakup/varhandlers.c
new file mode 100644
index 000000000..1b0d1c087
--- /dev/null
+++ b/drivers/staging/speakup/varhandlers.c
@@ -0,0 +1,332 @@
+#include <linux/ctype.h>
+#include "spk_types.h"
+#include "spk_priv.h"
+#include "speakup.h"
+
+static struct st_var_header var_headers[] = {
+ { "version", VERSION, VAR_PROC, NULL, NULL },
+ { "synth_name", SYNTH, VAR_PROC, NULL, NULL },
+ { "keymap", KEYMAP, VAR_PROC, NULL, NULL },
+ { "silent", SILENT, VAR_PROC, NULL, NULL },
+ { "punc_some", PUNC_SOME, VAR_PROC, NULL, NULL },
+ { "punc_most", PUNC_MOST, VAR_PROC, NULL, NULL },
+ { "punc_all", PUNC_ALL, VAR_PROC, NULL, NULL },
+ { "delimiters", DELIM, VAR_PROC, NULL, NULL },
+ { "repeats", REPEATS, VAR_PROC, NULL, NULL },
+ { "ex_num", EXNUMBER, VAR_PROC, NULL, NULL },
+ { "characters", CHARS, VAR_PROC, NULL, NULL },
+ { "synth_direct", SYNTH_DIRECT, VAR_PROC, NULL, NULL },
+ { "caps_start", CAPS_START, VAR_STRING, spk_str_caps_start, NULL },
+ { "caps_stop", CAPS_STOP, VAR_STRING, spk_str_caps_stop, NULL },
+ { "delay_time", DELAY, VAR_TIME, NULL, NULL },
+ { "trigger_time", TRIGGER, VAR_TIME, NULL, NULL },
+ { "jiffy_delta", JIFFY, VAR_TIME, NULL, NULL },
+ { "full_time", FULL, VAR_TIME, NULL, NULL },
+ { "spell_delay", SPELL_DELAY, VAR_NUM, &spk_spell_delay, NULL },
+ { "bleeps", BLEEPS, VAR_NUM, &spk_bleeps, NULL },
+ { "attrib_bleep", ATTRIB_BLEEP, VAR_NUM, &spk_attrib_bleep, NULL },
+ { "bleep_time", BLEEP_TIME, VAR_TIME, &spk_bleep_time, NULL },
+ { "cursor_time", CURSOR_TIME, VAR_TIME, NULL, NULL },
+ { "punc_level", PUNC_LEVEL, VAR_NUM, &spk_punc_level, NULL },
+ { "reading_punc", READING_PUNC, VAR_NUM, &spk_reading_punc, NULL },
+ { "say_control", SAY_CONTROL, VAR_NUM, &spk_say_ctrl, NULL },
+ { "say_word_ctl", SAY_WORD_CTL, VAR_NUM, &spk_say_word_ctl, NULL },
+ { "no_interrupt", NO_INTERRUPT, VAR_NUM, &spk_no_intr, NULL },
+ { "key_echo", KEY_ECHO, VAR_NUM, &spk_key_echo, NULL },
+ { "bell_pos", BELL_POS, VAR_NUM, &spk_bell_pos, NULL },
+ { "rate", RATE, VAR_NUM, NULL, NULL },
+ { "pitch", PITCH, VAR_NUM, NULL, NULL },
+ { "vol", VOL, VAR_NUM, NULL, NULL },
+ { "tone", TONE, VAR_NUM, NULL, NULL },
+ { "punct", PUNCT, VAR_NUM, NULL, NULL },
+ { "voice", VOICE, VAR_NUM, NULL, NULL },
+ { "freq", FREQUENCY, VAR_NUM, NULL, NULL },
+ { "lang", LANG, VAR_NUM, NULL, NULL },
+ { "chartab", CHARTAB, VAR_PROC, NULL, NULL },
+ { "direct", DIRECT, VAR_NUM, NULL, NULL },
+};
+
+static struct st_var_header *var_ptrs[MAXVARS] = { NULL, NULL, NULL };
+
+static struct punc_var_t punc_vars[] = {
+ { PUNC_SOME, 1 },
+ { PUNC_MOST, 2 },
+ { PUNC_ALL, 3 },
+ { DELIM, 4 },
+ { REPEATS, 5 },
+ { EXNUMBER, 6 },
+ { -1, -1 },
+};
+
+int spk_chartab_get_value(char *keyword)
+{
+ int value = 0;
+
+ if (!strcmp(keyword, "ALPHA"))
+ value = ALPHA;
+ else if (!strcmp(keyword, "B_CTL"))
+ value = B_CTL;
+ else if (!strcmp(keyword, "WDLM"))
+ value = WDLM;
+ else if (!strcmp(keyword, "A_PUNC"))
+ value = A_PUNC;
+ else if (!strcmp(keyword, "PUNC"))
+ value = PUNC;
+ else if (!strcmp(keyword, "NUM"))
+ value = NUM;
+ else if (!strcmp(keyword, "A_CAP"))
+ value = A_CAP;
+ else if (!strcmp(keyword, "B_CAPSYM"))
+ value = B_CAPSYM;
+ else if (!strcmp(keyword, "B_SYM"))
+ value = B_SYM;
+ return value;
+}
+
+void speakup_register_var(struct var_t *var)
+{
+ static char nothing[2] = "\0";
+ int i;
+ struct st_var_header *p_header;
+
+ BUG_ON(!var || var->var_id < 0 || var->var_id >= MAXVARS);
+ if (var_ptrs[0] == NULL) {
+ for (i = 0; i < MAXVARS; i++) {
+ p_header = &var_headers[i];
+ var_ptrs[p_header->var_id] = p_header;
+ p_header->data = NULL;
+ }
+ }
+ p_header = var_ptrs[var->var_id];
+ if (p_header->data != NULL)
+ return;
+ p_header->data = var;
+ switch (p_header->var_type) {
+ case VAR_STRING:
+ spk_set_string_var(nothing, p_header, 0);
+ break;
+ case VAR_NUM:
+ case VAR_TIME:
+ spk_set_num_var(0, p_header, E_DEFAULT);
+ break;
+ default:
+ break;
+ }
+}
+
+void speakup_unregister_var(enum var_id_t var_id)
+{
+ struct st_var_header *p_header;
+
+ BUG_ON(var_id < 0 || var_id >= MAXVARS);
+ p_header = var_ptrs[var_id];
+ p_header->data = NULL;
+}
+
+struct st_var_header *spk_get_var_header(enum var_id_t var_id)
+{
+ struct st_var_header *p_header;
+
+ if (var_id < 0 || var_id >= MAXVARS)
+ return NULL;
+ p_header = var_ptrs[var_id];
+ if (p_header->data == NULL)
+ return NULL;
+ return p_header;
+}
+
+struct st_var_header *spk_var_header_by_name(const char *name)
+{
+ int i;
+
+ if (!name)
+ return NULL;
+
+ for (i = 0; i < MAXVARS; i++) {
+ if (strcmp(name, var_ptrs[i]->name) == 0)
+ return var_ptrs[i];
+ }
+ return NULL;
+}
+
+struct var_t *spk_get_var(enum var_id_t var_id)
+{
+ BUG_ON(var_id < 0 || var_id >= MAXVARS);
+ BUG_ON(!var_ptrs[var_id]);
+ return var_ptrs[var_id]->data;
+}
+EXPORT_SYMBOL_GPL(spk_get_var);
+
+struct punc_var_t *spk_get_punc_var(enum var_id_t var_id)
+{
+ struct punc_var_t *rv = NULL;
+ struct punc_var_t *where;
+
+ where = punc_vars;
+ while ((where->var_id != -1) && (rv == NULL)) {
+ if (where->var_id == var_id)
+ rv = where;
+ else
+ where++;
+ }
+ return rv;
+}
+
+/* handlers for setting vars */
+int spk_set_num_var(int input, struct st_var_header *var, int how)
+{
+ int val;
+ short ret = 0;
+ int *p_val = var->p_val;
+ int l;
+ char buf[32];
+ char *cp;
+ struct var_t *var_data = var->data;
+
+ if (var_data == NULL)
+ return -ENODATA;
+
+ if (how == E_NEW_DEFAULT) {
+ if (input < var_data->u.n.low || input > var_data->u.n.high)
+ return -ERANGE;
+ var_data->u.n.default_val = input;
+ return 0;
+ }
+ if (how == E_DEFAULT) {
+ val = var_data->u.n.default_val;
+ ret = -ERESTART;
+ } else {
+ if (how == E_SET)
+ val = input;
+ else
+ val = var_data->u.n.value;
+ if (how == E_INC)
+ val += input;
+ else if (how == E_DEC)
+ val -= input;
+ if (val < var_data->u.n.low || val > var_data->u.n.high)
+ return -ERANGE;
+ }
+ var_data->u.n.value = val;
+ if (var->var_type == VAR_TIME && p_val != NULL) {
+ *p_val = msecs_to_jiffies(val);
+ return ret;
+ }
+ if (p_val != NULL)
+ *p_val = val;
+ if (var->var_id == PUNC_LEVEL) {
+ spk_punc_mask = spk_punc_masks[val];
+ return ret;
+ }
+ if (var_data->u.n.multiplier != 0)
+ val *= var_data->u.n.multiplier;
+ val += var_data->u.n.offset;
+ if (var->var_id < FIRST_SYNTH_VAR || synth == NULL)
+ return ret;
+ if (synth->synth_adjust != NULL) {
+ int status = synth->synth_adjust(var);
+
+ return (status != 0) ? status : ret;
+ }
+ if (!var_data->u.n.synth_fmt)
+ return ret;
+ if (var->var_id == PITCH)
+ cp = spk_pitch_buff;
+ else
+ cp = buf;
+ if (!var_data->u.n.out_str)
+ l = sprintf(cp, var_data->u.n.synth_fmt, (int)val);
+ else
+ l = sprintf(cp,
+ var_data->u.n.synth_fmt, var_data->u.n.out_str[val]);
+ synth_printf("%s", cp);
+ return ret;
+}
+
+int spk_set_string_var(const char *page, struct st_var_header *var, int len)
+{
+ struct var_t *var_data = var->data;
+
+ if (var_data == NULL)
+ return -ENODATA;
+ if (len > MAXVARLEN)
+ return -E2BIG;
+ if (!len) {
+ if (!var_data->u.s.default_val)
+ return 0;
+ if (!var->p_val)
+ var->p_val = var_data->u.s.default_val;
+ if (var->p_val != var_data->u.s.default_val)
+ strcpy((char *)var->p_val, var_data->u.s.default_val);
+ return -ERESTART;
+ } else if (var->p_val)
+ strcpy((char *)var->p_val, page);
+ else
+ return -E2BIG;
+ return 0;
+}
+
+/* spk_set_mask_bits sets or clears the punc/delim/repeat bits,
+ * if input is null uses the defaults.
+ * values for how: 0 clears bits of chars supplied,
+ * 1 clears allk, 2 sets bits for chars */
+int spk_set_mask_bits(const char *input, const int which, const int how)
+{
+ u_char *cp;
+ short mask = spk_punc_info[which].mask;
+
+ if (how&1) {
+ for (cp = (u_char *)spk_punc_info[3].value; *cp; cp++)
+ spk_chartab[*cp] &= ~mask;
+ }
+ cp = (u_char *)input;
+ if (!cp)
+ cp = spk_punc_info[which].value;
+ else {
+ for (; *cp; cp++) {
+ if (*cp < SPACE)
+ break;
+ if (mask < PUNC) {
+ if (!(spk_chartab[*cp]&PUNC))
+ break;
+ } else if (spk_chartab[*cp]&B_NUM)
+ break;
+ }
+ if (*cp)
+ return -EINVAL;
+ cp = (u_char *)input;
+ }
+ if (how&2) {
+ for (; *cp; cp++)
+ if (*cp > SPACE)
+ spk_chartab[*cp] |= mask;
+ } else {
+ for (; *cp; cp++)
+ if (*cp > SPACE)
+ spk_chartab[*cp] &= ~mask;
+ }
+ return 0;
+}
+
+char *spk_strlwr(char *s)
+{
+ char *p;
+
+ if (s == NULL)
+ return NULL;
+
+ for (p = s; *p; p++)
+ *p = tolower(*p);
+ return s;
+}
+
+char *spk_s2uchar(char *start, char *dest)
+{
+ int val = 0;
+
+ val = simple_strtoul(skip_spaces(start), &start, 10);
+ if (*start == ',')
+ start++;
+ *dest = (u_char)val;
+ return start;
+}
diff --git a/drivers/staging/staging.c b/drivers/staging/staging.c
new file mode 100644
index 000000000..233e589c0
--- /dev/null
+++ b/drivers/staging/staging.c
@@ -0,0 +1,19 @@
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/module.h>
+
+static int __init staging_init(void)
+{
+ return 0;
+}
+
+static void __exit staging_exit(void)
+{
+}
+
+module_init(staging_init);
+module_exit(staging_exit);
+
+MODULE_AUTHOR("Greg Kroah-Hartman");
+MODULE_DESCRIPTION("Staging Core");
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/ste_rmi4/Kconfig b/drivers/staging/ste_rmi4/Kconfig
new file mode 100644
index 000000000..e8679509e
--- /dev/null
+++ b/drivers/staging/ste_rmi4/Kconfig
@@ -0,0 +1,9 @@
+config TOUCHSCREEN_SYNAPTICS_I2C_RMI4
+ tristate "Synaptics i2c rmi4 touchscreen"
+ depends on I2C && INPUT
+ help
+ Say Y here if you have a Synaptics RMI4 and
+ want to enable support for the built-in touchscreen.
+
+ To compile this driver as a module, choose M here: the
+ module will be called synaptics_rmi4_ts.
diff --git a/drivers/staging/ste_rmi4/Makefile b/drivers/staging/ste_rmi4/Makefile
new file mode 100644
index 000000000..6cce2ed18
--- /dev/null
+++ b/drivers/staging/ste_rmi4/Makefile
@@ -0,0 +1,4 @@
+#
+# Makefile for the RMI4 touchscreen driver.
+#
+obj-$(CONFIG_TOUCHSCREEN_SYNAPTICS_I2C_RMI4) += synaptics_i2c_rmi4.o
diff --git a/drivers/staging/ste_rmi4/TODO b/drivers/staging/ste_rmi4/TODO
new file mode 100644
index 000000000..9be2437da
--- /dev/null
+++ b/drivers/staging/ste_rmi4/TODO
@@ -0,0 +1,7 @@
+TODO
+----
+
+Wait for the official upstream synaptics rmi4 clearpad drivers as promised over the past few months
+Merge any device support needed from this driver into it
+Delete this driver
+
diff --git a/drivers/staging/ste_rmi4/synaptics_i2c_rmi4.c b/drivers/staging/ste_rmi4/synaptics_i2c_rmi4.c
new file mode 100644
index 000000000..0f524bb7b
--- /dev/null
+++ b/drivers/staging/ste_rmi4/synaptics_i2c_rmi4.c
@@ -0,0 +1,1142 @@
+/**
+ *
+ * Synaptics Register Mapped Interface (RMI4) I2C Physical Layer Driver.
+ * Copyright (c) 2007-2010, Synaptics Incorporated
+ *
+ * Author: Js HA <js.ha@stericsson.com> for ST-Ericsson
+ * Author: Naveen Kumar G <naveen.gaddipati@stericsson.com> for ST-Ericsson
+ * Copyright 2010 (c) ST-Ericsson AB
+ */
+/*
+ * This file is licensed under the GPL2 license.
+ *
+ *#############################################################################
+ * GPL
+ *
+ * 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.
+ *
+ * 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.
+ *
+ *#############################################################################
+ */
+
+#include <linux/input.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/regulator/consumer.h>
+#include <linux/module.h>
+#include <linux/input/mt.h>
+#include "synaptics_i2c_rmi4.h"
+
+/* TODO: for multiple device support will need a per-device mutex */
+#define DRIVER_NAME "synaptics_rmi4_i2c"
+
+#define MAX_ERROR_REPORT 6
+#define MAX_TOUCH_MAJOR 15
+#define MAX_RETRY_COUNT 5
+#define STD_QUERY_LEN 21
+#define PAGE_LEN 2
+#define DATA_BUF_LEN 32
+#define BUF_LEN 37
+#define QUERY_LEN 9
+#define DATA_LEN 12
+#define HAS_TAP 0x01
+#define HAS_PALMDETECT 0x01
+#define HAS_ROTATE 0x02
+#define HAS_TAPANDHOLD 0x02
+#define HAS_DOUBLETAP 0x04
+#define HAS_EARLYTAP 0x08
+#define HAS_RELEASE 0x08
+#define HAS_FLICK 0x10
+#define HAS_PRESS 0x20
+#define HAS_PINCH 0x40
+
+#define MASK_16BIT 0xFFFF
+#define MASK_8BIT 0xFF
+#define MASK_7BIT 0x7F
+#define MASK_5BIT 0x1F
+#define MASK_4BIT 0x0F
+#define MASK_3BIT 0x07
+#define MASK_2BIT 0x03
+#define TOUCHPAD_CTRL_INTR 0x8
+#define PDT_START_SCAN_LOCATION (0x00E9)
+#define PDT_END_SCAN_LOCATION (0x000A)
+#define PDT_ENTRY_SIZE (0x0006)
+#define SYNAPTICS_RMI4_TOUCHPAD_FUNC_NUM (0x11)
+#define SYNAPTICS_RMI4_DEVICE_CONTROL_FUNC_NUM (0x01)
+
+/**
+ * struct synaptics_rmi4_fn_desc - contains the function descriptor information
+ * @query_base_addr: base address for query
+ * @cmd_base_addr: base address for command
+ * @ctrl_base_addr: base address for control
+ * @data_base_addr: base address for data
+ * @intr_src_count: count for the interrupt source
+ * @fn_number: function number
+ *
+ * This structure is used to gives the function descriptor information
+ * of the particular functionality.
+ */
+struct synaptics_rmi4_fn_desc {
+ unsigned char query_base_addr;
+ unsigned char cmd_base_addr;
+ unsigned char ctrl_base_addr;
+ unsigned char data_base_addr;
+ unsigned char intr_src_count;
+ unsigned char fn_number;
+};
+
+/**
+ * struct synaptics_rmi4_fn - contains the function information
+ * @fn_number: function number
+ * @num_of_data_sources: number of data sources
+ * @num_of_data_points: number of fingers touched
+ * @size_of_data_register_block: data register block size
+ * @index_to_intr_reg: index for interrupt register
+ * @intr_mask: interrupt mask value
+ * @fn_desc: variable for function descriptor structure
+ * @link: linked list for function descriptors
+ *
+ * This structure gives information about the number of data sources and
+ * the number of data registers associated with the function.
+ */
+struct synaptics_rmi4_fn {
+ unsigned char fn_number;
+ unsigned char num_of_data_sources;
+ unsigned char num_of_data_points;
+ unsigned char size_of_data_register_block;
+ unsigned char index_to_intr_reg;
+ unsigned char intr_mask;
+ struct synaptics_rmi4_fn_desc fn_desc;
+ struct list_head link;
+};
+
+/**
+ * struct synaptics_rmi4_device_info - contains the rmi4 device information
+ * @version_major: protocol major version number
+ * @version_minor: protocol minor version number
+ * @manufacturer_id: manufacturer identification byte
+ * @product_props: product properties information
+ * @product_info: product info array
+ * @date_code: device manufacture date
+ * @tester_id: tester id array
+ * @serial_number: serial number for that device
+ * @product_id_string: product id for the device
+ * @support_fn_list: linked list for device information
+ *
+ * This structure gives information about the number of data sources and
+ * the number of data registers associated with the function.
+ */
+struct synaptics_rmi4_device_info {
+ unsigned int version_major;
+ unsigned int version_minor;
+ unsigned char manufacturer_id;
+ unsigned char product_props;
+ unsigned char product_info[2];
+ unsigned char date_code[3];
+ unsigned short tester_id;
+ unsigned short serial_number;
+ unsigned char product_id_string[11];
+ struct list_head support_fn_list;
+};
+
+/**
+ * struct synaptics_rmi4_data - contains the rmi4 device data
+ * @rmi4_mod_info: structure variable for rmi4 device info
+ * @input_dev: pointer for input device
+ * @i2c_client: pointer for i2c client
+ * @board: constant pointer for touch platform data
+ * @fn_list_mutex: mutex for function list
+ * @rmi4_page_mutex: mutex for rmi4 page
+ * @current_page: variable for integer
+ * @number_of_interrupt_register: interrupt registers count
+ * @fn01_ctrl_base_addr: control base address for fn01
+ * @fn01_query_base_addr: query base address for fn01
+ * @fn01_data_base_addr: data base address for fn01
+ * @sensor_max_x: sensor maximum x value
+ * @sensor_max_y: sensor maximum y value
+ * @regulator: pointer to the regulator structure
+ * @wait: wait queue structure variable
+ * @touch_stopped: flag to stop the thread function
+ * @fingers_supported: maximum supported fingers
+ *
+ * This structure gives the device data information.
+ */
+struct synaptics_rmi4_data {
+ struct synaptics_rmi4_device_info rmi4_mod_info;
+ struct input_dev *input_dev;
+ struct i2c_client *i2c_client;
+ const struct synaptics_rmi4_platform_data *board;
+ struct mutex fn_list_mutex;
+ struct mutex rmi4_page_mutex;
+ int current_page;
+ unsigned int number_of_interrupt_register;
+ unsigned short fn01_ctrl_base_addr;
+ unsigned short fn01_query_base_addr;
+ unsigned short fn01_data_base_addr;
+ int sensor_max_x;
+ int sensor_max_y;
+ struct regulator *regulator;
+ wait_queue_head_t wait;
+ bool touch_stopped;
+ unsigned char fingers_supported;
+};
+
+/**
+ * synaptics_rmi4_set_page() - sets the page
+ * @pdata: pointer to synaptics_rmi4_data structure
+ * @address: set the address of the page
+ *
+ * This function is used to set the page and returns integer.
+ */
+static int synaptics_rmi4_set_page(struct synaptics_rmi4_data *pdata,
+ unsigned int address)
+{
+ unsigned char txbuf[PAGE_LEN];
+ int retval;
+ unsigned int page;
+ struct i2c_client *i2c = pdata->i2c_client;
+
+ page = ((address >> 8) & MASK_8BIT);
+ if (page != pdata->current_page) {
+ txbuf[0] = MASK_8BIT;
+ txbuf[1] = page;
+ retval = i2c_master_send(i2c, txbuf, PAGE_LEN);
+ if (retval != PAGE_LEN)
+ dev_err(&i2c->dev, "failed:%d\n", retval);
+ else
+ pdata->current_page = page;
+ } else
+ retval = PAGE_LEN;
+ return retval;
+}
+/**
+ * synaptics_rmi4_i2c_block_read() - read the block of data
+ * @pdata: pointer to synaptics_rmi4_data structure
+ * @address: read the block of data from this offset
+ * @valp: pointer to a buffer containing the data to be read
+ * @size: number of bytes to read
+ *
+ * This function is to read the block of data and returns integer.
+ */
+static int synaptics_rmi4_i2c_block_read(struct synaptics_rmi4_data *pdata,
+ unsigned short address,
+ unsigned char *valp, int size)
+{
+ int retval = 0;
+ int retry_count = 0;
+ int index;
+ struct i2c_client *i2c = pdata->i2c_client;
+
+ mutex_lock(&(pdata->rmi4_page_mutex));
+ retval = synaptics_rmi4_set_page(pdata, address);
+ if (retval != PAGE_LEN)
+ goto exit;
+ index = address & MASK_8BIT;
+retry:
+ retval = i2c_smbus_read_i2c_block_data(i2c, index, size, valp);
+ if (retval != size) {
+ if (++retry_count == MAX_RETRY_COUNT)
+ dev_err(&i2c->dev,
+ "%s:address 0x%04x size %d failed:%d\n",
+ __func__, address, size, retval);
+ else {
+ synaptics_rmi4_set_page(pdata, address);
+ goto retry;
+ }
+ }
+exit:
+ mutex_unlock(&(pdata->rmi4_page_mutex));
+ return retval;
+}
+
+/**
+ * synaptics_rmi4_i2c_byte_write() - write the single byte data
+ * @pdata: pointer to synaptics_rmi4_data structure
+ * @address: write the block of data from this offset
+ * @data: data to be write
+ *
+ * This function is to write the single byte data and returns integer.
+ */
+static int synaptics_rmi4_i2c_byte_write(struct synaptics_rmi4_data *pdata,
+ unsigned short address,
+ unsigned char data)
+{
+ unsigned char txbuf[2];
+ int retval = 0;
+ struct i2c_client *i2c = pdata->i2c_client;
+
+ /* Can't have anyone else changing the page behind our backs */
+ mutex_lock(&(pdata->rmi4_page_mutex));
+
+ retval = synaptics_rmi4_set_page(pdata, address);
+ if (retval != PAGE_LEN)
+ goto exit;
+ txbuf[0] = address & MASK_8BIT;
+ txbuf[1] = data;
+ retval = i2c_master_send(pdata->i2c_client, txbuf, 2);
+ /* Add in retry on writes only in certain error return values */
+ if (retval != 2) {
+ dev_err(&i2c->dev, "failed:%d\n", retval);
+ retval = -EIO;
+ } else
+ retval = 1;
+exit:
+ mutex_unlock(&(pdata->rmi4_page_mutex));
+ return retval;
+}
+
+/**
+ * synpatics_rmi4_touchpad_report() - reports for the rmi4 touchpad device
+ * @pdata: pointer to synaptics_rmi4_data structure
+ * @rfi: pointer to synaptics_rmi4_fn structure
+ *
+ * This function calls to reports for the rmi4 touchpad device
+ */
+static int synpatics_rmi4_touchpad_report(struct synaptics_rmi4_data *pdata,
+ struct synaptics_rmi4_fn *rfi)
+{
+ /* number of touch points - fingers down in this case */
+ int touch_count = 0;
+ int finger;
+ int finger_registers;
+ int reg;
+ int finger_shift;
+ int finger_status;
+ int retval;
+ int x, y;
+ int wx, wy;
+ unsigned short data_base_addr;
+ unsigned short data_offset;
+ unsigned char data_reg_blk_size;
+ unsigned char values[2];
+ unsigned char data[DATA_LEN];
+ unsigned char fingers_supported = pdata->fingers_supported;
+ struct i2c_client *client = pdata->i2c_client;
+ struct input_dev *input_dev = pdata->input_dev;
+
+ /* get 2D sensor finger data */
+ /*
+ * First get the finger status field - the size of the finger status
+ * field is determined by the number of finger supporte - 2 bits per
+ * finger, so the number of registers to read is:
+ * registerCount = ceil(numberOfFingers/4).
+ * Read the required number of registers and check each 2 bit field to
+ * determine if a finger is down:
+ * 00 = finger not present,
+ * 01 = finger present and data accurate,
+ * 10 = finger present but data may not be accurate,
+ * 11 = reserved for product use.
+ */
+ finger_registers = (fingers_supported + 3)/4;
+ data_base_addr = rfi->fn_desc.data_base_addr;
+ retval = synaptics_rmi4_i2c_block_read(pdata, data_base_addr, values,
+ finger_registers);
+ if (retval != finger_registers) {
+ dev_err(&client->dev, "%s:read status registers failed\n",
+ __func__);
+ return 0;
+ }
+ /*
+ * For each finger present, read the proper number of registers
+ * to get absolute data.
+ */
+ data_reg_blk_size = rfi->size_of_data_register_block;
+ for (finger = 0; finger < fingers_supported; finger++) {
+ /* determine which data byte the finger status is in */
+ reg = finger/4;
+ /* bit shift to get finger's status */
+ finger_shift = (finger % 4) * 2;
+ finger_status = (values[reg] >> finger_shift) & 3;
+ /*
+ * if finger status indicates a finger is present then
+ * read the finger data and report it
+ */
+ input_mt_slot(input_dev, finger);
+ input_mt_report_slot_state(input_dev, MT_TOOL_FINGER,
+ finger_status != 0);
+
+ if (finger_status) {
+ /* Read the finger data */
+ data_offset = data_base_addr +
+ ((finger * data_reg_blk_size) +
+ finger_registers);
+ retval = synaptics_rmi4_i2c_block_read(pdata,
+ data_offset, data,
+ data_reg_blk_size);
+ if (retval != data_reg_blk_size) {
+ dev_err(&client->dev, "%s:read data failed\n",
+ __func__);
+ return 0;
+ }
+ x = (data[0] << 4) | (data[2] & MASK_4BIT);
+ y = (data[1] << 4) | ((data[2] >> 4) & MASK_4BIT);
+ wy = (data[3] >> 4) & MASK_4BIT;
+ wx = (data[3] & MASK_4BIT);
+
+ if (pdata->board->x_flip)
+ x = pdata->sensor_max_x - x;
+ if (pdata->board->y_flip)
+ y = pdata->sensor_max_y - y;
+
+ input_report_abs(input_dev, ABS_MT_TOUCH_MAJOR,
+ max(wx, wy));
+ input_report_abs(input_dev, ABS_MT_POSITION_X, x);
+ input_report_abs(input_dev, ABS_MT_POSITION_Y, y);
+
+ /* number of active touch points */
+ touch_count++;
+ }
+ }
+
+ /* sync after groups of events */
+ input_mt_sync_frame(input_dev);
+ input_sync(input_dev);
+ /* return the number of touch points */
+ return touch_count;
+}
+
+/**
+ * synaptics_rmi4_report_device() - reports the rmi4 device
+ * @pdata: pointer to synaptics_rmi4_data structure
+ * @rfi: pointer to synaptics_rmi4_fn
+ *
+ * This function is used to call the report function of the rmi4 device.
+ */
+static int synaptics_rmi4_report_device(struct synaptics_rmi4_data *pdata,
+ struct synaptics_rmi4_fn *rfi)
+{
+ int touch = 0;
+ struct i2c_client *client = pdata->i2c_client;
+ static int num_error_reports;
+
+ if (rfi->fn_number != SYNAPTICS_RMI4_TOUCHPAD_FUNC_NUM) {
+ num_error_reports++;
+ if (num_error_reports < MAX_ERROR_REPORT)
+ dev_err(&client->dev, "%s:report not supported\n",
+ __func__);
+ } else
+ touch = synpatics_rmi4_touchpad_report(pdata, rfi);
+ return touch;
+}
+/**
+ * synaptics_rmi4_sensor_report() - reports to input subsystem
+ * @pdata: pointer to synaptics_rmi4_data structure
+ *
+ * This function is used to reads in all data sources and reports
+ * them to the input subsystem.
+ */
+static int synaptics_rmi4_sensor_report(struct synaptics_rmi4_data *pdata)
+{
+ unsigned char intr_status[4];
+ /* number of touch points - fingers or buttons */
+ int touch = 0;
+ unsigned int retval;
+ struct synaptics_rmi4_fn *rfi;
+ struct synaptics_rmi4_device_info *rmi;
+ struct i2c_client *client = pdata->i2c_client;
+
+ /*
+ * Get the interrupt status from the function $01
+ * control register+1 to find which source(s) were interrupting
+ * so we can read the data from the source(s) (2D sensor, buttons..)
+ */
+ retval = synaptics_rmi4_i2c_block_read(pdata,
+ pdata->fn01_data_base_addr + 1,
+ intr_status,
+ pdata->number_of_interrupt_register);
+ if (retval != pdata->number_of_interrupt_register) {
+ dev_err(&client->dev,
+ "could not read interrupt status registers\n");
+ return 0;
+ }
+ /*
+ * check each function that has data sources and if the interrupt for
+ * that triggered then call that RMI4 functions report() function to
+ * gather data and report it to the input subsystem
+ */
+ rmi = &(pdata->rmi4_mod_info);
+ list_for_each_entry(rfi, &rmi->support_fn_list, link) {
+ if (rfi->num_of_data_sources) {
+ if (intr_status[rfi->index_to_intr_reg] &
+ rfi->intr_mask)
+ touch = synaptics_rmi4_report_device(pdata,
+ rfi);
+ }
+ }
+ /* return the number of touch points */
+ return touch;
+}
+
+/**
+ * synaptics_rmi4_irq() - thread function for rmi4 attention line
+ * @irq: irq value
+ * @data: void pointer
+ *
+ * This function is interrupt thread function. It just notifies the
+ * application layer that attention is required.
+ */
+static irqreturn_t synaptics_rmi4_irq(int irq, void *data)
+{
+ struct synaptics_rmi4_data *pdata = data;
+ int touch_count;
+
+ do {
+ touch_count = synaptics_rmi4_sensor_report(pdata);
+ if (touch_count)
+ wait_event_timeout(pdata->wait, pdata->touch_stopped,
+ msecs_to_jiffies(1));
+ else
+ break;
+ } while (!pdata->touch_stopped);
+ return IRQ_HANDLED;
+}
+
+/**
+ * synpatics_rmi4_touchpad_detect() - detects the rmi4 touchpad device
+ * @pdata: pointer to synaptics_rmi4_data structure
+ * @rfi: pointer to synaptics_rmi4_fn structure
+ * @fd: pointer to synaptics_rmi4_fn_desc structure
+ * @interruptcount: count the number of interrupts
+ *
+ * This function calls to detects the rmi4 touchpad device
+ */
+static int synpatics_rmi4_touchpad_detect(struct synaptics_rmi4_data *pdata,
+ struct synaptics_rmi4_fn *rfi,
+ struct synaptics_rmi4_fn_desc *fd,
+ unsigned int interruptcount)
+{
+ unsigned char queries[QUERY_LEN];
+ unsigned short intr_offset;
+ unsigned char abs_data_size;
+ unsigned char abs_data_blk_size;
+ unsigned char egr_0, egr_1;
+ unsigned int all_data_blk_size;
+ int has_pinch, has_flick, has_tap;
+ int has_tapandhold, has_doubletap;
+ int has_earlytap, has_press;
+ int has_palmdetect, has_rotate;
+ int has_rel;
+ int i;
+ int retval;
+ struct i2c_client *client = pdata->i2c_client;
+
+ rfi->fn_desc.query_base_addr = fd->query_base_addr;
+ rfi->fn_desc.data_base_addr = fd->data_base_addr;
+ rfi->fn_desc.intr_src_count = fd->intr_src_count;
+ rfi->fn_desc.fn_number = fd->fn_number;
+ rfi->fn_number = fd->fn_number;
+ rfi->num_of_data_sources = fd->intr_src_count;
+ rfi->fn_desc.ctrl_base_addr = fd->ctrl_base_addr;
+ rfi->fn_desc.cmd_base_addr = fd->cmd_base_addr;
+
+ /*
+ * need to get number of fingers supported, data size, etc.
+ * to be used when getting data since the number of registers to
+ * read depends on the number of fingers supported and data size.
+ */
+ retval = synaptics_rmi4_i2c_block_read(pdata, fd->query_base_addr,
+ queries,
+ sizeof(queries));
+ if (retval != sizeof(queries)) {
+ dev_err(&client->dev, "%s:read function query registers\n",
+ __func__);
+ return retval;
+ }
+ /*
+ * 2D data sources have only 3 bits for the number of fingers
+ * supported - so the encoding is a bit weird.
+ */
+ if ((queries[1] & MASK_3BIT) <= 4)
+ /* add 1 since zero based */
+ rfi->num_of_data_points = (queries[1] & MASK_3BIT) + 1;
+ else {
+ /*
+ * a value of 5 is up to 10 fingers - 6 and 7 are reserved
+ * (shouldn't get these i int retval;n a normal 2D source).
+ */
+ if ((queries[1] & MASK_3BIT) == 5)
+ rfi->num_of_data_points = 10;
+ }
+ pdata->fingers_supported = rfi->num_of_data_points;
+ /* Need to get interrupt info for handling interrupts */
+ rfi->index_to_intr_reg = (interruptcount + 7)/8;
+ if (rfi->index_to_intr_reg != 0)
+ rfi->index_to_intr_reg -= 1;
+ /*
+ * loop through interrupts for each source in fn $11
+ * and or in a bit to the interrupt mask for each.
+ */
+ intr_offset = interruptcount % 8;
+ rfi->intr_mask = 0;
+ for (i = intr_offset;
+ i < ((fd->intr_src_count & MASK_3BIT) + intr_offset); i++)
+ rfi->intr_mask |= 1 << i;
+
+ /* Size of just the absolute data for one finger */
+ abs_data_size = queries[5] & MASK_2BIT;
+ /* One each for X and Y, one for LSB for X & Y, one for W, one for Z */
+ abs_data_blk_size = 3 + (2 * (abs_data_size == 0 ? 1 : 0));
+ rfi->size_of_data_register_block = abs_data_blk_size;
+
+ /*
+ * need to determine the size of data to read - this depends on
+ * conditions such as whether Relative data is reported and if Gesture
+ * data is reported.
+ */
+ egr_0 = queries[7];
+ egr_1 = queries[8];
+
+ /*
+ * Get info about what EGR data is supported, whether it has
+ * Relative data supported, etc.
+ */
+ has_pinch = egr_0 & HAS_PINCH;
+ has_flick = egr_0 & HAS_FLICK;
+ has_tap = egr_0 & HAS_TAP;
+ has_earlytap = egr_0 & HAS_EARLYTAP;
+ has_press = egr_0 & HAS_PRESS;
+ has_rotate = egr_1 & HAS_ROTATE;
+ has_rel = queries[1] & HAS_RELEASE;
+ has_tapandhold = egr_0 & HAS_TAPANDHOLD;
+ has_doubletap = egr_0 & HAS_DOUBLETAP;
+ has_palmdetect = egr_1 & HAS_PALMDETECT;
+
+ /*
+ * Size of all data including finger status, absolute data for each
+ * finger, relative data and EGR data
+ */
+ all_data_blk_size =
+ /* finger status, four fingers per register */
+ ((rfi->num_of_data_points + 3) / 4) +
+ /* absolute data, per finger times number of fingers */
+ (abs_data_blk_size * rfi->num_of_data_points) +
+ /*
+ * two relative registers (if relative is being reported)
+ */
+ 2 * has_rel +
+ /*
+ * F11_2D_data8 is only present if the egr_0
+ * register is non-zero.
+ */
+ !!(egr_0) +
+ /*
+ * F11_2D_data9 is only present if either egr_0 or
+ * egr_1 registers are non-zero.
+ */
+ (egr_0 || egr_1) +
+ /*
+ * F11_2D_data10 is only present if EGR_PINCH or EGR_FLICK of
+ * egr_0 reports as 1.
+ */
+ !!(has_pinch | has_flick) +
+ /*
+ * F11_2D_data11 and F11_2D_data12 are only present if
+ * EGR_FLICK of egr_0 reports as 1.
+ */
+ 2 * !!(has_flick);
+ return retval;
+}
+
+/**
+ * synaptics_rmi4_touchpad_config() - configures the rmi4 touchpad device
+ * @pdata: pointer to synaptics_rmi4_data structure
+ * @rfi: pointer to synaptics_rmi4_fn structure
+ *
+ * This function calls to configures the rmi4 touchpad device
+ */
+static int synaptics_rmi4_touchpad_config(struct synaptics_rmi4_data *pdata,
+ struct synaptics_rmi4_fn *rfi)
+{
+ /*
+ * For the data source - print info and do any
+ * source specific configuration.
+ */
+ unsigned char data[BUF_LEN];
+ int retval = 0;
+ struct i2c_client *client = pdata->i2c_client;
+
+ /* Get and print some info about the data source... */
+ /* To Query 2D devices we need to read from the address obtained
+ * from the function descriptor stored in the RMI function info.
+ */
+ retval = synaptics_rmi4_i2c_block_read(pdata,
+ rfi->fn_desc.query_base_addr,
+ data, QUERY_LEN);
+ if (retval != QUERY_LEN)
+ dev_err(&client->dev, "%s:read query registers failed\n",
+ __func__);
+ else {
+ retval = synaptics_rmi4_i2c_block_read(pdata,
+ rfi->fn_desc.ctrl_base_addr,
+ data, DATA_BUF_LEN);
+ if (retval != DATA_BUF_LEN) {
+ dev_err(&client->dev,
+ "%s:read control registers failed\n",
+ __func__);
+ return retval;
+ }
+ /* Store these for use later*/
+ pdata->sensor_max_x = ((data[6] & MASK_8BIT) << 0) |
+ ((data[7] & MASK_4BIT) << 8);
+ pdata->sensor_max_y = ((data[8] & MASK_5BIT) << 0) |
+ ((data[9] & MASK_4BIT) << 8);
+ }
+ return retval;
+}
+
+/**
+ * synaptics_rmi4_i2c_query_device() - query the rmi4 device
+ * @pdata: pointer to synaptics_rmi4_data structure
+ *
+ * This function is used to query the rmi4 device.
+ */
+static int synaptics_rmi4_i2c_query_device(struct synaptics_rmi4_data *pdata)
+{
+ int i;
+ int retval;
+ unsigned char std_queries[STD_QUERY_LEN];
+ unsigned char intr_count = 0;
+ int data_sources = 0;
+ unsigned int ctrl_offset;
+ struct synaptics_rmi4_fn *rfi;
+ struct synaptics_rmi4_fn_desc rmi_fd;
+ struct synaptics_rmi4_device_info *rmi;
+ struct i2c_client *client = pdata->i2c_client;
+
+ /*
+ * init the physical drivers RMI module
+ * info list of functions
+ */
+ INIT_LIST_HEAD(&pdata->rmi4_mod_info.support_fn_list);
+
+ /*
+ * Read the Page Descriptor Table to determine what functions
+ * are present
+ */
+ for (i = PDT_START_SCAN_LOCATION; i > PDT_END_SCAN_LOCATION;
+ i -= PDT_ENTRY_SIZE) {
+ retval = synaptics_rmi4_i2c_block_read(pdata, i,
+ (unsigned char *)&rmi_fd,
+ sizeof(rmi_fd));
+ if (retval != sizeof(rmi_fd)) {
+ /* failed to read next PDT entry */
+ dev_err(&client->dev, "%s: read error\n", __func__);
+ return -EIO;
+ }
+ rfi = NULL;
+ if (rmi_fd.fn_number) {
+ switch (rmi_fd.fn_number & MASK_8BIT) {
+ case SYNAPTICS_RMI4_DEVICE_CONTROL_FUNC_NUM:
+ pdata->fn01_query_base_addr =
+ rmi_fd.query_base_addr;
+ pdata->fn01_ctrl_base_addr =
+ rmi_fd.ctrl_base_addr;
+ pdata->fn01_data_base_addr =
+ rmi_fd.data_base_addr;
+ break;
+ case SYNAPTICS_RMI4_TOUCHPAD_FUNC_NUM:
+ if (rmi_fd.intr_src_count) {
+ rfi = kmalloc(sizeof(*rfi),
+ GFP_KERNEL);
+ if (!rfi)
+ return -ENOMEM;
+ retval = synpatics_rmi4_touchpad_detect
+ (pdata, rfi,
+ &rmi_fd,
+ intr_count);
+ if (retval < 0) {
+ kfree(rfi);
+ return retval;
+ }
+ }
+ break;
+ }
+ /* interrupt count for next iteration */
+ intr_count += (rmi_fd.intr_src_count & MASK_3BIT);
+ /*
+ * We only want to add functions to the list
+ * that have data associated with them.
+ */
+ if (rfi && rmi_fd.intr_src_count) {
+ /* link this function info to the RMI module */
+ mutex_lock(&(pdata->fn_list_mutex));
+ list_add_tail(&rfi->link,
+ &pdata->rmi4_mod_info.support_fn_list);
+ mutex_unlock(&(pdata->fn_list_mutex));
+ }
+ } else {
+ /*
+ * A zero in the function number
+ * signals the end of the PDT
+ */
+ dev_dbg(&client->dev,
+ "%s:end of PDT\n", __func__);
+ break;
+ }
+ }
+ /*
+ * calculate the interrupt register count - used in the
+ * ISR to read the correct number of interrupt registers
+ */
+ pdata->number_of_interrupt_register = (intr_count + 7) / 8;
+ /*
+ * Function $01 will be used to query the product properties,
+ * and product ID so we had to read the PDT above first to get
+ * the Fn $01 query address and prior to filling in the product
+ * info. NOTE: Even an unflashed device will still have FN $01.
+ */
+
+ /* Load up the standard queries and get the RMI4 module info */
+ retval = synaptics_rmi4_i2c_block_read(pdata,
+ pdata->fn01_query_base_addr,
+ std_queries,
+ sizeof(std_queries));
+ if (retval != sizeof(std_queries)) {
+ dev_err(&client->dev, "%s:Failed reading queries\n",
+ __func__);
+ return -EIO;
+ }
+
+ /* Currently supported RMI version is 4.0 */
+ pdata->rmi4_mod_info.version_major = 4;
+ pdata->rmi4_mod_info.version_minor = 0;
+ /*
+ * get manufacturer id, product_props, product info,
+ * date code, tester id, serial num and product id (name)
+ */
+ pdata->rmi4_mod_info.manufacturer_id = std_queries[0];
+ pdata->rmi4_mod_info.product_props = std_queries[1];
+ pdata->rmi4_mod_info.product_info[0] = std_queries[2];
+ pdata->rmi4_mod_info.product_info[1] = std_queries[3];
+ /* year - 2001-2032 */
+ pdata->rmi4_mod_info.date_code[0] = std_queries[4] & MASK_5BIT;
+ /* month - 1-12 */
+ pdata->rmi4_mod_info.date_code[1] = std_queries[5] & MASK_4BIT;
+ /* day - 1-31 */
+ pdata->rmi4_mod_info.date_code[2] = std_queries[6] & MASK_5BIT;
+ pdata->rmi4_mod_info.tester_id = ((std_queries[7] & MASK_7BIT) << 8) |
+ (std_queries[8] & MASK_7BIT);
+ pdata->rmi4_mod_info.serial_number =
+ ((std_queries[9] & MASK_7BIT) << 8) |
+ (std_queries[10] & MASK_7BIT);
+ memcpy(pdata->rmi4_mod_info.product_id_string, &std_queries[11], 10);
+
+ /* Check if this is a Synaptics device - report if not. */
+ if (pdata->rmi4_mod_info.manufacturer_id != 1)
+ dev_err(&client->dev, "non-Synaptics mfg id:%d\n",
+ pdata->rmi4_mod_info.manufacturer_id);
+
+ list_for_each_entry(rfi, &pdata->rmi4_mod_info.support_fn_list, link)
+ data_sources += rfi->num_of_data_sources;
+ if (data_sources) {
+ rmi = &(pdata->rmi4_mod_info);
+ list_for_each_entry(rfi, &rmi->support_fn_list, link) {
+ if (rfi->num_of_data_sources) {
+ if (rfi->fn_number ==
+ SYNAPTICS_RMI4_TOUCHPAD_FUNC_NUM) {
+ retval = synaptics_rmi4_touchpad_config
+ (pdata, rfi);
+ if (retval < 0)
+ return retval;
+ } else
+ dev_err(&client->dev,
+ "%s:fn_number not supported\n",
+ __func__);
+ /*
+ * Turn on interrupts for this
+ * function's data sources.
+ */
+ ctrl_offset = pdata->fn01_ctrl_base_addr + 1 +
+ rfi->index_to_intr_reg;
+ retval = synaptics_rmi4_i2c_byte_write(pdata,
+ ctrl_offset,
+ rfi->intr_mask);
+ if (retval < 0)
+ return retval;
+ }
+ }
+ }
+ return 0;
+}
+
+/*
+ * Descriptor structure.
+ * Describes the number of i2c devices on the bus that speak RMI.
+ */
+static struct synaptics_rmi4_platform_data synaptics_rmi4_platformdata = {
+ .irq_type = (IRQF_TRIGGER_FALLING | IRQF_SHARED),
+ .x_flip = false,
+ .y_flip = true,
+};
+
+/**
+ * synaptics_rmi4_probe() - Initialze the i2c-client touchscreen driver
+ * @i2c: i2c client structure pointer
+ * @id:i2c device id pointer
+ *
+ * This function will allocate and initialize the instance
+ * data and request the irq and set the instance data as the clients
+ * platform data then register the physical driver which will do a scan of
+ * the rmi4 Physical Device Table and enumerate any rmi4 functions that
+ * have data sources associated with them.
+ */
+static int synaptics_rmi4_probe
+ (struct i2c_client *client, const struct i2c_device_id *dev_id)
+{
+ int retval;
+ unsigned char intr_status[4];
+ struct synaptics_rmi4_data *rmi4_data;
+ const struct synaptics_rmi4_platform_data *platformdata =
+ client->dev.platform_data;
+
+ if (!i2c_check_functionality(client->adapter,
+ I2C_FUNC_SMBUS_BYTE_DATA)) {
+ dev_err(&client->dev, "i2c smbus byte data not supported\n");
+ return -EIO;
+ }
+
+ if (!platformdata)
+ platformdata = &synaptics_rmi4_platformdata;
+
+ /* Allocate and initialize the instance data for this client */
+ rmi4_data = kcalloc(2, sizeof(struct synaptics_rmi4_data),
+ GFP_KERNEL);
+ if (!rmi4_data)
+ return -ENOMEM;
+
+ rmi4_data->input_dev = input_allocate_device();
+ if (rmi4_data->input_dev == NULL) {
+ retval = -ENOMEM;
+ goto err_input;
+ }
+
+ rmi4_data->regulator = regulator_get(&client->dev, "vdd");
+ if (IS_ERR(rmi4_data->regulator)) {
+ dev_err(&client->dev, "%s:get regulator failed\n",
+ __func__);
+ retval = PTR_ERR(rmi4_data->regulator);
+ goto err_get_regulator;
+ }
+ retval = regulator_enable(rmi4_data->regulator);
+ if (retval < 0) {
+ dev_err(&client->dev, "%s:regulator enable failed\n",
+ __func__);
+ goto err_regulator_enable;
+ }
+ init_waitqueue_head(&rmi4_data->wait);
+ /*
+ * Copy i2c_client pointer into RTID's i2c_client pointer for
+ * later use in rmi4_read, rmi4_write, etc.
+ */
+ rmi4_data->i2c_client = client;
+ /* So we set the page correctly the first time */
+ rmi4_data->current_page = MASK_16BIT;
+ rmi4_data->board = platformdata;
+ rmi4_data->touch_stopped = false;
+
+ /* init the mutexes for maintain the lists */
+ mutex_init(&(rmi4_data->fn_list_mutex));
+ mutex_init(&(rmi4_data->rmi4_page_mutex));
+
+ /*
+ * Register physical driver - this will call the detect function that
+ * will then scan the device and determine the supported
+ * rmi4 functions.
+ */
+ retval = synaptics_rmi4_i2c_query_device(rmi4_data);
+ if (retval) {
+ dev_err(&client->dev, "%s: rmi4 query device failed\n",
+ __func__);
+ goto err_query_dev;
+ }
+
+ /* Store the instance data in the i2c_client */
+ i2c_set_clientdata(client, rmi4_data);
+
+ /*initialize the input device parameters */
+ rmi4_data->input_dev->name = DRIVER_NAME;
+ rmi4_data->input_dev->phys = "Synaptics_Clearpad";
+ rmi4_data->input_dev->id.bustype = BUS_I2C;
+ rmi4_data->input_dev->dev.parent = &client->dev;
+ input_set_drvdata(rmi4_data->input_dev, rmi4_data);
+
+ /* Initialize the function handlers for rmi4 */
+ set_bit(EV_SYN, rmi4_data->input_dev->evbit);
+ set_bit(EV_KEY, rmi4_data->input_dev->evbit);
+ set_bit(EV_ABS, rmi4_data->input_dev->evbit);
+
+ input_set_abs_params(rmi4_data->input_dev, ABS_MT_POSITION_X, 0,
+ rmi4_data->sensor_max_x, 0, 0);
+ input_set_abs_params(rmi4_data->input_dev, ABS_MT_POSITION_Y, 0,
+ rmi4_data->sensor_max_y, 0, 0);
+ input_set_abs_params(rmi4_data->input_dev, ABS_MT_TOUCH_MAJOR, 0,
+ MAX_TOUCH_MAJOR, 0, 0);
+ input_mt_init_slots(rmi4_data->input_dev,
+ rmi4_data->fingers_supported, 0);
+
+ /* Clear interrupts */
+ synaptics_rmi4_i2c_block_read(rmi4_data,
+ rmi4_data->fn01_data_base_addr + 1, intr_status,
+ rmi4_data->number_of_interrupt_register);
+ retval = request_threaded_irq(client->irq, NULL,
+ synaptics_rmi4_irq,
+ platformdata->irq_type,
+ DRIVER_NAME, rmi4_data);
+ if (retval) {
+ dev_err(&client->dev, "Unable to get attn irq %d\n",
+ client->irq);
+ goto err_query_dev;
+ }
+
+ retval = input_register_device(rmi4_data->input_dev);
+ if (retval) {
+ dev_err(&client->dev, "%s:input register failed\n", __func__);
+ goto err_free_irq;
+ }
+
+ return retval;
+
+err_free_irq:
+ free_irq(client->irq, rmi4_data);
+err_query_dev:
+ regulator_disable(rmi4_data->regulator);
+err_regulator_enable:
+ regulator_put(rmi4_data->regulator);
+err_get_regulator:
+ input_free_device(rmi4_data->input_dev);
+ rmi4_data->input_dev = NULL;
+err_input:
+ kfree(rmi4_data);
+
+ return retval;
+}
+/**
+ * synaptics_rmi4_remove() - Removes the i2c-client touchscreen driver
+ * @client: i2c client structure pointer
+ *
+ * This function uses to remove the i2c-client
+ * touchscreen driver and returns integer.
+ */
+static int synaptics_rmi4_remove(struct i2c_client *client)
+{
+ struct synaptics_rmi4_data *rmi4_data = i2c_get_clientdata(client);
+
+ rmi4_data->touch_stopped = true;
+ wake_up(&rmi4_data->wait);
+ free_irq(client->irq, rmi4_data);
+ input_unregister_device(rmi4_data->input_dev);
+ regulator_disable(rmi4_data->regulator);
+ regulator_put(rmi4_data->regulator);
+ kfree(rmi4_data);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+/**
+ * synaptics_rmi4_suspend() - suspend the touch screen controller
+ * @dev: pointer to device structure
+ *
+ * This function is used to suspend the
+ * touch panel controller and returns integer
+ */
+static int synaptics_rmi4_suspend(struct device *dev)
+{
+ /* Touch sleep mode */
+ int retval;
+ unsigned char intr_status;
+ struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(dev);
+
+ rmi4_data->touch_stopped = true;
+ disable_irq(rmi4_data->i2c_client->irq);
+
+ retval = synaptics_rmi4_i2c_block_read(rmi4_data,
+ rmi4_data->fn01_data_base_addr + 1,
+ &intr_status,
+ rmi4_data->number_of_interrupt_register);
+ if (retval < 0)
+ return retval;
+
+ retval = synaptics_rmi4_i2c_byte_write(rmi4_data,
+ rmi4_data->fn01_ctrl_base_addr + 1,
+ (intr_status & ~TOUCHPAD_CTRL_INTR));
+ if (retval < 0)
+ return retval;
+
+ regulator_disable(rmi4_data->regulator);
+
+ return 0;
+}
+/**
+ * synaptics_rmi4_resume() - resume the touch screen controller
+ * @dev: pointer to device structure
+ *
+ * This function is used to resume the touch panel
+ * controller and returns integer.
+ */
+static int synaptics_rmi4_resume(struct device *dev)
+{
+ int retval;
+ unsigned char intr_status;
+ struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(dev);
+
+ retval = regulator_enable(rmi4_data->regulator);
+ if (retval) {
+ dev_err(dev, "Regulator enable failed (%d)\n", retval);
+ return retval;
+ }
+
+ enable_irq(rmi4_data->i2c_client->irq);
+ rmi4_data->touch_stopped = false;
+
+ retval = synaptics_rmi4_i2c_block_read(rmi4_data,
+ rmi4_data->fn01_data_base_addr + 1,
+ &intr_status,
+ rmi4_data->number_of_interrupt_register);
+ if (retval < 0)
+ return retval;
+
+ retval = synaptics_rmi4_i2c_byte_write(rmi4_data,
+ rmi4_data->fn01_ctrl_base_addr + 1,
+ (intr_status | TOUCHPAD_CTRL_INTR));
+ if (retval < 0)
+ return retval;
+
+ return 0;
+}
+
+#endif
+
+static SIMPLE_DEV_PM_OPS(synaptics_rmi4_dev_pm_ops, synaptics_rmi4_suspend,
+ synaptics_rmi4_resume);
+
+static const struct i2c_device_id synaptics_rmi4_id_table[] = {
+ { DRIVER_NAME, 0 },
+ { },
+};
+MODULE_DEVICE_TABLE(i2c, synaptics_rmi4_id_table);
+
+static struct i2c_driver synaptics_rmi4_driver = {
+ .driver = {
+ .name = DRIVER_NAME,
+ .owner = THIS_MODULE,
+ .pm = &synaptics_rmi4_dev_pm_ops,
+ },
+ .probe = synaptics_rmi4_probe,
+ .remove = synaptics_rmi4_remove,
+ .id_table = synaptics_rmi4_id_table,
+};
+
+module_i2c_driver(synaptics_rmi4_driver);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("naveen.gaddipati@stericsson.com, js.ha@stericsson.com");
+MODULE_DESCRIPTION("synaptics rmi4 i2c touch Driver");
+MODULE_ALIAS("i2c:synaptics_rmi4_ts");
diff --git a/drivers/staging/ste_rmi4/synaptics_i2c_rmi4.h b/drivers/staging/ste_rmi4/synaptics_i2c_rmi4.h
new file mode 100644
index 000000000..8c9166ba7
--- /dev/null
+++ b/drivers/staging/ste_rmi4/synaptics_i2c_rmi4.h
@@ -0,0 +1,46 @@
+/**
+ *
+ * Synaptics Register Mapped Interface (RMI4) I2C Physical Layer Driver.
+ * Copyright (c) 2007-2010, Synaptics Incorporated
+ *
+ * Author: Js HA <js.ha@stericsson.com> for ST-Ericsson
+ * Author: Naveen Kumar G <naveen.gaddipati@stericsson.com> for ST-Ericsson
+ * Copyright 2010 (c) ST-Ericsson AB
+ */
+/*
+ * This file is licensed under the GPL2 license.
+ *
+ *#############################################################################
+ * GPL
+ *
+ * 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.
+ *
+ * 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.
+ *
+ *#############################################################################
+ */
+
+#ifndef _SYNAPTICS_RMI4_H_INCLUDED_
+#define _SYNAPTICS_RMI4_H_INCLUDED_
+
+/**
+ * struct synaptics_rmi4_platform_data - contains the rmi4 platform data
+ * @irq_number: irq number
+ * @irq_type: irq type
+ * @x flip: x flip flag
+ * @y flip: y flip flag
+ *
+ * This structure gives platform data for rmi4.
+ */
+struct synaptics_rmi4_platform_data {
+ int irq_type;
+ bool x_flip;
+ bool y_flip;
+};
+
+#endif
diff --git a/drivers/staging/unisys/Documentation/ABI/sysfs-platform-visorchipset b/drivers/staging/unisys/Documentation/ABI/sysfs-platform-visorchipset
new file mode 100644
index 000000000..28f8f1233
--- /dev/null
+++ b/drivers/staging/unisys/Documentation/ABI/sysfs-platform-visorchipset
@@ -0,0 +1,101 @@
+What: install/error
+Date: 7/18/2014
+KernelVersion: TBD
+Contact: sparmaintainer@unisys.com
+Description: used to send the ID of a string that should be displayed on
+ s-Par's automatic installation progress screen when an error
+ is encountered during installation. This field has no effect
+ if not in installation mode.
+Users: sparmaintainer@unisys.com
+
+What: install/remainingsteps
+Date: 7/18/2014
+KernelVersion: TBD
+Contact: sparmaintainer@unisys.com
+Description: used to set the value of the progress bar on the s-Par automatic
+ installation progress screen. This field has no effect if not in
+ installation mode.
+Users: sparmaintainer@unisys.com
+
+What: install/textid
+Date: 7/18/2014
+KernelVersion: TBD
+Contact: sparmaintainer@unisys.com
+Description: used to send the ID of a string that should be displayed on
+ s-Par's automatic installation progress screen. Setting this
+ field when not in installation mode (boottotool was set on
+ the previous guest boot) has no effect.
+Users: sparmaintainer@unisys.com
+
+What: install/boottotool
+Date: 7/18/2014
+KernelVersion: TBD
+Contact: sparmaintainer@unisys.com
+Description: The boottotool flag controls s-Par behavior on the next boot of
+ this guest. Setting the flag will cause the guest to boot from
+ the utility and installation image, which will use the value in
+ the toolaction field to determine what operation is being
+ requested.
+Users: sparmaintainer@unisys.com
+
+What: install/toolaction
+Date: 7/18/2014
+KernelVersion: TBD
+Contact: sparmaintainer@unisys.com
+Description: This field is used to tell s-Par which type of recovery tool
+ action to perform on the next guest boot-up. The meaning of the
+ value is dependent on the type of installation software used to
+ commission the guest.
+Users: sparmaintainer@unisys.com
+
+What: guest/chipsetready
+Date: 7/18/2014
+KernelVersion: TBD
+Contact: sparmaintainer@unisys.com
+Description: This entry is used by Unisys application software on the guest
+ to acknowledge completion of specific events for integration
+ purposes, but these acknowledgements are not required for the
+ guest to operate correctly. The interface accepts one of two
+ strings: MODULES_LOADED to indicate that the s-Par driver
+ modules have been loaded successfully, or CALLHOMEDISK_MOUNTED,
+ which indicates that the disk used to support call home services
+ has been successfully mounted.
+Users: sparmaintainer@unisys.com
+
+What: parahotplug/deviceenabled
+Date: 7/18/2014
+KernelVersion: TBD
+Contact: sparmaintainer@unisys.com
+Description: This entry is used by a Unisys support script installed on the
+ guest, and triggered by a udev event. The support script is
+ responsible for enabling and disabling SR-IOV devices when the
+ PF device is being recovered in another guest.
+
+ Some SR-IOV devices have problems when the PF is reset without
+ first disabling all VFs attached to that PF. s-Par handles this
+ situation by sending a message to guests using these VFs, and
+ the script will disable the device. When the PF is recovered,
+ another message is sent to the guests to re-enable the VFs.
+
+ The parahotplug/deviceenabled interface is used to acknowledge
+ the recovery message.
+Users: sparmaintainer@unisys.com
+
+What: parahotplug/devicedisabled
+Date: 7/18/2014
+KernelVersion: TBD
+Contact: sparmaintainer@unisys.com
+Description: This entry is used by a Unisys support script installed on the
+ guest, and triggered by a udev event. The support script is
+ responsible for enabling and disabling SR-IOV devices when the
+ PF device is being recovered in another guest.
+
+ Some SR-IOV devices have problems when the PF is reset without
+ first disabling all VFs attached to that PF. s-Par handles this
+ situation by sending a message to guests using these VFs, and
+ the script will disable the device. When the PF is recovered,
+ another message is sent to the guests to re-enable the VFs.
+
+ The parahotplug/devicedisaabled interface is used to acknowledge
+ the initial recovery message.
+Users: sparmaintainer@unisys.com
diff --git a/drivers/staging/unisys/Documentation/overview.txt b/drivers/staging/unisys/Documentation/overview.txt
new file mode 100644
index 000000000..8d078e4de
--- /dev/null
+++ b/drivers/staging/unisys/Documentation/overview.txt
@@ -0,0 +1,174 @@
+
+Overview
+
+This document describes the driver set for Unisys Secure Partitioning (s-Par®).
+
+s-Par is firmware that provides hardware partitioning capabilities for
+splitting large-scale Intel x86 servers into multiple isolated
+partitions. s-Par provides a set of para-virtualized device drivers to
+allow guest partitions on the same server to share devices that would
+normally be unsharable; specifically, PCI network interfaces and host
+bus adapters that do not support shared access via SR-IOV. The shared
+device is owned and managed by a small, single-purpose service
+partition, which communicates with each guest partition sharing that
+device through an area of shared memory called a channel. Additional
+drivers provide support interfaces for communicating with s-Par
+services, logging and diagnostics, and accessing the Linux console
+from the s-Par user interface.
+
+The driver stack consists of a set of support modules, a set of bus
+modules, and a set of device driver modules. The support modules
+handle a number of common functions across each of the other
+drivers. The bus modules provide organization for the device driver
+modules, which provide the shared device functionality.
+
+These drivers are for the Unisys virtual PCI hardware model where the
+hypervisor need not intervene (other than normal interrupt handling)
+in the interactions between the client drivers and the virtual adapter
+firmware in the adapter service partition.
+
+Driver Descriptions
+
+Device Modules
+
+The modules in this section handle shared devices and the virtual
+buses required to support them. These modules use functions in and
+depend on the modules described in the support modules section.
+
+visorchipset
+
+The visorchipset module receives device creation and destruction
+events from the Command service partition of s-Par, as well as
+controlling registration of shared device drivers with the s-Par
+driver core. The events received are used to populate other s-Par
+modules with their assigned shared devices. Visorchipset is required
+for shared device drivers to function properly. Visorchipset also
+stores information for handling dump disk device creation during
+kdump.
+
+In operation, the visorchipset module processes device creation and
+destruction messages sent by s-Par's Command service partition through
+a channel. These messages result in creation (or destruction) of each
+virtual bus and virtual device. Each bus and device is also associated
+with a communication channel, which is used to communicate with one or
+more IO service partitions to perform device IO on behalf of the
+guest.
+
+virthba
+
+The virthba module provides access to a shared SCSI host bus adapter
+and one or more disk devices, by proxying SCSI commands between the
+guest and the service partition that owns the shared SCSI adapter,
+using a channel between the guest and the service partition. The disks
+that appear on the shared bus are defined by the s-Par configuration
+and enforced by the service partition, while the guest driver handles
+sending commands and handling responses. Each disk is shared as a
+whole to a guest. Sharing the bus adapter in this way provides
+resiliency; should the device encounter an error, only the service
+partition is rebooted, and the device is reinitialized. This allows
+guests to continue running and to recover from the error.
+
+virtnic
+
+The virtnic module provides a paravirtualized network interface to a
+guest by proxying buffer information between the guest and the service
+partition that owns the shared network interface, using a channel
+between the guest and the service partition. The connectivity of this
+interface with the shared interface and possibly other guest
+partitions is defined by the s-Par configuration and enforced by the
+service partition; the guest driver handles communication and link
+status.
+
+visorserial
+
+The visorserial module allows the console of the linux guest to be
+accessed via the s-Par console serial channel. It creates devices in
+/dev/visorserialclientX which behave like a serial terminal and are
+connected to the diagnostics system in s-Par. By assigning a getty to
+the terminal in the guest, a user could log into and access the guest
+from the s-Par diagnostics SWITCH RUN terminal.
+
+visorbus
+
+The visorbus module handles the bus functions for most functional
+drivers except visorserial, visordiag, virthba, and virtnic. It
+maintains the sysfs subtree /sys/devices/visorbus*/. It is responsible
+for device creation and destruction of the devices on its bus.
+
+visorclientbus
+
+The visorclientbus module forwards the bus functions for virthba, and
+virtnic to the virtpci driver.
+
+virtpci
+
+The virtpci module handles the bus functions for virthba, and virtnic.
+
+s-Par Integration Modules
+
+The modules in this section provide integration with s-Par guest
+partition services like diagnostics and remote desktop. These modules
+depend on functions in the modules described in the support modules
+section.
+
+visorvideoclient
+
+The visorvideoclient module provides functionality for video support
+for the Unisys s-Par Partition Desktop application. The guest OS must
+also have the UEFI GOP protocol enabled for the partition desktop to
+function. visorconinclient The visorconinclient module provides
+keyboard and mouse support for the Unisys s-Par Partition Desktop
+application.
+
+sparstop
+
+The sparstop module handles requests from the Unisys s-Par platform to
+shutdown the linux guest. It allows a program on the guest to perform
+clean-up functions on the guest before the guest is shut down or
+rebooted using ACPI.
+
+visordiag
+
+This driver provides the ability for the guest to write information
+into the s-Par diagnostics subsystem. It creates a set of devices
+named /dev/visordiag.X which can be written to by the guest to add
+text to the s-Par system log.
+
+Support Modules
+
+The modules described in this section provide functions and
+abstractions to support the modules described in the previous
+sections, to avoid having duplicated functionality.
+
+visornoop
+
+The visornoop module is a placeholder that responds to device
+create/destroy messages that are currently not in use by linux guests.
+
+visoruislib
+
+The visoruislib module is a support library, used to handle requests
+from virtpci.
+
+visorchannelstub
+
+The visorchannelstub module provides support routines for storing and
+retrieving data from a channel.
+
+visorchannel
+
+The visorchannel module is a support library that abstracts reading
+and writing a channel in memory.
+
+visorutil
+
+The visorutil module is a support library required by all other s-Par
+driver modules. Among its features it abstracts reading, writing, and
+manipulating a block of memory.
+
+Minimum Required Driver Set
+
+The drivers required to boot a Linux guest are visorchipset, visorbus,
+visorvideoclient, visorconinclient, visoruislib, visorchannelstub,
+visorchannel, and visorutil. The other drivers are required by the
+product configurations that are currently being marketed.
diff --git a/drivers/staging/unisys/Documentation/proc-entries.txt b/drivers/staging/unisys/Documentation/proc-entries.txt
new file mode 100644
index 000000000..426f92b1c
--- /dev/null
+++ b/drivers/staging/unisys/Documentation/proc-entries.txt
@@ -0,0 +1,93 @@
+ s-Par Proc Entries
+This document describes the proc entries created by the Unisys s-Par modules.
+
+Support Module Entries
+These entries are provided primarily for debugging.
+
+/proc/uislib/info: This entry contains debugging information for the
+uislib module, including bus information and memory usage.
+
+/proc/visorchipset/controlvm: This directory contains debugging
+entries for the controlvm channel used by visorchipset.
+
+/proc/uislib/platform: This entry is used to display the platform
+number this node is in the system. For some guests, this may be
+invalid.
+
+/proc/visorchipset/chipsetready: This entry is written to by scripts
+to signify that any user level activity has been completed before the
+guest can be considered running and is shown as running in the s-Par
+UI.
+
+Device Entries
+These entries provide status of the devices shared by a service partition.
+
+/proc/uislib/vbus: this is a directory containing entries for each
+virtual bus. Each numbered sub-directory contains an info entry, which
+describes the devices that appear on that bus.
+
+/proc/uislib/cycles_before_wait: This entry is used to tune
+performance, by setting the number of cycles we wait before going idle
+when in polling mode. A longer time will reduce message latency but
+spend more processing time polling.
+
+/proc/uislib/smart_wakeup: This entry is used to tune performance, by
+enabling or disabling smart wakeup.
+
+/proc/virthba/info: This entry contains debugging information for the
+virthba module, including interrupt information and memory usage.
+
+/proc/virthba/enable_ints: This entry controls interrupt use by the
+virthba module. Writing a 0 to this entry will disable interrupts.
+
+/proc/virtnic/info: This entry contains debugging information for the
+virtnic module, including interrupt information, send and receive
+counts, and other device information.
+
+/proc/virtnic/ethX: This is a directory containing entries for each
+virtual NIC. Each named subdirectory contains two entries,
+clientstring and zone.
+
+/proc/virtpci/info: This entry contains debugging information for the
+virtpci module, including virtual PCI bus information and device
+locations.
+
+/proc/virtnic/enable_ints: This entry controls interrupt use by the
+virtnic module. Writing a 0 to this entry will disable interrupts.
+
+Visorconinclient, visordiag, visornoop, visorserialclient, and
+visorvideoclient Entries
+
+The entries in proc for these modules all follow the same
+pattern. Each module has its own proc directory with the same name,
+e.g. visordiag presents a /proc/visordiag directory. Inside of the
+module's directory are a device directory, which contains one numbered
+directory for each device provided by that module. Each device has a
+diag entry that presents the device number and visorbus name for that
+device. The module directory also has a driver/diag entry, which
+reports the corresponding s-Par version number of the driver.
+
+Automated Installation Entries
+
+These entries are used to pass information between the s-Par platform
+and the Linux-based installation and recovery tool. These values are
+read/write, however, the guest can only reset them to 0, or report an
+error status through the installer entry. The values are only set via
+s-Par's firmware interface, to help prevent accidentally booting into
+the tool.
+
+/proc/visorchipset/boottotool: This entry instructs s-Par that the
+next reboot will launch the installation and recovery tool. If set to
+0, the next boot will happen according to the UEFI boot manager
+settings.
+
+/proc/visorchipset/toolaction: This entry indicates the installation
+and recovery tool mode requested for the next boot.
+
+/proc/visorchipset/installer: this entry is used by the installation
+and recovery tool to pass status and result information back to the
+s-Par firmware.
+
+/proc/visorchipset/partition: This directory contains the guest
+partition configuration data for each virtual bus, for use during
+installation and at runtime for s-Par service partitions.
diff --git a/drivers/staging/unisys/Kconfig b/drivers/staging/unisys/Kconfig
new file mode 100644
index 000000000..19fcb3465
--- /dev/null
+++ b/drivers/staging/unisys/Kconfig
@@ -0,0 +1,19 @@
+#
+# Unisys SPAR driver configuration
+#
+menuconfig UNISYSSPAR
+ bool "Unisys SPAR driver support"
+ depends on X86_64
+ ---help---
+ Support for the Unisys SPAR drivers
+
+if UNISYSSPAR
+
+source "drivers/staging/unisys/visorutil/Kconfig"
+source "drivers/staging/unisys/visorchannel/Kconfig"
+source "drivers/staging/unisys/visorchipset/Kconfig"
+source "drivers/staging/unisys/uislib/Kconfig"
+source "drivers/staging/unisys/virtpci/Kconfig"
+source "drivers/staging/unisys/virthba/Kconfig"
+
+endif # UNISYSSPAR
diff --git a/drivers/staging/unisys/MAINTAINERS b/drivers/staging/unisys/MAINTAINERS
new file mode 100644
index 000000000..c9cef0b91
--- /dev/null
+++ b/drivers/staging/unisys/MAINTAINERS
@@ -0,0 +1,6 @@
+Unisys s-Par drivers
+M: Ben Romer <sparmaintainer@unisys.com>
+S: Maintained
+F: Documentation/s-Par/overview.txt
+F: Documentation/s-Par/proc-entries.txt
+F: drivers/staging/unisys/
diff --git a/drivers/staging/unisys/Makefile b/drivers/staging/unisys/Makefile
new file mode 100644
index 000000000..68b9925e7
--- /dev/null
+++ b/drivers/staging/unisys/Makefile
@@ -0,0 +1,9 @@
+#
+# Makefile for Unisys SPAR drivers
+#
+obj-$(CONFIG_UNISYS_VISORUTIL) += visorutil/
+obj-$(CONFIG_UNISYS_VISORCHANNEL) += visorchannel/
+obj-$(CONFIG_UNISYS_VISORCHIPSET) += visorchipset/
+obj-$(CONFIG_UNISYS_UISLIB) += uislib/
+obj-$(CONFIG_UNISYS_VIRTPCI) += virtpci/
+obj-$(CONFIG_UNISYS_VIRTHBA) += virthba/
diff --git a/drivers/staging/unisys/TODO b/drivers/staging/unisys/TODO
new file mode 100644
index 000000000..034ac61c4
--- /dev/null
+++ b/drivers/staging/unisys/TODO
@@ -0,0 +1,21 @@
+TODO:
+ -checkpatch warnings
+ -move /proc entries to /sys
+ -proper major number(s)
+ -add other drivers needed for full functionality:
+ -visorclientbus
+ -visorbus
+ -visordiag
+ -virtnic
+ -visornoop
+ -visorserial
+ -visorvideoclient
+ -visorconinclient
+ -sparstop
+ -move individual drivers into proper driver subsystems
+
+
+Patches to:
+ Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+ Ken Cox <jkc@redhat.com>
+ Unisys s-Par maintainer mailing list <sparmaintainer@unisys.com>
diff --git a/drivers/staging/unisys/common-spar/include/channels/channel.h b/drivers/staging/unisys/common-spar/include/channels/channel.h
new file mode 100644
index 000000000..6fb6e5b3d
--- /dev/null
+++ b/drivers/staging/unisys/common-spar/include/channels/channel.h
@@ -0,0 +1,590 @@
+/* Copyright (C) 2010 - 2013 UNISYS CORPORATION
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or (at
+ * your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
+ * NON INFRINGEMENT. See the GNU General Public License for more
+ * details.
+ */
+
+#ifndef __CHANNEL_H__
+#define __CHANNEL_H__
+
+#include <linux/types.h>
+#include <linux/io.h>
+#include <linux/uuid.h>
+
+/*
+* Whenever this file is changed a corresponding change must be made in
+* the Console/ServicePart/visordiag_early/supervisor_channel.h file
+* which is needed for Linux kernel compiles. These two files must be
+* in sync.
+*/
+
+/* define the following to prevent include nesting in kernel header
+ * files of similar abbreviated content
+ */
+#define __SUPERVISOR_CHANNEL_H__
+
+#define SIGNATURE_16(A, B) ((A) | (B<<8))
+#define SIGNATURE_32(A, B, C, D) \
+ (SIGNATURE_16(A, B) | (SIGNATURE_16(C, D) << 16))
+#define SIGNATURE_64(A, B, C, D, E, F, G, H) \
+ (SIGNATURE_32(A, B, C, D) | ((u64)(SIGNATURE_32(E, F, G, H)) << 32))
+
+#ifndef lengthof
+#define lengthof(TYPE, MEMBER) (sizeof(((TYPE *)0)->MEMBER))
+#endif
+#ifndef COVERQ
+#define COVERQ(v, d) (((v)+(d)-1) / (d))
+#endif
+#ifndef COVER
+#define COVER(v, d) ((d)*COVERQ(v, d))
+#endif
+
+#define ULTRA_CHANNEL_PROTOCOL_SIGNATURE SIGNATURE_32('E', 'C', 'N', 'L')
+
+enum channel_serverstate {
+ CHANNELSRV_UNINITIALIZED = 0, /* channel is in an undefined state */
+ CHANNELSRV_READY = 1 /* channel has been initialized by server */
+};
+
+enum channel_clientstate {
+ CHANNELCLI_DETACHED = 0,
+ CHANNELCLI_DISABLED = 1, /* client can see channel but is NOT
+ * allowed to use it unless given TBD
+ * explicit request (should actually be
+ * < DETACHED) */
+ CHANNELCLI_ATTACHING = 2, /* legacy EFI client request
+ * for EFI server to attach */
+ CHANNELCLI_ATTACHED = 3, /* idle, but client may want
+ * to use channel any time */
+ CHANNELCLI_BUSY = 4, /* client either wants to use or is
+ * using channel */
+ CHANNELCLI_OWNED = 5 /* "no worries" state - client can
+ * access channel anytime */
+};
+
+static inline const u8 *
+ULTRA_CHANNELCLI_STRING(u32 v)
+{
+ switch (v) {
+ case CHANNELCLI_DETACHED:
+ return (const u8 *)("DETACHED");
+ case CHANNELCLI_DISABLED:
+ return (const u8 *)("DISABLED");
+ case CHANNELCLI_ATTACHING:
+ return (const u8 *)("ATTACHING");
+ case CHANNELCLI_ATTACHED:
+ return (const u8 *)("ATTACHED");
+ case CHANNELCLI_BUSY:
+ return (const u8 *)("BUSY");
+ case CHANNELCLI_OWNED:
+ return (const u8 *)("OWNED");
+ default:
+ break;
+ }
+ return (const u8 *)("?");
+}
+
+#define SPAR_CHANNEL_SERVER_READY(ch) \
+ (readl(&(ch)->srv_state) == CHANNELSRV_READY)
+
+#define ULTRA_VALID_CHANNELCLI_TRANSITION(o, n) \
+ (((((o) == CHANNELCLI_DETACHED) && ((n) == CHANNELCLI_DISABLED)) || \
+ (((o) == CHANNELCLI_ATTACHING) && ((n) == CHANNELCLI_DISABLED)) || \
+ (((o) == CHANNELCLI_ATTACHED) && ((n) == CHANNELCLI_DISABLED)) || \
+ (((o) == CHANNELCLI_ATTACHING) && ((n) == CHANNELCLI_DETACHED)) || \
+ (((o) == CHANNELCLI_ATTACHED) && ((n) == CHANNELCLI_DETACHED)) || \
+ (((o) == CHANNELCLI_DETACHED) && ((n) == CHANNELCLI_ATTACHING)) || \
+ (((o) == CHANNELCLI_ATTACHING) && ((n) == CHANNELCLI_ATTACHED)) || \
+ (((o) == CHANNELCLI_DETACHED) && ((n) == CHANNELCLI_ATTACHED)) || \
+ (((o) == CHANNELCLI_BUSY) && ((n) == CHANNELCLI_ATTACHED)) || \
+ (((o) == CHANNELCLI_ATTACHED) && ((n) == CHANNELCLI_BUSY)) || \
+ (((o) == CHANNELCLI_DETACHED) && ((n) == CHANNELCLI_OWNED)) || \
+ (((o) == CHANNELCLI_DISABLED) && ((n) == CHANNELCLI_OWNED)) || \
+ (((o) == CHANNELCLI_ATTACHING) && ((n) == CHANNELCLI_OWNED)) || \
+ (((o) == CHANNELCLI_ATTACHED) && ((n) == CHANNELCLI_OWNED)) || \
+ (((o) == CHANNELCLI_BUSY) && ((n) == CHANNELCLI_OWNED)) || (0)) \
+ ? (1) : (0))
+
+#define SPAR_CHANNEL_CLIENT_CHK_TRANSITION(old, new, id, log, \
+ file, line) \
+ do { \
+ if (!ULTRA_VALID_CHANNELCLI_TRANSITION(old, new)) \
+ pr_info("%s Channel StateTransition INVALID! (%s) %s(%d)-->%s(%d) @%s:%d\n", \
+ id, "CliState<x>", \
+ ULTRA_CHANNELCLI_STRING(old), \
+ old, \
+ ULTRA_CHANNELCLI_STRING(new), \
+ new, \
+ pathname_last_n_nodes((u8 *)file, 4), \
+ line); \
+ } while (0)
+
+#define SPAR_CHANNEL_CLIENT_TRANSITION(ch, id, newstate, log) \
+ do { \
+ SPAR_CHANNEL_CLIENT_CHK_TRANSITION( \
+ readl(&(((struct channel_header __iomem *)\
+ (ch))->cli_state_os)), \
+ newstate, id, log, __FILE__, __LINE__); \
+ pr_info("%s Channel StateTransition (%s) %s(%d)-->%s(%d) @%s:%d\n", \
+ id, "CliStateOS", \
+ ULTRA_CHANNELCLI_STRING( \
+ readl(&((struct channel_header __iomem *)\
+ (ch))->cli_state_os)), \
+ readl(&((struct channel_header __iomem *)\
+ (ch))->cli_state_os), \
+ ULTRA_CHANNELCLI_STRING(newstate), \
+ newstate, \
+ pathname_last_n_nodes(__FILE__, 4), __LINE__); \
+ writel(newstate, &((struct channel_header __iomem *)\
+ (ch))->cli_state_os); \
+ mb(); /* required for channel synch */ \
+ } while (0)
+
+/* Values for ULTRA_CHANNEL_PROTOCOL.CliErrorBoot: */
+/* throttling invalid boot channel statetransition error due to client
+ * disabled */
+#define ULTRA_CLIERRORBOOT_THROTTLEMSG_DISABLED 0x01
+
+/* throttling invalid boot channel statetransition error due to client
+ * not attached */
+#define ULTRA_CLIERRORBOOT_THROTTLEMSG_NOTATTACHED 0x02
+
+/* throttling invalid boot channel statetransition error due to busy channel */
+#define ULTRA_CLIERRORBOOT_THROTTLEMSG_BUSY 0x04
+
+/* Values for ULTRA_CHANNEL_PROTOCOL.CliErrorOS: */
+/* throttling invalid guest OS channel statetransition error due to
+ * client disabled */
+#define ULTRA_CLIERROROS_THROTTLEMSG_DISABLED 0x01
+
+/* throttling invalid guest OS channel statetransition error due to
+ * client not attached */
+#define ULTRA_CLIERROROS_THROTTLEMSG_NOTATTACHED 0x02
+
+/* throttling invalid guest OS channel statetransition error due to
+ * busy channel */
+#define ULTRA_CLIERROROS_THROTTLEMSG_BUSY 0x04
+
+/* Values for ULTRA_CHANNEL_PROTOCOL.Features: This define exists so
+* that windows guest can look at the FeatureFlags in the io channel,
+* and configure the windows driver to use interrupts or not based on
+* this setting. This flag is set in uislib after the
+* ULTRA_VHBA_init_channel is called. All feature bits for all
+* channels should be defined here. The io channel feature bits are
+* defined right here */
+#define ULTRA_IO_DRIVER_ENABLES_INTS (0x1ULL << 1)
+#define ULTRA_IO_CHANNEL_IS_POLLING (0x1ULL << 3)
+#define ULTRA_IO_IOVM_IS_OK_WITH_DRIVER_DISABLING_INTS (0x1ULL << 4)
+#define ULTRA_IO_DRIVER_DISABLES_INTS (0x1ULL << 5)
+#define ULTRA_IO_DRIVER_SUPPORTS_ENHANCED_RCVBUF_CHECKING (0x1ULL << 6)
+
+#pragma pack(push, 1) /* both GCC and VC now allow this pragma */
+/* Common Channel Header */
+struct channel_header {
+ u64 signature; /* Signature */
+ u32 legacy_state; /* DEPRECATED - being replaced by */
+ /* / SrvState, CliStateBoot, and CliStateOS below */
+ u32 header_size; /* sizeof(struct channel_header) */
+ u64 size; /* Total size of this channel in bytes */
+ u64 features; /* Flags to modify behavior */
+ uuid_le chtype; /* Channel type: data, bus, control, etc. */
+ u64 partition_handle; /* ID of guest partition */
+ u64 handle; /* Device number of this channel in client */
+ u64 ch_space_offset; /* Offset in bytes to channel specific area */
+ u32 version_id; /* struct channel_header Version ID */
+ u32 partition_index; /* Index of guest partition */
+ uuid_le zone_uuid; /* Guid of Channel's zone */
+ u32 cli_str_offset; /* offset from channel header to
+ * nul-terminated ClientString (0 if
+ * ClientString not present) */
+ u32 cli_state_boot; /* CHANNEL_CLIENTSTATE of pre-boot
+ * EFI client of this channel */
+ u32 cmd_state_cli; /* CHANNEL_COMMANDSTATE (overloaded in
+ * Windows drivers, see ServerStateUp,
+ * ServerStateDown, etc) */
+ u32 cli_state_os; /* CHANNEL_CLIENTSTATE of Guest OS
+ * client of this channel */
+ u32 ch_characteristic; /* CHANNEL_CHARACTERISTIC_<xxx> */
+ u32 cmd_state_srv; /* CHANNEL_COMMANDSTATE (overloaded in
+ * Windows drivers, see ServerStateUp,
+ * ServerStateDown, etc) */
+ u32 srv_state; /* CHANNEL_SERVERSTATE */
+ u8 cli_error_boot; /* bits to indicate err states for
+ * boot clients, so err messages can
+ * be throttled */
+ u8 cli_error_os; /* bits to indicate err states for OS
+ * clients, so err messages can be
+ * throttled */
+ u8 filler[1]; /* Pad out to 128 byte cacheline */
+ /* Please add all new single-byte values below here */
+ u8 recover_channel;
+};
+
+#define ULTRA_CHANNEL_ENABLE_INTS (0x1ULL << 0)
+
+/* Subheader for the Signal Type variation of the Common Channel */
+struct signal_queue_header {
+ /* 1st cache line */
+ u32 version; /* SIGNAL_QUEUE_HEADER Version ID */
+ u32 chtype; /* Queue type: storage, network */
+ u64 size; /* Total size of this queue in bytes */
+ u64 sig_base_offset; /* Offset to signal queue area */
+ u64 features; /* Flags to modify behavior */
+ u64 num_sent; /* Total # of signals placed in this queue */
+ u64 num_overflows; /* Total # of inserts failed due to
+ * full queue */
+ u32 signal_size; /* Total size of a signal for this queue */
+ u32 max_slots; /* Max # of slots in queue, 1 slot is
+ * always empty */
+ u32 max_signals; /* Max # of signals in queue
+ * (MaxSignalSlots-1) */
+ u32 head; /* Queue head signal # */
+ /* 2nd cache line */
+ u64 num_received; /* Total # of signals removed from this queue */
+ u32 tail; /* Queue tail signal # (on separate
+ * cache line) */
+ u32 reserved1; /* Reserved field */
+ u64 reserved2; /* Reserved field */
+ u64 client_queue;
+ u64 num_irq_received; /* Total # of Interrupts received. This
+ * is incremented by the ISR in the
+ * guest windows driver */
+ u64 num_empty; /* Number of times that visor_signal_remove
+ * is called and returned Empty
+ * Status. */
+ u32 errorflags; /* Error bits set during SignalReinit
+ * to denote trouble with client's
+ * fields */
+ u8 filler[12]; /* Pad out to 64 byte cacheline */
+};
+
+#pragma pack(pop)
+
+#define spar_signal_init(chan, QHDRFLD, QDATAFLD, QDATATYPE, ver, typ) \
+ do { \
+ memset(&chan->QHDRFLD, 0, sizeof(chan->QHDRFLD)); \
+ chan->QHDRFLD.version = ver; \
+ chan->QHDRFLD.chtype = typ; \
+ chan->QHDRFLD.size = sizeof(chan->QDATAFLD); \
+ chan->QHDRFLD.signal_size = sizeof(QDATATYPE); \
+ chan->QHDRFLD.sig_base_offset = (u64)(chan->QDATAFLD)- \
+ (u64)(&chan->QHDRFLD); \
+ chan->QHDRFLD.max_slots = \
+ sizeof(chan->QDATAFLD)/sizeof(QDATATYPE); \
+ chan->QHDRFLD.max_signals = chan->QHDRFLD.max_slots-1; \
+ } while (0)
+
+/* Generic function useful for validating any type of channel when it is
+ * received by the client that will be accessing the channel.
+ * Note that <logCtx> is only needed for callers in the EFI environment, and
+ * is used to pass the EFI_DIAG_CAPTURE_PROTOCOL needed to log messages.
+ */
+static inline int
+spar_check_channel_client(void __iomem *ch,
+ uuid_le expected_uuid,
+ char *chname,
+ u64 expected_min_bytes,
+ u32 expected_version,
+ u64 expected_signature)
+{
+ if (uuid_le_cmp(expected_uuid, NULL_UUID_LE) != 0) {
+ uuid_le guid;
+
+ memcpy_fromio(&guid,
+ &((struct channel_header __iomem *)(ch))->chtype,
+ sizeof(guid));
+ /* caller wants us to verify type GUID */
+ if (uuid_le_cmp(guid, expected_uuid) != 0) {
+ pr_err("Channel mismatch on channel=%s(%pUL) field=type expected=%pUL actual=%pUL\n",
+ chname, &expected_uuid,
+ &expected_uuid, &guid);
+ return 0;
+ }
+ }
+ if (expected_min_bytes > 0) { /* caller wants us to verify
+ * channel size */
+ unsigned long long bytes =
+ readq(&((struct channel_header __iomem *)
+ (ch))->size);
+ if (bytes < expected_min_bytes) {
+ pr_err("Channel mismatch on channel=%s(%pUL) field=size expected=0x%-8.8Lx actual=0x%-8.8Lx\n",
+ chname, &expected_uuid,
+ (unsigned long long)expected_min_bytes, bytes);
+ return 0;
+ }
+ }
+ if (expected_version > 0) { /* caller wants us to verify
+ * channel version */
+ unsigned long ver = readl(&((struct channel_header __iomem *)
+ (ch))->version_id);
+ if (ver != expected_version) {
+ pr_err("Channel mismatch on channel=%s(%pUL) field=version expected=0x%-8.8lx actual=0x%-8.8lx\n",
+ chname, &expected_uuid,
+ (unsigned long)expected_version, ver);
+ return 0;
+ }
+ }
+ if (expected_signature > 0) { /* caller wants us to verify
+ * channel signature */
+ unsigned long long sig =
+ readq(&((struct channel_header __iomem *)
+ (ch))->signature);
+ if (sig != expected_signature) {
+ pr_err("Channel mismatch on channel=%s(%pUL) field=signature expected=0x%-8.8llx actual=0x%-8.8llx\n",
+ chname, &expected_uuid,
+ expected_signature, sig);
+ return 0;
+ }
+ }
+ return 1;
+}
+
+/* Generic function useful for validating any type of channel when it is about
+ * to be initialized by the server of the channel.
+ * Note that <logCtx> is only needed for callers in the EFI environment, and
+ * is used to pass the EFI_DIAG_CAPTURE_PROTOCOL needed to log messages.
+ */
+static inline int spar_check_channel_server(uuid_le typeuuid, char *name,
+ u64 expected_min_bytes,
+ u64 actual_bytes)
+{
+ if (expected_min_bytes > 0) /* caller wants us to verify
+ * channel size */
+ if (actual_bytes < expected_min_bytes) {
+ pr_err("Channel mismatch on channel=%s(%pUL) field=size expected=0x%-8.8llx actual=0x%-8.8llx\n",
+ name, &typeuuid, expected_min_bytes,
+ actual_bytes);
+ return 0;
+ }
+ return 1;
+}
+
+/* Given a file pathname <s> (with '/' or '\' separating directory nodes),
+ * returns a pointer to the beginning of a node within that pathname such
+ * that the number of nodes from that pointer to the end of the string is
+ * NOT more than <n>. Note that if the pathname has less than <n> nodes
+ * in it, the return pointer will be to the beginning of the string.
+ */
+static inline u8 *
+pathname_last_n_nodes(u8 *s, unsigned int n)
+{
+ u8 *p = s;
+ unsigned int node_count = 0;
+
+ while (*p != '\0') {
+ if ((*p == '/') || (*p == '\\'))
+ node_count++;
+ p++;
+ }
+ if (node_count <= n)
+ return s;
+ while (n > 0) {
+ p--;
+ if (p == s)
+ break; /* should never happen, unless someone
+ * is changing the string while we are
+ * looking at it!! */
+ if ((*p == '/') || (*p == '\\'))
+ n--;
+ }
+ return p + 1;
+}
+
+static inline int
+spar_channel_client_acquire_os(void __iomem *ch, u8 *id)
+{
+ struct channel_header __iomem *hdr = ch;
+
+ if (readl(&hdr->cli_state_os) == CHANNELCLI_DISABLED) {
+ if ((readb(&hdr->cli_error_os)
+ & ULTRA_CLIERROROS_THROTTLEMSG_DISABLED) == 0) {
+ /* we are NOT throttling this message */
+ writeb(readb(&hdr->cli_error_os) |
+ ULTRA_CLIERROROS_THROTTLEMSG_DISABLED,
+ &hdr->cli_error_os);
+ /* throttle until acquire successful */
+
+ pr_info("%s Channel StateTransition INVALID! - acquire failed because OS client DISABLED\n",
+ id);
+ }
+ return 0;
+ }
+ if ((readl(&hdr->cli_state_os) != CHANNELCLI_OWNED) &&
+ (readl(&hdr->cli_state_boot) == CHANNELCLI_DISABLED)) {
+ /* Our competitor is DISABLED, so we can transition to OWNED */
+ pr_info("%s Channel StateTransition (%s) %s(%d)-->%s(%d)\n",
+ id, "cli_state_os",
+ ULTRA_CHANNELCLI_STRING(readl(&hdr->cli_state_os)),
+ readl(&hdr->cli_state_os),
+ ULTRA_CHANNELCLI_STRING(CHANNELCLI_OWNED),
+ CHANNELCLI_OWNED);
+ writel(CHANNELCLI_OWNED, &hdr->cli_state_os);
+ mb(); /* required for channel synch */
+ }
+ if (readl(&hdr->cli_state_os) == CHANNELCLI_OWNED) {
+ if (readb(&hdr->cli_error_os) != 0) {
+ /* we are in an error msg throttling state;
+ * come out of it */
+ pr_info("%s Channel OS client acquire now successful\n",
+ id);
+ writeb(0, &hdr->cli_error_os);
+ }
+ return 1;
+ }
+
+ /* We have to do it the "hard way". We transition to BUSY,
+ * and can use the channel iff our competitor has not also
+ * transitioned to BUSY. */
+ if (readl(&hdr->cli_state_os) != CHANNELCLI_ATTACHED) {
+ if ((readb(&hdr->cli_error_os)
+ & ULTRA_CLIERROROS_THROTTLEMSG_NOTATTACHED) == 0) {
+ /* we are NOT throttling this message */
+ writeb(readb(&hdr->cli_error_os) |
+ ULTRA_CLIERROROS_THROTTLEMSG_NOTATTACHED,
+ &hdr->cli_error_os);
+ /* throttle until acquire successful */
+ pr_info("%s Channel StateTransition INVALID! - acquire failed because OS client NOT ATTACHED (state=%s(%d))\n",
+ id, ULTRA_CHANNELCLI_STRING(
+ readl(&hdr->cli_state_os)),
+ readl(&hdr->cli_state_os));
+ }
+ return 0;
+ }
+ writel(CHANNELCLI_BUSY, &hdr->cli_state_os);
+ mb(); /* required for channel synch */
+ if (readl(&hdr->cli_state_boot) == CHANNELCLI_BUSY) {
+ if ((readb(&hdr->cli_error_os)
+ & ULTRA_CLIERROROS_THROTTLEMSG_BUSY) == 0) {
+ /* we are NOT throttling this message */
+ writeb(readb(&hdr->cli_error_os) |
+ ULTRA_CLIERROROS_THROTTLEMSG_BUSY,
+ &hdr->cli_error_os);
+ /* throttle until acquire successful */
+ pr_info("%s Channel StateTransition failed - host OS acquire failed because boot BUSY\n",
+ id);
+ }
+ /* reset busy */
+ writel(CHANNELCLI_ATTACHED, &hdr->cli_state_os);
+ mb(); /* required for channel synch */
+ return 0;
+ }
+ if (readb(&hdr->cli_error_os) != 0) {
+ /* we are in an error msg throttling state; come out of it */
+ pr_info("%s Channel OS client acquire now successful\n", id);
+ writeb(0, &hdr->cli_error_os);
+ }
+ return 1;
+}
+
+static inline void
+spar_channel_client_release_os(void __iomem *ch, u8 *id)
+{
+ struct channel_header __iomem *hdr = ch;
+
+ if (readb(&hdr->cli_error_os) != 0) {
+ /* we are in an error msg throttling state; come out of it */
+ pr_info("%s Channel OS client error state cleared\n", id);
+ writeb(0, &hdr->cli_error_os);
+ }
+ if (readl(&hdr->cli_state_os) == CHANNELCLI_OWNED)
+ return;
+ if (readl(&hdr->cli_state_os) != CHANNELCLI_BUSY) {
+ pr_info("%s Channel StateTransition INVALID! - release failed because OS client NOT BUSY (state=%s(%d))\n",
+ id, ULTRA_CHANNELCLI_STRING(
+ readl(&hdr->cli_state_os)),
+ readl(&hdr->cli_state_os));
+ /* return; */
+ }
+ writel(CHANNELCLI_ATTACHED, &hdr->cli_state_os); /* release busy */
+}
+
+/*
+* Routine Description:
+* Tries to insert the prebuilt signal pointed to by pSignal into the nth
+* Queue of the Channel pointed to by pChannel
+*
+* Parameters:
+* pChannel: (IN) points to the IO Channel
+* Queue: (IN) nth Queue of the IO Channel
+* pSignal: (IN) pointer to the signal
+*
+* Assumptions:
+* - pChannel, Queue and pSignal are valid.
+* - If insertion fails due to a full queue, the caller will determine the
+* retry policy (e.g. wait & try again, report an error, etc.).
+*
+* Return value: 1 if the insertion succeeds, 0 if the queue was
+* full.
+*/
+
+unsigned char spar_signal_insert(struct channel_header __iomem *ch, u32 queue,
+ void *sig);
+
+/*
+* Routine Description:
+* Removes one signal from Channel pChannel's nth Queue at the
+* time of the call and copies it into the memory pointed to by
+* pSignal.
+*
+* Parameters:
+* pChannel: (IN) points to the IO Channel
+* Queue: (IN) nth Queue of the IO Channel
+* pSignal: (IN) pointer to where the signals are to be copied
+*
+* Assumptions:
+* - pChannel and Queue are valid.
+* - pSignal points to a memory area large enough to hold queue's SignalSize
+*
+* Return value: 1 if the removal succeeds, 0 if the queue was
+* empty.
+*/
+
+unsigned char spar_signal_remove(struct channel_header __iomem *ch, u32 queue,
+ void *sig);
+
+/*
+* Routine Description:
+* Removes all signals present in Channel pChannel's nth Queue at the
+* time of the call and copies them into the memory pointed to by
+* pSignal. Returns the # of signals copied as the value of the routine.
+*
+* Parameters:
+* pChannel: (IN) points to the IO Channel
+* Queue: (IN) nth Queue of the IO Channel
+* pSignal: (IN) pointer to where the signals are to be copied
+*
+* Assumptions:
+* - pChannel and Queue are valid.
+* - pSignal points to a memory area large enough to hold Queue's MaxSignals
+* # of signals, each of which is Queue's SignalSize.
+*
+* Return value:
+* # of signals copied.
+*/
+unsigned int spar_signal_remove_all(struct channel_header *ch, u32 queue,
+ void *sig);
+
+/*
+* Routine Description:
+* Determine whether a signal queue is empty.
+*
+* Parameters:
+* pChannel: (IN) points to the IO Channel
+* Queue: (IN) nth Queue of the IO Channel
+*
+* Return value:
+* 1 if the signal queue is empty, 0 otherwise.
+*/
+unsigned char spar_signalqueue_empty(struct channel_header __iomem *ch,
+ u32 queue);
+
+#endif
diff --git a/drivers/staging/unisys/common-spar/include/channels/channel_guid.h b/drivers/staging/unisys/common-spar/include/channels/channel_guid.h
new file mode 100644
index 000000000..706363fc3
--- /dev/null
+++ b/drivers/staging/unisys/common-spar/include/channels/channel_guid.h
@@ -0,0 +1,61 @@
+/* Copyright (C) 2010 - 2013 UNISYS CORPORATION
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or (at
+ * your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
+ * NON INFRINGEMENT. See the GNU General Public License for more
+ * details.
+ */
+
+/*
+ * CHANNEL Guids
+ */
+
+/* Used in IOChannel
+ * {414815ed-c58c-11da-95a9-00e08161165f}
+ */
+#define SPAR_VHBA_CHANNEL_PROTOCOL_UUID \
+ UUID_LE(0x414815ed, 0xc58c, 0x11da, \
+ 0x95, 0xa9, 0x0, 0xe0, 0x81, 0x61, 0x16, 0x5f)
+static const uuid_le spar_vhba_channel_protocol_uuid =
+ SPAR_VHBA_CHANNEL_PROTOCOL_UUID;
+
+/* Used in IOChannel
+ * {8cd5994d-c58e-11da-95a9-00e08161165f}
+ */
+#define SPAR_VNIC_CHANNEL_PROTOCOL_UUID \
+ UUID_LE(0x8cd5994d, 0xc58e, 0x11da, \
+ 0x95, 0xa9, 0x0, 0xe0, 0x81, 0x61, 0x16, 0x5f)
+static const uuid_le spar_vnic_channel_protocol_uuid =
+ SPAR_VNIC_CHANNEL_PROTOCOL_UUID;
+
+/* Used in IOChannel
+ * {72120008-4AAB-11DC-8530-444553544200}
+ */
+#define SPAR_SIOVM_UUID \
+ UUID_LE(0x72120008, 0x4AAB, 0x11DC, \
+ 0x85, 0x30, 0x44, 0x45, 0x53, 0x54, 0x42, 0x00)
+static const uuid_le spar_siovm_uuid = SPAR_SIOVM_UUID;
+
+/* Used in visornoop/visornoop_main.c
+ * {5b52c5ac-e5f5-4d42-8dff-429eaecd221f}
+ */
+#define SPAR_CONTROLDIRECTOR_CHANNEL_PROTOCOL_UUID \
+ UUID_LE(0x5b52c5ac, 0xe5f5, 0x4d42, \
+ 0x8d, 0xff, 0x42, 0x9e, 0xae, 0xcd, 0x22, 0x1f)
+
+static const uuid_le spar_controldirector_channel_protocol_uuid =
+ SPAR_CONTROLDIRECTOR_CHANNEL_PROTOCOL_UUID;
+
+/* Used in visorchipset/visorchipset_main.c
+ * {B4E79625-AEDE-4EAA-9E11-D3EDDCD4504C}
+ */
+#define SPAR_DIAG_POOL_CHANNEL_PROTOCOL_UUID \
+ UUID_LE(0xb4e79625, 0xaede, 0x4eaa, \
+ 0x9e, 0x11, 0xd3, 0xed, 0xdc, 0xd4, 0x50, 0x4c)
diff --git a/drivers/staging/unisys/common-spar/include/channels/controlframework.h b/drivers/staging/unisys/common-spar/include/channels/controlframework.h
new file mode 100644
index 000000000..33d9caf33
--- /dev/null
+++ b/drivers/staging/unisys/common-spar/include/channels/controlframework.h
@@ -0,0 +1,62 @@
+/* Copyright (C) 2010 - 2013 UNISYS CORPORATION
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or (at
+ * your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
+ * NON INFRINGEMENT. See the GNU General Public License for more
+ * details.
+ */
+
+/*
+ * Module Name:
+ * controlframework.h
+ *
+ * Abstract: This file defines common structures in the unmanaged
+ * Ultravisor (mostly EFI) space.
+ *
+ */
+
+#ifndef _CONTROL_FRAMEWORK_H_
+#define _CONTROL_FRAMEWORK_H_
+
+#include <linux/types.h>
+#include "channel.h"
+
+struct spar_segment_state {
+ u16 enabled:1; /* Bit 0: May enter other states */
+ u16 active:1; /* Bit 1: Assigned to active partition */
+ u16 alive:1; /* Bit 2: Configure message sent to
+ * service/server */
+ u16 revoked:1; /* Bit 3: similar to partition state
+ * ShuttingDown */
+ u16 allocated:1; /* Bit 4: memory (device/port number)
+ * has been selected by Command */
+ u16 known:1; /* Bit 5: has been introduced to the
+ * service/guest partition */
+ u16 ready:1; /* Bit 6: service/Guest partition has
+ * responded to introduction */
+ u16 operating:1; /* Bit 7: resource is configured and
+ * operating */
+ /* Note: don't use high bit unless we need to switch to ushort
+ * which is non-compliant */
+};
+
+static const struct spar_segment_state segment_state_running = {
+ 1, 1, 1, 0, 1, 1, 1, 1
+};
+
+static const struct spar_segment_state segment_state_paused = {
+ 1, 1, 1, 0, 1, 1, 1, 0
+};
+
+static const struct spar_segment_state segment_state_standby = {
+ 1, 1, 0, 0, 1, 1, 1, 0
+};
+
+#endif /* _CONTROL_FRAMEWORK_H_ not defined */
diff --git a/drivers/staging/unisys/common-spar/include/channels/controlvmchannel.h b/drivers/staging/unisys/common-spar/include/channels/controlvmchannel.h
new file mode 100644
index 000000000..a66db7968
--- /dev/null
+++ b/drivers/staging/unisys/common-spar/include/channels/controlvmchannel.h
@@ -0,0 +1,511 @@
+/* Copyright (C) 2010 - 2013 UNISYS CORPORATION
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or (at
+ * your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
+ * NON INFRINGEMENT. See the GNU General Public License for more
+ * details.
+ */
+
+#ifndef __CONTROLVMCHANNEL_H__
+#define __CONTROLVMCHANNEL_H__
+
+#include <linux/uuid.h>
+#include "channel.h"
+#include "controlframework.h"
+
+typedef u64 GUEST_PHYSICAL_ADDRESS;
+
+enum { INVALID_GUEST_FIRMWARE, SAMPLE_GUEST_FIRMWARE,
+ TIANO32_GUEST_FIRMWARE, TIANO64_GUEST_FIRMWARE
+};
+
+/* {2B3C2D10-7EF5-4ad8-B966-3448B7386B3D} */
+#define SPAR_CONTROLVM_CHANNEL_PROTOCOL_UUID \
+ UUID_LE(0x2b3c2d10, 0x7ef5, 0x4ad8, \
+ 0xb9, 0x66, 0x34, 0x48, 0xb7, 0x38, 0x6b, 0x3d)
+
+static const uuid_le spar_controlvm_channel_protocol_uuid =
+ SPAR_CONTROLVM_CHANNEL_PROTOCOL_UUID;
+
+#define ULTRA_CONTROLVM_CHANNEL_PROTOCOL_SIGNATURE \
+ ULTRA_CHANNEL_PROTOCOL_SIGNATURE
+#define CONTROLVM_MESSAGE_MAX 64
+
+/* Must increment this whenever you insert or delete fields within
+* this channel struct. Also increment whenever you change the meaning
+* of fields within this channel struct so as to break pre-existing
+* software. Note that you can usually add fields to the END of the
+* channel struct withOUT needing to increment this. */
+#define ULTRA_CONTROLVM_CHANNEL_PROTOCOL_VERSIONID 1
+
+#define SPAR_CONTROLVM_CHANNEL_OK_CLIENT(ch) \
+ spar_check_channel_client(ch, \
+ spar_controlvm_channel_protocol_uuid, \
+ "controlvm", \
+ sizeof(struct spar_controlvm_channel_protocol), \
+ ULTRA_CONTROLVM_CHANNEL_PROTOCOL_VERSIONID, \
+ ULTRA_CONTROLVM_CHANNEL_PROTOCOL_SIGNATURE)
+
+#define MY_DEVICE_INDEX 0
+#define MAX_MACDATA_LEN 8 /* number of bytes for MAC address in config packet */
+#define MAX_SERIAL_NUM 32
+
+#define DISK_ZERO_PUN_NUMBER 1 /* Target ID on the SCSI bus for LUN 0 */
+#define DISK_ZERO_LUN_NUMBER 3 /* Logical Unit Number */
+
+/* Defines for various channel queues... */
+#define CONTROLVM_QUEUE_REQUEST 0
+#define CONTROLVM_QUEUE_RESPONSE 1
+#define CONTROLVM_QUEUE_EVENT 2
+#define CONTROLVM_QUEUE_ACK 3
+
+/* Max number of messages stored during IOVM creation to be reused
+ * after crash */
+#define CONTROLVM_CRASHMSG_MAX 2
+
+/** Ids for commands that may appear in either queue of a ControlVm channel.
+ *
+ * Commands that are initiated by the command partition (CP), by an IO or
+ * console service partition (SP), or by a guest partition (GP)are:
+ * - issued on the RequestQueue queue (q #0) in the ControlVm channel
+ * - responded to on the ResponseQueue queue (q #1) in the ControlVm channel
+ *
+ * Events that are initiated by an IO or console service partition (SP) or
+ * by a guest partition (GP) are:
+ * - issued on the EventQueue queue (q #2) in the ControlVm channel
+ * - responded to on the EventAckQueue queue (q #3) in the ControlVm channel
+ */
+enum controlvm_id {
+ CONTROLVM_INVALID = 0,
+ /* SWITCH commands required Parameter: SwitchNumber */
+ /* BUS commands required Parameter: BusNumber */
+ CONTROLVM_BUS_CREATE = 0x101, /* CP --> SP, GP */
+ CONTROLVM_BUS_DESTROY = 0x102, /* CP --> SP, GP */
+ CONTROLVM_BUS_CONFIGURE = 0x104, /* CP --> SP */
+ CONTROLVM_BUS_CHANGESTATE = 0x105, /* CP --> SP, GP */
+ CONTROLVM_BUS_CHANGESTATE_EVENT = 0x106, /* SP, GP --> CP */
+/* DEVICE commands required Parameter: BusNumber, DeviceNumber */
+
+ CONTROLVM_DEVICE_CREATE = 0x201, /* CP --> SP, GP */
+ CONTROLVM_DEVICE_DESTROY = 0x202, /* CP --> SP, GP */
+ CONTROLVM_DEVICE_CONFIGURE = 0x203, /* CP --> SP */
+ CONTROLVM_DEVICE_CHANGESTATE = 0x204, /* CP --> SP, GP */
+ CONTROLVM_DEVICE_CHANGESTATE_EVENT = 0x205, /* SP, GP --> CP */
+ CONTROLVM_DEVICE_RECONFIGURE = 0x206, /* CP --> Boot */
+/* DISK commands required Parameter: BusNumber, DeviceNumber */
+ CONTROLVM_DISK_CREATE = 0x221, /* CP --> SP */
+ CONTROLVM_DISK_DESTROY = 0x222, /* CP --> SP */
+ CONTROLVM_DISK_CONFIGURE = 0x223, /* CP --> SP */
+ CONTROLVM_DISK_CHANGESTATE = 0x224, /* CP --> SP */
+/* CHIPSET commands */
+ CONTROLVM_CHIPSET_INIT = 0x301, /* CP --> SP, GP */
+ CONTROLVM_CHIPSET_STOP = 0x302, /* CP --> SP, GP */
+ CONTROLVM_CHIPSET_SHUTDOWN = 0x303, /* CP --> SP */
+ CONTROLVM_CHIPSET_READY = 0x304, /* CP --> SP */
+ CONTROLVM_CHIPSET_SELFTEST = 0x305, /* CP --> SP */
+
+};
+
+struct irq_info {
+ /**< specifies interrupt info. It is used to send interrupts
+ * for this channel. The peer at the end of this channel
+ * who has registered an interrupt (using recv fields
+ * above) will receive the interrupt. Passed as a parameter
+ * to Issue_VMCALL_IO_QUEUE_TRANSITION, which generates the
+ * interrupt. Currently this is used by IOPart-SP to wake
+ * up GP when Data Channel transitions from empty to
+ * non-empty.*/
+ u64 send_irq_handle;
+
+ /**< specifies interrupt handle. It is used to retrieve the
+ * corresponding interrupt pin from Monitor; and the
+ * interrupt pin is used to connect to the corresponding
+ * interrupt. Used by IOPart-GP only. */
+ u64 recv_irq_handle;
+
+ /**< specifies interrupt vector. It, interrupt pin, and shared are
+ * used to connect to the corresponding interrupt. Used by
+ * IOPart-GP only. */
+ u32 recv_irq_vector;
+
+ /**< specifies if the recvInterrupt is shared. It, interrupt pin
+ * and vector are used to connect to 0 = not shared; 1 = shared.
+ * the corresponding interrupt. Used by IOPart-GP only. */
+ u8 recv_irq_shared;
+ u8 reserved[3]; /* Natural alignment purposes */
+};
+
+struct pci_id {
+ u16 domain;
+ u8 bus;
+ u8 slot;
+ u8 func;
+ u8 reserved[3]; /* Natural alignment purposes */
+};
+
+struct efi_spar_indication {
+ u64 boot_to_fw_ui:1; /* Bit 0: Stop in uefi ui */
+ u64 clear_nvram:1; /* Bit 1: Clear NVRAM */
+ u64 clear_cmos:1; /* Bit 2: Clear CMOS */
+ u64 boot_to_tool:1; /* Bit 3: Run install tool */
+ /* remaining bits are available */
+};
+
+enum ultra_chipset_feature {
+ ULTRA_CHIPSET_FEATURE_REPLY = 0x00000001,
+ ULTRA_CHIPSET_FEATURE_PARA_HOTPLUG = 0x00000002,
+ ULTRA_CHIPSET_FEATURE_PCIVBUS = 0x00000004
+};
+
+/** This is the common structure that is at the beginning of every
+ * ControlVm message (both commands and responses) in any ControlVm
+ * queue. Commands are easily distinguished from responses by
+ * looking at the flags.response field.
+ */
+struct controlvm_message_header {
+ u32 id; /* See CONTROLVM_ID. */
+ /* For requests, indicates the message type. */
+ /* For responses, indicates the type of message we are responding to. */
+
+ u32 message_size; /* Includes size of this struct + size
+ * of message */
+ u32 segment_index; /* Index of segment containing Vm
+ * message/information */
+ u32 completion_status; /* Error status code or result of
+ * message completion */
+ struct {
+ u32 failed:1; /**< =1 in a response to * signify
+ * failure */
+ u32 response_expected:1; /**< =1 in all messages that expect a
+ * response (Control ignores this
+ * bit) */
+ u32 server:1; /**< =1 in all bus & device-related
+ * messages where the message
+ * receiver is to act as the bus or
+ * device server */
+ u32 test_message:1; /**< =1 for testing use only
+ * (Control and Command ignore this
+ * bit) */
+ u32 partial_completion:1; /**< =1 if there are forthcoming
+ * responses/acks associated
+ * with this message */
+ u32 preserve:1; /**< =1 this is to let us know to
+ * preserve channel contents
+ * (for running guests)*/
+ u32 writer_in_diag:1; /**< =1 the DiagWriter is active in the
+ * Diagnostic Partition*/
+ } flags;
+ u32 reserved; /* Natural alignment */
+ u64 message_handle; /* Identifies the particular message instance,
+ * and is used to match particular */
+ /* request instances with the corresponding response instance. */
+ u64 payload_vm_offset; /* Offset of payload area from start of this
+ * instance of ControlVm segment */
+ u32 payload_max_bytes; /* Maximum bytes allocated in payload
+ * area of ControlVm segment */
+ u32 payload_bytes; /* Actual number of bytes of payload
+ * area to copy between IO/Command; */
+ /* if non-zero, there is a payload to copy. */
+};
+
+struct controlvm_packet_device_create {
+ u32 bus_no; /* bus # (0..n-1) from the msg receiver's end */
+ u32 dev_no; /* bus-relative (0..n-1) device number */
+ u64 channel_addr; /* Guest physical address of the channel, which
+ * can be dereferenced by the receiver of this
+ * ControlVm command */
+ u64 channel_bytes; /* specifies size of the channel in bytes */
+ uuid_le data_type_uuid; /* specifies format of data in channel */
+ uuid_le dev_inst_uuid; /* instance guid for the device */
+ struct irq_info intr; /* specifies interrupt information */
+}; /* for CONTROLVM_DEVICE_CREATE */
+
+struct controlvm_packet_device_configure {
+ u32 bus_no; /**< bus # (0..n-1) from the msg
+ * receiver's perspective */
+
+ /* Control uses header SegmentIndex field to access bus number... */
+ u32 dev_no; /**< bus-relative (0..n-1) device number */
+} ; /* for CONTROLVM_DEVICE_CONFIGURE */
+
+struct controlvm_message_device_create {
+ struct controlvm_message_header header;
+ struct controlvm_packet_device_create packet;
+}; /* total 128 bytes */
+
+struct controlvm_message_device_configure {
+ struct controlvm_message_header header;
+ struct controlvm_packet_device_configure packet;
+}; /* total 56 bytes */
+
+/* This is the format for a message in any ControlVm queue. */
+struct controlvm_message_packet {
+ union {
+ struct {
+ u32 bus_no; /* bus # (0..n-1) from the msg
+ * receiver's perspective */
+ u32 dev_count; /* indicates the max number of
+ * devices on this bus */
+ u64 channel_addr; /* Guest physical address of
+ * the channel, which can be
+ * dereferenced by the receiver
+ * of this ControlVm command */
+ u64 channel_bytes; /* size of the channel */
+ uuid_le bus_data_type_uuid; /* indicates format of
+ * data in bus channel*/
+ uuid_le bus_inst_uuid; /* instance uuid for the bus */
+ } create_bus; /* for CONTROLVM_BUS_CREATE */
+ struct {
+ u32 bus_no; /* bus # (0..n-1) from the msg
+ * receiver's perspective */
+ u32 reserved; /* Natural alignment purposes */
+ } destroy_bus; /* for CONTROLVM_BUS_DESTROY */
+ struct {
+ u32 bus_no; /* bus # (0..n-1) from the receiver's
+ * perspective */
+ u32 reserved1; /* for alignment purposes */
+ u64 guest_handle; /* This is used to convert
+ * guest physical address to
+ * physical address */
+ u64 recv_bus_irq_handle;
+ /* specifies interrupt info. It is used by SP
+ * to register to receive interrupts from the
+ * CP. This interrupt is used for bus level
+ * notifications. The corresponding
+ * sendBusInterruptHandle is kept in CP. */
+ } configure_bus; /* for CONTROLVM_BUS_CONFIGURE */
+ /* for CONTROLVM_DEVICE_CREATE */
+ struct controlvm_packet_device_create create_device;
+ struct {
+ u32 bus_no; /* bus # (0..n-1) from the msg
+ * receiver's perspective */
+ u32 dev_no; /* bus-relative (0..n-1) device # */
+ } destroy_device; /* for CONTROLVM_DEVICE_DESTROY */
+ /* for CONTROLVM_DEVICE_CONFIGURE */
+ struct controlvm_packet_device_configure configure_device;
+ struct {
+ u32 bus_no; /* bus # (0..n-1) from the msg
+ * receiver's perspective */
+ u32 dev_no; /* bus-relative (0..n-1) device # */
+ } reconfigure_device; /* for CONTROLVM_DEVICE_RECONFIGURE */
+ struct {
+ u32 bus_no;
+ struct spar_segment_state state;
+ u8 reserved[2]; /* Natural alignment purposes */
+ } bus_change_state; /* for CONTROLVM_BUS_CHANGESTATE */
+ struct {
+ u32 bus_no;
+ u32 dev_no;
+ struct spar_segment_state state;
+ struct {
+ u32 phys_device:1; /* =1 if message is for
+ * a physical device */
+ } flags;
+ u8 reserved[2]; /* Natural alignment purposes */
+ } device_change_state; /* for CONTROLVM_DEVICE_CHANGESTATE */
+ struct {
+ u32 bus_no;
+ u32 dev_no;
+ struct spar_segment_state state;
+ u8 reserved[6]; /* Natural alignment purposes */
+ } device_change_state_event;
+ /* for CONTROLVM_DEVICE_CHANGESTATE_EVENT */
+ struct {
+ u32 bus_count; /* indicates the max number of busses */
+ u32 switch_count; /* indicates the max number of
+ * switches if a service partition */
+ enum ultra_chipset_feature features;
+ u32 platform_number; /* Platform Number */
+ } init_chipset; /* for CONTROLVM_CHIPSET_INIT */
+ struct {
+ u32 options; /* reserved */
+ u32 test; /* bit 0 set to run embedded selftest */
+ } chipset_selftest; /* for CONTROLVM_CHIPSET_SELFTEST */
+ u64 addr; /* a physical address of something, that can be
+ * dereferenced by the receiver of this
+ * ControlVm command (depends on command id) */
+ u64 handle; /* a handle of something (depends on command
+ * id) */
+ };
+};
+
+/* All messages in any ControlVm queue have this layout. */
+struct controlvm_message {
+ struct controlvm_message_header hdr;
+ struct controlvm_message_packet cmd;
+};
+
+struct device_map {
+ GUEST_PHYSICAL_ADDRESS device_channel_address;
+ u64 device_channel_size;
+ u32 ca_index;
+ u32 reserved; /* natural alignment */
+ u64 reserved2; /* Align structure on 32-byte boundary */
+};
+
+struct guest_devices {
+ struct device_map video_channel;
+ struct device_map keyboard_channel;
+ struct device_map network_channel;
+ struct device_map storage_channel;
+ struct device_map console_channel;
+ u32 partition_index;
+ u32 pad;
+};
+
+struct spar_controlvm_channel_protocol {
+ struct channel_header header;
+ GUEST_PHYSICAL_ADDRESS gp_controlvm; /* guest physical address of
+ * this channel */
+ GUEST_PHYSICAL_ADDRESS gp_partition_tables;/* guest physical address of
+ * partition tables */
+ GUEST_PHYSICAL_ADDRESS gp_diag_guest; /* guest physical address of
+ * diagnostic channel */
+ GUEST_PHYSICAL_ADDRESS gp_boot_romdisk;/* guest phys addr of (read
+ * only) Boot ROM disk */
+ GUEST_PHYSICAL_ADDRESS gp_boot_ramdisk;/* guest phys addr of writable
+ * Boot RAM disk */
+ GUEST_PHYSICAL_ADDRESS gp_acpi_table; /* guest phys addr of acpi
+ * table */
+ GUEST_PHYSICAL_ADDRESS gp_control_channel;/* guest phys addr of control
+ * channel */
+ GUEST_PHYSICAL_ADDRESS gp_diag_romdisk;/* guest phys addr of diagnostic
+ * ROM disk */
+ GUEST_PHYSICAL_ADDRESS gp_nvram; /* guest phys addr of NVRAM
+ * channel */
+ u64 request_payload_offset; /* Offset to request payload area */
+ u64 event_payload_offset; /* Offset to event payload area */
+ u32 request_payload_bytes; /* Bytes available in request payload
+ * area */
+ u32 event_payload_bytes;/* Bytes available in event payload area */
+ u32 control_channel_bytes;
+ u32 nvram_channel_bytes; /* Bytes in PartitionNvram segment */
+ u32 message_bytes; /* sizeof(CONTROLVM_MESSAGE) */
+ u32 message_count; /* CONTROLVM_MESSAGE_MAX */
+ GUEST_PHYSICAL_ADDRESS gp_smbios_table;/* guest phys addr of SMBIOS
+ * tables */
+ GUEST_PHYSICAL_ADDRESS gp_physical_smbios_table;/* guest phys addr of
+ * SMBIOS table */
+ /* ULTRA_MAX_GUESTS_PER_SERVICE */
+ struct guest_devices gp_obsolete_guest_devices[16];
+
+ /* guest physical address of EFI firmware image base */
+ GUEST_PHYSICAL_ADDRESS virtual_guest_firmware_image_base;
+
+ /* guest physical address of EFI firmware entry point */
+ GUEST_PHYSICAL_ADDRESS virtual_guest_firmware_entry_point;
+
+ /* guest EFI firmware image size */
+ u64 virtual_guest_firmware_image_size;
+
+ /* GPA = 1MB where EFI firmware image is copied to */
+ GUEST_PHYSICAL_ADDRESS virtual_guest_firmware_boot_base;
+ GUEST_PHYSICAL_ADDRESS virtual_guest_image_base;
+ GUEST_PHYSICAL_ADDRESS virtual_guest_image_size;
+ u64 prototype_control_channel_offset;
+ GUEST_PHYSICAL_ADDRESS virtual_guest_partition_handle;
+
+ u16 restore_action; /* Restore Action field to restore the guest
+ * partition */
+ u16 dump_action; /* For Windows guests it shows if the visordisk
+ * is running in dump mode */
+ u16 nvram_fail_count;
+ u16 saved_crash_message_count; /* = CONTROLVM_CRASHMSG_MAX */
+ u32 saved_crash_message_offset; /* Offset to request payload area needed
+ * for crash dump */
+ u32 installation_error; /* Type of error encountered during
+ * installation */
+ u32 installation_text_id; /* Id of string to display */
+ u16 installation_remaining_steps;/* Number of remaining installation
+ * steps (for progress bars) */
+ u8 tool_action; /* ULTRA_TOOL_ACTIONS Installation Action
+ * field */
+ u8 reserved; /* alignment */
+ struct efi_spar_indication efi_spar_ind;
+ struct efi_spar_indication efi_spar_ind_supported;
+ u32 sp_reserved;
+ u8 reserved2[28]; /* Force signals to begin on 128-byte cache
+ * line */
+ struct signal_queue_header request_queue;/* Service or guest partition
+ * uses this queue to send
+ * requests to Control */
+ struct signal_queue_header response_queue;/* Control uses this queue to
+ * respond to service or guest
+ * partition requests */
+ struct signal_queue_header event_queue; /* Control uses this queue to
+ * send events to service or
+ * guest partition */
+ struct signal_queue_header event_ack_queue;/* Service or guest partition
+ * uses this queue to ack
+ * Control events */
+
+ /* Request fixed-size message pool - does not include payload */
+ struct controlvm_message request_msg[CONTROLVM_MESSAGE_MAX];
+
+ /* Response fixed-size message pool - does not include payload */
+ struct controlvm_message response_msg[CONTROLVM_MESSAGE_MAX];
+
+ /* Event fixed-size message pool - does not include payload */
+ struct controlvm_message event_msg[CONTROLVM_MESSAGE_MAX];
+
+ /* Ack fixed-size message pool - does not include payload */
+ struct controlvm_message event_ack_msg[CONTROLVM_MESSAGE_MAX];
+
+ /* Message stored during IOVM creation to be reused after crash */
+ struct controlvm_message saved_crash_msg[CONTROLVM_CRASHMSG_MAX];
+};
+
+/* Offsets for VM channel attributes... */
+#define VM_CH_REQ_QUEUE_OFFSET \
+ offsetof(struct spar_controlvm_channel_protocol, request_queue)
+#define VM_CH_RESP_QUEUE_OFFSET \
+ offsetof(struct spar_controlvm_channel_protocol, response_queue)
+#define VM_CH_EVENT_QUEUE_OFFSET \
+ offsetof(struct spar_controlvm_channel_protocol, event_queue)
+#define VM_CH_ACK_QUEUE_OFFSET \
+ offsetof(struct spar_controlvm_channel_protocol, event_ack_queue)
+#define VM_CH_REQ_MSG_OFFSET \
+ offsetof(struct spar_controlvm_channel_protocol, request_msg)
+#define VM_CH_RESP_MSG_OFFSET \
+ offsetof(struct spar_controlvm_channel_protocol, response_msg)
+#define VM_CH_EVENT_MSG_OFFSET \
+ offsetof(struct spar_controlvm_channel_protocol, event_msg)
+#define VM_CH_ACK_MSG_OFFSET \
+ offsetof(struct spar_controlvm_channel_protocol, event_ack_msg)
+#define VM_CH_CRASH_MSG_OFFSET \
+ offsetof(struct spar_controlvm_channel_protocol, saved_crash_msg)
+
+/* The following header will be located at the beginning of PayloadVmOffset for
+ * various ControlVm commands. The receiver of a ControlVm command with a
+ * PayloadVmOffset will dereference this address and then use connection_offset,
+ * initiator_offset, and target_offset to get the location of UTF-8 formatted
+ * strings that can be parsed to obtain command-specific information. The value
+ * of total_length should equal PayloadBytes. The format of the strings at
+ * PayloadVmOffset will take different forms depending on the message.
+ */
+struct spar_controlvm_parameters_header {
+ u32 total_length;
+ u32 header_length;
+ u32 connection_offset;
+ u32 connection_length;
+ u32 initiator_offset;
+ u32 initiator_length;
+ u32 target_offset;
+ u32 target_length;
+ u32 client_offset;
+ u32 client_length;
+ u32 name_offset;
+ u32 name_length;
+ uuid_le id;
+ u32 revision;
+ u32 reserved; /* Natural alignment */
+};
+
+#endif /* __CONTROLVMCHANNEL_H__ */
diff --git a/drivers/staging/unisys/common-spar/include/channels/diagchannel.h b/drivers/staging/unisys/common-spar/include/channels/diagchannel.h
new file mode 100644
index 000000000..e8fb8678a
--- /dev/null
+++ b/drivers/staging/unisys/common-spar/include/channels/diagchannel.h
@@ -0,0 +1,427 @@
+/* Copyright (C) 2010 - 2013 UNISYS CORPORATION
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or (at
+ * your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
+ * NON INFRINGEMENT. See the GNU General Public License for more
+ * details.
+ */
+
+/*++
+ *
+ * Module Name:
+ *
+ * diagchannel.h
+ *
+ * Abstract:
+ *
+ * This file defines the DiagChannel protocol. This protocol is used to aid in
+ * preserving event data sent by external applications. This protocol provides
+ * a region for event data to reside in. This data will eventually be sent to
+ * the Boot Partition where it will be committed to memory and/or disk. This
+ * file contains platform-independent data that can be built using any
+ * Supervisor build environment (Windows, Linux, EFI).
+ *
+*/
+
+#ifndef _DIAG_CHANNEL_H_
+#define _DIAG_CHANNEL_H_
+
+#include <linux/uuid.h>
+#include "channel.h"
+
+/* {EEA7A573-DB82-447c-8716-EFBEAAAE4858} */
+#define SPAR_DIAG_CHANNEL_PROTOCOL_UUID \
+ UUID_LE(0xeea7a573, 0xdb82, 0x447c, \
+ 0x87, 0x16, 0xef, 0xbe, 0xaa, 0xae, 0x48, 0x58)
+
+static const uuid_le spar_diag_channel_protocol_uuid =
+ SPAR_DIAG_CHANNEL_PROTOCOL_UUID;
+
+/* {E850F968-3263-4484-8CA5-2A35D087A5A8} */
+#define ULTRA_DIAG_ROOT_CHANNEL_PROTOCOL_GUID \
+ UUID_LE(0xe850f968, 0x3263, 0x4484, \
+ 0x8c, 0xa5, 0x2a, 0x35, 0xd0, 0x87, 0xa5, 0xa8)
+
+#define ULTRA_DIAG_CHANNEL_PROTOCOL_SIGNATURE ULTRA_CHANNEL_PROTOCOL_SIGNATURE
+
+/* Must increment this whenever you insert or delete fields within this channel
+* struct. Also increment whenever you change the meaning of fields within this
+* channel struct so as to break pre-existing software. Note that you can
+* usually add fields to the END of the channel struct withOUT needing to
+* increment this. */
+#define ULTRA_DIAG_CHANNEL_PROTOCOL_VERSIONID 2
+
+#define SPAR_DIAG_CHANNEL_OK_CLIENT(ch)\
+ (spar_check_channel_client(ch,\
+ spar_diag_channel_protocol_uuid,\
+ "diag",\
+ sizeof(struct spar_diag_channel_protocol),\
+ ULTRA_DIAG_CHANNEL_PROTOCOL_VERSIONID,\
+ ULTRA_DIAG_CHANNEL_PROTOCOL_SIGNATURE))
+
+#define SPAR_DIAG_CHANNEL_OK_SERVER(bytes)\
+ (spar_check_channel_server(spar_diag_channel_protocol_uuid,\
+ "diag",\
+ sizeof(struct spar_diag_channel_protocol),\
+ bytes))
+
+#define MAX_MODULE_NAME_SIZE 128 /* Maximum length of module name... */
+#define MAX_ADDITIONAL_INFO_SIZE 256 /* Maximum length of any additional info
+ * accompanying event... */
+#define MAX_SUBSYSTEMS 64 /* Maximum number of subsystems allowed in
+ * DiagChannel... */
+#define LOW_SUBSYSTEMS 32 /* Half of MAX_SUBSYSTEMS to allow 64-bit
+ * math */
+#define SUBSYSTEM_DEBUG 0 /* Standard subsystem for debug events */
+#define SUBSYSTEM_DEFAULT 1 /* Default subsystem for legacy calls to
+ * ReportEvent */
+
+/* few useful subsystem mask values */
+#define SUBSYSTEM_MASK_DEBUG 0x01 /* Standard subsystem for debug
+ * events */
+#define SUBSYSTEM_MASK_DEFAULT 0x02 /* Default subsystem for legacy calls to
+ * ReportEvents */
+
+/* Event parameter "Severity" is overloaded with Cause in byte 2 and Severity in
+ * byte 0, bytes 1 and 3 are reserved */
+#define SEVERITY_MASK 0x0FF /* mask out all but the Severity in byte 0 */
+#define CAUSE_MASK 0x0FF0000 /* mask out all but the cause in byte 2 */
+#define CAUSE_SHIFT_AMT 16 /* shift 2 bytes to place it in byte 2 */
+
+/* SubsystemSeverityFilter */
+#define SEVERITY_FILTER_MASK 0x0F /* mask out the Cause half, SeverityFilter is
+ * in the lower nibble */
+#define CAUSE_FILTER_MASK 0xF0 /* mask out the Severity half, CauseFilter is in
+ * the upper nibble */
+#define CAUSE_FILTER_SHIFT_AMT 4 /* shift amount to place it in lower or upper
+ * nibble */
+
+/* Copied from EFI's EFI_TIME struct in efidef.h. EFI headers are not allowed
+* in some of the Supervisor areas, such as Monitor, so it has been "ported" here
+* for use in diagnostic event timestamps... */
+struct diag_efi_time {
+ u16 year; /* 1998 - 20XX */
+ u8 month; /* 1 - 12 */
+ u8 day; /* 1 - 31 */
+ u8 hour; /* 0 - 23 */
+ u8 minute; /* 0 - 59 */
+ u8 second; /* 0 - 59 */
+ u8 pad1;
+ u32 nanosecond; /* 0 - 999, 999, 999 */
+ s16 timezone; /* -1440 to 1440 or 2047 */
+ u8 daylight;
+ u8 pad2;
+};
+
+enum spar_component_types {
+ ULTRA_COMPONENT_GUEST = 0,
+ ULTRA_COMPONENT_MONITOR = 0x01,
+ ULTRA_COMPONENT_CCM = 0x02, /* Common Control module */
+ /* RESERVED 0x03 - 0x7 */
+
+ /* Ultravisor Components */
+ ULTRA_COMPONENT_BOOT = 0x08,
+ ULTRA_COMPONENT_IDLE = 0x09,
+ ULTRA_COMPONENT_CONTROL = 0x0A,
+ ULTRA_COMPONENT_LOGGER = 0x0B,
+ ULTRA_COMPONENT_ACPI = 0X0C,
+ /* RESERVED 0x0D - 0x0F */
+
+ /* sPAR Components */
+ ULTRA_COMPONENT_COMMAND = 0x10,
+ ULTRA_COMPONENT_IODRIVER = 0x11,
+ ULTRA_COMPONENT_CONSOLE = 0x12,
+ ULTRA_COMPONENT_OPERATIONS = 0x13,
+ ULTRA_COMPONENT_MANAGEMENT = 0x14,
+ ULTRA_COMPONENT_DIAG = 0x15,
+ ULTRA_COMPONENT_HWDIAG = 0x16,
+ ULTRA_COMPONENT_PSERVICES = 0x17,
+ ULTRA_COMPONENT_PDIAG = 0x18
+ /* RESERVED 0x18 - 0x1F */
+};
+
+/* Structure: diag_channel_event Purpose: Contains attributes that make up an
+ * event to be written to the DIAG_CHANNEL memory. Attributes: EventId: Id of
+ * the diagnostic event to write to memory. Severity: Severity of the event
+ * (Error, Info, etc). ModuleName: Module/file name where event originated.
+ * LineNumber: Line number in module name where event originated. Timestamp:
+ * Date/time when event was received by ReportEvent, and written to DiagChannel.
+ * Reserved: Padding to align structure on a 64-byte cache line boundary.
+ * AdditionalInfo: Array of characters for additional event info (may be
+ * empty). */
+struct diag_channel_event {
+ u32 event_id;
+ u32 severity;
+ u8 module_name[MAX_MODULE_NAME_SIZE];
+ u32 line_number;
+ struct diag_efi_time timestamp; /* Size = 16 bytes */
+ u32 partition_number; /* Filled in by Diag Switch as pool blocks are
+ * filled */
+ u16 vcpu_number;
+ u16 lcpu_number;
+ u8 component_type; /* ULTRA_COMPONENT_TYPES */
+ u8 subsystem;
+ u16 reserved0; /* pad to u64 alignment */
+ u32 block_no; /* filled in by DiagSwitch as pool blocks are
+ * filled */
+ u32 block_no_high;
+ u32 event_no; /* filled in by DiagSwitch as pool blocks are
+ * filled */
+ u32 event_no_high;
+
+ /* The block_no and event_no fields are set only by DiagSwitch
+ * and referenced only by WinDiagDisplay formatting tool as
+ * additional diagnostic information. Other tools including
+ * WinDiagDisplay currently ignore these 'Reserved' bytes. */
+ u8 reserved[8];
+ u8 additional_info[MAX_ADDITIONAL_INFO_SIZE];
+
+ /* NOTE: Changes to diag_channel_event generally need to be reflected in
+ * existing copies *
+ * - for AppOS at
+ * GuestLinux/visordiag_early/supervisor_diagchannel.h *
+ * - for WinDiagDisplay at
+ * EFI/Ultra/Tools/WinDiagDisplay/WinDiagDisplay/diagstruct.h */
+};
+
+/* Levels of severity for diagnostic events, in order from lowest severity to
+* highest (i.e. fatal errors are the most severe, and should always be logged,
+* but info events rarely need to be logged except during debugging). The values
+* DIAG_SEVERITY_ENUM_BEGIN and DIAG_SEVERITY_ENUM_END are not valid severity
+* values. They exist merely to dilineate the list, so that future additions
+* won't require changes to the driver (i.e. when checking for out-of-range
+* severities in SetSeverity). The values DIAG_SEVERITY_OVERRIDE and
+* DIAG_SEVERITY_SHUTOFF are not valid severity values for logging events but
+* they are valid for controlling the amount of event data. This enum is also
+* defined in DotNet\sParFramework\ControlFramework\ControlFramework.cs. If a
+* change is made to this enum, they should also be reflected in that file. */
+enum diag_severity {
+ DIAG_SEVERITY_ENUM_BEGIN = 0,
+ DIAG_SEVERITY_OVERRIDE = DIAG_SEVERITY_ENUM_BEGIN,
+ DIAG_SEVERITY_VERBOSE = DIAG_SEVERITY_OVERRIDE, /* 0 */
+ DIAG_SEVERITY_INFO = DIAG_SEVERITY_VERBOSE + 1, /* 1 */
+ DIAG_SEVERITY_WARNING = DIAG_SEVERITY_INFO + 1, /* 2 */
+ DIAG_SEVERITY_ERR = DIAG_SEVERITY_WARNING + 1, /* 3 */
+ DIAG_SEVERITY_PRINT = DIAG_SEVERITY_ERR + 1, /* 4 */
+ DIAG_SEVERITY_SHUTOFF = DIAG_SEVERITY_PRINT + 1, /* 5 */
+ DIAG_SEVERITY_ENUM_END = DIAG_SEVERITY_SHUTOFF, /* 5 */
+ DIAG_SEVERITY_NONFATAL_ERR = DIAG_SEVERITY_ERR,
+ DIAG_SEVERITY_FATAL_ERR = DIAG_SEVERITY_PRINT
+};
+
+/* Event Cause enums
+*
+* Levels of cause for diagnostic events, in order from least to greatest cause
+* Internal errors are most urgent since ideally they should never exist
+* Invalid requests are preventable by avoiding invalid inputs
+* Operations errors depend on environmental factors which may impact which
+* requests are possible
+* Manifest provides intermediate value to capture firmware and configuration
+* version information
+* Trace provides suplimental debug information in release firmware
+* Unknown Log captures unclasified LogEvent calls.
+* Debug is the least urgent since it provides suplimental debug information only
+* in debug firmware
+* Unknown Debug captures unclassified DebugEvent calls.
+* This enum is also defined in
+* DotNet\sParFramework\ControlFramework\ControlFramework.cs.
+* If a change is made to this enum, they should also be reflected in that
+* file. */
+
+/* A cause value "DIAG_CAUSE_FILE_XFER" together with a severity value of
+* "DIAG_SEVERITY_PRINT" (=4), is used for transferring text or binary file to
+* the Diag partition. This cause-severity combination will be used by Logger
+* DiagSwitch to segregate events into block types. The files are transferred in
+* 256 byte chunks maximum, in the AdditionalInfo field of the diag_channel_event
+* structure. In the file transfer mode, some event fields will have different
+* meaning: EventId specifies the file offset, severity specifies the block type,
+* ModuleName specifies the filename, LineNumber specifies the number of valid
+* data bytes in an event and AdditionalInfo contains up to 256 bytes of data. */
+
+/* The Diag DiagWriter appends event blocks to events.raw as today, and for data
+ * blocks uses diag_channel_event
+ * PartitionNumber to extract and append 'AdditionalInfo' to filename (specified
+ * by ModuleName). */
+
+/* The Dell PDiag uses this new mechanism to stash DSET .zip onto the
+ * 'diagnostic' virtual disk. */
+enum diag_cause {
+ DIAG_CAUSE_UNKNOWN = 0,
+ DIAG_CAUSE_UNKNOWN_DEBUG = DIAG_CAUSE_UNKNOWN + 1, /* 1 */
+ DIAG_CAUSE_DEBUG = DIAG_CAUSE_UNKNOWN_DEBUG + 1, /* 2 */
+ DIAG_CAUSE_UNKNOWN_LOG = DIAG_CAUSE_DEBUG + 1, /* 3 */
+ DIAG_CAUSE_TRACE = DIAG_CAUSE_UNKNOWN_LOG + 1, /* 4 */
+ DIAG_CAUSE_MANIFEST = DIAG_CAUSE_TRACE + 1, /* 5 */
+ DIAG_CAUSE_OPERATIONS_ERROR = DIAG_CAUSE_MANIFEST + 1, /* 6 */
+ DIAG_CAUSE_INVALID_REQUEST = DIAG_CAUSE_OPERATIONS_ERROR + 1, /* 7 */
+ DIAG_CAUSE_INTERNAL_ERROR = DIAG_CAUSE_INVALID_REQUEST + 1, /* 8 */
+ DIAG_CAUSE_FILE_XFER = DIAG_CAUSE_INTERNAL_ERROR + 1, /* 9 */
+ DIAG_CAUSE_ENUM_END = DIAG_CAUSE_FILE_XFER /* 9 */
+};
+
+/* Event Cause category defined into the byte 2 of Severity */
+#define CAUSE_DEBUG (DIAG_CAUSE_DEBUG << CAUSE_SHIFT_AMT)
+#define CAUSE_TRACE (DIAG_CAUSE_TRACE << CAUSE_SHIFT_AMT)
+#define CAUSE_MANIFEST (DIAG_CAUSE_MANIFEST << CAUSE_SHIFT_AMT)
+#define CAUSE_OPERATIONS_ERROR (DIAG_CAUSE_OPERATIONS_ERROR << CAUSE_SHIFT_AMT)
+#define CAUSE_INVALID_REQUEST (DIAG_CAUSE_INVALID_REQUEST << CAUSE_SHIFT_AMT)
+#define CAUSE_INTERNAL_ERROR (DIAG_CAUSE_INTERNAL_ERROR << CAUSE_SHIFT_AMT)
+#define CAUSE_FILE_XFER (DIAG_CAUSE_FILE_XFER << CAUSE_SHIFT_AMT)
+#define CAUSE_ENUM_END CAUSE_FILE_XFER
+
+/* Combine Cause and Severity categories into one */
+#define CAUSE_DEBUG_SEVERITY_VERBOSE \
+ (CAUSE_DEBUG | DIAG_SEVERITY_VERBOSE)
+#define CAUSE_TRACE_SEVERITY_VERBOSE \
+ (CAUSE_TRACE | DIAG_SEVERITY_VERBOSE)
+#define CAUSE_MANIFEST_SEVERITY_VERBOSE\
+ (CAUSE_MANIFEST | DIAG_SEVERITY_VERBOSE)
+#define CAUSE_OPERATIONS_SEVERITY_VERBOSE \
+ (CAUSE_OPERATIONS_ERROR | DIAG_SEVERITY_VERBOSE)
+#define CAUSE_INVALID_SEVERITY_VERBOSE \
+ (CAUSE_INVALID_REQUEST | DIAG_SEVERITY_VERBOSE)
+#define CAUSE_INTERNAL_SEVERITY_VERBOSE \
+ (CAUSE_INTERNAL_ERROR | DIAG_SEVERITY_VERBOSE)
+
+#define CAUSE_DEBUG_SEVERITY_INFO \
+ (CAUSE_DEBUG | DIAG_SEVERITY_INFO)
+#define CAUSE_TRACE_SEVERITY_INFO \
+ (CAUSE_TRACE | DIAG_SEVERITY_INFO)
+#define CAUSE_MANIFEST_SEVERITY_INFO \
+ (CAUSE_MANIFEST | DIAG_SEVERITY_INFO)
+#define CAUSE_OPERATIONS_SEVERITY_INFO \
+ (CAUSE_OPERATIONS_ERROR | DIAG_SEVERITY_INFO)
+#define CAUSE_INVALID_SEVERITY_INFO \
+ (CAUSE_INVALID_REQUEST | DIAG_SEVERITY_INFO)
+#define CAUSE_INTERNAL_SEVERITY_INFO \
+ (CAUSE_INTERNAL_ERROR | DIAG_SEVERITY_INFO)
+
+#define CAUSE_DEBUG_SEVERITY_WARN \
+ (CAUSE_DEBUG | DIAG_SEVERITY_WARNING)
+#define CAUSE_TRACE_SEVERITY_WARN \
+ (CAUSE_TRACE | DIAG_SEVERITY_WARNING)
+#define CAUSE_MANIFEST_SEVERITY_WARN \
+ (CAUSE_MANIFEST | DIAG_SEVERITY_WARNING)
+#define CAUSE_OPERATIONS_SEVERITY_WARN \
+ (CAUSE_OPERATIONS_ERROR | DIAG_SEVERITY_WARNING)
+#define CAUSE_INVALID_SEVERITY_WARN \
+ (CAUSE_INVALID_REQUEST | DIAG_SEVERITY_WARNING)
+#define CAUSE_INTERNAL_SEVERITY_WARN \
+ (CAUSE_INTERNAL_ERROR | DIAG_SEVERITY_WARNING)
+
+#define CAUSE_DEBUG_SEVERITY_ERR \
+ (CAUSE_DEBUG | DIAG_SEVERITY_ERR)
+#define CAUSE_TRACE_SEVERITY_ERR \
+ (CAUSE_TRACE | DIAG_SEVERITY_ERR)
+#define CAUSE_MANIFEST_SEVERITY_ERR \
+ (CAUSE_MANIFEST | DIAG_SEVERITY_ERR)
+#define CAUSE_OPERATIONS_SEVERITY_ERR \
+ (CAUSE_OPERATIONS_ERROR | DIAG_SEVERITY_ERR)
+#define CAUSE_INVALID_SEVERITY_ERR \
+ (CAUSE_INVALID_REQUEST | DIAG_SEVERITY_ERR)
+#define CAUSE_INTERNAL_SEVERITY_ERR \
+ (CAUSE_INTERNAL_ERROR | DIAG_SEVERITY_ERR)
+
+#define CAUSE_DEBUG_SEVERITY_PRINT \
+ (CAUSE_DEBUG | DIAG_SEVERITY_PRINT)
+#define CAUSE_TRACE_SEVERITY_PRINT \
+ (CAUSE_TRACE | DIAG_SEVERITY_PRINT)
+#define CAUSE_MANIFEST_SEVERITY_PRINT \
+ (CAUSE_MANIFEST | DIAG_SEVERITY_PRINT)
+#define CAUSE_OPERATIONS_SEVERITY_PRINT \
+ (CAUSE_OPERATIONS_ERROR | DIAG_SEVERITY_PRINT)
+#define CAUSE_INVALID_SEVERITY_PRINT \
+ (CAUSE_INVALID_REQUEST | DIAG_SEVERITY_PRINT)
+#define CAUSE_INTERNAL_SEVERITY_PRINT \
+ (CAUSE_INTERNAL_ERROR | DIAG_SEVERITY_PRINT)
+#define CAUSE_FILE_XFER_SEVERITY_PRINT \
+ (CAUSE_FILE_XFER | DIAG_SEVERITY_PRINT)
+
+/* Structure: diag_channel_protocol_header
+ *
+ * Purpose: Contains attributes that make up the header specific to the
+ * DIAG_CHANNEL area.
+ *
+ * Attributes:
+ *
+ * DiagLock: Diag Channel spinlock.
+ *
+ *IsChannelInitialized: 1 iff SignalInit was called for this channel; otherwise
+ * 0, and assume the channel is not ready for use yet.
+ *
+ * Reserved: Padding to align the fields in this structure.
+ *
+ *SubsystemSeverityFilter: Level of severity on a subsystem basis that controls
+ * whether events are logged. Any event's severity for a
+ * particular subsystem below this level will be discarded.
+ */
+struct diag_channel_protocol_header {
+ u32 diag_lock;
+ u8 channel_initialized;
+ u8 reserved[3];
+ u8 subsystem_severity_filter[64];
+};
+
+/* The Diagram for the Diagnostic Channel: */
+/* ----------------------- */
+/* | Channel Header | Defined by ULTRA_CHANNEL_PROTOCOL */
+/* ----------------------- */
+/* | Signal Queue Header | Defined by SIGNAL_QUEUE_HEADER */
+/* ----------------------- */
+/* | DiagChannel Header | Defined by diag_channel_protocol_header */
+/* ----------------------- */
+/* | Channel Event Info | Defined by diag_channel_event*MAX_EVENTS */
+/* ----------------------- */
+/* | Reserved | Reserved (pad out to 4MB) */
+/* ----------------------- */
+
+/* Offsets/sizes for diagnostic channel attributes... */
+#define DIAG_CH_QUEUE_HEADER_OFFSET (sizeof(struct channel_header))
+#define DIAG_CH_QUEUE_HEADER_SIZE (sizeof(struct signal_queue_header))
+#define DIAG_CH_PROTOCOL_HEADER_OFFSET \
+ (DIAG_CH_QUEUE_HEADER_OFFSET + DIAG_CH_QUEUE_HEADER_SIZE)
+#define DIAG_CH_PROTOCOL_HEADER_SIZE \
+ (sizeof(struct diag_channel_protocol_header))
+#define DIAG_CH_EVENT_OFFSET \
+ (DIAG_CH_PROTOCOL_HEADER_OFFSET + DIAG_CH_PROTOCOL_HEADER_SIZE)
+#define DIAG_CH_SIZE (4096 * 1024)
+
+/* For Control and Idle Partitions with larger (8 MB) diagnostic(root)
+ * channels */
+#define DIAG_CH_LRG_SIZE (2 * DIAG_CH_SIZE) /* 8 MB */
+
+/*
+ * Structure: spar_diag_channel_protocol
+ *
+ * Purpose: Contains attributes that make up the DIAG_CHANNEL memory.
+ *
+ * Attributes:
+ *
+ * CommonChannelHeader: Header info common to all channels.
+ *
+ * QueueHeader: Queue header common to all channels - used to determine where to
+ * store event.
+ *
+ * DiagChannelHeader: Diagnostic channel header info (see
+ * diag_channel_protocol_header comments).
+ *
+ * Events: Area where diagnostic events (up to MAX_EVENTS) are written.
+ *
+ *Reserved: Reserved area to allow for correct channel size padding.
+*/
+struct spar_diag_channel_protocol {
+ struct channel_header common_channel_header;
+ struct signal_queue_header queue_header;
+ struct diag_channel_protocol_header diag_channel_header;
+ struct diag_channel_event events[(DIAG_CH_SIZE - DIAG_CH_EVENT_OFFSET) /
+ sizeof(struct diag_channel_event)];
+};
+
+#endif
diff --git a/drivers/staging/unisys/common-spar/include/channels/iochannel.h b/drivers/staging/unisys/common-spar/include/channels/iochannel.h
new file mode 100644
index 000000000..3bd7579e1
--- /dev/null
+++ b/drivers/staging/unisys/common-spar/include/channels/iochannel.h
@@ -0,0 +1,784 @@
+/* Copyright (C) 2010 - 2013 UNISYS CORPORATION */
+/* All rights reserved. */
+#ifndef __IOCHANNEL_H__
+#define __IOCHANNEL_H__
+
+/*
+* Everything needed for IOPart-GuestPart communication is define in
+* this file. Note: Everything is OS-independent because this file is
+* used by Windows, Linux and possible EFI drivers. */
+
+/*
+* Communication flow between the IOPart and GuestPart uses the channel headers
+* channel state. The following states are currently being used:
+* UNINIT(All Zeroes), CHANNEL_ATTACHING, CHANNEL_ATTACHED, CHANNEL_OPENED
+*
+* additional states will be used later. No locking is needed to switch between
+* states due to the following rules:
+*
+* 1. IOPart is only the only partition allowed to change from UNIT
+* 2. IOPart is only the only partition allowed to change from
+* CHANNEL_ATTACHING
+* 3. GuestPart is only the only partition allowed to change from
+* CHANNEL_ATTACHED
+*
+* The state changes are the following: IOPart sees the channel is in UNINIT,
+* UNINIT -> CHANNEL_ATTACHING (performed only by IOPart)
+* CHANNEL_ATTACHING -> CHANNEL_ATTACHED (performed only by IOPart)
+* CHANNEL_ATTACHED -> CHANNEL_OPENED (performed only by GuestPart)
+*/
+
+#include <linux/uuid.h>
+
+#include "vmcallinterface.h"
+
+#define _ULTRA_CONTROLVM_CHANNEL_INLINE_
+#include <linux/dma-direction.h>
+#include "controlvmchannel.h"
+#include "vbuschannel.h"
+#undef _ULTRA_CONTROLVM_CHANNEL_INLINE_
+#include "channel.h"
+
+/*
+ * CHANNEL Guids
+ */
+
+#include "channel_guid.h"
+
+#define ULTRA_VHBA_CHANNEL_PROTOCOL_SIGNATURE ULTRA_CHANNEL_PROTOCOL_SIGNATURE
+#define ULTRA_VNIC_CHANNEL_PROTOCOL_SIGNATURE ULTRA_CHANNEL_PROTOCOL_SIGNATURE
+#define ULTRA_VSWITCH_CHANNEL_PROTOCOL_SIGNATURE \
+ ULTRA_CHANNEL_PROTOCOL_SIGNATURE
+
+/* Must increment these whenever you insert or delete fields within this channel
+* struct. Also increment whenever you change the meaning of fields within this
+* channel struct so as to break pre-existing software. Note that you can
+* usually add fields to the END of the channel struct withOUT needing to
+* increment this. */
+#define ULTRA_VHBA_CHANNEL_PROTOCOL_VERSIONID 2
+#define ULTRA_VNIC_CHANNEL_PROTOCOL_VERSIONID 2
+#define ULTRA_VSWITCH_CHANNEL_PROTOCOL_VERSIONID 1
+
+#define SPAR_VHBA_CHANNEL_OK_CLIENT(ch) \
+ (spar_check_channel_client(ch, spar_vhba_channel_protocol_uuid, \
+ "vhba", MIN_IO_CHANNEL_SIZE, \
+ ULTRA_VHBA_CHANNEL_PROTOCOL_VERSIONID, \
+ ULTRA_VHBA_CHANNEL_PROTOCOL_SIGNATURE))
+
+#define SPAR_VNIC_CHANNEL_OK_CLIENT(ch) \
+ (spar_check_channel_client(ch, spar_vnic_channel_protocol_uuid, \
+ "vnic", MIN_IO_CHANNEL_SIZE, \
+ ULTRA_VNIC_CHANNEL_PROTOCOL_VERSIONID, \
+ ULTRA_VNIC_CHANNEL_PROTOCOL_SIGNATURE))
+
+/*
+* Everything necessary to handle SCSI & NIC traffic between Guest Partition and
+* IO Partition is defined below. */
+
+/*
+* Defines and enums.
+*/
+
+#define MINNUM(a, b) (((a) < (b)) ? (a) : (b))
+#define MAXNUM(a, b) (((a) > (b)) ? (a) : (b))
+
+/* these define the two queues per data channel between iopart and
+ * ioguestparts */
+#define IOCHAN_TO_IOPART 0 /* used by ioguestpart to 'insert' signals to
+ * iopart */
+#define IOCHAN_FROM_GUESTPART 0 /* used by iopart to 'remove' signals from
+ * ioguestpart - same queue as previous queue */
+
+#define IOCHAN_TO_GUESTPART 1 /* used by iopart to 'insert' signals to
+ * ioguestpart */
+#define IOCHAN_FROM_IOPART 1 /* used by ioguestpart to 'remove' signals from
+ * iopart - same queue as previous queue */
+
+/* these define the two queues per control channel between controlpart and "its"
+ * guests, which includes the iopart */
+#define CTRLCHAN_TO_CTRLGUESTPART 0 /* used by ctrlguestpart to 'insert' signals
+ * to ctrlpart */
+#define CTLRCHAN_FROM_CTRLPART 0 /* used by ctrlpart to 'remove' signals from
+ * ctrlquestpart - same queue as previous
+ * queue */
+
+#define CTRLCHAN_TO_CTRLPART 1 /* used by ctrlpart to 'insert' signals to
+ * ctrlguestpart */
+#define CTRLCHAN_FROM_CTRLGUESTPART 1 /* used by ctrguestpart to 'remove'
+ * signals from ctrlpart - same queue as
+ * previous queue */
+
+/* these define the Event & Ack queues per control channel Events are generated
+* by CTRLGUESTPART and sent to CTRLPART; Acks are generated by CTRLPART and sent
+* to CTRLGUESTPART. */
+#define CTRLCHAN_EVENT_TO_CTRLPART 2 /* used by ctrlguestpart to 'insert' Events
+ * to ctrlpart */
+#define CTRLCHAN_EVENT_FROM_CTRLGUESTPART 2 /* used by ctrlpart to 'remove'
+ * Events from ctrlguestpart */
+
+#define CTRLCHAN_ACK_TO_CTRLGUESTPART 3 /* used by ctrlpart to 'insert' Acks to
+ * ctrlguestpart */
+#define CTRLCHAN_ACK_FROM_CTRLPART 3 /* used by ctrlguestpart to 'remove' Events
+ * from ctrlpart */
+
+/* size of cdb - i.e., scsi cmnd */
+#define MAX_CMND_SIZE 16
+
+#define MAX_SENSE_SIZE 64
+
+#define MAX_PHYS_INFO 64
+
+/* Because GuestToGuestCopy is limited to 4KiB segments, and we have limited the
+* Emulex Driver to 256 scatter list segments via the lpfc_sg_seg_cnt parameter
+* to 256, the maximum I/O size is limited to 256 * 4 KiB = 1 MB */
+#define MAX_IO_SIZE (1024*1024) /* 1 MB */
+
+/* NOTE 1: lpfc defines its support for segments in
+* #define LPFC_SG_SEG_CNT 64
+*
+* NOTE 2: In Linux, frags array in skb is currently allocated to be
+* MAX_SKB_FRAGS size, which is 18 which is smaller than MAX_PHYS_INFO for
+* now. */
+
+#ifndef MAX_SERIAL_NUM
+#define MAX_SERIAL_NUM 32
+#endif /* MAX_SERIAL_NUM */
+
+#define MAX_SCSI_BUSES 1
+#define MAX_SCSI_TARGETS 8
+#define MAX_SCSI_LUNS 16
+#define MAX_SCSI_FROM_HOST 0xFFFFFFFF /* Indicator to use Physical HBA
+ * SCSI Host value */
+
+/* various types of network packets that can be sent in cmdrsp */
+enum net_types {
+ NET_RCV_POST = 0, /* submit buffer to hold receiving
+ * incoming packet */
+ /* virtnic -> uisnic */
+ NET_RCV, /* incoming packet received */
+ /* uisnic -> virtpci */
+ NET_XMIT, /* for outgoing net packets */
+ /* virtnic -> uisnic */
+ NET_XMIT_DONE, /* outgoing packet xmitted */
+ /* uisnic -> virtpci */
+ NET_RCV_ENBDIS, /* enable/disable packet reception */
+ /* virtnic -> uisnic */
+ NET_RCV_ENBDIS_ACK, /* acknowledge enable/disable packet
+ * reception */
+ /* uisnic -> virtnic */
+ NET_RCV_PROMISC, /* enable/disable promiscuous mode */
+ /* virtnic -> uisnic */
+ NET_CONNECT_STATUS, /* indicate the loss or restoration of a network
+ * connection */
+ /* uisnic -> virtnic */
+ NET_MACADDR, /* indicates the client has requested to update
+ * its MAC addr */
+ NET_MACADDR_ACK, /* MAC address */
+
+};
+
+#define ETH_HEADER_SIZE 14 /* size of ethernet header */
+
+#define ETH_MIN_DATA_SIZE 46 /* minimum eth data size */
+#define ETH_MIN_PACKET_SIZE (ETH_HEADER_SIZE + ETH_MIN_DATA_SIZE)
+
+#define ETH_DEF_DATA_SIZE 1500 /* default data size */
+#define ETH_DEF_PACKET_SIZE (ETH_HEADER_SIZE + ETH_DEF_DATA_SIZE)
+
+#define ETH_MAX_MTU 16384 /* maximum data size */
+
+#ifndef MAX_MACADDR_LEN
+#define MAX_MACADDR_LEN 6 /* number of bytes in MAC address */
+#endif /* MAX_MACADDR_LEN */
+
+#define ETH_IS_LOCALLY_ADMINISTERED(address) \
+ (((u8 *)(address))[0] & ((u8)0x02))
+#define NIC_VENDOR_ID 0x0008000B
+
+/* various types of scsi task mgmt commands */
+enum task_mgmt_types {
+ TASK_MGMT_ABORT_TASK = 1,
+ TASK_MGMT_BUS_RESET,
+ TASK_MGMT_LUN_RESET,
+ TASK_MGMT_TARGET_RESET,
+};
+
+/* various types of vdisk mgmt commands */
+enum vdisk_mgmt_types {
+ VDISK_MGMT_ACQUIRE = 1,
+ VDISK_MGMT_RELEASE,
+};
+
+/* this is used in the vdest field */
+#define VDEST_ALL 0xFFFF
+
+#define MIN_NUMSIGNALS 64
+#define MAX_NUMSIGNALS 4096
+
+/* MAX_NET_RCV_BUF specifies the number of rcv buffers that are created by each
+* guest's virtnic and posted to uisnic. Uisnic, for each channel, keeps the rcv
+* buffers posted and uses them to receive data on behalf of the guest's virtnic.
+* NOTE: the num_rcv_bufs is configurable for each VNIC. So the following is
+* simply an upperlimit on what each VNIC can provide. Setting it to half of the
+* NUMSIGNALS to prevent queue full deadlocks */
+#define MAX_NET_RCV_BUFS (MIN_NUMSIGNALS / 2)
+
+/*
+ * structs with pragma pack */
+
+/* ///////////// BEGIN PRAGMA PACK PUSH 1 ///////////////////////// */
+/* ///////////// ONLY STRUCT TYPE SHOULD BE BELOW */
+
+#pragma pack(push, 1)
+
+struct guest_phys_info {
+ u64 address;
+ u64 length;
+};
+
+#define GPI_ENTRIES_PER_PAGE (PAGE_SIZE / sizeof(struct guest_phys_info))
+
+struct uisscsi_dest {
+ u32 channel; /* channel == bus number */
+ u32 id; /* id == target number */
+ u32 lun; /* lun == logical unit number */
+};
+
+struct vhba_wwnn {
+ u32 wwnn1;
+ u32 wwnn2;
+};
+
+/* WARNING: Values stired in this structure must contain maximum counts (not
+ * maximum values). */
+struct vhba_config_max { /* 20 bytes */
+ u32 max_channel; /* maximum channel for devices attached to this
+ * bus */
+ u32 max_id; /* maximum SCSI ID for devices attached to this
+ * bus */
+ u32 max_lun; /* maximum SCSI LUN for devices attached to this
+ * bus */
+ u32 cmd_per_lun; /* maximum number of outstanding commands per
+ * lun that are allowed at one time */
+ u32 max_io_size; /* maximum io size for devices attached to this
+ * bus */
+ /* max io size is often determined by the resource of the hba. e.g */
+ /* max scatter gather list length * page size / sector size */
+};
+
+struct uiscmdrsp_scsi {
+ void *scsicmd; /* the handle to the cmd that was received -
+ * send it back as is in the rsp packet. */
+ u8 cmnd[MAX_CMND_SIZE]; /* the cdb for the command */
+ u32 bufflen; /* length of data to be transferred out or in */
+ u16 guest_phys_entries; /* Number of entries in scatter-gather (sg)
+ * list */
+ struct guest_phys_info gpi_list[MAX_PHYS_INFO]; /* physical address
+ * information for each
+ * fragment */
+ enum dma_data_direction data_dir; /* direction of the data, if any */
+ struct uisscsi_dest vdest; /* identifies the virtual hba, id,
+ * channel, lun to which cmd was sent */
+
+ /* the following fields are needed to queue the rsp back to cmd
+ * originator */
+ int linuxstat; /* the original Linux status - for use by linux
+ * vdisk code */
+ u8 scsistat; /* the scsi status */
+ u8 addlstat; /* non-scsi status - covers cases like timeout
+ * needed by windows guests */
+#define ADDL_RESET 1
+#define ADDL_TIMEOUT 2
+#define ADDL_INTERNAL_ERROR 3
+#define ADDL_SEL_TIMEOUT 4
+#define ADDL_CMD_TIMEOUT 5
+#define ADDL_BAD_TARGET 6
+#define ADDL_RETRY 7
+
+ /* the following fields are need to determine the result of command */
+ u8 sensebuf[MAX_SENSE_SIZE]; /* sense info in case cmd failed; */
+ /* it holds the sense_data struct; */
+ /* see that struct for details. */
+ void *vdisk; /* contains pointer to the vdisk so that we can clean up
+ * when the IO completes. */
+ int no_disk_result; /* used to return no disk inquiry result */
+ /* when no_disk_result is set to 1, */
+ /* scsi.scsistat is SAM_STAT_GOOD */
+ /* scsi.addlstat is 0 */
+ /* scsi.linuxstat is SAM_STAT_GOOD */
+ /* That is, there is NO error. */
+};
+
+/*
+* Defines to support sending correct inquiry result when no disk is
+* configured. */
+
+/* From SCSI SPC2 -
+ *
+ * If the target is not capable of supporting a device on this logical unit, the
+ * device server shall set this field to 7Fh (PERIPHERAL QUALIFIER set to 011b
+ * and PERIPHERAL DEVICE TYPE set to 1Fh).
+ *
+ *The device server is capable of supporting the specified peripheral device
+ *type on this logical unit. However, the physical device is not currently
+ *connected to this logical unit.
+ */
+
+#define DEV_NOT_PRESENT 0x7f /* old name - compatibility */
+#define DEV_NOT_CAPABLE 0x7f /* peripheral qualifier of 0x3 */
+ /* peripheral type of 0x1f */
+ /* specifies no device but target present */
+
+#define DEV_DISK_CAPABLE_NOT_PRESENT 0x20 /* peripheral qualifier of 0x1 */
+ /* peripheral type of 0 - disk */
+ /* specifies device capable, but not present */
+
+#define DEV_PROC_CAPABLE_NOT_PRESENT 0x23 /* peripheral qualifier of 0x1 */
+ /* peripheral type of 3 - processor */
+ /* specifies device capable, but not present */
+
+#define DEV_HISUPPORT 0x10 /* HiSup = 1; shows support for report luns */
+ /* must be returned for lun 0. */
+
+/* NOTE: Linux code assumes inquiry contains 36 bytes. Without checking length
+* in buf[4] some linux code accesses bytes beyond 5 to retrieve vendor, product
+* & revision. Yikes! So let us always send back 36 bytes, the minimum for
+* inquiry result. */
+#define NO_DISK_INQUIRY_RESULT_LEN 36
+
+#define MIN_INQUIRY_RESULT_LEN 5 /* we need at least 5 bytes minimum for inquiry
+ * result */
+
+/* SCSI device version for no disk inquiry result */
+#define SCSI_SPC2_VER 4 /* indicates SCSI SPC2 (SPC3 is 5) */
+
+/* Windows and Linux want different things for a non-existent lun. So, we'll let
+ * caller pass in the peripheral qualifier and type.
+ * NOTE:[4] SCSI returns (n-4); so we return length-1-4 or length-5. */
+
+#define SET_NO_DISK_INQUIRY_RESULT(buf, len, lun, lun0notpresent, notpresent) \
+ do { \
+ memset(buf, 0, \
+ MINNUM(len, \
+ (unsigned int)NO_DISK_INQUIRY_RESULT_LEN)); \
+ buf[2] = (u8)SCSI_SPC2_VER; \
+ if (lun == 0) { \
+ buf[0] = (u8)lun0notpresent; \
+ buf[3] = (u8)DEV_HISUPPORT; \
+ } else \
+ buf[0] = (u8)notpresent; \
+ buf[4] = (u8)( \
+ MINNUM(len, \
+ (unsigned int)NO_DISK_INQUIRY_RESULT_LEN) - 5);\
+ if (len >= NO_DISK_INQUIRY_RESULT_LEN) { \
+ buf[8] = 'D'; \
+ buf[9] = 'E'; \
+ buf[10] = 'L'; \
+ buf[11] = 'L'; \
+ buf[16] = 'P'; \
+ buf[17] = 'S'; \
+ buf[18] = 'E'; \
+ buf[19] = 'U'; \
+ buf[20] = 'D'; \
+ buf[21] = 'O'; \
+ buf[22] = ' '; \
+ buf[23] = 'D'; \
+ buf[24] = 'E'; \
+ buf[25] = 'V'; \
+ buf[26] = 'I'; \
+ buf[27] = 'C'; \
+ buf[28] = 'E'; \
+ buf[30] = ' '; \
+ buf[31] = '.'; \
+ } \
+ } while (0)
+
+/*
+* Struct & Defines to support sense information.
+*/
+
+/* The following struct is returned in sensebuf field in uiscmdrsp_scsi. It is
+* initialized in exactly the manner that is recommended in Windows (hence the
+* odd values).
+* When set, these fields will have the following values:
+* ErrorCode = 0x70 indicates current error
+* Valid = 1 indicates sense info is valid
+* SenseKey contains sense key as defined by SCSI specs.
+* AdditionalSenseCode contains sense key as defined by SCSI specs.
+* AdditionalSenseCodeQualifier contains qualifier to sense code as defined by
+* scsi docs.
+* AdditionalSenseLength contains will be sizeof(sense_data)-8=10.
+*/
+struct sense_data {
+ u8 errorcode:7;
+ u8 valid:1;
+ u8 segment_number;
+ u8 sense_key:4;
+ u8 reserved:1;
+ u8 incorrect_length:1;
+ u8 end_of_media:1;
+ u8 file_mark:1;
+ u8 information[4];
+ u8 additional_sense_length;
+ u8 command_specific_information[4];
+ u8 additional_sense_code;
+ u8 additional_sense_code_qualifier;
+ u8 fru_code;
+ u8 sense_key_specific[3];
+};
+
+/* some SCSI ADSENSE codes */
+#ifndef SCSI_ADSENSE_LUN_NOT_READY
+#define SCSI_ADSENSE_LUN_NOT_READY 0x04
+#endif /* */
+#ifndef SCSI_ADSENSE_ILLEGAL_COMMAND
+#define SCSI_ADSENSE_ILLEGAL_COMMAND 0x20
+#endif /* */
+#ifndef SCSI_ADSENSE_ILLEGAL_BLOCK
+#endif /* */
+#ifndef SCSI_ADSENSE_ILLEGAL_BLOCK
+#define SCSI_ADSENSE_ILLEGAL_BLOCK 0x21
+#endif /* */
+#ifndef SCSI_ADSENSE_INVALID_CDB
+#define SCSI_ADSENSE_INVALID_CDB 0x24
+#endif /* */
+#ifndef SCSI_ADSENSE_INVALID_LUN
+#define SCSI_ADSENSE_INVALID_LUN 0x25
+#endif /* */
+#ifndef SCSI_ADWRITE_PROTECT
+#define SCSI_ADWRITE_PROTECT 0x27
+#endif /* */
+#ifndef SCSI_ADSENSE_MEDIUM_CHANGED
+#define SCSI_ADSENSE_MEDIUM_CHANGED 0x28
+#endif /* */
+#ifndef SCSI_ADSENSE_BUS_RESET
+#define SCSI_ADSENSE_BUS_RESET 0x29
+#endif /* */
+#ifndef SCSI_ADSENSE_NO_MEDIA_IN_DEVICE
+#define SCSI_ADSENSE_NO_MEDIA_IN_DEVICE 0x3a
+#endif /* */
+
+struct net_pkt_xmt {
+ int len; /* full length of data in the packet */
+ int num_frags; /* number of fragments in frags containing data */
+ struct phys_info frags[MAX_PHYS_INFO]; /* physical page information for
+ * each fragment */
+ char ethhdr[ETH_HEADER_SIZE]; /* the ethernet header */
+ struct {
+ /* these are needed for csum at uisnic end */
+ u8 valid; /* 1 = rest of this struct is valid - else
+ * ignore */
+ u8 hrawoffv; /* 1 = hwrafoff is valid */
+ u8 nhrawoffv; /* 1 = nhwrafoff is valid */
+ u16 protocol; /* specifies packet protocol */
+ u32 csum; /* value used to set skb->csum at IOPart */
+ u32 hrawoff; /* value used to set skb->h.raw at IOPart */
+ /* hrawoff points to the start of the TRANSPORT LAYER HEADER */
+ u32 nhrawoff; /* value used to set skb->nh.raw at IOPart */
+ /* nhrawoff points to the start of the NETWORK LAYER HEADER */
+ } lincsum;
+
+ /* **** NOTE ****
+ * The full packet is described in frags but the ethernet header is
+ * separately kept in ethhdr so that uisnic doesn't have "MAP" the
+ * guest memory to get to the header. uisnic needs ethhdr to
+ * determine how to route the packet.
+ */
+};
+
+struct net_pkt_xmtdone {
+ u32 xmt_done_result; /* result of NET_XMIT */
+#define XMIT_SUCCESS 0
+#define XMIT_FAILED 1
+};
+
+/* RCVPOST_BUF_SIZe must be at most page_size(4096) - cache_line_size (64) The
+* reason is because dev_skb_alloc which is used to generate RCV_POST skbs in
+* virtnic requires that there is "overhead" in the buffer, and pads 16 bytes. I
+* prefer to use 1 full cache line size for "overhead" so that transfers are
+* better. IOVM requires that a buffer be represented by 1 phys_info structure
+* which can only cover page_size. */
+#define RCVPOST_BUF_SIZE 4032
+#define MAX_NET_RCV_CHAIN \
+ ((ETH_MAX_MTU+ETH_HEADER_SIZE + RCVPOST_BUF_SIZE-1) / RCVPOST_BUF_SIZE)
+
+struct net_pkt_rcvpost {
+ /* rcv buf size must be large enough to include ethernet data len +
+ * ethernet header len - we are choosing 2K because it is guaranteed
+ * to be describable */
+ struct phys_info frag; /* physical page information for the
+ * single fragment 2K rcv buf */
+ u64 unique_num; /* This is used to make sure that
+ * receive posts are returned to */
+ /* the Adapter which sent them origonally. */
+};
+
+struct net_pkt_rcv {
+ /* the number of receive buffers that can be chained */
+ /* is based on max mtu and size of each rcv buf */
+ u32 rcv_done_len; /* length of received data */
+ u8 numrcvbufs; /* number of receive buffers that contain the */
+ /* incoming data; guest end MUST chain these together. */
+ void *rcvbuf[MAX_NET_RCV_CHAIN]; /* the list of receive buffers
+ * that must be chained; */
+ /* each entry is a receive buffer provided by NET_RCV_POST. */
+ /* NOTE: first rcvbuf in the chain will also be provided in net.buf. */
+ u64 unique_num;
+ u32 rcvs_dropped_delta;
+};
+
+struct net_pkt_enbdis {
+ void *context;
+ u16 enable; /* 1 = enable, 0 = disable */
+};
+
+struct net_pkt_macaddr {
+ void *context;
+ u8 macaddr[MAX_MACADDR_LEN]; /* 6 bytes */
+};
+
+/* cmd rsp packet used for VNIC network traffic */
+struct uiscmdrsp_net {
+ enum net_types type;
+ void *buf;
+ union {
+ struct net_pkt_xmt xmt; /* used for NET_XMIT */
+ struct net_pkt_xmtdone xmtdone; /* used for NET_XMIT_DONE */
+ struct net_pkt_rcvpost rcvpost; /* used for NET_RCV_POST */
+ struct net_pkt_rcv rcv; /* used for NET_RCV */
+ struct net_pkt_enbdis enbdis; /* used for NET_RCV_ENBDIS, */
+ /* NET_RCV_ENBDIS_ACK, */
+ /* NET_RCV_PROMSIC, */
+ /* and NET_CONNECT_STATUS */
+ struct net_pkt_macaddr macaddr;
+ };
+};
+
+struct uiscmdrsp_scsitaskmgmt {
+ enum task_mgmt_types tasktype;
+
+ /* the type of task */
+ struct uisscsi_dest vdest;
+
+ /* the vdisk for which this task mgmt is generated */
+ void *scsicmd;
+
+ /* This is some handle that the guest has saved off for its own use.
+ * Its value is preserved by iopart & returned as is in the task mgmt
+ * rsp. */
+ void *notify;
+
+ /* For linux guests, this is a pointer to wait_queue_head that a
+ * thread is waiting on to see if the taskmgmt command has completed.
+ * For windows guests, this is a pointer to a location that a waiting
+ * thread is testing to see if the taskmgmt command has completed.
+ * When the rsp is received by guest, the thread receiving the
+ * response uses this to notify the thread waiting for taskmgmt
+ * command completion. Its value is preserved by iopart & returned
+ * as is in the task mgmt rsp. */
+ void *notifyresult;
+
+ /* this is a handle to location in guest where the result of the
+ * taskmgmt command (result field) is to saved off when the response
+ * is handled. Its value is preserved by iopart & returned as is in
+ * the task mgmt rsp. */
+ char result;
+
+ /* result of taskmgmt command - set by IOPart - values are: */
+#define TASK_MGMT_FAILED 0
+#define TASK_MGMT_SUCCESS 1
+};
+
+/* The following is used by uissd to send disk add/remove notifications to
+ * Guest */
+/* Note that the vHba pointer is not used by the Client/Guest side. */
+struct uiscmdrsp_disknotify {
+ u8 add; /* 0-remove, 1-add */
+ void *v_hba; /* Pointer to vhba_info for channel info to
+ * route msg */
+ u32 channel, id, lun; /* SCSI Path of Disk to added or removed */
+};
+
+/* The following is used by virthba/vSCSI to send the Acquire/Release commands
+* to the IOVM. */
+struct uiscmdrsp_vdiskmgmt {
+ enum vdisk_mgmt_types vdisktype;
+
+ /* the type of task */
+ struct uisscsi_dest vdest;
+
+ /* the vdisk for which this task mgmt is generated */
+ void *scsicmd;
+
+ /* This is some handle that the guest has saved off for its own use.
+ * Its value is preserved by iopart & returned as is in the task mgmt
+ * rsp. */
+ void *notify;
+
+ /* For linux guests, this is a pointer to wait_queue_head that a
+ * thread is waiting on to see if the taskmgmt command has completed.
+ * For windows guests, this is a pointer to a location that a waiting
+ * thread is testing to see if the taskmgmt command has completed.
+ * When the rsp is received by guest, the thread receiving the
+ * response uses this to notify the thread waiting for taskmgmt
+ * command completion. Its value is preserved by iopart & returned
+ * as is in the task mgmt rsp. */
+ void *notifyresult;
+
+ /* this is a handle to location in guest where the result of the
+ * taskmgmt command (result field) is to saved off when the response
+ * is handled. Its value is preserved by iopart & returned as is in
+ * the task mgmt rsp. */
+ char result;
+
+ /* result of taskmgmt command - set by IOPart - values are: */
+#define VDISK_MGMT_FAILED 0
+#define VDISK_MGMT_SUCCESS 1
+};
+
+/* keeping cmd & rsp info in one structure for now cmd rsp packet for scsi */
+struct uiscmdrsp {
+ char cmdtype;
+
+ /* describes what type of information is in the struct */
+#define CMD_SCSI_TYPE 1
+#define CMD_NET_TYPE 2
+#define CMD_SCSITASKMGMT_TYPE 3
+#define CMD_NOTIFYGUEST_TYPE 4
+#define CMD_VDISKMGMT_TYPE 5
+ union {
+ struct uiscmdrsp_scsi scsi;
+ struct uiscmdrsp_net net;
+ struct uiscmdrsp_scsitaskmgmt scsitaskmgmt;
+ struct uiscmdrsp_disknotify disknotify;
+ struct uiscmdrsp_vdiskmgmt vdiskmgmt;
+ };
+ void *private_data; /* used to send the response when the cmd is
+ * done (scsi & scsittaskmgmt). */
+ struct uiscmdrsp *next; /* General Purpose Queue Link */
+ struct uiscmdrsp *activeQ_next; /* Used to track active commands */
+ struct uiscmdrsp *activeQ_prev; /* Used to track active commands */
+};
+
+/* This is just the header of the IO channel. It is assumed that directly after
+* this header there is a large region of memory which contains the command and
+* response queues as specified in cmd_q and rsp_q SIGNAL_QUEUE_HEADERS. */
+struct spar_io_channel_protocol {
+ struct channel_header channel_header;
+ struct signal_queue_header cmd_q;
+ struct signal_queue_header rsp_q;
+ union {
+ struct {
+ struct vhba_wwnn wwnn; /* 8 bytes */
+ struct vhba_config_max max; /* 20 bytes */
+ } vhba; /* 28 */
+ struct {
+ u8 macaddr[MAX_MACADDR_LEN]; /* 6 bytes */
+ u32 num_rcv_bufs; /* 4 */
+ u32 mtu; /* 4 */
+ uuid_le zone_uuid; /* 16 */
+ } vnic; /* total 30 */
+ };
+
+#define MAX_CLIENTSTRING_LEN 1024
+ u8 client_string[MAX_CLIENTSTRING_LEN];/* NULL terminated - so holds
+ * max - 1 bytes */
+};
+
+#pragma pack(pop)
+/* ///////////// END PRAGMA PACK PUSH 1 /////////////////////////// */
+
+/* define offsets to members of struct uiscmdrsp */
+#define OFFSET_CMDTYPE offsetof(struct uiscmdrsp, cmdtype)
+#define OFFSET_SCSI offsetof(struct uiscmdrsp, scsi)
+#define OFFSET_NET offsetof(struct uiscmdrsp, net)
+#define OFFSET_SCSITASKMGMT offsetof(struct uiscmdrsp, scsitaskmgmt)
+#define OFFSET_NEXT offsetof(struct uiscmdrsp, next)
+
+/* define offsets to members of struct uiscmdrsp_net */
+#define OFFSET_TYPE offsetof(struct uiscmdrsp_net, type)
+#define OFFSET_BUF offsetof(struct uiscmdrsp_net, buf)
+#define OFFSET_XMT offsetof(struct uiscmdrsp_net, xmt)
+#define OFFSET_XMT_DONE_RESULT offsetof(struct uiscmdrsp_net, xmtdone)
+#define OFFSET_RCVPOST offsetof(struct uiscmdrsp_net, rcvpost)
+#define OFFSET_RCV_DONE_LEN offsetof(struct uiscmdrsp_net, rcv)
+#define OFFSET_ENBDIS offsetof(struct uiscmdrsp_net, enbdis)
+
+/* define offsets to members of struct net_pkt_rcvpost */
+#define OFFSET_TOTALLEN offsetof(struct net_pkt_rcvpost, totallen)
+#define OFFSET_FRAG offsetof(struct net_pkt_rcvpost, frag)
+
+/*
+* INLINE functions for initializing and accessing I/O data channels
+*/
+
+#define SIZEOF_PROTOCOL (COVER(sizeof(struct spar_io_channel_protocol), 64))
+#define SIZEOF_CMDRSP (COVER(sizeof(struct uiscmdrsp), 64))
+
+#define MIN_IO_CHANNEL_SIZE COVER(SIZEOF_PROTOCOL + \
+ 2 * MIN_NUMSIGNALS * SIZEOF_CMDRSP, 4096)
+
+/*
+* INLINE function for expanding a guest's pfn-off-size into multiple 4K page
+* pfn-off-size entires.
+*/
+
+/* we deal with 4K page sizes when we it comes to passing page information
+ * between */
+/* Guest and IOPartition. */
+#define PI_PAGE_SIZE 0x1000
+#define PI_PAGE_MASK 0x0FFF
+#define PI_PAGE_SHIFT 12
+
+/* returns next non-zero index on success or zero on failure (i.e. out of
+ * room)
+ */
+static inline u16
+add_physinfo_entries(u32 inp_pfn, /* input - specifies the pfn to be used
+ * to add entries */
+ u16 inp_off, /* input - specifies the off to be used
+ * to add entries */
+ u32 inp_len, /* input - specifies the len to be used
+ * to add entries */
+ u16 index, /* input - index in array at which new
+ * entries are added */
+ u16 max_pi_arr_entries, /* input - specifies the maximum
+ * entries pi_arr can hold */
+ struct phys_info pi_arr[]) /* input & output - array to
+ * which entries are added */
+{
+ u32 len;
+ u16 i, firstlen;
+
+ firstlen = PI_PAGE_SIZE - inp_off;
+ if (inp_len <= firstlen) {
+ /* the input entry spans only one page - add as is */
+ if (index >= max_pi_arr_entries)
+ return 0;
+ pi_arr[index].pi_pfn = inp_pfn;
+ pi_arr[index].pi_off = (u16)inp_off;
+ pi_arr[index].pi_len = (u16)inp_len;
+ return index + 1;
+ }
+
+ /* this entry spans multiple pages */
+ for (len = inp_len, i = 0; len;
+ len -= pi_arr[index + i].pi_len, i++) {
+ if (index + i >= max_pi_arr_entries)
+ return 0;
+ pi_arr[index + i].pi_pfn = inp_pfn + i;
+ if (i == 0) {
+ pi_arr[index].pi_off = inp_off;
+ pi_arr[index].pi_len = firstlen;
+ }
+
+ else {
+ pi_arr[index + i].pi_off = 0;
+ pi_arr[index + i].pi_len =
+ (u16)MINNUM(len, (u32)PI_PAGE_SIZE);
+ }
+ }
+ return index + i;
+}
+
+#endif /* __IOCHANNEL_H__ */
diff --git a/drivers/staging/unisys/common-spar/include/channels/vbuschannel.h b/drivers/staging/unisys/common-spar/include/channels/vbuschannel.h
new file mode 100644
index 000000000..2c42ce16e
--- /dev/null
+++ b/drivers/staging/unisys/common-spar/include/channels/vbuschannel.h
@@ -0,0 +1,94 @@
+/* Copyright (C) 2010 - 2013 UNISYS CORPORATION
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or (at
+ * your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
+ * NON INFRINGEMENT. See the GNU General Public License for more
+ * details.
+ */
+
+#ifndef __VBUSCHANNEL_H__
+#define __VBUSCHANNEL_H__
+
+/* The vbus channel is the channel area provided via the BUS_CREATE controlvm
+ * message for each virtual bus. This channel area is provided to both server
+ * and client ends of the bus. The channel header area is initialized by
+ * the server, and the remaining information is filled in by the client.
+ * We currently use this for the client to provide various information about
+ * the client devices and client drivers for the server end to see.
+ */
+#include <linux/uuid.h>
+#include "vbusdeviceinfo.h"
+#include "channel.h"
+
+/* {193b331b-c58f-11da-95a9-00e08161165f} */
+#define SPAR_VBUS_CHANNEL_PROTOCOL_UUID \
+ UUID_LE(0x193b331b, 0xc58f, 0x11da, \
+ 0x95, 0xa9, 0x0, 0xe0, 0x81, 0x61, 0x16, 0x5f)
+static const uuid_le spar_vbus_channel_protocol_uuid =
+ SPAR_VBUS_CHANNEL_PROTOCOL_UUID;
+
+#define SPAR_VBUS_CHANNEL_PROTOCOL_SIGNATURE ULTRA_CHANNEL_PROTOCOL_SIGNATURE
+
+/* Must increment this whenever you insert or delete fields within this channel
+* struct. Also increment whenever you change the meaning of fields within this
+* channel struct so as to break pre-existing software. Note that you can
+* usually add fields to the END of the channel struct withOUT needing to
+* increment this. */
+#define SPAR_VBUS_CHANNEL_PROTOCOL_VERSIONID 1
+
+#define SPAR_VBUS_CHANNEL_OK_CLIENT(ch) \
+ spar_check_channel_client(ch, \
+ spar_vbus_channel_protocol_uuid, \
+ "vbus", \
+ sizeof(struct spar_vbus_channel_protocol),\
+ SPAR_VBUS_CHANNEL_PROTOCOL_VERSIONID, \
+ SPAR_VBUS_CHANNEL_PROTOCOL_SIGNATURE)
+
+#define SPAR_VBUS_CHANNEL_OK_SERVER(actual_bytes) \
+ (spar_check_channel_server(spar_vbus_channel_protocol_uuid, \
+ "vbus", \
+ sizeof(struct ultra_vbus_channel_protocol),\
+ actual_bytes))
+
+#pragma pack(push, 1) /* both GCC and VC now allow this pragma */
+struct spar_vbus_headerinfo {
+ u32 struct_bytes; /* size of this struct in bytes */
+ u32 device_info_struct_bytes; /* sizeof(ULTRA_VBUS_DEVICEINFO) */
+ u32 dev_info_count; /* num of items in DevInfo member */
+ /* (this is the allocated size) */
+ u32 chp_info_offset; /* byte offset from beginning of this struct */
+ /* to the ChpInfo struct (below) */
+ u32 bus_info_offset; /* byte offset from beginning of this struct */
+ /* to the BusInfo struct (below) */
+ u32 dev_info_offset; /* byte offset from beginning of this struct */
+ /* to the DevInfo array (below) */
+ u8 reserved[104];
+};
+
+struct spar_vbus_channel_protocol {
+ struct channel_header channel_header; /* initialized by server */
+ struct spar_vbus_headerinfo hdr_info; /* initialized by server */
+ /* the remainder of this channel is filled in by the client */
+ struct ultra_vbus_deviceinfo chp_info;
+ /* describes client chipset device and driver */
+ struct ultra_vbus_deviceinfo bus_info;
+ /* describes client bus device and driver */
+ struct ultra_vbus_deviceinfo dev_info[0];
+ /* describes client device and driver for each device on the bus */
+};
+
+#define VBUS_CH_SIZE_EXACT(MAXDEVICES) \
+ (sizeof(ULTRA_VBUS_CHANNEL_PROTOCOL) + ((MAXDEVICES) * \
+ sizeof(ULTRA_VBUS_DEVICEINFO)))
+#define VBUS_CH_SIZE(MAXDEVICES) COVER(VBUS_CH_SIZE_EXACT(MAXDEVICES), 4096)
+
+#pragma pack(pop)
+
+#endif
diff --git a/drivers/staging/unisys/common-spar/include/controlvmcompletionstatus.h b/drivers/staging/unisys/common-spar/include/controlvmcompletionstatus.h
new file mode 100644
index 000000000..f74f5d8c2
--- /dev/null
+++ b/drivers/staging/unisys/common-spar/include/controlvmcompletionstatus.h
@@ -0,0 +1,94 @@
+/* controlvmcompletionstatus.c
+ *
+ * Copyright (C) 2010 - 2013 UNISYS CORPORATION
+ * All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or (at
+ * your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
+ * NON INFRINGEMENT. See the GNU General Public License for more
+ * details.
+ */
+
+/* Defines for all valid values returned in the response message header
+ * completionStatus field. See controlvmchannel.h for description of
+ * the header: _CONTROLVM_MESSAGE_HEADER.
+ */
+
+#ifndef __CONTROLVMCOMPLETIONSTATUS_H__
+#define __CONTROLVMCOMPLETIONSTATUS_H__
+
+/* General Errors------------------------------------------------------[0-99] */
+#define CONTROLVM_RESP_SUCCESS 0
+#define CONTROLVM_RESP_ERROR_ALREADY_DONE 1
+#define CONTROLVM_RESP_ERROR_IOREMAP_FAILED 2
+#define CONTROLVM_RESP_ERROR_KMALLOC_FAILED 3
+#define CONTROLVM_RESP_ERROR_MESSAGE_ID_UNKNOWN 4
+#define CONTROLVM_RESP_ERROR_MESSAGE_ID_INVALID_FOR_CLIENT 5
+
+/* CONTROLVM_INIT_CHIPSET-------------------------------------------[100-199] */
+#define CONTROLVM_RESP_ERROR_CLIENT_SWITCHCOUNT_NONZERO 100
+#define CONTROLVM_RESP_ERROR_EXPECTED_CHIPSET_INIT 101
+
+/* Maximum Limit----------------------------------------------------[200-299] */
+#define CONTROLVM_RESP_ERROR_MAX_BUSES 201 /* BUS_CREATE */
+#define CONTROLVM_RESP_ERROR_MAX_DEVICES 202 /* DEVICE_CREATE */
+/* Payload and Parameter Related------------------------------------[400-499] */
+#define CONTROLVM_RESP_ERROR_PAYLOAD_INVALID 400 /* SWITCH_ATTACHEXTPORT,
+ * DEVICE_CONFIGURE */
+#define CONTROLVM_RESP_ERROR_INITIATOR_PARAMETER_INVALID 401 /* Multiple */
+#define CONTROLVM_RESP_ERROR_TARGET_PARAMETER_INVALID 402 /* DEVICE_CONFIGURE */
+#define CONTROLVM_RESP_ERROR_CLIENT_PARAMETER_INVALID 403 /* DEVICE_CONFIGURE */
+/* Specified[Packet Structure] Value-------------------------------[500-599] */
+#define CONTROLVM_RESP_ERROR_BUS_INVALID 500 /* SWITCH_ATTACHINTPORT,
+ * BUS_CONFIGURE,
+ * DEVICE_CREATE,
+ * DEVICE_CONFIG
+ * DEVICE_DESTROY */
+#define CONTROLVM_RESP_ERROR_DEVICE_INVALID 501 /* SWITCH_ATTACHINTPORT */
+ /* DEVICE_CREATE,
+ * DEVICE_CONFIGURE,
+ * DEVICE_DESTROY */
+#define CONTROLVM_RESP_ERROR_CHANNEL_INVALID 502 /* DEVICE_CREATE,
+ * DEVICE_CONFIGURE */
+/* Partition Driver Callback Interface----------------------[600-699] */
+#define CONTROLVM_RESP_ERROR_VIRTPCI_DRIVER_FAILURE 604 /* BUS_CREATE,
+ * BUS_DESTROY,
+ * DEVICE_CREATE,
+ * DEVICE_DESTROY */
+/* Unable to invoke VIRTPCI callback */
+#define CONTROLVM_RESP_ERROR_VIRTPCI_DRIVER_CALLBACK_ERROR 605
+ /* BUS_CREATE,
+ * BUS_DESTROY,
+ * DEVICE_CREATE,
+ * DEVICE_DESTROY */
+/* VIRTPCI Callback returned error */
+#define CONTROLVM_RESP_ERROR_GENERIC_DRIVER_CALLBACK_ERROR 606
+ /* SWITCH_ATTACHEXTPORT,
+ * SWITCH_DETACHEXTPORT
+ * DEVICE_CONFIGURE */
+
+/* generic device callback returned error */
+/* Bus Related------------------------------------------------------[700-799] */
+#define CONTROLVM_RESP_ERROR_BUS_DEVICE_ATTACHED 700 /* BUS_DESTROY */
+/* Channel Related--------------------------------------------------[800-899] */
+#define CONTROLVM_RESP_ERROR_CHANNEL_TYPE_UNKNOWN 800 /* GET_CHANNELINFO,
+ * DEVICE_DESTROY */
+#define CONTROLVM_RESP_ERROR_CHANNEL_SIZE_TOO_SMALL 801 /* DEVICE_CREATE */
+/* Chipset Shutdown Related---------------------------------------[1000-1099] */
+#define CONTROLVM_RESP_ERROR_CHIPSET_SHUTDOWN_FAILED 1000
+#define CONTROLVM_RESP_ERROR_CHIPSET_SHUTDOWN_ALREADY_ACTIVE 1001
+
+/* Chipset Stop Related-------------------------------------------[1100-1199] */
+#define CONTROLVM_RESP_ERROR_CHIPSET_STOP_FAILED_BUS 1100
+#define CONTROLVM_RESP_ERROR_CHIPSET_STOP_FAILED_SWITCH 1101
+
+/* Device Related-------------------------------------------------[1400-1499] */
+#define CONTROLVM_RESP_ERROR_DEVICE_UDEV_TIMEOUT 1400
+
+#endif /* __CONTROLVMCOMPLETIONSTATUS_H__ not defined */
diff --git a/drivers/staging/unisys/common-spar/include/diagnostics/appos_subsystems.h b/drivers/staging/unisys/common-spar/include/diagnostics/appos_subsystems.h
new file mode 100644
index 000000000..18cc9ed27
--- /dev/null
+++ b/drivers/staging/unisys/common-spar/include/diagnostics/appos_subsystems.h
@@ -0,0 +1,310 @@
+/* Copyright (C) 2010 - 2013 UNISYS CORPORATION
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or (at
+ * your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
+ * NON INFRINGEMENT. See the GNU General Public License for more
+ * details.
+ */
+
+/* Please note that this file is to be used ONLY for defining diagnostic
+ * subsystem values for the appos (sPAR Linux service partitions) component.
+ */
+#ifndef __APPOS_SUBSYSTEMS_H__
+#define __APPOS_SUBSYSTEMS_H__
+
+#ifdef __KERNEL__
+#include <linux/kernel.h>
+#include <linux/string.h>
+#else
+#include <stdio.h>
+#include <string.h>
+#endif
+
+static inline char *
+subsys_unknown_to_s(int subsys, char *s, int n)
+{
+ snprintf(s, n, "SUBSYS-%-2.2d", subsys);
+ s[n - 1] = '\0';
+ return s;
+}
+
+#define SUBSYS_TO_MASK(subsys) (1ULL << (subsys))
+
+/* The first SUBSYS_APPOS_MAX subsystems are the same for each AppOS type
+ * (IOVM, SMS, etc.) The rest have unique values for each AppOS type.
+ */
+#define SUBSYS_APPOS_MAX 16
+
+#define SUBSYS_APPOS_DEFAULT 1 /* or "other" */
+#define SUBSYS_APPOS_CHIPSET 2 /* controlvm and other */
+ /* low-level sPAR activity */
+#define SUBSYS_APPOS_BUS 3 /* sPAR bus */
+/* DAK #define SUBSYS_APPOS_DIAG 4 // diagnostics and dump */
+#define SUBSYS_APPOS_CHANNELACCESS 5 /* generic channel access */
+#define SUBSYS_APPOS_NICCLIENT 6 /* virtual NIC client */
+#define SUBSYS_APPOS_HBACLIENT 7 /* virtual HBA client */
+#define SUBSYS_APPOS_CONSOLESERIAL 8 /* sPAR virtual serial console */
+#define SUBSYS_APPOS_UISLIB 9 /* */
+#define SUBSYS_APPOS_VRTCUPDD 10 /* */
+#define SUBSYS_APPOS_WATCHDOG 11 /* watchdog timer and healthcheck */
+#define SUBSYS_APPOS_13 13 /* available */
+#define SUBSYS_APPOS_14 14 /* available */
+#define SUBSYS_APPOS_15 15 /* available */
+#define SUBSYS_APPOS_16 16 /* available */
+static inline char *
+subsys_generic_to_s(int subsys, char *s, int n)
+{
+ switch (subsys) {
+ case SUBSYS_APPOS_DEFAULT:
+ strncpy(s, "APPOS_DEFAULT", n);
+ break;
+ case SUBSYS_APPOS_CHIPSET:
+ strncpy(s, "APPOS_CHIPSET", n);
+ break;
+ case SUBSYS_APPOS_BUS:
+ strncpy(s, "APPOS_BUS", n);
+ break;
+ case SUBSYS_APPOS_CHANNELACCESS:
+ strncpy(s, "APPOS_CHANNELACCESS", n);
+ break;
+ case SUBSYS_APPOS_NICCLIENT:
+ strncpy(s, "APPOS_NICCLIENT", n);
+ break;
+ case SUBSYS_APPOS_HBACLIENT:
+ strncpy(s, "APPOS_HBACLIENT", n);
+ break;
+ case SUBSYS_APPOS_CONSOLESERIAL:
+ strncpy(s, "APPOS_CONSOLESERIAL", n);
+ break;
+ case SUBSYS_APPOS_UISLIB:
+ strncpy(s, "APPOS_UISLIB", n);
+ break;
+ case SUBSYS_APPOS_VRTCUPDD:
+ strncpy(s, "APPOS_VRTCUPDD", n);
+ break;
+ case SUBSYS_APPOS_WATCHDOG:
+ strncpy(s, "APPOS_WATCHDOG", n);
+ break;
+ case SUBSYS_APPOS_13:
+ strncpy(s, "APPOS_13", n);
+ break;
+ case SUBSYS_APPOS_14:
+ strncpy(s, "APPOS_14", n);
+ break;
+ case SUBSYS_APPOS_15:
+ strncpy(s, "APPOS_15", n);
+ break;
+ case SUBSYS_APPOS_16:
+ strncpy(s, "APPOS_16", n);
+ break;
+ default:
+ subsys_unknown_to_s(subsys, s, n);
+ break;
+ }
+ s[n - 1] = '\0';
+ return s;
+}
+
+/* CONSOLE */
+
+#define SUBSYS_CONSOLE_VIDEO (SUBSYS_APPOS_MAX + 1) /* 17 */
+#define SUBSYS_CONSOLE_KBDMOU (SUBSYS_APPOS_MAX + 2) /* 18 */
+#define SUBSYS_CONSOLE_04 (SUBSYS_APPOS_MAX + 4)
+#define SUBSYS_CONSOLE_05 (SUBSYS_APPOS_MAX + 5)
+#define SUBSYS_CONSOLE_06 (SUBSYS_APPOS_MAX + 6)
+#define SUBSYS_CONSOLE_07 (SUBSYS_APPOS_MAX + 7)
+#define SUBSYS_CONSOLE_08 (SUBSYS_APPOS_MAX + 8)
+#define SUBSYS_CONSOLE_09 (SUBSYS_APPOS_MAX + 9)
+#define SUBSYS_CONSOLE_10 (SUBSYS_APPOS_MAX + 10)
+#define SUBSYS_CONSOLE_11 (SUBSYS_APPOS_MAX + 11)
+#define SUBSYS_CONSOLE_12 (SUBSYS_APPOS_MAX + 12)
+#define SUBSYS_CONSOLE_13 (SUBSYS_APPOS_MAX + 13)
+#define SUBSYS_CONSOLE_14 (SUBSYS_APPOS_MAX + 14)
+#define SUBSYS_CONSOLE_15 (SUBSYS_APPOS_MAX + 15)
+#define SUBSYS_CONSOLE_16 (SUBSYS_APPOS_MAX + 16)
+#define SUBSYS_CONSOLE_17 (SUBSYS_APPOS_MAX + 17)
+#define SUBSYS_CONSOLE_18 (SUBSYS_APPOS_MAX + 18)
+#define SUBSYS_CONSOLE_19 (SUBSYS_APPOS_MAX + 19)
+#define SUBSYS_CONSOLE_20 (SUBSYS_APPOS_MAX + 20)
+#define SUBSYS_CONSOLE_21 (SUBSYS_APPOS_MAX + 21)
+#define SUBSYS_CONSOLE_22 (SUBSYS_APPOS_MAX + 22)
+#define SUBSYS_CONSOLE_23 (SUBSYS_APPOS_MAX + 23)
+#define SUBSYS_CONSOLE_24 (SUBSYS_APPOS_MAX + 24)
+#define SUBSYS_CONSOLE_25 (SUBSYS_APPOS_MAX + 25)
+#define SUBSYS_CONSOLE_26 (SUBSYS_APPOS_MAX + 26)
+#define SUBSYS_CONSOLE_27 (SUBSYS_APPOS_MAX + 27)
+#define SUBSYS_CONSOLE_28 (SUBSYS_APPOS_MAX + 28)
+#define SUBSYS_CONSOLE_29 (SUBSYS_APPOS_MAX + 29)
+#define SUBSYS_CONSOLE_30 (SUBSYS_APPOS_MAX + 30)
+#define SUBSYS_CONSOLE_31 (SUBSYS_APPOS_MAX + 31)
+#define SUBSYS_CONSOLE_32 (SUBSYS_APPOS_MAX + 32)
+#define SUBSYS_CONSOLE_33 (SUBSYS_APPOS_MAX + 33)
+#define SUBSYS_CONSOLE_34 (SUBSYS_APPOS_MAX + 34)
+#define SUBSYS_CONSOLE_35 (SUBSYS_APPOS_MAX + 35)
+#define SUBSYS_CONSOLE_36 (SUBSYS_APPOS_MAX + 36)
+#define SUBSYS_CONSOLE_37 (SUBSYS_APPOS_MAX + 37)
+#define SUBSYS_CONSOLE_38 (SUBSYS_APPOS_MAX + 38)
+#define SUBSYS_CONSOLE_39 (SUBSYS_APPOS_MAX + 39)
+#define SUBSYS_CONSOLE_40 (SUBSYS_APPOS_MAX + 40)
+#define SUBSYS_CONSOLE_41 (SUBSYS_APPOS_MAX + 41)
+#define SUBSYS_CONSOLE_42 (SUBSYS_APPOS_MAX + 42)
+#define SUBSYS_CONSOLE_43 (SUBSYS_APPOS_MAX + 43)
+#define SUBSYS_CONSOLE_44 (SUBSYS_APPOS_MAX + 44)
+#define SUBSYS_CONSOLE_45 (SUBSYS_APPOS_MAX + 45)
+#define SUBSYS_CONSOLE_46 (SUBSYS_APPOS_MAX + 46)
+
+static inline char *
+subsys_console_to_s(int subsys, char *s, int n)
+{
+ switch (subsys) {
+ case SUBSYS_CONSOLE_VIDEO:
+ strncpy(s, "CONSOLE_VIDEO", n);
+ break;
+ case SUBSYS_CONSOLE_KBDMOU:
+ strncpy(s, "CONSOLE_KBDMOU", n);
+ break;
+ case SUBSYS_CONSOLE_04:
+ strncpy(s, "CONSOLE_04", n);
+ break;
+ case SUBSYS_CONSOLE_05:
+ strncpy(s, "CONSOLE_05", n);
+ break;
+ case SUBSYS_CONSOLE_06:
+ strncpy(s, "CONSOLE_06", n);
+ break;
+ case SUBSYS_CONSOLE_07:
+ strncpy(s, "CONSOLE_07", n);
+ break;
+ case SUBSYS_CONSOLE_08:
+ strncpy(s, "CONSOLE_08", n);
+ break;
+ case SUBSYS_CONSOLE_09:
+ strncpy(s, "CONSOLE_09", n);
+ break;
+ case SUBSYS_CONSOLE_10:
+ strncpy(s, "CONSOLE_10", n);
+ break;
+ case SUBSYS_CONSOLE_11:
+ strncpy(s, "CONSOLE_11", n);
+ break;
+ case SUBSYS_CONSOLE_12:
+ strncpy(s, "CONSOLE_12", n);
+ break;
+ case SUBSYS_CONSOLE_13:
+ strncpy(s, "CONSOLE_13", n);
+ break;
+ case SUBSYS_CONSOLE_14:
+ strncpy(s, "CONSOLE_14", n);
+ break;
+ case SUBSYS_CONSOLE_15:
+ strncpy(s, "CONSOLE_15", n);
+ break;
+ case SUBSYS_CONSOLE_16:
+ strncpy(s, "CONSOLE_16", n);
+ break;
+ case SUBSYS_CONSOLE_17:
+ strncpy(s, "CONSOLE_17", n);
+ break;
+ case SUBSYS_CONSOLE_18:
+ strncpy(s, "CONSOLE_18", n);
+ break;
+ case SUBSYS_CONSOLE_19:
+ strncpy(s, "CONSOLE_19", n);
+ break;
+ case SUBSYS_CONSOLE_20:
+ strncpy(s, "CONSOLE_20", n);
+ break;
+ case SUBSYS_CONSOLE_21:
+ strncpy(s, "CONSOLE_21", n);
+ break;
+ case SUBSYS_CONSOLE_22:
+ strncpy(s, "CONSOLE_22", n);
+ break;
+ case SUBSYS_CONSOLE_23:
+ strncpy(s, "CONSOLE_23", n);
+ break;
+ case SUBSYS_CONSOLE_24:
+ strncpy(s, "CONSOLE_24", n);
+ break;
+ case SUBSYS_CONSOLE_25:
+ strncpy(s, "CONSOLE_25", n);
+ break;
+ case SUBSYS_CONSOLE_26:
+ strncpy(s, "CONSOLE_26", n);
+ break;
+ case SUBSYS_CONSOLE_27:
+ strncpy(s, "CONSOLE_27", n);
+ break;
+ case SUBSYS_CONSOLE_28:
+ strncpy(s, "CONSOLE_28", n);
+ break;
+ case SUBSYS_CONSOLE_29:
+ strncpy(s, "CONSOLE_29", n);
+ break;
+ case SUBSYS_CONSOLE_30:
+ strncpy(s, "CONSOLE_30", n);
+ break;
+ case SUBSYS_CONSOLE_31:
+ strncpy(s, "CONSOLE_31", n);
+ break;
+ case SUBSYS_CONSOLE_32:
+ strncpy(s, "CONSOLE_32", n);
+ break;
+ case SUBSYS_CONSOLE_33:
+ strncpy(s, "CONSOLE_33", n);
+ break;
+ case SUBSYS_CONSOLE_34:
+ strncpy(s, "CONSOLE_34", n);
+ break;
+ case SUBSYS_CONSOLE_35:
+ strncpy(s, "CONSOLE_35", n);
+ break;
+ case SUBSYS_CONSOLE_36:
+ strncpy(s, "CONSOLE_36", n);
+ break;
+ case SUBSYS_CONSOLE_37:
+ strncpy(s, "CONSOLE_37", n);
+ break;
+ case SUBSYS_CONSOLE_38:
+ strncpy(s, "CONSOLE_38", n);
+ break;
+ case SUBSYS_CONSOLE_39:
+ strncpy(s, "CONSOLE_39", n);
+ break;
+ case SUBSYS_CONSOLE_40:
+ strncpy(s, "CONSOLE_40", n);
+ break;
+ case SUBSYS_CONSOLE_41:
+ strncpy(s, "CONSOLE_41", n);
+ break;
+ case SUBSYS_CONSOLE_42:
+ strncpy(s, "CONSOLE_42", n);
+ break;
+ case SUBSYS_CONSOLE_43:
+ strncpy(s, "CONSOLE_43", n);
+ break;
+ case SUBSYS_CONSOLE_44:
+ strncpy(s, "CONSOLE_44", n);
+ break;
+ case SUBSYS_CONSOLE_45:
+ strncpy(s, "CONSOLE_45", n);
+ break;
+ case SUBSYS_CONSOLE_46:
+ strncpy(s, "CONSOLE_46", n);
+ break;
+ default:
+ subsys_unknown_to_s(subsys, s, n);
+ break;
+ }
+ s[n - 1] = '\0';
+ return s;
+}
+
+#endif
diff --git a/drivers/staging/unisys/common-spar/include/iovmcall_gnuc.h b/drivers/staging/unisys/common-spar/include/iovmcall_gnuc.h
new file mode 100644
index 000000000..57dd93e0c
--- /dev/null
+++ b/drivers/staging/unisys/common-spar/include/iovmcall_gnuc.h
@@ -0,0 +1,49 @@
+/* Copyright (C) 2010 - 2013 UNISYS CORPORATION
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or (at
+ * your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
+ * NON INFRINGEMENT. See the GNU General Public License for more
+ * details.
+ */
+
+/* Linux GCC Version (32-bit and 64-bit) */
+static inline unsigned long
+__unisys_vmcall_gnuc(unsigned long tuple, unsigned long reg_ebx,
+ unsigned long reg_ecx)
+{
+ unsigned long result = 0;
+ unsigned int cpuid_eax, cpuid_ebx, cpuid_ecx, cpuid_edx;
+
+ cpuid(0x00000001, &cpuid_eax, &cpuid_ebx, &cpuid_ecx, &cpuid_edx);
+ if (!(cpuid_ecx & 0x80000000))
+ return -1;
+
+ __asm__ __volatile__(".byte 0x00f, 0x001, 0x0c1" : "=a"(result) :
+ "a"(tuple), "b"(reg_ebx), "c"(reg_ecx));
+ return result;
+}
+
+static inline unsigned long
+__unisys_extended_vmcall_gnuc(unsigned long long tuple,
+ unsigned long long reg_ebx,
+ unsigned long long reg_ecx,
+ unsigned long long reg_edx)
+{
+ unsigned long result = 0;
+ unsigned int cpuid_eax, cpuid_ebx, cpuid_ecx, cpuid_edx;
+
+ cpuid(0x00000001, &cpuid_eax, &cpuid_ebx, &cpuid_ecx, &cpuid_edx);
+ if (!(cpuid_ecx & 0x80000000))
+ return -1;
+
+ __asm__ __volatile__(".byte 0x00f, 0x001, 0x0c1" : "=a"(result) :
+ "a"(tuple), "b"(reg_ebx), "c"(reg_ecx), "d"(reg_edx));
+ return result;
+}
diff --git a/drivers/staging/unisys/common-spar/include/vbusdeviceinfo.h b/drivers/staging/unisys/common-spar/include/vbusdeviceinfo.h
new file mode 100644
index 000000000..9b6d3e693
--- /dev/null
+++ b/drivers/staging/unisys/common-spar/include/vbusdeviceinfo.h
@@ -0,0 +1,213 @@
+/* Copyright (C) 2010 - 2013 UNISYS CORPORATION
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or (at
+ * your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
+ * NON INFRINGEMENT. See the GNU General Public License for more
+ * details.
+ */
+
+#ifndef __VBUSDEVICEINFO_H__
+#define __VBUSDEVICEINFO_H__
+
+#include <linux/types.h>
+
+#pragma pack(push, 1) /* both GCC and VC now allow this pragma */
+
+/* An array of this struct is present in the channel area for each vbus.
+ * (See vbuschannel.h.)
+ * It is filled in by the client side to provide info about the device
+ * and driver from the client's perspective.
+ */
+struct ultra_vbus_deviceinfo {
+ u8 devtype[16]; /* short string identifying the device type */
+ u8 drvname[16]; /* driver .sys file name */
+ u8 infostrs[96]; /* sequence of tab-delimited id strings: */
+ /* <DRIVER_REV> <DRIVER_VERTAG> <DRIVER_COMPILETIME> */
+ u8 reserved[128]; /* pad size to 256 bytes */
+};
+
+#pragma pack(pop)
+
+/* Reads chars from the buffer at <src> for <srcmax> bytes, and writes to
+ * the buffer at <p>, which is <remain> bytes long, ensuring never to
+ * overflow the buffer at <p>, using the following rules:
+ * - printable characters are simply copied from the buffer at <src> to the
+ * buffer at <p>
+ * - intervening streaks of non-printable characters in the buffer at <src>
+ * are replaced with a single space in the buffer at <p>
+ * Note that we pay no attention to '\0'-termination.
+ * Returns the number of bytes written to <p>.
+ *
+ * Pass <p> == NULL and <remain> == 0 for this special behavior. In this
+ * case, we simply return the number of bytes that WOULD HAVE been written
+ * to a buffer at <p>, had it been infinitely big.
+ */
+static inline int
+vbuschannel_sanitize_buffer(char *p, int remain, char *src, int srcmax)
+{
+ int chars = 0;
+ int nonprintable_streak = 0;
+
+ while (srcmax > 0) {
+ if ((*src >= ' ') && (*src < 0x7f)) {
+ if (nonprintable_streak) {
+ if (remain > 0) {
+ *p = ' ';
+ p++;
+ remain--;
+ chars++;
+ } else if (p == NULL) {
+ chars++;
+ }
+ nonprintable_streak = 0;
+ }
+ if (remain > 0) {
+ *p = *src;
+ p++;
+ remain--;
+ chars++;
+ } else if (p == NULL) {
+ chars++;
+ }
+ } else {
+ nonprintable_streak = 1;
+ }
+ src++;
+ srcmax--;
+ }
+ return chars;
+}
+
+#define VBUSCHANNEL_ADDACHAR(ch, p, remain, chars) \
+ do { \
+ if (remain <= 0) \
+ break; \
+ *p = ch; \
+ p++; chars++; remain--; \
+ } while (0)
+
+/* Converts the non-negative value at <num> to an ascii decimal string
+ * at <p>, writing at most <remain> bytes. Note there is NO '\0' termination
+ * written to <p>.
+ *
+ * Returns the number of bytes written to <p>.
+ *
+ * Note that we create this function because we need to do this operation in
+ * an environment-independent way (since we are in a common header file).
+ */
+static inline int
+vbuschannel_itoa(char *p, int remain, int num)
+{
+ int digits = 0;
+ char s[32];
+ int i;
+
+ if (num == 0) {
+ /* '0' is a special case */
+ if (remain <= 0)
+ return 0;
+ *p = '0';
+ return 1;
+ }
+ /* form a backwards decimal ascii string in <s> */
+ while (num > 0) {
+ if (digits >= (int)sizeof(s))
+ return 0;
+ s[digits++] = (num % 10) + '0';
+ num = num / 10;
+ }
+ if (remain < digits) {
+ /* not enough room left at <p> to hold number, so fill with
+ * '?' */
+ for (i = 0; i < remain; i++, p++)
+ *p = '?';
+ return remain;
+ }
+ /* plug in the decimal ascii string representing the number, by */
+ /* reversing the string we just built in <s> */
+ i = digits;
+ while (i > 0) {
+ i--;
+ *p = s[i];
+ p++;
+ }
+ return digits;
+}
+
+/* Reads <devInfo>, and converts its contents to a printable string at <p>,
+ * writing at most <remain> bytes. Note there is NO '\0' termination
+ * written to <p>.
+ *
+ * Pass <devix> >= 0 if you want a device index presented.
+ *
+ * Returns the number of bytes written to <p>.
+ */
+static inline int
+vbuschannel_devinfo_to_string(struct ultra_vbus_deviceinfo *devinfo,
+ char *p, int remain, int devix)
+{
+ char *psrc;
+ int nsrc, x, i, pad;
+ int chars = 0;
+
+ psrc = &devinfo->devtype[0];
+ nsrc = sizeof(devinfo->devtype);
+ if (vbuschannel_sanitize_buffer(NULL, 0, psrc, nsrc) <= 0)
+ return 0;
+
+ /* emit device index */
+ if (devix >= 0) {
+ VBUSCHANNEL_ADDACHAR('[', p, remain, chars);
+ x = vbuschannel_itoa(p, remain, devix);
+ p += x;
+ remain -= x;
+ chars += x;
+ VBUSCHANNEL_ADDACHAR(']', p, remain, chars);
+ } else {
+ VBUSCHANNEL_ADDACHAR(' ', p, remain, chars);
+ VBUSCHANNEL_ADDACHAR(' ', p, remain, chars);
+ VBUSCHANNEL_ADDACHAR(' ', p, remain, chars);
+ }
+
+ /* emit device type */
+ x = vbuschannel_sanitize_buffer(p, remain, psrc, nsrc);
+ p += x;
+ remain -= x;
+ chars += x;
+ pad = 15 - x; /* pad device type to be exactly 15 chars */
+ for (i = 0; i < pad; i++)
+ VBUSCHANNEL_ADDACHAR(' ', p, remain, chars);
+ VBUSCHANNEL_ADDACHAR(' ', p, remain, chars);
+
+ /* emit driver name */
+ psrc = &devinfo->drvname[0];
+ nsrc = sizeof(devinfo->drvname);
+ x = vbuschannel_sanitize_buffer(p, remain, psrc, nsrc);
+ p += x;
+ remain -= x;
+ chars += x;
+ pad = 15 - x; /* pad driver name to be exactly 15 chars */
+ for (i = 0; i < pad; i++)
+ VBUSCHANNEL_ADDACHAR(' ', p, remain, chars);
+ VBUSCHANNEL_ADDACHAR(' ', p, remain, chars);
+
+ /* emit strings */
+ psrc = &devinfo->infostrs[0];
+ nsrc = sizeof(devinfo->infostrs);
+ x = vbuschannel_sanitize_buffer(p, remain, psrc, nsrc);
+ p += x;
+ remain -= x;
+ chars += x;
+ VBUSCHANNEL_ADDACHAR('\n', p, remain, chars);
+
+ return chars;
+}
+
+#endif
diff --git a/drivers/staging/unisys/common-spar/include/version.h b/drivers/staging/unisys/common-spar/include/version.h
new file mode 100644
index 000000000..83d1da7a2
--- /dev/null
+++ b/drivers/staging/unisys/common-spar/include/version.h
@@ -0,0 +1,45 @@
+/* Copyright (C) 2010 - 2013 UNISYS CORPORATION
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or (at
+ * your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
+ * NON INFRINGEMENT. See the GNU General Public License for more
+ * details.
+ */
+
+/* version.h */
+
+/* Common version/release info needed by all components goes here.
+ * (This file must compile cleanly in all environments.)
+ * Ultimately, this will be combined with defines generated dynamically as
+ * part of the sysgen, and some of the defines below may in fact end up
+ * being replaced with dynamically generated ones.
+ */
+#ifndef __VERSION_H__
+#define __VERSION_H__
+
+#define SPARVER1 "1"
+#define SPARVER2 "0"
+#define SPARVER3 "0"
+#define SPARVER4 "0"
+
+#define VERSION SPARVER1 "." SPARVER2 "." SPARVER3 "." SPARVER4
+
+/* Here are various version forms needed in Windows environments.
+ */
+#define VISOR_PRODUCTVERSION SPARVERCOMMA
+#define VISOR_PRODUCTVERSION_STR SPARVER1 "." SPARVER2 "." SPARVER3 "." \
+ SPARVER4
+#define VISOR_OBJECTVERSION_STR SPARVER1 "," SPARVER2 "," SPARVER3 "," \
+ SPARVER4
+
+#define COPYRIGHT "Unisys Corporation"
+#define COPYRIGHTDATE "2010 - 2013"
+
+#endif
diff --git a/drivers/staging/unisys/common-spar/include/vmcallinterface.h b/drivers/staging/unisys/common-spar/include/vmcallinterface.h
new file mode 100644
index 000000000..59a7459eb
--- /dev/null
+++ b/drivers/staging/unisys/common-spar/include/vmcallinterface.h
@@ -0,0 +1,163 @@
+/* Copyright (C) 2010 - 2013 UNISYS CORPORATION
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or (at
+ * your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
+ * NON INFRINGEMENT. See the GNU General Public License for more
+ * details.
+ */
+
+#ifndef __IOMONINTF_H__
+#define __IOMONINTF_H__
+
+/*
+* This file contains all structures needed to support the VMCALLs for IO
+* Virtualization. The VMCALLs are provided by Monitor and used by IO code
+* running on IO Partitions.
+*/
+
+#ifdef __GNUC__
+#include "iovmcall_gnuc.h"
+#endif /* */
+#include "diagchannel.h"
+
+#ifdef VMCALL_IO_CONTROLVM_ADDR
+#undef VMCALL_IO_CONTROLVM_ADDR
+#endif /* */
+
+/* define subsystem number for AppOS, used in uislib driver */
+#define MDS_APPOS 0x4000000000000000L /* subsystem = 62 - AppOS */
+enum vmcall_monitor_interface_method_tuple { /* VMCALL identification tuples */
+ /* Note: when a new VMCALL is added:
+ * - the 1st 2 hex digits correspond to one of the
+ * VMCALL_MONITOR_INTERFACE types and
+ * - the next 2 hex digits are the nth relative instance of within a
+ * type
+ * E.G. for VMCALL_VIRTPART_RECYCLE_PART,
+ * - the 0x02 identifies it as a VMCALL_VIRTPART type and
+ * - the 0x01 identifies it as the 1st instance of a VMCALL_VIRTPART
+ * type of VMCALL
+ */
+
+ VMCALL_IO_CONTROLVM_ADDR = 0x0501, /* used by all Guests, not just
+ * IO */
+ VMCALL_IO_DIAG_ADDR = 0x0508,
+ VMCALL_IO_VISORSERIAL_ADDR = 0x0509,
+ VMCALL_QUERY_GUEST_VIRTUAL_TIME_OFFSET = 0x0708, /* Allow caller to
+ * query virtual time
+ * offset */
+ VMCALL_CHANNEL_VERSION_MISMATCH = 0x0709,
+ VMCALL_POST_CODE_LOGEVENT = 0x070B, /* LOGEVENT Post Code (RDX) with
+ * specified subsystem mask (RCX
+ * - monitor_subsystems.h) and
+ * severity (RDX) */
+ VMCALL_GENERIC_SURRENDER_QUANTUM_FOREVER = 0x0802, /* Yield the
+ * remainder & all
+ * future quantums of
+ * the caller */
+ VMCALL_MEASUREMENT_DO_NOTHING = 0x0901,
+ VMCALL_UPDATE_PHYSICAL_TIME = 0x0a02 /* Allow
+ * ULTRA_SERVICE_CAPABILITY_TIME
+ * capable guest to make
+ * VMCALL */
+};
+
+#define VMCALL_SUCCESS 0
+#define VMCALL_SUCCESSFUL(result) (result == 0)
+
+#ifdef __GNUC__
+#define unisys_vmcall(tuple, reg_ebx, reg_ecx) \
+ __unisys_vmcall_gnuc(tuple, reg_ebx, reg_ecx)
+#define unisys_extended_vmcall(tuple, reg_ebx, reg_ecx, reg_edx) \
+ __unisys_extended_vmcall_gnuc(tuple, reg_ebx, reg_ecx, reg_edx)
+#define ISSUE_IO_VMCALL(method, param, result) \
+ (result = unisys_vmcall(method, (param) & 0xFFFFFFFF, \
+ (param) >> 32))
+#define ISSUE_IO_EXTENDED_VMCALL(method, param1, param2, param3) \
+ unisys_extended_vmcall(method, param1, param2, param3)
+
+ /* The following uses VMCALL_POST_CODE_LOGEVENT interface but is currently
+ * not used much */
+#define ISSUE_IO_VMCALL_POSTCODE_SEVERITY(postcode, severity) \
+do { \
+ ISSUE_IO_EXTENDED_VMCALL(VMCALL_POST_CODE_LOGEVENT, severity, \
+ MDS_APPOS, postcode); \
+} while (0)
+#endif
+
+/* Structures for IO VMCALLs */
+
+/* ///////////// BEGIN PRAGMA PACK PUSH 1 ///////////////////////// */
+/* ///////////// ONLY STRUCT TYPE SHOULD BE BELOW */
+#pragma pack(push, 1)
+struct phys_info {
+ u64 pi_pfn;
+ u16 pi_off;
+ u16 pi_len;
+};
+
+#pragma pack(pop)
+/* ///////////// END PRAGMA PACK PUSH 1 /////////////////////////// */
+
+/* ///////////// BEGIN PRAGMA PACK PUSH 1 ///////////////////////// */
+/* ///////////// ONLY STRUCT TYPE SHOULD BE BELOW */
+#pragma pack(push, 1)
+/* Parameters to VMCALL_IO_CONTROLVM_ADDR interface */
+struct vmcall_io_controlvm_addr_params {
+ /* The Guest-relative physical address of the ControlVm channel.
+ * This VMCall fills this in with the appropriate address. */
+ u64 address; /* contents provided by this VMCALL (OUT) */
+ /* the size of the ControlVm channel in bytes This VMCall fills this
+ * in with the appropriate address. */
+ u32 channel_bytes; /* contents provided by this VMCALL (OUT) */
+ u8 unused[4]; /* Unused Bytes in the 64-Bit Aligned Struct */
+};
+
+#pragma pack(pop)
+/* ///////////// END PRAGMA PACK PUSH 1 /////////////////////////// */
+
+/* ///////////// BEGIN PRAGMA PACK PUSH 1 ///////////////////////// */
+/* ///////////// ONLY STRUCT TYPE SHOULD BE BELOW */
+#pragma pack(push, 1)
+/* Parameters to VMCALL_IO_DIAG_ADDR interface */
+struct vmcall_io_diag_addr_params {
+ /* The Guest-relative physical address of the diagnostic channel.
+ * This VMCall fills this in with the appropriate address. */
+ u64 address; /* contents provided by this VMCALL (OUT) */
+};
+
+#pragma pack(pop)
+/* ///////////// END PRAGMA PACK PUSH 1 /////////////////////////// */
+
+/* ///////////// BEGIN PRAGMA PACK PUSH 1 ///////////////////////// */
+/* ///////////// ONLY STRUCT TYPE SHOULD BE BELOW */
+#pragma pack(push, 1)
+/* Parameters to VMCALL_IO_VISORSERIAL_ADDR interface */
+struct vmcall_io_visorserial_addr_params {
+ /* The Guest-relative physical address of the serial console
+ * channel. This VMCall fills this in with the appropriate
+ * address. */
+ u64 address; /* contents provided by this VMCALL (OUT) */
+};
+
+#pragma pack(pop)
+/* ///////////// END PRAGMA PACK PUSH 1 /////////////////////////// */
+
+/* Parameters to VMCALL_CHANNEL_MISMATCH interface */
+struct vmcall_channel_version_mismatch_params {
+ u8 chname[32]; /* Null terminated string giving name of channel
+ * (IN) */
+ u8 item_name[32]; /* Null terminated string giving name of
+ * mismatched item (IN) */
+ u32 line_no; /* line# where invoked. (IN) */
+ u8 file_name[36]; /* source code where invoked - Null terminated
+ * string (IN) */
+};
+
+#endif /* __IOMONINTF_H__ */
diff --git a/drivers/staging/unisys/include/guestlinuxdebug.h b/drivers/staging/unisys/include/guestlinuxdebug.h
new file mode 100644
index 000000000..957a627d0
--- /dev/null
+++ b/drivers/staging/unisys/include/guestlinuxdebug.h
@@ -0,0 +1,180 @@
+/* Copyright (C) 2010 - 2013 UNISYS CORPORATION
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or (at
+ * your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
+ * NON INFRINGEMENT. See the GNU General Public License for more
+ * details.
+ */
+
+#ifndef __GUESTLINUXDEBUG_H__
+#define __GUESTLINUXDEBUG_H__
+
+/*
+* This file contains supporting interface for "vmcallinterface.h", particularly
+* regarding adding additional structure and functionality to linux
+* ISSUE_IO_VMCALL_POSTCODE_SEVERITY */
+
+/******* INFO ON ISSUE_POSTCODE_LINUX() BELOW *******/
+#include "vmcallinterface.h"
+enum driver_pc { /* POSTCODE driver identifier tuples */
+ /* visorchipset driver files */
+ VISOR_CHIPSET_PC = 0xA0,
+ VISOR_CHIPSET_PC_controlvm_c = 0xA1,
+ VISOR_CHIPSET_PC_controlvm_cm2 = 0xA2,
+ VISOR_CHIPSET_PC_controlvm_direct_c = 0xA3,
+ VISOR_CHIPSET_PC_file_c = 0xA4,
+ VISOR_CHIPSET_PC_parser_c = 0xA5,
+ VISOR_CHIPSET_PC_testing_c = 0xA6,
+ VISOR_CHIPSET_PC_visorchipset_main_c = 0xA7,
+ VISOR_CHIPSET_PC_visorswitchbus_c = 0xA8,
+ /* visorbus driver files */
+ VISOR_BUS_PC = 0xB0,
+ VISOR_BUS_PC_businst_attr_c = 0xB1,
+ VISOR_BUS_PC_channel_attr_c = 0xB2,
+ VISOR_BUS_PC_devmajorminor_attr_c = 0xB3,
+ VISOR_BUS_PC_visorbus_main_c = 0xB4,
+ /* visorclientbus driver files */
+ VISOR_CLIENT_BUS_PC = 0xC0,
+ VISOR_CLIENT_BUS_PC_visorclientbus_main_c = 0xC1,
+ /* virt hba driver files */
+ VIRT_HBA_PC = 0xC2,
+ VIRT_HBA_PC_virthba_c = 0xC3,
+ /* virtpci driver files */
+ VIRT_PCI_PC = 0xC4,
+ VIRT_PCI_PC_virtpci_c = 0xC5,
+ /* virtnic driver files */
+ VIRT_NIC_PC = 0xC6,
+ VIRT_NIC_P_virtnic_c = 0xC7,
+ /* uislib driver files */
+ UISLIB_PC = 0xD0,
+ UISLIB_PC_uislib_c = 0xD1,
+ UISLIB_PC_uisqueue_c = 0xD2,
+ UISLIB_PC_uisthread_c = 0xD3,
+ UISLIB_PC_uisutils_c = 0xD4,
+};
+
+enum event_pc { /* POSTCODE event identifier tuples */
+ ATTACH_PORT_ENTRY_PC = 0x001,
+ ATTACH_PORT_FAILURE_PC = 0x002,
+ ATTACH_PORT_SUCCESS_PC = 0x003,
+ BUS_FAILURE_PC = 0x004,
+ BUS_CREATE_ENTRY_PC = 0x005,
+ BUS_CREATE_FAILURE_PC = 0x006,
+ BUS_CREATE_EXIT_PC = 0x007,
+ BUS_CONFIGURE_ENTRY_PC = 0x008,
+ BUS_CONFIGURE_FAILURE_PC = 0x009,
+ BUS_CONFIGURE_EXIT_PC = 0x00A,
+ CHIPSET_INIT_ENTRY_PC = 0x00B,
+ CHIPSET_INIT_SUCCESS_PC = 0x00C,
+ CHIPSET_INIT_FAILURE_PC = 0x00D,
+ CHIPSET_INIT_EXIT_PC = 0x00E,
+ CREATE_WORKQUEUE_PC = 0x00F,
+ CREATE_WORKQUEUE_FAILED_PC = 0x0A0,
+ CONTROLVM_INIT_FAILURE_PC = 0x0A1,
+ DEVICE_CREATE_ENTRY_PC = 0x0A2,
+ DEVICE_CREATE_FAILURE_PC = 0x0A3,
+ DEVICE_CREATE_SUCCESS_PC = 0x0A4,
+ DEVICE_CREATE_EXIT_PC = 0x0A5,
+ DEVICE_ADD_PC = 0x0A6,
+ DEVICE_REGISTER_FAILURE_PC = 0x0A7,
+ DEVICE_CHANGESTATE_ENTRY_PC = 0x0A8,
+ DEVICE_CHANGESTATE_FAILURE_PC = 0x0A9,
+ DEVICE_CHANGESTATE_EXIT_PC = 0x0AA,
+ DRIVER_ENTRY_PC = 0x0AB,
+ DRIVER_EXIT_PC = 0x0AC,
+ MALLOC_FAILURE_PC = 0x0AD,
+ QUEUE_DELAYED_WORK_PC = 0x0AE,
+ UISLIB_THREAD_FAILURE_PC = 0x0B7,
+ VBUS_CHANNEL_ENTRY_PC = 0x0B8,
+ VBUS_CHANNEL_FAILURE_PC = 0x0B9,
+ VBUS_CHANNEL_EXIT_PC = 0x0BA,
+ VHBA_CREATE_ENTRY_PC = 0x0BB,
+ VHBA_CREATE_FAILURE_PC = 0x0BC,
+ VHBA_CREATE_EXIT_PC = 0x0BD,
+ VHBA_CREATE_SUCCESS_PC = 0x0BE,
+ VHBA_COMMAND_HANDLER_PC = 0x0BF,
+ VHBA_PROBE_ENTRY_PC = 0x0C0,
+ VHBA_PROBE_FAILURE_PC = 0x0C1,
+ VHBA_PROBE_EXIT_PC = 0x0C2,
+ VNIC_CREATE_ENTRY_PC = 0x0C3,
+ VNIC_CREATE_FAILURE_PC = 0x0C4,
+ VNIC_CREATE_SUCCESS_PC = 0x0C5,
+ VNIC_PROBE_ENTRY_PC = 0x0C6,
+ VNIC_PROBE_FAILURE_PC = 0x0C7,
+ VNIC_PROBE_EXIT_PC = 0x0C8,
+ VPCI_CREATE_ENTRY_PC = 0x0C9,
+ VPCI_CREATE_FAILURE_PC = 0x0CA,
+ VPCI_CREATE_EXIT_PC = 0x0CB,
+ VPCI_PROBE_ENTRY_PC = 0x0CC,
+ VPCI_PROBE_FAILURE_PC = 0x0CD,
+ VPCI_PROBE_EXIT_PC = 0x0CE,
+ CRASH_DEV_ENTRY_PC = 0x0CF,
+ CRASH_DEV_EXIT_PC = 0x0D0,
+ CRASH_DEV_HADDR_NULL = 0x0D1,
+ CRASH_DEV_CONTROLVM_NULL = 0x0D2,
+ CRASH_DEV_RD_BUS_FAIULRE_PC = 0x0D3,
+ CRASH_DEV_RD_DEV_FAIULRE_PC = 0x0D4,
+ CRASH_DEV_BUS_NULL_FAILURE_PC = 0x0D5,
+ CRASH_DEV_DEV_NULL_FAILURE_PC = 0x0D6,
+ CRASH_DEV_CTRL_RD_FAILURE_PC = 0x0D7,
+ CRASH_DEV_COUNT_FAILURE_PC = 0x0D8,
+ SAVE_MSG_BUS_FAILURE_PC = 0x0D9,
+ SAVE_MSG_DEV_FAILURE_PC = 0x0DA,
+ CALLHOME_INIT_FAILURE_PC = 0x0DB
+};
+
+#ifdef __GNUC__
+
+#define POSTCODE_SEVERITY_ERR DIAG_SEVERITY_ERR
+#define POSTCODE_SEVERITY_WARNING DIAG_SEVERITY_WARNING
+#define POSTCODE_SEVERITY_INFO DIAG_SEVERITY_PRINT /* TODO-> Info currently
+ * doesnt show, so we
+ * set info=warning */
+/* example call of POSTCODE_LINUX_2(VISOR_CHIPSET_PC, POSTCODE_SEVERITY_ERR);
+ * Please also note that the resulting postcode is in hex, so if you are
+ * searching for the __LINE__ number, convert it first to decimal. The line
+ * number combined with driver and type of call, will allow you to track down
+ * exactly what line an error occurred on, or where the last driver
+ * entered/exited from.
+ */
+
+/* BASE FUNCTIONS */
+#define POSTCODE_LINUX_A(DRIVER_PC, EVENT_PC, pc32bit, severity) \
+do { \
+ unsigned long long post_code_temp; \
+ post_code_temp = (((u64)DRIVER_PC) << 56) | (((u64)EVENT_PC) << 44) | \
+ ((((u64)__LINE__) & 0xFFF) << 32) | \
+ (((u64)pc32bit) & 0xFFFFFFFF); \
+ ISSUE_IO_VMCALL_POSTCODE_SEVERITY(post_code_temp, severity); \
+} while (0)
+
+#define POSTCODE_LINUX_B(DRIVER_PC, EVENT_PC, pc16bit1, pc16bit2, severity) \
+do { \
+ unsigned long long post_code_temp; \
+ post_code_temp = (((u64)DRIVER_PC) << 56) | (((u64)EVENT_PC) << 44) | \
+ ((((u64)__LINE__) & 0xFFF) << 32) | \
+ ((((u64)pc16bit1) & 0xFFFF) << 16) | \
+ (((u64)pc16bit2) & 0xFFFF); \
+ ISSUE_IO_VMCALL_POSTCODE_SEVERITY(post_code_temp, severity); \
+} while (0)
+
+/* MOST COMMON */
+#define POSTCODE_LINUX_2(EVENT_PC, severity) \
+ POSTCODE_LINUX_A(CURRENT_FILE_PC, EVENT_PC, 0x0000, severity)
+
+#define POSTCODE_LINUX_3(EVENT_PC, pc32bit, severity) \
+ POSTCODE_LINUX_A(CURRENT_FILE_PC, EVENT_PC, pc32bit, severity)
+
+#define POSTCODE_LINUX_4(EVENT_PC, pc16bit1, pc16bit2, severity) \
+ POSTCODE_LINUX_B(CURRENT_FILE_PC, EVENT_PC, pc16bit1, \
+ pc16bit2, severity)
+
+#endif
+#endif
diff --git a/drivers/staging/unisys/include/periodic_work.h b/drivers/staging/unisys/include/periodic_work.h
new file mode 100644
index 000000000..26ec10bdf
--- /dev/null
+++ b/drivers/staging/unisys/include/periodic_work.h
@@ -0,0 +1,38 @@
+/* periodic_work.h
+ *
+ * Copyright (C) 2010 - 2013 UNISYS CORPORATION
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or (at
+ * your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
+ * NON INFRINGEMENT. See the GNU General Public License for more
+ * details.
+ */
+
+#ifndef __PERIODIC_WORK_H__
+#define __PERIODIC_WORK_H__
+
+#include "timskmod.h"
+
+/* PERIODIC_WORK an opaque structure to users.
+ * Fields are declared only in the implementation .c files.
+ */
+struct periodic_work;
+
+struct periodic_work *visor_periodic_work_create(ulong jiffy_interval,
+ struct workqueue_struct *workqueue,
+ void (*workfunc)(void *),
+ void *workfuncarg,
+ const char *devnam);
+void visor_periodic_work_destroy(struct periodic_work *pw);
+BOOL visor_periodic_work_nextperiod(struct periodic_work *pw);
+BOOL visor_periodic_work_start(struct periodic_work *pw);
+BOOL visor_periodic_work_stop(struct periodic_work *pw);
+
+#endif
diff --git a/drivers/staging/unisys/include/procobjecttree.h b/drivers/staging/unisys/include/procobjecttree.h
new file mode 100644
index 000000000..809c67942
--- /dev/null
+++ b/drivers/staging/unisys/include/procobjecttree.h
@@ -0,0 +1,47 @@
+/* procobjecttree.h
+ *
+ * Copyright (C) 2010 - 2013 UNISYS CORPORATION
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or (at
+ * your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
+ * NON INFRINGEMENT. See the GNU General Public License for more
+ * details.
+ */
+
+/** @file *********************************************************************
+ *
+ * This describes the interfaces necessary for creating a tree of types,
+ * objects, and properties in /proc.
+ *
+ ******************************************************************************
+ */
+
+#ifndef __PROCOBJECTTREE_H__
+#define __PROCOBJECTTREE_H__
+
+#include "timskmod.h"
+
+/* These are opaque structures to users.
+ * Fields are declared only in the implementation .c files.
+ */
+typedef struct MYPROCOBJECT_Tag MYPROCOBJECT;
+typedef struct MYPROCTYPE_Tag MYPROCTYPE;
+
+MYPROCOBJECT *visor_proc_CreateObject(MYPROCTYPE *type, const char *name,
+ void *context);
+void visor_proc_DestroyObject(MYPROCOBJECT *obj);
+MYPROCTYPE *visor_proc_CreateType(struct proc_dir_entry *procRootDir,
+ const char **name,
+ const char **propertyNames,
+ void (*show_property)(struct seq_file *,
+ void *, int));
+void visor_proc_DestroyType(MYPROCTYPE *type);
+
+#endif
diff --git a/drivers/staging/unisys/include/sparstop.h b/drivers/staging/unisys/include/sparstop.h
new file mode 100644
index 000000000..05837399a
--- /dev/null
+++ b/drivers/staging/unisys/include/sparstop.h
@@ -0,0 +1,30 @@
+/* sparstop.h
+ *
+ * Copyright (C) 2010 - 2013 UNISYS CORPORATION
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or (at
+ * your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
+ * NON INFRINGEMENT. See the GNU General Public License for more
+ * details.
+ */
+
+#ifndef __SPARSTOP_H__
+#define __SPARSTOP_H__
+
+#include "timskmod.h"
+#include "version.h"
+#include <linux/ctype.h>
+
+typedef void (*SPARSTOP_COMPLETE_FUNC) (void *context, int status);
+
+int sp_stop(void *context, SPARSTOP_COMPLETE_FUNC get_complete_func);
+void test_remove_stop_device(void);
+
+#endif
diff --git a/drivers/staging/unisys/include/timskmod.h b/drivers/staging/unisys/include/timskmod.h
new file mode 100644
index 000000000..cde2494ad
--- /dev/null
+++ b/drivers/staging/unisys/include/timskmod.h
@@ -0,0 +1,153 @@
+/* timskmod.h
+ *
+ * Copyright (C) 2010 - 2013 UNISYS CORPORATION
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or (at
+ * your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
+ * NON INFRINGEMENT. See the GNU General Public License for more
+ * details.
+ */
+
+#ifndef __TIMSKMOD_H__
+#define __TIMSKMOD_H__
+
+#include <linux/version.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/kobject.h>
+#include <linux/sysfs.h>
+#include <linux/fs.h>
+#include <linux/string.h>
+#include <linux/sched.h>
+#include <linux/spinlock.h>
+#include <linux/slab.h>
+#include <linux/errno.h>
+#include <linux/interrupt.h>
+#include <linux/wait.h>
+#include <linux/vmalloc.h>
+#include <linux/proc_fs.h>
+#include <linux/cdev.h>
+#include <linux/types.h>
+#include <asm/irq.h>
+#include <linux/io.h>
+#include <asm/dma.h>
+#include <linux/uaccess.h>
+#include <linux/list.h>
+#include <linux/poll.h>
+/* #define EXPORT_SYMTAB */
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/fcntl.h>
+#include <linux/workqueue.h>
+#include <linux/kthread.h>
+#include <linux/seq_file.h>
+#include <linux/mm.h>
+
+/* #define DEBUG */
+#ifndef BOOL
+#define BOOL int
+#endif
+#define FALSE 0
+#define TRUE 1
+#if !defined SUCCESS
+#define SUCCESS 0
+#endif
+#define MIN(a, b) (((a) < (b)) ? (a) : (b))
+#define MAX(a, b) (((a) > (b)) ? (a) : (b))
+#define STRUCTSEQUAL(x, y) (memcmp(&x, &y, sizeof(x)) == 0)
+#ifndef HOSTADDRESS
+#define HOSTADDRESS unsigned long long
+#endif
+
+#define sizeofmember(TYPE, MEMBER) (sizeof(((TYPE *)0)->MEMBER))
+/** "Covered quotient" function */
+#define COVQ(v, d) (((v) + (d) - 1) / (d))
+#define SWAPPOINTERS(p1, p2) \
+ do { \
+ void *SWAPPOINTERS_TEMP = (void *)p1; \
+ (void *)(p1) = (void *)(p2); \
+ (void *)(p2) = SWAPPOINTERS_TEMP; \
+ } while (0)
+
+#define WARNDRV(fmt, args...) LOGWRN(fmt, ## args)
+#define SECUREDRV(fmt, args...) LOGWRN(fmt, ## args)
+
+#define PRINTKDEV(devname, fmt, args...) LOGINFDEV(devname, fmt, ## args)
+#define TBDDEV(devname, fmt, args...) LOGERRDEV(devname, fmt, ## args)
+#define HUHDEV(devname, fmt, args...) LOGERRDEV(devname, fmt, ## args)
+#define ERRDEV(devname, fmt, args...) LOGERRDEV(devname, fmt, ## args)
+#define ERRDEVX(devno, fmt, args...) LOGERRDEVX(devno, fmt, ## args)
+#define WARNDEV(devname, fmt, args...) LOGWRNDEV(devname, fmt, ## args)
+#define SECUREDEV(devname, fmt, args...) LOGWRNDEV(devname, fmt, ## args)
+#define INFODEV(devname, fmt, args...) LOGINFDEV(devname, fmt, ## args)
+#define INFODEVX(devno, fmt, args...) LOGINFDEVX(devno, fmt, ## args)
+
+/** Verifies the consistency of your PRIVATEDEVICEDATA structure using
+ * conventional "signature" fields:
+ * <p>
+ * - sig1 should contain the size of the structure
+ * - sig2 should contain a pointer to the beginning of the structure
+ */
+#define DDLOOKSVALID(dd) \
+ ((dd != NULL) && \
+ ((dd)->sig1 == sizeof(PRIVATEDEVICEDATA)) && \
+ ((dd)->sig2 == dd))
+
+/** Verifies the consistency of your PRIVATEFILEDATA structure using
+ * conventional "signature" fields:
+ * <p>
+ * - sig1 should contain the size of the structure
+ * - sig2 should contain a pointer to the beginning of the structure
+ */
+#define FDLOOKSVALID(fd) \
+ ((fd != NULL) && \
+ ((fd)->sig1 == sizeof(PRIVATEFILEDATA)) && \
+ ((fd)->sig2 == fd))
+
+/** Sleep for an indicated number of seconds (for use in kernel mode).
+ * x - the number of seconds to sleep.
+ */
+#define SLEEP(x) \
+ do { __set_current_state(TASK_INTERRUPTIBLE); \
+ schedule_timeout((x)*HZ); \
+ } while (0)
+
+/** Sleep for an indicated number of jiffies (for use in kernel mode).
+ * x - the number of jiffies to sleep.
+ */
+#define SLEEPJIFFIES(x) \
+ do { __set_current_state(TASK_INTERRUPTIBLE); \
+ schedule_timeout(x); \
+ } while (0)
+
+static inline struct cdev *cdev_alloc_init(struct module *owner,
+ const struct file_operations *fops)
+{
+ struct cdev *cdev = NULL;
+
+ cdev = cdev_alloc();
+ if (!cdev)
+ return NULL;
+ cdev->ops = fops;
+ cdev->owner = owner;
+
+ /* Note that the memory allocated for cdev will be deallocated
+ * when the usage count drops to 0, because it is controlled
+ * by a kobject of type ktype_cdev_dynamic. (This
+ * deallocation could very well happen outside of our kernel
+ * module, like via the cdev_put in __fput() for example.)
+ */
+ return cdev;
+}
+
+extern int unisys_spar_platform;
+
+#endif
diff --git a/drivers/staging/unisys/include/uisqueue.h b/drivers/staging/unisys/include/uisqueue.h
new file mode 100644
index 000000000..08ba16ea8
--- /dev/null
+++ b/drivers/staging/unisys/include/uisqueue.h
@@ -0,0 +1,396 @@
+/* uisqueue.h
+ *
+ * Copyright (C) 2010 - 2013 UNISYS CORPORATION
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or (at
+ * your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
+ * NON INFRINGEMENT. See the GNU General Public License for more
+ * details.
+ */
+
+/*
+ * Unisys IO Virtualization header NOTE: This file contains only Linux
+ * specific structs. All OS-independent structs are in iochannel.h.xx
+ */
+
+#ifndef __UISQUEUE_H__
+#define __UISQUEUE_H__
+
+#include "linux/version.h"
+#include "iochannel.h"
+#include <linux/atomic.h>
+#include <linux/semaphore.h>
+#include <linux/uuid.h>
+
+#include "controlvmchannel.h"
+#include "controlvmcompletionstatus.h"
+
+struct uisqueue_info {
+ struct channel_header __iomem *chan;
+ /* channel containing queues in which scsi commands &
+ * responses are queued
+ */
+ u64 packets_sent;
+ u64 packets_received;
+ u64 interrupts_sent;
+ u64 interrupts_received;
+ u64 max_not_empty_cnt;
+ u64 total_wakeup_cnt;
+ u64 non_empty_wakeup_cnt;
+
+ struct {
+ struct signal_queue_header reserved1; /* */
+ struct signal_queue_header reserved2; /* */
+ } safe_uis_queue;
+ unsigned int (*send_int_if_needed)(struct uisqueue_info *info,
+ unsigned int whichcqueue,
+ unsigned char issue_irq_if_empty,
+ u64 irq_handle,
+ unsigned char io_termination);
+};
+
+/* uisqueue_put_cmdrsp_with_lock_client queues a commmand or response
+ * to the specified queue, at the tail if the queue is full but
+ * oktowait == 0, then it return 0 indicating failure. otherwise it
+ * wait for the queue to become non-full. If command is queued, return
+ * 1 for success.
+ */
+#define DONT_ISSUE_INTERRUPT 0
+#define ISSUE_INTERRUPT 1
+
+#define DONT_WAIT 0
+#define OK_TO_WAIT 1
+#define UISLIB_LOCK_PREFIX \
+ ".section .smp_locks,\"a\"\n" \
+ _ASM_ALIGN "\n" \
+ _ASM_PTR "661f\n" /* address */ \
+ ".previous\n" \
+ "661:\n\tlock; "
+
+unsigned long long uisqueue_interlocked_or(unsigned long long __iomem *tgt,
+ unsigned long long set);
+unsigned long long uisqueue_interlocked_and(unsigned long long __iomem *tgt,
+ unsigned long long set);
+
+int uisqueue_put_cmdrsp_with_lock_client(struct uisqueue_info *queueinfo,
+ struct uiscmdrsp *cmdrsp,
+ unsigned int queue,
+ void *insertlock,
+ unsigned char issue_irq_if_empty,
+ u64 irq_handle,
+ char oktowait,
+ u8 *channel_id);
+
+/* uisqueue_get_cmdrsp gets the cmdrsp entry at the head of the queue
+ * and copies it to the area pointed by cmdrsp param.
+ * returns 0 if queue is empty, 1 otherwise
+ */
+int
+
+uisqueue_get_cmdrsp(struct uisqueue_info *queueinfo, void *cmdrsp,
+ unsigned int queue);
+
+#define MAX_NAME_SIZE_UISQUEUE 64
+
+struct extport_info {
+ u8 valid:1;
+ /* if 1, indicates this extport slot is occupied
+ * if 0, indicates that extport slot is unoccupied */
+
+ u32 num_devs_using;
+ /* When extport is added, this is set to 0. For exports
+ * located in NETWORK switches:
+ * Each time a VNIC, i.e., intport, is added to the switch this
+ * is used to assign a pref_pnic for the VNIC and when assigned
+ * to a VNIC this counter is incremented. When a VNIC is
+ * deleted, the extport corresponding to the VNIC's pref_pnic
+ * is located and its num_devs_using is decremented. For VNICs,
+ * num_devs_using is basically used to load-balance transmit
+ * traffic from VNICs.
+ */
+
+ struct switch_info *swtch;
+ struct pci_id pci_id;
+ char name[MAX_NAME_SIZE_UISQUEUE];
+ union {
+ struct vhba_wwnn wwnn;
+ unsigned char macaddr[MAX_MACADDR_LEN];
+ };
+};
+
+struct device_info {
+ void __iomem *chanptr;
+ u64 channel_addr;
+ u64 channel_bytes;
+ uuid_le channel_uuid;
+ uuid_le instance_uuid;
+ struct irq_info intr;
+ struct switch_info *swtch;
+ char devid[30]; /* "vbus<busno>:dev<devno>" */
+ u16 polling;
+ struct semaphore interrupt_callback_lock;
+ u32 bus_no;
+ u32 dev_no;
+ int (*interrupt)(void *);
+ void *interrupt_context;
+ void *private_data;
+ struct list_head list_polling_device_channels;
+ unsigned long long moved_to_tail_cnt;
+ unsigned long long first_busy_cnt;
+ unsigned long long last_on_list_cnt;
+};
+
+enum switch_type {
+ RECOVERY_LAN = 1,
+ IB_LAN = 2
+};
+
+struct bus_info {
+ u32 bus_no, device_count;
+ struct device_info **device;
+ u64 guest_handle, recv_bus_irq_handle;
+ uuid_le bus_inst_uuid;
+ struct ultra_vbus_channel_protocol __iomem *bus_channel;
+ int bus_channel_bytes;
+ struct proc_dir_entry *proc_dir; /* proc/uislib/vbus/<x> */
+ struct proc_dir_entry *proc_info; /* proc/uislib/vbus/<x>/info */
+ char name[25];
+ char partition_name[99];
+ struct bus_info *next;
+ u8 local_vnic; /* 1 if local vnic created internally
+ * by IOVM; 0 otherwise... */
+};
+
+struct sn_list_entry {
+ struct uisscsi_dest pdest; /* scsi bus, target, lun for
+ * phys disk */
+ u8 sernum[MAX_SERIAL_NUM]; /* serial num of physical
+ * disk.. The length is always
+ * MAX_SERIAL_NUM, padded with
+ * spaces */
+ struct sn_list_entry *next;
+};
+
+/*
+ * IO messages sent to UisnicControlChanFunc & UissdControlChanFunc by
+ * code that processes the ControlVm channel messages.
+ */
+
+enum iopart_msg_type {
+ IOPART_ADD_VNIC,
+ IOPART_DEL_VNIC,
+ IOPART_DEL_ALL_VNICS,
+ IOPART_ADD_VHBA,
+ IOPART_ADD_VDISK,
+ IOPART_DEL_VHBA,
+ IOPART_DEL_VDISK,
+ IOPART_DEL_ALL_VDISKS_FOR_VHBA,
+ IOPART_DEL_ALL_VHBAS,
+ IOPART_ATTACH_PHBA,
+ IOPART_DETACH_PHBA, /* 10 */
+ IOPART_ATTACH_PNIC,
+ IOPART_DETACH_PNIC,
+ IOPART_DETACH_VHBA,
+ IOPART_DETACH_VNIC,
+ IOPART_PAUSE_VDISK,
+ IOPART_RESUME_VDISK,
+ IOPART_ADD_DEVICE, /* add generic device */
+ IOPART_DEL_DEVICE, /* del generic device */
+};
+
+struct add_virt_iopart {
+ void *chanptr; /* pointer to data channel */
+ u64 guest_handle; /* used to convert guest physical
+ * address to real physical address
+ * for DMA, for ex. */
+ u64 recv_bus_irq_handle; /* used to register to receive
+ * bus level interrupts. */
+ struct irq_info intr; /* contains recv & send
+ * interrupt info */
+ /* recvInterruptHandle is used to register to receive
+ * interrupts on the data channel. Used by GuestLinux/Windows
+ * IO drivers to connect to interrupt. sendInterruptHandle is
+ * used by IOPart drivers as parameter to
+ * Issue_VMCALL_IO_QUEUE_TRANSITION to interrupt thread in
+ * guest linux/windows IO drivers when data channel queue for
+ * vhba/vnic goes from EMPTY to NON-EMPTY. */
+ struct switch_info *swtch; /* pointer to the virtual
+ * switch to which the vnic is
+ * connected */
+
+ u8 use_g2g_copy; /* Used to determine if a virtual HBA
+ * needs to use G2G copy. */
+ u8 filler[7];
+
+ u32 bus_no;
+ u32 dev_no;
+ char *params;
+ ulong params_bytes;
+
+};
+
+struct add_vdisk_iopart {
+ void *chanptr; /* pointer to data channel */
+ int implicit;
+ struct uisscsi_dest vdest; /* scsi bus, target, lun for virt disk */
+ struct uisscsi_dest pdest; /* scsi bus, target, lun for phys disk */
+ u8 sernum[MAX_SERIAL_NUM]; /* serial num of physical disk */
+ u32 serlen; /* length of serial num */
+};
+
+struct del_vdisk_iopart {
+ void *chanptr; /* pointer to data channel */
+ struct uisscsi_dest vdest; /* scsi bus, target, lun for virt disk */
+};
+
+struct del_virt_iopart {
+ void *chanptr; /* pointer to data channel */
+};
+
+struct det_virt_iopart { /* detach internal port */
+ void *chanptr; /* pointer to data channel */
+ struct switch_info *swtch;
+};
+
+struct paures_vdisk_iopart {
+ void *chanptr; /* pointer to data channel */
+ struct uisscsi_dest vdest; /* scsi bus, target, lun for virt disk */
+};
+
+struct add_switch_iopart { /* add switch */
+ struct switch_info *swtch;
+ char *params;
+ ulong params_bytes;
+};
+
+struct del_switch_iopart { /* destroy switch */
+ struct switch_info *swtch;
+};
+
+struct io_msgs {
+ enum iopart_msg_type msgtype;
+
+ /* additional params needed by some messages */
+ union {
+ struct add_virt_iopart add_vhba;
+ struct add_virt_iopart add_vnic;
+ struct add_vdisk_iopart add_vdisk;
+ struct del_virt_iopart del_vhba;
+ struct del_virt_iopart del_vnic;
+ struct det_virt_iopart det_vhba;
+ struct det_virt_iopart det_vnic;
+ struct del_vdisk_iopart del_vdisk;
+ struct del_virt_iopart del_all_vdisks_for_vhba;
+ struct add_virt_iopart add_device;
+ struct del_virt_iopart del_device;
+ struct det_virt_iopart det_intport;
+ struct add_switch_iopart add_switch;
+ struct del_switch_iopart del_switch;
+ struct extport_info *ext_port; /* for attach or detach
+ * pnic/generic delete all
+ * vhbas/allvnics need no
+ * parameters */
+ struct paures_vdisk_iopart paures_vdisk;
+ };
+};
+
+/*
+* Guest messages sent to VirtControlChanFunc by code that processes
+* the ControlVm channel messages.
+*/
+
+enum guestpart_msg_type {
+ GUEST_ADD_VBUS,
+ GUEST_ADD_VHBA,
+ GUEST_ADD_VNIC,
+ GUEST_DEL_VBUS,
+ GUEST_DEL_VHBA,
+ GUEST_DEL_VNIC,
+ GUEST_DEL_ALL_VHBAS,
+ GUEST_DEL_ALL_VNICS,
+ GUEST_DEL_ALL_VBUSES, /* deletes all vhbas & vnics on all
+ * buses and deletes all buses */
+ GUEST_PAUSE_VHBA,
+ GUEST_PAUSE_VNIC,
+ GUEST_RESUME_VHBA,
+ GUEST_RESUME_VNIC
+};
+
+struct add_vbus_guestpart {
+ void __iomem *chanptr; /* pointer to data channel for bus -
+ * NOT YET USED */
+ u32 bus_no; /* bus number to be created/deleted */
+ u32 dev_count; /* max num of devices on bus */
+ uuid_le bus_uuid; /* indicates type of bus */
+ uuid_le instance_uuid; /* instance guid for device */
+};
+
+struct del_vbus_guestpart {
+ u32 bus_no; /* bus number to be deleted */
+ /* once we start using the bus's channel, add can dump busNo
+ * into the channel header and then delete will need only one
+ * parameter, chanptr. */
+};
+
+struct add_virt_guestpart {
+ void __iomem *chanptr; /* pointer to data channel */
+ u32 bus_no; /* bus number for the operation */
+ u32 device_no; /* number of device on the bus */
+ uuid_le instance_uuid; /* instance guid for device */
+ struct irq_info intr; /* recv/send interrupt info */
+ /* recvInterruptHandle contains info needed in order to
+ * register to receive interrupts on the data channel.
+ * sendInterruptHandle contains handle which is provided to
+ * monitor VMCALL that will cause an interrupt to be generated
+ * for the other end.
+ */
+};
+
+struct pause_virt_guestpart {
+ void __iomem *chanptr; /* pointer to data channel */
+};
+
+struct resume_virt_guestpart {
+ void __iomem *chanptr; /* pointer to data channel */
+};
+
+struct del_virt_guestpart {
+ void __iomem *chanptr; /* pointer to data channel */
+};
+
+struct init_chipset_guestpart {
+ u32 bus_count; /* indicates the max number of busses */
+ u32 switch_count; /* indicates the max number of switches */
+};
+
+struct guest_msgs {
+ enum guestpart_msg_type msgtype;
+
+ /* additional params needed by messages */
+ union {
+ struct add_vbus_guestpart add_vbus;
+ struct add_virt_guestpart add_vhba;
+ struct add_virt_guestpart add_vnic;
+ struct pause_virt_guestpart pause_vhba;
+ struct pause_virt_guestpart pause_vnic;
+ struct resume_virt_guestpart resume_vhba;
+ struct resume_virt_guestpart resume_vnic;
+ struct del_vbus_guestpart del_vbus;
+ struct del_virt_guestpart del_vhba;
+ struct del_virt_guestpart del_vnic;
+ struct del_vbus_guestpart del_all_vhbas;
+ struct del_vbus_guestpart del_all_vnics;
+ /* del_all_vbuses needs no parameters */
+ };
+ struct init_chipset_guestpart init_chipset;
+
+};
+
+#endif /* __UISQUEUE_H__ */
diff --git a/drivers/staging/unisys/include/uisthread.h b/drivers/staging/unisys/include/uisthread.h
new file mode 100644
index 000000000..52c3eb4de
--- /dev/null
+++ b/drivers/staging/unisys/include/uisthread.h
@@ -0,0 +1,42 @@
+/* uisthread.h
+ *
+ * Copyright (C) 2010 - 2013 UNISYS CORPORATION
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or (at
+ * your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
+ * NON INFRINGEMENT. See the GNU General Public License for more
+ * details.
+ */
+
+/*****************************************************************************/
+/* Unisys thread utilities header */
+/*****************************************************************************/
+
+#ifndef __UISTHREAD_H__
+#define __UISTHREAD_H__
+
+#include "linux/completion.h"
+
+struct uisthread_info {
+ struct task_struct *task;
+ int id;
+ struct completion has_stopped;
+};
+
+/* returns 0 for failure, 1 for success */
+int uisthread_start(
+ struct uisthread_info *thrinfo,
+ int (*threadfn)(void *),
+ void *thrcontext,
+ char *name);
+
+void uisthread_stop(struct uisthread_info *thrinfo);
+
+#endif /* __UISTHREAD_H__ */
diff --git a/drivers/staging/unisys/include/uisutils.h b/drivers/staging/unisys/include/uisutils.h
new file mode 100644
index 000000000..c7d0ba8aa
--- /dev/null
+++ b/drivers/staging/unisys/include/uisutils.h
@@ -0,0 +1,299 @@
+/* uisutils.h
+ *
+ * Copyright (C) 2010 - 2013 UNISYS CORPORATION
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or (at
+ * your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
+ * NON INFRINGEMENT. See the GNU General Public License for more
+ * details.
+ */
+
+/*
+ * Unisys Virtual HBA utilities header
+ */
+
+#ifndef __UISUTILS__H__
+#define __UISUTILS__H__
+#include <linux/string.h>
+#include <linux/io.h>
+#include <linux/sched.h>
+#include <linux/gfp.h>
+#include <linux/uuid.h>
+#include <linux/if_ether.h>
+
+#include "vmcallinterface.h"
+#include "channel.h"
+#include "uisthread.h"
+#include "uisqueue.h"
+#include "diagnostics/appos_subsystems.h"
+#include "vbusdeviceinfo.h"
+#include <linux/atomic.h>
+
+/* This is the MAGIC number stuffed by virthba in host->this_id. Used to
+ * identify virtual hbas.
+ */
+#define UIS_MAGIC_VHBA 707
+
+/* global function pointers that act as callback functions into
+ * uisnicmod, uissdmod, and virtpcimod
+ */
+extern int (*uisnic_control_chan_func)(struct io_msgs *);
+extern int (*uissd_control_chan_func)(struct io_msgs *);
+extern int (*virt_control_chan_func)(struct guest_msgs *);
+
+/* Return values of above callback functions: */
+#define CCF_ERROR 0 /* completed and failed */
+#define CCF_OK 1 /* completed successfully */
+#define CCF_PENDING 2 /* operation still pending */
+extern atomic_t uisutils_registered_services;
+
+struct req_handler_info {
+ uuid_le switch_uuid;
+ int (*controlfunc)(struct io_msgs *);
+ unsigned long min_channel_bytes;
+ int (*server_channel_ok)(unsigned long channel_bytes);
+ int (*server_channel_init)(void *x, unsigned char *client_str,
+ u32 client_str_len, u64 bytes);
+ char switch_type_name[99];
+ struct list_head list_link; /* links into ReqHandlerInfo_list */
+};
+
+struct req_handler_info *req_handler_find(uuid_le switch_uuid);
+
+#define uislib_ioremap_cache(addr, size) \
+ dbg_ioremap_cache(addr, size, __FILE__, __LINE__)
+
+static inline void __iomem *
+dbg_ioremap_cache(u64 addr, unsigned long size, char *file, int line)
+{
+ void __iomem *new;
+
+ new = ioremap_cache(addr, size);
+ return new;
+}
+
+#define uislib_ioremap(addr, size) dbg_ioremap(addr, size, __FILE__, __LINE__)
+
+static inline void *
+dbg_ioremap(u64 addr, unsigned long size, char *file, int line)
+{
+ void *new;
+
+ new = ioremap(addr, size);
+ return new;
+}
+
+#define uislib_iounmap(addr) dbg_iounmap(addr, __FILE__, __LINE__)
+
+static inline void
+dbg_iounmap(void __iomem *addr, char *file, int line)
+{
+ iounmap(addr);
+}
+
+#define PROC_READ_BUFFER_SIZE 131072 /* size of the buffer to allocate to
+ * hold all of /proc/XXX/info */
+int uisutil_add_proc_line_ex(int *total, char **buffer, int *buffer_remaining,
+ char *format, ...);
+
+int uisctrl_register_req_handler(int type, void *fptr,
+ struct ultra_vbus_deviceinfo *chipset_driver_info);
+
+unsigned char *util_map_virt(struct phys_info *sg);
+void util_unmap_virt(struct phys_info *sg);
+unsigned char *util_map_virt_atomic(struct phys_info *sg);
+void util_unmap_virt_atomic(void *buf);
+int uislib_client_inject_add_bus(u32 bus_no, uuid_le inst_uuid,
+ u64 channel_addr, ulong n_channel_bytes);
+int uislib_client_inject_del_bus(u32 bus_no);
+
+int uislib_client_inject_add_vhba(u32 bus_no, u32 dev_no,
+ u64 phys_chan_addr, u32 chan_bytes,
+ int is_test_addr, uuid_le inst_uuid,
+ struct irq_info *intr);
+int uislib_client_inject_pause_vhba(u32 bus_no, u32 dev_no);
+int uislib_client_inject_resume_vhba(u32 bus_no, u32 dev_no);
+int uislib_client_inject_del_vhba(u32 bus_no, u32 dev_no);
+int uislib_client_inject_add_vnic(u32 bus_no, u32 dev_no,
+ u64 phys_chan_addr, u32 chan_bytes,
+ int is_test_addr, uuid_le inst_uuid,
+ struct irq_info *intr);
+int uislib_client_inject_pause_vnic(u32 bus_no, u32 dev_no);
+int uislib_client_inject_resume_vnic(u32 bus_no, u32 dev_no);
+int uislib_client_inject_del_vnic(u32 bus_no, u32 dev_no);
+#ifdef STORAGE_CHANNEL
+u64 uislib_storage_channel(int client_id);
+#endif
+int uislib_get_owned_pdest(struct uisscsi_dest *pdest);
+
+int uislib_send_event(enum controlvm_id id,
+ struct controlvm_message_packet *event);
+
+/* structure used by vhba & vnic to keep track of queue & thread info */
+struct chaninfo {
+ struct uisqueue_info *queueinfo;
+ /* this specifies the queue structures for a channel */
+ /* ALLOCATED BY THE OTHER END - WE JUST GET A POINTER TO THE MEMORY */
+ spinlock_t insertlock;
+ /* currently used only in virtnic when sending data to uisnic */
+ /* to synchronize the inserts into the signal queue */
+ struct uisthread_info threadinfo;
+ /* this specifies the thread structures used by the thread that */
+ /* handles this channel */
+};
+
+/* this is the wait code for all the threads - it is used to get
+* something from a queue choices: wait_for_completion_interruptible,
+* _timeout, interruptible_timeout
+*/
+#define UIS_THREAD_WAIT_MSEC(x) { \
+ set_current_state(TASK_INTERRUPTIBLE); \
+ schedule_timeout(msecs_to_jiffies(x)); \
+}
+
+#define UIS_THREAD_WAIT_USEC(x) { \
+ set_current_state(TASK_INTERRUPTIBLE); \
+ schedule_timeout(usecs_to_jiffies(x)); \
+}
+
+#define UIS_THREAD_WAIT UIS_THREAD_WAIT_MSEC(5)
+
+#define UIS_THREAD_WAIT_SEC(x) { \
+ set_current_state(TASK_INTERRUPTIBLE); \
+ schedule_timeout((x)*HZ); \
+}
+
+/* This is a hack until we fix IOVM to initialize the channel header
+ * correctly at DEVICE_CREATE time, INSTEAD OF waiting until
+ * DEVICE_CONFIGURE time.
+ */
+static inline void
+wait_for_valid_guid(uuid_le __iomem *guid)
+{
+ uuid_le tmpguid;
+
+ while (1) {
+ memcpy_fromio((void *)&tmpguid,
+ (void __iomem *)guid, sizeof(uuid_le));
+ if (uuid_le_cmp(tmpguid, NULL_UUID_LE) != 0)
+ break;
+ UIS_THREAD_WAIT_SEC(5);
+ }
+}
+
+static inline unsigned int
+issue_vmcall_io_controlvm_addr(u64 *control_addr, u32 *control_bytes)
+{
+ struct vmcall_io_controlvm_addr_params params;
+ int result = VMCALL_SUCCESS;
+ u64 physaddr;
+
+ physaddr = virt_to_phys(&params);
+ ISSUE_IO_VMCALL(VMCALL_IO_CONTROLVM_ADDR, physaddr, result);
+ if (VMCALL_SUCCESSFUL(result)) {
+ *control_addr = params.address;
+ *control_bytes = params.channel_bytes;
+ }
+ return result;
+}
+
+static inline unsigned int issue_vmcall_io_diag_addr(u64 *diag_channel_addr)
+{
+ struct vmcall_io_diag_addr_params params;
+ int result = VMCALL_SUCCESS;
+ u64 physaddr;
+
+ physaddr = virt_to_phys(&params);
+ ISSUE_IO_VMCALL(VMCALL_IO_DIAG_ADDR, physaddr, result);
+ if (VMCALL_SUCCESSFUL(result))
+ *diag_channel_addr = params.address;
+ return result;
+}
+
+static inline unsigned int issue_vmcall_io_visorserial_addr(u64 *channel_addr)
+{
+ struct vmcall_io_visorserial_addr_params params;
+ int result = VMCALL_SUCCESS;
+ u64 physaddr;
+
+ physaddr = virt_to_phys(&params);
+ ISSUE_IO_VMCALL(VMCALL_IO_VISORSERIAL_ADDR, physaddr, result);
+ if (VMCALL_SUCCESSFUL(result))
+ *channel_addr = params.address;
+ return result;
+}
+
+static inline s64 issue_vmcall_query_guest_virtual_time_offset(void)
+{
+ u64 result = VMCALL_SUCCESS;
+ u64 physaddr = 0;
+
+ ISSUE_IO_VMCALL(VMCALL_QUERY_GUEST_VIRTUAL_TIME_OFFSET, physaddr,
+ result);
+ return result;
+}
+
+struct log_info_t {
+ unsigned long long last_cycles;
+ unsigned long long delta_sum[64];
+ unsigned long long delta_cnt[64];
+ unsigned long long max_delta[64];
+ unsigned long long min_delta[64];
+};
+
+static inline int issue_vmcall_update_physical_time(u64 adjustment)
+{
+ int result = VMCALL_SUCCESS;
+
+ ISSUE_IO_VMCALL(VMCALL_UPDATE_PHYSICAL_TIME, adjustment, result);
+ return result;
+}
+
+static inline unsigned int issue_vmcall_channel_mismatch(const char *chname,
+ const char *item_name, u32 line_no,
+ const char *path_n_fn)
+{
+ struct vmcall_channel_version_mismatch_params params;
+ int result = VMCALL_SUCCESS;
+ u64 physaddr;
+ char *last_slash = NULL;
+
+ strlcpy(params.chname, chname, sizeof(params.chname));
+ strlcpy(params.item_name, item_name, sizeof(params.item_name));
+ params.line_no = line_no;
+
+ last_slash = strrchr(path_n_fn, '/');
+ if (last_slash != NULL) {
+ last_slash++;
+ strlcpy(params.file_name, last_slash, sizeof(params.file_name));
+ } else
+ strlcpy(params.file_name,
+ "Cannot determine source filename",
+ sizeof(params.file_name));
+
+ physaddr = virt_to_phys(&params);
+ ISSUE_IO_VMCALL(VMCALL_CHANNEL_VERSION_MISMATCH, physaddr, result);
+ return result;
+}
+
+#define UIS_DAEMONIZE(nam)
+void *uislib_cache_alloc(struct kmem_cache *cur_pool, char *fn, int ln);
+#define UISCACHEALLOC(cur_pool) uislib_cache_alloc(cur_pool, __FILE__, __LINE__)
+void uislib_cache_free(struct kmem_cache *cur_pool, void *p, char *fn, int ln);
+#define UISCACHEFREE(cur_pool, p) \
+ uislib_cache_free(cur_pool, p, __FILE__, __LINE__)
+
+void uislib_enable_channel_interrupts(u32 bus_no, u32 dev_no,
+ int (*interrupt)(void *),
+ void *interrupt_context);
+void uislib_disable_channel_interrupts(u32 bus_no, u32 dev_no);
+void uislib_force_channel_interrupt(u32 bus_no, u32 dev_no);
+
+#endif /* __UISUTILS__H__ */
diff --git a/drivers/staging/unisys/include/vbushelper.h b/drivers/staging/unisys/include/vbushelper.h
new file mode 100644
index 000000000..84abe5f99
--- /dev/null
+++ b/drivers/staging/unisys/include/vbushelper.h
@@ -0,0 +1,47 @@
+/* vbushelper.h
+ *
+ * Copyright (C) 2011 - 2013 UNISYS CORPORATION
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or (at
+ * your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
+ * NON INFRINGEMENT. See the GNU General Public License for more
+ * details.
+ */
+
+#ifndef __VBUSHELPER_H__
+#define __VBUSHELPER_H__
+
+#include "vbusdeviceinfo.h"
+
+/* TARGET_HOSTNAME specified as -DTARGET_HOSTNAME=\"thename\" on the
+ * command line */
+
+#define TARGET_HOSTNAME "linuxguest"
+
+static inline void bus_device_info_init(
+ struct ultra_vbus_deviceinfo *bus_device_info_ptr,
+ const char *dev_type, const char *drv_name,
+ const char *ver, const char *ver_tag)
+{
+ memset(bus_device_info_ptr, 0, sizeof(struct ultra_vbus_deviceinfo));
+ snprintf(bus_device_info_ptr->devtype,
+ sizeof(bus_device_info_ptr->devtype),
+ "%s", (dev_type) ? dev_type : "unknownType");
+ snprintf(bus_device_info_ptr->drvname,
+ sizeof(bus_device_info_ptr->drvname),
+ "%s", (drv_name) ? drv_name : "unknownDriver");
+ snprintf(bus_device_info_ptr->infostrs,
+ sizeof(bus_device_info_ptr->infostrs), "%s\t%s\t%s",
+ (ver) ? ver : "unknownVer",
+ (ver_tag) ? ver_tag : "unknownVerTag",
+ TARGET_HOSTNAME);
+}
+
+#endif
diff --git a/drivers/staging/unisys/uislib/Kconfig b/drivers/staging/unisys/uislib/Kconfig
new file mode 100644
index 000000000..c39a0a21a
--- /dev/null
+++ b/drivers/staging/unisys/uislib/Kconfig
@@ -0,0 +1,10 @@
+#
+# Unisys uislib configuration
+#
+
+config UNISYS_UISLIB
+ tristate "Unisys uislib driver"
+ select UNISYS_VISORCHIPSET
+ ---help---
+ If you say Y here, you will enable the Unisys uislib driver.
+
diff --git a/drivers/staging/unisys/uislib/Makefile b/drivers/staging/unisys/uislib/Makefile
new file mode 100644
index 000000000..860f494f1
--- /dev/null
+++ b/drivers/staging/unisys/uislib/Makefile
@@ -0,0 +1,12 @@
+#
+# Makefile for Unisys uislib
+#
+
+obj-$(CONFIG_UNISYS_UISLIB) += visoruislib.o
+
+visoruislib-y := uislib.o uisqueue.o uisthread.o uisutils.o
+
+ccflags-y += -Idrivers/staging/unisys/include
+ccflags-y += -Idrivers/staging/unisys/visorchipset
+ccflags-y += -Idrivers/staging/unisys/common-spar/include
+ccflags-y += -Idrivers/staging/unisys/common-spar/include/channels
diff --git a/drivers/staging/unisys/uislib/uislib.c b/drivers/staging/unisys/uislib/uislib.c
new file mode 100644
index 000000000..f93d0bb11
--- /dev/null
+++ b/drivers/staging/unisys/uislib/uislib.c
@@ -0,0 +1,1372 @@
+/* uislib.c
+ *
+ * Copyright (C) 2010 - 2013 UNISYS CORPORATION
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or (at
+ * your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
+ * NON INFRINGEMENT. See the GNU General Public License for more
+ * details.
+ */
+
+/* @ALL_INSPECTED */
+#define EXPORT_SYMTAB
+#include <linux/kernel.h>
+#include <linux/highmem.h>
+#ifdef CONFIG_MODVERSIONS
+#include <config/modversions.h>
+#endif
+#include <linux/module.h>
+#include <linux/debugfs.h>
+
+#include <linux/types.h>
+#include <linux/uuid.h>
+
+#include <linux/version.h>
+#include "diagnostics/appos_subsystems.h"
+#include "uisutils.h"
+#include "vbuschannel.h"
+
+#include <linux/proc_fs.h>
+#include <linux/uaccess.h> /* for copy_from_user */
+#include <linux/ctype.h> /* for toupper */
+#include <linux/list.h>
+
+#include "sparstop.h"
+#include "visorchipset.h"
+#include "version.h"
+#include "guestlinuxdebug.h"
+
+#define SET_PROC_OWNER(x, y)
+
+#define POLLJIFFIES_NORMAL 1
+/* Choose whether or not you want to wakeup the request-polling thread
+ * after an IO termination:
+ * this is shorter than using __FILE__ (full path name) in
+ * debug/info/error messages
+ */
+#define CURRENT_FILE_PC UISLIB_PC_uislib_c
+#define __MYFILE__ "uislib.c"
+
+/* global function pointers that act as callback functions into virtpcimod */
+int (*virt_control_chan_func)(struct guest_msgs *);
+
+static int debug_buf_valid;
+static char *debug_buf; /* Note this MUST be global,
+ * because the contents must */
+static unsigned int chipset_inited;
+
+#define WAIT_ON_CALLBACK(handle) \
+ do { \
+ if (handle) \
+ break; \
+ UIS_THREAD_WAIT; \
+ } while (1)
+
+static struct bus_info *bus_list;
+static rwlock_t bus_list_lock;
+static int bus_list_count; /* number of buses in the list */
+static int max_bus_count; /* maximum number of buses expected */
+static u64 phys_data_chan;
+static int platform_no;
+
+static struct uisthread_info incoming_ti;
+static BOOL incoming_started = FALSE;
+static LIST_HEAD(poll_dev_chan);
+static unsigned long long tot_moved_to_tail_cnt;
+static unsigned long long tot_wait_cnt;
+static unsigned long long tot_wakeup_cnt;
+static unsigned long long tot_schedule_cnt;
+static int en_smart_wakeup = 1;
+static DEFINE_SEMAPHORE(poll_dev_lock); /* unlocked */
+static DECLARE_WAIT_QUEUE_HEAD(poll_dev_wake_q);
+static int poll_dev_start;
+
+#define CALLHOME_PROC_ENTRY_FN "callhome"
+#define CALLHOME_THROTTLED_PROC_ENTRY_FN "callhome_throttled"
+
+#define DIR_DEBUGFS_ENTRY "uislib"
+static struct dentry *dir_debugfs;
+
+#define PLATFORMNUMBER_DEBUGFS_ENTRY_FN "platform"
+static struct dentry *platformnumber_debugfs_read;
+
+#define CYCLES_BEFORE_WAIT_DEBUGFS_ENTRY_FN "cycles_before_wait"
+static struct dentry *cycles_before_wait_debugfs_read;
+
+#define SMART_WAKEUP_DEBUGFS_ENTRY_FN "smart_wakeup"
+static struct dentry *smart_wakeup_debugfs_entry;
+
+#define INFO_DEBUGFS_ENTRY_FN "info"
+static struct dentry *info_debugfs_entry;
+
+static unsigned long long cycles_before_wait, wait_cycles;
+
+/*****************************************************/
+/* local functions */
+/*****************************************************/
+
+static ssize_t info_debugfs_read(struct file *file, char __user *buf,
+ size_t len, loff_t *offset);
+static const struct file_operations debugfs_info_fops = {
+ .read = info_debugfs_read,
+};
+
+static void
+init_msg_header(struct controlvm_message *msg, u32 id, uint rsp, uint svr)
+{
+ memset(msg, 0, sizeof(struct controlvm_message));
+ msg->hdr.id = id;
+ msg->hdr.flags.response_expected = rsp;
+ msg->hdr.flags.server = svr;
+}
+
+static __iomem void *init_vbus_channel(u64 ch_addr, u32 ch_bytes)
+{
+ void __iomem *ch = uislib_ioremap_cache(ch_addr, ch_bytes);
+
+ if (!ch)
+ return NULL;
+
+ if (!SPAR_VBUS_CHANNEL_OK_CLIENT(ch)) {
+ uislib_iounmap(ch);
+ return NULL;
+ }
+ return ch;
+}
+
+static int
+create_bus(struct controlvm_message *msg, char *buf)
+{
+ u32 bus_no, dev_count;
+ struct bus_info *tmp, *bus;
+ size_t size;
+
+ if (max_bus_count == bus_list_count) {
+ POSTCODE_LINUX_3(BUS_CREATE_FAILURE_PC, max_bus_count,
+ POSTCODE_SEVERITY_ERR);
+ return CONTROLVM_RESP_ERROR_MAX_BUSES;
+ }
+
+ bus_no = msg->cmd.create_bus.bus_no;
+ dev_count = msg->cmd.create_bus.dev_count;
+
+ POSTCODE_LINUX_4(BUS_CREATE_ENTRY_PC, bus_no, dev_count,
+ POSTCODE_SEVERITY_INFO);
+
+ size =
+ sizeof(struct bus_info) +
+ (dev_count * sizeof(struct device_info *));
+ bus = kzalloc(size, GFP_ATOMIC);
+ if (!bus) {
+ POSTCODE_LINUX_3(BUS_CREATE_FAILURE_PC, bus_no,
+ POSTCODE_SEVERITY_ERR);
+ return CONTROLVM_RESP_ERROR_KMALLOC_FAILED;
+ }
+
+ /* Currently by default, the bus Number is the GuestHandle.
+ * Configure Bus message can override this.
+ */
+ if (msg->hdr.flags.test_message) {
+ /* This implies we're the IOVM so set guest handle to 0... */
+ bus->guest_handle = 0;
+ bus->bus_no = bus_no;
+ bus->local_vnic = 1;
+ } else {
+ bus->bus_no = bus_no;
+ bus->guest_handle = bus_no;
+ }
+ sprintf(bus->name, "%d", (int)bus->bus_no);
+ bus->device_count = dev_count;
+ bus->device =
+ (struct device_info **)((char *)bus + sizeof(struct bus_info));
+ bus->bus_inst_uuid = msg->cmd.create_bus.bus_inst_uuid;
+ bus->bus_channel_bytes = 0;
+ bus->bus_channel = NULL;
+
+ /* add bus to our bus list - but check for duplicates first */
+ read_lock(&bus_list_lock);
+ for (tmp = bus_list; tmp; tmp = tmp->next) {
+ if (tmp->bus_no == bus->bus_no)
+ break;
+ }
+ read_unlock(&bus_list_lock);
+ if (tmp) {
+ /* found a bus already in the list with same bus_no -
+ * reject add
+ */
+ POSTCODE_LINUX_3(BUS_CREATE_FAILURE_PC, bus->bus_no,
+ POSTCODE_SEVERITY_ERR);
+ kfree(bus);
+ return CONTROLVM_RESP_ERROR_ALREADY_DONE;
+ }
+ if ((msg->cmd.create_bus.channel_addr != 0) &&
+ (msg->cmd.create_bus.channel_bytes != 0)) {
+ bus->bus_channel_bytes = msg->cmd.create_bus.channel_bytes;
+ bus->bus_channel =
+ init_vbus_channel(msg->cmd.create_bus.channel_addr,
+ msg->cmd.create_bus.channel_bytes);
+ }
+ /* the msg is bound for virtpci; send guest_msgs struct to callback */
+ if (!msg->hdr.flags.server) {
+ struct guest_msgs cmd;
+
+ cmd.msgtype = GUEST_ADD_VBUS;
+ cmd.add_vbus.bus_no = bus_no;
+ cmd.add_vbus.chanptr = bus->bus_channel;
+ cmd.add_vbus.dev_count = dev_count;
+ cmd.add_vbus.bus_uuid = msg->cmd.create_bus.bus_data_type_uuid;
+ cmd.add_vbus.instance_uuid = msg->cmd.create_bus.bus_inst_uuid;
+ if (!virt_control_chan_func) {
+ POSTCODE_LINUX_3(BUS_CREATE_FAILURE_PC, bus->bus_no,
+ POSTCODE_SEVERITY_ERR);
+ kfree(bus);
+ return CONTROLVM_RESP_ERROR_VIRTPCI_DRIVER_FAILURE;
+ }
+ if (!virt_control_chan_func(&cmd)) {
+ POSTCODE_LINUX_3(BUS_CREATE_FAILURE_PC, bus->bus_no,
+ POSTCODE_SEVERITY_ERR);
+ kfree(bus);
+ return
+ CONTROLVM_RESP_ERROR_VIRTPCI_DRIVER_CALLBACK_ERROR;
+ }
+ }
+
+ /* add bus at the head of our list */
+ write_lock(&bus_list_lock);
+ if (!bus_list) {
+ bus_list = bus;
+ } else {
+ bus->next = bus_list;
+ bus_list = bus;
+ }
+ bus_list_count++;
+ write_unlock(&bus_list_lock);
+
+ POSTCODE_LINUX_3(BUS_CREATE_EXIT_PC, bus->bus_no,
+ POSTCODE_SEVERITY_INFO);
+ return CONTROLVM_RESP_SUCCESS;
+}
+
+static int
+destroy_bus(struct controlvm_message *msg, char *buf)
+{
+ int i;
+ struct bus_info *bus, *prev = NULL;
+ struct guest_msgs cmd;
+ u32 bus_no;
+
+ bus_no = msg->cmd.destroy_bus.bus_no;
+
+ read_lock(&bus_list_lock);
+
+ bus = bus_list;
+ while (bus) {
+ if (bus->bus_no == bus_no)
+ break;
+ prev = bus;
+ bus = bus->next;
+ }
+
+ if (!bus) {
+ read_unlock(&bus_list_lock);
+ return CONTROLVM_RESP_ERROR_ALREADY_DONE;
+ }
+
+ /* verify that this bus has no devices. */
+ for (i = 0; i < bus->device_count; i++) {
+ if (bus->device[i]) {
+ read_unlock(&bus_list_lock);
+ return CONTROLVM_RESP_ERROR_BUS_DEVICE_ATTACHED;
+ }
+ }
+ read_unlock(&bus_list_lock);
+
+ if (msg->hdr.flags.server)
+ goto remove;
+
+ /* client messages require us to call the virtpci callback associated
+ with this bus. */
+ cmd.msgtype = GUEST_DEL_VBUS;
+ cmd.del_vbus.bus_no = bus_no;
+ if (!virt_control_chan_func)
+ return CONTROLVM_RESP_ERROR_VIRTPCI_DRIVER_FAILURE;
+
+ if (!virt_control_chan_func(&cmd))
+ return CONTROLVM_RESP_ERROR_VIRTPCI_DRIVER_CALLBACK_ERROR;
+
+ /* finally, remove the bus from the list */
+remove:
+ write_lock(&bus_list_lock);
+ if (prev) /* not at head */
+ prev->next = bus->next;
+ else
+ bus_list = bus->next;
+ bus_list_count--;
+ write_unlock(&bus_list_lock);
+
+ if (bus->bus_channel) {
+ uislib_iounmap(bus->bus_channel);
+ bus->bus_channel = NULL;
+ }
+
+ kfree(bus);
+ return CONTROLVM_RESP_SUCCESS;
+}
+
+static int create_device(struct controlvm_message *msg, char *buf)
+{
+ struct device_info *dev;
+ struct bus_info *bus;
+ struct guest_msgs cmd;
+ u32 bus_no, dev_no;
+ int result = CONTROLVM_RESP_SUCCESS;
+ u64 min_size = MIN_IO_CHANNEL_SIZE;
+ struct req_handler_info *req_handler;
+
+ bus_no = msg->cmd.create_device.bus_no;
+ dev_no = msg->cmd.create_device.dev_no;
+
+ POSTCODE_LINUX_4(DEVICE_CREATE_ENTRY_PC, dev_no, bus_no,
+ POSTCODE_SEVERITY_INFO);
+
+ dev = kzalloc(sizeof(*dev), GFP_ATOMIC);
+ if (!dev) {
+ POSTCODE_LINUX_4(DEVICE_CREATE_FAILURE_PC, dev_no, bus_no,
+ POSTCODE_SEVERITY_ERR);
+ return CONTROLVM_RESP_ERROR_KMALLOC_FAILED;
+ }
+
+ dev->channel_uuid = msg->cmd.create_device.data_type_uuid;
+ dev->intr = msg->cmd.create_device.intr;
+ dev->channel_addr = msg->cmd.create_device.channel_addr;
+ dev->bus_no = bus_no;
+ dev->dev_no = dev_no;
+ sema_init(&dev->interrupt_callback_lock, 1); /* unlocked */
+ sprintf(dev->devid, "vbus%u:dev%u", (unsigned)bus_no, (unsigned)dev_no);
+ /* map the channel memory for the device. */
+ if (msg->hdr.flags.test_message) {
+ dev->chanptr = (void __iomem *)__va(dev->channel_addr);
+ } else {
+ req_handler = req_handler_find(dev->channel_uuid);
+ if (req_handler)
+ /* generic service handler registered for this
+ * channel
+ */
+ min_size = req_handler->min_channel_bytes;
+ if (min_size > msg->cmd.create_device.channel_bytes) {
+ POSTCODE_LINUX_4(DEVICE_CREATE_FAILURE_PC, dev_no,
+ bus_no, POSTCODE_SEVERITY_ERR);
+ result = CONTROLVM_RESP_ERROR_CHANNEL_SIZE_TOO_SMALL;
+ goto cleanup;
+ }
+ dev->chanptr =
+ uislib_ioremap_cache(dev->channel_addr,
+ msg->cmd.create_device.channel_bytes);
+ if (!dev->chanptr) {
+ result = CONTROLVM_RESP_ERROR_IOREMAP_FAILED;
+ POSTCODE_LINUX_4(DEVICE_CREATE_FAILURE_PC, dev_no,
+ bus_no, POSTCODE_SEVERITY_ERR);
+ goto cleanup;
+ }
+ }
+ dev->instance_uuid = msg->cmd.create_device.dev_inst_uuid;
+ dev->channel_bytes = msg->cmd.create_device.channel_bytes;
+
+ read_lock(&bus_list_lock);
+ for (bus = bus_list; bus; bus = bus->next) {
+ if (bus->bus_no != bus_no)
+ continue;
+ /* make sure the device number is valid */
+ if (dev_no >= bus->device_count) {
+ result = CONTROLVM_RESP_ERROR_MAX_DEVICES;
+ POSTCODE_LINUX_4(DEVICE_CREATE_FAILURE_PC, dev_no,
+ bus_no, POSTCODE_SEVERITY_ERR);
+ read_unlock(&bus_list_lock);
+ goto cleanup;
+ }
+ /* make sure this device is not already set */
+ if (bus->device[dev_no]) {
+ POSTCODE_LINUX_4(DEVICE_CREATE_FAILURE_PC,
+ dev_no, bus_no,
+ POSTCODE_SEVERITY_ERR);
+ result = CONTROLVM_RESP_ERROR_ALREADY_DONE;
+ read_unlock(&bus_list_lock);
+ goto cleanup;
+ }
+ read_unlock(&bus_list_lock);
+ /* the msg is bound for virtpci; send
+ * guest_msgs struct to callback
+ */
+ if (msg->hdr.flags.server) {
+ bus->device[dev_no] = dev;
+ POSTCODE_LINUX_4(DEVICE_CREATE_SUCCESS_PC, dev_no,
+ bus_no, POSTCODE_SEVERITY_INFO);
+ return CONTROLVM_RESP_SUCCESS;
+ }
+ if (uuid_le_cmp(dev->channel_uuid,
+ spar_vhba_channel_protocol_uuid) == 0) {
+ wait_for_valid_guid(&((struct channel_header __iomem *)
+ (dev->chanptr))->chtype);
+ if (!SPAR_VHBA_CHANNEL_OK_CLIENT(dev->chanptr)) {
+ POSTCODE_LINUX_4(DEVICE_CREATE_FAILURE_PC,
+ dev_no, bus_no,
+ POSTCODE_SEVERITY_ERR);
+ result = CONTROLVM_RESP_ERROR_CHANNEL_INVALID;
+ goto cleanup;
+ }
+ cmd.msgtype = GUEST_ADD_VHBA;
+ cmd.add_vhba.chanptr = dev->chanptr;
+ cmd.add_vhba.bus_no = bus_no;
+ cmd.add_vhba.device_no = dev_no;
+ cmd.add_vhba.instance_uuid = dev->instance_uuid;
+ cmd.add_vhba.intr = dev->intr;
+ } else if (uuid_le_cmp(dev->channel_uuid,
+ spar_vnic_channel_protocol_uuid) == 0) {
+ wait_for_valid_guid(&((struct channel_header __iomem *)
+ (dev->chanptr))->chtype);
+ if (!SPAR_VNIC_CHANNEL_OK_CLIENT(dev->chanptr)) {
+ POSTCODE_LINUX_4(DEVICE_CREATE_FAILURE_PC,
+ dev_no, bus_no,
+ POSTCODE_SEVERITY_ERR);
+ result = CONTROLVM_RESP_ERROR_CHANNEL_INVALID;
+ goto cleanup;
+ }
+ cmd.msgtype = GUEST_ADD_VNIC;
+ cmd.add_vnic.chanptr = dev->chanptr;
+ cmd.add_vnic.bus_no = bus_no;
+ cmd.add_vnic.device_no = dev_no;
+ cmd.add_vnic.instance_uuid = dev->instance_uuid;
+ cmd.add_vhba.intr = dev->intr;
+ } else {
+ POSTCODE_LINUX_4(DEVICE_CREATE_FAILURE_PC, dev_no,
+ bus_no, POSTCODE_SEVERITY_ERR);
+ result = CONTROLVM_RESP_ERROR_CHANNEL_TYPE_UNKNOWN;
+ goto cleanup;
+ }
+
+ if (!virt_control_chan_func) {
+ POSTCODE_LINUX_4(DEVICE_CREATE_FAILURE_PC, dev_no,
+ bus_no, POSTCODE_SEVERITY_ERR);
+ result = CONTROLVM_RESP_ERROR_VIRTPCI_DRIVER_FAILURE;
+ goto cleanup;
+ }
+
+ if (!virt_control_chan_func(&cmd)) {
+ POSTCODE_LINUX_4(DEVICE_CREATE_FAILURE_PC, dev_no,
+ bus_no, POSTCODE_SEVERITY_ERR);
+ result =
+ CONTROLVM_RESP_ERROR_VIRTPCI_DRIVER_CALLBACK_ERROR;
+ goto cleanup;
+ }
+
+ bus->device[dev_no] = dev;
+ POSTCODE_LINUX_4(DEVICE_CREATE_SUCCESS_PC, dev_no,
+ bus_no, POSTCODE_SEVERITY_INFO);
+ return CONTROLVM_RESP_SUCCESS;
+ }
+ read_unlock(&bus_list_lock);
+
+ POSTCODE_LINUX_4(DEVICE_CREATE_FAILURE_PC, dev_no, bus_no,
+ POSTCODE_SEVERITY_ERR);
+ result = CONTROLVM_RESP_ERROR_BUS_INVALID;
+
+cleanup:
+ if (!msg->hdr.flags.test_message) {
+ uislib_iounmap(dev->chanptr);
+ dev->chanptr = NULL;
+ }
+
+ kfree(dev);
+ return result;
+}
+
+static int pause_device(struct controlvm_message *msg)
+{
+ u32 bus_no, dev_no;
+ struct bus_info *bus;
+ struct device_info *dev;
+ struct guest_msgs cmd;
+ int retval = CONTROLVM_RESP_SUCCESS;
+
+ bus_no = msg->cmd.device_change_state.bus_no;
+ dev_no = msg->cmd.device_change_state.dev_no;
+
+ read_lock(&bus_list_lock);
+ for (bus = bus_list; bus; bus = bus->next) {
+ if (bus->bus_no == bus_no) {
+ /* make sure the device number is valid */
+ if (dev_no >= bus->device_count) {
+ retval = CONTROLVM_RESP_ERROR_DEVICE_INVALID;
+ } else {
+ /* make sure this device exists */
+ dev = bus->device[dev_no];
+ if (!dev) {
+ retval =
+ CONTROLVM_RESP_ERROR_ALREADY_DONE;
+ }
+ }
+ break;
+ }
+ }
+ if (!bus)
+ retval = CONTROLVM_RESP_ERROR_BUS_INVALID;
+
+ read_unlock(&bus_list_lock);
+ if (retval == CONTROLVM_RESP_SUCCESS) {
+ /* the msg is bound for virtpci; send
+ * guest_msgs struct to callback
+ */
+ if (uuid_le_cmp(dev->channel_uuid,
+ spar_vhba_channel_protocol_uuid) == 0) {
+ cmd.msgtype = GUEST_PAUSE_VHBA;
+ cmd.pause_vhba.chanptr = dev->chanptr;
+ } else if (uuid_le_cmp(dev->channel_uuid,
+ spar_vnic_channel_protocol_uuid) == 0) {
+ cmd.msgtype = GUEST_PAUSE_VNIC;
+ cmd.pause_vnic.chanptr = dev->chanptr;
+ } else {
+ return CONTROLVM_RESP_ERROR_CHANNEL_TYPE_UNKNOWN;
+ }
+ if (!virt_control_chan_func)
+ return CONTROLVM_RESP_ERROR_VIRTPCI_DRIVER_FAILURE;
+ if (!virt_control_chan_func(&cmd)) {
+ return
+ CONTROLVM_RESP_ERROR_VIRTPCI_DRIVER_CALLBACK_ERROR;
+ }
+ }
+ return retval;
+}
+
+static int resume_device(struct controlvm_message *msg)
+{
+ u32 bus_no, dev_no;
+ struct bus_info *bus;
+ struct device_info *dev;
+ struct guest_msgs cmd;
+ int retval = CONTROLVM_RESP_SUCCESS;
+
+ bus_no = msg->cmd.device_change_state.bus_no;
+ dev_no = msg->cmd.device_change_state.dev_no;
+
+ read_lock(&bus_list_lock);
+ for (bus = bus_list; bus; bus = bus->next) {
+ if (bus->bus_no == bus_no) {
+ /* make sure the device number is valid */
+ if (dev_no >= bus->device_count) {
+ retval = CONTROLVM_RESP_ERROR_DEVICE_INVALID;
+ } else {
+ /* make sure this device exists */
+ dev = bus->device[dev_no];
+ if (!dev) {
+ retval =
+ CONTROLVM_RESP_ERROR_ALREADY_DONE;
+ }
+ }
+ break;
+ }
+ }
+
+ if (!bus)
+ retval = CONTROLVM_RESP_ERROR_BUS_INVALID;
+
+ read_unlock(&bus_list_lock);
+ /* the msg is bound for virtpci; send
+ * guest_msgs struct to callback
+ */
+ if (retval == CONTROLVM_RESP_SUCCESS) {
+ if (uuid_le_cmp(dev->channel_uuid,
+ spar_vhba_channel_protocol_uuid) == 0) {
+ cmd.msgtype = GUEST_RESUME_VHBA;
+ cmd.resume_vhba.chanptr = dev->chanptr;
+ } else if (uuid_le_cmp(dev->channel_uuid,
+ spar_vnic_channel_protocol_uuid) == 0) {
+ cmd.msgtype = GUEST_RESUME_VNIC;
+ cmd.resume_vnic.chanptr = dev->chanptr;
+ } else {
+ return CONTROLVM_RESP_ERROR_CHANNEL_TYPE_UNKNOWN;
+ }
+ if (!virt_control_chan_func)
+ return CONTROLVM_RESP_ERROR_VIRTPCI_DRIVER_FAILURE;
+ if (!virt_control_chan_func(&cmd)) {
+ return
+ CONTROLVM_RESP_ERROR_VIRTPCI_DRIVER_CALLBACK_ERROR;
+ }
+ }
+ return retval;
+}
+
+static int destroy_device(struct controlvm_message *msg, char *buf)
+{
+ u32 bus_no, dev_no;
+ struct bus_info *bus;
+ struct device_info *dev;
+ struct guest_msgs cmd;
+ int retval = CONTROLVM_RESP_SUCCESS;
+
+ bus_no = msg->cmd.destroy_device.bus_no;
+ dev_no = msg->cmd.destroy_device.bus_no;
+
+ read_lock(&bus_list_lock);
+ for (bus = bus_list; bus; bus = bus->next) {
+ if (bus->bus_no == bus_no) {
+ /* make sure the device number is valid */
+ if (dev_no >= bus->device_count) {
+ retval = CONTROLVM_RESP_ERROR_DEVICE_INVALID;
+ } else {
+ /* make sure this device exists */
+ dev = bus->device[dev_no];
+ if (!dev) {
+ retval =
+ CONTROLVM_RESP_ERROR_ALREADY_DONE;
+ }
+ }
+ break;
+ }
+ }
+
+ if (!bus)
+ retval = CONTROLVM_RESP_ERROR_BUS_INVALID;
+ read_unlock(&bus_list_lock);
+ if (retval == CONTROLVM_RESP_SUCCESS) {
+ /* the msg is bound for virtpci; send
+ * guest_msgs struct to callback
+ */
+ if (uuid_le_cmp(dev->channel_uuid,
+ spar_vhba_channel_protocol_uuid) == 0) {
+ cmd.msgtype = GUEST_DEL_VHBA;
+ cmd.del_vhba.chanptr = dev->chanptr;
+ } else if (uuid_le_cmp(dev->channel_uuid,
+ spar_vnic_channel_protocol_uuid) == 0) {
+ cmd.msgtype = GUEST_DEL_VNIC;
+ cmd.del_vnic.chanptr = dev->chanptr;
+ } else {
+ return
+ CONTROLVM_RESP_ERROR_CHANNEL_TYPE_UNKNOWN;
+ }
+ if (!virt_control_chan_func) {
+ return
+ CONTROLVM_RESP_ERROR_VIRTPCI_DRIVER_FAILURE;
+ }
+ if (!virt_control_chan_func(&cmd)) {
+ return
+ CONTROLVM_RESP_ERROR_VIRTPCI_DRIVER_CALLBACK_ERROR;
+ }
+/* you must disable channel interrupts BEFORE you unmap the channel,
+ * because if you unmap first, there may still be some activity going
+ * on which accesses the channel and you will get a "unable to handle
+ * kernel paging request"
+ */
+ if (dev->polling)
+ uislib_disable_channel_interrupts(bus_no, dev_no);
+ /* unmap the channel memory for the device. */
+ if (!msg->hdr.flags.test_message)
+ uislib_iounmap(dev->chanptr);
+ kfree(dev);
+ bus->device[dev_no] = NULL;
+ }
+ return retval;
+}
+
+static int
+init_chipset(struct controlvm_message *msg, char *buf)
+{
+ POSTCODE_LINUX_2(CHIPSET_INIT_ENTRY_PC, POSTCODE_SEVERITY_INFO);
+
+ max_bus_count = msg->cmd.init_chipset.bus_count;
+ platform_no = msg->cmd.init_chipset.platform_number;
+ phys_data_chan = 0;
+
+ /* We need to make sure we have our functions registered
+ * before processing messages. If we are a test vehicle the
+ * test_message for init_chipset will be set. We can ignore the
+ * waits for the callbacks, since this will be manually entered
+ * from a user. If no test_message is set, we will wait for the
+ * functions.
+ */
+ if (!msg->hdr.flags.test_message)
+ WAIT_ON_CALLBACK(virt_control_chan_func);
+
+ chipset_inited = 1;
+ POSTCODE_LINUX_2(CHIPSET_INIT_EXIT_PC, POSTCODE_SEVERITY_INFO);
+
+ return CONTROLVM_RESP_SUCCESS;
+}
+
+static int delete_bus_glue(u32 bus_no)
+{
+ struct controlvm_message msg;
+
+ init_msg_header(&msg, CONTROLVM_BUS_DESTROY, 0, 0);
+ msg.cmd.destroy_bus.bus_no = bus_no;
+ if (destroy_bus(&msg, NULL) != CONTROLVM_RESP_SUCCESS)
+ return 0;
+ return 1;
+}
+
+static int delete_device_glue(u32 bus_no, u32 dev_no)
+{
+ struct controlvm_message msg;
+
+ init_msg_header(&msg, CONTROLVM_DEVICE_DESTROY, 0, 0);
+ msg.cmd.destroy_device.bus_no = bus_no;
+ msg.cmd.destroy_device.dev_no = dev_no;
+ if (destroy_device(&msg, NULL) != CONTROLVM_RESP_SUCCESS)
+ return 0;
+ return 1;
+}
+
+int
+uislib_client_inject_add_bus(u32 bus_no, uuid_le inst_uuid,
+ u64 channel_addr, ulong n_channel_bytes)
+{
+ struct controlvm_message msg;
+
+ /* step 0: init the chipset */
+ POSTCODE_LINUX_3(CHIPSET_INIT_ENTRY_PC, bus_no, POSTCODE_SEVERITY_INFO);
+
+ if (!chipset_inited) {
+ /* step: initialize the chipset */
+ init_msg_header(&msg, CONTROLVM_CHIPSET_INIT, 0, 0);
+ /* this change is needed so that console will come up
+ * OK even when the bus 0 create comes in late. If the
+ * bus 0 create is the first create, then the add_vnic
+ * will work fine, but if the bus 0 create arrives
+ * after number 4, then the add_vnic will fail, and the
+ * ultraboot will fail.
+ */
+ msg.cmd.init_chipset.bus_count = 23;
+ msg.cmd.init_chipset.switch_count = 0;
+ if (init_chipset(&msg, NULL) != CONTROLVM_RESP_SUCCESS)
+ return 0;
+ POSTCODE_LINUX_3(CHIPSET_INIT_EXIT_PC, bus_no,
+ POSTCODE_SEVERITY_INFO);
+ }
+
+ /* step 1: create a bus */
+ POSTCODE_LINUX_3(BUS_CREATE_ENTRY_PC, bus_no,
+ POSTCODE_SEVERITY_WARNING);
+ init_msg_header(&msg, CONTROLVM_BUS_CREATE, 0, 0);
+ msg.cmd.create_bus.bus_no = bus_no;
+ msg.cmd.create_bus.dev_count = 23; /* devNo+1; */
+ msg.cmd.create_bus.channel_addr = channel_addr;
+ msg.cmd.create_bus.channel_bytes = n_channel_bytes;
+ if (create_bus(&msg, NULL) != CONTROLVM_RESP_SUCCESS) {
+ POSTCODE_LINUX_3(BUS_CREATE_FAILURE_PC, bus_no,
+ POSTCODE_SEVERITY_ERR);
+ return 0;
+ }
+ POSTCODE_LINUX_3(BUS_CREATE_EXIT_PC, bus_no, POSTCODE_SEVERITY_INFO);
+
+ return 1;
+}
+EXPORT_SYMBOL_GPL(uislib_client_inject_add_bus);
+
+int
+uislib_client_inject_del_bus(u32 bus_no)
+{
+ return delete_bus_glue(bus_no);
+}
+EXPORT_SYMBOL_GPL(uislib_client_inject_del_bus);
+
+int
+uislib_client_inject_pause_vhba(u32 bus_no, u32 dev_no)
+{
+ struct controlvm_message msg;
+ int rc;
+
+ init_msg_header(&msg, CONTROLVM_DEVICE_CHANGESTATE, 0, 0);
+ msg.cmd.device_change_state.bus_no = bus_no;
+ msg.cmd.device_change_state.dev_no = dev_no;
+ msg.cmd.device_change_state.state = segment_state_standby;
+ rc = pause_device(&msg);
+ if (rc != CONTROLVM_RESP_SUCCESS)
+ return rc;
+ return 0;
+}
+EXPORT_SYMBOL_GPL(uislib_client_inject_pause_vhba);
+
+int
+uislib_client_inject_resume_vhba(u32 bus_no, u32 dev_no)
+{
+ struct controlvm_message msg;
+ int rc;
+
+ init_msg_header(&msg, CONTROLVM_DEVICE_CHANGESTATE, 0, 0);
+ msg.cmd.device_change_state.bus_no = bus_no;
+ msg.cmd.device_change_state.dev_no = dev_no;
+ msg.cmd.device_change_state.state = segment_state_running;
+ rc = resume_device(&msg);
+ if (rc != CONTROLVM_RESP_SUCCESS)
+ return rc;
+ return 0;
+}
+EXPORT_SYMBOL_GPL(uislib_client_inject_resume_vhba);
+
+int
+uislib_client_inject_add_vhba(u32 bus_no, u32 dev_no,
+ u64 phys_chan_addr, u32 chan_bytes,
+ int is_test_addr, uuid_le inst_uuid,
+ struct irq_info *intr)
+{
+ struct controlvm_message msg;
+
+ /* chipset init'ed with bus bus has been previously created -
+ * Verify it still exists step 2: create the VHBA device on the
+ * bus
+ */
+ POSTCODE_LINUX_4(VHBA_CREATE_ENTRY_PC, dev_no, bus_no,
+ POSTCODE_SEVERITY_INFO);
+
+ init_msg_header(&msg, CONTROLVM_DEVICE_CREATE, 0, 0);
+ if (is_test_addr)
+ /* signify that the physical channel address does NOT
+ * need to be ioremap()ed
+ */
+ msg.hdr.flags.test_message = 1;
+ msg.cmd.create_device.bus_no = bus_no;
+ msg.cmd.create_device.dev_no = dev_no;
+ msg.cmd.create_device.dev_inst_uuid = inst_uuid;
+ if (intr)
+ msg.cmd.create_device.intr = *intr;
+ else
+ memset(&msg.cmd.create_device.intr, 0,
+ sizeof(struct irq_info));
+ msg.cmd.create_device.channel_addr = phys_chan_addr;
+ if (chan_bytes < MIN_IO_CHANNEL_SIZE) {
+ POSTCODE_LINUX_4(VHBA_CREATE_FAILURE_PC, chan_bytes,
+ MIN_IO_CHANNEL_SIZE, POSTCODE_SEVERITY_ERR);
+ return 0;
+ }
+ msg.cmd.create_device.channel_bytes = chan_bytes;
+ msg.cmd.create_device.data_type_uuid = spar_vhba_channel_protocol_uuid;
+ if (create_device(&msg, NULL) != CONTROLVM_RESP_SUCCESS) {
+ POSTCODE_LINUX_4(VHBA_CREATE_FAILURE_PC, dev_no, bus_no,
+ POSTCODE_SEVERITY_ERR);
+ return 0;
+ }
+ POSTCODE_LINUX_4(VHBA_CREATE_SUCCESS_PC, dev_no, bus_no,
+ POSTCODE_SEVERITY_INFO);
+ return 1;
+}
+EXPORT_SYMBOL_GPL(uislib_client_inject_add_vhba);
+
+int
+uislib_client_inject_del_vhba(u32 bus_no, u32 dev_no)
+{
+ return delete_device_glue(bus_no, dev_no);
+}
+EXPORT_SYMBOL_GPL(uislib_client_inject_del_vhba);
+
+int
+uislib_client_inject_add_vnic(u32 bus_no, u32 dev_no,
+ u64 phys_chan_addr, u32 chan_bytes,
+ int is_test_addr, uuid_le inst_uuid,
+ struct irq_info *intr)
+{
+ struct controlvm_message msg;
+
+ /* chipset init'ed with bus bus has been previously created -
+ * Verify it still exists step 2: create the VNIC device on the
+ * bus
+ */
+ POSTCODE_LINUX_4(VNIC_CREATE_ENTRY_PC, dev_no, bus_no,
+ POSTCODE_SEVERITY_INFO);
+
+ init_msg_header(&msg, CONTROLVM_DEVICE_CREATE, 0, 0);
+ if (is_test_addr)
+ /* signify that the physical channel address does NOT
+ * need to be ioremap()ed
+ */
+ msg.hdr.flags.test_message = 1;
+ msg.cmd.create_device.bus_no = bus_no;
+ msg.cmd.create_device.dev_no = dev_no;
+ msg.cmd.create_device.dev_inst_uuid = inst_uuid;
+ if (intr)
+ msg.cmd.create_device.intr = *intr;
+ else
+ memset(&msg.cmd.create_device.intr, 0,
+ sizeof(struct irq_info));
+ msg.cmd.create_device.channel_addr = phys_chan_addr;
+ if (chan_bytes < MIN_IO_CHANNEL_SIZE) {
+ POSTCODE_LINUX_4(VNIC_CREATE_FAILURE_PC, chan_bytes,
+ MIN_IO_CHANNEL_SIZE, POSTCODE_SEVERITY_ERR);
+ return 0;
+ }
+ msg.cmd.create_device.channel_bytes = chan_bytes;
+ msg.cmd.create_device.data_type_uuid = spar_vnic_channel_protocol_uuid;
+ if (create_device(&msg, NULL) != CONTROLVM_RESP_SUCCESS) {
+ POSTCODE_LINUX_4(VNIC_CREATE_FAILURE_PC, dev_no, bus_no,
+ POSTCODE_SEVERITY_ERR);
+ return 0;
+ }
+
+ POSTCODE_LINUX_4(VNIC_CREATE_SUCCESS_PC, dev_no, bus_no,
+ POSTCODE_SEVERITY_INFO);
+ return 1;
+}
+EXPORT_SYMBOL_GPL(uislib_client_inject_add_vnic);
+
+int
+uislib_client_inject_pause_vnic(u32 bus_no, u32 dev_no)
+{
+ struct controlvm_message msg;
+ int rc;
+
+ init_msg_header(&msg, CONTROLVM_DEVICE_CHANGESTATE, 0, 0);
+ msg.cmd.device_change_state.bus_no = bus_no;
+ msg.cmd.device_change_state.dev_no = dev_no;
+ msg.cmd.device_change_state.state = segment_state_standby;
+ rc = pause_device(&msg);
+ if (rc != CONTROLVM_RESP_SUCCESS)
+ return -1;
+ return 0;
+}
+EXPORT_SYMBOL_GPL(uislib_client_inject_pause_vnic);
+
+int
+uislib_client_inject_resume_vnic(u32 bus_no, u32 dev_no)
+{
+ struct controlvm_message msg;
+ int rc;
+
+ init_msg_header(&msg, CONTROLVM_DEVICE_CHANGESTATE, 0, 0);
+ msg.cmd.device_change_state.bus_no = bus_no;
+ msg.cmd.device_change_state.dev_no = dev_no;
+ msg.cmd.device_change_state.state = segment_state_running;
+ rc = resume_device(&msg);
+ if (rc != CONTROLVM_RESP_SUCCESS)
+ return -1;
+ return 0;
+}
+EXPORT_SYMBOL_GPL(uislib_client_inject_resume_vnic);
+
+int
+uislib_client_inject_del_vnic(u32 bus_no, u32 dev_no)
+{
+ return delete_device_glue(bus_no, dev_no);
+}
+EXPORT_SYMBOL_GPL(uislib_client_inject_del_vnic);
+
+void *
+uislib_cache_alloc(struct kmem_cache *cur_pool, char *fn, int ln)
+{
+ /* __GFP_NORETRY means "ok to fail", meaning kmalloc() can
+ * return NULL. If you do NOT specify __GFP_NORETRY, Linux
+ * will go to extreme measures to get memory for you (like,
+ * invoke oom killer), which will probably cripple the system.
+ */
+ void *p = kmem_cache_alloc(cur_pool, GFP_ATOMIC | __GFP_NORETRY);
+
+ if (!p)
+ return NULL;
+ return p;
+}
+EXPORT_SYMBOL_GPL(uislib_cache_alloc);
+
+void
+uislib_cache_free(struct kmem_cache *cur_pool, void *p, char *fn, int ln)
+{
+ if (!p)
+ return;
+ kmem_cache_free(cur_pool, p);
+}
+EXPORT_SYMBOL_GPL(uislib_cache_free);
+
+/*****************************************************/
+/* proc filesystem callback functions */
+/*****************************************************/
+
+#define PLINE(...) uisutil_add_proc_line_ex(&tot, buff, \
+ buff_len, __VA_ARGS__)
+
+static int
+info_debugfs_read_helper(char **buff, int *buff_len)
+{
+ int i, tot = 0;
+ struct bus_info *bus;
+
+ if (PLINE("\nBuses:\n") < 0)
+ goto err_done;
+
+ read_lock(&bus_list_lock);
+ for (bus = bus_list; bus; bus = bus->next) {
+ if (PLINE(" bus=0x%p, busNo=%d, deviceCount=%d\n",
+ bus, bus->bus_no, bus->device_count) < 0)
+ goto err_done_unlock;
+
+ if (PLINE(" Devices:\n") < 0)
+ goto err_done_unlock;
+
+ for (i = 0; i < bus->device_count; i++) {
+ if (bus->device[i]) {
+ if (PLINE(" busNo %d, device[%i]: 0x%p, chanptr=0x%p, swtch=0x%p\n",
+ bus->bus_no, i, bus->device[i],
+ bus->device[i]->chanptr,
+ bus->device[i]->swtch) < 0)
+ goto err_done_unlock;
+
+ if (PLINE(" first_busy_cnt=%llu, moved_to_tail_cnt=%llu, last_on_list_cnt=%llu\n",
+ bus->device[i]->first_busy_cnt,
+ bus->device[i]->moved_to_tail_cnt,
+ bus->device[i]->last_on_list_cnt) < 0)
+ goto err_done_unlock;
+ }
+ }
+ }
+ read_unlock(&bus_list_lock);
+
+ if (PLINE("UisUtils_Registered_Services: %d\n",
+ atomic_read(&uisutils_registered_services)) < 0)
+ goto err_done;
+ if (PLINE("cycles_before_wait %llu wait_cycles:%llu\n",
+ cycles_before_wait, wait_cycles) < 0)
+ goto err_done;
+ if (PLINE("tot_wakeup_cnt %llu:tot_wait_cnt %llu:tot_schedule_cnt %llu\n",
+ tot_wakeup_cnt, tot_wait_cnt, tot_schedule_cnt) < 0)
+ goto err_done;
+ if (PLINE("en_smart_wakeup %d\n", en_smart_wakeup) < 0)
+ goto err_done;
+ if (PLINE("tot_moved_to_tail_cnt %llu\n", tot_moved_to_tail_cnt) < 0)
+ goto err_done;
+
+ return tot;
+
+err_done_unlock:
+ read_unlock(&bus_list_lock);
+err_done:
+ return -1;
+}
+
+static ssize_t info_debugfs_read(struct file *file, char __user *buf,
+ size_t len, loff_t *offset)
+{
+ char *temp;
+ int total_bytes = 0;
+ int remaining_bytes = PROC_READ_BUFFER_SIZE;
+
+/* *start = buf; */
+ if (!debug_buf) {
+ debug_buf = vmalloc(PROC_READ_BUFFER_SIZE);
+
+ if (!debug_buf)
+ return -ENOMEM;
+ }
+
+ temp = debug_buf;
+
+ if ((*offset == 0) || (!debug_buf_valid)) {
+ /* if the read fails, then -1 will be returned */
+ total_bytes = info_debugfs_read_helper(&temp, &remaining_bytes);
+ debug_buf_valid = 1;
+ } else {
+ total_bytes = strlen(debug_buf);
+ }
+
+ return simple_read_from_buffer(buf, len, offset,
+ debug_buf, total_bytes);
+}
+
+static struct device_info *find_dev(u32 bus_no, u32 dev_no)
+{
+ struct bus_info *bus;
+ struct device_info *dev = NULL;
+
+ read_lock(&bus_list_lock);
+ for (bus = bus_list; bus; bus = bus->next) {
+ if (bus->bus_no == bus_no) {
+ /* make sure the device number is valid */
+ if (dev_no >= bus->device_count)
+ break;
+ dev = bus->device[dev_no];
+ break;
+ }
+ }
+ read_unlock(&bus_list_lock);
+ return dev;
+}
+
+/* This thread calls the "interrupt" function for each device that has
+ * enabled such using uislib_enable_channel_interrupts(). The "interrupt"
+ * function typically reads and processes the devices's channel input
+ * queue. This thread repeatedly does this, until the thread is told to stop
+ * (via uisthread_stop()). Sleeping rules:
+ * - If we have called the "interrupt" function for all devices, and all of
+ * them have reported "nothing processed" (returned 0), then we will go to
+ * sleep for a maximum of POLLJIFFIES_NORMAL jiffies.
+ * - If anyone calls uislib_force_channel_interrupt(), the above jiffy
+ * sleep will be interrupted, and we will resume calling the "interrupt"
+ * function for all devices.
+ * - The list of devices is dynamically re-ordered in order to
+ * attempt to preserve fairness. Whenever we spin thru the list of
+ * devices and call the dev->interrupt() function, if we find
+ * devices which report that there is still more work to do, the
+ * the first such device we find is moved to the end of the device
+ * list. This ensures that extremely busy devices don't starve out
+ * less-busy ones.
+ *
+ */
+static int process_incoming(void *v)
+{
+ unsigned long long cur_cycles, old_cycles, idle_cycles, delta_cycles;
+ struct list_head *new_tail = NULL;
+ int i;
+
+ UIS_DAEMONIZE("dev_incoming");
+ for (i = 0; i < 16; i++) {
+ old_cycles = get_cycles();
+ wait_event_timeout(poll_dev_wake_q,
+ 0, POLLJIFFIES_NORMAL);
+ cur_cycles = get_cycles();
+ if (wait_cycles == 0) {
+ wait_cycles = (cur_cycles - old_cycles);
+ } else {
+ if (wait_cycles < (cur_cycles - old_cycles))
+ wait_cycles = (cur_cycles - old_cycles);
+ }
+ }
+ cycles_before_wait = wait_cycles;
+ idle_cycles = 0;
+ poll_dev_start = 0;
+ while (1) {
+ struct list_head *lelt, *tmp;
+ struct device_info *dev = NULL;
+
+ /* poll each channel for input */
+ down(&poll_dev_lock);
+ new_tail = NULL;
+ list_for_each_safe(lelt, tmp, &poll_dev_chan) {
+ int rc = 0;
+
+ dev = list_entry(lelt, struct device_info,
+ list_polling_device_channels);
+ down(&dev->interrupt_callback_lock);
+ if (dev->interrupt)
+ rc = dev->interrupt(dev->interrupt_context);
+ else
+ continue;
+ up(&dev->interrupt_callback_lock);
+ if (rc) {
+ /* dev->interrupt returned, but there
+ * is still more work to do.
+ * Reschedule work to occur as soon as
+ * possible. */
+ idle_cycles = 0;
+ if (!new_tail) {
+ dev->first_busy_cnt++;
+ if (!
+ (list_is_last
+ (lelt,
+ &poll_dev_chan))) {
+ new_tail = lelt;
+ dev->moved_to_tail_cnt++;
+ } else {
+ dev->last_on_list_cnt++;
+ }
+ }
+ }
+ if (kthread_should_stop())
+ break;
+ }
+ if (new_tail) {
+ tot_moved_to_tail_cnt++;
+ list_move_tail(new_tail, &poll_dev_chan);
+ }
+ up(&poll_dev_lock);
+ cur_cycles = get_cycles();
+ delta_cycles = cur_cycles - old_cycles;
+ old_cycles = cur_cycles;
+
+ /* At this point, we have scanned thru all of the
+ * channels, and at least one of the following is true:
+ * - there is no input waiting on any of the channels
+ * - we have received a signal to stop this thread
+ */
+ if (kthread_should_stop())
+ break;
+ if (en_smart_wakeup == 0xFF)
+ break;
+ /* wait for POLLJIFFIES_NORMAL jiffies, or until
+ * someone wakes up poll_dev_wake_q,
+ * whichever comes first only do a wait when we have
+ * been idle for cycles_before_wait cycles.
+ */
+ if (idle_cycles > cycles_before_wait) {
+ poll_dev_start = 0;
+ tot_wait_cnt++;
+ wait_event_timeout(poll_dev_wake_q,
+ poll_dev_start,
+ POLLJIFFIES_NORMAL);
+ poll_dev_start = 1;
+ } else {
+ tot_schedule_cnt++;
+ schedule();
+ idle_cycles = idle_cycles + delta_cycles;
+ }
+ }
+ complete_and_exit(&incoming_ti.has_stopped, 0);
+}
+
+static BOOL
+initialize_incoming_thread(void)
+{
+ if (incoming_started)
+ return TRUE;
+ if (!uisthread_start(&incoming_ti,
+ &process_incoming, NULL, "dev_incoming")) {
+ return FALSE;
+ }
+ incoming_started = TRUE;
+ return TRUE;
+}
+
+/* Add a new device/channel to the list being processed by
+ * process_incoming().
+ * <interrupt> - indicates the function to call periodically.
+ * <interrupt_context> - indicates the data to pass to the <interrupt>
+ * function.
+ */
+void
+uislib_enable_channel_interrupts(u32 bus_no, u32 dev_no,
+ int (*interrupt)(void *),
+ void *interrupt_context)
+{
+ struct device_info *dev;
+
+ dev = find_dev(bus_no, dev_no);
+ if (!dev)
+ return;
+
+ down(&poll_dev_lock);
+ initialize_incoming_thread();
+ dev->interrupt = interrupt;
+ dev->interrupt_context = interrupt_context;
+ dev->polling = TRUE;
+ list_add_tail(&dev->list_polling_device_channels,
+ &poll_dev_chan);
+ up(&poll_dev_lock);
+}
+EXPORT_SYMBOL_GPL(uislib_enable_channel_interrupts);
+
+/* Remove a device/channel from the list being processed by
+ * process_incoming().
+ */
+void
+uislib_disable_channel_interrupts(u32 bus_no, u32 dev_no)
+{
+ struct device_info *dev;
+
+ dev = find_dev(bus_no, dev_no);
+ if (!dev)
+ return;
+ down(&poll_dev_lock);
+ list_del(&dev->list_polling_device_channels);
+ dev->polling = FALSE;
+ dev->interrupt = NULL;
+ up(&poll_dev_lock);
+}
+EXPORT_SYMBOL_GPL(uislib_disable_channel_interrupts);
+
+static void
+do_wakeup_polling_device_channels(struct work_struct *dummy)
+{
+ if (!poll_dev_start) {
+ poll_dev_start = 1;
+ wake_up(&poll_dev_wake_q);
+ }
+}
+
+static DECLARE_WORK(work_wakeup_polling_device_channels,
+ do_wakeup_polling_device_channels);
+
+/* Call this function when you want to send a hint to process_incoming() that
+ * your device might have more requests.
+ */
+void
+uislib_force_channel_interrupt(u32 bus_no, u32 dev_no)
+{
+ if (en_smart_wakeup == 0)
+ return;
+ if (poll_dev_start)
+ return;
+ /* The point of using schedule_work() instead of just doing
+ * the work inline is to force a slight delay before waking up
+ * the process_incoming() thread.
+ */
+ tot_wakeup_cnt++;
+ schedule_work(&work_wakeup_polling_device_channels);
+}
+EXPORT_SYMBOL_GPL(uislib_force_channel_interrupt);
+
+/*****************************************************/
+/* Module Init & Exit functions */
+/*****************************************************/
+
+static int __init
+uislib_mod_init(void)
+{
+ if (!unisys_spar_platform)
+ return -ENODEV;
+
+ /* initialize global pointers to NULL */
+ bus_list = NULL;
+ bus_list_count = 0;
+ max_bus_count = 0;
+ rwlock_init(&bus_list_lock);
+ virt_control_chan_func = NULL;
+
+ /* Issue VMCALL_GET_CONTROLVM_ADDR to get CtrlChanPhysAddr and
+ * then map this physical address to a virtual address. */
+ POSTCODE_LINUX_2(DRIVER_ENTRY_PC, POSTCODE_SEVERITY_INFO);
+
+ dir_debugfs = debugfs_create_dir(DIR_DEBUGFS_ENTRY, NULL);
+ if (dir_debugfs) {
+ info_debugfs_entry = debugfs_create_file(
+ INFO_DEBUGFS_ENTRY_FN, 0444, dir_debugfs, NULL,
+ &debugfs_info_fops);
+
+ platformnumber_debugfs_read = debugfs_create_u32(
+ PLATFORMNUMBER_DEBUGFS_ENTRY_FN, 0444, dir_debugfs,
+ &platform_no);
+
+ cycles_before_wait_debugfs_read = debugfs_create_u64(
+ CYCLES_BEFORE_WAIT_DEBUGFS_ENTRY_FN, 0666, dir_debugfs,
+ &cycles_before_wait);
+
+ smart_wakeup_debugfs_entry = debugfs_create_bool(
+ SMART_WAKEUP_DEBUGFS_ENTRY_FN, 0666, dir_debugfs,
+ &en_smart_wakeup);
+ }
+
+ POSTCODE_LINUX_3(DRIVER_EXIT_PC, 0, POSTCODE_SEVERITY_INFO);
+ return 0;
+}
+
+static void __exit
+uislib_mod_exit(void)
+{
+ if (debug_buf) {
+ vfree(debug_buf);
+ debug_buf = NULL;
+ }
+
+ debugfs_remove(info_debugfs_entry);
+ debugfs_remove(smart_wakeup_debugfs_entry);
+ debugfs_remove(cycles_before_wait_debugfs_read);
+ debugfs_remove(platformnumber_debugfs_read);
+ debugfs_remove(dir_debugfs);
+}
+
+module_init(uislib_mod_init);
+module_exit(uislib_mod_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Usha Srinivasan");
+MODULE_ALIAS("uislib");
+ /* this is extracted during depmod and kept in modules.dep */
diff --git a/drivers/staging/unisys/uislib/uisqueue.c b/drivers/staging/unisys/uislib/uisqueue.c
new file mode 100644
index 000000000..d46dd7428
--- /dev/null
+++ b/drivers/staging/unisys/uislib/uisqueue.c
@@ -0,0 +1,322 @@
+/* uisqueue.c
+ *
+ * Copyright (C) 2010 - 2013 UNISYS CORPORATION
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or (at
+ * your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
+ * NON INFRINGEMENT. See the GNU General Public License for more
+ * details.
+ */
+
+/* @ALL_INSPECTED */
+#include <linux/kernel.h>
+#include <linux/module.h>
+
+#include "uisutils.h"
+
+/* this is shorter than using __FILE__ (full path name) in
+ * debug/info/error messages */
+#define CURRENT_FILE_PC UISLIB_PC_uisqueue_c
+#define __MYFILE__ "uisqueue.c"
+
+#define CHECK_CACHE_ALIGN 0
+
+/*****************************************************/
+/* Exported functions */
+/*****************************************************/
+
+/*
+ * Routine Description:
+ * Tries to insert the prebuilt signal pointed to by pSignal into the nth
+ * Queue of the Channel pointed to by pChannel
+ *
+ * Parameters:
+ * pChannel: (IN) points to the IO Channel
+ * Queue: (IN) nth Queue of the IO Channel
+ * pSignal: (IN) pointer to the signal
+ *
+ * Assumptions:
+ * - pChannel, Queue and pSignal are valid.
+ * - If insertion fails due to a full queue, the caller will determine the
+ * retry policy (e.g. wait & try again, report an error, etc.).
+ *
+ * Return value:
+ * 1 if the insertion succeeds, 0 if the queue was full.
+ */
+unsigned char spar_signal_insert(struct channel_header __iomem *ch, u32 queue,
+ void *sig)
+{
+ void __iomem *psignal;
+ unsigned int head, tail, nof;
+
+ struct signal_queue_header __iomem *pqhdr =
+ (struct signal_queue_header __iomem *)
+ ((char __iomem *)ch + readq(&ch->ch_space_offset))
+ + queue;
+
+ /* capture current head and tail */
+ head = readl(&pqhdr->head);
+ tail = readl(&pqhdr->tail);
+
+ /* queue is full if (head + 1) % n equals tail */
+ if (((head + 1) % readl(&pqhdr->max_slots)) == tail) {
+ nof = readq(&pqhdr->num_overflows) + 1;
+ writeq(nof, &pqhdr->num_overflows);
+ return 0;
+ }
+
+ /* increment the head index */
+ head = (head + 1) % readl(&pqhdr->max_slots);
+
+ /* copy signal to the head location from the area pointed to
+ * by pSignal
+ */
+ psignal = (char __iomem *)pqhdr + readq(&pqhdr->sig_base_offset) +
+ (head * readl(&pqhdr->signal_size));
+ memcpy_toio(psignal, sig, readl(&pqhdr->signal_size));
+
+ mb(); /* channel synch */
+ writel(head, &pqhdr->head);
+
+ writeq(readq(&pqhdr->num_sent) + 1, &pqhdr->num_sent);
+ return 1;
+}
+EXPORT_SYMBOL_GPL(spar_signal_insert);
+
+/*
+ * Routine Description:
+ * Removes one signal from Channel pChannel's nth Queue at the
+ * time of the call and copies it into the memory pointed to by
+ * pSignal.
+ *
+ * Parameters:
+ * pChannel: (IN) points to the IO Channel
+ * Queue: (IN) nth Queue of the IO Channel
+ * pSignal: (IN) pointer to where the signals are to be copied
+ *
+ * Assumptions:
+ * - pChannel and Queue are valid.
+ * - pSignal points to a memory area large enough to hold queue's SignalSize
+ *
+ * Return value:
+ * 1 if the removal succeeds, 0 if the queue was empty.
+ */
+unsigned char
+spar_signal_remove(struct channel_header __iomem *ch, u32 queue, void *sig)
+{
+ void __iomem *psource;
+ unsigned int head, tail;
+ struct signal_queue_header __iomem *pqhdr =
+ (struct signal_queue_header __iomem *)((char __iomem *)ch +
+ readq(&ch->ch_space_offset)) + queue;
+
+ /* capture current head and tail */
+ head = readl(&pqhdr->head);
+ tail = readl(&pqhdr->tail);
+
+ /* queue is empty if the head index equals the tail index */
+ if (head == tail) {
+ writeq(readq(&pqhdr->num_empty) + 1, &pqhdr->num_empty);
+ return 0;
+ }
+
+ /* advance past the 'empty' front slot */
+ tail = (tail + 1) % readl(&pqhdr->max_slots);
+
+ /* copy signal from tail location to the area pointed to by pSignal */
+ psource = (char __iomem *)pqhdr + readq(&pqhdr->sig_base_offset) +
+ (tail * readl(&pqhdr->signal_size));
+ memcpy_fromio(sig, psource, readl(&pqhdr->signal_size));
+
+ mb(); /* channel synch */
+ writel(tail, &pqhdr->tail);
+
+ writeq(readq(&pqhdr->num_received) + 1,
+ &pqhdr->num_received);
+ return 1;
+}
+EXPORT_SYMBOL_GPL(spar_signal_remove);
+
+/*
+ * Routine Description:
+ * Removes all signals present in Channel pChannel's nth Queue at the
+ * time of the call and copies them into the memory pointed to by
+ * pSignal. Returns the # of signals copied as the value of the routine.
+ *
+ * Parameters:
+ * pChannel: (IN) points to the IO Channel
+ * Queue: (IN) nth Queue of the IO Channel
+ * pSignal: (IN) pointer to where the signals are to be copied
+ *
+ * Assumptions:
+ * - pChannel and Queue are valid.
+ * - pSignal points to a memory area large enough to hold Queue's MaxSignals
+ * # of signals, each of which is Queue's SignalSize.
+ *
+ * Return value:
+ * # of signals copied.
+ */
+unsigned int spar_signal_remove_all(struct channel_header *ch, u32 queue,
+ void *sig)
+{
+ void *psource;
+ unsigned int head, tail, count = 0;
+ struct signal_queue_header *pqhdr =
+ (struct signal_queue_header *)((char *)ch +
+ ch->ch_space_offset) + queue;
+
+ /* capture current head and tail */
+ head = pqhdr->head;
+ tail = pqhdr->tail;
+
+ /* queue is empty if the head index equals the tail index */
+ if (head == tail)
+ return 0;
+
+ while (head != tail) {
+ /* advance past the 'empty' front slot */
+ tail = (tail + 1) % pqhdr->max_slots;
+
+ /* copy signal from tail location to the area pointed
+ * to by pSignal
+ */
+ psource =
+ (char *)pqhdr + pqhdr->sig_base_offset +
+ (tail * pqhdr->signal_size);
+ memcpy((char *)sig + (pqhdr->signal_size * count),
+ psource, pqhdr->signal_size);
+
+ mb(); /* channel synch */
+ pqhdr->tail = tail;
+
+ count++;
+ pqhdr->num_received++;
+ }
+
+ return count;
+}
+
+/*
+ * Routine Description:
+ * Determine whether a signal queue is empty.
+ *
+ * Parameters:
+ * pChannel: (IN) points to the IO Channel
+ * Queue: (IN) nth Queue of the IO Channel
+ *
+ * Return value:
+ * 1 if the signal queue is empty, 0 otherwise.
+ */
+unsigned char spar_signalqueue_empty(struct channel_header __iomem *ch,
+ u32 queue)
+{
+ struct signal_queue_header __iomem *pqhdr =
+ (struct signal_queue_header __iomem *)((char __iomem *)ch +
+ readq(&ch->ch_space_offset)) + queue;
+ return readl(&pqhdr->head) == readl(&pqhdr->tail);
+}
+EXPORT_SYMBOL_GPL(spar_signalqueue_empty);
+
+unsigned long long
+uisqueue_interlocked_or(unsigned long long __iomem *tgt,
+ unsigned long long set)
+{
+ unsigned long long i;
+ unsigned long long j;
+
+ j = readq(tgt);
+ do {
+ i = j;
+ j = cmpxchg((__force unsigned long long *)tgt, i, i | set);
+
+ } while (i != j);
+
+ return j;
+}
+EXPORT_SYMBOL_GPL(uisqueue_interlocked_or);
+
+unsigned long long
+uisqueue_interlocked_and(unsigned long long __iomem *tgt,
+ unsigned long long set)
+{
+ unsigned long long i;
+ unsigned long long j;
+
+ j = readq(tgt);
+ do {
+ i = j;
+ j = cmpxchg((__force unsigned long long *)tgt, i, i & set);
+
+ } while (i != j);
+
+ return j;
+}
+EXPORT_SYMBOL_GPL(uisqueue_interlocked_and);
+
+static u8
+do_locked_client_insert(struct uisqueue_info *queueinfo,
+ unsigned int whichqueue,
+ void *signal,
+ spinlock_t *lock,
+ u8 *channel_id)
+{
+ unsigned long flags;
+ u8 rc = 0;
+
+ spin_lock_irqsave(lock, flags);
+ if (!spar_channel_client_acquire_os(queueinfo->chan, channel_id))
+ goto unlock;
+ if (spar_signal_insert(queueinfo->chan, whichqueue, signal)) {
+ queueinfo->packets_sent++;
+ rc = 1;
+ }
+ spar_channel_client_release_os(queueinfo->chan, channel_id);
+unlock:
+ spin_unlock_irqrestore((spinlock_t *)lock, flags);
+ return rc;
+}
+
+int
+uisqueue_put_cmdrsp_with_lock_client(struct uisqueue_info *queueinfo,
+ struct uiscmdrsp *cmdrsp,
+ unsigned int whichqueue,
+ void *insertlock,
+ unsigned char issue_irq_if_empty,
+ u64 irq_handle,
+ char oktowait, u8 *channel_id)
+{
+ while (!do_locked_client_insert(queueinfo, whichqueue, cmdrsp,
+ (spinlock_t *)insertlock,
+ channel_id)) {
+ if (oktowait != OK_TO_WAIT)
+ return 0; /* failed to queue */
+
+ /* try again */
+ set_current_state(TASK_INTERRUPTIBLE);
+ schedule_timeout(msecs_to_jiffies(10));
+ }
+ return 1;
+}
+EXPORT_SYMBOL_GPL(uisqueue_put_cmdrsp_with_lock_client);
+
+/* uisqueue_get_cmdrsp gets the cmdrsp entry at the head of the queue
+ * returns NULL if queue is empty */
+int
+uisqueue_get_cmdrsp(struct uisqueue_info *queueinfo,
+ void *cmdrsp, unsigned int whichqueue)
+{
+ if (!spar_signal_remove(queueinfo->chan, whichqueue, cmdrsp))
+ return 0;
+
+ queueinfo->packets_received++;
+
+ return 1; /* Success */
+}
+EXPORT_SYMBOL_GPL(uisqueue_get_cmdrsp);
diff --git a/drivers/staging/unisys/uislib/uisthread.c b/drivers/staging/unisys/uislib/uisthread.c
new file mode 100644
index 000000000..d3c973b61
--- /dev/null
+++ b/drivers/staging/unisys/uislib/uisthread.c
@@ -0,0 +1,69 @@
+/* uisthread.c
+ *
+ * Copyright (C) 2010 - 2013 UNISYS CORPORATION
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or (at
+ * your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
+ * NON INFRINGEMENT. See the GNU General Public License for more
+ * details.
+ */
+
+/* @ALL_INSPECTED */
+#include <asm/processor.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/kthread.h>
+#include "uisutils.h"
+#include "uisthread.h"
+
+/* this is shorter than using __FILE__ (full path name) in
+ * debug/info/error messages
+ */
+#define CURRENT_FILE_PC UISLIB_PC_uisthread_c
+#define __MYFILE__ "uisthread.c"
+
+/*****************************************************/
+/* Exported functions */
+/*****************************************************/
+
+/* returns 0 for failure, 1 for success */
+int
+uisthread_start(struct uisthread_info *thrinfo,
+ int (*threadfn)(void *), void *thrcontext, char *name)
+{
+ /* used to stop the thread */
+ init_completion(&thrinfo->has_stopped);
+ thrinfo->task = kthread_run(threadfn, thrcontext, name);
+ if (IS_ERR(thrinfo->task)) {
+ thrinfo->id = 0;
+ return 0; /* failure */
+ }
+ thrinfo->id = thrinfo->task->pid;
+ return 1;
+}
+EXPORT_SYMBOL_GPL(uisthread_start);
+
+void
+uisthread_stop(struct uisthread_info *thrinfo)
+{
+ int stopped = 0;
+
+ if (thrinfo->id == 0)
+ return; /* thread not running */
+
+ kthread_stop(thrinfo->task);
+ /* give up if the thread has NOT died in 1 minute */
+ if (wait_for_completion_timeout(&thrinfo->has_stopped, 60 * HZ))
+ stopped = 1;
+
+ if (stopped)
+ thrinfo->id = 0;
+}
+EXPORT_SYMBOL_GPL(uisthread_stop);
diff --git a/drivers/staging/unisys/uislib/uisutils.c b/drivers/staging/unisys/uislib/uisutils.c
new file mode 100644
index 000000000..26ab76526
--- /dev/null
+++ b/drivers/staging/unisys/uislib/uisutils.c
@@ -0,0 +1,137 @@
+/* uisutils.c
+ *
+ * Copyright (C) 2010 - 2013 UNISYS CORPORATION
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or (at
+ * your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
+ * NON INFRINGEMENT. See the GNU General Public License for more
+ * details.
+ */
+
+#include <linux/string.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <linux/uuid.h>
+#include <linux/spinlock.h>
+#include <linux/list.h>
+#include "uisutils.h"
+#include "version.h"
+#include "vbushelper.h"
+#include <linux/skbuff.h>
+#ifdef CONFIG_HIGHMEM
+#include <linux/highmem.h>
+#endif
+
+/* this is shorter than using __FILE__ (full path name) in
+ * debug/info/error messages
+ */
+#define CURRENT_FILE_PC UISLIB_PC_uisutils_c
+#define __MYFILE__ "uisutils.c"
+
+/* exports */
+atomic_t uisutils_registered_services = ATOMIC_INIT(0);
+ /* num registrations via
+ * uisctrl_register_req_handler() or
+ * uisctrl_register_req_handler_ex() */
+
+/*****************************************************/
+/* Utility functions */
+/*****************************************************/
+
+int
+uisutil_add_proc_line_ex(int *total, char **buffer, int *buffer_remaining,
+ char *format, ...)
+{
+ va_list args;
+ int len;
+
+ va_start(args, format);
+ len = vsnprintf(*buffer, *buffer_remaining, format, args);
+ va_end(args);
+ if (len >= *buffer_remaining) {
+ *buffer += *buffer_remaining;
+ *total += *buffer_remaining;
+ *buffer_remaining = 0;
+ return -1;
+ }
+ *buffer_remaining -= len;
+ *buffer += len;
+ *total += len;
+ return len;
+}
+EXPORT_SYMBOL_GPL(uisutil_add_proc_line_ex);
+
+int
+uisctrl_register_req_handler(int type, void *fptr,
+ struct ultra_vbus_deviceinfo *chipset_driver_info)
+{
+ switch (type) {
+ case 2:
+ if (fptr) {
+ if (!virt_control_chan_func)
+ atomic_inc(&uisutils_registered_services);
+ virt_control_chan_func = fptr;
+ } else {
+ if (virt_control_chan_func)
+ atomic_dec(&uisutils_registered_services);
+ virt_control_chan_func = NULL;
+ }
+ break;
+
+ default:
+ return 0;
+ }
+ if (chipset_driver_info)
+ bus_device_info_init(chipset_driver_info, "chipset", "uislib",
+ VERSION, NULL);
+
+ return 1;
+}
+EXPORT_SYMBOL_GPL(uisctrl_register_req_handler);
+
+/*
+ * unsigned int uisutil_copy_fragsinfo_from_skb(unsigned char *calling_ctx,
+ * void *skb_in,
+ * unsigned int firstfraglen,
+ * unsigned int frags_max,
+ * struct phys_info frags[])
+ *
+ * calling_ctx - input - a string that is displayed to show
+ * who called * this func
+ * void *skb_in - skb whose frag info we're copying type is hidden so we
+ * don't need to include skbbuff in uisutils.h which is
+ * included in non-networking code.
+ * unsigned int firstfraglen - input - length of first fragment in skb
+ * unsigned int frags_max - input - max len of frags array
+ * struct phys_info frags[] - output - frags array filled in on output
+ * return value indicates number of
+ * entries filled in frags
+ */
+
+static LIST_HEAD(req_handler_info_list); /* list of struct req_handler_info */
+static DEFINE_SPINLOCK(req_handler_info_list_lock);
+
+struct req_handler_info *
+req_handler_find(uuid_le switch_uuid)
+{
+ struct list_head *lelt, *tmp;
+ struct req_handler_info *entry = NULL;
+
+ spin_lock(&req_handler_info_list_lock);
+ list_for_each_safe(lelt, tmp, &req_handler_info_list) {
+ entry = list_entry(lelt, struct req_handler_info, list_link);
+ if (uuid_le_cmp(entry->switch_uuid, switch_uuid) == 0) {
+ spin_unlock(&req_handler_info_list_lock);
+ return entry;
+ }
+ }
+ spin_unlock(&req_handler_info_list_lock);
+ return NULL;
+}
diff --git a/drivers/staging/unisys/virthba/Kconfig b/drivers/staging/unisys/virthba/Kconfig
new file mode 100644
index 000000000..dfadfc491
--- /dev/null
+++ b/drivers/staging/unisys/virthba/Kconfig
@@ -0,0 +1,13 @@
+#
+# Unisys virthba configuration
+#
+
+config UNISYS_VIRTHBA
+ tristate "Unisys virthba driver"
+ depends on SCSI
+ select UNISYS_VISORCHIPSET
+ select UNISYS_UISLIB
+ select UNISYS_VIRTPCI
+ ---help---
+ If you say Y here, you will enable the Unisys virthba driver.
+
diff --git a/drivers/staging/unisys/virthba/Makefile b/drivers/staging/unisys/virthba/Makefile
new file mode 100644
index 000000000..a4e403739
--- /dev/null
+++ b/drivers/staging/unisys/virthba/Makefile
@@ -0,0 +1,12 @@
+#
+# Makefile for Unisys virthba
+#
+
+obj-$(CONFIG_UNISYS_VIRTHBA) += virthba.o
+
+ccflags-y += -Idrivers/staging/unisys/include
+ccflags-y += -Idrivers/staging/unisys/uislib
+ccflags-y += -Idrivers/staging/unisys/visorchipset
+ccflags-y += -Idrivers/staging/unisys/virtpci
+ccflags-y += -Idrivers/staging/unisys/common-spar/include
+ccflags-y += -Idrivers/staging/unisys/common-spar/include/channels
diff --git a/drivers/staging/unisys/virthba/virthba.c b/drivers/staging/unisys/virthba/virthba.c
new file mode 100644
index 000000000..d9001cca0
--- /dev/null
+++ b/drivers/staging/unisys/virthba/virthba.c
@@ -0,0 +1,1572 @@
+/* virthba.c
+ *
+ * Copyright (C) 2010 - 2013 UNISYS CORPORATION
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or (at
+ * your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
+ * NON INFRINGEMENT. See the GNU General Public License for more
+ * details.
+ */
+
+#define EXPORT_SYMTAB
+
+/* if you want to turn on some debugging of write device data or read
+ * device data, define these two undefs. You will probably want to
+ * customize the code which is here since it was written assuming
+ * reading and writing a specific data file df.64M.txt which is a
+ * 64Megabyte file created by Art Nilson using a scritp I wrote called
+ * cr_test_data.pl. The data file consists of 256 byte lines of text
+ * which start with an 8 digit sequence number, a colon, and then
+ * letters after that */
+
+#include <linux/kernel.h>
+#ifdef CONFIG_MODVERSIONS
+#include <config/modversions.h>
+#endif
+
+#include "diagnostics/appos_subsystems.h"
+#include "uisutils.h"
+#include "uisqueue.h"
+#include "uisthread.h"
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/pci.h>
+#include <linux/spinlock.h>
+#include <linux/device.h>
+#include <linux/slab.h>
+#include <scsi/scsi.h>
+#include <scsi/scsi_host.h>
+#include <scsi/scsi_cmnd.h>
+#include <scsi/scsi_device.h>
+#include <asm/param.h>
+#include <linux/debugfs.h>
+#include <linux/types.h>
+
+#include "virthba.h"
+#include "virtpci.h"
+#include "visorchipset.h"
+#include "version.h"
+#include "guestlinuxdebug.h"
+/* this is shorter than using __FILE__ (full path name) in
+ * debug/info/error messages
+ */
+#define CURRENT_FILE_PC VIRT_HBA_PC_virthba_c
+#define __MYFILE__ "virthba.c"
+
+/* NOTE: L1_CACHE_BYTES >=128 */
+#define DEVICE_ATTRIBUTE struct device_attribute
+
+ /* MAX_BUF = 6 lines x 10 MAXVHBA x 80 characters
+ * = 4800 bytes ~ 2^13 = 8192 bytes
+ */
+#define MAX_BUF 8192
+
+/*****************************************************/
+/* Forward declarations */
+/*****************************************************/
+static int virthba_probe(struct virtpci_dev *dev,
+ const struct pci_device_id *id);
+static void virthba_remove(struct virtpci_dev *dev);
+static int virthba_abort_handler(struct scsi_cmnd *scsicmd);
+static int virthba_bus_reset_handler(struct scsi_cmnd *scsicmd);
+static int virthba_device_reset_handler(struct scsi_cmnd *scsicmd);
+static int virthba_host_reset_handler(struct scsi_cmnd *scsicmd);
+static const char *virthba_get_info(struct Scsi_Host *shp);
+static int virthba_ioctl(struct scsi_device *dev, int cmd, void __user *arg);
+static int virthba_queue_command_lck(struct scsi_cmnd *scsicmd,
+ void (*virthba_cmnd_done)
+ (struct scsi_cmnd *));
+
+static const struct x86_cpu_id unisys_spar_ids[] = {
+ { X86_VENDOR_INTEL, 6, 62, X86_FEATURE_ANY },
+ {}
+};
+
+/* Autoload */
+MODULE_DEVICE_TABLE(x86cpu, unisys_spar_ids);
+
+#ifdef DEF_SCSI_QCMD
+static DEF_SCSI_QCMD(virthba_queue_command)
+#else
+#define virthba_queue_command virthba_queue_command_lck
+#endif
+
+static int virthba_slave_alloc(struct scsi_device *scsidev);
+static int virthba_slave_configure(struct scsi_device *scsidev);
+static void virthba_slave_destroy(struct scsi_device *scsidev);
+static int process_incoming_rsps(void *);
+static int virthba_serverup(struct virtpci_dev *virtpcidev);
+static int virthba_serverdown(struct virtpci_dev *virtpcidev, u32 state);
+static void do_disk_add_remove(struct work_struct *work);
+static void virthba_serverdown_complete(struct work_struct *work);
+static ssize_t info_debugfs_read(struct file *file, char __user *buf,
+ size_t len, loff_t *offset);
+static ssize_t enable_ints_write(struct file *file,
+ const char __user *buffer, size_t count,
+ loff_t *ppos);
+
+/*****************************************************/
+/* Globals */
+/*****************************************************/
+
+static int rsltq_wait_usecs = 4000; /* Default 4ms */
+static unsigned int max_buff_len;
+
+/* Module options */
+static char *virthba_options = "NONE";
+
+static const struct pci_device_id virthba_id_table[] = {
+ {PCI_DEVICE(PCI_VENDOR_ID_UNISYS, PCI_DEVICE_ID_VIRTHBA)},
+ {0},
+};
+
+/* export virthba_id_table */
+MODULE_DEVICE_TABLE(pci, virthba_id_table);
+
+static struct workqueue_struct *virthba_serverdown_workqueue;
+
+static struct virtpci_driver virthba_driver = {
+ .name = "uisvirthba",
+ .version = VERSION,
+ .vertag = NULL,
+ .id_table = virthba_id_table,
+ .probe = virthba_probe,
+ .remove = virthba_remove,
+ .resume = virthba_serverup,
+ .suspend = virthba_serverdown
+};
+
+/* The Send and Recive Buffers of the IO Queue may both be full */
+#define MAX_PENDING_REQUESTS (MIN_NUMSIGNALS*2)
+#define INTERRUPT_VECTOR_MASK 0x3F
+
+struct scsipending {
+ char cmdtype; /* Type of pointer that is being stored */
+ void *sent; /* The Data being tracked */
+ /* struct scsi_cmnd *type for virthba_queue_command */
+ /* struct uiscmdrsp *type for management commands */
+};
+
+#define VIRTHBA_ERROR_COUNT 30
+#define IOS_ERROR_THRESHOLD 1000
+struct virtdisk_info {
+ u32 valid;
+ u32 channel, id, lun; /* Disk Path */
+ atomic_t ios_threshold;
+ atomic_t error_count;
+ struct virtdisk_info *next;
+};
+
+/* Each Scsi_Host has a host_data area that contains this struct. */
+struct virthba_info {
+ struct Scsi_Host *scsihost;
+ struct virtpci_dev *virtpcidev;
+ struct list_head dev_info_list;
+ struct chaninfo chinfo;
+ struct irq_info intr; /* use recvInterrupt info to receive
+ interrupts when IOs complete */
+ int interrupt_vector;
+ struct scsipending pending[MAX_PENDING_REQUESTS]; /* Tracks the requests
+ that have been */
+ /* forwarded to the IOVM and haven't returned yet */
+ unsigned int nextinsert; /* Start search for next pending
+ free slot here */
+ spinlock_t privlock;
+ bool serverdown;
+ bool serverchangingstate;
+ unsigned long long acquire_failed_cnt;
+ unsigned long long interrupts_rcvd;
+ unsigned long long interrupts_notme;
+ unsigned long long interrupts_disabled;
+ struct work_struct serverdown_completion;
+ u64 __iomem *flags_addr;
+ atomic_t interrupt_rcvd;
+ wait_queue_head_t rsp_queue;
+ struct virtdisk_info head;
+};
+
+/* Work Data for dar_work_queue */
+struct diskaddremove {
+ u8 add; /* 0-remove, 1-add */
+ struct Scsi_Host *shost; /* Scsi Host for this virthba instance */
+ u32 channel, id, lun; /* Disk Path */
+ struct diskaddremove *next;
+};
+
+#define virtpci_dev_to_virthba_virthba_get_info(d) \
+ container_of(d, struct virthba_info, virtpcidev)
+
+static DEVICE_ATTRIBUTE *virthba_shost_attrs[];
+static struct scsi_host_template virthba_driver_template = {
+ .name = "Unisys Virtual HBA",
+ .info = virthba_get_info,
+ .ioctl = virthba_ioctl,
+ .queuecommand = virthba_queue_command,
+ .eh_abort_handler = virthba_abort_handler,
+ .eh_device_reset_handler = virthba_device_reset_handler,
+ .eh_bus_reset_handler = virthba_bus_reset_handler,
+ .eh_host_reset_handler = virthba_host_reset_handler,
+ .shost_attrs = virthba_shost_attrs,
+
+#define VIRTHBA_MAX_CMNDS 128
+ .can_queue = VIRTHBA_MAX_CMNDS,
+ .sg_tablesize = 64, /* largest number of address/length pairs */
+ .this_id = -1,
+ .slave_alloc = virthba_slave_alloc,
+ .slave_configure = virthba_slave_configure,
+ .slave_destroy = virthba_slave_destroy,
+ .use_clustering = ENABLE_CLUSTERING,
+};
+
+struct virthba_devices_open {
+ struct virthba_info *virthbainfo;
+};
+
+static const struct file_operations debugfs_info_fops = {
+ .read = info_debugfs_read,
+};
+
+static const struct file_operations debugfs_enable_ints_fops = {
+ .write = enable_ints_write,
+};
+
+/*****************************************************/
+/* Structs */
+/*****************************************************/
+
+#define VIRTHBASOPENMAX 1
+/* array of open devices maintained by open() and close(); */
+static struct virthba_devices_open virthbas_open[VIRTHBASOPENMAX];
+static struct dentry *virthba_debugfs_dir;
+
+/*****************************************************/
+/* Local Functions */
+/*****************************************************/
+static int
+add_scsipending_entry(struct virthba_info *vhbainfo, char cmdtype, void *new)
+{
+ unsigned long flags;
+ int insert_location;
+
+ spin_lock_irqsave(&vhbainfo->privlock, flags);
+ insert_location = vhbainfo->nextinsert;
+ while (vhbainfo->pending[insert_location].sent) {
+ insert_location = (insert_location + 1) % MAX_PENDING_REQUESTS;
+ if (insert_location == (int)vhbainfo->nextinsert) {
+ spin_unlock_irqrestore(&vhbainfo->privlock, flags);
+ return -1;
+ }
+ }
+
+ vhbainfo->pending[insert_location].cmdtype = cmdtype;
+ vhbainfo->pending[insert_location].sent = new;
+ vhbainfo->nextinsert = (insert_location + 1) % MAX_PENDING_REQUESTS;
+ spin_unlock_irqrestore(&vhbainfo->privlock, flags);
+
+ return insert_location;
+}
+
+static unsigned int
+add_scsipending_entry_with_wait(struct virthba_info *vhbainfo, char cmdtype,
+ void *new)
+{
+ int insert_location = add_scsipending_entry(vhbainfo, cmdtype, new);
+
+ while (insert_location == -1) {
+ set_current_state(TASK_INTERRUPTIBLE);
+ schedule_timeout(msecs_to_jiffies(10));
+ insert_location = add_scsipending_entry(vhbainfo, cmdtype, new);
+ }
+
+ return (unsigned int)insert_location;
+}
+
+static void *
+del_scsipending_entry(struct virthba_info *vhbainfo, uintptr_t del)
+{
+ unsigned long flags;
+ void *sent = NULL;
+
+ if (del < MAX_PENDING_REQUESTS) {
+ spin_lock_irqsave(&vhbainfo->privlock, flags);
+ sent = vhbainfo->pending[del].sent;
+
+ vhbainfo->pending[del].cmdtype = 0;
+ vhbainfo->pending[del].sent = NULL;
+ spin_unlock_irqrestore(&vhbainfo->privlock, flags);
+ }
+
+ return sent;
+}
+
+/* dar_work_queue (Disk Add/Remove) */
+static struct work_struct dar_work_queue;
+static struct diskaddremove *dar_work_queue_head;
+static spinlock_t dar_work_queue_lock;
+static unsigned short dar_work_queue_sched;
+#define QUEUE_DISKADDREMOVE(dar) { \
+ spin_lock_irqsave(&dar_work_queue_lock, flags); \
+ if (!dar_work_queue_head) { \
+ dar_work_queue_head = dar; \
+ dar->next = NULL; \
+ } \
+ else { \
+ dar->next = dar_work_queue_head; \
+ dar_work_queue_head = dar; \
+ } \
+ if (!dar_work_queue_sched) { \
+ schedule_work(&dar_work_queue); \
+ dar_work_queue_sched = 1; \
+ } \
+ spin_unlock_irqrestore(&dar_work_queue_lock, flags); \
+}
+
+static inline void
+send_disk_add_remove(struct diskaddremove *dar)
+{
+ struct scsi_device *sdev;
+ int error;
+
+ sdev = scsi_device_lookup(dar->shost, dar->channel, dar->id, dar->lun);
+ if (sdev) {
+ if (!(dar->add))
+ scsi_remove_device(sdev);
+ } else if (dar->add) {
+ error =
+ scsi_add_device(dar->shost, dar->channel, dar->id,
+ dar->lun);
+ }
+ kfree(dar);
+}
+
+/*****************************************************/
+/* dar_work_queue Handler Thread */
+/*****************************************************/
+static void
+do_disk_add_remove(struct work_struct *work)
+{
+ struct diskaddremove *dar;
+ struct diskaddremove *tmphead;
+ int i = 0;
+ unsigned long flags;
+
+ spin_lock_irqsave(&dar_work_queue_lock, flags);
+ tmphead = dar_work_queue_head;
+ dar_work_queue_head = NULL;
+ dar_work_queue_sched = 0;
+ spin_unlock_irqrestore(&dar_work_queue_lock, flags);
+ while (tmphead) {
+ dar = tmphead;
+ tmphead = dar->next;
+ send_disk_add_remove(dar);
+ i++;
+ }
+}
+
+/*****************************************************/
+/* Routine to add entry to dar_work_queue */
+/*****************************************************/
+static void
+process_disk_notify(struct Scsi_Host *shost, struct uiscmdrsp *cmdrsp)
+{
+ struct diskaddremove *dar;
+ unsigned long flags;
+
+ dar = kzalloc(sizeof(*dar), GFP_ATOMIC);
+ if (dar) {
+ dar->add = cmdrsp->disknotify.add;
+ dar->shost = shost;
+ dar->channel = cmdrsp->disknotify.channel;
+ dar->id = cmdrsp->disknotify.id;
+ dar->lun = cmdrsp->disknotify.lun;
+ QUEUE_DISKADDREMOVE(dar);
+ }
+}
+
+/*****************************************************/
+/* Probe Remove Functions */
+/*****************************************************/
+static irqreturn_t
+virthba_isr(int irq, void *dev_id)
+{
+ struct virthba_info *virthbainfo = (struct virthba_info *)dev_id;
+ struct channel_header __iomem *channel_header;
+ struct signal_queue_header __iomem *pqhdr;
+ u64 mask;
+ unsigned long long rc1;
+
+ if (!virthbainfo)
+ return IRQ_NONE;
+ virthbainfo->interrupts_rcvd++;
+ channel_header = virthbainfo->chinfo.queueinfo->chan;
+ if (((readq(&channel_header->features)
+ & ULTRA_IO_IOVM_IS_OK_WITH_DRIVER_DISABLING_INTS) != 0) &&
+ ((readq(&channel_header->features) &
+ ULTRA_IO_DRIVER_DISABLES_INTS) !=
+ 0)) {
+ virthbainfo->interrupts_disabled++;
+ mask = ~ULTRA_CHANNEL_ENABLE_INTS;
+ rc1 = uisqueue_interlocked_and(virthbainfo->flags_addr, mask);
+ }
+ if (spar_signalqueue_empty(channel_header, IOCHAN_FROM_IOPART)) {
+ virthbainfo->interrupts_notme++;
+ return IRQ_NONE;
+ }
+ pqhdr = (struct signal_queue_header __iomem *)
+ ((char __iomem *)channel_header +
+ readq(&channel_header->ch_space_offset)) + IOCHAN_FROM_IOPART;
+ writeq(readq(&pqhdr->num_irq_received) + 1,
+ &pqhdr->num_irq_received);
+ atomic_set(&virthbainfo->interrupt_rcvd, 1);
+ wake_up_interruptible(&virthbainfo->rsp_queue);
+ return IRQ_HANDLED;
+}
+
+static int
+virthba_probe(struct virtpci_dev *virtpcidev, const struct pci_device_id *id)
+{
+ int error;
+ struct Scsi_Host *scsihost;
+ struct virthba_info *virthbainfo;
+ int rsp;
+ int i;
+ irq_handler_t handler = virthba_isr;
+ struct channel_header __iomem *channel_header;
+ struct signal_queue_header __iomem *pqhdr;
+ u64 mask;
+
+ POSTCODE_LINUX_2(VHBA_PROBE_ENTRY_PC, POSTCODE_SEVERITY_INFO);
+ /* call scsi_host_alloc to register a scsi host adapter
+ * instance - this virthba that has just been created is an
+ * instance of a scsi host adapter. This scsi_host_alloc
+ * function allocates a new Scsi_Host struct & performs basic
+ * initialization. The host is not published to the scsi
+ * midlayer until scsi_add_host is called.
+ */
+
+ /* arg 2 passed in length of extra space we want allocated
+ * with scsi_host struct for our own use scsi_host_alloc
+ * assign host_no
+ */
+ scsihost = scsi_host_alloc(&virthba_driver_template,
+ sizeof(struct virthba_info));
+ if (!scsihost)
+ return -ENODEV;
+
+ scsihost->this_id = UIS_MAGIC_VHBA;
+ /* linux treats max-channel differently than max-id & max-lun.
+ * In the latter cases, those two values result in 0 to max-1
+ * (inclusive) being scanned. But in the case of channels, the
+ * scan is 0 to max (inclusive); so we will subtract one from
+ * the max-channel value.
+ */
+ scsihost->max_channel = (unsigned)virtpcidev->scsi.max.max_channel;
+ scsihost->max_id = (unsigned)virtpcidev->scsi.max.max_id;
+ scsihost->max_lun = (unsigned)virtpcidev->scsi.max.max_lun;
+ scsihost->cmd_per_lun = (unsigned)virtpcidev->scsi.max.cmd_per_lun;
+ scsihost->max_sectors =
+ (unsigned short)(virtpcidev->scsi.max.max_io_size >> 9);
+ scsihost->sg_tablesize =
+ (unsigned short)(virtpcidev->scsi.max.max_io_size / PAGE_SIZE);
+ if (scsihost->sg_tablesize > MAX_PHYS_INFO)
+ scsihost->sg_tablesize = MAX_PHYS_INFO;
+
+ /* this creates "host%d" in sysfs. If 2nd argument is NULL,
+ * then this generic /sys/devices/platform/host? device is
+ * created and /sys/scsi_host/host? ->
+ * /sys/devices/platform/host? If 2nd argument is not NULL,
+ * then this generic /sys/devices/<path>/host? is created and
+ * host? points to that device instead.
+ */
+ error = scsi_add_host(scsihost, &virtpcidev->generic_dev);
+ if (error) {
+ POSTCODE_LINUX_2(VHBA_PROBE_FAILURE_PC, POSTCODE_SEVERITY_ERR);
+ /* decr refcount on scsihost which was incremented by
+ * scsi_add_host so the scsi_host gets deleted
+ */
+ scsi_host_put(scsihost);
+ return -ENODEV;
+ }
+
+ virthbainfo = (struct virthba_info *)scsihost->hostdata;
+ memset(virthbainfo, 0, sizeof(struct virthba_info));
+ for (i = 0; i < VIRTHBASOPENMAX; i++) {
+ if (!virthbas_open[i].virthbainfo) {
+ virthbas_open[i].virthbainfo = virthbainfo;
+ break;
+ }
+ }
+ virthbainfo->interrupt_vector = -1;
+ virthbainfo->chinfo.queueinfo = &virtpcidev->queueinfo;
+ virthbainfo->virtpcidev = virtpcidev;
+ spin_lock_init(&virthbainfo->chinfo.insertlock);
+
+ init_waitqueue_head(&virthbainfo->rsp_queue);
+ spin_lock_init(&virthbainfo->privlock);
+ memset(&virthbainfo->pending, 0, sizeof(virthbainfo->pending));
+ virthbainfo->serverdown = false;
+ virthbainfo->serverchangingstate = false;
+
+ virthbainfo->intr = virtpcidev->intr;
+ /* save of host within virthba_info */
+ virthbainfo->scsihost = scsihost;
+
+ /* save of host within virtpci_dev */
+ virtpcidev->scsi.scsihost = scsihost;
+
+ /* Setup workqueue for serverdown messages */
+ INIT_WORK(&virthbainfo->serverdown_completion,
+ virthba_serverdown_complete);
+
+ writeq(readq(&virthbainfo->chinfo.queueinfo->chan->features) |
+ ULTRA_IO_CHANNEL_IS_POLLING,
+ &virthbainfo->chinfo.queueinfo->chan->features);
+ /* start thread that will receive scsicmnd responses */
+
+ channel_header = virthbainfo->chinfo.queueinfo->chan;
+ pqhdr = (struct signal_queue_header __iomem *)
+ ((char __iomem *)channel_header +
+ readq(&channel_header->ch_space_offset)) + IOCHAN_FROM_IOPART;
+ virthbainfo->flags_addr = &pqhdr->features;
+
+ if (!uisthread_start(&virthbainfo->chinfo.threadinfo,
+ process_incoming_rsps,
+ virthbainfo, "vhba_incoming")) {
+ /* decr refcount on scsihost which was incremented by
+ * scsi_add_host so the scsi_host gets deleted
+ */
+ POSTCODE_LINUX_2(VHBA_PROBE_FAILURE_PC, POSTCODE_SEVERITY_ERR);
+ scsi_host_put(scsihost);
+ return -ENODEV;
+ }
+ virthbainfo->interrupt_vector =
+ virthbainfo->intr.recv_irq_handle & INTERRUPT_VECTOR_MASK;
+ rsp = request_irq(virthbainfo->interrupt_vector, handler, IRQF_SHARED,
+ scsihost->hostt->name, virthbainfo);
+ if (rsp != 0) {
+ virthbainfo->interrupt_vector = -1;
+ POSTCODE_LINUX_2(VHBA_PROBE_FAILURE_PC, POSTCODE_SEVERITY_ERR);
+ } else {
+ u64 __iomem *features_addr =
+ &virthbainfo->chinfo.queueinfo->chan->features;
+ mask = ~(ULTRA_IO_CHANNEL_IS_POLLING |
+ ULTRA_IO_DRIVER_DISABLES_INTS);
+ uisqueue_interlocked_and(features_addr, mask);
+ mask = ULTRA_IO_DRIVER_ENABLES_INTS;
+ uisqueue_interlocked_or(features_addr, mask);
+ rsltq_wait_usecs = 4000000;
+ }
+
+ scsi_scan_host(scsihost);
+
+ POSTCODE_LINUX_2(VHBA_PROBE_EXIT_PC, POSTCODE_SEVERITY_INFO);
+ return 0;
+}
+
+static void
+virthba_remove(struct virtpci_dev *virtpcidev)
+{
+ struct virthba_info *virthbainfo;
+ struct Scsi_Host *scsihost =
+ (struct Scsi_Host *)virtpcidev->scsi.scsihost;
+
+ virthbainfo = (struct virthba_info *)scsihost->hostdata;
+ if (virthbainfo->interrupt_vector != -1)
+ free_irq(virthbainfo->interrupt_vector, virthbainfo);
+
+ scsi_remove_host(scsihost);
+
+ uisthread_stop(&virthbainfo->chinfo.threadinfo);
+
+ /* decr refcount on scsihost which was incremented by
+ * scsi_add_host so the scsi_host gets deleted
+ */
+ scsi_host_put(scsihost);
+}
+
+static int
+forward_vdiskmgmt_command(enum vdisk_mgmt_types vdiskcmdtype,
+ struct Scsi_Host *scsihost,
+ struct uisscsi_dest *vdest)
+{
+ struct uiscmdrsp *cmdrsp;
+ struct virthba_info *virthbainfo =
+ (struct virthba_info *)scsihost->hostdata;
+ int notifyresult = 0xffff;
+ wait_queue_head_t notifyevent;
+
+ if (virthbainfo->serverdown || virthbainfo->serverchangingstate)
+ return FAILED;
+
+ cmdrsp = kzalloc(SIZEOF_CMDRSP, GFP_ATOMIC);
+ if (!cmdrsp)
+ return FAILED; /* reject */
+
+ init_waitqueue_head(&notifyevent);
+
+ /* issue VDISK_MGMT_CMD
+ * set type to command - as opposed to task mgmt
+ */
+ cmdrsp->cmdtype = CMD_VDISKMGMT_TYPE;
+ /* specify the event that has to be triggered when this cmd is
+ * complete
+ */
+ cmdrsp->vdiskmgmt.notify = (void *)&notifyevent;
+ cmdrsp->vdiskmgmt.notifyresult = (void *)&notifyresult;
+
+ /* save destination */
+ cmdrsp->vdiskmgmt.vdisktype = vdiskcmdtype;
+ cmdrsp->vdiskmgmt.vdest.channel = vdest->channel;
+ cmdrsp->vdiskmgmt.vdest.id = vdest->id;
+ cmdrsp->vdiskmgmt.vdest.lun = vdest->lun;
+ cmdrsp->vdiskmgmt.scsicmd =
+ (void *)(uintptr_t)
+ add_scsipending_entry_with_wait(virthbainfo, CMD_VDISKMGMT_TYPE,
+ (void *)cmdrsp);
+
+ uisqueue_put_cmdrsp_with_lock_client(virthbainfo->chinfo.queueinfo,
+ cmdrsp, IOCHAN_TO_IOPART,
+ &virthbainfo->chinfo.insertlock,
+ DONT_ISSUE_INTERRUPT, (u64)NULL,
+ OK_TO_WAIT, "vhba");
+ wait_event(notifyevent, notifyresult != 0xffff);
+ kfree(cmdrsp);
+ return SUCCESS;
+}
+
+/*****************************************************/
+/* Scsi Host support functions */
+/*****************************************************/
+
+static int
+forward_taskmgmt_command(enum task_mgmt_types tasktype,
+ struct scsi_device *scsidev)
+{
+ struct uiscmdrsp *cmdrsp;
+ struct virthba_info *virthbainfo =
+ (struct virthba_info *)scsidev->host->hostdata;
+ int notifyresult = 0xffff;
+ wait_queue_head_t notifyevent;
+
+ if (virthbainfo->serverdown || virthbainfo->serverchangingstate)
+ return FAILED;
+
+ cmdrsp = kzalloc(SIZEOF_CMDRSP, GFP_ATOMIC);
+ if (!cmdrsp)
+ return FAILED; /* reject */
+
+ init_waitqueue_head(&notifyevent);
+
+ /* issue TASK_MGMT_ABORT_TASK */
+ /* set type to command - as opposed to task mgmt */
+ cmdrsp->cmdtype = CMD_SCSITASKMGMT_TYPE;
+ /* specify the event that has to be triggered when this */
+ /* cmd is complete */
+ cmdrsp->scsitaskmgmt.notify = (void *)&notifyevent;
+ cmdrsp->scsitaskmgmt.notifyresult = (void *)&notifyresult;
+
+ /* save destination */
+ cmdrsp->scsitaskmgmt.tasktype = tasktype;
+ cmdrsp->scsitaskmgmt.vdest.channel = scsidev->channel;
+ cmdrsp->scsitaskmgmt.vdest.id = scsidev->id;
+ cmdrsp->scsitaskmgmt.vdest.lun = scsidev->lun;
+ cmdrsp->scsitaskmgmt.scsicmd =
+ (void *)(uintptr_t)
+ add_scsipending_entry_with_wait(virthbainfo,
+ CMD_SCSITASKMGMT_TYPE,
+ (void *)cmdrsp);
+
+ uisqueue_put_cmdrsp_with_lock_client(virthbainfo->chinfo.queueinfo,
+ cmdrsp, IOCHAN_TO_IOPART,
+ &virthbainfo->chinfo.insertlock,
+ DONT_ISSUE_INTERRUPT, (u64)NULL,
+ OK_TO_WAIT, "vhba");
+ wait_event(notifyevent, notifyresult != 0xffff);
+ kfree(cmdrsp);
+ return SUCCESS;
+}
+
+/* The abort handler returns SUCCESS if it has succeeded to make LLDD
+ * and all related hardware forget about the scmd.
+ */
+static int
+virthba_abort_handler(struct scsi_cmnd *scsicmd)
+{
+ /* issue TASK_MGMT_ABORT_TASK */
+ struct scsi_device *scsidev;
+ struct virtdisk_info *vdisk;
+
+ scsidev = scsicmd->device;
+ for (vdisk = &((struct virthba_info *)scsidev->host->hostdata)->head;
+ vdisk->next; vdisk = vdisk->next) {
+ if ((scsidev->channel == vdisk->channel) &&
+ (scsidev->id == vdisk->id) &&
+ (scsidev->lun == vdisk->lun)) {
+ if (atomic_read(&vdisk->error_count) <
+ VIRTHBA_ERROR_COUNT) {
+ atomic_inc(&vdisk->error_count);
+ POSTCODE_LINUX_2(VHBA_COMMAND_HANDLER_PC,
+ POSTCODE_SEVERITY_INFO);
+ } else
+ atomic_set(&vdisk->ios_threshold,
+ IOS_ERROR_THRESHOLD);
+ }
+ }
+ return forward_taskmgmt_command(TASK_MGMT_ABORT_TASK, scsicmd->device);
+}
+
+static int
+virthba_bus_reset_handler(struct scsi_cmnd *scsicmd)
+{
+ /* issue TASK_MGMT_TARGET_RESET for each target on the bus */
+ struct scsi_device *scsidev;
+ struct virtdisk_info *vdisk;
+
+ scsidev = scsicmd->device;
+ for (vdisk = &((struct virthba_info *)scsidev->host->hostdata)->head;
+ vdisk->next; vdisk = vdisk->next) {
+ if ((scsidev->channel == vdisk->channel) &&
+ (scsidev->id == vdisk->id) &&
+ (scsidev->lun == vdisk->lun)) {
+ if (atomic_read(&vdisk->error_count) <
+ VIRTHBA_ERROR_COUNT) {
+ atomic_inc(&vdisk->error_count);
+ POSTCODE_LINUX_2(VHBA_COMMAND_HANDLER_PC,
+ POSTCODE_SEVERITY_INFO);
+ } else
+ atomic_set(&vdisk->ios_threshold,
+ IOS_ERROR_THRESHOLD);
+ }
+ }
+ return forward_taskmgmt_command(TASK_MGMT_BUS_RESET, scsicmd->device);
+}
+
+static int
+virthba_device_reset_handler(struct scsi_cmnd *scsicmd)
+{
+ /* issue TASK_MGMT_LUN_RESET */
+ struct scsi_device *scsidev;
+ struct virtdisk_info *vdisk;
+
+ scsidev = scsicmd->device;
+ for (vdisk = &((struct virthba_info *)scsidev->host->hostdata)->head;
+ vdisk->next; vdisk = vdisk->next) {
+ if ((scsidev->channel == vdisk->channel) &&
+ (scsidev->id == vdisk->id) &&
+ (scsidev->lun == vdisk->lun)) {
+ if (atomic_read(&vdisk->error_count) <
+ VIRTHBA_ERROR_COUNT) {
+ atomic_inc(&vdisk->error_count);
+ POSTCODE_LINUX_2(VHBA_COMMAND_HANDLER_PC,
+ POSTCODE_SEVERITY_INFO);
+ } else
+ atomic_set(&vdisk->ios_threshold,
+ IOS_ERROR_THRESHOLD);
+ }
+ }
+ return forward_taskmgmt_command(TASK_MGMT_LUN_RESET, scsicmd->device);
+}
+
+static int
+virthba_host_reset_handler(struct scsi_cmnd *scsicmd)
+{
+ /* issue TASK_MGMT_TARGET_RESET for each target on each bus for host */
+ return SUCCESS;
+}
+
+static char virthba_get_info_str[256];
+
+static const char *
+virthba_get_info(struct Scsi_Host *shp)
+{
+ /* Return version string */
+ sprintf(virthba_get_info_str, "virthba, version %s\n", VIRTHBA_VERSION);
+ return virthba_get_info_str;
+}
+
+static int
+virthba_ioctl(struct scsi_device *dev, int cmd, void __user *arg)
+{
+ return -EINVAL;
+}
+
+/* This returns SCSI_MLQUEUE_DEVICE_BUSY if the signal queue to IOpart
+ * is full.
+ */
+static int
+virthba_queue_command_lck(struct scsi_cmnd *scsicmd,
+ void (*virthba_cmnd_done)(struct scsi_cmnd *))
+{
+ struct scsi_device *scsidev = scsicmd->device;
+ int insert_location;
+ unsigned char op;
+ unsigned char *cdb = scsicmd->cmnd;
+ struct Scsi_Host *scsihost = scsidev->host;
+ struct uiscmdrsp *cmdrsp;
+ unsigned int i;
+ struct virthba_info *virthbainfo =
+ (struct virthba_info *)scsihost->hostdata;
+ struct scatterlist *sg = NULL;
+ struct scatterlist *sgl = NULL;
+ int sg_failed = 0;
+
+ if (virthbainfo->serverdown || virthbainfo->serverchangingstate)
+ return SCSI_MLQUEUE_DEVICE_BUSY;
+ cmdrsp = kzalloc(SIZEOF_CMDRSP, GFP_ATOMIC);
+ if (!cmdrsp)
+ return 1; /* reject the command */
+
+ /* now saving everything we need from scsi_cmd into cmdrsp
+ * before we queue cmdrsp set type to command - as opposed to
+ * task mgmt
+ */
+ cmdrsp->cmdtype = CMD_SCSI_TYPE;
+ /* save the pending insertion location. Deletion from pending
+ * will return the scsicmd pointer for completion
+ */
+ insert_location =
+ add_scsipending_entry(virthbainfo, CMD_SCSI_TYPE, (void *)scsicmd);
+ if (insert_location != -1) {
+ cmdrsp->scsi.scsicmd = (void *)(uintptr_t)insert_location;
+ } else {
+ kfree(cmdrsp);
+ return SCSI_MLQUEUE_DEVICE_BUSY;
+ }
+ /* save done function that we have call when cmd is complete */
+ scsicmd->scsi_done = virthba_cmnd_done;
+ /* save destination */
+ cmdrsp->scsi.vdest.channel = scsidev->channel;
+ cmdrsp->scsi.vdest.id = scsidev->id;
+ cmdrsp->scsi.vdest.lun = scsidev->lun;
+ /* save datadir */
+ cmdrsp->scsi.data_dir = scsicmd->sc_data_direction;
+ memcpy(cmdrsp->scsi.cmnd, cdb, MAX_CMND_SIZE);
+
+ cmdrsp->scsi.bufflen = scsi_bufflen(scsicmd);
+
+ /* keep track of the max buffer length so far. */
+ if (cmdrsp->scsi.bufflen > max_buff_len)
+ max_buff_len = cmdrsp->scsi.bufflen;
+
+ if (scsi_sg_count(scsicmd) > MAX_PHYS_INFO) {
+ del_scsipending_entry(virthbainfo, (uintptr_t)insert_location);
+ kfree(cmdrsp);
+ return 1; /* reject the command */
+ }
+
+ /* This is what we USED to do when we assumed we were running */
+ /* uissd & virthba on the same Linux system. */
+ /* cmdrsp->scsi.buffer = scsicmd->request_buffer; */
+ /* The following code does NOT make that assumption. */
+ /* convert buffer to phys information */
+ if (scsi_sg_count(scsicmd) == 0) {
+ if (scsi_bufflen(scsicmd) > 0) {
+ BUG_ON(scsi_sg_count(scsicmd) == 0);
+ }
+ } else {
+ /* buffer is scatterlist - copy it out */
+ sgl = scsi_sglist(scsicmd);
+
+ for_each_sg(sgl, sg, scsi_sg_count(scsicmd), i) {
+ cmdrsp->scsi.gpi_list[i].address = sg_phys(sg);
+ cmdrsp->scsi.gpi_list[i].length = sg->length;
+ }
+
+ if (sg_failed) {
+ /* BUG(); ***** For now, let it fail in uissd
+ * if it is a problem, as it might just
+ * work
+ */
+ }
+
+ cmdrsp->scsi.guest_phys_entries = scsi_sg_count(scsicmd);
+ }
+
+ op = cdb[0];
+ i = uisqueue_put_cmdrsp_with_lock_client(virthbainfo->chinfo.queueinfo,
+ cmdrsp, IOCHAN_TO_IOPART,
+ &virthbainfo->chinfo.
+ insertlock,
+ DONT_ISSUE_INTERRUPT,
+ (u64)NULL, DONT_WAIT, "vhba");
+ if (i == 0) {
+ /* queue must be full - and we said don't wait - return busy */
+ kfree(cmdrsp);
+ del_scsipending_entry(virthbainfo, (uintptr_t)insert_location);
+ return SCSI_MLQUEUE_DEVICE_BUSY;
+ }
+
+ /* we're done with cmdrsp space - data from it has been copied
+ * into channel - free it now.
+ */
+ kfree(cmdrsp);
+ return 0; /* non-zero implies host/device is busy */
+}
+
+static int
+virthba_slave_alloc(struct scsi_device *scsidev)
+{
+ /* this called by the midlayer before scan for new devices -
+ * LLD can alloc any struct & do init if needed.
+ */
+ struct virtdisk_info *vdisk;
+ struct virtdisk_info *tmpvdisk;
+ struct virthba_info *virthbainfo;
+ struct Scsi_Host *scsihost = (struct Scsi_Host *)scsidev->host;
+
+ virthbainfo = (struct virthba_info *)scsihost->hostdata;
+ if (!virthbainfo)
+ return 0; /* even though we errored, treat as success */
+
+ for (vdisk = &virthbainfo->head; vdisk->next; vdisk = vdisk->next) {
+ if (vdisk->next->valid &&
+ (vdisk->next->channel == scsidev->channel) &&
+ (vdisk->next->id == scsidev->id) &&
+ (vdisk->next->lun == scsidev->lun))
+ return 0;
+ }
+ tmpvdisk = kzalloc(sizeof(*tmpvdisk), GFP_ATOMIC);
+ if (!tmpvdisk)
+ return 0;
+
+ tmpvdisk->channel = scsidev->channel;
+ tmpvdisk->id = scsidev->id;
+ tmpvdisk->lun = scsidev->lun;
+ tmpvdisk->valid = 1;
+ vdisk->next = tmpvdisk;
+ return 0; /* success */
+}
+
+static int
+virthba_slave_configure(struct scsi_device *scsidev)
+{
+ return 0; /* success */
+}
+
+static void
+virthba_slave_destroy(struct scsi_device *scsidev)
+{
+ /* midlevel calls this after device has been quiesced and
+ * before it is to be deleted.
+ */
+ struct virtdisk_info *vdisk, *delvdisk;
+ struct virthba_info *virthbainfo;
+ struct Scsi_Host *scsihost = (struct Scsi_Host *)scsidev->host;
+
+ virthbainfo = (struct virthba_info *)scsihost->hostdata;
+ for (vdisk = &virthbainfo->head; vdisk->next; vdisk = vdisk->next) {
+ if (vdisk->next->valid &&
+ (vdisk->next->channel == scsidev->channel) &&
+ (vdisk->next->id == scsidev->id) &&
+ (vdisk->next->lun == scsidev->lun)) {
+ delvdisk = vdisk->next;
+ vdisk->next = vdisk->next->next;
+ kfree(delvdisk);
+ return;
+ }
+ }
+}
+
+/*****************************************************/
+/* Scsi Cmnd support thread */
+/*****************************************************/
+
+static void
+do_scsi_linuxstat(struct uiscmdrsp *cmdrsp, struct scsi_cmnd *scsicmd)
+{
+ struct virtdisk_info *vdisk;
+ struct scsi_device *scsidev;
+ struct sense_data *sd;
+
+ scsidev = scsicmd->device;
+ memcpy(scsicmd->sense_buffer, cmdrsp->scsi.sensebuf, MAX_SENSE_SIZE);
+ sd = (struct sense_data *)scsicmd->sense_buffer;
+
+ /* Do not log errors for disk-not-present inquiries */
+ if ((cmdrsp->scsi.cmnd[0] == INQUIRY) &&
+ (host_byte(cmdrsp->scsi.linuxstat) == DID_NO_CONNECT) &&
+ (cmdrsp->scsi.addlstat == ADDL_SEL_TIMEOUT))
+ return;
+
+ /* Okay see what our error_count is here.... */
+ for (vdisk = &((struct virthba_info *)scsidev->host->hostdata)->head;
+ vdisk->next; vdisk = vdisk->next) {
+ if ((scsidev->channel != vdisk->channel) ||
+ (scsidev->id != vdisk->id) ||
+ (scsidev->lun != vdisk->lun))
+ continue;
+
+ if (atomic_read(&vdisk->error_count) < VIRTHBA_ERROR_COUNT) {
+ atomic_inc(&vdisk->error_count);
+ atomic_set(&vdisk->ios_threshold, IOS_ERROR_THRESHOLD);
+ }
+ }
+}
+
+static void
+do_scsi_nolinuxstat(struct uiscmdrsp *cmdrsp, struct scsi_cmnd *scsicmd)
+{
+ struct scsi_device *scsidev;
+ unsigned char buf[36];
+ struct scatterlist *sg;
+ unsigned int i;
+ char *thispage;
+ char *thispage_orig;
+ int bufind = 0;
+ struct virtdisk_info *vdisk;
+
+ scsidev = scsicmd->device;
+ if ((cmdrsp->scsi.cmnd[0] == INQUIRY) &&
+ (cmdrsp->scsi.bufflen >= MIN_INQUIRY_RESULT_LEN)) {
+ if (cmdrsp->scsi.no_disk_result == 0)
+ return;
+
+ /* Linux scsi code is weird; it wants
+ * a device at Lun 0 to issue report
+ * luns, but we don't want a disk
+ * there so we'll present a processor
+ * there. */
+ SET_NO_DISK_INQUIRY_RESULT(buf, cmdrsp->scsi.bufflen,
+ scsidev->lun,
+ DEV_DISK_CAPABLE_NOT_PRESENT,
+ DEV_NOT_CAPABLE);
+
+ if (scsi_sg_count(scsicmd) == 0) {
+ if (scsi_bufflen(scsicmd) > 0) {
+ BUG_ON(scsi_sg_count(scsicmd) ==
+ 0);
+ }
+ memcpy(scsi_sglist(scsicmd), buf,
+ cmdrsp->scsi.bufflen);
+ return;
+ }
+
+ sg = scsi_sglist(scsicmd);
+ for (i = 0; i < scsi_sg_count(scsicmd); i++) {
+ thispage_orig = kmap_atomic(sg_page(sg + i));
+ thispage = (void *)((unsigned long)thispage_orig |
+ sg[i].offset);
+ memcpy(thispage, buf + bufind, sg[i].length);
+ kunmap_atomic(thispage_orig);
+ bufind += sg[i].length;
+ }
+ } else {
+ vdisk = &((struct virthba_info *)scsidev->host->hostdata)->head;
+ for ( ; vdisk->next; vdisk = vdisk->next) {
+ if ((scsidev->channel != vdisk->channel) ||
+ (scsidev->id != vdisk->id) ||
+ (scsidev->lun != vdisk->lun))
+ continue;
+
+ if (atomic_read(&vdisk->ios_threshold) > 0) {
+ atomic_dec(&vdisk->ios_threshold);
+ if (atomic_read(&vdisk->ios_threshold) == 0) {
+ atomic_set(&vdisk->error_count, 0);
+ }
+ }
+ }
+ }
+}
+
+static void
+complete_scsi_command(struct uiscmdrsp *cmdrsp, struct scsi_cmnd *scsicmd)
+{
+ /* take what we need out of cmdrsp and complete the scsicmd */
+ scsicmd->result = cmdrsp->scsi.linuxstat;
+ if (cmdrsp->scsi.linuxstat)
+ do_scsi_linuxstat(cmdrsp, scsicmd);
+ else
+ do_scsi_nolinuxstat(cmdrsp, scsicmd);
+
+ if (scsicmd->scsi_done)
+ scsicmd->scsi_done(scsicmd);
+}
+
+static inline void
+complete_vdiskmgmt_command(struct uiscmdrsp *cmdrsp)
+{
+ /* copy the result of the taskmgmt and */
+ /* wake up the error handler that is waiting for this */
+ *(int *)cmdrsp->vdiskmgmt.notifyresult = cmdrsp->vdiskmgmt.result;
+ wake_up_all((wait_queue_head_t *)cmdrsp->vdiskmgmt.notify);
+}
+
+static inline void
+complete_taskmgmt_command(struct uiscmdrsp *cmdrsp)
+{
+ /* copy the result of the taskmgmt and */
+ /* wake up the error handler that is waiting for this */
+ *(int *)cmdrsp->scsitaskmgmt.notifyresult =
+ cmdrsp->scsitaskmgmt.result;
+ wake_up_all((wait_queue_head_t *)cmdrsp->scsitaskmgmt.notify);
+}
+
+static void
+drain_queue(struct virthba_info *virthbainfo, struct chaninfo *dc,
+ struct uiscmdrsp *cmdrsp)
+{
+ unsigned long flags;
+ int qrslt = 0;
+ struct scsi_cmnd *scsicmd;
+ struct Scsi_Host *shost = virthbainfo->scsihost;
+
+ while (1) {
+ spin_lock_irqsave(&virthbainfo->chinfo.insertlock, flags);
+ if (!spar_channel_client_acquire_os(dc->queueinfo->chan,
+ "vhba")) {
+ spin_unlock_irqrestore(&virthbainfo->chinfo.insertlock,
+ flags);
+ virthbainfo->acquire_failed_cnt++;
+ break;
+ }
+ qrslt = uisqueue_get_cmdrsp(dc->queueinfo, cmdrsp,
+ IOCHAN_FROM_IOPART);
+ spar_channel_client_release_os(dc->queueinfo->chan, "vhba");
+ spin_unlock_irqrestore(&virthbainfo->chinfo.insertlock, flags);
+ if (qrslt == 0)
+ break;
+ if (cmdrsp->cmdtype == CMD_SCSI_TYPE) {
+ /* scsicmd location is returned by the
+ * deletion
+ */
+ scsicmd = del_scsipending_entry(virthbainfo,
+ (uintptr_t)
+ cmdrsp->scsi.scsicmd);
+ if (!scsicmd)
+ break;
+ /* complete the orig cmd */
+ complete_scsi_command(cmdrsp, scsicmd);
+ } else if (cmdrsp->cmdtype == CMD_SCSITASKMGMT_TYPE) {
+ if (!del_scsipending_entry(virthbainfo,
+ (uintptr_t)cmdrsp->scsitaskmgmt.scsicmd))
+ break;
+ complete_taskmgmt_command(cmdrsp);
+ } else if (cmdrsp->cmdtype == CMD_NOTIFYGUEST_TYPE) {
+ /* The vHba pointer has no meaning in
+ * a Client/Guest Partition. Let's be
+ * safe and set it to NULL now. Do
+ * not use it here! */
+ cmdrsp->disknotify.v_hba = NULL;
+ process_disk_notify(shost, cmdrsp);
+ } else if (cmdrsp->cmdtype == CMD_VDISKMGMT_TYPE) {
+ if (!del_scsipending_entry(virthbainfo,
+ (uintptr_t)
+ cmdrsp->vdiskmgmt.scsicmd))
+ break;
+ complete_vdiskmgmt_command(cmdrsp);
+ }
+ /* cmdrsp is now available for reuse */
+ }
+}
+
+/* main function for the thread that waits for scsi commands to arrive
+ * in a specified queue
+ */
+static int
+process_incoming_rsps(void *v)
+{
+ struct virthba_info *virthbainfo = v;
+ struct chaninfo *dc = &virthbainfo->chinfo;
+ struct uiscmdrsp *cmdrsp = NULL;
+ const int SZ = sizeof(struct uiscmdrsp);
+ u64 mask;
+ unsigned long long rc1;
+
+ UIS_DAEMONIZE("vhba_incoming");
+ /* alloc once and reuse */
+ cmdrsp = kmalloc(SZ, GFP_ATOMIC);
+ if (!cmdrsp) {
+ complete_and_exit(&dc->threadinfo.has_stopped, 0);
+ return 0;
+ }
+ mask = ULTRA_CHANNEL_ENABLE_INTS;
+ while (1) {
+ if (kthread_should_stop())
+ break;
+ wait_event_interruptible_timeout(virthbainfo->rsp_queue,
+ (atomic_read(&virthbainfo->interrupt_rcvd) == 1),
+ usecs_to_jiffies(rsltq_wait_usecs));
+ atomic_set(&virthbainfo->interrupt_rcvd, 0);
+ /* drain queue */
+ drain_queue(virthbainfo, dc, cmdrsp);
+ rc1 = uisqueue_interlocked_or(virthbainfo->flags_addr, mask);
+ }
+
+ kfree(cmdrsp);
+
+ complete_and_exit(&dc->threadinfo.has_stopped, 0);
+}
+
+/*****************************************************/
+/* Debugfs filesystem functions */
+/*****************************************************/
+
+static ssize_t info_debugfs_read(struct file *file,
+ char __user *buf, size_t len, loff_t *offset)
+{
+ ssize_t bytes_read = 0;
+ int str_pos = 0;
+ u64 phys_flags_addr;
+ int i;
+ struct virthba_info *virthbainfo;
+ char *vbuf;
+
+ if (len > MAX_BUF)
+ len = MAX_BUF;
+ vbuf = kzalloc(len, GFP_KERNEL);
+ if (!vbuf)
+ return -ENOMEM;
+
+ for (i = 0; i < VIRTHBASOPENMAX; i++) {
+ if (!virthbas_open[i].virthbainfo)
+ continue;
+
+ virthbainfo = virthbas_open[i].virthbainfo;
+
+ str_pos += scnprintf(vbuf + str_pos,
+ len - str_pos, "max_buff_len:%u\n",
+ max_buff_len);
+
+ str_pos += scnprintf(vbuf + str_pos, len - str_pos,
+ "\nvirthba result queue poll wait:%d usecs.\n",
+ rsltq_wait_usecs);
+ str_pos += scnprintf(vbuf + str_pos, len - str_pos,
+ "\ninterrupts_rcvd = %llu, interrupts_disabled = %llu\n",
+ virthbainfo->interrupts_rcvd,
+ virthbainfo->interrupts_disabled);
+ str_pos += scnprintf(vbuf + str_pos,
+ len - str_pos, "\ninterrupts_notme = %llu,\n",
+ virthbainfo->interrupts_notme);
+ phys_flags_addr = virt_to_phys((__force void *)
+ virthbainfo->flags_addr);
+ str_pos += scnprintf(vbuf + str_pos, len - str_pos,
+ "flags_addr = %p, phys_flags_addr=0x%016llx, FeatureFlags=%llu\n",
+ virthbainfo->flags_addr, phys_flags_addr,
+ (__le64)readq(virthbainfo->flags_addr));
+ str_pos += scnprintf(vbuf + str_pos,
+ len - str_pos, "acquire_failed_cnt:%llu\n",
+ virthbainfo->acquire_failed_cnt);
+ str_pos += scnprintf(vbuf + str_pos, len - str_pos, "\n");
+ }
+
+ bytes_read = simple_read_from_buffer(buf, len, offset, vbuf, str_pos);
+ kfree(vbuf);
+ return bytes_read;
+}
+
+static ssize_t enable_ints_write(struct file *file, const char __user *buffer,
+ size_t count, loff_t *ppos)
+{
+ char buf[4];
+ int i, new_value;
+ struct virthba_info *virthbainfo;
+
+ u64 __iomem *features_addr;
+ u64 mask;
+
+ if (count >= ARRAY_SIZE(buf))
+ return -EINVAL;
+
+ buf[count] = '\0';
+ if (copy_from_user(buf, buffer, count))
+ return -EFAULT;
+
+ i = kstrtoint(buf, 10, &new_value);
+
+ if (i != 0)
+ return -EFAULT;
+
+ /* set all counts to new_value usually 0 */
+ for (i = 0; i < VIRTHBASOPENMAX; i++) {
+ if (virthbas_open[i].virthbainfo) {
+ virthbainfo = virthbas_open[i].virthbainfo;
+ features_addr =
+ &virthbainfo->chinfo.queueinfo->chan->features;
+ if (new_value == 1) {
+ mask = ~(ULTRA_IO_CHANNEL_IS_POLLING |
+ ULTRA_IO_DRIVER_DISABLES_INTS);
+ uisqueue_interlocked_and(features_addr, mask);
+ mask = ULTRA_IO_DRIVER_ENABLES_INTS;
+ uisqueue_interlocked_or(features_addr, mask);
+ rsltq_wait_usecs = 4000000;
+ } else {
+ mask = ~(ULTRA_IO_DRIVER_ENABLES_INTS |
+ ULTRA_IO_DRIVER_DISABLES_INTS);
+ uisqueue_interlocked_and(features_addr, mask);
+ mask = ULTRA_IO_CHANNEL_IS_POLLING;
+ uisqueue_interlocked_or(features_addr, mask);
+ rsltq_wait_usecs = 4000;
+ }
+ }
+ }
+ return count;
+}
+
+/* As per VirtpciFunc returns 1 for success and 0 for failure */
+static int
+virthba_serverup(struct virtpci_dev *virtpcidev)
+{
+ struct virthba_info *virthbainfo =
+ (struct virthba_info *)((struct Scsi_Host *)virtpcidev->scsi.
+ scsihost)->hostdata;
+
+ if (!virthbainfo->serverdown)
+ return 1;
+
+ if (virthbainfo->serverchangingstate)
+ return 0;
+
+ virthbainfo->serverchangingstate = true;
+ /* Must transition channel to ATTACHED state BEFORE we
+ * can start using the device again
+ */
+ SPAR_CHANNEL_CLIENT_TRANSITION(virthbainfo->chinfo.queueinfo->chan,
+ dev_name(&virtpcidev->generic_dev),
+ CHANNELCLI_ATTACHED, NULL);
+
+ /* Start Processing the IOVM Response Queue Again */
+ if (!uisthread_start(&virthbainfo->chinfo.threadinfo,
+ process_incoming_rsps,
+ virthbainfo, "vhba_incoming")) {
+ return 0;
+ }
+ virthbainfo->serverdown = false;
+ virthbainfo->serverchangingstate = false;
+
+ return 1;
+}
+
+static void
+virthba_serverdown_complete(struct work_struct *work)
+{
+ struct virthba_info *virthbainfo;
+ struct virtpci_dev *virtpcidev;
+ int i;
+ struct scsipending *pendingdel = NULL;
+ struct scsi_cmnd *scsicmd = NULL;
+ struct uiscmdrsp *cmdrsp;
+ unsigned long flags;
+
+ virthbainfo = container_of(work, struct virthba_info,
+ serverdown_completion);
+
+ /* Stop Using the IOVM Response Queue (queue should be drained
+ * by the end)
+ */
+ uisthread_stop(&virthbainfo->chinfo.threadinfo);
+
+ /* Fail Commands that weren't completed */
+ spin_lock_irqsave(&virthbainfo->privlock, flags);
+ for (i = 0; i < MAX_PENDING_REQUESTS; i++) {
+ pendingdel = &virthbainfo->pending[i];
+ switch (pendingdel->cmdtype) {
+ case CMD_SCSI_TYPE:
+ scsicmd = (struct scsi_cmnd *)pendingdel->sent;
+ scsicmd->result = DID_RESET << 16;
+ if (scsicmd->scsi_done)
+ scsicmd->scsi_done(scsicmd);
+ break;
+ case CMD_SCSITASKMGMT_TYPE:
+ cmdrsp = (struct uiscmdrsp *)pendingdel->sent;
+ wake_up_all((wait_queue_head_t *)
+ cmdrsp->scsitaskmgmt.notify);
+ *(int *)cmdrsp->scsitaskmgmt.notifyresult =
+ TASK_MGMT_FAILED;
+ break;
+ case CMD_VDISKMGMT_TYPE:
+ cmdrsp = (struct uiscmdrsp *)pendingdel->sent;
+ *(int *)cmdrsp->vdiskmgmt.notifyresult =
+ VDISK_MGMT_FAILED;
+ wake_up_all((wait_queue_head_t *)
+ cmdrsp->vdiskmgmt.notify);
+ break;
+ default:
+ break;
+ }
+ pendingdel->cmdtype = 0;
+ pendingdel->sent = NULL;
+ }
+ spin_unlock_irqrestore(&virthbainfo->privlock, flags);
+
+ virtpcidev = virthbainfo->virtpcidev;
+
+ virthbainfo->serverdown = true;
+ virthbainfo->serverchangingstate = false;
+ /* Return the ServerDown response to Command */
+ visorchipset_device_pause_response(virtpcidev->bus_no,
+ virtpcidev->device_no, 0);
+}
+
+/* As per VirtpciFunc returns 1 for success and 0 for failure */
+static int
+virthba_serverdown(struct virtpci_dev *virtpcidev, u32 state)
+{
+ int stat = 1;
+
+ struct virthba_info *virthbainfo =
+ (struct virthba_info *)((struct Scsi_Host *)virtpcidev->scsi.
+ scsihost)->hostdata;
+
+ if (!virthbainfo->serverdown && !virthbainfo->serverchangingstate) {
+ virthbainfo->serverchangingstate = true;
+ queue_work(virthba_serverdown_workqueue,
+ &virthbainfo->serverdown_completion);
+ } else if (virthbainfo->serverchangingstate) {
+ stat = 0;
+ }
+
+ return stat;
+}
+
+/*****************************************************/
+/* Module Init & Exit functions */
+/*****************************************************/
+
+static int __init
+virthba_parse_line(char *str)
+{
+ return 1;
+}
+
+static void __init
+virthba_parse_options(char *line)
+{
+ char *next = line;
+
+ POSTCODE_LINUX_2(VHBA_CREATE_ENTRY_PC, POSTCODE_SEVERITY_INFO);
+ if (!line || !*line)
+ return;
+ while ((line = next)) {
+ next = strchr(line, ' ');
+ if (next)
+ *next++ = 0;
+ virthba_parse_line(line);
+ }
+
+ POSTCODE_LINUX_2(VHBA_CREATE_EXIT_PC, POSTCODE_SEVERITY_INFO);
+}
+
+static int __init
+virthba_mod_init(void)
+{
+ int error;
+ int i;
+
+ if (!unisys_spar_platform)
+ return -ENODEV;
+
+ POSTCODE_LINUX_2(VHBA_CREATE_ENTRY_PC, POSTCODE_SEVERITY_INFO);
+ virthba_parse_options(virthba_options);
+
+ error = virtpci_register_driver(&virthba_driver);
+ if (error < 0) {
+ POSTCODE_LINUX_3(VHBA_CREATE_FAILURE_PC, error,
+ POSTCODE_SEVERITY_ERR);
+ } else {
+ /* create the debugfs directories and entries */
+ virthba_debugfs_dir = debugfs_create_dir("virthba", NULL);
+ debugfs_create_file("info", S_IRUSR, virthba_debugfs_dir,
+ NULL, &debugfs_info_fops);
+ debugfs_create_u32("rqwait_usecs", S_IRUSR | S_IWUSR,
+ virthba_debugfs_dir, &rsltq_wait_usecs);
+ debugfs_create_file("enable_ints", S_IWUSR,
+ virthba_debugfs_dir, NULL,
+ &debugfs_enable_ints_fops);
+ /* Initialize dar_work_queue */
+ INIT_WORK(&dar_work_queue, do_disk_add_remove);
+ spin_lock_init(&dar_work_queue_lock);
+
+ /* clear out array */
+ for (i = 0; i < VIRTHBASOPENMAX; i++)
+ virthbas_open[i].virthbainfo = NULL;
+ /* Initialize the serverdown workqueue */
+ virthba_serverdown_workqueue =
+ create_singlethread_workqueue("virthba_serverdown");
+ if (!virthba_serverdown_workqueue) {
+ POSTCODE_LINUX_2(VHBA_CREATE_FAILURE_PC,
+ POSTCODE_SEVERITY_ERR);
+ error = -1;
+ }
+ }
+
+ POSTCODE_LINUX_2(VHBA_CREATE_EXIT_PC, POSTCODE_SEVERITY_INFO);
+ return error;
+}
+
+static ssize_t
+virthba_acquire_lun(struct device *cdev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct uisscsi_dest vdest;
+ struct Scsi_Host *shost = class_to_shost(cdev);
+ int i;
+
+ i = sscanf(buf, "%d-%d-%d", &vdest.channel, &vdest.id, &vdest.lun);
+ if (i != 3)
+ return i;
+
+ return forward_vdiskmgmt_command(VDISK_MGMT_ACQUIRE, shost, &vdest);
+}
+
+static ssize_t
+virthba_release_lun(struct device *cdev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct uisscsi_dest vdest;
+ struct Scsi_Host *shost = class_to_shost(cdev);
+ int i;
+
+ i = sscanf(buf, "%d-%d-%d", &vdest.channel, &vdest.id, &vdest.lun);
+ if (i != 3)
+ return i;
+
+ return forward_vdiskmgmt_command(VDISK_MGMT_RELEASE, shost, &vdest);
+}
+
+#define CLASS_DEVICE_ATTR(_name, _mode, _show, _store) \
+ struct device_attribute class_device_attr_##_name = \
+ __ATTR(_name, _mode, _show, _store)
+
+static CLASS_DEVICE_ATTR(acquire_lun, S_IWUSR, NULL, virthba_acquire_lun);
+static CLASS_DEVICE_ATTR(release_lun, S_IWUSR, NULL, virthba_release_lun);
+
+static DEVICE_ATTRIBUTE *virthba_shost_attrs[] = {
+ &class_device_attr_acquire_lun,
+ &class_device_attr_release_lun,
+ NULL
+};
+
+static void __exit
+virthba_mod_exit(void)
+{
+ virtpci_unregister_driver(&virthba_driver);
+ /* unregister is going to call virthba_remove */
+ /* destroy serverdown completion workqueue */
+ if (virthba_serverdown_workqueue) {
+ destroy_workqueue(virthba_serverdown_workqueue);
+ virthba_serverdown_workqueue = NULL;
+ }
+
+ debugfs_remove_recursive(virthba_debugfs_dir);
+}
+
+/* specify function to be run at module insertion time */
+module_init(virthba_mod_init);
+
+/* specify function to be run when module is removed */
+module_exit(virthba_mod_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Usha Srinivasan");
+MODULE_ALIAS("uisvirthba");
+ /* this is extracted during depmod and kept in modules.dep */
+/* module parameter */
+module_param(virthba_options, charp, S_IRUGO);
diff --git a/drivers/staging/unisys/virthba/virthba.h b/drivers/staging/unisys/virthba/virthba.h
new file mode 100644
index 000000000..59901668d
--- /dev/null
+++ b/drivers/staging/unisys/virthba/virthba.h
@@ -0,0 +1,27 @@
+/* virthba.h
+ *
+ * Copyright (C) 2010 - 2013 UNISYS CORPORATION
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or (at
+ * your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
+ * NON INFRINGEMENT. See the GNU General Public License for more
+ * details.
+ */
+
+/*
+ * Unisys Virtual HBA driver header
+ */
+
+#ifndef __VIRTHBA_H__
+#define __VIRTHBA_H__
+
+#define VIRTHBA_VERSION "01.00"
+
+#endif /* __VIRTHBA_H__ */
diff --git a/drivers/staging/unisys/virtpci/Kconfig b/drivers/staging/unisys/virtpci/Kconfig
new file mode 100644
index 000000000..6d19482ce
--- /dev/null
+++ b/drivers/staging/unisys/virtpci/Kconfig
@@ -0,0 +1,10 @@
+#
+# Unisys virtpci configuration
+#
+
+config UNISYS_VIRTPCI
+ tristate "Unisys virtpci driver"
+ select UNISYS_UISLIB
+ ---help---
+ If you say Y here, you will enable the Unisys virtpci driver.
+
diff --git a/drivers/staging/unisys/virtpci/Makefile b/drivers/staging/unisys/virtpci/Makefile
new file mode 100644
index 000000000..a26c69621
--- /dev/null
+++ b/drivers/staging/unisys/virtpci/Makefile
@@ -0,0 +1,10 @@
+#
+# Makefile for Unisys virtpci
+#
+
+obj-$(CONFIG_UNISYS_VIRTPCI) += virtpci.o
+
+ccflags-y += -Idrivers/staging/unisys/include
+ccflags-y += -Idrivers/staging/unisys/uislib
+ccflags-y += -Idrivers/staging/unisys/common-spar/include
+ccflags-y += -Idrivers/staging/unisys/common-spar/include/channels
diff --git a/drivers/staging/unisys/virtpci/virtpci.c b/drivers/staging/unisys/virtpci/virtpci.c
new file mode 100644
index 000000000..d5ad01783
--- /dev/null
+++ b/drivers/staging/unisys/virtpci/virtpci.c
@@ -0,0 +1,1394 @@
+/* virtpci.c
+ *
+ * Copyright (C) 2010 - 2013 UNISYS CORPORATION
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or (at
+ * your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
+ * NON INFRINGEMENT. See the GNU General Public License for more
+ * details.
+ */
+
+#define EXPORT_SYMTAB
+
+#include <linux/kernel.h>
+#ifdef CONFIG_MODVERSIONS
+#include <config/modversions.h>
+#endif
+#include "diagnostics/appos_subsystems.h"
+#include "uisutils.h"
+#include "vbuschannel.h"
+#include "vbushelper.h"
+#include <linux/types.h>
+#include <linux/io.h>
+#include <linux/uuid.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/pci.h>
+#include <linux/device.h>
+#include <linux/list.h>
+#include <linux/slab.h>
+#include <linux/mod_devicetable.h>
+#include <linux/if_ether.h>
+#include <linux/version.h>
+#include <linux/debugfs.h>
+#include "version.h"
+#include "guestlinuxdebug.h"
+#include "timskmod.h"
+
+struct driver_private {
+ struct kobject kobj;
+ struct klist klist_devices;
+ struct klist_node knode_bus;
+ struct module_kobject *mkobj;
+ struct device_driver *driver;
+};
+
+#define to_driver(obj) container_of(obj, struct driver_private, kobj)
+
+/* bus_id went away in 2.6.30 - the size was 20 bytes, so we'll define
+ * it ourselves, and a macro to make getting the field a bit simpler.
+ */
+#ifndef BUS_ID_SIZE
+#define BUS_ID_SIZE 20
+#endif
+
+#define BUS_ID(x) dev_name(x)
+
+/* MAX_BUF = 4 busses x ( 32 devices/bus + 1 busline) x 80 characters
+ * = 10,560 bytes ~ 2^14 = 16,384 bytes
+ */
+#define MAX_BUF 16384
+
+#include "virtpci.h"
+
+/* this is shorter than using __FILE__ (full path name) in
+ * debug/info/error messages
+ */
+#define CURRENT_FILE_PC VIRT_PCI_PC_virtpci_c
+#define __MYFILE__ "virtpci.c"
+
+#define VIRTPCI_VERSION "01.00"
+
+/*****************************************************/
+/* Forward declarations */
+/*****************************************************/
+
+static int delete_vbus_device(struct device *vbus, void *data);
+static int match_busid(struct device *dev, void *data);
+static void virtpci_bus_release(struct device *dev);
+static void virtpci_device_release(struct device *dev);
+static int virtpci_device_add(struct device *parentbus, int devtype,
+ struct add_virt_guestpart *addparams,
+ struct scsi_adap_info *scsi,
+ struct net_adap_info *net);
+static int virtpci_device_del(struct device *parentbus, int devtype,
+ struct vhba_wwnn *wwnn, unsigned char macaddr[]);
+static int virtpci_device_serverdown(struct device *parentbus, int devtype,
+ struct vhba_wwnn *wwnn,
+ unsigned char macaddr[]);
+static int virtpci_device_serverup(struct device *parentbus, int devtype,
+ struct vhba_wwnn *wwnn,
+ unsigned char macaddr[]);
+static ssize_t virtpci_driver_attr_show(struct kobject *kobj,
+ struct attribute *attr, char *buf);
+static ssize_t virtpci_driver_attr_store(struct kobject *kobj,
+ struct attribute *attr,
+ const char *buf, size_t count);
+static int virtpci_bus_match(struct device *dev, struct device_driver *drv);
+static int virtpci_uevent(struct device *dev, struct kobj_uevent_env *env);
+static int virtpci_device_probe(struct device *dev);
+static int virtpci_device_remove(struct device *dev);
+
+static ssize_t info_debugfs_read(struct file *file, char __user *buf,
+ size_t len, loff_t *offset);
+
+static const struct file_operations debugfs_info_fops = {
+ .read = info_debugfs_read,
+};
+
+/*****************************************************/
+/* Globals */
+/*****************************************************/
+
+/* methods in bus_type struct allow the bus code to serve as an
+ * intermediary between the device core and individual device core and
+ * individual drivers
+ */
+static struct bus_type virtpci_bus_type = {
+ .name = "uisvirtpci",
+ .match = virtpci_bus_match,
+ .uevent = virtpci_uevent,
+};
+
+static struct device virtpci_rootbus_device = {
+ .init_name = "vbusroot", /* root bus */
+ .release = virtpci_bus_release
+};
+
+/* filled in with info about parent chipset driver when we register with it */
+static struct ultra_vbus_deviceinfo chipset_driver_info;
+
+static const struct sysfs_ops virtpci_driver_sysfs_ops = {
+ .show = virtpci_driver_attr_show,
+ .store = virtpci_driver_attr_store,
+};
+
+static struct kobj_type virtpci_driver_kobj_type = {
+ .sysfs_ops = &virtpci_driver_sysfs_ops,
+};
+
+static struct virtpci_dev *vpcidev_list_head;
+static DEFINE_RWLOCK(vpcidev_list_lock);
+
+/* filled in with info about this driver, wrt it servicing client busses */
+static struct ultra_vbus_deviceinfo bus_driver_info;
+
+/*****************************************************/
+/* debugfs entries */
+/*****************************************************/
+/* dentry is used to create the debugfs entry directory
+ * for virtpci
+ */
+static struct dentry *virtpci_debugfs_dir;
+
+struct virtpci_busdev {
+ struct device virtpci_bus_device;
+};
+
+/*****************************************************/
+/* Local functions */
+/*****************************************************/
+
+static inline
+int WAIT_FOR_IO_CHANNEL(struct spar_io_channel_protocol __iomem *chanptr)
+{
+ int count = 120;
+
+ while (count > 0) {
+ if (SPAR_CHANNEL_SERVER_READY(&chanptr->channel_header))
+ return 1;
+ UIS_THREAD_WAIT_SEC(1);
+ count--;
+ }
+ return 0;
+}
+
+/* Write the contents of <info> to the ULTRA_VBUS_CHANNEL_PROTOCOL.ChpInfo. */
+static int write_vbus_chp_info(struct spar_vbus_channel_protocol *chan,
+ struct ultra_vbus_deviceinfo *info)
+{
+ int off;
+
+ if (!chan)
+ return -1;
+
+ off = sizeof(struct channel_header) + chan->hdr_info.chp_info_offset;
+ if (chan->hdr_info.chp_info_offset == 0) {
+ return -1;
+ }
+ memcpy(((u8 *)(chan)) + off, info, sizeof(*info));
+ return 0;
+}
+
+/* Write the contents of <info> to the ULTRA_VBUS_CHANNEL_PROTOCOL.BusInfo. */
+static int write_vbus_bus_info(struct spar_vbus_channel_protocol *chan,
+ struct ultra_vbus_deviceinfo *info)
+{
+ int off;
+
+ if (!chan)
+ return -1;
+
+ off = sizeof(struct channel_header) + chan->hdr_info.bus_info_offset;
+ if (chan->hdr_info.bus_info_offset == 0)
+ return -1;
+ memcpy(((u8 *)(chan)) + off, info, sizeof(*info));
+ return 0;
+}
+
+/* Write the contents of <info> to the
+ * ULTRA_VBUS_CHANNEL_PROTOCOL.DevInfo[<devix>].
+ */
+static int
+write_vbus_dev_info(struct spar_vbus_channel_protocol *chan,
+ struct ultra_vbus_deviceinfo *info, int devix)
+{
+ int off;
+
+ if (!chan)
+ return -1;
+
+ off =
+ (sizeof(struct channel_header) +
+ chan->hdr_info.dev_info_offset) +
+ (chan->hdr_info.device_info_struct_bytes * devix);
+ if (chan->hdr_info.dev_info_offset == 0)
+ return -1;
+
+ memcpy(((u8 *)(chan)) + off, info, sizeof(*info));
+ return 0;
+}
+
+/* adds a vbus
+ * returns 0 failure, 1 success,
+ */
+static int add_vbus(struct add_vbus_guestpart *addparams)
+{
+ int ret;
+ struct device *vbus;
+
+ vbus = kzalloc(sizeof(*vbus), GFP_ATOMIC);
+
+ POSTCODE_LINUX_2(VPCI_CREATE_ENTRY_PC, POSTCODE_SEVERITY_INFO);
+ if (!vbus)
+ return 0;
+
+ dev_set_name(vbus, "vbus%d", addparams->bus_no);
+ vbus->release = virtpci_bus_release;
+ vbus->parent = &virtpci_rootbus_device; /* root bus is parent */
+ vbus->bus = &virtpci_bus_type; /* bus type */
+ vbus->platform_data = (__force void *)addparams->chanptr;
+
+ /* register a virt bus device -
+ * this bus shows up under /sys/devices with .name value
+ * "virtpci%d" any devices added to this bus then show up under
+ * /sys/devices/virtpci0
+ */
+ ret = device_register(vbus);
+ if (ret) {
+ POSTCODE_LINUX_2(VPCI_CREATE_FAILURE_PC, POSTCODE_SEVERITY_ERR);
+ return 0;
+ }
+ write_vbus_chp_info(vbus->platform_data /* chanptr */,
+ &chipset_driver_info);
+ write_vbus_bus_info(vbus->platform_data /* chanptr */,
+ &bus_driver_info);
+ POSTCODE_LINUX_2(VPCI_CREATE_EXIT_PC, POSTCODE_SEVERITY_INFO);
+ return 1;
+}
+
+/* for CHANSOCK wwwnn/max are AUTO-GENERATED; for normal channels,
+ * wwnn/max are in the channel header.
+ */
+#define GET_SCSIADAPINFO_FROM_CHANPTR(chanptr) { \
+ memcpy_fromio(&scsi.wwnn, \
+ &((struct spar_io_channel_protocol __iomem *) \
+ chanptr)->vhba.wwnn, \
+ sizeof(struct vhba_wwnn)); \
+ memcpy_fromio(&scsi.max, \
+ &((struct spar_io_channel_protocol __iomem *) \
+ chanptr)->vhba.max, \
+ sizeof(struct vhba_config_max)); \
+ }
+
+/* adds a vhba
+ * returns 0 failure, 1 success,
+ */
+static int add_vhba(struct add_virt_guestpart *addparams)
+{
+ int i;
+ struct scsi_adap_info scsi;
+ struct device *vbus;
+ unsigned char busid[BUS_ID_SIZE];
+
+ POSTCODE_LINUX_2(VPCI_CREATE_ENTRY_PC, POSTCODE_SEVERITY_INFO);
+ if (!WAIT_FOR_IO_CHANNEL
+ ((struct spar_io_channel_protocol __iomem *)addparams->chanptr)) {
+ POSTCODE_LINUX_2(VPCI_CREATE_FAILURE_PC, POSTCODE_SEVERITY_ERR);
+ return 0;
+ }
+
+ GET_SCSIADAPINFO_FROM_CHANPTR(addparams->chanptr);
+
+ /* find bus device with the busid that matches match_busid */
+ sprintf(busid, "vbus%d", addparams->bus_no);
+ vbus = bus_find_device(&virtpci_bus_type, NULL,
+ (void *)busid, match_busid);
+ if (!vbus)
+ return 0;
+
+ i = virtpci_device_add(vbus, VIRTHBA_TYPE, addparams, &scsi, NULL);
+ if (i) {
+ POSTCODE_LINUX_3(VPCI_CREATE_EXIT_PC, i,
+ POSTCODE_SEVERITY_INFO);
+ }
+ return i;
+}
+
+/* for CHANSOCK macaddr is AUTO-GENERATED; for normal channels,
+ * macaddr is in the channel header.
+ */
+#define GET_NETADAPINFO_FROM_CHANPTR(chanptr) { \
+ memcpy_fromio(net.mac_addr, \
+ ((struct spar_io_channel_protocol __iomem *) \
+ chanptr)->vnic.macaddr, \
+ MAX_MACADDR_LEN); \
+ net.num_rcv_bufs = \
+ readl(&((struct spar_io_channel_protocol __iomem *)\
+ chanptr)->vnic.num_rcv_bufs); \
+ net.mtu = readl(&((struct spar_io_channel_protocol __iomem *) \
+ chanptr)->vnic.mtu); \
+ memcpy_fromio(&net.zone_uuid, \
+ &((struct spar_io_channel_protocol __iomem *)\
+ chanptr)->vnic.zone_uuid, \
+ sizeof(uuid_le)); \
+}
+
+/* adds a vnic
+ * returns 0 failure, 1 success,
+ */
+static int
+add_vnic(struct add_virt_guestpart *addparams)
+{
+ int i;
+ struct net_adap_info net;
+ struct device *vbus;
+ unsigned char busid[BUS_ID_SIZE];
+
+ POSTCODE_LINUX_2(VPCI_CREATE_ENTRY_PC, POSTCODE_SEVERITY_INFO);
+ if (!WAIT_FOR_IO_CHANNEL
+ ((struct spar_io_channel_protocol __iomem *)addparams->chanptr)) {
+ POSTCODE_LINUX_2(VPCI_CREATE_FAILURE_PC, POSTCODE_SEVERITY_ERR);
+ return 0;
+ }
+
+ GET_NETADAPINFO_FROM_CHANPTR(addparams->chanptr);
+
+ /* find bus device with the busid that matches match_busid */
+ sprintf(busid, "vbus%d", addparams->bus_no);
+ vbus = bus_find_device(&virtpci_bus_type, NULL,
+ (void *)busid, match_busid);
+ if (!vbus)
+ return 0;
+
+ i = virtpci_device_add(vbus, VIRTNIC_TYPE, addparams, NULL, &net);
+ if (i) {
+ POSTCODE_LINUX_3(VPCI_CREATE_EXIT_PC, i,
+ POSTCODE_SEVERITY_INFO);
+ return 1;
+ }
+ return 0;
+}
+
+/* delete vbus
+ * returns 0 failure, 1 success,
+ */
+static int
+delete_vbus(struct del_vbus_guestpart *delparams)
+{
+ struct device *vbus;
+ unsigned char busid[BUS_ID_SIZE];
+
+ /* find bus device with the busid that matches match_busid */
+ sprintf(busid, "vbus%d", delparams->bus_no);
+ vbus = bus_find_device(&virtpci_bus_type, NULL,
+ (void *)busid, match_busid);
+ if (!vbus)
+ return 0;
+
+ /* ensure that bus has no devices? -- TBD */
+ return 1;
+}
+
+static int
+delete_vbus_device(struct device *vbus, void *data)
+{
+ struct device *dev = &virtpci_rootbus_device;
+
+ if ((data) && match_busid(vbus, (void *)BUS_ID(dev))) {
+ /* skip it - don't delete root bus */
+ return 0; /* pretend no error */
+ }
+ device_unregister(vbus);
+ kfree(vbus);
+ return 0; /* no error */
+}
+
+/* pause vhba
+* returns 0 failure, 1 success,
+*/
+static int pause_vhba(struct pause_virt_guestpart *pauseparams)
+{
+ int i;
+ struct scsi_adap_info scsi;
+
+ GET_SCSIADAPINFO_FROM_CHANPTR(pauseparams->chanptr);
+
+ i = virtpci_device_serverdown(NULL /*no parent bus */, VIRTHBA_TYPE,
+ &scsi.wwnn, NULL);
+ return i;
+}
+
+/* pause vnic
+ * returns 0 failure, 1 success,
+ */
+static int pause_vnic(struct pause_virt_guestpart *pauseparams)
+{
+ int i;
+ struct net_adap_info net;
+
+ GET_NETADAPINFO_FROM_CHANPTR(pauseparams->chanptr);
+
+ i = virtpci_device_serverdown(NULL /*no parent bus */, VIRTNIC_TYPE,
+ NULL, net.mac_addr);
+ return i;
+}
+
+/* resume vhba
+ * returns 0 failure, 1 success,
+ */
+static int resume_vhba(struct resume_virt_guestpart *resumeparams)
+{
+ int i;
+ struct scsi_adap_info scsi;
+
+ GET_SCSIADAPINFO_FROM_CHANPTR(resumeparams->chanptr);
+
+ i = virtpci_device_serverup(NULL /*no parent bus */, VIRTHBA_TYPE,
+ &scsi.wwnn, NULL);
+ return i;
+}
+
+/* resume vnic
+* returns 0 failure, 1 success,
+*/
+static int
+resume_vnic(struct resume_virt_guestpart *resumeparams)
+{
+ int i;
+ struct net_adap_info net;
+
+ GET_NETADAPINFO_FROM_CHANPTR(resumeparams->chanptr);
+
+ i = virtpci_device_serverup(NULL /*no parent bus */, VIRTNIC_TYPE,
+ NULL, net.mac_addr);
+ return i;
+}
+
+/* delete vhba
+* returns 0 failure, 1 success,
+*/
+static int delete_vhba(struct del_virt_guestpart *delparams)
+{
+ int i;
+ struct scsi_adap_info scsi;
+
+ GET_SCSIADAPINFO_FROM_CHANPTR(delparams->chanptr);
+
+ i = virtpci_device_del(NULL /*no parent bus */, VIRTHBA_TYPE,
+ &scsi.wwnn, NULL);
+ if (i) {
+ return 1;
+ }
+ return 0;
+}
+
+/* deletes a vnic
+ * returns 0 failure, 1 success,
+ */
+static int delete_vnic(struct del_virt_guestpart *delparams)
+{
+ int i;
+ struct net_adap_info net;
+
+ GET_NETADAPINFO_FROM_CHANPTR(delparams->chanptr);
+
+ i = virtpci_device_del(NULL /*no parent bus */, VIRTNIC_TYPE, NULL,
+ net.mac_addr);
+ return i;
+}
+
+#define DELETE_ONE_VPCIDEV(vpcidev) { \
+ device_unregister(&vpcidev->generic_dev); \
+ kfree(vpcidev); \
+}
+
+/* deletes all vhbas and vnics
+ * returns 0 failure, 1 success,
+ */
+static void delete_all(void)
+{
+ int count = 0;
+ unsigned long flags;
+ struct virtpci_dev *tmpvpcidev, *nextvpcidev;
+
+ /* delete the entire vhba/vnic list in one shot */
+ write_lock_irqsave(&vpcidev_list_lock, flags);
+ tmpvpcidev = vpcidev_list_head;
+ vpcidev_list_head = NULL;
+ write_unlock_irqrestore(&vpcidev_list_lock, flags);
+
+ /* delete one vhba/vnic at a time */
+ while (tmpvpcidev) {
+ nextvpcidev = tmpvpcidev->next;
+ /* delete the vhba/vnic at tmpvpcidev */
+ DELETE_ONE_VPCIDEV(tmpvpcidev);
+ tmpvpcidev = nextvpcidev;
+ count++;
+ }
+
+ /* now delete each vbus */
+ bus_for_each_dev(&virtpci_bus_type, NULL, (void *)1,
+ delete_vbus_device);
+}
+
+/* deletes all vnics or vhbas
+ * returns 0 failure, 1 success,
+ */
+static int delete_all_virt(enum virtpci_dev_type devtype,
+ struct del_vbus_guestpart *delparams)
+{
+ int i;
+ unsigned char busid[BUS_ID_SIZE];
+ struct device *vbus;
+
+ /* find bus device with the busid that matches match_busid */
+ sprintf(busid, "vbus%d", delparams->bus_no);
+ vbus = bus_find_device(&virtpci_bus_type, NULL,
+ (void *)busid, match_busid);
+ if (!vbus)
+ return 0;
+
+ if ((devtype != VIRTHBA_TYPE) && (devtype != VIRTNIC_TYPE))
+ return 0;
+
+ /* delete all vhbas/vnics */
+ i = virtpci_device_del(vbus, devtype, NULL, NULL);
+ return 1;
+}
+
+static int virtpci_ctrlchan_func(struct guest_msgs *msg)
+{
+ switch (msg->msgtype) {
+ case GUEST_ADD_VBUS:
+ return add_vbus(&msg->add_vbus);
+ case GUEST_ADD_VHBA:
+ return add_vhba(&msg->add_vhba);
+ case GUEST_ADD_VNIC:
+ return add_vnic(&msg->add_vnic);
+ case GUEST_DEL_VBUS:
+ return delete_vbus(&msg->del_vbus);
+ case GUEST_DEL_VHBA:
+ return delete_vhba(&msg->del_vhba);
+ case GUEST_DEL_VNIC:
+ return delete_vnic(&msg->del_vhba);
+ case GUEST_DEL_ALL_VHBAS:
+ return delete_all_virt(VIRTHBA_TYPE, &msg->del_all_vhbas);
+ case GUEST_DEL_ALL_VNICS:
+ return delete_all_virt(VIRTNIC_TYPE, &msg->del_all_vnics);
+ case GUEST_DEL_ALL_VBUSES:
+ delete_all();
+ return 1;
+ case GUEST_PAUSE_VHBA:
+ return pause_vhba(&msg->pause_vhba);
+ case GUEST_PAUSE_VNIC:
+ return pause_vnic(&msg->pause_vnic);
+ case GUEST_RESUME_VHBA:
+ return resume_vhba(&msg->resume_vhba);
+ case GUEST_RESUME_VNIC:
+ return resume_vnic(&msg->resume_vnic);
+ default:
+ return 0;
+ }
+}
+
+/* same as driver_helper in bus.c linux */
+static int match_busid(struct device *dev, void *data)
+{
+ const char *name = data;
+
+ if (strcmp(name, BUS_ID(dev)) == 0)
+ return 1;
+ return 0;
+}
+
+/*****************************************************/
+/* Bus functions */
+/*****************************************************/
+
+static const struct pci_device_id *
+virtpci_match_device(const struct pci_device_id *ids,
+ const struct virtpci_dev *dev)
+{
+ while (ids->vendor || ids->subvendor || ids->class_mask) {
+ if ((ids->vendor == dev->vendor) &&
+ (ids->device == dev->device))
+ return ids;
+
+ ids++;
+ }
+ return NULL;
+}
+
+/* NOTE: !!!!!! This function is called when a new device is added
+* for this bus. Or, it is called for existing devices when a new
+* driver is added for this bus. It returns nonzero if a given device
+* can be handled by the given driver.
+*/
+static int virtpci_bus_match(struct device *dev, struct device_driver *drv)
+{
+ struct virtpci_dev *virtpcidev = device_to_virtpci_dev(dev);
+ struct virtpci_driver *virtpcidrv = driver_to_virtpci_driver(drv);
+ int match = 0;
+
+ /* check ids list for a match */
+ if (virtpci_match_device(virtpcidrv->id_table, virtpcidev))
+ match = 1;
+
+ return match; /* 0 - no match; 1 - yes it matches */
+}
+
+static int virtpci_uevent(struct device *dev, struct kobj_uevent_env *env)
+{
+ /* add variables to the environment prior to the generation of
+ * hotplug events to user space
+ */
+ if (add_uevent_var(env, "VIRTPCI_VERSION=%s", VIRTPCI_VERSION))
+ return -ENOMEM;
+ return 0;
+}
+
+/* For a child device just created on a client bus, fill in
+ * information about the driver that is controlling this device into
+ * the appropriate slot within the vbus channel of the bus
+ * instance.
+ */
+static void fix_vbus_dev_info(struct device *dev, int dev_no, int dev_type,
+ struct virtpci_driver *virtpcidrv)
+{
+ struct device *vbus;
+ void *chan;
+ struct ultra_vbus_deviceinfo dev_info;
+ const char *stype;
+
+ if (!dev)
+ return;
+ if (!virtpcidrv)
+ return;
+
+ vbus = dev->parent;
+ if (!vbus)
+ return;
+
+ chan = vbus->platform_data;
+ if (!chan)
+ return;
+
+ switch (dev_type) {
+ case PCI_DEVICE_ID_VIRTHBA:
+ stype = "vHBA";
+ break;
+ case PCI_DEVICE_ID_VIRTNIC:
+ stype = "vNIC";
+ break;
+ default:
+ stype = "unknown";
+ break;
+ }
+ bus_device_info_init(&dev_info, stype,
+ virtpcidrv->name,
+ virtpcidrv->version,
+ virtpcidrv->vertag);
+ write_vbus_dev_info(chan, &dev_info, dev_no);
+
+ /* Re-write bus+chipset info, because it is possible that this
+ * was previously written by our good counterpart, visorbus.
+ */
+ write_vbus_chp_info(chan, &chipset_driver_info);
+ write_vbus_bus_info(chan, &bus_driver_info);
+}
+
+/* This function is called to query the existence of a specific device
+* and whether this driver can work with it. It should return -ENODEV
+* in case of failure.
+*/
+static int virtpci_device_probe(struct device *dev)
+{
+ struct virtpci_dev *virtpcidev = device_to_virtpci_dev(dev);
+ struct virtpci_driver *virtpcidrv =
+ driver_to_virtpci_driver(dev->driver);
+ const struct pci_device_id *id;
+ int error = 0;
+
+ POSTCODE_LINUX_2(VPCI_PROBE_ENTRY_PC, POSTCODE_SEVERITY_INFO);
+ /* static match and static probe vs dynamic match & dynamic
+ * probe - do we care?.
+ */
+ if (!virtpcidrv->id_table)
+ return -ENODEV;
+
+ id = virtpci_match_device(virtpcidrv->id_table, virtpcidev);
+ if (!id)
+ return -ENODEV;
+
+ /* increment reference count */
+ get_device(dev);
+
+ /* if virtpcidev is not already claimed & probe function is
+ * valid, probe it
+ */
+ if (!virtpcidev->mydriver && virtpcidrv->probe) {
+ /* call the probe function - virthba or virtnic probe
+ * is what it should be
+ */
+ error = virtpcidrv->probe(virtpcidev, id);
+ if (!error) {
+ fix_vbus_dev_info(dev, virtpcidev->device_no,
+ virtpcidev->device, virtpcidrv);
+ virtpcidev->mydriver = virtpcidrv;
+ POSTCODE_LINUX_2(VPCI_PROBE_EXIT_PC,
+ POSTCODE_SEVERITY_INFO);
+ } else {
+ put_device(dev);
+ }
+ }
+ POSTCODE_LINUX_2(VPCI_PROBE_FAILURE_PC, POSTCODE_SEVERITY_ERR);
+ return error; /* -ENODEV for probe failure */
+}
+
+static int virtpci_device_remove(struct device *dev_)
+{
+ /* dev_ passed in is the HBA device which we called
+ * generic_dev in our virtpcidev struct
+ */
+ struct virtpci_dev *virtpcidev = device_to_virtpci_dev(dev_);
+ struct virtpci_driver *virtpcidrv = virtpcidev->mydriver;
+
+ if (virtpcidrv) {
+ /* TEMP: assuming we have only one such driver for now */
+ if (virtpcidrv->remove)
+ virtpcidrv->remove(virtpcidev);
+ virtpcidev->mydriver = NULL;
+ }
+
+ put_device(dev_);
+ return 0;
+}
+
+/*****************************************************/
+/* Bus functions */
+/*****************************************************/
+
+static void virtpci_bus_release(struct device *dev)
+{
+}
+
+/*****************************************************/
+/* Adapter functions */
+/*****************************************************/
+
+/* scsi is expected to be NULL for VNIC add
+ * net is expected to be NULL for VHBA add
+ */
+static int virtpci_device_add(struct device *parentbus, int devtype,
+ struct add_virt_guestpart *addparams,
+ struct scsi_adap_info *scsi,
+ struct net_adap_info *net)
+{
+ struct virtpci_dev *virtpcidev = NULL;
+ struct virtpci_dev *tmpvpcidev = NULL, *prev;
+ unsigned long flags;
+ int ret;
+ struct spar_io_channel_protocol __iomem *io_chan = NULL;
+ struct device *dev;
+
+ POSTCODE_LINUX_2(VPCI_CREATE_ENTRY_PC, POSTCODE_SEVERITY_INFO);
+
+ if ((devtype != VIRTHBA_TYPE) && (devtype != VIRTNIC_TYPE)) {
+ POSTCODE_LINUX_3(VPCI_CREATE_FAILURE_PC, devtype,
+ POSTCODE_SEVERITY_ERR);
+ return 0;
+ }
+
+ /* add a Virtual Device */
+ virtpcidev = kzalloc(sizeof(*virtpcidev), GFP_ATOMIC);
+ if (!virtpcidev) {
+ POSTCODE_LINUX_2(MALLOC_FAILURE_PC, POSTCODE_SEVERITY_ERR);
+ return 0;
+ }
+
+ /* initialize stuff unique to virtpci_dev struct */
+ virtpcidev->devtype = devtype;
+ if (devtype == VIRTHBA_TYPE) {
+ virtpcidev->device = PCI_DEVICE_ID_VIRTHBA;
+ virtpcidev->scsi = *scsi;
+ } else {
+ virtpcidev->device = PCI_DEVICE_ID_VIRTNIC;
+ virtpcidev->net = *net;
+ }
+ virtpcidev->vendor = PCI_VENDOR_ID_UNISYS;
+ virtpcidev->bus_no = addparams->bus_no;
+ virtpcidev->device_no = addparams->device_no;
+
+ virtpcidev->queueinfo.chan = addparams->chanptr;
+ virtpcidev->queueinfo.send_int_if_needed = NULL;
+
+ /* Set up safe queue... */
+ io_chan = (struct spar_io_channel_protocol __iomem *)
+ virtpcidev->queueinfo.chan;
+
+ virtpcidev->intr = addparams->intr;
+
+ /* initialize stuff in the device portion of the struct */
+ virtpcidev->generic_dev.bus = &virtpci_bus_type;
+ virtpcidev->generic_dev.parent = parentbus;
+ virtpcidev->generic_dev.release = virtpci_device_release;
+
+ dev_set_name(&virtpcidev->generic_dev, "%x:%x",
+ addparams->bus_no, addparams->device_no);
+
+ /* add the vhba/vnic to virtpci device list - but check for
+ * duplicate wwnn/macaddr first
+ */
+ write_lock_irqsave(&vpcidev_list_lock, flags);
+ for (tmpvpcidev = vpcidev_list_head; tmpvpcidev;
+ tmpvpcidev = tmpvpcidev->next) {
+ if (devtype == VIRTHBA_TYPE) {
+ if ((tmpvpcidev->scsi.wwnn.wwnn1 == scsi->wwnn.wwnn1) &&
+ (tmpvpcidev->scsi.wwnn.wwnn2 == scsi->wwnn.wwnn2)) {
+ /* duplicate - already have vpcidev
+ with this wwnn */
+ break;
+ }
+ } else
+ if (memcmp
+ (tmpvpcidev->net.mac_addr, net->mac_addr,
+ MAX_MACADDR_LEN) == 0) {
+ /* duplicate - already have vnic with this wwnn */
+ break;
+ }
+ }
+ if (tmpvpcidev) {
+ /* found a vhba/vnic already in the list with same
+ * wwnn or macaddr - reject add
+ */
+ write_unlock_irqrestore(&vpcidev_list_lock, flags);
+ kfree(virtpcidev);
+ POSTCODE_LINUX_2(VPCI_CREATE_FAILURE_PC, POSTCODE_SEVERITY_ERR);
+ return 0;
+ }
+
+ /* add it at the head */
+ if (!vpcidev_list_head) {
+ vpcidev_list_head = virtpcidev;
+ } else {
+ /* insert virtpcidev at the head of our linked list of
+ * vpcidevs
+ */
+ virtpcidev->next = vpcidev_list_head;
+ vpcidev_list_head = virtpcidev;
+ }
+
+ write_unlock_irqrestore(&vpcidev_list_lock, flags);
+
+ /* Must transition channel to ATTACHED state BEFORE
+ * registering the device, because polling of the channel
+ * queues can begin at any time after device_register().
+ */
+ dev = &virtpcidev->generic_dev;
+ SPAR_CHANNEL_CLIENT_TRANSITION(addparams->chanptr,
+ BUS_ID(dev),
+ CHANNELCLI_ATTACHED, NULL);
+
+ /* don't register until device has been added to
+ * list. Otherwise, a device_unregister from this function can
+ * cause a "scheduling while atomic".
+ */
+ ret = device_register(&virtpcidev->generic_dev);
+ /* NOTE: THIS IS CALLING HOTPLUG virtpci_hotplug!!!
+ * This call to device_register results in virtpci_bus_match
+ * being called !!!!! And, if match returns success, then
+ * virtpcidev->generic_dev.driver is setup to core_driver,
+ * i.e., virtpci and the probe function
+ * virtpcidev->generic_dev.driver->probe is called which
+ * results in virtpci_device_probe being called. And if
+ * virtpci_device_probe is successful
+ */
+ if (ret) {
+ dev = &virtpcidev->generic_dev;
+ SPAR_CHANNEL_CLIENT_TRANSITION(addparams->chanptr,
+ BUS_ID(dev),
+ CHANNELCLI_DETACHED, NULL);
+ /* remove virtpcidev, the one we just added, from the list */
+ write_lock_irqsave(&vpcidev_list_lock, flags);
+ for (tmpvpcidev = vpcidev_list_head, prev = NULL;
+ tmpvpcidev;
+ prev = tmpvpcidev, tmpvpcidev = tmpvpcidev->next) {
+ if (tmpvpcidev == virtpcidev) {
+ if (prev)
+ prev->next = tmpvpcidev->next;
+ else
+ vpcidev_list_head = tmpvpcidev->next;
+ break;
+ }
+ }
+ write_unlock_irqrestore(&vpcidev_list_lock, flags);
+ kfree(virtpcidev);
+ return 0;
+ }
+
+ POSTCODE_LINUX_2(VPCI_CREATE_EXIT_PC, POSTCODE_SEVERITY_INFO);
+ return 1;
+}
+
+static int virtpci_device_serverdown(struct device *parentbus,
+ int devtype,
+ struct vhba_wwnn *wwnn,
+ unsigned char macaddr[])
+{
+ int pausethisone = 0;
+ bool found = false;
+ struct virtpci_dev *tmpvpcidev, *prevvpcidev;
+ struct virtpci_driver *vpcidriver;
+ unsigned long flags;
+ int rc = 0;
+
+ if ((devtype != VIRTHBA_TYPE) && (devtype != VIRTNIC_TYPE))
+ return 0;
+
+ /* find the vhba or vnic in virtpci device list */
+ write_lock_irqsave(&vpcidev_list_lock, flags);
+
+ for (tmpvpcidev = vpcidev_list_head, prevvpcidev = NULL;
+ (tmpvpcidev && !found);
+ prevvpcidev = tmpvpcidev, tmpvpcidev = tmpvpcidev->next) {
+ if (tmpvpcidev->devtype != devtype)
+ continue;
+
+ if (devtype == VIRTHBA_TYPE) {
+ pausethisone =
+ ((tmpvpcidev->scsi.wwnn.wwnn1 == wwnn->wwnn1) &&
+ (tmpvpcidev->scsi.wwnn.wwnn2 == wwnn->wwnn2));
+ /* devtype is vhba, we're pausing vhba whose
+ * wwnn matches the current device's wwnn
+ */
+ } else { /* VIRTNIC_TYPE */
+ pausethisone =
+ memcmp(tmpvpcidev->net.mac_addr, macaddr,
+ MAX_MACADDR_LEN) == 0;
+ /* devtype is vnic, we're pausing vnic whose
+ * macaddr matches the current device's macaddr */
+ }
+
+ if (!pausethisone)
+ continue;
+
+ found = true;
+ vpcidriver = tmpvpcidev->mydriver;
+ rc = vpcidriver->suspend(tmpvpcidev, 0);
+ }
+ write_unlock_irqrestore(&vpcidev_list_lock, flags);
+
+ if (!found)
+ return 0;
+
+ return rc;
+}
+
+static int virtpci_device_serverup(struct device *parentbus,
+ int devtype,
+ struct vhba_wwnn *wwnn,
+ unsigned char macaddr[])
+{
+ int resumethisone = 0;
+ bool found = false;
+ struct virtpci_dev *tmpvpcidev, *prevvpcidev;
+ struct virtpci_driver *vpcidriver;
+ unsigned long flags;
+ int rc = 0;
+
+ if ((devtype != VIRTHBA_TYPE) && (devtype != VIRTNIC_TYPE))
+ return 0;
+
+
+ /* find the vhba or vnic in virtpci device list */
+ write_lock_irqsave(&vpcidev_list_lock, flags);
+
+ for (tmpvpcidev = vpcidev_list_head, prevvpcidev = NULL;
+ (tmpvpcidev && !found);
+ prevvpcidev = tmpvpcidev, tmpvpcidev = tmpvpcidev->next) {
+ if (tmpvpcidev->devtype != devtype)
+ continue;
+
+ if (devtype == VIRTHBA_TYPE) {
+ resumethisone =
+ ((tmpvpcidev->scsi.wwnn.wwnn1 == wwnn->wwnn1) &&
+ (tmpvpcidev->scsi.wwnn.wwnn2 == wwnn->wwnn2));
+ /* devtype is vhba, we're resuming vhba whose
+ * wwnn matches the current device's wwnn */
+ } else { /* VIRTNIC_TYPE */
+ resumethisone =
+ memcmp(tmpvpcidev->net.mac_addr, macaddr,
+ MAX_MACADDR_LEN) == 0;
+ /* devtype is vnic, we're resuming vnic whose
+ * macaddr matches the current device's macaddr */
+ }
+
+ if (!resumethisone)
+ continue;
+
+ found = true;
+ vpcidriver = tmpvpcidev->mydriver;
+ /* This should be done at BUS resume time, but an
+ * existing problem prevents us from ever getting a bus
+ * resume... This hack would fail to work should we
+ * ever have a bus that contains NO devices, since we
+ * would never even get here in that case.
+ */
+ fix_vbus_dev_info(&tmpvpcidev->generic_dev,
+ tmpvpcidev->device_no,
+ tmpvpcidev->device, vpcidriver);
+ rc = vpcidriver->resume(tmpvpcidev);
+ }
+
+ write_unlock_irqrestore(&vpcidev_list_lock, flags);
+
+ if (!found)
+ return 0;
+
+ return rc;
+}
+
+static int virtpci_device_del(struct device *parentbus,
+ int devtype, struct vhba_wwnn *wwnn,
+ unsigned char macaddr[])
+{
+ int count = 0, all = 0, delthisone;
+ struct virtpci_dev *tmpvpcidev, *prevvpcidev, *dellist = NULL;
+ unsigned long flags;
+
+#define DEL_CONTINUE { \
+ prevvpcidev = tmpvpcidev;\
+ tmpvpcidev = tmpvpcidev->next;\
+ continue; \
+}
+
+ if ((devtype != VIRTHBA_TYPE) && (devtype != VIRTNIC_TYPE))
+ return 0;
+
+ /* see if we are to delete all - NOTE: all implies we have a
+ * valid parentbus
+ */
+ all = ((devtype == VIRTHBA_TYPE) && (!wwnn)) ||
+ ((devtype == VIRTNIC_TYPE) && (!macaddr));
+
+ /* find all the vhba or vnic or both in virtpci device list
+ * keep list of ones we are deleting so we can call
+ * device_unregister after we release the lock; otherwise we
+ * encounter "schedule while atomic"
+ */
+ write_lock_irqsave(&vpcidev_list_lock, flags);
+ for (tmpvpcidev = vpcidev_list_head, prevvpcidev = NULL; tmpvpcidev;) {
+ if (tmpvpcidev->devtype != devtype)
+ DEL_CONTINUE;
+
+ if (all) {
+ delthisone =
+ (tmpvpcidev->generic_dev.parent == parentbus);
+ /* we're deleting all vhbas or vnics on the
+ * specified parent bus
+ */
+ } else if (devtype == VIRTHBA_TYPE) {
+ delthisone =
+ ((tmpvpcidev->scsi.wwnn.wwnn1 == wwnn->wwnn1) &&
+ (tmpvpcidev->scsi.wwnn.wwnn2 == wwnn->wwnn2));
+ /* devtype is vhba, we're deleting vhba whose
+ * wwnn matches the current device's wwnn
+ */
+ } else { /* VIRTNIC_TYPE */
+ delthisone =
+ memcmp(tmpvpcidev->net.mac_addr, macaddr,
+ MAX_MACADDR_LEN) == 0;
+ /* devtype is vnic, we're deleting vnic whose
+ * macaddr matches the current device's macaddr
+ */
+ }
+
+ if (!delthisone)
+ DEL_CONTINUE;
+
+ /* take vhba/vnic out of the list */
+ if (prevvpcidev)
+ /* not at head */
+ prevvpcidev->next = tmpvpcidev->next;
+ else
+ vpcidev_list_head = tmpvpcidev->next;
+
+ /* add it to our deletelist */
+ tmpvpcidev->next = dellist;
+ dellist = tmpvpcidev;
+
+ count++;
+ if (!all)
+ break; /* done */
+ /* going to top of loop again - set tmpvpcidev to next
+ * one we're to process
+ */
+ if (prevvpcidev)
+ tmpvpcidev = prevvpcidev->next;
+ else
+ tmpvpcidev = vpcidev_list_head;
+ }
+ write_unlock_irqrestore(&vpcidev_list_lock, flags);
+
+ if (!all && (count == 0))
+ return 0;
+
+ /* now delete each one from delete list */
+ while (dellist) {
+ /* save next */
+ tmpvpcidev = dellist->next;
+ /* delete the vhba/vnic at dellist */
+ DELETE_ONE_VPCIDEV(dellist);
+ /* do next */
+ dellist = tmpvpcidev;
+ }
+
+ return count;
+}
+
+static void virtpci_device_release(struct device *dev_)
+{
+ /* this function is called when the last reference to the
+ * device is removed
+ */
+}
+
+/*****************************************************/
+/* Driver functions */
+/*****************************************************/
+
+#define kobj_to_device_driver(obj) container_of(obj, struct device_driver, kobj)
+#define attribute_to_driver_attribute(obj) \
+ container_of(obj, struct driver_attribute, attr)
+
+static ssize_t virtpci_driver_attr_show(struct kobject *kobj,
+ struct attribute *attr,
+ char *buf)
+{
+ struct driver_attribute *dattr = attribute_to_driver_attribute(attr);
+ ssize_t ret = 0;
+
+ struct driver_private *dprivate = to_driver(kobj);
+ struct device_driver *driver = dprivate->driver;
+
+ if (dattr->show)
+ ret = dattr->show(driver, buf);
+
+ return ret;
+}
+
+static ssize_t virtpci_driver_attr_store(struct kobject *kobj,
+ struct attribute *attr,
+ const char *buf, size_t count)
+{
+ struct driver_attribute *dattr = attribute_to_driver_attribute(attr);
+ ssize_t ret = 0;
+
+ struct driver_private *dprivate = to_driver(kobj);
+ struct device_driver *driver = dprivate->driver;
+
+ if (dattr->store)
+ ret = dattr->store(driver, buf, count);
+
+ return ret;
+}
+
+/* register a new virtpci driver */
+int virtpci_register_driver(struct virtpci_driver *drv)
+{
+ int result = 0;
+
+ if (!drv->id_table)
+ return 1;
+ /* initialize core driver fields needed to call driver_register */
+ drv->core_driver.name = drv->name; /* name of driver in sysfs */
+ drv->core_driver.bus = &virtpci_bus_type; /* type of bus this
+ * driver works with */
+ drv->core_driver.probe = virtpci_device_probe; /* called to query the
+ * existence of a
+ * specific device and
+ * whether this driver
+ *can work with it */
+ drv->core_driver.remove = virtpci_device_remove; /* called when the
+ * device is removed
+ * from the system */
+ /* register with core */
+ result = driver_register(&drv->core_driver);
+ /* calls bus_add_driver which calls driver_attach and
+ * module_add_driver
+ */
+ if (result)
+ return result; /* failed */
+
+ drv->core_driver.p->kobj.ktype = &virtpci_driver_kobj_type;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(virtpci_register_driver);
+
+void virtpci_unregister_driver(struct virtpci_driver *drv)
+{
+ driver_unregister(&drv->core_driver);
+ /* driver_unregister calls bus_remove_driver
+ * bus_remove_driver calls device_detach
+ * device_detach calls device_release_driver for each of the
+ * driver's devices
+ * device_release driver calls drv->remove which is
+ * virtpci_device_remove
+ * virtpci_device_remove calls virthba_remove
+ */
+}
+EXPORT_SYMBOL_GPL(virtpci_unregister_driver);
+
+/*****************************************************/
+/* debugfs filesystem functions */
+/*****************************************************/
+struct print_vbus_info {
+ int *str_pos;
+ char *buf;
+ size_t *len;
+};
+
+static int print_vbus(struct device *vbus, void *data)
+{
+ struct print_vbus_info *p = (struct print_vbus_info *)data;
+
+ *p->str_pos += scnprintf(p->buf + *p->str_pos, *p->len - *p->str_pos,
+ "bus_id:%s\n", dev_name(vbus));
+ return 0;
+}
+
+static ssize_t info_debugfs_read(struct file *file, char __user *buf,
+ size_t len, loff_t *offset)
+{
+ ssize_t bytes_read = 0;
+ int str_pos = 0;
+ struct virtpci_dev *tmpvpcidev;
+ unsigned long flags;
+ struct print_vbus_info printparam;
+ char *vbuf;
+
+ if (len > MAX_BUF)
+ len = MAX_BUF;
+ vbuf = kzalloc(len, GFP_KERNEL);
+ if (!vbuf)
+ return -ENOMEM;
+
+ str_pos += scnprintf(vbuf + str_pos, len - str_pos,
+ " Virtual PCI Bus devices\n");
+ printparam.str_pos = &str_pos;
+ printparam.buf = vbuf;
+ printparam.len = &len;
+ bus_for_each_dev(&virtpci_bus_type, NULL, (void *)&printparam,
+ print_vbus);
+
+ str_pos += scnprintf(vbuf + str_pos, len - str_pos,
+ "\n Virtual PCI devices\n");
+ read_lock_irqsave(&vpcidev_list_lock, flags);
+ tmpvpcidev = vpcidev_list_head;
+ while (tmpvpcidev) {
+ if (tmpvpcidev->devtype == VIRTHBA_TYPE) {
+ str_pos += scnprintf(vbuf + str_pos, len - str_pos,
+ "[%d:%d] VHba:%08x:%08x max-config:%d-%d-%d-%d",
+ tmpvpcidev->bus_no,
+ tmpvpcidev->device_no,
+ tmpvpcidev->scsi.wwnn.wwnn1,
+ tmpvpcidev->scsi.wwnn.wwnn2,
+ tmpvpcidev->scsi.max.max_channel,
+ tmpvpcidev->scsi.max.max_id,
+ tmpvpcidev->scsi.max.max_lun,
+ tmpvpcidev->scsi.max.cmd_per_lun);
+ } else {
+ str_pos += scnprintf(vbuf + str_pos, len - str_pos,
+ "[%d:%d] VNic:%pM num_rcv_bufs:%d mtu:%d",
+ tmpvpcidev->bus_no,
+ tmpvpcidev->device_no,
+ tmpvpcidev->net.mac_addr,
+ tmpvpcidev->net.num_rcv_bufs,
+ tmpvpcidev->net.mtu);
+ }
+ str_pos += scnprintf(vbuf + str_pos,
+ len - str_pos, " chanptr:%p\n",
+ tmpvpcidev->queueinfo.chan);
+ tmpvpcidev = tmpvpcidev->next;
+ }
+ read_unlock_irqrestore(&vpcidev_list_lock, flags);
+
+ str_pos += scnprintf(vbuf + str_pos, len - str_pos, "\n");
+ bytes_read = simple_read_from_buffer(buf, len, offset, vbuf, str_pos);
+ kfree(vbuf);
+ return bytes_read;
+}
+
+/*****************************************************/
+/* Module Init & Exit functions */
+/*****************************************************/
+
+static int __init virtpci_mod_init(void)
+{
+ int ret;
+
+ if (!unisys_spar_platform)
+ return -ENODEV;
+
+ POSTCODE_LINUX_2(VPCI_CREATE_ENTRY_PC, POSTCODE_SEVERITY_INFO);
+
+ ret = bus_register(&virtpci_bus_type);
+ /* creates /sys/bus/uisvirtpci which contains devices &
+ * drivers directory
+ */
+ if (ret) {
+ POSTCODE_LINUX_3(VPCI_CREATE_FAILURE_PC, ret,
+ POSTCODE_SEVERITY_ERR);
+ return ret;
+ }
+ bus_device_info_init(&bus_driver_info, "clientbus", "virtpci",
+ VERSION, NULL);
+
+ /* create a root bus used to parent all the virtpci buses. */
+ ret = device_register(&virtpci_rootbus_device);
+ if (ret) {
+ bus_unregister(&virtpci_bus_type);
+ POSTCODE_LINUX_3(VPCI_CREATE_FAILURE_PC, ret,
+ POSTCODE_SEVERITY_ERR);
+ return ret;
+ }
+
+ if (!uisctrl_register_req_handler(2, (void *)&virtpci_ctrlchan_func,
+ &chipset_driver_info)) {
+ POSTCODE_LINUX_2(VPCI_CREATE_FAILURE_PC, POSTCODE_SEVERITY_ERR);
+ device_unregister(&virtpci_rootbus_device);
+ bus_unregister(&virtpci_bus_type);
+ return -1;
+ }
+
+ /* create debugfs directory and info file inside. */
+ virtpci_debugfs_dir = debugfs_create_dir("virtpci", NULL);
+ debugfs_create_file("info", S_IRUSR, virtpci_debugfs_dir,
+ NULL, &debugfs_info_fops);
+ POSTCODE_LINUX_2(VPCI_CREATE_EXIT_PC, POSTCODE_SEVERITY_INFO);
+ return 0;
+}
+
+static void __exit virtpci_mod_exit(void)
+{
+ /* unregister the callback function */
+ device_unregister(&virtpci_rootbus_device);
+ bus_unregister(&virtpci_bus_type);
+ debugfs_remove_recursive(virtpci_debugfs_dir);
+}
+
+module_init(virtpci_mod_init);
+module_exit(virtpci_mod_exit);
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Usha Srinivasan");
+MODULE_ALIAS("uisvirtpci");
+
diff --git a/drivers/staging/unisys/virtpci/virtpci.h b/drivers/staging/unisys/virtpci/virtpci.h
new file mode 100644
index 000000000..9d85f55e8
--- /dev/null
+++ b/drivers/staging/unisys/virtpci/virtpci.h
@@ -0,0 +1,103 @@
+/* virtpci.h
+ *
+ * Copyright (C) 2010 - 2013 UNISYS CORPORATION
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or (at
+ * your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
+ * NON INFRINGEMENT. See the GNU General Public License for more
+ * details.
+ */
+
+/*
+ * Unisys Virtual PCI driver header
+ */
+
+#ifndef __VIRTPCI_H__
+#define __VIRTPCI_H__
+
+#include "uisqueue.h"
+#include <linux/version.h>
+#include <linux/uuid.h>
+
+#define PCI_DEVICE_ID_VIRTHBA 0xAA00
+#define PCI_DEVICE_ID_VIRTNIC 0xAB00
+
+struct scsi_adap_info {
+ void *scsihost; /* scsi host if this device is a scsi hba */
+ struct vhba_wwnn wwnn; /* the world wide node name of vhba */
+ struct vhba_config_max max; /* various max specifications used
+ * to config vhba */
+};
+
+struct net_adap_info {
+ struct net_device *netdev; /* network device if this
+ * device is a NIC */
+ u8 mac_addr[MAX_MACADDR_LEN];
+ int num_rcv_bufs;
+ unsigned mtu;
+ uuid_le zone_uuid;
+};
+
+enum virtpci_dev_type {
+ VIRTHBA_TYPE = 0,
+ VIRTNIC_TYPE = 1,
+ VIRTBUS_TYPE = 6,
+};
+
+struct virtpci_dev {
+ enum virtpci_dev_type devtype; /* indicates type of the
+ * virtual pci device */
+ struct virtpci_driver *mydriver; /* which driver has allocated
+ * this device */
+ unsigned short vendor; /* vendor id for device */
+ unsigned short device; /* device id for device */
+ u32 bus_no; /* number of bus on which device exists */
+ u32 device_no; /* device's number on the bus */
+ struct irq_info intr; /* interrupt info */
+ struct device generic_dev; /* generic device */
+ union {
+ struct scsi_adap_info scsi;
+ struct net_adap_info net;
+ };
+
+ struct uisqueue_info queueinfo; /* holds ptr to channel where cmds &
+ * rsps are queued & retrieved */
+ struct virtpci_dev *next; /* points to next virtpci device */
+};
+
+struct virtpci_driver {
+ struct list_head node;
+ const char *name; /* the name of the driver in sysfs */
+ const char *version;
+ const char *vertag;
+ const struct pci_device_id *id_table; /* must be non-NULL for probe
+ * to be called */
+ int (*probe)(struct virtpci_dev *dev,
+ const struct pci_device_id *id); /* device inserted */
+ void (*remove)(struct virtpci_dev *dev); /* Device removed (NULL if
+ * not a hot-plug capable
+ * driver) */
+ int (*suspend)(struct virtpci_dev *dev,
+ u32 state); /* Device suspended */
+ int (*resume)(struct virtpci_dev *dev); /* Device woken up */
+ int (*enable_wake)(struct virtpci_dev *dev,
+ u32 state, int enable); /* Enable wake event */
+ struct device_driver core_driver; /* VIRTPCI core fills this in */
+};
+
+#define driver_to_virtpci_driver(in_drv) \
+ container_of(in_drv, struct virtpci_driver, core_driver)
+#define device_to_virtpci_dev(in_dev) \
+ container_of(in_dev, struct virtpci_dev, generic_dev)
+
+int virtpci_register_driver(struct virtpci_driver *);
+void virtpci_unregister_driver(struct virtpci_driver *);
+
+#endif /* __VIRTPCI_H__ */
diff --git a/drivers/staging/unisys/visorchannel/Kconfig b/drivers/staging/unisys/visorchannel/Kconfig
new file mode 100644
index 000000000..8d31bebf0
--- /dev/null
+++ b/drivers/staging/unisys/visorchannel/Kconfig
@@ -0,0 +1,10 @@
+#
+# Unisys visorchannel configuration
+#
+
+config UNISYS_VISORCHANNEL
+ tristate "Unisys visorchannel driver"
+ select UNISYS_VISORUTIL
+ ---help---
+ If you say Y here, you will enable the Unisys visorchannel driver.
+
diff --git a/drivers/staging/unisys/visorchannel/Makefile b/drivers/staging/unisys/visorchannel/Makefile
new file mode 100644
index 000000000..e079c96b1
--- /dev/null
+++ b/drivers/staging/unisys/visorchannel/Makefile
@@ -0,0 +1,12 @@
+#
+# Makefile for Unisys visorchannel
+#
+
+obj-$(CONFIG_UNISYS_VISORCHANNEL) += visorchannel.o
+
+visorchannel-y := visorchannel_main.o visorchannel_funcs.o
+
+ccflags-y += -Idrivers/staging/unisys/include
+ccflags-y += -Idrivers/staging/unisys/common-spar/include
+ccflags-y += -Idrivers/staging/unisys/common-spar/include/channels
+ccflags-y += -Idrivers/staging/unisys/visorutil
diff --git a/drivers/staging/unisys/visorchannel/globals.h b/drivers/staging/unisys/visorchannel/globals.h
new file mode 100644
index 000000000..0ed8e1d80
--- /dev/null
+++ b/drivers/staging/unisys/visorchannel/globals.h
@@ -0,0 +1,27 @@
+/* globals.h
+ *
+ * Copyright (C) 2010 - 2013 UNISYS CORPORATION
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or (at
+ * your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
+ * NON INFRINGEMENT. See the GNU General Public License for more
+ * details.
+ */
+
+#ifndef __VISORCHANNEL_GLOBALS_H__
+#define __VISORCHANNEL_GLOBALS_H__
+
+#include "timskmod.h"
+#include "memregion.h"
+#include "version.h"
+
+#define MYDRVNAME "visorchannel"
+
+#endif
diff --git a/drivers/staging/unisys/visorchannel/visorchannel.h b/drivers/staging/unisys/visorchannel/visorchannel.h
new file mode 100644
index 000000000..63f1b9760
--- /dev/null
+++ b/drivers/staging/unisys/visorchannel/visorchannel.h
@@ -0,0 +1,76 @@
+/* visorchannel.h
+ *
+ * Copyright (C) 2010 - 2013 UNISYS CORPORATION
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or (at
+ * your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
+ * NON INFRINGEMENT. See the GNU General Public License for more
+ * details.
+ */
+
+#ifndef __VISORCHANNEL_H__
+#define __VISORCHANNEL_H__
+
+#include <linux/uuid.h>
+
+#include "memregion.h"
+#include "channel.h"
+#ifndef HOSTADDRESS
+#define HOSTADDRESS u64
+#endif
+#ifndef BOOL
+#define BOOL int
+#endif
+
+/* Note that for visorchannel_create() and visorchannel_create_overlapped(),
+ * <channel_bytes> and <guid> arguments may be 0 if we are a channel CLIENT.
+ * In this case, the values can simply be read from the channel header.
+ */
+struct visorchannel *visorchannel_create(HOSTADDRESS physaddr,
+ ulong channel_bytes, uuid_le guid);
+struct visorchannel *visorchannel_create_overlapped(ulong channel_bytes,
+ struct visorchannel *parent,
+ ulong off, uuid_le guid);
+struct visorchannel *visorchannel_create_with_lock(HOSTADDRESS physaddr,
+ ulong channel_bytes,
+ uuid_le guid);
+struct visorchannel *visorchannel_create_overlapped_with_lock(
+ ulong channel_bytes,
+ struct visorchannel *parent,
+ ulong off, uuid_le guid);
+void visorchannel_destroy(struct visorchannel *channel);
+int visorchannel_read(struct visorchannel *channel, ulong offset,
+ void *local, ulong nbytes);
+int visorchannel_write(struct visorchannel *channel, ulong offset,
+ void *local, ulong nbytes);
+int visorchannel_clear(struct visorchannel *channel, ulong offset,
+ u8 ch, ulong nbytes);
+BOOL visorchannel_signalremove(struct visorchannel *channel, u32 queue,
+ void *msg);
+BOOL visorchannel_signalinsert(struct visorchannel *channel, u32 queue,
+ void *msg);
+int visorchannel_signalqueue_slots_avail(struct visorchannel *channel,
+ u32 queue);
+int visorchannel_signalqueue_max_slots(struct visorchannel *channel, u32 queue);
+HOSTADDRESS visorchannel_get_physaddr(struct visorchannel *channel);
+ulong visorchannel_get_nbytes(struct visorchannel *channel);
+char *visorchannel_id(struct visorchannel *channel, char *s);
+char *visorchannel_zoneid(struct visorchannel *channel, char *s);
+u64 visorchannel_get_clientpartition(struct visorchannel *channel);
+uuid_le visorchannel_get_uuid(struct visorchannel *channel);
+struct memregion *visorchannel_get_memregion(struct visorchannel *channel);
+char *visorchannel_uuid_id(uuid_le *guid, char *s);
+void visorchannel_debug(struct visorchannel *channel, int num_queues,
+ struct seq_file *seq, u32 off);
+void visorchannel_dump_section(struct visorchannel *chan, char *s,
+ int off, int len, struct seq_file *seq);
+void __iomem *visorchannel_get_header(struct visorchannel *channel);
+
+#endif
diff --git a/drivers/staging/unisys/visorchannel/visorchannel_funcs.c b/drivers/staging/unisys/visorchannel/visorchannel_funcs.c
new file mode 100644
index 000000000..7a9a7242f
--- /dev/null
+++ b/drivers/staging/unisys/visorchannel/visorchannel_funcs.c
@@ -0,0 +1,665 @@
+/* visorchannel_funcs.c
+ *
+ * Copyright (C) 2010 - 2013 UNISYS CORPORATION
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or (at
+ * your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
+ * NON INFRINGEMENT. See the GNU General Public License for more
+ * details.
+ */
+
+/*
+ * This provides Supervisor channel communication primitives, which are
+ * independent of the mechanism used to access the channel data. All channel
+ * data is accessed using the memregion abstraction. (memregion has both
+ * a CM2 implementation and a direct memory implementation.)
+ */
+
+#include "globals.h"
+#include "visorchannel.h"
+#include <linux/uuid.h>
+
+#define MYDRVNAME "visorchannel"
+
+struct visorchannel {
+ struct memregion *memregion; /* from visor_memregion_create() */
+ struct channel_header chan_hdr;
+ uuid_le guid;
+ ulong size;
+ BOOL needs_lock; /* channel creator knows if more than one
+ * thread will be inserting or removing */
+ spinlock_t insert_lock; /* protect head writes in chan_hdr */
+ spinlock_t remove_lock; /* protect tail writes in chan_hdr */
+
+ struct {
+ struct signal_queue_header req_queue;
+ struct signal_queue_header rsp_queue;
+ struct signal_queue_header event_queue;
+ struct signal_queue_header ack_queue;
+ } safe_uis_queue;
+};
+
+/* Creates the struct visorchannel abstraction for a data area in memory,
+ * but does NOT modify this data area.
+ */
+static struct visorchannel *
+visorchannel_create_guts(HOSTADDRESS physaddr, ulong channel_bytes,
+ struct visorchannel *parent, ulong off, uuid_le guid,
+ BOOL needs_lock)
+{
+ struct visorchannel *p = NULL;
+ void *rc = NULL;
+
+ p = kmalloc(sizeof(*p), GFP_KERNEL|__GFP_NORETRY);
+ if (!p) {
+ rc = NULL;
+ goto cleanup;
+ }
+ p->memregion = NULL;
+ p->needs_lock = needs_lock;
+ spin_lock_init(&p->insert_lock);
+ spin_lock_init(&p->remove_lock);
+
+ /* prepare chan_hdr (abstraction to read/write channel memory) */
+ if (!parent)
+ p->memregion =
+ visor_memregion_create(physaddr,
+ sizeof(struct channel_header));
+ else
+ p->memregion =
+ visor_memregion_create_overlapped(parent->memregion,
+ off, sizeof(struct channel_header));
+ if (!p->memregion) {
+ rc = NULL;
+ goto cleanup;
+ }
+ if (visor_memregion_read(p->memregion, 0, &p->chan_hdr,
+ sizeof(struct channel_header)) < 0) {
+ rc = NULL;
+ goto cleanup;
+ }
+ if (channel_bytes == 0)
+ /* we had better be a CLIENT of this channel */
+ channel_bytes = (ulong)p->chan_hdr.size;
+ if (uuid_le_cmp(guid, NULL_UUID_LE) == 0)
+ /* we had better be a CLIENT of this channel */
+ guid = p->chan_hdr.chtype;
+ if (visor_memregion_resize(p->memregion, channel_bytes) < 0) {
+ rc = NULL;
+ goto cleanup;
+ }
+ p->size = channel_bytes;
+ p->guid = guid;
+
+ rc = p;
+cleanup:
+
+ if (!rc) {
+ if (!p) {
+ visorchannel_destroy(p);
+ p = NULL;
+ }
+ }
+ return rc;
+}
+
+struct visorchannel *
+visorchannel_create(HOSTADDRESS physaddr, ulong channel_bytes, uuid_le guid)
+{
+ return visorchannel_create_guts(physaddr, channel_bytes, NULL, 0, guid,
+ FALSE);
+}
+EXPORT_SYMBOL_GPL(visorchannel_create);
+
+struct visorchannel *
+visorchannel_create_with_lock(HOSTADDRESS physaddr, ulong channel_bytes,
+ uuid_le guid)
+{
+ return visorchannel_create_guts(physaddr, channel_bytes, NULL, 0, guid,
+ TRUE);
+}
+EXPORT_SYMBOL_GPL(visorchannel_create_with_lock);
+
+struct visorchannel *
+visorchannel_create_overlapped(ulong channel_bytes,
+ struct visorchannel *parent, ulong off,
+ uuid_le guid)
+{
+ return visorchannel_create_guts(0, channel_bytes, parent, off, guid,
+ FALSE);
+}
+EXPORT_SYMBOL_GPL(visorchannel_create_overlapped);
+
+struct visorchannel *
+visorchannel_create_overlapped_with_lock(ulong channel_bytes,
+ struct visorchannel *parent, ulong off,
+ uuid_le guid)
+{
+ return visorchannel_create_guts(0, channel_bytes, parent, off, guid,
+ TRUE);
+}
+EXPORT_SYMBOL_GPL(visorchannel_create_overlapped_with_lock);
+
+void
+visorchannel_destroy(struct visorchannel *channel)
+{
+ if (!channel)
+ return;
+ if (channel->memregion) {
+ visor_memregion_destroy(channel->memregion);
+ channel->memregion = NULL;
+ }
+ kfree(channel);
+}
+EXPORT_SYMBOL_GPL(visorchannel_destroy);
+
+HOSTADDRESS
+visorchannel_get_physaddr(struct visorchannel *channel)
+{
+ return visor_memregion_get_physaddr(channel->memregion);
+}
+EXPORT_SYMBOL_GPL(visorchannel_get_physaddr);
+
+ulong
+visorchannel_get_nbytes(struct visorchannel *channel)
+{
+ return channel->size;
+}
+EXPORT_SYMBOL_GPL(visorchannel_get_nbytes);
+
+char *
+visorchannel_uuid_id(uuid_le *guid, char *s)
+{
+ sprintf(s, "%pUL", guid);
+ return s;
+}
+EXPORT_SYMBOL_GPL(visorchannel_uuid_id);
+
+char *
+visorchannel_id(struct visorchannel *channel, char *s)
+{
+ return visorchannel_uuid_id(&channel->guid, s);
+}
+EXPORT_SYMBOL_GPL(visorchannel_id);
+
+char *
+visorchannel_zoneid(struct visorchannel *channel, char *s)
+{
+ return visorchannel_uuid_id(&channel->chan_hdr.zone_uuid, s);
+}
+EXPORT_SYMBOL_GPL(visorchannel_zoneid);
+
+HOSTADDRESS
+visorchannel_get_clientpartition(struct visorchannel *channel)
+{
+ return channel->chan_hdr.partition_handle;
+}
+EXPORT_SYMBOL_GPL(visorchannel_get_clientpartition);
+
+uuid_le
+visorchannel_get_uuid(struct visorchannel *channel)
+{
+ return channel->guid;
+}
+EXPORT_SYMBOL_GPL(visorchannel_get_uuid);
+
+struct memregion *
+visorchannel_get_memregion(struct visorchannel *channel)
+{
+ return channel->memregion;
+}
+EXPORT_SYMBOL_GPL(visorchannel_get_memregion);
+
+int
+visorchannel_read(struct visorchannel *channel, ulong offset,
+ void *local, ulong nbytes)
+{
+ int rc = visor_memregion_read(channel->memregion, offset,
+ local, nbytes);
+ if ((rc >= 0) && (offset == 0) &&
+ (nbytes >= sizeof(struct channel_header))) {
+ memcpy(&channel->chan_hdr, local,
+ sizeof(struct channel_header));
+ }
+ return rc;
+}
+EXPORT_SYMBOL_GPL(visorchannel_read);
+
+int
+visorchannel_write(struct visorchannel *channel, ulong offset,
+ void *local, ulong nbytes)
+{
+ if (offset == 0 && nbytes >= sizeof(struct channel_header))
+ memcpy(&channel->chan_hdr, local,
+ sizeof(struct channel_header));
+ return visor_memregion_write(channel->memregion, offset, local, nbytes);
+}
+EXPORT_SYMBOL_GPL(visorchannel_write);
+
+int
+visorchannel_clear(struct visorchannel *channel, ulong offset, u8 ch,
+ ulong nbytes)
+{
+ int rc = -1;
+ int bufsize = 65536;
+ int written = 0;
+ u8 *buf = vmalloc(bufsize);
+
+ if (!buf)
+ goto cleanup;
+
+ memset(buf, ch, bufsize);
+ while (nbytes > 0) {
+ ulong thisbytes = bufsize;
+ int x = -1;
+
+ if (nbytes < thisbytes)
+ thisbytes = nbytes;
+ x = visor_memregion_write(channel->memregion, offset + written,
+ buf, thisbytes);
+ if (x < 0) {
+ rc = x;
+ goto cleanup;
+ }
+ written += thisbytes;
+ nbytes -= thisbytes;
+ }
+ rc = 0;
+
+cleanup:
+ if (buf) {
+ vfree(buf);
+ buf = NULL;
+ }
+ return rc;
+}
+EXPORT_SYMBOL_GPL(visorchannel_clear);
+
+void __iomem *
+visorchannel_get_header(struct visorchannel *channel)
+{
+ return (void __iomem *)&channel->chan_hdr;
+}
+EXPORT_SYMBOL_GPL(visorchannel_get_header);
+
+/** Return offset of a specific SIGNAL_QUEUE_HEADER from the beginning of a
+ * channel header
+ */
+#define SIG_QUEUE_OFFSET(chan_hdr, q) \
+ ((chan_hdr)->ch_space_offset + \
+ ((q) * sizeof(struct signal_queue_header)))
+
+/** Return offset of a specific queue entry (data) from the beginning of a
+ * channel header
+ */
+#define SIG_DATA_OFFSET(chan_hdr, q, sig_hdr, slot) \
+ (SIG_QUEUE_OFFSET(chan_hdr, q) + (sig_hdr)->sig_base_offset + \
+ ((slot) * (sig_hdr)->signal_size))
+
+/** Write the contents of a specific field within a SIGNAL_QUEUE_HEADER back
+ * into host memory
+ */
+#define SIG_WRITE_FIELD(channel, queue, sig_hdr, FIELD) \
+ (visor_memregion_write(channel->memregion, \
+ SIG_QUEUE_OFFSET(&channel->chan_hdr, queue)+ \
+ offsetof(struct signal_queue_header, FIELD),\
+ &((sig_hdr)->FIELD), \
+ sizeof((sig_hdr)->FIELD)) >= 0)
+
+static BOOL
+sig_read_header(struct visorchannel *channel, u32 queue,
+ struct signal_queue_header *sig_hdr)
+{
+ BOOL rc = FALSE;
+
+ if (channel->chan_hdr.ch_space_offset < sizeof(struct channel_header))
+ goto cleanup;
+
+ /* Read the appropriate SIGNAL_QUEUE_HEADER into local memory. */
+
+ if (visor_memregion_read(channel->memregion,
+ SIG_QUEUE_OFFSET(&channel->chan_hdr, queue),
+ sig_hdr,
+ sizeof(struct signal_queue_header)) < 0) {
+ goto cleanup;
+ }
+ rc = TRUE;
+cleanup:
+ return rc;
+}
+
+static BOOL
+sig_do_data(struct visorchannel *channel, u32 queue,
+ struct signal_queue_header *sig_hdr, u32 slot, void *data,
+ BOOL is_write)
+{
+ BOOL rc = FALSE;
+ int signal_data_offset = SIG_DATA_OFFSET(&channel->chan_hdr, queue,
+ sig_hdr, slot);
+ if (is_write) {
+ if (visor_memregion_write(channel->memregion,
+ signal_data_offset,
+ data, sig_hdr->signal_size) < 0) {
+ goto cleanup;
+ }
+ } else {
+ if (visor_memregion_read(channel->memregion, signal_data_offset,
+ data, sig_hdr->signal_size) < 0) {
+ goto cleanup;
+ }
+ }
+ rc = TRUE;
+cleanup:
+ return rc;
+}
+
+static inline BOOL
+sig_read_data(struct visorchannel *channel, u32 queue,
+ struct signal_queue_header *sig_hdr, u32 slot, void *data)
+{
+ return sig_do_data(channel, queue, sig_hdr, slot, data, FALSE);
+}
+
+static inline BOOL
+sig_write_data(struct visorchannel *channel, u32 queue,
+ struct signal_queue_header *sig_hdr, u32 slot, void *data)
+{
+ return sig_do_data(channel, queue, sig_hdr, slot, data, TRUE);
+}
+
+static inline unsigned char
+safe_sig_queue_validate(struct signal_queue_header *psafe_sqh,
+ struct signal_queue_header *punsafe_sqh,
+ u32 *phead, u32 *ptail)
+{
+ if ((*phead >= psafe_sqh->max_slots) ||
+ (*ptail >= psafe_sqh->max_slots)) {
+ /* Choose 0 or max, maybe based on current tail value */
+ *phead = 0;
+ *ptail = 0;
+
+ /* Sync with client as necessary */
+ punsafe_sqh->head = *phead;
+ punsafe_sqh->tail = *ptail;
+
+ return 0;
+ }
+ return 1;
+} /* end safe_sig_queue_validate */
+
+static BOOL
+signalremove_inner(struct visorchannel *channel, u32 queue, void *msg)
+{
+ struct signal_queue_header sig_hdr;
+
+ if (!sig_read_header(channel, queue, &sig_hdr))
+ return FALSE;
+ if (sig_hdr.head == sig_hdr.tail)
+ return FALSE; /* no signals to remove */
+
+ sig_hdr.tail = (sig_hdr.tail + 1) % sig_hdr.max_slots;
+ if (!sig_read_data(channel, queue, &sig_hdr, sig_hdr.tail, msg)) {
+ return FALSE;
+ }
+ sig_hdr.num_received++;
+
+ /* For each data field in SIGNAL_QUEUE_HEADER that was modified,
+ * update host memory.
+ */
+ mb(); /* required for channel synch */
+ if (!SIG_WRITE_FIELD(channel, queue, &sig_hdr, tail))
+ return FALSE;
+ if (!SIG_WRITE_FIELD(channel, queue, &sig_hdr, num_received))
+ return FALSE;
+ return TRUE;
+}
+
+BOOL
+visorchannel_signalremove(struct visorchannel *channel, u32 queue, void *msg)
+{
+ BOOL rc;
+
+ if (channel->needs_lock) {
+ spin_lock(&channel->remove_lock);
+ rc = signalremove_inner(channel, queue, msg);
+ spin_unlock(&channel->remove_lock);
+ } else {
+ rc = signalremove_inner(channel, queue, msg);
+ }
+
+ return rc;
+}
+EXPORT_SYMBOL_GPL(visorchannel_signalremove);
+
+static BOOL
+signalinsert_inner(struct visorchannel *channel, u32 queue, void *msg)
+{
+ struct signal_queue_header sig_hdr;
+
+ if (!sig_read_header(channel, queue, &sig_hdr))
+ return FALSE;
+
+ sig_hdr.head = ((sig_hdr.head + 1) % sig_hdr.max_slots);
+ if (sig_hdr.head == sig_hdr.tail) {
+ sig_hdr.num_overflows++;
+ visor_memregion_write(channel->memregion,
+ SIG_QUEUE_OFFSET(&channel->chan_hdr,
+ queue) +
+ offsetof(struct signal_queue_header,
+ num_overflows),
+ &(sig_hdr.num_overflows),
+ sizeof(sig_hdr.num_overflows));
+ return FALSE;
+ }
+
+ if (!sig_write_data(channel, queue, &sig_hdr, sig_hdr.head, msg))
+ return FALSE;
+
+ sig_hdr.num_sent++;
+
+ /* For each data field in SIGNAL_QUEUE_HEADER that was modified,
+ * update host memory.
+ */
+ mb(); /* required for channel synch */
+ if (!SIG_WRITE_FIELD(channel, queue, &sig_hdr, head))
+ return FALSE;
+ if (!SIG_WRITE_FIELD(channel, queue, &sig_hdr, num_sent)) {
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+BOOL
+visorchannel_signalinsert(struct visorchannel *channel, u32 queue, void *msg)
+{
+ BOOL rc;
+
+ if (channel->needs_lock) {
+ spin_lock(&channel->insert_lock);
+ rc = signalinsert_inner(channel, queue, msg);
+ spin_unlock(&channel->insert_lock);
+ } else {
+ rc = signalinsert_inner(channel, queue, msg);
+ }
+
+ return rc;
+}
+EXPORT_SYMBOL_GPL(visorchannel_signalinsert);
+
+int
+visorchannel_signalqueue_slots_avail(struct visorchannel *channel, u32 queue)
+{
+ struct signal_queue_header sig_hdr;
+ u32 slots_avail, slots_used;
+ u32 head, tail;
+
+ if (!sig_read_header(channel, queue, &sig_hdr))
+ return 0;
+ head = sig_hdr.head;
+ tail = sig_hdr.tail;
+ if (head < tail)
+ head = head + sig_hdr.max_slots;
+ slots_used = (head - tail);
+ slots_avail = sig_hdr.max_signals - slots_used;
+ return (int)slots_avail;
+}
+EXPORT_SYMBOL_GPL(visorchannel_signalqueue_slots_avail);
+
+int
+visorchannel_signalqueue_max_slots(struct visorchannel *channel, u32 queue)
+{
+ struct signal_queue_header sig_hdr;
+
+ if (!sig_read_header(channel, queue, &sig_hdr))
+ return 0;
+ return (int)sig_hdr.max_signals;
+}
+EXPORT_SYMBOL_GPL(visorchannel_signalqueue_max_slots);
+
+static void
+sigqueue_debug(struct signal_queue_header *q, int which, struct seq_file *seq)
+{
+ seq_printf(seq, "Signal Queue #%d\n", which);
+ seq_printf(seq, " VersionId = %lu\n", (ulong)q->version);
+ seq_printf(seq, " Type = %lu\n", (ulong)q->chtype);
+ seq_printf(seq, " oSignalBase = %llu\n",
+ (long long)q->sig_base_offset);
+ seq_printf(seq, " SignalSize = %lu\n", (ulong)q->signal_size);
+ seq_printf(seq, " MaxSignalSlots = %lu\n",
+ (ulong)q->max_slots);
+ seq_printf(seq, " MaxSignals = %lu\n", (ulong)q->max_signals);
+ seq_printf(seq, " FeatureFlags = %-16.16Lx\n",
+ (long long)q->features);
+ seq_printf(seq, " NumSignalsSent = %llu\n",
+ (long long)q->num_sent);
+ seq_printf(seq, " NumSignalsReceived = %llu\n",
+ (long long)q->num_received);
+ seq_printf(seq, " NumOverflows = %llu\n",
+ (long long)q->num_overflows);
+ seq_printf(seq, " Head = %lu\n", (ulong)q->head);
+ seq_printf(seq, " Tail = %lu\n", (ulong)q->tail);
+}
+
+void
+visorchannel_debug(struct visorchannel *channel, int num_queues,
+ struct seq_file *seq, u32 off)
+{
+ HOSTADDRESS addr = 0;
+ ulong nbytes = 0, nbytes_region = 0;
+ struct memregion *memregion = NULL;
+ struct channel_header hdr;
+ struct channel_header *phdr = &hdr;
+ int i = 0;
+ int errcode = 0;
+
+ if (!channel)
+ return;
+ memregion = channel->memregion;
+ if (!memregion)
+ return;
+
+ addr = visor_memregion_get_physaddr(memregion);
+ nbytes_region = visor_memregion_get_nbytes(memregion);
+ errcode = visorchannel_read(channel, off,
+ phdr, sizeof(struct channel_header));
+ if (errcode < 0) {
+ seq_printf(seq,
+ "Read of channel header failed with errcode=%d)\n",
+ errcode);
+ if (off == 0) {
+ phdr = &channel->chan_hdr;
+ seq_puts(seq, "(following data may be stale)\n");
+ } else {
+ return;
+ }
+ }
+ nbytes = (ulong)(phdr->size);
+ seq_printf(seq, "--- Begin channel @0x%-16.16Lx for 0x%lx bytes (region=0x%lx bytes) ---\n",
+ addr + off, nbytes, nbytes_region);
+ seq_printf(seq, "Type = %pUL\n", &phdr->chtype);
+ seq_printf(seq, "ZoneGuid = %pUL\n", &phdr->zone_uuid);
+ seq_printf(seq, "Signature = 0x%-16.16Lx\n",
+ (long long)phdr->signature);
+ seq_printf(seq, "LegacyState = %lu\n", (ulong)phdr->legacy_state);
+ seq_printf(seq, "SrvState = %lu\n", (ulong)phdr->srv_state);
+ seq_printf(seq, "CliStateBoot = %lu\n", (ulong)phdr->cli_state_boot);
+ seq_printf(seq, "CliStateOS = %lu\n", (ulong)phdr->cli_state_os);
+ seq_printf(seq, "HeaderSize = %lu\n", (ulong)phdr->header_size);
+ seq_printf(seq, "Size = %llu\n", (long long)phdr->size);
+ seq_printf(seq, "Features = 0x%-16.16llx\n",
+ (long long)phdr->features);
+ seq_printf(seq, "PartitionHandle = 0x%-16.16llx\n",
+ (long long)phdr->partition_handle);
+ seq_printf(seq, "Handle = 0x%-16.16llx\n",
+ (long long)phdr->handle);
+ seq_printf(seq, "VersionId = %lu\n", (ulong)phdr->version_id);
+ seq_printf(seq, "oChannelSpace = %llu\n",
+ (long long)phdr->ch_space_offset);
+ if ((phdr->ch_space_offset == 0) || (errcode < 0))
+ ;
+ else
+ for (i = 0; i < num_queues; i++) {
+ struct signal_queue_header q;
+
+ errcode = visorchannel_read(channel,
+ off +
+ phdr->ch_space_offset +
+ (i * sizeof(q)),
+ &q, sizeof(q));
+ if (errcode < 0) {
+ seq_printf(seq,
+ "failed to read signal queue #%d from channel @0x%-16.16Lx errcode=%d\n",
+ i, addr, errcode);
+ continue;
+ }
+ sigqueue_debug(&q, i, seq);
+ }
+ seq_printf(seq, "--- End channel @0x%-16.16Lx for 0x%lx bytes ---\n",
+ addr + off, nbytes);
+}
+EXPORT_SYMBOL_GPL(visorchannel_debug);
+
+void
+visorchannel_dump_section(struct visorchannel *chan, char *s,
+ int off, int len, struct seq_file *seq)
+{
+ char *buf, *tbuf, *fmtbuf;
+ int fmtbufsize = 0;
+ int i;
+ int errcode = 0;
+
+ fmtbufsize = 100 * COVQ(len, 16);
+ buf = kmalloc(len, GFP_KERNEL|__GFP_NORETRY);
+ if (!buf)
+ return;
+ fmtbuf = kmalloc(fmtbufsize, GFP_KERNEL|__GFP_NORETRY);
+ if (!fmtbuf)
+ goto fmt_failed;
+
+ errcode = visorchannel_read(chan, off, buf, len);
+ if (errcode < 0)
+ goto read_failed;
+ seq_printf(seq, "channel %s:\n", s);
+ tbuf = buf;
+ while (len > 0) {
+ i = (len < 16) ? len : 16;
+ hex_dump_to_buffer(tbuf, i, 16, 1, fmtbuf, fmtbufsize, TRUE);
+ seq_printf(seq, "%s\n", fmtbuf);
+ tbuf += 16;
+ len -= 16;
+ }
+
+read_failed:
+ kfree(fmtbuf);
+fmt_failed:
+ kfree(buf);
+}
+EXPORT_SYMBOL_GPL(visorchannel_dump_section);
diff --git a/drivers/staging/unisys/visorchannel/visorchannel_main.c b/drivers/staging/unisys/visorchannel/visorchannel_main.c
new file mode 100644
index 000000000..787d4774b
--- /dev/null
+++ b/drivers/staging/unisys/visorchannel/visorchannel_main.c
@@ -0,0 +1,50 @@
+/* visorchannel_main.c
+ *
+ * Copyright (C) 2010 - 2013 UNISYS CORPORATION
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or (at
+ * your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
+ * NON INFRINGEMENT. See the GNU General Public License for more
+ * details.
+ */
+
+/*
+ * This is a module "wrapper" around visorchannel_funcs.
+ */
+
+#include "globals.h"
+#include "channel.h"
+#include "visorchannel.h"
+#include <linux/uuid.h>
+
+#define MYDRVNAME "visorchannel"
+
+static int __init
+visorchannel_init(void)
+{
+ if (!unisys_spar_platform)
+ return -ENODEV;
+
+ return 0;
+}
+
+static void
+visorchannel_exit(void)
+{
+}
+
+module_init(visorchannel_init);
+module_exit(visorchannel_exit);
+
+MODULE_AUTHOR("Unisys");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Supervisor channel driver for service partition: ver "
+ VERSION);
+MODULE_VERSION(VERSION);
diff --git a/drivers/staging/unisys/visorchipset/Kconfig b/drivers/staging/unisys/visorchipset/Kconfig
new file mode 100644
index 000000000..b03bfc5c3
--- /dev/null
+++ b/drivers/staging/unisys/visorchipset/Kconfig
@@ -0,0 +1,11 @@
+#
+# Unisys visorchipset configuration
+#
+
+config UNISYS_VISORCHIPSET
+ tristate "Unisys visorchipset driver"
+ select UNISYS_VISORUTIL
+ select UNISYS_VISORCHANNEL
+ ---help---
+ If you say Y here, you will enable the Unisys visorchipset driver.
+
diff --git a/drivers/staging/unisys/visorchipset/Makefile b/drivers/staging/unisys/visorchipset/Makefile
new file mode 100644
index 000000000..12686906b
--- /dev/null
+++ b/drivers/staging/unisys/visorchipset/Makefile
@@ -0,0 +1,15 @@
+#
+# Makefile for Unisys visorchipset
+#
+
+obj-$(CONFIG_UNISYS_VISORCHIPSET) += visorchipset.o
+
+visorchipset-y := visorchipset_main.o file.o parser.o
+
+ccflags-y += -Idrivers/staging/unisys/include
+ccflags-y += -Idrivers/staging/unisys/uislib
+ccflags-y += -Idrivers/staging/unisys/visorchannel
+ccflags-y += -Idrivers/staging/unisys/common-spar/include
+ccflags-y += -Idrivers/staging/unisys/common-spar/include/channels
+ccflags-y += -Idrivers/staging/unisys/visorutil
+ccflags-y += -Iinclude/generated
diff --git a/drivers/staging/unisys/visorchipset/file.c b/drivers/staging/unisys/visorchipset/file.c
new file mode 100644
index 000000000..203de0b5f
--- /dev/null
+++ b/drivers/staging/unisys/visorchipset/file.c
@@ -0,0 +1,160 @@
+/* file.c
+ *
+ * Copyright (C) 2010 - 2013 UNISYS CORPORATION
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or (at
+ * your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
+ * NON INFRINGEMENT. See the GNU General Public License for more
+ * details.
+ */
+
+/* This contains the implementation that allows a usermode program to
+ * communicate with the visorchipset driver using a device/file interface.
+ */
+
+#include "globals.h"
+#include "visorchannel.h"
+#include <linux/mm.h>
+#include <linux/fs.h>
+#include "uisutils.h"
+#include "file.h"
+
+#define CURRENT_FILE_PC VISOR_CHIPSET_PC_file_c
+
+static struct cdev file_cdev;
+static struct visorchannel **file_controlvm_channel;
+
+void
+visorchipset_file_cleanup(dev_t major_dev)
+{
+ if (file_cdev.ops != NULL)
+ cdev_del(&file_cdev);
+ file_cdev.ops = NULL;
+ unregister_chrdev_region(major_dev, 1);
+}
+
+static int
+visorchipset_open(struct inode *inode, struct file *file)
+{
+ unsigned minor_number = iminor(inode);
+
+ if (minor_number != 0)
+ return -ENODEV;
+ file->private_data = NULL;
+ return 0;
+}
+
+static int
+visorchipset_release(struct inode *inode, struct file *file)
+{
+ return 0;
+}
+
+static int
+visorchipset_mmap(struct file *file, struct vm_area_struct *vma)
+{
+ ulong physaddr = 0;
+ ulong offset = vma->vm_pgoff << PAGE_SHIFT;
+ GUEST_PHYSICAL_ADDRESS addr = 0;
+
+ /* sv_enable_dfp(); */
+ if (offset & (PAGE_SIZE - 1))
+ return -ENXIO; /* need aligned offsets */
+
+ switch (offset) {
+ case VISORCHIPSET_MMAP_CONTROLCHANOFFSET:
+ vma->vm_flags |= VM_IO;
+ if (*file_controlvm_channel == NULL) {
+ return -ENXIO;
+ }
+ visorchannel_read(*file_controlvm_channel,
+ offsetof(struct spar_controlvm_channel_protocol,
+ gp_control_channel),
+ &addr, sizeof(addr));
+ if (addr == 0) {
+ return -ENXIO;
+ }
+ physaddr = (ulong)addr;
+ if (remap_pfn_range(vma, vma->vm_start,
+ physaddr >> PAGE_SHIFT,
+ vma->vm_end - vma->vm_start,
+ /*pgprot_noncached */
+ (vma->vm_page_prot))) {
+ return -EAGAIN;
+ }
+ break;
+ default:
+ return -ENOSYS;
+ }
+ return 0;
+}
+
+static long visorchipset_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ s64 adjustment;
+ s64 vrtc_offset;
+
+ switch (cmd) {
+ case VMCALL_QUERY_GUEST_VIRTUAL_TIME_OFFSET:
+ /* get the physical rtc offset */
+ vrtc_offset = issue_vmcall_query_guest_virtual_time_offset();
+ if (copy_to_user
+ ((void __user *)arg, &vrtc_offset, sizeof(vrtc_offset))) {
+ return -EFAULT;
+ }
+ return SUCCESS;
+ case VMCALL_UPDATE_PHYSICAL_TIME:
+ if (copy_from_user
+ (&adjustment, (void __user *)arg, sizeof(adjustment))) {
+ return -EFAULT;
+ }
+ return issue_vmcall_update_physical_time(adjustment);
+ default:
+ return -EFAULT;
+ }
+}
+
+static const struct file_operations visorchipset_fops = {
+ .owner = THIS_MODULE,
+ .open = visorchipset_open,
+ .read = NULL,
+ .write = NULL,
+ .unlocked_ioctl = visorchipset_ioctl,
+ .release = visorchipset_release,
+ .mmap = visorchipset_mmap,
+};
+
+int
+visorchipset_file_init(dev_t major_dev, struct visorchannel **controlvm_channel)
+{
+ int rc = 0;
+
+ file_controlvm_channel = controlvm_channel;
+ cdev_init(&file_cdev, &visorchipset_fops);
+ file_cdev.owner = THIS_MODULE;
+ if (MAJOR(major_dev) == 0) {
+ rc = alloc_chrdev_region(&major_dev, 0, 1, MYDRVNAME);
+ /* dynamic major device number registration required */
+ if (rc < 0)
+ return rc;
+ } else {
+ /* static major device number registration required */
+ rc = register_chrdev_region(major_dev, 1, MYDRVNAME);
+ if (rc < 0)
+ return rc;
+ }
+ rc = cdev_add(&file_cdev, MKDEV(MAJOR(major_dev), 0), 1);
+ if (rc < 0) {
+ unregister_chrdev_region(major_dev, 1);
+ return rc;
+ }
+ return 0;
+}
diff --git a/drivers/staging/unisys/visorchipset/file.h b/drivers/staging/unisys/visorchipset/file.h
new file mode 100644
index 000000000..51f7699b7
--- /dev/null
+++ b/drivers/staging/unisys/visorchipset/file.h
@@ -0,0 +1,27 @@
+/* file.h
+ *
+ * Copyright (C) 2010 - 2013 UNISYS CORPORATION
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or (at
+ * your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
+ * NON INFRINGEMENT. See the GNU General Public License for more
+ * details.
+ */
+
+#ifndef __FILE_H__
+#define __FILE_H__
+
+#include "globals.h"
+
+int visorchipset_file_init(dev_t majorDev,
+ struct visorchannel **pControlVm_channel);
+void visorchipset_file_cleanup(dev_t major_dev);
+
+#endif
diff --git a/drivers/staging/unisys/visorchipset/globals.h b/drivers/staging/unisys/visorchipset/globals.h
new file mode 100644
index 000000000..f76e498a3
--- /dev/null
+++ b/drivers/staging/unisys/visorchipset/globals.h
@@ -0,0 +1,42 @@
+/* globals.h
+ *
+ * Copyright (C) 2010 - 2013 UNISYS CORPORATION
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or (at
+ * your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
+ * NON INFRINGEMENT. See the GNU General Public License for more
+ * details.
+ */
+
+#ifndef __VISORCHIPSET_GLOBALS_H__
+#define __VISORCHIPSET_GLOBALS_H__
+
+#include "diagnostics/appos_subsystems.h"
+#include "timskmod.h"
+#include "visorchipset.h"
+#include "visorchipset_umode.h"
+#include "version.h"
+
+#define MYDRVNAME "visorchipset"
+
+/* module parameters */
+
+extern int visorchipset_testvnic;
+extern int visorchipset_testvnicclient;
+extern int visorchipset_testmsg;
+extern int visorchipset_major;
+extern int visorchipset_serverregwait;
+extern int visorchipset_clientregwait;
+extern int visorchipset_testteardown;
+extern int visorchipset_disable_controlvm;
+extern int visorchipset_crash_kernel;
+extern int visorchipset_holdchipsetready;
+
+#endif
diff --git a/drivers/staging/unisys/visorchipset/parser.c b/drivers/staging/unisys/visorchipset/parser.c
new file mode 100644
index 000000000..d8a2d6f5a
--- /dev/null
+++ b/drivers/staging/unisys/visorchipset/parser.c
@@ -0,0 +1,430 @@
+/* parser.c
+ *
+ * Copyright (C) 2010 - 2013 UNISYS CORPORATION
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or (at
+ * your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
+ * NON INFRINGEMENT. See the GNU General Public License for more
+ * details.
+ */
+
+#include "parser.h"
+#include "memregion.h"
+#include "controlvmchannel.h"
+#include <linux/ctype.h>
+#include <linux/mm.h>
+#include <linux/uuid.h>
+
+#define MYDRVNAME "visorchipset_parser"
+#define CURRENT_FILE_PC VISOR_CHIPSET_PC_parser_c
+
+/* We will refuse to allocate more than this many bytes to copy data from
+ * incoming payloads. This serves as a throttling mechanism.
+ */
+#define MAX_CONTROLVM_PAYLOAD_BYTES (1024*128)
+static ulong controlvm_payload_bytes_buffered;
+
+struct parser_context {
+ ulong allocbytes;
+ ulong param_bytes;
+ u8 *curr;
+ ulong bytes_remaining;
+ BOOL byte_stream;
+ char data[0];
+};
+
+static struct parser_context *
+parser_init_guts(u64 addr, u32 bytes, BOOL local,
+ BOOL standard_payload_header, BOOL *retry)
+{
+ int allocbytes = sizeof(struct parser_context) + bytes;
+ struct parser_context *rc = NULL;
+ struct parser_context *ctx = NULL;
+ struct memregion *rgn = NULL;
+ struct spar_controlvm_parameters_header *phdr = NULL;
+
+ if (retry)
+ *retry = FALSE;
+ if (!standard_payload_header)
+ /* alloc and 0 extra byte to ensure payload is
+ * '\0'-terminated
+ */
+ allocbytes++;
+ if ((controlvm_payload_bytes_buffered + bytes)
+ > MAX_CONTROLVM_PAYLOAD_BYTES) {
+ if (retry)
+ *retry = TRUE;
+ rc = NULL;
+ goto cleanup;
+ }
+ ctx = kzalloc(allocbytes, GFP_KERNEL|__GFP_NORETRY);
+ if (!ctx) {
+ if (retry)
+ *retry = TRUE;
+ rc = NULL;
+ goto cleanup;
+ }
+
+ ctx->allocbytes = allocbytes;
+ ctx->param_bytes = bytes;
+ ctx->curr = NULL;
+ ctx->bytes_remaining = 0;
+ ctx->byte_stream = FALSE;
+ if (local) {
+ void *p;
+
+ if (addr > virt_to_phys(high_memory - 1)) {
+ rc = NULL;
+ goto cleanup;
+ }
+ p = __va((ulong) (addr));
+ memcpy(ctx->data, p, bytes);
+ } else {
+ rgn = visor_memregion_create(addr, bytes);
+ if (!rgn) {
+ rc = NULL;
+ goto cleanup;
+ }
+ if (visor_memregion_read(rgn, 0, ctx->data, bytes) < 0) {
+ rc = NULL;
+ goto cleanup;
+ }
+ }
+ if (!standard_payload_header) {
+ ctx->byte_stream = TRUE;
+ rc = ctx;
+ goto cleanup;
+ }
+ phdr = (struct spar_controlvm_parameters_header *)(ctx->data);
+ if (phdr->total_length != bytes) {
+ rc = NULL;
+ goto cleanup;
+ }
+ if (phdr->total_length < phdr->header_length) {
+ rc = NULL;
+ goto cleanup;
+ }
+ if (phdr->header_length <
+ sizeof(struct spar_controlvm_parameters_header)) {
+ rc = NULL;
+ goto cleanup;
+ }
+
+ rc = ctx;
+cleanup:
+ if (rgn) {
+ visor_memregion_destroy(rgn);
+ rgn = NULL;
+ }
+ if (rc) {
+ controlvm_payload_bytes_buffered += ctx->param_bytes;
+ } else {
+ if (ctx) {
+ parser_done(ctx);
+ ctx = NULL;
+ }
+ }
+ return rc;
+}
+
+struct parser_context *
+parser_init(u64 addr, u32 bytes, BOOL local, BOOL *retry)
+{
+ return parser_init_guts(addr, bytes, local, TRUE, retry);
+}
+
+/* Call this instead of parser_init() if the payload area consists of just
+ * a sequence of bytes, rather than a struct spar_controlvm_parameters_header
+ * structures. Afterwards, you can call parser_simpleString_get() or
+ * parser_byteStream_get() to obtain the data.
+ */
+struct parser_context *
+parser_init_byte_stream(u64 addr, u32 bytes, BOOL local, BOOL *retry)
+{
+ return parser_init_guts(addr, bytes, local, FALSE, retry);
+}
+
+/* Obtain '\0'-terminated copy of string in payload area.
+ */
+char *
+parser_simpleString_get(struct parser_context *ctx)
+{
+ if (!ctx->byte_stream)
+ return NULL;
+ return ctx->data; /* note this IS '\0'-terminated, because of
+ * the num of bytes we alloc+clear in
+ * parser_init_byteStream() */
+}
+
+/* Obtain a copy of the buffer in the payload area.
+ */
+void *parser_byte_stream_get(struct parser_context *ctx, ulong *nbytes)
+{
+ if (!ctx->byte_stream)
+ return NULL;
+ if (nbytes)
+ *nbytes = ctx->param_bytes;
+ return (void *)ctx->data;
+}
+
+uuid_le
+parser_id_get(struct parser_context *ctx)
+{
+ struct spar_controlvm_parameters_header *phdr = NULL;
+
+ if (ctx == NULL)
+ return NULL_UUID_LE;
+ phdr = (struct spar_controlvm_parameters_header *)(ctx->data);
+ return phdr->id;
+}
+
+void
+parser_param_start(struct parser_context *ctx, PARSER_WHICH_STRING which_string)
+{
+ struct spar_controlvm_parameters_header *phdr = NULL;
+
+ if (ctx == NULL)
+ goto Away;
+ phdr = (struct spar_controlvm_parameters_header *)(ctx->data);
+ switch (which_string) {
+ case PARSERSTRING_INITIATOR:
+ ctx->curr = ctx->data + phdr->initiator_offset;
+ ctx->bytes_remaining = phdr->initiator_length;
+ break;
+ case PARSERSTRING_TARGET:
+ ctx->curr = ctx->data + phdr->target_offset;
+ ctx->bytes_remaining = phdr->target_length;
+ break;
+ case PARSERSTRING_CONNECTION:
+ ctx->curr = ctx->data + phdr->connection_offset;
+ ctx->bytes_remaining = phdr->connection_length;
+ break;
+ case PARSERSTRING_NAME:
+ ctx->curr = ctx->data + phdr->name_offset;
+ ctx->bytes_remaining = phdr->name_length;
+ break;
+ default:
+ break;
+ }
+
+Away:
+ return;
+}
+
+void
+parser_done(struct parser_context *ctx)
+{
+ if (!ctx)
+ return;
+ controlvm_payload_bytes_buffered -= ctx->param_bytes;
+ kfree(ctx);
+}
+
+/** Return length of string not counting trailing spaces. */
+static int
+string_length_no_trail(char *s, int len)
+{
+ int i = len - 1;
+
+ while (i >= 0) {
+ if (!isspace(s[i]))
+ return i + 1;
+ i--;
+ }
+ return 0;
+}
+
+/** Grab the next name and value out of the parameter buffer.
+ * The entire parameter buffer looks like this:
+ * <name>=<value>\0
+ * <name>=<value>\0
+ * ...
+ * \0
+ * If successful, the next <name> value is returned within the supplied
+ * <nam> buffer (the value is always upper-cased), and the corresponding
+ * <value> is returned within a kmalloc()ed buffer, whose pointer is
+ * provided as the return value of this function.
+ * (The total number of bytes allocated is strlen(<value>)+1.)
+ *
+ * NULL is returned to indicate failure, which can occur for several reasons:
+ * - all <name>=<value> pairs have already been processed
+ * - bad parameter
+ * - parameter buffer ends prematurely (couldn't find an '=' or '\0' within
+ * the confines of the parameter buffer)
+ * - the <nam> buffer is not large enough to hold the <name> of the next
+ * parameter
+ */
+void *
+parser_param_get(struct parser_context *ctx, char *nam, int namesize)
+{
+ u8 *pscan, *pnam = nam;
+ ulong nscan;
+ int value_length = -1, orig_value_length = -1;
+ void *value = NULL;
+ int i;
+ int closing_quote = 0;
+
+ if (!ctx)
+ return NULL;
+ pscan = ctx->curr;
+ nscan = ctx->bytes_remaining;
+ if (nscan == 0)
+ return NULL;
+ if (*pscan == '\0')
+ /* This is the normal return point after you have processed
+ * all of the <name>=<value> pairs in a syntactically-valid
+ * parameter buffer.
+ */
+ return NULL;
+
+ /* skip whitespace */
+ while (isspace(*pscan)) {
+ pscan++;
+ nscan--;
+ if (nscan == 0)
+ return NULL;
+ }
+
+ while (*pscan != ':') {
+ if (namesize <= 0)
+ return NULL;
+ *pnam = toupper(*pscan);
+ pnam++;
+ namesize--;
+ pscan++;
+ nscan--;
+ if (nscan == 0)
+ return NULL;
+ }
+ if (namesize <= 0)
+ return NULL;
+ *pnam = '\0';
+ nam[string_length_no_trail(nam, strlen(nam))] = '\0';
+
+ /* point to char immediately after ":" in "<name>:<value>" */
+ pscan++;
+ nscan--;
+ /* skip whitespace */
+ while (isspace(*pscan)) {
+ pscan++;
+ nscan--;
+ if (nscan == 0)
+ return NULL;
+ }
+ if (nscan == 0)
+ return NULL;
+ if (*pscan == '\'' || *pscan == '"') {
+ closing_quote = *pscan;
+ pscan++;
+ nscan--;
+ if (nscan == 0)
+ return NULL;
+ }
+
+ /* look for a separator character, terminator character, or
+ * end of data
+ */
+ for (i = 0, value_length = -1; i < nscan; i++) {
+ if (closing_quote) {
+ if (pscan[i] == '\0')
+ return NULL;
+ if (pscan[i] == closing_quote) {
+ value_length = i;
+ break;
+ }
+ } else
+ if (pscan[i] == ',' || pscan[i] == ';'
+ || pscan[i] == '\0') {
+ value_length = i;
+ break;
+ }
+ }
+ if (value_length < 0) {
+ if (closing_quote)
+ return NULL;
+ value_length = nscan;
+ }
+ orig_value_length = value_length;
+ if (closing_quote == 0)
+ value_length = string_length_no_trail(pscan, orig_value_length);
+ value = kmalloc(value_length + 1, GFP_KERNEL|__GFP_NORETRY);
+ if (value == NULL)
+ return NULL;
+ memcpy(value, pscan, value_length);
+ ((u8 *) (value))[value_length] = '\0';
+
+ pscan += orig_value_length;
+ nscan -= orig_value_length;
+
+ /* skip past separator or closing quote */
+ if (nscan > 0) {
+ if (*pscan != '\0') {
+ pscan++;
+ nscan--;
+ }
+ }
+
+ if (closing_quote && (nscan > 0)) {
+ /* we still need to skip around the real separator if present */
+ /* first, skip whitespace */
+ while (isspace(*pscan)) {
+ pscan++;
+ nscan--;
+ if (nscan == 0)
+ break;
+ }
+ if (nscan > 0) {
+ if (*pscan == ',' || *pscan == ';') {
+ pscan++;
+ nscan--;
+ } else if (*pscan != '\0') {
+ kfree(value);
+ value = NULL;
+ return NULL;
+ }
+ }
+ }
+ ctx->curr = pscan;
+ ctx->bytes_remaining = nscan;
+ return value;
+}
+
+void *
+parser_string_get(struct parser_context *ctx)
+{
+ u8 *pscan;
+ ulong nscan;
+ int value_length = -1;
+ void *value = NULL;
+ int i;
+
+ if (!ctx)
+ return NULL;
+ pscan = ctx->curr;
+ nscan = ctx->bytes_remaining;
+ if (nscan == 0)
+ return NULL;
+ if (!pscan)
+ return NULL;
+ for (i = 0, value_length = -1; i < nscan; i++)
+ if (pscan[i] == '\0') {
+ value_length = i;
+ break;
+ }
+ if (value_length < 0) /* '\0' was not included in the length */
+ value_length = nscan;
+ value = kmalloc(value_length + 1, GFP_KERNEL|__GFP_NORETRY);
+ if (value == NULL)
+ return NULL;
+ if (value_length > 0)
+ memcpy(value, pscan, value_length);
+ ((u8 *) (value))[value_length] = '\0';
+ return value;
+}
diff --git a/drivers/staging/unisys/visorchipset/parser.h b/drivers/staging/unisys/visorchipset/parser.h
new file mode 100644
index 000000000..2b903f1be
--- /dev/null
+++ b/drivers/staging/unisys/visorchipset/parser.h
@@ -0,0 +1,46 @@
+/* parser.h
+ *
+ * Copyright (C) 2010 - 2013 UNISYS CORPORATION
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or (at
+ * your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
+ * NON INFRINGEMENT. See the GNU General Public License for more
+ * details.
+ */
+
+#ifndef __PARSER_H__
+#define __PARSER_H__
+
+#include <linux/uuid.h>
+
+#include "timskmod.h"
+#include "channel.h"
+
+typedef enum {
+ PARSERSTRING_INITIATOR,
+ PARSERSTRING_TARGET,
+ PARSERSTRING_CONNECTION,
+ PARSERSTRING_NAME,
+} PARSER_WHICH_STRING;
+
+struct parser_context *parser_init(u64 addr, u32 bytes, BOOL isLocal,
+ BOOL *tryAgain);
+struct parser_context *parser_init_byte_stream(u64 addr, u32 bytes, BOOL local,
+ BOOL *retry);
+void parser_param_start(struct parser_context *ctx,
+ PARSER_WHICH_STRING which_string);
+void *parser_param_get(struct parser_context *ctx, char *nam, int namesize);
+void *parser_string_get(struct parser_context *ctx);
+uuid_le parser_id_get(struct parser_context *ctx);
+char *parser_simpleString_get(struct parser_context *ctx);
+void *parser_byte_stream_get(struct parser_context *ctx, ulong *nbytes);
+void parser_done(struct parser_context *ctx);
+
+#endif
diff --git a/drivers/staging/unisys/visorchipset/visorchipset.h b/drivers/staging/unisys/visorchipset/visorchipset.h
new file mode 100644
index 000000000..bd46df9ef
--- /dev/null
+++ b/drivers/staging/unisys/visorchipset/visorchipset.h
@@ -0,0 +1,236 @@
+/* visorchipset.h
+ *
+ * Copyright (C) 2010 - 2013 UNISYS CORPORATION
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or (at
+ * your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
+ * NON INFRINGEMENT. See the GNU General Public License for more
+ * details.
+ */
+
+#ifndef __VISORCHIPSET_H__
+#define __VISORCHIPSET_H__
+
+#include <linux/uuid.h>
+
+#include "timskmod.h"
+#include "channel.h"
+#include "controlvmchannel.h"
+#include "parser.h"
+#include "procobjecttree.h"
+#include "vbusdeviceinfo.h"
+#include "vbushelper.h"
+
+/** Describes the state from the perspective of which controlvm messages have
+ * been received for a bus or device.
+ */
+struct visorchipset_state {
+ u32 created:1;
+ u32 attached:1;
+ u32 configured:1;
+ u32 running:1;
+ /* Add new fields above. */
+ /* Remaining bits in this 32-bit word are unused. */
+};
+
+enum visorchipset_addresstype {
+ /** address is guest physical, but outside of the physical memory
+ * region that is controlled by the running OS (this is the normal
+ * address type for Supervisor channels)
+ */
+ ADDRTYPE_LOCALPHYSICAL,
+
+ /** address is guest physical, and withIN the confines of the
+ * physical memory controlled by the running OS.
+ */
+ ADDRTYPE_LOCALTEST,
+};
+
+enum crash_obj_type {
+ CRASH_DEV,
+ CRASH_BUS,
+};
+
+/** Attributes for a particular Supervisor channel.
+ */
+struct visorchipset_channel_info {
+ enum visorchipset_addresstype addr_type;
+ HOSTADDRESS channel_addr;
+ struct irq_info intr;
+ u64 n_channel_bytes;
+ uuid_le channel_type_uuid;
+ uuid_le channel_inst_uuid;
+
+};
+
+/** Attributes for a particular Supervisor device.
+ * Any visorchipset client can query these attributes using
+ * visorchipset_get_client_device_info() or
+ * visorchipset_get_server_device_info().
+ */
+struct visorchipset_device_info {
+ struct list_head entry;
+ u32 bus_no;
+ u32 dev_no;
+ uuid_le dev_inst_uuid;
+ struct visorchipset_state state;
+ struct visorchipset_channel_info chan_info;
+ u32 reserved1; /* control_vm_id */
+ u64 reserved2;
+ u32 switch_no; /* when devState.attached==1 */
+ u32 internal_port_no; /* when devState.attached==1 */
+ struct controlvm_message_header pending_msg_hdr;/* CONTROLVM_MESSAGE */
+ /** For private use by the bus driver */
+ void *bus_driver_context;
+
+};
+
+static inline struct visorchipset_device_info *finddevice(
+ struct list_head *list, u32 bus_no, u32 dev_no)
+{
+ struct visorchipset_device_info *p;
+
+ list_for_each_entry(p, list, entry) {
+ if (p->bus_no == bus_no && p->dev_no == dev_no)
+ return p;
+ }
+ return NULL;
+}
+
+static inline void delbusdevices(struct list_head *list, u32 bus_no)
+{
+ struct visorchipset_device_info *p, *tmp;
+
+ list_for_each_entry_safe(p, tmp, list, entry) {
+ if (p->bus_no == bus_no) {
+ list_del(&p->entry);
+ kfree(p);
+ }
+ }
+}
+
+/** Attributes for a particular Supervisor bus.
+ * (For a service partition acting as the server for buses/devices, there
+ * is a 1-to-1 relationship between busses and guest partitions.)
+ * Any visorchipset client can query these attributes using
+ * visorchipset_get_client_bus_info() or visorchipset_get_bus_info().
+ */
+struct visorchipset_bus_info {
+ struct list_head entry;
+ u32 bus_no;
+ struct visorchipset_state state;
+ struct visorchipset_channel_info chan_info;
+ uuid_le partition_uuid;
+ u64 partition_handle;
+ u8 *name; /* UTF8 */
+ u8 *description; /* UTF8 */
+ u64 reserved1;
+ u32 reserved2;
+ struct {
+ u32 server:1;
+ /* Add new fields above. */
+ /* Remaining bits in this 32-bit word are unused. */
+ } flags;
+ struct controlvm_message_header pending_msg_hdr;/* CONTROLVM MsgHdr */
+ /** For private use by the bus driver */
+ void *bus_driver_context;
+ u64 dev_no;
+
+};
+
+static inline struct visorchipset_bus_info *
+findbus(struct list_head *list, u32 bus_no)
+{
+ struct visorchipset_bus_info *p;
+
+ list_for_each_entry(p, list, entry) {
+ if (p->bus_no == bus_no)
+ return p;
+ }
+ return NULL;
+}
+
+/* These functions will be called from within visorchipset when certain
+ * events happen. (The implementation of these functions is outside of
+ * visorchipset.)
+ */
+struct visorchipset_busdev_notifiers {
+ void (*bus_create)(ulong bus_no);
+ void (*bus_destroy)(ulong bus_no);
+ void (*device_create)(ulong bus_no, ulong dev_no);
+ void (*device_destroy)(ulong bus_no, ulong dev_no);
+ void (*device_pause)(ulong bus_no, ulong dev_no);
+ void (*device_resume)(ulong bus_no, ulong dev_no);
+ int (*get_channel_info)(uuid_le type_uuid, ulong *min_size,
+ ulong *max_size);
+};
+
+/* These functions live inside visorchipset, and will be called to indicate
+ * responses to specific events (by code outside of visorchipset).
+ * For now, the value for each response is simply either:
+ * 0 = it worked
+ * -1 = it failed
+ */
+struct visorchipset_busdev_responders {
+ void (*bus_create)(ulong bus_no, int response);
+ void (*bus_destroy)(ulong bus_no, int response);
+ void (*device_create)(ulong bus_no, ulong dev_no, int response);
+ void (*device_destroy)(ulong bus_no, ulong dev_no, int response);
+ void (*device_pause)(ulong bus_no, ulong dev_no, int response);
+ void (*device_resume)(ulong bus_no, ulong dev_no, int response);
+};
+
+/** Register functions (in the bus driver) to get called by visorchipset
+ * whenever a bus or device appears for which this service partition is
+ * to be the server for. visorchipset will fill in <responders>, to
+ * indicate functions the bus driver should call to indicate message
+ * responses.
+ */
+void
+visorchipset_register_busdev_client(
+ struct visorchipset_busdev_notifiers *notifiers,
+ struct visorchipset_busdev_responders *responders,
+ struct ultra_vbus_deviceinfo *driver_info);
+
+/** Register functions (in the bus driver) to get called by visorchipset
+ * whenever a bus or device appears for which this service partition is
+ * to be the client for. visorchipset will fill in <responders>, to
+ * indicate functions the bus driver should call to indicate message
+ * responses.
+ */
+void
+visorchipset_register_busdev_server(
+ struct visorchipset_busdev_notifiers *notifiers,
+ struct visorchipset_busdev_responders *responders,
+ struct ultra_vbus_deviceinfo *driver_info);
+
+typedef void (*SPARREPORTEVENT_COMPLETE_FUNC) (struct controlvm_message *msg,
+ int status);
+
+void visorchipset_device_pause_response(ulong bus_no, ulong dev_no,
+ int response);
+
+BOOL visorchipset_get_bus_info(ulong bus_no,
+ struct visorchipset_bus_info *bus_info);
+BOOL visorchipset_get_device_info(ulong bus_no, ulong dev_no,
+ struct visorchipset_device_info *dev_info);
+BOOL visorchipset_set_bus_context(ulong bus_no, void *context);
+BOOL visorchipset_set_device_context(ulong bus_no, ulong dev_no, void *context);
+int visorchipset_chipset_ready(void);
+int visorchipset_chipset_selftest(void);
+int visorchipset_chipset_notready(void);
+void visorchipset_save_message(struct controlvm_message *msg,
+ enum crash_obj_type type);
+void *visorchipset_cache_alloc(struct kmem_cache *pool,
+ BOOL ok_to_block, char *fn, int ln);
+void visorchipset_cache_free(struct kmem_cache *pool, void *p,
+ char *fn, int ln);
+
+#endif
diff --git a/drivers/staging/unisys/visorchipset/visorchipset_main.c b/drivers/staging/unisys/visorchipset/visorchipset_main.c
new file mode 100644
index 000000000..f2663d2c7
--- /dev/null
+++ b/drivers/staging/unisys/visorchipset/visorchipset_main.c
@@ -0,0 +1,2335 @@
+/* visorchipset_main.c
+ *
+ * Copyright (C) 2010 - 2013 UNISYS CORPORATION
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or (at
+ * your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
+ * NON INFRINGEMENT. See the GNU General Public License for more
+ * details.
+ */
+
+#include "globals.h"
+#include "visorchipset.h"
+#include "procobjecttree.h"
+#include "visorchannel.h"
+#include "periodic_work.h"
+#include "file.h"
+#include "parser.h"
+#include "uisutils.h"
+#include "controlvmcompletionstatus.h"
+#include "guestlinuxdebug.h"
+
+#include <linux/nls.h>
+#include <linux/netdevice.h>
+#include <linux/platform_device.h>
+#include <linux/uuid.h>
+
+#define CURRENT_FILE_PC VISOR_CHIPSET_PC_visorchipset_main_c
+#define TEST_VNIC_PHYSITF "eth0" /* physical network itf for
+ * vnic loopback test */
+#define TEST_VNIC_SWITCHNO 1
+#define TEST_VNIC_BUSNO 9
+
+#define MAX_NAME_SIZE 128
+#define MAX_IP_SIZE 50
+#define MAXOUTSTANDINGCHANNELCOMMAND 256
+#define POLLJIFFIES_CONTROLVMCHANNEL_FAST 1
+#define POLLJIFFIES_CONTROLVMCHANNEL_SLOW 100
+
+/* When the controlvm channel is idle for at least MIN_IDLE_SECONDS,
+* we switch to slow polling mode. As soon as we get a controlvm
+* message, we switch back to fast polling mode.
+*/
+#define MIN_IDLE_SECONDS 10
+static ulong poll_jiffies = POLLJIFFIES_CONTROLVMCHANNEL_FAST;
+static ulong most_recent_message_jiffies; /* when we got our last
+ * controlvm message */
+static inline char *
+NONULLSTR(char *s)
+{
+ if (s)
+ return s;
+ return "";
+}
+
+static int serverregistered;
+static int clientregistered;
+
+#define MAX_CHIPSET_EVENTS 2
+static u8 chipset_events[MAX_CHIPSET_EVENTS] = { 0, 0 };
+
+static struct delayed_work periodic_controlvm_work;
+static struct workqueue_struct *periodic_controlvm_workqueue;
+static DEFINE_SEMAPHORE(notifier_lock);
+
+static struct controlvm_message_header g_diag_msg_hdr;
+static struct controlvm_message_header g_chipset_msg_hdr;
+static struct controlvm_message_header g_del_dump_msg_hdr;
+static const uuid_le spar_diag_pool_channel_protocol_uuid =
+ SPAR_DIAG_POOL_CHANNEL_PROTOCOL_UUID;
+/* 0xffffff is an invalid Bus/Device number */
+static ulong g_diagpool_bus_no = 0xffffff;
+static ulong g_diagpool_dev_no = 0xffffff;
+static struct controlvm_message_packet g_devicechangestate_packet;
+
+/* Only VNIC and VHBA channels are sent to visorclientbus (aka
+ * "visorhackbus")
+ */
+#define FOR_VISORHACKBUS(channel_type_guid) \
+ (((uuid_le_cmp(channel_type_guid,\
+ spar_vnic_channel_protocol_uuid) == 0) ||\
+ (uuid_le_cmp(channel_type_guid,\
+ spar_vhba_channel_protocol_uuid) == 0)))
+#define FOR_VISORBUS(channel_type_guid) (!(FOR_VISORHACKBUS(channel_type_guid)))
+
+#define is_diagpool_channel(channel_type_guid) \
+ (uuid_le_cmp(channel_type_guid,\
+ spar_diag_pool_channel_protocol_uuid) == 0)
+
+static LIST_HEAD(bus_info_list);
+static LIST_HEAD(dev_info_list);
+
+static struct visorchannel *controlvm_channel;
+
+/* Manages the request payload in the controlvm channel */
+static struct controlvm_payload_info {
+ u8 __iomem *ptr; /* pointer to base address of payload pool */
+ u64 offset; /* offset from beginning of controlvm
+ * channel to beginning of payload * pool */
+ u32 bytes; /* number of bytes in payload pool */
+} controlvm_payload_info;
+
+/* Manages the info for a CONTROLVM_DUMP_CAPTURESTATE /
+ * CONTROLVM_DUMP_GETTEXTDUMP / CONTROLVM_DUMP_COMPLETE conversation.
+ */
+static struct livedump_info {
+ struct controlvm_message_header dumpcapture_header;
+ struct controlvm_message_header gettextdump_header;
+ struct controlvm_message_header dumpcomplete_header;
+ BOOL gettextdump_outstanding;
+ u32 crc32;
+ ulong length;
+ atomic_t buffers_in_use;
+ ulong destination;
+} livedump_info;
+
+/* The following globals are used to handle the scenario where we are unable to
+ * offload the payload from a controlvm message due to memory requirements. In
+ * this scenario, we simply stash the controlvm message, then attempt to
+ * process it again the next time controlvm_periodic_work() runs.
+ */
+static struct controlvm_message controlvm_pending_msg;
+static BOOL controlvm_pending_msg_valid = FALSE;
+
+/* Pool of struct putfile_buffer_entry, for keeping track of pending (incoming)
+ * TRANSMIT_FILE PutFile payloads.
+ */
+static struct kmem_cache *putfile_buffer_list_pool;
+static const char putfile_buffer_list_pool_name[] =
+ "controlvm_putfile_buffer_list_pool";
+
+/* This identifies a data buffer that has been received via a controlvm messages
+ * in a remote --> local CONTROLVM_TRANSMIT_FILE conversation.
+ */
+struct putfile_buffer_entry {
+ struct list_head next; /* putfile_buffer_entry list */
+ struct parser_context *parser_ctx; /* points to input data buffer */
+};
+
+/* List of struct putfile_request *, via next_putfile_request member.
+ * Each entry in this list identifies an outstanding TRANSMIT_FILE
+ * conversation.
+ */
+static LIST_HEAD(putfile_request_list);
+
+/* This describes a buffer and its current state of transfer (e.g., how many
+ * bytes have already been supplied as putfile data, and how many bytes are
+ * remaining) for a putfile_request.
+ */
+struct putfile_active_buffer {
+ /* a payload from a controlvm message, containing a file data buffer */
+ struct parser_context *parser_ctx;
+ /* points within data area of parser_ctx to next byte of data */
+ u8 *pnext;
+ /* # bytes left from <pnext> to the end of this data buffer */
+ size_t bytes_remaining;
+};
+
+#define PUTFILE_REQUEST_SIG 0x0906101302281211
+/* This identifies a single remote --> local CONTROLVM_TRANSMIT_FILE
+ * conversation. Structs of this type are dynamically linked into
+ * <Putfile_request_list>.
+ */
+struct putfile_request {
+ u64 sig; /* PUTFILE_REQUEST_SIG */
+
+ /* header from original TransmitFile request */
+ struct controlvm_message_header controlvm_header;
+ u64 file_request_number; /* from original TransmitFile request */
+
+ /* link to next struct putfile_request */
+ struct list_head next_putfile_request;
+
+ /* most-recent sequence number supplied via a controlvm message */
+ u64 data_sequence_number;
+
+ /* head of putfile_buffer_entry list, which describes the data to be
+ * supplied as putfile data;
+ * - this list is added to when controlvm messages come in that supply
+ * file data
+ * - this list is removed from via the hotplug program that is actually
+ * consuming these buffers to write as file data */
+ struct list_head input_buffer_list;
+ spinlock_t req_list_lock; /* lock for input_buffer_list */
+
+ /* waiters for input_buffer_list to go non-empty */
+ wait_queue_head_t input_buffer_wq;
+
+ /* data not yet read within current putfile_buffer_entry */
+ struct putfile_active_buffer active_buf;
+
+ /* <0 = failed, 0 = in-progress, >0 = successful; */
+ /* note that this must be set with req_list_lock, and if you set <0, */
+ /* it is your responsibility to also free up all of the other objects */
+ /* in this struct (like input_buffer_list, active_buf.parser_ctx) */
+ /* before releasing the lock */
+ int completion_status;
+};
+
+static atomic_t visorchipset_cache_buffers_in_use = ATOMIC_INIT(0);
+
+struct parahotplug_request {
+ struct list_head list;
+ int id;
+ unsigned long expiration;
+ struct controlvm_message msg;
+};
+
+static LIST_HEAD(parahotplug_request_list);
+static DEFINE_SPINLOCK(parahotplug_request_list_lock); /* lock for above */
+static void parahotplug_process_list(void);
+
+/* Manages the info for a CONTROLVM_DUMP_CAPTURESTATE /
+ * CONTROLVM_REPORTEVENT.
+ */
+static struct visorchipset_busdev_notifiers busdev_server_notifiers;
+static struct visorchipset_busdev_notifiers busdev_client_notifiers;
+
+static void bus_create_response(ulong bus_no, int response);
+static void bus_destroy_response(ulong bus_no, int response);
+static void device_create_response(ulong bus_no, ulong dev_no, int response);
+static void device_destroy_response(ulong bus_no, ulong dev_no, int response);
+static void device_resume_response(ulong bus_no, ulong dev_no, int response);
+
+static struct visorchipset_busdev_responders busdev_responders = {
+ .bus_create = bus_create_response,
+ .bus_destroy = bus_destroy_response,
+ .device_create = device_create_response,
+ .device_destroy = device_destroy_response,
+ .device_pause = visorchipset_device_pause_response,
+ .device_resume = device_resume_response,
+};
+
+/* info for /dev/visorchipset */
+static dev_t major_dev = -1; /**< indicates major num for device */
+
+/* prototypes for attributes */
+static ssize_t toolaction_show(struct device *dev,
+ struct device_attribute *attr, char *buf);
+static ssize_t toolaction_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count);
+static DEVICE_ATTR_RW(toolaction);
+
+static ssize_t boottotool_show(struct device *dev,
+ struct device_attribute *attr, char *buf);
+static ssize_t boottotool_store(struct device *dev,
+ struct device_attribute *attr, const char *buf,
+ size_t count);
+static DEVICE_ATTR_RW(boottotool);
+
+static ssize_t error_show(struct device *dev, struct device_attribute *attr,
+ char *buf);
+static ssize_t error_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count);
+static DEVICE_ATTR_RW(error);
+
+static ssize_t textid_show(struct device *dev, struct device_attribute *attr,
+ char *buf);
+static ssize_t textid_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count);
+static DEVICE_ATTR_RW(textid);
+
+static ssize_t remaining_steps_show(struct device *dev,
+ struct device_attribute *attr, char *buf);
+static ssize_t remaining_steps_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count);
+static DEVICE_ATTR_RW(remaining_steps);
+
+static ssize_t chipsetready_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count);
+static DEVICE_ATTR_WO(chipsetready);
+
+static ssize_t devicedisabled_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count);
+static DEVICE_ATTR_WO(devicedisabled);
+
+static ssize_t deviceenabled_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count);
+static DEVICE_ATTR_WO(deviceenabled);
+
+static struct attribute *visorchipset_install_attrs[] = {
+ &dev_attr_toolaction.attr,
+ &dev_attr_boottotool.attr,
+ &dev_attr_error.attr,
+ &dev_attr_textid.attr,
+ &dev_attr_remaining_steps.attr,
+ NULL
+};
+
+static struct attribute_group visorchipset_install_group = {
+ .name = "install",
+ .attrs = visorchipset_install_attrs
+};
+
+static struct attribute *visorchipset_guest_attrs[] = {
+ &dev_attr_chipsetready.attr,
+ NULL
+};
+
+static struct attribute_group visorchipset_guest_group = {
+ .name = "guest",
+ .attrs = visorchipset_guest_attrs
+};
+
+static struct attribute *visorchipset_parahotplug_attrs[] = {
+ &dev_attr_devicedisabled.attr,
+ &dev_attr_deviceenabled.attr,
+ NULL
+};
+
+static struct attribute_group visorchipset_parahotplug_group = {
+ .name = "parahotplug",
+ .attrs = visorchipset_parahotplug_attrs
+};
+
+static const struct attribute_group *visorchipset_dev_groups[] = {
+ &visorchipset_install_group,
+ &visorchipset_guest_group,
+ &visorchipset_parahotplug_group,
+ NULL
+};
+
+/* /sys/devices/platform/visorchipset */
+static struct platform_device visorchipset_platform_device = {
+ .name = "visorchipset",
+ .id = -1,
+ .dev.groups = visorchipset_dev_groups,
+};
+
+/* Function prototypes */
+static void controlvm_respond(struct controlvm_message_header *msg_hdr,
+ int response);
+static void controlvm_respond_chipset_init(
+ struct controlvm_message_header *msg_hdr, int response,
+ enum ultra_chipset_feature features);
+static void controlvm_respond_physdev_changestate(
+ struct controlvm_message_header *msg_hdr, int response,
+ struct spar_segment_state state);
+
+static ssize_t toolaction_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ u8 tool_action;
+
+ visorchannel_read(controlvm_channel,
+ offsetof(struct spar_controlvm_channel_protocol,
+ tool_action), &tool_action, sizeof(u8));
+ return scnprintf(buf, PAGE_SIZE, "%u\n", tool_action);
+}
+
+static ssize_t toolaction_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ u8 tool_action;
+ int ret;
+
+ if (kstrtou8(buf, 10, &tool_action) != 0)
+ return -EINVAL;
+
+ ret = visorchannel_write(controlvm_channel,
+ offsetof(struct spar_controlvm_channel_protocol,
+ tool_action),
+ &tool_action, sizeof(u8));
+
+ if (ret)
+ return ret;
+ return count;
+}
+
+static ssize_t boottotool_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct efi_spar_indication efi_spar_indication;
+
+ visorchannel_read(controlvm_channel,
+ offsetof(struct spar_controlvm_channel_protocol,
+ efi_spar_ind), &efi_spar_indication,
+ sizeof(struct efi_spar_indication));
+ return scnprintf(buf, PAGE_SIZE, "%u\n",
+ efi_spar_indication.boot_to_tool);
+}
+
+static ssize_t boottotool_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ int val, ret;
+ struct efi_spar_indication efi_spar_indication;
+
+ if (kstrtoint(buf, 10, &val) != 0)
+ return -EINVAL;
+
+ efi_spar_indication.boot_to_tool = val;
+ ret = visorchannel_write(controlvm_channel,
+ offsetof(struct spar_controlvm_channel_protocol,
+ efi_spar_ind), &(efi_spar_indication),
+ sizeof(struct efi_spar_indication));
+
+ if (ret)
+ return ret;
+ return count;
+}
+
+static ssize_t error_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ u32 error;
+
+ visorchannel_read(controlvm_channel,
+ offsetof(struct spar_controlvm_channel_protocol,
+ installation_error),
+ &error, sizeof(u32));
+ return scnprintf(buf, PAGE_SIZE, "%i\n", error);
+}
+
+static ssize_t error_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ u32 error;
+ int ret;
+
+ if (kstrtou32(buf, 10, &error) != 0)
+ return -EINVAL;
+
+ ret = visorchannel_write(controlvm_channel,
+ offsetof(struct spar_controlvm_channel_protocol,
+ installation_error),
+ &error, sizeof(u32));
+ if (ret)
+ return ret;
+ return count;
+}
+
+static ssize_t textid_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ u32 text_id;
+
+ visorchannel_read(controlvm_channel,
+ offsetof(struct spar_controlvm_channel_protocol,
+ installation_text_id),
+ &text_id, sizeof(u32));
+ return scnprintf(buf, PAGE_SIZE, "%i\n", text_id);
+}
+
+static ssize_t textid_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ u32 text_id;
+ int ret;
+
+ if (kstrtou32(buf, 10, &text_id) != 0)
+ return -EINVAL;
+
+ ret = visorchannel_write(controlvm_channel,
+ offsetof(struct spar_controlvm_channel_protocol,
+ installation_text_id),
+ &text_id, sizeof(u32));
+ if (ret)
+ return ret;
+ return count;
+}
+
+static ssize_t remaining_steps_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ u16 remaining_steps;
+
+ visorchannel_read(controlvm_channel,
+ offsetof(struct spar_controlvm_channel_protocol,
+ installation_remaining_steps),
+ &remaining_steps, sizeof(u16));
+ return scnprintf(buf, PAGE_SIZE, "%hu\n", remaining_steps);
+}
+
+static ssize_t remaining_steps_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ u16 remaining_steps;
+ int ret;
+
+ if (kstrtou16(buf, 10, &remaining_steps) != 0)
+ return -EINVAL;
+
+ ret = visorchannel_write(controlvm_channel,
+ offsetof(struct spar_controlvm_channel_protocol,
+ installation_remaining_steps),
+ &remaining_steps, sizeof(u16));
+ if (ret)
+ return ret;
+ return count;
+}
+
+static void
+bus_info_clear(void *v)
+{
+ struct visorchipset_bus_info *p = (struct visorchipset_bus_info *) (v);
+
+ kfree(p->name);
+ p->name = NULL;
+
+ kfree(p->description);
+ p->description = NULL;
+
+ p->state.created = 0;
+ memset(p, 0, sizeof(struct visorchipset_bus_info));
+}
+
+static void
+dev_info_clear(void *v)
+{
+ struct visorchipset_device_info *p =
+ (struct visorchipset_device_info *)(v);
+
+ p->state.created = 0;
+ memset(p, 0, sizeof(struct visorchipset_device_info));
+}
+
+static u8
+check_chipset_events(void)
+{
+ int i;
+ u8 send_msg = 1;
+ /* Check events to determine if response should be sent */
+ for (i = 0; i < MAX_CHIPSET_EVENTS; i++)
+ send_msg &= chipset_events[i];
+ return send_msg;
+}
+
+static void
+clear_chipset_events(void)
+{
+ int i;
+ /* Clear chipset_events */
+ for (i = 0; i < MAX_CHIPSET_EVENTS; i++)
+ chipset_events[i] = 0;
+}
+
+void
+visorchipset_register_busdev_server(
+ struct visorchipset_busdev_notifiers *notifiers,
+ struct visorchipset_busdev_responders *responders,
+ struct ultra_vbus_deviceinfo *driver_info)
+{
+ down(&notifier_lock);
+ if (!notifiers) {
+ memset(&busdev_server_notifiers, 0,
+ sizeof(busdev_server_notifiers));
+ serverregistered = 0; /* clear flag */
+ } else {
+ busdev_server_notifiers = *notifiers;
+ serverregistered = 1; /* set flag */
+ }
+ if (responders)
+ *responders = busdev_responders;
+ if (driver_info)
+ bus_device_info_init(driver_info, "chipset", "visorchipset",
+ VERSION, NULL);
+
+ up(&notifier_lock);
+}
+EXPORT_SYMBOL_GPL(visorchipset_register_busdev_server);
+
+void
+visorchipset_register_busdev_client(
+ struct visorchipset_busdev_notifiers *notifiers,
+ struct visorchipset_busdev_responders *responders,
+ struct ultra_vbus_deviceinfo *driver_info)
+{
+ down(&notifier_lock);
+ if (!notifiers) {
+ memset(&busdev_client_notifiers, 0,
+ sizeof(busdev_client_notifiers));
+ clientregistered = 0; /* clear flag */
+ } else {
+ busdev_client_notifiers = *notifiers;
+ clientregistered = 1; /* set flag */
+ }
+ if (responders)
+ *responders = busdev_responders;
+ if (driver_info)
+ bus_device_info_init(driver_info, "chipset(bolts)",
+ "visorchipset", VERSION, NULL);
+ up(&notifier_lock);
+}
+EXPORT_SYMBOL_GPL(visorchipset_register_busdev_client);
+
+static void
+cleanup_controlvm_structures(void)
+{
+ struct visorchipset_bus_info *bi, *tmp_bi;
+ struct visorchipset_device_info *di, *tmp_di;
+
+ list_for_each_entry_safe(bi, tmp_bi, &bus_info_list, entry) {
+ bus_info_clear(bi);
+ list_del(&bi->entry);
+ kfree(bi);
+ }
+
+ list_for_each_entry_safe(di, tmp_di, &dev_info_list, entry) {
+ dev_info_clear(di);
+ list_del(&di->entry);
+ kfree(di);
+ }
+}
+
+static void
+chipset_init(struct controlvm_message *inmsg)
+{
+ static int chipset_inited;
+ enum ultra_chipset_feature features = 0;
+ int rc = CONTROLVM_RESP_SUCCESS;
+
+ POSTCODE_LINUX_2(CHIPSET_INIT_ENTRY_PC, POSTCODE_SEVERITY_INFO);
+ if (chipset_inited) {
+ rc = -CONTROLVM_RESP_ERROR_ALREADY_DONE;
+ goto cleanup;
+ }
+ chipset_inited = 1;
+ POSTCODE_LINUX_2(CHIPSET_INIT_EXIT_PC, POSTCODE_SEVERITY_INFO);
+
+ /* Set features to indicate we support parahotplug (if Command
+ * also supports it). */
+ features =
+ inmsg->cmd.init_chipset.
+ features & ULTRA_CHIPSET_FEATURE_PARA_HOTPLUG;
+
+ /* Set the "reply" bit so Command knows this is a
+ * features-aware driver. */
+ features |= ULTRA_CHIPSET_FEATURE_REPLY;
+
+cleanup:
+ if (rc < 0)
+ cleanup_controlvm_structures();
+ if (inmsg->hdr.flags.response_expected)
+ controlvm_respond_chipset_init(&inmsg->hdr, rc, features);
+}
+
+static void
+controlvm_init_response(struct controlvm_message *msg,
+ struct controlvm_message_header *msg_hdr, int response)
+{
+ memset(msg, 0, sizeof(struct controlvm_message));
+ memcpy(&msg->hdr, msg_hdr, sizeof(struct controlvm_message_header));
+ msg->hdr.payload_bytes = 0;
+ msg->hdr.payload_vm_offset = 0;
+ msg->hdr.payload_max_bytes = 0;
+ if (response < 0) {
+ msg->hdr.flags.failed = 1;
+ msg->hdr.completion_status = (u32) (-response);
+ }
+}
+
+static void
+controlvm_respond(struct controlvm_message_header *msg_hdr, int response)
+{
+ struct controlvm_message outmsg;
+
+ controlvm_init_response(&outmsg, msg_hdr, response);
+ /* For DiagPool channel DEVICE_CHANGESTATE, we need to send
+ * back the deviceChangeState structure in the packet. */
+ if (msg_hdr->id == CONTROLVM_DEVICE_CHANGESTATE &&
+ g_devicechangestate_packet.device_change_state.bus_no ==
+ g_diagpool_bus_no &&
+ g_devicechangestate_packet.device_change_state.dev_no ==
+ g_diagpool_dev_no)
+ outmsg.cmd = g_devicechangestate_packet;
+ if (outmsg.hdr.flags.test_message == 1)
+ return;
+
+ if (!visorchannel_signalinsert(controlvm_channel,
+ CONTROLVM_QUEUE_REQUEST, &outmsg)) {
+ return;
+ }
+}
+
+static void
+controlvm_respond_chipset_init(struct controlvm_message_header *msg_hdr,
+ int response,
+ enum ultra_chipset_feature features)
+{
+ struct controlvm_message outmsg;
+
+ controlvm_init_response(&outmsg, msg_hdr, response);
+ outmsg.cmd.init_chipset.features = features;
+ if (!visorchannel_signalinsert(controlvm_channel,
+ CONTROLVM_QUEUE_REQUEST, &outmsg)) {
+ return;
+ }
+}
+
+static void controlvm_respond_physdev_changestate(
+ struct controlvm_message_header *msg_hdr, int response,
+ struct spar_segment_state state)
+{
+ struct controlvm_message outmsg;
+
+ controlvm_init_response(&outmsg, msg_hdr, response);
+ outmsg.cmd.device_change_state.state = state;
+ outmsg.cmd.device_change_state.flags.phys_device = 1;
+ if (!visorchannel_signalinsert(controlvm_channel,
+ CONTROLVM_QUEUE_REQUEST, &outmsg)) {
+ return;
+ }
+}
+
+void
+visorchipset_save_message(struct controlvm_message *msg,
+ enum crash_obj_type type)
+{
+ u32 crash_msg_offset;
+ u16 crash_msg_count;
+
+ /* get saved message count */
+ if (visorchannel_read(controlvm_channel,
+ offsetof(struct spar_controlvm_channel_protocol,
+ saved_crash_message_count),
+ &crash_msg_count, sizeof(u16)) < 0) {
+ POSTCODE_LINUX_2(CRASH_DEV_CTRL_RD_FAILURE_PC,
+ POSTCODE_SEVERITY_ERR);
+ return;
+ }
+
+ if (crash_msg_count != CONTROLVM_CRASHMSG_MAX) {
+ POSTCODE_LINUX_3(CRASH_DEV_COUNT_FAILURE_PC,
+ crash_msg_count,
+ POSTCODE_SEVERITY_ERR);
+ return;
+ }
+
+ /* get saved crash message offset */
+ if (visorchannel_read(controlvm_channel,
+ offsetof(struct spar_controlvm_channel_protocol,
+ saved_crash_message_offset),
+ &crash_msg_offset, sizeof(u32)) < 0) {
+ POSTCODE_LINUX_2(CRASH_DEV_CTRL_RD_FAILURE_PC,
+ POSTCODE_SEVERITY_ERR);
+ return;
+ }
+
+ if (type == CRASH_BUS) {
+ if (visorchannel_write(controlvm_channel,
+ crash_msg_offset,
+ msg,
+ sizeof(struct controlvm_message)) < 0) {
+ POSTCODE_LINUX_2(SAVE_MSG_BUS_FAILURE_PC,
+ POSTCODE_SEVERITY_ERR);
+ return;
+ }
+ } else {
+ if (visorchannel_write(controlvm_channel,
+ crash_msg_offset +
+ sizeof(struct controlvm_message), msg,
+ sizeof(struct controlvm_message)) < 0) {
+ POSTCODE_LINUX_2(SAVE_MSG_DEV_FAILURE_PC,
+ POSTCODE_SEVERITY_ERR);
+ return;
+ }
+ }
+}
+EXPORT_SYMBOL_GPL(visorchipset_save_message);
+
+static void
+bus_responder(enum controlvm_id cmd_id, ulong bus_no, int response)
+{
+ struct visorchipset_bus_info *p = NULL;
+ BOOL need_clear = FALSE;
+
+ p = findbus(&bus_info_list, bus_no);
+ if (!p)
+ return;
+
+ if (response < 0) {
+ if ((cmd_id == CONTROLVM_BUS_CREATE) &&
+ (response != (-CONTROLVM_RESP_ERROR_ALREADY_DONE)))
+ /* undo the row we just created... */
+ delbusdevices(&dev_info_list, bus_no);
+ } else {
+ if (cmd_id == CONTROLVM_BUS_CREATE)
+ p->state.created = 1;
+ if (cmd_id == CONTROLVM_BUS_DESTROY)
+ need_clear = TRUE;
+ }
+
+ if (p->pending_msg_hdr.id == CONTROLVM_INVALID)
+ return; /* no controlvm response needed */
+ if (p->pending_msg_hdr.id != (u32)cmd_id)
+ return;
+ controlvm_respond(&p->pending_msg_hdr, response);
+ p->pending_msg_hdr.id = CONTROLVM_INVALID;
+ if (need_clear) {
+ bus_info_clear(p);
+ delbusdevices(&dev_info_list, bus_no);
+ }
+}
+
+static void
+device_changestate_responder(enum controlvm_id cmd_id,
+ ulong bus_no, ulong dev_no, int response,
+ struct spar_segment_state response_state)
+{
+ struct visorchipset_device_info *p = NULL;
+ struct controlvm_message outmsg;
+
+ p = finddevice(&dev_info_list, bus_no, dev_no);
+ if (!p)
+ return;
+ if (p->pending_msg_hdr.id == CONTROLVM_INVALID)
+ return; /* no controlvm response needed */
+ if (p->pending_msg_hdr.id != cmd_id)
+ return;
+
+ controlvm_init_response(&outmsg, &p->pending_msg_hdr, response);
+
+ outmsg.cmd.device_change_state.bus_no = bus_no;
+ outmsg.cmd.device_change_state.dev_no = dev_no;
+ outmsg.cmd.device_change_state.state = response_state;
+
+ if (!visorchannel_signalinsert(controlvm_channel,
+ CONTROLVM_QUEUE_REQUEST, &outmsg))
+ return;
+
+ p->pending_msg_hdr.id = CONTROLVM_INVALID;
+}
+
+static void
+device_responder(enum controlvm_id cmd_id, ulong bus_no, ulong dev_no,
+ int response)
+{
+ struct visorchipset_device_info *p = NULL;
+ BOOL need_clear = FALSE;
+
+ p = finddevice(&dev_info_list, bus_no, dev_no);
+ if (!p)
+ return;
+ if (response >= 0) {
+ if (cmd_id == CONTROLVM_DEVICE_CREATE)
+ p->state.created = 1;
+ if (cmd_id == CONTROLVM_DEVICE_DESTROY)
+ need_clear = TRUE;
+ }
+
+ if (p->pending_msg_hdr.id == CONTROLVM_INVALID)
+ return; /* no controlvm response needed */
+
+ if (p->pending_msg_hdr.id != (u32)cmd_id)
+ return;
+
+ controlvm_respond(&p->pending_msg_hdr, response);
+ p->pending_msg_hdr.id = CONTROLVM_INVALID;
+ if (need_clear)
+ dev_info_clear(p);
+}
+
+static void
+bus_epilog(u32 bus_no,
+ u32 cmd, struct controlvm_message_header *msg_hdr,
+ int response, BOOL need_response)
+{
+ BOOL notified = FALSE;
+
+ struct visorchipset_bus_info *bus_info = findbus(&bus_info_list,
+ bus_no);
+
+ if (!bus_info)
+ return;
+
+ if (need_response) {
+ memcpy(&bus_info->pending_msg_hdr, msg_hdr,
+ sizeof(struct controlvm_message_header));
+ } else {
+ bus_info->pending_msg_hdr.id = CONTROLVM_INVALID;
+ }
+
+ down(&notifier_lock);
+ if (response == CONTROLVM_RESP_SUCCESS) {
+ switch (cmd) {
+ case CONTROLVM_BUS_CREATE:
+ /* We can't tell from the bus_create
+ * information which of our 2 bus flavors the
+ * devices on this bus will ultimately end up.
+ * FORTUNATELY, it turns out it is harmless to
+ * send the bus_create to both of them. We can
+ * narrow things down a little bit, though,
+ * because we know: - BusDev_Server can handle
+ * either server or client devices
+ * - BusDev_Client can handle ONLY client
+ * devices */
+ if (busdev_server_notifiers.bus_create) {
+ (*busdev_server_notifiers.bus_create) (bus_no);
+ notified = TRUE;
+ }
+ if ((!bus_info->flags.server) /*client */ &&
+ busdev_client_notifiers.bus_create) {
+ (*busdev_client_notifiers.bus_create) (bus_no);
+ notified = TRUE;
+ }
+ break;
+ case CONTROLVM_BUS_DESTROY:
+ if (busdev_server_notifiers.bus_destroy) {
+ (*busdev_server_notifiers.bus_destroy) (bus_no);
+ notified = TRUE;
+ }
+ if ((!bus_info->flags.server) /*client */ &&
+ busdev_client_notifiers.bus_destroy) {
+ (*busdev_client_notifiers.bus_destroy) (bus_no);
+ notified = TRUE;
+ }
+ break;
+ }
+ }
+ if (notified)
+ /* The callback function just called above is responsible
+ * for calling the appropriate visorchipset_busdev_responders
+ * function, which will call bus_responder()
+ */
+ ;
+ else
+ bus_responder(cmd, bus_no, response);
+ up(&notifier_lock);
+}
+
+static void
+device_epilog(u32 bus_no, u32 dev_no, struct spar_segment_state state, u32 cmd,
+ struct controlvm_message_header *msg_hdr, int response,
+ BOOL need_response, BOOL for_visorbus)
+{
+ struct visorchipset_busdev_notifiers *notifiers = NULL;
+ BOOL notified = FALSE;
+
+ struct visorchipset_device_info *dev_info =
+ finddevice(&dev_info_list, bus_no, dev_no);
+ char *envp[] = {
+ "SPARSP_DIAGPOOL_PAUSED_STATE = 1",
+ NULL
+ };
+
+ if (!dev_info)
+ return;
+
+ if (for_visorbus)
+ notifiers = &busdev_server_notifiers;
+ else
+ notifiers = &busdev_client_notifiers;
+ if (need_response) {
+ memcpy(&dev_info->pending_msg_hdr, msg_hdr,
+ sizeof(struct controlvm_message_header));
+ } else {
+ dev_info->pending_msg_hdr.id = CONTROLVM_INVALID;
+ }
+
+ down(&notifier_lock);
+ if (response >= 0) {
+ switch (cmd) {
+ case CONTROLVM_DEVICE_CREATE:
+ if (notifiers->device_create) {
+ (*notifiers->device_create) (bus_no, dev_no);
+ notified = TRUE;
+ }
+ break;
+ case CONTROLVM_DEVICE_CHANGESTATE:
+ /* ServerReady / ServerRunning / SegmentStateRunning */
+ if (state.alive == segment_state_running.alive &&
+ state.operating ==
+ segment_state_running.operating) {
+ if (notifiers->device_resume) {
+ (*notifiers->device_resume) (bus_no,
+ dev_no);
+ notified = TRUE;
+ }
+ }
+ /* ServerNotReady / ServerLost / SegmentStateStandby */
+ else if (state.alive == segment_state_standby.alive &&
+ state.operating ==
+ segment_state_standby.operating) {
+ /* technically this is standby case
+ * where server is lost
+ */
+ if (notifiers->device_pause) {
+ (*notifiers->device_pause) (bus_no,
+ dev_no);
+ notified = TRUE;
+ }
+ } else if (state.alive == segment_state_paused.alive &&
+ state.operating ==
+ segment_state_paused.operating) {
+ /* this is lite pause where channel is
+ * still valid just 'pause' of it
+ */
+ if (bus_no == g_diagpool_bus_no &&
+ dev_no == g_diagpool_dev_no) {
+ /* this will trigger the
+ * diag_shutdown.sh script in
+ * the visorchipset hotplug */
+ kobject_uevent_env
+ (&visorchipset_platform_device.dev.
+ kobj, KOBJ_ONLINE, envp);
+ }
+ }
+ break;
+ case CONTROLVM_DEVICE_DESTROY:
+ if (notifiers->device_destroy) {
+ (*notifiers->device_destroy) (bus_no, dev_no);
+ notified = TRUE;
+ }
+ break;
+ }
+ }
+ if (notified)
+ /* The callback function just called above is responsible
+ * for calling the appropriate visorchipset_busdev_responders
+ * function, which will call device_responder()
+ */
+ ;
+ else
+ device_responder(cmd, bus_no, dev_no, response);
+ up(&notifier_lock);
+}
+
+static void
+bus_create(struct controlvm_message *inmsg)
+{
+ struct controlvm_message_packet *cmd = &inmsg->cmd;
+ ulong bus_no = cmd->create_bus.bus_no;
+ int rc = CONTROLVM_RESP_SUCCESS;
+ struct visorchipset_bus_info *bus_info = NULL;
+
+ bus_info = findbus(&bus_info_list, bus_no);
+ if (bus_info && (bus_info->state.created == 1)) {
+ POSTCODE_LINUX_3(BUS_CREATE_FAILURE_PC, bus_no,
+ POSTCODE_SEVERITY_ERR);
+ rc = -CONTROLVM_RESP_ERROR_ALREADY_DONE;
+ goto cleanup;
+ }
+ bus_info = kzalloc(sizeof(*bus_info), GFP_KERNEL);
+ if (!bus_info) {
+ POSTCODE_LINUX_3(BUS_CREATE_FAILURE_PC, bus_no,
+ POSTCODE_SEVERITY_ERR);
+ rc = -CONTROLVM_RESP_ERROR_KMALLOC_FAILED;
+ goto cleanup;
+ }
+
+ INIT_LIST_HEAD(&bus_info->entry);
+ bus_info->bus_no = bus_no;
+ bus_info->dev_no = cmd->create_bus.dev_count;
+
+ POSTCODE_LINUX_3(BUS_CREATE_ENTRY_PC, bus_no, POSTCODE_SEVERITY_INFO);
+
+ if (inmsg->hdr.flags.test_message == 1)
+ bus_info->chan_info.addr_type = ADDRTYPE_LOCALTEST;
+ else
+ bus_info->chan_info.addr_type = ADDRTYPE_LOCALPHYSICAL;
+
+ bus_info->flags.server = inmsg->hdr.flags.server;
+ bus_info->chan_info.channel_addr = cmd->create_bus.channel_addr;
+ bus_info->chan_info.n_channel_bytes = cmd->create_bus.channel_bytes;
+ bus_info->chan_info.channel_type_uuid =
+ cmd->create_bus.bus_data_type_uuid;
+ bus_info->chan_info.channel_inst_uuid = cmd->create_bus.bus_inst_uuid;
+
+ list_add(&bus_info->entry, &bus_info_list);
+
+ POSTCODE_LINUX_3(BUS_CREATE_EXIT_PC, bus_no, POSTCODE_SEVERITY_INFO);
+
+cleanup:
+ bus_epilog(bus_no, CONTROLVM_BUS_CREATE, &inmsg->hdr,
+ rc, inmsg->hdr.flags.response_expected == 1);
+}
+
+static void
+bus_destroy(struct controlvm_message *inmsg)
+{
+ struct controlvm_message_packet *cmd = &inmsg->cmd;
+ ulong bus_no = cmd->destroy_bus.bus_no;
+ struct visorchipset_bus_info *bus_info;
+ int rc = CONTROLVM_RESP_SUCCESS;
+
+ bus_info = findbus(&bus_info_list, bus_no);
+ if (!bus_info)
+ rc = -CONTROLVM_RESP_ERROR_BUS_INVALID;
+ else if (bus_info->state.created == 0)
+ rc = -CONTROLVM_RESP_ERROR_ALREADY_DONE;
+
+ bus_epilog(bus_no, CONTROLVM_BUS_DESTROY, &inmsg->hdr,
+ rc, inmsg->hdr.flags.response_expected == 1);
+}
+
+static void
+bus_configure(struct controlvm_message *inmsg,
+ struct parser_context *parser_ctx)
+{
+ struct controlvm_message_packet *cmd = &inmsg->cmd;
+ ulong bus_no = cmd->configure_bus.bus_no;
+ struct visorchipset_bus_info *bus_info = NULL;
+ int rc = CONTROLVM_RESP_SUCCESS;
+ char s[99];
+
+ bus_no = cmd->configure_bus.bus_no;
+ POSTCODE_LINUX_3(BUS_CONFIGURE_ENTRY_PC, bus_no,
+ POSTCODE_SEVERITY_INFO);
+
+ bus_info = findbus(&bus_info_list, bus_no);
+ if (!bus_info) {
+ POSTCODE_LINUX_3(BUS_CONFIGURE_FAILURE_PC, bus_no,
+ POSTCODE_SEVERITY_ERR);
+ rc = -CONTROLVM_RESP_ERROR_BUS_INVALID;
+ } else if (bus_info->state.created == 0) {
+ POSTCODE_LINUX_3(BUS_CONFIGURE_FAILURE_PC, bus_no,
+ POSTCODE_SEVERITY_ERR);
+ rc = -CONTROLVM_RESP_ERROR_BUS_INVALID;
+ } else if (bus_info->pending_msg_hdr.id != CONTROLVM_INVALID) {
+ POSTCODE_LINUX_3(BUS_CONFIGURE_FAILURE_PC, bus_no,
+ POSTCODE_SEVERITY_ERR);
+ rc = -CONTROLVM_RESP_ERROR_MESSAGE_ID_INVALID_FOR_CLIENT;
+ } else {
+ bus_info->partition_handle = cmd->configure_bus.guest_handle;
+ bus_info->partition_uuid = parser_id_get(parser_ctx);
+ parser_param_start(parser_ctx, PARSERSTRING_NAME);
+ bus_info->name = parser_string_get(parser_ctx);
+
+ visorchannel_uuid_id(&bus_info->partition_uuid, s);
+ POSTCODE_LINUX_3(BUS_CONFIGURE_EXIT_PC, bus_no,
+ POSTCODE_SEVERITY_INFO);
+ }
+ bus_epilog(bus_no, CONTROLVM_BUS_CONFIGURE, &inmsg->hdr,
+ rc, inmsg->hdr.flags.response_expected == 1);
+}
+
+static void
+my_device_create(struct controlvm_message *inmsg)
+{
+ struct controlvm_message_packet *cmd = &inmsg->cmd;
+ ulong bus_no = cmd->create_device.bus_no;
+ ulong dev_no = cmd->create_device.dev_no;
+ struct visorchipset_device_info *dev_info = NULL;
+ struct visorchipset_bus_info *bus_info = NULL;
+ int rc = CONTROLVM_RESP_SUCCESS;
+
+ dev_info = finddevice(&dev_info_list, bus_no, dev_no);
+ if (dev_info && (dev_info->state.created == 1)) {
+ POSTCODE_LINUX_4(DEVICE_CREATE_FAILURE_PC, dev_no, bus_no,
+ POSTCODE_SEVERITY_ERR);
+ rc = -CONTROLVM_RESP_ERROR_ALREADY_DONE;
+ goto cleanup;
+ }
+ bus_info = findbus(&bus_info_list, bus_no);
+ if (!bus_info) {
+ POSTCODE_LINUX_4(DEVICE_CREATE_FAILURE_PC, dev_no, bus_no,
+ POSTCODE_SEVERITY_ERR);
+ rc = -CONTROLVM_RESP_ERROR_BUS_INVALID;
+ goto cleanup;
+ }
+ if (bus_info->state.created == 0) {
+ POSTCODE_LINUX_4(DEVICE_CREATE_FAILURE_PC, dev_no, bus_no,
+ POSTCODE_SEVERITY_ERR);
+ rc = -CONTROLVM_RESP_ERROR_BUS_INVALID;
+ goto cleanup;
+ }
+ dev_info = kzalloc(sizeof(*dev_info), GFP_KERNEL);
+ if (!dev_info) {
+ POSTCODE_LINUX_4(DEVICE_CREATE_FAILURE_PC, dev_no, bus_no,
+ POSTCODE_SEVERITY_ERR);
+ rc = -CONTROLVM_RESP_ERROR_KMALLOC_FAILED;
+ goto cleanup;
+ }
+
+ INIT_LIST_HEAD(&dev_info->entry);
+ dev_info->bus_no = bus_no;
+ dev_info->dev_no = dev_no;
+ dev_info->dev_inst_uuid = cmd->create_device.dev_inst_uuid;
+ POSTCODE_LINUX_4(DEVICE_CREATE_ENTRY_PC, dev_no, bus_no,
+ POSTCODE_SEVERITY_INFO);
+
+ if (inmsg->hdr.flags.test_message == 1)
+ dev_info->chan_info.addr_type = ADDRTYPE_LOCALTEST;
+ else
+ dev_info->chan_info.addr_type = ADDRTYPE_LOCALPHYSICAL;
+ dev_info->chan_info.channel_addr = cmd->create_device.channel_addr;
+ dev_info->chan_info.n_channel_bytes = cmd->create_device.channel_bytes;
+ dev_info->chan_info.channel_type_uuid =
+ cmd->create_device.data_type_uuid;
+ dev_info->chan_info.intr = cmd->create_device.intr;
+ list_add(&dev_info->entry, &dev_info_list);
+ POSTCODE_LINUX_4(DEVICE_CREATE_EXIT_PC, dev_no, bus_no,
+ POSTCODE_SEVERITY_INFO);
+cleanup:
+ /* get the bus and devNo for DiagPool channel */
+ if (dev_info &&
+ is_diagpool_channel(dev_info->chan_info.channel_type_uuid)) {
+ g_diagpool_bus_no = bus_no;
+ g_diagpool_dev_no = dev_no;
+ }
+ device_epilog(bus_no, dev_no, segment_state_running,
+ CONTROLVM_DEVICE_CREATE, &inmsg->hdr, rc,
+ inmsg->hdr.flags.response_expected == 1,
+ FOR_VISORBUS(dev_info->chan_info.channel_type_uuid));
+}
+
+static void
+my_device_changestate(struct controlvm_message *inmsg)
+{
+ struct controlvm_message_packet *cmd = &inmsg->cmd;
+ ulong bus_no = cmd->device_change_state.bus_no;
+ ulong dev_no = cmd->device_change_state.dev_no;
+ struct spar_segment_state state = cmd->device_change_state.state;
+ struct visorchipset_device_info *dev_info = NULL;
+ int rc = CONTROLVM_RESP_SUCCESS;
+
+ dev_info = finddevice(&dev_info_list, bus_no, dev_no);
+ if (!dev_info) {
+ POSTCODE_LINUX_4(DEVICE_CHANGESTATE_FAILURE_PC, dev_no, bus_no,
+ POSTCODE_SEVERITY_ERR);
+ rc = -CONTROLVM_RESP_ERROR_DEVICE_INVALID;
+ } else if (dev_info->state.created == 0) {
+ POSTCODE_LINUX_4(DEVICE_CHANGESTATE_FAILURE_PC, dev_no, bus_no,
+ POSTCODE_SEVERITY_ERR);
+ rc = -CONTROLVM_RESP_ERROR_DEVICE_INVALID;
+ }
+ if ((rc >= CONTROLVM_RESP_SUCCESS) && dev_info)
+ device_epilog(bus_no, dev_no, state,
+ CONTROLVM_DEVICE_CHANGESTATE, &inmsg->hdr, rc,
+ inmsg->hdr.flags.response_expected == 1,
+ FOR_VISORBUS(
+ dev_info->chan_info.channel_type_uuid));
+}
+
+static void
+my_device_destroy(struct controlvm_message *inmsg)
+{
+ struct controlvm_message_packet *cmd = &inmsg->cmd;
+ ulong bus_no = cmd->destroy_device.bus_no;
+ ulong dev_no = cmd->destroy_device.dev_no;
+ struct visorchipset_device_info *dev_info = NULL;
+ int rc = CONTROLVM_RESP_SUCCESS;
+
+ dev_info = finddevice(&dev_info_list, bus_no, dev_no);
+ if (!dev_info)
+ rc = -CONTROLVM_RESP_ERROR_DEVICE_INVALID;
+ else if (dev_info->state.created == 0)
+ rc = -CONTROLVM_RESP_ERROR_ALREADY_DONE;
+
+ if ((rc >= CONTROLVM_RESP_SUCCESS) && dev_info)
+ device_epilog(bus_no, dev_no, segment_state_running,
+ CONTROLVM_DEVICE_DESTROY, &inmsg->hdr, rc,
+ inmsg->hdr.flags.response_expected == 1,
+ FOR_VISORBUS(
+ dev_info->chan_info.channel_type_uuid));
+}
+
+/* When provided with the physical address of the controlvm channel
+ * (phys_addr), the offset to the payload area we need to manage
+ * (offset), and the size of this payload area (bytes), fills in the
+ * controlvm_payload_info struct. Returns TRUE for success or FALSE
+ * for failure.
+ */
+static int
+initialize_controlvm_payload_info(HOSTADDRESS phys_addr, u64 offset, u32 bytes,
+ struct controlvm_payload_info *info)
+{
+ u8 __iomem *payload = NULL;
+ int rc = CONTROLVM_RESP_SUCCESS;
+
+ if (!info) {
+ rc = -CONTROLVM_RESP_ERROR_PAYLOAD_INVALID;
+ goto cleanup;
+ }
+ memset(info, 0, sizeof(struct controlvm_payload_info));
+ if ((offset == 0) || (bytes == 0)) {
+ rc = -CONTROLVM_RESP_ERROR_PAYLOAD_INVALID;
+ goto cleanup;
+ }
+ payload = ioremap_cache(phys_addr + offset, bytes);
+ if (!payload) {
+ rc = -CONTROLVM_RESP_ERROR_IOREMAP_FAILED;
+ goto cleanup;
+ }
+
+ info->offset = offset;
+ info->bytes = bytes;
+ info->ptr = payload;
+
+cleanup:
+ if (rc < 0) {
+ if (payload) {
+ iounmap(payload);
+ payload = NULL;
+ }
+ }
+ return rc;
+}
+
+static void
+destroy_controlvm_payload_info(struct controlvm_payload_info *info)
+{
+ if (info->ptr) {
+ iounmap(info->ptr);
+ info->ptr = NULL;
+ }
+ memset(info, 0, sizeof(struct controlvm_payload_info));
+}
+
+static void
+initialize_controlvm_payload(void)
+{
+ HOSTADDRESS phys_addr = visorchannel_get_physaddr(controlvm_channel);
+ u64 payload_offset = 0;
+ u32 payload_bytes = 0;
+
+ if (visorchannel_read(controlvm_channel,
+ offsetof(struct spar_controlvm_channel_protocol,
+ request_payload_offset),
+ &payload_offset, sizeof(payload_offset)) < 0) {
+ POSTCODE_LINUX_2(CONTROLVM_INIT_FAILURE_PC,
+ POSTCODE_SEVERITY_ERR);
+ return;
+ }
+ if (visorchannel_read(controlvm_channel,
+ offsetof(struct spar_controlvm_channel_protocol,
+ request_payload_bytes),
+ &payload_bytes, sizeof(payload_bytes)) < 0) {
+ POSTCODE_LINUX_2(CONTROLVM_INIT_FAILURE_PC,
+ POSTCODE_SEVERITY_ERR);
+ return;
+ }
+ initialize_controlvm_payload_info(phys_addr,
+ payload_offset, payload_bytes,
+ &controlvm_payload_info);
+}
+
+/* Send ACTION=online for DEVPATH=/sys/devices/platform/visorchipset.
+ * Returns CONTROLVM_RESP_xxx code.
+ */
+int
+visorchipset_chipset_ready(void)
+{
+ kobject_uevent(&visorchipset_platform_device.dev.kobj, KOBJ_ONLINE);
+ return CONTROLVM_RESP_SUCCESS;
+}
+EXPORT_SYMBOL_GPL(visorchipset_chipset_ready);
+
+int
+visorchipset_chipset_selftest(void)
+{
+ char env_selftest[20];
+ char *envp[] = { env_selftest, NULL };
+
+ sprintf(env_selftest, "SPARSP_SELFTEST=%d", 1);
+ kobject_uevent_env(&visorchipset_platform_device.dev.kobj, KOBJ_CHANGE,
+ envp);
+ return CONTROLVM_RESP_SUCCESS;
+}
+EXPORT_SYMBOL_GPL(visorchipset_chipset_selftest);
+
+/* Send ACTION=offline for DEVPATH=/sys/devices/platform/visorchipset.
+ * Returns CONTROLVM_RESP_xxx code.
+ */
+int
+visorchipset_chipset_notready(void)
+{
+ kobject_uevent(&visorchipset_platform_device.dev.kobj, KOBJ_OFFLINE);
+ return CONTROLVM_RESP_SUCCESS;
+}
+EXPORT_SYMBOL_GPL(visorchipset_chipset_notready);
+
+static void
+chipset_ready(struct controlvm_message_header *msg_hdr)
+{
+ int rc = visorchipset_chipset_ready();
+
+ if (rc != CONTROLVM_RESP_SUCCESS)
+ rc = -rc;
+ if (msg_hdr->flags.response_expected && !visorchipset_holdchipsetready)
+ controlvm_respond(msg_hdr, rc);
+ if (msg_hdr->flags.response_expected && visorchipset_holdchipsetready) {
+ /* Send CHIPSET_READY response when all modules have been loaded
+ * and disks mounted for the partition
+ */
+ g_chipset_msg_hdr = *msg_hdr;
+ }
+}
+
+static void
+chipset_selftest(struct controlvm_message_header *msg_hdr)
+{
+ int rc = visorchipset_chipset_selftest();
+
+ if (rc != CONTROLVM_RESP_SUCCESS)
+ rc = -rc;
+ if (msg_hdr->flags.response_expected)
+ controlvm_respond(msg_hdr, rc);
+}
+
+static void
+chipset_notready(struct controlvm_message_header *msg_hdr)
+{
+ int rc = visorchipset_chipset_notready();
+
+ if (rc != CONTROLVM_RESP_SUCCESS)
+ rc = -rc;
+ if (msg_hdr->flags.response_expected)
+ controlvm_respond(msg_hdr, rc);
+}
+
+/* This is your "one-stop" shop for grabbing the next message from the
+ * CONTROLVM_QUEUE_EVENT queue in the controlvm channel.
+ */
+static BOOL
+read_controlvm_event(struct controlvm_message *msg)
+{
+ if (visorchannel_signalremove(controlvm_channel,
+ CONTROLVM_QUEUE_EVENT, msg)) {
+ /* got a message */
+ if (msg->hdr.flags.test_message == 1)
+ return FALSE;
+ return TRUE;
+ }
+ return FALSE;
+}
+
+/*
+ * The general parahotplug flow works as follows. The visorchipset
+ * driver receives a DEVICE_CHANGESTATE message from Command
+ * specifying a physical device to enable or disable. The CONTROLVM
+ * message handler calls parahotplug_process_message, which then adds
+ * the message to a global list and kicks off a udev event which
+ * causes a user level script to enable or disable the specified
+ * device. The udev script then writes to
+ * /proc/visorchipset/parahotplug, which causes parahotplug_proc_write
+ * to get called, at which point the appropriate CONTROLVM message is
+ * retrieved from the list and responded to.
+ */
+
+#define PARAHOTPLUG_TIMEOUT_MS 2000
+
+/*
+ * Generate unique int to match an outstanding CONTROLVM message with a
+ * udev script /proc response
+ */
+static int
+parahotplug_next_id(void)
+{
+ static atomic_t id = ATOMIC_INIT(0);
+
+ return atomic_inc_return(&id);
+}
+
+/*
+ * Returns the time (in jiffies) when a CONTROLVM message on the list
+ * should expire -- PARAHOTPLUG_TIMEOUT_MS in the future
+ */
+static unsigned long
+parahotplug_next_expiration(void)
+{
+ return jiffies + msecs_to_jiffies(PARAHOTPLUG_TIMEOUT_MS);
+}
+
+/*
+ * Create a parahotplug_request, which is basically a wrapper for a
+ * CONTROLVM_MESSAGE that we can stick on a list
+ */
+static struct parahotplug_request *
+parahotplug_request_create(struct controlvm_message *msg)
+{
+ struct parahotplug_request *req;
+
+ req = kmalloc(sizeof(*req), GFP_KERNEL | __GFP_NORETRY);
+ if (!req)
+ return NULL;
+
+ req->id = parahotplug_next_id();
+ req->expiration = parahotplug_next_expiration();
+ req->msg = *msg;
+
+ return req;
+}
+
+/*
+ * Free a parahotplug_request.
+ */
+static void
+parahotplug_request_destroy(struct parahotplug_request *req)
+{
+ kfree(req);
+}
+
+/*
+ * Cause uevent to run the user level script to do the disable/enable
+ * specified in (the CONTROLVM message in) the specified
+ * parahotplug_request
+ */
+static void
+parahotplug_request_kickoff(struct parahotplug_request *req)
+{
+ struct controlvm_message_packet *cmd = &req->msg.cmd;
+ char env_cmd[40], env_id[40], env_state[40], env_bus[40], env_dev[40],
+ env_func[40];
+ char *envp[] = {
+ env_cmd, env_id, env_state, env_bus, env_dev, env_func, NULL
+ };
+
+ sprintf(env_cmd, "SPAR_PARAHOTPLUG=1");
+ sprintf(env_id, "SPAR_PARAHOTPLUG_ID=%d", req->id);
+ sprintf(env_state, "SPAR_PARAHOTPLUG_STATE=%d",
+ cmd->device_change_state.state.active);
+ sprintf(env_bus, "SPAR_PARAHOTPLUG_BUS=%d",
+ cmd->device_change_state.bus_no);
+ sprintf(env_dev, "SPAR_PARAHOTPLUG_DEVICE=%d",
+ cmd->device_change_state.dev_no >> 3);
+ sprintf(env_func, "SPAR_PARAHOTPLUG_FUNCTION=%d",
+ cmd->device_change_state.dev_no & 0x7);
+
+ kobject_uevent_env(&visorchipset_platform_device.dev.kobj, KOBJ_CHANGE,
+ envp);
+}
+
+/*
+ * Remove any request from the list that's been on there too long and
+ * respond with an error.
+ */
+static void
+parahotplug_process_list(void)
+{
+ struct list_head *pos = NULL;
+ struct list_head *tmp = NULL;
+
+ spin_lock(&parahotplug_request_list_lock);
+
+ list_for_each_safe(pos, tmp, &parahotplug_request_list) {
+ struct parahotplug_request *req =
+ list_entry(pos, struct parahotplug_request, list);
+
+ if (!time_after_eq(jiffies, req->expiration))
+ continue;
+
+ list_del(pos);
+ if (req->msg.hdr.flags.response_expected)
+ controlvm_respond_physdev_changestate(
+ &req->msg.hdr,
+ CONTROLVM_RESP_ERROR_DEVICE_UDEV_TIMEOUT,
+ req->msg.cmd.device_change_state.state);
+ parahotplug_request_destroy(req);
+ }
+
+ spin_unlock(&parahotplug_request_list_lock);
+}
+
+/*
+ * Called from the /proc handler, which means the user script has
+ * finished the enable/disable. Find the matching identifier, and
+ * respond to the CONTROLVM message with success.
+ */
+static int
+parahotplug_request_complete(int id, u16 active)
+{
+ struct list_head *pos = NULL;
+ struct list_head *tmp = NULL;
+
+ spin_lock(&parahotplug_request_list_lock);
+
+ /* Look for a request matching "id". */
+ list_for_each_safe(pos, tmp, &parahotplug_request_list) {
+ struct parahotplug_request *req =
+ list_entry(pos, struct parahotplug_request, list);
+ if (req->id == id) {
+ /* Found a match. Remove it from the list and
+ * respond.
+ */
+ list_del(pos);
+ spin_unlock(&parahotplug_request_list_lock);
+ req->msg.cmd.device_change_state.state.active = active;
+ if (req->msg.hdr.flags.response_expected)
+ controlvm_respond_physdev_changestate(
+ &req->msg.hdr, CONTROLVM_RESP_SUCCESS,
+ req->msg.cmd.device_change_state.state);
+ parahotplug_request_destroy(req);
+ return 0;
+ }
+ }
+
+ spin_unlock(&parahotplug_request_list_lock);
+ return -1;
+}
+
+/*
+ * Enables or disables a PCI device by kicking off a udev script
+ */
+static void
+parahotplug_process_message(struct controlvm_message *inmsg)
+{
+ struct parahotplug_request *req;
+
+ req = parahotplug_request_create(inmsg);
+
+ if (!req)
+ return;
+
+ if (inmsg->cmd.device_change_state.state.active) {
+ /* For enable messages, just respond with success
+ * right away. This is a bit of a hack, but there are
+ * issues with the early enable messages we get (with
+ * either the udev script not detecting that the device
+ * is up, or not getting called at all). Fortunately
+ * the messages that get lost don't matter anyway, as
+ * devices are automatically enabled at
+ * initialization.
+ */
+ parahotplug_request_kickoff(req);
+ controlvm_respond_physdev_changestate(&inmsg->hdr,
+ CONTROLVM_RESP_SUCCESS,
+ inmsg->cmd.device_change_state.state);
+ parahotplug_request_destroy(req);
+ } else {
+ /* For disable messages, add the request to the
+ * request list before kicking off the udev script. It
+ * won't get responded to until the script has
+ * indicated it's done.
+ */
+ spin_lock(&parahotplug_request_list_lock);
+ list_add_tail(&req->list, &parahotplug_request_list);
+ spin_unlock(&parahotplug_request_list_lock);
+
+ parahotplug_request_kickoff(req);
+ }
+}
+
+/* Process a controlvm message.
+ * Return result:
+ * FALSE - this function will return FALSE only in the case where the
+ * controlvm message was NOT processed, but processing must be
+ * retried before reading the next controlvm message; a
+ * scenario where this can occur is when we need to throttle
+ * the allocation of memory in which to copy out controlvm
+ * payload data
+ * TRUE - processing of the controlvm message completed,
+ * either successfully or with an error.
+ */
+static BOOL
+handle_command(struct controlvm_message inmsg, HOSTADDRESS channel_addr)
+{
+ struct controlvm_message_packet *cmd = &inmsg.cmd;
+ u64 parm_addr = 0;
+ u32 parm_bytes = 0;
+ struct parser_context *parser_ctx = NULL;
+ bool local_addr = false;
+ struct controlvm_message ackmsg;
+
+ /* create parsing context if necessary */
+ local_addr = (inmsg.hdr.flags.test_message == 1);
+ if (channel_addr == 0)
+ return TRUE;
+ parm_addr = channel_addr + inmsg.hdr.payload_vm_offset;
+ parm_bytes = inmsg.hdr.payload_bytes;
+
+ /* Parameter and channel addresses within test messages actually lie
+ * within our OS-controlled memory. We need to know that, because it
+ * makes a difference in how we compute the virtual address.
+ */
+ if (parm_addr != 0 && parm_bytes != 0) {
+ BOOL retry = FALSE;
+
+ parser_ctx =
+ parser_init_byte_stream(parm_addr, parm_bytes,
+ local_addr, &retry);
+ if (!parser_ctx && retry)
+ return FALSE;
+ }
+
+ if (!local_addr) {
+ controlvm_init_response(&ackmsg, &inmsg.hdr,
+ CONTROLVM_RESP_SUCCESS);
+ if (controlvm_channel)
+ visorchannel_signalinsert(controlvm_channel,
+ CONTROLVM_QUEUE_ACK,
+ &ackmsg);
+ }
+ switch (inmsg.hdr.id) {
+ case CONTROLVM_CHIPSET_INIT:
+ chipset_init(&inmsg);
+ break;
+ case CONTROLVM_BUS_CREATE:
+ bus_create(&inmsg);
+ break;
+ case CONTROLVM_BUS_DESTROY:
+ bus_destroy(&inmsg);
+ break;
+ case CONTROLVM_BUS_CONFIGURE:
+ bus_configure(&inmsg, parser_ctx);
+ break;
+ case CONTROLVM_DEVICE_CREATE:
+ my_device_create(&inmsg);
+ break;
+ case CONTROLVM_DEVICE_CHANGESTATE:
+ if (cmd->device_change_state.flags.phys_device) {
+ parahotplug_process_message(&inmsg);
+ } else {
+ /* save the hdr and cmd structures for later use */
+ /* when sending back the response to Command */
+ my_device_changestate(&inmsg);
+ g_diag_msg_hdr = inmsg.hdr;
+ g_devicechangestate_packet = inmsg.cmd;
+ break;
+ }
+ break;
+ case CONTROLVM_DEVICE_DESTROY:
+ my_device_destroy(&inmsg);
+ break;
+ case CONTROLVM_DEVICE_CONFIGURE:
+ /* no op for now, just send a respond that we passed */
+ if (inmsg.hdr.flags.response_expected)
+ controlvm_respond(&inmsg.hdr, CONTROLVM_RESP_SUCCESS);
+ break;
+ case CONTROLVM_CHIPSET_READY:
+ chipset_ready(&inmsg.hdr);
+ break;
+ case CONTROLVM_CHIPSET_SELFTEST:
+ chipset_selftest(&inmsg.hdr);
+ break;
+ case CONTROLVM_CHIPSET_STOP:
+ chipset_notready(&inmsg.hdr);
+ break;
+ default:
+ if (inmsg.hdr.flags.response_expected)
+ controlvm_respond(&inmsg.hdr,
+ -CONTROLVM_RESP_ERROR_MESSAGE_ID_UNKNOWN);
+ break;
+ }
+
+ if (parser_ctx) {
+ parser_done(parser_ctx);
+ parser_ctx = NULL;
+ }
+ return TRUE;
+}
+
+static HOSTADDRESS controlvm_get_channel_address(void)
+{
+ u64 addr = 0;
+ u32 size = 0;
+
+ if (!VMCALL_SUCCESSFUL(issue_vmcall_io_controlvm_addr(&addr, &size)))
+ return 0;
+
+ return addr;
+}
+
+static void
+controlvm_periodic_work(struct work_struct *work)
+{
+ struct controlvm_message inmsg;
+ BOOL got_command = FALSE;
+ BOOL handle_command_failed = FALSE;
+ static u64 poll_count;
+
+ /* make sure visorbus server is registered for controlvm callbacks */
+ if (visorchipset_serverregwait && !serverregistered)
+ goto cleanup;
+ /* make sure visorclientbus server is regsitered for controlvm
+ * callbacks
+ */
+ if (visorchipset_clientregwait && !clientregistered)
+ goto cleanup;
+
+ poll_count++;
+ if (poll_count >= 250)
+ ; /* keep going */
+ else
+ goto cleanup;
+
+ /* Check events to determine if response to CHIPSET_READY
+ * should be sent
+ */
+ if (visorchipset_holdchipsetready &&
+ (g_chipset_msg_hdr.id != CONTROLVM_INVALID)) {
+ if (check_chipset_events() == 1) {
+ controlvm_respond(&g_chipset_msg_hdr, 0);
+ clear_chipset_events();
+ memset(&g_chipset_msg_hdr, 0,
+ sizeof(struct controlvm_message_header));
+ }
+ }
+
+ while (visorchannel_signalremove(controlvm_channel,
+ CONTROLVM_QUEUE_RESPONSE,
+ &inmsg))
+ ;
+ if (!got_command) {
+ if (controlvm_pending_msg_valid) {
+ /* we throttled processing of a prior
+ * msg, so try to process it again
+ * rather than reading a new one
+ */
+ inmsg = controlvm_pending_msg;
+ controlvm_pending_msg_valid = FALSE;
+ got_command = true;
+ } else {
+ got_command = read_controlvm_event(&inmsg);
+ }
+ }
+
+ handle_command_failed = FALSE;
+ while (got_command && (!handle_command_failed)) {
+ most_recent_message_jiffies = jiffies;
+ if (handle_command(inmsg,
+ visorchannel_get_physaddr
+ (controlvm_channel)))
+ got_command = read_controlvm_event(&inmsg);
+ else {
+ /* this is a scenario where throttling
+ * is required, but probably NOT an
+ * error...; we stash the current
+ * controlvm msg so we will attempt to
+ * reprocess it on our next loop
+ */
+ handle_command_failed = TRUE;
+ controlvm_pending_msg = inmsg;
+ controlvm_pending_msg_valid = TRUE;
+ }
+ }
+
+ /* parahotplug_worker */
+ parahotplug_process_list();
+
+cleanup:
+
+ if (time_after(jiffies,
+ most_recent_message_jiffies + (HZ * MIN_IDLE_SECONDS))) {
+ /* it's been longer than MIN_IDLE_SECONDS since we
+ * processed our last controlvm message; slow down the
+ * polling
+ */
+ if (poll_jiffies != POLLJIFFIES_CONTROLVMCHANNEL_SLOW)
+ poll_jiffies = POLLJIFFIES_CONTROLVMCHANNEL_SLOW;
+ } else {
+ if (poll_jiffies != POLLJIFFIES_CONTROLVMCHANNEL_FAST)
+ poll_jiffies = POLLJIFFIES_CONTROLVMCHANNEL_FAST;
+ }
+
+ queue_delayed_work(periodic_controlvm_workqueue,
+ &periodic_controlvm_work, poll_jiffies);
+}
+
+static void
+setup_crash_devices_work_queue(struct work_struct *work)
+{
+ struct controlvm_message local_crash_bus_msg;
+ struct controlvm_message local_crash_dev_msg;
+ struct controlvm_message msg;
+ u32 local_crash_msg_offset;
+ u16 local_crash_msg_count;
+
+ /* make sure visorbus server is registered for controlvm callbacks */
+ if (visorchipset_serverregwait && !serverregistered)
+ goto cleanup;
+
+ /* make sure visorclientbus server is regsitered for controlvm
+ * callbacks
+ */
+ if (visorchipset_clientregwait && !clientregistered)
+ goto cleanup;
+
+ POSTCODE_LINUX_2(CRASH_DEV_ENTRY_PC, POSTCODE_SEVERITY_INFO);
+
+ /* send init chipset msg */
+ msg.hdr.id = CONTROLVM_CHIPSET_INIT;
+ msg.cmd.init_chipset.bus_count = 23;
+ msg.cmd.init_chipset.switch_count = 0;
+
+ chipset_init(&msg);
+
+ /* get saved message count */
+ if (visorchannel_read(controlvm_channel,
+ offsetof(struct spar_controlvm_channel_protocol,
+ saved_crash_message_count),
+ &local_crash_msg_count, sizeof(u16)) < 0) {
+ POSTCODE_LINUX_2(CRASH_DEV_CTRL_RD_FAILURE_PC,
+ POSTCODE_SEVERITY_ERR);
+ return;
+ }
+
+ if (local_crash_msg_count != CONTROLVM_CRASHMSG_MAX) {
+ POSTCODE_LINUX_3(CRASH_DEV_COUNT_FAILURE_PC,
+ local_crash_msg_count,
+ POSTCODE_SEVERITY_ERR);
+ return;
+ }
+
+ /* get saved crash message offset */
+ if (visorchannel_read(controlvm_channel,
+ offsetof(struct spar_controlvm_channel_protocol,
+ saved_crash_message_offset),
+ &local_crash_msg_offset, sizeof(u32)) < 0) {
+ POSTCODE_LINUX_2(CRASH_DEV_CTRL_RD_FAILURE_PC,
+ POSTCODE_SEVERITY_ERR);
+ return;
+ }
+
+ /* read create device message for storage bus offset */
+ if (visorchannel_read(controlvm_channel,
+ local_crash_msg_offset,
+ &local_crash_bus_msg,
+ sizeof(struct controlvm_message)) < 0) {
+ POSTCODE_LINUX_2(CRASH_DEV_RD_BUS_FAIULRE_PC,
+ POSTCODE_SEVERITY_ERR);
+ return;
+ }
+
+ /* read create device message for storage device */
+ if (visorchannel_read(controlvm_channel,
+ local_crash_msg_offset +
+ sizeof(struct controlvm_message),
+ &local_crash_dev_msg,
+ sizeof(struct controlvm_message)) < 0) {
+ POSTCODE_LINUX_2(CRASH_DEV_RD_DEV_FAIULRE_PC,
+ POSTCODE_SEVERITY_ERR);
+ return;
+ }
+
+ /* reuse IOVM create bus message */
+ if (local_crash_bus_msg.cmd.create_bus.channel_addr != 0) {
+ bus_create(&local_crash_bus_msg);
+ } else {
+ POSTCODE_LINUX_2(CRASH_DEV_BUS_NULL_FAILURE_PC,
+ POSTCODE_SEVERITY_ERR);
+ return;
+ }
+
+ /* reuse create device message for storage device */
+ if (local_crash_dev_msg.cmd.create_device.channel_addr != 0) {
+ my_device_create(&local_crash_dev_msg);
+ } else {
+ POSTCODE_LINUX_2(CRASH_DEV_DEV_NULL_FAILURE_PC,
+ POSTCODE_SEVERITY_ERR);
+ return;
+ }
+ POSTCODE_LINUX_2(CRASH_DEV_EXIT_PC, POSTCODE_SEVERITY_INFO);
+ return;
+
+cleanup:
+
+ poll_jiffies = POLLJIFFIES_CONTROLVMCHANNEL_SLOW;
+
+ queue_delayed_work(periodic_controlvm_workqueue,
+ &periodic_controlvm_work, poll_jiffies);
+}
+
+static void
+bus_create_response(ulong bus_no, int response)
+{
+ bus_responder(CONTROLVM_BUS_CREATE, bus_no, response);
+}
+
+static void
+bus_destroy_response(ulong bus_no, int response)
+{
+ bus_responder(CONTROLVM_BUS_DESTROY, bus_no, response);
+}
+
+static void
+device_create_response(ulong bus_no, ulong dev_no, int response)
+{
+ device_responder(CONTROLVM_DEVICE_CREATE, bus_no, dev_no, response);
+}
+
+static void
+device_destroy_response(ulong bus_no, ulong dev_no, int response)
+{
+ device_responder(CONTROLVM_DEVICE_DESTROY, bus_no, dev_no, response);
+}
+
+void
+visorchipset_device_pause_response(ulong bus_no, ulong dev_no, int response)
+{
+ device_changestate_responder(CONTROLVM_DEVICE_CHANGESTATE,
+ bus_no, dev_no, response,
+ segment_state_standby);
+}
+EXPORT_SYMBOL_GPL(visorchipset_device_pause_response);
+
+static void
+device_resume_response(ulong bus_no, ulong dev_no, int response)
+{
+ device_changestate_responder(CONTROLVM_DEVICE_CHANGESTATE,
+ bus_no, dev_no, response,
+ segment_state_running);
+}
+
+BOOL
+visorchipset_get_bus_info(ulong bus_no, struct visorchipset_bus_info *bus_info)
+{
+ void *p = findbus(&bus_info_list, bus_no);
+
+ if (!p)
+ return FALSE;
+ memcpy(bus_info, p, sizeof(struct visorchipset_bus_info));
+ return TRUE;
+}
+EXPORT_SYMBOL_GPL(visorchipset_get_bus_info);
+
+BOOL
+visorchipset_set_bus_context(ulong bus_no, void *context)
+{
+ struct visorchipset_bus_info *p = findbus(&bus_info_list, bus_no);
+
+ if (!p)
+ return FALSE;
+ p->bus_driver_context = context;
+ return TRUE;
+}
+EXPORT_SYMBOL_GPL(visorchipset_set_bus_context);
+
+BOOL
+visorchipset_get_device_info(ulong bus_no, ulong dev_no,
+ struct visorchipset_device_info *dev_info)
+{
+ void *p = finddevice(&dev_info_list, bus_no, dev_no);
+
+ if (!p)
+ return FALSE;
+ memcpy(dev_info, p, sizeof(struct visorchipset_device_info));
+ return TRUE;
+}
+EXPORT_SYMBOL_GPL(visorchipset_get_device_info);
+
+BOOL
+visorchipset_set_device_context(ulong bus_no, ulong dev_no, void *context)
+{
+ struct visorchipset_device_info *p =
+ finddevice(&dev_info_list, bus_no, dev_no);
+
+ if (!p)
+ return FALSE;
+ p->bus_driver_context = context;
+ return TRUE;
+}
+EXPORT_SYMBOL_GPL(visorchipset_set_device_context);
+
+/* Generic wrapper function for allocating memory from a kmem_cache pool.
+ */
+void *
+visorchipset_cache_alloc(struct kmem_cache *pool, BOOL ok_to_block,
+ char *fn, int ln)
+{
+ gfp_t gfp;
+ void *p;
+
+ if (ok_to_block)
+ gfp = GFP_KERNEL;
+ else
+ gfp = GFP_ATOMIC;
+ /* __GFP_NORETRY means "ok to fail", meaning
+ * kmem_cache_alloc() can return NULL, implying the caller CAN
+ * cope with failure. If you do NOT specify __GFP_NORETRY,
+ * Linux will go to extreme measures to get memory for you
+ * (like, invoke oom killer), which will probably cripple the
+ * system.
+ */
+ gfp |= __GFP_NORETRY;
+ p = kmem_cache_alloc(pool, gfp);
+ if (!p)
+ return NULL;
+
+ atomic_inc(&visorchipset_cache_buffers_in_use);
+ return p;
+}
+
+/* Generic wrapper function for freeing memory from a kmem_cache pool.
+ */
+void
+visorchipset_cache_free(struct kmem_cache *pool, void *p, char *fn, int ln)
+{
+ if (!p)
+ return;
+
+ atomic_dec(&visorchipset_cache_buffers_in_use);
+ kmem_cache_free(pool, p);
+}
+
+static ssize_t chipsetready_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ char msgtype[64];
+
+ if (sscanf(buf, "%63s", msgtype) != 1)
+ return -EINVAL;
+
+ if (strcmp(msgtype, "CALLHOMEDISK_MOUNTED") == 0) {
+ chipset_events[0] = 1;
+ return count;
+ } else if (strcmp(msgtype, "MODULES_LOADED") == 0) {
+ chipset_events[1] = 1;
+ return count;
+ }
+ return -EINVAL;
+}
+
+/* The parahotplug/devicedisabled interface gets called by our support script
+ * when an SR-IOV device has been shut down. The ID is passed to the script
+ * and then passed back when the device has been removed.
+ */
+static ssize_t devicedisabled_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ uint id;
+
+ if (kstrtouint(buf, 10, &id) != 0)
+ return -EINVAL;
+
+ parahotplug_request_complete(id, 0);
+ return count;
+}
+
+/* The parahotplug/deviceenabled interface gets called by our support script
+ * when an SR-IOV device has been recovered. The ID is passed to the script
+ * and then passed back when the device has been brought back up.
+ */
+static ssize_t deviceenabled_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ uint id;
+
+ if (kstrtouint(buf, 10, &id) != 0)
+ return -EINVAL;
+
+ parahotplug_request_complete(id, 1);
+ return count;
+}
+
+static int __init
+visorchipset_init(void)
+{
+ int rc = 0, x = 0;
+ HOSTADDRESS addr;
+
+ if (!unisys_spar_platform)
+ return -ENODEV;
+
+ memset(&busdev_server_notifiers, 0, sizeof(busdev_server_notifiers));
+ memset(&busdev_client_notifiers, 0, sizeof(busdev_client_notifiers));
+ memset(&controlvm_payload_info, 0, sizeof(controlvm_payload_info));
+ memset(&livedump_info, 0, sizeof(livedump_info));
+ atomic_set(&livedump_info.buffers_in_use, 0);
+
+ if (visorchipset_testvnic) {
+ POSTCODE_LINUX_3(CHIPSET_INIT_FAILURE_PC, x, DIAG_SEVERITY_ERR);
+ rc = x;
+ goto cleanup;
+ }
+
+ addr = controlvm_get_channel_address();
+ if (addr != 0) {
+ controlvm_channel =
+ visorchannel_create_with_lock
+ (addr,
+ sizeof(struct spar_controlvm_channel_protocol),
+ spar_controlvm_channel_protocol_uuid);
+ if (SPAR_CONTROLVM_CHANNEL_OK_CLIENT(
+ visorchannel_get_header(controlvm_channel))) {
+ initialize_controlvm_payload();
+ } else {
+ visorchannel_destroy(controlvm_channel);
+ controlvm_channel = NULL;
+ return -ENODEV;
+ }
+ } else {
+ return -ENODEV;
+ }
+
+ major_dev = MKDEV(visorchipset_major, 0);
+ rc = visorchipset_file_init(major_dev, &controlvm_channel);
+ if (rc < 0) {
+ POSTCODE_LINUX_2(CHIPSET_INIT_FAILURE_PC, DIAG_SEVERITY_ERR);
+ goto cleanup;
+ }
+
+ memset(&g_diag_msg_hdr, 0, sizeof(struct controlvm_message_header));
+
+ memset(&g_chipset_msg_hdr, 0, sizeof(struct controlvm_message_header));
+
+ memset(&g_del_dump_msg_hdr, 0, sizeof(struct controlvm_message_header));
+
+ putfile_buffer_list_pool =
+ kmem_cache_create(putfile_buffer_list_pool_name,
+ sizeof(struct putfile_buffer_entry),
+ 0, SLAB_HWCACHE_ALIGN, NULL);
+ if (!putfile_buffer_list_pool) {
+ POSTCODE_LINUX_2(CHIPSET_INIT_FAILURE_PC, DIAG_SEVERITY_ERR);
+ rc = -1;
+ goto cleanup;
+ }
+ if (!visorchipset_disable_controlvm) {
+ /* if booting in a crash kernel */
+ if (visorchipset_crash_kernel)
+ INIT_DELAYED_WORK(&periodic_controlvm_work,
+ setup_crash_devices_work_queue);
+ else
+ INIT_DELAYED_WORK(&periodic_controlvm_work,
+ controlvm_periodic_work);
+ periodic_controlvm_workqueue =
+ create_singlethread_workqueue("visorchipset_controlvm");
+
+ if (!periodic_controlvm_workqueue) {
+ POSTCODE_LINUX_2(CREATE_WORKQUEUE_FAILED_PC,
+ DIAG_SEVERITY_ERR);
+ rc = -ENOMEM;
+ goto cleanup;
+ }
+ most_recent_message_jiffies = jiffies;
+ poll_jiffies = POLLJIFFIES_CONTROLVMCHANNEL_FAST;
+ rc = queue_delayed_work(periodic_controlvm_workqueue,
+ &periodic_controlvm_work, poll_jiffies);
+ if (rc < 0) {
+ POSTCODE_LINUX_2(QUEUE_DELAYED_WORK_PC,
+ DIAG_SEVERITY_ERR);
+ goto cleanup;
+ }
+ }
+
+ visorchipset_platform_device.dev.devt = major_dev;
+ if (platform_device_register(&visorchipset_platform_device) < 0) {
+ POSTCODE_LINUX_2(DEVICE_REGISTER_FAILURE_PC, DIAG_SEVERITY_ERR);
+ rc = -1;
+ goto cleanup;
+ }
+ POSTCODE_LINUX_2(CHIPSET_INIT_SUCCESS_PC, POSTCODE_SEVERITY_INFO);
+ rc = 0;
+cleanup:
+ if (rc) {
+ POSTCODE_LINUX_3(CHIPSET_INIT_FAILURE_PC, rc,
+ POSTCODE_SEVERITY_ERR);
+ }
+ return rc;
+}
+
+static void
+visorchipset_exit(void)
+{
+ POSTCODE_LINUX_2(DRIVER_EXIT_PC, POSTCODE_SEVERITY_INFO);
+
+ if (visorchipset_disable_controlvm) {
+ ;
+ } else {
+ cancel_delayed_work(&periodic_controlvm_work);
+ flush_workqueue(periodic_controlvm_workqueue);
+ destroy_workqueue(periodic_controlvm_workqueue);
+ periodic_controlvm_workqueue = NULL;
+ destroy_controlvm_payload_info(&controlvm_payload_info);
+ }
+ if (putfile_buffer_list_pool) {
+ kmem_cache_destroy(putfile_buffer_list_pool);
+ putfile_buffer_list_pool = NULL;
+ }
+
+ cleanup_controlvm_structures();
+
+ memset(&g_diag_msg_hdr, 0, sizeof(struct controlvm_message_header));
+
+ memset(&g_chipset_msg_hdr, 0, sizeof(struct controlvm_message_header));
+
+ memset(&g_del_dump_msg_hdr, 0, sizeof(struct controlvm_message_header));
+
+ visorchannel_destroy(controlvm_channel);
+
+ visorchipset_file_cleanup(visorchipset_platform_device.dev.devt);
+ POSTCODE_LINUX_2(DRIVER_EXIT_PC, POSTCODE_SEVERITY_INFO);
+}
+
+module_param_named(testvnic, visorchipset_testvnic, int, S_IRUGO);
+MODULE_PARM_DESC(visorchipset_testvnic, "1 to test vnic, using dummy VNIC connected via a loopback to a physical ethernet");
+int visorchipset_testvnic = 0;
+
+module_param_named(testvnicclient, visorchipset_testvnicclient, int, S_IRUGO);
+MODULE_PARM_DESC(visorchipset_testvnicclient, "1 to test vnic, using real VNIC channel attached to a separate IOVM guest");
+int visorchipset_testvnicclient = 0;
+
+module_param_named(testmsg, visorchipset_testmsg, int, S_IRUGO);
+MODULE_PARM_DESC(visorchipset_testmsg,
+ "1 to manufacture the chipset, bus, and switch messages");
+int visorchipset_testmsg = 0;
+
+module_param_named(major, visorchipset_major, int, S_IRUGO);
+MODULE_PARM_DESC(visorchipset_major, "major device number to use for the device node");
+int visorchipset_major = 0;
+
+module_param_named(serverregwait, visorchipset_serverregwait, int, S_IRUGO);
+MODULE_PARM_DESC(visorchipset_serverreqwait,
+ "1 to have the module wait for the visor bus to register");
+int visorchipset_serverregwait = 0; /* default is off */
+module_param_named(clientregwait, visorchipset_clientregwait, int, S_IRUGO);
+MODULE_PARM_DESC(visorchipset_clientregwait, "1 to have the module wait for the visorclientbus to register");
+int visorchipset_clientregwait = 1; /* default is on */
+module_param_named(testteardown, visorchipset_testteardown, int, S_IRUGO);
+MODULE_PARM_DESC(visorchipset_testteardown,
+ "1 to test teardown of the chipset, bus, and switch");
+int visorchipset_testteardown = 0; /* default is off */
+module_param_named(disable_controlvm, visorchipset_disable_controlvm, int,
+ S_IRUGO);
+MODULE_PARM_DESC(visorchipset_disable_controlvm,
+ "1 to disable polling of controlVm channel");
+int visorchipset_disable_controlvm = 0; /* default is off */
+module_param_named(crash_kernel, visorchipset_crash_kernel, int, S_IRUGO);
+MODULE_PARM_DESC(visorchipset_crash_kernel,
+ "1 means we are running in crash kernel");
+int visorchipset_crash_kernel = 0; /* default is running in non-crash kernel */
+module_param_named(holdchipsetready, visorchipset_holdchipsetready,
+ int, S_IRUGO);
+MODULE_PARM_DESC(visorchipset_holdchipsetready,
+ "1 to hold response to CHIPSET_READY");
+int visorchipset_holdchipsetready = 0; /* default is to send CHIPSET_READY
+ * response immediately */
+module_init(visorchipset_init);
+module_exit(visorchipset_exit);
+
+MODULE_AUTHOR("Unisys");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Supervisor chipset driver for service partition: ver "
+ VERSION);
+MODULE_VERSION(VERSION);
diff --git a/drivers/staging/unisys/visorchipset/visorchipset_umode.h b/drivers/staging/unisys/visorchipset/visorchipset_umode.h
new file mode 100644
index 000000000..6cf6eccb3
--- /dev/null
+++ b/drivers/staging/unisys/visorchipset/visorchipset_umode.h
@@ -0,0 +1,35 @@
+/* visorchipset_umode.h
+ *
+ * Copyright (C) 2010 - 2013 UNISYS CORPORATION
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or (at
+ * your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
+ * NON INFRINGEMENT. See the GNU General Public License for more
+ * details.
+ */
+
+/** @file *********************************************************************
+ *
+ * This describes structures needed for the interface between the
+ * visorchipset driver and a user-mode component that opens the device.
+ *
+ ******************************************************************************
+ */
+
+#ifndef __VISORCHIPSET_UMODE_H
+#define __VISORCHIPSET_UMODE_H
+
+/** The user-mode program can access the control channel buffer directly
+ * via this memory map.
+ */
+#define VISORCHIPSET_MMAP_CONTROLCHANOFFSET (0x00000000)
+#define VISORCHIPSET_MMAP_CONTROLCHANSIZE (0x00400000) /* 4MB */
+
+#endif /* __VISORCHIPSET_UMODE_H */
diff --git a/drivers/staging/unisys/visorutil/Kconfig b/drivers/staging/unisys/visorutil/Kconfig
new file mode 100644
index 000000000..be9c2cf89
--- /dev/null
+++ b/drivers/staging/unisys/visorutil/Kconfig
@@ -0,0 +1,9 @@
+#
+# Unisys timskmod configuration
+#
+
+config UNISYS_VISORUTIL
+ tristate "Unisys visorutil driver"
+ ---help---
+ If you say Y here, you will enable the Unisys visorutil driver.
+
diff --git a/drivers/staging/unisys/visorutil/Makefile b/drivers/staging/unisys/visorutil/Makefile
new file mode 100644
index 000000000..d9ab5a36e
--- /dev/null
+++ b/drivers/staging/unisys/visorutil/Makefile
@@ -0,0 +1,9 @@
+#
+# Makefile for Unisys timskmod
+#
+
+obj-$(CONFIG_UNISYS_VISORUTIL) += visorutil.o
+
+visorutil-y := charqueue.o periodic_work.o memregion_direct.o visorkmodutils.o
+
+ccflags-y += -Idrivers/staging/unisys/include
diff --git a/drivers/staging/unisys/visorutil/charqueue.c b/drivers/staging/unisys/visorutil/charqueue.c
new file mode 100644
index 000000000..c91752a2d
--- /dev/null
+++ b/drivers/staging/unisys/visorutil/charqueue.c
@@ -0,0 +1,127 @@
+/* charqueue.c
+ *
+ * Copyright (C) 2010 - 2013 UNISYS CORPORATION
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or (at
+ * your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
+ * NON INFRINGEMENT. See the GNU General Public License for more
+ * details.
+ */
+
+/*
+ * Simple character queue implementation for Linux kernel mode.
+ */
+
+#include "charqueue.h"
+
+#define MYDRVNAME "charqueue"
+
+#define IS_EMPTY(charqueue) (charqueue->head == charqueue->tail)
+
+struct charqueue {
+ int alloc_size;
+ int nslots;
+ spinlock_t lock; /* read/write lock for this structure */
+ int head, tail;
+ unsigned char buf[0];
+};
+
+struct charqueue *visor_charqueue_create(ulong nslots)
+{
+ int alloc_size = sizeof(struct charqueue) + nslots + 1;
+ struct charqueue *cq;
+
+ cq = kmalloc(alloc_size, GFP_KERNEL|__GFP_NORETRY);
+ if (cq == NULL)
+ return NULL;
+ cq->alloc_size = alloc_size;
+ cq->nslots = nslots;
+ cq->head = 0;
+ cq->tail = 0;
+ spin_lock_init(&cq->lock);
+ return cq;
+}
+EXPORT_SYMBOL_GPL(visor_charqueue_create);
+
+void visor_charqueue_enqueue(struct charqueue *charqueue, unsigned char c)
+{
+ int alloc_slots = charqueue->nslots+1; /* 1 slot is always empty */
+
+ spin_lock(&charqueue->lock);
+ charqueue->head = (charqueue->head+1) % alloc_slots;
+ if (charqueue->head == charqueue->tail)
+ /* overflow; overwrite the oldest entry */
+ charqueue->tail = (charqueue->tail+1) % alloc_slots;
+ charqueue->buf[charqueue->head] = c;
+ spin_unlock(&charqueue->lock);
+}
+EXPORT_SYMBOL_GPL(visor_charqueue_enqueue);
+
+BOOL visor_charqueue_is_empty(struct charqueue *charqueue)
+{
+ BOOL b;
+
+ spin_lock(&charqueue->lock);
+ b = IS_EMPTY(charqueue);
+ spin_unlock(&charqueue->lock);
+ return b;
+}
+EXPORT_SYMBOL_GPL(visor_charqueue_is_empty);
+
+static int charqueue_dequeue_1(struct charqueue *charqueue)
+{
+ int alloc_slots = charqueue->nslots + 1; /* 1 slot is always empty */
+
+ if (IS_EMPTY(charqueue))
+ return -1;
+ charqueue->tail = (charqueue->tail+1) % alloc_slots;
+ return charqueue->buf[charqueue->tail];
+}
+
+int charqueue_dequeue(struct charqueue *charqueue)
+{
+ int rc;
+
+ spin_lock(&charqueue->lock);
+ rc = charqueue_dequeue_1(charqueue);
+ spin_unlock(&charqueue->lock);
+ return rc;
+}
+
+int visor_charqueue_dequeue_n(struct charqueue *charqueue, unsigned char *buf,
+ int n)
+{
+ int rc, counter = 0, c;
+
+ spin_lock(&charqueue->lock);
+ for (;;) {
+ if (n <= 0)
+ break; /* no more buffer space */
+ c = charqueue_dequeue_1(charqueue);
+ if (c < 0)
+ break; /* no more input */
+ *buf = (unsigned char)(c);
+ buf++;
+ n--;
+ counter++;
+ }
+ rc = counter;
+ spin_unlock(&charqueue->lock);
+ return rc;
+}
+EXPORT_SYMBOL_GPL(visor_charqueue_dequeue_n);
+
+void visor_charqueue_destroy(struct charqueue *charqueue)
+{
+ if (charqueue == NULL)
+ return;
+ kfree(charqueue);
+}
+EXPORT_SYMBOL_GPL(visor_charqueue_destroy);
diff --git a/drivers/staging/unisys/visorutil/charqueue.h b/drivers/staging/unisys/visorutil/charqueue.h
new file mode 100644
index 000000000..f46a776b9
--- /dev/null
+++ b/drivers/staging/unisys/visorutil/charqueue.h
@@ -0,0 +1,37 @@
+/* charqueue.h
+ *
+ * Copyright (C) 2010 - 2013 UNISYS CORPORATION
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or (at
+ * your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
+ * NON INFRINGEMENT. See the GNU General Public License for more
+ * details.
+ */
+
+#ifndef __CHARQUEUE_H__
+#define __CHARQUEUE_H__
+
+#include "timskmod.h"
+
+/* struct charqueue is an opaque structure to users.
+ * Fields are declared only in the implementation .c files.
+ */
+struct charqueue;
+
+struct charqueue *visor_charqueue_create(ulong nslots);
+void visor_charqueue_enqueue(struct charqueue *charqueue, unsigned char c);
+int charqueue_dequeue(struct charqueue *charqueue);
+int visor_charqueue_dequeue_n(struct charqueue *charqueue, unsigned char *buf,
+ int n);
+BOOL visor_charqueue_is_empty(struct charqueue *charqueue);
+void visor_charqueue_destroy(struct charqueue *charqueue);
+
+#endif
+
diff --git a/drivers/staging/unisys/visorutil/memregion.h b/drivers/staging/unisys/visorutil/memregion.h
new file mode 100644
index 000000000..0c3eebcf6
--- /dev/null
+++ b/drivers/staging/unisys/visorutil/memregion.h
@@ -0,0 +1,43 @@
+/* memregion.h
+ *
+ * Copyright (C) 2010 - 2013 UNISYS CORPORATION
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or (at
+ * your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
+ * NON INFRINGEMENT. See the GNU General Public License for more
+ * details.
+ */
+
+#ifndef __MEMREGION_H__
+#define __MEMREGION_H__
+
+#include "timskmod.h"
+
+/* struct memregion is an opaque structure to users.
+ * Fields are declared only in the implementation .c files.
+ */
+struct memregion;
+
+struct memregion *visor_memregion_create(HOSTADDRESS physaddr, ulong nbytes);
+struct memregion *visor_memregion_create_overlapped(struct memregion *parent,
+ ulong offset, ulong nbytes);
+int visor_memregion_resize(struct memregion *memregion, ulong newsize);
+int visor_memregion_read(struct memregion *memregion,
+ ulong offset, void *dest, ulong nbytes);
+int visor_memregion_write(struct memregion *memregion,
+ ulong offset, void *src, ulong nbytes);
+void visor_memregion_destroy(struct memregion *memregion);
+HOSTADDRESS visor_memregion_get_physaddr(struct memregion *memregion);
+ulong visor_memregion_get_nbytes(struct memregion *memregion);
+void memregion_dump(struct memregion *memregion, char *s,
+ ulong off, ulong len, struct seq_file *seq);
+void __iomem *visor_memregion_get_pointer(struct memregion *memregion);
+
+#endif
diff --git a/drivers/staging/unisys/visorutil/memregion_direct.c b/drivers/staging/unisys/visorutil/memregion_direct.c
new file mode 100644
index 000000000..eb7422fbe
--- /dev/null
+++ b/drivers/staging/unisys/visorutil/memregion_direct.c
@@ -0,0 +1,207 @@
+/* memregion_direct.c
+ *
+ * Copyright (C) 2010 - 2013 UNISYS CORPORATION
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or (at
+ * your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
+ * NON INFRINGEMENT. See the GNU General Public License for more
+ * details.
+ */
+
+/*
+ * This is an implementation of memory regions that can be used to read/write
+ * channel memory (in main memory of the host system) from code running in
+ * a virtual partition.
+ */
+#include "timskmod.h"
+#include "memregion.h"
+
+#define MYDRVNAME "memregion"
+
+struct memregion {
+ HOSTADDRESS physaddr;
+ ulong nbytes;
+ void __iomem *mapped;
+ BOOL requested;
+ BOOL overlapped;
+};
+
+static BOOL mapit(struct memregion *memregion);
+static void unmapit(struct memregion *memregion);
+
+struct memregion *
+visor_memregion_create(HOSTADDRESS physaddr, ulong nbytes)
+{
+ struct memregion *rc = NULL;
+ struct memregion *memregion;
+
+ memregion = kzalloc(sizeof(*memregion), GFP_KERNEL | __GFP_NORETRY);
+ if (memregion == NULL)
+ return NULL;
+
+ memregion->physaddr = physaddr;
+ memregion->nbytes = nbytes;
+ memregion->overlapped = FALSE;
+ if (!mapit(memregion)) {
+ rc = NULL;
+ goto cleanup;
+ }
+ rc = memregion;
+cleanup:
+ if (rc == NULL) {
+ visor_memregion_destroy(memregion);
+ memregion = NULL;
+ }
+ return rc;
+}
+EXPORT_SYMBOL_GPL(visor_memregion_create);
+
+struct memregion *
+visor_memregion_create_overlapped(struct memregion *parent, ulong offset,
+ ulong nbytes)
+{
+ struct memregion *memregion = NULL;
+
+ if (parent == NULL)
+ return NULL;
+
+ if (parent->mapped == NULL)
+ return NULL;
+
+ if ((offset >= parent->nbytes) ||
+ ((offset + nbytes) >= parent->nbytes))
+ return NULL;
+
+ memregion = kzalloc(sizeof(*memregion), GFP_KERNEL|__GFP_NORETRY);
+ if (memregion == NULL)
+ return NULL;
+
+ memregion->physaddr = parent->physaddr + offset;
+ memregion->nbytes = nbytes;
+ memregion->mapped = ((u8 __iomem *)(parent->mapped)) + offset;
+ memregion->requested = FALSE;
+ memregion->overlapped = TRUE;
+ return memregion;
+}
+EXPORT_SYMBOL_GPL(visor_memregion_create_overlapped);
+
+static BOOL
+mapit(struct memregion *memregion)
+{
+ ulong physaddr = (ulong)(memregion->physaddr);
+ ulong nbytes = memregion->nbytes;
+
+ memregion->requested = FALSE;
+ if (request_mem_region(physaddr, nbytes, MYDRVNAME))
+ memregion->requested = TRUE;
+ memregion->mapped = ioremap_cache(physaddr, nbytes);
+ if (!memregion->mapped)
+ return FALSE;
+ return TRUE;
+}
+
+static void
+unmapit(struct memregion *memregion)
+{
+ if (memregion->mapped != NULL) {
+ iounmap(memregion->mapped);
+ memregion->mapped = NULL;
+ }
+ if (memregion->requested) {
+ release_mem_region((ulong)(memregion->physaddr),
+ memregion->nbytes);
+ memregion->requested = FALSE;
+ }
+}
+
+HOSTADDRESS
+visor_memregion_get_physaddr(struct memregion *memregion)
+{
+ return memregion->physaddr;
+}
+EXPORT_SYMBOL_GPL(visor_memregion_get_physaddr);
+
+ulong
+visor_memregion_get_nbytes(struct memregion *memregion)
+{
+ return memregion->nbytes;
+}
+EXPORT_SYMBOL_GPL(visor_memregion_get_nbytes);
+
+void __iomem *
+visor_memregion_get_pointer(struct memregion *memregion)
+{
+ return memregion->mapped;
+}
+EXPORT_SYMBOL_GPL(visor_memregion_get_pointer);
+
+int
+visor_memregion_resize(struct memregion *memregion, ulong newsize)
+{
+ if (newsize == memregion->nbytes)
+ return 0;
+ if (memregion->overlapped)
+ /* no error check here - we no longer know the
+ * parent's range!
+ */
+ memregion->nbytes = newsize;
+ else {
+ unmapit(memregion);
+ memregion->nbytes = newsize;
+ if (!mapit(memregion))
+ return -1;
+ }
+ return 0;
+}
+EXPORT_SYMBOL_GPL(visor_memregion_resize);
+
+static int
+memregion_readwrite(BOOL is_write,
+ struct memregion *memregion, ulong offset,
+ void *local, ulong nbytes)
+{
+ if (offset + nbytes > memregion->nbytes)
+ return -EIO;
+
+ if (is_write)
+ memcpy_toio(memregion->mapped + offset, local, nbytes);
+ else
+ memcpy_fromio(local, memregion->mapped + offset, nbytes);
+
+ return 0;
+}
+
+int
+visor_memregion_read(struct memregion *memregion, ulong offset, void *dest,
+ ulong nbytes)
+{
+ return memregion_readwrite(FALSE, memregion, offset, dest, nbytes);
+}
+EXPORT_SYMBOL_GPL(visor_memregion_read);
+
+int
+visor_memregion_write(struct memregion *memregion, ulong offset, void *src,
+ ulong nbytes)
+{
+ return memregion_readwrite(TRUE, memregion, offset, src, nbytes);
+}
+EXPORT_SYMBOL_GPL(visor_memregion_write);
+
+void
+visor_memregion_destroy(struct memregion *memregion)
+{
+ if (memregion == NULL)
+ return;
+ if (!memregion->overlapped)
+ unmapit(memregion);
+ kfree(memregion);
+}
+EXPORT_SYMBOL_GPL(visor_memregion_destroy);
+
diff --git a/drivers/staging/unisys/visorutil/periodic_work.c b/drivers/staging/unisys/visorutil/periodic_work.c
new file mode 100644
index 000000000..abbfb4889
--- /dev/null
+++ b/drivers/staging/unisys/visorutil/periodic_work.c
@@ -0,0 +1,204 @@
+/* periodic_work.c
+ *
+ * Copyright (C) 2010 - 2013 UNISYS CORPORATION
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or (at
+ * your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
+ * NON INFRINGEMENT. See the GNU General Public License for more
+ * details.
+ */
+
+/*
+ * Helper functions to schedule periodic work in Linux kernel mode.
+ */
+
+#include "timskmod.h"
+#include "periodic_work.h"
+
+#define MYDRVNAME "periodic_work"
+
+struct periodic_work {
+ rwlock_t lock;
+ struct delayed_work work;
+ void (*workfunc)(void *);
+ void *workfuncarg;
+ BOOL is_scheduled;
+ BOOL want_to_stop;
+ ulong jiffy_interval;
+ struct workqueue_struct *workqueue;
+ const char *devnam;
+};
+
+static void periodic_work_func(struct work_struct *work)
+{
+ struct periodic_work *pw;
+
+ pw = container_of(work, struct periodic_work, work.work);
+ (*pw->workfunc)(pw->workfuncarg);
+}
+
+struct periodic_work *visor_periodic_work_create(ulong jiffy_interval,
+ struct workqueue_struct *workqueue,
+ void (*workfunc)(void *),
+ void *workfuncarg,
+ const char *devnam)
+{
+ struct periodic_work *pw;
+
+ pw = kzalloc(sizeof(*pw), GFP_KERNEL | __GFP_NORETRY);
+ if (!pw)
+ return NULL;
+
+ rwlock_init(&pw->lock);
+ pw->jiffy_interval = jiffy_interval;
+ pw->workqueue = workqueue;
+ pw->workfunc = workfunc;
+ pw->workfuncarg = workfuncarg;
+ pw->devnam = devnam;
+ return pw;
+}
+EXPORT_SYMBOL_GPL(visor_periodic_work_create);
+
+void visor_periodic_work_destroy(struct periodic_work *pw)
+{
+ kfree(pw);
+}
+EXPORT_SYMBOL_GPL(visor_periodic_work_destroy);
+
+/** Call this from your periodic work worker function to schedule the next
+ * call.
+ * If this function returns FALSE, there was a failure and the
+ * periodic work is no longer scheduled
+ */
+BOOL visor_periodic_work_nextperiod(struct periodic_work *pw)
+{
+ BOOL rc = FALSE;
+
+ write_lock(&pw->lock);
+ if (pw->want_to_stop) {
+ pw->is_scheduled = FALSE;
+ pw->want_to_stop = FALSE;
+ rc = TRUE; /* yes, TRUE; see visor_periodic_work_stop() */
+ goto unlock;
+ } else if (queue_delayed_work(pw->workqueue, &pw->work,
+ pw->jiffy_interval) < 0) {
+ pw->is_scheduled = FALSE;
+ rc = FALSE;
+ goto unlock;
+ }
+ rc = TRUE;
+unlock:
+ write_unlock(&pw->lock);
+ return rc;
+}
+EXPORT_SYMBOL_GPL(visor_periodic_work_nextperiod);
+
+/** This function returns TRUE iff new periodic work was actually started.
+ * If this function returns FALSE, then no work was started
+ * (either because it was already started, or because of a failure).
+ */
+BOOL visor_periodic_work_start(struct periodic_work *pw)
+{
+ BOOL rc = FALSE;
+
+ write_lock(&pw->lock);
+ if (pw->is_scheduled) {
+ rc = FALSE;
+ goto unlock;
+ }
+ if (pw->want_to_stop) {
+ rc = FALSE;
+ goto unlock;
+ }
+ INIT_DELAYED_WORK(&pw->work, &periodic_work_func);
+ if (queue_delayed_work(pw->workqueue, &pw->work,
+ pw->jiffy_interval) < 0) {
+ rc = FALSE;
+ goto unlock;
+ }
+ pw->is_scheduled = TRUE;
+ rc = TRUE;
+unlock:
+ write_unlock(&pw->lock);
+ return rc;
+}
+EXPORT_SYMBOL_GPL(visor_periodic_work_start);
+
+/** This function returns TRUE iff your call actually stopped the periodic
+ * work.
+ *
+ * -- PAY ATTENTION... this is important --
+ *
+ * NO NO #1
+ *
+ * Do NOT call this function from some function that is running on the
+ * same workqueue as the work you are trying to stop might be running
+ * on! If you violate this rule, visor_periodic_work_stop() MIGHT work,
+ * but it also MIGHT get hung up in an infinite loop saying
+ * "waiting for delayed work...". This will happen if the delayed work
+ * you are trying to cancel has been put in the workqueue list, but can't
+ * run yet because we are running that same workqueue thread right now.
+ *
+ * Bottom line: If you need to call visor_periodic_work_stop() from a
+ * workitem, be sure the workitem is on a DIFFERENT workqueue than the
+ * workitem that you are trying to cancel.
+ *
+ * If I could figure out some way to check for this "no no" condition in
+ * the code, I would. It would have saved me the trouble of writing this
+ * long comment. And also, don't think this is some "theoretical" race
+ * condition. It is REAL, as I have spent the day chasing it.
+ *
+ * NO NO #2
+ *
+ * Take close note of the locks that you own when you call this function.
+ * You must NOT own any locks that are needed by the periodic work
+ * function that is currently installed. If you DO, a deadlock may result,
+ * because stopping the periodic work often involves waiting for the last
+ * iteration of the periodic work function to complete. Again, if you hit
+ * this deadlock, you will get hung up in an infinite loop saying
+ * "waiting for delayed work...".
+ */
+BOOL visor_periodic_work_stop(struct periodic_work *pw)
+{
+ BOOL stopped_something = FALSE;
+
+ write_lock(&pw->lock);
+ stopped_something = pw->is_scheduled && (!pw->want_to_stop);
+ while (pw->is_scheduled) {
+ pw->want_to_stop = TRUE;
+ if (cancel_delayed_work(&pw->work)) {
+ /* We get here if the delayed work was pending as
+ * delayed work, but was NOT run.
+ */
+ WARN_ON(!pw->is_scheduled);
+ pw->is_scheduled = FALSE;
+ } else {
+ /* If we get here, either the delayed work:
+ * - was run, OR,
+ * - is running RIGHT NOW on another processor, OR,
+ * - wasn't even scheduled (there is a miniscule
+ * timing window where this could be the case)
+ * flush_workqueue() would make sure it is finished
+ * executing, but that still isn't very useful, which
+ * explains the loop...
+ */
+ }
+ if (pw->is_scheduled) {
+ write_unlock(&pw->lock);
+ SLEEPJIFFIES(10);
+ write_lock(&pw->lock);
+ } else {
+ pw->want_to_stop = FALSE;
+ }
+ }
+ write_unlock(&pw->lock);
+ return stopped_something;
+}
+EXPORT_SYMBOL_GPL(visor_periodic_work_stop);
diff --git a/drivers/staging/unisys/visorutil/visorkmodutils.c b/drivers/staging/unisys/visorutil/visorkmodutils.c
new file mode 100644
index 000000000..62f0f7046
--- /dev/null
+++ b/drivers/staging/unisys/visorutil/visorkmodutils.c
@@ -0,0 +1,71 @@
+/* timskmodutils.c
+ *
+ * Copyright (C) 2010 - 2013 UNISYS CORPORATION
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or (at
+ * your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
+ * NON INFRINGEMENT. See the GNU General Public License for more
+ * details.
+ */
+
+#include "timskmod.h"
+
+#define MYDRVNAME "timskmodutils"
+
+/* s-Par uses the Intel processor's VT-X features to separate groups of
+ * processors into partitions. The firmware sets the hypervisor bit and
+ * reports an ID in the HV capabilities leaf so that the partition's OS
+ * knows s-Par is present and managing the processors.
+ */
+
+#define UNISYS_SPAR_LEAF_ID 0x40000000
+
+/* The s-Par leaf ID returns "UnisysSpar64" encoded across ebx, ecx, edx */
+#define UNISYS_SPAR_ID_EBX 0x73696e55
+#define UNISYS_SPAR_ID_ECX 0x70537379
+#define UNISYS_SPAR_ID_EDX 0x34367261
+
+int unisys_spar_platform;
+EXPORT_SYMBOL_GPL(unisys_spar_platform);
+
+static __init uint32_t visorutil_spar_detect(void)
+{
+ unsigned int eax, ebx, ecx, edx;
+
+ if (cpu_has_hypervisor) {
+ /* check the ID */
+ cpuid(UNISYS_SPAR_LEAF_ID, &eax, &ebx, &ecx, &edx);
+ return (ebx == UNISYS_SPAR_ID_EBX) &&
+ (ecx == UNISYS_SPAR_ID_ECX) &&
+ (edx == UNISYS_SPAR_ID_EDX);
+ } else {
+ return 0;
+ }
+}
+
+static __init int visorutil_mod_init(void)
+{
+ if (visorutil_spar_detect()) {
+ unisys_spar_platform = TRUE;
+ return 0;
+ } else {
+ return -ENODEV;
+ }
+}
+
+static __exit void
+visorutil_mod_exit(void)
+{
+}
+
+module_init(visorutil_mod_init);
+module_exit(visorutil_mod_exit);
+
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/vhba/Kconfig b/drivers/staging/vhba/Kconfig
new file mode 100644
index 000000000..7ccb7d8dc
--- /dev/null
+++ b/drivers/staging/vhba/Kconfig
@@ -0,0 +1,9 @@
+config VHBA
+ tristate "Virtual (SCSI) Host Bus Adapter"
+ depends on SCSI
+ ---help---
+ This is the in-kernel part of CDEmu, a CD/DVD-ROM device
+ emulator.
+
+ This driver can also be built as a module. If so, the module
+ will be called vhba.
diff --git a/drivers/staging/vhba/Makefile b/drivers/staging/vhba/Makefile
new file mode 100644
index 000000000..60b9e2619
--- /dev/null
+++ b/drivers/staging/vhba/Makefile
@@ -0,0 +1,4 @@
+VHBA_VERSION := 20140928
+
+obj-$(CONFIG_VHBA) += vhba.o
+ccflags-y := -DVHBA_VERSION=\"$(VHBA_VERSION)\" -Werror
diff --git a/drivers/staging/vhba/vhba.c b/drivers/staging/vhba/vhba.c
new file mode 100644
index 000000000..4a14a10e9
--- /dev/null
+++ b/drivers/staging/vhba/vhba.c
@@ -0,0 +1,1071 @@
+/*
+ * vhba.c
+ *
+ * Copyright (C) 2007-2012 Chia-I Wu <b90201047 AT ntu DOT edu DOT tw>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/highmem.h>
+#include <linux/fs.h>
+#include <linux/sched.h>
+#include <linux/platform_device.h>
+#include <linux/miscdevice.h>
+#include <linux/poll.h>
+#include <linux/slab.h>
+#include <linux/version.h>
+#ifdef CONFIG_COMPAT
+#include <linux/compat.h>
+#endif
+#include <asm/uaccess.h>
+#include <scsi/scsi.h>
+#include <scsi/scsi_host.h>
+#include <scsi/scsi_cmnd.h>
+#include <scsi/scsi_device.h>
+
+/* scatterlist.page_link and sg_page() were introduced in 2.6.24 */
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 24)
+#define USE_SG_PAGE
+#include <linux/scatterlist.h>
+#endif
+
+MODULE_AUTHOR("Chia-I Wu");
+MODULE_VERSION(VHBA_VERSION);
+MODULE_DESCRIPTION("Virtual SCSI HBA");
+MODULE_LICENSE("GPL");
+
+#ifdef DEBUG
+#define DPRINTK(fmt, args...) printk(KERN_DEBUG "%s: " fmt, __FUNCTION__, ## args)
+#else
+#define DPRINTK(fmt, args...)
+#endif
+
+/* scmd_dbg was introduced in 3.15 */
+#ifndef scmd_dbg
+#define scmd_dbg(scmd, fmt, a...) \
+ dev_dbg(&(scmd)->device->sdev_gendev, fmt, ##a)
+#endif
+
+#ifndef scmd_warn
+#define scmd_warn(scmd, fmt, a...) \
+ dev_warn(&(scmd)->device->sdev_gendev, fmt, ##a)
+#endif
+
+#define VHBA_MAX_SECTORS_PER_IO 256
+#define VHBA_MAX_ID 32
+#define VHBA_CAN_QUEUE 32
+#define VHBA_INVALID_ID VHBA_MAX_ID
+
+#define DATA_TO_DEVICE(dir) ((dir) == DMA_TO_DEVICE || (dir) == DMA_BIDIRECTIONAL)
+#define DATA_FROM_DEVICE(dir) ((dir) == DMA_FROM_DEVICE || (dir) == DMA_BIDIRECTIONAL)
+
+
+/* SCSI macros were introduced in 2.6.23 */
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 23)
+#define scsi_sg_count(cmd) ((cmd)->use_sg)
+#define scsi_sglist(cmd) ((cmd)->request_buffer)
+#define scsi_bufflen(cmd) ((cmd)->request_bufflen)
+#define scsi_set_resid(cmd, to_read) {(cmd)->resid = (to_read);}
+#endif
+
+/* 1-argument form of k[un]map_atomic was introduced in 2.6.37-rc1;
+ 2-argument form was deprecated in 3.4-rc1 */
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 37)
+#define vhba_kmap_atomic kmap_atomic
+#define vhba_kunmap_atomic kunmap_atomic
+#else
+#define vhba_kmap_atomic(page) kmap_atomic(page, KM_USER0)
+#define vhba_kunmap_atomic(page) kunmap_atomic(page, KM_USER0)
+#endif
+
+
+enum vhba_req_state {
+ VHBA_REQ_FREE,
+ VHBA_REQ_PENDING,
+ VHBA_REQ_READING,
+ VHBA_REQ_SENT,
+ VHBA_REQ_WRITING,
+};
+
+struct vhba_command {
+ struct scsi_cmnd *cmd;
+ int status;
+ struct list_head entry;
+};
+
+struct vhba_device {
+ uint id;
+ spinlock_t cmd_lock;
+ struct list_head cmd_list;
+ wait_queue_head_t cmd_wq;
+ atomic_t refcnt;
+};
+
+struct vhba_host {
+ struct Scsi_Host *shost;
+ spinlock_t cmd_lock;
+ int cmd_next;
+ struct vhba_command commands[VHBA_CAN_QUEUE];
+ spinlock_t dev_lock;
+ struct vhba_device *devices[VHBA_MAX_ID];
+ int num_devices;
+ DECLARE_BITMAP(chgmap, VHBA_MAX_ID);
+ int chgtype[VHBA_MAX_ID];
+ struct work_struct scan_devices;
+};
+
+#define MAX_COMMAND_SIZE 16
+
+struct vhba_request {
+ __u32 tag;
+ __u32 lun;
+ __u8 cdb[MAX_COMMAND_SIZE];
+ __u8 cdb_len;
+ __u32 data_len;
+};
+
+struct vhba_response {
+ __u32 tag;
+ __u32 status;
+ __u32 data_len;
+};
+
+static struct vhba_command *vhba_alloc_command (void);
+static void vhba_free_command (struct vhba_command *vcmd);
+
+static struct platform_device vhba_platform_device;
+
+static struct vhba_device *vhba_device_alloc (void)
+{
+ struct vhba_device *vdev;
+
+ vdev = kzalloc(sizeof(struct vhba_device), GFP_KERNEL);
+ if (!vdev) {
+ return NULL;
+ }
+
+ vdev->id = VHBA_INVALID_ID;
+ spin_lock_init(&vdev->cmd_lock);
+ INIT_LIST_HEAD(&vdev->cmd_list);
+ init_waitqueue_head(&vdev->cmd_wq);
+ atomic_set(&vdev->refcnt, 1);
+
+ return vdev;
+}
+
+static void vhba_device_put (struct vhba_device *vdev)
+{
+ if (atomic_dec_and_test(&vdev->refcnt)) {
+ kfree(vdev);
+ }
+}
+
+static struct vhba_device *vhba_device_get (struct vhba_device *vdev)
+{
+ atomic_inc(&vdev->refcnt);
+
+ return vdev;
+}
+
+static int vhba_device_queue (struct vhba_device *vdev, struct scsi_cmnd *cmd)
+{
+ struct vhba_command *vcmd;
+ unsigned long flags;
+
+ vcmd = vhba_alloc_command();
+ if (!vcmd) {
+ return SCSI_MLQUEUE_HOST_BUSY;
+ }
+
+ vcmd->cmd = cmd;
+
+ spin_lock_irqsave(&vdev->cmd_lock, flags);
+ list_add_tail(&vcmd->entry, &vdev->cmd_list);
+ spin_unlock_irqrestore(&vdev->cmd_lock, flags);
+
+ wake_up_interruptible(&vdev->cmd_wq);
+
+ return 0;
+}
+
+static int vhba_device_dequeue (struct vhba_device *vdev, struct scsi_cmnd *cmd)
+{
+ struct vhba_command *vcmd;
+ int retval;
+ unsigned long flags;
+
+ spin_lock_irqsave(&vdev->cmd_lock, flags);
+ list_for_each_entry(vcmd, &vdev->cmd_list, entry) {
+ if (vcmd->cmd == cmd) {
+ list_del_init(&vcmd->entry);
+ break;
+ }
+ }
+
+ /* command not found */
+ if (&vcmd->entry == &vdev->cmd_list) {
+ spin_unlock_irqrestore(&vdev->cmd_lock, flags);
+ return SUCCESS;
+ }
+
+ while (vcmd->status == VHBA_REQ_READING || vcmd->status == VHBA_REQ_WRITING) {
+ spin_unlock_irqrestore(&vdev->cmd_lock, flags);
+ scmd_dbg(cmd, "wait for I/O before aborting\n");
+ schedule_timeout(1);
+ spin_lock_irqsave(&vdev->cmd_lock, flags);
+ }
+
+ retval = (vcmd->status == VHBA_REQ_SENT) ? FAILED : SUCCESS;
+
+ vhba_free_command(vcmd);
+
+ spin_unlock_irqrestore(&vdev->cmd_lock, flags);
+
+ return retval;
+}
+
+static inline void vhba_scan_devices_add (struct vhba_host *vhost, int id)
+{
+ struct scsi_device *sdev;
+
+ sdev = scsi_device_lookup(vhost->shost, 0, id, 0);
+ if (!sdev) {
+ scsi_add_device(vhost->shost, 0, id, 0);
+ } else {
+ dev_warn(&vhost->shost->shost_gendev, "tried to add an already-existing device 0:%d:0!\n", id);
+ scsi_device_put(sdev);
+ }
+}
+
+static inline void vhba_scan_devices_remove (struct vhba_host *vhost, int id)
+{
+ struct scsi_device *sdev;
+
+ sdev = scsi_device_lookup(vhost->shost, 0, id, 0);
+ if (sdev) {
+ scsi_remove_device(sdev);
+ scsi_device_put(sdev);
+ } else {
+ dev_warn(&vhost->shost->shost_gendev, "tried to remove non-existing device 0:%d:0!\n", id);
+ }
+}
+
+static void vhba_scan_devices (struct work_struct *work)
+{
+ struct vhba_host *vhost = container_of(work, struct vhba_host, scan_devices);
+ unsigned long flags;
+ int id, change, exists;
+
+ while (1) {
+ spin_lock_irqsave(&vhost->dev_lock, flags);
+
+ id = find_first_bit(vhost->chgmap, vhost->shost->max_id);
+ if (id >= vhost->shost->max_id) {
+ spin_unlock_irqrestore(&vhost->dev_lock, flags);
+ break;
+ }
+ change = vhost->chgtype[id];
+ exists = vhost->devices[id] != NULL;
+
+ vhost->chgtype[id] = 0;
+ clear_bit(id, vhost->chgmap);
+
+ spin_unlock_irqrestore(&vhost->dev_lock, flags);
+
+ if (change < 0) {
+ dev_dbg(&vhost->shost->shost_gendev, "trying to remove target 0:%d:0\n", id);
+ vhba_scan_devices_remove(vhost, id);
+ } else if (change > 0) {
+ dev_dbg(&vhost->shost->shost_gendev, "trying to add target 0:%d:0\n", id);
+ vhba_scan_devices_add(vhost, id);
+ } else {
+ /* quick sequence of add/remove or remove/add; we determine
+ which one it was by checking if device structure exists */
+ if (exists) {
+ /* remove followed by add: remove and (re)add */
+ dev_dbg(&vhost->shost->shost_gendev, "trying to (re)add target 0:%d:0\n", id);
+ vhba_scan_devices_remove(vhost, id);
+ vhba_scan_devices_add(vhost, id);
+ } else {
+ /* add followed by remove: no-op */
+ dev_dbg(&vhost->shost->shost_gendev, "no-op for target 0:%d:0\n", id);
+ }
+ }
+ }
+}
+
+static int vhba_add_device (struct vhba_device *vdev)
+{
+ struct vhba_host *vhost;
+ int i;
+ unsigned long flags;
+
+ vhost = platform_get_drvdata(&vhba_platform_device);
+
+ vhba_device_get(vdev);
+
+ spin_lock_irqsave(&vhost->dev_lock, flags);
+ if (vhost->num_devices >= vhost->shost->max_id) {
+ spin_unlock_irqrestore(&vhost->dev_lock, flags);
+ vhba_device_put(vdev);
+ return -EBUSY;
+ }
+
+ for (i = 0; i < vhost->shost->max_id; i++) {
+ if (vhost->devices[i] == NULL) {
+ vdev->id = i;
+ vhost->devices[i] = vdev;
+ vhost->num_devices++;
+ set_bit(vdev->id, vhost->chgmap);
+ vhost->chgtype[vdev->id]++;
+ break;
+ }
+ }
+ spin_unlock_irqrestore(&vhost->dev_lock, flags);
+
+ schedule_work(&vhost->scan_devices);
+
+ return 0;
+}
+
+static int vhba_remove_device (struct vhba_device *vdev)
+{
+ struct vhba_host *vhost;
+ unsigned long flags;
+
+ vhost = platform_get_drvdata(&vhba_platform_device);
+
+ spin_lock_irqsave(&vhost->dev_lock, flags);
+ set_bit(vdev->id, vhost->chgmap);
+ vhost->chgtype[vdev->id]--;
+ vhost->devices[vdev->id] = NULL;
+ vhost->num_devices--;
+ vdev->id = VHBA_INVALID_ID;
+ spin_unlock_irqrestore(&vhost->dev_lock, flags);
+
+ vhba_device_put(vdev);
+
+ schedule_work(&vhost->scan_devices);
+
+ return 0;
+}
+
+static struct vhba_device *vhba_lookup_device (int id)
+{
+ struct vhba_host *vhost;
+ struct vhba_device *vdev = NULL;
+ unsigned long flags;
+
+ vhost = platform_get_drvdata(&vhba_platform_device);
+
+ if (likely(id < vhost->shost->max_id)) {
+ spin_lock_irqsave(&vhost->dev_lock, flags);
+ vdev = vhost->devices[id];
+ if (vdev) {
+ vdev = vhba_device_get(vdev);
+ }
+
+ spin_unlock_irqrestore(&vhost->dev_lock, flags);
+ }
+
+ return vdev;
+}
+
+static struct vhba_command *vhba_alloc_command (void)
+{
+ struct vhba_host *vhost;
+ struct vhba_command *vcmd;
+ unsigned long flags;
+ int i;
+
+ vhost = platform_get_drvdata(&vhba_platform_device);
+
+ spin_lock_irqsave(&vhost->cmd_lock, flags);
+
+ vcmd = vhost->commands + vhost->cmd_next++;
+ if (vcmd->status != VHBA_REQ_FREE) {
+ for (i = 0; i < vhost->shost->can_queue; i++) {
+ vcmd = vhost->commands + i;
+
+ if (vcmd->status == VHBA_REQ_FREE) {
+ vhost->cmd_next = i + 1;
+ break;
+ }
+ }
+
+ if (i == vhost->shost->can_queue) {
+ vcmd = NULL;
+ }
+ }
+
+ if (vcmd) {
+ vcmd->status = VHBA_REQ_PENDING;
+ }
+
+ vhost->cmd_next %= vhost->shost->can_queue;
+
+ spin_unlock_irqrestore(&vhost->cmd_lock, flags);
+
+ return vcmd;
+}
+
+static void vhba_free_command (struct vhba_command *vcmd)
+{
+ struct vhba_host *vhost;
+ unsigned long flags;
+
+ vhost = platform_get_drvdata(&vhba_platform_device);
+
+ spin_lock_irqsave(&vhost->cmd_lock, flags);
+ vcmd->status = VHBA_REQ_FREE;
+ spin_unlock_irqrestore(&vhost->cmd_lock, flags);
+}
+
+static int vhba_queuecommand_lck (struct scsi_cmnd *cmd, void (*done)(struct scsi_cmnd *))
+{
+ struct vhba_device *vdev;
+ int retval;
+
+ scmd_dbg(cmd, "queue %lu\n", cmd->serial_number);
+
+ vdev = vhba_lookup_device(cmd->device->id);
+ if (!vdev) {
+ scmd_dbg(cmd, "no such device\n");
+
+ cmd->result = DID_NO_CONNECT << 16;
+ done(cmd);
+
+ return 0;
+ }
+
+ cmd->scsi_done = done;
+ retval = vhba_device_queue(vdev, cmd);
+
+ vhba_device_put(vdev);
+
+ return retval;
+}
+
+#ifdef DEF_SCSI_QCMD
+DEF_SCSI_QCMD(vhba_queuecommand)
+#else
+#define vhba_queuecommand vhba_queuecommand_lck
+#endif
+
+static int vhba_abort (struct scsi_cmnd *cmd)
+{
+ struct vhba_device *vdev;
+ int retval = SUCCESS;
+
+ scmd_warn(cmd, "abort %lu\n", cmd->serial_number);
+
+ vdev = vhba_lookup_device(cmd->device->id);
+ if (vdev) {
+ retval = vhba_device_dequeue(vdev, cmd);
+ vhba_device_put(vdev);
+ } else {
+ cmd->result = DID_NO_CONNECT << 16;
+ }
+
+ return retval;
+}
+
+static struct scsi_host_template vhba_template = {
+ .module = THIS_MODULE,
+ .name = "vhba",
+ .proc_name = "vhba",
+ .queuecommand = vhba_queuecommand,
+ .eh_abort_handler = vhba_abort,
+ .can_queue = VHBA_CAN_QUEUE,
+ .this_id = -1,
+ .cmd_per_lun = 1,
+ .max_sectors = VHBA_MAX_SECTORS_PER_IO,
+ .sg_tablesize = 256,
+};
+
+static ssize_t do_request (struct scsi_cmnd *cmd, char __user *buf, size_t buf_len)
+{
+ struct vhba_request vreq;
+ ssize_t ret;
+
+ scmd_dbg(cmd, "request %lu, cdb 0x%x, bufflen %d, use_sg %d\n",
+ cmd->serial_number, cmd->cmnd[0], scsi_bufflen(cmd), scsi_sg_count(cmd));
+
+ ret = sizeof(vreq);
+ if (DATA_TO_DEVICE(cmd->sc_data_direction)) {
+ ret += scsi_bufflen(cmd);
+ }
+
+ if (ret > buf_len) {
+ scmd_warn(cmd, "buffer too small (%zd < %zd) for a request\n", buf_len, ret);
+ return -EIO;
+ }
+
+ vreq.tag = cmd->serial_number;
+ vreq.lun = cmd->device->lun;
+ memcpy(vreq.cdb, cmd->cmnd, MAX_COMMAND_SIZE);
+ vreq.cdb_len = cmd->cmd_len;
+ vreq.data_len = scsi_bufflen(cmd);
+
+ if (copy_to_user(buf, &vreq, sizeof(vreq))) {
+ return -EFAULT;
+ }
+
+ if (DATA_TO_DEVICE(cmd->sc_data_direction) && vreq.data_len) {
+ buf += sizeof(vreq);
+
+ if (scsi_sg_count(cmd)) {
+ unsigned char buf_stack[64];
+ unsigned char *kaddr, *uaddr, *kbuf;
+ struct scatterlist *sg = scsi_sglist(cmd);
+ int i;
+
+ uaddr = (unsigned char *) buf;
+
+ if (vreq.data_len > 64) {
+ kbuf = kmalloc(PAGE_SIZE, GFP_KERNEL);
+ } else {
+ kbuf = buf_stack;
+ }
+
+ for (i = 0; i < scsi_sg_count(cmd); i++) {
+ size_t len = sg[i].length;
+
+#ifdef USE_SG_PAGE
+ kaddr = vhba_kmap_atomic(sg_page(&sg[i]));
+#else
+ kaddr = vhba_kmap_atomic(sg[i].page);
+#endif
+ memcpy(kbuf, kaddr + sg[i].offset, len);
+ vhba_kunmap_atomic(kaddr);
+
+ if (copy_to_user(uaddr, kbuf, len)) {
+ if (kbuf != buf_stack) {
+ kfree(kbuf);
+ }
+ return -EFAULT;
+ }
+ uaddr += len;
+ }
+
+ if (kbuf != buf_stack) {
+ kfree(kbuf);
+ }
+ } else {
+ if (copy_to_user(buf, scsi_sglist(cmd), vreq.data_len)) {
+ return -EFAULT;
+ }
+ }
+ }
+
+ return ret;
+}
+
+static ssize_t do_response (struct scsi_cmnd *cmd, const char __user *buf, size_t buf_len, struct vhba_response *res)
+{
+ ssize_t ret = 0;
+
+ scmd_dbg(cmd, "response %lu, status %x, data len %d, use_sg %d\n",
+ cmd->serial_number, res->status, res->data_len, scsi_sg_count(cmd));
+
+ if (res->status) {
+ unsigned char sense_stack[SCSI_SENSE_BUFFERSIZE];
+
+ if (res->data_len > SCSI_SENSE_BUFFERSIZE) {
+ scmd_warn(cmd, "truncate sense (%d < %d)", SCSI_SENSE_BUFFERSIZE, res->data_len);
+ res->data_len = SCSI_SENSE_BUFFERSIZE;
+ }
+
+ /* Copy via temporary buffer on stack in order to avoid problems
+ with PAX on grsecurity-enabled kernels */
+ if (copy_from_user(sense_stack, buf, res->data_len)) {
+ return -EFAULT;
+ }
+ memcpy(cmd->sense_buffer, sense_stack, res->data_len);
+
+ cmd->result = res->status;
+
+ ret += res->data_len;
+ } else if (DATA_FROM_DEVICE(cmd->sc_data_direction) && scsi_bufflen(cmd)) {
+ size_t to_read;
+
+ if (res->data_len > scsi_bufflen(cmd)) {
+ scmd_warn(cmd, "truncate data (%d < %d)\n", scsi_bufflen(cmd), res->data_len);
+ res->data_len = scsi_bufflen(cmd);
+ }
+
+ to_read = res->data_len;
+
+ if (scsi_sg_count(cmd)) {
+ unsigned char buf_stack[64];
+ unsigned char *kaddr, *uaddr, *kbuf;
+ struct scatterlist *sg = scsi_sglist(cmd);
+ int i;
+
+ uaddr = (unsigned char *)buf;
+
+ if (res->data_len > 64) {
+ kbuf = kmalloc(PAGE_SIZE, GFP_KERNEL);
+ } else {
+ kbuf = buf_stack;
+ }
+
+ for (i = 0; i < scsi_sg_count(cmd); i++) {
+ size_t len = (sg[i].length < to_read) ? sg[i].length : to_read;
+
+ if (copy_from_user(kbuf, uaddr, len)) {
+ if (kbuf != buf_stack) {
+ kfree(kbuf);
+ }
+ return -EFAULT;
+ }
+ uaddr += len;
+
+#ifdef USE_SG_PAGE
+ kaddr = vhba_kmap_atomic(sg_page(&sg[i]));
+#else
+ kaddr = vhba_kmap_atomic(sg[i].page);
+#endif
+ memcpy(kaddr + sg[i].offset, kbuf, len);
+ vhba_kunmap_atomic(kaddr);
+
+ to_read -= len;
+ if (to_read == 0) {
+ break;
+ }
+ }
+
+ if (kbuf != buf_stack) {
+ kfree(kbuf);
+ }
+ } else {
+ if (copy_from_user(scsi_sglist(cmd), buf, res->data_len)) {
+ return -EFAULT;
+ }
+
+ to_read -= res->data_len;
+ }
+
+ scsi_set_resid(cmd, to_read);
+
+ ret += res->data_len - to_read;
+ }
+
+ return ret;
+}
+
+static inline struct vhba_command *next_command (struct vhba_device *vdev)
+{
+ struct vhba_command *vcmd;
+
+ list_for_each_entry(vcmd, &vdev->cmd_list, entry) {
+ if (vcmd->status == VHBA_REQ_PENDING) {
+ break;
+ }
+ }
+
+ if (&vcmd->entry == &vdev->cmd_list) {
+ vcmd = NULL;
+ }
+
+ return vcmd;
+}
+
+static inline struct vhba_command *match_command (struct vhba_device *vdev, u32 tag)
+{
+ struct vhba_command *vcmd;
+
+ list_for_each_entry(vcmd, &vdev->cmd_list, entry) {
+ if (vcmd->cmd->serial_number == tag) {
+ break;
+ }
+ }
+
+ if (&vcmd->entry == &vdev->cmd_list) {
+ vcmd = NULL;
+ }
+
+ return vcmd;
+}
+
+static struct vhba_command *wait_command (struct vhba_device *vdev, unsigned long flags)
+{
+ struct vhba_command *vcmd;
+ DEFINE_WAIT(wait);
+
+ while (!(vcmd = next_command(vdev))) {
+ if (signal_pending(current)) {
+ break;
+ }
+
+ prepare_to_wait(&vdev->cmd_wq, &wait, TASK_INTERRUPTIBLE);
+
+ spin_unlock_irqrestore(&vdev->cmd_lock, flags);
+
+ schedule();
+
+ spin_lock_irqsave(&vdev->cmd_lock, flags);
+ }
+
+ finish_wait(&vdev->cmd_wq, &wait);
+ if (vcmd) {
+ vcmd->status = VHBA_REQ_READING;
+ }
+
+ return vcmd;
+}
+
+static ssize_t vhba_ctl_read (struct file *file, char __user *buf, size_t buf_len, loff_t *offset)
+{
+ struct vhba_device *vdev;
+ struct vhba_command *vcmd;
+ ssize_t ret;
+ unsigned long flags;
+
+ vdev = file->private_data;
+
+ /* Get next command */
+ if (file->f_flags & O_NONBLOCK) {
+ /* Non-blocking variant */
+ spin_lock_irqsave(&vdev->cmd_lock, flags);
+ vcmd = next_command(vdev);
+ spin_unlock_irqrestore(&vdev->cmd_lock, flags);
+
+ if (!vcmd) {
+ return -EWOULDBLOCK;
+ }
+ } else {
+ /* Blocking variant */
+ spin_lock_irqsave(&vdev->cmd_lock, flags);
+ vcmd = wait_command(vdev, flags);
+ spin_unlock_irqrestore(&vdev->cmd_lock, flags);
+
+ if (!vcmd) {
+ return -ERESTARTSYS;
+ }
+ }
+
+ ret = do_request(vcmd->cmd, buf, buf_len);
+
+ spin_lock_irqsave(&vdev->cmd_lock, flags);
+ if (ret >= 0) {
+ vcmd->status = VHBA_REQ_SENT;
+ *offset += ret;
+ } else {
+ vcmd->status = VHBA_REQ_PENDING;
+ }
+
+ spin_unlock_irqrestore(&vdev->cmd_lock, flags);
+
+ return ret;
+}
+
+static ssize_t vhba_ctl_write (struct file *file, const char __user *buf, size_t buf_len, loff_t *offset)
+{
+ struct vhba_device *vdev;
+ struct vhba_command *vcmd;
+ struct vhba_response res;
+ ssize_t ret;
+ unsigned long flags;
+
+ if (buf_len < sizeof(res)) {
+ return -EIO;
+ }
+
+ if (copy_from_user(&res, buf, sizeof(res))) {
+ return -EFAULT;
+ }
+
+ vdev = file->private_data;
+
+ spin_lock_irqsave(&vdev->cmd_lock, flags);
+ vcmd = match_command(vdev, res.tag);
+ if (!vcmd || vcmd->status != VHBA_REQ_SENT) {
+ spin_unlock_irqrestore(&vdev->cmd_lock, flags);
+ DPRINTK("not expecting response\n");
+ return -EIO;
+ }
+ vcmd->status = VHBA_REQ_WRITING;
+ spin_unlock_irqrestore(&vdev->cmd_lock, flags);
+
+ ret = do_response(vcmd->cmd, buf + sizeof(res), buf_len - sizeof(res), &res);
+
+ spin_lock_irqsave(&vdev->cmd_lock, flags);
+ if (ret >= 0) {
+ vcmd->cmd->scsi_done(vcmd->cmd);
+ ret += sizeof(res);
+
+ /* don't compete with vhba_device_dequeue */
+ if (!list_empty(&vcmd->entry)) {
+ list_del_init(&vcmd->entry);
+ vhba_free_command(vcmd);
+ }
+ } else {
+ vcmd->status = VHBA_REQ_SENT;
+ }
+
+ spin_unlock_irqrestore(&vdev->cmd_lock, flags);
+
+ return ret;
+}
+
+static long vhba_ctl_ioctl (struct file *file, unsigned int cmd, unsigned long arg)
+{
+ struct vhba_device *vdev = file->private_data;
+ struct vhba_host *vhost;
+ struct scsi_device *sdev;
+
+ switch (cmd) {
+ case 0xBEEF001: {
+ vhost = platform_get_drvdata(&vhba_platform_device);
+ sdev = scsi_device_lookup(vhost->shost, 0, vdev->id, 0);
+
+ if (sdev) {
+ int id[4] = {
+ sdev->host->host_no,
+ sdev->channel,
+ sdev->id,
+ sdev->lun
+ };
+
+ scsi_device_put(sdev);
+
+ if (copy_to_user((void *)arg, id, sizeof(id))) {
+ return -EFAULT;
+ }
+
+ return 0;
+ } else {
+ return -ENODEV;
+ }
+ }
+ }
+
+ return -ENOTTY;
+}
+
+#ifdef CONFIG_COMPAT
+static long vhba_ctl_compat_ioctl (struct file *file, unsigned int cmd, unsigned long arg)
+{
+ unsigned long compat_arg = (unsigned long)compat_ptr(arg);
+ return vhba_ctl_ioctl(file, cmd, compat_arg);
+}
+#endif
+
+static unsigned int vhba_ctl_poll (struct file *file, poll_table *wait)
+{
+ struct vhba_device *vdev = file->private_data;
+ unsigned int mask = 0;
+ unsigned long flags;
+
+ poll_wait(file, &vdev->cmd_wq, wait);
+
+ spin_lock_irqsave(&vdev->cmd_lock, flags);
+ if (next_command(vdev)) {
+ mask |= POLLIN | POLLRDNORM;
+ }
+ spin_unlock_irqrestore(&vdev->cmd_lock, flags);
+
+ return mask;
+}
+
+static int vhba_ctl_open (struct inode *inode, struct file *file)
+{
+ struct vhba_device *vdev;
+ int retval;
+
+ DPRINTK("open\n");
+
+ /* check if vhba is probed */
+ if (!platform_get_drvdata(&vhba_platform_device)) {
+ return -ENODEV;
+ }
+
+ vdev = vhba_device_alloc();
+ if (!vdev) {
+ return -ENOMEM;
+ }
+
+ if (!(retval = vhba_add_device(vdev))) {
+ file->private_data = vdev;
+ }
+
+ vhba_device_put(vdev);
+
+ return retval;
+}
+
+static int vhba_ctl_release (struct inode *inode, struct file *file)
+{
+ struct vhba_device *vdev;
+ struct vhba_command *vcmd;
+ unsigned long flags;
+
+ DPRINTK("release\n");
+
+ vdev = file->private_data;
+
+ vhba_device_get(vdev);
+ vhba_remove_device(vdev);
+
+ spin_lock_irqsave(&vdev->cmd_lock, flags);
+ list_for_each_entry(vcmd, &vdev->cmd_list, entry) {
+ WARN_ON(vcmd->status == VHBA_REQ_READING || vcmd->status == VHBA_REQ_WRITING);
+
+ scmd_warn(vcmd->cmd, "device released with command %lu\n", vcmd->cmd->serial_number);
+ vcmd->cmd->result = DID_NO_CONNECT << 16;
+ vcmd->cmd->scsi_done(vcmd->cmd);
+
+ vhba_free_command(vcmd);
+ }
+ INIT_LIST_HEAD(&vdev->cmd_list);
+ spin_unlock_irqrestore(&vdev->cmd_lock, flags);
+
+ vhba_device_put(vdev);
+
+ return 0;
+}
+
+static struct file_operations vhba_ctl_fops = {
+ .owner = THIS_MODULE,
+ .open = vhba_ctl_open,
+ .release = vhba_ctl_release,
+ .read = vhba_ctl_read,
+ .write = vhba_ctl_write,
+ .poll = vhba_ctl_poll,
+ .unlocked_ioctl = vhba_ctl_ioctl,
+#ifdef CONFIG_COMPAT
+ .compat_ioctl = vhba_ctl_compat_ioctl,
+#endif
+};
+
+static struct miscdevice vhba_miscdev = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "vhba_ctl",
+ .fops = &vhba_ctl_fops,
+};
+
+static int vhba_probe (struct platform_device *pdev)
+{
+ struct Scsi_Host *shost;
+ struct vhba_host *vhost;
+ int i;
+
+ shost = scsi_host_alloc(&vhba_template, sizeof(struct vhba_host));
+ if (!shost) {
+ return -ENOMEM;
+ }
+
+ shost->max_id = VHBA_MAX_ID;
+ /* we don't support lun > 0 */
+ shost->max_lun = 1;
+ shost->max_cmd_len = MAX_COMMAND_SIZE;
+
+ vhost = (struct vhba_host *)shost->hostdata;
+ memset(vhost, 0, sizeof(*vhost));
+
+ vhost->shost = shost;
+ vhost->num_devices = 0;
+ spin_lock_init(&vhost->dev_lock);
+ spin_lock_init(&vhost->cmd_lock);
+ INIT_WORK(&vhost->scan_devices, vhba_scan_devices);
+ vhost->cmd_next = 0;
+ for (i = 0; i < vhost->shost->can_queue; i++) {
+ vhost->commands[i].status = VHBA_REQ_FREE;
+ }
+
+ platform_set_drvdata(pdev, vhost);
+
+ if (scsi_add_host(shost, &pdev->dev)) {
+ scsi_host_put(shost);
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+static int vhba_remove (struct platform_device *pdev)
+{
+ struct vhba_host *vhost;
+ struct Scsi_Host *shost;
+
+ vhost = platform_get_drvdata(pdev);
+ shost = vhost->shost;
+
+ scsi_remove_host(shost);
+ scsi_host_put(shost);
+
+ return 0;
+}
+
+static void vhba_release (struct device * dev)
+{
+ return;
+}
+
+static struct platform_device vhba_platform_device = {
+ .name = "vhba",
+ .id = -1,
+ .dev = {
+ .release = vhba_release,
+ },
+};
+
+static struct platform_driver vhba_platform_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "vhba",
+ },
+ .probe = vhba_probe,
+ .remove = vhba_remove,
+};
+
+static int __init vhba_init (void)
+{
+ int ret;
+
+ ret = platform_device_register(&vhba_platform_device);
+ if (ret < 0) {
+ return ret;
+ }
+
+ ret = platform_driver_register(&vhba_platform_driver);
+ if (ret < 0) {
+ platform_device_unregister(&vhba_platform_device);
+ return ret;
+ }
+
+ ret = misc_register(&vhba_miscdev);
+ if (ret < 0) {
+ platform_driver_unregister(&vhba_platform_driver);
+ platform_device_unregister(&vhba_platform_device);
+ return ret;
+ }
+
+ return 0;
+}
+
+static void __exit vhba_exit(void)
+{
+ misc_deregister(&vhba_miscdev);
+ platform_driver_unregister(&vhba_platform_driver);
+ platform_device_unregister(&vhba_platform_device);
+}
+
+module_init(vhba_init);
+module_exit(vhba_exit);
+
diff --git a/drivers/staging/vme/Makefile b/drivers/staging/vme/Makefile
new file mode 100644
index 000000000..accdb72e3
--- /dev/null
+++ b/drivers/staging/vme/Makefile
@@ -0,0 +1 @@
+obj-y += devices/
diff --git a/drivers/staging/vme/devices/Kconfig b/drivers/staging/vme/devices/Kconfig
new file mode 100644
index 000000000..1d2ff0cc4
--- /dev/null
+++ b/drivers/staging/vme/devices/Kconfig
@@ -0,0 +1,25 @@
+comment "VME Device Drivers"
+
+config VME_USER
+ tristate "VME user space access driver"
+ depends on STAGING
+ help
+ If you say Y here you want to be able to access a limited number of
+ VME windows in a manner at least semi-compatible with the interface
+ provided with the original driver at <http://www.vmelinux.org/>.
+
+ To compile this driver as a module, choose M here. The module will
+ be called vme_user. If unsure, say N.
+
+config VME_PIO2
+ tristate "GE PIO2 VME"
+ depends on STAGING && GPIOLIB
+ help
+ Say Y here to include support for the GE PIO2. The PIO2 is a 6U VME
+ slave card, implementing 32 solid-state relay switched IO lines, in
+ 4 groups of 8. Each bank of IO lines is built to function as input,
+ output or both depending on the variant of the card.
+
+ To compile this driver as a module, choose M here. The module will
+ be called vme_pio2. If unsure, say N.
+
diff --git a/drivers/staging/vme/devices/Makefile b/drivers/staging/vme/devices/Makefile
new file mode 100644
index 000000000..172512cb5
--- /dev/null
+++ b/drivers/staging/vme/devices/Makefile
@@ -0,0 +1,8 @@
+#
+# Makefile for the VME device drivers.
+#
+
+obj-$(CONFIG_VME_USER) += vme_user.o
+
+vme_pio2-objs := vme_pio2_cntr.o vme_pio2_gpio.o vme_pio2_core.o
+obj-$(CONFIG_VME_PIO2) += vme_pio2.o
diff --git a/drivers/staging/vme/devices/vme_pio2.h b/drivers/staging/vme/devices/vme_pio2.h
new file mode 100644
index 000000000..d5d94c43c
--- /dev/null
+++ b/drivers/staging/vme/devices/vme_pio2.h
@@ -0,0 +1,249 @@
+#ifndef _VME_PIO2_H_
+#define _VME_PIO2_H_
+
+#define PIO2_CARDS_MAX 32
+
+#define PIO2_VARIANT_LENGTH 5
+
+#define PIO2_NUM_CHANNELS 32
+#define PIO2_NUM_IRQS 11
+#define PIO2_NUM_CNTRS 6
+
+#define PIO2_REGS_SIZE 0x40
+
+#define PIO2_REGS_DATA0 0x0
+#define PIO2_REGS_DATA1 0x1
+#define PIO2_REGS_DATA2 0x2
+#define PIO2_REGS_DATA3 0x3
+
+static const int PIO2_REGS_DATA[4] = { PIO2_REGS_DATA0, PIO2_REGS_DATA1,
+ PIO2_REGS_DATA2, PIO2_REGS_DATA3 };
+
+#define PIO2_REGS_INT_STAT0 0x8
+#define PIO2_REGS_INT_STAT1 0x9
+#define PIO2_REGS_INT_STAT2 0xa
+#define PIO2_REGS_INT_STAT3 0xb
+
+static const int PIO2_REGS_INT_STAT[4] = { PIO2_REGS_INT_STAT0,
+ PIO2_REGS_INT_STAT1,
+ PIO2_REGS_INT_STAT2,
+ PIO2_REGS_INT_STAT3 };
+
+#define PIO2_REGS_INT_STAT_CNTR 0xc
+#define PIO2_REGS_INT_MASK0 0x10
+#define PIO2_REGS_INT_MASK1 0x11
+#define PIO2_REGS_INT_MASK2 0x12
+#define PIO2_REGS_INT_MASK3 0x13
+#define PIO2_REGS_INT_MASK4 0x14
+#define PIO2_REGS_INT_MASK5 0x15
+#define PIO2_REGS_INT_MASK6 0x16
+#define PIO2_REGS_INT_MASK7 0x17
+
+static const int PIO2_REGS_INT_MASK[8] = { PIO2_REGS_INT_MASK0,
+ PIO2_REGS_INT_MASK1,
+ PIO2_REGS_INT_MASK2,
+ PIO2_REGS_INT_MASK3,
+ PIO2_REGS_INT_MASK4,
+ PIO2_REGS_INT_MASK5,
+ PIO2_REGS_INT_MASK6,
+ PIO2_REGS_INT_MASK7 };
+
+
+
+#define PIO2_REGS_CTRL 0x18
+#define PIO2_REGS_VME_VECTOR 0x19
+#define PIO2_REGS_CNTR0 0x20
+#define PIO2_REGS_CNTR1 0x22
+#define PIO2_REGS_CNTR2 0x24
+#define PIO2_REGS_CTRL_WRD0 0x26
+#define PIO2_REGS_CNTR3 0x28
+#define PIO2_REGS_CNTR4 0x2a
+#define PIO2_REGS_CNTR5 0x2c
+#define PIO2_REGS_CTRL_WRD1 0x2e
+
+#define PIO2_REGS_ID 0x30
+
+
+/* PIO2_REGS_DATAx (0x0 - 0x3) */
+
+static const int PIO2_CHANNEL_BANK[32] = { 0, 0, 0, 0, 0, 0, 0, 0,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 2, 2, 2, 2, 2, 2, 2, 2,
+ 3, 3, 3, 3, 3, 3, 3, 3 };
+
+#define PIO2_CHANNEL0_BIT (1 << 0)
+#define PIO2_CHANNEL1_BIT (1 << 1)
+#define PIO2_CHANNEL2_BIT (1 << 2)
+#define PIO2_CHANNEL3_BIT (1 << 3)
+#define PIO2_CHANNEL4_BIT (1 << 4)
+#define PIO2_CHANNEL5_BIT (1 << 5)
+#define PIO2_CHANNEL6_BIT (1 << 6)
+#define PIO2_CHANNEL7_BIT (1 << 7)
+#define PIO2_CHANNEL8_BIT (1 << 0)
+#define PIO2_CHANNEL9_BIT (1 << 1)
+#define PIO2_CHANNEL10_BIT (1 << 2)
+#define PIO2_CHANNEL11_BIT (1 << 3)
+#define PIO2_CHANNEL12_BIT (1 << 4)
+#define PIO2_CHANNEL13_BIT (1 << 5)
+#define PIO2_CHANNEL14_BIT (1 << 6)
+#define PIO2_CHANNEL15_BIT (1 << 7)
+#define PIO2_CHANNEL16_BIT (1 << 0)
+#define PIO2_CHANNEL17_BIT (1 << 1)
+#define PIO2_CHANNEL18_BIT (1 << 2)
+#define PIO2_CHANNEL19_BIT (1 << 3)
+#define PIO2_CHANNEL20_BIT (1 << 4)
+#define PIO2_CHANNEL21_BIT (1 << 5)
+#define PIO2_CHANNEL22_BIT (1 << 6)
+#define PIO2_CHANNEL23_BIT (1 << 7)
+#define PIO2_CHANNEL24_BIT (1 << 0)
+#define PIO2_CHANNEL25_BIT (1 << 1)
+#define PIO2_CHANNEL26_BIT (1 << 2)
+#define PIO2_CHANNEL27_BIT (1 << 3)
+#define PIO2_CHANNEL28_BIT (1 << 4)
+#define PIO2_CHANNEL29_BIT (1 << 5)
+#define PIO2_CHANNEL30_BIT (1 << 6)
+#define PIO2_CHANNEL31_BIT (1 << 7)
+
+static const int PIO2_CHANNEL_BIT[32] = { PIO2_CHANNEL0_BIT, PIO2_CHANNEL1_BIT,
+ PIO2_CHANNEL2_BIT, PIO2_CHANNEL3_BIT,
+ PIO2_CHANNEL4_BIT, PIO2_CHANNEL5_BIT,
+ PIO2_CHANNEL6_BIT, PIO2_CHANNEL7_BIT,
+ PIO2_CHANNEL8_BIT, PIO2_CHANNEL9_BIT,
+ PIO2_CHANNEL10_BIT, PIO2_CHANNEL11_BIT,
+ PIO2_CHANNEL12_BIT, PIO2_CHANNEL13_BIT,
+ PIO2_CHANNEL14_BIT, PIO2_CHANNEL15_BIT,
+ PIO2_CHANNEL16_BIT, PIO2_CHANNEL17_BIT,
+ PIO2_CHANNEL18_BIT, PIO2_CHANNEL19_BIT,
+ PIO2_CHANNEL20_BIT, PIO2_CHANNEL21_BIT,
+ PIO2_CHANNEL22_BIT, PIO2_CHANNEL23_BIT,
+ PIO2_CHANNEL24_BIT, PIO2_CHANNEL25_BIT,
+ PIO2_CHANNEL26_BIT, PIO2_CHANNEL27_BIT,
+ PIO2_CHANNEL28_BIT, PIO2_CHANNEL29_BIT,
+ PIO2_CHANNEL30_BIT, PIO2_CHANNEL31_BIT
+ };
+
+/* PIO2_REGS_INT_STAT_CNTR (0xc) */
+#define PIO2_COUNTER0 (1 << 0)
+#define PIO2_COUNTER1 (1 << 1)
+#define PIO2_COUNTER2 (1 << 2)
+#define PIO2_COUNTER3 (1 << 3)
+#define PIO2_COUNTER4 (1 << 4)
+#define PIO2_COUNTER5 (1 << 5)
+
+static const int PIO2_COUNTER[6] = { PIO2_COUNTER0, PIO2_COUNTER1,
+ PIO2_COUNTER2, PIO2_COUNTER3,
+ PIO2_COUNTER4, PIO2_COUNTER5 };
+
+/* PIO2_REGS_CTRL (0x18) */
+#define PIO2_VME_INT_MASK 0x7
+#define PIO2_LED (1 << 6)
+#define PIO2_LOOP (1 << 7)
+
+/* PIO2_REGS_VME_VECTOR (0x19) */
+#define PIO2_VME_VECTOR_SPUR 0x0
+#define PIO2_VME_VECTOR_BANK0 0x1
+#define PIO2_VME_VECTOR_BANK1 0x2
+#define PIO2_VME_VECTOR_BANK2 0x3
+#define PIO2_VME_VECTOR_BANK3 0x4
+#define PIO2_VME_VECTOR_CNTR0 0x5
+#define PIO2_VME_VECTOR_CNTR1 0x6
+#define PIO2_VME_VECTOR_CNTR2 0x7
+#define PIO2_VME_VECTOR_CNTR3 0x8
+#define PIO2_VME_VECTOR_CNTR4 0x9
+#define PIO2_VME_VECTOR_CNTR5 0xa
+
+#define PIO2_VME_VECTOR_MASK 0xf0
+
+static const int PIO2_VECTOR_BANK[4] = { PIO2_VME_VECTOR_BANK0,
+ PIO2_VME_VECTOR_BANK1,
+ PIO2_VME_VECTOR_BANK2,
+ PIO2_VME_VECTOR_BANK3 };
+
+static const int PIO2_VECTOR_CNTR[6] = { PIO2_VME_VECTOR_CNTR0,
+ PIO2_VME_VECTOR_CNTR1,
+ PIO2_VME_VECTOR_CNTR2,
+ PIO2_VME_VECTOR_CNTR3,
+ PIO2_VME_VECTOR_CNTR4,
+ PIO2_VME_VECTOR_CNTR5 };
+
+/* PIO2_REGS_CNTRx (0x20 - 0x24 & 0x28 - 0x2c) */
+
+static const int PIO2_CNTR_DATA[6] = { PIO2_REGS_CNTR0, PIO2_REGS_CNTR1,
+ PIO2_REGS_CNTR2, PIO2_REGS_CNTR3,
+ PIO2_REGS_CNTR4, PIO2_REGS_CNTR5 };
+
+/* PIO2_REGS_CTRL_WRDx (0x26 & 0x2e) */
+
+static const int PIO2_CNTR_CTRL[6] = { PIO2_REGS_CTRL_WRD0,
+ PIO2_REGS_CTRL_WRD0,
+ PIO2_REGS_CTRL_WRD0,
+ PIO2_REGS_CTRL_WRD1,
+ PIO2_REGS_CTRL_WRD1,
+ PIO2_REGS_CTRL_WRD1 };
+
+#define PIO2_CNTR_SC_DEV0 0
+#define PIO2_CNTR_SC_DEV1 (1 << 6)
+#define PIO2_CNTR_SC_DEV2 (2 << 6)
+#define PIO2_CNTR_SC_RDBACK (3 << 6)
+
+static const int PIO2_CNTR_SC_DEV[6] = { PIO2_CNTR_SC_DEV0, PIO2_CNTR_SC_DEV1,
+ PIO2_CNTR_SC_DEV2, PIO2_CNTR_SC_DEV0,
+ PIO2_CNTR_SC_DEV1, PIO2_CNTR_SC_DEV2 };
+
+#define PIO2_CNTR_RW_LATCH 0
+#define PIO2_CNTR_RW_LSB (1 << 4)
+#define PIO2_CNTR_RW_MSB (2 << 4)
+#define PIO2_CNTR_RW_BOTH (3 << 4)
+
+#define PIO2_CNTR_MODE0 0
+#define PIO2_CNTR_MODE1 (1 << 1)
+#define PIO2_CNTR_MODE2 (2 << 1)
+#define PIO2_CNTR_MODE3 (3 << 1)
+#define PIO2_CNTR_MODE4 (4 << 1)
+#define PIO2_CNTR_MODE5 (5 << 1)
+
+#define PIO2_CNTR_BCD 1
+
+
+
+enum pio2_bank_config { NOFIT, INPUT, OUTPUT, BOTH };
+enum pio2_int_config { NONE = 0, LOW2HIGH = 1, HIGH2LOW = 2, EITHER = 4 };
+
+/* Bank configuration structure */
+struct pio2_io_bank {
+ enum pio2_bank_config config;
+ u8 value;
+ enum pio2_int_config irq[8];
+};
+
+/* Counter configuration structure */
+struct pio2_cntr {
+ int mode;
+ int count;
+};
+
+struct pio2_card {
+ int id;
+ int bus;
+ long base;
+ int irq_vector;
+ int irq_level;
+ char variant[6];
+ int led;
+
+ struct vme_dev *vdev;
+ struct vme_resource *window;
+
+ struct gpio_chip gc;
+ struct pio2_io_bank bank[4];
+
+ struct pio2_cntr cntr[6];
+};
+
+int pio2_cntr_reset(struct pio2_card *);
+
+int pio2_gpio_reset(struct pio2_card *);
+int pio2_gpio_init(struct pio2_card *);
+void pio2_gpio_exit(struct pio2_card *);
+
+#endif /* _VME_PIO2_H_ */
diff --git a/drivers/staging/vme/devices/vme_pio2_cntr.c b/drivers/staging/vme/devices/vme_pio2_cntr.c
new file mode 100644
index 000000000..6335471fa
--- /dev/null
+++ b/drivers/staging/vme/devices/vme_pio2_cntr.c
@@ -0,0 +1,71 @@
+/*
+ * GE PIO2 Counter Driver
+ *
+ * Author: Martyn Welch <martyn.welch@ge.com>
+ * Copyright 2009 GE Intelligent Platforms Embedded Systems, 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.
+ *
+ * The PIO-2 has 6 counters, currently this code just disables the interrupts
+ * and leaves them alone.
+ *
+ */
+
+#include <linux/device.h>
+#include <linux/types.h>
+#include <linux/gpio.h>
+#include <linux/vme.h>
+
+#include "vme_pio2.h"
+
+static int pio2_cntr_irq_set(struct pio2_card *card, int id)
+{
+ int retval;
+ u8 data;
+
+ data = PIO2_CNTR_SC_DEV[id] | PIO2_CNTR_RW_BOTH | card->cntr[id].mode;
+ retval = vme_master_write(card->window, &data, 1, PIO2_CNTR_CTRL[id]);
+ if (retval < 0)
+ return retval;
+
+ data = card->cntr[id].count & 0xFF;
+ retval = vme_master_write(card->window, &data, 1, PIO2_CNTR_DATA[id]);
+ if (retval < 0)
+ return retval;
+
+ data = (card->cntr[id].count >> 8) & 0xFF;
+ retval = vme_master_write(card->window, &data, 1, PIO2_CNTR_DATA[id]);
+ if (retval < 0)
+ return retval;
+
+ return 0;
+}
+
+int pio2_cntr_reset(struct pio2_card *card)
+{
+ int i, retval = 0;
+ u8 reg;
+
+ /* Clear down all timers */
+ for (i = 0; i < 6; i++) {
+ card->cntr[i].mode = PIO2_CNTR_MODE5;
+ card->cntr[i].count = 0;
+ retval = pio2_cntr_irq_set(card, i);
+ if (retval < 0)
+ return retval;
+ }
+
+ /* Ensure all counter interrupts are cleared */
+ do {
+ retval = vme_master_read(card->window, &reg, 1,
+ PIO2_REGS_INT_STAT_CNTR);
+ if (retval < 0)
+ return retval;
+ } while (reg != 0);
+
+ return retval;
+}
+
diff --git a/drivers/staging/vme/devices/vme_pio2_core.c b/drivers/staging/vme/devices/vme_pio2_core.c
new file mode 100644
index 000000000..eabbcc710
--- /dev/null
+++ b/drivers/staging/vme/devices/vme_pio2_core.c
@@ -0,0 +1,511 @@
+/*
+ * GE PIO2 6U VME I/O Driver
+ *
+ * Author: Martyn Welch <martyn.welch@ge.com>
+ * Copyright 2009 GE Intelligent Platforms Embedded Systems, 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.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/device.h>
+#include <linux/ctype.h>
+#include <linux/gpio.h>
+#include <linux/slab.h>
+#include <linux/vme.h>
+
+#include "vme_pio2.h"
+
+
+static const char driver_name[] = "pio2";
+
+static int bus[PIO2_CARDS_MAX];
+static int bus_num;
+static long base[PIO2_CARDS_MAX];
+static int base_num;
+static int vector[PIO2_CARDS_MAX];
+static int vector_num;
+static int level[PIO2_CARDS_MAX];
+static int level_num;
+static char *variant[PIO2_CARDS_MAX];
+static int variant_num;
+
+static bool loopback;
+
+static int pio2_match(struct vme_dev *);
+static int pio2_probe(struct vme_dev *);
+static int pio2_remove(struct vme_dev *);
+
+static int pio2_get_led(struct pio2_card *card)
+{
+ /* Can't read hardware, state saved in structure */
+ return card->led;
+}
+
+static int pio2_set_led(struct pio2_card *card, int state)
+{
+ u8 reg;
+ int retval;
+
+ reg = card->irq_level;
+
+ /* Register state inverse of led state */
+ if (!state)
+ reg |= PIO2_LED;
+
+ if (loopback)
+ reg |= PIO2_LOOP;
+
+ retval = vme_master_write(card->window, &reg, 1, PIO2_REGS_CTRL);
+ if (retval < 0)
+ return retval;
+
+ card->led = state ? 1 : 0;
+
+ return 0;
+}
+
+static void pio2_int(int level, int vector, void *ptr)
+{
+ int vec, i, channel, retval;
+ u8 reg;
+ struct pio2_card *card = ptr;
+
+ vec = vector & ~PIO2_VME_VECTOR_MASK;
+
+ switch (vec) {
+ case 0:
+ dev_warn(&card->vdev->dev, "Spurious Interrupt\n");
+ break;
+ case 1:
+ case 2:
+ case 3:
+ case 4:
+ /* Channels 0 to 7 */
+ retval = vme_master_read(card->window, &reg, 1,
+ PIO2_REGS_INT_STAT[vec - 1]);
+ if (retval < 0) {
+ dev_err(&card->vdev->dev,
+ "Unable to read IRQ status register\n");
+ return;
+ }
+ for (i = 0; i < 8; i++) {
+ channel = ((vec - 1) * 8) + i;
+ if (reg & PIO2_CHANNEL_BIT[channel])
+ dev_info(&card->vdev->dev,
+ "Interrupt on I/O channel %d\n",
+ channel);
+ }
+ break;
+ case 5:
+ case 6:
+ case 7:
+ case 8:
+ case 9:
+ case 10:
+ /* Counters are dealt with by their own handler */
+ dev_err(&card->vdev->dev,
+ "Counter interrupt\n");
+ break;
+ }
+}
+
+
+/*
+ * We return whether this has been successful - this is used in the probe to
+ * ensure we have a valid card.
+ */
+static int pio2_reset_card(struct pio2_card *card)
+{
+ int retval = 0;
+ u8 data = 0;
+
+ /* Clear main register*/
+ retval = vme_master_write(card->window, &data, 1, PIO2_REGS_CTRL);
+ if (retval < 0)
+ return retval;
+
+ /* Clear VME vector */
+ retval = vme_master_write(card->window, &data, 1, PIO2_REGS_VME_VECTOR);
+ if (retval < 0)
+ return retval;
+
+ /* Reset GPIO */
+ retval = pio2_gpio_reset(card);
+ if (retval < 0)
+ return retval;
+
+ /* Reset counters */
+ retval = pio2_cntr_reset(card);
+ if (retval < 0)
+ return retval;
+
+ return 0;
+}
+
+static struct vme_driver pio2_driver = {
+ .name = driver_name,
+ .match = pio2_match,
+ .probe = pio2_probe,
+ .remove = pio2_remove,
+};
+
+
+static int __init pio2_init(void)
+{
+ if (bus_num == 0) {
+ pr_err("No cards, skipping registration\n");
+ return -ENODEV;
+ }
+
+ if (bus_num > PIO2_CARDS_MAX) {
+ pr_err("Driver only able to handle %d PIO2 Cards\n",
+ PIO2_CARDS_MAX);
+ bus_num = PIO2_CARDS_MAX;
+ }
+
+ /* Register the PIO2 driver */
+ return vme_register_driver(&pio2_driver, bus_num);
+}
+
+static int pio2_match(struct vme_dev *vdev)
+{
+
+ if (vdev->num >= bus_num) {
+ dev_err(&vdev->dev,
+ "The enumeration of the VMEbus to which the board is connected must be specified\n");
+ return 0;
+ }
+
+ if (vdev->num >= base_num) {
+ dev_err(&vdev->dev,
+ "The VME address for the cards registers must be specified\n");
+ return 0;
+ }
+
+ if (vdev->num >= vector_num) {
+ dev_err(&vdev->dev,
+ "The IRQ vector used by the card must be specified\n");
+ return 0;
+ }
+
+ if (vdev->num >= level_num) {
+ dev_err(&vdev->dev,
+ "The IRQ level used by the card must be specified\n");
+ return 0;
+ }
+
+ if (vdev->num >= variant_num) {
+ dev_err(&vdev->dev, "The variant of the card must be specified\n");
+ return 0;
+ }
+
+ return 1;
+}
+
+static int pio2_probe(struct vme_dev *vdev)
+{
+ struct pio2_card *card;
+ int retval;
+ int i;
+ u8 reg;
+ int vec;
+
+ card = kzalloc(sizeof(struct pio2_card), GFP_KERNEL);
+ if (card == NULL) {
+ retval = -ENOMEM;
+ goto err_struct;
+ }
+
+ card->id = vdev->num;
+ card->bus = bus[card->id];
+ card->base = base[card->id];
+ card->irq_vector = vector[card->id];
+ card->irq_level = level[card->id] & PIO2_VME_INT_MASK;
+ strncpy(card->variant, variant[card->id], PIO2_VARIANT_LENGTH);
+ card->vdev = vdev;
+
+ for (i = 0; i < PIO2_VARIANT_LENGTH; i++) {
+
+ if (isdigit(card->variant[i]) == 0) {
+ dev_err(&card->vdev->dev, "Variant invalid\n");
+ retval = -EINVAL;
+ goto err_variant;
+ }
+ }
+
+ /*
+ * Bottom 4 bits of VME interrupt vector used to determine source,
+ * provided vector should only use upper 4 bits.
+ */
+ if (card->irq_vector & ~PIO2_VME_VECTOR_MASK) {
+ dev_err(&card->vdev->dev,
+ "Invalid VME IRQ Vector, vector must not use lower 4 bits\n");
+ retval = -EINVAL;
+ goto err_vector;
+ }
+
+ /*
+ * There is no way to determine the build variant or whether each bank
+ * is input, output or both at run time. The inputs are also inverted
+ * if configured as both.
+ *
+ * We pass in the board variant and use that to determine the
+ * configuration of the banks.
+ */
+ for (i = 1; i < PIO2_VARIANT_LENGTH; i++) {
+ switch (card->variant[i]) {
+ case '0':
+ card->bank[i-1].config = NOFIT;
+ break;
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ card->bank[i-1].config = INPUT;
+ break;
+ case '5':
+ card->bank[i-1].config = OUTPUT;
+ break;
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ card->bank[i-1].config = BOTH;
+ break;
+ }
+ }
+
+ /* Get a master window and position over regs */
+ card->window = vme_master_request(vdev, VME_A24, VME_SCT, VME_D16);
+ if (card->window == NULL) {
+ dev_err(&card->vdev->dev,
+ "Unable to assign VME master resource\n");
+ retval = -EIO;
+ goto err_window;
+ }
+
+ retval = vme_master_set(card->window, 1, card->base, 0x10000, VME_A24,
+ (VME_SCT | VME_USER | VME_DATA), VME_D16);
+ if (retval) {
+ dev_err(&card->vdev->dev,
+ "Unable to configure VME master resource\n");
+ goto err_set;
+ }
+
+ /*
+ * There is also no obvious register which we can probe to determine
+ * whether the provided base is valid. If we can read the "ID Register"
+ * offset and the reset function doesn't error, assume we have a valid
+ * location.
+ */
+ retval = vme_master_read(card->window, &reg, 1, PIO2_REGS_ID);
+ if (retval < 0) {
+ dev_err(&card->vdev->dev, "Unable to read from device\n");
+ goto err_read;
+ }
+
+ dev_dbg(&card->vdev->dev, "ID Register:%x\n", reg);
+
+ /*
+ * Ensure all the I/O is cleared. We can't read back the states, so
+ * this is the only method we have to ensure that the I/O is in a known
+ * state.
+ */
+ retval = pio2_reset_card(card);
+ if (retval) {
+ dev_err(&card->vdev->dev,
+ "Failed to reset card, is location valid?\n");
+ retval = -ENODEV;
+ goto err_reset;
+ }
+
+ /* Configure VME Interrupts */
+ reg = card->irq_level;
+ if (pio2_get_led(card))
+ reg |= PIO2_LED;
+ if (loopback)
+ reg |= PIO2_LOOP;
+ retval = vme_master_write(card->window, &reg, 1, PIO2_REGS_CTRL);
+ if (retval < 0)
+ return retval;
+
+ /* Set VME vector */
+ retval = vme_master_write(card->window, &card->irq_vector, 1,
+ PIO2_REGS_VME_VECTOR);
+ if (retval < 0)
+ return retval;
+
+ /* Attach spurious interrupt handler. */
+ vec = card->irq_vector | PIO2_VME_VECTOR_SPUR;
+
+ retval = vme_irq_request(vdev, card->irq_level, vec,
+ &pio2_int, (void *)card);
+ if (retval < 0) {
+ dev_err(&card->vdev->dev,
+ "Unable to attach VME interrupt vector0x%x, level 0x%x\n",
+ vec, card->irq_level);
+ goto err_irq;
+ }
+
+ /* Attach GPIO interrupt handlers. */
+ for (i = 0; i < 4; i++) {
+ vec = card->irq_vector | PIO2_VECTOR_BANK[i];
+
+ retval = vme_irq_request(vdev, card->irq_level, vec,
+ &pio2_int, (void *)card);
+ if (retval < 0) {
+ dev_err(&card->vdev->dev,
+ "Unable to attach VME interrupt vector0x%x, level 0x%x\n",
+ vec, card->irq_level);
+ goto err_gpio_irq;
+ }
+ }
+
+ /* Attach counter interrupt handlers. */
+ for (i = 0; i < 6; i++) {
+ vec = card->irq_vector | PIO2_VECTOR_CNTR[i];
+
+ retval = vme_irq_request(vdev, card->irq_level, vec,
+ &pio2_int, (void *)card);
+ if (retval < 0) {
+ dev_err(&card->vdev->dev,
+ "Unable to attach VME interrupt vector0x%x, level 0x%x\n",
+ vec, card->irq_level);
+ goto err_cntr_irq;
+ }
+ }
+
+ /* Register IO */
+ retval = pio2_gpio_init(card);
+ if (retval < 0) {
+ dev_err(&card->vdev->dev,
+ "Unable to register with GPIO framework\n");
+ goto err_gpio;
+ }
+
+ /* Set LED - This also sets interrupt level */
+ retval = pio2_set_led(card, 0);
+ if (retval < 0) {
+ dev_err(&card->vdev->dev, "Unable to set LED\n");
+ goto err_led;
+ }
+
+ dev_set_drvdata(&card->vdev->dev, card);
+
+ dev_info(&card->vdev->dev,
+ "PIO2 (variant %s) configured at 0x%lx\n", card->variant,
+ card->base);
+
+ return 0;
+
+err_led:
+ pio2_gpio_exit(card);
+err_gpio:
+ i = 6;
+err_cntr_irq:
+ while (i > 0) {
+ i--;
+ vec = card->irq_vector | PIO2_VECTOR_CNTR[i];
+ vme_irq_free(vdev, card->irq_level, vec);
+ }
+
+ i = 4;
+err_gpio_irq:
+ while (i > 0) {
+ i--;
+ vec = card->irq_vector | PIO2_VECTOR_BANK[i];
+ vme_irq_free(vdev, card->irq_level, vec);
+ }
+
+ vec = (card->irq_vector & PIO2_VME_VECTOR_MASK) | PIO2_VME_VECTOR_SPUR;
+ vme_irq_free(vdev, card->irq_level, vec);
+err_irq:
+ pio2_reset_card(card);
+err_reset:
+err_read:
+ vme_master_set(card->window, 0, 0, 0, VME_A16, 0, VME_D16);
+err_set:
+ vme_master_free(card->window);
+err_window:
+err_vector:
+err_variant:
+ kfree(card);
+err_struct:
+ return retval;
+}
+
+static int pio2_remove(struct vme_dev *vdev)
+{
+ int vec;
+ int i;
+
+ struct pio2_card *card = dev_get_drvdata(&vdev->dev);
+
+ pio2_gpio_exit(card);
+
+ for (i = 0; i < 6; i++) {
+ vec = card->irq_vector | PIO2_VECTOR_CNTR[i];
+ vme_irq_free(vdev, card->irq_level, vec);
+ }
+
+ for (i = 0; i < 4; i++) {
+ vec = card->irq_vector | PIO2_VECTOR_BANK[i];
+ vme_irq_free(vdev, card->irq_level, vec);
+ }
+
+ vec = (card->irq_vector & PIO2_VME_VECTOR_MASK) | PIO2_VME_VECTOR_SPUR;
+ vme_irq_free(vdev, card->irq_level, vec);
+
+ pio2_reset_card(card);
+
+ vme_master_set(card->window, 0, 0, 0, VME_A16, 0, VME_D16);
+
+ vme_master_free(card->window);
+
+ kfree(card);
+
+ return 0;
+}
+
+static void __exit pio2_exit(void)
+{
+ vme_unregister_driver(&pio2_driver);
+}
+
+
+/* These are required for each board */
+MODULE_PARM_DESC(bus, "Enumeration of VMEbus to which the board is connected");
+module_param_array(bus, int, &bus_num, S_IRUGO);
+
+MODULE_PARM_DESC(base, "Base VME address for PIO2 Registers");
+module_param_array(base, long, &base_num, S_IRUGO);
+
+MODULE_PARM_DESC(vector, "VME IRQ Vector (Lower 4 bits masked)");
+module_param_array(vector, int, &vector_num, S_IRUGO);
+
+MODULE_PARM_DESC(level, "VME IRQ Level");
+module_param_array(level, int, &level_num, S_IRUGO);
+
+MODULE_PARM_DESC(variant, "Last 4 characters of PIO2 board variant");
+module_param_array(variant, charp, &variant_num, S_IRUGO);
+
+/* This is for debugging */
+MODULE_PARM_DESC(loopback, "Enable loopback mode on all cards");
+module_param(loopback, bool, S_IRUGO);
+
+MODULE_DESCRIPTION("GE PIO2 6U VME I/O Driver");
+MODULE_AUTHOR("Martyn Welch <martyn.welch@ge.com");
+MODULE_LICENSE("GPL");
+
+module_init(pio2_init);
+module_exit(pio2_exit);
+
diff --git a/drivers/staging/vme/devices/vme_pio2_gpio.c b/drivers/staging/vme/devices/vme_pio2_gpio.c
new file mode 100644
index 000000000..77901b345
--- /dev/null
+++ b/drivers/staging/vme/devices/vme_pio2_gpio.c
@@ -0,0 +1,226 @@
+/*
+ * GE PIO2 GPIO Driver
+ *
+ * Author: Martyn Welch <martyn.welch@ge.com>
+ * Copyright 2009 GE Intelligent Platforms Embedded Systems, 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.
+ */
+
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/ctype.h>
+#include <linux/gpio.h>
+#include <linux/slab.h>
+#include <linux/vme.h>
+
+#include "vme_pio2.h"
+
+static const char driver_name[] = "pio2_gpio";
+
+static struct pio2_card *gpio_to_pio2_card(struct gpio_chip *chip)
+{
+ return container_of(chip, struct pio2_card, gc);
+}
+
+static int pio2_gpio_get(struct gpio_chip *chip, unsigned int offset)
+{
+ u8 reg;
+ int retval;
+ struct pio2_card *card = gpio_to_pio2_card(chip);
+
+ if ((card->bank[PIO2_CHANNEL_BANK[offset]].config == OUTPUT) |
+ (card->bank[PIO2_CHANNEL_BANK[offset]].config == NOFIT)) {
+
+ dev_err(&card->vdev->dev, "Channel not available as input\n");
+ return 0;
+ }
+
+ retval = vme_master_read(card->window, &reg, 1,
+ PIO2_REGS_DATA[PIO2_CHANNEL_BANK[offset]]);
+ if (retval < 0) {
+ dev_err(&card->vdev->dev, "Unable to read from GPIO\n");
+ return 0;
+ }
+
+ /*
+ * Remember, input on channels configured as both input and output
+ * are inverted!
+ */
+ if (reg & PIO2_CHANNEL_BIT[offset]) {
+ if (card->bank[PIO2_CHANNEL_BANK[offset]].config != BOTH)
+ return 0;
+
+ return 1;
+ }
+
+ if (card->bank[PIO2_CHANNEL_BANK[offset]].config != BOTH)
+ return 1;
+
+ return 0;
+}
+
+static void pio2_gpio_set(struct gpio_chip *chip, unsigned int offset,
+ int value)
+{
+ u8 reg;
+ int retval;
+ struct pio2_card *card = gpio_to_pio2_card(chip);
+
+ if ((card->bank[PIO2_CHANNEL_BANK[offset]].config == INPUT) |
+ (card->bank[PIO2_CHANNEL_BANK[offset]].config == NOFIT)) {
+
+ dev_err(&card->vdev->dev, "Channel not available as output\n");
+ return;
+ }
+
+ if (value)
+ reg = card->bank[PIO2_CHANNEL_BANK[offset]].value |
+ PIO2_CHANNEL_BIT[offset];
+ else
+ reg = card->bank[PIO2_CHANNEL_BANK[offset]].value &
+ ~PIO2_CHANNEL_BIT[offset];
+
+ retval = vme_master_write(card->window, &reg, 1,
+ PIO2_REGS_DATA[PIO2_CHANNEL_BANK[offset]]);
+ if (retval < 0) {
+ dev_err(&card->vdev->dev, "Unable to write to GPIO\n");
+ return;
+ }
+
+ card->bank[PIO2_CHANNEL_BANK[offset]].value = reg;
+}
+
+/* Directionality configured at board build - send appropriate response */
+static int pio2_gpio_dir_in(struct gpio_chip *chip, unsigned offset)
+{
+ int data;
+ struct pio2_card *card = gpio_to_pio2_card(chip);
+
+ if ((card->bank[PIO2_CHANNEL_BANK[offset]].config == OUTPUT) |
+ (card->bank[PIO2_CHANNEL_BANK[offset]].config == NOFIT)) {
+ dev_err(&card->vdev->dev,
+ "Channel directionality not configurable at runtime\n");
+
+ data = -EINVAL;
+ } else {
+ data = 0;
+ }
+
+ return data;
+}
+
+/* Directionality configured at board build - send appropriate response */
+static int pio2_gpio_dir_out(struct gpio_chip *chip, unsigned offset, int value)
+{
+ int data;
+ struct pio2_card *card = gpio_to_pio2_card(chip);
+
+ if ((card->bank[PIO2_CHANNEL_BANK[offset]].config == INPUT) |
+ (card->bank[PIO2_CHANNEL_BANK[offset]].config == NOFIT)) {
+ dev_err(&card->vdev->dev,
+ "Channel directionality not configurable at runtime\n");
+
+ data = -EINVAL;
+ } else {
+ data = 0;
+ }
+
+ return data;
+}
+
+/*
+ * We return whether this has been successful - this is used in the probe to
+ * ensure we have a valid card.
+ */
+int pio2_gpio_reset(struct pio2_card *card)
+{
+ int retval = 0;
+ int i, j;
+
+ u8 data = 0;
+
+ /* Zero output registers */
+ for (i = 0; i < 4; i++) {
+ retval = vme_master_write(card->window, &data, 1,
+ PIO2_REGS_DATA[i]);
+ if (retval < 0)
+ return retval;
+ card->bank[i].value = 0;
+ }
+
+ /* Set input interrupt masks */
+ for (i = 0; i < 4; i++) {
+ retval = vme_master_write(card->window, &data, 1,
+ PIO2_REGS_INT_MASK[i * 2]);
+ if (retval < 0)
+ return retval;
+
+ retval = vme_master_write(card->window, &data, 1,
+ PIO2_REGS_INT_MASK[(i * 2) + 1]);
+ if (retval < 0)
+ return retval;
+
+ for (j = 0; j < 8; j++)
+ card->bank[i].irq[j] = NONE;
+ }
+
+ /* Ensure all I/O interrupts are cleared */
+ for (i = 0; i < 4; i++) {
+ do {
+ retval = vme_master_read(card->window, &data, 1,
+ PIO2_REGS_INT_STAT[i]);
+ if (retval < 0)
+ return retval;
+ } while (data != 0);
+ }
+
+ return 0;
+}
+
+int pio2_gpio_init(struct pio2_card *card)
+{
+ int retval = 0;
+ char *label;
+
+ label = kasprintf(GFP_KERNEL,
+ "%s@%s", driver_name, dev_name(&card->vdev->dev));
+ if (label == NULL)
+ return -ENOMEM;
+
+ card->gc.label = label;
+
+ card->gc.ngpio = PIO2_NUM_CHANNELS;
+ /* Dynamic allocation of base */
+ card->gc.base = -1;
+ /* Setup pointers to chip functions */
+ card->gc.direction_input = pio2_gpio_dir_in;
+ card->gc.direction_output = pio2_gpio_dir_out;
+ card->gc.get = pio2_gpio_get;
+ card->gc.set = pio2_gpio_set;
+
+ /* This function adds a memory mapped GPIO chip */
+ retval = gpiochip_add(&(card->gc));
+ if (retval) {
+ dev_err(&card->vdev->dev, "Unable to register GPIO\n");
+ kfree(card->gc.label);
+ }
+
+ return retval;
+};
+
+void pio2_gpio_exit(struct pio2_card *card)
+{
+ const char *label = card->gc.label;
+
+ gpiochip_remove(&(card->gc));
+ kfree(label);
+}
+
diff --git a/drivers/staging/vme/devices/vme_user.c b/drivers/staging/vme/devices/vme_user.c
new file mode 100644
index 000000000..19ba749bb
--- /dev/null
+++ b/drivers/staging/vme/devices/vme_user.c
@@ -0,0 +1,962 @@
+/*
+ * VMEbus User access driver
+ *
+ * Author: Martyn Welch <martyn.welch@ge.com>
+ * Copyright 2008 GE Intelligent Platforms Embedded Systems, Inc.
+ *
+ * Based on work by:
+ * Tom Armistead and Ajit Prem
+ * Copyright 2004 Motorola 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.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/atomic.h>
+#include <linux/cdev.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/dma-mapping.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/ioctl.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/module.h>
+#include <linux/pagemap.h>
+#include <linux/pci.h>
+#include <linux/mutex.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/syscalls.h>
+#include <linux/types.h>
+
+#include <linux/io.h>
+#include <linux/uaccess.h>
+#include <linux/vme.h>
+
+#include "vme_user.h"
+
+static const char driver_name[] = "vme_user";
+
+static int bus[VME_USER_BUS_MAX];
+static unsigned int bus_num;
+
+/* Currently Documentation/devices.txt defines the following for VME:
+ *
+ * 221 char VME bus
+ * 0 = /dev/bus/vme/m0 First master image
+ * 1 = /dev/bus/vme/m1 Second master image
+ * 2 = /dev/bus/vme/m2 Third master image
+ * 3 = /dev/bus/vme/m3 Fourth master image
+ * 4 = /dev/bus/vme/s0 First slave image
+ * 5 = /dev/bus/vme/s1 Second slave image
+ * 6 = /dev/bus/vme/s2 Third slave image
+ * 7 = /dev/bus/vme/s3 Fourth slave image
+ * 8 = /dev/bus/vme/ctl Control
+ *
+ * It is expected that all VME bus drivers will use the
+ * same interface. For interface documentation see
+ * http://www.vmelinux.org/.
+ *
+ * However the VME driver at http://www.vmelinux.org/ is rather old and doesn't
+ * even support the tsi148 chipset (which has 8 master and 8 slave windows).
+ * We'll run with this for now as far as possible, however it probably makes
+ * sense to get rid of the old mappings and just do everything dynamically.
+ *
+ * So for now, we'll restrict the driver to providing 4 masters and 4 slaves as
+ * defined above and try to support at least some of the interface from
+ * http://www.vmelinux.org/ as an alternative the driver can be written
+ * providing a saner interface later.
+ *
+ * The vmelinux.org driver never supported slave images, the devices reserved
+ * for slaves were repurposed to support all 8 master images on the UniverseII!
+ * We shall support 4 masters and 4 slaves with this driver.
+ */
+#define VME_MAJOR 221 /* VME Major Device Number */
+#define VME_DEVS 9 /* Number of dev entries */
+
+#define MASTER_MINOR 0
+#define MASTER_MAX 3
+#define SLAVE_MINOR 4
+#define SLAVE_MAX 7
+#define CONTROL_MINOR 8
+
+#define PCI_BUF_SIZE 0x20000 /* Size of one slave image buffer */
+
+/*
+ * Structure to handle image related parameters.
+ */
+struct image_desc {
+ void *kern_buf; /* Buffer address in kernel space */
+ dma_addr_t pci_buf; /* Buffer address in PCI address space */
+ unsigned long long size_buf; /* Buffer size */
+ struct mutex mutex; /* Mutex for locking image */
+ struct device *device; /* Sysfs device */
+ struct vme_resource *resource; /* VME resource */
+ int users; /* Number of current users */
+ int mmap_count; /* Number of current mmap's */
+};
+static struct image_desc image[VME_DEVS];
+
+struct driver_stats {
+ unsigned long reads;
+ unsigned long writes;
+ unsigned long ioctls;
+ unsigned long irqs;
+ unsigned long berrs;
+ unsigned long dmaerrors;
+ unsigned long timeouts;
+ unsigned long external;
+};
+static struct driver_stats statistics;
+
+static struct cdev *vme_user_cdev; /* Character device */
+static struct class *vme_user_sysfs_class; /* Sysfs class */
+static struct vme_dev *vme_user_bridge; /* Pointer to user device */
+
+
+static const int type[VME_DEVS] = { MASTER_MINOR, MASTER_MINOR,
+ MASTER_MINOR, MASTER_MINOR,
+ SLAVE_MINOR, SLAVE_MINOR,
+ SLAVE_MINOR, SLAVE_MINOR,
+ CONTROL_MINOR
+ };
+
+
+static int vme_user_open(struct inode *, struct file *);
+static int vme_user_release(struct inode *, struct file *);
+static ssize_t vme_user_read(struct file *, char __user *, size_t, loff_t *);
+static ssize_t vme_user_write(struct file *, const char __user *, size_t,
+ loff_t *);
+static loff_t vme_user_llseek(struct file *, loff_t, int);
+static long vme_user_unlocked_ioctl(struct file *, unsigned int, unsigned long);
+static int vme_user_mmap(struct file *file, struct vm_area_struct *vma);
+
+static void vme_user_vm_open(struct vm_area_struct *vma);
+static void vme_user_vm_close(struct vm_area_struct *vma);
+
+static int vme_user_match(struct vme_dev *);
+static int vme_user_probe(struct vme_dev *);
+static int vme_user_remove(struct vme_dev *);
+
+static const struct file_operations vme_user_fops = {
+ .open = vme_user_open,
+ .release = vme_user_release,
+ .read = vme_user_read,
+ .write = vme_user_write,
+ .llseek = vme_user_llseek,
+ .unlocked_ioctl = vme_user_unlocked_ioctl,
+ .compat_ioctl = vme_user_unlocked_ioctl,
+ .mmap = vme_user_mmap,
+};
+
+struct vme_user_vma_priv {
+ unsigned int minor;
+ atomic_t refcnt;
+};
+
+static const struct vm_operations_struct vme_user_vm_ops = {
+ .open = vme_user_vm_open,
+ .close = vme_user_vm_close,
+};
+
+
+/*
+ * Reset all the statistic counters
+ */
+static void reset_counters(void)
+{
+ statistics.reads = 0;
+ statistics.writes = 0;
+ statistics.ioctls = 0;
+ statistics.irqs = 0;
+ statistics.berrs = 0;
+ statistics.dmaerrors = 0;
+ statistics.timeouts = 0;
+}
+
+static int vme_user_open(struct inode *inode, struct file *file)
+{
+ int err;
+ unsigned int minor = MINOR(inode->i_rdev);
+
+ mutex_lock(&image[minor].mutex);
+ /* Allow device to be opened if a resource is needed and allocated. */
+ if (minor < CONTROL_MINOR && image[minor].resource == NULL) {
+ pr_err("No resources allocated for device\n");
+ err = -EINVAL;
+ goto err_res;
+ }
+
+ /* Increment user count */
+ image[minor].users++;
+
+ mutex_unlock(&image[minor].mutex);
+
+ return 0;
+
+err_res:
+ mutex_unlock(&image[minor].mutex);
+
+ return err;
+}
+
+static int vme_user_release(struct inode *inode, struct file *file)
+{
+ unsigned int minor = MINOR(inode->i_rdev);
+
+ mutex_lock(&image[minor].mutex);
+
+ /* Decrement user count */
+ image[minor].users--;
+
+ mutex_unlock(&image[minor].mutex);
+
+ return 0;
+}
+
+/*
+ * We are going ot alloc a page during init per window for small transfers.
+ * Small transfers will go VME -> buffer -> user space. Larger (more than a
+ * page) transfers will lock the user space buffer into memory and then
+ * transfer the data directly into the user space buffers.
+ */
+static ssize_t resource_to_user(int minor, char __user *buf, size_t count,
+ loff_t *ppos)
+{
+ ssize_t retval;
+ ssize_t copied = 0;
+
+ if (count <= image[minor].size_buf) {
+ /* We copy to kernel buffer */
+ copied = vme_master_read(image[minor].resource,
+ image[minor].kern_buf, count, *ppos);
+ if (copied < 0)
+ return (int)copied;
+
+ retval = __copy_to_user(buf, image[minor].kern_buf,
+ (unsigned long)copied);
+ if (retval != 0) {
+ copied = (copied - retval);
+ pr_info("User copy failed\n");
+ return -EINVAL;
+ }
+
+ } else {
+ /* XXX Need to write this */
+ pr_info("Currently don't support large transfers\n");
+ /* Map in pages from userspace */
+
+ /* Call vme_master_read to do the transfer */
+ return -EINVAL;
+ }
+
+ return copied;
+}
+
+/*
+ * We are going to alloc a page during init per window for small transfers.
+ * Small transfers will go user space -> buffer -> VME. Larger (more than a
+ * page) transfers will lock the user space buffer into memory and then
+ * transfer the data directly from the user space buffers out to VME.
+ */
+static ssize_t resource_from_user(unsigned int minor, const char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ ssize_t retval;
+ ssize_t copied = 0;
+
+ if (count <= image[minor].size_buf) {
+ retval = __copy_from_user(image[minor].kern_buf, buf,
+ (unsigned long)count);
+ if (retval != 0)
+ copied = (copied - retval);
+ else
+ copied = count;
+
+ copied = vme_master_write(image[minor].resource,
+ image[minor].kern_buf, copied, *ppos);
+ } else {
+ /* XXX Need to write this */
+ pr_info("Currently don't support large transfers\n");
+ /* Map in pages from userspace */
+
+ /* Call vme_master_write to do the transfer */
+ return -EINVAL;
+ }
+
+ return copied;
+}
+
+static ssize_t buffer_to_user(unsigned int minor, char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ void *image_ptr;
+ ssize_t retval;
+
+ image_ptr = image[minor].kern_buf + *ppos;
+
+ retval = __copy_to_user(buf, image_ptr, (unsigned long)count);
+ if (retval != 0) {
+ retval = (count - retval);
+ pr_warn("Partial copy to userspace\n");
+ } else
+ retval = count;
+
+ /* Return number of bytes successfully read */
+ return retval;
+}
+
+static ssize_t buffer_from_user(unsigned int minor, const char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ void *image_ptr;
+ size_t retval;
+
+ image_ptr = image[minor].kern_buf + *ppos;
+
+ retval = __copy_from_user(image_ptr, buf, (unsigned long)count);
+ if (retval != 0) {
+ retval = (count - retval);
+ pr_warn("Partial copy to userspace\n");
+ } else
+ retval = count;
+
+ /* Return number of bytes successfully read */
+ return retval;
+}
+
+static ssize_t vme_user_read(struct file *file, char __user *buf, size_t count,
+ loff_t *ppos)
+{
+ unsigned int minor = MINOR(file_inode(file)->i_rdev);
+ ssize_t retval;
+ size_t image_size;
+ size_t okcount;
+
+ if (minor == CONTROL_MINOR)
+ return 0;
+
+ mutex_lock(&image[minor].mutex);
+
+ /* XXX Do we *really* want this helper - we can use vme_*_get ? */
+ image_size = vme_get_size(image[minor].resource);
+
+ /* Ensure we are starting at a valid location */
+ if ((*ppos < 0) || (*ppos > (image_size - 1))) {
+ mutex_unlock(&image[minor].mutex);
+ return 0;
+ }
+
+ /* Ensure not reading past end of the image */
+ if (*ppos + count > image_size)
+ okcount = image_size - *ppos;
+ else
+ okcount = count;
+
+ switch (type[minor]) {
+ case MASTER_MINOR:
+ retval = resource_to_user(minor, buf, okcount, ppos);
+ break;
+ case SLAVE_MINOR:
+ retval = buffer_to_user(minor, buf, okcount, ppos);
+ break;
+ default:
+ retval = -EINVAL;
+ }
+
+ mutex_unlock(&image[minor].mutex);
+ if (retval > 0)
+ *ppos += retval;
+
+ return retval;
+}
+
+static ssize_t vme_user_write(struct file *file, const char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ unsigned int minor = MINOR(file_inode(file)->i_rdev);
+ ssize_t retval;
+ size_t image_size;
+ size_t okcount;
+
+ if (minor == CONTROL_MINOR)
+ return 0;
+
+ mutex_lock(&image[minor].mutex);
+
+ image_size = vme_get_size(image[minor].resource);
+
+ /* Ensure we are starting at a valid location */
+ if ((*ppos < 0) || (*ppos > (image_size - 1))) {
+ mutex_unlock(&image[minor].mutex);
+ return 0;
+ }
+
+ /* Ensure not reading past end of the image */
+ if (*ppos + count > image_size)
+ okcount = image_size - *ppos;
+ else
+ okcount = count;
+
+ switch (type[minor]) {
+ case MASTER_MINOR:
+ retval = resource_from_user(minor, buf, okcount, ppos);
+ break;
+ case SLAVE_MINOR:
+ retval = buffer_from_user(minor, buf, okcount, ppos);
+ break;
+ default:
+ retval = -EINVAL;
+ }
+
+ mutex_unlock(&image[minor].mutex);
+
+ if (retval > 0)
+ *ppos += retval;
+
+ return retval;
+}
+
+static loff_t vme_user_llseek(struct file *file, loff_t off, int whence)
+{
+ unsigned int minor = MINOR(file_inode(file)->i_rdev);
+ size_t image_size;
+ loff_t res;
+
+ if (minor == CONTROL_MINOR)
+ return -EINVAL;
+
+ mutex_lock(&image[minor].mutex);
+ image_size = vme_get_size(image[minor].resource);
+ res = fixed_size_llseek(file, off, whence, image_size);
+ mutex_unlock(&image[minor].mutex);
+
+ return res;
+}
+
+/*
+ * The ioctls provided by the old VME access method (the one at vmelinux.org)
+ * are most certainly wrong as the effectively push the registers layout
+ * through to user space. Given that the VME core can handle multiple bridges,
+ * with different register layouts this is most certainly not the way to go.
+ *
+ * We aren't using the structures defined in the Motorola driver either - these
+ * are also quite low level, however we should use the definitions that have
+ * already been defined.
+ */
+static int vme_user_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ struct vme_master master;
+ struct vme_slave slave;
+ struct vme_irq_id irq_req;
+ unsigned long copied;
+ unsigned int minor = MINOR(inode->i_rdev);
+ int retval;
+ dma_addr_t pci_addr;
+ void __user *argp = (void __user *)arg;
+
+ statistics.ioctls++;
+
+ switch (type[minor]) {
+ case CONTROL_MINOR:
+ switch (cmd) {
+ case VME_IRQ_GEN:
+ copied = copy_from_user(&irq_req, argp,
+ sizeof(struct vme_irq_id));
+ if (copied != 0) {
+ pr_warn("Partial copy from userspace\n");
+ return -EFAULT;
+ }
+
+ return vme_irq_generate(vme_user_bridge,
+ irq_req.level,
+ irq_req.statid);
+ }
+ break;
+ case MASTER_MINOR:
+ switch (cmd) {
+ case VME_GET_MASTER:
+ memset(&master, 0, sizeof(struct vme_master));
+
+ /* XXX We do not want to push aspace, cycle and width
+ * to userspace as they are
+ */
+ retval = vme_master_get(image[minor].resource,
+ &master.enable, &master.vme_addr,
+ &master.size, &master.aspace,
+ &master.cycle, &master.dwidth);
+
+ copied = copy_to_user(argp, &master,
+ sizeof(struct vme_master));
+ if (copied != 0) {
+ pr_warn("Partial copy to userspace\n");
+ return -EFAULT;
+ }
+
+ return retval;
+
+ case VME_SET_MASTER:
+
+ if (image[minor].mmap_count != 0) {
+ pr_warn("Can't adjust mapped window\n");
+ return -EPERM;
+ }
+
+ copied = copy_from_user(&master, argp, sizeof(master));
+ if (copied != 0) {
+ pr_warn("Partial copy from userspace\n");
+ return -EFAULT;
+ }
+
+ /* XXX We do not want to push aspace, cycle and width
+ * to userspace as they are
+ */
+ return vme_master_set(image[minor].resource,
+ master.enable, master.vme_addr, master.size,
+ master.aspace, master.cycle, master.dwidth);
+
+ break;
+ }
+ break;
+ case SLAVE_MINOR:
+ switch (cmd) {
+ case VME_GET_SLAVE:
+ memset(&slave, 0, sizeof(struct vme_slave));
+
+ /* XXX We do not want to push aspace, cycle and width
+ * to userspace as they are
+ */
+ retval = vme_slave_get(image[minor].resource,
+ &slave.enable, &slave.vme_addr,
+ &slave.size, &pci_addr, &slave.aspace,
+ &slave.cycle);
+
+ copied = copy_to_user(argp, &slave,
+ sizeof(struct vme_slave));
+ if (copied != 0) {
+ pr_warn("Partial copy to userspace\n");
+ return -EFAULT;
+ }
+
+ return retval;
+
+ case VME_SET_SLAVE:
+
+ copied = copy_from_user(&slave, argp, sizeof(slave));
+ if (copied != 0) {
+ pr_warn("Partial copy from userspace\n");
+ return -EFAULT;
+ }
+
+ /* XXX We do not want to push aspace, cycle and width
+ * to userspace as they are
+ */
+ return vme_slave_set(image[minor].resource,
+ slave.enable, slave.vme_addr, slave.size,
+ image[minor].pci_buf, slave.aspace,
+ slave.cycle);
+
+ break;
+ }
+ break;
+ }
+
+ return -EINVAL;
+}
+
+static long
+vme_user_unlocked_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+ int ret;
+ struct inode *inode = file_inode(file);
+ unsigned int minor = MINOR(inode->i_rdev);
+
+ mutex_lock(&image[minor].mutex);
+ ret = vme_user_ioctl(inode, file, cmd, arg);
+ mutex_unlock(&image[minor].mutex);
+
+ return ret;
+}
+
+static void vme_user_vm_open(struct vm_area_struct *vma)
+{
+ struct vme_user_vma_priv *vma_priv = vma->vm_private_data;
+
+ atomic_inc(&vma_priv->refcnt);
+}
+
+static void vme_user_vm_close(struct vm_area_struct *vma)
+{
+ struct vme_user_vma_priv *vma_priv = vma->vm_private_data;
+ unsigned int minor = vma_priv->minor;
+
+ if (!atomic_dec_and_test(&vma_priv->refcnt))
+ return;
+
+ mutex_lock(&image[minor].mutex);
+ image[minor].mmap_count--;
+ mutex_unlock(&image[minor].mutex);
+
+ kfree(vma_priv);
+}
+
+static int vme_user_master_mmap(unsigned int minor, struct vm_area_struct *vma)
+{
+ int err;
+ struct vme_user_vma_priv *vma_priv;
+
+ mutex_lock(&image[minor].mutex);
+
+ err = vme_master_mmap(image[minor].resource, vma);
+ if (err) {
+ mutex_unlock(&image[minor].mutex);
+ return err;
+ }
+
+ vma_priv = kmalloc(sizeof(struct vme_user_vma_priv), GFP_KERNEL);
+ if (vma_priv == NULL) {
+ mutex_unlock(&image[minor].mutex);
+ return -ENOMEM;
+ }
+
+ vma_priv->minor = minor;
+ atomic_set(&vma_priv->refcnt, 1);
+ vma->vm_ops = &vme_user_vm_ops;
+ vma->vm_private_data = vma_priv;
+
+ image[minor].mmap_count++;
+
+ mutex_unlock(&image[minor].mutex);
+
+ return 0;
+}
+
+static int vme_user_mmap(struct file *file, struct vm_area_struct *vma)
+{
+ unsigned int minor = MINOR(file_inode(file)->i_rdev);
+
+ if (type[minor] == MASTER_MINOR)
+ return vme_user_master_mmap(minor, vma);
+
+ return -ENODEV;
+}
+
+
+/*
+ * Unallocate a previously allocated buffer
+ */
+static void buf_unalloc(int num)
+{
+ if (image[num].kern_buf) {
+#ifdef VME_DEBUG
+ pr_debug("UniverseII:Releasing buffer at %p\n",
+ image[num].pci_buf);
+#endif
+
+ vme_free_consistent(image[num].resource, image[num].size_buf,
+ image[num].kern_buf, image[num].pci_buf);
+
+ image[num].kern_buf = NULL;
+ image[num].pci_buf = 0;
+ image[num].size_buf = 0;
+
+#ifdef VME_DEBUG
+ } else {
+ pr_debug("UniverseII: Buffer not allocated\n");
+#endif
+ }
+}
+
+static struct vme_driver vme_user_driver = {
+ .name = driver_name,
+ .match = vme_user_match,
+ .probe = vme_user_probe,
+ .remove = vme_user_remove,
+};
+
+
+static int __init vme_user_init(void)
+{
+ int retval = 0;
+
+ pr_info("VME User Space Access Driver\n");
+
+ if (bus_num == 0) {
+ pr_err("No cards, skipping registration\n");
+ retval = -ENODEV;
+ goto err_nocard;
+ }
+
+ /* Let's start by supporting one bus, we can support more than one
+ * in future revisions if that ever becomes necessary.
+ */
+ if (bus_num > VME_USER_BUS_MAX) {
+ pr_err("Driver only able to handle %d buses\n",
+ VME_USER_BUS_MAX);
+ bus_num = VME_USER_BUS_MAX;
+ }
+
+ /*
+ * Here we just register the maximum number of devices we can and
+ * leave vme_user_match() to allow only 1 to go through to probe().
+ * This way, if we later want to allow multiple user access devices,
+ * we just change the code in vme_user_match().
+ */
+ retval = vme_register_driver(&vme_user_driver, VME_MAX_SLOTS);
+ if (retval != 0)
+ goto err_reg;
+
+ return retval;
+
+err_reg:
+err_nocard:
+ return retval;
+}
+
+static int vme_user_match(struct vme_dev *vdev)
+{
+ int i;
+
+ int cur_bus = vme_bus_num(vdev);
+ int cur_slot = vme_slot_num(vdev);
+
+ for (i = 0; i < bus_num; i++)
+ if ((cur_bus == bus[i]) && (cur_slot == vdev->num))
+ return 1;
+
+ return 0;
+}
+
+/*
+ * In this simple access driver, the old behaviour is being preserved as much
+ * as practical. We will therefore reserve the buffers and request the images
+ * here so that we don't have to do it later.
+ */
+static int vme_user_probe(struct vme_dev *vdev)
+{
+ int i, err;
+ char *name;
+
+ /* Save pointer to the bridge device */
+ if (vme_user_bridge != NULL) {
+ dev_err(&vdev->dev, "Driver can only be loaded for 1 device\n");
+ err = -EINVAL;
+ goto err_dev;
+ }
+ vme_user_bridge = vdev;
+
+ /* Initialise descriptors */
+ for (i = 0; i < VME_DEVS; i++) {
+ image[i].kern_buf = NULL;
+ image[i].pci_buf = 0;
+ mutex_init(&image[i].mutex);
+ image[i].device = NULL;
+ image[i].resource = NULL;
+ image[i].users = 0;
+ }
+
+ /* Initialise statistics counters */
+ reset_counters();
+
+ /* Assign major and minor numbers for the driver */
+ err = register_chrdev_region(MKDEV(VME_MAJOR, 0), VME_DEVS,
+ driver_name);
+ if (err) {
+ dev_warn(&vdev->dev, "Error getting Major Number %d for driver.\n",
+ VME_MAJOR);
+ goto err_region;
+ }
+
+ /* Register the driver as a char device */
+ vme_user_cdev = cdev_alloc();
+ if (!vme_user_cdev) {
+ err = -ENOMEM;
+ goto err_char;
+ }
+ vme_user_cdev->ops = &vme_user_fops;
+ vme_user_cdev->owner = THIS_MODULE;
+ err = cdev_add(vme_user_cdev, MKDEV(VME_MAJOR, 0), VME_DEVS);
+ if (err) {
+ dev_warn(&vdev->dev, "cdev_all failed\n");
+ goto err_char;
+ }
+
+ /* Request slave resources and allocate buffers (128kB wide) */
+ for (i = SLAVE_MINOR; i < (SLAVE_MAX + 1); i++) {
+ /* XXX Need to properly request attributes */
+ /* For ca91cx42 bridge there are only two slave windows
+ * supporting A16 addressing, so we request A24 supported
+ * by all windows.
+ */
+ image[i].resource = vme_slave_request(vme_user_bridge,
+ VME_A24, VME_SCT);
+ if (image[i].resource == NULL) {
+ dev_warn(&vdev->dev,
+ "Unable to allocate slave resource\n");
+ err = -ENOMEM;
+ goto err_slave;
+ }
+ image[i].size_buf = PCI_BUF_SIZE;
+ image[i].kern_buf = vme_alloc_consistent(image[i].resource,
+ image[i].size_buf, &image[i].pci_buf);
+ if (image[i].kern_buf == NULL) {
+ dev_warn(&vdev->dev,
+ "Unable to allocate memory for buffer\n");
+ image[i].pci_buf = 0;
+ vme_slave_free(image[i].resource);
+ err = -ENOMEM;
+ goto err_slave;
+ }
+ }
+
+ /*
+ * Request master resources allocate page sized buffers for small
+ * reads and writes
+ */
+ for (i = MASTER_MINOR; i < (MASTER_MAX + 1); i++) {
+ /* XXX Need to properly request attributes */
+ image[i].resource = vme_master_request(vme_user_bridge,
+ VME_A32, VME_SCT, VME_D32);
+ if (image[i].resource == NULL) {
+ dev_warn(&vdev->dev,
+ "Unable to allocate master resource\n");
+ err = -ENOMEM;
+ goto err_master;
+ }
+ image[i].size_buf = PCI_BUF_SIZE;
+ image[i].kern_buf = kmalloc(image[i].size_buf, GFP_KERNEL);
+ if (image[i].kern_buf == NULL) {
+ err = -ENOMEM;
+ vme_master_free(image[i].resource);
+ goto err_master;
+ }
+ }
+
+ /* Create sysfs entries - on udev systems this creates the dev files */
+ vme_user_sysfs_class = class_create(THIS_MODULE, driver_name);
+ if (IS_ERR(vme_user_sysfs_class)) {
+ dev_err(&vdev->dev, "Error creating vme_user class.\n");
+ err = PTR_ERR(vme_user_sysfs_class);
+ goto err_class;
+ }
+
+ /* Add sysfs Entries */
+ for (i = 0; i < VME_DEVS; i++) {
+ int num;
+
+ switch (type[i]) {
+ case MASTER_MINOR:
+ name = "bus/vme/m%d";
+ break;
+ case CONTROL_MINOR:
+ name = "bus/vme/ctl";
+ break;
+ case SLAVE_MINOR:
+ name = "bus/vme/s%d";
+ break;
+ default:
+ err = -EINVAL;
+ goto err_sysfs;
+ }
+
+ num = (type[i] == SLAVE_MINOR) ? i - (MASTER_MAX + 1) : i;
+ image[i].device = device_create(vme_user_sysfs_class, NULL,
+ MKDEV(VME_MAJOR, i), NULL, name, num);
+ if (IS_ERR(image[i].device)) {
+ dev_info(&vdev->dev, "Error creating sysfs device\n");
+ err = PTR_ERR(image[i].device);
+ goto err_sysfs;
+ }
+ }
+
+ return 0;
+
+err_sysfs:
+ while (i > 0) {
+ i--;
+ device_destroy(vme_user_sysfs_class, MKDEV(VME_MAJOR, i));
+ }
+ class_destroy(vme_user_sysfs_class);
+
+ /* Ensure counter set correcty to unalloc all master windows */
+ i = MASTER_MAX + 1;
+err_master:
+ while (i > MASTER_MINOR) {
+ i--;
+ kfree(image[i].kern_buf);
+ vme_master_free(image[i].resource);
+ }
+
+ /*
+ * Ensure counter set correcty to unalloc all slave windows and buffers
+ */
+ i = SLAVE_MAX + 1;
+err_slave:
+ while (i > SLAVE_MINOR) {
+ i--;
+ buf_unalloc(i);
+ vme_slave_free(image[i].resource);
+ }
+err_class:
+ cdev_del(vme_user_cdev);
+err_char:
+ unregister_chrdev_region(MKDEV(VME_MAJOR, 0), VME_DEVS);
+err_region:
+err_dev:
+ return err;
+}
+
+static int vme_user_remove(struct vme_dev *dev)
+{
+ int i;
+
+ /* Remove sysfs Entries */
+ for (i = 0; i < VME_DEVS; i++) {
+ mutex_destroy(&image[i].mutex);
+ device_destroy(vme_user_sysfs_class, MKDEV(VME_MAJOR, i));
+ }
+ class_destroy(vme_user_sysfs_class);
+
+ for (i = MASTER_MINOR; i < (MASTER_MAX + 1); i++) {
+ kfree(image[i].kern_buf);
+ vme_master_free(image[i].resource);
+ }
+
+ for (i = SLAVE_MINOR; i < (SLAVE_MAX + 1); i++) {
+ vme_slave_set(image[i].resource, 0, 0, 0, 0, VME_A32, 0);
+ buf_unalloc(i);
+ vme_slave_free(image[i].resource);
+ }
+
+ /* Unregister device driver */
+ cdev_del(vme_user_cdev);
+
+ /* Unregiser the major and minor device numbers */
+ unregister_chrdev_region(MKDEV(VME_MAJOR, 0), VME_DEVS);
+
+ return 0;
+}
+
+static void __exit vme_user_exit(void)
+{
+ vme_unregister_driver(&vme_user_driver);
+}
+
+
+MODULE_PARM_DESC(bus, "Enumeration of VMEbus to which the driver is connected");
+module_param_array(bus, int, &bus_num, 0);
+
+MODULE_DESCRIPTION("VME User Space Access Driver");
+MODULE_AUTHOR("Martyn Welch <martyn.welch@ge.com");
+MODULE_LICENSE("GPL");
+
+module_init(vme_user_init);
+module_exit(vme_user_exit);
diff --git a/drivers/staging/vme/devices/vme_user.h b/drivers/staging/vme/devices/vme_user.h
new file mode 100644
index 000000000..b8cc7bc78
--- /dev/null
+++ b/drivers/staging/vme/devices/vme_user.h
@@ -0,0 +1,58 @@
+#ifndef _VME_USER_H_
+#define _VME_USER_H_
+
+#define VME_USER_BUS_MAX 1
+
+/*
+ * VMEbus Master Window Configuration Structure
+ */
+struct vme_master {
+ __u32 enable; /* State of Window */
+ __u64 vme_addr; /* Starting Address on the VMEbus */
+ __u64 size; /* Window Size */
+ __u32 aspace; /* Address Space */
+ __u32 cycle; /* Cycle properties */
+ __u32 dwidth; /* Maximum Data Width */
+#if 0
+ char prefetchenable; /* Prefetch Read Enable State */
+ int prefetchsize; /* Prefetch Read Size (Cache Lines) */
+ char wrpostenable; /* Write Post State */
+#endif
+} __packed;
+
+
+/*
+ * IOCTL Commands and structures
+ */
+
+/* Magic number for use in ioctls */
+#define VME_IOC_MAGIC 0xAE
+
+
+/* VMEbus Slave Window Configuration Structure */
+struct vme_slave {
+ __u32 enable; /* State of Window */
+ __u64 vme_addr; /* Starting Address on the VMEbus */
+ __u64 size; /* Window Size */
+ __u32 aspace; /* Address Space */
+ __u32 cycle; /* Cycle properties */
+#if 0
+ char wrpostenable; /* Write Post State */
+ char rmwlock; /* Lock PCI during RMW Cycles */
+ char data64bitcapable; /* non-VMEbus capable of 64-bit Data */
+#endif
+} __packed;
+
+struct vme_irq_id {
+ __u8 level;
+ __u8 statid;
+};
+
+#define VME_GET_SLAVE _IOR(VME_IOC_MAGIC, 1, struct vme_slave)
+#define VME_SET_SLAVE _IOW(VME_IOC_MAGIC, 2, struct vme_slave)
+#define VME_GET_MASTER _IOR(VME_IOC_MAGIC, 3, struct vme_master)
+#define VME_SET_MASTER _IOW(VME_IOC_MAGIC, 4, struct vme_master)
+#define VME_IRQ_GEN _IOW(VME_IOC_MAGIC, 5, struct vme_irq_id)
+
+#endif /* _VME_USER_H_ */
+
diff --git a/drivers/staging/vt6655/Kconfig b/drivers/staging/vt6655/Kconfig
new file mode 100644
index 000000000..77cfc708c
--- /dev/null
+++ b/drivers/staging/vt6655/Kconfig
@@ -0,0 +1,6 @@
+config VT6655
+ tristate "VIA Technologies VT6655 support"
+ depends on PCI && MAC80211 && m
+ ---help---
+ This is a vendor-written driver for VIA VT6655.
+
diff --git a/drivers/staging/vt6655/Makefile b/drivers/staging/vt6655/Makefile
new file mode 100644
index 000000000..115b951bf
--- /dev/null
+++ b/drivers/staging/vt6655/Makefile
@@ -0,0 +1,18 @@
+# TODO: all of these should be removed
+ccflags-y := -DLINUX -D__KERNEL__ -D__NO_VERSION__
+ccflags-y += -DHOSTAP
+
+vt6655_stage-y += device_main.o \
+ card.o \
+ channel.o \
+ mac.o \
+ baseband.o \
+ rxtx.o \
+ dpc.o \
+ power.o \
+ srom.o \
+ mib.o \
+ key.o \
+ rf.o
+
+obj-$(CONFIG_VT6655) += vt6655_stage.o
diff --git a/drivers/staging/vt6655/TODO b/drivers/staging/vt6655/TODO
new file mode 100644
index 000000000..63607ef9c
--- /dev/null
+++ b/drivers/staging/vt6655/TODO
@@ -0,0 +1,21 @@
+TODO:
+- remove __cplusplus ifdefs -- done
+- prepare for merge with vt6656 driver:
+ - rename DEVICE_PRT() to DBG_PRT() -- done
+ - share 80211*.h includes
+ - split rf.c
+ - remove dead code
+ - abstract VT3253 chipset specific code
+- add common vt665x infrastructure
+- kill ttype.h
+- switch to use LIB80211
+- switch to use MAC80211
+- verify unsigned long usage for x86-64 arch
+- reduce .data footprint
+- use kernel coding style
+- checkpatch.pl fixes
+- sparse fixes
+- integrate with drivers/net/wireless
+
+Please send any patches to Greg Kroah-Hartman <greg@kroah.com>
+and Forest Bond <forest@alittletooquiet.net>.
diff --git a/drivers/staging/vt6655/baseband.c b/drivers/staging/vt6655/baseband.c
new file mode 100644
index 000000000..b0ea38f19
--- /dev/null
+++ b/drivers/staging/vt6655/baseband.c
@@ -0,0 +1,2397 @@
+/*
+ * Copyright (c) 1996, 2003 VIA Networking Technologies, Inc.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ *
+ * File: baseband.c
+ *
+ * Purpose: Implement functions to access baseband
+ *
+ * Author: Kyle Hsu
+ *
+ * Date: Aug.22, 2002
+ *
+ * Functions:
+ * BBuGetFrameTime - Calculate data frame transmitting time
+ * BBvCaculateParameter - Caculate PhyLength, PhyService and Phy Signal
+ * parameter for baseband Tx
+ * BBbReadEmbedded - Embedded read baseband register via MAC
+ * BBbWriteEmbedded - Embedded write baseband register via MAC
+ * BBbVT3253Init - VIA VT3253 baseband chip init code
+ *
+ * Revision History:
+ * 06-10-2003 Bryan YC Fan: Re-write codes to support VT3253 spec.
+ * 08-07-2003 Bryan YC Fan: Add MAXIM2827/2825 and RFMD2959 support.
+ * 08-26-2003 Kyle Hsu : Modify BBuGetFrameTime() and BBvCalculateParameter().
+ * cancel the setting of MAC_REG_SOFTPWRCTL on BBbVT3253Init().
+ * Add the comments.
+ * 09-01-2003 Bryan YC Fan: RF & BB tables updated.
+ * Modified BBvLoopbackOn & BBvLoopbackOff().
+ *
+ *
+ */
+
+#include "tmacro.h"
+#include "mac.h"
+#include "baseband.h"
+#include "srom.h"
+#include "rf.h"
+
+/*--------------------- Static Classes ----------------------------*/
+
+/*--------------------- Static Variables --------------------------*/
+
+/*--------------------- Static Functions --------------------------*/
+
+/*--------------------- Export Variables --------------------------*/
+
+/*--------------------- Static Definitions -------------------------*/
+
+/*--------------------- Static Classes ----------------------------*/
+
+/*--------------------- Static Variables --------------------------*/
+
+#define CB_VT3253_INIT_FOR_RFMD 446
+static unsigned char byVT3253InitTab_RFMD[CB_VT3253_INIT_FOR_RFMD][2] = {
+ {0x00, 0x30},
+ {0x01, 0x00},
+ {0x02, 0x00},
+ {0x03, 0x00},
+ {0x04, 0x00},
+ {0x05, 0x00},
+ {0x06, 0x00},
+ {0x07, 0x00},
+ {0x08, 0x70},
+ {0x09, 0x45},
+ {0x0a, 0x2a},
+ {0x0b, 0x76},
+ {0x0c, 0x00},
+ {0x0d, 0x01},
+ {0x0e, 0x80},
+ {0x0f, 0x00},
+ {0x10, 0x00},
+ {0x11, 0x00},
+ {0x12, 0x00},
+ {0x13, 0x00},
+ {0x14, 0x00},
+ {0x15, 0x00},
+ {0x16, 0x00},
+ {0x17, 0x00},
+ {0x18, 0x00},
+ {0x19, 0x00},
+ {0x1a, 0x00},
+ {0x1b, 0x9d},
+ {0x1c, 0x05},
+ {0x1d, 0x00},
+ {0x1e, 0x00},
+ {0x1f, 0x00},
+ {0x20, 0x00},
+ {0x21, 0x00},
+ {0x22, 0x00},
+ {0x23, 0x00},
+ {0x24, 0x00},
+ {0x25, 0x4a},
+ {0x26, 0x00},
+ {0x27, 0x00},
+ {0x28, 0x00},
+ {0x29, 0x00},
+ {0x2a, 0x00},
+ {0x2b, 0x00},
+ {0x2c, 0x00},
+ {0x2d, 0xa8},
+ {0x2e, 0x1a},
+ {0x2f, 0x0c},
+ {0x30, 0x26},
+ {0x31, 0x5b},
+ {0x32, 0x00},
+ {0x33, 0x00},
+ {0x34, 0x00},
+ {0x35, 0x00},
+ {0x36, 0xaa},
+ {0x37, 0xaa},
+ {0x38, 0xff},
+ {0x39, 0xff},
+ {0x3a, 0x00},
+ {0x3b, 0x00},
+ {0x3c, 0x00},
+ {0x3d, 0x0d},
+ {0x3e, 0x51},
+ {0x3f, 0x04},
+ {0x40, 0x00},
+ {0x41, 0x08},
+ {0x42, 0x00},
+ {0x43, 0x08},
+ {0x44, 0x06},
+ {0x45, 0x14},
+ {0x46, 0x05},
+ {0x47, 0x08},
+ {0x48, 0x00},
+ {0x49, 0x00},
+ {0x4a, 0x00},
+ {0x4b, 0x00},
+ {0x4c, 0x09},
+ {0x4d, 0x80},
+ {0x4e, 0x00},
+ {0x4f, 0xc5},
+ {0x50, 0x14},
+ {0x51, 0x19},
+ {0x52, 0x00},
+ {0x53, 0x00},
+ {0x54, 0x00},
+ {0x55, 0x00},
+ {0x56, 0x00},
+ {0x57, 0x00},
+ {0x58, 0x00},
+ {0x59, 0xb0},
+ {0x5a, 0x00},
+ {0x5b, 0x00},
+ {0x5c, 0x00},
+ {0x5d, 0x00},
+ {0x5e, 0x00},
+ {0x5f, 0x00},
+ {0x60, 0x44},
+ {0x61, 0x04},
+ {0x62, 0x00},
+ {0x63, 0x00},
+ {0x64, 0x00},
+ {0x65, 0x00},
+ {0x66, 0x04},
+ {0x67, 0xb7},
+ {0x68, 0x00},
+ {0x69, 0x00},
+ {0x6a, 0x00},
+ {0x6b, 0x00},
+ {0x6c, 0x00},
+ {0x6d, 0x03},
+ {0x6e, 0x01},
+ {0x6f, 0x00},
+ {0x70, 0x00},
+ {0x71, 0x00},
+ {0x72, 0x00},
+ {0x73, 0x00},
+ {0x74, 0x00},
+ {0x75, 0x00},
+ {0x76, 0x00},
+ {0x77, 0x00},
+ {0x78, 0x00},
+ {0x79, 0x00},
+ {0x7a, 0x00},
+ {0x7b, 0x00},
+ {0x7c, 0x00},
+ {0x7d, 0x00},
+ {0x7e, 0x00},
+ {0x7f, 0x00},
+ {0x80, 0x0b},
+ {0x81, 0x00},
+ {0x82, 0x3c},
+ {0x83, 0x00},
+ {0x84, 0x00},
+ {0x85, 0x00},
+ {0x86, 0x00},
+ {0x87, 0x00},
+ {0x88, 0x08},
+ {0x89, 0x00},
+ {0x8a, 0x08},
+ {0x8b, 0xa6},
+ {0x8c, 0x84},
+ {0x8d, 0x47},
+ {0x8e, 0xbb},
+ {0x8f, 0x02},
+ {0x90, 0x21},
+ {0x91, 0x0c},
+ {0x92, 0x04},
+ {0x93, 0x22},
+ {0x94, 0x00},
+ {0x95, 0x00},
+ {0x96, 0x00},
+ {0x97, 0xeb},
+ {0x98, 0x00},
+ {0x99, 0x00},
+ {0x9a, 0x00},
+ {0x9b, 0x00},
+ {0x9c, 0x00},
+ {0x9d, 0x00},
+ {0x9e, 0x00},
+ {0x9f, 0x00},
+ {0xa0, 0x00},
+ {0xa1, 0x00},
+ {0xa2, 0x00},
+ {0xa3, 0x00},
+ {0xa4, 0x00},
+ {0xa5, 0x00},
+ {0xa6, 0x10},
+ {0xa7, 0x04},
+ {0xa8, 0x10},
+ {0xa9, 0x00},
+ {0xaa, 0x8f},
+ {0xab, 0x00},
+ {0xac, 0x00},
+ {0xad, 0x00},
+ {0xae, 0x00},
+ {0xaf, 0x80},
+ {0xb0, 0x38},
+ {0xb1, 0x00},
+ {0xb2, 0x00},
+ {0xb3, 0x00},
+ {0xb4, 0xee},
+ {0xb5, 0xff},
+ {0xb6, 0x10},
+ {0xb7, 0x00},
+ {0xb8, 0x00},
+ {0xb9, 0x00},
+ {0xba, 0x00},
+ {0xbb, 0x03},
+ {0xbc, 0x00},
+ {0xbd, 0x00},
+ {0xbe, 0x00},
+ {0xbf, 0x00},
+ {0xc0, 0x10},
+ {0xc1, 0x10},
+ {0xc2, 0x18},
+ {0xc3, 0x20},
+ {0xc4, 0x10},
+ {0xc5, 0x00},
+ {0xc6, 0x22},
+ {0xc7, 0x14},
+ {0xc8, 0x0f},
+ {0xc9, 0x08},
+ {0xca, 0xa4},
+ {0xcb, 0xa7},
+ {0xcc, 0x3c},
+ {0xcd, 0x10},
+ {0xce, 0x20},
+ {0xcf, 0x00},
+ {0xd0, 0x00},
+ {0xd1, 0x10},
+ {0xd2, 0x00},
+ {0xd3, 0x00},
+ {0xd4, 0x10},
+ {0xd5, 0x33},
+ {0xd6, 0x70},
+ {0xd7, 0x01},
+ {0xd8, 0x00},
+ {0xd9, 0x00},
+ {0xda, 0x00},
+ {0xdb, 0x00},
+ {0xdc, 0x00},
+ {0xdd, 0x00},
+ {0xde, 0x00},
+ {0xdf, 0x00},
+ {0xe0, 0x00},
+ {0xe1, 0x00},
+ {0xe2, 0xcc},
+ {0xe3, 0x04},
+ {0xe4, 0x08},
+ {0xe5, 0x10},
+ {0xe6, 0x00},
+ {0xe7, 0x0e},
+ {0xe8, 0x88},
+ {0xe9, 0xd4},
+ {0xea, 0x05},
+ {0xeb, 0xf0},
+ {0xec, 0x79},
+ {0xed, 0x0f},
+ {0xee, 0x04},
+ {0xef, 0x04},
+ {0xf0, 0x00},
+ {0xf1, 0x00},
+ {0xf2, 0x00},
+ {0xf3, 0x00},
+ {0xf4, 0x00},
+ {0xf5, 0x00},
+ {0xf6, 0x00},
+ {0xf7, 0x00},
+ {0xf8, 0x00},
+ {0xf9, 0x00},
+ {0xF0, 0x00},
+ {0xF1, 0xF8},
+ {0xF0, 0x80},
+ {0xF0, 0x00},
+ {0xF1, 0xF4},
+ {0xF0, 0x81},
+ {0xF0, 0x01},
+ {0xF1, 0xF0},
+ {0xF0, 0x82},
+ {0xF0, 0x02},
+ {0xF1, 0xEC},
+ {0xF0, 0x83},
+ {0xF0, 0x03},
+ {0xF1, 0xE8},
+ {0xF0, 0x84},
+ {0xF0, 0x04},
+ {0xF1, 0xE4},
+ {0xF0, 0x85},
+ {0xF0, 0x05},
+ {0xF1, 0xE0},
+ {0xF0, 0x86},
+ {0xF0, 0x06},
+ {0xF1, 0xDC},
+ {0xF0, 0x87},
+ {0xF0, 0x07},
+ {0xF1, 0xD8},
+ {0xF0, 0x88},
+ {0xF0, 0x08},
+ {0xF1, 0xD4},
+ {0xF0, 0x89},
+ {0xF0, 0x09},
+ {0xF1, 0xD0},
+ {0xF0, 0x8A},
+ {0xF0, 0x0A},
+ {0xF1, 0xCC},
+ {0xF0, 0x8B},
+ {0xF0, 0x0B},
+ {0xF1, 0xC8},
+ {0xF0, 0x8C},
+ {0xF0, 0x0C},
+ {0xF1, 0xC4},
+ {0xF0, 0x8D},
+ {0xF0, 0x0D},
+ {0xF1, 0xC0},
+ {0xF0, 0x8E},
+ {0xF0, 0x0E},
+ {0xF1, 0xBC},
+ {0xF0, 0x8F},
+ {0xF0, 0x0F},
+ {0xF1, 0xB8},
+ {0xF0, 0x90},
+ {0xF0, 0x10},
+ {0xF1, 0xB4},
+ {0xF0, 0x91},
+ {0xF0, 0x11},
+ {0xF1, 0xB0},
+ {0xF0, 0x92},
+ {0xF0, 0x12},
+ {0xF1, 0xAC},
+ {0xF0, 0x93},
+ {0xF0, 0x13},
+ {0xF1, 0xA8},
+ {0xF0, 0x94},
+ {0xF0, 0x14},
+ {0xF1, 0xA4},
+ {0xF0, 0x95},
+ {0xF0, 0x15},
+ {0xF1, 0xA0},
+ {0xF0, 0x96},
+ {0xF0, 0x16},
+ {0xF1, 0x9C},
+ {0xF0, 0x97},
+ {0xF0, 0x17},
+ {0xF1, 0x98},
+ {0xF0, 0x98},
+ {0xF0, 0x18},
+ {0xF1, 0x94},
+ {0xF0, 0x99},
+ {0xF0, 0x19},
+ {0xF1, 0x90},
+ {0xF0, 0x9A},
+ {0xF0, 0x1A},
+ {0xF1, 0x8C},
+ {0xF0, 0x9B},
+ {0xF0, 0x1B},
+ {0xF1, 0x88},
+ {0xF0, 0x9C},
+ {0xF0, 0x1C},
+ {0xF1, 0x84},
+ {0xF0, 0x9D},
+ {0xF0, 0x1D},
+ {0xF1, 0x80},
+ {0xF0, 0x9E},
+ {0xF0, 0x1E},
+ {0xF1, 0x7C},
+ {0xF0, 0x9F},
+ {0xF0, 0x1F},
+ {0xF1, 0x78},
+ {0xF0, 0xA0},
+ {0xF0, 0x20},
+ {0xF1, 0x74},
+ {0xF0, 0xA1},
+ {0xF0, 0x21},
+ {0xF1, 0x70},
+ {0xF0, 0xA2},
+ {0xF0, 0x22},
+ {0xF1, 0x6C},
+ {0xF0, 0xA3},
+ {0xF0, 0x23},
+ {0xF1, 0x68},
+ {0xF0, 0xA4},
+ {0xF0, 0x24},
+ {0xF1, 0x64},
+ {0xF0, 0xA5},
+ {0xF0, 0x25},
+ {0xF1, 0x60},
+ {0xF0, 0xA6},
+ {0xF0, 0x26},
+ {0xF1, 0x5C},
+ {0xF0, 0xA7},
+ {0xF0, 0x27},
+ {0xF1, 0x58},
+ {0xF0, 0xA8},
+ {0xF0, 0x28},
+ {0xF1, 0x54},
+ {0xF0, 0xA9},
+ {0xF0, 0x29},
+ {0xF1, 0x50},
+ {0xF0, 0xAA},
+ {0xF0, 0x2A},
+ {0xF1, 0x4C},
+ {0xF0, 0xAB},
+ {0xF0, 0x2B},
+ {0xF1, 0x48},
+ {0xF0, 0xAC},
+ {0xF0, 0x2C},
+ {0xF1, 0x44},
+ {0xF0, 0xAD},
+ {0xF0, 0x2D},
+ {0xF1, 0x40},
+ {0xF0, 0xAE},
+ {0xF0, 0x2E},
+ {0xF1, 0x3C},
+ {0xF0, 0xAF},
+ {0xF0, 0x2F},
+ {0xF1, 0x38},
+ {0xF0, 0xB0},
+ {0xF0, 0x30},
+ {0xF1, 0x34},
+ {0xF0, 0xB1},
+ {0xF0, 0x31},
+ {0xF1, 0x30},
+ {0xF0, 0xB2},
+ {0xF0, 0x32},
+ {0xF1, 0x2C},
+ {0xF0, 0xB3},
+ {0xF0, 0x33},
+ {0xF1, 0x28},
+ {0xF0, 0xB4},
+ {0xF0, 0x34},
+ {0xF1, 0x24},
+ {0xF0, 0xB5},
+ {0xF0, 0x35},
+ {0xF1, 0x20},
+ {0xF0, 0xB6},
+ {0xF0, 0x36},
+ {0xF1, 0x1C},
+ {0xF0, 0xB7},
+ {0xF0, 0x37},
+ {0xF1, 0x18},
+ {0xF0, 0xB8},
+ {0xF0, 0x38},
+ {0xF1, 0x14},
+ {0xF0, 0xB9},
+ {0xF0, 0x39},
+ {0xF1, 0x10},
+ {0xF0, 0xBA},
+ {0xF0, 0x3A},
+ {0xF1, 0x0C},
+ {0xF0, 0xBB},
+ {0xF0, 0x3B},
+ {0xF1, 0x08},
+ {0xF0, 0x00},
+ {0xF0, 0x3C},
+ {0xF1, 0x04},
+ {0xF0, 0xBD},
+ {0xF0, 0x3D},
+ {0xF1, 0x00},
+ {0xF0, 0xBE},
+ {0xF0, 0x3E},
+ {0xF1, 0x00},
+ {0xF0, 0xBF},
+ {0xF0, 0x3F},
+ {0xF1, 0x00},
+ {0xF0, 0xC0},
+ {0xF0, 0x00},
+};
+
+#define CB_VT3253B0_INIT_FOR_RFMD 256
+static unsigned char byVT3253B0_RFMD[CB_VT3253B0_INIT_FOR_RFMD][2] = {
+ {0x00, 0x31},
+ {0x01, 0x00},
+ {0x02, 0x00},
+ {0x03, 0x00},
+ {0x04, 0x00},
+ {0x05, 0x81},
+ {0x06, 0x00},
+ {0x07, 0x00},
+ {0x08, 0x38},
+ {0x09, 0x45},
+ {0x0a, 0x2a},
+ {0x0b, 0x76},
+ {0x0c, 0x00},
+ {0x0d, 0x00},
+ {0x0e, 0x80},
+ {0x0f, 0x00},
+ {0x10, 0x00},
+ {0x11, 0x00},
+ {0x12, 0x00},
+ {0x13, 0x00},
+ {0x14, 0x00},
+ {0x15, 0x00},
+ {0x16, 0x00},
+ {0x17, 0x00},
+ {0x18, 0x00},
+ {0x19, 0x00},
+ {0x1a, 0x00},
+ {0x1b, 0x8e},
+ {0x1c, 0x06},
+ {0x1d, 0x00},
+ {0x1e, 0x00},
+ {0x1f, 0x00},
+ {0x20, 0x00},
+ {0x21, 0x00},
+ {0x22, 0x00},
+ {0x23, 0x00},
+ {0x24, 0x00},
+ {0x25, 0x4a},
+ {0x26, 0x00},
+ {0x27, 0x00},
+ {0x28, 0x00},
+ {0x29, 0x00},
+ {0x2a, 0x00},
+ {0x2b, 0x00},
+ {0x2c, 0x00},
+ {0x2d, 0x34},
+ {0x2e, 0x18},
+ {0x2f, 0x0c},
+ {0x30, 0x26},
+ {0x31, 0x5b},
+ {0x32, 0x00},
+ {0x33, 0x00},
+ {0x34, 0x00},
+ {0x35, 0x00},
+ {0x36, 0xaa},
+ {0x37, 0xaa},
+ {0x38, 0xff},
+ {0x39, 0xff},
+ {0x3a, 0xf8},
+ {0x3b, 0x00},
+ {0x3c, 0x00},
+ {0x3d, 0x09},
+ {0x3e, 0x0d},
+ {0x3f, 0x04},
+ {0x40, 0x00},
+ {0x41, 0x08},
+ {0x42, 0x00},
+ {0x43, 0x08},
+ {0x44, 0x08},
+ {0x45, 0x14},
+ {0x46, 0x05},
+ {0x47, 0x08},
+ {0x48, 0x00},
+ {0x49, 0x00},
+ {0x4a, 0x00},
+ {0x4b, 0x00},
+ {0x4c, 0x09},
+ {0x4d, 0x80},
+ {0x4e, 0x00},
+ {0x4f, 0xc5},
+ {0x50, 0x14},
+ {0x51, 0x19},
+ {0x52, 0x00},
+ {0x53, 0x00},
+ {0x54, 0x00},
+ {0x55, 0x00},
+ {0x56, 0x00},
+ {0x57, 0x00},
+ {0x58, 0x00},
+ {0x59, 0xb0},
+ {0x5a, 0x00},
+ {0x5b, 0x00},
+ {0x5c, 0x00},
+ {0x5d, 0x00},
+ {0x5e, 0x00},
+ {0x5f, 0x00},
+ {0x60, 0x39},
+ {0x61, 0x83},
+ {0x62, 0x00},
+ {0x63, 0x00},
+ {0x64, 0x00},
+ {0x65, 0x00},
+ {0x66, 0xc0},
+ {0x67, 0x49},
+ {0x68, 0x00},
+ {0x69, 0x00},
+ {0x6a, 0x00},
+ {0x6b, 0x00},
+ {0x6c, 0x00},
+ {0x6d, 0x03},
+ {0x6e, 0x01},
+ {0x6f, 0x00},
+ {0x70, 0x00},
+ {0x71, 0x00},
+ {0x72, 0x00},
+ {0x73, 0x00},
+ {0x74, 0x00},
+ {0x75, 0x00},
+ {0x76, 0x00},
+ {0x77, 0x00},
+ {0x78, 0x00},
+ {0x79, 0x00},
+ {0x7a, 0x00},
+ {0x7b, 0x00},
+ {0x7c, 0x00},
+ {0x7d, 0x00},
+ {0x7e, 0x00},
+ {0x7f, 0x00},
+ {0x80, 0x89},
+ {0x81, 0x00},
+ {0x82, 0x0e},
+ {0x83, 0x00},
+ {0x84, 0x00},
+ {0x85, 0x00},
+ {0x86, 0x00},
+ {0x87, 0x00},
+ {0x88, 0x08},
+ {0x89, 0x00},
+ {0x8a, 0x0e},
+ {0x8b, 0xa7},
+ {0x8c, 0x88},
+ {0x8d, 0x47},
+ {0x8e, 0xaa},
+ {0x8f, 0x02},
+ {0x90, 0x23},
+ {0x91, 0x0c},
+ {0x92, 0x06},
+ {0x93, 0x08},
+ {0x94, 0x00},
+ {0x95, 0x00},
+ {0x96, 0x00},
+ {0x97, 0xeb},
+ {0x98, 0x00},
+ {0x99, 0x00},
+ {0x9a, 0x00},
+ {0x9b, 0x00},
+ {0x9c, 0x00},
+ {0x9d, 0x00},
+ {0x9e, 0x00},
+ {0x9f, 0x00},
+ {0xa0, 0x00},
+ {0xa1, 0x00},
+ {0xa2, 0x00},
+ {0xa3, 0xcd},
+ {0xa4, 0x07},
+ {0xa5, 0x33},
+ {0xa6, 0x18},
+ {0xa7, 0x00},
+ {0xa8, 0x18},
+ {0xa9, 0x00},
+ {0xaa, 0x28},
+ {0xab, 0x00},
+ {0xac, 0x00},
+ {0xad, 0x00},
+ {0xae, 0x00},
+ {0xaf, 0x18},
+ {0xb0, 0x38},
+ {0xb1, 0x30},
+ {0xb2, 0x00},
+ {0xb3, 0x00},
+ {0xb4, 0x00},
+ {0xb5, 0x00},
+ {0xb6, 0x84},
+ {0xb7, 0xfd},
+ {0xb8, 0x00},
+ {0xb9, 0x00},
+ {0xba, 0x00},
+ {0xbb, 0x03},
+ {0xbc, 0x00},
+ {0xbd, 0x00},
+ {0xbe, 0x00},
+ {0xbf, 0x00},
+ {0xc0, 0x10},
+ {0xc1, 0x20},
+ {0xc2, 0x18},
+ {0xc3, 0x20},
+ {0xc4, 0x10},
+ {0xc5, 0x2c},
+ {0xc6, 0x1e},
+ {0xc7, 0x10},
+ {0xc8, 0x12},
+ {0xc9, 0x01},
+ {0xca, 0x6f},
+ {0xcb, 0xa7},
+ {0xcc, 0x3c},
+ {0xcd, 0x10},
+ {0xce, 0x00},
+ {0xcf, 0x22},
+ {0xd0, 0x00},
+ {0xd1, 0x10},
+ {0xd2, 0x00},
+ {0xd3, 0x00},
+ {0xd4, 0x10},
+ {0xd5, 0x33},
+ {0xd6, 0x80},
+ {0xd7, 0x21},
+ {0xd8, 0x00},
+ {0xd9, 0x00},
+ {0xda, 0x00},
+ {0xdb, 0x00},
+ {0xdc, 0x00},
+ {0xdd, 0x00},
+ {0xde, 0x00},
+ {0xdf, 0x00},
+ {0xe0, 0x00},
+ {0xe1, 0xB3},
+ {0xe2, 0x00},
+ {0xe3, 0x00},
+ {0xe4, 0x00},
+ {0xe5, 0x10},
+ {0xe6, 0x00},
+ {0xe7, 0x18},
+ {0xe8, 0x08},
+ {0xe9, 0xd4},
+ {0xea, 0x00},
+ {0xeb, 0xff},
+ {0xec, 0x79},
+ {0xed, 0x10},
+ {0xee, 0x30},
+ {0xef, 0x02},
+ {0xf0, 0x00},
+ {0xf1, 0x09},
+ {0xf2, 0x00},
+ {0xf3, 0x00},
+ {0xf4, 0x00},
+ {0xf5, 0x00},
+ {0xf6, 0x00},
+ {0xf7, 0x00},
+ {0xf8, 0x00},
+ {0xf9, 0x00},
+ {0xfa, 0x00},
+ {0xfb, 0x00},
+ {0xfc, 0x00},
+ {0xfd, 0x00},
+ {0xfe, 0x00},
+ {0xff, 0x00},
+};
+
+#define CB_VT3253B0_AGC_FOR_RFMD2959 195
+/* For RFMD2959 */
+static unsigned char byVT3253B0_AGC4_RFMD2959[CB_VT3253B0_AGC_FOR_RFMD2959][2] = {
+ {0xF0, 0x00},
+ {0xF1, 0x3E},
+ {0xF0, 0x80},
+ {0xF0, 0x00},
+ {0xF1, 0x3E},
+ {0xF0, 0x81},
+ {0xF0, 0x01},
+ {0xF1, 0x3E},
+ {0xF0, 0x82},
+ {0xF0, 0x02},
+ {0xF1, 0x3E},
+ {0xF0, 0x83},
+ {0xF0, 0x03},
+ {0xF1, 0x3B},
+ {0xF0, 0x84},
+ {0xF0, 0x04},
+ {0xF1, 0x39},
+ {0xF0, 0x85},
+ {0xF0, 0x05},
+ {0xF1, 0x38},
+ {0xF0, 0x86},
+ {0xF0, 0x06},
+ {0xF1, 0x37},
+ {0xF0, 0x87},
+ {0xF0, 0x07},
+ {0xF1, 0x36},
+ {0xF0, 0x88},
+ {0xF0, 0x08},
+ {0xF1, 0x35},
+ {0xF0, 0x89},
+ {0xF0, 0x09},
+ {0xF1, 0x35},
+ {0xF0, 0x8A},
+ {0xF0, 0x0A},
+ {0xF1, 0x34},
+ {0xF0, 0x8B},
+ {0xF0, 0x0B},
+ {0xF1, 0x34},
+ {0xF0, 0x8C},
+ {0xF0, 0x0C},
+ {0xF1, 0x33},
+ {0xF0, 0x8D},
+ {0xF0, 0x0D},
+ {0xF1, 0x32},
+ {0xF0, 0x8E},
+ {0xF0, 0x0E},
+ {0xF1, 0x31},
+ {0xF0, 0x8F},
+ {0xF0, 0x0F},
+ {0xF1, 0x30},
+ {0xF0, 0x90},
+ {0xF0, 0x10},
+ {0xF1, 0x2F},
+ {0xF0, 0x91},
+ {0xF0, 0x11},
+ {0xF1, 0x2F},
+ {0xF0, 0x92},
+ {0xF0, 0x12},
+ {0xF1, 0x2E},
+ {0xF0, 0x93},
+ {0xF0, 0x13},
+ {0xF1, 0x2D},
+ {0xF0, 0x94},
+ {0xF0, 0x14},
+ {0xF1, 0x2C},
+ {0xF0, 0x95},
+ {0xF0, 0x15},
+ {0xF1, 0x2B},
+ {0xF0, 0x96},
+ {0xF0, 0x16},
+ {0xF1, 0x2B},
+ {0xF0, 0x97},
+ {0xF0, 0x17},
+ {0xF1, 0x2A},
+ {0xF0, 0x98},
+ {0xF0, 0x18},
+ {0xF1, 0x29},
+ {0xF0, 0x99},
+ {0xF0, 0x19},
+ {0xF1, 0x28},
+ {0xF0, 0x9A},
+ {0xF0, 0x1A},
+ {0xF1, 0x27},
+ {0xF0, 0x9B},
+ {0xF0, 0x1B},
+ {0xF1, 0x26},
+ {0xF0, 0x9C},
+ {0xF0, 0x1C},
+ {0xF1, 0x25},
+ {0xF0, 0x9D},
+ {0xF0, 0x1D},
+ {0xF1, 0x24},
+ {0xF0, 0x9E},
+ {0xF0, 0x1E},
+ {0xF1, 0x24},
+ {0xF0, 0x9F},
+ {0xF0, 0x1F},
+ {0xF1, 0x23},
+ {0xF0, 0xA0},
+ {0xF0, 0x20},
+ {0xF1, 0x22},
+ {0xF0, 0xA1},
+ {0xF0, 0x21},
+ {0xF1, 0x21},
+ {0xF0, 0xA2},
+ {0xF0, 0x22},
+ {0xF1, 0x20},
+ {0xF0, 0xA3},
+ {0xF0, 0x23},
+ {0xF1, 0x20},
+ {0xF0, 0xA4},
+ {0xF0, 0x24},
+ {0xF1, 0x1F},
+ {0xF0, 0xA5},
+ {0xF0, 0x25},
+ {0xF1, 0x1E},
+ {0xF0, 0xA6},
+ {0xF0, 0x26},
+ {0xF1, 0x1D},
+ {0xF0, 0xA7},
+ {0xF0, 0x27},
+ {0xF1, 0x1C},
+ {0xF0, 0xA8},
+ {0xF0, 0x28},
+ {0xF1, 0x1B},
+ {0xF0, 0xA9},
+ {0xF0, 0x29},
+ {0xF1, 0x1B},
+ {0xF0, 0xAA},
+ {0xF0, 0x2A},
+ {0xF1, 0x1A},
+ {0xF0, 0xAB},
+ {0xF0, 0x2B},
+ {0xF1, 0x1A},
+ {0xF0, 0xAC},
+ {0xF0, 0x2C},
+ {0xF1, 0x19},
+ {0xF0, 0xAD},
+ {0xF0, 0x2D},
+ {0xF1, 0x18},
+ {0xF0, 0xAE},
+ {0xF0, 0x2E},
+ {0xF1, 0x17},
+ {0xF0, 0xAF},
+ {0xF0, 0x2F},
+ {0xF1, 0x16},
+ {0xF0, 0xB0},
+ {0xF0, 0x30},
+ {0xF1, 0x15},
+ {0xF0, 0xB1},
+ {0xF0, 0x31},
+ {0xF1, 0x15},
+ {0xF0, 0xB2},
+ {0xF0, 0x32},
+ {0xF1, 0x15},
+ {0xF0, 0xB3},
+ {0xF0, 0x33},
+ {0xF1, 0x14},
+ {0xF0, 0xB4},
+ {0xF0, 0x34},
+ {0xF1, 0x13},
+ {0xF0, 0xB5},
+ {0xF0, 0x35},
+ {0xF1, 0x12},
+ {0xF0, 0xB6},
+ {0xF0, 0x36},
+ {0xF1, 0x11},
+ {0xF0, 0xB7},
+ {0xF0, 0x37},
+ {0xF1, 0x10},
+ {0xF0, 0xB8},
+ {0xF0, 0x38},
+ {0xF1, 0x0F},
+ {0xF0, 0xB9},
+ {0xF0, 0x39},
+ {0xF1, 0x0E},
+ {0xF0, 0xBA},
+ {0xF0, 0x3A},
+ {0xF1, 0x0D},
+ {0xF0, 0xBB},
+ {0xF0, 0x3B},
+ {0xF1, 0x0C},
+ {0xF0, 0xBC},
+ {0xF0, 0x3C},
+ {0xF1, 0x0B},
+ {0xF0, 0xBD},
+ {0xF0, 0x3D},
+ {0xF1, 0x0B},
+ {0xF0, 0xBE},
+ {0xF0, 0x3E},
+ {0xF1, 0x0A},
+ {0xF0, 0xBF},
+ {0xF0, 0x3F},
+ {0xF1, 0x09},
+ {0xF0, 0x00},
+};
+
+#define CB_VT3253B0_INIT_FOR_AIROHA2230 256
+/* For AIROHA */
+static unsigned char byVT3253B0_AIROHA2230[CB_VT3253B0_INIT_FOR_AIROHA2230][2] = {
+ {0x00, 0x31},
+ {0x01, 0x00},
+ {0x02, 0x00},
+ {0x03, 0x00},
+ {0x04, 0x00},
+ {0x05, 0x80},
+ {0x06, 0x00},
+ {0x07, 0x00},
+ {0x08, 0x70},
+ {0x09, 0x41},
+ {0x0a, 0x2A},
+ {0x0b, 0x76},
+ {0x0c, 0x00},
+ {0x0d, 0x00},
+ {0x0e, 0x80},
+ {0x0f, 0x00},
+ {0x10, 0x00},
+ {0x11, 0x00},
+ {0x12, 0x00},
+ {0x13, 0x00},
+ {0x14, 0x00},
+ {0x15, 0x00},
+ {0x16, 0x00},
+ {0x17, 0x00},
+ {0x18, 0x00},
+ {0x19, 0x00},
+ {0x1a, 0x00},
+ {0x1b, 0x8f},
+ {0x1c, 0x09},
+ {0x1d, 0x00},
+ {0x1e, 0x00},
+ {0x1f, 0x00},
+ {0x20, 0x00},
+ {0x21, 0x00},
+ {0x22, 0x00},
+ {0x23, 0x00},
+ {0x24, 0x00},
+ {0x25, 0x4a},
+ {0x26, 0x00},
+ {0x27, 0x00},
+ {0x28, 0x00},
+ {0x29, 0x00},
+ {0x2a, 0x00},
+ {0x2b, 0x00},
+ {0x2c, 0x00},
+ {0x2d, 0x4a},
+ {0x2e, 0x00},
+ {0x2f, 0x0a},
+ {0x30, 0x26},
+ {0x31, 0x5b},
+ {0x32, 0x00},
+ {0x33, 0x00},
+ {0x34, 0x00},
+ {0x35, 0x00},
+ {0x36, 0xaa},
+ {0x37, 0xaa},
+ {0x38, 0xff},
+ {0x39, 0xff},
+ {0x3a, 0x79},
+ {0x3b, 0x00},
+ {0x3c, 0x00},
+ {0x3d, 0x0b},
+ {0x3e, 0x48},
+ {0x3f, 0x04},
+ {0x40, 0x00},
+ {0x41, 0x08},
+ {0x42, 0x00},
+ {0x43, 0x08},
+ {0x44, 0x08},
+ {0x45, 0x14},
+ {0x46, 0x05},
+ {0x47, 0x09},
+ {0x48, 0x00},
+ {0x49, 0x00},
+ {0x4a, 0x00},
+ {0x4b, 0x00},
+ {0x4c, 0x09},
+ {0x4d, 0x73},
+ {0x4e, 0x00},
+ {0x4f, 0xc5},
+ {0x50, 0x15},
+ {0x51, 0x19},
+ {0x52, 0x00},
+ {0x53, 0x00},
+ {0x54, 0x00},
+ {0x55, 0x00},
+ {0x56, 0x00},
+ {0x57, 0x00},
+ {0x58, 0x00},
+ {0x59, 0xb0},
+ {0x5a, 0x00},
+ {0x5b, 0x00},
+ {0x5c, 0x00},
+ {0x5d, 0x00},
+ {0x5e, 0x00},
+ {0x5f, 0x00},
+ {0x60, 0xe4},
+ {0x61, 0x80},
+ {0x62, 0x00},
+ {0x63, 0x00},
+ {0x64, 0x00},
+ {0x65, 0x00},
+ {0x66, 0x98},
+ {0x67, 0x0a},
+ {0x68, 0x00},
+ {0x69, 0x00},
+ {0x6a, 0x00},
+ {0x6b, 0x00},
+ {0x6c, 0x00}, /* RobertYu:20050125, request by JJSue */
+ {0x6d, 0x03},
+ {0x6e, 0x01},
+ {0x6f, 0x00},
+ {0x70, 0x00},
+ {0x71, 0x00},
+ {0x72, 0x00},
+ {0x73, 0x00},
+ {0x74, 0x00},
+ {0x75, 0x00},
+ {0x76, 0x00},
+ {0x77, 0x00},
+ {0x78, 0x00},
+ {0x79, 0x00},
+ {0x7a, 0x00},
+ {0x7b, 0x00},
+ {0x7c, 0x00},
+ {0x7d, 0x00},
+ {0x7e, 0x00},
+ {0x7f, 0x00},
+ {0x80, 0x8c},
+ {0x81, 0x01},
+ {0x82, 0x09},
+ {0x83, 0x00},
+ {0x84, 0x00},
+ {0x85, 0x00},
+ {0x86, 0x00},
+ {0x87, 0x00},
+ {0x88, 0x08},
+ {0x89, 0x00},
+ {0x8a, 0x0f},
+ {0x8b, 0xb7},
+ {0x8c, 0x88},
+ {0x8d, 0x47},
+ {0x8e, 0xaa},
+ {0x8f, 0x02},
+ {0x90, 0x22},
+ {0x91, 0x00},
+ {0x92, 0x00},
+ {0x93, 0x00},
+ {0x94, 0x00},
+ {0x95, 0x00},
+ {0x96, 0x00},
+ {0x97, 0xeb},
+ {0x98, 0x00},
+ {0x99, 0x00},
+ {0x9a, 0x00},
+ {0x9b, 0x00},
+ {0x9c, 0x00},
+ {0x9d, 0x00},
+ {0x9e, 0x00},
+ {0x9f, 0x01},
+ {0xa0, 0x00},
+ {0xa1, 0x00},
+ {0xa2, 0x00},
+ {0xa3, 0x00},
+ {0xa4, 0x00},
+ {0xa5, 0x00},
+ {0xa6, 0x10},
+ {0xa7, 0x00},
+ {0xa8, 0x18},
+ {0xa9, 0x00},
+ {0xaa, 0x00},
+ {0xab, 0x00},
+ {0xac, 0x00},
+ {0xad, 0x00},
+ {0xae, 0x00},
+ {0xaf, 0x18},
+ {0xb0, 0x38},
+ {0xb1, 0x30},
+ {0xb2, 0x00},
+ {0xb3, 0x00},
+ {0xb4, 0xff},
+ {0xb5, 0x0f},
+ {0xb6, 0xe4},
+ {0xb7, 0xe2},
+ {0xb8, 0x00},
+ {0xb9, 0x00},
+ {0xba, 0x00},
+ {0xbb, 0x03},
+ {0xbc, 0x01},
+ {0xbd, 0x00},
+ {0xbe, 0x00},
+ {0xbf, 0x00},
+ {0xc0, 0x18},
+ {0xc1, 0x20},
+ {0xc2, 0x07},
+ {0xc3, 0x18},
+ {0xc4, 0xff},
+ {0xc5, 0x2c},
+ {0xc6, 0x0c},
+ {0xc7, 0x0a},
+ {0xc8, 0x0e},
+ {0xc9, 0x01},
+ {0xca, 0x68},
+ {0xcb, 0xa7},
+ {0xcc, 0x3c},
+ {0xcd, 0x10},
+ {0xce, 0x00},
+ {0xcf, 0x25},
+ {0xd0, 0x40},
+ {0xd1, 0x12},
+ {0xd2, 0x00},
+ {0xd3, 0x00},
+ {0xd4, 0x10},
+ {0xd5, 0x28},
+ {0xd6, 0x80},
+ {0xd7, 0x2A},
+ {0xd8, 0x00},
+ {0xd9, 0x00},
+ {0xda, 0x00},
+ {0xdb, 0x00},
+ {0xdc, 0x00},
+ {0xdd, 0x00},
+ {0xde, 0x00},
+ {0xdf, 0x00},
+ {0xe0, 0x00},
+ {0xe1, 0xB3},
+ {0xe2, 0x00},
+ {0xe3, 0x00},
+ {0xe4, 0x00},
+ {0xe5, 0x10},
+ {0xe6, 0x00},
+ {0xe7, 0x1C},
+ {0xe8, 0x00},
+ {0xe9, 0xf4},
+ {0xea, 0x00},
+ {0xeb, 0xff},
+ {0xec, 0x79},
+ {0xed, 0x20},
+ {0xee, 0x30},
+ {0xef, 0x01},
+ {0xf0, 0x00},
+ {0xf1, 0x3e},
+ {0xf2, 0x00},
+ {0xf3, 0x00},
+ {0xf4, 0x00},
+ {0xf5, 0x00},
+ {0xf6, 0x00},
+ {0xf7, 0x00},
+ {0xf8, 0x00},
+ {0xf9, 0x00},
+ {0xfa, 0x00},
+ {0xfb, 0x00},
+ {0xfc, 0x00},
+ {0xfd, 0x00},
+ {0xfe, 0x00},
+ {0xff, 0x00},
+};
+
+#define CB_VT3253B0_INIT_FOR_UW2451 256
+/* For UW2451 */
+static unsigned char byVT3253B0_UW2451[CB_VT3253B0_INIT_FOR_UW2451][2] = {
+ {0x00, 0x31},
+ {0x01, 0x00},
+ {0x02, 0x00},
+ {0x03, 0x00},
+ {0x04, 0x00},
+ {0x05, 0x81},
+ {0x06, 0x00},
+ {0x07, 0x00},
+ {0x08, 0x38},
+ {0x09, 0x45},
+ {0x0a, 0x28},
+ {0x0b, 0x76},
+ {0x0c, 0x00},
+ {0x0d, 0x00},
+ {0x0e, 0x80},
+ {0x0f, 0x00},
+ {0x10, 0x00},
+ {0x11, 0x00},
+ {0x12, 0x00},
+ {0x13, 0x00},
+ {0x14, 0x00},
+ {0x15, 0x00},
+ {0x16, 0x00},
+ {0x17, 0x00},
+ {0x18, 0x00},
+ {0x19, 0x00},
+ {0x1a, 0x00},
+ {0x1b, 0x8f},
+ {0x1c, 0x0f},
+ {0x1d, 0x00},
+ {0x1e, 0x00},
+ {0x1f, 0x00},
+ {0x20, 0x00},
+ {0x21, 0x00},
+ {0x22, 0x00},
+ {0x23, 0x00},
+ {0x24, 0x00},
+ {0x25, 0x4a},
+ {0x26, 0x00},
+ {0x27, 0x00},
+ {0x28, 0x00},
+ {0x29, 0x00},
+ {0x2a, 0x00},
+ {0x2b, 0x00},
+ {0x2c, 0x00},
+ {0x2d, 0x18},
+ {0x2e, 0x00},
+ {0x2f, 0x0a},
+ {0x30, 0x26},
+ {0x31, 0x5b},
+ {0x32, 0x00},
+ {0x33, 0x00},
+ {0x34, 0x00},
+ {0x35, 0x00},
+ {0x36, 0xaa},
+ {0x37, 0xaa},
+ {0x38, 0xff},
+ {0x39, 0xff},
+ {0x3a, 0x00},
+ {0x3b, 0x00},
+ {0x3c, 0x00},
+ {0x3d, 0x03},
+ {0x3e, 0x1d},
+ {0x3f, 0x04},
+ {0x40, 0x00},
+ {0x41, 0x08},
+ {0x42, 0x00},
+ {0x43, 0x08},
+ {0x44, 0x08},
+ {0x45, 0x14},
+ {0x46, 0x05},
+ {0x47, 0x09},
+ {0x48, 0x00},
+ {0x49, 0x00},
+ {0x4a, 0x00},
+ {0x4b, 0x00},
+ {0x4c, 0x09},
+ {0x4d, 0x90},
+ {0x4e, 0x00},
+ {0x4f, 0xc5},
+ {0x50, 0x15},
+ {0x51, 0x19},
+ {0x52, 0x00},
+ {0x53, 0x00},
+ {0x54, 0x00},
+ {0x55, 0x00},
+ {0x56, 0x00},
+ {0x57, 0x00},
+ {0x58, 0x00},
+ {0x59, 0xb0},
+ {0x5a, 0x00},
+ {0x5b, 0x00},
+ {0x5c, 0x00},
+ {0x5d, 0x00},
+ {0x5e, 0x00},
+ {0x5f, 0x00},
+ {0x60, 0xb3},
+ {0x61, 0x81},
+ {0x62, 0x00},
+ {0x63, 0x00},
+ {0x64, 0x00},
+ {0x65, 0x00},
+ {0x66, 0x57},
+ {0x67, 0x6c},
+ {0x68, 0x00},
+ {0x69, 0x00},
+ {0x6a, 0x00},
+ {0x6b, 0x00},
+ {0x6c, 0x00}, /* RobertYu:20050125, request by JJSue */
+ {0x6d, 0x03},
+ {0x6e, 0x01},
+ {0x6f, 0x00},
+ {0x70, 0x00},
+ {0x71, 0x00},
+ {0x72, 0x00},
+ {0x73, 0x00},
+ {0x74, 0x00},
+ {0x75, 0x00},
+ {0x76, 0x00},
+ {0x77, 0x00},
+ {0x78, 0x00},
+ {0x79, 0x00},
+ {0x7a, 0x00},
+ {0x7b, 0x00},
+ {0x7c, 0x00},
+ {0x7d, 0x00},
+ {0x7e, 0x00},
+ {0x7f, 0x00},
+ {0x80, 0x8c},
+ {0x81, 0x00},
+ {0x82, 0x0e},
+ {0x83, 0x00},
+ {0x84, 0x00},
+ {0x85, 0x00},
+ {0x86, 0x00},
+ {0x87, 0x00},
+ {0x88, 0x08},
+ {0x89, 0x00},
+ {0x8a, 0x0e},
+ {0x8b, 0xa7},
+ {0x8c, 0x88},
+ {0x8d, 0x47},
+ {0x8e, 0xaa},
+ {0x8f, 0x02},
+ {0x90, 0x00},
+ {0x91, 0x00},
+ {0x92, 0x00},
+ {0x93, 0x00},
+ {0x94, 0x00},
+ {0x95, 0x00},
+ {0x96, 0x00},
+ {0x97, 0xe3},
+ {0x98, 0x00},
+ {0x99, 0x00},
+ {0x9a, 0x00},
+ {0x9b, 0x00},
+ {0x9c, 0x00},
+ {0x9d, 0x00},
+ {0x9e, 0x00},
+ {0x9f, 0x00},
+ {0xa0, 0x00},
+ {0xa1, 0x00},
+ {0xa2, 0x00},
+ {0xa3, 0x00},
+ {0xa4, 0x00},
+ {0xa5, 0x00},
+ {0xa6, 0x10},
+ {0xa7, 0x00},
+ {0xa8, 0x18},
+ {0xa9, 0x00},
+ {0xaa, 0x00},
+ {0xab, 0x00},
+ {0xac, 0x00},
+ {0xad, 0x00},
+ {0xae, 0x00},
+ {0xaf, 0x18},
+ {0xb0, 0x18},
+ {0xb1, 0x30},
+ {0xb2, 0x00},
+ {0xb3, 0x00},
+ {0xb4, 0x00},
+ {0xb5, 0x00},
+ {0xb6, 0x00},
+ {0xb7, 0x00},
+ {0xb8, 0x00},
+ {0xb9, 0x00},
+ {0xba, 0x00},
+ {0xbb, 0x03},
+ {0xbc, 0x01},
+ {0xbd, 0x00},
+ {0xbe, 0x00},
+ {0xbf, 0x00},
+ {0xc0, 0x10},
+ {0xc1, 0x20},
+ {0xc2, 0x00},
+ {0xc3, 0x20},
+ {0xc4, 0x00},
+ {0xc5, 0x2c},
+ {0xc6, 0x1c},
+ {0xc7, 0x10},
+ {0xc8, 0x10},
+ {0xc9, 0x01},
+ {0xca, 0x68},
+ {0xcb, 0xa7},
+ {0xcc, 0x3c},
+ {0xcd, 0x09},
+ {0xce, 0x00},
+ {0xcf, 0x20},
+ {0xd0, 0x40},
+ {0xd1, 0x10},
+ {0xd2, 0x00},
+ {0xd3, 0x00},
+ {0xd4, 0x20},
+ {0xd5, 0x28},
+ {0xd6, 0xa0},
+ {0xd7, 0x2a},
+ {0xd8, 0x00},
+ {0xd9, 0x00},
+ {0xda, 0x00},
+ {0xdb, 0x00},
+ {0xdc, 0x00},
+ {0xdd, 0x00},
+ {0xde, 0x00},
+ {0xdf, 0x00},
+ {0xe0, 0x00},
+ {0xe1, 0xd3},
+ {0xe2, 0xc0},
+ {0xe3, 0x00},
+ {0xe4, 0x00},
+ {0xe5, 0x10},
+ {0xe6, 0x00},
+ {0xe7, 0x12},
+ {0xe8, 0x12},
+ {0xe9, 0x34},
+ {0xea, 0x00},
+ {0xeb, 0xff},
+ {0xec, 0x79},
+ {0xed, 0x20},
+ {0xee, 0x30},
+ {0xef, 0x01},
+ {0xf0, 0x00},
+ {0xf1, 0x3e},
+ {0xf2, 0x00},
+ {0xf3, 0x00},
+ {0xf4, 0x00},
+ {0xf5, 0x00},
+ {0xf6, 0x00},
+ {0xf7, 0x00},
+ {0xf8, 0x00},
+ {0xf9, 0x00},
+ {0xfa, 0x00},
+ {0xfb, 0x00},
+ {0xfc, 0x00},
+ {0xfd, 0x00},
+ {0xfe, 0x00},
+ {0xff, 0x00},
+};
+
+#define CB_VT3253B0_AGC 193
+/* For AIROHA */
+static unsigned char byVT3253B0_AGC[CB_VT3253B0_AGC][2] = {
+ {0xF0, 0x00},
+ {0xF1, 0x00},
+ {0xF0, 0x80},
+ {0xF0, 0x01},
+ {0xF1, 0x00},
+ {0xF0, 0x81},
+ {0xF0, 0x02},
+ {0xF1, 0x02},
+ {0xF0, 0x82},
+ {0xF0, 0x03},
+ {0xF1, 0x04},
+ {0xF0, 0x83},
+ {0xF0, 0x03},
+ {0xF1, 0x04},
+ {0xF0, 0x84},
+ {0xF0, 0x04},
+ {0xF1, 0x06},
+ {0xF0, 0x85},
+ {0xF0, 0x05},
+ {0xF1, 0x06},
+ {0xF0, 0x86},
+ {0xF0, 0x06},
+ {0xF1, 0x06},
+ {0xF0, 0x87},
+ {0xF0, 0x07},
+ {0xF1, 0x08},
+ {0xF0, 0x88},
+ {0xF0, 0x08},
+ {0xF1, 0x08},
+ {0xF0, 0x89},
+ {0xF0, 0x09},
+ {0xF1, 0x0A},
+ {0xF0, 0x8A},
+ {0xF0, 0x0A},
+ {0xF1, 0x0A},
+ {0xF0, 0x8B},
+ {0xF0, 0x0B},
+ {0xF1, 0x0C},
+ {0xF0, 0x8C},
+ {0xF0, 0x0C},
+ {0xF1, 0x0C},
+ {0xF0, 0x8D},
+ {0xF0, 0x0D},
+ {0xF1, 0x0E},
+ {0xF0, 0x8E},
+ {0xF0, 0x0E},
+ {0xF1, 0x0E},
+ {0xF0, 0x8F},
+ {0xF0, 0x0F},
+ {0xF1, 0x10},
+ {0xF0, 0x90},
+ {0xF0, 0x10},
+ {0xF1, 0x10},
+ {0xF0, 0x91},
+ {0xF0, 0x11},
+ {0xF1, 0x12},
+ {0xF0, 0x92},
+ {0xF0, 0x12},
+ {0xF1, 0x12},
+ {0xF0, 0x93},
+ {0xF0, 0x13},
+ {0xF1, 0x14},
+ {0xF0, 0x94},
+ {0xF0, 0x14},
+ {0xF1, 0x14},
+ {0xF0, 0x95},
+ {0xF0, 0x15},
+ {0xF1, 0x16},
+ {0xF0, 0x96},
+ {0xF0, 0x16},
+ {0xF1, 0x16},
+ {0xF0, 0x97},
+ {0xF0, 0x17},
+ {0xF1, 0x18},
+ {0xF0, 0x98},
+ {0xF0, 0x18},
+ {0xF1, 0x18},
+ {0xF0, 0x99},
+ {0xF0, 0x19},
+ {0xF1, 0x1A},
+ {0xF0, 0x9A},
+ {0xF0, 0x1A},
+ {0xF1, 0x1A},
+ {0xF0, 0x9B},
+ {0xF0, 0x1B},
+ {0xF1, 0x1C},
+ {0xF0, 0x9C},
+ {0xF0, 0x1C},
+ {0xF1, 0x1C},
+ {0xF0, 0x9D},
+ {0xF0, 0x1D},
+ {0xF1, 0x1E},
+ {0xF0, 0x9E},
+ {0xF0, 0x1E},
+ {0xF1, 0x1E},
+ {0xF0, 0x9F},
+ {0xF0, 0x1F},
+ {0xF1, 0x20},
+ {0xF0, 0xA0},
+ {0xF0, 0x20},
+ {0xF1, 0x20},
+ {0xF0, 0xA1},
+ {0xF0, 0x21},
+ {0xF1, 0x22},
+ {0xF0, 0xA2},
+ {0xF0, 0x22},
+ {0xF1, 0x22},
+ {0xF0, 0xA3},
+ {0xF0, 0x23},
+ {0xF1, 0x24},
+ {0xF0, 0xA4},
+ {0xF0, 0x24},
+ {0xF1, 0x24},
+ {0xF0, 0xA5},
+ {0xF0, 0x25},
+ {0xF1, 0x26},
+ {0xF0, 0xA6},
+ {0xF0, 0x26},
+ {0xF1, 0x26},
+ {0xF0, 0xA7},
+ {0xF0, 0x27},
+ {0xF1, 0x28},
+ {0xF0, 0xA8},
+ {0xF0, 0x28},
+ {0xF1, 0x28},
+ {0xF0, 0xA9},
+ {0xF0, 0x29},
+ {0xF1, 0x2A},
+ {0xF0, 0xAA},
+ {0xF0, 0x2A},
+ {0xF1, 0x2A},
+ {0xF0, 0xAB},
+ {0xF0, 0x2B},
+ {0xF1, 0x2C},
+ {0xF0, 0xAC},
+ {0xF0, 0x2C},
+ {0xF1, 0x2C},
+ {0xF0, 0xAD},
+ {0xF0, 0x2D},
+ {0xF1, 0x2E},
+ {0xF0, 0xAE},
+ {0xF0, 0x2E},
+ {0xF1, 0x2E},
+ {0xF0, 0xAF},
+ {0xF0, 0x2F},
+ {0xF1, 0x30},
+ {0xF0, 0xB0},
+ {0xF0, 0x30},
+ {0xF1, 0x30},
+ {0xF0, 0xB1},
+ {0xF0, 0x31},
+ {0xF1, 0x32},
+ {0xF0, 0xB2},
+ {0xF0, 0x32},
+ {0xF1, 0x32},
+ {0xF0, 0xB3},
+ {0xF0, 0x33},
+ {0xF1, 0x34},
+ {0xF0, 0xB4},
+ {0xF0, 0x34},
+ {0xF1, 0x34},
+ {0xF0, 0xB5},
+ {0xF0, 0x35},
+ {0xF1, 0x36},
+ {0xF0, 0xB6},
+ {0xF0, 0x36},
+ {0xF1, 0x36},
+ {0xF0, 0xB7},
+ {0xF0, 0x37},
+ {0xF1, 0x38},
+ {0xF0, 0xB8},
+ {0xF0, 0x38},
+ {0xF1, 0x38},
+ {0xF0, 0xB9},
+ {0xF0, 0x39},
+ {0xF1, 0x3A},
+ {0xF0, 0xBA},
+ {0xF0, 0x3A},
+ {0xF1, 0x3A},
+ {0xF0, 0xBB},
+ {0xF0, 0x3B},
+ {0xF1, 0x3C},
+ {0xF0, 0xBC},
+ {0xF0, 0x3C},
+ {0xF1, 0x3C},
+ {0xF0, 0xBD},
+ {0xF0, 0x3D},
+ {0xF1, 0x3E},
+ {0xF0, 0xBE},
+ {0xF0, 0x3E},
+ {0xF1, 0x3E},
+ {0xF0, 0xBF},
+ {0xF0, 0x00},
+};
+
+static const unsigned short awcFrameTime[MAX_RATE] = {
+ 10, 20, 55, 110, 24, 36, 48, 72, 96, 144, 192, 216
+};
+
+/*--------------------- Export Variables --------------------------*/
+/*
+ * Description: Calculate data frame transmitting time
+ *
+ * Parameters:
+ * In:
+ * byPreambleType - Preamble Type
+ * byPktType - PK_TYPE_11A, PK_TYPE_11B, PK_TYPE_11GB, PK_TYPE_11GA
+ * cbFrameLength - Baseband Type
+ * wRate - Tx Rate
+ * Out:
+ *
+ * Return Value: FrameTime
+ *
+ */
+unsigned int
+BBuGetFrameTime(
+ unsigned char byPreambleType,
+ unsigned char byPktType,
+ unsigned int cbFrameLength,
+ unsigned short wRate
+)
+{
+ unsigned int uFrameTime;
+ unsigned int uPreamble;
+ unsigned int uTmp;
+ unsigned int uRateIdx = (unsigned int) wRate;
+ unsigned int uRate = 0;
+
+ if (uRateIdx > RATE_54M) {
+ ASSERT(0);
+ return 0;
+ }
+
+ uRate = (unsigned int)awcFrameTime[uRateIdx];
+
+ if (uRateIdx <= 3) { /* CCK mode */
+ if (byPreambleType == 1) /* Short */
+ uPreamble = 96;
+ else
+ uPreamble = 192;
+
+ uFrameTime = (cbFrameLength * 80) / uRate; /* ????? */
+ uTmp = (uFrameTime * uRate) / 80;
+ if (cbFrameLength != uTmp)
+ uFrameTime++;
+
+ return uPreamble + uFrameTime;
+ }
+ uFrameTime = (cbFrameLength * 8 + 22) / uRate; /* ???????? */
+ uTmp = ((uFrameTime * uRate) - 22) / 8;
+ if (cbFrameLength != uTmp)
+ uFrameTime++;
+
+ uFrameTime = uFrameTime * 4; /* ??????? */
+ if (byPktType != PK_TYPE_11A)
+ uFrameTime += 6; /* ?????? */
+
+ return 20 + uFrameTime; /* ?????? */
+}
+
+/*
+ * Description: Calculate Length, Service, and Signal fields of Phy for Tx
+ *
+ * Parameters:
+ * In:
+ * priv - Device Structure
+ * frame_length - Tx Frame Length
+ * tx_rate - Tx Rate
+ * Out:
+ * struct vnt_phy_field *phy
+ * - pointer to Phy Length field
+ * - pointer to Phy Service field
+ * - pointer to Phy Signal field
+ *
+ * Return Value: none
+ *
+ */
+void vnt_get_phy_field(struct vnt_private *priv, u32 frame_length,
+ u16 tx_rate, u8 pkt_type, struct vnt_phy_field *phy)
+{
+ u32 bit_count;
+ u32 count = 0;
+ u32 tmp;
+ int ext_bit;
+ u8 preamble_type = priv->byPreambleType;
+
+ bit_count = frame_length * 8;
+ ext_bit = false;
+
+ switch (tx_rate) {
+ case RATE_1M:
+ count = bit_count;
+
+ phy->signal = 0x00;
+
+ break;
+ case RATE_2M:
+ count = bit_count / 2;
+
+ if (preamble_type == 1)
+ phy->signal = 0x09;
+ else
+ phy->signal = 0x01;
+
+ break;
+ case RATE_5M:
+ count = (bit_count * 10) / 55;
+ tmp = (count * 55) / 10;
+
+ if (tmp != bit_count)
+ count++;
+
+ if (preamble_type == 1)
+ phy->signal = 0x0a;
+ else
+ phy->signal = 0x02;
+
+ break;
+ case RATE_11M:
+ count = bit_count / 11;
+ tmp = count * 11;
+
+ if (tmp != bit_count) {
+ count++;
+
+ if ((bit_count - tmp) <= 3)
+ ext_bit = true;
+ }
+
+ if (preamble_type == 1)
+ phy->signal = 0x0b;
+ else
+ phy->signal = 0x03;
+
+ break;
+ case RATE_6M:
+ if (pkt_type == PK_TYPE_11A)
+ phy->signal = 0x9b;
+ else
+ phy->signal = 0x8b;
+
+ break;
+ case RATE_9M:
+ if (pkt_type == PK_TYPE_11A)
+ phy->signal = 0x9f;
+ else
+ phy->signal = 0x8f;
+
+ break;
+ case RATE_12M:
+ if (pkt_type == PK_TYPE_11A)
+ phy->signal = 0x9a;
+ else
+ phy->signal = 0x8a;
+
+ break;
+ case RATE_18M:
+ if (pkt_type == PK_TYPE_11A)
+ phy->signal = 0x9e;
+ else
+ phy->signal = 0x8e;
+
+ break;
+ case RATE_24M:
+ if (pkt_type == PK_TYPE_11A)
+ phy->signal = 0x99;
+ else
+ phy->signal = 0x89;
+
+ break;
+ case RATE_36M:
+ if (pkt_type == PK_TYPE_11A)
+ phy->signal = 0x9d;
+ else
+ phy->signal = 0x8d;
+
+ break;
+ case RATE_48M:
+ if (pkt_type == PK_TYPE_11A)
+ phy->signal = 0x98;
+ else
+ phy->signal = 0x88;
+
+ break;
+ case RATE_54M:
+ if (pkt_type == PK_TYPE_11A)
+ phy->signal = 0x9c;
+ else
+ phy->signal = 0x8c;
+ break;
+ default:
+ if (pkt_type == PK_TYPE_11A)
+ phy->signal = 0x9c;
+ else
+ phy->signal = 0x8c;
+ break;
+ }
+
+ if (pkt_type == PK_TYPE_11B) {
+ phy->service = 0x00;
+ if (ext_bit)
+ phy->service |= 0x80;
+ phy->len = cpu_to_le16((u16)count);
+ } else {
+ phy->service = 0x00;
+ phy->len = cpu_to_le16((u16)frame_length);
+ }
+}
+
+/*
+ * Description: Read a byte from BASEBAND, by embedded programming
+ *
+ * Parameters:
+ * In:
+ * dwIoBase - I/O base address
+ * byBBAddr - address of register in Baseband
+ * Out:
+ * pbyData - data read
+ *
+ * Return Value: true if succeeded; false if failed.
+ *
+ */
+bool BBbReadEmbedded(struct vnt_private *priv,
+ unsigned char byBBAddr, unsigned char *pbyData)
+{
+ void __iomem *dwIoBase = priv->PortOffset;
+ unsigned short ww;
+ unsigned char byValue;
+
+ /* BB reg offset */
+ VNSvOutPortB(dwIoBase + MAC_REG_BBREGADR, byBBAddr);
+
+ /* turn on REGR */
+ MACvRegBitsOn(dwIoBase, MAC_REG_BBREGCTL, BBREGCTL_REGR);
+ /* W_MAX_TIMEOUT is the timeout period */
+ for (ww = 0; ww < W_MAX_TIMEOUT; ww++) {
+ VNSvInPortB(dwIoBase + MAC_REG_BBREGCTL, &byValue);
+ if (byValue & BBREGCTL_DONE)
+ break;
+ }
+
+ /* get BB data */
+ VNSvInPortB(dwIoBase + MAC_REG_BBREGDATA, pbyData);
+
+ if (ww == W_MAX_TIMEOUT) {
+ DBG_PORT80(0x30);
+ pr_debug(" DBG_PORT80(0x30)\n");
+ return false;
+ }
+ return true;
+}
+
+/*
+ * Description: Write a Byte to BASEBAND, by embedded programming
+ *
+ * Parameters:
+ * In:
+ * dwIoBase - I/O base address
+ * byBBAddr - address of register in Baseband
+ * byData - data to write
+ * Out:
+ * none
+ *
+ * Return Value: true if succeeded; false if failed.
+ *
+ */
+bool BBbWriteEmbedded(struct vnt_private *priv,
+ unsigned char byBBAddr, unsigned char byData)
+{
+ void __iomem *dwIoBase = priv->PortOffset;
+ unsigned short ww;
+ unsigned char byValue;
+
+ /* BB reg offset */
+ VNSvOutPortB(dwIoBase + MAC_REG_BBREGADR, byBBAddr);
+ /* set BB data */
+ VNSvOutPortB(dwIoBase + MAC_REG_BBREGDATA, byData);
+
+ /* turn on BBREGCTL_REGW */
+ MACvRegBitsOn(dwIoBase, MAC_REG_BBREGCTL, BBREGCTL_REGW);
+ /* W_MAX_TIMEOUT is the timeout period */
+ for (ww = 0; ww < W_MAX_TIMEOUT; ww++) {
+ VNSvInPortB(dwIoBase + MAC_REG_BBREGCTL, &byValue);
+ if (byValue & BBREGCTL_DONE)
+ break;
+ }
+
+ if (ww == W_MAX_TIMEOUT) {
+ DBG_PORT80(0x31);
+ pr_debug(" DBG_PORT80(0x31)\n");
+ return false;
+ }
+ return true;
+}
+
+/*
+ * Description: VIA VT3253 Baseband chip init function
+ *
+ * Parameters:
+ * In:
+ * dwIoBase - I/O base address
+ * byRevId - Revision ID
+ * byRFType - RF type
+ * Out:
+ * none
+ *
+ * Return Value: true if succeeded; false if failed.
+ *
+ */
+
+bool BBbVT3253Init(struct vnt_private *priv)
+{
+ bool bResult = true;
+ int ii;
+ void __iomem *dwIoBase = priv->PortOffset;
+ unsigned char byRFType = priv->byRFType;
+ unsigned char byLocalID = priv->byLocalID;
+
+ if (byRFType == RF_RFMD2959) {
+ if (byLocalID <= REV_ID_VT3253_A1) {
+ for (ii = 0; ii < CB_VT3253_INIT_FOR_RFMD; ii++)
+ bResult &= BBbWriteEmbedded(priv,
+ byVT3253InitTab_RFMD[ii][0],
+ byVT3253InitTab_RFMD[ii][1]);
+
+ } else {
+ for (ii = 0; ii < CB_VT3253B0_INIT_FOR_RFMD; ii++)
+ bResult &= BBbWriteEmbedded(priv,
+ byVT3253B0_RFMD[ii][0],
+ byVT3253B0_RFMD[ii][1]);
+
+ for (ii = 0; ii < CB_VT3253B0_AGC_FOR_RFMD2959; ii++)
+ bResult &= BBbWriteEmbedded(priv,
+ byVT3253B0_AGC4_RFMD2959[ii][0],
+ byVT3253B0_AGC4_RFMD2959[ii][1]);
+
+ VNSvOutPortD(dwIoBase + MAC_REG_ITRTMSET, 0x23);
+ MACvRegBitsOn(dwIoBase, MAC_REG_PAPEDELAY, BIT(0));
+ }
+ priv->abyBBVGA[0] = 0x18;
+ priv->abyBBVGA[1] = 0x0A;
+ priv->abyBBVGA[2] = 0x0;
+ priv->abyBBVGA[3] = 0x0;
+ priv->ldBmThreshold[0] = -70;
+ priv->ldBmThreshold[1] = -50;
+ priv->ldBmThreshold[2] = 0;
+ priv->ldBmThreshold[3] = 0;
+ } else if ((byRFType == RF_AIROHA) || (byRFType == RF_AL2230S)) {
+ for (ii = 0; ii < CB_VT3253B0_INIT_FOR_AIROHA2230; ii++)
+ bResult &= BBbWriteEmbedded(priv,
+ byVT3253B0_AIROHA2230[ii][0],
+ byVT3253B0_AIROHA2230[ii][1]);
+
+ for (ii = 0; ii < CB_VT3253B0_AGC; ii++)
+ bResult &= BBbWriteEmbedded(priv,
+ byVT3253B0_AGC[ii][0], byVT3253B0_AGC[ii][1]);
+
+ priv->abyBBVGA[0] = 0x1C;
+ priv->abyBBVGA[1] = 0x10;
+ priv->abyBBVGA[2] = 0x0;
+ priv->abyBBVGA[3] = 0x0;
+ priv->ldBmThreshold[0] = -70;
+ priv->ldBmThreshold[1] = -48;
+ priv->ldBmThreshold[2] = 0;
+ priv->ldBmThreshold[3] = 0;
+ } else if (byRFType == RF_UW2451) {
+ for (ii = 0; ii < CB_VT3253B0_INIT_FOR_UW2451; ii++)
+ bResult &= BBbWriteEmbedded(priv,
+ byVT3253B0_UW2451[ii][0],
+ byVT3253B0_UW2451[ii][1]);
+
+ for (ii = 0; ii < CB_VT3253B0_AGC; ii++)
+ bResult &= BBbWriteEmbedded(priv,
+ byVT3253B0_AGC[ii][0],
+ byVT3253B0_AGC[ii][1]);
+
+ VNSvOutPortB(dwIoBase + MAC_REG_ITRTMSET, 0x23);
+ MACvRegBitsOn(dwIoBase, MAC_REG_PAPEDELAY, BIT(0));
+
+ priv->abyBBVGA[0] = 0x14;
+ priv->abyBBVGA[1] = 0x0A;
+ priv->abyBBVGA[2] = 0x0;
+ priv->abyBBVGA[3] = 0x0;
+ priv->ldBmThreshold[0] = -60;
+ priv->ldBmThreshold[1] = -50;
+ priv->ldBmThreshold[2] = 0;
+ priv->ldBmThreshold[3] = 0;
+ } else if (byRFType == RF_UW2452) {
+ for (ii = 0; ii < CB_VT3253B0_INIT_FOR_UW2451; ii++)
+ bResult &= BBbWriteEmbedded(priv,
+ byVT3253B0_UW2451[ii][0],
+ byVT3253B0_UW2451[ii][1]);
+
+ /* Init ANT B select,TX Config CR09 = 0x61->0x45, 0x45->0x41(VC1/VC2 define, make the ANT_A, ANT_B inverted) */
+ /*bResult &= BBbWriteEmbedded(dwIoBase,0x09,0x41);*/
+ /* Init ANT B select,RX Config CR10 = 0x28->0x2A, 0x2A->0x28(VC1/VC2 define, make the ANT_A, ANT_B inverted) */
+ /*bResult &= BBbWriteEmbedded(dwIoBase,0x0a,0x28);*/
+ /* Select VC1/VC2, CR215 = 0x02->0x06 */
+ bResult &= BBbWriteEmbedded(priv, 0xd7, 0x06);
+
+ /* {{RobertYu:20050125, request by Jack */
+ bResult &= BBbWriteEmbedded(priv, 0x90, 0x20);
+ bResult &= BBbWriteEmbedded(priv, 0x97, 0xeb);
+ /* }} */
+
+ /* {{RobertYu:20050221, request by Jack */
+ bResult &= BBbWriteEmbedded(priv, 0xa6, 0x00);
+ bResult &= BBbWriteEmbedded(priv, 0xa8, 0x30);
+ /* }} */
+ bResult &= BBbWriteEmbedded(priv, 0xb0, 0x58);
+
+ for (ii = 0; ii < CB_VT3253B0_AGC; ii++)
+ bResult &= BBbWriteEmbedded(priv,
+ byVT3253B0_AGC[ii][0], byVT3253B0_AGC[ii][1]);
+
+ priv->abyBBVGA[0] = 0x14;
+ priv->abyBBVGA[1] = 0x0A;
+ priv->abyBBVGA[2] = 0x0;
+ priv->abyBBVGA[3] = 0x0;
+ priv->ldBmThreshold[0] = -60;
+ priv->ldBmThreshold[1] = -50;
+ priv->ldBmThreshold[2] = 0;
+ priv->ldBmThreshold[3] = 0;
+ /* }} RobertYu */
+
+ } else if (byRFType == RF_VT3226) {
+ for (ii = 0; ii < CB_VT3253B0_INIT_FOR_AIROHA2230; ii++)
+ bResult &= BBbWriteEmbedded(priv,
+ byVT3253B0_AIROHA2230[ii][0],
+ byVT3253B0_AIROHA2230[ii][1]);
+
+ for (ii = 0; ii < CB_VT3253B0_AGC; ii++)
+ bResult &= BBbWriteEmbedded(priv,
+ byVT3253B0_AGC[ii][0], byVT3253B0_AGC[ii][1]);
+
+ priv->abyBBVGA[0] = 0x1C;
+ priv->abyBBVGA[1] = 0x10;
+ priv->abyBBVGA[2] = 0x0;
+ priv->abyBBVGA[3] = 0x0;
+ priv->ldBmThreshold[0] = -70;
+ priv->ldBmThreshold[1] = -48;
+ priv->ldBmThreshold[2] = 0;
+ priv->ldBmThreshold[3] = 0;
+ /* Fix VT3226 DFC system timing issue */
+ MACvSetRFLE_LatchBase(dwIoBase);
+ /* {{ RobertYu: 20050104 */
+ } else if (byRFType == RF_AIROHA7230) {
+ for (ii = 0; ii < CB_VT3253B0_INIT_FOR_AIROHA2230; ii++)
+ bResult &= BBbWriteEmbedded(priv,
+ byVT3253B0_AIROHA2230[ii][0],
+ byVT3253B0_AIROHA2230[ii][1]);
+
+
+ /* {{ RobertYu:20050223, request by JerryChung */
+ /* Init ANT B select,TX Config CR09 = 0x61->0x45, 0x45->0x41(VC1/VC2 define, make the ANT_A, ANT_B inverted) */
+ /*bResult &= BBbWriteEmbedded(dwIoBase,0x09,0x41);*/
+ /* Init ANT B select,RX Config CR10 = 0x28->0x2A, 0x2A->0x28(VC1/VC2 define, make the ANT_A, ANT_B inverted) */
+ /*bResult &= BBbWriteEmbedded(dwIoBase,0x0a,0x28);*/
+ /* Select VC1/VC2, CR215 = 0x02->0x06 */
+ bResult &= BBbWriteEmbedded(priv, 0xd7, 0x06);
+ /* }} */
+
+ for (ii = 0; ii < CB_VT3253B0_AGC; ii++)
+ bResult &= BBbWriteEmbedded(priv,
+ byVT3253B0_AGC[ii][0], byVT3253B0_AGC[ii][1]);
+
+ priv->abyBBVGA[0] = 0x1C;
+ priv->abyBBVGA[1] = 0x10;
+ priv->abyBBVGA[2] = 0x0;
+ priv->abyBBVGA[3] = 0x0;
+ priv->ldBmThreshold[0] = -70;
+ priv->ldBmThreshold[1] = -48;
+ priv->ldBmThreshold[2] = 0;
+ priv->ldBmThreshold[3] = 0;
+ /* }} RobertYu */
+ } else {
+ /* No VGA Table now */
+ priv->bUpdateBBVGA = false;
+ priv->abyBBVGA[0] = 0x1C;
+ }
+
+ if (byLocalID > REV_ID_VT3253_A1) {
+ BBbWriteEmbedded(priv, 0x04, 0x7F);
+ BBbWriteEmbedded(priv, 0x0D, 0x01);
+ }
+
+ return bResult;
+}
+
+/*
+ * Description: Set ShortSlotTime mode
+ *
+ * Parameters:
+ * In:
+ * priv - Device Structure
+ * Out:
+ * none
+ *
+ * Return Value: none
+ *
+ */
+void
+BBvSetShortSlotTime(struct vnt_private *priv)
+{
+ unsigned char byBBRxConf = 0;
+ unsigned char byBBVGA = 0;
+
+ BBbReadEmbedded(priv, 0x0A, &byBBRxConf); /* CR10 */
+
+ if (priv->bShortSlotTime)
+ byBBRxConf &= 0xDF; /* 1101 1111 */
+ else
+ byBBRxConf |= 0x20; /* 0010 0000 */
+
+ /* patch for 3253B0 Baseband with Cardbus module */
+ BBbReadEmbedded(priv, 0xE7, &byBBVGA);
+ if (byBBVGA == priv->abyBBVGA[0])
+ byBBRxConf |= 0x20; /* 0010 0000 */
+
+ BBbWriteEmbedded(priv, 0x0A, byBBRxConf); /* CR10 */
+}
+
+void BBvSetVGAGainOffset(struct vnt_private *priv, unsigned char byData)
+{
+ unsigned char byBBRxConf = 0;
+
+ BBbWriteEmbedded(priv, 0xE7, byData);
+
+ BBbReadEmbedded(priv, 0x0A, &byBBRxConf); /* CR10 */
+ /* patch for 3253B0 Baseband with Cardbus module */
+ if (byData == priv->abyBBVGA[0])
+ byBBRxConf |= 0x20; /* 0010 0000 */
+ else if (priv->bShortSlotTime)
+ byBBRxConf &= 0xDF; /* 1101 1111 */
+ else
+ byBBRxConf |= 0x20; /* 0010 0000 */
+ priv->byBBVGACurrent = byData;
+ BBbWriteEmbedded(priv, 0x0A, byBBRxConf); /* CR10 */
+}
+
+/*
+ * Description: Baseband SoftwareReset
+ *
+ * Parameters:
+ * In:
+ * dwIoBase - I/O base address
+ * Out:
+ * none
+ *
+ * Return Value: none
+ *
+ */
+void
+BBvSoftwareReset(struct vnt_private *priv)
+{
+ BBbWriteEmbedded(priv, 0x50, 0x40);
+ BBbWriteEmbedded(priv, 0x50, 0);
+ BBbWriteEmbedded(priv, 0x9C, 0x01);
+ BBbWriteEmbedded(priv, 0x9C, 0);
+}
+
+/*
+ * Description: Baseband Power Save Mode ON
+ *
+ * Parameters:
+ * In:
+ * dwIoBase - I/O base address
+ * Out:
+ * none
+ *
+ * Return Value: none
+ *
+ */
+void
+BBvPowerSaveModeON(struct vnt_private *priv)
+{
+ unsigned char byOrgData;
+
+ BBbReadEmbedded(priv, 0x0D, &byOrgData);
+ byOrgData |= BIT(0);
+ BBbWriteEmbedded(priv, 0x0D, byOrgData);
+}
+
+/*
+ * Description: Baseband Power Save Mode OFF
+ *
+ * Parameters:
+ * In:
+ * dwIoBase - I/O base address
+ * Out:
+ * none
+ *
+ * Return Value: none
+ *
+ */
+void
+BBvPowerSaveModeOFF(struct vnt_private *priv)
+{
+ unsigned char byOrgData;
+
+ BBbReadEmbedded(priv, 0x0D, &byOrgData);
+ byOrgData &= ~(BIT(0));
+ BBbWriteEmbedded(priv, 0x0D, byOrgData);
+}
+
+/*
+ * Description: Set Tx Antenna mode
+ *
+ * Parameters:
+ * In:
+ * priv - Device Structure
+ * byAntennaMode - Antenna Mode
+ * Out:
+ * none
+ *
+ * Return Value: none
+ *
+ */
+
+void
+BBvSetTxAntennaMode(struct vnt_private *priv, unsigned char byAntennaMode)
+{
+ unsigned char byBBTxConf;
+
+ BBbReadEmbedded(priv, 0x09, &byBBTxConf); /* CR09 */
+ if (byAntennaMode == ANT_DIVERSITY) {
+ /* bit 1 is diversity */
+ byBBTxConf |= 0x02;
+ } else if (byAntennaMode == ANT_A) {
+ /* bit 2 is ANTSEL */
+ byBBTxConf &= 0xF9; /* 1111 1001 */
+ } else if (byAntennaMode == ANT_B) {
+ byBBTxConf &= 0xFD; /* 1111 1101 */
+ byBBTxConf |= 0x04;
+ }
+ BBbWriteEmbedded(priv, 0x09, byBBTxConf); /* CR09 */
+}
+
+/*
+ * Description: Set Rx Antenna mode
+ *
+ * Parameters:
+ * In:
+ * priv - Device Structure
+ * byAntennaMode - Antenna Mode
+ * Out:
+ * none
+ *
+ * Return Value: none
+ *
+ */
+
+void
+BBvSetRxAntennaMode(struct vnt_private *priv, unsigned char byAntennaMode)
+{
+ unsigned char byBBRxConf;
+
+ BBbReadEmbedded(priv, 0x0A, &byBBRxConf); /* CR10 */
+ if (byAntennaMode == ANT_DIVERSITY) {
+ byBBRxConf |= 0x01;
+
+ } else if (byAntennaMode == ANT_A) {
+ byBBRxConf &= 0xFC; /* 1111 1100 */
+ } else if (byAntennaMode == ANT_B) {
+ byBBRxConf &= 0xFE; /* 1111 1110 */
+ byBBRxConf |= 0x02;
+ }
+ BBbWriteEmbedded(priv, 0x0A, byBBRxConf); /* CR10 */
+}
+
+/*
+ * Description: BBvSetDeepSleep
+ *
+ * Parameters:
+ * In:
+ * priv - Device Structure
+ * Out:
+ * none
+ *
+ * Return Value: none
+ *
+ */
+void
+BBvSetDeepSleep(struct vnt_private *priv, unsigned char byLocalID)
+{
+ BBbWriteEmbedded(priv, 0x0C, 0x17); /* CR12 */
+ BBbWriteEmbedded(priv, 0x0D, 0xB9); /* CR13 */
+}
+
+void
+BBvExitDeepSleep(struct vnt_private *priv, unsigned char byLocalID)
+{
+ BBbWriteEmbedded(priv, 0x0C, 0x00); /* CR12 */
+ BBbWriteEmbedded(priv, 0x0D, 0x01); /* CR13 */
+}
diff --git a/drivers/staging/vt6655/baseband.h b/drivers/staging/vt6655/baseband.h
new file mode 100644
index 000000000..43a4fb1f3
--- /dev/null
+++ b/drivers/staging/vt6655/baseband.h
@@ -0,0 +1,96 @@
+/*
+ * Copyright (c) 1996, 2003 VIA Networking Technologies, Inc.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ *
+ * File: baseband.h
+ *
+ * Purpose: Implement functions to access baseband
+ *
+ * Author: Jerry Chen
+ *
+ * Date: Jun. 5, 2002
+ *
+ */
+
+#ifndef __BASEBAND_H__
+#define __BASEBAND_H__
+
+#include "device.h"
+
+/*
+ * Registers in the BASEBAND
+ */
+#define BB_MAX_CONTEXT_SIZE 256
+
+/*
+ * Baseband RF pair definition in eeprom (Bits 6..0)
+ */
+
+#define PREAMBLE_LONG 0
+#define PREAMBLE_SHORT 1
+
+#define F5G 0
+#define F2_4G 1
+
+#define TOP_RATE_54M 0x80000000
+#define TOP_RATE_48M 0x40000000
+#define TOP_RATE_36M 0x20000000
+#define TOP_RATE_24M 0x10000000
+#define TOP_RATE_18M 0x08000000
+#define TOP_RATE_12M 0x04000000
+#define TOP_RATE_11M 0x02000000
+#define TOP_RATE_9M 0x01000000
+#define TOP_RATE_6M 0x00800000
+#define TOP_RATE_55M 0x00400000
+#define TOP_RATE_2M 0x00200000
+#define TOP_RATE_1M 0x00100000
+
+#define BBvClearFOE(dwIoBase) \
+ BBbWriteEmbedded(dwIoBase, 0xB1, 0)
+
+#define BBvSetFOE(dwIoBase) \
+ BBbWriteEmbedded(dwIoBase, 0xB1, 0x0C)
+
+unsigned int
+BBuGetFrameTime(
+ unsigned char byPreambleType,
+ unsigned char byPktType,
+ unsigned int cbFrameLength,
+ unsigned short wRate
+);
+
+void vnt_get_phy_field(struct vnt_private *, u32 frame_length,
+ u16 tx_rate, u8 pkt_type, struct vnt_phy_field *);
+
+bool BBbReadEmbedded(struct vnt_private *, unsigned char byBBAddr, unsigned char *pbyData);
+bool BBbWriteEmbedded(struct vnt_private *, unsigned char byBBAddr, unsigned char byData);
+
+void BBvSetShortSlotTime(struct vnt_private *);
+void BBvSetVGAGainOffset(struct vnt_private *, unsigned char byData);
+
+/* VT3253 Baseband */
+bool BBbVT3253Init(struct vnt_private *);
+void BBvSoftwareReset(struct vnt_private *);
+void BBvPowerSaveModeON(struct vnt_private *);
+void BBvPowerSaveModeOFF(struct vnt_private *);
+void BBvSetTxAntennaMode(struct vnt_private *, unsigned char byAntennaMode);
+void BBvSetRxAntennaMode(struct vnt_private *, unsigned char byAntennaMode);
+void BBvSetDeepSleep(struct vnt_private *, unsigned char byLocalID);
+void BBvExitDeepSleep(struct vnt_private *, unsigned char byLocalID);
+
+#endif /* __BASEBAND_H__ */
diff --git a/drivers/staging/vt6655/card.c b/drivers/staging/vt6655/card.c
new file mode 100644
index 000000000..e00c0605d
--- /dev/null
+++ b/drivers/staging/vt6655/card.c
@@ -0,0 +1,1017 @@
+/*
+ * Copyright (c) 1996, 2003 VIA Networking Technologies, Inc.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * File: card.c
+ * Purpose: Provide functions to setup NIC operation mode
+ * Functions:
+ * s_vSafeResetTx - Rest Tx
+ * CARDvSetRSPINF - Set RSPINF
+ * CARDvUpdateBasicTopRate - Update BasicTopRate
+ * CARDbAddBasicRate - Add to BasicRateSet
+ * CARDbIsOFDMinBasicRate - Check if any OFDM rate is in BasicRateSet
+ * CARDvSetLoopbackMode - Set Loopback mode
+ * CARDbSoftwareReset - Sortware reset NIC
+ * CARDqGetTSFOffset - Calculate TSFOffset
+ * CARDbGetCurrentTSF - Read Current NIC TSF counter
+ * CARDqGetNextTBTT - Calculate Next Beacon TSF counter
+ * CARDvSetFirstNextTBTT - Set NIC Beacon time
+ * CARDvUpdateNextTBTT - Sync. NIC Beacon time
+ * CARDbRadioPowerOff - Turn Off NIC Radio Power
+ * CARDbRadioPowerOn - Turn On NIC Radio Power
+ *
+ * Revision History:
+ * 06-10-2003 Bryan YC Fan: Re-write codes to support VT3253 spec.
+ * 08-26-2003 Kyle Hsu: Modify the defination type of dwIoBase.
+ * 09-01-2003 Bryan YC Fan: Add vUpdateIFS().
+ *
+ */
+
+#include "tmacro.h"
+#include "card.h"
+#include "baseband.h"
+#include "mac.h"
+#include "desc.h"
+#include "rf.h"
+#include "power.h"
+
+/*--------------------- Static Definitions -------------------------*/
+
+#define C_SIFS_A 16 /* micro sec. */
+#define C_SIFS_BG 10
+
+#define C_EIFS 80 /* micro sec. */
+
+#define C_SLOT_SHORT 9 /* micro sec. */
+#define C_SLOT_LONG 20
+
+#define C_CWMIN_A 15 /* slot time */
+#define C_CWMIN_B 31
+
+#define C_CWMAX 1023 /* slot time */
+
+#define WAIT_BEACON_TX_DOWN_TMO 3 /* Times */
+
+/*--------------------- Static Variables --------------------------*/
+
+static const unsigned short cwRXBCNTSFOff[MAX_RATE] = {
+ 17, 17, 17, 17, 34, 23, 17, 11, 8, 5, 4, 3};
+
+/*--------------------- Static Functions --------------------------*/
+
+static
+void
+s_vCalculateOFDMRParameter(
+ unsigned char byRate,
+ u8 bb_type,
+ unsigned char *pbyTxRate,
+ unsigned char *pbyRsvTime
+);
+
+/*--------------------- Export Functions --------------------------*/
+
+/*
+ * Description: Calculate TxRate and RsvTime fields for RSPINF in OFDM mode.
+ *
+ * Parameters:
+ * In:
+ * wRate - Tx Rate
+ * byPktType - Tx Packet type
+ * Out:
+ * pbyTxRate - pointer to RSPINF TxRate field
+ * pbyRsvTime - pointer to RSPINF RsvTime field
+ *
+ * Return Value: none
+ */
+static
+void
+s_vCalculateOFDMRParameter(
+ unsigned char byRate,
+ u8 bb_type,
+ unsigned char *pbyTxRate,
+ unsigned char *pbyRsvTime
+)
+{
+ switch (byRate) {
+ case RATE_6M:
+ if (bb_type == BB_TYPE_11A) { /* 5GHZ */
+ *pbyTxRate = 0x9B;
+ *pbyRsvTime = 44;
+ } else {
+ *pbyTxRate = 0x8B;
+ *pbyRsvTime = 50;
+ }
+ break;
+
+ case RATE_9M:
+ if (bb_type == BB_TYPE_11A) { /* 5GHZ */
+ *pbyTxRate = 0x9F;
+ *pbyRsvTime = 36;
+ } else {
+ *pbyTxRate = 0x8F;
+ *pbyRsvTime = 42;
+ }
+ break;
+
+ case RATE_12M:
+ if (bb_type == BB_TYPE_11A) { /* 5GHZ */
+ *pbyTxRate = 0x9A;
+ *pbyRsvTime = 32;
+ } else {
+ *pbyTxRate = 0x8A;
+ *pbyRsvTime = 38;
+ }
+ break;
+
+ case RATE_18M:
+ if (bb_type == BB_TYPE_11A) { /* 5GHZ */
+ *pbyTxRate = 0x9E;
+ *pbyRsvTime = 28;
+ } else {
+ *pbyTxRate = 0x8E;
+ *pbyRsvTime = 34;
+ }
+ break;
+
+ case RATE_36M:
+ if (bb_type == BB_TYPE_11A) { /* 5GHZ */
+ *pbyTxRate = 0x9D;
+ *pbyRsvTime = 24;
+ } else {
+ *pbyTxRate = 0x8D;
+ *pbyRsvTime = 30;
+ }
+ break;
+
+ case RATE_48M:
+ if (bb_type == BB_TYPE_11A) { /* 5GHZ */
+ *pbyTxRate = 0x98;
+ *pbyRsvTime = 24;
+ } else {
+ *pbyTxRate = 0x88;
+ *pbyRsvTime = 30;
+ }
+ break;
+
+ case RATE_54M:
+ if (bb_type == BB_TYPE_11A) { /* 5GHZ */
+ *pbyTxRate = 0x9C;
+ *pbyRsvTime = 24;
+ } else {
+ *pbyTxRate = 0x8C;
+ *pbyRsvTime = 30;
+ }
+ break;
+
+ case RATE_24M:
+ default:
+ if (bb_type == BB_TYPE_11A) { /* 5GHZ */
+ *pbyTxRate = 0x99;
+ *pbyRsvTime = 28;
+ } else {
+ *pbyTxRate = 0x89;
+ *pbyRsvTime = 34;
+ }
+ break;
+ }
+}
+
+/*--------------------- Export Functions --------------------------*/
+
+/*
+ * Description: Update IFS
+ *
+ * Parameters:
+ * In:
+ * pDevice - The adapter to be set
+ * Out:
+ * none
+ *
+ * Return Value: None.
+ */
+bool CARDbSetPhyParameter(struct vnt_private *pDevice, u8 bb_type)
+{
+ unsigned char byCWMaxMin = 0;
+ unsigned char bySlot = 0;
+ unsigned char bySIFS = 0;
+ unsigned char byDIFS = 0;
+ unsigned char byData;
+ int i;
+
+ /* Set SIFS, DIFS, EIFS, SlotTime, CwMin */
+ if (bb_type == BB_TYPE_11A) {
+ if (pDevice->byRFType == RF_AIROHA7230) {
+ /* AL7230 use single PAPE and connect to PAPE_2.4G */
+ MACvSetBBType(pDevice->PortOffset, BB_TYPE_11G);
+ pDevice->abyBBVGA[0] = 0x20;
+ pDevice->abyBBVGA[2] = 0x10;
+ pDevice->abyBBVGA[3] = 0x10;
+ BBbReadEmbedded(pDevice, 0xE7, &byData);
+ if (byData == 0x1C)
+ BBbWriteEmbedded(pDevice, 0xE7, pDevice->abyBBVGA[0]);
+
+ } else if (pDevice->byRFType == RF_UW2452) {
+ MACvSetBBType(pDevice->PortOffset, BB_TYPE_11A);
+ pDevice->abyBBVGA[0] = 0x18;
+ BBbReadEmbedded(pDevice, 0xE7, &byData);
+ if (byData == 0x14) {
+ BBbWriteEmbedded(pDevice, 0xE7, pDevice->abyBBVGA[0]);
+ BBbWriteEmbedded(pDevice, 0xE1, 0x57);
+ }
+ } else {
+ MACvSetBBType(pDevice->PortOffset, BB_TYPE_11A);
+ }
+ BBbWriteEmbedded(pDevice, 0x88, 0x03);
+ bySlot = C_SLOT_SHORT;
+ bySIFS = C_SIFS_A;
+ byDIFS = C_SIFS_A + 2*C_SLOT_SHORT;
+ byCWMaxMin = 0xA4;
+ } else if (bb_type == BB_TYPE_11B) {
+ MACvSetBBType(pDevice->PortOffset, BB_TYPE_11B);
+ if (pDevice->byRFType == RF_AIROHA7230) {
+ pDevice->abyBBVGA[0] = 0x1C;
+ pDevice->abyBBVGA[2] = 0x00;
+ pDevice->abyBBVGA[3] = 0x00;
+ BBbReadEmbedded(pDevice, 0xE7, &byData);
+ if (byData == 0x20)
+ BBbWriteEmbedded(pDevice, 0xE7, pDevice->abyBBVGA[0]);
+
+ } else if (pDevice->byRFType == RF_UW2452) {
+ pDevice->abyBBVGA[0] = 0x14;
+ BBbReadEmbedded(pDevice, 0xE7, &byData);
+ if (byData == 0x18) {
+ BBbWriteEmbedded(pDevice, 0xE7, pDevice->abyBBVGA[0]);
+ BBbWriteEmbedded(pDevice, 0xE1, 0xD3);
+ }
+ }
+ BBbWriteEmbedded(pDevice, 0x88, 0x02);
+ bySlot = C_SLOT_LONG;
+ bySIFS = C_SIFS_BG;
+ byDIFS = C_SIFS_BG + 2*C_SLOT_LONG;
+ byCWMaxMin = 0xA5;
+ } else { /* PK_TYPE_11GA & PK_TYPE_11GB */
+ MACvSetBBType(pDevice->PortOffset, BB_TYPE_11G);
+ if (pDevice->byRFType == RF_AIROHA7230) {
+ pDevice->abyBBVGA[0] = 0x1C;
+ pDevice->abyBBVGA[2] = 0x00;
+ pDevice->abyBBVGA[3] = 0x00;
+ BBbReadEmbedded(pDevice, 0xE7, &byData);
+ if (byData == 0x20)
+ BBbWriteEmbedded(pDevice, 0xE7, pDevice->abyBBVGA[0]);
+
+ } else if (pDevice->byRFType == RF_UW2452) {
+ pDevice->abyBBVGA[0] = 0x14;
+ BBbReadEmbedded(pDevice, 0xE7, &byData);
+ if (byData == 0x18) {
+ BBbWriteEmbedded(pDevice, 0xE7, pDevice->abyBBVGA[0]);
+ BBbWriteEmbedded(pDevice, 0xE1, 0xD3);
+ }
+ }
+ BBbWriteEmbedded(pDevice, 0x88, 0x08);
+ bySIFS = C_SIFS_BG;
+
+ if (pDevice->bShortSlotTime) {
+ bySlot = C_SLOT_SHORT;
+ byDIFS = C_SIFS_BG + 2*C_SLOT_SHORT;
+ } else {
+ bySlot = C_SLOT_LONG;
+ byDIFS = C_SIFS_BG + 2*C_SLOT_LONG;
+ }
+
+ byCWMaxMin = 0xa4;
+
+ for (i = RATE_54M; i >= RATE_6M; i--) {
+ if (pDevice->basic_rates & ((u32)(0x1 << i))) {
+ byCWMaxMin |= 0x1;
+ break;
+ }
+ }
+ }
+
+ if (pDevice->byRFType == RF_RFMD2959) {
+ /*
+ * bcs TX_PE will reserve 3 us hardware's processing
+ * time here is 2 us.
+ */
+ bySIFS -= 3;
+ byDIFS -= 3;
+ /*
+ * TX_PE will reserve 3 us for MAX2829 A mode only, it is for
+ * better TX throughput; MAC will need 2 us to process, so the
+ * SIFS, DIFS can be shorter by 2 us.
+ */
+ }
+
+ if (pDevice->bySIFS != bySIFS) {
+ pDevice->bySIFS = bySIFS;
+ VNSvOutPortB(pDevice->PortOffset + MAC_REG_SIFS, pDevice->bySIFS);
+ }
+ if (pDevice->byDIFS != byDIFS) {
+ pDevice->byDIFS = byDIFS;
+ VNSvOutPortB(pDevice->PortOffset + MAC_REG_DIFS, pDevice->byDIFS);
+ }
+ if (pDevice->byEIFS != C_EIFS) {
+ pDevice->byEIFS = C_EIFS;
+ VNSvOutPortB(pDevice->PortOffset + MAC_REG_EIFS, pDevice->byEIFS);
+ }
+ if (pDevice->bySlot != bySlot) {
+ pDevice->bySlot = bySlot;
+ VNSvOutPortB(pDevice->PortOffset + MAC_REG_SLOT, pDevice->bySlot);
+
+ BBvSetShortSlotTime(pDevice);
+ }
+ if (pDevice->byCWMaxMin != byCWMaxMin) {
+ pDevice->byCWMaxMin = byCWMaxMin;
+ VNSvOutPortB(pDevice->PortOffset + MAC_REG_CWMAXMIN0, pDevice->byCWMaxMin);
+ }
+
+ pDevice->byPacketType = CARDbyGetPktType(pDevice);
+
+ CARDvSetRSPINF(pDevice, bb_type);
+
+ return true;
+}
+
+/*
+ * Description: Sync. TSF counter to BSS
+ * Get TSF offset and write to HW
+ *
+ * Parameters:
+ * In:
+ * pDevice - The adapter to be sync.
+ * byRxRate - data rate of receive beacon
+ * qwBSSTimestamp - Rx BCN's TSF
+ * qwLocalTSF - Local TSF
+ * Out:
+ * none
+ *
+ * Return Value: none
+ */
+bool CARDbUpdateTSF(struct vnt_private *pDevice, unsigned char byRxRate,
+ u64 qwBSSTimestamp)
+{
+ u64 local_tsf;
+ u64 qwTSFOffset = 0;
+
+ CARDbGetCurrentTSF(pDevice, &local_tsf);
+
+ if (qwBSSTimestamp != local_tsf) {
+ qwTSFOffset = CARDqGetTSFOffset(byRxRate, qwBSSTimestamp,
+ local_tsf);
+ /* adjust TSF, HW's TSF add TSF Offset reg */
+ VNSvOutPortD(pDevice->PortOffset + MAC_REG_TSFOFST, (u32)qwTSFOffset);
+ VNSvOutPortD(pDevice->PortOffset + MAC_REG_TSFOFST + 4, (u32)(qwTSFOffset >> 32));
+ MACvRegBitsOn(pDevice->PortOffset, MAC_REG_TFTCTL, TFTCTL_TSFSYNCEN);
+ }
+ return true;
+}
+
+/*
+ * Description: Set NIC TSF counter for first Beacon time
+ * Get NEXTTBTT from adjusted TSF and Beacon Interval
+ *
+ * Parameters:
+ * In:
+ * pDevice - The adapter to be set.
+ * wBeaconInterval - Beacon Interval
+ * Out:
+ * none
+ *
+ * Return Value: true if succeed; otherwise false
+ */
+bool CARDbSetBeaconPeriod(struct vnt_private *pDevice,
+ unsigned short wBeaconInterval)
+{
+ u64 qwNextTBTT = 0;
+
+ CARDbGetCurrentTSF(pDevice, &qwNextTBTT); /* Get Local TSF counter */
+
+ qwNextTBTT = CARDqGetNextTBTT(qwNextTBTT, wBeaconInterval);
+
+ /* set HW beacon interval */
+ VNSvOutPortW(pDevice->PortOffset + MAC_REG_BI, wBeaconInterval);
+ pDevice->wBeaconInterval = wBeaconInterval;
+ /* Set NextTBTT */
+ VNSvOutPortD(pDevice->PortOffset + MAC_REG_NEXTTBTT, (u32)qwNextTBTT);
+ VNSvOutPortD(pDevice->PortOffset + MAC_REG_NEXTTBTT + 4, (u32)(qwNextTBTT >> 32));
+ MACvRegBitsOn(pDevice->PortOffset, MAC_REG_TFTCTL, TFTCTL_TBTTSYNCEN);
+
+ return true;
+}
+
+/*
+ * Description: Turn off Radio power
+ *
+ * Parameters:
+ * In:
+ * pDevice - The adapter to be turned off
+ * Out:
+ * none
+ *
+ * Return Value: true if success; otherwise false
+ */
+bool CARDbRadioPowerOff(struct vnt_private *pDevice)
+{
+ bool bResult = true;
+
+ if (pDevice->bRadioOff == true)
+ return true;
+
+ switch (pDevice->byRFType) {
+ case RF_RFMD2959:
+ MACvWordRegBitsOff(pDevice->PortOffset, MAC_REG_SOFTPWRCTL, SOFTPWRCTL_TXPEINV);
+ MACvWordRegBitsOn(pDevice->PortOffset, MAC_REG_SOFTPWRCTL, SOFTPWRCTL_SWPE1);
+ break;
+
+ case RF_AIROHA:
+ case RF_AL2230S:
+ case RF_AIROHA7230:
+ MACvWordRegBitsOff(pDevice->PortOffset, MAC_REG_SOFTPWRCTL, SOFTPWRCTL_SWPE2);
+ MACvWordRegBitsOff(pDevice->PortOffset, MAC_REG_SOFTPWRCTL, SOFTPWRCTL_SWPE3);
+ break;
+
+ }
+
+ MACvRegBitsOff(pDevice->PortOffset, MAC_REG_HOSTCR, HOSTCR_RXON);
+
+ BBvSetDeepSleep(pDevice, pDevice->byLocalID);
+
+ pDevice->bRadioOff = true;
+ pr_debug("chester power off\n");
+ MACvRegBitsOn(pDevice->PortOffset, MAC_REG_GPIOCTL0, LED_ACTSET); /* LED issue */
+ return bResult;
+}
+
+/*
+ * Description: Turn on Radio power
+ *
+ * Parameters:
+ * In:
+ * pDevice - The adapter to be turned on
+ * Out:
+ * none
+ *
+ * Return Value: true if success; otherwise false
+ */
+bool CARDbRadioPowerOn(struct vnt_private *pDevice)
+{
+ bool bResult = true;
+
+ pr_debug("chester power on\n");
+ if (pDevice->bRadioControlOff == true) {
+ if (pDevice->bHWRadioOff == true)
+ pr_debug("chester bHWRadioOff\n");
+ if (pDevice->bRadioControlOff == true)
+ pr_debug("chester bRadioControlOff\n");
+ return false; }
+
+ if (pDevice->bRadioOff == false) {
+ pr_debug("chester pbRadioOff\n");
+ return true; }
+
+ BBvExitDeepSleep(pDevice, pDevice->byLocalID);
+
+ MACvRegBitsOn(pDevice->PortOffset, MAC_REG_HOSTCR, HOSTCR_RXON);
+
+ switch (pDevice->byRFType) {
+ case RF_RFMD2959:
+ MACvWordRegBitsOn(pDevice->PortOffset, MAC_REG_SOFTPWRCTL, SOFTPWRCTL_TXPEINV);
+ MACvWordRegBitsOff(pDevice->PortOffset, MAC_REG_SOFTPWRCTL, SOFTPWRCTL_SWPE1);
+ break;
+
+ case RF_AIROHA:
+ case RF_AL2230S:
+ case RF_AIROHA7230:
+ MACvWordRegBitsOn(pDevice->PortOffset, MAC_REG_SOFTPWRCTL, (SOFTPWRCTL_SWPE2 |
+ SOFTPWRCTL_SWPE3));
+ break;
+
+ }
+
+ pDevice->bRadioOff = false;
+ pr_debug("chester power on\n");
+ MACvRegBitsOff(pDevice->PortOffset, MAC_REG_GPIOCTL0, LED_ACTSET); /* LED issue */
+ return bResult;
+}
+
+void
+CARDvSafeResetTx(
+ struct vnt_private *pDevice
+)
+{
+ unsigned int uu;
+ PSTxDesc pCurrTD;
+
+ /* initialize TD index */
+ pDevice->apTailTD[0] = pDevice->apCurrTD[0] = &(pDevice->apTD0Rings[0]);
+ pDevice->apTailTD[1] = pDevice->apCurrTD[1] = &(pDevice->apTD1Rings[0]);
+
+ for (uu = 0; uu < TYPE_MAXTD; uu++)
+ pDevice->iTDUsed[uu] = 0;
+
+ for (uu = 0; uu < pDevice->sOpts.nTxDescs[0]; uu++) {
+ pCurrTD = &(pDevice->apTD0Rings[uu]);
+ pCurrTD->m_td0TD0.f1Owner = OWNED_BY_HOST;
+ /* init all Tx Packet pointer to NULL */
+ }
+ for (uu = 0; uu < pDevice->sOpts.nTxDescs[1]; uu++) {
+ pCurrTD = &(pDevice->apTD1Rings[uu]);
+ pCurrTD->m_td0TD0.f1Owner = OWNED_BY_HOST;
+ /* init all Tx Packet pointer to NULL */
+ }
+
+ /* set MAC TD pointer */
+ MACvSetCurrTXDescAddr(TYPE_TXDMA0, pDevice->PortOffset,
+ (pDevice->td0_pool_dma));
+
+ MACvSetCurrTXDescAddr(TYPE_AC0DMA, pDevice->PortOffset,
+ (pDevice->td1_pool_dma));
+
+ /* set MAC Beacon TX pointer */
+ MACvSetCurrBCNTxDescAddr(pDevice->PortOffset,
+ (pDevice->tx_beacon_dma));
+}
+
+/*
+ * Description:
+ * Reset Rx
+ *
+ * Parameters:
+ * In:
+ * pDevice - Pointer to the adapter
+ * Out:
+ * none
+ *
+ * Return Value: none
+ */
+void
+CARDvSafeResetRx(
+ struct vnt_private *pDevice
+)
+{
+ unsigned int uu;
+ PSRxDesc pDesc;
+
+ /* initialize RD index */
+ pDevice->pCurrRD[0] = &(pDevice->aRD0Ring[0]);
+ pDevice->pCurrRD[1] = &(pDevice->aRD1Ring[0]);
+
+ /* init state, all RD is chip's */
+ for (uu = 0; uu < pDevice->sOpts.nRxDescs0; uu++) {
+ pDesc = &(pDevice->aRD0Ring[uu]);
+ pDesc->m_rd0RD0.wResCount = (unsigned short)(pDevice->rx_buf_sz);
+ pDesc->m_rd0RD0.f1Owner = OWNED_BY_NIC;
+ pDesc->m_rd1RD1.wReqCount = (unsigned short)(pDevice->rx_buf_sz);
+ }
+
+ /* init state, all RD is chip's */
+ for (uu = 0; uu < pDevice->sOpts.nRxDescs1; uu++) {
+ pDesc = &(pDevice->aRD1Ring[uu]);
+ pDesc->m_rd0RD0.wResCount = (unsigned short)(pDevice->rx_buf_sz);
+ pDesc->m_rd0RD0.f1Owner = OWNED_BY_NIC;
+ pDesc->m_rd1RD1.wReqCount = (unsigned short)(pDevice->rx_buf_sz);
+ }
+
+ /* set perPkt mode */
+ MACvRx0PerPktMode(pDevice->PortOffset);
+ MACvRx1PerPktMode(pDevice->PortOffset);
+ /* set MAC RD pointer */
+ MACvSetCurrRx0DescAddr(pDevice->PortOffset,
+ pDevice->rd0_pool_dma);
+
+ MACvSetCurrRx1DescAddr(pDevice->PortOffset,
+ pDevice->rd1_pool_dma);
+}
+
+/*
+ * Description: Get response Control frame rate in CCK mode
+ *
+ * Parameters:
+ * In:
+ * pDevice - The adapter to be set
+ * wRateIdx - Receiving data rate
+ * Out:
+ * none
+ *
+ * Return Value: response Control frame rate
+ */
+static unsigned short CARDwGetCCKControlRate(struct vnt_private *pDevice,
+ unsigned short wRateIdx)
+{
+ unsigned int ui = (unsigned int) wRateIdx;
+
+ while (ui > RATE_1M) {
+ if (pDevice->basic_rates & ((u32)0x1 << ui))
+ return (unsigned short)ui;
+
+ ui--;
+ }
+ return (unsigned short)RATE_1M;
+}
+
+/*
+ * Description: Get response Control frame rate in OFDM mode
+ *
+ * Parameters:
+ * In:
+ * pDevice - The adapter to be set
+ * wRateIdx - Receiving data rate
+ * Out:
+ * none
+ *
+ * Return Value: response Control frame rate
+ */
+static unsigned short CARDwGetOFDMControlRate(struct vnt_private *pDevice,
+ unsigned short wRateIdx)
+{
+ unsigned int ui = (unsigned int) wRateIdx;
+
+ pr_debug("BASIC RATE: %X\n", pDevice->basic_rates);
+
+ if (!CARDbIsOFDMinBasicRate((void *)pDevice)) {
+ pr_debug("CARDwGetOFDMControlRate:(NO OFDM) %d\n", wRateIdx);
+ if (wRateIdx > RATE_24M)
+ wRateIdx = RATE_24M;
+ return wRateIdx;
+ }
+ while (ui > RATE_11M) {
+ if (pDevice->basic_rates & ((u32)0x1 << ui)) {
+ pr_debug("CARDwGetOFDMControlRate : %d\n", ui);
+ return (unsigned short)ui;
+ }
+ ui--;
+ }
+ pr_debug("CARDwGetOFDMControlRate: 6M\n");
+ return (unsigned short)RATE_24M;
+}
+
+/*
+ * Description: Set RSPINF
+ *
+ * Parameters:
+ * In:
+ * pDevice - The adapter to be set
+ * Out:
+ * none
+ *
+ * Return Value: None.
+ */
+void CARDvSetRSPINF(struct vnt_private *pDevice, u8 bb_type)
+{
+ union vnt_phy_field_swap phy;
+ unsigned char byTxRate, byRsvTime; /* For OFDM */
+ unsigned long flags;
+
+ spin_lock_irqsave(&pDevice->lock, flags);
+
+ /* Set to Page1 */
+ MACvSelectPage1(pDevice->PortOffset);
+
+ /* RSPINF_b_1 */
+ vnt_get_phy_field(pDevice, 14,
+ CARDwGetCCKControlRate(pDevice, RATE_1M),
+ PK_TYPE_11B, &phy.field_read);
+
+ /* swap over to get correct write order */
+ swap(phy.swap[0], phy.swap[1]);
+
+ VNSvOutPortD(pDevice->PortOffset + MAC_REG_RSPINF_B_1, phy.field_write);
+
+ /* RSPINF_b_2 */
+ vnt_get_phy_field(pDevice, 14,
+ CARDwGetCCKControlRate(pDevice, RATE_2M),
+ PK_TYPE_11B, &phy.field_read);
+
+ swap(phy.swap[0], phy.swap[1]);
+
+ VNSvOutPortD(pDevice->PortOffset + MAC_REG_RSPINF_B_2, phy.field_write);
+
+ /* RSPINF_b_5 */
+ vnt_get_phy_field(pDevice, 14,
+ CARDwGetCCKControlRate(pDevice, RATE_5M),
+ PK_TYPE_11B, &phy.field_read);
+
+ swap(phy.swap[0], phy.swap[1]);
+
+ VNSvOutPortD(pDevice->PortOffset + MAC_REG_RSPINF_B_5, phy.field_write);
+
+ /* RSPINF_b_11 */
+ vnt_get_phy_field(pDevice, 14,
+ CARDwGetCCKControlRate(pDevice, RATE_11M),
+ PK_TYPE_11B, &phy.field_read);
+
+ swap(phy.swap[0], phy.swap[1]);
+
+ VNSvOutPortD(pDevice->PortOffset + MAC_REG_RSPINF_B_11, phy.field_write);
+
+ /* RSPINF_a_6 */
+ s_vCalculateOFDMRParameter(RATE_6M,
+ bb_type,
+ &byTxRate,
+ &byRsvTime);
+ VNSvOutPortW(pDevice->PortOffset + MAC_REG_RSPINF_A_6, MAKEWORD(byTxRate, byRsvTime));
+ /* RSPINF_a_9 */
+ s_vCalculateOFDMRParameter(RATE_9M,
+ bb_type,
+ &byTxRate,
+ &byRsvTime);
+ VNSvOutPortW(pDevice->PortOffset + MAC_REG_RSPINF_A_9, MAKEWORD(byTxRate, byRsvTime));
+ /* RSPINF_a_12 */
+ s_vCalculateOFDMRParameter(RATE_12M,
+ bb_type,
+ &byTxRate,
+ &byRsvTime);
+ VNSvOutPortW(pDevice->PortOffset + MAC_REG_RSPINF_A_12, MAKEWORD(byTxRate, byRsvTime));
+ /* RSPINF_a_18 */
+ s_vCalculateOFDMRParameter(RATE_18M,
+ bb_type,
+ &byTxRate,
+ &byRsvTime);
+ VNSvOutPortW(pDevice->PortOffset + MAC_REG_RSPINF_A_18, MAKEWORD(byTxRate, byRsvTime));
+ /* RSPINF_a_24 */
+ s_vCalculateOFDMRParameter(RATE_24M,
+ bb_type,
+ &byTxRate,
+ &byRsvTime);
+ VNSvOutPortW(pDevice->PortOffset + MAC_REG_RSPINF_A_24, MAKEWORD(byTxRate, byRsvTime));
+ /* RSPINF_a_36 */
+ s_vCalculateOFDMRParameter(CARDwGetOFDMControlRate((void *)pDevice, RATE_36M),
+ bb_type,
+ &byTxRate,
+ &byRsvTime);
+ VNSvOutPortW(pDevice->PortOffset + MAC_REG_RSPINF_A_36, MAKEWORD(byTxRate, byRsvTime));
+ /* RSPINF_a_48 */
+ s_vCalculateOFDMRParameter(CARDwGetOFDMControlRate((void *)pDevice, RATE_48M),
+ bb_type,
+ &byTxRate,
+ &byRsvTime);
+ VNSvOutPortW(pDevice->PortOffset + MAC_REG_RSPINF_A_48, MAKEWORD(byTxRate, byRsvTime));
+ /* RSPINF_a_54 */
+ s_vCalculateOFDMRParameter(CARDwGetOFDMControlRate((void *)pDevice, RATE_54M),
+ bb_type,
+ &byTxRate,
+ &byRsvTime);
+ VNSvOutPortW(pDevice->PortOffset + MAC_REG_RSPINF_A_54, MAKEWORD(byTxRate, byRsvTime));
+ /* RSPINF_a_72 */
+ s_vCalculateOFDMRParameter(CARDwGetOFDMControlRate((void *)pDevice, RATE_54M),
+ bb_type,
+ &byTxRate,
+ &byRsvTime);
+ VNSvOutPortW(pDevice->PortOffset + MAC_REG_RSPINF_A_72, MAKEWORD(byTxRate, byRsvTime));
+ /* Set to Page0 */
+ MACvSelectPage0(pDevice->PortOffset);
+
+ spin_unlock_irqrestore(&pDevice->lock, flags);
+}
+
+void CARDvUpdateBasicTopRate(struct vnt_private *pDevice)
+{
+ unsigned char byTopOFDM = RATE_24M, byTopCCK = RATE_1M;
+ unsigned char ii;
+
+ /* Determines the highest basic rate. */
+ for (ii = RATE_54M; ii >= RATE_6M; ii--) {
+ if ((pDevice->basic_rates) & ((u32)(1 << ii))) {
+ byTopOFDM = ii;
+ break;
+ }
+ }
+ pDevice->byTopOFDMBasicRate = byTopOFDM;
+
+ for (ii = RATE_11M;; ii--) {
+ if ((pDevice->basic_rates) & ((u32)(1 << ii))) {
+ byTopCCK = ii;
+ break;
+ }
+ if (ii == RATE_1M)
+ break;
+ }
+ pDevice->byTopCCKBasicRate = byTopCCK;
+}
+
+bool CARDbIsOFDMinBasicRate(struct vnt_private *pDevice)
+{
+ int ii;
+
+ for (ii = RATE_54M; ii >= RATE_6M; ii--) {
+ if ((pDevice->basic_rates) & ((u32)(1 << ii)))
+ return true;
+ }
+ return false;
+}
+
+unsigned char CARDbyGetPktType(struct vnt_private *pDevice)
+{
+
+ if (pDevice->byBBType == BB_TYPE_11A || pDevice->byBBType == BB_TYPE_11B)
+ return (unsigned char)pDevice->byBBType;
+ else if (CARDbIsOFDMinBasicRate((void *)pDevice))
+ return PK_TYPE_11GA;
+ else
+ return PK_TYPE_11GB;
+}
+
+/*
+ * Description: Set NIC Loopback mode
+ *
+ * Parameters:
+ * In:
+ * pDevice - The adapter to be set
+ * wLoopbackMode - Loopback mode to be set
+ * Out:
+ * none
+ *
+ * Return Value: none
+ */
+void CARDvSetLoopbackMode(struct vnt_private *priv, unsigned short wLoopbackMode)
+{
+ void __iomem *dwIoBase = priv->PortOffset;
+
+ switch (wLoopbackMode) {
+ case CARD_LB_NONE:
+ case CARD_LB_MAC:
+ case CARD_LB_PHY:
+ break;
+ default:
+ ASSERT(false);
+ break;
+ }
+ /* set MAC loopback */
+ MACvSetLoopbackMode(dwIoBase, LOBYTE(wLoopbackMode));
+ /* set Baseband loopback */
+}
+
+/*
+ * Description: Software Reset NIC
+ *
+ * Parameters:
+ * In:
+ * pDevice - The adapter to be reset
+ * Out:
+ * none
+ *
+ * Return Value: none
+ */
+bool CARDbSoftwareReset(struct vnt_private *pDevice)
+{
+
+ /* reset MAC */
+ if (!MACbSafeSoftwareReset(pDevice->PortOffset))
+ return false;
+
+ return true;
+}
+
+/*
+ * Description: Calculate TSF offset of two TSF input
+ * Get TSF Offset from RxBCN's TSF and local TSF
+ *
+ * Parameters:
+ * In:
+ * pDevice - The adapter to be sync.
+ * qwTSF1 - Rx BCN's TSF
+ * qwTSF2 - Local TSF
+ * Out:
+ * none
+ *
+ * Return Value: TSF Offset value
+ */
+u64 CARDqGetTSFOffset(unsigned char byRxRate, u64 qwTSF1, u64 qwTSF2)
+{
+ u64 qwTSFOffset = 0;
+ unsigned short wRxBcnTSFOffst = 0;
+
+ wRxBcnTSFOffst = cwRXBCNTSFOff[byRxRate%MAX_RATE];
+
+ qwTSF2 += (u64)wRxBcnTSFOffst;
+
+ qwTSFOffset = qwTSF1 - qwTSF2;
+
+ return qwTSFOffset;
+}
+
+/*
+ * Description: Read NIC TSF counter
+ * Get local TSF counter
+ *
+ * Parameters:
+ * In:
+ * pDevice - The adapter to be read
+ * Out:
+ * qwCurrTSF - Current TSF counter
+ *
+ * Return Value: true if success; otherwise false
+ */
+bool CARDbGetCurrentTSF(struct vnt_private *priv, u64 *pqwCurrTSF)
+{
+ void __iomem *dwIoBase = priv->PortOffset;
+ unsigned short ww;
+ unsigned char byData;
+
+ MACvRegBitsOn(dwIoBase, MAC_REG_TFTCTL, TFTCTL_TSFCNTRRD);
+ for (ww = 0; ww < W_MAX_TIMEOUT; ww++) {
+ VNSvInPortB(dwIoBase + MAC_REG_TFTCTL, &byData);
+ if (!(byData & TFTCTL_TSFCNTRRD))
+ break;
+ }
+ if (ww == W_MAX_TIMEOUT)
+ return false;
+ VNSvInPortD(dwIoBase + MAC_REG_TSFCNTR, (u32 *)pqwCurrTSF);
+ VNSvInPortD(dwIoBase + MAC_REG_TSFCNTR + 4, (u32 *)pqwCurrTSF + 1);
+
+ return true;
+}
+
+/*
+ * Description: Read NIC TSF counter
+ * Get NEXTTBTT from adjusted TSF and Beacon Interval
+ *
+ * Parameters:
+ * In:
+ * qwTSF - Current TSF counter
+ * wbeaconInterval - Beacon Interval
+ * Out:
+ * qwCurrTSF - Current TSF counter
+ *
+ * Return Value: TSF value of next Beacon
+ */
+u64 CARDqGetNextTBTT(u64 qwTSF, unsigned short wBeaconInterval)
+{
+ u32 beacon_int;
+
+ beacon_int = wBeaconInterval * 1024;
+ if (beacon_int) {
+ do_div(qwTSF, beacon_int);
+ qwTSF += 1;
+ qwTSF *= beacon_int;
+ }
+
+ return qwTSF;
+}
+
+/*
+ * Description: Set NIC TSF counter for first Beacon time
+ * Get NEXTTBTT from adjusted TSF and Beacon Interval
+ *
+ * Parameters:
+ * In:
+ * dwIoBase - IO Base
+ * wBeaconInterval - Beacon Interval
+ * Out:
+ * none
+ *
+ * Return Value: none
+ */
+void CARDvSetFirstNextTBTT(struct vnt_private *priv, unsigned short wBeaconInterval)
+{
+ void __iomem *dwIoBase = priv->PortOffset;
+ u64 qwNextTBTT = 0;
+
+ CARDbGetCurrentTSF(priv, &qwNextTBTT); /* Get Local TSF counter */
+
+ qwNextTBTT = CARDqGetNextTBTT(qwNextTBTT, wBeaconInterval);
+ /* Set NextTBTT */
+ VNSvOutPortD(dwIoBase + MAC_REG_NEXTTBTT, (u32)qwNextTBTT);
+ VNSvOutPortD(dwIoBase + MAC_REG_NEXTTBTT + 4, (u32)(qwNextTBTT >> 32));
+ MACvRegBitsOn(dwIoBase, MAC_REG_TFTCTL, TFTCTL_TBTTSYNCEN);
+}
+
+/*
+ * Description: Sync NIC TSF counter for Beacon time
+ * Get NEXTTBTT and write to HW
+ *
+ * Parameters:
+ * In:
+ * pDevice - The adapter to be set
+ * qwTSF - Current TSF counter
+ * wBeaconInterval - Beacon Interval
+ * Out:
+ * none
+ *
+ * Return Value: none
+ */
+void CARDvUpdateNextTBTT(struct vnt_private *priv, u64 qwTSF, unsigned short wBeaconInterval)
+{
+ void __iomem *dwIoBase = priv->PortOffset;
+
+ qwTSF = CARDqGetNextTBTT(qwTSF, wBeaconInterval);
+ /* Set NextTBTT */
+ VNSvOutPortD(dwIoBase + MAC_REG_NEXTTBTT, (u32)qwTSF);
+ VNSvOutPortD(dwIoBase + MAC_REG_NEXTTBTT + 4, (u32)(qwTSF >> 32));
+ MACvRegBitsOn(dwIoBase, MAC_REG_TFTCTL, TFTCTL_TBTTSYNCEN);
+ pr_debug("Card:Update Next TBTT[%8llx]\n", qwTSF);
+}
diff --git a/drivers/staging/vt6655/card.h b/drivers/staging/vt6655/card.h
new file mode 100644
index 000000000..16cca49e6
--- /dev/null
+++ b/drivers/staging/vt6655/card.h
@@ -0,0 +1,89 @@
+/*
+ * Copyright (c) 1996, 2003 VIA Networking Technologies, Inc.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * File: card.h
+ *
+ * Purpose: Provide functions to setup NIC operation mode
+ *
+ * Author: Tevin Chen
+ *
+ * Date: May 21, 1996
+ *
+ */
+
+#ifndef __CARD_H__
+#define __CARD_H__
+
+#include <linux/types.h>
+#include <linux/nl80211.h>
+
+/*
+ * Loopback mode
+ *
+ * LOBYTE is MAC LB mode, HIBYTE is MII LB mode
+ */
+#define CARD_LB_NONE MAKEWORD(MAC_LB_NONE, 0)
+#define CARD_LB_MAC MAKEWORD(MAC_LB_INTERNAL, 0) /* PHY must ISO, avoid MAC loopback packet go out */
+#define CARD_LB_PHY MAKEWORD(MAC_LB_EXT, 0)
+
+#define DEFAULT_MSDU_LIFETIME 512 /* ms */
+#define DEFAULT_MSDU_LIFETIME_RES_64us 8000 /* 64us */
+
+#define DEFAULT_MGN_LIFETIME 8 /* ms */
+#define DEFAULT_MGN_LIFETIME_RES_64us 125 /* 64us */
+
+#define CB_MAX_CHANNEL_24G 14
+#define CB_MAX_CHANNEL_5G 42
+#define CB_MAX_CHANNEL (CB_MAX_CHANNEL_24G+CB_MAX_CHANNEL_5G)
+
+typedef enum _CARD_PKT_TYPE {
+ PKT_TYPE_802_11_BCN,
+ PKT_TYPE_802_11_MNG,
+ PKT_TYPE_802_11_DATA,
+ PKT_TYPE_802_11_ALL
+} CARD_PKT_TYPE, *PCARD_PKT_TYPE;
+
+typedef enum _CARD_STATUS_TYPE {
+ CARD_STATUS_MEDIA_CONNECT,
+ CARD_STATUS_MEDIA_DISCONNECT,
+ CARD_STATUS_PMKID
+} CARD_STATUS_TYPE, *PCARD_STATUS_TYPE;
+
+struct vnt_private;
+
+void CARDvSetRSPINF(struct vnt_private *, u8);
+void CARDvUpdateBasicTopRate(struct vnt_private *);
+bool CARDbIsOFDMinBasicRate(struct vnt_private *);
+void CARDvSetLoopbackMode(struct vnt_private *, unsigned short wLoopbackMode);
+bool CARDbSoftwareReset(struct vnt_private *);
+void CARDvSetFirstNextTBTT(struct vnt_private *, unsigned short wBeaconInterval);
+void CARDvUpdateNextTBTT(struct vnt_private *, u64 qwTSF, unsigned short wBeaconInterval);
+bool CARDbGetCurrentTSF(struct vnt_private *, u64 *pqwCurrTSF);
+u64 CARDqGetNextTBTT(u64 qwTSF, unsigned short wBeaconInterval);
+u64 CARDqGetTSFOffset(unsigned char byRxRate, u64 qwTSF1, u64 qwTSF2);
+unsigned char CARDbyGetPktType(struct vnt_private *);
+void CARDvSafeResetTx(struct vnt_private *);
+void CARDvSafeResetRx(struct vnt_private *);
+bool CARDbRadioPowerOff(struct vnt_private *);
+bool CARDbRadioPowerOn(struct vnt_private *);
+bool CARDbSetPhyParameter(struct vnt_private *, u8);
+bool CARDbUpdateTSF(struct vnt_private *, unsigned char byRxRate,
+ u64 qwBSSTimestamp);
+bool CARDbSetBeaconPeriod(struct vnt_private *, unsigned short wBeaconInterval);
+
+#endif /* __CARD_H__ */
diff --git a/drivers/staging/vt6655/channel.c b/drivers/staging/vt6655/channel.c
new file mode 100644
index 000000000..7a717828f
--- /dev/null
+++ b/drivers/staging/vt6655/channel.c
@@ -0,0 +1,237 @@
+/*
+ * Copyright (c) 1996, 2003 VIA Networking Technologies, Inc.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * File: channel.c
+ *
+ */
+
+#include "baseband.h"
+#include "channel.h"
+#include "device.h"
+#include "rf.h"
+
+static struct ieee80211_rate vnt_rates_bg[] = {
+ { .bitrate = 10, .hw_value = RATE_1M },
+ { .bitrate = 20, .hw_value = RATE_2M },
+ { .bitrate = 55, .hw_value = RATE_5M },
+ { .bitrate = 110, .hw_value = RATE_11M },
+ { .bitrate = 60, .hw_value = RATE_6M },
+ { .bitrate = 90, .hw_value = RATE_9M },
+ { .bitrate = 120, .hw_value = RATE_12M },
+ { .bitrate = 180, .hw_value = RATE_18M },
+ { .bitrate = 240, .hw_value = RATE_24M },
+ { .bitrate = 360, .hw_value = RATE_36M },
+ { .bitrate = 480, .hw_value = RATE_48M },
+ { .bitrate = 540, .hw_value = RATE_54M },
+};
+
+static struct ieee80211_rate vnt_rates_a[] = {
+ { .bitrate = 60, .hw_value = RATE_6M },
+ { .bitrate = 90, .hw_value = RATE_9M },
+ { .bitrate = 120, .hw_value = RATE_12M },
+ { .bitrate = 180, .hw_value = RATE_18M },
+ { .bitrate = 240, .hw_value = RATE_24M },
+ { .bitrate = 360, .hw_value = RATE_36M },
+ { .bitrate = 480, .hw_value = RATE_48M },
+ { .bitrate = 540, .hw_value = RATE_54M },
+};
+
+static struct ieee80211_channel vnt_channels_2ghz[] = {
+ { .center_freq = 2412, .hw_value = 1 },
+ { .center_freq = 2417, .hw_value = 2 },
+ { .center_freq = 2422, .hw_value = 3 },
+ { .center_freq = 2427, .hw_value = 4 },
+ { .center_freq = 2432, .hw_value = 5 },
+ { .center_freq = 2437, .hw_value = 6 },
+ { .center_freq = 2442, .hw_value = 7 },
+ { .center_freq = 2447, .hw_value = 8 },
+ { .center_freq = 2452, .hw_value = 9 },
+ { .center_freq = 2457, .hw_value = 10 },
+ { .center_freq = 2462, .hw_value = 11 },
+ { .center_freq = 2467, .hw_value = 12 },
+ { .center_freq = 2472, .hw_value = 13 },
+ { .center_freq = 2484, .hw_value = 14 }
+};
+
+static struct ieee80211_channel vnt_channels_5ghz[] = {
+ { .center_freq = 4915, .hw_value = 15 },
+ { .center_freq = 4920, .hw_value = 16 },
+ { .center_freq = 4925, .hw_value = 17 },
+ { .center_freq = 4935, .hw_value = 18 },
+ { .center_freq = 4940, .hw_value = 19 },
+ { .center_freq = 4945, .hw_value = 20 },
+ { .center_freq = 4960, .hw_value = 21 },
+ { .center_freq = 4980, .hw_value = 22 },
+ { .center_freq = 5035, .hw_value = 23 },
+ { .center_freq = 5040, .hw_value = 24 },
+ { .center_freq = 5045, .hw_value = 25 },
+ { .center_freq = 5055, .hw_value = 26 },
+ { .center_freq = 5060, .hw_value = 27 },
+ { .center_freq = 5080, .hw_value = 28 },
+ { .center_freq = 5170, .hw_value = 29 },
+ { .center_freq = 5180, .hw_value = 30 },
+ { .center_freq = 5190, .hw_value = 31 },
+ { .center_freq = 5200, .hw_value = 32 },
+ { .center_freq = 5210, .hw_value = 33 },
+ { .center_freq = 5220, .hw_value = 34 },
+ { .center_freq = 5230, .hw_value = 35 },
+ { .center_freq = 5240, .hw_value = 36 },
+ { .center_freq = 5260, .hw_value = 37 },
+ { .center_freq = 5280, .hw_value = 38 },
+ { .center_freq = 5300, .hw_value = 39 },
+ { .center_freq = 5320, .hw_value = 40 },
+ { .center_freq = 5500, .hw_value = 41 },
+ { .center_freq = 5520, .hw_value = 42 },
+ { .center_freq = 5540, .hw_value = 43 },
+ { .center_freq = 5560, .hw_value = 44 },
+ { .center_freq = 5580, .hw_value = 45 },
+ { .center_freq = 5600, .hw_value = 46 },
+ { .center_freq = 5620, .hw_value = 47 },
+ { .center_freq = 5640, .hw_value = 48 },
+ { .center_freq = 5660, .hw_value = 49 },
+ { .center_freq = 5680, .hw_value = 50 },
+ { .center_freq = 5700, .hw_value = 51 },
+ { .center_freq = 5745, .hw_value = 52 },
+ { .center_freq = 5765, .hw_value = 53 },
+ { .center_freq = 5785, .hw_value = 54 },
+ { .center_freq = 5805, .hw_value = 55 },
+ { .center_freq = 5825, .hw_value = 56 }
+};
+
+static struct ieee80211_supported_band vnt_supported_2ghz_band = {
+ .channels = vnt_channels_2ghz,
+ .n_channels = ARRAY_SIZE(vnt_channels_2ghz),
+ .bitrates = vnt_rates_bg,
+ .n_bitrates = ARRAY_SIZE(vnt_rates_bg),
+};
+
+static struct ieee80211_supported_band vnt_supported_5ghz_band = {
+ .channels = vnt_channels_5ghz,
+ .n_channels = ARRAY_SIZE(vnt_channels_5ghz),
+ .bitrates = vnt_rates_a,
+ .n_bitrates = ARRAY_SIZE(vnt_rates_a),
+};
+
+void vnt_init_bands(struct vnt_private *priv)
+{
+ struct ieee80211_channel *ch;
+ int i;
+
+ switch (priv->byRFType) {
+ case RF_AIROHA7230:
+ case RF_UW2452:
+ case RF_NOTHING:
+ default:
+ ch = vnt_channels_5ghz;
+
+ for (i = 0; i < ARRAY_SIZE(vnt_channels_5ghz); i++) {
+ ch[i].max_power = 0x3f;
+ ch[i].flags = IEEE80211_CHAN_NO_HT40;
+ }
+
+ priv->hw->wiphy->bands[IEEE80211_BAND_5GHZ] =
+ &vnt_supported_5ghz_band;
+ /* fallthrough */
+ case RF_RFMD2959:
+ case RF_AIROHA:
+ case RF_AL2230S:
+ case RF_UW2451:
+ case RF_VT3226:
+ ch = vnt_channels_2ghz;
+
+ for (i = 0; i < ARRAY_SIZE(vnt_channels_2ghz); i++) {
+ ch[i].max_power = 0x3f;
+ ch[i].flags = IEEE80211_CHAN_NO_HT40;
+ }
+
+ priv->hw->wiphy->bands[IEEE80211_BAND_2GHZ] =
+ &vnt_supported_2ghz_band;
+ break;
+ }
+}
+
+/**
+ * set_channel() - Set NIC media channel
+ *
+ * @pDeviceHandler: The adapter to be set
+ * @uConnectionChannel: Channel to be set
+ *
+ * Return Value: true if succeeded; false if failed.
+ *
+ */
+bool set_channel(void *pDeviceHandler, struct ieee80211_channel *ch)
+{
+ struct vnt_private *pDevice = pDeviceHandler;
+ bool bResult = true;
+
+ if (pDevice->byCurrentCh == ch->hw_value)
+ return bResult;
+
+ /* Set VGA to max sensitivity */
+ if (pDevice->bUpdateBBVGA &&
+ pDevice->byBBVGACurrent != pDevice->abyBBVGA[0]) {
+ pDevice->byBBVGACurrent = pDevice->abyBBVGA[0];
+
+ BBvSetVGAGainOffset(pDevice, pDevice->byBBVGACurrent);
+ }
+
+ /* clear NAV */
+ MACvRegBitsOn(pDevice->PortOffset, MAC_REG_MACCR, MACCR_CLRNAV);
+
+ /* TX_PE will reserve 3 us for MAX2829 A mode only,
+ it is for better TX throughput */
+
+ if (pDevice->byRFType == RF_AIROHA7230)
+ RFbAL7230SelectChannelPostProcess(pDevice, pDevice->byCurrentCh,
+ ch->hw_value);
+
+ pDevice->byCurrentCh = ch->hw_value;
+ bResult &= RFbSelectChannel(pDevice, pDevice->byRFType,
+ ch->hw_value);
+
+ /* Init Synthesizer Table */
+ if (pDevice->bEnablePSMode)
+ RFvWriteWakeProgSyn(pDevice, pDevice->byRFType, ch->hw_value);
+
+ BBvSoftwareReset(pDevice);
+
+ if (pDevice->byLocalID > REV_ID_VT3253_B1) {
+ unsigned long flags;
+
+ spin_lock_irqsave(&pDevice->lock, flags);
+
+ /* set HW default power register */
+ MACvSelectPage1(pDevice->PortOffset);
+ RFbSetPower(pDevice, RATE_1M, pDevice->byCurrentCh);
+ VNSvOutPortB(pDevice->PortOffset + MAC_REG_PWRCCK,
+ pDevice->byCurPwr);
+ RFbSetPower(pDevice, RATE_6M, pDevice->byCurrentCh);
+ VNSvOutPortB(pDevice->PortOffset + MAC_REG_PWROFDM,
+ pDevice->byCurPwr);
+ MACvSelectPage0(pDevice->PortOffset);
+
+ spin_unlock_irqrestore(&pDevice->lock, flags);
+ }
+
+ if (pDevice->byBBType == BB_TYPE_11B)
+ RFbSetPower(pDevice, RATE_1M, pDevice->byCurrentCh);
+ else
+ RFbSetPower(pDevice, RATE_6M, pDevice->byCurrentCh);
+
+ return bResult;
+}
diff --git a/drivers/staging/vt6655/channel.h b/drivers/staging/vt6655/channel.h
new file mode 100644
index 000000000..e2be6fca5
--- /dev/null
+++ b/drivers/staging/vt6655/channel.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 1996, 2003 VIA Networking Technologies, Inc.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * File: channel.h
+ *
+ */
+
+#ifndef _CHANNEL_H_
+#define _CHANNEL_H_
+
+#include "card.h"
+
+void vnt_init_bands(struct vnt_private *);
+
+bool set_channel(void *pDeviceHandler, struct ieee80211_channel *);
+
+#endif /* _CHANNEL_H_ */
diff --git a/drivers/staging/vt6655/desc.h b/drivers/staging/vt6655/desc.h
new file mode 100644
index 000000000..758eeb2af
--- /dev/null
+++ b/drivers/staging/vt6655/desc.h
@@ -0,0 +1,351 @@
+/*
+ * Copyright (c) 1996, 2003 VIA Networking Technologies, Inc.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * File: desc.h
+ *
+ * Purpose:The header file of descriptor
+ *
+ * Revision History:
+ *
+ * Author: Tevin Chen
+ *
+ * Date: May 21, 1996
+ *
+ */
+
+#ifndef __DESC_H__
+#define __DESC_H__
+
+#include <linux/types.h>
+#include <linux/mm.h>
+#include "linux/ieee80211.h"
+
+#define B_OWNED_BY_CHIP 1
+#define B_OWNED_BY_HOST 0
+
+/* Bits in the RSR register */
+#define RSR_ADDRBROAD 0x80
+#define RSR_ADDRMULTI 0x40
+#define RSR_ADDRUNI 0x00
+#define RSR_IVLDTYP 0x20
+#define RSR_IVLDLEN 0x10 /* invalid len (> 2312 byte) */
+#define RSR_BSSIDOK 0x08
+#define RSR_CRCOK 0x04
+#define RSR_BCNSSIDOK 0x02
+#define RSR_ADDROK 0x01
+
+/* Bits in the new RSR register */
+#define NEWRSR_DECRYPTOK 0x10
+#define NEWRSR_CFPIND 0x08
+#define NEWRSR_HWUTSF 0x04
+#define NEWRSR_BCNHITAID 0x02
+#define NEWRSR_BCNHITAID0 0x01
+
+/* Bits in the TSR0 register */
+#define TSR0_PWRSTS1_2 0xC0
+#define TSR0_PWRSTS7 0x20
+#define TSR0_NCR 0x1F
+
+/* Bits in the TSR1 register */
+#define TSR1_TERR 0x80
+#define TSR1_PWRSTS4_6 0x70
+#define TSR1_RETRYTMO 0x08
+#define TSR1_TMO 0x04
+#define TSR1_PWRSTS3 0x02
+#define ACK_DATA 0x01
+
+/* Bits in the TCR register */
+#define EDMSDU 0x04 /* end of sdu */
+#define TCR_EDP 0x02 /* end of packet */
+#define TCR_STP 0x01 /* start of packet */
+
+/* max transmit or receive buffer size */
+#define CB_MAX_BUF_SIZE 2900U
+ /* NOTE: must be multiple of 4 */
+#define CB_MAX_TX_BUF_SIZE CB_MAX_BUF_SIZE
+#define CB_MAX_RX_BUF_SIZE_NORMAL CB_MAX_BUF_SIZE
+
+#define CB_BEACON_BUF_SIZE 512U
+
+#define CB_MAX_RX_DESC 128
+#define CB_MIN_RX_DESC 16
+#define CB_MAX_TX_DESC 64
+#define CB_MIN_TX_DESC 16
+
+#define CB_MAX_RECEIVED_PACKETS 16
+ /*
+ * limit our receive routine to indicating
+ * this many at a time for 2 reasons:
+ * 1. driver flow control to protocol layer
+ * 2. limit the time used in ISR routine
+ */
+
+#define CB_EXTRA_RD_NUM 32
+#define CB_RD_NUM 32
+#define CB_TD_NUM 32
+
+/*
+ * max number of physical segments in a single NDIS packet. Above this
+ * threshold, the packet is copied into a single physically contiguous buffer
+ */
+#define CB_MAX_SEGMENT 4
+
+#define CB_MIN_MAP_REG_NUM 4
+#define CB_MAX_MAP_REG_NUM CB_MAX_TX_DESC
+
+#define CB_PROTOCOL_RESERVED_SECTION 16
+
+/*
+ * if retrys excess 15 times , tx will abort, and if tx fifo underflow,
+ * tx will fail, we should try to resend it
+ */
+#define CB_MAX_TX_ABORT_RETRY 3
+
+/* WMAC definition FIFO Control */
+#define FIFOCTL_AUTO_FB_1 0x1000
+#define FIFOCTL_AUTO_FB_0 0x0800
+#define FIFOCTL_GRPACK 0x0400
+#define FIFOCTL_11GA 0x0300
+#define FIFOCTL_11GB 0x0200
+#define FIFOCTL_11B 0x0100
+#define FIFOCTL_11A 0x0000
+#define FIFOCTL_RTS 0x0080
+#define FIFOCTL_ISDMA0 0x0040
+#define FIFOCTL_GENINT 0x0020
+#define FIFOCTL_TMOEN 0x0010
+#define FIFOCTL_LRETRY 0x0008
+#define FIFOCTL_CRCDIS 0x0004
+#define FIFOCTL_NEEDACK 0x0002
+#define FIFOCTL_LHEAD 0x0001
+
+/* WMAC definition Frag Control */
+#define FRAGCTL_AES 0x0300
+#define FRAGCTL_TKIP 0x0200
+#define FRAGCTL_LEGACY 0x0100
+#define FRAGCTL_NONENCRYPT 0x0000
+#define FRAGCTL_ENDFRAG 0x0003
+#define FRAGCTL_MIDFRAG 0x0002
+#define FRAGCTL_STAFRAG 0x0001
+#define FRAGCTL_NONFRAG 0x0000
+
+#define TYPE_TXDMA0 0
+#define TYPE_AC0DMA 1
+#define TYPE_ATIMDMA 2
+#define TYPE_SYNCDMA 3
+#define TYPE_MAXTD 2
+
+#define TYPE_BEACONDMA 4
+
+#define TYPE_RXDMA0 0
+#define TYPE_RXDMA1 1
+#define TYPE_MAXRD 2
+
+/* TD_INFO flags control bit */
+#define TD_FLAGS_NETIF_SKB 0x01 /* check if need release skb */
+#define TD_FLAGS_PRIV_SKB 0x02 /* check if called from private skb (hostap) */
+#define TD_FLAGS_PS_RETRY 0x04 /* check if PS STA frame re-transmit */
+
+/*
+ * ref_sk_buff is used for mapping the skb structure between pre-built
+ * driver-obj & running kernel. Since different kernel version (2.4x) may
+ * change skb structure, i.e. pre-built driver-obj may link to older skb that
+ * leads error.
+ */
+
+typedef struct tagDEVICE_RD_INFO {
+ struct sk_buff *skb;
+ dma_addr_t skb_dma;
+ dma_addr_t curr_desc;
+} DEVICE_RD_INFO, *PDEVICE_RD_INFO;
+
+#ifdef __BIG_ENDIAN
+
+typedef struct tagRDES0 {
+ volatile unsigned short wResCount;
+ union {
+ volatile u16 f15Reserved;
+ struct {
+ volatile u8 f8Reserved1;
+ volatile u8 f1Owner:1;
+ volatile u8 f7Reserved:7;
+ } __attribute__ ((__packed__));
+ } __attribute__ ((__packed__));
+} __attribute__ ((__packed__))
+SRDES0, *PSRDES0;
+
+#else
+
+typedef struct tagRDES0 {
+ unsigned short wResCount;
+ unsigned short f15Reserved:15;
+ unsigned short f1Owner:1;
+} __attribute__ ((__packed__))
+SRDES0;
+
+#endif
+
+typedef struct tagRDES1 {
+ unsigned short wReqCount;
+ unsigned short wReserved;
+} __attribute__ ((__packed__))
+SRDES1;
+
+/* Rx descriptor*/
+typedef struct tagSRxDesc {
+ volatile SRDES0 m_rd0RD0;
+ volatile SRDES1 m_rd1RD1;
+ volatile u32 buff_addr;
+ volatile u32 next_desc;
+ struct tagSRxDesc *next __aligned(8);
+ volatile PDEVICE_RD_INFO pRDInfo __aligned(8);
+} __attribute__ ((__packed__))
+SRxDesc, *PSRxDesc;
+typedef const SRxDesc *PCSRxDesc;
+
+#ifdef __BIG_ENDIAN
+
+typedef struct tagTDES0 {
+ volatile unsigned char byTSR0;
+ volatile unsigned char byTSR1;
+ union {
+ volatile u16 f15Txtime;
+ struct {
+ volatile u8 f8Reserved1;
+ volatile u8 f1Owner:1;
+ volatile u8 f7Reserved:7;
+ } __attribute__ ((__packed__));
+ } __attribute__ ((__packed__));
+} __attribute__ ((__packed__))
+STDES0, PSTDES0;
+
+#else
+
+typedef struct tagTDES0 {
+ volatile unsigned char byTSR0;
+ volatile unsigned char byTSR1;
+ volatile unsigned short f15Txtime:15;
+ volatile unsigned short f1Owner:1;
+} __attribute__ ((__packed__))
+STDES0;
+
+#endif
+
+typedef struct tagTDES1 {
+ volatile unsigned short wReqCount;
+ volatile unsigned char byTCR;
+ volatile unsigned char byReserved;
+} __attribute__ ((__packed__))
+STDES1;
+
+typedef struct tagDEVICE_TD_INFO {
+ void *mic_hdr;
+ struct sk_buff *skb;
+ unsigned char *buf;
+ dma_addr_t skb_dma;
+ dma_addr_t buf_dma;
+ dma_addr_t curr_desc;
+ unsigned long dwReqCount;
+ unsigned long dwHeaderLength;
+ unsigned char byFlags;
+} DEVICE_TD_INFO, *PDEVICE_TD_INFO;
+
+/* transmit descriptor */
+typedef struct tagSTxDesc {
+ volatile STDES0 m_td0TD0;
+ volatile STDES1 m_td1TD1;
+ volatile u32 buff_addr;
+ volatile u32 next_desc;
+ struct tagSTxDesc *next __aligned(8);
+ volatile PDEVICE_TD_INFO pTDInfo __aligned(8);
+} __attribute__ ((__packed__))
+STxDesc, *PSTxDesc;
+typedef const STxDesc *PCSTxDesc;
+
+typedef struct tagSTxSyncDesc {
+ volatile STDES0 m_td0TD0;
+ volatile STDES1 m_td1TD1;
+ volatile u32 buff_addr; /* pointer to logical buffer */
+ volatile u32 next_desc; /* pointer to next logical descriptor */
+ volatile unsigned short m_wFIFOCtl;
+ volatile unsigned short m_wTimeStamp;
+ struct tagSTxSyncDesc *next __aligned(8);
+ volatile PDEVICE_TD_INFO pTDInfo __aligned(8);
+} __attribute__ ((__packed__))
+STxSyncDesc, *PSTxSyncDesc;
+typedef const STxSyncDesc *PCSTxSyncDesc;
+
+/* RsvTime buffer header */
+typedef struct tagSRrvTime_atim {
+ unsigned short wCTSTxRrvTime_ba;
+ unsigned short wTxRrvTime_a;
+} __attribute__ ((__packed__))
+SRrvTime_atim, *PSRrvTime_atim;
+typedef const SRrvTime_atim *PCSRrvTime_atim;
+
+/* Length, Service, and Signal fields of Phy for Tx */
+struct vnt_phy_field {
+ u8 signal;
+ u8 service;
+ __le16 len;
+} __packed;
+
+union vnt_phy_field_swap {
+ struct vnt_phy_field field_read;
+ u16 swap[2];
+ u32 field_write;
+};
+
+/* Tx FIFO header */
+typedef struct tagSTxBufHead {
+ u32 adwTxKey[4];
+ unsigned short wFIFOCtl;
+ unsigned short wTimeStamp;
+ unsigned short wFragCtl;
+ unsigned char byTxPower;
+ unsigned char wReserved;
+} __attribute__ ((__packed__))
+STxBufHead, *PSTxBufHead;
+typedef const STxBufHead *PCSTxBufHead;
+
+typedef struct tagSBEACONCtl {
+ u32 BufReady:1;
+ u32 TSF:15;
+ u32 BufLen:11;
+ u32 Reserved:5;
+} __attribute__ ((__packed__))
+SBEACONCtl;
+
+typedef struct tagSSecretKey {
+ u32 dwLowDword;
+ unsigned char byHighByte;
+} __attribute__ ((__packed__))
+SSecretKey;
+
+typedef struct tagSKeyEntry {
+ unsigned char abyAddrHi[2];
+ unsigned short wKCTL;
+ unsigned char abyAddrLo[4];
+ u32 dwKey0[4];
+ u32 dwKey1[4];
+ u32 dwKey2[4];
+ u32 dwKey3[4];
+ u32 dwKey4[4];
+} __attribute__ ((__packed__))
+SKeyEntry;
+
+#endif /* __DESC_H__ */
diff --git a/drivers/staging/vt6655/device.h b/drivers/staging/vt6655/device.h
new file mode 100644
index 000000000..440537e47
--- /dev/null
+++ b/drivers/staging/vt6655/device.h
@@ -0,0 +1,424 @@
+/*
+ * Copyright (c) 1996, 2003 VIA Networking Technologies, Inc.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * File: device.h
+ *
+ * Purpose: MAC Data structure
+ *
+ * Author: Tevin Chen
+ *
+ * Date: Mar 17, 1997
+ *
+ */
+
+#ifndef __DEVICE_H__
+#define __DEVICE_H__
+
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/mm.h>
+#include <linux/errno.h>
+#include <linux/ioport.h>
+#include <linux/pci.h>
+#include <linux/kernel.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/delay.h>
+#include <linux/timer.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/string.h>
+#include <linux/wait.h>
+#include <linux/if_arp.h>
+#include <linux/sched.h>
+#include <linux/io.h>
+#include <linux/if.h>
+#include <linux/crc32.h>
+#include <linux/uaccess.h>
+#include <linux/proc_fs.h>
+#include <linux/inetdevice.h>
+#include <linux/reboot.h>
+#include <linux/ethtool.h>
+/* Include Wireless Extension definition and check version - Jean II */
+#include <net/mac80211.h>
+#include <linux/wireless.h>
+#include <net/iw_handler.h> /* New driver API */
+
+#ifndef WPA_SUPPLICANT_DRIVER_WEXT_SUPPORT
+#define WPA_SUPPLICANT_DRIVER_WEXT_SUPPORT
+#endif
+
+/* device specific */
+
+#include "device_cfg.h"
+#include "card.h"
+#include "mib.h"
+#include "srom.h"
+#include "desc.h"
+#include "key.h"
+#include "mac.h"
+
+/*--------------------- Export Definitions -------------------------*/
+
+#define RATE_1M 0
+#define RATE_2M 1
+#define RATE_5M 2
+#define RATE_11M 3
+#define RATE_6M 4
+#define RATE_9M 5
+#define RATE_12M 6
+#define RATE_18M 7
+#define RATE_24M 8
+#define RATE_36M 9
+#define RATE_48M 10
+#define RATE_54M 11
+#define RATE_AUTO 12
+#define MAX_RATE 12
+
+#define MAC_MAX_CONTEXT_REG (256+128)
+
+#define MAX_MULTICAST_ADDRESS_NUM 32
+#define MULTICAST_ADDRESS_LIST_SIZE (MAX_MULTICAST_ADDRESS_NUM * ETH_ALEN)
+
+#define DUPLICATE_RX_CACHE_LENGTH 5
+
+#define NUM_KEY_ENTRY 11
+
+#define TX_WEP_NONE 0
+#define TX_WEP_OTF 1
+#define TX_WEP_SW 2
+#define TX_WEP_SWOTP 3
+#define TX_WEP_OTPSW 4
+#define TX_WEP_SW232 5
+
+#define KEYSEL_WEP40 0
+#define KEYSEL_WEP104 1
+#define KEYSEL_TKIP 2
+#define KEYSEL_CCMP 3
+
+#define AUTO_FB_NONE 0
+#define AUTO_FB_0 1
+#define AUTO_FB_1 2
+
+#define FB_RATE0 0
+#define FB_RATE1 1
+
+/* Antenna Mode */
+#define ANT_A 0
+#define ANT_B 1
+#define ANT_DIVERSITY 2
+#define ANT_RXD_TXA 3
+#define ANT_RXD_TXB 4
+#define ANT_UNKNOWN 0xFF
+
+#define MAXCHECKHANGCNT 4
+
+#define BB_VGA_LEVEL 4
+#define BB_VGA_CHANGE_THRESHOLD 16
+
+#ifndef RUN_AT
+#define RUN_AT(x) (jiffies+(x))
+#endif
+
+#define MAKE_BEACON_RESERVED 10 /* (us) */
+
+/* DMA related */
+#define RESERV_AC0DMA 4
+
+/* BUILD OBJ mode */
+
+#define AVAIL_TD(p, q) ((p)->sOpts.nTxDescs[(q)] - ((p)->iTDUsed[(q)]))
+
+#define NUM 64
+
+/* 0:11A 1:11B 2:11G */
+#define BB_TYPE_11A 0
+#define BB_TYPE_11B 1
+#define BB_TYPE_11G 2
+
+/* 0:11a, 1:11b, 2:11gb (only CCK in BasicRate), 3:11ga (OFDM in BasicRate) */
+#define PK_TYPE_11A 0
+#define PK_TYPE_11B 1
+#define PK_TYPE_11GB 2
+#define PK_TYPE_11GA 3
+
+typedef struct __chip_info_tbl {
+ CHIP_TYPE chip_id;
+ char *name;
+ int io_size;
+ int nTxQueue;
+ u32 flags;
+} CHIP_INFO, *PCHIP_INFO;
+
+typedef enum {
+ OWNED_BY_HOST = 0,
+ OWNED_BY_NIC = 1
+} DEVICE_OWNER_TYPE, *PDEVICE_OWNER_TYPE;
+
+/* flags for options */
+#define DEVICE_FLAGS_IP_ALIGN 0x00000001UL
+#define DEVICE_FLAGS_PREAMBLE_TYPE 0x00000002UL
+#define DEVICE_FLAGS_OP_MODE 0x00000004UL
+#define DEVICE_FLAGS_PS_MODE 0x00000008UL
+#define DEVICE_FLAGS_80211h_MODE 0x00000010UL
+#define DEVICE_FLAGS_DiversityANT 0x00000020UL
+
+/* flags for driver status */
+#define DEVICE_FLAGS_OPENED 0x00010000UL
+#define DEVICE_FLAGS_WOL_ENABLED 0x00080000UL
+/* flags for capabilities */
+#define DEVICE_FLAGS_TX_ALIGN 0x01000000UL
+#define DEVICE_FLAGS_HAVE_CAM 0x02000000UL
+#define DEVICE_FLAGS_FLOW_CTRL 0x04000000UL
+
+/* flags for MII status */
+#define DEVICE_LINK_FAIL 0x00000001UL
+#define DEVICE_SPEED_10 0x00000002UL
+#define DEVICE_SPEED_100 0x00000004UL
+#define DEVICE_SPEED_1000 0x00000008UL
+#define DEVICE_DUPLEX_FULL 0x00000010UL
+#define DEVICE_AUTONEG_ENABLE 0x00000020UL
+#define DEVICE_FORCED_BY_EEPROM 0x00000040UL
+/* for device_set_media_duplex */
+#define DEVICE_LINK_CHANGE 0x00000001UL
+
+typedef struct __device_opt {
+ int nRxDescs0; /* Number of RX descriptors0 */
+ int nRxDescs1; /* Number of RX descriptors1 */
+ int nTxDescs[2]; /* Number of TX descriptors 0, 1 */
+ int int_works; /* interrupt limits */
+ int short_retry;
+ int long_retry;
+ int bbp_type;
+ u32 flags;
+} OPTIONS, *POPTIONS;
+
+struct vnt_private {
+ struct pci_dev *pcid;
+ /* mac80211 */
+ struct ieee80211_hw *hw;
+ struct ieee80211_vif *vif;
+ unsigned long key_entry_inuse;
+ u32 basic_rates;
+ u16 current_aid;
+ int mc_list_count;
+ u8 mac_hw;
+
+/* dma addr, rx/tx pool */
+ dma_addr_t pool_dma;
+ dma_addr_t rd0_pool_dma;
+ dma_addr_t rd1_pool_dma;
+
+ dma_addr_t td0_pool_dma;
+ dma_addr_t td1_pool_dma;
+
+ dma_addr_t tx_bufs_dma0;
+ dma_addr_t tx_bufs_dma1;
+ dma_addr_t tx_beacon_dma;
+
+ unsigned char *tx0_bufs;
+ unsigned char *tx1_bufs;
+ unsigned char *tx_beacon_bufs;
+
+ CHIP_TYPE chip_id;
+
+ void __iomem *PortOffset;
+ unsigned long dwIsr;
+ u32 memaddr;
+ u32 ioaddr;
+ u32 io_size;
+
+ unsigned char byRevId;
+ unsigned char byRxMode;
+ unsigned short SubSystemID;
+ unsigned short SubVendorID;
+
+ spinlock_t lock;
+
+ int nTxQueues;
+ volatile int iTDUsed[TYPE_MAXTD];
+
+ volatile PSTxDesc apCurrTD[TYPE_MAXTD];
+ volatile PSTxDesc apTailTD[TYPE_MAXTD];
+
+ volatile PSTxDesc apTD0Rings;
+ volatile PSTxDesc apTD1Rings;
+
+ volatile PSRxDesc aRD0Ring;
+ volatile PSRxDesc aRD1Ring;
+ volatile PSRxDesc pCurrRD[TYPE_MAXRD];
+
+ OPTIONS sOpts;
+
+ u32 flags;
+
+ u32 rx_buf_sz;
+ u8 rx_rate;
+ int multicast_limit;
+
+ u32 rx_bytes;
+
+ /* Version control */
+ unsigned char byLocalID;
+ unsigned char byRFType;
+
+ unsigned char byMaxPwrLevel;
+ unsigned char byZoneType;
+ bool bZoneRegExist;
+ unsigned char byOriginalZonetype;
+
+ unsigned char abyCurrentNetAddr[ETH_ALEN]; __aligned(2)
+ bool bLinkPass; /* link status: OK or fail */
+
+ /* Adapter statistics */
+ SStatCounter scStatistic;
+ /* 802.11 counter */
+ SDot11Counters s802_11Counter;
+
+ unsigned int uCurrRSSI;
+ unsigned char byCurrSQ;
+
+ unsigned long dwTxAntennaSel;
+ unsigned long dwRxAntennaSel;
+ unsigned char byAntennaCount;
+ unsigned char byRxAntennaMode;
+ unsigned char byTxAntennaMode;
+ bool bTxRxAntInv;
+
+ unsigned char *pbyTmpBuff;
+ unsigned int uSIFS; /* Current SIFS */
+ unsigned int uDIFS; /* Current DIFS */
+ unsigned int uEIFS; /* Current EIFS */
+ unsigned int uSlot; /* Current SlotTime */
+ unsigned int uCwMin; /* Current CwMin */
+ unsigned int uCwMax; /* CwMax is fixed on 1023. */
+ /* PHY parameter */
+ unsigned char bySIFS;
+ unsigned char byDIFS;
+ unsigned char byEIFS;
+ unsigned char bySlot;
+ unsigned char byCWMaxMin;
+
+ u8 byBBType; /* 0:11A, 1:11B, 2:11G */
+ u8 byPacketType; /*
+ * 0:11a,1:11b,2:11gb (only CCK
+ * in BasicRate), 3:11ga (OFDM in
+ * Basic Rate)
+ */
+ unsigned short wBasicRate;
+ unsigned char byACKRate;
+ unsigned char byTopOFDMBasicRate;
+ unsigned char byTopCCKBasicRate;
+
+ unsigned char byMinChannel;
+ unsigned char byMaxChannel;
+
+ unsigned char byPreambleType;
+ unsigned char byShortPreamble;
+
+ unsigned short wCurrentRate;
+ unsigned char byShortRetryLimit;
+ unsigned char byLongRetryLimit;
+ enum nl80211_iftype op_mode;
+ bool bBSSIDFilter;
+ unsigned short wMaxTransmitMSDULifetime;
+
+ bool bEncryptionEnable;
+ bool bLongHeader;
+ bool bShortSlotTime;
+ bool bProtectMode;
+ bool bNonERPPresent;
+ bool bBarkerPreambleMd;
+
+ bool bRadioControlOff;
+ bool bRadioOff;
+ bool bEnablePSMode;
+ unsigned short wListenInterval;
+ bool bPWBitOn;
+
+ /* GPIO Radio Control */
+ unsigned char byRadioCtl;
+ unsigned char byGPIO;
+ bool bHWRadioOff;
+ bool bPrvActive4RadioOFF;
+ bool bGPIOBlockRead;
+
+ /* Beacon related */
+ unsigned short wSeqCounter;
+ unsigned short wBCNBufLen;
+ bool bBeaconBufReady;
+ bool bBeaconSent;
+ bool bIsBeaconBufReadySet;
+ unsigned int cbBeaconBufReadySetCnt;
+ bool bFixRate;
+ u16 byCurrentCh;
+
+ bool bAES;
+
+ unsigned char byAutoFBCtrl;
+
+ /* For Update BaseBand VGA Gain Offset */
+ bool bUpdateBBVGA;
+ unsigned int uBBVGADiffCount;
+ unsigned char byBBVGANew;
+ unsigned char byBBVGACurrent;
+ unsigned char abyBBVGA[BB_VGA_LEVEL];
+ long ldBmThreshold[BB_VGA_LEVEL];
+
+ unsigned char byBBPreEDRSSI;
+ unsigned char byBBPreEDIndex;
+
+ unsigned long dwDiagRefCount;
+
+ /* For FOE Tuning */
+ unsigned char byFOETuning;
+
+ /* For RF Power table */
+ unsigned char byCCKPwr;
+ unsigned char byOFDMPwrG;
+ unsigned char byCurPwr;
+ char byCurPwrdBm;
+ unsigned char abyCCKPwrTbl[CB_MAX_CHANNEL_24G+1];
+ unsigned char abyOFDMPwrTbl[CB_MAX_CHANNEL+1];
+ char abyCCKDefaultPwr[CB_MAX_CHANNEL_24G+1];
+ char abyOFDMDefaultPwr[CB_MAX_CHANNEL+1];
+ char abyRegPwr[CB_MAX_CHANNEL+1];
+ char abyLocalPwr[CB_MAX_CHANNEL+1];
+
+ /* BaseBand Loopback Use */
+ unsigned char byBBCR4d;
+ unsigned char byBBCRc9;
+ unsigned char byBBCR88;
+ unsigned char byBBCR09;
+
+ unsigned char abyEEPROM[EEP_MAX_CONTEXT_SIZE]; /* unsigned long alignment */
+
+ unsigned short wBeaconInterval;
+};
+
+static inline PDEVICE_RD_INFO alloc_rd_info(void)
+{
+ return kzalloc(sizeof(DEVICE_RD_INFO), GFP_ATOMIC);
+}
+
+static inline PDEVICE_TD_INFO alloc_td_info(void)
+{
+ return kzalloc(sizeof(DEVICE_TD_INFO), GFP_ATOMIC);
+}
+#endif
diff --git a/drivers/staging/vt6655/device_cfg.h b/drivers/staging/vt6655/device_cfg.h
new file mode 100644
index 000000000..a4a8a8489
--- /dev/null
+++ b/drivers/staging/vt6655/device_cfg.h
@@ -0,0 +1,87 @@
+/*
+ * Copyright (c) 1996, 2003 VIA Networking Technologies, Inc.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * File: device_cfg.h
+ *
+ * Purpose: Driver configuration header
+ * Author: Lyndon Chen
+ *
+ * Date: Dec 17, 2002
+ *
+ */
+#ifndef __DEVICE_CONFIG_H
+#define __DEVICE_CONFIG_H
+
+#include <linux/types.h>
+
+typedef
+struct _version {
+ unsigned char major;
+ unsigned char minor;
+ unsigned char build;
+} version_t, *pversion_t;
+
+#define VID_TABLE_SIZE 64
+#define MCAST_TABLE_SIZE 64
+#define MCAM_SIZE 32
+#define VCAM_SIZE 32
+#define TX_QUEUE_NO 8
+
+#define DEVICE_NAME "vt6655"
+#define DEVICE_FULL_DRV_NAM "VIA Networking Solomon-A/B/G Wireless LAN Adapter Driver"
+
+#ifndef MAJOR_VERSION
+#define MAJOR_VERSION 1
+#endif
+
+#ifndef MINOR_VERSION
+#define MINOR_VERSION 17
+#endif
+
+#ifndef DEVICE_VERSION
+#define DEVICE_VERSION "1.19.12"
+#endif
+
+#include <linux/fs.h>
+#include <linux/fcntl.h>
+#ifndef CONFIG_PATH
+#define CONFIG_PATH "/etc/vntconfiguration.dat"
+#endif
+
+#define PKT_BUF_SZ 2390
+
+typedef enum _chip_type {
+ VT3253 = 1
+} CHIP_TYPE, *PCHIP_TYPE;
+
+#ifdef VIAWET_DEBUG
+#define ASSERT(x) \
+do { \
+ if (!(x)) { \
+ pr_err("assertion %s failed: file %s line %d\n", \
+ #x, __func__, __LINE__); \
+ *(int *)0 = 0; \
+ } \
+} while (0)
+#define DBG_PORT80(value) outb(value, 0x80)
+#else
+#define ASSERT(x)
+#define DBG_PORT80(value)
+#endif
+
+#endif
diff --git a/drivers/staging/vt6655/device_main.c b/drivers/staging/vt6655/device_main.c
new file mode 100644
index 000000000..15baacb12
--- /dev/null
+++ b/drivers/staging/vt6655/device_main.c
@@ -0,0 +1,1914 @@
+/*
+ * Copyright (c) 1996, 2003 VIA Networking Technologies, Inc.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * File: device_main.c
+ *
+ * Purpose: driver entry for initial, open, close, tx and rx.
+ *
+ * Author: Lyndon Chen
+ *
+ * Date: Jan 8, 2003
+ *
+ * Functions:
+ *
+ * vt6655_probe - module initial (insmod) driver entry
+ * vt6655_remove - module remove entry
+ * vt6655_init_info - device structure resource allocation function
+ * device_free_info - device structure resource free function
+ * device_get_pci_info - get allocated pci io/mem resource
+ * device_print_info - print out resource
+ * device_intr - interrupt handle function
+ * device_rx_srv - rx service function
+ * device_alloc_rx_buf - rx buffer pre-allocated function
+ * device_free_tx_buf - free tx buffer function
+ * device_init_rd0_ring- initial rd dma0 ring
+ * device_init_rd1_ring- initial rd dma1 ring
+ * device_init_td0_ring- initial tx dma0 ring buffer
+ * device_init_td1_ring- initial tx dma1 ring buffer
+ * device_init_registers- initial MAC & BBP & RF internal registers.
+ * device_init_rings- initial tx/rx ring buffer
+ * device_free_rings- free all allocated ring buffer
+ * device_tx_srv- tx interrupt service function
+ *
+ * Revision History:
+ */
+#undef __NO_VERSION__
+
+#include <linux/file.h>
+#include "device.h"
+#include "card.h"
+#include "channel.h"
+#include "baseband.h"
+#include "mac.h"
+#include "power.h"
+#include "rxtx.h"
+#include "dpc.h"
+#include "rf.h"
+#include <linux/delay.h>
+#include <linux/kthread.h>
+#include <linux/slab.h>
+
+/*--------------------- Static Definitions -------------------------*/
+/*
+ * Define module options
+ */
+MODULE_AUTHOR("VIA Networking Technologies, Inc., <lyndonchen@vntek.com.tw>");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("VIA Networking Solomon-A/B/G Wireless LAN Adapter Driver");
+
+#define DEVICE_PARAM(N, D)
+
+#define RX_DESC_MIN0 16
+#define RX_DESC_MAX0 128
+#define RX_DESC_DEF0 32
+DEVICE_PARAM(RxDescriptors0, "Number of receive descriptors0");
+
+#define RX_DESC_MIN1 16
+#define RX_DESC_MAX1 128
+#define RX_DESC_DEF1 32
+DEVICE_PARAM(RxDescriptors1, "Number of receive descriptors1");
+
+#define TX_DESC_MIN0 16
+#define TX_DESC_MAX0 128
+#define TX_DESC_DEF0 32
+DEVICE_PARAM(TxDescriptors0, "Number of transmit descriptors0");
+
+#define TX_DESC_MIN1 16
+#define TX_DESC_MAX1 128
+#define TX_DESC_DEF1 64
+DEVICE_PARAM(TxDescriptors1, "Number of transmit descriptors1");
+
+#define INT_WORKS_DEF 20
+#define INT_WORKS_MIN 10
+#define INT_WORKS_MAX 64
+
+DEVICE_PARAM(int_works, "Number of packets per interrupt services");
+
+#define RTS_THRESH_DEF 2347
+
+#define FRAG_THRESH_DEF 2346
+
+#define SHORT_RETRY_MIN 0
+#define SHORT_RETRY_MAX 31
+#define SHORT_RETRY_DEF 8
+
+DEVICE_PARAM(ShortRetryLimit, "Short frame retry limits");
+
+#define LONG_RETRY_MIN 0
+#define LONG_RETRY_MAX 15
+#define LONG_RETRY_DEF 4
+
+DEVICE_PARAM(LongRetryLimit, "long frame retry limits");
+
+/* BasebandType[] baseband type selected
+ 0: indicate 802.11a type
+ 1: indicate 802.11b type
+ 2: indicate 802.11g type
+*/
+#define BBP_TYPE_MIN 0
+#define BBP_TYPE_MAX 2
+#define BBP_TYPE_DEF 2
+
+DEVICE_PARAM(BasebandType, "baseband type");
+
+/*
+ * Static vars definitions
+ */
+static CHIP_INFO chip_info_table[] = {
+ { VT3253, "VIA Networking Solomon-A/B/G Wireless LAN Adapter ",
+ 256, 1, DEVICE_FLAGS_IP_ALIGN|DEVICE_FLAGS_TX_ALIGN },
+ {0, NULL}
+};
+
+static const struct pci_device_id vt6655_pci_id_table[] = {
+ { PCI_VDEVICE(VIA, 0x3253), (kernel_ulong_t)chip_info_table},
+ { 0, }
+};
+
+/*--------------------- Static Functions --------------------------*/
+
+static int vt6655_probe(struct pci_dev *pcid, const struct pci_device_id *ent);
+static void vt6655_init_info(struct pci_dev *pcid,
+ struct vnt_private **ppDevice, PCHIP_INFO);
+static void device_free_info(struct vnt_private *pDevice);
+static bool device_get_pci_info(struct vnt_private *, struct pci_dev *pcid);
+static void device_print_info(struct vnt_private *pDevice);
+static irqreturn_t device_intr(int irq, void *dev_instance);
+
+#ifdef CONFIG_PM
+static int device_notify_reboot(struct notifier_block *, unsigned long event, void *ptr);
+static struct notifier_block device_notifier = {
+ .notifier_call = device_notify_reboot,
+ .next = NULL,
+ .priority = 0,
+};
+#endif
+
+static void device_init_rd0_ring(struct vnt_private *pDevice);
+static void device_init_rd1_ring(struct vnt_private *pDevice);
+static void device_init_td0_ring(struct vnt_private *pDevice);
+static void device_init_td1_ring(struct vnt_private *pDevice);
+
+static int device_rx_srv(struct vnt_private *pDevice, unsigned int uIdx);
+static int device_tx_srv(struct vnt_private *pDevice, unsigned int uIdx);
+static bool device_alloc_rx_buf(struct vnt_private *pDevice, PSRxDesc pDesc);
+static void device_init_registers(struct vnt_private *pDevice);
+static void device_free_tx_buf(struct vnt_private *pDevice, PSTxDesc pDesc);
+static void device_free_td0_ring(struct vnt_private *pDevice);
+static void device_free_td1_ring(struct vnt_private *pDevice);
+static void device_free_rd0_ring(struct vnt_private *pDevice);
+static void device_free_rd1_ring(struct vnt_private *pDevice);
+static void device_free_rings(struct vnt_private *pDevice);
+
+/*--------------------- Export Variables --------------------------*/
+
+/*--------------------- Export Functions --------------------------*/
+
+static char *get_chip_name(int chip_id)
+{
+ int i;
+
+ for (i = 0; chip_info_table[i].name != NULL; i++)
+ if (chip_info_table[i].chip_id == chip_id)
+ break;
+ return chip_info_table[i].name;
+}
+
+static void vt6655_remove(struct pci_dev *pcid)
+{
+ struct vnt_private *pDevice = pci_get_drvdata(pcid);
+
+ if (pDevice == NULL)
+ return;
+ device_free_info(pDevice);
+}
+
+static void device_get_options(struct vnt_private *pDevice)
+{
+ POPTIONS pOpts = &(pDevice->sOpts);
+
+ pOpts->nRxDescs0 = RX_DESC_DEF0;
+ pOpts->nRxDescs1 = RX_DESC_DEF1;
+ pOpts->nTxDescs[0] = TX_DESC_DEF0;
+ pOpts->nTxDescs[1] = TX_DESC_DEF1;
+ pOpts->int_works = INT_WORKS_DEF;
+
+ pOpts->short_retry = SHORT_RETRY_DEF;
+ pOpts->long_retry = LONG_RETRY_DEF;
+ pOpts->bbp_type = BBP_TYPE_DEF;
+}
+
+static void
+device_set_options(struct vnt_private *pDevice)
+{
+ pDevice->byShortRetryLimit = pDevice->sOpts.short_retry;
+ pDevice->byLongRetryLimit = pDevice->sOpts.long_retry;
+ pDevice->byBBType = pDevice->sOpts.bbp_type;
+ pDevice->byPacketType = pDevice->byBBType;
+ pDevice->byAutoFBCtrl = AUTO_FB_0;
+ pDevice->bUpdateBBVGA = true;
+ pDevice->byPreambleType = 0;
+
+ pr_debug(" byShortRetryLimit= %d\n", (int)pDevice->byShortRetryLimit);
+ pr_debug(" byLongRetryLimit= %d\n", (int)pDevice->byLongRetryLimit);
+ pr_debug(" byPreambleType= %d\n", (int)pDevice->byPreambleType);
+ pr_debug(" byShortPreamble= %d\n", (int)pDevice->byShortPreamble);
+ pr_debug(" byBBType= %d\n", (int)pDevice->byBBType);
+}
+
+/*
+ * Initialisation of MAC & BBP registers
+ */
+
+static void device_init_registers(struct vnt_private *pDevice)
+{
+ unsigned long flags;
+ unsigned int ii;
+ unsigned char byValue;
+ unsigned char byCCKPwrdBm = 0;
+ unsigned char byOFDMPwrdBm = 0;
+
+ MACbShutdown(pDevice->PortOffset);
+ BBvSoftwareReset(pDevice);
+
+ /* Do MACbSoftwareReset in MACvInitialize */
+ MACbSoftwareReset(pDevice->PortOffset);
+
+ pDevice->bAES = false;
+
+ /* Only used in 11g type, sync with ERP IE */
+ pDevice->bProtectMode = false;
+
+ pDevice->bNonERPPresent = false;
+ pDevice->bBarkerPreambleMd = false;
+ pDevice->wCurrentRate = RATE_1M;
+ pDevice->byTopOFDMBasicRate = RATE_24M;
+ pDevice->byTopCCKBasicRate = RATE_1M;
+
+ /* Target to IF pin while programming to RF chip. */
+ pDevice->byRevId = 0;
+
+ /* init MAC */
+ MACvInitialize(pDevice->PortOffset);
+
+ /* Get Local ID */
+ VNSvInPortB(pDevice->PortOffset + MAC_REG_LOCALID, &pDevice->byLocalID);
+
+ spin_lock_irqsave(&pDevice->lock, flags);
+
+ SROMvReadAllContents(pDevice->PortOffset, pDevice->abyEEPROM);
+
+ spin_unlock_irqrestore(&pDevice->lock, flags);
+
+ /* Get Channel range */
+ pDevice->byMinChannel = 1;
+ pDevice->byMaxChannel = CB_MAX_CHANNEL;
+
+ /* Get Antena */
+ byValue = SROMbyReadEmbedded(pDevice->PortOffset, EEP_OFS_ANTENNA);
+ if (byValue & EEP_ANTINV)
+ pDevice->bTxRxAntInv = true;
+ else
+ pDevice->bTxRxAntInv = false;
+
+ byValue &= (EEP_ANTENNA_AUX | EEP_ANTENNA_MAIN);
+ /* if not set default is All */
+ if (byValue == 0)
+ byValue = (EEP_ANTENNA_AUX | EEP_ANTENNA_MAIN);
+
+ if (byValue == (EEP_ANTENNA_AUX | EEP_ANTENNA_MAIN)) {
+ pDevice->byAntennaCount = 2;
+ pDevice->byTxAntennaMode = ANT_B;
+ pDevice->dwTxAntennaSel = 1;
+ pDevice->dwRxAntennaSel = 1;
+
+ if (pDevice->bTxRxAntInv)
+ pDevice->byRxAntennaMode = ANT_A;
+ else
+ pDevice->byRxAntennaMode = ANT_B;
+ } else {
+ pDevice->byAntennaCount = 1;
+ pDevice->dwTxAntennaSel = 0;
+ pDevice->dwRxAntennaSel = 0;
+
+ if (byValue & EEP_ANTENNA_AUX) {
+ pDevice->byTxAntennaMode = ANT_A;
+
+ if (pDevice->bTxRxAntInv)
+ pDevice->byRxAntennaMode = ANT_B;
+ else
+ pDevice->byRxAntennaMode = ANT_A;
+ } else {
+ pDevice->byTxAntennaMode = ANT_B;
+
+ if (pDevice->bTxRxAntInv)
+ pDevice->byRxAntennaMode = ANT_A;
+ else
+ pDevice->byRxAntennaMode = ANT_B;
+ }
+ }
+
+ /* Set initial antenna mode */
+ BBvSetTxAntennaMode(pDevice, pDevice->byTxAntennaMode);
+ BBvSetRxAntennaMode(pDevice, pDevice->byRxAntennaMode);
+
+ /* zonetype initial */
+ pDevice->byOriginalZonetype = pDevice->abyEEPROM[EEP_OFS_ZONETYPE];
+
+ if (!pDevice->bZoneRegExist)
+ pDevice->byZoneType = pDevice->abyEEPROM[EEP_OFS_ZONETYPE];
+
+ pr_debug("pDevice->byZoneType = %x\n", pDevice->byZoneType);
+
+ /* Init RF module */
+ RFbInit(pDevice);
+
+ /* Get Desire Power Value */
+ pDevice->byCurPwr = 0xFF;
+ pDevice->byCCKPwr = SROMbyReadEmbedded(pDevice->PortOffset, EEP_OFS_PWR_CCK);
+ pDevice->byOFDMPwrG = SROMbyReadEmbedded(pDevice->PortOffset, EEP_OFS_PWR_OFDMG);
+
+ /* Load power Table */
+ for (ii = 0; ii < CB_MAX_CHANNEL_24G; ii++) {
+ pDevice->abyCCKPwrTbl[ii + 1] =
+ SROMbyReadEmbedded(pDevice->PortOffset,
+ (unsigned char)(ii + EEP_OFS_CCK_PWR_TBL));
+ if (pDevice->abyCCKPwrTbl[ii + 1] == 0)
+ pDevice->abyCCKPwrTbl[ii+1] = pDevice->byCCKPwr;
+
+ pDevice->abyOFDMPwrTbl[ii + 1] =
+ SROMbyReadEmbedded(pDevice->PortOffset,
+ (unsigned char)(ii + EEP_OFS_OFDM_PWR_TBL));
+ if (pDevice->abyOFDMPwrTbl[ii + 1] == 0)
+ pDevice->abyOFDMPwrTbl[ii + 1] = pDevice->byOFDMPwrG;
+
+ pDevice->abyCCKDefaultPwr[ii + 1] = byCCKPwrdBm;
+ pDevice->abyOFDMDefaultPwr[ii + 1] = byOFDMPwrdBm;
+ }
+
+ /* recover 12,13 ,14channel for EUROPE by 11 channel */
+ for (ii = 11; ii < 14; ii++) {
+ pDevice->abyCCKPwrTbl[ii] = pDevice->abyCCKPwrTbl[10];
+ pDevice->abyOFDMPwrTbl[ii] = pDevice->abyOFDMPwrTbl[10];
+ }
+
+ /* Load OFDM A Power Table */
+ for (ii = 0; ii < CB_MAX_CHANNEL_5G; ii++) {
+ pDevice->abyOFDMPwrTbl[ii + CB_MAX_CHANNEL_24G + 1] =
+ SROMbyReadEmbedded(pDevice->PortOffset,
+ (unsigned char)(ii + EEP_OFS_OFDMA_PWR_TBL));
+
+ pDevice->abyOFDMDefaultPwr[ii + CB_MAX_CHANNEL_24G + 1] =
+ SROMbyReadEmbedded(pDevice->PortOffset,
+ (unsigned char)(ii + EEP_OFS_OFDMA_PWR_dBm));
+ }
+
+ if (pDevice->byLocalID > REV_ID_VT3253_B1) {
+ MACvSelectPage1(pDevice->PortOffset);
+
+ VNSvOutPortB(pDevice->PortOffset + MAC_REG_MSRCTL + 1,
+ (MSRCTL1_TXPWR | MSRCTL1_CSAPAREN));
+
+ MACvSelectPage0(pDevice->PortOffset);
+ }
+
+ /* use relative tx timeout and 802.11i D4 */
+ MACvWordRegBitsOn(pDevice->PortOffset,
+ MAC_REG_CFG, (CFG_TKIPOPT | CFG_NOTXTIMEOUT));
+
+ /* set performance parameter by registry */
+ MACvSetShortRetryLimit(pDevice->PortOffset, pDevice->byShortRetryLimit);
+ MACvSetLongRetryLimit(pDevice->PortOffset, pDevice->byLongRetryLimit);
+
+ /* reset TSF counter */
+ VNSvOutPortB(pDevice->PortOffset + MAC_REG_TFTCTL, TFTCTL_TSFCNTRST);
+ /* enable TSF counter */
+ VNSvOutPortB(pDevice->PortOffset + MAC_REG_TFTCTL, TFTCTL_TSFCNTREN);
+
+ /* initialize BBP registers */
+ BBbVT3253Init(pDevice);
+
+ if (pDevice->bUpdateBBVGA) {
+ pDevice->byBBVGACurrent = pDevice->abyBBVGA[0];
+ pDevice->byBBVGANew = pDevice->byBBVGACurrent;
+ BBvSetVGAGainOffset(pDevice, pDevice->abyBBVGA[0]);
+ }
+
+ BBvSetRxAntennaMode(pDevice, pDevice->byRxAntennaMode);
+ BBvSetTxAntennaMode(pDevice, pDevice->byTxAntennaMode);
+
+ /* Set BB and packet type at the same time. */
+ /* Set Short Slot Time, xIFS, and RSPINF. */
+ pDevice->wCurrentRate = RATE_54M;
+
+ pDevice->bRadioOff = false;
+
+ pDevice->byRadioCtl = SROMbyReadEmbedded(pDevice->PortOffset,
+ EEP_OFS_RADIOCTL);
+ pDevice->bHWRadioOff = false;
+
+ if (pDevice->byRadioCtl & EEP_RADIOCTL_ENABLE) {
+ /* Get GPIO */
+ MACvGPIOIn(pDevice->PortOffset, &pDevice->byGPIO);
+
+ if (((pDevice->byGPIO & GPIO0_DATA) &&
+ !(pDevice->byRadioCtl & EEP_RADIOCTL_INV)) ||
+ (!(pDevice->byGPIO & GPIO0_DATA) &&
+ (pDevice->byRadioCtl & EEP_RADIOCTL_INV)))
+ pDevice->bHWRadioOff = true;
+ }
+
+ if (pDevice->bHWRadioOff || pDevice->bRadioControlOff)
+ CARDbRadioPowerOff(pDevice);
+
+ /* get Permanent network address */
+ SROMvReadEtherAddress(pDevice->PortOffset, pDevice->abyCurrentNetAddr);
+ pr_debug("Network address = %pM\n", pDevice->abyCurrentNetAddr);
+
+ /* reset Tx pointer */
+ CARDvSafeResetRx(pDevice);
+ /* reset Rx pointer */
+ CARDvSafeResetTx(pDevice);
+
+ if (pDevice->byLocalID <= REV_ID_VT3253_A1)
+ MACvRegBitsOn(pDevice->PortOffset, MAC_REG_RCR, RCR_WPAERR);
+
+ /* Turn On Rx DMA */
+ MACvReceive0(pDevice->PortOffset);
+ MACvReceive1(pDevice->PortOffset);
+
+ /* start the adapter */
+ MACvStart(pDevice->PortOffset);
+}
+
+static void device_print_info(struct vnt_private *pDevice)
+{
+ dev_info(&pDevice->pcid->dev, "%s\n", get_chip_name(pDevice->chip_id));
+
+ dev_info(&pDevice->pcid->dev, "MAC=%pM IO=0x%lx Mem=0x%lx IRQ=%d\n",
+ pDevice->abyCurrentNetAddr, (unsigned long)pDevice->ioaddr,
+ (unsigned long)pDevice->PortOffset, pDevice->pcid->irq);
+}
+
+static void vt6655_init_info(struct pci_dev *pcid,
+ struct vnt_private **ppDevice,
+ PCHIP_INFO pChip_info)
+{
+ memset(*ppDevice, 0, sizeof(**ppDevice));
+
+ (*ppDevice)->pcid = pcid;
+ (*ppDevice)->chip_id = pChip_info->chip_id;
+ (*ppDevice)->io_size = pChip_info->io_size;
+ (*ppDevice)->nTxQueues = pChip_info->nTxQueue;
+ (*ppDevice)->multicast_limit = 32;
+
+ spin_lock_init(&((*ppDevice)->lock));
+}
+
+static bool device_get_pci_info(struct vnt_private *pDevice,
+ struct pci_dev *pcid)
+{
+ u16 pci_cmd;
+ u8 b;
+ unsigned int cis_addr;
+
+ pci_read_config_byte(pcid, PCI_REVISION_ID, &pDevice->byRevId);
+ pci_read_config_word(pcid, PCI_SUBSYSTEM_ID, &pDevice->SubSystemID);
+ pci_read_config_word(pcid, PCI_SUBSYSTEM_VENDOR_ID, &pDevice->SubVendorID);
+ pci_read_config_word(pcid, PCI_COMMAND, (u16 *)&(pci_cmd));
+
+ pci_set_master(pcid);
+
+ pDevice->memaddr = pci_resource_start(pcid, 0);
+ pDevice->ioaddr = pci_resource_start(pcid, 1);
+
+ cis_addr = pci_resource_start(pcid, 2);
+
+ pDevice->pcid = pcid;
+
+ pci_read_config_byte(pcid, PCI_COMMAND, &b);
+ pci_write_config_byte(pcid, PCI_COMMAND, (b|PCI_COMMAND_MASTER));
+
+ return true;
+}
+
+static void device_free_info(struct vnt_private *pDevice)
+{
+ if (!pDevice)
+ return;
+
+ if (pDevice->mac_hw)
+ ieee80211_unregister_hw(pDevice->hw);
+
+ if (pDevice->PortOffset)
+ iounmap(pDevice->PortOffset);
+
+ if (pDevice->pcid)
+ pci_release_regions(pDevice->pcid);
+
+ if (pDevice->hw)
+ ieee80211_free_hw(pDevice->hw);
+}
+
+static bool device_init_rings(struct vnt_private *pDevice)
+{
+ void *vir_pool;
+
+ /*allocate all RD/TD rings a single pool*/
+ vir_pool = dma_zalloc_coherent(&pDevice->pcid->dev,
+ pDevice->sOpts.nRxDescs0 * sizeof(SRxDesc) +
+ pDevice->sOpts.nRxDescs1 * sizeof(SRxDesc) +
+ pDevice->sOpts.nTxDescs[0] * sizeof(STxDesc) +
+ pDevice->sOpts.nTxDescs[1] * sizeof(STxDesc),
+ &pDevice->pool_dma, GFP_ATOMIC);
+ if (vir_pool == NULL) {
+ dev_err(&pDevice->pcid->dev, "allocate desc dma memory failed\n");
+ return false;
+ }
+
+ pDevice->aRD0Ring = vir_pool;
+ pDevice->aRD1Ring = vir_pool +
+ pDevice->sOpts.nRxDescs0 * sizeof(SRxDesc);
+
+ pDevice->rd0_pool_dma = pDevice->pool_dma;
+ pDevice->rd1_pool_dma = pDevice->rd0_pool_dma +
+ pDevice->sOpts.nRxDescs0 * sizeof(SRxDesc);
+
+ pDevice->tx0_bufs = dma_zalloc_coherent(&pDevice->pcid->dev,
+ pDevice->sOpts.nTxDescs[0] * PKT_BUF_SZ +
+ pDevice->sOpts.nTxDescs[1] * PKT_BUF_SZ +
+ CB_BEACON_BUF_SIZE +
+ CB_MAX_BUF_SIZE,
+ &pDevice->tx_bufs_dma0,
+ GFP_ATOMIC);
+ if (pDevice->tx0_bufs == NULL) {
+ dev_err(&pDevice->pcid->dev, "allocate buf dma memory failed\n");
+
+ dma_free_coherent(&pDevice->pcid->dev,
+ pDevice->sOpts.nRxDescs0 * sizeof(SRxDesc) +
+ pDevice->sOpts.nRxDescs1 * sizeof(SRxDesc) +
+ pDevice->sOpts.nTxDescs[0] * sizeof(STxDesc) +
+ pDevice->sOpts.nTxDescs[1] * sizeof(STxDesc),
+ vir_pool, pDevice->pool_dma
+ );
+ return false;
+ }
+
+ pDevice->td0_pool_dma = pDevice->rd1_pool_dma +
+ pDevice->sOpts.nRxDescs1 * sizeof(SRxDesc);
+
+ pDevice->td1_pool_dma = pDevice->td0_pool_dma +
+ pDevice->sOpts.nTxDescs[0] * sizeof(STxDesc);
+
+ /* vir_pool: pvoid type */
+ pDevice->apTD0Rings = vir_pool
+ + pDevice->sOpts.nRxDescs0 * sizeof(SRxDesc)
+ + pDevice->sOpts.nRxDescs1 * sizeof(SRxDesc);
+
+ pDevice->apTD1Rings = vir_pool
+ + pDevice->sOpts.nRxDescs0 * sizeof(SRxDesc)
+ + pDevice->sOpts.nRxDescs1 * sizeof(SRxDesc)
+ + pDevice->sOpts.nTxDescs[0] * sizeof(STxDesc);
+
+ pDevice->tx1_bufs = pDevice->tx0_bufs +
+ pDevice->sOpts.nTxDescs[0] * PKT_BUF_SZ;
+
+ pDevice->tx_beacon_bufs = pDevice->tx1_bufs +
+ pDevice->sOpts.nTxDescs[1] * PKT_BUF_SZ;
+
+ pDevice->pbyTmpBuff = pDevice->tx_beacon_bufs +
+ CB_BEACON_BUF_SIZE;
+
+ pDevice->tx_bufs_dma1 = pDevice->tx_bufs_dma0 +
+ pDevice->sOpts.nTxDescs[0] * PKT_BUF_SZ;
+
+ pDevice->tx_beacon_dma = pDevice->tx_bufs_dma1 +
+ pDevice->sOpts.nTxDescs[1] * PKT_BUF_SZ;
+
+ return true;
+}
+
+static void device_free_rings(struct vnt_private *pDevice)
+{
+ dma_free_coherent(&pDevice->pcid->dev,
+ pDevice->sOpts.nRxDescs0 * sizeof(SRxDesc) +
+ pDevice->sOpts.nRxDescs1 * sizeof(SRxDesc) +
+ pDevice->sOpts.nTxDescs[0] * sizeof(STxDesc) +
+ pDevice->sOpts.nTxDescs[1] * sizeof(STxDesc)
+ ,
+ pDevice->aRD0Ring, pDevice->pool_dma
+ );
+
+ if (pDevice->tx0_bufs)
+ dma_free_coherent(&pDevice->pcid->dev,
+ pDevice->sOpts.nTxDescs[0] * PKT_BUF_SZ +
+ pDevice->sOpts.nTxDescs[1] * PKT_BUF_SZ +
+ CB_BEACON_BUF_SIZE +
+ CB_MAX_BUF_SIZE,
+ pDevice->tx0_bufs, pDevice->tx_bufs_dma0
+ );
+}
+
+static void device_init_rd0_ring(struct vnt_private *pDevice)
+{
+ int i;
+ dma_addr_t curr = pDevice->rd0_pool_dma;
+ PSRxDesc pDesc;
+
+ /* Init the RD0 ring entries */
+ for (i = 0; i < pDevice->sOpts.nRxDescs0; i ++, curr += sizeof(SRxDesc)) {
+ pDesc = &(pDevice->aRD0Ring[i]);
+ pDesc->pRDInfo = alloc_rd_info();
+ ASSERT(pDesc->pRDInfo);
+ if (!device_alloc_rx_buf(pDevice, pDesc))
+ dev_err(&pDevice->pcid->dev, "can not alloc rx bufs\n");
+
+ pDesc->next = &(pDevice->aRD0Ring[(i+1) % pDevice->sOpts.nRxDescs0]);
+ pDesc->pRDInfo->curr_desc = cpu_to_le32(curr);
+ pDesc->next_desc = cpu_to_le32(curr + sizeof(SRxDesc));
+ }
+
+ if (i > 0)
+ pDevice->aRD0Ring[i-1].next_desc = cpu_to_le32(pDevice->rd0_pool_dma);
+ pDevice->pCurrRD[0] = &(pDevice->aRD0Ring[0]);
+}
+
+static void device_init_rd1_ring(struct vnt_private *pDevice)
+{
+ int i;
+ dma_addr_t curr = pDevice->rd1_pool_dma;
+ PSRxDesc pDesc;
+
+ /* Init the RD1 ring entries */
+ for (i = 0; i < pDevice->sOpts.nRxDescs1; i ++, curr += sizeof(SRxDesc)) {
+ pDesc = &(pDevice->aRD1Ring[i]);
+ pDesc->pRDInfo = alloc_rd_info();
+ ASSERT(pDesc->pRDInfo);
+ if (!device_alloc_rx_buf(pDevice, pDesc))
+ dev_err(&pDevice->pcid->dev, "can not alloc rx bufs\n");
+
+ pDesc->next = &(pDevice->aRD1Ring[(i+1) % pDevice->sOpts.nRxDescs1]);
+ pDesc->pRDInfo->curr_desc = cpu_to_le32(curr);
+ pDesc->next_desc = cpu_to_le32(curr + sizeof(SRxDesc));
+ }
+
+ if (i > 0)
+ pDevice->aRD1Ring[i-1].next_desc = cpu_to_le32(pDevice->rd1_pool_dma);
+ pDevice->pCurrRD[1] = &(pDevice->aRD1Ring[0]);
+}
+
+static void device_free_rd0_ring(struct vnt_private *pDevice)
+{
+ int i;
+
+ for (i = 0; i < pDevice->sOpts.nRxDescs0; i++) {
+ PSRxDesc pDesc = &(pDevice->aRD0Ring[i]);
+ PDEVICE_RD_INFO pRDInfo = pDesc->pRDInfo;
+
+ dma_unmap_single(&pDevice->pcid->dev, pRDInfo->skb_dma,
+ pDevice->rx_buf_sz, DMA_FROM_DEVICE);
+
+ dev_kfree_skb(pRDInfo->skb);
+
+ kfree(pDesc->pRDInfo);
+ }
+}
+
+static void device_free_rd1_ring(struct vnt_private *pDevice)
+{
+ int i;
+
+ for (i = 0; i < pDevice->sOpts.nRxDescs1; i++) {
+ PSRxDesc pDesc = &(pDevice->aRD1Ring[i]);
+ PDEVICE_RD_INFO pRDInfo = pDesc->pRDInfo;
+
+ dma_unmap_single(&pDevice->pcid->dev, pRDInfo->skb_dma,
+ pDevice->rx_buf_sz, DMA_FROM_DEVICE);
+
+ dev_kfree_skb(pRDInfo->skb);
+
+ kfree(pDesc->pRDInfo);
+ }
+}
+
+static void device_init_td0_ring(struct vnt_private *pDevice)
+{
+ int i;
+ dma_addr_t curr;
+ PSTxDesc pDesc;
+
+ curr = pDevice->td0_pool_dma;
+ for (i = 0; i < pDevice->sOpts.nTxDescs[0]; i++, curr += sizeof(STxDesc)) {
+ pDesc = &(pDevice->apTD0Rings[i]);
+ pDesc->pTDInfo = alloc_td_info();
+ ASSERT(pDesc->pTDInfo);
+ if (pDevice->flags & DEVICE_FLAGS_TX_ALIGN) {
+ pDesc->pTDInfo->buf = pDevice->tx0_bufs + (i)*PKT_BUF_SZ;
+ pDesc->pTDInfo->buf_dma = pDevice->tx_bufs_dma0 + (i)*PKT_BUF_SZ;
+ }
+ pDesc->next = &(pDevice->apTD0Rings[(i+1) % pDevice->sOpts.nTxDescs[0]]);
+ pDesc->pTDInfo->curr_desc = cpu_to_le32(curr);
+ pDesc->next_desc = cpu_to_le32(curr+sizeof(STxDesc));
+ }
+
+ if (i > 0)
+ pDevice->apTD0Rings[i-1].next_desc = cpu_to_le32(pDevice->td0_pool_dma);
+ pDevice->apTailTD[0] = pDevice->apCurrTD[0] = &(pDevice->apTD0Rings[0]);
+}
+
+static void device_init_td1_ring(struct vnt_private *pDevice)
+{
+ int i;
+ dma_addr_t curr;
+ PSTxDesc pDesc;
+
+ /* Init the TD ring entries */
+ curr = pDevice->td1_pool_dma;
+ for (i = 0; i < pDevice->sOpts.nTxDescs[1]; i++, curr += sizeof(STxDesc)) {
+ pDesc = &(pDevice->apTD1Rings[i]);
+ pDesc->pTDInfo = alloc_td_info();
+ ASSERT(pDesc->pTDInfo);
+ if (pDevice->flags & DEVICE_FLAGS_TX_ALIGN) {
+ pDesc->pTDInfo->buf = pDevice->tx1_bufs + (i) * PKT_BUF_SZ;
+ pDesc->pTDInfo->buf_dma = pDevice->tx_bufs_dma1 + (i) * PKT_BUF_SZ;
+ }
+ pDesc->next = &(pDevice->apTD1Rings[(i + 1) % pDevice->sOpts.nTxDescs[1]]);
+ pDesc->pTDInfo->curr_desc = cpu_to_le32(curr);
+ pDesc->next_desc = cpu_to_le32(curr+sizeof(STxDesc));
+ }
+
+ if (i > 0)
+ pDevice->apTD1Rings[i-1].next_desc = cpu_to_le32(pDevice->td1_pool_dma);
+ pDevice->apTailTD[1] = pDevice->apCurrTD[1] = &(pDevice->apTD1Rings[0]);
+}
+
+static void device_free_td0_ring(struct vnt_private *pDevice)
+{
+ int i;
+
+ for (i = 0; i < pDevice->sOpts.nTxDescs[0]; i++) {
+ PSTxDesc pDesc = &(pDevice->apTD0Rings[i]);
+ PDEVICE_TD_INFO pTDInfo = pDesc->pTDInfo;
+
+ if (pTDInfo->skb_dma && (pTDInfo->skb_dma != pTDInfo->buf_dma))
+ dma_unmap_single(&pDevice->pcid->dev, pTDInfo->skb_dma,
+ pTDInfo->skb->len, DMA_TO_DEVICE);
+
+ if (pTDInfo->skb)
+ dev_kfree_skb(pTDInfo->skb);
+
+ kfree(pDesc->pTDInfo);
+ }
+}
+
+static void device_free_td1_ring(struct vnt_private *pDevice)
+{
+ int i;
+
+ for (i = 0; i < pDevice->sOpts.nTxDescs[1]; i++) {
+ PSTxDesc pDesc = &(pDevice->apTD1Rings[i]);
+ PDEVICE_TD_INFO pTDInfo = pDesc->pTDInfo;
+
+ if (pTDInfo->skb_dma && (pTDInfo->skb_dma != pTDInfo->buf_dma))
+ dma_unmap_single(&pDevice->pcid->dev, pTDInfo->skb_dma,
+ pTDInfo->skb->len, DMA_TO_DEVICE);
+
+ if (pTDInfo->skb)
+ dev_kfree_skb(pTDInfo->skb);
+
+ kfree(pDesc->pTDInfo);
+ }
+}
+
+/*-----------------------------------------------------------------*/
+
+static int device_rx_srv(struct vnt_private *pDevice, unsigned int uIdx)
+{
+ PSRxDesc pRD;
+ int works = 0;
+
+ for (pRD = pDevice->pCurrRD[uIdx];
+ pRD->m_rd0RD0.f1Owner == OWNED_BY_HOST;
+ pRD = pRD->next) {
+ if (works++ > 15)
+ break;
+
+ if (!pRD->pRDInfo->skb)
+ break;
+
+ if (vnt_receive_frame(pDevice, pRD)) {
+ if (!device_alloc_rx_buf(pDevice, pRD)) {
+ dev_err(&pDevice->pcid->dev,
+ "can not allocate rx buf\n");
+ break;
+ }
+ }
+ pRD->m_rd0RD0.f1Owner = OWNED_BY_NIC;
+ }
+
+ pDevice->pCurrRD[uIdx] = pRD;
+
+ return works;
+}
+
+static bool device_alloc_rx_buf(struct vnt_private *pDevice, PSRxDesc pRD)
+{
+ PDEVICE_RD_INFO pRDInfo = pRD->pRDInfo;
+
+ pRDInfo->skb = dev_alloc_skb((int)pDevice->rx_buf_sz);
+ if (pRDInfo->skb == NULL)
+ return false;
+ ASSERT(pRDInfo->skb);
+
+ pRDInfo->skb_dma =
+ dma_map_single(&pDevice->pcid->dev,
+ skb_put(pRDInfo->skb, skb_tailroom(pRDInfo->skb)),
+ pDevice->rx_buf_sz, DMA_FROM_DEVICE);
+
+ *((unsigned int *)&(pRD->m_rd0RD0)) = 0; /* FIX cast */
+
+ pRD->m_rd0RD0.wResCount = cpu_to_le16(pDevice->rx_buf_sz);
+ pRD->m_rd0RD0.f1Owner = OWNED_BY_NIC;
+ pRD->m_rd1RD1.wReqCount = cpu_to_le16(pDevice->rx_buf_sz);
+ pRD->buff_addr = cpu_to_le32(pRDInfo->skb_dma);
+
+ return true;
+}
+
+static const u8 fallback_rate0[5][5] = {
+ {RATE_18M, RATE_18M, RATE_12M, RATE_12M, RATE_12M},
+ {RATE_24M, RATE_24M, RATE_18M, RATE_12M, RATE_12M},
+ {RATE_36M, RATE_36M, RATE_24M, RATE_18M, RATE_18M},
+ {RATE_48M, RATE_48M, RATE_36M, RATE_24M, RATE_24M},
+ {RATE_54M, RATE_54M, RATE_48M, RATE_36M, RATE_36M}
+};
+
+static const u8 fallback_rate1[5][5] = {
+ {RATE_18M, RATE_18M, RATE_12M, RATE_6M, RATE_6M},
+ {RATE_24M, RATE_24M, RATE_18M, RATE_6M, RATE_6M},
+ {RATE_36M, RATE_36M, RATE_24M, RATE_12M, RATE_12M},
+ {RATE_48M, RATE_48M, RATE_24M, RATE_12M, RATE_12M},
+ {RATE_54M, RATE_54M, RATE_36M, RATE_18M, RATE_18M}
+};
+
+static int vnt_int_report_rate(struct vnt_private *priv,
+ PDEVICE_TD_INFO context, u8 tsr0, u8 tsr1)
+{
+ struct vnt_tx_fifo_head *fifo_head;
+ struct ieee80211_tx_info *info;
+ struct ieee80211_rate *rate;
+ u16 fb_option;
+ u8 tx_retry = (tsr0 & TSR0_NCR);
+ s8 idx;
+
+ if (!context)
+ return -ENOMEM;
+
+ if (!context->skb)
+ return -EINVAL;
+
+ fifo_head = (struct vnt_tx_fifo_head *)context->buf;
+ fb_option = (le16_to_cpu(fifo_head->fifo_ctl) &
+ (FIFOCTL_AUTO_FB_0 | FIFOCTL_AUTO_FB_1));
+
+ info = IEEE80211_SKB_CB(context->skb);
+ idx = info->control.rates[0].idx;
+
+ if (fb_option && !(tsr1 & TSR1_TERR)) {
+ u8 tx_rate;
+ u8 retry = tx_retry;
+
+ rate = ieee80211_get_tx_rate(priv->hw, info);
+ tx_rate = rate->hw_value - RATE_18M;
+
+ if (retry > 4)
+ retry = 4;
+
+ if (fb_option & FIFOCTL_AUTO_FB_0)
+ tx_rate = fallback_rate0[tx_rate][retry];
+ else if (fb_option & FIFOCTL_AUTO_FB_1)
+ tx_rate = fallback_rate1[tx_rate][retry];
+
+ if (info->band == IEEE80211_BAND_5GHZ)
+ idx = tx_rate - RATE_6M;
+ else
+ idx = tx_rate;
+ }
+
+ ieee80211_tx_info_clear_status(info);
+
+ info->status.rates[0].count = tx_retry;
+
+ if (!(tsr1 & TSR1_TERR)) {
+ info->status.rates[0].idx = idx;
+
+ if (info->flags & IEEE80211_TX_CTL_NO_ACK)
+ info->flags |= IEEE80211_TX_STAT_NOACK_TRANSMITTED;
+ else
+ info->flags |= IEEE80211_TX_STAT_ACK;
+ }
+
+ return 0;
+}
+
+static int device_tx_srv(struct vnt_private *pDevice, unsigned int uIdx)
+{
+ PSTxDesc pTD;
+ int works = 0;
+ unsigned char byTsr0;
+ unsigned char byTsr1;
+
+ for (pTD = pDevice->apTailTD[uIdx]; pDevice->iTDUsed[uIdx] > 0; pTD = pTD->next) {
+ if (pTD->m_td0TD0.f1Owner == OWNED_BY_NIC)
+ break;
+ if (works++ > 15)
+ break;
+
+ byTsr0 = pTD->m_td0TD0.byTSR0;
+ byTsr1 = pTD->m_td0TD0.byTSR1;
+
+ /* Only the status of first TD in the chain is correct */
+ if (pTD->m_td1TD1.byTCR & TCR_STP) {
+ if ((pTD->pTDInfo->byFlags & TD_FLAGS_NETIF_SKB) != 0) {
+ if (!(byTsr1 & TSR1_TERR)) {
+ if (byTsr0 != 0) {
+ pr_debug(" Tx[%d] OK but has error. tsr1[%02X] tsr0[%02X]\n",
+ (int)uIdx, byTsr1,
+ byTsr0);
+ }
+ } else {
+ pr_debug(" Tx[%d] dropped & tsr1[%02X] tsr0[%02X]\n",
+ (int)uIdx, byTsr1, byTsr0);
+ }
+ }
+
+ if (byTsr1 & TSR1_TERR) {
+ if ((pTD->pTDInfo->byFlags & TD_FLAGS_PRIV_SKB) != 0) {
+ pr_debug(" Tx[%d] fail has error. tsr1[%02X] tsr0[%02X]\n",
+ (int)uIdx, byTsr1, byTsr0);
+ }
+ }
+
+ vnt_int_report_rate(pDevice, pTD->pTDInfo, byTsr0, byTsr1);
+
+ device_free_tx_buf(pDevice, pTD);
+ pDevice->iTDUsed[uIdx]--;
+ }
+ }
+
+ pDevice->apTailTD[uIdx] = pTD;
+
+ return works;
+}
+
+static void device_error(struct vnt_private *pDevice, unsigned short status)
+{
+ if (status & ISR_FETALERR) {
+ dev_err(&pDevice->pcid->dev, "Hardware fatal error\n");
+
+ MACbShutdown(pDevice->PortOffset);
+ return;
+ }
+}
+
+static void device_free_tx_buf(struct vnt_private *pDevice, PSTxDesc pDesc)
+{
+ PDEVICE_TD_INFO pTDInfo = pDesc->pTDInfo;
+ struct sk_buff *skb = pTDInfo->skb;
+
+ /* pre-allocated buf_dma can't be unmapped. */
+ if (pTDInfo->skb_dma && (pTDInfo->skb_dma != pTDInfo->buf_dma)) {
+ dma_unmap_single(&pDevice->pcid->dev, pTDInfo->skb_dma,
+ skb->len, DMA_TO_DEVICE);
+ }
+
+ if (skb)
+ ieee80211_tx_status_irqsafe(pDevice->hw, skb);
+
+ pTDInfo->skb_dma = 0;
+ pTDInfo->skb = NULL;
+ pTDInfo->byFlags = 0;
+}
+
+static void vnt_check_bb_vga(struct vnt_private *priv)
+{
+ long dbm;
+ int i;
+
+ if (!priv->bUpdateBBVGA)
+ return;
+
+ if (priv->hw->conf.flags & IEEE80211_CONF_OFFCHANNEL)
+ return;
+
+ if (!(priv->vif->bss_conf.assoc && priv->uCurrRSSI))
+ return;
+
+ RFvRSSITodBm(priv, (u8)priv->uCurrRSSI, &dbm);
+
+ for (i = 0; i < BB_VGA_LEVEL; i++) {
+ if (dbm < priv->ldBmThreshold[i]) {
+ priv->byBBVGANew = priv->abyBBVGA[i];
+ break;
+ }
+ }
+
+ if (priv->byBBVGANew == priv->byBBVGACurrent) {
+ priv->uBBVGADiffCount = 1;
+ return;
+ }
+
+ priv->uBBVGADiffCount++;
+
+ if (priv->uBBVGADiffCount == 1) {
+ /* first VGA diff gain */
+ BBvSetVGAGainOffset(priv, priv->byBBVGANew);
+
+ dev_dbg(&priv->pcid->dev,
+ "First RSSI[%d] NewGain[%d] OldGain[%d] Count[%d]\n",
+ (int)dbm, priv->byBBVGANew,
+ priv->byBBVGACurrent,
+ (int)priv->uBBVGADiffCount);
+ }
+
+ if (priv->uBBVGADiffCount >= BB_VGA_CHANGE_THRESHOLD) {
+ dev_dbg(&priv->pcid->dev,
+ "RSSI[%d] NewGain[%d] OldGain[%d] Count[%d]\n",
+ (int)dbm, priv->byBBVGANew,
+ priv->byBBVGACurrent,
+ (int)priv->uBBVGADiffCount);
+
+ BBvSetVGAGainOffset(priv, priv->byBBVGANew);
+ }
+}
+
+static irqreturn_t device_intr(int irq, void *dev_instance)
+{
+ struct vnt_private *pDevice = dev_instance;
+ int max_count = 0;
+ unsigned long dwMIBCounter = 0;
+ unsigned char byOrgPageSel = 0;
+ int handled = 0;
+ unsigned long flags;
+
+ MACvReadISR(pDevice->PortOffset, &pDevice->dwIsr);
+
+ if (pDevice->dwIsr == 0)
+ return IRQ_RETVAL(handled);
+
+ if (pDevice->dwIsr == 0xffffffff) {
+ pr_debug("dwIsr = 0xffff\n");
+ return IRQ_RETVAL(handled);
+ }
+
+ handled = 1;
+ MACvIntDisable(pDevice->PortOffset);
+
+ spin_lock_irqsave(&pDevice->lock, flags);
+
+ /* Make sure current page is 0 */
+ VNSvInPortB(pDevice->PortOffset + MAC_REG_PAGE1SEL, &byOrgPageSel);
+ if (byOrgPageSel == 1)
+ MACvSelectPage0(pDevice->PortOffset);
+ else
+ byOrgPageSel = 0;
+
+ MACvReadMIBCounter(pDevice->PortOffset, &dwMIBCounter);
+ /*
+ * TBD....
+ * Must do this after doing rx/tx, cause ISR bit is slow
+ * than RD/TD write back
+ * update ISR counter
+ */
+ STAvUpdate802_11Counter(&pDevice->s802_11Counter, &pDevice->scStatistic, dwMIBCounter);
+ while (pDevice->dwIsr != 0) {
+ STAvUpdateIsrStatCounter(&pDevice->scStatistic, pDevice->dwIsr);
+ MACvWriteISR(pDevice->PortOffset, pDevice->dwIsr);
+
+ if (pDevice->dwIsr & ISR_FETALERR) {
+ pr_debug(" ISR_FETALERR\n");
+ VNSvOutPortB(pDevice->PortOffset + MAC_REG_SOFTPWRCTL, 0);
+ VNSvOutPortW(pDevice->PortOffset + MAC_REG_SOFTPWRCTL, SOFTPWRCTL_SWPECTI);
+ device_error(pDevice, pDevice->dwIsr);
+ }
+
+ if (pDevice->dwIsr & ISR_TBTT) {
+ if (pDevice->vif &&
+ pDevice->op_mode != NL80211_IFTYPE_ADHOC)
+ vnt_check_bb_vga(pDevice);
+
+ pDevice->bBeaconSent = false;
+ if (pDevice->bEnablePSMode)
+ PSbIsNextTBTTWakeUp((void *)pDevice);
+
+ if ((pDevice->op_mode == NL80211_IFTYPE_AP ||
+ pDevice->op_mode == NL80211_IFTYPE_ADHOC) &&
+ pDevice->vif->bss_conf.enable_beacon) {
+ MACvOneShotTimer1MicroSec(pDevice->PortOffset,
+ (pDevice->vif->bss_conf.beacon_int - MAKE_BEACON_RESERVED) << 10);
+ }
+
+ /* TODO: adhoc PS mode */
+
+ }
+
+ if (pDevice->dwIsr & ISR_BNTX) {
+ if (pDevice->op_mode == NL80211_IFTYPE_ADHOC) {
+ pDevice->bIsBeaconBufReadySet = false;
+ pDevice->cbBeaconBufReadySetCnt = 0;
+ }
+
+ pDevice->bBeaconSent = true;
+ }
+
+ if (pDevice->dwIsr & ISR_RXDMA0)
+ max_count += device_rx_srv(pDevice, TYPE_RXDMA0);
+
+ if (pDevice->dwIsr & ISR_RXDMA1)
+ max_count += device_rx_srv(pDevice, TYPE_RXDMA1);
+
+ if (pDevice->dwIsr & ISR_TXDMA0)
+ max_count += device_tx_srv(pDevice, TYPE_TXDMA0);
+
+ if (pDevice->dwIsr & ISR_AC0DMA)
+ max_count += device_tx_srv(pDevice, TYPE_AC0DMA);
+
+ if (pDevice->dwIsr & ISR_SOFTTIMER1) {
+ if (pDevice->vif) {
+ if (pDevice->vif->bss_conf.enable_beacon)
+ vnt_beacon_make(pDevice, pDevice->vif);
+ }
+ }
+
+ /* If both buffers available wake the queue */
+ if (pDevice->vif) {
+ if (AVAIL_TD(pDevice, TYPE_TXDMA0) &&
+ AVAIL_TD(pDevice, TYPE_AC0DMA) &&
+ ieee80211_queue_stopped(pDevice->hw, 0))
+ ieee80211_wake_queues(pDevice->hw);
+ }
+
+ MACvReadISR(pDevice->PortOffset, &pDevice->dwIsr);
+
+ MACvReceive0(pDevice->PortOffset);
+ MACvReceive1(pDevice->PortOffset);
+
+ if (max_count > pDevice->sOpts.int_works)
+ break;
+ }
+
+ if (byOrgPageSel == 1)
+ MACvSelectPage1(pDevice->PortOffset);
+
+ spin_unlock_irqrestore(&pDevice->lock, flags);
+
+ MACvIntEnable(pDevice->PortOffset, IMR_MASK_VALUE);
+
+ return IRQ_RETVAL(handled);
+}
+
+static int vnt_tx_packet(struct vnt_private *priv, struct sk_buff *skb)
+{
+ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
+ PSTxDesc head_td;
+ u32 dma_idx;
+ unsigned long flags;
+
+ spin_lock_irqsave(&priv->lock, flags);
+
+ if (ieee80211_is_data(hdr->frame_control))
+ dma_idx = TYPE_AC0DMA;
+ else
+ dma_idx = TYPE_TXDMA0;
+
+ if (AVAIL_TD(priv, dma_idx) < 1) {
+ spin_unlock_irqrestore(&priv->lock, flags);
+ return -ENOMEM;
+ }
+
+ head_td = priv->apCurrTD[dma_idx];
+
+ head_td->m_td1TD1.byTCR = 0;
+
+ head_td->pTDInfo->skb = skb;
+
+ if (dma_idx == TYPE_AC0DMA)
+ head_td->pTDInfo->byFlags = TD_FLAGS_NETIF_SKB;
+
+ priv->apCurrTD[dma_idx] = head_td->next;
+
+ spin_unlock_irqrestore(&priv->lock, flags);
+
+ vnt_generate_fifo_header(priv, dma_idx, head_td, skb);
+
+ if (MACbIsRegBitsOn(priv->PortOffset, MAC_REG_PSCTL, PSCTL_PS))
+ MACbPSWakeup(priv->PortOffset);
+
+ spin_lock_irqsave(&priv->lock, flags);
+
+ priv->bPWBitOn = false;
+
+ /* Set TSR1 & ReqCount in TxDescHead */
+ head_td->m_td1TD1.byTCR |= (TCR_STP | TCR_EDP | EDMSDU);
+ head_td->m_td1TD1.wReqCount =
+ cpu_to_le16((u16)head_td->pTDInfo->dwReqCount);
+
+ head_td->buff_addr = cpu_to_le32(head_td->pTDInfo->skb_dma);
+
+ /* Poll Transmit the adapter */
+ wmb();
+ head_td->m_td0TD0.f1Owner = OWNED_BY_NIC;
+ wmb(); /* second memory barrier */
+
+ if (head_td->pTDInfo->byFlags & TD_FLAGS_NETIF_SKB)
+ MACvTransmitAC0(priv->PortOffset);
+ else
+ MACvTransmit0(priv->PortOffset);
+
+ priv->iTDUsed[dma_idx]++;
+
+ spin_unlock_irqrestore(&priv->lock, flags);
+
+ return 0;
+}
+
+static void vnt_tx_80211(struct ieee80211_hw *hw,
+ struct ieee80211_tx_control *control,
+ struct sk_buff *skb)
+{
+ struct vnt_private *priv = hw->priv;
+
+ ieee80211_stop_queues(hw);
+
+ if (vnt_tx_packet(priv, skb)) {
+ ieee80211_free_txskb(hw, skb);
+
+ ieee80211_wake_queues(hw);
+ }
+}
+
+static int vnt_start(struct ieee80211_hw *hw)
+{
+ struct vnt_private *priv = hw->priv;
+ int ret;
+
+ priv->rx_buf_sz = PKT_BUF_SZ;
+ if (!device_init_rings(priv))
+ return -ENOMEM;
+
+ ret = request_irq(priv->pcid->irq, &device_intr,
+ IRQF_SHARED, "vt6655", priv);
+ if (ret) {
+ dev_dbg(&priv->pcid->dev, "failed to start irq\n");
+ return ret;
+ }
+
+ dev_dbg(&priv->pcid->dev, "call device init rd0 ring\n");
+ device_init_rd0_ring(priv);
+ device_init_rd1_ring(priv);
+ device_init_td0_ring(priv);
+ device_init_td1_ring(priv);
+
+ device_init_registers(priv);
+
+ dev_dbg(&priv->pcid->dev, "call MACvIntEnable\n");
+ MACvIntEnable(priv->PortOffset, IMR_MASK_VALUE);
+
+ ieee80211_wake_queues(hw);
+
+ return 0;
+}
+
+static void vnt_stop(struct ieee80211_hw *hw)
+{
+ struct vnt_private *priv = hw->priv;
+
+ ieee80211_stop_queues(hw);
+
+ MACbShutdown(priv->PortOffset);
+ MACbSoftwareReset(priv->PortOffset);
+ CARDbRadioPowerOff(priv);
+
+ device_free_td0_ring(priv);
+ device_free_td1_ring(priv);
+ device_free_rd0_ring(priv);
+ device_free_rd1_ring(priv);
+ device_free_rings(priv);
+
+ free_irq(priv->pcid->irq, priv);
+}
+
+static int vnt_add_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
+{
+ struct vnt_private *priv = hw->priv;
+
+ priv->vif = vif;
+
+ switch (vif->type) {
+ case NL80211_IFTYPE_STATION:
+ break;
+ case NL80211_IFTYPE_ADHOC:
+ MACvRegBitsOff(priv->PortOffset, MAC_REG_RCR, RCR_UNICAST);
+
+ MACvRegBitsOn(priv->PortOffset, MAC_REG_HOSTCR, HOSTCR_ADHOC);
+
+ break;
+ case NL80211_IFTYPE_AP:
+ MACvRegBitsOff(priv->PortOffset, MAC_REG_RCR, RCR_UNICAST);
+
+ MACvRegBitsOn(priv->PortOffset, MAC_REG_HOSTCR, HOSTCR_AP);
+
+ break;
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ priv->op_mode = vif->type;
+
+ return 0;
+}
+
+static void vnt_remove_interface(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif)
+{
+ struct vnt_private *priv = hw->priv;
+
+ switch (vif->type) {
+ case NL80211_IFTYPE_STATION:
+ break;
+ case NL80211_IFTYPE_ADHOC:
+ MACvRegBitsOff(priv->PortOffset, MAC_REG_TCR, TCR_AUTOBCNTX);
+ MACvRegBitsOff(priv->PortOffset,
+ MAC_REG_TFTCTL, TFTCTL_TSFCNTREN);
+ MACvRegBitsOff(priv->PortOffset, MAC_REG_HOSTCR, HOSTCR_ADHOC);
+ break;
+ case NL80211_IFTYPE_AP:
+ MACvRegBitsOff(priv->PortOffset, MAC_REG_TCR, TCR_AUTOBCNTX);
+ MACvRegBitsOff(priv->PortOffset,
+ MAC_REG_TFTCTL, TFTCTL_TSFCNTREN);
+ MACvRegBitsOff(priv->PortOffset, MAC_REG_HOSTCR, HOSTCR_AP);
+ break;
+ default:
+ break;
+ }
+
+ priv->op_mode = NL80211_IFTYPE_UNSPECIFIED;
+}
+
+
+static int vnt_config(struct ieee80211_hw *hw, u32 changed)
+{
+ struct vnt_private *priv = hw->priv;
+ struct ieee80211_conf *conf = &hw->conf;
+ u8 bb_type;
+
+ if (changed & IEEE80211_CONF_CHANGE_PS) {
+ if (conf->flags & IEEE80211_CONF_PS)
+ PSvEnablePowerSaving(priv, conf->listen_interval);
+ else
+ PSvDisablePowerSaving(priv);
+ }
+
+ if ((changed & IEEE80211_CONF_CHANGE_CHANNEL) ||
+ (conf->flags & IEEE80211_CONF_OFFCHANNEL)) {
+ set_channel(priv, conf->chandef.chan);
+
+ if (conf->chandef.chan->band == IEEE80211_BAND_5GHZ)
+ bb_type = BB_TYPE_11A;
+ else
+ bb_type = BB_TYPE_11G;
+
+ if (priv->byBBType != bb_type) {
+ priv->byBBType = bb_type;
+
+ CARDbSetPhyParameter(priv, priv->byBBType);
+ }
+ }
+
+ if (changed & IEEE80211_CONF_CHANGE_POWER) {
+ if (priv->byBBType == BB_TYPE_11B)
+ priv->wCurrentRate = RATE_1M;
+ else
+ priv->wCurrentRate = RATE_54M;
+
+ RFbSetPower(priv, priv->wCurrentRate,
+ conf->chandef.chan->hw_value);
+ }
+
+ return 0;
+}
+
+static void vnt_bss_info_changed(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif, struct ieee80211_bss_conf *conf,
+ u32 changed)
+{
+ struct vnt_private *priv = hw->priv;
+
+ priv->current_aid = conf->aid;
+
+ if (changed & BSS_CHANGED_BSSID && conf->bssid) {
+ unsigned long flags;
+
+ spin_lock_irqsave(&priv->lock, flags);
+
+ MACvWriteBSSIDAddress(priv->PortOffset, (u8 *)conf->bssid);
+
+ spin_unlock_irqrestore(&priv->lock, flags);
+ }
+
+ if (changed & BSS_CHANGED_BASIC_RATES) {
+ priv->basic_rates = conf->basic_rates;
+
+ CARDvUpdateBasicTopRate(priv);
+
+ dev_dbg(&priv->pcid->dev,
+ "basic rates %x\n", conf->basic_rates);
+ }
+
+ if (changed & BSS_CHANGED_ERP_PREAMBLE) {
+ if (conf->use_short_preamble) {
+ MACvEnableBarkerPreambleMd(priv->PortOffset);
+ priv->byPreambleType = true;
+ } else {
+ MACvDisableBarkerPreambleMd(priv->PortOffset);
+ priv->byPreambleType = false;
+ }
+ }
+
+ if (changed & BSS_CHANGED_ERP_CTS_PROT) {
+ if (conf->use_cts_prot)
+ MACvEnableProtectMD(priv->PortOffset);
+ else
+ MACvDisableProtectMD(priv->PortOffset);
+ }
+
+ if (changed & BSS_CHANGED_ERP_SLOT) {
+ if (conf->use_short_slot)
+ priv->bShortSlotTime = true;
+ else
+ priv->bShortSlotTime = false;
+
+ CARDbSetPhyParameter(priv, priv->byBBType);
+ BBvSetVGAGainOffset(priv, priv->abyBBVGA[0]);
+ }
+
+ if (changed & BSS_CHANGED_TXPOWER)
+ RFbSetPower(priv, priv->wCurrentRate,
+ conf->chandef.chan->hw_value);
+
+ if (changed & BSS_CHANGED_BEACON_ENABLED) {
+ dev_dbg(&priv->pcid->dev,
+ "Beacon enable %d\n", conf->enable_beacon);
+
+ if (conf->enable_beacon) {
+ vnt_beacon_enable(priv, vif, conf);
+
+ MACvRegBitsOn(priv->PortOffset, MAC_REG_TCR,
+ TCR_AUTOBCNTX);
+ } else {
+ MACvRegBitsOff(priv->PortOffset, MAC_REG_TCR,
+ TCR_AUTOBCNTX);
+ }
+ }
+
+ if (changed & BSS_CHANGED_ASSOC && priv->op_mode != NL80211_IFTYPE_AP) {
+ if (conf->assoc) {
+ CARDbUpdateTSF(priv, conf->beacon_rate->hw_value,
+ conf->sync_tsf);
+
+ CARDbSetBeaconPeriod(priv, conf->beacon_int);
+
+ CARDvSetFirstNextTBTT(priv, conf->beacon_int);
+ } else {
+ VNSvOutPortB(priv->PortOffset + MAC_REG_TFTCTL,
+ TFTCTL_TSFCNTRST);
+ VNSvOutPortB(priv->PortOffset + MAC_REG_TFTCTL,
+ TFTCTL_TSFCNTREN);
+ }
+ }
+}
+
+static u64 vnt_prepare_multicast(struct ieee80211_hw *hw,
+ struct netdev_hw_addr_list *mc_list)
+{
+ struct vnt_private *priv = hw->priv;
+ struct netdev_hw_addr *ha;
+ u64 mc_filter = 0;
+ u32 bit_nr = 0;
+
+ netdev_hw_addr_list_for_each(ha, mc_list) {
+ bit_nr = ether_crc(ETH_ALEN, ha->addr) >> 26;
+
+ mc_filter |= 1ULL << (bit_nr & 0x3f);
+ }
+
+ priv->mc_list_count = mc_list->count;
+
+ return mc_filter;
+}
+
+static void vnt_configure(struct ieee80211_hw *hw,
+ unsigned int changed_flags, unsigned int *total_flags, u64 multicast)
+{
+ struct vnt_private *priv = hw->priv;
+ u8 rx_mode = 0;
+
+ *total_flags &= FIF_ALLMULTI | FIF_OTHER_BSS | FIF_PROMISC_IN_BSS |
+ FIF_BCN_PRBRESP_PROMISC;
+
+ VNSvInPortB(priv->PortOffset + MAC_REG_RCR, &rx_mode);
+
+ dev_dbg(&priv->pcid->dev, "rx mode in = %x\n", rx_mode);
+
+ if (changed_flags & FIF_PROMISC_IN_BSS) {
+ /* unconditionally log net taps */
+ if (*total_flags & FIF_PROMISC_IN_BSS)
+ rx_mode |= RCR_UNICAST;
+ else
+ rx_mode &= ~RCR_UNICAST;
+ }
+
+ if (changed_flags & FIF_ALLMULTI) {
+ if (*total_flags & FIF_ALLMULTI) {
+ unsigned long flags;
+
+ spin_lock_irqsave(&priv->lock, flags);
+
+ if (priv->mc_list_count > 2) {
+ MACvSelectPage1(priv->PortOffset);
+
+ VNSvOutPortD(priv->PortOffset +
+ MAC_REG_MAR0, 0xffffffff);
+ VNSvOutPortD(priv->PortOffset +
+ MAC_REG_MAR0 + 4, 0xffffffff);
+
+ MACvSelectPage0(priv->PortOffset);
+ } else {
+ MACvSelectPage1(priv->PortOffset);
+
+ VNSvOutPortD(priv->PortOffset +
+ MAC_REG_MAR0, (u32)multicast);
+ VNSvOutPortD(priv->PortOffset +
+ MAC_REG_MAR0 + 4,
+ (u32)(multicast >> 32));
+
+ MACvSelectPage0(priv->PortOffset);
+ }
+
+ spin_unlock_irqrestore(&priv->lock, flags);
+
+ rx_mode |= RCR_MULTICAST | RCR_BROADCAST;
+ } else {
+ rx_mode &= ~(RCR_MULTICAST | RCR_BROADCAST);
+ }
+ }
+
+ if (changed_flags & (FIF_OTHER_BSS | FIF_BCN_PRBRESP_PROMISC)) {
+ rx_mode |= RCR_MULTICAST | RCR_BROADCAST;
+
+ if (*total_flags & (FIF_OTHER_BSS | FIF_BCN_PRBRESP_PROMISC))
+ rx_mode &= ~RCR_BSSID;
+ else
+ rx_mode |= RCR_BSSID;
+ }
+
+ VNSvOutPortB(priv->PortOffset + MAC_REG_RCR, rx_mode);
+
+ dev_dbg(&priv->pcid->dev, "rx mode out= %x\n", rx_mode);
+}
+
+static int vnt_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
+ struct ieee80211_vif *vif, struct ieee80211_sta *sta,
+ struct ieee80211_key_conf *key)
+{
+ struct vnt_private *priv = hw->priv;
+
+ switch (cmd) {
+ case SET_KEY:
+ if (vnt_set_keys(hw, sta, vif, key))
+ return -EOPNOTSUPP;
+ break;
+ case DISABLE_KEY:
+ if (test_bit(key->hw_key_idx, &priv->key_entry_inuse))
+ clear_bit(key->hw_key_idx, &priv->key_entry_inuse);
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static u64 vnt_get_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
+{
+ struct vnt_private *priv = hw->priv;
+ u64 tsf;
+
+ CARDbGetCurrentTSF(priv, &tsf);
+
+ return tsf;
+}
+
+static void vnt_set_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ u64 tsf)
+{
+ struct vnt_private *priv = hw->priv;
+
+ CARDvUpdateNextTBTT(priv, tsf, vif->bss_conf.beacon_int);
+}
+
+static void vnt_reset_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
+{
+ struct vnt_private *priv = hw->priv;
+
+ /* reset TSF counter */
+ VNSvOutPortB(priv->PortOffset + MAC_REG_TFTCTL, TFTCTL_TSFCNTRST);
+}
+
+static const struct ieee80211_ops vnt_mac_ops = {
+ .tx = vnt_tx_80211,
+ .start = vnt_start,
+ .stop = vnt_stop,
+ .add_interface = vnt_add_interface,
+ .remove_interface = vnt_remove_interface,
+ .config = vnt_config,
+ .bss_info_changed = vnt_bss_info_changed,
+ .prepare_multicast = vnt_prepare_multicast,
+ .configure_filter = vnt_configure,
+ .set_key = vnt_set_key,
+ .get_tsf = vnt_get_tsf,
+ .set_tsf = vnt_set_tsf,
+ .reset_tsf = vnt_reset_tsf,
+};
+
+static int vnt_init(struct vnt_private *priv)
+{
+ SET_IEEE80211_PERM_ADDR(priv->hw, priv->abyCurrentNetAddr);
+
+ vnt_init_bands(priv);
+
+ if (ieee80211_register_hw(priv->hw))
+ return -ENODEV;
+
+ priv->mac_hw = true;
+
+ CARDbRadioPowerOff(priv);
+
+ return 0;
+}
+
+static int
+vt6655_probe(struct pci_dev *pcid, const struct pci_device_id *ent)
+{
+ PCHIP_INFO pChip_info = (PCHIP_INFO)ent->driver_data;
+ struct vnt_private *priv;
+ struct ieee80211_hw *hw;
+ struct wiphy *wiphy;
+ int rc;
+
+ dev_notice(&pcid->dev,
+ "%s Ver. %s\n", DEVICE_FULL_DRV_NAM, DEVICE_VERSION);
+
+ dev_notice(&pcid->dev,
+ "Copyright (c) 2003 VIA Networking Technologies, Inc.\n");
+
+ hw = ieee80211_alloc_hw(sizeof(*priv), &vnt_mac_ops);
+ if (!hw) {
+ dev_err(&pcid->dev, "could not register ieee80211_hw\n");
+ return -ENOMEM;
+ }
+
+ priv = hw->priv;
+
+ vt6655_init_info(pcid, &priv, pChip_info);
+
+ priv->hw = hw;
+
+ SET_IEEE80211_DEV(priv->hw, &pcid->dev);
+
+ if (pci_enable_device(pcid)) {
+ device_free_info(priv);
+ return -ENODEV;
+ }
+
+ dev_dbg(&pcid->dev,
+ "Before get pci_info memaddr is %x\n", priv->memaddr);
+
+ if (!device_get_pci_info(priv, pcid)) {
+ dev_err(&pcid->dev, ": Failed to find PCI device.\n");
+ device_free_info(priv);
+ return -ENODEV;
+ }
+
+#ifdef DEBUG
+ dev_dbg(&pcid->dev,
+ "after get pci_info memaddr is %x, io addr is %x,io_size is %d\n",
+ priv->memaddr, priv->ioaddr, priv->io_size);
+ {
+ int i;
+ u32 bar, len;
+ u32 address[] = {
+ PCI_BASE_ADDRESS_0,
+ PCI_BASE_ADDRESS_1,
+ PCI_BASE_ADDRESS_2,
+ PCI_BASE_ADDRESS_3,
+ PCI_BASE_ADDRESS_4,
+ PCI_BASE_ADDRESS_5,
+ 0};
+ for (i = 0; address[i]; i++) {
+ pci_read_config_dword(pcid, address[i], &bar);
+
+ dev_dbg(&pcid->dev, "bar %d is %x\n", i, bar);
+
+ if (!bar) {
+ dev_dbg(&pcid->dev,
+ "bar %d not implemented\n", i);
+ continue;
+ }
+
+ if (bar & PCI_BASE_ADDRESS_SPACE_IO) {
+ /* This is IO */
+
+ len = bar & (PCI_BASE_ADDRESS_IO_MASK & 0xffff);
+ len = len & ~(len - 1);
+
+ dev_dbg(&pcid->dev,
+ "IO space: len in IO %x, BAR %d\n",
+ len, i);
+ } else {
+ len = bar & 0xfffffff0;
+ len = ~len + 1;
+
+ dev_dbg(&pcid->dev,
+ "len in MEM %x, BAR %d\n", len, i);
+ }
+ }
+ }
+#endif
+
+ priv->PortOffset = ioremap(priv->memaddr & PCI_BASE_ADDRESS_MEM_MASK,
+ priv->io_size);
+ if (!priv->PortOffset) {
+ dev_err(&pcid->dev, ": Failed to IO remapping ..\n");
+ device_free_info(priv);
+ return -ENODEV;
+ }
+
+ rc = pci_request_regions(pcid, DEVICE_NAME);
+ if (rc) {
+ dev_err(&pcid->dev, ": Failed to find PCI device\n");
+ device_free_info(priv);
+ return -ENODEV;
+ }
+
+ /* do reset */
+ if (!MACbSoftwareReset(priv->PortOffset)) {
+ dev_err(&pcid->dev, ": Failed to access MAC hardware..\n");
+ device_free_info(priv);
+ return -ENODEV;
+ }
+ /* initial to reload eeprom */
+ MACvInitialize(priv->PortOffset);
+ MACvReadEtherAddress(priv->PortOffset, priv->abyCurrentNetAddr);
+
+ /* Get RFType */
+ priv->byRFType = SROMbyReadEmbedded(priv->PortOffset, EEP_OFS_RFTYPE);
+ priv->byRFType &= RF_MASK;
+
+ dev_dbg(&pcid->dev, "RF Type = %x\n", priv->byRFType);
+
+ device_get_options(priv);
+ device_set_options(priv);
+ /* Mask out the options cannot be set to the chip */
+ priv->sOpts.flags &= pChip_info->flags;
+
+ /* Enable the chip specified capabilities */
+ priv->flags = priv->sOpts.flags | (pChip_info->flags & 0xff000000UL);
+
+ wiphy = priv->hw->wiphy;
+
+ wiphy->frag_threshold = FRAG_THRESH_DEF;
+ wiphy->rts_threshold = RTS_THRESH_DEF;
+ wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) |
+ BIT(NL80211_IFTYPE_ADHOC) | BIT(NL80211_IFTYPE_AP);
+
+ priv->hw->flags = IEEE80211_HW_RX_INCLUDES_FCS |
+ IEEE80211_HW_REPORTS_TX_ACK_STATUS |
+ IEEE80211_HW_SIGNAL_DBM |
+ IEEE80211_HW_TIMING_BEACON_ONLY;
+
+ priv->hw->max_signal = 100;
+
+ if (vnt_init(priv))
+ return -ENODEV;
+
+ device_print_info(priv);
+ pci_set_drvdata(pcid, priv);
+
+ return 0;
+}
+
+/*------------------------------------------------------------------*/
+
+#ifdef CONFIG_PM
+static int vt6655_suspend(struct pci_dev *pcid, pm_message_t state)
+{
+ struct vnt_private *priv = pci_get_drvdata(pcid);
+ unsigned long flags;
+
+ spin_lock_irqsave(&priv->lock, flags);
+
+ pci_save_state(pcid);
+
+ MACbShutdown(priv->PortOffset);
+
+ pci_disable_device(pcid);
+ pci_set_power_state(pcid, pci_choose_state(pcid, state));
+
+ spin_unlock_irqrestore(&priv->lock, flags);
+
+ return 0;
+}
+
+static int vt6655_resume(struct pci_dev *pcid)
+{
+
+ pci_set_power_state(pcid, PCI_D0);
+ pci_enable_wake(pcid, PCI_D0, 0);
+ pci_restore_state(pcid);
+
+ return 0;
+}
+#endif
+
+MODULE_DEVICE_TABLE(pci, vt6655_pci_id_table);
+
+static struct pci_driver device_driver = {
+ .name = DEVICE_NAME,
+ .id_table = vt6655_pci_id_table,
+ .probe = vt6655_probe,
+ .remove = vt6655_remove,
+#ifdef CONFIG_PM
+ .suspend = vt6655_suspend,
+ .resume = vt6655_resume,
+#endif
+};
+
+static int __init vt6655_init_module(void)
+{
+ int ret;
+
+ ret = pci_register_driver(&device_driver);
+#ifdef CONFIG_PM
+ if (ret >= 0)
+ register_reboot_notifier(&device_notifier);
+#endif
+
+ return ret;
+}
+
+static void __exit vt6655_cleanup_module(void)
+{
+#ifdef CONFIG_PM
+ unregister_reboot_notifier(&device_notifier);
+#endif
+ pci_unregister_driver(&device_driver);
+}
+
+module_init(vt6655_init_module);
+module_exit(vt6655_cleanup_module);
+
+#ifdef CONFIG_PM
+static int
+device_notify_reboot(struct notifier_block *nb, unsigned long event, void *p)
+{
+ struct pci_dev *pdev = NULL;
+
+ switch (event) {
+ case SYS_DOWN:
+ case SYS_HALT:
+ case SYS_POWER_OFF:
+ for_each_pci_dev(pdev) {
+ if (pci_dev_driver(pdev) == &device_driver) {
+ if (pci_get_drvdata(pdev))
+ vt6655_suspend(pdev, PMSG_HIBERNATE);
+ }
+ }
+ }
+ return NOTIFY_DONE;
+}
+#endif
diff --git a/drivers/staging/vt6655/dpc.c b/drivers/staging/vt6655/dpc.c
new file mode 100644
index 000000000..b25ee9625
--- /dev/null
+++ b/drivers/staging/vt6655/dpc.c
@@ -0,0 +1,162 @@
+/*
+ * Copyright (c) 1996, 2003 VIA Networking Technologies, Inc.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * File: dpc.c
+ *
+ * Purpose: handle dpc rx functions
+ *
+ * Author: Lyndon Chen
+ *
+ * Date: May 20, 2003
+ *
+ * Functions:
+ *
+ * Revision History:
+ *
+ */
+
+#include "device.h"
+#include "baseband.h"
+#include "rf.h"
+#include "dpc.h"
+
+static bool vnt_rx_data(struct vnt_private *priv, struct sk_buff *skb,
+ u16 bytes_received)
+{
+ struct ieee80211_hw *hw = priv->hw;
+ struct ieee80211_supported_band *sband;
+ struct ieee80211_rx_status rx_status = { 0 };
+ struct ieee80211_hdr *hdr;
+ __le16 fc;
+ u8 *rsr, *new_rsr, *rssi;
+ __le64 *tsf_time;
+ u16 frame_size;
+ int ii, r;
+ u8 *rx_sts, *rx_rate, *sq;
+ u8 *skb_data;
+ u8 rate_idx = 0;
+ u8 rate[MAX_RATE] = {2, 4, 11, 22, 12, 18, 24, 36, 48, 72, 96, 108};
+ long rx_dbm;
+
+ /* [31:16]RcvByteCount ( not include 4-byte Status ) */
+ frame_size = le16_to_cpu(*((__le16 *)(skb->data + 2)));
+ if (frame_size > 2346 || frame_size < 14) {
+ dev_dbg(&priv->pcid->dev, "------- WRONG Length 1\n");
+ return false;
+ }
+
+ skb_data = (u8 *)skb->data;
+
+ rx_sts = skb_data;
+ rx_rate = skb_data + 1;
+
+ sband = hw->wiphy->bands[hw->conf.chandef.chan->band];
+
+ for (r = RATE_1M; r < MAX_RATE; r++) {
+ if (*rx_rate == rate[r])
+ break;
+ }
+
+ priv->rx_rate = r;
+
+ for (ii = 0; ii < sband->n_bitrates; ii++) {
+ if (sband->bitrates[ii].hw_value == r) {
+ rate_idx = ii;
+ break;
+ }
+ }
+
+ if (ii == sband->n_bitrates) {
+ dev_dbg(&priv->pcid->dev, "Wrong RxRate %x\n", *rx_rate);
+ return false;
+ }
+
+ tsf_time = (__le64 *)(skb_data + bytes_received - 12);
+ sq = skb_data + bytes_received - 4;
+ new_rsr = skb_data + bytes_received - 3;
+ rssi = skb_data + bytes_received - 2;
+ rsr = skb_data + bytes_received - 1;
+ if (*rsr & (RSR_IVLDTYP | RSR_IVLDLEN))
+ return false;
+
+ RFvRSSITodBm(priv, *rssi, &rx_dbm);
+
+ priv->byBBPreEDRSSI = (u8)rx_dbm + 1;
+ priv->uCurrRSSI = *rssi;
+
+ skb_pull(skb, 4);
+ skb_trim(skb, frame_size);
+
+ rx_status.mactime = le64_to_cpu(*tsf_time);
+ rx_status.band = hw->conf.chandef.chan->band;
+ rx_status.signal = rx_dbm;
+ rx_status.flag = 0;
+ rx_status.freq = hw->conf.chandef.chan->center_freq;
+
+ if (!(*rsr & RSR_CRCOK))
+ rx_status.flag |= RX_FLAG_FAILED_FCS_CRC;
+
+ hdr = (struct ieee80211_hdr *)(skb->data);
+ fc = hdr->frame_control;
+
+ rx_status.rate_idx = rate_idx;
+
+ if (ieee80211_has_protected(fc)) {
+ if (priv->byLocalID > REV_ID_VT3253_A1)
+ rx_status.flag |= RX_FLAG_DECRYPTED;
+
+ /* Drop packet */
+ if (!(*new_rsr & NEWRSR_DECRYPTOK))
+ return false;
+ }
+
+ memcpy(IEEE80211_SKB_RXCB(skb), &rx_status, sizeof(rx_status));
+
+ ieee80211_rx_irqsafe(priv->hw, skb);
+
+ return true;
+}
+
+bool vnt_receive_frame(struct vnt_private *priv, PSRxDesc curr_rd)
+{
+ PDEVICE_RD_INFO rd_info = curr_rd->pRDInfo;
+ struct sk_buff *skb;
+ u16 frame_size;
+
+ skb = rd_info->skb;
+
+ dma_unmap_single(&priv->pcid->dev, rd_info->skb_dma,
+ priv->rx_buf_sz, DMA_FROM_DEVICE);
+
+ frame_size = le16_to_cpu(curr_rd->m_rd1RD1.wReqCount)
+ - cpu_to_le16(curr_rd->m_rd0RD0.wResCount);
+
+ if ((frame_size > 2364) || (frame_size < 33)) {
+ /* Frame Size error drop this packet.*/
+ dev_dbg(&priv->pcid->dev, "Wrong frame size %d\n", frame_size);
+ dev_kfree_skb_irq(skb);
+ return true;
+ }
+
+ if (vnt_rx_data(priv, skb, frame_size))
+ return true;
+
+ dev_kfree_skb_irq(skb);
+
+ return true;
+}
diff --git a/drivers/staging/vt6655/dpc.h b/drivers/staging/vt6655/dpc.h
new file mode 100644
index 000000000..ad495719a
--- /dev/null
+++ b/drivers/staging/vt6655/dpc.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 1996, 2003 VIA Networking Technologies, Inc.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * File: dpc.h
+ *
+ * Purpose:
+ *
+ * Author: Jerry Chen
+ *
+ * Date: Jun. 27, 2002
+ *
+ */
+
+#ifndef __DPC_H__
+#define __DPC_H__
+
+#include "device.h"
+
+bool vnt_receive_frame(struct vnt_private *priv, PSRxDesc curr_rd);
+
+#endif /* __RXTX_H__ */
diff --git a/drivers/staging/vt6655/key.c b/drivers/staging/vt6655/key.c
new file mode 100644
index 000000000..f2b3fea90
--- /dev/null
+++ b/drivers/staging/vt6655/key.c
@@ -0,0 +1,168 @@
+/*
+ * Copyright (c) 1996, 2003 VIA Networking Technologies, Inc.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ *
+ * File: key.c
+ *
+ * Purpose: Implement functions for 802.11i Key management
+ *
+ * Author: Jerry Chen
+ *
+ * Date: May 29, 2003
+ *
+ */
+
+#include "tmacro.h"
+#include "key.h"
+#include "mac.h"
+
+int vnt_key_init_table(struct vnt_private *priv)
+{
+ u32 i;
+
+ for (i = 0; i < MAX_KEY_TABLE; i++)
+ MACvDisableKeyEntry(priv->PortOffset, i);
+
+ return 0;
+}
+
+static int vnt_set_keymode(struct ieee80211_hw *hw, u8 *mac_addr,
+ struct ieee80211_key_conf *key, u32 key_type, u32 mode,
+ bool onfly_latch)
+{
+ struct vnt_private *priv = hw->priv;
+ u8 broadcast[6] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
+ u16 key_mode = 0;
+ u32 entry = 0;
+ u8 *bssid;
+ u8 key_inx = key->keyidx;
+ u8 i;
+
+ if (mac_addr)
+ bssid = mac_addr;
+ else
+ bssid = &broadcast[0];
+
+ if (key_type != VNT_KEY_DEFAULTKEY) {
+ for (i = 0; i < (MAX_KEY_TABLE - 1); i++) {
+ if (!test_bit(i, &priv->key_entry_inuse)) {
+ set_bit(i, &priv->key_entry_inuse);
+
+ key->hw_key_idx = i;
+ entry = key->hw_key_idx;
+ break;
+ }
+ }
+ }
+
+ switch (key_type) {
+ /* fallthrough */
+ case VNT_KEY_DEFAULTKEY:
+ /* default key last entry */
+ entry = MAX_KEY_TABLE - 1;
+ key->hw_key_idx = entry;
+ case VNT_KEY_ALLGROUP:
+ key_mode |= VNT_KEY_ALLGROUP;
+ if (onfly_latch)
+ key_mode |= VNT_KEY_ONFLY_ALL;
+ case VNT_KEY_GROUP_ADDRESS:
+ key_mode |= mode;
+ case VNT_KEY_GROUP:
+ key_mode |= (mode << 4);
+ key_mode |= VNT_KEY_GROUP;
+ break;
+ case VNT_KEY_PAIRWISE:
+ key_mode |= mode;
+ key_inx = 4;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (onfly_latch)
+ key_mode |= VNT_KEY_ONFLY;
+
+ if (mode == KEY_CTL_WEP) {
+ if (key->keylen == WLAN_KEY_LEN_WEP40)
+ key->key[15] &= 0x7f;
+ if (key->keylen == WLAN_KEY_LEN_WEP104)
+ key->key[15] |= 0x80;
+ }
+
+ MACvSetKeyEntry(priv->PortOffset, key_mode, entry, key_inx,
+ bssid, (u32 *)key->key, priv->byLocalID);
+
+ return 0;
+}
+
+int vnt_set_keys(struct ieee80211_hw *hw, struct ieee80211_sta *sta,
+ struct ieee80211_vif *vif, struct ieee80211_key_conf *key)
+{
+ struct ieee80211_bss_conf *conf = &vif->bss_conf;
+ struct vnt_private *priv = hw->priv;
+ u8 *mac_addr = NULL;
+ u8 key_dec_mode = 0;
+ int ret = 0;
+ u32 u;
+
+ if (sta)
+ mac_addr = &sta->addr[0];
+
+ switch (key->cipher) {
+ case 0:
+ for (u = 0 ; u < MAX_KEY_TABLE; u++)
+ MACvDisableKeyEntry(priv->PortOffset, u);
+ return ret;
+
+ case WLAN_CIPHER_SUITE_WEP40:
+ case WLAN_CIPHER_SUITE_WEP104:
+ for (u = 0; u < MAX_KEY_TABLE; u++)
+ MACvDisableKeyEntry(priv->PortOffset, u);
+
+ vnt_set_keymode(hw, mac_addr,
+ key, VNT_KEY_DEFAULTKEY, KEY_CTL_WEP, true);
+
+ key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV;
+
+ return ret;
+ case WLAN_CIPHER_SUITE_TKIP:
+ key->flags |= IEEE80211_KEY_FLAG_GENERATE_MMIC;
+ key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV;
+
+ key_dec_mode = KEY_CTL_TKIP;
+
+ break;
+ case WLAN_CIPHER_SUITE_CCMP:
+ key_dec_mode = KEY_CTL_CCMP;
+
+ key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV;
+ }
+
+ if (key->flags & IEEE80211_KEY_FLAG_PAIRWISE) {
+ vnt_set_keymode(hw, mac_addr,
+ key, VNT_KEY_PAIRWISE, key_dec_mode, true);
+ } else {
+ vnt_set_keymode(hw, mac_addr,
+ key, VNT_KEY_DEFAULTKEY, key_dec_mode, true);
+
+ vnt_set_keymode(hw, (u8 *)conf->bssid,
+ key, VNT_KEY_GROUP_ADDRESS, key_dec_mode, true);
+ }
+
+ return 0;
+}
diff --git a/drivers/staging/vt6655/key.h b/drivers/staging/vt6655/key.h
new file mode 100644
index 000000000..261f8181d
--- /dev/null
+++ b/drivers/staging/vt6655/key.h
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 1996, 2003 VIA Networking Technologies, Inc.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ *
+ * File: key.h
+ *
+ * Purpose: Implement functions for 802.11i Key management
+ *
+ * Author: Jerry Chen
+ *
+ * Date: May 29, 2003
+ *
+ */
+
+#ifndef __KEY_H__
+#define __KEY_H__
+
+#include <net/mac80211.h>
+
+/*--------------------- Export Definitions -------------------------*/
+#define MAX_GROUP_KEY 4
+#define MAX_KEY_TABLE 11
+#define MAX_KEY_LEN 32
+#define AES_KEY_LEN 16
+
+#define AUTHENTICATOR_KEY 0x10000000
+#define USE_KEYRSC 0x20000000
+#define PAIRWISE_KEY 0x40000000
+#define TRANSMIT_KEY 0x80000000
+
+#define GROUP_KEY 0x00000000
+
+#define KEY_CTL_WEP 0x00
+#define KEY_CTL_NONE 0x01
+#define KEY_CTL_TKIP 0x02
+#define KEY_CTL_CCMP 0x03
+#define KEY_CTL_INVALID 0xFF
+
+#define VNT_KEY_DEFAULTKEY 0x1
+#define VNT_KEY_GROUP_ADDRESS 0x2
+#define VNT_KEY_ALLGROUP 0x4
+#define VNT_KEY_GROUP 0x40
+#define VNT_KEY_PAIRWISE 0x00
+#define VNT_KEY_ONFLY 0x8000
+#define VNT_KEY_ONFLY_ALL 0x4000
+
+struct vnt_private;
+
+int vnt_key_init_table(struct vnt_private *);
+
+int vnt_set_keys(struct ieee80211_hw *hw, struct ieee80211_sta *sta,
+ struct ieee80211_vif *vif, struct ieee80211_key_conf *key);
+
+#endif /* __KEY_H__ */
diff --git a/drivers/staging/vt6655/mac.c b/drivers/staging/vt6655/mac.c
new file mode 100644
index 000000000..8048b3263
--- /dev/null
+++ b/drivers/staging/vt6655/mac.c
@@ -0,0 +1,895 @@
+/*
+ * Copyright (c) 1996, 2003 VIA Networking Technologies, Inc.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ *
+ * File: mac.c
+ *
+ * Purpose: MAC routines
+ *
+ * Author: Tevin Chen
+ *
+ * Date: May 21, 1996
+ *
+ * Functions:
+ * MACbIsRegBitsOn - Test if All test Bits On
+ * MACbIsRegBitsOff - Test if All test Bits Off
+ * MACbIsIntDisable - Test if MAC interrupt disable
+ * MACvSetShortRetryLimit - Set 802.11 Short Retry limit
+ * MACvSetLongRetryLimit - Set 802.11 Long Retry limit
+ * MACvSetLoopbackMode - Set MAC Loopback Mode
+ * MACvSaveContext - Save Context of MAC Registers
+ * MACvRestoreContext - Restore Context of MAC Registers
+ * MACbSoftwareReset - Software Reset MAC
+ * MACbSafeRxOff - Turn Off MAC Rx
+ * MACbSafeTxOff - Turn Off MAC Tx
+ * MACbSafeStop - Stop MAC function
+ * MACbShutdown - Shut down MAC
+ * MACvInitialize - Initialize MAC
+ * MACvSetCurrRxDescAddr - Set Rx Descriptors Address
+ * MACvSetCurrTx0DescAddr - Set Tx0 Descriptors Address
+ * MACvSetCurrTx1DescAddr - Set Tx1 Descriptors Address
+ * MACvTimer0MicroSDelay - Micro Second Delay Loop by MAC
+ *
+ * Revision History:
+ * 08-22-2003 Kyle Hsu : Porting MAC functions from sim53
+ * 09-03-2003 Bryan YC Fan : Add MACvClearBusSusInd()& MACvEnableBusSusEn()
+ * 09-18-2003 Jerry Chen : Add MACvSetKeyEntry & MACvDisableKeyEntry
+ *
+ */
+
+#include "tmacro.h"
+#include "mac.h"
+
+/*
+ * Description:
+ * Test if all test bits on
+ *
+ * Parameters:
+ * In:
+ * dwIoBase - Base Address for MAC
+ * byRegOfs - Offset of MAC Register
+ * byTestBits - Test bits
+ * Out:
+ * none
+ *
+ * Return Value: true if all test bits On; otherwise false
+ *
+ */
+bool MACbIsRegBitsOn(void __iomem *dwIoBase, unsigned char byRegOfs, unsigned char byTestBits)
+{
+ unsigned char byData;
+
+ VNSvInPortB(dwIoBase + byRegOfs, &byData);
+ return (byData & byTestBits) == byTestBits;
+}
+
+/*
+ * Description:
+ * Test if all test bits off
+ *
+ * Parameters:
+ * In:
+ * dwIoBase - Base Address for MAC
+ * byRegOfs - Offset of MAC Register
+ * byTestBits - Test bits
+ * Out:
+ * none
+ *
+ * Return Value: true if all test bits Off; otherwise false
+ *
+ */
+bool MACbIsRegBitsOff(void __iomem *dwIoBase, unsigned char byRegOfs, unsigned char byTestBits)
+{
+ unsigned char byData;
+
+ VNSvInPortB(dwIoBase + byRegOfs, &byData);
+ return !(byData & byTestBits);
+}
+
+/*
+ * Description:
+ * Test if MAC interrupt disable
+ *
+ * Parameters:
+ * In:
+ * dwIoBase - Base Address for MAC
+ * Out:
+ * none
+ *
+ * Return Value: true if interrupt is disable; otherwise false
+ *
+ */
+bool MACbIsIntDisable(void __iomem *dwIoBase)
+{
+ unsigned long dwData;
+
+ VNSvInPortD(dwIoBase + MAC_REG_IMR, &dwData);
+ if (dwData != 0)
+ return false;
+
+ return true;
+}
+
+/*
+ * Description:
+ * Set 802.11 Short Retry Limit
+ *
+ * Parameters:
+ * In:
+ * dwIoBase - Base Address for MAC
+ * byRetryLimit- Retry Limit
+ * Out:
+ * none
+ *
+ * Return Value: none
+ *
+ */
+void MACvSetShortRetryLimit(void __iomem *dwIoBase, unsigned char byRetryLimit)
+{
+ /* set SRT */
+ VNSvOutPortB(dwIoBase + MAC_REG_SRT, byRetryLimit);
+}
+
+
+/*
+ * Description:
+ * Set 802.11 Long Retry Limit
+ *
+ * Parameters:
+ * In:
+ * dwIoBase - Base Address for MAC
+ * byRetryLimit- Retry Limit
+ * Out:
+ * none
+ *
+ * Return Value: none
+ *
+ */
+void MACvSetLongRetryLimit(void __iomem *dwIoBase, unsigned char byRetryLimit)
+{
+ /* set LRT */
+ VNSvOutPortB(dwIoBase + MAC_REG_LRT, byRetryLimit);
+}
+
+/*
+ * Description:
+ * Set MAC Loopback mode
+ *
+ * Parameters:
+ * In:
+ * dwIoBase - Base Address for MAC
+ * byLoopbackMode - Loopback Mode
+ * Out:
+ * none
+ *
+ * Return Value: none
+ *
+ */
+void MACvSetLoopbackMode(void __iomem *dwIoBase, unsigned char byLoopbackMode)
+{
+ unsigned char byOrgValue;
+
+ ASSERT(byLoopbackMode < 3);
+ byLoopbackMode <<= 6;
+ /* set TCR */
+ VNSvInPortB(dwIoBase + MAC_REG_TEST, &byOrgValue);
+ byOrgValue = byOrgValue & 0x3F;
+ byOrgValue = byOrgValue | byLoopbackMode;
+ VNSvOutPortB(dwIoBase + MAC_REG_TEST, byOrgValue);
+}
+
+/*
+ * Description:
+ * Save MAC registers to context buffer
+ *
+ * Parameters:
+ * In:
+ * dwIoBase - Base Address for MAC
+ * Out:
+ * pbyCxtBuf - Context buffer
+ *
+ * Return Value: none
+ *
+ */
+void MACvSaveContext(void __iomem *dwIoBase, unsigned char *pbyCxtBuf)
+{
+ int ii;
+
+ /* read page0 register */
+ for (ii = 0; ii < MAC_MAX_CONTEXT_SIZE_PAGE0; ii++)
+ VNSvInPortB((dwIoBase + ii), (pbyCxtBuf + ii));
+
+ MACvSelectPage1(dwIoBase);
+
+ /* read page1 register */
+ for (ii = 0; ii < MAC_MAX_CONTEXT_SIZE_PAGE1; ii++)
+ VNSvInPortB((dwIoBase + ii), (pbyCxtBuf + MAC_MAX_CONTEXT_SIZE_PAGE0 + ii));
+
+ MACvSelectPage0(dwIoBase);
+}
+
+/*
+ * Description:
+ * Restore MAC registers from context buffer
+ *
+ * Parameters:
+ * In:
+ * dwIoBase - Base Address for MAC
+ * pbyCxtBuf - Context buffer
+ * Out:
+ * none
+ *
+ * Return Value: none
+ *
+ */
+void MACvRestoreContext(void __iomem *dwIoBase, unsigned char *pbyCxtBuf)
+{
+ int ii;
+
+ MACvSelectPage1(dwIoBase);
+ /* restore page1 */
+ for (ii = 0; ii < MAC_MAX_CONTEXT_SIZE_PAGE1; ii++)
+ VNSvOutPortB((dwIoBase + ii), *(pbyCxtBuf + MAC_MAX_CONTEXT_SIZE_PAGE0 + ii));
+
+ MACvSelectPage0(dwIoBase);
+
+ /* restore RCR,TCR,IMR... */
+ for (ii = MAC_REG_RCR; ii < MAC_REG_ISR; ii++)
+ VNSvOutPortB(dwIoBase + ii, *(pbyCxtBuf + ii));
+
+ /* restore MAC Config. */
+ for (ii = MAC_REG_LRT; ii < MAC_REG_PAGE1SEL; ii++)
+ VNSvOutPortB(dwIoBase + ii, *(pbyCxtBuf + ii));
+
+ VNSvOutPortB(dwIoBase + MAC_REG_CFG, *(pbyCxtBuf + MAC_REG_CFG));
+
+ /* restore PS Config. */
+ for (ii = MAC_REG_PSCFG; ii < MAC_REG_BBREGCTL; ii++)
+ VNSvOutPortB(dwIoBase + ii, *(pbyCxtBuf + ii));
+
+ /* restore CURR_RX_DESC_ADDR, CURR_TX_DESC_ADDR */
+ VNSvOutPortD(dwIoBase + MAC_REG_TXDMAPTR0, *(unsigned long *)(pbyCxtBuf + MAC_REG_TXDMAPTR0));
+ VNSvOutPortD(dwIoBase + MAC_REG_AC0DMAPTR, *(unsigned long *)(pbyCxtBuf + MAC_REG_AC0DMAPTR));
+ VNSvOutPortD(dwIoBase + MAC_REG_BCNDMAPTR, *(unsigned long *)(pbyCxtBuf + MAC_REG_BCNDMAPTR));
+
+ VNSvOutPortD(dwIoBase + MAC_REG_RXDMAPTR0, *(unsigned long *)(pbyCxtBuf + MAC_REG_RXDMAPTR0));
+
+ VNSvOutPortD(dwIoBase + MAC_REG_RXDMAPTR1, *(unsigned long *)(pbyCxtBuf + MAC_REG_RXDMAPTR1));
+}
+
+/*
+ * Description:
+ * Software Reset MAC
+ *
+ * Parameters:
+ * In:
+ * dwIoBase - Base Address for MAC
+ * Out:
+ * none
+ *
+ * Return Value: true if Reset Success; otherwise false
+ *
+ */
+bool MACbSoftwareReset(void __iomem *dwIoBase)
+{
+ unsigned char byData;
+ unsigned short ww;
+
+ /* turn on HOSTCR_SOFTRST, just write 0x01 to reset */
+ VNSvOutPortB(dwIoBase + MAC_REG_HOSTCR, 0x01);
+
+ for (ww = 0; ww < W_MAX_TIMEOUT; ww++) {
+ VNSvInPortB(dwIoBase + MAC_REG_HOSTCR, &byData);
+ if (!(byData & HOSTCR_SOFTRST))
+ break;
+ }
+ if (ww == W_MAX_TIMEOUT)
+ return false;
+ return true;
+}
+
+/*
+ * Description:
+ * save some important register's value, then do reset, then restore register's value
+ *
+ * Parameters:
+ * In:
+ * dwIoBase - Base Address for MAC
+ * Out:
+ * none
+ *
+ * Return Value: true if success; otherwise false
+ *
+ */
+bool MACbSafeSoftwareReset(void __iomem *dwIoBase)
+{
+ unsigned char abyTmpRegData[MAC_MAX_CONTEXT_SIZE_PAGE0+MAC_MAX_CONTEXT_SIZE_PAGE1];
+ bool bRetVal;
+
+ /* PATCH....
+ * save some important register's value, then do
+ * reset, then restore register's value
+ */
+ /* save MAC context */
+ MACvSaveContext(dwIoBase, abyTmpRegData);
+ /* do reset */
+ bRetVal = MACbSoftwareReset(dwIoBase);
+ /* restore MAC context, except CR0 */
+ MACvRestoreContext(dwIoBase, abyTmpRegData);
+
+ return bRetVal;
+}
+
+/*
+ * Description:
+ * Turn Off MAC Rx
+ *
+ * Parameters:
+ * In:
+ * dwIoBase - Base Address for MAC
+ * Out:
+ * none
+ *
+ * Return Value: true if success; otherwise false
+ *
+ */
+bool MACbSafeRxOff(void __iomem *dwIoBase)
+{
+ unsigned short ww;
+ unsigned long dwData;
+ unsigned char byData;
+
+ /* turn off wow temp for turn off Rx safely */
+
+ /* Clear RX DMA0,1 */
+ VNSvOutPortD(dwIoBase + MAC_REG_RXDMACTL0, DMACTL_CLRRUN);
+ VNSvOutPortD(dwIoBase + MAC_REG_RXDMACTL1, DMACTL_CLRRUN);
+ for (ww = 0; ww < W_MAX_TIMEOUT; ww++) {
+ VNSvInPortD(dwIoBase + MAC_REG_RXDMACTL0, &dwData);
+ if (!(dwData & DMACTL_RUN))
+ break;
+ }
+ if (ww == W_MAX_TIMEOUT) {
+ DBG_PORT80(0x10);
+ pr_debug(" DBG_PORT80(0x10)\n");
+ return false;
+ }
+ for (ww = 0; ww < W_MAX_TIMEOUT; ww++) {
+ VNSvInPortD(dwIoBase + MAC_REG_RXDMACTL1, &dwData);
+ if (!(dwData & DMACTL_RUN))
+ break;
+ }
+ if (ww == W_MAX_TIMEOUT) {
+ DBG_PORT80(0x11);
+ pr_debug(" DBG_PORT80(0x11)\n");
+ return false;
+ }
+
+ /* try to safe shutdown RX */
+ MACvRegBitsOff(dwIoBase, MAC_REG_HOSTCR, HOSTCR_RXON);
+ /* W_MAX_TIMEOUT is the timeout period */
+ for (ww = 0; ww < W_MAX_TIMEOUT; ww++) {
+ VNSvInPortB(dwIoBase + MAC_REG_HOSTCR, &byData);
+ if (!(byData & HOSTCR_RXONST))
+ break;
+ }
+ if (ww == W_MAX_TIMEOUT) {
+ DBG_PORT80(0x12);
+ pr_debug(" DBG_PORT80(0x12)\n");
+ return false;
+ }
+ return true;
+}
+
+/*
+ * Description:
+ * Turn Off MAC Tx
+ *
+ * Parameters:
+ * In:
+ * dwIoBase - Base Address for MAC
+ * Out:
+ * none
+ *
+ * Return Value: true if success; otherwise false
+ *
+ */
+bool MACbSafeTxOff(void __iomem *dwIoBase)
+{
+ unsigned short ww;
+ unsigned long dwData;
+ unsigned char byData;
+
+ /* Clear TX DMA */
+ /* Tx0 */
+ VNSvOutPortD(dwIoBase + MAC_REG_TXDMACTL0, DMACTL_CLRRUN);
+ /* AC0 */
+ VNSvOutPortD(dwIoBase + MAC_REG_AC0DMACTL, DMACTL_CLRRUN);
+
+ for (ww = 0; ww < W_MAX_TIMEOUT; ww++) {
+ VNSvInPortD(dwIoBase + MAC_REG_TXDMACTL0, &dwData);
+ if (!(dwData & DMACTL_RUN))
+ break;
+ }
+ if (ww == W_MAX_TIMEOUT) {
+ DBG_PORT80(0x20);
+ pr_debug(" DBG_PORT80(0x20)\n");
+ return false;
+ }
+ for (ww = 0; ww < W_MAX_TIMEOUT; ww++) {
+ VNSvInPortD(dwIoBase + MAC_REG_AC0DMACTL, &dwData);
+ if (!(dwData & DMACTL_RUN))
+ break;
+ }
+ if (ww == W_MAX_TIMEOUT) {
+ DBG_PORT80(0x21);
+ pr_debug(" DBG_PORT80(0x21)\n");
+ return false;
+ }
+
+ /* try to safe shutdown TX */
+ MACvRegBitsOff(dwIoBase, MAC_REG_HOSTCR, HOSTCR_TXON);
+
+ /* W_MAX_TIMEOUT is the timeout period */
+ for (ww = 0; ww < W_MAX_TIMEOUT; ww++) {
+ VNSvInPortB(dwIoBase + MAC_REG_HOSTCR, &byData);
+ if (!(byData & HOSTCR_TXONST))
+ break;
+ }
+ if (ww == W_MAX_TIMEOUT) {
+ DBG_PORT80(0x24);
+ pr_debug(" DBG_PORT80(0x24)\n");
+ return false;
+ }
+ return true;
+}
+
+/*
+ * Description:
+ * Stop MAC function
+ *
+ * Parameters:
+ * In:
+ * dwIoBase - Base Address for MAC
+ * Out:
+ * none
+ *
+ * Return Value: true if success; otherwise false
+ *
+ */
+bool MACbSafeStop(void __iomem *dwIoBase)
+{
+ MACvRegBitsOff(dwIoBase, MAC_REG_TCR, TCR_AUTOBCNTX);
+
+ if (!MACbSafeRxOff(dwIoBase)) {
+ DBG_PORT80(0xA1);
+ pr_debug(" MACbSafeRxOff == false)\n");
+ MACbSafeSoftwareReset(dwIoBase);
+ return false;
+ }
+ if (!MACbSafeTxOff(dwIoBase)) {
+ DBG_PORT80(0xA2);
+ pr_debug(" MACbSafeTxOff == false)\n");
+ MACbSafeSoftwareReset(dwIoBase);
+ return false;
+ }
+
+ MACvRegBitsOff(dwIoBase, MAC_REG_HOSTCR, HOSTCR_MACEN);
+
+ return true;
+}
+
+/*
+ * Description:
+ * Shut Down MAC
+ *
+ * Parameters:
+ * In:
+ * dwIoBase - Base Address for MAC
+ * Out:
+ * none
+ *
+ * Return Value: true if success; otherwise false
+ *
+ */
+bool MACbShutdown(void __iomem *dwIoBase)
+{
+ /* disable MAC IMR */
+ MACvIntDisable(dwIoBase);
+ MACvSetLoopbackMode(dwIoBase, MAC_LB_INTERNAL);
+ /* stop the adapter */
+ if (!MACbSafeStop(dwIoBase)) {
+ MACvSetLoopbackMode(dwIoBase, MAC_LB_NONE);
+ return false;
+ }
+ MACvSetLoopbackMode(dwIoBase, MAC_LB_NONE);
+ return true;
+}
+
+/*
+ * Description:
+ * Initialize MAC
+ *
+ * Parameters:
+ * In:
+ * dwIoBase - Base Address for MAC
+ * Out:
+ * none
+ *
+ * Return Value: none
+ *
+ */
+void MACvInitialize(void __iomem *dwIoBase)
+{
+ /* clear sticky bits */
+ MACvClearStckDS(dwIoBase);
+ /* disable force PME-enable */
+ VNSvOutPortB(dwIoBase + MAC_REG_PMC1, PME_OVR);
+ /* only 3253 A */
+
+ /* do reset */
+ MACbSoftwareReset(dwIoBase);
+
+ /* reset TSF counter */
+ VNSvOutPortB(dwIoBase + MAC_REG_TFTCTL, TFTCTL_TSFCNTRST);
+ /* enable TSF counter */
+ VNSvOutPortB(dwIoBase + MAC_REG_TFTCTL, TFTCTL_TSFCNTREN);
+}
+
+/*
+ * Description:
+ * Set the chip with current rx descriptor address
+ *
+ * Parameters:
+ * In:
+ * dwIoBase - Base Address for MAC
+ * dwCurrDescAddr - Descriptor Address
+ * Out:
+ * none
+ *
+ * Return Value: none
+ *
+ */
+void MACvSetCurrRx0DescAddr(void __iomem *dwIoBase, unsigned long dwCurrDescAddr)
+{
+ unsigned short ww;
+ unsigned char byData;
+ unsigned char byOrgDMACtl;
+
+ VNSvInPortB(dwIoBase + MAC_REG_RXDMACTL0, &byOrgDMACtl);
+ if (byOrgDMACtl & DMACTL_RUN)
+ VNSvOutPortB(dwIoBase + MAC_REG_RXDMACTL0+2, DMACTL_RUN);
+
+ for (ww = 0; ww < W_MAX_TIMEOUT; ww++) {
+ VNSvInPortB(dwIoBase + MAC_REG_RXDMACTL0, &byData);
+ if (!(byData & DMACTL_RUN))
+ break;
+ }
+
+ if (ww == W_MAX_TIMEOUT)
+ DBG_PORT80(0x13);
+
+ VNSvOutPortD(dwIoBase + MAC_REG_RXDMAPTR0, dwCurrDescAddr);
+ if (byOrgDMACtl & DMACTL_RUN)
+ VNSvOutPortB(dwIoBase + MAC_REG_RXDMACTL0, DMACTL_RUN);
+}
+
+/*
+ * Description:
+ * Set the chip with current rx descriptor address
+ *
+ * Parameters:
+ * In:
+ * dwIoBase - Base Address for MAC
+ * dwCurrDescAddr - Descriptor Address
+ * Out:
+ * none
+ *
+ * Return Value: none
+ *
+ */
+void MACvSetCurrRx1DescAddr(void __iomem *dwIoBase, unsigned long dwCurrDescAddr)
+{
+ unsigned short ww;
+ unsigned char byData;
+ unsigned char byOrgDMACtl;
+
+ VNSvInPortB(dwIoBase + MAC_REG_RXDMACTL1, &byOrgDMACtl);
+ if (byOrgDMACtl & DMACTL_RUN)
+ VNSvOutPortB(dwIoBase + MAC_REG_RXDMACTL1+2, DMACTL_RUN);
+
+ for (ww = 0; ww < W_MAX_TIMEOUT; ww++) {
+ VNSvInPortB(dwIoBase + MAC_REG_RXDMACTL1, &byData);
+ if (!(byData & DMACTL_RUN))
+ break;
+ }
+ if (ww == W_MAX_TIMEOUT)
+ DBG_PORT80(0x14);
+
+ VNSvOutPortD(dwIoBase + MAC_REG_RXDMAPTR1, dwCurrDescAddr);
+ if (byOrgDMACtl & DMACTL_RUN)
+ VNSvOutPortB(dwIoBase + MAC_REG_RXDMACTL1, DMACTL_RUN);
+
+}
+
+/*
+ * Description:
+ * Set the chip with current tx0 descriptor address
+ *
+ * Parameters:
+ * In:
+ * dwIoBase - Base Address for MAC
+ * dwCurrDescAddr - Descriptor Address
+ * Out:
+ * none
+ *
+ * Return Value: none
+ *
+ */
+void MACvSetCurrTx0DescAddrEx(void __iomem *dwIoBase, unsigned long dwCurrDescAddr)
+{
+ unsigned short ww;
+ unsigned char byData;
+ unsigned char byOrgDMACtl;
+
+ VNSvInPortB(dwIoBase + MAC_REG_TXDMACTL0, &byOrgDMACtl);
+ if (byOrgDMACtl & DMACTL_RUN)
+ VNSvOutPortB(dwIoBase + MAC_REG_TXDMACTL0+2, DMACTL_RUN);
+
+ for (ww = 0; ww < W_MAX_TIMEOUT; ww++) {
+ VNSvInPortB(dwIoBase + MAC_REG_TXDMACTL0, &byData);
+ if (!(byData & DMACTL_RUN))
+ break;
+ }
+ if (ww == W_MAX_TIMEOUT)
+ DBG_PORT80(0x25);
+
+ VNSvOutPortD(dwIoBase + MAC_REG_TXDMAPTR0, dwCurrDescAddr);
+ if (byOrgDMACtl & DMACTL_RUN)
+ VNSvOutPortB(dwIoBase + MAC_REG_TXDMACTL0, DMACTL_RUN);
+}
+
+/*
+ * Description:
+ * Set the chip with current AC0 descriptor address
+ *
+ * Parameters:
+ * In:
+ * dwIoBase - Base Address for MAC
+ * dwCurrDescAddr - Descriptor Address
+ * Out:
+ * none
+ *
+ * Return Value: none
+ *
+ */
+/* TxDMA1 = AC0DMA */
+void MACvSetCurrAC0DescAddrEx(void __iomem *dwIoBase, unsigned long dwCurrDescAddr)
+{
+ unsigned short ww;
+ unsigned char byData;
+ unsigned char byOrgDMACtl;
+
+ VNSvInPortB(dwIoBase + MAC_REG_AC0DMACTL, &byOrgDMACtl);
+ if (byOrgDMACtl & DMACTL_RUN)
+ VNSvOutPortB(dwIoBase + MAC_REG_AC0DMACTL+2, DMACTL_RUN);
+
+ for (ww = 0; ww < W_MAX_TIMEOUT; ww++) {
+ VNSvInPortB(dwIoBase + MAC_REG_AC0DMACTL, &byData);
+ if (!(byData & DMACTL_RUN))
+ break;
+ }
+ if (ww == W_MAX_TIMEOUT) {
+ DBG_PORT80(0x26);
+ pr_debug(" DBG_PORT80(0x26)\n");
+ }
+ VNSvOutPortD(dwIoBase + MAC_REG_AC0DMAPTR, dwCurrDescAddr);
+ if (byOrgDMACtl & DMACTL_RUN)
+ VNSvOutPortB(dwIoBase + MAC_REG_AC0DMACTL, DMACTL_RUN);
+}
+
+void MACvSetCurrTXDescAddr(int iTxType, void __iomem *dwIoBase, unsigned long dwCurrDescAddr)
+{
+ if (iTxType == TYPE_AC0DMA)
+ MACvSetCurrAC0DescAddrEx(dwIoBase, dwCurrDescAddr);
+ else if (iTxType == TYPE_TXDMA0)
+ MACvSetCurrTx0DescAddrEx(dwIoBase, dwCurrDescAddr);
+}
+
+/*
+ * Description:
+ * Micro Second Delay via MAC
+ *
+ * Parameters:
+ * In:
+ * dwIoBase - Base Address for MAC
+ * uDelay - Delay time (timer resolution is 4 us)
+ * Out:
+ * none
+ *
+ * Return Value: none
+ *
+ */
+void MACvTimer0MicroSDelay(void __iomem *dwIoBase, unsigned int uDelay)
+{
+ unsigned char byValue;
+ unsigned int uu, ii;
+
+ VNSvOutPortB(dwIoBase + MAC_REG_TMCTL0, 0);
+ VNSvOutPortD(dwIoBase + MAC_REG_TMDATA0, uDelay);
+ VNSvOutPortB(dwIoBase + MAC_REG_TMCTL0, (TMCTL_TMD | TMCTL_TE));
+ for (ii = 0; ii < 66; ii++) { /* assume max PCI clock is 66Mhz */
+ for (uu = 0; uu < uDelay; uu++) {
+ VNSvInPortB(dwIoBase + MAC_REG_TMCTL0, &byValue);
+ if ((byValue == 0) ||
+ (byValue & TMCTL_TSUSP)) {
+ VNSvOutPortB(dwIoBase + MAC_REG_TMCTL0, 0);
+ return;
+ }
+ }
+ }
+ VNSvOutPortB(dwIoBase + MAC_REG_TMCTL0, 0);
+}
+
+/*
+ * Description:
+ * Micro Second One shot timer via MAC
+ *
+ * Parameters:
+ * In:
+ * dwIoBase - Base Address for MAC
+ * uDelay - Delay time
+ * Out:
+ * none
+ *
+ * Return Value: none
+ *
+ */
+void MACvOneShotTimer1MicroSec(void __iomem *dwIoBase, unsigned int uDelayTime)
+{
+ VNSvOutPortB(dwIoBase + MAC_REG_TMCTL1, 0);
+ VNSvOutPortD(dwIoBase + MAC_REG_TMDATA1, uDelayTime);
+ VNSvOutPortB(dwIoBase + MAC_REG_TMCTL1, (TMCTL_TMD | TMCTL_TE));
+}
+
+void MACvSetMISCFifo(void __iomem *dwIoBase, unsigned short wOffset, unsigned long dwData)
+{
+ if (wOffset > 273)
+ return;
+ VNSvOutPortW(dwIoBase + MAC_REG_MISCFFNDEX, wOffset);
+ VNSvOutPortD(dwIoBase + MAC_REG_MISCFFDATA, dwData);
+ VNSvOutPortW(dwIoBase + MAC_REG_MISCFFCTL, MISCFFCTL_WRITE);
+}
+
+bool MACbPSWakeup(void __iomem *dwIoBase)
+{
+ unsigned char byOrgValue;
+ unsigned int ww;
+ /* Read PSCTL */
+ if (MACbIsRegBitsOff(dwIoBase, MAC_REG_PSCTL, PSCTL_PS))
+ return true;
+
+ /* Disable PS */
+ MACvRegBitsOff(dwIoBase, MAC_REG_PSCTL, PSCTL_PSEN);
+
+ /* Check if SyncFlushOK */
+ for (ww = 0; ww < W_MAX_TIMEOUT; ww++) {
+ VNSvInPortB(dwIoBase + MAC_REG_PSCTL, &byOrgValue);
+ if (byOrgValue & PSCTL_WAKEDONE)
+ break;
+ }
+ if (ww == W_MAX_TIMEOUT) {
+ DBG_PORT80(0x36);
+ pr_debug(" DBG_PORT80(0x33)\n");
+ return false;
+ }
+ return true;
+}
+
+/*
+ * Description:
+ * Set the Key by MISCFIFO
+ *
+ * Parameters:
+ * In:
+ * dwIoBase - Base Address for MAC
+ *
+ * Out:
+ * none
+ *
+ * Return Value: none
+ *
+ */
+
+void MACvSetKeyEntry(void __iomem *dwIoBase, unsigned short wKeyCtl, unsigned int uEntryIdx,
+ unsigned int uKeyIdx, unsigned char *pbyAddr, u32 *pdwKey, unsigned char byLocalID)
+{
+ unsigned short wOffset;
+ u32 dwData;
+ int ii;
+
+ if (byLocalID <= 1)
+ return;
+
+ pr_debug("MACvSetKeyEntry\n");
+ wOffset = MISCFIFO_KEYETRY0;
+ wOffset += (uEntryIdx * MISCFIFO_KEYENTRYSIZE);
+
+ dwData = 0;
+ dwData |= wKeyCtl;
+ dwData <<= 16;
+ dwData |= MAKEWORD(*(pbyAddr+4), *(pbyAddr+5));
+ pr_debug("1. wOffset: %d, Data: %X, KeyCtl:%X\n",
+ wOffset, dwData, wKeyCtl);
+
+ VNSvOutPortW(dwIoBase + MAC_REG_MISCFFNDEX, wOffset);
+ VNSvOutPortD(dwIoBase + MAC_REG_MISCFFDATA, dwData);
+ VNSvOutPortW(dwIoBase + MAC_REG_MISCFFCTL, MISCFFCTL_WRITE);
+ wOffset++;
+
+ dwData = 0;
+ dwData |= *(pbyAddr+3);
+ dwData <<= 8;
+ dwData |= *(pbyAddr+2);
+ dwData <<= 8;
+ dwData |= *(pbyAddr+1);
+ dwData <<= 8;
+ dwData |= *(pbyAddr+0);
+ pr_debug("2. wOffset: %d, Data: %X\n", wOffset, dwData);
+
+ VNSvOutPortW(dwIoBase + MAC_REG_MISCFFNDEX, wOffset);
+ VNSvOutPortD(dwIoBase + MAC_REG_MISCFFDATA, dwData);
+ VNSvOutPortW(dwIoBase + MAC_REG_MISCFFCTL, MISCFFCTL_WRITE);
+ wOffset++;
+
+ wOffset += (uKeyIdx * 4);
+ for (ii = 0; ii < 4; ii++) {
+ /* always push 128 bits */
+ pr_debug("3.(%d) wOffset: %d, Data: %X\n",
+ ii, wOffset+ii, *pdwKey);
+ VNSvOutPortW(dwIoBase + MAC_REG_MISCFFNDEX, wOffset+ii);
+ VNSvOutPortD(dwIoBase + MAC_REG_MISCFFDATA, *pdwKey++);
+ VNSvOutPortW(dwIoBase + MAC_REG_MISCFFCTL, MISCFFCTL_WRITE);
+ }
+}
+
+/*
+ * Description:
+ * Disable the Key Entry by MISCFIFO
+ *
+ * Parameters:
+ * In:
+ * dwIoBase - Base Address for MAC
+ *
+ * Out:
+ * none
+ *
+ * Return Value: none
+ *
+ */
+void MACvDisableKeyEntry(void __iomem *dwIoBase, unsigned int uEntryIdx)
+{
+ unsigned short wOffset;
+
+ wOffset = MISCFIFO_KEYETRY0;
+ wOffset += (uEntryIdx * MISCFIFO_KEYENTRYSIZE);
+
+ VNSvOutPortW(dwIoBase + MAC_REG_MISCFFNDEX, wOffset);
+ VNSvOutPortD(dwIoBase + MAC_REG_MISCFFDATA, 0);
+ VNSvOutPortW(dwIoBase + MAC_REG_MISCFFCTL, MISCFFCTL_WRITE);
+}
diff --git a/drivers/staging/vt6655/mac.h b/drivers/staging/vt6655/mac.h
new file mode 100644
index 000000000..8e0200a78
--- /dev/null
+++ b/drivers/staging/vt6655/mac.h
@@ -0,0 +1,946 @@
+/*
+ * Copyright (c) 1996, 2003 VIA Networking Technologies, Inc.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ *
+ * File: mac.h
+ *
+ * Purpose: MAC routines
+ *
+ * Author: Tevin Chen
+ *
+ * Date: May 21, 1996
+ *
+ * Revision History:
+ * 07-01-2003 Bryan YC Fan: Re-write codes to support VT3253 spec.
+ * 08-25-2003 Kyle Hsu: Porting MAC functions from sim53.
+ * 09-03-2003 Bryan YC Fan: Add MACvDisableProtectMD & MACvEnableProtectMD
+ */
+
+#ifndef __MAC_H__
+#define __MAC_H__
+
+#include "tmacro.h"
+#include "upc.h"
+
+/*--------------------- Export Definitions -------------------------*/
+/* Registers in the MAC */
+#define MAC_MAX_CONTEXT_SIZE_PAGE0 256
+#define MAC_MAX_CONTEXT_SIZE_PAGE1 128
+
+/* Registers not related to 802.11b */
+#define MAC_REG_BCFG0 0x00
+#define MAC_REG_BCFG1 0x01
+#define MAC_REG_FCR0 0x02
+#define MAC_REG_FCR1 0x03
+#define MAC_REG_BISTCMD 0x04
+#define MAC_REG_BISTSR0 0x05
+#define MAC_REG_BISTSR1 0x06
+#define MAC_REG_BISTSR2 0x07
+#define MAC_REG_I2MCSR 0x08
+#define MAC_REG_I2MTGID 0x09
+#define MAC_REG_I2MTGAD 0x0A
+#define MAC_REG_I2MCFG 0x0B
+#define MAC_REG_I2MDIPT 0x0C
+#define MAC_REG_I2MDOPT 0x0E
+#define MAC_REG_PMC0 0x10
+#define MAC_REG_PMC1 0x11
+#define MAC_REG_STICKHW 0x12
+#define MAC_REG_LOCALID 0x14
+#define MAC_REG_TESTCFG 0x15
+#define MAC_REG_JUMPER0 0x16
+#define MAC_REG_JUMPER1 0x17
+#define MAC_REG_TMCTL0 0x18
+#define MAC_REG_TMCTL1 0x19
+#define MAC_REG_TMDATA0 0x1C
+
+/* MAC Parameter related */
+#define MAC_REG_LRT 0x20
+#define MAC_REG_SRT 0x21
+#define MAC_REG_SIFS 0x22
+#define MAC_REG_DIFS 0x23
+#define MAC_REG_EIFS 0x24
+#define MAC_REG_SLOT 0x25
+#define MAC_REG_BI 0x26
+#define MAC_REG_CWMAXMIN0 0x28
+#define MAC_REG_LINKOFFTOTM 0x2A
+#define MAC_REG_SWTMOT 0x2B
+#define MAC_REG_MIBCNTR 0x2C
+#define MAC_REG_RTSOKCNT 0x2C
+#define MAC_REG_RTSFAILCNT 0x2D
+#define MAC_REG_ACKFAILCNT 0x2E
+#define MAC_REG_FCSERRCNT 0x2F
+
+/* TSF Related */
+#define MAC_REG_TSFCNTR 0x30
+#define MAC_REG_NEXTTBTT 0x38
+#define MAC_REG_TSFOFST 0x40
+#define MAC_REG_TFTCTL 0x48
+
+/* WMAC Control/Status Related */
+#define MAC_REG_ENCFG 0x4C
+#define MAC_REG_PAGE1SEL 0x4F
+#define MAC_REG_CFG 0x50
+#define MAC_REG_TEST 0x52
+#define MAC_REG_HOSTCR 0x54
+#define MAC_REG_MACCR 0x55
+#define MAC_REG_RCR 0x56
+#define MAC_REG_TCR 0x57
+#define MAC_REG_IMR 0x58
+#define MAC_REG_ISR 0x5C
+
+/* Power Saving Related */
+#define MAC_REG_PSCFG 0x60
+#define MAC_REG_PSCTL 0x61
+#define MAC_REG_PSPWRSIG 0x62
+#define MAC_REG_BBCR13 0x63
+#define MAC_REG_AIDATIM 0x64
+#define MAC_REG_PWBT 0x66
+#define MAC_REG_WAKEOKTMR 0x68
+#define MAC_REG_CALTMR 0x69
+#define MAC_REG_SYNSPACCNT 0x6A
+#define MAC_REG_WAKSYNOPT 0x6B
+
+/* Baseband/IF Control Group */
+#define MAC_REG_BBREGCTL 0x6C
+#define MAC_REG_CHANNEL 0x6D
+#define MAC_REG_BBREGADR 0x6E
+#define MAC_REG_BBREGDATA 0x6F
+#define MAC_REG_IFREGCTL 0x70
+#define MAC_REG_IFDATA 0x71
+#define MAC_REG_ITRTMSET 0x74
+#define MAC_REG_PAPEDELAY 0x77
+#define MAC_REG_SOFTPWRCTL 0x78
+#define MAC_REG_GPIOCTL0 0x7A
+#define MAC_REG_GPIOCTL1 0x7B
+
+/* MAC DMA Related Group */
+#define MAC_REG_TXDMACTL0 0x7C
+#define MAC_REG_TXDMAPTR0 0x80
+#define MAC_REG_AC0DMACTL 0x84
+#define MAC_REG_AC0DMAPTR 0x88
+#define MAC_REG_BCNDMACTL 0x8C
+#define MAC_REG_BCNDMAPTR 0x90
+#define MAC_REG_RXDMACTL0 0x94
+#define MAC_REG_RXDMAPTR0 0x98
+#define MAC_REG_RXDMACTL1 0x9C
+#define MAC_REG_RXDMAPTR1 0xA0
+#define MAC_REG_SYNCDMACTL 0xA4
+#define MAC_REG_SYNCDMAPTR 0xA8
+#define MAC_REG_ATIMDMACTL 0xAC
+#define MAC_REG_ATIMDMAPTR 0xB0
+
+/* MiscFF PIO related */
+#define MAC_REG_MISCFFNDEX 0xB4
+#define MAC_REG_MISCFFCTL 0xB6
+#define MAC_REG_MISCFFDATA 0xB8
+
+/* Extend SW Timer */
+#define MAC_REG_TMDATA1 0xBC
+
+/* WOW Related Group */
+#define MAC_REG_WAKEUPEN0 0xC0
+#define MAC_REG_WAKEUPEN1 0xC1
+#define MAC_REG_WAKEUPSR0 0xC2
+#define MAC_REG_WAKEUPSR1 0xC3
+#define MAC_REG_WAKE128_0 0xC4
+#define MAC_REG_WAKE128_1 0xD4
+#define MAC_REG_WAKE128_2 0xE4
+#define MAC_REG_WAKE128_3 0xF4
+
+/************** Page 1 ******************/
+#define MAC_REG_CRC_128_0 0x04
+#define MAC_REG_CRC_128_1 0x06
+#define MAC_REG_CRC_128_2 0x08
+#define MAC_REG_CRC_128_3 0x0A
+
+/* MAC Configuration Group */
+#define MAC_REG_PAR0 0x0C
+#define MAC_REG_PAR4 0x10
+#define MAC_REG_BSSID0 0x14
+#define MAC_REG_BSSID4 0x18
+#define MAC_REG_MAR0 0x1C
+#define MAC_REG_MAR4 0x20
+
+/* MAC RSPPKT INFO Group */
+#define MAC_REG_RSPINF_B_1 0x24
+#define MAC_REG_RSPINF_B_2 0x28
+#define MAC_REG_RSPINF_B_5 0x2C
+#define MAC_REG_RSPINF_B_11 0x30
+#define MAC_REG_RSPINF_A_6 0x34
+#define MAC_REG_RSPINF_A_9 0x36
+#define MAC_REG_RSPINF_A_12 0x38
+#define MAC_REG_RSPINF_A_18 0x3A
+#define MAC_REG_RSPINF_A_24 0x3C
+#define MAC_REG_RSPINF_A_36 0x3E
+#define MAC_REG_RSPINF_A_48 0x40
+#define MAC_REG_RSPINF_A_54 0x42
+#define MAC_REG_RSPINF_A_72 0x44
+
+/* 802.11h relative */
+#define MAC_REG_QUIETINIT 0x60
+#define MAC_REG_QUIETGAP 0x62
+#define MAC_REG_QUIETDUR 0x64
+#define MAC_REG_MSRCTL 0x66
+#define MAC_REG_MSRBBSTS 0x67
+#define MAC_REG_MSRSTART 0x68
+#define MAC_REG_MSRDURATION 0x70
+#define MAC_REG_CCAFRACTION 0x72
+#define MAC_REG_PWRCCK 0x73
+#define MAC_REG_PWROFDM 0x7C
+
+/* Bits in the BCFG0 register */
+#define BCFG0_PERROFF 0x40
+#define BCFG0_MRDMDIS 0x20
+#define BCFG0_MRDLDIS 0x10
+#define BCFG0_MWMEN 0x08
+#define BCFG0_VSERREN 0x02
+#define BCFG0_LATMEN 0x01
+
+/* Bits in the BCFG1 register */
+#define BCFG1_CFUNOPT 0x80
+#define BCFG1_CREQOPT 0x40
+#define BCFG1_DMA8 0x10
+#define BCFG1_ARBITOPT 0x08
+#define BCFG1_PCIMEN 0x04
+#define BCFG1_MIOEN 0x02
+#define BCFG1_CISDLYEN 0x01
+
+/* Bits in RAMBIST registers */
+#define BISTCMD_TSTPAT5 0x00
+#define BISTCMD_TSTPATA 0x80
+#define BISTCMD_TSTERR 0x20
+#define BISTCMD_TSTPATF 0x18
+#define BISTCMD_TSTPAT0 0x10
+#define BISTCMD_TSTMODE 0x04
+#define BISTCMD_TSTITTX 0x03
+#define BISTCMD_TSTATRX 0x02
+#define BISTCMD_TSTATTX 0x01
+#define BISTCMD_TSTRX 0x00
+#define BISTSR0_BISTGO 0x01
+#define BISTSR1_TSTSR 0x01
+#define BISTSR2_CMDPRTEN 0x02
+#define BISTSR2_RAMTSTEN 0x01
+
+/* Bits in the I2MCFG EEPROM register */
+#define I2MCFG_BOUNDCTL 0x80
+#define I2MCFG_WAITCTL 0x20
+#define I2MCFG_SCLOECTL 0x10
+#define I2MCFG_WBUSYCTL 0x08
+#define I2MCFG_NORETRY 0x04
+#define I2MCFG_I2MLDSEQ 0x02
+#define I2MCFG_I2CMFAST 0x01
+
+/* Bits in the I2MCSR EEPROM register */
+#define I2MCSR_EEMW 0x80
+#define I2MCSR_EEMR 0x40
+#define I2MCSR_AUTOLD 0x08
+#define I2MCSR_NACK 0x02
+#define I2MCSR_DONE 0x01
+
+/* Bits in the PMC1 register */
+#define SPS_RST 0x80
+#define PCISTIKY 0x40
+#define PME_OVR 0x02
+
+/* Bits in the STICKYHW register */
+#define STICKHW_DS1_SHADOW 0x02
+#define STICKHW_DS0_SHADOW 0x01
+
+/* Bits in the TMCTL register */
+#define TMCTL_TSUSP 0x04
+#define TMCTL_TMD 0x02
+#define TMCTL_TE 0x01
+
+/* Bits in the TFTCTL register */
+#define TFTCTL_HWUTSF 0x80
+#define TFTCTL_TBTTSYNC 0x40
+#define TFTCTL_HWUTSFEN 0x20
+#define TFTCTL_TSFCNTRRD 0x10
+#define TFTCTL_TBTTSYNCEN 0x08
+#define TFTCTL_TSFSYNCEN 0x04
+#define TFTCTL_TSFCNTRST 0x02
+#define TFTCTL_TSFCNTREN 0x01
+
+/* Bits in the EnhanceCFG register */
+#define EnCFG_BarkerPream 0x00020000
+#define EnCFG_NXTBTTCFPSTR 0x00010000
+#define EnCFG_BcnSusClr 0x00000200
+#define EnCFG_BcnSusInd 0x00000100
+#define EnCFG_CFP_ProtectEn 0x00000040
+#define EnCFG_ProtectMd 0x00000020
+#define EnCFG_HwParCFP 0x00000010
+#define EnCFG_CFNULRSP 0x00000004
+#define EnCFG_BBType_MASK 0x00000003
+#define EnCFG_BBType_g 0x00000002
+#define EnCFG_BBType_b 0x00000001
+#define EnCFG_BBType_a 0x00000000
+
+/* Bits in the Page1Sel register */
+#define PAGE1_SEL 0x01
+
+/* Bits in the CFG register */
+#define CFG_TKIPOPT 0x80
+#define CFG_RXDMAOPT 0x40
+#define CFG_TMOT_SW 0x20
+#define CFG_TMOT_HWLONG 0x10
+#define CFG_TMOT_HW 0x00
+#define CFG_CFPENDOPT 0x08
+#define CFG_BCNSUSEN 0x04
+#define CFG_NOTXTIMEOUT 0x02
+#define CFG_NOBUFOPT 0x01
+
+/* Bits in the TEST register */
+#define TEST_LBEXT 0x80
+#define TEST_LBINT 0x40
+#define TEST_LBNONE 0x00
+#define TEST_SOFTINT 0x20
+#define TEST_CONTTX 0x10
+#define TEST_TXPE 0x08
+#define TEST_NAVDIS 0x04
+#define TEST_NOCTS 0x02
+#define TEST_NOACK 0x01
+
+/* Bits in the HOSTCR register */
+#define HOSTCR_TXONST 0x80
+#define HOSTCR_RXONST 0x40
+#define HOSTCR_ADHOC 0x20 /* Network Type 1 = Ad-hoc */
+#define HOSTCR_AP 0x10 /* Port Type 1 = AP */
+#define HOSTCR_TXON 0x08 /* 0000 1000 */
+#define HOSTCR_RXON 0x04 /* 0000 0100 */
+#define HOSTCR_MACEN 0x02 /* 0000 0010 */
+#define HOSTCR_SOFTRST 0x01 /* 0000 0001 */
+
+/* Bits in the MACCR register */
+#define MACCR_SYNCFLUSHOK 0x04
+#define MACCR_SYNCFLUSH 0x02
+#define MACCR_CLRNAV 0x01
+
+/* Bits in the MAC_REG_GPIOCTL0 register */
+#define LED_ACTSET 0x01
+#define LED_RFOFF 0x02
+#define LED_NOCONNECT 0x04
+
+/* Bits in the RCR register */
+#define RCR_SSID 0x80
+#define RCR_RXALLTYPE 0x40
+#define RCR_UNICAST 0x20
+#define RCR_BROADCAST 0x10
+#define RCR_MULTICAST 0x08
+#define RCR_WPAERR 0x04
+#define RCR_ERRCRC 0x02
+#define RCR_BSSID 0x01
+
+/* Bits in the TCR register */
+#define TCR_SYNCDCFOPT 0x02
+#define TCR_AUTOBCNTX 0x01 /* Beacon automatically transmit enable */
+
+/* Bits in the IMR register */
+#define IMR_MEASURESTART 0x80000000
+#define IMR_QUIETSTART 0x20000000
+#define IMR_RADARDETECT 0x10000000
+#define IMR_MEASUREEND 0x08000000
+#define IMR_SOFTTIMER1 0x00200000
+#define IMR_RXDMA1 0x00001000 /* 0000 0000 0001 0000 0000 0000 */
+#define IMR_RXNOBUF 0x00000800
+#define IMR_MIBNEARFULL 0x00000400
+#define IMR_SOFTINT 0x00000200
+#define IMR_FETALERR 0x00000100
+#define IMR_WATCHDOG 0x00000080
+#define IMR_SOFTTIMER 0x00000040
+#define IMR_GPIO 0x00000020
+#define IMR_TBTT 0x00000010
+#define IMR_RXDMA0 0x00000008
+#define IMR_BNTX 0x00000004
+#define IMR_AC0DMA 0x00000002
+#define IMR_TXDMA0 0x00000001
+
+/* Bits in the ISR register */
+#define ISR_MEASURESTART 0x80000000
+#define ISR_QUIETSTART 0x20000000
+#define ISR_RADARDETECT 0x10000000
+#define ISR_MEASUREEND 0x08000000
+#define ISR_SOFTTIMER1 0x00200000
+#define ISR_RXDMA1 0x00001000 /* 0000 0000 0001 0000 0000 0000 */
+#define ISR_RXNOBUF 0x00000800 /* 0000 0000 0000 1000 0000 0000 */
+#define ISR_MIBNEARFULL 0x00000400 /* 0000 0000 0000 0100 0000 0000 */
+#define ISR_SOFTINT 0x00000200
+#define ISR_FETALERR 0x00000100
+#define ISR_WATCHDOG 0x00000080
+#define ISR_SOFTTIMER 0x00000040
+#define ISR_GPIO 0x00000020
+#define ISR_TBTT 0x00000010
+#define ISR_RXDMA0 0x00000008
+#define ISR_BNTX 0x00000004
+#define ISR_AC0DMA 0x00000002
+#define ISR_TXDMA0 0x00000001
+
+/* Bits in the PSCFG register */
+#define PSCFG_PHILIPMD 0x40
+#define PSCFG_WAKECALEN 0x20
+#define PSCFG_WAKETMREN 0x10
+#define PSCFG_BBPSPROG 0x08
+#define PSCFG_WAKESYN 0x04
+#define PSCFG_SLEEPSYN 0x02
+#define PSCFG_AUTOSLEEP 0x01
+
+/* Bits in the PSCTL register */
+#define PSCTL_WAKEDONE 0x20
+#define PSCTL_PS 0x10
+#define PSCTL_GO2DOZE 0x08
+#define PSCTL_LNBCN 0x04
+#define PSCTL_ALBCN 0x02
+#define PSCTL_PSEN 0x01
+
+/* Bits in the PSPWSIG register */
+#define PSSIG_WPE3 0x80
+#define PSSIG_WPE2 0x40
+#define PSSIG_WPE1 0x20
+#define PSSIG_WRADIOPE 0x10
+#define PSSIG_SPE3 0x08
+#define PSSIG_SPE2 0x04
+#define PSSIG_SPE1 0x02
+#define PSSIG_SRADIOPE 0x01
+
+/* Bits in the BBREGCTL register */
+#define BBREGCTL_DONE 0x04
+#define BBREGCTL_REGR 0x02
+#define BBREGCTL_REGW 0x01
+
+/* Bits in the IFREGCTL register */
+#define IFREGCTL_DONE 0x04
+#define IFREGCTL_IFRF 0x02
+#define IFREGCTL_REGW 0x01
+
+/* Bits in the SOFTPWRCTL register */
+#define SOFTPWRCTL_RFLEOPT 0x0800
+#define SOFTPWRCTL_TXPEINV 0x0200
+#define SOFTPWRCTL_SWPECTI 0x0100
+#define SOFTPWRCTL_SWPAPE 0x0020
+#define SOFTPWRCTL_SWCALEN 0x0010
+#define SOFTPWRCTL_SWRADIO_PE 0x0008
+#define SOFTPWRCTL_SWPE2 0x0004
+#define SOFTPWRCTL_SWPE1 0x0002
+#define SOFTPWRCTL_SWPE3 0x0001
+
+/* Bits in the GPIOCTL1 register */
+#define GPIO1_DATA1 0x20
+#define GPIO1_MD1 0x10
+#define GPIO1_DATA0 0x02
+#define GPIO1_MD0 0x01
+
+/* Bits in the DMACTL register */
+#define DMACTL_CLRRUN 0x00080000
+#define DMACTL_RUN 0x00000008
+#define DMACTL_WAKE 0x00000004
+#define DMACTL_DEAD 0x00000002
+#define DMACTL_ACTIVE 0x00000001
+
+/* Bits in the RXDMACTL0 register */
+#define RX_PERPKT 0x00000100
+#define RX_PERPKTCLR 0x01000000
+
+/* Bits in the BCNDMACTL register */
+#define BEACON_READY 0x01
+
+/* Bits in the MISCFFCTL register */
+#define MISCFFCTL_WRITE 0x0001
+
+/* Bits in WAKEUPEN0 */
+#define WAKEUPEN0_DIRPKT 0x10
+#define WAKEUPEN0_LINKOFF 0x08
+#define WAKEUPEN0_ATIMEN 0x04
+#define WAKEUPEN0_TIMEN 0x02
+#define WAKEUPEN0_MAGICEN 0x01
+
+/* Bits in WAKEUPEN1 */
+#define WAKEUPEN1_128_3 0x08
+#define WAKEUPEN1_128_2 0x04
+#define WAKEUPEN1_128_1 0x02
+#define WAKEUPEN1_128_0 0x01
+
+/* Bits in WAKEUPSR0 */
+#define WAKEUPSR0_DIRPKT 0x10
+#define WAKEUPSR0_LINKOFF 0x08
+#define WAKEUPSR0_ATIMEN 0x04
+#define WAKEUPSR0_TIMEN 0x02
+#define WAKEUPSR0_MAGICEN 0x01
+
+/* Bits in WAKEUPSR1 */
+#define WAKEUPSR1_128_3 0x08
+#define WAKEUPSR1_128_2 0x04
+#define WAKEUPSR1_128_1 0x02
+#define WAKEUPSR1_128_0 0x01
+
+/* Bits in the MAC_REG_GPIOCTL register */
+#define GPIO0_MD 0x01
+#define GPIO0_DATA 0x02
+#define GPIO0_INTMD 0x04
+#define GPIO1_MD 0x10
+#define GPIO1_DATA 0x20
+
+/* Bits in the MSRCTL register */
+#define MSRCTL_FINISH 0x80
+#define MSRCTL_READY 0x40
+#define MSRCTL_RADARDETECT 0x20
+#define MSRCTL_EN 0x10
+#define MSRCTL_QUIETTXCHK 0x08
+#define MSRCTL_QUIETRPT 0x04
+#define MSRCTL_QUIETINT 0x02
+#define MSRCTL_QUIETEN 0x01
+
+/* Bits in the MSRCTL1 register */
+#define MSRCTL1_TXPWR 0x08
+#define MSRCTL1_CSAPAREN 0x04
+#define MSRCTL1_TXPAUSE 0x01
+
+/* Loopback mode */
+#define MAC_LB_EXT 0x02
+#define MAC_LB_INTERNAL 0x01
+#define MAC_LB_NONE 0x00
+
+#define Default_BI 0x200
+
+/* MiscFIFO Offset */
+#define MISCFIFO_KEYETRY0 32
+#define MISCFIFO_KEYENTRYSIZE 22
+#define MISCFIFO_SYNINFO_IDX 10
+#define MISCFIFO_SYNDATA_IDX 11
+#define MISCFIFO_SYNDATASIZE 21
+
+/* enabled mask value of irq */
+#define IMR_MASK_VALUE (IMR_SOFTTIMER1 | \
+ IMR_RXDMA1 | \
+ IMR_RXNOBUF | \
+ IMR_MIBNEARFULL | \
+ IMR_SOFTINT | \
+ IMR_FETALERR | \
+ IMR_WATCHDOG | \
+ IMR_SOFTTIMER | \
+ IMR_GPIO | \
+ IMR_TBTT | \
+ IMR_RXDMA0 | \
+ IMR_BNTX | \
+ IMR_AC0DMA | \
+ IMR_TXDMA0)
+
+/* max time out delay time */
+#define W_MAX_TIMEOUT 0xFFF0U
+
+/* wait time within loop */
+#define CB_DELAY_LOOP_WAIT 10 /* 10ms */
+
+/* revision id */
+#define REV_ID_VT3253_A0 0x00
+#define REV_ID_VT3253_A1 0x01
+#define REV_ID_VT3253_B0 0x08
+#define REV_ID_VT3253_B1 0x09
+
+/*--------------------- Export Types ------------------------------*/
+
+/*--------------------- Export Macros ------------------------------*/
+
+#define MACvRegBitsOn(dwIoBase, byRegOfs, byBits) \
+do { \
+ unsigned char byData; \
+ VNSvInPortB(dwIoBase + byRegOfs, &byData); \
+ VNSvOutPortB(dwIoBase + byRegOfs, byData | (byBits)); \
+} while (0)
+
+#define MACvWordRegBitsOn(dwIoBase, byRegOfs, wBits) \
+do { \
+ unsigned short wData; \
+ VNSvInPortW(dwIoBase + byRegOfs, &wData); \
+ VNSvOutPortW(dwIoBase + byRegOfs, wData | (wBits)); \
+} while (0)
+
+#define MACvDWordRegBitsOn(dwIoBase, byRegOfs, dwBits) \
+do { \
+ unsigned long dwData; \
+ VNSvInPortD(dwIoBase + byRegOfs, &dwData); \
+ VNSvOutPortD(dwIoBase + byRegOfs, dwData | (dwBits)); \
+} while (0)
+
+#define MACvRegBitsOnEx(dwIoBase, byRegOfs, byMask, byBits) \
+do { \
+ unsigned char byData; \
+ VNSvInPortB(dwIoBase + byRegOfs, &byData); \
+ byData &= byMask; \
+ VNSvOutPortB(dwIoBase + byRegOfs, byData | (byBits)); \
+} while (0)
+
+#define MACvRegBitsOff(dwIoBase, byRegOfs, byBits) \
+do { \
+ unsigned char byData; \
+ VNSvInPortB(dwIoBase + byRegOfs, &byData); \
+ VNSvOutPortB(dwIoBase + byRegOfs, byData & ~(byBits)); \
+} while (0)
+
+#define MACvWordRegBitsOff(dwIoBase, byRegOfs, wBits) \
+do { \
+ unsigned short wData; \
+ VNSvInPortW(dwIoBase + byRegOfs, &wData); \
+ VNSvOutPortW(dwIoBase + byRegOfs, wData & ~(wBits)); \
+} while (0)
+
+#define MACvDWordRegBitsOff(dwIoBase, byRegOfs, dwBits) \
+do { \
+ unsigned long dwData; \
+ VNSvInPortD(dwIoBase + byRegOfs, &dwData); \
+ VNSvOutPortD(dwIoBase + byRegOfs, dwData & ~(dwBits)); \
+} while (0)
+
+#define MACvGetCurrRx0DescAddr(dwIoBase, pdwCurrDescAddr) \
+ VNSvInPortD(dwIoBase + MAC_REG_RXDMAPTR0, \
+ (unsigned long *)pdwCurrDescAddr)
+
+#define MACvGetCurrRx1DescAddr(dwIoBase, pdwCurrDescAddr) \
+ VNSvInPortD(dwIoBase + MAC_REG_RXDMAPTR1, \
+ (unsigned long *)pdwCurrDescAddr)
+
+#define MACvGetCurrTx0DescAddr(dwIoBase, pdwCurrDescAddr) \
+ VNSvInPortD(dwIoBase + MAC_REG_TXDMAPTR0, \
+ (unsigned long *)pdwCurrDescAddr)
+
+#define MACvGetCurrAC0DescAddr(dwIoBase, pdwCurrDescAddr) \
+ VNSvInPortD(dwIoBase + MAC_REG_AC0DMAPTR, \
+ (unsigned long *)pdwCurrDescAddr)
+
+#define MACvGetCurrSyncDescAddr(dwIoBase, pdwCurrDescAddr) \
+ VNSvInPortD(dwIoBase + MAC_REG_SYNCDMAPTR, \
+ (unsigned long *)pdwCurrDescAddr)
+
+#define MACvGetCurrATIMDescAddr(dwIoBase, pdwCurrDescAddr) \
+ VNSvInPortD(dwIoBase + MAC_REG_ATIMDMAPTR, \
+ (unsigned long *)pdwCurrDescAddr)
+
+/* set the chip with current BCN tx descriptor address */
+#define MACvSetCurrBCNTxDescAddr(dwIoBase, dwCurrDescAddr) \
+ VNSvOutPortD(dwIoBase + MAC_REG_BCNDMAPTR, \
+ dwCurrDescAddr)
+
+/* set the chip with current BCN length */
+#define MACvSetCurrBCNLength(dwIoBase, wCurrBCNLength) \
+ VNSvOutPortW(dwIoBase + MAC_REG_BCNDMACTL+2, \
+ wCurrBCNLength)
+
+#define MACvReadBSSIDAddress(dwIoBase, pbyEtherAddr) \
+do { \
+ VNSvOutPortB(dwIoBase + MAC_REG_PAGE1SEL, 1); \
+ VNSvInPortB(dwIoBase + MAC_REG_BSSID0, \
+ (unsigned char *)pbyEtherAddr); \
+ VNSvInPortB(dwIoBase + MAC_REG_BSSID0 + 1, \
+ pbyEtherAddr + 1); \
+ VNSvInPortB(dwIoBase + MAC_REG_BSSID0 + 2, \
+ pbyEtherAddr + 2); \
+ VNSvInPortB(dwIoBase + MAC_REG_BSSID0 + 3, \
+ pbyEtherAddr + 3); \
+ VNSvInPortB(dwIoBase + MAC_REG_BSSID0 + 4, \
+ pbyEtherAddr + 4); \
+ VNSvInPortB(dwIoBase + MAC_REG_BSSID0 + 5, \
+ pbyEtherAddr + 5); \
+ VNSvOutPortB(dwIoBase + MAC_REG_PAGE1SEL, 0); \
+} while (0)
+
+#define MACvWriteBSSIDAddress(dwIoBase, pbyEtherAddr) \
+do { \
+ VNSvOutPortB(dwIoBase + MAC_REG_PAGE1SEL, 1); \
+ VNSvOutPortB(dwIoBase + MAC_REG_BSSID0, \
+ *(pbyEtherAddr)); \
+ VNSvOutPortB(dwIoBase + MAC_REG_BSSID0 + 1, \
+ *(pbyEtherAddr + 1)); \
+ VNSvOutPortB(dwIoBase + MAC_REG_BSSID0 + 2, \
+ *(pbyEtherAddr + 2)); \
+ VNSvOutPortB(dwIoBase + MAC_REG_BSSID0 + 3, \
+ *(pbyEtherAddr + 3)); \
+ VNSvOutPortB(dwIoBase + MAC_REG_BSSID0 + 4, \
+ *(pbyEtherAddr + 4)); \
+ VNSvOutPortB(dwIoBase + MAC_REG_BSSID0 + 5, \
+ *(pbyEtherAddr + 5)); \
+ VNSvOutPortB(dwIoBase + MAC_REG_PAGE1SEL, 0); \
+} while (0)
+
+#define MACvReadEtherAddress(dwIoBase, pbyEtherAddr) \
+do { \
+ VNSvOutPortB(dwIoBase + MAC_REG_PAGE1SEL, 1); \
+ VNSvInPortB(dwIoBase + MAC_REG_PAR0, \
+ (unsigned char *)pbyEtherAddr); \
+ VNSvInPortB(dwIoBase + MAC_REG_PAR0 + 1, \
+ pbyEtherAddr + 1); \
+ VNSvInPortB(dwIoBase + MAC_REG_PAR0 + 2, \
+ pbyEtherAddr + 2); \
+ VNSvInPortB(dwIoBase + MAC_REG_PAR0 + 3, \
+ pbyEtherAddr + 3); \
+ VNSvInPortB(dwIoBase + MAC_REG_PAR0 + 4, \
+ pbyEtherAddr + 4); \
+ VNSvInPortB(dwIoBase + MAC_REG_PAR0 + 5, \
+ pbyEtherAddr + 5); \
+ VNSvOutPortB(dwIoBase + MAC_REG_PAGE1SEL, 0); \
+} while (0)
+
+#define MACvWriteEtherAddress(dwIoBase, pbyEtherAddr) \
+do { \
+ VNSvOutPortB(dwIoBase + MAC_REG_PAGE1SEL, 1); \
+ VNSvOutPortB(dwIoBase + MAC_REG_PAR0, \
+ *pbyEtherAddr); \
+ VNSvOutPortB(dwIoBase + MAC_REG_PAR0 + 1, \
+ *(pbyEtherAddr + 1)); \
+ VNSvOutPortB(dwIoBase + MAC_REG_PAR0 + 2, \
+ *(pbyEtherAddr + 2)); \
+ VNSvOutPortB(dwIoBase + MAC_REG_PAR0 + 3, \
+ *(pbyEtherAddr + 3)); \
+ VNSvOutPortB(dwIoBase + MAC_REG_PAR0 + 4, \
+ *(pbyEtherAddr + 4)); \
+ VNSvOutPortB(dwIoBase + MAC_REG_PAR0 + 5, \
+ *(pbyEtherAddr + 5)); \
+ VNSvOutPortB(dwIoBase + MAC_REG_PAGE1SEL, 0); \
+} while (0)
+
+#define MACvClearISR(dwIoBase) \
+ VNSvOutPortD(dwIoBase + MAC_REG_ISR, IMR_MASK_VALUE)
+
+#define MACvStart(dwIoBase) \
+ VNSvOutPortB(dwIoBase + MAC_REG_HOSTCR, \
+ (HOSTCR_MACEN | HOSTCR_RXON | HOSTCR_TXON))
+
+#define MACvRx0PerPktMode(dwIoBase) \
+ VNSvOutPortD(dwIoBase + MAC_REG_RXDMACTL0, RX_PERPKT)
+
+#define MACvRx0BufferFillMode(dwIoBase) \
+ VNSvOutPortD(dwIoBase + MAC_REG_RXDMACTL0, RX_PERPKTCLR)
+
+#define MACvRx1PerPktMode(dwIoBase) \
+ VNSvOutPortD(dwIoBase + MAC_REG_RXDMACTL1, RX_PERPKT)
+
+#define MACvRx1BufferFillMode(dwIoBase) \
+ VNSvOutPortD(dwIoBase + MAC_REG_RXDMACTL1, RX_PERPKTCLR)
+
+#define MACvRxOn(dwIoBase) \
+ MACvRegBitsOn(dwIoBase, MAC_REG_HOSTCR, HOSTCR_RXON)
+
+#define MACvReceive0(dwIoBase) \
+do { \
+ unsigned long dwData; \
+ VNSvInPortD(dwIoBase + MAC_REG_RXDMACTL0, &dwData); \
+ if (dwData & DMACTL_RUN) \
+ VNSvOutPortD(dwIoBase + MAC_REG_RXDMACTL0, DMACTL_WAKE); \
+ else \
+ VNSvOutPortD(dwIoBase + MAC_REG_RXDMACTL0, DMACTL_RUN); \
+} while (0)
+
+#define MACvReceive1(dwIoBase) \
+do { \
+ unsigned long dwData; \
+ VNSvInPortD(dwIoBase + MAC_REG_RXDMACTL1, &dwData); \
+ if (dwData & DMACTL_RUN) \
+ VNSvOutPortD(dwIoBase + MAC_REG_RXDMACTL1, DMACTL_WAKE); \
+ else \
+ VNSvOutPortD(dwIoBase + MAC_REG_RXDMACTL1, DMACTL_RUN); \
+} while (0)
+
+#define MACvTxOn(dwIoBase) \
+ MACvRegBitsOn(dwIoBase, MAC_REG_HOSTCR, HOSTCR_TXON)
+
+#define MACvTransmit0(dwIoBase) \
+do { \
+ unsigned long dwData; \
+ VNSvInPortD(dwIoBase + MAC_REG_TXDMACTL0, &dwData); \
+ if (dwData & DMACTL_RUN) \
+ VNSvOutPortD(dwIoBase + MAC_REG_TXDMACTL0, DMACTL_WAKE); \
+ else \
+ VNSvOutPortD(dwIoBase + MAC_REG_TXDMACTL0, DMACTL_RUN); \
+} while (0)
+
+#define MACvTransmitAC0(dwIoBase) \
+do { \
+ unsigned long dwData; \
+ VNSvInPortD(dwIoBase + MAC_REG_AC0DMACTL, &dwData); \
+ if (dwData & DMACTL_RUN) \
+ VNSvOutPortD(dwIoBase + MAC_REG_AC0DMACTL, DMACTL_WAKE); \
+ else \
+ VNSvOutPortD(dwIoBase + MAC_REG_AC0DMACTL, DMACTL_RUN); \
+} while (0)
+
+#define MACvTransmitSYNC(dwIoBase) \
+do { \
+ unsigned long dwData; \
+ VNSvInPortD(dwIoBase + MAC_REG_SYNCDMACTL, &dwData); \
+ if (dwData & DMACTL_RUN) \
+ VNSvOutPortD(dwIoBase + MAC_REG_SYNCDMACTL, DMACTL_WAKE); \
+ else \
+ VNSvOutPortD(dwIoBase + MAC_REG_SYNCDMACTL, DMACTL_RUN); \
+} while (0)
+
+#define MACvTransmitATIM(dwIoBase) \
+do { \
+ unsigned long dwData; \
+ VNSvInPortD(dwIoBase + MAC_REG_ATIMDMACTL, &dwData); \
+ if (dwData & DMACTL_RUN) \
+ VNSvOutPortD(dwIoBase + MAC_REG_ATIMDMACTL, DMACTL_WAKE); \
+ else \
+ VNSvOutPortD(dwIoBase + MAC_REG_ATIMDMACTL, DMACTL_RUN); \
+} while (0)
+
+#define MACvTransmitBCN(dwIoBase) \
+ VNSvOutPortB(dwIoBase + MAC_REG_BCNDMACTL, BEACON_READY)
+
+#define MACvClearStckDS(dwIoBase) \
+do { \
+ unsigned char byOrgValue; \
+ VNSvInPortB(dwIoBase + MAC_REG_STICKHW, &byOrgValue); \
+ byOrgValue = byOrgValue & 0xFC; \
+ VNSvOutPortB(dwIoBase + MAC_REG_STICKHW, byOrgValue); \
+} while (0)
+
+#define MACvReadISR(dwIoBase, pdwValue) \
+ VNSvInPortD(dwIoBase + MAC_REG_ISR, pdwValue)
+
+#define MACvWriteISR(dwIoBase, dwValue) \
+ VNSvOutPortD(dwIoBase + MAC_REG_ISR, dwValue)
+
+#define MACvIntEnable(dwIoBase, dwMask) \
+ VNSvOutPortD(dwIoBase + MAC_REG_IMR, dwMask)
+
+#define MACvIntDisable(dwIoBase) \
+ VNSvOutPortD(dwIoBase + MAC_REG_IMR, 0)
+
+#define MACvSelectPage0(dwIoBase) \
+ VNSvOutPortB(dwIoBase + MAC_REG_PAGE1SEL, 0)
+
+#define MACvSelectPage1(dwIoBase) \
+ VNSvOutPortB(dwIoBase + MAC_REG_PAGE1SEL, 1)
+
+#define MACvReadMIBCounter(dwIoBase, pdwCounter) \
+ VNSvInPortD(dwIoBase + MAC_REG_MIBCNTR, pdwCounter)
+
+#define MACvPwrEvntDisable(dwIoBase) \
+ VNSvOutPortW(dwIoBase + MAC_REG_WAKEUPEN0, 0x0000)
+
+#define MACvEnableProtectMD(dwIoBase) \
+do { \
+ unsigned long dwOrgValue; \
+ VNSvInPortD(dwIoBase + MAC_REG_ENCFG, &dwOrgValue); \
+ dwOrgValue = dwOrgValue | EnCFG_ProtectMd; \
+ VNSvOutPortD(dwIoBase + MAC_REG_ENCFG, dwOrgValue); \
+} while (0)
+
+#define MACvDisableProtectMD(dwIoBase) \
+do { \
+ unsigned long dwOrgValue; \
+ VNSvInPortD(dwIoBase + MAC_REG_ENCFG, &dwOrgValue); \
+ dwOrgValue = dwOrgValue & ~EnCFG_ProtectMd; \
+ VNSvOutPortD(dwIoBase + MAC_REG_ENCFG, dwOrgValue); \
+} while (0)
+
+#define MACvEnableBarkerPreambleMd(dwIoBase) \
+do { \
+ unsigned long dwOrgValue; \
+ VNSvInPortD(dwIoBase + MAC_REG_ENCFG, &dwOrgValue); \
+ dwOrgValue = dwOrgValue | EnCFG_BarkerPream; \
+ VNSvOutPortD(dwIoBase + MAC_REG_ENCFG, dwOrgValue); \
+} while (0)
+
+#define MACvDisableBarkerPreambleMd(dwIoBase) \
+do { \
+ unsigned long dwOrgValue; \
+ VNSvInPortD(dwIoBase + MAC_REG_ENCFG, &dwOrgValue); \
+ dwOrgValue = dwOrgValue & ~EnCFG_BarkerPream; \
+ VNSvOutPortD(dwIoBase + MAC_REG_ENCFG, dwOrgValue); \
+} while (0)
+
+#define MACvSetBBType(dwIoBase, byTyp) \
+do { \
+ unsigned long dwOrgValue; \
+ VNSvInPortD(dwIoBase + MAC_REG_ENCFG, &dwOrgValue); \
+ dwOrgValue = dwOrgValue & ~EnCFG_BBType_MASK; \
+ dwOrgValue = dwOrgValue | (unsigned long)byTyp; \
+ VNSvOutPortD(dwIoBase + MAC_REG_ENCFG, dwOrgValue); \
+} while (0)
+
+#define MACvReadATIMW(dwIoBase, pwCounter) \
+ VNSvInPortW(dwIoBase + MAC_REG_AIDATIM, pwCounter)
+
+#define MACvWriteATIMW(dwIoBase, wCounter) \
+ VNSvOutPortW(dwIoBase + MAC_REG_AIDATIM, wCounter)
+
+#define MACvWriteCRC16_128(dwIoBase, byRegOfs, wCRC) \
+do { \
+ VNSvOutPortB(dwIoBase + MAC_REG_PAGE1SEL, 1); \
+ VNSvOutPortW(dwIoBase + byRegOfs, wCRC); \
+ VNSvOutPortB(dwIoBase + MAC_REG_PAGE1SEL, 0); \
+} while (0)
+
+#define MACvGPIOIn(dwIoBase, pbyValue) \
+ VNSvInPortB(dwIoBase + MAC_REG_GPIOCTL1, pbyValue)
+
+#define MACvSetRFLE_LatchBase(dwIoBase) \
+ MACvWordRegBitsOn(dwIoBase, MAC_REG_SOFTPWRCTL, SOFTPWRCTL_RFLEOPT)
+
+bool MACbIsRegBitsOn(void __iomem *dwIoBase, unsigned char byRegOfs,
+ unsigned char byTestBits);
+bool MACbIsRegBitsOff(void __iomem *dwIoBase, unsigned char byRegOfs,
+ unsigned char byTestBits);
+
+bool MACbIsIntDisable(void __iomem *dwIoBase);
+
+void MACvSetShortRetryLimit(void __iomem *dwIoBase, unsigned char byRetryLimit);
+
+void MACvSetLongRetryLimit(void __iomem *dwIoBase, unsigned char byRetryLimit);
+void MACvGetLongRetryLimit(void __iomem *dwIoBase,
+ unsigned char *pbyRetryLimit);
+
+void MACvSetLoopbackMode(void __iomem *dwIoBase, unsigned char byLoopbackMode);
+
+void MACvSaveContext(void __iomem *dwIoBase, unsigned char *pbyCxtBuf);
+void MACvRestoreContext(void __iomem *dwIoBase, unsigned char *pbyCxtBuf);
+
+bool MACbSoftwareReset(void __iomem *dwIoBase);
+bool MACbSafeSoftwareReset(void __iomem *dwIoBase);
+bool MACbSafeRxOff(void __iomem *dwIoBase);
+bool MACbSafeTxOff(void __iomem *dwIoBase);
+bool MACbSafeStop(void __iomem *dwIoBase);
+bool MACbShutdown(void __iomem *dwIoBase);
+void MACvInitialize(void __iomem *dwIoBase);
+void MACvSetCurrRx0DescAddr(void __iomem *dwIoBase,
+ unsigned long dwCurrDescAddr);
+void MACvSetCurrRx1DescAddr(void __iomem *dwIoBase,
+ unsigned long dwCurrDescAddr);
+void MACvSetCurrTXDescAddr(int iTxType, void __iomem *dwIoBase,
+ unsigned long dwCurrDescAddr);
+void MACvSetCurrTx0DescAddrEx(void __iomem *dwIoBase,
+ unsigned long dwCurrDescAddr);
+void MACvSetCurrAC0DescAddrEx(void __iomem *dwIoBase,
+ unsigned long dwCurrDescAddr);
+void MACvSetCurrSyncDescAddrEx(void __iomem *dwIoBase,
+ unsigned long dwCurrDescAddr);
+void MACvSetCurrATIMDescAddrEx(void __iomem *dwIoBase,
+ unsigned long dwCurrDescAddr);
+void MACvTimer0MicroSDelay(void __iomem *dwIoBase, unsigned int uDelay);
+void MACvOneShotTimer1MicroSec(void __iomem *dwIoBase, unsigned int uDelayTime);
+
+void MACvSetMISCFifo(void __iomem *dwIoBase, unsigned short wOffset,
+ unsigned long dwData);
+
+bool MACbPSWakeup(void __iomem *dwIoBase);
+
+void MACvSetKeyEntry(void __iomem *dwIoBase, unsigned short wKeyCtl,
+ unsigned int uEntryIdx, unsigned int uKeyIdx,
+ unsigned char *pbyAddr, u32 *pdwKey,
+ unsigned char byLocalID);
+void MACvDisableKeyEntry(void __iomem *dwIoBase, unsigned int uEntryIdx);
+
+#endif /* __MAC_H__ */
diff --git a/drivers/staging/vt6655/mib.c b/drivers/staging/vt6655/mib.c
new file mode 100644
index 000000000..d55c76202
--- /dev/null
+++ b/drivers/staging/vt6655/mib.c
@@ -0,0 +1,139 @@
+/*
+ * Copyright (c) 1996, 2003 VIA Networking Technologies, Inc.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * File: mib.c
+ *
+ * Purpose: Implement MIB Data Structure
+ *
+ * Author: Tevin Chen
+ *
+ * Date: May 21, 1996
+ *
+ * Functions:
+ * STAvUpdateIstStatCounter - Update ISR statistic counter
+ * STAvUpdate802_11Counter - Update 802.11 mib counter
+ *
+ * Revision History:
+ *
+ */
+
+#include "mac.h"
+#include "mib.h"
+
+/*--------------------- Static Classes ----------------------------*/
+
+/*--------------------- Static Variables --------------------------*/
+
+/*--------------------- Static Functions --------------------------*/
+
+/*--------------------- Export Variables --------------------------*/
+
+/*--------------------- Export Functions --------------------------*/
+
+/*
+ * Description: Update Isr Statistic Counter
+ *
+ * Parameters:
+ * In:
+ * pStatistic - Pointer to Statistic Counter Data Structure
+ * wisr - Interrupt status
+ * Out:
+ * none
+ *
+ * Return Value: none
+ *
+ */
+void STAvUpdateIsrStatCounter(PSStatCounter pStatistic, unsigned long dwIsr)
+{
+ /**********************/
+ /* ABNORMAL interrupt */
+ /**********************/
+ /* not any IMR bit invoke irq */
+
+ if (dwIsr == 0) {
+ pStatistic->ISRStat.dwIsrUnknown++;
+ return;
+ }
+
+/* Added by Kyle */
+ if (dwIsr & ISR_TXDMA0) /* ISR, bit0 */
+ pStatistic->ISRStat.dwIsrTx0OK++; /* TXDMA0 successful */
+
+ if (dwIsr & ISR_AC0DMA) /* ISR, bit1 */
+ pStatistic->ISRStat.dwIsrAC0TxOK++; /* AC0DMA successful */
+
+ if (dwIsr & ISR_BNTX) /* ISR, bit2 */
+ pStatistic->ISRStat.dwIsrBeaconTxOK++; /* BeaconTx successful */
+
+ if (dwIsr & ISR_RXDMA0) /* ISR, bit3 */
+ pStatistic->ISRStat.dwIsrRx0OK++; /* Rx0 successful */
+
+ if (dwIsr & ISR_TBTT) /* ISR, bit4 */
+ pStatistic->ISRStat.dwIsrTBTTInt++; /* TBTT successful */
+
+ if (dwIsr & ISR_SOFTTIMER) /* ISR, bit6 */
+ pStatistic->ISRStat.dwIsrSTIMERInt++;
+
+ if (dwIsr & ISR_WATCHDOG) /* ISR, bit7 */
+ pStatistic->ISRStat.dwIsrWatchDog++;
+
+ if (dwIsr & ISR_FETALERR) /* ISR, bit8 */
+ pStatistic->ISRStat.dwIsrUnrecoverableError++;
+
+ if (dwIsr & ISR_SOFTINT) /* ISR, bit9 */
+ pStatistic->ISRStat.dwIsrSoftInterrupt++; /* software interrupt */
+
+ if (dwIsr & ISR_MIBNEARFULL) /* ISR, bit10 */
+ pStatistic->ISRStat.dwIsrMIBNearfull++;
+
+ if (dwIsr & ISR_RXNOBUF) /* ISR, bit11 */
+ pStatistic->ISRStat.dwIsrRxNoBuf++; /* Rx No Buff */
+
+ if (dwIsr & ISR_RXDMA1) /* ISR, bit12 */
+ pStatistic->ISRStat.dwIsrRx1OK++; /* Rx1 successful */
+
+ if (dwIsr & ISR_SOFTTIMER1) /* ISR, bit21 */
+ pStatistic->ISRStat.dwIsrSTIMER1Int++;
+}
+
+/*
+ * Description: Update 802.11 mib counter
+ *
+ * Parameters:
+ * In:
+ * p802_11Counter - Pointer to 802.11 mib counter
+ * pStatistic - Pointer to Statistic Counter Data Structure
+ * dwCounter - hardware counter for 802.11 mib
+ * Out:
+ * none
+ *
+ * Return Value: none
+ *
+ */
+void
+STAvUpdate802_11Counter(
+ PSDot11Counters p802_11Counter,
+ PSStatCounter pStatistic,
+ unsigned long dwCounter
+)
+{
+ p802_11Counter->RTSSuccessCount += (unsigned long long) (dwCounter & 0x000000ff);
+ p802_11Counter->RTSFailureCount += (unsigned long long) ((dwCounter & 0x0000ff00) >> 8);
+ p802_11Counter->ACKFailureCount += (unsigned long long) ((dwCounter & 0x00ff0000) >> 16);
+ p802_11Counter->FCSErrorCount += (unsigned long long) ((dwCounter & 0xff000000) >> 24);
+}
diff --git a/drivers/staging/vt6655/mib.h b/drivers/staging/vt6655/mib.h
new file mode 100644
index 000000000..5cb59b8a1
--- /dev/null
+++ b/drivers/staging/vt6655/mib.h
@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) 1996, 2003 VIA Networking Technologies, Inc.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * File: mib.h
+ *
+ * Purpose: Implement MIB Data Structure
+ *
+ * Author: Tevin Chen
+ *
+ * Date: May 21, 1996
+ *
+ */
+
+#ifndef __MIB_H__
+#define __MIB_H__
+
+#include "desc.h"
+
+//
+// 802.11 counter
+//
+
+typedef struct tagSDot11Counters {
+ unsigned long long RTSSuccessCount;
+ unsigned long long RTSFailureCount;
+ unsigned long long ACKFailureCount;
+ unsigned long long FCSErrorCount;
+} SDot11Counters, *PSDot11Counters;
+
+//
+// Custom counter
+//
+typedef struct tagSISRCounters {
+ unsigned long dwIsrTx0OK;
+ unsigned long dwIsrAC0TxOK;
+ unsigned long dwIsrBeaconTxOK;
+ unsigned long dwIsrRx0OK;
+ unsigned long dwIsrTBTTInt;
+ unsigned long dwIsrSTIMERInt;
+ unsigned long dwIsrWatchDog;
+ unsigned long dwIsrUnrecoverableError;
+ unsigned long dwIsrSoftInterrupt;
+ unsigned long dwIsrMIBNearfull;
+ unsigned long dwIsrRxNoBuf;
+
+ unsigned long dwIsrUnknown;
+
+ unsigned long dwIsrRx1OK;
+ unsigned long dwIsrSTIMER1Int;
+} SISRCounters, *PSISRCounters;
+
+//
+// statistic counter
+//
+typedef struct tagSStatCounter {
+ SISRCounters ISRStat;
+} SStatCounter, *PSStatCounter;
+
+void STAvUpdateIsrStatCounter(PSStatCounter pStatistic, unsigned long dwIsr);
+
+void STAvUpdate802_11Counter(
+ PSDot11Counters p802_11Counter,
+ PSStatCounter pStatistic,
+ unsigned long dwCounter
+);
+
+#endif // __MIB_H__
diff --git a/drivers/staging/vt6655/power.c b/drivers/staging/vt6655/power.c
new file mode 100644
index 000000000..be3c4e949
--- /dev/null
+++ b/drivers/staging/vt6655/power.c
@@ -0,0 +1,167 @@
+/*
+ * Copyright (c) 1996, 2003 VIA Networking Technologies, Inc.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ *
+ * File: power.c
+ *
+ * Purpose: Handles 802.11 power management functions
+ *
+ * Author: Lyndon Chen
+ *
+ * Date: July 17, 2002
+ *
+ * Functions:
+ * PSvEnablePowerSaving - Enable Power Saving Mode
+ * PSvDiasblePowerSaving - Disable Power Saving Mode
+ * PSbConsiderPowerDown - Decide if we can Power Down
+ * PSvSendPSPOLL - Send PS-POLL packet
+ * PSbSendNullPacket - Send Null packet
+ * PSbIsNextTBTTWakeUp - Decide if we need to wake up at next Beacon
+ *
+ * Revision History:
+ *
+ */
+
+#include "mac.h"
+#include "device.h"
+#include "power.h"
+#include "card.h"
+
+/*--------------------- Static Definitions -------------------------*/
+
+/*--------------------- Static Classes ----------------------------*/
+
+/*--------------------- Static Functions --------------------------*/
+
+/*--------------------- Export Variables --------------------------*/
+
+/*--------------------- Export Functions --------------------------*/
+
+/*+
+ *
+ * Routine Description:
+ * Enable hw power saving functions
+ *
+ * Return Value:
+ * None.
+ *
+ -*/
+
+void
+PSvEnablePowerSaving(
+ void *hDeviceContext,
+ unsigned short wListenInterval
+)
+{
+ struct vnt_private *pDevice = hDeviceContext;
+ u16 wAID = pDevice->current_aid | BIT(14) | BIT(15);
+
+ /* set period of power up before TBTT */
+ VNSvOutPortW(pDevice->PortOffset + MAC_REG_PWBT, C_PWBT);
+ if (pDevice->op_mode != NL80211_IFTYPE_ADHOC) {
+ /* set AID */
+ VNSvOutPortW(pDevice->PortOffset + MAC_REG_AIDATIM, wAID);
+ } else {
+ /* set ATIM Window */
+#if 0 /* TODO atim window */
+ MACvWriteATIMW(pDevice->PortOffset, pMgmt->wCurrATIMWindow);
+#endif
+ }
+ /* Set AutoSleep */
+ MACvRegBitsOn(pDevice->PortOffset, MAC_REG_PSCFG, PSCFG_AUTOSLEEP);
+ /* Set HWUTSF */
+ MACvRegBitsOn(pDevice->PortOffset, MAC_REG_TFTCTL, TFTCTL_HWUTSF);
+
+ if (wListenInterval >= 2) {
+ /* clear always listen beacon */
+ MACvRegBitsOff(pDevice->PortOffset, MAC_REG_PSCTL, PSCTL_ALBCN);
+ /* first time set listen next beacon */
+ MACvRegBitsOn(pDevice->PortOffset, MAC_REG_PSCTL, PSCTL_LNBCN);
+ } else {
+ /* always listen beacon */
+ MACvRegBitsOn(pDevice->PortOffset, MAC_REG_PSCTL, PSCTL_ALBCN);
+ }
+
+ /* enable power saving hw function */
+ MACvRegBitsOn(pDevice->PortOffset, MAC_REG_PSCTL, PSCTL_PSEN);
+ pDevice->bEnablePSMode = true;
+
+ pDevice->bPWBitOn = true;
+ pr_debug("PS:Power Saving Mode Enable...\n");
+}
+
+/*+
+ *
+ * Routine Description:
+ * Disable hw power saving functions
+ *
+ * Return Value:
+ * None.
+ *
+ -*/
+
+void
+PSvDisablePowerSaving(
+ void *hDeviceContext
+)
+{
+ struct vnt_private *pDevice = hDeviceContext;
+
+ /* disable power saving hw function */
+ MACbPSWakeup(pDevice->PortOffset);
+ /* clear AutoSleep */
+ MACvRegBitsOff(pDevice->PortOffset, MAC_REG_PSCFG, PSCFG_AUTOSLEEP);
+ /* clear HWUTSF */
+ MACvRegBitsOff(pDevice->PortOffset, MAC_REG_TFTCTL, TFTCTL_HWUTSF);
+ /* set always listen beacon */
+ MACvRegBitsOn(pDevice->PortOffset, MAC_REG_PSCTL, PSCTL_ALBCN);
+
+ pDevice->bEnablePSMode = false;
+
+ pDevice->bPWBitOn = false;
+}
+
+
+/*+
+ *
+ * Routine Description:
+ * Check if Next TBTT must wake up
+ *
+ * Return Value:
+ * None.
+ *
+ -*/
+
+bool
+PSbIsNextTBTTWakeUp(
+ void *hDeviceContext
+)
+{
+ struct vnt_private *pDevice = hDeviceContext;
+ struct ieee80211_hw *hw = pDevice->hw;
+ struct ieee80211_conf *conf = &hw->conf;
+ bool bWakeUp = false;
+
+ if (conf->listen_interval == 1) {
+ /* Turn on wake up to listen next beacon */
+ MACvRegBitsOn(pDevice->PortOffset, MAC_REG_PSCTL, PSCTL_LNBCN);
+ bWakeUp = true;
+ }
+
+ return bWakeUp;
+}
diff --git a/drivers/staging/vt6655/power.h b/drivers/staging/vt6655/power.h
new file mode 100644
index 000000000..1083341b2
--- /dev/null
+++ b/drivers/staging/vt6655/power.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 1996, 2003 VIA Networking Technologies, Inc.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * File: power.h
+ *
+ * Purpose: Handles 802.11 power management functions
+ *
+ * Author: Lyndon Chen
+ *
+ * Date: July 17, 2002
+ *
+ */
+
+#ifndef __POWER_H__
+#define __POWER_H__
+
+#define C_PWBT 1000 // micro sec. power up before TBTT
+#define PS_FAST_INTERVAL 1 // Fast power saving listen interval
+#define PS_MAX_INTERVAL 4 // MAX power saving listen interval
+
+void
+PSvDisablePowerSaving(
+ void *hDeviceContext
+);
+
+void
+PSvEnablePowerSaving(
+ void *hDeviceContext,
+ unsigned short wListenInterval
+);
+
+
+bool
+PSbIsNextTBTTWakeUp(
+ void *hDeviceContext
+);
+
+#endif //__POWER_H__
diff --git a/drivers/staging/vt6655/rf.c b/drivers/staging/vt6655/rf.c
new file mode 100644
index 000000000..7626f635f
--- /dev/null
+++ b/drivers/staging/vt6655/rf.c
@@ -0,0 +1,965 @@
+/*
+ * Copyright (c) 1996, 2003 VIA Networking Technologies, Inc.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ *
+ * File: rf.c
+ *
+ * Purpose: rf function code
+ *
+ * Author: Jerry Chen
+ *
+ * Date: Feb. 19, 2004
+ *
+ * Functions:
+ * IFRFbWriteEmbedded - Embedded write RF register via MAC
+ *
+ * Revision History:
+ * RobertYu 2005
+ * chester 2008
+ *
+ */
+
+#include "mac.h"
+#include "srom.h"
+#include "rf.h"
+#include "baseband.h"
+
+#define BY_AL2230_REG_LEN 23 //24bit
+#define CB_AL2230_INIT_SEQ 15
+#define SWITCH_CHANNEL_DELAY_AL2230 200 //us
+#define AL2230_PWR_IDX_LEN 64
+
+#define BY_AL7230_REG_LEN 23 //24bit
+#define CB_AL7230_INIT_SEQ 16
+#define SWITCH_CHANNEL_DELAY_AL7230 200 //us
+#define AL7230_PWR_IDX_LEN 64
+
+static const unsigned long dwAL2230InitTable[CB_AL2230_INIT_SEQ] = {
+ 0x03F79000+(BY_AL2230_REG_LEN<<3)+IFREGCTL_REGW, //
+ 0x03333100+(BY_AL2230_REG_LEN<<3)+IFREGCTL_REGW, //
+ 0x01A00200+(BY_AL2230_REG_LEN<<3)+IFREGCTL_REGW, //
+ 0x00FFF300+(BY_AL2230_REG_LEN<<3)+IFREGCTL_REGW, //
+ 0x0005A400+(BY_AL2230_REG_LEN<<3)+IFREGCTL_REGW, //
+ 0x0F4DC500+(BY_AL2230_REG_LEN<<3)+IFREGCTL_REGW, //
+ 0x0805B600+(BY_AL2230_REG_LEN<<3)+IFREGCTL_REGW, //
+ 0x0146C700+(BY_AL2230_REG_LEN<<3)+IFREGCTL_REGW, //
+ 0x00068800+(BY_AL2230_REG_LEN<<3)+IFREGCTL_REGW, //
+ 0x0403B900+(BY_AL2230_REG_LEN<<3)+IFREGCTL_REGW, //
+ 0x00DBBA00+(BY_AL2230_REG_LEN<<3)+IFREGCTL_REGW, //
+ 0x00099B00+(BY_AL2230_REG_LEN<<3)+IFREGCTL_REGW, //
+ 0x0BDFFC00+(BY_AL2230_REG_LEN<<3)+IFREGCTL_REGW,
+ 0x00000D00+(BY_AL2230_REG_LEN<<3)+IFREGCTL_REGW,
+ 0x00580F00+(BY_AL2230_REG_LEN<<3)+IFREGCTL_REGW
+};
+
+static const unsigned long dwAL2230ChannelTable0[CB_MAX_CHANNEL] = {
+ 0x03F79000+(BY_AL2230_REG_LEN<<3)+IFREGCTL_REGW, // channel = 1, Tf = 2412MHz
+ 0x03F79000+(BY_AL2230_REG_LEN<<3)+IFREGCTL_REGW, // channel = 2, Tf = 2417MHz
+ 0x03E79000+(BY_AL2230_REG_LEN<<3)+IFREGCTL_REGW, // channel = 3, Tf = 2422MHz
+ 0x03E79000+(BY_AL2230_REG_LEN<<3)+IFREGCTL_REGW, // channel = 4, Tf = 2427MHz
+ 0x03F7A000+(BY_AL2230_REG_LEN<<3)+IFREGCTL_REGW, // channel = 5, Tf = 2432MHz
+ 0x03F7A000+(BY_AL2230_REG_LEN<<3)+IFREGCTL_REGW, // channel = 6, Tf = 2437MHz
+ 0x03E7A000+(BY_AL2230_REG_LEN<<3)+IFREGCTL_REGW, // channel = 7, Tf = 2442MHz
+ 0x03E7A000+(BY_AL2230_REG_LEN<<3)+IFREGCTL_REGW, // channel = 8, Tf = 2447MHz
+ 0x03F7B000+(BY_AL2230_REG_LEN<<3)+IFREGCTL_REGW, // channel = 9, Tf = 2452MHz
+ 0x03F7B000+(BY_AL2230_REG_LEN<<3)+IFREGCTL_REGW, // channel = 10, Tf = 2457MHz
+ 0x03E7B000+(BY_AL2230_REG_LEN<<3)+IFREGCTL_REGW, // channel = 11, Tf = 2462MHz
+ 0x03E7B000+(BY_AL2230_REG_LEN<<3)+IFREGCTL_REGW, // channel = 12, Tf = 2467MHz
+ 0x03F7C000+(BY_AL2230_REG_LEN<<3)+IFREGCTL_REGW, // channel = 13, Tf = 2472MHz
+ 0x03E7C000+(BY_AL2230_REG_LEN<<3)+IFREGCTL_REGW // channel = 14, Tf = 2412M
+};
+
+static const unsigned long dwAL2230ChannelTable1[CB_MAX_CHANNEL] = {
+ 0x03333100+(BY_AL2230_REG_LEN<<3)+IFREGCTL_REGW, // channel = 1, Tf = 2412MHz
+ 0x0B333100+(BY_AL2230_REG_LEN<<3)+IFREGCTL_REGW, // channel = 2, Tf = 2417MHz
+ 0x03333100+(BY_AL2230_REG_LEN<<3)+IFREGCTL_REGW, // channel = 3, Tf = 2422MHz
+ 0x0B333100+(BY_AL2230_REG_LEN<<3)+IFREGCTL_REGW, // channel = 4, Tf = 2427MHz
+ 0x03333100+(BY_AL2230_REG_LEN<<3)+IFREGCTL_REGW, // channel = 5, Tf = 2432MHz
+ 0x0B333100+(BY_AL2230_REG_LEN<<3)+IFREGCTL_REGW, // channel = 6, Tf = 2437MHz
+ 0x03333100+(BY_AL2230_REG_LEN<<3)+IFREGCTL_REGW, // channel = 7, Tf = 2442MHz
+ 0x0B333100+(BY_AL2230_REG_LEN<<3)+IFREGCTL_REGW, // channel = 8, Tf = 2447MHz
+ 0x03333100+(BY_AL2230_REG_LEN<<3)+IFREGCTL_REGW, // channel = 9, Tf = 2452MHz
+ 0x0B333100+(BY_AL2230_REG_LEN<<3)+IFREGCTL_REGW, // channel = 10, Tf = 2457MHz
+ 0x03333100+(BY_AL2230_REG_LEN<<3)+IFREGCTL_REGW, // channel = 11, Tf = 2462MHz
+ 0x0B333100+(BY_AL2230_REG_LEN<<3)+IFREGCTL_REGW, // channel = 12, Tf = 2467MHz
+ 0x03333100+(BY_AL2230_REG_LEN<<3)+IFREGCTL_REGW, // channel = 13, Tf = 2472MHz
+ 0x06666100+(BY_AL2230_REG_LEN<<3)+IFREGCTL_REGW // channel = 14, Tf = 2412M
+};
+
+static unsigned long dwAL2230PowerTable[AL2230_PWR_IDX_LEN] = {
+ 0x04040900+(BY_AL2230_REG_LEN<<3)+IFREGCTL_REGW,
+ 0x04041900+(BY_AL2230_REG_LEN<<3)+IFREGCTL_REGW,
+ 0x04042900+(BY_AL2230_REG_LEN<<3)+IFREGCTL_REGW,
+ 0x04043900+(BY_AL2230_REG_LEN<<3)+IFREGCTL_REGW,
+ 0x04044900+(BY_AL2230_REG_LEN<<3)+IFREGCTL_REGW,
+ 0x04045900+(BY_AL2230_REG_LEN<<3)+IFREGCTL_REGW,
+ 0x04046900+(BY_AL2230_REG_LEN<<3)+IFREGCTL_REGW,
+ 0x04047900+(BY_AL2230_REG_LEN<<3)+IFREGCTL_REGW,
+ 0x04048900+(BY_AL2230_REG_LEN<<3)+IFREGCTL_REGW,
+ 0x04049900+(BY_AL2230_REG_LEN<<3)+IFREGCTL_REGW,
+ 0x0404A900+(BY_AL2230_REG_LEN<<3)+IFREGCTL_REGW,
+ 0x0404B900+(BY_AL2230_REG_LEN<<3)+IFREGCTL_REGW,
+ 0x0404C900+(BY_AL2230_REG_LEN<<3)+IFREGCTL_REGW,
+ 0x0404D900+(BY_AL2230_REG_LEN<<3)+IFREGCTL_REGW,
+ 0x0404E900+(BY_AL2230_REG_LEN<<3)+IFREGCTL_REGW,
+ 0x0404F900+(BY_AL2230_REG_LEN<<3)+IFREGCTL_REGW,
+ 0x04050900+(BY_AL2230_REG_LEN<<3)+IFREGCTL_REGW,
+ 0x04051900+(BY_AL2230_REG_LEN<<3)+IFREGCTL_REGW,
+ 0x04052900+(BY_AL2230_REG_LEN<<3)+IFREGCTL_REGW,
+ 0x04053900+(BY_AL2230_REG_LEN<<3)+IFREGCTL_REGW,
+ 0x04054900+(BY_AL2230_REG_LEN<<3)+IFREGCTL_REGW,
+ 0x04055900+(BY_AL2230_REG_LEN<<3)+IFREGCTL_REGW,
+ 0x04056900+(BY_AL2230_REG_LEN<<3)+IFREGCTL_REGW,
+ 0x04057900+(BY_AL2230_REG_LEN<<3)+IFREGCTL_REGW,
+ 0x04058900+(BY_AL2230_REG_LEN<<3)+IFREGCTL_REGW,
+ 0x04059900+(BY_AL2230_REG_LEN<<3)+IFREGCTL_REGW,
+ 0x0405A900+(BY_AL2230_REG_LEN<<3)+IFREGCTL_REGW,
+ 0x0405B900+(BY_AL2230_REG_LEN<<3)+IFREGCTL_REGW,
+ 0x0405C900+(BY_AL2230_REG_LEN<<3)+IFREGCTL_REGW,
+ 0x0405D900+(BY_AL2230_REG_LEN<<3)+IFREGCTL_REGW,
+ 0x0405E900+(BY_AL2230_REG_LEN<<3)+IFREGCTL_REGW,
+ 0x0405F900+(BY_AL2230_REG_LEN<<3)+IFREGCTL_REGW,
+ 0x04060900+(BY_AL2230_REG_LEN<<3)+IFREGCTL_REGW,
+ 0x04061900+(BY_AL2230_REG_LEN<<3)+IFREGCTL_REGW,
+ 0x04062900+(BY_AL2230_REG_LEN<<3)+IFREGCTL_REGW,
+ 0x04063900+(BY_AL2230_REG_LEN<<3)+IFREGCTL_REGW,
+ 0x04064900+(BY_AL2230_REG_LEN<<3)+IFREGCTL_REGW,
+ 0x04065900+(BY_AL2230_REG_LEN<<3)+IFREGCTL_REGW,
+ 0x04066900+(BY_AL2230_REG_LEN<<3)+IFREGCTL_REGW,
+ 0x04067900+(BY_AL2230_REG_LEN<<3)+IFREGCTL_REGW,
+ 0x04068900+(BY_AL2230_REG_LEN<<3)+IFREGCTL_REGW,
+ 0x04069900+(BY_AL2230_REG_LEN<<3)+IFREGCTL_REGW,
+ 0x0406A900+(BY_AL2230_REG_LEN<<3)+IFREGCTL_REGW,
+ 0x0406B900+(BY_AL2230_REG_LEN<<3)+IFREGCTL_REGW,
+ 0x0406C900+(BY_AL2230_REG_LEN<<3)+IFREGCTL_REGW,
+ 0x0406D900+(BY_AL2230_REG_LEN<<3)+IFREGCTL_REGW,
+ 0x0406E900+(BY_AL2230_REG_LEN<<3)+IFREGCTL_REGW,
+ 0x0406F900+(BY_AL2230_REG_LEN<<3)+IFREGCTL_REGW,
+ 0x04070900+(BY_AL2230_REG_LEN<<3)+IFREGCTL_REGW,
+ 0x04071900+(BY_AL2230_REG_LEN<<3)+IFREGCTL_REGW,
+ 0x04072900+(BY_AL2230_REG_LEN<<3)+IFREGCTL_REGW,
+ 0x04073900+(BY_AL2230_REG_LEN<<3)+IFREGCTL_REGW,
+ 0x04074900+(BY_AL2230_REG_LEN<<3)+IFREGCTL_REGW,
+ 0x04075900+(BY_AL2230_REG_LEN<<3)+IFREGCTL_REGW,
+ 0x04076900+(BY_AL2230_REG_LEN<<3)+IFREGCTL_REGW,
+ 0x04077900+(BY_AL2230_REG_LEN<<3)+IFREGCTL_REGW,
+ 0x04078900+(BY_AL2230_REG_LEN<<3)+IFREGCTL_REGW,
+ 0x04079900+(BY_AL2230_REG_LEN<<3)+IFREGCTL_REGW,
+ 0x0407A900+(BY_AL2230_REG_LEN<<3)+IFREGCTL_REGW,
+ 0x0407B900+(BY_AL2230_REG_LEN<<3)+IFREGCTL_REGW,
+ 0x0407C900+(BY_AL2230_REG_LEN<<3)+IFREGCTL_REGW,
+ 0x0407D900+(BY_AL2230_REG_LEN<<3)+IFREGCTL_REGW,
+ 0x0407E900+(BY_AL2230_REG_LEN<<3)+IFREGCTL_REGW,
+ 0x0407F900+(BY_AL2230_REG_LEN<<3)+IFREGCTL_REGW
+};
+
+// 40MHz reference frequency
+// Need to Pull PLLON(PE3) low when writing channel registers through 3-wire.
+static const unsigned long dwAL7230InitTable[CB_AL7230_INIT_SEQ] = {
+ 0x00379000+(BY_AL7230_REG_LEN<<3)+IFREGCTL_REGW, // Channel1 // Need modify for 11a
+ 0x13333100+(BY_AL7230_REG_LEN<<3)+IFREGCTL_REGW, // Channel1 // Need modify for 11a
+ 0x841FF200+(BY_AL7230_REG_LEN<<3)+IFREGCTL_REGW, // Need modify for 11a: 451FE2
+ 0x3FDFA300+(BY_AL7230_REG_LEN<<3)+IFREGCTL_REGW, // Need modify for 11a: 5FDFA3
+ 0x7FD78400+(BY_AL7230_REG_LEN<<3)+IFREGCTL_REGW, // 11b/g // Need modify for 11a
+ // RoberYu:20050113, Rev0.47 Regsiter Setting Guide
+ 0x802B5500+(BY_AL7230_REG_LEN<<3)+IFREGCTL_REGW, // Need modify for 11a: 8D1B55
+ 0x56AF3600+(BY_AL7230_REG_LEN<<3)+IFREGCTL_REGW,
+ 0xCE020700+(BY_AL7230_REG_LEN<<3)+IFREGCTL_REGW, // Need modify for 11a: 860207
+ 0x6EBC0800+(BY_AL7230_REG_LEN<<3)+IFREGCTL_REGW,
+ 0x221BB900+(BY_AL7230_REG_LEN<<3)+IFREGCTL_REGW,
+ 0xE0000A00+(BY_AL7230_REG_LEN<<3)+IFREGCTL_REGW, // Need modify for 11a: E0600A
+ 0x08031B00+(BY_AL7230_REG_LEN<<3)+IFREGCTL_REGW, // init 0x080B1B00 => 0x080F1B00 for 3 wire control TxGain(D10)
+ // RoberYu:20050113, Rev0.47 Regsiter Setting Guide
+ 0x000A3C00+(BY_AL7230_REG_LEN<<3)+IFREGCTL_REGW, // Need modify for 11a: 00143C
+ 0xFFFFFD00+(BY_AL7230_REG_LEN<<3)+IFREGCTL_REGW,
+ 0x00000E00+(BY_AL7230_REG_LEN<<3)+IFREGCTL_REGW,
+ 0x1ABA8F00+(BY_AL7230_REG_LEN<<3)+IFREGCTL_REGW // Need modify for 11a: 12BACF
+};
+
+static const unsigned long dwAL7230InitTableAMode[CB_AL7230_INIT_SEQ] = {
+ 0x0FF52000+(BY_AL7230_REG_LEN<<3)+IFREGCTL_REGW, // Channel184 // Need modify for 11b/g
+ 0x00000100+(BY_AL7230_REG_LEN<<3)+IFREGCTL_REGW, // Channel184 // Need modify for 11b/g
+ 0x451FE200+(BY_AL7230_REG_LEN<<3)+IFREGCTL_REGW, // Need modify for 11b/g
+ 0x5FDFA300+(BY_AL7230_REG_LEN<<3)+IFREGCTL_REGW, // Need modify for 11b/g
+ 0x67F78400+(BY_AL7230_REG_LEN<<3)+IFREGCTL_REGW, // 11a // Need modify for 11b/g
+ 0x853F5500+(BY_AL7230_REG_LEN<<3)+IFREGCTL_REGW, // Need modify for 11b/g, RoberYu:20050113
+ 0x56AF3600+(BY_AL7230_REG_LEN<<3)+IFREGCTL_REGW,
+ 0xCE020700+(BY_AL7230_REG_LEN<<3)+IFREGCTL_REGW, // Need modify for 11b/g
+ 0x6EBC0800+(BY_AL7230_REG_LEN<<3)+IFREGCTL_REGW,
+ 0x221BB900+(BY_AL7230_REG_LEN<<3)+IFREGCTL_REGW,
+ 0xE0600A00+(BY_AL7230_REG_LEN<<3)+IFREGCTL_REGW, // Need modify for 11b/g
+ 0x08031B00+(BY_AL7230_REG_LEN<<3)+IFREGCTL_REGW, // init 0x080B1B00 => 0x080F1B00 for 3 wire control TxGain(D10)
+ 0x00147C00+(BY_AL7230_REG_LEN<<3)+IFREGCTL_REGW, // Need modify for 11b/g
+ 0xFFFFFD00+(BY_AL7230_REG_LEN<<3)+IFREGCTL_REGW,
+ 0x00000E00+(BY_AL7230_REG_LEN<<3)+IFREGCTL_REGW,
+ 0x12BACF00+(BY_AL7230_REG_LEN<<3)+IFREGCTL_REGW // Need modify for 11b/g
+};
+
+static const unsigned long dwAL7230ChannelTable0[CB_MAX_CHANNEL] = {
+ 0x00379000+(BY_AL7230_REG_LEN<<3)+IFREGCTL_REGW, // channel = 1, Tf = 2412MHz
+ 0x00379000+(BY_AL7230_REG_LEN<<3)+IFREGCTL_REGW, // channel = 2, Tf = 2417MHz
+ 0x00379000+(BY_AL7230_REG_LEN<<3)+IFREGCTL_REGW, // channel = 3, Tf = 2422MHz
+ 0x00379000+(BY_AL7230_REG_LEN<<3)+IFREGCTL_REGW, // channel = 4, Tf = 2427MHz
+ 0x0037A000+(BY_AL7230_REG_LEN<<3)+IFREGCTL_REGW, // channel = 5, Tf = 2432MHz
+ 0x0037A000+(BY_AL7230_REG_LEN<<3)+IFREGCTL_REGW, // channel = 6, Tf = 2437MHz
+ 0x0037A000+(BY_AL7230_REG_LEN<<3)+IFREGCTL_REGW, // channel = 7, Tf = 2442MHz
+ 0x0037A000+(BY_AL7230_REG_LEN<<3)+IFREGCTL_REGW, // channel = 8, Tf = 2447MHz //RobertYu: 20050218, update for APNode 0.49
+ 0x0037B000+(BY_AL7230_REG_LEN<<3)+IFREGCTL_REGW, // channel = 9, Tf = 2452MHz //RobertYu: 20050218, update for APNode 0.49
+ 0x0037B000+(BY_AL7230_REG_LEN<<3)+IFREGCTL_REGW, // channel = 10, Tf = 2457MHz //RobertYu: 20050218, update for APNode 0.49
+ 0x0037B000+(BY_AL7230_REG_LEN<<3)+IFREGCTL_REGW, // channel = 11, Tf = 2462MHz //RobertYu: 20050218, update for APNode 0.49
+ 0x0037B000+(BY_AL7230_REG_LEN<<3)+IFREGCTL_REGW, // channel = 12, Tf = 2467MHz //RobertYu: 20050218, update for APNode 0.49
+ 0x0037C000+(BY_AL7230_REG_LEN<<3)+IFREGCTL_REGW, // channel = 13, Tf = 2472MHz //RobertYu: 20050218, update for APNode 0.49
+ 0x0037C000+(BY_AL7230_REG_LEN<<3)+IFREGCTL_REGW, // channel = 14, Tf = 2484MHz
+
+ // 4.9G => Ch 183, 184, 185, 187, 188, 189, 192, 196 (Value:15 ~ 22)
+ 0x0FF52000+(BY_AL7230_REG_LEN<<3)+IFREGCTL_REGW, // channel = 183, Tf = 4915MHz (15)
+ 0x0FF52000+(BY_AL7230_REG_LEN<<3)+IFREGCTL_REGW, // channel = 184, Tf = 4920MHz (16)
+ 0x0FF52000+(BY_AL7230_REG_LEN<<3)+IFREGCTL_REGW, // channel = 185, Tf = 4925MHz (17)
+ 0x0FF52000+(BY_AL7230_REG_LEN<<3)+IFREGCTL_REGW, // channel = 187, Tf = 4935MHz (18)
+ 0x0FF52000+(BY_AL7230_REG_LEN<<3)+IFREGCTL_REGW, // channel = 188, Tf = 4940MHz (19)
+ 0x0FF52000+(BY_AL7230_REG_LEN<<3)+IFREGCTL_REGW, // channel = 189, Tf = 4945MHz (20)
+ 0x0FF53000+(BY_AL7230_REG_LEN<<3)+IFREGCTL_REGW, // channel = 192, Tf = 4960MHz (21)
+ 0x0FF53000+(BY_AL7230_REG_LEN<<3)+IFREGCTL_REGW, // channel = 196, Tf = 4980MHz (22)
+
+ // 5G => Ch 7, 8, 9, 11, 12, 16, 34, 36, 38, 40, 42, 44, 46, 48, 52, 56, 60, 64,
+ // 100, 104, 108, 112, 116, 120, 124, 128, 132, 136, 140, 149, 153, 157, 161, 165 (Value 23 ~ 56)
+
+ 0x0FF54000+(BY_AL7230_REG_LEN<<3)+IFREGCTL_REGW, // channel = 7, Tf = 5035MHz (23)
+ 0x0FF54000+(BY_AL7230_REG_LEN<<3)+IFREGCTL_REGW, // channel = 8, Tf = 5040MHz (24)
+ 0x0FF54000+(BY_AL7230_REG_LEN<<3)+IFREGCTL_REGW, // channel = 9, Tf = 5045MHz (25)
+ 0x0FF54000+(BY_AL7230_REG_LEN<<3)+IFREGCTL_REGW, // channel = 11, Tf = 5055MHz (26)
+ 0x0FF54000+(BY_AL7230_REG_LEN<<3)+IFREGCTL_REGW, // channel = 12, Tf = 5060MHz (27)
+ 0x0FF55000+(BY_AL7230_REG_LEN<<3)+IFREGCTL_REGW, // channel = 16, Tf = 5080MHz (28)
+ 0x0FF56000+(BY_AL7230_REG_LEN<<3)+IFREGCTL_REGW, // channel = 34, Tf = 5170MHz (29)
+ 0x0FF56000+(BY_AL7230_REG_LEN<<3)+IFREGCTL_REGW, // channel = 36, Tf = 5180MHz (30)
+ 0x0FF57000+(BY_AL7230_REG_LEN<<3)+IFREGCTL_REGW, // channel = 38, Tf = 5190MHz (31) //RobertYu: 20050218, update for APNode 0.49
+ 0x0FF57000+(BY_AL7230_REG_LEN<<3)+IFREGCTL_REGW, // channel = 40, Tf = 5200MHz (32)
+ 0x0FF57000+(BY_AL7230_REG_LEN<<3)+IFREGCTL_REGW, // channel = 42, Tf = 5210MHz (33)
+ 0x0FF57000+(BY_AL7230_REG_LEN<<3)+IFREGCTL_REGW, // channel = 44, Tf = 5220MHz (34)
+ 0x0FF57000+(BY_AL7230_REG_LEN<<3)+IFREGCTL_REGW, // channel = 46, Tf = 5230MHz (35)
+ 0x0FF57000+(BY_AL7230_REG_LEN<<3)+IFREGCTL_REGW, // channel = 48, Tf = 5240MHz (36)
+ 0x0FF58000+(BY_AL7230_REG_LEN<<3)+IFREGCTL_REGW, // channel = 52, Tf = 5260MHz (37)
+ 0x0FF58000+(BY_AL7230_REG_LEN<<3)+IFREGCTL_REGW, // channel = 56, Tf = 5280MHz (38)
+ 0x0FF58000+(BY_AL7230_REG_LEN<<3)+IFREGCTL_REGW, // channel = 60, Tf = 5300MHz (39)
+ 0x0FF59000+(BY_AL7230_REG_LEN<<3)+IFREGCTL_REGW, // channel = 64, Tf = 5320MHz (40)
+
+ 0x0FF5C000+(BY_AL7230_REG_LEN<<3)+IFREGCTL_REGW, // channel = 100, Tf = 5500MHz (41)
+ 0x0FF5C000+(BY_AL7230_REG_LEN<<3)+IFREGCTL_REGW, // channel = 104, Tf = 5520MHz (42)
+ 0x0FF5C000+(BY_AL7230_REG_LEN<<3)+IFREGCTL_REGW, // channel = 108, Tf = 5540MHz (43)
+ 0x0FF5D000+(BY_AL7230_REG_LEN<<3)+IFREGCTL_REGW, // channel = 112, Tf = 5560MHz (44)
+ 0x0FF5D000+(BY_AL7230_REG_LEN<<3)+IFREGCTL_REGW, // channel = 116, Tf = 5580MHz (45)
+ 0x0FF5D000+(BY_AL7230_REG_LEN<<3)+IFREGCTL_REGW, // channel = 120, Tf = 5600MHz (46)
+ 0x0FF5E000+(BY_AL7230_REG_LEN<<3)+IFREGCTL_REGW, // channel = 124, Tf = 5620MHz (47)
+ 0x0FF5E000+(BY_AL7230_REG_LEN<<3)+IFREGCTL_REGW, // channel = 128, Tf = 5640MHz (48)
+ 0x0FF5E000+(BY_AL7230_REG_LEN<<3)+IFREGCTL_REGW, // channel = 132, Tf = 5660MHz (49)
+ 0x0FF5F000+(BY_AL7230_REG_LEN<<3)+IFREGCTL_REGW, // channel = 136, Tf = 5680MHz (50)
+ 0x0FF5F000+(BY_AL7230_REG_LEN<<3)+IFREGCTL_REGW, // channel = 140, Tf = 5700MHz (51)
+ 0x0FF60000+(BY_AL7230_REG_LEN<<3)+IFREGCTL_REGW, // channel = 149, Tf = 5745MHz (52)
+ 0x0FF60000+(BY_AL7230_REG_LEN<<3)+IFREGCTL_REGW, // channel = 153, Tf = 5765MHz (53)
+ 0x0FF60000+(BY_AL7230_REG_LEN<<3)+IFREGCTL_REGW, // channel = 157, Tf = 5785MHz (54)
+ 0x0FF61000+(BY_AL7230_REG_LEN<<3)+IFREGCTL_REGW, // channel = 161, Tf = 5805MHz (55)
+ 0x0FF61000+(BY_AL7230_REG_LEN<<3)+IFREGCTL_REGW // channel = 165, Tf = 5825MHz (56)
+};
+
+static const unsigned long dwAL7230ChannelTable1[CB_MAX_CHANNEL] = {
+ 0x13333100+(BY_AL7230_REG_LEN<<3)+IFREGCTL_REGW, // channel = 1, Tf = 2412MHz
+ 0x1B333100+(BY_AL7230_REG_LEN<<3)+IFREGCTL_REGW, // channel = 2, Tf = 2417MHz
+ 0x03333100+(BY_AL7230_REG_LEN<<3)+IFREGCTL_REGW, // channel = 3, Tf = 2422MHz
+ 0x0B333100+(BY_AL7230_REG_LEN<<3)+IFREGCTL_REGW, // channel = 4, Tf = 2427MHz
+ 0x13333100+(BY_AL7230_REG_LEN<<3)+IFREGCTL_REGW, // channel = 5, Tf = 2432MHz
+ 0x1B333100+(BY_AL7230_REG_LEN<<3)+IFREGCTL_REGW, // channel = 6, Tf = 2437MHz
+ 0x03333100+(BY_AL7230_REG_LEN<<3)+IFREGCTL_REGW, // channel = 7, Tf = 2442MHz
+ 0x0B333100+(BY_AL7230_REG_LEN<<3)+IFREGCTL_REGW, // channel = 8, Tf = 2447MHz
+ 0x13333100+(BY_AL7230_REG_LEN<<3)+IFREGCTL_REGW, // channel = 9, Tf = 2452MHz
+ 0x1B333100+(BY_AL7230_REG_LEN<<3)+IFREGCTL_REGW, // channel = 10, Tf = 2457MHz
+ 0x03333100+(BY_AL7230_REG_LEN<<3)+IFREGCTL_REGW, // channel = 11, Tf = 2462MHz
+ 0x0B333100+(BY_AL7230_REG_LEN<<3)+IFREGCTL_REGW, // channel = 12, Tf = 2467MHz
+ 0x13333100+(BY_AL7230_REG_LEN<<3)+IFREGCTL_REGW, // channel = 13, Tf = 2472MHz
+ 0x06666100+(BY_AL7230_REG_LEN<<3)+IFREGCTL_REGW, // channel = 14, Tf = 2484MHz
+
+ // 4.9G => Ch 183, 184, 185, 187, 188, 189, 192, 196 (Value:15 ~ 22)
+ 0x1D555100+(BY_AL7230_REG_LEN<<3)+IFREGCTL_REGW, // channel = 183, Tf = 4915MHz (15)
+ 0x00000100+(BY_AL7230_REG_LEN<<3)+IFREGCTL_REGW, // channel = 184, Tf = 4920MHz (16)
+ 0x02AAA100+(BY_AL7230_REG_LEN<<3)+IFREGCTL_REGW, // channel = 185, Tf = 4925MHz (17)
+ 0x08000100+(BY_AL7230_REG_LEN<<3)+IFREGCTL_REGW, // channel = 187, Tf = 4935MHz (18)
+ 0x0AAAA100+(BY_AL7230_REG_LEN<<3)+IFREGCTL_REGW, // channel = 188, Tf = 4940MHz (19)
+ 0x0D555100+(BY_AL7230_REG_LEN<<3)+IFREGCTL_REGW, // channel = 189, Tf = 4945MHz (20)
+ 0x15555100+(BY_AL7230_REG_LEN<<3)+IFREGCTL_REGW, // channel = 192, Tf = 4960MHz (21)
+ 0x00000100+(BY_AL7230_REG_LEN<<3)+IFREGCTL_REGW, // channel = 196, Tf = 4980MHz (22)
+
+ // 5G => Ch 7, 8, 9, 11, 12, 16, 34, 36, 38, 40, 42, 44, 46, 48, 52, 56, 60, 64,
+ // 100, 104, 108, 112, 116, 120, 124, 128, 132, 136, 140, 149, 153, 157, 161, 165 (Value 23 ~ 56)
+ 0x1D555100+(BY_AL7230_REG_LEN<<3)+IFREGCTL_REGW, // channel = 7, Tf = 5035MHz (23)
+ 0x00000100+(BY_AL7230_REG_LEN<<3)+IFREGCTL_REGW, // channel = 8, Tf = 5040MHz (24)
+ 0x02AAA100+(BY_AL7230_REG_LEN<<3)+IFREGCTL_REGW, // channel = 9, Tf = 5045MHz (25)
+ 0x08000100+(BY_AL7230_REG_LEN<<3)+IFREGCTL_REGW, // channel = 11, Tf = 5055MHz (26)
+ 0x0AAAA100+(BY_AL7230_REG_LEN<<3)+IFREGCTL_REGW, // channel = 12, Tf = 5060MHz (27)
+ 0x15555100+(BY_AL7230_REG_LEN<<3)+IFREGCTL_REGW, // channel = 16, Tf = 5080MHz (28)
+ 0x05555100+(BY_AL7230_REG_LEN<<3)+IFREGCTL_REGW, // channel = 34, Tf = 5170MHz (29)
+ 0x0AAAA100+(BY_AL7230_REG_LEN<<3)+IFREGCTL_REGW, // channel = 36, Tf = 5180MHz (30)
+ 0x10000100+(BY_AL7230_REG_LEN<<3)+IFREGCTL_REGW, // channel = 38, Tf = 5190MHz (31)
+ 0x15555100+(BY_AL7230_REG_LEN<<3)+IFREGCTL_REGW, // channel = 40, Tf = 5200MHz (32)
+ 0x1AAAA100+(BY_AL7230_REG_LEN<<3)+IFREGCTL_REGW, // channel = 42, Tf = 5210MHz (33)
+ 0x00000100+(BY_AL7230_REG_LEN<<3)+IFREGCTL_REGW, // channel = 44, Tf = 5220MHz (34)
+ 0x05555100+(BY_AL7230_REG_LEN<<3)+IFREGCTL_REGW, // channel = 46, Tf = 5230MHz (35)
+ 0x0AAAA100+(BY_AL7230_REG_LEN<<3)+IFREGCTL_REGW, // channel = 48, Tf = 5240MHz (36)
+ 0x15555100+(BY_AL7230_REG_LEN<<3)+IFREGCTL_REGW, // channel = 52, Tf = 5260MHz (37)
+ 0x00000100+(BY_AL7230_REG_LEN<<3)+IFREGCTL_REGW, // channel = 56, Tf = 5280MHz (38)
+ 0x0AAAA100+(BY_AL7230_REG_LEN<<3)+IFREGCTL_REGW, // channel = 60, Tf = 5300MHz (39)
+ 0x15555100+(BY_AL7230_REG_LEN<<3)+IFREGCTL_REGW, // channel = 64, Tf = 5320MHz (40)
+ 0x15555100+(BY_AL7230_REG_LEN<<3)+IFREGCTL_REGW, // channel = 100, Tf = 5500MHz (41)
+ 0x00000100+(BY_AL7230_REG_LEN<<3)+IFREGCTL_REGW, // channel = 104, Tf = 5520MHz (42)
+ 0x0AAAA100+(BY_AL7230_REG_LEN<<3)+IFREGCTL_REGW, // channel = 108, Tf = 5540MHz (43)
+ 0x15555100+(BY_AL7230_REG_LEN<<3)+IFREGCTL_REGW, // channel = 112, Tf = 5560MHz (44)
+ 0x00000100+(BY_AL7230_REG_LEN<<3)+IFREGCTL_REGW, // channel = 116, Tf = 5580MHz (45)
+ 0x0AAAA100+(BY_AL7230_REG_LEN<<3)+IFREGCTL_REGW, // channel = 120, Tf = 5600MHz (46)
+ 0x15555100+(BY_AL7230_REG_LEN<<3)+IFREGCTL_REGW, // channel = 124, Tf = 5620MHz (47)
+ 0x00000100+(BY_AL7230_REG_LEN<<3)+IFREGCTL_REGW, // channel = 128, Tf = 5640MHz (48)
+ 0x0AAAA100+(BY_AL7230_REG_LEN<<3)+IFREGCTL_REGW, // channel = 132, Tf = 5660MHz (49)
+ 0x15555100+(BY_AL7230_REG_LEN<<3)+IFREGCTL_REGW, // channel = 136, Tf = 5680MHz (50)
+ 0x00000100+(BY_AL7230_REG_LEN<<3)+IFREGCTL_REGW, // channel = 140, Tf = 5700MHz (51)
+ 0x18000100+(BY_AL7230_REG_LEN<<3)+IFREGCTL_REGW, // channel = 149, Tf = 5745MHz (52)
+ 0x02AAA100+(BY_AL7230_REG_LEN<<3)+IFREGCTL_REGW, // channel = 153, Tf = 5765MHz (53)
+ 0x0D555100+(BY_AL7230_REG_LEN<<3)+IFREGCTL_REGW, // channel = 157, Tf = 5785MHz (54)
+ 0x18000100+(BY_AL7230_REG_LEN<<3)+IFREGCTL_REGW, // channel = 161, Tf = 5805MHz (55)
+ 0x02AAA100+(BY_AL7230_REG_LEN<<3)+IFREGCTL_REGW // channel = 165, Tf = 5825MHz (56)
+};
+
+static const unsigned long dwAL7230ChannelTable2[CB_MAX_CHANNEL] = {
+ 0x7FD78400+(BY_AL7230_REG_LEN<<3)+IFREGCTL_REGW, // channel = 1, Tf = 2412MHz
+ 0x7FD78400+(BY_AL7230_REG_LEN<<3)+IFREGCTL_REGW, // channel = 2, Tf = 2417MHz
+ 0x7FD78400+(BY_AL7230_REG_LEN<<3)+IFREGCTL_REGW, // channel = 3, Tf = 2422MHz
+ 0x7FD78400+(BY_AL7230_REG_LEN<<3)+IFREGCTL_REGW, // channel = 4, Tf = 2427MHz
+ 0x7FD78400+(BY_AL7230_REG_LEN<<3)+IFREGCTL_REGW, // channel = 5, Tf = 2432MHz
+ 0x7FD78400+(BY_AL7230_REG_LEN<<3)+IFREGCTL_REGW, // channel = 6, Tf = 2437MHz
+ 0x7FD78400+(BY_AL7230_REG_LEN<<3)+IFREGCTL_REGW, // channel = 7, Tf = 2442MHz
+ 0x7FD78400+(BY_AL7230_REG_LEN<<3)+IFREGCTL_REGW, // channel = 8, Tf = 2447MHz
+ 0x7FD78400+(BY_AL7230_REG_LEN<<3)+IFREGCTL_REGW, // channel = 9, Tf = 2452MHz
+ 0x7FD78400+(BY_AL7230_REG_LEN<<3)+IFREGCTL_REGW, // channel = 10, Tf = 2457MHz
+ 0x7FD78400+(BY_AL7230_REG_LEN<<3)+IFREGCTL_REGW, // channel = 11, Tf = 2462MHz
+ 0x7FD78400+(BY_AL7230_REG_LEN<<3)+IFREGCTL_REGW, // channel = 12, Tf = 2467MHz
+ 0x7FD78400+(BY_AL7230_REG_LEN<<3)+IFREGCTL_REGW, // channel = 13, Tf = 2472MHz
+ 0x7FD78400+(BY_AL7230_REG_LEN<<3)+IFREGCTL_REGW, // channel = 14, Tf = 2484MHz
+
+ // 4.9G => Ch 183, 184, 185, 187, 188, 189, 192, 196 (Value:15 ~ 22)
+ 0x77D78400+(BY_AL7230_REG_LEN<<3)+IFREGCTL_REGW, // channel = 183, Tf = 4915MHz (15)
+ 0x67D78400+(BY_AL7230_REG_LEN<<3)+IFREGCTL_REGW, // channel = 184, Tf = 4920MHz (16)
+ 0x77D78400+(BY_AL7230_REG_LEN<<3)+IFREGCTL_REGW, // channel = 185, Tf = 4925MHz (17)
+ 0x77D78400+(BY_AL7230_REG_LEN<<3)+IFREGCTL_REGW, // channel = 187, Tf = 4935MHz (18)
+ 0x77D78400+(BY_AL7230_REG_LEN<<3)+IFREGCTL_REGW, // channel = 188, Tf = 4940MHz (19)
+ 0x77D78400+(BY_AL7230_REG_LEN<<3)+IFREGCTL_REGW, // channel = 189, Tf = 4945MHz (20)
+ 0x77D78400+(BY_AL7230_REG_LEN<<3)+IFREGCTL_REGW, // channel = 192, Tf = 4960MHz (21)
+ 0x67D78400+(BY_AL7230_REG_LEN<<3)+IFREGCTL_REGW, // channel = 196, Tf = 4980MHz (22)
+
+ // 5G => Ch 7, 8, 9, 11, 12, 16, 34, 36, 38, 40, 42, 44, 46, 48, 52, 56, 60, 64,
+ // 100, 104, 108, 112, 116, 120, 124, 128, 132, 136, 140, 149, 153, 157, 161, 165 (Value 23 ~ 56)
+ 0x77D78400+(BY_AL7230_REG_LEN<<3)+IFREGCTL_REGW, // channel = 7, Tf = 5035MHz (23)
+ 0x67D78400+(BY_AL7230_REG_LEN<<3)+IFREGCTL_REGW, // channel = 8, Tf = 5040MHz (24)
+ 0x77D78400+(BY_AL7230_REG_LEN<<3)+IFREGCTL_REGW, // channel = 9, Tf = 5045MHz (25)
+ 0x77D78400+(BY_AL7230_REG_LEN<<3)+IFREGCTL_REGW, // channel = 11, Tf = 5055MHz (26)
+ 0x77D78400+(BY_AL7230_REG_LEN<<3)+IFREGCTL_REGW, // channel = 12, Tf = 5060MHz (27)
+ 0x77D78400+(BY_AL7230_REG_LEN<<3)+IFREGCTL_REGW, // channel = 16, Tf = 5080MHz (28)
+ 0x77D78400+(BY_AL7230_REG_LEN<<3)+IFREGCTL_REGW, // channel = 34, Tf = 5170MHz (29)
+ 0x77D78400+(BY_AL7230_REG_LEN<<3)+IFREGCTL_REGW, // channel = 36, Tf = 5180MHz (30)
+ 0x77D78400+(BY_AL7230_REG_LEN<<3)+IFREGCTL_REGW, // channel = 38, Tf = 5190MHz (31)
+ 0x77D78400+(BY_AL7230_REG_LEN<<3)+IFREGCTL_REGW, // channel = 40, Tf = 5200MHz (32)
+ 0x77D78400+(BY_AL7230_REG_LEN<<3)+IFREGCTL_REGW, // channel = 42, Tf = 5210MHz (33)
+ 0x67D78400+(BY_AL7230_REG_LEN<<3)+IFREGCTL_REGW, // channel = 44, Tf = 5220MHz (34)
+ 0x77D78400+(BY_AL7230_REG_LEN<<3)+IFREGCTL_REGW, // channel = 46, Tf = 5230MHz (35)
+ 0x77D78400+(BY_AL7230_REG_LEN<<3)+IFREGCTL_REGW, // channel = 48, Tf = 5240MHz (36)
+ 0x77D78400+(BY_AL7230_REG_LEN<<3)+IFREGCTL_REGW, // channel = 52, Tf = 5260MHz (37)
+ 0x67D78400+(BY_AL7230_REG_LEN<<3)+IFREGCTL_REGW, // channel = 56, Tf = 5280MHz (38)
+ 0x77D78400+(BY_AL7230_REG_LEN<<3)+IFREGCTL_REGW, // channel = 60, Tf = 5300MHz (39)
+ 0x77D78400+(BY_AL7230_REG_LEN<<3)+IFREGCTL_REGW, // channel = 64, Tf = 5320MHz (40)
+ 0x77D78400+(BY_AL7230_REG_LEN<<3)+IFREGCTL_REGW, // channel = 100, Tf = 5500MHz (41)
+ 0x67D78400+(BY_AL7230_REG_LEN<<3)+IFREGCTL_REGW, // channel = 104, Tf = 5520MHz (42)
+ 0x77D78400+(BY_AL7230_REG_LEN<<3)+IFREGCTL_REGW, // channel = 108, Tf = 5540MHz (43)
+ 0x77D78400+(BY_AL7230_REG_LEN<<3)+IFREGCTL_REGW, // channel = 112, Tf = 5560MHz (44)
+ 0x67D78400+(BY_AL7230_REG_LEN<<3)+IFREGCTL_REGW, // channel = 116, Tf = 5580MHz (45)
+ 0x77D78400+(BY_AL7230_REG_LEN<<3)+IFREGCTL_REGW, // channel = 120, Tf = 5600MHz (46)
+ 0x77D78400+(BY_AL7230_REG_LEN<<3)+IFREGCTL_REGW, // channel = 124, Tf = 5620MHz (47)
+ 0x67D78400+(BY_AL7230_REG_LEN<<3)+IFREGCTL_REGW, // channel = 128, Tf = 5640MHz (48)
+ 0x77D78400+(BY_AL7230_REG_LEN<<3)+IFREGCTL_REGW, // channel = 132, Tf = 5660MHz (49)
+ 0x77D78400+(BY_AL7230_REG_LEN<<3)+IFREGCTL_REGW, // channel = 136, Tf = 5680MHz (50)
+ 0x67D78400+(BY_AL7230_REG_LEN<<3)+IFREGCTL_REGW, // channel = 140, Tf = 5700MHz (51)
+ 0x77D78400+(BY_AL7230_REG_LEN<<3)+IFREGCTL_REGW, // channel = 149, Tf = 5745MHz (52)
+ 0x77D78400+(BY_AL7230_REG_LEN<<3)+IFREGCTL_REGW, // channel = 153, Tf = 5765MHz (53)
+ 0x77D78400+(BY_AL7230_REG_LEN<<3)+IFREGCTL_REGW, // channel = 157, Tf = 5785MHz (54)
+ 0x77D78400+(BY_AL7230_REG_LEN<<3)+IFREGCTL_REGW, // channel = 161, Tf = 5805MHz (55)
+ 0x77D78400+(BY_AL7230_REG_LEN<<3)+IFREGCTL_REGW // channel = 165, Tf = 5825MHz (56)
+};
+
+/*
+ * Description: AIROHA IFRF chip init function
+ *
+ * Parameters:
+ * In:
+ * dwIoBase - I/O base address
+ * Out:
+ * none
+ *
+ * Return Value: true if succeeded; false if failed.
+ *
+ */
+static bool s_bAL7230Init(struct vnt_private *priv)
+{
+ void __iomem *dwIoBase = priv->PortOffset;
+ int ii;
+ bool bResult;
+
+ bResult = true;
+
+ /* 3-wire control for normal mode */
+ VNSvOutPortB(dwIoBase + MAC_REG_SOFTPWRCTL, 0);
+
+ MACvWordRegBitsOn(dwIoBase, MAC_REG_SOFTPWRCTL, (SOFTPWRCTL_SWPECTI |
+ SOFTPWRCTL_TXPEINV));
+ BBvPowerSaveModeOFF(priv); /* RobertYu:20050106, have DC value for Calibration */
+
+ for (ii = 0; ii < CB_AL7230_INIT_SEQ; ii++)
+ bResult &= IFRFbWriteEmbedded(priv, dwAL7230InitTable[ii]);
+
+ /* PLL On */
+ MACvWordRegBitsOn(dwIoBase, MAC_REG_SOFTPWRCTL, SOFTPWRCTL_SWPE3);
+
+ /* Calibration */
+ MACvTimer0MicroSDelay(dwIoBase, 150);//150us
+ /* TXDCOC:active, RCK:disable */
+ bResult &= IFRFbWriteEmbedded(priv, (0x9ABA8F00+(BY_AL7230_REG_LEN<<3)+IFREGCTL_REGW));
+ MACvTimer0MicroSDelay(dwIoBase, 30);//30us
+ /* TXDCOC:disable, RCK:active */
+ bResult &= IFRFbWriteEmbedded(priv, (0x3ABA8F00+(BY_AL7230_REG_LEN<<3)+IFREGCTL_REGW));
+ MACvTimer0MicroSDelay(dwIoBase, 30);//30us
+ /* TXDCOC:disable, RCK:disable */
+ bResult &= IFRFbWriteEmbedded(priv, dwAL7230InitTable[CB_AL7230_INIT_SEQ-1]);
+
+ MACvWordRegBitsOn(dwIoBase, MAC_REG_SOFTPWRCTL, (SOFTPWRCTL_SWPE3 |
+ SOFTPWRCTL_SWPE2 |
+ SOFTPWRCTL_SWPECTI |
+ SOFTPWRCTL_TXPEINV));
+
+ BBvPowerSaveModeON(priv); /* RobertYu:20050106 */
+
+ /* PE1: TX_ON, PE2: RX_ON, PE3: PLLON */
+ /* 3-wire control for power saving mode */
+ VNSvOutPortB(dwIoBase + MAC_REG_PSPWRSIG, (PSSIG_WPE3 | PSSIG_WPE2)); //1100 0000
+
+ return bResult;
+}
+
+/* Need to Pull PLLON low when writing channel registers through
+ * 3-wire interface */
+static bool s_bAL7230SelectChannel(struct vnt_private *priv, unsigned char byChannel)
+{
+ void __iomem *dwIoBase = priv->PortOffset;
+ bool bResult;
+
+ bResult = true;
+
+ /* PLLON Off */
+ MACvWordRegBitsOff(dwIoBase, MAC_REG_SOFTPWRCTL, SOFTPWRCTL_SWPE3);
+
+ bResult &= IFRFbWriteEmbedded(priv, dwAL7230ChannelTable0[byChannel - 1]);
+ bResult &= IFRFbWriteEmbedded(priv, dwAL7230ChannelTable1[byChannel - 1]);
+ bResult &= IFRFbWriteEmbedded(priv, dwAL7230ChannelTable2[byChannel - 1]);
+
+ /* PLLOn On */
+ MACvWordRegBitsOn(dwIoBase, MAC_REG_SOFTPWRCTL, SOFTPWRCTL_SWPE3);
+
+ /* Set Channel[7] = 0 to tell H/W channel is changing now. */
+ VNSvOutPortB(dwIoBase + MAC_REG_CHANNEL, (byChannel & 0x7F));
+ MACvTimer0MicroSDelay(dwIoBase, SWITCH_CHANNEL_DELAY_AL7230);
+ /* Set Channel[7] = 1 to tell H/W channel change is done. */
+ VNSvOutPortB(dwIoBase + MAC_REG_CHANNEL, (byChannel | 0x80));
+
+ return bResult;
+}
+
+/*
+ * Description: Write to IF/RF, by embedded programming
+ *
+ * Parameters:
+ * In:
+ * dwIoBase - I/O base address
+ * dwData - data to write
+ * Out:
+ * none
+ *
+ * Return Value: true if succeeded; false if failed.
+ *
+ */
+bool IFRFbWriteEmbedded(struct vnt_private *priv, unsigned long dwData)
+{
+ void __iomem *dwIoBase = priv->PortOffset;
+ unsigned short ww;
+ unsigned long dwValue;
+
+ VNSvOutPortD(dwIoBase + MAC_REG_IFREGCTL, dwData);
+
+ /* W_MAX_TIMEOUT is the timeout period */
+ for (ww = 0; ww < W_MAX_TIMEOUT; ww++) {
+ VNSvInPortD(dwIoBase + MAC_REG_IFREGCTL, &dwValue);
+ if (dwValue & IFREGCTL_DONE)
+ break;
+ }
+
+ if (ww == W_MAX_TIMEOUT)
+ return false;
+
+ return true;
+}
+
+/*
+ * Description: AIROHA IFRF chip init function
+ *
+ * Parameters:
+ * In:
+ * dwIoBase - I/O base address
+ * Out:
+ * none
+ *
+ * Return Value: true if succeeded; false if failed.
+ *
+ */
+static bool RFbAL2230Init(struct vnt_private *priv)
+{
+ void __iomem *dwIoBase = priv->PortOffset;
+ int ii;
+ bool bResult;
+
+ bResult = true;
+
+ /* 3-wire control for normal mode */
+ VNSvOutPortB(dwIoBase + MAC_REG_SOFTPWRCTL, 0);
+
+ MACvWordRegBitsOn(dwIoBase, MAC_REG_SOFTPWRCTL, (SOFTPWRCTL_SWPECTI |
+ SOFTPWRCTL_TXPEINV));
+ /* PLL Off */
+ MACvWordRegBitsOff(dwIoBase, MAC_REG_SOFTPWRCTL, SOFTPWRCTL_SWPE3);
+
+ /* patch abnormal AL2230 frequency output */
+ IFRFbWriteEmbedded(priv, (0x07168700+(BY_AL2230_REG_LEN<<3)+IFREGCTL_REGW));
+
+ for (ii = 0; ii < CB_AL2230_INIT_SEQ; ii++)
+ bResult &= IFRFbWriteEmbedded(priv, dwAL2230InitTable[ii]);
+ MACvTimer0MicroSDelay(dwIoBase, 30); //delay 30 us
+
+ /* PLL On */
+ MACvWordRegBitsOn(dwIoBase, MAC_REG_SOFTPWRCTL, SOFTPWRCTL_SWPE3);
+
+ MACvTimer0MicroSDelay(dwIoBase, 150);//150us
+ bResult &= IFRFbWriteEmbedded(priv, (0x00d80f00+(BY_AL2230_REG_LEN<<3)+IFREGCTL_REGW));
+ MACvTimer0MicroSDelay(dwIoBase, 30);//30us
+ bResult &= IFRFbWriteEmbedded(priv, (0x00780f00+(BY_AL2230_REG_LEN<<3)+IFREGCTL_REGW));
+ MACvTimer0MicroSDelay(dwIoBase, 30);//30us
+ bResult &= IFRFbWriteEmbedded(priv, dwAL2230InitTable[CB_AL2230_INIT_SEQ-1]);
+
+ MACvWordRegBitsOn(dwIoBase, MAC_REG_SOFTPWRCTL, (SOFTPWRCTL_SWPE3 |
+ SOFTPWRCTL_SWPE2 |
+ SOFTPWRCTL_SWPECTI |
+ SOFTPWRCTL_TXPEINV));
+
+ /* 3-wire control for power saving mode */
+ VNSvOutPortB(dwIoBase + MAC_REG_PSPWRSIG, (PSSIG_WPE3 | PSSIG_WPE2)); //1100 0000
+
+ return bResult;
+}
+
+static bool RFbAL2230SelectChannel(struct vnt_private *priv, unsigned char byChannel)
+{
+ void __iomem *dwIoBase = priv->PortOffset;
+ bool bResult;
+
+ bResult = true;
+
+ bResult &= IFRFbWriteEmbedded(priv, dwAL2230ChannelTable0[byChannel - 1]);
+ bResult &= IFRFbWriteEmbedded(priv, dwAL2230ChannelTable1[byChannel - 1]);
+
+ /* Set Channel[7] = 0 to tell H/W channel is changing now. */
+ VNSvOutPortB(dwIoBase + MAC_REG_CHANNEL, (byChannel & 0x7F));
+ MACvTimer0MicroSDelay(dwIoBase, SWITCH_CHANNEL_DELAY_AL2230);
+ /* Set Channel[7] = 1 to tell H/W channel change is done. */
+ VNSvOutPortB(dwIoBase + MAC_REG_CHANNEL, (byChannel | 0x80));
+
+ return bResult;
+}
+
+/*
+ * Description: RF init function
+ *
+ * Parameters:
+ * In:
+ * byBBType
+ * byRFType
+ * Out:
+ * none
+ *
+ * Return Value: true if succeeded; false if failed.
+ *
+ */
+bool RFbInit(
+ struct vnt_private *priv
+)
+{
+ bool bResult = true;
+
+ switch (priv->byRFType) {
+ case RF_AIROHA:
+ case RF_AL2230S:
+ priv->byMaxPwrLevel = AL2230_PWR_IDX_LEN;
+ bResult = RFbAL2230Init(priv);
+ break;
+ case RF_AIROHA7230:
+ priv->byMaxPwrLevel = AL7230_PWR_IDX_LEN;
+ bResult = s_bAL7230Init(priv);
+ break;
+ case RF_NOTHING:
+ bResult = true;
+ break;
+ default:
+ bResult = false;
+ break;
+ }
+ return bResult;
+}
+
+/*
+ * Description: Select channel
+ *
+ * Parameters:
+ * In:
+ * byRFType
+ * byChannel - Channel number
+ * Out:
+ * none
+ *
+ * Return Value: true if succeeded; false if failed.
+ *
+ */
+bool RFbSelectChannel(struct vnt_private *priv, unsigned char byRFType,
+ u16 byChannel)
+{
+ bool bResult = true;
+
+ switch (byRFType) {
+ case RF_AIROHA:
+ case RF_AL2230S:
+ bResult = RFbAL2230SelectChannel(priv, byChannel);
+ break;
+ //{{ RobertYu: 20050104
+ case RF_AIROHA7230:
+ bResult = s_bAL7230SelectChannel(priv, byChannel);
+ break;
+ //}} RobertYu
+ case RF_NOTHING:
+ bResult = true;
+ break;
+ default:
+ bResult = false;
+ break;
+ }
+ return bResult;
+}
+
+/*
+ * Description: Write WakeProgSyn
+ *
+ * Parameters:
+ * In:
+ * dwIoBase - I/O base address
+ * uChannel - channel number
+ * bySleepCnt - SleepProgSyn count
+ *
+ * Return Value: None.
+ *
+ */
+bool RFvWriteWakeProgSyn(struct vnt_private *priv, unsigned char byRFType,
+ u16 uChannel)
+{
+ void __iomem *dwIoBase = priv->PortOffset;
+ int ii;
+ unsigned char byInitCount = 0;
+ unsigned char bySleepCount = 0;
+
+ VNSvOutPortW(dwIoBase + MAC_REG_MISCFFNDEX, 0);
+ switch (byRFType) {
+ case RF_AIROHA:
+ case RF_AL2230S:
+
+ if (uChannel > CB_MAX_CHANNEL_24G)
+ return false;
+
+ /* Init Reg + Channel Reg (2) */
+ byInitCount = CB_AL2230_INIT_SEQ + 2;
+ bySleepCount = 0;
+ if (byInitCount > (MISCFIFO_SYNDATASIZE - bySleepCount))
+ return false;
+
+ for (ii = 0; ii < CB_AL2230_INIT_SEQ; ii++)
+ MACvSetMISCFifo(dwIoBase, (unsigned short)(MISCFIFO_SYNDATA_IDX + ii), dwAL2230InitTable[ii]);
+
+ MACvSetMISCFifo(dwIoBase, (unsigned short)(MISCFIFO_SYNDATA_IDX + ii), dwAL2230ChannelTable0[uChannel-1]);
+ ii++;
+ MACvSetMISCFifo(dwIoBase, (unsigned short)(MISCFIFO_SYNDATA_IDX + ii), dwAL2230ChannelTable1[uChannel-1]);
+ break;
+
+ /* Need to check, PLLON need to be low for channel setting */
+ case RF_AIROHA7230:
+ /* Init Reg + Channel Reg (3) */
+ byInitCount = CB_AL7230_INIT_SEQ + 3;
+ bySleepCount = 0;
+ if (byInitCount > (MISCFIFO_SYNDATASIZE - bySleepCount))
+ return false;
+
+ if (uChannel <= CB_MAX_CHANNEL_24G) {
+ for (ii = 0; ii < CB_AL7230_INIT_SEQ; ii++)
+ MACvSetMISCFifo(dwIoBase, (unsigned short)(MISCFIFO_SYNDATA_IDX + ii), dwAL7230InitTable[ii]);
+ } else {
+ for (ii = 0; ii < CB_AL7230_INIT_SEQ; ii++)
+ MACvSetMISCFifo(dwIoBase, (unsigned short)(MISCFIFO_SYNDATA_IDX + ii), dwAL7230InitTableAMode[ii]);
+ }
+
+ MACvSetMISCFifo(dwIoBase, (unsigned short)(MISCFIFO_SYNDATA_IDX + ii), dwAL7230ChannelTable0[uChannel-1]);
+ ii++;
+ MACvSetMISCFifo(dwIoBase, (unsigned short)(MISCFIFO_SYNDATA_IDX + ii), dwAL7230ChannelTable1[uChannel-1]);
+ ii++;
+ MACvSetMISCFifo(dwIoBase, (unsigned short)(MISCFIFO_SYNDATA_IDX + ii), dwAL7230ChannelTable2[uChannel-1]);
+ break;
+
+ case RF_NOTHING:
+ return true;
+
+ default:
+ return false;
+ }
+
+ MACvSetMISCFifo(dwIoBase, MISCFIFO_SYNINFO_IDX, (unsigned long)MAKEWORD(bySleepCount, byInitCount));
+
+ return true;
+}
+
+/*
+ * Description: Set Tx power
+ *
+ * Parameters:
+ * In:
+ * dwIoBase - I/O base address
+ * dwRFPowerTable - RF Tx Power Setting
+ * Out:
+ * none
+ *
+ * Return Value: true if succeeded; false if failed.
+ *
+ */
+bool RFbSetPower(
+ struct vnt_private *priv,
+ unsigned int uRATE,
+ u16 uCH
+)
+{
+ bool bResult = true;
+ unsigned char byPwr = 0;
+ unsigned char byDec = 0;
+
+ if (priv->dwDiagRefCount != 0)
+ return true;
+
+ if ((uCH < 1) || (uCH > CB_MAX_CHANNEL))
+ return false;
+
+ switch (uRATE) {
+ case RATE_1M:
+ case RATE_2M:
+ case RATE_5M:
+ case RATE_11M:
+ if (uCH > CB_MAX_CHANNEL_24G)
+ return false;
+
+ byPwr = priv->abyCCKPwrTbl[uCH];
+ break;
+ case RATE_6M:
+ case RATE_9M:
+ case RATE_12M:
+ case RATE_18M:
+ byPwr = priv->abyOFDMPwrTbl[uCH];
+ if (priv->byRFType == RF_UW2452)
+ byDec = byPwr + 14;
+ else
+ byDec = byPwr + 10;
+
+ if (byDec >= priv->byMaxPwrLevel)
+ byDec = priv->byMaxPwrLevel-1;
+
+ byPwr = byDec;
+ break;
+ case RATE_24M:
+ case RATE_36M:
+ case RATE_48M:
+ case RATE_54M:
+ byPwr = priv->abyOFDMPwrTbl[uCH];
+ break;
+ }
+
+ if (priv->byCurPwr == byPwr)
+ return true;
+
+ bResult = RFbRawSetPower(priv, byPwr, uRATE);
+ if (bResult)
+ priv->byCurPwr = byPwr;
+
+ return bResult;
+}
+
+/*
+ * Description: Set Tx power
+ *
+ * Parameters:
+ * In:
+ * dwIoBase - I/O base address
+ * dwRFPowerTable - RF Tx Power Setting
+ * Out:
+ * none
+ *
+ * Return Value: true if succeeded; false if failed.
+ *
+ */
+
+bool RFbRawSetPower(
+ struct vnt_private *priv,
+ unsigned char byPwr,
+ unsigned int uRATE
+)
+{
+ bool bResult = true;
+ unsigned long dwMax7230Pwr = 0;
+
+ if (byPwr >= priv->byMaxPwrLevel)
+ return false;
+
+ switch (priv->byRFType) {
+ case RF_AIROHA:
+ bResult &= IFRFbWriteEmbedded(priv, dwAL2230PowerTable[byPwr]);
+ if (uRATE <= RATE_11M)
+ bResult &= IFRFbWriteEmbedded(priv, 0x0001B400+(BY_AL2230_REG_LEN<<3)+IFREGCTL_REGW);
+ else
+ bResult &= IFRFbWriteEmbedded(priv, 0x0005A400+(BY_AL2230_REG_LEN<<3)+IFREGCTL_REGW);
+
+ break;
+
+ case RF_AL2230S:
+ bResult &= IFRFbWriteEmbedded(priv, dwAL2230PowerTable[byPwr]);
+ if (uRATE <= RATE_11M) {
+ bResult &= IFRFbWriteEmbedded(priv, 0x040C1400+(BY_AL2230_REG_LEN<<3)+IFREGCTL_REGW);
+ bResult &= IFRFbWriteEmbedded(priv, 0x00299B00+(BY_AL2230_REG_LEN<<3)+IFREGCTL_REGW);
+ } else {
+ bResult &= IFRFbWriteEmbedded(priv, 0x0005A400+(BY_AL2230_REG_LEN<<3)+IFREGCTL_REGW);
+ bResult &= IFRFbWriteEmbedded(priv, 0x00099B00+(BY_AL2230_REG_LEN<<3)+IFREGCTL_REGW);
+ }
+
+ break;
+
+ case RF_AIROHA7230:
+ /* 0x080F1B00 for 3 wire control TxGain(D10)
+ * and 0x31 as TX Gain value */
+ dwMax7230Pwr = 0x080C0B00 | ((byPwr) << 12) |
+ (BY_AL7230_REG_LEN << 3) | IFREGCTL_REGW;
+
+ bResult &= IFRFbWriteEmbedded(priv, dwMax7230Pwr);
+ break;
+
+ default:
+ break;
+ }
+ return bResult;
+}
+
+/*+
+ *
+ * Routine Description:
+ * Translate RSSI to dBm
+ *
+ * Parameters:
+ * In:
+ * priv - The adapter to be translated
+ * byCurrRSSI - RSSI to be translated
+ * Out:
+ * pdwdbm - Translated dbm number
+ *
+ * Return Value: none
+ *
+ -*/
+void
+RFvRSSITodBm(
+ struct vnt_private *priv,
+ unsigned char byCurrRSSI,
+ long *pldBm
+ )
+{
+ unsigned char byIdx = (((byCurrRSSI & 0xC0) >> 6) & 0x03);
+ long b = (byCurrRSSI & 0x3F);
+ long a = 0;
+ unsigned char abyAIROHARF[4] = {0, 18, 0, 40};
+
+ switch (priv->byRFType) {
+ case RF_AIROHA:
+ case RF_AL2230S:
+ case RF_AIROHA7230:
+ a = abyAIROHARF[byIdx];
+ break;
+ default:
+ break;
+ }
+
+ *pldBm = -1 * (a + b * 2);
+}
+
+/* Post processing for the 11b/g and 11a.
+ * for save time on changing Reg2,3,5,7,10,12,15 */
+bool RFbAL7230SelectChannelPostProcess(struct vnt_private *priv,
+ u16 byOldChannel,
+ u16 byNewChannel)
+{
+ bool bResult;
+
+ bResult = true;
+
+ /* if change between 11 b/g and 11a need to update the following
+ * register
+ * Channel Index 1~14 */
+ if ((byOldChannel <= CB_MAX_CHANNEL_24G) && (byNewChannel > CB_MAX_CHANNEL_24G)) {
+ /* Change from 2.4G to 5G [Reg] */
+ bResult &= IFRFbWriteEmbedded(priv, dwAL7230InitTableAMode[2]);
+ bResult &= IFRFbWriteEmbedded(priv, dwAL7230InitTableAMode[3]);
+ bResult &= IFRFbWriteEmbedded(priv, dwAL7230InitTableAMode[5]);
+ bResult &= IFRFbWriteEmbedded(priv, dwAL7230InitTableAMode[7]);
+ bResult &= IFRFbWriteEmbedded(priv, dwAL7230InitTableAMode[10]);
+ bResult &= IFRFbWriteEmbedded(priv, dwAL7230InitTableAMode[12]);
+ bResult &= IFRFbWriteEmbedded(priv, dwAL7230InitTableAMode[15]);
+ } else if ((byOldChannel > CB_MAX_CHANNEL_24G) && (byNewChannel <= CB_MAX_CHANNEL_24G)) {
+ /* Change from 5G to 2.4G [Reg] */
+ bResult &= IFRFbWriteEmbedded(priv, dwAL7230InitTable[2]);
+ bResult &= IFRFbWriteEmbedded(priv, dwAL7230InitTable[3]);
+ bResult &= IFRFbWriteEmbedded(priv, dwAL7230InitTable[5]);
+ bResult &= IFRFbWriteEmbedded(priv, dwAL7230InitTable[7]);
+ bResult &= IFRFbWriteEmbedded(priv, dwAL7230InitTable[10]);
+ bResult &= IFRFbWriteEmbedded(priv, dwAL7230InitTable[12]);
+ bResult &= IFRFbWriteEmbedded(priv, dwAL7230InitTable[15]);
+ }
+
+ return bResult;
+}
diff --git a/drivers/staging/vt6655/rf.h b/drivers/staging/vt6655/rf.h
new file mode 100644
index 000000000..2ea21e2b0
--- /dev/null
+++ b/drivers/staging/vt6655/rf.h
@@ -0,0 +1,100 @@
+/*
+ * Copyright (c) 1996, 2003 VIA Networking Technologies, Inc.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ *
+ * File: rf.h
+ *
+ * Purpose:
+ *
+ * Author: Jerry Chen
+ *
+ * Date: Feb. 19, 2004
+ *
+ */
+
+#ifndef __RF_H__
+#define __RF_H__
+
+#include "device.h"
+
+/*--------------------- Export Definitions -------------------------*/
+//
+// Baseband RF pair definition in eeprom (Bits 6..0)
+//
+#define RF_RFMD2959 0x01
+#define RF_MAXIMAG 0x02
+#define RF_AIROHA 0x03
+
+#define RF_UW2451 0x05
+#define RF_MAXIMG 0x06
+#define RF_MAXIM2829 0x07 // RobertYu: 20041118
+#define RF_UW2452 0x08 // RobertYu: 20041210
+#define RF_AIROHA7230 0x0a // RobertYu: 20050104
+#define RF_UW2453 0x0b
+
+#define RF_VT3226 0x09
+#define RF_AL2230S 0x0e
+
+#define RF_NOTHING 0x7E
+#define RF_EMU 0x80
+#define RF_MASK 0x7F
+
+#define ZONE_FCC 0
+#define ZONE_MKK1 1
+#define ZONE_ETSI 2
+#define ZONE_IC 3
+#define ZONE_SPAIN 4
+#define ZONE_FRANCE 5
+#define ZONE_MKK 6
+#define ZONE_ISRAEL 7
+
+//[20050104] CB_MAXIM2829_CHANNEL_5G_HIGH, CB_UW2452_CHANNEL_5G_HIGH: 40==>41
+#define CB_MAXIM2829_CHANNEL_5G_HIGH 41 //Index41: channel = 100, Tf = 5500MHz, set the (A3:A0=0101) D6=1
+#define CB_UW2452_CHANNEL_5G_HIGH 41 //[20041210] Index41: channel = 100, Tf = 5500MHz, change VCO2->VCO3
+
+/*--------------------- Export Classes ----------------------------*/
+
+/*--------------------- Export Variables --------------------------*/
+
+/*--------------------- Export Functions --------------------------*/
+
+bool IFRFbWriteEmbedded(struct vnt_private *, unsigned long dwData);
+bool RFbSelectChannel(struct vnt_private *, unsigned char byRFType, u16);
+bool RFbInit(
+ struct vnt_private *
+);
+bool RFvWriteWakeProgSyn(struct vnt_private *, unsigned char byRFType, u16);
+bool RFbSetPower(struct vnt_private *, unsigned int uRATE, u16);
+bool RFbRawSetPower(
+ struct vnt_private *,
+ unsigned char byPwr,
+ unsigned int uRATE
+);
+
+void
+RFvRSSITodBm(
+ struct vnt_private *,
+ unsigned char byCurrRSSI,
+ long *pldBm
+);
+
+//{{ RobertYu: 20050104
+bool RFbAL7230SelectChannelPostProcess(struct vnt_private *, u16, u16);
+//}} RobertYu
+
+#endif // __RF_H__
diff --git a/drivers/staging/vt6655/rxtx.c b/drivers/staging/vt6655/rxtx.c
new file mode 100644
index 000000000..74687761b
--- /dev/null
+++ b/drivers/staging/vt6655/rxtx.c
@@ -0,0 +1,1522 @@
+/*
+ * Copyright (c) 1996, 2003 VIA Networking Technologies, Inc.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * File: rxtx.c
+ *
+ * Purpose: handle WMAC/802.3/802.11 rx & tx functions
+ *
+ * Author: Lyndon Chen
+ *
+ * Date: May 20, 2003
+ *
+ * Functions:
+ * s_vGenerateTxParameter - Generate tx dma required parameter.
+ * vGenerateMACHeader - Translate 802.3 to 802.11 header
+ * cbGetFragCount - Calculate fragment number count
+ * csBeacon_xmit - beacon tx function
+ * csMgmt_xmit - management tx function
+ * s_cbFillTxBufHead - fulfill tx dma buffer header
+ * s_uGetDataDuration - get tx data required duration
+ * s_uFillDataHead- fulfill tx data duration header
+ * s_uGetRTSCTSDuration- get rtx/cts required duration
+ * s_uGetRTSCTSRsvTime- get rts/cts reserved time
+ * s_uGetTxRsvTime- get frame reserved time
+ * s_vFillCTSHead- fulfill CTS ctl header
+ * s_vFillFragParameter- Set fragment ctl parameter.
+ * s_vFillRTSHead- fulfill RTS ctl header
+ * s_vFillTxKey- fulfill tx encrypt key
+ * s_vSWencryption- Software encrypt header
+ * vDMA0_tx_80211- tx 802.11 frame via dma0
+ * vGenerateFIFOHeader- Generate tx FIFO ctl header
+ *
+ * Revision History:
+ *
+ */
+
+#include "device.h"
+#include "rxtx.h"
+#include "card.h"
+#include "mac.h"
+#include "baseband.h"
+#include "rf.h"
+
+/*--------------------- Static Definitions -------------------------*/
+
+/*--------------------- Static Classes ----------------------------*/
+
+/*--------------------- Static Variables --------------------------*/
+
+/*--------------------- Static Functions --------------------------*/
+
+/*--------------------- Static Definitions -------------------------*/
+#define CRITICAL_PACKET_LEN 256 /* if packet size < 256 -> in-direct send
+ packet size >= 256 -> direct send */
+
+static const unsigned short wTimeStampOff[2][MAX_RATE] = {
+ {384, 288, 226, 209, 54, 43, 37, 31, 28, 25, 24, 23}, /* Long Preamble */
+ {384, 192, 130, 113, 54, 43, 37, 31, 28, 25, 24, 23}, /* Short Preamble */
+};
+
+static const unsigned short wFB_Opt0[2][5] = {
+ {RATE_12M, RATE_18M, RATE_24M, RATE_36M, RATE_48M}, /* fallback_rate0 */
+ {RATE_12M, RATE_12M, RATE_18M, RATE_24M, RATE_36M}, /* fallback_rate1 */
+};
+static const unsigned short wFB_Opt1[2][5] = {
+ {RATE_12M, RATE_18M, RATE_24M, RATE_24M, RATE_36M}, /* fallback_rate0 */
+ {RATE_6M , RATE_6M, RATE_12M, RATE_12M, RATE_18M}, /* fallback_rate1 */
+};
+
+#define RTSDUR_BB 0
+#define RTSDUR_BA 1
+#define RTSDUR_AA 2
+#define CTSDUR_BA 3
+#define RTSDUR_BA_F0 4
+#define RTSDUR_AA_F0 5
+#define RTSDUR_BA_F1 6
+#define RTSDUR_AA_F1 7
+#define CTSDUR_BA_F0 8
+#define CTSDUR_BA_F1 9
+#define DATADUR_B 10
+#define DATADUR_A 11
+#define DATADUR_A_F0 12
+#define DATADUR_A_F1 13
+
+/*--------------------- Static Functions --------------------------*/
+static
+void
+s_vFillRTSHead(
+ struct vnt_private *pDevice,
+ unsigned char byPktType,
+ void *pvRTS,
+ unsigned int cbFrameLength,
+ bool bNeedAck,
+ bool bDisCRC,
+ struct ieee80211_hdr *hdr,
+ unsigned short wCurrentRate,
+ unsigned char byFBOption
+);
+
+static
+void
+s_vGenerateTxParameter(
+ struct vnt_private *pDevice,
+ unsigned char byPktType,
+ struct vnt_tx_fifo_head *,
+ void *pvRrvTime,
+ void *pvRTS,
+ void *pvCTS,
+ unsigned int cbFrameSize,
+ bool bNeedACK,
+ unsigned int uDMAIdx,
+ void *psEthHeader,
+ unsigned short wCurrentRate
+);
+
+static unsigned int
+s_cbFillTxBufHead(struct vnt_private *pDevice, unsigned char byPktType,
+ unsigned char *pbyTxBufferAddr,
+ unsigned int uDMAIdx, PSTxDesc pHeadTD,
+ unsigned int uNodeIndex);
+
+static
+__le16
+s_uFillDataHead(
+ struct vnt_private *pDevice,
+ unsigned char byPktType,
+ void *pTxDataHead,
+ unsigned int cbFrameLength,
+ unsigned int uDMAIdx,
+ bool bNeedAck,
+ unsigned int uFragIdx,
+ unsigned int cbLastFragmentSize,
+ unsigned int uMACfragNum,
+ unsigned char byFBOption,
+ unsigned short wCurrentRate,
+ bool is_pspoll
+);
+
+/*--------------------- Export Variables --------------------------*/
+
+static __le16 vnt_time_stamp_off(struct vnt_private *priv, u16 rate)
+{
+ return cpu_to_le16(wTimeStampOff[priv->byPreambleType % 2]
+ [rate % MAX_RATE]);
+}
+
+/*byPktType : PK_TYPE_11A 0
+ PK_TYPE_11B 1
+ PK_TYPE_11GB 2
+ PK_TYPE_11GA 3
+*/
+static
+unsigned int
+s_uGetTxRsvTime(
+ struct vnt_private *pDevice,
+ unsigned char byPktType,
+ unsigned int cbFrameLength,
+ unsigned short wRate,
+ bool bNeedAck
+)
+{
+ unsigned int uDataTime, uAckTime;
+
+ uDataTime = BBuGetFrameTime(pDevice->byPreambleType, byPktType, cbFrameLength, wRate);
+ if (byPktType == PK_TYPE_11B) /* llb,CCK mode */
+ uAckTime = BBuGetFrameTime(pDevice->byPreambleType, byPktType, 14, (unsigned short)pDevice->byTopCCKBasicRate);
+ else /* 11g 2.4G OFDM mode & 11a 5G OFDM mode */
+ uAckTime = BBuGetFrameTime(pDevice->byPreambleType, byPktType, 14, (unsigned short)pDevice->byTopOFDMBasicRate);
+
+ if (bNeedAck)
+ return uDataTime + pDevice->uSIFS + uAckTime;
+ else
+ return uDataTime;
+}
+
+static __le16 vnt_rxtx_rsvtime_le16(struct vnt_private *priv, u8 pkt_type,
+ u32 frame_length, u16 rate, bool need_ack)
+{
+ return cpu_to_le16((u16)s_uGetTxRsvTime(priv, pkt_type,
+ frame_length, rate, need_ack));
+}
+
+/* byFreqType: 0=>5GHZ 1=>2.4GHZ */
+static
+__le16
+s_uGetRTSCTSRsvTime(
+ struct vnt_private *pDevice,
+ unsigned char byRTSRsvType,
+ unsigned char byPktType,
+ unsigned int cbFrameLength,
+ unsigned short wCurrentRate
+)
+{
+ unsigned int uRrvTime, uRTSTime, uCTSTime, uAckTime, uDataTime;
+
+ uRrvTime = uRTSTime = uCTSTime = uAckTime = uDataTime = 0;
+
+ uDataTime = BBuGetFrameTime(pDevice->byPreambleType, byPktType, cbFrameLength, wCurrentRate);
+ if (byRTSRsvType == 0) { /* RTSTxRrvTime_bb */
+ uRTSTime = BBuGetFrameTime(pDevice->byPreambleType, byPktType, 20, pDevice->byTopCCKBasicRate);
+ uCTSTime = uAckTime = BBuGetFrameTime(pDevice->byPreambleType, byPktType, 14, pDevice->byTopCCKBasicRate);
+ } else if (byRTSRsvType == 1) { /* RTSTxRrvTime_ba, only in 2.4GHZ */
+ uRTSTime = BBuGetFrameTime(pDevice->byPreambleType, byPktType, 20, pDevice->byTopCCKBasicRate);
+ uCTSTime = BBuGetFrameTime(pDevice->byPreambleType, byPktType, 14, pDevice->byTopCCKBasicRate);
+ uAckTime = BBuGetFrameTime(pDevice->byPreambleType, byPktType, 14, pDevice->byTopOFDMBasicRate);
+ } else if (byRTSRsvType == 2) { /* RTSTxRrvTime_aa */
+ uRTSTime = BBuGetFrameTime(pDevice->byPreambleType, byPktType, 20, pDevice->byTopOFDMBasicRate);
+ uCTSTime = uAckTime = BBuGetFrameTime(pDevice->byPreambleType, byPktType, 14, pDevice->byTopOFDMBasicRate);
+ } else if (byRTSRsvType == 3) { /* CTSTxRrvTime_ba, only in 2.4GHZ */
+ uCTSTime = BBuGetFrameTime(pDevice->byPreambleType, byPktType, 14, pDevice->byTopCCKBasicRate);
+ uAckTime = BBuGetFrameTime(pDevice->byPreambleType, byPktType, 14, pDevice->byTopOFDMBasicRate);
+ uRrvTime = uCTSTime + uAckTime + uDataTime + 2*pDevice->uSIFS;
+ return cpu_to_le16((u16)uRrvTime);
+ }
+
+ /* RTSRrvTime */
+ uRrvTime = uRTSTime + uCTSTime + uAckTime + uDataTime + 3*pDevice->uSIFS;
+ return cpu_to_le16((u16)uRrvTime);
+}
+
+/* byFreqType 0: 5GHz, 1:2.4Ghz */
+static
+unsigned int
+s_uGetDataDuration(
+ struct vnt_private *pDevice,
+ unsigned char byDurType,
+ unsigned int cbFrameLength,
+ unsigned char byPktType,
+ unsigned short wRate,
+ bool bNeedAck,
+ unsigned int uFragIdx,
+ unsigned int cbLastFragmentSize,
+ unsigned int uMACfragNum,
+ unsigned char byFBOption
+)
+{
+ bool bLastFrag = false;
+ unsigned int uAckTime = 0, uNextPktTime = 0;
+
+ if (uFragIdx == (uMACfragNum-1))
+ bLastFrag = true;
+
+ switch (byDurType) {
+ case DATADUR_B: /* DATADUR_B */
+ if (((uMACfragNum == 1)) || bLastFrag) {/* Non Frag or Last Frag */
+ if (bNeedAck) {
+ uAckTime = BBuGetFrameTime(pDevice->byPreambleType, byPktType, 14, pDevice->byTopCCKBasicRate);
+ return pDevice->uSIFS + uAckTime;
+ } else {
+ return 0;
+ }
+ } else {/* First Frag or Mid Frag */
+ if (uFragIdx == (uMACfragNum-2))
+ uNextPktTime = s_uGetTxRsvTime(pDevice, byPktType, cbLastFragmentSize, wRate, bNeedAck);
+ else
+ uNextPktTime = s_uGetTxRsvTime(pDevice, byPktType, cbFrameLength, wRate, bNeedAck);
+
+ if (bNeedAck) {
+ uAckTime = BBuGetFrameTime(pDevice->byPreambleType, byPktType, 14, pDevice->byTopCCKBasicRate);
+ return pDevice->uSIFS + uAckTime + uNextPktTime;
+ } else {
+ return pDevice->uSIFS + uNextPktTime;
+ }
+ }
+ break;
+
+ case DATADUR_A: /* DATADUR_A */
+ if (((uMACfragNum == 1)) || bLastFrag) {/* Non Frag or Last Frag */
+ if (bNeedAck) {
+ uAckTime = BBuGetFrameTime(pDevice->byPreambleType, byPktType, 14, pDevice->byTopOFDMBasicRate);
+ return pDevice->uSIFS + uAckTime;
+ } else {
+ return 0;
+ }
+ } else {/* First Frag or Mid Frag */
+ if (uFragIdx == (uMACfragNum-2))
+ uNextPktTime = s_uGetTxRsvTime(pDevice, byPktType, cbLastFragmentSize, wRate, bNeedAck);
+ else
+ uNextPktTime = s_uGetTxRsvTime(pDevice, byPktType, cbFrameLength, wRate, bNeedAck);
+
+ if (bNeedAck) {
+ uAckTime = BBuGetFrameTime(pDevice->byPreambleType, byPktType, 14, pDevice->byTopOFDMBasicRate);
+ return pDevice->uSIFS + uAckTime + uNextPktTime;
+ } else {
+ return pDevice->uSIFS + uNextPktTime;
+ }
+ }
+ break;
+
+ case DATADUR_A_F0: /* DATADUR_A_F0 */
+ if (((uMACfragNum == 1)) || bLastFrag) {/* Non Frag or Last Frag */
+ if (bNeedAck) {
+ uAckTime = BBuGetFrameTime(pDevice->byPreambleType, byPktType, 14, pDevice->byTopOFDMBasicRate);
+ return pDevice->uSIFS + uAckTime;
+ } else {
+ return 0;
+ }
+ } else { /* First Frag or Mid Frag */
+ if (byFBOption == AUTO_FB_0) {
+ if (wRate < RATE_18M)
+ wRate = RATE_18M;
+ else if (wRate > RATE_54M)
+ wRate = RATE_54M;
+
+ if (uFragIdx == (uMACfragNum-2))
+ uNextPktTime = s_uGetTxRsvTime(pDevice, byPktType, cbLastFragmentSize, wFB_Opt0[FB_RATE0][wRate-RATE_18M], bNeedAck);
+ else
+ uNextPktTime = s_uGetTxRsvTime(pDevice, byPktType, cbFrameLength, wFB_Opt0[FB_RATE0][wRate-RATE_18M], bNeedAck);
+
+ } else { /* (byFBOption == AUTO_FB_1) */
+ if (wRate < RATE_18M)
+ wRate = RATE_18M;
+ else if (wRate > RATE_54M)
+ wRate = RATE_54M;
+
+ if (uFragIdx == (uMACfragNum-2))
+ uNextPktTime = s_uGetTxRsvTime(pDevice, byPktType, cbLastFragmentSize, wFB_Opt1[FB_RATE0][wRate-RATE_18M], bNeedAck);
+ else
+ uNextPktTime = s_uGetTxRsvTime(pDevice, byPktType, cbFrameLength, wFB_Opt1[FB_RATE0][wRate-RATE_18M], bNeedAck);
+
+ }
+
+ if (bNeedAck) {
+ uAckTime = BBuGetFrameTime(pDevice->byPreambleType, byPktType, 14, pDevice->byTopOFDMBasicRate);
+ return pDevice->uSIFS + uAckTime + uNextPktTime;
+ } else {
+ return pDevice->uSIFS + uNextPktTime;
+ }
+ }
+ break;
+
+ case DATADUR_A_F1: /* DATADUR_A_F1 */
+ if (((uMACfragNum == 1)) || bLastFrag) { /* Non Frag or Last Frag */
+ if (bNeedAck) {
+ uAckTime = BBuGetFrameTime(pDevice->byPreambleType, byPktType, 14, pDevice->byTopOFDMBasicRate);
+ return pDevice->uSIFS + uAckTime;
+ } else {
+ return 0;
+ }
+ } else { /* First Frag or Mid Frag */
+ if (byFBOption == AUTO_FB_0) {
+ if (wRate < RATE_18M)
+ wRate = RATE_18M;
+ else if (wRate > RATE_54M)
+ wRate = RATE_54M;
+
+ if (uFragIdx == (uMACfragNum-2))
+ uNextPktTime = s_uGetTxRsvTime(pDevice, byPktType, cbLastFragmentSize, wFB_Opt0[FB_RATE1][wRate-RATE_18M], bNeedAck);
+ else
+ uNextPktTime = s_uGetTxRsvTime(pDevice, byPktType, cbFrameLength, wFB_Opt0[FB_RATE1][wRate-RATE_18M], bNeedAck);
+
+ } else { /* (byFBOption == AUTO_FB_1) */
+ if (wRate < RATE_18M)
+ wRate = RATE_18M;
+ else if (wRate > RATE_54M)
+ wRate = RATE_54M;
+
+ if (uFragIdx == (uMACfragNum-2))
+ uNextPktTime = s_uGetTxRsvTime(pDevice, byPktType, cbLastFragmentSize, wFB_Opt1[FB_RATE1][wRate-RATE_18M], bNeedAck);
+ else
+ uNextPktTime = s_uGetTxRsvTime(pDevice, byPktType, cbFrameLength, wFB_Opt1[FB_RATE1][wRate-RATE_18M], bNeedAck);
+ }
+ if (bNeedAck) {
+ uAckTime = BBuGetFrameTime(pDevice->byPreambleType, byPktType, 14, pDevice->byTopOFDMBasicRate);
+ return pDevice->uSIFS + uAckTime + uNextPktTime;
+ } else {
+ return pDevice->uSIFS + uNextPktTime;
+ }
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ ASSERT(false);
+ return 0;
+}
+
+/* byFreqType: 0=>5GHZ 1=>2.4GHZ */
+static
+__le16
+s_uGetRTSCTSDuration(
+ struct vnt_private *pDevice,
+ unsigned char byDurType,
+ unsigned int cbFrameLength,
+ unsigned char byPktType,
+ unsigned short wRate,
+ bool bNeedAck,
+ unsigned char byFBOption
+)
+{
+ unsigned int uCTSTime = 0, uDurTime = 0;
+
+ switch (byDurType) {
+ case RTSDUR_BB: /* RTSDuration_bb */
+ uCTSTime = BBuGetFrameTime(pDevice->byPreambleType, byPktType, 14, pDevice->byTopCCKBasicRate);
+ uDurTime = uCTSTime + 2*pDevice->uSIFS + s_uGetTxRsvTime(pDevice, byPktType, cbFrameLength, wRate, bNeedAck);
+ break;
+
+ case RTSDUR_BA: /* RTSDuration_ba */
+ uCTSTime = BBuGetFrameTime(pDevice->byPreambleType, byPktType, 14, pDevice->byTopCCKBasicRate);
+ uDurTime = uCTSTime + 2*pDevice->uSIFS + s_uGetTxRsvTime(pDevice, byPktType, cbFrameLength, wRate, bNeedAck);
+ break;
+
+ case RTSDUR_AA: /* RTSDuration_aa */
+ uCTSTime = BBuGetFrameTime(pDevice->byPreambleType, byPktType, 14, pDevice->byTopOFDMBasicRate);
+ uDurTime = uCTSTime + 2*pDevice->uSIFS + s_uGetTxRsvTime(pDevice, byPktType, cbFrameLength, wRate, bNeedAck);
+ break;
+
+ case CTSDUR_BA: /* CTSDuration_ba */
+ uDurTime = pDevice->uSIFS + s_uGetTxRsvTime(pDevice, byPktType, cbFrameLength, wRate, bNeedAck);
+ break;
+
+ case RTSDUR_BA_F0: /* RTSDuration_ba_f0 */
+ uCTSTime = BBuGetFrameTime(pDevice->byPreambleType, byPktType, 14, pDevice->byTopCCKBasicRate);
+ if ((byFBOption == AUTO_FB_0) && (wRate >= RATE_18M) && (wRate <= RATE_54M))
+ uDurTime = uCTSTime + 2 * pDevice->uSIFS + s_uGetTxRsvTime(pDevice, byPktType, cbFrameLength, wFB_Opt0[FB_RATE0][wRate-RATE_18M], bNeedAck);
+ else if ((byFBOption == AUTO_FB_1) && (wRate >= RATE_18M) && (wRate <= RATE_54M))
+ uDurTime = uCTSTime + 2 * pDevice->uSIFS + s_uGetTxRsvTime(pDevice, byPktType, cbFrameLength, wFB_Opt1[FB_RATE0][wRate-RATE_18M], bNeedAck);
+
+ break;
+
+ case RTSDUR_AA_F0: /* RTSDuration_aa_f0 */
+ uCTSTime = BBuGetFrameTime(pDevice->byPreambleType, byPktType, 14, pDevice->byTopOFDMBasicRate);
+ if ((byFBOption == AUTO_FB_0) && (wRate >= RATE_18M) && (wRate <= RATE_54M))
+ uDurTime = uCTSTime + 2*pDevice->uSIFS + s_uGetTxRsvTime(pDevice, byPktType, cbFrameLength, wFB_Opt0[FB_RATE0][wRate-RATE_18M], bNeedAck);
+ else if ((byFBOption == AUTO_FB_1) && (wRate >= RATE_18M) && (wRate <= RATE_54M))
+ uDurTime = uCTSTime + 2*pDevice->uSIFS + s_uGetTxRsvTime(pDevice, byPktType, cbFrameLength, wFB_Opt1[FB_RATE0][wRate-RATE_18M], bNeedAck);
+
+ break;
+
+ case RTSDUR_BA_F1: /* RTSDuration_ba_f1 */
+ uCTSTime = BBuGetFrameTime(pDevice->byPreambleType, byPktType, 14, pDevice->byTopCCKBasicRate);
+ if ((byFBOption == AUTO_FB_0) && (wRate >= RATE_18M) && (wRate <= RATE_54M))
+ uDurTime = uCTSTime + 2*pDevice->uSIFS + s_uGetTxRsvTime(pDevice, byPktType, cbFrameLength, wFB_Opt0[FB_RATE1][wRate-RATE_18M], bNeedAck);
+ else if ((byFBOption == AUTO_FB_1) && (wRate >= RATE_18M) && (wRate <= RATE_54M))
+ uDurTime = uCTSTime + 2*pDevice->uSIFS + s_uGetTxRsvTime(pDevice, byPktType, cbFrameLength, wFB_Opt1[FB_RATE1][wRate-RATE_18M], bNeedAck);
+
+ break;
+
+ case RTSDUR_AA_F1: /* RTSDuration_aa_f1 */
+ uCTSTime = BBuGetFrameTime(pDevice->byPreambleType, byPktType, 14, pDevice->byTopOFDMBasicRate);
+ if ((byFBOption == AUTO_FB_0) && (wRate >= RATE_18M) && (wRate <= RATE_54M))
+ uDurTime = uCTSTime + 2*pDevice->uSIFS + s_uGetTxRsvTime(pDevice, byPktType, cbFrameLength, wFB_Opt0[FB_RATE1][wRate-RATE_18M], bNeedAck);
+ else if ((byFBOption == AUTO_FB_1) && (wRate >= RATE_18M) && (wRate <= RATE_54M))
+ uDurTime = uCTSTime + 2*pDevice->uSIFS + s_uGetTxRsvTime(pDevice, byPktType, cbFrameLength, wFB_Opt1[FB_RATE1][wRate-RATE_18M], bNeedAck);
+
+ break;
+
+ case CTSDUR_BA_F0: /* CTSDuration_ba_f0 */
+ if ((byFBOption == AUTO_FB_0) && (wRate >= RATE_18M) && (wRate <= RATE_54M))
+ uDurTime = pDevice->uSIFS + s_uGetTxRsvTime(pDevice, byPktType, cbFrameLength, wFB_Opt0[FB_RATE0][wRate-RATE_18M], bNeedAck);
+ else if ((byFBOption == AUTO_FB_1) && (wRate >= RATE_18M) && (wRate <= RATE_54M))
+ uDurTime = pDevice->uSIFS + s_uGetTxRsvTime(pDevice, byPktType, cbFrameLength, wFB_Opt1[FB_RATE0][wRate-RATE_18M], bNeedAck);
+
+ break;
+
+ case CTSDUR_BA_F1: /* CTSDuration_ba_f1 */
+ if ((byFBOption == AUTO_FB_0) && (wRate >= RATE_18M) && (wRate <= RATE_54M))
+ uDurTime = pDevice->uSIFS + s_uGetTxRsvTime(pDevice, byPktType, cbFrameLength, wFB_Opt0[FB_RATE1][wRate-RATE_18M], bNeedAck);
+ else if ((byFBOption == AUTO_FB_1) && (wRate >= RATE_18M) && (wRate <= RATE_54M))
+ uDurTime = pDevice->uSIFS + s_uGetTxRsvTime(pDevice, byPktType, cbFrameLength, wFB_Opt1[FB_RATE1][wRate-RATE_18M], bNeedAck);
+
+ break;
+
+ default:
+ break;
+ }
+
+ return cpu_to_le16((u16)uDurTime);
+}
+
+static
+__le16
+s_uFillDataHead(
+ struct vnt_private *pDevice,
+ unsigned char byPktType,
+ void *pTxDataHead,
+ unsigned int cbFrameLength,
+ unsigned int uDMAIdx,
+ bool bNeedAck,
+ unsigned int uFragIdx,
+ unsigned int cbLastFragmentSize,
+ unsigned int uMACfragNum,
+ unsigned char byFBOption,
+ unsigned short wCurrentRate,
+ bool is_pspoll
+)
+{
+
+ if (pTxDataHead == NULL)
+ return 0;
+
+
+ if (byPktType == PK_TYPE_11GB || byPktType == PK_TYPE_11GA) {
+ if (byFBOption == AUTO_FB_NONE) {
+ struct vnt_tx_datahead_g *buf = pTxDataHead;
+ /* Get SignalField, ServiceField & Length */
+ vnt_get_phy_field(pDevice, cbFrameLength, wCurrentRate,
+ byPktType, &buf->a);
+
+ vnt_get_phy_field(pDevice, cbFrameLength,
+ pDevice->byTopCCKBasicRate,
+ PK_TYPE_11B, &buf->b);
+
+ if (is_pspoll) {
+ __le16 dur = cpu_to_le16(pDevice->current_aid | BIT(14) | BIT(15));
+
+ buf->duration_a = dur;
+ buf->duration_b = dur;
+ } else {
+ /* Get Duration and TimeStamp */
+ buf->duration_a =
+ cpu_to_le16((u16)s_uGetDataDuration(pDevice, DATADUR_A, cbFrameLength,
+ byPktType, wCurrentRate, bNeedAck, uFragIdx,
+ cbLastFragmentSize, uMACfragNum,
+ byFBOption));
+ buf->duration_b =
+ cpu_to_le16((u16)s_uGetDataDuration(pDevice, DATADUR_B, cbFrameLength,
+ PK_TYPE_11B, pDevice->byTopCCKBasicRate,
+ bNeedAck, uFragIdx, cbLastFragmentSize,
+ uMACfragNum, byFBOption));
+ }
+
+ buf->time_stamp_off_a = vnt_time_stamp_off(pDevice, wCurrentRate);
+ buf->time_stamp_off_b = vnt_time_stamp_off(pDevice, pDevice->byTopCCKBasicRate);
+
+ return buf->duration_a;
+ } else {
+ /* Auto Fallback */
+ struct vnt_tx_datahead_g_fb *buf = pTxDataHead;
+ /* Get SignalField, ServiceField & Length */
+ vnt_get_phy_field(pDevice, cbFrameLength, wCurrentRate,
+ byPktType, &buf->a);
+
+ vnt_get_phy_field(pDevice, cbFrameLength,
+ pDevice->byTopCCKBasicRate,
+ PK_TYPE_11B, &buf->b);
+ /* Get Duration and TimeStamp */
+ buf->duration_a = cpu_to_le16((u16)s_uGetDataDuration(pDevice, DATADUR_A, cbFrameLength, byPktType,
+ wCurrentRate, bNeedAck, uFragIdx, cbLastFragmentSize, uMACfragNum, byFBOption));
+ buf->duration_b = cpu_to_le16((u16)s_uGetDataDuration(pDevice, DATADUR_B, cbFrameLength, PK_TYPE_11B,
+ pDevice->byTopCCKBasicRate, bNeedAck, uFragIdx, cbLastFragmentSize, uMACfragNum, byFBOption));
+ buf->duration_a_f0 = cpu_to_le16((u16)s_uGetDataDuration(pDevice, DATADUR_A_F0, cbFrameLength, byPktType,
+ wCurrentRate, bNeedAck, uFragIdx, cbLastFragmentSize, uMACfragNum, byFBOption));
+ buf->duration_a_f1 = cpu_to_le16((u16)s_uGetDataDuration(pDevice, DATADUR_A_F1, cbFrameLength, byPktType,
+ wCurrentRate, bNeedAck, uFragIdx, cbLastFragmentSize, uMACfragNum, byFBOption));
+
+ buf->time_stamp_off_a = vnt_time_stamp_off(pDevice, wCurrentRate);
+ buf->time_stamp_off_b = vnt_time_stamp_off(pDevice, pDevice->byTopCCKBasicRate);
+
+ return buf->duration_a;
+ } /* if (byFBOption == AUTO_FB_NONE) */
+ } else if (byPktType == PK_TYPE_11A) {
+ if ((byFBOption != AUTO_FB_NONE)) {
+ /* Auto Fallback */
+ struct vnt_tx_datahead_a_fb *buf = pTxDataHead;
+ /* Get SignalField, ServiceField & Length */
+ vnt_get_phy_field(pDevice, cbFrameLength, wCurrentRate,
+ byPktType, &buf->a);
+
+ /* Get Duration and TimeStampOff */
+ buf->duration = cpu_to_le16((u16)s_uGetDataDuration(pDevice, DATADUR_A, cbFrameLength, byPktType,
+ wCurrentRate, bNeedAck, uFragIdx, cbLastFragmentSize, uMACfragNum, byFBOption));
+ buf->duration_f0 = cpu_to_le16((u16)s_uGetDataDuration(pDevice, DATADUR_A_F0, cbFrameLength, byPktType,
+ wCurrentRate, bNeedAck, uFragIdx, cbLastFragmentSize, uMACfragNum, byFBOption));
+ buf->duration_f1 = cpu_to_le16((u16)s_uGetDataDuration(pDevice, DATADUR_A_F1, cbFrameLength, byPktType,
+ wCurrentRate, bNeedAck, uFragIdx, cbLastFragmentSize, uMACfragNum, byFBOption));
+ buf->time_stamp_off = vnt_time_stamp_off(pDevice, wCurrentRate);
+ return buf->duration;
+ } else {
+ struct vnt_tx_datahead_ab *buf = pTxDataHead;
+ /* Get SignalField, ServiceField & Length */
+ vnt_get_phy_field(pDevice, cbFrameLength, wCurrentRate,
+ byPktType, &buf->ab);
+
+ if (is_pspoll) {
+ __le16 dur = cpu_to_le16(pDevice->current_aid | BIT(14) | BIT(15));
+
+ buf->duration = dur;
+ } else {
+ /* Get Duration and TimeStampOff */
+ buf->duration =
+ cpu_to_le16((u16)s_uGetDataDuration(pDevice, DATADUR_A, cbFrameLength, byPktType,
+ wCurrentRate, bNeedAck, uFragIdx,
+ cbLastFragmentSize, uMACfragNum,
+ byFBOption));
+ }
+
+ buf->time_stamp_off = vnt_time_stamp_off(pDevice, wCurrentRate);
+ return buf->duration;
+ }
+ } else {
+ struct vnt_tx_datahead_ab *buf = pTxDataHead;
+ /* Get SignalField, ServiceField & Length */
+ vnt_get_phy_field(pDevice, cbFrameLength, wCurrentRate,
+ byPktType, &buf->ab);
+
+ if (is_pspoll) {
+ __le16 dur = cpu_to_le16(pDevice->current_aid | BIT(14) | BIT(15));
+
+ buf->duration = dur;
+ } else {
+ /* Get Duration and TimeStampOff */
+ buf->duration =
+ cpu_to_le16((u16)s_uGetDataDuration(pDevice, DATADUR_B, cbFrameLength, byPktType,
+ wCurrentRate, bNeedAck, uFragIdx,
+ cbLastFragmentSize, uMACfragNum,
+ byFBOption));
+ }
+
+ buf->time_stamp_off = vnt_time_stamp_off(pDevice, wCurrentRate);
+ return buf->duration;
+ }
+ return 0;
+}
+
+
+static
+void
+s_vFillRTSHead(
+ struct vnt_private *pDevice,
+ unsigned char byPktType,
+ void *pvRTS,
+ unsigned int cbFrameLength,
+ bool bNeedAck,
+ bool bDisCRC,
+ struct ieee80211_hdr *hdr,
+ unsigned short wCurrentRate,
+ unsigned char byFBOption
+)
+{
+ unsigned int uRTSFrameLen = 20;
+
+ if (pvRTS == NULL)
+ return;
+
+ if (bDisCRC) {
+ /* When CRCDIS bit is on, H/W forgot to generate FCS for RTS frame,
+ in this case we need to decrease its length by 4. */
+ uRTSFrameLen -= 4;
+ }
+
+ /* Note: So far RTSHead dosen't appear in ATIM & Beacom DMA, so we don't need to take them into account.
+ Otherwise, we need to modify codes for them. */
+ if (byPktType == PK_TYPE_11GB || byPktType == PK_TYPE_11GA) {
+ if (byFBOption == AUTO_FB_NONE) {
+ struct vnt_rts_g *buf = pvRTS;
+ /* Get SignalField, ServiceField & Length */
+ vnt_get_phy_field(pDevice, uRTSFrameLen,
+ pDevice->byTopCCKBasicRate,
+ PK_TYPE_11B, &buf->b);
+
+ vnt_get_phy_field(pDevice, uRTSFrameLen,
+ pDevice->byTopOFDMBasicRate,
+ byPktType, &buf->a);
+ /* Get Duration */
+ buf->duration_bb =
+ s_uGetRTSCTSDuration(pDevice, RTSDUR_BB,
+ cbFrameLength, PK_TYPE_11B,
+ pDevice->byTopCCKBasicRate,
+ bNeedAck, byFBOption);
+ buf->duration_aa =
+ s_uGetRTSCTSDuration(pDevice, RTSDUR_AA,
+ cbFrameLength, byPktType,
+ wCurrentRate, bNeedAck,
+ byFBOption);
+ buf->duration_ba =
+ s_uGetRTSCTSDuration(pDevice, RTSDUR_BA,
+ cbFrameLength, byPktType,
+ wCurrentRate, bNeedAck,
+ byFBOption);
+
+ buf->data.duration = buf->duration_aa;
+ /* Get RTS Frame body */
+ buf->data.frame_control =
+ cpu_to_le16(IEEE80211_FTYPE_CTL |
+ IEEE80211_STYPE_RTS);
+
+ ether_addr_copy(buf->data.ra, hdr->addr1);
+ ether_addr_copy(buf->data.ta, hdr->addr2);
+ } else {
+ struct vnt_rts_g_fb *buf = pvRTS;
+ /* Get SignalField, ServiceField & Length */
+ vnt_get_phy_field(pDevice, uRTSFrameLen,
+ pDevice->byTopCCKBasicRate,
+ PK_TYPE_11B, &buf->b);
+
+ vnt_get_phy_field(pDevice, uRTSFrameLen,
+ pDevice->byTopOFDMBasicRate,
+ byPktType, &buf->a);
+ /* Get Duration */
+ buf->duration_bb =
+ s_uGetRTSCTSDuration(pDevice, RTSDUR_BB,
+ cbFrameLength, PK_TYPE_11B,
+ pDevice->byTopCCKBasicRate,
+ bNeedAck, byFBOption);
+ buf->duration_aa =
+ s_uGetRTSCTSDuration(pDevice, RTSDUR_AA,
+ cbFrameLength, byPktType,
+ wCurrentRate, bNeedAck,
+ byFBOption);
+ buf->duration_ba =
+ s_uGetRTSCTSDuration(pDevice, RTSDUR_BA,
+ cbFrameLength, byPktType,
+ wCurrentRate, bNeedAck,
+ byFBOption);
+ buf->rts_duration_ba_f0 =
+ s_uGetRTSCTSDuration(pDevice, RTSDUR_BA_F0,
+ cbFrameLength, byPktType,
+ wCurrentRate, bNeedAck,
+ byFBOption);
+ buf->rts_duration_aa_f0 =
+ s_uGetRTSCTSDuration(pDevice, RTSDUR_AA_F0,
+ cbFrameLength, byPktType,
+ wCurrentRate, bNeedAck,
+ byFBOption);
+ buf->rts_duration_ba_f1 =
+ s_uGetRTSCTSDuration(pDevice, RTSDUR_BA_F1,
+ cbFrameLength, byPktType,
+ wCurrentRate, bNeedAck,
+ byFBOption);
+ buf->rts_duration_aa_f1 =
+ s_uGetRTSCTSDuration(pDevice, RTSDUR_AA_F1,
+ cbFrameLength, byPktType,
+ wCurrentRate, bNeedAck,
+ byFBOption);
+ buf->data.duration = buf->duration_aa;
+ /* Get RTS Frame body */
+ buf->data.frame_control =
+ cpu_to_le16(IEEE80211_FTYPE_CTL |
+ IEEE80211_STYPE_RTS);
+
+ ether_addr_copy(buf->data.ra, hdr->addr1);
+ ether_addr_copy(buf->data.ta, hdr->addr2);
+ } /* if (byFBOption == AUTO_FB_NONE) */
+ } else if (byPktType == PK_TYPE_11A) {
+ if (byFBOption == AUTO_FB_NONE) {
+ struct vnt_rts_ab *buf = pvRTS;
+ /* Get SignalField, ServiceField & Length */
+ vnt_get_phy_field(pDevice, uRTSFrameLen,
+ pDevice->byTopOFDMBasicRate,
+ byPktType, &buf->ab);
+ /* Get Duration */
+ buf->duration =
+ s_uGetRTSCTSDuration(pDevice, RTSDUR_AA,
+ cbFrameLength, byPktType,
+ wCurrentRate, bNeedAck,
+ byFBOption);
+ buf->data.duration = buf->duration;
+ /* Get RTS Frame body */
+ buf->data.frame_control =
+ cpu_to_le16(IEEE80211_FTYPE_CTL |
+ IEEE80211_STYPE_RTS);
+
+ ether_addr_copy(buf->data.ra, hdr->addr1);
+ ether_addr_copy(buf->data.ta, hdr->addr2);
+ } else {
+ struct vnt_rts_a_fb *buf = pvRTS;
+ /* Get SignalField, ServiceField & Length */
+ vnt_get_phy_field(pDevice, uRTSFrameLen,
+ pDevice->byTopOFDMBasicRate,
+ byPktType, &buf->a);
+ /* Get Duration */
+ buf->duration =
+ s_uGetRTSCTSDuration(pDevice, RTSDUR_AA,
+ cbFrameLength, byPktType,
+ wCurrentRate, bNeedAck,
+ byFBOption);
+ buf->rts_duration_f0 =
+ s_uGetRTSCTSDuration(pDevice, RTSDUR_AA_F0,
+ cbFrameLength, byPktType,
+ wCurrentRate, bNeedAck,
+ byFBOption);
+ buf->rts_duration_f1 =
+ s_uGetRTSCTSDuration(pDevice, RTSDUR_AA_F1,
+ cbFrameLength, byPktType,
+ wCurrentRate, bNeedAck,
+ byFBOption);
+ buf->data.duration = buf->duration;
+ /* Get RTS Frame body */
+ buf->data.frame_control =
+ cpu_to_le16(IEEE80211_FTYPE_CTL |
+ IEEE80211_STYPE_RTS);
+
+ ether_addr_copy(buf->data.ra, hdr->addr1);
+ ether_addr_copy(buf->data.ta, hdr->addr2);
+ }
+ } else if (byPktType == PK_TYPE_11B) {
+ struct vnt_rts_ab *buf = pvRTS;
+ /* Get SignalField, ServiceField & Length */
+ vnt_get_phy_field(pDevice, uRTSFrameLen,
+ pDevice->byTopCCKBasicRate,
+ PK_TYPE_11B, &buf->ab);
+ /* Get Duration */
+ buf->duration =
+ s_uGetRTSCTSDuration(pDevice, RTSDUR_BB, cbFrameLength,
+ byPktType, wCurrentRate, bNeedAck,
+ byFBOption);
+
+ buf->data.duration = buf->duration;
+ /* Get RTS Frame body */
+ buf->data.frame_control =
+ cpu_to_le16(IEEE80211_FTYPE_CTL | IEEE80211_STYPE_RTS);
+
+ ether_addr_copy(buf->data.ra, hdr->addr1);
+ ether_addr_copy(buf->data.ta, hdr->addr2);
+ }
+}
+
+static
+void
+s_vFillCTSHead(
+ struct vnt_private *pDevice,
+ unsigned int uDMAIdx,
+ unsigned char byPktType,
+ void *pvCTS,
+ unsigned int cbFrameLength,
+ bool bNeedAck,
+ bool bDisCRC,
+ unsigned short wCurrentRate,
+ unsigned char byFBOption
+)
+{
+ unsigned int uCTSFrameLen = 14;
+
+ if (pvCTS == NULL)
+ return;
+
+ if (bDisCRC) {
+ /* When CRCDIS bit is on, H/W forgot to generate FCS for CTS frame,
+ in this case we need to decrease its length by 4. */
+ uCTSFrameLen -= 4;
+ }
+
+ if (byPktType == PK_TYPE_11GB || byPktType == PK_TYPE_11GA) {
+ if (byFBOption != AUTO_FB_NONE && uDMAIdx != TYPE_ATIMDMA && uDMAIdx != TYPE_BEACONDMA) {
+ /* Auto Fall back */
+ struct vnt_cts_fb *buf = pvCTS;
+ /* Get SignalField, ServiceField & Length */
+ vnt_get_phy_field(pDevice, uCTSFrameLen,
+ pDevice->byTopCCKBasicRate,
+ PK_TYPE_11B, &buf->b);
+
+ buf->duration_ba =
+ s_uGetRTSCTSDuration(pDevice, CTSDUR_BA,
+ cbFrameLength, byPktType,
+ wCurrentRate, bNeedAck,
+ byFBOption);
+
+ /* Get CTSDuration_ba_f0 */
+ buf->cts_duration_ba_f0 =
+ s_uGetRTSCTSDuration(pDevice, CTSDUR_BA_F0,
+ cbFrameLength, byPktType,
+ wCurrentRate, bNeedAck,
+ byFBOption);
+
+ /* Get CTSDuration_ba_f1 */
+ buf->cts_duration_ba_f1 =
+ s_uGetRTSCTSDuration(pDevice, CTSDUR_BA_F1,
+ cbFrameLength, byPktType,
+ wCurrentRate, bNeedAck,
+ byFBOption);
+
+ /* Get CTS Frame body */
+ buf->data.duration = buf->duration_ba;
+
+ buf->data.frame_control =
+ cpu_to_le16(IEEE80211_FTYPE_CTL |
+ IEEE80211_STYPE_CTS);
+
+ buf->reserved2 = 0x0;
+
+ ether_addr_copy(buf->data.ra,
+ pDevice->abyCurrentNetAddr);
+ } else { /* if (byFBOption != AUTO_FB_NONE && uDMAIdx != TYPE_ATIMDMA && uDMAIdx != TYPE_BEACONDMA) */
+ struct vnt_cts *buf = pvCTS;
+ /* Get SignalField, ServiceField & Length */
+ vnt_get_phy_field(pDevice, uCTSFrameLen,
+ pDevice->byTopCCKBasicRate,
+ PK_TYPE_11B, &buf->b);
+
+ /* Get CTSDuration_ba */
+ buf->duration_ba =
+ s_uGetRTSCTSDuration(pDevice, CTSDUR_BA,
+ cbFrameLength, byPktType,
+ wCurrentRate, bNeedAck,
+ byFBOption);
+
+ /* Get CTS Frame body */
+ buf->data.duration = buf->duration_ba;
+
+ buf->data.frame_control =
+ cpu_to_le16(IEEE80211_FTYPE_CTL |
+ IEEE80211_STYPE_CTS);
+
+ buf->reserved2 = 0x0;
+ ether_addr_copy(buf->data.ra,
+ pDevice->abyCurrentNetAddr);
+ }
+ }
+}
+
+/*+
+ *
+ * Description:
+ * Generate FIFO control for MAC & Baseband controller
+ *
+ * Parameters:
+ * In:
+ * pDevice - Pointer to adapter
+ * pTxDataHead - Transmit Data Buffer
+ * pTxBufHead - pTxBufHead
+ * pvRrvTime - pvRrvTime
+ * pvRTS - RTS Buffer
+ * pCTS - CTS Buffer
+ * cbFrameSize - Transmit Data Length (Hdr+Payload+FCS)
+ * bNeedACK - If need ACK
+ * uDescIdx - Desc Index
+ * Out:
+ * none
+ *
+ * Return Value: none
+ *
+ -
+ * unsigned int cbFrameSize, Hdr+Payload+FCS */
+static
+void
+s_vGenerateTxParameter(
+ struct vnt_private *pDevice,
+ unsigned char byPktType,
+ struct vnt_tx_fifo_head *tx_buffer_head,
+ void *pvRrvTime,
+ void *pvRTS,
+ void *pvCTS,
+ unsigned int cbFrameSize,
+ bool bNeedACK,
+ unsigned int uDMAIdx,
+ void *psEthHeader,
+ unsigned short wCurrentRate
+)
+{
+ u16 fifo_ctl = le16_to_cpu(tx_buffer_head->fifo_ctl);
+ bool bDisCRC = false;
+ unsigned char byFBOption = AUTO_FB_NONE;
+
+ tx_buffer_head->current_rate = cpu_to_le16(wCurrentRate);
+
+ if (fifo_ctl & FIFOCTL_CRCDIS)
+ bDisCRC = true;
+
+ if (fifo_ctl & FIFOCTL_AUTO_FB_0)
+ byFBOption = AUTO_FB_0;
+ else if (fifo_ctl & FIFOCTL_AUTO_FB_1)
+ byFBOption = AUTO_FB_1;
+
+ if (!pvRrvTime)
+ return;
+
+ if (byPktType == PK_TYPE_11GB || byPktType == PK_TYPE_11GA) {
+ if (pvRTS != NULL) { /* RTS_need
+ Fill RsvTime */
+ struct vnt_rrv_time_rts *buf = pvRrvTime;
+
+ buf->rts_rrv_time_aa = s_uGetRTSCTSRsvTime(pDevice, 2, byPktType, cbFrameSize, wCurrentRate);
+ buf->rts_rrv_time_ba = s_uGetRTSCTSRsvTime(pDevice, 1, byPktType, cbFrameSize, wCurrentRate);
+ buf->rts_rrv_time_bb = s_uGetRTSCTSRsvTime(pDevice, 0, byPktType, cbFrameSize, wCurrentRate);
+ buf->rrv_time_a = vnt_rxtx_rsvtime_le16(pDevice, byPktType, cbFrameSize, wCurrentRate, bNeedACK);
+ buf->rrv_time_b = vnt_rxtx_rsvtime_le16(pDevice, PK_TYPE_11B, cbFrameSize, pDevice->byTopCCKBasicRate, bNeedACK);
+
+ s_vFillRTSHead(pDevice, byPktType, pvRTS, cbFrameSize, bNeedACK, bDisCRC, psEthHeader, wCurrentRate, byFBOption);
+ } else {/* RTS_needless, PCF mode */
+ struct vnt_rrv_time_cts *buf = pvRrvTime;
+
+ buf->rrv_time_a = vnt_rxtx_rsvtime_le16(pDevice, byPktType, cbFrameSize, wCurrentRate, bNeedACK);
+ buf->rrv_time_b = vnt_rxtx_rsvtime_le16(pDevice, PK_TYPE_11B, cbFrameSize, pDevice->byTopCCKBasicRate, bNeedACK);
+ buf->cts_rrv_time_ba = s_uGetRTSCTSRsvTime(pDevice, 3, byPktType, cbFrameSize, wCurrentRate);
+
+ /* Fill CTS */
+ s_vFillCTSHead(pDevice, uDMAIdx, byPktType, pvCTS, cbFrameSize, bNeedACK, bDisCRC, wCurrentRate, byFBOption);
+ }
+ } else if (byPktType == PK_TYPE_11A) {
+ if (pvRTS != NULL) {/* RTS_need, non PCF mode */
+ struct vnt_rrv_time_ab *buf = pvRrvTime;
+
+ buf->rts_rrv_time = s_uGetRTSCTSRsvTime(pDevice, 2, byPktType, cbFrameSize, wCurrentRate);
+ buf->rrv_time = vnt_rxtx_rsvtime_le16(pDevice, byPktType, cbFrameSize, wCurrentRate, bNeedACK);
+
+ /* Fill RTS */
+ s_vFillRTSHead(pDevice, byPktType, pvRTS, cbFrameSize, bNeedACK, bDisCRC, psEthHeader, wCurrentRate, byFBOption);
+ } else if (pvRTS == NULL) {/* RTS_needless, non PCF mode */
+ struct vnt_rrv_time_ab *buf = pvRrvTime;
+
+ buf->rrv_time = vnt_rxtx_rsvtime_le16(pDevice, PK_TYPE_11A, cbFrameSize, wCurrentRate, bNeedACK);
+ }
+ } else if (byPktType == PK_TYPE_11B) {
+ if ((pvRTS != NULL)) {/* RTS_need, non PCF mode */
+ struct vnt_rrv_time_ab *buf = pvRrvTime;
+
+ buf->rts_rrv_time = s_uGetRTSCTSRsvTime(pDevice, 0, byPktType, cbFrameSize, wCurrentRate);
+ buf->rrv_time = vnt_rxtx_rsvtime_le16(pDevice, PK_TYPE_11B, cbFrameSize, wCurrentRate, bNeedACK);
+
+ /* Fill RTS */
+ s_vFillRTSHead(pDevice, byPktType, pvRTS, cbFrameSize, bNeedACK, bDisCRC, psEthHeader, wCurrentRate, byFBOption);
+ } else { /* RTS_needless, non PCF mode */
+ struct vnt_rrv_time_ab *buf = pvRrvTime;
+
+ buf->rrv_time = vnt_rxtx_rsvtime_le16(pDevice, PK_TYPE_11B, cbFrameSize, wCurrentRate, bNeedACK);
+ }
+ }
+}
+
+static unsigned int
+s_cbFillTxBufHead(struct vnt_private *pDevice, unsigned char byPktType,
+ unsigned char *pbyTxBufferAddr,
+ unsigned int uDMAIdx, PSTxDesc pHeadTD,
+ unsigned int is_pspoll)
+{
+ PDEVICE_TD_INFO td_info = pHeadTD->pTDInfo;
+ struct sk_buff *skb = td_info->skb;
+ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
+ struct vnt_tx_fifo_head *tx_buffer_head =
+ (struct vnt_tx_fifo_head *)td_info->buf;
+ u16 fifo_ctl = le16_to_cpu(tx_buffer_head->fifo_ctl);
+ unsigned int cbFrameSize;
+ __le16 uDuration;
+ unsigned char *pbyBuffer;
+ unsigned int uLength = 0;
+ unsigned int cbMICHDR = 0;
+ unsigned int uMACfragNum = 1;
+ unsigned int uPadding = 0;
+ unsigned int cbReqCount = 0;
+ bool bNeedACK = (bool)(fifo_ctl & FIFOCTL_NEEDACK);
+ bool bRTS = (bool)(fifo_ctl & FIFOCTL_RTS);
+ PSTxDesc ptdCurr;
+ unsigned int cbHeaderLength = 0;
+ void *pvRrvTime;
+ struct vnt_mic_hdr *pMICHDR;
+ void *pvRTS;
+ void *pvCTS;
+ void *pvTxDataHd;
+ unsigned short wTxBufSize; /* FFinfo size */
+ unsigned char byFBOption = AUTO_FB_NONE;
+
+ pvRrvTime = pMICHDR = pvRTS = pvCTS = pvTxDataHd = NULL;
+
+ cbFrameSize = skb->len + 4;
+
+ if (info->control.hw_key) {
+ switch (info->control.hw_key->cipher) {
+ case WLAN_CIPHER_SUITE_CCMP:
+ cbMICHDR = sizeof(struct vnt_mic_hdr);
+ default:
+ break;
+ }
+
+ cbFrameSize += info->control.hw_key->icv_len;
+
+ if (pDevice->byLocalID > REV_ID_VT3253_A1) {
+ /* MAC Header should be padding 0 to DW alignment. */
+ uPadding = 4 - (ieee80211_get_hdrlen_from_skb(skb) % 4);
+ uPadding %= 4;
+ }
+ }
+
+ /*
+ * Use for AUTO FALL BACK
+ */
+ if (fifo_ctl & FIFOCTL_AUTO_FB_0)
+ byFBOption = AUTO_FB_0;
+ else if (fifo_ctl & FIFOCTL_AUTO_FB_1)
+ byFBOption = AUTO_FB_1;
+
+
+ /* Set RrvTime/RTS/CTS Buffer */
+ wTxBufSize = sizeof(STxBufHead);
+ if (byPktType == PK_TYPE_11GB || byPktType == PK_TYPE_11GA) {/* 802.11g packet */
+
+ if (byFBOption == AUTO_FB_NONE) {
+ if (bRTS == true) {/* RTS_need */
+ pvRrvTime = (void *)(pbyTxBufferAddr + wTxBufSize);
+ pMICHDR = (struct vnt_mic_hdr *)(pbyTxBufferAddr + wTxBufSize + sizeof(struct vnt_rrv_time_rts));
+ pvRTS = (void *)(pbyTxBufferAddr + wTxBufSize + sizeof(struct vnt_rrv_time_rts) + cbMICHDR);
+ pvCTS = NULL;
+ pvTxDataHd = (void *)(pbyTxBufferAddr + wTxBufSize + sizeof(struct vnt_rrv_time_rts) +
+ cbMICHDR + sizeof(struct vnt_rts_g));
+ cbHeaderLength = wTxBufSize + sizeof(struct vnt_rrv_time_rts) +
+ cbMICHDR + sizeof(struct vnt_rts_g) +
+ sizeof(struct vnt_tx_datahead_g);
+ } else { /* RTS_needless */
+ pvRrvTime = (void *)(pbyTxBufferAddr + wTxBufSize);
+ pMICHDR = (struct vnt_mic_hdr *) (pbyTxBufferAddr + wTxBufSize + sizeof(struct vnt_rrv_time_cts));
+ pvRTS = NULL;
+ pvCTS = (void *) (pbyTxBufferAddr + wTxBufSize + sizeof(struct vnt_rrv_time_cts) + cbMICHDR);
+ pvTxDataHd = (void *)(pbyTxBufferAddr + wTxBufSize +
+ sizeof(struct vnt_rrv_time_cts) + cbMICHDR + sizeof(struct vnt_cts));
+ cbHeaderLength = wTxBufSize + sizeof(struct vnt_rrv_time_cts) +
+ cbMICHDR + sizeof(struct vnt_cts) + sizeof(struct vnt_tx_datahead_g);
+ }
+ } else {
+ /* Auto Fall Back */
+ if (bRTS == true) {/* RTS_need */
+ pvRrvTime = (void *)(pbyTxBufferAddr + wTxBufSize);
+ pMICHDR = (struct vnt_mic_hdr *) (pbyTxBufferAddr + wTxBufSize + sizeof(struct vnt_rrv_time_rts));
+ pvRTS = (void *) (pbyTxBufferAddr + wTxBufSize + sizeof(struct vnt_rrv_time_rts) + cbMICHDR);
+ pvCTS = NULL;
+ pvTxDataHd = (void *)(pbyTxBufferAddr + wTxBufSize + sizeof(struct vnt_rrv_time_rts) +
+ cbMICHDR + sizeof(struct vnt_rts_g_fb));
+ cbHeaderLength = wTxBufSize + sizeof(struct vnt_rrv_time_rts) +
+ cbMICHDR + sizeof(struct vnt_rts_g_fb) + sizeof(struct vnt_tx_datahead_g_fb);
+ } else { /* RTS_needless */
+ pvRrvTime = (void *)(pbyTxBufferAddr + wTxBufSize);
+ pMICHDR = (struct vnt_mic_hdr *) (pbyTxBufferAddr + wTxBufSize + sizeof(struct vnt_rrv_time_cts));
+ pvRTS = NULL;
+ pvCTS = (void *)(pbyTxBufferAddr + wTxBufSize + sizeof(struct vnt_rrv_time_cts) + cbMICHDR);
+ pvTxDataHd = (void *)(pbyTxBufferAddr + wTxBufSize + sizeof(struct vnt_rrv_time_cts) +
+ cbMICHDR + sizeof(struct vnt_cts_fb));
+ cbHeaderLength = wTxBufSize + sizeof(struct vnt_rrv_time_cts) +
+ cbMICHDR + sizeof(struct vnt_cts_fb) + sizeof(struct vnt_tx_datahead_g_fb);
+ }
+ } /* Auto Fall Back */
+ } else {/* 802.11a/b packet */
+
+ if (byFBOption == AUTO_FB_NONE) {
+ if (bRTS == true) {
+ pvRrvTime = (void *)(pbyTxBufferAddr + wTxBufSize);
+ pMICHDR = (struct vnt_mic_hdr *) (pbyTxBufferAddr + wTxBufSize + sizeof(struct vnt_rrv_time_ab));
+ pvRTS = (void *)(pbyTxBufferAddr + wTxBufSize + sizeof(struct vnt_rrv_time_ab) + cbMICHDR);
+ pvCTS = NULL;
+ pvTxDataHd = (void *)(pbyTxBufferAddr + wTxBufSize +
+ sizeof(struct vnt_rrv_time_ab) + cbMICHDR + sizeof(struct vnt_rts_ab));
+ cbHeaderLength = wTxBufSize + sizeof(struct vnt_rrv_time_ab) +
+ cbMICHDR + sizeof(struct vnt_rts_ab) + sizeof(struct vnt_tx_datahead_ab);
+ } else { /* RTS_needless, need MICHDR */
+ pvRrvTime = (void *)(pbyTxBufferAddr + wTxBufSize);
+ pMICHDR = (struct vnt_mic_hdr *) (pbyTxBufferAddr + wTxBufSize + sizeof(struct vnt_rrv_time_ab));
+ pvRTS = NULL;
+ pvCTS = NULL;
+ pvTxDataHd = (void *)(pbyTxBufferAddr + wTxBufSize + sizeof(struct vnt_rrv_time_ab) + cbMICHDR);
+ cbHeaderLength = wTxBufSize + sizeof(struct vnt_rrv_time_ab) +
+ cbMICHDR + sizeof(struct vnt_tx_datahead_ab);
+ }
+ } else {
+ /* Auto Fall Back */
+ if (bRTS == true) { /* RTS_need */
+ pvRrvTime = (void *)(pbyTxBufferAddr + wTxBufSize);
+ pMICHDR = (struct vnt_mic_hdr *) (pbyTxBufferAddr + wTxBufSize + sizeof(struct vnt_rrv_time_ab));
+ pvRTS = (void *)(pbyTxBufferAddr + wTxBufSize + sizeof(struct vnt_rrv_time_ab) + cbMICHDR);
+ pvCTS = NULL;
+ pvTxDataHd = (void *)(pbyTxBufferAddr + wTxBufSize +
+ sizeof(struct vnt_rrv_time_ab) + cbMICHDR + sizeof(struct vnt_rts_a_fb));
+ cbHeaderLength = wTxBufSize + sizeof(struct vnt_rrv_time_ab) +
+ cbMICHDR + sizeof(struct vnt_rts_a_fb) + sizeof(struct vnt_tx_datahead_a_fb);
+ } else { /* RTS_needless */
+ pvRrvTime = (void *)(pbyTxBufferAddr + wTxBufSize);
+ pMICHDR = (struct vnt_mic_hdr *)(pbyTxBufferAddr + wTxBufSize + sizeof(struct vnt_rrv_time_ab));
+ pvRTS = NULL;
+ pvCTS = NULL;
+ pvTxDataHd = (void *)(pbyTxBufferAddr + wTxBufSize + sizeof(struct vnt_rrv_time_ab) + cbMICHDR);
+ cbHeaderLength = wTxBufSize + sizeof(struct vnt_rrv_time_ab) +
+ cbMICHDR + sizeof(struct vnt_tx_datahead_a_fb);
+ }
+ } /* Auto Fall Back */
+ }
+
+ td_info->mic_hdr = pMICHDR;
+
+ memset((void *)(pbyTxBufferAddr + wTxBufSize), 0, (cbHeaderLength - wTxBufSize));
+
+ /* Fill FIFO,RrvTime,RTS,and CTS */
+ s_vGenerateTxParameter(pDevice, byPktType, tx_buffer_head, pvRrvTime, pvRTS, pvCTS,
+ cbFrameSize, bNeedACK, uDMAIdx, hdr, pDevice->wCurrentRate);
+ /* Fill DataHead */
+ uDuration = s_uFillDataHead(pDevice, byPktType, pvTxDataHd, cbFrameSize, uDMAIdx, bNeedACK,
+ 0, 0, uMACfragNum, byFBOption, pDevice->wCurrentRate, is_pspoll);
+
+ hdr->duration_id = uDuration;
+
+ cbReqCount = cbHeaderLength + uPadding + skb->len;
+ pbyBuffer = (unsigned char *)pHeadTD->pTDInfo->buf;
+ uLength = cbHeaderLength + uPadding;
+
+ /* Copy the Packet into a tx Buffer */
+ memcpy((pbyBuffer + uLength), skb->data, skb->len);
+
+ ptdCurr = (PSTxDesc)pHeadTD;
+
+ ptdCurr->pTDInfo->dwReqCount = cbReqCount;
+ ptdCurr->pTDInfo->dwHeaderLength = cbHeaderLength;
+ ptdCurr->pTDInfo->skb_dma = ptdCurr->pTDInfo->buf_dma;
+
+ return cbHeaderLength;
+}
+
+static void vnt_fill_txkey(struct ieee80211_hdr *hdr, u8 *key_buffer,
+ struct ieee80211_key_conf *tx_key,
+ struct sk_buff *skb, u16 payload_len,
+ struct vnt_mic_hdr *mic_hdr)
+{
+ struct ieee80211_key_seq seq;
+ u8 *iv = ((u8 *)hdr + ieee80211_get_hdrlen_from_skb(skb));
+
+ /* strip header and icv len from payload */
+ payload_len -= ieee80211_get_hdrlen_from_skb(skb);
+ payload_len -= tx_key->icv_len;
+
+ switch (tx_key->cipher) {
+ case WLAN_CIPHER_SUITE_WEP40:
+ case WLAN_CIPHER_SUITE_WEP104:
+ memcpy(key_buffer, iv, 3);
+ memcpy(key_buffer + 3, tx_key->key, tx_key->keylen);
+
+ if (tx_key->keylen == WLAN_KEY_LEN_WEP40) {
+ memcpy(key_buffer + 8, iv, 3);
+ memcpy(key_buffer + 11,
+ tx_key->key, WLAN_KEY_LEN_WEP40);
+ }
+
+ break;
+ case WLAN_CIPHER_SUITE_TKIP:
+ ieee80211_get_tkip_p2k(tx_key, skb, key_buffer);
+
+ break;
+ case WLAN_CIPHER_SUITE_CCMP:
+
+ if (!mic_hdr)
+ return;
+
+ mic_hdr->id = 0x59;
+ mic_hdr->payload_len = cpu_to_be16(payload_len);
+ ether_addr_copy(mic_hdr->mic_addr2, hdr->addr2);
+
+ ieee80211_get_key_tx_seq(tx_key, &seq);
+
+ memcpy(mic_hdr->ccmp_pn, seq.ccmp.pn, IEEE80211_CCMP_PN_LEN);
+
+ if (ieee80211_has_a4(hdr->frame_control))
+ mic_hdr->hlen = cpu_to_be16(28);
+ else
+ mic_hdr->hlen = cpu_to_be16(22);
+
+ ether_addr_copy(mic_hdr->addr1, hdr->addr1);
+ ether_addr_copy(mic_hdr->addr2, hdr->addr2);
+ ether_addr_copy(mic_hdr->addr3, hdr->addr3);
+
+ mic_hdr->frame_control = cpu_to_le16(
+ le16_to_cpu(hdr->frame_control) & 0xc78f);
+ mic_hdr->seq_ctrl = cpu_to_le16(
+ le16_to_cpu(hdr->seq_ctrl) & 0xf);
+
+ if (ieee80211_has_a4(hdr->frame_control))
+ ether_addr_copy(mic_hdr->addr4, hdr->addr4);
+
+ memcpy(key_buffer, tx_key->key, WLAN_KEY_LEN_CCMP);
+
+ break;
+ default:
+ break;
+ }
+}
+
+int vnt_generate_fifo_header(struct vnt_private *priv, u32 dma_idx,
+ PSTxDesc head_td, struct sk_buff *skb)
+{
+ PDEVICE_TD_INFO td_info = head_td->pTDInfo;
+ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+ struct ieee80211_tx_rate *tx_rate = &info->control.rates[0];
+ struct ieee80211_rate *rate;
+ struct ieee80211_key_conf *tx_key;
+ struct ieee80211_hdr *hdr;
+ struct vnt_tx_fifo_head *tx_buffer_head =
+ (struct vnt_tx_fifo_head *)td_info->buf;
+ u16 tx_body_size = skb->len, current_rate;
+ u8 pkt_type;
+ bool is_pspoll = false;
+
+ memset(tx_buffer_head, 0, sizeof(*tx_buffer_head));
+
+ hdr = (struct ieee80211_hdr *)(skb->data);
+
+ rate = ieee80211_get_tx_rate(priv->hw, info);
+
+ current_rate = rate->hw_value;
+ if (priv->wCurrentRate != current_rate &&
+ !(priv->hw->conf.flags & IEEE80211_CONF_OFFCHANNEL)) {
+ priv->wCurrentRate = current_rate;
+
+ RFbSetPower(priv, priv->wCurrentRate,
+ priv->hw->conf.chandef.chan->hw_value);
+ }
+
+ if (current_rate > RATE_11M) {
+ if (info->band == IEEE80211_BAND_5GHZ) {
+ pkt_type = PK_TYPE_11A;
+ } else {
+ if (tx_rate->flags & IEEE80211_TX_RC_USE_CTS_PROTECT)
+ pkt_type = PK_TYPE_11GB;
+ else
+ pkt_type = PK_TYPE_11GA;
+ }
+ } else {
+ pkt_type = PK_TYPE_11B;
+ }
+
+ /*Set fifo controls */
+ if (pkt_type == PK_TYPE_11A)
+ tx_buffer_head->fifo_ctl = 0;
+ else if (pkt_type == PK_TYPE_11B)
+ tx_buffer_head->fifo_ctl = cpu_to_le16(FIFOCTL_11B);
+ else if (pkt_type == PK_TYPE_11GB)
+ tx_buffer_head->fifo_ctl = cpu_to_le16(FIFOCTL_11GB);
+ else if (pkt_type == PK_TYPE_11GA)
+ tx_buffer_head->fifo_ctl = cpu_to_le16(FIFOCTL_11GA);
+
+ /* generate interrupt */
+ tx_buffer_head->fifo_ctl |= cpu_to_le16(FIFOCTL_GENINT);
+
+ if (!ieee80211_is_data(hdr->frame_control)) {
+ tx_buffer_head->fifo_ctl |= cpu_to_le16(FIFOCTL_TMOEN);
+ tx_buffer_head->fifo_ctl |= cpu_to_le16(FIFOCTL_ISDMA0);
+ tx_buffer_head->time_stamp =
+ cpu_to_le16(DEFAULT_MGN_LIFETIME_RES_64us);
+ } else {
+ tx_buffer_head->time_stamp =
+ cpu_to_le16(DEFAULT_MSDU_LIFETIME_RES_64us);
+ }
+
+ if (!(info->flags & IEEE80211_TX_CTL_NO_ACK))
+ tx_buffer_head->fifo_ctl |= cpu_to_le16(FIFOCTL_NEEDACK);
+
+ if (ieee80211_has_retry(hdr->frame_control))
+ tx_buffer_head->fifo_ctl |= cpu_to_le16(FIFOCTL_LRETRY);
+
+ if (tx_rate->flags & IEEE80211_TX_RC_USE_SHORT_PREAMBLE)
+ priv->byPreambleType = PREAMBLE_SHORT;
+ else
+ priv->byPreambleType = PREAMBLE_LONG;
+
+ if (tx_rate->flags & IEEE80211_TX_RC_USE_RTS_CTS)
+ tx_buffer_head->fifo_ctl |= cpu_to_le16(FIFOCTL_RTS);
+
+ if (ieee80211_has_a4(hdr->frame_control)) {
+ tx_buffer_head->fifo_ctl |= cpu_to_le16(FIFOCTL_LHEAD);
+ priv->bLongHeader = true;
+ }
+
+ if (info->flags & IEEE80211_TX_CTL_NO_PS_BUFFER)
+ is_pspoll = true;
+
+ tx_buffer_head->frag_ctl =
+ cpu_to_le16(ieee80211_get_hdrlen_from_skb(skb) << 10);
+
+ if (info->control.hw_key) {
+ tx_key = info->control.hw_key;
+
+ switch (info->control.hw_key->cipher) {
+ case WLAN_CIPHER_SUITE_WEP40:
+ case WLAN_CIPHER_SUITE_WEP104:
+ tx_buffer_head->frag_ctl |= cpu_to_le16(FRAGCTL_LEGACY);
+ break;
+ case WLAN_CIPHER_SUITE_TKIP:
+ tx_buffer_head->frag_ctl |= cpu_to_le16(FRAGCTL_TKIP);
+ break;
+ case WLAN_CIPHER_SUITE_CCMP:
+ tx_buffer_head->frag_ctl |= cpu_to_le16(FRAGCTL_AES);
+ default:
+ break;
+ }
+ }
+
+ tx_buffer_head->current_rate = cpu_to_le16(current_rate);
+
+ /* legacy rates TODO use ieee80211_tx_rate */
+ if (current_rate >= RATE_18M && ieee80211_is_data(hdr->frame_control)) {
+ if (priv->byAutoFBCtrl == AUTO_FB_0)
+ tx_buffer_head->fifo_ctl |=
+ cpu_to_le16(FIFOCTL_AUTO_FB_0);
+ else if (priv->byAutoFBCtrl == AUTO_FB_1)
+ tx_buffer_head->fifo_ctl |=
+ cpu_to_le16(FIFOCTL_AUTO_FB_1);
+
+ }
+
+ tx_buffer_head->frag_ctl |= cpu_to_le16(FRAGCTL_NONFRAG);
+
+ s_cbFillTxBufHead(priv, pkt_type, (u8 *)tx_buffer_head,
+ dma_idx, head_td, is_pspoll);
+
+ if (info->control.hw_key) {
+ tx_key = info->control.hw_key;
+ if (tx_key->keylen > 0)
+ vnt_fill_txkey(hdr, tx_buffer_head->tx_key,
+ tx_key, skb, tx_body_size, td_info->mic_hdr);
+ }
+
+ return 0;
+}
+
+static int vnt_beacon_xmit(struct vnt_private *priv,
+ struct sk_buff *skb)
+{
+ struct vnt_tx_short_buf_head *short_head =
+ (struct vnt_tx_short_buf_head *)priv->tx_beacon_bufs;
+ struct ieee80211_mgmt *mgmt_hdr = (struct ieee80211_mgmt *)
+ (priv->tx_beacon_bufs + sizeof(*short_head));
+ struct ieee80211_tx_info *info;
+ u32 frame_size = skb->len + 4;
+ u16 current_rate;
+
+ memset(priv->tx_beacon_bufs, 0, sizeof(*short_head));
+
+ if (priv->byBBType == BB_TYPE_11A) {
+ current_rate = RATE_6M;
+
+ /* Get SignalField,ServiceField,Length */
+ vnt_get_phy_field(priv, frame_size, current_rate,
+ PK_TYPE_11A, &short_head->ab);
+
+ /* Get Duration and TimeStampOff */
+ short_head->duration =
+ cpu_to_le16((u16)s_uGetDataDuration(priv, DATADUR_B,
+ frame_size, PK_TYPE_11A, current_rate,
+ false, 0, 0, 1, AUTO_FB_NONE));
+
+ short_head->time_stamp_off =
+ vnt_time_stamp_off(priv, current_rate);
+ } else {
+ current_rate = RATE_1M;
+ short_head->fifo_ctl |= cpu_to_le16(FIFOCTL_11B);
+
+ /* Get SignalField,ServiceField,Length */
+ vnt_get_phy_field(priv, frame_size, current_rate,
+ PK_TYPE_11B, &short_head->ab);
+
+ /* Get Duration and TimeStampOff */
+ short_head->duration =
+ cpu_to_le16((u16)s_uGetDataDuration(priv, DATADUR_B,
+ frame_size, PK_TYPE_11B, current_rate,
+ false, 0, 0, 1, AUTO_FB_NONE));
+
+ short_head->time_stamp_off =
+ vnt_time_stamp_off(priv, current_rate);
+ }
+
+ short_head->fifo_ctl |= cpu_to_le16(FIFOCTL_GENINT);
+
+ /* Copy Beacon */
+ memcpy(mgmt_hdr, skb->data, skb->len);
+
+ /* time stamp always 0 */
+ mgmt_hdr->u.beacon.timestamp = 0;
+
+ info = IEEE80211_SKB_CB(skb);
+ if (info->flags & IEEE80211_TX_CTL_ASSIGN_SEQ) {
+ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)mgmt_hdr;
+
+ hdr->duration_id = 0;
+ hdr->seq_ctrl = cpu_to_le16(priv->wSeqCounter << 4);
+ }
+
+ priv->wSeqCounter++;
+ if (priv->wSeqCounter > 0x0fff)
+ priv->wSeqCounter = 0;
+
+ priv->wBCNBufLen = sizeof(*short_head) + skb->len;
+
+ MACvSetCurrBCNTxDescAddr(priv->PortOffset, priv->tx_beacon_dma);
+
+ MACvSetCurrBCNLength(priv->PortOffset, priv->wBCNBufLen);
+ /* Set auto Transmit on */
+ MACvRegBitsOn(priv->PortOffset, MAC_REG_TCR, TCR_AUTOBCNTX);
+ /* Poll Transmit the adapter */
+ MACvTransmitBCN(priv->PortOffset);
+
+ return 0;
+}
+
+int vnt_beacon_make(struct vnt_private *priv, struct ieee80211_vif *vif)
+{
+ struct sk_buff *beacon;
+
+ beacon = ieee80211_beacon_get(priv->hw, vif);
+ if (!beacon)
+ return -ENOMEM;
+
+ if (vnt_beacon_xmit(priv, beacon)) {
+ ieee80211_free_txskb(priv->hw, beacon);
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+int vnt_beacon_enable(struct vnt_private *priv, struct ieee80211_vif *vif,
+ struct ieee80211_bss_conf *conf)
+{
+ VNSvOutPortB(priv->PortOffset + MAC_REG_TFTCTL, TFTCTL_TSFCNTRST);
+
+ VNSvOutPortB(priv->PortOffset + MAC_REG_TFTCTL, TFTCTL_TSFCNTREN);
+
+ CARDvSetFirstNextTBTT(priv, conf->beacon_int);
+
+ CARDbSetBeaconPeriod(priv, conf->beacon_int);
+
+ return vnt_beacon_make(priv, vif);
+}
diff --git a/drivers/staging/vt6655/rxtx.h b/drivers/staging/vt6655/rxtx.h
new file mode 100644
index 000000000..b9bd1639b
--- /dev/null
+++ b/drivers/staging/vt6655/rxtx.h
@@ -0,0 +1,200 @@
+/*
+ * Copyright (c) 1996, 2003 VIA Networking Technologies, Inc.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * File: rxtx.h
+ *
+ * Purpose:
+ *
+ * Author: Jerry Chen
+ *
+ * Date: Jun. 27, 2002
+ *
+ */
+
+#ifndef __RXTX_H__
+#define __RXTX_H__
+
+#include "device.h"
+
+#define DEFAULT_MSDU_LIFETIME_RES_64us 8000 /* 64us */
+#define DEFAULT_MGN_LIFETIME_RES_64us 125 /* 64us */
+
+
+/*--------------------- Export Definitions -------------------------*/
+
+/*--------------------- Export Variables --------------------------*/
+
+/*--------------------- Export Functions --------------------------*/
+
+/* MIC HDR data header */
+struct vnt_mic_hdr {
+ u8 id;
+ u8 tx_priority;
+ u8 mic_addr2[ETH_ALEN];
+ u8 ccmp_pn[IEEE80211_CCMP_PN_LEN];
+ __be16 payload_len;
+ __be16 hlen;
+ __le16 frame_control;
+ u8 addr1[ETH_ALEN];
+ u8 addr2[ETH_ALEN];
+ u8 addr3[ETH_ALEN];
+ __le16 seq_ctrl;
+ u8 addr4[ETH_ALEN];
+ u16 packing; /* packing to 48 bytes */
+} __packed;
+
+/* RsvTime buffer header */
+struct vnt_rrv_time_rts {
+ __le16 rts_rrv_time_ba;
+ __le16 rts_rrv_time_aa;
+ __le16 rts_rrv_time_bb;
+ u16 reserved;
+ __le16 rrv_time_b;
+ __le16 rrv_time_a;
+} __packed;
+
+struct vnt_rrv_time_cts {
+ __le16 cts_rrv_time_ba;
+ u16 reserved;
+ __le16 rrv_time_b;
+ __le16 rrv_time_a;
+} __packed;
+
+struct vnt_rrv_time_ab {
+ __le16 rts_rrv_time;
+ __le16 rrv_time;
+} __packed;
+
+/* TX data header */
+struct vnt_tx_datahead_g {
+ struct vnt_phy_field b;
+ struct vnt_phy_field a;
+ __le16 duration_b;
+ __le16 duration_a;
+ __le16 time_stamp_off_b;
+ __le16 time_stamp_off_a;
+} __packed;
+
+struct vnt_tx_datahead_g_fb {
+ struct vnt_phy_field b;
+ struct vnt_phy_field a;
+ __le16 duration_b;
+ __le16 duration_a;
+ __le16 duration_a_f0;
+ __le16 duration_a_f1;
+ __le16 time_stamp_off_b;
+ __le16 time_stamp_off_a;
+} __packed;
+
+struct vnt_tx_datahead_ab {
+ struct vnt_phy_field ab;
+ __le16 duration;
+ __le16 time_stamp_off;
+} __packed;
+
+struct vnt_tx_datahead_a_fb {
+ struct vnt_phy_field a;
+ __le16 duration;
+ __le16 time_stamp_off;
+ __le16 duration_f0;
+ __le16 duration_f1;
+} __packed;
+
+/* RTS buffer header */
+struct vnt_rts_g {
+ struct vnt_phy_field b;
+ struct vnt_phy_field a;
+ __le16 duration_ba;
+ __le16 duration_aa;
+ __le16 duration_bb;
+ u16 reserved;
+ struct ieee80211_rts data;
+} __packed;
+
+struct vnt_rts_g_fb {
+ struct vnt_phy_field b;
+ struct vnt_phy_field a;
+ __le16 duration_ba;
+ __le16 duration_aa;
+ __le16 duration_bb;
+ u16 wReserved;
+ __le16 rts_duration_ba_f0;
+ __le16 rts_duration_aa_f0;
+ __le16 rts_duration_ba_f1;
+ __le16 rts_duration_aa_f1;
+ struct ieee80211_rts data;
+} __packed;
+
+struct vnt_rts_ab {
+ struct vnt_phy_field ab;
+ __le16 duration;
+ u16 reserved;
+ struct ieee80211_rts data;
+} __packed;
+
+struct vnt_rts_a_fb {
+ struct vnt_phy_field a;
+ __le16 duration;
+ u16 reserved;
+ __le16 rts_duration_f0;
+ __le16 rts_duration_f1;
+ struct ieee80211_rts data;
+} __packed;
+
+/* CTS buffer header */
+struct vnt_cts {
+ struct vnt_phy_field b;
+ __le16 duration_ba;
+ u16 reserved;
+ struct ieee80211_cts data;
+ u16 reserved2;
+} __packed;
+
+struct vnt_cts_fb {
+ struct vnt_phy_field b;
+ __le16 duration_ba;
+ u16 reserved;
+ __le16 cts_duration_ba_f0;
+ __le16 cts_duration_ba_f1;
+ struct ieee80211_cts data;
+ u16 reserved2;
+} __packed;
+
+struct vnt_tx_fifo_head {
+ u8 tx_key[WLAN_KEY_LEN_CCMP];
+ __le16 fifo_ctl;
+ __le16 time_stamp;
+ __le16 frag_ctl;
+ __le16 current_rate;
+} __packed;
+
+struct vnt_tx_short_buf_head {
+ __le16 fifo_ctl;
+ u16 time_stamp;
+ struct vnt_phy_field ab;
+ __le16 duration;
+ __le16 time_stamp_off;
+} __packed;
+
+int vnt_generate_fifo_header(struct vnt_private *, u32,
+ PSTxDesc head_td, struct sk_buff *);
+int vnt_beacon_make(struct vnt_private *, struct ieee80211_vif *);
+int vnt_beacon_enable(struct vnt_private *, struct ieee80211_vif *,
+ struct ieee80211_bss_conf *);
+
+#endif // __RXTX_H__
diff --git a/drivers/staging/vt6655/srom.c b/drivers/staging/vt6655/srom.c
new file mode 100644
index 000000000..9ec49e653
--- /dev/null
+++ b/drivers/staging/vt6655/srom.c
@@ -0,0 +1,153 @@
+/*
+ * Copyright (c) 1996, 2003 VIA Networking Technologies, Inc.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * File: srom.c
+ *
+ * Purpose:Implement functions to access eeprom
+ *
+ * Author: Jerry Chen
+ *
+ * Date: Jan 29, 2003
+ *
+ * Functions:
+ * SROMbyReadEmbedded - Embedded read eeprom via MAC
+ * SROMbWriteEmbedded - Embedded write eeprom via MAC
+ * SROMvRegBitsOn - Set Bits On in eeprom
+ * SROMvRegBitsOff - Clear Bits Off in eeprom
+ * SROMbIsRegBitsOn - Test if Bits On in eeprom
+ * SROMbIsRegBitsOff - Test if Bits Off in eeprom
+ * SROMvReadAllContents - Read all contents in eeprom
+ * SROMvWriteAllContents - Write all contents in eeprom
+ * SROMvReadEtherAddress - Read Ethernet Address in eeprom
+ * SROMvWriteEtherAddress - Write Ethernet Address in eeprom
+ * SROMvReadSubSysVenId - Read Sub_VID and Sub_SysId in eeprom
+ * SROMbAutoLoad - Auto Load eeprom to MAC register
+ *
+ * Revision History:
+ *
+ */
+
+#include "upc.h"
+#include "tmacro.h"
+#include "mac.h"
+#include "srom.h"
+
+/*--------------------- Static Definitions -------------------------*/
+
+/*--------------------- Static Classes ----------------------------*/
+
+/*--------------------- Static Variables --------------------------*/
+
+/*--------------------- Static Functions --------------------------*/
+
+/*--------------------- Export Variables --------------------------*/
+
+/*--------------------- Export Functions --------------------------*/
+
+/*
+ * Description: Read a byte from EEPROM, by MAC I2C
+ *
+ * Parameters:
+ * In:
+ * dwIoBase - I/O base address
+ * byContntOffset - address of EEPROM
+ * Out:
+ * none
+ *
+ * Return Value: data read
+ *
+ */
+unsigned char SROMbyReadEmbedded(void __iomem *dwIoBase, unsigned char byContntOffset)
+{
+ unsigned short wDelay, wNoACK;
+ unsigned char byWait;
+ unsigned char byData;
+ unsigned char byOrg;
+
+ byData = 0xFF;
+ VNSvInPortB(dwIoBase + MAC_REG_I2MCFG, &byOrg);
+ /* turn off hardware retry for getting NACK */
+ VNSvOutPortB(dwIoBase + MAC_REG_I2MCFG, (byOrg & (~I2MCFG_NORETRY)));
+ for (wNoACK = 0; wNoACK < W_MAX_I2CRETRY; wNoACK++) {
+ VNSvOutPortB(dwIoBase + MAC_REG_I2MTGID, EEP_I2C_DEV_ID);
+ VNSvOutPortB(dwIoBase + MAC_REG_I2MTGAD, byContntOffset);
+
+ /* issue read command */
+ VNSvOutPortB(dwIoBase + MAC_REG_I2MCSR, I2MCSR_EEMR);
+ /* wait DONE be set */
+ for (wDelay = 0; wDelay < W_MAX_TIMEOUT; wDelay++) {
+ VNSvInPortB(dwIoBase + MAC_REG_I2MCSR, &byWait);
+ if (byWait & (I2MCSR_DONE | I2MCSR_NACK))
+ break;
+ PCAvDelayByIO(CB_DELAY_LOOP_WAIT);
+ }
+ if ((wDelay < W_MAX_TIMEOUT) &&
+ (!(byWait & I2MCSR_NACK))) {
+ break;
+ }
+ }
+ VNSvInPortB(dwIoBase + MAC_REG_I2MDIPT, &byData);
+ VNSvOutPortB(dwIoBase + MAC_REG_I2MCFG, byOrg);
+ return byData;
+}
+
+/*
+ * Description: Read all contents of eeprom to buffer
+ *
+ * Parameters:
+ * In:
+ * dwIoBase - I/O base address
+ * Out:
+ * pbyEepromRegs - EEPROM content Buffer
+ *
+ * Return Value: none
+ *
+ */
+void SROMvReadAllContents(void __iomem *dwIoBase, unsigned char *pbyEepromRegs)
+{
+ int ii;
+
+ /* ii = Rom Address */
+ for (ii = 0; ii < EEP_MAX_CONTEXT_SIZE; ii++) {
+ *pbyEepromRegs = SROMbyReadEmbedded(dwIoBase, (unsigned char)ii);
+ pbyEepromRegs++;
+ }
+}
+
+/*
+ * Description: Read Ethernet Address from eeprom to buffer
+ *
+ * Parameters:
+ * In:
+ * dwIoBase - I/O base address
+ * Out:
+ * pbyEtherAddress - Ethernet Address buffer
+ *
+ * Return Value: none
+ *
+ */
+void SROMvReadEtherAddress(void __iomem *dwIoBase, unsigned char *pbyEtherAddress)
+{
+ unsigned char ii;
+
+ /* ii = Rom Address */
+ for (ii = 0; ii < ETH_ALEN; ii++) {
+ *pbyEtherAddress = SROMbyReadEmbedded(dwIoBase, ii);
+ pbyEtherAddress++;
+ }
+}
diff --git a/drivers/staging/vt6655/srom.h b/drivers/staging/vt6655/srom.h
new file mode 100644
index 000000000..531bf0069
--- /dev/null
+++ b/drivers/staging/vt6655/srom.h
@@ -0,0 +1,101 @@
+/*
+ * Copyright (c) 1996, 2003 VIA Networking Technologies, Inc.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ *
+ * File: srom.h
+ *
+ * Purpose: Implement functions to access eeprom
+ *
+ * Author: Jerry Chen
+ *
+ * Date: Jan 29, 2003
+ */
+
+#ifndef __SROM_H__
+#define __SROM_H__
+
+/*--------------------- Export Definitions -------------------------*/
+
+#define EEP_MAX_CONTEXT_SIZE 256
+
+#define CB_EEPROM_READBYTE_WAIT 900 /* us */
+
+#define W_MAX_I2CRETRY 0x0fff
+
+/* Contents in the EEPROM */
+#define EEP_OFS_PAR 0x00 /* physical address */
+#define EEP_OFS_ANTENNA 0x16
+#define EEP_OFS_RADIOCTL 0x17
+#define EEP_OFS_RFTYPE 0x1B /* for select RF */
+#define EEP_OFS_MINCHANNEL 0x1C /* Min Channel # */
+#define EEP_OFS_MAXCHANNEL 0x1D /* Max Channel # */
+#define EEP_OFS_SIGNATURE 0x1E
+#define EEP_OFS_ZONETYPE 0x1F
+#define EEP_OFS_RFTABLE 0x20 /* RF POWER TABLE */
+#define EEP_OFS_PWR_CCK 0x20
+#define EEP_OFS_SETPT_CCK 0x21
+#define EEP_OFS_PWR_OFDMG 0x23
+#define EEP_OFS_SETPT_OFDMG 0x24
+#define EEP_OFS_PWR_FORMULA_OST 0x26
+#define EEP_OFS_MAJOR_VER 0x2E
+#define EEP_OFS_MINOR_VER 0x2F
+#define EEP_OFS_CCK_PWR_TBL 0x30
+#define EEP_OFS_CCK_PWR_dBm 0x3F
+#define EEP_OFS_OFDM_PWR_TBL 0x40
+#define EEP_OFS_OFDM_PWR_dBm 0x4F
+/*{{ RobertYu: 20041124 */
+#define EEP_OFS_SETPT_OFDMA 0x4E
+#define EEP_OFS_OFDMA_PWR_TBL 0x50
+/*}}*/
+#define EEP_OFS_OFDMA_PWR_dBm 0xD2
+
+/*----------need to remove --------------------*/
+#define EEP_OFS_BBTAB_LEN 0x70 /* BB Table Length */
+#define EEP_OFS_BBTAB_ADR 0x71 /* BB Table Offset */
+#define EEP_OFS_CHECKSUM 0xFF /* reserved area for baseband 28h~78h */
+
+#define EEP_I2C_DEV_ID 0x50 /* EEPROM device address on I2C bus */
+
+/* Bits in EEP_OFS_ANTENNA */
+#define EEP_ANTENNA_MAIN 0x01
+#define EEP_ANTENNA_AUX 0x02
+#define EEP_ANTINV 0x04
+
+/* Bits in EEP_OFS_RADIOCTL */
+#define EEP_RADIOCTL_ENABLE 0x80
+#define EEP_RADIOCTL_INV 0x01
+
+/*--------------------- Export Types ------------------------------*/
+
+/*--------------------- Export Macros ------------------------------*/
+
+/*--------------------- Export Classes ----------------------------*/
+
+/*--------------------- Export Variables --------------------------*/
+
+/*--------------------- Export Functions --------------------------*/
+
+unsigned char SROMbyReadEmbedded(void __iomem *dwIoBase,
+ unsigned char byContntOffset);
+
+void SROMvReadAllContents(void __iomem *dwIoBase, unsigned char *pbyEepromRegs);
+
+void SROMvReadEtherAddress(void __iomem *dwIoBase,
+ unsigned char *pbyEtherAddress);
+
+#endif /* __EEPROM_H__*/
diff --git a/drivers/staging/vt6655/test b/drivers/staging/vt6655/test
new file mode 100644
index 000000000..039f7d71c
--- /dev/null
+++ b/drivers/staging/vt6655/test
@@ -0,0 +1,9 @@
+KSP := /lib/modules/$(shell uname -r)/build \
+ /usr/src/linux-$(shell uname -r) \
+ /usr/src/linux-$(shell uname -r | sed 's/-.*//') \
+# /usr/src/kernel-headers-$(shell uname -r) \
+# /usr/src/kernel-source-$(shell uname -r) \
+# /usr/src/linux-$(shell uname -r | sed 's/\([0-9]*\.[0-9]*\)\..*/\1/') \
+# /usr/src/linux /home/plice
+test_dir = $(shell [ -e $(dir)/include/linux ] && echo $(dir))
+KSP := $(foreach dir, $(KSP), $(test_dir)) \ No newline at end of file
diff --git a/drivers/staging/vt6655/tmacro.h b/drivers/staging/vt6655/tmacro.h
new file mode 100644
index 000000000..597efefc0
--- /dev/null
+++ b/drivers/staging/vt6655/tmacro.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 1996, 2003 VIA Networking Technologies, Inc.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * File: tmacro.h
+ *
+ * Purpose: define basic common types and macros
+ *
+ * Author: Tevin Chen
+ *
+ * Date: May 21, 1996
+ *
+ */
+
+#ifndef __TMACRO_H__
+#define __TMACRO_H__
+
+/****** Common helper macros ***********************************************/
+
+#if !defined(LOBYTE)
+#define LOBYTE(w) ((unsigned char)(w))
+#endif
+#if !defined(HIBYTE)
+#define HIBYTE(w) ((unsigned char)(((unsigned short)(w) >> 8) & 0xFF))
+#endif
+
+#if !defined(LOWORD)
+#define LOWORD(d) ((unsigned short)(d))
+#endif
+#if !defined(HIWORD)
+#define HIWORD(d) ((unsigned short)((((unsigned long)(d)) >> 16) & 0xFFFF))
+#endif
+
+#define LODWORD(q) ((q).u.dwLowDword)
+#define HIDWORD(q) ((q).u.dwHighDword)
+
+#if !defined(MAKEWORD)
+#define MAKEWORD(lb, hb) ((unsigned short)(((unsigned char)(lb)) | (((unsigned short)((unsigned char)(hb))) << 8)))
+#endif
+#if !defined(MAKEDWORD)
+#define MAKEDWORD(lw, hw) ((unsigned long)(((unsigned short)(lw)) | (((unsigned long)((unsigned short)(hw))) << 16)))
+#endif
+
+#endif /* __TMACRO_H__ */
diff --git a/drivers/staging/vt6655/upc.h b/drivers/staging/vt6655/upc.h
new file mode 100644
index 000000000..cc63dc8d4
--- /dev/null
+++ b/drivers/staging/vt6655/upc.h
@@ -0,0 +1,89 @@
+/*
+ * Copyright (c) 1996, 2003 VIA Networking Technologies, Inc.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * File: upc.h
+ *
+ * Purpose: Macros to access device
+ *
+ * Author: Tevin Chen
+ *
+ * Date: Mar 17, 1997
+ *
+ */
+
+#ifndef __UPC_H__
+#define __UPC_H__
+
+#include "device.h"
+
+/*--------------------- Export Definitions -------------------------*/
+
+
+/* For memory mapped IO */
+
+
+#define VNSvInPortB(dwIOAddress, pbyData) \
+do { \
+ *(pbyData) = ioread8(dwIOAddress); \
+} while (0)
+
+#define VNSvInPortW(dwIOAddress, pwData) \
+do { \
+ *(pwData) = ioread16(dwIOAddress); \
+} while (0)
+
+#define VNSvInPortD(dwIOAddress, pdwData) \
+do { \
+ *(pdwData) = ioread32(dwIOAddress); \
+} while (0)
+
+#define VNSvOutPortB(dwIOAddress, byData) \
+do { \
+ iowrite8((u8)byData, dwIOAddress); \
+} while (0)
+
+#define VNSvOutPortW(dwIOAddress, wData) \
+do { \
+ iowrite16((u16)wData, dwIOAddress); \
+} while (0)
+
+#define VNSvOutPortD(dwIOAddress, dwData) \
+do { \
+ iowrite32((u32)dwData, dwIOAddress); \
+} while (0)
+
+#define PCAvDelayByIO(uDelayUnit) \
+do { \
+ unsigned char byData; \
+ unsigned long ii; \
+ \
+ if (uDelayUnit <= 50) { \
+ udelay(uDelayUnit); \
+ } else { \
+ for (ii = 0; ii < (uDelayUnit); ii++) \
+ byData = inb(0x61); \
+ } \
+} while (0)
+
+/*--------------------- Export Classes ----------------------------*/
+
+/*--------------------- Export Variables --------------------------*/
+
+/*--------------------- Export Functions --------------------------*/
+
+#endif /* __UPC_H__ */
diff --git a/drivers/staging/vt6656/Kconfig b/drivers/staging/vt6656/Kconfig
new file mode 100644
index 000000000..b602ef175
--- /dev/null
+++ b/drivers/staging/vt6656/Kconfig
@@ -0,0 +1,7 @@
+config VT6656
+ tristate "VIA Technologies VT6656 support"
+ depends on MAC80211 && USB && WLAN && m
+ select FW_LOADER
+ ---help---
+ This is a vendor-written driver for VIA VT6656.
+
diff --git a/drivers/staging/vt6656/Makefile b/drivers/staging/vt6656/Makefile
new file mode 100644
index 000000000..3dbe1f89d
--- /dev/null
+++ b/drivers/staging/vt6656/Makefile
@@ -0,0 +1,20 @@
+# TODO: all of these should be removed
+ccflags-y := -DLINUX -D__KERNEL__ -DEXPORT_SYMTAB -D__NO_VERSION__
+ccflags-y += -DHOSTAP
+
+vt6656_stage-y += main_usb.o \
+ card.o \
+ mac.o \
+ baseband.o \
+ wcmd.o\
+ rxtx.o \
+ dpc.o \
+ power.o \
+ key.o \
+ rf.o \
+ usbpipe.o \
+ channel.o \
+ firmware.o \
+ int.o
+
+obj-$(CONFIG_VT6656) += vt6656_stage.o
diff --git a/drivers/staging/vt6656/TODO b/drivers/staging/vt6656/TODO
new file mode 100644
index 000000000..e154b2f3b
--- /dev/null
+++ b/drivers/staging/vt6656/TODO
@@ -0,0 +1,19 @@
+TODO:
+- remove __cplusplus ifdefs -- done
+- remove kernel version compatibility wrappers
+- remove support for older wireless extensions
+- prepare for merge with vt6655 driver:
+ - remove PRINT_K() macro
+ - split rf.c
+ - abstract VT3184 chipset specific code
+- add common vt665x infrastructure
+- kill ttype.h -- done
+- switch to use LIB80211
+- switch to use MAC80211
+- use kernel coding style
+- checkpatch.pl fixes
+- sparse fixes
+- integrate with drivers/net/wireless
+
+Please send any patches to Greg Kroah-Hartman <greg@kroah.com>
+and Forest Bond <forest@alittletooquiet.net>.
diff --git a/drivers/staging/vt6656/baseband.c b/drivers/staging/vt6656/baseband.c
new file mode 100644
index 000000000..26b16772f
--- /dev/null
+++ b/drivers/staging/vt6656/baseband.c
@@ -0,0 +1,831 @@
+/*
+ * Copyright (c) 1996, 2003 VIA Networking Technologies, Inc.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ *
+ * File: baseband.c
+ *
+ * Purpose: Implement functions to access baseband
+ *
+ * Author: Jerry Chen
+ *
+ * Date: Jun. 5, 2002
+ *
+ * Functions:
+ * vnt_get_frame_time - Calculate data frame transmitting time
+ * vnt_get_phy_field - Calculate PhyLength, PhyService and Phy
+ * Signal parameter for baseband Tx
+ * vnt_vt3184_init - VIA VT3184 baseband chip init code
+ *
+ * Revision History:
+ *
+ *
+ */
+
+#include "mac.h"
+#include "baseband.h"
+#include "rf.h"
+#include "usbpipe.h"
+
+static u8 vnt_vt3184_agc[] = {
+ 0x00, 0x00, 0x02, 0x02, 0x04, 0x04, 0x06, 0x06,
+ 0x08, 0x08, 0x0a, 0x0a, 0x0c, 0x0c, 0x0e, 0x0e, /* 0x0f */
+ 0x10, 0x10, 0x12, 0x12, 0x14, 0x14, 0x16, 0x16,
+ 0x18, 0x18, 0x1a, 0x1a, 0x1c, 0x1c, 0x1e, 0x1e, /* 0x1f */
+ 0x20, 0x20, 0x22, 0x22, 0x24, 0x24, 0x26, 0x26,
+ 0x28, 0x28, 0x2a, 0x2a, 0x2c, 0x2c, 0x2e, 0x2e, /* 0x2f */
+ 0x30, 0x30, 0x32, 0x32, 0x34, 0x34, 0x36, 0x36,
+ 0x38, 0x38, 0x3a, 0x3a, 0x3c, 0x3c, 0x3e, 0x3e /* 0x3f */
+};
+
+static u8 vnt_vt3184_al2230[] = {
+ 0x31, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00,
+ 0x70, 0x45, 0x2a, 0x76, 0x00, 0x00, 0x80, 0x00, /* 0x0f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x8e, 0x0a, 0x00, 0x00, 0x00, /* 0x1f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x4a, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x4a, 0x00, 0x0c, /* 0x2f */
+ 0x26, 0x5b, 0x00, 0x00, 0x00, 0x00, 0xaa, 0xaa,
+ 0xff, 0xff, 0x79, 0x00, 0x00, 0x0b, 0x48, 0x04, /* 0x3f */
+ 0x00, 0x08, 0x00, 0x08, 0x08, 0x14, 0x05, 0x09,
+ 0x00, 0x00, 0x00, 0x00, 0x09, 0x73, 0x00, 0xc5, /* 0x4f */
+ 0x00, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0xd0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x5f */
+ 0xe4, 0x80, 0x00, 0x00, 0x00, 0x00, 0x98, 0x0a,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x01, 0x00, /* 0x6f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x7f */
+ 0x8c, 0x01, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x08, 0x00, 0x1f, 0xb7, 0x88, 0x47, 0xaa, 0x00, /* 0x8f */
+ 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xeb,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, /* 0x9f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00,
+ 0x18, 0x00, 0x00, 0x00, 0x00, 0x15, 0x00, 0x18, /* 0xaf */
+ 0x38, 0x30, 0x00, 0x00, 0xff, 0x0f, 0xe4, 0xe2,
+ 0x00, 0x00, 0x00, 0x03, 0x01, 0x00, 0x00, 0x00, /* 0xbf */
+ 0x18, 0x20, 0x07, 0x18, 0xff, 0xff, 0x0e, 0x0a,
+ 0x0e, 0x00, 0x82, 0xa7, 0x3c, 0x10, 0x30, 0x05, /* 0xcf */
+ 0x40, 0x12, 0x00, 0x00, 0x10, 0x28, 0x80, 0x2a,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xdf */
+ 0x00, 0xf3, 0x00, 0x00, 0x00, 0x10, 0x00, 0x12,
+ 0x00, 0xf4, 0x00, 0xff, 0x79, 0x20, 0x30, 0x05, /* 0xef */
+ 0x00, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 /* 0xff */
+};
+
+/* {{RobertYu:20060515, new BB setting for VT3226D0 */
+static u8 vnt_vt3184_vt3226d0[] = {
+ 0x31, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00,
+ 0x70, 0x45, 0x2a, 0x76, 0x00, 0x00, 0x80, 0x00, /* 0x0f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x8e, 0x0a, 0x00, 0x00, 0x00, /* 0x1f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x4a, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x4a, 0x00, 0x0c, /* 0x2f */
+ 0x26, 0x5b, 0x00, 0x00, 0x00, 0x00, 0xaa, 0xaa,
+ 0xff, 0xff, 0x79, 0x00, 0x00, 0x0b, 0x48, 0x04, /* 0x3f */
+ 0x00, 0x08, 0x00, 0x08, 0x08, 0x14, 0x05, 0x09,
+ 0x00, 0x00, 0x00, 0x00, 0x09, 0x73, 0x00, 0xc5, /* 0x4f */
+ 0x00, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0xd0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x5f */
+ 0xe4, 0x80, 0x00, 0x00, 0x00, 0x00, 0x98, 0x0a,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x01, 0x00, /* 0x6f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x7f */
+ 0x8c, 0x01, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x08, 0x00, 0x1f, 0xb7, 0x88, 0x47, 0xaa, 0x00, /* 0x8f */
+ 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xeb,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, /* 0x9f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00,
+ 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, /* 0xaf */
+ 0x38, 0x30, 0x00, 0x00, 0xff, 0x0f, 0xe4, 0xe2,
+ 0x00, 0x00, 0x00, 0x03, 0x01, 0x00, 0x00, 0x00, /* 0xbf */
+ 0x18, 0x20, 0x07, 0x18, 0xff, 0xff, 0x10, 0x0a,
+ 0x0e, 0x00, 0x84, 0xa7, 0x3c, 0x10, 0x24, 0x05, /* 0xcf */
+ 0x40, 0x12, 0x00, 0x00, 0x10, 0x28, 0x80, 0x2a,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xdf */
+ 0x00, 0xf3, 0x00, 0x00, 0x00, 0x10, 0x00, 0x10,
+ 0x00, 0xf4, 0x00, 0xff, 0x79, 0x20, 0x30, 0x08, /* 0xef */
+ 0x00, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 /* 0xff */
+};
+
+static const u16 vnt_frame_time[MAX_RATE] = {
+ 10, 20, 55, 110, 24, 36, 48, 72, 96, 144, 192, 216
+};
+
+/*
+ * Description: Calculate data frame transmitting time
+ *
+ * Parameters:
+ * In:
+ * preamble_type - Preamble Type
+ * pkt_type - PK_TYPE_11A, PK_TYPE_11B, PK_TYPE_11GB, PK_TYPE_11GA
+ * frame_length - Baseband Type
+ * tx_rate - Tx Rate
+ * Out:
+ *
+ * Return Value: FrameTime
+ *
+ */
+unsigned int vnt_get_frame_time(u8 preamble_type, u8 pkt_type,
+ unsigned int frame_length, u16 tx_rate)
+{
+ unsigned int frame_time;
+ unsigned int preamble;
+ unsigned int tmp;
+ unsigned int rate = 0;
+
+ if (tx_rate > RATE_54M)
+ return 0;
+
+ rate = (unsigned int)vnt_frame_time[tx_rate];
+
+ if (tx_rate <= 3) {
+ if (preamble_type == 1)
+ preamble = 96;
+ else
+ preamble = 192;
+
+ frame_time = (frame_length * 80) / rate;
+ tmp = (frame_time * rate) / 80;
+
+ if (frame_length != tmp)
+ frame_time++;
+
+ return preamble + frame_time;
+ }
+ frame_time = (frame_length * 8 + 22) / rate;
+ tmp = ((frame_time * rate) - 22) / 8;
+
+ if (frame_length != tmp)
+ frame_time++;
+
+ frame_time = frame_time * 4;
+
+ if (pkt_type != PK_TYPE_11A)
+ frame_time += 6;
+ return 20 + frame_time;
+}
+
+/*
+ * Description: Calculate Length, Service, and Signal fields of Phy for Tx
+ *
+ * Parameters:
+ * In:
+ * priv - Device Structure
+ * frame_length - Tx Frame Length
+ * tx_rate - Tx Rate
+ * Out:
+ * struct vnt_phy_field *phy
+ * - pointer to Phy Length field
+ * - pointer to Phy Service field
+ * - pointer to Phy Signal field
+ *
+ * Return Value: none
+ *
+ */
+void vnt_get_phy_field(struct vnt_private *priv, u32 frame_length,
+ u16 tx_rate, u8 pkt_type, struct vnt_phy_field *phy)
+{
+ u32 bit_count;
+ u32 count = 0;
+ u32 tmp;
+ int ext_bit;
+ u8 preamble_type = priv->preamble_type;
+
+ bit_count = frame_length * 8;
+ ext_bit = false;
+
+ switch (tx_rate) {
+ case RATE_1M:
+ count = bit_count;
+
+ phy->signal = 0x00;
+
+ break;
+ case RATE_2M:
+ count = bit_count / 2;
+
+ if (preamble_type == 1)
+ phy->signal = 0x09;
+ else
+ phy->signal = 0x01;
+
+ break;
+ case RATE_5M:
+ count = (bit_count * 10) / 55;
+ tmp = (count * 55) / 10;
+
+ if (tmp != bit_count)
+ count++;
+
+ if (preamble_type == 1)
+ phy->signal = 0x0a;
+ else
+ phy->signal = 0x02;
+
+ break;
+ case RATE_11M:
+ count = bit_count / 11;
+ tmp = count * 11;
+
+ if (tmp != bit_count) {
+ count++;
+
+ if ((bit_count - tmp) <= 3)
+ ext_bit = true;
+ }
+
+ if (preamble_type == 1)
+ phy->signal = 0x0b;
+ else
+ phy->signal = 0x03;
+
+ break;
+ case RATE_6M:
+ if (pkt_type == PK_TYPE_11A)
+ phy->signal = 0x9b;
+ else
+ phy->signal = 0x8b;
+
+ break;
+ case RATE_9M:
+ if (pkt_type == PK_TYPE_11A)
+ phy->signal = 0x9f;
+ else
+ phy->signal = 0x8f;
+
+ break;
+ case RATE_12M:
+ if (pkt_type == PK_TYPE_11A)
+ phy->signal = 0x9a;
+ else
+ phy->signal = 0x8a;
+
+ break;
+ case RATE_18M:
+ if (pkt_type == PK_TYPE_11A)
+ phy->signal = 0x9e;
+ else
+ phy->signal = 0x8e;
+
+ break;
+ case RATE_24M:
+ if (pkt_type == PK_TYPE_11A)
+ phy->signal = 0x99;
+ else
+ phy->signal = 0x89;
+
+ break;
+ case RATE_36M:
+ if (pkt_type == PK_TYPE_11A)
+ phy->signal = 0x9d;
+ else
+ phy->signal = 0x8d;
+
+ break;
+ case RATE_48M:
+ if (pkt_type == PK_TYPE_11A)
+ phy->signal = 0x98;
+ else
+ phy->signal = 0x88;
+
+ break;
+ case RATE_54M:
+ if (pkt_type == PK_TYPE_11A)
+ phy->signal = 0x9c;
+ else
+ phy->signal = 0x8c;
+ break;
+ default:
+ if (pkt_type == PK_TYPE_11A)
+ phy->signal = 0x9c;
+ else
+ phy->signal = 0x8c;
+ break;
+ }
+
+ if (pkt_type == PK_TYPE_11B) {
+ phy->service = 0x00;
+ if (ext_bit)
+ phy->service |= 0x80;
+ phy->len = cpu_to_le16((u16)count);
+ } else {
+ phy->service = 0x00;
+ phy->len = cpu_to_le16((u16)frame_length);
+ }
+}
+
+/*
+ * Description: Set Antenna mode
+ *
+ * Parameters:
+ * In:
+ * priv - Device Structure
+ * antenna_mode - Antenna Mode
+ * Out:
+ * none
+ *
+ * Return Value: none
+ *
+ */
+void vnt_set_antenna_mode(struct vnt_private *priv, u8 antenna_mode)
+{
+ switch (antenna_mode) {
+ case ANT_TXA:
+ case ANT_TXB:
+ break;
+ case ANT_RXA:
+ priv->bb_rx_conf &= 0xFC;
+ break;
+ case ANT_RXB:
+ priv->bb_rx_conf &= 0xFE;
+ priv->bb_rx_conf |= 0x02;
+ break;
+ }
+
+ vnt_control_out(priv, MESSAGE_TYPE_SET_ANTMD,
+ (u16)antenna_mode, 0, 0, NULL);
+}
+
+/*
+ * Description: Set Antenna mode
+ *
+ * Parameters:
+ * In:
+ * pDevice - Device Structure
+ * byAntennaMode - Antenna Mode
+ * Out:
+ * none
+ *
+ * Return Value: none
+ *
+ */
+
+int vnt_vt3184_init(struct vnt_private *priv)
+{
+ int status;
+ u16 length;
+ u8 *addr;
+ u8 *agc;
+ u16 length_agc;
+ u8 array[256];
+ u8 data;
+
+ status = vnt_control_in(priv, MESSAGE_TYPE_READ, 0,
+ MESSAGE_REQUEST_EEPROM, EEP_MAX_CONTEXT_SIZE,
+ priv->eeprom);
+ if (status != STATUS_SUCCESS)
+ return false;
+
+ priv->rf_type = priv->eeprom[EEP_OFS_RFTYPE];
+
+ dev_dbg(&priv->usb->dev, "RF Type %d\n", priv->rf_type);
+
+ if ((priv->rf_type == RF_AL2230) ||
+ (priv->rf_type == RF_AL2230S)) {
+ priv->bb_rx_conf = vnt_vt3184_al2230[10];
+ length = sizeof(vnt_vt3184_al2230);
+ addr = vnt_vt3184_al2230;
+ agc = vnt_vt3184_agc;
+ length_agc = sizeof(vnt_vt3184_agc);
+
+ priv->bb_vga[0] = 0x1C;
+ priv->bb_vga[1] = 0x10;
+ priv->bb_vga[2] = 0x0;
+ priv->bb_vga[3] = 0x0;
+
+ } else if (priv->rf_type == RF_AIROHA7230) {
+ priv->bb_rx_conf = vnt_vt3184_al2230[10];
+ length = sizeof(vnt_vt3184_al2230);
+ addr = vnt_vt3184_al2230;
+ agc = vnt_vt3184_agc;
+ length_agc = sizeof(vnt_vt3184_agc);
+
+ addr[0xd7] = 0x06;
+
+ priv->bb_vga[0] = 0x1c;
+ priv->bb_vga[1] = 0x10;
+ priv->bb_vga[2] = 0x0;
+ priv->bb_vga[3] = 0x0;
+
+ } else if ((priv->rf_type == RF_VT3226) ||
+ (priv->rf_type == RF_VT3226D0)) {
+ priv->bb_rx_conf = vnt_vt3184_vt3226d0[10];
+ length = sizeof(vnt_vt3184_vt3226d0);
+ addr = vnt_vt3184_vt3226d0;
+ agc = vnt_vt3184_agc;
+ length_agc = sizeof(vnt_vt3184_agc);
+
+ priv->bb_vga[0] = 0x20;
+ priv->bb_vga[1] = 0x10;
+ priv->bb_vga[2] = 0x0;
+ priv->bb_vga[3] = 0x0;
+
+ /* Fix VT3226 DFC system timing issue */
+ vnt_mac_reg_bits_on(priv, MAC_REG_SOFTPWRCTL2,
+ SOFTPWRCTL_RFLEOPT);
+ } else if (priv->rf_type == RF_VT3342A0) {
+ priv->bb_rx_conf = vnt_vt3184_vt3226d0[10];
+ length = sizeof(vnt_vt3184_vt3226d0);
+ addr = vnt_vt3184_vt3226d0;
+ agc = vnt_vt3184_agc;
+ length_agc = sizeof(vnt_vt3184_agc);
+
+ priv->bb_vga[0] = 0x20;
+ priv->bb_vga[1] = 0x10;
+ priv->bb_vga[2] = 0x0;
+ priv->bb_vga[3] = 0x0;
+
+ /* Fix VT3226 DFC system timing issue */
+ vnt_mac_reg_bits_on(priv, MAC_REG_SOFTPWRCTL2,
+ SOFTPWRCTL_RFLEOPT);
+ } else {
+ return true;
+ }
+
+ memcpy(array, addr, length);
+
+ vnt_control_out(priv, MESSAGE_TYPE_WRITE, 0,
+ MESSAGE_REQUEST_BBREG, length, array);
+
+ memcpy(array, agc, length_agc);
+
+ vnt_control_out(priv, MESSAGE_TYPE_WRITE, 0,
+ MESSAGE_REQUEST_BBAGC, length_agc, array);
+
+ if ((priv->rf_type == RF_VT3226) ||
+ (priv->rf_type == RF_VT3342A0)) {
+ vnt_control_out_u8(priv, MESSAGE_REQUEST_MACREG,
+ MAC_REG_ITRTMSET, 0x23);
+ vnt_mac_reg_bits_on(priv, MAC_REG_PAPEDELAY, 0x01);
+ } else if (priv->rf_type == RF_VT3226D0) {
+ vnt_control_out_u8(priv, MESSAGE_REQUEST_MACREG,
+ MAC_REG_ITRTMSET, 0x11);
+ vnt_mac_reg_bits_on(priv, MAC_REG_PAPEDELAY, 0x01);
+ }
+
+ vnt_control_out_u8(priv, MESSAGE_REQUEST_BBREG, 0x04, 0x7f);
+ vnt_control_out_u8(priv, MESSAGE_REQUEST_BBREG, 0x0d, 0x01);
+
+ vnt_rf_table_download(priv);
+
+ /* Fix for TX USB resets from vendors driver */
+ vnt_control_in(priv, MESSAGE_TYPE_READ, USB_REG4,
+ MESSAGE_REQUEST_MEM, sizeof(data), &data);
+
+ data |= 0x2;
+
+ vnt_control_out(priv, MESSAGE_TYPE_WRITE, USB_REG4,
+ MESSAGE_REQUEST_MEM, sizeof(data), &data);
+
+ return true;
+}
+
+/*
+ * Description: Set ShortSlotTime mode
+ *
+ * Parameters:
+ * In:
+ * priv - Device Structure
+ * Out:
+ * none
+ *
+ * Return Value: none
+ *
+ */
+void vnt_set_short_slot_time(struct vnt_private *priv)
+{
+ u8 bb_vga = 0;
+
+ if (priv->short_slot_time)
+ priv->bb_rx_conf &= 0xdf;
+ else
+ priv->bb_rx_conf |= 0x20;
+
+ vnt_control_in_u8(priv, MESSAGE_REQUEST_BBREG, 0xe7, &bb_vga);
+
+ if (bb_vga == priv->bb_vga[0])
+ priv->bb_rx_conf |= 0x20;
+
+ vnt_control_out_u8(priv, MESSAGE_REQUEST_BBREG, 0x0a, priv->bb_rx_conf);
+}
+
+void vnt_set_vga_gain_offset(struct vnt_private *priv, u8 data)
+{
+
+ vnt_control_out_u8(priv, MESSAGE_REQUEST_BBREG, 0xE7, data);
+
+ /* patch for 3253B0 Baseband with Cardbus module */
+ if (priv->short_slot_time)
+ priv->bb_rx_conf &= 0xdf; /* 1101 1111 */
+ else
+ priv->bb_rx_conf |= 0x20; /* 0010 0000 */
+
+ vnt_control_out_u8(priv, MESSAGE_REQUEST_BBREG, 0x0a, priv->bb_rx_conf);
+}
+
+/*
+ * Description: vnt_set_deep_sleep
+ *
+ * Parameters:
+ * In:
+ * priv - Device Structure
+ * Out:
+ * none
+ *
+ * Return Value: none
+ *
+ */
+void vnt_set_deep_sleep(struct vnt_private *priv)
+{
+ vnt_control_out_u8(priv, MESSAGE_REQUEST_BBREG, 0x0c, 0x17);/* CR12 */
+ vnt_control_out_u8(priv, MESSAGE_REQUEST_BBREG, 0x0d, 0xB9);/* CR13 */
+}
+
+void vnt_exit_deep_sleep(struct vnt_private *priv)
+{
+ vnt_control_out_u8(priv, MESSAGE_REQUEST_BBREG, 0x0c, 0x00);/* CR12 */
+ vnt_control_out_u8(priv, MESSAGE_REQUEST_BBREG, 0x0d, 0x01);/* CR13 */
+}
+
+void vnt_update_pre_ed_threshold(struct vnt_private *priv, int scanning)
+{
+ u8 cr_201 = 0x0, cr_206 = 0x0;
+ u8 ed_inx = priv->bb_pre_ed_index;
+
+ switch (priv->rf_type) {
+ case RF_AL2230:
+ case RF_AL2230S:
+ case RF_AIROHA7230:
+ if (scanning) { /* Max sensitivity */
+ ed_inx = 0;
+ cr_206 = 0x30;
+ break;
+ }
+
+ if (priv->bb_pre_ed_rssi <= 45) {
+ ed_inx = 20;
+ cr_201 = 0xff;
+ } else if (priv->bb_pre_ed_rssi <= 46) {
+ ed_inx = 19;
+ cr_201 = 0x1a;
+ } else if (priv->bb_pre_ed_rssi <= 47) {
+ ed_inx = 18;
+ cr_201 = 0x15;
+ } else if (priv->bb_pre_ed_rssi <= 49) {
+ ed_inx = 17;
+ cr_201 = 0xe;
+ } else if (priv->bb_pre_ed_rssi <= 51) {
+ ed_inx = 16;
+ cr_201 = 0x9;
+ } else if (priv->bb_pre_ed_rssi <= 53) {
+ ed_inx = 15;
+ cr_201 = 0x6;
+ } else if (priv->bb_pre_ed_rssi <= 55) {
+ ed_inx = 14;
+ cr_201 = 0x3;
+ } else if (priv->bb_pre_ed_rssi <= 56) {
+ ed_inx = 13;
+ cr_201 = 0x2;
+ cr_206 = 0xa0;
+ } else if (priv->bb_pre_ed_rssi <= 57) {
+ ed_inx = 12;
+ cr_201 = 0x2;
+ cr_206 = 0x20;
+ } else if (priv->bb_pre_ed_rssi <= 58) {
+ ed_inx = 11;
+ cr_201 = 0x1;
+ cr_206 = 0xa0;
+ } else if (priv->bb_pre_ed_rssi <= 59) {
+ ed_inx = 10;
+ cr_201 = 0x1;
+ cr_206 = 0x54;
+ } else if (priv->bb_pre_ed_rssi <= 60) {
+ ed_inx = 9;
+ cr_201 = 0x1;
+ cr_206 = 0x18;
+ } else if (priv->bb_pre_ed_rssi <= 61) {
+ ed_inx = 8;
+ cr_206 = 0xe3;
+ } else if (priv->bb_pre_ed_rssi <= 62) {
+ ed_inx = 7;
+ cr_206 = 0xb9;
+ } else if (priv->bb_pre_ed_rssi <= 63) {
+ ed_inx = 6;
+ cr_206 = 0x93;
+ } else if (priv->bb_pre_ed_rssi <= 64) {
+ ed_inx = 5;
+ cr_206 = 0x79;
+ } else if (priv->bb_pre_ed_rssi <= 65) {
+ ed_inx = 4;
+ cr_206 = 0x62;
+ } else if (priv->bb_pre_ed_rssi <= 66) {
+ ed_inx = 3;
+ cr_206 = 0x51;
+ } else if (priv->bb_pre_ed_rssi <= 67) {
+ ed_inx = 2;
+ cr_206 = 0x43;
+ } else if (priv->bb_pre_ed_rssi <= 68) {
+ ed_inx = 1;
+ cr_206 = 0x36;
+ } else {
+ ed_inx = 0;
+ cr_206 = 0x30;
+ }
+ break;
+
+ case RF_VT3226:
+ case RF_VT3226D0:
+ if (scanning) { /* Max sensitivity */
+ ed_inx = 0;
+ cr_206 = 0x24;
+ break;
+ }
+
+ if (priv->bb_pre_ed_rssi <= 41) {
+ ed_inx = 22;
+ cr_201 = 0xff;
+ } else if (priv->bb_pre_ed_rssi <= 42) {
+ ed_inx = 21;
+ cr_201 = 0x36;
+ } else if (priv->bb_pre_ed_rssi <= 43) {
+ ed_inx = 20;
+ cr_201 = 0x26;
+ } else if (priv->bb_pre_ed_rssi <= 45) {
+ ed_inx = 19;
+ cr_201 = 0x18;
+ } else if (priv->bb_pre_ed_rssi <= 47) {
+ ed_inx = 18;
+ cr_201 = 0x11;
+ } else if (priv->bb_pre_ed_rssi <= 49) {
+ ed_inx = 17;
+ cr_201 = 0xa;
+ } else if (priv->bb_pre_ed_rssi <= 51) {
+ ed_inx = 16;
+ cr_201 = 0x7;
+ } else if (priv->bb_pre_ed_rssi <= 53) {
+ ed_inx = 15;
+ cr_201 = 0x4;
+ } else if (priv->bb_pre_ed_rssi <= 55) {
+ ed_inx = 14;
+ cr_201 = 0x2;
+ cr_206 = 0xc0;
+ } else if (priv->bb_pre_ed_rssi <= 56) {
+ ed_inx = 13;
+ cr_201 = 0x2;
+ cr_206 = 0x30;
+ } else if (priv->bb_pre_ed_rssi <= 57) {
+ ed_inx = 12;
+ cr_201 = 0x1;
+ cr_206 = 0xb0;
+ } else if (priv->bb_pre_ed_rssi <= 58) {
+ ed_inx = 11;
+ cr_201 = 0x1;
+ cr_206 = 0x70;
+ } else if (priv->bb_pre_ed_rssi <= 59) {
+ ed_inx = 10;
+ cr_201 = 0x1;
+ cr_206 = 0x30;
+ } else if (priv->bb_pre_ed_rssi <= 60) {
+ ed_inx = 9;
+ cr_206 = 0xea;
+ } else if (priv->bb_pre_ed_rssi <= 61) {
+ ed_inx = 8;
+ cr_206 = 0xc0;
+ } else if (priv->bb_pre_ed_rssi <= 62) {
+ ed_inx = 7;
+ cr_206 = 0x9c;
+ } else if (priv->bb_pre_ed_rssi <= 63) {
+ ed_inx = 6;
+ cr_206 = 0x80;
+ } else if (priv->bb_pre_ed_rssi <= 64) {
+ ed_inx = 5;
+ cr_206 = 0x68;
+ } else if (priv->bb_pre_ed_rssi <= 65) {
+ ed_inx = 4;
+ cr_206 = 0x52;
+ } else if (priv->bb_pre_ed_rssi <= 66) {
+ ed_inx = 3;
+ cr_206 = 0x43;
+ } else if (priv->bb_pre_ed_rssi <= 67) {
+ ed_inx = 2;
+ cr_206 = 0x36;
+ } else if (priv->bb_pre_ed_rssi <= 68) {
+ ed_inx = 1;
+ cr_206 = 0x2d;
+ } else {
+ ed_inx = 0;
+ cr_206 = 0x24;
+ }
+ break;
+
+ case RF_VT3342A0:
+ if (scanning) { /* need Max sensitivity */
+ ed_inx = 0;
+ cr_206 = 0x38;
+ break;
+ }
+
+ if (priv->bb_pre_ed_rssi <= 41) {
+ ed_inx = 20;
+ cr_201 = 0xff;
+ } else if (priv->bb_pre_ed_rssi <= 42) {
+ ed_inx = 19;
+ cr_201 = 0x36;
+ } else if (priv->bb_pre_ed_rssi <= 43) {
+ ed_inx = 18;
+ cr_201 = 0x26;
+ } else if (priv->bb_pre_ed_rssi <= 45) {
+ ed_inx = 17;
+ cr_201 = 0x18;
+ } else if (priv->bb_pre_ed_rssi <= 47) {
+ ed_inx = 16;
+ cr_201 = 0x11;
+ } else if (priv->bb_pre_ed_rssi <= 49) {
+ ed_inx = 15;
+ cr_201 = 0xa;
+ } else if (priv->bb_pre_ed_rssi <= 51) {
+ ed_inx = 14;
+ cr_201 = 0x7;
+ } else if (priv->bb_pre_ed_rssi <= 53) {
+ ed_inx = 13;
+ cr_201 = 0x4;
+ } else if (priv->bb_pre_ed_rssi <= 55) {
+ ed_inx = 12;
+ cr_201 = 0x2;
+ cr_206 = 0xc0;
+ } else if (priv->bb_pre_ed_rssi <= 56) {
+ ed_inx = 11;
+ cr_201 = 0x2;
+ cr_206 = 0x30;
+ } else if (priv->bb_pre_ed_rssi <= 57) {
+ ed_inx = 10;
+ cr_201 = 0x1;
+ cr_206 = 0xb0;
+ } else if (priv->bb_pre_ed_rssi <= 58) {
+ ed_inx = 9;
+ cr_201 = 0x1;
+ cr_206 = 0x70;
+ } else if (priv->bb_pre_ed_rssi <= 59) {
+ ed_inx = 8;
+ cr_201 = 0x1;
+ cr_206 = 0x30;
+ } else if (priv->bb_pre_ed_rssi <= 60) {
+ ed_inx = 7;
+ cr_206 = 0xea;
+ } else if (priv->bb_pre_ed_rssi <= 61) {
+ ed_inx = 6;
+ cr_206 = 0xc0;
+ } else if (priv->bb_pre_ed_rssi <= 62) {
+ ed_inx = 5;
+ cr_206 = 0x9c;
+ } else if (priv->bb_pre_ed_rssi <= 63) {
+ ed_inx = 4;
+ cr_206 = 0x80;
+ } else if (priv->bb_pre_ed_rssi <= 64) {
+ ed_inx = 3;
+ cr_206 = 0x68;
+ } else if (priv->bb_pre_ed_rssi <= 65) {
+ ed_inx = 2;
+ cr_206 = 0x52;
+ } else if (priv->bb_pre_ed_rssi <= 66) {
+ ed_inx = 1;
+ cr_206 = 0x43;
+ } else {
+ ed_inx = 0;
+ cr_206 = 0x38;
+ }
+ break;
+
+ }
+
+ if (ed_inx == priv->bb_pre_ed_index && !scanning)
+ return;
+
+ priv->bb_pre_ed_index = ed_inx;
+
+ dev_dbg(&priv->usb->dev, "%s bb_pre_ed_rssi %d\n",
+ __func__, priv->bb_pre_ed_rssi);
+
+ if (!cr_201 && !cr_206)
+ return;
+
+ vnt_control_out_u8(priv, MESSAGE_REQUEST_BBREG, 0xc9, cr_201);
+ vnt_control_out_u8(priv, MESSAGE_REQUEST_BBREG, 0xce, cr_206);
+}
+
diff --git a/drivers/staging/vt6656/baseband.h b/drivers/staging/vt6656/baseband.h
new file mode 100644
index 000000000..771ea4054
--- /dev/null
+++ b/drivers/staging/vt6656/baseband.h
@@ -0,0 +1,104 @@
+/*
+ * Copyright (c) 1996, 2003 VIA Networking Technologies, Inc.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ *
+ * File: baseband.h
+ *
+ * Purpose: Implement functions to access baseband
+ *
+ * Author: Jerry Chen
+ *
+ * Date: Jun. 5, 2002
+ *
+ * Revision History:
+ * 06-10-2003 Bryan YC Fan: Re-write codes to support VT3253 spec.
+ * 08-26-2003 Kyle Hsu : Add defines of packet type and TX rate.
+ */
+
+#ifndef __BASEBAND_H__
+#define __BASEBAND_H__
+
+#include "device.h"
+
+#define PREAMBLE_LONG 0
+#define PREAMBLE_SHORT 1
+
+/*
+ * Registers in the BASEBAND
+ */
+#define BB_MAX_CONTEXT_SIZE 256
+
+#define C_SIFS_A 16 /* usec */
+#define C_SIFS_BG 10
+
+#define C_EIFS 80 /* usec */
+
+#define C_SLOT_SHORT 9 /* usec */
+#define C_SLOT_LONG 20
+
+#define C_CWMIN_A 15 /* slot time */
+#define C_CWMIN_B 31
+
+#define C_CWMAX 1023 /* slot time */
+
+/* 0:11A 1:11B 2:11G */
+#define BB_TYPE_11A 0
+#define BB_TYPE_11B 1
+#define BB_TYPE_11G 2
+
+/* 0:11a, 1:11b, 2:11gb (only CCK in BasicRate), 3:11ga (OFDM in BasicRate) */
+#define PK_TYPE_11A 0
+#define PK_TYPE_11B 1
+#define PK_TYPE_11GB 2
+#define PK_TYPE_11GA 3
+
+#define TOP_RATE_54M 0x80000000
+#define TOP_RATE_48M 0x40000000
+#define TOP_RATE_36M 0x20000000
+#define TOP_RATE_24M 0x10000000
+#define TOP_RATE_18M 0x08000000
+#define TOP_RATE_12M 0x04000000
+#define TOP_RATE_11M 0x02000000
+#define TOP_RATE_9M 0x01000000
+#define TOP_RATE_6M 0x00800000
+#define TOP_RATE_55M 0x00400000
+#define TOP_RATE_2M 0x00200000
+#define TOP_RATE_1M 0x00100000
+
+/* Length, Service, and Signal fields of Phy for Tx */
+struct vnt_phy_field {
+ u8 signal;
+ u8 service;
+ __le16 len;
+} __packed;
+
+unsigned int vnt_get_frame_time(u8 preamble_type, u8 pkt_type,
+ unsigned int frame_length, u16 tx_rate);
+
+void vnt_get_phy_field(struct vnt_private *, u32 frame_length,
+ u16 tx_rate, u8 pkt_type, struct vnt_phy_field *);
+
+void vnt_set_short_slot_time(struct vnt_private *);
+void vnt_set_vga_gain_offset(struct vnt_private *, u8);
+void vnt_set_antenna_mode(struct vnt_private *, u8);
+int vnt_vt3184_init(struct vnt_private *);
+void vnt_set_deep_sleep(struct vnt_private *);
+void vnt_exit_deep_sleep(struct vnt_private *);
+void vnt_update_pre_ed_threshold(struct vnt_private *, int scanning);
+
+#endif /* __BASEBAND_H__ */
diff --git a/drivers/staging/vt6656/card.c b/drivers/staging/vt6656/card.c
new file mode 100644
index 000000000..67ff13f4f
--- /dev/null
+++ b/drivers/staging/vt6656/card.c
@@ -0,0 +1,820 @@
+/*
+ * Copyright (c) 1996, 2003 VIA Networking Technologies, Inc.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * File: card.c
+ * Purpose: Provide functions to setup NIC operation mode
+ * Functions:
+ * vnt_set_rspinf - Set RSPINF
+ * vnt_update_ifs - Update slotTime,SIFS,DIFS, and EIFS
+ * vnt_update_top_rates - Update BasicTopRate
+ * vnt_add_basic_rate - Add to BasicRateSet
+ * vnt_ofdm_min_rate - Check if any OFDM rate is in BasicRateSet
+ * vnt_get_tsf_offset - Calculate TSFOffset
+ * vnt_get_current_tsf - Read Current NIC TSF counter
+ * vnt_get_next_tbtt - Calculate Next Beacon TSF counter
+ * vnt_reset_next_tbtt - Set NIC Beacon time
+ * vnt_update_next_tbtt - Sync. NIC Beacon time
+ * vnt_radio_power_off - Turn Off NIC Radio Power
+ * vnt_radio_power_on - Turn On NIC Radio Power
+ *
+ * Revision History:
+ * 06-10-2003 Bryan YC Fan: Re-write codes to support VT3253 spec.
+ * 08-26-2003 Kyle Hsu: Modify the definition type of dwIoBase.
+ * 09-01-2003 Bryan YC Fan: Add vnt_update_ifs().
+ *
+ */
+
+#include "device.h"
+#include "card.h"
+#include "baseband.h"
+#include "mac.h"
+#include "desc.h"
+#include "rf.h"
+#include "power.h"
+#include "key.h"
+#include "usbpipe.h"
+
+/* const u16 cwRXBCNTSFOff[MAX_RATE] =
+ {17, 34, 96, 192, 34, 23, 17, 11, 8, 5, 4, 3}; */
+
+static const u16 cwRXBCNTSFOff[MAX_RATE] = {
+ 192, 96, 34, 17, 34, 23, 17, 11, 8, 5, 4, 3
+};
+
+/*
+ * Description: Set NIC media channel
+ *
+ * Parameters:
+ * In:
+ * pDevice - The adapter to be set
+ * connection_channel - Channel to be set
+ * Out:
+ * none
+ */
+void vnt_set_channel(struct vnt_private *priv, u32 connection_channel)
+{
+
+ if (connection_channel > CB_MAX_CHANNEL || !connection_channel)
+ return;
+
+ /* clear NAV */
+ vnt_mac_reg_bits_on(priv, MAC_REG_MACCR, MACCR_CLRNAV);
+
+ /* Set Channel[7] = 0 to tell H/W channel is changing now. */
+ vnt_mac_reg_bits_off(priv, MAC_REG_CHANNEL, 0xb0);
+
+ vnt_control_out(priv, MESSAGE_TYPE_SELECT_CHANNEL,
+ connection_channel, 0, 0, NULL);
+
+ vnt_control_out_u8(priv, MESSAGE_REQUEST_MACREG, MAC_REG_CHANNEL,
+ (u8)(connection_channel|0x80));
+}
+
+/*
+ * Description: Get CCK mode basic rate
+ *
+ * Parameters:
+ * In:
+ * priv - The adapter to be set
+ * rate_idx - Receiving data rate
+ * Out:
+ * none
+ *
+ * Return Value: response Control frame rate
+ *
+ */
+static u16 vnt_get_cck_rate(struct vnt_private *priv, u16 rate_idx)
+{
+ u16 ui = rate_idx;
+
+ while (ui > RATE_1M) {
+ if (priv->basic_rates & (1 << ui))
+ return ui;
+ ui--;
+ }
+
+ return RATE_1M;
+}
+
+/*
+ * Description: Get OFDM mode basic rate
+ *
+ * Parameters:
+ * In:
+ * priv - The adapter to be set
+ * rate_idx - Receiving data rate
+ * Out:
+ * none
+ *
+ * Return Value: response Control frame rate
+ *
+ */
+static u16 vnt_get_ofdm_rate(struct vnt_private *priv, u16 rate_idx)
+{
+ u16 ui = rate_idx;
+
+ dev_dbg(&priv->usb->dev, "%s basic rate: %d\n",
+ __func__, priv->basic_rates);
+
+ if (!vnt_ofdm_min_rate(priv)) {
+ dev_dbg(&priv->usb->dev, "%s (NO OFDM) %d\n",
+ __func__, rate_idx);
+ if (rate_idx > RATE_24M)
+ rate_idx = RATE_24M;
+ return rate_idx;
+ }
+
+ while (ui > RATE_11M) {
+ if (priv->basic_rates & (1 << ui)) {
+ dev_dbg(&priv->usb->dev, "%s rate: %d\n",
+ __func__, ui);
+ return ui;
+ }
+ ui--;
+ }
+
+ dev_dbg(&priv->usb->dev, "%s basic rate: 24M\n", __func__);
+
+ return RATE_24M;
+}
+
+/*
+ * Description: Calculate TxRate and RsvTime fields for RSPINF in OFDM mode.
+ *
+ * Parameters:
+ * In:
+ * rate - Tx Rate
+ * bb_type - Tx Packet type
+ * Out:
+ * tx_rate - pointer to RSPINF TxRate field
+ * rsv_time- pointer to RSPINF RsvTime field
+ *
+ * Return Value: none
+ *
+ */
+static void vnt_calculate_ofdm_rate(u16 rate, u8 bb_type,
+ u8 *tx_rate, u8 *rsv_time)
+{
+
+ switch (rate) {
+ case RATE_6M:
+ if (bb_type == BB_TYPE_11A) {
+ *tx_rate = 0x9b;
+ *rsv_time = 24;
+ } else {
+ *tx_rate = 0x8b;
+ *rsv_time = 30;
+ }
+ break;
+ case RATE_9M:
+ if (bb_type == BB_TYPE_11A) {
+ *tx_rate = 0x9f;
+ *rsv_time = 16;
+ } else {
+ *tx_rate = 0x8f;
+ *rsv_time = 22;
+ }
+ break;
+ case RATE_12M:
+ if (bb_type == BB_TYPE_11A) {
+ *tx_rate = 0x9a;
+ *rsv_time = 12;
+ } else {
+ *tx_rate = 0x8a;
+ *rsv_time = 18;
+ }
+ break;
+ case RATE_18M:
+ if (bb_type == BB_TYPE_11A) {
+ *tx_rate = 0x9e;
+ *rsv_time = 8;
+ } else {
+ *tx_rate = 0x8e;
+ *rsv_time = 14;
+ }
+ break;
+ case RATE_36M:
+ if (bb_type == BB_TYPE_11A) {
+ *tx_rate = 0x9d;
+ *rsv_time = 4;
+ } else {
+ *tx_rate = 0x8d;
+ *rsv_time = 10;
+ }
+ break;
+ case RATE_48M:
+ if (bb_type == BB_TYPE_11A) {
+ *tx_rate = 0x98;
+ *rsv_time = 4;
+ } else {
+ *tx_rate = 0x88;
+ *rsv_time = 10;
+ }
+ break;
+ case RATE_54M:
+ if (bb_type == BB_TYPE_11A) {
+ *tx_rate = 0x9c;
+ *rsv_time = 4;
+ } else {
+ *tx_rate = 0x8c;
+ *rsv_time = 10;
+ }
+ break;
+ case RATE_24M:
+ default:
+ if (bb_type == BB_TYPE_11A) {
+ *tx_rate = 0x99;
+ *rsv_time = 8;
+ } else {
+ *tx_rate = 0x89;
+ *rsv_time = 14;
+ }
+ break;
+ }
+}
+
+/*
+ * Description: Set RSPINF
+ *
+ * Parameters:
+ * In:
+ * pDevice - The adapter to be set
+ * Out:
+ * none
+ *
+ * Return Value: None.
+ *
+ */
+
+void vnt_set_rspinf(struct vnt_private *priv, u8 bb_type)
+{
+ struct vnt_phy_field phy[4];
+ u8 tx_rate[9] = {0, 0, 0, 0, 0, 0, 0, 0, 0}; /* For OFDM */
+ u8 rsv_time[9] = {0, 0, 0, 0, 0, 0, 0, 0, 0};
+ u8 data[34];
+ int i;
+
+ /*RSPINF_b_1*/
+ vnt_get_phy_field(priv, 14,
+ vnt_get_cck_rate(priv, RATE_1M), PK_TYPE_11B, &phy[0]);
+
+ /*RSPINF_b_2*/
+ vnt_get_phy_field(priv, 14,
+ vnt_get_cck_rate(priv, RATE_2M), PK_TYPE_11B, &phy[1]);
+
+ /*RSPINF_b_5*/
+ vnt_get_phy_field(priv, 14,
+ vnt_get_cck_rate(priv, RATE_5M), PK_TYPE_11B, &phy[2]);
+
+ /*RSPINF_b_11*/
+ vnt_get_phy_field(priv, 14,
+ vnt_get_cck_rate(priv, RATE_11M), PK_TYPE_11B, &phy[3]);
+
+
+ /*RSPINF_a_6*/
+ vnt_calculate_ofdm_rate(RATE_6M, bb_type, &tx_rate[0], &rsv_time[0]);
+
+ /*RSPINF_a_9*/
+ vnt_calculate_ofdm_rate(RATE_9M, bb_type, &tx_rate[1], &rsv_time[1]);
+
+ /*RSPINF_a_12*/
+ vnt_calculate_ofdm_rate(RATE_12M, bb_type, &tx_rate[2], &rsv_time[2]);
+
+ /*RSPINF_a_18*/
+ vnt_calculate_ofdm_rate(RATE_18M, bb_type, &tx_rate[3], &rsv_time[3]);
+
+ /*RSPINF_a_24*/
+ vnt_calculate_ofdm_rate(RATE_24M, bb_type, &tx_rate[4], &rsv_time[4]);
+
+ /*RSPINF_a_36*/
+ vnt_calculate_ofdm_rate(vnt_get_ofdm_rate(priv, RATE_36M),
+ bb_type, &tx_rate[5], &rsv_time[5]);
+
+ /*RSPINF_a_48*/
+ vnt_calculate_ofdm_rate(vnt_get_ofdm_rate(priv, RATE_48M),
+ bb_type, &tx_rate[6], &rsv_time[6]);
+
+ /*RSPINF_a_54*/
+ vnt_calculate_ofdm_rate(vnt_get_ofdm_rate(priv, RATE_54M),
+ bb_type, &tx_rate[7], &rsv_time[7]);
+
+ /*RSPINF_a_72*/
+ vnt_calculate_ofdm_rate(vnt_get_ofdm_rate(priv, RATE_54M),
+ bb_type, &tx_rate[8], &rsv_time[8]);
+
+ put_unaligned(phy[0].len, (u16 *)&data[0]);
+ data[2] = phy[0].signal;
+ data[3] = phy[0].service;
+
+ put_unaligned(phy[1].len, (u16 *)&data[4]);
+ data[6] = phy[1].signal;
+ data[7] = phy[1].service;
+
+ put_unaligned(phy[2].len, (u16 *)&data[8]);
+ data[10] = phy[2].signal;
+ data[11] = phy[2].service;
+
+ put_unaligned(phy[3].len, (u16 *)&data[12]);
+ data[14] = phy[3].signal;
+ data[15] = phy[3].service;
+
+ for (i = 0; i < 9; i++) {
+ data[16 + i * 2] = tx_rate[i];
+ data[16 + i * 2 + 1] = rsv_time[i];
+ }
+
+ vnt_control_out(priv, MESSAGE_TYPE_WRITE,
+ MAC_REG_RSPINF_B_1, MESSAGE_REQUEST_MACREG, 34, &data[0]);
+}
+
+/*
+ * Description: Update IFS
+ *
+ * Parameters:
+ * In:
+ * priv - The adapter to be set
+ * Out:
+ * none
+ *
+ * Return Value: None.
+ *
+ */
+void vnt_update_ifs(struct vnt_private *priv)
+{
+ u8 max_min = 0;
+ u8 data[4];
+
+ if (priv->packet_type == PK_TYPE_11A) {
+ priv->slot = C_SLOT_SHORT;
+ priv->sifs = C_SIFS_A;
+ priv->difs = C_SIFS_A + 2 * C_SLOT_SHORT;
+ max_min = 4;
+ } else if (priv->packet_type == PK_TYPE_11B) {
+ priv->slot = C_SLOT_LONG;
+ priv->sifs = C_SIFS_BG;
+ priv->difs = C_SIFS_BG + 2 * C_SLOT_LONG;
+ max_min = 5;
+ } else {/* PK_TYPE_11GA & PK_TYPE_11GB */
+ bool ofdm_rate = false;
+ unsigned int ii = 0;
+
+ priv->sifs = C_SIFS_BG;
+
+ if (priv->short_slot_time)
+ priv->slot = C_SLOT_SHORT;
+ else
+ priv->slot = C_SLOT_LONG;
+
+ priv->difs = C_SIFS_BG + 2 * priv->slot;
+
+ for (ii = RATE_54M; ii >= RATE_6M; ii--) {
+ if (priv->basic_rates & ((u32)(0x1 << ii))) {
+ ofdm_rate = true;
+ break;
+ }
+ }
+
+ if (ofdm_rate == true)
+ max_min = 4;
+ else
+ max_min = 5;
+ }
+
+ priv->eifs = C_EIFS;
+
+ switch (priv->rf_type) {
+ case RF_VT3226D0:
+ if (priv->bb_type != BB_TYPE_11B) {
+ priv->sifs -= 1;
+ priv->difs -= 1;
+ break;
+ }
+ case RF_AIROHA7230:
+ case RF_AL2230:
+ case RF_AL2230S:
+ if (priv->bb_type != BB_TYPE_11B)
+ break;
+ case RF_RFMD2959:
+ case RF_VT3226:
+ case RF_VT3342A0:
+ priv->sifs -= 3;
+ priv->difs -= 3;
+ break;
+ case RF_MAXIM2829:
+ if (priv->bb_type == BB_TYPE_11A) {
+ priv->sifs -= 5;
+ priv->difs -= 5;
+ } else {
+ priv->sifs -= 2;
+ priv->difs -= 2;
+ }
+
+ break;
+ }
+
+ data[0] = (u8)priv->sifs;
+ data[1] = (u8)priv->difs;
+ data[2] = (u8)priv->eifs;
+ data[3] = (u8)priv->slot;
+
+ vnt_control_out(priv, MESSAGE_TYPE_WRITE, MAC_REG_SIFS,
+ MESSAGE_REQUEST_MACREG, 4, &data[0]);
+
+ max_min |= 0xa0;
+
+ vnt_control_out(priv, MESSAGE_TYPE_WRITE, MAC_REG_CWMAXMIN0,
+ MESSAGE_REQUEST_MACREG, 1, &max_min);
+}
+
+void vnt_update_top_rates(struct vnt_private *priv)
+{
+ u8 top_ofdm = RATE_24M, top_cck = RATE_1M;
+ u8 i;
+
+ /*Determines the highest basic rate.*/
+ for (i = RATE_54M; i >= RATE_6M; i--) {
+ if (priv->basic_rates & (u16)(1 << i)) {
+ top_ofdm = i;
+ break;
+ }
+ }
+
+ priv->top_ofdm_basic_rate = top_ofdm;
+
+ for (i = RATE_11M;; i--) {
+ if (priv->basic_rates & (u16)(1 << i)) {
+ top_cck = i;
+ break;
+ }
+ if (i == RATE_1M)
+ break;
+ }
+
+ priv->top_cck_basic_rate = top_cck;
+}
+
+int vnt_ofdm_min_rate(struct vnt_private *priv)
+{
+ int ii;
+
+ for (ii = RATE_54M; ii >= RATE_6M; ii--) {
+ if ((priv->basic_rates) & ((u16)(1 << ii)))
+ return true;
+ }
+
+ return false;
+}
+
+u8 vnt_get_pkt_type(struct vnt_private *priv)
+{
+
+ if (priv->bb_type == BB_TYPE_11A || priv->bb_type == BB_TYPE_11B)
+ return (u8)priv->bb_type;
+ else if (vnt_ofdm_min_rate(priv))
+ return PK_TYPE_11GA;
+ return PK_TYPE_11GB;
+}
+
+/*
+ * Description: Calculate TSF offset of two TSF input
+ * Get TSF Offset from RxBCN's TSF and local TSF
+ *
+ * Parameters:
+ * In:
+ * rx_rate - rx rate.
+ * tsf1 - Rx BCN's TSF
+ * tsf2 - Local TSF
+ * Out:
+ * none
+ *
+ * Return Value: TSF Offset value
+ *
+ */
+u64 vnt_get_tsf_offset(u8 rx_rate, u64 tsf1, u64 tsf2)
+{
+ u64 tsf_offset = 0;
+ u16 rx_bcn_offset = 0;
+
+ rx_bcn_offset = cwRXBCNTSFOff[rx_rate % MAX_RATE];
+
+ tsf2 += (u64)rx_bcn_offset;
+
+ tsf_offset = tsf1 - tsf2;
+
+ return tsf_offset;
+}
+
+/*
+ * Description: Sync. TSF counter to BSS
+ * Get TSF offset and write to HW
+ *
+ * Parameters:
+ * In:
+ * priv - The adapter to be sync.
+ * time_stamp - Rx BCN's TSF
+ * local_tsf - Local TSF
+ * Out:
+ * none
+ *
+ * Return Value: none
+ *
+ */
+void vnt_adjust_tsf(struct vnt_private *priv, u8 rx_rate,
+ u64 time_stamp, u64 local_tsf)
+{
+ u64 tsf_offset = 0;
+ u8 data[8];
+
+ tsf_offset = vnt_get_tsf_offset(rx_rate, time_stamp, local_tsf);
+
+ data[0] = (u8)tsf_offset;
+ data[1] = (u8)(tsf_offset >> 8);
+ data[2] = (u8)(tsf_offset >> 16);
+ data[3] = (u8)(tsf_offset >> 24);
+ data[4] = (u8)(tsf_offset >> 32);
+ data[5] = (u8)(tsf_offset >> 40);
+ data[6] = (u8)(tsf_offset >> 48);
+ data[7] = (u8)(tsf_offset >> 56);
+
+ vnt_control_out(priv, MESSAGE_TYPE_SET_TSFTBTT,
+ MESSAGE_REQUEST_TSF, 0, 8, data);
+}
+/*
+ * Description: Read NIC TSF counter
+ * Get local TSF counter
+ *
+ * Parameters:
+ * In:
+ * priv - The adapter to be read
+ * Out:
+ * current_tsf - Current TSF counter
+ *
+ * Return Value: true if success; otherwise false
+ *
+ */
+bool vnt_get_current_tsf(struct vnt_private *priv, u64 *current_tsf)
+{
+
+ *current_tsf = priv->current_tsf;
+
+ return true;
+}
+
+/*
+ * Description: Clear NIC TSF counter
+ * Clear local TSF counter
+ *
+ * Parameters:
+ * In:
+ * priv - The adapter to be read
+ *
+ * Return Value: true if success; otherwise false
+ *
+ */
+bool vnt_clear_current_tsf(struct vnt_private *priv)
+{
+
+ vnt_mac_reg_bits_on(priv, MAC_REG_TFTCTL, TFTCTL_TSFCNTRST);
+
+ priv->current_tsf = 0;
+
+ return true;
+}
+
+/*
+ * Description: Read NIC TSF counter
+ * Get NEXTTBTT from adjusted TSF and Beacon Interval
+ *
+ * Parameters:
+ * In:
+ * tsf - Current TSF counter
+ * beacon_interval - Beacon Interval
+ * Out:
+ * tsf - Current TSF counter
+ *
+ * Return Value: TSF value of next Beacon
+ *
+ */
+u64 vnt_get_next_tbtt(u64 tsf, u16 beacon_interval)
+{
+ u32 beacon_int;
+
+ beacon_int = beacon_interval * 1024;
+
+ /* Next TBTT =
+ * ((local_current_TSF / beacon_interval) + 1) * beacon_interval
+ */
+ if (beacon_int) {
+ do_div(tsf, beacon_int);
+ tsf += 1;
+ tsf *= beacon_int;
+ }
+
+ return tsf;
+}
+
+/*
+ * Description: Set NIC TSF counter for first Beacon time
+ * Get NEXTTBTT from adjusted TSF and Beacon Interval
+ *
+ * Parameters:
+ * In:
+ * dwIoBase - IO Base
+ * beacon_interval - Beacon Interval
+ * Out:
+ * none
+ *
+ * Return Value: none
+ *
+ */
+void vnt_reset_next_tbtt(struct vnt_private *priv, u16 beacon_interval)
+{
+ u64 next_tbtt = 0;
+ u8 data[8];
+
+ vnt_clear_current_tsf(priv);
+
+ next_tbtt = vnt_get_next_tbtt(next_tbtt, beacon_interval);
+
+ data[0] = (u8)next_tbtt;
+ data[1] = (u8)(next_tbtt >> 8);
+ data[2] = (u8)(next_tbtt >> 16);
+ data[3] = (u8)(next_tbtt >> 24);
+ data[4] = (u8)(next_tbtt >> 32);
+ data[5] = (u8)(next_tbtt >> 40);
+ data[6] = (u8)(next_tbtt >> 48);
+ data[7] = (u8)(next_tbtt >> 56);
+
+ vnt_control_out(priv, MESSAGE_TYPE_SET_TSFTBTT,
+ MESSAGE_REQUEST_TBTT, 0, 8, data);
+}
+
+/*
+ * Description: Sync NIC TSF counter for Beacon time
+ * Get NEXTTBTT and write to HW
+ *
+ * Parameters:
+ * In:
+ * priv - The adapter to be set
+ * tsf - Current TSF counter
+ * beacon_interval - Beacon Interval
+ * Out:
+ * none
+ *
+ * Return Value: none
+ *
+ */
+void vnt_update_next_tbtt(struct vnt_private *priv, u64 tsf,
+ u16 beacon_interval)
+{
+ u8 data[8];
+
+ tsf = vnt_get_next_tbtt(tsf, beacon_interval);
+
+ data[0] = (u8)tsf;
+ data[1] = (u8)(tsf >> 8);
+ data[2] = (u8)(tsf >> 16);
+ data[3] = (u8)(tsf >> 24);
+ data[4] = (u8)(tsf >> 32);
+ data[5] = (u8)(tsf >> 40);
+ data[6] = (u8)(tsf >> 48);
+ data[7] = (u8)(tsf >> 56);
+
+ vnt_control_out(priv, MESSAGE_TYPE_SET_TSFTBTT,
+ MESSAGE_REQUEST_TBTT, 0, 8, data);
+
+ dev_dbg(&priv->usb->dev, "%s TBTT: %8llx\n", __func__, tsf);
+}
+
+/*
+ * Description: Turn off Radio power
+ *
+ * Parameters:
+ * In:
+ * priv - The adapter to be turned off
+ * Out:
+ * none
+ *
+ * Return Value: true if success; otherwise false
+ *
+ */
+int vnt_radio_power_off(struct vnt_private *priv)
+{
+ int ret = true;
+
+ switch (priv->rf_type) {
+ case RF_AL2230:
+ case RF_AL2230S:
+ case RF_AIROHA7230:
+ case RF_VT3226:
+ case RF_VT3226D0:
+ case RF_VT3342A0:
+ vnt_mac_reg_bits_off(priv, MAC_REG_SOFTPWRCTL,
+ (SOFTPWRCTL_SWPE2 | SOFTPWRCTL_SWPE3));
+ break;
+ }
+
+ vnt_mac_reg_bits_off(priv, MAC_REG_HOSTCR, HOSTCR_RXON);
+
+ vnt_set_deep_sleep(priv);
+
+ vnt_mac_reg_bits_on(priv, MAC_REG_GPIOCTL1, GPIO3_INTMD);
+
+ return ret;
+}
+
+/*
+ * Description: Turn on Radio power
+ *
+ * Parameters:
+ * In:
+ * priv - The adapter to be turned on
+ * Out:
+ * none
+ *
+ * Return Value: true if success; otherwise false
+ *
+ */
+int vnt_radio_power_on(struct vnt_private *priv)
+{
+ int ret = true;
+
+ vnt_exit_deep_sleep(priv);
+
+ vnt_mac_reg_bits_on(priv, MAC_REG_HOSTCR, HOSTCR_RXON);
+
+ switch (priv->rf_type) {
+ case RF_AL2230:
+ case RF_AL2230S:
+ case RF_AIROHA7230:
+ case RF_VT3226:
+ case RF_VT3226D0:
+ case RF_VT3342A0:
+ vnt_mac_reg_bits_on(priv, MAC_REG_SOFTPWRCTL,
+ (SOFTPWRCTL_SWPE2 | SOFTPWRCTL_SWPE3));
+ break;
+ }
+
+ vnt_mac_reg_bits_off(priv, MAC_REG_GPIOCTL1, GPIO3_INTMD);
+
+ return ret;
+}
+
+void vnt_set_bss_mode(struct vnt_private *priv)
+{
+ if (priv->rf_type == RF_AIROHA7230 && priv->bb_type == BB_TYPE_11A)
+ vnt_mac_set_bb_type(priv, BB_TYPE_11G);
+ else
+ vnt_mac_set_bb_type(priv, priv->bb_type);
+
+ priv->packet_type = vnt_get_pkt_type(priv);
+
+ if (priv->bb_type == BB_TYPE_11A)
+ vnt_control_out_u8(priv, MESSAGE_REQUEST_BBREG, 0x88, 0x03);
+ else if (priv->bb_type == BB_TYPE_11B)
+ vnt_control_out_u8(priv, MESSAGE_REQUEST_BBREG, 0x88, 0x02);
+ else if (priv->bb_type == BB_TYPE_11G)
+ vnt_control_out_u8(priv, MESSAGE_REQUEST_BBREG, 0x88, 0x08);
+
+ vnt_update_ifs(priv);
+ vnt_set_rspinf(priv, (u8)priv->bb_type);
+
+ if (priv->bb_type == BB_TYPE_11A) {
+ if (priv->rf_type == RF_AIROHA7230) {
+ priv->bb_vga[0] = 0x20;
+
+ vnt_control_out_u8(priv, MESSAGE_REQUEST_BBREG,
+ 0xe7, priv->bb_vga[0]);
+ }
+
+ priv->bb_vga[2] = 0x10;
+ priv->bb_vga[3] = 0x10;
+ } else {
+ if (priv->rf_type == RF_AIROHA7230) {
+ priv->bb_vga[0] = 0x1c;
+
+ vnt_control_out_u8(priv, MESSAGE_REQUEST_BBREG,
+ 0xe7, priv->bb_vga[0]);
+ }
+
+ priv->bb_vga[2] = 0x0;
+ priv->bb_vga[3] = 0x0;
+ }
+
+ vnt_set_vga_gain_offset(priv, priv->bb_vga[0]);
+}
diff --git a/drivers/staging/vt6656/card.h b/drivers/staging/vt6656/card.h
new file mode 100644
index 000000000..03fc16788
--- /dev/null
+++ b/drivers/staging/vt6656/card.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 1996, 2003 VIA Networking Technologies, Inc.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * File: card.h
+ *
+ * Purpose: Provide functions to setup NIC operation mode
+ *
+ * Author: Tevin Chen
+ *
+ * Date: May 21, 1996
+ *
+ */
+
+#ifndef __CARD_H__
+#define __CARD_H__
+#include "device.h"
+
+/* init card type */
+
+#define CB_MAX_CHANNEL_24G 14
+#define CB_MAX_CHANNEL_5G 42 /* add channel9(5045MHz), 41==>42 */
+#define CB_MAX_CHANNEL (CB_MAX_CHANNEL_24G + CB_MAX_CHANNEL_5G)
+
+struct vnt_private;
+
+void vnt_set_channel(struct vnt_private *, u32);
+void vnt_set_rspinf(struct vnt_private *, u8);
+void vnt_update_ifs(struct vnt_private *);
+void vnt_update_top_rates(struct vnt_private *);
+int vnt_ofdm_min_rate(struct vnt_private *);
+void vnt_adjust_tsf(struct vnt_private *, u8, u64, u64);
+bool vnt_get_current_tsf(struct vnt_private *, u64 *);
+bool vnt_clear_current_tsf(struct vnt_private *);
+void vnt_reset_next_tbtt(struct vnt_private *, u16);
+void vnt_update_next_tbtt(struct vnt_private *, u64, u16);
+u64 vnt_get_next_tbtt(u64, u16);
+u64 vnt_get_tsf_offset(u8 byRxRate, u64 qwTSF1, u64 qwTSF2);
+int vnt_radio_power_off(struct vnt_private *);
+int vnt_radio_power_on(struct vnt_private *);
+u8 vnt_get_pkt_type(struct vnt_private *);
+void vnt_set_bss_mode(struct vnt_private *);
+
+#endif /* __CARD_H__ */
diff --git a/drivers/staging/vt6656/channel.c b/drivers/staging/vt6656/channel.c
new file mode 100644
index 000000000..8412d0532
--- /dev/null
+++ b/drivers/staging/vt6656/channel.c
@@ -0,0 +1,178 @@
+/*
+ * Copyright (c) 1996, 2003 VIA Networking Technologies, Inc.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ *
+ * File: channel.c
+ *
+ * Purpose: Channel number mapping
+ *
+ * Author: Lucas Lin
+ *
+ * Date: Dec 24, 2004
+ *
+ *
+ *
+ * Revision History:
+ * 01-18-2005 RobertYu: remove the for loop searching in
+ * ChannelValid, change ChannelRuleTab
+ * to lookup-type, reorder table items.
+ *
+ *
+ */
+
+#include "device.h"
+#include "channel.h"
+#include "rf.h"
+
+static struct ieee80211_rate vnt_rates_bg[] = {
+ { .bitrate = 10, .hw_value = RATE_1M },
+ { .bitrate = 20, .hw_value = RATE_2M },
+ { .bitrate = 55, .hw_value = RATE_5M },
+ { .bitrate = 110, .hw_value = RATE_11M },
+ { .bitrate = 60, .hw_value = RATE_6M },
+ { .bitrate = 90, .hw_value = RATE_9M },
+ { .bitrate = 120, .hw_value = RATE_12M },
+ { .bitrate = 180, .hw_value = RATE_18M },
+ { .bitrate = 240, .hw_value = RATE_24M },
+ { .bitrate = 360, .hw_value = RATE_36M },
+ { .bitrate = 480, .hw_value = RATE_48M },
+ { .bitrate = 540, .hw_value = RATE_54M },
+};
+
+static struct ieee80211_rate vnt_rates_a[] = {
+ { .bitrate = 60, .hw_value = RATE_6M },
+ { .bitrate = 90, .hw_value = RATE_9M },
+ { .bitrate = 120, .hw_value = RATE_12M },
+ { .bitrate = 180, .hw_value = RATE_18M },
+ { .bitrate = 240, .hw_value = RATE_24M },
+ { .bitrate = 360, .hw_value = RATE_36M },
+ { .bitrate = 480, .hw_value = RATE_48M },
+ { .bitrate = 540, .hw_value = RATE_54M },
+};
+
+static struct ieee80211_channel vnt_channels_2ghz[] = {
+ { .center_freq = 2412, .hw_value = 1 },
+ { .center_freq = 2417, .hw_value = 2 },
+ { .center_freq = 2422, .hw_value = 3 },
+ { .center_freq = 2427, .hw_value = 4 },
+ { .center_freq = 2432, .hw_value = 5 },
+ { .center_freq = 2437, .hw_value = 6 },
+ { .center_freq = 2442, .hw_value = 7 },
+ { .center_freq = 2447, .hw_value = 8 },
+ { .center_freq = 2452, .hw_value = 9 },
+ { .center_freq = 2457, .hw_value = 10 },
+ { .center_freq = 2462, .hw_value = 11 },
+ { .center_freq = 2467, .hw_value = 12 },
+ { .center_freq = 2472, .hw_value = 13 },
+ { .center_freq = 2484, .hw_value = 14 }
+};
+
+static struct ieee80211_channel vnt_channels_5ghz[] = {
+ { .center_freq = 4915, .hw_value = 15 },
+ { .center_freq = 4920, .hw_value = 16 },
+ { .center_freq = 4925, .hw_value = 17 },
+ { .center_freq = 4935, .hw_value = 18 },
+ { .center_freq = 4940, .hw_value = 19 },
+ { .center_freq = 4945, .hw_value = 20 },
+ { .center_freq = 4960, .hw_value = 21 },
+ { .center_freq = 4980, .hw_value = 22 },
+ { .center_freq = 5035, .hw_value = 23 },
+ { .center_freq = 5040, .hw_value = 24 },
+ { .center_freq = 5045, .hw_value = 25 },
+ { .center_freq = 5055, .hw_value = 26 },
+ { .center_freq = 5060, .hw_value = 27 },
+ { .center_freq = 5080, .hw_value = 28 },
+ { .center_freq = 5170, .hw_value = 29 },
+ { .center_freq = 5180, .hw_value = 30 },
+ { .center_freq = 5190, .hw_value = 31 },
+ { .center_freq = 5200, .hw_value = 32 },
+ { .center_freq = 5210, .hw_value = 33 },
+ { .center_freq = 5220, .hw_value = 34 },
+ { .center_freq = 5230, .hw_value = 35 },
+ { .center_freq = 5240, .hw_value = 36 },
+ { .center_freq = 5260, .hw_value = 37 },
+ { .center_freq = 5280, .hw_value = 38 },
+ { .center_freq = 5300, .hw_value = 39 },
+ { .center_freq = 5320, .hw_value = 40 },
+ { .center_freq = 5500, .hw_value = 41 },
+ { .center_freq = 5520, .hw_value = 42 },
+ { .center_freq = 5540, .hw_value = 43 },
+ { .center_freq = 5560, .hw_value = 44 },
+ { .center_freq = 5580, .hw_value = 45 },
+ { .center_freq = 5600, .hw_value = 46 },
+ { .center_freq = 5620, .hw_value = 47 },
+ { .center_freq = 5640, .hw_value = 48 },
+ { .center_freq = 5660, .hw_value = 49 },
+ { .center_freq = 5680, .hw_value = 50 },
+ { .center_freq = 5700, .hw_value = 51 },
+ { .center_freq = 5745, .hw_value = 52 },
+ { .center_freq = 5765, .hw_value = 53 },
+ { .center_freq = 5785, .hw_value = 54 },
+ { .center_freq = 5805, .hw_value = 55 },
+ { .center_freq = 5825, .hw_value = 56 }
+};
+
+static struct ieee80211_supported_band vnt_supported_2ghz_band = {
+ .channels = vnt_channels_2ghz,
+ .n_channels = ARRAY_SIZE(vnt_channels_2ghz),
+ .bitrates = vnt_rates_bg,
+ .n_bitrates = ARRAY_SIZE(vnt_rates_bg),
+};
+
+static struct ieee80211_supported_band vnt_supported_5ghz_band = {
+ .channels = vnt_channels_5ghz,
+ .n_channels = ARRAY_SIZE(vnt_channels_5ghz),
+ .bitrates = vnt_rates_a,
+ .n_bitrates = ARRAY_SIZE(vnt_rates_a),
+};
+
+void vnt_init_bands(struct vnt_private *priv)
+{
+ struct ieee80211_channel *ch;
+ int i;
+
+ switch (priv->rf_type) {
+ case RF_AIROHA7230:
+ case RF_VT3342A0:
+ default:
+ ch = vnt_channels_5ghz;
+
+ for (i = 0; i < ARRAY_SIZE(vnt_channels_5ghz); i++) {
+ ch[i].max_power = VNT_RF_MAX_POWER;
+ ch[i].flags = IEEE80211_CHAN_NO_HT40;
+ }
+
+ priv->hw->wiphy->bands[IEEE80211_BAND_5GHZ] =
+ &vnt_supported_5ghz_band;
+ /* fallthrough */
+ case RF_AL2230:
+ case RF_AL2230S:
+ case RF_VT3226:
+ case RF_VT3226D0:
+ ch = vnt_channels_2ghz;
+
+ for (i = 0; i < ARRAY_SIZE(vnt_channels_2ghz); i++) {
+ ch[i].max_power = VNT_RF_MAX_POWER;
+ ch[i].flags = IEEE80211_CHAN_NO_HT40;
+ }
+
+ priv->hw->wiphy->bands[IEEE80211_BAND_2GHZ] =
+ &vnt_supported_2ghz_band;
+ break;
+ }
+}
diff --git a/drivers/staging/vt6656/channel.h b/drivers/staging/vt6656/channel.h
new file mode 100644
index 000000000..21c080803
--- /dev/null
+++ b/drivers/staging/vt6656/channel.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 1996, 2003 VIA Networking Technologies, Inc.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ *
+ * File: channel.h
+ *
+ * Purpose: Country Regulation Rules header file
+ *
+ * Author: Lucas Lin
+ *
+ * Date: Dec 23, 2004
+ *
+ */
+
+#ifndef _CHANNEL_H_
+#define _CHANNEL_H_
+
+#include "device.h"
+
+void vnt_init_bands(struct vnt_private *);
+
+#endif /* _CHANNEL_H_ */
diff --git a/drivers/staging/vt6656/desc.h b/drivers/staging/vt6656/desc.h
new file mode 100644
index 000000000..f79af8513
--- /dev/null
+++ b/drivers/staging/vt6656/desc.h
@@ -0,0 +1,105 @@
+/*
+ * Copyright (c) 1996, 2003 VIA Networking Technologies, Inc.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * File: desc.h
+ *
+ * Purpose:The header file of descriptor
+ *
+ * Revision History:
+ *
+ * Author: Tevin Chen
+ *
+ * Date: May 21, 1996
+ *
+ */
+
+#ifndef __DESC_H__
+#define __DESC_H__
+
+#include <linux/types.h>
+#include <linux/mm.h>
+
+/* max transmit or receive buffer size */
+#define CB_MAX_BUF_SIZE 2900U /* NOTE: must be multiple of 4 */
+
+#define MAX_TOTAL_SIZE_WITH_ALL_HEADERS CB_MAX_BUF_SIZE
+
+#define MAX_INTERRUPT_SIZE 32
+
+#define CB_MAX_RX_DESC 128 /* max # of descriptors */
+#define CB_MIN_RX_DESC 16 /* min # of RX descriptors */
+#define CB_MAX_TX_DESC 128 /* max # of descriptors */
+#define CB_MIN_TX_DESC 16 /* min # of TX descriptors */
+
+/*
+ * bits in the RSR register
+ */
+#define RSR_ADDRBROAD 0x80
+#define RSR_ADDRMULTI 0x40
+#define RSR_ADDRUNI 0x00
+#define RSR_IVLDTYP 0x20 /* invalid packet type */
+#define RSR_IVLDLEN 0x10 /* invalid len (> 2312 byte) */
+#define RSR_BSSIDOK 0x08
+#define RSR_CRCOK 0x04
+#define RSR_BCNSSIDOK 0x02
+#define RSR_ADDROK 0x01
+
+/*
+ * bits in the new RSR register
+ */
+#define NEWRSR_DECRYPTOK 0x10
+#define NEWRSR_CFPIND 0x08
+#define NEWRSR_HWUTSF 0x04
+#define NEWRSR_BCNHITAID 0x02
+#define NEWRSR_BCNHITAID0 0x01
+
+/*
+ * bits in the TSR register
+ */
+#define TSR_RETRYTMO 0x08
+#define TSR_TMO 0x04
+#define TSR_ACKDATA 0x02
+#define TSR_VALID 0x01
+
+#define FIFOCTL_AUTO_FB_1 0x1000
+#define FIFOCTL_AUTO_FB_0 0x0800
+#define FIFOCTL_GRPACK 0x0400
+#define FIFOCTL_11GA 0x0300
+#define FIFOCTL_11GB 0x0200
+#define FIFOCTL_11B 0x0100
+#define FIFOCTL_11A 0x0000
+#define FIFOCTL_RTS 0x0080
+#define FIFOCTL_ISDMA0 0x0040
+#define FIFOCTL_GENINT 0x0020
+#define FIFOCTL_TMOEN 0x0010
+#define FIFOCTL_LRETRY 0x0008
+#define FIFOCTL_CRCDIS 0x0004
+#define FIFOCTL_NEEDACK 0x0002
+#define FIFOCTL_LHEAD 0x0001
+
+/* WMAC definition Frag Control */
+#define FRAGCTL_AES 0x0300
+#define FRAGCTL_TKIP 0x0200
+#define FRAGCTL_LEGACY 0x0100
+#define FRAGCTL_NONENCRYPT 0x0000
+#define FRAGCTL_ENDFRAG 0x0003
+#define FRAGCTL_MIDFRAG 0x0002
+#define FRAGCTL_STAFRAG 0x0001
+#define FRAGCTL_NONFRAG 0x0000
+
+#endif /* __DESC_H__ */
diff --git a/drivers/staging/vt6656/device.h b/drivers/staging/vt6656/device.h
new file mode 100644
index 000000000..f71d59fa3
--- /dev/null
+++ b/drivers/staging/vt6656/device.h
@@ -0,0 +1,407 @@
+/*
+ * Copyright (c) 1996, 2003 VIA Networking Technologies, Inc.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * File: device.h
+ *
+ * Purpose: MAC Data structure
+ *
+ * Author: Tevin Chen
+ *
+ * Date: Mar 17, 1997
+ *
+ */
+
+#ifndef __DEVICE_H__
+#define __DEVICE_H__
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/firmware.h>
+#include <linux/suspend.h>
+#include <linux/if_arp.h>
+#include <linux/wireless.h>
+#include <linux/timer.h>
+#include <linux/usb.h>
+#include <linux/crc32.h>
+#include <net/mac80211.h>
+
+#ifdef SIOCETHTOOL
+#define DEVICE_ETHTOOL_IOCTL_SUPPORT
+#include <linux/ethtool.h>
+#else
+#undef DEVICE_ETHTOOL_IOCTL_SUPPORT
+#endif
+
+#define RATE_1M 0
+#define RATE_2M 1
+#define RATE_5M 2
+#define RATE_11M 3
+#define RATE_6M 4
+#define RATE_9M 5
+#define RATE_12M 6
+#define RATE_18M 7
+#define RATE_24M 8
+#define RATE_36M 9
+#define RATE_48M 10
+#define RATE_54M 11
+#define RATE_AUTO 12
+
+#define MAX_RATE 12
+
+/*
+ * device specific
+ */
+
+#include "wcmd.h"
+#include "desc.h"
+#include "key.h"
+#include "card.h"
+
+#define VNT_USB_VENDOR_ID 0x160a
+#define VNT_USB_PRODUCT_ID 0x3184
+
+#define DEVICE_NAME "vt6656"
+#define DEVICE_FULL_DRV_NAM "VIA Networking Wireless LAN USB Driver"
+
+#define DEVICE_VERSION "mac80211"
+
+#define CONFIG_PATH "/etc/vntconfiguration.dat"
+
+#define MAX_UINTS 8
+#define OPTION_DEFAULT { [0 ... MAX_UINTS-1] = -1}
+
+#define DUPLICATE_RX_CACHE_LENGTH 5
+
+#define AUTO_FB_NONE 0
+#define AUTO_FB_0 1
+#define AUTO_FB_1 2
+
+#define FB_RATE0 0
+#define FB_RATE1 1
+
+/* Antenna Mode */
+#define ANT_A 0
+#define ANT_B 1
+#define ANT_DIVERSITY 2
+#define ANT_RXD_TXA 3
+#define ANT_RXD_TXB 4
+#define ANT_UNKNOWN 0xFF
+#define ANT_TXA 0
+#define ANT_TXB 1
+#define ANT_RXA 2
+#define ANT_RXB 3
+
+#define BB_VGA_LEVEL 4
+#define BB_VGA_CHANGE_THRESHOLD 3
+
+#define EEP_MAX_CONTEXT_SIZE 256
+
+/* Contents in the EEPROM */
+#define EEP_OFS_PAR 0x0
+#define EEP_OFS_ANTENNA 0x17
+#define EEP_OFS_RADIOCTL 0x18
+#define EEP_OFS_RFTYPE 0x1b
+#define EEP_OFS_MINCHANNEL 0x1c
+#define EEP_OFS_MAXCHANNEL 0x1d
+#define EEP_OFS_SIGNATURE 0x1e
+#define EEP_OFS_ZONETYPE 0x1f
+#define EEP_OFS_RFTABLE 0x20
+#define EEP_OFS_PWR_CCK 0x20
+#define EEP_OFS_SETPT_CCK 0x21
+#define EEP_OFS_PWR_OFDMG 0x23
+
+#define EEP_OFS_CALIB_TX_IQ 0x24
+#define EEP_OFS_CALIB_TX_DC 0x25
+#define EEP_OFS_CALIB_RX_IQ 0x26
+
+#define EEP_OFS_MAJOR_VER 0x2e
+#define EEP_OFS_MINOR_VER 0x2f
+
+#define EEP_OFS_CCK_PWR_TBL 0x30
+#define EEP_OFS_OFDM_PWR_TBL 0x40
+#define EEP_OFS_OFDMA_PWR_TBL 0x50
+
+/* Bits in EEP_OFS_ANTENNA */
+#define EEP_ANTENNA_MAIN 0x1
+#define EEP_ANTENNA_AUX 0x2
+#define EEP_ANTINV 0x4
+
+/* Bits in EEP_OFS_RADIOCTL */
+#define EEP_RADIOCTL_ENABLE 0x80
+
+/* control commands */
+#define MESSAGE_TYPE_READ 0x1
+#define MESSAGE_TYPE_WRITE 0x0
+#define MESSAGE_TYPE_LOCK_OR 0x2
+#define MESSAGE_TYPE_LOCK_AND 0x3
+#define MESSAGE_TYPE_WRITE_MASK 0x4
+#define MESSAGE_TYPE_CARDINIT 0x5
+#define MESSAGE_TYPE_INIT_RSP 0x6
+#define MESSAGE_TYPE_MACSHUTDOWN 0x7
+#define MESSAGE_TYPE_SETKEY 0x8
+#define MESSAGE_TYPE_CLRKEYENTRY 0x9
+#define MESSAGE_TYPE_WRITE_MISCFF 0xa
+#define MESSAGE_TYPE_SET_ANTMD 0xb
+#define MESSAGE_TYPE_SELECT_CHANNEL 0xc
+#define MESSAGE_TYPE_SET_TSFTBTT 0xd
+#define MESSAGE_TYPE_SET_SSTIFS 0xe
+#define MESSAGE_TYPE_CHANGE_BBTYPE 0xf
+#define MESSAGE_TYPE_DISABLE_PS 0x10
+#define MESSAGE_TYPE_WRITE_IFRF 0x11
+
+/* command read/write(index) */
+#define MESSAGE_REQUEST_MEM 0x1
+#define MESSAGE_REQUEST_BBREG 0x2
+#define MESSAGE_REQUEST_MACREG 0x3
+#define MESSAGE_REQUEST_EEPROM 0x4
+#define MESSAGE_REQUEST_TSF 0x5
+#define MESSAGE_REQUEST_TBTT 0x6
+#define MESSAGE_REQUEST_BBAGC 0x7
+#define MESSAGE_REQUEST_VERSION 0x8
+#define MESSAGE_REQUEST_RF_INIT 0x9
+#define MESSAGE_REQUEST_RF_INIT2 0xa
+#define MESSAGE_REQUEST_RF_CH0 0xb
+#define MESSAGE_REQUEST_RF_CH1 0xc
+#define MESSAGE_REQUEST_RF_CH2 0xd
+
+/* USB registers */
+#define USB_REG4 0x604
+
+#define DEVICE_INIT_COLD 0x0 /* cold init */
+#define DEVICE_INIT_RESET 0x1 /* reset init or Dx to D0 power remain */
+#define DEVICE_INIT_DXPL 0x2 /* Dx to D0 power lost init */
+
+/* Device init */
+struct vnt_cmd_card_init {
+ u8 init_class;
+ u8 exist_sw_net_addr;
+ u8 sw_net_addr[6];
+ u8 short_retry_limit;
+ u8 long_retry_limit;
+};
+
+struct vnt_rsp_card_init {
+ u8 status;
+ u8 net_addr[6];
+ u8 rf_type;
+ u8 min_channel;
+ u8 max_channel;
+};
+
+/* USB */
+
+/*
+ * Enum of context types for SendPacket
+ */
+enum {
+ CONTEXT_DATA_PACKET = 1,
+ CONTEXT_MGMT_PACKET,
+ CONTEXT_BEACON_PACKET
+};
+
+/* RCB (Receive Control Block) */
+struct vnt_rcb {
+ void *priv;
+ struct urb *urb;
+ struct sk_buff *skb;
+ int in_use;
+};
+
+/* used to track bulk out irps */
+struct vnt_usb_send_context {
+ void *priv;
+ struct sk_buff *skb;
+ struct urb *urb;
+ struct ieee80211_hdr *hdr;
+ unsigned int buf_len;
+ u32 frame_len;
+ u16 tx_hdr_size;
+ u16 tx_rate;
+ u8 type;
+ u8 pkt_no;
+ u8 pkt_type;
+ u8 need_ack;
+ u8 fb_option;
+ bool in_use;
+ unsigned char data[MAX_TOTAL_SIZE_WITH_ALL_HEADERS];
+};
+
+/*
+ * Structure to keep track of USB interrupt packets
+ */
+struct vnt_interrupt_buffer {
+ u8 *data_buf;
+ bool in_use;
+};
+
+/*++ NDIS related */
+
+enum {
+ STATUS_SUCCESS = 0,
+ STATUS_FAILURE,
+ STATUS_RESOURCES,
+ STATUS_PENDING,
+};
+
+/* flags for options */
+#define DEVICE_FLAGS_UNPLUG BIT(0)
+#define DEVICE_FLAGS_DISCONNECTED BIT(1)
+
+struct vnt_private {
+ /* mac80211 */
+ struct ieee80211_hw *hw;
+ struct ieee80211_vif *vif;
+ u8 mac_hw;
+ /* netdev */
+ struct usb_device *usb;
+
+ u64 tsf_time;
+ u8 rx_rate;
+
+ u32 rx_buf_sz;
+ int mc_list_count;
+
+ spinlock_t lock;
+ struct mutex usb_lock;
+
+ unsigned long flags;
+
+ /* USB */
+ struct urb *interrupt_urb;
+ u32 int_interval;
+
+ /* Variables to track resources for the BULK In Pipe */
+ struct vnt_rcb *rcb[CB_MAX_RX_DESC];
+ u32 num_rcb;
+
+ /* Variables to track resources for the BULK Out Pipe */
+ struct vnt_usb_send_context *tx_context[CB_MAX_TX_DESC];
+ u32 num_tx_context;
+
+ /* Variables to track resources for the Interrupt In Pipe */
+ struct vnt_interrupt_buffer int_buf;
+
+ /* Version control */
+ u16 firmware_version;
+ u8 local_id;
+ u8 rf_type;
+ u8 bb_rx_conf;
+
+ struct vnt_cmd_card_init init_command;
+ struct vnt_rsp_card_init init_response;
+ u8 current_net_addr[ETH_ALEN] __aligned(2);
+ u8 permanent_net_addr[ETH_ALEN] __aligned(2);
+
+ u8 exist_sw_net_addr;
+
+ u64 current_tsf;
+
+ /* 802.11 MAC specific */
+ u32 current_rssi;
+
+ /* Antenna Diversity */
+ int tx_rx_ant_inv;
+ u32 rx_antenna_sel;
+ u8 rx_antenna_mode;
+ u8 tx_antenna_mode;
+ u8 radio_ctl;
+
+ /* IFS & Cw */
+ u32 sifs; /* Current SIFS */
+ u32 difs; /* Current DIFS */
+ u32 eifs; /* Current EIFS */
+ u32 slot; /* Current SlotTime */
+
+ /* Rate */
+ u8 bb_type; /* 0: 11A, 1:11B, 2:11G */
+ u8 packet_type; /* 0:11a 1:11b 2:11gb 3:11ga */
+ u32 basic_rates;
+ u8 top_ofdm_basic_rate;
+ u8 top_cck_basic_rate;
+
+ u8 eeprom[EEP_MAX_CONTEXT_SIZE]; /*u32 alignment */
+
+ u8 preamble_type;
+
+ /* For RF Power table */
+ u8 cck_pwr;
+ u8 ofdm_pwr_g;
+ u8 ofdm_pwr_a;
+ u8 power;
+ u8 cck_pwr_tbl[14];
+ u8 ofdm_pwr_tbl[14];
+ u8 ofdm_a_pwr_tbl[42];
+
+ u16 current_rate;
+ u16 tx_rate_fb0;
+ u16 tx_rate_fb1;
+
+ u8 short_retry_limit;
+ u8 long_retry_limit;
+
+ enum nl80211_iftype op_mode;
+
+ int short_slot_time;
+
+ /* Power save */
+ u16 current_aid;
+
+ /* Beacon releated */
+ u16 seq_counter;
+
+ enum vnt_cmd_state command_state;
+
+ enum vnt_cmd command;
+
+ /* 802.11 counter */
+
+ enum vnt_cmd cmd_queue[CMD_Q_SIZE];
+ u32 cmd_dequeue_idx;
+ u32 cmd_enqueue_idx;
+ u32 free_cmd_queue;
+ int cmd_running;
+
+ unsigned long key_entry_inuse;
+
+ u8 auto_fb_ctrl;
+
+ /* For Update BaseBand VGA Gain Offset */
+ u8 bb_vga[BB_VGA_LEVEL];
+
+ u8 bb_pre_ed_rssi;
+ u8 bb_pre_ed_index;
+
+ /* command timer */
+ struct delayed_work run_command_work;
+
+ struct ieee80211_low_level_stats low_stats;
+};
+
+#define ADD_ONE_WITH_WRAP_AROUND(uVar, uModulo) { \
+ if ((uVar) >= ((uModulo) - 1)) \
+ (uVar) = 0; \
+ else \
+ (uVar)++; \
+}
+
+int vnt_init(struct vnt_private *priv);
+
+#endif
diff --git a/drivers/staging/vt6656/dpc.c b/drivers/staging/vt6656/dpc.c
new file mode 100644
index 000000000..e6367ed3b
--- /dev/null
+++ b/drivers/staging/vt6656/dpc.c
@@ -0,0 +1,186 @@
+/*
+ * Copyright (c) 1996, 2003 VIA Networking Technologies, Inc.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * File: dpc.c
+ *
+ * Purpose: handle dpc rx functions
+ *
+ * Author: Lyndon Chen
+ *
+ * Date: May 20, 2003
+ *
+ * Functions:
+ *
+ * Revision History:
+ *
+ */
+
+#include "dpc.h"
+#include "device.h"
+#include "mac.h"
+#include "baseband.h"
+#include "rf.h"
+
+int vnt_rx_data(struct vnt_private *priv, struct vnt_rcb *ptr_rcb,
+ unsigned long bytes_received)
+{
+ struct ieee80211_hw *hw = priv->hw;
+ struct ieee80211_supported_band *sband;
+ struct sk_buff *skb;
+ struct ieee80211_rx_status rx_status = { 0 };
+ struct ieee80211_hdr *hdr;
+ __le16 fc;
+ u8 *rsr, *new_rsr, *rssi, *frame;
+ __le64 *tsf_time;
+ u32 frame_size;
+ int ii, r;
+ u8 *rx_sts, *rx_rate, *sq, *sq_3;
+ u32 wbk_status;
+ u8 *skb_data;
+ u16 *pay_load_len;
+ u16 pay_load_with_padding;
+ u8 rate_idx = 0;
+ u8 rate[MAX_RATE] = {2, 4, 11, 22, 12, 18, 24, 36, 48, 72, 96, 108};
+ long rx_dbm;
+
+ skb = ptr_rcb->skb;
+
+ /* [31:16]RcvByteCount ( not include 4-byte Status ) */
+ wbk_status = *((u32 *)(skb->data));
+ frame_size = wbk_status >> 16;
+ frame_size += 4;
+
+ if (bytes_received != frame_size) {
+ dev_dbg(&priv->usb->dev, "------- WRONG Length 1\n");
+ return false;
+ }
+
+ if ((bytes_received > 2372) || (bytes_received <= 40)) {
+ /* Frame Size error drop this packet.*/
+ dev_dbg(&priv->usb->dev, "------ WRONG Length 2\n");
+ return false;
+ }
+
+ skb_data = (u8 *)skb->data;
+
+ rx_sts = skb_data+4;
+ rx_rate = skb_data+5;
+
+ /* real Frame Size = USBframe_size -4WbkStatus - 4RxStatus */
+ /* -8TSF - 4RSR - 4SQ3 - ?Padding */
+
+ /* if SQ3 the range is 24~27, if no SQ3 the range is 20~23 */
+
+ pay_load_len = (u16 *) (skb_data + 6);
+
+ /*Fix hardware bug => PLCP_Length error */
+ if (((bytes_received - (*pay_load_len)) > 27) ||
+ ((bytes_received - (*pay_load_len)) < 24) ||
+ (bytes_received < (*pay_load_len))) {
+ dev_dbg(&priv->usb->dev, "Wrong PLCP Length %x\n",
+ *pay_load_len);
+ return false;
+ }
+
+ sband = hw->wiphy->bands[hw->conf.chandef.chan->band];
+
+ for (r = RATE_1M; r < MAX_RATE; r++) {
+ if (*rx_rate == rate[r])
+ break;
+ }
+
+ priv->rx_rate = r;
+
+ for (ii = 0; ii < sband->n_bitrates; ii++) {
+ if (sband->bitrates[ii].hw_value == r) {
+ rate_idx = ii;
+ break;
+ }
+ }
+
+ if (ii == sband->n_bitrates) {
+ dev_dbg(&priv->usb->dev, "Wrong RxRate %x\n", *rx_rate);
+ return false;
+ }
+
+ pay_load_with_padding = ((*pay_load_len / 4) +
+ ((*pay_load_len % 4) ? 1 : 0)) * 4;
+
+ tsf_time = (__le64 *)(skb_data + 8 + pay_load_with_padding);
+
+ priv->tsf_time = le64_to_cpu(*tsf_time);
+
+ if (priv->bb_type == BB_TYPE_11G) {
+ sq_3 = skb_data + 8 + pay_load_with_padding + 12;
+ sq = sq_3;
+ } else {
+ sq = skb_data + 8 + pay_load_with_padding + 8;
+ sq_3 = sq;
+ }
+
+ new_rsr = skb_data + 8 + pay_load_with_padding + 9;
+ rssi = skb_data + 8 + pay_load_with_padding + 10;
+
+ rsr = skb_data + 8 + pay_load_with_padding + 11;
+ if (*rsr & (RSR_IVLDTYP | RSR_IVLDLEN))
+ return false;
+
+ frame_size = *pay_load_len;
+
+ vnt_rf_rssi_to_dbm(priv, *rssi, &rx_dbm);
+
+ priv->bb_pre_ed_rssi = (u8)rx_dbm + 1;
+ priv->current_rssi = priv->bb_pre_ed_rssi;
+
+ frame = skb_data + 8;
+
+ skb_pull(skb, 8);
+ skb_trim(skb, frame_size);
+
+ rx_status.mactime = priv->tsf_time;
+ rx_status.band = hw->conf.chandef.chan->band;
+ rx_status.signal = rx_dbm;
+ rx_status.flag = 0;
+ rx_status.freq = hw->conf.chandef.chan->center_freq;
+
+ if (!(*rsr & RSR_CRCOK))
+ rx_status.flag |= RX_FLAG_FAILED_FCS_CRC;
+
+ hdr = (struct ieee80211_hdr *)(skb->data);
+ fc = hdr->frame_control;
+
+ rx_status.rate_idx = rate_idx;
+
+ if (ieee80211_has_protected(fc)) {
+ if (priv->local_id > REV_ID_VT3253_A1) {
+ rx_status.flag |= RX_FLAG_DECRYPTED;
+
+ /* Drop packet */
+ if (!(*new_rsr & NEWRSR_DECRYPTOK)) {
+ dev_kfree_skb(skb);
+ return true;
+ }
+ }
+ }
+
+ memcpy(IEEE80211_SKB_RXCB(skb), &rx_status, sizeof(rx_status));
+
+ ieee80211_rx_irqsafe(priv->hw, skb);
+
+ return true;
+}
diff --git a/drivers/staging/vt6656/dpc.h b/drivers/staging/vt6656/dpc.h
new file mode 100644
index 000000000..95e0e83a4
--- /dev/null
+++ b/drivers/staging/vt6656/dpc.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 1996, 2003 VIA Networking Technologies, Inc.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * File: dpc.h
+ *
+ * Purpose:
+ *
+ * Author: Jerry Chen
+ *
+ * Date: Jun. 27, 2002
+ *
+ */
+
+#ifndef __DPC_H__
+#define __DPC_H__
+
+#include "device.h"
+
+int vnt_rx_data(struct vnt_private *, struct vnt_rcb *,
+ unsigned long bytes_received);
+
+#endif /* __RXTX_H__ */
diff --git a/drivers/staging/vt6656/firmware.c b/drivers/staging/vt6656/firmware.c
new file mode 100644
index 000000000..657051897
--- /dev/null
+++ b/drivers/staging/vt6656/firmware.c
@@ -0,0 +1,141 @@
+/*
+ * Copyright (c) 1996, 2003 VIA Networking Technologies, Inc.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ *
+ * File: baseband.c
+ *
+ * Purpose: Implement functions to access baseband
+ *
+ * Author: Yiching Chen
+ *
+ * Date: May 20, 2004
+ *
+ * Functions:
+ *
+ * Revision History:
+ *
+ */
+
+#include <linux/compiler.h>
+#include "firmware.h"
+#include "usbpipe.h"
+
+#define FIRMWARE_VERSION 0x133 /* version 1.51 */
+#define FIRMWARE_NAME "/*(DEBLOBBED)*/"
+
+#define FIRMWARE_CHUNK_SIZE 0x400
+
+int vnt_download_firmware(struct vnt_private *priv)
+{
+ struct device *dev = &priv->usb->dev;
+ const struct firmware *fw;
+ int status;
+ void *buffer = NULL;
+ bool result = false;
+ u16 length;
+ int ii, rc;
+
+ dev_dbg(dev, "---->Download firmware\n");
+
+ rc = reject_firmware(&fw, FIRMWARE_NAME, dev);
+ if (rc) {
+ dev_err(dev, "firmware file %s request failed (%d)\n",
+ FIRMWARE_NAME, rc);
+ goto out;
+ }
+
+ buffer = kmalloc(FIRMWARE_CHUNK_SIZE, GFP_KERNEL);
+ if (!buffer)
+ goto free_fw;
+
+ for (ii = 0; ii < fw->size; ii += FIRMWARE_CHUNK_SIZE) {
+ length = min_t(int, fw->size - ii, FIRMWARE_CHUNK_SIZE);
+ memcpy(buffer, fw->data + ii, length);
+
+ status = vnt_control_out(priv,
+ 0,
+ 0x1200+ii,
+ 0x0000,
+ length,
+ buffer);
+
+ dev_dbg(dev, "Download firmware...%d %zu\n", ii, fw->size);
+
+ if (status != STATUS_SUCCESS)
+ goto free_fw;
+ }
+
+ result = true;
+free_fw:
+ release_firmware(fw);
+
+out:
+ kfree(buffer);
+
+ return result;
+}
+/*(DEBLOBBED)*/
+
+int vnt_firmware_branch_to_sram(struct vnt_private *priv)
+{
+ int status;
+
+ dev_dbg(&priv->usb->dev, "---->Branch to Sram\n");
+
+ status = vnt_control_out(priv,
+ 1,
+ 0x1200,
+ 0x0000,
+ 0,
+ NULL);
+ return status == STATUS_SUCCESS;
+}
+
+int vnt_check_firmware_version(struct vnt_private *priv)
+{
+ int status;
+
+ status = vnt_control_in(priv,
+ MESSAGE_TYPE_READ,
+ 0,
+ MESSAGE_REQUEST_VERSION,
+ 2,
+ (u8 *)&priv->firmware_version);
+
+ dev_dbg(&priv->usb->dev, "Firmware Version [%04x]\n",
+ priv->firmware_version);
+
+ if (status != STATUS_SUCCESS) {
+ dev_dbg(&priv->usb->dev, "Firmware Invalid.\n");
+ return false;
+ }
+ if (priv->firmware_version == 0xFFFF) {
+ dev_dbg(&priv->usb->dev, "In Loader.\n");
+ return false;
+ }
+
+ dev_dbg(&priv->usb->dev, "Firmware Version [%04x]\n",
+ priv->firmware_version);
+
+ if (priv->firmware_version < FIRMWARE_VERSION) {
+ /* branch to loader for download new firmware */
+ vnt_firmware_branch_to_sram(priv);
+ return false;
+ }
+ return true;
+}
diff --git a/drivers/staging/vt6656/firmware.h b/drivers/staging/vt6656/firmware.h
new file mode 100644
index 000000000..d594dbe1c
--- /dev/null
+++ b/drivers/staging/vt6656/firmware.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 1996, 2003 VIA Networking Technologies, Inc.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ *
+ * File: firmware.h
+ *
+ * Purpose: Version and Release Information
+ *
+ * Author: Yiching Chen
+ *
+ * Date: May 20, 2004
+ *
+ */
+
+#ifndef __FIRMWARE_H__
+#define __FIRMWARE_H__
+
+#include "device.h"
+
+int vnt_download_firmware(struct vnt_private *);
+int vnt_firmware_branch_to_sram(struct vnt_private *);
+int vnt_check_firmware_version(struct vnt_private *);
+
+#endif /* __FIRMWARE_H__ */
diff --git a/drivers/staging/vt6656/int.c b/drivers/staging/vt6656/int.c
new file mode 100644
index 000000000..2ef70e470
--- /dev/null
+++ b/drivers/staging/vt6656/int.c
@@ -0,0 +1,166 @@
+/*
+ * Copyright (c) 1996, 2003 VIA Networking Technologies, Inc.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ *
+ * File: int.c
+ *
+ * Purpose: Handle USB interrupt endpoint
+ *
+ * Author: Jerry Chen
+ *
+ * Date: Apr. 2, 2004
+ *
+ * Functions:
+ *
+ * Revision History:
+ * 04-02-2004 Jerry Chen: Initial release
+ *
+ */
+
+#include "int.h"
+#include "mac.h"
+#include "power.h"
+#include "usbpipe.h"
+
+static const u8 fallback_rate0[5][5] = {
+ {RATE_18M, RATE_18M, RATE_12M, RATE_12M, RATE_12M},
+ {RATE_24M, RATE_24M, RATE_18M, RATE_12M, RATE_12M},
+ {RATE_36M, RATE_36M, RATE_24M, RATE_18M, RATE_18M},
+ {RATE_48M, RATE_48M, RATE_36M, RATE_24M, RATE_24M},
+ {RATE_54M, RATE_54M, RATE_48M, RATE_36M, RATE_36M}
+};
+
+static const u8 fallback_rate1[5][5] = {
+ {RATE_18M, RATE_18M, RATE_12M, RATE_6M, RATE_6M},
+ {RATE_24M, RATE_24M, RATE_18M, RATE_6M, RATE_6M},
+ {RATE_36M, RATE_36M, RATE_24M, RATE_12M, RATE_12M},
+ {RATE_48M, RATE_48M, RATE_24M, RATE_12M, RATE_12M},
+ {RATE_54M, RATE_54M, RATE_36M, RATE_18M, RATE_18M}
+};
+
+void vnt_int_start_interrupt(struct vnt_private *priv)
+{
+ unsigned long flags;
+ int status;
+
+ dev_dbg(&priv->usb->dev, "---->Interrupt Polling Thread\n");
+
+ spin_lock_irqsave(&priv->lock, flags);
+
+ status = vnt_start_interrupt_urb(priv);
+
+ spin_unlock_irqrestore(&priv->lock, flags);
+}
+
+static int vnt_int_report_rate(struct vnt_private *priv, u8 pkt_no, u8 tsr)
+{
+ struct vnt_usb_send_context *context;
+ struct ieee80211_tx_info *info;
+ struct ieee80211_rate *rate;
+ u8 tx_retry = (tsr & 0xf0) >> 4;
+ s8 idx;
+
+ if (pkt_no >= priv->num_tx_context)
+ return -EINVAL;
+
+ context = priv->tx_context[pkt_no];
+
+ if (!context->skb)
+ return -EINVAL;
+
+ info = IEEE80211_SKB_CB(context->skb);
+ idx = info->control.rates[0].idx;
+
+ if (context->fb_option && !(tsr & (TSR_TMO | TSR_RETRYTMO))) {
+ u8 tx_rate;
+ u8 retry = tx_retry;
+
+ rate = ieee80211_get_tx_rate(priv->hw, info);
+ tx_rate = rate->hw_value - RATE_18M;
+
+ if (retry > 4)
+ retry = 4;
+
+ if (context->fb_option == AUTO_FB_0)
+ tx_rate = fallback_rate0[tx_rate][retry];
+ else if (context->fb_option == AUTO_FB_1)
+ tx_rate = fallback_rate1[tx_rate][retry];
+
+ if (info->band == IEEE80211_BAND_5GHZ)
+ idx = tx_rate - RATE_6M;
+ else
+ idx = tx_rate;
+ }
+
+ ieee80211_tx_info_clear_status(info);
+
+ info->status.rates[0].count = tx_retry;
+
+ if (!(tsr & (TSR_TMO | TSR_RETRYTMO))) {
+ info->status.rates[0].idx = idx;
+ info->flags |= IEEE80211_TX_STAT_ACK;
+ }
+
+ ieee80211_tx_status_irqsafe(priv->hw, context->skb);
+
+ context->in_use = false;
+
+ return 0;
+}
+
+void vnt_int_process_data(struct vnt_private *priv)
+{
+ struct vnt_interrupt_data *int_data;
+ struct ieee80211_low_level_stats *low_stats = &priv->low_stats;
+
+ dev_dbg(&priv->usb->dev, "---->s_nsInterruptProcessData\n");
+
+ int_data = (struct vnt_interrupt_data *)priv->int_buf.data_buf;
+
+ if (int_data->tsr0 & TSR_VALID)
+ vnt_int_report_rate(priv, int_data->pkt0, int_data->tsr0);
+
+ if (int_data->tsr1 & TSR_VALID)
+ vnt_int_report_rate(priv, int_data->pkt1, int_data->tsr1);
+
+ if (int_data->tsr2 & TSR_VALID)
+ vnt_int_report_rate(priv, int_data->pkt2, int_data->tsr2);
+
+ if (int_data->tsr3 & TSR_VALID)
+ vnt_int_report_rate(priv, int_data->pkt3, int_data->tsr3);
+
+ if (int_data->isr0 != 0) {
+ if (int_data->isr0 & ISR_BNTX &&
+ priv->op_mode == NL80211_IFTYPE_AP)
+ vnt_schedule_command(priv, WLAN_CMD_BECON_SEND);
+
+ if (int_data->isr0 & ISR_TBTT) {
+ if (priv->hw->conf.flags & IEEE80211_CONF_PS)
+ vnt_schedule_command(priv,
+ WLAN_CMD_TBTT_WAKEUP);
+ }
+ priv->current_tsf = le64_to_cpu(int_data->tsf);
+
+ low_stats->dot11RTSSuccessCount += int_data->rts_success;
+ low_stats->dot11RTSFailureCount += int_data->rts_fail;
+ low_stats->dot11ACKFailureCount += int_data->ack_fail;
+ low_stats->dot11FCSErrorCount += int_data->fcs_err;
+ }
+
+ priv->int_buf.in_use = false;
+}
diff --git a/drivers/staging/vt6656/int.h b/drivers/staging/vt6656/int.h
new file mode 100644
index 000000000..154605c63
--- /dev/null
+++ b/drivers/staging/vt6656/int.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 1996, 2003 VIA Networking Technologies, Inc.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ *
+ * File: int.h
+ *
+ * Purpose:
+ *
+ * Author: Jerry Chen
+ *
+ * Date: Apr. 2, 2004
+ *
+ */
+
+#ifndef __INT_H__
+#define __INT_H__
+
+#include "device.h"
+
+struct vnt_interrupt_data {
+ u8 tsr0;
+ u8 pkt0;
+ u16 time0;
+ u8 tsr1;
+ u8 pkt1;
+ u16 time1;
+ u8 tsr2;
+ u8 pkt2;
+ u16 time2;
+ u8 tsr3;
+ u8 pkt3;
+ u16 time3;
+ __le64 tsf;
+ u8 isr0;
+ u8 isr1;
+ u8 rts_success;
+ u8 rts_fail;
+ u8 ack_fail;
+ u8 fcs_err;
+ u8 sw[2];
+} __packed;
+
+void vnt_int_start_interrupt(struct vnt_private *);
+void vnt_int_process_data(struct vnt_private *);
+
+#endif /* __INT_H__ */
diff --git a/drivers/staging/vt6656/key.c b/drivers/staging/vt6656/key.c
new file mode 100644
index 000000000..181745d8e
--- /dev/null
+++ b/drivers/staging/vt6656/key.c
@@ -0,0 +1,179 @@
+/*
+ * Copyright (c) 1996, 2003 VIA Networking Technologies, Inc.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ *
+ * File: key.c
+ *
+ * Purpose: Implement functions for 802.11i Key management
+ *
+ * Author: Jerry Chen
+ *
+ * Date: May 29, 2003
+ *
+ * Functions:
+ *
+ * Revision History:
+ *
+ */
+
+#include "mac.h"
+#include "key.h"
+#include "usbpipe.h"
+
+int vnt_key_init_table(struct vnt_private *priv)
+{
+ u8 i;
+ u8 data[MAX_KEY_TABLE];
+
+ for (i = 0; i < MAX_KEY_TABLE; i++)
+ data[i] = i;
+
+ return vnt_control_out(priv, MESSAGE_TYPE_CLRKEYENTRY,
+ 0, 0, ARRAY_SIZE(data), data);
+}
+
+static int vnt_set_keymode(struct ieee80211_hw *hw, u8 *mac_addr,
+ struct ieee80211_key_conf *key, u32 key_type, u32 mode,
+ bool onfly_latch)
+{
+ struct vnt_private *priv = hw->priv;
+ u8 broadcast[6] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
+ u16 key_mode = 0;
+ u32 entry = 0;
+ u8 *bssid;
+ u8 key_inx = key->keyidx;
+ u8 i;
+
+ if (mac_addr)
+ bssid = mac_addr;
+ else
+ bssid = &broadcast[0];
+
+ if (key_type != VNT_KEY_DEFAULTKEY) {
+ for (i = 0; i < (MAX_KEY_TABLE - 1); i++) {
+ if (!test_bit(i, &priv->key_entry_inuse)) {
+ set_bit(i, &priv->key_entry_inuse);
+
+ key->hw_key_idx = i;
+ entry = key->hw_key_idx;
+ break;
+ }
+ }
+ }
+
+ switch (key_type) {
+ /* fallthrough */
+ case VNT_KEY_DEFAULTKEY:
+ /* default key last entry */
+ entry = MAX_KEY_TABLE - 1;
+ key->hw_key_idx = entry;
+ case VNT_KEY_ALLGROUP:
+ key_mode |= VNT_KEY_ALLGROUP;
+ if (onfly_latch)
+ key_mode |= VNT_KEY_ONFLY_ALL;
+ case VNT_KEY_GROUP_ADDRESS:
+ key_mode |= mode;
+ case VNT_KEY_GROUP:
+ key_mode |= (mode << 4);
+ key_mode |= VNT_KEY_GROUP;
+ break;
+ case VNT_KEY_PAIRWISE:
+ key_mode |= mode;
+ key_inx = 4;
+ /* Don't save entry for pairwise key for station mode */
+ if (priv->op_mode == NL80211_IFTYPE_STATION)
+ clear_bit(entry, &priv->key_entry_inuse);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (onfly_latch)
+ key_mode |= VNT_KEY_ONFLY;
+
+ if (mode == KEY_CTL_WEP) {
+ if (key->keylen == WLAN_KEY_LEN_WEP40)
+ key->key[15] &= 0x7f;
+ if (key->keylen == WLAN_KEY_LEN_WEP104)
+ key->key[15] |= 0x80;
+ }
+
+ vnt_mac_set_keyentry(priv, key_mode, entry, key_inx, bssid, key->key);
+
+ return 0;
+}
+
+int vnt_set_keys(struct ieee80211_hw *hw, struct ieee80211_sta *sta,
+ struct ieee80211_vif *vif, struct ieee80211_key_conf *key)
+{
+ struct ieee80211_bss_conf *conf = &vif->bss_conf;
+ struct vnt_private *priv = hw->priv;
+ u8 *mac_addr = NULL;
+ u8 key_dec_mode = 0;
+ int ret = 0, u;
+
+ if (sta)
+ mac_addr = &sta->addr[0];
+
+ switch (key->cipher) {
+ case 0:
+ for (u = 0 ; u < MAX_KEY_TABLE; u++)
+ vnt_mac_disable_keyentry(priv, u);
+ return ret;
+
+ case WLAN_CIPHER_SUITE_WEP40:
+ case WLAN_CIPHER_SUITE_WEP104:
+ for (u = 0; u < MAX_KEY_TABLE; u++)
+ vnt_mac_disable_keyentry(priv, u);
+
+ vnt_set_keymode(hw, mac_addr, key, VNT_KEY_DEFAULTKEY,
+ KEY_CTL_WEP, true);
+
+ key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV;
+
+ return ret;
+ case WLAN_CIPHER_SUITE_TKIP:
+ key->flags |= IEEE80211_KEY_FLAG_GENERATE_MMIC;
+ key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV;
+
+ key_dec_mode = KEY_CTL_TKIP;
+
+ break;
+ case WLAN_CIPHER_SUITE_CCMP:
+ if (priv->local_id <= MAC_REVISION_A1)
+ return -EINVAL;
+
+ key_dec_mode = KEY_CTL_CCMP;
+
+ key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV;
+ }
+
+
+ if (key->flags & IEEE80211_KEY_FLAG_PAIRWISE) {
+ vnt_set_keymode(hw, mac_addr, key, VNT_KEY_PAIRWISE,
+ key_dec_mode, true);
+ } else {
+ vnt_set_keymode(hw, mac_addr, key, VNT_KEY_DEFAULTKEY,
+ key_dec_mode, true);
+
+ vnt_set_keymode(hw, (u8 *)conf->bssid, key,
+ VNT_KEY_GROUP_ADDRESS, key_dec_mode, true);
+ }
+
+ return 0;
+}
diff --git a/drivers/staging/vt6656/key.h b/drivers/staging/vt6656/key.h
new file mode 100644
index 000000000..3cb129105
--- /dev/null
+++ b/drivers/staging/vt6656/key.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 1996, 2003 VIA Networking Technologies, Inc.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ *
+ * File: key.h
+ *
+ * Purpose: Implement functions for 802.11i Key management
+ *
+ * Author: Jerry Chen
+ *
+ * Date: May 29, 2003
+ *
+ */
+
+#ifndef __KEY_H__
+#define __KEY_H__
+
+#include "device.h"
+
+#define MAX_KEY_TABLE 11
+
+#define KEY_CTL_WEP 0x00
+#define KEY_CTL_NONE 0x01
+#define KEY_CTL_TKIP 0x02
+#define KEY_CTL_CCMP 0x03
+
+#define VNT_KEY_DEFAULTKEY 0x1
+#define VNT_KEY_GROUP_ADDRESS 0x2
+#define VNT_KEY_ALLGROUP 0x4
+#define VNT_KEY_GROUP 0x40
+#define VNT_KEY_PAIRWISE 0x00
+#define VNT_KEY_ONFLY 0x8000
+#define VNT_KEY_ONFLY_ALL 0x4000
+
+int vnt_key_init_table(struct vnt_private *);
+
+int vnt_set_keys(struct ieee80211_hw *hw, struct ieee80211_sta *sta,
+ struct ieee80211_vif *vif, struct ieee80211_key_conf *key);
+
+#endif /* __KEY_H__ */
diff --git a/drivers/staging/vt6656/mac.c b/drivers/staging/vt6656/mac.c
new file mode 100644
index 000000000..5dfac05b9
--- /dev/null
+++ b/drivers/staging/vt6656/mac.c
@@ -0,0 +1,248 @@
+/*
+ * Copyright (c) 1996, 2003 VIA Networking Technologies, Inc.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ *
+ * File: mac.c
+ *
+ * Purpose: MAC routines
+ *
+ * Author: Tevin Chen
+ *
+ * Date: May 21, 1996
+ *
+ * Functions:
+ *
+ * Revision History:
+ */
+
+#include <linux/etherdevice.h>
+
+#include "desc.h"
+#include "mac.h"
+#include "usbpipe.h"
+
+/*
+ * Description:
+ * Write MAC Multicast Address Mask
+ *
+ * Parameters:
+ * In:
+ * mc_filter (mac filter)
+ * Out:
+ * none
+ *
+ * Return Value: none
+ *
+ */
+void vnt_mac_set_filter(struct vnt_private *priv, u64 mc_filter)
+{
+ __le64 le_mc = cpu_to_le64(mc_filter);
+
+ vnt_control_out(priv, MESSAGE_TYPE_WRITE, MAC_REG_MAR0,
+ MESSAGE_REQUEST_MACREG, sizeof(le_mc), (u8 *)&le_mc);
+}
+
+/*
+ * Description:
+ * Shut Down MAC
+ *
+ * Parameters:
+ * In:
+ * Out:
+ * none
+ *
+ *
+ */
+void vnt_mac_shutdown(struct vnt_private *priv)
+{
+ vnt_control_out(priv, MESSAGE_TYPE_MACSHUTDOWN, 0, 0, 0, NULL);
+}
+
+void vnt_mac_set_bb_type(struct vnt_private *priv, u8 type)
+{
+ u8 data[2];
+
+ data[0] = type;
+ data[1] = EnCFG_BBType_MASK;
+
+ vnt_control_out(priv, MESSAGE_TYPE_WRITE_MASK, MAC_REG_ENCFG0,
+ MESSAGE_REQUEST_MACREG, ARRAY_SIZE(data), data);
+}
+
+/*
+ * Description:
+ * Disable the Key Entry by MISCFIFO
+ *
+ * Parameters:
+ * In:
+ * dwIoBase - Base Address for MAC
+ *
+ * Out:
+ * none
+ *
+ * Return Value: none
+ *
+ */
+void vnt_mac_disable_keyentry(struct vnt_private *priv, u8 entry_idx)
+{
+ vnt_control_out(priv, MESSAGE_TYPE_CLRKEYENTRY, 0, 0,
+ sizeof(entry_idx), &entry_idx);
+}
+
+/*
+ * Description:
+ * Set the Key by MISCFIFO
+ *
+ * Parameters:
+ * In:
+ * dwIoBase - Base Address for MAC
+ *
+ * Out:
+ * none
+ *
+ * Return Value: none
+ *
+ */
+void vnt_mac_set_keyentry(struct vnt_private *priv, u16 key_ctl, u32 entry_idx,
+ u32 key_idx, u8 *addr, u8 *key)
+{
+ struct vnt_mac_set_key set_key;
+ u16 offset;
+
+ offset = MISCFIFO_KEYETRY0;
+ offset += (entry_idx * MISCFIFO_KEYENTRYSIZE);
+
+ set_key.u.write.key_ctl = cpu_to_le16(key_ctl);
+ ether_addr_copy(set_key.u.write.addr, addr);
+
+ /* swap over swap[0] and swap[1] to get correct write order */
+ swap(set_key.u.swap[0], set_key.u.swap[1]);
+
+ memcpy(set_key.key, key, WLAN_KEY_LEN_CCMP);
+
+ dev_dbg(&priv->usb->dev, "offset %d key ctl %d set key %24ph\n",
+ offset, key_ctl, (u8 *)&set_key);
+
+ vnt_control_out(priv, MESSAGE_TYPE_SETKEY, offset,
+ (u16)key_idx, sizeof(struct vnt_mac_set_key), (u8 *)&set_key);
+}
+
+void vnt_mac_reg_bits_off(struct vnt_private *priv, u8 reg_ofs, u8 bits)
+{
+ u8 data[2];
+
+ data[0] = 0;
+ data[1] = bits;
+
+ vnt_control_out(priv, MESSAGE_TYPE_WRITE_MASK,
+ reg_ofs, MESSAGE_REQUEST_MACREG, ARRAY_SIZE(data), data);
+}
+
+void vnt_mac_reg_bits_on(struct vnt_private *priv, u8 reg_ofs, u8 bits)
+{
+ u8 data[2];
+
+ data[0] = bits;
+ data[1] = bits;
+
+ vnt_control_out(priv, MESSAGE_TYPE_WRITE_MASK,
+ reg_ofs, MESSAGE_REQUEST_MACREG, ARRAY_SIZE(data), data);
+}
+
+void vnt_mac_write_word(struct vnt_private *priv, u8 reg_ofs, u16 word)
+{
+ u8 data[2];
+
+ data[0] = (u8)(word & 0xff);
+ data[1] = (u8)(word >> 8);
+
+ vnt_control_out(priv, MESSAGE_TYPE_WRITE,
+ reg_ofs, MESSAGE_REQUEST_MACREG, ARRAY_SIZE(data), data);
+}
+
+void vnt_mac_set_bssid_addr(struct vnt_private *priv, u8 *addr)
+{
+ vnt_control_out(priv, MESSAGE_TYPE_WRITE, MAC_REG_BSSID0,
+ MESSAGE_REQUEST_MACREG, ETH_ALEN, addr);
+}
+
+void vnt_mac_enable_protect_mode(struct vnt_private *priv)
+{
+ u8 data[2];
+
+ data[0] = EnCFG_ProtectMd;
+ data[1] = EnCFG_ProtectMd;
+
+ vnt_control_out(priv, MESSAGE_TYPE_WRITE_MASK,
+ MAC_REG_ENCFG0, MESSAGE_REQUEST_MACREG, ARRAY_SIZE(data), data);
+}
+
+void vnt_mac_disable_protect_mode(struct vnt_private *priv)
+{
+ u8 data[2];
+
+ data[0] = 0;
+ data[1] = EnCFG_ProtectMd;
+
+ vnt_control_out(priv, MESSAGE_TYPE_WRITE_MASK,
+ MAC_REG_ENCFG0, MESSAGE_REQUEST_MACREG, ARRAY_SIZE(data), data);
+}
+
+void vnt_mac_enable_barker_preamble_mode(struct vnt_private *priv)
+{
+ u8 data[2];
+
+ data[0] = EnCFG_BarkerPream;
+ data[1] = EnCFG_BarkerPream;
+
+ vnt_control_out(priv, MESSAGE_TYPE_WRITE_MASK,
+ MAC_REG_ENCFG2, MESSAGE_REQUEST_MACREG, ARRAY_SIZE(data), data);
+}
+
+void vnt_mac_disable_barker_preamble_mode(struct vnt_private *priv)
+{
+ u8 data[2];
+
+ data[0] = 0;
+ data[1] = EnCFG_BarkerPream;
+
+ vnt_control_out(priv, MESSAGE_TYPE_WRITE_MASK,
+ MAC_REG_ENCFG2, MESSAGE_REQUEST_MACREG, ARRAY_SIZE(data), data);
+}
+
+void vnt_mac_set_beacon_interval(struct vnt_private *priv, u16 interval)
+{
+ u8 data[2];
+
+ data[0] = (u8)(interval & 0xff);
+ data[1] = (u8)(interval >> 8);
+
+ vnt_control_out(priv, MESSAGE_TYPE_WRITE,
+ MAC_REG_BI, MESSAGE_REQUEST_MACREG, ARRAY_SIZE(data), data);
+}
+
+void vnt_mac_set_led(struct vnt_private *priv, u8 state, u8 led)
+{
+ u8 data[2];
+
+ data[0] = led;
+ data[1] = state;
+
+ vnt_control_out(priv, MESSAGE_TYPE_WRITE_MASK, MAC_REG_PAPEDELAY,
+ MESSAGE_REQUEST_MACREG, ARRAY_SIZE(data), data);
+}
diff --git a/drivers/staging/vt6656/mac.h b/drivers/staging/vt6656/mac.h
new file mode 100644
index 000000000..d53fcef87
--- /dev/null
+++ b/drivers/staging/vt6656/mac.h
@@ -0,0 +1,387 @@
+/*
+ * Copyright (c) 1996, 2003 VIA Networking Technologies, Inc.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ *
+ * File: mac.h
+ *
+ * Purpose: MAC routines
+ *
+ * Author: Tevin Chen
+ *
+ * Date: May 21, 1996
+ *
+ * Revision History:
+ * 07-01-2003 Bryan YC Fan: Re-write codes to support VT3253 spec.
+ * 08-25-2003 Kyle Hsu: Porting MAC functions from sim53.
+ * 09-03-2003 Bryan YC Fan: Add MACvDisableProtectMD & MACvEnableProtectMD
+ */
+
+#ifndef __MAC_H__
+#define __MAC_H__
+
+#include "device.h"
+
+#define REV_ID_VT3253_A0 0x00
+#define REV_ID_VT3253_A1 0x01
+#define REV_ID_VT3253_B0 0x08
+#define REV_ID_VT3253_B1 0x09
+
+/* Registers in the MAC */
+#define MAC_REG_BISTCMD 0x04
+#define MAC_REG_BISTSR0 0x05
+#define MAC_REG_BISTSR1 0x06
+#define MAC_REG_BISTSR2 0x07
+#define MAC_REG_I2MCSR 0x08
+#define MAC_REG_I2MTGID 0x09
+#define MAC_REG_I2MTGAD 0x0a
+#define MAC_REG_I2MCFG 0x0b
+#define MAC_REG_I2MDIPT 0x0c
+#define MAC_REG_I2MDOPT 0x0e
+#define MAC_REG_USBSUS 0x0f
+
+#define MAC_REG_LOCALID 0x14
+#define MAC_REG_TESTCFG 0x15
+#define MAC_REG_JUMPER0 0x16
+#define MAC_REG_JUMPER1 0x17
+#define MAC_REG_TMCTL 0x18
+#define MAC_REG_TMDATA0 0x1c
+#define MAC_REG_TMDATA1 0x1d
+#define MAC_REG_TMDATA2 0x1e
+#define MAC_REG_TMDATA3 0x1f
+
+/* MAC Parameter related */
+#define MAC_REG_LRT 0x20
+#define MAC_REG_SRT 0x21
+#define MAC_REG_SIFS 0x22
+#define MAC_REG_DIFS 0x23
+#define MAC_REG_EIFS 0x24
+#define MAC_REG_SLOT 0x25
+#define MAC_REG_BI 0x26
+#define MAC_REG_CWMAXMIN0 0x28
+#define MAC_REG_LINKOFFTOTM 0x2a
+#define MAC_REG_SWTMOT 0x2b
+#define MAC_REG_RTSOKCNT 0x2c
+#define MAC_REG_RTSFAILCNT 0x2d
+#define MAC_REG_ACKFAILCNT 0x2e
+#define MAC_REG_FCSERRCNT 0x2f
+
+/* TSF Related */
+#define MAC_REG_TSFCNTR 0x30
+#define MAC_REG_NEXTTBTT 0x38
+#define MAC_REG_TSFOFST 0x40
+#define MAC_REG_TFTCTL 0x48
+
+/* WMAC Control/Status Related */
+#define MAC_REG_ENCFG0 0x4c
+#define MAC_REG_ENCFG1 0x4d
+#define MAC_REG_ENCFG2 0x4e
+
+#define MAC_REG_CFG 0x50
+#define MAC_REG_TEST 0x52
+#define MAC_REG_HOSTCR 0x54
+#define MAC_REG_MACCR 0x55
+#define MAC_REG_RCR 0x56
+#define MAC_REG_TCR 0x57
+#define MAC_REG_IMR 0x58
+#define MAC_REG_ISR 0x5c
+#define MAC_REG_ISR1 0x5d
+
+/* Power Saving Related */
+#define MAC_REG_PSCFG 0x60
+#define MAC_REG_PSCTL 0x61
+#define MAC_REG_PSPWRSIG 0x62
+#define MAC_REG_BBCR13 0x63
+#define MAC_REG_AIDATIM 0x64
+#define MAC_REG_PWBT 0x66
+#define MAC_REG_WAKEOKTMR 0x68
+#define MAC_REG_CALTMR 0x69
+#define MAC_REG_SYNSPACCNT 0x6a
+#define MAC_REG_WAKSYNOPT 0x6b
+
+/* Baseband/IF Control Group */
+#define MAC_REG_BBREGCTL 0x6c
+#define MAC_REG_CHANNEL 0x6d
+#define MAC_REG_BBREGADR 0x6e
+#define MAC_REG_BBREGDATA 0x6f
+#define MAC_REG_IFREGCTL 0x70
+#define MAC_REG_IFDATA 0x71
+#define MAC_REG_ITRTMSET 0x74
+#define MAC_REG_PAPEDELAY 0x77
+#define MAC_REG_SOFTPWRCTL 0x78
+#define MAC_REG_SOFTPWRCTL2 0x79
+#define MAC_REG_GPIOCTL0 0x7a
+#define MAC_REG_GPIOCTL1 0x7b
+
+/* MiscFF PIO related */
+#define MAC_REG_MISCFFNDEX 0xbc
+#define MAC_REG_MISCFFCTL 0xbe
+#define MAC_REG_MISCFFDATA 0xc0
+
+/* MAC Configuration Group */
+#define MAC_REG_PAR0 0xc4
+#define MAC_REG_PAR4 0xc8
+#define MAC_REG_BSSID0 0xcc
+#define MAC_REG_BSSID4 0xd0
+#define MAC_REG_MAR0 0xd4
+#define MAC_REG_MAR4 0xd8
+
+/* MAC RSPPKT INFO Group */
+#define MAC_REG_RSPINF_B_1 0xdC
+#define MAC_REG_RSPINF_B_2 0xe0
+#define MAC_REG_RSPINF_B_5 0xe4
+#define MAC_REG_RSPINF_B_11 0xe8
+#define MAC_REG_RSPINF_A_6 0xec
+#define MAC_REG_RSPINF_A_9 0xee
+#define MAC_REG_RSPINF_A_12 0xf0
+#define MAC_REG_RSPINF_A_18 0xf2
+#define MAC_REG_RSPINF_A_24 0xf4
+#define MAC_REG_RSPINF_A_36 0xf6
+#define MAC_REG_RSPINF_A_48 0xf8
+#define MAC_REG_RSPINF_A_54 0xfa
+#define MAC_REG_RSPINF_A_72 0xfc
+
+/* Bits in the I2MCFG EEPROM register */
+#define I2MCFG_BOUNDCTL 0x80
+#define I2MCFG_WAITCTL 0x20
+#define I2MCFG_SCLOECTL 0x10
+#define I2MCFG_WBUSYCTL 0x08
+#define I2MCFG_NORETRY 0x04
+#define I2MCFG_I2MLDSEQ 0x02
+#define I2MCFG_I2CMFAST 0x01
+
+/* Bits in the I2MCSR EEPROM register */
+#define I2MCSR_EEMW 0x80
+#define I2MCSR_EEMR 0x40
+#define I2MCSR_AUTOLD 0x08
+#define I2MCSR_NACK 0x02
+#define I2MCSR_DONE 0x01
+
+/* Bits in the TMCTL register */
+#define TMCTL_TSUSP 0x04
+#define TMCTL_TMD 0x02
+#define TMCTL_TE 0x01
+
+/* Bits in the TFTCTL register */
+#define TFTCTL_HWUTSF 0x80
+#define TFTCTL_TBTTSYNC 0x40
+#define TFTCTL_HWUTSFEN 0x20
+#define TFTCTL_TSFCNTRRD 0x10
+#define TFTCTL_TBTTSYNCEN 0x08
+#define TFTCTL_TSFSYNCEN 0x04
+#define TFTCTL_TSFCNTRST 0x02
+#define TFTCTL_TSFCNTREN 0x01
+
+/* Bits in the EnhanceCFG_0 register */
+#define EnCFG_BBType_a 0x00
+#define EnCFG_BBType_b 0x01
+#define EnCFG_BBType_g 0x02
+#define EnCFG_BBType_MASK 0x03
+#define EnCFG_ProtectMd 0x20
+
+/* Bits in the EnhanceCFG_1 register */
+#define EnCFG_BcnSusInd 0x01
+#define EnCFG_BcnSusClr 0x02
+
+/* Bits in the EnhanceCFG_2 register */
+#define EnCFG_NXTBTTCFPSTR 0x01
+#define EnCFG_BarkerPream 0x02
+#define EnCFG_PktBurstMode 0x04
+
+/* Bits in the CFG register */
+#define CFG_TKIPOPT 0x80
+#define CFG_RXDMAOPT 0x40
+#define CFG_TMOT_SW 0x20
+#define CFG_TMOT_HWLONG 0x10
+#define CFG_TMOT_HW 0x00
+#define CFG_CFPENDOPT 0x08
+#define CFG_BCNSUSEN 0x04
+#define CFG_NOTXTIMEOUT 0x02
+#define CFG_NOBUFOPT 0x01
+
+/* Bits in the TEST register */
+#define TEST_LBEXT 0x80
+#define TEST_LBINT 0x40
+#define TEST_LBNONE 0x00
+#define TEST_SOFTINT 0x20
+#define TEST_CONTTX 0x10
+#define TEST_TXPE 0x08
+#define TEST_NAVDIS 0x04
+#define TEST_NOCTS 0x02
+#define TEST_NOACK 0x01
+
+/* Bits in the HOSTCR register */
+#define HOSTCR_TXONST 0x80
+#define HOSTCR_RXONST 0x40
+#define HOSTCR_ADHOC 0x20
+#define HOSTCR_AP 0x10
+#define HOSTCR_TXON 0x08
+#define HOSTCR_RXON 0x04
+#define HOSTCR_MACEN 0x02
+#define HOSTCR_SOFTRST 0x01
+
+/* Bits in the MACCR register */
+#define MACCR_SYNCFLUSHOK 0x04
+#define MACCR_SYNCFLUSH 0x02
+#define MACCR_CLRNAV 0x01
+
+/* Bits in the RCR register */
+#define RCR_SSID 0x80
+#define RCR_RXALLTYPE 0x40
+#define RCR_UNICAST 0x20
+#define RCR_BROADCAST 0x10
+#define RCR_MULTICAST 0x08
+#define RCR_WPAERR 0x04
+#define RCR_ERRCRC 0x02
+#define RCR_BSSID 0x01
+
+/* Bits in the TCR register */
+#define TCR_SYNCDCFOPT 0x02
+#define TCR_AUTOBCNTX 0x01
+
+/* ISR1 */
+#define ISR_GPIO3 0x40
+#define ISR_RXNOBUF 0x08
+#define ISR_MIBNEARFULL 0x04
+#define ISR_SOFTINT 0x02
+#define ISR_FETALERR 0x01
+
+#define LEDSTS_STS 0x06
+#define LEDSTS_TMLEN 0x78
+#define LEDSTS_OFF 0x00
+#define LEDSTS_ON 0x02
+#define LEDSTS_SLOW 0x04
+#define LEDSTS_INTER 0x06
+
+/* ISR0 */
+#define ISR_WATCHDOG 0x80
+#define ISR_SOFTTIMER 0x40
+#define ISR_GPIO0 0x20
+#define ISR_TBTT 0x10
+#define ISR_RXDMA0 0x08
+#define ISR_BNTX 0x04
+#define ISR_ACTX 0x01
+
+/* Bits in the PSCFG register */
+#define PSCFG_PHILIPMD 0x40
+#define PSCFG_WAKECALEN 0x20
+#define PSCFG_WAKETMREN 0x10
+#define PSCFG_BBPSPROG 0x08
+#define PSCFG_WAKESYN 0x04
+#define PSCFG_SLEEPSYN 0x02
+#define PSCFG_AUTOSLEEP 0x01
+
+/* Bits in the PSCTL register */
+#define PSCTL_WAKEDONE 0x20
+#define PSCTL_PS 0x10
+#define PSCTL_GO2DOZE 0x08
+#define PSCTL_LNBCN 0x04
+#define PSCTL_ALBCN 0x02
+#define PSCTL_PSEN 0x01
+
+/* Bits in the PSPWSIG register */
+#define PSSIG_WPE3 0x80
+#define PSSIG_WPE2 0x40
+#define PSSIG_WPE1 0x20
+#define PSSIG_WRADIOPE 0x10
+#define PSSIG_SPE3 0x08
+#define PSSIG_SPE2 0x04
+#define PSSIG_SPE1 0x02
+#define PSSIG_SRADIOPE 0x01
+
+/* Bits in the BBREGCTL register */
+#define BBREGCTL_DONE 0x04
+#define BBREGCTL_REGR 0x02
+#define BBREGCTL_REGW 0x01
+
+/* Bits in the IFREGCTL register */
+#define IFREGCTL_DONE 0x04
+#define IFREGCTL_IFRF 0x02
+#define IFREGCTL_REGW 0x01
+
+/* Bits in the SOFTPWRCTL register */
+#define SOFTPWRCTL_RFLEOPT 0x08
+#define SOFTPWRCTL_TXPEINV 0x02
+#define SOFTPWRCTL_SWPECTI 0x01
+#define SOFTPWRCTL_SWPAPE 0x20
+#define SOFTPWRCTL_SWCALEN 0x10
+#define SOFTPWRCTL_SWRADIO_PE 0x08
+#define SOFTPWRCTL_SWPE2 0x04
+#define SOFTPWRCTL_SWPE1 0x02
+#define SOFTPWRCTL_SWPE3 0x01
+
+/* Bits in the GPIOCTL1 register */
+#define GPIO3_MD 0x20
+#define GPIO3_DATA 0x40
+#define GPIO3_INTMD 0x80
+
+/* Bits in the MISCFFCTL register */
+#define MISCFFCTL_WRITE 0x0001
+
+/* Loopback mode */
+#define MAC_LB_EXT 0x02
+#define MAC_LB_INTERNAL 0x01
+#define MAC_LB_NONE 0x00
+
+/* Ethernet address filter type */
+#define PKT_TYPE_NONE 0x00 /* turn off receiver */
+#define PKT_TYPE_ALL_MULTICAST 0x80
+#define PKT_TYPE_PROMISCUOUS 0x40
+#define PKT_TYPE_DIRECTED 0x20 /* obselete */
+#define PKT_TYPE_BROADCAST 0x10
+#define PKT_TYPE_MULTICAST 0x08
+#define PKT_TYPE_ERROR_WPA 0x04
+#define PKT_TYPE_ERROR_CRC 0x02
+#define PKT_TYPE_BSSID 0x01
+
+#define Default_BI 0x200
+
+/* MiscFIFO Offset */
+#define MISCFIFO_KEYETRY0 32
+#define MISCFIFO_KEYENTRYSIZE 22
+
+#define MAC_REVISION_A0 0x00
+#define MAC_REVISION_A1 0x01
+
+struct vnt_mac_set_key {
+ union {
+ struct {
+ u8 addr[ETH_ALEN];
+ __le16 key_ctl;
+ } write __packed;
+ u32 swap[2];
+ } u;
+ u8 key[WLAN_KEY_LEN_CCMP];
+} __packed;
+
+void vnt_mac_set_filter(struct vnt_private *, u64);
+void vnt_mac_shutdown(struct vnt_private *);
+void vnt_mac_set_bb_type(struct vnt_private *, u8);
+void vnt_mac_disable_keyentry(struct vnt_private *, u8);
+void vnt_mac_set_keyentry(struct vnt_private *, u16, u32, u32, u8 *, u8 *);
+void vnt_mac_reg_bits_off(struct vnt_private *, u8, u8);
+void vnt_mac_reg_bits_on(struct vnt_private *, u8, u8);
+void vnt_mac_write_word(struct vnt_private *, u8, u16);
+void vnt_mac_set_bssid_addr(struct vnt_private *, u8 *);
+void vnt_mac_enable_protect_mode(struct vnt_private *);
+void vnt_mac_disable_protect_mode(struct vnt_private *);
+void vnt_mac_enable_barker_preamble_mode(struct vnt_private *);
+void vnt_mac_disable_barker_preamble_mode(struct vnt_private *);
+void vnt_mac_set_beacon_interval(struct vnt_private *, u16);
+void vnt_mac_set_led(struct vnt_private *priv, u8, u8);
+
+#endif /* __MAC_H__ */
diff --git a/drivers/staging/vt6656/main_usb.c b/drivers/staging/vt6656/main_usb.c
new file mode 100644
index 000000000..766fdcece
--- /dev/null
+++ b/drivers/staging/vt6656/main_usb.c
@@ -0,0 +1,1059 @@
+/*
+ * Copyright (c) 1996, 2003 VIA Networking Technologies, Inc.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * File: main_usb.c
+ *
+ * Purpose: driver entry for initial, open, close, tx and rx.
+ *
+ * Author: Lyndon Chen
+ *
+ * Date: Dec 8, 2005
+ *
+ * Functions:
+ *
+ * vt6656_probe - module initial (insmod) driver entry
+ * vnt_free_tx_bufs - free tx buffer function
+ * vnt_init_registers- initial MAC & BBP & RF internal registers.
+ *
+ * Revision History:
+ */
+#undef __NO_VERSION__
+
+#include <linux/etherdevice.h>
+#include <linux/file.h>
+#include "device.h"
+#include "card.h"
+#include "baseband.h"
+#include "mac.h"
+#include "power.h"
+#include "wcmd.h"
+#include "rxtx.h"
+#include "dpc.h"
+#include "rf.h"
+#include "firmware.h"
+#include "usbpipe.h"
+#include "channel.h"
+#include "int.h"
+
+/*
+ * define module options
+ */
+
+/* version information */
+#define DRIVER_AUTHOR \
+ "VIA Networking Technologies, Inc., <lyndonchen@vntek.com.tw>"
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION(DEVICE_FULL_DRV_NAM);
+
+#define RX_DESC_DEF0 64
+static int vnt_rx_buffers = RX_DESC_DEF0;
+module_param_named(rx_buffers, vnt_rx_buffers, int, 0644);
+MODULE_PARM_DESC(rx_buffers, "Number of receive usb rx buffers");
+
+#define TX_DESC_DEF0 64
+static int vnt_tx_buffers = TX_DESC_DEF0;
+module_param_named(tx_buffers, vnt_tx_buffers, int, 0644);
+MODULE_PARM_DESC(tx_buffers, "Number of receive usb tx buffers");
+
+#define RTS_THRESH_DEF 2347
+#define FRAG_THRESH_DEF 2346
+#define SHORT_RETRY_DEF 8
+#define LONG_RETRY_DEF 4
+
+/* BasebandType[] baseband type selected
+ 0: indicate 802.11a type
+ 1: indicate 802.11b type
+ 2: indicate 802.11g type
+*/
+
+#define BBP_TYPE_DEF 2
+
+/*
+ * Static vars definitions
+ */
+
+static struct usb_device_id vt6656_table[] = {
+ {USB_DEVICE(VNT_USB_VENDOR_ID, VNT_USB_PRODUCT_ID)},
+ {}
+};
+
+static void vnt_set_options(struct vnt_private *priv)
+{
+ /* Set number of TX buffers */
+ if (vnt_tx_buffers < CB_MIN_TX_DESC || vnt_tx_buffers > CB_MAX_TX_DESC)
+ priv->num_tx_context = TX_DESC_DEF0;
+ else
+ priv->num_tx_context = vnt_tx_buffers;
+
+ /* Set number of RX buffers */
+ if (vnt_rx_buffers < CB_MIN_RX_DESC || vnt_rx_buffers > CB_MAX_RX_DESC)
+ priv->num_rcb = RX_DESC_DEF0;
+ else
+ priv->num_rcb = vnt_rx_buffers;
+
+ priv->short_retry_limit = SHORT_RETRY_DEF;
+ priv->long_retry_limit = LONG_RETRY_DEF;
+ priv->op_mode = NL80211_IFTYPE_UNSPECIFIED;
+ priv->bb_type = BBP_TYPE_DEF;
+ priv->packet_type = priv->bb_type;
+ priv->auto_fb_ctrl = AUTO_FB_0;
+ priv->preamble_type = 0;
+ priv->exist_sw_net_addr = false;
+}
+
+/*
+ * initialization of MAC & BBP registers
+ */
+static int vnt_init_registers(struct vnt_private *priv)
+{
+ struct vnt_cmd_card_init *init_cmd = &priv->init_command;
+ struct vnt_rsp_card_init *init_rsp = &priv->init_response;
+ u8 antenna;
+ int ii;
+ int status = STATUS_SUCCESS;
+ u8 tmp;
+ u8 calib_tx_iq = 0, calib_tx_dc = 0, calib_rx_iq = 0;
+
+ dev_dbg(&priv->usb->dev, "---->INIbInitAdapter. [%d][%d]\n",
+ DEVICE_INIT_COLD, priv->packet_type);
+
+ if (!vnt_check_firmware_version(priv)) {
+ if (vnt_download_firmware(priv) == true) {
+ if (vnt_firmware_branch_to_sram(priv) == false) {
+ dev_dbg(&priv->usb->dev,
+ " vnt_firmware_branch_to_sram fail\n");
+ return false;
+ }
+ } else {
+ dev_dbg(&priv->usb->dev, "FIRMWAREbDownload fail\n");
+ return false;
+ }
+ }
+
+ if (!vnt_vt3184_init(priv)) {
+ dev_dbg(&priv->usb->dev, "vnt_vt3184_init fail\n");
+ return false;
+ }
+
+ init_cmd->init_class = DEVICE_INIT_COLD;
+ init_cmd->exist_sw_net_addr = priv->exist_sw_net_addr;
+ for (ii = 0; ii < 6; ii++)
+ init_cmd->sw_net_addr[ii] = priv->current_net_addr[ii];
+ init_cmd->short_retry_limit = priv->short_retry_limit;
+ init_cmd->long_retry_limit = priv->long_retry_limit;
+
+ /* issue card_init command to device */
+ status = vnt_control_out(priv,
+ MESSAGE_TYPE_CARDINIT, 0, 0,
+ sizeof(struct vnt_cmd_card_init), (u8 *)init_cmd);
+ if (status != STATUS_SUCCESS) {
+ dev_dbg(&priv->usb->dev, "Issue Card init fail\n");
+ return false;
+ }
+
+ status = vnt_control_in(priv, MESSAGE_TYPE_INIT_RSP, 0, 0,
+ sizeof(struct vnt_rsp_card_init), (u8 *)init_rsp);
+ if (status != STATUS_SUCCESS) {
+ dev_dbg(&priv->usb->dev,
+ "Cardinit request in status fail!\n");
+ return false;
+ }
+
+ /* local ID for AES functions */
+ status = vnt_control_in(priv, MESSAGE_TYPE_READ,
+ MAC_REG_LOCALID, MESSAGE_REQUEST_MACREG, 1,
+ &priv->local_id);
+ if (status != STATUS_SUCCESS)
+ return false;
+
+ /* do MACbSoftwareReset in MACvInitialize */
+
+ priv->top_ofdm_basic_rate = RATE_24M;
+ priv->top_cck_basic_rate = RATE_1M;
+
+ /* target to IF pin while programming to RF chip */
+ priv->power = 0xFF;
+
+ priv->cck_pwr = priv->eeprom[EEP_OFS_PWR_CCK];
+ priv->ofdm_pwr_g = priv->eeprom[EEP_OFS_PWR_OFDMG];
+ /* load power table */
+ for (ii = 0; ii < 14; ii++) {
+ priv->cck_pwr_tbl[ii] =
+ priv->eeprom[ii + EEP_OFS_CCK_PWR_TBL];
+ if (priv->cck_pwr_tbl[ii] == 0)
+ priv->cck_pwr_tbl[ii] = priv->cck_pwr;
+
+ priv->ofdm_pwr_tbl[ii] =
+ priv->eeprom[ii + EEP_OFS_OFDM_PWR_TBL];
+ if (priv->ofdm_pwr_tbl[ii] == 0)
+ priv->ofdm_pwr_tbl[ii] = priv->ofdm_pwr_g;
+ }
+
+ /*
+ * original zonetype is USA, but custom zonetype is Europe,
+ * then need to recover 12, 13, 14 channels with 11 channel
+ */
+ for (ii = 11; ii < 14; ii++) {
+ priv->cck_pwr_tbl[ii] = priv->cck_pwr_tbl[10];
+ priv->ofdm_pwr_tbl[ii] = priv->ofdm_pwr_tbl[10];
+ }
+
+ priv->ofdm_pwr_a = 0x34; /* same as RFbMA2829SelectChannel */
+
+ /* load OFDM A power table */
+ for (ii = 0; ii < CB_MAX_CHANNEL_5G; ii++) {
+ priv->ofdm_a_pwr_tbl[ii] =
+ priv->eeprom[ii + EEP_OFS_OFDMA_PWR_TBL];
+
+ if (priv->ofdm_a_pwr_tbl[ii] == 0)
+ priv->ofdm_a_pwr_tbl[ii] = priv->ofdm_pwr_a;
+ }
+
+ antenna = priv->eeprom[EEP_OFS_ANTENNA];
+
+ if (antenna & EEP_ANTINV)
+ priv->tx_rx_ant_inv = true;
+ else
+ priv->tx_rx_ant_inv = false;
+
+ antenna &= (EEP_ANTENNA_AUX | EEP_ANTENNA_MAIN);
+
+ if (antenna == 0) /* if not set default is both */
+ antenna = (EEP_ANTENNA_AUX | EEP_ANTENNA_MAIN);
+
+ if (antenna == (EEP_ANTENNA_AUX | EEP_ANTENNA_MAIN)) {
+ priv->tx_antenna_mode = ANT_B;
+ priv->rx_antenna_sel = 1;
+
+ if (priv->tx_rx_ant_inv == true)
+ priv->rx_antenna_mode = ANT_A;
+ else
+ priv->rx_antenna_mode = ANT_B;
+ } else {
+ priv->rx_antenna_sel = 0;
+
+ if (antenna & EEP_ANTENNA_AUX) {
+ priv->tx_antenna_mode = ANT_A;
+
+ if (priv->tx_rx_ant_inv == true)
+ priv->rx_antenna_mode = ANT_B;
+ else
+ priv->rx_antenna_mode = ANT_A;
+ } else {
+ priv->tx_antenna_mode = ANT_B;
+
+ if (priv->tx_rx_ant_inv == true)
+ priv->rx_antenna_mode = ANT_A;
+ else
+ priv->rx_antenna_mode = ANT_B;
+ }
+ }
+
+ /* Set initial antenna mode */
+ vnt_set_antenna_mode(priv, priv->rx_antenna_mode);
+
+ /* get Auto Fall Back type */
+ priv->auto_fb_ctrl = AUTO_FB_0;
+
+ /* default Auto Mode */
+ priv->bb_type = BB_TYPE_11G;
+
+ /* get RFType */
+ priv->rf_type = init_rsp->rf_type;
+
+ /* load vt3266 calibration parameters in EEPROM */
+ if (priv->rf_type == RF_VT3226D0) {
+ if ((priv->eeprom[EEP_OFS_MAJOR_VER] == 0x1) &&
+ (priv->eeprom[EEP_OFS_MINOR_VER] >= 0x4)) {
+
+ calib_tx_iq = priv->eeprom[EEP_OFS_CALIB_TX_IQ];
+ calib_tx_dc = priv->eeprom[EEP_OFS_CALIB_TX_DC];
+ calib_rx_iq = priv->eeprom[EEP_OFS_CALIB_RX_IQ];
+ if (calib_tx_iq || calib_tx_dc || calib_rx_iq) {
+ /* CR255, enable TX/RX IQ and
+ DC compensation mode */
+ vnt_control_out_u8(priv,
+ MESSAGE_REQUEST_BBREG,
+ 0xff,
+ 0x03);
+ /* CR251, TX I/Q Imbalance Calibration */
+ vnt_control_out_u8(priv,
+ MESSAGE_REQUEST_BBREG,
+ 0xfb,
+ calib_tx_iq);
+ /* CR252, TX DC-Offset Calibration */
+ vnt_control_out_u8(priv,
+ MESSAGE_REQUEST_BBREG,
+ 0xfC,
+ calib_tx_dc);
+ /* CR253, RX I/Q Imbalance Calibration */
+ vnt_control_out_u8(priv,
+ MESSAGE_REQUEST_BBREG,
+ 0xfd,
+ calib_rx_iq);
+ } else {
+ /* CR255, turn off
+ BB Calibration compensation */
+ vnt_control_out_u8(priv,
+ MESSAGE_REQUEST_BBREG,
+ 0xff,
+ 0x0);
+ }
+ }
+ }
+
+ /* get permanent network address */
+ memcpy(priv->permanent_net_addr, init_rsp->net_addr, 6);
+ ether_addr_copy(priv->current_net_addr, priv->permanent_net_addr);
+
+ /* if exist SW network address, use it */
+ dev_dbg(&priv->usb->dev, "Network address = %pM\n",
+ priv->current_net_addr);
+
+ /*
+ * set BB and packet type at the same time
+ * set Short Slot Time, xIFS, and RSPINF
+ */
+ if (priv->bb_type == BB_TYPE_11A)
+ priv->short_slot_time = true;
+ else
+ priv->short_slot_time = false;
+
+ vnt_set_short_slot_time(priv);
+
+ priv->radio_ctl = priv->eeprom[EEP_OFS_RADIOCTL];
+
+ if ((priv->radio_ctl & EEP_RADIOCTL_ENABLE) != 0) {
+ status = vnt_control_in(priv, MESSAGE_TYPE_READ,
+ MAC_REG_GPIOCTL1, MESSAGE_REQUEST_MACREG, 1, &tmp);
+
+ if (status != STATUS_SUCCESS)
+ return false;
+
+ if ((tmp & GPIO3_DATA) == 0)
+ vnt_mac_reg_bits_on(priv, MAC_REG_GPIOCTL1,
+ GPIO3_INTMD);
+ else
+ vnt_mac_reg_bits_off(priv, MAC_REG_GPIOCTL1,
+ GPIO3_INTMD);
+ }
+
+ vnt_mac_set_led(priv, LEDSTS_TMLEN, 0x38);
+
+ vnt_mac_set_led(priv, LEDSTS_STS, LEDSTS_SLOW);
+
+ vnt_mac_reg_bits_on(priv, MAC_REG_GPIOCTL0, 0x01);
+
+ vnt_radio_power_on(priv);
+
+ dev_dbg(&priv->usb->dev, "<----INIbInitAdapter Exit\n");
+
+ return true;
+}
+
+static void vnt_free_tx_bufs(struct vnt_private *priv)
+{
+ struct vnt_usb_send_context *tx_context;
+ int ii;
+
+ for (ii = 0; ii < priv->num_tx_context; ii++) {
+ tx_context = priv->tx_context[ii];
+ /* deallocate URBs */
+ if (tx_context->urb) {
+ usb_kill_urb(tx_context->urb);
+ usb_free_urb(tx_context->urb);
+ }
+
+ kfree(tx_context);
+ }
+}
+
+static void vnt_free_rx_bufs(struct vnt_private *priv)
+{
+ struct vnt_rcb *rcb;
+ int ii;
+
+ for (ii = 0; ii < priv->num_rcb; ii++) {
+ rcb = priv->rcb[ii];
+ if (!rcb)
+ continue;
+
+ /* deallocate URBs */
+ if (rcb->urb) {
+ usb_kill_urb(rcb->urb);
+ usb_free_urb(rcb->urb);
+ }
+
+ /* deallocate skb */
+ if (rcb->skb)
+ dev_kfree_skb(rcb->skb);
+
+ kfree(rcb);
+ }
+}
+
+static void usb_device_reset(struct vnt_private *priv)
+{
+ int status;
+
+ status = usb_reset_device(priv->usb);
+ if (status)
+ dev_warn(&priv->usb->dev,
+ "usb_device_reset fail status=%d\n", status);
+}
+
+static void vnt_free_int_bufs(struct vnt_private *priv)
+{
+ kfree(priv->int_buf.data_buf);
+}
+
+static bool vnt_alloc_bufs(struct vnt_private *priv)
+{
+ struct vnt_usb_send_context *tx_context;
+ struct vnt_rcb *rcb;
+ int ii;
+
+ for (ii = 0; ii < priv->num_tx_context; ii++) {
+ tx_context = kmalloc(sizeof(struct vnt_usb_send_context),
+ GFP_KERNEL);
+ if (tx_context == NULL)
+ goto free_tx;
+
+ priv->tx_context[ii] = tx_context;
+ tx_context->priv = priv;
+ tx_context->pkt_no = ii;
+
+ /* allocate URBs */
+ tx_context->urb = usb_alloc_urb(0, GFP_ATOMIC);
+ if (!tx_context->urb) {
+ dev_err(&priv->usb->dev, "alloc tx urb failed\n");
+ goto free_tx;
+ }
+
+ tx_context->in_use = false;
+ }
+
+ for (ii = 0; ii < priv->num_rcb; ii++) {
+ priv->rcb[ii] = kzalloc(sizeof(struct vnt_rcb), GFP_KERNEL);
+ if (!priv->rcb[ii]) {
+ dev_err(&priv->usb->dev,
+ "failed to allocate rcb no %d\n", ii);
+ goto free_rx_tx;
+ }
+
+ rcb = priv->rcb[ii];
+
+ rcb->priv = priv;
+
+ /* allocate URBs */
+ rcb->urb = usb_alloc_urb(0, GFP_ATOMIC);
+ if (rcb->urb == NULL) {
+ dev_err(&priv->usb->dev, "Failed to alloc rx urb\n");
+ goto free_rx_tx;
+ }
+
+ rcb->skb = dev_alloc_skb(priv->rx_buf_sz);
+ if (rcb->skb == NULL)
+ goto free_rx_tx;
+
+ rcb->in_use = false;
+
+ /* submit rx urb */
+ if (vnt_submit_rx_urb(priv, rcb))
+ goto free_rx_tx;
+ }
+
+ priv->interrupt_urb = usb_alloc_urb(0, GFP_ATOMIC);
+ if (priv->interrupt_urb == NULL) {
+ dev_err(&priv->usb->dev, "Failed to alloc int urb\n");
+ goto free_rx_tx;
+ }
+
+ priv->int_buf.data_buf = kmalloc(MAX_INTERRUPT_SIZE, GFP_KERNEL);
+ if (priv->int_buf.data_buf == NULL) {
+ usb_free_urb(priv->interrupt_urb);
+ goto free_rx_tx;
+ }
+
+ return true;
+
+free_rx_tx:
+ vnt_free_rx_bufs(priv);
+
+free_tx:
+ vnt_free_tx_bufs(priv);
+
+ return false;
+}
+
+static void vnt_tx_80211(struct ieee80211_hw *hw,
+ struct ieee80211_tx_control *control, struct sk_buff *skb)
+{
+ struct vnt_private *priv = hw->priv;
+
+ ieee80211_stop_queues(hw);
+
+ if (vnt_tx_packet(priv, skb)) {
+ ieee80211_free_txskb(hw, skb);
+
+ ieee80211_wake_queues(hw);
+ }
+}
+
+static int vnt_start(struct ieee80211_hw *hw)
+{
+ struct vnt_private *priv = hw->priv;
+
+ priv->rx_buf_sz = MAX_TOTAL_SIZE_WITH_ALL_HEADERS;
+
+ if (vnt_alloc_bufs(priv) == false) {
+ dev_dbg(&priv->usb->dev, "vnt_alloc_bufs fail...\n");
+ return -ENOMEM;
+ }
+
+ clear_bit(DEVICE_FLAGS_DISCONNECTED, &priv->flags);
+
+ if (vnt_init_registers(priv) == false) {
+ dev_dbg(&priv->usb->dev, " init register fail\n");
+ goto free_all;
+ }
+
+ priv->int_interval = 1; /* bInterval is set to 1 */
+
+ vnt_int_start_interrupt(priv);
+
+ ieee80211_wake_queues(hw);
+
+ return 0;
+
+free_all:
+ vnt_free_rx_bufs(priv);
+ vnt_free_tx_bufs(priv);
+ vnt_free_int_bufs(priv);
+
+ usb_kill_urb(priv->interrupt_urb);
+ usb_free_urb(priv->interrupt_urb);
+
+ return -ENOMEM;
+}
+
+static void vnt_stop(struct ieee80211_hw *hw)
+{
+ struct vnt_private *priv = hw->priv;
+ int i;
+
+ if (!priv)
+ return;
+
+ for (i = 0; i < MAX_KEY_TABLE; i++)
+ vnt_mac_disable_keyentry(priv, i);
+
+ /* clear all keys */
+ priv->key_entry_inuse = 0;
+
+ if (!test_bit(DEVICE_FLAGS_UNPLUG, &priv->flags))
+ vnt_mac_shutdown(priv);
+
+ ieee80211_stop_queues(hw);
+
+ set_bit(DEVICE_FLAGS_DISCONNECTED, &priv->flags);
+
+ cancel_delayed_work_sync(&priv->run_command_work);
+
+ priv->cmd_running = false;
+
+ vnt_free_tx_bufs(priv);
+ vnt_free_rx_bufs(priv);
+ vnt_free_int_bufs(priv);
+
+ usb_kill_urb(priv->interrupt_urb);
+ usb_free_urb(priv->interrupt_urb);
+}
+
+static int vnt_add_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
+{
+ struct vnt_private *priv = hw->priv;
+
+ priv->vif = vif;
+
+ switch (vif->type) {
+ case NL80211_IFTYPE_STATION:
+ break;
+ case NL80211_IFTYPE_ADHOC:
+ vnt_mac_reg_bits_off(priv, MAC_REG_RCR, RCR_UNICAST);
+
+ vnt_mac_reg_bits_on(priv, MAC_REG_HOSTCR, HOSTCR_ADHOC);
+
+ break;
+ case NL80211_IFTYPE_AP:
+ vnt_mac_reg_bits_off(priv, MAC_REG_RCR, RCR_UNICAST);
+
+ vnt_mac_reg_bits_on(priv, MAC_REG_HOSTCR, HOSTCR_AP);
+
+ break;
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ priv->op_mode = vif->type;
+
+ vnt_set_bss_mode(priv);
+
+ /* LED blink on TX */
+ vnt_mac_set_led(priv, LEDSTS_STS, LEDSTS_INTER);
+
+ return 0;
+}
+
+static void vnt_remove_interface(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif)
+{
+ struct vnt_private *priv = hw->priv;
+
+ switch (vif->type) {
+ case NL80211_IFTYPE_STATION:
+ break;
+ case NL80211_IFTYPE_ADHOC:
+ vnt_mac_reg_bits_off(priv, MAC_REG_TCR, TCR_AUTOBCNTX);
+ vnt_mac_reg_bits_off(priv, MAC_REG_TFTCTL, TFTCTL_TSFCNTREN);
+ vnt_mac_reg_bits_off(priv, MAC_REG_HOSTCR, HOSTCR_ADHOC);
+ break;
+ case NL80211_IFTYPE_AP:
+ vnt_mac_reg_bits_off(priv, MAC_REG_TCR, TCR_AUTOBCNTX);
+ vnt_mac_reg_bits_off(priv, MAC_REG_TFTCTL, TFTCTL_TSFCNTREN);
+ vnt_mac_reg_bits_off(priv, MAC_REG_HOSTCR, HOSTCR_AP);
+ break;
+ default:
+ break;
+ }
+
+ vnt_radio_power_off(priv);
+
+ priv->op_mode = NL80211_IFTYPE_UNSPECIFIED;
+
+ /* LED slow blink */
+ vnt_mac_set_led(priv, LEDSTS_STS, LEDSTS_SLOW);
+}
+
+static int vnt_config(struct ieee80211_hw *hw, u32 changed)
+{
+ struct vnt_private *priv = hw->priv;
+ struct ieee80211_conf *conf = &hw->conf;
+ u8 bb_type;
+
+ if (changed & IEEE80211_CONF_CHANGE_PS) {
+ if (conf->flags & IEEE80211_CONF_PS)
+ vnt_enable_power_saving(priv, conf->listen_interval);
+ else
+ vnt_disable_power_saving(priv);
+ }
+
+ if ((changed & IEEE80211_CONF_CHANGE_CHANNEL) ||
+ (conf->flags & IEEE80211_CONF_OFFCHANNEL)) {
+ vnt_set_channel(priv, conf->chandef.chan->hw_value);
+
+ if (conf->chandef.chan->band == IEEE80211_BAND_5GHZ)
+ bb_type = BB_TYPE_11A;
+ else
+ bb_type = BB_TYPE_11G;
+
+ if (priv->bb_type != bb_type) {
+ priv->bb_type = bb_type;
+
+ vnt_set_bss_mode(priv);
+ }
+ }
+
+ if (changed & IEEE80211_CONF_CHANGE_POWER) {
+ if (priv->bb_type == BB_TYPE_11B)
+ priv->current_rate = RATE_1M;
+ else
+ priv->current_rate = RATE_54M;
+
+ vnt_rf_setpower(priv, priv->current_rate,
+ conf->chandef.chan->hw_value);
+ }
+
+ return 0;
+}
+
+static void vnt_bss_info_changed(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif, struct ieee80211_bss_conf *conf,
+ u32 changed)
+{
+ struct vnt_private *priv = hw->priv;
+
+ priv->current_aid = conf->aid;
+
+ if (changed & BSS_CHANGED_BSSID && conf->bssid)
+ vnt_mac_set_bssid_addr(priv, (u8 *)conf->bssid);
+
+
+ if (changed & BSS_CHANGED_BASIC_RATES) {
+ priv->basic_rates = conf->basic_rates;
+
+ vnt_update_top_rates(priv);
+
+ dev_dbg(&priv->usb->dev, "basic rates %x\n", conf->basic_rates);
+ }
+
+ if (changed & BSS_CHANGED_ERP_PREAMBLE) {
+ if (conf->use_short_preamble) {
+ vnt_mac_enable_barker_preamble_mode(priv);
+ priv->preamble_type = true;
+ } else {
+ vnt_mac_disable_barker_preamble_mode(priv);
+ priv->preamble_type = false;
+ }
+ }
+
+ if (changed & BSS_CHANGED_ERP_CTS_PROT) {
+ if (conf->use_cts_prot)
+ vnt_mac_enable_protect_mode(priv);
+ else
+ vnt_mac_disable_protect_mode(priv);
+ }
+
+ if (changed & BSS_CHANGED_ERP_SLOT) {
+ if (conf->use_short_slot)
+ priv->short_slot_time = true;
+ else
+ priv->short_slot_time = false;
+
+ vnt_set_short_slot_time(priv);
+ vnt_set_vga_gain_offset(priv, priv->bb_vga[0]);
+ vnt_update_pre_ed_threshold(priv, false);
+ }
+
+ if (changed & BSS_CHANGED_TXPOWER)
+ vnt_rf_setpower(priv, priv->current_rate,
+ conf->chandef.chan->hw_value);
+
+ if (changed & BSS_CHANGED_BEACON_ENABLED) {
+ dev_dbg(&priv->usb->dev,
+ "Beacon enable %d\n", conf->enable_beacon);
+
+ if (conf->enable_beacon) {
+ vnt_beacon_enable(priv, vif, conf);
+
+ vnt_mac_reg_bits_on(priv, MAC_REG_TCR, TCR_AUTOBCNTX);
+ } else {
+ vnt_mac_reg_bits_off(priv, MAC_REG_TCR, TCR_AUTOBCNTX);
+ }
+ }
+}
+
+static u64 vnt_prepare_multicast(struct ieee80211_hw *hw,
+ struct netdev_hw_addr_list *mc_list)
+{
+ struct vnt_private *priv = hw->priv;
+ struct netdev_hw_addr *ha;
+ u64 mc_filter = 0;
+ u32 bit_nr = 0;
+
+ netdev_hw_addr_list_for_each(ha, mc_list) {
+ bit_nr = ether_crc(ETH_ALEN, ha->addr) >> 26;
+
+ mc_filter |= 1ULL << (bit_nr & 0x3f);
+ }
+
+ priv->mc_list_count = mc_list->count;
+
+ return mc_filter;
+}
+
+static void vnt_configure(struct ieee80211_hw *hw,
+ unsigned int changed_flags, unsigned int *total_flags, u64 multicast)
+{
+ struct vnt_private *priv = hw->priv;
+ u8 rx_mode = 0;
+ int rc;
+
+ *total_flags &= FIF_ALLMULTI | FIF_OTHER_BSS | FIF_PROMISC_IN_BSS |
+ FIF_BCN_PRBRESP_PROMISC;
+
+ rc = vnt_control_in(priv, MESSAGE_TYPE_READ, MAC_REG_RCR,
+ MESSAGE_REQUEST_MACREG, sizeof(u8), &rx_mode);
+
+ if (!rc)
+ rx_mode = RCR_MULTICAST | RCR_BROADCAST;
+
+ dev_dbg(&priv->usb->dev, "rx mode in = %x\n", rx_mode);
+
+ if (changed_flags & FIF_PROMISC_IN_BSS) {
+ /* unconditionally log net taps */
+ if (*total_flags & FIF_PROMISC_IN_BSS)
+ rx_mode |= RCR_UNICAST;
+ else
+ rx_mode &= ~RCR_UNICAST;
+ }
+
+ if (changed_flags & FIF_ALLMULTI) {
+ if (*total_flags & FIF_ALLMULTI) {
+ if (priv->mc_list_count > 2)
+ vnt_mac_set_filter(priv, ~0);
+ else
+ vnt_mac_set_filter(priv, multicast);
+
+ rx_mode |= RCR_MULTICAST | RCR_BROADCAST;
+ } else {
+ rx_mode &= ~(RCR_MULTICAST | RCR_BROADCAST);
+ }
+
+ }
+
+ if (changed_flags & (FIF_OTHER_BSS | FIF_BCN_PRBRESP_PROMISC)) {
+ if (*total_flags & (FIF_OTHER_BSS | FIF_BCN_PRBRESP_PROMISC))
+ rx_mode &= ~RCR_BSSID;
+ else
+ rx_mode |= RCR_BSSID;
+ }
+
+ vnt_control_out_u8(priv, MESSAGE_REQUEST_MACREG, MAC_REG_RCR, rx_mode);
+
+ dev_dbg(&priv->usb->dev, "rx mode out= %x\n", rx_mode);
+}
+
+static int vnt_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
+ struct ieee80211_vif *vif, struct ieee80211_sta *sta,
+ struct ieee80211_key_conf *key)
+{
+ struct vnt_private *priv = hw->priv;
+
+ switch (cmd) {
+ case SET_KEY:
+ if (vnt_set_keys(hw, sta, vif, key))
+ return -EOPNOTSUPP;
+ break;
+ case DISABLE_KEY:
+ if (test_bit(key->hw_key_idx, &priv->key_entry_inuse))
+ clear_bit(key->hw_key_idx, &priv->key_entry_inuse);
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static void vnt_sw_scan_start(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ const u8 *addr)
+{
+ struct vnt_private *priv = hw->priv;
+
+ vnt_set_bss_mode(priv);
+ /* Set max sensitivity*/
+ vnt_update_pre_ed_threshold(priv, true);
+}
+
+static void vnt_sw_scan_complete(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif)
+{
+ struct vnt_private *priv = hw->priv;
+
+ /* Return sensitivity to channel level*/
+ vnt_update_pre_ed_threshold(priv, false);
+}
+
+static int vnt_get_stats(struct ieee80211_hw *hw,
+ struct ieee80211_low_level_stats *stats)
+{
+ struct vnt_private *priv = hw->priv;
+
+ memcpy(stats, &priv->low_stats, sizeof(*stats));
+
+ return 0;
+}
+
+static u64 vnt_get_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
+{
+ struct vnt_private *priv = hw->priv;
+
+ return priv->current_tsf;
+}
+
+static void vnt_set_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ u64 tsf)
+{
+ struct vnt_private *priv = hw->priv;
+
+ vnt_update_next_tbtt(priv, tsf, vif->bss_conf.beacon_int);
+}
+
+static void vnt_reset_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
+{
+ struct vnt_private *priv = hw->priv;
+
+ vnt_mac_reg_bits_off(priv, MAC_REG_TFTCTL, TFTCTL_TSFCNTREN);
+
+ vnt_clear_current_tsf(priv);
+}
+
+static const struct ieee80211_ops vnt_mac_ops = {
+ .tx = vnt_tx_80211,
+ .start = vnt_start,
+ .stop = vnt_stop,
+ .add_interface = vnt_add_interface,
+ .remove_interface = vnt_remove_interface,
+ .config = vnt_config,
+ .bss_info_changed = vnt_bss_info_changed,
+ .prepare_multicast = vnt_prepare_multicast,
+ .configure_filter = vnt_configure,
+ .set_key = vnt_set_key,
+ .sw_scan_start = vnt_sw_scan_start,
+ .sw_scan_complete = vnt_sw_scan_complete,
+ .get_stats = vnt_get_stats,
+ .get_tsf = vnt_get_tsf,
+ .set_tsf = vnt_set_tsf,
+ .reset_tsf = vnt_reset_tsf,
+};
+
+int vnt_init(struct vnt_private *priv)
+{
+
+ if (!(vnt_init_registers(priv)))
+ return -EAGAIN;
+
+ SET_IEEE80211_PERM_ADDR(priv->hw, priv->permanent_net_addr);
+
+ vnt_init_bands(priv);
+
+ if (ieee80211_register_hw(priv->hw))
+ return -ENODEV;
+
+ priv->mac_hw = true;
+
+ vnt_radio_power_off(priv);
+
+ return 0;
+}
+
+static int
+vt6656_probe(struct usb_interface *intf, const struct usb_device_id *id)
+{
+ struct usb_device *udev;
+ struct vnt_private *priv;
+ struct ieee80211_hw *hw;
+ struct wiphy *wiphy;
+ int rc = 0;
+
+ udev = usb_get_dev(interface_to_usbdev(intf));
+
+ dev_notice(&udev->dev, "%s Ver. %s\n",
+ DEVICE_FULL_DRV_NAM, DEVICE_VERSION);
+ dev_notice(&udev->dev,
+ "Copyright (c) 2004 VIA Networking Technologies, Inc.\n");
+
+ hw = ieee80211_alloc_hw(sizeof(struct vnt_private), &vnt_mac_ops);
+ if (!hw) {
+ dev_err(&udev->dev, "could not register ieee80211_hw\n");
+ rc = -ENOMEM;
+ goto err_nomem;
+ }
+
+ priv = hw->priv;
+ priv->hw = hw;
+ priv->usb = udev;
+
+ vnt_set_options(priv);
+
+ spin_lock_init(&priv->lock);
+ mutex_init(&priv->usb_lock);
+
+ INIT_DELAYED_WORK(&priv->run_command_work, vnt_run_command);
+
+ usb_set_intfdata(intf, priv);
+
+ wiphy = priv->hw->wiphy;
+
+ wiphy->frag_threshold = FRAG_THRESH_DEF;
+ wiphy->rts_threshold = RTS_THRESH_DEF;
+ wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) |
+ BIT(NL80211_IFTYPE_ADHOC) | BIT(NL80211_IFTYPE_AP);
+
+ priv->hw->flags = IEEE80211_HW_RX_INCLUDES_FCS |
+ IEEE80211_HW_REPORTS_TX_ACK_STATUS |
+ IEEE80211_HW_SIGNAL_DBM |
+ IEEE80211_HW_TIMING_BEACON_ONLY;
+
+ priv->hw->max_signal = 100;
+
+ SET_IEEE80211_DEV(priv->hw, &intf->dev);
+
+ usb_device_reset(priv);
+
+ clear_bit(DEVICE_FLAGS_DISCONNECTED, &priv->flags);
+ vnt_reset_command_timer(priv);
+
+ vnt_schedule_command(priv, WLAN_CMD_INIT_MAC80211);
+
+ return 0;
+
+err_nomem:
+ usb_put_dev(udev);
+
+ return rc;
+}
+
+static void vt6656_disconnect(struct usb_interface *intf)
+{
+ struct vnt_private *priv = usb_get_intfdata(intf);
+
+ if (!priv)
+ return;
+
+ if (priv->mac_hw)
+ ieee80211_unregister_hw(priv->hw);
+
+ usb_set_intfdata(intf, NULL);
+ usb_put_dev(interface_to_usbdev(intf));
+
+ set_bit(DEVICE_FLAGS_UNPLUG, &priv->flags);
+
+ ieee80211_free_hw(priv->hw);
+}
+
+#ifdef CONFIG_PM
+
+static int vt6656_suspend(struct usb_interface *intf, pm_message_t message)
+{
+ return 0;
+}
+
+static int vt6656_resume(struct usb_interface *intf)
+{
+ return 0;
+}
+
+#endif /* CONFIG_PM */
+
+MODULE_DEVICE_TABLE(usb, vt6656_table);
+
+static struct usb_driver vt6656_driver = {
+ .name = DEVICE_NAME,
+ .probe = vt6656_probe,
+ .disconnect = vt6656_disconnect,
+ .id_table = vt6656_table,
+#ifdef CONFIG_PM
+ .suspend = vt6656_suspend,
+ .resume = vt6656_resume,
+#endif /* CONFIG_PM */
+};
+
+module_usb_driver(vt6656_driver);
diff --git a/drivers/staging/vt6656/power.c b/drivers/staging/vt6656/power.c
new file mode 100644
index 000000000..0ffbaed5d
--- /dev/null
+++ b/drivers/staging/vt6656/power.c
@@ -0,0 +1,144 @@
+/*
+ * Copyright (c) 1996, 2003 VIA Networking Technologies, Inc.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ *
+ * File: power.c
+ *
+ * Purpose: Handles 802.11 power management functions
+ *
+ * Author: Lyndon Chen
+ *
+ * Date: July 17, 2002
+ *
+ * Functions:
+ * vnt_enable_power_saving - Enable Power Saving Mode
+ * PSvDiasblePowerSaving - Disable Power Saving Mode
+ * vnt_next_tbtt_wakeup - Decide if we need to wake up at next Beacon
+ *
+ * Revision History:
+ *
+ */
+
+#include "mac.h"
+#include "device.h"
+#include "power.h"
+#include "wcmd.h"
+#include "rxtx.h"
+#include "card.h"
+#include "usbpipe.h"
+
+/*
+ *
+ * Routine Description:
+ * Enable hw power saving functions
+ *
+ * Return Value:
+ * None.
+ *
+ */
+
+void vnt_enable_power_saving(struct vnt_private *priv, u16 listen_interval)
+{
+ u16 aid = priv->current_aid | BIT(14) | BIT(15);
+
+ /* set period of power up before TBTT */
+ vnt_mac_write_word(priv, MAC_REG_PWBT, C_PWBT);
+
+ if (priv->op_mode != NL80211_IFTYPE_ADHOC) {
+ /* set AID */
+ vnt_mac_write_word(priv, MAC_REG_AIDATIM, aid);
+ }
+
+ /* Warren:06-18-2004,the sequence must follow
+ * PSEN->AUTOSLEEP->GO2DOZE
+ */
+ /* enable power saving hw function */
+ vnt_mac_reg_bits_on(priv, MAC_REG_PSCTL, PSCTL_PSEN);
+
+ /* Set AutoSleep */
+ vnt_mac_reg_bits_on(priv, MAC_REG_PSCFG, PSCFG_AUTOSLEEP);
+
+ /* Warren:MUST turn on this once before turn on AUTOSLEEP ,or the
+ * AUTOSLEEP doesn't work
+ */
+ vnt_mac_reg_bits_on(priv, MAC_REG_PSCTL, PSCTL_GO2DOZE);
+
+ if (listen_interval >= 2) {
+
+ /* clear always listen beacon */
+ vnt_mac_reg_bits_off(priv, MAC_REG_PSCTL, PSCTL_ALBCN);
+
+ /* first time set listen next beacon */
+ vnt_mac_reg_bits_on(priv, MAC_REG_PSCTL, PSCTL_LNBCN);
+ } else {
+
+ /* always listen beacon */
+ vnt_mac_reg_bits_on(priv, MAC_REG_PSCTL, PSCTL_ALBCN);
+ }
+
+ dev_dbg(&priv->usb->dev, "PS:Power Saving Mode Enable...\n");
+}
+
+/*
+ *
+ * Routine Description:
+ * Disable hw power saving functions
+ *
+ * Return Value:
+ * None.
+ *
+ */
+
+void vnt_disable_power_saving(struct vnt_private *priv)
+{
+
+ /* disable power saving hw function */
+ vnt_control_out(priv, MESSAGE_TYPE_DISABLE_PS, 0,
+ 0, 0, NULL);
+
+ /* clear AutoSleep */
+ vnt_mac_reg_bits_off(priv, MAC_REG_PSCFG, PSCFG_AUTOSLEEP);
+
+ /* set always listen beacon */
+ vnt_mac_reg_bits_on(priv, MAC_REG_PSCTL, PSCTL_ALBCN);
+}
+
+/*
+ *
+ * Routine Description:
+ * Check if Next TBTT must wake up
+ *
+ * Return Value:
+ * None.
+ *
+ */
+
+int vnt_next_tbtt_wakeup(struct vnt_private *priv)
+{
+ struct ieee80211_hw *hw = priv->hw;
+ struct ieee80211_conf *conf = &hw->conf;
+ int wake_up = false;
+
+ if (conf->listen_interval == 1) {
+ /* Turn on wake up to listen next beacon */
+ vnt_mac_reg_bits_on(priv, MAC_REG_PSCTL, PSCTL_LNBCN);
+ wake_up = true;
+ }
+
+ return wake_up;
+}
diff --git a/drivers/staging/vt6656/power.h b/drivers/staging/vt6656/power.h
new file mode 100644
index 000000000..7696b7148
--- /dev/null
+++ b/drivers/staging/vt6656/power.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 1996, 2003 VIA Networking Technologies, Inc.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * File: power.h
+ *
+ * Purpose: Handles 802.11 power management functions
+ *
+ * Author: Lyndon Chen
+ *
+ * Date: July 17, 2002
+ *
+ */
+
+#ifndef __POWER_H__
+#define __POWER_H__
+
+#define C_PWBT 1000 /* micro sec. power up before TBTT */
+
+void vnt_disable_power_saving(struct vnt_private *);
+void vnt_enable_power_saving(struct vnt_private *, u16);
+int vnt_next_tbtt_wakeup(struct vnt_private *);
+
+#endif /* __POWER_H__ */
diff --git a/drivers/staging/vt6656/rf.c b/drivers/staging/vt6656/rf.c
new file mode 100644
index 000000000..c4286ccac
--- /dev/null
+++ b/drivers/staging/vt6656/rf.c
@@ -0,0 +1,951 @@
+/*
+ * Copyright (c) 1996, 2003 VIA Networking Technologies, Inc.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ *
+ * File: rf.c
+ *
+ * Purpose: rf function code
+ *
+ * Author: Jerry Chen
+ *
+ * Date: Feb. 19, 2004
+ *
+ * Functions:
+ * vnt_rf_write_embedded - Embedded write RF register via MAC
+ *
+ * Revision History:
+ * RF_VT3226: RobertYu:20051111, VT3226C0 and before
+ * RF_VT3226D0: RobertYu:20051228
+ * RF_VT3342A0: RobertYu:20060609
+ *
+ */
+
+#include "mac.h"
+#include "rf.h"
+#include "baseband.h"
+#include "usbpipe.h"
+
+#define CB_AL2230_INIT_SEQ 15
+#define AL2230_PWR_IDX_LEN 64
+
+#define CB_AL7230_INIT_SEQ 16
+#define AL7230_PWR_IDX_LEN 64
+
+#define CB_VT3226_INIT_SEQ 11
+#define VT3226_PWR_IDX_LEN 64
+
+#define CB_VT3342_INIT_SEQ 13
+#define VT3342_PWR_IDX_LEN 64
+
+static u8 al2230_init_table[CB_AL2230_INIT_SEQ][3] = {
+ {0x03, 0xf7, 0x90},
+ {0x03, 0x33, 0x31},
+ {0x01, 0xb8, 0x02},
+ {0x00, 0xff, 0xf3},
+ {0x00, 0x05, 0xa4},
+ {0x0f, 0x4d, 0xc5},
+ {0x08, 0x05, 0xb6},
+ {0x01, 0x47, 0xc7},
+ {0x00, 0x06, 0x88},
+ {0x04, 0x03, 0xb9},
+ {0x00, 0xdb, 0xba},
+ {0x00, 0x09, 0x9b},
+ {0x0b, 0xdf, 0xfc},
+ {0x00, 0x00, 0x0d},
+ {0x00, 0x58, 0x0f}
+};
+
+static u8 al2230_channel_table0[CB_MAX_CHANNEL_24G][3] = {
+ {0x03, 0xf7, 0x90},
+ {0x03, 0xf7, 0x90},
+ {0x03, 0xe7, 0x90},
+ {0x03, 0xe7, 0x90},
+ {0x03, 0xf7, 0xa0},
+ {0x03, 0xf7, 0xa0},
+ {0x03, 0xe7, 0xa0},
+ {0x03, 0xe7, 0xa0},
+ {0x03, 0xf7, 0xb0},
+ {0x03, 0xf7, 0xb0},
+ {0x03, 0xe7, 0xb0},
+ {0x03, 0xe7, 0xb0},
+ {0x03, 0xf7, 0xc0},
+ {0x03, 0xe7, 0xc0}
+};
+
+static u8 al2230_channel_table1[CB_MAX_CHANNEL_24G][3] = {
+ {0x03, 0x33, 0x31},
+ {0x0b, 0x33, 0x31},
+ {0x03, 0x33, 0x31},
+ {0x0b, 0x33, 0x31},
+ {0x03, 0x33, 0x31},
+ {0x0b, 0x33, 0x31},
+ {0x03, 0x33, 0x31},
+ {0x0b, 0x33, 0x31},
+ {0x03, 0x33, 0x31},
+ {0x0b, 0x33, 0x31},
+ {0x03, 0x33, 0x31},
+ {0x0b, 0x33, 0x31},
+ {0x03, 0x33, 0x31},
+ {0x06, 0x66, 0x61}
+};
+
+static u8 al7230_init_table[CB_AL7230_INIT_SEQ][3] = {
+ {0x20, 0x37, 0x90},
+ {0x13, 0x33, 0x31},
+ {0x84, 0x1f, 0xf2},
+ {0x3f, 0xdf, 0xa3},
+ {0x7f, 0xd7, 0x84},
+ {0x80, 0x2b, 0x55},
+ {0x56, 0xaf, 0x36},
+ {0xce, 0x02, 0x07},
+ {0x6e, 0xbc, 0x98},
+ {0x22, 0x1b, 0xb9},
+ {0xe0, 0x00, 0x0a},
+ {0x08, 0x03, 0x1b},
+ {0x00, 0x0a, 0x3c},
+ {0xff, 0xff, 0xfd},
+ {0x00, 0x00, 0x0e},
+ {0x1a, 0xba, 0x8f}
+};
+
+static u8 al7230_init_table_amode[CB_AL7230_INIT_SEQ][3] = {
+ {0x2f, 0xf5, 0x20},
+ {0x00, 0x00, 0x01},
+ {0x45, 0x1f, 0xe2},
+ {0x5f, 0xdf, 0xa3},
+ {0x6f, 0xd7, 0x84},
+ {0x85, 0x3f, 0x55},
+ {0x56, 0xaf, 0x36},
+ {0xce, 0x02, 0x07},
+ {0x6e, 0xbc, 0x98},
+ {0x22, 0x1b, 0xb9},
+ {0xe0, 0x60, 0x0a},
+ {0x08, 0x03, 0x1b},
+ {0x00, 0x14, 0x7c},
+ {0xff, 0xff, 0xfd},
+ {0x00, 0x00, 0x0e},
+ {0x12, 0xba, 0xcf}
+};
+
+static u8 al7230_channel_table0[CB_MAX_CHANNEL][3] = {
+ {0x20, 0x37, 0x90},
+ {0x20, 0x37, 0x90},
+ {0x20, 0x37, 0x90},
+ {0x20, 0x37, 0x90},
+ {0x20, 0x37, 0xa0},
+ {0x20, 0x37, 0xa0},
+ {0x20, 0x37, 0xa0},
+ {0x20, 0x37, 0xa0},
+ {0x20, 0x37, 0xb0},
+ {0x20, 0x37, 0xb0},
+ {0x20, 0x37, 0xb0},
+ {0x20, 0x37, 0xb0},
+ {0x20, 0x37, 0xc0},
+ {0x20, 0x37, 0xc0},
+ {0x0f, 0xf5, 0x20}, /* channel 15 Tf = 4915MHz */
+ {0x2f, 0xf5, 0x20},
+ {0x0f, 0xf5, 0x20},
+ {0x0f, 0xf5, 0x20},
+ {0x2f, 0xf5, 0x20},
+ {0x0f, 0xf5, 0x20},
+ {0x2f, 0xf5, 0x30},
+ {0x2f, 0xf5, 0x30},
+ {0x0f, 0xf5, 0x40},
+ {0x2f, 0xf5, 0x40},
+ {0x0f, 0xf5, 0x40},
+ {0x0f, 0xf5, 0x40},
+ {0x2f, 0xf5, 0x40},
+ {0x2f, 0xf5, 0x50},
+ {0x2f, 0xf5, 0x60},
+ {0x2f, 0xf5, 0x60},
+ {0x2f, 0xf5, 0x70},
+ {0x2f, 0xf5, 0x70},
+ {0x2f, 0xf5, 0x70},
+ {0x2f, 0xf5, 0x70},
+ {0x2f, 0xf5, 0x70},
+ {0x2f, 0xf5, 0x70},
+ {0x2f, 0xf5, 0x80},
+ {0x2f, 0xf5, 0x80},
+ {0x2f, 0xf5, 0x80},
+ {0x2f, 0xf5, 0x90},
+ {0x2f, 0xf5, 0xc0},
+ {0x2f, 0xf5, 0xc0},
+ {0x2f, 0xf5, 0xc0},
+ {0x2f, 0xf5, 0xd0},
+ {0x2f, 0xf5, 0xd0},
+ {0x2f, 0xf5, 0xd0},
+ {0x2f, 0xf5, 0xe0},
+ {0x2f, 0xf5, 0xe0},
+ {0x2f, 0xf5, 0xe0},
+ {0x2f, 0xf5, 0xf0},
+ {0x2f, 0xf5, 0xf0},
+ {0x2f, 0xf6, 0x00},
+ {0x2f, 0xf6, 0x00},
+ {0x2f, 0xf6, 0x00},
+ {0x2f, 0xf6, 0x10},
+ {0x2f, 0xf6, 0x10}
+};
+
+static u8 al7230_channel_table1[CB_MAX_CHANNEL][3] = {
+ {0x13, 0x33, 0x31},
+ {0x1b, 0x33, 0x31},
+ {0x03, 0x33, 0x31},
+ {0x0b, 0x33, 0x31},
+ {0x13, 0x33, 0x31},
+ {0x1b, 0x33, 0x31},
+ {0x03, 0x33, 0x31},
+ {0x0b, 0x33, 0x31},
+ {0x13, 0x33, 0x31},
+ {0x1b, 0x33, 0x31},
+ {0x03, 0x33, 0x31},
+ {0x0b, 0x33, 0x31},
+ {0x13, 0x33, 0x31},
+ {0x06, 0x66, 0x61},
+ {0x1d, 0x55, 0x51}, /* channel = 15, Tf = 4915MHz */
+ {0x00, 0x00, 0x01},
+ {0x02, 0xaa, 0xa1},
+ {0x08, 0x00, 0x01},
+ {0x0a, 0xaa, 0xa1},
+ {0x0d, 0x55, 0x51},
+ {0x15, 0x55, 0x51},
+ {0x00, 0x00, 0x01},
+ {0x1d, 0x55, 0x51},
+ {0x00, 0x00, 0x01},
+ {0x02, 0xaa, 0xa1},
+ {0x08, 0x00, 0x01},
+ {0x0a, 0xaa, 0xa1},
+ {0x15, 0x55, 0x51},
+ {0x05, 0x55, 0x51},
+ {0x0a, 0xaa, 0xa1},
+ {0x10, 0x00, 0x01},
+ {0x15, 0x55, 0x51},
+ {0x1a, 0xaa, 0xa1},
+ {0x00, 0x00, 0x01},
+ {0x05, 0x55, 0x51},
+ {0x0a, 0xaa, 0xa1},
+ {0x15, 0x55, 0x51},
+ {0x00, 0x00, 0x01},
+ {0x0a, 0xaa, 0xa1},
+ {0x15, 0x55, 0x51},
+ {0x15, 0x55, 0x51},
+ {0x00, 0x00, 0x01},
+ {0x0a, 0xaa, 0xa1},
+ {0x15, 0x55, 0x51},
+ {0x00, 0x00, 0x01},
+ {0x0a, 0xaa, 0xa1},
+ {0x15, 0x55, 0x51},
+ {0x00, 0x00, 0x01},
+ {0x0a, 0xaa, 0xa1},
+ {0x15, 0x55, 0x51},
+ {0x00, 0x00, 0x01},
+ {0x18, 0x00, 0x01},
+ {0x02, 0xaa, 0xa1},
+ {0x0d, 0x55, 0x51},
+ {0x18, 0x00, 0x01},
+ {0x02, 0xaa, 0xb1}
+};
+
+static u8 al7230_channel_table2[CB_MAX_CHANNEL][3] = {
+ {0x7f, 0xd7, 0x84},
+ {0x7f, 0xd7, 0x84},
+ {0x7f, 0xd7, 0x84},
+ {0x7f, 0xd7, 0x84},
+ {0x7f, 0xd7, 0x84},
+ {0x7f, 0xd7, 0x84},
+ {0x7f, 0xd7, 0x84},
+ {0x7f, 0xd7, 0x84},
+ {0x7f, 0xd7, 0x84},
+ {0x7f, 0xd7, 0x84},
+ {0x7f, 0xd7, 0x84},
+ {0x7f, 0xd7, 0x84},
+ {0x7f, 0xd7, 0x84},
+ {0x7f, 0xd7, 0x84},
+ {0x7f, 0xd7, 0x84}, /* channel = 15 Tf = 4915MHz */
+ {0x6f, 0xd7, 0x84},
+ {0x7f, 0xd7, 0x84},
+ {0x7f, 0xd7, 0x84},
+ {0x7f, 0xd7, 0x84},
+ {0x7f, 0xd7, 0x84},
+ {0x7f, 0xd7, 0x84},
+ {0x6f, 0xd7, 0x84},
+ {0x7f, 0xd7, 0x84},
+ {0x6f, 0xd7, 0x84},
+ {0x7f, 0xd7, 0x84},
+ {0x7f, 0xd7, 0x84},
+ {0x7f, 0xd7, 0x84},
+ {0x7f, 0xd7, 0x84},
+ {0x7f, 0xd7, 0x84},
+ {0x7f, 0xd7, 0x84},
+ {0x7f, 0xd7, 0x84},
+ {0x7f, 0xd7, 0x84},
+ {0x7f, 0xd7, 0x84},
+ {0x6f, 0xd7, 0x84},
+ {0x7f, 0xd7, 0x84},
+ {0x7f, 0xd7, 0x84},
+ {0x7f, 0xd7, 0x84},
+ {0x6f, 0xd7, 0x84},
+ {0x7f, 0xd7, 0x84},
+ {0x7f, 0xd7, 0x84},
+ {0x7f, 0xd7, 0x84},
+ {0x6f, 0xd7, 0x84},
+ {0x7f, 0xd7, 0x84},
+ {0x7f, 0xd7, 0x84},
+ {0x6f, 0xd7, 0x84},
+ {0x7f, 0xd7, 0x84},
+ {0x7f, 0xd7, 0x84},
+ {0x6f, 0xd7, 0x84},
+ {0x7f, 0xd7, 0x84},
+ {0x7f, 0xd7, 0x84},
+ {0x6f, 0xd7, 0x84},
+ {0x7f, 0xd7, 0x84},
+ {0x7f, 0xd7, 0x84},
+ {0x7f, 0xd7, 0x84},
+ {0x7f, 0xd7, 0x84},
+ {0x7f, 0xd7, 0x84}
+};
+
+static u8 vt3226_init_table[CB_VT3226_INIT_SEQ][3] = {
+ {0x03, 0xff, 0x80},
+ {0x02, 0x82, 0xa1},
+ {0x03, 0xc6, 0xa2},
+ {0x01, 0x97, 0x93},
+ {0x03, 0x66, 0x64},
+ {0x00, 0x61, 0xa5},
+ {0x01, 0x7b, 0xd6},
+ {0x00, 0x80, 0x17},
+ {0x03, 0xf8, 0x08},
+ {0x00, 0x02, 0x39},
+ {0x02, 0x00, 0x2a}
+};
+
+static u8 vt3226d0_init_table[CB_VT3226_INIT_SEQ][3] = {
+ {0x03, 0xff, 0x80},
+ {0x03, 0x02, 0x21},
+ {0x03, 0xc6, 0xa2},
+ {0x01, 0x97, 0x93},
+ {0x03, 0x66, 0x64},
+ {0x00, 0x71, 0xa5},
+ {0x01, 0x15, 0xc6},
+ {0x01, 0x2e, 0x07},
+ {0x00, 0x58, 0x08},
+ {0x00, 0x02, 0x79},
+ {0x02, 0x01, 0xaa}
+};
+
+static u8 vt3226_channel_table0[CB_MAX_CHANNEL_24G][3] = {
+ {0x01, 0x97, 0x83},
+ {0x01, 0x97, 0x83},
+ {0x01, 0x97, 0x93},
+ {0x01, 0x97, 0x93},
+ {0x01, 0x97, 0x93},
+ {0x01, 0x97, 0x93},
+ {0x01, 0x97, 0xa3},
+ {0x01, 0x97, 0xa3},
+ {0x01, 0x97, 0xa3},
+ {0x01, 0x97, 0xa3},
+ {0x01, 0x97, 0xb3},
+ {0x01, 0x97, 0xb3},
+ {0x01, 0x97, 0xb3},
+ {0x03, 0x37, 0xc3}
+};
+
+static u8 vt3226_channel_table1[CB_MAX_CHANNEL_24G][3] = {
+ {0x02, 0x66, 0x64},
+ {0x03, 0x66, 0x64},
+ {0x00, 0x66, 0x64},
+ {0x01, 0x66, 0x64},
+ {0x02, 0x66, 0x64},
+ {0x03, 0x66, 0x64},
+ {0x00, 0x66, 0x64},
+ {0x01, 0x66, 0x64},
+ {0x02, 0x66, 0x64},
+ {0x03, 0x66, 0x64},
+ {0x00, 0x66, 0x64},
+ {0x01, 0x66, 0x64},
+ {0x02, 0x66, 0x64},
+ {0x00, 0xcc, 0xc4}
+};
+
+static const u32 vt3226d0_lo_current_table[CB_MAX_CHANNEL_24G] = {
+ 0x0135c600,
+ 0x0135c600,
+ 0x0235c600,
+ 0x0235c600,
+ 0x0235c600,
+ 0x0335c600,
+ 0x0335c600,
+ 0x0335c600,
+ 0x0335c600,
+ 0x0335c600,
+ 0x0335c600,
+ 0x0335c600,
+ 0x0335c600,
+ 0x0135c600
+};
+
+static u8 vt3342a0_init_table[CB_VT3342_INIT_SEQ][3] = { /* 11b/g mode */
+ {0x03, 0xff, 0x80},
+ {0x02, 0x08, 0x81},
+ {0x00, 0xc6, 0x02},
+ {0x03, 0xc5, 0x13},
+ {0x00, 0xee, 0xe4},
+ {0x00, 0x71, 0xa5},
+ {0x01, 0x75, 0x46},
+ {0x01, 0x40, 0x27},
+ {0x01, 0x54, 0x08},
+ {0x00, 0x01, 0x69},
+ {0x02, 0x00, 0xaa},
+ {0x00, 0x08, 0xcb},
+ {0x01, 0x70, 0x0c}
+};
+
+static u8 vt3342_channel_table0[CB_MAX_CHANNEL][3] = {
+ {0x02, 0x05, 0x03},
+ {0x01, 0x15, 0x03},
+ {0x03, 0xc5, 0x03},
+ {0x02, 0x65, 0x03},
+ {0x01, 0x15, 0x13},
+ {0x03, 0xc5, 0x13},
+ {0x02, 0x05, 0x13},
+ {0x01, 0x15, 0x13},
+ {0x03, 0xc5, 0x13},
+ {0x02, 0x65, 0x13},
+ {0x01, 0x15, 0x23},
+ {0x03, 0xc5, 0x23},
+ {0x02, 0x05, 0x23},
+ {0x00, 0xd5, 0x23},
+ {0x01, 0x15, 0x13}, /* channel = 15 Tf = 4915MHz */
+ {0x01, 0x15, 0x13},
+ {0x01, 0x15, 0x13},
+ {0x01, 0x15, 0x13},
+ {0x01, 0x15, 0x13},
+ {0x01, 0x15, 0x13},
+ {0x01, 0x15, 0x13},
+ {0x01, 0x15, 0x13},
+ {0x01, 0x15, 0x13},
+ {0x01, 0x15, 0x13},
+ {0x01, 0x15, 0x13},
+ {0x01, 0x15, 0x13},
+ {0x01, 0x15, 0x13},
+ {0x01, 0x15, 0x13},
+ {0x01, 0x15, 0x13},
+ {0x01, 0x55, 0x63},
+ {0x01, 0x55, 0x63},
+ {0x02, 0xa5, 0x63},
+ {0x02, 0xa5, 0x63},
+ {0x00, 0x05, 0x73},
+ {0x00, 0x05, 0x73},
+ {0x01, 0x55, 0x73},
+ {0x02, 0xa5, 0x73},
+ {0x00, 0x05, 0x83},
+ {0x01, 0x55, 0x83},
+ {0x02, 0xa5, 0x83},
+ {0x02, 0xa5, 0x83},
+ {0x02, 0xa5, 0x83},
+ {0x02, 0xa5, 0x83},
+ {0x02, 0xa5, 0x83},
+ {0x02, 0xa5, 0x83},
+ {0x02, 0xa5, 0x83},
+ {0x02, 0xa5, 0x83},
+ {0x02, 0xa5, 0x83},
+ {0x02, 0xa5, 0x83},
+ {0x02, 0xa5, 0x83},
+ {0x02, 0xa5, 0x83},
+ {0x00, 0x05, 0xF3},
+ {0x01, 0x56, 0x03},
+ {0x02, 0xa6, 0x03},
+ {0x00, 0x06, 0x03},
+ {0x00, 0x06, 0x03}
+};
+
+static u8 vt3342_channel_table1[CB_MAX_CHANNEL][3] = {
+ {0x01, 0x99, 0x94},
+ {0x02, 0x44, 0x44},
+ {0x02, 0xee, 0xe4},
+ {0x03, 0x99, 0x94},
+ {0x00, 0x44, 0x44},
+ {0x00, 0xee, 0xe4},
+ {0x01, 0x99, 0x94},
+ {0x02, 0x44, 0x44},
+ {0x02, 0xee, 0xe4},
+ {0x03, 0x99, 0x94},
+ {0x00, 0x44, 0x44},
+ {0x00, 0xee, 0xe4},
+ {0x01, 0x99, 0x94},
+ {0x03, 0x33, 0x34},
+ {0x00, 0x44, 0x44}, /* channel = 15 Tf = 4915MHz */
+ {0x00, 0x44, 0x44},
+ {0x00, 0x44, 0x44},
+ {0x00, 0x44, 0x44},
+ {0x00, 0x44, 0x44},
+ {0x00, 0x44, 0x44},
+ {0x00, 0x44, 0x44},
+ {0x00, 0x44, 0x44},
+ {0x00, 0x44, 0x44},
+ {0x00, 0x44, 0x44},
+ {0x00, 0x44, 0x44},
+ {0x00, 0x44, 0x44},
+ {0x00, 0x44, 0x44},
+ {0x00, 0x44, 0x44},
+ {0x00, 0x44, 0x44},
+ {0x01, 0x55, 0x54},
+ {0x01, 0x55, 0x54},
+ {0x02, 0xaa, 0xa4},
+ {0x02, 0xaa, 0xa4},
+ {0x00, 0x00, 0x04},
+ {0x00, 0x00, 0x04},
+ {0x01, 0x55, 0x54},
+ {0x02, 0xaa, 0xa4},
+ {0x00, 0x00, 0x04},
+ {0x01, 0x55, 0x54},
+ {0x02, 0xaa, 0xa4},
+ {0x02, 0xaa, 0xa4},
+ {0x02, 0xaa, 0xa4},
+ {0x02, 0xaa, 0xa4},
+ {0x02, 0xaa, 0xa4},
+ {0x02, 0xaa, 0xa4},
+ {0x02, 0xaa, 0xa4},
+ {0x02, 0xaa, 0xa4},
+ {0x02, 0xaa, 0xa4},
+ {0x02, 0xaa, 0xa4},
+ {0x02, 0xaa, 0xa4},
+ {0x02, 0xaa, 0xa4},
+ {0x03, 0x00, 0x04},
+ {0x00, 0x55, 0x54},
+ {0x01, 0xaa, 0xa4},
+ {0x03, 0x00, 0x04},
+ {0x03, 0x00, 0x04}
+};
+
+/* Power Table */
+static const u32 al2230_power_table[AL2230_PWR_IDX_LEN] = {
+ 0x04040900,
+ 0x04041900,
+ 0x04042900,
+ 0x04043900,
+ 0x04044900,
+ 0x04045900,
+ 0x04046900,
+ 0x04047900,
+ 0x04048900,
+ 0x04049900,
+ 0x0404a900,
+ 0x0404b900,
+ 0x0404c900,
+ 0x0404d900,
+ 0x0404e900,
+ 0x0404f900,
+ 0x04050900,
+ 0x04051900,
+ 0x04052900,
+ 0x04053900,
+ 0x04054900,
+ 0x04055900,
+ 0x04056900,
+ 0x04057900,
+ 0x04058900,
+ 0x04059900,
+ 0x0405a900,
+ 0x0405b900,
+ 0x0405c900,
+ 0x0405d900,
+ 0x0405e900,
+ 0x0405f900,
+ 0x04060900,
+ 0x04061900,
+ 0x04062900,
+ 0x04063900,
+ 0x04064900,
+ 0x04065900,
+ 0x04066900,
+ 0x04067900,
+ 0x04068900,
+ 0x04069900,
+ 0x0406a900,
+ 0x0406b900,
+ 0x0406c900,
+ 0x0406d900,
+ 0x0406e900,
+ 0x0406f900,
+ 0x04070900,
+ 0x04071900,
+ 0x04072900,
+ 0x04073900,
+ 0x04074900,
+ 0x04075900,
+ 0x04076900,
+ 0x04077900,
+ 0x04078900,
+ 0x04079900,
+ 0x0407a900,
+ 0x0407b900,
+ 0x0407c900,
+ 0x0407d900,
+ 0x0407e900,
+ 0x0407f900
+};
+
+/*
+ * Description: Write to IF/RF, by embedded programming
+ */
+int vnt_rf_write_embedded(struct vnt_private *priv, u32 data)
+{
+ u8 reg_data[4];
+
+ data |= (VNT_RF_REG_LEN << 3) | IFREGCTL_REGW;
+
+ reg_data[0] = (u8)data;
+ reg_data[1] = (u8)(data >> 8);
+ reg_data[2] = (u8)(data >> 16);
+ reg_data[3] = (u8)(data >> 24);
+
+ vnt_control_out(priv, MESSAGE_TYPE_WRITE_IFRF,
+ 0, 0, ARRAY_SIZE(reg_data), reg_data);
+
+ return true;
+}
+
+/* Set Tx power by rate and channel number */
+int vnt_rf_setpower(struct vnt_private *priv, u32 rate, u32 channel)
+{
+ u8 power = priv->cck_pwr;
+
+ if (channel == 0)
+ return -EINVAL;
+
+ switch (rate) {
+ case RATE_1M:
+ case RATE_2M:
+ case RATE_5M:
+ case RATE_11M:
+ channel--;
+
+ if (channel < sizeof(priv->cck_pwr_tbl))
+ power = priv->cck_pwr_tbl[channel];
+ break;
+ case RATE_6M:
+ case RATE_9M:
+ case RATE_12M:
+ case RATE_18M:
+ case RATE_24M:
+ case RATE_36M:
+ case RATE_48M:
+ case RATE_54M:
+ if (channel > CB_MAX_CHANNEL_24G)
+ power = priv->ofdm_a_pwr_tbl[channel-15];
+ else
+ power = priv->ofdm_pwr_tbl[channel-1];
+ break;
+ }
+
+ return vnt_rf_set_txpower(priv, power, rate);
+}
+
+static u8 vnt_rf_addpower(struct vnt_private *priv)
+{
+ s32 rssi = -priv->current_rssi;
+
+ if (!rssi)
+ return 7;
+
+ if (priv->rf_type == RF_VT3226D0) {
+ if (rssi < -70)
+ return 9;
+ else if (rssi < -65)
+ return 7;
+ else if (rssi < -60)
+ return 5;
+ } else {
+ if (rssi < -80)
+ return 9;
+ else if (rssi < -75)
+ return 7;
+ else if (rssi < -70)
+ return 5;
+ }
+
+ return 0;
+}
+
+/* Set Tx power by power level and rate */
+int vnt_rf_set_txpower(struct vnt_private *priv, u8 power, u32 rate)
+{
+ u32 power_setting = 0;
+ int ret = true;
+
+ power += vnt_rf_addpower(priv);
+ if (power > VNT_RF_MAX_POWER)
+ power = VNT_RF_MAX_POWER;
+
+ if (priv->power == power)
+ return true;
+
+ priv->power = power;
+
+ switch (priv->rf_type) {
+ case RF_AL2230:
+ if (power >= AL2230_PWR_IDX_LEN)
+ return false;
+
+ ret &= vnt_rf_write_embedded(priv, al2230_power_table[power]);
+
+ if (rate <= RATE_11M)
+ ret &= vnt_rf_write_embedded(priv, 0x0001b400);
+ else
+ ret &= vnt_rf_write_embedded(priv, 0x0005a400);
+ break;
+ case RF_AL2230S:
+ if (power >= AL2230_PWR_IDX_LEN)
+ return false;
+
+ ret &= vnt_rf_write_embedded(priv, al2230_power_table[power]);
+
+ if (rate <= RATE_11M) {
+ ret &= vnt_rf_write_embedded(priv, 0x040c1400);
+ ret &= vnt_rf_write_embedded(priv, 0x00299b00);
+ } else {
+ ret &= vnt_rf_write_embedded(priv, 0x0005a400);
+ ret &= vnt_rf_write_embedded(priv, 0x00099b00);
+ }
+ break;
+
+ case RF_AIROHA7230:
+ if (rate <= RATE_11M)
+ ret &= vnt_rf_write_embedded(priv, 0x111bb900);
+ else
+ ret &= vnt_rf_write_embedded(priv, 0x221bb900);
+
+ if (power >= AL7230_PWR_IDX_LEN)
+ return false;
+
+ /*
+ * 0x080F1B00 for 3 wire control TxGain(D10)
+ * and 0x31 as TX Gain value
+ */
+ power_setting = 0x080c0b00 | (power << 12);
+
+ ret &= vnt_rf_write_embedded(priv, power_setting);
+
+ break;
+
+ case RF_VT3226:
+ if (power >= VT3226_PWR_IDX_LEN)
+ return false;
+ power_setting = ((0x3f - power) << 20) | (0x17 << 8);
+
+ ret &= vnt_rf_write_embedded(priv, power_setting);
+
+ break;
+ case RF_VT3226D0:
+ if (power >= VT3226_PWR_IDX_LEN)
+ return false;
+
+ if (rate <= RATE_11M) {
+ u16 hw_value = priv->hw->conf.chandef.chan->hw_value;
+
+ power_setting = ((0x3f - power) << 20) | (0xe07 << 8);
+
+ ret &= vnt_rf_write_embedded(priv, power_setting);
+ ret &= vnt_rf_write_embedded(priv, 0x03c6a200);
+
+ dev_dbg(&priv->usb->dev,
+ "%s 11b channel [%d]\n", __func__, hw_value);
+
+ hw_value--;
+
+ if (hw_value < ARRAY_SIZE(vt3226d0_lo_current_table))
+ ret &= vnt_rf_write_embedded(priv,
+ vt3226d0_lo_current_table[hw_value]);
+
+ ret &= vnt_rf_write_embedded(priv, 0x015C0800);
+ } else {
+ dev_dbg(&priv->usb->dev,
+ "@@@@ vnt_rf_set_txpower> 11G mode\n");
+
+ power_setting = ((0x3f - power) << 20) | (0x7 << 8);
+
+ ret &= vnt_rf_write_embedded(priv, power_setting);
+ ret &= vnt_rf_write_embedded(priv, 0x00C6A200);
+ ret &= vnt_rf_write_embedded(priv, 0x016BC600);
+ ret &= vnt_rf_write_embedded(priv, 0x00900800);
+ }
+ break;
+
+ case RF_VT3342A0:
+ if (power >= VT3342_PWR_IDX_LEN)
+ return false;
+
+ power_setting = ((0x3f - power) << 20) | (0x27 << 8);
+
+ ret &= vnt_rf_write_embedded(priv, power_setting);
+
+ break;
+ default:
+ break;
+ }
+ return ret;
+}
+
+/* Convert rssi to dbm */
+void vnt_rf_rssi_to_dbm(struct vnt_private *priv, u8 rssi, long *dbm)
+{
+ u8 idx = (((rssi & 0xc0) >> 6) & 0x03);
+ long b = (rssi & 0x3f);
+ long a = 0;
+ u8 airoharf[4] = {0, 18, 0, 40};
+
+ switch (priv->rf_type) {
+ case RF_AL2230:
+ case RF_AL2230S:
+ case RF_AIROHA7230:
+ case RF_VT3226:
+ case RF_VT3226D0:
+ case RF_VT3342A0:
+ a = airoharf[idx];
+ break;
+ default:
+ break;
+ }
+
+ *dbm = -1 * (a + b * 2);
+}
+
+void vnt_rf_table_download(struct vnt_private *priv)
+{
+ u16 length1 = 0, length2 = 0, length3 = 0;
+ u8 *addr1 = NULL, *addr2 = NULL, *addr3 = NULL;
+ u16 length, value;
+ u8 array[256];
+
+ switch (priv->rf_type) {
+ case RF_AL2230:
+ case RF_AL2230S:
+ length1 = CB_AL2230_INIT_SEQ * 3;
+ length2 = CB_MAX_CHANNEL_24G * 3;
+ length3 = CB_MAX_CHANNEL_24G * 3;
+ addr1 = &al2230_init_table[0][0];
+ addr2 = &al2230_channel_table0[0][0];
+ addr3 = &al2230_channel_table1[0][0];
+ break;
+ case RF_AIROHA7230:
+ length1 = CB_AL7230_INIT_SEQ * 3;
+ length2 = CB_MAX_CHANNEL * 3;
+ length3 = CB_MAX_CHANNEL * 3;
+ addr1 = &al7230_init_table[0][0];
+ addr2 = &al7230_channel_table0[0][0];
+ addr3 = &al7230_channel_table1[0][0];
+ break;
+ case RF_VT3226:
+ length1 = CB_VT3226_INIT_SEQ * 3;
+ length2 = CB_MAX_CHANNEL_24G * 3;
+ length3 = CB_MAX_CHANNEL_24G * 3;
+ addr1 = &vt3226_init_table[0][0];
+ addr2 = &vt3226_channel_table0[0][0];
+ addr3 = &vt3226_channel_table1[0][0];
+ break;
+ case RF_VT3226D0:
+ length1 = CB_VT3226_INIT_SEQ * 3;
+ length2 = CB_MAX_CHANNEL_24G * 3;
+ length3 = CB_MAX_CHANNEL_24G * 3;
+ addr1 = &vt3226d0_init_table[0][0];
+ addr2 = &vt3226_channel_table0[0][0];
+ addr3 = &vt3226_channel_table1[0][0];
+ break;
+ case RF_VT3342A0:
+ length1 = CB_VT3342_INIT_SEQ * 3;
+ length2 = CB_MAX_CHANNEL * 3;
+ length3 = CB_MAX_CHANNEL * 3;
+ addr1 = &vt3342a0_init_table[0][0];
+ addr2 = &vt3342_channel_table0[0][0];
+ addr3 = &vt3342_channel_table1[0][0];
+ break;
+ }
+
+ /* Init Table */
+ memcpy(array, addr1, length1);
+
+ vnt_control_out(priv, MESSAGE_TYPE_WRITE, 0,
+ MESSAGE_REQUEST_RF_INIT, length1, array);
+
+ /* Channel Table 0 */
+ value = 0;
+ while (length2 > 0) {
+ if (length2 >= 64)
+ length = 64;
+ else
+ length = length2;
+
+ memcpy(array, addr2, length);
+
+ vnt_control_out(priv, MESSAGE_TYPE_WRITE,
+ value, MESSAGE_REQUEST_RF_CH0, length, array);
+
+ length2 -= length;
+ value += length;
+ addr2 += length;
+ }
+
+ /* Channel table 1 */
+ value = 0;
+ while (length3 > 0) {
+ if (length3 >= 64)
+ length = 64;
+ else
+ length = length3;
+
+ memcpy(array, addr3, length);
+
+ vnt_control_out(priv, MESSAGE_TYPE_WRITE,
+ value, MESSAGE_REQUEST_RF_CH1, length, array);
+
+ length3 -= length;
+ value += length;
+ addr3 += length;
+ }
+
+ if (priv->rf_type == RF_AIROHA7230) {
+ length1 = CB_AL7230_INIT_SEQ * 3;
+ length2 = CB_MAX_CHANNEL * 3;
+ addr1 = &(al7230_init_table_amode[0][0]);
+ addr2 = &(al7230_channel_table2[0][0]);
+
+ memcpy(array, addr1, length1);
+
+ /* Init Table 2 */
+ vnt_control_out(priv, MESSAGE_TYPE_WRITE,
+ 0, MESSAGE_REQUEST_RF_INIT2, length1, array);
+
+ /* Channel Table 0 */
+ value = 0;
+ while (length2 > 0) {
+ if (length2 >= 64)
+ length = 64;
+ else
+ length = length2;
+
+ memcpy(array, addr2, length);
+
+ vnt_control_out(priv, MESSAGE_TYPE_WRITE,
+ value, MESSAGE_REQUEST_RF_CH2, length, array);
+
+ length2 -= length;
+ value += length;
+ addr2 += length;
+ }
+ }
+}
diff --git a/drivers/staging/vt6656/rf.h b/drivers/staging/vt6656/rf.h
new file mode 100644
index 000000000..3acdc65b1
--- /dev/null
+++ b/drivers/staging/vt6656/rf.h
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 1996, 2003 VIA Networking Technologies, Inc.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ *
+ * File: rf.h
+ *
+ * Purpose:
+ *
+ * Author: Jerry Chen
+ *
+ * Date: Feb. 19, 2004
+ *
+ */
+
+#ifndef __RF_H__
+#define __RF_H__
+
+#include "device.h"
+
+/* Baseband RF pair definition in eeprom (Bits 6..0) */
+#define RF_RFMD2959 0x01
+#define RF_MAXIMAG 0x02
+#define RF_AL2230 0x03
+#define RF_GCT5103 0x04
+#define RF_UW2451 0x05
+#define RF_MAXIMG 0x06
+#define RF_MAXIM2829 0x07
+#define RF_UW2452 0x08
+#define RF_VT3226 0x09
+#define RF_AIROHA7230 0x0a
+#define RF_UW2453 0x0b
+#define RF_VT3226D0 0x0c /* RobertYu:20051114 */
+#define RF_VT3342A0 0x0d /* RobertYu:20060609 */
+#define RF_AL2230S 0x0e
+
+#define RF_EMU 0x80
+#define RF_MASK 0x7F
+
+#define VNT_RF_MAX_POWER 0x3f
+#define VNT_RF_REG_LEN 0x17 /* 24 bit length */
+
+int vnt_rf_write_embedded(struct vnt_private *, u32);
+int vnt_rf_setpower(struct vnt_private *, u32, u32);
+int vnt_rf_set_txpower(struct vnt_private *, u8, u32);
+void vnt_rf_rssi_to_dbm(struct vnt_private *, u8, long *);
+void vnt_rf_table_download(struct vnt_private *);
+
+#endif /* __RF_H__ */
diff --git a/drivers/staging/vt6656/rxtx.c b/drivers/staging/vt6656/rxtx.c
new file mode 100644
index 000000000..5c589962a
--- /dev/null
+++ b/drivers/staging/vt6656/rxtx.c
@@ -0,0 +1,1112 @@
+/*
+ * Copyright (c) 1996, 2003 VIA Networking Technologies, Inc.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * File: rxtx.c
+ *
+ * Purpose: handle WMAC/802.3/802.11 rx & tx functions
+ *
+ * Author: Lyndon Chen
+ *
+ * Date: May 20, 2003
+ *
+ * Functions:
+ * vnt_generate_tx_parameter - Generate tx dma required parameter.
+ * vnt_get_duration_le - get tx data required duration
+ * vnt_get_rtscts_duration_le- get rtx/cts required duration
+ * vnt_get_rtscts_rsvtime_le- get rts/cts reserved time
+ * vnt_get_rsvtime- get frame reserved time
+ * vnt_fill_cts_head- fulfill CTS ctl header
+ *
+ * Revision History:
+ *
+ */
+
+#include <linux/etherdevice.h>
+#include "device.h"
+#include "rxtx.h"
+#include "card.h"
+#include "mac.h"
+#include "rf.h"
+#include "usbpipe.h"
+
+static const u16 vnt_time_stampoff[2][MAX_RATE] = {
+ {384, 288, 226, 209, 54, 43, 37, 31, 28, 25, 24, 23},/* Long Preamble */
+ {384, 192, 130, 113, 54, 43, 37, 31, 28, 25, 24, 23},/* Short Preamble */
+};
+
+static const u16 vnt_fb_opt0[2][5] = {
+ {RATE_12M, RATE_18M, RATE_24M, RATE_36M, RATE_48M}, /* fallback_rate0 */
+ {RATE_12M, RATE_12M, RATE_18M, RATE_24M, RATE_36M}, /* fallback_rate1 */
+};
+
+static const u16 vnt_fb_opt1[2][5] = {
+ {RATE_12M, RATE_18M, RATE_24M, RATE_24M, RATE_36M}, /* fallback_rate0 */
+ {RATE_6M, RATE_6M, RATE_12M, RATE_12M, RATE_18M}, /* fallback_rate1 */
+};
+
+#define RTSDUR_BB 0
+#define RTSDUR_BA 1
+#define RTSDUR_AA 2
+#define CTSDUR_BA 3
+#define RTSDUR_BA_F0 4
+#define RTSDUR_AA_F0 5
+#define RTSDUR_BA_F1 6
+#define RTSDUR_AA_F1 7
+#define CTSDUR_BA_F0 8
+#define CTSDUR_BA_F1 9
+#define DATADUR_B 10
+#define DATADUR_A 11
+#define DATADUR_A_F0 12
+#define DATADUR_A_F1 13
+
+static struct vnt_usb_send_context
+ *vnt_get_free_context(struct vnt_private *priv)
+{
+ struct vnt_usb_send_context *context = NULL;
+ int ii;
+
+ dev_dbg(&priv->usb->dev, "%s\n", __func__);
+
+ for (ii = 0; ii < priv->num_tx_context; ii++) {
+ if (!priv->tx_context[ii])
+ return NULL;
+
+ context = priv->tx_context[ii];
+ if (context->in_use == false) {
+ context->in_use = true;
+ memset(context->data, 0,
+ MAX_TOTAL_SIZE_WITH_ALL_HEADERS);
+
+ context->hdr = NULL;
+
+ return context;
+ }
+ }
+
+ if (ii == priv->num_tx_context)
+ dev_dbg(&priv->usb->dev, "%s No Free Tx Context\n", __func__);
+
+ return NULL;
+}
+
+static __le16 vnt_time_stamp_off(struct vnt_private *priv, u16 rate)
+{
+ return cpu_to_le16(vnt_time_stampoff[priv->preamble_type % 2]
+ [rate % MAX_RATE]);
+}
+
+static u32 vnt_get_rsvtime(struct vnt_private *priv, u8 pkt_type,
+ u32 frame_length, u16 rate, int need_ack)
+{
+ u32 data_time, ack_time;
+
+ data_time = vnt_get_frame_time(priv->preamble_type, pkt_type,
+ frame_length, rate);
+
+ if (pkt_type == PK_TYPE_11B)
+ ack_time = vnt_get_frame_time(priv->preamble_type, pkt_type,
+ 14, (u16)priv->top_cck_basic_rate);
+ else
+ ack_time = vnt_get_frame_time(priv->preamble_type, pkt_type,
+ 14, (u16)priv->top_ofdm_basic_rate);
+
+ if (need_ack)
+ return data_time + priv->sifs + ack_time;
+
+ return data_time;
+}
+
+static __le16 vnt_rxtx_rsvtime_le16(struct vnt_private *priv, u8 pkt_type,
+ u32 frame_length, u16 rate, int need_ack)
+{
+ return cpu_to_le16((u16)vnt_get_rsvtime(priv, pkt_type,
+ frame_length, rate, need_ack));
+}
+
+static __le16 vnt_get_rtscts_rsvtime_le(struct vnt_private *priv,
+ u8 rsv_type, u8 pkt_type, u32 frame_length, u16 current_rate)
+{
+ u32 rrv_time, rts_time, cts_time, ack_time, data_time;
+
+ rrv_time = rts_time = cts_time = ack_time = data_time = 0;
+
+ data_time = vnt_get_frame_time(priv->preamble_type, pkt_type,
+ frame_length, current_rate);
+
+ if (rsv_type == 0) {
+ rts_time = vnt_get_frame_time(priv->preamble_type,
+ pkt_type, 20, priv->top_cck_basic_rate);
+ cts_time = ack_time = vnt_get_frame_time(priv->preamble_type,
+ pkt_type, 14, priv->top_cck_basic_rate);
+ } else if (rsv_type == 1) {
+ rts_time = vnt_get_frame_time(priv->preamble_type,
+ pkt_type, 20, priv->top_cck_basic_rate);
+ cts_time = vnt_get_frame_time(priv->preamble_type, pkt_type,
+ 14, priv->top_cck_basic_rate);
+ ack_time = vnt_get_frame_time(priv->preamble_type, pkt_type,
+ 14, priv->top_ofdm_basic_rate);
+ } else if (rsv_type == 2) {
+ rts_time = vnt_get_frame_time(priv->preamble_type, pkt_type,
+ 20, priv->top_ofdm_basic_rate);
+ cts_time = ack_time = vnt_get_frame_time(priv->preamble_type,
+ pkt_type, 14, priv->top_ofdm_basic_rate);
+ } else if (rsv_type == 3) {
+ cts_time = vnt_get_frame_time(priv->preamble_type, pkt_type,
+ 14, priv->top_cck_basic_rate);
+ ack_time = vnt_get_frame_time(priv->preamble_type, pkt_type,
+ 14, priv->top_ofdm_basic_rate);
+
+ rrv_time = cts_time + ack_time + data_time + 2 * priv->sifs;
+
+ return cpu_to_le16((u16)rrv_time);
+ }
+
+ rrv_time = rts_time + cts_time + ack_time + data_time + 3 * priv->sifs;
+
+ return cpu_to_le16((u16)rrv_time);
+}
+
+static __le16 vnt_get_duration_le(struct vnt_private *priv,
+ u8 pkt_type, int need_ack)
+{
+ u32 ack_time = 0;
+
+ if (need_ack) {
+ if (pkt_type == PK_TYPE_11B)
+ ack_time = vnt_get_frame_time(priv->preamble_type,
+ pkt_type, 14, priv->top_cck_basic_rate);
+ else
+ ack_time = vnt_get_frame_time(priv->preamble_type,
+ pkt_type, 14, priv->top_ofdm_basic_rate);
+
+ return cpu_to_le16((u16)(priv->sifs + ack_time));
+ }
+
+ return 0;
+}
+
+static __le16 vnt_get_rtscts_duration_le(struct vnt_usb_send_context *context,
+ u8 dur_type, u8 pkt_type, u16 rate)
+{
+ struct vnt_private *priv = context->priv;
+ u32 cts_time = 0, dur_time = 0;
+ u32 frame_length = context->frame_len;
+ u8 need_ack = context->need_ack;
+
+ switch (dur_type) {
+ case RTSDUR_BB:
+ case RTSDUR_BA:
+ case RTSDUR_BA_F0:
+ case RTSDUR_BA_F1:
+ cts_time = vnt_get_frame_time(priv->preamble_type,
+ pkt_type, 14, priv->top_cck_basic_rate);
+ dur_time = cts_time + 2 * priv->sifs +
+ vnt_get_rsvtime(priv, pkt_type,
+ frame_length, rate, need_ack);
+ break;
+
+ case RTSDUR_AA:
+ case RTSDUR_AA_F0:
+ case RTSDUR_AA_F1:
+ cts_time = vnt_get_frame_time(priv->preamble_type,
+ pkt_type, 14, priv->top_ofdm_basic_rate);
+ dur_time = cts_time + 2 * priv->sifs +
+ vnt_get_rsvtime(priv, pkt_type,
+ frame_length, rate, need_ack);
+ break;
+
+ case CTSDUR_BA:
+ case CTSDUR_BA_F0:
+ case CTSDUR_BA_F1:
+ dur_time = priv->sifs + vnt_get_rsvtime(priv,
+ pkt_type, frame_length, rate, need_ack);
+ break;
+
+ default:
+ break;
+ }
+
+ return cpu_to_le16((u16)dur_time);
+}
+
+static u16 vnt_mac_hdr_pos(struct vnt_usb_send_context *tx_context,
+ struct ieee80211_hdr *hdr)
+{
+ u8 *head = tx_context->data + offsetof(struct vnt_tx_buffer, fifo_head);
+ u8 *hdr_pos = (u8 *)hdr;
+
+ tx_context->hdr = hdr;
+ if (!tx_context->hdr)
+ return 0;
+
+ return (u16)(hdr_pos - head);
+}
+
+static u16 vnt_rxtx_datahead_g(struct vnt_usb_send_context *tx_context,
+ struct vnt_tx_datahead_g *buf)
+{
+
+ struct vnt_private *priv = tx_context->priv;
+ struct ieee80211_hdr *hdr =
+ (struct ieee80211_hdr *)tx_context->skb->data;
+ u32 frame_len = tx_context->frame_len;
+ u16 rate = tx_context->tx_rate;
+ u8 need_ack = tx_context->need_ack;
+
+ /* Get SignalField,ServiceField,Length */
+ vnt_get_phy_field(priv, frame_len, rate, tx_context->pkt_type, &buf->a);
+ vnt_get_phy_field(priv, frame_len, priv->top_cck_basic_rate,
+ PK_TYPE_11B, &buf->b);
+
+ /* Get Duration and TimeStamp */
+ if (ieee80211_is_pspoll(hdr->frame_control)) {
+ __le16 dur = cpu_to_le16(priv->current_aid | BIT(14) | BIT(15));
+
+ buf->duration_a = dur;
+ buf->duration_b = dur;
+ } else {
+ buf->duration_a = vnt_get_duration_le(priv,
+ tx_context->pkt_type, need_ack);
+ buf->duration_b = vnt_get_duration_le(priv,
+ PK_TYPE_11B, need_ack);
+ }
+
+ buf->time_stamp_off_a = vnt_time_stamp_off(priv, rate);
+ buf->time_stamp_off_b = vnt_time_stamp_off(priv,
+ priv->top_cck_basic_rate);
+
+ tx_context->tx_hdr_size = vnt_mac_hdr_pos(tx_context, &buf->hdr);
+
+ return le16_to_cpu(buf->duration_a);
+}
+
+static u16 vnt_rxtx_datahead_g_fb(struct vnt_usb_send_context *tx_context,
+ struct vnt_tx_datahead_g_fb *buf)
+{
+ struct vnt_private *priv = tx_context->priv;
+ u32 frame_len = tx_context->frame_len;
+ u16 rate = tx_context->tx_rate;
+ u8 need_ack = tx_context->need_ack;
+
+ /* Get SignalField,ServiceField,Length */
+ vnt_get_phy_field(priv, frame_len, rate, tx_context->pkt_type, &buf->a);
+
+ vnt_get_phy_field(priv, frame_len, priv->top_cck_basic_rate,
+ PK_TYPE_11B, &buf->b);
+
+ /* Get Duration and TimeStamp */
+ buf->duration_a = vnt_get_duration_le(priv, tx_context->pkt_type,
+ need_ack);
+ buf->duration_b = vnt_get_duration_le(priv, PK_TYPE_11B, need_ack);
+
+ buf->duration_a_f0 = vnt_get_duration_le(priv, tx_context->pkt_type,
+ need_ack);
+ buf->duration_a_f1 = vnt_get_duration_le(priv, tx_context->pkt_type,
+ need_ack);
+
+ buf->time_stamp_off_a = vnt_time_stamp_off(priv, rate);
+ buf->time_stamp_off_b = vnt_time_stamp_off(priv,
+ priv->top_cck_basic_rate);
+
+ tx_context->tx_hdr_size = vnt_mac_hdr_pos(tx_context, &buf->hdr);
+
+ return le16_to_cpu(buf->duration_a);
+}
+
+static u16 vnt_rxtx_datahead_a_fb(struct vnt_usb_send_context *tx_context,
+ struct vnt_tx_datahead_a_fb *buf)
+{
+ struct vnt_private *priv = tx_context->priv;
+ u16 rate = tx_context->tx_rate;
+ u8 pkt_type = tx_context->pkt_type;
+ u8 need_ack = tx_context->need_ack;
+ u32 frame_len = tx_context->frame_len;
+
+ /* Get SignalField,ServiceField,Length */
+ vnt_get_phy_field(priv, frame_len, rate, pkt_type, &buf->a);
+ /* Get Duration and TimeStampOff */
+ buf->duration = vnt_get_duration_le(priv, pkt_type, need_ack);
+
+ buf->duration_f0 = vnt_get_duration_le(priv, pkt_type, need_ack);
+ buf->duration_f1 = vnt_get_duration_le(priv, pkt_type, need_ack);
+
+ buf->time_stamp_off = vnt_time_stamp_off(priv, rate);
+
+ tx_context->tx_hdr_size = vnt_mac_hdr_pos(tx_context, &buf->hdr);
+
+ return le16_to_cpu(buf->duration);
+}
+
+static u16 vnt_rxtx_datahead_ab(struct vnt_usb_send_context *tx_context,
+ struct vnt_tx_datahead_ab *buf)
+{
+ struct vnt_private *priv = tx_context->priv;
+ struct ieee80211_hdr *hdr =
+ (struct ieee80211_hdr *)tx_context->skb->data;
+ u32 frame_len = tx_context->frame_len;
+ u16 rate = tx_context->tx_rate;
+ u8 need_ack = tx_context->need_ack;
+
+ /* Get SignalField,ServiceField,Length */
+ vnt_get_phy_field(priv, frame_len, rate,
+ tx_context->pkt_type, &buf->ab);
+
+ /* Get Duration and TimeStampOff */
+ if (ieee80211_is_pspoll(hdr->frame_control)) {
+ __le16 dur = cpu_to_le16(priv->current_aid | BIT(14) | BIT(15));
+
+ buf->duration = dur;
+ } else {
+ buf->duration = vnt_get_duration_le(priv, tx_context->pkt_type,
+ need_ack);
+ }
+
+ buf->time_stamp_off = vnt_time_stamp_off(priv, rate);
+
+ tx_context->tx_hdr_size = vnt_mac_hdr_pos(tx_context, &buf->hdr);
+
+ return le16_to_cpu(buf->duration);
+}
+
+static int vnt_fill_ieee80211_rts(struct vnt_usb_send_context *tx_context,
+ struct ieee80211_rts *rts, __le16 duration)
+{
+ struct ieee80211_hdr *hdr =
+ (struct ieee80211_hdr *)tx_context->skb->data;
+
+ rts->duration = duration;
+ rts->frame_control =
+ cpu_to_le16(IEEE80211_FTYPE_CTL | IEEE80211_STYPE_RTS);
+
+ ether_addr_copy(rts->ra, hdr->addr1);
+ ether_addr_copy(rts->ta, hdr->addr2);
+
+ return 0;
+}
+
+static u16 vnt_rxtx_rts_g_head(struct vnt_usb_send_context *tx_context,
+ struct vnt_rts_g *buf)
+{
+ struct vnt_private *priv = tx_context->priv;
+ u16 rts_frame_len = 20;
+ u16 current_rate = tx_context->tx_rate;
+
+ vnt_get_phy_field(priv, rts_frame_len, priv->top_cck_basic_rate,
+ PK_TYPE_11B, &buf->b);
+ vnt_get_phy_field(priv, rts_frame_len, priv->top_ofdm_basic_rate,
+ tx_context->pkt_type, &buf->a);
+
+ buf->duration_bb = vnt_get_rtscts_duration_le(tx_context, RTSDUR_BB,
+ PK_TYPE_11B,
+ priv->top_cck_basic_rate);
+ buf->duration_aa = vnt_get_rtscts_duration_le(tx_context, RTSDUR_AA,
+ tx_context->pkt_type,
+ current_rate);
+ buf->duration_ba = vnt_get_rtscts_duration_le(tx_context, RTSDUR_BA,
+ tx_context->pkt_type,
+ current_rate);
+
+ vnt_fill_ieee80211_rts(tx_context, &buf->data, buf->duration_aa);
+
+ return vnt_rxtx_datahead_g(tx_context, &buf->data_head);
+}
+
+static u16 vnt_rxtx_rts_g_fb_head(struct vnt_usb_send_context *tx_context,
+ struct vnt_rts_g_fb *buf)
+{
+ struct vnt_private *priv = tx_context->priv;
+ u16 current_rate = tx_context->tx_rate;
+ u16 rts_frame_len = 20;
+
+ vnt_get_phy_field(priv, rts_frame_len, priv->top_cck_basic_rate,
+ PK_TYPE_11B, &buf->b);
+ vnt_get_phy_field(priv, rts_frame_len, priv->top_ofdm_basic_rate,
+ tx_context->pkt_type, &buf->a);
+
+ buf->duration_bb = vnt_get_rtscts_duration_le(tx_context, RTSDUR_BB,
+ PK_TYPE_11B,
+ priv->top_cck_basic_rate);
+ buf->duration_aa = vnt_get_rtscts_duration_le(tx_context, RTSDUR_AA,
+ tx_context->pkt_type,
+ current_rate);
+ buf->duration_ba = vnt_get_rtscts_duration_le(tx_context, RTSDUR_BA,
+ tx_context->pkt_type,
+ current_rate);
+
+ buf->rts_duration_ba_f0 =
+ vnt_get_rtscts_duration_le(tx_context, RTSDUR_BA_F0,
+ tx_context->pkt_type,
+ priv->tx_rate_fb0);
+ buf->rts_duration_aa_f0 =
+ vnt_get_rtscts_duration_le(tx_context, RTSDUR_AA_F0,
+ tx_context->pkt_type,
+ priv->tx_rate_fb0);
+ buf->rts_duration_ba_f1 =
+ vnt_get_rtscts_duration_le(tx_context, RTSDUR_BA_F1,
+ tx_context->pkt_type,
+ priv->tx_rate_fb1);
+ buf->rts_duration_aa_f1 =
+ vnt_get_rtscts_duration_le(tx_context, RTSDUR_AA_F1,
+ tx_context->pkt_type,
+ priv->tx_rate_fb1);
+
+ vnt_fill_ieee80211_rts(tx_context, &buf->data, buf->duration_aa);
+
+ return vnt_rxtx_datahead_g_fb(tx_context, &buf->data_head);
+}
+
+static u16 vnt_rxtx_rts_ab_head(struct vnt_usb_send_context *tx_context,
+ struct vnt_rts_ab *buf)
+{
+ struct vnt_private *priv = tx_context->priv;
+ u16 current_rate = tx_context->tx_rate;
+ u16 rts_frame_len = 20;
+
+ vnt_get_phy_field(priv, rts_frame_len, priv->top_ofdm_basic_rate,
+ tx_context->pkt_type, &buf->ab);
+
+ buf->duration = vnt_get_rtscts_duration_le(tx_context, RTSDUR_AA,
+ tx_context->pkt_type,
+ current_rate);
+
+ vnt_fill_ieee80211_rts(tx_context, &buf->data, buf->duration);
+
+ return vnt_rxtx_datahead_ab(tx_context, &buf->data_head);
+}
+
+static u16 vnt_rxtx_rts_a_fb_head(struct vnt_usb_send_context *tx_context,
+ struct vnt_rts_a_fb *buf)
+{
+ struct vnt_private *priv = tx_context->priv;
+ u16 current_rate = tx_context->tx_rate;
+ u16 rts_frame_len = 20;
+
+ vnt_get_phy_field(priv, rts_frame_len,
+ priv->top_ofdm_basic_rate, tx_context->pkt_type, &buf->a);
+
+ buf->duration = vnt_get_rtscts_duration_le(tx_context, RTSDUR_AA,
+ tx_context->pkt_type,
+ current_rate);
+
+ buf->rts_duration_f0 =
+ vnt_get_rtscts_duration_le(tx_context, RTSDUR_AA_F0,
+ tx_context->pkt_type,
+ priv->tx_rate_fb0);
+
+ buf->rts_duration_f1 =
+ vnt_get_rtscts_duration_le(tx_context, RTSDUR_AA_F1,
+ tx_context->pkt_type,
+ priv->tx_rate_fb1);
+
+ vnt_fill_ieee80211_rts(tx_context, &buf->data, buf->duration);
+
+ return vnt_rxtx_datahead_a_fb(tx_context, &buf->data_head);
+}
+
+static u16 vnt_fill_cts_fb_head(struct vnt_usb_send_context *tx_context,
+ union vnt_tx_data_head *head)
+{
+ struct vnt_private *priv = tx_context->priv;
+ struct vnt_cts_fb *buf = &head->cts_g_fb;
+ u32 cts_frame_len = 14;
+ u16 current_rate = tx_context->tx_rate;
+
+ /* Get SignalField,ServiceField,Length */
+ vnt_get_phy_field(priv, cts_frame_len, priv->top_cck_basic_rate,
+ PK_TYPE_11B, &buf->b);
+
+ buf->duration_ba =
+ vnt_get_rtscts_duration_le(tx_context, CTSDUR_BA,
+ tx_context->pkt_type,
+ current_rate);
+ /* Get CTSDuration_ba_f0 */
+ buf->cts_duration_ba_f0 =
+ vnt_get_rtscts_duration_le(tx_context, CTSDUR_BA_F0,
+ tx_context->pkt_type,
+ priv->tx_rate_fb0);
+ /* Get CTSDuration_ba_f1 */
+ buf->cts_duration_ba_f1 =
+ vnt_get_rtscts_duration_le(tx_context, CTSDUR_BA_F1,
+ tx_context->pkt_type,
+ priv->tx_rate_fb1);
+ /* Get CTS Frame body */
+ buf->data.duration = buf->duration_ba;
+ buf->data.frame_control =
+ cpu_to_le16(IEEE80211_FTYPE_CTL | IEEE80211_STYPE_CTS);
+
+ ether_addr_copy(buf->data.ra, priv->current_net_addr);
+
+ return vnt_rxtx_datahead_g_fb(tx_context, &buf->data_head);
+}
+
+static u16 vnt_fill_cts_head(struct vnt_usb_send_context *tx_context,
+ union vnt_tx_data_head *head)
+{
+ struct vnt_private *priv = tx_context->priv;
+ struct vnt_cts *buf = &head->cts_g;
+ u32 cts_frame_len = 14;
+ u16 current_rate = tx_context->tx_rate;
+
+ /* Get SignalField,ServiceField,Length */
+ vnt_get_phy_field(priv, cts_frame_len, priv->top_cck_basic_rate,
+ PK_TYPE_11B, &buf->b);
+ /* Get CTSDuration_ba */
+ buf->duration_ba =
+ vnt_get_rtscts_duration_le(tx_context, CTSDUR_BA,
+ tx_context->pkt_type,
+ current_rate);
+ /*Get CTS Frame body*/
+ buf->data.duration = buf->duration_ba;
+ buf->data.frame_control =
+ cpu_to_le16(IEEE80211_FTYPE_CTL | IEEE80211_STYPE_CTS);
+
+ ether_addr_copy(buf->data.ra, priv->current_net_addr);
+
+ return vnt_rxtx_datahead_g(tx_context, &buf->data_head);
+}
+
+static u16 vnt_rxtx_rts(struct vnt_usb_send_context *tx_context,
+ union vnt_tx_head *tx_head, bool need_mic)
+{
+ struct vnt_private *priv = tx_context->priv;
+ struct vnt_rrv_time_rts *buf = &tx_head->tx_rts.rts;
+ union vnt_tx_data_head *head = &tx_head->tx_rts.tx.head;
+ u32 frame_len = tx_context->frame_len;
+ u16 current_rate = tx_context->tx_rate;
+ u8 need_ack = tx_context->need_ack;
+
+ buf->rts_rrv_time_aa = vnt_get_rtscts_rsvtime_le(priv, 2,
+ tx_context->pkt_type, frame_len, current_rate);
+ buf->rts_rrv_time_ba = vnt_get_rtscts_rsvtime_le(priv, 1,
+ tx_context->pkt_type, frame_len, current_rate);
+ buf->rts_rrv_time_bb = vnt_get_rtscts_rsvtime_le(priv, 0,
+ tx_context->pkt_type, frame_len, current_rate);
+
+ buf->rrv_time_a = vnt_rxtx_rsvtime_le16(priv, tx_context->pkt_type,
+ frame_len, current_rate,
+ need_ack);
+ buf->rrv_time_b = vnt_rxtx_rsvtime_le16(priv, PK_TYPE_11B, frame_len,
+ priv->top_cck_basic_rate, need_ack);
+
+ if (need_mic)
+ head = &tx_head->tx_rts.tx.mic.head;
+
+ if (tx_context->fb_option)
+ return vnt_rxtx_rts_g_fb_head(tx_context, &head->rts_g_fb);
+
+ return vnt_rxtx_rts_g_head(tx_context, &head->rts_g);
+}
+
+static u16 vnt_rxtx_cts(struct vnt_usb_send_context *tx_context,
+ union vnt_tx_head *tx_head, bool need_mic)
+{
+ struct vnt_private *priv = tx_context->priv;
+ struct vnt_rrv_time_cts *buf = &tx_head->tx_cts.cts;
+ union vnt_tx_data_head *head = &tx_head->tx_cts.tx.head;
+ u32 frame_len = tx_context->frame_len;
+ u16 current_rate = tx_context->tx_rate;
+ u8 need_ack = tx_context->need_ack;
+
+ buf->rrv_time_a = vnt_rxtx_rsvtime_le16(priv, tx_context->pkt_type,
+ frame_len, current_rate, need_ack);
+ buf->rrv_time_b = vnt_rxtx_rsvtime_le16(priv, PK_TYPE_11B,
+ frame_len, priv->top_cck_basic_rate, need_ack);
+
+ buf->cts_rrv_time_ba = vnt_get_rtscts_rsvtime_le(priv, 3,
+ tx_context->pkt_type, frame_len, current_rate);
+
+ if (need_mic)
+ head = &tx_head->tx_cts.tx.mic.head;
+
+ /* Fill CTS */
+ if (tx_context->fb_option)
+ return vnt_fill_cts_fb_head(tx_context, head);
+
+ return vnt_fill_cts_head(tx_context, head);
+}
+
+static u16 vnt_rxtx_ab(struct vnt_usb_send_context *tx_context,
+ union vnt_tx_head *tx_head, bool need_rts, bool need_mic)
+{
+ struct vnt_private *priv = tx_context->priv;
+ struct vnt_rrv_time_ab *buf = &tx_head->tx_ab.ab;
+ union vnt_tx_data_head *head = &tx_head->tx_ab.tx.head;
+ u32 frame_len = tx_context->frame_len;
+ u16 current_rate = tx_context->tx_rate;
+ u8 need_ack = tx_context->need_ack;
+
+ buf->rrv_time = vnt_rxtx_rsvtime_le16(priv, tx_context->pkt_type,
+ frame_len, current_rate, need_ack);
+
+ if (need_mic)
+ head = &tx_head->tx_ab.tx.mic.head;
+
+ if (need_rts) {
+ if (tx_context->pkt_type == PK_TYPE_11B)
+ buf->rts_rrv_time = vnt_get_rtscts_rsvtime_le(priv, 0,
+ tx_context->pkt_type, frame_len, current_rate);
+ else /* PK_TYPE_11A */
+ buf->rts_rrv_time = vnt_get_rtscts_rsvtime_le(priv, 2,
+ tx_context->pkt_type, frame_len, current_rate);
+
+ if (tx_context->fb_option &&
+ tx_context->pkt_type == PK_TYPE_11A)
+ return vnt_rxtx_rts_a_fb_head(tx_context,
+ &head->rts_a_fb);
+
+ return vnt_rxtx_rts_ab_head(tx_context, &head->rts_ab);
+ }
+
+ if (tx_context->pkt_type == PK_TYPE_11A)
+ return vnt_rxtx_datahead_a_fb(tx_context,
+ &head->data_head_a_fb);
+
+ return vnt_rxtx_datahead_ab(tx_context, &head->data_head_ab);
+}
+
+static u16 vnt_generate_tx_parameter(struct vnt_usb_send_context *tx_context,
+ struct vnt_tx_buffer *tx_buffer,
+ struct vnt_mic_hdr **mic_hdr, u32 need_mic,
+ bool need_rts)
+{
+
+ if (tx_context->pkt_type == PK_TYPE_11GB ||
+ tx_context->pkt_type == PK_TYPE_11GA) {
+ if (need_rts) {
+ if (need_mic)
+ *mic_hdr = &tx_buffer->
+ tx_head.tx_rts.tx.mic.hdr;
+
+ return vnt_rxtx_rts(tx_context, &tx_buffer->tx_head,
+ need_mic);
+ }
+
+ if (need_mic)
+ *mic_hdr = &tx_buffer->tx_head.tx_cts.tx.mic.hdr;
+
+ return vnt_rxtx_cts(tx_context, &tx_buffer->tx_head, need_mic);
+ }
+
+ if (need_mic)
+ *mic_hdr = &tx_buffer->tx_head.tx_ab.tx.mic.hdr;
+
+ return vnt_rxtx_ab(tx_context, &tx_buffer->tx_head, need_rts, need_mic);
+}
+
+static void vnt_fill_txkey(struct vnt_usb_send_context *tx_context,
+ u8 *key_buffer, struct ieee80211_key_conf *tx_key, struct sk_buff *skb,
+ u16 payload_len, struct vnt_mic_hdr *mic_hdr)
+{
+ struct ieee80211_hdr *hdr = tx_context->hdr;
+ struct ieee80211_key_seq seq;
+ u8 *iv = ((u8 *)hdr + ieee80211_get_hdrlen_from_skb(skb));
+
+ /* strip header and icv len from payload */
+ payload_len -= ieee80211_get_hdrlen_from_skb(skb);
+ payload_len -= tx_key->icv_len;
+
+ switch (tx_key->cipher) {
+ case WLAN_CIPHER_SUITE_WEP40:
+ case WLAN_CIPHER_SUITE_WEP104:
+ memcpy(key_buffer, iv, 3);
+ memcpy(key_buffer + 3, tx_key->key, tx_key->keylen);
+
+ if (tx_key->keylen == WLAN_KEY_LEN_WEP40) {
+ memcpy(key_buffer + 8, iv, 3);
+ memcpy(key_buffer + 11,
+ tx_key->key, WLAN_KEY_LEN_WEP40);
+ }
+
+ break;
+ case WLAN_CIPHER_SUITE_TKIP:
+ ieee80211_get_tkip_p2k(tx_key, skb, key_buffer);
+
+ break;
+ case WLAN_CIPHER_SUITE_CCMP:
+
+ if (!mic_hdr)
+ return;
+
+ mic_hdr->id = 0x59;
+ mic_hdr->payload_len = cpu_to_be16(payload_len);
+ ether_addr_copy(mic_hdr->mic_addr2, hdr->addr2);
+
+ ieee80211_get_key_tx_seq(tx_key, &seq);
+
+ memcpy(mic_hdr->ccmp_pn, seq.ccmp.pn, IEEE80211_CCMP_PN_LEN);
+
+ if (ieee80211_has_a4(hdr->frame_control))
+ mic_hdr->hlen = cpu_to_be16(28);
+ else
+ mic_hdr->hlen = cpu_to_be16(22);
+
+ ether_addr_copy(mic_hdr->addr1, hdr->addr1);
+ ether_addr_copy(mic_hdr->addr2, hdr->addr2);
+ ether_addr_copy(mic_hdr->addr3, hdr->addr3);
+
+ mic_hdr->frame_control = cpu_to_le16(
+ le16_to_cpu(hdr->frame_control) & 0xc78f);
+ mic_hdr->seq_ctrl = cpu_to_le16(
+ le16_to_cpu(hdr->seq_ctrl) & 0xf);
+
+ if (ieee80211_has_a4(hdr->frame_control))
+ ether_addr_copy(mic_hdr->addr4, hdr->addr4);
+
+
+ memcpy(key_buffer, tx_key->key, WLAN_KEY_LEN_CCMP);
+
+ break;
+ default:
+ break;
+ }
+
+}
+
+int vnt_tx_packet(struct vnt_private *priv, struct sk_buff *skb)
+{
+ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+ struct ieee80211_tx_rate *tx_rate = &info->control.rates[0];
+ struct ieee80211_rate *rate;
+ struct ieee80211_key_conf *tx_key;
+ struct ieee80211_hdr *hdr;
+ struct vnt_mic_hdr *mic_hdr = NULL;
+ struct vnt_tx_buffer *tx_buffer;
+ struct vnt_tx_fifo_head *tx_buffer_head;
+ struct vnt_usb_send_context *tx_context;
+ unsigned long flags;
+ u16 tx_bytes, tx_header_size, tx_body_size, current_rate, duration_id;
+ u8 pkt_type, fb_option = AUTO_FB_NONE;
+ bool need_rts = false, is_pspoll = false;
+ bool need_mic = false;
+
+ hdr = (struct ieee80211_hdr *)(skb->data);
+
+ rate = ieee80211_get_tx_rate(priv->hw, info);
+
+ current_rate = rate->hw_value;
+ if (priv->current_rate != current_rate &&
+ !(priv->hw->conf.flags & IEEE80211_CONF_OFFCHANNEL)) {
+ priv->current_rate = current_rate;
+ vnt_schedule_command(priv, WLAN_CMD_SETPOWER);
+ }
+
+ if (current_rate > RATE_11M) {
+ if (info->band == IEEE80211_BAND_5GHZ) {
+ pkt_type = PK_TYPE_11A;
+ } else {
+ if (tx_rate->flags & IEEE80211_TX_RC_USE_CTS_PROTECT)
+ pkt_type = PK_TYPE_11GB;
+ else
+ pkt_type = PK_TYPE_11GA;
+ }
+ } else {
+ pkt_type = PK_TYPE_11B;
+ }
+
+ spin_lock_irqsave(&priv->lock, flags);
+
+ tx_context = vnt_get_free_context(priv);
+ if (!tx_context) {
+ dev_dbg(&priv->usb->dev, "%s No free context\n", __func__);
+ spin_unlock_irqrestore(&priv->lock, flags);
+ return -ENOMEM;
+ }
+
+ tx_context->skb = skb;
+ tx_context->pkt_type = pkt_type;
+ tx_context->need_ack = false;
+ tx_context->frame_len = skb->len + 4;
+ tx_context->tx_rate = current_rate;
+
+ spin_unlock_irqrestore(&priv->lock, flags);
+
+ tx_buffer = (struct vnt_tx_buffer *)tx_context->data;
+ tx_buffer_head = &tx_buffer->fifo_head;
+ tx_body_size = skb->len;
+
+ /*Set fifo controls */
+ if (pkt_type == PK_TYPE_11A)
+ tx_buffer_head->fifo_ctl = 0;
+ else if (pkt_type == PK_TYPE_11B)
+ tx_buffer_head->fifo_ctl = cpu_to_le16(FIFOCTL_11B);
+ else if (pkt_type == PK_TYPE_11GB)
+ tx_buffer_head->fifo_ctl = cpu_to_le16(FIFOCTL_11GB);
+ else if (pkt_type == PK_TYPE_11GA)
+ tx_buffer_head->fifo_ctl = cpu_to_le16(FIFOCTL_11GA);
+
+ if (!ieee80211_is_data(hdr->frame_control)) {
+ tx_buffer_head->fifo_ctl |= cpu_to_le16(FIFOCTL_GENINT |
+ FIFOCTL_ISDMA0);
+ tx_buffer_head->fifo_ctl |= cpu_to_le16(FIFOCTL_TMOEN);
+
+ tx_buffer_head->time_stamp =
+ cpu_to_le16(DEFAULT_MGN_LIFETIME_RES_64us);
+ } else {
+ tx_buffer_head->time_stamp =
+ cpu_to_le16(DEFAULT_MSDU_LIFETIME_RES_64us);
+ }
+
+ if (!(info->flags & IEEE80211_TX_CTL_NO_ACK)) {
+ tx_buffer_head->fifo_ctl |= cpu_to_le16(FIFOCTL_NEEDACK);
+ tx_context->need_ack = true;
+ }
+
+ if (ieee80211_has_retry(hdr->frame_control))
+ tx_buffer_head->fifo_ctl |= cpu_to_le16(FIFOCTL_LRETRY);
+
+ if (tx_rate->flags & IEEE80211_TX_RC_USE_SHORT_PREAMBLE)
+ priv->preamble_type = PREAMBLE_SHORT;
+ else
+ priv->preamble_type = PREAMBLE_LONG;
+
+ if (tx_rate->flags & IEEE80211_TX_RC_USE_RTS_CTS) {
+ need_rts = true;
+ tx_buffer_head->fifo_ctl |= cpu_to_le16(FIFOCTL_RTS);
+ }
+
+ if (ieee80211_has_a4(hdr->frame_control))
+ tx_buffer_head->fifo_ctl |= cpu_to_le16(FIFOCTL_LHEAD);
+
+ if (info->flags & IEEE80211_TX_CTL_NO_PS_BUFFER)
+ is_pspoll = true;
+
+ tx_buffer_head->frag_ctl =
+ cpu_to_le16(ieee80211_get_hdrlen_from_skb(skb) << 10);
+
+ if (info->control.hw_key) {
+ tx_key = info->control.hw_key;
+ switch (info->control.hw_key->cipher) {
+ case WLAN_CIPHER_SUITE_WEP40:
+ case WLAN_CIPHER_SUITE_WEP104:
+ tx_buffer_head->frag_ctl |= cpu_to_le16(FRAGCTL_LEGACY);
+ break;
+ case WLAN_CIPHER_SUITE_TKIP:
+ tx_buffer_head->frag_ctl |= cpu_to_le16(FRAGCTL_TKIP);
+ break;
+ case WLAN_CIPHER_SUITE_CCMP:
+ tx_buffer_head->frag_ctl |= cpu_to_le16(FRAGCTL_AES);
+ need_mic = true;
+ default:
+ break;
+ }
+ tx_context->frame_len += tx_key->icv_len;
+ }
+
+ tx_buffer_head->current_rate = cpu_to_le16(current_rate);
+
+ /* legacy rates TODO use ieee80211_tx_rate */
+ if (current_rate >= RATE_18M && ieee80211_is_data(hdr->frame_control)) {
+ if (priv->auto_fb_ctrl == AUTO_FB_0) {
+ tx_buffer_head->fifo_ctl |=
+ cpu_to_le16(FIFOCTL_AUTO_FB_0);
+
+ priv->tx_rate_fb0 =
+ vnt_fb_opt0[FB_RATE0][current_rate - RATE_18M];
+ priv->tx_rate_fb1 =
+ vnt_fb_opt0[FB_RATE1][current_rate - RATE_18M];
+
+ fb_option = AUTO_FB_0;
+ } else if (priv->auto_fb_ctrl == AUTO_FB_1) {
+ tx_buffer_head->fifo_ctl |=
+ cpu_to_le16(FIFOCTL_AUTO_FB_1);
+
+ priv->tx_rate_fb0 =
+ vnt_fb_opt1[FB_RATE0][current_rate - RATE_18M];
+ priv->tx_rate_fb1 =
+ vnt_fb_opt1[FB_RATE1][current_rate - RATE_18M];
+
+ fb_option = AUTO_FB_1;
+ }
+ }
+
+ tx_context->fb_option = fb_option;
+
+ duration_id = vnt_generate_tx_parameter(tx_context, tx_buffer, &mic_hdr,
+ need_mic, need_rts);
+
+ tx_header_size = tx_context->tx_hdr_size;
+ if (!tx_header_size) {
+ tx_context->in_use = false;
+ return -ENOMEM;
+ }
+
+ tx_buffer_head->frag_ctl |= cpu_to_le16(FRAGCTL_NONFRAG);
+
+ tx_bytes = tx_header_size + tx_body_size;
+
+ memcpy(tx_context->hdr, skb->data, tx_body_size);
+
+ hdr->duration_id = cpu_to_le16(duration_id);
+
+ if (info->control.hw_key) {
+ tx_key = info->control.hw_key;
+ if (tx_key->keylen > 0)
+ vnt_fill_txkey(tx_context, tx_buffer_head->tx_key,
+ tx_key, skb, tx_body_size, mic_hdr);
+ }
+
+ priv->seq_counter = (le16_to_cpu(hdr->seq_ctrl) &
+ IEEE80211_SCTL_SEQ) >> 4;
+
+ tx_buffer->tx_byte_count = cpu_to_le16(tx_bytes);
+ tx_buffer->pkt_no = tx_context->pkt_no;
+ tx_buffer->type = 0x00;
+
+ tx_bytes += 4;
+
+ tx_context->type = CONTEXT_DATA_PACKET;
+ tx_context->buf_len = tx_bytes;
+
+ spin_lock_irqsave(&priv->lock, flags);
+
+ if (vnt_tx_context(priv, tx_context) != STATUS_PENDING) {
+ spin_unlock_irqrestore(&priv->lock, flags);
+ return -EIO;
+ }
+
+ spin_unlock_irqrestore(&priv->lock, flags);
+
+ return 0;
+}
+
+static int vnt_beacon_xmit(struct vnt_private *priv,
+ struct sk_buff *skb)
+{
+ struct vnt_beacon_buffer *beacon_buffer;
+ struct vnt_tx_short_buf_head *short_head;
+ struct ieee80211_tx_info *info;
+ struct vnt_usb_send_context *context;
+ struct ieee80211_mgmt *mgmt_hdr;
+ unsigned long flags;
+ u32 frame_size = skb->len + 4;
+ u16 current_rate, count;
+
+ spin_lock_irqsave(&priv->lock, flags);
+
+ context = vnt_get_free_context(priv);
+ if (!context) {
+ dev_dbg(&priv->usb->dev, "%s No free context!\n", __func__);
+ spin_unlock_irqrestore(&priv->lock, flags);
+ return -ENOMEM;
+ }
+
+ context->skb = skb;
+
+ spin_unlock_irqrestore(&priv->lock, flags);
+
+ beacon_buffer = (struct vnt_beacon_buffer *)&context->data[0];
+ short_head = &beacon_buffer->short_head;
+
+ if (priv->bb_type == BB_TYPE_11A) {
+ current_rate = RATE_6M;
+
+ /* Get SignalField,ServiceField,Length */
+ vnt_get_phy_field(priv, frame_size, current_rate,
+ PK_TYPE_11A, &short_head->ab);
+
+ /* Get Duration and TimeStampOff */
+ short_head->duration = vnt_get_duration_le(priv,
+ PK_TYPE_11A, false);
+ short_head->time_stamp_off =
+ vnt_time_stamp_off(priv, current_rate);
+ } else {
+ current_rate = RATE_1M;
+ short_head->fifo_ctl |= cpu_to_le16(FIFOCTL_11B);
+
+ /* Get SignalField,ServiceField,Length */
+ vnt_get_phy_field(priv, frame_size, current_rate,
+ PK_TYPE_11B, &short_head->ab);
+
+ /* Get Duration and TimeStampOff */
+ short_head->duration = vnt_get_duration_le(priv,
+ PK_TYPE_11B, false);
+ short_head->time_stamp_off =
+ vnt_time_stamp_off(priv, current_rate);
+ }
+
+ /* Generate Beacon Header */
+ mgmt_hdr = &beacon_buffer->mgmt_hdr;
+ memcpy(mgmt_hdr, skb->data, skb->len);
+
+ /* time stamp always 0 */
+ mgmt_hdr->u.beacon.timestamp = 0;
+
+ info = IEEE80211_SKB_CB(skb);
+ if (info->flags & IEEE80211_TX_CTL_ASSIGN_SEQ) {
+ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)mgmt_hdr;
+
+ hdr->duration_id = 0;
+ hdr->seq_ctrl = cpu_to_le16(priv->seq_counter << 4);
+ }
+
+ priv->seq_counter++;
+ if (priv->seq_counter > 0x0fff)
+ priv->seq_counter = 0;
+
+ count = sizeof(struct vnt_tx_short_buf_head) + skb->len;
+
+ beacon_buffer->tx_byte_count = cpu_to_le16(count);
+ beacon_buffer->pkt_no = context->pkt_no;
+ beacon_buffer->type = 0x01;
+
+ context->type = CONTEXT_BEACON_PACKET;
+ context->buf_len = count + 4; /* USB header */
+
+ spin_lock_irqsave(&priv->lock, flags);
+
+ if (vnt_tx_context(priv, context) != STATUS_PENDING)
+ ieee80211_free_txskb(priv->hw, context->skb);
+
+ spin_unlock_irqrestore(&priv->lock, flags);
+
+ return 0;
+}
+
+int vnt_beacon_make(struct vnt_private *priv, struct ieee80211_vif *vif)
+{
+ struct sk_buff *beacon;
+
+ beacon = ieee80211_beacon_get(priv->hw, vif);
+ if (!beacon)
+ return -ENOMEM;
+
+ if (vnt_beacon_xmit(priv, beacon)) {
+ ieee80211_free_txskb(priv->hw, beacon);
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+int vnt_beacon_enable(struct vnt_private *priv, struct ieee80211_vif *vif,
+ struct ieee80211_bss_conf *conf)
+{
+ vnt_mac_reg_bits_off(priv, MAC_REG_TCR, TCR_AUTOBCNTX);
+
+ vnt_mac_reg_bits_off(priv, MAC_REG_TFTCTL, TFTCTL_TSFCNTREN);
+
+ vnt_mac_set_beacon_interval(priv, conf->beacon_int);
+
+ vnt_clear_current_tsf(priv);
+
+ vnt_mac_reg_bits_on(priv, MAC_REG_TFTCTL, TFTCTL_TSFCNTREN);
+
+ vnt_reset_next_tbtt(priv, conf->beacon_int);
+
+ return vnt_beacon_make(priv, vif);
+}
diff --git a/drivers/staging/vt6656/rxtx.h b/drivers/staging/vt6656/rxtx.h
new file mode 100644
index 000000000..90b34ab2f
--- /dev/null
+++ b/drivers/staging/vt6656/rxtx.h
@@ -0,0 +1,260 @@
+/*
+ * Copyright (c) 1996, 2003 VIA Networking Technologies, Inc.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * File: rxtx.h
+ *
+ * Purpose:
+ *
+ * Author: Jerry Chen
+ *
+ * Date: Jun. 27, 2002
+ *
+ */
+
+#ifndef __RXTX_H__
+#define __RXTX_H__
+
+#include "device.h"
+#include "wcmd.h"
+#include "baseband.h"
+
+#define DEFAULT_MGN_LIFETIME_RES_64us 125 /* 64us */
+#define DEFAULT_MSDU_LIFETIME_RES_64us 8000
+
+/* MIC HDR data header */
+struct vnt_mic_hdr {
+ u8 id;
+ u8 tx_priority;
+ u8 mic_addr2[6];
+ u8 ccmp_pn[IEEE80211_CCMP_PN_LEN];
+ __be16 payload_len;
+ __be16 hlen;
+ __le16 frame_control;
+ u8 addr1[6];
+ u8 addr2[6];
+ u8 addr3[6];
+ __le16 seq_ctrl;
+ u8 addr4[6];
+ u16 packing; /* packing to 48 bytes */
+} __packed;
+
+/* RsvTime buffer header */
+struct vnt_rrv_time_rts {
+ __le16 rts_rrv_time_ba;
+ __le16 rts_rrv_time_aa;
+ __le16 rts_rrv_time_bb;
+ u16 wReserved;
+ __le16 rrv_time_b;
+ __le16 rrv_time_a;
+} __packed;
+
+struct vnt_rrv_time_cts {
+ __le16 cts_rrv_time_ba;
+ u16 wReserved;
+ __le16 rrv_time_b;
+ __le16 rrv_time_a;
+} __packed;
+
+struct vnt_rrv_time_ab {
+ __le16 rts_rrv_time;
+ __le16 rrv_time;
+} __packed;
+
+/* TX data header */
+struct vnt_tx_datahead_g {
+ struct vnt_phy_field b;
+ struct vnt_phy_field a;
+ __le16 duration_b;
+ __le16 duration_a;
+ __le16 time_stamp_off_b;
+ __le16 time_stamp_off_a;
+ struct ieee80211_hdr hdr;
+} __packed;
+
+struct vnt_tx_datahead_g_fb {
+ struct vnt_phy_field b;
+ struct vnt_phy_field a;
+ __le16 duration_b;
+ __le16 duration_a;
+ __le16 duration_a_f0;
+ __le16 duration_a_f1;
+ __le16 time_stamp_off_b;
+ __le16 time_stamp_off_a;
+ struct ieee80211_hdr hdr;
+} __packed;
+
+struct vnt_tx_datahead_ab {
+ struct vnt_phy_field ab;
+ __le16 duration;
+ __le16 time_stamp_off;
+ struct ieee80211_hdr hdr;
+} __packed;
+
+struct vnt_tx_datahead_a_fb {
+ struct vnt_phy_field a;
+ __le16 duration;
+ __le16 time_stamp_off;
+ __le16 duration_f0;
+ __le16 duration_f1;
+ struct ieee80211_hdr hdr;
+} __packed;
+
+/* RTS buffer header */
+struct vnt_rts_g {
+ struct vnt_phy_field b;
+ struct vnt_phy_field a;
+ __le16 duration_ba;
+ __le16 duration_aa;
+ __le16 duration_bb;
+ u16 wReserved;
+ struct ieee80211_rts data;
+ struct vnt_tx_datahead_g data_head;
+} __packed;
+
+struct vnt_rts_g_fb {
+ struct vnt_phy_field b;
+ struct vnt_phy_field a;
+ __le16 duration_ba;
+ __le16 duration_aa;
+ __le16 duration_bb;
+ u16 wReserved;
+ __le16 rts_duration_ba_f0;
+ __le16 rts_duration_aa_f0;
+ __le16 rts_duration_ba_f1;
+ __le16 rts_duration_aa_f1;
+ struct ieee80211_rts data;
+ struct vnt_tx_datahead_g_fb data_head;
+} __packed;
+
+struct vnt_rts_ab {
+ struct vnt_phy_field ab;
+ __le16 duration;
+ u16 wReserved;
+ struct ieee80211_rts data;
+ struct vnt_tx_datahead_ab data_head;
+} __packed;
+
+struct vnt_rts_a_fb {
+ struct vnt_phy_field a;
+ __le16 duration;
+ u16 wReserved;
+ __le16 rts_duration_f0;
+ __le16 rts_duration_f1;
+ struct ieee80211_rts data;
+ struct vnt_tx_datahead_a_fb data_head;
+} __packed;
+
+/* CTS buffer header */
+struct vnt_cts {
+ struct vnt_phy_field b;
+ __le16 duration_ba;
+ u16 wReserved;
+ struct ieee80211_cts data;
+ u16 reserved2;
+ struct vnt_tx_datahead_g data_head;
+} __packed;
+
+struct vnt_cts_fb {
+ struct vnt_phy_field b;
+ __le16 duration_ba;
+ u16 wReserved;
+ __le16 cts_duration_ba_f0;
+ __le16 cts_duration_ba_f1;
+ struct ieee80211_cts data;
+ u16 reserved2;
+ struct vnt_tx_datahead_g_fb data_head;
+} __packed;
+
+union vnt_tx_data_head {
+ /* rts g */
+ struct vnt_rts_g rts_g;
+ struct vnt_rts_g_fb rts_g_fb;
+ /* rts a/b */
+ struct vnt_rts_ab rts_ab;
+ struct vnt_rts_a_fb rts_a_fb;
+ /* cts g */
+ struct vnt_cts cts_g;
+ struct vnt_cts_fb cts_g_fb;
+ /* no rts/cts */
+ struct vnt_tx_datahead_a_fb data_head_a_fb;
+ struct vnt_tx_datahead_ab data_head_ab;
+};
+
+struct vnt_tx_mic_hdr {
+ struct vnt_mic_hdr hdr;
+ union vnt_tx_data_head head;
+} __packed;
+
+union vnt_tx {
+ struct vnt_tx_mic_hdr mic;
+ union vnt_tx_data_head head;
+};
+
+union vnt_tx_head {
+ struct {
+ struct vnt_rrv_time_rts rts;
+ union vnt_tx tx;
+ } __packed tx_rts;
+ struct {
+ struct vnt_rrv_time_cts cts;
+ union vnt_tx tx;
+ } __packed tx_cts;
+ struct {
+ struct vnt_rrv_time_ab ab;
+ union vnt_tx tx;
+ } __packed tx_ab;
+};
+
+struct vnt_tx_fifo_head {
+ u8 tx_key[WLAN_KEY_LEN_CCMP];
+ __le16 fifo_ctl;
+ __le16 time_stamp;
+ __le16 frag_ctl;
+ __le16 current_rate;
+} __packed;
+
+struct vnt_tx_buffer {
+ u8 type;
+ u8 pkt_no;
+ __le16 tx_byte_count;
+ struct vnt_tx_fifo_head fifo_head;
+ union vnt_tx_head tx_head;
+} __packed;
+
+struct vnt_tx_short_buf_head {
+ __le16 fifo_ctl;
+ u16 time_stamp;
+ struct vnt_phy_field ab;
+ __le16 duration;
+ __le16 time_stamp_off;
+} __packed;
+
+struct vnt_beacon_buffer {
+ u8 type;
+ u8 pkt_no;
+ __le16 tx_byte_count;
+ struct vnt_tx_short_buf_head short_head;
+ struct ieee80211_mgmt mgmt_hdr;
+} __packed;
+
+int vnt_tx_packet(struct vnt_private *, struct sk_buff *);
+int vnt_beacon_make(struct vnt_private *, struct ieee80211_vif *);
+int vnt_beacon_enable(struct vnt_private *, struct ieee80211_vif *,
+ struct ieee80211_bss_conf *);
+
+#endif /* __RXTX_H__ */
diff --git a/drivers/staging/vt6656/usbpipe.c b/drivers/staging/vt6656/usbpipe.c
new file mode 100644
index 000000000..88bf518f2
--- /dev/null
+++ b/drivers/staging/vt6656/usbpipe.c
@@ -0,0 +1,308 @@
+/*
+ * Copyright (c) 1996, 2003 VIA Networking Technologies, Inc.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ *
+ * File: usbpipe.c
+ *
+ * Purpose: Handle USB control endpoint
+ *
+ * Author: Warren Hsu
+ *
+ * Date: Mar. 29, 2005
+ *
+ * Functions:
+ * vnt_control_out - Write variable length bytes to MEM/BB/MAC/EEPROM
+ * vnt_control_in - Read variable length bytes from MEM/BB/MAC/EEPROM
+ * vnt_control_out_u8 - Write one byte to MEM/BB/MAC/EEPROM
+ * vnt_control_in_u8 - Read one byte from MEM/BB/MAC/EEPROM
+ *
+ * Revision History:
+ * 04-05-2004 Jerry Chen: Initial release
+ * 11-24-2004 Warren Hsu: Add ControlvWriteByte,ControlvReadByte,ControlvMaskByte
+ *
+ */
+
+#include "int.h"
+#include "rxtx.h"
+#include "dpc.h"
+#include "desc.h"
+#include "device.h"
+#include "usbpipe.h"
+
+#define USB_CTL_WAIT 500 /* ms */
+
+int vnt_control_out(struct vnt_private *priv, u8 request, u16 value,
+ u16 index, u16 length, u8 *buffer)
+{
+ int status = 0;
+
+ if (test_bit(DEVICE_FLAGS_DISCONNECTED, &priv->flags))
+ return STATUS_FAILURE;
+
+ mutex_lock(&priv->usb_lock);
+
+ status = usb_control_msg(priv->usb,
+ usb_sndctrlpipe(priv->usb, 0), request, 0x40, value,
+ index, buffer, length, USB_CTL_WAIT);
+
+ mutex_unlock(&priv->usb_lock);
+
+ if (status < (int)length)
+ return STATUS_FAILURE;
+
+ return STATUS_SUCCESS;
+}
+
+void vnt_control_out_u8(struct vnt_private *priv, u8 reg, u8 reg_off, u8 data)
+{
+ vnt_control_out(priv, MESSAGE_TYPE_WRITE,
+ reg_off, reg, sizeof(u8), &data);
+}
+
+int vnt_control_in(struct vnt_private *priv, u8 request, u16 value,
+ u16 index, u16 length, u8 *buffer)
+{
+ int status;
+
+ if (test_bit(DEVICE_FLAGS_DISCONNECTED, &priv->flags))
+ return STATUS_FAILURE;
+
+ mutex_lock(&priv->usb_lock);
+
+ status = usb_control_msg(priv->usb,
+ usb_rcvctrlpipe(priv->usb, 0), request, 0xc0, value,
+ index, buffer, length, USB_CTL_WAIT);
+
+ mutex_unlock(&priv->usb_lock);
+
+ if (status < (int)length)
+ return STATUS_FAILURE;
+
+ return STATUS_SUCCESS;
+}
+
+void vnt_control_in_u8(struct vnt_private *priv, u8 reg, u8 reg_off, u8 *data)
+{
+ vnt_control_in(priv, MESSAGE_TYPE_READ,
+ reg_off, reg, sizeof(u8), data);
+}
+
+static void vnt_start_interrupt_urb_complete(struct urb *urb)
+{
+ struct vnt_private *priv = urb->context;
+ int status;
+
+ switch (urb->status) {
+ case 0:
+ case -ETIMEDOUT:
+ break;
+ case -ECONNRESET:
+ case -ENOENT:
+ case -ESHUTDOWN:
+ priv->int_buf.in_use = false;
+ return;
+ default:
+ break;
+ }
+
+ status = urb->status;
+
+ if (status != STATUS_SUCCESS) {
+ priv->int_buf.in_use = false;
+
+ dev_dbg(&priv->usb->dev, "%s status = %d\n", __func__, status);
+ } else {
+ vnt_int_process_data(priv);
+ }
+
+ status = usb_submit_urb(priv->interrupt_urb, GFP_ATOMIC);
+ if (status)
+ dev_dbg(&priv->usb->dev, "Submit int URB failed %d\n", status);
+ else
+ priv->int_buf.in_use = true;
+}
+
+int vnt_start_interrupt_urb(struct vnt_private *priv)
+{
+ int status = STATUS_FAILURE;
+
+ if (priv->int_buf.in_use == true)
+ return STATUS_FAILURE;
+
+ priv->int_buf.in_use = true;
+
+ usb_fill_int_urb(priv->interrupt_urb,
+ priv->usb,
+ usb_rcvintpipe(priv->usb, 1),
+ priv->int_buf.data_buf,
+ MAX_INTERRUPT_SIZE,
+ vnt_start_interrupt_urb_complete,
+ priv,
+ priv->int_interval);
+
+ status = usb_submit_urb(priv->interrupt_urb, GFP_ATOMIC);
+ if (status) {
+ dev_dbg(&priv->usb->dev, "Submit int URB failed %d\n", status);
+ priv->int_buf.in_use = false;
+ }
+
+ return status;
+}
+
+static void vnt_submit_rx_urb_complete(struct urb *urb)
+{
+ struct vnt_rcb *rcb = urb->context;
+ struct vnt_private *priv = rcb->priv;
+ unsigned long flags;
+
+ switch (urb->status) {
+ case 0:
+ break;
+ case -ECONNRESET:
+ case -ENOENT:
+ case -ESHUTDOWN:
+ return;
+ case -ETIMEDOUT:
+ default:
+ dev_dbg(&priv->usb->dev, "BULK In failed %d\n", urb->status);
+ break;
+ }
+
+ if (urb->actual_length) {
+ spin_lock_irqsave(&priv->lock, flags);
+
+ if (vnt_rx_data(priv, rcb, urb->actual_length)) {
+ rcb->skb = dev_alloc_skb(priv->rx_buf_sz);
+ if (!rcb->skb) {
+ dev_dbg(&priv->usb->dev,
+ "Failed to re-alloc rx skb\n");
+
+ rcb->in_use = false;
+ spin_unlock_irqrestore(&priv->lock, flags);
+ return;
+ }
+ } else {
+ skb_push(rcb->skb, skb_headroom(rcb->skb));
+ skb_trim(rcb->skb, 0);
+ }
+
+ urb->transfer_buffer = skb_put(rcb->skb,
+ skb_tailroom(rcb->skb));
+
+ spin_unlock_irqrestore(&priv->lock, flags);
+ }
+
+ if (usb_submit_urb(urb, GFP_ATOMIC)) {
+ dev_dbg(&priv->usb->dev, "Failed to re submit rx skb\n");
+
+ rcb->in_use = false;
+ }
+}
+
+int vnt_submit_rx_urb(struct vnt_private *priv, struct vnt_rcb *rcb)
+{
+ int status = 0;
+ struct urb *urb;
+
+ urb = rcb->urb;
+ if (rcb->skb == NULL) {
+ dev_dbg(&priv->usb->dev, "rcb->skb is null\n");
+ return status;
+ }
+
+ usb_fill_bulk_urb(urb,
+ priv->usb,
+ usb_rcvbulkpipe(priv->usb, 2),
+ skb_put(rcb->skb, skb_tailroom(rcb->skb)),
+ MAX_TOTAL_SIZE_WITH_ALL_HEADERS,
+ vnt_submit_rx_urb_complete,
+ rcb);
+
+ status = usb_submit_urb(urb, GFP_ATOMIC);
+ if (status != 0) {
+ dev_dbg(&priv->usb->dev, "Submit Rx URB failed %d\n", status);
+ return STATUS_FAILURE;
+ }
+
+ rcb->in_use = true;
+
+ return status;
+}
+
+static void vnt_tx_context_complete(struct urb *urb)
+{
+ struct vnt_usb_send_context *context = urb->context;
+ struct vnt_private *priv = context->priv;
+
+ switch (urb->status) {
+ case 0:
+ dev_dbg(&priv->usb->dev, "Write %d bytes\n", context->buf_len);
+ break;
+ case -ECONNRESET:
+ case -ENOENT:
+ case -ESHUTDOWN:
+ context->in_use = false;
+ return;
+ case -ETIMEDOUT:
+ default:
+ dev_dbg(&priv->usb->dev, "BULK Out failed %d\n", urb->status);
+ break;
+ }
+
+ if (context->type == CONTEXT_DATA_PACKET)
+ ieee80211_wake_queues(priv->hw);
+
+ if (urb->status || context->type == CONTEXT_BEACON_PACKET) {
+ if (context->skb)
+ ieee80211_free_txskb(priv->hw, context->skb);
+
+ context->in_use = false;
+ }
+}
+
+int vnt_tx_context(struct vnt_private *priv,
+ struct vnt_usb_send_context *context)
+{
+ int status;
+ struct urb *urb;
+
+ if (test_bit(DEVICE_FLAGS_DISCONNECTED, &priv->flags)) {
+ context->in_use = false;
+ return STATUS_RESOURCES;
+ }
+
+ urb = context->urb;
+
+ usb_fill_bulk_urb(urb,
+ priv->usb,
+ usb_sndbulkpipe(priv->usb, 3),
+ context->data,
+ context->buf_len,
+ vnt_tx_context_complete,
+ context);
+
+ status = usb_submit_urb(urb, GFP_ATOMIC);
+ if (status != 0) {
+ dev_dbg(&priv->usb->dev, "Submit Tx URB failed %d\n", status);
+
+ context->in_use = false;
+ return STATUS_FAILURE;
+ }
+
+ return STATUS_PENDING;
+}
diff --git a/drivers/staging/vt6656/usbpipe.h b/drivers/staging/vt6656/usbpipe.h
new file mode 100644
index 000000000..e74aa0809
--- /dev/null
+++ b/drivers/staging/vt6656/usbpipe.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 1996, 2003 VIA Networking Technologies, Inc.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ *
+ * File: usbpipe.h
+ *
+ * Purpose:
+ *
+ * Author: Warren Hsu
+ *
+ * Date: Mar. 30, 2005
+ *
+ */
+
+#ifndef __USBPIPE_H__
+#define __USBPIPE_H__
+
+#include "device.h"
+
+int vnt_control_out(struct vnt_private *, u8, u16, u16, u16, u8 *);
+int vnt_control_in(struct vnt_private *, u8, u16, u16, u16, u8 *);
+
+void vnt_control_out_u8(struct vnt_private *, u8, u8, u8);
+void vnt_control_in_u8(struct vnt_private *, u8, u8, u8 *);
+
+int vnt_start_interrupt_urb(struct vnt_private *);
+int vnt_submit_rx_urb(struct vnt_private *, struct vnt_rcb *);
+int vnt_tx_context(struct vnt_private *, struct vnt_usb_send_context *);
+
+#endif /* __USBPIPE_H__ */
diff --git a/drivers/staging/vt6656/wcmd.c b/drivers/staging/vt6656/wcmd.c
new file mode 100644
index 000000000..3cbf4791b
--- /dev/null
+++ b/drivers/staging/vt6656/wcmd.c
@@ -0,0 +1,194 @@
+/*
+ * Copyright (c) 1996, 2003 VIA Networking Technologies, Inc.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * File: wcmd.c
+ *
+ * Purpose: Handles the management command interface functions
+ *
+ * Author: Lyndon Chen
+ *
+ * Date: May 8, 2003
+ *
+ * Functions:
+ * vnt_cmd_complete - Command Complete function
+ * vnt_schedule_command - Push Command and wait Command Scheduler to do
+ * vnt_cmd_timer_wait- Call back timer
+ *
+ * Revision History:
+ *
+ */
+
+#include "device.h"
+#include "mac.h"
+#include "wcmd.h"
+#include "power.h"
+#include "usbpipe.h"
+#include "rxtx.h"
+#include "rf.h"
+
+static void vnt_cmd_timer_wait(struct vnt_private *priv, unsigned long msecs)
+{
+ schedule_delayed_work(&priv->run_command_work, msecs_to_jiffies(msecs));
+}
+
+static int vnt_cmd_complete(struct vnt_private *priv)
+{
+
+ priv->command_state = WLAN_CMD_IDLE;
+ if (priv->free_cmd_queue == CMD_Q_SIZE) {
+ /* Command Queue Empty */
+ priv->cmd_running = false;
+ return true;
+ }
+
+ priv->command = priv->cmd_queue[priv->cmd_dequeue_idx];
+
+ ADD_ONE_WITH_WRAP_AROUND(priv->cmd_dequeue_idx, CMD_Q_SIZE);
+ priv->free_cmd_queue++;
+ priv->cmd_running = true;
+
+ switch (priv->command) {
+ case WLAN_CMD_INIT_MAC80211:
+ priv->command_state = WLAN_CMD_INIT_MAC80211_START;
+ break;
+
+ case WLAN_CMD_TBTT_WAKEUP:
+ priv->command_state = WLAN_CMD_TBTT_WAKEUP_START;
+ break;
+
+ case WLAN_CMD_BECON_SEND:
+ priv->command_state = WLAN_CMD_BECON_SEND_START;
+ break;
+
+ case WLAN_CMD_SETPOWER:
+ priv->command_state = WLAN_CMD_SETPOWER_START;
+ break;
+
+ case WLAN_CMD_CHANGE_ANTENNA:
+ priv->command_state = WLAN_CMD_CHANGE_ANTENNA_START;
+ break;
+
+ default:
+ break;
+ }
+
+ vnt_cmd_timer_wait(priv, 0);
+
+ return true;
+}
+
+void vnt_run_command(struct work_struct *work)
+{
+ struct vnt_private *priv =
+ container_of(work, struct vnt_private, run_command_work.work);
+
+ if (test_bit(DEVICE_FLAGS_DISCONNECTED, &priv->flags))
+ return;
+
+ if (priv->cmd_running != true)
+ return;
+
+ switch (priv->command_state) {
+ case WLAN_CMD_INIT_MAC80211_START:
+ if (priv->mac_hw)
+ break;
+
+ dev_info(&priv->usb->dev, "Starting mac80211\n");
+
+ if (vnt_init(priv)) {
+ /* If fail all ends TODO retry */
+ dev_err(&priv->usb->dev, "failed to start\n");
+ ieee80211_free_hw(priv->hw);
+ return;
+ }
+
+ break;
+
+ case WLAN_CMD_TBTT_WAKEUP_START:
+ vnt_next_tbtt_wakeup(priv);
+ break;
+
+ case WLAN_CMD_BECON_SEND_START:
+ if (!priv->vif)
+ break;
+
+ vnt_beacon_make(priv, priv->vif);
+
+ vnt_mac_reg_bits_on(priv, MAC_REG_TCR, TCR_AUTOBCNTX);
+
+ break;
+
+ case WLAN_CMD_SETPOWER_START:
+
+ vnt_rf_setpower(priv, priv->current_rate,
+ priv->hw->conf.chandef.chan->hw_value);
+
+ break;
+
+ case WLAN_CMD_CHANGE_ANTENNA_START:
+ dev_dbg(&priv->usb->dev, "Change from Antenna%d to",
+ priv->rx_antenna_sel);
+
+ if (priv->rx_antenna_sel == 0) {
+ priv->rx_antenna_sel = 1;
+ if (priv->tx_rx_ant_inv == true)
+ vnt_set_antenna_mode(priv, ANT_RXA);
+ else
+ vnt_set_antenna_mode(priv, ANT_RXB);
+ } else {
+ priv->rx_antenna_sel = 0;
+ if (priv->tx_rx_ant_inv == true)
+ vnt_set_antenna_mode(priv, ANT_RXB);
+ else
+ vnt_set_antenna_mode(priv, ANT_RXA);
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ vnt_cmd_complete(priv);
+}
+
+int vnt_schedule_command(struct vnt_private *priv, enum vnt_cmd command)
+{
+
+ if (priv->free_cmd_queue == 0)
+ return false;
+
+ priv->cmd_queue[priv->cmd_enqueue_idx] = command;
+
+ ADD_ONE_WITH_WRAP_AROUND(priv->cmd_enqueue_idx, CMD_Q_SIZE);
+ priv->free_cmd_queue--;
+
+ if (priv->cmd_running == false)
+ vnt_cmd_complete(priv);
+
+ return true;
+
+}
+
+void vnt_reset_command_timer(struct vnt_private *priv)
+{
+ priv->free_cmd_queue = CMD_Q_SIZE;
+ priv->cmd_dequeue_idx = 0;
+ priv->cmd_enqueue_idx = 0;
+ priv->command_state = WLAN_CMD_IDLE;
+ priv->cmd_running = false;
+}
diff --git a/drivers/staging/vt6656/wcmd.h b/drivers/staging/vt6656/wcmd.h
new file mode 100644
index 000000000..2b0ee285e
--- /dev/null
+++ b/drivers/staging/vt6656/wcmd.h
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 1996, 2003 VIA Networking Technologies, Inc.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * File: wcmd.h
+ *
+ * Purpose: Handles the management command interface functions
+ *
+ * Author: Lyndon Chen
+ *
+ * Date: May 8, 2002
+ *
+ */
+
+#ifndef __WCMD_H__
+#define __WCMD_H__
+
+#include "device.h"
+
+/* Command code */
+enum vnt_cmd {
+ WLAN_CMD_INIT_MAC80211,
+ WLAN_CMD_SETPOWER,
+ WLAN_CMD_TBTT_WAKEUP,
+ WLAN_CMD_BECON_SEND,
+ WLAN_CMD_CHANGE_ANTENNA
+};
+
+#define CMD_Q_SIZE 32
+
+/* Command state */
+enum vnt_cmd_state {
+ WLAN_CMD_INIT_MAC80211_START,
+ WLAN_CMD_SETPOWER_START,
+ WLAN_CMD_TBTT_WAKEUP_START,
+ WLAN_CMD_BECON_SEND_START,
+ WLAN_CMD_CHANGE_ANTENNA_START,
+ WLAN_CMD_IDLE
+};
+
+struct vnt_private;
+
+void vnt_reset_command_timer(struct vnt_private *);
+
+int vnt_schedule_command(struct vnt_private *, enum vnt_cmd);
+
+void vnt_run_command(struct work_struct *work);
+
+#endif /* __WCMD_H__ */
diff --git a/drivers/staging/wlan-ng/Kconfig b/drivers/staging/wlan-ng/Kconfig
new file mode 100644
index 000000000..426d4efba
--- /dev/null
+++ b/drivers/staging/wlan-ng/Kconfig
@@ -0,0 +1,12 @@
+config PRISM2_USB
+ tristate "Prism2.5/3 USB driver"
+ depends on WLAN && USB && CFG80211
+ select WIRELESS_EXT
+ select WEXT_PRIV
+ default n
+ ---help---
+ This is the wlan-ng prism 2.5/3 USB driver for a wide range of
+ old USB wireless devices.
+
+ To compile this driver as a module, choose M here: the module
+ will be called prism2_usb.
diff --git a/drivers/staging/wlan-ng/Makefile b/drivers/staging/wlan-ng/Makefile
new file mode 100644
index 000000000..32b69f238
--- /dev/null
+++ b/drivers/staging/wlan-ng/Makefile
@@ -0,0 +1,7 @@
+obj-$(CONFIG_PRISM2_USB) += prism2_usb.o
+
+prism2_usb-y := prism2usb.o \
+ p80211conv.o \
+ p80211req.o \
+ p80211wep.o \
+ p80211netdev.o
diff --git a/drivers/staging/wlan-ng/README b/drivers/staging/wlan-ng/README
new file mode 100644
index 000000000..d0621f827
--- /dev/null
+++ b/drivers/staging/wlan-ng/README
@@ -0,0 +1,8 @@
+TODO:
+ - checkpatch.pl cleanups
+ - sparse warnings
+ - move to use the in-kernel wireless stack
+
+Please send any patches or complaints about this driver to Greg
+Kroah-Hartman <greg@kroah.com> and don't bother the upstream wireless
+kernel developers about it, they want nothing to do with it.
diff --git a/drivers/staging/wlan-ng/cfg80211.c b/drivers/staging/wlan-ng/cfg80211.c
new file mode 100644
index 000000000..7c87aecf4
--- /dev/null
+++ b/drivers/staging/wlan-ng/cfg80211.c
@@ -0,0 +1,796 @@
+/* cfg80211 Interface for prism2_usb module */
+#include "hfa384x.h"
+#include "prism2mgmt.h"
+
+
+/* Prism2 channel/frequency/bitrate declarations */
+static const struct ieee80211_channel prism2_channels[] = {
+ { .center_freq = 2412 },
+ { .center_freq = 2417 },
+ { .center_freq = 2422 },
+ { .center_freq = 2427 },
+ { .center_freq = 2432 },
+ { .center_freq = 2437 },
+ { .center_freq = 2442 },
+ { .center_freq = 2447 },
+ { .center_freq = 2452 },
+ { .center_freq = 2457 },
+ { .center_freq = 2462 },
+ { .center_freq = 2467 },
+ { .center_freq = 2472 },
+ { .center_freq = 2484 },
+};
+
+static const struct ieee80211_rate prism2_rates[] = {
+ { .bitrate = 10 },
+ { .bitrate = 20 },
+ { .bitrate = 55 },
+ { .bitrate = 110 }
+};
+
+#define PRISM2_NUM_CIPHER_SUITES 2
+static const u32 prism2_cipher_suites[PRISM2_NUM_CIPHER_SUITES] = {
+ WLAN_CIPHER_SUITE_WEP40,
+ WLAN_CIPHER_SUITE_WEP104
+};
+
+
+/* prism2 device private data */
+struct prism2_wiphy_private {
+ wlandevice_t *wlandev;
+
+ struct ieee80211_supported_band band;
+ struct ieee80211_channel channels[ARRAY_SIZE(prism2_channels)];
+ struct ieee80211_rate rates[ARRAY_SIZE(prism2_rates)];
+
+ struct cfg80211_scan_request *scan_request;
+};
+
+static const void * const prism2_wiphy_privid = &prism2_wiphy_privid;
+
+
+/* Helper Functions */
+static int prism2_result2err(int prism2_result)
+{
+ int err = 0;
+
+ switch (prism2_result) {
+ case P80211ENUM_resultcode_invalid_parameters:
+ err = -EINVAL;
+ break;
+ case P80211ENUM_resultcode_implementation_failure:
+ err = -EIO;
+ break;
+ case P80211ENUM_resultcode_not_supported:
+ err = -EOPNOTSUPP;
+ break;
+ default:
+ err = 0;
+ break;
+ }
+
+ return err;
+}
+
+static int prism2_domibset_uint32(wlandevice_t *wlandev, u32 did, u32 data)
+{
+ struct p80211msg_dot11req_mibset msg;
+ p80211item_uint32_t *mibitem =
+ (p80211item_uint32_t *) &msg.mibattribute.data;
+
+ msg.msgcode = DIDmsg_dot11req_mibset;
+ mibitem->did = did;
+ mibitem->data = data;
+
+ return p80211req_dorequest(wlandev, (u8 *) &msg);
+}
+
+static int prism2_domibset_pstr32(wlandevice_t *wlandev,
+ u32 did, u8 len, const u8 *data)
+{
+ struct p80211msg_dot11req_mibset msg;
+ p80211item_pstr32_t *mibitem =
+ (p80211item_pstr32_t *) &msg.mibattribute.data;
+
+ msg.msgcode = DIDmsg_dot11req_mibset;
+ mibitem->did = did;
+ mibitem->data.len = len;
+ memcpy(mibitem->data.data, data, len);
+
+ return p80211req_dorequest(wlandev, (u8 *) &msg);
+}
+
+
+/* The interface functions, called by the cfg80211 layer */
+static int prism2_change_virtual_intf(struct wiphy *wiphy,
+ struct net_device *dev,
+ enum nl80211_iftype type, u32 *flags,
+ struct vif_params *params)
+{
+ wlandevice_t *wlandev = dev->ml_priv;
+ u32 data;
+ int result;
+ int err = 0;
+
+ switch (type) {
+ case NL80211_IFTYPE_ADHOC:
+ if (wlandev->macmode == WLAN_MACMODE_IBSS_STA)
+ goto exit;
+ wlandev->macmode = WLAN_MACMODE_IBSS_STA;
+ data = 0;
+ break;
+ case NL80211_IFTYPE_STATION:
+ if (wlandev->macmode == WLAN_MACMODE_ESS_STA)
+ goto exit;
+ wlandev->macmode = WLAN_MACMODE_ESS_STA;
+ data = 1;
+ break;
+ default:
+ netdev_warn(dev, "Operation mode: %d not support\n", type);
+ return -EOPNOTSUPP;
+ }
+
+ /* Set Operation mode to the PORT TYPE RID */
+ result = prism2_domibset_uint32(wlandev,
+ DIDmib_p2_p2Static_p2CnfPortType,
+ data);
+
+ if (result)
+ err = -EFAULT;
+
+ dev->ieee80211_ptr->iftype = type;
+
+exit:
+ return err;
+}
+
+static int prism2_add_key(struct wiphy *wiphy, struct net_device *dev,
+ u8 key_index, bool pairwise, const u8 *mac_addr,
+ struct key_params *params)
+{
+ wlandevice_t *wlandev = dev->ml_priv;
+ u32 did;
+
+ int err = 0;
+ int result = 0;
+
+ switch (params->cipher) {
+ case WLAN_CIPHER_SUITE_WEP40:
+ case WLAN_CIPHER_SUITE_WEP104:
+ result = prism2_domibset_uint32(wlandev,
+ DIDmib_dot11smt_dot11PrivacyTable_dot11WEPDefaultKeyID,
+ key_index);
+ if (result)
+ goto exit;
+
+ /* send key to driver */
+ switch (key_index) {
+ case 0:
+ did = DIDmib_dot11smt_dot11WEPDefaultKeysTable_dot11WEPDefaultKey0;
+ break;
+
+ case 1:
+ did = DIDmib_dot11smt_dot11WEPDefaultKeysTable_dot11WEPDefaultKey1;
+ break;
+
+ case 2:
+ did = DIDmib_dot11smt_dot11WEPDefaultKeysTable_dot11WEPDefaultKey2;
+ break;
+
+ case 3:
+ did = DIDmib_dot11smt_dot11WEPDefaultKeysTable_dot11WEPDefaultKey3;
+ break;
+
+ default:
+ err = -EINVAL;
+ goto exit;
+ }
+
+ result = prism2_domibset_pstr32(wlandev, did,
+ params->key_len, params->key);
+ if (result)
+ goto exit;
+ break;
+
+ default:
+ pr_debug("Unsupported cipher suite\n");
+ result = 1;
+ }
+
+exit:
+ if (result)
+ err = -EFAULT;
+
+ return err;
+}
+
+static int prism2_get_key(struct wiphy *wiphy, struct net_device *dev,
+ u8 key_index, bool pairwise,
+ const u8 *mac_addr, void *cookie,
+ void (*callback)(void *cookie, struct key_params*))
+{
+ wlandevice_t *wlandev = dev->ml_priv;
+ struct key_params params;
+ int len;
+
+ if (key_index >= NUM_WEPKEYS)
+ return -EINVAL;
+
+ len = wlandev->wep_keylens[key_index];
+ memset(&params, 0, sizeof(params));
+
+ if (len == 13)
+ params.cipher = WLAN_CIPHER_SUITE_WEP104;
+ else if (len == 5)
+ params.cipher = WLAN_CIPHER_SUITE_WEP104;
+ else
+ return -ENOENT;
+ params.key_len = len;
+ params.key = wlandev->wep_keys[key_index];
+ params.seq_len = 0;
+
+ callback(cookie, &params);
+
+ return 0;
+}
+
+static int prism2_del_key(struct wiphy *wiphy, struct net_device *dev,
+ u8 key_index, bool pairwise, const u8 *mac_addr)
+{
+ wlandevice_t *wlandev = dev->ml_priv;
+ u32 did;
+ int err = 0;
+ int result = 0;
+
+ /* There is no direct way in the hardware (AFAIK) of removing
+ a key, so we will cheat by setting the key to a bogus value */
+ /* send key to driver */
+ switch (key_index) {
+ case 0:
+ did =
+ DIDmib_dot11smt_dot11WEPDefaultKeysTable_dot11WEPDefaultKey0;
+ break;
+
+ case 1:
+ did =
+ DIDmib_dot11smt_dot11WEPDefaultKeysTable_dot11WEPDefaultKey1;
+ break;
+
+ case 2:
+ did =
+ DIDmib_dot11smt_dot11WEPDefaultKeysTable_dot11WEPDefaultKey2;
+ break;
+
+ case 3:
+ did =
+ DIDmib_dot11smt_dot11WEPDefaultKeysTable_dot11WEPDefaultKey3;
+ break;
+
+ default:
+ err = -EINVAL;
+ goto exit;
+ }
+
+ result = prism2_domibset_pstr32(wlandev, did, 13, "0000000000000");
+
+exit:
+ if (result)
+ err = -EFAULT;
+
+ return err;
+}
+
+static int prism2_set_default_key(struct wiphy *wiphy, struct net_device *dev,
+ u8 key_index, bool unicast, bool multicast)
+{
+ wlandevice_t *wlandev = dev->ml_priv;
+
+ int err = 0;
+ int result = 0;
+
+ result = prism2_domibset_uint32(wlandev,
+ DIDmib_dot11smt_dot11PrivacyTable_dot11WEPDefaultKeyID,
+ key_index);
+
+ if (result)
+ err = -EFAULT;
+
+ return err;
+}
+
+
+static int prism2_get_station(struct wiphy *wiphy, struct net_device *dev,
+ const u8 *mac, struct station_info *sinfo)
+{
+ wlandevice_t *wlandev = dev->ml_priv;
+ struct p80211msg_lnxreq_commsquality quality;
+ int result;
+
+ memset(sinfo, 0, sizeof(*sinfo));
+
+ if ((wlandev == NULL) || (wlandev->msdstate != WLAN_MSD_RUNNING))
+ return -EOPNOTSUPP;
+
+ /* build request message */
+ quality.msgcode = DIDmsg_lnxreq_commsquality;
+ quality.dbm.data = P80211ENUM_truth_true;
+ quality.dbm.status = P80211ENUM_msgitem_status_data_ok;
+
+ /* send message to nsd */
+ if (wlandev->mlmerequest == NULL)
+ return -EOPNOTSUPP;
+
+ result = wlandev->mlmerequest(wlandev, (struct p80211msg *) &quality);
+
+
+ if (result == 0) {
+ sinfo->txrate.legacy = quality.txrate.data;
+ sinfo->filled |= BIT(NL80211_STA_INFO_TX_BITRATE);
+ sinfo->signal = quality.level.data;
+ sinfo->filled |= BIT(NL80211_STA_INFO_SIGNAL);
+ }
+
+ return result;
+}
+
+static int prism2_scan(struct wiphy *wiphy,
+ struct cfg80211_scan_request *request)
+{
+ struct net_device *dev;
+ struct prism2_wiphy_private *priv = wiphy_priv(wiphy);
+ wlandevice_t *wlandev;
+ struct p80211msg_dot11req_scan msg1;
+ struct p80211msg_dot11req_scan_results msg2;
+ struct cfg80211_bss *bss;
+ int result;
+ int err = 0;
+ int numbss = 0;
+ int i = 0;
+ u8 ie_buf[46];
+ int ie_len;
+
+ if (!request)
+ return -EINVAL;
+
+ dev = request->wdev->netdev;
+ wlandev = dev->ml_priv;
+
+ if (priv->scan_request && priv->scan_request != request)
+ return -EBUSY;
+
+ if (wlandev->macmode == WLAN_MACMODE_ESS_AP) {
+ netdev_err(dev, "Can't scan in AP mode\n");
+ return -EOPNOTSUPP;
+ }
+
+ priv->scan_request = request;
+
+ memset(&msg1, 0x00, sizeof(struct p80211msg_dot11req_scan));
+ msg1.msgcode = DIDmsg_dot11req_scan;
+ msg1.bsstype.data = P80211ENUM_bsstype_any;
+
+ memset(&msg1.bssid.data.data, 0xFF, sizeof(msg1.bssid.data.data));
+ msg1.bssid.data.len = 6;
+
+ if (request->n_ssids > 0) {
+ msg1.scantype.data = P80211ENUM_scantype_active;
+ msg1.ssid.data.len = request->ssids->ssid_len;
+ memcpy(msg1.ssid.data.data,
+ request->ssids->ssid, request->ssids->ssid_len);
+ } else {
+ msg1.scantype.data = 0;
+ }
+ msg1.probedelay.data = 0;
+
+ for (i = 0;
+ (i < request->n_channels) && i < ARRAY_SIZE(prism2_channels);
+ i++)
+ msg1.channellist.data.data[i] =
+ ieee80211_frequency_to_channel(
+ request->channels[i]->center_freq);
+ msg1.channellist.data.len = request->n_channels;
+
+ msg1.maxchanneltime.data = 250;
+ msg1.minchanneltime.data = 200;
+
+ result = p80211req_dorequest(wlandev, (u8 *) &msg1);
+ if (result) {
+ err = prism2_result2err(msg1.resultcode.data);
+ goto exit;
+ }
+ /* Now retrieve scan results */
+ numbss = msg1.numbss.data;
+
+ for (i = 0; i < numbss; i++) {
+ int freq;
+
+ memset(&msg2, 0, sizeof(msg2));
+ msg2.msgcode = DIDmsg_dot11req_scan_results;
+ msg2.bssindex.data = i;
+
+ result = p80211req_dorequest(wlandev, (u8 *) &msg2);
+ if ((result != 0) ||
+ (msg2.resultcode.data != P80211ENUM_resultcode_success)) {
+ break;
+ }
+
+ ie_buf[0] = WLAN_EID_SSID;
+ ie_buf[1] = msg2.ssid.data.len;
+ ie_len = ie_buf[1] + 2;
+ memcpy(&ie_buf[2], &(msg2.ssid.data.data), msg2.ssid.data.len);
+ freq = ieee80211_channel_to_frequency(msg2.dschannel.data,
+ IEEE80211_BAND_2GHZ);
+ bss = cfg80211_inform_bss(wiphy,
+ ieee80211_get_channel(wiphy, freq),
+ CFG80211_BSS_FTYPE_UNKNOWN,
+ (const u8 *) &(msg2.bssid.data.data),
+ msg2.timestamp.data, msg2.capinfo.data,
+ msg2.beaconperiod.data,
+ ie_buf,
+ ie_len,
+ (msg2.signal.data - 65536) * 100, /* Conversion to signed type */
+ GFP_KERNEL
+ );
+
+ if (!bss) {
+ err = -ENOMEM;
+ goto exit;
+ }
+
+ cfg80211_put_bss(wiphy, bss);
+ }
+
+ if (result)
+ err = prism2_result2err(msg2.resultcode.data);
+
+exit:
+ cfg80211_scan_done(request, err ? 1 : 0);
+ priv->scan_request = NULL;
+ return err;
+}
+
+static int prism2_set_wiphy_params(struct wiphy *wiphy, u32 changed)
+{
+ struct prism2_wiphy_private *priv = wiphy_priv(wiphy);
+ wlandevice_t *wlandev = priv->wlandev;
+ u32 data;
+ int result;
+ int err = 0;
+
+ if (changed & WIPHY_PARAM_RTS_THRESHOLD) {
+ if (wiphy->rts_threshold == -1)
+ data = 2347;
+ else
+ data = wiphy->rts_threshold;
+
+ result = prism2_domibset_uint32(wlandev,
+ DIDmib_dot11mac_dot11OperationTable_dot11RTSThreshold,
+ data);
+ if (result) {
+ err = -EFAULT;
+ goto exit;
+ }
+ }
+
+ if (changed & WIPHY_PARAM_FRAG_THRESHOLD) {
+ if (wiphy->frag_threshold == -1)
+ data = 2346;
+ else
+ data = wiphy->frag_threshold;
+
+ result = prism2_domibset_uint32(wlandev,
+ DIDmib_dot11mac_dot11OperationTable_dot11FragmentationThreshold,
+ data);
+ if (result) {
+ err = -EFAULT;
+ goto exit;
+ }
+ }
+
+exit:
+ return err;
+}
+
+static int prism2_connect(struct wiphy *wiphy, struct net_device *dev,
+ struct cfg80211_connect_params *sme)
+{
+ wlandevice_t *wlandev = dev->ml_priv;
+ struct ieee80211_channel *channel = sme->channel;
+ struct p80211msg_lnxreq_autojoin msg_join;
+ u32 did;
+ int length = sme->ssid_len;
+ int chan = -1;
+ int is_wep = (sme->crypto.cipher_group == WLAN_CIPHER_SUITE_WEP40) ||
+ (sme->crypto.cipher_group == WLAN_CIPHER_SUITE_WEP104);
+ int result;
+ int err = 0;
+
+ /* Set the channel */
+ if (channel) {
+ chan = ieee80211_frequency_to_channel(channel->center_freq);
+ result = prism2_domibset_uint32(wlandev,
+ DIDmib_dot11phy_dot11PhyDSSSTable_dot11CurrentChannel,
+ chan);
+ if (result)
+ goto exit;
+ }
+
+ /* Set the authorization */
+ if ((sme->auth_type == NL80211_AUTHTYPE_OPEN_SYSTEM) ||
+ ((sme->auth_type == NL80211_AUTHTYPE_AUTOMATIC) && !is_wep))
+ msg_join.authtype.data = P80211ENUM_authalg_opensystem;
+ else if ((sme->auth_type == NL80211_AUTHTYPE_SHARED_KEY) ||
+ ((sme->auth_type == NL80211_AUTHTYPE_AUTOMATIC) && is_wep))
+ msg_join.authtype.data = P80211ENUM_authalg_sharedkey;
+ else
+ netdev_warn(dev,
+ "Unhandled authorisation type for connect (%d)\n",
+ sme->auth_type);
+
+ /* Set the encryption - we only support wep */
+ if (is_wep) {
+ if (sme->key) {
+ result = prism2_domibset_uint32(wlandev,
+ DIDmib_dot11smt_dot11PrivacyTable_dot11WEPDefaultKeyID,
+ sme->key_idx);
+ if (result)
+ goto exit;
+
+ /* send key to driver */
+ switch (sme->key_idx) {
+ case 0:
+ did = DIDmib_dot11smt_dot11WEPDefaultKeysTable_dot11WEPDefaultKey0;
+ break;
+
+ case 1:
+ did = DIDmib_dot11smt_dot11WEPDefaultKeysTable_dot11WEPDefaultKey1;
+ break;
+
+ case 2:
+ did = DIDmib_dot11smt_dot11WEPDefaultKeysTable_dot11WEPDefaultKey2;
+ break;
+
+ case 3:
+ did = DIDmib_dot11smt_dot11WEPDefaultKeysTable_dot11WEPDefaultKey3;
+ break;
+
+ default:
+ err = -EINVAL;
+ goto exit;
+ }
+
+ result = prism2_domibset_pstr32(wlandev,
+ did, sme->key_len,
+ (u8 *)sme->key);
+ if (result)
+ goto exit;
+
+ }
+
+ /* Assume we should set privacy invoked and exclude unencrypted
+ We could possibly use sme->privacy here, but the assumption
+ seems reasonable anyway */
+ result = prism2_domibset_uint32(wlandev,
+ DIDmib_dot11smt_dot11PrivacyTable_dot11PrivacyInvoked,
+ P80211ENUM_truth_true);
+ if (result)
+ goto exit;
+
+ result = prism2_domibset_uint32(wlandev,
+ DIDmib_dot11smt_dot11PrivacyTable_dot11ExcludeUnencrypted,
+ P80211ENUM_truth_true);
+ if (result)
+ goto exit;
+
+ } else {
+ /* Assume we should unset privacy invoked
+ and exclude unencrypted */
+ result = prism2_domibset_uint32(wlandev,
+ DIDmib_dot11smt_dot11PrivacyTable_dot11PrivacyInvoked,
+ P80211ENUM_truth_false);
+ if (result)
+ goto exit;
+
+ result = prism2_domibset_uint32(wlandev,
+ DIDmib_dot11smt_dot11PrivacyTable_dot11ExcludeUnencrypted,
+ P80211ENUM_truth_false);
+ if (result)
+ goto exit;
+
+ }
+
+ /* Now do the actual join. Note there is no way that I can
+ see to request a specific bssid */
+ msg_join.msgcode = DIDmsg_lnxreq_autojoin;
+
+ memcpy(msg_join.ssid.data.data, sme->ssid, length);
+ msg_join.ssid.data.len = length;
+
+ result = p80211req_dorequest(wlandev, (u8 *) &msg_join);
+
+exit:
+ if (result)
+ err = -EFAULT;
+
+ return err;
+}
+
+static int prism2_disconnect(struct wiphy *wiphy, struct net_device *dev,
+ u16 reason_code)
+{
+ wlandevice_t *wlandev = dev->ml_priv;
+ struct p80211msg_lnxreq_autojoin msg_join;
+ int result;
+ int err = 0;
+
+
+ /* Do a join, with a bogus ssid. Thats the only way I can think of */
+ msg_join.msgcode = DIDmsg_lnxreq_autojoin;
+
+ memcpy(msg_join.ssid.data.data, "---", 3);
+ msg_join.ssid.data.len = 3;
+
+ result = p80211req_dorequest(wlandev, (u8 *) &msg_join);
+
+ if (result)
+ err = -EFAULT;
+
+ return err;
+}
+
+
+static int prism2_join_ibss(struct wiphy *wiphy, struct net_device *dev,
+ struct cfg80211_ibss_params *params)
+{
+ return -EOPNOTSUPP;
+}
+
+static int prism2_leave_ibss(struct wiphy *wiphy, struct net_device *dev)
+{
+ return -EOPNOTSUPP;
+}
+
+
+static int prism2_set_tx_power(struct wiphy *wiphy, struct wireless_dev *wdev,
+ enum nl80211_tx_power_setting type, int mbm)
+{
+ struct prism2_wiphy_private *priv = wiphy_priv(wiphy);
+ wlandevice_t *wlandev = priv->wlandev;
+ u32 data;
+ int result;
+ int err = 0;
+
+ if (type == NL80211_TX_POWER_AUTOMATIC)
+ data = 30;
+ else
+ data = MBM_TO_DBM(mbm);
+
+ result = prism2_domibset_uint32(wlandev,
+ DIDmib_dot11phy_dot11PhyTxPowerTable_dot11CurrentTxPowerLevel,
+ data);
+
+ if (result) {
+ err = -EFAULT;
+ goto exit;
+ }
+
+exit:
+ return err;
+}
+
+static int prism2_get_tx_power(struct wiphy *wiphy, struct wireless_dev *wdev,
+ int *dbm)
+{
+ struct prism2_wiphy_private *priv = wiphy_priv(wiphy);
+ wlandevice_t *wlandev = priv->wlandev;
+ struct p80211msg_dot11req_mibget msg;
+ p80211item_uint32_t *mibitem;
+ int result;
+ int err = 0;
+
+ mibitem = (p80211item_uint32_t *) &msg.mibattribute.data;
+ msg.msgcode = DIDmsg_dot11req_mibget;
+ mibitem->did =
+ DIDmib_dot11phy_dot11PhyTxPowerTable_dot11CurrentTxPowerLevel;
+
+ result = p80211req_dorequest(wlandev, (u8 *) &msg);
+
+ if (result) {
+ err = -EFAULT;
+ goto exit;
+ }
+
+ *dbm = mibitem->data;
+
+exit:
+ return err;
+}
+
+
+
+
+/* Interface callback functions, passing data back up to the cfg80211 layer */
+void prism2_connect_result(wlandevice_t *wlandev, u8 failed)
+{
+ u16 status = failed ?
+ WLAN_STATUS_UNSPECIFIED_FAILURE : WLAN_STATUS_SUCCESS;
+
+ cfg80211_connect_result(wlandev->netdev, wlandev->bssid,
+ NULL, 0, NULL, 0, status, GFP_KERNEL);
+}
+
+void prism2_disconnected(wlandevice_t *wlandev)
+{
+ cfg80211_disconnected(wlandev->netdev, 0, NULL,
+ 0, GFP_KERNEL);
+}
+
+void prism2_roamed(wlandevice_t *wlandev)
+{
+ cfg80211_roamed(wlandev->netdev, NULL, wlandev->bssid,
+ NULL, 0, NULL, 0, GFP_KERNEL);
+}
+
+
+/* Structures for declaring wiphy interface */
+static const struct cfg80211_ops prism2_usb_cfg_ops = {
+ .change_virtual_intf = prism2_change_virtual_intf,
+ .add_key = prism2_add_key,
+ .get_key = prism2_get_key,
+ .del_key = prism2_del_key,
+ .set_default_key = prism2_set_default_key,
+ .get_station = prism2_get_station,
+ .scan = prism2_scan,
+ .set_wiphy_params = prism2_set_wiphy_params,
+ .connect = prism2_connect,
+ .disconnect = prism2_disconnect,
+ .join_ibss = prism2_join_ibss,
+ .leave_ibss = prism2_leave_ibss,
+ .set_tx_power = prism2_set_tx_power,
+ .get_tx_power = prism2_get_tx_power,
+};
+
+
+/* Functions to create/free wiphy interface */
+static struct wiphy *wlan_create_wiphy(struct device *dev, wlandevice_t *wlandev)
+{
+ struct wiphy *wiphy;
+ struct prism2_wiphy_private *priv;
+
+ wiphy = wiphy_new(&prism2_usb_cfg_ops, sizeof(*priv));
+ if (!wiphy)
+ return NULL;
+
+ priv = wiphy_priv(wiphy);
+ priv->wlandev = wlandev;
+ memcpy(priv->channels, prism2_channels, sizeof(prism2_channels));
+ memcpy(priv->rates, prism2_rates, sizeof(prism2_rates));
+ priv->band.channels = priv->channels;
+ priv->band.n_channels = ARRAY_SIZE(prism2_channels);
+ priv->band.bitrates = priv->rates;
+ priv->band.n_bitrates = ARRAY_SIZE(prism2_rates);
+ priv->band.band = IEEE80211_BAND_2GHZ;
+ priv->band.ht_cap.ht_supported = false;
+ wiphy->bands[IEEE80211_BAND_2GHZ] = &priv->band;
+
+ set_wiphy_dev(wiphy, dev);
+ wiphy->privid = prism2_wiphy_privid;
+ wiphy->max_scan_ssids = 1;
+ wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION)
+ | BIT(NL80211_IFTYPE_ADHOC);
+ wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM;
+ wiphy->n_cipher_suites = PRISM2_NUM_CIPHER_SUITES;
+ wiphy->cipher_suites = prism2_cipher_suites;
+
+ if (wiphy_register(wiphy) < 0)
+ return NULL;
+
+ return wiphy;
+}
+
+
+static void wlan_free_wiphy(struct wiphy *wiphy)
+{
+ wiphy_unregister(wiphy);
+ wiphy_free(wiphy);
+}
diff --git a/drivers/staging/wlan-ng/hfa384x.h b/drivers/staging/wlan-ng/hfa384x.h
new file mode 100644
index 000000000..8dfe4381d
--- /dev/null
+++ b/drivers/staging/wlan-ng/hfa384x.h
@@ -0,0 +1,1431 @@
+/* hfa384x.h
+*
+* Defines the constants and data structures for the hfa384x
+*
+* Copyright (C) 1999 AbsoluteValue Systems, Inc. All Rights Reserved.
+* --------------------------------------------------------------------
+*
+* linux-wlan
+*
+* The contents of this file are subject to the Mozilla Public
+* License Version 1.1 (the "License"); you may not use this file
+* except in compliance with the License. You may obtain a copy of
+* the License at http://www.mozilla.org/MPL/
+*
+* Software distributed under the License is distributed on an "AS
+* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+* implied. See the License for the specific language governing
+* rights and limitations under the License.
+*
+* Alternatively, the contents of this file may be used under the
+* terms of the GNU Public License version 2 (the "GPL"), in which
+* case the provisions of the GPL are applicable instead of the
+* above. If you wish to allow the use of your version of this file
+* only under the terms of the GPL and not to allow others to use
+* your version of this file under the MPL, indicate your decision
+* by deleting the provisions above and replace them with the notice
+* and other provisions required by the GPL. If you do not delete
+* the provisions above, a recipient may use your version of this
+* file under either the MPL or the GPL.
+*
+* --------------------------------------------------------------------
+*
+* Inquiries regarding the linux-wlan Open Source project can be
+* made directly to:
+*
+* AbsoluteValue Systems Inc.
+* info@linux-wlan.com
+* http://www.linux-wlan.com
+*
+* --------------------------------------------------------------------
+*
+* Portions of the development of this software were funded by
+* Intersil Corporation as part of PRISM(R) chipset product development.
+*
+* --------------------------------------------------------------------
+*
+* [Implementation and usage notes]
+*
+* [References]
+* CW10 Programmer's Manual v1.5
+* IEEE 802.11 D10.0
+*
+* --------------------------------------------------------------------
+*/
+
+#ifndef _HFA384x_H
+#define _HFA384x_H
+
+#define HFA384x_FIRMWARE_VERSION(a, b, c) (((a) << 16) + ((b) << 8) + (c))
+
+#include <linux/if_ether.h>
+#include <linux/usb.h>
+
+/*--- Mins & Maxs -----------------------------------*/
+#define HFA384x_PORTID_MAX ((u16)7)
+#define HFA384x_NUMPORTS_MAX ((u16)(HFA384x_PORTID_MAX+1))
+#define HFA384x_PDR_LEN_MAX ((u16)512) /* in bytes, from EK */
+#define HFA384x_PDA_RECS_MAX ((u16)200) /* a guess */
+#define HFA384x_PDA_LEN_MAX ((u16)1024) /* in bytes, from EK*/
+#define HFA384x_SCANRESULT_MAX ((u16)31)
+#define HFA384x_HSCANRESULT_MAX ((u16)31)
+#define HFA384x_CHINFORESULT_MAX ((u16)16)
+#define HFA384x_RID_GUESSING_MAXLEN 2048 /* I'm not really sure */
+#define HFA384x_RIDDATA_MAXLEN HFA384x_RID_GUESSING_MAXLEN
+#define HFA384x_USB_RWMEM_MAXLEN 2048
+
+/*--- Support Constants -----------------------------*/
+#define HFA384x_PORTTYPE_IBSS ((u16)0)
+#define HFA384x_PORTTYPE_BSS ((u16)1)
+#define HFA384x_PORTTYPE_PSUEDOIBSS ((u16)3)
+#define HFA384x_WEPFLAGS_PRIVINVOKED ((u16)BIT(0))
+#define HFA384x_WEPFLAGS_EXCLUDE ((u16)BIT(1))
+#define HFA384x_WEPFLAGS_DISABLE_TXCRYPT ((u16)BIT(4))
+#define HFA384x_WEPFLAGS_DISABLE_RXCRYPT ((u16)BIT(7))
+#define HFA384x_ROAMMODE_HOSTSCAN_HOSTROAM ((u16)3)
+#define HFA384x_PORTSTATUS_DISABLED ((u16)1)
+#define HFA384x_RATEBIT_1 ((u16)1)
+#define HFA384x_RATEBIT_2 ((u16)2)
+#define HFA384x_RATEBIT_5dot5 ((u16)4)
+#define HFA384x_RATEBIT_11 ((u16)8)
+
+/*--- MAC Internal memory constants and macros ------*/
+/* masks and macros used to manipulate MAC internal memory addresses. */
+/* MAC internal memory addresses are 23 bit quantities. The MAC uses
+ * a paged address space where the upper 16 bits are the page number
+ * and the lower 7 bits are the offset. There are various Host API
+ * elements that require two 16-bit quantities to specify a MAC
+ * internal memory address. Unfortunately, some of the API's use a
+ * page/offset format where the offset value is JUST the lower seven
+ * bits and the page is the remaining 16 bits. Some of the API's
+ * assume that the 23 bit address has been split at the 16th bit. We
+ * refer to these two formats as AUX format and CMD format. The
+ * macros below help handle some of this.
+ */
+
+/* Mask bits for discarding unwanted pieces in a flat address */
+#define HFA384x_ADDR_FLAT_AUX_PAGE_MASK (0x007fff80)
+#define HFA384x_ADDR_FLAT_AUX_OFF_MASK (0x0000007f)
+#define HFA384x_ADDR_FLAT_CMD_PAGE_MASK (0xffff0000)
+#define HFA384x_ADDR_FLAT_CMD_OFF_MASK (0x0000ffff)
+
+/* Mask bits for discarding unwanted pieces in AUX format
+ 16-bit address parts */
+#define HFA384x_ADDR_AUX_PAGE_MASK (0xffff)
+#define HFA384x_ADDR_AUX_OFF_MASK (0x007f)
+
+/* Make a 32-bit flat address from AUX format 16-bit page and offset */
+#define HFA384x_ADDR_AUX_MKFLAT(p, o) \
+ ((((u32)(((u16)(p))&HFA384x_ADDR_AUX_PAGE_MASK)) << 7) | \
+ ((u32)(((u16)(o))&HFA384x_ADDR_AUX_OFF_MASK)))
+
+/* Make CMD format offset and page from a 32-bit flat address */
+#define HFA384x_ADDR_CMD_MKPAGE(f) \
+ ((u16)((((u32)(f))&HFA384x_ADDR_FLAT_CMD_PAGE_MASK)>>16))
+#define HFA384x_ADDR_CMD_MKOFF(f) \
+ ((u16)(((u32)(f))&HFA384x_ADDR_FLAT_CMD_OFF_MASK))
+
+/*--- Controller Memory addresses -------------------*/
+#define HFA3842_PDA_BASE (0x007f0000UL)
+#define HFA3841_PDA_BASE (0x003f0000UL)
+#define HFA3841_PDA_BOGUS_BASE (0x00390000UL)
+
+/*--- Driver Download states -----------------------*/
+#define HFA384x_DLSTATE_DISABLED 0
+#define HFA384x_DLSTATE_RAMENABLED 1
+#define HFA384x_DLSTATE_FLASHENABLED 2
+
+/*--- Register Field Masks --------------------------*/
+#define HFA384x_CMD_AINFO ((u16)(BIT(14) | BIT(13) \
+ | BIT(12) | BIT(11) \
+ | BIT(10) | BIT(9) \
+ | BIT(8)))
+#define HFA384x_CMD_MACPORT ((u16)(BIT(10) | BIT(9) | \
+ BIT(8)))
+#define HFA384x_CMD_PROGMODE ((u16)(BIT(9) | BIT(8)))
+#define HFA384x_CMD_CMDCODE ((u16)(BIT(5) | BIT(4) | \
+ BIT(3) | BIT(2) | \
+ BIT(1) | BIT(0)))
+
+#define HFA384x_STATUS_RESULT ((u16)(BIT(14) | BIT(13) \
+ | BIT(12) | BIT(11) \
+ | BIT(10) | BIT(9) \
+ | BIT(8)))
+
+/*--- Command Code Constants --------------------------*/
+/*--- Controller Commands --------------------------*/
+#define HFA384x_CMDCODE_INIT ((u16)0x00)
+#define HFA384x_CMDCODE_ENABLE ((u16)0x01)
+#define HFA384x_CMDCODE_DISABLE ((u16)0x02)
+
+/*--- Regulate Commands --------------------------*/
+#define HFA384x_CMDCODE_INQ ((u16)0x11)
+
+/*--- Configure Commands --------------------------*/
+#define HFA384x_CMDCODE_DOWNLD ((u16)0x22)
+
+/*--- Debugging Commands -----------------------------*/
+#define HFA384x_CMDCODE_MONITOR ((u16)(0x38))
+#define HFA384x_MONITOR_ENABLE ((u16)(0x0b))
+#define HFA384x_MONITOR_DISABLE ((u16)(0x0f))
+
+/*--- Result Codes --------------------------*/
+#define HFA384x_CMD_ERR ((u16)(0x7F))
+
+/*--- Programming Modes --------------------------
+ MODE 0: Disable programming
+ MODE 1: Enable volatile memory programming
+ MODE 2: Enable non-volatile memory programming
+ MODE 3: Program non-volatile memory section
+--------------------------------------------------*/
+#define HFA384x_PROGMODE_DISABLE ((u16)0x00)
+#define HFA384x_PROGMODE_RAM ((u16)0x01)
+#define HFA384x_PROGMODE_NV ((u16)0x02)
+#define HFA384x_PROGMODE_NVWRITE ((u16)0x03)
+
+/*--- Record ID Constants --------------------------*/
+/*--------------------------------------------------------------------
+Configuration RIDs: Network Parameters, Static Configuration Entities
+--------------------------------------------------------------------*/
+#define HFA384x_RID_CNFPORTTYPE ((u16)0xFC00)
+#define HFA384x_RID_CNFOWNMACADDR ((u16)0xFC01)
+#define HFA384x_RID_CNFDESIREDSSID ((u16)0xFC02)
+#define HFA384x_RID_CNFOWNCHANNEL ((u16)0xFC03)
+#define HFA384x_RID_CNFOWNSSID ((u16)0xFC04)
+#define HFA384x_RID_CNFMAXDATALEN ((u16)0xFC07)
+
+/*--------------------------------------------------------------------
+Configuration RID lengths: Network Params, Static Config Entities
+ This is the length of JUST the DATA part of the RID (does not
+ include the len or code fields)
+--------------------------------------------------------------------*/
+#define HFA384x_RID_CNFOWNMACADDR_LEN ((u16)6)
+#define HFA384x_RID_CNFDESIREDSSID_LEN ((u16)34)
+#define HFA384x_RID_CNFOWNSSID_LEN ((u16)34)
+
+/*--------------------------------------------------------------------
+Configuration RIDs: Network Parameters, Dynamic Configuration Entities
+--------------------------------------------------------------------*/
+#define HFA384x_RID_CREATEIBSS ((u16)0xFC81)
+#define HFA384x_RID_FRAGTHRESH ((u16)0xFC82)
+#define HFA384x_RID_RTSTHRESH ((u16)0xFC83)
+#define HFA384x_RID_TXRATECNTL ((u16)0xFC84)
+#define HFA384x_RID_PROMISCMODE ((u16)0xFC85)
+
+/*----------------------------------------------------------------------
+Information RIDs: NIC Information
+--------------------------------------------------------------------*/
+#define HFA384x_RID_MAXLOADTIME ((u16)0xFD00)
+#define HFA384x_RID_DOWNLOADBUFFER ((u16)0xFD01)
+#define HFA384x_RID_PRIIDENTITY ((u16)0xFD02)
+#define HFA384x_RID_PRISUPRANGE ((u16)0xFD03)
+#define HFA384x_RID_PRI_CFIACTRANGES ((u16)0xFD04)
+#define HFA384x_RID_NICSERIALNUMBER ((u16)0xFD0A)
+#define HFA384x_RID_NICIDENTITY ((u16)0xFD0B)
+#define HFA384x_RID_MFISUPRANGE ((u16)0xFD0C)
+#define HFA384x_RID_CFISUPRANGE ((u16)0xFD0D)
+#define HFA384x_RID_STAIDENTITY ((u16)0xFD20)
+#define HFA384x_RID_STASUPRANGE ((u16)0xFD21)
+#define HFA384x_RID_STA_MFIACTRANGES ((u16)0xFD22)
+#define HFA384x_RID_STA_CFIACTRANGES ((u16)0xFD23)
+
+/*----------------------------------------------------------------------
+Information RID Lengths: NIC Information
+ This is the length of JUST the DATA part of the RID (does not
+ include the len or code fields)
+--------------------------------------------------------------------*/
+#define HFA384x_RID_NICSERIALNUMBER_LEN ((u16)12)
+
+/*--------------------------------------------------------------------
+Information RIDs: MAC Information
+--------------------------------------------------------------------*/
+#define HFA384x_RID_PORTSTATUS ((u16)0xFD40)
+#define HFA384x_RID_CURRENTSSID ((u16)0xFD41)
+#define HFA384x_RID_CURRENTBSSID ((u16)0xFD42)
+#define HFA384x_RID_CURRENTTXRATE ((u16)0xFD44)
+#define HFA384x_RID_SHORTRETRYLIMIT ((u16)0xFD48)
+#define HFA384x_RID_LONGRETRYLIMIT ((u16)0xFD49)
+#define HFA384x_RID_MAXTXLIFETIME ((u16)0xFD4A)
+#define HFA384x_RID_PRIVACYOPTIMP ((u16)0xFD4F)
+#define HFA384x_RID_DBMCOMMSQUALITY ((u16)0xFD51)
+
+/*--------------------------------------------------------------------
+Information RID Lengths: MAC Information
+ This is the length of JUST the DATA part of the RID (does not
+ include the len or code fields)
+--------------------------------------------------------------------*/
+#define HFA384x_RID_DBMCOMMSQUALITY_LEN \
+ ((u16) sizeof(hfa384x_dbmcommsquality_t))
+#define HFA384x_RID_JOINREQUEST_LEN \
+ ((u16)sizeof(hfa384x_JoinRequest_data_t))
+
+/*--------------------------------------------------------------------
+Information RIDs: Modem Information
+--------------------------------------------------------------------*/
+#define HFA384x_RID_CURRENTCHANNEL ((u16)0xFDC1)
+
+/*--------------------------------------------------------------------
+API ENHANCEMENTS (NOT ALREADY IMPLEMENTED)
+--------------------------------------------------------------------*/
+#define HFA384x_RID_CNFWEPDEFAULTKEYID ((u16)0xFC23)
+#define HFA384x_RID_CNFWEPDEFAULTKEY0 ((u16)0xFC24)
+#define HFA384x_RID_CNFWEPDEFAULTKEY1 ((u16)0xFC25)
+#define HFA384x_RID_CNFWEPDEFAULTKEY2 ((u16)0xFC26)
+#define HFA384x_RID_CNFWEPDEFAULTKEY3 ((u16)0xFC27)
+#define HFA384x_RID_CNFWEPFLAGS ((u16)0xFC28)
+#define HFA384x_RID_CNFAUTHENTICATION ((u16)0xFC2A)
+#define HFA384x_RID_CNFROAMINGMODE ((u16)0xFC2D)
+#define HFA384x_RID_CNFAPBCNint ((u16)0xFC33)
+#define HFA384x_RID_CNFDBMADJUST ((u16)0xFC46)
+#define HFA384x_RID_CNFWPADATA ((u16)0xFC48)
+#define HFA384x_RID_CNFBASICRATES ((u16)0xFCB3)
+#define HFA384x_RID_CNFSUPPRATES ((u16)0xFCB4)
+#define HFA384x_RID_CNFPASSIVESCANCTRL ((u16)0xFCBA)
+#define HFA384x_RID_TXPOWERMAX ((u16)0xFCBE)
+#define HFA384x_RID_JOINREQUEST ((u16)0xFCE2)
+#define HFA384x_RID_AUTHENTICATESTA ((u16)0xFCE3)
+#define HFA384x_RID_HOSTSCAN ((u16)0xFCE5)
+
+#define HFA384x_RID_CNFWEPDEFAULTKEY_LEN ((u16)6)
+#define HFA384x_RID_CNFWEP128DEFAULTKEY_LEN ((u16)14)
+
+/*--------------------------------------------------------------------
+PD Record codes
+--------------------------------------------------------------------*/
+#define HFA384x_PDR_PCB_PARTNUM ((u16)0x0001)
+#define HFA384x_PDR_PDAVER ((u16)0x0002)
+#define HFA384x_PDR_NIC_SERIAL ((u16)0x0003)
+#define HFA384x_PDR_MKK_MEASUREMENTS ((u16)0x0004)
+#define HFA384x_PDR_NIC_RAMSIZE ((u16)0x0005)
+#define HFA384x_PDR_MFISUPRANGE ((u16)0x0006)
+#define HFA384x_PDR_CFISUPRANGE ((u16)0x0007)
+#define HFA384x_PDR_NICID ((u16)0x0008)
+#define HFA384x_PDR_MAC_ADDRESS ((u16)0x0101)
+#define HFA384x_PDR_REGDOMAIN ((u16)0x0103)
+#define HFA384x_PDR_ALLOWED_CHANNEL ((u16)0x0104)
+#define HFA384x_PDR_DEFAULT_CHANNEL ((u16)0x0105)
+#define HFA384x_PDR_TEMPTYPE ((u16)0x0107)
+#define HFA384x_PDR_IFR_SETTING ((u16)0x0200)
+#define HFA384x_PDR_RFR_SETTING ((u16)0x0201)
+#define HFA384x_PDR_HFA3861_BASELINE ((u16)0x0202)
+#define HFA384x_PDR_HFA3861_SHADOW ((u16)0x0203)
+#define HFA384x_PDR_HFA3861_IFRF ((u16)0x0204)
+#define HFA384x_PDR_HFA3861_CHCALSP ((u16)0x0300)
+#define HFA384x_PDR_HFA3861_CHCALI ((u16)0x0301)
+#define HFA384x_PDR_MAX_TX_POWER ((u16)0x0302)
+#define HFA384x_PDR_MASTER_CHAN_LIST ((u16)0x0303)
+#define HFA384x_PDR_3842_NIC_CONFIG ((u16)0x0400)
+#define HFA384x_PDR_USB_ID ((u16)0x0401)
+#define HFA384x_PDR_PCI_ID ((u16)0x0402)
+#define HFA384x_PDR_PCI_IFCONF ((u16)0x0403)
+#define HFA384x_PDR_PCI_PMCONF ((u16)0x0404)
+#define HFA384x_PDR_RFENRGY ((u16)0x0406)
+#define HFA384x_PDR_USB_POWER_TYPE ((u16)0x0407)
+#define HFA384x_PDR_USB_MAX_POWER ((u16)0x0409)
+#define HFA384x_PDR_USB_MANUFACTURER ((u16)0x0410)
+#define HFA384x_PDR_USB_PRODUCT ((u16)0x0411)
+#define HFA384x_PDR_ANT_DIVERSITY ((u16)0x0412)
+#define HFA384x_PDR_HFO_DELAY ((u16)0x0413)
+#define HFA384x_PDR_SCALE_THRESH ((u16)0x0414)
+
+#define HFA384x_PDR_HFA3861_MANF_TESTSP ((u16)0x0900)
+#define HFA384x_PDR_HFA3861_MANF_TESTI ((u16)0x0901)
+#define HFA384x_PDR_END_OF_PDA ((u16)0x0000)
+
+/*--- Register Test/Get/Set Field macros ------------------------*/
+
+#define HFA384x_CMD_AINFO_SET(value) ((u16)((u16)(value) << 8))
+#define HFA384x_CMD_MACPORT_SET(value) \
+ ((u16)HFA384x_CMD_AINFO_SET(value))
+#define HFA384x_CMD_PROGMODE_SET(value) \
+ ((u16)HFA384x_CMD_AINFO_SET((u16)value))
+#define HFA384x_CMD_CMDCODE_SET(value) ((u16)(value))
+
+#define HFA384x_STATUS_RESULT_SET(value) (((u16)(value)) << 8)
+
+/* Host Maintained State Info */
+#define HFA384x_STATE_PREINIT 0
+#define HFA384x_STATE_INIT 1
+#define HFA384x_STATE_RUNNING 2
+
+/*-------------------------------------------------------------*/
+/* Commonly used basic types */
+struct hfa384x_bytestr {
+ u16 len;
+ u8 data[0];
+} __packed;
+
+typedef struct hfa384x_bytestr32 {
+ u16 len;
+ u8 data[32];
+} __packed hfa384x_bytestr32_t;
+
+/*--------------------------------------------------------------------
+Configuration Record Structures:
+ Network Parameters, Static Configuration Entities
+--------------------------------------------------------------------*/
+
+/*-- Hardware/Firmware Component Information ----------*/
+typedef struct hfa384x_compident {
+ u16 id;
+ u16 variant;
+ u16 major;
+ u16 minor;
+} __packed hfa384x_compident_t;
+
+typedef struct hfa384x_caplevel {
+ u16 role;
+ u16 id;
+ u16 variant;
+ u16 bottom;
+ u16 top;
+} __packed hfa384x_caplevel_t;
+
+/*-- Configuration Record: cnfAuthentication --*/
+#define HFA384x_CNFAUTHENTICATION_OPENSYSTEM 0x0001
+#define HFA384x_CNFAUTHENTICATION_SHAREDKEY 0x0002
+#define HFA384x_CNFAUTHENTICATION_LEAP 0x0004
+
+/*--------------------------------------------------------------------
+Configuration Record Structures:
+ Network Parameters, Dynamic Configuration Entities
+--------------------------------------------------------------------*/
+
+#define HFA384x_CREATEIBSS_JOINCREATEIBSS 0
+
+/*-- Configuration Record: HostScanRequest (data portion only) --*/
+typedef struct hfa384x_HostScanRequest_data {
+ u16 channelList;
+ u16 txRate;
+ hfa384x_bytestr32_t ssid;
+} __packed hfa384x_HostScanRequest_data_t;
+
+/*-- Configuration Record: JoinRequest (data portion only) --*/
+typedef struct hfa384x_JoinRequest_data {
+ u8 bssid[WLAN_BSSID_LEN];
+ u16 channel;
+} __packed hfa384x_JoinRequest_data_t;
+
+/*-- Configuration Record: authenticateStation (data portion only) --*/
+typedef struct hfa384x_authenticateStation_data {
+ u8 address[ETH_ALEN];
+ u16 status;
+ u16 algorithm;
+} __packed hfa384x_authenticateStation_data_t;
+
+/*-- Configuration Record: WPAData (data portion only) --*/
+typedef struct hfa384x_WPAData {
+ u16 datalen;
+ u8 data[0]; /* max 80 */
+} __packed hfa384x_WPAData_t;
+
+/*--------------------------------------------------------------------
+Information Record Structures: NIC Information
+--------------------------------------------------------------------*/
+
+/*-- Information Record: DownLoadBuffer --*/
+/* NOTE: The page and offset are in AUX format */
+typedef struct hfa384x_downloadbuffer {
+ u16 page;
+ u16 offset;
+ u16 len;
+} __packed hfa384x_downloadbuffer_t;
+
+/*--------------------------------------------------------------------
+Information Record Structures: NIC Information
+--------------------------------------------------------------------*/
+
+#define HFA384x_PSTATUS_CONN_IBSS ((u16)3)
+
+/*-- Information Record: commsquality --*/
+typedef struct hfa384x_commsquality {
+ u16 CQ_currBSS;
+ u16 ASL_currBSS;
+ u16 ANL_currFC;
+} __packed hfa384x_commsquality_t;
+
+/*-- Information Record: dmbcommsquality --*/
+typedef struct hfa384x_dbmcommsquality {
+ u16 CQdbm_currBSS;
+ u16 ASLdbm_currBSS;
+ u16 ANLdbm_currFC;
+} __packed hfa384x_dbmcommsquality_t;
+
+/*--------------------------------------------------------------------
+FRAME STRUCTURES: Communication Frames
+----------------------------------------------------------------------
+Communication Frames: Transmit Frames
+--------------------------------------------------------------------*/
+/*-- Communication Frame: Transmit Frame Structure --*/
+typedef struct hfa384x_tx_frame {
+ u16 status;
+ u16 reserved1;
+ u16 reserved2;
+ u32 sw_support;
+ u8 tx_retrycount;
+ u8 tx_rate;
+ u16 tx_control;
+
+ /*-- 802.11 Header Information --*/
+
+ u16 frame_control;
+ u16 duration_id;
+ u8 address1[6];
+ u8 address2[6];
+ u8 address3[6];
+ u16 sequence_control;
+ u8 address4[6];
+ u16 data_len; /* little endian format */
+
+ /*-- 802.3 Header Information --*/
+
+ u8 dest_addr[6];
+ u8 src_addr[6];
+ u16 data_length; /* big endian format */
+} __packed hfa384x_tx_frame_t;
+/*--------------------------------------------------------------------
+Communication Frames: Field Masks for Transmit Frames
+--------------------------------------------------------------------*/
+/*-- Status Field --*/
+#define HFA384x_TXSTATUS_ACKERR ((u16)BIT(5))
+#define HFA384x_TXSTATUS_FORMERR ((u16)BIT(3))
+#define HFA384x_TXSTATUS_DISCON ((u16)BIT(2))
+#define HFA384x_TXSTATUS_AGEDERR ((u16)BIT(1))
+#define HFA384x_TXSTATUS_RETRYERR ((u16)BIT(0))
+/*-- Transmit Control Field --*/
+#define HFA384x_TX_MACPORT ((u16)(BIT(10) | \
+ BIT(9) | BIT(8)))
+#define HFA384x_TX_STRUCTYPE ((u16)(BIT(4) | BIT(3)))
+#define HFA384x_TX_TXEX ((u16)BIT(2))
+#define HFA384x_TX_TXOK ((u16)BIT(1))
+/*--------------------------------------------------------------------
+Communication Frames: Test/Get/Set Field Values for Transmit Frames
+--------------------------------------------------------------------*/
+/*-- Status Field --*/
+#define HFA384x_TXSTATUS_ISERROR(v) \
+ (((u16)(v))&\
+ (HFA384x_TXSTATUS_ACKERR|HFA384x_TXSTATUS_FORMERR|\
+ HFA384x_TXSTATUS_DISCON|HFA384x_TXSTATUS_AGEDERR|\
+ HFA384x_TXSTATUS_RETRYERR))
+
+#define HFA384x_TX_SET(v, m, s) ((((u16)(v))<<((u16)(s)))&((u16)(m)))
+
+#define HFA384x_TX_MACPORT_SET(v) HFA384x_TX_SET(v, HFA384x_TX_MACPORT, 8)
+#define HFA384x_TX_STRUCTYPE_SET(v) HFA384x_TX_SET(v, \
+ HFA384x_TX_STRUCTYPE, 3)
+#define HFA384x_TX_TXEX_SET(v) HFA384x_TX_SET(v, HFA384x_TX_TXEX, 2)
+#define HFA384x_TX_TXOK_SET(v) HFA384x_TX_SET(v, HFA384x_TX_TXOK, 1)
+/*--------------------------------------------------------------------
+Communication Frames: Receive Frames
+--------------------------------------------------------------------*/
+/*-- Communication Frame: Receive Frame Structure --*/
+typedef struct hfa384x_rx_frame {
+ /*-- MAC rx descriptor (hfa384x byte order) --*/
+ u16 status;
+ u32 time;
+ u8 silence;
+ u8 signal;
+ u8 rate;
+ u8 rx_flow;
+ u16 reserved1;
+ u16 reserved2;
+
+ /*-- 802.11 Header Information (802.11 byte order) --*/
+ __le16 frame_control;
+ u16 duration_id;
+ u8 address1[6];
+ u8 address2[6];
+ u8 address3[6];
+ u16 sequence_control;
+ u8 address4[6];
+ __le16 data_len; /* hfa384x (little endian) format */
+
+ /*-- 802.3 Header Information --*/
+ u8 dest_addr[6];
+ u8 src_addr[6];
+ u16 data_length; /* IEEE? (big endian) format */
+} __packed hfa384x_rx_frame_t;
+/*--------------------------------------------------------------------
+Communication Frames: Field Masks for Receive Frames
+--------------------------------------------------------------------*/
+
+/*-- Status Fields --*/
+#define HFA384x_RXSTATUS_MACPORT ((u16)(BIT(10) | \
+ BIT(9) | \
+ BIT(8)))
+#define HFA384x_RXSTATUS_FCSERR ((u16)BIT(0))
+/*--------------------------------------------------------------------
+Communication Frames: Test/Get/Set Field Values for Receive Frames
+--------------------------------------------------------------------*/
+#define HFA384x_RXSTATUS_MACPORT_GET(value) ((u16)((((u16)(value)) \
+ & HFA384x_RXSTATUS_MACPORT) >> 8))
+#define HFA384x_RXSTATUS_ISFCSERR(value) ((u16)(((u16)(value)) \
+ & HFA384x_RXSTATUS_FCSERR))
+/*--------------------------------------------------------------------
+ FRAME STRUCTURES: Information Types and Information Frame Structures
+----------------------------------------------------------------------
+Information Types
+--------------------------------------------------------------------*/
+#define HFA384x_IT_HANDOVERADDR ((u16)0xF000UL)
+#define HFA384x_IT_COMMTALLIES ((u16)0xF100UL)
+#define HFA384x_IT_SCANRESULTS ((u16)0xF101UL)
+#define HFA384x_IT_CHINFORESULTS ((u16)0xF102UL)
+#define HFA384x_IT_HOSTSCANRESULTS ((u16)0xF103UL)
+#define HFA384x_IT_LINKSTATUS ((u16)0xF200UL)
+#define HFA384x_IT_ASSOCSTATUS ((u16)0xF201UL)
+#define HFA384x_IT_AUTHREQ ((u16)0xF202UL)
+#define HFA384x_IT_PSUSERCNT ((u16)0xF203UL)
+#define HFA384x_IT_KEYIDCHANGED ((u16)0xF204UL)
+#define HFA384x_IT_ASSOCREQ ((u16)0xF205UL)
+#define HFA384x_IT_MICFAILURE ((u16)0xF206UL)
+
+/*--------------------------------------------------------------------
+Information Frames Structures
+----------------------------------------------------------------------
+Information Frames: Notification Frame Structures
+--------------------------------------------------------------------*/
+
+/*-- Inquiry Frame, Diagnose: Communication Tallies --*/
+typedef struct hfa384x_CommTallies16 {
+ u16 txunicastframes;
+ u16 txmulticastframes;
+ u16 txfragments;
+ u16 txunicastoctets;
+ u16 txmulticastoctets;
+ u16 txdeferredtrans;
+ u16 txsingleretryframes;
+ u16 txmultipleretryframes;
+ u16 txretrylimitexceeded;
+ u16 txdiscards;
+ u16 rxunicastframes;
+ u16 rxmulticastframes;
+ u16 rxfragments;
+ u16 rxunicastoctets;
+ u16 rxmulticastoctets;
+ u16 rxfcserrors;
+ u16 rxdiscardsnobuffer;
+ u16 txdiscardswrongsa;
+ u16 rxdiscardswepundecr;
+ u16 rxmsginmsgfrag;
+ u16 rxmsginbadmsgfrag;
+} __packed hfa384x_CommTallies16_t;
+
+typedef struct hfa384x_CommTallies32 {
+ u32 txunicastframes;
+ u32 txmulticastframes;
+ u32 txfragments;
+ u32 txunicastoctets;
+ u32 txmulticastoctets;
+ u32 txdeferredtrans;
+ u32 txsingleretryframes;
+ u32 txmultipleretryframes;
+ u32 txretrylimitexceeded;
+ u32 txdiscards;
+ u32 rxunicastframes;
+ u32 rxmulticastframes;
+ u32 rxfragments;
+ u32 rxunicastoctets;
+ u32 rxmulticastoctets;
+ u32 rxfcserrors;
+ u32 rxdiscardsnobuffer;
+ u32 txdiscardswrongsa;
+ u32 rxdiscardswepundecr;
+ u32 rxmsginmsgfrag;
+ u32 rxmsginbadmsgfrag;
+} __packed hfa384x_CommTallies32_t;
+
+/*-- Inquiry Frame, Diagnose: Scan Results & Subfields--*/
+typedef struct hfa384x_ScanResultSub {
+ u16 chid;
+ u16 anl;
+ u16 sl;
+ u8 bssid[WLAN_BSSID_LEN];
+ u16 bcnint;
+ u16 capinfo;
+ hfa384x_bytestr32_t ssid;
+ u8 supprates[10]; /* 802.11 info element */
+ u16 proberesp_rate;
+} __packed hfa384x_ScanResultSub_t;
+
+typedef struct hfa384x_ScanResult {
+ u16 rsvd;
+ u16 scanreason;
+ hfa384x_ScanResultSub_t result[HFA384x_SCANRESULT_MAX];
+} __packed hfa384x_ScanResult_t;
+
+/*-- Inquiry Frame, Diagnose: ChInfo Results & Subfields--*/
+typedef struct hfa384x_ChInfoResultSub {
+ u16 chid;
+ u16 anl;
+ u16 pnl;
+ u16 active;
+} __packed hfa384x_ChInfoResultSub_t;
+
+#define HFA384x_CHINFORESULT_BSSACTIVE BIT(0)
+#define HFA384x_CHINFORESULT_PCFACTIVE BIT(1)
+
+typedef struct hfa384x_ChInfoResult {
+ u16 scanchannels;
+ hfa384x_ChInfoResultSub_t result[HFA384x_CHINFORESULT_MAX];
+} __packed hfa384x_ChInfoResult_t;
+
+/*-- Inquiry Frame, Diagnose: Host Scan Results & Subfields--*/
+typedef struct hfa384x_HScanResultSub {
+ u16 chid;
+ u16 anl;
+ u16 sl;
+ u8 bssid[WLAN_BSSID_LEN];
+ u16 bcnint;
+ u16 capinfo;
+ hfa384x_bytestr32_t ssid;
+ u8 supprates[10]; /* 802.11 info element */
+ u16 proberesp_rate;
+ u16 atim;
+} __packed hfa384x_HScanResultSub_t;
+
+typedef struct hfa384x_HScanResult {
+ u16 nresult;
+ u16 rsvd;
+ hfa384x_HScanResultSub_t result[HFA384x_HSCANRESULT_MAX];
+} __packed hfa384x_HScanResult_t;
+
+/*-- Unsolicited Frame, MAC Mgmt: LinkStatus --*/
+
+#define HFA384x_LINK_NOTCONNECTED ((u16)0)
+#define HFA384x_LINK_CONNECTED ((u16)1)
+#define HFA384x_LINK_DISCONNECTED ((u16)2)
+#define HFA384x_LINK_AP_CHANGE ((u16)3)
+#define HFA384x_LINK_AP_OUTOFRANGE ((u16)4)
+#define HFA384x_LINK_AP_INRANGE ((u16)5)
+#define HFA384x_LINK_ASSOCFAIL ((u16)6)
+
+typedef struct hfa384x_LinkStatus {
+ u16 linkstatus;
+} __packed hfa384x_LinkStatus_t;
+
+/*-- Unsolicited Frame, MAC Mgmt: AssociationStatus (--*/
+
+#define HFA384x_ASSOCSTATUS_STAASSOC ((u16)1)
+#define HFA384x_ASSOCSTATUS_REASSOC ((u16)2)
+#define HFA384x_ASSOCSTATUS_AUTHFAIL ((u16)5)
+
+typedef struct hfa384x_AssocStatus {
+ u16 assocstatus;
+ u8 sta_addr[ETH_ALEN];
+ /* old_ap_addr is only valid if assocstatus == 2 */
+ u8 old_ap_addr[ETH_ALEN];
+ u16 reason;
+ u16 reserved;
+} __packed hfa384x_AssocStatus_t;
+
+/*-- Unsolicited Frame, MAC Mgmt: AuthRequest (AP Only) --*/
+
+typedef struct hfa384x_AuthRequest {
+ u8 sta_addr[ETH_ALEN];
+ u16 algorithm;
+} __packed hfa384x_AuthReq_t;
+
+/*-- Unsolicited Frame, MAC Mgmt: PSUserCount (AP Only) --*/
+
+typedef struct hfa384x_PSUserCount {
+ u16 usercnt;
+} __packed hfa384x_PSUserCount_t;
+
+typedef struct hfa384x_KeyIDChanged {
+ u8 sta_addr[ETH_ALEN];
+ u16 keyid;
+} __packed hfa384x_KeyIDChanged_t;
+
+/*-- Collection of all Inf frames ---------------*/
+typedef union hfa384x_infodata {
+ hfa384x_CommTallies16_t commtallies16;
+ hfa384x_CommTallies32_t commtallies32;
+ hfa384x_ScanResult_t scanresult;
+ hfa384x_ChInfoResult_t chinforesult;
+ hfa384x_HScanResult_t hscanresult;
+ hfa384x_LinkStatus_t linkstatus;
+ hfa384x_AssocStatus_t assocstatus;
+ hfa384x_AuthReq_t authreq;
+ hfa384x_PSUserCount_t psusercnt;
+ hfa384x_KeyIDChanged_t keyidchanged;
+} __packed hfa384x_infodata_t;
+
+typedef struct hfa384x_InfFrame {
+ u16 framelen;
+ u16 infotype;
+ hfa384x_infodata_t info;
+} __packed hfa384x_InfFrame_t;
+
+/*--------------------------------------------------------------------
+USB Packet structures and constants.
+--------------------------------------------------------------------*/
+
+/* Should be sent to the bulkout endpoint */
+#define HFA384x_USB_TXFRM 0
+#define HFA384x_USB_CMDREQ 1
+#define HFA384x_USB_WRIDREQ 2
+#define HFA384x_USB_RRIDREQ 3
+#define HFA384x_USB_WMEMREQ 4
+#define HFA384x_USB_RMEMREQ 5
+
+/* Received from the bulkin endpoint */
+#define HFA384x_USB_ISTXFRM(a) (((a) & 0x9000) == 0x1000)
+#define HFA384x_USB_ISRXFRM(a) (!((a) & 0x9000))
+#define HFA384x_USB_INFOFRM 0x8000
+#define HFA384x_USB_CMDRESP 0x8001
+#define HFA384x_USB_WRIDRESP 0x8002
+#define HFA384x_USB_RRIDRESP 0x8003
+#define HFA384x_USB_WMEMRESP 0x8004
+#define HFA384x_USB_RMEMRESP 0x8005
+#define HFA384x_USB_BUFAVAIL 0x8006
+#define HFA384x_USB_ERROR 0x8007
+
+/*------------------------------------*/
+/* Request (bulk OUT) packet contents */
+
+typedef struct hfa384x_usb_txfrm {
+ hfa384x_tx_frame_t desc;
+ u8 data[WLAN_DATA_MAXLEN];
+} __packed hfa384x_usb_txfrm_t;
+
+typedef struct hfa384x_usb_cmdreq {
+ u16 type;
+ u16 cmd;
+ u16 parm0;
+ u16 parm1;
+ u16 parm2;
+ u8 pad[54];
+} __packed hfa384x_usb_cmdreq_t;
+
+typedef struct hfa384x_usb_wridreq {
+ u16 type;
+ u16 frmlen;
+ u16 rid;
+ u8 data[HFA384x_RIDDATA_MAXLEN];
+} __packed hfa384x_usb_wridreq_t;
+
+typedef struct hfa384x_usb_rridreq {
+ u16 type;
+ u16 frmlen;
+ u16 rid;
+ u8 pad[58];
+} __packed hfa384x_usb_rridreq_t;
+
+typedef struct hfa384x_usb_wmemreq {
+ u16 type;
+ u16 frmlen;
+ u16 offset;
+ u16 page;
+ u8 data[HFA384x_USB_RWMEM_MAXLEN];
+} __packed hfa384x_usb_wmemreq_t;
+
+typedef struct hfa384x_usb_rmemreq {
+ u16 type;
+ u16 frmlen;
+ u16 offset;
+ u16 page;
+ u8 pad[56];
+} __packed hfa384x_usb_rmemreq_t;
+
+/*------------------------------------*/
+/* Response (bulk IN) packet contents */
+
+typedef struct hfa384x_usb_rxfrm {
+ hfa384x_rx_frame_t desc;
+ u8 data[WLAN_DATA_MAXLEN];
+} __packed hfa384x_usb_rxfrm_t;
+
+typedef struct hfa384x_usb_infofrm {
+ u16 type;
+ hfa384x_InfFrame_t info;
+} __packed hfa384x_usb_infofrm_t;
+
+typedef struct hfa384x_usb_statusresp {
+ u16 type;
+ u16 status;
+ u16 resp0;
+ u16 resp1;
+ u16 resp2;
+} __packed hfa384x_usb_cmdresp_t;
+
+typedef hfa384x_usb_cmdresp_t hfa384x_usb_wridresp_t;
+
+typedef struct hfa384x_usb_rridresp {
+ u16 type;
+ u16 frmlen;
+ u16 rid;
+ u8 data[HFA384x_RIDDATA_MAXLEN];
+} __packed hfa384x_usb_rridresp_t;
+
+typedef hfa384x_usb_cmdresp_t hfa384x_usb_wmemresp_t;
+
+typedef struct hfa384x_usb_rmemresp {
+ u16 type;
+ u16 frmlen;
+ u8 data[HFA384x_USB_RWMEM_MAXLEN];
+} __packed hfa384x_usb_rmemresp_t;
+
+typedef struct hfa384x_usb_bufavail {
+ u16 type;
+ u16 frmlen;
+} __packed hfa384x_usb_bufavail_t;
+
+typedef struct hfa384x_usb_error {
+ u16 type;
+ u16 errortype;
+} __packed hfa384x_usb_error_t;
+
+/*----------------------------------------------------------*/
+/* Unions for packaging all the known packet types together */
+
+typedef union hfa384x_usbout {
+ __le16 type;
+ hfa384x_usb_txfrm_t txfrm;
+ hfa384x_usb_cmdreq_t cmdreq;
+ hfa384x_usb_wridreq_t wridreq;
+ hfa384x_usb_rridreq_t rridreq;
+ hfa384x_usb_wmemreq_t wmemreq;
+ hfa384x_usb_rmemreq_t rmemreq;
+} __packed hfa384x_usbout_t;
+
+typedef union hfa384x_usbin {
+ __le16 type;
+ hfa384x_usb_rxfrm_t rxfrm;
+ hfa384x_usb_txfrm_t txfrm;
+ hfa384x_usb_infofrm_t infofrm;
+ hfa384x_usb_cmdresp_t cmdresp;
+ hfa384x_usb_wridresp_t wridresp;
+ hfa384x_usb_rridresp_t rridresp;
+ hfa384x_usb_wmemresp_t wmemresp;
+ hfa384x_usb_rmemresp_t rmemresp;
+ hfa384x_usb_bufavail_t bufavail;
+ hfa384x_usb_error_t usberror;
+ u8 boguspad[3000];
+} __packed hfa384x_usbin_t;
+
+/*--------------------------------------------------------------------
+PD record structures.
+--------------------------------------------------------------------*/
+
+typedef struct hfa384x_pdr_pcb_partnum {
+ u8 num[8];
+} __packed hfa384x_pdr_pcb_partnum_t;
+
+typedef struct hfa384x_pdr_pcb_tracenum {
+ u8 num[8];
+} __packed hfa384x_pdr_pcb_tracenum_t;
+
+typedef struct hfa384x_pdr_nic_serial {
+ u8 num[12];
+} __packed hfa384x_pdr_nic_serial_t;
+
+typedef struct hfa384x_pdr_mkk_measurements {
+ double carrier_freq;
+ double occupied_band;
+ double power_density;
+ double tx_spur_f1;
+ double tx_spur_f2;
+ double tx_spur_f3;
+ double tx_spur_f4;
+ double tx_spur_l1;
+ double tx_spur_l2;
+ double tx_spur_l3;
+ double tx_spur_l4;
+ double rx_spur_f1;
+ double rx_spur_f2;
+ double rx_spur_l1;
+ double rx_spur_l2;
+} __packed hfa384x_pdr_mkk_measurements_t;
+
+typedef struct hfa384x_pdr_nic_ramsize {
+ u8 size[12]; /* units of KB */
+} __packed hfa384x_pdr_nic_ramsize_t;
+
+typedef struct hfa384x_pdr_mfisuprange {
+ u16 id;
+ u16 variant;
+ u16 bottom;
+ u16 top;
+} __packed hfa384x_pdr_mfisuprange_t;
+
+typedef struct hfa384x_pdr_cfisuprange {
+ u16 id;
+ u16 variant;
+ u16 bottom;
+ u16 top;
+} __packed hfa384x_pdr_cfisuprange_t;
+
+typedef struct hfa384x_pdr_nicid {
+ u16 id;
+ u16 variant;
+ u16 major;
+ u16 minor;
+} __packed hfa384x_pdr_nicid_t;
+
+typedef struct hfa384x_pdr_refdac_measurements {
+ u16 value[0];
+} __packed hfa384x_pdr_refdac_measurements_t;
+
+typedef struct hfa384x_pdr_vgdac_measurements {
+ u16 value[0];
+} __packed hfa384x_pdr_vgdac_measurements_t;
+
+typedef struct hfa384x_pdr_level_comp_measurements {
+ u16 value[0];
+} __packed hfa384x_pdr_level_compc_measurements_t;
+
+typedef struct hfa384x_pdr_mac_address {
+ u8 addr[6];
+} __packed hfa384x_pdr_mac_address_t;
+
+typedef struct hfa384x_pdr_mkk_callname {
+ u8 callname[8];
+} __packed hfa384x_pdr_mkk_callname_t;
+
+typedef struct hfa384x_pdr_regdomain {
+ u16 numdomains;
+ u16 domain[5];
+} __packed hfa384x_pdr_regdomain_t;
+
+typedef struct hfa384x_pdr_allowed_channel {
+ u16 ch_bitmap;
+} __packed hfa384x_pdr_allowed_channel_t;
+
+typedef struct hfa384x_pdr_default_channel {
+ u16 channel;
+} __packed hfa384x_pdr_default_channel_t;
+
+typedef struct hfa384x_pdr_privacy_option {
+ u16 available;
+} __packed hfa384x_pdr_privacy_option_t;
+
+typedef struct hfa384x_pdr_temptype {
+ u16 type;
+} __packed hfa384x_pdr_temptype_t;
+
+typedef struct hfa384x_pdr_refdac_setup {
+ u16 ch_value[14];
+} __packed hfa384x_pdr_refdac_setup_t;
+
+typedef struct hfa384x_pdr_vgdac_setup {
+ u16 ch_value[14];
+} __packed hfa384x_pdr_vgdac_setup_t;
+
+typedef struct hfa384x_pdr_level_comp_setup {
+ u16 ch_value[14];
+} __packed hfa384x_pdr_level_comp_setup_t;
+
+typedef struct hfa384x_pdr_trimdac_setup {
+ u16 trimidac;
+ u16 trimqdac;
+} __packed hfa384x_pdr_trimdac_setup_t;
+
+typedef struct hfa384x_pdr_ifr_setting {
+ u16 value[3];
+} __packed hfa384x_pdr_ifr_setting_t;
+
+typedef struct hfa384x_pdr_rfr_setting {
+ u16 value[3];
+} __packed hfa384x_pdr_rfr_setting_t;
+
+typedef struct hfa384x_pdr_hfa3861_baseline {
+ u16 value[50];
+} __packed hfa384x_pdr_hfa3861_baseline_t;
+
+typedef struct hfa384x_pdr_hfa3861_shadow {
+ u32 value[32];
+} __packed hfa384x_pdr_hfa3861_shadow_t;
+
+typedef struct hfa384x_pdr_hfa3861_ifrf {
+ u32 value[20];
+} __packed hfa384x_pdr_hfa3861_ifrf_t;
+
+typedef struct hfa384x_pdr_hfa3861_chcalsp {
+ u16 value[14];
+} __packed hfa384x_pdr_hfa3861_chcalsp_t;
+
+typedef struct hfa384x_pdr_hfa3861_chcali {
+ u16 value[17];
+} __packed hfa384x_pdr_hfa3861_chcali_t;
+
+typedef struct hfa384x_pdr_hfa3861_nic_config {
+ u16 config_bitmap;
+} __packed hfa384x_pdr_nic_config_t;
+
+typedef struct hfa384x_pdr_hfo_delay {
+ u8 hfo_delay;
+} __packed hfa384x_hfo_delay_t;
+
+typedef struct hfa384x_pdr_hfa3861_manf_testsp {
+ u16 value[30];
+} __packed hfa384x_pdr_hfa3861_manf_testsp_t;
+
+typedef struct hfa384x_pdr_hfa3861_manf_testi {
+ u16 value[30];
+} __packed hfa384x_pdr_hfa3861_manf_testi_t;
+
+typedef struct hfa384x_end_of_pda {
+ u16 crc;
+} __packed hfa384x_pdr_end_of_pda_t;
+
+typedef struct hfa384x_pdrec {
+ u16 len; /* in words */
+ u16 code;
+ union pdr {
+ hfa384x_pdr_pcb_partnum_t pcb_partnum;
+ hfa384x_pdr_pcb_tracenum_t pcb_tracenum;
+ hfa384x_pdr_nic_serial_t nic_serial;
+ hfa384x_pdr_mkk_measurements_t mkk_measurements;
+ hfa384x_pdr_nic_ramsize_t nic_ramsize;
+ hfa384x_pdr_mfisuprange_t mfisuprange;
+ hfa384x_pdr_cfisuprange_t cfisuprange;
+ hfa384x_pdr_nicid_t nicid;
+ hfa384x_pdr_refdac_measurements_t refdac_measurements;
+ hfa384x_pdr_vgdac_measurements_t vgdac_measurements;
+ hfa384x_pdr_level_compc_measurements_t level_compc_measurements;
+ hfa384x_pdr_mac_address_t mac_address;
+ hfa384x_pdr_mkk_callname_t mkk_callname;
+ hfa384x_pdr_regdomain_t regdomain;
+ hfa384x_pdr_allowed_channel_t allowed_channel;
+ hfa384x_pdr_default_channel_t default_channel;
+ hfa384x_pdr_privacy_option_t privacy_option;
+ hfa384x_pdr_temptype_t temptype;
+ hfa384x_pdr_refdac_setup_t refdac_setup;
+ hfa384x_pdr_vgdac_setup_t vgdac_setup;
+ hfa384x_pdr_level_comp_setup_t level_comp_setup;
+ hfa384x_pdr_trimdac_setup_t trimdac_setup;
+ hfa384x_pdr_ifr_setting_t ifr_setting;
+ hfa384x_pdr_rfr_setting_t rfr_setting;
+ hfa384x_pdr_hfa3861_baseline_t hfa3861_baseline;
+ hfa384x_pdr_hfa3861_shadow_t hfa3861_shadow;
+ hfa384x_pdr_hfa3861_ifrf_t hfa3861_ifrf;
+ hfa384x_pdr_hfa3861_chcalsp_t hfa3861_chcalsp;
+ hfa384x_pdr_hfa3861_chcali_t hfa3861_chcali;
+ hfa384x_pdr_nic_config_t nic_config;
+ hfa384x_hfo_delay_t hfo_delay;
+ hfa384x_pdr_hfa3861_manf_testsp_t hfa3861_manf_testsp;
+ hfa384x_pdr_hfa3861_manf_testi_t hfa3861_manf_testi;
+ hfa384x_pdr_end_of_pda_t end_of_pda;
+
+ } data;
+} __packed hfa384x_pdrec_t;
+
+#ifdef __KERNEL__
+/*--------------------------------------------------------------------
+--- MAC state structure, argument to all functions --
+--- Also, a collection of support types --
+--------------------------------------------------------------------*/
+typedef struct hfa384x_statusresult {
+ u16 status;
+ u16 resp0;
+ u16 resp1;
+ u16 resp2;
+} hfa384x_cmdresult_t;
+
+/* USB Control Exchange (CTLX):
+ * A queue of the structure below is maintained for all of the
+ * Request/Response type USB packets supported by Prism2.
+ */
+/* The following hfa384x_* structures are arguments to
+ * the usercb() for the different CTLX types.
+ */
+typedef struct hfa384x_rridresult {
+ u16 rid;
+ const void *riddata;
+ unsigned int riddata_len;
+} hfa384x_rridresult_t;
+
+enum ctlx_state {
+ CTLX_START = 0, /* Start state, not queued */
+
+ CTLX_COMPLETE, /* CTLX successfully completed */
+ CTLX_REQ_FAILED, /* OUT URB completed w/ error */
+
+ CTLX_PENDING, /* Queued, data valid */
+ CTLX_REQ_SUBMITTED, /* OUT URB submitted */
+ CTLX_REQ_COMPLETE, /* OUT URB complete */
+ CTLX_RESP_COMPLETE /* IN URB received */
+};
+typedef enum ctlx_state CTLX_STATE;
+
+struct hfa384x_usbctlx;
+struct hfa384x;
+
+typedef void (*ctlx_cmdcb_t) (struct hfa384x *, const struct hfa384x_usbctlx *);
+
+typedef void (*ctlx_usercb_t) (struct hfa384x *hw,
+ void *ctlxresult, void *usercb_data);
+
+typedef struct hfa384x_usbctlx {
+ struct list_head list;
+
+ size_t outbufsize;
+ hfa384x_usbout_t outbuf; /* pkt buf for OUT */
+ hfa384x_usbin_t inbuf; /* pkt buf for IN(a copy) */
+
+ CTLX_STATE state; /* Tracks running state */
+
+ struct completion done;
+ volatile int reapable; /* Food for the reaper task */
+
+ ctlx_cmdcb_t cmdcb; /* Async command callback */
+ ctlx_usercb_t usercb; /* Async user callback, */
+ void *usercb_data; /* at CTLX completion */
+
+ int variant; /* Identifies cmd variant */
+} hfa384x_usbctlx_t;
+
+typedef struct hfa384x_usbctlxq {
+ spinlock_t lock;
+ struct list_head pending;
+ struct list_head active;
+ struct list_head completing;
+ struct list_head reapable;
+} hfa384x_usbctlxq_t;
+
+typedef struct hfa484x_metacmd {
+ u16 cmd;
+
+ u16 parm0;
+ u16 parm1;
+ u16 parm2;
+
+ hfa384x_cmdresult_t result;
+} hfa384x_metacmd_t;
+
+#define MAX_GRP_ADDR 32
+#define WLAN_COMMENT_MAX 80 /* Max. length of user comment string. */
+
+#define WLAN_AUTH_MAX 60 /* Max. # of authenticated stations. */
+#define WLAN_ACCESS_MAX 60 /* Max. # of stations in an access list. */
+#define WLAN_ACCESS_NONE 0 /* No stations may be authenticated. */
+#define WLAN_ACCESS_ALL 1 /* All stations may be authenticated. */
+#define WLAN_ACCESS_ALLOW 2 /* Authenticate only "allowed" stations. */
+#define WLAN_ACCESS_DENY 3 /* Do not authenticate "denied" stations. */
+
+/* XXX These are going away ASAP */
+struct prism2sta_authlist {
+ unsigned int cnt;
+ u8 addr[WLAN_AUTH_MAX][ETH_ALEN];
+ u8 assoc[WLAN_AUTH_MAX];
+};
+
+struct prism2sta_accesslist {
+ unsigned int modify;
+ unsigned int cnt;
+ u8 addr[WLAN_ACCESS_MAX][ETH_ALEN];
+ unsigned int cnt1;
+ u8 addr1[WLAN_ACCESS_MAX][ETH_ALEN];
+};
+
+typedef struct hfa384x {
+ /* USB support data */
+ struct usb_device *usb;
+ struct urb rx_urb;
+ struct sk_buff *rx_urb_skb;
+ struct urb tx_urb;
+ struct urb ctlx_urb;
+ hfa384x_usbout_t txbuff;
+ hfa384x_usbctlxq_t ctlxq;
+ struct timer_list reqtimer;
+ struct timer_list resptimer;
+
+ struct timer_list throttle;
+
+ struct tasklet_struct reaper_bh;
+ struct tasklet_struct completion_bh;
+
+ struct work_struct usb_work;
+
+ unsigned long usb_flags;
+#define THROTTLE_RX 0
+#define THROTTLE_TX 1
+#define WORK_RX_HALT 2
+#define WORK_TX_HALT 3
+#define WORK_RX_RESUME 4
+#define WORK_TX_RESUME 5
+
+ unsigned short req_timer_done:1;
+ unsigned short resp_timer_done:1;
+
+ int endp_in;
+ int endp_out;
+
+ int sniff_fcs;
+ int sniff_channel;
+ int sniff_truncate;
+ int sniffhdr;
+
+ wait_queue_head_t cmdq; /* wait queue itself */
+
+ /* Controller state */
+ u32 state;
+ u32 isap;
+ u8 port_enabled[HFA384x_NUMPORTS_MAX];
+
+ /* Download support */
+ unsigned int dlstate;
+ hfa384x_downloadbuffer_t bufinfo;
+ u16 dltimeout;
+
+ int scanflag; /* to signal scan complete */
+ int join_ap; /* are we joined to a specific ap */
+ int join_retries; /* number of join retries till we fail */
+ hfa384x_JoinRequest_data_t joinreq; /* join request saved data */
+
+ wlandevice_t *wlandev;
+ /* Timer to allow for the deferred processing of linkstatus messages */
+ struct work_struct link_bh;
+
+ struct work_struct commsqual_bh;
+ hfa384x_commsquality_t qual;
+ struct timer_list commsqual_timer;
+
+ u16 link_status;
+ u16 link_status_new;
+ struct sk_buff_head authq;
+
+ u32 txrate;
+
+ /* And here we have stuff that used to be in priv */
+
+ /* State variables */
+ unsigned int presniff_port_type;
+ u16 presniff_wepflags;
+ u32 dot11_desired_bss_type;
+
+ int dbmadjust;
+
+ /* Group Addresses - right now, there are up to a total
+ of MAX_GRP_ADDR group addresses */
+ u8 dot11_grp_addr[MAX_GRP_ADDR][ETH_ALEN];
+ unsigned int dot11_grpcnt;
+
+ /* Component Identities */
+ hfa384x_compident_t ident_nic;
+ hfa384x_compident_t ident_pri_fw;
+ hfa384x_compident_t ident_sta_fw;
+ hfa384x_compident_t ident_ap_fw;
+ u16 mm_mods;
+
+ /* Supplier compatibility ranges */
+ hfa384x_caplevel_t cap_sup_mfi;
+ hfa384x_caplevel_t cap_sup_cfi;
+ hfa384x_caplevel_t cap_sup_pri;
+ hfa384x_caplevel_t cap_sup_sta;
+ hfa384x_caplevel_t cap_sup_ap;
+
+ /* Actor compatibility ranges */
+ hfa384x_caplevel_t cap_act_pri_cfi; /*
+ * pri f/w to controller
+ * interface
+ */
+
+ hfa384x_caplevel_t cap_act_sta_cfi; /*
+ * sta f/w to controller
+ * interface
+ */
+
+ hfa384x_caplevel_t cap_act_sta_mfi; /* sta f/w to modem interface */
+
+ hfa384x_caplevel_t cap_act_ap_cfi; /*
+ * ap f/w to controller
+ * interface
+ */
+
+ hfa384x_caplevel_t cap_act_ap_mfi; /* ap f/w to modem interface */
+
+ u32 psusercount; /* Power save user count. */
+ hfa384x_CommTallies32_t tallies; /* Communication tallies. */
+ u8 comment[WLAN_COMMENT_MAX + 1]; /* User comment */
+
+ /* Channel Info request results (AP only) */
+ struct {
+ atomic_t done;
+ u8 count;
+ hfa384x_ChInfoResult_t results;
+ } channel_info;
+
+ hfa384x_InfFrame_t *scanresults;
+
+ struct prism2sta_authlist authlist; /* Authenticated station list. */
+ unsigned int accessmode; /* Access mode. */
+ struct prism2sta_accesslist allow; /* Allowed station list. */
+ struct prism2sta_accesslist deny; /* Denied station list. */
+
+} hfa384x_t;
+
+void hfa384x_create(hfa384x_t *hw, struct usb_device *usb);
+void hfa384x_destroy(hfa384x_t *hw);
+
+int
+hfa384x_corereset(hfa384x_t *hw, int holdtime, int settletime, int genesis);
+int hfa384x_drvr_commtallies(hfa384x_t *hw);
+int hfa384x_drvr_disable(hfa384x_t *hw, u16 macport);
+int hfa384x_drvr_enable(hfa384x_t *hw, u16 macport);
+int hfa384x_drvr_flashdl_enable(hfa384x_t *hw);
+int hfa384x_drvr_flashdl_disable(hfa384x_t *hw);
+int hfa384x_drvr_flashdl_write(hfa384x_t *hw, u32 daddr, void *buf, u32 len);
+int hfa384x_drvr_getconfig(hfa384x_t *hw, u16 rid, void *buf, u16 len);
+int hfa384x_drvr_ramdl_enable(hfa384x_t *hw, u32 exeaddr);
+int hfa384x_drvr_ramdl_disable(hfa384x_t *hw);
+int hfa384x_drvr_ramdl_write(hfa384x_t *hw, u32 daddr, void *buf, u32 len);
+int hfa384x_drvr_readpda(hfa384x_t *hw, void *buf, unsigned int len);
+int hfa384x_drvr_setconfig(hfa384x_t *hw, u16 rid, void *buf, u16 len);
+
+static inline int hfa384x_drvr_getconfig16(hfa384x_t *hw, u16 rid, void *val)
+{
+ int result = 0;
+
+ result = hfa384x_drvr_getconfig(hw, rid, val, sizeof(u16));
+ if (result == 0)
+ *((u16 *) val) = le16_to_cpu(*((u16 *) val));
+ return result;
+}
+
+static inline int hfa384x_drvr_setconfig16(hfa384x_t *hw, u16 rid, u16 val)
+{
+ u16 value = cpu_to_le16(val);
+
+ return hfa384x_drvr_setconfig(hw, rid, &value, sizeof(value));
+}
+
+int
+hfa384x_drvr_getconfig_async(hfa384x_t *hw,
+ u16 rid, ctlx_usercb_t usercb, void *usercb_data);
+
+int
+hfa384x_drvr_setconfig_async(hfa384x_t *hw,
+ u16 rid,
+ void *buf,
+ u16 len, ctlx_usercb_t usercb, void *usercb_data);
+
+static inline int
+hfa384x_drvr_setconfig16_async(hfa384x_t *hw, u16 rid, u16 val)
+{
+ u16 value = cpu_to_le16(val);
+
+ return hfa384x_drvr_setconfig_async(hw, rid, &value, sizeof(value),
+ NULL, NULL);
+}
+
+int hfa384x_drvr_start(hfa384x_t *hw);
+int hfa384x_drvr_stop(hfa384x_t *hw);
+int
+hfa384x_drvr_txframe(hfa384x_t *hw, struct sk_buff *skb,
+ union p80211_hdr *p80211_hdr,
+ struct p80211_metawep *p80211_wep);
+void hfa384x_tx_timeout(wlandevice_t *wlandev);
+
+int hfa384x_cmd_initialize(hfa384x_t *hw);
+int hfa384x_cmd_enable(hfa384x_t *hw, u16 macport);
+int hfa384x_cmd_disable(hfa384x_t *hw, u16 macport);
+int hfa384x_cmd_allocate(hfa384x_t *hw, u16 len);
+int hfa384x_cmd_monitor(hfa384x_t *hw, u16 enable);
+int
+hfa384x_cmd_download(hfa384x_t *hw,
+ u16 mode, u16 lowaddr, u16 highaddr, u16 codelen);
+
+#endif /*__KERNEL__ */
+
+#endif /*_HFA384x_H */
diff --git a/drivers/staging/wlan-ng/hfa384x_usb.c b/drivers/staging/wlan-ng/hfa384x_usb.c
new file mode 100644
index 000000000..e109a7fd4
--- /dev/null
+++ b/drivers/staging/wlan-ng/hfa384x_usb.c
@@ -0,0 +1,4127 @@
+/* src/prism2/driver/hfa384x_usb.c
+*
+* Functions that talk to the USB variantof the Intersil hfa384x MAC
+*
+* Copyright (C) 1999 AbsoluteValue Systems, Inc. All Rights Reserved.
+* --------------------------------------------------------------------
+*
+* linux-wlan
+*
+* The contents of this file are subject to the Mozilla Public
+* License Version 1.1 (the "License"); you may not use this file
+* except in compliance with the License. You may obtain a copy of
+* the License at http://www.mozilla.org/MPL/
+*
+* Software distributed under the License is distributed on an "AS
+* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+* implied. See the License for the specific language governing
+* rights and limitations under the License.
+*
+* Alternatively, the contents of this file may be used under the
+* terms of the GNU Public License version 2 (the "GPL"), in which
+* case the provisions of the GPL are applicable instead of the
+* above. If you wish to allow the use of your version of this file
+* only under the terms of the GPL and not to allow others to use
+* your version of this file under the MPL, indicate your decision
+* by deleting the provisions above and replace them with the notice
+* and other provisions required by the GPL. If you do not delete
+* the provisions above, a recipient may use your version of this
+* file under either the MPL or the GPL.
+*
+* --------------------------------------------------------------------
+*
+* Inquiries regarding the linux-wlan Open Source project can be
+* made directly to:
+*
+* AbsoluteValue Systems Inc.
+* info@linux-wlan.com
+* http://www.linux-wlan.com
+*
+* --------------------------------------------------------------------
+*
+* Portions of the development of this software were funded by
+* Intersil Corporation as part of PRISM(R) chipset product development.
+*
+* --------------------------------------------------------------------
+*
+* This file implements functions that correspond to the prism2/hfa384x
+* 802.11 MAC hardware and firmware host interface.
+*
+* The functions can be considered to represent several levels of
+* abstraction. The lowest level functions are simply C-callable wrappers
+* around the register accesses. The next higher level represents C-callable
+* prism2 API functions that match the Intersil documentation as closely
+* as is reasonable. The next higher layer implements common sequences
+* of invocations of the API layer (e.g. write to bap, followed by cmd).
+*
+* Common sequences:
+* hfa384x_drvr_xxx Highest level abstractions provided by the
+* hfa384x code. They are driver defined wrappers
+* for common sequences. These functions generally
+* use the services of the lower levels.
+*
+* hfa384x_drvr_xxxconfig An example of the drvr level abstraction. These
+* functions are wrappers for the RID get/set
+* sequence. They call copy_[to|from]_bap() and
+* cmd_access(). These functions operate on the
+* RIDs and buffers without validation. The caller
+* is responsible for that.
+*
+* API wrapper functions:
+* hfa384x_cmd_xxx functions that provide access to the f/w commands.
+* The function arguments correspond to each command
+* argument, even command arguments that get packed
+* into single registers. These functions _just_
+* issue the command by setting the cmd/parm regs
+* & reading the status/resp regs. Additional
+* activities required to fully use a command
+* (read/write from/to bap, get/set int status etc.)
+* are implemented separately. Think of these as
+* C-callable prism2 commands.
+*
+* Lowest Layer Functions:
+* hfa384x_docmd_xxx These functions implement the sequence required
+* to issue any prism2 command. Primarily used by the
+* hfa384x_cmd_xxx functions.
+*
+* hfa384x_bap_xxx BAP read/write access functions.
+* Note: we usually use BAP0 for non-interrupt context
+* and BAP1 for interrupt context.
+*
+* hfa384x_dl_xxx download related functions.
+*
+* Driver State Issues:
+* Note that there are two pairs of functions that manage the
+* 'initialized' and 'running' states of the hw/MAC combo. The four
+* functions are create(), destroy(), start(), and stop(). create()
+* sets up the data structures required to support the hfa384x_*
+* functions and destroy() cleans them up. The start() function gets
+* the actual hardware running and enables the interrupts. The stop()
+* function shuts the hardware down. The sequence should be:
+* create()
+* start()
+* .
+* . Do interesting things w/ the hardware
+* .
+* stop()
+* destroy()
+*
+* Note that destroy() can be called without calling stop() first.
+* --------------------------------------------------------------------
+*/
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/types.h>
+#include <linux/slab.h>
+#include <linux/wireless.h>
+#include <linux/netdevice.h>
+#include <linux/timer.h>
+#include <linux/io.h>
+#include <linux/delay.h>
+#include <asm/byteorder.h>
+#include <linux/bitops.h>
+#include <linux/list.h>
+#include <linux/usb.h>
+#include <linux/byteorder/generic.h>
+
+#define SUBMIT_URB(u, f) usb_submit_urb(u, f)
+
+#include "p80211types.h"
+#include "p80211hdr.h"
+#include "p80211mgmt.h"
+#include "p80211conv.h"
+#include "p80211msg.h"
+#include "p80211netdev.h"
+#include "p80211req.h"
+#include "p80211metadef.h"
+#include "p80211metastruct.h"
+#include "hfa384x.h"
+#include "prism2mgmt.h"
+
+enum cmd_mode {
+ DOWAIT = 0,
+ DOASYNC
+};
+
+#define THROTTLE_JIFFIES (HZ/8)
+#define URB_ASYNC_UNLINK 0
+#define USB_QUEUE_BULK 0
+
+#define ROUNDUP64(a) (((a)+63)&~63)
+
+#ifdef DEBUG_USB
+static void dbprint_urb(struct urb *urb);
+#endif
+
+static void
+hfa384x_int_rxmonitor(wlandevice_t *wlandev, hfa384x_usb_rxfrm_t *rxfrm);
+
+static void hfa384x_usb_defer(struct work_struct *data);
+
+static int submit_rx_urb(hfa384x_t *hw, gfp_t flags);
+
+static int submit_tx_urb(hfa384x_t *hw, struct urb *tx_urb, gfp_t flags);
+
+/*---------------------------------------------------*/
+/* Callbacks */
+static void hfa384x_usbout_callback(struct urb *urb);
+static void hfa384x_ctlxout_callback(struct urb *urb);
+static void hfa384x_usbin_callback(struct urb *urb);
+
+static void
+hfa384x_usbin_txcompl(wlandevice_t *wlandev, hfa384x_usbin_t *usbin);
+
+static void hfa384x_usbin_rx(wlandevice_t *wlandev, struct sk_buff *skb);
+
+static void hfa384x_usbin_info(wlandevice_t *wlandev, hfa384x_usbin_t *usbin);
+
+static void
+hfa384x_usbout_tx(wlandevice_t *wlandev, hfa384x_usbout_t *usbout);
+
+static void hfa384x_usbin_ctlx(hfa384x_t *hw, hfa384x_usbin_t *usbin,
+ int urb_status);
+
+/*---------------------------------------------------*/
+/* Functions to support the prism2 usb command queue */
+
+static void hfa384x_usbctlxq_run(hfa384x_t *hw);
+
+static void hfa384x_usbctlx_reqtimerfn(unsigned long data);
+
+static void hfa384x_usbctlx_resptimerfn(unsigned long data);
+
+static void hfa384x_usb_throttlefn(unsigned long data);
+
+static void hfa384x_usbctlx_completion_task(unsigned long data);
+
+static void hfa384x_usbctlx_reaper_task(unsigned long data);
+
+static int hfa384x_usbctlx_submit(hfa384x_t *hw, hfa384x_usbctlx_t *ctlx);
+
+static void unlocked_usbctlx_complete(hfa384x_t *hw, hfa384x_usbctlx_t *ctlx);
+
+struct usbctlx_completor {
+ int (*complete)(struct usbctlx_completor *);
+};
+
+static int
+hfa384x_usbctlx_complete_sync(hfa384x_t *hw,
+ hfa384x_usbctlx_t *ctlx,
+ struct usbctlx_completor *completor);
+
+static int
+unlocked_usbctlx_cancel_async(hfa384x_t *hw, hfa384x_usbctlx_t *ctlx);
+
+static void hfa384x_cb_status(hfa384x_t *hw, const hfa384x_usbctlx_t *ctlx);
+
+static void hfa384x_cb_rrid(hfa384x_t *hw, const hfa384x_usbctlx_t *ctlx);
+
+static int
+usbctlx_get_status(const hfa384x_usb_cmdresp_t *cmdresp,
+ hfa384x_cmdresult_t *result);
+
+static void
+usbctlx_get_rridresult(const hfa384x_usb_rridresp_t *rridresp,
+ hfa384x_rridresult_t *result);
+
+/*---------------------------------------------------*/
+/* Low level req/resp CTLX formatters and submitters */
+static int
+hfa384x_docmd(hfa384x_t *hw,
+ enum cmd_mode mode,
+ hfa384x_metacmd_t *cmd,
+ ctlx_cmdcb_t cmdcb, ctlx_usercb_t usercb, void *usercb_data);
+
+static int
+hfa384x_dorrid(hfa384x_t *hw,
+ enum cmd_mode mode,
+ u16 rid,
+ void *riddata,
+ unsigned int riddatalen,
+ ctlx_cmdcb_t cmdcb, ctlx_usercb_t usercb, void *usercb_data);
+
+static int
+hfa384x_dowrid(hfa384x_t *hw,
+ enum cmd_mode mode,
+ u16 rid,
+ void *riddata,
+ unsigned int riddatalen,
+ ctlx_cmdcb_t cmdcb, ctlx_usercb_t usercb, void *usercb_data);
+
+static int
+hfa384x_dormem(hfa384x_t *hw,
+ enum cmd_mode mode,
+ u16 page,
+ u16 offset,
+ void *data,
+ unsigned int len,
+ ctlx_cmdcb_t cmdcb, ctlx_usercb_t usercb, void *usercb_data);
+
+static int
+hfa384x_dowmem(hfa384x_t *hw,
+ enum cmd_mode mode,
+ u16 page,
+ u16 offset,
+ void *data,
+ unsigned int len,
+ ctlx_cmdcb_t cmdcb, ctlx_usercb_t usercb, void *usercb_data);
+
+static int hfa384x_isgood_pdrcode(u16 pdrcode);
+
+static inline const char *ctlxstr(CTLX_STATE s)
+{
+ static const char * const ctlx_str[] = {
+ "Initial state",
+ "Complete",
+ "Request failed",
+ "Request pending",
+ "Request packet submitted",
+ "Request packet completed",
+ "Response packet completed"
+ };
+
+ return ctlx_str[s];
+};
+
+static inline hfa384x_usbctlx_t *get_active_ctlx(hfa384x_t *hw)
+{
+ return list_entry(hw->ctlxq.active.next, hfa384x_usbctlx_t, list);
+}
+
+#ifdef DEBUG_USB
+void dbprint_urb(struct urb *urb)
+{
+ pr_debug("urb->pipe=0x%08x\n", urb->pipe);
+ pr_debug("urb->status=0x%08x\n", urb->status);
+ pr_debug("urb->transfer_flags=0x%08x\n", urb->transfer_flags);
+ pr_debug("urb->transfer_buffer=0x%08x\n",
+ (unsigned int)urb->transfer_buffer);
+ pr_debug("urb->transfer_buffer_length=0x%08x\n",
+ urb->transfer_buffer_length);
+ pr_debug("urb->actual_length=0x%08x\n", urb->actual_length);
+ pr_debug("urb->bandwidth=0x%08x\n", urb->bandwidth);
+ pr_debug("urb->setup_packet(ctl)=0x%08x\n",
+ (unsigned int)urb->setup_packet);
+ pr_debug("urb->start_frame(iso/irq)=0x%08x\n", urb->start_frame);
+ pr_debug("urb->interval(irq)=0x%08x\n", urb->interval);
+ pr_debug("urb->error_count(iso)=0x%08x\n", urb->error_count);
+ pr_debug("urb->timeout=0x%08x\n", urb->timeout);
+ pr_debug("urb->context=0x%08x\n", (unsigned int)urb->context);
+ pr_debug("urb->complete=0x%08x\n", (unsigned int)urb->complete);
+}
+#endif
+
+/*----------------------------------------------------------------
+* submit_rx_urb
+*
+* Listen for input data on the BULK-IN pipe. If the pipe has
+* stalled then schedule it to be reset.
+*
+* Arguments:
+* hw device struct
+* memflags memory allocation flags
+*
+* Returns:
+* error code from submission
+*
+* Call context:
+* Any
+----------------------------------------------------------------*/
+static int submit_rx_urb(hfa384x_t *hw, gfp_t memflags)
+{
+ struct sk_buff *skb;
+ int result;
+
+ skb = dev_alloc_skb(sizeof(hfa384x_usbin_t));
+ if (skb == NULL) {
+ result = -ENOMEM;
+ goto done;
+ }
+
+ /* Post the IN urb */
+ usb_fill_bulk_urb(&hw->rx_urb, hw->usb,
+ hw->endp_in,
+ skb->data, sizeof(hfa384x_usbin_t),
+ hfa384x_usbin_callback, hw->wlandev);
+
+ hw->rx_urb_skb = skb;
+
+ result = -ENOLINK;
+ if (!hw->wlandev->hwremoved &&
+ !test_bit(WORK_RX_HALT, &hw->usb_flags)) {
+ result = SUBMIT_URB(&hw->rx_urb, memflags);
+
+ /* Check whether we need to reset the RX pipe */
+ if (result == -EPIPE) {
+ netdev_warn(hw->wlandev->netdev,
+ "%s rx pipe stalled: requesting reset\n",
+ hw->wlandev->netdev->name);
+ if (!test_and_set_bit(WORK_RX_HALT, &hw->usb_flags))
+ schedule_work(&hw->usb_work);
+ }
+ }
+
+ /* Don't leak memory if anything should go wrong */
+ if (result != 0) {
+ dev_kfree_skb(skb);
+ hw->rx_urb_skb = NULL;
+ }
+
+done:
+ return result;
+}
+
+/*----------------------------------------------------------------
+* submit_tx_urb
+*
+* Prepares and submits the URB of transmitted data. If the
+* submission fails then it will schedule the output pipe to
+* be reset.
+*
+* Arguments:
+* hw device struct
+* tx_urb URB of data for transmission
+* memflags memory allocation flags
+*
+* Returns:
+* error code from submission
+*
+* Call context:
+* Any
+----------------------------------------------------------------*/
+static int submit_tx_urb(hfa384x_t *hw, struct urb *tx_urb, gfp_t memflags)
+{
+ struct net_device *netdev = hw->wlandev->netdev;
+ int result;
+
+ result = -ENOLINK;
+ if (netif_running(netdev)) {
+ if (!hw->wlandev->hwremoved &&
+ !test_bit(WORK_TX_HALT, &hw->usb_flags)) {
+ result = SUBMIT_URB(tx_urb, memflags);
+
+ /* Test whether we need to reset the TX pipe */
+ if (result == -EPIPE) {
+ netdev_warn(hw->wlandev->netdev,
+ "%s tx pipe stalled: requesting reset\n",
+ netdev->name);
+ set_bit(WORK_TX_HALT, &hw->usb_flags);
+ schedule_work(&hw->usb_work);
+ } else if (result == 0) {
+ netif_stop_queue(netdev);
+ }
+ }
+ }
+
+ return result;
+}
+
+/*----------------------------------------------------------------
+* hfa394x_usb_defer
+*
+* There are some things that the USB stack cannot do while
+* in interrupt context, so we arrange this function to run
+* in process context.
+*
+* Arguments:
+* hw device structure
+*
+* Returns:
+* nothing
+*
+* Call context:
+* process (by design)
+----------------------------------------------------------------*/
+static void hfa384x_usb_defer(struct work_struct *data)
+{
+ hfa384x_t *hw = container_of(data, struct hfa384x, usb_work);
+ struct net_device *netdev = hw->wlandev->netdev;
+
+ /* Don't bother trying to reset anything if the plug
+ * has been pulled ...
+ */
+ if (hw->wlandev->hwremoved)
+ return;
+
+ /* Reception has stopped: try to reset the input pipe */
+ if (test_bit(WORK_RX_HALT, &hw->usb_flags)) {
+ int ret;
+
+ usb_kill_urb(&hw->rx_urb); /* Cannot be holding spinlock! */
+
+ ret = usb_clear_halt(hw->usb, hw->endp_in);
+ if (ret != 0) {
+ netdev_err(hw->wlandev->netdev,
+ "Failed to clear rx pipe for %s: err=%d\n",
+ netdev->name, ret);
+ } else {
+ netdev_info(hw->wlandev->netdev, "%s rx pipe reset complete.\n",
+ netdev->name);
+ clear_bit(WORK_RX_HALT, &hw->usb_flags);
+ set_bit(WORK_RX_RESUME, &hw->usb_flags);
+ }
+ }
+
+ /* Resume receiving data back from the device. */
+ if (test_bit(WORK_RX_RESUME, &hw->usb_flags)) {
+ int ret;
+
+ ret = submit_rx_urb(hw, GFP_KERNEL);
+ if (ret != 0) {
+ netdev_err(hw->wlandev->netdev,
+ "Failed to resume %s rx pipe.\n",
+ netdev->name);
+ } else {
+ clear_bit(WORK_RX_RESUME, &hw->usb_flags);
+ }
+ }
+
+ /* Transmission has stopped: try to reset the output pipe */
+ if (test_bit(WORK_TX_HALT, &hw->usb_flags)) {
+ int ret;
+
+ usb_kill_urb(&hw->tx_urb);
+ ret = usb_clear_halt(hw->usb, hw->endp_out);
+ if (ret != 0) {
+ netdev_err(hw->wlandev->netdev,
+ "Failed to clear tx pipe for %s: err=%d\n",
+ netdev->name, ret);
+ } else {
+ netdev_info(hw->wlandev->netdev, "%s tx pipe reset complete.\n",
+ netdev->name);
+ clear_bit(WORK_TX_HALT, &hw->usb_flags);
+ set_bit(WORK_TX_RESUME, &hw->usb_flags);
+
+ /* Stopping the BULK-OUT pipe also blocked
+ * us from sending any more CTLX URBs, so
+ * we need to re-run our queue ...
+ */
+ hfa384x_usbctlxq_run(hw);
+ }
+ }
+
+ /* Resume transmitting. */
+ if (test_and_clear_bit(WORK_TX_RESUME, &hw->usb_flags))
+ netif_wake_queue(hw->wlandev->netdev);
+}
+
+/*----------------------------------------------------------------
+* hfa384x_create
+*
+* Sets up the hfa384x_t data structure for use. Note this
+* does _not_ initialize the actual hardware, just the data structures
+* we use to keep track of its state.
+*
+* Arguments:
+* hw device structure
+* irq device irq number
+* iobase i/o base address for register access
+* membase memory base address for register access
+*
+* Returns:
+* nothing
+*
+* Side effects:
+*
+* Call context:
+* process
+----------------------------------------------------------------*/
+void hfa384x_create(hfa384x_t *hw, struct usb_device *usb)
+{
+ memset(hw, 0, sizeof(hfa384x_t));
+ hw->usb = usb;
+
+ /* set up the endpoints */
+ hw->endp_in = usb_rcvbulkpipe(usb, 1);
+ hw->endp_out = usb_sndbulkpipe(usb, 2);
+
+ /* Set up the waitq */
+ init_waitqueue_head(&hw->cmdq);
+
+ /* Initialize the command queue */
+ spin_lock_init(&hw->ctlxq.lock);
+ INIT_LIST_HEAD(&hw->ctlxq.pending);
+ INIT_LIST_HEAD(&hw->ctlxq.active);
+ INIT_LIST_HEAD(&hw->ctlxq.completing);
+ INIT_LIST_HEAD(&hw->ctlxq.reapable);
+
+ /* Initialize the authentication queue */
+ skb_queue_head_init(&hw->authq);
+
+ tasklet_init(&hw->reaper_bh,
+ hfa384x_usbctlx_reaper_task, (unsigned long)hw);
+ tasklet_init(&hw->completion_bh,
+ hfa384x_usbctlx_completion_task, (unsigned long)hw);
+ INIT_WORK(&hw->link_bh, prism2sta_processing_defer);
+ INIT_WORK(&hw->usb_work, hfa384x_usb_defer);
+
+ setup_timer(&hw->throttle, hfa384x_usb_throttlefn, (unsigned long)hw);
+
+ setup_timer(&hw->resptimer, hfa384x_usbctlx_resptimerfn,
+ (unsigned long)hw);
+
+ setup_timer(&hw->reqtimer, hfa384x_usbctlx_reqtimerfn,
+ (unsigned long)hw);
+
+ usb_init_urb(&hw->rx_urb);
+ usb_init_urb(&hw->tx_urb);
+ usb_init_urb(&hw->ctlx_urb);
+
+ hw->link_status = HFA384x_LINK_NOTCONNECTED;
+ hw->state = HFA384x_STATE_INIT;
+
+ INIT_WORK(&hw->commsqual_bh, prism2sta_commsqual_defer);
+ setup_timer(&hw->commsqual_timer, prism2sta_commsqual_timer,
+ (unsigned long)hw);
+}
+
+/*----------------------------------------------------------------
+* hfa384x_destroy
+*
+* Partner to hfa384x_create(). This function cleans up the hw
+* structure so that it can be freed by the caller using a simple
+* kfree. Currently, this function is just a placeholder. If, at some
+* point in the future, an hw in the 'shutdown' state requires a 'deep'
+* kfree, this is where it should be done. Note that if this function
+* is called on a _running_ hw structure, the drvr_stop() function is
+* called.
+*
+* Arguments:
+* hw device structure
+*
+* Returns:
+* nothing, this function is not allowed to fail.
+*
+* Side effects:
+*
+* Call context:
+* process
+----------------------------------------------------------------*/
+void hfa384x_destroy(hfa384x_t *hw)
+{
+ struct sk_buff *skb;
+
+ if (hw->state == HFA384x_STATE_RUNNING)
+ hfa384x_drvr_stop(hw);
+ hw->state = HFA384x_STATE_PREINIT;
+
+ kfree(hw->scanresults);
+ hw->scanresults = NULL;
+
+ /* Now to clean out the auth queue */
+ while ((skb = skb_dequeue(&hw->authq)))
+ dev_kfree_skb(skb);
+}
+
+static hfa384x_usbctlx_t *usbctlx_alloc(void)
+{
+ hfa384x_usbctlx_t *ctlx;
+
+ ctlx = kzalloc(sizeof(*ctlx),
+ in_interrupt() ? GFP_ATOMIC : GFP_KERNEL);
+ if (ctlx != NULL)
+ init_completion(&ctlx->done);
+
+ return ctlx;
+}
+
+static int
+usbctlx_get_status(const hfa384x_usb_cmdresp_t *cmdresp,
+ hfa384x_cmdresult_t *result)
+{
+ result->status = le16_to_cpu(cmdresp->status);
+ result->resp0 = le16_to_cpu(cmdresp->resp0);
+ result->resp1 = le16_to_cpu(cmdresp->resp1);
+ result->resp2 = le16_to_cpu(cmdresp->resp2);
+
+ pr_debug("cmdresult:status=0x%04x resp0=0x%04x resp1=0x%04x resp2=0x%04x\n",
+ result->status, result->resp0, result->resp1, result->resp2);
+
+ return result->status & HFA384x_STATUS_RESULT;
+}
+
+static void
+usbctlx_get_rridresult(const hfa384x_usb_rridresp_t *rridresp,
+ hfa384x_rridresult_t *result)
+{
+ result->rid = le16_to_cpu(rridresp->rid);
+ result->riddata = rridresp->data;
+ result->riddata_len = ((le16_to_cpu(rridresp->frmlen) - 1) * 2);
+}
+
+/*----------------------------------------------------------------
+* Completor object:
+* This completor must be passed to hfa384x_usbctlx_complete_sync()
+* when processing a CTLX that returns a hfa384x_cmdresult_t structure.
+----------------------------------------------------------------*/
+struct usbctlx_cmd_completor {
+ struct usbctlx_completor head;
+
+ const hfa384x_usb_cmdresp_t *cmdresp;
+ hfa384x_cmdresult_t *result;
+};
+
+static inline int usbctlx_cmd_completor_fn(struct usbctlx_completor *head)
+{
+ struct usbctlx_cmd_completor *complete;
+
+ complete = (struct usbctlx_cmd_completor *)head;
+ return usbctlx_get_status(complete->cmdresp, complete->result);
+}
+
+static inline struct usbctlx_completor *init_cmd_completor(
+ struct usbctlx_cmd_completor
+ *completor,
+ const hfa384x_usb_cmdresp_t
+ *cmdresp,
+ hfa384x_cmdresult_t *result)
+{
+ completor->head.complete = usbctlx_cmd_completor_fn;
+ completor->cmdresp = cmdresp;
+ completor->result = result;
+ return &(completor->head);
+}
+
+/*----------------------------------------------------------------
+* Completor object:
+* This completor must be passed to hfa384x_usbctlx_complete_sync()
+* when processing a CTLX that reads a RID.
+----------------------------------------------------------------*/
+struct usbctlx_rrid_completor {
+ struct usbctlx_completor head;
+
+ const hfa384x_usb_rridresp_t *rridresp;
+ void *riddata;
+ unsigned int riddatalen;
+};
+
+static int usbctlx_rrid_completor_fn(struct usbctlx_completor *head)
+{
+ struct usbctlx_rrid_completor *complete;
+ hfa384x_rridresult_t rridresult;
+
+ complete = (struct usbctlx_rrid_completor *)head;
+ usbctlx_get_rridresult(complete->rridresp, &rridresult);
+
+ /* Validate the length, note body len calculation in bytes */
+ if (rridresult.riddata_len != complete->riddatalen) {
+ pr_warn("RID len mismatch, rid=0x%04x hlen=%d fwlen=%d\n",
+ rridresult.rid,
+ complete->riddatalen, rridresult.riddata_len);
+ return -ENODATA;
+ }
+
+ memcpy(complete->riddata, rridresult.riddata, complete->riddatalen);
+ return 0;
+}
+
+static inline struct usbctlx_completor *init_rrid_completor(
+ struct usbctlx_rrid_completor
+ *completor,
+ const hfa384x_usb_rridresp_t
+ *rridresp,
+ void *riddata,
+ unsigned int riddatalen)
+{
+ completor->head.complete = usbctlx_rrid_completor_fn;
+ completor->rridresp = rridresp;
+ completor->riddata = riddata;
+ completor->riddatalen = riddatalen;
+ return &(completor->head);
+}
+
+/*----------------------------------------------------------------
+* Completor object:
+* Interprets the results of a synchronous RID-write
+----------------------------------------------------------------*/
+#define init_wrid_completor init_cmd_completor
+
+/*----------------------------------------------------------------
+* Completor object:
+* Interprets the results of a synchronous memory-write
+----------------------------------------------------------------*/
+#define init_wmem_completor init_cmd_completor
+
+/*----------------------------------------------------------------
+* Completor object:
+* Interprets the results of a synchronous memory-read
+----------------------------------------------------------------*/
+struct usbctlx_rmem_completor {
+ struct usbctlx_completor head;
+
+ const hfa384x_usb_rmemresp_t *rmemresp;
+ void *data;
+ unsigned int len;
+};
+
+static int usbctlx_rmem_completor_fn(struct usbctlx_completor *head)
+{
+ struct usbctlx_rmem_completor *complete =
+ (struct usbctlx_rmem_completor *)head;
+
+ pr_debug("rmemresp:len=%d\n", complete->rmemresp->frmlen);
+ memcpy(complete->data, complete->rmemresp->data, complete->len);
+ return 0;
+}
+
+static inline struct usbctlx_completor *init_rmem_completor(
+ struct usbctlx_rmem_completor
+ *completor,
+ hfa384x_usb_rmemresp_t
+ *rmemresp,
+ void *data,
+ unsigned int len)
+{
+ completor->head.complete = usbctlx_rmem_completor_fn;
+ completor->rmemresp = rmemresp;
+ completor->data = data;
+ completor->len = len;
+ return &(completor->head);
+}
+
+/*----------------------------------------------------------------
+* hfa384x_cb_status
+*
+* Ctlx_complete handler for async CMD type control exchanges.
+* mark the hw struct as such.
+*
+* Note: If the handling is changed here, it should probably be
+* changed in docmd as well.
+*
+* Arguments:
+* hw hw struct
+* ctlx completed CTLX
+*
+* Returns:
+* nothing
+*
+* Side effects:
+*
+* Call context:
+* interrupt
+----------------------------------------------------------------*/
+static void hfa384x_cb_status(hfa384x_t *hw, const hfa384x_usbctlx_t *ctlx)
+{
+ if (ctlx->usercb != NULL) {
+ hfa384x_cmdresult_t cmdresult;
+
+ if (ctlx->state != CTLX_COMPLETE) {
+ memset(&cmdresult, 0, sizeof(cmdresult));
+ cmdresult.status =
+ HFA384x_STATUS_RESULT_SET(HFA384x_CMD_ERR);
+ } else {
+ usbctlx_get_status(&ctlx->inbuf.cmdresp, &cmdresult);
+ }
+
+ ctlx->usercb(hw, &cmdresult, ctlx->usercb_data);
+ }
+}
+
+/*----------------------------------------------------------------
+* hfa384x_cb_rrid
+*
+* CTLX completion handler for async RRID type control exchanges.
+*
+* Note: If the handling is changed here, it should probably be
+* changed in dorrid as well.
+*
+* Arguments:
+* hw hw struct
+* ctlx completed CTLX
+*
+* Returns:
+* nothing
+*
+* Side effects:
+*
+* Call context:
+* interrupt
+----------------------------------------------------------------*/
+static void hfa384x_cb_rrid(hfa384x_t *hw, const hfa384x_usbctlx_t *ctlx)
+{
+ if (ctlx->usercb != NULL) {
+ hfa384x_rridresult_t rridresult;
+
+ if (ctlx->state != CTLX_COMPLETE) {
+ memset(&rridresult, 0, sizeof(rridresult));
+ rridresult.rid = le16_to_cpu(ctlx->outbuf.rridreq.rid);
+ } else {
+ usbctlx_get_rridresult(&ctlx->inbuf.rridresp,
+ &rridresult);
+ }
+
+ ctlx->usercb(hw, &rridresult, ctlx->usercb_data);
+ }
+}
+
+static inline int hfa384x_docmd_wait(hfa384x_t *hw, hfa384x_metacmd_t *cmd)
+{
+ return hfa384x_docmd(hw, DOWAIT, cmd, NULL, NULL, NULL);
+}
+
+static inline int
+hfa384x_docmd_async(hfa384x_t *hw,
+ hfa384x_metacmd_t *cmd,
+ ctlx_cmdcb_t cmdcb, ctlx_usercb_t usercb, void *usercb_data)
+{
+ return hfa384x_docmd(hw, DOASYNC, cmd, cmdcb, usercb, usercb_data);
+}
+
+static inline int
+hfa384x_dorrid_wait(hfa384x_t *hw, u16 rid, void *riddata,
+ unsigned int riddatalen)
+{
+ return hfa384x_dorrid(hw, DOWAIT,
+ rid, riddata, riddatalen, NULL, NULL, NULL);
+}
+
+static inline int
+hfa384x_dorrid_async(hfa384x_t *hw,
+ u16 rid, void *riddata, unsigned int riddatalen,
+ ctlx_cmdcb_t cmdcb,
+ ctlx_usercb_t usercb, void *usercb_data)
+{
+ return hfa384x_dorrid(hw, DOASYNC,
+ rid, riddata, riddatalen,
+ cmdcb, usercb, usercb_data);
+}
+
+static inline int
+hfa384x_dowrid_wait(hfa384x_t *hw, u16 rid, void *riddata,
+ unsigned int riddatalen)
+{
+ return hfa384x_dowrid(hw, DOWAIT,
+ rid, riddata, riddatalen, NULL, NULL, NULL);
+}
+
+static inline int
+hfa384x_dowrid_async(hfa384x_t *hw,
+ u16 rid, void *riddata, unsigned int riddatalen,
+ ctlx_cmdcb_t cmdcb,
+ ctlx_usercb_t usercb, void *usercb_data)
+{
+ return hfa384x_dowrid(hw, DOASYNC,
+ rid, riddata, riddatalen,
+ cmdcb, usercb, usercb_data);
+}
+
+static inline int
+hfa384x_dormem_wait(hfa384x_t *hw,
+ u16 page, u16 offset, void *data, unsigned int len)
+{
+ return hfa384x_dormem(hw, DOWAIT,
+ page, offset, data, len, NULL, NULL, NULL);
+}
+
+static inline int
+hfa384x_dormem_async(hfa384x_t *hw,
+ u16 page, u16 offset, void *data, unsigned int len,
+ ctlx_cmdcb_t cmdcb,
+ ctlx_usercb_t usercb, void *usercb_data)
+{
+ return hfa384x_dormem(hw, DOASYNC,
+ page, offset, data, len,
+ cmdcb, usercb, usercb_data);
+}
+
+static inline int
+hfa384x_dowmem_wait(hfa384x_t *hw,
+ u16 page, u16 offset, void *data, unsigned int len)
+{
+ return hfa384x_dowmem(hw, DOWAIT,
+ page, offset, data, len, NULL, NULL, NULL);
+}
+
+static inline int
+hfa384x_dowmem_async(hfa384x_t *hw,
+ u16 page,
+ u16 offset,
+ void *data,
+ unsigned int len,
+ ctlx_cmdcb_t cmdcb,
+ ctlx_usercb_t usercb, void *usercb_data)
+{
+ return hfa384x_dowmem(hw, DOASYNC,
+ page, offset, data, len,
+ cmdcb, usercb, usercb_data);
+}
+
+/*----------------------------------------------------------------
+* hfa384x_cmd_initialize
+*
+* Issues the initialize command and sets the hw->state based
+* on the result.
+*
+* Arguments:
+* hw device structure
+*
+* Returns:
+* 0 success
+* >0 f/w reported error - f/w status code
+* <0 driver reported error
+*
+* Side effects:
+*
+* Call context:
+* process
+----------------------------------------------------------------*/
+int hfa384x_cmd_initialize(hfa384x_t *hw)
+{
+ int result = 0;
+ int i;
+ hfa384x_metacmd_t cmd;
+
+ cmd.cmd = HFA384x_CMDCODE_INIT;
+ cmd.parm0 = 0;
+ cmd.parm1 = 0;
+ cmd.parm2 = 0;
+
+ result = hfa384x_docmd_wait(hw, &cmd);
+
+ pr_debug("cmdresp.init: status=0x%04x, resp0=0x%04x, resp1=0x%04x, resp2=0x%04x\n",
+ cmd.result.status,
+ cmd.result.resp0, cmd.result.resp1, cmd.result.resp2);
+ if (result == 0) {
+ for (i = 0; i < HFA384x_NUMPORTS_MAX; i++)
+ hw->port_enabled[i] = 0;
+ }
+
+ hw->link_status = HFA384x_LINK_NOTCONNECTED;
+
+ return result;
+}
+
+/*----------------------------------------------------------------
+* hfa384x_cmd_disable
+*
+* Issues the disable command to stop communications on one of
+* the MACs 'ports'.
+*
+* Arguments:
+* hw device structure
+* macport MAC port number (host order)
+*
+* Returns:
+* 0 success
+* >0 f/w reported failure - f/w status code
+* <0 driver reported error (timeout|bad arg)
+*
+* Side effects:
+*
+* Call context:
+* process
+----------------------------------------------------------------*/
+int hfa384x_cmd_disable(hfa384x_t *hw, u16 macport)
+{
+ int result = 0;
+ hfa384x_metacmd_t cmd;
+
+ cmd.cmd = HFA384x_CMD_CMDCODE_SET(HFA384x_CMDCODE_DISABLE) |
+ HFA384x_CMD_MACPORT_SET(macport);
+ cmd.parm0 = 0;
+ cmd.parm1 = 0;
+ cmd.parm2 = 0;
+
+ result = hfa384x_docmd_wait(hw, &cmd);
+
+ return result;
+}
+
+/*----------------------------------------------------------------
+* hfa384x_cmd_enable
+*
+* Issues the enable command to enable communications on one of
+* the MACs 'ports'.
+*
+* Arguments:
+* hw device structure
+* macport MAC port number
+*
+* Returns:
+* 0 success
+* >0 f/w reported failure - f/w status code
+* <0 driver reported error (timeout|bad arg)
+*
+* Side effects:
+*
+* Call context:
+* process
+----------------------------------------------------------------*/
+int hfa384x_cmd_enable(hfa384x_t *hw, u16 macport)
+{
+ int result = 0;
+ hfa384x_metacmd_t cmd;
+
+ cmd.cmd = HFA384x_CMD_CMDCODE_SET(HFA384x_CMDCODE_ENABLE) |
+ HFA384x_CMD_MACPORT_SET(macport);
+ cmd.parm0 = 0;
+ cmd.parm1 = 0;
+ cmd.parm2 = 0;
+
+ result = hfa384x_docmd_wait(hw, &cmd);
+
+ return result;
+}
+
+/*----------------------------------------------------------------
+* hfa384x_cmd_monitor
+*
+* Enables the 'monitor mode' of the MAC. Here's the description of
+* monitor mode that I've received thus far:
+*
+* "The "monitor mode" of operation is that the MAC passes all
+* frames for which the PLCP checks are correct. All received
+* MPDUs are passed to the host with MAC Port = 7, with a
+* receive status of good, FCS error, or undecryptable. Passing
+* certain MPDUs is a violation of the 802.11 standard, but useful
+* for a debugging tool." Normal communication is not possible
+* while monitor mode is enabled.
+*
+* Arguments:
+* hw device structure
+* enable a code (0x0b|0x0f) that enables/disables
+* monitor mode. (host order)
+*
+* Returns:
+* 0 success
+* >0 f/w reported failure - f/w status code
+* <0 driver reported error (timeout|bad arg)
+*
+* Side effects:
+*
+* Call context:
+* process
+----------------------------------------------------------------*/
+int hfa384x_cmd_monitor(hfa384x_t *hw, u16 enable)
+{
+ int result = 0;
+ hfa384x_metacmd_t cmd;
+
+ cmd.cmd = HFA384x_CMD_CMDCODE_SET(HFA384x_CMDCODE_MONITOR) |
+ HFA384x_CMD_AINFO_SET(enable);
+ cmd.parm0 = 0;
+ cmd.parm1 = 0;
+ cmd.parm2 = 0;
+
+ result = hfa384x_docmd_wait(hw, &cmd);
+
+ return result;
+}
+
+/*----------------------------------------------------------------
+* hfa384x_cmd_download
+*
+* Sets the controls for the MAC controller code/data download
+* process. The arguments set the mode and address associated
+* with a download. Note that the aux registers should be enabled
+* prior to setting one of the download enable modes.
+*
+* Arguments:
+* hw device structure
+* mode 0 - Disable programming and begin code exec
+* 1 - Enable volatile mem programming
+* 2 - Enable non-volatile mem programming
+* 3 - Program non-volatile section from NV download
+* buffer.
+* (host order)
+* lowaddr
+* highaddr For mode 1, sets the high & low order bits of
+* the "destination address". This address will be
+* the execution start address when download is
+* subsequently disabled.
+* For mode 2, sets the high & low order bits of
+* the destination in NV ram.
+* For modes 0 & 3, should be zero. (host order)
+* NOTE: these are CMD format.
+* codelen Length of the data to write in mode 2,
+* zero otherwise. (host order)
+*
+* Returns:
+* 0 success
+* >0 f/w reported failure - f/w status code
+* <0 driver reported error (timeout|bad arg)
+*
+* Side effects:
+*
+* Call context:
+* process
+----------------------------------------------------------------*/
+int hfa384x_cmd_download(hfa384x_t *hw, u16 mode, u16 lowaddr,
+ u16 highaddr, u16 codelen)
+{
+ int result = 0;
+ hfa384x_metacmd_t cmd;
+
+ pr_debug("mode=%d, lowaddr=0x%04x, highaddr=0x%04x, codelen=%d\n",
+ mode, lowaddr, highaddr, codelen);
+
+ cmd.cmd = (HFA384x_CMD_CMDCODE_SET(HFA384x_CMDCODE_DOWNLD) |
+ HFA384x_CMD_PROGMODE_SET(mode));
+
+ cmd.parm0 = lowaddr;
+ cmd.parm1 = highaddr;
+ cmd.parm2 = codelen;
+
+ result = hfa384x_docmd_wait(hw, &cmd);
+
+ return result;
+}
+
+/*----------------------------------------------------------------
+* hfa384x_corereset
+*
+* Perform a reset of the hfa38xx MAC core. We assume that the hw
+* structure is in its "created" state. That is, it is initialized
+* with proper values. Note that if a reset is done after the
+* device has been active for awhile, the caller might have to clean
+* up some leftover cruft in the hw structure.
+*
+* Arguments:
+* hw device structure
+* holdtime how long (in ms) to hold the reset
+* settletime how long (in ms) to wait after releasing
+* the reset
+*
+* Returns:
+* nothing
+*
+* Side effects:
+*
+* Call context:
+* process
+----------------------------------------------------------------*/
+int hfa384x_corereset(hfa384x_t *hw, int holdtime, int settletime, int genesis)
+{
+ int result = 0;
+
+ result = usb_reset_device(hw->usb);
+ if (result < 0) {
+ netdev_err(hw->wlandev->netdev, "usb_reset_device() failed, result=%d.\n",
+ result);
+ }
+
+ return result;
+}
+
+/*----------------------------------------------------------------
+* hfa384x_usbctlx_complete_sync
+*
+* Waits for a synchronous CTLX object to complete,
+* and then handles the response.
+*
+* Arguments:
+* hw device structure
+* ctlx CTLX ptr
+* completor functor object to decide what to
+* do with the CTLX's result.
+*
+* Returns:
+* 0 Success
+* -ERESTARTSYS Interrupted by a signal
+* -EIO CTLX failed
+* -ENODEV Adapter was unplugged
+* ??? Result from completor
+*
+* Side effects:
+*
+* Call context:
+* process
+----------------------------------------------------------------*/
+static int hfa384x_usbctlx_complete_sync(hfa384x_t *hw,
+ hfa384x_usbctlx_t *ctlx,
+ struct usbctlx_completor *completor)
+{
+ unsigned long flags;
+ int result;
+
+ result = wait_for_completion_interruptible(&ctlx->done);
+
+ spin_lock_irqsave(&hw->ctlxq.lock, flags);
+
+ /*
+ * We can only handle the CTLX if the USB disconnect
+ * function has not run yet ...
+ */
+cleanup:
+ if (hw->wlandev->hwremoved) {
+ spin_unlock_irqrestore(&hw->ctlxq.lock, flags);
+ result = -ENODEV;
+ } else if (result != 0) {
+ int runqueue = 0;
+
+ /*
+ * We were probably interrupted, so delete
+ * this CTLX asynchronously, kill the timers
+ * and the URB, and then start the next
+ * pending CTLX.
+ *
+ * NOTE: We can only delete the timers and
+ * the URB if this CTLX is active.
+ */
+ if (ctlx == get_active_ctlx(hw)) {
+ spin_unlock_irqrestore(&hw->ctlxq.lock, flags);
+
+ del_singleshot_timer_sync(&hw->reqtimer);
+ del_singleshot_timer_sync(&hw->resptimer);
+ hw->req_timer_done = 1;
+ hw->resp_timer_done = 1;
+ usb_kill_urb(&hw->ctlx_urb);
+
+ spin_lock_irqsave(&hw->ctlxq.lock, flags);
+
+ runqueue = 1;
+
+ /*
+ * This scenario is so unlikely that I'm
+ * happy with a grubby "goto" solution ...
+ */
+ if (hw->wlandev->hwremoved)
+ goto cleanup;
+ }
+
+ /*
+ * The completion task will send this CTLX
+ * to the reaper the next time it runs. We
+ * are no longer in a hurry.
+ */
+ ctlx->reapable = 1;
+ ctlx->state = CTLX_REQ_FAILED;
+ list_move_tail(&ctlx->list, &hw->ctlxq.completing);
+
+ spin_unlock_irqrestore(&hw->ctlxq.lock, flags);
+
+ if (runqueue)
+ hfa384x_usbctlxq_run(hw);
+ } else {
+ if (ctlx->state == CTLX_COMPLETE) {
+ result = completor->complete(completor);
+ } else {
+ netdev_warn(hw->wlandev->netdev, "CTLX[%d] error: state(%s)\n",
+ le16_to_cpu(ctlx->outbuf.type),
+ ctlxstr(ctlx->state));
+ result = -EIO;
+ }
+
+ list_del(&ctlx->list);
+ spin_unlock_irqrestore(&hw->ctlxq.lock, flags);
+ kfree(ctlx);
+ }
+
+ return result;
+}
+
+/*----------------------------------------------------------------
+* hfa384x_docmd
+*
+* Constructs a command CTLX and submits it.
+*
+* NOTE: Any changes to the 'post-submit' code in this function
+* need to be carried over to hfa384x_cbcmd() since the handling
+* is virtually identical.
+*
+* Arguments:
+* hw device structure
+* mode DOWAIT or DOASYNC
+* cmd cmd structure. Includes all arguments and result
+* data points. All in host order. in host order
+* cmdcb command-specific callback
+* usercb user callback for async calls, NULL for DOWAIT calls
+* usercb_data user supplied data pointer for async calls, NULL
+* for DOASYNC calls
+*
+* Returns:
+* 0 success
+* -EIO CTLX failure
+* -ERESTARTSYS Awakened on signal
+* >0 command indicated error, Status and Resp0-2 are
+* in hw structure.
+*
+* Side effects:
+*
+*
+* Call context:
+* process
+----------------------------------------------------------------*/
+static int
+hfa384x_docmd(hfa384x_t *hw,
+ enum cmd_mode mode,
+ hfa384x_metacmd_t *cmd,
+ ctlx_cmdcb_t cmdcb, ctlx_usercb_t usercb, void *usercb_data)
+{
+ int result;
+ hfa384x_usbctlx_t *ctlx;
+
+ ctlx = usbctlx_alloc();
+ if (ctlx == NULL) {
+ result = -ENOMEM;
+ goto done;
+ }
+
+ /* Initialize the command */
+ ctlx->outbuf.cmdreq.type = cpu_to_le16(HFA384x_USB_CMDREQ);
+ ctlx->outbuf.cmdreq.cmd = cpu_to_le16(cmd->cmd);
+ ctlx->outbuf.cmdreq.parm0 = cpu_to_le16(cmd->parm0);
+ ctlx->outbuf.cmdreq.parm1 = cpu_to_le16(cmd->parm1);
+ ctlx->outbuf.cmdreq.parm2 = cpu_to_le16(cmd->parm2);
+
+ ctlx->outbufsize = sizeof(ctlx->outbuf.cmdreq);
+
+ pr_debug("cmdreq: cmd=0x%04x parm0=0x%04x parm1=0x%04x parm2=0x%04x\n",
+ cmd->cmd, cmd->parm0, cmd->parm1, cmd->parm2);
+
+ ctlx->reapable = mode;
+ ctlx->cmdcb = cmdcb;
+ ctlx->usercb = usercb;
+ ctlx->usercb_data = usercb_data;
+
+ result = hfa384x_usbctlx_submit(hw, ctlx);
+ if (result != 0) {
+ kfree(ctlx);
+ } else if (mode == DOWAIT) {
+ struct usbctlx_cmd_completor completor;
+
+ result =
+ hfa384x_usbctlx_complete_sync(hw, ctlx,
+ init_cmd_completor(&completor,
+ &ctlx->
+ inbuf.
+ cmdresp,
+ &cmd->
+ result));
+ }
+
+done:
+ return result;
+}
+
+/*----------------------------------------------------------------
+* hfa384x_dorrid
+*
+* Constructs a read rid CTLX and issues it.
+*
+* NOTE: Any changes to the 'post-submit' code in this function
+* need to be carried over to hfa384x_cbrrid() since the handling
+* is virtually identical.
+*
+* Arguments:
+* hw device structure
+* mode DOWAIT or DOASYNC
+* rid Read RID number (host order)
+* riddata Caller supplied buffer that MAC formatted RID.data
+* record will be written to for DOWAIT calls. Should
+* be NULL for DOASYNC calls.
+* riddatalen Buffer length for DOWAIT calls. Zero for DOASYNC calls.
+* cmdcb command callback for async calls, NULL for DOWAIT calls
+* usercb user callback for async calls, NULL for DOWAIT calls
+* usercb_data user supplied data pointer for async calls, NULL
+* for DOWAIT calls
+*
+* Returns:
+* 0 success
+* -EIO CTLX failure
+* -ERESTARTSYS Awakened on signal
+* -ENODATA riddatalen != macdatalen
+* >0 command indicated error, Status and Resp0-2 are
+* in hw structure.
+*
+* Side effects:
+*
+* Call context:
+* interrupt (DOASYNC)
+* process (DOWAIT or DOASYNC)
+----------------------------------------------------------------*/
+static int
+hfa384x_dorrid(hfa384x_t *hw,
+ enum cmd_mode mode,
+ u16 rid,
+ void *riddata,
+ unsigned int riddatalen,
+ ctlx_cmdcb_t cmdcb, ctlx_usercb_t usercb, void *usercb_data)
+{
+ int result;
+ hfa384x_usbctlx_t *ctlx;
+
+ ctlx = usbctlx_alloc();
+ if (ctlx == NULL) {
+ result = -ENOMEM;
+ goto done;
+ }
+
+ /* Initialize the command */
+ ctlx->outbuf.rridreq.type = cpu_to_le16(HFA384x_USB_RRIDREQ);
+ ctlx->outbuf.rridreq.frmlen =
+ cpu_to_le16(sizeof(ctlx->outbuf.rridreq.rid));
+ ctlx->outbuf.rridreq.rid = cpu_to_le16(rid);
+
+ ctlx->outbufsize = sizeof(ctlx->outbuf.rridreq);
+
+ ctlx->reapable = mode;
+ ctlx->cmdcb = cmdcb;
+ ctlx->usercb = usercb;
+ ctlx->usercb_data = usercb_data;
+
+ /* Submit the CTLX */
+ result = hfa384x_usbctlx_submit(hw, ctlx);
+ if (result != 0) {
+ kfree(ctlx);
+ } else if (mode == DOWAIT) {
+ struct usbctlx_rrid_completor completor;
+
+ result =
+ hfa384x_usbctlx_complete_sync(hw, ctlx,
+ init_rrid_completor
+ (&completor,
+ &ctlx->inbuf.rridresp,
+ riddata, riddatalen));
+ }
+
+done:
+ return result;
+}
+
+/*----------------------------------------------------------------
+* hfa384x_dowrid
+*
+* Constructs a write rid CTLX and issues it.
+*
+* NOTE: Any changes to the 'post-submit' code in this function
+* need to be carried over to hfa384x_cbwrid() since the handling
+* is virtually identical.
+*
+* Arguments:
+* hw device structure
+* enum cmd_mode DOWAIT or DOASYNC
+* rid RID code
+* riddata Data portion of RID formatted for MAC
+* riddatalen Length of the data portion in bytes
+* cmdcb command callback for async calls, NULL for DOWAIT calls
+* usercb user callback for async calls, NULL for DOWAIT calls
+* usercb_data user supplied data pointer for async calls
+*
+* Returns:
+* 0 success
+* -ETIMEDOUT timed out waiting for register ready or
+* command completion
+* >0 command indicated error, Status and Resp0-2 are
+* in hw structure.
+*
+* Side effects:
+*
+* Call context:
+* interrupt (DOASYNC)
+* process (DOWAIT or DOASYNC)
+----------------------------------------------------------------*/
+static int
+hfa384x_dowrid(hfa384x_t *hw,
+ enum cmd_mode mode,
+ u16 rid,
+ void *riddata,
+ unsigned int riddatalen,
+ ctlx_cmdcb_t cmdcb, ctlx_usercb_t usercb, void *usercb_data)
+{
+ int result;
+ hfa384x_usbctlx_t *ctlx;
+
+ ctlx = usbctlx_alloc();
+ if (ctlx == NULL) {
+ result = -ENOMEM;
+ goto done;
+ }
+
+ /* Initialize the command */
+ ctlx->outbuf.wridreq.type = cpu_to_le16(HFA384x_USB_WRIDREQ);
+ ctlx->outbuf.wridreq.frmlen = cpu_to_le16((sizeof
+ (ctlx->outbuf.wridreq.rid) +
+ riddatalen + 1) / 2);
+ ctlx->outbuf.wridreq.rid = cpu_to_le16(rid);
+ memcpy(ctlx->outbuf.wridreq.data, riddata, riddatalen);
+
+ ctlx->outbufsize = sizeof(ctlx->outbuf.wridreq.type) +
+ sizeof(ctlx->outbuf.wridreq.frmlen) +
+ sizeof(ctlx->outbuf.wridreq.rid) + riddatalen;
+
+ ctlx->reapable = mode;
+ ctlx->cmdcb = cmdcb;
+ ctlx->usercb = usercb;
+ ctlx->usercb_data = usercb_data;
+
+ /* Submit the CTLX */
+ result = hfa384x_usbctlx_submit(hw, ctlx);
+ if (result != 0) {
+ kfree(ctlx);
+ } else if (mode == DOWAIT) {
+ struct usbctlx_cmd_completor completor;
+ hfa384x_cmdresult_t wridresult;
+
+ result = hfa384x_usbctlx_complete_sync(hw,
+ ctlx,
+ init_wrid_completor
+ (&completor,
+ &ctlx->inbuf.wridresp,
+ &wridresult));
+ }
+
+done:
+ return result;
+}
+
+/*----------------------------------------------------------------
+* hfa384x_dormem
+*
+* Constructs a readmem CTLX and issues it.
+*
+* NOTE: Any changes to the 'post-submit' code in this function
+* need to be carried over to hfa384x_cbrmem() since the handling
+* is virtually identical.
+*
+* Arguments:
+* hw device structure
+* mode DOWAIT or DOASYNC
+* page MAC address space page (CMD format)
+* offset MAC address space offset
+* data Ptr to data buffer to receive read
+* len Length of the data to read (max == 2048)
+* cmdcb command callback for async calls, NULL for DOWAIT calls
+* usercb user callback for async calls, NULL for DOWAIT calls
+* usercb_data user supplied data pointer for async calls
+*
+* Returns:
+* 0 success
+* -ETIMEDOUT timed out waiting for register ready or
+* command completion
+* >0 command indicated error, Status and Resp0-2 are
+* in hw structure.
+*
+* Side effects:
+*
+* Call context:
+* interrupt (DOASYNC)
+* process (DOWAIT or DOASYNC)
+----------------------------------------------------------------*/
+static int
+hfa384x_dormem(hfa384x_t *hw,
+ enum cmd_mode mode,
+ u16 page,
+ u16 offset,
+ void *data,
+ unsigned int len,
+ ctlx_cmdcb_t cmdcb, ctlx_usercb_t usercb, void *usercb_data)
+{
+ int result;
+ hfa384x_usbctlx_t *ctlx;
+
+ ctlx = usbctlx_alloc();
+ if (ctlx == NULL) {
+ result = -ENOMEM;
+ goto done;
+ }
+
+ /* Initialize the command */
+ ctlx->outbuf.rmemreq.type = cpu_to_le16(HFA384x_USB_RMEMREQ);
+ ctlx->outbuf.rmemreq.frmlen =
+ cpu_to_le16(sizeof(ctlx->outbuf.rmemreq.offset) +
+ sizeof(ctlx->outbuf.rmemreq.page) + len);
+ ctlx->outbuf.rmemreq.offset = cpu_to_le16(offset);
+ ctlx->outbuf.rmemreq.page = cpu_to_le16(page);
+
+ ctlx->outbufsize = sizeof(ctlx->outbuf.rmemreq);
+
+ pr_debug("type=0x%04x frmlen=%d offset=0x%04x page=0x%04x\n",
+ ctlx->outbuf.rmemreq.type,
+ ctlx->outbuf.rmemreq.frmlen,
+ ctlx->outbuf.rmemreq.offset, ctlx->outbuf.rmemreq.page);
+
+ pr_debug("pktsize=%zd\n", ROUNDUP64(sizeof(ctlx->outbuf.rmemreq)));
+
+ ctlx->reapable = mode;
+ ctlx->cmdcb = cmdcb;
+ ctlx->usercb = usercb;
+ ctlx->usercb_data = usercb_data;
+
+ result = hfa384x_usbctlx_submit(hw, ctlx);
+ if (result != 0) {
+ kfree(ctlx);
+ } else if (mode == DOWAIT) {
+ struct usbctlx_rmem_completor completor;
+
+ result =
+ hfa384x_usbctlx_complete_sync(hw, ctlx,
+ init_rmem_completor
+ (&completor,
+ &ctlx->inbuf.rmemresp, data,
+ len));
+ }
+
+done:
+ return result;
+}
+
+/*----------------------------------------------------------------
+* hfa384x_dowmem
+*
+* Constructs a writemem CTLX and issues it.
+*
+* NOTE: Any changes to the 'post-submit' code in this function
+* need to be carried over to hfa384x_cbwmem() since the handling
+* is virtually identical.
+*
+* Arguments:
+* hw device structure
+* mode DOWAIT or DOASYNC
+* page MAC address space page (CMD format)
+* offset MAC address space offset
+* data Ptr to data buffer containing write data
+* len Length of the data to read (max == 2048)
+* cmdcb command callback for async calls, NULL for DOWAIT calls
+* usercb user callback for async calls, NULL for DOWAIT calls
+* usercb_data user supplied data pointer for async calls.
+*
+* Returns:
+* 0 success
+* -ETIMEDOUT timed out waiting for register ready or
+* command completion
+* >0 command indicated error, Status and Resp0-2 are
+* in hw structure.
+*
+* Side effects:
+*
+* Call context:
+* interrupt (DOWAIT)
+* process (DOWAIT or DOASYNC)
+----------------------------------------------------------------*/
+static int
+hfa384x_dowmem(hfa384x_t *hw,
+ enum cmd_mode mode,
+ u16 page,
+ u16 offset,
+ void *data,
+ unsigned int len,
+ ctlx_cmdcb_t cmdcb, ctlx_usercb_t usercb, void *usercb_data)
+{
+ int result;
+ hfa384x_usbctlx_t *ctlx;
+
+ pr_debug("page=0x%04x offset=0x%04x len=%d\n", page, offset, len);
+
+ ctlx = usbctlx_alloc();
+ if (ctlx == NULL) {
+ result = -ENOMEM;
+ goto done;
+ }
+
+ /* Initialize the command */
+ ctlx->outbuf.wmemreq.type = cpu_to_le16(HFA384x_USB_WMEMREQ);
+ ctlx->outbuf.wmemreq.frmlen =
+ cpu_to_le16(sizeof(ctlx->outbuf.wmemreq.offset) +
+ sizeof(ctlx->outbuf.wmemreq.page) + len);
+ ctlx->outbuf.wmemreq.offset = cpu_to_le16(offset);
+ ctlx->outbuf.wmemreq.page = cpu_to_le16(page);
+ memcpy(ctlx->outbuf.wmemreq.data, data, len);
+
+ ctlx->outbufsize = sizeof(ctlx->outbuf.wmemreq.type) +
+ sizeof(ctlx->outbuf.wmemreq.frmlen) +
+ sizeof(ctlx->outbuf.wmemreq.offset) +
+ sizeof(ctlx->outbuf.wmemreq.page) + len;
+
+ ctlx->reapable = mode;
+ ctlx->cmdcb = cmdcb;
+ ctlx->usercb = usercb;
+ ctlx->usercb_data = usercb_data;
+
+ result = hfa384x_usbctlx_submit(hw, ctlx);
+ if (result != 0) {
+ kfree(ctlx);
+ } else if (mode == DOWAIT) {
+ struct usbctlx_cmd_completor completor;
+ hfa384x_cmdresult_t wmemresult;
+
+ result = hfa384x_usbctlx_complete_sync(hw,
+ ctlx,
+ init_wmem_completor
+ (&completor,
+ &ctlx->inbuf.wmemresp,
+ &wmemresult));
+ }
+
+done:
+ return result;
+}
+
+/*----------------------------------------------------------------
+* hfa384x_drvr_commtallies
+*
+* Send a commtallies inquiry to the MAC. Note that this is an async
+* call that will result in an info frame arriving sometime later.
+*
+* Arguments:
+* hw device structure
+*
+* Returns:
+* zero success.
+*
+* Side effects:
+*
+* Call context:
+* process
+----------------------------------------------------------------*/
+int hfa384x_drvr_commtallies(hfa384x_t *hw)
+{
+ hfa384x_metacmd_t cmd;
+
+ cmd.cmd = HFA384x_CMDCODE_INQ;
+ cmd.parm0 = HFA384x_IT_COMMTALLIES;
+ cmd.parm1 = 0;
+ cmd.parm2 = 0;
+
+ hfa384x_docmd_async(hw, &cmd, NULL, NULL, NULL);
+
+ return 0;
+}
+
+/*----------------------------------------------------------------
+* hfa384x_drvr_disable
+*
+* Issues the disable command to stop communications on one of
+* the MACs 'ports'. Only macport 0 is valid for stations.
+* APs may also disable macports 1-6. Only ports that have been
+* previously enabled may be disabled.
+*
+* Arguments:
+* hw device structure
+* macport MAC port number (host order)
+*
+* Returns:
+* 0 success
+* >0 f/w reported failure - f/w status code
+* <0 driver reported error (timeout|bad arg)
+*
+* Side effects:
+*
+* Call context:
+* process
+----------------------------------------------------------------*/
+int hfa384x_drvr_disable(hfa384x_t *hw, u16 macport)
+{
+ int result = 0;
+
+ if ((!hw->isap && macport != 0) ||
+ (hw->isap && !(macport <= HFA384x_PORTID_MAX)) ||
+ !(hw->port_enabled[macport])) {
+ result = -EINVAL;
+ } else {
+ result = hfa384x_cmd_disable(hw, macport);
+ if (result == 0)
+ hw->port_enabled[macport] = 0;
+ }
+ return result;
+}
+
+/*----------------------------------------------------------------
+* hfa384x_drvr_enable
+*
+* Issues the enable command to enable communications on one of
+* the MACs 'ports'. Only macport 0 is valid for stations.
+* APs may also enable macports 1-6. Only ports that are currently
+* disabled may be enabled.
+*
+* Arguments:
+* hw device structure
+* macport MAC port number
+*
+* Returns:
+* 0 success
+* >0 f/w reported failure - f/w status code
+* <0 driver reported error (timeout|bad arg)
+*
+* Side effects:
+*
+* Call context:
+* process
+----------------------------------------------------------------*/
+int hfa384x_drvr_enable(hfa384x_t *hw, u16 macport)
+{
+ int result = 0;
+
+ if ((!hw->isap && macport != 0) ||
+ (hw->isap && !(macport <= HFA384x_PORTID_MAX)) ||
+ (hw->port_enabled[macport])) {
+ result = -EINVAL;
+ } else {
+ result = hfa384x_cmd_enable(hw, macport);
+ if (result == 0)
+ hw->port_enabled[macport] = 1;
+ }
+ return result;
+}
+
+/*----------------------------------------------------------------
+* hfa384x_drvr_flashdl_enable
+*
+* Begins the flash download state. Checks to see that we're not
+* already in a download state and that a port isn't enabled.
+* Sets the download state and retrieves the flash download
+* buffer location, buffer size, and timeout length.
+*
+* Arguments:
+* hw device structure
+*
+* Returns:
+* 0 success
+* >0 f/w reported error - f/w status code
+* <0 driver reported error
+*
+* Side effects:
+*
+* Call context:
+* process
+----------------------------------------------------------------*/
+int hfa384x_drvr_flashdl_enable(hfa384x_t *hw)
+{
+ int result = 0;
+ int i;
+
+ /* Check that a port isn't active */
+ for (i = 0; i < HFA384x_PORTID_MAX; i++) {
+ if (hw->port_enabled[i]) {
+ pr_debug("called when port enabled.\n");
+ return -EINVAL;
+ }
+ }
+
+ /* Check that we're not already in a download state */
+ if (hw->dlstate != HFA384x_DLSTATE_DISABLED)
+ return -EINVAL;
+
+ /* Retrieve the buffer loc&size and timeout */
+ result = hfa384x_drvr_getconfig(hw, HFA384x_RID_DOWNLOADBUFFER,
+ &(hw->bufinfo), sizeof(hw->bufinfo));
+ if (result)
+ return result;
+
+ hw->bufinfo.page = le16_to_cpu(hw->bufinfo.page);
+ hw->bufinfo.offset = le16_to_cpu(hw->bufinfo.offset);
+ hw->bufinfo.len = le16_to_cpu(hw->bufinfo.len);
+ result = hfa384x_drvr_getconfig16(hw, HFA384x_RID_MAXLOADTIME,
+ &(hw->dltimeout));
+ if (result)
+ return result;
+
+ hw->dltimeout = le16_to_cpu(hw->dltimeout);
+
+ pr_debug("flashdl_enable\n");
+
+ hw->dlstate = HFA384x_DLSTATE_FLASHENABLED;
+
+ return result;
+}
+
+/*----------------------------------------------------------------
+* hfa384x_drvr_flashdl_disable
+*
+* Ends the flash download state. Note that this will cause the MAC
+* firmware to restart.
+*
+* Arguments:
+* hw device structure
+*
+* Returns:
+* 0 success
+* >0 f/w reported error - f/w status code
+* <0 driver reported error
+*
+* Side effects:
+*
+* Call context:
+* process
+----------------------------------------------------------------*/
+int hfa384x_drvr_flashdl_disable(hfa384x_t *hw)
+{
+ /* Check that we're already in the download state */
+ if (hw->dlstate != HFA384x_DLSTATE_FLASHENABLED)
+ return -EINVAL;
+
+ pr_debug("flashdl_enable\n");
+
+ /* There isn't much we can do at this point, so I don't */
+ /* bother w/ the return value */
+ hfa384x_cmd_download(hw, HFA384x_PROGMODE_DISABLE, 0, 0, 0);
+ hw->dlstate = HFA384x_DLSTATE_DISABLED;
+
+ return 0;
+}
+
+/*----------------------------------------------------------------
+* hfa384x_drvr_flashdl_write
+*
+* Performs a FLASH download of a chunk of data. First checks to see
+* that we're in the FLASH download state, then sets the download
+* mode, uses the aux functions to 1) copy the data to the flash
+* buffer, 2) sets the download 'write flash' mode, 3) readback and
+* compare. Lather rinse, repeat as many times an necessary to get
+* all the given data into flash.
+* When all data has been written using this function (possibly
+* repeatedly), call drvr_flashdl_disable() to end the download state
+* and restart the MAC.
+*
+* Arguments:
+* hw device structure
+* daddr Card address to write to. (host order)
+* buf Ptr to data to write.
+* len Length of data (host order).
+*
+* Returns:
+* 0 success
+* >0 f/w reported error - f/w status code
+* <0 driver reported error
+*
+* Side effects:
+*
+* Call context:
+* process
+----------------------------------------------------------------*/
+int hfa384x_drvr_flashdl_write(hfa384x_t *hw, u32 daddr, void *buf, u32 len)
+{
+ int result = 0;
+ u32 dlbufaddr;
+ int nburns;
+ u32 burnlen;
+ u32 burndaddr;
+ u16 burnlo;
+ u16 burnhi;
+ int nwrites;
+ u8 *writebuf;
+ u16 writepage;
+ u16 writeoffset;
+ u32 writelen;
+ int i;
+ int j;
+
+ pr_debug("daddr=0x%08x len=%d\n", daddr, len);
+
+ /* Check that we're in the flash download state */
+ if (hw->dlstate != HFA384x_DLSTATE_FLASHENABLED)
+ return -EINVAL;
+
+ netdev_info(hw->wlandev->netdev,
+ "Download %d bytes to flash @0x%06x\n", len, daddr);
+
+ /* Convert to flat address for arithmetic */
+ /* NOTE: dlbuffer RID stores the address in AUX format */
+ dlbufaddr =
+ HFA384x_ADDR_AUX_MKFLAT(hw->bufinfo.page, hw->bufinfo.offset);
+ pr_debug("dlbuf.page=0x%04x dlbuf.offset=0x%04x dlbufaddr=0x%08x\n",
+ hw->bufinfo.page, hw->bufinfo.offset, dlbufaddr);
+ /* Calculations to determine how many fills of the dlbuffer to do
+ * and how many USB wmemreq's to do for each fill. At this point
+ * in time, the dlbuffer size and the wmemreq size are the same.
+ * Therefore, nwrites should always be 1. The extra complexity
+ * here is a hedge against future changes.
+ */
+
+ /* Figure out how many times to do the flash programming */
+ nburns = len / hw->bufinfo.len;
+ nburns += (len % hw->bufinfo.len) ? 1 : 0;
+
+ /* For each flash program cycle, how many USB wmemreq's are needed? */
+ nwrites = hw->bufinfo.len / HFA384x_USB_RWMEM_MAXLEN;
+ nwrites += (hw->bufinfo.len % HFA384x_USB_RWMEM_MAXLEN) ? 1 : 0;
+
+ /* For each burn */
+ for (i = 0; i < nburns; i++) {
+ /* Get the dest address and len */
+ burnlen = (len - (hw->bufinfo.len * i)) > hw->bufinfo.len ?
+ hw->bufinfo.len : (len - (hw->bufinfo.len * i));
+ burndaddr = daddr + (hw->bufinfo.len * i);
+ burnlo = HFA384x_ADDR_CMD_MKOFF(burndaddr);
+ burnhi = HFA384x_ADDR_CMD_MKPAGE(burndaddr);
+
+ netdev_info(hw->wlandev->netdev, "Writing %d bytes to flash @0x%06x\n",
+ burnlen, burndaddr);
+
+ /* Set the download mode */
+ result = hfa384x_cmd_download(hw, HFA384x_PROGMODE_NV,
+ burnlo, burnhi, burnlen);
+ if (result) {
+ netdev_err(hw->wlandev->netdev,
+ "download(NV,lo=%x,hi=%x,len=%x) cmd failed, result=%d. Aborting d/l\n",
+ burnlo, burnhi, burnlen, result);
+ goto exit_proc;
+ }
+
+ /* copy the data to the flash download buffer */
+ for (j = 0; j < nwrites; j++) {
+ writebuf = buf +
+ (i * hw->bufinfo.len) +
+ (j * HFA384x_USB_RWMEM_MAXLEN);
+
+ writepage = HFA384x_ADDR_CMD_MKPAGE(dlbufaddr +
+ (j * HFA384x_USB_RWMEM_MAXLEN));
+ writeoffset = HFA384x_ADDR_CMD_MKOFF(dlbufaddr +
+ (j * HFA384x_USB_RWMEM_MAXLEN));
+
+ writelen = burnlen - (j * HFA384x_USB_RWMEM_MAXLEN);
+ writelen = writelen > HFA384x_USB_RWMEM_MAXLEN ?
+ HFA384x_USB_RWMEM_MAXLEN : writelen;
+
+ result = hfa384x_dowmem_wait(hw,
+ writepage,
+ writeoffset,
+ writebuf, writelen);
+ }
+
+ /* set the download 'write flash' mode */
+ result = hfa384x_cmd_download(hw,
+ HFA384x_PROGMODE_NVWRITE,
+ 0, 0, 0);
+ if (result) {
+ netdev_err(hw->wlandev->netdev,
+ "download(NVWRITE,lo=%x,hi=%x,len=%x) cmd failed, result=%d. Aborting d/l\n",
+ burnlo, burnhi, burnlen, result);
+ goto exit_proc;
+ }
+
+ /* TODO: We really should do a readback and compare. */
+ }
+
+exit_proc:
+
+ /* Leave the firmware in the 'post-prog' mode. flashdl_disable will */
+ /* actually disable programming mode. Remember, that will cause the */
+ /* the firmware to effectively reset itself. */
+
+ return result;
+}
+
+/*----------------------------------------------------------------
+* hfa384x_drvr_getconfig
+*
+* Performs the sequence necessary to read a config/info item.
+*
+* Arguments:
+* hw device structure
+* rid config/info record id (host order)
+* buf host side record buffer. Upon return it will
+* contain the body portion of the record (minus the
+* RID and len).
+* len buffer length (in bytes, should match record length)
+*
+* Returns:
+* 0 success
+* >0 f/w reported error - f/w status code
+* <0 driver reported error
+* -ENODATA length mismatch between argument and retrieved
+* record.
+*
+* Side effects:
+*
+* Call context:
+* process
+----------------------------------------------------------------*/
+int hfa384x_drvr_getconfig(hfa384x_t *hw, u16 rid, void *buf, u16 len)
+{
+ return hfa384x_dorrid_wait(hw, rid, buf, len);
+}
+
+/*----------------------------------------------------------------
+ * hfa384x_drvr_getconfig_async
+ *
+ * Performs the sequence necessary to perform an async read of
+ * of a config/info item.
+ *
+ * Arguments:
+ * hw device structure
+ * rid config/info record id (host order)
+ * buf host side record buffer. Upon return it will
+ * contain the body portion of the record (minus the
+ * RID and len).
+ * len buffer length (in bytes, should match record length)
+ * cbfn caller supplied callback, called when the command
+ * is done (successful or not).
+ * cbfndata pointer to some caller supplied data that will be
+ * passed in as an argument to the cbfn.
+ *
+ * Returns:
+ * nothing the cbfn gets a status argument identifying if
+ * any errors occur.
+ * Side effects:
+ * Queues an hfa384x_usbcmd_t for subsequent execution.
+ *
+ * Call context:
+ * Any
+ ----------------------------------------------------------------*/
+int
+hfa384x_drvr_getconfig_async(hfa384x_t *hw,
+ u16 rid, ctlx_usercb_t usercb, void *usercb_data)
+{
+ return hfa384x_dorrid_async(hw, rid, NULL, 0,
+ hfa384x_cb_rrid, usercb, usercb_data);
+}
+
+/*----------------------------------------------------------------
+ * hfa384x_drvr_setconfig_async
+ *
+ * Performs the sequence necessary to write a config/info item.
+ *
+ * Arguments:
+ * hw device structure
+ * rid config/info record id (in host order)
+ * buf host side record buffer
+ * len buffer length (in bytes)
+ * usercb completion callback
+ * usercb_data completion callback argument
+ *
+ * Returns:
+ * 0 success
+ * >0 f/w reported error - f/w status code
+ * <0 driver reported error
+ *
+ * Side effects:
+ *
+ * Call context:
+ * process
+ ----------------------------------------------------------------*/
+int
+hfa384x_drvr_setconfig_async(hfa384x_t *hw,
+ u16 rid,
+ void *buf,
+ u16 len, ctlx_usercb_t usercb, void *usercb_data)
+{
+ return hfa384x_dowrid_async(hw, rid, buf, len,
+ hfa384x_cb_status, usercb, usercb_data);
+}
+
+/*----------------------------------------------------------------
+* hfa384x_drvr_ramdl_disable
+*
+* Ends the ram download state.
+*
+* Arguments:
+* hw device structure
+*
+* Returns:
+* 0 success
+* >0 f/w reported error - f/w status code
+* <0 driver reported error
+*
+* Side effects:
+*
+* Call context:
+* process
+----------------------------------------------------------------*/
+int hfa384x_drvr_ramdl_disable(hfa384x_t *hw)
+{
+ /* Check that we're already in the download state */
+ if (hw->dlstate != HFA384x_DLSTATE_RAMENABLED)
+ return -EINVAL;
+
+ pr_debug("ramdl_disable()\n");
+
+ /* There isn't much we can do at this point, so I don't */
+ /* bother w/ the return value */
+ hfa384x_cmd_download(hw, HFA384x_PROGMODE_DISABLE, 0, 0, 0);
+ hw->dlstate = HFA384x_DLSTATE_DISABLED;
+
+ return 0;
+}
+
+/*----------------------------------------------------------------
+* hfa384x_drvr_ramdl_enable
+*
+* Begins the ram download state. Checks to see that we're not
+* already in a download state and that a port isn't enabled.
+* Sets the download state and calls cmd_download with the
+* ENABLE_VOLATILE subcommand and the exeaddr argument.
+*
+* Arguments:
+* hw device structure
+* exeaddr the card execution address that will be
+* jumped to when ramdl_disable() is called
+* (host order).
+*
+* Returns:
+* 0 success
+* >0 f/w reported error - f/w status code
+* <0 driver reported error
+*
+* Side effects:
+*
+* Call context:
+* process
+----------------------------------------------------------------*/
+int hfa384x_drvr_ramdl_enable(hfa384x_t *hw, u32 exeaddr)
+{
+ int result = 0;
+ u16 lowaddr;
+ u16 hiaddr;
+ int i;
+
+ /* Check that a port isn't active */
+ for (i = 0; i < HFA384x_PORTID_MAX; i++) {
+ if (hw->port_enabled[i]) {
+ netdev_err(hw->wlandev->netdev,
+ "Can't download with a macport enabled.\n");
+ return -EINVAL;
+ }
+ }
+
+ /* Check that we're not already in a download state */
+ if (hw->dlstate != HFA384x_DLSTATE_DISABLED) {
+ netdev_err(hw->wlandev->netdev, "Download state not disabled.\n");
+ return -EINVAL;
+ }
+
+ pr_debug("ramdl_enable, exeaddr=0x%08x\n", exeaddr);
+
+ /* Call the download(1,addr) function */
+ lowaddr = HFA384x_ADDR_CMD_MKOFF(exeaddr);
+ hiaddr = HFA384x_ADDR_CMD_MKPAGE(exeaddr);
+
+ result = hfa384x_cmd_download(hw, HFA384x_PROGMODE_RAM,
+ lowaddr, hiaddr, 0);
+
+ if (result == 0) {
+ /* Set the download state */
+ hw->dlstate = HFA384x_DLSTATE_RAMENABLED;
+ } else {
+ pr_debug("cmd_download(0x%04x, 0x%04x) failed, result=%d.\n",
+ lowaddr, hiaddr, result);
+ }
+
+ return result;
+}
+
+/*----------------------------------------------------------------
+* hfa384x_drvr_ramdl_write
+*
+* Performs a RAM download of a chunk of data. First checks to see
+* that we're in the RAM download state, then uses the [read|write]mem USB
+* commands to 1) copy the data, 2) readback and compare. The download
+* state is unaffected. When all data has been written using
+* this function, call drvr_ramdl_disable() to end the download state
+* and restart the MAC.
+*
+* Arguments:
+* hw device structure
+* daddr Card address to write to. (host order)
+* buf Ptr to data to write.
+* len Length of data (host order).
+*
+* Returns:
+* 0 success
+* >0 f/w reported error - f/w status code
+* <0 driver reported error
+*
+* Side effects:
+*
+* Call context:
+* process
+----------------------------------------------------------------*/
+int hfa384x_drvr_ramdl_write(hfa384x_t *hw, u32 daddr, void *buf, u32 len)
+{
+ int result = 0;
+ int nwrites;
+ u8 *data = buf;
+ int i;
+ u32 curraddr;
+ u16 currpage;
+ u16 curroffset;
+ u16 currlen;
+
+ /* Check that we're in the ram download state */
+ if (hw->dlstate != HFA384x_DLSTATE_RAMENABLED)
+ return -EINVAL;
+
+ netdev_info(hw->wlandev->netdev, "Writing %d bytes to ram @0x%06x\n",
+ len, daddr);
+
+ /* How many dowmem calls? */
+ nwrites = len / HFA384x_USB_RWMEM_MAXLEN;
+ nwrites += len % HFA384x_USB_RWMEM_MAXLEN ? 1 : 0;
+
+ /* Do blocking wmem's */
+ for (i = 0; i < nwrites; i++) {
+ /* make address args */
+ curraddr = daddr + (i * HFA384x_USB_RWMEM_MAXLEN);
+ currpage = HFA384x_ADDR_CMD_MKPAGE(curraddr);
+ curroffset = HFA384x_ADDR_CMD_MKOFF(curraddr);
+ currlen = len - (i * HFA384x_USB_RWMEM_MAXLEN);
+ if (currlen > HFA384x_USB_RWMEM_MAXLEN)
+ currlen = HFA384x_USB_RWMEM_MAXLEN;
+
+ /* Do blocking ctlx */
+ result = hfa384x_dowmem_wait(hw,
+ currpage,
+ curroffset,
+ data +
+ (i * HFA384x_USB_RWMEM_MAXLEN),
+ currlen);
+
+ if (result)
+ break;
+
+ /* TODO: We really should have a readback. */
+ }
+
+ return result;
+}
+
+/*----------------------------------------------------------------
+* hfa384x_drvr_readpda
+*
+* Performs the sequence to read the PDA space. Note there is no
+* drvr_writepda() function. Writing a PDA is
+* generally implemented by a calling component via calls to
+* cmd_download and writing to the flash download buffer via the
+* aux regs.
+*
+* Arguments:
+* hw device structure
+* buf buffer to store PDA in
+* len buffer length
+*
+* Returns:
+* 0 success
+* >0 f/w reported error - f/w status code
+* <0 driver reported error
+* -ETIMEDOUT timeout waiting for the cmd regs to become
+* available, or waiting for the control reg
+* to indicate the Aux port is enabled.
+* -ENODATA the buffer does NOT contain a valid PDA.
+* Either the card PDA is bad, or the auxdata
+* reads are giving us garbage.
+
+*
+* Side effects:
+*
+* Call context:
+* process or non-card interrupt.
+----------------------------------------------------------------*/
+int hfa384x_drvr_readpda(hfa384x_t *hw, void *buf, unsigned int len)
+{
+ int result = 0;
+ u16 *pda = buf;
+ int pdaok = 0;
+ int morepdrs = 1;
+ int currpdr = 0; /* word offset of the current pdr */
+ size_t i;
+ u16 pdrlen; /* pdr length in bytes, host order */
+ u16 pdrcode; /* pdr code, host order */
+ u16 currpage;
+ u16 curroffset;
+ struct pdaloc {
+ u32 cardaddr;
+ u16 auxctl;
+ } pdaloc[] = {
+ {
+ HFA3842_PDA_BASE, 0}, {
+ HFA3841_PDA_BASE, 0}, {
+ HFA3841_PDA_BOGUS_BASE, 0}
+ };
+
+ /* Read the pda from each known address. */
+ for (i = 0; i < ARRAY_SIZE(pdaloc); i++) {
+ /* Make address */
+ currpage = HFA384x_ADDR_CMD_MKPAGE(pdaloc[i].cardaddr);
+ curroffset = HFA384x_ADDR_CMD_MKOFF(pdaloc[i].cardaddr);
+
+ /* units of bytes */
+ result = hfa384x_dormem_wait(hw, currpage, curroffset, buf,
+ len);
+
+ if (result) {
+ netdev_warn(hw->wlandev->netdev,
+ "Read from index %zd failed, continuing\n",
+ i);
+ continue;
+ }
+
+ /* Test for garbage */
+ pdaok = 1; /* initially assume good */
+ morepdrs = 1;
+ while (pdaok && morepdrs) {
+ pdrlen = le16_to_cpu(pda[currpdr]) * 2;
+ pdrcode = le16_to_cpu(pda[currpdr + 1]);
+ /* Test the record length */
+ if (pdrlen > HFA384x_PDR_LEN_MAX || pdrlen == 0) {
+ netdev_err(hw->wlandev->netdev,
+ "pdrlen invalid=%d\n", pdrlen);
+ pdaok = 0;
+ break;
+ }
+ /* Test the code */
+ if (!hfa384x_isgood_pdrcode(pdrcode)) {
+ netdev_err(hw->wlandev->netdev, "pdrcode invalid=%d\n",
+ pdrcode);
+ pdaok = 0;
+ break;
+ }
+ /* Test for completion */
+ if (pdrcode == HFA384x_PDR_END_OF_PDA)
+ morepdrs = 0;
+
+ /* Move to the next pdr (if necessary) */
+ if (morepdrs) {
+ /* note the access to pda[], need words here */
+ currpdr += le16_to_cpu(pda[currpdr]) + 1;
+ }
+ }
+ if (pdaok) {
+ netdev_info(hw->wlandev->netdev,
+ "PDA Read from 0x%08x in %s space.\n",
+ pdaloc[i].cardaddr,
+ pdaloc[i].auxctl == 0 ? "EXTDS" :
+ pdaloc[i].auxctl == 1 ? "NV" :
+ pdaloc[i].auxctl == 2 ? "PHY" :
+ pdaloc[i].auxctl == 3 ? "ICSRAM" :
+ "<bogus auxctl>");
+ break;
+ }
+ }
+ result = pdaok ? 0 : -ENODATA;
+
+ if (result)
+ pr_debug("Failure: pda is not okay\n");
+
+ return result;
+}
+
+/*----------------------------------------------------------------
+* hfa384x_drvr_setconfig
+*
+* Performs the sequence necessary to write a config/info item.
+*
+* Arguments:
+* hw device structure
+* rid config/info record id (in host order)
+* buf host side record buffer
+* len buffer length (in bytes)
+*
+* Returns:
+* 0 success
+* >0 f/w reported error - f/w status code
+* <0 driver reported error
+*
+* Side effects:
+*
+* Call context:
+* process
+----------------------------------------------------------------*/
+int hfa384x_drvr_setconfig(hfa384x_t *hw, u16 rid, void *buf, u16 len)
+{
+ return hfa384x_dowrid_wait(hw, rid, buf, len);
+}
+
+/*----------------------------------------------------------------
+* hfa384x_drvr_start
+*
+* Issues the MAC initialize command, sets up some data structures,
+* and enables the interrupts. After this function completes, the
+* low-level stuff should be ready for any/all commands.
+*
+* Arguments:
+* hw device structure
+* Returns:
+* 0 success
+* >0 f/w reported error - f/w status code
+* <0 driver reported error
+*
+* Side effects:
+*
+* Call context:
+* process
+----------------------------------------------------------------*/
+
+int hfa384x_drvr_start(hfa384x_t *hw)
+{
+ int result, result1, result2;
+ u16 status;
+
+ might_sleep();
+
+ /* Clear endpoint stalls - but only do this if the endpoint
+ * is showing a stall status. Some prism2 cards seem to behave
+ * badly if a clear_halt is called when the endpoint is already
+ * ok
+ */
+ result =
+ usb_get_status(hw->usb, USB_RECIP_ENDPOINT, hw->endp_in, &status);
+ if (result < 0) {
+ netdev_err(hw->wlandev->netdev, "Cannot get bulk in endpoint status.\n");
+ goto done;
+ }
+ if ((status == 1) && usb_clear_halt(hw->usb, hw->endp_in))
+ netdev_err(hw->wlandev->netdev, "Failed to reset bulk in endpoint.\n");
+
+ result =
+ usb_get_status(hw->usb, USB_RECIP_ENDPOINT, hw->endp_out, &status);
+ if (result < 0) {
+ netdev_err(hw->wlandev->netdev, "Cannot get bulk out endpoint status.\n");
+ goto done;
+ }
+ if ((status == 1) && usb_clear_halt(hw->usb, hw->endp_out))
+ netdev_err(hw->wlandev->netdev, "Failed to reset bulk out endpoint.\n");
+
+ /* Synchronous unlink, in case we're trying to restart the driver */
+ usb_kill_urb(&hw->rx_urb);
+
+ /* Post the IN urb */
+ result = submit_rx_urb(hw, GFP_KERNEL);
+ if (result != 0) {
+ netdev_err(hw->wlandev->netdev,
+ "Fatal, failed to submit RX URB, result=%d\n",
+ result);
+ goto done;
+ }
+
+ /* Call initialize twice, with a 1 second sleep in between.
+ * This is a nasty work-around since many prism2 cards seem to
+ * need time to settle after an init from cold. The second
+ * call to initialize in theory is not necessary - but we call
+ * it anyway as a double insurance policy:
+ * 1) If the first init should fail, the second may well succeed
+ * and the card can still be used
+ * 2) It helps ensures all is well with the card after the first
+ * init and settle time.
+ */
+ result1 = hfa384x_cmd_initialize(hw);
+ msleep(1000);
+ result = hfa384x_cmd_initialize(hw);
+ result2 = result;
+ if (result1 != 0) {
+ if (result2 != 0) {
+ netdev_err(hw->wlandev->netdev,
+ "cmd_initialize() failed on two attempts, results %d and %d\n",
+ result1, result2);
+ usb_kill_urb(&hw->rx_urb);
+ goto done;
+ } else {
+ pr_debug("First cmd_initialize() failed (result %d),\n",
+ result1);
+ pr_debug("but second attempt succeeded. All should be ok\n");
+ }
+ } else if (result2 != 0) {
+ netdev_warn(hw->wlandev->netdev, "First cmd_initialize() succeeded, but second attempt failed (result=%d)\n",
+ result2);
+ netdev_warn(hw->wlandev->netdev,
+ "Most likely the card will be functional\n");
+ goto done;
+ }
+
+ hw->state = HFA384x_STATE_RUNNING;
+
+done:
+ return result;
+}
+
+/*----------------------------------------------------------------
+* hfa384x_drvr_stop
+*
+* Shuts down the MAC to the point where it is safe to unload the
+* driver. Any subsystem that may be holding a data or function
+* ptr into the driver must be cleared/deinitialized.
+*
+* Arguments:
+* hw device structure
+* Returns:
+* 0 success
+* >0 f/w reported error - f/w status code
+* <0 driver reported error
+*
+* Side effects:
+*
+* Call context:
+* process
+----------------------------------------------------------------*/
+int hfa384x_drvr_stop(hfa384x_t *hw)
+{
+ int i;
+
+ might_sleep();
+
+ /* There's no need for spinlocks here. The USB "disconnect"
+ * function sets this "removed" flag and then calls us.
+ */
+ if (!hw->wlandev->hwremoved) {
+ /* Call initialize to leave the MAC in its 'reset' state */
+ hfa384x_cmd_initialize(hw);
+
+ /* Cancel the rxurb */
+ usb_kill_urb(&hw->rx_urb);
+ }
+
+ hw->link_status = HFA384x_LINK_NOTCONNECTED;
+ hw->state = HFA384x_STATE_INIT;
+
+ del_timer_sync(&hw->commsqual_timer);
+
+ /* Clear all the port status */
+ for (i = 0; i < HFA384x_NUMPORTS_MAX; i++)
+ hw->port_enabled[i] = 0;
+
+ return 0;
+}
+
+/*----------------------------------------------------------------
+* hfa384x_drvr_txframe
+*
+* Takes a frame from prism2sta and queues it for transmission.
+*
+* Arguments:
+* hw device structure
+* skb packet buffer struct. Contains an 802.11
+* data frame.
+* p80211_hdr points to the 802.11 header for the packet.
+* Returns:
+* 0 Success and more buffs available
+* 1 Success but no more buffs
+* 2 Allocation failure
+* 4 Buffer full or queue busy
+*
+* Side effects:
+*
+* Call context:
+* interrupt
+----------------------------------------------------------------*/
+int hfa384x_drvr_txframe(hfa384x_t *hw, struct sk_buff *skb,
+ union p80211_hdr *p80211_hdr,
+ struct p80211_metawep *p80211_wep)
+{
+ int usbpktlen = sizeof(hfa384x_tx_frame_t);
+ int result;
+ int ret;
+ char *ptr;
+
+ if (hw->tx_urb.status == -EINPROGRESS) {
+ netdev_warn(hw->wlandev->netdev, "TX URB already in use\n");
+ result = 3;
+ goto exit;
+ }
+
+ /* Build Tx frame structure */
+ /* Set up the control field */
+ memset(&hw->txbuff.txfrm.desc, 0, sizeof(hw->txbuff.txfrm.desc));
+
+ /* Setup the usb type field */
+ hw->txbuff.type = cpu_to_le16(HFA384x_USB_TXFRM);
+
+ /* Set up the sw_support field to identify this frame */
+ hw->txbuff.txfrm.desc.sw_support = 0x0123;
+
+/* Tx complete and Tx exception disable per dleach. Might be causing
+ * buf depletion
+ */
+/* #define DOEXC SLP -- doboth breaks horribly under load, doexc less so. */
+#if defined(DOBOTH)
+ hw->txbuff.txfrm.desc.tx_control =
+ HFA384x_TX_MACPORT_SET(0) | HFA384x_TX_STRUCTYPE_SET(1) |
+ HFA384x_TX_TXEX_SET(1) | HFA384x_TX_TXOK_SET(1);
+#elif defined(DOEXC)
+ hw->txbuff.txfrm.desc.tx_control =
+ HFA384x_TX_MACPORT_SET(0) | HFA384x_TX_STRUCTYPE_SET(1) |
+ HFA384x_TX_TXEX_SET(1) | HFA384x_TX_TXOK_SET(0);
+#else
+ hw->txbuff.txfrm.desc.tx_control =
+ HFA384x_TX_MACPORT_SET(0) | HFA384x_TX_STRUCTYPE_SET(1) |
+ HFA384x_TX_TXEX_SET(0) | HFA384x_TX_TXOK_SET(0);
+#endif
+ hw->txbuff.txfrm.desc.tx_control =
+ cpu_to_le16(hw->txbuff.txfrm.desc.tx_control);
+
+ /* copy the header over to the txdesc */
+ memcpy(&(hw->txbuff.txfrm.desc.frame_control), p80211_hdr,
+ sizeof(union p80211_hdr));
+
+ /* if we're using host WEP, increase size by IV+ICV */
+ if (p80211_wep->data) {
+ hw->txbuff.txfrm.desc.data_len = cpu_to_le16(skb->len + 8);
+ usbpktlen += 8;
+ } else {
+ hw->txbuff.txfrm.desc.data_len = cpu_to_le16(skb->len);
+ }
+
+ usbpktlen += skb->len;
+
+ /* copy over the WEP IV if we are using host WEP */
+ ptr = hw->txbuff.txfrm.data;
+ if (p80211_wep->data) {
+ memcpy(ptr, p80211_wep->iv, sizeof(p80211_wep->iv));
+ ptr += sizeof(p80211_wep->iv);
+ memcpy(ptr, p80211_wep->data, skb->len);
+ } else {
+ memcpy(ptr, skb->data, skb->len);
+ }
+ /* copy over the packet data */
+ ptr += skb->len;
+
+ /* copy over the WEP ICV if we are using host WEP */
+ if (p80211_wep->data)
+ memcpy(ptr, p80211_wep->icv, sizeof(p80211_wep->icv));
+
+ /* Send the USB packet */
+ usb_fill_bulk_urb(&(hw->tx_urb), hw->usb,
+ hw->endp_out,
+ &(hw->txbuff), ROUNDUP64(usbpktlen),
+ hfa384x_usbout_callback, hw->wlandev);
+ hw->tx_urb.transfer_flags |= USB_QUEUE_BULK;
+
+ result = 1;
+ ret = submit_tx_urb(hw, &hw->tx_urb, GFP_ATOMIC);
+ if (ret != 0) {
+ netdev_err(hw->wlandev->netdev,
+ "submit_tx_urb() failed, error=%d\n", ret);
+ result = 3;
+ }
+
+exit:
+ return result;
+}
+
+void hfa384x_tx_timeout(wlandevice_t *wlandev)
+{
+ hfa384x_t *hw = wlandev->priv;
+ unsigned long flags;
+
+ spin_lock_irqsave(&hw->ctlxq.lock, flags);
+
+ if (!hw->wlandev->hwremoved) {
+ int sched;
+
+ sched = !test_and_set_bit(WORK_TX_HALT, &hw->usb_flags);
+ sched |= !test_and_set_bit(WORK_RX_HALT, &hw->usb_flags);
+ if (sched)
+ schedule_work(&hw->usb_work);
+ }
+
+ spin_unlock_irqrestore(&hw->ctlxq.lock, flags);
+}
+
+/*----------------------------------------------------------------
+* hfa384x_usbctlx_reaper_task
+*
+* Tasklet to delete dead CTLX objects
+*
+* Arguments:
+* data ptr to a hfa384x_t
+*
+* Returns:
+*
+* Call context:
+* Interrupt
+----------------------------------------------------------------*/
+static void hfa384x_usbctlx_reaper_task(unsigned long data)
+{
+ hfa384x_t *hw = (hfa384x_t *)data;
+ struct list_head *entry;
+ struct list_head *temp;
+ unsigned long flags;
+
+ spin_lock_irqsave(&hw->ctlxq.lock, flags);
+
+ /* This list is guaranteed to be empty if someone
+ * has unplugged the adapter.
+ */
+ list_for_each_safe(entry, temp, &hw->ctlxq.reapable) {
+ hfa384x_usbctlx_t *ctlx;
+
+ ctlx = list_entry(entry, hfa384x_usbctlx_t, list);
+ list_del(&ctlx->list);
+ kfree(ctlx);
+ }
+
+ spin_unlock_irqrestore(&hw->ctlxq.lock, flags);
+}
+
+/*----------------------------------------------------------------
+* hfa384x_usbctlx_completion_task
+*
+* Tasklet to call completion handlers for returned CTLXs
+*
+* Arguments:
+* data ptr to hfa384x_t
+*
+* Returns:
+* Nothing
+*
+* Call context:
+* Interrupt
+----------------------------------------------------------------*/
+static void hfa384x_usbctlx_completion_task(unsigned long data)
+{
+ hfa384x_t *hw = (hfa384x_t *)data;
+ struct list_head *entry;
+ struct list_head *temp;
+ unsigned long flags;
+
+ int reap = 0;
+
+ spin_lock_irqsave(&hw->ctlxq.lock, flags);
+
+ /* This list is guaranteed to be empty if someone
+ * has unplugged the adapter ...
+ */
+ list_for_each_safe(entry, temp, &hw->ctlxq.completing) {
+ hfa384x_usbctlx_t *ctlx;
+
+ ctlx = list_entry(entry, hfa384x_usbctlx_t, list);
+
+ /* Call the completion function that this
+ * command was assigned, assuming it has one.
+ */
+ if (ctlx->cmdcb != NULL) {
+ spin_unlock_irqrestore(&hw->ctlxq.lock, flags);
+ ctlx->cmdcb(hw, ctlx);
+ spin_lock_irqsave(&hw->ctlxq.lock, flags);
+
+ /* Make sure we don't try and complete
+ * this CTLX more than once!
+ */
+ ctlx->cmdcb = NULL;
+
+ /* Did someone yank the adapter out
+ * while our list was (briefly) unlocked?
+ */
+ if (hw->wlandev->hwremoved) {
+ reap = 0;
+ break;
+ }
+ }
+
+ /*
+ * "Reapable" CTLXs are ones which don't have any
+ * threads waiting for them to die. Hence they must
+ * be delivered to The Reaper!
+ */
+ if (ctlx->reapable) {
+ /* Move the CTLX off the "completing" list (hopefully)
+ * on to the "reapable" list where the reaper task
+ * can find it. And "reapable" means that this CTLX
+ * isn't sitting on a wait-queue somewhere.
+ */
+ list_move_tail(&ctlx->list, &hw->ctlxq.reapable);
+ reap = 1;
+ }
+
+ complete(&ctlx->done);
+ }
+ spin_unlock_irqrestore(&hw->ctlxq.lock, flags);
+
+ if (reap)
+ tasklet_schedule(&hw->reaper_bh);
+}
+
+/*----------------------------------------------------------------
+* unlocked_usbctlx_cancel_async
+*
+* Mark the CTLX dead asynchronously, and ensure that the
+* next command on the queue is run afterwards.
+*
+* Arguments:
+* hw ptr to the hfa384x_t structure
+* ctlx ptr to a CTLX structure
+*
+* Returns:
+* 0 the CTLX's URB is inactive
+* -EINPROGRESS the URB is currently being unlinked
+*
+* Call context:
+* Either process or interrupt, but presumably interrupt
+----------------------------------------------------------------*/
+static int unlocked_usbctlx_cancel_async(hfa384x_t *hw,
+ hfa384x_usbctlx_t *ctlx)
+{
+ int ret;
+
+ /*
+ * Try to delete the URB containing our request packet.
+ * If we succeed, then its completion handler will be
+ * called with a status of -ECONNRESET.
+ */
+ hw->ctlx_urb.transfer_flags |= URB_ASYNC_UNLINK;
+ ret = usb_unlink_urb(&hw->ctlx_urb);
+
+ if (ret != -EINPROGRESS) {
+ /*
+ * The OUT URB had either already completed
+ * or was still in the pending queue, so the
+ * URB's completion function will not be called.
+ * We will have to complete the CTLX ourselves.
+ */
+ ctlx->state = CTLX_REQ_FAILED;
+ unlocked_usbctlx_complete(hw, ctlx);
+ ret = 0;
+ }
+
+ return ret;
+}
+
+/*----------------------------------------------------------------
+* unlocked_usbctlx_complete
+*
+* A CTLX has completed. It may have been successful, it may not
+* have been. At this point, the CTLX should be quiescent. The URBs
+* aren't active and the timers should have been stopped.
+*
+* The CTLX is migrated to the "completing" queue, and the completing
+* tasklet is scheduled.
+*
+* Arguments:
+* hw ptr to a hfa384x_t structure
+* ctlx ptr to a ctlx structure
+*
+* Returns:
+* nothing
+*
+* Side effects:
+*
+* Call context:
+* Either, assume interrupt
+----------------------------------------------------------------*/
+static void unlocked_usbctlx_complete(hfa384x_t *hw, hfa384x_usbctlx_t *ctlx)
+{
+ /* Timers have been stopped, and ctlx should be in
+ * a terminal state. Retire it from the "active"
+ * queue.
+ */
+ list_move_tail(&ctlx->list, &hw->ctlxq.completing);
+ tasklet_schedule(&hw->completion_bh);
+
+ switch (ctlx->state) {
+ case CTLX_COMPLETE:
+ case CTLX_REQ_FAILED:
+ /* This are the correct terminating states. */
+ break;
+
+ default:
+ netdev_err(hw->wlandev->netdev, "CTLX[%d] not in a terminating state(%s)\n",
+ le16_to_cpu(ctlx->outbuf.type),
+ ctlxstr(ctlx->state));
+ break;
+ } /* switch */
+}
+
+/*----------------------------------------------------------------
+* hfa384x_usbctlxq_run
+*
+* Checks to see if the head item is running. If not, starts it.
+*
+* Arguments:
+* hw ptr to hfa384x_t
+*
+* Returns:
+* nothing
+*
+* Side effects:
+*
+* Call context:
+* any
+----------------------------------------------------------------*/
+static void hfa384x_usbctlxq_run(hfa384x_t *hw)
+{
+ unsigned long flags;
+
+ /* acquire lock */
+ spin_lock_irqsave(&hw->ctlxq.lock, flags);
+
+ /* Only one active CTLX at any one time, because there's no
+ * other (reliable) way to match the response URB to the
+ * correct CTLX.
+ *
+ * Don't touch any of these CTLXs if the hardware
+ * has been removed or the USB subsystem is stalled.
+ */
+ if (!list_empty(&hw->ctlxq.active) ||
+ test_bit(WORK_TX_HALT, &hw->usb_flags) || hw->wlandev->hwremoved)
+ goto unlock;
+
+ while (!list_empty(&hw->ctlxq.pending)) {
+ hfa384x_usbctlx_t *head;
+ int result;
+
+ /* This is the first pending command */
+ head = list_entry(hw->ctlxq.pending.next,
+ hfa384x_usbctlx_t, list);
+
+ /* We need to split this off to avoid a race condition */
+ list_move_tail(&head->list, &hw->ctlxq.active);
+
+ /* Fill the out packet */
+ usb_fill_bulk_urb(&(hw->ctlx_urb), hw->usb,
+ hw->endp_out,
+ &(head->outbuf), ROUNDUP64(head->outbufsize),
+ hfa384x_ctlxout_callback, hw);
+ hw->ctlx_urb.transfer_flags |= USB_QUEUE_BULK;
+
+ /* Now submit the URB and update the CTLX's state */
+ result = SUBMIT_URB(&hw->ctlx_urb, GFP_ATOMIC);
+ if (result == 0) {
+ /* This CTLX is now running on the active queue */
+ head->state = CTLX_REQ_SUBMITTED;
+
+ /* Start the OUT wait timer */
+ hw->req_timer_done = 0;
+ hw->reqtimer.expires = jiffies + HZ;
+ add_timer(&hw->reqtimer);
+
+ /* Start the IN wait timer */
+ hw->resp_timer_done = 0;
+ hw->resptimer.expires = jiffies + 2 * HZ;
+ add_timer(&hw->resptimer);
+
+ break;
+ }
+
+ if (result == -EPIPE) {
+ /* The OUT pipe needs resetting, so put
+ * this CTLX back in the "pending" queue
+ * and schedule a reset ...
+ */
+ netdev_warn(hw->wlandev->netdev,
+ "%s tx pipe stalled: requesting reset\n",
+ hw->wlandev->netdev->name);
+ list_move(&head->list, &hw->ctlxq.pending);
+ set_bit(WORK_TX_HALT, &hw->usb_flags);
+ schedule_work(&hw->usb_work);
+ break;
+ }
+
+ if (result == -ESHUTDOWN) {
+ netdev_warn(hw->wlandev->netdev, "%s urb shutdown!\n",
+ hw->wlandev->netdev->name);
+ break;
+ }
+
+ netdev_err(hw->wlandev->netdev, "Failed to submit CTLX[%d]: error=%d\n",
+ le16_to_cpu(head->outbuf.type), result);
+ unlocked_usbctlx_complete(hw, head);
+ } /* while */
+
+unlock:
+ spin_unlock_irqrestore(&hw->ctlxq.lock, flags);
+}
+
+/*----------------------------------------------------------------
+* hfa384x_usbin_callback
+*
+* Callback for URBs on the BULKIN endpoint.
+*
+* Arguments:
+* urb ptr to the completed urb
+*
+* Returns:
+* nothing
+*
+* Side effects:
+*
+* Call context:
+* interrupt
+----------------------------------------------------------------*/
+static void hfa384x_usbin_callback(struct urb *urb)
+{
+ wlandevice_t *wlandev = urb->context;
+ hfa384x_t *hw;
+ hfa384x_usbin_t *usbin = (hfa384x_usbin_t *)urb->transfer_buffer;
+ struct sk_buff *skb = NULL;
+ int result;
+ int urb_status;
+ u16 type;
+
+ enum USBIN_ACTION {
+ HANDLE,
+ RESUBMIT,
+ ABORT
+ } action;
+
+ if (!wlandev || !wlandev->netdev || wlandev->hwremoved)
+ goto exit;
+
+ hw = wlandev->priv;
+ if (!hw)
+ goto exit;
+
+ skb = hw->rx_urb_skb;
+ BUG_ON(!skb || (skb->data != urb->transfer_buffer));
+
+ hw->rx_urb_skb = NULL;
+
+ /* Check for error conditions within the URB */
+ switch (urb->status) {
+ case 0:
+ action = HANDLE;
+
+ /* Check for short packet */
+ if (urb->actual_length == 0) {
+ wlandev->netdev->stats.rx_errors++;
+ wlandev->netdev->stats.rx_length_errors++;
+ action = RESUBMIT;
+ }
+ break;
+
+ case -EPIPE:
+ netdev_warn(hw->wlandev->netdev, "%s rx pipe stalled: requesting reset\n",
+ wlandev->netdev->name);
+ if (!test_and_set_bit(WORK_RX_HALT, &hw->usb_flags))
+ schedule_work(&hw->usb_work);
+ wlandev->netdev->stats.rx_errors++;
+ action = ABORT;
+ break;
+
+ case -EILSEQ:
+ case -ETIMEDOUT:
+ case -EPROTO:
+ if (!test_and_set_bit(THROTTLE_RX, &hw->usb_flags) &&
+ !timer_pending(&hw->throttle)) {
+ mod_timer(&hw->throttle, jiffies + THROTTLE_JIFFIES);
+ }
+ wlandev->netdev->stats.rx_errors++;
+ action = ABORT;
+ break;
+
+ case -EOVERFLOW:
+ wlandev->netdev->stats.rx_over_errors++;
+ action = RESUBMIT;
+ break;
+
+ case -ENODEV:
+ case -ESHUTDOWN:
+ pr_debug("status=%d, device removed.\n", urb->status);
+ action = ABORT;
+ break;
+
+ case -ENOENT:
+ case -ECONNRESET:
+ pr_debug("status=%d, urb explicitly unlinked.\n", urb->status);
+ action = ABORT;
+ break;
+
+ default:
+ pr_debug("urb status=%d, transfer flags=0x%x\n",
+ urb->status, urb->transfer_flags);
+ wlandev->netdev->stats.rx_errors++;
+ action = RESUBMIT;
+ break;
+ }
+
+ urb_status = urb->status;
+
+ if (action != ABORT) {
+ /* Repost the RX URB */
+ result = submit_rx_urb(hw, GFP_ATOMIC);
+
+ if (result != 0) {
+ netdev_err(hw->wlandev->netdev,
+ "Fatal, failed to resubmit rx_urb. error=%d\n",
+ result);
+ }
+ }
+
+ /* Handle any USB-IN packet */
+ /* Note: the check of the sw_support field, the type field doesn't
+ * have bit 12 set like the docs suggest.
+ */
+ type = le16_to_cpu(usbin->type);
+ if (HFA384x_USB_ISRXFRM(type)) {
+ if (action == HANDLE) {
+ if (usbin->txfrm.desc.sw_support == 0x0123) {
+ hfa384x_usbin_txcompl(wlandev, usbin);
+ } else {
+ skb_put(skb, sizeof(*usbin));
+ hfa384x_usbin_rx(wlandev, skb);
+ skb = NULL;
+ }
+ }
+ goto exit;
+ }
+ if (HFA384x_USB_ISTXFRM(type)) {
+ if (action == HANDLE)
+ hfa384x_usbin_txcompl(wlandev, usbin);
+ goto exit;
+ }
+ switch (type) {
+ case HFA384x_USB_INFOFRM:
+ if (action == ABORT)
+ goto exit;
+ if (action == HANDLE)
+ hfa384x_usbin_info(wlandev, usbin);
+ break;
+
+ case HFA384x_USB_CMDRESP:
+ case HFA384x_USB_WRIDRESP:
+ case HFA384x_USB_RRIDRESP:
+ case HFA384x_USB_WMEMRESP:
+ case HFA384x_USB_RMEMRESP:
+ /* ALWAYS, ALWAYS, ALWAYS handle this CTLX!!!! */
+ hfa384x_usbin_ctlx(hw, usbin, urb_status);
+ break;
+
+ case HFA384x_USB_BUFAVAIL:
+ pr_debug("Received BUFAVAIL packet, frmlen=%d\n",
+ usbin->bufavail.frmlen);
+ break;
+
+ case HFA384x_USB_ERROR:
+ pr_debug("Received USB_ERROR packet, errortype=%d\n",
+ usbin->usberror.errortype);
+ break;
+
+ default:
+ pr_debug("Unrecognized USBIN packet, type=%x, status=%d\n",
+ usbin->type, urb_status);
+ break;
+ } /* switch */
+
+exit:
+
+ if (skb)
+ dev_kfree_skb(skb);
+}
+
+/*----------------------------------------------------------------
+* hfa384x_usbin_ctlx
+*
+* We've received a URB containing a Prism2 "response" message.
+* This message needs to be matched up with a CTLX on the active
+* queue and our state updated accordingly.
+*
+* Arguments:
+* hw ptr to hfa384x_t
+* usbin ptr to USB IN packet
+* urb_status status of this Bulk-In URB
+*
+* Returns:
+* nothing
+*
+* Side effects:
+*
+* Call context:
+* interrupt
+----------------------------------------------------------------*/
+static void hfa384x_usbin_ctlx(hfa384x_t *hw, hfa384x_usbin_t *usbin,
+ int urb_status)
+{
+ hfa384x_usbctlx_t *ctlx;
+ int run_queue = 0;
+ unsigned long flags;
+
+retry:
+ spin_lock_irqsave(&hw->ctlxq.lock, flags);
+
+ /* There can be only one CTLX on the active queue
+ * at any one time, and this is the CTLX that the
+ * timers are waiting for.
+ */
+ if (list_empty(&hw->ctlxq.active))
+ goto unlock;
+
+ /* Remove the "response timeout". It's possible that
+ * we are already too late, and that the timeout is
+ * already running. And that's just too bad for us,
+ * because we could lose our CTLX from the active
+ * queue here ...
+ */
+ if (del_timer(&hw->resptimer) == 0) {
+ if (hw->resp_timer_done == 0) {
+ spin_unlock_irqrestore(&hw->ctlxq.lock, flags);
+ goto retry;
+ }
+ } else {
+ hw->resp_timer_done = 1;
+ }
+
+ ctlx = get_active_ctlx(hw);
+
+ if (urb_status != 0) {
+ /*
+ * Bad CTLX, so get rid of it. But we only
+ * remove it from the active queue if we're no
+ * longer expecting the OUT URB to complete.
+ */
+ if (unlocked_usbctlx_cancel_async(hw, ctlx) == 0)
+ run_queue = 1;
+ } else {
+ const __le16 intype = (usbin->type & ~cpu_to_le16(0x8000));
+
+ /*
+ * Check that our message is what we're expecting ...
+ */
+ if (ctlx->outbuf.type != intype) {
+ netdev_warn(hw->wlandev->netdev,
+ "Expected IN[%d], received IN[%d] - ignored.\n",
+ le16_to_cpu(ctlx->outbuf.type),
+ le16_to_cpu(intype));
+ goto unlock;
+ }
+
+ /* This URB has succeeded, so grab the data ... */
+ memcpy(&ctlx->inbuf, usbin, sizeof(ctlx->inbuf));
+
+ switch (ctlx->state) {
+ case CTLX_REQ_SUBMITTED:
+ /*
+ * We have received our response URB before
+ * our request has been acknowledged. Odd,
+ * but our OUT URB is still alive...
+ */
+ pr_debug("Causality violation: please reboot Universe\n");
+ ctlx->state = CTLX_RESP_COMPLETE;
+ break;
+
+ case CTLX_REQ_COMPLETE:
+ /*
+ * This is the usual path: our request
+ * has already been acknowledged, and
+ * now we have received the reply too.
+ */
+ ctlx->state = CTLX_COMPLETE;
+ unlocked_usbctlx_complete(hw, ctlx);
+ run_queue = 1;
+ break;
+
+ default:
+ /*
+ * Throw this CTLX away ...
+ */
+ netdev_err(hw->wlandev->netdev,
+ "Matched IN URB, CTLX[%d] in invalid state(%s). Discarded.\n",
+ le16_to_cpu(ctlx->outbuf.type),
+ ctlxstr(ctlx->state));
+ if (unlocked_usbctlx_cancel_async(hw, ctlx) == 0)
+ run_queue = 1;
+ break;
+ } /* switch */
+ }
+
+unlock:
+ spin_unlock_irqrestore(&hw->ctlxq.lock, flags);
+
+ if (run_queue)
+ hfa384x_usbctlxq_run(hw);
+}
+
+/*----------------------------------------------------------------
+* hfa384x_usbin_txcompl
+*
+* At this point we have the results of a previous transmit.
+*
+* Arguments:
+* wlandev wlan device
+* usbin ptr to the usb transfer buffer
+*
+* Returns:
+* nothing
+*
+* Side effects:
+*
+* Call context:
+* interrupt
+----------------------------------------------------------------*/
+static void hfa384x_usbin_txcompl(wlandevice_t *wlandev,
+ hfa384x_usbin_t *usbin)
+{
+ u16 status;
+
+ status = le16_to_cpu(usbin->type); /* yeah I know it says type... */
+
+ /* Was there an error? */
+ if (HFA384x_TXSTATUS_ISERROR(status))
+ prism2sta_ev_txexc(wlandev, status);
+ else
+ prism2sta_ev_tx(wlandev, status);
+}
+
+/*----------------------------------------------------------------
+* hfa384x_usbin_rx
+*
+* At this point we have a successful received a rx frame packet.
+*
+* Arguments:
+* wlandev wlan device
+* usbin ptr to the usb transfer buffer
+*
+* Returns:
+* nothing
+*
+* Side effects:
+*
+* Call context:
+* interrupt
+----------------------------------------------------------------*/
+static void hfa384x_usbin_rx(wlandevice_t *wlandev, struct sk_buff *skb)
+{
+ hfa384x_usbin_t *usbin = (hfa384x_usbin_t *)skb->data;
+ hfa384x_t *hw = wlandev->priv;
+ int hdrlen;
+ struct p80211_rxmeta *rxmeta;
+ u16 data_len;
+ u16 fc;
+
+ /* Byte order convert once up front. */
+ usbin->rxfrm.desc.status = le16_to_cpu(usbin->rxfrm.desc.status);
+ usbin->rxfrm.desc.time = le32_to_cpu(usbin->rxfrm.desc.time);
+
+ /* Now handle frame based on port# */
+ switch (HFA384x_RXSTATUS_MACPORT_GET(usbin->rxfrm.desc.status)) {
+ case 0:
+ fc = le16_to_cpu(usbin->rxfrm.desc.frame_control);
+
+ /* If exclude and we receive an unencrypted, drop it */
+ if ((wlandev->hostwep & HOSTWEP_EXCLUDEUNENCRYPTED) &&
+ !WLAN_GET_FC_ISWEP(fc)) {
+ break;
+ }
+
+ data_len = le16_to_cpu(usbin->rxfrm.desc.data_len);
+
+ /* How much header data do we have? */
+ hdrlen = p80211_headerlen(fc);
+
+ /* Pull off the descriptor */
+ skb_pull(skb, sizeof(hfa384x_rx_frame_t));
+
+ /* Now shunt the header block up against the data block
+ * with an "overlapping" copy
+ */
+ memmove(skb_push(skb, hdrlen),
+ &usbin->rxfrm.desc.frame_control, hdrlen);
+
+ skb->dev = wlandev->netdev;
+ skb->dev->last_rx = jiffies;
+
+ /* And set the frame length properly */
+ skb_trim(skb, data_len + hdrlen);
+
+ /* The prism2 series does not return the CRC */
+ memset(skb_put(skb, WLAN_CRC_LEN), 0xff, WLAN_CRC_LEN);
+
+ skb_reset_mac_header(skb);
+
+ /* Attach the rxmeta, set some stuff */
+ p80211skb_rxmeta_attach(wlandev, skb);
+ rxmeta = P80211SKB_RXMETA(skb);
+ rxmeta->mactime = usbin->rxfrm.desc.time;
+ rxmeta->rxrate = usbin->rxfrm.desc.rate;
+ rxmeta->signal = usbin->rxfrm.desc.signal - hw->dbmadjust;
+ rxmeta->noise = usbin->rxfrm.desc.silence - hw->dbmadjust;
+
+ prism2sta_ev_rx(wlandev, skb);
+
+ break;
+
+ case 7:
+ if (!HFA384x_RXSTATUS_ISFCSERR(usbin->rxfrm.desc.status)) {
+ /* Copy to wlansnif skb */
+ hfa384x_int_rxmonitor(wlandev, &usbin->rxfrm);
+ dev_kfree_skb(skb);
+ } else {
+ pr_debug("Received monitor frame: FCSerr set\n");
+ }
+ break;
+
+ default:
+ netdev_warn(hw->wlandev->netdev, "Received frame on unsupported port=%d\n",
+ HFA384x_RXSTATUS_MACPORT_GET(
+ usbin->rxfrm.desc.status));
+ break;
+ }
+}
+
+/*----------------------------------------------------------------
+* hfa384x_int_rxmonitor
+*
+* Helper function for int_rx. Handles monitor frames.
+* Note that this function allocates space for the FCS and sets it
+* to 0xffffffff. The hfa384x doesn't give us the FCS value but the
+* higher layers expect it. 0xffffffff is used as a flag to indicate
+* the FCS is bogus.
+*
+* Arguments:
+* wlandev wlan device structure
+* rxfrm rx descriptor read from card in int_rx
+*
+* Returns:
+* nothing
+*
+* Side effects:
+* Allocates an skb and passes it up via the PF_PACKET interface.
+* Call context:
+* interrupt
+----------------------------------------------------------------*/
+static void hfa384x_int_rxmonitor(wlandevice_t *wlandev,
+ hfa384x_usb_rxfrm_t *rxfrm)
+{
+ hfa384x_rx_frame_t *rxdesc = &(rxfrm->desc);
+ unsigned int hdrlen = 0;
+ unsigned int datalen = 0;
+ unsigned int skblen = 0;
+ u8 *datap;
+ u16 fc;
+ struct sk_buff *skb;
+ hfa384x_t *hw = wlandev->priv;
+
+ /* Remember the status, time, and data_len fields are in host order */
+ /* Figure out how big the frame is */
+ fc = le16_to_cpu(rxdesc->frame_control);
+ hdrlen = p80211_headerlen(fc);
+ datalen = le16_to_cpu(rxdesc->data_len);
+
+ /* Allocate an ind message+framesize skb */
+ skblen = sizeof(struct p80211_caphdr) + hdrlen + datalen + WLAN_CRC_LEN;
+
+ /* sanity check the length */
+ if (skblen >
+ (sizeof(struct p80211_caphdr) +
+ WLAN_HDR_A4_LEN + WLAN_DATA_MAXLEN + WLAN_CRC_LEN)) {
+ pr_debug("overlen frm: len=%zd\n",
+ skblen - sizeof(struct p80211_caphdr));
+ }
+
+ skb = dev_alloc_skb(skblen);
+ if (skb == NULL)
+ return;
+
+ /* only prepend the prism header if in the right mode */
+ if ((wlandev->netdev->type == ARPHRD_IEEE80211_PRISM) &&
+ (hw->sniffhdr != 0)) {
+ struct p80211_caphdr *caphdr;
+ /* The NEW header format! */
+ datap = skb_put(skb, sizeof(struct p80211_caphdr));
+ caphdr = (struct p80211_caphdr *)datap;
+
+ caphdr->version = htonl(P80211CAPTURE_VERSION);
+ caphdr->length = htonl(sizeof(struct p80211_caphdr));
+ caphdr->mactime = __cpu_to_be64(rxdesc->time) * 1000;
+ caphdr->hosttime = __cpu_to_be64(jiffies);
+ caphdr->phytype = htonl(4); /* dss_dot11_b */
+ caphdr->channel = htonl(hw->sniff_channel);
+ caphdr->datarate = htonl(rxdesc->rate);
+ caphdr->antenna = htonl(0); /* unknown */
+ caphdr->priority = htonl(0); /* unknown */
+ caphdr->ssi_type = htonl(3); /* rssi_raw */
+ caphdr->ssi_signal = htonl(rxdesc->signal);
+ caphdr->ssi_noise = htonl(rxdesc->silence);
+ caphdr->preamble = htonl(0); /* unknown */
+ caphdr->encoding = htonl(1); /* cck */
+ }
+
+ /* Copy the 802.11 header to the skb
+ (ctl frames may be less than a full header) */
+ datap = skb_put(skb, hdrlen);
+ memcpy(datap, &(rxdesc->frame_control), hdrlen);
+
+ /* If any, copy the data from the card to the skb */
+ if (datalen > 0) {
+ datap = skb_put(skb, datalen);
+ memcpy(datap, rxfrm->data, datalen);
+
+ /* check for unencrypted stuff if WEP bit set. */
+ if (*(datap - hdrlen + 1) & 0x40) /* wep set */
+ if ((*(datap) == 0xaa) && (*(datap + 1) == 0xaa))
+ /* clear wep; it's the 802.2 header! */
+ *(datap - hdrlen + 1) &= 0xbf;
+ }
+
+ if (hw->sniff_fcs) {
+ /* Set the FCS */
+ datap = skb_put(skb, WLAN_CRC_LEN);
+ memset(datap, 0xff, WLAN_CRC_LEN);
+ }
+
+ /* pass it back up */
+ prism2sta_ev_rx(wlandev, skb);
+}
+
+/*----------------------------------------------------------------
+* hfa384x_usbin_info
+*
+* At this point we have a successful received a Prism2 info frame.
+*
+* Arguments:
+* wlandev wlan device
+* usbin ptr to the usb transfer buffer
+*
+* Returns:
+* nothing
+*
+* Side effects:
+*
+* Call context:
+* interrupt
+----------------------------------------------------------------*/
+static void hfa384x_usbin_info(wlandevice_t *wlandev, hfa384x_usbin_t *usbin)
+{
+ usbin->infofrm.info.framelen =
+ le16_to_cpu(usbin->infofrm.info.framelen);
+ prism2sta_ev_info(wlandev, &usbin->infofrm.info);
+}
+
+/*----------------------------------------------------------------
+* hfa384x_usbout_callback
+*
+* Callback for URBs on the BULKOUT endpoint.
+*
+* Arguments:
+* urb ptr to the completed urb
+*
+* Returns:
+* nothing
+*
+* Side effects:
+*
+* Call context:
+* interrupt
+----------------------------------------------------------------*/
+static void hfa384x_usbout_callback(struct urb *urb)
+{
+ wlandevice_t *wlandev = urb->context;
+ hfa384x_usbout_t *usbout = urb->transfer_buffer;
+
+#ifdef DEBUG_USB
+ dbprint_urb(urb);
+#endif
+
+ if (wlandev && wlandev->netdev) {
+ switch (urb->status) {
+ case 0:
+ hfa384x_usbout_tx(wlandev, usbout);
+ break;
+
+ case -EPIPE:
+ {
+ hfa384x_t *hw = wlandev->priv;
+
+ netdev_warn(hw->wlandev->netdev,
+ "%s tx pipe stalled: requesting reset\n",
+ wlandev->netdev->name);
+ if (!test_and_set_bit
+ (WORK_TX_HALT, &hw->usb_flags))
+ schedule_work(&hw->usb_work);
+ wlandev->netdev->stats.tx_errors++;
+ break;
+ }
+
+ case -EPROTO:
+ case -ETIMEDOUT:
+ case -EILSEQ:
+ {
+ hfa384x_t *hw = wlandev->priv;
+
+ if (!test_and_set_bit
+ (THROTTLE_TX, &hw->usb_flags) &&
+ !timer_pending(&hw->throttle)) {
+ mod_timer(&hw->throttle,
+ jiffies + THROTTLE_JIFFIES);
+ }
+ wlandev->netdev->stats.tx_errors++;
+ netif_stop_queue(wlandev->netdev);
+ break;
+ }
+
+ case -ENOENT:
+ case -ESHUTDOWN:
+ /* Ignorable errors */
+ break;
+
+ default:
+ netdev_info(wlandev->netdev, "unknown urb->status=%d\n",
+ urb->status);
+ wlandev->netdev->stats.tx_errors++;
+ break;
+ } /* switch */
+ }
+}
+
+/*----------------------------------------------------------------
+* hfa384x_ctlxout_callback
+*
+* Callback for control data on the BULKOUT endpoint.
+*
+* Arguments:
+* urb ptr to the completed urb
+*
+* Returns:
+* nothing
+*
+* Side effects:
+*
+* Call context:
+* interrupt
+----------------------------------------------------------------*/
+static void hfa384x_ctlxout_callback(struct urb *urb)
+{
+ hfa384x_t *hw = urb->context;
+ int delete_resptimer = 0;
+ int timer_ok = 1;
+ int run_queue = 0;
+ hfa384x_usbctlx_t *ctlx;
+ unsigned long flags;
+
+ pr_debug("urb->status=%d\n", urb->status);
+#ifdef DEBUG_USB
+ dbprint_urb(urb);
+#endif
+ if ((urb->status == -ESHUTDOWN) ||
+ (urb->status == -ENODEV) || (hw == NULL))
+ return;
+
+retry:
+ spin_lock_irqsave(&hw->ctlxq.lock, flags);
+
+ /*
+ * Only one CTLX at a time on the "active" list, and
+ * none at all if we are unplugged. However, we can
+ * rely on the disconnect function to clean everything
+ * up if someone unplugged the adapter.
+ */
+ if (list_empty(&hw->ctlxq.active)) {
+ spin_unlock_irqrestore(&hw->ctlxq.lock, flags);
+ return;
+ }
+
+ /*
+ * Having something on the "active" queue means
+ * that we have timers to worry about ...
+ */
+ if (del_timer(&hw->reqtimer) == 0) {
+ if (hw->req_timer_done == 0) {
+ /*
+ * This timer was actually running while we
+ * were trying to delete it. Let it terminate
+ * gracefully instead.
+ */
+ spin_unlock_irqrestore(&hw->ctlxq.lock, flags);
+ goto retry;
+ }
+ } else {
+ hw->req_timer_done = 1;
+ }
+
+ ctlx = get_active_ctlx(hw);
+
+ if (urb->status == 0) {
+ /* Request portion of a CTLX is successful */
+ switch (ctlx->state) {
+ case CTLX_REQ_SUBMITTED:
+ /* This OUT-ACK received before IN */
+ ctlx->state = CTLX_REQ_COMPLETE;
+ break;
+
+ case CTLX_RESP_COMPLETE:
+ /* IN already received before this OUT-ACK,
+ * so this command must now be complete.
+ */
+ ctlx->state = CTLX_COMPLETE;
+ unlocked_usbctlx_complete(hw, ctlx);
+ run_queue = 1;
+ break;
+
+ default:
+ /* This is NOT a valid CTLX "success" state! */
+ netdev_err(hw->wlandev->netdev,
+ "Illegal CTLX[%d] success state(%s, %d) in OUT URB\n",
+ le16_to_cpu(ctlx->outbuf.type),
+ ctlxstr(ctlx->state), urb->status);
+ break;
+ } /* switch */
+ } else {
+ /* If the pipe has stalled then we need to reset it */
+ if ((urb->status == -EPIPE) &&
+ !test_and_set_bit(WORK_TX_HALT, &hw->usb_flags)) {
+ netdev_warn(hw->wlandev->netdev,
+ "%s tx pipe stalled: requesting reset\n",
+ hw->wlandev->netdev->name);
+ schedule_work(&hw->usb_work);
+ }
+
+ /* If someone cancels the OUT URB then its status
+ * should be either -ECONNRESET or -ENOENT.
+ */
+ ctlx->state = CTLX_REQ_FAILED;
+ unlocked_usbctlx_complete(hw, ctlx);
+ delete_resptimer = 1;
+ run_queue = 1;
+ }
+
+delresp:
+ if (delete_resptimer) {
+ timer_ok = del_timer(&hw->resptimer);
+ if (timer_ok != 0)
+ hw->resp_timer_done = 1;
+ }
+
+ spin_unlock_irqrestore(&hw->ctlxq.lock, flags);
+
+ if (!timer_ok && (hw->resp_timer_done == 0)) {
+ spin_lock_irqsave(&hw->ctlxq.lock, flags);
+ goto delresp;
+ }
+
+ if (run_queue)
+ hfa384x_usbctlxq_run(hw);
+}
+
+/*----------------------------------------------------------------
+* hfa384x_usbctlx_reqtimerfn
+*
+* Timer response function for CTLX request timeouts. If this
+* function is called, it means that the callback for the OUT
+* URB containing a Prism2.x XXX_Request was never called.
+*
+* Arguments:
+* data a ptr to the hfa384x_t
+*
+* Returns:
+* nothing
+*
+* Side effects:
+*
+* Call context:
+* interrupt
+----------------------------------------------------------------*/
+static void hfa384x_usbctlx_reqtimerfn(unsigned long data)
+{
+ hfa384x_t *hw = (hfa384x_t *)data;
+ unsigned long flags;
+
+ spin_lock_irqsave(&hw->ctlxq.lock, flags);
+
+ hw->req_timer_done = 1;
+
+ /* Removing the hardware automatically empties
+ * the active list ...
+ */
+ if (!list_empty(&hw->ctlxq.active)) {
+ /*
+ * We must ensure that our URB is removed from
+ * the system, if it hasn't already expired.
+ */
+ hw->ctlx_urb.transfer_flags |= URB_ASYNC_UNLINK;
+ if (usb_unlink_urb(&hw->ctlx_urb) == -EINPROGRESS) {
+ hfa384x_usbctlx_t *ctlx = get_active_ctlx(hw);
+
+ ctlx->state = CTLX_REQ_FAILED;
+
+ /* This URB was active, but has now been
+ * cancelled. It will now have a status of
+ * -ECONNRESET in the callback function.
+ *
+ * We are cancelling this CTLX, so we're
+ * not going to need to wait for a response.
+ * The URB's callback function will check
+ * that this timer is truly dead.
+ */
+ if (del_timer(&hw->resptimer) != 0)
+ hw->resp_timer_done = 1;
+ }
+ }
+
+ spin_unlock_irqrestore(&hw->ctlxq.lock, flags);
+}
+
+/*----------------------------------------------------------------
+* hfa384x_usbctlx_resptimerfn
+*
+* Timer response function for CTLX response timeouts. If this
+* function is called, it means that the callback for the IN
+* URB containing a Prism2.x XXX_Response was never called.
+*
+* Arguments:
+* data a ptr to the hfa384x_t
+*
+* Returns:
+* nothing
+*
+* Side effects:
+*
+* Call context:
+* interrupt
+----------------------------------------------------------------*/
+static void hfa384x_usbctlx_resptimerfn(unsigned long data)
+{
+ hfa384x_t *hw = (hfa384x_t *)data;
+ unsigned long flags;
+
+ spin_lock_irqsave(&hw->ctlxq.lock, flags);
+
+ hw->resp_timer_done = 1;
+
+ /* The active list will be empty if the
+ * adapter has been unplugged ...
+ */
+ if (!list_empty(&hw->ctlxq.active)) {
+ hfa384x_usbctlx_t *ctlx = get_active_ctlx(hw);
+
+ if (unlocked_usbctlx_cancel_async(hw, ctlx) == 0) {
+ spin_unlock_irqrestore(&hw->ctlxq.lock, flags);
+ hfa384x_usbctlxq_run(hw);
+ return;
+ }
+ }
+ spin_unlock_irqrestore(&hw->ctlxq.lock, flags);
+}
+
+/*----------------------------------------------------------------
+* hfa384x_usb_throttlefn
+*
+*
+* Arguments:
+* data ptr to hw
+*
+* Returns:
+* Nothing
+*
+* Side effects:
+*
+* Call context:
+* Interrupt
+----------------------------------------------------------------*/
+static void hfa384x_usb_throttlefn(unsigned long data)
+{
+ hfa384x_t *hw = (hfa384x_t *)data;
+ unsigned long flags;
+
+ spin_lock_irqsave(&hw->ctlxq.lock, flags);
+
+ /*
+ * We need to check BOTH the RX and the TX throttle controls,
+ * so we use the bitwise OR instead of the logical OR.
+ */
+ pr_debug("flags=0x%lx\n", hw->usb_flags);
+ if (!hw->wlandev->hwremoved &&
+ ((test_and_clear_bit(THROTTLE_RX, &hw->usb_flags) &&
+ !test_and_set_bit(WORK_RX_RESUME, &hw->usb_flags))
+ |
+ (test_and_clear_bit(THROTTLE_TX, &hw->usb_flags) &&
+ !test_and_set_bit(WORK_TX_RESUME, &hw->usb_flags))
+ )) {
+ schedule_work(&hw->usb_work);
+ }
+
+ spin_unlock_irqrestore(&hw->ctlxq.lock, flags);
+}
+
+/*----------------------------------------------------------------
+* hfa384x_usbctlx_submit
+*
+* Called from the doxxx functions to submit a CTLX to the queue
+*
+* Arguments:
+* hw ptr to the hw struct
+* ctlx ctlx structure to enqueue
+*
+* Returns:
+* -ENODEV if the adapter is unplugged
+* 0
+*
+* Side effects:
+*
+* Call context:
+* process or interrupt
+----------------------------------------------------------------*/
+static int hfa384x_usbctlx_submit(hfa384x_t *hw, hfa384x_usbctlx_t *ctlx)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&hw->ctlxq.lock, flags);
+
+ if (hw->wlandev->hwremoved) {
+ spin_unlock_irqrestore(&hw->ctlxq.lock, flags);
+ return -ENODEV;
+ }
+
+ ctlx->state = CTLX_PENDING;
+ list_add_tail(&ctlx->list, &hw->ctlxq.pending);
+ spin_unlock_irqrestore(&hw->ctlxq.lock, flags);
+ hfa384x_usbctlxq_run(hw);
+
+ return 0;
+}
+
+/*----------------------------------------------------------------
+* hfa384x_usbout_tx
+*
+* At this point we have finished a send of a frame. Mark the URB
+* as available and call ev_alloc to notify higher layers we're
+* ready for more.
+*
+* Arguments:
+* wlandev wlan device
+* usbout ptr to the usb transfer buffer
+*
+* Returns:
+* nothing
+*
+* Side effects:
+*
+* Call context:
+* interrupt
+----------------------------------------------------------------*/
+static void hfa384x_usbout_tx(wlandevice_t *wlandev, hfa384x_usbout_t *usbout)
+{
+ prism2sta_ev_alloc(wlandev);
+}
+
+/*----------------------------------------------------------------
+* hfa384x_isgood_pdrcore
+*
+* Quick check of PDR codes.
+*
+* Arguments:
+* pdrcode PDR code number (host order)
+*
+* Returns:
+* zero not good.
+* one is good.
+*
+* Side effects:
+*
+* Call context:
+----------------------------------------------------------------*/
+static int hfa384x_isgood_pdrcode(u16 pdrcode)
+{
+ switch (pdrcode) {
+ case HFA384x_PDR_END_OF_PDA:
+ case HFA384x_PDR_PCB_PARTNUM:
+ case HFA384x_PDR_PDAVER:
+ case HFA384x_PDR_NIC_SERIAL:
+ case HFA384x_PDR_MKK_MEASUREMENTS:
+ case HFA384x_PDR_NIC_RAMSIZE:
+ case HFA384x_PDR_MFISUPRANGE:
+ case HFA384x_PDR_CFISUPRANGE:
+ case HFA384x_PDR_NICID:
+ case HFA384x_PDR_MAC_ADDRESS:
+ case HFA384x_PDR_REGDOMAIN:
+ case HFA384x_PDR_ALLOWED_CHANNEL:
+ case HFA384x_PDR_DEFAULT_CHANNEL:
+ case HFA384x_PDR_TEMPTYPE:
+ case HFA384x_PDR_IFR_SETTING:
+ case HFA384x_PDR_RFR_SETTING:
+ case HFA384x_PDR_HFA3861_BASELINE:
+ case HFA384x_PDR_HFA3861_SHADOW:
+ case HFA384x_PDR_HFA3861_IFRF:
+ case HFA384x_PDR_HFA3861_CHCALSP:
+ case HFA384x_PDR_HFA3861_CHCALI:
+ case HFA384x_PDR_3842_NIC_CONFIG:
+ case HFA384x_PDR_USB_ID:
+ case HFA384x_PDR_PCI_ID:
+ case HFA384x_PDR_PCI_IFCONF:
+ case HFA384x_PDR_PCI_PMCONF:
+ case HFA384x_PDR_RFENRGY:
+ case HFA384x_PDR_HFA3861_MANF_TESTSP:
+ case HFA384x_PDR_HFA3861_MANF_TESTI:
+ /* code is OK */
+ return 1;
+ default:
+ if (pdrcode < 0x1000) {
+ /* code is OK, but we don't know exactly what it is */
+ pr_debug("Encountered unknown PDR#=0x%04x, assuming it's ok.\n",
+ pdrcode);
+ return 1;
+ }
+ break;
+ }
+ /* bad code */
+ pr_debug("Encountered unknown PDR#=0x%04x, (>=0x1000), assuming it's bad.\n",
+ pdrcode);
+ return 0;
+}
diff --git a/drivers/staging/wlan-ng/p80211conv.c b/drivers/staging/wlan-ng/p80211conv.c
new file mode 100644
index 000000000..bd69e8cf2
--- /dev/null
+++ b/drivers/staging/wlan-ng/p80211conv.c
@@ -0,0 +1,661 @@
+/* src/p80211/p80211conv.c
+*
+* Ether/802.11 conversions and packet buffer routines
+*
+* Copyright (C) 1999 AbsoluteValue Systems, Inc. All Rights Reserved.
+* --------------------------------------------------------------------
+*
+* linux-wlan
+*
+* The contents of this file are subject to the Mozilla Public
+* License Version 1.1 (the "License"); you may not use this file
+* except in compliance with the License. You may obtain a copy of
+* the License at http://www.mozilla.org/MPL/
+*
+* Software distributed under the License is distributed on an "AS
+* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+* implied. See the License for the specific language governing
+* rights and limitations under the License.
+*
+* Alternatively, the contents of this file may be used under the
+* terms of the GNU Public License version 2 (the "GPL"), in which
+* case the provisions of the GPL are applicable instead of the
+* above. If you wish to allow the use of your version of this file
+* only under the terms of the GPL and not to allow others to use
+* your version of this file under the MPL, indicate your decision
+* by deleting the provisions above and replace them with the notice
+* and other provisions required by the GPL. If you do not delete
+* the provisions above, a recipient may use your version of this
+* file under either the MPL or the GPL.
+*
+* --------------------------------------------------------------------
+*
+* Inquiries regarding the linux-wlan Open Source project can be
+* made directly to:
+*
+* AbsoluteValue Systems Inc.
+* info@linux-wlan.com
+* http://www.linux-wlan.com
+*
+* --------------------------------------------------------------------
+*
+* Portions of the development of this software were funded by
+* Intersil Corporation as part of PRISM(R) chipset product development.
+*
+* --------------------------------------------------------------------
+*
+* This file defines the functions that perform Ethernet to/from
+* 802.11 frame conversions.
+*
+* --------------------------------------------------------------------
+*
+*================================================================ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/types.h>
+#include <linux/skbuff.h>
+#include <linux/slab.h>
+#include <linux/wireless.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/if_ether.h>
+#include <linux/byteorder/generic.h>
+
+#include <asm/byteorder.h>
+
+#include "p80211types.h"
+#include "p80211hdr.h"
+#include "p80211conv.h"
+#include "p80211mgmt.h"
+#include "p80211msg.h"
+#include "p80211netdev.h"
+#include "p80211ioctl.h"
+#include "p80211req.h"
+
+static u8 oui_rfc1042[] = { 0x00, 0x00, 0x00 };
+static u8 oui_8021h[] = { 0x00, 0x00, 0xf8 };
+
+/*----------------------------------------------------------------
+* p80211pb_ether_to_80211
+*
+* Uses the contents of the ether frame and the etherconv setting
+* to build the elements of the 802.11 frame.
+*
+* We don't actually set
+* up the frame header here. That's the MAC's job. We're only handling
+* conversion of DIXII or 802.3+LLC frames to something that works
+* with 802.11.
+*
+* Note -- 802.11 header is NOT part of the skb. Likewise, the 802.11
+* FCS is also not present and will need to be added elsewhere.
+*
+* Arguments:
+* ethconv Conversion type to perform
+* skb skbuff containing the ether frame
+* p80211_hdr 802.11 header
+*
+* Returns:
+* 0 on success, non-zero otherwise
+*
+* Call context:
+* May be called in interrupt or non-interrupt context
+----------------------------------------------------------------*/
+int skb_ether_to_p80211(wlandevice_t *wlandev, u32 ethconv,
+ struct sk_buff *skb, union p80211_hdr *p80211_hdr,
+ struct p80211_metawep *p80211_wep)
+{
+
+ __le16 fc;
+ u16 proto;
+ struct wlan_ethhdr e_hdr;
+ struct wlan_llc *e_llc;
+ struct wlan_snap *e_snap;
+ int foo;
+
+ memcpy(&e_hdr, skb->data, sizeof(e_hdr));
+
+ if (skb->len <= 0) {
+ pr_debug("zero-length skb!\n");
+ return 1;
+ }
+
+ if (ethconv == WLAN_ETHCONV_ENCAP) { /* simplest case */
+ pr_debug("ENCAP len: %d\n", skb->len);
+ /* here, we don't care what kind of ether frm. Just stick it */
+ /* in the 80211 payload */
+ /* which is to say, leave the skb alone. */
+ } else {
+ /* step 1: classify ether frame, DIX or 802.3? */
+ proto = ntohs(e_hdr.type);
+ if (proto <= 1500) {
+ pr_debug("802.3 len: %d\n", skb->len);
+ /* codes <= 1500 reserved for 802.3 lengths */
+ /* it's 802.3, pass ether payload unchanged, */
+
+ /* trim off ethernet header */
+ skb_pull(skb, WLAN_ETHHDR_LEN);
+
+ /* leave off any PAD octets. */
+ skb_trim(skb, proto);
+ } else {
+ pr_debug("DIXII len: %d\n", skb->len);
+ /* it's DIXII, time for some conversion */
+
+ /* trim off ethernet header */
+ skb_pull(skb, WLAN_ETHHDR_LEN);
+
+ /* tack on SNAP */
+ e_snap =
+ (struct wlan_snap *) skb_push(skb,
+ sizeof(struct wlan_snap));
+ e_snap->type = htons(proto);
+ if (ethconv == WLAN_ETHCONV_8021h
+ && p80211_stt_findproto(proto)) {
+ memcpy(e_snap->oui, oui_8021h,
+ WLAN_IEEE_OUI_LEN);
+ } else {
+ memcpy(e_snap->oui, oui_rfc1042,
+ WLAN_IEEE_OUI_LEN);
+ }
+
+ /* tack on llc */
+ e_llc =
+ (struct wlan_llc *) skb_push(skb,
+ sizeof(struct wlan_llc));
+ e_llc->dsap = 0xAA; /* SNAP, see IEEE 802 */
+ e_llc->ssap = 0xAA;
+ e_llc->ctl = 0x03;
+
+ }
+ }
+
+ /* Set up the 802.11 header */
+ /* It's a data frame */
+ fc = cpu_to_le16(WLAN_SET_FC_FTYPE(WLAN_FTYPE_DATA) |
+ WLAN_SET_FC_FSTYPE(WLAN_FSTYPE_DATAONLY));
+
+ switch (wlandev->macmode) {
+ case WLAN_MACMODE_IBSS_STA:
+ memcpy(p80211_hdr->a3.a1, &e_hdr.daddr, ETH_ALEN);
+ memcpy(p80211_hdr->a3.a2, wlandev->netdev->dev_addr, ETH_ALEN);
+ memcpy(p80211_hdr->a3.a3, wlandev->bssid, ETH_ALEN);
+ break;
+ case WLAN_MACMODE_ESS_STA:
+ fc |= cpu_to_le16(WLAN_SET_FC_TODS(1));
+ memcpy(p80211_hdr->a3.a1, wlandev->bssid, ETH_ALEN);
+ memcpy(p80211_hdr->a3.a2, wlandev->netdev->dev_addr, ETH_ALEN);
+ memcpy(p80211_hdr->a3.a3, &e_hdr.daddr, ETH_ALEN);
+ break;
+ case WLAN_MACMODE_ESS_AP:
+ fc |= cpu_to_le16(WLAN_SET_FC_FROMDS(1));
+ memcpy(p80211_hdr->a3.a1, &e_hdr.daddr, ETH_ALEN);
+ memcpy(p80211_hdr->a3.a2, wlandev->bssid, ETH_ALEN);
+ memcpy(p80211_hdr->a3.a3, &e_hdr.saddr, ETH_ALEN);
+ break;
+ default:
+ netdev_err(wlandev->netdev,
+ "Error: Converting eth to wlan in unknown mode.\n");
+ return 1;
+ }
+
+ p80211_wep->data = NULL;
+
+ if ((wlandev->hostwep & HOSTWEP_PRIVACYINVOKED)
+ && (wlandev->hostwep & HOSTWEP_ENCRYPT)) {
+ /* XXXX need to pick keynum other than default? */
+
+ p80211_wep->data = kmalloc(skb->len, GFP_ATOMIC);
+ foo = wep_encrypt(wlandev, skb->data, p80211_wep->data,
+ skb->len,
+ (wlandev->hostwep & HOSTWEP_DEFAULTKEY_MASK),
+ p80211_wep->iv, p80211_wep->icv);
+ if (foo) {
+ netdev_warn(wlandev->netdev,
+ "Host en-WEP failed, dropping frame (%d).\n",
+ foo);
+ return 2;
+ }
+ fc |= cpu_to_le16(WLAN_SET_FC_ISWEP(1));
+ }
+
+ /* skb->nh.raw = skb->data; */
+
+ p80211_hdr->a3.fc = fc;
+ p80211_hdr->a3.dur = 0;
+ p80211_hdr->a3.seq = 0;
+
+ return 0;
+}
+
+/* jkriegl: from orinoco, modified */
+static void orinoco_spy_gather(wlandevice_t *wlandev, char *mac,
+ struct p80211_rxmeta *rxmeta)
+{
+ int i;
+
+ /* Gather wireless spy statistics: for each packet, compare the
+ * source address with out list, and if match, get the stats... */
+
+ for (i = 0; i < wlandev->spy_number; i++) {
+
+ if (!memcmp(wlandev->spy_address[i], mac, ETH_ALEN)) {
+ memcpy(wlandev->spy_address[i], mac, ETH_ALEN);
+ wlandev->spy_stat[i].level = rxmeta->signal;
+ wlandev->spy_stat[i].noise = rxmeta->noise;
+ wlandev->spy_stat[i].qual =
+ (rxmeta->signal >
+ rxmeta->noise) ? (rxmeta->signal -
+ rxmeta->noise) : 0;
+ wlandev->spy_stat[i].updated = 0x7;
+ }
+ }
+}
+
+/*----------------------------------------------------------------
+* p80211pb_80211_to_ether
+*
+* Uses the contents of a received 802.11 frame and the etherconv
+* setting to build an ether frame.
+*
+* This function extracts the src and dest address from the 802.11
+* frame to use in the construction of the eth frame.
+*
+* Arguments:
+* ethconv Conversion type to perform
+* skb Packet buffer containing the 802.11 frame
+*
+* Returns:
+* 0 on success, non-zero otherwise
+*
+* Call context:
+* May be called in interrupt or non-interrupt context
+----------------------------------------------------------------*/
+int skb_p80211_to_ether(wlandevice_t *wlandev, u32 ethconv,
+ struct sk_buff *skb)
+{
+ netdevice_t *netdev = wlandev->netdev;
+ u16 fc;
+ unsigned int payload_length;
+ unsigned int payload_offset;
+ u8 daddr[WLAN_ETHADDR_LEN];
+ u8 saddr[WLAN_ETHADDR_LEN];
+ union p80211_hdr *w_hdr;
+ struct wlan_ethhdr *e_hdr;
+ struct wlan_llc *e_llc;
+ struct wlan_snap *e_snap;
+
+ int foo;
+
+ payload_length = skb->len - WLAN_HDR_A3_LEN - WLAN_CRC_LEN;
+ payload_offset = WLAN_HDR_A3_LEN;
+
+ w_hdr = (union p80211_hdr *) skb->data;
+
+ /* setup some vars for convenience */
+ fc = le16_to_cpu(w_hdr->a3.fc);
+ if ((WLAN_GET_FC_TODS(fc) == 0) && (WLAN_GET_FC_FROMDS(fc) == 0)) {
+ memcpy(daddr, w_hdr->a3.a1, WLAN_ETHADDR_LEN);
+ memcpy(saddr, w_hdr->a3.a2, WLAN_ETHADDR_LEN);
+ } else if ((WLAN_GET_FC_TODS(fc) == 0)
+ && (WLAN_GET_FC_FROMDS(fc) == 1)) {
+ memcpy(daddr, w_hdr->a3.a1, WLAN_ETHADDR_LEN);
+ memcpy(saddr, w_hdr->a3.a3, WLAN_ETHADDR_LEN);
+ } else if ((WLAN_GET_FC_TODS(fc) == 1)
+ && (WLAN_GET_FC_FROMDS(fc) == 0)) {
+ memcpy(daddr, w_hdr->a3.a3, WLAN_ETHADDR_LEN);
+ memcpy(saddr, w_hdr->a3.a2, WLAN_ETHADDR_LEN);
+ } else {
+ payload_offset = WLAN_HDR_A4_LEN;
+ if (payload_length < WLAN_HDR_A4_LEN - WLAN_HDR_A3_LEN) {
+ netdev_err(netdev, "A4 frame too short!\n");
+ return 1;
+ }
+ payload_length -= (WLAN_HDR_A4_LEN - WLAN_HDR_A3_LEN);
+ memcpy(daddr, w_hdr->a4.a3, WLAN_ETHADDR_LEN);
+ memcpy(saddr, w_hdr->a4.a4, WLAN_ETHADDR_LEN);
+ }
+
+ /* perform de-wep if necessary.. */
+ if ((wlandev->hostwep & HOSTWEP_PRIVACYINVOKED) && WLAN_GET_FC_ISWEP(fc)
+ && (wlandev->hostwep & HOSTWEP_DECRYPT)) {
+ if (payload_length <= 8) {
+ netdev_err(netdev,
+ "WEP frame too short (%u).\n", skb->len);
+ return 1;
+ }
+ foo = wep_decrypt(wlandev, skb->data + payload_offset + 4,
+ payload_length - 8, -1,
+ skb->data + payload_offset,
+ skb->data + payload_offset +
+ payload_length - 4);
+ if (foo) {
+ /* de-wep failed, drop skb. */
+ pr_debug("Host de-WEP failed, dropping frame (%d).\n",
+ foo);
+ wlandev->rx.decrypt_err++;
+ return 2;
+ }
+
+ /* subtract the IV+ICV length off the payload */
+ payload_length -= 8;
+ /* chop off the IV */
+ skb_pull(skb, 4);
+ /* chop off the ICV. */
+ skb_trim(skb, skb->len - 4);
+
+ wlandev->rx.decrypt++;
+ }
+
+ e_hdr = (struct wlan_ethhdr *) (skb->data + payload_offset);
+
+ e_llc = (struct wlan_llc *) (skb->data + payload_offset);
+ e_snap =
+ (struct wlan_snap *) (skb->data + payload_offset +
+ sizeof(struct wlan_llc));
+
+ /* Test for the various encodings */
+ if ((payload_length >= sizeof(struct wlan_ethhdr)) &&
+ (e_llc->dsap != 0xaa || e_llc->ssap != 0xaa) &&
+ ((memcmp(daddr, e_hdr->daddr, WLAN_ETHADDR_LEN) == 0) ||
+ (memcmp(saddr, e_hdr->saddr, WLAN_ETHADDR_LEN) == 0))) {
+ pr_debug("802.3 ENCAP len: %d\n", payload_length);
+ /* 802.3 Encapsulated */
+ /* Test for an overlength frame */
+ if (payload_length > (netdev->mtu + WLAN_ETHHDR_LEN)) {
+ /* A bogus length ethfrm has been encap'd. */
+ /* Is someone trying an oflow attack? */
+ netdev_err(netdev, "ENCAP frame too large (%d > %d)\n",
+ payload_length, netdev->mtu + WLAN_ETHHDR_LEN);
+ return 1;
+ }
+
+ /* Chop off the 802.11 header. it's already sane. */
+ skb_pull(skb, payload_offset);
+ /* chop off the 802.11 CRC */
+ skb_trim(skb, skb->len - WLAN_CRC_LEN);
+
+ } else if ((payload_length >= sizeof(struct wlan_llc) +
+ sizeof(struct wlan_snap))
+ && (e_llc->dsap == 0xaa)
+ && (e_llc->ssap == 0xaa)
+ && (e_llc->ctl == 0x03)
+ &&
+ (((memcmp(e_snap->oui, oui_rfc1042, WLAN_IEEE_OUI_LEN) == 0)
+ && (ethconv == WLAN_ETHCONV_8021h)
+ && (p80211_stt_findproto(le16_to_cpu(e_snap->type))))
+ || (memcmp(e_snap->oui, oui_rfc1042, WLAN_IEEE_OUI_LEN) !=
+ 0))) {
+ pr_debug("SNAP+RFC1042 len: %d\n", payload_length);
+ /* it's a SNAP + RFC1042 frame && protocol is in STT */
+ /* build 802.3 + RFC1042 */
+
+ /* Test for an overlength frame */
+ if (payload_length > netdev->mtu) {
+ /* A bogus length ethfrm has been sent. */
+ /* Is someone trying an oflow attack? */
+ netdev_err(netdev, "SNAP frame too large (%d > %d)\n",
+ payload_length, netdev->mtu);
+ return 1;
+ }
+
+ /* chop 802.11 header from skb. */
+ skb_pull(skb, payload_offset);
+
+ /* create 802.3 header at beginning of skb. */
+ e_hdr = (struct wlan_ethhdr *) skb_push(skb, WLAN_ETHHDR_LEN);
+ memcpy(e_hdr->daddr, daddr, WLAN_ETHADDR_LEN);
+ memcpy(e_hdr->saddr, saddr, WLAN_ETHADDR_LEN);
+ e_hdr->type = htons(payload_length);
+
+ /* chop off the 802.11 CRC */
+ skb_trim(skb, skb->len - WLAN_CRC_LEN);
+
+ } else if ((payload_length >= sizeof(struct wlan_llc) +
+ sizeof(struct wlan_snap))
+ && (e_llc->dsap == 0xaa)
+ && (e_llc->ssap == 0xaa)
+ && (e_llc->ctl == 0x03)) {
+ pr_debug("802.1h/RFC1042 len: %d\n", payload_length);
+ /* it's an 802.1h frame || (an RFC1042 && protocol not in STT)
+ build a DIXII + RFC894 */
+
+ /* Test for an overlength frame */
+ if ((payload_length - sizeof(struct wlan_llc) -
+ sizeof(struct wlan_snap))
+ > netdev->mtu) {
+ /* A bogus length ethfrm has been sent. */
+ /* Is someone trying an oflow attack? */
+ netdev_err(netdev, "DIXII frame too large (%ld > %d)\n",
+ (long int)(payload_length -
+ sizeof(struct wlan_llc) -
+ sizeof(struct wlan_snap)), netdev->mtu);
+ return 1;
+ }
+
+ /* chop 802.11 header from skb. */
+ skb_pull(skb, payload_offset);
+
+ /* chop llc header from skb. */
+ skb_pull(skb, sizeof(struct wlan_llc));
+
+ /* chop snap header from skb. */
+ skb_pull(skb, sizeof(struct wlan_snap));
+
+ /* create 802.3 header at beginning of skb. */
+ e_hdr = (struct wlan_ethhdr *) skb_push(skb, WLAN_ETHHDR_LEN);
+ e_hdr->type = e_snap->type;
+ memcpy(e_hdr->daddr, daddr, WLAN_ETHADDR_LEN);
+ memcpy(e_hdr->saddr, saddr, WLAN_ETHADDR_LEN);
+
+ /* chop off the 802.11 CRC */
+ skb_trim(skb, skb->len - WLAN_CRC_LEN);
+ } else {
+ pr_debug("NON-ENCAP len: %d\n", payload_length);
+ /* any NON-ENCAP */
+ /* it's a generic 80211+LLC or IPX 'Raw 802.3' */
+ /* build an 802.3 frame */
+ /* allocate space and setup hostbuf */
+
+ /* Test for an overlength frame */
+ if (payload_length > netdev->mtu) {
+ /* A bogus length ethfrm has been sent. */
+ /* Is someone trying an oflow attack? */
+ netdev_err(netdev, "OTHER frame too large (%d > %d)\n",
+ payload_length, netdev->mtu);
+ return 1;
+ }
+
+ /* Chop off the 802.11 header. */
+ skb_pull(skb, payload_offset);
+
+ /* create 802.3 header at beginning of skb. */
+ e_hdr = (struct wlan_ethhdr *) skb_push(skb, WLAN_ETHHDR_LEN);
+ memcpy(e_hdr->daddr, daddr, WLAN_ETHADDR_LEN);
+ memcpy(e_hdr->saddr, saddr, WLAN_ETHADDR_LEN);
+ e_hdr->type = htons(payload_length);
+
+ /* chop off the 802.11 CRC */
+ skb_trim(skb, skb->len - WLAN_CRC_LEN);
+
+ }
+
+ /*
+ * Note that eth_type_trans() expects an skb w/ skb->data pointing
+ * at the MAC header, it then sets the following skb members:
+ * skb->mac_header,
+ * skb->data, and
+ * skb->pkt_type.
+ * It then _returns_ the value that _we're_ supposed to stuff in
+ * skb->protocol. This is nuts.
+ */
+ skb->protocol = eth_type_trans(skb, netdev);
+
+ /* jkriegl: process signal and noise as set in hfa384x_int_rx() */
+ /* jkriegl: only process signal/noise if requested by iwspy */
+ if (wlandev->spy_number)
+ orinoco_spy_gather(wlandev, eth_hdr(skb)->h_source,
+ P80211SKB_RXMETA(skb));
+
+ /* Free the metadata */
+ p80211skb_rxmeta_detach(skb);
+
+ return 0;
+}
+
+/*----------------------------------------------------------------
+* p80211_stt_findproto
+*
+* Searches the 802.1h Selective Translation Table for a given
+* protocol.
+*
+* Arguments:
+* proto protocol number (in host order) to search for.
+*
+* Returns:
+* 1 - if the table is empty or a match is found.
+* 0 - if the table is non-empty and a match is not found.
+*
+* Call context:
+* May be called in interrupt or non-interrupt context
+----------------------------------------------------------------*/
+int p80211_stt_findproto(u16 proto)
+{
+ /* Always return found for now. This is the behavior used by the */
+ /* Zoom Win95 driver when 802.1h mode is selected */
+ /* TODO: If necessary, add an actual search we'll probably
+ need this to match the CMAC's way of doing things.
+ Need to do some testing to confirm.
+ */
+
+ if (proto == 0x80f3) /* APPLETALK */
+ return 1;
+
+ return 0;
+}
+
+/*----------------------------------------------------------------
+* p80211skb_rxmeta_detach
+*
+* Disconnects the frmmeta and rxmeta from an skb.
+*
+* Arguments:
+* wlandev The wlandev this skb belongs to.
+* skb The skb we're attaching to.
+*
+* Returns:
+* 0 on success, non-zero otherwise
+*
+* Call context:
+* May be called in interrupt or non-interrupt context
+----------------------------------------------------------------*/
+void p80211skb_rxmeta_detach(struct sk_buff *skb)
+{
+ struct p80211_rxmeta *rxmeta;
+ struct p80211_frmmeta *frmmeta;
+
+ /* Sanity checks */
+ if (skb == NULL) { /* bad skb */
+ pr_debug("Called w/ null skb.\n");
+ return;
+ }
+ frmmeta = P80211SKB_FRMMETA(skb);
+ if (frmmeta == NULL) { /* no magic */
+ pr_debug("Called w/ bad frmmeta magic.\n");
+ return;
+ }
+ rxmeta = frmmeta->rx;
+ if (rxmeta == NULL) { /* bad meta ptr */
+ pr_debug("Called w/ bad rxmeta ptr.\n");
+ return;
+ }
+
+ /* Free rxmeta */
+ kfree(rxmeta);
+
+ /* Clear skb->cb */
+ memset(skb->cb, 0, sizeof(skb->cb));
+}
+
+/*----------------------------------------------------------------
+* p80211skb_rxmeta_attach
+*
+* Allocates a p80211rxmeta structure, initializes it, and attaches
+* it to an skb.
+*
+* Arguments:
+* wlandev The wlandev this skb belongs to.
+* skb The skb we're attaching to.
+*
+* Returns:
+* 0 on success, non-zero otherwise
+*
+* Call context:
+* May be called in interrupt or non-interrupt context
+----------------------------------------------------------------*/
+int p80211skb_rxmeta_attach(struct wlandevice *wlandev, struct sk_buff *skb)
+{
+ int result = 0;
+ struct p80211_rxmeta *rxmeta;
+ struct p80211_frmmeta *frmmeta;
+
+ /* If these already have metadata, we error out! */
+ if (P80211SKB_RXMETA(skb) != NULL) {
+ netdev_err(wlandev->netdev,
+ "%s: RXmeta already attached!\n", wlandev->name);
+ result = 0;
+ goto exit;
+ }
+
+ /* Allocate the rxmeta */
+ rxmeta = kzalloc(sizeof(struct p80211_rxmeta), GFP_ATOMIC);
+
+ if (rxmeta == NULL) {
+ netdev_err(wlandev->netdev,
+ "%s: Failed to allocate rxmeta.\n", wlandev->name);
+ result = 1;
+ goto exit;
+ }
+
+ /* Initialize the rxmeta */
+ rxmeta->wlandev = wlandev;
+ rxmeta->hosttime = jiffies;
+
+ /* Overlay a frmmeta_t onto skb->cb */
+ memset(skb->cb, 0, sizeof(struct p80211_frmmeta));
+ frmmeta = (struct p80211_frmmeta *) (skb->cb);
+ frmmeta->magic = P80211_FRMMETA_MAGIC;
+ frmmeta->rx = rxmeta;
+exit:
+ return result;
+}
+
+/*----------------------------------------------------------------
+* p80211skb_free
+*
+* Frees an entire p80211skb by checking and freeing the meta struct
+* and then freeing the skb.
+*
+* Arguments:
+* wlandev The wlandev this skb belongs to.
+* skb The skb we're attaching to.
+*
+* Returns:
+* 0 on success, non-zero otherwise
+*
+* Call context:
+* May be called in interrupt or non-interrupt context
+----------------------------------------------------------------*/
+void p80211skb_free(struct wlandevice *wlandev, struct sk_buff *skb)
+{
+ struct p80211_frmmeta *meta;
+
+ meta = P80211SKB_FRMMETA(skb);
+ if (meta && meta->rx)
+ p80211skb_rxmeta_detach(skb);
+ else
+ netdev_err(wlandev->netdev,
+ "Freeing an skb (%p) w/ no frmmeta.\n", skb);
+ dev_kfree_skb(skb);
+}
diff --git a/drivers/staging/wlan-ng/p80211conv.h b/drivers/staging/wlan-ng/p80211conv.h
new file mode 100644
index 000000000..e031a74d2
--- /dev/null
+++ b/drivers/staging/wlan-ng/p80211conv.h
@@ -0,0 +1,163 @@
+/* p80211conv.h
+*
+* Ether/802.11 conversions and packet buffer routines
+*
+* Copyright (C) 1999 AbsoluteValue Systems, Inc. All Rights Reserved.
+* --------------------------------------------------------------------
+*
+* linux-wlan
+*
+* The contents of this file are subject to the Mozilla Public
+* License Version 1.1 (the "License"); you may not use this file
+* except in compliance with the License. You may obtain a copy of
+* the License at http://www.mozilla.org/MPL/
+*
+* Software distributed under the License is distributed on an "AS
+* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+* implied. See the License for the specific language governing
+* rights and limitations under the License.
+*
+* Alternatively, the contents of this file may be used under the
+* terms of the GNU Public License version 2 (the "GPL"), in which
+* case the provisions of the GPL are applicable instead of the
+* above. If you wish to allow the use of your version of this file
+* only under the terms of the GPL and not to allow others to use
+* your version of this file under the MPL, indicate your decision
+* by deleting the provisions above and replace them with the notice
+* and other provisions required by the GPL. If you do not delete
+* the provisions above, a recipient may use your version of this
+* file under either the MPL or the GPL.
+*
+* --------------------------------------------------------------------
+*
+* Inquiries regarding the linux-wlan Open Source project can be
+* made directly to:
+*
+* AbsoluteValue Systems Inc.
+* info@linux-wlan.com
+* http://www.linux-wlan.com
+*
+* --------------------------------------------------------------------
+*
+* Portions of the development of this software were funded by
+* Intersil Corporation as part of PRISM(R) chipset product development.
+*
+* --------------------------------------------------------------------
+*
+* This file declares the functions, types and macros that perform
+* Ethernet to/from 802.11 frame conversions.
+*
+* --------------------------------------------------------------------
+*/
+
+#ifndef _LINUX_P80211CONV_H
+#define _LINUX_P80211CONV_H
+
+#define WLAN_ETHADDR_LEN 6
+#define WLAN_IEEE_OUI_LEN 3
+
+#define WLAN_ETHCONV_ENCAP 1
+#define WLAN_ETHCONV_8021h 3
+
+#define WLAN_ETHHDR_LEN 14
+
+#define P80211CAPTURE_VERSION 0x80211001
+
+#define P80211_FRMMETA_MAGIC 0x802110
+
+#define P80211SKB_FRMMETA(s) \
+ (((((struct p80211_frmmeta *)((s)->cb))->magic) == \
+ P80211_FRMMETA_MAGIC) ? \
+ ((struct p80211_frmmeta *)((s)->cb)) : \
+ (NULL))
+
+#define P80211SKB_RXMETA(s) \
+ (P80211SKB_FRMMETA((s)) ? P80211SKB_FRMMETA((s))->rx : \
+ ((struct p80211_rxmeta *)(NULL)))
+
+struct p80211_rxmeta {
+ struct wlandevice *wlandev;
+
+ u64 mactime; /* Hi-rez MAC-supplied time value */
+ u64 hosttime; /* Best-rez host supplied time value */
+
+ unsigned int rxrate; /* Receive data rate in 100kbps */
+ unsigned int priority; /* 0-15, 0=contention, 6=CF */
+ int signal; /* An SSI, see p80211netdev.h */
+ int noise; /* An SSI, see p80211netdev.h */
+ unsigned int channel; /* Receive channel (mostly for snifs) */
+ unsigned int preamble; /* P80211ENUM_preambletype_* */
+ unsigned int encoding; /* P80211ENUM_encoding_* */
+
+};
+
+struct p80211_frmmeta {
+ unsigned int magic;
+ struct p80211_rxmeta *rx;
+};
+
+void p80211skb_free(struct wlandevice *wlandev, struct sk_buff *skb);
+int p80211skb_rxmeta_attach(struct wlandevice *wlandev, struct sk_buff *skb);
+void p80211skb_rxmeta_detach(struct sk_buff *skb);
+
+/*
+ * Frame capture header. (See doc/capturefrm.txt)
+ */
+struct p80211_caphdr {
+ u32 version;
+ u32 length;
+ u64 mactime;
+ u64 hosttime;
+ u32 phytype;
+ u32 channel;
+ u32 datarate;
+ u32 antenna;
+ u32 priority;
+ u32 ssi_type;
+ s32 ssi_signal;
+ s32 ssi_noise;
+ u32 preamble;
+ u32 encoding;
+};
+
+/* buffer free method pointer type */
+typedef void (*freebuf_method_t) (void *buf, int size);
+
+struct p80211_metawep {
+ void *data;
+ u8 iv[4];
+ u8 icv[4];
+};
+
+/* local ether header type */
+struct wlan_ethhdr {
+ u8 daddr[WLAN_ETHADDR_LEN];
+ u8 saddr[WLAN_ETHADDR_LEN];
+ u16 type;
+} __packed;
+
+/* local llc header type */
+struct wlan_llc {
+ u8 dsap;
+ u8 ssap;
+ u8 ctl;
+} __packed;
+
+/* local snap header type */
+struct wlan_snap {
+ u8 oui[WLAN_IEEE_OUI_LEN];
+ u16 type;
+} __packed;
+
+/* Circular include trick */
+struct wlandevice;
+
+int skb_p80211_to_ether(struct wlandevice *wlandev, u32 ethconv,
+ struct sk_buff *skb);
+int skb_ether_to_p80211(struct wlandevice *wlandev, u32 ethconv,
+ struct sk_buff *skb, union p80211_hdr *p80211_hdr,
+ struct p80211_metawep *p80211_wep);
+
+int p80211_stt_findproto(u16 proto);
+
+#endif
diff --git a/drivers/staging/wlan-ng/p80211hdr.h b/drivers/staging/wlan-ng/p80211hdr.h
new file mode 100644
index 000000000..79d9b20b3
--- /dev/null
+++ b/drivers/staging/wlan-ng/p80211hdr.h
@@ -0,0 +1,213 @@
+/* p80211hdr.h
+*
+* Macros, types, and functions for handling 802.11 MAC headers
+*
+* Copyright (C) 1999 AbsoluteValue Systems, Inc. All Rights Reserved.
+* --------------------------------------------------------------------
+*
+* linux-wlan
+*
+* The contents of this file are subject to the Mozilla Public
+* License Version 1.1 (the "License"); you may not use this file
+* except in compliance with the License. You may obtain a copy of
+* the License at http://www.mozilla.org/MPL/
+*
+* Software distributed under the License is distributed on an "AS
+* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+* implied. See the License for the specific language governing
+* rights and limitations under the License.
+*
+* Alternatively, the contents of this file may be used under the
+* terms of the GNU Public License version 2 (the "GPL"), in which
+* case the provisions of the GPL are applicable instead of the
+* above. If you wish to allow the use of your version of this file
+* only under the terms of the GPL and not to allow others to use
+* your version of this file under the MPL, indicate your decision
+* by deleting the provisions above and replace them with the notice
+* and other provisions required by the GPL. If you do not delete
+* the provisions above, a recipient may use your version of this
+* file under either the MPL or the GPL.
+*
+* --------------------------------------------------------------------
+*
+* Inquiries regarding the linux-wlan Open Source project can be
+* made directly to:
+*
+* AbsoluteValue Systems Inc.
+* info@linux-wlan.com
+* http://www.linux-wlan.com
+*
+* --------------------------------------------------------------------
+*
+* Portions of the development of this software were funded by
+* Intersil Corporation as part of PRISM(R) chipset product development.
+*
+* --------------------------------------------------------------------
+*
+* This file declares the constants and types used in the interface
+* between a wlan driver and the user mode utilities.
+*
+* Note:
+* - Constant values are always in HOST byte order. To assign
+* values to multi-byte fields they _must_ be converted to
+* ieee byte order. To retrieve multi-byte values from incoming
+* frames, they must be converted to host order.
+*
+* All functions declared here are implemented in p80211.c
+* --------------------------------------------------------------------
+*/
+
+#ifndef _P80211HDR_H
+#define _P80211HDR_H
+
+#include <linux/if_ether.h>
+
+/*--- Sizes -----------------------------------------------*/
+#define WLAN_CRC_LEN 4
+#define WLAN_BSSID_LEN 6
+#define WLAN_HDR_A3_LEN 24
+#define WLAN_HDR_A4_LEN 30
+#define WLAN_SSID_MAXLEN 32
+#define WLAN_DATA_MAXLEN 2312
+#define WLAN_WEP_IV_LEN 4
+#define WLAN_WEP_ICV_LEN 4
+
+/*--- Frame Control Field -------------------------------------*/
+/* Frame Types */
+#define WLAN_FTYPE_MGMT 0x00
+#define WLAN_FTYPE_CTL 0x01
+#define WLAN_FTYPE_DATA 0x02
+
+/* Frame subtypes */
+/* Management */
+#define WLAN_FSTYPE_ASSOCREQ 0x00
+#define WLAN_FSTYPE_ASSOCRESP 0x01
+#define WLAN_FSTYPE_REASSOCREQ 0x02
+#define WLAN_FSTYPE_REASSOCRESP 0x03
+#define WLAN_FSTYPE_PROBEREQ 0x04
+#define WLAN_FSTYPE_PROBERESP 0x05
+#define WLAN_FSTYPE_BEACON 0x08
+#define WLAN_FSTYPE_ATIM 0x09
+#define WLAN_FSTYPE_DISASSOC 0x0a
+#define WLAN_FSTYPE_AUTHEN 0x0b
+#define WLAN_FSTYPE_DEAUTHEN 0x0c
+
+/* Control */
+#define WLAN_FSTYPE_BLOCKACKREQ 0x8
+#define WLAN_FSTYPE_BLOCKACK 0x9
+#define WLAN_FSTYPE_PSPOLL 0x0a
+#define WLAN_FSTYPE_RTS 0x0b
+#define WLAN_FSTYPE_CTS 0x0c
+#define WLAN_FSTYPE_ACK 0x0d
+#define WLAN_FSTYPE_CFEND 0x0e
+#define WLAN_FSTYPE_CFENDCFACK 0x0f
+
+/* Data */
+#define WLAN_FSTYPE_DATAONLY 0x00
+#define WLAN_FSTYPE_DATA_CFACK 0x01
+#define WLAN_FSTYPE_DATA_CFPOLL 0x02
+#define WLAN_FSTYPE_DATA_CFACK_CFPOLL 0x03
+#define WLAN_FSTYPE_NULL 0x04
+#define WLAN_FSTYPE_CFACK 0x05
+#define WLAN_FSTYPE_CFPOLL 0x06
+#define WLAN_FSTYPE_CFACK_CFPOLL 0x07
+
+/*--- FC Macros ----------------------------------------------*/
+/* Macros to get/set the bitfields of the Frame Control Field */
+/* GET_FC_??? - takes the host byte-order value of an FC */
+/* and retrieves the value of one of the */
+/* bitfields and moves that value so its lsb is */
+/* in bit 0. */
+/* SET_FC_??? - takes a host order value for one of the FC */
+/* bitfields and moves it to the proper bit */
+/* location for ORing into a host order FC. */
+/* To send the FC produced from SET_FC_???, */
+/* one must put the bytes in IEEE order. */
+/* e.g. */
+/* printf("the frame subtype is %x", */
+/* GET_FC_FTYPE( ieee2host( rx.fc ))) */
+/* */
+/* tx.fc = host2ieee( SET_FC_FTYPE(WLAN_FTYP_CTL) | */
+/* SET_FC_FSTYPE(WLAN_FSTYPE_RTS) ); */
+/*------------------------------------------------------------*/
+
+#define WLAN_GET_FC_FTYPE(n) ((((u16)(n)) & (BIT(2) | BIT(3))) >> 2)
+#define WLAN_GET_FC_FSTYPE(n) ((((u16)(n)) & (BIT(4)|BIT(5)|BIT(6)|BIT(7))) >> 4)
+#define WLAN_GET_FC_TODS(n) ((((u16)(n)) & (BIT(8))) >> 8)
+#define WLAN_GET_FC_FROMDS(n) ((((u16)(n)) & (BIT(9))) >> 9)
+#define WLAN_GET_FC_ISWEP(n) ((((u16)(n)) & (BIT(14))) >> 14)
+
+#define WLAN_SET_FC_FTYPE(n) (((u16)(n)) << 2)
+#define WLAN_SET_FC_FSTYPE(n) (((u16)(n)) << 4)
+#define WLAN_SET_FC_TODS(n) (((u16)(n)) << 8)
+#define WLAN_SET_FC_FROMDS(n) (((u16)(n)) << 9)
+#define WLAN_SET_FC_ISWEP(n) (((u16)(n)) << 14)
+
+#define DOT11_RATE5_ISBASIC_GET(r) (((u8)(r)) & BIT(7))
+
+/* Generic 802.11 Header types */
+
+struct p80211_hdr_a3 {
+ __le16 fc;
+ u16 dur;
+ u8 a1[ETH_ALEN];
+ u8 a2[ETH_ALEN];
+ u8 a3[ETH_ALEN];
+ u16 seq;
+} __packed;
+
+struct p80211_hdr_a4 {
+ u16 fc;
+ u16 dur;
+ u8 a1[ETH_ALEN];
+ u8 a2[ETH_ALEN];
+ u8 a3[ETH_ALEN];
+ u16 seq;
+ u8 a4[ETH_ALEN];
+} __packed;
+
+union p80211_hdr {
+ struct p80211_hdr_a3 a3;
+ struct p80211_hdr_a4 a4;
+} __packed;
+
+/* Frame and header length macros */
+
+#define WLAN_CTL_FRAMELEN(fstype) (\
+ (fstype) == WLAN_FSTYPE_BLOCKACKREQ ? 24 : \
+ (fstype) == WLAN_FSTYPE_BLOCKACK ? 152 : \
+ (fstype) == WLAN_FSTYPE_PSPOLL ? 20 : \
+ (fstype) == WLAN_FSTYPE_RTS ? 20 : \
+ (fstype) == WLAN_FSTYPE_CTS ? 14 : \
+ (fstype) == WLAN_FSTYPE_ACK ? 14 : \
+ (fstype) == WLAN_FSTYPE_CFEND ? 20 : \
+ (fstype) == WLAN_FSTYPE_CFENDCFACK ? 20 : 4)
+
+#define WLAN_FCS_LEN 4
+
+/* ftcl in HOST order */
+static inline u16 p80211_headerlen(u16 fctl)
+{
+ u16 hdrlen = 0;
+
+ switch (WLAN_GET_FC_FTYPE(fctl)) {
+ case WLAN_FTYPE_MGMT:
+ hdrlen = WLAN_HDR_A3_LEN;
+ break;
+ case WLAN_FTYPE_DATA:
+ hdrlen = WLAN_HDR_A3_LEN;
+ if (WLAN_GET_FC_TODS(fctl) && WLAN_GET_FC_FROMDS(fctl))
+ hdrlen += ETH_ALEN;
+ break;
+ case WLAN_FTYPE_CTL:
+ hdrlen = WLAN_CTL_FRAMELEN(WLAN_GET_FC_FSTYPE(fctl)) -
+ WLAN_FCS_LEN;
+ break;
+ default:
+ hdrlen = WLAN_HDR_A3_LEN;
+ }
+
+ return hdrlen;
+}
+
+#endif /* _P80211HDR_H */
diff --git a/drivers/staging/wlan-ng/p80211ioctl.h b/drivers/staging/wlan-ng/p80211ioctl.h
new file mode 100644
index 000000000..06c5e3664
--- /dev/null
+++ b/drivers/staging/wlan-ng/p80211ioctl.h
@@ -0,0 +1,89 @@
+/* p80211ioctl.h
+*
+* Declares constants and types for the p80211 ioctls
+*
+* Copyright (C) 1999 AbsoluteValue Systems, Inc. All Rights Reserved.
+* --------------------------------------------------------------------
+*
+* linux-wlan
+*
+* The contents of this file are subject to the Mozilla Public
+* License Version 1.1 (the "License"); you may not use this file
+* except in compliance with the License. You may obtain a copy of
+* the License at http://www.mozilla.org/MPL/
+*
+* Software distributed under the License is distributed on an "AS
+* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+* implied. See the License for the specific language governing
+* rights and limitations under the License.
+*
+* Alternatively, the contents of this file may be used under the
+* terms of the GNU Public License version 2 (the "GPL"), in which
+* case the provisions of the GPL are applicable instead of the
+* above. If you wish to allow the use of your version of this file
+* only under the terms of the GPL and not to allow others to use
+* your version of this file under the MPL, indicate your decision
+* by deleting the provisions above and replace them with the notice
+* and other provisions required by the GPL. If you do not delete
+* the provisions above, a recipient may use your version of this
+* file under either the MPL or the GPL.
+*
+* --------------------------------------------------------------------
+*
+* Inquiries regarding the linux-wlan Open Source project can be
+* made directly to:
+*
+* AbsoluteValue Systems Inc.
+* info@linux-wlan.com
+* http://www.linux-wlan.com
+*
+* --------------------------------------------------------------------
+*
+* Portions of the development of this software were funded by
+* Intersil Corporation as part of PRISM(R) chipset product development.
+*
+* --------------------------------------------------------------------
+*
+* While this file is called 'ioctl' is purpose goes a little beyond
+* that. This file defines the types and contants used to implement
+* the p80211 request/confirm/indicate interfaces on Linux. The
+* request/confirm interface is, in fact, normally implemented as an
+* ioctl. The indicate interface on the other hand, is implemented
+* using the Linux 'netlink' interface.
+*
+* The reason I say that request/confirm is 'normally' implemented
+* via ioctl is that we're reserving the right to be able to send
+* request commands via the netlink interface. This will be necessary
+* if we ever need to send request messages when there aren't any
+* wlan network devices present (i.e. sending a message that only p80211
+* cares about.
+* --------------------------------------------------------------------
+*/
+
+#ifndef _P80211IOCTL_H
+#define _P80211IOCTL_H
+
+/* p80211 ioctl "request" codes. See argument 2 of ioctl(2). */
+
+#define P80211_IFTEST (SIOCDEVPRIVATE + 0)
+#define P80211_IFREQ (SIOCDEVPRIVATE + 1)
+
+/*----------------------------------------------------------------*/
+/* Magic number, a quick test to see we're getting the desired struct */
+
+#define P80211_IOCTL_MAGIC (0x4a2d464dUL)
+
+/*----------------------------------------------------------------*/
+/* A ptr to the following structure type is passed as the third */
+/* argument to the ioctl system call when issuing a request to */
+/* the p80211 module. */
+
+struct p80211ioctl_req {
+ char name[WLAN_DEVNAMELEN_MAX];
+ caddr_t data;
+ u32 magic;
+ u16 len;
+ u32 result;
+} __packed;
+
+#endif /* _P80211IOCTL_H */
diff --git a/drivers/staging/wlan-ng/p80211meta.h b/drivers/staging/wlan-ng/p80211meta.h
new file mode 100644
index 000000000..c5f1a63ad
--- /dev/null
+++ b/drivers/staging/wlan-ng/p80211meta.h
@@ -0,0 +1,90 @@
+/* p80211meta.h
+*
+* Macros, constants, types, and funcs for p80211 metadata
+*
+* Copyright (C) 1999 AbsoluteValue Systems, Inc. All Rights Reserved.
+* --------------------------------------------------------------------
+*
+* linux-wlan
+*
+* The contents of this file are subject to the Mozilla Public
+* License Version 1.1 (the "License"); you may not use this file
+* except in compliance with the License. You may obtain a copy of
+* the License at http://www.mozilla.org/MPL/
+*
+* Software distributed under the License is distributed on an "AS
+* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+* implied. See the License for the specific language governing
+* rights and limitations under the License.
+*
+* Alternatively, the contents of this file may be used under the
+* terms of the GNU Public License version 2 (the "GPL"), in which
+* case the provisions of the GPL are applicable instead of the
+* above. If you wish to allow the use of your version of this file
+* only under the terms of the GPL and not to allow others to use
+* your version of this file under the MPL, indicate your decision
+* by deleting the provisions above and replace them with the notice
+* and other provisions required by the GPL. If you do not delete
+* the provisions above, a recipient may use your version of this
+* file under either the MPL or the GPL.
+*
+* --------------------------------------------------------------------
+*
+* Inquiries regarding the linux-wlan Open Source project can be
+* made directly to:
+*
+* AbsoluteValue Systems Inc.
+* info@linux-wlan.com
+* http://www.linux-wlan.com
+*
+* --------------------------------------------------------------------
+*
+* Portions of the development of this software were funded by
+* Intersil Corporation as part of PRISM(R) chipset product development.
+*
+* --------------------------------------------------------------------
+*
+* This file declares some of the constants and types used in various
+* parts of the linux-wlan system.
+*
+* Notes:
+* - Constant values are always in HOST byte order.
+*
+* All functions and statics declared here are implemented in p80211types.c
+* --------------------------------------------------------------------
+*/
+
+#ifndef _P80211META_H
+#define _P80211META_H
+
+/*----------------------------------------------------------------*/
+/* The following structure types are used for the metadata */
+/* representation of category list metadata, group list metadata, */
+/* and data item metadata for both Mib and Messages. */
+
+struct p80211meta {
+ char *name; /* data item name */
+ u32 did; /* partial did */
+ u32 flags; /* set of various flag bits */
+ u32 min; /* min value of a BOUNDEDint */
+ u32 max; /* max value of a BOUNDEDint */
+
+ u32 maxlen; /* maxlen of a OCTETSTR or DISPLAYSTR */
+ u32 minlen; /* minlen of a OCTETSTR or DISPLAYSTR */
+ p80211enum_t *enumptr; /* ptr to the enum type for ENUMint */
+ p80211_totext_t totextptr; /* ptr to totext conversion function */
+ p80211_fromtext_t fromtextptr; /* ptr to totext conversion function */
+ p80211_valid_t validfunptr; /* ptr to totext conversion function */
+};
+
+struct grplistitem {
+ char *name;
+ struct p80211meta *itemlist;
+};
+
+struct catlistitem {
+ char *name;
+ struct grplistitem *grplist;
+};
+
+#endif /* _P80211META_H */
diff --git a/drivers/staging/wlan-ng/p80211metadef.h b/drivers/staging/wlan-ng/p80211metadef.h
new file mode 100644
index 000000000..0ccfba129
--- /dev/null
+++ b/drivers/staging/wlan-ng/p80211metadef.h
@@ -0,0 +1,261 @@
+/* This file is GENERATED AUTOMATICALLY. DO NOT EDIT OR MODIFY.
+* --------------------------------------------------------------------
+*
+* Copyright (C) 1999 AbsoluteValue Systems, Inc. All Rights Reserved.
+* --------------------------------------------------------------------
+*
+* linux-wlan
+*
+* The contents of this file are subject to the Mozilla Public
+* License Version 1.1 (the "License"); you may not use this file
+* except in compliance with the License. You may obtain a copy of
+* the License at http://www.mozilla.org/MPL/
+*
+* Software distributed under the License is distributed on an "AS
+* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+* implied. See the License for the specific language governing
+* rights and limitations under the License.
+*
+* Alternatively, the contents of this file may be used under the
+* terms of the GNU Public License version 2 (the "GPL"), in which
+* case the provisions of the GPL are applicable instead of the
+* above. If you wish to allow the use of your version of this file
+* only under the terms of the GPL and not to allow others to use
+* your version of this file under the MPL, indicate your decision
+* by deleting the provisions above and replace them with the notice
+* and other provisions required by the GPL. If you do not delete
+* the provisions above, a recipient may use your version of this
+* file under either the MPL or the GPL.
+*
+* --------------------------------------------------------------------
+*
+* Inquiries regarding the linux-wlan Open Source project can be
+* made directly to:
+*
+* AbsoluteValue Systems Inc.
+* info@linux-wlan.com
+* http://www.linux-wlan.com
+*
+* --------------------------------------------------------------------
+*
+* Portions of the development of this software were funded by
+* Intersil Corporation as part of PRISM(R) chipset product development.
+*
+* --------------------------------------------------------------------
+*/
+
+#ifndef _P80211MKMETADEF_H
+#define _P80211MKMETADEF_H
+
+#define DIDmsg_dot11req_mibget \
+ (P80211DID_MKSECTION(1) | \
+ P80211DID_MKGROUP(1))
+#define DIDmsg_dot11req_mibget_mibattribute \
+ (P80211DID_MKSECTION(1) | \
+ P80211DID_MKGROUP(1) | \
+ P80211DID_MKITEM(1) | 0x00000000)
+#define DIDmsg_dot11req_mibget_resultcode \
+ (P80211DID_MKSECTION(1) | \
+ P80211DID_MKGROUP(1) | \
+ P80211DID_MKITEM(2) | 0x00000000)
+#define DIDmsg_dot11req_mibset \
+ (P80211DID_MKSECTION(1) | \
+ P80211DID_MKGROUP(2))
+#define DIDmsg_dot11req_mibset_mibattribute \
+ (P80211DID_MKSECTION(1) | \
+ P80211DID_MKGROUP(2) | \
+ P80211DID_MKITEM(1) | 0x00000000)
+#define DIDmsg_dot11req_mibset_resultcode \
+ (P80211DID_MKSECTION(1) | \
+ P80211DID_MKGROUP(2) | \
+ P80211DID_MKITEM(2) | 0x00000000)
+#define DIDmsg_dot11req_scan \
+ (P80211DID_MKSECTION(1) | \
+ P80211DID_MKGROUP(4))
+#define DIDmsg_dot11req_scan_results \
+ (P80211DID_MKSECTION(1) | \
+ P80211DID_MKGROUP(5))
+#define DIDmsg_dot11req_start \
+ (P80211DID_MKSECTION(1) | \
+ P80211DID_MKGROUP(13))
+#define DIDmsg_dot11ind_authenticate \
+ (P80211DID_MKSECTION(2) | \
+ P80211DID_MKGROUP(1))
+#define DIDmsg_dot11ind_associate \
+ (P80211DID_MKSECTION(2) | \
+ P80211DID_MKGROUP(3))
+#define DIDmsg_lnxreq_ifstate \
+ (P80211DID_MKSECTION(3) | \
+ P80211DID_MKGROUP(1))
+#define DIDmsg_lnxreq_wlansniff \
+ (P80211DID_MKSECTION(3) | \
+ P80211DID_MKGROUP(2))
+#define DIDmsg_lnxreq_hostwep \
+ (P80211DID_MKSECTION(3) | \
+ P80211DID_MKGROUP(3))
+#define DIDmsg_lnxreq_commsquality \
+ (P80211DID_MKSECTION(3) | \
+ P80211DID_MKGROUP(4))
+#define DIDmsg_lnxreq_autojoin \
+ (P80211DID_MKSECTION(3) | \
+ P80211DID_MKGROUP(5))
+#define DIDmsg_p2req_readpda \
+ (P80211DID_MKSECTION(5) | \
+ P80211DID_MKGROUP(2))
+#define DIDmsg_p2req_readpda_pda \
+ (P80211DID_MKSECTION(5) | \
+ P80211DID_MKGROUP(2) | \
+ P80211DID_MKITEM(1) | 0x00000000)
+#define DIDmsg_p2req_readpda_resultcode \
+ (P80211DID_MKSECTION(5) | \
+ P80211DID_MKGROUP(2) | \
+ P80211DID_MKITEM(2) | 0x00000000)
+#define DIDmsg_p2req_ramdl_state \
+ (P80211DID_MKSECTION(5) | \
+ P80211DID_MKGROUP(11))
+#define DIDmsg_p2req_ramdl_state_enable \
+ (P80211DID_MKSECTION(5) | \
+ P80211DID_MKGROUP(11) | \
+ P80211DID_MKITEM(1) | 0x00000000)
+#define DIDmsg_p2req_ramdl_state_exeaddr \
+ (P80211DID_MKSECTION(5) | \
+ P80211DID_MKGROUP(11) | \
+ P80211DID_MKITEM(2) | 0x00000000)
+#define DIDmsg_p2req_ramdl_state_resultcode \
+ (P80211DID_MKSECTION(5) | \
+ P80211DID_MKGROUP(11) | \
+ P80211DID_MKITEM(3) | 0x00000000)
+#define DIDmsg_p2req_ramdl_write \
+ (P80211DID_MKSECTION(5) | \
+ P80211DID_MKGROUP(12))
+#define DIDmsg_p2req_ramdl_write_addr \
+ (P80211DID_MKSECTION(5) | \
+ P80211DID_MKGROUP(12) | \
+ P80211DID_MKITEM(1) | 0x00000000)
+#define DIDmsg_p2req_ramdl_write_len \
+ (P80211DID_MKSECTION(5) | \
+ P80211DID_MKGROUP(12) | \
+ P80211DID_MKITEM(2) | 0x00000000)
+#define DIDmsg_p2req_ramdl_write_data \
+ (P80211DID_MKSECTION(5) | \
+ P80211DID_MKGROUP(12) | \
+ P80211DID_MKITEM(3) | 0x00000000)
+#define DIDmsg_p2req_ramdl_write_resultcode \
+ (P80211DID_MKSECTION(5) | \
+ P80211DID_MKGROUP(12) | \
+ P80211DID_MKITEM(4) | 0x00000000)
+#define DIDmsg_p2req_flashdl_state \
+ (P80211DID_MKSECTION(5) | \
+ P80211DID_MKGROUP(13))
+#define DIDmsg_p2req_flashdl_write \
+ (P80211DID_MKSECTION(5) | \
+ P80211DID_MKGROUP(14))
+#define DIDmib_cat_dot11smt \
+ P80211DID_MKSECTION(1)
+#define DIDmib_dot11smt_dot11WEPDefaultKeysTable \
+ (P80211DID_MKSECTION(1) | \
+ P80211DID_MKGROUP(4))
+#define DIDmib_dot11smt_dot11WEPDefaultKeysTable_dot11WEPDefaultKey0 \
+ (P80211DID_MKSECTION(1) | \
+ P80211DID_MKGROUP(4) | \
+ P80211DID_MKITEM(1) | 0x0c000000)
+#define DIDmib_dot11smt_dot11WEPDefaultKeysTable_dot11WEPDefaultKey1 \
+ (P80211DID_MKSECTION(1) | \
+ P80211DID_MKGROUP(4) | \
+ P80211DID_MKITEM(2) | 0x0c000000)
+#define DIDmib_dot11smt_dot11WEPDefaultKeysTable_dot11WEPDefaultKey2 \
+ (P80211DID_MKSECTION(1) | \
+ P80211DID_MKGROUP(4) | \
+ P80211DID_MKITEM(3) | 0x0c000000)
+#define DIDmib_dot11smt_dot11WEPDefaultKeysTable_dot11WEPDefaultKey3 \
+ (P80211DID_MKSECTION(1) | \
+ P80211DID_MKGROUP(4) | \
+ P80211DID_MKITEM(4) | 0x0c000000)
+#define DIDmib_dot11smt_dot11PrivacyTable \
+ (P80211DID_MKSECTION(1) | \
+ P80211DID_MKGROUP(6))
+#define DIDmib_dot11smt_dot11PrivacyTable_dot11PrivacyInvoked \
+ (P80211DID_MKSECTION(1) | \
+ P80211DID_MKGROUP(6) | \
+ P80211DID_MKITEM(1) | 0x18000000)
+#define DIDmib_dot11smt_dot11PrivacyTable_dot11WEPDefaultKeyID \
+ (P80211DID_MKSECTION(1) | \
+ P80211DID_MKGROUP(6) | \
+ P80211DID_MKITEM(2) | 0x18000000)
+#define DIDmib_dot11smt_dot11PrivacyTable_dot11ExcludeUnencrypted \
+ (P80211DID_MKSECTION(1) | \
+ P80211DID_MKGROUP(6) | \
+ P80211DID_MKITEM(4) | 0x18000000)
+#define DIDmib_dot11mac_dot11OperationTable \
+ (P80211DID_MKSECTION(2) | \
+ P80211DID_MKGROUP(1))
+#define DIDmib_dot11mac_dot11OperationTable_dot11MACAddress \
+ (P80211DID_MKSECTION(2) | \
+ P80211DID_MKGROUP(1) | \
+ P80211DID_MKITEM(1) | 0x18000000)
+#define DIDmib_dot11mac_dot11OperationTable_dot11RTSThreshold \
+ (P80211DID_MKSECTION(2) | \
+ P80211DID_MKGROUP(1) | \
+ P80211DID_MKITEM(2) | 0x18000000)
+#define DIDmib_dot11mac_dot11OperationTable_dot11ShortRetryLimit \
+ (P80211DID_MKSECTION(2) | \
+ P80211DID_MKGROUP(1) | \
+ P80211DID_MKITEM(3) | 0x10000000)
+#define DIDmib_dot11mac_dot11OperationTable_dot11LongRetryLimit \
+ (P80211DID_MKSECTION(2) | \
+ P80211DID_MKGROUP(1) | \
+ P80211DID_MKITEM(4) | 0x10000000)
+#define DIDmib_dot11mac_dot11OperationTable_dot11FragmentationThreshold \
+ (P80211DID_MKSECTION(2) | \
+ P80211DID_MKGROUP(1) | \
+ P80211DID_MKITEM(5) | 0x18000000)
+#define DIDmib_dot11mac_dot11OperationTable_dot11MaxTransmitMSDULifetime \
+ (P80211DID_MKSECTION(2) | \
+ P80211DID_MKGROUP(1) | \
+ P80211DID_MKITEM(6) | 0x10000000)
+#define DIDmib_cat_dot11phy \
+ P80211DID_MKSECTION(3)
+#define DIDmib_dot11phy_dot11PhyOperationTable \
+ (P80211DID_MKSECTION(3) | \
+ P80211DID_MKGROUP(1))
+#define DIDmib_dot11phy_dot11PhyTxPowerTable_dot11CurrentTxPowerLevel \
+ (P80211DID_MKSECTION(3) | \
+ P80211DID_MKGROUP(3) | \
+ P80211DID_MKITEM(10) | 0x18000000)
+#define DIDmib_dot11phy_dot11PhyDSSSTable \
+ (P80211DID_MKSECTION(3) | \
+ P80211DID_MKGROUP(5))
+#define DIDmib_dot11phy_dot11PhyDSSSTable_dot11CurrentChannel \
+ (P80211DID_MKSECTION(3) | \
+ P80211DID_MKGROUP(5) | \
+ P80211DID_MKITEM(1) | 0x10000000)
+#define DIDmib_cat_lnx \
+ P80211DID_MKSECTION(4)
+#define DIDmib_lnx_lnxConfigTable \
+ (P80211DID_MKSECTION(4) | \
+ P80211DID_MKGROUP(1))
+#define DIDmib_lnx_lnxConfigTable_lnxRSNAIE \
+ (P80211DID_MKSECTION(4) | \
+ P80211DID_MKGROUP(1) | \
+ P80211DID_MKITEM(1) | 0x18000000)
+#define DIDmib_cat_p2 \
+ P80211DID_MKSECTION(5)
+#define DIDmib_p2_p2Static \
+ (P80211DID_MKSECTION(5) | \
+ P80211DID_MKGROUP(2))
+#define DIDmib_p2_p2Static_p2CnfPortType \
+ (P80211DID_MKSECTION(5) | \
+ P80211DID_MKGROUP(2) | \
+ P80211DID_MKITEM(1) | 0x18000000)
+#define DIDmib_p2_p2NIC_p2PRISupRange \
+ (P80211DID_MKSECTION(5) | \
+ P80211DID_MKGROUP(5) | \
+ P80211DID_MKITEM(6) | 0x10000000)
+#define DIDmib_p2_p2MAC \
+ (P80211DID_MKSECTION(5) | \
+ P80211DID_MKGROUP(6))
+#define DIDmib_p2_p2MAC_p2CurrentTxRate \
+ (P80211DID_MKSECTION(5) | \
+ P80211DID_MKGROUP(6) | \
+ P80211DID_MKITEM(12) | 0x10000000)
+#endif
diff --git a/drivers/staging/wlan-ng/p80211metastruct.h b/drivers/staging/wlan-ng/p80211metastruct.h
new file mode 100644
index 000000000..c501162c3
--- /dev/null
+++ b/drivers/staging/wlan-ng/p80211metastruct.h
@@ -0,0 +1,271 @@
+/* This file is GENERATED AUTOMATICALLY. DO NOT EDIT OR MODIFY.
+* --------------------------------------------------------------------
+*
+* Copyright (C) 1999 AbsoluteValue Systems, Inc. All Rights Reserved.
+* --------------------------------------------------------------------
+*
+* linux-wlan
+*
+* The contents of this file are subject to the Mozilla Public
+* License Version 1.1 (the "License"); you may not use this file
+* except in compliance with the License. You may obtain a copy of
+* the License at http://www.mozilla.org/MPL/
+*
+* Software distributed under the License is distributed on an "AS
+* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+* implied. See the License for the specific language governing
+* rights and limitations under the License.
+*
+* Alternatively, the contents of this file may be used under the
+* terms of the GNU Public License version 2 (the "GPL"), in which
+* case the provisions of the GPL are applicable instead of the
+* above. If you wish to allow the use of your version of this file
+* only under the terms of the GPL and not to allow others to use
+* your version of this file under the MPL, indicate your decision
+* by deleting the provisions above and replace them with the notice
+* and other provisions required by the GPL. If you do not delete
+* the provisions above, a recipient may use your version of this
+* file under either the MPL or the GPL.
+*
+* --------------------------------------------------------------------
+*
+* Inquiries regarding the linux-wlan Open Source project can be
+* made directly to:
+*
+* AbsoluteValue Systems Inc.
+* info@linux-wlan.com
+* http://www.linux-wlan.com
+*
+* --------------------------------------------------------------------
+*
+* Portions of the development of this software were funded by
+* Intersil Corporation as part of PRISM(R) chipset product development.
+*
+* --------------------------------------------------------------------
+*/
+
+#ifndef _P80211MKMETASTRUCT_H
+#define _P80211MKMETASTRUCT_H
+
+struct p80211msg_dot11req_mibget {
+ u32 msgcode;
+ u32 msglen;
+ u8 devname[WLAN_DEVNAMELEN_MAX];
+ p80211item_unk392_t mibattribute;
+ p80211item_uint32_t resultcode;
+} __packed;
+
+struct p80211msg_dot11req_mibset {
+ u32 msgcode;
+ u32 msglen;
+ u8 devname[WLAN_DEVNAMELEN_MAX];
+ p80211item_unk392_t mibattribute;
+ p80211item_uint32_t resultcode;
+} __packed;
+
+struct p80211msg_dot11req_scan {
+ u32 msgcode;
+ u32 msglen;
+ u8 devname[WLAN_DEVNAMELEN_MAX];
+ p80211item_uint32_t bsstype;
+ p80211item_pstr6_t bssid;
+ u8 pad_0C[1];
+ p80211item_pstr32_t ssid;
+ u8 pad_1D[3];
+ p80211item_uint32_t scantype;
+ p80211item_uint32_t probedelay;
+ p80211item_pstr14_t channellist;
+ u8 pad_2C[1];
+ p80211item_uint32_t minchanneltime;
+ p80211item_uint32_t maxchanneltime;
+ p80211item_uint32_t resultcode;
+ p80211item_uint32_t numbss;
+ p80211item_uint32_t append;
+} __packed;
+
+struct p80211msg_dot11req_scan_results {
+ u32 msgcode;
+ u32 msglen;
+ u8 devname[WLAN_DEVNAMELEN_MAX];
+ p80211item_uint32_t bssindex;
+ p80211item_uint32_t resultcode;
+ p80211item_uint32_t signal;
+ p80211item_uint32_t noise;
+ p80211item_pstr6_t bssid;
+ u8 pad_3C[1];
+ p80211item_pstr32_t ssid;
+ u8 pad_4D[3];
+ p80211item_uint32_t bsstype;
+ p80211item_uint32_t beaconperiod;
+ p80211item_uint32_t dtimperiod;
+ p80211item_uint32_t timestamp;
+ p80211item_uint32_t localtime;
+ p80211item_uint32_t fhdwelltime;
+ p80211item_uint32_t fhhopset;
+ p80211item_uint32_t fhhoppattern;
+ p80211item_uint32_t fhhopindex;
+ p80211item_uint32_t dschannel;
+ p80211item_uint32_t cfpcount;
+ p80211item_uint32_t cfpperiod;
+ p80211item_uint32_t cfpmaxduration;
+ p80211item_uint32_t cfpdurremaining;
+ p80211item_uint32_t ibssatimwindow;
+ p80211item_uint32_t cfpollable;
+ p80211item_uint32_t cfpollreq;
+ p80211item_uint32_t privacy;
+ p80211item_uint32_t capinfo;
+ p80211item_uint32_t basicrate1;
+ p80211item_uint32_t basicrate2;
+ p80211item_uint32_t basicrate3;
+ p80211item_uint32_t basicrate4;
+ p80211item_uint32_t basicrate5;
+ p80211item_uint32_t basicrate6;
+ p80211item_uint32_t basicrate7;
+ p80211item_uint32_t basicrate8;
+ p80211item_uint32_t supprate1;
+ p80211item_uint32_t supprate2;
+ p80211item_uint32_t supprate3;
+ p80211item_uint32_t supprate4;
+ p80211item_uint32_t supprate5;
+ p80211item_uint32_t supprate6;
+ p80211item_uint32_t supprate7;
+ p80211item_uint32_t supprate8;
+} __packed;
+
+struct p80211msg_dot11req_start {
+ u32 msgcode;
+ u32 msglen;
+ u8 devname[WLAN_DEVNAMELEN_MAX];
+ p80211item_pstr32_t ssid;
+ u8 pad_12D[3];
+ p80211item_uint32_t bsstype;
+ p80211item_uint32_t beaconperiod;
+ p80211item_uint32_t dtimperiod;
+ p80211item_uint32_t cfpperiod;
+ p80211item_uint32_t cfpmaxduration;
+ p80211item_uint32_t fhdwelltime;
+ p80211item_uint32_t fhhopset;
+ p80211item_uint32_t fhhoppattern;
+ p80211item_uint32_t dschannel;
+ p80211item_uint32_t ibssatimwindow;
+ p80211item_uint32_t probedelay;
+ p80211item_uint32_t cfpollable;
+ p80211item_uint32_t cfpollreq;
+ p80211item_uint32_t basicrate1;
+ p80211item_uint32_t basicrate2;
+ p80211item_uint32_t basicrate3;
+ p80211item_uint32_t basicrate4;
+ p80211item_uint32_t basicrate5;
+ p80211item_uint32_t basicrate6;
+ p80211item_uint32_t basicrate7;
+ p80211item_uint32_t basicrate8;
+ p80211item_uint32_t operationalrate1;
+ p80211item_uint32_t operationalrate2;
+ p80211item_uint32_t operationalrate3;
+ p80211item_uint32_t operationalrate4;
+ p80211item_uint32_t operationalrate5;
+ p80211item_uint32_t operationalrate6;
+ p80211item_uint32_t operationalrate7;
+ p80211item_uint32_t operationalrate8;
+ p80211item_uint32_t resultcode;
+} __packed;
+
+struct p80211msg_lnxreq_ifstate {
+ u32 msgcode;
+ u32 msglen;
+ u8 devname[WLAN_DEVNAMELEN_MAX];
+ p80211item_uint32_t ifstate;
+ p80211item_uint32_t resultcode;
+} __packed;
+
+struct p80211msg_lnxreq_wlansniff {
+ u32 msgcode;
+ u32 msglen;
+ u8 devname[WLAN_DEVNAMELEN_MAX];
+ p80211item_uint32_t enable;
+ p80211item_uint32_t channel;
+ p80211item_uint32_t prismheader;
+ p80211item_uint32_t wlanheader;
+ p80211item_uint32_t keepwepflags;
+ p80211item_uint32_t stripfcs;
+ p80211item_uint32_t packet_trunc;
+ p80211item_uint32_t resultcode;
+} __packed;
+
+struct p80211msg_lnxreq_hostwep {
+ u32 msgcode;
+ u32 msglen;
+ u8 devname[WLAN_DEVNAMELEN_MAX];
+ p80211item_uint32_t resultcode;
+ p80211item_uint32_t decrypt;
+ p80211item_uint32_t encrypt;
+} __packed;
+
+struct p80211msg_lnxreq_commsquality {
+ u32 msgcode;
+ u32 msglen;
+ u8 devname[WLAN_DEVNAMELEN_MAX];
+ p80211item_uint32_t resultcode;
+ p80211item_uint32_t dbm;
+ p80211item_uint32_t link;
+ p80211item_uint32_t level;
+ p80211item_uint32_t noise;
+ p80211item_uint32_t txrate;
+} __packed;
+
+struct p80211msg_lnxreq_autojoin {
+ u32 msgcode;
+ u32 msglen;
+ u8 devname[WLAN_DEVNAMELEN_MAX];
+ p80211item_pstr32_t ssid;
+ u8 pad_19D[3];
+ p80211item_uint32_t authtype;
+ p80211item_uint32_t resultcode;
+} __packed;
+
+struct p80211msg_p2req_readpda {
+ u32 msgcode;
+ u32 msglen;
+ u8 devname[WLAN_DEVNAMELEN_MAX];
+ p80211item_unk1024_t pda;
+ p80211item_uint32_t resultcode;
+} __packed;
+
+struct p80211msg_p2req_ramdl_state {
+ u32 msgcode;
+ u32 msglen;
+ u8 devname[WLAN_DEVNAMELEN_MAX];
+ p80211item_uint32_t enable;
+ p80211item_uint32_t exeaddr;
+ p80211item_uint32_t resultcode;
+} __packed;
+
+struct p80211msg_p2req_ramdl_write {
+ u32 msgcode;
+ u32 msglen;
+ u8 devname[WLAN_DEVNAMELEN_MAX];
+ p80211item_uint32_t addr;
+ p80211item_uint32_t len;
+ p80211item_unk4096_t data;
+ p80211item_uint32_t resultcode;
+} __packed;
+
+struct p80211msg_p2req_flashdl_state {
+ u32 msgcode;
+ u32 msglen;
+ u8 devname[WLAN_DEVNAMELEN_MAX];
+ p80211item_uint32_t enable;
+ p80211item_uint32_t resultcode;
+} __packed;
+
+struct p80211msg_p2req_flashdl_write {
+ u32 msgcode;
+ u32 msglen;
+ u8 devname[WLAN_DEVNAMELEN_MAX];
+ p80211item_uint32_t addr;
+ p80211item_uint32_t len;
+ p80211item_unk4096_t data;
+ p80211item_uint32_t resultcode;
+} __packed;
+
+#endif
diff --git a/drivers/staging/wlan-ng/p80211mgmt.h b/drivers/staging/wlan-ng/p80211mgmt.h
new file mode 100644
index 000000000..3dd066ac0
--- /dev/null
+++ b/drivers/staging/wlan-ng/p80211mgmt.h
@@ -0,0 +1,520 @@
+/* p80211mgmt.h
+*
+* Macros, types, and functions to handle 802.11 mgmt frames
+*
+* Copyright (C) 1999 AbsoluteValue Systems, Inc. All Rights Reserved.
+* --------------------------------------------------------------------
+*
+* linux-wlan
+*
+* The contents of this file are subject to the Mozilla Public
+* License Version 1.1 (the "License"); you may not use this file
+* except in compliance with the License. You may obtain a copy of
+* the License at http://www.mozilla.org/MPL/
+*
+* Software distributed under the License is distributed on an "AS
+* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+* implied. See the License for the specific language governing
+* rights and limitations under the License.
+*
+* Alternatively, the contents of this file may be used under the
+* terms of the GNU Public License version 2 (the "GPL"), in which
+* case the provisions of the GPL are applicable instead of the
+* above. If you wish to allow the use of your version of this file
+* only under the terms of the GPL and not to allow others to use
+* your version of this file under the MPL, indicate your decision
+* by deleting the provisions above and replace them with the notice
+* and other provisions required by the GPL. If you do not delete
+* the provisions above, a recipient may use your version of this
+* file under either the MPL or the GPL.
+*
+* --------------------------------------------------------------------
+*
+* Inquiries regarding the linux-wlan Open Source project can be
+* made directly to:
+*
+* AbsoluteValue Systems Inc.
+* info@linux-wlan.com
+* http://www.linux-wlan.com
+*
+* --------------------------------------------------------------------
+*
+* Portions of the development of this software were funded by
+* Intersil Corporation as part of PRISM(R) chipset product development.
+*
+* --------------------------------------------------------------------
+*
+* This file declares the constants and types used in the interface
+* between a wlan driver and the user mode utilities.
+*
+* Notes:
+* - Constant values are always in HOST byte order. To assign
+* values to multi-byte fields they _must_ be converted to
+* ieee byte order. To retrieve multi-byte values from incoming
+* frames, they must be converted to host order.
+*
+* - The len member of the frame structure does NOT!!! include
+* the MAC CRC. Therefore, the len field on rx'd frames should
+* have 4 subtracted from it.
+*
+* All functions declared here are implemented in p80211.c
+*
+* The types, macros, and functions defined here are primarily
+* used for encoding and decoding management frames. They are
+* designed to follow these patterns of use:
+*
+* DECODE:
+* 1) a frame of length len is received into buffer b
+* 2) using the hdr structure and macros, we determine the type
+* 3) an appropriate mgmt frame structure, mf, is allocated and zeroed
+* 4) mf.hdr = b
+* mf.buf = b
+* mf.len = len
+* 5) call mgmt_decode( mf )
+* 6) the frame field pointers in mf are now set. Note that any
+* multi-byte frame field values accessed using the frame field
+* pointers are in ieee byte order and will have to be converted
+* to host order.
+*
+* ENCODE:
+* 1) Library client allocates buffer space for maximum length
+* frame of the desired type
+* 2) Library client allocates a mgmt frame structure, called mf,
+* of the desired type
+* 3) Set the following:
+* mf.type = <desired type>
+* mf.buf = <allocated buffer address>
+* 4) call mgmt_encode( mf )
+* 5) all of the fixed field pointers and fixed length information element
+* pointers in mf are now set to their respective locations in the
+* allocated space (fortunately, all variable length information elements
+* fall at the end of their respective frames).
+* 5a) The length field is set to include the last of the fixed and fixed
+* length fields. It may have to be updated for optional or variable
+* length information elements.
+* 6) Optional and variable length information elements are special cases
+* and must be handled individually by the client code.
+* --------------------------------------------------------------------
+*/
+
+#ifndef _P80211MGMT_H
+#define _P80211MGMT_H
+
+#ifndef _P80211HDR_H
+#include "p80211hdr.h"
+#endif
+
+/*-- Information Element IDs --------------------*/
+#define WLAN_EID_SSID 0
+#define WLAN_EID_SUPP_RATES 1
+#define WLAN_EID_FH_PARMS 2
+#define WLAN_EID_DS_PARMS 3
+#define WLAN_EID_CF_PARMS 4
+#define WLAN_EID_TIM 5
+#define WLAN_EID_IBSS_PARMS 6
+/*-- values 7-15 reserved --*/
+#define WLAN_EID_CHALLENGE 16
+/*-- values 17-31 reserved for challenge text extension --*/
+/*-- values 32-255 reserved --*/
+
+/*-- Reason Codes -------------------------------*/
+#define WLAN_MGMT_REASON_RSVD 0
+#define WLAN_MGMT_REASON_UNSPEC 1
+#define WLAN_MGMT_REASON_PRIOR_AUTH_INVALID 2
+#define WLAN_MGMT_REASON_DEAUTH_LEAVING 3
+#define WLAN_MGMT_REASON_DISASSOC_INACTIVE 4
+#define WLAN_MGMT_REASON_DISASSOC_AP_BUSY 5
+#define WLAN_MGMT_REASON_CLASS2_NONAUTH 6
+#define WLAN_MGMT_REASON_CLASS3_NONASSOC 7
+#define WLAN_MGMT_REASON_DISASSOC_STA_HASLEFT 8
+#define WLAN_MGMT_REASON_CANT_ASSOC_NONAUTH 9
+
+/*-- Status Codes -------------------------------*/
+#define WLAN_MGMT_STATUS_SUCCESS 0
+#define WLAN_MGMT_STATUS_UNSPEC_FAILURE 1
+#define WLAN_MGMT_STATUS_CAPS_UNSUPPORTED 10
+#define WLAN_MGMT_STATUS_REASSOC_NO_ASSOC 11
+#define WLAN_MGMT_STATUS_ASSOC_DENIED_UNSPEC 12
+#define WLAN_MGMT_STATUS_UNSUPPORTED_AUTHALG 13
+#define WLAN_MGMT_STATUS_RX_AUTH_NOSEQ 14
+#define WLAN_MGMT_STATUS_CHALLENGE_FAIL 15
+#define WLAN_MGMT_STATUS_AUTH_TIMEOUT 16
+#define WLAN_MGMT_STATUS_ASSOC_DENIED_BUSY 17
+#define WLAN_MGMT_STATUS_ASSOC_DENIED_RATES 18
+ /* p80211b additions */
+#define WLAN_MGMT_STATUS_ASSOC_DENIED_NOSHORT 19
+#define WLAN_MGMT_STATUS_ASSOC_DENIED_NOPBCC 20
+#define WLAN_MGMT_STATUS_ASSOC_DENIED_NOAGILITY 21
+
+/*-- Auth Algorithm Field ---------------------------*/
+#define WLAN_AUTH_ALG_OPENSYSTEM 0
+#define WLAN_AUTH_ALG_SHAREDKEY 1
+
+/*-- Management Frame Field Offsets -------------*/
+/* Note: Not all fields are listed because of variable lengths, */
+/* see the code in p80211.c to see how we search for fields */
+/* Note: These offsets are from the start of the frame data */
+
+#define WLAN_BEACON_OFF_TS 0
+#define WLAN_BEACON_OFF_BCN_int 8
+#define WLAN_BEACON_OFF_CAPINFO 10
+#define WLAN_BEACON_OFF_SSID 12
+
+#define WLAN_DISASSOC_OFF_REASON 0
+
+#define WLAN_ASSOCREQ_OFF_CAP_INFO 0
+#define WLAN_ASSOCREQ_OFF_LISTEN_int 2
+#define WLAN_ASSOCREQ_OFF_SSID 4
+
+#define WLAN_ASSOCRESP_OFF_CAP_INFO 0
+#define WLAN_ASSOCRESP_OFF_STATUS 2
+#define WLAN_ASSOCRESP_OFF_AID 4
+#define WLAN_ASSOCRESP_OFF_SUPP_RATES 6
+
+#define WLAN_REASSOCREQ_OFF_CAP_INFO 0
+#define WLAN_REASSOCREQ_OFF_LISTEN_int 2
+#define WLAN_REASSOCREQ_OFF_CURR_AP 4
+#define WLAN_REASSOCREQ_OFF_SSID 10
+
+#define WLAN_REASSOCRESP_OFF_CAP_INFO 0
+#define WLAN_REASSOCRESP_OFF_STATUS 2
+#define WLAN_REASSOCRESP_OFF_AID 4
+#define WLAN_REASSOCRESP_OFF_SUPP_RATES 6
+
+#define WLAN_PROBEREQ_OFF_SSID 0
+
+#define WLAN_PROBERESP_OFF_TS 0
+#define WLAN_PROBERESP_OFF_BCN_int 8
+#define WLAN_PROBERESP_OFF_CAP_INFO 10
+#define WLAN_PROBERESP_OFF_SSID 12
+
+#define WLAN_AUTHEN_OFF_AUTH_ALG 0
+#define WLAN_AUTHEN_OFF_AUTH_SEQ 2
+#define WLAN_AUTHEN_OFF_STATUS 4
+#define WLAN_AUTHEN_OFF_CHALLENGE 6
+
+#define WLAN_DEAUTHEN_OFF_REASON 0
+
+/*-- Capability Field ---------------------------*/
+#define WLAN_GET_MGMT_CAP_INFO_ESS(n) ((n) & BIT(0))
+#define WLAN_GET_MGMT_CAP_INFO_IBSS(n) (((n) & BIT(1)) >> 1)
+#define WLAN_GET_MGMT_CAP_INFO_CFPOLLABLE(n) (((n) & BIT(2)) >> 2)
+#define WLAN_GET_MGMT_CAP_INFO_CFPOLLREQ(n) (((n) & BIT(3)) >> 3)
+#define WLAN_GET_MGMT_CAP_INFO_PRIVACY(n) (((n) & BIT(4)) >> 4)
+ /* p80211b additions */
+#define WLAN_GET_MGMT_CAP_INFO_SHORT(n) (((n) & BIT(5)) >> 5)
+#define WLAN_GET_MGMT_CAP_INFO_PBCC(n) (((n) & BIT(6)) >> 6)
+#define WLAN_GET_MGMT_CAP_INFO_AGILITY(n) (((n) & BIT(7)) >> 7)
+
+#define WLAN_SET_MGMT_CAP_INFO_ESS(n) (n)
+#define WLAN_SET_MGMT_CAP_INFO_IBSS(n) ((n) << 1)
+#define WLAN_SET_MGMT_CAP_INFO_CFPOLLABLE(n) ((n) << 2)
+#define WLAN_SET_MGMT_CAP_INFO_CFPOLLREQ(n) ((n) << 3)
+#define WLAN_SET_MGMT_CAP_INFO_PRIVACY(n) ((n) << 4)
+ /* p80211b additions */
+#define WLAN_SET_MGMT_CAP_INFO_SHORT(n) ((n) << 5)
+#define WLAN_SET_MGMT_CAP_INFO_PBCC(n) ((n) << 6)
+#define WLAN_SET_MGMT_CAP_INFO_AGILITY(n) ((n) << 7)
+
+/*-- Information Element Types --------------------*/
+/* prototype structure, all IEs start with these members */
+
+struct wlan_ie {
+ u8 eid;
+ u8 len;
+} __packed;
+
+/*-- Service Set Identity (SSID) -----------------*/
+struct wlan_ie_ssid {
+ u8 eid;
+ u8 len;
+ u8 ssid[1]; /* may be zero, ptrs may overlap */
+} __packed;
+
+/*-- Supported Rates -----------------------------*/
+struct wlan_ie_supp_rates {
+ u8 eid;
+ u8 len;
+ u8 rates[1]; /* had better be at LEAST one! */
+} __packed;
+
+/*-- FH Parameter Set ----------------------------*/
+struct wlan_ie_fh_parms {
+ u8 eid;
+ u8 len;
+ u16 dwell;
+ u8 hopset;
+ u8 hoppattern;
+ u8 hopindex;
+} __packed;
+
+/*-- DS Parameter Set ----------------------------*/
+struct wlan_ie_ds_parms {
+ u8 eid;
+ u8 len;
+ u8 curr_ch;
+} __packed;
+
+/*-- CF Parameter Set ----------------------------*/
+
+struct wlan_ie_cf_parms {
+ u8 eid;
+ u8 len;
+ u8 cfp_cnt;
+ u8 cfp_period;
+ u16 cfp_maxdur;
+ u16 cfp_durremaining;
+} __packed;
+
+/*-- TIM ------------------------------------------*/
+struct wlan_ie_tim {
+ u8 eid;
+ u8 len;
+ u8 dtim_cnt;
+ u8 dtim_period;
+ u8 bitmap_ctl;
+ u8 virt_bm[1];
+} __packed;
+
+/*-- IBSS Parameter Set ---------------------------*/
+struct wlan_ie_ibss_parms {
+ u8 eid;
+ u8 len;
+ u16 atim_win;
+} __packed;
+
+/*-- Challenge Text ------------------------------*/
+struct wlan_ie_challenge {
+ u8 eid;
+ u8 len;
+ u8 challenge[1];
+} __packed;
+
+/*-------------------------------------------------*/
+/* Frame Types */
+
+/* prototype structure, all mgmt frame types will start with these members */
+struct wlan_fr_mgmt {
+ u16 type;
+ u16 len; /* DOES NOT include CRC !!!! */
+ u8 *buf;
+ union p80211_hdr *hdr;
+ /* used for target specific data, skb in Linux */
+ void *priv;
+ /*-- fixed fields -----------*/
+ /*-- info elements ----------*/
+};
+
+/*-- Beacon ---------------------------------------*/
+struct wlan_fr_beacon {
+ u16 type;
+ u16 len;
+ u8 *buf;
+ union p80211_hdr *hdr;
+ /* used for target specific data, skb in Linux */
+ void *priv;
+ /*-- fixed fields -----------*/
+ u64 *ts;
+ u16 *bcn_int;
+ u16 *cap_info;
+ /*-- info elements ----------*/
+ struct wlan_ie_ssid *ssid;
+ struct wlan_ie_supp_rates *supp_rates;
+ struct wlan_ie_fh_parms *fh_parms;
+ struct wlan_ie_ds_parms *ds_parms;
+ struct wlan_ie_cf_parms *cf_parms;
+ struct wlan_ie_ibss_parms *ibss_parms;
+ struct wlan_ie_tim *tim;
+
+};
+
+/*-- IBSS ATIM ------------------------------------*/
+struct wlan_fr_ibssatim {
+ u16 type;
+ u16 len;
+ u8 *buf;
+ union p80211_hdr *hdr;
+ /* used for target specific data, skb in Linux */
+ void *priv;
+
+ /*-- fixed fields -----------*/
+ /*-- info elements ----------*/
+
+ /* this frame type has a null body */
+
+};
+
+/*-- Disassociation -------------------------------*/
+struct wlan_fr_disassoc {
+ u16 type;
+ u16 len;
+ u8 *buf;
+ union p80211_hdr *hdr;
+ /* used for target specific data, skb in Linux */
+ void *priv;
+ /*-- fixed fields -----------*/
+ u16 *reason;
+
+ /*-- info elements ----------*/
+
+};
+
+/*-- Association Request --------------------------*/
+struct wlan_fr_assocreq {
+ u16 type;
+ u16 len;
+ u8 *buf;
+ union p80211_hdr *hdr;
+ /* used for target specific data, skb in Linux */
+ void *priv;
+ /*-- fixed fields -----------*/
+ u16 *cap_info;
+ u16 *listen_int;
+ /*-- info elements ----------*/
+ struct wlan_ie_ssid *ssid;
+ struct wlan_ie_supp_rates *supp_rates;
+
+};
+
+/*-- Association Response -------------------------*/
+struct wlan_fr_assocresp {
+ u16 type;
+ u16 len;
+ u8 *buf;
+ union p80211_hdr *hdr;
+ /* used for target specific data, skb in Linux */
+ void *priv;
+ /*-- fixed fields -----------*/
+ u16 *cap_info;
+ u16 *status;
+ u16 *aid;
+ /*-- info elements ----------*/
+ struct wlan_ie_supp_rates *supp_rates;
+
+};
+
+/*-- Reassociation Request ------------------------*/
+struct wlan_fr_reassocreq {
+ u16 type;
+ u16 len;
+ u8 *buf;
+ union p80211_hdr *hdr;
+ /* used for target specific data, skb in Linux */
+ void *priv;
+ /*-- fixed fields -----------*/
+ u16 *cap_info;
+ u16 *listen_int;
+ u8 *curr_ap;
+ /*-- info elements ----------*/
+ struct wlan_ie_ssid *ssid;
+ struct wlan_ie_supp_rates *supp_rates;
+
+};
+
+/*-- Reassociation Response -----------------------*/
+struct wlan_fr_reassocresp {
+ u16 type;
+ u16 len;
+ u8 *buf;
+ union p80211_hdr *hdr;
+ /* used for target specific data, skb in Linux */
+ void *priv;
+ /*-- fixed fields -----------*/
+ u16 *cap_info;
+ u16 *status;
+ u16 *aid;
+ /*-- info elements ----------*/
+ struct wlan_ie_supp_rates *supp_rates;
+
+};
+
+/*-- Probe Request --------------------------------*/
+struct wlan_fr_probereq {
+ u16 type;
+ u16 len;
+ u8 *buf;
+ union p80211_hdr *hdr;
+ /* used for target specific data, skb in Linux */
+ void *priv;
+ /*-- fixed fields -----------*/
+ /*-- info elements ----------*/
+ struct wlan_ie_ssid *ssid;
+ struct wlan_ie_supp_rates *supp_rates;
+
+};
+
+/*-- Probe Response -------------------------------*/
+struct wlan_fr_proberesp {
+ u16 type;
+ u16 len;
+ u8 *buf;
+ union p80211_hdr *hdr;
+ /* used for target specific data, skb in Linux */
+ void *priv;
+ /*-- fixed fields -----------*/
+ u64 *ts;
+ u16 *bcn_int;
+ u16 *cap_info;
+ /*-- info elements ----------*/
+ struct wlan_ie_ssid *ssid;
+ struct wlan_ie_supp_rates *supp_rates;
+ struct wlan_ie_fh_parms *fh_parms;
+ struct wlan_ie_ds_parms *ds_parms;
+ struct wlan_ie_cf_parms *cf_parms;
+ struct wlan_ie_ibss_parms *ibss_parms;
+};
+
+/*-- Authentication -------------------------------*/
+struct wlan_fr_authen {
+ u16 type;
+ u16 len;
+ u8 *buf;
+ union p80211_hdr *hdr;
+ /* used for target specific data, skb in Linux */
+ void *priv;
+ /*-- fixed fields -----------*/
+ u16 *auth_alg;
+ u16 *auth_seq;
+ u16 *status;
+ /*-- info elements ----------*/
+ struct wlan_ie_challenge *challenge;
+
+};
+
+/*-- Deauthenication -----------------------------*/
+struct wlan_fr_deauthen {
+ u16 type;
+ u16 len;
+ u8 *buf;
+ union p80211_hdr *hdr;
+ /* used for target specific data, skb in Linux */
+ void *priv;
+ /*-- fixed fields -----------*/
+ u16 *reason;
+
+ /*-- info elements ----------*/
+
+};
+
+void wlan_mgmt_encode_beacon(struct wlan_fr_beacon *f);
+void wlan_mgmt_decode_beacon(struct wlan_fr_beacon *f);
+void wlan_mgmt_encode_disassoc(struct wlan_fr_disassoc *f);
+void wlan_mgmt_decode_disassoc(struct wlan_fr_disassoc *f);
+void wlan_mgmt_encode_assocreq(struct wlan_fr_assocreq *f);
+void wlan_mgmt_decode_assocreq(struct wlan_fr_assocreq *f);
+void wlan_mgmt_encode_assocresp(struct wlan_fr_assocresp *f);
+void wlan_mgmt_decode_assocresp(struct wlan_fr_assocresp *f);
+void wlan_mgmt_encode_reassocreq(struct wlan_fr_reassocreq *f);
+void wlan_mgmt_decode_reassocreq(struct wlan_fr_reassocreq *f);
+void wlan_mgmt_encode_reassocresp(struct wlan_fr_reassocresp *f);
+void wlan_mgmt_decode_reassocresp(struct wlan_fr_reassocresp *f);
+void wlan_mgmt_encode_probereq(struct wlan_fr_probereq *f);
+void wlan_mgmt_decode_probereq(struct wlan_fr_probereq *f);
+void wlan_mgmt_encode_proberesp(struct wlan_fr_proberesp *f);
+void wlan_mgmt_decode_proberesp(struct wlan_fr_proberesp *f);
+void wlan_mgmt_encode_authen(struct wlan_fr_authen *f);
+void wlan_mgmt_decode_authen(struct wlan_fr_authen *f);
+void wlan_mgmt_encode_deauthen(struct wlan_fr_deauthen *f);
+void wlan_mgmt_decode_deauthen(struct wlan_fr_deauthen *f);
+
+#endif /* _P80211MGMT_H */
diff --git a/drivers/staging/wlan-ng/p80211msg.h b/drivers/staging/wlan-ng/p80211msg.h
new file mode 100644
index 000000000..43d2f971e
--- /dev/null
+++ b/drivers/staging/wlan-ng/p80211msg.h
@@ -0,0 +1,59 @@
+/* p80211msg.h
+*
+* Macros, constants, types, and funcs for req and ind messages
+*
+* Copyright (C) 1999 AbsoluteValue Systems, Inc. All Rights Reserved.
+* --------------------------------------------------------------------
+*
+* linux-wlan
+*
+* The contents of this file are subject to the Mozilla Public
+* License Version 1.1 (the "License"); you may not use this file
+* except in compliance with the License. You may obtain a copy of
+* the License at http://www.mozilla.org/MPL/
+*
+* Software distributed under the License is distributed on an "AS
+* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+* implied. See the License for the specific language governing
+* rights and limitations under the License.
+*
+* Alternatively, the contents of this file may be used under the
+* terms of the GNU Public License version 2 (the "GPL"), in which
+* case the provisions of the GPL are applicable instead of the
+* above. If you wish to allow the use of your version of this file
+* only under the terms of the GPL and not to allow others to use
+* your version of this file under the MPL, indicate your decision
+* by deleting the provisions above and replace them with the notice
+* and other provisions required by the GPL. If you do not delete
+* the provisions above, a recipient may use your version of this
+* file under either the MPL or the GPL.
+*
+* --------------------------------------------------------------------
+*
+* Inquiries regarding the linux-wlan Open Source project can be
+* made directly to:
+*
+* AbsoluteValue Systems Inc.
+* info@linux-wlan.com
+* http://www.linux-wlan.com
+*
+* --------------------------------------------------------------------
+*
+* Portions of the development of this software were funded by
+* Intersil Corporation as part of PRISM(R) chipset product development.
+*
+* --------------------------------------------------------------------
+*/
+
+#ifndef _P80211MSG_H
+#define _P80211MSG_H
+
+#define WLAN_DEVNAMELEN_MAX 16
+
+struct p80211msg {
+ u32 msgcode;
+ u32 msglen;
+ u8 devname[WLAN_DEVNAMELEN_MAX];
+} __packed;
+
+#endif /* _P80211MSG_H */
diff --git a/drivers/staging/wlan-ng/p80211netdev.c b/drivers/staging/wlan-ng/p80211netdev.c
new file mode 100644
index 000000000..a9c1e0baf
--- /dev/null
+++ b/drivers/staging/wlan-ng/p80211netdev.c
@@ -0,0 +1,1085 @@
+/* src/p80211/p80211knetdev.c
+*
+* Linux Kernel net device interface
+*
+* Copyright (C) 1999 AbsoluteValue Systems, Inc. All Rights Reserved.
+* --------------------------------------------------------------------
+*
+* linux-wlan
+*
+* The contents of this file are subject to the Mozilla Public
+* License Version 1.1 (the "License"); you may not use this file
+* except in compliance with the License. You may obtain a copy of
+* the License at http://www.mozilla.org/MPL/
+*
+* Software distributed under the License is distributed on an "AS
+* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+* implied. See the License for the specific language governing
+* rights and limitations under the License.
+*
+* Alternatively, the contents of this file may be used under the
+* terms of the GNU Public License version 2 (the "GPL"), in which
+* case the provisions of the GPL are applicable instead of the
+* above. If you wish to allow the use of your version of this file
+* only under the terms of the GPL and not to allow others to use
+* your version of this file under the MPL, indicate your decision
+* by deleting the provisions above and replace them with the notice
+* and other provisions required by the GPL. If you do not delete
+* the provisions above, a recipient may use your version of this
+* file under either the MPL or the GPL.
+*
+* --------------------------------------------------------------------
+*
+* Inquiries regarding the linux-wlan Open Source project can be
+* made directly to:
+*
+* AbsoluteValue Systems Inc.
+* info@linux-wlan.com
+* http://www.linux-wlan.com
+*
+* --------------------------------------------------------------------
+*
+* Portions of the development of this software were funded by
+* Intersil Corporation as part of PRISM(R) chipset product development.
+*
+* --------------------------------------------------------------------
+*
+* The functions required for a Linux network device are defined here.
+*
+* --------------------------------------------------------------------
+*/
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/types.h>
+#include <linux/skbuff.h>
+#include <linux/slab.h>
+#include <linux/proc_fs.h>
+#include <linux/interrupt.h>
+#include <linux/netdevice.h>
+#include <linux/kmod.h>
+#include <linux/if_arp.h>
+#include <linux/wireless.h>
+#include <linux/sockios.h>
+#include <linux/etherdevice.h>
+#include <linux/if_ether.h>
+#include <linux/byteorder/generic.h>
+#include <linux/bitops.h>
+#include <linux/uaccess.h>
+#include <asm/byteorder.h>
+
+#ifdef SIOCETHTOOL
+#include <linux/ethtool.h>
+#endif
+
+#include <net/iw_handler.h>
+#include <net/net_namespace.h>
+#include <net/cfg80211.h>
+
+#include "p80211types.h"
+#include "p80211hdr.h"
+#include "p80211conv.h"
+#include "p80211mgmt.h"
+#include "p80211msg.h"
+#include "p80211netdev.h"
+#include "p80211ioctl.h"
+#include "p80211req.h"
+#include "p80211metastruct.h"
+#include "p80211metadef.h"
+
+#include "cfg80211.c"
+
+/* netdevice method functions */
+static int p80211knetdev_init(netdevice_t *netdev);
+static int p80211knetdev_open(netdevice_t *netdev);
+static int p80211knetdev_stop(netdevice_t *netdev);
+static int p80211knetdev_hard_start_xmit(struct sk_buff *skb,
+ netdevice_t *netdev);
+static void p80211knetdev_set_multicast_list(netdevice_t *dev);
+static int p80211knetdev_do_ioctl(netdevice_t *dev, struct ifreq *ifr,
+ int cmd);
+static int p80211knetdev_set_mac_address(netdevice_t *dev, void *addr);
+static void p80211knetdev_tx_timeout(netdevice_t *netdev);
+static int p80211_rx_typedrop(wlandevice_t *wlandev, u16 fc);
+
+int wlan_watchdog = 5000;
+module_param(wlan_watchdog, int, 0644);
+MODULE_PARM_DESC(wlan_watchdog, "transmit timeout in milliseconds");
+
+int wlan_wext_write = 1;
+module_param(wlan_wext_write, int, 0644);
+MODULE_PARM_DESC(wlan_wext_write, "enable write wireless extensions");
+
+/*----------------------------------------------------------------
+* p80211knetdev_init
+*
+* Init method for a Linux netdevice. Called in response to
+* register_netdev.
+*
+* Arguments:
+* none
+*
+* Returns:
+* nothing
+----------------------------------------------------------------*/
+static int p80211knetdev_init(netdevice_t *netdev)
+{
+ /* Called in response to register_netdev */
+ /* This is usually the probe function, but the probe has */
+ /* already been done by the MSD and the create_kdev */
+ /* function. All we do here is return success */
+ return 0;
+}
+
+/*----------------------------------------------------------------
+* p80211knetdev_open
+*
+* Linux netdevice open method. Following a successful call here,
+* the device is supposed to be ready for tx and rx. In our
+* situation that may not be entirely true due to the state of the
+* MAC below.
+*
+* Arguments:
+* netdev Linux network device structure
+*
+* Returns:
+* zero on success, non-zero otherwise
+----------------------------------------------------------------*/
+static int p80211knetdev_open(netdevice_t *netdev)
+{
+ int result = 0; /* success */
+ wlandevice_t *wlandev = netdev->ml_priv;
+
+ /* Check to make sure the MSD is running */
+ if (wlandev->msdstate != WLAN_MSD_RUNNING)
+ return -ENODEV;
+
+ /* Tell the MSD to open */
+ if (wlandev->open != NULL) {
+ result = wlandev->open(wlandev);
+ if (result == 0) {
+ netif_start_queue(wlandev->netdev);
+ wlandev->state = WLAN_DEVICE_OPEN;
+ }
+ } else {
+ result = -EAGAIN;
+ }
+
+ return result;
+}
+
+/*----------------------------------------------------------------
+* p80211knetdev_stop
+*
+* Linux netdevice stop (close) method. Following this call,
+* no frames should go up or down through this interface.
+*
+* Arguments:
+* netdev Linux network device structure
+*
+* Returns:
+* zero on success, non-zero otherwise
+----------------------------------------------------------------*/
+static int p80211knetdev_stop(netdevice_t *netdev)
+{
+ int result = 0;
+ wlandevice_t *wlandev = netdev->ml_priv;
+
+ if (wlandev->close != NULL)
+ result = wlandev->close(wlandev);
+
+ netif_stop_queue(wlandev->netdev);
+ wlandev->state = WLAN_DEVICE_CLOSED;
+
+ return result;
+}
+
+/*----------------------------------------------------------------
+* p80211netdev_rx
+*
+* Frame receive function called by the mac specific driver.
+*
+* Arguments:
+* wlandev WLAN network device structure
+* skb skbuff containing a full 802.11 frame.
+* Returns:
+* nothing
+* Side effects:
+*
+----------------------------------------------------------------*/
+void p80211netdev_rx(wlandevice_t *wlandev, struct sk_buff *skb)
+{
+ /* Enqueue for post-irq processing */
+ skb_queue_tail(&wlandev->nsd_rxq, skb);
+ tasklet_schedule(&wlandev->rx_bh);
+}
+
+#define CONV_TO_ETHER_SKIPPED 0x01
+#define CONV_TO_ETHER_FAILED 0x02
+
+/**
+ * p80211_convert_to_ether - conversion from 802.11 frame to ethernet frame
+ * @wlandev: pointer to WLAN device
+ * @skb: pointer to socket buffer
+ *
+ * Returns: 0 if conversion succeeded
+ * CONV_TO_ETHER_FAILED if conversion failed
+ * CONV_TO_ETHER_SKIPPED if frame is ignored
+ */
+static int p80211_convert_to_ether(wlandevice_t *wlandev, struct sk_buff *skb)
+{
+ struct p80211_hdr_a3 *hdr;
+
+ hdr = (struct p80211_hdr_a3 *) skb->data;
+ if (p80211_rx_typedrop(wlandev, hdr->fc))
+ return CONV_TO_ETHER_SKIPPED;
+
+ /* perform mcast filtering: allow my local address through but reject
+ * anything else that isn't multicast
+ */
+ if (wlandev->netdev->flags & IFF_ALLMULTI) {
+ if (!ether_addr_equal_unaligned(wlandev->netdev->dev_addr,
+ hdr->a1)) {
+ if (!is_multicast_ether_addr(hdr->a1))
+ return CONV_TO_ETHER_SKIPPED;
+ }
+ }
+
+ if (skb_p80211_to_ether(wlandev, wlandev->ethconv, skb) == 0) {
+ skb->dev->last_rx = jiffies;
+ wlandev->netdev->stats.rx_packets++;
+ wlandev->netdev->stats.rx_bytes += skb->len;
+ netif_rx_ni(skb);
+ return 0;
+ }
+
+ netdev_dbg(wlandev->netdev, "p80211_convert_to_ether failed.\n");
+ return CONV_TO_ETHER_FAILED;
+}
+
+/**
+ * p80211netdev_rx_bh - deferred processing of all received frames
+ *
+ * @arg: pointer to WLAN network device structure (cast to unsigned long)
+ */
+static void p80211netdev_rx_bh(unsigned long arg)
+{
+ wlandevice_t *wlandev = (wlandevice_t *) arg;
+ struct sk_buff *skb = NULL;
+ netdevice_t *dev = wlandev->netdev;
+
+ /* Let's empty our our queue */
+ while ((skb = skb_dequeue(&wlandev->nsd_rxq))) {
+ if (wlandev->state == WLAN_DEVICE_OPEN) {
+
+ if (dev->type != ARPHRD_ETHER) {
+ /* RAW frame; we shouldn't convert it */
+ /* XXX Append the Prism Header here instead. */
+
+ /* set up various data fields */
+ skb->dev = dev;
+ skb_reset_mac_header(skb);
+ skb->ip_summed = CHECKSUM_NONE;
+ skb->pkt_type = PACKET_OTHERHOST;
+ skb->protocol = htons(ETH_P_80211_RAW);
+ dev->last_rx = jiffies;
+
+ dev->stats.rx_packets++;
+ dev->stats.rx_bytes += skb->len;
+ netif_rx_ni(skb);
+ continue;
+ } else {
+ if (!p80211_convert_to_ether(wlandev, skb))
+ continue;
+ }
+ }
+ dev_kfree_skb(skb);
+ }
+}
+
+/*----------------------------------------------------------------
+* p80211knetdev_hard_start_xmit
+*
+* Linux netdevice method for transmitting a frame.
+*
+* Arguments:
+* skb Linux sk_buff containing the frame.
+* netdev Linux netdevice.
+*
+* Side effects:
+* If the lower layers report that buffers are full. netdev->tbusy
+* will be set to prevent higher layers from sending more traffic.
+*
+* Note: If this function returns non-zero, higher layers retain
+* ownership of the skb.
+*
+* Returns:
+* zero on success, non-zero on failure.
+----------------------------------------------------------------*/
+static int p80211knetdev_hard_start_xmit(struct sk_buff *skb,
+ netdevice_t *netdev)
+{
+ int result = 0;
+ int txresult = -1;
+ wlandevice_t *wlandev = netdev->ml_priv;
+ union p80211_hdr p80211_hdr;
+ struct p80211_metawep p80211_wep;
+
+ p80211_wep.data = NULL;
+
+ if (skb == NULL)
+ return NETDEV_TX_OK;
+
+ if (wlandev->state != WLAN_DEVICE_OPEN) {
+ result = 1;
+ goto failed;
+ }
+
+ memset(&p80211_hdr, 0, sizeof(union p80211_hdr));
+ memset(&p80211_wep, 0, sizeof(struct p80211_metawep));
+
+ if (netif_queue_stopped(netdev)) {
+ netdev_dbg(netdev, "called when queue stopped.\n");
+ result = 1;
+ goto failed;
+ }
+
+ netif_stop_queue(netdev);
+
+ /* Check to see that a valid mode is set */
+ switch (wlandev->macmode) {
+ case WLAN_MACMODE_IBSS_STA:
+ case WLAN_MACMODE_ESS_STA:
+ case WLAN_MACMODE_ESS_AP:
+ break;
+ default:
+ /* Mode isn't set yet, just drop the frame
+ * and return success .
+ * TODO: we need a saner way to handle this
+ */
+ if (be16_to_cpu(skb->protocol) != ETH_P_80211_RAW) {
+ netif_start_queue(wlandev->netdev);
+ netdev_notice(netdev, "Tx attempt prior to association, frame dropped.\n");
+ netdev->stats.tx_dropped++;
+ result = 0;
+ goto failed;
+ }
+ break;
+ }
+
+ /* Check for raw transmits */
+ if (be16_to_cpu(skb->protocol) == ETH_P_80211_RAW) {
+ if (!capable(CAP_NET_ADMIN)) {
+ result = 1;
+ goto failed;
+ }
+ /* move the header over */
+ memcpy(&p80211_hdr, skb->data, sizeof(union p80211_hdr));
+ skb_pull(skb, sizeof(union p80211_hdr));
+ } else {
+ if (skb_ether_to_p80211
+ (wlandev, wlandev->ethconv, skb, &p80211_hdr,
+ &p80211_wep) != 0) {
+ /* convert failed */
+ netdev_dbg(netdev, "ether_to_80211(%d) failed.\n",
+ wlandev->ethconv);
+ result = 1;
+ goto failed;
+ }
+ }
+ if (wlandev->txframe == NULL) {
+ result = 1;
+ goto failed;
+ }
+
+ netdev->trans_start = jiffies;
+
+ netdev->stats.tx_packets++;
+ /* count only the packet payload */
+ netdev->stats.tx_bytes += skb->len;
+
+ txresult = wlandev->txframe(wlandev, skb, &p80211_hdr, &p80211_wep);
+
+ if (txresult == 0) {
+ /* success and more buf */
+ /* avail, re: hw_txdata */
+ netif_wake_queue(wlandev->netdev);
+ result = NETDEV_TX_OK;
+ } else if (txresult == 1) {
+ /* success, no more avail */
+ netdev_dbg(netdev, "txframe success, no more bufs\n");
+ /* netdev->tbusy = 1; don't set here, irqhdlr */
+ /* may have already cleared it */
+ result = NETDEV_TX_OK;
+ } else if (txresult == 2) {
+ /* alloc failure, drop frame */
+ netdev_dbg(netdev, "txframe returned alloc_fail\n");
+ result = NETDEV_TX_BUSY;
+ } else {
+ /* buffer full or queue busy, drop frame. */
+ netdev_dbg(netdev, "txframe returned full or busy\n");
+ result = NETDEV_TX_BUSY;
+ }
+
+failed:
+ /* Free up the WEP buffer if it's not the same as the skb */
+ if ((p80211_wep.data) && (p80211_wep.data != skb->data))
+ kzfree(p80211_wep.data);
+
+ /* we always free the skb here, never in a lower level. */
+ if (!result)
+ dev_kfree_skb(skb);
+
+ return result;
+}
+
+/*----------------------------------------------------------------
+* p80211knetdev_set_multicast_list
+*
+* Called from higher layers whenever there's a need to set/clear
+* promiscuous mode or rewrite the multicast list.
+*
+* Arguments:
+* none
+*
+* Returns:
+* nothing
+----------------------------------------------------------------*/
+static void p80211knetdev_set_multicast_list(netdevice_t *dev)
+{
+ wlandevice_t *wlandev = dev->ml_priv;
+
+ /* TODO: real multicast support as well */
+
+ if (wlandev->set_multicast_list)
+ wlandev->set_multicast_list(wlandev, dev);
+
+}
+
+#ifdef SIOCETHTOOL
+
+static int p80211netdev_ethtool(wlandevice_t *wlandev, void __user *useraddr)
+{
+ u32 ethcmd;
+ struct ethtool_drvinfo info;
+ struct ethtool_value edata;
+
+ memset(&info, 0, sizeof(info));
+ memset(&edata, 0, sizeof(edata));
+
+ if (copy_from_user(&ethcmd, useraddr, sizeof(ethcmd)))
+ return -EFAULT;
+
+ switch (ethcmd) {
+ case ETHTOOL_GDRVINFO:
+ info.cmd = ethcmd;
+ snprintf(info.driver, sizeof(info.driver), "p80211_%s",
+ wlandev->nsdname);
+ snprintf(info.version, sizeof(info.version), "%s",
+ WLAN_RELEASE);
+
+ if (copy_to_user(useraddr, &info, sizeof(info)))
+ return -EFAULT;
+ return 0;
+#ifdef ETHTOOL_GLINK
+ case ETHTOOL_GLINK:
+ edata.cmd = ethcmd;
+
+ if (wlandev->linkstatus &&
+ (wlandev->macmode != WLAN_MACMODE_NONE)) {
+ edata.data = 1;
+ } else {
+ edata.data = 0;
+ }
+
+ if (copy_to_user(useraddr, &edata, sizeof(edata)))
+ return -EFAULT;
+ return 0;
+#endif
+ }
+
+ return -EOPNOTSUPP;
+}
+
+#endif
+
+/*----------------------------------------------------------------
+* p80211knetdev_do_ioctl
+*
+* Handle an ioctl call on one of our devices. Everything Linux
+* ioctl specific is done here. Then we pass the contents of the
+* ifr->data to the request message handler.
+*
+* Arguments:
+* dev Linux kernel netdevice
+* ifr Our private ioctl request structure, typed for the
+* generic struct ifreq so we can use ptr to func
+* w/o cast.
+*
+* Returns:
+* zero on success, a negative errno on failure. Possible values:
+* -ENETDOWN Device isn't up.
+* -EBUSY cmd already in progress
+* -ETIME p80211 cmd timed out (MSD may have its own timers)
+* -EFAULT memory fault copying msg from user buffer
+* -ENOMEM unable to allocate kernel msg buffer
+* -ENOSYS bad magic, it the cmd really for us?
+* -EintR sleeping on cmd, awakened by signal, cmd cancelled.
+*
+* Call Context:
+* Process thread (ioctl caller). TODO: SMP support may require
+* locks.
+----------------------------------------------------------------*/
+static int p80211knetdev_do_ioctl(netdevice_t *dev, struct ifreq *ifr, int cmd)
+{
+ int result = 0;
+ struct p80211ioctl_req *req = (struct p80211ioctl_req *) ifr;
+ wlandevice_t *wlandev = dev->ml_priv;
+ u8 *msgbuf;
+
+ netdev_dbg(dev, "rx'd ioctl, cmd=%d, len=%d\n", cmd, req->len);
+
+#ifdef SIOCETHTOOL
+ if (cmd == SIOCETHTOOL) {
+ result =
+ p80211netdev_ethtool(wlandev, (void __user *)ifr->ifr_data);
+ goto bail;
+ }
+#endif
+
+ /* Test the magic, assume ifr is good if it's there */
+ if (req->magic != P80211_IOCTL_MAGIC) {
+ result = -ENOSYS;
+ goto bail;
+ }
+
+ if (cmd == P80211_IFTEST) {
+ result = 0;
+ goto bail;
+ } else if (cmd != P80211_IFREQ) {
+ result = -ENOSYS;
+ goto bail;
+ }
+
+ /* Allocate a buf of size req->len */
+ msgbuf = kmalloc(req->len, GFP_KERNEL);
+ if (msgbuf) {
+ if (copy_from_user(msgbuf, (void __user *)req->data, req->len))
+ result = -EFAULT;
+ else
+ result = p80211req_dorequest(wlandev, msgbuf);
+
+ if (result == 0) {
+ if (copy_to_user
+ ((void __user *)req->data, msgbuf, req->len)) {
+ result = -EFAULT;
+ }
+ }
+ kfree(msgbuf);
+ } else {
+ result = -ENOMEM;
+ }
+bail:
+ /* If allocate,copyfrom or copyto fails, return errno */
+ return result;
+}
+
+/*----------------------------------------------------------------
+* p80211knetdev_set_mac_address
+*
+* Handles the ioctl for changing the MACAddress of a netdevice
+*
+* references: linux/netdevice.h and drivers/net/net_init.c
+*
+* NOTE: [MSM] We only prevent address changes when the netdev is
+* up. We don't control anything based on dot11 state. If the
+* address is changed on a STA that's currently associated, you
+* will probably lose the ability to send and receive data frames.
+* Just be aware. Therefore, this should usually only be done
+* prior to scan/join/auth/assoc.
+*
+* Arguments:
+* dev netdevice struct
+* addr the new MACAddress (a struct)
+*
+* Returns:
+* zero on success, a negative errno on failure. Possible values:
+* -EBUSY device is bussy (cmd not possible)
+* -and errors returned by: p80211req_dorequest(..)
+*
+* by: Collin R. Mulliner <collin@mulliner.org>
+----------------------------------------------------------------*/
+static int p80211knetdev_set_mac_address(netdevice_t *dev, void *addr)
+{
+ struct sockaddr *new_addr = addr;
+ struct p80211msg_dot11req_mibset dot11req;
+ p80211item_unk392_t *mibattr;
+ p80211item_pstr6_t *macaddr;
+ p80211item_uint32_t *resultcode;
+ int result;
+
+ /* If we're running, we don't allow MAC address changes */
+ if (netif_running(dev))
+ return -EBUSY;
+
+ /* Set up some convenience pointers. */
+ mibattr = &dot11req.mibattribute;
+ macaddr = (p80211item_pstr6_t *) &mibattr->data;
+ resultcode = &dot11req.resultcode;
+
+ /* Set up a dot11req_mibset */
+ memset(&dot11req, 0, sizeof(struct p80211msg_dot11req_mibset));
+ dot11req.msgcode = DIDmsg_dot11req_mibset;
+ dot11req.msglen = sizeof(struct p80211msg_dot11req_mibset);
+ memcpy(dot11req.devname,
+ ((wlandevice_t *) dev->ml_priv)->name, WLAN_DEVNAMELEN_MAX - 1);
+
+ /* Set up the mibattribute argument */
+ mibattr->did = DIDmsg_dot11req_mibset_mibattribute;
+ mibattr->status = P80211ENUM_msgitem_status_data_ok;
+ mibattr->len = sizeof(mibattr->data);
+
+ macaddr->did = DIDmib_dot11mac_dot11OperationTable_dot11MACAddress;
+ macaddr->status = P80211ENUM_msgitem_status_data_ok;
+ macaddr->len = sizeof(macaddr->data);
+ macaddr->data.len = ETH_ALEN;
+ memcpy(&macaddr->data.data, new_addr->sa_data, ETH_ALEN);
+
+ /* Set up the resultcode argument */
+ resultcode->did = DIDmsg_dot11req_mibset_resultcode;
+ resultcode->status = P80211ENUM_msgitem_status_no_value;
+ resultcode->len = sizeof(resultcode->data);
+ resultcode->data = 0;
+
+ /* now fire the request */
+ result = p80211req_dorequest(dev->ml_priv, (u8 *) &dot11req);
+
+ /* If the request wasn't successful, report an error and don't
+ * change the netdev address
+ */
+ if (result != 0 || resultcode->data != P80211ENUM_resultcode_success) {
+ netdev_err(dev, "Low-level driver failed dot11req_mibset(dot11MACAddress).\n");
+ result = -EADDRNOTAVAIL;
+ } else {
+ /* everything's ok, change the addr in netdev */
+ memcpy(dev->dev_addr, new_addr->sa_data, dev->addr_len);
+ }
+
+ return result;
+}
+
+static int wlan_change_mtu(netdevice_t *dev, int new_mtu)
+{
+ /* 2312 is max 802.11 payload, 20 is overhead, (ether + llc +snap)
+ and another 8 for wep. */
+ if ((new_mtu < 68) || (new_mtu > (2312 - 20 - 8)))
+ return -EINVAL;
+
+ dev->mtu = new_mtu;
+
+ return 0;
+}
+
+static const struct net_device_ops p80211_netdev_ops = {
+ .ndo_init = p80211knetdev_init,
+ .ndo_open = p80211knetdev_open,
+ .ndo_stop = p80211knetdev_stop,
+ .ndo_start_xmit = p80211knetdev_hard_start_xmit,
+ .ndo_set_rx_mode = p80211knetdev_set_multicast_list,
+ .ndo_do_ioctl = p80211knetdev_do_ioctl,
+ .ndo_set_mac_address = p80211knetdev_set_mac_address,
+ .ndo_tx_timeout = p80211knetdev_tx_timeout,
+ .ndo_change_mtu = wlan_change_mtu,
+ .ndo_validate_addr = eth_validate_addr,
+};
+
+/*----------------------------------------------------------------
+* wlan_setup
+*
+* Roughly matches the functionality of ether_setup. Here
+* we set up any members of the wlandevice structure that are common
+* to all devices. Additionally, we allocate a linux 'struct device'
+* and perform the same setup as ether_setup.
+*
+* Note: It's important that the caller have setup the wlandev->name
+* ptr prior to calling this function.
+*
+* Arguments:
+* wlandev ptr to the wlandev structure for the
+* interface.
+* physdev ptr to usb device
+* Returns:
+* zero on success, non-zero otherwise.
+* Call Context:
+* Should be process thread. We'll assume it might be
+* interrupt though. When we add support for statically
+* compiled drivers, this function will be called in the
+* context of the kernel startup code.
+----------------------------------------------------------------*/
+int wlan_setup(wlandevice_t *wlandev, struct device *physdev)
+{
+ int result = 0;
+ netdevice_t *netdev;
+ struct wiphy *wiphy;
+ struct wireless_dev *wdev;
+
+ /* Set up the wlandev */
+ wlandev->state = WLAN_DEVICE_CLOSED;
+ wlandev->ethconv = WLAN_ETHCONV_8021h;
+ wlandev->macmode = WLAN_MACMODE_NONE;
+
+ /* Set up the rx queue */
+ skb_queue_head_init(&wlandev->nsd_rxq);
+ tasklet_init(&wlandev->rx_bh,
+ p80211netdev_rx_bh, (unsigned long)wlandev);
+
+ /* Allocate and initialize the wiphy struct */
+ wiphy = wlan_create_wiphy(physdev, wlandev);
+ if (wiphy == NULL) {
+ dev_err(physdev, "Failed to alloc wiphy.\n");
+ return 1;
+ }
+
+ /* Allocate and initialize the struct device */
+ netdev = alloc_netdev(sizeof(struct wireless_dev), "wlan%d",
+ NET_NAME_UNKNOWN, ether_setup);
+ if (netdev == NULL) {
+ dev_err(physdev, "Failed to alloc netdev.\n");
+ wlan_free_wiphy(wiphy);
+ result = 1;
+ } else {
+ wlandev->netdev = netdev;
+ netdev->ml_priv = wlandev;
+ netdev->netdev_ops = &p80211_netdev_ops;
+ wdev = netdev_priv(netdev);
+ wdev->wiphy = wiphy;
+ wdev->iftype = NL80211_IFTYPE_STATION;
+ netdev->ieee80211_ptr = wdev;
+
+ netif_stop_queue(netdev);
+ netif_carrier_off(netdev);
+ }
+
+ return result;
+}
+
+/*----------------------------------------------------------------
+* wlan_unsetup
+*
+* This function is paired with the wlan_setup routine. It should
+* be called after unregister_wlandev. Basically, all it does is
+* free the 'struct device' that's associated with the wlandev.
+* We do it here because the 'struct device' isn't allocated
+* explicitly in the driver code, it's done in wlan_setup. To
+* do the free in the driver might seem like 'magic'.
+*
+* Arguments:
+* wlandev ptr to the wlandev structure for the
+* interface.
+* Call Context:
+* Should be process thread. We'll assume it might be
+* interrupt though. When we add support for statically
+* compiled drivers, this function will be called in the
+* context of the kernel startup code.
+----------------------------------------------------------------*/
+void wlan_unsetup(wlandevice_t *wlandev)
+{
+ struct wireless_dev *wdev;
+
+ tasklet_kill(&wlandev->rx_bh);
+
+ if (wlandev->netdev) {
+ wdev = netdev_priv(wlandev->netdev);
+ if (wdev->wiphy)
+ wlan_free_wiphy(wdev->wiphy);
+ free_netdev(wlandev->netdev);
+ wlandev->netdev = NULL;
+ }
+}
+
+/*----------------------------------------------------------------
+* register_wlandev
+*
+* Roughly matches the functionality of register_netdev. This function
+* is called after the driver has successfully probed and set up the
+* resources for the device. It's now ready to become a named device
+* in the Linux system.
+*
+* First we allocate a name for the device (if not already set), then
+* we call the Linux function register_netdevice.
+*
+* Arguments:
+* wlandev ptr to the wlandev structure for the
+* interface.
+* Returns:
+* zero on success, non-zero otherwise.
+* Call Context:
+* Can be either interrupt or not.
+----------------------------------------------------------------*/
+int register_wlandev(wlandevice_t *wlandev)
+{
+ return register_netdev(wlandev->netdev);
+}
+
+/*----------------------------------------------------------------
+* unregister_wlandev
+*
+* Roughly matches the functionality of unregister_netdev. This
+* function is called to remove a named device from the system.
+*
+* First we tell linux that the device should no longer exist.
+* Then we remove it from the list of known wlan devices.
+*
+* Arguments:
+* wlandev ptr to the wlandev structure for the
+* interface.
+* Returns:
+* zero on success, non-zero otherwise.
+* Call Context:
+* Can be either interrupt or not.
+----------------------------------------------------------------*/
+int unregister_wlandev(wlandevice_t *wlandev)
+{
+ struct sk_buff *skb;
+
+ unregister_netdev(wlandev->netdev);
+
+ /* Now to clean out the rx queue */
+ while ((skb = skb_dequeue(&wlandev->nsd_rxq)))
+ dev_kfree_skb(skb);
+
+ return 0;
+}
+
+/*----------------------------------------------------------------
+* p80211netdev_hwremoved
+*
+* Hardware removed notification. This function should be called
+* immediately after an MSD has detected that the underlying hardware
+* has been yanked out from under us. The primary things we need
+* to do are:
+* - Mark the wlandev
+* - Prevent any further traffic from the knetdev i/f
+* - Prevent any further requests from mgmt i/f
+* - If there are any waitq'd mgmt requests or mgmt-frame exchanges,
+* shut them down.
+* - Call the MSD hwremoved function.
+*
+* The remainder of the cleanup will be handled by unregister().
+* Our primary goal here is to prevent as much tickling of the MSD
+* as possible since the MSD is already in a 'wounded' state.
+*
+* TODO: As new features are added, this function should be
+* updated.
+*
+* Arguments:
+* wlandev WLAN network device structure
+* Returns:
+* nothing
+* Side effects:
+*
+* Call context:
+* Usually interrupt.
+----------------------------------------------------------------*/
+void p80211netdev_hwremoved(wlandevice_t *wlandev)
+{
+ wlandev->hwremoved = 1;
+ if (wlandev->state == WLAN_DEVICE_OPEN)
+ netif_stop_queue(wlandev->netdev);
+
+ netif_device_detach(wlandev->netdev);
+}
+
+/*----------------------------------------------------------------
+* p80211_rx_typedrop
+*
+* Classifies the frame, increments the appropriate counter, and
+* returns 0|1|2 indicating whether the driver should handle, ignore, or
+* drop the frame
+*
+* Arguments:
+* wlandev wlan device structure
+* fc frame control field
+*
+* Returns:
+* zero if the frame should be handled by the driver,
+* one if the frame should be ignored
+* anything else means we drop it.
+*
+* Side effects:
+*
+* Call context:
+* interrupt
+----------------------------------------------------------------*/
+static int p80211_rx_typedrop(wlandevice_t *wlandev, u16 fc)
+{
+ u16 ftype;
+ u16 fstype;
+ int drop = 0;
+ /* Classify frame, increment counter */
+ ftype = WLAN_GET_FC_FTYPE(fc);
+ fstype = WLAN_GET_FC_FSTYPE(fc);
+#if 0
+ netdev_dbg(wlandev->netdev, "rx_typedrop : ftype=%d fstype=%d.\n",
+ ftype, fstype);
+#endif
+ switch (ftype) {
+ case WLAN_FTYPE_MGMT:
+ if ((wlandev->netdev->flags & IFF_PROMISC) ||
+ (wlandev->netdev->flags & IFF_ALLMULTI)) {
+ drop = 1;
+ break;
+ }
+ netdev_dbg(wlandev->netdev, "rx'd mgmt:\n");
+ wlandev->rx.mgmt++;
+ switch (fstype) {
+ case WLAN_FSTYPE_ASSOCREQ:
+ /* printk("assocreq"); */
+ wlandev->rx.assocreq++;
+ break;
+ case WLAN_FSTYPE_ASSOCRESP:
+ /* printk("assocresp"); */
+ wlandev->rx.assocresp++;
+ break;
+ case WLAN_FSTYPE_REASSOCREQ:
+ /* printk("reassocreq"); */
+ wlandev->rx.reassocreq++;
+ break;
+ case WLAN_FSTYPE_REASSOCRESP:
+ /* printk("reassocresp"); */
+ wlandev->rx.reassocresp++;
+ break;
+ case WLAN_FSTYPE_PROBEREQ:
+ /* printk("probereq"); */
+ wlandev->rx.probereq++;
+ break;
+ case WLAN_FSTYPE_PROBERESP:
+ /* printk("proberesp"); */
+ wlandev->rx.proberesp++;
+ break;
+ case WLAN_FSTYPE_BEACON:
+ /* printk("beacon"); */
+ wlandev->rx.beacon++;
+ break;
+ case WLAN_FSTYPE_ATIM:
+ /* printk("atim"); */
+ wlandev->rx.atim++;
+ break;
+ case WLAN_FSTYPE_DISASSOC:
+ /* printk("disassoc"); */
+ wlandev->rx.disassoc++;
+ break;
+ case WLAN_FSTYPE_AUTHEN:
+ /* printk("authen"); */
+ wlandev->rx.authen++;
+ break;
+ case WLAN_FSTYPE_DEAUTHEN:
+ /* printk("deauthen"); */
+ wlandev->rx.deauthen++;
+ break;
+ default:
+ /* printk("unknown"); */
+ wlandev->rx.mgmt_unknown++;
+ break;
+ }
+ /* printk("\n"); */
+ drop = 2;
+ break;
+
+ case WLAN_FTYPE_CTL:
+ if ((wlandev->netdev->flags & IFF_PROMISC) ||
+ (wlandev->netdev->flags & IFF_ALLMULTI)) {
+ drop = 1;
+ break;
+ }
+ netdev_dbg(wlandev->netdev, "rx'd ctl:\n");
+ wlandev->rx.ctl++;
+ switch (fstype) {
+ case WLAN_FSTYPE_PSPOLL:
+ /* printk("pspoll"); */
+ wlandev->rx.pspoll++;
+ break;
+ case WLAN_FSTYPE_RTS:
+ /* printk("rts"); */
+ wlandev->rx.rts++;
+ break;
+ case WLAN_FSTYPE_CTS:
+ /* printk("cts"); */
+ wlandev->rx.cts++;
+ break;
+ case WLAN_FSTYPE_ACK:
+ /* printk("ack"); */
+ wlandev->rx.ack++;
+ break;
+ case WLAN_FSTYPE_CFEND:
+ /* printk("cfend"); */
+ wlandev->rx.cfend++;
+ break;
+ case WLAN_FSTYPE_CFENDCFACK:
+ /* printk("cfendcfack"); */
+ wlandev->rx.cfendcfack++;
+ break;
+ default:
+ /* printk("unknown"); */
+ wlandev->rx.ctl_unknown++;
+ break;
+ }
+ /* printk("\n"); */
+ drop = 2;
+ break;
+
+ case WLAN_FTYPE_DATA:
+ wlandev->rx.data++;
+ switch (fstype) {
+ case WLAN_FSTYPE_DATAONLY:
+ wlandev->rx.dataonly++;
+ break;
+ case WLAN_FSTYPE_DATA_CFACK:
+ wlandev->rx.data_cfack++;
+ break;
+ case WLAN_FSTYPE_DATA_CFPOLL:
+ wlandev->rx.data_cfpoll++;
+ break;
+ case WLAN_FSTYPE_DATA_CFACK_CFPOLL:
+ wlandev->rx.data__cfack_cfpoll++;
+ break;
+ case WLAN_FSTYPE_NULL:
+ netdev_dbg(wlandev->netdev, "rx'd data:null\n");
+ wlandev->rx.null++;
+ break;
+ case WLAN_FSTYPE_CFACK:
+ netdev_dbg(wlandev->netdev, "rx'd data:cfack\n");
+ wlandev->rx.cfack++;
+ break;
+ case WLAN_FSTYPE_CFPOLL:
+ netdev_dbg(wlandev->netdev, "rx'd data:cfpoll\n");
+ wlandev->rx.cfpoll++;
+ break;
+ case WLAN_FSTYPE_CFACK_CFPOLL:
+ netdev_dbg(wlandev->netdev, "rx'd data:cfack_cfpoll\n");
+ wlandev->rx.cfack_cfpoll++;
+ break;
+ default:
+ /* printk("unknown"); */
+ wlandev->rx.data_unknown++;
+ break;
+ }
+
+ break;
+ }
+ return drop;
+}
+
+static void p80211knetdev_tx_timeout(netdevice_t *netdev)
+{
+ wlandevice_t *wlandev = netdev->ml_priv;
+
+ if (wlandev->tx_timeout) {
+ wlandev->tx_timeout(wlandev);
+ } else {
+ netdev_warn(netdev, "Implement tx_timeout for %s\n",
+ wlandev->nsdname);
+ netif_wake_queue(wlandev->netdev);
+ }
+}
diff --git a/drivers/staging/wlan-ng/p80211netdev.h b/drivers/staging/wlan-ng/p80211netdev.h
new file mode 100644
index 000000000..c547e1cb4
--- /dev/null
+++ b/drivers/staging/wlan-ng/p80211netdev.h
@@ -0,0 +1,242 @@
+/* p80211netdev.h
+*
+* WLAN net device structure and functions
+*
+* Copyright (C) 1999 AbsoluteValue Systems, Inc. All Rights Reserved.
+* --------------------------------------------------------------------
+*
+* linux-wlan
+*
+* The contents of this file are subject to the Mozilla Public
+* License Version 1.1 (the "License"); you may not use this file
+* except in compliance with the License. You may obtain a copy of
+* the License at http://www.mozilla.org/MPL/
+*
+* Software distributed under the License is distributed on an "AS
+* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+* implied. See the License for the specific language governing
+* rights and limitations under the License.
+*
+* Alternatively, the contents of this file may be used under the
+* terms of the GNU Public License version 2 (the "GPL"), in which
+* case the provisions of the GPL are applicable instead of the
+* above. If you wish to allow the use of your version of this file
+* only under the terms of the GPL and not to allow others to use
+* your version of this file under the MPL, indicate your decision
+* by deleting the provisions above and replace them with the notice
+* and other provisions required by the GPL. If you do not delete
+* the provisions above, a recipient may use your version of this
+* file under either the MPL or the GPL.
+*
+* --------------------------------------------------------------------
+*
+* Inquiries regarding the linux-wlan Open Source project can be
+* made directly to:
+*
+* AbsoluteValue Systems Inc.
+* info@linux-wlan.com
+* http://www.linux-wlan.com
+*
+* --------------------------------------------------------------------
+*
+* Portions of the development of this software were funded by
+* Intersil Corporation as part of PRISM(R) chipset product development.
+*
+* --------------------------------------------------------------------
+*
+* This file declares the structure type that represents each wlan
+* interface.
+*
+* --------------------------------------------------------------------
+*/
+
+#ifndef _LINUX_P80211NETDEV_H
+#define _LINUX_P80211NETDEV_H
+
+#include <linux/interrupt.h>
+#include <linux/wireless.h>
+#include <linux/netdevice.h>
+
+#undef netdevice_t
+typedef struct net_device netdevice_t;
+
+#define WLAN_RELEASE "0.3.0-staging"
+
+#define WLAN_DEVICE_CLOSED 0
+#define WLAN_DEVICE_OPEN 1
+
+#define WLAN_MACMODE_NONE 0
+#define WLAN_MACMODE_IBSS_STA 1
+#define WLAN_MACMODE_ESS_STA 2
+#define WLAN_MACMODE_ESS_AP 3
+
+/* MSD States */
+#define WLAN_MSD_HWPRESENT_PENDING 1
+#define WLAN_MSD_HWFAIL 2
+#define WLAN_MSD_HWPRESENT 3
+#define WLAN_MSD_FWLOAD_PENDING 4
+#define WLAN_MSD_FWLOAD 5
+#define WLAN_MSD_RUNNING_PENDING 6
+#define WLAN_MSD_RUNNING 7
+
+#ifndef ETH_P_ECONET
+#define ETH_P_ECONET 0x0018 /* needed for 2.2.x kernels */
+#endif
+
+#define ETH_P_80211_RAW (ETH_P_ECONET + 1)
+
+#ifndef ARPHRD_IEEE80211
+#define ARPHRD_IEEE80211 801 /* kernel 2.4.6 */
+#endif
+
+#ifndef ARPHRD_IEEE80211_PRISM /* kernel 2.4.18 */
+#define ARPHRD_IEEE80211_PRISM 802
+#endif
+
+/*--- NSD Capabilities Flags ------------------------------*/
+#define P80211_NSDCAP_HARDWAREWEP 0x01 /* hardware wep engine */
+#define P80211_NSDCAP_SHORT_PREAMBLE 0x10 /* hardware supports */
+#define P80211_NSDCAP_HWFRAGMENT 0x80 /* nsd handles frag/defrag */
+#define P80211_NSDCAP_AUTOJOIN 0x100 /* nsd does autojoin */
+#define P80211_NSDCAP_NOSCAN 0x200 /* nsd can scan */
+
+/* Received frame statistics */
+typedef struct p80211_frmrx_t {
+ u32 mgmt;
+ u32 assocreq;
+ u32 assocresp;
+ u32 reassocreq;
+ u32 reassocresp;
+ u32 probereq;
+ u32 proberesp;
+ u32 beacon;
+ u32 atim;
+ u32 disassoc;
+ u32 authen;
+ u32 deauthen;
+ u32 mgmt_unknown;
+ u32 ctl;
+ u32 pspoll;
+ u32 rts;
+ u32 cts;
+ u32 ack;
+ u32 cfend;
+ u32 cfendcfack;
+ u32 ctl_unknown;
+ u32 data;
+ u32 dataonly;
+ u32 data_cfack;
+ u32 data_cfpoll;
+ u32 data__cfack_cfpoll;
+ u32 null;
+ u32 cfack;
+ u32 cfpoll;
+ u32 cfack_cfpoll;
+ u32 data_unknown;
+ u32 decrypt;
+ u32 decrypt_err;
+} p80211_frmrx_t;
+
+/* called by /proc/net/wireless */
+struct iw_statistics *p80211wext_get_wireless_stats(netdevice_t *dev);
+/* wireless extensions' ioctls */
+extern struct iw_handler_def p80211wext_handler_def;
+int p80211wext_event_associated(struct wlandevice *wlandev, int assoc);
+
+/* WEP stuff */
+#define NUM_WEPKEYS 4
+#define MAX_KEYLEN 32
+
+#define HOSTWEP_DEFAULTKEY_MASK (BIT(1)|BIT(0))
+#define HOSTWEP_SHAREDKEY BIT(3)
+#define HOSTWEP_DECRYPT BIT(4)
+#define HOSTWEP_ENCRYPT BIT(5)
+#define HOSTWEP_PRIVACYINVOKED BIT(6)
+#define HOSTWEP_EXCLUDEUNENCRYPTED BIT(7)
+
+extern int wlan_watchdog;
+extern int wlan_wext_write;
+
+/* WLAN device type */
+typedef struct wlandevice {
+ struct wlandevice *next; /* link for list of devices */
+ void *priv; /* private data for MSD */
+
+ /* Subsystem State */
+ char name[WLAN_DEVNAMELEN_MAX]; /* Dev name, from register_wlandev() */
+ char *nsdname;
+
+ u32 state; /* Device I/F state (open/closed) */
+ u32 msdstate; /* state of underlying driver */
+ u32 hwremoved; /* Has the hw been yanked out? */
+
+ /* Hardware config */
+ unsigned int irq;
+ unsigned int iobase;
+ unsigned int membase;
+ u32 nsdcaps; /* NSD Capabilities flags */
+
+ /* Config vars */
+ unsigned int ethconv;
+
+ /* device methods (init by MSD, used by p80211 */
+ int (*open)(struct wlandevice *wlandev);
+ int (*close)(struct wlandevice *wlandev);
+ void (*reset)(struct wlandevice *wlandev);
+ int (*txframe)(struct wlandevice *wlandev, struct sk_buff *skb,
+ union p80211_hdr *p80211_hdr,
+ struct p80211_metawep *p80211_wep);
+ int (*mlmerequest)(struct wlandevice *wlandev, struct p80211msg *msg);
+ int (*set_multicast_list)(struct wlandevice *wlandev,
+ netdevice_t *dev);
+ void (*tx_timeout)(struct wlandevice *wlandev);
+
+ /* 802.11 State */
+ u8 bssid[WLAN_BSSID_LEN];
+ p80211pstr32_t ssid;
+ u32 macmode;
+ int linkstatus;
+
+ /* WEP State */
+ u8 wep_keys[NUM_WEPKEYS][MAX_KEYLEN];
+ u8 wep_keylens[NUM_WEPKEYS];
+ int hostwep;
+
+ /* Request/Confirm i/f state (used by p80211) */
+ unsigned long request_pending; /* flag, access atomically */
+
+ /* netlink socket */
+ /* queue for indications waiting for cmd completion */
+ /* Linux netdevice and support */
+ netdevice_t *netdev; /* ptr to linux netdevice */
+
+ /* Rx bottom half */
+ struct tasklet_struct rx_bh;
+
+ struct sk_buff_head nsd_rxq;
+
+ /* 802.11 device statistics */
+ struct p80211_frmrx_t rx;
+
+ struct iw_statistics wstats;
+
+ /* jkriegl: iwspy fields */
+ u8 spy_number;
+ char spy_address[IW_MAX_SPY][ETH_ALEN];
+ struct iw_quality spy_stat[IW_MAX_SPY];
+} wlandevice_t;
+
+/* WEP stuff */
+int wep_change_key(wlandevice_t *wlandev, int keynum, u8 *key, int keylen);
+int wep_decrypt(wlandevice_t *wlandev, u8 *buf, u32 len, int key_override,
+ u8 *iv, u8 *icv);
+int wep_encrypt(wlandevice_t *wlandev, u8 *buf, u8 *dst, u32 len, int keynum,
+ u8 *iv, u8 *icv);
+
+int wlan_setup(wlandevice_t *wlandev, struct device *physdev);
+void wlan_unsetup(wlandevice_t *wlandev);
+int register_wlandev(wlandevice_t *wlandev);
+int unregister_wlandev(wlandevice_t *wlandev);
+void p80211netdev_rx(wlandevice_t *wlandev, struct sk_buff *skb);
+void p80211netdev_hwremoved(wlandevice_t *wlandev);
+#endif
diff --git a/drivers/staging/wlan-ng/p80211req.c b/drivers/staging/wlan-ng/p80211req.c
new file mode 100644
index 000000000..4b84b568f
--- /dev/null
+++ b/drivers/staging/wlan-ng/p80211req.c
@@ -0,0 +1,250 @@
+/* src/p80211/p80211req.c
+*
+* Request/Indication/MacMgmt interface handling functions
+*
+* Copyright (C) 1999 AbsoluteValue Systems, Inc. All Rights Reserved.
+* --------------------------------------------------------------------
+*
+* linux-wlan
+*
+* The contents of this file are subject to the Mozilla Public
+* License Version 1.1 (the "License"); you may not use this file
+* except in compliance with the License. You may obtain a copy of
+* the License at http://www.mozilla.org/MPL/
+*
+* Software distributed under the License is distributed on an "AS
+* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+* implied. See the License for the specific language governing
+* rights and limitations under the License.
+*
+* Alternatively, the contents of this file may be used under the
+* terms of the GNU Public License version 2 (the "GPL"), in which
+* case the provisions of the GPL are applicable instead of the
+* above. If you wish to allow the use of your version of this file
+* only under the terms of the GPL and not to allow others to use
+* your version of this file under the MPL, indicate your decision
+* by deleting the provisions above and replace them with the notice
+* and other provisions required by the GPL. If you do not delete
+* the provisions above, a recipient may use your version of this
+* file under either the MPL or the GPL.
+*
+* --------------------------------------------------------------------
+*
+* Inquiries regarding the linux-wlan Open Source project can be
+* made directly to:
+*
+* AbsoluteValue Systems Inc.
+* info@linux-wlan.com
+* http://www.linux-wlan.com
+*
+* --------------------------------------------------------------------
+*
+* Portions of the development of this software were funded by
+* Intersil Corporation as part of PRISM(R) chipset product development.
+*
+* --------------------------------------------------------------------
+*
+* This file contains the functions, types, and macros to support the
+* MLME request interface that's implemented via the device ioctls.
+*
+* --------------------------------------------------------------------
+*/
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/types.h>
+#include <linux/skbuff.h>
+#include <linux/wireless.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <net/sock.h>
+#include <linux/netlink.h>
+
+#include "p80211types.h"
+#include "p80211hdr.h"
+#include "p80211mgmt.h"
+#include "p80211conv.h"
+#include "p80211msg.h"
+#include "p80211netdev.h"
+#include "p80211ioctl.h"
+#include "p80211metadef.h"
+#include "p80211metastruct.h"
+#include "p80211req.h"
+
+static void p80211req_handlemsg(wlandevice_t *wlandev, struct p80211msg *msg);
+static void p80211req_mibset_mibget(wlandevice_t *wlandev,
+ struct p80211msg_dot11req_mibget *mib_msg,
+ int isget);
+
+/*----------------------------------------------------------------
+* p80211req_dorequest
+*
+* Handles an MLME request/confirm message.
+*
+* Arguments:
+* wlandev WLAN device struct
+* msgbuf Buffer containing a request message
+*
+* Returns:
+* 0 on success, an errno otherwise
+*
+* Call context:
+* Potentially blocks the caller, so it's a good idea to
+* not call this function from an interrupt context.
+----------------------------------------------------------------*/
+int p80211req_dorequest(wlandevice_t *wlandev, u8 *msgbuf)
+{
+ struct p80211msg *msg = (struct p80211msg *) msgbuf;
+
+ /* Check to make sure the MSD is running */
+ if (!((wlandev->msdstate == WLAN_MSD_HWPRESENT &&
+ msg->msgcode == DIDmsg_lnxreq_ifstate) ||
+ wlandev->msdstate == WLAN_MSD_RUNNING ||
+ wlandev->msdstate == WLAN_MSD_FWLOAD)) {
+ return -ENODEV;
+ }
+
+ /* Check Permissions */
+ if (!capable(CAP_NET_ADMIN) &&
+ (msg->msgcode != DIDmsg_dot11req_mibget)) {
+ netdev_err(wlandev->netdev,
+ "%s: only dot11req_mibget allowed for non-root.\n",
+ wlandev->name);
+ return -EPERM;
+ }
+
+ /* Check for busy status */
+ if (test_and_set_bit(1, &(wlandev->request_pending)))
+ return -EBUSY;
+
+ /* Allow p80211 to look at msg and handle if desired. */
+ /* So far, all p80211 msgs are immediate, no waitq/timer necessary */
+ /* This may change. */
+ p80211req_handlemsg(wlandev, msg);
+
+ /* Pass it down to wlandev via wlandev->mlmerequest */
+ if (wlandev->mlmerequest != NULL)
+ wlandev->mlmerequest(wlandev, msg);
+
+ clear_bit(1, &(wlandev->request_pending));
+ return 0; /* if result==0, msg->status still may contain an err */
+}
+
+/*----------------------------------------------------------------
+* p80211req_handlemsg
+*
+* p80211 message handler. Primarily looks for messages that
+* belong to p80211 and then dispatches the appropriate response.
+* TODO: we don't do anything yet. Once the linuxMIB is better
+* defined we'll need a get/set handler.
+*
+* Arguments:
+* wlandev WLAN device struct
+* msg message structure
+*
+* Returns:
+* nothing (any results are set in the status field of the msg)
+*
+* Call context:
+* Process thread
+----------------------------------------------------------------*/
+static void p80211req_handlemsg(wlandevice_t *wlandev, struct p80211msg *msg)
+{
+ switch (msg->msgcode) {
+
+ case DIDmsg_lnxreq_hostwep:{
+ struct p80211msg_lnxreq_hostwep *req =
+ (struct p80211msg_lnxreq_hostwep *) msg;
+ wlandev->hostwep &=
+ ~(HOSTWEP_DECRYPT | HOSTWEP_ENCRYPT);
+ if (req->decrypt.data == P80211ENUM_truth_true)
+ wlandev->hostwep |= HOSTWEP_DECRYPT;
+ if (req->encrypt.data == P80211ENUM_truth_true)
+ wlandev->hostwep |= HOSTWEP_ENCRYPT;
+
+ break;
+ }
+ case DIDmsg_dot11req_mibget:
+ case DIDmsg_dot11req_mibset:{
+ int isget = (msg->msgcode == DIDmsg_dot11req_mibget);
+ struct p80211msg_dot11req_mibget *mib_msg =
+ (struct p80211msg_dot11req_mibget *) msg;
+ p80211req_mibset_mibget(wlandev, mib_msg, isget);
+ break;
+ }
+ } /* switch msg->msgcode */
+}
+
+static void p80211req_mibset_mibget(wlandevice_t *wlandev,
+ struct p80211msg_dot11req_mibget *mib_msg,
+ int isget)
+{
+ p80211itemd_t *mibitem = (p80211itemd_t *) mib_msg->mibattribute.data;
+ p80211pstrd_t *pstr = (p80211pstrd_t *) mibitem->data;
+ u8 *key = mibitem->data + sizeof(p80211pstrd_t);
+
+ switch (mibitem->did) {
+ case DIDmib_dot11smt_dot11WEPDefaultKeysTable_dot11WEPDefaultKey0:{
+ if (!isget)
+ wep_change_key(wlandev, 0, key, pstr->len);
+ break;
+ }
+ case DIDmib_dot11smt_dot11WEPDefaultKeysTable_dot11WEPDefaultKey1:{
+ if (!isget)
+ wep_change_key(wlandev, 1, key, pstr->len);
+ break;
+ }
+ case DIDmib_dot11smt_dot11WEPDefaultKeysTable_dot11WEPDefaultKey2:{
+ if (!isget)
+ wep_change_key(wlandev, 2, key, pstr->len);
+ break;
+ }
+ case DIDmib_dot11smt_dot11WEPDefaultKeysTable_dot11WEPDefaultKey3:{
+ if (!isget)
+ wep_change_key(wlandev, 3, key, pstr->len);
+ break;
+ }
+ case DIDmib_dot11smt_dot11PrivacyTable_dot11WEPDefaultKeyID:{
+ u32 *data = (u32 *) mibitem->data;
+
+ if (isget) {
+ *data = wlandev->hostwep & HOSTWEP_DEFAULTKEY_MASK;
+ } else {
+ wlandev->hostwep &= ~(HOSTWEP_DEFAULTKEY_MASK);
+ wlandev->hostwep |= (*data & HOSTWEP_DEFAULTKEY_MASK);
+ }
+ break;
+ }
+ case DIDmib_dot11smt_dot11PrivacyTable_dot11PrivacyInvoked:{
+ u32 *data = (u32 *) mibitem->data;
+
+ if (isget) {
+ if (wlandev->hostwep & HOSTWEP_PRIVACYINVOKED)
+ *data = P80211ENUM_truth_true;
+ else
+ *data = P80211ENUM_truth_false;
+ } else {
+ wlandev->hostwep &= ~(HOSTWEP_PRIVACYINVOKED);
+ if (*data == P80211ENUM_truth_true)
+ wlandev->hostwep |= HOSTWEP_PRIVACYINVOKED;
+ }
+ break;
+ }
+ case DIDmib_dot11smt_dot11PrivacyTable_dot11ExcludeUnencrypted:{
+ u32 *data = (u32 *) mibitem->data;
+
+ if (isget) {
+ if (wlandev->hostwep & HOSTWEP_EXCLUDEUNENCRYPTED)
+ *data = P80211ENUM_truth_true;
+ else
+ *data = P80211ENUM_truth_false;
+ } else {
+ wlandev->hostwep &= ~(HOSTWEP_EXCLUDEUNENCRYPTED);
+ if (*data == P80211ENUM_truth_true)
+ wlandev->hostwep |= HOSTWEP_EXCLUDEUNENCRYPTED;
+ }
+ break;
+ }
+ }
+}
diff --git a/drivers/staging/wlan-ng/p80211req.h b/drivers/staging/wlan-ng/p80211req.h
new file mode 100644
index 000000000..a95a45a68
--- /dev/null
+++ b/drivers/staging/wlan-ng/p80211req.h
@@ -0,0 +1,53 @@
+/* p80211req.h
+*
+* Request handling functions
+*
+* Copyright (C) 1999 AbsoluteValue Systems, Inc. All Rights Reserved.
+* --------------------------------------------------------------------
+*
+* linux-wlan
+*
+* The contents of this file are subject to the Mozilla Public
+* License Version 1.1 (the "License"); you may not use this file
+* except in compliance with the License. You may obtain a copy of
+* the License at http://www.mozilla.org/MPL/
+*
+* Software distributed under the License is distributed on an "AS
+* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+* implied. See the License for the specific language governing
+* rights and limitations under the License.
+*
+* Alternatively, the contents of this file may be used under the
+* terms of the GNU Public License version 2 (the "GPL"), in which
+* case the provisions of the GPL are applicable instead of the
+* above. If you wish to allow the use of your version of this file
+* only under the terms of the GPL and not to allow others to use
+* your version of this file under the MPL, indicate your decision
+* by deleting the provisions above and replace them with the notice
+* and other provisions required by the GPL. If you do not delete
+* the provisions above, a recipient may use your version of this
+* file under either the MPL or the GPL.
+*
+* --------------------------------------------------------------------
+*
+* Inquiries regarding the linux-wlan Open Source project can be
+* made directly to:
+*
+* AbsoluteValue Systems Inc.
+* info@linux-wlan.com
+* http://www.linux-wlan.com
+*
+* --------------------------------------------------------------------
+*
+* Portions of the development of this software were funded by
+* Intersil Corporation as part of PRISM(R) chipset product development.
+*
+* --------------------------------------------------------------------
+*/
+
+#ifndef _LINUX_P80211REQ_H
+#define _LINUX_P80211REQ_H
+
+int p80211req_dorequest(wlandevice_t *wlandev, u8 *msgbuf);
+
+#endif
diff --git a/drivers/staging/wlan-ng/p80211types.h b/drivers/staging/wlan-ng/p80211types.h
new file mode 100644
index 000000000..8cb4fc644
--- /dev/null
+++ b/drivers/staging/wlan-ng/p80211types.h
@@ -0,0 +1,375 @@
+/* p80211types.h
+*
+* Macros, constants, types, and funcs for p80211 data types
+*
+* Copyright (C) 1999 AbsoluteValue Systems, Inc. All Rights Reserved.
+* --------------------------------------------------------------------
+*
+* linux-wlan
+*
+* The contents of this file are subject to the Mozilla Public
+* License Version 1.1 (the "License"); you may not use this file
+* except in compliance with the License. You may obtain a copy of
+* the License at http://www.mozilla.org/MPL/
+*
+* Software distributed under the License is distributed on an "AS
+* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+* implied. See the License for the specific language governing
+* rights and limitations under the License.
+*
+* Alternatively, the contents of this file may be used under the
+* terms of the GNU Public License version 2 (the "GPL"), in which
+* case the provisions of the GPL are applicable instead of the
+* above. If you wish to allow the use of your version of this file
+* only under the terms of the GPL and not to allow others to use
+* your version of this file under the MPL, indicate your decision
+* by deleting the provisions above and replace them with the notice
+* and other provisions required by the GPL. If you do not delete
+* the provisions above, a recipient may use your version of this
+* file under either the MPL or the GPL.
+*
+* --------------------------------------------------------------------
+*
+* Inquiries regarding the linux-wlan Open Source project can be
+* made directly to:
+*
+* AbsoluteValue Systems Inc.
+* info@linux-wlan.com
+* http://www.linux-wlan.com
+*
+* --------------------------------------------------------------------
+*
+* Portions of the development of this software were funded by
+* Intersil Corporation as part of PRISM(R) chipset product development.
+*
+* --------------------------------------------------------------------
+*
+* This file declares some of the constants and types used in various
+* parts of the linux-wlan system.
+*
+* Notes:
+* - Constant values are always in HOST byte order.
+*
+* All functions and statics declared here are implemented in p80211types.c
+* --------------------------------------------------------------------
+*/
+
+#ifndef _P80211TYPES_H
+#define _P80211TYPES_H
+
+/*----------------------------------------------------------------*/
+/* The following constants are indexes into the Mib Category List */
+/* and the Message Category List */
+
+/* Mib Category List */
+#define P80211_MIB_CAT_DOT11SMT 1
+#define P80211_MIB_CAT_DOT11MAC 2
+#define P80211_MIB_CAT_DOT11PHY 3
+
+#define P80211SEC_DOT11SMT P80211_MIB_CAT_DOT11SMT
+#define P80211SEC_DOT11MAC P80211_MIB_CAT_DOT11MAC
+#define P80211SEC_DOT11PHY P80211_MIB_CAT_DOT11PHY
+
+/* Message Category List */
+#define P80211_MSG_CAT_DOT11REQ 1
+#define P80211_MSG_CAT_DOT11IND 2
+
+/*----------------------------------------------------------------*/
+/* p80211 enumeration constants. The value to text mappings for */
+/* these is in p80211types.c. These defines were generated */
+/* from the mappings. */
+
+/* error codes for lookups */
+
+#define P80211ENUM_truth_false 0
+#define P80211ENUM_truth_true 1
+#define P80211ENUM_ifstate_disable 0
+#define P80211ENUM_ifstate_fwload 1
+#define P80211ENUM_ifstate_enable 2
+#define P80211ENUM_bsstype_infrastructure 1
+#define P80211ENUM_bsstype_independent 2
+#define P80211ENUM_bsstype_any 3
+#define P80211ENUM_authalg_opensystem 1
+#define P80211ENUM_authalg_sharedkey 2
+#define P80211ENUM_scantype_active 1
+#define P80211ENUM_resultcode_success 1
+#define P80211ENUM_resultcode_invalid_parameters 2
+#define P80211ENUM_resultcode_not_supported 3
+#define P80211ENUM_resultcode_refused 6
+#define P80211ENUM_resultcode_cant_set_readonly_mib 10
+#define P80211ENUM_resultcode_implementation_failure 11
+#define P80211ENUM_resultcode_cant_get_writeonly_mib 12
+#define P80211ENUM_status_successful 0
+#define P80211ENUM_status_unspec_failure 1
+#define P80211ENUM_status_ap_full 17
+#define P80211ENUM_msgitem_status_data_ok 0
+#define P80211ENUM_msgitem_status_no_value 1
+
+/*----------------------------------------------------------------*/
+/* p80211 max length constants for the different pascal strings. */
+
+#define MAXLEN_PSTR6 (6) /* pascal array of 6 bytes */
+#define MAXLEN_PSTR14 (14) /* pascal array of 14 bytes */
+#define MAXLEN_PSTR32 (32) /* pascal array of 32 bytes */
+#define MAXLEN_PSTR255 (255) /* pascal array of 255 bytes */
+#define MAXLEN_MIBATTRIBUTE (392) /* maximum mibattribute */
+ /* where the size of the DATA itself */
+ /* is a DID-LEN-DATA triple */
+ /* with a max size of 4+4+384 */
+
+/*----------------------------------------------------------------*/
+/* The following macro creates a name for an enum */
+
+#define MKENUMNAME(name) p80211enum_ ## name
+
+/*----------------------------------------------------------------
+* The following constants and macros are used to construct and
+* deconstruct the Data ID codes. The coding is as follows:
+*
+* ...rwtnnnnnnnniiiiiiggggggssssss s - Section
+* g - Group
+* i - Item
+* n - Index
+* t - Table flag
+* w - Write flag
+* r - Read flag
+* . - Unused
+*/
+
+#define P80211DID_LSB_SECTION (0)
+#define P80211DID_LSB_GROUP (6)
+#define P80211DID_LSB_ITEM (12)
+#define P80211DID_LSB_INDEX (18)
+#define P80211DID_LSB_ISTABLE (26)
+#define P80211DID_LSB_ACCESS (27)
+
+#define P80211DID_MASK_SECTION (0x0000003fUL)
+#define P80211DID_MASK_GROUP (0x0000003fUL)
+#define P80211DID_MASK_ITEM (0x0000003fUL)
+#define P80211DID_MASK_INDEX (0x000000ffUL)
+#define P80211DID_MASK_ISTABLE (0x00000001UL)
+#define P80211DID_MASK_ACCESS (0x00000003UL)
+
+#define P80211DID_MK(a, m, l) ((((u32)(a)) & (m)) << (l))
+
+#define P80211DID_MKSECTION(a) P80211DID_MK(a, \
+ P80211DID_MASK_SECTION, \
+ P80211DID_LSB_SECTION)
+#define P80211DID_MKGROUP(a) P80211DID_MK(a, \
+ P80211DID_MASK_GROUP, \
+ P80211DID_LSB_GROUP)
+#define P80211DID_MKITEM(a) P80211DID_MK(a, \
+ P80211DID_MASK_ITEM, \
+ P80211DID_LSB_ITEM)
+#define P80211DID_MKINDEX(a) P80211DID_MK(a, \
+ P80211DID_MASK_INDEX, \
+ P80211DID_LSB_INDEX)
+#define P80211DID_MKISTABLE(a) P80211DID_MK(a, \
+ P80211DID_MASK_ISTABLE, \
+ P80211DID_LSB_ISTABLE)
+
+#define P80211DID_MKID(s, g, i, n, t, a) (P80211DID_MKSECTION(s) | \
+ P80211DID_MKGROUP(g) | \
+ P80211DID_MKITEM(i) | \
+ P80211DID_MKINDEX(n) | \
+ P80211DID_MKISTABLE(t) | \
+ (a))
+
+#define P80211DID_GET(a, m, l) ((((u32)(a)) >> (l)) & (m))
+
+#define P80211DID_SECTION(a) P80211DID_GET(a, \
+ P80211DID_MASK_SECTION, \
+ P80211DID_LSB_SECTION)
+#define P80211DID_GROUP(a) P80211DID_GET(a, \
+ P80211DID_MASK_GROUP, \
+ P80211DID_LSB_GROUP)
+#define P80211DID_ITEM(a) P80211DID_GET(a, \
+ P80211DID_MASK_ITEM, \
+ P80211DID_LSB_ITEM)
+#define P80211DID_INDEX(a) P80211DID_GET(a, \
+ P80211DID_MASK_INDEX, \
+ P80211DID_LSB_INDEX)
+#define P80211DID_ISTABLE(a) P80211DID_GET(a, \
+ P80211DID_MASK_ISTABLE, \
+ P80211DID_LSB_ISTABLE)
+#define P80211DID_ACCESS(a) P80211DID_GET(a, \
+ P80211DID_MASK_ACCESS, \
+ P80211DID_LSB_ACCESS)
+
+/*----------------------------------------------------------------*/
+/* The following structure types are used for the representation */
+/* of ENUMint type metadata. */
+
+typedef struct p80211enumpair {
+ u32 val;
+ char *name;
+} p80211enumpair_t;
+
+typedef struct p80211enum {
+ int nitems;
+ p80211enumpair_t *list;
+} p80211enum_t;
+
+/*----------------------------------------------------------------*/
+/* The following structure types are used to store data items in */
+/* messages. */
+
+/* Template pascal string */
+typedef struct p80211pstr {
+ u8 len;
+} __packed p80211pstr_t;
+
+typedef struct p80211pstrd {
+ u8 len;
+ u8 data[0];
+} __packed p80211pstrd_t;
+
+/* Maximum pascal string */
+typedef struct p80211pstr255 {
+ u8 len;
+ u8 data[MAXLEN_PSTR255];
+} __packed p80211pstr255_t;
+
+/* pascal string for macaddress and bssid */
+typedef struct p80211pstr6 {
+ u8 len;
+ u8 data[MAXLEN_PSTR6];
+} __packed p80211pstr6_t;
+
+/* pascal string for channel list */
+typedef struct p80211pstr14 {
+ u8 len;
+ u8 data[MAXLEN_PSTR14];
+} __packed p80211pstr14_t;
+
+/* pascal string for ssid */
+typedef struct p80211pstr32 {
+ u8 len;
+ u8 data[MAXLEN_PSTR32];
+} __packed p80211pstr32_t;
+
+/* MAC address array */
+typedef struct p80211macarray {
+ u32 cnt;
+ u8 data[1][MAXLEN_PSTR6];
+} __packed p80211macarray_t;
+
+/* prototype template */
+typedef struct p80211item {
+ u32 did;
+ u16 status;
+ u16 len;
+} __packed p80211item_t;
+
+/* prototype template w/ data item */
+typedef struct p80211itemd {
+ u32 did;
+ u16 status;
+ u16 len;
+ u8 data[0];
+} __packed p80211itemd_t;
+
+/* message data item for int, BOUNDEDINT, ENUMINT */
+typedef struct p80211item_uint32 {
+ u32 did;
+ u16 status;
+ u16 len;
+ u32 data;
+} __packed p80211item_uint32_t;
+
+/* message data item for OCTETSTR, DISPLAYSTR */
+typedef struct p80211item_pstr6 {
+ u32 did;
+ u16 status;
+ u16 len;
+ p80211pstr6_t data;
+} __packed p80211item_pstr6_t;
+
+/* message data item for OCTETSTR, DISPLAYSTR */
+typedef struct p80211item_pstr14 {
+ u32 did;
+ u16 status;
+ u16 len;
+ p80211pstr14_t data;
+} __packed p80211item_pstr14_t;
+
+/* message data item for OCTETSTR, DISPLAYSTR */
+typedef struct p80211item_pstr32 {
+ u32 did;
+ u16 status;
+ u16 len;
+ p80211pstr32_t data;
+} __packed p80211item_pstr32_t;
+
+/* message data item for OCTETSTR, DISPLAYSTR */
+typedef struct p80211item_pstr255 {
+ u32 did;
+ u16 status;
+ u16 len;
+ p80211pstr255_t data;
+} __packed p80211item_pstr255_t;
+
+/* message data item for UNK 392, namely mib items */
+typedef struct p80211item_unk392 {
+ u32 did;
+ u16 status;
+ u16 len;
+ u8 data[MAXLEN_MIBATTRIBUTE];
+} __packed p80211item_unk392_t;
+
+/* message data item for UNK 1025, namely p2 pdas */
+typedef struct p80211item_unk1024 {
+ u32 did;
+ u16 status;
+ u16 len;
+ u8 data[1024];
+} __packed p80211item_unk1024_t;
+
+/* message data item for UNK 4096, namely p2 download chunks */
+typedef struct p80211item_unk4096 {
+ u32 did;
+ u16 status;
+ u16 len;
+ u8 data[4096];
+} __packed p80211item_unk4096_t;
+
+struct catlistitem;
+
+/*----------------------------------------------------------------*/
+/* The following structure type is used to represent all of the */
+/* metadata items. Some components may choose to use more, */
+/* less or different metadata items. */
+
+typedef void (*p80211_totext_t) (struct catlistitem *, u32 did, u8 *itembuf,
+ char *textbuf);
+typedef void (*p80211_fromtext_t) (struct catlistitem *, u32 did, u8 *itembuf,
+ char *textbuf);
+typedef u32(*p80211_valid_t) (struct catlistitem *, u32 did, u8 *itembuf);
+
+/*----------------------------------------------------------------*/
+/* Enumeration Lists */
+/* The following are the external declarations */
+/* for all enumerations */
+
+extern p80211enum_t MKENUMNAME(truth);
+extern p80211enum_t MKENUMNAME(ifstate);
+extern p80211enum_t MKENUMNAME(powermgmt);
+extern p80211enum_t MKENUMNAME(bsstype);
+extern p80211enum_t MKENUMNAME(authalg);
+extern p80211enum_t MKENUMNAME(phytype);
+extern p80211enum_t MKENUMNAME(temptype);
+extern p80211enum_t MKENUMNAME(regdomain);
+extern p80211enum_t MKENUMNAME(ccamode);
+extern p80211enum_t MKENUMNAME(diversity);
+extern p80211enum_t MKENUMNAME(scantype);
+extern p80211enum_t MKENUMNAME(resultcode);
+extern p80211enum_t MKENUMNAME(reason);
+extern p80211enum_t MKENUMNAME(status);
+extern p80211enum_t MKENUMNAME(msgcode);
+extern p80211enum_t MKENUMNAME(msgitem_status);
+
+extern p80211enum_t MKENUMNAME(lnxroam_reason);
+
+extern p80211enum_t MKENUMNAME(p2preamble);
+
+#endif /* _P80211TYPES_H */
diff --git a/drivers/staging/wlan-ng/p80211wep.c b/drivers/staging/wlan-ng/p80211wep.c
new file mode 100644
index 000000000..c4fabadf5
--- /dev/null
+++ b/drivers/staging/wlan-ng/p80211wep.c
@@ -0,0 +1,301 @@
+/* src/p80211/p80211wep.c
+*
+* WEP encode/decode for P80211.
+*
+* Copyright (C) 2002 AbsoluteValue Systems, Inc. All Rights Reserved.
+* --------------------------------------------------------------------
+*
+* linux-wlan
+*
+* The contents of this file are subject to the Mozilla Public
+* License Version 1.1 (the "License"); you may not use this file
+* except in compliance with the License. You may obtain a copy of
+* the License at http://www.mozilla.org/MPL/
+*
+* Software distributed under the License is distributed on an "AS
+* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+* implied. See the License for the specific language governing
+* rights and limitations under the License.
+*
+* Alternatively, the contents of this file may be used under the
+* terms of the GNU Public License version 2 (the "GPL"), in which
+* case the provisions of the GPL are applicable instead of the
+* above. If you wish to allow the use of your version of this file
+* only under the terms of the GPL and not to allow others to use
+* your version of this file under the MPL, indicate your decision
+* by deleting the provisions above and replace them with the notice
+* and other provisions required by the GPL. If you do not delete
+* the provisions above, a recipient may use your version of this
+* file under either the MPL or the GPL.
+*
+* --------------------------------------------------------------------
+*
+* Inquiries regarding the linux-wlan Open Source project can be
+* made directly to:
+*
+* AbsoluteValue Systems Inc.
+* info@linux-wlan.com
+* http://www.linux-wlan.com
+*
+* --------------------------------------------------------------------
+*
+* Portions of the development of this software were funded by
+* Intersil Corporation as part of PRISM(R) chipset product development.
+*
+* --------------------------------------------------------------------
+*/
+
+/*================================================================*/
+/* System Includes */
+
+#include <linux/netdevice.h>
+#include <linux/wireless.h>
+#include <linux/random.h>
+#include <linux/kernel.h>
+
+/* #define WEP_DEBUG */
+
+#include "p80211hdr.h"
+#include "p80211types.h"
+#include "p80211msg.h"
+#include "p80211conv.h"
+#include "p80211netdev.h"
+
+#define WEP_KEY(x) (((x) & 0xC0) >> 6)
+
+static const u32 wep_crc32_table[256] = {
+ 0x00000000L, 0x77073096L, 0xee0e612cL, 0x990951baL, 0x076dc419L,
+ 0x706af48fL, 0xe963a535L, 0x9e6495a3L, 0x0edb8832L, 0x79dcb8a4L,
+ 0xe0d5e91eL, 0x97d2d988L, 0x09b64c2bL, 0x7eb17cbdL, 0xe7b82d07L,
+ 0x90bf1d91L, 0x1db71064L, 0x6ab020f2L, 0xf3b97148L, 0x84be41deL,
+ 0x1adad47dL, 0x6ddde4ebL, 0xf4d4b551L, 0x83d385c7L, 0x136c9856L,
+ 0x646ba8c0L, 0xfd62f97aL, 0x8a65c9ecL, 0x14015c4fL, 0x63066cd9L,
+ 0xfa0f3d63L, 0x8d080df5L, 0x3b6e20c8L, 0x4c69105eL, 0xd56041e4L,
+ 0xa2677172L, 0x3c03e4d1L, 0x4b04d447L, 0xd20d85fdL, 0xa50ab56bL,
+ 0x35b5a8faL, 0x42b2986cL, 0xdbbbc9d6L, 0xacbcf940L, 0x32d86ce3L,
+ 0x45df5c75L, 0xdcd60dcfL, 0xabd13d59L, 0x26d930acL, 0x51de003aL,
+ 0xc8d75180L, 0xbfd06116L, 0x21b4f4b5L, 0x56b3c423L, 0xcfba9599L,
+ 0xb8bda50fL, 0x2802b89eL, 0x5f058808L, 0xc60cd9b2L, 0xb10be924L,
+ 0x2f6f7c87L, 0x58684c11L, 0xc1611dabL, 0xb6662d3dL, 0x76dc4190L,
+ 0x01db7106L, 0x98d220bcL, 0xefd5102aL, 0x71b18589L, 0x06b6b51fL,
+ 0x9fbfe4a5L, 0xe8b8d433L, 0x7807c9a2L, 0x0f00f934L, 0x9609a88eL,
+ 0xe10e9818L, 0x7f6a0dbbL, 0x086d3d2dL, 0x91646c97L, 0xe6635c01L,
+ 0x6b6b51f4L, 0x1c6c6162L, 0x856530d8L, 0xf262004eL, 0x6c0695edL,
+ 0x1b01a57bL, 0x8208f4c1L, 0xf50fc457L, 0x65b0d9c6L, 0x12b7e950L,
+ 0x8bbeb8eaL, 0xfcb9887cL, 0x62dd1ddfL, 0x15da2d49L, 0x8cd37cf3L,
+ 0xfbd44c65L, 0x4db26158L, 0x3ab551ceL, 0xa3bc0074L, 0xd4bb30e2L,
+ 0x4adfa541L, 0x3dd895d7L, 0xa4d1c46dL, 0xd3d6f4fbL, 0x4369e96aL,
+ 0x346ed9fcL, 0xad678846L, 0xda60b8d0L, 0x44042d73L, 0x33031de5L,
+ 0xaa0a4c5fL, 0xdd0d7cc9L, 0x5005713cL, 0x270241aaL, 0xbe0b1010L,
+ 0xc90c2086L, 0x5768b525L, 0x206f85b3L, 0xb966d409L, 0xce61e49fL,
+ 0x5edef90eL, 0x29d9c998L, 0xb0d09822L, 0xc7d7a8b4L, 0x59b33d17L,
+ 0x2eb40d81L, 0xb7bd5c3bL, 0xc0ba6cadL, 0xedb88320L, 0x9abfb3b6L,
+ 0x03b6e20cL, 0x74b1d29aL, 0xead54739L, 0x9dd277afL, 0x04db2615L,
+ 0x73dc1683L, 0xe3630b12L, 0x94643b84L, 0x0d6d6a3eL, 0x7a6a5aa8L,
+ 0xe40ecf0bL, 0x9309ff9dL, 0x0a00ae27L, 0x7d079eb1L, 0xf00f9344L,
+ 0x8708a3d2L, 0x1e01f268L, 0x6906c2feL, 0xf762575dL, 0x806567cbL,
+ 0x196c3671L, 0x6e6b06e7L, 0xfed41b76L, 0x89d32be0L, 0x10da7a5aL,
+ 0x67dd4accL, 0xf9b9df6fL, 0x8ebeeff9L, 0x17b7be43L, 0x60b08ed5L,
+ 0xd6d6a3e8L, 0xa1d1937eL, 0x38d8c2c4L, 0x4fdff252L, 0xd1bb67f1L,
+ 0xa6bc5767L, 0x3fb506ddL, 0x48b2364bL, 0xd80d2bdaL, 0xaf0a1b4cL,
+ 0x36034af6L, 0x41047a60L, 0xdf60efc3L, 0xa867df55L, 0x316e8eefL,
+ 0x4669be79L, 0xcb61b38cL, 0xbc66831aL, 0x256fd2a0L, 0x5268e236L,
+ 0xcc0c7795L, 0xbb0b4703L, 0x220216b9L, 0x5505262fL, 0xc5ba3bbeL,
+ 0xb2bd0b28L, 0x2bb45a92L, 0x5cb36a04L, 0xc2d7ffa7L, 0xb5d0cf31L,
+ 0x2cd99e8bL, 0x5bdeae1dL, 0x9b64c2b0L, 0xec63f226L, 0x756aa39cL,
+ 0x026d930aL, 0x9c0906a9L, 0xeb0e363fL, 0x72076785L, 0x05005713L,
+ 0x95bf4a82L, 0xe2b87a14L, 0x7bb12baeL, 0x0cb61b38L, 0x92d28e9bL,
+ 0xe5d5be0dL, 0x7cdcefb7L, 0x0bdbdf21L, 0x86d3d2d4L, 0xf1d4e242L,
+ 0x68ddb3f8L, 0x1fda836eL, 0x81be16cdL, 0xf6b9265bL, 0x6fb077e1L,
+ 0x18b74777L, 0x88085ae6L, 0xff0f6a70L, 0x66063bcaL, 0x11010b5cL,
+ 0x8f659effL, 0xf862ae69L, 0x616bffd3L, 0x166ccf45L, 0xa00ae278L,
+ 0xd70dd2eeL, 0x4e048354L, 0x3903b3c2L, 0xa7672661L, 0xd06016f7L,
+ 0x4969474dL, 0x3e6e77dbL, 0xaed16a4aL, 0xd9d65adcL, 0x40df0b66L,
+ 0x37d83bf0L, 0xa9bcae53L, 0xdebb9ec5L, 0x47b2cf7fL, 0x30b5ffe9L,
+ 0xbdbdf21cL, 0xcabac28aL, 0x53b39330L, 0x24b4a3a6L, 0xbad03605L,
+ 0xcdd70693L, 0x54de5729L, 0x23d967bfL, 0xb3667a2eL, 0xc4614ab8L,
+ 0x5d681b02L, 0x2a6f2b94L, 0xb40bbe37L, 0xc30c8ea1L, 0x5a05df1bL,
+ 0x2d02ef8dL
+};
+
+/* keylen in bytes! */
+
+int wep_change_key(wlandevice_t *wlandev, int keynum, u8 *key, int keylen)
+{
+ if (keylen < 0)
+ return -1;
+ if (keylen >= MAX_KEYLEN)
+ return -1;
+ if (key == NULL)
+ return -1;
+ if (keynum < 0)
+ return -1;
+ if (keynum >= NUM_WEPKEYS)
+ return -1;
+
+#ifdef WEP_DEBUG
+ pr_debug("WEP key %d len %d = %*phC\n", keynum, keylen,
+ 8, key);
+#endif
+
+ wlandev->wep_keylens[keynum] = keylen;
+ memcpy(wlandev->wep_keys[keynum], key, keylen);
+
+ return 0;
+}
+
+/*
+ 4-byte IV at start of buffer, 4-byte ICV at end of buffer.
+ if successful, buf start is payload begin, length -= 8;
+ */
+int wep_decrypt(wlandevice_t *wlandev, u8 *buf, u32 len, int key_override,
+ u8 *iv, u8 *icv)
+{
+ u32 i, j, k, crc, keylen;
+ u8 s[256], key[64], c_crc[4];
+ u8 keyidx;
+
+ /* Needs to be at least 8 bytes of payload */
+ if (len <= 0)
+ return -1;
+
+ /* initialize the first bytes of the key from the IV */
+ key[0] = iv[0];
+ key[1] = iv[1];
+ key[2] = iv[2];
+ keyidx = WEP_KEY(iv[3]);
+
+ if (key_override >= 0)
+ keyidx = key_override;
+
+ if (keyidx >= NUM_WEPKEYS)
+ return -2;
+
+ keylen = wlandev->wep_keylens[keyidx];
+
+ if (keylen == 0)
+ return -3;
+
+ /* copy the rest of the key over from the designated key */
+ memcpy(key + 3, wlandev->wep_keys[keyidx], keylen);
+
+ keylen += 3; /* add in IV bytes */
+
+#ifdef WEP_DEBUG
+ pr_debug("D %d: %*ph (%d %d) %*phC\n", len, 3, key,
+ keyidx, keylen, 5, key + 3);
+#endif
+
+ /* set up the RC4 state */
+ for (i = 0; i < 256; i++)
+ s[i] = i;
+ j = 0;
+ for (i = 0; i < 256; i++) {
+ j = (j + s[i] + key[i % keylen]) & 0xff;
+ swap(i, j);
+ }
+
+ /* Apply the RC4 to the data, update the CRC32 */
+ crc = ~0;
+ i = j = 0;
+ for (k = 0; k < len; k++) {
+ i = (i + 1) & 0xff;
+ j = (j + s[i]) & 0xff;
+ swap(i, j);
+ buf[k] ^= s[(s[i] + s[j]) & 0xff];
+ crc = wep_crc32_table[(crc ^ buf[k]) & 0xff] ^ (crc >> 8);
+ }
+ crc = ~crc;
+
+ /* now let's check the crc */
+ c_crc[0] = crc;
+ c_crc[1] = crc >> 8;
+ c_crc[2] = crc >> 16;
+ c_crc[3] = crc >> 24;
+
+ for (k = 0; k < 4; k++) {
+ i = (i + 1) & 0xff;
+ j = (j + s[i]) & 0xff;
+ swap(i, j);
+ if ((c_crc[k] ^ s[(s[i] + s[j]) & 0xff]) != icv[k])
+ return -(4 | (k << 4)); /* ICV mismatch */
+ }
+
+ return 0;
+}
+
+/* encrypts in-place. */
+int wep_encrypt(wlandevice_t *wlandev, u8 *buf, u8 *dst, u32 len, int keynum,
+ u8 *iv, u8 *icv)
+{
+ u32 i, j, k, crc, keylen;
+ u8 s[256], key[64];
+
+ /* no point in WEPping an empty frame */
+ if (len <= 0)
+ return -1;
+
+ /* we need to have a real key.. */
+ if (keynum >= NUM_WEPKEYS)
+ return -2;
+ keylen = wlandev->wep_keylens[keynum];
+ if (keylen <= 0)
+ return -3;
+
+ /* use a random IV. And skip known weak ones. */
+ get_random_bytes(iv, 3);
+ while ((iv[1] == 0xff) && (iv[0] >= 3) && (iv[0] < keylen))
+ get_random_bytes(iv, 3);
+
+ iv[3] = (keynum & 0x03) << 6;
+
+ key[0] = iv[0];
+ key[1] = iv[1];
+ key[2] = iv[2];
+
+ /* copy the rest of the key over from the designated key */
+ memcpy(key + 3, wlandev->wep_keys[keynum], keylen);
+
+ keylen += 3; /* add in IV bytes */
+
+#ifdef WEP_DEBUG
+ pr_debug("E %d (%d/%d %d) %*ph %*phC\n", len,
+ iv[3], keynum, keylen, 3, key, 5, key + 3);
+#endif
+
+ /* set up the RC4 state */
+ for (i = 0; i < 256; i++)
+ s[i] = i;
+ j = 0;
+ for (i = 0; i < 256; i++) {
+ j = (j + s[i] + key[i % keylen]) & 0xff;
+ swap(i, j);
+ }
+
+ /* Update CRC32 then apply RC4 to the data */
+ crc = ~0;
+ i = j = 0;
+ for (k = 0; k < len; k++) {
+ crc = wep_crc32_table[(crc ^ buf[k]) & 0xff] ^ (crc >> 8);
+ i = (i + 1) & 0xff;
+ j = (j + s[i]) & 0xff;
+ swap(i, j);
+ dst[k] = buf[k] ^ s[(s[i] + s[j]) & 0xff];
+ }
+ crc = ~crc;
+
+ /* now let's encrypt the crc */
+ icv[0] = crc;
+ icv[1] = crc >> 8;
+ icv[2] = crc >> 16;
+ icv[3] = crc >> 24;
+
+ for (k = 0; k < 4; k++) {
+ i = (i + 1) & 0xff;
+ j = (j + s[i]) & 0xff;
+ swap(i, j);
+ icv[k] ^= s[(s[i] + s[j]) & 0xff];
+ }
+
+ return 0;
+}
diff --git a/drivers/staging/wlan-ng/prism2fw.c b/drivers/staging/wlan-ng/prism2fw.c
new file mode 100644
index 000000000..800eb8634
--- /dev/null
+++ b/drivers/staging/wlan-ng/prism2fw.c
@@ -0,0 +1,1217 @@
+/* from src/prism2/download/prism2dl.c
+*
+* utility for downloading prism2 images moved into kernelspace
+*
+* Copyright (C) 1999 AbsoluteValue Systems, Inc. All Rights Reserved.
+* --------------------------------------------------------------------
+*
+* linux-wlan
+*
+* The contents of this file are subject to the Mozilla Public
+* License Version 1.1 (the "License"); you may not use this file
+* except in compliance with the License. You may obtain a copy of
+* the License at http://www.mozilla.org/MPL/
+*
+* Software distributed under the License is distributed on an "AS
+* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+* implied. See the License for the specific language governing
+* rights and limitations under the License.
+*
+* Alternatively, the contents of this file may be used under the
+* terms of the GNU Public License version 2 (the "GPL"), in which
+* case the provisions of the GPL are applicable instead of the
+* above. If you wish to allow the use of your version of this file
+* only under the terms of the GPL and not to allow others to use
+* your version of this file under the MPL, indicate your decision
+* by deleting the provisions above and replace them with the notice
+* and other provisions required by the GPL. If you do not delete
+* the provisions above, a recipient may use your version of this
+* file under either the MPL or the GPL.
+*
+* --------------------------------------------------------------------
+*
+* Inquiries regarding the linux-wlan Open Source project can be
+* made directly to:
+*
+* AbsoluteValue Systems Inc.
+* info@linux-wlan.com
+* http://www.linux-wlan.com
+*
+* --------------------------------------------------------------------
+*
+* Portions of the development of this software were funded by
+* Intersil Corporation as part of PRISM(R) chipset product development.
+*
+* --------------------------------------------------------------------
+*/
+
+/*================================================================*/
+/* System Includes */
+#include <linux/ihex.h>
+#include <linux/slab.h>
+
+/*================================================================*/
+/* Local Constants */
+
+#define PRISM2_USB_FWFILE "/*(DEBLOBBED)*/"
+/*(DEBLOBBED)*/
+
+#define S3DATA_MAX 5000
+#define S3PLUG_MAX 200
+#define S3CRC_MAX 200
+#define S3INFO_MAX 50
+
+#define S3ADDR_PLUG (0xff000000UL)
+#define S3ADDR_CRC (0xff100000UL)
+#define S3ADDR_INFO (0xff200000UL)
+#define S3ADDR_START (0xff400000UL)
+
+#define CHUNKS_MAX 100
+
+#define WRITESIZE_MAX 4096
+
+/*================================================================*/
+/* Local Types */
+
+struct s3datarec {
+ u32 len;
+ u32 addr;
+ u8 checksum;
+ u8 *data;
+};
+
+struct s3plugrec {
+ u32 itemcode;
+ u32 addr;
+ u32 len;
+};
+
+struct s3crcrec {
+ u32 addr;
+ u32 len;
+ unsigned int dowrite;
+};
+
+struct s3inforec {
+ u16 len;
+ u16 type;
+ union {
+ hfa384x_compident_t version;
+ hfa384x_caplevel_t compat;
+ u16 buildseq;
+ hfa384x_compident_t platform;
+ } info;
+};
+
+struct pda {
+ u8 buf[HFA384x_PDA_LEN_MAX];
+ hfa384x_pdrec_t *rec[HFA384x_PDA_RECS_MAX];
+ unsigned int nrec;
+};
+
+struct imgchunk {
+ u32 addr; /* start address */
+ u32 len; /* in bytes */
+ u16 crc; /* CRC value (if it falls at a chunk boundary) */
+ u8 *data;
+};
+
+/*================================================================*/
+/* Local Static Definitions */
+
+/*----------------------------------------------------------------*/
+/* s-record image processing */
+
+/* Data records */
+static unsigned int ns3data;
+static struct s3datarec s3data[S3DATA_MAX];
+
+/* Plug records */
+static unsigned int ns3plug;
+static struct s3plugrec s3plug[S3PLUG_MAX];
+
+/* CRC records */
+static unsigned int ns3crc;
+static struct s3crcrec s3crc[S3CRC_MAX];
+
+/* Info records */
+static unsigned int ns3info;
+static struct s3inforec s3info[S3INFO_MAX];
+
+/* S7 record (there _better_ be only one) */
+static u32 startaddr;
+
+/* Load image chunks */
+static unsigned int nfchunks;
+static struct imgchunk fchunk[CHUNKS_MAX];
+
+/* Note that for the following pdrec_t arrays, the len and code */
+/* fields are stored in HOST byte order. The mkpdrlist() function */
+/* does the conversion. */
+/*----------------------------------------------------------------*/
+/* PDA, built from [card|newfile]+[addfile1+addfile2...] */
+
+static struct pda pda;
+static hfa384x_compident_t nicid;
+static hfa384x_caplevel_t rfid;
+static hfa384x_caplevel_t macid;
+static hfa384x_caplevel_t priid;
+
+/*================================================================*/
+/* Local Function Declarations */
+
+static int prism2_fwapply(const struct ihex_binrec *rfptr,
+wlandevice_t *wlandev);
+
+static int read_fwfile(const struct ihex_binrec *rfptr);
+
+static int mkimage(struct imgchunk *clist, unsigned int *ccnt);
+
+static int read_cardpda(struct pda *pda, wlandevice_t *wlandev);
+
+static int mkpdrlist(struct pda *pda);
+
+static int plugimage(struct imgchunk *fchunk, unsigned int nfchunks,
+ struct s3plugrec *s3plug, unsigned int ns3plug, struct pda *pda);
+
+static int crcimage(struct imgchunk *fchunk, unsigned int nfchunks,
+ struct s3crcrec *s3crc, unsigned int ns3crc);
+
+static int writeimage(wlandevice_t *wlandev, struct imgchunk *fchunk,
+ unsigned int nfchunks);
+static void free_chunks(struct imgchunk *fchunk, unsigned int *nfchunks);
+
+static void free_srecs(void);
+
+static int validate_identity(void);
+
+/*================================================================*/
+/* Function Definitions */
+
+/*----------------------------------------------------------------
+* prism2_fwtry
+*
+* Try and get firmware into memory
+*
+* Arguments:
+* udev usb device structure
+* wlandev wlan device structure
+*
+* Returns:
+* 0 - success
+* ~0 - failure
+----------------------------------------------------------------*/
+static int prism2_fwtry(struct usb_device *udev, wlandevice_t *wlandev)
+{
+ const struct firmware *fw_entry = NULL;
+
+ netdev_info(wlandev->netdev, "prism2_usb: Checking for firmware %s\n",
+ PRISM2_USB_FWFILE);
+ if (reject_firmware(&fw_entry,
+ PRISM2_USB_FWFILE, &udev->dev) != 0) {
+ netdev_info(wlandev->netdev,
+ "prism2_usb: Firmware not available, but not essential\n");
+ netdev_info(wlandev->netdev,
+ "prism2_usb: can continue to use card anyway.\n");
+ return 1;
+ }
+
+ netdev_info(wlandev->netdev,
+ "prism2_usb: %s will be processed, size %zu\n",
+ PRISM2_USB_FWFILE, fw_entry->size);
+ prism2_fwapply((const struct ihex_binrec *)fw_entry->data, wlandev);
+
+ release_firmware(fw_entry);
+ return 0;
+}
+
+/*----------------------------------------------------------------
+* prism2_fwapply
+*
+* Apply the firmware loaded into memory
+*
+* Arguments:
+* rfptr firmware image in kernel memory
+* wlandev device
+*
+* Returns:
+* 0 - success
+* ~0 - failure
+----------------------------------------------------------------*/
+static int prism2_fwapply(const struct ihex_binrec *rfptr,
+ wlandevice_t *wlandev)
+{
+ signed int result = 0;
+ struct p80211msg_dot11req_mibget getmsg;
+ p80211itemd_t *item;
+ u32 *data;
+
+ /* Initialize the data structures */
+ ns3data = 0;
+ memset(s3data, 0, sizeof(s3data));
+ ns3plug = 0;
+ memset(s3plug, 0, sizeof(s3plug));
+ ns3crc = 0;
+ memset(s3crc, 0, sizeof(s3crc));
+ ns3info = 0;
+ memset(s3info, 0, sizeof(s3info));
+ startaddr = 0;
+
+ nfchunks = 0;
+ memset(fchunk, 0, sizeof(fchunk));
+ memset(&nicid, 0, sizeof(nicid));
+ memset(&rfid, 0, sizeof(rfid));
+ memset(&macid, 0, sizeof(macid));
+ memset(&priid, 0, sizeof(priid));
+
+ /* clear the pda and add an initial END record */
+ memset(&pda, 0, sizeof(pda));
+ pda.rec[0] = (hfa384x_pdrec_t *) pda.buf;
+ pda.rec[0]->len = cpu_to_le16(2); /* len in words */
+ pda.rec[0]->code = cpu_to_le16(HFA384x_PDR_END_OF_PDA);
+ pda.nrec = 1;
+
+ /*-----------------------------------------------------*/
+ /* Put card into fwload state */
+ prism2sta_ifstate(wlandev, P80211ENUM_ifstate_fwload);
+
+ /* Build the PDA we're going to use. */
+ if (read_cardpda(&pda, wlandev)) {
+ netdev_err(wlandev->netdev, "load_cardpda failed, exiting.\n");
+ return 1;
+ }
+
+ /* read the card's PRI-SUP */
+ memset(&getmsg, 0, sizeof(getmsg));
+ getmsg.msgcode = DIDmsg_dot11req_mibget;
+ getmsg.msglen = sizeof(getmsg);
+ strcpy(getmsg.devname, wlandev->name);
+
+ getmsg.mibattribute.did = DIDmsg_dot11req_mibget_mibattribute;
+ getmsg.mibattribute.status = P80211ENUM_msgitem_status_data_ok;
+ getmsg.resultcode.did = DIDmsg_dot11req_mibget_resultcode;
+ getmsg.resultcode.status = P80211ENUM_msgitem_status_no_value;
+
+ item = (p80211itemd_t *) getmsg.mibattribute.data;
+ item->did = DIDmib_p2_p2NIC_p2PRISupRange;
+ item->status = P80211ENUM_msgitem_status_no_value;
+
+ data = (u32 *) item->data;
+
+ /* DIDmsg_dot11req_mibget */
+ prism2mgmt_mibset_mibget(wlandev, &getmsg);
+ if (getmsg.resultcode.data != P80211ENUM_resultcode_success)
+ netdev_err(wlandev->netdev, "Couldn't fetch PRI-SUP info\n");
+
+ /* Already in host order */
+ priid.role = *data++;
+ priid.id = *data++;
+ priid.variant = *data++;
+ priid.bottom = *data++;
+ priid.top = *data++;
+
+ /* Read the S3 file */
+ result = read_fwfile(rfptr);
+ if (result) {
+ netdev_err(wlandev->netdev,
+ "Failed to read the data exiting.\n");
+ return 1;
+ }
+
+ result = validate_identity();
+
+ if (result) {
+ netdev_err(wlandev->netdev, "Incompatible firmware image.\n");
+ return 1;
+ }
+
+ if (startaddr == 0x00000000) {
+ netdev_err(wlandev->netdev,
+ "Can't RAM download a Flash image!\n");
+ return 1;
+ }
+
+ /* Make the image chunks */
+ result = mkimage(fchunk, &nfchunks);
+
+ /* Do any plugging */
+ result = plugimage(fchunk, nfchunks, s3plug, ns3plug, &pda);
+ if (result) {
+ netdev_err(wlandev->netdev, "Failed to plug data.\n");
+ return 1;
+ }
+
+ /* Insert any CRCs */
+ if (crcimage(fchunk, nfchunks, s3crc, ns3crc)) {
+ netdev_err(wlandev->netdev, "Failed to insert all CRCs\n");
+ return 1;
+ }
+
+ /* Write the image */
+ result = writeimage(wlandev, fchunk, nfchunks);
+ if (result) {
+ netdev_err(wlandev->netdev, "Failed to ramwrite image data.\n");
+ return 1;
+ }
+
+ /* clear any allocated memory */
+ free_chunks(fchunk, &nfchunks);
+ free_srecs();
+
+ netdev_info(wlandev->netdev, "prism2_usb: firmware loading finished.\n");
+
+ return result;
+}
+
+/*----------------------------------------------------------------
+* crcimage
+*
+* Adds a CRC16 in the two bytes prior to each block identified by
+* an S3 CRC record. Currently, we don't actually do a CRC we just
+* insert the value 0xC0DE in hfa384x order.
+*
+* Arguments:
+* fchunk Array of image chunks
+* nfchunks Number of image chunks
+* s3crc Array of crc records
+* ns3crc Number of crc records
+*
+* Returns:
+* 0 success
+* ~0 failure
+----------------------------------------------------------------*/
+static int crcimage(struct imgchunk *fchunk, unsigned int nfchunks,
+ struct s3crcrec *s3crc, unsigned int ns3crc)
+{
+ int result = 0;
+ int i;
+ int c;
+ u32 crcstart;
+ u32 crcend;
+ u32 cstart = 0;
+ u32 cend;
+ u8 *dest;
+ u32 chunkoff;
+
+ for (i = 0; i < ns3crc; i++) {
+ if (!s3crc[i].dowrite)
+ continue;
+ crcstart = s3crc[i].addr;
+ crcend = s3crc[i].addr + s3crc[i].len;
+ /* Find chunk */
+ for (c = 0; c < nfchunks; c++) {
+ cstart = fchunk[c].addr;
+ cend = fchunk[c].addr + fchunk[c].len;
+ /* the line below does an address & len match search */
+ /* unfortunately, I've found that the len fields of */
+ /* some crc records don't match with the length of */
+ /* the actual data, so we're not checking right now */
+ /* if (crcstart-2 >= cstart && crcend <= cend) break; */
+
+ /* note the -2 below, it's to make sure the chunk has */
+ /* space for the CRC value */
+ if (crcstart - 2 >= cstart && crcstart < cend)
+ break;
+ }
+ if (c >= nfchunks) {
+ pr_err("Failed to find chunk for crcrec[%d], addr=0x%06x len=%d , aborting crc.\n",
+ i, s3crc[i].addr, s3crc[i].len);
+ return 1;
+ }
+
+ /* Insert crc */
+ pr_debug("Adding crc @ 0x%06x\n", s3crc[i].addr - 2);
+ chunkoff = crcstart - cstart - 2;
+ dest = fchunk[c].data + chunkoff;
+ *dest = 0xde;
+ *(dest + 1) = 0xc0;
+
+ }
+ return result;
+}
+
+/*----------------------------------------------------------------
+* free_chunks
+*
+* Clears the chunklist data structures in preparation for a new file.
+*
+* Arguments:
+* none
+*
+* Returns:
+* nothing
+----------------------------------------------------------------*/
+static void free_chunks(struct imgchunk *fchunk, unsigned int *nfchunks)
+{
+ int i;
+
+ for (i = 0; i < *nfchunks; i++)
+ kfree(fchunk[i].data);
+
+ *nfchunks = 0;
+ memset(fchunk, 0, sizeof(*fchunk));
+
+}
+
+/*----------------------------------------------------------------
+* free_srecs
+*
+* Clears the srec data structures in preparation for a new file.
+*
+* Arguments:
+* none
+*
+* Returns:
+* nothing
+----------------------------------------------------------------*/
+static void free_srecs(void)
+{
+ ns3data = 0;
+ memset(s3data, 0, sizeof(s3data));
+ ns3plug = 0;
+ memset(s3plug, 0, sizeof(s3plug));
+ ns3crc = 0;
+ memset(s3crc, 0, sizeof(s3crc));
+ ns3info = 0;
+ memset(s3info, 0, sizeof(s3info));
+ startaddr = 0;
+}
+
+/*----------------------------------------------------------------
+* mkimage
+*
+* Scans the currently loaded set of S records for data residing
+* in contiguous memory regions. Each contiguous region is then
+* made into a 'chunk'. This function assumes that we're building
+* a new chunk list. Assumes the s3data items are in sorted order.
+*
+* Arguments: none
+*
+* Returns:
+* 0 - success
+* ~0 - failure (probably an errno)
+----------------------------------------------------------------*/
+static int mkimage(struct imgchunk *clist, unsigned int *ccnt)
+{
+ int result = 0;
+ int i;
+ int j;
+ int currchunk = 0;
+ u32 nextaddr = 0;
+ u32 s3start;
+ u32 s3end;
+ u32 cstart = 0;
+ u32 cend;
+ u32 coffset;
+
+ /* There may already be data in the chunklist */
+ *ccnt = 0;
+
+ /* Establish the location and size of each chunk */
+ for (i = 0; i < ns3data; i++) {
+ if (s3data[i].addr == nextaddr) {
+ /* existing chunk, grow it */
+ clist[currchunk].len += s3data[i].len;
+ nextaddr += s3data[i].len;
+ } else {
+ /* New chunk */
+ (*ccnt)++;
+ currchunk = *ccnt - 1;
+ clist[currchunk].addr = s3data[i].addr;
+ clist[currchunk].len = s3data[i].len;
+ nextaddr = s3data[i].addr + s3data[i].len;
+ /* Expand the chunk if there is a CRC record at */
+ /* their beginning bound */
+ for (j = 0; j < ns3crc; j++) {
+ if (s3crc[j].dowrite &&
+ s3crc[j].addr == clist[currchunk].addr) {
+ clist[currchunk].addr -= 2;
+ clist[currchunk].len += 2;
+ }
+ }
+ }
+ }
+
+ /* We're currently assuming there aren't any overlapping chunks */
+ /* if this proves false, we'll need to add code to coalesce. */
+
+ /* Allocate buffer space for chunks */
+ for (i = 0; i < *ccnt; i++) {
+ clist[i].data = kzalloc(clist[i].len, GFP_KERNEL);
+ if (clist[i].data == NULL) {
+ pr_err("failed to allocate image space, exitting.\n");
+ return 1;
+ }
+ pr_debug("chunk[%d]: addr=0x%06x len=%d\n",
+ i, clist[i].addr, clist[i].len);
+ }
+
+ /* Copy srec data to chunks */
+ for (i = 0; i < ns3data; i++) {
+ s3start = s3data[i].addr;
+ s3end = s3start + s3data[i].len - 1;
+ for (j = 0; j < *ccnt; j++) {
+ cstart = clist[j].addr;
+ cend = cstart + clist[j].len - 1;
+ if (s3start >= cstart && s3end <= cend)
+ break;
+ }
+ if (((unsigned int)j) >= (*ccnt)) {
+ pr_err("s3rec(a=0x%06x,l=%d), no chunk match, exiting.\n",
+ s3start, s3data[i].len);
+ return 1;
+ }
+ coffset = s3start - cstart;
+ memcpy(clist[j].data + coffset, s3data[i].data, s3data[i].len);
+ }
+
+ return result;
+}
+
+/*----------------------------------------------------------------
+* mkpdrlist
+*
+* Reads a raw PDA and builds an array of pdrec_t structures.
+*
+* Arguments:
+* pda buffer containing raw PDA bytes
+* pdrec ptr to an array of pdrec_t's. Will be filled on exit.
+* nrec ptr to a variable that will contain the count of PDRs
+*
+* Returns:
+* 0 - success
+* ~0 - failure (probably an errno)
+----------------------------------------------------------------*/
+static int mkpdrlist(struct pda *pda)
+{
+ int result = 0;
+ u16 *pda16 = (u16 *) pda->buf;
+ int curroff; /* in 'words' */
+
+ pda->nrec = 0;
+ curroff = 0;
+ while (curroff < (HFA384x_PDA_LEN_MAX / 2) &&
+ le16_to_cpu(pda16[curroff + 1]) != HFA384x_PDR_END_OF_PDA) {
+ pda->rec[pda->nrec] = (hfa384x_pdrec_t *) &(pda16[curroff]);
+
+ if (le16_to_cpu(pda->rec[pda->nrec]->code) ==
+ HFA384x_PDR_NICID) {
+ memcpy(&nicid, &pda->rec[pda->nrec]->data.nicid,
+ sizeof(nicid));
+ nicid.id = le16_to_cpu(nicid.id);
+ nicid.variant = le16_to_cpu(nicid.variant);
+ nicid.major = le16_to_cpu(nicid.major);
+ nicid.minor = le16_to_cpu(nicid.minor);
+ }
+ if (le16_to_cpu(pda->rec[pda->nrec]->code) ==
+ HFA384x_PDR_MFISUPRANGE) {
+ memcpy(&rfid, &pda->rec[pda->nrec]->data.mfisuprange,
+ sizeof(rfid));
+ rfid.id = le16_to_cpu(rfid.id);
+ rfid.variant = le16_to_cpu(rfid.variant);
+ rfid.bottom = le16_to_cpu(rfid.bottom);
+ rfid.top = le16_to_cpu(rfid.top);
+ }
+ if (le16_to_cpu(pda->rec[pda->nrec]->code) ==
+ HFA384x_PDR_CFISUPRANGE) {
+ memcpy(&macid, &pda->rec[pda->nrec]->data.cfisuprange,
+ sizeof(macid));
+ macid.id = le16_to_cpu(macid.id);
+ macid.variant = le16_to_cpu(macid.variant);
+ macid.bottom = le16_to_cpu(macid.bottom);
+ macid.top = le16_to_cpu(macid.top);
+ }
+
+ (pda->nrec)++;
+ curroff += le16_to_cpu(pda16[curroff]) + 1;
+
+ }
+ if (curroff >= (HFA384x_PDA_LEN_MAX / 2)) {
+ pr_err("no end record found or invalid lengths in PDR data, exiting. %x %d\n",
+ curroff, pda->nrec);
+ return 1;
+ }
+ if (le16_to_cpu(pda16[curroff + 1]) == HFA384x_PDR_END_OF_PDA) {
+ pda->rec[pda->nrec] = (hfa384x_pdrec_t *) &(pda16[curroff]);
+ (pda->nrec)++;
+ }
+ return result;
+}
+
+/*----------------------------------------------------------------
+* plugimage
+*
+* Plugs the given image using the given plug records from the given
+* PDA and filename.
+*
+* Arguments:
+* fchunk Array of image chunks
+* nfchunks Number of image chunks
+* s3plug Array of plug records
+* ns3plug Number of plug records
+* pda Current pda data
+*
+* Returns:
+* 0 success
+* ~0 failure
+----------------------------------------------------------------*/
+static int plugimage(struct imgchunk *fchunk, unsigned int nfchunks,
+ struct s3plugrec *s3plug, unsigned int ns3plug, struct pda *pda)
+{
+ int result = 0;
+ int i; /* plug index */
+ int j; /* index of PDR or -1 if fname plug */
+ int c; /* chunk index */
+ u32 pstart;
+ u32 pend;
+ u32 cstart = 0;
+ u32 cend;
+ u32 chunkoff;
+ u8 *dest;
+
+ /* for each plug record */
+ for (i = 0; i < ns3plug; i++) {
+ pstart = s3plug[i].addr;
+ pend = s3plug[i].addr + s3plug[i].len;
+ /* find the matching PDR (or filename) */
+ if (s3plug[i].itemcode != 0xffffffffUL) { /* not filename */
+ for (j = 0; j < pda->nrec; j++) {
+ if (s3plug[i].itemcode ==
+ le16_to_cpu(pda->rec[j]->code))
+ break;
+ }
+ } else {
+ j = -1;
+ }
+ if (j >= pda->nrec && j != -1) { /* if no matching PDR, fail */
+ pr_warn("warning: Failed to find PDR for plugrec 0x%04x.\n",
+ s3plug[i].itemcode);
+ continue; /* and move on to the next PDR */
+#if 0
+ /* MSM: They swear that unless it's the MAC address,
+ * the serial number, or the TX calibration records,
+ * then there's reasonable defaults in the f/w
+ * image. Therefore, missing PDRs in the card
+ * should only be a warning, not fatal.
+ * TODO: add fatals for the PDRs mentioned above.
+ */
+ result = 1;
+ continue;
+#endif
+ }
+
+ /* Validate plug len against PDR len */
+ if (j != -1 && s3plug[i].len < le16_to_cpu(pda->rec[j]->len)) {
+ pr_err("error: Plug vs. PDR len mismatch for plugrec 0x%04x, abort plugging.\n",
+ s3plug[i].itemcode);
+ result = 1;
+ continue;
+ }
+
+ /* Validate plug address against chunk data and identify chunk */
+ for (c = 0; c < nfchunks; c++) {
+ cstart = fchunk[c].addr;
+ cend = fchunk[c].addr + fchunk[c].len;
+ if (pstart >= cstart && pend <= cend)
+ break;
+ }
+ if (c >= nfchunks) {
+ pr_err("error: Failed to find image chunk for plugrec 0x%04x.\n",
+ s3plug[i].itemcode);
+ result = 1;
+ continue;
+ }
+
+ /* Plug data */
+ chunkoff = pstart - cstart;
+ dest = fchunk[c].data + chunkoff;
+ pr_debug("Plugging item 0x%04x @ 0x%06x, len=%d, cnum=%d coff=0x%06x\n",
+ s3plug[i].itemcode, pstart, s3plug[i].len,
+ c, chunkoff);
+
+ if (j == -1) { /* plug the filename */
+ memset(dest, 0, s3plug[i].len);
+ strncpy(dest, PRISM2_USB_FWFILE, s3plug[i].len - 1);
+ } else { /* plug a PDR */
+ memcpy(dest, &(pda->rec[j]->data), s3plug[i].len);
+ }
+ }
+ return result;
+
+}
+
+/*----------------------------------------------------------------
+* read_cardpda
+*
+* Sends the command for the driver to read the pda from the card
+* named in the device variable. Upon success, the card pda is
+* stored in the "cardpda" variables. Note that the pda structure
+* is considered 'well formed' after this function. That means
+* that the nrecs is valid, the rec array has been set up, and there's
+* a valid PDAEND record in the raw PDA data.
+*
+* Arguments:
+* pda pda structure
+* wlandev device
+*
+* Returns:
+* 0 - success
+* ~0 - failure (probably an errno)
+----------------------------------------------------------------*/
+static int read_cardpda(struct pda *pda, wlandevice_t *wlandev)
+{
+ int result = 0;
+ struct p80211msg_p2req_readpda *msg;
+
+ msg = kzalloc(sizeof(*msg), GFP_KERNEL);
+ if (!msg)
+ return -ENOMEM;
+
+ /* set up the msg */
+ msg->msgcode = DIDmsg_p2req_readpda;
+ msg->msglen = sizeof(msg);
+ strcpy(msg->devname, wlandev->name);
+ msg->pda.did = DIDmsg_p2req_readpda_pda;
+ msg->pda.len = HFA384x_PDA_LEN_MAX;
+ msg->pda.status = P80211ENUM_msgitem_status_no_value;
+ msg->resultcode.did = DIDmsg_p2req_readpda_resultcode;
+ msg->resultcode.len = sizeof(u32);
+ msg->resultcode.status = P80211ENUM_msgitem_status_no_value;
+
+ if (prism2mgmt_readpda(wlandev, msg) != 0) {
+ /* prism2mgmt_readpda prints an errno if appropriate */
+ result = -1;
+ } else if (msg->resultcode.data == P80211ENUM_resultcode_success) {
+ memcpy(pda->buf, msg->pda.data, HFA384x_PDA_LEN_MAX);
+ result = mkpdrlist(pda);
+ } else {
+ /* resultcode must've been something other than success */
+ result = -1;
+ }
+
+ kfree(msg);
+ return result;
+}
+
+/*----------------------------------------------------------------
+* read_fwfile
+*
+* Reads the given fw file which should have been compiled from an srec
+* file. Each record in the fw file will either be a plain data record,
+* a start address record, or other records used for plugging.
+*
+* Note that data records are expected to be sorted into
+* ascending address order in the fw file.
+*
+* Note also that the start address record, originally an S7 record in
+* the srec file, is expected in the fw file to be like a data record but
+* with a certain address to make it identifiable.
+*
+* Here's the SREC format that the fw should have come from:
+* S[37]nnaaaaaaaaddd...dddcc
+*
+* nn - number of bytes starting with the address field
+* aaaaaaaa - address in readable (or big endian) format
+* dd....dd - 0-245 data bytes (two chars per byte)
+* cc - checksum
+*
+* The S7 record's (there should be only one) address value gets
+* converted to an S3 record with address of 0xff400000, with the
+* start address being stored as a 4 byte data word. That address is
+* the start execution address used for RAM downloads.
+*
+* The S3 records have a collection of subformats indicated by the
+* value of aaaaaaaa:
+* 0xff000000 - Plug record, data field format:
+* xxxxxxxxaaaaaaaassssssss
+* x - PDR code number (little endian)
+* a - Address in load image to plug (little endian)
+* s - Length of plug data area (little endian)
+*
+* 0xff100000 - CRC16 generation record, data field format:
+* aaaaaaaassssssssbbbbbbbb
+* a - Start address for CRC calculation (little endian)
+* s - Length of data to calculate over (little endian)
+* b - Boolean, true=write crc, false=don't write
+*
+* 0xff200000 - Info record, data field format:
+* ssssttttdd..dd
+* s - Size in words (little endian)
+* t - Info type (little endian), see #defines and
+* struct s3inforec for details about types.
+* d - (s - 1) little endian words giving the contents of
+* the given info type.
+*
+* 0xff400000 - Start address record, data field format:
+* aaaaaaaa
+* a - Address in load image to plug (little endian)
+*
+* Arguments:
+* record firmware image (ihex record structure) in kernel memory
+*
+* Returns:
+* 0 - success
+* ~0 - failure (probably an errno)
+----------------------------------------------------------------*/
+static int read_fwfile(const struct ihex_binrec *record)
+{
+ int i;
+ int rcnt = 0;
+ u16 *tmpinfo;
+ u16 *ptr16;
+ u32 *ptr32, len, addr;
+
+ pr_debug("Reading fw file ...\n");
+
+ while (record) {
+
+ rcnt++;
+
+ len = be16_to_cpu(record->len);
+ addr = be32_to_cpu(record->addr);
+
+ /* Point into data for different word lengths */
+ ptr32 = (u32 *) record->data;
+ ptr16 = (u16 *) record->data;
+
+ /* parse what was an S3 srec and put it in the right array */
+ switch (addr) {
+ case S3ADDR_START:
+ startaddr = *ptr32;
+ pr_debug(" S7 start addr, record=%d addr=0x%08x\n",
+ rcnt,
+ startaddr);
+ break;
+ case S3ADDR_PLUG:
+ s3plug[ns3plug].itemcode = *ptr32;
+ s3plug[ns3plug].addr = *(ptr32 + 1);
+ s3plug[ns3plug].len = *(ptr32 + 2);
+
+ pr_debug(" S3 plugrec, record=%d itemcode=0x%08x addr=0x%08x len=%d\n",
+ rcnt,
+ s3plug[ns3plug].itemcode,
+ s3plug[ns3plug].addr,
+ s3plug[ns3plug].len);
+
+ ns3plug++;
+ if (ns3plug == S3PLUG_MAX) {
+ pr_err("S3 plugrec limit reached - aborting\n");
+ return 1;
+ }
+ break;
+ case S3ADDR_CRC:
+ s3crc[ns3crc].addr = *ptr32;
+ s3crc[ns3crc].len = *(ptr32 + 1);
+ s3crc[ns3crc].dowrite = *(ptr32 + 2);
+
+ pr_debug(" S3 crcrec, record=%d addr=0x%08x len=%d write=0x%08x\n",
+ rcnt,
+ s3crc[ns3crc].addr,
+ s3crc[ns3crc].len,
+ s3crc[ns3crc].dowrite);
+ ns3crc++;
+ if (ns3crc == S3CRC_MAX) {
+ pr_err("S3 crcrec limit reached - aborting\n");
+ return 1;
+ }
+ break;
+ case S3ADDR_INFO:
+ s3info[ns3info].len = *ptr16;
+ s3info[ns3info].type = *(ptr16 + 1);
+
+ pr_debug(" S3 inforec, record=%d len=0x%04x type=0x%04x\n",
+ rcnt,
+ s3info[ns3info].len,
+ s3info[ns3info].type);
+ if (((s3info[ns3info].len - 1) * sizeof(u16)) > sizeof(s3info[ns3info].info)) {
+ pr_err("S3 inforec length too long - aborting\n");
+ return 1;
+ }
+
+ tmpinfo = (u16 *)&(s3info[ns3info].info.version);
+ pr_debug(" info=");
+ for (i = 0; i < s3info[ns3info].len - 1; i++) {
+ tmpinfo[i] = *(ptr16 + 2 + i);
+ pr_debug("%04x ", tmpinfo[i]);
+ }
+ pr_debug("\n");
+
+ ns3info++;
+ if (ns3info == S3INFO_MAX) {
+ pr_err("S3 inforec limit reached - aborting\n");
+ return 1;
+ }
+ break;
+ default: /* Data record */
+ s3data[ns3data].addr = addr;
+ s3data[ns3data].len = len;
+ s3data[ns3data].data = (uint8_t *) record->data;
+ ns3data++;
+ if (ns3data == S3DATA_MAX) {
+ pr_err("S3 datarec limit reached - aborting\n");
+ return 1;
+ }
+ break;
+ }
+ record = ihex_next_binrec(record);
+ }
+ return 0;
+}
+
+/*----------------------------------------------------------------
+* writeimage
+*
+* Takes the chunks, builds p80211 messages and sends them down
+* to the driver for writing to the card.
+*
+* Arguments:
+* wlandev device
+* fchunk Array of image chunks
+* nfchunks Number of image chunks
+*
+* Returns:
+* 0 success
+* ~0 failure
+----------------------------------------------------------------*/
+static int writeimage(wlandevice_t *wlandev, struct imgchunk *fchunk,
+ unsigned int nfchunks)
+{
+ int result = 0;
+ struct p80211msg_p2req_ramdl_state *rstmsg;
+ struct p80211msg_p2req_ramdl_write *rwrmsg;
+ u32 resultcode;
+ int i;
+ int j;
+ unsigned int nwrites;
+ u32 curroff;
+ u32 currlen;
+ u32 currdaddr;
+
+ rstmsg = kzalloc(sizeof(*rstmsg), GFP_KERNEL);
+ rwrmsg = kzalloc(sizeof(*rwrmsg), GFP_KERNEL);
+ if (!rstmsg || !rwrmsg) {
+ kfree(rstmsg);
+ kfree(rwrmsg);
+ netdev_err(wlandev->netdev,
+ "writeimage: no memory for firmware download, aborting download\n");
+ return -ENOMEM;
+ }
+
+ /* Initialize the messages */
+ strcpy(rstmsg->devname, wlandev->name);
+ rstmsg->msgcode = DIDmsg_p2req_ramdl_state;
+ rstmsg->msglen = sizeof(*rstmsg);
+ rstmsg->enable.did = DIDmsg_p2req_ramdl_state_enable;
+ rstmsg->exeaddr.did = DIDmsg_p2req_ramdl_state_exeaddr;
+ rstmsg->resultcode.did = DIDmsg_p2req_ramdl_state_resultcode;
+ rstmsg->enable.status = P80211ENUM_msgitem_status_data_ok;
+ rstmsg->exeaddr.status = P80211ENUM_msgitem_status_data_ok;
+ rstmsg->resultcode.status = P80211ENUM_msgitem_status_no_value;
+ rstmsg->enable.len = sizeof(u32);
+ rstmsg->exeaddr.len = sizeof(u32);
+ rstmsg->resultcode.len = sizeof(u32);
+
+ strcpy(rwrmsg->devname, wlandev->name);
+ rwrmsg->msgcode = DIDmsg_p2req_ramdl_write;
+ rwrmsg->msglen = sizeof(*rwrmsg);
+ rwrmsg->addr.did = DIDmsg_p2req_ramdl_write_addr;
+ rwrmsg->len.did = DIDmsg_p2req_ramdl_write_len;
+ rwrmsg->data.did = DIDmsg_p2req_ramdl_write_data;
+ rwrmsg->resultcode.did = DIDmsg_p2req_ramdl_write_resultcode;
+ rwrmsg->addr.status = P80211ENUM_msgitem_status_data_ok;
+ rwrmsg->len.status = P80211ENUM_msgitem_status_data_ok;
+ rwrmsg->data.status = P80211ENUM_msgitem_status_data_ok;
+ rwrmsg->resultcode.status = P80211ENUM_msgitem_status_no_value;
+ rwrmsg->addr.len = sizeof(u32);
+ rwrmsg->len.len = sizeof(u32);
+ rwrmsg->data.len = WRITESIZE_MAX;
+ rwrmsg->resultcode.len = sizeof(u32);
+
+ /* Send xxx_state(enable) */
+ pr_debug("Sending dl_state(enable) message.\n");
+ rstmsg->enable.data = P80211ENUM_truth_true;
+ rstmsg->exeaddr.data = startaddr;
+
+ result = prism2mgmt_ramdl_state(wlandev, rstmsg);
+ if (result) {
+ netdev_err(wlandev->netdev,
+ "writeimage state enable failed w/ result=%d, aborting download\n",
+ result);
+ goto free_result;
+ }
+ resultcode = rstmsg->resultcode.data;
+ if (resultcode != P80211ENUM_resultcode_success) {
+ netdev_err(wlandev->netdev,
+ "writeimage()->xxxdl_state msg indicates failure, w/ resultcode=%d, aborting download.\n",
+ resultcode);
+ result = 1;
+ goto free_result;
+ }
+
+ /* Now, loop through the data chunks and send WRITESIZE_MAX data */
+ for (i = 0; i < nfchunks; i++) {
+ nwrites = fchunk[i].len / WRITESIZE_MAX;
+ nwrites += (fchunk[i].len % WRITESIZE_MAX) ? 1 : 0;
+ curroff = 0;
+ for (j = 0; j < nwrites; j++) {
+ /* TODO Move this to a separate function */
+ int lenleft = fchunk[i].len - (WRITESIZE_MAX * j);
+
+ if (fchunk[i].len > WRITESIZE_MAX)
+ currlen = WRITESIZE_MAX;
+ else
+ currlen = lenleft;
+ curroff = j * WRITESIZE_MAX;
+ currdaddr = fchunk[i].addr + curroff;
+ /* Setup the message */
+ rwrmsg->addr.data = currdaddr;
+ rwrmsg->len.data = currlen;
+ memcpy(rwrmsg->data.data,
+ fchunk[i].data + curroff, currlen);
+
+ /* Send flashdl_write(pda) */
+ pr_debug
+ ("Sending xxxdl_write message addr=%06x len=%d.\n",
+ currdaddr, currlen);
+
+ result = prism2mgmt_ramdl_write(wlandev, rwrmsg);
+
+ /* Check the results */
+ if (result) {
+ netdev_err(wlandev->netdev,
+ "writeimage chunk write failed w/ result=%d, aborting download\n",
+ result);
+ goto free_result;
+ }
+ resultcode = rstmsg->resultcode.data;
+ if (resultcode != P80211ENUM_resultcode_success) {
+ pr_err("writeimage()->xxxdl_write msg indicates failure, w/ resultcode=%d, aborting download.\n",
+ resultcode);
+ result = 1;
+ goto free_result;
+ }
+
+ }
+ }
+
+ /* Send xxx_state(disable) */
+ pr_debug("Sending dl_state(disable) message.\n");
+ rstmsg->enable.data = P80211ENUM_truth_false;
+ rstmsg->exeaddr.data = 0;
+
+ result = prism2mgmt_ramdl_state(wlandev, rstmsg);
+ if (result) {
+ netdev_err(wlandev->netdev,
+ "writeimage state disable failed w/ result=%d, aborting download\n",
+ result);
+ goto free_result;
+ }
+ resultcode = rstmsg->resultcode.data;
+ if (resultcode != P80211ENUM_resultcode_success) {
+ netdev_err(wlandev->netdev,
+ "writeimage()->xxxdl_state msg indicates failure, w/ resultcode=%d, aborting download.\n",
+ resultcode);
+ result = 1;
+ goto free_result;
+ }
+
+free_result:
+ kfree(rstmsg);
+ kfree(rwrmsg);
+ return result;
+}
+
+static int validate_identity(void)
+{
+ int i;
+ int result = 1;
+ int trump = 0;
+
+ pr_debug("NIC ID: %#x v%d.%d.%d\n",
+ nicid.id, nicid.major, nicid.minor, nicid.variant);
+ pr_debug("MFI ID: %#x v%d %d->%d\n",
+ rfid.id, rfid.variant, rfid.bottom, rfid.top);
+ pr_debug("CFI ID: %#x v%d %d->%d\n",
+ macid.id, macid.variant, macid.bottom, macid.top);
+ pr_debug("PRI ID: %#x v%d %d->%d\n",
+ priid.id, priid.variant, priid.bottom, priid.top);
+
+ for (i = 0; i < ns3info; i++) {
+ switch (s3info[i].type) {
+ case 1:
+ pr_debug("Version: ID %#x %d.%d.%d\n",
+ s3info[i].info.version.id,
+ s3info[i].info.version.major,
+ s3info[i].info.version.minor,
+ s3info[i].info.version.variant);
+ break;
+ case 2:
+ pr_debug("Compat: Role %#x Id %#x v%d %d->%d\n",
+ s3info[i].info.compat.role,
+ s3info[i].info.compat.id,
+ s3info[i].info.compat.variant,
+ s3info[i].info.compat.bottom,
+ s3info[i].info.compat.top);
+
+ /* MAC compat range */
+ if ((s3info[i].info.compat.role == 1) &&
+ (s3info[i].info.compat.id == 2)) {
+ if (s3info[i].info.compat.variant !=
+ macid.variant) {
+ result = 2;
+ }
+ }
+
+ /* PRI compat range */
+ if ((s3info[i].info.compat.role == 1) &&
+ (s3info[i].info.compat.id == 3)) {
+ if ((s3info[i].info.compat.bottom > priid.top)
+ || (s3info[i].info.compat.top <
+ priid.bottom)) {
+ result = 3;
+ }
+ }
+ /* SEC compat range */
+ if ((s3info[i].info.compat.role == 1) &&
+ (s3info[i].info.compat.id == 4)) {
+ /* FIXME: isn't something missing here? */
+ }
+
+ break;
+ case 3:
+ pr_debug("Seq: %#x\n", s3info[i].info.buildseq);
+
+ break;
+ case 4:
+ pr_debug("Platform: ID %#x %d.%d.%d\n",
+ s3info[i].info.version.id,
+ s3info[i].info.version.major,
+ s3info[i].info.version.minor,
+ s3info[i].info.version.variant);
+
+ if (nicid.id != s3info[i].info.version.id)
+ continue;
+ if (nicid.major != s3info[i].info.version.major)
+ continue;
+ if (nicid.minor != s3info[i].info.version.minor)
+ continue;
+ if ((nicid.variant != s3info[i].info.version.variant) &&
+ (nicid.id != 0x8008))
+ continue;
+
+ trump = 1;
+ break;
+ case 0x8001:
+ pr_debug("name inforec len %d\n", s3info[i].len);
+
+ break;
+ default:
+ pr_debug("Unknown inforec type %d\n", s3info[i].type);
+ }
+ }
+ /* walk through */
+
+ if (trump && (result != 2))
+ result = 0;
+ return result;
+}
diff --git a/drivers/staging/wlan-ng/prism2mgmt.c b/drivers/staging/wlan-ng/prism2mgmt.c
new file mode 100644
index 000000000..013a6240f
--- /dev/null
+++ b/drivers/staging/wlan-ng/prism2mgmt.c
@@ -0,0 +1,1322 @@
+/* src/prism2/driver/prism2mgmt.c
+*
+* Management request handler functions.
+*
+* Copyright (C) 1999 AbsoluteValue Systems, Inc. All Rights Reserved.
+* --------------------------------------------------------------------
+*
+* linux-wlan
+*
+* The contents of this file are subject to the Mozilla Public
+* License Version 1.1 (the "License"); you may not use this file
+* except in compliance with the License. You may obtain a copy of
+* the License at http://www.mozilla.org/MPL/
+*
+* Software distributed under the License is distributed on an "AS
+* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+* implied. See the License for the specific language governing
+* rights and limitations under the License.
+*
+* Alternatively, the contents of this file may be used under the
+* terms of the GNU Public License version 2 (the "GPL"), in which
+* case the provisions of the GPL are applicable instead of the
+* above. If you wish to allow the use of your version of this file
+* only under the terms of the GPL and not to allow others to use
+* your version of this file under the MPL, indicate your decision
+* by deleting the provisions above and replace them with the notice
+* and other provisions required by the GPL. If you do not delete
+* the provisions above, a recipient may use your version of this
+* file under either the MPL or the GPL.
+*
+* --------------------------------------------------------------------
+*
+* Inquiries regarding the linux-wlan Open Source project can be
+* made directly to:
+*
+* AbsoluteValue Systems Inc.
+* info@linux-wlan.com
+* http://www.linux-wlan.com
+*
+* --------------------------------------------------------------------
+*
+* Portions of the development of this software were funded by
+* Intersil Corporation as part of PRISM(R) chipset product development.
+*
+* --------------------------------------------------------------------
+*
+* The functions in this file handle management requests sent from
+* user mode.
+*
+* Most of these functions have two separate blocks of code that are
+* conditional on whether this is a station or an AP. This is used
+* to separate out the STA and AP responses to these management primitives.
+* It's a choice (good, bad, indifferent?) to have the code in the same
+* place so it's clear that the same primitive is implemented in both
+* cases but has different behavior.
+*
+* --------------------------------------------------------------------
+*/
+
+#include <linux/if_arp.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/wait.h>
+#include <linux/sched.h>
+#include <linux/types.h>
+#include <linux/wireless.h>
+#include <linux/netdevice.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <asm/byteorder.h>
+#include <linux/random.h>
+#include <linux/usb.h>
+#include <linux/bitops.h>
+
+#include "p80211types.h"
+#include "p80211hdr.h"
+#include "p80211mgmt.h"
+#include "p80211conv.h"
+#include "p80211msg.h"
+#include "p80211netdev.h"
+#include "p80211metadef.h"
+#include "p80211metastruct.h"
+#include "hfa384x.h"
+#include "prism2mgmt.h"
+
+/* Converts 802.11 format rate specifications to prism2 */
+#define p80211rate_to_p2bit(n) ((((n)&~BIT(7)) == 2) ? BIT(0) : \
+ (((n)&~BIT(7)) == 4) ? BIT(1) : \
+ (((n)&~BIT(7)) == 11) ? BIT(2) : \
+ (((n)&~BIT(7)) == 22) ? BIT(3) : 0)
+
+/*----------------------------------------------------------------
+* prism2mgmt_scan
+*
+* Initiate a scan for BSSs.
+*
+* This function corresponds to MLME-scan.request and part of
+* MLME-scan.confirm. As far as I can tell in the standard, there
+* are no restrictions on when a scan.request may be issued. We have
+* to handle in whatever state the driver/MAC happen to be.
+*
+* Arguments:
+* wlandev wlan device structure
+* msgp ptr to msg buffer
+*
+* Returns:
+* 0 success and done
+* <0 success, but we're waiting for something to finish.
+* >0 an error occurred while handling the message.
+* Side effects:
+*
+* Call context:
+* process thread (usually)
+* interrupt
+----------------------------------------------------------------*/
+int prism2mgmt_scan(wlandevice_t *wlandev, void *msgp)
+{
+ int result = 0;
+ hfa384x_t *hw = wlandev->priv;
+ struct p80211msg_dot11req_scan *msg = msgp;
+ u16 roamingmode, word;
+ int i, timeout;
+ int istmpenable = 0;
+
+ hfa384x_HostScanRequest_data_t scanreq;
+
+ /* gatekeeper check */
+ if (HFA384x_FIRMWARE_VERSION(hw->ident_sta_fw.major,
+ hw->ident_sta_fw.minor,
+ hw->ident_sta_fw.variant) <
+ HFA384x_FIRMWARE_VERSION(1, 3, 2)) {
+ netdev_err(wlandev->netdev,
+ "HostScan not supported with current firmware (<1.3.2).\n");
+ result = 1;
+ msg->resultcode.data = P80211ENUM_resultcode_not_supported;
+ goto exit;
+ }
+
+ memset(&scanreq, 0, sizeof(scanreq));
+
+ /* save current roaming mode */
+ result = hfa384x_drvr_getconfig16(hw,
+ HFA384x_RID_CNFROAMINGMODE,
+ &roamingmode);
+ if (result) {
+ netdev_err(wlandev->netdev,
+ "getconfig(ROAMMODE) failed. result=%d\n", result);
+ msg->resultcode.data =
+ P80211ENUM_resultcode_implementation_failure;
+ goto exit;
+ }
+
+ /* drop into mode 3 for the scan */
+ result = hfa384x_drvr_setconfig16(hw,
+ HFA384x_RID_CNFROAMINGMODE,
+ HFA384x_ROAMMODE_HOSTSCAN_HOSTROAM);
+ if (result) {
+ netdev_err(wlandev->netdev,
+ "setconfig(ROAMINGMODE) failed. result=%d\n",
+ result);
+ msg->resultcode.data =
+ P80211ENUM_resultcode_implementation_failure;
+ goto exit;
+ }
+
+ /* active or passive? */
+ if (HFA384x_FIRMWARE_VERSION(hw->ident_sta_fw.major,
+ hw->ident_sta_fw.minor,
+ hw->ident_sta_fw.variant) >
+ HFA384x_FIRMWARE_VERSION(1, 5, 0)) {
+ if (msg->scantype.data != P80211ENUM_scantype_active)
+ word = cpu_to_le16(msg->maxchanneltime.data);
+ else
+ word = 0;
+
+ result =
+ hfa384x_drvr_setconfig16(hw, HFA384x_RID_CNFPASSIVESCANCTRL,
+ word);
+ if (result) {
+ netdev_warn(wlandev->netdev,
+ "Passive scan not supported with current firmware. (<1.5.1)\n");
+ }
+ }
+
+ /* set up the txrate to be 2MBPS. Should be fastest basicrate... */
+ word = HFA384x_RATEBIT_2;
+ scanreq.txRate = cpu_to_le16(word);
+
+ /* set up the channel list */
+ word = 0;
+ for (i = 0; i < msg->channellist.data.len; i++) {
+ u8 channel = msg->channellist.data.data[i];
+
+ if (channel > 14)
+ continue;
+ /* channel 1 is BIT 0 ... channel 14 is BIT 13 */
+ word |= (1 << (channel - 1));
+ }
+ scanreq.channelList = cpu_to_le16(word);
+
+ /* set up the ssid, if present. */
+ scanreq.ssid.len = cpu_to_le16(msg->ssid.data.len);
+ memcpy(scanreq.ssid.data, msg->ssid.data.data, msg->ssid.data.len);
+
+ /* Enable the MAC port if it's not already enabled */
+ result = hfa384x_drvr_getconfig16(hw, HFA384x_RID_PORTSTATUS, &word);
+ if (result) {
+ netdev_err(wlandev->netdev,
+ "getconfig(PORTSTATUS) failed. result=%d\n", result);
+ msg->resultcode.data =
+ P80211ENUM_resultcode_implementation_failure;
+ goto exit;
+ }
+ if (word == HFA384x_PORTSTATUS_DISABLED) {
+ u16 wordbuf[17];
+
+ result = hfa384x_drvr_setconfig16(hw,
+ HFA384x_RID_CNFROAMINGMODE,
+ HFA384x_ROAMMODE_HOSTSCAN_HOSTROAM);
+ if (result) {
+ netdev_err(wlandev->netdev,
+ "setconfig(ROAMINGMODE) failed. result=%d\n",
+ result);
+ msg->resultcode.data =
+ P80211ENUM_resultcode_implementation_failure;
+ goto exit;
+ }
+ /* Construct a bogus SSID and assign it to OwnSSID and
+ * DesiredSSID
+ */
+ wordbuf[0] = cpu_to_le16(WLAN_SSID_MAXLEN);
+ get_random_bytes(&wordbuf[1], WLAN_SSID_MAXLEN);
+ result = hfa384x_drvr_setconfig(hw, HFA384x_RID_CNFOWNSSID,
+ wordbuf,
+ HFA384x_RID_CNFOWNSSID_LEN);
+ if (result) {
+ netdev_err(wlandev->netdev, "Failed to set OwnSSID.\n");
+ msg->resultcode.data =
+ P80211ENUM_resultcode_implementation_failure;
+ goto exit;
+ }
+ result = hfa384x_drvr_setconfig(hw, HFA384x_RID_CNFDESIREDSSID,
+ wordbuf,
+ HFA384x_RID_CNFDESIREDSSID_LEN);
+ if (result) {
+ netdev_err(wlandev->netdev,
+ "Failed to set DesiredSSID.\n");
+ msg->resultcode.data =
+ P80211ENUM_resultcode_implementation_failure;
+ goto exit;
+ }
+ /* bsstype */
+ result = hfa384x_drvr_setconfig16(hw,
+ HFA384x_RID_CNFPORTTYPE,
+ HFA384x_PORTTYPE_IBSS);
+ if (result) {
+ netdev_err(wlandev->netdev,
+ "Failed to set CNFPORTTYPE.\n");
+ msg->resultcode.data =
+ P80211ENUM_resultcode_implementation_failure;
+ goto exit;
+ }
+ /* ibss options */
+ result = hfa384x_drvr_setconfig16(hw,
+ HFA384x_RID_CREATEIBSS,
+ HFA384x_CREATEIBSS_JOINCREATEIBSS);
+ if (result) {
+ netdev_err(wlandev->netdev,
+ "Failed to set CREATEIBSS.\n");
+ msg->resultcode.data =
+ P80211ENUM_resultcode_implementation_failure;
+ goto exit;
+ }
+ result = hfa384x_drvr_enable(hw, 0);
+ if (result) {
+ netdev_err(wlandev->netdev,
+ "drvr_enable(0) failed. result=%d\n",
+ result);
+ msg->resultcode.data =
+ P80211ENUM_resultcode_implementation_failure;
+ goto exit;
+ }
+ istmpenable = 1;
+ }
+
+ /* Figure out our timeout first Kus, then HZ */
+ timeout = msg->channellist.data.len * msg->maxchanneltime.data;
+ timeout = (timeout * HZ) / 1000;
+
+ /* Issue the scan request */
+ hw->scanflag = 0;
+
+ result = hfa384x_drvr_setconfig(hw,
+ HFA384x_RID_HOSTSCAN, &scanreq,
+ sizeof(hfa384x_HostScanRequest_data_t));
+ if (result) {
+ netdev_err(wlandev->netdev,
+ "setconfig(SCANREQUEST) failed. result=%d\n",
+ result);
+ msg->resultcode.data =
+ P80211ENUM_resultcode_implementation_failure;
+ goto exit;
+ }
+
+ /* sleep until info frame arrives */
+ wait_event_interruptible_timeout(hw->cmdq, hw->scanflag, timeout);
+
+ msg->numbss.status = P80211ENUM_msgitem_status_data_ok;
+ if (hw->scanflag == -1)
+ hw->scanflag = 0;
+
+ msg->numbss.data = hw->scanflag;
+
+ hw->scanflag = 0;
+
+ /* Disable port if we temporarily enabled it. */
+ if (istmpenable) {
+ result = hfa384x_drvr_disable(hw, 0);
+ if (result) {
+ netdev_err(wlandev->netdev,
+ "drvr_disable(0) failed. result=%d\n",
+ result);
+ msg->resultcode.data =
+ P80211ENUM_resultcode_implementation_failure;
+ goto exit;
+ }
+ }
+
+ /* restore original roaming mode */
+ result = hfa384x_drvr_setconfig16(hw, HFA384x_RID_CNFROAMINGMODE,
+ roamingmode);
+ if (result) {
+ netdev_err(wlandev->netdev,
+ "setconfig(ROAMMODE) failed. result=%d\n", result);
+ msg->resultcode.data =
+ P80211ENUM_resultcode_implementation_failure;
+ goto exit;
+ }
+
+ result = 0;
+ msg->resultcode.data = P80211ENUM_resultcode_success;
+
+exit:
+ msg->resultcode.status = P80211ENUM_msgitem_status_data_ok;
+
+ return result;
+}
+
+/*----------------------------------------------------------------
+* prism2mgmt_scan_results
+*
+* Retrieve the BSS description for one of the BSSs identified in
+* a scan.
+*
+* Arguments:
+* wlandev wlan device structure
+* msgp ptr to msg buffer
+*
+* Returns:
+* 0 success and done
+* <0 success, but we're waiting for something to finish.
+* >0 an error occurred while handling the message.
+* Side effects:
+*
+* Call context:
+* process thread (usually)
+* interrupt
+----------------------------------------------------------------*/
+int prism2mgmt_scan_results(wlandevice_t *wlandev, void *msgp)
+{
+ int result = 0;
+ struct p80211msg_dot11req_scan_results *req;
+ hfa384x_t *hw = wlandev->priv;
+ hfa384x_HScanResultSub_t *item = NULL;
+
+ int count;
+
+ req = (struct p80211msg_dot11req_scan_results *) msgp;
+
+ req->resultcode.status = P80211ENUM_msgitem_status_data_ok;
+
+ if (!hw->scanresults) {
+ netdev_err(wlandev->netdev,
+ "dot11req_scan_results can only be used after a successful dot11req_scan.\n");
+ result = 2;
+ req->resultcode.data = P80211ENUM_resultcode_invalid_parameters;
+ goto exit;
+ }
+
+ count = (hw->scanresults->framelen - 3) / 32;
+ if (count > HFA384x_SCANRESULT_MAX)
+ count = HFA384x_SCANRESULT_MAX;
+
+ if (req->bssindex.data >= count) {
+ pr_debug("requested index (%d) out of range (%d)\n",
+ req->bssindex.data, count);
+ result = 2;
+ req->resultcode.data = P80211ENUM_resultcode_invalid_parameters;
+ goto exit;
+ }
+
+ item = &(hw->scanresults->info.hscanresult.result[req->bssindex.data]);
+ /* signal and noise */
+ req->signal.status = P80211ENUM_msgitem_status_data_ok;
+ req->noise.status = P80211ENUM_msgitem_status_data_ok;
+ req->signal.data = le16_to_cpu(item->sl);
+ req->noise.data = le16_to_cpu(item->anl);
+
+ /* BSSID */
+ req->bssid.status = P80211ENUM_msgitem_status_data_ok;
+ req->bssid.data.len = WLAN_BSSID_LEN;
+ memcpy(req->bssid.data.data, item->bssid, WLAN_BSSID_LEN);
+
+ /* SSID */
+ req->ssid.status = P80211ENUM_msgitem_status_data_ok;
+ req->ssid.data.len = le16_to_cpu(item->ssid.len);
+ req->ssid.data.len = min_t(u16, req->ssid.data.len, WLAN_SSID_MAXLEN);
+ memcpy(req->ssid.data.data, item->ssid.data, req->ssid.data.len);
+
+ /* supported rates */
+ for (count = 0; count < 10; count++)
+ if (item->supprates[count] == 0)
+ break;
+
+#define REQBASICRATE(N) \
+ do { \
+ if ((count >= N) && DOT11_RATE5_ISBASIC_GET( \
+ item->supprates[(N)-1])) { \
+ req->basicrate ## N .data = item->supprates[(N)-1]; \
+ req->basicrate ## N .status = \
+ P80211ENUM_msgitem_status_data_ok; \
+ } \
+ } while (0)
+
+ REQBASICRATE(1);
+ REQBASICRATE(2);
+ REQBASICRATE(3);
+ REQBASICRATE(4);
+ REQBASICRATE(5);
+ REQBASICRATE(6);
+ REQBASICRATE(7);
+ REQBASICRATE(8);
+
+#define REQSUPPRATE(N) \
+ do { \
+ if (count >= N) { \
+ req->supprate ## N .data = item->supprates[(N)-1]; \
+ req->supprate ## N .status = \
+ P80211ENUM_msgitem_status_data_ok; \
+ } \
+ } while (0)
+
+ REQSUPPRATE(1);
+ REQSUPPRATE(2);
+ REQSUPPRATE(3);
+ REQSUPPRATE(4);
+ REQSUPPRATE(5);
+ REQSUPPRATE(6);
+ REQSUPPRATE(7);
+ REQSUPPRATE(8);
+
+ /* beacon period */
+ req->beaconperiod.status = P80211ENUM_msgitem_status_data_ok;
+ req->beaconperiod.data = le16_to_cpu(item->bcnint);
+
+ /* timestamps */
+ req->timestamp.status = P80211ENUM_msgitem_status_data_ok;
+ req->timestamp.data = jiffies;
+ req->localtime.status = P80211ENUM_msgitem_status_data_ok;
+ req->localtime.data = jiffies;
+
+ /* atim window */
+ req->ibssatimwindow.status = P80211ENUM_msgitem_status_data_ok;
+ req->ibssatimwindow.data = le16_to_cpu(item->atim);
+
+ /* Channel */
+ req->dschannel.status = P80211ENUM_msgitem_status_data_ok;
+ req->dschannel.data = le16_to_cpu(item->chid);
+
+ /* capinfo bits */
+ count = le16_to_cpu(item->capinfo);
+ req->capinfo.status = P80211ENUM_msgitem_status_data_ok;
+ req->capinfo.data = count;
+
+ /* privacy flag */
+ req->privacy.status = P80211ENUM_msgitem_status_data_ok;
+ req->privacy.data = WLAN_GET_MGMT_CAP_INFO_PRIVACY(count);
+
+ /* cfpollable */
+ req->cfpollable.status = P80211ENUM_msgitem_status_data_ok;
+ req->cfpollable.data = WLAN_GET_MGMT_CAP_INFO_CFPOLLABLE(count);
+
+ /* cfpollreq */
+ req->cfpollreq.status = P80211ENUM_msgitem_status_data_ok;
+ req->cfpollreq.data = WLAN_GET_MGMT_CAP_INFO_CFPOLLREQ(count);
+
+ /* bsstype */
+ req->bsstype.status = P80211ENUM_msgitem_status_data_ok;
+ req->bsstype.data = (WLAN_GET_MGMT_CAP_INFO_ESS(count)) ?
+ P80211ENUM_bsstype_infrastructure : P80211ENUM_bsstype_independent;
+
+ result = 0;
+ req->resultcode.data = P80211ENUM_resultcode_success;
+
+exit:
+ return result;
+}
+
+/*----------------------------------------------------------------
+* prism2mgmt_start
+*
+* Start a BSS. Any station can do this for IBSS, only AP for ESS.
+*
+* Arguments:
+* wlandev wlan device structure
+* msgp ptr to msg buffer
+*
+* Returns:
+* 0 success and done
+* <0 success, but we're waiting for something to finish.
+* >0 an error occurred while handling the message.
+* Side effects:
+*
+* Call context:
+* process thread (usually)
+* interrupt
+----------------------------------------------------------------*/
+int prism2mgmt_start(wlandevice_t *wlandev, void *msgp)
+{
+ int result = 0;
+ hfa384x_t *hw = wlandev->priv;
+ struct p80211msg_dot11req_start *msg = msgp;
+
+ p80211pstrd_t *pstr;
+ u8 bytebuf[80];
+ struct hfa384x_bytestr *p2bytestr = (struct hfa384x_bytestr *) bytebuf;
+ u16 word;
+
+ wlandev->macmode = WLAN_MACMODE_NONE;
+
+ /* Set the SSID */
+ memcpy(&wlandev->ssid, &msg->ssid.data, sizeof(msg->ssid.data));
+
+ /*** ADHOC IBSS ***/
+ /* see if current f/w is less than 8c3 */
+ if (HFA384x_FIRMWARE_VERSION(hw->ident_sta_fw.major,
+ hw->ident_sta_fw.minor,
+ hw->ident_sta_fw.variant) <
+ HFA384x_FIRMWARE_VERSION(0, 8, 3)) {
+ /* Ad-Hoc not quite supported on Prism2 */
+ msg->resultcode.status = P80211ENUM_msgitem_status_data_ok;
+ msg->resultcode.data = P80211ENUM_resultcode_not_supported;
+ goto done;
+ }
+
+ msg->resultcode.status = P80211ENUM_msgitem_status_data_ok;
+
+ /*** STATION ***/
+ /* Set the REQUIRED config items */
+ /* SSID */
+ pstr = (p80211pstrd_t *) &(msg->ssid.data);
+ prism2mgmt_pstr2bytestr(p2bytestr, pstr);
+ result = hfa384x_drvr_setconfig(hw, HFA384x_RID_CNFOWNSSID,
+ bytebuf, HFA384x_RID_CNFOWNSSID_LEN);
+ if (result) {
+ netdev_err(wlandev->netdev, "Failed to set CnfOwnSSID\n");
+ goto failed;
+ }
+ result = hfa384x_drvr_setconfig(hw, HFA384x_RID_CNFDESIREDSSID,
+ bytebuf,
+ HFA384x_RID_CNFDESIREDSSID_LEN);
+ if (result) {
+ netdev_err(wlandev->netdev, "Failed to set CnfDesiredSSID\n");
+ goto failed;
+ }
+
+ /* bsstype - we use the default in the ap firmware */
+ /* IBSS port */
+ hfa384x_drvr_setconfig16(hw, HFA384x_RID_CNFPORTTYPE, 0);
+
+ /* beacon period */
+ word = msg->beaconperiod.data;
+ result = hfa384x_drvr_setconfig16(hw, HFA384x_RID_CNFAPBCNint, word);
+ if (result) {
+ netdev_err(wlandev->netdev,
+ "Failed to set beacon period=%d.\n", word);
+ goto failed;
+ }
+
+ /* dschannel */
+ word = msg->dschannel.data;
+ result = hfa384x_drvr_setconfig16(hw, HFA384x_RID_CNFOWNCHANNEL, word);
+ if (result) {
+ netdev_err(wlandev->netdev,
+ "Failed to set channel=%d.\n", word);
+ goto failed;
+ }
+ /* Basic rates */
+ word = p80211rate_to_p2bit(msg->basicrate1.data);
+ if (msg->basicrate2.status == P80211ENUM_msgitem_status_data_ok)
+ word |= p80211rate_to_p2bit(msg->basicrate2.data);
+
+ if (msg->basicrate3.status == P80211ENUM_msgitem_status_data_ok)
+ word |= p80211rate_to_p2bit(msg->basicrate3.data);
+
+ if (msg->basicrate4.status == P80211ENUM_msgitem_status_data_ok)
+ word |= p80211rate_to_p2bit(msg->basicrate4.data);
+
+ if (msg->basicrate5.status == P80211ENUM_msgitem_status_data_ok)
+ word |= p80211rate_to_p2bit(msg->basicrate5.data);
+
+ if (msg->basicrate6.status == P80211ENUM_msgitem_status_data_ok)
+ word |= p80211rate_to_p2bit(msg->basicrate6.data);
+
+ if (msg->basicrate7.status == P80211ENUM_msgitem_status_data_ok)
+ word |= p80211rate_to_p2bit(msg->basicrate7.data);
+
+ if (msg->basicrate8.status == P80211ENUM_msgitem_status_data_ok)
+ word |= p80211rate_to_p2bit(msg->basicrate8.data);
+
+ result = hfa384x_drvr_setconfig16(hw, HFA384x_RID_CNFBASICRATES, word);
+ if (result) {
+ netdev_err(wlandev->netdev,
+ "Failed to set basicrates=%d.\n", word);
+ goto failed;
+ }
+
+ /* Operational rates (supprates and txratecontrol) */
+ word = p80211rate_to_p2bit(msg->operationalrate1.data);
+ if (msg->operationalrate2.status == P80211ENUM_msgitem_status_data_ok)
+ word |= p80211rate_to_p2bit(msg->operationalrate2.data);
+
+ if (msg->operationalrate3.status == P80211ENUM_msgitem_status_data_ok)
+ word |= p80211rate_to_p2bit(msg->operationalrate3.data);
+
+ if (msg->operationalrate4.status == P80211ENUM_msgitem_status_data_ok)
+ word |= p80211rate_to_p2bit(msg->operationalrate4.data);
+
+ if (msg->operationalrate5.status == P80211ENUM_msgitem_status_data_ok)
+ word |= p80211rate_to_p2bit(msg->operationalrate5.data);
+
+ if (msg->operationalrate6.status == P80211ENUM_msgitem_status_data_ok)
+ word |= p80211rate_to_p2bit(msg->operationalrate6.data);
+
+ if (msg->operationalrate7.status == P80211ENUM_msgitem_status_data_ok)
+ word |= p80211rate_to_p2bit(msg->operationalrate7.data);
+
+ if (msg->operationalrate8.status == P80211ENUM_msgitem_status_data_ok)
+ word |= p80211rate_to_p2bit(msg->operationalrate8.data);
+
+ result = hfa384x_drvr_setconfig16(hw, HFA384x_RID_CNFSUPPRATES, word);
+ if (result) {
+ netdev_err(wlandev->netdev,
+ "Failed to set supprates=%d.\n", word);
+ goto failed;
+ }
+
+ result = hfa384x_drvr_setconfig16(hw, HFA384x_RID_TXRATECNTL, word);
+ if (result) {
+ netdev_err(wlandev->netdev, "Failed to set txrates=%d.\n",
+ word);
+ goto failed;
+ }
+
+ /* Set the macmode so the frame setup code knows what to do */
+ if (msg->bsstype.data == P80211ENUM_bsstype_independent) {
+ wlandev->macmode = WLAN_MACMODE_IBSS_STA;
+ /* lets extend the data length a bit */
+ hfa384x_drvr_setconfig16(hw, HFA384x_RID_CNFMAXDATALEN, 2304);
+ }
+
+ /* Enable the Port */
+ result = hfa384x_drvr_enable(hw, 0);
+ if (result) {
+ netdev_err(wlandev->netdev,
+ "Enable macport failed, result=%d.\n", result);
+ goto failed;
+ }
+
+ msg->resultcode.data = P80211ENUM_resultcode_success;
+
+ goto done;
+failed:
+ pr_debug("Failed to set a config option, result=%d\n", result);
+ msg->resultcode.data = P80211ENUM_resultcode_invalid_parameters;
+
+done:
+ result = 0;
+
+ return result;
+}
+
+/*----------------------------------------------------------------
+* prism2mgmt_readpda
+*
+* Collect the PDA data and put it in the message.
+*
+* Arguments:
+* wlandev wlan device structure
+* msgp ptr to msg buffer
+*
+* Returns:
+* 0 success and done
+* <0 success, but we're waiting for something to finish.
+* >0 an error occurred while handling the message.
+* Side effects:
+*
+* Call context:
+* process thread (usually)
+----------------------------------------------------------------*/
+int prism2mgmt_readpda(wlandevice_t *wlandev, void *msgp)
+{
+ hfa384x_t *hw = wlandev->priv;
+ struct p80211msg_p2req_readpda *msg = msgp;
+ int result;
+
+ /* We only support collecting the PDA when in the FWLOAD
+ * state.
+ */
+ if (wlandev->msdstate != WLAN_MSD_FWLOAD) {
+ netdev_err(wlandev->netdev,
+ "PDA may only be read in the fwload state.\n");
+ msg->resultcode.data =
+ P80211ENUM_resultcode_implementation_failure;
+ msg->resultcode.status = P80211ENUM_msgitem_status_data_ok;
+ } else {
+ /* Call drvr_readpda(), it handles the auxport enable
+ * and validating the returned PDA.
+ */
+ result = hfa384x_drvr_readpda(hw,
+ msg->pda.data,
+ HFA384x_PDA_LEN_MAX);
+ if (result) {
+ netdev_err(wlandev->netdev,
+ "hfa384x_drvr_readpda() failed, result=%d\n",
+ result);
+
+ msg->resultcode.data =
+ P80211ENUM_resultcode_implementation_failure;
+ msg->resultcode.status =
+ P80211ENUM_msgitem_status_data_ok;
+ return 0;
+ }
+ msg->pda.status = P80211ENUM_msgitem_status_data_ok;
+ msg->resultcode.data = P80211ENUM_resultcode_success;
+ msg->resultcode.status = P80211ENUM_msgitem_status_data_ok;
+ }
+
+ return 0;
+}
+
+/*----------------------------------------------------------------
+* prism2mgmt_ramdl_state
+*
+* Establishes the beginning/end of a card RAM download session.
+*
+* It is expected that the ramdl_write() function will be called
+* one or more times between the 'enable' and 'disable' calls to
+* this function.
+*
+* Note: This function should not be called when a mac comm port
+* is active.
+*
+* Arguments:
+* wlandev wlan device structure
+* msgp ptr to msg buffer
+*
+* Returns:
+* 0 success and done
+* <0 success, but we're waiting for something to finish.
+* >0 an error occurred while handling the message.
+* Side effects:
+*
+* Call context:
+* process thread (usually)
+----------------------------------------------------------------*/
+int prism2mgmt_ramdl_state(wlandevice_t *wlandev, void *msgp)
+{
+ hfa384x_t *hw = wlandev->priv;
+ struct p80211msg_p2req_ramdl_state *msg = msgp;
+
+ if (wlandev->msdstate != WLAN_MSD_FWLOAD) {
+ netdev_err(wlandev->netdev,
+ "ramdl_state(): may only be called in the fwload state.\n");
+ msg->resultcode.data =
+ P80211ENUM_resultcode_implementation_failure;
+ msg->resultcode.status = P80211ENUM_msgitem_status_data_ok;
+ return 0;
+ }
+
+ /*
+ ** Note: Interrupts are locked out if this is an AP and are NOT
+ ** locked out if this is a station.
+ */
+
+ msg->resultcode.status = P80211ENUM_msgitem_status_data_ok;
+ if (msg->enable.data == P80211ENUM_truth_true) {
+ if (hfa384x_drvr_ramdl_enable(hw, msg->exeaddr.data)) {
+ msg->resultcode.data =
+ P80211ENUM_resultcode_implementation_failure;
+ } else {
+ msg->resultcode.data = P80211ENUM_resultcode_success;
+ }
+ } else {
+ hfa384x_drvr_ramdl_disable(hw);
+ msg->resultcode.data = P80211ENUM_resultcode_success;
+ }
+
+ return 0;
+}
+
+/*----------------------------------------------------------------
+* prism2mgmt_ramdl_write
+*
+* Writes a buffer to the card RAM using the download state. This
+* is for writing code to card RAM. To just read or write raw data
+* use the aux functions.
+*
+* Arguments:
+* wlandev wlan device structure
+* msgp ptr to msg buffer
+*
+* Returns:
+* 0 success and done
+* <0 success, but we're waiting for something to finish.
+* >0 an error occurred while handling the message.
+* Side effects:
+*
+* Call context:
+* process thread (usually)
+----------------------------------------------------------------*/
+int prism2mgmt_ramdl_write(wlandevice_t *wlandev, void *msgp)
+{
+ hfa384x_t *hw = wlandev->priv;
+ struct p80211msg_p2req_ramdl_write *msg = msgp;
+ u32 addr;
+ u32 len;
+ u8 *buf;
+
+ if (wlandev->msdstate != WLAN_MSD_FWLOAD) {
+ netdev_err(wlandev->netdev,
+ "ramdl_write(): may only be called in the fwload state.\n");
+ msg->resultcode.data =
+ P80211ENUM_resultcode_implementation_failure;
+ msg->resultcode.status = P80211ENUM_msgitem_status_data_ok;
+ return 0;
+ }
+
+ msg->resultcode.status = P80211ENUM_msgitem_status_data_ok;
+ /* first validate the length */
+ if (msg->len.data > sizeof(msg->data.data)) {
+ msg->resultcode.status =
+ P80211ENUM_resultcode_invalid_parameters;
+ return 0;
+ }
+ /* call the hfa384x function to do the write */
+ addr = msg->addr.data;
+ len = msg->len.data;
+ buf = msg->data.data;
+ if (hfa384x_drvr_ramdl_write(hw, addr, buf, len))
+ msg->resultcode.data = P80211ENUM_resultcode_refused;
+
+ msg->resultcode.data = P80211ENUM_resultcode_success;
+
+ return 0;
+}
+
+/*----------------------------------------------------------------
+* prism2mgmt_flashdl_state
+*
+* Establishes the beginning/end of a card Flash download session.
+*
+* It is expected that the flashdl_write() function will be called
+* one or more times between the 'enable' and 'disable' calls to
+* this function.
+*
+* Note: This function should not be called when a mac comm port
+* is active.
+*
+* Arguments:
+* wlandev wlan device structure
+* msgp ptr to msg buffer
+*
+* Returns:
+* 0 success and done
+* <0 success, but we're waiting for something to finish.
+* >0 an error occurred while handling the message.
+* Side effects:
+*
+* Call context:
+* process thread (usually)
+----------------------------------------------------------------*/
+int prism2mgmt_flashdl_state(wlandevice_t *wlandev, void *msgp)
+{
+ int result = 0;
+ hfa384x_t *hw = wlandev->priv;
+ struct p80211msg_p2req_flashdl_state *msg = msgp;
+
+ if (wlandev->msdstate != WLAN_MSD_FWLOAD) {
+ netdev_err(wlandev->netdev,
+ "flashdl_state(): may only be called in the fwload state.\n");
+ msg->resultcode.data =
+ P80211ENUM_resultcode_implementation_failure;
+ msg->resultcode.status = P80211ENUM_msgitem_status_data_ok;
+ return 0;
+ }
+
+ /*
+ ** Note: Interrupts are locked out if this is an AP and are NOT
+ ** locked out if this is a station.
+ */
+
+ msg->resultcode.status = P80211ENUM_msgitem_status_data_ok;
+ if (msg->enable.data == P80211ENUM_truth_true) {
+ if (hfa384x_drvr_flashdl_enable(hw)) {
+ msg->resultcode.data =
+ P80211ENUM_resultcode_implementation_failure;
+ } else {
+ msg->resultcode.data = P80211ENUM_resultcode_success;
+ }
+ } else {
+ hfa384x_drvr_flashdl_disable(hw);
+ msg->resultcode.data = P80211ENUM_resultcode_success;
+ /* NOTE: At this point, the MAC is in the post-reset
+ * state and the driver is in the fwload state.
+ * We need to get the MAC back into the fwload
+ * state. To do this, we set the nsdstate to HWPRESENT
+ * and then call the ifstate function to redo everything
+ * that got us into the fwload state.
+ */
+ wlandev->msdstate = WLAN_MSD_HWPRESENT;
+ result = prism2sta_ifstate(wlandev, P80211ENUM_ifstate_fwload);
+ if (result != P80211ENUM_resultcode_success) {
+ netdev_err(wlandev->netdev,
+ "prism2sta_ifstate(fwload) failed, P80211ENUM_resultcode=%d\n",
+ result);
+ msg->resultcode.data =
+ P80211ENUM_resultcode_implementation_failure;
+ result = -1;
+ }
+ }
+
+ return 0;
+}
+
+/*----------------------------------------------------------------
+* prism2mgmt_flashdl_write
+*
+*
+*
+* Arguments:
+* wlandev wlan device structure
+* msgp ptr to msg buffer
+*
+* Returns:
+* 0 success and done
+* <0 success, but we're waiting for something to finish.
+* >0 an error occurred while handling the message.
+* Side effects:
+*
+* Call context:
+* process thread (usually)
+----------------------------------------------------------------*/
+int prism2mgmt_flashdl_write(wlandevice_t *wlandev, void *msgp)
+{
+ hfa384x_t *hw = wlandev->priv;
+ struct p80211msg_p2req_flashdl_write *msg = msgp;
+ u32 addr;
+ u32 len;
+ u8 *buf;
+
+ if (wlandev->msdstate != WLAN_MSD_FWLOAD) {
+ netdev_err(wlandev->netdev,
+ "flashdl_write(): may only be called in the fwload state.\n");
+ msg->resultcode.data =
+ P80211ENUM_resultcode_implementation_failure;
+ msg->resultcode.status = P80211ENUM_msgitem_status_data_ok;
+ return 0;
+ }
+
+ /*
+ ** Note: Interrupts are locked out if this is an AP and are NOT
+ ** locked out if this is a station.
+ */
+
+ msg->resultcode.status = P80211ENUM_msgitem_status_data_ok;
+ /* first validate the length */
+ if (msg->len.data > sizeof(msg->data.data)) {
+ msg->resultcode.status =
+ P80211ENUM_resultcode_invalid_parameters;
+ return 0;
+ }
+ /* call the hfa384x function to do the write */
+ addr = msg->addr.data;
+ len = msg->len.data;
+ buf = msg->data.data;
+ if (hfa384x_drvr_flashdl_write(hw, addr, buf, len))
+ msg->resultcode.data = P80211ENUM_resultcode_refused;
+
+ msg->resultcode.data = P80211ENUM_resultcode_success;
+
+ return 0;
+}
+
+/*----------------------------------------------------------------
+* prism2mgmt_autojoin
+*
+* Associate with an ESS.
+*
+* Arguments:
+* wlandev wlan device structure
+* msgp ptr to msg buffer
+*
+* Returns:
+* 0 success and done
+* <0 success, but we're waiting for something to finish.
+* >0 an error occurred while handling the message.
+* Side effects:
+*
+* Call context:
+* process thread (usually)
+* interrupt
+----------------------------------------------------------------*/
+int prism2mgmt_autojoin(wlandevice_t *wlandev, void *msgp)
+{
+ hfa384x_t *hw = wlandev->priv;
+ int result = 0;
+ u16 reg;
+ u16 port_type;
+ struct p80211msg_lnxreq_autojoin *msg = msgp;
+ p80211pstrd_t *pstr;
+ u8 bytebuf[256];
+ struct hfa384x_bytestr *p2bytestr = (struct hfa384x_bytestr *) bytebuf;
+
+ wlandev->macmode = WLAN_MACMODE_NONE;
+
+ /* Set the SSID */
+ memcpy(&wlandev->ssid, &msg->ssid.data, sizeof(msg->ssid.data));
+
+ /* Disable the Port */
+ hfa384x_drvr_disable(hw, 0);
+
+ /*** STATION ***/
+ /* Set the TxRates */
+ hfa384x_drvr_setconfig16(hw, HFA384x_RID_TXRATECNTL, 0x000f);
+
+ /* Set the auth type */
+ if (msg->authtype.data == P80211ENUM_authalg_sharedkey)
+ reg = HFA384x_CNFAUTHENTICATION_SHAREDKEY;
+ else
+ reg = HFA384x_CNFAUTHENTICATION_OPENSYSTEM;
+
+ hfa384x_drvr_setconfig16(hw, HFA384x_RID_CNFAUTHENTICATION, reg);
+
+ /* Set the ssid */
+ memset(bytebuf, 0, 256);
+ pstr = (p80211pstrd_t *) &(msg->ssid.data);
+ prism2mgmt_pstr2bytestr(p2bytestr, pstr);
+ result = hfa384x_drvr_setconfig(hw, HFA384x_RID_CNFDESIREDSSID,
+ bytebuf,
+ HFA384x_RID_CNFDESIREDSSID_LEN);
+ port_type = HFA384x_PORTTYPE_BSS;
+ /* Set the PortType */
+ hfa384x_drvr_setconfig16(hw, HFA384x_RID_CNFPORTTYPE, port_type);
+
+ /* Enable the Port */
+ hfa384x_drvr_enable(hw, 0);
+
+ /* Set the resultcode */
+ msg->resultcode.status = P80211ENUM_msgitem_status_data_ok;
+ msg->resultcode.data = P80211ENUM_resultcode_success;
+
+ return result;
+}
+
+/*----------------------------------------------------------------
+* prism2mgmt_wlansniff
+*
+* Start or stop sniffing.
+*
+* Arguments:
+* wlandev wlan device structure
+* msgp ptr to msg buffer
+*
+* Returns:
+* 0 success and done
+* <0 success, but we're waiting for something to finish.
+* >0 an error occurred while handling the message.
+* Side effects:
+*
+* Call context:
+* process thread (usually)
+* interrupt
+----------------------------------------------------------------*/
+int prism2mgmt_wlansniff(wlandevice_t *wlandev, void *msgp)
+{
+ int result = 0;
+ struct p80211msg_lnxreq_wlansniff *msg = msgp;
+
+ hfa384x_t *hw = wlandev->priv;
+ u16 word;
+
+ msg->resultcode.status = P80211ENUM_msgitem_status_data_ok;
+ switch (msg->enable.data) {
+ case P80211ENUM_truth_false:
+ /* Confirm that we're in monitor mode */
+ if (wlandev->netdev->type == ARPHRD_ETHER) {
+ msg->resultcode.data =
+ P80211ENUM_resultcode_invalid_parameters;
+ return 0;
+ }
+ /* Disable monitor mode */
+ result = hfa384x_cmd_monitor(hw, HFA384x_MONITOR_DISABLE);
+ if (result) {
+ pr_debug("failed to disable monitor mode, result=%d\n",
+ result);
+ goto failed;
+ }
+ /* Disable port 0 */
+ result = hfa384x_drvr_disable(hw, 0);
+ if (result) {
+ pr_debug
+ ("failed to disable port 0 after sniffing, result=%d\n",
+ result);
+ goto failed;
+ }
+ /* Clear the driver state */
+ wlandev->netdev->type = ARPHRD_ETHER;
+
+ /* Restore the wepflags */
+ result = hfa384x_drvr_setconfig16(hw,
+ HFA384x_RID_CNFWEPFLAGS,
+ hw->presniff_wepflags);
+ if (result) {
+ pr_debug
+ ("failed to restore wepflags=0x%04x, result=%d\n",
+ hw->presniff_wepflags, result);
+ goto failed;
+ }
+
+ /* Set the port to its prior type and enable (if necessary) */
+ if (hw->presniff_port_type != 0) {
+ word = hw->presniff_port_type;
+ result = hfa384x_drvr_setconfig16(hw,
+ HFA384x_RID_CNFPORTTYPE,
+ word);
+ if (result) {
+ pr_debug
+ ("failed to restore porttype, result=%d\n",
+ result);
+ goto failed;
+ }
+
+ /* Enable the port */
+ result = hfa384x_drvr_enable(hw, 0);
+ if (result) {
+ pr_debug("failed to enable port to presniff setting, result=%d\n",
+ result);
+ goto failed;
+ }
+ } else {
+ result = hfa384x_drvr_disable(hw, 0);
+
+ }
+
+ netdev_info(wlandev->netdev, "monitor mode disabled\n");
+ msg->resultcode.data = P80211ENUM_resultcode_success;
+ return 0;
+ case P80211ENUM_truth_true:
+ /* Disable the port (if enabled), only check Port 0 */
+ if (hw->port_enabled[0]) {
+ if (wlandev->netdev->type == ARPHRD_ETHER) {
+ /* Save macport 0 state */
+ result = hfa384x_drvr_getconfig16(hw,
+ HFA384x_RID_CNFPORTTYPE,
+ &(hw->presniff_port_type));
+ if (result) {
+ pr_debug
+ ("failed to read porttype, result=%d\n",
+ result);
+ goto failed;
+ }
+ /* Save the wepflags state */
+ result = hfa384x_drvr_getconfig16(hw,
+ HFA384x_RID_CNFWEPFLAGS,
+ &(hw->presniff_wepflags));
+ if (result) {
+ pr_debug
+ ("failed to read wepflags, result=%d\n",
+ result);
+ goto failed;
+ }
+ hfa384x_drvr_stop(hw);
+ result = hfa384x_drvr_start(hw);
+ if (result) {
+ pr_debug("failed to restart the card for sniffing, result=%d\n",
+ result);
+ goto failed;
+ }
+ } else {
+ /* Disable the port */
+ result = hfa384x_drvr_disable(hw, 0);
+ if (result) {
+ pr_debug("failed to enable port for sniffing, result=%d\n",
+ result);
+ goto failed;
+ }
+ }
+ } else {
+ hw->presniff_port_type = 0;
+ }
+
+ /* Set the channel we wish to sniff */
+ word = msg->channel.data;
+ result = hfa384x_drvr_setconfig16(hw,
+ HFA384x_RID_CNFOWNCHANNEL,
+ word);
+ hw->sniff_channel = word;
+
+ if (result) {
+ pr_debug("failed to set channel %d, result=%d\n",
+ word, result);
+ goto failed;
+ }
+
+ /* Now if we're already sniffing, we can skip the rest */
+ if (wlandev->netdev->type != ARPHRD_ETHER) {
+ /* Set the port type to pIbss */
+ word = HFA384x_PORTTYPE_PSUEDOIBSS;
+ result = hfa384x_drvr_setconfig16(hw,
+ HFA384x_RID_CNFPORTTYPE,
+ word);
+ if (result) {
+ pr_debug
+ ("failed to set porttype %d, result=%d\n",
+ word, result);
+ goto failed;
+ }
+ if ((msg->keepwepflags.status ==
+ P80211ENUM_msgitem_status_data_ok)
+ && (msg->keepwepflags.data !=
+ P80211ENUM_truth_true)) {
+ /* Set the wepflags for no decryption */
+ word = HFA384x_WEPFLAGS_DISABLE_TXCRYPT |
+ HFA384x_WEPFLAGS_DISABLE_RXCRYPT;
+ result =
+ hfa384x_drvr_setconfig16(hw,
+ HFA384x_RID_CNFWEPFLAGS,
+ word);
+ }
+
+ if (result) {
+ pr_debug
+ ("failed to set wepflags=0x%04x, result=%d\n",
+ word, result);
+ goto failed;
+ }
+ }
+
+ /* Do we want to strip the FCS in monitor mode? */
+ if ((msg->stripfcs.status == P80211ENUM_msgitem_status_data_ok)
+ && (msg->stripfcs.data == P80211ENUM_truth_true)) {
+ hw->sniff_fcs = 0;
+ } else {
+ hw->sniff_fcs = 1;
+ }
+
+ /* Do we want to truncate the packets? */
+ if (msg->packet_trunc.status ==
+ P80211ENUM_msgitem_status_data_ok) {
+ hw->sniff_truncate = msg->packet_trunc.data;
+ } else {
+ hw->sniff_truncate = 0;
+ }
+
+ /* Enable the port */
+ result = hfa384x_drvr_enable(hw, 0);
+ if (result) {
+ pr_debug
+ ("failed to enable port for sniffing, result=%d\n",
+ result);
+ goto failed;
+ }
+ /* Enable monitor mode */
+ result = hfa384x_cmd_monitor(hw, HFA384x_MONITOR_ENABLE);
+ if (result) {
+ pr_debug("failed to enable monitor mode, result=%d\n",
+ result);
+ goto failed;
+ }
+
+ if (wlandev->netdev->type == ARPHRD_ETHER)
+ netdev_info(wlandev->netdev, "monitor mode enabled\n");
+
+ /* Set the driver state */
+ /* Do we want the prism2 header? */
+ if ((msg->prismheader.status ==
+ P80211ENUM_msgitem_status_data_ok)
+ && (msg->prismheader.data == P80211ENUM_truth_true)) {
+ hw->sniffhdr = 0;
+ wlandev->netdev->type = ARPHRD_IEEE80211_PRISM;
+ } else
+ if ((msg->wlanheader.status ==
+ P80211ENUM_msgitem_status_data_ok)
+ && (msg->wlanheader.data == P80211ENUM_truth_true)) {
+ hw->sniffhdr = 1;
+ wlandev->netdev->type = ARPHRD_IEEE80211_PRISM;
+ } else {
+ wlandev->netdev->type = ARPHRD_IEEE80211;
+ }
+
+ msg->resultcode.data = P80211ENUM_resultcode_success;
+ return 0;
+ default:
+ msg->resultcode.data = P80211ENUM_resultcode_invalid_parameters;
+ return 0;
+ }
+
+failed:
+ msg->resultcode.data = P80211ENUM_resultcode_refused;
+ return 0;
+}
diff --git a/drivers/staging/wlan-ng/prism2mgmt.h b/drivers/staging/wlan-ng/prism2mgmt.h
new file mode 100644
index 000000000..16f123959
--- /dev/null
+++ b/drivers/staging/wlan-ng/prism2mgmt.h
@@ -0,0 +1,117 @@
+/* prism2mgmt.h
+*
+* Declares the mgmt command handler functions
+*
+* Copyright (C) 1999 AbsoluteValue Systems, Inc. All Rights Reserved.
+* --------------------------------------------------------------------
+*
+* linux-wlan
+*
+* The contents of this file are subject to the Mozilla Public
+* License Version 1.1 (the "License"); you may not use this file
+* except in compliance with the License. You may obtain a copy of
+* the License at http://www.mozilla.org/MPL/
+*
+* Software distributed under the License is distributed on an "AS
+* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+* implied. See the License for the specific language governing
+* rights and limitations under the License.
+*
+* Alternatively, the contents of this file may be used under the
+* terms of the GNU Public License version 2 (the "GPL"), in which
+* case the provisions of the GPL are applicable instead of the
+* above. If you wish to allow the use of your version of this file
+* only under the terms of the GPL and not to allow others to use
+* your version of this file under the MPL, indicate your decision
+* by deleting the provisions above and replace them with the notice
+* and other provisions required by the GPL. If you do not delete
+* the provisions above, a recipient may use your version of this
+* file under either the MPL or the GPL.
+*
+* --------------------------------------------------------------------
+*
+* Inquiries regarding the linux-wlan Open Source project can be
+* made directly to:
+*
+* AbsoluteValue Systems Inc.
+* info@linux-wlan.com
+* http://www.linux-wlan.com
+*
+* --------------------------------------------------------------------
+*
+* Portions of the development of this software were funded by
+* Intersil Corporation as part of PRISM(R) chipset product development.
+*
+* --------------------------------------------------------------------
+*
+* This file contains the constants and data structures for interaction
+* with the hfa384x Wireless LAN (WLAN) Media Access Controller (MAC).
+* The hfa384x is a portion of the Harris PRISM(tm) WLAN chipset.
+*
+* [Implementation and usage notes]
+*
+* [References]
+* CW10 Programmer's Manual v1.5
+* IEEE 802.11 D10.0
+*
+* --------------------------------------------------------------------
+*/
+
+#ifndef _PRISM2MGMT_H
+#define _PRISM2MGMT_H
+
+extern int prism2_reset_holdtime;
+extern int prism2_reset_settletime;
+
+u32 prism2sta_ifstate(wlandevice_t *wlandev, u32 ifstate);
+
+void prism2sta_ev_info(wlandevice_t *wlandev, hfa384x_InfFrame_t *inf);
+void prism2sta_ev_txexc(wlandevice_t *wlandev, u16 status);
+void prism2sta_ev_tx(wlandevice_t *wlandev, u16 status);
+void prism2sta_ev_rx(wlandevice_t *wlandev, struct sk_buff *skb);
+void prism2sta_ev_alloc(wlandevice_t *wlandev);
+
+int prism2mgmt_mibset_mibget(wlandevice_t *wlandev, void *msgp);
+int prism2mgmt_scan(wlandevice_t *wlandev, void *msgp);
+int prism2mgmt_scan_results(wlandevice_t *wlandev, void *msgp);
+int prism2mgmt_start(wlandevice_t *wlandev, void *msgp);
+int prism2mgmt_wlansniff(wlandevice_t *wlandev, void *msgp);
+int prism2mgmt_readpda(wlandevice_t *wlandev, void *msgp);
+int prism2mgmt_ramdl_state(wlandevice_t *wlandev, void *msgp);
+int prism2mgmt_ramdl_write(wlandevice_t *wlandev, void *msgp);
+int prism2mgmt_flashdl_state(wlandevice_t *wlandev, void *msgp);
+int prism2mgmt_flashdl_write(wlandevice_t *wlandev, void *msgp);
+int prism2mgmt_autojoin(wlandevice_t *wlandev, void *msgp);
+
+/*---------------------------------------------------------------
+* conversion functions going between wlan message data types and
+* Prism2 data types
+---------------------------------------------------------------*/
+/* byte area conversion functions*/
+void prism2mgmt_pstr2bytearea(u8 *bytearea, p80211pstrd_t *pstr);
+void prism2mgmt_bytearea2pstr(u8 *bytearea, p80211pstrd_t *pstr, int len);
+
+/* byte string conversion functions*/
+void prism2mgmt_pstr2bytestr(struct hfa384x_bytestr *bytestr,
+ p80211pstrd_t *pstr);
+void prism2mgmt_bytestr2pstr(struct hfa384x_bytestr *bytestr,
+ p80211pstrd_t *pstr);
+
+/* functions to convert Group Addresses */
+void prism2mgmt_get_grpaddr(u32 did, p80211pstrd_t *pstr, hfa384x_t *priv);
+int prism2mgmt_set_grpaddr(u32 did,
+ u8 *prism2buf, p80211pstrd_t *pstr,
+ hfa384x_t *priv);
+int prism2mgmt_get_grpaddr_index(u32 did);
+
+void prism2sta_processing_defer(struct work_struct *data);
+
+void prism2sta_commsqual_defer(struct work_struct *data);
+void prism2sta_commsqual_timer(unsigned long data);
+
+/* Interface callback functions, passing data back up to the cfg80211 layer */
+void prism2_connect_result(wlandevice_t *wlandev, u8 failed);
+void prism2_disconnected(wlandevice_t *wlandev);
+void prism2_roamed(wlandevice_t *wlandev);
+
+#endif
diff --git a/drivers/staging/wlan-ng/prism2mib.c b/drivers/staging/wlan-ng/prism2mib.c
new file mode 100644
index 000000000..b4a15ef3a
--- /dev/null
+++ b/drivers/staging/wlan-ng/prism2mib.c
@@ -0,0 +1,825 @@
+/* src/prism2/driver/prism2mib.c
+*
+* Management request for mibset/mibget
+*
+* Copyright (C) 1999 AbsoluteValue Systems, Inc. All Rights Reserved.
+* --------------------------------------------------------------------
+*
+* linux-wlan
+*
+* The contents of this file are subject to the Mozilla Public
+* License Version 1.1 (the "License"); you may not use this file
+* except in compliance with the License. You may obtain a copy of
+* the License at http://www.mozilla.org/MPL/
+*
+* Software distributed under the License is distributed on an "AS
+* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+* implied. See the License for the specific language governing
+* rights and limitations under the License.
+*
+* Alternatively, the contents of this file may be used under the
+* terms of the GNU Public License version 2 (the "GPL"), in which
+* case the provisions of the GPL are applicable instead of the
+* above. If you wish to allow the use of your version of this file
+* only under the terms of the GPL and not to allow others to use
+* your version of this file under the MPL, indicate your decision
+* by deleting the provisions above and replace them with the notice
+* and other provisions required by the GPL. If you do not delete
+* the provisions above, a recipient may use your version of this
+* file under either the MPL or the GPL.
+*
+* --------------------------------------------------------------------
+*
+* Inquiries regarding the linux-wlan Open Source project can be
+* made directly to:
+*
+* AbsoluteValue Systems Inc.
+* info@linux-wlan.com
+* http://www.linux-wlan.com
+*
+* --------------------------------------------------------------------
+*
+* Portions of the development of this software were funded by
+* Intersil Corporation as part of PRISM(R) chipset product development.
+*
+* --------------------------------------------------------------------
+*
+* The functions in this file handle the mibset/mibget management
+* functions.
+*
+* --------------------------------------------------------------------
+*/
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/types.h>
+#include <linux/wireless.h>
+#include <linux/netdevice.h>
+#include <linux/io.h>
+#include <linux/delay.h>
+#include <asm/byteorder.h>
+#include <linux/usb.h>
+#include <linux/bitops.h>
+
+#include "p80211types.h"
+#include "p80211hdr.h"
+#include "p80211mgmt.h"
+#include "p80211conv.h"
+#include "p80211msg.h"
+#include "p80211netdev.h"
+#include "p80211metadef.h"
+#include "p80211metastruct.h"
+#include "hfa384x.h"
+#include "prism2mgmt.h"
+
+#define MIB_TMP_MAXLEN 200 /* Max length of RID record (in bytes). */
+
+#define F_STA 0x1 /* MIB is supported on stations. */
+#define F_READ 0x2 /* MIB may be read. */
+#define F_WRITE 0x4 /* MIB may be written. */
+
+struct mibrec {
+ u32 did;
+ u16 flag;
+ u16 parm1;
+ u16 parm2;
+ u16 parm3;
+ int (*func)(struct mibrec *mib,
+ int isget,
+ wlandevice_t *wlandev,
+ hfa384x_t *hw,
+ struct p80211msg_dot11req_mibset *msg, void *data);
+};
+
+static int prism2mib_bytearea2pstr(struct mibrec *mib,
+ int isget,
+ wlandevice_t *wlandev,
+ hfa384x_t *hw,
+ struct p80211msg_dot11req_mibset *msg,
+ void *data);
+
+static int prism2mib_uint32(struct mibrec *mib,
+ int isget,
+ wlandevice_t *wlandev,
+ hfa384x_t *hw,
+ struct p80211msg_dot11req_mibset *msg, void *data);
+
+static int prism2mib_flag(struct mibrec *mib,
+ int isget,
+ wlandevice_t *wlandev,
+ hfa384x_t *hw,
+ struct p80211msg_dot11req_mibset *msg, void *data);
+
+static int prism2mib_wepdefaultkey(struct mibrec *mib,
+ int isget,
+ wlandevice_t *wlandev,
+ hfa384x_t *hw,
+ struct p80211msg_dot11req_mibset *msg,
+ void *data);
+
+static int prism2mib_privacyinvoked(struct mibrec *mib,
+ int isget,
+ wlandevice_t *wlandev,
+ hfa384x_t *hw,
+ struct p80211msg_dot11req_mibset *msg,
+ void *data);
+
+static int prism2mib_excludeunencrypted(struct mibrec *mib,
+ int isget,
+ wlandevice_t *wlandev,
+ hfa384x_t *hw,
+ struct p80211msg_dot11req_mibset *msg,
+ void *data);
+
+static int prism2mib_fragmentationthreshold(struct mibrec *mib,
+ int isget,
+ wlandevice_t *wlandev,
+ hfa384x_t *hw,
+ struct p80211msg_dot11req_mibset *msg,
+ void *data);
+
+static int prism2mib_priv(struct mibrec *mib,
+ int isget,
+ wlandevice_t *wlandev,
+ hfa384x_t *hw,
+ struct p80211msg_dot11req_mibset *msg, void *data);
+
+static struct mibrec mibtab[] = {
+
+ /* dot11smt MIB's */
+ {DIDmib_dot11smt_dot11WEPDefaultKeysTable_dot11WEPDefaultKey0,
+ F_STA | F_WRITE,
+ HFA384x_RID_CNFWEPDEFAULTKEY0, 0, 0,
+ prism2mib_wepdefaultkey},
+ {DIDmib_dot11smt_dot11WEPDefaultKeysTable_dot11WEPDefaultKey1,
+ F_STA | F_WRITE,
+ HFA384x_RID_CNFWEPDEFAULTKEY1, 0, 0,
+ prism2mib_wepdefaultkey},
+ {DIDmib_dot11smt_dot11WEPDefaultKeysTable_dot11WEPDefaultKey2,
+ F_STA | F_WRITE,
+ HFA384x_RID_CNFWEPDEFAULTKEY2, 0, 0,
+ prism2mib_wepdefaultkey},
+ {DIDmib_dot11smt_dot11WEPDefaultKeysTable_dot11WEPDefaultKey3,
+ F_STA | F_WRITE,
+ HFA384x_RID_CNFWEPDEFAULTKEY3, 0, 0,
+ prism2mib_wepdefaultkey},
+ {DIDmib_dot11smt_dot11PrivacyTable_dot11PrivacyInvoked,
+ F_STA | F_READ | F_WRITE,
+ HFA384x_RID_CNFWEPFLAGS, HFA384x_WEPFLAGS_PRIVINVOKED, 0,
+ prism2mib_privacyinvoked},
+ {DIDmib_dot11smt_dot11PrivacyTable_dot11WEPDefaultKeyID,
+ F_STA | F_READ | F_WRITE,
+ HFA384x_RID_CNFWEPDEFAULTKEYID, 0, 0,
+ prism2mib_uint32},
+ {DIDmib_dot11smt_dot11PrivacyTable_dot11ExcludeUnencrypted,
+ F_STA | F_READ | F_WRITE,
+ HFA384x_RID_CNFWEPFLAGS, HFA384x_WEPFLAGS_EXCLUDE, 0,
+ prism2mib_excludeunencrypted},
+
+ /* dot11mac MIB's */
+
+ {DIDmib_dot11mac_dot11OperationTable_dot11MACAddress,
+ F_STA | F_READ | F_WRITE,
+ HFA384x_RID_CNFOWNMACADDR, HFA384x_RID_CNFOWNMACADDR_LEN, 0,
+ prism2mib_bytearea2pstr},
+ {DIDmib_dot11mac_dot11OperationTable_dot11RTSThreshold,
+ F_STA | F_READ | F_WRITE,
+ HFA384x_RID_RTSTHRESH, 0, 0,
+ prism2mib_uint32},
+ {DIDmib_dot11mac_dot11OperationTable_dot11ShortRetryLimit,
+ F_STA | F_READ,
+ HFA384x_RID_SHORTRETRYLIMIT, 0, 0,
+ prism2mib_uint32},
+ {DIDmib_dot11mac_dot11OperationTable_dot11LongRetryLimit,
+ F_STA | F_READ,
+ HFA384x_RID_LONGRETRYLIMIT, 0, 0,
+ prism2mib_uint32},
+ {DIDmib_dot11mac_dot11OperationTable_dot11FragmentationThreshold,
+ F_STA | F_READ | F_WRITE,
+ HFA384x_RID_FRAGTHRESH, 0, 0,
+ prism2mib_fragmentationthreshold},
+ {DIDmib_dot11mac_dot11OperationTable_dot11MaxTransmitMSDULifetime,
+ F_STA | F_READ,
+ HFA384x_RID_MAXTXLIFETIME, 0, 0,
+ prism2mib_uint32},
+
+ /* dot11phy MIB's */
+
+ {DIDmib_dot11phy_dot11PhyDSSSTable_dot11CurrentChannel,
+ F_STA | F_READ,
+ HFA384x_RID_CURRENTCHANNEL, 0, 0,
+ prism2mib_uint32},
+ {DIDmib_dot11phy_dot11PhyTxPowerTable_dot11CurrentTxPowerLevel,
+ F_STA | F_READ | F_WRITE,
+ HFA384x_RID_TXPOWERMAX, 0, 0,
+ prism2mib_uint32},
+
+ /* p2Static MIB's */
+
+ {DIDmib_p2_p2Static_p2CnfPortType,
+ F_STA | F_READ | F_WRITE,
+ HFA384x_RID_CNFPORTTYPE, 0, 0,
+ prism2mib_uint32},
+
+ /* p2MAC MIB's */
+
+ {DIDmib_p2_p2MAC_p2CurrentTxRate,
+ F_STA | F_READ,
+ HFA384x_RID_CURRENTTXRATE, 0, 0,
+ prism2mib_uint32},
+
+ /* And finally, lnx mibs */
+ {DIDmib_lnx_lnxConfigTable_lnxRSNAIE,
+ F_STA | F_READ | F_WRITE,
+ HFA384x_RID_CNFWPADATA, 0, 0,
+ prism2mib_priv},
+ {0, 0, 0, 0, 0, NULL}
+};
+
+/*----------------------------------------------------------------
+* prism2mgmt_mibset_mibget
+*
+* Set the value of a mib item.
+*
+* Arguments:
+* wlandev wlan device structure
+* msgp ptr to msg buffer
+*
+* Returns:
+* 0 success and done
+* <0 success, but we're waiting for something to finish.
+* >0 an error occurred while handling the message.
+* Side effects:
+*
+* Call context:
+* process thread (usually)
+* interrupt
+----------------------------------------------------------------*/
+
+int prism2mgmt_mibset_mibget(wlandevice_t *wlandev, void *msgp)
+{
+ hfa384x_t *hw = wlandev->priv;
+ int result, isget;
+ struct mibrec *mib;
+
+ u16 which;
+
+ struct p80211msg_dot11req_mibset *msg = msgp;
+ p80211itemd_t *mibitem;
+
+ msg->resultcode.status = P80211ENUM_msgitem_status_data_ok;
+ msg->resultcode.data = P80211ENUM_resultcode_success;
+
+ /*
+ ** Determine if this is an Access Point or a station.
+ */
+
+ which = F_STA;
+
+ /*
+ ** Find the MIB in the MIB table. Note that a MIB may be in the
+ ** table twice...once for an AP and once for a station. Make sure
+ ** to get the correct one. Note that DID=0 marks the end of the
+ ** MIB table.
+ */
+
+ mibitem = (p80211itemd_t *) msg->mibattribute.data;
+
+ for (mib = mibtab; mib->did != 0; mib++)
+ if (mib->did == mibitem->did && (mib->flag & which))
+ break;
+
+ if (mib->did == 0) {
+ msg->resultcode.data = P80211ENUM_resultcode_not_supported;
+ goto done;
+ }
+
+ /*
+ ** Determine if this is a "mibget" or a "mibset". If this is a
+ ** "mibget", then make sure that the MIB may be read. Otherwise,
+ ** this is a "mibset" so make make sure that the MIB may be written.
+ */
+
+ isget = (msg->msgcode == DIDmsg_dot11req_mibget);
+
+ if (isget) {
+ if (!(mib->flag & F_READ)) {
+ msg->resultcode.data =
+ P80211ENUM_resultcode_cant_get_writeonly_mib;
+ goto done;
+ }
+ } else {
+ if (!(mib->flag & F_WRITE)) {
+ msg->resultcode.data =
+ P80211ENUM_resultcode_cant_set_readonly_mib;
+ goto done;
+ }
+ }
+
+ /*
+ ** Execute the MIB function. If things worked okay, then make
+ ** sure that the MIB function also worked okay. If so, and this
+ ** is a "mibget", then the status value must be set for both the
+ ** "mibattribute" parameter and the mib item within the data
+ ** portion of the "mibattribute".
+ */
+
+ result = mib->func(mib, isget, wlandev, hw, msg, (void *)mibitem->data);
+
+ if (msg->resultcode.data == P80211ENUM_resultcode_success) {
+ if (result != 0) {
+ pr_debug("get/set failure, result=%d\n", result);
+ msg->resultcode.data =
+ P80211ENUM_resultcode_implementation_failure;
+ } else {
+ if (isget) {
+ msg->mibattribute.status =
+ P80211ENUM_msgitem_status_data_ok;
+ mibitem->status =
+ P80211ENUM_msgitem_status_data_ok;
+ }
+ }
+ }
+
+done:
+ return 0;
+}
+
+/*----------------------------------------------------------------
+* prism2mib_bytearea2pstr
+*
+* Get/set pstr data to/from a byte area.
+*
+* MIB record parameters:
+* parm1 Prism2 RID value.
+* parm2 Number of bytes of RID data.
+* parm3 Not used.
+*
+* Arguments:
+* mib MIB record.
+* isget MIBGET/MIBSET flag.
+* wlandev wlan device structure.
+* priv "priv" structure.
+* hw "hw" structure.
+* msg Message structure.
+* data Data buffer.
+*
+* Returns:
+* 0 - Success.
+* ~0 - Error.
+*
+----------------------------------------------------------------*/
+
+static int prism2mib_bytearea2pstr(struct mibrec *mib,
+ int isget,
+ wlandevice_t *wlandev,
+ hfa384x_t *hw,
+ struct p80211msg_dot11req_mibset *msg,
+ void *data)
+{
+ int result;
+ p80211pstrd_t *pstr = (p80211pstrd_t *) data;
+ u8 bytebuf[MIB_TMP_MAXLEN];
+
+ if (isget) {
+ result =
+ hfa384x_drvr_getconfig(hw, mib->parm1, bytebuf, mib->parm2);
+ prism2mgmt_bytearea2pstr(bytebuf, pstr, mib->parm2);
+ } else {
+ memset(bytebuf, 0, mib->parm2);
+ prism2mgmt_pstr2bytearea(bytebuf, pstr);
+ result =
+ hfa384x_drvr_setconfig(hw, mib->parm1, bytebuf, mib->parm2);
+ }
+
+ return result;
+}
+
+/*----------------------------------------------------------------
+* prism2mib_uint32
+*
+* Get/set uint32 data.
+*
+* MIB record parameters:
+* parm1 Prism2 RID value.
+* parm2 Not used.
+* parm3 Not used.
+*
+* Arguments:
+* mib MIB record.
+* isget MIBGET/MIBSET flag.
+* wlandev wlan device structure.
+* priv "priv" structure.
+* hw "hw" structure.
+* msg Message structure.
+* data Data buffer.
+*
+* Returns:
+* 0 - Success.
+* ~0 - Error.
+*
+----------------------------------------------------------------*/
+
+static int prism2mib_uint32(struct mibrec *mib,
+ int isget,
+ wlandevice_t *wlandev,
+ hfa384x_t *hw,
+ struct p80211msg_dot11req_mibset *msg, void *data)
+{
+ int result;
+ u32 *uint32 = (u32 *) data;
+ u8 bytebuf[MIB_TMP_MAXLEN];
+ u16 *wordbuf = (u16 *) bytebuf;
+
+ if (isget) {
+ result = hfa384x_drvr_getconfig16(hw, mib->parm1, wordbuf);
+ *uint32 = *wordbuf;
+ } else {
+ *wordbuf = *uint32;
+ result = hfa384x_drvr_setconfig16(hw, mib->parm1, *wordbuf);
+ }
+
+ return result;
+}
+
+/*----------------------------------------------------------------
+* prism2mib_flag
+*
+* Get/set a flag.
+*
+* MIB record parameters:
+* parm1 Prism2 RID value.
+* parm2 Bit to get/set.
+* parm3 Not used.
+*
+* Arguments:
+* mib MIB record.
+* isget MIBGET/MIBSET flag.
+* wlandev wlan device structure.
+* priv "priv" structure.
+* hw "hw" structure.
+* msg Message structure.
+* data Data buffer.
+*
+* Returns:
+* 0 - Success.
+* ~0 - Error.
+*
+----------------------------------------------------------------*/
+
+static int prism2mib_flag(struct mibrec *mib,
+ int isget,
+ wlandevice_t *wlandev,
+ hfa384x_t *hw,
+ struct p80211msg_dot11req_mibset *msg, void *data)
+{
+ int result;
+ u32 *uint32 = (u32 *) data;
+ u8 bytebuf[MIB_TMP_MAXLEN];
+ u16 *wordbuf = (u16 *) bytebuf;
+ u32 flags;
+
+ result = hfa384x_drvr_getconfig16(hw, mib->parm1, wordbuf);
+ if (result == 0) {
+ flags = *wordbuf;
+ if (isget) {
+ *uint32 = (flags & mib->parm2) ?
+ P80211ENUM_truth_true : P80211ENUM_truth_false;
+ } else {
+ if ((*uint32) == P80211ENUM_truth_true)
+ flags |= mib->parm2;
+ else
+ flags &= ~mib->parm2;
+ *wordbuf = flags;
+ result =
+ hfa384x_drvr_setconfig16(hw, mib->parm1, *wordbuf);
+ }
+ }
+
+ return result;
+}
+
+/*----------------------------------------------------------------
+* prism2mib_wepdefaultkey
+*
+* Get/set WEP default keys.
+*
+* MIB record parameters:
+* parm1 Prism2 RID value.
+* parm2 Number of bytes of RID data.
+* parm3 Not used.
+*
+* Arguments:
+* mib MIB record.
+* isget MIBGET/MIBSET flag.
+* wlandev wlan device structure.
+* priv "priv" structure.
+* hw "hw" structure.
+* msg Message structure.
+* data Data buffer.
+*
+* Returns:
+* 0 - Success.
+* ~0 - Error.
+*
+----------------------------------------------------------------*/
+
+static int prism2mib_wepdefaultkey(struct mibrec *mib,
+ int isget,
+ wlandevice_t *wlandev,
+ hfa384x_t *hw,
+ struct p80211msg_dot11req_mibset *msg,
+ void *data)
+{
+ int result;
+ p80211pstrd_t *pstr = (p80211pstrd_t *) data;
+ u8 bytebuf[MIB_TMP_MAXLEN];
+ u16 len;
+
+ if (isget) {
+ result = 0; /* Should never happen. */
+ } else {
+ len = (pstr->len > 5) ? HFA384x_RID_CNFWEP128DEFAULTKEY_LEN :
+ HFA384x_RID_CNFWEPDEFAULTKEY_LEN;
+ memset(bytebuf, 0, len);
+ prism2mgmt_pstr2bytearea(bytebuf, pstr);
+ result = hfa384x_drvr_setconfig(hw, mib->parm1, bytebuf, len);
+ }
+
+ return result;
+}
+
+/*----------------------------------------------------------------
+* prism2mib_privacyinvoked
+*
+* Get/set the dot11PrivacyInvoked value.
+*
+* MIB record parameters:
+* parm1 Prism2 RID value.
+* parm2 Bit value for PrivacyInvoked flag.
+* parm3 Not used.
+*
+* Arguments:
+* mib MIB record.
+* isget MIBGET/MIBSET flag.
+* wlandev wlan device structure.
+* priv "priv" structure.
+* hw "hw" structure.
+* msg Message structure.
+* data Data buffer.
+*
+* Returns:
+* 0 - Success.
+* ~0 - Error.
+*
+----------------------------------------------------------------*/
+
+static int prism2mib_privacyinvoked(struct mibrec *mib,
+ int isget,
+ wlandevice_t *wlandev,
+ hfa384x_t *hw,
+ struct p80211msg_dot11req_mibset *msg,
+ void *data)
+{
+ if (wlandev->hostwep & HOSTWEP_DECRYPT) {
+ if (wlandev->hostwep & HOSTWEP_DECRYPT)
+ mib->parm2 |= HFA384x_WEPFLAGS_DISABLE_RXCRYPT;
+ if (wlandev->hostwep & HOSTWEP_ENCRYPT)
+ mib->parm2 |= HFA384x_WEPFLAGS_DISABLE_TXCRYPT;
+ }
+
+ return prism2mib_flag(mib, isget, wlandev, hw, msg, data);
+}
+
+/*----------------------------------------------------------------
+* prism2mib_excludeunencrypted
+*
+* Get/set the dot11ExcludeUnencrypted value.
+*
+* MIB record parameters:
+* parm1 Prism2 RID value.
+* parm2 Bit value for ExcludeUnencrypted flag.
+* parm3 Not used.
+*
+* Arguments:
+* mib MIB record.
+* isget MIBGET/MIBSET flag.
+* wlandev wlan device structure.
+* priv "priv" structure.
+* hw "hw" structure.
+* msg Message structure.
+* data Data buffer.
+*
+* Returns:
+* 0 - Success.
+* ~0 - Error.
+*
+----------------------------------------------------------------*/
+
+static int prism2mib_excludeunencrypted(struct mibrec *mib,
+ int isget,
+ wlandevice_t *wlandev,
+ hfa384x_t *hw,
+ struct p80211msg_dot11req_mibset *msg,
+ void *data)
+{
+
+ return prism2mib_flag(mib, isget, wlandev, hw, msg, data);
+}
+
+/*----------------------------------------------------------------
+* prism2mib_fragmentationthreshold
+*
+* Get/set the fragmentation threshold.
+*
+* MIB record parameters:
+* parm1 Prism2 RID value.
+* parm2 Not used.
+* parm3 Not used.
+*
+* Arguments:
+* mib MIB record.
+* isget MIBGET/MIBSET flag.
+* wlandev wlan device structure.
+* priv "priv" structure.
+* hw "hw" structure.
+* msg Message structure.
+* data Data buffer.
+*
+* Returns:
+* 0 - Success.
+* ~0 - Error.
+*
+----------------------------------------------------------------*/
+
+static int prism2mib_fragmentationthreshold(struct mibrec *mib,
+ int isget,
+ wlandevice_t *wlandev,
+ hfa384x_t *hw,
+ struct p80211msg_dot11req_mibset *msg,
+ void *data)
+{
+ int result;
+ u32 *uint32 = (u32 *) data;
+
+ if (!isget)
+ if ((*uint32) % 2) {
+ netdev_warn(wlandev->netdev,
+ "Attempt to set odd number FragmentationThreshold\n");
+ msg->resultcode.data =
+ P80211ENUM_resultcode_not_supported;
+ return 0;
+ }
+
+ result = prism2mib_uint32(mib, isget, wlandev, hw, msg, data);
+
+ return result;
+}
+
+/*----------------------------------------------------------------
+* prism2mib_priv
+*
+* Get/set values in the "priv" data structure.
+*
+* MIB record parameters:
+* parm1 Not used.
+* parm2 Not used.
+* parm3 Not used.
+*
+* Arguments:
+* mib MIB record.
+* isget MIBGET/MIBSET flag.
+* wlandev wlan device structure.
+* priv "priv" structure.
+* hw "hw" structure.
+* msg Message structure.
+* data Data buffer.
+*
+* Returns:
+* 0 - Success.
+* ~0 - Error.
+*
+----------------------------------------------------------------*/
+
+static int prism2mib_priv(struct mibrec *mib,
+ int isget,
+ wlandevice_t *wlandev,
+ hfa384x_t *hw,
+ struct p80211msg_dot11req_mibset *msg, void *data)
+{
+ p80211pstrd_t *pstr = (p80211pstrd_t *) data;
+
+ switch (mib->did) {
+ case DIDmib_lnx_lnxConfigTable_lnxRSNAIE:{
+ hfa384x_WPAData_t wpa;
+
+ if (isget) {
+ hfa384x_drvr_getconfig(hw,
+ HFA384x_RID_CNFWPADATA,
+ (u8 *) &wpa,
+ sizeof(wpa));
+ pstr->len = le16_to_cpu(wpa.datalen);
+ memcpy(pstr->data, wpa.data, pstr->len);
+ } else {
+ wpa.datalen = cpu_to_le16(pstr->len);
+ memcpy(wpa.data, pstr->data, pstr->len);
+
+ hfa384x_drvr_setconfig(hw,
+ HFA384x_RID_CNFWPADATA,
+ (u8 *) &wpa,
+ sizeof(wpa));
+ }
+ break;
+ }
+ default:
+ netdev_err(wlandev->netdev, "Unhandled DID 0x%08x\n", mib->did);
+ }
+
+ return 0;
+}
+
+/*----------------------------------------------------------------
+* prism2mgmt_pstr2bytestr
+*
+* Convert the pstr data in the WLAN message structure into an hfa384x
+* byte string format.
+*
+* Arguments:
+* bytestr hfa384x byte string data type
+* pstr wlan message data
+*
+* Returns:
+* Nothing
+*
+----------------------------------------------------------------*/
+
+void prism2mgmt_pstr2bytestr(struct hfa384x_bytestr *bytestr,
+ p80211pstrd_t *pstr)
+{
+ bytestr->len = cpu_to_le16((u16) (pstr->len));
+ memcpy(bytestr->data, pstr->data, pstr->len);
+}
+
+/*----------------------------------------------------------------
+* prism2mgmt_pstr2bytearea
+*
+* Convert the pstr data in the WLAN message structure into an hfa384x
+* byte area format.
+*
+* Arguments:
+* bytearea hfa384x byte area data type
+* pstr wlan message data
+*
+* Returns:
+* Nothing
+*
+----------------------------------------------------------------*/
+
+void prism2mgmt_pstr2bytearea(u8 *bytearea, p80211pstrd_t *pstr)
+{
+ memcpy(bytearea, pstr->data, pstr->len);
+}
+
+/*----------------------------------------------------------------
+* prism2mgmt_bytestr2pstr
+*
+* Convert the data in an hfa384x byte string format into a
+* pstr in the WLAN message.
+*
+* Arguments:
+* bytestr hfa384x byte string data type
+* msg wlan message
+*
+* Returns:
+* Nothing
+*
+----------------------------------------------------------------*/
+
+void prism2mgmt_bytestr2pstr(struct hfa384x_bytestr *bytestr,
+ p80211pstrd_t *pstr)
+{
+ pstr->len = (u8) (le16_to_cpu((u16) (bytestr->len)));
+ memcpy(pstr->data, bytestr->data, pstr->len);
+}
+
+/*----------------------------------------------------------------
+* prism2mgmt_bytearea2pstr
+*
+* Convert the data in an hfa384x byte area format into a pstr
+* in the WLAN message.
+*
+* Arguments:
+* bytearea hfa384x byte area data type
+* msg wlan message
+*
+* Returns:
+* Nothing
+*
+----------------------------------------------------------------*/
+
+void prism2mgmt_bytearea2pstr(u8 *bytearea, p80211pstrd_t *pstr, int len)
+{
+ pstr->len = (u8) len;
+ memcpy(pstr->data, bytearea, len);
+}
diff --git a/drivers/staging/wlan-ng/prism2sta.c b/drivers/staging/wlan-ng/prism2sta.c
new file mode 100644
index 000000000..ddb294e70
--- /dev/null
+++ b/drivers/staging/wlan-ng/prism2sta.c
@@ -0,0 +1,2018 @@
+/* src/prism2/driver/prism2sta.c
+*
+* Implements the station functionality for prism2
+*
+* Copyright (C) 1999 AbsoluteValue Systems, Inc. All Rights Reserved.
+* --------------------------------------------------------------------
+*
+* linux-wlan
+*
+* The contents of this file are subject to the Mozilla Public
+* License Version 1.1 (the "License"); you may not use this file
+* except in compliance with the License. You may obtain a copy of
+* the License at http://www.mozilla.org/MPL/
+*
+* Software distributed under the License is distributed on an "AS
+* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+* implied. See the License for the specific language governing
+* rights and limitations under the License.
+*
+* Alternatively, the contents of this file may be used under the
+* terms of the GNU Public License version 2 (the "GPL"), in which
+* case the provisions of the GPL are applicable instead of the
+* above. If you wish to allow the use of your version of this file
+* only under the terms of the GPL and not to allow others to use
+* your version of this file under the MPL, indicate your decision
+* by deleting the provisions above and replace them with the notice
+* and other provisions required by the GPL. If you do not delete
+* the provisions above, a recipient may use your version of this
+* file under either the MPL or the GPL.
+*
+* --------------------------------------------------------------------
+*
+* Inquiries regarding the linux-wlan Open Source project can be
+* made directly to:
+*
+* AbsoluteValue Systems Inc.
+* info@linux-wlan.com
+* http://www.linux-wlan.com
+*
+* --------------------------------------------------------------------
+*
+* Portions of the development of this software were funded by
+* Intersil Corporation as part of PRISM(R) chipset product development.
+*
+* --------------------------------------------------------------------
+*
+* This file implements the module and linux pcmcia routines for the
+* prism2 driver.
+*
+* --------------------------------------------------------------------
+*/
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/types.h>
+#include <linux/slab.h>
+#include <linux/wireless.h>
+#include <linux/netdevice.h>
+#include <linux/workqueue.h>
+#include <linux/byteorder/generic.h>
+#include <linux/etherdevice.h>
+
+#include <linux/io.h>
+#include <linux/delay.h>
+#include <asm/byteorder.h>
+#include <linux/if_arp.h>
+#include <linux/if_ether.h>
+#include <linux/bitops.h>
+
+#include "p80211types.h"
+#include "p80211hdr.h"
+#include "p80211mgmt.h"
+#include "p80211conv.h"
+#include "p80211msg.h"
+#include "p80211netdev.h"
+#include "p80211req.h"
+#include "p80211metadef.h"
+#include "p80211metastruct.h"
+#include "hfa384x.h"
+#include "prism2mgmt.h"
+
+static char *dev_info = "prism2_usb";
+static wlandevice_t *create_wlan(void);
+
+int prism2_reset_holdtime = 30; /* Reset hold time in ms */
+int prism2_reset_settletime = 100; /* Reset settle time in ms */
+
+static int prism2_doreset; /* Do a reset at init? */
+
+module_param(prism2_doreset, int, 0644);
+MODULE_PARM_DESC(prism2_doreset, "Issue a reset on initialization");
+
+module_param(prism2_reset_holdtime, int, 0644);
+MODULE_PARM_DESC(prism2_reset_holdtime, "reset hold time in ms");
+module_param(prism2_reset_settletime, int, 0644);
+MODULE_PARM_DESC(prism2_reset_settletime, "reset settle time in ms");
+
+MODULE_LICENSE("Dual MPL/GPL");
+
+static int prism2sta_open(wlandevice_t *wlandev);
+static int prism2sta_close(wlandevice_t *wlandev);
+static void prism2sta_reset(wlandevice_t *wlandev);
+static int prism2sta_txframe(wlandevice_t *wlandev, struct sk_buff *skb,
+ union p80211_hdr *p80211_hdr,
+ struct p80211_metawep *p80211_wep);
+static int prism2sta_mlmerequest(wlandevice_t *wlandev, struct p80211msg *msg);
+static int prism2sta_getcardinfo(wlandevice_t *wlandev);
+static int prism2sta_globalsetup(wlandevice_t *wlandev);
+static int prism2sta_setmulticast(wlandevice_t *wlandev, netdevice_t *dev);
+
+static void prism2sta_inf_handover(wlandevice_t *wlandev,
+ hfa384x_InfFrame_t *inf);
+static void prism2sta_inf_tallies(wlandevice_t *wlandev,
+ hfa384x_InfFrame_t *inf);
+static void prism2sta_inf_hostscanresults(wlandevice_t *wlandev,
+ hfa384x_InfFrame_t *inf);
+static void prism2sta_inf_scanresults(wlandevice_t *wlandev,
+ hfa384x_InfFrame_t *inf);
+static void prism2sta_inf_chinforesults(wlandevice_t *wlandev,
+ hfa384x_InfFrame_t *inf);
+static void prism2sta_inf_linkstatus(wlandevice_t *wlandev,
+ hfa384x_InfFrame_t *inf);
+static void prism2sta_inf_assocstatus(wlandevice_t *wlandev,
+ hfa384x_InfFrame_t *inf);
+static void prism2sta_inf_authreq(wlandevice_t *wlandev,
+ hfa384x_InfFrame_t *inf);
+static void prism2sta_inf_authreq_defer(wlandevice_t *wlandev,
+ hfa384x_InfFrame_t *inf);
+static void prism2sta_inf_psusercnt(wlandevice_t *wlandev,
+ hfa384x_InfFrame_t *inf);
+
+/*----------------------------------------------------------------
+* prism2sta_open
+*
+* WLAN device open method. Called from p80211netdev when kernel
+* device open (start) method is called in response to the
+* SIOCSIIFFLAGS ioctl changing the flags bit IFF_UP
+* from clear to set.
+*
+* Arguments:
+* wlandev wlan device structure
+*
+* Returns:
+* 0 success
+* >0 f/w reported error
+* <0 driver reported error
+*
+* Side effects:
+*
+* Call context:
+* process thread
+----------------------------------------------------------------*/
+static int prism2sta_open(wlandevice_t *wlandev)
+{
+ /* We don't currently have to do anything else.
+ * The setup of the MAC should be subsequently completed via
+ * the mlme commands.
+ * Higher layers know we're ready from dev->start==1 and
+ * dev->tbusy==0. Our rx path knows to pass up received/
+ * frames because of dev->flags&IFF_UP is true.
+ */
+
+ return 0;
+}
+
+/*----------------------------------------------------------------
+* prism2sta_close
+*
+* WLAN device close method. Called from p80211netdev when kernel
+* device close method is called in response to the
+* SIOCSIIFFLAGS ioctl changing the flags bit IFF_UP
+* from set to clear.
+*
+* Arguments:
+* wlandev wlan device structure
+*
+* Returns:
+* 0 success
+* >0 f/w reported error
+* <0 driver reported error
+*
+* Side effects:
+*
+* Call context:
+* process thread
+----------------------------------------------------------------*/
+static int prism2sta_close(wlandevice_t *wlandev)
+{
+ /* We don't currently have to do anything else.
+ * Higher layers know we're not ready from dev->start==0 and
+ * dev->tbusy==1. Our rx path knows to not pass up received
+ * frames because of dev->flags&IFF_UP is false.
+ */
+
+ return 0;
+}
+
+/*----------------------------------------------------------------
+* prism2sta_reset
+*
+* Currently not implemented.
+*
+* Arguments:
+* wlandev wlan device structure
+* none
+*
+* Returns:
+* nothing
+*
+* Side effects:
+*
+* Call context:
+* process thread
+----------------------------------------------------------------*/
+static void prism2sta_reset(wlandevice_t *wlandev)
+{
+}
+
+/*----------------------------------------------------------------
+* prism2sta_txframe
+*
+* Takes a frame from p80211 and queues it for transmission.
+*
+* Arguments:
+* wlandev wlan device structure
+* pb packet buffer struct. Contains an 802.11
+* data frame.
+* p80211_hdr points to the 802.11 header for the packet.
+* Returns:
+* 0 Success and more buffs available
+* 1 Success but no more buffs
+* 2 Allocation failure
+* 4 Buffer full or queue busy
+*
+* Side effects:
+*
+* Call context:
+* process thread
+----------------------------------------------------------------*/
+static int prism2sta_txframe(wlandevice_t *wlandev, struct sk_buff *skb,
+ union p80211_hdr *p80211_hdr,
+ struct p80211_metawep *p80211_wep)
+{
+ hfa384x_t *hw = (hfa384x_t *) wlandev->priv;
+
+ /* If necessary, set the 802.11 WEP bit */
+ if ((wlandev->hostwep & (HOSTWEP_PRIVACYINVOKED | HOSTWEP_ENCRYPT)) ==
+ HOSTWEP_PRIVACYINVOKED) {
+ p80211_hdr->a3.fc |= cpu_to_le16(WLAN_SET_FC_ISWEP(1));
+ }
+
+ return hfa384x_drvr_txframe(hw, skb, p80211_hdr, p80211_wep);
+}
+
+/*----------------------------------------------------------------
+* prism2sta_mlmerequest
+*
+* wlan command message handler. All we do here is pass the message
+* over to the prism2sta_mgmt_handler.
+*
+* Arguments:
+* wlandev wlan device structure
+* msg wlan command message
+* Returns:
+* 0 success
+* <0 successful acceptance of message, but we're
+* waiting for an async process to finish before
+* we're done with the msg. When the asynch
+* process is done, we'll call the p80211
+* function p80211req_confirm() .
+* >0 An error occurred while we were handling
+* the message.
+*
+* Side effects:
+*
+* Call context:
+* process thread
+----------------------------------------------------------------*/
+static int prism2sta_mlmerequest(wlandevice_t *wlandev, struct p80211msg *msg)
+{
+ hfa384x_t *hw = (hfa384x_t *) wlandev->priv;
+
+ int result = 0;
+
+ switch (msg->msgcode) {
+ case DIDmsg_dot11req_mibget:
+ pr_debug("Received mibget request\n");
+ result = prism2mgmt_mibset_mibget(wlandev, msg);
+ break;
+ case DIDmsg_dot11req_mibset:
+ pr_debug("Received mibset request\n");
+ result = prism2mgmt_mibset_mibget(wlandev, msg);
+ break;
+ case DIDmsg_dot11req_scan:
+ pr_debug("Received scan request\n");
+ result = prism2mgmt_scan(wlandev, msg);
+ break;
+ case DIDmsg_dot11req_scan_results:
+ pr_debug("Received scan_results request\n");
+ result = prism2mgmt_scan_results(wlandev, msg);
+ break;
+ case DIDmsg_dot11req_start:
+ pr_debug("Received mlme start request\n");
+ result = prism2mgmt_start(wlandev, msg);
+ break;
+ /*
+ * Prism2 specific messages
+ */
+ case DIDmsg_p2req_readpda:
+ pr_debug("Received mlme readpda request\n");
+ result = prism2mgmt_readpda(wlandev, msg);
+ break;
+ case DIDmsg_p2req_ramdl_state:
+ pr_debug("Received mlme ramdl_state request\n");
+ result = prism2mgmt_ramdl_state(wlandev, msg);
+ break;
+ case DIDmsg_p2req_ramdl_write:
+ pr_debug("Received mlme ramdl_write request\n");
+ result = prism2mgmt_ramdl_write(wlandev, msg);
+ break;
+ case DIDmsg_p2req_flashdl_state:
+ pr_debug("Received mlme flashdl_state request\n");
+ result = prism2mgmt_flashdl_state(wlandev, msg);
+ break;
+ case DIDmsg_p2req_flashdl_write:
+ pr_debug("Received mlme flashdl_write request\n");
+ result = prism2mgmt_flashdl_write(wlandev, msg);
+ break;
+ /*
+ * Linux specific messages
+ */
+ case DIDmsg_lnxreq_hostwep:
+ break; /* ignore me. */
+ case DIDmsg_lnxreq_ifstate:
+ {
+ struct p80211msg_lnxreq_ifstate *ifstatemsg;
+
+ pr_debug("Received mlme ifstate request\n");
+ ifstatemsg = (struct p80211msg_lnxreq_ifstate *) msg;
+ result =
+ prism2sta_ifstate(wlandev,
+ ifstatemsg->ifstate.data);
+ ifstatemsg->resultcode.status =
+ P80211ENUM_msgitem_status_data_ok;
+ ifstatemsg->resultcode.data = result;
+ result = 0;
+ }
+ break;
+ case DIDmsg_lnxreq_wlansniff:
+ pr_debug("Received mlme wlansniff request\n");
+ result = prism2mgmt_wlansniff(wlandev, msg);
+ break;
+ case DIDmsg_lnxreq_autojoin:
+ pr_debug("Received mlme autojoin request\n");
+ result = prism2mgmt_autojoin(wlandev, msg);
+ break;
+ case DIDmsg_lnxreq_commsquality:{
+ struct p80211msg_lnxreq_commsquality *qualmsg;
+
+ pr_debug("Received commsquality request\n");
+
+ qualmsg = (struct p80211msg_lnxreq_commsquality *) msg;
+
+ qualmsg->link.status =
+ P80211ENUM_msgitem_status_data_ok;
+ qualmsg->level.status =
+ P80211ENUM_msgitem_status_data_ok;
+ qualmsg->noise.status =
+ P80211ENUM_msgitem_status_data_ok;
+
+ qualmsg->link.data = le16_to_cpu(hw->qual.CQ_currBSS);
+ qualmsg->level.data = le16_to_cpu(hw->qual.ASL_currBSS);
+ qualmsg->noise.data = le16_to_cpu(hw->qual.ANL_currFC);
+ qualmsg->txrate.data = hw->txrate;
+
+ break;
+ }
+ default:
+ netdev_warn(wlandev->netdev,
+ "Unknown mgmt request message 0x%08x",
+ msg->msgcode);
+ break;
+ }
+
+ return result;
+}
+
+/*----------------------------------------------------------------
+* prism2sta_ifstate
+*
+* Interface state. This is the primary WLAN interface enable/disable
+* handler. Following the driver/load/deviceprobe sequence, this
+* function must be called with a state of "enable" before any other
+* commands will be accepted.
+*
+* Arguments:
+* wlandev wlan device structure
+* msgp ptr to msg buffer
+*
+* Returns:
+* A p80211 message resultcode value.
+*
+* Side effects:
+*
+* Call context:
+* process thread (usually)
+* interrupt
+----------------------------------------------------------------*/
+u32 prism2sta_ifstate(wlandevice_t *wlandev, u32 ifstate)
+{
+ hfa384x_t *hw = (hfa384x_t *) wlandev->priv;
+ u32 result;
+
+ result = P80211ENUM_resultcode_implementation_failure;
+
+ pr_debug("Current MSD state(%d), requesting(%d)\n",
+ wlandev->msdstate, ifstate);
+ switch (ifstate) {
+ case P80211ENUM_ifstate_fwload:
+ switch (wlandev->msdstate) {
+ case WLAN_MSD_HWPRESENT:
+ wlandev->msdstate = WLAN_MSD_FWLOAD_PENDING;
+ /*
+ * Initialize the device+driver sufficiently
+ * for firmware loading.
+ */
+ result = hfa384x_drvr_start(hw);
+ if (result) {
+ netdev_err(wlandev->netdev,
+ "hfa384x_drvr_start() failed,result=%d\n", (int)result);
+ result =
+ P80211ENUM_resultcode_implementation_failure;
+ wlandev->msdstate = WLAN_MSD_HWPRESENT;
+ break;
+ }
+ wlandev->msdstate = WLAN_MSD_FWLOAD;
+ result = P80211ENUM_resultcode_success;
+ break;
+ case WLAN_MSD_FWLOAD:
+ hfa384x_cmd_initialize(hw);
+ result = P80211ENUM_resultcode_success;
+ break;
+ case WLAN_MSD_RUNNING:
+ netdev_warn(wlandev->netdev,
+ "Cannot enter fwload state from enable state, you must disable first.\n");
+ result = P80211ENUM_resultcode_invalid_parameters;
+ break;
+ case WLAN_MSD_HWFAIL:
+ default:
+ /* probe() had a problem or the msdstate contains
+ * an unrecognized value, there's nothing we can do.
+ */
+ result = P80211ENUM_resultcode_implementation_failure;
+ break;
+ }
+ break;
+ case P80211ENUM_ifstate_enable:
+ switch (wlandev->msdstate) {
+ case WLAN_MSD_HWPRESENT:
+ case WLAN_MSD_FWLOAD:
+ wlandev->msdstate = WLAN_MSD_RUNNING_PENDING;
+ /* Initialize the device+driver for full
+ * operation. Note that this might me an FWLOAD to
+ * to RUNNING transition so we must not do a chip
+ * or board level reset. Note that on failure,
+ * the MSD state is set to HWPRESENT because we
+ * can't make any assumptions about the state
+ * of the hardware or a previous firmware load.
+ */
+ result = hfa384x_drvr_start(hw);
+ if (result) {
+ netdev_err(wlandev->netdev,
+ "hfa384x_drvr_start() failed,result=%d\n", (int)result);
+ result =
+ P80211ENUM_resultcode_implementation_failure;
+ wlandev->msdstate = WLAN_MSD_HWPRESENT;
+ break;
+ }
+
+ result = prism2sta_getcardinfo(wlandev);
+ if (result) {
+ netdev_err(wlandev->netdev,
+ "prism2sta_getcardinfo() failed,result=%d\n", (int)result);
+ result =
+ P80211ENUM_resultcode_implementation_failure;
+ hfa384x_drvr_stop(hw);
+ wlandev->msdstate = WLAN_MSD_HWPRESENT;
+ break;
+ }
+ result = prism2sta_globalsetup(wlandev);
+ if (result) {
+ netdev_err(wlandev->netdev,
+ "prism2sta_globalsetup() failed,result=%d\n", (int)result);
+ result =
+ P80211ENUM_resultcode_implementation_failure;
+ hfa384x_drvr_stop(hw);
+ wlandev->msdstate = WLAN_MSD_HWPRESENT;
+ break;
+ }
+ wlandev->msdstate = WLAN_MSD_RUNNING;
+ hw->join_ap = 0;
+ hw->join_retries = 60;
+ result = P80211ENUM_resultcode_success;
+ break;
+ case WLAN_MSD_RUNNING:
+ /* Do nothing, we're already in this state. */
+ result = P80211ENUM_resultcode_success;
+ break;
+ case WLAN_MSD_HWFAIL:
+ default:
+ /* probe() had a problem or the msdstate contains
+ * an unrecognized value, there's nothing we can do.
+ */
+ result = P80211ENUM_resultcode_implementation_failure;
+ break;
+ }
+ break;
+ case P80211ENUM_ifstate_disable:
+ switch (wlandev->msdstate) {
+ case WLAN_MSD_HWPRESENT:
+ /* Do nothing, we're already in this state. */
+ result = P80211ENUM_resultcode_success;
+ break;
+ case WLAN_MSD_FWLOAD:
+ case WLAN_MSD_RUNNING:
+ wlandev->msdstate = WLAN_MSD_HWPRESENT_PENDING;
+ /*
+ * TODO: Shut down the MAC completely. Here a chip
+ * or board level reset is probably called for.
+ * After a "disable" _all_ results are lost, even
+ * those from a fwload.
+ */
+ if (!wlandev->hwremoved)
+ netif_carrier_off(wlandev->netdev);
+
+ hfa384x_drvr_stop(hw);
+
+ wlandev->macmode = WLAN_MACMODE_NONE;
+ wlandev->msdstate = WLAN_MSD_HWPRESENT;
+ result = P80211ENUM_resultcode_success;
+ break;
+ case WLAN_MSD_HWFAIL:
+ default:
+ /* probe() had a problem or the msdstate contains
+ * an unrecognized value, there's nothing we can do.
+ */
+ result = P80211ENUM_resultcode_implementation_failure;
+ break;
+ }
+ break;
+ default:
+ result = P80211ENUM_resultcode_invalid_parameters;
+ break;
+ }
+
+ return result;
+}
+
+/*----------------------------------------------------------------
+* prism2sta_getcardinfo
+*
+* Collect the NICID, firmware version and any other identifiers
+* we'd like to have in host-side data structures.
+*
+* Arguments:
+* wlandev wlan device structure
+*
+* Returns:
+* 0 success
+* >0 f/w reported error
+* <0 driver reported error
+*
+* Side effects:
+*
+* Call context:
+* Either.
+----------------------------------------------------------------*/
+static int prism2sta_getcardinfo(wlandevice_t *wlandev)
+{
+ int result = 0;
+ hfa384x_t *hw = (hfa384x_t *) wlandev->priv;
+ u16 temp;
+ u8 snum[HFA384x_RID_NICSERIALNUMBER_LEN];
+
+ /* Collect version and compatibility info */
+ /* Some are critical, some are not */
+ /* NIC identity */
+ result = hfa384x_drvr_getconfig(hw, HFA384x_RID_NICIDENTITY,
+ &hw->ident_nic,
+ sizeof(hfa384x_compident_t));
+ if (result) {
+ netdev_err(wlandev->netdev, "Failed to retrieve NICIDENTITY\n");
+ goto failed;
+ }
+
+ /* get all the nic id fields in host byte order */
+ hw->ident_nic.id = le16_to_cpu(hw->ident_nic.id);
+ hw->ident_nic.variant = le16_to_cpu(hw->ident_nic.variant);
+ hw->ident_nic.major = le16_to_cpu(hw->ident_nic.major);
+ hw->ident_nic.minor = le16_to_cpu(hw->ident_nic.minor);
+
+ netdev_info(wlandev->netdev, "ident: nic h/w: id=0x%02x %d.%d.%d\n",
+ hw->ident_nic.id, hw->ident_nic.major,
+ hw->ident_nic.minor, hw->ident_nic.variant);
+
+ /* Primary f/w identity */
+ result = hfa384x_drvr_getconfig(hw, HFA384x_RID_PRIIDENTITY,
+ &hw->ident_pri_fw,
+ sizeof(hfa384x_compident_t));
+ if (result) {
+ netdev_err(wlandev->netdev, "Failed to retrieve PRIIDENTITY\n");
+ goto failed;
+ }
+
+ /* get all the private fw id fields in host byte order */
+ hw->ident_pri_fw.id = le16_to_cpu(hw->ident_pri_fw.id);
+ hw->ident_pri_fw.variant = le16_to_cpu(hw->ident_pri_fw.variant);
+ hw->ident_pri_fw.major = le16_to_cpu(hw->ident_pri_fw.major);
+ hw->ident_pri_fw.minor = le16_to_cpu(hw->ident_pri_fw.minor);
+
+ netdev_info(wlandev->netdev, "ident: pri f/w: id=0x%02x %d.%d.%d\n",
+ hw->ident_pri_fw.id, hw->ident_pri_fw.major,
+ hw->ident_pri_fw.minor, hw->ident_pri_fw.variant);
+
+ /* Station (Secondary?) f/w identity */
+ result = hfa384x_drvr_getconfig(hw, HFA384x_RID_STAIDENTITY,
+ &hw->ident_sta_fw,
+ sizeof(hfa384x_compident_t));
+ if (result) {
+ netdev_err(wlandev->netdev, "Failed to retrieve STAIDENTITY\n");
+ goto failed;
+ }
+
+ if (hw->ident_nic.id < 0x8000) {
+ netdev_err(wlandev->netdev,
+ "FATAL: Card is not an Intersil Prism2/2.5/3\n");
+ result = -1;
+ goto failed;
+ }
+
+ /* get all the station fw id fields in host byte order */
+ hw->ident_sta_fw.id = le16_to_cpu(hw->ident_sta_fw.id);
+ hw->ident_sta_fw.variant = le16_to_cpu(hw->ident_sta_fw.variant);
+ hw->ident_sta_fw.major = le16_to_cpu(hw->ident_sta_fw.major);
+ hw->ident_sta_fw.minor = le16_to_cpu(hw->ident_sta_fw.minor);
+
+ /* strip out the 'special' variant bits */
+ hw->mm_mods = hw->ident_sta_fw.variant & (BIT(14) | BIT(15));
+ hw->ident_sta_fw.variant &= ~((u16) (BIT(14) | BIT(15)));
+
+ if (hw->ident_sta_fw.id == 0x1f) {
+ netdev_info(wlandev->netdev,
+ "ident: sta f/w: id=0x%02x %d.%d.%d\n",
+ hw->ident_sta_fw.id, hw->ident_sta_fw.major,
+ hw->ident_sta_fw.minor, hw->ident_sta_fw.variant);
+ } else {
+ netdev_info(wlandev->netdev,
+ "ident: ap f/w: id=0x%02x %d.%d.%d\n",
+ hw->ident_sta_fw.id, hw->ident_sta_fw.major,
+ hw->ident_sta_fw.minor, hw->ident_sta_fw.variant);
+ netdev_err(wlandev->netdev, "Unsupported Tertiary AP firmware loaded!\n");
+ goto failed;
+ }
+
+ /* Compatibility range, Modem supplier */
+ result = hfa384x_drvr_getconfig(hw, HFA384x_RID_MFISUPRANGE,
+ &hw->cap_sup_mfi,
+ sizeof(hfa384x_caplevel_t));
+ if (result) {
+ netdev_err(wlandev->netdev, "Failed to retrieve MFISUPRANGE\n");
+ goto failed;
+ }
+
+ /* get all the Compatibility range, modem interface supplier
+ fields in byte order */
+ hw->cap_sup_mfi.role = le16_to_cpu(hw->cap_sup_mfi.role);
+ hw->cap_sup_mfi.id = le16_to_cpu(hw->cap_sup_mfi.id);
+ hw->cap_sup_mfi.variant = le16_to_cpu(hw->cap_sup_mfi.variant);
+ hw->cap_sup_mfi.bottom = le16_to_cpu(hw->cap_sup_mfi.bottom);
+ hw->cap_sup_mfi.top = le16_to_cpu(hw->cap_sup_mfi.top);
+
+ netdev_info(wlandev->netdev,
+ "MFI:SUP:role=0x%02x:id=0x%02x:var=0x%02x:b/t=%d/%d\n",
+ hw->cap_sup_mfi.role, hw->cap_sup_mfi.id,
+ hw->cap_sup_mfi.variant, hw->cap_sup_mfi.bottom,
+ hw->cap_sup_mfi.top);
+
+ /* Compatibility range, Controller supplier */
+ result = hfa384x_drvr_getconfig(hw, HFA384x_RID_CFISUPRANGE,
+ &hw->cap_sup_cfi,
+ sizeof(hfa384x_caplevel_t));
+ if (result) {
+ netdev_err(wlandev->netdev, "Failed to retrieve CFISUPRANGE\n");
+ goto failed;
+ }
+
+ /* get all the Compatibility range, controller interface supplier
+ fields in byte order */
+ hw->cap_sup_cfi.role = le16_to_cpu(hw->cap_sup_cfi.role);
+ hw->cap_sup_cfi.id = le16_to_cpu(hw->cap_sup_cfi.id);
+ hw->cap_sup_cfi.variant = le16_to_cpu(hw->cap_sup_cfi.variant);
+ hw->cap_sup_cfi.bottom = le16_to_cpu(hw->cap_sup_cfi.bottom);
+ hw->cap_sup_cfi.top = le16_to_cpu(hw->cap_sup_cfi.top);
+
+ netdev_info(wlandev->netdev,
+ "CFI:SUP:role=0x%02x:id=0x%02x:var=0x%02x:b/t=%d/%d\n",
+ hw->cap_sup_cfi.role, hw->cap_sup_cfi.id,
+ hw->cap_sup_cfi.variant, hw->cap_sup_cfi.bottom,
+ hw->cap_sup_cfi.top);
+
+ /* Compatibility range, Primary f/w supplier */
+ result = hfa384x_drvr_getconfig(hw, HFA384x_RID_PRISUPRANGE,
+ &hw->cap_sup_pri,
+ sizeof(hfa384x_caplevel_t));
+ if (result) {
+ netdev_err(wlandev->netdev, "Failed to retrieve PRISUPRANGE\n");
+ goto failed;
+ }
+
+ /* get all the Compatibility range, primary firmware supplier
+ fields in byte order */
+ hw->cap_sup_pri.role = le16_to_cpu(hw->cap_sup_pri.role);
+ hw->cap_sup_pri.id = le16_to_cpu(hw->cap_sup_pri.id);
+ hw->cap_sup_pri.variant = le16_to_cpu(hw->cap_sup_pri.variant);
+ hw->cap_sup_pri.bottom = le16_to_cpu(hw->cap_sup_pri.bottom);
+ hw->cap_sup_pri.top = le16_to_cpu(hw->cap_sup_pri.top);
+
+ netdev_info(wlandev->netdev,
+ "PRI:SUP:role=0x%02x:id=0x%02x:var=0x%02x:b/t=%d/%d\n",
+ hw->cap_sup_pri.role, hw->cap_sup_pri.id,
+ hw->cap_sup_pri.variant, hw->cap_sup_pri.bottom,
+ hw->cap_sup_pri.top);
+
+ /* Compatibility range, Station f/w supplier */
+ result = hfa384x_drvr_getconfig(hw, HFA384x_RID_STASUPRANGE,
+ &hw->cap_sup_sta,
+ sizeof(hfa384x_caplevel_t));
+ if (result) {
+ netdev_err(wlandev->netdev, "Failed to retrieve STASUPRANGE\n");
+ goto failed;
+ }
+
+ /* get all the Compatibility range, station firmware supplier
+ fields in byte order */
+ hw->cap_sup_sta.role = le16_to_cpu(hw->cap_sup_sta.role);
+ hw->cap_sup_sta.id = le16_to_cpu(hw->cap_sup_sta.id);
+ hw->cap_sup_sta.variant = le16_to_cpu(hw->cap_sup_sta.variant);
+ hw->cap_sup_sta.bottom = le16_to_cpu(hw->cap_sup_sta.bottom);
+ hw->cap_sup_sta.top = le16_to_cpu(hw->cap_sup_sta.top);
+
+ if (hw->cap_sup_sta.id == 0x04) {
+ netdev_info(wlandev->netdev,
+ "STA:SUP:role=0x%02x:id=0x%02x:var=0x%02x:b/t=%d/%d\n",
+ hw->cap_sup_sta.role, hw->cap_sup_sta.id,
+ hw->cap_sup_sta.variant, hw->cap_sup_sta.bottom,
+ hw->cap_sup_sta.top);
+ } else {
+ netdev_info(wlandev->netdev,
+ "AP:SUP:role=0x%02x:id=0x%02x:var=0x%02x:b/t=%d/%d\n",
+ hw->cap_sup_sta.role, hw->cap_sup_sta.id,
+ hw->cap_sup_sta.variant, hw->cap_sup_sta.bottom,
+ hw->cap_sup_sta.top);
+ }
+
+ /* Compatibility range, primary f/w actor, CFI supplier */
+ result = hfa384x_drvr_getconfig(hw, HFA384x_RID_PRI_CFIACTRANGES,
+ &hw->cap_act_pri_cfi,
+ sizeof(hfa384x_caplevel_t));
+ if (result) {
+ netdev_err(wlandev->netdev, "Failed to retrieve PRI_CFIACTRANGES\n");
+ goto failed;
+ }
+
+ /* get all the Compatibility range, primary f/w actor, CFI supplier
+ fields in byte order */
+ hw->cap_act_pri_cfi.role = le16_to_cpu(hw->cap_act_pri_cfi.role);
+ hw->cap_act_pri_cfi.id = le16_to_cpu(hw->cap_act_pri_cfi.id);
+ hw->cap_act_pri_cfi.variant = le16_to_cpu(hw->cap_act_pri_cfi.variant);
+ hw->cap_act_pri_cfi.bottom = le16_to_cpu(hw->cap_act_pri_cfi.bottom);
+ hw->cap_act_pri_cfi.top = le16_to_cpu(hw->cap_act_pri_cfi.top);
+
+ netdev_info(wlandev->netdev,
+ "PRI-CFI:ACT:role=0x%02x:id=0x%02x:var=0x%02x:b/t=%d/%d\n",
+ hw->cap_act_pri_cfi.role, hw->cap_act_pri_cfi.id,
+ hw->cap_act_pri_cfi.variant, hw->cap_act_pri_cfi.bottom,
+ hw->cap_act_pri_cfi.top);
+
+ /* Compatibility range, sta f/w actor, CFI supplier */
+ result = hfa384x_drvr_getconfig(hw, HFA384x_RID_STA_CFIACTRANGES,
+ &hw->cap_act_sta_cfi,
+ sizeof(hfa384x_caplevel_t));
+ if (result) {
+ netdev_err(wlandev->netdev, "Failed to retrieve STA_CFIACTRANGES\n");
+ goto failed;
+ }
+
+ /* get all the Compatibility range, station f/w actor, CFI supplier
+ fields in byte order */
+ hw->cap_act_sta_cfi.role = le16_to_cpu(hw->cap_act_sta_cfi.role);
+ hw->cap_act_sta_cfi.id = le16_to_cpu(hw->cap_act_sta_cfi.id);
+ hw->cap_act_sta_cfi.variant = le16_to_cpu(hw->cap_act_sta_cfi.variant);
+ hw->cap_act_sta_cfi.bottom = le16_to_cpu(hw->cap_act_sta_cfi.bottom);
+ hw->cap_act_sta_cfi.top = le16_to_cpu(hw->cap_act_sta_cfi.top);
+
+ netdev_info(wlandev->netdev,
+ "STA-CFI:ACT:role=0x%02x:id=0x%02x:var=0x%02x:b/t=%d/%d\n",
+ hw->cap_act_sta_cfi.role, hw->cap_act_sta_cfi.id,
+ hw->cap_act_sta_cfi.variant, hw->cap_act_sta_cfi.bottom,
+ hw->cap_act_sta_cfi.top);
+
+ /* Compatibility range, sta f/w actor, MFI supplier */
+ result = hfa384x_drvr_getconfig(hw, HFA384x_RID_STA_MFIACTRANGES,
+ &hw->cap_act_sta_mfi,
+ sizeof(hfa384x_caplevel_t));
+ if (result) {
+ netdev_err(wlandev->netdev, "Failed to retrieve STA_MFIACTRANGES\n");
+ goto failed;
+ }
+
+ /* get all the Compatibility range, station f/w actor, MFI supplier
+ fields in byte order */
+ hw->cap_act_sta_mfi.role = le16_to_cpu(hw->cap_act_sta_mfi.role);
+ hw->cap_act_sta_mfi.id = le16_to_cpu(hw->cap_act_sta_mfi.id);
+ hw->cap_act_sta_mfi.variant = le16_to_cpu(hw->cap_act_sta_mfi.variant);
+ hw->cap_act_sta_mfi.bottom = le16_to_cpu(hw->cap_act_sta_mfi.bottom);
+ hw->cap_act_sta_mfi.top = le16_to_cpu(hw->cap_act_sta_mfi.top);
+
+ netdev_info(wlandev->netdev,
+ "STA-MFI:ACT:role=0x%02x:id=0x%02x:var=0x%02x:b/t=%d/%d\n",
+ hw->cap_act_sta_mfi.role, hw->cap_act_sta_mfi.id,
+ hw->cap_act_sta_mfi.variant, hw->cap_act_sta_mfi.bottom,
+ hw->cap_act_sta_mfi.top);
+
+ /* Serial Number */
+ result = hfa384x_drvr_getconfig(hw, HFA384x_RID_NICSERIALNUMBER,
+ snum, HFA384x_RID_NICSERIALNUMBER_LEN);
+ if (!result) {
+ netdev_info(wlandev->netdev, "Prism2 card SN: %*pEhp\n",
+ HFA384x_RID_NICSERIALNUMBER_LEN, snum);
+ } else {
+ netdev_err(wlandev->netdev, "Failed to retrieve Prism2 Card SN\n");
+ goto failed;
+ }
+
+ /* Collect the MAC address */
+ result = hfa384x_drvr_getconfig(hw, HFA384x_RID_CNFOWNMACADDR,
+ wlandev->netdev->dev_addr, ETH_ALEN);
+ if (result != 0) {
+ netdev_err(wlandev->netdev, "Failed to retrieve mac address\n");
+ goto failed;
+ }
+
+ /* short preamble is always implemented */
+ wlandev->nsdcaps |= P80211_NSDCAP_SHORT_PREAMBLE;
+
+ /* find out if hardware wep is implemented */
+ hfa384x_drvr_getconfig16(hw, HFA384x_RID_PRIVACYOPTIMP, &temp);
+ if (temp)
+ wlandev->nsdcaps |= P80211_NSDCAP_HARDWAREWEP;
+
+ /* get the dBm Scaling constant */
+ hfa384x_drvr_getconfig16(hw, HFA384x_RID_CNFDBMADJUST, &temp);
+ hw->dbmadjust = temp;
+
+ /* Only enable scan by default on newer firmware */
+ if (HFA384x_FIRMWARE_VERSION(hw->ident_sta_fw.major,
+ hw->ident_sta_fw.minor,
+ hw->ident_sta_fw.variant) <
+ HFA384x_FIRMWARE_VERSION(1, 5, 5)) {
+ wlandev->nsdcaps |= P80211_NSDCAP_NOSCAN;
+ }
+
+ /* TODO: Set any internally managed config items */
+
+ goto done;
+failed:
+ netdev_err(wlandev->netdev, "Failed, result=%d\n", result);
+done:
+ return result;
+}
+
+/*----------------------------------------------------------------
+* prism2sta_globalsetup
+*
+* Set any global RIDs that we want to set at device activation.
+*
+* Arguments:
+* wlandev wlan device structure
+*
+* Returns:
+* 0 success
+* >0 f/w reported error
+* <0 driver reported error
+*
+* Side effects:
+*
+* Call context:
+* process thread
+----------------------------------------------------------------*/
+static int prism2sta_globalsetup(wlandevice_t *wlandev)
+{
+ hfa384x_t *hw = (hfa384x_t *) wlandev->priv;
+
+ /* Set the maximum frame size */
+ return hfa384x_drvr_setconfig16(hw, HFA384x_RID_CNFMAXDATALEN,
+ WLAN_DATA_MAXLEN);
+}
+
+static int prism2sta_setmulticast(wlandevice_t *wlandev, netdevice_t *dev)
+{
+ int result = 0;
+ hfa384x_t *hw = (hfa384x_t *) wlandev->priv;
+
+ u16 promisc;
+
+ /* If we're not ready, what's the point? */
+ if (hw->state != HFA384x_STATE_RUNNING)
+ goto exit;
+
+ if ((dev->flags & (IFF_PROMISC | IFF_ALLMULTI)) != 0)
+ promisc = P80211ENUM_truth_true;
+ else
+ promisc = P80211ENUM_truth_false;
+
+ result =
+ hfa384x_drvr_setconfig16_async(hw, HFA384x_RID_PROMISCMODE,
+ promisc);
+exit:
+ return result;
+}
+
+/*----------------------------------------------------------------
+* prism2sta_inf_handover
+*
+* Handles the receipt of a Handover info frame. Should only be present
+* in APs only.
+*
+* Arguments:
+* wlandev wlan device structure
+* inf ptr to info frame (contents in hfa384x order)
+*
+* Returns:
+* nothing
+*
+* Side effects:
+*
+* Call context:
+* interrupt
+----------------------------------------------------------------*/
+static void prism2sta_inf_handover(wlandevice_t *wlandev,
+ hfa384x_InfFrame_t *inf)
+{
+ pr_debug("received infoframe:HANDOVER (unhandled)\n");
+}
+
+/*----------------------------------------------------------------
+* prism2sta_inf_tallies
+*
+* Handles the receipt of a CommTallies info frame.
+*
+* Arguments:
+* wlandev wlan device structure
+* inf ptr to info frame (contents in hfa384x order)
+*
+* Returns:
+* nothing
+*
+* Side effects:
+*
+* Call context:
+* interrupt
+----------------------------------------------------------------*/
+static void prism2sta_inf_tallies(wlandevice_t *wlandev,
+ hfa384x_InfFrame_t *inf)
+{
+ hfa384x_t *hw = (hfa384x_t *) wlandev->priv;
+ u16 *src16;
+ u32 *dst;
+ u32 *src32;
+ int i;
+ int cnt;
+
+ /*
+ ** Determine if these are 16-bit or 32-bit tallies, based on the
+ ** record length of the info record.
+ */
+
+ cnt = sizeof(hfa384x_CommTallies32_t) / sizeof(u32);
+ if (inf->framelen > 22) {
+ dst = (u32 *) &hw->tallies;
+ src32 = (u32 *) &inf->info.commtallies32;
+ for (i = 0; i < cnt; i++, dst++, src32++)
+ *dst += le32_to_cpu(*src32);
+ } else {
+ dst = (u32 *) &hw->tallies;
+ src16 = (u16 *) &inf->info.commtallies16;
+ for (i = 0; i < cnt; i++, dst++, src16++)
+ *dst += le16_to_cpu(*src16);
+ }
+}
+
+/*----------------------------------------------------------------
+* prism2sta_inf_scanresults
+*
+* Handles the receipt of a Scan Results info frame.
+*
+* Arguments:
+* wlandev wlan device structure
+* inf ptr to info frame (contents in hfa384x order)
+*
+* Returns:
+* nothing
+*
+* Side effects:
+*
+* Call context:
+* interrupt
+----------------------------------------------------------------*/
+static void prism2sta_inf_scanresults(wlandevice_t *wlandev,
+ hfa384x_InfFrame_t *inf)
+{
+
+ hfa384x_t *hw = (hfa384x_t *) wlandev->priv;
+ int nbss;
+ hfa384x_ScanResult_t *sr = &(inf->info.scanresult);
+ int i;
+ hfa384x_JoinRequest_data_t joinreq;
+ int result;
+
+ /* Get the number of results, first in bytes, then in results */
+ nbss = (inf->framelen * sizeof(u16)) -
+ sizeof(inf->infotype) - sizeof(inf->info.scanresult.scanreason);
+ nbss /= sizeof(hfa384x_ScanResultSub_t);
+
+ /* Print em */
+ pr_debug("rx scanresults, reason=%d, nbss=%d:\n",
+ inf->info.scanresult.scanreason, nbss);
+ for (i = 0; i < nbss; i++) {
+ pr_debug("chid=%d anl=%d sl=%d bcnint=%d\n",
+ sr->result[i].chid,
+ sr->result[i].anl,
+ sr->result[i].sl, sr->result[i].bcnint);
+ pr_debug(" capinfo=0x%04x proberesp_rate=%d\n",
+ sr->result[i].capinfo, sr->result[i].proberesp_rate);
+ }
+ /* issue a join request */
+ joinreq.channel = sr->result[0].chid;
+ memcpy(joinreq.bssid, sr->result[0].bssid, WLAN_BSSID_LEN);
+ result = hfa384x_drvr_setconfig(hw,
+ HFA384x_RID_JOINREQUEST,
+ &joinreq, HFA384x_RID_JOINREQUEST_LEN);
+ if (result) {
+ netdev_err(wlandev->netdev, "setconfig(joinreq) failed, result=%d\n",
+ result);
+ }
+}
+
+/*----------------------------------------------------------------
+* prism2sta_inf_hostscanresults
+*
+* Handles the receipt of a Scan Results info frame.
+*
+* Arguments:
+* wlandev wlan device structure
+* inf ptr to info frame (contents in hfa384x order)
+*
+* Returns:
+* nothing
+*
+* Side effects:
+*
+* Call context:
+* interrupt
+----------------------------------------------------------------*/
+static void prism2sta_inf_hostscanresults(wlandevice_t *wlandev,
+ hfa384x_InfFrame_t *inf)
+{
+ hfa384x_t *hw = (hfa384x_t *) wlandev->priv;
+ int nbss;
+
+ nbss = (inf->framelen - 3) / 32;
+ pr_debug("Received %d hostscan results\n", nbss);
+
+ if (nbss > 32)
+ nbss = 32;
+
+ kfree(hw->scanresults);
+
+ hw->scanresults = kmemdup(inf, sizeof(hfa384x_InfFrame_t), GFP_ATOMIC);
+
+ if (nbss == 0)
+ nbss = -1;
+
+ /* Notify/wake the sleeping caller. */
+ hw->scanflag = nbss;
+ wake_up_interruptible(&hw->cmdq);
+};
+
+/*----------------------------------------------------------------
+* prism2sta_inf_chinforesults
+*
+* Handles the receipt of a Channel Info Results info frame.
+*
+* Arguments:
+* wlandev wlan device structure
+* inf ptr to info frame (contents in hfa384x order)
+*
+* Returns:
+* nothing
+*
+* Side effects:
+*
+* Call context:
+* interrupt
+----------------------------------------------------------------*/
+static void prism2sta_inf_chinforesults(wlandevice_t *wlandev,
+ hfa384x_InfFrame_t *inf)
+{
+ hfa384x_t *hw = (hfa384x_t *) wlandev->priv;
+ unsigned int i, n;
+
+ hw->channel_info.results.scanchannels =
+ le16_to_cpu(inf->info.chinforesult.scanchannels);
+
+ for (i = 0, n = 0; i < HFA384x_CHINFORESULT_MAX; i++) {
+ hfa384x_ChInfoResultSub_t *result;
+ hfa384x_ChInfoResultSub_t *chinforesult;
+ int chan;
+
+ if (!(hw->channel_info.results.scanchannels & (1 << i)))
+ continue;
+
+ result = &inf->info.chinforesult.result[n];
+ chan = le16_to_cpu(result->chid) - 1;
+
+ if (chan < 0 || chan >= HFA384x_CHINFORESULT_MAX)
+ continue;
+
+ chinforesult = &hw->channel_info.results.result[chan];
+ chinforesult->chid = chan;
+ chinforesult->anl = le16_to_cpu(result->anl);
+ chinforesult->pnl = le16_to_cpu(result->pnl);
+ chinforesult->active = le16_to_cpu(result->active);
+
+ pr_debug("chinfo: channel %d, %s level (avg/peak)=%d/%d dB, pcf %d\n",
+ chan + 1,
+ (chinforesult->active & HFA384x_CHINFORESULT_BSSACTIVE)
+ ? "signal" : "noise",
+ chinforesult->anl, chinforesult->pnl,
+ (chinforesult->active & HFA384x_CHINFORESULT_PCFACTIVE)
+ ? 1 : 0);
+ n++;
+ }
+ atomic_set(&hw->channel_info.done, 2);
+
+ hw->channel_info.count = n;
+}
+
+void prism2sta_processing_defer(struct work_struct *data)
+{
+ hfa384x_t *hw = container_of(data, struct hfa384x, link_bh);
+ wlandevice_t *wlandev = hw->wlandev;
+ hfa384x_bytestr32_t ssid;
+ int result;
+
+ /* First let's process the auth frames */
+ {
+ struct sk_buff *skb;
+ hfa384x_InfFrame_t *inf;
+
+ while ((skb = skb_dequeue(&hw->authq))) {
+ inf = (hfa384x_InfFrame_t *) skb->data;
+ prism2sta_inf_authreq_defer(wlandev, inf);
+ }
+
+ }
+
+ /* Now let's handle the linkstatus stuff */
+ if (hw->link_status == hw->link_status_new)
+ return;
+
+ hw->link_status = hw->link_status_new;
+
+ switch (hw->link_status) {
+ case HFA384x_LINK_NOTCONNECTED:
+ /* I'm currently assuming that this is the initial link
+ * state. It should only be possible immediately
+ * following an Enable command.
+ * Response:
+ * Block Transmits, Ignore receives of data frames
+ */
+ netif_carrier_off(wlandev->netdev);
+
+ netdev_info(wlandev->netdev, "linkstatus=NOTCONNECTED (unhandled)\n");
+ break;
+
+ case HFA384x_LINK_CONNECTED:
+ /* This one indicates a successful scan/join/auth/assoc.
+ * When we have the full MLME complement, this event will
+ * signify successful completion of both mlme_authenticate
+ * and mlme_associate. State management will get a little
+ * ugly here.
+ * Response:
+ * Indicate authentication and/or association
+ * Enable Transmits, Receives and pass up data frames
+ */
+
+ netif_carrier_on(wlandev->netdev);
+
+ /* If we are joining a specific AP, set our
+ * state and reset retries
+ */
+ if (hw->join_ap == 1)
+ hw->join_ap = 2;
+ hw->join_retries = 60;
+
+ /* Don't call this in monitor mode */
+ if (wlandev->netdev->type == ARPHRD_ETHER) {
+ u16 portstatus;
+
+ netdev_info(wlandev->netdev, "linkstatus=CONNECTED\n");
+
+ /* For non-usb devices, we can use the sync versions */
+ /* Collect the BSSID, and set state to allow tx */
+
+ result = hfa384x_drvr_getconfig(hw,
+ HFA384x_RID_CURRENTBSSID,
+ wlandev->bssid,
+ WLAN_BSSID_LEN);
+ if (result) {
+ pr_debug
+ ("getconfig(0x%02x) failed, result = %d\n",
+ HFA384x_RID_CURRENTBSSID, result);
+ return;
+ }
+
+ result = hfa384x_drvr_getconfig(hw,
+ HFA384x_RID_CURRENTSSID,
+ &ssid, sizeof(ssid));
+ if (result) {
+ pr_debug
+ ("getconfig(0x%02x) failed, result = %d\n",
+ HFA384x_RID_CURRENTSSID, result);
+ return;
+ }
+ prism2mgmt_bytestr2pstr((struct hfa384x_bytestr *) &ssid,
+ (p80211pstrd_t *) &
+ wlandev->ssid);
+
+ /* Collect the port status */
+ result = hfa384x_drvr_getconfig16(hw,
+ HFA384x_RID_PORTSTATUS,
+ &portstatus);
+ if (result) {
+ pr_debug
+ ("getconfig(0x%02x) failed, result = %d\n",
+ HFA384x_RID_PORTSTATUS, result);
+ return;
+ }
+ wlandev->macmode =
+ (portstatus == HFA384x_PSTATUS_CONN_IBSS) ?
+ WLAN_MACMODE_IBSS_STA : WLAN_MACMODE_ESS_STA;
+
+ /* signal back up to cfg80211 layer */
+ prism2_connect_result(wlandev, P80211ENUM_truth_false);
+
+ /* Get the ball rolling on the comms quality stuff */
+ prism2sta_commsqual_defer(&hw->commsqual_bh);
+ }
+ break;
+
+ case HFA384x_LINK_DISCONNECTED:
+ /* This one indicates that our association is gone. We've
+ * lost connection with the AP and/or been disassociated.
+ * This indicates that the MAC has completely cleared it's
+ * associated state. We * should send a deauth indication
+ * (implying disassoc) up * to the MLME.
+ * Response:
+ * Indicate Deauthentication
+ * Block Transmits, Ignore receives of data frames
+ */
+ if (wlandev->netdev->type == ARPHRD_ETHER)
+ netdev_info(wlandev->netdev,
+ "linkstatus=DISCONNECTED (unhandled)\n");
+ wlandev->macmode = WLAN_MACMODE_NONE;
+
+ netif_carrier_off(wlandev->netdev);
+
+ /* signal back up to cfg80211 layer */
+ prism2_disconnected(wlandev);
+
+ break;
+
+ case HFA384x_LINK_AP_CHANGE:
+ /* This one indicates that the MAC has decided to and
+ * successfully completed a change to another AP. We
+ * should probably implement a reassociation indication
+ * in response to this one. I'm thinking that the the
+ * p80211 layer needs to be notified in case of
+ * buffering/queueing issues. User mode also needs to be
+ * notified so that any BSS dependent elements can be
+ * updated.
+ * associated state. We * should send a deauth indication
+ * (implying disassoc) up * to the MLME.
+ * Response:
+ * Indicate Reassociation
+ * Enable Transmits, Receives and pass up data frames
+ */
+ netdev_info(wlandev->netdev, "linkstatus=AP_CHANGE\n");
+
+ result = hfa384x_drvr_getconfig(hw,
+ HFA384x_RID_CURRENTBSSID,
+ wlandev->bssid, WLAN_BSSID_LEN);
+ if (result) {
+ pr_debug("getconfig(0x%02x) failed, result = %d\n",
+ HFA384x_RID_CURRENTBSSID, result);
+ return;
+ }
+
+ result = hfa384x_drvr_getconfig(hw,
+ HFA384x_RID_CURRENTSSID,
+ &ssid, sizeof(ssid));
+ if (result) {
+ pr_debug("getconfig(0x%02x) failed, result = %d\n",
+ HFA384x_RID_CURRENTSSID, result);
+ return;
+ }
+ prism2mgmt_bytestr2pstr((struct hfa384x_bytestr *) &ssid,
+ (p80211pstrd_t *) &wlandev->ssid);
+
+ hw->link_status = HFA384x_LINK_CONNECTED;
+ netif_carrier_on(wlandev->netdev);
+
+ /* signal back up to cfg80211 layer */
+ prism2_roamed(wlandev);
+
+ break;
+
+ case HFA384x_LINK_AP_OUTOFRANGE:
+ /* This one indicates that the MAC has decided that the
+ * AP is out of range, but hasn't found a better candidate
+ * so the MAC maintains its "associated" state in case
+ * we get back in range. We should block transmits and
+ * receives in this state. Do we need an indication here?
+ * Probably not since a polling user-mode element would
+ * get this status from from p2PortStatus(FD40). What about
+ * p80211?
+ * Response:
+ * Block Transmits, Ignore receives of data frames
+ */
+ netdev_info(wlandev->netdev, "linkstatus=AP_OUTOFRANGE (unhandled)\n");
+
+ netif_carrier_off(wlandev->netdev);
+
+ break;
+
+ case HFA384x_LINK_AP_INRANGE:
+ /* This one indicates that the MAC has decided that the
+ * AP is back in range. We continue working with our
+ * existing association.
+ * Response:
+ * Enable Transmits, Receives and pass up data frames
+ */
+ netdev_info(wlandev->netdev, "linkstatus=AP_INRANGE\n");
+
+ hw->link_status = HFA384x_LINK_CONNECTED;
+ netif_carrier_on(wlandev->netdev);
+
+ break;
+
+ case HFA384x_LINK_ASSOCFAIL:
+ /* This one is actually a peer to CONNECTED. We've
+ * requested a join for a given SSID and optionally BSSID.
+ * We can use this one to indicate authentication and
+ * association failures. The trick is going to be
+ * 1) identifying the failure, and 2) state management.
+ * Response:
+ * Disable Transmits, Ignore receives of data frames
+ */
+ if (hw->join_ap && --hw->join_retries > 0) {
+ hfa384x_JoinRequest_data_t joinreq;
+
+ joinreq = hw->joinreq;
+ /* Send the join request */
+ hfa384x_drvr_setconfig(hw,
+ HFA384x_RID_JOINREQUEST,
+ &joinreq,
+ HFA384x_RID_JOINREQUEST_LEN);
+ netdev_info(wlandev->netdev,
+ "linkstatus=ASSOCFAIL (re-submitting join)\n");
+ } else {
+ netdev_info(wlandev->netdev, "linkstatus=ASSOCFAIL (unhandled)\n");
+ }
+
+ netif_carrier_off(wlandev->netdev);
+
+ /* signal back up to cfg80211 layer */
+ prism2_connect_result(wlandev, P80211ENUM_truth_true);
+
+ break;
+
+ default:
+ /* This is bad, IO port problems? */
+ netdev_warn(wlandev->netdev,
+ "unknown linkstatus=0x%02x\n", hw->link_status);
+ return;
+ }
+
+ wlandev->linkstatus = (hw->link_status == HFA384x_LINK_CONNECTED);
+}
+
+/*----------------------------------------------------------------
+* prism2sta_inf_linkstatus
+*
+* Handles the receipt of a Link Status info frame.
+*
+* Arguments:
+* wlandev wlan device structure
+* inf ptr to info frame (contents in hfa384x order)
+*
+* Returns:
+* nothing
+*
+* Side effects:
+*
+* Call context:
+* interrupt
+----------------------------------------------------------------*/
+static void prism2sta_inf_linkstatus(wlandevice_t *wlandev,
+ hfa384x_InfFrame_t *inf)
+{
+ hfa384x_t *hw = (hfa384x_t *) wlandev->priv;
+
+ hw->link_status_new = le16_to_cpu(inf->info.linkstatus.linkstatus);
+
+ schedule_work(&hw->link_bh);
+}
+
+/*----------------------------------------------------------------
+* prism2sta_inf_assocstatus
+*
+* Handles the receipt of an Association Status info frame. Should
+* be present in APs only.
+*
+* Arguments:
+* wlandev wlan device structure
+* inf ptr to info frame (contents in hfa384x order)
+*
+* Returns:
+* nothing
+*
+* Side effects:
+*
+* Call context:
+* interrupt
+----------------------------------------------------------------*/
+static void prism2sta_inf_assocstatus(wlandevice_t *wlandev,
+ hfa384x_InfFrame_t *inf)
+{
+ hfa384x_t *hw = (hfa384x_t *) wlandev->priv;
+ hfa384x_AssocStatus_t rec;
+ int i;
+
+ memcpy(&rec, &inf->info.assocstatus, sizeof(rec));
+ rec.assocstatus = le16_to_cpu(rec.assocstatus);
+ rec.reason = le16_to_cpu(rec.reason);
+
+ /*
+ ** Find the address in the list of authenticated stations.
+ ** If it wasn't found, then this address has not been previously
+ ** authenticated and something weird has happened if this is
+ ** anything other than an "authentication failed" message.
+ ** If the address was found, then set the "associated" flag for
+ ** that station, based on whether the station is associating or
+ ** losing its association. Something weird has also happened
+ ** if we find the address in the list of authenticated stations
+ ** but we are getting an "authentication failed" message.
+ */
+
+ for (i = 0; i < hw->authlist.cnt; i++)
+ if (memcmp(rec.sta_addr, hw->authlist.addr[i], ETH_ALEN) == 0)
+ break;
+
+ if (i >= hw->authlist.cnt) {
+ if (rec.assocstatus != HFA384x_ASSOCSTATUS_AUTHFAIL)
+ netdev_warn(wlandev->netdev,
+ "assocstatus info frame received for non-authenticated station.\n");
+ } else {
+ hw->authlist.assoc[i] =
+ (rec.assocstatus == HFA384x_ASSOCSTATUS_STAASSOC ||
+ rec.assocstatus == HFA384x_ASSOCSTATUS_REASSOC);
+
+ if (rec.assocstatus == HFA384x_ASSOCSTATUS_AUTHFAIL)
+ netdev_warn(wlandev->netdev,
+"authfail assocstatus info frame received for authenticated station.\n");
+ }
+}
+
+/*----------------------------------------------------------------
+* prism2sta_inf_authreq
+*
+* Handles the receipt of an Authentication Request info frame. Should
+* be present in APs only.
+*
+* Arguments:
+* wlandev wlan device structure
+* inf ptr to info frame (contents in hfa384x order)
+*
+* Returns:
+* nothing
+*
+* Side effects:
+*
+* Call context:
+* interrupt
+*
+----------------------------------------------------------------*/
+static void prism2sta_inf_authreq(wlandevice_t *wlandev,
+ hfa384x_InfFrame_t *inf)
+{
+ hfa384x_t *hw = (hfa384x_t *) wlandev->priv;
+ struct sk_buff *skb;
+
+ skb = dev_alloc_skb(sizeof(*inf));
+ if (skb) {
+ skb_put(skb, sizeof(*inf));
+ memcpy(skb->data, inf, sizeof(*inf));
+ skb_queue_tail(&hw->authq, skb);
+ schedule_work(&hw->link_bh);
+ }
+}
+
+static void prism2sta_inf_authreq_defer(wlandevice_t *wlandev,
+ hfa384x_InfFrame_t *inf)
+{
+ hfa384x_t *hw = (hfa384x_t *) wlandev->priv;
+ hfa384x_authenticateStation_data_t rec;
+
+ int i, added, result, cnt;
+ u8 *addr;
+
+ /*
+ ** Build the AuthenticateStation record. Initialize it for denying
+ ** authentication.
+ */
+
+ ether_addr_copy(rec.address, inf->info.authreq.sta_addr);
+ rec.status = P80211ENUM_status_unspec_failure;
+
+ /*
+ ** Authenticate based on the access mode.
+ */
+
+ switch (hw->accessmode) {
+ case WLAN_ACCESS_NONE:
+
+ /*
+ ** Deny all new authentications. However, if a station
+ ** is ALREADY authenticated, then accept it.
+ */
+
+ for (i = 0; i < hw->authlist.cnt; i++)
+ if (memcmp(rec.address, hw->authlist.addr[i],
+ ETH_ALEN) == 0) {
+ rec.status = P80211ENUM_status_successful;
+ break;
+ }
+
+ break;
+
+ case WLAN_ACCESS_ALL:
+
+ /*
+ ** Allow all authentications.
+ */
+
+ rec.status = P80211ENUM_status_successful;
+ break;
+
+ case WLAN_ACCESS_ALLOW:
+
+ /*
+ ** Only allow the authentication if the MAC address
+ ** is in the list of allowed addresses.
+ **
+ ** Since this is the interrupt handler, we may be here
+ ** while the access list is in the middle of being
+ ** updated. Choose the list which is currently okay.
+ ** See "prism2mib_priv_accessallow()" for details.
+ */
+
+ if (hw->allow.modify == 0) {
+ cnt = hw->allow.cnt;
+ addr = hw->allow.addr[0];
+ } else {
+ cnt = hw->allow.cnt1;
+ addr = hw->allow.addr1[0];
+ }
+
+ for (i = 0; i < cnt; i++, addr += ETH_ALEN)
+ if (memcmp(rec.address, addr, ETH_ALEN) == 0) {
+ rec.status = P80211ENUM_status_successful;
+ break;
+ }
+
+ break;
+
+ case WLAN_ACCESS_DENY:
+
+ /*
+ ** Allow the authentication UNLESS the MAC address is
+ ** in the list of denied addresses.
+ **
+ ** Since this is the interrupt handler, we may be here
+ ** while the access list is in the middle of being
+ ** updated. Choose the list which is currently okay.
+ ** See "prism2mib_priv_accessdeny()" for details.
+ */
+
+ if (hw->deny.modify == 0) {
+ cnt = hw->deny.cnt;
+ addr = hw->deny.addr[0];
+ } else {
+ cnt = hw->deny.cnt1;
+ addr = hw->deny.addr1[0];
+ }
+
+ rec.status = P80211ENUM_status_successful;
+
+ for (i = 0; i < cnt; i++, addr += ETH_ALEN)
+ if (memcmp(rec.address, addr, ETH_ALEN) == 0) {
+ rec.status = P80211ENUM_status_unspec_failure;
+ break;
+ }
+
+ break;
+ }
+
+ /*
+ ** If the authentication is okay, then add the MAC address to the
+ ** list of authenticated stations. Don't add the address if it
+ ** is already in the list. (802.11b does not seem to disallow
+ ** a station from issuing an authentication request when the
+ ** station is already authenticated. Does this sort of thing
+ ** ever happen? We might as well do the check just in case.)
+ */
+
+ added = 0;
+
+ if (rec.status == P80211ENUM_status_successful) {
+ for (i = 0; i < hw->authlist.cnt; i++)
+ if (memcmp(rec.address, hw->authlist.addr[i], ETH_ALEN)
+ == 0)
+ break;
+
+ if (i >= hw->authlist.cnt) {
+ if (hw->authlist.cnt >= WLAN_AUTH_MAX) {
+ rec.status = P80211ENUM_status_ap_full;
+ } else {
+ ether_addr_copy(hw->authlist.addr[hw->authlist.cnt],
+ rec.address);
+ hw->authlist.cnt++;
+ added = 1;
+ }
+ }
+ }
+
+ /*
+ ** Send back the results of the authentication. If this doesn't work,
+ ** then make sure to remove the address from the authenticated list if
+ ** it was added.
+ */
+
+ rec.status = cpu_to_le16(rec.status);
+ rec.algorithm = inf->info.authreq.algorithm;
+
+ result = hfa384x_drvr_setconfig(hw, HFA384x_RID_AUTHENTICATESTA,
+ &rec, sizeof(rec));
+ if (result) {
+ if (added)
+ hw->authlist.cnt--;
+ netdev_err(wlandev->netdev,
+ "setconfig(authenticatestation) failed, result=%d\n",
+ result);
+ }
+}
+
+/*----------------------------------------------------------------
+* prism2sta_inf_psusercnt
+*
+* Handles the receipt of a PowerSaveUserCount info frame. Should
+* be present in APs only.
+*
+* Arguments:
+* wlandev wlan device structure
+* inf ptr to info frame (contents in hfa384x order)
+*
+* Returns:
+* nothing
+*
+* Side effects:
+*
+* Call context:
+* interrupt
+----------------------------------------------------------------*/
+static void prism2sta_inf_psusercnt(wlandevice_t *wlandev,
+ hfa384x_InfFrame_t *inf)
+{
+ hfa384x_t *hw = (hfa384x_t *) wlandev->priv;
+
+ hw->psusercount = le16_to_cpu(inf->info.psusercnt.usercnt);
+}
+
+/*----------------------------------------------------------------
+* prism2sta_ev_info
+*
+* Handles the Info event.
+*
+* Arguments:
+* wlandev wlan device structure
+* inf ptr to a generic info frame
+*
+* Returns:
+* nothing
+*
+* Side effects:
+*
+* Call context:
+* interrupt
+----------------------------------------------------------------*/
+void prism2sta_ev_info(wlandevice_t *wlandev, hfa384x_InfFrame_t *inf)
+{
+ inf->infotype = le16_to_cpu(inf->infotype);
+ /* Dispatch */
+ switch (inf->infotype) {
+ case HFA384x_IT_HANDOVERADDR:
+ prism2sta_inf_handover(wlandev, inf);
+ break;
+ case HFA384x_IT_COMMTALLIES:
+ prism2sta_inf_tallies(wlandev, inf);
+ break;
+ case HFA384x_IT_HOSTSCANRESULTS:
+ prism2sta_inf_hostscanresults(wlandev, inf);
+ break;
+ case HFA384x_IT_SCANRESULTS:
+ prism2sta_inf_scanresults(wlandev, inf);
+ break;
+ case HFA384x_IT_CHINFORESULTS:
+ prism2sta_inf_chinforesults(wlandev, inf);
+ break;
+ case HFA384x_IT_LINKSTATUS:
+ prism2sta_inf_linkstatus(wlandev, inf);
+ break;
+ case HFA384x_IT_ASSOCSTATUS:
+ prism2sta_inf_assocstatus(wlandev, inf);
+ break;
+ case HFA384x_IT_AUTHREQ:
+ prism2sta_inf_authreq(wlandev, inf);
+ break;
+ case HFA384x_IT_PSUSERCNT:
+ prism2sta_inf_psusercnt(wlandev, inf);
+ break;
+ case HFA384x_IT_KEYIDCHANGED:
+ netdev_warn(wlandev->netdev, "Unhandled IT_KEYIDCHANGED\n");
+ break;
+ case HFA384x_IT_ASSOCREQ:
+ netdev_warn(wlandev->netdev, "Unhandled IT_ASSOCREQ\n");
+ break;
+ case HFA384x_IT_MICFAILURE:
+ netdev_warn(wlandev->netdev, "Unhandled IT_MICFAILURE\n");
+ break;
+ default:
+ netdev_warn(wlandev->netdev,
+ "Unknown info type=0x%02x\n", inf->infotype);
+ break;
+ }
+}
+
+/*----------------------------------------------------------------
+* prism2sta_ev_txexc
+*
+* Handles the TxExc event. A Transmit Exception event indicates
+* that the MAC's TX process was unsuccessful - so the packet did
+* not get transmitted.
+*
+* Arguments:
+* wlandev wlan device structure
+* status tx frame status word
+*
+* Returns:
+* nothing
+*
+* Side effects:
+*
+* Call context:
+* interrupt
+----------------------------------------------------------------*/
+void prism2sta_ev_txexc(wlandevice_t *wlandev, u16 status)
+{
+ pr_debug("TxExc status=0x%x.\n", status);
+}
+
+/*----------------------------------------------------------------
+* prism2sta_ev_tx
+*
+* Handles the Tx event.
+*
+* Arguments:
+* wlandev wlan device structure
+* status tx frame status word
+* Returns:
+* nothing
+*
+* Side effects:
+*
+* Call context:
+* interrupt
+----------------------------------------------------------------*/
+void prism2sta_ev_tx(wlandevice_t *wlandev, u16 status)
+{
+ pr_debug("Tx Complete, status=0x%04x\n", status);
+ /* update linux network stats */
+ wlandev->netdev->stats.tx_packets++;
+}
+
+/*----------------------------------------------------------------
+* prism2sta_ev_rx
+*
+* Handles the Rx event.
+*
+* Arguments:
+* wlandev wlan device structure
+*
+* Returns:
+* nothing
+*
+* Side effects:
+*
+* Call context:
+* interrupt
+----------------------------------------------------------------*/
+void prism2sta_ev_rx(wlandevice_t *wlandev, struct sk_buff *skb)
+{
+ p80211netdev_rx(wlandev, skb);
+}
+
+/*----------------------------------------------------------------
+* prism2sta_ev_alloc
+*
+* Handles the Alloc event.
+*
+* Arguments:
+* wlandev wlan device structure
+*
+* Returns:
+* nothing
+*
+* Side effects:
+*
+* Call context:
+* interrupt
+----------------------------------------------------------------*/
+void prism2sta_ev_alloc(wlandevice_t *wlandev)
+{
+ netif_wake_queue(wlandev->netdev);
+}
+
+/*----------------------------------------------------------------
+* create_wlan
+*
+* Called at module init time. This creates the wlandevice_t structure
+* and initializes it with relevant bits.
+*
+* Arguments:
+* none
+*
+* Returns:
+* the created wlandevice_t structure.
+*
+* Side effects:
+* also allocates the priv/hw structures.
+*
+* Call context:
+* process thread
+*
+----------------------------------------------------------------*/
+static wlandevice_t *create_wlan(void)
+{
+ wlandevice_t *wlandev = NULL;
+ hfa384x_t *hw = NULL;
+
+ /* Alloc our structures */
+ wlandev = kzalloc(sizeof(wlandevice_t), GFP_KERNEL);
+ hw = kzalloc(sizeof(hfa384x_t), GFP_KERNEL);
+
+ if (!wlandev || !hw) {
+ pr_err("%s: Memory allocation failure.\n", dev_info);
+ kfree(wlandev);
+ kfree(hw);
+ return NULL;
+ }
+
+ /* Initialize the network device object. */
+ wlandev->nsdname = dev_info;
+ wlandev->msdstate = WLAN_MSD_HWPRESENT_PENDING;
+ wlandev->priv = hw;
+ wlandev->open = prism2sta_open;
+ wlandev->close = prism2sta_close;
+ wlandev->reset = prism2sta_reset;
+ wlandev->txframe = prism2sta_txframe;
+ wlandev->mlmerequest = prism2sta_mlmerequest;
+ wlandev->set_multicast_list = prism2sta_setmulticast;
+ wlandev->tx_timeout = hfa384x_tx_timeout;
+
+ wlandev->nsdcaps = P80211_NSDCAP_HWFRAGMENT | P80211_NSDCAP_AUTOJOIN;
+
+ /* Initialize the device private data structure. */
+ hw->dot11_desired_bss_type = 1;
+
+ return wlandev;
+}
+
+void prism2sta_commsqual_defer(struct work_struct *data)
+{
+ hfa384x_t *hw = container_of(data, struct hfa384x, commsqual_bh);
+ wlandevice_t *wlandev = hw->wlandev;
+ hfa384x_bytestr32_t ssid;
+ struct p80211msg_dot11req_mibget msg;
+ p80211item_uint32_t *mibitem = (p80211item_uint32_t *)
+ &msg.mibattribute.data;
+ int result = 0;
+
+ if (hw->wlandev->hwremoved)
+ return;
+
+ /* we don't care if we're in AP mode */
+ if ((wlandev->macmode == WLAN_MACMODE_NONE) ||
+ (wlandev->macmode == WLAN_MACMODE_ESS_AP)) {
+ return;
+ }
+
+ /* It only makes sense to poll these in non-IBSS */
+ if (wlandev->macmode != WLAN_MACMODE_IBSS_STA) {
+ result = hfa384x_drvr_getconfig(
+ hw, HFA384x_RID_DBMCOMMSQUALITY,
+ &hw->qual, HFA384x_RID_DBMCOMMSQUALITY_LEN);
+
+ if (result) {
+ netdev_err(wlandev->netdev, "error fetching commsqual\n");
+ return;
+ }
+
+ pr_debug("commsqual %d %d %d\n",
+ le16_to_cpu(hw->qual.CQ_currBSS),
+ le16_to_cpu(hw->qual.ASL_currBSS),
+ le16_to_cpu(hw->qual.ANL_currFC));
+ }
+
+ /* Get the signal rate */
+ msg.msgcode = DIDmsg_dot11req_mibget;
+ mibitem->did = DIDmib_p2_p2MAC_p2CurrentTxRate;
+ result = p80211req_dorequest(wlandev, (u8 *) &msg);
+
+ if (result) {
+ pr_debug("get signal rate failed, result = %d\n",
+ result);
+ return;
+ }
+
+ switch (mibitem->data) {
+ case HFA384x_RATEBIT_1:
+ hw->txrate = 10;
+ break;
+ case HFA384x_RATEBIT_2:
+ hw->txrate = 20;
+ break;
+ case HFA384x_RATEBIT_5dot5:
+ hw->txrate = 55;
+ break;
+ case HFA384x_RATEBIT_11:
+ hw->txrate = 110;
+ break;
+ default:
+ pr_debug("Bad ratebit (%d)\n", mibitem->data);
+ }
+
+ /* Lastly, we need to make sure the BSSID didn't change on us */
+ result = hfa384x_drvr_getconfig(hw,
+ HFA384x_RID_CURRENTBSSID,
+ wlandev->bssid, WLAN_BSSID_LEN);
+ if (result) {
+ pr_debug("getconfig(0x%02x) failed, result = %d\n",
+ HFA384x_RID_CURRENTBSSID, result);
+ return;
+ }
+
+ result = hfa384x_drvr_getconfig(hw,
+ HFA384x_RID_CURRENTSSID,
+ &ssid, sizeof(ssid));
+ if (result) {
+ pr_debug("getconfig(0x%02x) failed, result = %d\n",
+ HFA384x_RID_CURRENTSSID, result);
+ return;
+ }
+ prism2mgmt_bytestr2pstr((struct hfa384x_bytestr *) &ssid,
+ (p80211pstrd_t *) &wlandev->ssid);
+
+ /* Reschedule timer */
+ mod_timer(&hw->commsqual_timer, jiffies + HZ);
+}
+
+void prism2sta_commsqual_timer(unsigned long data)
+{
+ hfa384x_t *hw = (hfa384x_t *) data;
+
+ schedule_work(&hw->commsqual_bh);
+}
diff --git a/drivers/staging/wlan-ng/prism2usb.c b/drivers/staging/wlan-ng/prism2usb.c
new file mode 100644
index 000000000..e92bbc12b
--- /dev/null
+++ b/drivers/staging/wlan-ng/prism2usb.c
@@ -0,0 +1,299 @@
+#include "hfa384x_usb.c"
+#include "prism2mgmt.c"
+#include "prism2mib.c"
+#include "prism2sta.c"
+#include "prism2fw.c"
+
+#define PRISM_DEV(vid, pid, name) \
+ { USB_DEVICE(vid, pid), \
+ .driver_info = (unsigned long) name }
+
+static struct usb_device_id usb_prism_tbl[] = {
+ PRISM_DEV(0x04bb, 0x0922, "IOData AirPort WN-B11/USBS"),
+ PRISM_DEV(0x07aa, 0x0012, "Corega Wireless LAN USB Stick-11"),
+ PRISM_DEV(0x09aa, 0x3642, "Prism2.x 11Mbps WLAN USB Adapter"),
+ PRISM_DEV(0x1668, 0x0408, "Actiontec Prism2.5 11Mbps WLAN USB Adapter"),
+ PRISM_DEV(0x1668, 0x0421, "Actiontec Prism2.5 11Mbps WLAN USB Adapter"),
+ PRISM_DEV(0x1915, 0x2236, "Linksys WUSB11v3.0 11Mbps WLAN USB Adapter"),
+ PRISM_DEV(0x066b, 0x2212, "Linksys WUSB11v2.5 11Mbps WLAN USB Adapter"),
+ PRISM_DEV(0x066b, 0x2213, "Linksys WUSB12v1.1 11Mbps WLAN USB Adapter"),
+ PRISM_DEV(0x0411, 0x0016, "Melco WLI-USB-S11 11Mbps WLAN Adapter"),
+ PRISM_DEV(0x08de, 0x7a01, "PRISM25 IEEE 802.11 Mini USB Adapter"),
+ PRISM_DEV(0x8086, 0x1111, "Intel PRO/Wireless 2011B LAN USB Adapter"),
+ PRISM_DEV(0x0d8e, 0x7a01, "PRISM25 IEEE 802.11 Mini USB Adapter"),
+ PRISM_DEV(0x045e, 0x006e, "Microsoft MN510 Wireless USB Adapter"),
+ PRISM_DEV(0x0967, 0x0204, "Acer Warplink USB Adapter"),
+ PRISM_DEV(0x0cde, 0x0002, "Z-Com 725/726 Prism2.5 USB/USB Integrated"),
+ PRISM_DEV(0x0cde, 0x0005, "Z-Com Xl735 Wireless 802.11b USB Adapter"),
+ PRISM_DEV(0x413c, 0x8100, "Dell TrueMobile 1180 Wireless USB Adapter"),
+ PRISM_DEV(0x0b3b, 0x1601, "ALLNET 0193 11Mbps WLAN USB Adapter"),
+ PRISM_DEV(0x0b3b, 0x1602, "ZyXEL ZyAIR B200 Wireless USB Adapter"),
+ PRISM_DEV(0x0baf, 0x00eb, "USRobotics USR1120 Wireless USB Adapter"),
+ PRISM_DEV(0x0411, 0x0027, "Melco WLI-USB-KS11G 11Mbps WLAN Adapter"),
+ PRISM_DEV(0x04f1, 0x3009, "JVC MP-XP7250 Builtin USB WLAN Adapter"),
+ PRISM_DEV(0x0846, 0x4110, "NetGear MA111"),
+ PRISM_DEV(0x03f3, 0x0020, "Adaptec AWN-8020 USB WLAN Adapter"),
+ PRISM_DEV(0x2821, 0x3300, "ASUS-WL140 Wireless USB Adapter"),
+ PRISM_DEV(0x2001, 0x3700, "DWL-122 Wireless USB Adapter"),
+ PRISM_DEV(0x2001, 0x3702, "DWL-120 Rev F Wireless USB Adapter"),
+ PRISM_DEV(0x50c2, 0x4013, "Averatec USB WLAN Adapter"),
+ PRISM_DEV(0x2c02, 0x14ea, "Planex GW-US11H WLAN USB Adapter"),
+ PRISM_DEV(0x124a, 0x168b, "Airvast PRISM3 WLAN USB Adapter"),
+ PRISM_DEV(0x083a, 0x3503, "T-Sinus 111 USB WLAN Adapter"),
+ PRISM_DEV(0x2821, 0x3300, "Hawking HighDB USB Adapter"),
+ PRISM_DEV(0x0411, 0x0044, "Melco WLI-USB-KB11 11Mbps WLAN Adapter"),
+ PRISM_DEV(0x1668, 0x6106, "ROPEX FreeLan 802.11b USB Adapter"),
+ PRISM_DEV(0x124a, 0x4017, "Pheenet WL-503IA 802.11b USB Adapter"),
+ PRISM_DEV(0x0bb2, 0x0302, "Ambit Microsystems Corp."),
+ PRISM_DEV(0x9016, 0x182d, "Sitecom WL-022 802.11b USB Adapter"),
+ PRISM_DEV(0x0543, 0x0f01,
+ "ViewSonic Airsync USB Adapter 11Mbps (Prism2.5)"),
+ PRISM_DEV(0x067c, 0x1022,
+ "Siemens SpeedStream 1022 11Mbps WLAN USB Adapter"),
+ PRISM_DEV(0x049f, 0x0033,
+ "Compaq/Intel W100 PRO/Wireless 11Mbps multiport WLAN Adapter"),
+ { } /* terminator */
+};
+MODULE_DEVICE_TABLE(usb, usb_prism_tbl);
+
+static int prism2sta_probe_usb(struct usb_interface *interface,
+ const struct usb_device_id *id)
+{
+ struct usb_device *dev;
+
+ wlandevice_t *wlandev = NULL;
+ hfa384x_t *hw = NULL;
+ int result = 0;
+
+ dev = interface_to_usbdev(interface);
+ wlandev = create_wlan();
+ if (wlandev == NULL) {
+ dev_err(&interface->dev, "Memory allocation failure.\n");
+ result = -EIO;
+ goto failed;
+ }
+ hw = wlandev->priv;
+
+ if (wlan_setup(wlandev, &(interface->dev)) != 0) {
+ dev_err(&interface->dev, "wlan_setup() failed.\n");
+ result = -EIO;
+ goto failed;
+ }
+
+ /* Initialize the hw data */
+ hfa384x_create(hw, dev);
+ hw->wlandev = wlandev;
+
+ /* Register the wlandev, this gets us a name and registers the
+ * linux netdevice.
+ */
+ SET_NETDEV_DEV(wlandev->netdev, &(interface->dev));
+
+ /* Do a chip-level reset on the MAC */
+ if (prism2_doreset) {
+ result = hfa384x_corereset(hw,
+ prism2_reset_holdtime,
+ prism2_reset_settletime, 0);
+ if (result != 0) {
+ result = -EIO;
+ dev_err(&interface->dev,
+ "hfa384x_corereset() failed.\n");
+ goto failed_reset;
+ }
+ }
+
+ usb_get_dev(dev);
+
+ wlandev->msdstate = WLAN_MSD_HWPRESENT;
+
+ /* Try and load firmware, then enable card before we register */
+ prism2_fwtry(dev, wlandev);
+ prism2sta_ifstate(wlandev, P80211ENUM_ifstate_enable);
+
+ if (register_wlandev(wlandev) != 0) {
+ dev_err(&interface->dev, "register_wlandev() failed.\n");
+ result = -EIO;
+ goto failed_register;
+ }
+
+ goto done;
+
+failed_register:
+ usb_put_dev(dev);
+failed_reset:
+ wlan_unsetup(wlandev);
+failed:
+ kfree(wlandev);
+ kfree(hw);
+ wlandev = NULL;
+
+done:
+ usb_set_intfdata(interface, wlandev);
+ return result;
+}
+
+static void prism2sta_disconnect_usb(struct usb_interface *interface)
+{
+ wlandevice_t *wlandev;
+
+ wlandev = (wlandevice_t *) usb_get_intfdata(interface);
+ if (wlandev != NULL) {
+ LIST_HEAD(cleanlist);
+ struct list_head *entry;
+ struct list_head *temp;
+ unsigned long flags;
+
+ hfa384x_t *hw = wlandev->priv;
+
+ if (!hw)
+ goto exit;
+
+ spin_lock_irqsave(&hw->ctlxq.lock, flags);
+
+ p80211netdev_hwremoved(wlandev);
+ list_splice_init(&hw->ctlxq.reapable, &cleanlist);
+ list_splice_init(&hw->ctlxq.completing, &cleanlist);
+ list_splice_init(&hw->ctlxq.pending, &cleanlist);
+ list_splice_init(&hw->ctlxq.active, &cleanlist);
+
+ spin_unlock_irqrestore(&hw->ctlxq.lock, flags);
+
+ /* There's no hardware to shutdown, but the driver
+ * might have some tasks or tasklets that must be
+ * stopped before we can tear everything down.
+ */
+ prism2sta_ifstate(wlandev, P80211ENUM_ifstate_disable);
+
+ del_singleshot_timer_sync(&hw->throttle);
+ del_singleshot_timer_sync(&hw->reqtimer);
+ del_singleshot_timer_sync(&hw->resptimer);
+
+ /* Unlink all the URBs. This "removes the wheels"
+ * from the entire CTLX handling mechanism.
+ */
+ usb_kill_urb(&hw->rx_urb);
+ usb_kill_urb(&hw->tx_urb);
+ usb_kill_urb(&hw->ctlx_urb);
+
+ tasklet_kill(&hw->completion_bh);
+ tasklet_kill(&hw->reaper_bh);
+
+ flush_scheduled_work();
+
+ /* Now we complete any outstanding commands
+ * and tell everyone who is waiting for their
+ * responses that we have shut down.
+ */
+ list_for_each(entry, &cleanlist) {
+ hfa384x_usbctlx_t *ctlx;
+
+ ctlx = list_entry(entry, hfa384x_usbctlx_t, list);
+ complete(&ctlx->done);
+ }
+
+ /* Give any outstanding synchronous commands
+ * a chance to complete. All they need to do
+ * is "wake up", so that's easy.
+ * (I'd like a better way to do this, really.)
+ */
+ msleep(100);
+
+ /* Now delete the CTLXs, because no-one else can now. */
+ list_for_each_safe(entry, temp, &cleanlist) {
+ hfa384x_usbctlx_t *ctlx;
+
+ ctlx = list_entry(entry, hfa384x_usbctlx_t, list);
+ kfree(ctlx);
+ }
+
+ /* Unhook the wlandev */
+ unregister_wlandev(wlandev);
+ wlan_unsetup(wlandev);
+
+ usb_put_dev(hw->usb);
+
+ hfa384x_destroy(hw);
+ kfree(hw);
+
+ kfree(wlandev);
+ }
+
+exit:
+ usb_set_intfdata(interface, NULL);
+}
+
+#ifdef CONFIG_PM
+static int prism2sta_suspend(struct usb_interface *interface,
+ pm_message_t message)
+{
+ hfa384x_t *hw = NULL;
+ wlandevice_t *wlandev;
+
+ wlandev = (wlandevice_t *) usb_get_intfdata(interface);
+ if (!wlandev)
+ return -ENODEV;
+
+ hw = wlandev->priv;
+ if (!hw)
+ return -ENODEV;
+
+ prism2sta_ifstate(wlandev, P80211ENUM_ifstate_disable);
+
+ usb_kill_urb(&hw->rx_urb);
+ usb_kill_urb(&hw->tx_urb);
+ usb_kill_urb(&hw->ctlx_urb);
+
+ return 0;
+}
+
+static int prism2sta_resume(struct usb_interface *interface)
+{
+ int result = 0;
+ hfa384x_t *hw = NULL;
+ wlandevice_t *wlandev;
+
+ wlandev = (wlandevice_t *) usb_get_intfdata(interface);
+ if (!wlandev)
+ return -ENODEV;
+
+ hw = wlandev->priv;
+ if (!hw)
+ return -ENODEV;
+
+ /* Do a chip-level reset on the MAC */
+ if (prism2_doreset) {
+ result = hfa384x_corereset(hw,
+ prism2_reset_holdtime,
+ prism2_reset_settletime, 0);
+ if (result != 0) {
+ unregister_wlandev(wlandev);
+ hfa384x_destroy(hw);
+ dev_err(&interface->dev, "hfa384x_corereset() failed.\n");
+ kfree(wlandev);
+ kfree(hw);
+ wlandev = NULL;
+ return -ENODEV;
+ }
+ }
+
+ prism2sta_ifstate(wlandev, P80211ENUM_ifstate_enable);
+
+ return 0;
+}
+#else
+#define prism2sta_suspend NULL
+#define prism2sta_resume NULL
+#endif /* CONFIG_PM */
+
+static struct usb_driver prism2_usb_driver = {
+ .name = "prism2_usb",
+ .probe = prism2sta_probe_usb,
+ .disconnect = prism2sta_disconnect_usb,
+ .id_table = usb_prism_tbl,
+ .suspend = prism2sta_suspend,
+ .resume = prism2sta_resume,
+ .reset_resume = prism2sta_resume,
+ /* fops, minor? */
+};
+
+module_usb_driver(prism2_usb_driver);
diff --git a/drivers/staging/xgifb/Kconfig b/drivers/staging/xgifb/Kconfig
new file mode 100644
index 000000000..bb0ca5974
--- /dev/null
+++ b/drivers/staging/xgifb/Kconfig
@@ -0,0 +1,11 @@
+config FB_XGI
+ tristate "XGI display support"
+ depends on FB && PCI
+ select FB_CFB_FILLRECT
+ select FB_CFB_COPYAREA
+ select FB_CFB_IMAGEBLIT
+ help
+ This driver supports notebooks with XGI Z7,Z9,Z11 PCI chips.
+ Say Y if you have such a graphics card.
+ To compile this driver as a module, choose M here: the
+ module will be called xgifb.ko
diff --git a/drivers/staging/xgifb/Makefile b/drivers/staging/xgifb/Makefile
new file mode 100644
index 000000000..55e519905
--- /dev/null
+++ b/drivers/staging/xgifb/Makefile
@@ -0,0 +1,4 @@
+obj-$(CONFIG_FB_XGI) += xgifb.o
+
+xgifb-y := XGI_main_26.o vb_init.o vb_setmode.o vb_util.o
+
diff --git a/drivers/staging/xgifb/TODO b/drivers/staging/xgifb/TODO
new file mode 100644
index 000000000..7eb99140a
--- /dev/null
+++ b/drivers/staging/xgifb/TODO
@@ -0,0 +1,13 @@
+This drivers still needs a lot of work. I can list all cleanups to do but it's
+going to be long. So, I'm writing "cleanups" and not the list.
+
+Arnaud
+
+TODO:
+- clean ups
+- sort out dup ids with SiS driver
+- remove useless/wrong/unused code...
+- get rid of non-linux related stuff
+
+Please send patches to:
+Arnaud Patard <arnaud.patard@rtp-net.org>
diff --git a/drivers/staging/xgifb/XGI_main.h b/drivers/staging/xgifb/XGI_main.h
new file mode 100644
index 000000000..85079fea7
--- /dev/null
+++ b/drivers/staging/xgifb/XGI_main.h
@@ -0,0 +1,377 @@
+#ifndef _XGIFB_MAIN
+#define _XGIFB_MAIN
+/* ------------------- Constant Definitions ------------------------- */
+#include "XGIfb.h"
+#include "vb_def.h"
+
+#define PCI_DEVICE_ID_XGI_42 0x042
+#define PCI_DEVICE_ID_XGI_27 0x027
+
+static const struct pci_device_id xgifb_pci_table[] = {
+ {PCI_DEVICE(PCI_VENDOR_ID_XGI, PCI_DEVICE_ID_XGI_20)},
+ {PCI_DEVICE(PCI_VENDOR_ID_XGI, PCI_DEVICE_ID_XGI_27)},
+ {PCI_DEVICE(PCI_VENDOR_ID_XGI, PCI_DEVICE_ID_XGI_40)},
+ {PCI_DEVICE(PCI_VENDOR_ID_XGI, PCI_DEVICE_ID_XGI_42)},
+ {0}
+};
+
+MODULE_DEVICE_TABLE(pci, xgifb_pci_table);
+
+/* To be included in fb.h */
+#define XGISR (xgifb_info->dev_info.P3c4)
+#define XGICR (xgifb_info->dev_info.P3d4)
+#define XGIDACA (xgifb_info->dev_info.P3c8)
+#define XGIDACD (xgifb_info->dev_info.P3c9)
+#define XGIPART1 (xgifb_info->dev_info.Part1Port)
+#define XGIPART2 (xgifb_info->dev_info.Part2Port)
+#define XGIPART3 (xgifb_info->dev_info.Part3Port)
+#define XGIPART4 (xgifb_info->dev_info.Part4Port)
+#define XGIPART5 (xgifb_info->dev_info.Part5Port)
+#define XGIDAC2A XGIPART5
+#define XGIDAC2D (XGIPART5 + 1)
+
+#define IND_XGI_SCRATCH_REG_CR30 0x30 /* CRs */
+#define IND_XGI_SCRATCH_REG_CR31 0x31
+#define IND_XGI_SCRATCH_REG_CR32 0x32
+#define IND_XGI_SCRATCH_REG_CR33 0x33
+#define IND_XGI_LCD_PANEL 0x36
+#define IND_XGI_SCRATCH_REG_CR37 0x37
+
+#define XGI_DRAM_SIZE_MASK 0xF0 /*SR14 */
+#define XGI_DRAM_SIZE_1MB 0x00
+#define XGI_DRAM_SIZE_2MB 0x01
+#define XGI_DRAM_SIZE_4MB 0x02
+#define XGI_DRAM_SIZE_8MB 0x03
+#define XGI_DRAM_SIZE_16MB 0x04
+#define XGI_DRAM_SIZE_32MB 0x05
+#define XGI_DRAM_SIZE_64MB 0x06
+#define XGI_DRAM_SIZE_128MB 0x07
+#define XGI_DRAM_SIZE_256MB 0x08
+
+/* ------------------- Global Variables ----------------------------- */
+
+/* display status */
+static int XGIfb_crt1off;
+static int XGIfb_forcecrt1 = -1;
+
+/* global flags */
+static int XGIfb_tvmode;
+static int enable_dstn;
+static int XGIfb_ypan = -1;
+
+/* TW: CRT2 type (for overriding autodetection) */
+static int XGIfb_crt2type = -1;
+/* PR: Tv plug type (for overriding autodetection) */
+static int XGIfb_tvplug = -1;
+
+#define MD_XGI315 1
+
+/* mode table */
+static const struct _XGIbios_mode {
+ u8 mode_no;
+ u16 vesa_mode_no_1; /* "XGI defined" VESA mode number */
+ u16 vesa_mode_no_2; /* Real VESA mode numbers */
+ u16 xres;
+ u16 yres;
+ u16 bpp;
+ u8 chipset;
+} XGIbios_mode[] = {
+ { 0x56, 0x0000, 0x0000, 320, 240, 16, MD_XGI315 },
+ { 0x5A, 0x0000, 0x0000, 320, 480, 8, MD_XGI315 },
+ { 0x5B, 0x0000, 0x0000, 320, 480, 16, MD_XGI315 },
+ { 0x2E, 0x0101, 0x0101, 640, 480, 8, MD_XGI315 },
+ { 0x44, 0x0111, 0x0111, 640, 480, 16, MD_XGI315 },
+ { 0x62, 0x013a, 0x0112, 640, 480, 32, MD_XGI315 },
+ { 0x31, 0x0000, 0x0000, 720, 480, 8, MD_XGI315 },
+ { 0x33, 0x0000, 0x0000, 720, 480, 16, MD_XGI315 },
+ { 0x35, 0x0000, 0x0000, 720, 480, 32, MD_XGI315 },
+ { 0x32, 0x0000, 0x0000, 720, 576, 8, MD_XGI315 },
+ { 0x34, 0x0000, 0x0000, 720, 576, 16, MD_XGI315 },
+ { 0x36, 0x0000, 0x0000, 720, 576, 32, MD_XGI315 },
+ { 0x36, 0x0000, 0x0000, 720, 576, 32, MD_XGI315 },
+ { 0x70, 0x0000, 0x0000, 800, 480, 8, MD_XGI315 },
+ { 0x7a, 0x0000, 0x0000, 800, 480, 16, MD_XGI315 },
+ { 0x76, 0x0000, 0x0000, 800, 480, 32, MD_XGI315 },
+ { 0x30, 0x0103, 0x0103, 800, 600, 8, MD_XGI315 },
+#define DEFAULT_MODE 17 /* index for 800x600x16 */
+ { 0x47, 0x0114, 0x0114, 800, 600, 16, MD_XGI315 },
+ { 0x63, 0x013b, 0x0115, 800, 600, 32, MD_XGI315 },
+ { 0x71, 0x0000, 0x0000, 1024, 576, 8, MD_XGI315 },
+ { 0x74, 0x0000, 0x0000, 1024, 576, 16, MD_XGI315 },
+ { 0x77, 0x0000, 0x0000, 1024, 576, 32, MD_XGI315 },
+ { 0x77, 0x0000, 0x0000, 1024, 576, 32, MD_XGI315 },
+ { 0x20, 0x0000, 0x0000, 1024, 600, 8, },
+ { 0x21, 0x0000, 0x0000, 1024, 600, 16, },
+ { 0x22, 0x0000, 0x0000, 1024, 600, 32, },
+ { 0x38, 0x0105, 0x0105, 1024, 768, 8, MD_XGI315 },
+ { 0x4A, 0x0117, 0x0117, 1024, 768, 16, MD_XGI315 },
+ { 0x64, 0x013c, 0x0118, 1024, 768, 32, MD_XGI315 },
+ { 0x64, 0x013c, 0x0118, 1024, 768, 32, MD_XGI315 },
+ { 0x23, 0x0000, 0x0000, 1152, 768, 8, },
+ { 0x24, 0x0000, 0x0000, 1152, 768, 16, },
+ { 0x25, 0x0000, 0x0000, 1152, 768, 32, },
+ { 0x79, 0x0000, 0x0000, 1280, 720, 8, MD_XGI315 },
+ { 0x75, 0x0000, 0x0000, 1280, 720, 16, MD_XGI315 },
+ { 0x78, 0x0000, 0x0000, 1280, 720, 32, MD_XGI315 },
+ { 0x23, 0x0000, 0x0000, 1280, 768, 8, MD_XGI315 },
+ { 0x24, 0x0000, 0x0000, 1280, 768, 16, MD_XGI315 },
+ { 0x25, 0x0000, 0x0000, 1280, 768, 32, MD_XGI315 },
+ { 0x7C, 0x0000, 0x0000, 1280, 960, 8, MD_XGI315 },
+ { 0x7D, 0x0000, 0x0000, 1280, 960, 16, MD_XGI315 },
+ { 0x7E, 0x0000, 0x0000, 1280, 960, 32, MD_XGI315 },
+ { 0x3A, 0x0107, 0x0107, 1280, 1024, 8, MD_XGI315 },
+ { 0x4D, 0x011a, 0x011a, 1280, 1024, 16, MD_XGI315 },
+ { 0x65, 0x013d, 0x011b, 1280, 1024, 32, MD_XGI315 },
+ { 0x26, 0x0000, 0x0000, 1400, 1050, 8, MD_XGI315 },
+ { 0x27, 0x0000, 0x0000, 1400, 1050, 16, MD_XGI315 },
+ { 0x28, 0x0000, 0x0000, 1400, 1050, 32, MD_XGI315 },
+ { 0x3C, 0x0130, 0x011c, 1600, 1200, 8, MD_XGI315 },
+ { 0x3D, 0x0131, 0x011e, 1600, 1200, 16, MD_XGI315 },
+ { 0x66, 0x013e, 0x011f, 1600, 1200, 32, MD_XGI315 },
+ { 0x68, 0x013f, 0x0000, 1920, 1440, 8, MD_XGI315 },
+ { 0x69, 0x0140, 0x0000, 1920, 1440, 16, MD_XGI315 },
+ { 0x6B, 0x0141, 0x0000, 1920, 1440, 32, MD_XGI315 },
+ { 0x6c, 0x0000, 0x0000, 2048, 1536, 8, MD_XGI315 },
+ { 0x6d, 0x0000, 0x0000, 2048, 1536, 16, MD_XGI315 },
+ { 0x6e, 0x0000, 0x0000, 2048, 1536, 32, MD_XGI315 },
+ { 0 },
+};
+
+static const unsigned short XGI310paneltype[] = {
+ LCD_UNKNOWN, LCD_800x600, LCD_1024x768, LCD_1280x1024,
+ LCD_640x480, LCD_1024x600, LCD_1152x864, LCD_1280x960,
+ LCD_1152x768, LCD_1400x1050, LCD_1280x768, LCD_1600x1200,
+ LCD_1024x768, LCD_1024x768, LCD_1024x768};
+
+static const struct _XGI_crt2type {
+ char name[10];
+ int type_no;
+ int tvplug_no;
+} XGI_crt2type[] = {
+ {"NONE", 0, -1},
+ {"LCD", XGIFB_DISP_LCD, -1},
+ {"TV", XGIFB_DISP_TV, -1},
+ {"VGA", XGIFB_DISP_CRT, -1},
+ {"SVIDEO", XGIFB_DISP_TV, TVPLUG_SVIDEO},
+ {"COMPOSITE", XGIFB_DISP_TV, TVPLUG_COMPOSITE},
+ {"SCART", XGIFB_DISP_TV, TVPLUG_SCART},
+ {"none", 0, -1},
+ {"lcd", XGIFB_DISP_LCD, -1},
+ {"tv", XGIFB_DISP_TV, -1},
+ {"vga", XGIFB_DISP_CRT, -1},
+ {"svideo", XGIFB_DISP_TV, TVPLUG_SVIDEO},
+ {"composite", XGIFB_DISP_TV, TVPLUG_COMPOSITE},
+ {"scart", XGIFB_DISP_TV, TVPLUG_SCART},
+ {"\0", -1, -1}
+};
+
+/* TV standard */
+static const struct _XGI_tvtype {
+ char name[6];
+ int type_no;
+} XGI_tvtype[] = {
+ {"PAL", 1},
+ {"NTSC", 2},
+ {"pal", 1},
+ {"ntsc", 2},
+ {"\0", -1}
+};
+
+static const struct _XGI_vrate {
+ u16 idx;
+ u16 xres;
+ u16 yres;
+ u16 refresh;
+} XGIfb_vrate[] = {
+ {1, 640, 480, 60}, {2, 640, 480, 72},
+ {3, 640, 480, 75}, {4, 640, 480, 85},
+
+ {5, 640, 480, 100}, {6, 640, 480, 120},
+ {7, 640, 480, 160}, {8, 640, 480, 200},
+
+ {1, 720, 480, 60},
+ {1, 720, 576, 58},
+ {1, 800, 480, 60}, {2, 800, 480, 75}, {3, 800, 480, 85},
+ {1, 800, 600, 60}, {2, 800, 600, 72}, {3, 800, 600, 75},
+ {4, 800, 600, 85}, {5, 800, 600, 100},
+ {6, 800, 600, 120}, {7, 800, 600, 160},
+
+ {1, 1024, 768, 60}, {2, 1024, 768, 70}, {3, 1024, 768, 75},
+ {4, 1024, 768, 85}, {5, 1024, 768, 100}, {6, 1024, 768, 120},
+ {1, 1024, 576, 60}, {2, 1024, 576, 75}, {3, 1024, 576, 85},
+ {1, 1024, 600, 60},
+ {1, 1152, 768, 60},
+ {1, 1280, 720, 60}, {2, 1280, 720, 75}, {3, 1280, 720, 85},
+ {1, 1280, 768, 60},
+ {1, 1280, 1024, 60}, {2, 1280, 1024, 75}, {3, 1280, 1024, 85},
+ {1, 1280, 960, 70},
+ {1, 1400, 1050, 60},
+ {1, 1600, 1200, 60}, {2, 1600, 1200, 65},
+ {3, 1600, 1200, 70}, {4, 1600, 1200, 75},
+
+ {5, 1600, 1200, 85}, {6, 1600, 1200, 100},
+ {7, 1600, 1200, 120},
+
+ {1, 1920, 1440, 60}, {2, 1920, 1440, 65},
+ {3, 1920, 1440, 70}, {4, 1920, 1440, 75},
+
+ {5, 1920, 1440, 85}, {6, 1920, 1440, 100},
+ {1, 2048, 1536, 60}, {2, 2048, 1536, 65},
+ {3, 2048, 1536, 70}, {4, 2048, 1536, 75},
+
+ {5, 2048, 1536, 85},
+ {0, 0, 0, 0}
+};
+
+static const struct _XGI_TV_filter {
+ u8 filter[9][4];
+} XGI_TV_filter[] = {
+ { { {0x00, 0x00, 0x00, 0x40}, /* NTSCFilter_0 */
+ {0x00, 0xE0, 0x10, 0x60},
+ {0x00, 0xEE, 0x10, 0x44},
+ {0x00, 0xF4, 0x10, 0x38},
+ {0xF8, 0xF4, 0x18, 0x38},
+ {0xFC, 0xFB, 0x14, 0x2A},
+ {0x00, 0x00, 0x10, 0x20},
+ {0x00, 0x04, 0x10, 0x18},
+ {0xFF, 0xFF, 0xFF, 0xFF} } },
+ { { {0x00, 0x00, 0x00, 0x40}, /* NTSCFilter_1 */
+ {0x00, 0xE0, 0x10, 0x60},
+ {0x00, 0xEE, 0x10, 0x44},
+ {0x00, 0xF4, 0x10, 0x38},
+ {0xF8, 0xF4, 0x18, 0x38},
+ {0xFC, 0xFB, 0x14, 0x2A},
+ {0x00, 0x00, 0x10, 0x20},
+ {0x00, 0x04, 0x10, 0x18},
+ {0xFF, 0xFF, 0xFF, 0xFF} } },
+ { { {0x00, 0x00, 0x00, 0x40}, /* NTSCFilter_2 */
+ {0xF5, 0xEE, 0x1B, 0x44},
+ {0xF8, 0xF4, 0x18, 0x38},
+ {0xEB, 0x04, 0x25, 0x18},
+ {0xF1, 0x05, 0x1F, 0x16},
+ {0xF6, 0x06, 0x1A, 0x14},
+ {0xFA, 0x06, 0x16, 0x14},
+ {0x00, 0x04, 0x10, 0x18},
+ {0xFF, 0xFF, 0xFF, 0xFF} } },
+ { { {0x00, 0x00, 0x00, 0x40}, /* NTSCFilter_3 */
+ {0xF1, 0x04, 0x1F, 0x18},
+ {0xEE, 0x0D, 0x22, 0x06},
+ {0xF7, 0x06, 0x19, 0x14},
+ {0xF4, 0x0B, 0x1C, 0x0A},
+ {0xFA, 0x07, 0x16, 0x12},
+ {0xF9, 0x0A, 0x17, 0x0C},
+ {0x00, 0x07, 0x10, 0x12},
+ {0xFF, 0xFF, 0xFF, 0xFF} } },
+ { { {0x00, 0x00, 0x00, 0x40}, /* NTSCFilter_4 */
+ {0x00, 0xE0, 0x10, 0x60},
+ {0x00, 0xEE, 0x10, 0x44},
+ {0x00, 0xF4, 0x10, 0x38},
+ {0xF8, 0xF4, 0x18, 0x38},
+ {0xFC, 0xFB, 0x14, 0x2A},
+ {0x00, 0x00, 0x10, 0x20},
+ {0x00, 0x04, 0x10, 0x18},
+ {0xFF, 0xFF, 0xFF, 0xFF} } },
+ { { {0x00, 0x00, 0x00, 0x40}, /* NTSCFilter_5 */
+ {0xF5, 0xEE, 0x1B, 0x44},
+ {0xF8, 0xF4, 0x18, 0x38},
+ {0xEB, 0x04, 0x25, 0x18},
+ {0xF1, 0x05, 0x1F, 0x16},
+ {0xF6, 0x06, 0x1A, 0x14},
+ {0xFA, 0x06, 0x16, 0x14},
+ {0x00, 0x04, 0x10, 0x18},
+ {0xFF, 0xFF, 0xFF, 0xFF} } },
+ { { {0x00, 0x00, 0x00, 0x40}, /* NTSCFilter_6 */
+ {0xEB, 0x04, 0x25, 0x18},
+ {0xE7, 0x0E, 0x29, 0x04},
+ {0xEE, 0x0C, 0x22, 0x08},
+ {0xF6, 0x0B, 0x1A, 0x0A},
+ {0xF9, 0x0A, 0x17, 0x0C},
+ {0xFC, 0x0A, 0x14, 0x0C},
+ {0x00, 0x08, 0x10, 0x10},
+ {0xFF, 0xFF, 0xFF, 0xFF} } },
+ { { {0x00, 0x00, 0x00, 0x40}, /* NTSCFilter_7 */
+ {0xEC, 0x02, 0x24, 0x1C},
+ {0xF2, 0x04, 0x1E, 0x18},
+ {0xEB, 0x15, 0x25, 0xF6},
+ {0xF4, 0x10, 0x1C, 0x00},
+ {0xF8, 0x0F, 0x18, 0x02},
+ {0x00, 0x04, 0x10, 0x18},
+ {0x01, 0x06, 0x0F, 0x14},
+ {0xFF, 0xFF, 0xFF, 0xFF} } },
+ { { {0x00, 0x00, 0x00, 0x40}, /* PALFilter_0 */
+ {0x00, 0xE0, 0x10, 0x60},
+ {0x00, 0xEE, 0x10, 0x44},
+ {0x00, 0xF4, 0x10, 0x38},
+ {0xF8, 0xF4, 0x18, 0x38},
+ {0xFC, 0xFB, 0x14, 0x2A},
+ {0x00, 0x00, 0x10, 0x20},
+ {0x00, 0x04, 0x10, 0x18},
+ {0xFF, 0xFF, 0xFF, 0xFF} } },
+ { { {0x00, 0x00, 0x00, 0x40}, /* PALFilter_1 */
+ {0x00, 0xE0, 0x10, 0x60},
+ {0x00, 0xEE, 0x10, 0x44},
+ {0x00, 0xF4, 0x10, 0x38},
+ {0xF8, 0xF4, 0x18, 0x38},
+ {0xFC, 0xFB, 0x14, 0x2A},
+ {0x00, 0x00, 0x10, 0x20},
+ {0x00, 0x04, 0x10, 0x18},
+ {0xFF, 0xFF, 0xFF, 0xFF} } },
+ { { {0x00, 0x00, 0x00, 0x40}, /* PALFilter_2 */
+ {0xF5, 0xEE, 0x1B, 0x44},
+ {0xF8, 0xF4, 0x18, 0x38},
+ {0xF1, 0xF7, 0x01, 0x32},
+ {0xF5, 0xFB, 0x1B, 0x2A},
+ {0xF9, 0xFF, 0x17, 0x22},
+ {0xFB, 0x01, 0x15, 0x1E},
+ {0x00, 0x04, 0x10, 0x18},
+ {0xFF, 0xFF, 0xFF, 0xFF} } },
+ { { {0x00, 0x00, 0x00, 0x40}, /* PALFilter_3 */
+ {0xF5, 0xFB, 0x1B, 0x2A},
+ {0xEE, 0xFE, 0x22, 0x24},
+ {0xF3, 0x00, 0x1D, 0x20},
+ {0xF9, 0x03, 0x17, 0x1A},
+ {0xFB, 0x02, 0x14, 0x1E},
+ {0xFB, 0x04, 0x15, 0x18},
+ {0x00, 0x06, 0x10, 0x14},
+ {0xFF, 0xFF, 0xFF, 0xFF} } },
+ { { {0x00, 0x00, 0x00, 0x40}, /* PALFilter_4 */
+ {0x00, 0xE0, 0x10, 0x60},
+ {0x00, 0xEE, 0x10, 0x44},
+ {0x00, 0xF4, 0x10, 0x38},
+ {0xF8, 0xF4, 0x18, 0x38},
+ {0xFC, 0xFB, 0x14, 0x2A},
+ {0x00, 0x00, 0x10, 0x20},
+ {0x00, 0x04, 0x10, 0x18},
+ {0xFF, 0xFF, 0xFF, 0xFF} } },
+ { { {0x00, 0x00, 0x00, 0x40}, /* PALFilter_5 */
+ {0xF5, 0xEE, 0x1B, 0x44},
+ {0xF8, 0xF4, 0x18, 0x38},
+ {0xF1, 0xF7, 0x1F, 0x32},
+ {0xF5, 0xFB, 0x1B, 0x2A},
+ {0xF9, 0xFF, 0x17, 0x22},
+ {0xFB, 0x01, 0x15, 0x1E},
+ {0x00, 0x04, 0x10, 0x18},
+ {0xFF, 0xFF, 0xFF, 0xFF} } },
+ { { {0x00, 0x00, 0x00, 0x40}, /* PALFilter_6 */
+ {0xF5, 0xEE, 0x1B, 0x2A},
+ {0xEE, 0xFE, 0x22, 0x24},
+ {0xF3, 0x00, 0x1D, 0x20},
+ {0xF9, 0x03, 0x17, 0x1A},
+ {0xFB, 0x02, 0x14, 0x1E},
+ {0xFB, 0x04, 0x15, 0x18},
+ {0x00, 0x06, 0x10, 0x14},
+ {0xFF, 0xFF, 0xFF, 0xFF} } },
+ { { {0x00, 0x00, 0x00, 0x40}, /* PALFilter_7 */
+ {0xF5, 0xEE, 0x1B, 0x44},
+ {0xF8, 0xF4, 0x18, 0x38},
+ {0xFC, 0xFB, 0x14, 0x2A},
+ {0xEB, 0x05, 0x25, 0x16},
+ {0xF1, 0x05, 0x1F, 0x16},
+ {0xFA, 0x07, 0x16, 0x12},
+ {0x00, 0x07, 0x10, 0x12},
+ {0xFF, 0xFF, 0xFF, 0xFF} } }
+};
+
+static int filter = -1;
+
+#endif
diff --git a/drivers/staging/xgifb/XGI_main_26.c b/drivers/staging/xgifb/XGI_main_26.c
new file mode 100644
index 000000000..74e882007
--- /dev/null
+++ b/drivers/staging/xgifb/XGI_main_26.c
@@ -0,0 +1,2127 @@
+/*
+ * XG20, XG21, XG40, XG42 frame buffer device
+ * for Linux kernels 2.5.x, 2.6.x
+ * Base on TW's sis fbdev code.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/sizes.h>
+#include <linux/module.h>
+
+#ifdef CONFIG_MTRR
+#include <asm/mtrr.h>
+#endif
+
+#include "XGI_main.h"
+#include "vb_init.h"
+#include "vb_util.h"
+#include "vb_setmode.h"
+
+#define Index_CR_GPIO_Reg1 0x48
+#define Index_CR_GPIO_Reg3 0x4a
+
+#define GPIOG_EN (1<<6)
+#define GPIOG_READ (1<<1)
+
+static char *forcecrt2type;
+static char *mode;
+static int vesa = -1;
+static unsigned int refresh_rate;
+
+/* -------------------- Macro definitions ---------------------------- */
+
+#ifdef DEBUG
+static void dumpVGAReg(void)
+{
+ u8 i, reg;
+
+ xgifb_reg_set(XGISR, 0x05, 0x86);
+
+ for (i = 0; i < 0x4f; i++) {
+ reg = xgifb_reg_get(XGISR, i);
+ pr_debug("o 3c4 %x\n", i);
+ pr_debug("i 3c5 => %x\n", reg);
+ }
+
+ for (i = 0; i < 0xF0; i++) {
+ reg = xgifb_reg_get(XGICR, i);
+ pr_debug("o 3d4 %x\n", i);
+ pr_debug("i 3d5 => %x\n", reg);
+ }
+}
+#else
+static inline void dumpVGAReg(void)
+{
+}
+#endif
+
+/* --------------- Hardware Access Routines -------------------------- */
+
+static int XGIfb_mode_rate_to_dclock(struct vb_device_info *XGI_Pr,
+ struct xgi_hw_device_info *HwDeviceExtension,
+ unsigned char modeno)
+{
+ unsigned short ModeNo = modeno;
+ unsigned short ModeIdIndex = 0, ClockIndex = 0;
+ unsigned short RefreshRateTableIndex = 0;
+ int Clock;
+
+ InitTo330Pointer(HwDeviceExtension->jChipType, XGI_Pr);
+
+ XGI_SearchModeID(ModeNo, &ModeIdIndex);
+
+ RefreshRateTableIndex = XGI_GetRatePtrCRT2(HwDeviceExtension, ModeNo,
+ ModeIdIndex, XGI_Pr);
+
+ ClockIndex = XGI330_RefIndex[RefreshRateTableIndex].Ext_CRTVCLK;
+
+ Clock = XGI_VCLKData[ClockIndex].CLOCK * 1000;
+
+ return Clock;
+}
+
+static int XGIfb_mode_rate_to_ddata(struct vb_device_info *XGI_Pr,
+ struct xgi_hw_device_info *HwDeviceExtension,
+ unsigned char modeno,
+ u32 *left_margin, u32 *right_margin, u32 *upper_margin,
+ u32 *lower_margin, u32 *hsync_len, u32 *vsync_len, u32 *sync,
+ u32 *vmode)
+{
+ unsigned short ModeNo = modeno;
+ unsigned short ModeIdIndex, index = 0;
+ unsigned short RefreshRateTableIndex = 0;
+
+ unsigned short VRE, VBE, VRS, VDE;
+ unsigned short HRE, HBE, HRS, HDE;
+ unsigned char sr_data, cr_data, cr_data2;
+ int B, C, D, F, temp, j;
+
+ InitTo330Pointer(HwDeviceExtension->jChipType, XGI_Pr);
+ if (!XGI_SearchModeID(ModeNo, &ModeIdIndex))
+ return 0;
+ RefreshRateTableIndex = XGI_GetRatePtrCRT2(HwDeviceExtension, ModeNo,
+ ModeIdIndex, XGI_Pr);
+ index = XGI330_RefIndex[RefreshRateTableIndex].Ext_CRT1CRTC;
+
+ sr_data = XGI_CRT1Table[index].CR[5];
+
+ HDE = XGI330_RefIndex[RefreshRateTableIndex].XRes >> 3;
+
+ cr_data = XGI_CRT1Table[index].CR[3];
+
+ /* Horizontal retrace (=sync) start */
+ HRS = (cr_data & 0xff) | ((unsigned short) (sr_data & 0xC0) << 2);
+ F = HRS - HDE - 3;
+
+ sr_data = XGI_CRT1Table[index].CR[6];
+
+ cr_data = XGI_CRT1Table[index].CR[2];
+
+ cr_data2 = XGI_CRT1Table[index].CR[4];
+
+ /* Horizontal blank end */
+ HBE = (cr_data & 0x1f) | ((unsigned short) (cr_data2 & 0x80) >> 2)
+ | ((unsigned short) (sr_data & 0x03) << 6);
+
+ /* Horizontal retrace (=sync) end */
+ HRE = (cr_data2 & 0x1f) | ((sr_data & 0x04) << 3);
+
+ temp = HBE - ((HDE - 1) & 255);
+ B = (temp > 0) ? temp : (temp + 256);
+
+ temp = HRE - ((HDE + F + 3) & 63);
+ C = (temp > 0) ? temp : (temp + 64);
+
+ D = B - F - C;
+
+ *left_margin = D * 8;
+ *right_margin = F * 8;
+ *hsync_len = C * 8;
+
+ sr_data = XGI_CRT1Table[index].CR[14];
+
+ cr_data2 = XGI_CRT1Table[index].CR[9];
+
+ VDE = XGI330_RefIndex[RefreshRateTableIndex].YRes;
+
+ cr_data = XGI_CRT1Table[index].CR[10];
+
+ /* Vertical retrace (=sync) start */
+ VRS = (cr_data & 0xff) | ((unsigned short) (cr_data2 & 0x04) << 6)
+ | ((unsigned short) (cr_data2 & 0x80) << 2)
+ | ((unsigned short) (sr_data & 0x08) << 7);
+ F = VRS + 1 - VDE;
+
+ cr_data = XGI_CRT1Table[index].CR[13];
+
+ /* Vertical blank end */
+ VBE = (cr_data & 0xff) | ((unsigned short) (sr_data & 0x10) << 4);
+ temp = VBE - ((VDE - 1) & 511);
+ B = (temp > 0) ? temp : (temp + 512);
+
+ cr_data = XGI_CRT1Table[index].CR[11];
+
+ /* Vertical retrace (=sync) end */
+ VRE = (cr_data & 0x0f) | ((sr_data & 0x20) >> 1);
+ temp = VRE - ((VDE + F - 1) & 31);
+ C = (temp > 0) ? temp : (temp + 32);
+
+ D = B - F - C;
+
+ *upper_margin = D;
+ *lower_margin = F;
+ *vsync_len = C;
+
+ if (XGI330_RefIndex[RefreshRateTableIndex].Ext_InfoFlag & 0x8000)
+ *sync &= ~FB_SYNC_VERT_HIGH_ACT;
+ else
+ *sync |= FB_SYNC_VERT_HIGH_ACT;
+
+ if (XGI330_RefIndex[RefreshRateTableIndex].Ext_InfoFlag & 0x4000)
+ *sync &= ~FB_SYNC_HOR_HIGH_ACT;
+ else
+ *sync |= FB_SYNC_HOR_HIGH_ACT;
+
+ *vmode = FB_VMODE_NONINTERLACED;
+ if (XGI330_RefIndex[RefreshRateTableIndex].Ext_InfoFlag & 0x0080)
+ *vmode = FB_VMODE_INTERLACED;
+ else {
+ j = 0;
+ while (XGI330_EModeIDTable[j].Ext_ModeID != 0xff) {
+ if (XGI330_EModeIDTable[j].Ext_ModeID ==
+ XGI330_RefIndex[RefreshRateTableIndex].ModeID) {
+ if (XGI330_EModeIDTable[j].Ext_ModeFlag &
+ DoubleScanMode) {
+ *vmode = FB_VMODE_DOUBLE;
+ }
+ break;
+ }
+ j++;
+ }
+ }
+
+ return 1;
+}
+
+void XGIRegInit(struct vb_device_info *XGI_Pr, unsigned long BaseAddr)
+{
+ XGI_Pr->P3c4 = BaseAddr + 0x14;
+ XGI_Pr->P3d4 = BaseAddr + 0x24;
+ XGI_Pr->P3c0 = BaseAddr + 0x10;
+ XGI_Pr->P3ce = BaseAddr + 0x1e;
+ XGI_Pr->P3c2 = BaseAddr + 0x12;
+ XGI_Pr->P3cc = BaseAddr + 0x1c;
+ XGI_Pr->P3ca = BaseAddr + 0x1a;
+ XGI_Pr->P3c6 = BaseAddr + 0x16;
+ XGI_Pr->P3c7 = BaseAddr + 0x17;
+ XGI_Pr->P3c8 = BaseAddr + 0x18;
+ XGI_Pr->P3c9 = BaseAddr + 0x19;
+ XGI_Pr->P3da = BaseAddr + 0x2A;
+ XGI_Pr->Part0Port = BaseAddr + XGI_CRT2_PORT_00;
+ /* Digital video interface registers (LCD) */
+ XGI_Pr->Part1Port = BaseAddr + SIS_CRT2_PORT_04;
+ /* 301 TV Encoder registers */
+ XGI_Pr->Part2Port = BaseAddr + SIS_CRT2_PORT_10;
+ /* 301 Macrovision registers */
+ XGI_Pr->Part3Port = BaseAddr + SIS_CRT2_PORT_12;
+ /* 301 VGA2 (and LCD) registers */
+ XGI_Pr->Part4Port = BaseAddr + SIS_CRT2_PORT_14;
+ /* 301 palette address port registers */
+ XGI_Pr->Part5Port = BaseAddr + SIS_CRT2_PORT_14 + 2;
+
+}
+
+/* ------------------ Internal helper routines ----------------- */
+
+static int XGIfb_GetXG21DefaultLVDSModeIdx(struct xgifb_video_info *xgifb_info)
+{
+ int i = 0;
+
+ while ((XGIbios_mode[i].mode_no != 0)
+ && (XGIbios_mode[i].xres <= xgifb_info->lvds_data.LVDSHDE)) {
+ if ((XGIbios_mode[i].xres == xgifb_info->lvds_data.LVDSHDE)
+ && (XGIbios_mode[i].yres == xgifb_info->lvds_data.LVDSVDE)
+ && (XGIbios_mode[i].bpp == 8)) {
+ return i;
+ }
+ i++;
+ }
+
+ return -1;
+}
+
+static void XGIfb_search_mode(struct xgifb_video_info *xgifb_info,
+ const char *name)
+{
+ unsigned int xres;
+ unsigned int yres;
+ unsigned int bpp;
+ int i;
+
+ if (sscanf(name, "%ux%ux%u", &xres, &yres, &bpp) != 3)
+ goto invalid_mode;
+
+ if (bpp == 24)
+ bpp = 32; /* That's for people who mix up color and fb depth. */
+
+ for (i = 0; XGIbios_mode[i].mode_no != 0; i++)
+ if (XGIbios_mode[i].xres == xres &&
+ XGIbios_mode[i].yres == yres &&
+ XGIbios_mode[i].bpp == bpp) {
+ xgifb_info->mode_idx = i;
+ return;
+ }
+invalid_mode:
+ pr_info("Invalid mode '%s'\n", name);
+}
+
+static void XGIfb_search_vesamode(struct xgifb_video_info *xgifb_info,
+ unsigned int vesamode)
+{
+ int i = 0;
+
+ if (vesamode == 0)
+ goto invalid;
+
+ vesamode &= 0x1dff; /* Clean VESA mode number from other flags */
+
+ while (XGIbios_mode[i].mode_no != 0) {
+ if ((XGIbios_mode[i].vesa_mode_no_1 == vesamode) ||
+ (XGIbios_mode[i].vesa_mode_no_2 == vesamode)) {
+ xgifb_info->mode_idx = i;
+ return;
+ }
+ i++;
+ }
+
+invalid:
+ pr_info("Invalid VESA mode 0x%x'\n", vesamode);
+}
+
+static int XGIfb_validate_mode(struct xgifb_video_info *xgifb_info, int myindex)
+{
+ u16 xres, yres;
+ struct xgi_hw_device_info *hw_info = &xgifb_info->hw_info;
+ unsigned long required_mem;
+
+ if (xgifb_info->chip == XG21) {
+ if (xgifb_info->display2 == XGIFB_DISP_LCD) {
+ xres = xgifb_info->lvds_data.LVDSHDE;
+ yres = xgifb_info->lvds_data.LVDSVDE;
+ if (XGIbios_mode[myindex].xres > xres)
+ return -1;
+ if (XGIbios_mode[myindex].yres > yres)
+ return -1;
+ if ((XGIbios_mode[myindex].xres < xres) &&
+ (XGIbios_mode[myindex].yres < yres)) {
+ if (XGIbios_mode[myindex].bpp > 8)
+ return -1;
+ }
+
+ }
+ goto check_memory;
+
+ }
+
+ /* FIXME: for now, all is valid on XG27 */
+ if (xgifb_info->chip == XG27)
+ goto check_memory;
+
+ if (!(XGIbios_mode[myindex].chipset & MD_XGI315))
+ return -1;
+
+ switch (xgifb_info->display2) {
+ case XGIFB_DISP_LCD:
+ switch (hw_info->ulCRT2LCDType) {
+ case LCD_640x480:
+ xres = 640;
+ yres = 480;
+ break;
+ case LCD_800x600:
+ xres = 800;
+ yres = 600;
+ break;
+ case LCD_1024x600:
+ xres = 1024;
+ yres = 600;
+ break;
+ case LCD_1024x768:
+ xres = 1024;
+ yres = 768;
+ break;
+ case LCD_1152x768:
+ xres = 1152;
+ yres = 768;
+ break;
+ case LCD_1280x960:
+ xres = 1280;
+ yres = 960;
+ break;
+ case LCD_1280x768:
+ xres = 1280;
+ yres = 768;
+ break;
+ case LCD_1280x1024:
+ xres = 1280;
+ yres = 1024;
+ break;
+ case LCD_1400x1050:
+ xres = 1400;
+ yres = 1050;
+ break;
+ case LCD_1600x1200:
+ xres = 1600;
+ yres = 1200;
+ break;
+ default:
+ xres = 0;
+ yres = 0;
+ break;
+ }
+ if (XGIbios_mode[myindex].xres > xres)
+ return -1;
+ if (XGIbios_mode[myindex].yres > yres)
+ return -1;
+ if ((hw_info->ulExternalChip == 0x01) || /* LVDS */
+ (hw_info->ulExternalChip == 0x05)) { /* LVDS+Chrontel */
+ switch (XGIbios_mode[myindex].xres) {
+ case 512:
+ if (XGIbios_mode[myindex].yres != 512)
+ return -1;
+ if (hw_info->ulCRT2LCDType == LCD_1024x600)
+ return -1;
+ break;
+ case 640:
+ if ((XGIbios_mode[myindex].yres != 400)
+ && (XGIbios_mode[myindex].yres
+ != 480))
+ return -1;
+ break;
+ case 800:
+ if (XGIbios_mode[myindex].yres != 600)
+ return -1;
+ break;
+ case 1024:
+ if ((XGIbios_mode[myindex].yres != 600) &&
+ (XGIbios_mode[myindex].yres != 768))
+ return -1;
+ if ((XGIbios_mode[myindex].yres == 600) &&
+ (hw_info->ulCRT2LCDType != LCD_1024x600))
+ return -1;
+ break;
+ case 1152:
+ if ((XGIbios_mode[myindex].yres) != 768)
+ return -1;
+ if (hw_info->ulCRT2LCDType != LCD_1152x768)
+ return -1;
+ break;
+ case 1280:
+ if ((XGIbios_mode[myindex].yres != 768) &&
+ (XGIbios_mode[myindex].yres != 1024))
+ return -1;
+ if ((XGIbios_mode[myindex].yres == 768) &&
+ (hw_info->ulCRT2LCDType != LCD_1280x768))
+ return -1;
+ break;
+ case 1400:
+ if (XGIbios_mode[myindex].yres != 1050)
+ return -1;
+ break;
+ case 1600:
+ if (XGIbios_mode[myindex].yres != 1200)
+ return -1;
+ break;
+ default:
+ return -1;
+ }
+ } else {
+ switch (XGIbios_mode[myindex].xres) {
+ case 512:
+ if (XGIbios_mode[myindex].yres != 512)
+ return -1;
+ break;
+ case 640:
+ if ((XGIbios_mode[myindex].yres != 400) &&
+ (XGIbios_mode[myindex].yres != 480))
+ return -1;
+ break;
+ case 800:
+ if (XGIbios_mode[myindex].yres != 600)
+ return -1;
+ break;
+ case 1024:
+ if (XGIbios_mode[myindex].yres != 768)
+ return -1;
+ break;
+ case 1280:
+ if ((XGIbios_mode[myindex].yres != 960) &&
+ (XGIbios_mode[myindex].yres != 1024))
+ return -1;
+ if (XGIbios_mode[myindex].yres == 960) {
+ if (hw_info->ulCRT2LCDType ==
+ LCD_1400x1050)
+ return -1;
+ }
+ break;
+ case 1400:
+ if (XGIbios_mode[myindex].yres != 1050)
+ return -1;
+ break;
+ case 1600:
+ if (XGIbios_mode[myindex].yres != 1200)
+ return -1;
+ break;
+ default:
+ return -1;
+ }
+ }
+ break;
+ case XGIFB_DISP_TV:
+ switch (XGIbios_mode[myindex].xres) {
+ case 512:
+ case 640:
+ case 800:
+ break;
+ case 720:
+ if (xgifb_info->TV_type == TVMODE_NTSC) {
+ if (XGIbios_mode[myindex].yres != 480)
+ return -1;
+ } else if (xgifb_info->TV_type == TVMODE_PAL) {
+ if (XGIbios_mode[myindex].yres != 576)
+ return -1;
+ }
+ /* LVDS/CHRONTEL does not support 720 */
+ if (xgifb_info->hasVB == HASVB_LVDS_CHRONTEL ||
+ xgifb_info->hasVB == HASVB_CHRONTEL) {
+ return -1;
+ }
+ break;
+ case 1024:
+ if (xgifb_info->TV_type == TVMODE_NTSC) {
+ if (XGIbios_mode[myindex].bpp == 32)
+ return -1;
+ }
+ break;
+ default:
+ return -1;
+ }
+ break;
+ case XGIFB_DISP_CRT:
+ if (XGIbios_mode[myindex].xres > 1280)
+ return -1;
+ break;
+ case XGIFB_DISP_NONE:
+ break;
+ }
+
+check_memory:
+ required_mem = XGIbios_mode[myindex].xres * XGIbios_mode[myindex].yres *
+ XGIbios_mode[myindex].bpp / 8;
+ if (required_mem > xgifb_info->video_size)
+ return -1;
+ return myindex;
+
+}
+
+static void XGIfb_search_crt2type(const char *name)
+{
+ int i = 0;
+
+ if (name == NULL)
+ return;
+
+ while (XGI_crt2type[i].type_no != -1) {
+ if (!strcmp(name, XGI_crt2type[i].name)) {
+ XGIfb_crt2type = XGI_crt2type[i].type_no;
+ XGIfb_tvplug = XGI_crt2type[i].tvplug_no;
+ break;
+ }
+ i++;
+ }
+ if (XGIfb_crt2type < 0)
+ pr_info("Invalid CRT2 type: %s\n", name);
+}
+
+static u8 XGIfb_search_refresh_rate(struct xgifb_video_info *xgifb_info,
+ unsigned int rate)
+{
+ u16 xres, yres;
+ int i = 0;
+
+ xres = XGIbios_mode[xgifb_info->mode_idx].xres;
+ yres = XGIbios_mode[xgifb_info->mode_idx].yres;
+
+ xgifb_info->rate_idx = 0;
+ while ((XGIfb_vrate[i].idx != 0) && (XGIfb_vrate[i].xres <= xres)) {
+ if ((XGIfb_vrate[i].xres == xres) &&
+ (XGIfb_vrate[i].yres == yres)) {
+ if (XGIfb_vrate[i].refresh == rate) {
+ xgifb_info->rate_idx = XGIfb_vrate[i].idx;
+ break;
+ } else if (XGIfb_vrate[i].refresh > rate) {
+ if ((XGIfb_vrate[i].refresh - rate) <= 3) {
+ pr_debug("Adjusting rate from %d up to %d\n",
+ rate, XGIfb_vrate[i].refresh);
+ xgifb_info->rate_idx =
+ XGIfb_vrate[i].idx;
+ xgifb_info->refresh_rate =
+ XGIfb_vrate[i].refresh;
+ } else if (((rate - XGIfb_vrate[i - 1].refresh)
+ <= 2) && (XGIfb_vrate[i].idx
+ != 1)) {
+ pr_debug("Adjusting rate from %d down to %d\n",
+ rate,
+ XGIfb_vrate[i-1].refresh);
+ xgifb_info->rate_idx =
+ XGIfb_vrate[i - 1].idx;
+ xgifb_info->refresh_rate =
+ XGIfb_vrate[i - 1].refresh;
+ }
+ break;
+ } else if ((rate - XGIfb_vrate[i].refresh) <= 2) {
+ pr_debug("Adjusting rate from %d down to %d\n",
+ rate, XGIfb_vrate[i].refresh);
+ xgifb_info->rate_idx = XGIfb_vrate[i].idx;
+ break;
+ }
+ }
+ i++;
+ }
+ if (xgifb_info->rate_idx > 0)
+ return xgifb_info->rate_idx;
+ pr_info("Unsupported rate %d for %dx%d\n",
+ rate, xres, yres);
+ return 0;
+}
+
+static void XGIfb_search_tvstd(const char *name)
+{
+ int i = 0;
+
+ if (name == NULL)
+ return;
+
+ while (XGI_tvtype[i].type_no != -1) {
+ if (!strcmp(name, XGI_tvtype[i].name)) {
+ XGIfb_tvmode = XGI_tvtype[i].type_no;
+ break;
+ }
+ i++;
+ }
+}
+
+/* ----------- FBDev related routines for all series ----------- */
+
+static void XGIfb_bpp_to_var(struct xgifb_video_info *xgifb_info,
+ struct fb_var_screeninfo *var)
+{
+ switch (var->bits_per_pixel) {
+ case 8:
+ var->red.offset = var->green.offset = var->blue.offset = 0;
+ var->red.length = var->green.length = var->blue.length = 6;
+ xgifb_info->video_cmap_len = 256;
+ break;
+ case 16:
+ var->red.offset = 11;
+ var->red.length = 5;
+ var->green.offset = 5;
+ var->green.length = 6;
+ var->blue.offset = 0;
+ var->blue.length = 5;
+ var->transp.offset = 0;
+ var->transp.length = 0;
+ xgifb_info->video_cmap_len = 16;
+ break;
+ case 32:
+ var->red.offset = 16;
+ var->red.length = 8;
+ var->green.offset = 8;
+ var->green.length = 8;
+ var->blue.offset = 0;
+ var->blue.length = 8;
+ var->transp.offset = 24;
+ var->transp.length = 8;
+ xgifb_info->video_cmap_len = 16;
+ break;
+ }
+}
+
+/* --------------------- SetMode routines ------------------------- */
+
+static void XGIfb_pre_setmode(struct xgifb_video_info *xgifb_info)
+{
+ u8 cr30 = 0, cr31 = 0;
+
+ cr31 = xgifb_reg_get(XGICR, 0x31);
+ cr31 &= ~0x60;
+
+ switch (xgifb_info->display2) {
+ case XGIFB_DISP_CRT:
+ cr30 = (SIS_VB_OUTPUT_CRT2 | SIS_SIMULTANEOUS_VIEW_ENABLE);
+ cr31 |= SIS_DRIVER_MODE;
+ break;
+ case XGIFB_DISP_LCD:
+ cr30 = (SIS_VB_OUTPUT_LCD | SIS_SIMULTANEOUS_VIEW_ENABLE);
+ cr31 |= SIS_DRIVER_MODE;
+ break;
+ case XGIFB_DISP_TV:
+ if (xgifb_info->TV_type == TVMODE_HIVISION)
+ cr30 = (SIS_VB_OUTPUT_HIVISION
+ | SIS_SIMULTANEOUS_VIEW_ENABLE);
+ else if (xgifb_info->TV_plug == TVPLUG_SVIDEO)
+ cr30 = (SIS_VB_OUTPUT_SVIDEO
+ | SIS_SIMULTANEOUS_VIEW_ENABLE);
+ else if (xgifb_info->TV_plug == TVPLUG_COMPOSITE)
+ cr30 = (SIS_VB_OUTPUT_COMPOSITE
+ | SIS_SIMULTANEOUS_VIEW_ENABLE);
+ else if (xgifb_info->TV_plug == TVPLUG_SCART)
+ cr30 = (SIS_VB_OUTPUT_SCART
+ | SIS_SIMULTANEOUS_VIEW_ENABLE);
+ cr31 |= SIS_DRIVER_MODE;
+
+ if (XGIfb_tvmode == 1 || xgifb_info->TV_type == TVMODE_PAL)
+ cr31 |= 0x01;
+ else
+ cr31 &= ~0x01;
+ break;
+ default: /* disable CRT2 */
+ cr30 = 0x00;
+ cr31 |= (SIS_DRIVER_MODE | SIS_VB_OUTPUT_DISABLE);
+ }
+
+ xgifb_reg_set(XGICR, IND_XGI_SCRATCH_REG_CR30, cr30);
+ xgifb_reg_set(XGICR, IND_XGI_SCRATCH_REG_CR31, cr31);
+ xgifb_reg_set(XGICR, IND_XGI_SCRATCH_REG_CR33,
+ (xgifb_info->rate_idx & 0x0F));
+}
+
+static void XGIfb_post_setmode(struct xgifb_video_info *xgifb_info)
+{
+ u8 reg;
+ unsigned char doit = 1;
+
+ if (xgifb_info->video_bpp == 8) {
+ /*
+ * We can't switch off CRT1 on LVDS/Chrontel
+ * in 8bpp Modes
+ */
+ if ((xgifb_info->hasVB == HASVB_LVDS) ||
+ (xgifb_info->hasVB == HASVB_LVDS_CHRONTEL)) {
+ doit = 0;
+ }
+ /*
+ * We can't switch off CRT1 on 301B-DH
+ * in 8bpp Modes if using LCD
+ */
+ if (xgifb_info->display2 == XGIFB_DISP_LCD)
+ doit = 0;
+ }
+
+ /* We can't switch off CRT1 if bridge is in slave mode */
+ if (xgifb_info->hasVB != HASVB_NONE) {
+ reg = xgifb_reg_get(XGIPART1, 0x00);
+
+ if ((reg & 0x50) == 0x10)
+ doit = 0;
+
+ } else {
+ XGIfb_crt1off = 0;
+ }
+
+ reg = xgifb_reg_get(XGICR, 0x17);
+ if ((XGIfb_crt1off) && (doit))
+ reg &= ~0x80;
+ else
+ reg |= 0x80;
+ xgifb_reg_set(XGICR, 0x17, reg);
+
+ xgifb_reg_and(XGISR, IND_SIS_RAMDAC_CONTROL, ~0x04);
+
+ if (xgifb_info->display2 == XGIFB_DISP_TV &&
+ xgifb_info->hasVB == HASVB_301) {
+
+ reg = xgifb_reg_get(XGIPART4, 0x01);
+
+ if (reg < 0xB0) { /* Set filter for XGI301 */
+ int filter_tb;
+
+ switch (xgifb_info->video_width) {
+ case 320:
+ filter_tb = (xgifb_info->TV_type ==
+ TVMODE_NTSC) ? 4 : 12;
+ break;
+ case 640:
+ filter_tb = (xgifb_info->TV_type ==
+ TVMODE_NTSC) ? 5 : 13;
+ break;
+ case 720:
+ filter_tb = (xgifb_info->TV_type ==
+ TVMODE_NTSC) ? 6 : 14;
+ break;
+ case 800:
+ filter_tb = (xgifb_info->TV_type ==
+ TVMODE_NTSC) ? 7 : 15;
+ break;
+ default:
+ filter_tb = 0;
+ filter = -1;
+ break;
+ }
+ xgifb_reg_or(XGIPART1,
+ SIS_CRT2_WENABLE_315,
+ 0x01);
+
+ if (xgifb_info->TV_type == TVMODE_NTSC) {
+
+ xgifb_reg_and(XGIPART2, 0x3a, 0x1f);
+
+ if (xgifb_info->TV_plug == TVPLUG_SVIDEO) {
+
+ xgifb_reg_and(XGIPART2, 0x30, 0xdf);
+
+ } else if (xgifb_info->TV_plug
+ == TVPLUG_COMPOSITE) {
+
+ xgifb_reg_or(XGIPART2, 0x30, 0x20);
+
+ switch (xgifb_info->video_width) {
+ case 640:
+ xgifb_reg_set(XGIPART2,
+ 0x35,
+ 0xEB);
+ xgifb_reg_set(XGIPART2,
+ 0x36,
+ 0x04);
+ xgifb_reg_set(XGIPART2,
+ 0x37,
+ 0x25);
+ xgifb_reg_set(XGIPART2,
+ 0x38,
+ 0x18);
+ break;
+ case 720:
+ xgifb_reg_set(XGIPART2,
+ 0x35,
+ 0xEE);
+ xgifb_reg_set(XGIPART2,
+ 0x36,
+ 0x0C);
+ xgifb_reg_set(XGIPART2,
+ 0x37,
+ 0x22);
+ xgifb_reg_set(XGIPART2,
+ 0x38,
+ 0x08);
+ break;
+ case 800:
+ xgifb_reg_set(XGIPART2,
+ 0x35,
+ 0xEB);
+ xgifb_reg_set(XGIPART2,
+ 0x36,
+ 0x15);
+ xgifb_reg_set(XGIPART2,
+ 0x37,
+ 0x25);
+ xgifb_reg_set(XGIPART2,
+ 0x38,
+ 0xF6);
+ break;
+ }
+ }
+
+ } else if (xgifb_info->TV_type == TVMODE_PAL) {
+
+ xgifb_reg_and(XGIPART2, 0x3A, 0x1F);
+
+ if (xgifb_info->TV_plug == TVPLUG_SVIDEO) {
+
+ xgifb_reg_and(XGIPART2, 0x30, 0xDF);
+
+ } else if (xgifb_info->TV_plug
+ == TVPLUG_COMPOSITE) {
+
+ xgifb_reg_or(XGIPART2, 0x30, 0x20);
+
+ switch (xgifb_info->video_width) {
+ case 640:
+ xgifb_reg_set(XGIPART2,
+ 0x35,
+ 0xF1);
+ xgifb_reg_set(XGIPART2,
+ 0x36,
+ 0xF7);
+ xgifb_reg_set(XGIPART2,
+ 0x37,
+ 0x1F);
+ xgifb_reg_set(XGIPART2,
+ 0x38,
+ 0x32);
+ break;
+ case 720:
+ xgifb_reg_set(XGIPART2,
+ 0x35,
+ 0xF3);
+ xgifb_reg_set(XGIPART2,
+ 0x36,
+ 0x00);
+ xgifb_reg_set(XGIPART2,
+ 0x37,
+ 0x1D);
+ xgifb_reg_set(XGIPART2,
+ 0x38,
+ 0x20);
+ break;
+ case 800:
+ xgifb_reg_set(XGIPART2,
+ 0x35,
+ 0xFC);
+ xgifb_reg_set(XGIPART2,
+ 0x36,
+ 0xFB);
+ xgifb_reg_set(XGIPART2,
+ 0x37,
+ 0x14);
+ xgifb_reg_set(XGIPART2,
+ 0x38,
+ 0x2A);
+ break;
+ }
+ }
+ }
+
+ if ((filter >= 0) && (filter <= 7)) {
+ pr_debug("FilterTable[%d]-%d: %*ph\n",
+ filter_tb, filter,
+ 4, XGI_TV_filter[filter_tb].
+ filter[filter]);
+ xgifb_reg_set(
+ XGIPART2,
+ 0x35,
+ (XGI_TV_filter[filter_tb].
+ filter[filter][0]));
+ xgifb_reg_set(
+ XGIPART2,
+ 0x36,
+ (XGI_TV_filter[filter_tb].
+ filter[filter][1]));
+ xgifb_reg_set(
+ XGIPART2,
+ 0x37,
+ (XGI_TV_filter[filter_tb].
+ filter[filter][2]));
+ xgifb_reg_set(
+ XGIPART2,
+ 0x38,
+ (XGI_TV_filter[filter_tb].
+ filter[filter][3]));
+ }
+ }
+ }
+}
+
+static int XGIfb_do_set_var(struct fb_var_screeninfo *var, int isactive,
+ struct fb_info *info)
+{
+ struct xgifb_video_info *xgifb_info = info->par;
+ struct xgi_hw_device_info *hw_info = &xgifb_info->hw_info;
+ unsigned int htotal = var->left_margin + var->xres + var->right_margin
+ + var->hsync_len;
+ unsigned int vtotal = var->upper_margin + var->yres + var->lower_margin
+ + var->vsync_len;
+#if defined(__BIG_ENDIAN)
+ u8 cr_data;
+#endif
+ unsigned int drate = 0, hrate = 0;
+ int found_mode = 0;
+ int old_mode;
+
+ info->var.xres_virtual = var->xres_virtual;
+ info->var.yres_virtual = var->yres_virtual;
+ info->var.bits_per_pixel = var->bits_per_pixel;
+
+ if ((var->vmode & FB_VMODE_MASK) == FB_VMODE_NONINTERLACED)
+ vtotal <<= 1;
+ else if ((var->vmode & FB_VMODE_MASK) == FB_VMODE_DOUBLE)
+ vtotal <<= 2;
+
+ if (!htotal || !vtotal) {
+ pr_debug("Invalid 'var' information\n");
+ return -EINVAL;
+ } pr_debug("var->pixclock=%d, htotal=%d, vtotal=%d\n",
+ var->pixclock, htotal, vtotal);
+
+ if (var->pixclock) {
+ drate = 1000000000 / var->pixclock;
+ hrate = (drate * 1000) / htotal;
+ xgifb_info->refresh_rate = (unsigned int) (hrate * 2
+ / vtotal);
+ } else {
+ xgifb_info->refresh_rate = 60;
+ }
+
+ pr_debug("Change mode to %dx%dx%d-%dHz\n",
+ var->xres,
+ var->yres,
+ var->bits_per_pixel,
+ xgifb_info->refresh_rate);
+
+ old_mode = xgifb_info->mode_idx;
+ xgifb_info->mode_idx = 0;
+
+ while ((XGIbios_mode[xgifb_info->mode_idx].mode_no != 0) &&
+ (XGIbios_mode[xgifb_info->mode_idx].xres <= var->xres)) {
+ if ((XGIbios_mode[xgifb_info->mode_idx].xres == var->xres) &&
+ (XGIbios_mode[xgifb_info->mode_idx].yres == var->yres) &&
+ (XGIbios_mode[xgifb_info->mode_idx].bpp
+ == var->bits_per_pixel)) {
+ found_mode = 1;
+ break;
+ }
+ xgifb_info->mode_idx++;
+ }
+
+ if (found_mode)
+ xgifb_info->mode_idx = XGIfb_validate_mode(xgifb_info,
+ xgifb_info->mode_idx);
+ else
+ xgifb_info->mode_idx = -1;
+
+ if (xgifb_info->mode_idx < 0) {
+ pr_err("Mode %dx%dx%d not supported\n",
+ var->xres, var->yres, var->bits_per_pixel);
+ xgifb_info->mode_idx = old_mode;
+ return -EINVAL;
+ }
+
+ if (XGIfb_search_refresh_rate(xgifb_info,
+ xgifb_info->refresh_rate) == 0) {
+ xgifb_info->rate_idx = 1;
+ xgifb_info->refresh_rate = 60;
+ }
+
+ if (isactive) {
+
+ XGIfb_pre_setmode(xgifb_info);
+ if (XGISetModeNew(xgifb_info, hw_info,
+ XGIbios_mode[xgifb_info->mode_idx].mode_no)
+ == 0) {
+ pr_err("Setting mode[0x%x] failed\n",
+ XGIbios_mode[xgifb_info->mode_idx].mode_no);
+ return -EINVAL;
+ }
+ info->fix.line_length = (info->var.xres_virtual
+ * info->var.bits_per_pixel) >> 6;
+
+ xgifb_reg_set(XGISR, IND_SIS_PASSWORD, SIS_PASSWORD);
+
+ xgifb_reg_set(XGICR, 0x13, (info->fix.line_length & 0x00ff));
+ xgifb_reg_set(XGISR,
+ 0x0E,
+ (info->fix.line_length & 0xff00) >> 8);
+
+ XGIfb_post_setmode(xgifb_info);
+
+ pr_debug("Set new mode: %dx%dx%d-%d\n",
+ XGIbios_mode[xgifb_info->mode_idx].xres,
+ XGIbios_mode[xgifb_info->mode_idx].yres,
+ XGIbios_mode[xgifb_info->mode_idx].bpp,
+ xgifb_info->refresh_rate);
+
+ xgifb_info->video_bpp = XGIbios_mode[xgifb_info->mode_idx].bpp;
+ xgifb_info->video_vwidth = info->var.xres_virtual;
+ xgifb_info->video_width =
+ XGIbios_mode[xgifb_info->mode_idx].xres;
+ xgifb_info->video_vheight = info->var.yres_virtual;
+ xgifb_info->video_height =
+ XGIbios_mode[xgifb_info->mode_idx].yres;
+ xgifb_info->org_x = xgifb_info->org_y = 0;
+ xgifb_info->video_linelength = info->var.xres_virtual
+ * (xgifb_info->video_bpp >> 3);
+ switch (xgifb_info->video_bpp) {
+ case 8:
+ xgifb_info->DstColor = 0x0000;
+ xgifb_info->XGI310_AccelDepth = 0x00000000;
+ xgifb_info->video_cmap_len = 256;
+#if defined(__BIG_ENDIAN)
+ cr_data = xgifb_reg_get(XGICR, 0x4D);
+ xgifb_reg_set(XGICR, 0x4D, (cr_data & 0xE0));
+#endif
+ break;
+ case 16:
+ xgifb_info->DstColor = 0x8000;
+ xgifb_info->XGI310_AccelDepth = 0x00010000;
+#if defined(__BIG_ENDIAN)
+ cr_data = xgifb_reg_get(XGICR, 0x4D);
+ xgifb_reg_set(XGICR, 0x4D, ((cr_data & 0xE0) | 0x0B));
+#endif
+ xgifb_info->video_cmap_len = 16;
+ break;
+ case 32:
+ xgifb_info->DstColor = 0xC000;
+ xgifb_info->XGI310_AccelDepth = 0x00020000;
+ xgifb_info->video_cmap_len = 16;
+#if defined(__BIG_ENDIAN)
+ cr_data = xgifb_reg_get(XGICR, 0x4D);
+ xgifb_reg_set(XGICR, 0x4D, ((cr_data & 0xE0) | 0x15));
+#endif
+ break;
+ default:
+ xgifb_info->video_cmap_len = 16;
+ pr_err("Unsupported depth %d\n",
+ xgifb_info->video_bpp);
+ break;
+ }
+ }
+ XGIfb_bpp_to_var(xgifb_info, var); /*update ARGB info*/
+
+ dumpVGAReg();
+ return 0;
+}
+
+static int XGIfb_pan_var(struct fb_var_screeninfo *var, struct fb_info *info)
+{
+ struct xgifb_video_info *xgifb_info = info->par;
+ unsigned int base;
+
+ base = var->yoffset * info->var.xres_virtual + var->xoffset;
+
+ /* calculate base bpp dep. */
+ switch (info->var.bits_per_pixel) {
+ case 16:
+ base >>= 1;
+ break;
+ case 32:
+ break;
+ case 8:
+ default:
+ base >>= 2;
+ break;
+ }
+
+ xgifb_reg_set(XGISR, IND_SIS_PASSWORD, SIS_PASSWORD);
+
+ xgifb_reg_set(XGICR, 0x0D, base & 0xFF);
+ xgifb_reg_set(XGICR, 0x0C, (base >> 8) & 0xFF);
+ xgifb_reg_set(XGISR, 0x0D, (base >> 16) & 0xFF);
+ xgifb_reg_set(XGISR, 0x37, (base >> 24) & 0x03);
+ xgifb_reg_and_or(XGISR, 0x37, 0xDF, (base >> 21) & 0x04);
+
+ if (xgifb_info->display2 != XGIFB_DISP_NONE) {
+ xgifb_reg_or(XGIPART1, SIS_CRT2_WENABLE_315, 0x01);
+ xgifb_reg_set(XGIPART1, 0x06, (base & 0xFF));
+ xgifb_reg_set(XGIPART1, 0x05, ((base >> 8) & 0xFF));
+ xgifb_reg_set(XGIPART1, 0x04, ((base >> 16) & 0xFF));
+ xgifb_reg_and_or(XGIPART1,
+ 0x02,
+ 0x7F,
+ ((base >> 24) & 0x01) << 7);
+ }
+ return 0;
+}
+
+static int XGIfb_open(struct fb_info *info, int user)
+{
+ return 0;
+}
+
+static int XGIfb_release(struct fb_info *info, int user)
+{
+ return 0;
+}
+
+/* similar to sisfb_get_cmap_len */
+static int XGIfb_get_cmap_len(const struct fb_var_screeninfo *var)
+{
+ return (var->bits_per_pixel == 8) ? 256 : 16;
+}
+
+static int XGIfb_setcolreg(unsigned regno, unsigned red, unsigned green,
+ unsigned blue, unsigned transp, struct fb_info *info)
+{
+ struct xgifb_video_info *xgifb_info = info->par;
+
+ if (regno >= XGIfb_get_cmap_len(&info->var))
+ return 1;
+
+ switch (info->var.bits_per_pixel) {
+ case 8:
+ outb(regno, XGIDACA);
+ outb((red >> 10), XGIDACD);
+ outb((green >> 10), XGIDACD);
+ outb((blue >> 10), XGIDACD);
+ if (xgifb_info->display2 != XGIFB_DISP_NONE) {
+ outb(regno, XGIDAC2A);
+ outb((red >> 8), XGIDAC2D);
+ outb((green >> 8), XGIDAC2D);
+ outb((blue >> 8), XGIDAC2D);
+ }
+ break;
+ case 16:
+ ((u32 *) (info->pseudo_palette))[regno] = ((red & 0xf800))
+ | ((green & 0xfc00) >> 5) | ((blue & 0xf800)
+ >> 11);
+ break;
+ case 32:
+ red >>= 8;
+ green >>= 8;
+ blue >>= 8;
+ ((u32 *) (info->pseudo_palette))[regno] = (red << 16) | (green
+ << 8) | (blue);
+ break;
+ }
+ return 0;
+}
+
+/* ----------- FBDev related routines for all series ---------- */
+
+static int XGIfb_get_fix(struct fb_fix_screeninfo *fix, int con,
+ struct fb_info *info)
+{
+ struct xgifb_video_info *xgifb_info = info->par;
+
+ memset(fix, 0, sizeof(struct fb_fix_screeninfo));
+
+ strncpy(fix->id, "XGI", sizeof(fix->id) - 1);
+
+ /* if register_framebuffer has been called, we must lock */
+ if (atomic_read(&info->count))
+ mutex_lock(&info->mm_lock);
+
+ fix->smem_start = xgifb_info->video_base;
+ fix->smem_len = xgifb_info->video_size;
+
+ /* if register_framebuffer has been called, we can unlock */
+ if (atomic_read(&info->count))
+ mutex_unlock(&info->mm_lock);
+
+ fix->type = FB_TYPE_PACKED_PIXELS;
+ fix->type_aux = 0;
+ if (xgifb_info->video_bpp == 8)
+ fix->visual = FB_VISUAL_PSEUDOCOLOR;
+ else
+ fix->visual = FB_VISUAL_DIRECTCOLOR;
+ fix->xpanstep = 0;
+ if (XGIfb_ypan)
+ fix->ypanstep = 1;
+ fix->ywrapstep = 0;
+ fix->line_length = xgifb_info->video_linelength;
+ fix->mmio_start = xgifb_info->mmio_base;
+ fix->mmio_len = xgifb_info->mmio_size;
+ fix->accel = FB_ACCEL_SIS_XABRE;
+
+ return 0;
+}
+
+static int XGIfb_set_par(struct fb_info *info)
+{
+ int err;
+
+ err = XGIfb_do_set_var(&info->var, 1, info);
+ if (err)
+ return err;
+ XGIfb_get_fix(&info->fix, -1, info);
+ return 0;
+}
+
+static int XGIfb_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
+{
+ struct xgifb_video_info *xgifb_info = info->par;
+ unsigned int htotal = var->left_margin + var->xres + var->right_margin
+ + var->hsync_len;
+ unsigned int vtotal = 0;
+ unsigned int drate = 0, hrate = 0;
+ int found_mode = 0;
+ int refresh_rate, search_idx;
+
+ if ((var->vmode & FB_VMODE_MASK) == FB_VMODE_NONINTERLACED) {
+ vtotal = var->upper_margin + var->yres + var->lower_margin
+ + var->vsync_len;
+ vtotal <<= 1;
+ } else if ((var->vmode & FB_VMODE_MASK) == FB_VMODE_DOUBLE) {
+ vtotal = var->upper_margin + var->yres + var->lower_margin
+ + var->vsync_len;
+ vtotal <<= 2;
+ } else if ((var->vmode & FB_VMODE_MASK) == FB_VMODE_INTERLACED) {
+ vtotal = var->upper_margin + (var->yres / 2)
+ + var->lower_margin + var->vsync_len;
+ } else
+ vtotal = var->upper_margin + var->yres + var->lower_margin
+ + var->vsync_len;
+
+ if (!(htotal) || !(vtotal)) {
+ pr_debug("No valid timing data\n");
+ return -EINVAL;
+ }
+
+ if (var->pixclock && htotal && vtotal) {
+ drate = 1000000000 / var->pixclock;
+ hrate = (drate * 1000) / htotal;
+ xgifb_info->refresh_rate =
+ (unsigned int) (hrate * 2 / vtotal);
+ pr_debug(
+ "%s: pixclock = %d ,htotal=%d, vtotal=%d\n"
+ "%s: drate=%d, hrate=%d, refresh_rate=%d\n",
+ __func__, var->pixclock, htotal, vtotal,
+ __func__, drate, hrate, xgifb_info->refresh_rate);
+ } else {
+ xgifb_info->refresh_rate = 60;
+ }
+
+ /* Calculation wrong for 1024x600 - force it to 60Hz */
+ if ((var->xres == 1024) && (var->yres == 600))
+ refresh_rate = 60;
+
+ search_idx = 0;
+ while ((XGIbios_mode[search_idx].mode_no != 0) &&
+ (XGIbios_mode[search_idx].xres <= var->xres)) {
+ if ((XGIbios_mode[search_idx].xres == var->xres) &&
+ (XGIbios_mode[search_idx].yres == var->yres) &&
+ (XGIbios_mode[search_idx].bpp == var->bits_per_pixel)) {
+ if (XGIfb_validate_mode(xgifb_info, search_idx) > 0) {
+ found_mode = 1;
+ break;
+ }
+ }
+ search_idx++;
+ }
+
+ if (!found_mode) {
+
+ pr_err("%dx%dx%d is no valid mode\n",
+ var->xres, var->yres, var->bits_per_pixel);
+ search_idx = 0;
+ while (XGIbios_mode[search_idx].mode_no != 0) {
+ if ((var->xres <= XGIbios_mode[search_idx].xres) &&
+ (var->yres <= XGIbios_mode[search_idx].yres) &&
+ (var->bits_per_pixel ==
+ XGIbios_mode[search_idx].bpp)) {
+ if (XGIfb_validate_mode(xgifb_info,
+ search_idx) > 0) {
+ found_mode = 1;
+ break;
+ }
+ }
+ search_idx++;
+ }
+ if (found_mode) {
+ var->xres = XGIbios_mode[search_idx].xres;
+ var->yres = XGIbios_mode[search_idx].yres;
+ pr_debug("Adapted to mode %dx%dx%d\n",
+ var->xres, var->yres, var->bits_per_pixel);
+
+ } else {
+ pr_err("Failed to find similar mode to %dx%dx%d\n",
+ var->xres, var->yres, var->bits_per_pixel);
+ return -EINVAL;
+ }
+ }
+
+ /* Adapt RGB settings */
+ XGIfb_bpp_to_var(xgifb_info, var);
+
+ if (!XGIfb_ypan) {
+ if (var->xres != var->xres_virtual)
+ var->xres_virtual = var->xres;
+ if (var->yres != var->yres_virtual)
+ var->yres_virtual = var->yres;
+ }
+
+ /* Truncate offsets to maximum if too high */
+ if (var->xoffset > var->xres_virtual - var->xres)
+ var->xoffset = var->xres_virtual - var->xres - 1;
+
+ if (var->yoffset > var->yres_virtual - var->yres)
+ var->yoffset = var->yres_virtual - var->yres - 1;
+
+ /* Set everything else to 0 */
+ var->red.msb_right =
+ var->green.msb_right =
+ var->blue.msb_right =
+ var->transp.offset = var->transp.length = var->transp.msb_right = 0;
+
+ return 0;
+}
+
+static int XGIfb_pan_display(struct fb_var_screeninfo *var,
+ struct fb_info *info)
+{
+ int err;
+
+ if (var->xoffset > (info->var.xres_virtual - info->var.xres))
+ return -EINVAL;
+ if (var->yoffset > (info->var.yres_virtual - info->var.yres))
+ return -EINVAL;
+
+ if (var->vmode & FB_VMODE_YWRAP) {
+ if (var->yoffset >= info->var.yres_virtual || var->xoffset)
+ return -EINVAL;
+ } else if (var->xoffset + info->var.xres > info->var.xres_virtual
+ || var->yoffset + info->var.yres
+ > info->var.yres_virtual) {
+ return -EINVAL;
+ }
+ err = XGIfb_pan_var(var, info);
+ if (err < 0)
+ return err;
+
+ info->var.xoffset = var->xoffset;
+ info->var.yoffset = var->yoffset;
+ if (var->vmode & FB_VMODE_YWRAP)
+ info->var.vmode |= FB_VMODE_YWRAP;
+ else
+ info->var.vmode &= ~FB_VMODE_YWRAP;
+
+ return 0;
+}
+
+static int XGIfb_blank(int blank, struct fb_info *info)
+{
+ struct xgifb_video_info *xgifb_info = info->par;
+ u8 reg;
+
+ reg = xgifb_reg_get(XGICR, 0x17);
+
+ if (blank > 0)
+ reg &= 0x7f;
+ else
+ reg |= 0x80;
+
+ xgifb_reg_set(XGICR, 0x17, reg);
+ xgifb_reg_set(XGISR, 0x00, 0x01); /* Synchronous Reset */
+ xgifb_reg_set(XGISR, 0x00, 0x03); /* End Reset */
+ return 0;
+}
+
+static struct fb_ops XGIfb_ops = {
+ .owner = THIS_MODULE,
+ .fb_open = XGIfb_open,
+ .fb_release = XGIfb_release,
+ .fb_check_var = XGIfb_check_var,
+ .fb_set_par = XGIfb_set_par,
+ .fb_setcolreg = XGIfb_setcolreg,
+ .fb_pan_display = XGIfb_pan_display,
+ .fb_blank = XGIfb_blank,
+ .fb_fillrect = cfb_fillrect,
+ .fb_copyarea = cfb_copyarea,
+ .fb_imageblit = cfb_imageblit,
+};
+
+/* ---------------- Chip generation dependent routines ---------------- */
+
+/* for XGI 315/550/650/740/330 */
+
+static int XGIfb_get_dram_size(struct xgifb_video_info *xgifb_info)
+{
+
+ u8 ChannelNum, tmp;
+ u8 reg = 0;
+
+ /* xorg driver sets 32MB * 1 channel */
+ if (xgifb_info->chip == XG27)
+ xgifb_reg_set(XGISR, IND_SIS_DRAM_SIZE, 0x51);
+
+ reg = xgifb_reg_get(XGISR, IND_SIS_DRAM_SIZE);
+ if (!reg)
+ return -1;
+
+ switch ((reg & XGI_DRAM_SIZE_MASK) >> 4) {
+ case XGI_DRAM_SIZE_1MB:
+ xgifb_info->video_size = 0x100000;
+ break;
+ case XGI_DRAM_SIZE_2MB:
+ xgifb_info->video_size = 0x200000;
+ break;
+ case XGI_DRAM_SIZE_4MB:
+ xgifb_info->video_size = 0x400000;
+ break;
+ case XGI_DRAM_SIZE_8MB:
+ xgifb_info->video_size = 0x800000;
+ break;
+ case XGI_DRAM_SIZE_16MB:
+ xgifb_info->video_size = 0x1000000;
+ break;
+ case XGI_DRAM_SIZE_32MB:
+ xgifb_info->video_size = 0x2000000;
+ break;
+ case XGI_DRAM_SIZE_64MB:
+ xgifb_info->video_size = 0x4000000;
+ break;
+ case XGI_DRAM_SIZE_128MB:
+ xgifb_info->video_size = 0x8000000;
+ break;
+ case XGI_DRAM_SIZE_256MB:
+ xgifb_info->video_size = 0x10000000;
+ break;
+ default:
+ return -1;
+ }
+
+ tmp = (reg & 0x0c) >> 2;
+ switch (xgifb_info->chip) {
+ case XG20:
+ case XG21:
+ case XG27:
+ ChannelNum = 1;
+ break;
+
+ case XG42:
+ if (reg & 0x04)
+ ChannelNum = 2;
+ else
+ ChannelNum = 1;
+ break;
+
+ case XG40:
+ default:
+ if (tmp == 2)
+ ChannelNum = 2;
+ else if (tmp == 3)
+ ChannelNum = 3;
+ else
+ ChannelNum = 1;
+ break;
+ }
+
+ xgifb_info->video_size = xgifb_info->video_size * ChannelNum;
+
+ pr_info("SR14=%x DramSzie %x ChannelNum %x\n",
+ reg,
+ xgifb_info->video_size, ChannelNum);
+ return 0;
+
+}
+
+static void XGIfb_detect_VB(struct xgifb_video_info *xgifb_info)
+{
+ u8 cr32, temp = 0;
+
+ xgifb_info->TV_plug = xgifb_info->TV_type = 0;
+
+ cr32 = xgifb_reg_get(XGICR, IND_XGI_SCRATCH_REG_CR32);
+
+ if ((cr32 & SIS_CRT1) && !XGIfb_crt1off)
+ XGIfb_crt1off = 0;
+ else {
+ if (cr32 & 0x5F)
+ XGIfb_crt1off = 1;
+ else
+ XGIfb_crt1off = 0;
+ }
+
+ if (!xgifb_info->display2_force) {
+ if (cr32 & SIS_VB_TV)
+ xgifb_info->display2 = XGIFB_DISP_TV;
+ else if (cr32 & SIS_VB_LCD)
+ xgifb_info->display2 = XGIFB_DISP_LCD;
+ else if (cr32 & SIS_VB_CRT2)
+ xgifb_info->display2 = XGIFB_DISP_CRT;
+ else
+ xgifb_info->display2 = XGIFB_DISP_NONE;
+ }
+
+ if (XGIfb_tvplug != -1)
+ /* Override with option */
+ xgifb_info->TV_plug = XGIfb_tvplug;
+ else if (cr32 & SIS_VB_HIVISION) {
+ xgifb_info->TV_type = TVMODE_HIVISION;
+ xgifb_info->TV_plug = TVPLUG_SVIDEO;
+ } else if (cr32 & SIS_VB_SVIDEO)
+ xgifb_info->TV_plug = TVPLUG_SVIDEO;
+ else if (cr32 & SIS_VB_COMPOSITE)
+ xgifb_info->TV_plug = TVPLUG_COMPOSITE;
+ else if (cr32 & SIS_VB_SCART)
+ xgifb_info->TV_plug = TVPLUG_SCART;
+
+ if (xgifb_info->TV_type == 0) {
+ temp = xgifb_reg_get(XGICR, 0x38);
+ if (temp & 0x10)
+ xgifb_info->TV_type = TVMODE_PAL;
+ else
+ xgifb_info->TV_type = TVMODE_NTSC;
+ }
+
+ /* Copy forceCRT1 option to CRT1off if option is given */
+ if (XGIfb_forcecrt1 != -1) {
+ if (XGIfb_forcecrt1)
+ XGIfb_crt1off = 0;
+ else
+ XGIfb_crt1off = 1;
+ }
+}
+
+static int XGIfb_has_VB(struct xgifb_video_info *xgifb_info)
+{
+ u8 vb_chipid;
+
+ vb_chipid = xgifb_reg_get(XGIPART4, 0x00);
+ switch (vb_chipid) {
+ case 0x01:
+ xgifb_info->hasVB = HASVB_301;
+ break;
+ case 0x02:
+ xgifb_info->hasVB = HASVB_302;
+ break;
+ default:
+ xgifb_info->hasVB = HASVB_NONE;
+ return 0;
+ }
+ return 1;
+}
+
+static void XGIfb_get_VB_type(struct xgifb_video_info *xgifb_info)
+{
+ u8 reg;
+
+ if (!XGIfb_has_VB(xgifb_info)) {
+ reg = xgifb_reg_get(XGICR, IND_XGI_SCRATCH_REG_CR37);
+ switch ((reg & SIS_EXTERNAL_CHIP_MASK) >> 1) {
+ case SIS_EXTERNAL_CHIP_LVDS:
+ xgifb_info->hasVB = HASVB_LVDS;
+ break;
+ case SIS_EXTERNAL_CHIP_LVDS_CHRONTEL:
+ xgifb_info->hasVB = HASVB_LVDS_CHRONTEL;
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+static int __init xgifb_optval(char *fullopt, int validx)
+{
+ unsigned long lres;
+
+ if (kstrtoul(fullopt + validx, 0, &lres) < 0 || lres > INT_MAX) {
+ pr_err("Invalid value for option: %s\n", fullopt);
+ return 0;
+ }
+ return lres;
+}
+
+static int __init XGIfb_setup(char *options)
+{
+ char *this_opt;
+
+ if (!options || !*options)
+ return 0;
+
+ pr_info("Options: %s\n", options);
+
+ while ((this_opt = strsep(&options, ",")) != NULL) {
+
+ if (!*this_opt)
+ continue;
+
+ if (!strncmp(this_opt, "mode:", 5)) {
+ mode = this_opt + 5;
+ } else if (!strncmp(this_opt, "vesa:", 5)) {
+ vesa = xgifb_optval(this_opt, 5);
+ } else if (!strncmp(this_opt, "vrate:", 6)) {
+ refresh_rate = xgifb_optval(this_opt, 6);
+ } else if (!strncmp(this_opt, "rate:", 5)) {
+ refresh_rate = xgifb_optval(this_opt, 5);
+ } else if (!strncmp(this_opt, "crt1off", 7)) {
+ XGIfb_crt1off = 1;
+ } else if (!strncmp(this_opt, "filter:", 7)) {
+ filter = xgifb_optval(this_opt, 7);
+ } else if (!strncmp(this_opt, "forcecrt2type:", 14)) {
+ XGIfb_search_crt2type(this_opt + 14);
+ } else if (!strncmp(this_opt, "forcecrt1:", 10)) {
+ XGIfb_forcecrt1 = xgifb_optval(this_opt, 10);
+ } else if (!strncmp(this_opt, "tvmode:", 7)) {
+ XGIfb_search_tvstd(this_opt + 7);
+ } else if (!strncmp(this_opt, "tvstandard:", 11)) {
+ XGIfb_search_tvstd(this_opt + 7);
+ } else if (!strncmp(this_opt, "dstn", 4)) {
+ enable_dstn = 1;
+ /* DSTN overrules forcecrt2type */
+ XGIfb_crt2type = XGIFB_DISP_LCD;
+ } else if (!strncmp(this_opt, "noypan", 6)) {
+ XGIfb_ypan = 0;
+ } else {
+ mode = this_opt;
+ }
+ }
+ return 0;
+}
+
+static int xgifb_probe(struct pci_dev *pdev,
+ const struct pci_device_id *ent)
+{
+ u8 reg, reg1;
+ u8 CR48, CR38;
+ int ret;
+ struct fb_info *fb_info;
+ struct xgifb_video_info *xgifb_info;
+ struct xgi_hw_device_info *hw_info;
+ unsigned long video_size_max;
+
+ fb_info = framebuffer_alloc(sizeof(*xgifb_info), &pdev->dev);
+ if (!fb_info)
+ return -ENOMEM;
+
+ xgifb_info = fb_info->par;
+ hw_info = &xgifb_info->hw_info;
+ xgifb_info->fb_info = fb_info;
+ xgifb_info->chip_id = pdev->device;
+ pci_read_config_byte(pdev,
+ PCI_REVISION_ID,
+ &xgifb_info->revision_id);
+ hw_info->jChipRevision = xgifb_info->revision_id;
+
+ xgifb_info->pcibus = pdev->bus->number;
+ xgifb_info->pcislot = PCI_SLOT(pdev->devfn);
+ xgifb_info->pcifunc = PCI_FUNC(pdev->devfn);
+ xgifb_info->subsysvendor = pdev->subsystem_vendor;
+ xgifb_info->subsysdevice = pdev->subsystem_device;
+
+ video_size_max = pci_resource_len(pdev, 0);
+ xgifb_info->video_base = pci_resource_start(pdev, 0);
+ xgifb_info->mmio_base = pci_resource_start(pdev, 1);
+ xgifb_info->mmio_size = pci_resource_len(pdev, 1);
+ xgifb_info->vga_base = pci_resource_start(pdev, 2) + 0x30;
+ dev_info(&pdev->dev, "Relocate IO address: %Lx [%08lx]\n",
+ (u64) pci_resource_start(pdev, 2),
+ xgifb_info->vga_base);
+
+ if (pci_enable_device(pdev)) {
+ ret = -EIO;
+ goto error;
+ }
+
+ if (XGIfb_crt2type != -1) {
+ xgifb_info->display2 = XGIfb_crt2type;
+ xgifb_info->display2_force = true;
+ }
+
+ XGIRegInit(&xgifb_info->dev_info, xgifb_info->vga_base);
+
+ xgifb_reg_set(XGISR, IND_SIS_PASSWORD, SIS_PASSWORD);
+ reg1 = xgifb_reg_get(XGISR, IND_SIS_PASSWORD);
+
+ if (reg1 != 0xa1) { /*I/O error */
+ dev_err(&pdev->dev, "I/O error\n");
+ ret = -EIO;
+ goto error_disable;
+ }
+
+ switch (xgifb_info->chip_id) {
+ case PCI_DEVICE_ID_XGI_20:
+ xgifb_reg_or(XGICR, Index_CR_GPIO_Reg3, GPIOG_EN);
+ CR48 = xgifb_reg_get(XGICR, Index_CR_GPIO_Reg1);
+ if (CR48&GPIOG_READ)
+ xgifb_info->chip = XG21;
+ else
+ xgifb_info->chip = XG20;
+ break;
+ case PCI_DEVICE_ID_XGI_40:
+ xgifb_info->chip = XG40;
+ break;
+ case PCI_DEVICE_ID_XGI_42:
+ xgifb_info->chip = XG42;
+ break;
+ case PCI_DEVICE_ID_XGI_27:
+ xgifb_info->chip = XG27;
+ break;
+ default:
+ ret = -ENODEV;
+ goto error_disable;
+ }
+
+ dev_info(&pdev->dev, "chipid = %x\n", xgifb_info->chip);
+ hw_info->jChipType = xgifb_info->chip;
+
+ if (XGIfb_get_dram_size(xgifb_info)) {
+ xgifb_info->video_size = min_t(unsigned long, video_size_max,
+ SZ_16M);
+ } else if (xgifb_info->video_size > video_size_max) {
+ xgifb_info->video_size = video_size_max;
+ }
+
+ /* Enable PCI_LINEAR_ADDRESSING and MMIO_ENABLE */
+ xgifb_reg_or(XGISR,
+ IND_SIS_PCI_ADDRESS_SET,
+ (SIS_PCI_ADDR_ENABLE | SIS_MEM_MAP_IO_ENABLE));
+ /* Enable 2D accelerator engine */
+ xgifb_reg_or(XGISR, IND_SIS_MODULE_ENABLE, SIS_ENABLE_2D);
+
+ hw_info->ulVideoMemorySize = xgifb_info->video_size;
+
+ if (!request_mem_region(xgifb_info->video_base,
+ xgifb_info->video_size,
+ "XGIfb FB")) {
+ dev_err(&pdev->dev, "Unable request memory size %x\n",
+ xgifb_info->video_size);
+ dev_err(&pdev->dev,
+ "Fatal error: Unable to reserve frame buffer memory. Is there another framebuffer driver active?\n");
+ ret = -ENODEV;
+ goto error_disable;
+ }
+
+ if (!request_mem_region(xgifb_info->mmio_base,
+ xgifb_info->mmio_size,
+ "XGIfb MMIO")) {
+ dev_err(&pdev->dev,
+ "Fatal error: Unable to reserve MMIO region\n");
+ ret = -ENODEV;
+ goto error_0;
+ }
+
+ xgifb_info->video_vbase = hw_info->pjVideoMemoryAddress =
+ ioremap(xgifb_info->video_base, xgifb_info->video_size);
+ xgifb_info->mmio_vbase = ioremap(xgifb_info->mmio_base,
+ xgifb_info->mmio_size);
+
+ dev_info(&pdev->dev,
+ "Framebuffer at 0x%Lx, mapped to 0x%p, size %dk\n",
+ (u64) xgifb_info->video_base,
+ xgifb_info->video_vbase,
+ xgifb_info->video_size / 1024);
+
+ dev_info(&pdev->dev,
+ "MMIO at 0x%Lx, mapped to 0x%p, size %ldk\n",
+ (u64) xgifb_info->mmio_base, xgifb_info->mmio_vbase,
+ xgifb_info->mmio_size / 1024);
+
+ pci_set_drvdata(pdev, xgifb_info);
+ if (!XGIInitNew(pdev))
+ dev_err(&pdev->dev, "XGIInitNew() failed!\n");
+
+ xgifb_info->mtrr = -1;
+
+ xgifb_info->hasVB = HASVB_NONE;
+ if ((xgifb_info->chip == XG20) ||
+ (xgifb_info->chip == XG27)) {
+ xgifb_info->hasVB = HASVB_NONE;
+ } else if (xgifb_info->chip == XG21) {
+ CR38 = xgifb_reg_get(XGICR, 0x38);
+ if ((CR38&0xE0) == 0xC0)
+ xgifb_info->display2 = XGIFB_DISP_LCD;
+ else if ((CR38&0xE0) == 0x60)
+ xgifb_info->hasVB = HASVB_CHRONTEL;
+ else
+ xgifb_info->hasVB = HASVB_NONE;
+ } else {
+ XGIfb_get_VB_type(xgifb_info);
+ }
+
+ hw_info->ujVBChipID = VB_CHIP_UNKNOWN;
+
+ hw_info->ulExternalChip = 0;
+
+ switch (xgifb_info->hasVB) {
+ case HASVB_301:
+ reg = xgifb_reg_get(XGIPART4, 0x01);
+ if (reg >= 0xE0) {
+ hw_info->ujVBChipID = VB_CHIP_302LV;
+ dev_info(&pdev->dev,
+ "XGI302LV bridge detected (revision 0x%02x)\n",
+ reg);
+ } else if (reg >= 0xD0) {
+ hw_info->ujVBChipID = VB_CHIP_301LV;
+ dev_info(&pdev->dev,
+ "XGI301LV bridge detected (revision 0x%02x)\n",
+ reg);
+ } else {
+ hw_info->ujVBChipID = VB_CHIP_301;
+ dev_info(&pdev->dev, "XGI301 bridge detected\n");
+ }
+ break;
+ case HASVB_302:
+ reg = xgifb_reg_get(XGIPART4, 0x01);
+ if (reg >= 0xE0) {
+ hw_info->ujVBChipID = VB_CHIP_302LV;
+ dev_info(&pdev->dev,
+ "XGI302LV bridge detected (revision 0x%02x)\n",
+ reg);
+ } else if (reg >= 0xD0) {
+ hw_info->ujVBChipID = VB_CHIP_301LV;
+ dev_info(&pdev->dev,
+ "XGI302LV bridge detected (revision 0x%02x)\n",
+ reg);
+ } else if (reg >= 0xB0) {
+ reg1 = xgifb_reg_get(XGIPART4, 0x23);
+
+ hw_info->ujVBChipID = VB_CHIP_302B;
+
+ } else {
+ hw_info->ujVBChipID = VB_CHIP_302;
+ dev_info(&pdev->dev, "XGI302 bridge detected\n");
+ }
+ break;
+ case HASVB_LVDS:
+ hw_info->ulExternalChip = 0x1;
+ dev_info(&pdev->dev, "LVDS transmitter detected\n");
+ break;
+ case HASVB_TRUMPION:
+ hw_info->ulExternalChip = 0x2;
+ dev_info(&pdev->dev, "Trumpion Zurac LVDS scaler detected\n");
+ break;
+ case HASVB_CHRONTEL:
+ hw_info->ulExternalChip = 0x4;
+ dev_info(&pdev->dev, "Chrontel TV encoder detected\n");
+ break;
+ case HASVB_LVDS_CHRONTEL:
+ hw_info->ulExternalChip = 0x5;
+ dev_info(&pdev->dev,
+ "LVDS transmitter and Chrontel TV encoder detected\n");
+ break;
+ default:
+ dev_info(&pdev->dev, "No or unknown bridge type detected\n");
+ break;
+ }
+
+ if (xgifb_info->hasVB != HASVB_NONE)
+ XGIfb_detect_VB(xgifb_info);
+ else if (xgifb_info->chip != XG21)
+ xgifb_info->display2 = XGIFB_DISP_NONE;
+
+ if (xgifb_info->display2 == XGIFB_DISP_LCD) {
+ if (!enable_dstn) {
+ reg = xgifb_reg_get(XGICR, IND_XGI_LCD_PANEL);
+ reg &= 0x0f;
+ hw_info->ulCRT2LCDType = XGI310paneltype[reg];
+ }
+ }
+
+ xgifb_info->mode_idx = -1;
+
+ if (mode)
+ XGIfb_search_mode(xgifb_info, mode);
+ else if (vesa != -1)
+ XGIfb_search_vesamode(xgifb_info, vesa);
+
+ if (xgifb_info->mode_idx >= 0)
+ xgifb_info->mode_idx =
+ XGIfb_validate_mode(xgifb_info, xgifb_info->mode_idx);
+
+ if (xgifb_info->mode_idx < 0) {
+ if (xgifb_info->display2 == XGIFB_DISP_LCD &&
+ xgifb_info->chip == XG21)
+ xgifb_info->mode_idx =
+ XGIfb_GetXG21DefaultLVDSModeIdx(xgifb_info);
+ else
+ xgifb_info->mode_idx = DEFAULT_MODE;
+ }
+
+ if (xgifb_info->mode_idx < 0) {
+ dev_err(&pdev->dev, "No supported video mode found\n");
+ ret = -EINVAL;
+ goto error_1;
+ }
+
+ /* set default refresh rate */
+ xgifb_info->refresh_rate = refresh_rate;
+ if (xgifb_info->refresh_rate == 0)
+ xgifb_info->refresh_rate = 60;
+ if (XGIfb_search_refresh_rate(xgifb_info,
+ xgifb_info->refresh_rate) == 0) {
+ xgifb_info->rate_idx = 1;
+ xgifb_info->refresh_rate = 60;
+ }
+
+ xgifb_info->video_bpp = XGIbios_mode[xgifb_info->mode_idx].bpp;
+ xgifb_info->video_vwidth =
+ xgifb_info->video_width =
+ XGIbios_mode[xgifb_info->mode_idx].xres;
+ xgifb_info->video_vheight =
+ xgifb_info->video_height =
+ XGIbios_mode[xgifb_info->mode_idx].yres;
+ xgifb_info->org_x = xgifb_info->org_y = 0;
+ xgifb_info->video_linelength =
+ xgifb_info->video_width *
+ (xgifb_info->video_bpp >> 3);
+ switch (xgifb_info->video_bpp) {
+ case 8:
+ xgifb_info->DstColor = 0x0000;
+ xgifb_info->XGI310_AccelDepth = 0x00000000;
+ xgifb_info->video_cmap_len = 256;
+ break;
+ case 16:
+ xgifb_info->DstColor = 0x8000;
+ xgifb_info->XGI310_AccelDepth = 0x00010000;
+ xgifb_info->video_cmap_len = 16;
+ break;
+ case 32:
+ xgifb_info->DstColor = 0xC000;
+ xgifb_info->XGI310_AccelDepth = 0x00020000;
+ xgifb_info->video_cmap_len = 16;
+ break;
+ default:
+ xgifb_info->video_cmap_len = 16;
+ pr_info("Unsupported depth %d\n",
+ xgifb_info->video_bpp);
+ break;
+ }
+
+ pr_info("Default mode is %dx%dx%d (%dHz)\n",
+ xgifb_info->video_width,
+ xgifb_info->video_height,
+ xgifb_info->video_bpp,
+ xgifb_info->refresh_rate);
+
+ fb_info->var.red.length = 8;
+ fb_info->var.green.length = 8;
+ fb_info->var.blue.length = 8;
+ fb_info->var.activate = FB_ACTIVATE_NOW;
+ fb_info->var.height = -1;
+ fb_info->var.width = -1;
+ fb_info->var.vmode = FB_VMODE_NONINTERLACED;
+ fb_info->var.xres = xgifb_info->video_width;
+ fb_info->var.xres_virtual = xgifb_info->video_width;
+ fb_info->var.yres = xgifb_info->video_height;
+ fb_info->var.yres_virtual = xgifb_info->video_height;
+ fb_info->var.bits_per_pixel = xgifb_info->video_bpp;
+
+ XGIfb_bpp_to_var(xgifb_info, &fb_info->var);
+
+ fb_info->var.pixclock = (u32) (1000000000 /
+ XGIfb_mode_rate_to_dclock(&xgifb_info->dev_info,
+ hw_info,
+ XGIbios_mode[xgifb_info->mode_idx].mode_no));
+
+ if (XGIfb_mode_rate_to_ddata(&xgifb_info->dev_info, hw_info,
+ XGIbios_mode[xgifb_info->mode_idx].mode_no,
+ &fb_info->var.left_margin,
+ &fb_info->var.right_margin,
+ &fb_info->var.upper_margin,
+ &fb_info->var.lower_margin,
+ &fb_info->var.hsync_len,
+ &fb_info->var.vsync_len,
+ &fb_info->var.sync,
+ &fb_info->var.vmode)) {
+
+ if ((fb_info->var.vmode & FB_VMODE_MASK) ==
+ FB_VMODE_INTERLACED) {
+ fb_info->var.yres <<= 1;
+ fb_info->var.yres_virtual <<= 1;
+ } else if ((fb_info->var.vmode & FB_VMODE_MASK) ==
+ FB_VMODE_DOUBLE) {
+ fb_info->var.pixclock >>= 1;
+ fb_info->var.yres >>= 1;
+ fb_info->var.yres_virtual >>= 1;
+ }
+
+ }
+
+ fb_info->flags = FBINFO_FLAG_DEFAULT;
+ fb_info->screen_base = xgifb_info->video_vbase;
+ fb_info->fbops = &XGIfb_ops;
+ XGIfb_get_fix(&fb_info->fix, -1, fb_info);
+ fb_info->pseudo_palette = xgifb_info->pseudo_palette;
+
+ fb_alloc_cmap(&fb_info->cmap, 256, 0);
+
+#ifdef CONFIG_MTRR
+ xgifb_info->mtrr = mtrr_add(xgifb_info->video_base,
+ xgifb_info->video_size, MTRR_TYPE_WRCOMB, 1);
+ if (xgifb_info->mtrr >= 0)
+ dev_info(&pdev->dev, "Added MTRR\n");
+#endif
+
+ if (register_framebuffer(fb_info) < 0) {
+ ret = -EINVAL;
+ goto error_mtrr;
+ }
+
+ dumpVGAReg();
+
+ return 0;
+
+error_mtrr:
+#ifdef CONFIG_MTRR
+ if (xgifb_info->mtrr >= 0)
+ mtrr_del(xgifb_info->mtrr, xgifb_info->video_base,
+ xgifb_info->video_size);
+#endif /* CONFIG_MTRR */
+error_1:
+ iounmap(xgifb_info->mmio_vbase);
+ iounmap(xgifb_info->video_vbase);
+ release_mem_region(xgifb_info->mmio_base, xgifb_info->mmio_size);
+error_0:
+ release_mem_region(xgifb_info->video_base, xgifb_info->video_size);
+error_disable:
+ pci_disable_device(pdev);
+error:
+ framebuffer_release(fb_info);
+ return ret;
+}
+
+/*****************************************************/
+/* PCI DEVICE HANDLING */
+/*****************************************************/
+
+static void xgifb_remove(struct pci_dev *pdev)
+{
+ struct xgifb_video_info *xgifb_info = pci_get_drvdata(pdev);
+ struct fb_info *fb_info = xgifb_info->fb_info;
+
+ unregister_framebuffer(fb_info);
+#ifdef CONFIG_MTRR
+ if (xgifb_info->mtrr >= 0)
+ mtrr_del(xgifb_info->mtrr, xgifb_info->video_base,
+ xgifb_info->video_size);
+#endif /* CONFIG_MTRR */
+ iounmap(xgifb_info->mmio_vbase);
+ iounmap(xgifb_info->video_vbase);
+ release_mem_region(xgifb_info->mmio_base, xgifb_info->mmio_size);
+ release_mem_region(xgifb_info->video_base, xgifb_info->video_size);
+ pci_disable_device(pdev);
+ framebuffer_release(fb_info);
+}
+
+static struct pci_driver xgifb_driver = {
+ .name = "xgifb",
+ .id_table = xgifb_pci_table,
+ .probe = xgifb_probe,
+ .remove = xgifb_remove
+};
+
+
+
+/*****************************************************/
+/* MODULE */
+/*****************************************************/
+
+module_param(mode, charp, 0);
+MODULE_PARM_DESC(mode,
+ "Selects the desired default display mode in the format XxYxDepth (eg. 1024x768x16).");
+
+module_param(forcecrt2type, charp, 0);
+MODULE_PARM_DESC(forcecrt2type,
+ "Force the second display output type. Possible values are NONE, LCD, TV, VGA, SVIDEO or COMPOSITE.");
+
+module_param(vesa, int, 0);
+MODULE_PARM_DESC(vesa,
+ "Selects the desired default display mode by VESA mode number (eg. 0x117).");
+
+module_param(filter, int, 0);
+MODULE_PARM_DESC(filter,
+ "Selects TV flicker filter type (only for systems with a SiS301 video bridge). Possible values 0-7. Default: [no filter]).");
+
+static int __init xgifb_init(void)
+{
+ char *option = NULL;
+
+ if (forcecrt2type != NULL)
+ XGIfb_search_crt2type(forcecrt2type);
+ if (fb_get_options("xgifb", &option))
+ return -ENODEV;
+ XGIfb_setup(option);
+
+ return pci_register_driver(&xgifb_driver);
+}
+
+static void __exit xgifb_remove_module(void)
+{
+ pci_unregister_driver(&xgifb_driver);
+ pr_debug("Module unloaded\n");
+}
+
+MODULE_DESCRIPTION("Z7 Z9 Z9S Z11 framebuffer device driver");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("XGITECH , Others");
+module_init(xgifb_init);
+module_exit(xgifb_remove_module);
diff --git a/drivers/staging/xgifb/XGIfb.h b/drivers/staging/xgifb/XGIfb.h
new file mode 100644
index 000000000..af5036239
--- /dev/null
+++ b/drivers/staging/xgifb/XGIfb.h
@@ -0,0 +1,108 @@
+#ifndef _LINUX_XGIFB
+#define _LINUX_XGIFB
+#include "vgatypes.h"
+#include "vb_struct.h"
+
+enum xgifb_display_type {
+ XGIFB_DISP_NONE = 0,
+ XGIFB_DISP_CRT,
+ XGIFB_DISP_LCD,
+ XGIFB_DISP_TV,
+};
+
+#define HASVB_NONE 0x00
+#define HASVB_301 0x01
+#define HASVB_LVDS 0x02
+#define HASVB_TRUMPION 0x04
+#define HASVB_LVDS_CHRONTEL 0x10
+#define HASVB_302 0x20
+#define HASVB_CHRONTEL 0x80
+
+enum XGI_CHIP_TYPE {
+ XG40 = 32,
+ XG42,
+ XG20 = 48,
+ XG21,
+ XG27,
+};
+
+enum xgi_tvtype {
+ TVMODE_NTSC = 0,
+ TVMODE_PAL,
+ TVMODE_HIVISION,
+ TVTYPE_PALM,
+ TVTYPE_PALN,
+ TVTYPE_NTSCJ,
+ TVMODE_TOTAL
+};
+
+enum xgi_tv_plug {
+ TVPLUG_UNKNOWN = 0,
+ TVPLUG_COMPOSITE = 1,
+ TVPLUG_SVIDEO = 2,
+ TVPLUG_COMPOSITE_AND_SVIDEO = 3,
+ TVPLUG_SCART = 4,
+ TVPLUG_YPBPR_525i = 5,
+ TVPLUG_YPBPR_525P = 6,
+ TVPLUG_YPBPR_750P = 7,
+ TVPLUG_YPBPR_1080i = 8,
+ TVPLUG_TOTAL
+};
+
+struct xgifb_video_info {
+ struct fb_info *fb_info;
+ struct xgi_hw_device_info hw_info;
+ struct vb_device_info dev_info;
+
+ int mode_idx;
+ int rate_idx;
+
+ u32 pseudo_palette[17];
+
+ int chip_id;
+ unsigned int video_size;
+ phys_addr_t video_base;
+ void __iomem *video_vbase;
+ phys_addr_t mmio_base;
+ unsigned long mmio_size;
+ void __iomem *mmio_vbase;
+ unsigned long vga_base;
+ int mtrr;
+
+ int video_bpp;
+ int video_cmap_len;
+ int video_width;
+ int video_height;
+ int video_vwidth;
+ int video_vheight;
+ int org_x;
+ int org_y;
+ int video_linelength;
+ unsigned int refresh_rate;
+
+ enum xgifb_display_type display2; /* the second display output type */
+ bool display2_force;
+ unsigned char hasVB;
+ unsigned char TV_type;
+ unsigned char TV_plug;
+
+ struct XGI21_LVDSCapStruct lvds_data;
+
+ enum XGI_CHIP_TYPE chip;
+ unsigned char revision_id;
+
+ unsigned short DstColor;
+ unsigned long XGI310_AccelDepth;
+ unsigned long CommandReg;
+
+ unsigned int pcibus;
+ unsigned int pcislot;
+ unsigned int pcifunc;
+
+ unsigned short subsysvendor;
+ unsigned short subsysdevice;
+
+ char reserved[236];
+};
+
+#endif
diff --git a/drivers/staging/xgifb/vb_def.h b/drivers/staging/xgifb/vb_def.h
new file mode 100644
index 000000000..d9524a2e9
--- /dev/null
+++ b/drivers/staging/xgifb/vb_def.h
@@ -0,0 +1,257 @@
+#ifndef _VB_DEF_
+#define _VB_DEF_
+#include "../../video/fbdev/sis/initdef.h"
+
+#define VB_XGI301C 0x0020 /* for 301C */
+
+#define SupportCRT2in301C 0x0100 /* for 301C */
+#define SetCHTVOverScan 0x8000
+
+#define PanelResInfo 0x1F /* CR36 Panel Type/LCDResInfo */
+#define Panel_1024x768x75 0x22
+#define Panel_1280x1024x75 0x23
+
+#define PanelRef60Hz 0x00
+#define PanelRef75Hz 0x20
+
+#define YPbPr525iVCLK 0x03B
+#define YPbPr525iVCLK_2 0x03A
+
+#define XGI_CRT2_PORT_00 (0x00 - 0x030)
+
+#define SupportAllCRT2 0x0078
+#define NoSupportTV 0x0070
+#define NoSupportHiVisionTV 0x0060
+#define NoSupportLCD 0x0058
+
+/* -------------- SetMode Stack/Scratch */
+#define XGI_SetCRT2ToLCDA 0x0100
+#define SetCRT2ToDualEdge 0x8000
+
+#define ReserveTVOption 0x0008
+
+#define SetTVLowResolution 0x0400
+#define TVSimuMode 0x0800
+#define RPLLDIV2XO 0x1000
+#define NTSC1024x768 0x2000
+#define SetTVLockMode 0x4000
+
+#define XGI_LCDVESATiming 0x0001 /* LCD Info/CR37 */
+#define XGI_EnableLVDSDDA 0x0002
+#define EnableScalingLCD 0x0008
+#define SetPWDEnable 0x0004
+#define SetLCDtoNonExpanding 0x0010
+#define SetLCDDualLink 0x0100
+#define SetLCDLowResolution 0x0200
+
+/* LCD Capability shampoo */
+#define DefaultLCDCap 0x80ea
+#define EnableLCD24bpp 0x0004 /* default */
+#define LCDPolarity 0x00c0 /* default: SyncNN */
+#define XGI_LCDDualLink 0x0100
+#define EnableSpectrum 0x0200
+#define PWDEnable 0x0400
+#define EnableVBCLKDRVLOW 0x4000
+#define EnablePLLSPLOW 0x8000
+
+#define AVIDEOSense 0x01 /* CR32 */
+#define SVIDEOSense 0x02
+#define SCARTSense 0x04
+#define LCDSense 0x08
+#define Monitor2Sense 0x10
+#define Monitor1Sense 0x20
+#define HiTVSense 0x40
+
+#define YPbPrSense 0x80 /* NEW SCRATCH */
+
+#define TVSense 0xc7
+
+#define YPbPrMode 0xe0
+#define YPbPrMode525i 0x00
+#define YPbPrMode525p 0x20
+#define YPbPrMode750p 0x40
+#define YPbPrMode1080i 0x60
+
+#define ScalingLCD 0x08
+
+#define SetYPbPr 0x04
+
+/* ---------------------- VUMA Information */
+#define DisplayDeviceFromCMOS 0x10
+
+/* ---------------------- HK Evnet Definition */
+#define XGI_ModeSwitchStatus 0xf0
+#define ActiveCRT1 0x10
+#define ActiveLCD 0x0020
+#define ActiveTV 0x40
+#define ActiveCRT2 0x80
+
+#define ActiveAVideo 0x01
+#define ActiveSVideo 0x02
+#define ActiveSCART 0x04
+#define ActiveHiTV 0x08
+#define ActiveYPbPr 0x10
+
+#define NTSC1024x768HT 1908
+
+#define YPbPrTV525iHT 1716 /* YPbPr */
+#define YPbPrTV525iVT 525
+#define YPbPrTV525pHT 1716
+#define YPbPrTV525pVT 525
+#define YPbPrTV750pHT 1650
+#define YPbPrTV750pVT 750
+
+#define VCLK25_175 0x00
+#define VCLK28_322 0x01
+#define VCLK31_5 0x02
+#define VCLK36 0x03
+#define VCLK43_163 0x05
+#define VCLK44_9 0x06
+#define VCLK49_5 0x07
+#define VCLK50 0x08
+#define VCLK52_406 0x09
+#define VCLK56_25 0x0A
+#define VCLK68_179 0x0D
+#define VCLK72_852 0x0E
+#define VCLK75 0x0F
+#define VCLK78_75 0x11
+#define VCLK79_411 0x12
+#define VCLK83_95 0x13
+#define VCLK86_6 0x15
+#define VCLK94_5 0x16
+#define VCLK113_309 0x1B
+#define VCLK116_406 0x1C
+#define VCLK135_5 0x1E
+#define VCLK139_054 0x1F
+#define VCLK157_5 0x20
+#define VCLK162 0x21
+#define VCLK175 0x22
+#define VCLK189 0x23
+#define VCLK202_5 0x25
+#define VCLK229_5 0x26
+#define VCLK234 0x27
+#define VCLK254_817 0x29
+#define VCLK266_952 0x2B
+#define VCLK269_655 0x2C
+#define VCLK277_015 0x2E
+#define VCLK291_132 0x30
+#define VCLK291_766 0x31
+#define VCLK315_195 0x33
+#define VCLK323_586 0x34
+#define VCLK330_615 0x35
+#define VCLK340_477 0x37
+#define VCLK375_847 0x38
+#define VCLK388_631 0x39
+#define VCLK125_999 0x51
+#define VCLK148_5 0x52
+#define VCLK217_325 0x55
+#define XGI_YPbPr750pVCLK 0x57
+
+#define VCLK39_77 0x40
+#define YPbPr525pVCLK 0x3A
+#define NTSC1024VCLK 0x41
+#define VCLK35_2 0x49 /* ; 800x480 */
+#define VCLK122_61 0x4A
+#define VCLK80_350 0x4B
+#define VCLK107_385 0x4C
+
+#define RES320x200 0x00
+#define RES320x240 0x01
+#define RES400x300 0x02
+#define RES512x384 0x03
+#define RES640x400 0x04
+#define RES640x480x60 0x05
+#define RES640x480x72 0x06
+#define RES640x480x75 0x07
+#define RES640x480x85 0x08
+#define RES640x480x100 0x09
+#define RES640x480x120 0x0A
+#define RES640x480x160 0x0B
+#define RES640x480x200 0x0C
+#define RES800x600x56 0x0D
+#define RES800x600x60 0x0E
+#define RES800x600x72 0x0F
+#define RES800x600x75 0x10
+#define RES800x600x85 0x11
+#define RES800x600x100 0x12
+#define RES800x600x120 0x13
+#define RES800x600x160 0x14
+#define RES1024x768x43 0x15
+#define RES1024x768x60 0x16
+#define RES1024x768x70 0x17
+#define RES1024x768x75 0x18
+#define RES1024x768x85 0x19
+#define RES1024x768x100 0x1A
+#define RES1024x768x120 0x1B
+#define RES1280x1024x43 0x1C
+#define RES1280x1024x60 0x1D
+#define RES1280x1024x75 0x1E
+#define RES1280x1024x85 0x1F
+#define RES1600x1200x60 0x20
+#define RES1600x1200x65 0x21
+#define RES1600x1200x70 0x22
+#define RES1600x1200x75 0x23
+#define RES1600x1200x85 0x24
+#define RES1600x1200x100 0x25
+#define RES1600x1200x120 0x26
+#define RES1920x1440x60 0x27
+#define RES1920x1440x65 0x28
+#define RES1920x1440x70 0x29
+#define RES1920x1440x75 0x2A
+#define RES1920x1440x85 0x2B
+#define RES1920x1440x100 0x2C
+#define RES2048x1536x60 0x2D
+#define RES2048x1536x65 0x2E
+#define RES2048x1536x70 0x2F
+#define RES2048x1536x75 0x30
+#define RES2048x1536x85 0x31
+#define RES800x480x60 0x32
+#define RES800x480x75 0x33
+#define RES800x480x85 0x34
+#define RES1024x576x60 0x35
+#define RES1024x576x75 0x36
+#define RES1024x576x85 0x37
+#define RES1280x720x60 0x38
+#define RES1280x720x75 0x39
+#define RES1280x720x85 0x3A
+#define RES1280x960x60 0x3B
+#define RES720x480x60 0x3C
+#define RES720x576x56 0x3D
+#define RES856x480x79I 0x3E
+#define RES856x480x60 0x3F
+#define RES1280x768x60 0x40
+#define RES1400x1050x60 0x41
+#define RES1152x864x60 0x42
+#define RES1152x864x75 0x43
+#define RES1024x768x160 0x44
+#define RES1280x960x75 0x45
+#define RES1280x960x85 0x46
+#define RES1280x960x120 0x47
+
+
+#define XG27_CR8F 0x0C
+#define XG27_SR36 0x30
+#define XG27_SR40 0x04
+#define XG27_SR41 0x00
+#define XG40_CRCF 0x13
+#define XGI330_CRT2Data_1_2 0
+#define XGI330_CRT2Data_4_D 0
+#define XGI330_CRT2Data_4_E 0
+#define XGI330_CRT2Data_4_10 0x80
+#define XGI330_SR07 0x18
+#define XGI330_SR1F 0
+#define XGI330_SR23 0xf6
+#define XGI330_SR24 0x0d
+#define XGI330_SR31 0xc0
+#define XGI330_SR32 0x11
+#define XGI330_SR33 0
+
+extern const struct XGI_ExtStruct XGI330_EModeIDTable[];
+extern const struct XGI_Ext2Struct XGI330_RefIndex[];
+extern const struct XGI_CRT1TableStruct XGI_CRT1Table[];
+extern const struct XGI_ECLKDataStruct XGI340_ECLKData[];
+extern const struct SiS_VCLKData XGI_VCLKData[];
+extern const unsigned char XGI340_CR6B[][4];
+extern const unsigned char XGI340_AGPReg[];
+
+#endif
diff --git a/drivers/staging/xgifb/vb_init.c b/drivers/staging/xgifb/vb_init.c
new file mode 100644
index 000000000..2b233af8e
--- /dev/null
+++ b/drivers/staging/xgifb/vb_init.c
@@ -0,0 +1,1367 @@
+#include <linux/delay.h>
+#include <linux/vmalloc.h>
+
+#include "XGIfb.h"
+#include "vb_def.h"
+#include "vb_util.h"
+#include "vb_setmode.h"
+#include "vb_init.h"
+static const unsigned short XGINew_DDRDRAM_TYPE340[4][2] = {
+ { 16, 0x45},
+ { 8, 0x35},
+ { 4, 0x31},
+ { 2, 0x21} };
+
+static const unsigned short XGINew_DDRDRAM_TYPE20[12][2] = {
+ { 128, 0x5D},
+ { 64, 0x59},
+ { 64, 0x4D},
+ { 32, 0x55},
+ { 32, 0x49},
+ { 32, 0x3D},
+ { 16, 0x51},
+ { 16, 0x45},
+ { 16, 0x39},
+ { 8, 0x41},
+ { 8, 0x35},
+ { 4, 0x31} };
+
+#define XGIFB_ROM_SIZE 65536
+
+static unsigned char
+XGINew_GetXG20DRAMType(struct xgi_hw_device_info *HwDeviceExtension,
+ struct vb_device_info *pVBInfo)
+{
+ unsigned char data, temp;
+
+ if (HwDeviceExtension->jChipType < XG20) {
+ data = xgifb_reg_get(pVBInfo->P3c4, 0x39) & 0x02;
+ if (data == 0)
+ data = (xgifb_reg_get(pVBInfo->P3c4, 0x3A) &
+ 0x02) >> 1;
+ return data;
+ } else if (HwDeviceExtension->jChipType == XG27) {
+ temp = xgifb_reg_get(pVBInfo->P3c4, 0x3B);
+ /* SR3B[7][3]MAA15 MAA11 (Power on Trapping) */
+ if (((temp & 0x88) == 0x80) || ((temp & 0x88) == 0x08))
+ data = 0; /* DDR */
+ else
+ data = 1; /* DDRII */
+ return data;
+ } else if (HwDeviceExtension->jChipType == XG21) {
+ /* Independent GPIO control */
+ xgifb_reg_and(pVBInfo->P3d4, 0xB4, ~0x02);
+ udelay(800);
+ xgifb_reg_or(pVBInfo->P3d4, 0x4A, 0x80); /* Enable GPIOH read */
+ /* GPIOF 0:DVI 1:DVO */
+ data = xgifb_reg_get(pVBInfo->P3d4, 0x48);
+ /* HOTPLUG_SUPPORT */
+ /* for current XG20 & XG21, GPIOH is floating, driver will
+ * fix DDR temporarily */
+ /* DVI read GPIOH */
+ data &= 0x01; /* 1=DDRII, 0=DDR */
+ /* ~HOTPLUG_SUPPORT */
+ xgifb_reg_or(pVBInfo->P3d4, 0xB4, 0x02);
+ return data;
+ }
+ data = xgifb_reg_get(pVBInfo->P3d4, 0x97) & 0x01;
+
+ if (data == 1)
+ data++;
+
+ return data;
+}
+
+static void XGINew_DDR1x_MRS_340(unsigned long P3c4,
+ struct vb_device_info *pVBInfo)
+{
+ xgifb_reg_set(P3c4, 0x18, 0x01);
+ xgifb_reg_set(P3c4, 0x19, 0x20);
+ xgifb_reg_set(P3c4, 0x16, 0x00);
+ xgifb_reg_set(P3c4, 0x16, 0x80);
+
+ mdelay(3);
+ xgifb_reg_set(P3c4, 0x18, 0x00);
+ xgifb_reg_set(P3c4, 0x19, 0x20);
+ xgifb_reg_set(P3c4, 0x16, 0x00);
+ xgifb_reg_set(P3c4, 0x16, 0x80);
+
+ udelay(60);
+ xgifb_reg_set(P3c4, 0x18, pVBInfo->SR18[pVBInfo->ram_type]); /* SR18 */
+ xgifb_reg_set(P3c4, 0x19, 0x01);
+ xgifb_reg_set(P3c4, 0x16, 0x03);
+ xgifb_reg_set(P3c4, 0x16, 0x83);
+ mdelay(1);
+ xgifb_reg_set(P3c4, 0x1B, 0x03);
+ udelay(500);
+ xgifb_reg_set(P3c4, 0x18, pVBInfo->SR18[pVBInfo->ram_type]); /* SR18 */
+ xgifb_reg_set(P3c4, 0x19, 0x00);
+ xgifb_reg_set(P3c4, 0x16, 0x03);
+ xgifb_reg_set(P3c4, 0x16, 0x83);
+ xgifb_reg_set(P3c4, 0x1B, 0x00);
+}
+
+static void XGINew_SetMemoryClock(struct vb_device_info *pVBInfo)
+{
+ xgifb_reg_set(pVBInfo->P3c4,
+ 0x28,
+ pVBInfo->MCLKData[pVBInfo->ram_type].SR28);
+ xgifb_reg_set(pVBInfo->P3c4,
+ 0x29,
+ pVBInfo->MCLKData[pVBInfo->ram_type].SR29);
+ xgifb_reg_set(pVBInfo->P3c4,
+ 0x2A,
+ pVBInfo->MCLKData[pVBInfo->ram_type].SR2A);
+
+ xgifb_reg_set(pVBInfo->P3c4,
+ 0x2E,
+ XGI340_ECLKData[pVBInfo->ram_type].SR2E);
+ xgifb_reg_set(pVBInfo->P3c4,
+ 0x2F,
+ XGI340_ECLKData[pVBInfo->ram_type].SR2F);
+ xgifb_reg_set(pVBInfo->P3c4,
+ 0x30,
+ XGI340_ECLKData[pVBInfo->ram_type].SR30);
+}
+
+static void XGINew_DDRII_Bootup_XG27(
+ struct xgi_hw_device_info *HwDeviceExtension,
+ unsigned long P3c4, struct vb_device_info *pVBInfo)
+{
+ unsigned long P3d4 = P3c4 + 0x10;
+
+ pVBInfo->ram_type = XGINew_GetXG20DRAMType(HwDeviceExtension, pVBInfo);
+ XGINew_SetMemoryClock(pVBInfo);
+
+ /* Set Double Frequency */
+ xgifb_reg_set(P3d4, 0x97, pVBInfo->XGINew_CR97); /* CR97 */
+
+ udelay(200);
+
+ xgifb_reg_set(P3c4, 0x18, 0x00); /* Set SR18 */ /* EMRS2 */
+ xgifb_reg_set(P3c4, 0x19, 0x80); /* Set SR19 */
+ xgifb_reg_set(P3c4, 0x16, 0x20); /* Set SR16 */
+ udelay(15);
+ xgifb_reg_set(P3c4, 0x16, 0xA0); /* Set SR16 */
+ udelay(15);
+
+ xgifb_reg_set(P3c4, 0x18, 0x00); /* Set SR18 */ /* EMRS3 */
+ xgifb_reg_set(P3c4, 0x19, 0xC0); /* Set SR19 */
+ xgifb_reg_set(P3c4, 0x16, 0x20); /* Set SR16 */
+ udelay(15);
+ xgifb_reg_set(P3c4, 0x16, 0xA0); /* Set SR16 */
+ udelay(15);
+
+ xgifb_reg_set(P3c4, 0x18, 0x00); /* Set SR18 */ /* EMRS1 */
+ xgifb_reg_set(P3c4, 0x19, 0x40); /* Set SR19 */
+ xgifb_reg_set(P3c4, 0x16, 0x20); /* Set SR16 */
+ udelay(30);
+ xgifb_reg_set(P3c4, 0x16, 0xA0); /* Set SR16 */
+ udelay(15);
+
+ xgifb_reg_set(P3c4, 0x18, 0x42); /* Set SR18 */ /* MRS, DLL Enable */
+ xgifb_reg_set(P3c4, 0x19, 0x0A); /* Set SR19 */
+ xgifb_reg_set(P3c4, 0x16, 0x00); /* Set SR16 */
+ udelay(30);
+ xgifb_reg_set(P3c4, 0x16, 0x00); /* Set SR16 */
+ xgifb_reg_set(P3c4, 0x16, 0x80); /* Set SR16 */
+
+ xgifb_reg_set(P3c4, 0x1B, 0x04); /* Set SR1B */
+ udelay(60);
+ xgifb_reg_set(P3c4, 0x1B, 0x00); /* Set SR1B */
+
+ xgifb_reg_set(P3c4, 0x18, 0x42); /* Set SR18 */ /* MRS, DLL Reset */
+ xgifb_reg_set(P3c4, 0x19, 0x08); /* Set SR19 */
+ xgifb_reg_set(P3c4, 0x16, 0x00); /* Set SR16 */
+
+ udelay(30);
+ xgifb_reg_set(P3c4, 0x16, 0x83); /* Set SR16 */
+ udelay(15);
+
+ xgifb_reg_set(P3c4, 0x18, 0x80); /* Set SR18 */ /* MRS, ODT */
+ xgifb_reg_set(P3c4, 0x19, 0x46); /* Set SR19 */
+ xgifb_reg_set(P3c4, 0x16, 0x20); /* Set SR16 */
+ udelay(30);
+ xgifb_reg_set(P3c4, 0x16, 0xA0); /* Set SR16 */
+ udelay(15);
+
+ xgifb_reg_set(P3c4, 0x18, 0x00); /* Set SR18 */ /* EMRS */
+ xgifb_reg_set(P3c4, 0x19, 0x40); /* Set SR19 */
+ xgifb_reg_set(P3c4, 0x16, 0x20); /* Set SR16 */
+ udelay(30);
+ xgifb_reg_set(P3c4, 0x16, 0xA0); /* Set SR16 */
+ udelay(15);
+
+ /* Set SR1B refresh control 000:close; 010:open */
+ xgifb_reg_set(P3c4, 0x1B, 0x04);
+ udelay(200);
+
+}
+
+static void XGINew_DDR2_MRS_XG20(struct xgi_hw_device_info *HwDeviceExtension,
+ unsigned long P3c4, struct vb_device_info *pVBInfo)
+{
+ unsigned long P3d4 = P3c4 + 0x10;
+
+ pVBInfo->ram_type = XGINew_GetXG20DRAMType(HwDeviceExtension, pVBInfo);
+ XGINew_SetMemoryClock(pVBInfo);
+
+ xgifb_reg_set(P3d4, 0x97, 0x11); /* CR97 */
+
+ udelay(200);
+ xgifb_reg_set(P3c4, 0x18, 0x00); /* EMRS2 */
+ xgifb_reg_set(P3c4, 0x19, 0x80);
+ xgifb_reg_set(P3c4, 0x16, 0x05);
+ xgifb_reg_set(P3c4, 0x16, 0x85);
+
+ xgifb_reg_set(P3c4, 0x18, 0x00); /* EMRS3 */
+ xgifb_reg_set(P3c4, 0x19, 0xC0);
+ xgifb_reg_set(P3c4, 0x16, 0x05);
+ xgifb_reg_set(P3c4, 0x16, 0x85);
+
+ xgifb_reg_set(P3c4, 0x18, 0x00); /* EMRS1 */
+ xgifb_reg_set(P3c4, 0x19, 0x40);
+ xgifb_reg_set(P3c4, 0x16, 0x05);
+ xgifb_reg_set(P3c4, 0x16, 0x85);
+
+ xgifb_reg_set(P3c4, 0x18, 0x42); /* MRS1 */
+ xgifb_reg_set(P3c4, 0x19, 0x02);
+ xgifb_reg_set(P3c4, 0x16, 0x05);
+ xgifb_reg_set(P3c4, 0x16, 0x85);
+
+ udelay(15);
+ xgifb_reg_set(P3c4, 0x1B, 0x04); /* SR1B */
+ udelay(30);
+ xgifb_reg_set(P3c4, 0x1B, 0x00); /* SR1B */
+ udelay(100);
+
+ xgifb_reg_set(P3c4, 0x18, 0x42); /* MRS1 */
+ xgifb_reg_set(P3c4, 0x19, 0x00);
+ xgifb_reg_set(P3c4, 0x16, 0x05);
+ xgifb_reg_set(P3c4, 0x16, 0x85);
+
+ udelay(200);
+}
+
+static void XGINew_DDR1x_MRS_XG20(unsigned long P3c4,
+ struct vb_device_info *pVBInfo)
+{
+ xgifb_reg_set(P3c4, 0x18, 0x01);
+ xgifb_reg_set(P3c4, 0x19, 0x40);
+ xgifb_reg_set(P3c4, 0x16, 0x00);
+ xgifb_reg_set(P3c4, 0x16, 0x80);
+ udelay(60);
+
+ xgifb_reg_set(P3c4, 0x18, 0x00);
+ xgifb_reg_set(P3c4, 0x19, 0x40);
+ xgifb_reg_set(P3c4, 0x16, 0x00);
+ xgifb_reg_set(P3c4, 0x16, 0x80);
+ udelay(60);
+ xgifb_reg_set(P3c4, 0x18, pVBInfo->SR18[pVBInfo->ram_type]); /* SR18 */
+ xgifb_reg_set(P3c4, 0x19, 0x01);
+ xgifb_reg_set(P3c4, 0x16, 0x03);
+ xgifb_reg_set(P3c4, 0x16, 0x83);
+ mdelay(1);
+ xgifb_reg_set(P3c4, 0x1B, 0x03);
+ udelay(500);
+ xgifb_reg_set(P3c4, 0x18, pVBInfo->SR18[pVBInfo->ram_type]); /* SR18 */
+ xgifb_reg_set(P3c4, 0x19, 0x00);
+ xgifb_reg_set(P3c4, 0x16, 0x03);
+ xgifb_reg_set(P3c4, 0x16, 0x83);
+ xgifb_reg_set(P3c4, 0x1B, 0x00);
+}
+
+static void XGINew_DDR1x_DefaultRegister(
+ struct xgi_hw_device_info *HwDeviceExtension,
+ unsigned long Port, struct vb_device_info *pVBInfo)
+{
+ unsigned long P3d4 = Port, P3c4 = Port - 0x10;
+
+ if (HwDeviceExtension->jChipType >= XG20) {
+ XGINew_SetMemoryClock(pVBInfo);
+ xgifb_reg_set(P3d4,
+ 0x82,
+ pVBInfo->CR40[11][pVBInfo->ram_type]); /* CR82 */
+ xgifb_reg_set(P3d4,
+ 0x85,
+ pVBInfo->CR40[12][pVBInfo->ram_type]); /* CR85 */
+ xgifb_reg_set(P3d4,
+ 0x86,
+ pVBInfo->CR40[13][pVBInfo->ram_type]); /* CR86 */
+
+ xgifb_reg_set(P3d4, 0x98, 0x01);
+ xgifb_reg_set(P3d4, 0x9A, 0x02);
+
+ XGINew_DDR1x_MRS_XG20(P3c4, pVBInfo);
+ } else {
+ XGINew_SetMemoryClock(pVBInfo);
+
+ switch (HwDeviceExtension->jChipType) {
+ case XG42:
+ /* CR82 */
+ xgifb_reg_set(P3d4,
+ 0x82,
+ pVBInfo->CR40[11][pVBInfo->ram_type]);
+ /* CR85 */
+ xgifb_reg_set(P3d4,
+ 0x85,
+ pVBInfo->CR40[12][pVBInfo->ram_type]);
+ /* CR86 */
+ xgifb_reg_set(P3d4,
+ 0x86,
+ pVBInfo->CR40[13][pVBInfo->ram_type]);
+ break;
+ default:
+ xgifb_reg_set(P3d4, 0x82, 0x88);
+ xgifb_reg_set(P3d4, 0x86, 0x00);
+ /* Insert read command for delay */
+ xgifb_reg_get(P3d4, 0x86);
+ xgifb_reg_set(P3d4, 0x86, 0x88);
+ xgifb_reg_get(P3d4, 0x86);
+ xgifb_reg_set(P3d4,
+ 0x86,
+ pVBInfo->CR40[13][pVBInfo->ram_type]);
+ xgifb_reg_set(P3d4, 0x82, 0x77);
+ xgifb_reg_set(P3d4, 0x85, 0x00);
+
+ /* Insert read command for delay */
+ xgifb_reg_get(P3d4, 0x85);
+ xgifb_reg_set(P3d4, 0x85, 0x88);
+
+ /* Insert read command for delay */
+ xgifb_reg_get(P3d4, 0x85);
+ /* CR85 */
+ xgifb_reg_set(P3d4,
+ 0x85,
+ pVBInfo->CR40[12][pVBInfo->ram_type]);
+ /* CR82 */
+ xgifb_reg_set(P3d4,
+ 0x82,
+ pVBInfo->CR40[11][pVBInfo->ram_type]);
+ break;
+ }
+
+ xgifb_reg_set(P3d4, 0x97, 0x00);
+ xgifb_reg_set(P3d4, 0x98, 0x01);
+ xgifb_reg_set(P3d4, 0x9A, 0x02);
+ XGINew_DDR1x_MRS_340(P3c4, pVBInfo);
+ }
+}
+
+static void XGINew_DDR2_DefaultRegister(
+ struct xgi_hw_device_info *HwDeviceExtension,
+ unsigned long Port, struct vb_device_info *pVBInfo)
+{
+ unsigned long P3d4 = Port, P3c4 = Port - 0x10;
+
+ /* keep following setting sequence, each setting in
+ * the same reg insert idle */
+ xgifb_reg_set(P3d4, 0x82, 0x77);
+ xgifb_reg_set(P3d4, 0x86, 0x00);
+ xgifb_reg_get(P3d4, 0x86); /* Insert read command for delay */
+ xgifb_reg_set(P3d4, 0x86, 0x88);
+ xgifb_reg_get(P3d4, 0x86); /* Insert read command for delay */
+ /* CR86 */
+ xgifb_reg_set(P3d4, 0x86, pVBInfo->CR40[13][pVBInfo->ram_type]);
+ xgifb_reg_set(P3d4, 0x82, 0x77);
+ xgifb_reg_set(P3d4, 0x85, 0x00);
+ xgifb_reg_get(P3d4, 0x85); /* Insert read command for delay */
+ xgifb_reg_set(P3d4, 0x85, 0x88);
+ xgifb_reg_get(P3d4, 0x85); /* Insert read command for delay */
+ xgifb_reg_set(P3d4,
+ 0x85,
+ pVBInfo->CR40[12][pVBInfo->ram_type]); /* CR85 */
+ if (HwDeviceExtension->jChipType == XG27)
+ /* CR82 */
+ xgifb_reg_set(P3d4, 0x82, pVBInfo->CR40[11][pVBInfo->ram_type]);
+ else
+ xgifb_reg_set(P3d4, 0x82, 0xA8); /* CR82 */
+
+ xgifb_reg_set(P3d4, 0x98, 0x01);
+ xgifb_reg_set(P3d4, 0x9A, 0x02);
+ if (HwDeviceExtension->jChipType == XG27)
+ XGINew_DDRII_Bootup_XG27(HwDeviceExtension, P3c4, pVBInfo);
+ else
+ XGINew_DDR2_MRS_XG20(HwDeviceExtension, P3c4, pVBInfo);
+}
+
+static void XGI_SetDRAM_Helper(unsigned long P3d4, u8 seed, u8 temp2, u8 reg,
+ u8 shift_factor, u8 mask1, u8 mask2)
+{
+ u8 j;
+
+ for (j = 0; j < 4; j++) {
+ temp2 |= (((seed >> (2 * j)) & 0x03) << shift_factor);
+ xgifb_reg_set(P3d4, reg, temp2);
+ xgifb_reg_get(P3d4, reg);
+ temp2 &= mask1;
+ temp2 += mask2;
+ }
+}
+
+static void XGINew_SetDRAMDefaultRegister340(
+ struct xgi_hw_device_info *HwDeviceExtension,
+ unsigned long Port, struct vb_device_info *pVBInfo)
+{
+ unsigned char temp, temp1, temp2, temp3, j, k;
+
+ unsigned long P3d4 = Port, P3c4 = Port - 0x10;
+
+ xgifb_reg_set(P3d4, 0x6D, pVBInfo->CR40[8][pVBInfo->ram_type]);
+ xgifb_reg_set(P3d4, 0x68, pVBInfo->CR40[5][pVBInfo->ram_type]);
+ xgifb_reg_set(P3d4, 0x69, pVBInfo->CR40[6][pVBInfo->ram_type]);
+ xgifb_reg_set(P3d4, 0x6A, pVBInfo->CR40[7][pVBInfo->ram_type]);
+
+ /* CR6B DQS fine tune delay */
+ temp = 0xaa;
+ XGI_SetDRAM_Helper(P3d4, temp, 0, 0x6B, 2, 0xF0, 0x10);
+
+ /* CR6E DQM fine tune delay */
+ XGI_SetDRAM_Helper(P3d4, 0, 0, 0x6E, 2, 0xF0, 0x10);
+
+ temp3 = 0;
+ for (k = 0; k < 4; k++) {
+ /* CR6E_D[1:0] select channel */
+ xgifb_reg_and_or(P3d4, 0x6E, 0xFC, temp3);
+ XGI_SetDRAM_Helper(P3d4, 0, 0, 0x6F, 0, 0xF8, 0x08);
+ temp3 += 0x01;
+ }
+
+ xgifb_reg_set(P3d4,
+ 0x80,
+ pVBInfo->CR40[9][pVBInfo->ram_type]); /* CR80 */
+ xgifb_reg_set(P3d4,
+ 0x81,
+ pVBInfo->CR40[10][pVBInfo->ram_type]); /* CR81 */
+
+ temp2 = 0x80;
+ /* CR89 terminator type select */
+ XGI_SetDRAM_Helper(P3d4, 0, temp2, 0x89, 0, 0xF0, 0x10);
+
+ temp = 0;
+ temp1 = temp & 0x03;
+ temp2 |= temp1;
+ xgifb_reg_set(P3d4, 0x89, temp2);
+
+ temp = pVBInfo->CR40[3][pVBInfo->ram_type];
+ temp1 = temp & 0x0F;
+ temp2 = (temp >> 4) & 0x07;
+ temp3 = temp & 0x80;
+ xgifb_reg_set(P3d4, 0x45, temp1); /* CR45 */
+ xgifb_reg_set(P3d4, 0x99, temp2); /* CR99 */
+ xgifb_reg_or(P3d4, 0x40, temp3); /* CR40_D[7] */
+ xgifb_reg_set(P3d4,
+ 0x41,
+ pVBInfo->CR40[0][pVBInfo->ram_type]); /* CR41 */
+
+ if (HwDeviceExtension->jChipType == XG27)
+ xgifb_reg_set(P3d4, 0x8F, XG27_CR8F); /* CR8F */
+
+ for (j = 0; j <= 6; j++) /* CR90 - CR96 */
+ xgifb_reg_set(P3d4, (0x90 + j),
+ pVBInfo->CR40[14 + j][pVBInfo->ram_type]);
+
+ for (j = 0; j <= 2; j++) /* CRC3 - CRC5 */
+ xgifb_reg_set(P3d4, (0xC3 + j),
+ pVBInfo->CR40[21 + j][pVBInfo->ram_type]);
+
+ for (j = 0; j < 2; j++) /* CR8A - CR8B */
+ xgifb_reg_set(P3d4, (0x8A + j),
+ pVBInfo->CR40[1 + j][pVBInfo->ram_type]);
+
+ if (HwDeviceExtension->jChipType == XG42)
+ xgifb_reg_set(P3d4, 0x8C, 0x87);
+
+ xgifb_reg_set(P3d4,
+ 0x59,
+ pVBInfo->CR40[4][pVBInfo->ram_type]); /* CR59 */
+
+ xgifb_reg_set(P3d4, 0x83, 0x09); /* CR83 */
+ xgifb_reg_set(P3d4, 0x87, 0x00); /* CR87 */
+ xgifb_reg_set(P3d4, 0xCF, XG40_CRCF); /* CRCF */
+ if (pVBInfo->ram_type) {
+ xgifb_reg_set(P3c4, 0x17, 0x80); /* SR17 DDRII */
+ if (HwDeviceExtension->jChipType == XG27)
+ xgifb_reg_set(P3c4, 0x17, 0x02); /* SR17 DDRII */
+
+ } else {
+ xgifb_reg_set(P3c4, 0x17, 0x00); /* SR17 DDR */
+ }
+ xgifb_reg_set(P3c4, 0x1A, 0x87); /* SR1A */
+
+ temp = XGINew_GetXG20DRAMType(HwDeviceExtension, pVBInfo);
+ if (temp == 0) {
+ XGINew_DDR1x_DefaultRegister(HwDeviceExtension, P3d4, pVBInfo);
+ } else {
+ xgifb_reg_set(P3d4, 0xB0, 0x80); /* DDRII Dual frequency mode */
+ XGINew_DDR2_DefaultRegister(HwDeviceExtension, P3d4, pVBInfo);
+ }
+ xgifb_reg_set(P3c4, 0x1B, 0x03); /* SR1B */
+}
+
+
+static unsigned short XGINew_SetDRAMSize20Reg(
+ unsigned short dram_size,
+ struct vb_device_info *pVBInfo)
+{
+ unsigned short data = 0, memsize = 0;
+ int RankSize;
+ unsigned char ChannelNo;
+
+ RankSize = dram_size * pVBInfo->ram_bus / 8;
+ data = xgifb_reg_get(pVBInfo->P3c4, 0x13);
+ data &= 0x80;
+
+ if (data == 0x80)
+ RankSize *= 2;
+
+ data = 0;
+
+ if (pVBInfo->ram_channel == 3)
+ ChannelNo = 4;
+ else
+ ChannelNo = pVBInfo->ram_channel;
+
+ if (ChannelNo * RankSize <= 256) {
+ while ((RankSize >>= 1) > 0)
+ data += 0x10;
+
+ memsize = data >> 4;
+
+ /* Fix DRAM Sizing Error */
+ xgifb_reg_set(pVBInfo->P3c4,
+ 0x14,
+ (xgifb_reg_get(pVBInfo->P3c4, 0x14) & 0x0F) |
+ (data & 0xF0));
+ udelay(15);
+ }
+ return memsize;
+}
+
+static int XGINew_ReadWriteRest(unsigned short StopAddr,
+ unsigned short StartAddr, struct vb_device_info *pVBInfo)
+{
+ int i;
+ unsigned long Position = 0;
+ void __iomem *fbaddr = pVBInfo->FBAddr;
+
+ writel(Position, fbaddr + Position);
+
+ for (i = StartAddr; i <= StopAddr; i++) {
+ Position = 1 << i;
+ writel(Position, fbaddr + Position);
+ }
+
+ udelay(500); /* Fix #1759 Memory Size error in Multi-Adapter. */
+
+ Position = 0;
+
+ if (readl(fbaddr + Position) != Position)
+ return 0;
+
+ for (i = StartAddr; i <= StopAddr; i++) {
+ Position = 1 << i;
+ if (readl(fbaddr + Position) != Position)
+ return 0;
+ }
+ return 1;
+}
+
+static unsigned char XGINew_CheckFrequence(struct vb_device_info *pVBInfo)
+{
+ unsigned char data;
+
+ data = xgifb_reg_get(pVBInfo->P3d4, 0x97);
+
+ if ((data & 0x10) == 0) {
+ data = xgifb_reg_get(pVBInfo->P3c4, 0x39);
+ data = (data & 0x02) >> 1;
+ return data;
+ }
+ return data & 0x01;
+}
+
+static void XGINew_CheckChannel(struct xgi_hw_device_info *HwDeviceExtension,
+ struct vb_device_info *pVBInfo)
+{
+ unsigned char data;
+
+ switch (HwDeviceExtension->jChipType) {
+ case XG20:
+ case XG21:
+ data = xgifb_reg_get(pVBInfo->P3d4, 0x97);
+ data = data & 0x01;
+ pVBInfo->ram_channel = 1; /* XG20 "JUST" one channel */
+
+ if (data == 0) { /* Single_32_16 */
+
+ if ((HwDeviceExtension->ulVideoMemorySize - 1)
+ > 0x1000000) {
+
+ pVBInfo->ram_bus = 32; /* 32 bits */
+ /* 22bit + 2 rank + 32bit */
+ xgifb_reg_set(pVBInfo->P3c4, 0x13, 0xB1);
+ xgifb_reg_set(pVBInfo->P3c4, 0x14, 0x52);
+ udelay(15);
+
+ if (XGINew_ReadWriteRest(24, 23, pVBInfo) == 1)
+ return;
+
+ if ((HwDeviceExtension->ulVideoMemorySize - 1) >
+ 0x800000) {
+ /* 22bit + 1 rank + 32bit */
+ xgifb_reg_set(pVBInfo->P3c4,
+ 0x13,
+ 0x31);
+ xgifb_reg_set(pVBInfo->P3c4,
+ 0x14,
+ 0x42);
+ udelay(15);
+
+ if (XGINew_ReadWriteRest(23,
+ 23,
+ pVBInfo) == 1)
+ return;
+ }
+ }
+
+ if ((HwDeviceExtension->ulVideoMemorySize - 1) >
+ 0x800000) {
+ pVBInfo->ram_bus = 16; /* 16 bits */
+ /* 22bit + 2 rank + 16bit */
+ xgifb_reg_set(pVBInfo->P3c4, 0x13, 0xB1);
+ xgifb_reg_set(pVBInfo->P3c4, 0x14, 0x41);
+ udelay(15);
+
+ if (XGINew_ReadWriteRest(23, 22, pVBInfo) == 1)
+ return;
+ xgifb_reg_set(pVBInfo->P3c4,
+ 0x13,
+ 0x31);
+ udelay(15);
+ }
+
+ } else { /* Dual_16_8 */
+ if ((HwDeviceExtension->ulVideoMemorySize - 1) >
+ 0x800000) {
+ pVBInfo->ram_bus = 16; /* 16 bits */
+ /* (0x31:12x8x2) 22bit + 2 rank */
+ xgifb_reg_set(pVBInfo->P3c4, 0x13, 0xB1);
+ /* 0x41:16Mx16 bit*/
+ xgifb_reg_set(pVBInfo->P3c4, 0x14, 0x41);
+ udelay(15);
+
+ if (XGINew_ReadWriteRest(23, 22, pVBInfo) == 1)
+ return;
+
+ if ((HwDeviceExtension->ulVideoMemorySize - 1) >
+ 0x400000) {
+ /* (0x31:12x8x2) 22bit + 1 rank */
+ xgifb_reg_set(pVBInfo->P3c4,
+ 0x13,
+ 0x31);
+ /* 0x31:8Mx16 bit*/
+ xgifb_reg_set(pVBInfo->P3c4,
+ 0x14,
+ 0x31);
+ udelay(15);
+
+ if (XGINew_ReadWriteRest(22,
+ 22,
+ pVBInfo) == 1)
+ return;
+ }
+ }
+
+ if ((HwDeviceExtension->ulVideoMemorySize - 1) >
+ 0x400000) {
+ pVBInfo->ram_bus = 8; /* 8 bits */
+ /* (0x31:12x8x2) 22bit + 2 rank */
+ xgifb_reg_set(pVBInfo->P3c4, 0x13, 0xB1);
+ /* 0x30:8Mx8 bit*/
+ xgifb_reg_set(pVBInfo->P3c4, 0x14, 0x30);
+ udelay(15);
+
+ if (XGINew_ReadWriteRest(22, 21, pVBInfo) == 1)
+ return;
+
+ /* (0x31:12x8x2) 22bit + 1 rank */
+ xgifb_reg_set(pVBInfo->P3c4,
+ 0x13,
+ 0x31);
+ udelay(15);
+ }
+ }
+ break;
+
+ case XG27:
+ pVBInfo->ram_bus = 16; /* 16 bits */
+ pVBInfo->ram_channel = 1; /* Single channel */
+ xgifb_reg_set(pVBInfo->P3c4, 0x14, 0x51); /* 32Mx16 bit*/
+ break;
+ case XG42:
+ /*
+ XG42 SR14 D[3] Reserve
+ D[2] = 1, Dual Channel
+ = 0, Single Channel
+
+ It's Different from Other XG40 Series.
+ */
+ if (XGINew_CheckFrequence(pVBInfo) == 1) { /* DDRII, DDR2x */
+ pVBInfo->ram_bus = 32; /* 32 bits */
+ pVBInfo->ram_channel = 2; /* 2 Channel */
+ xgifb_reg_set(pVBInfo->P3c4, 0x13, 0xA1);
+ xgifb_reg_set(pVBInfo->P3c4, 0x14, 0x44);
+
+ if (XGINew_ReadWriteRest(24, 23, pVBInfo) == 1)
+ return;
+
+ xgifb_reg_set(pVBInfo->P3c4, 0x13, 0x21);
+ xgifb_reg_set(pVBInfo->P3c4, 0x14, 0x34);
+ if (XGINew_ReadWriteRest(23, 22, pVBInfo) == 1)
+ return;
+
+ pVBInfo->ram_channel = 1; /* Single Channel */
+ xgifb_reg_set(pVBInfo->P3c4, 0x13, 0xA1);
+ xgifb_reg_set(pVBInfo->P3c4, 0x14, 0x40);
+
+ if (XGINew_ReadWriteRest(23, 22, pVBInfo) == 1)
+ return;
+ xgifb_reg_set(pVBInfo->P3c4, 0x13, 0x21);
+ xgifb_reg_set(pVBInfo->P3c4, 0x14, 0x30);
+ } else { /* DDR */
+ pVBInfo->ram_bus = 64; /* 64 bits */
+ pVBInfo->ram_channel = 1; /* 1 channels */
+ xgifb_reg_set(pVBInfo->P3c4, 0x13, 0xA1);
+ xgifb_reg_set(pVBInfo->P3c4, 0x14, 0x52);
+
+ if (XGINew_ReadWriteRest(24, 23, pVBInfo) == 1)
+ return;
+ xgifb_reg_set(pVBInfo->P3c4, 0x13, 0x21);
+ xgifb_reg_set(pVBInfo->P3c4, 0x14, 0x42);
+ }
+
+ break;
+
+ default: /* XG40 */
+
+ if (XGINew_CheckFrequence(pVBInfo) == 1) { /* DDRII */
+ pVBInfo->ram_bus = 32; /* 32 bits */
+ pVBInfo->ram_channel = 3;
+ xgifb_reg_set(pVBInfo->P3c4, 0x13, 0xA1);
+ xgifb_reg_set(pVBInfo->P3c4, 0x14, 0x4C);
+
+ if (XGINew_ReadWriteRest(25, 23, pVBInfo) == 1)
+ return;
+
+ pVBInfo->ram_channel = 2; /* 2 channels */
+ xgifb_reg_set(pVBInfo->P3c4, 0x14, 0x48);
+
+ if (XGINew_ReadWriteRest(24, 23, pVBInfo) == 1)
+ return;
+
+ xgifb_reg_set(pVBInfo->P3c4, 0x13, 0x21);
+ xgifb_reg_set(pVBInfo->P3c4, 0x14, 0x3C);
+
+ if (XGINew_ReadWriteRest(24, 23, pVBInfo) == 1) {
+ pVBInfo->ram_channel = 3; /* 4 channels */
+ } else {
+ pVBInfo->ram_channel = 2; /* 2 channels */
+ xgifb_reg_set(pVBInfo->P3c4, 0x14, 0x38);
+ }
+ } else { /* DDR */
+ pVBInfo->ram_bus = 64; /* 64 bits */
+ pVBInfo->ram_channel = 2; /* 2 channels */
+ xgifb_reg_set(pVBInfo->P3c4, 0x13, 0xA1);
+ xgifb_reg_set(pVBInfo->P3c4, 0x14, 0x5A);
+
+ if (XGINew_ReadWriteRest(25, 24, pVBInfo) == 1)
+ return;
+ xgifb_reg_set(pVBInfo->P3c4, 0x13, 0x21);
+ xgifb_reg_set(pVBInfo->P3c4, 0x14, 0x4A);
+ }
+ break;
+ }
+}
+
+static int XGINew_DDRSizing340(struct xgi_hw_device_info *HwDeviceExtension,
+ struct vb_device_info *pVBInfo)
+{
+ u8 i, size;
+ unsigned short memsize, start_addr;
+ const unsigned short (*dram_table)[2];
+
+ xgifb_reg_set(pVBInfo->P3c4, 0x15, 0x00); /* noninterleaving */
+ xgifb_reg_set(pVBInfo->P3c4, 0x1C, 0x00); /* nontiling */
+ XGINew_CheckChannel(HwDeviceExtension, pVBInfo);
+
+ if (HwDeviceExtension->jChipType >= XG20) {
+ dram_table = XGINew_DDRDRAM_TYPE20;
+ size = ARRAY_SIZE(XGINew_DDRDRAM_TYPE20);
+ start_addr = 5;
+ } else {
+ dram_table = XGINew_DDRDRAM_TYPE340;
+ size = ARRAY_SIZE(XGINew_DDRDRAM_TYPE340);
+ start_addr = 9;
+ }
+
+ for (i = 0; i < size; i++) {
+ /* SetDRAMSizingType */
+ xgifb_reg_and_or(pVBInfo->P3c4, 0x13, 0x80, dram_table[i][1]);
+ udelay(15); /* should delay 50 ns */
+
+ memsize = XGINew_SetDRAMSize20Reg(dram_table[i][0], pVBInfo);
+
+ if (memsize == 0)
+ continue;
+
+ memsize += (pVBInfo->ram_channel - 2) + 20;
+ if ((HwDeviceExtension->ulVideoMemorySize - 1) <
+ (unsigned long) (1 << memsize))
+ continue;
+
+ if (XGINew_ReadWriteRest(memsize, start_addr, pVBInfo) == 1)
+ return 1;
+ }
+ return 0;
+}
+
+static void XGINew_SetDRAMSize_340(struct xgifb_video_info *xgifb_info,
+ struct xgi_hw_device_info *HwDeviceExtension,
+ struct vb_device_info *pVBInfo)
+{
+ unsigned short data;
+
+ pVBInfo->FBAddr = HwDeviceExtension->pjVideoMemoryAddress;
+
+ XGISetModeNew(xgifb_info, HwDeviceExtension, 0x2e);
+
+ data = xgifb_reg_get(pVBInfo->P3c4, 0x21);
+ /* disable read cache */
+ xgifb_reg_set(pVBInfo->P3c4, 0x21, (unsigned short) (data & 0xDF));
+ XGI_DisplayOff(xgifb_info, HwDeviceExtension, pVBInfo);
+
+ XGINew_DDRSizing340(HwDeviceExtension, pVBInfo);
+ data = xgifb_reg_get(pVBInfo->P3c4, 0x21);
+ /* enable read cache */
+ xgifb_reg_set(pVBInfo->P3c4, 0x21, (unsigned short) (data | 0x20));
+}
+
+static u8 *xgifb_copy_rom(struct pci_dev *dev, size_t *rom_size)
+{
+ void __iomem *rom_address;
+ u8 *rom_copy;
+
+ rom_address = pci_map_rom(dev, rom_size);
+ if (rom_address == NULL)
+ return NULL;
+
+ rom_copy = vzalloc(XGIFB_ROM_SIZE);
+ if (rom_copy == NULL)
+ goto done;
+
+ *rom_size = min_t(size_t, *rom_size, XGIFB_ROM_SIZE);
+ memcpy_fromio(rom_copy, rom_address, *rom_size);
+
+done:
+ pci_unmap_rom(dev, rom_address);
+ return rom_copy;
+}
+
+static bool xgifb_read_vbios(struct pci_dev *pdev)
+{
+ struct xgifb_video_info *xgifb_info = pci_get_drvdata(pdev);
+ u8 *vbios;
+ unsigned long i;
+ unsigned char j;
+ struct XGI21_LVDSCapStruct *lvds;
+ size_t vbios_size;
+ int entry;
+
+ vbios = xgifb_copy_rom(pdev, &vbios_size);
+ if (vbios == NULL) {
+ dev_err(&pdev->dev, "Video BIOS not available\n");
+ return false;
+ }
+ if (vbios_size <= 0x65)
+ goto error;
+ /*
+ * The user can ignore the LVDS bit in the BIOS and force the display
+ * type.
+ */
+ if (!(vbios[0x65] & 0x1) &&
+ (!xgifb_info->display2_force ||
+ xgifb_info->display2 != XGIFB_DISP_LCD)) {
+ vfree(vbios);
+ return false;
+ }
+ if (vbios_size <= 0x317)
+ goto error;
+ i = vbios[0x316] | (vbios[0x317] << 8);
+ if (vbios_size <= i - 1)
+ goto error;
+ j = vbios[i - 1];
+ if (j == 0)
+ goto error;
+ if (j == 0xff)
+ j = 1;
+ /*
+ * Read the LVDS table index scratch register set by the BIOS.
+ */
+ entry = xgifb_reg_get(xgifb_info->dev_info.P3d4, 0x36);
+ if (entry >= j)
+ entry = 0;
+ i += entry * 25;
+ lvds = &xgifb_info->lvds_data;
+ if (vbios_size <= i + 24)
+ goto error;
+ lvds->LVDS_Capability = vbios[i] | (vbios[i + 1] << 8);
+ lvds->LVDSHT = vbios[i + 2] | (vbios[i + 3] << 8);
+ lvds->LVDSVT = vbios[i + 4] | (vbios[i + 5] << 8);
+ lvds->LVDSHDE = vbios[i + 6] | (vbios[i + 7] << 8);
+ lvds->LVDSVDE = vbios[i + 8] | (vbios[i + 9] << 8);
+ lvds->LVDSHFP = vbios[i + 10] | (vbios[i + 11] << 8);
+ lvds->LVDSVFP = vbios[i + 12] | (vbios[i + 13] << 8);
+ lvds->LVDSHSYNC = vbios[i + 14] | (vbios[i + 15] << 8);
+ lvds->LVDSVSYNC = vbios[i + 16] | (vbios[i + 17] << 8);
+ lvds->VCLKData1 = vbios[i + 18];
+ lvds->VCLKData2 = vbios[i + 19];
+ lvds->PSC_S1 = vbios[i + 20];
+ lvds->PSC_S2 = vbios[i + 21];
+ lvds->PSC_S3 = vbios[i + 22];
+ lvds->PSC_S4 = vbios[i + 23];
+ lvds->PSC_S5 = vbios[i + 24];
+ vfree(vbios);
+ return true;
+error:
+ dev_err(&pdev->dev, "Video BIOS corrupted\n");
+ vfree(vbios);
+ return false;
+}
+
+static void XGINew_ChkSenseStatus(struct vb_device_info *pVBInfo)
+{
+ unsigned short tempbx = 0, temp, tempcx, CR3CData;
+
+ temp = xgifb_reg_get(pVBInfo->P3d4, 0x32);
+
+ if (temp & Monitor1Sense)
+ tempbx |= ActiveCRT1;
+ if (temp & LCDSense)
+ tempbx |= ActiveLCD;
+ if (temp & Monitor2Sense)
+ tempbx |= ActiveCRT2;
+ if (temp & TVSense) {
+ tempbx |= ActiveTV;
+ if (temp & AVIDEOSense)
+ tempbx |= (ActiveAVideo << 8);
+ if (temp & SVIDEOSense)
+ tempbx |= (ActiveSVideo << 8);
+ if (temp & SCARTSense)
+ tempbx |= (ActiveSCART << 8);
+ if (temp & HiTVSense)
+ tempbx |= (ActiveHiTV << 8);
+ if (temp & YPbPrSense)
+ tempbx |= (ActiveYPbPr << 8);
+ }
+
+ tempcx = xgifb_reg_get(pVBInfo->P3d4, 0x3d);
+ tempcx |= (xgifb_reg_get(pVBInfo->P3d4, 0x3e) << 8);
+
+ if (tempbx & tempcx) {
+ CR3CData = xgifb_reg_get(pVBInfo->P3d4, 0x3c);
+ if (!(CR3CData & DisplayDeviceFromCMOS))
+ tempcx = 0x1FF0;
+ } else {
+ tempcx = 0x1FF0;
+ }
+
+ tempbx &= tempcx;
+ xgifb_reg_set(pVBInfo->P3d4, 0x3d, (tempbx & 0x00FF));
+ xgifb_reg_set(pVBInfo->P3d4, 0x3e, ((tempbx & 0xFF00) >> 8));
+}
+
+static void XGINew_SetModeScratch(struct vb_device_info *pVBInfo)
+{
+ unsigned short temp, tempcl = 0, tempch = 0, CR31Data, CR38Data;
+
+ temp = xgifb_reg_get(pVBInfo->P3d4, 0x3d);
+ temp |= xgifb_reg_get(pVBInfo->P3d4, 0x3e) << 8;
+ temp |= (xgifb_reg_get(pVBInfo->P3d4, 0x31) & (DriverMode >> 8)) << 8;
+
+ if (pVBInfo->IF_DEF_CRT2Monitor == 1) {
+ if (temp & ActiveCRT2)
+ tempcl = SetCRT2ToRAMDAC;
+ }
+
+ if (temp & ActiveLCD) {
+ tempcl |= SetCRT2ToLCD;
+ if (temp & DriverMode) {
+ if (temp & ActiveTV) {
+ tempch = SetToLCDA | EnableDualEdge;
+ temp ^= SetCRT2ToLCD;
+
+ if ((temp >> 8) & ActiveAVideo)
+ tempcl |= SetCRT2ToAVIDEO;
+ if ((temp >> 8) & ActiveSVideo)
+ tempcl |= SetCRT2ToSVIDEO;
+ if ((temp >> 8) & ActiveSCART)
+ tempcl |= SetCRT2ToSCART;
+
+ if (pVBInfo->IF_DEF_HiVision == 1) {
+ if ((temp >> 8) & ActiveHiTV)
+ tempcl |= SetCRT2ToHiVision;
+ }
+
+ if (pVBInfo->IF_DEF_YPbPr == 1) {
+ if ((temp >> 8) & ActiveYPbPr)
+ tempch |= SetYPbPr;
+ }
+ }
+ }
+ } else {
+ if ((temp >> 8) & ActiveAVideo)
+ tempcl |= SetCRT2ToAVIDEO;
+ if ((temp >> 8) & ActiveSVideo)
+ tempcl |= SetCRT2ToSVIDEO;
+ if ((temp >> 8) & ActiveSCART)
+ tempcl |= SetCRT2ToSCART;
+
+ if (pVBInfo->IF_DEF_HiVision == 1) {
+ if ((temp >> 8) & ActiveHiTV)
+ tempcl |= SetCRT2ToHiVision;
+ }
+
+ if (pVBInfo->IF_DEF_YPbPr == 1) {
+ if ((temp >> 8) & ActiveYPbPr)
+ tempch |= SetYPbPr;
+ }
+ }
+
+ tempcl |= SetSimuScanMode;
+ if ((!(temp & ActiveCRT1)) && ((temp & ActiveLCD) || (temp & ActiveTV)
+ || (temp & ActiveCRT2)))
+ tempcl ^= (SetSimuScanMode | SwitchCRT2);
+ if ((temp & ActiveLCD) && (temp & ActiveTV))
+ tempcl ^= (SetSimuScanMode | SwitchCRT2);
+ xgifb_reg_set(pVBInfo->P3d4, 0x30, tempcl);
+
+ CR31Data = xgifb_reg_get(pVBInfo->P3d4, 0x31);
+ CR31Data &= ~(SetNotSimuMode >> 8);
+ if (!(temp & ActiveCRT1))
+ CR31Data |= (SetNotSimuMode >> 8);
+ CR31Data &= ~(DisableCRT2Display >> 8);
+ if (!((temp & ActiveLCD) || (temp & ActiveTV) || (temp & ActiveCRT2)))
+ CR31Data |= (DisableCRT2Display >> 8);
+ xgifb_reg_set(pVBInfo->P3d4, 0x31, CR31Data);
+
+ CR38Data = xgifb_reg_get(pVBInfo->P3d4, 0x38);
+ CR38Data &= ~SetYPbPr;
+ CR38Data |= tempch;
+ xgifb_reg_set(pVBInfo->P3d4, 0x38, CR38Data);
+
+}
+
+static unsigned short XGINew_SenseLCD(struct xgi_hw_device_info
+ *HwDeviceExtension,
+ struct vb_device_info *pVBInfo)
+{
+ unsigned short temp = HwDeviceExtension->ulCRT2LCDType;
+
+ switch (HwDeviceExtension->ulCRT2LCDType) {
+ case LCD_640x480:
+ case LCD_1024x600:
+ case LCD_1152x864:
+ case LCD_1280x960:
+ case LCD_1152x768:
+ case LCD_1920x1440:
+ case LCD_2048x1536:
+ temp = 0; /* overwrite used ulCRT2LCDType */
+ break;
+ case LCD_UNKNOWN: /* unknown lcd, do nothing */
+ return 0;
+ }
+ xgifb_reg_and_or(pVBInfo->P3d4, 0x36, 0xF0, temp);
+ return 1;
+}
+
+static void XGINew_GetXG21Sense(struct pci_dev *pdev,
+ struct vb_device_info *pVBInfo)
+{
+ struct xgifb_video_info *xgifb_info = pci_get_drvdata(pdev);
+ unsigned char Temp;
+
+ if (xgifb_read_vbios(pdev)) { /* For XG21 LVDS */
+ xgifb_reg_or(pVBInfo->P3d4, 0x32, LCDSense);
+ /* LVDS on chip */
+ xgifb_reg_and_or(pVBInfo->P3d4, 0x38, ~0xE0, 0xC0);
+ } else {
+ /* Enable GPIOA/B read */
+ xgifb_reg_and_or(pVBInfo->P3d4, 0x4A, ~0x03, 0x03);
+ Temp = xgifb_reg_get(pVBInfo->P3d4, 0x48) & 0xC0;
+ if (Temp == 0xC0) { /* DVI & DVO GPIOA/B pull high */
+ XGINew_SenseLCD(&xgifb_info->hw_info, pVBInfo);
+ xgifb_reg_or(pVBInfo->P3d4, 0x32, LCDSense);
+ /* Enable read GPIOF */
+ xgifb_reg_and_or(pVBInfo->P3d4, 0x4A, ~0x20, 0x20);
+ if (xgifb_reg_get(pVBInfo->P3d4, 0x48) & 0x04)
+ Temp = 0xA0; /* Only DVO on chip */
+ else
+ Temp = 0x80; /* TMDS on chip */
+ xgifb_reg_and_or(pVBInfo->P3d4, 0x38, ~0xE0, Temp);
+ /* Disable read GPIOF */
+ xgifb_reg_and(pVBInfo->P3d4, 0x4A, ~0x20);
+ }
+ }
+}
+
+static void XGINew_GetXG27Sense(struct vb_device_info *pVBInfo)
+{
+ unsigned char Temp, bCR4A;
+
+ bCR4A = xgifb_reg_get(pVBInfo->P3d4, 0x4A);
+ /* Enable GPIOA/B/C read */
+ xgifb_reg_and_or(pVBInfo->P3d4, 0x4A, ~0x07, 0x07);
+ Temp = xgifb_reg_get(pVBInfo->P3d4, 0x48) & 0x07;
+ xgifb_reg_set(pVBInfo->P3d4, 0x4A, bCR4A);
+
+ if (Temp <= 0x02) {
+ /* LVDS setting */
+ xgifb_reg_and_or(pVBInfo->P3d4, 0x38, ~0xE0, 0xC0);
+ xgifb_reg_set(pVBInfo->P3d4, 0x30, 0x21);
+ } else {
+ /* TMDS/DVO setting */
+ xgifb_reg_and_or(pVBInfo->P3d4, 0x38, ~0xE0, 0xA0);
+ }
+ xgifb_reg_or(pVBInfo->P3d4, 0x32, LCDSense);
+
+}
+
+static unsigned char GetXG21FPBits(struct vb_device_info *pVBInfo)
+{
+ unsigned char CR38, CR4A, temp;
+
+ CR4A = xgifb_reg_get(pVBInfo->P3d4, 0x4A);
+ /* enable GPIOE read */
+ xgifb_reg_and_or(pVBInfo->P3d4, 0x4A, ~0x10, 0x10);
+ CR38 = xgifb_reg_get(pVBInfo->P3d4, 0x38);
+ temp = 0;
+ if ((CR38 & 0xE0) > 0x80) {
+ temp = xgifb_reg_get(pVBInfo->P3d4, 0x48);
+ temp &= 0x08;
+ temp >>= 3;
+ }
+
+ xgifb_reg_set(pVBInfo->P3d4, 0x4A, CR4A);
+
+ return temp;
+}
+
+static unsigned char GetXG27FPBits(struct vb_device_info *pVBInfo)
+{
+ unsigned char CR4A, temp;
+
+ CR4A = xgifb_reg_get(pVBInfo->P3d4, 0x4A);
+ /* enable GPIOA/B/C read */
+ xgifb_reg_and_or(pVBInfo->P3d4, 0x4A, ~0x03, 0x03);
+ temp = xgifb_reg_get(pVBInfo->P3d4, 0x48);
+ if (temp > 2)
+ temp = ((temp & 0x04) >> 1) | ((~temp) & 0x01);
+
+ xgifb_reg_set(pVBInfo->P3d4, 0x4A, CR4A);
+
+ return temp;
+}
+
+static bool xgifb_bridge_is_on(struct vb_device_info *vb_info)
+{
+ u8 flag;
+
+ flag = xgifb_reg_get(vb_info->Part4Port, 0x00);
+ return flag == 1 || flag == 2;
+}
+
+unsigned char XGIInitNew(struct pci_dev *pdev)
+{
+ struct xgifb_video_info *xgifb_info = pci_get_drvdata(pdev);
+ struct xgi_hw_device_info *HwDeviceExtension = &xgifb_info->hw_info;
+ struct vb_device_info VBINF;
+ struct vb_device_info *pVBInfo = &VBINF;
+ unsigned char i, temp = 0, temp1;
+
+ pVBInfo->FBAddr = HwDeviceExtension->pjVideoMemoryAddress;
+
+ if (pVBInfo->FBAddr == NULL) {
+ dev_dbg(&pdev->dev, "pVBInfo->FBAddr == 0\n");
+ return 0;
+ }
+
+ XGIRegInit(pVBInfo, xgifb_info->vga_base);
+
+ outb(0x67, pVBInfo->P3c2);
+
+ InitTo330Pointer(HwDeviceExtension->jChipType, pVBInfo);
+
+ /* Openkey */
+ xgifb_reg_set(pVBInfo->P3c4, 0x05, 0x86);
+
+ /* GetXG21Sense (GPIO) */
+ if (HwDeviceExtension->jChipType == XG21)
+ XGINew_GetXG21Sense(pdev, pVBInfo);
+
+ if (HwDeviceExtension->jChipType == XG27)
+ XGINew_GetXG27Sense(pVBInfo);
+
+ /* Reset Extended register */
+
+ for (i = 0x06; i < 0x20; i++)
+ xgifb_reg_set(pVBInfo->P3c4, i, 0);
+
+ for (i = 0x21; i <= 0x27; i++)
+ xgifb_reg_set(pVBInfo->P3c4, i, 0);
+
+ for (i = 0x31; i <= 0x3B; i++)
+ xgifb_reg_set(pVBInfo->P3c4, i, 0);
+
+ /* Auto over driver for XG42 */
+ if (HwDeviceExtension->jChipType == XG42)
+ xgifb_reg_set(pVBInfo->P3c4, 0x3B, 0xC0);
+
+ for (i = 0x79; i <= 0x7C; i++)
+ xgifb_reg_set(pVBInfo->P3d4, i, 0);
+
+ if (HwDeviceExtension->jChipType >= XG20)
+ xgifb_reg_set(pVBInfo->P3d4, 0x97, pVBInfo->XGINew_CR97);
+
+ /* SetDefExt1Regs begin */
+ xgifb_reg_set(pVBInfo->P3c4, 0x07, XGI330_SR07);
+ if (HwDeviceExtension->jChipType == XG27) {
+ xgifb_reg_set(pVBInfo->P3c4, 0x40, XG27_SR40);
+ xgifb_reg_set(pVBInfo->P3c4, 0x41, XG27_SR41);
+ }
+ xgifb_reg_set(pVBInfo->P3c4, 0x11, 0x0F);
+ xgifb_reg_set(pVBInfo->P3c4, 0x1F, XGI330_SR1F);
+ /* Frame buffer can read/write SR20 */
+ xgifb_reg_set(pVBInfo->P3c4, 0x20, 0xA0);
+ /* H/W request for slow corner chip */
+ xgifb_reg_set(pVBInfo->P3c4, 0x36, 0x70);
+ if (HwDeviceExtension->jChipType == XG27)
+ xgifb_reg_set(pVBInfo->P3c4, 0x36, XG27_SR36);
+
+ if (HwDeviceExtension->jChipType < XG20) {
+ u32 Temp;
+
+ /* Set AGP customize registers (in SetDefAGPRegs) Start */
+ for (i = 0x47; i <= 0x4C; i++)
+ xgifb_reg_set(pVBInfo->P3d4,
+ i,
+ XGI340_AGPReg[i - 0x47]);
+
+ for (i = 0x70; i <= 0x71; i++)
+ xgifb_reg_set(pVBInfo->P3d4,
+ i,
+ XGI340_AGPReg[6 + i - 0x70]);
+
+ for (i = 0x74; i <= 0x77; i++)
+ xgifb_reg_set(pVBInfo->P3d4,
+ i,
+ XGI340_AGPReg[8 + i - 0x74]);
+
+ pci_read_config_dword(pdev, 0x50, &Temp);
+ Temp >>= 20;
+ Temp &= 0xF;
+
+ if (Temp == 1)
+ xgifb_reg_set(pVBInfo->P3d4, 0x48, 0x20); /* CR48 */
+ } /* != XG20 */
+
+ /* Set PCI */
+ xgifb_reg_set(pVBInfo->P3c4, 0x23, XGI330_SR23);
+ xgifb_reg_set(pVBInfo->P3c4, 0x24, XGI330_SR24);
+ xgifb_reg_set(pVBInfo->P3c4, 0x25, 0);
+
+ if (HwDeviceExtension->jChipType < XG20) {
+ /* Set VB */
+ XGI_UnLockCRT2(pVBInfo);
+ /* disable VideoCapture */
+ xgifb_reg_and_or(pVBInfo->Part0Port, 0x3F, 0xEF, 0x00);
+ xgifb_reg_set(pVBInfo->Part1Port, 0x00, 0x00);
+ /* chk if BCLK>=100MHz */
+ temp1 = xgifb_reg_get(pVBInfo->P3d4, 0x7B);
+
+ xgifb_reg_set(pVBInfo->Part1Port,
+ 0x02, XGI330_CRT2Data_1_2);
+
+ xgifb_reg_set(pVBInfo->Part1Port, 0x2E, 0x08); /* use VB */
+ } /* != XG20 */
+
+ xgifb_reg_set(pVBInfo->P3c4, 0x27, 0x1F);
+
+ if ((HwDeviceExtension->jChipType == XG42) &&
+ XGINew_GetXG20DRAMType(HwDeviceExtension, pVBInfo) != 0) {
+ /* Not DDR */
+ xgifb_reg_set(pVBInfo->P3c4,
+ 0x31,
+ (XGI330_SR31 & 0x3F) | 0x40);
+ xgifb_reg_set(pVBInfo->P3c4,
+ 0x32,
+ (XGI330_SR32 & 0xFC) | 0x01);
+ } else {
+ xgifb_reg_set(pVBInfo->P3c4, 0x31, XGI330_SR31);
+ xgifb_reg_set(pVBInfo->P3c4, 0x32, XGI330_SR32);
+ }
+ xgifb_reg_set(pVBInfo->P3c4, 0x33, XGI330_SR33);
+
+ if (HwDeviceExtension->jChipType < XG20) {
+ if (xgifb_bridge_is_on(pVBInfo)) {
+ xgifb_reg_set(pVBInfo->Part2Port, 0x00, 0x1C);
+ xgifb_reg_set(pVBInfo->Part4Port,
+ 0x0D, XGI330_CRT2Data_4_D);
+ xgifb_reg_set(pVBInfo->Part4Port,
+ 0x0E, XGI330_CRT2Data_4_E);
+ xgifb_reg_set(pVBInfo->Part4Port,
+ 0x10, XGI330_CRT2Data_4_10);
+ xgifb_reg_set(pVBInfo->Part4Port, 0x0F, 0x3F);
+ XGI_LockCRT2(pVBInfo);
+ }
+ } /* != XG20 */
+
+ XGI_SenseCRT1(pVBInfo);
+
+ if (HwDeviceExtension->jChipType == XG21) {
+
+ xgifb_reg_and_or(pVBInfo->P3d4,
+ 0x32,
+ ~Monitor1Sense,
+ Monitor1Sense); /* Z9 default has CRT */
+ temp = GetXG21FPBits(pVBInfo);
+ xgifb_reg_and_or(pVBInfo->P3d4, 0x37, ~0x01, temp);
+
+ }
+ if (HwDeviceExtension->jChipType == XG27) {
+ xgifb_reg_and_or(pVBInfo->P3d4,
+ 0x32,
+ ~Monitor1Sense,
+ Monitor1Sense); /* Z9 default has CRT */
+ temp = GetXG27FPBits(pVBInfo);
+ xgifb_reg_and_or(pVBInfo->P3d4, 0x37, ~0x03, temp);
+ }
+
+ pVBInfo->ram_type = XGINew_GetXG20DRAMType(HwDeviceExtension, pVBInfo);
+
+ XGINew_SetDRAMDefaultRegister340(HwDeviceExtension,
+ pVBInfo->P3d4,
+ pVBInfo);
+
+ XGINew_SetDRAMSize_340(xgifb_info, HwDeviceExtension, pVBInfo);
+
+ xgifb_reg_set(pVBInfo->P3c4, 0x22, 0xfa);
+ xgifb_reg_set(pVBInfo->P3c4, 0x21, 0xa3);
+
+ XGINew_ChkSenseStatus(pVBInfo);
+ XGINew_SetModeScratch(pVBInfo);
+
+ xgifb_reg_set(pVBInfo->P3d4, 0x8c, 0x87);
+
+ return 1;
+} /* end of init */
diff --git a/drivers/staging/xgifb/vb_init.h b/drivers/staging/xgifb/vb_init.h
new file mode 100644
index 000000000..24573026a
--- /dev/null
+++ b/drivers/staging/xgifb/vb_init.h
@@ -0,0 +1,6 @@
+#ifndef _VBINIT_
+#define _VBINIT_
+extern unsigned char XGIInitNew(struct pci_dev *pdev);
+extern void XGIRegInit(struct vb_device_info *, unsigned long);
+#endif
+
diff --git a/drivers/staging/xgifb/vb_setmode.c b/drivers/staging/xgifb/vb_setmode.c
new file mode 100644
index 000000000..a47395e92
--- /dev/null
+++ b/drivers/staging/xgifb/vb_setmode.c
@@ -0,0 +1,5554 @@
+#include <linux/delay.h>
+#include "XGIfb.h"
+
+#include "vb_def.h"
+#include "vb_init.h"
+#include "vb_util.h"
+#include "vb_table.h"
+#include "vb_setmode.h"
+
+#define IndexMask 0xff
+#define TVCLKBASE_315_25 (TVCLKBASE_315 + 25)
+
+static const unsigned short XGINew_VGA_DAC[] = {
+ 0x00, 0x10, 0x04, 0x14, 0x01, 0x11, 0x09, 0x15,
+ 0x2A, 0x3A, 0x2E, 0x3E, 0x2B, 0x3B, 0x2F, 0x3F,
+ 0x00, 0x05, 0x08, 0x0B, 0x0E, 0x11, 0x14, 0x18,
+ 0x1C, 0x20, 0x24, 0x28, 0x2D, 0x32, 0x38, 0x3F,
+ 0x00, 0x10, 0x1F, 0x2F, 0x3F, 0x1F, 0x27, 0x2F,
+ 0x37, 0x3F, 0x2D, 0x31, 0x36, 0x3A, 0x3F, 0x00,
+ 0x07, 0x0E, 0x15, 0x1C, 0x0E, 0x11, 0x15, 0x18,
+ 0x1C, 0x14, 0x16, 0x18, 0x1A, 0x1C, 0x00, 0x04,
+ 0x08, 0x0C, 0x10, 0x08, 0x0A, 0x0C, 0x0E, 0x10,
+ 0x0B, 0x0C, 0x0D, 0x0F, 0x10};
+
+void InitTo330Pointer(unsigned char ChipType, struct vb_device_info *pVBInfo)
+{
+ pVBInfo->MCLKData = XGI340New_MCLKData;
+
+ pVBInfo->LCDResInfo = 0;
+ pVBInfo->LCDTypeInfo = 0;
+ pVBInfo->LCDInfo = 0;
+ pVBInfo->VBInfo = 0;
+ pVBInfo->TVInfo = 0;
+
+ pVBInfo->SR18 = XGI340_SR18;
+ pVBInfo->CR40 = XGI340_cr41;
+
+ if (ChipType < XG20)
+ XGI_GetVBType(pVBInfo);
+
+ /* 310 customization related */
+ if ((pVBInfo->VBType & VB_SIS301LV) || (pVBInfo->VBType & VB_SIS302LV))
+ pVBInfo->LCDCapList = XGI_LCDDLCapList;
+ else
+ pVBInfo->LCDCapList = XGI_LCDCapList;
+
+ if (ChipType >= XG20)
+ pVBInfo->XGINew_CR97 = 0x10;
+
+ if (ChipType == XG27) {
+ unsigned char temp;
+
+ pVBInfo->MCLKData = XGI27New_MCLKData;
+ pVBInfo->CR40 = XGI27_cr41;
+ pVBInfo->XGINew_CR97 = 0xc1;
+ pVBInfo->SR18 = XG27_SR18;
+
+ /*Z11m DDR*/
+ temp = xgifb_reg_get(pVBInfo->P3c4, 0x3B);
+ /* SR3B[7][3]MAA15 MAA11 (Power on Trapping) */
+ if (((temp & 0x88) == 0x80) || ((temp & 0x88) == 0x08))
+ pVBInfo->XGINew_CR97 = 0x80;
+ }
+
+}
+
+static void XGI_SetSeqRegs(struct vb_device_info *pVBInfo)
+{
+ unsigned char SRdata, i;
+
+ xgifb_reg_set(pVBInfo->P3c4, 0x00, 0x03); /* Set SR0 */
+
+ for (i = 0; i < 4; i++) {
+ /* Get SR1,2,3,4 from file */
+ /* SR1 is with screen off 0x20 */
+ SRdata = XGI330_StandTable.SR[i];
+ xgifb_reg_set(pVBInfo->P3c4, i+1, SRdata); /* Set SR 1 2 3 4 */
+ }
+}
+
+static void XGI_SetCRTCRegs(struct vb_device_info *pVBInfo)
+{
+ unsigned char CRTCdata;
+ unsigned short i;
+
+ CRTCdata = xgifb_reg_get(pVBInfo->P3d4, 0x11);
+ CRTCdata &= 0x7f;
+ xgifb_reg_set(pVBInfo->P3d4, 0x11, CRTCdata); /* Unlock CRTC */
+
+ for (i = 0; i <= 0x18; i++) {
+ /* Get CRTC from file */
+ CRTCdata = XGI330_StandTable.CRTC[i];
+ xgifb_reg_set(pVBInfo->P3d4, i, CRTCdata); /* Set CRTC(3d4) */
+ }
+}
+
+static void XGI_SetATTRegs(unsigned short ModeIdIndex,
+ struct vb_device_info *pVBInfo)
+{
+ unsigned char ARdata;
+ unsigned short i, modeflag;
+
+ modeflag = XGI330_EModeIDTable[ModeIdIndex].Ext_ModeFlag;
+
+ for (i = 0; i <= 0x13; i++) {
+ ARdata = XGI330_StandTable.ATTR[i];
+
+ if ((modeflag & Charx8Dot) && i == 0x13) { /* ifndef Dot9 */
+ if (pVBInfo->VBInfo & XGI_SetCRT2ToLCDA) {
+ ARdata = 0;
+ } else if ((pVBInfo->VBInfo &
+ (SetCRT2ToTV | SetCRT2ToLCD)) &&
+ (pVBInfo->VBInfo & SetInSlaveMode)) {
+ ARdata = 0;
+ }
+ }
+
+ inb(pVBInfo->P3da); /* reset 3da */
+ outb(i, pVBInfo->P3c0); /* set index */
+ outb(ARdata, pVBInfo->P3c0); /* set data */
+ }
+
+ inb(pVBInfo->P3da); /* reset 3da */
+ outb(0x14, pVBInfo->P3c0); /* set index */
+ outb(0x00, pVBInfo->P3c0); /* set data */
+ inb(pVBInfo->P3da); /* Enable Attribute */
+ outb(0x20, pVBInfo->P3c0);
+}
+
+static void XGI_SetGRCRegs(struct vb_device_info *pVBInfo)
+{
+ unsigned char GRdata;
+ unsigned short i;
+
+ for (i = 0; i <= 0x08; i++) {
+ /* Get GR from file */
+ GRdata = XGI330_StandTable.GRC[i];
+ xgifb_reg_set(pVBInfo->P3ce, i, GRdata); /* Set GR(3ce) */
+ }
+
+ if (pVBInfo->ModeType > ModeVGA) {
+ GRdata = xgifb_reg_get(pVBInfo->P3ce, 0x05);
+ GRdata &= 0xBF; /* 256 color disable */
+ xgifb_reg_set(pVBInfo->P3ce, 0x05, GRdata);
+ }
+}
+
+static void XGI_ClearExt1Regs(struct vb_device_info *pVBInfo)
+{
+ unsigned short i;
+
+ for (i = 0x0A; i <= 0x0E; i++)
+ xgifb_reg_set(pVBInfo->P3c4, i, 0x00); /* Clear SR0A-SR0E */
+}
+
+static unsigned char XGI_SetDefaultVCLK(struct vb_device_info *pVBInfo)
+{
+
+ xgifb_reg_and_or(pVBInfo->P3c4, 0x31, ~0x30, 0x20);
+ xgifb_reg_set(pVBInfo->P3c4, 0x2B, XGI_VCLKData[0].SR2B);
+ xgifb_reg_set(pVBInfo->P3c4, 0x2C, XGI_VCLKData[0].SR2C);
+
+ xgifb_reg_and_or(pVBInfo->P3c4, 0x31, ~0x30, 0x10);
+ xgifb_reg_set(pVBInfo->P3c4, 0x2B, XGI_VCLKData[1].SR2B);
+ xgifb_reg_set(pVBInfo->P3c4, 0x2C, XGI_VCLKData[1].SR2C);
+
+ xgifb_reg_and(pVBInfo->P3c4, 0x31, ~0x30);
+ return 0;
+}
+
+static unsigned char XGI_AjustCRT2Rate(unsigned short ModeIdIndex,
+ unsigned short RefreshRateTableIndex, unsigned short *i,
+ struct vb_device_info *pVBInfo)
+{
+ unsigned short tempax, tempbx, resinfo, modeflag, infoflag;
+
+ modeflag = XGI330_EModeIDTable[ModeIdIndex].Ext_ModeFlag;
+ resinfo = XGI330_EModeIDTable[ModeIdIndex].Ext_RESINFO;
+ tempbx = XGI330_RefIndex[RefreshRateTableIndex + (*i)].ModeID;
+ tempax = 0;
+
+ if (pVBInfo->VBInfo & SetCRT2ToRAMDAC) {
+ tempax |= SupportRAMDAC2;
+
+ if (pVBInfo->VBType & VB_XGI301C)
+ tempax |= SupportCRT2in301C;
+ }
+
+ /* 301b */
+ if (pVBInfo->VBInfo & (SetCRT2ToLCD | XGI_SetCRT2ToLCDA)) {
+ tempax |= SupportLCD;
+
+ if (pVBInfo->LCDResInfo != Panel_1280x1024 &&
+ pVBInfo->LCDResInfo != Panel_1280x960 &&
+ (pVBInfo->LCDInfo & LCDNonExpanding) &&
+ resinfo >= 9)
+ return 0;
+ }
+
+ if (pVBInfo->VBInfo & SetCRT2ToHiVision) { /* for HiTV */
+ tempax |= SupportHiVision;
+ if ((pVBInfo->VBInfo & SetInSlaveMode) &&
+ ((resinfo == 4) ||
+ (resinfo == 3 && (pVBInfo->SetFlag & TVSimuMode)) ||
+ (resinfo > 7)))
+ return 0;
+ } else if (pVBInfo->VBInfo & (SetCRT2ToAVIDEO | SetCRT2ToSVIDEO |
+ SetCRT2ToSCART | SetCRT2ToYPbPr525750 |
+ SetCRT2ToHiVision)) {
+ tempax |= SupportTV;
+
+ if (pVBInfo->VBType & (VB_SIS301B | VB_SIS302B | VB_SIS301LV |
+ VB_SIS302LV | VB_XGI301C))
+ tempax |= SupportTV1024;
+
+ if (!(pVBInfo->VBInfo & TVSetPAL) &&
+ (modeflag & NoSupportSimuTV) &&
+ (pVBInfo->VBInfo & SetInSlaveMode) &&
+ (!(pVBInfo->VBInfo & SetNotSimuMode)))
+ return 0;
+ }
+
+ for (; XGI330_RefIndex[RefreshRateTableIndex + (*i)].ModeID ==
+ tempbx; (*i)--) {
+ infoflag = XGI330_RefIndex[RefreshRateTableIndex + (*i)].
+ Ext_InfoFlag;
+ if (infoflag & tempax)
+ return 1;
+
+ if ((*i) == 0)
+ break;
+ }
+
+ for ((*i) = 0;; (*i)++) {
+ infoflag = XGI330_RefIndex[RefreshRateTableIndex + (*i)].
+ Ext_InfoFlag;
+ if (XGI330_RefIndex[RefreshRateTableIndex + (*i)].ModeID
+ != tempbx) {
+ return 0;
+ }
+
+ if (infoflag & tempax)
+ return 1;
+ }
+ return 1;
+}
+
+static void XGI_SetSync(unsigned short RefreshRateTableIndex,
+ struct vb_device_info *pVBInfo)
+{
+ unsigned short sync, temp;
+
+ /* di+0x00 */
+ sync = XGI330_RefIndex[RefreshRateTableIndex].Ext_InfoFlag >> 8;
+ sync &= 0xC0;
+ temp = 0x2F;
+ temp |= sync;
+ outb(temp, pVBInfo->P3c2); /* Set Misc(3c2) */
+}
+
+static void XGI_SetCRT1Timing_H(struct vb_device_info *pVBInfo,
+ struct xgi_hw_device_info *HwDeviceExtension)
+{
+ unsigned char data, data1, pushax;
+ unsigned short i, j;
+
+ /* unlock cr0-7 */
+ data = xgifb_reg_get(pVBInfo->P3d4, 0x11);
+ data &= 0x7F;
+ xgifb_reg_set(pVBInfo->P3d4, 0x11, data);
+
+ data = pVBInfo->TimingH.data[0];
+ xgifb_reg_set(pVBInfo->P3d4, 0, data);
+
+ for (i = 0x01; i <= 0x04; i++) {
+ data = pVBInfo->TimingH.data[i];
+ xgifb_reg_set(pVBInfo->P3d4, (unsigned short) (i + 1), data);
+ }
+
+ for (i = 0x05; i <= 0x06; i++) {
+ data = pVBInfo->TimingH.data[i];
+ xgifb_reg_set(pVBInfo->P3c4, (unsigned short) (i + 6), data);
+ }
+
+ j = xgifb_reg_get(pVBInfo->P3c4, 0x0e);
+ j &= 0x1F;
+ data = pVBInfo->TimingH.data[7];
+ data &= 0xE0;
+ data |= j;
+ xgifb_reg_set(pVBInfo->P3c4, 0x0e, data);
+
+ if (HwDeviceExtension->jChipType >= XG20) {
+ data = xgifb_reg_get(pVBInfo->P3d4, 0x04);
+ data = data - 1;
+ xgifb_reg_set(pVBInfo->P3d4, 0x04, data);
+ data = xgifb_reg_get(pVBInfo->P3d4, 0x05);
+ data1 = data;
+ data1 &= 0xE0;
+ data &= 0x1F;
+ if (data == 0) {
+ pushax = data;
+ data = xgifb_reg_get(pVBInfo->P3c4, 0x0c);
+ data &= 0xFB;
+ xgifb_reg_set(pVBInfo->P3c4, 0x0c, data);
+ data = pushax;
+ }
+ data = data - 1;
+ data |= data1;
+ xgifb_reg_set(pVBInfo->P3d4, 0x05, data);
+ data = xgifb_reg_get(pVBInfo->P3c4, 0x0e);
+ data >>= 5;
+ data = data + 3;
+ if (data > 7)
+ data = data - 7;
+ data <<= 5;
+ xgifb_reg_and_or(pVBInfo->P3c4, 0x0e, ~0xE0, data);
+ }
+}
+
+static void XGI_SetCRT1Timing_V(unsigned short ModeIdIndex,
+ struct vb_device_info *pVBInfo)
+{
+ unsigned char data;
+ unsigned short i, j;
+
+ for (i = 0x00; i <= 0x01; i++) {
+ data = pVBInfo->TimingV.data[i];
+ xgifb_reg_set(pVBInfo->P3d4, (unsigned short) (i + 6), data);
+ }
+
+ for (i = 0x02; i <= 0x03; i++) {
+ data = pVBInfo->TimingV.data[i];
+ xgifb_reg_set(pVBInfo->P3d4, (unsigned short) (i + 0x0e), data);
+ }
+
+ for (i = 0x04; i <= 0x05; i++) {
+ data = pVBInfo->TimingV.data[i];
+ xgifb_reg_set(pVBInfo->P3d4, (unsigned short) (i + 0x11), data);
+ }
+
+ j = xgifb_reg_get(pVBInfo->P3c4, 0x0a);
+ j &= 0xC0;
+ data = pVBInfo->TimingV.data[6];
+ data &= 0x3F;
+ data |= j;
+ xgifb_reg_set(pVBInfo->P3c4, 0x0a, data);
+
+ data = pVBInfo->TimingV.data[6];
+ data &= 0x80;
+ data >>= 2;
+
+ i = XGI330_EModeIDTable[ModeIdIndex].Ext_ModeFlag;
+ i &= DoubleScanMode;
+ if (i)
+ data |= 0x80;
+
+ j = xgifb_reg_get(pVBInfo->P3d4, 0x09);
+ j &= 0x5F;
+ data |= j;
+ xgifb_reg_set(pVBInfo->P3d4, 0x09, data);
+}
+
+static void XGI_SetCRT1CRTC(unsigned short ModeIdIndex,
+ unsigned short RefreshRateTableIndex,
+ struct vb_device_info *pVBInfo,
+ struct xgi_hw_device_info *HwDeviceExtension)
+{
+ unsigned char index, data;
+ unsigned short i;
+
+ /* Get index */
+ index = XGI330_RefIndex[RefreshRateTableIndex].Ext_CRT1CRTC;
+ index = index & IndexMask;
+
+ data = xgifb_reg_get(pVBInfo->P3d4, 0x11);
+ data &= 0x7F;
+ xgifb_reg_set(pVBInfo->P3d4, 0x11, data); /* Unlock CRTC */
+
+ for (i = 0; i < 8; i++)
+ pVBInfo->TimingH.data[i]
+ = XGI_CRT1Table[index].CR[i];
+
+ for (i = 0; i < 7; i++)
+ pVBInfo->TimingV.data[i]
+ = XGI_CRT1Table[index].CR[i + 8];
+
+ XGI_SetCRT1Timing_H(pVBInfo, HwDeviceExtension);
+
+ XGI_SetCRT1Timing_V(ModeIdIndex, pVBInfo);
+
+ if (pVBInfo->ModeType > 0x03)
+ xgifb_reg_set(pVBInfo->P3d4, 0x14, 0x4F);
+}
+
+/* --------------------------------------------------------------------- */
+/* Function : XGI_SetXG21CRTC */
+/* Input : Stand or enhance CRTC table */
+/* Output : Fill CRT Hsync/Vsync to SR2E/SR2F/SR30/SR33/SR34/SR3F */
+/* Description : Set LCD timing */
+/* --------------------------------------------------------------------- */
+static void XGI_SetXG21CRTC(unsigned short RefreshRateTableIndex,
+ struct vb_device_info *pVBInfo)
+{
+ unsigned char index, Tempax, Tempbx, Tempcx, Tempdx;
+ unsigned short Temp1, Temp2, Temp3;
+
+ index = XGI330_RefIndex[RefreshRateTableIndex].Ext_CRT1CRTC;
+ /* Tempax: CR4 HRS */
+ Tempax = XGI_CRT1Table[index].CR[3];
+ Tempcx = Tempax; /* Tempcx: HRS */
+ /* SR2E[7:0]->HRS */
+ xgifb_reg_set(pVBInfo->P3c4, 0x2E, Tempax);
+
+ Tempdx = XGI_CRT1Table[index].CR[5]; /* SRB */
+ Tempdx &= 0xC0; /* Tempdx[7:6]: SRB[7:6] */
+ Temp1 = Tempdx; /* Temp1[7:6]: HRS[9:8] */
+ Temp1 <<= 2; /* Temp1[9:8]: HRS[9:8] */
+ Temp1 |= Tempax; /* Temp1[9:0]: HRS[9:0] */
+
+ Tempax = XGI_CRT1Table[index].CR[4]; /* CR5 HRE */
+ Tempax &= 0x1F; /* Tempax[4:0]: HRE[4:0] */
+
+ Tempbx = XGI_CRT1Table[index].CR[6]; /* SRC */
+ Tempbx &= 0x04; /* Tempbx[2]: HRE[5] */
+ Tempbx <<= 3; /* Tempbx[5]: HRE[5] */
+ Tempax |= Tempbx; /* Tempax[5:0]: HRE[5:0] */
+
+ Temp2 = Temp1 & 0x3C0; /* Temp2[9:6]: HRS[9:6] */
+ Temp2 |= Tempax; /* Temp2[9:0]: HRE[9:0] */
+
+ Tempcx &= 0x3F; /* Tempcx[5:0]: HRS[5:0] */
+ if (Tempax < Tempcx) /* HRE < HRS */
+ Temp2 |= 0x40; /* Temp2 + 0x40 */
+
+ Temp2 &= 0xFF;
+ Tempax = (unsigned char) Temp2; /* Tempax: HRE[7:0] */
+ Tempax <<= 2; /* Tempax[7:2]: HRE[5:0] */
+ Tempdx >>= 6; /* Tempdx[7:6]->[1:0] HRS[9:8] */
+ Tempax |= Tempdx; /* HRE[5:0]HRS[9:8] */
+ /* SR2F D[7:2]->HRE, D[1:0]->HRS */
+ xgifb_reg_set(pVBInfo->P3c4, 0x2F, Tempax);
+ xgifb_reg_and_or(pVBInfo->P3c4, 0x30, 0xE3, 00);
+
+ /* CR10 VRS */
+ Tempax = XGI_CRT1Table[index].CR[10];
+ Tempbx = Tempax; /* Tempbx: VRS */
+ Tempax &= 0x01; /* Tempax[0]: VRS[0] */
+ xgifb_reg_or(pVBInfo->P3c4, 0x33, Tempax); /* SR33[0]->VRS[0] */
+ /* CR7[2][7] VRE */
+ Tempax = XGI_CRT1Table[index].CR[9];
+ Tempcx = Tempbx >> 1; /* Tempcx[6:0]: VRS[7:1] */
+ Tempdx = Tempax & 0x04; /* Tempdx[2]: CR7[2] */
+ Tempdx <<= 5; /* Tempdx[7]: VRS[8] */
+ Tempcx |= Tempdx; /* Tempcx[7:0]: VRS[8:1] */
+ xgifb_reg_set(pVBInfo->P3c4, 0x34, Tempcx); /* SR34[8:1]->VRS */
+
+ Temp1 = Tempdx; /* Temp1[7]: Tempdx[7] */
+ Temp1 <<= 1; /* Temp1[8]: VRS[8] */
+ Temp1 |= Tempbx; /* Temp1[8:0]: VRS[8:0] */
+ Tempax &= 0x80;
+ Temp2 = Tempax << 2; /* Temp2[9]: VRS[9] */
+ Temp1 |= Temp2; /* Temp1[9:0]: VRS[9:0] */
+ /* Tempax: SRA */
+ Tempax = XGI_CRT1Table[index].CR[14];
+ Tempax &= 0x08; /* Tempax[3]: VRS[3] */
+ Temp2 = Tempax;
+ Temp2 <<= 7; /* Temp2[10]: VRS[10] */
+ Temp1 |= Temp2; /* Temp1[10:0]: VRS[10:0] */
+
+ /* Tempax: CR11 VRE */
+ Tempax = XGI_CRT1Table[index].CR[11];
+ Tempax &= 0x0F; /* Tempax[3:0]: VRE[3:0] */
+ /* Tempbx: SRA */
+ Tempbx = XGI_CRT1Table[index].CR[14];
+ Tempbx &= 0x20; /* Tempbx[5]: VRE[5] */
+ Tempbx >>= 1; /* Tempbx[4]: VRE[4] */
+ Tempax |= Tempbx; /* Tempax[4:0]: VRE[4:0] */
+ Temp2 = Temp1 & 0x7E0; /* Temp2[10:5]: VRS[10:5] */
+ Temp2 |= Tempax; /* Temp2[10:5]: VRE[10:5] */
+
+ Temp3 = Temp1 & 0x1F; /* Temp3[4:0]: VRS[4:0] */
+ if (Tempax < Temp3) /* VRE < VRS */
+ Temp2 |= 0x20; /* VRE + 0x20 */
+
+ Temp2 &= 0xFF;
+ Tempax = (unsigned char) Temp2; /* Tempax: VRE[7:0] */
+ Tempax <<= 2; /* Tempax[7:0]; VRE[5:0]00 */
+ Temp1 &= 0x600; /* Temp1[10:9]: VRS[10:9] */
+ Temp1 >>= 9; /* Temp1[1:0]: VRS[10:9] */
+ Tempbx = (unsigned char) Temp1;
+ Tempax |= Tempbx; /* Tempax[7:0]: VRE[5:0]VRS[10:9] */
+ Tempax &= 0x7F;
+ /* SR3F D[7:2]->VRE D[1:0]->VRS */
+ xgifb_reg_set(pVBInfo->P3c4, 0x3F, Tempax);
+}
+
+static void XGI_SetXG27CRTC(unsigned short RefreshRateTableIndex,
+ struct vb_device_info *pVBInfo)
+{
+ unsigned short index, Tempax, Tempbx, Tempcx;
+
+ index = XGI330_RefIndex[RefreshRateTableIndex].Ext_CRT1CRTC;
+ /* Tempax: CR4 HRS */
+ Tempax = XGI_CRT1Table[index].CR[3];
+ Tempbx = Tempax; /* Tempbx: HRS[7:0] */
+ /* SR2E[7:0]->HRS */
+ xgifb_reg_set(pVBInfo->P3c4, 0x2E, Tempax);
+
+ /* SR0B */
+ Tempax = XGI_CRT1Table[index].CR[5];
+ Tempax &= 0xC0; /* Tempax[7:6]: SR0B[7:6]: HRS[9:8]*/
+ Tempbx |= (Tempax << 2); /* Tempbx: HRS[9:0] */
+
+ Tempax = XGI_CRT1Table[index].CR[4]; /* CR5 HRE */
+ Tempax &= 0x1F; /* Tempax[4:0]: HRE[4:0] */
+ Tempcx = Tempax; /* Tempcx: HRE[4:0] */
+
+ Tempax = XGI_CRT1Table[index].CR[6]; /* SRC */
+ Tempax &= 0x04; /* Tempax[2]: HRE[5] */
+ Tempax <<= 3; /* Tempax[5]: HRE[5] */
+ Tempcx |= Tempax; /* Tempcx[5:0]: HRE[5:0] */
+
+ Tempbx = Tempbx & 0x3C0; /* Tempbx[9:6]: HRS[9:6] */
+ Tempbx |= Tempcx; /* Tempbx: HRS[9:6]HRE[5:0] */
+
+ /* Tempax: CR4 HRS */
+ Tempax = XGI_CRT1Table[index].CR[3];
+ Tempax &= 0x3F; /* Tempax: HRS[5:0] */
+ if (Tempcx <= Tempax) /* HRE[5:0] < HRS[5:0] */
+ Tempbx += 0x40; /* Tempbx= Tempbx + 0x40 : HRE[9:0]*/
+
+ Tempax = XGI_CRT1Table[index].CR[5]; /* SR0B */
+ Tempax &= 0xC0; /* Tempax[7:6]: SR0B[7:6]: HRS[9:8]*/
+ Tempax >>= 6; /* Tempax[1:0]: HRS[9:8]*/
+ Tempax |= ((Tempbx << 2) & 0xFF); /* Tempax[7:2]: HRE[5:0] */
+ /* SR2F [7:2][1:0]: HRE[5:0]HRS[9:8] */
+ xgifb_reg_set(pVBInfo->P3c4, 0x2F, Tempax);
+ xgifb_reg_and_or(pVBInfo->P3c4, 0x30, 0xE3, 00);
+
+ /* CR10 VRS */
+ Tempax = XGI_CRT1Table[index].CR[10];
+ /* SR34[7:0]->VRS[7:0] */
+ xgifb_reg_set(pVBInfo->P3c4, 0x34, Tempax);
+
+ Tempcx = Tempax; /* Tempcx <= VRS[7:0] */
+ /* CR7[7][2] VRS[9][8] */
+ Tempax = XGI_CRT1Table[index].CR[9];
+ Tempbx = Tempax; /* Tempbx <= CR07[7:0] */
+ Tempax = Tempax & 0x04; /* Tempax[2]: CR7[2]: VRS[8] */
+ Tempax >>= 2; /* Tempax[0]: VRS[8] */
+ /* SR35[0]: VRS[8] */
+ xgifb_reg_and_or(pVBInfo->P3c4, 0x35, ~0x01, Tempax);
+ Tempcx |= (Tempax << 8); /* Tempcx <= VRS[8:0] */
+ Tempcx |= ((Tempbx & 0x80) << 2); /* Tempcx <= VRS[9:0] */
+ /* Tempax: SR0A */
+ Tempax = XGI_CRT1Table[index].CR[14];
+ Tempax &= 0x08; /* SR0A[3] VRS[10] */
+ Tempcx |= (Tempax << 7); /* Tempcx <= VRS[10:0] */
+
+ /* Tempax: CR11 VRE */
+ Tempax = XGI_CRT1Table[index].CR[11];
+ Tempax &= 0x0F; /* Tempax[3:0]: VRE[3:0] */
+ /* Tempbx: SR0A */
+ Tempbx = XGI_CRT1Table[index].CR[14];
+ Tempbx &= 0x20; /* Tempbx[5]: SR0A[5]: VRE[4] */
+ Tempbx >>= 1; /* Tempbx[4]: VRE[4] */
+ Tempax |= Tempbx; /* Tempax[4:0]: VRE[4:0] */
+ Tempbx = Tempcx; /* Tempbx: VRS[10:0] */
+ Tempbx &= 0x7E0; /* Tempbx[10:5]: VRS[10:5] */
+ Tempbx |= Tempax; /* Tempbx: VRS[10:5]VRE[4:0] */
+
+ if (Tempbx <= Tempcx) /* VRE <= VRS */
+ Tempbx |= 0x20; /* VRE + 0x20 */
+
+ /* Tempax: Tempax[7:0]; VRE[5:0]00 */
+ Tempax = (Tempbx << 2) & 0xFF;
+ /* SR3F[7:2]:VRE[5:0] */
+ xgifb_reg_and_or(pVBInfo->P3c4, 0x3F, ~0xFC, Tempax);
+ Tempax = Tempcx >> 8;
+ /* SR35[2:0]:VRS[10:8] */
+ xgifb_reg_and_or(pVBInfo->P3c4, 0x35, ~0x07, Tempax);
+}
+
+static void XGI_SetXG27FPBits(struct vb_device_info *pVBInfo)
+{
+ unsigned char temp;
+
+ /* D[1:0] 01: 18bit, 00: dual 12, 10: single 24 */
+ temp = xgifb_reg_get(pVBInfo->P3d4, 0x37);
+ temp = (temp & 3) << 6;
+ /* SR06[7]0: dual 12/1: single 24 [6] 18bit Dither <= 0 h/w recommend */
+ xgifb_reg_and_or(pVBInfo->P3c4, 0x06, ~0xc0, temp & 0x80);
+ /* SR09[7] enable FP output, SR09[6] 1: sigle 18bits, 0: 24bits */
+ xgifb_reg_and_or(pVBInfo->P3c4, 0x09, ~0xc0, temp | 0x80);
+
+}
+
+static void xgifb_set_lcd(int chip_id,
+ struct vb_device_info *pVBInfo,
+ unsigned short RefreshRateTableIndex)
+{
+ unsigned short temp;
+
+ xgifb_reg_set(pVBInfo->P3d4, 0x2E, 0x00);
+ xgifb_reg_set(pVBInfo->P3d4, 0x2F, 0x00);
+ xgifb_reg_set(pVBInfo->P3d4, 0x46, 0x00);
+ xgifb_reg_set(pVBInfo->P3d4, 0x47, 0x00);
+
+ if (chip_id == XG27) {
+ temp = xgifb_reg_get(pVBInfo->P3d4, 0x37);
+ if ((temp & 0x03) == 0) { /* dual 12 */
+ xgifb_reg_set(pVBInfo->P3d4, 0x46, 0x13);
+ xgifb_reg_set(pVBInfo->P3d4, 0x47, 0x13);
+ }
+ }
+
+ if (chip_id == XG27) {
+ XGI_SetXG27FPBits(pVBInfo);
+ } else {
+ temp = xgifb_reg_get(pVBInfo->P3d4, 0x37);
+ if (temp & 0x01) {
+ /* 18 bits FP */
+ xgifb_reg_or(pVBInfo->P3c4, 0x06, 0x40);
+ xgifb_reg_or(pVBInfo->P3c4, 0x09, 0x40);
+ }
+ }
+
+ xgifb_reg_or(pVBInfo->P3c4, 0x1E, 0x01); /* Negative blank polarity */
+
+ xgifb_reg_and(pVBInfo->P3c4, 0x30, ~0x20); /* Hsync polarity */
+ xgifb_reg_and(pVBInfo->P3c4, 0x35, ~0x80); /* Vsync polarity */
+
+ temp = XGI330_RefIndex[RefreshRateTableIndex].Ext_InfoFlag;
+ if (temp & 0x4000)
+ /* Hsync polarity */
+ xgifb_reg_or(pVBInfo->P3c4, 0x30, 0x20);
+ if (temp & 0x8000)
+ /* Vsync polarity */
+ xgifb_reg_or(pVBInfo->P3c4, 0x35, 0x80);
+}
+
+/* --------------------------------------------------------------------- */
+/* Function : XGI_UpdateXG21CRTC */
+/* Input : */
+/* Output : CRT1 CRTC */
+/* Description : Modify CRT1 Hsync/Vsync to fix LCD mode timing */
+/* --------------------------------------------------------------------- */
+static void XGI_UpdateXG21CRTC(unsigned short ModeNo,
+ struct vb_device_info *pVBInfo,
+ unsigned short RefreshRateTableIndex)
+{
+ int index = -1;
+
+ xgifb_reg_and(pVBInfo->P3d4, 0x11, 0x7F); /* Unlock CR0~7 */
+ if (ModeNo == 0x2E &&
+ (XGI330_RefIndex[RefreshRateTableIndex].Ext_CRT1CRTC ==
+ RES640x480x60))
+ index = 12;
+ else if (ModeNo == 0x2E && (XGI330_RefIndex[RefreshRateTableIndex].
+ Ext_CRT1CRTC == RES640x480x72))
+ index = 13;
+ else if (ModeNo == 0x2F)
+ index = 14;
+ else if (ModeNo == 0x50)
+ index = 15;
+ else if (ModeNo == 0x59)
+ index = 16;
+
+ if (index != -1) {
+ xgifb_reg_set(pVBInfo->P3d4, 0x02,
+ XGI_UpdateCRT1Table[index].CR02);
+ xgifb_reg_set(pVBInfo->P3d4, 0x03,
+ XGI_UpdateCRT1Table[index].CR03);
+ xgifb_reg_set(pVBInfo->P3d4, 0x15,
+ XGI_UpdateCRT1Table[index].CR15);
+ xgifb_reg_set(pVBInfo->P3d4, 0x16,
+ XGI_UpdateCRT1Table[index].CR16);
+ }
+}
+
+static void XGI_SetCRT1DE(unsigned short ModeIdIndex,
+ unsigned short RefreshRateTableIndex,
+ struct vb_device_info *pVBInfo)
+{
+ unsigned short resindex, tempax, tempbx, tempcx, temp, modeflag;
+
+ unsigned char data;
+
+ resindex = XGI330_EModeIDTable[ModeIdIndex].Ext_RESINFO;
+
+ modeflag = XGI330_EModeIDTable[ModeIdIndex].Ext_ModeFlag;
+ tempax = XGI330_ModeResInfo[resindex].HTotal;
+ tempbx = XGI330_ModeResInfo[resindex].VTotal;
+
+ if (modeflag & HalfDCLK)
+ tempax >>= 1;
+
+ if (modeflag & HalfDCLK)
+ tempax <<= 1;
+
+ temp = XGI330_RefIndex[RefreshRateTableIndex].Ext_InfoFlag;
+
+ if (temp & InterlaceMode)
+ tempbx >>= 1;
+
+ if (modeflag & DoubleScanMode)
+ tempbx <<= 1;
+
+ tempcx = 8;
+
+ tempax /= tempcx;
+ tempax -= 1;
+ tempbx -= 1;
+ tempcx = tempax;
+ temp = xgifb_reg_get(pVBInfo->P3d4, 0x11);
+ data = xgifb_reg_get(pVBInfo->P3d4, 0x11);
+ data &= 0x7F;
+ xgifb_reg_set(pVBInfo->P3d4, 0x11, data); /* Unlock CRTC */
+ xgifb_reg_set(pVBInfo->P3d4, 0x01, (unsigned short) (tempcx & 0xff));
+ xgifb_reg_and_or(pVBInfo->P3d4, 0x0b, ~0x0c,
+ (unsigned short) ((tempcx & 0x0ff00) >> 10));
+ xgifb_reg_set(pVBInfo->P3d4, 0x12, (unsigned short) (tempbx & 0xff));
+ tempax = 0;
+ tempbx >>= 8;
+
+ if (tempbx & 0x01)
+ tempax |= 0x02;
+
+ if (tempbx & 0x02)
+ tempax |= 0x40;
+
+ xgifb_reg_and_or(pVBInfo->P3d4, 0x07, ~0x42, tempax);
+ data = xgifb_reg_get(pVBInfo->P3d4, 0x07);
+ tempax = 0;
+
+ if (tempbx & 0x04)
+ tempax |= 0x02;
+
+ xgifb_reg_and_or(pVBInfo->P3d4, 0x0a, ~0x02, tempax);
+ xgifb_reg_set(pVBInfo->P3d4, 0x11, temp);
+}
+
+static void XGI_SetCRT1Offset(unsigned short ModeNo,
+ unsigned short ModeIdIndex,
+ unsigned short RefreshRateTableIndex,
+ struct xgi_hw_device_info *HwDeviceExtension,
+ struct vb_device_info *pVBInfo)
+{
+ unsigned short temp, ah, al, temp2, i, DisplayUnit;
+
+ /* GetOffset */
+ temp = XGI330_EModeIDTable[ModeIdIndex].Ext_ModeInfo;
+ temp >>= 8;
+ temp = XGI330_ScreenOffset[temp];
+
+ temp2 = XGI330_RefIndex[RefreshRateTableIndex].Ext_InfoFlag;
+ temp2 &= InterlaceMode;
+
+ if (temp2)
+ temp <<= 1;
+
+ temp2 = pVBInfo->ModeType - ModeEGA;
+
+ switch (temp2) {
+ case 0:
+ temp2 = 1;
+ break;
+ case 1:
+ temp2 = 2;
+ break;
+ case 2:
+ temp2 = 4;
+ break;
+ case 3:
+ temp2 = 4;
+ break;
+ case 4:
+ temp2 = 6;
+ break;
+ case 5:
+ temp2 = 8;
+ break;
+ default:
+ break;
+ }
+
+ if ((ModeNo >= 0x26) && (ModeNo <= 0x28))
+ temp = temp * temp2 + temp2 / 2;
+ else
+ temp *= temp2;
+
+ /* SetOffset */
+ DisplayUnit = temp;
+ temp2 = temp;
+ temp >>= 8; /* ah */
+ temp &= 0x0F;
+ i = xgifb_reg_get(pVBInfo->P3c4, 0x0E);
+ i &= 0xF0;
+ i |= temp;
+ xgifb_reg_set(pVBInfo->P3c4, 0x0E, i);
+
+ temp = (unsigned char) temp2;
+ temp &= 0xFF; /* al */
+ xgifb_reg_set(pVBInfo->P3d4, 0x13, temp);
+
+ /* SetDisplayUnit */
+ temp2 = XGI330_RefIndex[RefreshRateTableIndex].Ext_InfoFlag;
+ temp2 &= InterlaceMode;
+ if (temp2)
+ DisplayUnit >>= 1;
+
+ DisplayUnit <<= 5;
+ ah = (DisplayUnit & 0xff00) >> 8;
+ al = DisplayUnit & 0x00ff;
+ if (al == 0)
+ ah += 1;
+ else
+ ah += 2;
+
+ if (HwDeviceExtension->jChipType >= XG20)
+ if ((ModeNo == 0x4A) | (ModeNo == 0x49))
+ ah -= 1;
+
+ xgifb_reg_set(pVBInfo->P3c4, 0x10, ah);
+}
+
+static unsigned short XGI_GetVCLK2Ptr(unsigned short ModeIdIndex,
+ unsigned short RefreshRateTableIndex,
+ struct vb_device_info *pVBInfo)
+{
+ unsigned short VCLKIndex, modeflag;
+
+ /* si+Ext_ResInfo */
+ modeflag = XGI330_EModeIDTable[ModeIdIndex].Ext_ModeFlag;
+
+ if (pVBInfo->VBInfo & (SetCRT2ToLCD | XGI_SetCRT2ToLCDA)) { /*301b*/
+ if (pVBInfo->LCDResInfo != Panel_1024x768)
+ /* LCDXlat2VCLK */
+ VCLKIndex = VCLK108_2_315 + 5;
+ else
+ VCLKIndex = VCLK65_315 + 2; /* LCDXlat1VCLK */
+ } else if (pVBInfo->VBInfo & SetCRT2ToHiVision) {
+ if (pVBInfo->SetFlag & RPLLDIV2XO)
+ VCLKIndex = TVCLKBASE_315_25 + HiTVVCLKDIV2;
+ else
+ VCLKIndex = TVCLKBASE_315_25 + HiTVVCLK;
+
+ if (pVBInfo->SetFlag & TVSimuMode) {
+ if (modeflag & Charx8Dot)
+ VCLKIndex = TVCLKBASE_315_25 + HiTVSimuVCLK;
+ else
+ VCLKIndex = TVCLKBASE_315_25 + HiTVTextVCLK;
+ }
+
+ /* 301lv */
+ if (pVBInfo->VBType & VB_SIS301LV) {
+ if (pVBInfo->SetFlag & RPLLDIV2XO)
+ VCLKIndex = YPbPr525iVCLK_2;
+ else
+ VCLKIndex = YPbPr525iVCLK;
+ }
+ } else if (pVBInfo->VBInfo & SetCRT2ToTV) {
+ if (pVBInfo->SetFlag & RPLLDIV2XO)
+ VCLKIndex = TVCLKBASE_315_25 + TVVCLKDIV2;
+ else
+ VCLKIndex = TVCLKBASE_315_25 + TVVCLK;
+ } else { /* for CRT2 */
+ /* di+Ext_CRTVCLK */
+ VCLKIndex = XGI330_RefIndex[RefreshRateTableIndex].Ext_CRTVCLK;
+ VCLKIndex &= IndexMask;
+ }
+
+ return VCLKIndex;
+}
+
+static void XGI_SetCRT1VCLK(unsigned short ModeIdIndex,
+ struct xgi_hw_device_info *HwDeviceExtension,
+ unsigned short RefreshRateTableIndex,
+ struct vb_device_info *pVBInfo)
+{
+ unsigned char index, data;
+ unsigned short vclkindex;
+
+ if ((pVBInfo->IF_DEF_LVDS == 0) &&
+ (pVBInfo->VBType & (VB_SIS301B | VB_SIS302B | VB_SIS301LV |
+ VB_SIS302LV | VB_XGI301C)) &&
+ (pVBInfo->VBInfo & XGI_SetCRT2ToLCDA)) {
+ vclkindex = XGI_GetVCLK2Ptr(ModeIdIndex, RefreshRateTableIndex,
+ pVBInfo);
+ data = xgifb_reg_get(pVBInfo->P3c4, 0x31) & 0xCF;
+ xgifb_reg_set(pVBInfo->P3c4, 0x31, data);
+ data = XGI_VBVCLKData[vclkindex].Part4_A;
+ xgifb_reg_set(pVBInfo->P3c4, 0x2B, data);
+ data = XGI_VBVCLKData[vclkindex].Part4_B;
+ xgifb_reg_set(pVBInfo->P3c4, 0x2C, data);
+ xgifb_reg_set(pVBInfo->P3c4, 0x2D, 0x01);
+ } else {
+ index = XGI330_RefIndex[RefreshRateTableIndex].Ext_CRTVCLK;
+ data = xgifb_reg_get(pVBInfo->P3c4, 0x31) & 0xCF;
+ xgifb_reg_set(pVBInfo->P3c4, 0x31, data);
+ xgifb_reg_set(pVBInfo->P3c4, 0x2B, XGI_VCLKData[index].SR2B);
+ xgifb_reg_set(pVBInfo->P3c4, 0x2C, XGI_VCLKData[index].SR2C);
+ xgifb_reg_set(pVBInfo->P3c4, 0x2D, 0x01);
+ }
+
+ if (HwDeviceExtension->jChipType >= XG20) {
+ if (XGI330_EModeIDTable[ModeIdIndex].Ext_ModeFlag &
+ HalfDCLK) {
+ data = xgifb_reg_get(pVBInfo->P3c4, 0x2B);
+ xgifb_reg_set(pVBInfo->P3c4, 0x2B, data);
+ data = xgifb_reg_get(pVBInfo->P3c4, 0x2C);
+ index = data;
+ index &= 0xE0;
+ data &= 0x1F;
+ data <<= 1;
+ data += 1;
+ data |= index;
+ xgifb_reg_set(pVBInfo->P3c4, 0x2C, data);
+ }
+ }
+}
+
+static void XGI_SetXG21FPBits(struct vb_device_info *pVBInfo)
+{
+ unsigned char temp;
+
+ temp = xgifb_reg_get(pVBInfo->P3d4, 0x37); /* D[0] 1: 18bit */
+ temp = (temp & 1) << 6;
+ /* SR06[6] 18bit Dither */
+ xgifb_reg_and_or(pVBInfo->P3c4, 0x06, ~0x40, temp);
+ /* SR09[7] enable FP output, SR09[6] 1: sigle 18bits, 0: dual 12bits */
+ xgifb_reg_and_or(pVBInfo->P3c4, 0x09, ~0xc0, temp | 0x80);
+
+}
+
+static void XGI_SetCRT1FIFO(struct xgi_hw_device_info *HwDeviceExtension,
+ struct vb_device_info *pVBInfo)
+{
+ unsigned short data;
+
+ data = xgifb_reg_get(pVBInfo->P3c4, 0x3D);
+ data &= 0xfe;
+ xgifb_reg_set(pVBInfo->P3c4, 0x3D, data); /* diable auto-threshold */
+
+ xgifb_reg_set(pVBInfo->P3c4, 0x08, 0x34);
+ data = xgifb_reg_get(pVBInfo->P3c4, 0x09);
+ data &= 0xC0;
+ xgifb_reg_set(pVBInfo->P3c4, 0x09, data | 0x30);
+ data = xgifb_reg_get(pVBInfo->P3c4, 0x3D);
+ data |= 0x01;
+ xgifb_reg_set(pVBInfo->P3c4, 0x3D, data);
+
+ if (HwDeviceExtension->jChipType == XG21)
+ XGI_SetXG21FPBits(pVBInfo); /* Fix SR9[7:6] can't read back */
+}
+
+static void XGI_SetVCLKState(struct xgi_hw_device_info *HwDeviceExtension,
+ unsigned short RefreshRateTableIndex,
+ struct vb_device_info *pVBInfo)
+{
+ unsigned short data, data2 = 0;
+ short VCLK;
+
+ unsigned char index;
+
+ index = XGI330_RefIndex[RefreshRateTableIndex].Ext_CRTVCLK;
+ index &= IndexMask;
+ VCLK = XGI_VCLKData[index].CLOCK;
+
+ data = xgifb_reg_get(pVBInfo->P3c4, 0x32);
+ data &= 0xf3;
+ if (VCLK >= 200)
+ data |= 0x0c; /* VCLK > 200 */
+
+ if (HwDeviceExtension->jChipType >= XG20)
+ data &= ~0x04; /* 2 pixel mode */
+
+ xgifb_reg_set(pVBInfo->P3c4, 0x32, data);
+
+ if (HwDeviceExtension->jChipType < XG20) {
+ data = xgifb_reg_get(pVBInfo->P3c4, 0x1F);
+ data &= 0xE7;
+ if (VCLK < 200)
+ data |= 0x10;
+ xgifb_reg_set(pVBInfo->P3c4, 0x1F, data);
+ }
+
+ data2 = 0x00;
+
+ xgifb_reg_and_or(pVBInfo->P3c4, 0x07, 0xFC, data2);
+ if (HwDeviceExtension->jChipType >= XG27)
+ xgifb_reg_and_or(pVBInfo->P3c4, 0x40, 0xFC, data2 & 0x03);
+
+}
+
+static void XGI_SetCRT1ModeRegs(struct xgi_hw_device_info *HwDeviceExtension,
+ unsigned short ModeIdIndex,
+ unsigned short RefreshRateTableIndex,
+ struct vb_device_info *pVBInfo)
+{
+ unsigned short data, data2, data3, infoflag = 0, modeflag, resindex,
+ xres;
+
+ modeflag = XGI330_EModeIDTable[ModeIdIndex].Ext_ModeFlag;
+ infoflag = XGI330_RefIndex[RefreshRateTableIndex].Ext_InfoFlag;
+
+ if (xgifb_reg_get(pVBInfo->P3d4, 0x31) & 0x01)
+ xgifb_reg_and_or(pVBInfo->P3c4, 0x1F, 0x3F, 0x00);
+
+ data = infoflag;
+ data2 = 0;
+ data2 |= 0x02;
+ data3 = pVBInfo->ModeType - ModeVGA;
+ data3 <<= 2;
+ data2 |= data3;
+ data &= InterlaceMode;
+
+ if (data)
+ data2 |= 0x20;
+
+ xgifb_reg_and_or(pVBInfo->P3c4, 0x06, ~0x3F, data2);
+ resindex = XGI330_EModeIDTable[ModeIdIndex].Ext_RESINFO;
+ xres = XGI330_ModeResInfo[resindex].HTotal; /* xres->ax */
+
+ data = 0x0000;
+ if (infoflag & InterlaceMode) {
+ if (xres == 1024)
+ data = 0x0035;
+ else if (xres == 1280)
+ data = 0x0048;
+ }
+
+ xgifb_reg_and_or(pVBInfo->P3d4, 0x19, 0xFF, data);
+ xgifb_reg_and_or(pVBInfo->P3d4, 0x19, 0xFC, 0);
+
+ if (modeflag & HalfDCLK)
+ xgifb_reg_and_or(pVBInfo->P3c4, 0x01, 0xF7, 0x08);
+
+ data2 = 0;
+
+ if (modeflag & LineCompareOff)
+ data2 |= 0x08;
+
+ xgifb_reg_and_or(pVBInfo->P3c4, 0x0F, ~0x48, data2);
+ data = 0x60;
+ data = data ^ 0x60;
+ data = data ^ 0xA0;
+ xgifb_reg_and_or(pVBInfo->P3c4, 0x21, 0x1F, data);
+
+ XGI_SetVCLKState(HwDeviceExtension, RefreshRateTableIndex, pVBInfo);
+
+ data = xgifb_reg_get(pVBInfo->P3d4, 0x31);
+
+ if (HwDeviceExtension->jChipType == XG27) {
+ if (data & 0x40)
+ data = 0x2c;
+ else
+ data = 0x6c;
+ xgifb_reg_set(pVBInfo->P3d4, 0x52, data);
+ xgifb_reg_or(pVBInfo->P3d4, 0x51, 0x10);
+ } else if (HwDeviceExtension->jChipType >= XG20) {
+ if (data & 0x40)
+ data = 0x33;
+ else
+ data = 0x73;
+ xgifb_reg_set(pVBInfo->P3d4, 0x52, data);
+ xgifb_reg_set(pVBInfo->P3d4, 0x51, 0x02);
+ } else {
+ if (data & 0x40)
+ data = 0x2c;
+ else
+ data = 0x6c;
+ xgifb_reg_set(pVBInfo->P3d4, 0x52, data);
+ }
+
+}
+
+static void XGI_WriteDAC(unsigned short dl,
+ unsigned short ah,
+ unsigned short al,
+ unsigned short dh,
+ struct vb_device_info *pVBInfo)
+{
+ unsigned short temp, bh, bl;
+
+ bh = ah;
+ bl = al;
+
+ if (dl != 0) {
+ temp = bh;
+ bh = dh;
+ dh = temp;
+ if (dl == 1) {
+ temp = bl;
+ bl = dh;
+ dh = temp;
+ } else {
+ temp = bl;
+ bl = bh;
+ bh = temp;
+ }
+ }
+ outb((unsigned short) dh, pVBInfo->P3c9);
+ outb((unsigned short) bh, pVBInfo->P3c9);
+ outb((unsigned short) bl, pVBInfo->P3c9);
+}
+
+static void XGI_LoadDAC(struct vb_device_info *pVBInfo)
+{
+ unsigned short data, data2, i, k, m, n, o, si, di, bx, dl, al, ah, dh;
+ const unsigned short *table = XGINew_VGA_DAC;
+
+ outb(0xFF, pVBInfo->P3c6);
+ outb(0x00, pVBInfo->P3c8);
+
+ for (i = 0; i < 16; i++) {
+ data = table[i];
+
+ for (k = 0; k < 3; k++) {
+ data2 = 0;
+
+ if (data & 0x01)
+ data2 = 0x2A;
+
+ if (data & 0x02)
+ data2 += 0x15;
+
+ outb(data2, pVBInfo->P3c9);
+ data >>= 2;
+ }
+ }
+
+ for (i = 16; i < 32; i++) {
+ data = table[i];
+
+ for (k = 0; k < 3; k++)
+ outb(data, pVBInfo->P3c9);
+ }
+
+ si = 32;
+
+ for (m = 0; m < 9; m++) {
+ di = si;
+ bx = si + 0x04;
+ dl = 0;
+
+ for (n = 0; n < 3; n++) {
+ for (o = 0; o < 5; o++) {
+ dh = table[si];
+ ah = table[di];
+ al = table[bx];
+ si++;
+ XGI_WriteDAC(dl, ah, al, dh, pVBInfo);
+ }
+
+ si -= 2;
+
+ for (o = 0; o < 3; o++) {
+ dh = table[bx];
+ ah = table[di];
+ al = table[si];
+ si--;
+ XGI_WriteDAC(dl, ah, al, dh, pVBInfo);
+ }
+
+ dl++;
+ }
+
+ si += 5;
+ }
+}
+
+static void XGI_GetLVDSResInfo(unsigned short ModeIdIndex,
+ struct vb_device_info *pVBInfo)
+{
+ unsigned short resindex, xres, yres, modeflag;
+
+ /* si+Ext_ResInfo */
+ modeflag = XGI330_EModeIDTable[ModeIdIndex].Ext_RESINFO;
+
+ /* si+Ext_ResInfo */
+ resindex = XGI330_EModeIDTable[ModeIdIndex].Ext_RESINFO;
+
+ xres = XGI330_ModeResInfo[resindex].HTotal;
+ yres = XGI330_ModeResInfo[resindex].VTotal;
+
+ if (modeflag & HalfDCLK)
+ xres <<= 1;
+
+ if (modeflag & DoubleScanMode)
+ yres <<= 1;
+
+ if (xres == 720)
+ xres = 640;
+
+ pVBInfo->VGAHDE = xres;
+ pVBInfo->HDE = xres;
+ pVBInfo->VGAVDE = yres;
+ pVBInfo->VDE = yres;
+}
+
+static void const *XGI_GetLcdPtr(struct XGI330_LCDDataTablStruct const *table,
+ unsigned short ModeIdIndex,
+ struct vb_device_info *pVBInfo)
+{
+ unsigned short i, tempdx, tempbx, modeflag;
+
+ tempbx = 0;
+
+ modeflag = XGI330_EModeIDTable[ModeIdIndex].Ext_ModeFlag;
+
+ i = 0;
+
+ while (table[i].PANELID != 0xff) {
+ tempdx = pVBInfo->LCDResInfo;
+ if (tempbx & 0x0080) { /* OEMUtil */
+ tempbx &= (~0x0080);
+ tempdx = pVBInfo->LCDTypeInfo;
+ }
+
+ if (pVBInfo->LCDInfo & EnableScalingLCD)
+ tempdx &= (~PanelResInfo);
+
+ if (table[i].PANELID == tempdx) {
+ tempbx = table[i].MASK;
+ tempdx = pVBInfo->LCDInfo;
+
+ if (modeflag & HalfDCLK)
+ tempdx |= SetLCDLowResolution;
+
+ tempbx &= tempdx;
+ if (tempbx == table[i].CAP)
+ break;
+ }
+ i++;
+ }
+
+ return table[i].DATAPTR;
+}
+
+static struct SiS_TVData const *XGI_GetTVPtr(unsigned short ModeIdIndex,
+ unsigned short RefreshRateTableIndex,
+ struct vb_device_info *pVBInfo)
+{
+ unsigned short i, tempdx, tempal, modeflag;
+
+ modeflag = XGI330_EModeIDTable[ModeIdIndex].Ext_ModeFlag;
+ tempal = XGI330_RefIndex[RefreshRateTableIndex].Ext_CRT2CRTC;
+ tempal = tempal & 0x3f;
+ tempdx = pVBInfo->TVInfo;
+
+ if (pVBInfo->VBInfo & SetInSlaveMode)
+ tempdx = tempdx | SetTVLockMode;
+
+ if (modeflag & HalfDCLK)
+ tempdx = tempdx | SetTVLowResolution;
+
+ i = 0;
+
+ while (XGI_TVDataTable[i].MASK != 0xffff) {
+ if ((tempdx & XGI_TVDataTable[i].MASK) ==
+ XGI_TVDataTable[i].CAP)
+ break;
+ i++;
+ }
+
+ return &XGI_TVDataTable[i].DATAPTR[tempal];
+}
+
+static void XGI_GetLVDSData(unsigned short ModeIdIndex,
+ struct vb_device_info *pVBInfo)
+{
+ struct SiS_LVDSData const *LCDPtr;
+
+ if (!(pVBInfo->VBInfo & (SetCRT2ToLCD | XGI_SetCRT2ToLCDA)))
+ return;
+
+ LCDPtr = XGI_GetLcdPtr(XGI_EPLLCDDataPtr, ModeIdIndex, pVBInfo);
+ pVBInfo->VGAHT = LCDPtr->VGAHT;
+ pVBInfo->VGAVT = LCDPtr->VGAVT;
+ pVBInfo->HT = LCDPtr->LCDHT;
+ pVBInfo->VT = LCDPtr->LCDVT;
+
+ if (pVBInfo->LCDInfo & (SetLCDtoNonExpanding | EnableScalingLCD))
+ return;
+
+ if ((pVBInfo->LCDResInfo == Panel_1024x768) ||
+ (pVBInfo->LCDResInfo == Panel_1024x768x75)) {
+ pVBInfo->HDE = 1024;
+ pVBInfo->VDE = 768;
+ } else if ((pVBInfo->LCDResInfo == Panel_1280x1024) ||
+ (pVBInfo->LCDResInfo == Panel_1280x1024x75)) {
+ pVBInfo->HDE = 1280;
+ pVBInfo->VDE = 1024;
+ } else if (pVBInfo->LCDResInfo == Panel_1400x1050) {
+ pVBInfo->HDE = 1400;
+ pVBInfo->VDE = 1050;
+ } else {
+ pVBInfo->HDE = 1600;
+ pVBInfo->VDE = 1200;
+ }
+}
+
+static void XGI_ModCRT1Regs(unsigned short ModeIdIndex,
+ struct xgi_hw_device_info *HwDeviceExtension,
+ struct vb_device_info *pVBInfo)
+{
+ unsigned short i;
+ struct XGI_LVDSCRT1HDataStruct const *LCDPtr = NULL;
+ struct XGI_LVDSCRT1VDataStruct const *LCDPtr1 = NULL;
+
+ if (pVBInfo->VBInfo & (SetCRT2ToLCD | XGI_SetCRT2ToLCDA)) {
+ LCDPtr = XGI_GetLcdPtr(xgifb_epllcd_crt1_h, ModeIdIndex,
+ pVBInfo);
+
+ for (i = 0; i < 8; i++)
+ pVBInfo->TimingH.data[i] = LCDPtr[0].Reg[i];
+ }
+
+ XGI_SetCRT1Timing_H(pVBInfo, HwDeviceExtension);
+
+ if (pVBInfo->VBInfo & (SetCRT2ToLCD | XGI_SetCRT2ToLCDA)) {
+ LCDPtr1 = XGI_GetLcdPtr(xgifb_epllcd_crt1_v, ModeIdIndex,
+ pVBInfo);
+ for (i = 0; i < 7; i++)
+ pVBInfo->TimingV.data[i] = LCDPtr1[0].Reg[i];
+ }
+
+ XGI_SetCRT1Timing_V(ModeIdIndex, pVBInfo);
+}
+
+static unsigned short XGI_GetLCDCapPtr(struct vb_device_info *pVBInfo)
+{
+ unsigned char tempal, tempah, tempbl, i;
+
+ tempah = xgifb_reg_get(pVBInfo->P3d4, 0x36);
+ tempal = tempah & 0x0F;
+ tempah = tempah & 0xF0;
+ i = 0;
+ tempbl = pVBInfo->LCDCapList[i].LCD_ID;
+
+ while (tempbl != 0xFF) {
+ if (tempbl & 0x80) { /* OEMUtil */
+ tempal = tempah;
+ tempbl = tempbl & ~(0x80);
+ }
+
+ if (tempal == tempbl)
+ break;
+
+ i++;
+
+ tempbl = pVBInfo->LCDCapList[i].LCD_ID;
+ }
+
+ return i;
+}
+
+static unsigned short XGI_GetLCDCapPtr1(struct vb_device_info *pVBInfo)
+{
+ unsigned short tempah, tempal, tempbl, i;
+
+ tempal = pVBInfo->LCDResInfo;
+ tempah = pVBInfo->LCDTypeInfo;
+
+ i = 0;
+ tempbl = pVBInfo->LCDCapList[i].LCD_ID;
+
+ while (tempbl != 0xFF) {
+ if ((tempbl & 0x80) && (tempbl != 0x80)) {
+ tempal = tempah;
+ tempbl &= ~0x80;
+ }
+
+ if (tempal == tempbl)
+ break;
+
+ i++;
+ tempbl = pVBInfo->LCDCapList[i].LCD_ID;
+ }
+
+ if (tempbl == 0xFF) {
+ pVBInfo->LCDResInfo = Panel_1024x768;
+ pVBInfo->LCDTypeInfo = 0;
+ i = 0;
+ }
+
+ return i;
+}
+
+static void XGI_GetLCDSync(unsigned short *HSyncWidth,
+ unsigned short *VSyncWidth,
+ struct vb_device_info *pVBInfo)
+{
+ unsigned short Index;
+
+ Index = XGI_GetLCDCapPtr(pVBInfo);
+ *HSyncWidth = pVBInfo->LCDCapList[Index].LCD_HSyncWidth;
+ *VSyncWidth = pVBInfo->LCDCapList[Index].LCD_VSyncWidth;
+}
+
+static void XGI_SetLVDSRegs(unsigned short ModeIdIndex,
+ struct vb_device_info *pVBInfo)
+{
+ unsigned short tempbx, tempax, tempcx, tempdx, push1, push2, modeflag;
+ unsigned long temp, temp1, temp2, temp3, push3;
+ struct XGI330_LCDDataDesStruct2 const *LCDPtr1 = NULL;
+
+ modeflag = XGI330_EModeIDTable[ModeIdIndex].Ext_ModeFlag;
+ LCDPtr1 = XGI_GetLcdPtr(XGI_EPLLCDDesDataPtr, ModeIdIndex, pVBInfo);
+
+ XGI_GetLCDSync(&tempax, &tempbx, pVBInfo);
+ push1 = tempbx;
+ push2 = tempax;
+
+ /* GetLCDResInfo */
+ if ((pVBInfo->LCDResInfo == Panel_1024x768) ||
+ (pVBInfo->LCDResInfo == Panel_1024x768x75)) {
+ tempax = 1024;
+ tempbx = 768;
+ } else if ((pVBInfo->LCDResInfo == Panel_1280x1024) ||
+ (pVBInfo->LCDResInfo == Panel_1280x1024x75)) {
+ tempax = 1280;
+ tempbx = 1024;
+ } else if (pVBInfo->LCDResInfo == Panel_1400x1050) {
+ tempax = 1400;
+ tempbx = 1050;
+ } else {
+ tempax = 1600;
+ tempbx = 1200;
+ }
+
+ if (pVBInfo->LCDInfo & SetLCDtoNonExpanding) {
+ pVBInfo->HDE = tempax;
+ pVBInfo->VDE = tempbx;
+ pVBInfo->VGAHDE = tempax;
+ pVBInfo->VGAVDE = tempbx;
+ }
+
+ tempax = pVBInfo->HT;
+
+ tempbx = LCDPtr1->LCDHDES;
+
+ tempcx = pVBInfo->HDE;
+ tempbx = tempbx & 0x0fff;
+ tempcx += tempbx;
+
+ if (tempcx >= tempax)
+ tempcx -= tempax;
+
+ xgifb_reg_set(pVBInfo->Part1Port, 0x1A, tempbx & 0x07);
+
+ tempcx >>= 3;
+ tempbx >>= 3;
+
+ xgifb_reg_set(pVBInfo->Part1Port, 0x16,
+ (unsigned short) (tempbx & 0xff));
+ xgifb_reg_set(pVBInfo->Part1Port, 0x17,
+ (unsigned short) (tempcx & 0xff));
+
+ tempax = pVBInfo->HT;
+
+ tempbx = LCDPtr1->LCDHRS;
+
+ tempcx = push2;
+
+ if (pVBInfo->LCDInfo & EnableScalingLCD)
+ tempcx = LCDPtr1->LCDHSync;
+
+ tempcx += tempbx;
+
+ if (tempcx >= tempax)
+ tempcx -= tempax;
+
+ tempax = tempbx & 0x07;
+ tempax >>= 5;
+ tempcx >>= 3;
+ tempbx >>= 3;
+
+ tempcx &= 0x1f;
+ tempax |= tempcx;
+
+ xgifb_reg_set(pVBInfo->Part1Port, 0x15, tempax);
+ xgifb_reg_set(pVBInfo->Part1Port, 0x14,
+ (unsigned short) (tempbx & 0xff));
+
+ tempax = pVBInfo->VT;
+ tempbx = LCDPtr1->LCDVDES;
+ tempcx = pVBInfo->VDE;
+
+ tempbx = tempbx & 0x0fff;
+ tempcx += tempbx;
+ if (tempcx >= tempax)
+ tempcx -= tempax;
+
+ xgifb_reg_set(pVBInfo->Part1Port, 0x1b,
+ (unsigned short) (tempbx & 0xff));
+ xgifb_reg_set(pVBInfo->Part1Port, 0x1c,
+ (unsigned short) (tempcx & 0xff));
+
+ tempbx = (tempbx >> 8) & 0x07;
+ tempcx = (tempcx >> 8) & 0x07;
+
+ xgifb_reg_set(pVBInfo->Part1Port, 0x1d,
+ (unsigned short) ((tempcx << 3)
+ | tempbx));
+
+ tempax = pVBInfo->VT;
+ tempbx = LCDPtr1->LCDVRS;
+
+ tempcx = push1;
+
+ if (pVBInfo->LCDInfo & EnableScalingLCD)
+ tempcx = LCDPtr1->LCDVSync;
+
+ tempcx += tempbx;
+ if (tempcx >= tempax)
+ tempcx -= tempax;
+
+ xgifb_reg_set(pVBInfo->Part1Port, 0x18,
+ (unsigned short) (tempbx & 0xff));
+ xgifb_reg_and_or(pVBInfo->Part1Port, 0x19, ~0x0f,
+ (unsigned short) (tempcx & 0x0f));
+
+ tempax = ((tempbx >> 8) & 0x07) << 3;
+
+ tempbx = pVBInfo->VGAVDE;
+ if (tempbx != pVBInfo->VDE)
+ tempax |= 0x40;
+
+ if (pVBInfo->LCDInfo & XGI_EnableLVDSDDA)
+ tempax |= 0x40;
+
+ xgifb_reg_and_or(pVBInfo->Part1Port, 0x1a, 0x07,
+ tempax);
+
+ tempbx = pVBInfo->VDE;
+ tempax = pVBInfo->VGAVDE;
+
+ temp = tempax; /* 0430 ylshieh */
+ temp1 = (temp << 18) / tempbx;
+
+ tempdx = (unsigned short) ((temp << 18) % tempbx);
+
+ if (tempdx != 0)
+ temp1 += 1;
+
+ temp2 = temp1;
+ push3 = temp2;
+
+ xgifb_reg_set(pVBInfo->Part1Port, 0x37,
+ (unsigned short) (temp2 & 0xff));
+ xgifb_reg_set(pVBInfo->Part1Port, 0x36,
+ (unsigned short) ((temp2 >> 8) & 0xff));
+
+ tempbx = (unsigned short) (temp2 >> 16);
+ tempax = tempbx & 0x03;
+
+ tempbx = pVBInfo->VGAVDE;
+ if (tempbx == pVBInfo->VDE)
+ tempax |= 0x04;
+
+ xgifb_reg_set(pVBInfo->Part1Port, 0x35, tempax);
+
+ if (pVBInfo->VBType & VB_XGI301C) {
+ temp2 = push3;
+ xgifb_reg_set(pVBInfo->Part4Port,
+ 0x3c,
+ (unsigned short) (temp2 & 0xff));
+ xgifb_reg_set(pVBInfo->Part4Port,
+ 0x3b,
+ (unsigned short) ((temp2 >> 8) &
+ 0xff));
+ tempbx = (unsigned short) (temp2 >> 16);
+ xgifb_reg_and_or(pVBInfo->Part4Port, 0x3a,
+ ~0xc0,
+ (unsigned short) ((tempbx &
+ 0xff) << 6));
+
+ tempcx = pVBInfo->VGAVDE;
+ if (tempcx == pVBInfo->VDE)
+ xgifb_reg_and_or(pVBInfo->Part4Port,
+ 0x30, ~0x0c, 0x00);
+ else
+ xgifb_reg_and_or(pVBInfo->Part4Port,
+ 0x30, ~0x0c, 0x08);
+ }
+
+ tempcx = pVBInfo->VGAHDE;
+ tempbx = pVBInfo->HDE;
+
+ temp1 = tempcx << 16;
+
+ tempax = (unsigned short) (temp1 / tempbx);
+
+ if ((tempbx & 0xffff) == (tempcx & 0xffff))
+ tempax = 65535;
+
+ temp3 = tempax;
+ temp1 = pVBInfo->VGAHDE << 16;
+
+ temp1 /= temp3;
+ temp3 <<= 16;
+ temp1 -= 1;
+
+ temp3 = (temp3 & 0xffff0000) + (temp1 & 0xffff);
+
+ tempax = (unsigned short) (temp3 & 0xff);
+ xgifb_reg_set(pVBInfo->Part1Port, 0x1f, tempax);
+
+ temp1 = pVBInfo->VGAVDE << 18;
+ temp1 = temp1 / push3;
+ tempbx = (unsigned short) (temp1 & 0xffff);
+
+ if (pVBInfo->LCDResInfo == Panel_1024x768)
+ tempbx -= 1;
+
+ tempax = ((tempbx >> 8) & 0xff) << 3;
+ tempax |= (unsigned short) ((temp3 >> 8) & 0x07);
+ xgifb_reg_set(pVBInfo->Part1Port, 0x20,
+ (unsigned short) (tempax & 0xff));
+ xgifb_reg_set(pVBInfo->Part1Port, 0x21,
+ (unsigned short) (tempbx & 0xff));
+
+ temp3 >>= 16;
+
+ if (modeflag & HalfDCLK)
+ temp3 >>= 1;
+
+ xgifb_reg_set(pVBInfo->Part1Port, 0x22,
+ (unsigned short) ((temp3 >> 8) & 0xff));
+ xgifb_reg_set(pVBInfo->Part1Port, 0x23,
+ (unsigned short) (temp3 & 0xff));
+}
+
+/* --------------------------------------------------------------------- */
+/* Function : XGI_GETLCDVCLKPtr */
+/* Input : */
+/* Output : al -> VCLK Index */
+/* Description : */
+/* --------------------------------------------------------------------- */
+static void XGI_GetLCDVCLKPtr(unsigned char *di_0, unsigned char *di_1,
+ struct vb_device_info *pVBInfo)
+{
+ unsigned short index;
+
+ if (pVBInfo->VBInfo & (SetCRT2ToLCD | XGI_SetCRT2ToLCDA)) {
+ index = XGI_GetLCDCapPtr1(pVBInfo);
+
+ if (pVBInfo->VBInfo & SetCRT2ToLCD) { /* LCDB */
+ *di_0 = pVBInfo->LCDCapList[index].LCUCHAR_VCLKData1;
+ *di_1 = pVBInfo->LCDCapList[index].LCUCHAR_VCLKData2;
+ } else { /* LCDA */
+ *di_0 = pVBInfo->LCDCapList[index].LCDA_VCLKData1;
+ *di_1 = pVBInfo->LCDCapList[index].LCDA_VCLKData2;
+ }
+ }
+}
+
+static unsigned char XGI_GetVCLKPtr(unsigned short RefreshRateTableIndex,
+ unsigned short ModeIdIndex, struct vb_device_info *pVBInfo)
+{
+
+ unsigned short index, modeflag;
+ unsigned char tempal;
+
+ /* si+Ext_ResInfo */
+ modeflag = XGI330_EModeIDTable[ModeIdIndex].Ext_ModeFlag;
+
+ if ((pVBInfo->SetFlag & ProgrammingCRT2) &&
+ (!(pVBInfo->LCDInfo & EnableScalingLCD))) { /* {LCDA/LCDB} */
+ index = XGI_GetLCDCapPtr(pVBInfo);
+ tempal = pVBInfo->LCDCapList[index].LCD_VCLK;
+
+ if (pVBInfo->VBInfo & (SetCRT2ToLCD | XGI_SetCRT2ToLCDA))
+ return tempal;
+
+ /* {TV} */
+ if (pVBInfo->VBType &
+ (VB_SIS301B |
+ VB_SIS302B |
+ VB_SIS301LV |
+ VB_SIS302LV |
+ VB_XGI301C)) {
+ if (pVBInfo->VBInfo & SetCRT2ToHiVision) {
+ tempal = TVCLKBASE_315 + HiTVVCLKDIV2;
+ if (!(pVBInfo->TVInfo & RPLLDIV2XO))
+ tempal = TVCLKBASE_315 + HiTVVCLK;
+ if (pVBInfo->TVInfo & TVSimuMode) {
+ tempal = TVCLKBASE_315 + HiTVSimuVCLK;
+ if (!(modeflag & Charx8Dot))
+ tempal = TVCLKBASE_315 +
+ HiTVTextVCLK;
+
+ }
+ return tempal;
+ }
+
+ if (pVBInfo->TVInfo & TVSetYPbPr750p) {
+ tempal = XGI_YPbPr750pVCLK;
+ return tempal;
+ }
+
+ if (pVBInfo->TVInfo & TVSetYPbPr525p) {
+ tempal = YPbPr525pVCLK;
+ return tempal;
+ }
+
+ tempal = NTSC1024VCLK;
+
+ if (!(pVBInfo->TVInfo & NTSC1024x768)) {
+ tempal = TVCLKBASE_315 + TVVCLKDIV2;
+ if (!(pVBInfo->TVInfo & RPLLDIV2XO))
+ tempal = TVCLKBASE_315 + TVVCLK;
+ }
+
+ if (pVBInfo->VBInfo & SetCRT2ToTV)
+ return tempal;
+ }
+ } /* {End of VB} */
+
+ inb((pVBInfo->P3ca + 0x02));
+ tempal = XGI330_RefIndex[RefreshRateTableIndex].Ext_CRTVCLK;
+ return tempal;
+}
+
+static void XGI_GetVCLKLen(unsigned char tempal, unsigned char *di_0,
+ unsigned char *di_1, struct vb_device_info *pVBInfo)
+{
+ if (pVBInfo->VBType & (VB_SIS301 | VB_SIS301B | VB_SIS302B
+ | VB_SIS301LV | VB_SIS302LV | VB_XGI301C)) {
+ if ((!(pVBInfo->VBInfo & XGI_SetCRT2ToLCDA)) &&
+ (pVBInfo->SetFlag & ProgrammingCRT2)) {
+ *di_0 = XGI_VBVCLKData[tempal].Part4_A;
+ *di_1 = XGI_VBVCLKData[tempal].Part4_B;
+ }
+ } else {
+ *di_0 = XGI_VCLKData[tempal].SR2B;
+ *di_1 = XGI_VCLKData[tempal].SR2C;
+ }
+}
+
+static void XGI_SetCRT2ECLK(unsigned short ModeIdIndex,
+ unsigned short RefreshRateTableIndex,
+ struct vb_device_info *pVBInfo)
+{
+ unsigned char di_0, di_1, tempal;
+ int i;
+
+ tempal = XGI_GetVCLKPtr(RefreshRateTableIndex, ModeIdIndex, pVBInfo);
+ XGI_GetVCLKLen(tempal, &di_0, &di_1, pVBInfo);
+ XGI_GetLCDVCLKPtr(&di_0, &di_1, pVBInfo);
+
+ for (i = 0; i < 4; i++) {
+ xgifb_reg_and_or(pVBInfo->P3d4, 0x31, ~0x30,
+ (unsigned short) (0x10 * i));
+ if ((!(pVBInfo->VBInfo & XGI_SetCRT2ToLCDA))
+ && (!(pVBInfo->VBInfo & SetInSlaveMode))) {
+ xgifb_reg_set(pVBInfo->P3c4, 0x2e, di_0);
+ xgifb_reg_set(pVBInfo->P3c4, 0x2f, di_1);
+ } else {
+ xgifb_reg_set(pVBInfo->P3c4, 0x2b, di_0);
+ xgifb_reg_set(pVBInfo->P3c4, 0x2c, di_1);
+ }
+ }
+}
+
+static void XGI_UpdateModeInfo(struct vb_device_info *pVBInfo)
+{
+ unsigned short tempcl, tempch, temp, tempbl, tempax;
+
+ if (pVBInfo->VBType & (VB_SIS301B | VB_SIS302B | VB_SIS301LV
+ | VB_SIS302LV | VB_XGI301C)) {
+ tempcl = 0;
+ tempch = 0;
+ temp = xgifb_reg_get(pVBInfo->P3c4, 0x01);
+
+ if (!(temp & 0x20)) {
+ temp = xgifb_reg_get(pVBInfo->P3d4, 0x17);
+ if (temp & 0x80) {
+ temp = xgifb_reg_get(pVBInfo->P3d4, 0x53);
+ if (!(temp & 0x40))
+ tempcl |= ActiveCRT1;
+ }
+ }
+
+ temp = xgifb_reg_get(pVBInfo->Part1Port, 0x2e);
+ temp &= 0x0f;
+
+ if (!(temp == 0x08)) {
+ /* Check ChannelA */
+ tempax = xgifb_reg_get(pVBInfo->Part1Port, 0x13);
+ if (tempax & 0x04)
+ tempcl = tempcl | ActiveLCD;
+
+ temp &= 0x05;
+
+ if (!(tempcl & ActiveLCD))
+ if (temp == 0x01)
+ tempcl |= ActiveCRT2;
+
+ if (temp == 0x04)
+ tempcl |= ActiveLCD;
+
+ if (temp == 0x05) {
+ temp = xgifb_reg_get(pVBInfo->Part2Port, 0x00);
+
+ if (!(temp & 0x08))
+ tempch |= ActiveAVideo;
+
+ if (!(temp & 0x04))
+ tempch |= ActiveSVideo;
+
+ if (temp & 0x02)
+ tempch |= ActiveSCART;
+
+ if (pVBInfo->VBInfo & SetCRT2ToHiVision) {
+ if (temp & 0x01)
+ tempch |= ActiveHiTV;
+ }
+
+ if (pVBInfo->VBInfo & SetCRT2ToYPbPr525750) {
+ temp = xgifb_reg_get(
+ pVBInfo->Part2Port,
+ 0x4d);
+
+ if (temp & 0x10)
+ tempch |= ActiveYPbPr;
+ }
+
+ if (tempch != 0)
+ tempcl |= ActiveTV;
+ }
+ }
+
+ temp = xgifb_reg_get(pVBInfo->P3d4, 0x3d);
+ if (tempcl & ActiveLCD) {
+ if ((pVBInfo->SetFlag & ReserveTVOption)) {
+ if (temp & ActiveTV)
+ tempcl |= ActiveTV;
+ }
+ }
+ temp = tempcl;
+ tempbl = ~XGI_ModeSwitchStatus;
+ xgifb_reg_and_or(pVBInfo->P3d4, 0x3d, tempbl, temp);
+
+ if (!(pVBInfo->SetFlag & ReserveTVOption))
+ xgifb_reg_set(pVBInfo->P3d4, 0x3e, tempch);
+ }
+}
+
+void XGI_GetVBType(struct vb_device_info *pVBInfo)
+{
+ unsigned short flag, tempbx, tempah;
+
+ tempbx = VB_SIS302B;
+ flag = xgifb_reg_get(pVBInfo->Part4Port, 0x00);
+ if (flag == 0x02)
+ goto finish;
+
+ tempbx = VB_SIS301;
+ flag = xgifb_reg_get(pVBInfo->Part4Port, 0x01);
+ if (flag < 0xB0)
+ goto finish;
+
+ tempbx = VB_SIS301B;
+ if (flag < 0xC0)
+ goto bigger_than_0xB0;
+
+ tempbx = VB_XGI301C;
+ if (flag < 0xD0)
+ goto bigger_than_0xB0;
+
+ tempbx = VB_SIS301LV;
+ if (flag < 0xE0)
+ goto bigger_than_0xB0;
+
+ tempbx = VB_SIS302LV;
+ tempah = xgifb_reg_get(pVBInfo->Part4Port, 0x39);
+ if (tempah != 0xFF)
+ tempbx = VB_XGI301C;
+
+bigger_than_0xB0:
+ if (tempbx & (VB_SIS301B | VB_SIS302B)) {
+ flag = xgifb_reg_get(pVBInfo->Part4Port, 0x23);
+ if (!(flag & 0x02))
+ tempbx = tempbx | VB_NoLCD;
+ }
+
+finish:
+ pVBInfo->VBType = tempbx;
+}
+
+static void XGI_GetVBInfo(unsigned short ModeIdIndex,
+ struct vb_device_info *pVBInfo)
+{
+ unsigned short tempax, push, tempbx, temp, modeflag;
+
+ modeflag = XGI330_EModeIDTable[ModeIdIndex].Ext_ModeFlag;
+ pVBInfo->SetFlag = 0;
+ pVBInfo->ModeType = modeflag & ModeTypeMask;
+ tempbx = 0;
+
+ if (!(pVBInfo->VBType & 0xFFFF))
+ return;
+
+ /* Check Display Device */
+ temp = xgifb_reg_get(pVBInfo->P3d4, 0x30);
+ tempbx = tempbx | temp;
+ temp = xgifb_reg_get(pVBInfo->P3d4, 0x31);
+ push = temp;
+ push <<= 8;
+ tempax = temp << 8;
+ tempbx = tempbx | tempax;
+ temp = (SetCRT2ToDualEdge | SetCRT2ToYPbPr525750 | XGI_SetCRT2ToLCDA
+ | SetInSlaveMode | DisableCRT2Display);
+ temp = 0xFFFF ^ temp;
+ tempbx &= temp;
+
+ temp = xgifb_reg_get(pVBInfo->P3d4, 0x38);
+
+ if (pVBInfo->VBType & (VB_SIS302B | VB_SIS301LV | VB_SIS302LV |
+ VB_XGI301C)) {
+ if (temp & EnableDualEdge) {
+ tempbx |= SetCRT2ToDualEdge;
+ if (temp & SetToLCDA)
+ tempbx |= XGI_SetCRT2ToLCDA;
+ }
+ }
+
+ if (pVBInfo->VBType & (VB_SIS301LV|VB_SIS302LV|VB_XGI301C)) {
+ if (temp & SetYPbPr) {
+ /* shampoo add for new scratch */
+ temp = xgifb_reg_get(pVBInfo->P3d4, 0x35);
+ temp &= YPbPrMode;
+ tempbx |= SetCRT2ToHiVision;
+
+ if (temp != YPbPrMode1080i) {
+ tempbx &= (~SetCRT2ToHiVision);
+ tempbx |= SetCRT2ToYPbPr525750;
+ }
+ }
+ }
+
+ tempax = push; /* restore CR31 */
+
+ temp = 0x09FC;
+
+ if (!(tempbx & temp)) {
+ tempax |= DisableCRT2Display;
+ tempbx = 0;
+ }
+
+ if (!(pVBInfo->VBType & VB_NoLCD)) {
+ if (tempbx & XGI_SetCRT2ToLCDA) {
+ if (tempbx & SetSimuScanMode)
+ tempbx &= (~(SetCRT2ToLCD | SetCRT2ToRAMDAC |
+ SwitchCRT2));
+ else
+ tempbx &= (~(SetCRT2ToLCD | SetCRT2ToRAMDAC |
+ SetCRT2ToTV | SwitchCRT2));
+ }
+ }
+
+ /* shampoo add */
+ /* for driver abnormal */
+ if (!(tempbx & (SwitchCRT2 | SetSimuScanMode))) {
+ if (tempbx & SetCRT2ToRAMDAC) {
+ tempbx &= (0xFF00 | SetCRT2ToRAMDAC |
+ SwitchCRT2 | SetSimuScanMode);
+ tempbx &= (0x00FF | (~SetCRT2ToYPbPr525750));
+ }
+ }
+
+ if (!(pVBInfo->VBType & VB_NoLCD)) {
+ if (tempbx & SetCRT2ToLCD) {
+ tempbx &= (0xFF00 | SetCRT2ToLCD | SwitchCRT2 |
+ SetSimuScanMode);
+ tempbx &= (0x00FF | (~SetCRT2ToYPbPr525750));
+ }
+ }
+
+ if (tempbx & SetCRT2ToSCART) {
+ tempbx &= (0xFF00 | SetCRT2ToSCART | SwitchCRT2 |
+ SetSimuScanMode);
+ tempbx &= (0x00FF | (~SetCRT2ToYPbPr525750));
+ }
+
+ if (tempbx & SetCRT2ToYPbPr525750)
+ tempbx &= (0xFF00 | SwitchCRT2 | SetSimuScanMode);
+
+ if (tempbx & SetCRT2ToHiVision)
+ tempbx &= (0xFF00 | SetCRT2ToHiVision | SwitchCRT2 |
+ SetSimuScanMode);
+
+ if (tempax & DisableCRT2Display) { /* Set Display Device Info */
+ if (!(tempbx & (SwitchCRT2 | SetSimuScanMode)))
+ tempbx = DisableCRT2Display;
+ }
+
+ if (!(tempbx & DisableCRT2Display)) {
+ if ((!(tempbx & DriverMode)) || (!(modeflag & CRT2Mode))) {
+ if (!(tempbx & XGI_SetCRT2ToLCDA))
+ tempbx |= (SetInSlaveMode | SetSimuScanMode);
+ }
+
+ /* LCD+TV can't support in slave mode
+ * (Force LCDA+TV->LCDB) */
+ if ((tempbx & SetInSlaveMode) && (tempbx & XGI_SetCRT2ToLCDA)) {
+ tempbx ^= (SetCRT2ToLCD | XGI_SetCRT2ToLCDA |
+ SetCRT2ToDualEdge);
+ pVBInfo->SetFlag |= ReserveTVOption;
+ }
+ }
+
+ pVBInfo->VBInfo = tempbx;
+}
+
+static void XGI_GetTVInfo(unsigned short ModeIdIndex,
+ struct vb_device_info *pVBInfo)
+{
+ unsigned short tempbx = 0, resinfo = 0, modeflag, index1;
+
+ if (pVBInfo->VBInfo & SetCRT2ToTV) {
+ modeflag = XGI330_EModeIDTable[ModeIdIndex].Ext_ModeFlag;
+ resinfo = XGI330_EModeIDTable[ModeIdIndex].Ext_RESINFO;
+
+ tempbx = xgifb_reg_get(pVBInfo->P3d4, 0x35);
+ if (tempbx & TVSetPAL) {
+ tempbx &= (SetCHTVOverScan |
+ TVSetPALM |
+ TVSetPALN |
+ TVSetPAL);
+ if (tempbx & TVSetPALM)
+ /* set to NTSC if PAL-M */
+ tempbx &= ~TVSetPAL;
+ } else
+ tempbx &= (SetCHTVOverScan |
+ TVSetNTSCJ |
+ TVSetPAL);
+
+ if (pVBInfo->VBInfo & SetCRT2ToSCART)
+ tempbx |= TVSetPAL;
+
+ if (pVBInfo->VBInfo & SetCRT2ToYPbPr525750) {
+ index1 = xgifb_reg_get(pVBInfo->P3d4, 0x35);
+ index1 &= YPbPrMode;
+
+ if (index1 == YPbPrMode525i)
+ tempbx |= TVSetYPbPr525i;
+
+ if (index1 == YPbPrMode525p)
+ tempbx = tempbx | TVSetYPbPr525p;
+ if (index1 == YPbPrMode750p)
+ tempbx = tempbx | TVSetYPbPr750p;
+ }
+
+ if (pVBInfo->VBInfo & SetCRT2ToHiVision)
+ tempbx = tempbx | TVSetHiVision | TVSetPAL;
+
+ if ((pVBInfo->VBInfo & SetInSlaveMode) &&
+ (!(pVBInfo->VBInfo & SetNotSimuMode)))
+ tempbx |= TVSimuMode;
+
+ if (!(tempbx & TVSetPAL) && (modeflag > 13) && (resinfo == 8))
+ /* NTSC 1024x768, */
+ tempbx |= NTSC1024x768;
+
+ tempbx |= RPLLDIV2XO;
+
+ if (pVBInfo->VBInfo & SetCRT2ToHiVision) {
+ if (pVBInfo->VBInfo & SetInSlaveMode)
+ tempbx &= (~RPLLDIV2XO);
+ } else if (tempbx & (TVSetYPbPr525p | TVSetYPbPr750p)) {
+ tempbx &= (~RPLLDIV2XO);
+ } else if (!(pVBInfo->VBType & (VB_SIS301B | VB_SIS302B |
+ VB_SIS301LV | VB_SIS302LV |
+ VB_XGI301C))) {
+ if (tempbx & TVSimuMode)
+ tempbx &= (~RPLLDIV2XO);
+ }
+ }
+ pVBInfo->TVInfo = tempbx;
+}
+
+static unsigned char XGI_GetLCDInfo(unsigned short ModeIdIndex,
+ struct vb_device_info *pVBInfo)
+{
+ unsigned short temp, tempax, tempbx, resinfo = 0, LCDIdIndex;
+
+ pVBInfo->LCDResInfo = 0;
+ pVBInfo->LCDTypeInfo = 0;
+ pVBInfo->LCDInfo = 0;
+
+ /* si+Ext_ResInfo // */
+ resinfo = XGI330_EModeIDTable[ModeIdIndex].Ext_RESINFO;
+ temp = xgifb_reg_get(pVBInfo->P3d4, 0x36); /* Get LCD Res.Info */
+ tempbx = temp & 0x0F;
+
+ if (tempbx == 0)
+ tempbx = Panel_1024x768; /* default */
+
+ /* LCD75 */
+ if ((tempbx == Panel_1024x768) || (tempbx == Panel_1280x1024)) {
+ if (pVBInfo->VBInfo & DriverMode) {
+ tempax = xgifb_reg_get(pVBInfo->P3d4, 0x33);
+ if (pVBInfo->VBInfo & XGI_SetCRT2ToLCDA)
+ tempax &= 0x0F;
+ else
+ tempax >>= 4;
+
+ if ((resinfo == 6) || (resinfo == 9)) {
+ if (tempax >= 3)
+ tempbx |= PanelRef75Hz;
+ } else if ((resinfo == 7) || (resinfo == 8)) {
+ if (tempax >= 4)
+ tempbx |= PanelRef75Hz;
+ }
+ }
+ }
+
+ pVBInfo->LCDResInfo = tempbx;
+
+ /* End of LCD75 */
+
+ if (!(pVBInfo->VBInfo & (SetCRT2ToLCD | XGI_SetCRT2ToLCDA)))
+ return 0;
+
+ tempbx = 0;
+
+ temp = xgifb_reg_get(pVBInfo->P3d4, 0x37);
+
+ temp &= (ScalingLCD | LCDNonExpanding | LCDSyncBit | SetPWDEnable);
+
+ tempbx |= temp;
+
+ LCDIdIndex = XGI_GetLCDCapPtr1(pVBInfo);
+
+ tempax = pVBInfo->LCDCapList[LCDIdIndex].LCD_Capability;
+
+ if (((pVBInfo->VBType & VB_SIS302LV) ||
+ (pVBInfo->VBType & VB_XGI301C)) && (tempax & XGI_LCDDualLink))
+ tempbx |= SetLCDDualLink;
+
+ if ((pVBInfo->LCDResInfo == Panel_1400x1050) &&
+ (pVBInfo->VBInfo & SetCRT2ToLCD) && (resinfo == 9) &&
+ (!(tempbx & EnableScalingLCD)))
+ /*
+ * set to center in 1280x1024 LCDB
+ * for Panel_1400x1050
+ */
+ tempbx |= SetLCDtoNonExpanding;
+
+ if (pVBInfo->VBInfo & SetInSlaveMode) {
+ if (pVBInfo->VBInfo & SetNotSimuMode)
+ tempbx |= XGI_LCDVESATiming;
+ } else {
+ tempbx |= XGI_LCDVESATiming;
+ }
+
+ pVBInfo->LCDInfo = tempbx;
+
+ return 1;
+}
+
+unsigned char XGI_SearchModeID(unsigned short ModeNo,
+ unsigned short *ModeIdIndex)
+{
+ for (*ModeIdIndex = 0;; (*ModeIdIndex)++) {
+ if (XGI330_EModeIDTable[*ModeIdIndex].Ext_ModeID == ModeNo)
+ break;
+ if (XGI330_EModeIDTable[*ModeIdIndex].Ext_ModeID == 0xFF)
+ return 0;
+ }
+
+ return 1;
+}
+
+static unsigned char XG21GPIODataTransfer(unsigned char ujDate)
+{
+ unsigned char ujRet = 0;
+ unsigned char i = 0;
+
+ for (i = 0; i < 8; i++) {
+ ujRet <<= 1;
+ ujRet |= (ujDate >> i) & 1;
+ }
+
+ return ujRet;
+}
+
+/*----------------------------------------------------------------------------*/
+/* output */
+/* bl[5] : LVDS signal */
+/* bl[1] : LVDS backlight */
+/* bl[0] : LVDS VDD */
+/*----------------------------------------------------------------------------*/
+static unsigned char XGI_XG21GetPSCValue(struct vb_device_info *pVBInfo)
+{
+ unsigned char CR4A, temp;
+
+ CR4A = xgifb_reg_get(pVBInfo->P3d4, 0x4A);
+ xgifb_reg_and(pVBInfo->P3d4, 0x4A, ~0x23); /* enable GPIO write */
+
+ temp = xgifb_reg_get(pVBInfo->P3d4, 0x48);
+
+ temp = XG21GPIODataTransfer(temp);
+ temp &= 0x23;
+ xgifb_reg_set(pVBInfo->P3d4, 0x4A, CR4A);
+ return temp;
+}
+
+/*----------------------------------------------------------------------------*/
+/* output */
+/* bl[5] : LVDS signal */
+/* bl[1] : LVDS backlight */
+/* bl[0] : LVDS VDD */
+/*----------------------------------------------------------------------------*/
+static unsigned char XGI_XG27GetPSCValue(struct vb_device_info *pVBInfo)
+{
+ unsigned char CR4A, CRB4, temp;
+
+ CR4A = xgifb_reg_get(pVBInfo->P3d4, 0x4A);
+ xgifb_reg_and(pVBInfo->P3d4, 0x4A, ~0x0C); /* enable GPIO write */
+
+ temp = xgifb_reg_get(pVBInfo->P3d4, 0x48);
+
+ temp &= 0x0C;
+ temp >>= 2;
+ xgifb_reg_set(pVBInfo->P3d4, 0x4A, CR4A);
+ CRB4 = xgifb_reg_get(pVBInfo->P3d4, 0xB4);
+ temp |= ((CRB4 & 0x04) << 3);
+ return temp;
+}
+
+/*----------------------------------------------------------------------------*/
+/* input */
+/* bl[5] : 1;LVDS signal on */
+/* bl[1] : 1;LVDS backlight on */
+/* bl[0] : 1:LVDS VDD on */
+/* bh: 100000b : clear bit 5, to set bit5 */
+/* 000010b : clear bit 1, to set bit1 */
+/* 000001b : clear bit 0, to set bit0 */
+/*----------------------------------------------------------------------------*/
+static void XGI_XG21BLSignalVDD(unsigned short tempbh, unsigned short tempbl,
+ struct vb_device_info *pVBInfo)
+{
+ unsigned char CR4A, temp;
+
+ CR4A = xgifb_reg_get(pVBInfo->P3d4, 0x4A);
+ tempbh &= 0x23;
+ tempbl &= 0x23;
+ xgifb_reg_and(pVBInfo->P3d4, 0x4A, ~tempbh); /* enable GPIO write */
+
+ if (tempbh & 0x20) {
+ temp = (tempbl >> 4) & 0x02;
+
+ /* CR B4[1] */
+ xgifb_reg_and_or(pVBInfo->P3d4, 0xB4, ~0x02, temp);
+
+ }
+
+ temp = xgifb_reg_get(pVBInfo->P3d4, 0x48);
+
+ temp = XG21GPIODataTransfer(temp);
+ temp &= ~tempbh;
+ temp |= tempbl;
+ xgifb_reg_set(pVBInfo->P3d4, 0x48, temp);
+}
+
+static void XGI_XG27BLSignalVDD(unsigned short tempbh, unsigned short tempbl,
+ struct vb_device_info *pVBInfo)
+{
+ unsigned char CR4A, temp;
+ unsigned short tempbh0, tempbl0;
+
+ tempbh0 = tempbh;
+ tempbl0 = tempbl;
+ tempbh0 &= 0x20;
+ tempbl0 &= 0x20;
+ tempbh0 >>= 3;
+ tempbl0 >>= 3;
+
+ if (tempbh & 0x20) {
+ temp = (tempbl >> 4) & 0x02;
+
+ /* CR B4[1] */
+ xgifb_reg_and_or(pVBInfo->P3d4, 0xB4, ~0x02, temp);
+
+ }
+ xgifb_reg_and_or(pVBInfo->P3d4, 0xB4, ~tempbh0, tempbl0);
+
+ CR4A = xgifb_reg_get(pVBInfo->P3d4, 0x4A);
+ tempbh &= 0x03;
+ tempbl &= 0x03;
+ tempbh <<= 2;
+ tempbl <<= 2; /* GPIOC,GPIOD */
+ xgifb_reg_and(pVBInfo->P3d4, 0x4A, ~tempbh); /* enable GPIO write */
+ xgifb_reg_and_or(pVBInfo->P3d4, 0x48, ~tempbh, tempbl);
+}
+
+static void XGI_DisplayOn(struct xgifb_video_info *xgifb_info,
+ struct xgi_hw_device_info *pXGIHWDE,
+ struct vb_device_info *pVBInfo)
+{
+
+ xgifb_reg_and_or(pVBInfo->P3c4, 0x01, 0xDF, 0x00);
+ if (pXGIHWDE->jChipType == XG21) {
+ if (pVBInfo->IF_DEF_LVDS == 1) {
+ if (!(XGI_XG21GetPSCValue(pVBInfo) & 0x1)) {
+ /* LVDS VDD on */
+ XGI_XG21BLSignalVDD(0x01, 0x01, pVBInfo);
+ mdelay(xgifb_info->lvds_data.PSC_S2);
+ }
+ if (!(XGI_XG21GetPSCValue(pVBInfo) & 0x20))
+ /* LVDS signal on */
+ XGI_XG21BLSignalVDD(0x20, 0x20, pVBInfo);
+ mdelay(xgifb_info->lvds_data.PSC_S3);
+ /* LVDS backlight on */
+ XGI_XG21BLSignalVDD(0x02, 0x02, pVBInfo);
+ } else {
+ /* DVO/DVI signal on */
+ XGI_XG21BLSignalVDD(0x20, 0x20, pVBInfo);
+ }
+
+ }
+
+ if (pXGIHWDE->jChipType == XG27) {
+ if (pVBInfo->IF_DEF_LVDS == 1) {
+ if (!(XGI_XG27GetPSCValue(pVBInfo) & 0x1)) {
+ /* LVDS VDD on */
+ XGI_XG27BLSignalVDD(0x01, 0x01, pVBInfo);
+ mdelay(xgifb_info->lvds_data.PSC_S2);
+ }
+ if (!(XGI_XG27GetPSCValue(pVBInfo) & 0x20))
+ /* LVDS signal on */
+ XGI_XG27BLSignalVDD(0x20, 0x20, pVBInfo);
+ mdelay(xgifb_info->lvds_data.PSC_S3);
+ /* LVDS backlight on */
+ XGI_XG27BLSignalVDD(0x02, 0x02, pVBInfo);
+ } else {
+ /* DVO/DVI signal on */
+ XGI_XG27BLSignalVDD(0x20, 0x20, pVBInfo);
+ }
+
+ }
+}
+
+void XGI_DisplayOff(struct xgifb_video_info *xgifb_info,
+ struct xgi_hw_device_info *pXGIHWDE,
+ struct vb_device_info *pVBInfo)
+{
+
+ if (pXGIHWDE->jChipType == XG21) {
+ if (pVBInfo->IF_DEF_LVDS == 1) {
+ /* LVDS backlight off */
+ XGI_XG21BLSignalVDD(0x02, 0x00, pVBInfo);
+ mdelay(xgifb_info->lvds_data.PSC_S3);
+ } else {
+ /* DVO/DVI signal off */
+ XGI_XG21BLSignalVDD(0x20, 0x00, pVBInfo);
+ }
+ }
+
+ if (pXGIHWDE->jChipType == XG27) {
+ if ((XGI_XG27GetPSCValue(pVBInfo) & 0x2)) {
+ /* LVDS backlight off */
+ XGI_XG27BLSignalVDD(0x02, 0x00, pVBInfo);
+ mdelay(xgifb_info->lvds_data.PSC_S3);
+ }
+
+ if (pVBInfo->IF_DEF_LVDS == 0)
+ /* DVO/DVI signal off */
+ XGI_XG27BLSignalVDD(0x20, 0x00, pVBInfo);
+ }
+
+ xgifb_reg_and_or(pVBInfo->P3c4, 0x01, 0xDF, 0x20);
+}
+
+static void XGI_WaitDisply(struct vb_device_info *pVBInfo)
+{
+ while ((inb(pVBInfo->P3da) & 0x01))
+ break;
+
+ while (!(inb(pVBInfo->P3da) & 0x01))
+ break;
+}
+
+static void XGI_AutoThreshold(struct vb_device_info *pVBInfo)
+{
+ xgifb_reg_or(pVBInfo->Part1Port, 0x01, 0x40);
+}
+
+static void XGI_SaveCRT2Info(unsigned short ModeNo,
+ struct vb_device_info *pVBInfo)
+{
+ unsigned short temp1, temp2;
+
+ /* reserve CR34 for CRT1 Mode No */
+ xgifb_reg_set(pVBInfo->P3d4, 0x34, ModeNo);
+ temp1 = (pVBInfo->VBInfo & SetInSlaveMode) >> 8;
+ temp2 = ~(SetInSlaveMode >> 8);
+ xgifb_reg_and_or(pVBInfo->P3d4, 0x31, temp2, temp1);
+}
+
+static void XGI_GetCRT2ResInfo(unsigned short ModeIdIndex,
+ struct vb_device_info *pVBInfo)
+{
+ unsigned short xres, yres, modeflag, resindex;
+
+ resindex = XGI330_EModeIDTable[ModeIdIndex].Ext_RESINFO;
+ xres = XGI330_ModeResInfo[resindex].HTotal; /* xres->ax */
+ yres = XGI330_ModeResInfo[resindex].VTotal; /* yres->bx */
+ /* si+St_ModeFlag */
+ modeflag = XGI330_EModeIDTable[ModeIdIndex].Ext_ModeFlag;
+
+ if (modeflag & HalfDCLK)
+ xres *= 2;
+
+ if (modeflag & DoubleScanMode)
+ yres *= 2;
+
+ if (!(pVBInfo->VBInfo & SetCRT2ToLCD))
+ goto exit;
+
+ if (pVBInfo->LCDResInfo == Panel_1600x1200) {
+ if (!(pVBInfo->LCDInfo & XGI_LCDVESATiming)) {
+ if (yres == 1024)
+ yres = 1056;
+ }
+ }
+
+ if (pVBInfo->LCDResInfo == Panel_1280x1024) {
+ if (yres == 400)
+ yres = 405;
+ else if (yres == 350)
+ yres = 360;
+
+ if (pVBInfo->LCDInfo & XGI_LCDVESATiming) {
+ if (yres == 360)
+ yres = 375;
+ }
+ }
+
+ if (pVBInfo->LCDResInfo == Panel_1024x768) {
+ if (!(pVBInfo->LCDInfo & XGI_LCDVESATiming)) {
+ if (!(pVBInfo->LCDInfo & LCDNonExpanding)) {
+ if (yres == 350)
+ yres = 357;
+ else if (yres == 400)
+ yres = 420;
+ else if (yres == 480)
+ yres = 525;
+ }
+ }
+ }
+
+ if (xres == 720)
+ xres = 640;
+
+exit:
+ pVBInfo->VGAHDE = xres;
+ pVBInfo->HDE = xres;
+ pVBInfo->VGAVDE = yres;
+ pVBInfo->VDE = yres;
+}
+
+static unsigned char XGI_IsLCDDualLink(struct vb_device_info *pVBInfo)
+{
+
+ if ((pVBInfo->VBInfo & (SetCRT2ToLCD | XGI_SetCRT2ToLCDA)) &&
+ (pVBInfo->LCDInfo & SetLCDDualLink)) /* shampoo0129 */
+ return 1;
+
+ return 0;
+}
+
+static void XGI_GetRAMDAC2DATA(unsigned short ModeIdIndex,
+ unsigned short RefreshRateTableIndex,
+ struct vb_device_info *pVBInfo)
+{
+ unsigned short tempax, tempbx, temp1, temp2, modeflag = 0, tempcx,
+ CRT1Index;
+
+ pVBInfo->RVBHCMAX = 1;
+ pVBInfo->RVBHCFACT = 1;
+ modeflag = XGI330_EModeIDTable[ModeIdIndex].Ext_ModeFlag;
+ CRT1Index = XGI330_RefIndex[RefreshRateTableIndex].Ext_CRT1CRTC;
+ CRT1Index &= IndexMask;
+ temp1 = (unsigned short) XGI_CRT1Table[CRT1Index].CR[0];
+ temp2 = (unsigned short) XGI_CRT1Table[CRT1Index].CR[5];
+ tempax = (temp1 & 0xFF) | ((temp2 & 0x03) << 8);
+ tempbx = (unsigned short) XGI_CRT1Table[CRT1Index].CR[8];
+ tempcx = (unsigned short)
+ XGI_CRT1Table[CRT1Index].CR[14] << 8;
+ tempcx &= 0x0100;
+ tempcx <<= 2;
+ tempbx |= tempcx;
+ temp1 = (unsigned short) XGI_CRT1Table[CRT1Index].CR[9];
+
+ if (temp1 & 0x01)
+ tempbx |= 0x0100;
+
+ if (temp1 & 0x20)
+ tempbx |= 0x0200;
+ tempax += 5;
+
+ if (modeflag & Charx8Dot)
+ tempax *= 8;
+ else
+ tempax *= 9;
+
+ pVBInfo->VGAHT = tempax;
+ pVBInfo->HT = tempax;
+ tempbx++;
+ pVBInfo->VGAVT = tempbx;
+ pVBInfo->VT = tempbx;
+}
+
+static void XGI_GetCRT2Data(unsigned short ModeIdIndex,
+ unsigned short RefreshRateTableIndex,
+ struct vb_device_info *pVBInfo)
+{
+ unsigned short tempax = 0, tempbx = 0, modeflag, resinfo;
+
+ struct SiS_LCDData const *LCDPtr = NULL;
+
+ /* si+Ext_ResInfo */
+ modeflag = XGI330_EModeIDTable[ModeIdIndex].Ext_ModeFlag;
+ resinfo = XGI330_EModeIDTable[ModeIdIndex].Ext_RESINFO;
+ pVBInfo->NewFlickerMode = 0;
+ pVBInfo->RVBHRS = 50;
+
+ if (pVBInfo->VBInfo & SetCRT2ToRAMDAC) {
+ XGI_GetRAMDAC2DATA(ModeIdIndex, RefreshRateTableIndex, pVBInfo);
+ return;
+ }
+
+ if (pVBInfo->VBInfo & (SetCRT2ToLCD | XGI_SetCRT2ToLCDA)) {
+ LCDPtr = XGI_GetLcdPtr(XGI_LCDDataTable, ModeIdIndex,
+ pVBInfo);
+
+ pVBInfo->RVBHCMAX = LCDPtr->RVBHCMAX;
+ pVBInfo->RVBHCFACT = LCDPtr->RVBHCFACT;
+ pVBInfo->VGAHT = LCDPtr->VGAHT;
+ pVBInfo->VGAVT = LCDPtr->VGAVT;
+ pVBInfo->HT = LCDPtr->LCDHT;
+ pVBInfo->VT = LCDPtr->LCDVT;
+
+ if (pVBInfo->LCDResInfo == Panel_1024x768) {
+ tempax = 1024;
+ tempbx = 768;
+
+ if (!(pVBInfo->LCDInfo & XGI_LCDVESATiming)) {
+ if (pVBInfo->VGAVDE == 357)
+ tempbx = 527;
+ else if (pVBInfo->VGAVDE == 420)
+ tempbx = 620;
+ else if (pVBInfo->VGAVDE == 525)
+ tempbx = 775;
+ else if (pVBInfo->VGAVDE == 600)
+ tempbx = 775;
+ }
+ } else if (pVBInfo->LCDResInfo == Panel_1024x768x75) {
+ tempax = 1024;
+ tempbx = 768;
+ } else if (pVBInfo->LCDResInfo == Panel_1280x1024) {
+ tempax = 1280;
+ if (pVBInfo->VGAVDE == 360)
+ tempbx = 768;
+ else if (pVBInfo->VGAVDE == 375)
+ tempbx = 800;
+ else if (pVBInfo->VGAVDE == 405)
+ tempbx = 864;
+ else
+ tempbx = 1024;
+ } else if (pVBInfo->LCDResInfo == Panel_1280x1024x75) {
+ tempax = 1280;
+ tempbx = 1024;
+ } else if (pVBInfo->LCDResInfo == Panel_1280x960) {
+ tempax = 1280;
+ if (pVBInfo->VGAVDE == 350)
+ tempbx = 700;
+ else if (pVBInfo->VGAVDE == 400)
+ tempbx = 800;
+ else if (pVBInfo->VGAVDE == 1024)
+ tempbx = 960;
+ else
+ tempbx = 960;
+ } else if (pVBInfo->LCDResInfo == Panel_1400x1050) {
+ tempax = 1400;
+ tempbx = 1050;
+
+ if (pVBInfo->VGAVDE == 1024) {
+ tempax = 1280;
+ tempbx = 1024;
+ }
+ } else if (pVBInfo->LCDResInfo == Panel_1600x1200) {
+ tempax = 1600;
+ tempbx = 1200; /* alan 10/14/2003 */
+ if (!(pVBInfo->LCDInfo & XGI_LCDVESATiming)) {
+ if (pVBInfo->VGAVDE == 350)
+ tempbx = 875;
+ else if (pVBInfo->VGAVDE == 400)
+ tempbx = 1000;
+ }
+ }
+
+ if (pVBInfo->LCDInfo & LCDNonExpanding) {
+ tempax = pVBInfo->VGAHDE;
+ tempbx = pVBInfo->VGAVDE;
+ }
+
+ pVBInfo->HDE = tempax;
+ pVBInfo->VDE = tempbx;
+ return;
+ }
+
+ if (pVBInfo->VBInfo & (SetCRT2ToTV)) {
+ struct SiS_TVData const *TVPtr;
+
+ TVPtr = XGI_GetTVPtr(ModeIdIndex, RefreshRateTableIndex,
+ pVBInfo);
+
+ pVBInfo->RVBHCMAX = TVPtr->RVBHCMAX;
+ pVBInfo->RVBHCFACT = TVPtr->RVBHCFACT;
+ pVBInfo->VGAHT = TVPtr->VGAHT;
+ pVBInfo->VGAVT = TVPtr->VGAVT;
+ pVBInfo->HDE = TVPtr->TVHDE;
+ pVBInfo->VDE = TVPtr->TVVDE;
+ pVBInfo->RVBHRS = TVPtr->RVBHRS;
+ pVBInfo->NewFlickerMode = TVPtr->FlickerMode;
+
+ if (pVBInfo->VBInfo & SetCRT2ToHiVision) {
+ if (resinfo == 0x08)
+ pVBInfo->NewFlickerMode = 0x40;
+ else if (resinfo == 0x09)
+ pVBInfo->NewFlickerMode = 0x40;
+ else if (resinfo == 0x12)
+ pVBInfo->NewFlickerMode = 0x40;
+
+ if (pVBInfo->VGAVDE == 350)
+ pVBInfo->TVInfo |= TVSimuMode;
+
+ tempax = ExtHiTVHT;
+ tempbx = ExtHiTVVT;
+
+ if (pVBInfo->VBInfo & SetInSlaveMode) {
+ if (pVBInfo->TVInfo & TVSimuMode) {
+ tempax = StHiTVHT;
+ tempbx = StHiTVVT;
+
+ if (!(modeflag & Charx8Dot)) {
+ tempax = StHiTextTVHT;
+ tempbx = StHiTextTVVT;
+ }
+ }
+ }
+ } else if (pVBInfo->VBInfo & SetCRT2ToYPbPr525750) {
+ if (pVBInfo->TVInfo & TVSetYPbPr750p) {
+ tempax = YPbPrTV750pHT; /* Ext750pTVHT */
+ tempbx = YPbPrTV750pVT; /* Ext750pTVVT */
+ }
+
+ if (pVBInfo->TVInfo & TVSetYPbPr525p) {
+ tempax = YPbPrTV525pHT; /* Ext525pTVHT */
+ tempbx = YPbPrTV525pVT; /* Ext525pTVVT */
+ } else if (pVBInfo->TVInfo & TVSetYPbPr525i) {
+ tempax = YPbPrTV525iHT; /* Ext525iTVHT */
+ tempbx = YPbPrTV525iVT; /* Ext525iTVVT */
+ if (pVBInfo->TVInfo & NTSC1024x768)
+ tempax = NTSC1024x768HT;
+ }
+ } else {
+ tempax = PALHT;
+ tempbx = PALVT;
+ if (!(pVBInfo->TVInfo & TVSetPAL)) {
+ tempax = NTSCHT;
+ tempbx = NTSCVT;
+ if (pVBInfo->TVInfo & NTSC1024x768)
+ tempax = NTSC1024x768HT;
+ }
+ }
+
+ pVBInfo->HT = tempax;
+ pVBInfo->VT = tempbx;
+ }
+}
+
+static void XGI_SetCRT2VCLK(unsigned short ModeIdIndex,
+ unsigned short RefreshRateTableIndex,
+ struct vb_device_info *pVBInfo)
+{
+ unsigned char di_0, di_1, tempal;
+
+ tempal = XGI_GetVCLKPtr(RefreshRateTableIndex, ModeIdIndex, pVBInfo);
+ XGI_GetVCLKLen(tempal, &di_0, &di_1, pVBInfo);
+ XGI_GetLCDVCLKPtr(&di_0, &di_1, pVBInfo);
+
+ if (pVBInfo->VBType & VB_SIS301) { /* shampoo 0129 */
+ /* 301 */
+ xgifb_reg_set(pVBInfo->Part4Port, 0x0A, 0x10);
+ xgifb_reg_set(pVBInfo->Part4Port, 0x0B, di_1);
+ xgifb_reg_set(pVBInfo->Part4Port, 0x0A, di_0);
+ } else { /* 301b/302b/301lv/302lv */
+ xgifb_reg_set(pVBInfo->Part4Port, 0x0A, di_0);
+ xgifb_reg_set(pVBInfo->Part4Port, 0x0B, di_1);
+ }
+
+ xgifb_reg_set(pVBInfo->Part4Port, 0x00, 0x12);
+
+ if (pVBInfo->VBInfo & SetCRT2ToRAMDAC)
+ xgifb_reg_or(pVBInfo->Part4Port, 0x12, 0x28);
+ else
+ xgifb_reg_or(pVBInfo->Part4Port, 0x12, 0x08);
+}
+
+static unsigned short XGI_GetColorDepth(unsigned short ModeIdIndex)
+{
+ unsigned short ColorDepth[6] = { 1, 2, 4, 4, 6, 8 };
+ short index;
+ unsigned short modeflag;
+
+ modeflag = XGI330_EModeIDTable[ModeIdIndex].Ext_ModeFlag;
+ index = (modeflag & ModeTypeMask) - ModeEGA;
+
+ if (index < 0)
+ index = 0;
+
+ return ColorDepth[index];
+}
+
+static unsigned short XGI_GetOffset(unsigned short ModeNo,
+ unsigned short ModeIdIndex,
+ unsigned short RefreshRateTableIndex)
+{
+ unsigned short temp, colordepth, modeinfo, index, infoflag,
+ ColorDepth[] = { 0x01, 0x02, 0x04 };
+
+ modeinfo = XGI330_EModeIDTable[ModeIdIndex].Ext_ModeInfo;
+ infoflag = XGI330_RefIndex[RefreshRateTableIndex].Ext_InfoFlag;
+
+ index = (modeinfo >> 8) & 0xFF;
+
+ temp = XGI330_ScreenOffset[index];
+
+ if (infoflag & InterlaceMode)
+ temp <<= 1;
+
+ colordepth = XGI_GetColorDepth(ModeIdIndex);
+
+ if ((ModeNo >= 0x7C) && (ModeNo <= 0x7E)) {
+ temp = ModeNo - 0x7C;
+ colordepth = ColorDepth[temp];
+ temp = 0x6B;
+ if (infoflag & InterlaceMode)
+ temp <<= 1;
+ }
+ return temp * colordepth;
+}
+
+static void XGI_SetCRT2Offset(unsigned short ModeNo,
+ unsigned short ModeIdIndex,
+ unsigned short RefreshRateTableIndex,
+ struct vb_device_info *pVBInfo)
+{
+ unsigned short offset;
+ unsigned char temp;
+
+ if (pVBInfo->VBInfo & SetInSlaveMode)
+ return;
+
+ offset = XGI_GetOffset(ModeNo, ModeIdIndex, RefreshRateTableIndex);
+ temp = (unsigned char) (offset & 0xFF);
+ xgifb_reg_set(pVBInfo->Part1Port, 0x07, temp);
+ temp = (unsigned char) ((offset & 0xFF00) >> 8);
+ xgifb_reg_set(pVBInfo->Part1Port, 0x09, temp);
+ temp = (unsigned char) (((offset >> 3) & 0xFF) + 1);
+ xgifb_reg_set(pVBInfo->Part1Port, 0x03, temp);
+}
+
+static void XGI_SetCRT2FIFO(struct vb_device_info *pVBInfo)
+{
+ /* threshold high ,disable auto threshold */
+ xgifb_reg_set(pVBInfo->Part1Port, 0x01, 0x3B);
+ /* threshold low default 04h */
+ xgifb_reg_and_or(pVBInfo->Part1Port, 0x02, ~(0x3F), 0x04);
+}
+
+static void XGI_PreSetGroup1(unsigned short ModeNo, unsigned short ModeIdIndex,
+ unsigned short RefreshRateTableIndex,
+ struct vb_device_info *pVBInfo)
+{
+ u8 tempcx;
+
+ XGI_SetCRT2Offset(ModeNo, ModeIdIndex, RefreshRateTableIndex, pVBInfo);
+ XGI_SetCRT2FIFO(pVBInfo);
+
+ for (tempcx = 4; tempcx < 7; tempcx++)
+ xgifb_reg_set(pVBInfo->Part1Port, tempcx, 0x0);
+
+ xgifb_reg_set(pVBInfo->Part1Port, 0x50, 0x00);
+ xgifb_reg_set(pVBInfo->Part1Port, 0x02, 0x44); /* temp 0206 */
+}
+
+static void XGI_SetGroup1(unsigned short ModeIdIndex,
+ unsigned short RefreshRateTableIndex,
+ struct vb_device_info *pVBInfo)
+{
+ unsigned short temp = 0, tempax = 0, tempbx = 0, tempcx = 0,
+ pushbx = 0, CRT1Index, modeflag;
+
+ CRT1Index = XGI330_RefIndex[RefreshRateTableIndex].Ext_CRT1CRTC;
+ CRT1Index &= IndexMask;
+ modeflag = XGI330_EModeIDTable[ModeIdIndex].Ext_ModeFlag;
+
+ /* bainy change table name */
+ if (modeflag & HalfDCLK) {
+ /* BTVGA2HT 0x08,0x09 */
+ temp = (pVBInfo->VGAHT / 2 - 1) & 0x0FF;
+ xgifb_reg_set(pVBInfo->Part1Port, 0x08, temp);
+ temp = (((pVBInfo->VGAHT / 2 - 1) & 0xFF00) >> 8) << 4;
+ xgifb_reg_and_or(pVBInfo->Part1Port, 0x09, ~0x0F0, temp);
+ /* BTVGA2HDEE 0x0A,0x0C */
+ temp = (pVBInfo->VGAHDE / 2 + 16) & 0x0FF;
+ xgifb_reg_set(pVBInfo->Part1Port, 0x0A, temp);
+ tempcx = ((pVBInfo->VGAHT - pVBInfo->VGAHDE) / 2) >> 2;
+ pushbx = pVBInfo->VGAHDE / 2 + 16;
+ tempcx >>= 1;
+ tempbx = pushbx + tempcx; /* bx BTVGA@HRS 0x0B,0x0C */
+ tempcx += tempbx;
+
+ if (pVBInfo->VBInfo & SetCRT2ToRAMDAC) {
+ tempbx = XGI_CRT1Table[CRT1Index].CR[4];
+ tempbx |= ((XGI_CRT1Table[CRT1Index].CR[14] &
+ 0xC0) << 2);
+ tempbx = (tempbx - 3) << 3; /* (VGAHRS-3)*8 */
+ tempcx = XGI_CRT1Table[CRT1Index].CR[5];
+ tempcx &= 0x1F;
+ temp = XGI_CRT1Table[CRT1Index].CR[15];
+ temp = (temp & 0x04) << (5 - 2); /* VGAHRE D[5] */
+ tempcx = ((tempcx | temp) - 3) << 3; /* (VGAHRE-3)*8 */
+ }
+
+ tempbx += 4;
+ tempcx += 4;
+
+ if (tempcx > (pVBInfo->VGAHT / 2))
+ tempcx = pVBInfo->VGAHT / 2;
+
+ temp = tempbx & 0x00FF;
+
+ xgifb_reg_set(pVBInfo->Part1Port, 0x0B, temp);
+ } else {
+ temp = (pVBInfo->VGAHT - 1) & 0x0FF; /* BTVGA2HT 0x08,0x09 */
+ xgifb_reg_set(pVBInfo->Part1Port, 0x08, temp);
+ temp = (((pVBInfo->VGAHT - 1) & 0xFF00) >> 8) << 4;
+ xgifb_reg_and_or(pVBInfo->Part1Port, 0x09, ~0x0F0, temp);
+ /* BTVGA2HDEE 0x0A,0x0C */
+ temp = (pVBInfo->VGAHDE + 16) & 0x0FF;
+ xgifb_reg_set(pVBInfo->Part1Port, 0x0A, temp);
+ tempcx = (pVBInfo->VGAHT - pVBInfo->VGAHDE) >> 2; /* cx */
+ pushbx = pVBInfo->VGAHDE + 16;
+ tempcx >>= 1;
+ tempbx = pushbx + tempcx; /* bx BTVGA@HRS 0x0B,0x0C */
+ tempcx += tempbx;
+
+ if (pVBInfo->VBInfo & SetCRT2ToRAMDAC) {
+ tempbx = XGI_CRT1Table[CRT1Index].CR[3];
+ tempbx |= ((XGI_CRT1Table[CRT1Index].CR[5] &
+ 0xC0) << 2);
+ tempbx = (tempbx - 3) << 3; /* (VGAHRS-3)*8 */
+ tempcx = XGI_CRT1Table[CRT1Index].CR[4];
+ tempcx &= 0x1F;
+ temp = XGI_CRT1Table[CRT1Index].CR[6];
+ temp = (temp & 0x04) << (5 - 2); /* VGAHRE D[5] */
+ tempcx = ((tempcx | temp) - 3) << 3; /* (VGAHRE-3)*8 */
+ tempbx += 16;
+ tempcx += 16;
+ }
+
+ if (tempcx > pVBInfo->VGAHT)
+ tempcx = pVBInfo->VGAHT;
+
+ temp = tempbx & 0x00FF;
+ xgifb_reg_set(pVBInfo->Part1Port, 0x0B, temp);
+ }
+
+ tempax = (tempax & 0x00FF) | (tempbx & 0xFF00);
+ tempbx = pushbx;
+ tempbx = (tempbx & 0x00FF) | ((tempbx & 0xFF00) << 4);
+ tempax |= (tempbx & 0xFF00);
+ temp = (tempax & 0xFF00) >> 8;
+ xgifb_reg_set(pVBInfo->Part1Port, 0x0C, temp);
+ temp = tempcx & 0x00FF;
+ xgifb_reg_set(pVBInfo->Part1Port, 0x0D, temp);
+ tempcx = (pVBInfo->VGAVT - 1);
+ temp = tempcx & 0x00FF;
+
+ xgifb_reg_set(pVBInfo->Part1Port, 0x0E, temp);
+ tempbx = pVBInfo->VGAVDE - 1;
+ temp = tempbx & 0x00FF;
+ xgifb_reg_set(pVBInfo->Part1Port, 0x0F, temp);
+ temp = ((tempbx & 0xFF00) << 3) >> 8;
+ temp |= ((tempcx & 0xFF00) >> 8);
+ xgifb_reg_set(pVBInfo->Part1Port, 0x12, temp);
+
+ /* BTVGA2VRS 0x10,0x11 */
+ tempbx = (pVBInfo->VGAVT + pVBInfo->VGAVDE) >> 1;
+ /* BTVGA2VRE 0x11 */
+ tempcx = ((pVBInfo->VGAVT - pVBInfo->VGAVDE) >> 4) + tempbx + 1;
+
+ if (pVBInfo->VBInfo & SetCRT2ToRAMDAC) {
+ tempbx = XGI_CRT1Table[CRT1Index].CR[10];
+ temp = XGI_CRT1Table[CRT1Index].CR[9];
+
+ if (temp & 0x04)
+ tempbx |= 0x0100;
+
+ if (temp & 0x080)
+ tempbx |= 0x0200;
+
+ temp = XGI_CRT1Table[CRT1Index].CR[14];
+
+ if (temp & 0x08)
+ tempbx |= 0x0400;
+
+ temp = XGI_CRT1Table[CRT1Index].CR[11];
+ tempcx = (tempcx & 0xFF00) | (temp & 0x00FF);
+ }
+
+ temp = tempbx & 0x00FF;
+ xgifb_reg_set(pVBInfo->Part1Port, 0x10, temp);
+ temp = ((tempbx & 0xFF00) >> 8) << 4;
+ temp = ((tempcx & 0x000F) | (temp));
+ xgifb_reg_set(pVBInfo->Part1Port, 0x11, temp);
+ tempax = 0;
+
+ if (modeflag & DoubleScanMode)
+ tempax |= 0x80;
+
+ if (modeflag & HalfDCLK)
+ tempax |= 0x40;
+
+ xgifb_reg_and_or(pVBInfo->Part1Port, 0x2C, ~0x0C0, tempax);
+}
+
+static unsigned short XGI_GetVGAHT2(struct vb_device_info *pVBInfo)
+{
+ unsigned long tempax, tempbx;
+
+ tempbx = ((pVBInfo->VGAVT - pVBInfo->VGAVDE) * pVBInfo->RVBHCMAX)
+ & 0xFFFF;
+ tempax = (pVBInfo->VT - pVBInfo->VDE) * pVBInfo->RVBHCFACT;
+ tempax = (tempax * pVBInfo->HT) / tempbx;
+
+ return (unsigned short) tempax;
+}
+
+static void XGI_SetLockRegs(unsigned short ModeNo, unsigned short ModeIdIndex,
+ struct vb_device_info *pVBInfo)
+{
+ unsigned short push1, push2, tempax, tempbx = 0, tempcx, temp, resinfo,
+ modeflag;
+
+ /* si+Ext_ResInfo */
+ modeflag = XGI330_EModeIDTable[ModeIdIndex].Ext_ModeFlag;
+ resinfo = XGI330_EModeIDTable[ModeIdIndex].Ext_RESINFO;
+
+ if (!(pVBInfo->VBInfo & SetInSlaveMode))
+ return;
+
+ temp = 0xFF; /* set MAX HT */
+ xgifb_reg_set(pVBInfo->Part1Port, 0x03, temp);
+ tempcx = 0x08;
+
+ if (pVBInfo->VBType & (VB_SIS301LV | VB_SIS302LV | VB_XGI301C))
+ modeflag |= Charx8Dot;
+
+ tempax = pVBInfo->VGAHDE; /* 0x04 Horizontal Display End */
+
+ if (modeflag & HalfDCLK)
+ tempax >>= 1;
+
+ tempax = (tempax / tempcx) - 1;
+ tempbx |= ((tempax & 0x00FF) << 8);
+ temp = tempax & 0x00FF;
+ xgifb_reg_set(pVBInfo->Part1Port, 0x04, temp);
+
+ temp = (tempbx & 0xFF00) >> 8;
+
+ if (pVBInfo->VBInfo & SetCRT2ToTV) {
+ if (!(pVBInfo->VBType & (VB_SIS301B | VB_SIS302B | VB_SIS301LV
+ | VB_SIS302LV | VB_XGI301C)))
+ temp += 2;
+
+ if ((pVBInfo->VBInfo & SetCRT2ToHiVision) &&
+ !(pVBInfo->VBType & VB_SIS301LV) && (resinfo == 7))
+ temp -= 2;
+ }
+
+ /* 0x05 Horizontal Display Start */
+ xgifb_reg_set(pVBInfo->Part1Port, 0x05, temp);
+ /* 0x06 Horizontal Blank end */
+ xgifb_reg_set(pVBInfo->Part1Port, 0x06, 0x03);
+
+ if (!(pVBInfo->VBInfo & DisableCRT2Display)) { /* 030226 bainy */
+ if (pVBInfo->VBInfo & SetCRT2ToTV)
+ tempax = pVBInfo->VGAHT;
+ else
+ tempax = XGI_GetVGAHT2(pVBInfo);
+ }
+
+ if (tempax >= pVBInfo->VGAHT)
+ tempax = pVBInfo->VGAHT;
+
+ if (modeflag & HalfDCLK)
+ tempax >>= 1;
+
+ tempax = (tempax / tempcx) - 5;
+ tempcx = tempax; /* 20030401 0x07 horizontal Retrace Start */
+ if (pVBInfo->VBInfo & SetCRT2ToHiVision) {
+ temp = (tempbx & 0x00FF) - 1;
+ if (!(modeflag & HalfDCLK)) {
+ temp -= 6;
+ if (pVBInfo->TVInfo & TVSimuMode) {
+ temp -= 4;
+ temp -= 10;
+ }
+ }
+ } else {
+ tempbx = (tempbx & 0xFF00) >> 8;
+ tempcx = (tempcx + tempbx) >> 1;
+ temp = (tempcx & 0x00FF) + 2;
+
+ if (pVBInfo->VBInfo & SetCRT2ToTV) {
+ temp -= 1;
+ if (!(modeflag & HalfDCLK)) {
+ if ((modeflag & Charx8Dot)) {
+ temp += 4;
+ if (pVBInfo->VGAHDE >= 800)
+ temp -= 6;
+ }
+ }
+ } else if (!(modeflag & HalfDCLK)) {
+ temp -= 4;
+ if (pVBInfo->LCDResInfo != Panel_1280x960 &&
+ pVBInfo->VGAHDE >= 800) {
+ temp -= 7;
+ if (pVBInfo->VGAHDE >= 1280 &&
+ pVBInfo->LCDResInfo != Panel_1280x960 &&
+ (pVBInfo->LCDInfo & LCDNonExpanding))
+ temp += 28;
+ }
+ }
+ }
+
+ /* 0x07 Horizontal Retrace Start */
+ xgifb_reg_set(pVBInfo->Part1Port, 0x07, temp);
+ /* 0x08 Horizontal Retrace End */
+ xgifb_reg_set(pVBInfo->Part1Port, 0x08, 0);
+
+ if (pVBInfo->VBInfo & SetCRT2ToTV) {
+ if (pVBInfo->TVInfo & TVSimuMode) {
+ if (ModeNo == 0x50) {
+ if (pVBInfo->TVInfo == SetNTSCTV) {
+ xgifb_reg_set(pVBInfo->Part1Port,
+ 0x07, 0x30);
+ xgifb_reg_set(pVBInfo->Part1Port,
+ 0x08, 0x03);
+ } else {
+ xgifb_reg_set(pVBInfo->Part1Port,
+ 0x07, 0x2f);
+ xgifb_reg_set(pVBInfo->Part1Port,
+ 0x08, 0x02);
+ }
+ }
+ }
+ }
+
+ xgifb_reg_set(pVBInfo->Part1Port, 0x18, 0x03); /* 0x18 SR0B */
+ xgifb_reg_and_or(pVBInfo->Part1Port, 0x19, 0xF0, 0x00);
+ xgifb_reg_set(pVBInfo->Part1Port, 0x09, 0xFF); /* 0x09 Set Max VT */
+
+ tempbx = pVBInfo->VGAVT;
+ push1 = tempbx;
+ tempcx = 0x121;
+ tempbx = pVBInfo->VGAVDE; /* 0x0E Virtical Display End */
+
+ if (tempbx == 357)
+ tempbx = 350;
+ if (tempbx == 360)
+ tempbx = 350;
+ if (tempbx == 375)
+ tempbx = 350;
+ if (tempbx == 405)
+ tempbx = 400;
+ if (tempbx == 525)
+ tempbx = 480;
+
+ push2 = tempbx;
+
+ if (pVBInfo->VBInfo & SetCRT2ToLCD) {
+ if (pVBInfo->LCDResInfo == Panel_1024x768) {
+ if (!(pVBInfo->LCDInfo & XGI_LCDVESATiming)) {
+ if (tempbx == 350)
+ tempbx += 5;
+ if (tempbx == 480)
+ tempbx += 5;
+ }
+ }
+ }
+ tempbx--;
+ tempbx--;
+ temp = tempbx & 0x00FF;
+ /* 0x10 vertical Blank Start */
+ xgifb_reg_set(pVBInfo->Part1Port, 0x10, temp);
+ tempbx = push2;
+ tempbx--;
+ temp = tempbx & 0x00FF;
+ xgifb_reg_set(pVBInfo->Part1Port, 0x0E, temp);
+
+ if (tempbx & 0x0100)
+ tempcx |= 0x0002;
+
+ tempax = 0x000B;
+
+ if (modeflag & DoubleScanMode)
+ tempax |= 0x08000;
+
+ if (tempbx & 0x0200)
+ tempcx |= 0x0040;
+
+ temp = (tempax & 0xFF00) >> 8;
+ xgifb_reg_set(pVBInfo->Part1Port, 0x0B, temp);
+
+ if (tempbx & 0x0400)
+ tempcx |= 0x0600;
+
+ /* 0x11 Vertival Blank End */
+ xgifb_reg_set(pVBInfo->Part1Port, 0x11, 0x00);
+
+ tempax = push1;
+ tempax -= tempbx; /* 0x0C Vertical Retrace Start */
+ tempax >>= 2;
+ push1 = tempax; /* push ax */
+
+ if (resinfo != 0x09) {
+ tempax <<= 1;
+ tempbx += tempax;
+ }
+
+ if (pVBInfo->VBInfo & SetCRT2ToHiVision) {
+ if ((pVBInfo->VBType & VB_SIS301LV) &&
+ !(pVBInfo->TVInfo & TVSetHiVision)) {
+ if ((pVBInfo->TVInfo & TVSimuMode) &&
+ (pVBInfo->TVInfo & TVSetPAL)) {
+ if (!(pVBInfo->VBType & VB_SIS301LV) ||
+ !(pVBInfo->TVInfo &
+ (TVSetYPbPr525p |
+ TVSetYPbPr750p |
+ TVSetHiVision)))
+ tempbx += 40;
+ }
+ } else {
+ tempbx -= 10;
+ }
+ } else if (pVBInfo->TVInfo & TVSimuMode) {
+ if (pVBInfo->TVInfo & TVSetPAL) {
+ if (pVBInfo->VBType & VB_SIS301LV) {
+ if (!(pVBInfo->TVInfo &
+ (TVSetYPbPr525p |
+ TVSetYPbPr750p |
+ TVSetHiVision)))
+ tempbx += 40;
+ } else {
+ tempbx += 40;
+ }
+ }
+ }
+ tempax = push1;
+ tempax >>= 2;
+ tempax++;
+ tempax += tempbx;
+ push1 = tempax; /* push ax */
+
+ if ((pVBInfo->TVInfo & TVSetPAL)) {
+ if (tempbx <= 513) {
+ if (tempax >= 513)
+ tempbx = 513;
+ }
+ }
+
+ temp = tempbx & 0x00FF;
+ xgifb_reg_set(pVBInfo->Part1Port, 0x0C, temp);
+ tempbx--;
+ temp = tempbx & 0x00FF;
+ xgifb_reg_set(pVBInfo->Part1Port, 0x10, temp);
+
+ if (tempbx & 0x0100)
+ tempcx |= 0x0008;
+
+ if (tempbx & 0x0200)
+ xgifb_reg_and_or(pVBInfo->Part1Port, 0x0B, 0x0FF, 0x20);
+
+ tempbx++;
+
+ if (tempbx & 0x0100)
+ tempcx |= 0x0004;
+
+ if (tempbx & 0x0200)
+ tempcx |= 0x0080;
+
+ if (tempbx & 0x0400)
+ tempcx |= 0x0C00;
+
+ tempbx = push1; /* pop ax */
+ temp = tempbx & 0x00FF;
+ temp &= 0x0F;
+ /* 0x0D vertical Retrace End */
+ xgifb_reg_set(pVBInfo->Part1Port, 0x0D, temp);
+
+ if (tempbx & 0x0010)
+ tempcx |= 0x2000;
+
+ temp = tempcx & 0x00FF;
+ xgifb_reg_set(pVBInfo->Part1Port, 0x0A, temp); /* 0x0A CR07 */
+ temp = (tempcx & 0x0FF00) >> 8;
+ xgifb_reg_set(pVBInfo->Part1Port, 0x17, temp); /* 0x17 SR0A */
+ tempax = modeflag;
+ temp = (tempax & 0xFF00) >> 8;
+
+ temp = (temp >> 1) & 0x09;
+
+ if (pVBInfo->VBType & (VB_SIS301LV | VB_SIS302LV | VB_XGI301C))
+ temp |= 0x01;
+
+ xgifb_reg_set(pVBInfo->Part1Port, 0x16, temp); /* 0x16 SR01 */
+ xgifb_reg_set(pVBInfo->Part1Port, 0x0F, 0); /* 0x0F CR14 */
+ xgifb_reg_set(pVBInfo->Part1Port, 0x12, 0); /* 0x12 CR17 */
+
+ if (pVBInfo->LCDInfo & LCDRGB18Bit)
+ temp = 0x80;
+ else
+ temp = 0x00;
+
+ xgifb_reg_set(pVBInfo->Part1Port, 0x1A, temp); /* 0x1A SR0E */
+}
+
+static void XGI_SetGroup2(unsigned short ModeNo, unsigned short ModeIdIndex,
+ struct vb_device_info *pVBInfo)
+{
+ unsigned short i, j, tempax, tempbx, tempcx, temp, push1, push2,
+ modeflag;
+ unsigned char const *TimingPoint;
+
+ unsigned long longtemp, tempeax, tempebx, temp2, tempecx;
+
+ /* si+Ext_ResInfo */
+ modeflag = XGI330_EModeIDTable[ModeIdIndex].Ext_ModeFlag;
+
+ tempax = 0;
+
+ if (!(pVBInfo->VBInfo & SetCRT2ToAVIDEO))
+ tempax |= 0x0800;
+
+ if (!(pVBInfo->VBInfo & SetCRT2ToSVIDEO))
+ tempax |= 0x0400;
+
+ if (pVBInfo->VBInfo & SetCRT2ToSCART)
+ tempax |= 0x0200;
+
+ if (!(pVBInfo->TVInfo & TVSetPAL))
+ tempax |= 0x1000;
+
+ if (pVBInfo->VBInfo & SetCRT2ToHiVision)
+ tempax |= 0x0100;
+
+ if (pVBInfo->TVInfo & (TVSetYPbPr525p | TVSetYPbPr750p))
+ tempax &= 0xfe00;
+
+ tempax = (tempax & 0xff00) >> 8;
+
+ xgifb_reg_set(pVBInfo->Part2Port, 0x0, tempax);
+ TimingPoint = XGI330_NTSCTiming;
+
+ if (pVBInfo->TVInfo & TVSetPAL)
+ TimingPoint = XGI330_PALTiming;
+
+ if (pVBInfo->VBInfo & SetCRT2ToHiVision) {
+ TimingPoint = XGI330_HiTVExtTiming;
+
+ if (pVBInfo->VBInfo & SetInSlaveMode)
+ TimingPoint = XGI330_HiTVSt2Timing;
+
+ if (pVBInfo->SetFlag & TVSimuMode)
+ TimingPoint = XGI330_HiTVSt1Timing;
+
+ if (!(modeflag & Charx8Dot))
+ TimingPoint = XGI330_HiTVTextTiming;
+ }
+
+ if (pVBInfo->VBInfo & SetCRT2ToYPbPr525750) {
+ if (pVBInfo->TVInfo & TVSetYPbPr525i)
+ TimingPoint = XGI330_YPbPr525iTiming;
+
+ if (pVBInfo->TVInfo & TVSetYPbPr525p)
+ TimingPoint = XGI330_YPbPr525pTiming;
+
+ if (pVBInfo->TVInfo & TVSetYPbPr750p)
+ TimingPoint = XGI330_YPbPr750pTiming;
+ }
+
+ for (i = 0x01, j = 0; i <= 0x2D; i++, j++)
+ xgifb_reg_set(pVBInfo->Part2Port, i, TimingPoint[j]);
+
+ for (i = 0x39; i <= 0x45; i++, j++)
+ /* di->temp2[j] */
+ xgifb_reg_set(pVBInfo->Part2Port, i, TimingPoint[j]);
+
+ if (pVBInfo->VBInfo & SetCRT2ToTV)
+ xgifb_reg_and_or(pVBInfo->Part2Port, 0x3A, 0x1F, 0x00);
+
+ temp = pVBInfo->NewFlickerMode;
+ temp &= 0x80;
+ xgifb_reg_and_or(pVBInfo->Part2Port, 0x0A, 0xFF, temp);
+
+ if (pVBInfo->TVInfo & TVSetPAL)
+ tempax = 520;
+ else
+ tempax = 440;
+
+ if (pVBInfo->VDE <= tempax) {
+ tempax -= pVBInfo->VDE;
+ tempax >>= 2;
+ tempax = (tempax & 0x00FF) | ((tempax & 0x00FF) << 8);
+ push1 = tempax;
+ temp = (tempax & 0xFF00) >> 8;
+ temp += (unsigned short) TimingPoint[0];
+
+ if (pVBInfo->VBType & (VB_SIS301B | VB_SIS302B | VB_SIS301LV
+ | VB_SIS302LV | VB_XGI301C)) {
+ if (pVBInfo->VBInfo & (SetCRT2ToAVIDEO
+ | SetCRT2ToSVIDEO | SetCRT2ToSCART
+ | SetCRT2ToYPbPr525750)) {
+ tempcx = pVBInfo->VGAHDE;
+ if (tempcx >= 1024) {
+ temp = 0x17; /* NTSC */
+ if (pVBInfo->TVInfo & TVSetPAL)
+ temp = 0x19; /* PAL */
+ }
+ }
+ }
+
+ xgifb_reg_set(pVBInfo->Part2Port, 0x01, temp);
+ tempax = push1;
+ temp = (tempax & 0xFF00) >> 8;
+ temp += TimingPoint[1];
+
+ if (pVBInfo->VBType & (VB_SIS301B | VB_SIS302B | VB_SIS301LV
+ | VB_SIS302LV | VB_XGI301C)) {
+ if ((pVBInfo->VBInfo & (SetCRT2ToAVIDEO
+ | SetCRT2ToSVIDEO | SetCRT2ToSCART
+ | SetCRT2ToYPbPr525750))) {
+ tempcx = pVBInfo->VGAHDE;
+ if (tempcx >= 1024) {
+ temp = 0x1D; /* NTSC */
+ if (pVBInfo->TVInfo & TVSetPAL)
+ temp = 0x52; /* PAL */
+ }
+ }
+ }
+ xgifb_reg_set(pVBInfo->Part2Port, 0x02, temp);
+ }
+
+ /* 301b */
+ tempcx = pVBInfo->HT;
+
+ if (XGI_IsLCDDualLink(pVBInfo))
+ tempcx >>= 1;
+
+ tempcx -= 2;
+ temp = tempcx & 0x00FF;
+ xgifb_reg_set(pVBInfo->Part2Port, 0x1B, temp);
+
+ temp = (tempcx & 0xFF00) >> 8;
+ xgifb_reg_and_or(pVBInfo->Part2Port, 0x1D, ~0x0F, temp);
+
+ tempcx = pVBInfo->HT >> 1;
+ push1 = tempcx; /* push cx */
+ tempcx += 7;
+
+ if (pVBInfo->VBInfo & SetCRT2ToHiVision)
+ tempcx -= 4;
+
+ temp = tempcx & 0x00FF;
+ temp <<= 4;
+ xgifb_reg_and_or(pVBInfo->Part2Port, 0x22, 0x0F, temp);
+
+ tempbx = TimingPoint[j] | ((TimingPoint[j + 1]) << 8);
+ tempbx += tempcx;
+ push2 = tempbx;
+ temp = tempbx & 0x00FF;
+ xgifb_reg_set(pVBInfo->Part2Port, 0x24, temp);
+ temp = (tempbx & 0xFF00) >> 8;
+ temp <<= 4;
+ xgifb_reg_and_or(pVBInfo->Part2Port, 0x25, 0x0F, temp);
+
+ tempbx = push2;
+ tempbx = tempbx + 8;
+ if (pVBInfo->VBInfo & SetCRT2ToHiVision) {
+ tempbx = tempbx - 4;
+ tempcx = tempbx;
+ }
+
+ temp = (tempbx & 0x00FF) << 4;
+ xgifb_reg_and_or(pVBInfo->Part2Port, 0x29, 0x0F, temp);
+
+ j += 2;
+ tempcx += (TimingPoint[j] | ((TimingPoint[j + 1]) << 8));
+ temp = tempcx & 0x00FF;
+ xgifb_reg_set(pVBInfo->Part2Port, 0x27, temp);
+ temp = ((tempcx & 0xFF00) >> 8) << 4;
+ xgifb_reg_and_or(pVBInfo->Part2Port, 0x28, 0x0F, temp);
+
+ tempcx += 8;
+ if (pVBInfo->VBInfo & SetCRT2ToHiVision)
+ tempcx -= 4;
+
+ temp = tempcx & 0xFF;
+ temp <<= 4;
+ xgifb_reg_and_or(pVBInfo->Part2Port, 0x2A, 0x0F, temp);
+
+ tempcx = push1; /* pop cx */
+ j += 2;
+ temp = TimingPoint[j] | ((TimingPoint[j + 1]) << 8);
+ tempcx -= temp;
+ temp = tempcx & 0x00FF;
+ temp <<= 4;
+ xgifb_reg_and_or(pVBInfo->Part2Port, 0x2D, 0x0F, temp);
+
+ tempcx -= 11;
+
+ if (!(pVBInfo->VBInfo & SetCRT2ToTV)) {
+ tempax = XGI_GetVGAHT2(pVBInfo);
+ tempcx = tempax - 1;
+ }
+ temp = tempcx & 0x00FF;
+ xgifb_reg_set(pVBInfo->Part2Port, 0x2E, temp);
+
+ tempbx = pVBInfo->VDE;
+
+ if (pVBInfo->VGAVDE == 360)
+ tempbx = 746;
+ if (pVBInfo->VGAVDE == 375)
+ tempbx = 746;
+ if (pVBInfo->VGAVDE == 405)
+ tempbx = 853;
+
+ if (pVBInfo->VBInfo & SetCRT2ToTV) {
+ if (pVBInfo->VBType &
+ (VB_SIS301LV | VB_SIS302LV | VB_XGI301C)) {
+ if (!(pVBInfo->TVInfo &
+ (TVSetYPbPr525p | TVSetYPbPr750p)))
+ tempbx >>= 1;
+ } else
+ tempbx >>= 1;
+ }
+
+ tempbx -= 2;
+ temp = tempbx & 0x00FF;
+
+ if (pVBInfo->VBInfo & SetCRT2ToHiVision) {
+ if (pVBInfo->VBType & VB_SIS301LV) {
+ if (pVBInfo->TVInfo & TVSetHiVision) {
+ if (pVBInfo->VBInfo & SetInSlaveMode) {
+ if (ModeNo == 0x2f)
+ temp += 1;
+ }
+ }
+ } else if (pVBInfo->VBInfo & SetInSlaveMode) {
+ if (ModeNo == 0x2f)
+ temp += 1;
+ }
+ }
+
+ xgifb_reg_set(pVBInfo->Part2Port, 0x2F, temp);
+
+ temp = (tempcx & 0xFF00) >> 8;
+ temp |= ((tempbx & 0xFF00) >> 8) << 6;
+
+ if (!(pVBInfo->VBInfo & SetCRT2ToHiVision)) {
+ if (pVBInfo->VBType & VB_SIS301LV) {
+ if (pVBInfo->TVInfo & TVSetHiVision) {
+ temp |= 0x10;
+
+ if (!(pVBInfo->VBInfo & SetCRT2ToSVIDEO))
+ temp |= 0x20;
+ }
+ } else {
+ temp |= 0x10;
+ if (!(pVBInfo->VBInfo & SetCRT2ToSVIDEO))
+ temp |= 0x20;
+ }
+ }
+
+ xgifb_reg_set(pVBInfo->Part2Port, 0x30, temp);
+
+ if (pVBInfo->VBType & (VB_SIS301B | VB_SIS302B | VB_SIS301LV
+ | VB_SIS302LV | VB_XGI301C)) { /* TV gatingno */
+ tempbx = pVBInfo->VDE;
+ tempcx = tempbx - 2;
+
+ if (pVBInfo->VBInfo & SetCRT2ToTV) {
+ if (!(pVBInfo->TVInfo & (TVSetYPbPr525p
+ | TVSetYPbPr750p)))
+ tempbx >>= 1;
+ }
+
+ if (pVBInfo->VBType & (VB_SIS302LV | VB_XGI301C)) {
+ temp = 0;
+ if (tempcx & 0x0400)
+ temp |= 0x20;
+
+ if (tempbx & 0x0400)
+ temp |= 0x40;
+
+ xgifb_reg_set(pVBInfo->Part4Port, 0x10, temp);
+ }
+
+ temp = (((tempbx - 3) & 0x0300) >> 8) << 5;
+ xgifb_reg_set(pVBInfo->Part2Port, 0x46, temp);
+ temp = (tempbx - 3) & 0x00FF;
+ xgifb_reg_set(pVBInfo->Part2Port, 0x47, temp);
+ }
+
+ tempbx = tempbx & 0x00FF;
+
+ if (!(modeflag & HalfDCLK)) {
+ tempcx = pVBInfo->VGAHDE;
+ if (tempcx >= pVBInfo->HDE) {
+ tempbx |= 0x2000;
+ tempax &= 0x00FF;
+ }
+ }
+
+ tempcx = 0x0101;
+
+ if (pVBInfo->VBInfo & SetCRT2ToTV) { /*301b*/
+ if (pVBInfo->VGAHDE >= 1024) {
+ tempcx = 0x1920;
+ if (pVBInfo->VGAHDE >= 1280) {
+ tempcx = 0x1420;
+ tempbx = tempbx & 0xDFFF;
+ }
+ }
+ }
+
+ if (!(tempbx & 0x2000)) {
+ if (modeflag & HalfDCLK)
+ tempcx = (tempcx & 0xFF00) | ((tempcx & 0x00FF) << 1);
+
+ push1 = tempbx;
+ tempeax = pVBInfo->VGAHDE;
+ tempebx = (tempcx & 0xFF00) >> 8;
+ longtemp = tempeax * tempebx;
+ tempecx = tempcx & 0x00FF;
+ longtemp = longtemp / tempecx;
+
+ /* 301b */
+ tempecx = 8 * 1024;
+
+ if (pVBInfo->VBType & (VB_SIS301B | VB_SIS302B | VB_SIS301LV
+ | VB_SIS302LV | VB_XGI301C)) {
+ tempecx = tempecx * 8;
+ }
+
+ longtemp = longtemp * tempecx;
+ tempecx = pVBInfo->HDE;
+ temp2 = longtemp % tempecx;
+ tempeax = longtemp / tempecx;
+ if (temp2 != 0)
+ tempeax += 1;
+
+ tempax = (unsigned short) tempeax;
+
+ /* 301b */
+ if (pVBInfo->VBType & (VB_SIS301B | VB_SIS302B | VB_SIS301LV
+ | VB_SIS302LV | VB_XGI301C)) {
+ tempcx = ((tempax & 0xFF00) >> 5) >> 8;
+ }
+ /* end 301b */
+
+ tempbx = push1;
+ tempbx = (unsigned short) (((tempeax & 0x0000FF00) & 0x1F00)
+ | (tempbx & 0x00FF));
+ tempax = (unsigned short) (((tempeax & 0x000000FF) << 8)
+ | (tempax & 0x00FF));
+ temp = (tempax & 0xFF00) >> 8;
+ } else {
+ temp = (tempax & 0x00FF) >> 8;
+ }
+
+ xgifb_reg_set(pVBInfo->Part2Port, 0x44, temp);
+ temp = (tempbx & 0xFF00) >> 8;
+ xgifb_reg_and_or(pVBInfo->Part2Port, 0x45, ~0x03F, temp);
+ temp = tempcx & 0x00FF;
+
+ if (tempbx & 0x2000)
+ temp = 0;
+
+ if (!(pVBInfo->VBInfo & SetCRT2ToLCD))
+ temp |= 0x18;
+
+ xgifb_reg_and_or(pVBInfo->Part2Port, 0x46, ~0x1F, temp);
+ if (pVBInfo->TVInfo & TVSetPAL) {
+ tempbx = 0x0382;
+ tempcx = 0x007e;
+ } else {
+ tempbx = 0x0369;
+ tempcx = 0x0061;
+ }
+
+ temp = tempbx & 0x00FF;
+ xgifb_reg_set(pVBInfo->Part2Port, 0x4b, temp);
+ temp = tempcx & 0x00FF;
+ xgifb_reg_set(pVBInfo->Part2Port, 0x4c, temp);
+
+ temp = ((tempcx & 0xFF00) >> 8) & 0x03;
+ temp <<= 2;
+ temp |= ((tempbx & 0xFF00) >> 8) & 0x03;
+
+ if (pVBInfo->VBInfo & SetCRT2ToYPbPr525750) {
+ temp |= 0x10;
+
+ if (pVBInfo->TVInfo & TVSetYPbPr525p)
+ temp |= 0x20;
+
+ if (pVBInfo->TVInfo & TVSetYPbPr750p)
+ temp |= 0x60;
+ }
+
+ xgifb_reg_set(pVBInfo->Part2Port, 0x4d, temp);
+ temp = xgifb_reg_get(pVBInfo->Part2Port, 0x43); /* 301b change */
+ xgifb_reg_set(pVBInfo->Part2Port, 0x43, (unsigned short) (temp - 3));
+
+ if (!(pVBInfo->TVInfo & (TVSetYPbPr525p | TVSetYPbPr750p))) {
+ if (pVBInfo->TVInfo & NTSC1024x768) {
+ TimingPoint = XGI_NTSC1024AdjTime;
+ for (i = 0x1c, j = 0; i <= 0x30; i++, j++) {
+ xgifb_reg_set(pVBInfo->Part2Port, i,
+ TimingPoint[j]);
+ }
+ xgifb_reg_set(pVBInfo->Part2Port, 0x43, 0x72);
+ }
+ }
+
+ /* Modify for 301C PALM Support */
+ if (pVBInfo->VBType & VB_XGI301C) {
+ if (pVBInfo->TVInfo & TVSetPALM)
+ xgifb_reg_and_or(pVBInfo->Part2Port, 0x4E, ~0x08,
+ 0x08); /* PALM Mode */
+ }
+
+ if (pVBInfo->TVInfo & TVSetPALM) {
+ tempax = xgifb_reg_get(pVBInfo->Part2Port, 0x01);
+ tempax--;
+ xgifb_reg_and(pVBInfo->Part2Port, 0x01, tempax);
+
+ xgifb_reg_and(pVBInfo->Part2Port, 0x00, 0xEF);
+ }
+
+ if (pVBInfo->VBInfo & SetCRT2ToHiVision) {
+ if (!(pVBInfo->VBInfo & SetInSlaveMode))
+ xgifb_reg_set(pVBInfo->Part2Port, 0x0B, 0x00);
+ }
+}
+
+static void XGI_SetLCDRegs(unsigned short ModeIdIndex,
+ struct vb_device_info *pVBInfo)
+{
+ unsigned short pushbx, tempax, tempbx, tempcx, temp, tempah,
+ tempbh, tempch;
+
+ struct XGI_LCDDesStruct const *LCDBDesPtr = NULL;
+
+ /* si+Ext_ResInfo */
+ if (!(pVBInfo->VBInfo & SetCRT2ToLCD))
+ return;
+
+ tempbx = pVBInfo->HDE; /* RHACTE=HDE-1 */
+
+ if (XGI_IsLCDDualLink(pVBInfo))
+ tempbx >>= 1;
+
+ tempbx -= 1;
+ temp = tempbx & 0x00FF;
+ xgifb_reg_set(pVBInfo->Part2Port, 0x2C, temp);
+ temp = (tempbx & 0xFF00) >> 8;
+ temp <<= 4;
+ xgifb_reg_and_or(pVBInfo->Part2Port, 0x2B, 0x0F, temp);
+ temp = 0x01;
+
+ xgifb_reg_set(pVBInfo->Part2Port, 0x0B, temp);
+ tempbx = pVBInfo->VDE; /* RTVACTEO=(VDE-1)&0xFF */
+ tempbx--;
+ temp = tempbx & 0x00FF;
+ xgifb_reg_set(pVBInfo->Part2Port, 0x03, temp);
+ temp = ((tempbx & 0xFF00) >> 8) & 0x07;
+ xgifb_reg_and_or(pVBInfo->Part2Port, 0x0C, ~0x07, temp);
+
+ tempcx = pVBInfo->VT - 1;
+ temp = tempcx & 0x00FF; /* RVTVT=VT-1 */
+ xgifb_reg_set(pVBInfo->Part2Port, 0x19, temp);
+ temp = (tempcx & 0xFF00) >> 8;
+ temp <<= 5;
+ xgifb_reg_set(pVBInfo->Part2Port, 0x1A, temp);
+ xgifb_reg_and_or(pVBInfo->Part2Port, 0x09, 0xF0, 0x00);
+ xgifb_reg_and_or(pVBInfo->Part2Port, 0x0A, 0xF0, 0x00);
+ xgifb_reg_and_or(pVBInfo->Part2Port, 0x17, 0xFB, 0x00);
+ xgifb_reg_and_or(pVBInfo->Part2Port, 0x18, 0xDF, 0x00);
+
+ /* Customized LCDB Does not add */
+ if ((pVBInfo->VBType & VB_SIS301LV) || (pVBInfo->VBType & VB_SIS302LV))
+ LCDBDesPtr = XGI_GetLcdPtr(xgifb_lcddldes, ModeIdIndex,
+ pVBInfo);
+ else
+ LCDBDesPtr = XGI_GetLcdPtr(XGI_LCDDesDataTable, ModeIdIndex,
+ pVBInfo);
+
+ tempah = pVBInfo->LCDResInfo;
+ tempah &= PanelResInfo;
+
+ if ((tempah == Panel_1024x768) || (tempah == Panel_1024x768x75)) {
+ tempbx = 1024;
+ tempcx = 768;
+ } else if ((tempah == Panel_1280x1024) ||
+ (tempah == Panel_1280x1024x75)) {
+ tempbx = 1280;
+ tempcx = 1024;
+ } else if (tempah == Panel_1400x1050) {
+ tempbx = 1400;
+ tempcx = 1050;
+ } else {
+ tempbx = 1600;
+ tempcx = 1200;
+ }
+
+ if (pVBInfo->LCDInfo & EnableScalingLCD) {
+ tempbx = pVBInfo->HDE;
+ tempcx = pVBInfo->VDE;
+ }
+
+ pushbx = tempbx;
+ tempax = pVBInfo->VT;
+ pVBInfo->LCDHDES = LCDBDesPtr->LCDHDES;
+ pVBInfo->LCDHRS = LCDBDesPtr->LCDHRS;
+ pVBInfo->LCDVDES = LCDBDesPtr->LCDVDES;
+ pVBInfo->LCDVRS = LCDBDesPtr->LCDVRS;
+ tempbx = pVBInfo->LCDVDES;
+ tempcx += tempbx;
+
+ if (tempcx >= tempax)
+ tempcx -= tempax; /* lcdvdes */
+
+ temp = tempbx & 0x00FF; /* RVEQ1EQ=lcdvdes */
+ xgifb_reg_set(pVBInfo->Part2Port, 0x05, temp);
+ temp = tempcx & 0x00FF;
+ xgifb_reg_set(pVBInfo->Part2Port, 0x06, temp);
+ tempch = ((tempcx & 0xFF00) >> 8) & 0x07;
+ tempbh = ((tempbx & 0xFF00) >> 8) & 0x07;
+ tempah = tempch;
+ tempah <<= 3;
+ tempah |= tempbh;
+ xgifb_reg_set(pVBInfo->Part2Port, 0x02, tempah);
+
+ /* getlcdsync() */
+ XGI_GetLCDSync(&tempax, &tempbx, pVBInfo);
+ tempcx = tempbx;
+ tempax = pVBInfo->VT;
+ tempbx = pVBInfo->LCDVRS;
+
+ tempcx += tempbx;
+ if (tempcx >= tempax)
+ tempcx -= tempax;
+
+ temp = tempbx & 0x00FF; /* RTVACTEE=lcdvrs */
+ xgifb_reg_set(pVBInfo->Part2Port, 0x04, temp);
+ temp = (tempbx & 0xFF00) >> 8;
+ temp <<= 4;
+ temp |= (tempcx & 0x000F);
+ xgifb_reg_set(pVBInfo->Part2Port, 0x01, temp);
+ tempcx = pushbx;
+ tempax = pVBInfo->HT;
+ tempbx = pVBInfo->LCDHDES;
+ tempbx &= 0x0FFF;
+
+ if (XGI_IsLCDDualLink(pVBInfo)) {
+ tempax >>= 1;
+ tempbx >>= 1;
+ tempcx >>= 1;
+ }
+
+ if (pVBInfo->VBType & VB_SIS302LV)
+ tempbx += 1;
+
+ if (pVBInfo->VBType & VB_XGI301C) /* tap4 */
+ tempbx += 1;
+
+ tempcx += tempbx;
+
+ if (tempcx >= tempax)
+ tempcx -= tempax;
+
+ temp = tempbx & 0x00FF;
+ xgifb_reg_set(pVBInfo->Part2Port, 0x1F, temp); /* RHBLKE=lcdhdes */
+ temp = ((tempbx & 0xFF00) >> 8) << 4;
+ xgifb_reg_set(pVBInfo->Part2Port, 0x20, temp);
+ temp = tempcx & 0x00FF;
+ xgifb_reg_set(pVBInfo->Part2Port, 0x23, temp); /* RHEQPLE=lcdhdee */
+ temp = (tempcx & 0xFF00) >> 8;
+ xgifb_reg_set(pVBInfo->Part2Port, 0x25, temp);
+
+ XGI_GetLCDSync(&tempax, &tempbx, pVBInfo);
+ tempcx = tempax;
+ tempax = pVBInfo->HT;
+ tempbx = pVBInfo->LCDHRS;
+ if (XGI_IsLCDDualLink(pVBInfo)) {
+ tempax >>= 1;
+ tempbx >>= 1;
+ tempcx >>= 1;
+ }
+
+ if (pVBInfo->VBType & VB_SIS302LV)
+ tempbx += 1;
+
+ tempcx += tempbx;
+
+ if (tempcx >= tempax)
+ tempcx -= tempax;
+
+ temp = tempbx & 0x00FF; /* RHBURSTS=lcdhrs */
+ xgifb_reg_set(pVBInfo->Part2Port, 0x1C, temp);
+
+ temp = (tempbx & 0xFF00) >> 8;
+ temp <<= 4;
+ xgifb_reg_and_or(pVBInfo->Part2Port, 0x1D, ~0x0F0, temp);
+ temp = tempcx & 0x00FF; /* RHSYEXP2S=lcdhre */
+ xgifb_reg_set(pVBInfo->Part2Port, 0x21, temp);
+
+ if (!(pVBInfo->LCDInfo & XGI_LCDVESATiming)) {
+ if (pVBInfo->VGAVDE == 525) {
+ if (pVBInfo->VBType & (VB_SIS301B | VB_SIS302B
+ | VB_SIS301LV | VB_SIS302LV
+ | VB_XGI301C)) {
+ temp = 0xC6;
+ } else
+ temp = 0xC4;
+
+ xgifb_reg_set(pVBInfo->Part2Port, 0x2f, temp);
+ xgifb_reg_set(pVBInfo->Part2Port, 0x30, 0xB3);
+ }
+
+ if (pVBInfo->VGAVDE == 420) {
+ if (pVBInfo->VBType & (VB_SIS301B | VB_SIS302B
+ | VB_SIS301LV | VB_SIS302LV
+ | VB_XGI301C)) {
+ temp = 0x4F;
+ } else
+ temp = 0x4E;
+ xgifb_reg_set(pVBInfo->Part2Port, 0x2f, temp);
+ }
+ }
+}
+
+/* --------------------------------------------------------------------- */
+/* Function : XGI_GetTap4Ptr */
+/* Input : */
+/* Output : di -> Tap4 Reg. Setting Pointer */
+/* Description : */
+/* --------------------------------------------------------------------- */
+static struct XGI301C_Tap4TimingStruct const
+*XGI_GetTap4Ptr(unsigned short tempcx, struct vb_device_info *pVBInfo)
+{
+ unsigned short tempax, tempbx, i;
+ struct XGI301C_Tap4TimingStruct const *Tap4TimingPtr;
+
+ if (tempcx == 0) {
+ tempax = pVBInfo->VGAHDE;
+ tempbx = pVBInfo->HDE;
+ } else {
+ tempax = pVBInfo->VGAVDE;
+ tempbx = pVBInfo->VDE;
+ }
+
+ if (tempax <= tempbx)
+ return &xgifb_tap4_timing[0];
+ Tap4TimingPtr = xgifb_ntsc_525_tap4_timing; /* NTSC */
+
+ if (pVBInfo->TVInfo & TVSetPAL)
+ Tap4TimingPtr = PALTap4Timing;
+
+ if (pVBInfo->VBInfo & SetCRT2ToYPbPr525750) {
+ if ((pVBInfo->TVInfo & TVSetYPbPr525i) ||
+ (pVBInfo->TVInfo & TVSetYPbPr525p))
+ Tap4TimingPtr = xgifb_ntsc_525_tap4_timing;
+ if (pVBInfo->TVInfo & TVSetYPbPr750p)
+ Tap4TimingPtr = YPbPr750pTap4Timing;
+ }
+
+ if (pVBInfo->VBInfo & SetCRT2ToHiVision)
+ Tap4TimingPtr = xgifb_tap4_timing;
+
+ i = 0;
+ while (Tap4TimingPtr[i].DE != 0xFFFF) {
+ if (Tap4TimingPtr[i].DE == tempax)
+ break;
+ i++;
+ }
+ return &Tap4TimingPtr[i];
+}
+
+static void XGI_SetTap4Regs(struct vb_device_info *pVBInfo)
+{
+ unsigned short i, j;
+ struct XGI301C_Tap4TimingStruct const *Tap4TimingPtr;
+
+ if (!(pVBInfo->VBType & VB_XGI301C))
+ return;
+
+ Tap4TimingPtr = XGI_GetTap4Ptr(0, pVBInfo); /* Set Horizontal Scaling */
+ for (i = 0x80, j = 0; i <= 0xBF; i++, j++)
+ xgifb_reg_set(pVBInfo->Part2Port, i, Tap4TimingPtr->Reg[j]);
+
+ if ((pVBInfo->VBInfo & SetCRT2ToTV) &&
+ (!(pVBInfo->VBInfo & SetCRT2ToHiVision))) {
+ /* Set Vertical Scaling */
+ Tap4TimingPtr = XGI_GetTap4Ptr(1, pVBInfo);
+ for (i = 0xC0, j = 0; i < 0xFF; i++, j++)
+ xgifb_reg_set(pVBInfo->Part2Port,
+ i,
+ Tap4TimingPtr->Reg[j]);
+ }
+
+ if ((pVBInfo->VBInfo & SetCRT2ToTV) &&
+ (!(pVBInfo->VBInfo & SetCRT2ToHiVision)))
+ /* Enable V.Scaling */
+ xgifb_reg_and_or(pVBInfo->Part2Port, 0x4E, ~0x14, 0x04);
+ else
+ /* Enable H.Scaling */
+ xgifb_reg_and_or(pVBInfo->Part2Port, 0x4E, ~0x14, 0x10);
+}
+
+static void XGI_SetGroup3(unsigned short ModeIdIndex,
+ struct vb_device_info *pVBInfo)
+{
+ unsigned short i;
+ unsigned char const *tempdi;
+ unsigned short modeflag;
+
+ /* si+Ext_ResInfo */
+ modeflag = XGI330_EModeIDTable[ModeIdIndex].Ext_ModeFlag;
+
+ xgifb_reg_set(pVBInfo->Part3Port, 0x00, 0x00);
+ if (pVBInfo->TVInfo & TVSetPAL) {
+ xgifb_reg_set(pVBInfo->Part3Port, 0x13, 0xFA);
+ xgifb_reg_set(pVBInfo->Part3Port, 0x14, 0xC8);
+ } else {
+ xgifb_reg_set(pVBInfo->Part3Port, 0x13, 0xF5);
+ xgifb_reg_set(pVBInfo->Part3Port, 0x14, 0xB7);
+ }
+
+ if (!(pVBInfo->VBInfo & SetCRT2ToTV))
+ return;
+
+ if (pVBInfo->TVInfo & TVSetPALM) {
+ xgifb_reg_set(pVBInfo->Part3Port, 0x13, 0xFA);
+ xgifb_reg_set(pVBInfo->Part3Port, 0x14, 0xC8);
+ xgifb_reg_set(pVBInfo->Part3Port, 0x3D, 0xA8);
+ }
+
+ if ((pVBInfo->VBInfo & SetCRT2ToHiVision) || (pVBInfo->VBInfo
+ & SetCRT2ToYPbPr525750)) {
+ if (pVBInfo->TVInfo & TVSetYPbPr525i)
+ return;
+
+ tempdi = XGI330_HiTVGroup3Data;
+ if (pVBInfo->SetFlag & TVSimuMode) {
+ tempdi = XGI330_HiTVGroup3Simu;
+ if (!(modeflag & Charx8Dot))
+ tempdi = XGI330_HiTVGroup3Text;
+ }
+
+ if (pVBInfo->TVInfo & TVSetYPbPr525p)
+ tempdi = XGI330_Ren525pGroup3;
+
+ if (pVBInfo->TVInfo & TVSetYPbPr750p)
+ tempdi = XGI330_Ren750pGroup3;
+
+ for (i = 0; i <= 0x3E; i++)
+ xgifb_reg_set(pVBInfo->Part3Port, i, tempdi[i]);
+
+ if (pVBInfo->VBType & VB_XGI301C) { /* Marcovision */
+ if (pVBInfo->TVInfo & TVSetYPbPr525p)
+ xgifb_reg_set(pVBInfo->Part3Port, 0x28, 0x3f);
+ }
+ }
+}
+
+static void XGI_SetGroup4(unsigned short ModeIdIndex,
+ unsigned short RefreshRateTableIndex,
+ struct vb_device_info *pVBInfo)
+{
+ unsigned short tempax, tempcx, tempbx, modeflag, temp, temp2;
+
+ unsigned long tempebx, tempeax, templong;
+
+ /* si+Ext_ResInfo */
+ modeflag = XGI330_EModeIDTable[ModeIdIndex].Ext_ModeFlag;
+ temp = pVBInfo->RVBHCFACT;
+ xgifb_reg_set(pVBInfo->Part4Port, 0x13, temp);
+
+ tempbx = pVBInfo->RVBHCMAX;
+ temp = tempbx & 0x00FF;
+ xgifb_reg_set(pVBInfo->Part4Port, 0x14, temp);
+ temp2 = ((tempbx & 0xFF00) >> 8) << 7;
+ tempcx = pVBInfo->VGAHT - 1;
+ temp = tempcx & 0x00FF;
+ xgifb_reg_set(pVBInfo->Part4Port, 0x16, temp);
+
+ temp = ((tempcx & 0xFF00) >> 8) << 3;
+ temp2 |= temp;
+
+ tempcx = pVBInfo->VGAVT - 1;
+ if (!(pVBInfo->VBInfo & SetCRT2ToTV))
+ tempcx -= 5;
+
+ temp = tempcx & 0x00FF;
+ xgifb_reg_set(pVBInfo->Part4Port, 0x17, temp);
+ temp = temp2 | ((tempcx & 0xFF00) >> 8);
+ xgifb_reg_set(pVBInfo->Part4Port, 0x15, temp);
+ xgifb_reg_or(pVBInfo->Part4Port, 0x0D, 0x08);
+ tempcx = pVBInfo->VBInfo;
+ tempbx = pVBInfo->VGAHDE;
+
+ if (modeflag & HalfDCLK)
+ tempbx >>= 1;
+
+ if (XGI_IsLCDDualLink(pVBInfo))
+ tempbx >>= 1;
+
+ if (tempcx & SetCRT2ToHiVision) {
+ temp = 0;
+ if (tempbx <= 1024)
+ temp = 0xA0;
+ if (tempbx == 1280)
+ temp = 0xC0;
+ } else if (tempcx & SetCRT2ToTV) {
+ temp = 0xA0;
+ if (tempbx <= 800)
+ temp = 0x80;
+ } else {
+ temp = 0x80;
+ if (pVBInfo->VBInfo & SetCRT2ToLCD) {
+ temp = 0;
+ if (tempbx > 800)
+ temp = 0x60;
+ }
+ }
+
+ if (pVBInfo->TVInfo & (TVSetYPbPr525p | TVSetYPbPr750p)) {
+ temp = 0x00;
+ if (pVBInfo->VGAHDE == 1280)
+ temp = 0x40;
+ if (pVBInfo->VGAHDE == 1024)
+ temp = 0x20;
+ }
+ xgifb_reg_and_or(pVBInfo->Part4Port, 0x0E, ~0xEF, temp);
+
+ tempebx = pVBInfo->VDE;
+
+ tempcx = pVBInfo->RVBHRS;
+ temp = tempcx & 0x00FF;
+ xgifb_reg_set(pVBInfo->Part4Port, 0x18, temp);
+
+ tempeax = pVBInfo->VGAVDE;
+ tempcx |= 0x04000;
+
+ if (tempeax <= tempebx) {
+ tempcx = (tempcx & (~0x4000));
+ tempeax = pVBInfo->VGAVDE;
+ } else {
+ tempeax -= tempebx;
+ }
+
+ templong = (tempeax * 256 * 1024) % tempebx;
+ tempeax = (tempeax * 256 * 1024) / tempebx;
+ tempebx = tempeax;
+
+ if (templong != 0)
+ tempebx++;
+
+ temp = (unsigned short) (tempebx & 0x000000FF);
+ xgifb_reg_set(pVBInfo->Part4Port, 0x1B, temp);
+
+ temp = (unsigned short) ((tempebx & 0x0000FF00) >> 8);
+ xgifb_reg_set(pVBInfo->Part4Port, 0x1A, temp);
+ tempbx = (unsigned short) (tempebx >> 16);
+ temp = tempbx & 0x00FF;
+ temp <<= 4;
+ temp |= ((tempcx & 0xFF00) >> 8);
+ xgifb_reg_set(pVBInfo->Part4Port, 0x19, temp);
+
+ /* 301b */
+ if (pVBInfo->VBType & (VB_SIS301B | VB_SIS302B | VB_SIS301LV
+ | VB_SIS302LV | VB_XGI301C)) {
+ temp = 0x0028;
+ xgifb_reg_set(pVBInfo->Part4Port, 0x1C, temp);
+ tempax = pVBInfo->VGAHDE;
+ if (modeflag & HalfDCLK)
+ tempax >>= 1;
+
+ if (XGI_IsLCDDualLink(pVBInfo))
+ tempax >>= 1;
+
+ if (pVBInfo->VBInfo & SetCRT2ToLCD) {
+ if (tempax > 800)
+ tempax -= 800;
+ } else if (pVBInfo->VGAHDE > 800) {
+ if (pVBInfo->VGAHDE == 1024)
+ tempax = (tempax * 25 / 32) - 1;
+ else
+ tempax = (tempax * 20 / 32) - 1;
+ }
+ tempax -= 1;
+
+ temp = (tempax & 0xFF00) >> 8;
+ temp = (temp & 0x0003) << 4;
+ xgifb_reg_set(pVBInfo->Part4Port, 0x1E, temp);
+ temp = (tempax & 0x00FF);
+ xgifb_reg_set(pVBInfo->Part4Port, 0x1D, temp);
+
+ if (pVBInfo->VBInfo & (SetCRT2ToTV | SetCRT2ToHiVision)) {
+ if (pVBInfo->VGAHDE > 800)
+ xgifb_reg_or(pVBInfo->Part4Port, 0x1E, 0x08);
+
+ }
+ temp = 0x0036;
+
+ if (pVBInfo->VBInfo & SetCRT2ToTV) {
+ if (!(pVBInfo->TVInfo & (NTSC1024x768
+ | TVSetYPbPr525p | TVSetYPbPr750p
+ | TVSetHiVision))) {
+ temp |= 0x0001;
+ if ((pVBInfo->VBInfo & SetInSlaveMode)
+ && (!(pVBInfo->TVInfo
+ & TVSimuMode)))
+ temp &= (~0x0001);
+ }
+ }
+
+ xgifb_reg_and_or(pVBInfo->Part4Port, 0x1F, 0x00C0, temp);
+ tempbx = pVBInfo->HT;
+ if (XGI_IsLCDDualLink(pVBInfo))
+ tempbx >>= 1;
+ tempbx = (tempbx >> 1) - 2;
+ temp = ((tempbx & 0x0700) >> 8) << 3;
+ xgifb_reg_and_or(pVBInfo->Part4Port, 0x21, 0x00C0, temp);
+ temp = tempbx & 0x00FF;
+ xgifb_reg_set(pVBInfo->Part4Port, 0x22, temp);
+ }
+ /* end 301b */
+
+ XGI_SetCRT2VCLK(ModeIdIndex, RefreshRateTableIndex, pVBInfo);
+}
+
+static void XGINew_EnableCRT2(struct vb_device_info *pVBInfo)
+{
+ xgifb_reg_and_or(pVBInfo->P3c4, 0x1E, 0xFF, 0x20);
+}
+
+static void XGI_SetGroup5(struct vb_device_info *pVBInfo)
+{
+ if (pVBInfo->ModeType == ModeVGA) {
+ if (!(pVBInfo->VBInfo & (SetInSlaveMode | LoadDACFlag
+ | DisableCRT2Display))) {
+ XGINew_EnableCRT2(pVBInfo);
+ }
+ }
+}
+
+static void XGI_DisableGatingCRT(struct vb_device_info *pVBInfo)
+{
+ xgifb_reg_and_or(pVBInfo->P3d4, 0x63, 0xBF, 0x00);
+}
+
+static unsigned char XGI_XG21CheckLVDSMode(struct xgifb_video_info *xgifb_info,
+ unsigned short ModeNo, unsigned short ModeIdIndex)
+{
+ unsigned short xres, yres, colordepth, modeflag, resindex;
+
+ resindex = XGI330_EModeIDTable[ModeIdIndex].Ext_RESINFO;
+ xres = XGI330_ModeResInfo[resindex].HTotal; /* xres->ax */
+ yres = XGI330_ModeResInfo[resindex].VTotal; /* yres->bx */
+ /* si+St_ModeFlag */
+ modeflag = XGI330_EModeIDTable[ModeIdIndex].Ext_ModeFlag;
+
+ if (!(modeflag & Charx8Dot)) {
+ xres /= 9;
+ xres *= 8;
+ }
+
+ if ((ModeNo > 0x13) && (modeflag & HalfDCLK))
+ xres *= 2;
+
+ if ((ModeNo > 0x13) && (modeflag & DoubleScanMode))
+ yres *= 2;
+
+ if (xres > xgifb_info->lvds_data.LVDSHDE)
+ return 0;
+
+ if (yres > xgifb_info->lvds_data.LVDSVDE)
+ return 0;
+
+ if (xres != xgifb_info->lvds_data.LVDSHDE ||
+ yres != xgifb_info->lvds_data.LVDSVDE) {
+ colordepth = XGI_GetColorDepth(ModeIdIndex);
+ if (colordepth > 2)
+ return 0;
+ }
+ return 1;
+}
+
+static void xgifb_set_lvds(struct xgifb_video_info *xgifb_info,
+ int chip_id,
+ unsigned short ModeIdIndex,
+ struct vb_device_info *pVBInfo)
+{
+ unsigned char temp, Miscdata;
+ unsigned short xres, yres, modeflag, resindex;
+ unsigned short LVDSHT, LVDSHBS, LVDSHRS, LVDSHRE, LVDSHBE;
+ unsigned short LVDSVT, LVDSVBS, LVDSVRS, LVDSVRE, LVDSVBE;
+ unsigned short value;
+
+ temp = (unsigned char) ((xgifb_info->lvds_data.LVDS_Capability &
+ (LCDPolarity << 8)) >> 8);
+ temp &= LCDPolarity;
+ Miscdata = inb(pVBInfo->P3cc);
+
+ outb((Miscdata & 0x3F) | temp, pVBInfo->P3c2);
+
+ temp = xgifb_info->lvds_data.LVDS_Capability & LCDPolarity;
+ /* SR35[7] FP VSync polarity */
+ xgifb_reg_and_or(pVBInfo->P3c4, 0x35, ~0x80, temp & 0x80);
+ /* SR30[5] FP HSync polarity */
+ xgifb_reg_and_or(pVBInfo->P3c4, 0x30, ~0x20, (temp & 0x40) >> 1);
+
+ if (chip_id == XG27)
+ XGI_SetXG27FPBits(pVBInfo);
+ else
+ XGI_SetXG21FPBits(pVBInfo);
+
+ resindex = XGI330_EModeIDTable[ModeIdIndex].Ext_RESINFO;
+ xres = XGI330_ModeResInfo[resindex].HTotal; /* xres->ax */
+ yres = XGI330_ModeResInfo[resindex].VTotal; /* yres->bx */
+ /* si+St_ModeFlag */
+ modeflag = XGI330_EModeIDTable[ModeIdIndex].Ext_ModeFlag;
+
+ if (!(modeflag & Charx8Dot))
+ xres = xres * 8 / 9;
+
+ LVDSHT = xgifb_info->lvds_data.LVDSHT;
+
+ LVDSHBS = xres + (xgifb_info->lvds_data.LVDSHDE - xres) / 2;
+
+ if (LVDSHBS > LVDSHT)
+ LVDSHBS -= LVDSHT;
+
+ LVDSHRS = LVDSHBS + xgifb_info->lvds_data.LVDSHFP;
+ if (LVDSHRS > LVDSHT)
+ LVDSHRS -= LVDSHT;
+
+ LVDSHRE = LVDSHRS + xgifb_info->lvds_data.LVDSHSYNC;
+ if (LVDSHRE > LVDSHT)
+ LVDSHRE -= LVDSHT;
+
+ LVDSHBE = LVDSHBS + LVDSHT - xgifb_info->lvds_data.LVDSHDE;
+
+ LVDSVT = xgifb_info->lvds_data.LVDSVT;
+
+ LVDSVBS = yres + (xgifb_info->lvds_data.LVDSVDE - yres) / 2;
+ if (modeflag & DoubleScanMode)
+ LVDSVBS += yres / 2;
+
+ if (LVDSVBS > LVDSVT)
+ LVDSVBS -= LVDSVT;
+
+ LVDSVRS = LVDSVBS + xgifb_info->lvds_data.LVDSVFP;
+ if (LVDSVRS > LVDSVT)
+ LVDSVRS -= LVDSVT;
+
+ LVDSVRE = LVDSVRS + xgifb_info->lvds_data.LVDSVSYNC;
+ if (LVDSVRE > LVDSVT)
+ LVDSVRE -= LVDSVT;
+
+ LVDSVBE = LVDSVBS + LVDSVT - xgifb_info->lvds_data.LVDSVDE;
+
+ temp = xgifb_reg_get(pVBInfo->P3d4, 0x11);
+ xgifb_reg_set(pVBInfo->P3d4, 0x11, temp & 0x7f); /* Unlock CRTC */
+
+ if (!(modeflag & Charx8Dot))
+ xgifb_reg_or(pVBInfo->P3c4, 0x1, 0x1);
+
+ /* HT SR0B[1:0] CR00 */
+ value = (LVDSHT >> 3) - 5;
+ xgifb_reg_and_or(pVBInfo->P3c4, 0x0B, ~0x03, (value & 0x300) >> 8);
+ xgifb_reg_set(pVBInfo->P3d4, 0x0, (value & 0xFF));
+
+ /* HBS SR0B[5:4] CR02 */
+ value = (LVDSHBS >> 3) - 1;
+ xgifb_reg_and_or(pVBInfo->P3c4, 0x0B, ~0x30, (value & 0x300) >> 4);
+ xgifb_reg_set(pVBInfo->P3d4, 0x2, (value & 0xFF));
+
+ /* HBE SR0C[1:0] CR05[7] CR03[4:0] */
+ value = (LVDSHBE >> 3) - 1;
+ xgifb_reg_and_or(pVBInfo->P3c4, 0x0C, ~0x03, (value & 0xC0) >> 6);
+ xgifb_reg_and_or(pVBInfo->P3d4, 0x05, ~0x80, (value & 0x20) << 2);
+ xgifb_reg_and_or(pVBInfo->P3d4, 0x03, ~0x1F, value & 0x1F);
+
+ /* HRS SR0B[7:6] CR04 */
+ value = (LVDSHRS >> 3) + 2;
+ xgifb_reg_and_or(pVBInfo->P3c4, 0x0B, ~0xC0, (value & 0x300) >> 2);
+ xgifb_reg_set(pVBInfo->P3d4, 0x4, (value & 0xFF));
+
+ /* Panel HRS SR2F[1:0] SR2E[7:0] */
+ value--;
+ xgifb_reg_and_or(pVBInfo->P3c4, 0x2F, ~0x03, (value & 0x300) >> 8);
+ xgifb_reg_set(pVBInfo->P3c4, 0x2E, (value & 0xFF));
+
+ /* HRE SR0C[2] CR05[4:0] */
+ value = (LVDSHRE >> 3) + 2;
+ xgifb_reg_and_or(pVBInfo->P3c4, 0x0C, ~0x04, (value & 0x20) >> 3);
+ xgifb_reg_and_or(pVBInfo->P3d4, 0x05, ~0x1F, value & 0x1F);
+
+ /* Panel HRE SR2F[7:2] */
+ value--;
+ xgifb_reg_and_or(pVBInfo->P3c4, 0x2F, ~0xFC, value << 2);
+
+ /* VT SR0A[0] CR07[5][0] CR06 */
+ value = LVDSVT - 2;
+ xgifb_reg_and_or(pVBInfo->P3c4, 0x0A, ~0x01, (value & 0x400) >> 10);
+ xgifb_reg_and_or(pVBInfo->P3d4, 0x07, ~0x20, (value & 0x200) >> 4);
+ xgifb_reg_and_or(pVBInfo->P3d4, 0x07, ~0x01, (value & 0x100) >> 8);
+ xgifb_reg_set(pVBInfo->P3d4, 0x06, (value & 0xFF));
+
+ /* VBS SR0A[2] CR09[5] CR07[3] CR15 */
+ value = LVDSVBS - 1;
+ xgifb_reg_and_or(pVBInfo->P3c4, 0x0A, ~0x04, (value & 0x400) >> 8);
+ xgifb_reg_and_or(pVBInfo->P3d4, 0x09, ~0x20, (value & 0x200) >> 4);
+ xgifb_reg_and_or(pVBInfo->P3d4, 0x07, ~0x08, (value & 0x100) >> 5);
+ xgifb_reg_set(pVBInfo->P3d4, 0x15, (value & 0xFF));
+
+ /* VBE SR0A[4] CR16 */
+ value = LVDSVBE - 1;
+ xgifb_reg_and_or(pVBInfo->P3c4, 0x0A, ~0x10, (value & 0x100) >> 4);
+ xgifb_reg_set(pVBInfo->P3d4, 0x16, (value & 0xFF));
+
+ /* VRS SR0A[3] CR7[7][2] CR10 */
+ value = LVDSVRS - 1;
+ xgifb_reg_and_or(pVBInfo->P3c4, 0x0A, ~0x08, (value & 0x400) >> 7);
+ xgifb_reg_and_or(pVBInfo->P3d4, 0x07, ~0x80, (value & 0x200) >> 2);
+ xgifb_reg_and_or(pVBInfo->P3d4, 0x07, ~0x04, (value & 0x100) >> 6);
+ xgifb_reg_set(pVBInfo->P3d4, 0x10, (value & 0xFF));
+
+ if (chip_id == XG27) {
+ /* Panel VRS SR35[2:0] SR34[7:0] */
+ xgifb_reg_and_or(pVBInfo->P3c4, 0x35, ~0x07,
+ (value & 0x700) >> 8);
+ xgifb_reg_set(pVBInfo->P3c4, 0x34, value & 0xFF);
+ } else {
+ /* Panel VRS SR3F[1:0] SR34[7:0] SR33[0] */
+ xgifb_reg_and_or(pVBInfo->P3c4, 0x3F, ~0x03,
+ (value & 0x600) >> 9);
+ xgifb_reg_set(pVBInfo->P3c4, 0x34, (value >> 1) & 0xFF);
+ xgifb_reg_and_or(pVBInfo->P3d4, 0x33, ~0x01, value & 0x01);
+ }
+
+ /* VRE SR0A[5] CR11[3:0] */
+ value = LVDSVRE - 1;
+ xgifb_reg_and_or(pVBInfo->P3c4, 0x0A, ~0x20, (value & 0x10) << 1);
+ xgifb_reg_and_or(pVBInfo->P3d4, 0x11, ~0x0F, value & 0x0F);
+
+ /* Panel VRE SR3F[7:2] */
+ if (chip_id == XG27)
+ xgifb_reg_and_or(pVBInfo->P3c4, 0x3F, ~0xFC,
+ (value << 2) & 0xFC);
+ else
+ /* SR3F[7] has to be 0, h/w bug */
+ xgifb_reg_and_or(pVBInfo->P3c4, 0x3F, ~0xFC,
+ (value << 2) & 0x7C);
+
+ for (temp = 0, value = 0; temp < 3; temp++) {
+
+ xgifb_reg_and_or(pVBInfo->P3c4, 0x31, ~0x30, value);
+ xgifb_reg_set(pVBInfo->P3c4,
+ 0x2B, xgifb_info->lvds_data.VCLKData1);
+ xgifb_reg_set(pVBInfo->P3c4,
+ 0x2C, xgifb_info->lvds_data.VCLKData2);
+ value += 0x10;
+ }
+
+ if (!(modeflag & Charx8Dot)) {
+ inb(pVBInfo->P3da); /* reset 3da */
+ outb(0x13, pVBInfo->P3c0); /* set index */
+ /* set data, panning = 0, shift left 1 dot*/
+ outb(0x00, pVBInfo->P3c0);
+
+ inb(pVBInfo->P3da); /* Enable Attribute */
+ outb(0x20, pVBInfo->P3c0);
+
+ inb(pVBInfo->P3da); /* reset 3da */
+ }
+
+}
+
+/* --------------------------------------------------------------------- */
+/* Function : XGI_IsLCDON */
+/* Input : */
+/* Output : 0 : Skip PSC Control */
+/* 1: Disable PSC */
+/* Description : */
+/* --------------------------------------------------------------------- */
+static unsigned char XGI_IsLCDON(struct vb_device_info *pVBInfo)
+{
+ unsigned short tempax;
+
+ tempax = pVBInfo->VBInfo;
+ if (tempax & SetCRT2ToDualEdge)
+ return 0;
+ else if (tempax & (DisableCRT2Display | SwitchCRT2 | SetSimuScanMode))
+ return 1;
+
+ return 0;
+}
+
+static void XGI_DisableBridge(struct xgifb_video_info *xgifb_info,
+ struct xgi_hw_device_info *HwDeviceExtension,
+ struct vb_device_info *pVBInfo)
+{
+ unsigned short tempah = 0;
+
+ if (pVBInfo->VBType & (VB_SIS301B | VB_SIS302B | VB_SIS301LV
+ | VB_SIS302LV | VB_XGI301C)) {
+ tempah = 0x3F;
+ if (!(pVBInfo->VBInfo &
+ (DisableCRT2Display | SetSimuScanMode))) {
+ if (pVBInfo->VBInfo & XGI_SetCRT2ToLCDA) {
+ if (pVBInfo->VBInfo & SetCRT2ToDualEdge)
+ tempah = 0x7F; /* Disable Channel A */
+ }
+ }
+
+ /* disable part4_1f */
+ xgifb_reg_and(pVBInfo->Part4Port, 0x1F, tempah);
+
+ if (pVBInfo->VBType & (VB_SIS302LV | VB_XGI301C)) {
+ if (((pVBInfo->VBInfo &
+ (SetCRT2ToLCD | XGI_SetCRT2ToLCDA))) ||
+ (XGI_IsLCDON(pVBInfo)))
+ /* LVDS Driver power down */
+ xgifb_reg_or(pVBInfo->Part4Port, 0x30, 0x80);
+ }
+
+ if (pVBInfo->VBInfo & (DisableCRT2Display | XGI_SetCRT2ToLCDA |
+ SetSimuScanMode))
+ XGI_DisplayOff(xgifb_info, HwDeviceExtension, pVBInfo);
+
+ if (pVBInfo->VBInfo & XGI_SetCRT2ToLCDA)
+ /* Power down */
+ xgifb_reg_and(pVBInfo->Part1Port, 0x1e, 0xdf);
+
+ /* disable TV as primary VGA swap */
+ xgifb_reg_and(pVBInfo->P3c4, 0x32, 0xdf);
+
+ if ((pVBInfo->VBInfo & (SetSimuScanMode | SetCRT2ToDualEdge)))
+ xgifb_reg_and(pVBInfo->Part2Port, 0x00, 0xdf);
+
+ if ((pVBInfo->VBInfo &
+ (DisableCRT2Display | SetSimuScanMode)) ||
+ ((!(pVBInfo->VBInfo & XGI_SetCRT2ToLCDA)) &&
+ (pVBInfo->VBInfo &
+ (SetCRT2ToRAMDAC | SetCRT2ToLCD | SetCRT2ToTV))))
+ xgifb_reg_or(pVBInfo->Part1Port, 0x00, 0x80);
+
+ if ((pVBInfo->VBInfo &
+ (DisableCRT2Display | SetSimuScanMode)) ||
+ (!(pVBInfo->VBInfo & XGI_SetCRT2ToLCDA)) ||
+ (pVBInfo->VBInfo &
+ (SetCRT2ToRAMDAC | SetCRT2ToLCD | SetCRT2ToTV))) {
+ /* save Part1 index 0 */
+ tempah = xgifb_reg_get(pVBInfo->Part1Port, 0x00);
+ /* BTDAC = 1, avoid VB reset */
+ xgifb_reg_or(pVBInfo->Part1Port, 0x00, 0x10);
+ /* disable CRT2 */
+ xgifb_reg_and(pVBInfo->Part1Port, 0x1E, 0xDF);
+ /* restore Part1 index 0 */
+ xgifb_reg_set(pVBInfo->Part1Port, 0x00, tempah);
+ }
+ } else { /* {301} */
+ if (pVBInfo->VBInfo & (SetCRT2ToLCD | SetCRT2ToTV)) {
+ xgifb_reg_or(pVBInfo->Part1Port, 0x00, 0x80);
+ /* Disable CRT2 */
+ xgifb_reg_and(pVBInfo->Part1Port, 0x1E, 0xDF);
+ /* Disable TV asPrimary VGA swap */
+ xgifb_reg_and(pVBInfo->P3c4, 0x32, 0xDF);
+ }
+
+ if (pVBInfo->VBInfo & (DisableCRT2Display | XGI_SetCRT2ToLCDA
+ | SetSimuScanMode))
+ XGI_DisplayOff(xgifb_info, HwDeviceExtension, pVBInfo);
+ }
+}
+
+/* --------------------------------------------------------------------- */
+/* Function : XGI_GetTVPtrIndex */
+/* Input : */
+/* Output : */
+/* Description : bx 0 : ExtNTSC */
+/* 1 : StNTSC */
+/* 2 : ExtPAL */
+/* 3 : StPAL */
+/* 4 : ExtHiTV */
+/* 5 : StHiTV */
+/* 6 : Ext525i */
+/* 7 : St525i */
+/* 8 : Ext525p */
+/* 9 : St525p */
+/* A : Ext750p */
+/* B : St750p */
+/* --------------------------------------------------------------------- */
+static unsigned short XGI_GetTVPtrIndex(struct vb_device_info *pVBInfo)
+{
+ unsigned short tempbx = 0;
+
+ if (pVBInfo->TVInfo & TVSetPAL)
+ tempbx = 2;
+ if (pVBInfo->TVInfo & TVSetHiVision)
+ tempbx = 4;
+ if (pVBInfo->TVInfo & TVSetYPbPr525i)
+ tempbx = 6;
+ if (pVBInfo->TVInfo & TVSetYPbPr525p)
+ tempbx = 8;
+ if (pVBInfo->TVInfo & TVSetYPbPr750p)
+ tempbx = 10;
+ if (pVBInfo->TVInfo & TVSimuMode)
+ tempbx++;
+
+ return tempbx;
+}
+
+/* --------------------------------------------------------------------- */
+/* Function : XGI_GetTVPtrIndex2 */
+/* Input : */
+/* Output : bx 0 : NTSC */
+/* 1 : PAL */
+/* 2 : PALM */
+/* 3 : PALN */
+/* 4 : NTSC1024x768 */
+/* 5 : PAL-M 1024x768 */
+/* 6-7: reserved */
+/* cl 0 : YFilter1 */
+/* 1 : YFilter2 */
+/* ch 0 : 301A */
+/* 1 : 301B/302B/301LV/302LV */
+/* Description : */
+/* --------------------------------------------------------------------- */
+static void XGI_GetTVPtrIndex2(unsigned short *tempbx, unsigned char *tempcl,
+ unsigned char *tempch, struct vb_device_info *pVBInfo)
+{
+ *tempbx = 0;
+ *tempcl = 0;
+ *tempch = 0;
+
+ if (pVBInfo->TVInfo & TVSetPAL)
+ *tempbx = 1;
+
+ if (pVBInfo->TVInfo & TVSetPALM)
+ *tempbx = 2;
+
+ if (pVBInfo->TVInfo & TVSetPALN)
+ *tempbx = 3;
+
+ if (pVBInfo->TVInfo & NTSC1024x768) {
+ *tempbx = 4;
+ if (pVBInfo->TVInfo & TVSetPALM)
+ *tempbx = 5;
+ }
+
+ if (pVBInfo->VBType & (VB_SIS301B | VB_SIS302B | VB_SIS301LV
+ | VB_SIS302LV | VB_XGI301C)) {
+ if ((!(pVBInfo->VBInfo & SetInSlaveMode)) || (pVBInfo->TVInfo
+ & TVSimuMode)) {
+ *tempbx += 8;
+ *tempcl += 1;
+ }
+ }
+
+ if (pVBInfo->VBType & (VB_SIS301B | VB_SIS302B | VB_SIS301LV
+ | VB_SIS302LV | VB_XGI301C))
+ (*tempch)++;
+}
+
+static void XGI_SetDelayComp(struct vb_device_info *pVBInfo)
+{
+ unsigned char tempah, tempbl, tempbh;
+
+ if (pVBInfo->VBType & (VB_SIS301B | VB_SIS302B | VB_SIS301LV
+ | VB_SIS302LV | VB_XGI301C)) {
+ if (pVBInfo->VBInfo & (SetCRT2ToLCD | XGI_SetCRT2ToLCDA
+ | SetCRT2ToTV | SetCRT2ToRAMDAC)) {
+ tempbh = 0;
+ tempbl = XGI301TVDelay;
+
+ if (pVBInfo->VBInfo & SetCRT2ToDualEdge)
+ tempbl >>= 4;
+ if (pVBInfo->VBInfo &
+ (SetCRT2ToLCD | XGI_SetCRT2ToLCDA)) {
+ tempbh = XGI301LCDDelay;
+
+ if (!(pVBInfo->VBInfo & XGI_SetCRT2ToLCDA))
+ tempbl = tempbh;
+ }
+
+ tempbl &= 0x0F;
+ tempbh &= 0xF0;
+ tempah = xgifb_reg_get(pVBInfo->Part1Port, 0x2D);
+
+ if (pVBInfo->VBInfo & (SetCRT2ToRAMDAC | SetCRT2ToLCD
+ | SetCRT2ToTV)) { /* Channel B */
+ tempah &= 0xF0;
+ tempah |= tempbl;
+ }
+
+ if (pVBInfo->VBInfo & XGI_SetCRT2ToLCDA) {
+ /* Channel A */
+ tempah &= 0x0F;
+ tempah |= tempbh;
+ }
+ xgifb_reg_set(pVBInfo->Part1Port, 0x2D, tempah);
+ }
+ }
+}
+
+static void XGI_SetLCDCap_A(unsigned short tempcx,
+ struct vb_device_info *pVBInfo)
+{
+ unsigned short temp;
+
+ temp = xgifb_reg_get(pVBInfo->P3d4, 0x37);
+
+ if (temp & LCDRGB18Bit) {
+ xgifb_reg_and_or(pVBInfo->Part1Port, 0x19, 0x0F,
+ /* Enable Dither */
+ (unsigned short) (0x20 | (tempcx & 0x00C0)));
+ xgifb_reg_and_or(pVBInfo->Part1Port, 0x1A, 0x7F, 0x80);
+ } else {
+ xgifb_reg_and_or(pVBInfo->Part1Port, 0x19, 0x0F,
+ (unsigned short) (0x30 | (tempcx & 0x00C0)));
+ xgifb_reg_and_or(pVBInfo->Part1Port, 0x1A, 0x7F, 0x00);
+ }
+}
+
+/* --------------------------------------------------------------------- */
+/* Function : XGI_SetLCDCap_B */
+/* Input : cx -> LCD Capability */
+/* Output : */
+/* Description : */
+/* --------------------------------------------------------------------- */
+static void XGI_SetLCDCap_B(unsigned short tempcx,
+ struct vb_device_info *pVBInfo)
+{
+ if (tempcx & EnableLCD24bpp) /* 24bits */
+ xgifb_reg_and_or(pVBInfo->Part2Port, 0x1A, 0xE0,
+ (unsigned short) (((tempcx & 0x00ff) >> 6)
+ | 0x0c));
+ else
+ xgifb_reg_and_or(pVBInfo->Part2Port, 0x1A, 0xE0,
+ (unsigned short) (((tempcx & 0x00ff) >> 6)
+ | 0x18)); /* Enable Dither */
+}
+
+static void XGI_LongWait(struct vb_device_info *pVBInfo)
+{
+ unsigned short i;
+
+ i = xgifb_reg_get(pVBInfo->P3c4, 0x1F);
+
+ if (!(i & 0xC0)) {
+ for (i = 0; i < 0xFFFF; i++) {
+ if (!(inb(pVBInfo->P3da) & 0x08))
+ break;
+ }
+
+ for (i = 0; i < 0xFFFF; i++) {
+ if ((inb(pVBInfo->P3da) & 0x08))
+ break;
+ }
+ }
+}
+
+static void SetSpectrum(struct vb_device_info *pVBInfo)
+{
+ unsigned short index;
+
+ index = XGI_GetLCDCapPtr(pVBInfo);
+
+ /* disable down spectrum D[4] */
+ xgifb_reg_and(pVBInfo->Part4Port, 0x30, 0x8F);
+ XGI_LongWait(pVBInfo);
+ xgifb_reg_or(pVBInfo->Part4Port, 0x30, 0x20); /* reset spectrum */
+ XGI_LongWait(pVBInfo);
+
+ xgifb_reg_set(pVBInfo->Part4Port, 0x31,
+ pVBInfo->LCDCapList[index].Spectrum_31);
+ xgifb_reg_set(pVBInfo->Part4Port, 0x32,
+ pVBInfo->LCDCapList[index].Spectrum_32);
+ xgifb_reg_set(pVBInfo->Part4Port, 0x33,
+ pVBInfo->LCDCapList[index].Spectrum_33);
+ xgifb_reg_set(pVBInfo->Part4Port, 0x34,
+ pVBInfo->LCDCapList[index].Spectrum_34);
+ XGI_LongWait(pVBInfo);
+ xgifb_reg_or(pVBInfo->Part4Port, 0x30, 0x40); /* enable spectrum */
+}
+
+static void XGI_SetLCDCap(struct vb_device_info *pVBInfo)
+{
+ unsigned short tempcx;
+
+ tempcx = pVBInfo->LCDCapList[XGI_GetLCDCapPtr(pVBInfo)].LCD_Capability;
+
+ if (pVBInfo->VBType & (VB_SIS301B | VB_SIS302B | VB_SIS301LV |
+ VB_SIS302LV | VB_XGI301C)) {
+ if (pVBInfo->VBType &
+ (VB_SIS301LV | VB_SIS302LV | VB_XGI301C)) {
+ /* Set 301LV Capability */
+ xgifb_reg_set(pVBInfo->Part4Port, 0x24,
+ (unsigned char) (tempcx & 0x1F));
+ }
+ /* VB Driving */
+ xgifb_reg_and_or(pVBInfo->Part4Port, 0x0D,
+ ~((EnableVBCLKDRVLOW | EnablePLLSPLOW) >> 8),
+ (unsigned short) ((tempcx & (EnableVBCLKDRVLOW
+ | EnablePLLSPLOW)) >> 8));
+
+ if (pVBInfo->VBInfo & SetCRT2ToLCD)
+ XGI_SetLCDCap_B(tempcx, pVBInfo);
+ else if (pVBInfo->VBInfo & XGI_SetCRT2ToLCDA)
+ XGI_SetLCDCap_A(tempcx, pVBInfo);
+
+ if (pVBInfo->VBType & (VB_SIS302LV | VB_XGI301C)) {
+ if (tempcx & EnableSpectrum)
+ SetSpectrum(pVBInfo);
+ }
+ } else {
+ /* LVDS,CH7017 */
+ XGI_SetLCDCap_A(tempcx, pVBInfo);
+ }
+}
+
+/* --------------------------------------------------------------------- */
+/* Function : XGI_SetAntiFlicker */
+/* Input : */
+/* Output : */
+/* Description : Set TV Customized Param. */
+/* --------------------------------------------------------------------- */
+static void XGI_SetAntiFlicker(struct vb_device_info *pVBInfo)
+{
+ unsigned short tempbx;
+
+ unsigned char tempah;
+
+ if (pVBInfo->TVInfo & (TVSetYPbPr525p | TVSetYPbPr750p))
+ return;
+
+ tempbx = XGI_GetTVPtrIndex(pVBInfo);
+ tempbx &= 0xFE;
+ tempah = TVAntiFlickList[tempbx];
+ tempah <<= 4;
+
+ xgifb_reg_and_or(pVBInfo->Part2Port, 0x0A, 0x8F, tempah);
+}
+
+static void XGI_SetEdgeEnhance(struct vb_device_info *pVBInfo)
+{
+ unsigned short tempbx;
+
+ unsigned char tempah;
+
+ tempbx = XGI_GetTVPtrIndex(pVBInfo);
+ tempbx &= 0xFE;
+ tempah = TVEdgeList[tempbx];
+ tempah <<= 5;
+
+ xgifb_reg_and_or(pVBInfo->Part2Port, 0x3A, 0x1F, tempah);
+}
+
+static void XGI_SetPhaseIncr(struct vb_device_info *pVBInfo)
+{
+ unsigned short tempbx;
+
+ unsigned char tempcl, tempch;
+
+ unsigned long tempData;
+
+ XGI_GetTVPtrIndex2(&tempbx, &tempcl, &tempch, pVBInfo); /* bx, cl, ch */
+ tempData = TVPhaseList[tempbx];
+
+ xgifb_reg_set(pVBInfo->Part2Port, 0x31, (unsigned short) (tempData
+ & 0x000000FF));
+ xgifb_reg_set(pVBInfo->Part2Port, 0x32, (unsigned short) ((tempData
+ & 0x0000FF00) >> 8));
+ xgifb_reg_set(pVBInfo->Part2Port, 0x33, (unsigned short) ((tempData
+ & 0x00FF0000) >> 16));
+ xgifb_reg_set(pVBInfo->Part2Port, 0x34, (unsigned short) ((tempData
+ & 0xFF000000) >> 24));
+}
+
+static void XGI_SetYFilter(unsigned short ModeIdIndex,
+ struct vb_device_info *pVBInfo)
+{
+ unsigned short tempbx, index;
+ unsigned char const *filterPtr;
+ unsigned char tempcl, tempch, tempal;
+
+ XGI_GetTVPtrIndex2(&tempbx, &tempcl, &tempch, pVBInfo); /* bx, cl, ch */
+
+ switch (tempbx) {
+ case 0x00:
+ case 0x04:
+ filterPtr = NTSCYFilter1;
+ break;
+
+ case 0x01:
+ filterPtr = PALYFilter1;
+ break;
+
+ case 0x02:
+ case 0x05:
+ case 0x0D:
+ case 0x03:
+ filterPtr = xgifb_palmn_yfilter1;
+ break;
+
+ case 0x08:
+ case 0x0C:
+ case 0x0A:
+ case 0x0B:
+ case 0x09:
+ filterPtr = xgifb_yfilter2;
+ break;
+
+ default:
+ return;
+ }
+
+ tempal = XGI330_EModeIDTable[ModeIdIndex].VB_ExtTVYFilterIndex;
+ if (tempcl == 0)
+ index = tempal * 4;
+ else
+ index = tempal * 7;
+
+ if ((tempcl == 0) && (tempch == 1)) {
+ xgifb_reg_set(pVBInfo->Part2Port, 0x35, 0);
+ xgifb_reg_set(pVBInfo->Part2Port, 0x36, 0);
+ xgifb_reg_set(pVBInfo->Part2Port, 0x37, 0);
+ xgifb_reg_set(pVBInfo->Part2Port, 0x38, filterPtr[index++]);
+ } else {
+ xgifb_reg_set(pVBInfo->Part2Port, 0x35, filterPtr[index++]);
+ xgifb_reg_set(pVBInfo->Part2Port, 0x36, filterPtr[index++]);
+ xgifb_reg_set(pVBInfo->Part2Port, 0x37, filterPtr[index++]);
+ xgifb_reg_set(pVBInfo->Part2Port, 0x38, filterPtr[index++]);
+ }
+
+ if (pVBInfo->VBType & (VB_SIS301B | VB_SIS302B | VB_SIS301LV
+ | VB_SIS302LV | VB_XGI301C)) {
+ xgifb_reg_set(pVBInfo->Part2Port, 0x48, filterPtr[index++]);
+ xgifb_reg_set(pVBInfo->Part2Port, 0x49, filterPtr[index++]);
+ xgifb_reg_set(pVBInfo->Part2Port, 0x4A, filterPtr[index++]);
+ }
+}
+
+/* --------------------------------------------------------------------- */
+/* Function : XGI_OEM310Setting */
+/* Input : */
+/* Output : */
+/* Description : Customized Param. for 301 */
+/* --------------------------------------------------------------------- */
+static void XGI_OEM310Setting(unsigned short ModeIdIndex,
+ struct vb_device_info *pVBInfo)
+{
+ XGI_SetDelayComp(pVBInfo);
+
+ if (pVBInfo->VBInfo & (SetCRT2ToLCD | XGI_SetCRT2ToLCDA))
+ XGI_SetLCDCap(pVBInfo);
+
+ if (pVBInfo->VBInfo & SetCRT2ToTV) {
+ XGI_SetPhaseIncr(pVBInfo);
+ XGI_SetYFilter(ModeIdIndex, pVBInfo);
+ XGI_SetAntiFlicker(pVBInfo);
+
+ if (pVBInfo->VBType & VB_SIS301)
+ XGI_SetEdgeEnhance(pVBInfo);
+ }
+}
+
+/* --------------------------------------------------------------------- */
+/* Function : XGI_SetCRT2ModeRegs */
+/* Input : */
+/* Output : */
+/* Description : Origin code for crt2group */
+/* --------------------------------------------------------------------- */
+static void XGI_SetCRT2ModeRegs(struct vb_device_info *pVBInfo)
+{
+ unsigned short tempbl;
+ short tempcl;
+
+ unsigned char tempah;
+
+ tempah = 0;
+ if (!(pVBInfo->VBInfo & DisableCRT2Display)) {
+ tempah = xgifb_reg_get(pVBInfo->Part1Port, 0x00);
+ tempah &= ~0x10; /* BTRAMDAC */
+ tempah |= 0x40; /* BTRAM */
+
+ if (pVBInfo->VBInfo & (SetCRT2ToRAMDAC | SetCRT2ToTV
+ | SetCRT2ToLCD)) {
+ tempah = 0x40; /* BTDRAM */
+ tempcl = pVBInfo->ModeType;
+ tempcl -= ModeVGA;
+ if (tempcl >= 0) {
+ /* BT Color */
+ tempah = (0x008 >> tempcl);
+ if (tempah == 0)
+ tempah = 1;
+ tempah |= 0x040;
+ }
+ if (pVBInfo->VBInfo & SetInSlaveMode)
+ tempah ^= 0x50; /* BTDAC */
+ }
+ }
+
+ xgifb_reg_set(pVBInfo->Part1Port, 0x00, tempah);
+ tempah = 0x08;
+ tempbl = 0xf0;
+
+ if (pVBInfo->VBInfo & DisableCRT2Display)
+ goto reg_and_or;
+
+ tempah = 0x00;
+ tempbl = 0xff;
+
+ if (!(pVBInfo->VBInfo & (SetCRT2ToRAMDAC | SetCRT2ToTV |
+ SetCRT2ToLCD | XGI_SetCRT2ToLCDA)))
+ goto reg_and_or;
+
+ if ((pVBInfo->VBInfo & XGI_SetCRT2ToLCDA) &&
+ (!(pVBInfo->VBInfo & SetSimuScanMode))) {
+ tempbl &= 0xf7;
+ tempah |= 0x01;
+ goto reg_and_or;
+ }
+
+ if (pVBInfo->VBInfo & XGI_SetCRT2ToLCDA) {
+ tempbl &= 0xf7;
+ tempah |= 0x01;
+ }
+
+ if (!(pVBInfo->VBInfo & (SetCRT2ToRAMDAC | SetCRT2ToTV | SetCRT2ToLCD)))
+ goto reg_and_or;
+
+ tempbl &= 0xf8;
+ tempah = 0x01;
+
+ if (!(pVBInfo->VBInfo & SetInSlaveMode))
+ tempah |= 0x02;
+
+ if (!(pVBInfo->VBInfo & SetCRT2ToRAMDAC)) {
+ tempah = tempah ^ 0x05;
+ if (!(pVBInfo->VBInfo & SetCRT2ToLCD))
+ tempah = tempah ^ 0x01;
+ }
+
+ if (!(pVBInfo->VBInfo & SetCRT2ToDualEdge))
+ tempah |= 0x08;
+
+reg_and_or:
+ xgifb_reg_and_or(pVBInfo->Part1Port, 0x2e, tempbl, tempah);
+
+ if (pVBInfo->VBInfo & (SetCRT2ToRAMDAC | SetCRT2ToTV | SetCRT2ToLCD
+ | XGI_SetCRT2ToLCDA)) {
+ tempah &= (~0x08);
+ if ((pVBInfo->ModeType == ModeVGA) && (!(pVBInfo->VBInfo
+ & SetInSlaveMode))) {
+ tempah |= 0x010;
+ }
+ tempah |= 0x080;
+
+ if (pVBInfo->VBInfo & SetCRT2ToTV) {
+ tempah |= 0x020;
+ if (pVBInfo->VBInfo & DriverMode)
+ tempah = tempah ^ 0x20;
+ }
+
+ xgifb_reg_and_or(pVBInfo->Part4Port, 0x0D, ~0x0BF, tempah);
+ tempah = 0;
+
+ if (pVBInfo->LCDInfo & SetLCDDualLink)
+ tempah |= 0x40;
+
+ if (pVBInfo->VBInfo & SetCRT2ToTV) {
+ if (pVBInfo->TVInfo & RPLLDIV2XO)
+ tempah |= 0x40;
+ }
+
+ if ((pVBInfo->LCDResInfo == Panel_1280x1024)
+ || (pVBInfo->LCDResInfo == Panel_1280x1024x75))
+ tempah |= 0x80;
+
+ if (pVBInfo->LCDResInfo == Panel_1280x960)
+ tempah |= 0x80;
+
+ xgifb_reg_set(pVBInfo->Part4Port, 0x0C, tempah);
+ }
+
+ if (pVBInfo->VBType & (VB_SIS301B | VB_SIS302B | VB_SIS301LV
+ | VB_SIS302LV | VB_XGI301C)) {
+ tempah = 0;
+ tempbl = 0xfb;
+
+ if (pVBInfo->VBInfo & SetCRT2ToDualEdge) {
+ tempbl = 0xff;
+ if (pVBInfo->VBInfo & XGI_SetCRT2ToLCDA)
+ tempah |= 0x04; /* shampoo 0129 */
+ }
+
+ xgifb_reg_and_or(pVBInfo->Part1Port, 0x13, tempbl, tempah);
+ tempah = 0x00;
+ tempbl = 0xcf;
+ if (!(pVBInfo->VBInfo & DisableCRT2Display)) {
+ if (pVBInfo->VBInfo & SetCRT2ToDualEdge)
+ tempah |= 0x30;
+ }
+
+ xgifb_reg_and_or(pVBInfo->Part1Port, 0x2c, tempbl, tempah);
+ tempah = 0;
+ tempbl = 0x3f;
+
+ if (!(pVBInfo->VBInfo & DisableCRT2Display)) {
+ if (pVBInfo->VBInfo & SetCRT2ToDualEdge)
+ tempah |= 0xc0;
+ }
+ xgifb_reg_and_or(pVBInfo->Part4Port, 0x21, tempbl, tempah);
+ }
+
+ tempah = 0;
+ tempbl = 0x7f;
+ if (!(pVBInfo->VBInfo & XGI_SetCRT2ToLCDA)) {
+ tempbl = 0xff;
+ if (!(pVBInfo->VBInfo & SetCRT2ToDualEdge))
+ tempah |= 0x80;
+ }
+
+ xgifb_reg_and_or(pVBInfo->Part4Port, 0x23, tempbl, tempah);
+
+ if (pVBInfo->VBType & (VB_SIS302LV | VB_XGI301C)) {
+ if (pVBInfo->LCDInfo & SetLCDDualLink) {
+ xgifb_reg_or(pVBInfo->Part4Port, 0x27, 0x20);
+ xgifb_reg_or(pVBInfo->Part4Port, 0x34, 0x10);
+ }
+ }
+}
+
+
+void XGI_UnLockCRT2(struct vb_device_info *pVBInfo)
+{
+ xgifb_reg_and_or(pVBInfo->Part1Port, 0x2f, 0xFF, 0x01);
+}
+
+void XGI_LockCRT2(struct vb_device_info *pVBInfo)
+{
+ xgifb_reg_and_or(pVBInfo->Part1Port, 0x2F, 0xFE, 0x00);
+}
+
+unsigned short XGI_GetRatePtrCRT2(struct xgi_hw_device_info *pXGIHWDE,
+ unsigned short ModeNo, unsigned short ModeIdIndex,
+ struct vb_device_info *pVBInfo)
+{
+ const u8 LCDARefreshIndex[] = {
+ 0x00, 0x00, 0x03, 0x01, 0x01, 0x01, 0x01, 0x00 };
+
+ unsigned short RefreshRateTableIndex, i, index, temp;
+
+ index = xgifb_reg_get(pVBInfo->P3d4, 0x33);
+ index >>= pVBInfo->SelectCRT2Rate;
+ index &= 0x0F;
+
+ if (pVBInfo->LCDInfo & LCDNonExpanding)
+ index = 0;
+
+ if (index > 0)
+ index--;
+
+ if (pVBInfo->SetFlag & ProgrammingCRT2) {
+ if (pVBInfo->VBInfo & (SetCRT2ToLCD | XGI_SetCRT2ToLCDA)) {
+ temp = LCDARefreshIndex[pVBInfo->LCDResInfo & 0x07];
+
+ if (index > temp)
+ index = temp;
+ }
+ }
+
+ RefreshRateTableIndex = XGI330_EModeIDTable[ModeIdIndex].REFindex;
+ ModeNo = XGI330_RefIndex[RefreshRateTableIndex].ModeID;
+ if (pXGIHWDE->jChipType >= XG20) { /* for XG20, XG21, XG27 */
+ if ((XGI330_RefIndex[RefreshRateTableIndex].XRes == 800) &&
+ (XGI330_RefIndex[RefreshRateTableIndex].YRes == 600)) {
+ index++;
+ }
+ /* do the similar adjustment like XGISearchCRT1Rate() */
+ if ((XGI330_RefIndex[RefreshRateTableIndex].XRes == 1024) &&
+ (XGI330_RefIndex[RefreshRateTableIndex].YRes == 768)) {
+ index++;
+ }
+ if ((XGI330_RefIndex[RefreshRateTableIndex].XRes == 1280) &&
+ (XGI330_RefIndex[RefreshRateTableIndex].YRes == 1024)) {
+ index++;
+ }
+ }
+
+ i = 0;
+ do {
+ if (XGI330_RefIndex[RefreshRateTableIndex + i].
+ ModeID != ModeNo)
+ break;
+ temp = XGI330_RefIndex[RefreshRateTableIndex + i].Ext_InfoFlag;
+ temp &= ModeTypeMask;
+ if (temp < pVBInfo->ModeType)
+ break;
+ i++;
+ index--;
+
+ } while (index != 0xFFFF);
+ if (!(pVBInfo->VBInfo & SetCRT2ToRAMDAC)) {
+ if (pVBInfo->VBInfo & SetInSlaveMode) {
+ temp = XGI330_RefIndex[RefreshRateTableIndex + i - 1].
+ Ext_InfoFlag;
+ if (temp & InterlaceMode)
+ i++;
+ }
+ }
+ i--;
+ if ((pVBInfo->SetFlag & ProgrammingCRT2)) {
+ temp = XGI_AjustCRT2Rate(ModeIdIndex, RefreshRateTableIndex,
+ &i, pVBInfo);
+ }
+ return RefreshRateTableIndex + i;
+}
+
+static void XGI_SetLCDAGroup(unsigned short ModeNo, unsigned short ModeIdIndex,
+ struct xgi_hw_device_info *HwDeviceExtension,
+ struct vb_device_info *pVBInfo)
+{
+ unsigned short RefreshRateTableIndex;
+
+ pVBInfo->SetFlag |= ProgrammingCRT2;
+ RefreshRateTableIndex = XGI_GetRatePtrCRT2(HwDeviceExtension, ModeNo,
+ ModeIdIndex, pVBInfo);
+ XGI_GetLVDSResInfo(ModeIdIndex, pVBInfo);
+ XGI_GetLVDSData(ModeIdIndex, pVBInfo);
+ XGI_ModCRT1Regs(ModeIdIndex, HwDeviceExtension, pVBInfo);
+ XGI_SetLVDSRegs(ModeIdIndex, pVBInfo);
+ XGI_SetCRT2ECLK(ModeIdIndex, RefreshRateTableIndex, pVBInfo);
+}
+
+static unsigned char XGI_SetCRT2Group301(unsigned short ModeNo,
+ struct xgi_hw_device_info *HwDeviceExtension,
+ struct vb_device_info *pVBInfo)
+{
+ unsigned short ModeIdIndex, RefreshRateTableIndex;
+
+ pVBInfo->SetFlag |= ProgrammingCRT2;
+ XGI_SearchModeID(ModeNo, &ModeIdIndex);
+ pVBInfo->SelectCRT2Rate = 4;
+ RefreshRateTableIndex = XGI_GetRatePtrCRT2(HwDeviceExtension, ModeNo,
+ ModeIdIndex, pVBInfo);
+ XGI_SaveCRT2Info(ModeNo, pVBInfo);
+ XGI_GetCRT2ResInfo(ModeIdIndex, pVBInfo);
+ XGI_GetCRT2Data(ModeIdIndex, RefreshRateTableIndex, pVBInfo);
+ XGI_PreSetGroup1(ModeNo, ModeIdIndex, RefreshRateTableIndex, pVBInfo);
+ XGI_SetGroup1(ModeIdIndex, RefreshRateTableIndex, pVBInfo);
+ XGI_SetLockRegs(ModeNo, ModeIdIndex, pVBInfo);
+ XGI_SetGroup2(ModeNo, ModeIdIndex, pVBInfo);
+ XGI_SetLCDRegs(ModeIdIndex, pVBInfo);
+ XGI_SetTap4Regs(pVBInfo);
+ XGI_SetGroup3(ModeIdIndex, pVBInfo);
+ XGI_SetGroup4(ModeIdIndex, RefreshRateTableIndex, pVBInfo);
+ XGI_SetCRT2VCLK(ModeIdIndex, RefreshRateTableIndex, pVBInfo);
+ XGI_SetGroup5(pVBInfo);
+ XGI_AutoThreshold(pVBInfo);
+ return 1;
+}
+
+void XGI_SenseCRT1(struct vb_device_info *pVBInfo)
+{
+ unsigned char CRTCData[17] = { 0x5F, 0x4F, 0x50, 0x82, 0x55, 0x81,
+ 0x0B, 0x3E, 0xE9, 0x0B, 0xDF, 0xE7, 0x04, 0x00, 0x00,
+ 0x05, 0x00 };
+
+ unsigned char SR01 = 0, SR1F = 0, SR07 = 0, SR06 = 0;
+
+ unsigned char CR17, CR63, SR31;
+ unsigned short temp;
+
+ int i;
+
+ xgifb_reg_set(pVBInfo->P3c4, 0x05, 0x86);
+
+ /* to fix XG42 single LCD sense to CRT+LCD */
+ xgifb_reg_set(pVBInfo->P3d4, 0x57, 0x4A);
+ xgifb_reg_set(pVBInfo->P3d4, 0x53, (xgifb_reg_get(
+ pVBInfo->P3d4, 0x53) | 0x02));
+
+ SR31 = xgifb_reg_get(pVBInfo->P3c4, 0x31);
+ CR63 = xgifb_reg_get(pVBInfo->P3d4, 0x63);
+ SR01 = xgifb_reg_get(pVBInfo->P3c4, 0x01);
+
+ xgifb_reg_set(pVBInfo->P3c4, 0x01, (unsigned char) (SR01 & 0xDF));
+ xgifb_reg_set(pVBInfo->P3d4, 0x63, (unsigned char) (CR63 & 0xBF));
+
+ CR17 = xgifb_reg_get(pVBInfo->P3d4, 0x17);
+ xgifb_reg_set(pVBInfo->P3d4, 0x17, (unsigned char) (CR17 | 0x80));
+
+ SR1F = xgifb_reg_get(pVBInfo->P3c4, 0x1F);
+ xgifb_reg_set(pVBInfo->P3c4, 0x1F, (unsigned char) (SR1F | 0x04));
+
+ SR07 = xgifb_reg_get(pVBInfo->P3c4, 0x07);
+ xgifb_reg_set(pVBInfo->P3c4, 0x07, (unsigned char) (SR07 & 0xFB));
+ SR06 = xgifb_reg_get(pVBInfo->P3c4, 0x06);
+ xgifb_reg_set(pVBInfo->P3c4, 0x06, (unsigned char) (SR06 & 0xC3));
+
+ xgifb_reg_set(pVBInfo->P3d4, 0x11, 0x00);
+
+ for (i = 0; i < 8; i++)
+ xgifb_reg_set(pVBInfo->P3d4, (unsigned short) i, CRTCData[i]);
+
+ for (i = 8; i < 11; i++)
+ xgifb_reg_set(pVBInfo->P3d4, (unsigned short) (i + 8),
+ CRTCData[i]);
+
+ for (i = 11; i < 13; i++)
+ xgifb_reg_set(pVBInfo->P3d4, (unsigned short) (i + 4),
+ CRTCData[i]);
+
+ for (i = 13; i < 16; i++)
+ xgifb_reg_set(pVBInfo->P3c4, (unsigned short) (i - 3),
+ CRTCData[i]);
+
+ xgifb_reg_set(pVBInfo->P3c4, 0x0E, (unsigned char) (CRTCData[16]
+ & 0xE0));
+
+ xgifb_reg_set(pVBInfo->P3c4, 0x31, 0x00);
+ xgifb_reg_set(pVBInfo->P3c4, 0x2B, 0x1B);
+ xgifb_reg_set(pVBInfo->P3c4, 0x2C, 0xE1);
+
+ outb(0x00, pVBInfo->P3c8);
+
+ for (i = 0; i < 256 * 3; i++)
+ outb(0x0F, (pVBInfo->P3c8 + 1)); /* DAC_TEST_PARMS */
+
+ mdelay(1);
+
+ XGI_WaitDisply(pVBInfo);
+ temp = inb(pVBInfo->P3c2);
+
+ if (temp & 0x10)
+ xgifb_reg_and_or(pVBInfo->P3d4, 0x32, 0xDF, 0x20);
+ else
+ xgifb_reg_and_or(pVBInfo->P3d4, 0x32, 0xDF, 0x00);
+
+ /* avoid display something, set BLACK DAC if not restore DAC */
+ outb(0x00, pVBInfo->P3c8);
+
+ for (i = 0; i < 256 * 3; i++)
+ outb(0, (pVBInfo->P3c8 + 1));
+
+ xgifb_reg_set(pVBInfo->P3c4, 0x01, SR01);
+ xgifb_reg_set(pVBInfo->P3d4, 0x63, CR63);
+ xgifb_reg_set(pVBInfo->P3c4, 0x31, SR31);
+
+ xgifb_reg_set(pVBInfo->P3d4, 0x53, (xgifb_reg_get(
+ pVBInfo->P3d4, 0x53) & 0xFD));
+ xgifb_reg_set(pVBInfo->P3c4, 0x1F, (unsigned char) SR1F);
+}
+
+static void XGI_EnableBridge(struct xgifb_video_info *xgifb_info,
+ struct xgi_hw_device_info *HwDeviceExtension,
+ struct vb_device_info *pVBInfo)
+{
+ unsigned short tempah;
+
+ if (pVBInfo->VBType & (VB_SIS301B | VB_SIS302B | VB_SIS301LV
+ | VB_SIS302LV | VB_XGI301C)) {
+ if (pVBInfo->VBInfo & SetCRT2ToDualEdge)
+ /* Power on */
+ xgifb_reg_set(pVBInfo->Part1Port, 0x1E, 0x20);
+
+ if (pVBInfo->VBInfo & (SetCRT2ToLCD | SetCRT2ToTV |
+ SetCRT2ToRAMDAC)) {
+ tempah = xgifb_reg_get(pVBInfo->P3c4, 0x32);
+ tempah &= 0xDF;
+ if (pVBInfo->VBInfo & SetInSlaveMode) {
+ if (!(pVBInfo->VBInfo & SetCRT2ToRAMDAC))
+ tempah |= 0x20;
+ }
+ xgifb_reg_set(pVBInfo->P3c4, 0x32, tempah);
+ xgifb_reg_or(pVBInfo->P3c4, 0x1E, 0x20);
+
+ tempah = xgifb_reg_get(pVBInfo->Part1Port, 0x2E);
+
+ if (!(tempah & 0x80))
+ xgifb_reg_or(pVBInfo->Part1Port, 0x2E, 0x80);
+ xgifb_reg_and(pVBInfo->Part1Port, 0x00, 0x7F);
+ }
+
+ if (!(pVBInfo->VBInfo & DisableCRT2Display)) {
+ xgifb_reg_and_or(pVBInfo->Part2Port, 0x00, ~0xE0,
+ 0x20); /* shampoo 0129 */
+ if (pVBInfo->VBType & (VB_SIS302LV | VB_XGI301C)) {
+ if (pVBInfo->VBInfo &
+ (SetCRT2ToLCD | XGI_SetCRT2ToLCDA))
+ /* LVDS PLL power on */
+ xgifb_reg_and(pVBInfo->Part4Port, 0x2A,
+ 0x7F);
+ /* LVDS Driver power on */
+ xgifb_reg_and(pVBInfo->Part4Port, 0x30, 0x7F);
+ }
+ }
+
+ tempah = 0x00;
+
+ if (!(pVBInfo->VBInfo & DisableCRT2Display)) {
+ tempah = 0xc0;
+
+ if (!(pVBInfo->VBInfo & SetSimuScanMode) &&
+ (pVBInfo->VBInfo & XGI_SetCRT2ToLCDA) &&
+ (pVBInfo->VBInfo & SetCRT2ToDualEdge)) {
+ tempah = tempah & 0x40;
+ if (pVBInfo->VBInfo & XGI_SetCRT2ToLCDA)
+ tempah = tempah ^ 0xC0;
+ }
+ }
+
+ /* EnablePart4_1F */
+ xgifb_reg_or(pVBInfo->Part4Port, 0x1F, tempah);
+
+ XGI_DisableGatingCRT(pVBInfo);
+ XGI_DisplayOn(xgifb_info, HwDeviceExtension, pVBInfo);
+ } /* 301 */
+ else { /* LVDS */
+ if (pVBInfo->VBInfo & (SetCRT2ToTV | SetCRT2ToLCD
+ | XGI_SetCRT2ToLCDA))
+ /* enable CRT2 */
+ xgifb_reg_or(pVBInfo->Part1Port, 0x1E, 0x20);
+
+ tempah = xgifb_reg_get(pVBInfo->Part1Port, 0x2E);
+ if (!(tempah & 0x80))
+ xgifb_reg_or(pVBInfo->Part1Port, 0x2E, 0x80);
+
+ xgifb_reg_and(pVBInfo->Part1Port, 0x00, 0x7F);
+ XGI_DisplayOn(xgifb_info, HwDeviceExtension, pVBInfo);
+ } /* End of VB */
+}
+
+static void XGI_SetCRT1Group(struct xgifb_video_info *xgifb_info,
+ struct xgi_hw_device_info *HwDeviceExtension,
+ unsigned short ModeNo, unsigned short ModeIdIndex,
+ struct vb_device_info *pVBInfo)
+{
+ unsigned short RefreshRateTableIndex, temp;
+
+ XGI_SetSeqRegs(pVBInfo);
+ outb(XGI330_StandTable.MISC, pVBInfo->P3c2);
+ XGI_SetCRTCRegs(pVBInfo);
+ XGI_SetATTRegs(ModeIdIndex, pVBInfo);
+ XGI_SetGRCRegs(pVBInfo);
+ XGI_ClearExt1Regs(pVBInfo);
+
+ if (HwDeviceExtension->jChipType == XG27) {
+ if (pVBInfo->IF_DEF_LVDS == 0)
+ XGI_SetDefaultVCLK(pVBInfo);
+ }
+
+ temp = ~ProgrammingCRT2;
+ pVBInfo->SetFlag &= temp;
+ pVBInfo->SelectCRT2Rate = 0;
+
+ if (pVBInfo->VBType & (VB_SIS301B | VB_SIS302B | VB_SIS301LV
+ | VB_SIS302LV | VB_XGI301C)) {
+ if (pVBInfo->VBInfo & (SetSimuScanMode | XGI_SetCRT2ToLCDA
+ | SetInSlaveMode)) {
+ pVBInfo->SetFlag |= ProgrammingCRT2;
+ }
+ }
+
+ RefreshRateTableIndex = XGI_GetRatePtrCRT2(HwDeviceExtension, ModeNo,
+ ModeIdIndex, pVBInfo);
+ if (RefreshRateTableIndex != 0xFFFF) {
+ XGI_SetSync(RefreshRateTableIndex, pVBInfo);
+ XGI_SetCRT1CRTC(ModeIdIndex, RefreshRateTableIndex,
+ pVBInfo, HwDeviceExtension);
+ XGI_SetCRT1DE(ModeIdIndex, RefreshRateTableIndex, pVBInfo);
+ XGI_SetCRT1Offset(ModeNo, ModeIdIndex, RefreshRateTableIndex,
+ HwDeviceExtension, pVBInfo);
+ XGI_SetCRT1VCLK(ModeIdIndex, HwDeviceExtension,
+ RefreshRateTableIndex, pVBInfo);
+ }
+
+ if (HwDeviceExtension->jChipType >= XG21) {
+ temp = xgifb_reg_get(pVBInfo->P3d4, 0x38);
+ if (temp & 0xA0) {
+
+ if (HwDeviceExtension->jChipType == XG27)
+ XGI_SetXG27CRTC(RefreshRateTableIndex, pVBInfo);
+ else
+ XGI_SetXG21CRTC(RefreshRateTableIndex, pVBInfo);
+
+ XGI_UpdateXG21CRTC(ModeNo, pVBInfo,
+ RefreshRateTableIndex);
+
+ xgifb_set_lcd(HwDeviceExtension->jChipType,
+ pVBInfo, RefreshRateTableIndex);
+
+ if (pVBInfo->IF_DEF_LVDS == 1)
+ xgifb_set_lvds(xgifb_info,
+ HwDeviceExtension->jChipType,
+ ModeIdIndex, pVBInfo);
+ }
+ }
+
+ pVBInfo->SetFlag &= (~ProgrammingCRT2);
+ XGI_SetCRT1FIFO(HwDeviceExtension, pVBInfo);
+ XGI_SetCRT1ModeRegs(HwDeviceExtension, ModeIdIndex,
+ RefreshRateTableIndex, pVBInfo);
+ XGI_LoadDAC(pVBInfo);
+}
+
+unsigned char XGISetModeNew(struct xgifb_video_info *xgifb_info,
+ struct xgi_hw_device_info *HwDeviceExtension,
+ unsigned short ModeNo)
+{
+ unsigned short ModeIdIndex;
+ struct vb_device_info VBINF;
+ struct vb_device_info *pVBInfo = &VBINF;
+
+ pVBInfo->IF_DEF_LVDS = 0;
+
+ if (HwDeviceExtension->jChipType >= XG20)
+ pVBInfo->VBType = 0; /*set VBType default 0*/
+
+ XGIRegInit(pVBInfo, xgifb_info->vga_base);
+
+ /* for x86 Linux, XG21 LVDS */
+ if (HwDeviceExtension->jChipType == XG21) {
+ if ((xgifb_reg_get(pVBInfo->P3d4, 0x38) & 0xE0) == 0xC0)
+ pVBInfo->IF_DEF_LVDS = 1;
+ }
+ if (HwDeviceExtension->jChipType == XG27) {
+ if ((xgifb_reg_get(pVBInfo->P3d4, 0x38) & 0xE0) == 0xC0) {
+ if (xgifb_reg_get(pVBInfo->P3d4, 0x30) & 0x20)
+ pVBInfo->IF_DEF_LVDS = 1;
+ }
+ }
+
+ InitTo330Pointer(HwDeviceExtension->jChipType, pVBInfo);
+ if (ModeNo & 0x80)
+ ModeNo = ModeNo & 0x7F;
+ xgifb_reg_set(pVBInfo->P3c4, 0x05, 0x86);
+
+ if (HwDeviceExtension->jChipType < XG20)
+ XGI_UnLockCRT2(pVBInfo);
+
+ XGI_SearchModeID(ModeNo, &ModeIdIndex);
+
+ if (HwDeviceExtension->jChipType < XG20) {
+ XGI_GetVBInfo(ModeIdIndex, pVBInfo);
+ XGI_GetTVInfo(ModeIdIndex, pVBInfo);
+ XGI_GetLCDInfo(ModeIdIndex, pVBInfo);
+ XGI_DisableBridge(xgifb_info, HwDeviceExtension, pVBInfo);
+
+ if (pVBInfo->VBInfo & (SetSimuScanMode | XGI_SetCRT2ToLCDA) ||
+ (!(pVBInfo->VBInfo & SwitchCRT2))) {
+ XGI_SetCRT1Group(xgifb_info, HwDeviceExtension, ModeNo,
+ ModeIdIndex, pVBInfo);
+
+ if (pVBInfo->VBInfo & XGI_SetCRT2ToLCDA) {
+ XGI_SetLCDAGroup(ModeNo, ModeIdIndex,
+ HwDeviceExtension, pVBInfo);
+ }
+ }
+
+ if (pVBInfo->VBInfo & (SetSimuScanMode | SwitchCRT2)) {
+ switch (HwDeviceExtension->ujVBChipID) {
+ case VB_CHIP_301: /* fall through */
+ case VB_CHIP_302:
+ XGI_SetCRT2Group301(ModeNo, HwDeviceExtension,
+ pVBInfo); /*add for CRT2 */
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ XGI_SetCRT2ModeRegs(pVBInfo);
+ XGI_OEM310Setting(ModeIdIndex, pVBInfo); /*0212*/
+ XGI_EnableBridge(xgifb_info, HwDeviceExtension, pVBInfo);
+ } /* !XG20 */
+ else {
+ if (pVBInfo->IF_DEF_LVDS == 1)
+ if (!XGI_XG21CheckLVDSMode(xgifb_info, ModeNo,
+ ModeIdIndex))
+ return 0;
+
+ pVBInfo->ModeType = XGI330_EModeIDTable[ModeIdIndex].
+ Ext_ModeFlag & ModeTypeMask;
+
+ pVBInfo->SetFlag = 0;
+ pVBInfo->VBInfo = DisableCRT2Display;
+
+ XGI_DisplayOff(xgifb_info, HwDeviceExtension, pVBInfo);
+
+ XGI_SetCRT1Group(xgifb_info, HwDeviceExtension, ModeNo,
+ ModeIdIndex, pVBInfo);
+
+ XGI_DisplayOn(xgifb_info, HwDeviceExtension, pVBInfo);
+ }
+
+ XGI_UpdateModeInfo(pVBInfo);
+
+ if (HwDeviceExtension->jChipType < XG20)
+ XGI_LockCRT2(pVBInfo);
+
+ return 1;
+}
diff --git a/drivers/staging/xgifb/vb_setmode.h b/drivers/staging/xgifb/vb_setmode.h
new file mode 100644
index 000000000..5301bec64
--- /dev/null
+++ b/drivers/staging/xgifb/vb_setmode.h
@@ -0,0 +1,23 @@
+#ifndef _VBSETMODE_
+#define _VBSETMODE_
+
+extern void InitTo330Pointer(unsigned char, struct vb_device_info *);
+extern void XGI_UnLockCRT2(struct vb_device_info *);
+extern void XGI_LockCRT2(struct vb_device_info *);
+extern void XGI_DisplayOff(struct xgifb_video_info *,
+ struct xgi_hw_device_info *,
+ struct vb_device_info *);
+extern void XGI_GetVBType(struct vb_device_info *);
+extern void XGI_SenseCRT1(struct vb_device_info *);
+extern unsigned char XGISetModeNew(struct xgifb_video_info *xgifb_info,
+ struct xgi_hw_device_info *HwDeviceExtension,
+ unsigned short ModeNo);
+
+extern unsigned char XGI_SearchModeID(unsigned short ModeNo,
+ unsigned short *ModeIdIndex);
+extern unsigned short XGI_GetRatePtrCRT2(struct xgi_hw_device_info *pXGIHWDE,
+ unsigned short ModeNo,
+ unsigned short ModeIdIndex,
+ struct vb_device_info *);
+
+#endif
diff --git a/drivers/staging/xgifb/vb_struct.h b/drivers/staging/xgifb/vb_struct.h
new file mode 100644
index 000000000..0d2759455
--- /dev/null
+++ b/drivers/staging/xgifb/vb_struct.h
@@ -0,0 +1,166 @@
+#ifndef _VB_STRUCT_
+#define _VB_STRUCT_
+#include "../../video/fbdev/sis/vstruct.h"
+
+struct XGI_LVDSCRT1HDataStruct {
+ unsigned char Reg[8];
+};
+
+struct XGI_LVDSCRT1VDataStruct {
+ unsigned char Reg[7];
+};
+
+struct XGI_ExtStruct {
+ unsigned char Ext_ModeID;
+ unsigned short Ext_ModeFlag;
+ unsigned short Ext_ModeInfo;
+ unsigned char Ext_RESINFO;
+ unsigned char VB_ExtTVYFilterIndex;
+ unsigned char REFindex;
+};
+
+struct XGI_Ext2Struct {
+ unsigned short Ext_InfoFlag;
+ unsigned char Ext_CRT1CRTC;
+ unsigned char Ext_CRTVCLK;
+ unsigned char Ext_CRT2CRTC;
+ unsigned char Ext_CRT2CRTC2;
+ unsigned char ModeID;
+ unsigned short XRes;
+ unsigned short YRes;
+};
+
+struct XGI_ECLKDataStruct {
+ unsigned char SR2E, SR2F, SR30;
+ unsigned short CLOCK;
+};
+
+/*add for new UNIVGABIOS*/
+struct XGI_LCDDesStruct {
+ unsigned short LCDHDES;
+ unsigned short LCDHRS;
+ unsigned short LCDVDES;
+ unsigned short LCDVRS;
+};
+
+struct XGI330_LCDDataDesStruct2 {
+ unsigned short LCDHDES;
+ unsigned short LCDHRS;
+ unsigned short LCDVDES;
+ unsigned short LCDVRS;
+ unsigned short LCDHSync;
+ unsigned short LCDVSync;
+};
+
+struct XGI330_LCDDataTablStruct {
+ unsigned char PANELID;
+ unsigned short MASK;
+ unsigned short CAP;
+ void const *DATAPTR;
+};
+
+struct XGI330_TVDataTablStruct {
+ unsigned short MASK;
+ unsigned short CAP;
+ struct SiS_TVData const *DATAPTR;
+};
+
+
+struct XGI_TimingHStruct {
+ unsigned char data[8];
+};
+
+struct XGI_TimingVStruct {
+ unsigned char data[7];
+};
+
+struct XGI_XG21CRT1Struct {
+ unsigned char ModeID, CR02, CR03, CR15, CR16;
+};
+
+struct XGI330_LCDCapStruct {
+ unsigned char LCD_ID;
+ unsigned short LCD_Capability;
+ unsigned char LCD_HSyncWidth;
+ unsigned char LCD_VSyncWidth;
+ unsigned char LCD_VCLK;
+ unsigned char LCDA_VCLKData1;
+ unsigned char LCDA_VCLKData2;
+ unsigned char LCUCHAR_VCLKData1;
+ unsigned char LCUCHAR_VCLKData2;
+ unsigned char Spectrum_31;
+ unsigned char Spectrum_32;
+ unsigned char Spectrum_33;
+ unsigned char Spectrum_34;
+};
+
+struct XGI21_LVDSCapStruct {
+ unsigned short LVDS_Capability;
+ unsigned short LVDSHT;
+ unsigned short LVDSVT;
+ unsigned short LVDSHDE;
+ unsigned short LVDSVDE;
+ unsigned short LVDSHFP;
+ unsigned short LVDSVFP;
+ unsigned short LVDSHSYNC;
+ unsigned short LVDSVSYNC;
+ unsigned char VCLKData1;
+ unsigned char VCLKData2;
+ unsigned char PSC_S1; /* Duration between CPL on and signal on */
+ unsigned char PSC_S2; /* Duration signal on and Vdd on */
+ unsigned char PSC_S3; /* Duration between CPL off and signal off */
+ unsigned char PSC_S4; /* Duration signal off and Vdd off */
+ unsigned char PSC_S5;
+};
+
+struct XGI_CRT1TableStruct {
+ unsigned char CR[16];
+};
+
+
+struct XGI301C_Tap4TimingStruct {
+ unsigned short DE;
+ unsigned char Reg[64]; /* C0-FF */
+};
+
+struct vb_device_info {
+ unsigned long P3c4, P3d4, P3c0, P3ce, P3c2, P3cc;
+ unsigned long P3ca, P3c6, P3c7, P3c8, P3c9, P3da;
+ unsigned long Part0Port, Part1Port, Part2Port;
+ unsigned long Part3Port, Part4Port, Part5Port;
+ unsigned short RVBHCFACT, RVBHCMAX, RVBHRS;
+ unsigned short VGAVT, VGAHT, VGAVDE, VGAHDE;
+ unsigned short VT, HT, VDE, HDE;
+ unsigned short LCDHRS, LCDVRS, LCDHDES, LCDVDES;
+
+ unsigned short ModeType;
+ unsigned short IF_DEF_LVDS;
+ unsigned short IF_DEF_CRT2Monitor;
+ unsigned short IF_DEF_YPbPr;
+ unsigned short IF_DEF_HiVision;
+ unsigned short LCDResInfo, LCDTypeInfo, VBType;/*301b*/
+ unsigned short VBInfo, TVInfo, LCDInfo;
+ unsigned short SetFlag;
+ unsigned short NewFlickerMode;
+ unsigned short SelectCRT2Rate;
+
+ void __iomem *FBAddr;
+
+ unsigned char const *SR18;
+ unsigned char const (*CR40)[3];
+
+ struct SiS_MCLKData const *MCLKData;
+
+ unsigned char XGINew_CR97;
+
+ struct XGI330_LCDCapStruct const *LCDCapList;
+
+ struct XGI_TimingHStruct TimingH;
+ struct XGI_TimingVStruct TimingV;
+
+ int ram_type;
+ int ram_channel;
+ int ram_bus;
+}; /* _struct vb_device_info */
+
+#endif /* _VB_STRUCT_ */
diff --git a/drivers/staging/xgifb/vb_table.h b/drivers/staging/xgifb/vb_table.h
new file mode 100644
index 000000000..f17e5b9bd
--- /dev/null
+++ b/drivers/staging/xgifb/vb_table.h
@@ -0,0 +1,2491 @@
+#ifndef _VB_TABLE_
+#define _VB_TABLE_
+static const struct SiS_MCLKData XGI340New_MCLKData[] = {
+ {0x16, 0x01, 0x01, 166},
+ {0x19, 0x02, 0x01, 124},
+ {0x7C, 0x08, 0x01, 200},
+};
+
+static const struct SiS_MCLKData XGI27New_MCLKData[] = {
+ {0x5c, 0x23, 0x01, 166},
+ {0x19, 0x02, 0x01, 124},
+ {0x7C, 0x08, 0x80, 200},
+};
+
+const struct XGI_ECLKDataStruct XGI340_ECLKData[] = {
+ {0x5c, 0x23, 0x01, 166},
+ {0x55, 0x84, 0x01, 123},
+ {0x7C, 0x08, 0x01, 200},
+};
+
+static const unsigned char XG27_SR18[3] = {
+ 0x32, 0x32, 0x42 /* SR18 */
+};
+
+static const unsigned char XGI340_SR18[3] = {
+ 0x31, 0x42, 0x42 /* SR18 */
+};
+
+static const unsigned char XGI340_cr41[24][3] = {
+ {0x20, 0x50, 0x60}, /* 0 CR41 */
+ {0xc4, 0x40, 0x84}, /* 1 CR8A */
+ {0xc4, 0x40, 0x84}, /* 2 CR8B */
+ {0xb5, 0xa4, 0xa4},
+ {0xf0, 0xf0, 0xf0},
+ {0x90, 0x90, 0x24}, /* 5 CR68 */
+ {0x77, 0x77, 0x44}, /* 6 CR69 */
+ {0x77, 0x77, 0x44}, /* 7 CR6A */
+ {0xff, 0xff, 0xff}, /* 8 CR6D */
+ {0x55, 0x55, 0x55}, /* 9 CR80 */
+ {0x00, 0x00, 0x00}, /* 10 CR81 */
+ {0x88, 0xa8, 0x48}, /* 11 CR82 */
+ {0x44, 0x44, 0x77}, /* 12 CR85 */
+ {0x48, 0x48, 0x88}, /* 13 CR86 */
+ {0x54, 0x54, 0x44}, /* 14 CR90 */
+ {0x54, 0x54, 0x44}, /* 15 CR91 */
+ {0x0a, 0x0a, 0x07}, /* 16 CR92 */
+ {0x44, 0x44, 0x44}, /* 17 CR93 */
+ {0x10, 0x10, 0x0A}, /* 18 CR94 */
+ {0x11, 0x11, 0x0a}, /* 19 CR95 */
+ {0x05, 0x05, 0x05}, /* 20 CR96 */
+ {0xf0, 0xf0, 0xf0}, /* 21 CRC3 */
+ {0x05, 0x00, 0x02}, /* 22 CRC4 */
+ {0x00, 0x00, 0x00} /* 23 CRC5 */
+};
+
+static const unsigned char XGI27_cr41[24][3] = {
+ {0x20, 0x40, 0x60}, /* 0 CR41 */
+ {0xC4, 0x40, 0x84}, /* 1 CR8A */
+ {0xC4, 0x40, 0x84}, /* 2 CR8B */
+ {0xB3, 0x13, 0xa4}, /* 3 CR40[7],
+ CR99[2:0],
+ CR45[3:0]*/
+ {0xf0, 0xf5, 0xf0}, /* 4 CR59 */
+ {0x90, 0x90, 0x24}, /* 5 CR68 */
+ {0x77, 0x67, 0x44}, /* 6 CR69 */
+ {0x77, 0x77, 0x44}, /* 7 CR6A */
+ {0xff, 0xff, 0xff}, /* 8 CR6D */
+ {0x55, 0x55, 0x55}, /* 9 CR80 */
+ {0x00, 0x00, 0x00}, /* 10 CR81 */
+ {0x88, 0xcc, 0x48}, /* 11 CR82 */
+ {0x44, 0x88, 0x77}, /* 12 CR85 */
+ {0x48, 0x88, 0x88}, /* 13 CR86 */
+ {0x54, 0x32, 0x44}, /* 14 CR90 */
+ {0x54, 0x33, 0x44}, /* 15 CR91 */
+ {0x0a, 0x07, 0x07}, /* 16 CR92 */
+ {0x44, 0x63, 0x44}, /* 17 CR93 */
+ {0x10, 0x14, 0x0A}, /* 18 CR94 */
+ {0x11, 0x0B, 0x0C}, /* 19 CR95 */
+ {0x05, 0x22, 0x05}, /* 20 CR96 */
+ {0xf0, 0xf0, 0x00}, /* 21 CRC3 */
+ {0x05, 0x00, 0x02}, /* 22 CRC4 */
+ {0x00, 0x00, 0x00} /* 23 CRC5 */
+};
+
+/* CR47,CR48,CR49,CR4A,CR4B,CR4C,CR70,CR71,CR74,CR75,CR76,CR77 */
+const unsigned char XGI340_AGPReg[12] = {
+ 0x28, 0x23, 0x00, 0x20, 0x00, 0x20,
+ 0x00, 0x05, 0xd0, 0x10, 0x10, 0x00
+};
+
+const struct XGI_ExtStruct XGI330_EModeIDTable[] = {
+ {0x2e, 0x0a1b, 0x0306, 0x06, 0x05, 0x06},
+ {0x2f, 0x0a1b, 0x0305, 0x05, 0x05, 0x05},
+ {0x30, 0x2a1b, 0x0407, 0x07, 0x07, 0x0e},
+ {0x31, 0x0a1b, 0x030d, 0x0d, 0x06, 0x3d},
+ {0x32, 0x0a1b, 0x0a0e, 0x0e, 0x06, 0x3e},
+ {0x33, 0x0a1d, 0x0a0d, 0x0d, 0x06, 0x3d},
+ {0x34, 0x2a1d, 0x0a0e, 0x0e, 0x06, 0x3e},
+ {0x35, 0x0a1f, 0x0a0d, 0x0d, 0x06, 0x3d},
+ {0x36, 0x2a1f, 0x0a0e, 0x0e, 0x06, 0x3e},
+ {0x38, 0x0a1b, 0x0508, 0x08, 0x00, 0x16},
+ {0x3a, 0x0e3b, 0x0609, 0x09, 0x00, 0x1e},
+ {0x3c, 0x0e3b, 0x070a, 0x0a, 0x00, 0x22}, /* mode 1600x1200
+ add CRT2MODE [2003/10/07] */
+ {0x3d, 0x0e7d, 0x070a, 0x0a, 0x00, 0x22}, /* mode 1600x1200
+ add CRT2MODE */
+ {0x40, 0x9a1c, 0x0000, 0x00, 0x04, 0x00},
+ {0x41, 0x9a1d, 0x0000, 0x00, 0x04, 0x00},
+ {0x43, 0x0a1c, 0x0306, 0x06, 0x05, 0x06},
+ {0x44, 0x0a1d, 0x0306, 0x06, 0x05, 0x06},
+ {0x46, 0x2a1c, 0x0407, 0x07, 0x07, 0x0e},
+ {0x47, 0x2a1d, 0x0407, 0x07, 0x07, 0x0e},
+ {0x49, 0x0a3c, 0x0508, 0x08, 0x00, 0x16},
+ {0x4a, 0x0a3d, 0x0508, 0x08, 0x00, 0x16},
+ {0x4c, 0x0e7c, 0x0609, 0x09, 0x00, 0x1e},
+ {0x4d, 0x0e7d, 0x0609, 0x09, 0x00, 0x1e},
+ {0x50, 0x9a1b, 0x0001, 0x01, 0x04, 0x02},
+ {0x51, 0xba1b, 0x0103, 0x03, 0x07, 0x03},
+ {0x52, 0x9a1b, 0x0204, 0x04, 0x00, 0x04},
+ {0x56, 0x9a1d, 0x0001, 0x01, 0x04, 0x02},
+ {0x57, 0xba1d, 0x0103, 0x03, 0x07, 0x03},
+ {0x58, 0x9a1d, 0x0204, 0x04, 0x00, 0x04},
+ {0x59, 0x9a1b, 0x0000, 0x00, 0x04, 0x00},
+ {0x5A, 0x021b, 0x0014, 0x01, 0x04, 0x3f},
+ {0x5B, 0x0a1d, 0x0014, 0x01, 0x04, 0x3f},
+ {0x5d, 0x0a1d, 0x0305, 0x05, 0x07, 0x05},
+ {0x62, 0x0a3f, 0x0306, 0x06, 0x05, 0x06},
+ {0x63, 0x2a3f, 0x0407, 0x07, 0x07, 0x0e},
+ {0x64, 0x0a7f, 0x0508, 0x08, 0x00, 0x16},
+ {0x65, 0x0eff, 0x0609, 0x09, 0x00, 0x1e},
+ {0x66, 0x0eff, 0x070a, 0x0a, 0x00, 0x22}, /* mode 1600x1200
+ add CRT2MODE */
+ {0x68, 0x067b, 0x080b, 0x0b, 0x00, 0x29},
+ {0x69, 0x06fd, 0x080b, 0x0b, 0x00, 0x29},
+ {0x6b, 0x07ff, 0x080b, 0x0b, 0x00, 0x29},
+ {0x6c, 0x067b, 0x090c, 0x0c, 0x00, 0x2f},
+ {0x6d, 0x06fd, 0x090c, 0x0c, 0x00, 0x2f},
+ {0x6e, 0x07ff, 0x090c, 0x0c, 0x00, 0x2f},
+ {0x70, 0x2a1b, 0x0410, 0x10, 0x07, 0x34},
+ {0x71, 0x0a1b, 0x0511, 0x11, 0x00, 0x37},
+ {0x74, 0x0a1d, 0x0511, 0x11, 0x00, 0x37},
+ {0x75, 0x0a3d, 0x0612, 0x12, 0x00, 0x3a},
+ {0x76, 0x2a1f, 0x0410, 0x10, 0x07, 0x34},
+ {0x77, 0x0a1f, 0x0511, 0x11, 0x00, 0x37},
+ {0x78, 0x0a3f, 0x0612, 0x12, 0x00, 0x3a},
+ {0x79, 0x0a3b, 0x0612, 0x12, 0x00, 0x3a},
+ {0x7a, 0x2a1d, 0x0410, 0x10, 0x07, 0x34},
+ {0x7b, 0x0e3b, 0x060f, 0x0f, 0x00, 0x1d},
+ {0x7c, 0x0e7d, 0x060f, 0x0f, 0x00, 0x1d},
+ {0x7d, 0x0eff, 0x060f, 0x0f, 0x00, 0x1d},
+ {0x20, 0x0e3b, 0x0D16, 0x16, 0x00, 0x43},
+ {0x21, 0x0e7d, 0x0D16, 0x16, 0x00, 0x43},
+ {0x22, 0x0eff, 0x0D16, 0x16, 0x00, 0x43},
+ {0x23, 0x0e3b, 0x0614, 0x14, 0x00, 0x41},
+ {0x24, 0x0e7d, 0x0614, 0x14, 0x00, 0x41},
+ {0x25, 0x0eff, 0x0614, 0x14, 0x00, 0x41},
+ {0x26, 0x063b, 0x0c15, 0x15, 0x00, 0x42},
+ {0x27, 0x067d, 0x0c15, 0x15, 0x00, 0x42},
+ {0x28, 0x06ff, 0x0c15, 0x15, 0x00, 0x42},
+ {0xff, 0x0000, 0x0000, 0x00, 0x00, 0x00}
+};
+
+static const struct SiS_StandTable_S XGI330_StandTable = {
+/* ExtVGATable */
+ 0x00, 0x00, 0x00, 0x0000,
+ {0x21, 0x0f, 0x00, 0x0e}, /* 0x21 = 0x01 | (0x20 = screen off) */
+ 0x23,
+ {0x5f, 0x4f, 0x50, 0x82, 0x54, 0x80, 0x0b, 0x3e,
+ 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xea, 0x8c, 0xdf, 0x28, 0x40, 0xe7, 0x04, 0xa3,
+ 0xff},
+ {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x01, 0x00, 0x00, 0x00},
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x05, 0x0f,
+ 0xff}
+};
+
+static const struct XGI_XG21CRT1Struct XGI_UpdateCRT1Table[] = {
+ {0x01, 0x27, 0x91, 0x8f, 0xc0}, /* 00 */
+ {0x03, 0x4f, 0x83, 0x8f, 0xc0}, /* 01 */
+ {0x05, 0x27, 0x91, 0x8f, 0xc0}, /* 02 */
+ {0x06, 0x4f, 0x83, 0x8f, 0xc0}, /* 03 */
+ {0x07, 0x4f, 0x83, 0x8f, 0xc0}, /* 04 */
+ {0x0d, 0x27, 0x91, 0x8f, 0xc0}, /* 05 */
+ {0x0e, 0x4f, 0x83, 0x8f, 0xc0}, /* 06 */
+ {0x0f, 0x4f, 0x83, 0x5d, 0xc0}, /* 07 */
+ {0x10, 0x4f, 0x83, 0x5d, 0xc0}, /* 08 */
+ {0x11, 0x4f, 0x83, 0xdf, 0x0c}, /* 09 */
+ {0x12, 0x4f, 0x83, 0xdf, 0x0c}, /* 10 */
+ {0x13, 0x4f, 0x83, 0x8f, 0xc0}, /* 11 */
+ {0x2e, 0x4f, 0x83, 0xdf, 0x0c}, /* 12 */
+ {0x2e, 0x4f, 0x87, 0xdf, 0xc0}, /* 13 */
+ {0x2f, 0x4f, 0x83, 0x8f, 0xc0}, /* 14 */
+ {0x50, 0x27, 0x91, 0xdf, 0x0c}, /* 15 */
+ {0x59, 0x27, 0x91, 0x8f, 0xc0} /* 16 */
+};
+
+const struct XGI_CRT1TableStruct XGI_CRT1Table[] = {
+ { {0x2d, 0x28, 0x90, 0x2c, 0x90, 0x00, 0x04, 0x00,
+ 0xbf, 0x1f, 0x9c, 0x8e, 0x96, 0xb9, 0x30} }, /* 0x0 */
+ { {0x2d, 0x28, 0x90, 0x2c, 0x90, 0x00, 0x04, 0x00,
+ 0x0b, 0x3e, 0xe9, 0x8b, 0xe7, 0x04, 0x00} }, /* 0x1 */
+ { {0x3D, 0x31, 0x81, 0x37, 0x1F, 0x00, 0x05, 0x00,
+ 0x72, 0xF0, 0x58, 0x8C, 0x57, 0x73, 0xA0} }, /* 0x2 */
+ { {0x4F, 0x3F, 0x93, 0x45, 0x0D, 0x00, 0x01, 0x00,
+ 0x24, 0xF5, 0x02, 0x88, 0xFF, 0x25, 0x90} }, /* 0x3 */
+ { {0x5F, 0x50, 0x82, 0x55, 0x81, 0x00, 0x05, 0x00,
+ 0xBF, 0x1F, 0x9C, 0x8E, 0x96, 0xB9, 0x30} }, /* 0x4 */
+ { {0x5F, 0x50, 0x82, 0x55, 0x81, 0x00, 0x05, 0x00,
+ 0x0B, 0x3E, 0xE9, 0x8B, 0xE7, 0x04, 0x00} }, /* 0x5 */
+ { {0x63, 0x50, 0x86, 0x56, 0x9B, 0x00, 0x01, 0x00,
+ 0x06, 0x3E, 0xE8, 0x8B, 0xE7, 0xFF, 0x10} }, /* 0x6 */
+ { {0x64, 0x4F, 0x88, 0x55, 0x9D, 0x00, 0x01, 0x00,
+ 0xF2, 0x1F, 0xE0, 0x83, 0xDF, 0xF3, 0x10} }, /* 0x7 */
+ { {0x63, 0x4F, 0x87, 0x5A, 0x81, 0x00, 0x05, 0x00,
+ 0xFB, 0x1F, 0xE0, 0x83, 0xDF, 0xFC, 0x10} }, /* 0x8 */
+ { {0x65, 0x4F, 0x89, 0x58, 0x80, 0x00, 0x05, 0x60,
+ 0xFB, 0x1F, 0xE0, 0x83, 0xDF, 0xFC, 0x80} }, /* 0x9 */
+ { {0x65, 0x4F, 0x89, 0x58, 0x80, 0x00, 0x05, 0x60,
+ 0x01, 0x3E, 0xE0, 0x83, 0xDF, 0x02, 0x80} }, /* 0xa */
+ { {0x67, 0x4F, 0x8B, 0x58, 0x81, 0x00, 0x05, 0x60,
+ 0x0D, 0x3E, 0xE0, 0x83, 0xDF, 0x0E, 0x90} }, /* 0xb */
+ { {0x65, 0x4F, 0x89, 0x57, 0x9F, 0x00, 0x01, 0x00,
+ 0xFB, 0x1F, 0xE6, 0x8A, 0xDF, 0xFC, 0x10} }, /* 0xc */
+ { {0x7B, 0x63, 0x9F, 0x6A, 0x93, 0x00, 0x05, 0x00, /* ;
+ 0D (800x600,56Hz) */
+ 0x6F, 0xF0, 0x58, 0x8A, 0x57, 0x70, 0xA0} }, /* ;
+ (VCLK 36.0MHz) */
+ { {0x7F, 0x63, 0x83, 0x6C, 0x1C, 0x00, 0x06, 0x00, /* ;
+ 0E (800x600,60Hz) */
+ 0x72, 0xF0, 0x58, 0x8C, 0x57, 0x73, 0xA0} }, /* ;
+ (VCLK 40.0MHz) */
+ { {0x7D, 0x63, 0x81, 0x6E, 0x1D, 0x00, 0x06, 0x00, /* ;
+ 0F (800x600,72Hz) */
+ 0x98, 0xF0, 0x7C, 0x82, 0x57, 0x99, 0x80} }, /* ;
+ (VCLK 50.0MHz) */
+ { {0x7F, 0x63, 0x83, 0x69, 0x13, 0x00, 0x06, 0x00, /* ;
+ 10 (800x600,75Hz) */
+ 0x6F, 0xF0, 0x58, 0x8B, 0x57, 0x70, 0xA0} }, /* ;
+ (VCLK 49.5MHz) */
+ { {0x7E, 0x63, 0x82, 0x6B, 0x13, 0x00, 0x06, 0x00, /* ;
+ 11 (800x600,85Hz) */
+ 0x75, 0xF0, 0x58, 0x8B, 0x57, 0x76, 0xA0} }, /* ;
+ (VCLK 56.25MHz) */
+ { {0x81, 0x63, 0x85, 0x6D, 0x18, 0x00, 0x06, 0x60, /* ;
+ 12 (800x600,100Hz) */
+ 0x7A, 0xF0, 0x58, 0x8B, 0x57, 0x7B, 0xA0} }, /* ;
+ (VCLK 75.8MHz) */
+ { {0x83, 0x63, 0x87, 0x6E, 0x19, 0x00, 0x06, 0x60, /* ;
+ 13 (800x600,120Hz) */
+ 0x81, 0xF0, 0x58, 0x8B, 0x57, 0x82, 0xA0} }, /* ;
+ (VCLK 79.411MHz) */
+ { {0x85, 0x63, 0x89, 0x6F, 0x1A, 0x00, 0x06, 0x60, /* ;
+ 14 (800x600,160Hz) */
+ 0x91, 0xF0, 0x58, 0x8B, 0x57, 0x92, 0xA0} }, /* ;
+ (VCLK 105.822MHz) */
+ { {0x99, 0x7F, 0x9D, 0x84, 0x1A, 0x00, 0x02, 0x00,
+ 0x96, 0x1F, 0x7F, 0x83, 0x7F, 0x97, 0x10} }, /* 0x15 */
+ { {0xA3, 0x7F, 0x87, 0x86, 0x97, 0x00, 0x02, 0x00,
+ 0x24, 0xF5, 0x02, 0x88, 0xFF, 0x25, 0x90} }, /* 0x16 */
+ { {0xA1, 0x7F, 0x85, 0x86, 0x97, 0x00, 0x02, 0x00,
+ 0x24, 0xF5, 0x02, 0x88, 0xFF, 0x25, 0x90} }, /* 0x17 */
+ { {0x9F, 0x7F, 0x83, 0x85, 0x91, 0x00, 0x02, 0x00,
+ 0x1E, 0xF5, 0x00, 0x83, 0xFF, 0x1F, 0x90} }, /* 0x18 */
+ { {0xA7, 0x7F, 0x8B, 0x89, 0x95, 0x00, 0x02, 0x00,
+ 0x26, 0xF5, 0x00, 0x83, 0xFF, 0x27, 0x90} }, /* 0x19 */
+ { {0xA9, 0x7F, 0x8D, 0x8C, 0x9A, 0x00, 0x02, 0x62,
+ 0x2C, 0xF5, 0x00, 0x83, 0xFF, 0x2D, 0x14} }, /* 0x1a */
+ { {0xAB, 0x7F, 0x8F, 0x8D, 0x9B, 0x00, 0x02, 0x62,
+ 0x35, 0xF5, 0x00, 0x83, 0xFF, 0x36, 0x14} }, /* 0x1b */
+ { {0xCF, 0x9F, 0x93, 0xB2, 0x01, 0x00, 0x03, 0x00,
+ 0x14, 0xBA, 0x00, 0x83, 0xFF, 0x15, 0x00} }, /* 0x1c */
+ { {0xCE, 0x9F, 0x92, 0xA9, 0x17, 0x00, 0x07, 0x00,
+ 0x28, 0x5A, 0x00, 0x83, 0xFF, 0x29, 0x89} }, /* 0x1d */
+ { {0xCE, 0x9F, 0x92, 0xA5, 0x17, 0x00, 0x07, 0x00,
+ 0x28, 0x5A, 0x00, 0x83, 0xFF, 0x29, 0x89} }, /* 0x1e */
+ { {0xD3, 0x9F, 0x97, 0xAB, 0x1F, 0x00, 0x07, 0x00,
+ 0x2E, 0x5A, 0x00, 0x83, 0xFF, 0x2F, 0x89} }, /* 0x1f */
+ { {0x09, 0xC7, 0x8D, 0xD3, 0x0B, 0x01, 0x04, 0x00,
+ 0xE0, 0x10, 0xB0, 0x83, 0xAF, 0xE1, 0x2F} }, /* 0x20 */
+ { {0x09, 0xC7, 0x8D, 0xD3, 0x0B, 0x01, 0x04, 0x00,
+ 0xE0, 0x10, 0xB0, 0x83, 0xAF, 0xE1, 0x2F} }, /* 0x21 */
+ { {0x09, 0xC7, 0x8D, 0xD3, 0x0B, 0x01, 0x04, 0x00,
+ 0xE0, 0x10, 0xB0, 0x83, 0xAF, 0xE1, 0x2F} }, /* 0x22 */
+ { {0x09, 0xC7, 0x8D, 0xD3, 0x0B, 0x01, 0x04, 0x00,
+ 0xE0, 0x10, 0xB0, 0x83, 0xAF, 0xE1, 0x2F} }, /* 0x23 */
+ { {0x09, 0xC7, 0x8D, 0xD3, 0x0B, 0x01, 0x04, 0x00,
+ 0xE0, 0x10, 0xB0, 0x83, 0xAF, 0xE1, 0x2F} }, /* 0x24 */
+ { {0x09, 0xC7, 0x8D, 0xD3, 0x0B, 0x01, 0x04, 0x00,
+ 0xE0, 0x10, 0xB0, 0x83, 0xAF, 0xE1, 0x2F} }, /* 0x25 */
+ { {0x09, 0xC7, 0x8D, 0xD3, 0x0B, 0x01, 0x04, 0x00,
+ 0xE0, 0x10, 0xB0, 0x83, 0xAF, 0xE1, 0x2F} }, /* 0x26 */
+ { {0x40, 0xEF, 0x84, 0x03, 0x1D, 0x41, 0x01, 0x00,
+ 0xDA, 0x1F, 0xA0, 0x83, 0x9F, 0xDB, 0x1F} }, /* 0x27 */
+ { {0x43, 0xEF, 0x87, 0x06, 0x00, 0x41, 0x05, 0x62,
+ 0xD4, 0x1F, 0xA0, 0x83, 0x9F, 0xD5, 0x9F} }, /* 0x28 */
+ { {0x45, 0xEF, 0x89, 0x07, 0x01, 0x41, 0x05, 0x62,
+ 0xD9, 0x1F, 0xA0, 0x83, 0x9F, 0xDA, 0x9F} }, /* 0x29 */
+ { {0x40, 0xEF, 0x84, 0x03, 0x1D, 0x41, 0x01, 0x00,
+ 0xDA, 0x1F, 0xA0, 0x83, 0x9F, 0xDB, 0x1F} }, /* 0x2a */
+ { {0x40, 0xEF, 0x84, 0x03, 0x1D, 0x41, 0x01, 0x00,
+ 0xDA, 0x1F, 0xA0, 0x83, 0x9F, 0xDB, 0x1F} }, /* 0x2b */
+ { {0x40, 0xEF, 0x84, 0x03, 0x1D, 0x41, 0x01, 0x00,
+ 0xDA, 0x1F, 0xA0, 0x83, 0x9F, 0xDB, 0x1F} }, /* 0x2c */
+ { {0x59, 0xFF, 0x9D, 0x17, 0x13, 0x41, 0x05, 0x44,
+ 0x33, 0xBA, 0x00, 0x83, 0xFF, 0x34, 0x0F} }, /* 0x2d */
+ { {0x5B, 0xFF, 0x9F, 0x18, 0x14, 0x41, 0x05, 0x44,
+ 0x38, 0xBA, 0x00, 0x83, 0xFF, 0x39, 0x0F} }, /* 0x2e */
+ { {0x5B, 0xFF, 0x9F, 0x18, 0x14, 0x41, 0x05, 0x44,
+ 0x3D, 0xBA, 0x00, 0x83, 0xFF, 0x3E, 0x0F} }, /* 0x2f */
+ { {0x5D, 0xFF, 0x81, 0x19, 0x95, 0x41, 0x05, 0x44,
+ 0x41, 0xBA, 0x00, 0x84, 0xFF, 0x42, 0x0F} }, /* 0x30 */
+ { {0x55, 0xFF, 0x99, 0x0D, 0x0C, 0x41, 0x05, 0x00,
+ 0x3E, 0xBA, 0x00, 0x84, 0xFF, 0x3F, 0x0F} }, /* 0x31 */
+ { {0x7F, 0x63, 0x83, 0x6C, 0x1C, 0x00, 0x06, 0x00,
+ 0x72, 0xBA, 0x27, 0x8B, 0xDF, 0x73, 0x80} }, /* 0x32 */
+ { {0x7F, 0x63, 0x83, 0x69, 0x13, 0x00, 0x06, 0x00,
+ 0x6F, 0xBA, 0x26, 0x89, 0xDF, 0x6F, 0x80} }, /* 0x33 */
+ { {0x7F, 0x63, 0x82, 0x6B, 0x13, 0x00, 0x06, 0x00,
+ 0x75, 0xBA, 0x29, 0x8C, 0xDF, 0x75, 0x80} }, /* 0x34 */
+ { {0xA3, 0x7F, 0x87, 0x86, 0x97, 0x00, 0x02, 0x00,
+ 0x24, 0xF1, 0xAF, 0x85, 0x3F, 0x25, 0xB0} }, /* 0x35 */
+ { {0x9F, 0x7F, 0x83, 0x85, 0x91, 0x00, 0x02, 0x00,
+ 0x1E, 0xF1, 0xAD, 0x81, 0x3F, 0x1F, 0xB0} }, /* 0x36 */
+ { {0xA7, 0x7F, 0x88, 0x89, 0x15, 0x00, 0x02, 0x00,
+ 0x26, 0xF1, 0xB1, 0x85, 0x3F, 0x27, 0xB0} }, /* 0x37 */
+ { {0xCE, 0x9F, 0x92, 0xA9, 0x17, 0x00, 0x07, 0x00,
+ 0x28, 0xC4, 0x7A, 0x8E, 0xCF, 0x29, 0xA1} }, /* 0x38 */
+ { {0xCE, 0x9F, 0x92, 0xA5, 0x17, 0x00, 0x07, 0x00,
+ 0x28, 0xD4, 0x7A, 0x8E, 0xCF, 0x29, 0xA1} }, /* 0x39 */
+ { {0xD3, 0x9F, 0x97, 0xAB, 0x1F, 0x00, 0x07, 0x00,
+ 0x2E, 0xD4, 0x7D, 0x81, 0xCF, 0x2F, 0xA1} }, /* 0x3a */
+ { {0xDC, 0x9F, 0x00, 0xAB, 0x19, 0x00, 0x07, 0x00,
+ 0xE6, 0xEF, 0xC0, 0xC3, 0xBF, 0xE7, 0x90} }, /* 0x3b */
+ { {0x6B, 0x59, 0x8F, 0x5E, 0x8C, 0x00, 0x05, 0x00,
+ 0x0B, 0x3E, 0xE9, 0x8B, 0xE7, 0x04, 0x00} }, /* 0x3c */
+ { {0x7B, 0x63, 0x9F, 0x6A, 0x93, 0x00, 0x05, 0x00,
+ 0x6F, 0xF0, 0x58, 0x8A, 0x57, 0x70, 0xA0} }, /* 0x3d */
+ { {0x86, 0x6A, 0x8a, 0x74, 0x06, 0x00, 0x02, 0x00,
+ 0x8c, 0x15, 0x4f, 0x83, 0xef, 0x8d, 0x30} }, /* 0x3e */
+ { {0x81, 0x6A, 0x85, 0x70, 0x00, 0x00, 0x02, 0x00,
+ 0x0f, 0x3e, 0xeb, 0x8e, 0xdf, 0x10, 0x00} }, /* 0x3f */
+ { {0xCE, 0x9F, 0x92, 0xA9, 0x17, 0x00, 0x07, 0x00,
+ 0x20, 0xF5, 0x03, 0x88, 0xFF, 0x21, 0x90} }, /* 0x40 */
+ { {0xE6, 0xAE, 0x8A, 0xBD, 0x90, 0x00, 0x03, 0x00,
+ 0x3D, 0x10, 0x1A, 0x8D, 0x19, 0x3E, 0x2F} }, /* 0x41 */
+ { {0xB9, 0x8F, 0x9D, 0x9B, 0x8A, 0x00, 0x06, 0x00,
+ 0x7D, 0xFF, 0x60, 0x83, 0x5F, 0x7E, 0x90} }, /* 0x42 */
+ { {0xC3, 0x8F, 0x87, 0x9B, 0x0B, 0x00, 0x07, 0x00,
+ 0x82, 0xFF, 0x60, 0x83, 0x5F, 0x83, 0x90} }, /* 0x43 */
+ { {0xAD, 0x7F, 0x91, 0x8E, 0x9C, 0x00, 0x02, 0x82,
+ 0x49, 0xF5, 0x00, 0x83, 0xFF, 0x4A, 0x90} }, /* 0x44 */
+ { {0xCD, 0x9F, 0x91, 0xA7, 0x19, 0x00, 0x07, 0x60,
+ 0xE6, 0xFF, 0xC0, 0x83, 0xBF, 0xE7, 0x90} }, /* 0x45 */
+ { {0xD3, 0x9F, 0x97, 0xAB, 0x1F, 0x00, 0x07, 0x60,
+ 0xF1, 0xFF, 0xC0, 0x83, 0xBF, 0xF2, 0x90} }, /* 0x46 */
+ { {0xD7, 0x9F, 0x9B, 0xAC, 0x1E, 0x00, 0x07, 0x00,
+ 0x03, 0xDE, 0xC0, 0x84, 0xBF, 0x04, 0x90} } /* 0x47 */
+};
+
+/*add for new UNIVGABIOS*/
+static const struct SiS_LCDData XGI_StLCD1024x768Data[] = {
+ {62, 25, 800, 546, 1344, 806},
+ {32, 15, 930, 546, 1344, 806},
+ {62, 25, 800, 546, 1344, 806}, /*chiawenfordot9->dot8*/
+ {104, 45, 945, 496, 1344, 806},
+ {62, 25, 800, 546, 1344, 806},
+ {31, 18, 1008, 624, 1344, 806},
+ {1, 1, 1344, 806, 1344, 806}
+};
+
+static const struct SiS_LCDData XGI_ExtLCD1024x768Data[] = {
+ {42, 25, 1536, 419, 1344, 806},
+ {48, 25, 1536, 369, 1344, 806},
+ {42, 25, 1536, 419, 1344, 806},
+ {48, 25, 1536, 369, 1344, 806},
+ {12, 5, 896, 500, 1344, 806},
+ {42, 25, 1024, 625, 1344, 806},
+ {1, 1, 1344, 806, 1344, 806},
+ {12, 5, 896, 500, 1344, 806},
+ {42, 25, 1024, 625, 1344, 806},
+ {1, 1, 1344, 806, 1344, 806},
+ {12, 5, 896, 500, 1344, 806},
+ {42, 25, 1024, 625, 1344, 806},
+ {1, 1, 1344, 806, 1344, 806}
+};
+
+static const struct SiS_LCDData XGI_CetLCD1024x768Data[] = {
+ {1, 1, 1344, 806, 1344, 806}, /* ; 00 (320x200,320x400,
+ 640x200,640x400) */
+ {1, 1, 1344, 806, 1344, 806}, /* 01 (320x350,640x350) */
+ {1, 1, 1344, 806, 1344, 806}, /* 02 (360x400,720x400) */
+ {1, 1, 1344, 806, 1344, 806}, /* 03 (720x350) */
+ {1, 1, 1344, 806, 1344, 806}, /* 04 (640x480x60Hz) */
+ {1, 1, 1344, 806, 1344, 806}, /* 05 (800x600x60Hz) */
+ {1, 1, 1344, 806, 1344, 806} /* 06 (1024x768x60Hz) */
+};
+
+static const struct SiS_LCDData XGI_StLCD1280x1024Data[] = {
+ {22, 5, 800, 510, 1650, 1088},
+ {22, 5, 800, 510, 1650, 1088},
+ {176, 45, 900, 510, 1650, 1088},
+ {176, 45, 900, 510, 1650, 1088},
+ {22, 5, 800, 510, 1650, 1088},
+ {13, 5, 1024, 675, 1560, 1152},
+ {16, 9, 1266, 804, 1688, 1072},
+ {1, 1, 1688, 1066, 1688, 1066}
+};
+
+static const struct SiS_LCDData XGI_ExtLCD1280x1024Data[] = {
+ {211, 60, 1024, 501, 1688, 1066},
+ {211, 60, 1024, 508, 1688, 1066},
+ {211, 60, 1024, 501, 1688, 1066},
+ {211, 60, 1024, 508, 1688, 1066},
+ {211, 60, 1024, 500, 1688, 1066},
+ {211, 75, 1024, 625, 1688, 1066},
+ {211, 120, 1280, 798, 1688, 1066},
+ {1, 1, 1688, 1066, 1688, 1066}
+};
+
+static const struct SiS_LCDData XGI_CetLCD1280x1024Data[] = {
+ {1, 1, 1688, 1066, 1688, 1066}, /* 00 (320x200,320x400,
+ 640x200,640x400) */
+ {1, 1, 1688, 1066, 1688, 1066}, /* 01 (320x350,640x350) */
+ {1, 1, 1688, 1066, 1688, 1066}, /* 02 (360x400,720x400) */
+ {1, 1, 1688, 1066, 1688, 1066}, /* 03 (720x350) */
+ {1, 1, 1688, 1066, 1688, 1066}, /* 04 (640x480x60Hz) */
+ {1, 1, 1688, 1066, 1688, 1066}, /* 05 (800x600x60Hz) */
+ {1, 1, 1688, 1066, 1688, 1066}, /* 06 (1024x768x60Hz) */
+ {1, 1, 1688, 1066, 1688, 1066}, /* 07 (1280x1024x60Hz) */
+ {1, 1, 1688, 1066, 1688, 1066} /* 08 (1400x1050x60Hz) */
+};
+
+static const struct SiS_LCDData xgifb_lcd_1400x1050[] = {
+ {211, 100, 2100, 408, 1688, 1066}, /* 00 (320x200,320x400,
+ 640x200,640x400) */
+ {211, 64, 1536, 358, 1688, 1066}, /* 01 (320x350,640x350) */
+ {211, 100, 2100, 408, 1688, 1066}, /* 02 (360x400,720x400) */
+ {211, 64, 1536, 358, 1688, 1066}, /* 03 (720x350) */
+ {211, 48, 840, 488, 1688, 1066}, /* 04 (640x480x60Hz) */
+ {211, 72, 1008, 609, 1688, 1066}, /* 05 (800x600x60Hz) */
+ {211, 128, 1400, 776, 1688, 1066}, /* 06 (1024x768x60Hz) */
+ {1, 1, 1688, 1066, 1688, 1066}, /* 07 (1280x1024x60Hz
+ w/o Scaling) */
+ {1, 1, 1688, 1066, 1688, 1066} /* 08 (1400x1050x60Hz) */
+};
+
+static const struct SiS_LCDData XGI_ExtLCD1600x1200Data[] = {
+ {4, 1, 1620, 420, 2160, 1250}, /* 00 (320x200,320x400,
+ 640x200,640x400)*/
+ {27, 7, 1920, 375, 2160, 1250}, /* 01 (320x350,640x350) */
+ {4, 1, 1620, 420, 2160, 1250}, /* 02 (360x400,720x400)*/
+ {27, 7, 1920, 375, 2160, 1250}, /* 03 (720x350) */
+ {27, 4, 800, 500, 2160, 1250}, /* 04 (640x480x60Hz) */
+ {4, 1, 1080, 625, 2160, 1250}, /* 05 (800x600x60Hz) */
+ {5, 2, 1350, 800, 2160, 1250}, /* 06 (1024x768x60Hz) */
+ {27, 16, 1500, 1064, 2160, 1250}, /* 07 (1280x1024x60Hz) */
+ {9, 7, 1920, 1106, 2160, 1250}, /* 08 (1400x1050x60Hz) */
+ {1, 1, 2160, 1250, 2160, 1250} /* 09 (1600x1200x60Hz) ;302lv */
+};
+
+static const struct SiS_LCDData XGI_StLCD1600x1200Data[] = {
+ {27, 4, 800, 500, 2160, 1250}, /* 00 (320x200,320x400,
+ 640x200,640x400) */
+ {27, 4, 800, 500, 2160, 1250}, /* 01 (320x350,640x350) */
+ {27, 4, 800, 500, 2160, 1250}, /* 02 (360x400,720x400) */
+ {27, 4, 800, 500, 2160, 1250}, /* 03 (720x350) */
+ {27, 4, 800, 500, 2160, 1250}, /* 04 (320x240,640x480) */
+ {4, 1, 1080, 625, 2160, 1250}, /* 05 (400x300,800x600) */
+ {5, 2, 1350, 800, 2160, 1250}, /* 06 (512x384,1024x768) */
+ {135, 88, 1600, 1100, 2160, 1250}, /* 07 (1280x1024) */
+ {1, 1, 1800, 1500, 2160, 1250}, /* 08 (1400x1050) */
+ {1, 1, 2160, 1250, 2160, 1250} /* 09 (1600x1200) */
+};
+
+#define XGI_CetLCD1400x1050Data XGI_CetLCD1280x1024Data
+
+static const struct SiS_LCDData XGI_NoScalingData[] = {
+ {1, 1, 800, 449, 800, 449},
+ {1, 1, 800, 449, 800, 449},
+ {1, 1, 900, 449, 900, 449},
+ {1, 1, 900, 449, 900, 449},
+ {1, 1, 800, 525, 800, 525},
+ {1, 1, 1056, 628, 1056, 628},
+ {1, 1, 1344, 806, 1344, 806},
+ {1, 1, 1688, 1066, 1688, 1066}
+};
+
+static const struct SiS_LCDData XGI_ExtLCD1024x768x75Data[] = {
+ {42, 25, 1536, 419, 1344, 806}, /* ; 00 (320x200,320x400,
+ 640x200,640x400) */
+ {48, 25, 1536, 369, 1344, 806}, /* ; 01 (320x350,640x350) */
+ {42, 25, 1536, 419, 1344, 806}, /* ; 02 (360x400,720x400) */
+ {48, 25, 1536, 369, 1344, 806}, /* ; 03 (720x350) */
+ {8, 5, 1312, 500, 1312, 800}, /* ; 04 (640x480x75Hz) */
+ {41, 25, 1024, 625, 1312, 800}, /* ; 05 (800x600x75Hz) */
+ {1, 1, 1312, 800, 1312, 800} /* ; 06 (1024x768x75Hz) */
+};
+
+static const struct SiS_LCDData XGI_CetLCD1024x768x75Data[] = {
+ {1, 1, 1312, 800, 1312, 800}, /* ; 00 (320x200,320x400,
+ 640x200,640x400) */
+ {1, 1, 1312, 800, 1312, 800}, /* ; 01 (320x350,640x350) */
+ {1, 1, 1312, 800, 1312, 800}, /* ; 02 (360x400,720x400) */
+ {1, 1, 1312, 800, 1312, 800}, /* ; 03 (720x350) */
+ {1, 1, 1312, 800, 1312, 800}, /* ; 04 (640x480x75Hz) */
+ {1, 1, 1312, 800, 1312, 800}, /* ; 05 (800x600x75Hz) */
+ {1, 1, 1312, 800, 1312, 800} /* ; 06 (1024x768x75Hz) */
+};
+
+static const struct SiS_LCDData xgifb_lcd_1280x1024x75[] = {
+ {211, 60, 1024, 501, 1688, 1066}, /* ; 00 (320x200,320x400,
+ 640x200,640x400) */
+ {211, 60, 1024, 508, 1688, 1066}, /* ; 01 (320x350,640x350) */
+ {211, 60, 1024, 501, 1688, 1066}, /* ; 02 (360x400,720x400) */
+ {211, 60, 1024, 508, 1688, 1066}, /* ; 03 (720x350) */
+ {211, 45, 768, 498, 1688, 1066}, /* ; 04 (640x480x75Hz) */
+ {211, 75, 1024, 625, 1688, 1066}, /* ; 05 (800x600x75Hz) */
+ {211, 120, 1280, 798, 1688, 1066}, /* ; 06 (1024x768x75Hz) */
+ {1, 1, 1688, 1066, 1688, 1066} /* ; 07 (1280x1024x75Hz) */
+};
+
+#define XGI_CetLCD1280x1024x75Data XGI_CetLCD1280x1024Data
+
+static const struct SiS_LCDData XGI_NoScalingDatax75[] = {
+ {1, 1, 800, 449, 800, 449}, /* ; 00 (320x200, 320x400,
+ 640x200, 640x400) */
+ {1, 1, 800, 449, 800, 449}, /* ; 01 (320x350, 640x350) */
+ {1, 1, 900, 449, 900, 449}, /* ; 02 (360x400, 720x400) */
+ {1, 1, 900, 449, 900, 449}, /* ; 03 (720x350) */
+ {1, 1, 840, 500, 840, 500}, /* ; 04 (640x480x75Hz) */
+ {1, 1, 1056, 625, 1056, 625}, /* ; 05 (800x600x75Hz) */
+ {1, 1, 1312, 800, 1312, 800}, /* ; 06 (1024x768x75Hz) */
+ {1, 1, 1688, 1066, 1688, 1066}, /* ; 07 (1280x1024x75Hz) */
+ {1, 1, 1688, 1066, 1688, 1066}, /* ; 08 (1400x1050x75Hz)*/
+ {1, 1, 2160, 1250, 2160, 1250}, /* ; 09 (1600x1200x75Hz) */
+ {1, 1, 1688, 806, 1688, 806} /* ; 0A (1280x768x75Hz) */
+};
+
+static const struct XGI_LCDDesStruct XGI_ExtLCDDes1024x768Data[] = {
+ {9, 1057, 0, 771}, /* ; 00 (320x200,320x400,640x200,640x400) */
+ {9, 1057, 0, 771}, /* ; 01 (320x350,640x350) */
+ {9, 1057, 0, 771}, /* ; 02 (360x400,720x400) */
+ {9, 1057, 0, 771}, /* ; 03 (720x350) */
+ {9, 1057, 0, 771}, /* ; 04 (640x480x60Hz) */
+ {9, 1057, 0, 771}, /* ; 05 (800x600x60Hz) */
+ {9, 1057, 805, 770} /* ; 06 (1024x768x60Hz) */
+};
+
+static const struct XGI_LCDDesStruct XGI_StLCDDes1024x768Data[] = {
+ {9, 1057, 737, 703}, /* ; 00 (320x200,320x400,640x200,640x400) */
+ {9, 1057, 686, 651}, /* ; 01 (320x350,640x350) */
+ {9, 1057, 737, 703}, /* ; 02 (360x400,720x400) */
+ {9, 1057, 686, 651}, /* ; 03 (720x350) */
+ {9, 1057, 776, 741}, /* ; 04 (640x480x60Hz) */
+ {9, 1057, 0, 771}, /* ; 05 (800x600x60Hz) */
+ {9, 1057, 805, 770} /* ; 06 (1024x768x60Hz) */
+};
+
+static const struct XGI_LCDDesStruct XGI_CetLCDDes1024x768Data[] = {
+ {1152, 856, 622, 587}, /* ; 00 (320x200,320x400,640x200,640x400) */
+ {1152, 856, 597, 562}, /* ; 01 (320x350,640x350) */
+ {1152, 856, 622, 587}, /* ; 02 (360x400,720x400) */
+ {1152, 856, 597, 562}, /* ; 03 (720x350) */
+ {1152, 856, 662, 627}, /* ; 04 (640x480x60Hz) */
+ {1232, 936, 722, 687}, /* ; 05 (800x600x60Hz) */
+ {0, 1048, 805, 770} /* ; 06 (1024x768x60Hz) */
+};
+
+static const struct XGI_LCDDesStruct XGI_ExtLCDDLDes1280x1024Data[] = {
+ {18, 1346, 981, 940}, /* 00 (320x200,320x400,640x200,640x400) */
+ {18, 1346, 926, 865}, /* 01 (320x350,640x350) */
+ {18, 1346, 981, 940}, /* 02 (360x400,720x400) */
+ {18, 1346, 926, 865}, /* 03 (720x350) */
+ {18, 1346, 0, 1025}, /* 04 (640x480x60Hz) */
+ {18, 1346, 0, 1025}, /* 05 (800x600x60Hz) */
+ {18, 1346, 1065, 1024}, /* 06 (1024x768x60Hz) */
+ {18, 1346, 1065, 1024} /* 07 (1280x1024x60Hz) */
+};
+
+static const struct XGI_LCDDesStruct XGI_StLCDDLDes1280x1024Data[] = {
+ {18, 1346, 970, 907}, /* 00 (320x200,320x400,640x200,640x400) */
+ {18, 1346, 917, 854}, /* 01 (320x350,640x350) */
+ {18, 1346, 970, 907}, /* 02 (360x400,720x400) */
+ {18, 1346, 917, 854}, /* 03 (720x350) */
+ {18, 1346, 0, 1025}, /* 04 (640x480x60Hz) */
+ {18, 1346, 0, 1025}, /* 05 (800x600x60Hz) */
+ {18, 1346, 1065, 1024}, /* 06 (1024x768x60Hz) */
+ {18, 1346, 1065, 1024} /* 07 (1280x1024x60Hz) */
+};
+
+static const struct XGI_LCDDesStruct XGI_CetLCDDLDes1280x1024Data[] = {
+ {1368, 1008, 752, 711}, /* 00 (320x200,320x400,640x200,640x400) */
+ {1368, 1008, 729, 688}, /* 01 (320x350,640x350) */
+ {1368, 1008, 752, 711}, /* 02 (360x400,720x400) */
+ {1368, 1008, 729, 688}, /* 03 (720x350) */
+ {1368, 1008, 794, 753}, /* 04 (640x480x60Hz) */
+ {1448, 1068, 854, 813}, /* 05 (800x600x60Hz) */
+ {1560, 1200, 938, 897}, /* 06 (1024x768x60Hz) */
+ {18, 1346, 1065, 1024} /* 07 (1280x1024x60Hz) */
+};
+
+static const struct XGI_LCDDesStruct XGI_ExtLCDDes1280x1024Data[] = {
+ {9, 1337, 981, 940}, /* ; 00 (320x200,320x400,640x200,640x400) */
+ {9, 1337, 926, 884}, /* ; 01 (320x350,640x350) alan, 2003/09/30 */
+ {9, 1337, 981, 940}, /* ; 02 (360x400,720x400) */
+ {9, 1337, 926, 884}, /* ; 03 (720x350) alan, 2003/09/30 */
+ {9, 1337, 0, 1025}, /* ; 04 (640x480x60Hz) */
+ {9, 1337, 0, 1025}, /* ; 05 (800x600x60Hz) */
+ {9, 1337, 1065, 1024}, /* ; 06 (1024x768x60Hz) */
+ {9, 1337, 1065, 1024} /* ; 07 (1280x1024x60Hz) */
+};
+
+static const struct XGI_LCDDesStruct XGI_StLCDDes1280x1024Data[] = {
+ {9, 1337, 970, 907}, /* ; 00 (320x200,320x400,640x200,640x400) */
+ {9, 1337, 917, 854}, /* ; 01 (320x350,640x350) */
+ {9, 1337, 970, 907}, /* ; 02 (360x400,720x400) */
+ {9, 1337, 917, 854}, /* ; 03 (720x350) */
+ {9, 1337, 0, 1025}, /* ; 04 (640x480x60Hz) */
+ {9, 1337, 0, 1025}, /* ; 05 (800x600x60Hz) */
+ {9, 1337, 1065, 1024}, /* ; 06 (1024x768x60Hz) */
+ {9, 1337, 1065, 1024} /* ; 07 (1280x1024x60Hz) */
+};
+
+static const struct XGI_LCDDesStruct XGI_CetLCDDes1280x1024Data[] = {
+ {1368, 1008, 752, 711}, /* 00 (320x200,320x400,640x200,640x400) */
+ {1368, 1008, 729, 688}, /* 01 (320x350,640x350) */
+ {1368, 1008, 752, 711}, /* 02 (360x400,720x400) */
+ {1368, 1008, 729, 688}, /* 03 (720x350) */
+ {1368, 1008, 794, 753}, /* 04 (640x480x60Hz) */
+ {1448, 1068, 854, 813}, /* 05 (800x600x60Hz) */
+ {1560, 1200, 938, 897}, /* 06 (1024x768x60Hz) */
+ {9, 1337, 1065, 1024} /* 07 (1280x1024x60Hz) */
+};
+
+static const struct XGI_LCDDesStruct xgifb_lcddldes_1400x1050[] = {
+ {18, 1464, 0, 1051}, /* 00 (320x200,320x400,640x200,640x400) */
+ {18, 1464, 0, 1051}, /* 01 (320x350,640x350) */
+ {18, 1464, 0, 1051}, /* 02 (360x400,720x400) */
+ {18, 1464, 0, 1051}, /* 03 (720x350) */
+ {18, 1464, 0, 1051}, /* 04 (640x480x60Hz) */
+ {18, 1464, 0, 1051}, /* 05 (800x600x60Hz) */
+ {18, 1464, 0, 1051}, /* 06 (1024x768x60Hz) */
+ {1646, 1406, 1053, 1038}, /* 07 (1280x1024x60Hz) */
+ {18, 1464, 0, 1051} /* 08 (1400x1050x60Hz) */
+};
+
+static const struct XGI_LCDDesStruct xgifb_lcddes_1400x1050[] = {
+ {9, 1455, 0, 1051}, /* 00 (320x200,320x400,640x200,640x400) */
+ {9, 1455, 0, 1051}, /* 01 (320x350,640x350) */
+ {9, 1455, 0, 1051}, /* 02 (360x400,720x400) */
+ {9, 1455, 0, 1051}, /* 03 (720x350) */
+ {9, 1455, 0, 1051}, /* 04 (640x480x60Hz) */
+ {9, 1455, 0, 1051}, /* 05 (800x600x60Hz) */
+ {9, 1455, 0, 1051}, /* 06 (1024x768x60Hz) */
+ {1637, 1397, 1053, 1038}, /* 07 (1280x1024x60Hz) */
+ {9, 1455, 0, 1051} /* 08 (1400x1050x60Hz) */
+};
+
+static const struct XGI_LCDDesStruct XGI_CetLCDDes1400x1050Data[] = {
+ {1308, 1068, 781, 766}, /* 00 (320x200,320x400,640x200,640x400) */
+ {1308, 1068, 781, 766}, /* 01 (320x350,640x350) */
+ {1308, 1068, 781, 766}, /* 02 (360x400,720x400) */
+ {1308, 1068, 781, 766}, /* 03 (720x350) */
+ {1308, 1068, 781, 766}, /* 04 (640x480x60Hz) */
+ {1388, 1148, 841, 826}, /* 05 (800x600x60Hz) */
+ {1490, 1250, 925, 910}, /* 06 (1024x768x60Hz) */
+ {1646, 1406, 1053, 1038}, /* 07 (1280x1024x60Hz) */
+ {18, 1464, 0, 1051} /* 08 (1400x1050x60Hz) */
+};
+
+static const struct XGI_LCDDesStruct XGI_CetLCDDes1400x1050Data2[] = {
+ {0, 1448, 0, 1051}, /* 00 (320x200,320x400,640x200,640x400) */
+ {0, 1448, 0, 1051}, /* 01 (320x350,640x350) */
+ {0, 1448, 0, 1051}, /* 02 (360x400,720x400) */
+ {0, 1448, 0, 1051}, /* 03 (720x350) */
+ {0, 1448, 0, 1051} /* 04 (640x480x60Hz) */
+};
+
+static const struct XGI_LCDDesStruct XGI_ExtLCDDLDes1600x1200Data[] = {
+ {18, 1682, 0, 1201}, /* 00 (320x200,320x400,640x200,640x400) */
+ {18, 1682, 0, 1201}, /* 01 (320x350,640x350) */
+ {18, 1682, 0, 1201}, /* 02 (360x400,720x400) */
+ {18, 1682, 0, 1201}, /* 03 (720x350) */
+ {18, 1682, 0, 1201}, /* 04 (640x480x60Hz) */
+ {18, 1682, 0, 1201}, /* 05 (800x600x60Hz) */
+ {18, 1682, 0, 1201}, /* 06 (1024x768x60Hz) */
+ {18, 1682, 0, 1201}, /* 07 (1280x1024x60Hz) */
+ {18, 1682, 0, 1201}, /* 08 (1400x1050x60Hz) */
+ {18, 1682, 0, 1201} /* 09 (1600x1200x60Hz) */
+};
+
+static const struct XGI_LCDDesStruct XGI_StLCDDLDes1600x1200Data[] = {
+ {18, 1682, 1150, 1101}, /* 00 (320x200,320x400,640x200,640x400) */
+ {18, 1682, 1083, 1034}, /* 01 (320x350,640x350) */
+ {18, 1682, 1150, 1101}, /* 02 (360x400,720x400) */
+ {18, 1682, 1083, 1034}, /* 03 (720x350) */
+ {18, 1682, 0, 1201}, /* 04 (640x480x60Hz) */
+ {18, 1682, 0, 1201}, /* 05 (800x600x60Hz) */
+ {18, 1682, 0, 1201}, /* 06 (1024x768x60Hz) */
+ {18, 1682, 1232, 1183}, /* 07 (1280x1024x60Hz) */
+ {18, 1682, 0, 1201}, /* 08 (1400x1050x60Hz) */
+ {18, 1682, 0, 1201} /* 09 (1600x1200x60Hz) */
+};
+
+static const struct XGI_LCDDesStruct XGI_ExtLCDDes1600x1200Data[] = {
+ {9, 1673, 0, 1201}, /* 00 (320x200,320x400,640x200,640x400) */
+ {9, 1673, 0, 1201}, /* 01 (320x350,640x350) */
+ {9, 1673, 0, 1201}, /* 02 (360x400,720x400) */
+ {9, 1673, 0, 1201}, /* 03 (720x350) */
+ {9, 1673, 0, 1201}, /* 04 (640x480x60Hz) */
+ {9, 1673, 0, 1201}, /* 05 (800x600x60Hz) */
+ {9, 1673, 0, 1201}, /* 06 (1024x768x60Hz) */
+ {9, 1673, 0, 1201}, /* 07 (1280x1024x60Hz) */
+ {9, 1673, 0, 1201}, /* 08 (1400x1050x60Hz) */
+ {9, 1673, 0, 1201} /* 09 (1600x1200x60Hz) */
+};
+
+static const struct XGI_LCDDesStruct XGI_StLCDDes1600x1200Data[] = {
+ {9, 1673, 1150, 1101}, /* 00 (320x200,320x400,640x200,640x400) */
+ {9, 1673, 1083, 1034}, /* 01 (320x350,640x350) */
+ {9, 1673, 1150, 1101}, /* 02 (360x400,720x400) */
+ {9, 1673, 1083, 1034}, /* 03 (720x350) */
+ {9, 1673, 0, 1201}, /* 04 (640x480x60Hz) */
+ {9, 1673, 0, 1201}, /* 05 (800x600x60Hz) */
+ {9, 1673, 0, 1201}, /* 06 (1024x768x60Hz) */
+ {9, 1673, 1232, 1183}, /* 07 (1280x1024x60Hz) */
+ {9, 1673, 0, 1201}, /* 08 (1400x1050x60Hz) */
+ {9, 1673, 0, 1201} /* 09 (1600x1200x60Hz) */
+};
+
+static const struct XGI330_LCDDataDesStruct2 XGI_NoScalingDesData[] = {
+ {9, 657, 448, 405, 96, 2}, /* 00 (320x200,320x400,
+ 640x200,640x400) */
+ {9, 657, 448, 355, 96, 2}, /* 01 (320x350,640x350) */
+ {9, 657, 448, 405, 96, 2}, /* 02 (360x400,720x400) */
+ {9, 657, 448, 355, 96, 2}, /* 03 (720x350) */
+ {9, 657, 1, 483, 96, 2}, /* 04 (640x480x60Hz) */
+ {9, 849, 627, 600, 128, 4}, /* 05 (800x600x60Hz) */
+ {9, 1057, 805, 770, 0136, 6}, /* 06 (1024x768x60Hz) */
+ {9, 1337, 0, 1025, 112, 3}, /* 07 (1280x1024x60Hz) */
+ {9, 1457, 0, 1051, 112, 3}, /* 08 (1400x1050x60Hz)*/
+ {9, 1673, 0, 1201, 192, 3}, /* 09 (1600x1200x60Hz) */
+ {9, 1337, 0, 771, 112, 6} /* 0A (1280x768x60Hz) */
+};
+
+/* ;;1024x768x75Hz */
+static const struct XGI_LCDDesStruct xgifb_lcddes_1024x768x75[] = {
+ {9, 1049, 0, 769}, /* ; 00 (320x200,320x400,640x200,640x400) */
+ {9, 1049, 0, 769}, /* ; 01 (320x350,640x350) */
+ {9, 1049, 0, 769}, /* ; 02 (360x400,720x400) */
+ {9, 1049, 0, 769}, /* ; 03 (720x350) */
+ {9, 1049, 0, 769}, /* ; 04 (640x480x75Hz) */
+ {9, 1049, 0, 769}, /* ; 05 (800x600x75Hz) */
+ {9, 1049, 0, 769} /* ; 06 (1024x768x75Hz) */
+};
+
+/* ;;1024x768x75Hz */
+static const struct XGI_LCDDesStruct XGI_CetLCDDes1024x768x75Data[] = {
+ {1152, 856, 622, 587}, /* ; 00 (320x200,320x400,640x200,640x400) */
+ {1152, 856, 597, 562}, /* ; 01 (320x350,640x350) */
+ {1192, 896, 622, 587}, /* ; 02 (360x400,720x400) */
+ {1192, 896, 597, 562}, /* ; 03 (720x350) */
+ {1129, 857, 656, 625}, /* ; 04 (640x480x75Hz) */
+ {1209, 937, 716, 685}, /* ; 05 (800x600x75Hz) */
+ {9, 1049, 0, 769} /* ; 06 (1024x768x75Hz) */
+};
+
+/* ;;1280x1024x75Hz */
+static const struct XGI_LCDDesStruct xgifb_lcddldes_1280x1024x75[] = {
+ {18, 1314, 0, 1025}, /* ; 00 (320x200,320x400,640x200,640x400) */
+ {18, 1314, 0, 1025}, /* ; 01 (320x350,640x350) */
+ {18, 1314, 0, 1025}, /* ; 02 (360x400,720x400) */
+ {18, 1314, 0, 1025}, /* ; 03 (720x350) */
+ {18, 1314, 0, 1025}, /* ; 04 (640x480x60Hz) */
+ {18, 1314, 0, 1025}, /* ; 05 (800x600x60Hz) */
+ {18, 1314, 0, 1025}, /* ; 06 (1024x768x60Hz) */
+ {18, 1314, 0, 1025} /* ; 07 (1280x1024x60Hz) */
+};
+
+/* 1280x1024x75Hz */
+static const struct XGI_LCDDesStruct XGI_CetLCDDLDes1280x1024x75Data[] = {
+ {1368, 1008, 752, 711}, /* ; 00 (320x200,320x400,640x200,640x400) */
+ {1368, 1008, 729, 688}, /* ; 01 (320x350,640x350) */
+ {1408, 1048, 752, 711}, /* ; 02 (360x400,720x400) */
+ {1408, 1048, 729, 688}, /* ; 03 (720x350) */
+ {1377, 985, 794, 753}, /* ; 04 (640x480x75Hz) */
+ {1457, 1065, 854, 813}, /* ; 05 (800x600x75Hz) */
+ {1569, 1177, 938, 897}, /* ; 06 (1024x768x75Hz) */
+ {18, 1314, 0, 1025} /* ; 07 (1280x1024x75Hz) */
+};
+
+/* ;;1280x1024x75Hz */
+static const struct XGI_LCDDesStruct xgifb_lcddes_1280x1024x75[] = {
+ {9, 1305, 0, 1025}, /* ; 00 (320x200,320x400,640x200,640x400) */
+ {9, 1305, 0, 1025}, /* ; 01 (320x350,640x350) */
+ {9, 1305, 0, 1025}, /* ; 02 (360x400,720x400) */
+ {9, 1305, 0, 1025}, /* ; 03 (720x350) */
+ {9, 1305, 0, 1025}, /* ; 04 (640x480x60Hz) */
+ {9, 1305, 0, 1025}, /* ; 05 (800x600x60Hz) */
+ {9, 1305, 0, 1025}, /* ; 06 (1024x768x60Hz) */
+ {9, 1305, 0, 1025} /* ; 07 (1280x1024x60Hz) */
+};
+
+/* 1280x1024x75Hz */
+static const struct XGI_LCDDesStruct XGI_CetLCDDes1280x1024x75Data[] = {
+ {1368, 1008, 752, 711}, /* ; 00 (320x200,320x400,640x200,640x400) */
+ {1368, 1008, 729, 688}, /* ; 01 (320x350,640x350) */
+ {1408, 1048, 752, 711}, /* ; 02 (360x400,720x400) */
+ {1408, 1048, 729, 688}, /* ; 03 (720x350) */
+ {1377, 985, 794, 753}, /* ; 04 (640x480x75Hz) */
+ {1457, 1065, 854, 813}, /* ; 05 (800x600x75Hz) */
+ {1569, 1177, 938, 897}, /* ; 06 (1024x768x75Hz) */
+ {9, 1305, 0, 1025} /* ; 07 (1280x1024x75Hz) */
+};
+
+/* Scaling LCD 75Hz */
+static const struct XGI330_LCDDataDesStruct2 XGI_NoScalingDesDatax75[] = {
+ {9, 657, 448, 405, 96, 2}, /* ; 00 (320x200,320x400,
+ 640x200,640x400) */
+ {9, 657, 448, 355, 96, 2}, /* ; 01 (320x350,640x350) */
+ {9, 738, 448, 405, 108, 2}, /* ; 02 (360x400,720x400) */
+ {9, 738, 448, 355, 108, 2}, /* ; 03 (720x350) */
+ {9, 665, 0, 481, 64, 3}, /* ; 04 (640x480x75Hz) */
+ {9, 825, 0, 601, 80, 3}, /* ; 05 (800x600x75Hz) */
+ {9, 1049, 0, 769, 96, 3}, /* ; 06 (1024x768x75Hz) */
+ {9, 1305, 0, 1025, 144, 3}, /* ; 07 (1280x1024x75Hz) */
+ {9, 1457, 0, 1051, 112, 3}, /* ; 08 (1400x1050x60Hz)*/
+ {9, 1673, 0, 1201, 192, 3}, /* ; 09 (1600x1200x75Hz) */
+ {9, 1337, 0, 771, 112, 6} /* ; 0A (1280x768x60Hz) */
+};
+
+static const struct SiS_TVData XGI_StPALData[] = {
+ {1, 1, 864, 525, 1270, 400, 100, 0, 760},
+ {1, 1, 864, 525, 1270, 350, 100, 0, 760},
+ {1, 1, 864, 525, 1270, 400, 0, 0, 720},
+ {1, 1, 864, 525, 1270, 350, 0, 0, 720},
+ {1, 1, 864, 525, 1270, 480, 50, 0, 760},
+ {1, 1, 864, 525, 1270, 600, 50, 0, 0}
+};
+
+static const struct SiS_TVData XGI_ExtPALData[] = {
+ {2, 1, 1080, 463, 1270, 500, 50, 0, 50},
+ {15, 7, 1152, 413, 1270, 500, 50, 0, 50},
+ {2, 1, 1080, 463, 1270, 500, 50, 0, 50},
+ {15, 7, 1152, 413, 1270, 500, 50, 0, 50},
+ {2, 1, 900, 543, 1270, 500, 0, 0, 50},
+ {4, 3, 1080, 663, 1270, 500, 438, 0, 438},
+ {1, 1, 1125, 831, 1270, 500, 686, 0, 686}, /*301b*/
+ {3, 2, 1080, 619, 1270, 540, 438, 0, 438}
+};
+
+static const struct SiS_TVData XGI_StNTSCData[] = {
+ {1, 1, 858, 525, 1270, 400, 50, 0, 760},
+ {1, 1, 858, 525, 1270, 350, 50, 0, 640},
+ {1, 1, 858, 525, 1270, 400, 0, 0, 720},
+ {1, 1, 858, 525, 1270, 350, 0, 0, 720},
+ {1, 1, 858, 525, 1270, 480, 0, 0, 760}
+};
+
+static const struct SiS_TVData XGI_ExtNTSCData[] = {
+ {9, 5, 1001, 453, 1270, 420, 171, 0, 171},
+ {12, 5, 858, 403, 1270, 420, 171, 0, 171},
+ {9, 5, 1001, 453, 1270, 420, 171, 0, 171},
+ {12, 5, 858, 403, 1270, 420, 171, 0, 171},
+ {143, 80, 836, 523, 1270, 420, 224, 0, 0},
+ {143, 120, 1008, 643, 1270, 420, 0, 1, 0},
+ {1, 1, 1120, 821, 1516, 420, 0, 1, 0}, /*301b*/
+ {2, 1, 858, 503, 1584, 480, 0, 1, 0},
+ {3, 2, 1001, 533, 1270, 420, 0, 0, 0}
+};
+
+static const struct SiS_TVData XGI_St1HiTVData[] = {
+ {1, 1, 892, 563, 690, 800, 0, 0, 0}, /* 00 (320x200,320x400,
+ 640x200,640x400) */
+ {1, 1, 892, 563, 690, 700, 0, 0, 0}, /* 01 (320x350,640x350) */
+ {1, 1, 1000, 563, 785, 800, 0, 0, 0}, /* 02 (360x400,720x400) */
+ {1, 1, 1000, 563, 785, 700, 0, 0, 0}, /* 03 (720x350) */
+ {1, 1, 892, 563, 690, 960, 0, 0, 0}, /* 04 (320x240,640x480) */
+ {8, 5, 1050, 683, 1648, 960, 0x150, 1, 0} /* 05 (400x300,800x600) */
+};
+
+static const struct SiS_TVData XGI_St2HiTVData[] = {
+ {3, 1, 840, 483, 1648, 960, 0x032, 0, 0}, /* 00 (320x200,320x400,
+ 640x200,640x400) */
+ {1, 1, 892, 563, 690, 700, 0, 0, 0}, /* 01 (320x350,640x350) */
+ {3, 1, 840, 483, 1648, 960, 0x032, 0, 0}, /* 02 (360x400,720x400) */
+ {1, 1, 1000, 563, 785, 700, 0, 0, 0}, /* 03 (720x350) */
+ {5, 2, 840, 563, 1648, 960, 0x08D, 1, 0}, /* 04 (320x240,640x480) */
+ {8, 5, 1050, 683, 1648, 960, 0x17C, 1, 0} /* 05 (400x300,800x600) */
+};
+
+static const struct SiS_TVData XGI_ExtHiTVData[] = {
+ {6, 1, 840, 563, 1632, 960, 0, 0, 0}, /* 00 (320x200,320x400,
+ 640x200,640x400) */
+ {3, 1, 960, 563, 1632, 960, 0, 0, 0}, /* 01 (320x350,640x350) */
+ {3, 1, 840, 483, 1632, 960, 0, 0, 0}, /* 02 (360x400,720x400) */
+ {3, 1, 960, 563, 1632, 960, 0, 0, 0}, /* 03 (720x350) */
+ {5, 1, 840, 563, 1648, 960, 0x166, 1, 0}, /* 04 (320x240,640x480) */
+ {16, 5, 1050, 683, 1648, 960, 0x143, 1, 0}, /* 05 (400x300,800x600) */
+ {25, 12, 1260, 851, 1648, 960, 0x032, 0, 0}, /* 06 (512x384,1024x768)*/
+ {5, 4, 1575, 1124, 1648, 960, 0x128, 0, 0}, /* 07 (1280x1024) */
+ {4, 1, 1050, 563, 1548, 960, 0x143, 1, 0}, /* 08 (800x480) */
+ {5, 2, 1400, 659, 1648, 960, 0x032, 0, 0}, /* 09 (1024x576) */
+ {8, 5, 1750, 803, 1648, 960, 0x128, 0, 0} /* 0A (1280x720) */
+};
+
+static const struct SiS_TVData XGI_ExtYPbPr525iData[] = {
+ { 9, 5, 1001, 453, 1270, 420, 171, 0, 171},
+ { 12, 5, 858, 403, 1270, 420, 171, 0, 171},
+ { 9, 5, 1001, 453, 1270, 420, 171, 0, 171},
+ { 12, 5, 858, 403, 1270, 420, 171, 0, 171},
+ {143, 80, 836, 523, 1250, 420, 224, 0, 0},
+ {143, 120, 1008, 643, 1250, 420, 0, 1, 0},
+ { 1, 1, 1120, 821, 1516, 420, 0, 1, 0}, /*301b*/
+ { 2, 1, 858, 503, 1584, 480, 0, 1, 0},
+ { 3, 2, 1001, 533, 1250, 420, 0, 0, 0}
+};
+
+static const struct SiS_TVData XGI_StYPbPr525iData[] = {
+ {1, 1, 858, 525, 1270, 400, 50, 0, 760},
+ {1, 1, 858, 525, 1270, 350, 50, 0, 640},
+ {1, 1, 858, 525, 1270, 400, 0, 0, 720},
+ {1, 1, 858, 525, 1270, 350, 0, 0, 720},
+ {1, 1, 858, 525, 1270, 480, 0, 0, 760},
+};
+
+static const struct SiS_TVData XGI_ExtYPbPr525pData[] = {
+ { 9, 5, 1001, 453, 1270, 420, 171, 0, 171},
+ { 12, 5, 858, 403, 1270, 420, 171, 0, 171},
+ { 9, 5, 1001, 453, 1270, 420, 171, 0, 171},
+ { 12, 5, 858, 403, 1270, 420, 171, 0, 171},
+ {143, 80, 836, 523, 1270, 420, 224, 0, 0},
+ {143, 120, 1008, 643, 1270, 420, 0, 1, 0},
+ { 1, 1, 1120, 821, 1516, 420, 0, 1, 0}, /*301b*/
+ { 2, 1, 858, 503, 1584, 480, 0, 1, 0},
+ { 3, 2, 1001, 533, 1270, 420, 0, 0, 0}
+};
+
+static const struct SiS_TVData XGI_StYPbPr525pData[] = {
+ {1, 1, 1716, 525, 1270, 400, 50, 0, 760},
+ {1, 1, 1716, 525, 1270, 350, 50, 0, 640},
+ {1, 1, 1716, 525, 1270, 400, 0, 0, 720},
+ {1, 1, 1716, 525, 1270, 350, 0, 0, 720},
+ {1, 1, 1716, 525, 1270, 480, 0, 0, 760},
+};
+
+static const struct SiS_TVData XGI_ExtYPbPr750pData[] = {
+ { 3, 1, 935, 470, 1130, 680, 50, 0, 0}, /* 00 (320x200,320x400,
+ 640x200,640x400) */
+ {24, 7, 935, 420, 1130, 680, 50, 0, 0}, /* 01 (320x350,640x350) */
+ { 3, 1, 935, 470, 1130, 680, 50, 0, 0}, /* 02 (360x400,720x400) */
+ {24, 7, 935, 420, 1130, 680, 50, 0, 0}, /* 03 (720x350) */
+ { 2, 1, 1100, 590, 1130, 640, 50, 0, 0}, /* 04 (320x240,640x480) */
+ { 3, 2, 1210, 690, 1130, 660, 50, 0, 0}, /* 05 (400x300,800x600) */
+ { 1, 1, 1375, 878, 1130, 640, 638, 0, 0}, /* 06 (1024x768) */
+ { 2, 1, 858, 503, 1130, 480, 0, 1, 0}, /* 07 (720x480) */
+ { 5, 4, 1815, 570, 1130, 660, 50, 0, 0},
+ { 5, 3, 1100, 686, 1130, 640, 50, 1, 0},
+ {10, 9, 1320, 830, 1130, 640, 50, 0, 0}
+};
+
+static const struct SiS_TVData XGI_StYPbPr750pData[] = {
+ {1, 1, 1650, 750, 1280, 400, 50, 0, 760},
+ {1, 1, 1650, 750, 1280, 350, 50, 0, 640},
+ {1, 1, 1650, 750, 1280, 400, 0, 0, 720},
+ {1, 1, 1650, 750, 1280, 350, 0, 0, 720},
+ {1, 1, 1650, 750, 1280, 480, 0, 0, 760},
+};
+
+static const unsigned char XGI330_NTSCTiming[] = {
+ 0x17, 0x1d, 0x03, 0x09, 0x05, 0x06, 0x0c, 0x0c,
+ 0x94, 0x49, 0x01, 0x0a, 0x06, 0x0d, 0x04, 0x0a,
+ 0x06, 0x14, 0x0d, 0x04, 0x0a, 0x00, 0x85, 0x1b,
+ 0x0c, 0x50, 0x00, 0x97, 0x00, 0xda, 0x4a, 0x17,
+ 0x7d, 0x05, 0x4b, 0x00, 0x00, 0xe2, 0x00, 0x02,
+ 0x03, 0x0a, 0x65, 0x9d, 0x08, 0x92, 0x8f, 0x40,
+ 0x60, 0x80, 0x14, 0x90, 0x8c, 0x60, 0x14, 0x50,
+ 0x00, 0x40, 0x44, 0x00, 0xdb, 0x02, 0x3b, 0x00
+};
+
+static const unsigned char XGI330_PALTiming[] = {
+ 0x21, 0x5A, 0x35, 0x6e, 0x04, 0x38, 0x3d, 0x70,
+ 0x94, 0x49, 0x01, 0x12, 0x06, 0x3e, 0x35, 0x6d,
+ 0x06, 0x14, 0x3e, 0x35, 0x6d, 0x00, 0x45, 0x2b,
+ 0x70, 0x50, 0x00, 0x9b, 0x00, 0xd9, 0x5d, 0x17,
+ 0x7d, 0x05, 0x45, 0x00, 0x00, 0xe8, 0x00, 0x02,
+ 0x0d, 0x00, 0x68, 0xb0, 0x0b, 0x92, 0x8f, 0x40,
+ 0x60, 0x80, 0x14, 0x90, 0x8c, 0x60, 0x14, 0x63,
+ 0x00, 0x40, 0x3e, 0x00, 0xe1, 0x02, 0x28, 0x00
+};
+
+static const unsigned char XGI330_HiTVExtTiming[] = {
+ 0x2D, 0x60, 0x2C, 0x5F, 0x08, 0x31, 0x3A, 0x64,
+ 0x28, 0x02, 0x01, 0x3D, 0x06, 0x3E, 0x35, 0x6D,
+ 0x06, 0x14, 0x3E, 0x35, 0x6D, 0x00, 0xC5, 0x3F,
+ 0x64, 0x90, 0x33, 0x8C, 0x18, 0x36, 0x3E, 0x13,
+ 0x2A, 0xDE, 0x2A, 0x44, 0x40, 0x2A, 0x44, 0x40,
+ 0x8E, 0x8E, 0x82, 0x07, 0x0B,
+ 0x92, 0x0F, 0x40, 0x60, 0x80, 0x14, 0x90, 0x8C,
+ 0x60, 0x14, 0x3D, 0x63, 0x4F,
+ 0x27, 0x00, 0xfc, 0xff, 0x6a, 0x00
+};
+
+static const unsigned char XGI330_HiTVSt1Timing[] = {
+ 0x32, 0x65, 0x2C, 0x5F, 0x08, 0x31, 0x3A, 0x65,
+ 0x28, 0x02, 0x01, 0x3D, 0x06, 0x3E, 0x35, 0x6D,
+ 0x06, 0x14, 0x3E, 0x35, 0x6D, 0x00, 0xC5, 0x3F,
+ 0x65, 0x90, 0x7B, 0xA8, 0x03, 0xF0, 0x87, 0x03,
+ 0x11, 0x15, 0x11, 0xCF, 0x10, 0x11, 0xCF, 0x10,
+ 0x35, 0x35, 0x3B, 0x69, 0x1D,
+ 0x92, 0x0F, 0x40, 0x60, 0x80, 0x14, 0x90, 0x8C,
+ 0x60, 0x04, 0x86, 0xAF, 0x5D,
+ 0x0E, 0x00, 0xfc, 0xff, 0x2d, 0x00
+};
+
+static const unsigned char XGI330_HiTVSt2Timing[] = {
+ 0x32, 0x65, 0x2C, 0x5F, 0x08, 0x31, 0x3A, 0x64,
+ 0x28, 0x02, 0x01, 0x3D, 0x06, 0x3E, 0x35, 0x6D,
+ 0x06, 0x14, 0x3E, 0x35, 0x6D, 0x00, 0xC5, 0x3F,
+ 0x64, 0x90, 0x33, 0x8C, 0x18, 0x36, 0x3E, 0x13,
+ 0x2A, 0xDE, 0x2A, 0x44, 0x40, 0x2A, 0x44, 0x40,
+ 0x8E, 0x8E, 0x82, 0x07, 0x0B,
+ 0x92, 0x0F, 0x40, 0x60, 0x80, 0x14, 0x90, 0x8C,
+ 0x60, 0x14, 0x3D, 0x63, 0x4F,
+ 0x27, 0x00, 0xFC, 0xff, 0x6a, 0x00
+};
+
+static const unsigned char XGI330_HiTVTextTiming[] = {
+ 0x32, 0x65, 0x2C, 0x5F, 0x08, 0x31, 0x3A, 0x65,
+ 0x28, 0x02, 0x01, 0x3D, 0x06, 0x3E, 0x35, 0x6D,
+ 0x06, 0x14, 0x3E, 0x35, 0x6D, 0x00, 0xC5, 0x3F,
+ 0x65, 0x90, 0xE7, 0xBC, 0x03, 0x0C, 0x97, 0x03,
+ 0x14, 0x78, 0x14, 0x08, 0x20, 0x14, 0x08, 0x20,
+ 0xC8, 0xC8, 0x3B, 0xD2, 0x26,
+ 0x92, 0x0F, 0x40, 0x60, 0x80, 0x14, 0x90, 0x8C,
+ 0x60, 0x04, 0x96, 0x72, 0x5C,
+ 0x11, 0x00, 0xFC, 0xFF, 0x32, 0x00
+};
+
+static const unsigned char XGI330_YPbPr750pTiming[] = {
+ 0x30, 0x1d, 0xe8, 0x09, 0x09, 0xed, 0x0c, 0x0c,
+ 0x98, 0x0a, 0x01, 0x0c, 0x06, 0x0d, 0x04, 0x0a,
+ 0x06, 0x14, 0x0d, 0x04, 0x0a, 0x00, 0x85, 0x3f,
+ 0xed, 0x50, 0x70, 0x9f, 0x16, 0x59, 0x60, 0x13,
+ 0x27, 0x0b, 0x27, 0xfc, 0x30, 0x27, 0x1c, 0xb0,
+ 0x4b, 0x4b, 0x6f, 0x2f, 0x63,
+ 0x92, 0x0F, 0x40, 0x60, 0x80, 0x14, 0x90, 0x8C,
+ 0x60, 0x14, 0x73, 0x00, 0x40,
+ 0x11, 0x00, 0xfc, 0xff, 0x32, 0x00
+};
+
+static const unsigned char XGI330_YPbPr525pTiming[] = {
+ 0x3E, 0x11, 0x06, 0x09, 0x0b, 0x0c, 0x0c, 0x0c,
+ 0x98, 0x0a, 0x01, 0x0d, 0x06, 0x0d, 0x04, 0x0a,
+ 0x06, 0x14, 0x0d, 0x04, 0x0a, 0x00, 0x85, 0x3f,
+ 0x0c, 0x50, 0xb2, 0x9f, 0x16, 0x59, 0x4f, 0x13,
+ 0xad, 0x11, 0xad, 0x1d, 0x40, 0x8a, 0x3d, 0xb8,
+ 0x51, 0x5e, 0x60, 0x49, 0x7d,
+ 0x92, 0x0F, 0x40, 0x60, 0x80, 0x14, 0x90, 0x8C,
+ 0x60, 0x14, 0x4B, 0x43, 0x41,
+ 0x11, 0x00, 0xFC, 0xFF, 0x32, 0x00
+};
+
+static const unsigned char XGI330_YPbPr525iTiming[] = {
+ 0x1B, 0x21, 0x03, 0x09, 0x05, 0x06, 0x0C, 0x0C,
+ 0x94, 0x49, 0x01, 0x0A, 0x06, 0x0D, 0x04, 0x0A,
+ 0x06, 0x14, 0x0D, 0x04, 0x0A, 0x00, 0x85, 0x1B,
+ 0x0C, 0x50, 0x00, 0x97, 0x00, 0xDA, 0x4A, 0x17,
+ 0x7D, 0x05, 0x4B, 0x00, 0x00, 0xE2, 0x00, 0x02,
+ 0x03, 0x0A, 0x65, 0x9D, 0x08,
+ 0x92, 0x8F, 0x40, 0x60, 0x80, 0x14, 0x90, 0x8C,
+ 0x60, 0x14, 0x4B, 0x00, 0x40,
+ 0x44, 0x00, 0xDB, 0x02, 0x3B, 0x00
+};
+
+static const unsigned char XGI330_HiTVGroup3Data[] = {
+ 0x00, 0x1A, 0x22, 0x63, 0x62, 0x22, 0x08, 0x5F,
+ 0x05, 0x21, 0xB2, 0xB2, 0x55, 0x77, 0x2A, 0xA6,
+ 0x25, 0x2F, 0x47, 0xFA, 0xC8, 0xFF, 0x8E, 0x20,
+ 0x8C, 0x6E, 0x60, 0x2E, 0x58, 0x48, 0x72, 0x44,
+ 0x56, 0x36, 0x4F, 0x6E, 0x3F, 0x80, 0x00, 0x80,
+ 0x4F, 0x7F, 0x03, 0xA8, 0x7D, 0x20, 0x1A, 0xA9,
+ 0x14, 0x05, 0x03, 0x7E, 0x64, 0x31, 0x14, 0x75,
+ 0x18, 0x05, 0x18, 0x05, 0x4C, 0xA8, 0x01
+};
+
+static const unsigned char XGI330_HiTVGroup3Simu[] = {
+ 0x00, 0x1A, 0x22, 0x63, 0x62, 0x22, 0x08, 0x95,
+ 0xDB, 0x20, 0xB8, 0xB8, 0x55, 0x47, 0x2A, 0xA6,
+ 0x25, 0x2F, 0x47, 0xFA, 0xC8, 0xFF, 0x8E, 0x20,
+ 0x8C, 0x6E, 0x60, 0x15, 0x26, 0xD3, 0xE4, 0x11,
+ 0x56, 0x36, 0x4F, 0x6E, 0x3F, 0x80, 0x00, 0x80,
+ 0x67, 0x36, 0x01, 0x47, 0x0E, 0x10, 0xBE, 0xB4,
+ 0x01, 0x05, 0x03, 0x7E, 0x65, 0x31, 0x14, 0x75,
+ 0x18, 0x05, 0x18, 0x05, 0x4C, 0xA8, 0x01
+};
+
+static const unsigned char XGI330_HiTVGroup3Text[] = {
+ 0x00, 0x1A, 0x22, 0x63, 0x62, 0x22, 0x08, 0xA7,
+ 0xF5, 0x20, 0xCE, 0xCE, 0x55, 0x47, 0x2A, 0xA6,
+ 0x25, 0x2F, 0x47, 0xFA, 0xC8, 0xFF, 0x8E, 0x20,
+ 0x8C, 0x6E, 0x60, 0x18, 0x2C, 0x0C, 0x20, 0x22,
+ 0x56, 0x36, 0x4F, 0x6E, 0x3F, 0x80, 0x00, 0x80,
+ 0x93, 0x3C, 0x01, 0x50, 0x2F, 0x10, 0xF4, 0xCA,
+ 0x01, 0x05, 0x03, 0x7E, 0x65, 0x31, 0x14, 0x75,
+ 0x18, 0x05, 0x18, 0x05, 0x4C, 0xA8, 0x01
+};
+
+static const unsigned char XGI330_Ren525pGroup3[] = {
+ 0x00, 0x14, 0x15, 0x25, 0x55, 0x15, 0x0b, 0x13,
+ 0xB1, 0x41, 0x62, 0x62, 0xFF, 0xF4, 0x45, 0xa6,
+ 0x25, 0x2F, 0x67, 0xF6, 0xbf, 0xFF, 0x8E, 0x20,
+ 0xAC, 0xDA, 0x60, 0xFe, 0x6A, 0x9A, 0x06, 0x10,
+ 0xd1, 0x04, 0x18, 0x0a, 0xFF, 0x80, 0x00, 0x80,
+ 0x3c, 0x77, 0x00, 0xEF, 0xE0, 0x10, 0xB0, 0xE0,
+ 0x10, 0x4F, 0x0F, 0x0F, 0x05, 0x0F, 0x08, 0x6E,
+ 0x1a, 0x1F, 0x25, 0x2a, 0x4C, 0xAA, 0x01
+};
+
+static const unsigned char XGI330_Ren750pGroup3[] = {
+ 0x00, 0x14, 0x15, 0x25, 0x55, 0x15, 0x0b, 0x7a,
+ 0x54, 0x41, 0xE7, 0xE7, 0xFF, 0xF4, 0x45, 0xa6,
+ 0x25, 0x2F, 0x67, 0xF6, 0xbf, 0xFF, 0x8E, 0x20,
+ 0xAC, 0x6A, 0x60, 0x2b, 0x52, 0xCD, 0x61, 0x10,
+ 0x51, 0x04, 0x18, 0x0a, 0x1F, 0x80, 0x00, 0x80,
+ 0xFF, 0xA4, 0x04, 0x2B, 0x94, 0x21, 0x72, 0x94,
+ 0x26, 0x05, 0x01, 0x0F, 0xed, 0x0F, 0x0A, 0x64,
+ 0x18, 0x1D, 0x23, 0x28, 0x4C, 0xAA, 0x01
+};
+
+static const struct SiS_LVDSData XGI_LVDS1024x768Data_1[] = {
+ { 960, 438, 1344, 806}, /* 00 (320x200,320x400,640x200,640x400) */
+ { 960, 388, 1344, 806}, /* 01 (320x350,640x350) */
+ {1040, 438, 1344, 806}, /* 02 (360x400,720x400) */
+ {1040, 388, 1344, 806}, /* 03 (720x350) */
+ { 960, 518, 1344, 806}, /* 04 (320x240,640x480) */
+ {1120, 638, 1344, 806}, /* 05 (400x300,800x600) */
+ {1344, 806, 1344, 806} /* 06 (512x384,1024x768) */
+};
+
+
+static const struct SiS_LVDSData XGI_LVDS1024x768Data_2[] = {
+ {1344, 806, 1344, 806},
+ {1344, 806, 1344, 806},
+ {1344, 806, 1344, 806},
+ {1344, 806, 1344, 806},
+ {1344, 806, 1344, 806},
+ {1344, 806, 1344, 806},
+ {1344, 806, 1344, 806},
+ {800, 449, 1280, 801},
+ {800, 525, 1280, 813}
+};
+
+static const struct SiS_LVDSData XGI_LVDS1280x1024Data_1[] = {
+ {1048, 442, 1688, 1066},
+ {1048, 392, 1688, 1066},
+ {1048, 442, 1688, 1066},
+ {1048, 392, 1688, 1066},
+ {1048, 522, 1688, 1066},
+ {1208, 642, 1688, 1066},
+ {1432, 810, 1688, 1066},
+ {1688, 1066, 1688, 1066}
+};
+
+#define XGI_LVDS1280x1024Data_2 XGI_LVDS1024x768Data_2
+
+static const struct SiS_LVDSData XGI_LVDS1400x1050Data_1[] = {
+ {928, 416, 1688, 1066},
+ {928, 366, 1688, 1066},
+ {928, 416, 1688, 1066},
+ {928, 366, 1688, 1066},
+ {928, 496, 1688, 1066},
+ {1088, 616, 1688, 1066},
+ {1312, 784, 1688, 1066},
+ {1568, 1040, 1688, 1066},
+ {1688, 1066, 1688, 1066}
+};
+
+static const struct SiS_LVDSData XGI_LVDS1400x1050Data_2[] = {
+ {1688, 1066, 1688, 1066},
+ {1688, 1066, 1688, 1066},
+ {1688, 1066, 1688, 1066},
+ {1688, 1066, 1688, 1066},
+ {1688, 1066, 1688, 1066},
+ {1688, 1066, 1688, 1066},
+ {1688, 1066, 1688, 1066},
+ {1688, 1066, 1688, 1066},
+ {1688, 1066, 1688, 1066}
+};
+
+/* ;;[ycchen] 12/05/02 LCDHTxLCDVT=2048x1320 */
+static const struct SiS_LVDSData XGI_LVDS1600x1200Data_1[] = {
+ {1088, 520, 2048, 1320}, /* 00 (320x200,320x400,640x200,640x400) */
+ {1088, 470, 2048, 1320}, /* 01 (320x350,640x350) */
+ {1088, 520, 2048, 1320}, /* 02 (360x400,720x400) */
+ {1088, 470, 2048, 1320}, /* 03 (720x350) */
+ {1088, 600, 2048, 1320}, /* 04 (320x240,640x480) */
+ {1248, 720, 2048, 1320}, /* 05 (400x300,800x600) */
+ {1472, 888, 2048, 1320}, /* 06 (512x384,1024x768) */
+ {1728, 1144, 2048, 1320}, /* 07 (640x512,1280x1024) */
+ {1848, 1170, 2048, 1320}, /* 08 (1400x1050) */
+ {2048, 1320, 2048, 1320} /* 09 (1600x1200) */
+};
+
+static const struct SiS_LVDSData XGI_LVDSNoScalingData[] = {
+ { 800, 449, 800, 449}, /* 00 (320x200,320x400,640x200,640x400) */
+ { 800, 449, 800, 449}, /* 01 (320x350,640x350) */
+ { 800, 449, 800, 449}, /* 02 (360x400,720x400) */
+ { 800, 449, 800, 449}, /* 03 (720x350) */
+ { 800, 525, 800, 525}, /* 04 (640x480x60Hz) */
+ {1056, 628, 1056, 628}, /* 05 (800x600x60Hz) */
+ {1344, 806, 1344, 806}, /* 06 (1024x768x60Hz) */
+ {1688, 1066, 1688, 1066}, /* 07 (1280x1024x60Hz) */
+ {1688, 1066, 1688, 1066}, /* 08 (1400x1050x60Hz) */
+ {2160, 1250, 2160, 1250}, /* 09 (1600x1200x60Hz) */
+ {1688, 806, 1688, 806} /* 0A (1280x768x60Hz) */
+};
+
+static const struct SiS_LVDSData XGI_LVDS1024x768Data_1x75[] = {
+ { 960, 438, 1312, 800}, /* 00 (320x200,320x400,640x200,640x400) */
+ { 960, 388, 1312, 800}, /* 01 (320x350,640x350) */
+ {1040, 438, 1312, 800}, /* 02 (360x400,720x400) */
+ {1040, 388, 1312, 800}, /* 03 (720x350) */
+ { 928, 512, 1312, 800}, /* 04 (320x240,640x480) */
+ {1088, 632, 1312, 800}, /* 05 (400x300,800x600) */
+ {1312, 800, 1312, 800}, /* 06 (512x384,1024x768) */
+};
+
+
+static const struct SiS_LVDSData XGI_LVDS1024x768Data_2x75[] = {
+ {1312, 800, 1312, 800}, /* ; 00 (320x200,320x400,640x200,640x400) */
+ {1312, 800, 1312, 800}, /* ; 01 (320x350,640x350) */
+ {1312, 800, 1312, 800}, /* ; 02 (360x400,720x400) */
+ {1312, 800, 1312, 800}, /* ; 03 (720x350) */
+ {1312, 800, 1312, 800}, /* ; 04 (320x240,640x480) */
+ {1312, 800, 1312, 800}, /* ; 05 (400x300,800x600) */
+ {1312, 800, 1312, 800}, /* ; 06 (512x384,1024x768) */
+};
+
+static const struct SiS_LVDSData XGI_LVDS1280x1024Data_1x75[] = {
+ {1048, 442, 1688, 1066 }, /* ; 00 (320x200,320x400,640x200,640x400) */
+ {1048, 392, 1688, 1066 }, /* ; 01 (320x350,640x350) */
+ {1128, 442, 1688, 1066 }, /* ; 02 (360x400,720x400) */
+ {1128, 392, 1688, 1066 }, /* ; 03 (720x350) */
+ {1048, 522, 1688, 1066 }, /* ; 04 (320x240,640x480) */
+ {1208, 642, 1688, 1066 }, /* ; 05 (400x300,800x600) */
+ {1432, 810, 1688, 1066 }, /* ; 06 (512x384,1024x768) */
+ {1688, 1066, 1688, 1066 }, /* ; 06; 07 (640x512,1280x1024) */
+};
+
+static const struct SiS_LVDSData XGI_LVDS1280x1024Data_2x75[] = {
+ {1688, 1066, 1688, 1066 }, /* ; 00 (320x200,320x400,640x200,640x400) */
+ {1688, 1066, 1688, 1066 }, /* ; 01 (320x350,640x350) */
+ {1688, 1066, 1688, 1066 }, /* ; 02 (360x400,720x400) */
+ {1688, 1066, 1688, 1066 }, /* ; 03 (720x350) */
+ {1688, 1066, 1688, 1066 }, /* ; 04 (320x240,640x480) */
+ {1688, 1066, 1688, 1066 }, /* ; 05 (400x300,800x600) */
+ {1688, 1066, 1688, 1066 }, /* ; 06 (512x384,1024x768) */
+ {1688, 1066, 1688, 1066 }, /* ; 06; 07 (640x512,1280x1024) */
+};
+
+static const struct SiS_LVDSData XGI_LVDSNoScalingDatax75[] = {
+ { 800, 449, 800, 449}, /* ; 00 (320x200,320x400,640x200,640x400) */
+ { 800, 449, 800, 449}, /* ; 01 (320x350,640x350) */
+ { 900, 449, 900, 449}, /* ; 02 (360x400,720x400) */
+ { 900, 449, 900, 449}, /* ; 03 (720x350) */
+ { 800, 500, 800, 500}, /* ; 04 (640x480x75Hz) */
+ {1056, 625, 1056, 625}, /* ; 05 (800x600x75Hz) */
+ {1312, 800, 1312, 800}, /* ; 06 (1024x768x75Hz) */
+ {1688, 1066, 1688, 1066}, /* ; 07 (1280x1024x75Hz) */
+ {1688, 1066, 1688, 1066}, /* ; 08 (1400x1050x75Hz)
+ ;;[ycchen] 12/19/02 */
+ {2160, 1250, 2160, 1250}, /* ; 09 (1600x1200x75Hz) */
+ {1688, 806, 1688, 806}, /* ; 0A (1280x768x75Hz) */
+};
+
+static const struct SiS_LVDSData XGI_LVDS1024x768Des_1[] = {
+ {0, 1048, 0, 771}, /* 00 (320x200,320x400,640x200,640x400) */
+ {0, 1048, 0, 771}, /* 01 (320x350,640x350) */
+ {0, 1048, 0, 771}, /* 02 (360x400,720x400) */
+ {0, 1048, 0, 771}, /* 03 (720x350) */
+ {0, 1048, 0, 771}, /* 04 (640x480x60Hz) */
+ {0, 1048, 0, 771}, /* 05 (800x600x60Hz) */
+ {0, 1048, 805, 770} /* 06 (1024x768x60Hz) */
+};
+
+static const struct SiS_LVDSData XGI_LVDS1024x768Des_2[] = {
+ {1142, 856, 622, 587}, /* 00 (320x200,320x400,640x200,640x400) */
+ {1142, 856, 597, 562}, /* 01 (320x350,640x350) */
+ {1142, 856, 622, 587}, /* 02 (360x400,720x400) */
+ {1142, 856, 597, 562}, /* 03 (720x350) */
+ {1142, 1048, 722, 687}, /* 04 (640x480x60Hz) */
+ {1232, 936, 722, 687}, /* 05 (800x600x60Hz) */
+ { 0, 1048, 805, 771} /* 06 (1024x768x60Hz) */
+};
+
+static const struct SiS_LVDSData XGI_LVDS1024x768Des_3[] = {
+ {320, 24, 622, 587}, /* 00 (320x200,320x400,640x200,640x400) */
+ {320, 24, 597, 562}, /* 01 (320x350,640x350) */
+ {320, 24, 622, 587}, /* 02 (360x400,720x400) */
+ {320, 24, 597, 562}, /* 03 (720x350) */
+ {320, 24, 722, 687} /* 04 (640x480x60Hz) */
+};
+
+static const struct SiS_LVDSData XGI_LVDS1280x1024Des_1[] = {
+ {0, 1328, 0, 1025}, /* 00 (320x200,320x400,640x200,640x400) */
+ {0, 1328, 0, 1025}, /* 01 (320x350,640x350) */
+ {0, 1328, 0, 1025}, /* 02 (360x400,720x400) */
+ {0, 1328, 0, 1025}, /* 03 (720x350) */
+ {0, 1328, 0, 1025}, /* 04 (640x480x60Hz) */
+ {0, 1328, 0, 1025}, /* 05 (800x600x60Hz) */
+ {0, 1328, 0, 1025}, /* 06 (1024x768x60Hz) */
+ {0, 1328, 1065, 1024} /* 07 (1280x1024x60Hz) */
+};
+
+ /* The Display setting for DE Mode Panel */
+static const struct SiS_LVDSData XGI_LVDS1280x1024Des_2[] = {
+ {1368, 1008, 752, 711}, /* 00 (320x200,320x400,640x200,640x400) */
+ {1368, 1008, 729, 688}, /* 01 (320x350,640x350) */
+ {1408, 1048, 752, 711}, /* 02 (360x400,720x400) */
+ {1408, 1048, 729, 688}, /* 03 (720x350) */
+ {1368, 1008, 794, 753}, /* 04 (640x480x60Hz) */
+ {1448, 1068, 854, 813}, /* 05 (800x600x60Hz) */
+ {1560, 1200, 938, 897}, /* 06 (1024x768x60Hz) */
+ {0000, 1328, 0, 1025} /* 07 (1280x1024x60Hz) */
+};
+
+static const struct SiS_LVDSData XGI_LVDS1400x1050Des_1[] = {
+ {0, 1448, 0, 1051}, /* 00 (320x200,320x400,640x200,640x400) */
+ {0, 1448, 0, 1051}, /* 01 (320x350,640x350) */
+ {0, 1448, 0, 1051}, /* 02 (360x400,720x400) */
+ {0, 1448, 0, 1051}, /* 03 (720x350) */
+ {0, 1448, 0, 1051}, /* 04 (640x480x60Hz) */
+ {0, 1448, 0, 1051}, /* 05 (800x600x60Hz) */
+ {0, 1448, 0, 1051}, /* 06 (1024x768x60Hz) */
+ {0, 1448, 0, 1051}, /* 07 (1280x1024x60Hz) */
+ {0, 1448, 0, 1051} /* 08 (1400x1050x60Hz) */
+};
+
+static const struct SiS_LVDSData XGI_LVDS1400x1050Des_2[] = {
+ {1308, 1068, 781, 766}, /* 00 (320x200,320x400,640x200,640x400) */
+ {1308, 1068, 781, 766}, /* 01 (320x350,640x350) */
+ {1308, 1068, 781, 766}, /* 02 (360x400,720x400) */
+ {1308, 1068, 781, 766}, /* 03 (720x350) */
+ {1308, 1068, 781, 766}, /* 04 (640x480x60Hz) */
+ {1388, 1148, 841, 826}, /* 05 (800x600x60Hz) */
+ {1490, 1250, 925, 910}, /* 06 (1024x768x60Hz) */
+ {1608, 1368, 1053, 1038}, /* 07 (1280x1024x60Hz) */
+ { 0, 1448, 0, 1051} /* 08 (1400x1050x60Hz) */
+};
+
+static const struct SiS_LVDSData XGI_LVDS1600x1200Des_1[] = {
+ {0, 1664, 0, 1201}, /* 00 (320x200,320x400,640x200,640x400) */
+ {0, 1664, 0, 1201}, /* 01 (320x350,640x350) */
+ {0, 1664, 0, 1201}, /* 02 (360x400,720x400) */
+ {0, 1664, 0, 1201}, /* 03 (720x350) */
+ {0, 1664, 0, 1201}, /* 04 (640x480x60Hz) */
+ {0, 1664, 0, 1201}, /* 05 (800x600x60Hz) */
+ {0, 1664, 0, 1201}, /* 06 (1024x768x60Hz) */
+ {0, 1664, 0, 1201}, /* 07 (1280x1024x60Hz) */
+ {0, 1664, 0, 1201}, /* 08 (1400x1050x60Hz) */
+ {0, 1664, 0, 1201} /* 09 (1600x1200x60Hz) */
+};
+
+static const struct XGI330_LCDDataDesStruct2 XGI_LVDSNoScalingDesData[] = {
+ {0, 648, 448, 405, 96, 2}, /* 00 (320x200,320x400,
+ 640x200,640x400) */
+ {0, 648, 448, 355, 96, 2}, /* 01 (320x350,640x350) */
+ {0, 648, 448, 405, 96, 2}, /* 02 (360x400,720x400) */
+ {0, 648, 448, 355, 96, 2}, /* 03 (720x350) */
+ {0, 648, 1, 483, 96, 2}, /* 04 (640x480x60Hz) */
+ {0, 840, 627, 600, 128, 4}, /* 05 (800x600x60Hz) */
+ {0, 1048, 805, 770, 136, 6}, /* 06 (1024x768x60Hz) */
+ {0, 1328, 0, 1025, 112, 3}, /* 07 (1280x1024x60Hz) */
+ {0, 1438, 0, 1051, 112, 3}, /* 08 (1400x1050x60Hz)*/
+ {0, 1664, 0, 1201, 192, 3}, /* 09 (1600x1200x60Hz) */
+ {0, 1328, 0, 0771, 112, 6} /* 0A (1280x768x60Hz) */
+};
+
+/* ; 1024x768 Full-screen */
+static const struct SiS_LVDSData XGI_LVDS1024x768Des_1x75[] = {
+ {0, 1040, 0, 769}, /* ; 00 (320x200,320x400,640x200,640x400) */
+ {0, 1040, 0, 769}, /* ; 01 (320x350,640x350) */
+ {0, 1040, 0, 769}, /* ; 02 (360x400,720x400) */
+ {0, 1040, 0, 769}, /* ; 03 (720x350) */
+ {0, 1040, 0, 769}, /* ; 04 (640x480x75Hz) */
+ {0, 1040, 0, 769}, /* ; 05 (800x600x75Hz) */
+ {0, 1040, 0, 769} /* ; 06 (1024x768x75Hz) */
+};
+
+/* ; 1024x768 center-screen (Enh. Mode) */
+static const struct SiS_LVDSData XGI_LVDS1024x768Des_2x75[] = {
+ {1142, 856, 622, 587}, /* 00 (320x200,320x400,640x200,640x400) */
+ {1142, 856, 597, 562}, /* 01 (320x350,640x350) */
+ {1142, 856, 622, 587}, /* 02 (360x400,720x400) */
+ {1142, 856, 597, 562}, /* 03 (720x350) */
+ {1142, 1048, 722, 687}, /* 04 (640x480x60Hz) */
+ {1232, 936, 722, 687}, /* 05 (800x600x60Hz) */
+ { 0, 1048, 805, 771} /* 06 (1024x768x60Hz) */
+};
+
+/* ; 1024x768 center-screen (St.Mode) */
+static const struct SiS_LVDSData XGI_LVDS1024x768Des_3x75[] = {
+ {320, 24, 622, 587}, /* ; 00 (320x200,320x400,640x200,640x400) */
+ {320, 24, 597, 562}, /* ; 01 (320x350,640x350) */
+ {320, 24, 622, 587}, /* ; 02 (360x400,720x400) */
+ {320, 24, 597, 562}, /* ; 03 (720x350) */
+ {320, 24, 722, 687} /* ; 04 (640x480x60Hz) */
+};
+
+static const struct SiS_LVDSData XGI_LVDS1280x1024Des_1x75[] = {
+ {0, 1296, 0, 1025}, /* ; 00 (320x200,320x400,640x200,640x400) */
+ {0, 1296, 0, 1025}, /* ; 01 (320x350,640x350) */
+ {0, 1296, 0, 1025}, /* ; 02 (360x400,720x400) */
+ {0, 1296, 0, 1025}, /* ; 03 (720x350) */
+ {0, 1296, 0, 1025}, /* ; 04 (640x480x75Hz) */
+ {0, 1296, 0, 1025}, /* ; 05 (800x600x75Hz) */
+ {0, 1296, 0, 1025}, /* ; 06 (1024x768x75Hz) */
+ {0, 1296, 0, 1025} /* ; 07 (1280x1024x75Hz) */
+};
+
+/* The Display setting for DE Mode Panel */
+/* Set DE as default */
+static const struct SiS_LVDSData XGI_LVDS1280x1024Des_2x75[] = {
+ {1368, 976, 752, 711}, /* ; 00 (320x200,320x400,640x200,640x400) */
+ {1368, 976, 729, 688}, /* ; 01 (320x350,640x350) */
+ {1408, 976, 752, 711}, /* ; 02 (360x400,720x400) */
+ {1408, 976, 729, 688}, /* ; 03 (720x350) */
+ {1368, 976, 794, 753}, /* ; 04 (640x480x75Hz) */
+ {1448, 1036, 854, 813}, /* ; 05 (800x600x75Hz) */
+ {1560, 1168, 938, 897}, /* ; 06 (1024x768x75Hz) */
+ { 0, 1296, 0, 1025} /* ; 07 (1280x1024x75Hz) */
+};
+
+/* Scaling LCD 75Hz */
+static const struct XGI330_LCDDataDesStruct2 XGI_LVDSNoScalingDesDatax75[] = {
+ {0, 648, 448, 405, 96, 2}, /* ; 00 (320x200,320x400,
+ 640x200,640x400) */
+ {0, 648, 448, 355, 96, 2}, /* ; 01 (320x350,640x350) */
+ {0, 729, 448, 405, 108, 2}, /* ; 02 (360x400,720x400) */
+ {0, 729, 448, 355, 108, 2}, /* ; 03 (720x350) */
+ {0, 656, 0, 481, 64, 3}, /* ; 04 (640x480x75Hz) */
+ {0, 816, 0, 601, 80, 3}, /* ; 05 (800x600x75Hz) */
+ {0, 1040, 0, 769, 96, 3}, /* ; 06 (1024x768x75Hz) */
+ {0, 1296, 0, 1025, 144, 3}, /* ; 07 (1280x1024x75Hz) */
+ {0, 1448, 0, 1051, 112, 3}, /* ; 08 (1400x1050x75Hz) */
+ {0, 1664, 0, 1201, 192, 3}, /* ; 09 (1600x1200x75Hz) */
+ {0, 1328, 0, 771, 112, 6} /* ; 0A (1280x768x75Hz) */
+};
+
+/* CR00,CR02,CR03,CR04,CR05,SR0B,SR0C,SR0E */
+static const struct XGI_LVDSCRT1HDataStruct XGI_LVDSCRT11024x768_1_H[] = {
+ { {0x4B, 0x27, 0x8F, 0x32, 0x1B, 0x00, 0x45, 0x00} }, /* 00 (320x) */
+ { {0x4B, 0x27, 0x8F, 0x2B, 0x03, 0x00, 0x44, 0x00} }, /* 01 (360x) */
+ { {0x55, 0x31, 0x99, 0x46, 0x1D, 0x00, 0x55, 0x00} }, /* 02 (400x) */
+ { {0x63, 0x3F, 0x87, 0x4A, 0x93, 0x00, 0x01, 0x00} }, /* 03 (512x) */
+ { {0x73, 0x4F, 0x97, 0x55, 0x86, 0x00, 0x05, 0x00} }, /* 04 (640x) */
+ { {0x73, 0x4F, 0x97, 0x55, 0x86, 0x00, 0x05, 0x00} }, /* 05 (720x) */
+ { {0x87, 0x63, 0x8B, 0x69, 0x1A, 0x00, 0x26, 0x00} }, /* 06 (800x) */
+ { {0xA3, 0x7F, 0x87, 0x86, 0x97, 0x00, 0x02, 0x00} } /* 07 (1024x) */
+};
+
+/* CR00,CR02,CR03,CR04,CR05,SR0B,SR0C,SR0E */
+static const struct XGI_LVDSCRT1HDataStruct XGI_LVDSCRT11280x1024_1_H[] = {
+ { {0x56, 0x27, 0x9A, 0x30, 0x1E, 0x00, 0x05, 0x00 } }, /* 00 (320x) */
+ { {0x56, 0x27, 0x9A, 0x30, 0x1E, 0x00, 0x05, 0x00 } }, /* 01 (360x) */
+ { {0x60, 0x31, 0x84, 0x3A, 0x88, 0x00, 0x01, 0x00 } }, /* 02 (400x) */
+ { {0x6E, 0x3F, 0x92, 0x48, 0x96, 0x00, 0x01, 0x00 } }, /* 03 (512x) */
+ { {0x7E, 0x4F, 0x82, 0x58, 0x06, 0x00, 0x06, 0x00 } }, /* 04 (640x) */
+ { {0x7E, 0x4F, 0x82, 0x58, 0x06, 0x00, 0x06, 0x00 } }, /* 05 (720x) */
+ { {0x92, 0x63, 0x96, 0x6C, 0x1A, 0x00, 0x06, 0x00 } }, /* 06 (800x) */
+ { {0xAE, 0x7F, 0x92, 0x88, 0x96, 0x00, 0x02, 0x00 } }, /* 07 (1024x) */
+ { {0xCE, 0x9F, 0x92, 0xA8, 0x16, 0x00, 0x07, 0x00 } } /* 08 (1280x) */
+};
+
+/* CR00,CR02,CR03,CR04,CR05,SR0B,SR0C,SR0E */
+static const struct XGI_LVDSCRT1HDataStruct XGI_LVDSCRT11024x768_2_H[] = {
+ { {0x63, 0x27, 0x87, 0x3B, 0x8C, 0x00, 0x01, 0x00} }, /* 00 (320x) */
+ { {0x63, 0x27, 0x87, 0x3B, 0x8C, 0x00, 0x01, 0x00} }, /* 01 (360x) */
+ { {0x63, 0x31, 0x87, 0x3D, 0x8E, 0x00, 0x01, 0x00} }, /* 02 (400x) */
+ { {0x63, 0x3F, 0x87, 0x45, 0x96, 0x00, 0x01, 0x00} }, /* 03 (512x) */
+ { {0xA3, 0x4F, 0x87, 0x6E, 0x9F, 0x00, 0x06, 0x00} }, /* 04 (640x) */
+ { {0xA3, 0x4F, 0x87, 0x6E, 0x9F, 0x00, 0x06, 0x00} }, /* 05 (720x) */
+ { {0xA3, 0x63, 0x87, 0x78, 0x89, 0x00, 0x02, 0x00} }, /* 06 (800x) */
+ { {0xA3, 0x7F, 0x87, 0x86, 0x97, 0x00, 0x02, 0x00} } /* 07 (1024x) */
+};
+
+/* CR00,CR02,CR03,CR04,CR05,SR0B,SR0C,SR0E */
+static const struct XGI_LVDSCRT1HDataStruct XGI_LVDSCRT11280x1024_2_H[] = {
+ { {0x7E, 0x3B, 0x9A, 0x44, 0x12, 0x00, 0x01, 0x00} }, /* 00 (320x) */
+ { {0x7E, 0x3B, 0x9A, 0x44, 0x12, 0x00, 0x01, 0x00} }, /* 01 (360x) */
+ { {0x7E, 0x40, 0x84, 0x49, 0x91, 0x00, 0x01, 0x00} }, /* 02 (400x) */
+ { {0x7E, 0x47, 0x93, 0x50, 0x9E, 0x00, 0x01, 0x00} }, /* 03 (512x) */
+ { {0xCE, 0x77, 0x8A, 0x80, 0x8E, 0x00, 0x02, 0x00} }, /* 04 (640x) */
+ { {0xCE, 0x77, 0x8A, 0x80, 0x8E, 0x00, 0x02, 0x00} }, /* 05 (720x) */
+ { {0xCE, 0x81, 0x94, 0x8A, 0x98, 0x00, 0x02, 0x00} }, /* 06 (800x) */
+ { {0xCE, 0x8F, 0x82, 0x98, 0x06, 0x00, 0x07, 0x00} }, /* 07 (1024x) */
+ { {0xCE, 0x9F, 0x92, 0xA8, 0x16, 0x00, 0x07, 0x00} } /* 08 (1280x) */
+};
+
+/* CR00,CR02,CR03,CR04,CR05,SR0B,SR0C,SR0E */
+static const struct XGI_LVDSCRT1HDataStruct XGI_LVDSCRT11400x1050_1_H[] = {
+ { {0x47, 0x27, 0x8B, 0x2C, 0x1A, 0x00, 0x05, 0x00} }, /* 00 (320x) */
+ { {0x47, 0x27, 0x8B, 0x30, 0x1E, 0x00, 0x05, 0x00} }, /* 01 (360x) */
+ { {0x51, 0x31, 0x95, 0x36, 0x04, 0x00, 0x01, 0x00} }, /* 02 (400x) */
+ { {0x5F, 0x3F, 0x83, 0x44, 0x92, 0x00, 0x01, 0x00} }, /* 03 (512x) */
+ { {0x6F, 0x4F, 0x93, 0x54, 0x82, 0x00, 0x05, 0x00} }, /* 04 (640x) */
+ { {0x6F, 0x4F, 0x93, 0x54, 0x82, 0x00, 0x05, 0x00} }, /* 05 (720x) */
+ { {0x83, 0x63, 0x87, 0x68, 0x16, 0x00, 0x06, 0x00} }, /* 06 (800x) */
+ { {0x9F, 0x7F, 0x83, 0x84, 0x92, 0x00, 0x02, 0x00} }, /* 07 (1024x) */
+ { {0xBF, 0x9F, 0x83, 0xA4, 0x12, 0x00, 0x07, 0x00} }, /* 08 (1280x) */
+ { {0xCE, 0xAE, 0x92, 0xB3, 0x01, 0x00, 0x03, 0x00} } /* 09 (1400x) */
+};
+
+/* CR00,CR02,CR03,CR04,CR05,SR0B,SR0C,SR0E */
+static const struct XGI_LVDSCRT1HDataStruct XGI_LVDSCRT11400x1050_2_H[] = {
+ { {0x76, 0x3F, 0x83, 0x45, 0x8C, 0x00, 0x41, 0x00} }, /* 00 (320x) */
+ { {0x76, 0x3F, 0x83, 0x45, 0x8C, 0x00, 0x41, 0x00} }, /* 01 (360x) */
+ { {0x76, 0x31, 0x9A, 0x48, 0x9F, 0x00, 0x41, 0x00} }, /* 02 (400x) */
+ { {0x76, 0x3F, 0x9A, 0x4F, 0x96, 0x00, 0x41, 0x00} }, /* 03 (512x) */
+ { {0xCE, 0x7E, 0x82, 0x87, 0x9E, 0x00, 0x02, 0x00} }, /* 04 (640x) */
+ { {0xCE, 0x7E, 0x82, 0x87, 0x9E, 0x00, 0x02, 0x00} }, /* 05 (720x) */
+ { {0xCE, 0x63, 0x92, 0x96, 0x04, 0x00, 0x07, 0x00} }, /* 06 (800x) */
+ { {0xCE, 0x7F, 0x92, 0xA4, 0x12, 0x00, 0x07, 0x00} }, /* 07 (1024x) */
+ { {0xCE, 0x9F, 0x92, 0xB4, 0x02, 0x00, 0x03, 0x00} }, /* 08 (1280x) */
+ { {0xCE, 0xAE, 0x92, 0xBC, 0x0A, 0x00, 0x03, 0x00} } /* 09 (1400x) */
+};
+
+/* ;302lv channelA [ycchen] 12/05/02 LCDHT=2048 */
+/* ; CR00,CR02,CR03,CR04,CR05,SR0B,SR0C,SR0E */
+static const struct XGI_LVDSCRT1HDataStruct XGI_LVDSCRT11600x1200_1_H[] = {
+ { {0x5B, 0x27, 0x9F, 0x32, 0x0A, 0x00, 0x01, 0x00} }, /* 00 (320x) */
+ { {0x5B, 0x27, 0x9F, 0x32, 0x0A, 0x00, 0x01, 0x00} }, /* 01 (360x) */
+ { {0x65, 0x31, 0x89, 0x3C, 0x94, 0x00, 0x01, 0x00} }, /* 02 (400x) */
+ { {0x73, 0x3F, 0x97, 0x4A, 0x82, 0x00, 0x05, 0x00} }, /* 03 (512x) */
+ { {0x83, 0x4F, 0x87, 0x51, 0x09, 0x00, 0x06, 0x00} }, /* 04 (640x) */
+ { {0x83, 0x4F, 0x87, 0x51, 0x09, 0x00, 0x06, 0x00} }, /* 05 (720x) */
+ { {0x97, 0x63, 0x9B, 0x65, 0x1D, 0x00, 0x06, 0xF0} }, /* 06 (800x) */
+ { {0xB3, 0x7F, 0x97, 0x81, 0x99, 0x00, 0x02, 0x00} }, /* 07 (1024x) */
+ { {0xD3, 0x9F, 0x97, 0xA1, 0x19, 0x00, 0x07, 0x00} }, /* 08 (1280x) */
+ { {0xE2, 0xAE, 0x86, 0xB9, 0x91, 0x00, 0x03, 0x00} }, /* 09 (1400x) */
+ { {0xFB, 0xC7, 0x9F, 0xC9, 0x81, 0x00, 0x07, 0x00} } /* 0A (1600x) */
+};
+
+/* CR06,CR07,CR10,CR11,CR15,CR16,SR0A+CR09(5->7) */
+static const struct XGI_LVDSCRT1VDataStruct XGI_LVDSCRT11024x768_1_V[] = {
+ { {0x97, 0x1F, 0x60, 0x87, 0x5D, 0x83, 0x10} }, /* 00 (x350) */
+ { {0xB4, 0x1F, 0x92, 0x89, 0x8F, 0xB5, 0x30} }, /* 01 (x400) */
+ { {0x04, 0x3E, 0xE2, 0x89, 0xDF, 0x05, 0x00} }, /* 02 (x480) */
+ { {0x7C, 0xF0, 0x5A, 0x8F, 0x57, 0x7D, 0xA0} }, /* 03 (x600) */
+ { {0x24, 0xF5, 0x02, 0x88, 0xFF, 0x25, 0x90} } /* 04 (x768) */
+};
+
+/* CR06,CR07,CR10,CR11,CR15,CR16,SR0A */
+static const struct XGI_LVDSCRT1VDataStruct XGI_LVDSCRT11024x768_2_V[] = {
+ { {0x24, 0xBB, 0x31, 0x87, 0x5D, 0x25, 0x30} }, /* 00 (x350) */
+ { {0x24, 0xBB, 0x4A, 0x80, 0x8F, 0x25, 0x30} }, /* 01 (x400) */
+ { {0x24, 0xBB, 0x72, 0x88, 0xDF, 0x25, 0x30} }, /* 02 (x480) */
+ { {0x24, 0xF1, 0xAE, 0x84, 0x57, 0x25, 0xB0} }, /* 03 (x600) */
+ { {0x24, 0xF5, 0x02, 0x88, 0xFF, 0x25, 0x90} } /* 04 (x768) */
+};
+
+/* CR06,CR07,CR10,CR11,CR15,CR16,SR0A */
+static const struct XGI_LVDSCRT1VDataStruct XGI_LVDSCRT11280x1024_1_V[] = {
+ { {0x86, 0x1F, 0x5E, 0x82, 0x5D, 0x87, 0x00} }, /* 00 (x350) */
+ { {0xB8, 0x1F, 0x90, 0x84, 0x8F, 0xB9, 0x30} }, /* 01 (x400) */
+ { {0x08, 0x3E, 0xE0, 0x84, 0xDF, 0x09, 0x00} }, /* 02 (x480) */
+ { {0x80, 0xF0, 0x58, 0x8C, 0x57, 0x81, 0xA0} }, /* 03 (x600) */
+ { {0x28, 0xF5, 0x00, 0x84, 0xFF, 0x29, 0x90} }, /* 04 (x768) */
+ { {0x28, 0x5A, 0x13, 0x87, 0xFF, 0x29, 0xA9} } /* 05 (x1024) */
+};
+
+/* CR06,CR07,CR10,CR11,CR15,CR16,SR0A */
+static const struct XGI_LVDSCRT1VDataStruct XGI_LVDSCRT11280x1024_2_V[] = {
+ { {0x28, 0xD2, 0xAF, 0x83, 0xAE, 0xD8, 0xA1} }, /* 00 (x350) */
+ { {0x28, 0xD2, 0xC8, 0x8C, 0xC7, 0xF2, 0x81} }, /* 01 (x400) */
+ { {0x28, 0xD2, 0xF0, 0x84, 0xEF, 0x1A, 0xB1} }, /* 02 (x480) */
+ { {0x28, 0xDE, 0x2C, 0x8F, 0x2B, 0x56, 0x91} }, /* 03 (x600) */
+ { {0x28, 0xDE, 0x80, 0x83, 0x7F, 0xAA, 0x91} }, /* 04 (x768) */
+ { {0x28, 0x5A, 0x13, 0x87, 0xFF, 0x29, 0xA9} } /* 05 (x1024) */
+};
+
+/* CR06,CR07,CR10,CR11,CR15,CR16,SR0A */
+static const struct XGI_LVDSCRT1VDataStruct XGI_LVDSCRT11400x1050_1_V[] = {
+ { {0x6C, 0x1F, 0x60, 0x84, 0x5D, 0x6D, 0x10} }, /* 00 (x350) */
+ { {0x9E, 0x1F, 0x93, 0x86, 0x8F, 0x9F, 0x30} }, /* 01 (x400) */
+ { {0xEE, 0x1F, 0xE2, 0x86, 0xDF, 0xEF, 0x10} }, /* 02 (x480) */
+ { {0x66, 0xF0, 0x5A, 0x8e, 0x57, 0x67, 0xA0} }, /* 03 (x600) */
+ { {0x0E, 0xF5, 0x02, 0x86, 0xFF, 0x0F, 0x90} }, /* 04 (x768) */
+ { {0x0E, 0x5A, 0x02, 0x86, 0xFF, 0x0F, 0x89} }, /* 05 (x1024) */
+ { {0x28, 0x10, 0x1A, 0x80, 0x19, 0x29, 0x0F} } /* 06 (x1050) */
+};
+
+/* CR06,CR07,CR10,CR11,CR15,CR16,SR0A */
+static const struct XGI_LVDSCRT1VDataStruct XGI_LVDSCRT11400x1050_2_V[] = {
+ { {0x28, 0x92, 0xB6, 0x83, 0xB5, 0xCF, 0x81} }, /* 00 (x350) */
+ { {0x28, 0x92, 0xD5, 0x82, 0xD4, 0xEE, 0x81} }, /* 01 (x400) */
+ { {0x28, 0x92, 0xFD, 0x8A, 0xFC, 0x16, 0xB1} }, /* 02 (x480) */
+ { {0x28, 0xD4, 0x39, 0x86, 0x57, 0x29, 0x81} }, /* 03 (x600) */
+ { {0x28, 0xD4, 0x8D, 0x9A, 0xFF, 0x29, 0xA1} }, /* 04 (x768) */
+ { {0x28, 0x5A, 0x0D, 0x9A, 0xFF, 0x29, 0xA9} }, /* 05 (x1024) */
+ { {0x28, 0x10, 0x1A, 0x87, 0x19, 0x29, 0x8F} } /* 06 (x1050) */
+};
+
+/* CR06,CR07,CR10,CR11,CR15,CR16,SR0A+CR09(5->7) */
+static const struct XGI_LVDSCRT1VDataStruct XGI_LVDSCRT11600x1200_1_V[] = {
+ { {0xd4, 0x1F, 0x81, 0x84, 0x5D, 0xd5, 0x10} }, /* 00 (x350) */
+ { {0x06, 0x3e, 0xb3, 0x86, 0x8F, 0x07, 0x20} }, /* 01 (x400) */
+ { {0x56, 0xba, 0x03, 0x86, 0xDF, 0x57, 0x00} }, /* 02 (x480) */
+ { {0xce, 0xF0, 0x7b, 0x8e, 0x57, 0xcf, 0xa0} }, /* 03 (x600) */
+ { {0x76, 0xF5, 0x23, 0x86, 0xFF, 0x77, 0x90} }, /* 04 (x768) */
+ { {0x76, 0x5A, 0x23, 0x86, 0xFF, 0x77, 0x89} }, /* 05 (x1024) */
+ { {0x90, 0x10, 0x1A, 0x8E, 0x19, 0x91, 0x2F} }, /* 06 (x1050) */
+ { {0x26, 0x11, 0xd3, 0x86, 0xaF, 0x27, 0x3f} } /* 07 (x1200) */
+};
+
+/* CR00,CR02,CR03,CR04,CR05,SR0B,SR0C,SR0E */
+static const struct XGI_LVDSCRT1HDataStruct XGI_LVDSCRT11024x768_1_Hx75[] = {
+ { {0x4B, 0x27, 0x8F, 0x32, 0x1B, 0x00, 0x45, 0x00} },/* ; 00 (320x) */
+ { {0x4B, 0x27, 0x8F, 0x2B, 0x03, 0x00, 0x44, 0x00} },/* ; 01 (360x) */
+ { {0x55, 0x31, 0x99, 0x46, 0x1D, 0x00, 0x55, 0x00} },/* ; 02 (400x) */
+ { {0x63, 0x3F, 0x87, 0x4A, 0x93, 0x00, 0x01, 0x00} },/* ; 03 (512x) */
+ { {0x6F, 0x4F, 0x93, 0x54, 0x80, 0x00, 0x05, 0x00} },/* ; 04 (640x) */
+ { {0x6F, 0x4F, 0x93, 0x54, 0x80, 0x00, 0x05, 0x00} },/* ; 05 (720x) */
+ { {0x83, 0x63, 0x87, 0x68, 0x14, 0x00, 0x26, 0x00} },/* ; 06 (800x) */
+ { {0x9F, 0x7F, 0x83, 0x85, 0x91, 0x00, 0x02, 0x00} } /* ; 07 (1024x) */
+};
+
+/* CR06,CR07,CR10,CR11,CR15,CR16,SR0A+CR09(5->7) */
+static const struct XGI_LVDSCRT1VDataStruct XGI_LVDSCRT11024x768_1_Vx75[] = {
+ { {0x97, 0x1F, 0x60, 0x87, 0x5D, 0x83, 0x10} },/* ; 00 (x350) */
+ { {0xB4, 0x1F, 0x92, 0x89, 0x8F, 0xB5, 0x30} },/* ; 01 (x400) */
+ { {0xFE, 0x1F, 0xE0, 0x84, 0xDF, 0xFF, 0x10} },/* ; 02 (x480) */
+ { {0x76, 0xF0, 0x58, 0x8C, 0x57, 0x77, 0xA0} },/* ; 03 (x600) */
+ { {0x1E, 0xF5, 0x00, 0x83, 0xFF, 0x1F, 0x90} } /* ; 04 (x768) */
+};
+
+/* CR00,CR02,CR03,CR04,CR05,SR0B,SR0C,SR0E */
+static const struct XGI_LVDSCRT1HDataStruct XGI_LVDSCRT11024x768_2_Hx75[] = {
+ { {0x63, 0x27, 0x87, 0x3B, 0x8C, 0x00, 0x01, 0x00} },/* ; 00 (320x) */
+ { {0x63, 0x27, 0x87, 0x3B, 0x8C, 0x00, 0x01, 0x00} },/* ; 01 (360x) */
+ { {0x63, 0x31, 0x87, 0x3D, 0x8E, 0x00, 0x01, 0x00} },/* ; 02 (400x) */
+ { {0x63, 0x3F, 0x87, 0x45, 0x96, 0x00, 0x01, 0x00} },/* ; 03 (512x) */
+ { {0xA3, 0x4F, 0x87, 0x6E, 0x9F, 0x00, 0x06, 0x00} },/* ; 04 (640x) */
+ { {0xA3, 0x4F, 0x87, 0x6E, 0x9F, 0x00, 0x06, 0x00} },/* ; 05 (720x) */
+ { {0xA3, 0x63, 0x87, 0x78, 0x89, 0x00, 0x02, 0x00} },/* ; 06 (800x) */
+ { {0xA3, 0x7F, 0x87, 0x86, 0x97, 0x00, 0x02, 0x00} } /* ; 07 (1024x) */
+};
+
+/* CR06,CR07,CR10,CR11,CR15,CR16,SR0A */
+static const struct XGI_LVDSCRT1VDataStruct XGI_LVDSCRT11024x768_2_Vx75[] = {
+ { {0x24, 0xBB, 0x31, 0x87, 0x5D, 0x25, 0x30} },/* ; 00 (x350) */
+ { {0x24, 0xBB, 0x4A, 0x80, 0x8F, 0x25, 0x30} },/* ; 01 (x400) */
+ { {0x24, 0xBB, 0x72, 0x88, 0xDF, 0x25, 0x30} },/* ; 02 (x480) */
+ { {0x24, 0xF1, 0xAE, 0x84, 0x57, 0x25, 0xB0} },/* ; 03 (x600) */
+ { {0x24, 0xF5, 0x02, 0x88, 0xFF, 0x25, 0x90} } /* ; 04 (x768) */
+};
+
+/* CR00,CR02,CR03,CR04,CR05,SR0B,SR0C,SR0E */
+static const struct XGI_LVDSCRT1HDataStruct XGI_LVDSCRT11280x1024_1_Hx75[] = {
+ { {0x56, 0x27, 0x9A, 0x30, 0x1E, 0x00, 0x05, 0x00} },/* ; 00 (320x) */
+ { {0x56, 0x27, 0x9A, 0x30, 0x1E, 0x00, 0x05, 0x00} },/* ; 01 (360x) */
+ { {0x60, 0x31, 0x84, 0x3A, 0x88, 0x00, 0x01, 0x00} },/* ; 02 (400x) */
+ { {0x6E, 0x3F, 0x92, 0x48, 0x96, 0x00, 0x01, 0x00} },/* ; 03 (512x) */
+ { {0x7E, 0x4F, 0x82, 0x54, 0x06, 0x00, 0x06, 0x00} },/* ; 04 (640x) */
+ { {0x7E, 0x4F, 0x82, 0x54, 0x06, 0x00, 0x06, 0x00} },/* ; 05 (720x) */
+ { {0x92, 0x63, 0x96, 0x68, 0x1A, 0x00, 0x06, 0x00} },/* ; 06 (800x) */
+ { {0xAE, 0x7F, 0x92, 0x84, 0x96, 0x00, 0x02, 0x00} },/* ; 07 (1024x) */
+ { {0xCE, 0x9F, 0x92, 0xA5, 0x17, 0x00, 0x07, 0x00} } /* ; 08 (1280x) */
+};
+
+/* CR06,CR07,CR10,CR11,CR15,CR16,SR0A */
+static const struct XGI_LVDSCRT1VDataStruct XGI_LVDSCRT11280x1024_1_Vx75[] = {
+ { {0x86, 0xD1, 0xBC, 0x80, 0xBB, 0xE5, 0x00} },/* ; 00 (x350) */
+ { {0xB8, 0x1F, 0x90, 0x84, 0x8F, 0xB9, 0x30} },/* ; 01 (x400) */
+ { {0x08, 0x3E, 0xE0, 0x84, 0xDF, 0x09, 0x00} },/* ; 02 (x480) */
+ { {0x80, 0xF0, 0x58, 0x8C, 0x57, 0x81, 0xA0} },/* ; 03 (x600) */
+ { {0x28, 0xF5, 0x00, 0x84, 0xFF, 0x29, 0x90} },/* ; 04 (x768) */
+ { {0x28, 0x5A, 0x13, 0x87, 0xFF, 0x29, 0xA9} } /* ; 05 (x1024) */
+};
+/* CR00,CR02,CR03,CR04,CR05,SR0B,SR0C,SR0E */
+static const struct XGI_LVDSCRT1HDataStruct XGI_LVDSCRT11280x1024_2_Hx75[] = {
+ { {0x7E, 0x3B, 0x9A, 0x44, 0x12, 0x00, 0x01, 0x00} },/* ; 00 (320x) */
+ { {0x7E, 0x3B, 0x9A, 0x44, 0x12, 0x00, 0x01, 0x00} },/* ; 01 (360x) */
+ { {0x7E, 0x40, 0x84, 0x49, 0x91, 0x00, 0x01, 0x00} },/* ; 02 (400x) */
+ { {0x7E, 0x47, 0x93, 0x50, 0x9E, 0x00, 0x01, 0x00} },/* ; 03 (512x) */
+ { {0xCE, 0x77, 0x8A, 0x80, 0x8E, 0x00, 0x02, 0x00} },/* ; 04 (640x) */
+ { {0xCE, 0x77, 0x8A, 0x80, 0x8E, 0x00, 0x02, 0x00} },/* ; 05 (720x) */
+ { {0xCE, 0x81, 0x94, 0x8A, 0x98, 0x00, 0x02, 0x00} },/* ; 06 (800x) */
+ { {0xCE, 0x8F, 0x82, 0x98, 0x06, 0x00, 0x07, 0x00} },/* ; 07 (1024x) */
+ { {0xCE, 0x9F, 0x92, 0xA8, 0x16, 0x00, 0x07, 0x00} } /* ; 08 (1280x) */
+};
+
+/* CR06,CR07,CR10,CR11,CR15,CR16,SR0A */
+static const struct XGI_LVDSCRT1VDataStruct XGI_LVDSCRT11280x1024_2_Vx75[] = {
+ { {0x28, 0xD2, 0xAF, 0x83, 0xAE, 0xD8, 0xA1} },/* ; 00 (x350) */
+ { {0x28, 0xD2, 0xC8, 0x8C, 0xC7, 0xF2, 0x81} },/* ; 01 (x400) */
+ { {0x28, 0xD2, 0xF0, 0x84, 0xEF, 0x1A, 0xB1} },/* ; 02 (x480) */
+ { {0x28, 0xDE, 0x2C, 0x8F, 0x2B, 0x56, 0x91} },/* ; 03 (x600) */
+ { {0x28, 0xDE, 0x80, 0x83, 0x7F, 0xAA, 0x91} },/* ; 04 (x768) */
+ { {0x28, 0x5A, 0x13, 0x87, 0xFF, 0x29, 0xA9} } /* ; 05 (x1024) */
+};
+
+/*add for new UNIVGABIOS*/
+static const struct XGI330_LCDDataTablStruct XGI_LCDDataTable[] = {
+ {Panel_1024x768, 0x0019, 0x0001, XGI_ExtLCD1024x768Data },
+ {Panel_1024x768, 0x0019, 0x0000, XGI_StLCD1024x768Data },
+ {Panel_1024x768, 0x0018, 0x0010, XGI_CetLCD1024x768Data },
+ {Panel_1280x1024, 0x0019, 0x0001, XGI_ExtLCD1280x1024Data },
+ {Panel_1280x1024, 0x0019, 0x0000, XGI_StLCD1280x1024Data },
+ {Panel_1280x1024, 0x0018, 0x0010, XGI_CetLCD1280x1024Data },
+ {Panel_1400x1050, 0x0019, 0x0001, xgifb_lcd_1400x1050 },
+ {Panel_1400x1050, 0x0019, 0x0000, xgifb_lcd_1400x1050 },
+ {Panel_1400x1050, 0x0018, 0x0010, XGI_CetLCD1400x1050Data },
+ {Panel_1600x1200, 0x0019, 0x0001, XGI_ExtLCD1600x1200Data },
+ {Panel_1600x1200, 0x0019, 0x0000, XGI_StLCD1600x1200Data },
+ {PanelRef60Hz, 0x0008, 0x0008, XGI_NoScalingData },
+ {Panel_1024x768x75, 0x0019, 0x0001, XGI_ExtLCD1024x768x75Data },
+ {Panel_1024x768x75, 0x0019, 0x0000, XGI_ExtLCD1024x768x75Data },
+ {Panel_1024x768x75, 0x0018, 0x0010, XGI_CetLCD1024x768x75Data },
+ {Panel_1280x1024x75, 0x0019, 0x0001, xgifb_lcd_1280x1024x75 },
+ {Panel_1280x1024x75, 0x0019, 0x0000, xgifb_lcd_1280x1024x75 },
+ {Panel_1280x1024x75, 0x0018, 0x0010, XGI_CetLCD1280x1024x75Data },
+ {PanelRef75Hz, 0x0008, 0x0008, XGI_NoScalingDatax75 },
+ {0xFF, 0x0000, 0x0000, NULL } /* End of table */
+};
+
+static const struct XGI330_LCDDataTablStruct XGI_LCDDesDataTable[] = {
+ {Panel_1024x768, 0x0019, 0x0001, XGI_ExtLCDDes1024x768Data },
+ {Panel_1024x768, 0x0019, 0x0000, XGI_StLCDDes1024x768Data },
+ {Panel_1024x768, 0x0018, 0x0010, XGI_CetLCDDes1024x768Data },
+ {Panel_1280x1024, 0x0019, 0x0001, XGI_ExtLCDDes1280x1024Data },
+ {Panel_1280x1024, 0x0019, 0x0000, XGI_StLCDDes1280x1024Data },
+ {Panel_1280x1024, 0x0018, 0x0010, XGI_CetLCDDes1280x1024Data },
+ {Panel_1400x1050, 0x0019, 0x0001, xgifb_lcddes_1400x1050 },
+ {Panel_1400x1050, 0x0019, 0x0000, xgifb_lcddes_1400x1050 },
+ {Panel_1400x1050, 0x0418, 0x0010, XGI_CetLCDDes1400x1050Data },
+ {Panel_1400x1050, 0x0418, 0x0410, XGI_CetLCDDes1400x1050Data2 },
+ {Panel_1600x1200, 0x0019, 0x0001, XGI_ExtLCDDes1600x1200Data },
+ {Panel_1600x1200, 0x0019, 0x0000, XGI_StLCDDes1600x1200Data },
+ {PanelRef60Hz, 0x0008, 0x0008, XGI_NoScalingDesData },
+ {Panel_1024x768x75, 0x0019, 0x0001, xgifb_lcddes_1024x768x75 },
+ {Panel_1024x768x75, 0x0019, 0x0000, xgifb_lcddes_1024x768x75 },
+ {Panel_1024x768x75, 0x0018, 0x0010, XGI_CetLCDDes1024x768x75Data },
+ {Panel_1280x1024x75, 0x0019, 0x0001, xgifb_lcddes_1280x1024x75 },
+ {Panel_1280x1024x75, 0x0019, 0x0000, xgifb_lcddes_1280x1024x75 },
+ {Panel_1280x1024x75, 0x0018, 0x0010, XGI_CetLCDDes1280x1024x75Data },
+ {PanelRef75Hz, 0x0008, 0x0008, XGI_NoScalingDesDatax75 },
+ {0xFF, 0x0000, 0x0000, NULL }
+};
+
+static const struct XGI330_LCDDataTablStruct xgifb_lcddldes[] = {
+ {Panel_1024x768, 0x0019, 0x0001, XGI_ExtLCDDes1024x768Data },
+ {Panel_1024x768, 0x0019, 0x0000, XGI_StLCDDes1024x768Data },
+ {Panel_1024x768, 0x0018, 0x0010, XGI_CetLCDDes1024x768Data },
+ {Panel_1280x1024, 0x0019, 0x0001, XGI_ExtLCDDLDes1280x1024Data },
+ {Panel_1280x1024, 0x0019, 0x0000, XGI_StLCDDLDes1280x1024Data },
+ {Panel_1280x1024, 0x0018, 0x0010, XGI_CetLCDDLDes1280x1024Data },
+ {Panel_1400x1050, 0x0019, 0x0001, xgifb_lcddldes_1400x1050 },
+ {Panel_1400x1050, 0x0019, 0x0000, xgifb_lcddldes_1400x1050 },
+ {Panel_1400x1050, 0x0418, 0x0010, XGI_CetLCDDes1400x1050Data },
+ {Panel_1400x1050, 0x0418, 0x0410, XGI_CetLCDDes1400x1050Data2 },
+ {Panel_1600x1200, 0x0019, 0x0001, XGI_ExtLCDDLDes1600x1200Data },
+ {Panel_1600x1200, 0x0019, 0x0000, XGI_StLCDDLDes1600x1200Data },
+ {PanelRef60Hz, 0x0008, 0x0008, XGI_NoScalingDesData },
+ {Panel_1024x768x75, 0x0019, 0x0001, xgifb_lcddes_1024x768x75 },
+ {Panel_1024x768x75, 0x0019, 0x0000, xgifb_lcddes_1024x768x75 },
+ {Panel_1024x768x75, 0x0018, 0x0010, XGI_CetLCDDes1024x768x75Data },
+ {Panel_1280x1024x75, 0x0019, 0x0001, xgifb_lcddldes_1280x1024x75 },
+ {Panel_1280x1024x75, 0x0019, 0x0000, xgifb_lcddldes_1280x1024x75 },
+ {Panel_1280x1024x75, 0x0018, 0x0010, XGI_CetLCDDLDes1280x1024x75Data },
+ {PanelRef75Hz, 0x0008, 0x0008, XGI_NoScalingDesDatax75 },
+ {0xFF, 0x0000, 0x0000, NULL }
+};
+
+static const struct XGI330_LCDDataTablStruct xgifb_epllcd_crt1_h[] = {
+ {Panel_1024x768, 0x0018, 0x0000, XGI_LVDSCRT11024x768_1_H },
+ {Panel_1024x768, 0x0018, 0x0010, XGI_LVDSCRT11024x768_2_H },
+ {Panel_1280x1024, 0x0018, 0x0000, XGI_LVDSCRT11280x1024_1_H },
+ {Panel_1280x1024, 0x0018, 0x0010, XGI_LVDSCRT11280x1024_2_H },
+ {Panel_1400x1050, 0x0018, 0x0000, XGI_LVDSCRT11400x1050_1_H },
+ {Panel_1400x1050, 0x0018, 0x0010, XGI_LVDSCRT11400x1050_2_H },
+ {Panel_1600x1200, 0x0018, 0x0000, XGI_LVDSCRT11600x1200_1_H },
+ {Panel_1024x768x75, 0x0018, 0x0000, XGI_LVDSCRT11024x768_1_Hx75 },
+ {Panel_1024x768x75, 0x0018, 0x0010, XGI_LVDSCRT11024x768_2_Hx75 },
+ {Panel_1280x1024x75, 0x0018, 0x0000, XGI_LVDSCRT11280x1024_1_Hx75 },
+ {Panel_1280x1024x75, 0x0018, 0x0010, XGI_LVDSCRT11280x1024_2_Hx75 },
+ {0xFF, 0x0000, 0x0000, NULL }
+};
+
+static const struct XGI330_LCDDataTablStruct xgifb_epllcd_crt1_v[] = {
+ {Panel_1024x768, 0x0018, 0x0000, XGI_LVDSCRT11024x768_1_V },
+ {Panel_1024x768, 0x0018, 0x0010, XGI_LVDSCRT11024x768_2_V },
+ {Panel_1280x1024, 0x0018, 0x0000, XGI_LVDSCRT11280x1024_1_V },
+ {Panel_1280x1024, 0x0018, 0x0010, XGI_LVDSCRT11280x1024_2_V },
+ {Panel_1400x1050, 0x0018, 0x0000, XGI_LVDSCRT11400x1050_1_V },
+ {Panel_1400x1050, 0x0018, 0x0010, XGI_LVDSCRT11400x1050_2_V },
+ {Panel_1600x1200, 0x0018, 0x0000, XGI_LVDSCRT11600x1200_1_V },
+ {Panel_1024x768x75, 0x0018, 0x0000, XGI_LVDSCRT11024x768_1_Vx75 },
+ {Panel_1024x768x75, 0x0018, 0x0010, XGI_LVDSCRT11024x768_2_Vx75 },
+ {Panel_1280x1024x75, 0x0018, 0x0000, XGI_LVDSCRT11280x1024_1_Vx75 },
+ {Panel_1280x1024x75, 0x0018, 0x0010, XGI_LVDSCRT11280x1024_2_Vx75 },
+ {0xFF, 0x0000, 0x0000, NULL }
+};
+
+static const struct XGI330_LCDDataTablStruct XGI_EPLLCDDataPtr[] = {
+ {Panel_1024x768, 0x0018, 0x0000, XGI_LVDS1024x768Data_1 },
+ {Panel_1024x768, 0x0018, 0x0010, XGI_LVDS1024x768Data_2 },
+ {Panel_1280x1024, 0x0018, 0x0000, XGI_LVDS1280x1024Data_1 },
+ {Panel_1280x1024, 0x0018, 0x0010, XGI_LVDS1280x1024Data_2 },
+ {Panel_1400x1050, 0x0018, 0x0000, XGI_LVDS1400x1050Data_1 },
+ {Panel_1400x1050, 0x0018, 0x0010, XGI_LVDS1400x1050Data_2 },
+ {Panel_1600x1200, 0x0018, 0x0000, XGI_LVDS1600x1200Data_1 },
+ {PanelRef60Hz, 0x0008, 0x0008, XGI_LVDSNoScalingData },
+ {Panel_1024x768x75, 0x0018, 0x0000, XGI_LVDS1024x768Data_1x75 },
+ {Panel_1024x768x75, 0x0018, 0x0010, XGI_LVDS1024x768Data_2x75 },
+ {Panel_1280x1024x75, 0x0018, 0x0000, XGI_LVDS1280x1024Data_1x75 },
+ {Panel_1280x1024x75, 0x0018, 0x0010, XGI_LVDS1280x1024Data_2x75 },
+ {PanelRef75Hz, 0x0008, 0x0008, XGI_LVDSNoScalingDatax75 },
+ {0xFF, 0x0000, 0x0000, NULL }
+};
+
+static const struct XGI330_LCDDataTablStruct XGI_EPLLCDDesDataPtr[] = {
+ {Panel_1024x768, 0x0018, 0x0000, XGI_LVDS1024x768Des_1 },
+ {Panel_1024x768, 0x0618, 0x0410, XGI_LVDS1024x768Des_3 },
+ {Panel_1024x768, 0x0018, 0x0010, XGI_LVDS1024x768Des_2 },
+ {Panel_1280x1024, 0x0018, 0x0000, XGI_LVDS1280x1024Des_1 },
+ {Panel_1280x1024, 0x0018, 0x0010, XGI_LVDS1280x1024Des_2 },
+ {Panel_1400x1050, 0x0018, 0x0000, XGI_LVDS1400x1050Des_1 },
+ {Panel_1400x1050, 0x0018, 0x0010, XGI_LVDS1400x1050Des_2 },
+ {Panel_1600x1200, 0x0018, 0x0000, XGI_LVDS1600x1200Des_1 },
+ {PanelRef60Hz, 0x0008, 0x0008, XGI_LVDSNoScalingDesData },
+ {Panel_1024x768x75, 0x0018, 0x0000, XGI_LVDS1024x768Des_1x75 },
+ {Panel_1024x768x75, 0x0618, 0x0410, XGI_LVDS1024x768Des_3x75 },
+ {Panel_1024x768x75, 0x0018, 0x0010, XGI_LVDS1024x768Des_2x75 },
+ {Panel_1280x1024x75, 0x0018, 0x0000, XGI_LVDS1280x1024Des_1x75 },
+ {Panel_1280x1024x75, 0x0018, 0x0010, XGI_LVDS1280x1024Des_2x75 },
+ {PanelRef75Hz, 0x0008, 0x0008, XGI_LVDSNoScalingDesDatax75 },
+ {0xFF, 0x0000, 0x0000, NULL }
+};
+
+static const struct XGI330_TVDataTablStruct XGI_TVDataTable[] = {
+ {0x09E1, 0x0001, XGI_ExtPALData},
+ {0x09E1, 0x0000, XGI_ExtNTSCData},
+ {0x09E1, 0x0801, XGI_StPALData},
+ {0x09E1, 0x0800, XGI_StNTSCData},
+ {0x49E0, 0x0100, XGI_ExtHiTVData},
+ {0x49E0, 0x4100, XGI_St2HiTVData},
+ {0x49E0, 0x4900, XGI_St1HiTVData},
+ {0x09E0, 0x0020, XGI_ExtYPbPr525iData},
+ {0x09E0, 0x0040, XGI_ExtYPbPr525pData},
+ {0x09E0, 0x0080, XGI_ExtYPbPr750pData},
+ {0x09E0, 0x0820, XGI_StYPbPr525iData},
+ {0x09E0, 0x0840, XGI_StYPbPr525pData},
+ {0x09E0, 0x0880, XGI_StYPbPr750pData},
+ {0xffff, 0x0000, XGI_ExtNTSCData},
+};
+
+/* Dual link only */
+static const struct XGI330_LCDCapStruct XGI_LCDDLCapList[] = {
+/* LCDCap1024x768 */
+ {Panel_1024x768, DefaultLCDCap, 0x88, 0x06, VCLK65_315,
+ 0x6C, 0xC3, 0x35, 0x62,
+ 0x0A, 0xC0, 0x28, 0x10},
+/* LCDCap1280x1024 */
+ {Panel_1280x1024, XGI_LCDDualLink+DefaultLCDCap,
+ 0x70, 0x03, VCLK108_2_315,
+ 0x70, 0x44, 0xF8, 0x2F,
+ 0x0A, 0xC0, 0x30, 0x10},
+/* LCDCap1400x1050 */
+ {Panel_1400x1050, XGI_LCDDualLink+DefaultLCDCap,
+ 0x70, 0x03, VCLK108_2_315,
+ 0x70, 0x44, 0xF8, 0x2F,
+ 0x0A, 0xC0, 0x30, 0x10},
+/* LCDCap1600x1200 */
+ {Panel_1600x1200, XGI_LCDDualLink+DefaultLCDCap,
+ 0xC0, 0x03, VCLK162,
+ 0x43, 0x22, 0x70, 0x24,
+ 0x0A, 0xC0, 0x30, 0x10},
+/* LCDCap1024x768x75 */
+ {Panel_1024x768x75, DefaultLCDCap, 0x60, 0, VCLK78_75,
+ 0x2B, 0x61, 0x2B, 0x61,
+ 0x0A, 0xC0, 0x28, 0x10},
+/* LCDCap1280x1024x75 */
+ {Panel_1280x1024x75, XGI_LCDDualLink+DefaultLCDCap,
+ 0x90, 0x03, VCLK135_5,
+ 0x54, 0x42, 0x4A, 0x61,
+ 0x0A, 0xC0, 0x30, 0x10},
+/* LCDCapDefault */
+ {0xFF, DefaultLCDCap, 0x88, 0x06, VCLK65_315,
+ 0x6C, 0xC3, 0x35, 0x62,
+ 0x0A, 0xC0, 0x28, 0x10}
+};
+
+static const struct XGI330_LCDCapStruct XGI_LCDCapList[] = {
+/* LCDCap1024x768 */
+ {Panel_1024x768, DefaultLCDCap, 0x88, 0x06, VCLK65_315,
+ 0x6C, 0xC3, 0x35, 0x62,
+ 0x0A, 0xC0, 0x28, 0x10},
+/* LCDCap1280x1024 */
+ {Panel_1280x1024, DefaultLCDCap,
+ 0x70, 0x03, VCLK108_2_315,
+ 0x70, 0x44, 0xF8, 0x2F,
+ 0x0A, 0xC0, 0x30, 0x10},
+/* LCDCap1400x1050 */
+ {Panel_1400x1050, DefaultLCDCap,
+ 0x70, 0x03, VCLK108_2_315,
+ 0x70, 0x44, 0xF8, 0x2F,
+ 0x0A, 0xC0, 0x30, 0x10},
+/* LCDCap1600x1200 */
+ {Panel_1600x1200, DefaultLCDCap,
+ 0xC0, 0x03, VCLK162,
+ 0x5A, 0x23, 0x5A, 0x23,
+ 0x0A, 0xC0, 0x30, 0x10},
+/* LCDCap1024x768x75 */
+ {Panel_1024x768x75, DefaultLCDCap, 0x60, 0, VCLK78_75,
+ 0x2B, 0x61, 0x2B, 0x61,
+ 0x0A, 0xC0, 0x28, 0x10},
+/* LCDCap1280x1024x75 */
+ {Panel_1280x1024x75, DefaultLCDCap,
+ 0x90, 0x03, VCLK135_5,
+ 0x54, 0x42, 0x4A, 0x61,
+ 0x0A, 0xC0, 0x30, 0x10},
+/* LCDCapDefault */
+ {0xFF, DefaultLCDCap, 0x88, 0x06, VCLK65_315,
+ 0x6C, 0xC3, 0x35, 0x62,
+ 0x0A, 0xC0, 0x28, 0x10}
+};
+
+const struct XGI_Ext2Struct XGI330_RefIndex[] = {
+ {Mode32Bpp + SupportAllCRT2 + SyncPN, RES320x200, VCLK25_175,
+ 0x00, 0x10, 0x59, 320, 200},/* 00 */
+ {Mode32Bpp + SupportAllCRT2 + SyncPN, RES320x200, VCLK25_175,
+ 0x00, 0x10, 0x00, 320, 400},/* 01 */
+ {Mode32Bpp + SupportAllCRT2 + SyncNN, RES320x240, VCLK25_175,
+ 0x04, 0x20, 0x50, 320, 240},/* 02 */
+ {Mode32Bpp + SupportAllCRT2 + SyncPP, RES400x300, VCLK40,
+ 0x05, 0x32, 0x51, 400, 300},/* 03 */
+ {Mode32Bpp + NoSupportTV + SyncNN + SupportTV1024, RES512x384,
+ VCLK65_315, 0x06, 0x43, 0x52, 512, 384},/* 04 */
+ {Mode32Bpp + SupportAllCRT2 + SyncPN, RES640x400, VCLK25_175,
+ 0x00, 0x14, 0x2f, 640, 400},/* 05 */
+ {Mode32Bpp + SupportAllCRT2 + SyncNN, RES640x480x60, VCLK25_175,
+ 0x04, 0x24, 0x2e, 640, 480},/* 06 640x480x60Hz (LCD 640x480x60z) */
+ {Mode32Bpp + NoSupportHiVisionTV + SyncNN, RES640x480x72, VCLK31_5,
+ 0x04, 0x24, 0x2e, 640, 480},/* 07 640x480x72Hz (LCD 640x480x70Hz) */
+ {Mode32Bpp + NoSupportHiVisionTV + SyncNN, RES640x480x75, VCLK31_5,
+ 0x47, 0x24, 0x2e, 640, 480},/* 08 640x480x75Hz (LCD 640x480x75Hz) */
+ {Mode32Bpp + SupportRAMDAC2 + SyncNN, RES640x480x85, VCLK36,
+ 0x8A, 0x24, 0x2e, 640, 480},/* 09 640x480x85Hz */
+ {Mode32Bpp + SupportRAMDAC2 + SyncPN, RES640x480x100, VCLK43_163,
+ 0x00, 0x24, 0x2e, 640, 480},/* 0a 640x480x100Hz */
+ {Mode32Bpp + SupportRAMDAC2 + SyncPN, RES640x480x120, VCLK52_406,
+ 0x00, 0x24, 0x2e, 640, 480},/* 0b 640x480x120Hz */
+ {Mode32Bpp + SupportRAMDAC2 + SyncPN, RES640x480x160, VCLK72_852,
+ 0x00, 0x24, 0x2e, 640, 480},/* 0c 640x480x160Hz */
+ {Mode32Bpp + SupportRAMDAC2 + SyncNN, RES640x480x200, VCLK86_6,
+ 0x00, 0x24, 0x2e, 640, 480},/* 0d 640x480x200Hz */
+ {Mode32Bpp + NoSupportLCD + SyncPP, RES800x600x56, VCLK36,
+ 0x05, 0x36, 0x6a, 800, 600},/* 0e 800x600x56Hz */
+ {Mode32Bpp + NoSupportTV + SyncPP, RES800x600x60, VCLK40,
+ 0x05, 0x36, 0x6a, 800, 600},/* 0f 800x600x60Hz (LCD 800x600x60Hz) */
+ {Mode32Bpp + NoSupportHiVisionTV + SyncPP, RES800x600x72, VCLK50,
+ 0x48, 0x36, 0x6a, 800, 600},/* 10 800x600x72Hz (LCD 800x600x70Hz) */
+ {Mode32Bpp + NoSupportHiVisionTV + SyncPP, RES800x600x75, VCLK49_5,
+ 0x8B, 0x36, 0x6a, 800, 600},/* 11 800x600x75Hz (LCD 800x600x75Hz) */
+ {Mode32Bpp + SupportRAMDAC2 + SyncPP, RES800x600x85, VCLK56_25,
+ 0x00, 0x36, 0x6a, 800, 600},/* 12 800x600x85Hz */
+ {Mode32Bpp + SupportRAMDAC2 + SyncPN, RES800x600x100, VCLK68_179,
+ 0x00, 0x36, 0x6a, 800, 600},/* 13 800x600x100Hz */
+ {Mode32Bpp + SupportRAMDAC2 + SyncPN, RES800x600x120, VCLK83_95,
+ 0x00, 0x36, 0x6a, 800, 600},/* 14 800x600x120Hz */
+ {Mode32Bpp + SupportRAMDAC2 + SyncPN, RES800x600x160, VCLK116_406,
+ 0x00, 0x36, 0x6a, 800, 600},/* 15 800x600x160Hz */
+ {Mode32Bpp + InterlaceMode + SyncPP, RES1024x768x43, VCLK44_9,
+ 0x00, 0x47, 0x37, 1024, 768},/* 16 1024x768x43Hz */
+ /* 17 1024x768x60Hz (LCD 1024x768x60Hz) */
+ {Mode32Bpp + NoSupportTV + SyncNN + SupportTV1024, RES1024x768x60,
+ VCLK65_315, 0x06, 0x47, 0x37, 1024, 768},
+ {Mode32Bpp + NoSupportHiVisionTV + SyncNN, RES1024x768x70, VCLK75,
+ 0x49, 0x47, 0x37, 1024, 768},/* 18 1024x768x70Hz (LCD 1024x768x70Hz) */
+ {Mode32Bpp + NoSupportHiVisionTV + SyncPP, RES1024x768x75, VCLK78_75,
+ 0x00, 0x47, 0x37, 1024, 768},/* 19 1024x768x75Hz (LCD 1024x768x75Hz) */
+ {Mode32Bpp + SupportRAMDAC2 + SyncPP, RES1024x768x85, VCLK94_5,
+ 0x8C, 0x47, 0x37, 1024, 768},/* 1a 1024x768x85Hz */
+ {Mode32Bpp + SupportRAMDAC2 + SyncPN, RES1024x768x100, VCLK113_309,
+ 0x00, 0x47, 0x37, 1024, 768},/* 1b 1024x768x100Hz */
+ {Mode32Bpp + SupportRAMDAC2 + SyncPN, RES1024x768x120, VCLK139_054,
+ 0x00, 0x47, 0x37, 1024, 768},/* 1c 1024x768x120Hz */
+ {Mode32Bpp + SupportLCD + SyncPP, RES1280x960x60, VCLK108_2_315,
+ 0x08, 0x58, 0x7b, 1280, 960},/* 1d 1280x960x60Hz */
+ {Mode32Bpp + InterlaceMode + SyncPP, RES1280x1024x43, VCLK78_75,
+ 0x00, 0x58, 0x3a, 1280, 1024},/* 1e 1280x1024x43Hz */
+ {Mode32Bpp + NoSupportTV + SyncPP, RES1280x1024x60, VCLK108_2_315,
+ 0x07, 0x58, 0x3a, 1280, 1024},/*1f 1280x1024x60Hz (LCD 1280x1024x60Hz)*/
+ {Mode32Bpp + NoSupportTV + SyncPP, RES1280x1024x75, VCLK135_5,
+ 0x00, 0x58, 0x3a, 1280, 1024},/*20 1280x1024x75Hz (LCD 1280x1024x75Hz)*/
+ {Mode32Bpp + SyncPP, RES1280x1024x85, VCLK157_5,
+ 0x00, 0x58, 0x3a, 1280, 1024},/* 21 1280x1024x85Hz */
+ /* 22 1600x1200x60Hz */
+ {Mode32Bpp + SupportLCD + SyncPP + SupportCRT2in301C,
+ RES1600x1200x60, VCLK162, 0x09, 0x7A, 0x3c, 1600, 1200},
+ {Mode32Bpp + SyncPP + SupportCRT2in301C, RES1600x1200x65, VCLK175,
+ 0x00, 0x69, 0x3c, 1600, 1200},/* 23 1600x1200x65Hz */
+ {Mode32Bpp + SyncPP + SupportCRT2in301C, RES1600x1200x70, VCLK189,
+ 0x00, 0x69, 0x3c, 1600, 1200},/* 24 1600x1200x70Hz */
+ {Mode32Bpp + SyncPP + SupportCRT2in301C, RES1600x1200x75, VCLK202_5,
+ 0x00, 0x69, 0x3c, 1600, 1200},/* 25 1600x1200x75Hz */
+ {Mode32Bpp + SyncPP, RES1600x1200x85, VCLK229_5,
+ 0x00, 0x69, 0x3c, 1600, 1200},/* 26 1600x1200x85Hz */
+ {Mode32Bpp + SyncPP, RES1600x1200x100, VCLK269_655,
+ 0x00, 0x69, 0x3c, 1600, 1200},/* 27 1600x1200x100Hz */
+ {Mode32Bpp + SyncPP, RES1600x1200x120, VCLK323_586,
+ 0x00, 0x69, 0x3c, 1600, 1200},/* 28 1600x1200x120Hz */
+ {Mode32Bpp + SupportLCD + SyncNP, RES1920x1440x60, VCLK234,
+ 0x00, 0x00, 0x68, 1920, 1440},/* 29 1920x1440x60Hz */
+ {Mode32Bpp + SyncPN, RES1920x1440x65, VCLK254_817,
+ 0x00, 0x00, 0x68, 1920, 1440},/* 2a 1920x1440x65Hz */
+ {Mode32Bpp + SyncPN, RES1920x1440x70, VCLK277_015,
+ 0x00, 0x00, 0x68, 1920, 1440},/* 2b 1920x1440x70Hz */
+ {Mode32Bpp + SyncPN, RES1920x1440x75, VCLK291_132,
+ 0x00, 0x00, 0x68, 1920, 1440},/* 2c 1920x1440x75Hz */
+ {Mode32Bpp + SyncPN, RES1920x1440x85, VCLK330_615,
+ 0x00, 0x00, 0x68, 1920, 1440},/* 2d 1920x1440x85Hz */
+ {Mode16Bpp + SyncPN, RES1920x1440x100, VCLK388_631,
+ 0x00, 0x00, 0x68, 1920, 1440},/* 2e 1920x1440x100Hz */
+ {Mode32Bpp + SupportLCD + SyncPN, RES2048x1536x60, VCLK266_952,
+ 0x00, 0x00, 0x6c, 2048, 1536},/* 2f 2048x1536x60Hz */
+ {Mode32Bpp + SyncPN, RES2048x1536x65, VCLK291_766,
+ 0x00, 0x00, 0x6c, 2048, 1536},/* 30 2048x1536x65Hz */
+ {Mode32Bpp + SyncPN, RES2048x1536x70, VCLK315_195,
+ 0x00, 0x00, 0x6c, 2048, 1536},/* 31 2048x1536x70Hz */
+ {Mode32Bpp + SyncPN, RES2048x1536x75, VCLK340_477,
+ 0x00, 0x00, 0x6c, 2048, 1536},/* 32 2048x1536x75Hz */
+ {Mode16Bpp + SyncPN, RES2048x1536x85, VCLK375_847,
+ 0x00, 0x00, 0x6c, 2048, 1536},/* 33 2048x1536x85Hz */
+ {Mode32Bpp + SupportHiVision + SupportRAMDAC2 +
+ SyncPP + SupportYPbPr750p, RES800x480x60, VCLK39_77,
+ 0x08, 0x00, 0x70, 800, 480},/* 34 800x480x60Hz */
+ {Mode32Bpp + SupportRAMDAC2 + SyncPP, RES800x480x75, VCLK49_5,
+ 0x08, 0x00, 0x70, 800, 480},/* 35 800x480x75Hz */
+ {Mode32Bpp + SupportRAMDAC2 + SyncPP, RES800x480x85, VCLK56_25,
+ 0x08, 0x00, 0x70, 800, 480},/* 36 800x480x85Hz */
+ {Mode32Bpp + SupportHiVision + SupportRAMDAC2 +
+ SyncPP + SupportYPbPr750p, RES1024x576x60, VCLK65_315,
+ 0x09, 0x00, 0x71, 1024, 576},/* 37 1024x576x60Hz */
+ {Mode32Bpp + SupportRAMDAC2 + SyncPP, RES1024x576x75, VCLK78_75,
+ 0x09, 0x00, 0x71, 1024, 576},/* 38 1024x576x75Hz */
+ {Mode32Bpp + SupportRAMDAC2 + SyncPP, RES1024x576x85, VCLK94_5,
+ 0x09, 0x00, 0x71, 1024, 576},/* 39 1024x576x85Hz */
+ {Mode32Bpp + SupportHiVision + SupportRAMDAC2 +
+ SyncPP + SupportYPbPr750p, RES1280x720x60, VCLK108_2_315,
+ 0x0A, 0x00, 0x75, 1280, 720},/* 3a 1280x720x60Hz*/
+ {Mode32Bpp + SupportRAMDAC2 + SyncPP, RES1280x720x75, VCLK135_5,
+ 0x0A, 0x00, 0x75, 1280, 720},/* 3b 1280x720x75Hz */
+ {Mode32Bpp + SupportRAMDAC2 + SyncPP, RES1280x720x85, VCLK157_5,
+ 0x0A, 0x00, 0x75, 1280, 720},/* 3c 1280x720x85Hz */
+ {Mode32Bpp + SupportTV + SyncNN, RES720x480x60, VCLK28_322,
+ 0x06, 0x00, 0x31, 720, 480},/* 3d 720x480x60Hz */
+ {Mode32Bpp + SupportTV + SyncPP, RES720x576x56, VCLK36,
+ 0x06, 0x00, 0x32, 720, 576},/* 3e 720x576x56Hz */
+ {Mode32Bpp + InterlaceMode + NoSupportLCD + SyncPP, RES856x480x79I,
+ VCLK35_2, 0x00, 0x00, 0x00, 856, 480},/* 3f 856x480x79I */
+ {Mode32Bpp + NoSupportLCD + SyncNN, RES856x480x60, VCLK35_2,
+ 0x00, 0x00, 0x00, 856, 480},/* 40 856x480x60Hz */
+ {Mode32Bpp + NoSupportHiVisionTV + SyncPP, RES1280x768x60,
+ VCLK79_411, 0x08, 0x48, 0x23, 1280, 768},/* 41 1280x768x60Hz */
+ {Mode32Bpp + NoSupportHiVisionTV + SyncPP, RES1400x1050x60,
+ VCLK122_61, 0x08, 0x69, 0x26, 1400, 1050},/* 42 1400x1050x60Hz */
+ {Mode32Bpp + SupportRAMDAC2 + SyncPP, RES1152x864x60, VCLK80_350,
+ 0x37, 0x00, 0x20, 1152, 864},/* 43 1152x864x60Hz */
+ {Mode32Bpp + SupportRAMDAC2 + SyncPP, RES1152x864x75, VCLK107_385,
+ 0x37, 0x00, 0x20, 1152, 864},/* 44 1152x864x75Hz */
+ {Mode32Bpp + SupportLCD + SupportRAMDAC2 + SyncPP, RES1280x960x75,
+ VCLK125_999, 0x3A, 0x88, 0x7b, 1280, 960},/* 45 1280x960x75Hz */
+ {Mode32Bpp + SupportLCD + SupportRAMDAC2 + SyncPP, RES1280x960x85,
+ VCLK148_5, 0x0A, 0x88, 0x7b, 1280, 960},/* 46 1280x960x85Hz */
+ {Mode32Bpp + SupportLCD + SupportRAMDAC2 + SyncPP, RES1280x960x120,
+ VCLK217_325, 0x3A, 0x88, 0x7b, 1280, 960},/* 47 1280x960x120Hz */
+ {Mode32Bpp + SupportRAMDAC2 + SyncPN, RES1024x768x160, VCLK139_054,
+ 0x30, 0x47, 0x37, 1024, 768},/* 48 1024x768x160Hz */
+};
+
+static const unsigned char XGI330_ScreenOffset[] = {
+ 0x14, 0x19, 0x20, 0x28, 0x32, 0x40,
+ 0x50, 0x64, 0x78, 0x80, 0x2d, 0x35,
+ 0x57, 0x48
+};
+
+static const struct SiS_ModeResInfo_S XGI330_ModeResInfo[] = {
+ { 320, 200, 8, 8},
+ { 320, 240, 8, 8},
+ { 320, 400, 8, 8},
+ { 400, 300, 8, 8},
+ { 512, 384, 8, 8},
+ { 640, 400, 8, 16},
+ { 640, 480, 8, 16},
+ { 800, 600, 8, 16},
+ {1024, 768, 8, 16},
+ {1280, 1024, 8, 16},
+ {1600, 1200, 8, 16},
+ {1920, 1440, 8, 16},
+ {2048, 1536, 8, 16},
+ { 720, 480, 8, 16},
+ { 720, 576, 8, 16},
+ {1280, 960, 8, 16},
+ { 800, 480, 8, 16},
+ {1024, 576, 8, 16},
+ {1280, 720, 8, 16},
+ { 856, 480, 8, 16},
+ {1280, 768, 8, 16},
+ {1400, 1050, 8, 16},
+ {1152, 864, 8, 16}
+};
+
+const struct SiS_VCLKData XGI_VCLKData[] = {
+ /* SR2B,SR2C,SR2D */
+ {0x1B, 0xE1, 25}, /* 00 (25.175MHz) */
+ {0x4E, 0xE4, 28}, /* 01 (28.322MHz) */
+ {0x57, 0xE4, 31}, /* 02 (31.500MHz) */
+ {0xC3, 0xC8, 36}, /* 03 (36.000MHz) */
+ {0x42, 0xE2, 40}, /* 04 (40.000MHz) */
+ {0xFE, 0xCD, 43}, /* 05 (43.163MHz) */
+ {0x5D, 0xC4, 44}, /* 06 (44.900MHz) */
+ {0x52, 0xE2, 49}, /* 07 (49.500MHz) */
+ {0x53, 0xE2, 50}, /* 08 (50.000MHz) */
+ {0x74, 0x67, 52}, /* 09 (52.406MHz) */
+ {0x6D, 0x66, 56}, /* 0A (56.250MHz) */
+ {0x6C, 0xC3, 65}, /* 0B (65.000MHz) */
+ {0x46, 0x44, 67}, /* 0C (67.765MHz) */
+ {0xB1, 0x46, 68}, /* 0D (68.179MHz) */
+ {0xD3, 0x4A, 72}, /* 0E (72.852MHz) */
+ {0x29, 0x61, 75}, /* 0F (75.000MHz) */
+ {0x6E, 0x46, 76}, /* 10 (75.800MHz) */
+ {0x2B, 0x61, 78}, /* 11 (78.750MHz) */
+ {0x31, 0x42, 79}, /* 12 (79.411MHz) */
+ {0xAB, 0x44, 83}, /* 13 (83.950MHz) */
+ {0x46, 0x25, 84}, /* 14 (84.800MHz) */
+ {0x78, 0x29, 86}, /* 15 (86.600MHz) */
+ {0x62, 0x44, 94}, /* 16 (94.500MHz) */
+ {0x2B, 0x41, 104}, /* 17 (104.998MHz) */
+ {0x3A, 0x23, 105}, /* 18 (105.882MHz) */
+ {0x70, 0x44, 108}, /* 19 (107.862MHz) */
+ {0x3C, 0x23, 109}, /* 1A (109.175MHz) */
+ {0x5E, 0x43, 113}, /* 1B (113.309MHz) */
+ {0xBC, 0x44, 116}, /* 1C (116.406MHz) */
+ {0xE0, 0x46, 132}, /* 1D (132.258MHz) */
+ {0x54, 0x42, 135}, /* 1E (135.500MHz) */
+ {0x9C, 0x22, 139}, /* 1F (139.275MHz) */
+ {0x41, 0x22, 157}, /* 20 (157.500MHz) */
+ {0x70, 0x24, 162}, /* 21 (161.793MHz) */
+ {0x30, 0x21, 175}, /* 22 (175.000MHz) */
+ {0x4E, 0x22, 189}, /* 23 (188.520MHz) */
+ {0xDE, 0x26, 194}, /* 24 (194.400MHz) */
+ {0x62, 0x06, 202}, /* 25 (202.500MHz) */
+ {0x3F, 0x03, 229}, /* 26 (229.500MHz) */
+ {0xB8, 0x06, 234}, /* 27 (233.178MHz) */
+ {0x34, 0x02, 253}, /* 28 (252.699MHz) */
+ {0x58, 0x04, 255}, /* 29 (254.817MHz) */
+ {0x24, 0x01, 265}, /* 2A (265.728MHz) */
+ {0x9B, 0x02, 267}, /* 2B (266.952MHz) */
+ {0x70, 0x05, 270}, /* 2C (269.65567MHz) */
+ {0x25, 0x01, 272}, /* 2D (272.04199MHz) */
+ {0x9C, 0x02, 277}, /* 2E (277.015MHz) */
+ {0x27, 0x01, 286}, /* 2F (286.359985MHz) */
+ {0xB3, 0x04, 291}, /* 30 (291.13266MHz) */
+ {0xBC, 0x05, 292}, /* 31 (291.766MHz) */
+ {0xF6, 0x0A, 310}, /* 32 (309.789459MHz) */
+ {0x95, 0x01, 315}, /* 33 (315.195MHz) */
+ {0xF0, 0x09, 324}, /* 34 (323.586792MHz) */
+ {0xFE, 0x0A, 331}, /* 35 (330.615631MHz) */
+ {0xF3, 0x09, 332}, /* 36 (332.177612MHz) */
+ {0x5E, 0x03, 340}, /* 37 (340.477MHz) */
+ {0xE8, 0x07, 376}, /* 38 (375.847504MHz) */
+ {0xDE, 0x06, 389}, /* 39 (388.631439MHz) */
+ {0x52, 0x2A, 54}, /* 3A (54.000MHz) */
+ {0x52, 0x6A, 27}, /* 3B (27.000MHz) */
+ {0x62, 0x24, 70}, /* 3C (70.874991MHz) */
+ {0x62, 0x64, 70}, /* 3D (70.1048912MHz) */
+ {0xA8, 0x4C, 30}, /* 3E (30.1048912MHz) */
+ {0x20, 0x26, 33}, /* 3F (33.7499957MHz) */
+ {0x31, 0xc2, 39}, /* 40 (39.77MHz) */
+ {0x11, 0x21, 30}, /* 41 (30MHz) }// NTSC 1024X768 */
+ {0x2E, 0x48, 25}, /* 42 (25.175MHz) }// ScaleLCD */
+ {0x24, 0x46, 25}, /* 43 (25.175MHz) */
+ {0x26, 0x64, 28}, /* 44 (28.322MHz) */
+ {0x37, 0x64, 40}, /* 45 (40.000MHz) */
+ {0xA1, 0x42, 108}, /* 46 (95.000MHz) }// QVGA */
+ {0x37, 0x61, 100}, /* 47 (100.00MHz) */
+ {0x78, 0x27, 108}, /* 48 (108.200MHz) */
+ {0xBF, 0xC8, 35}, /* 49 (35.2MHz) */
+ {0x66, 0x43, 123}, /* 4A (122.61Mhz) */
+ {0x2C, 0x61, 80}, /* 4B (80.350Mhz) */
+ {0x3B, 0x61, 108}, /* 4C (107.385Mhz) */
+ {0x69, 0x61, 191}, /* 4D (190.96MHz ) */
+ {0x4F, 0x22, 192}, /* 4E (192.069MHz) */
+ {0x28, 0x26, 322}, /* 4F (322.273MHz) */
+ {0x5C, 0x6B, 27}, /* 50 (27.74HMz) */
+ {0x57, 0x24, 126}, /* 51 (125.999MHz) */
+ {0x5C, 0x42, 148}, /* 52 (148.5MHz) */
+ {0x42, 0x61, 120}, /* 53 (120.839MHz) */
+ {0x62, 0x61, 178}, /* 54 (178.992MHz) */
+ {0x59, 0x22, 217}, /* 55 (217.325MHz) */
+ {0x29, 0x01, 300}, /* 56 (299.505Mhz) */
+ {0x52, 0x63, 74}, /* 57 (74.25MHz) */
+ {0xFF, 0x00, 0} /* End mark */
+};
+
+static const struct SiS_VBVCLKData XGI_VBVCLKData[] = {
+ {0x1B, 0xE1, 25}, /* 00 (25.175MHz) */
+ {0x4E, 0xE4, 28}, /* 01 (28.322MHz) */
+ {0x57, 0xE4, 31}, /* 02 (31.500MHz) */
+ {0xC3, 0xC8, 36}, /* 03 (36.000MHz) */
+ {0x42, 0x47, 40}, /* 04 (40.000MHz) */
+ {0xFE, 0xCD, 43}, /* 05 (43.163MHz) */
+ {0x5D, 0xC4, 44}, /* 06 (44.900MHz) */
+ {0x52, 0x47, 49}, /* 07 (49.500MHz) */
+ {0x53, 0x47, 50}, /* 08 (50.000MHz) */
+ {0x74, 0x67, 52}, /* 09 (52.406MHz) */
+ {0x6D, 0x66, 56}, /* 0A (56.250MHz) */
+ {0x35, 0x62, 65}, /* 0B (65.000MHz) */
+ {0x46, 0x44, 67}, /* 0C (67.765MHz) */
+ {0xB1, 0x46, 68}, /* 0D (68.179MHz) */
+ {0xD3, 0x4A, 72}, /* 0E (72.852MHz) */
+ {0x29, 0x61, 75}, /* 0F (75.000MHz) */
+ {0x6D, 0x46, 75}, /* 10 (75.800MHz) */
+ {0x41, 0x43, 78}, /* 11 (78.750MHz) */
+ {0x31, 0x42, 79}, /* 12 (79.411MHz) */
+ {0xAB, 0x44, 83}, /* 13 (83.950MHz) */
+ {0x46, 0x25, 84}, /* 14 (84.800MHz) */
+ {0x78, 0x29, 86}, /* 15 (86.600MHz) */
+ {0x62, 0x44, 94}, /* 16 (94.500MHz) */
+ {0x2B, 0x22, 104}, /* 17 (104.998MHz) */
+ {0x49, 0x24, 105}, /* 18 (105.882MHz) */
+ {0xF8, 0x2F, 108}, /* 19 (108.279MHz) */
+ {0x3C, 0x23, 109}, /* 1A (109.175MHz) */
+ {0x5E, 0x43, 113}, /* 1B (113.309MHz) */
+ {0xBC, 0x44, 116}, /* 1C (116.406MHz) */
+ {0xE0, 0x46, 132}, /* 1D (132.258MHz) */
+ {0xD4, 0x28, 135}, /* 1E (135.220MHz) */
+ {0xEA, 0x2A, 139}, /* 1F (139.275MHz) */
+ {0x41, 0x22, 157}, /* 20 (157.500MHz) */
+ {0x70, 0x24, 162}, /* 21 (161.793MHz) */
+ {0x30, 0x21, 175}, /* 22 (175.000MHz) */
+ {0x4E, 0x22, 189}, /* 23 (188.520MHz) */
+ {0xDE, 0x26, 194}, /* 24 (194.400MHz) */
+ {0x70, 0x07, 202}, /* 25 (202.500MHz) */
+ {0x3F, 0x03, 229}, /* 26 (229.500MHz) */
+ {0xB8, 0x06, 234}, /* 27 (233.178MHz) */
+ {0x34, 0x02, 253}, /* 28 (252.699997 MHz) */
+ {0x58, 0x04, 255}, /* 29 (254.817MHz) */
+ {0x24, 0x01, 265}, /* 2A (265.728MHz) */
+ {0x9B, 0x02, 267}, /* 2B (266.952MHz) */
+ {0x70, 0x05, 270}, /* 2C (269.65567 MHz) */
+ {0x25, 0x01, 272}, /* 2D (272.041992 MHz) */
+ {0x9C, 0x02, 277}, /* 2E (277.015MHz) */
+ {0x27, 0x01, 286}, /* 2F (286.359985 MHz) */
+ {0x3C, 0x02, 291}, /* 30 (291.132660 MHz) */
+ {0xEF, 0x0A, 292}, /* 31 (291.766MHz) */
+ {0xF6, 0x0A, 310}, /* 32 (309.789459 MHz) */
+ {0x95, 0x01, 315}, /* 33 (315.195MHz) */
+ {0xF0, 0x09, 324}, /* 34 (323.586792 MHz) */
+ {0xFE, 0x0A, 331}, /* 35 (330.615631 MHz) */
+ {0xF3, 0x09, 332}, /* 36 (332.177612 MHz) */
+ {0xEA, 0x08, 340}, /* 37 (340.477MHz) */
+ {0xE8, 0x07, 376}, /* 38 (375.847504 MHz) */
+ {0xDE, 0x06, 389}, /* 39 (388.631439 MHz) */
+ {0x52, 0x2A, 54}, /* 3A (54.000MHz) */
+ {0x52, 0x6A, 27}, /* 3B (27.000MHz) */
+ {0x62, 0x24, 70}, /* 3C (70.874991MHz) */
+ {0x62, 0x64, 70}, /* 3D (70.1048912MHz) */
+ {0xA8, 0x4C, 30}, /* 3E (30.1048912MHz) */
+ {0x20, 0x26, 33}, /* 3F (33.7499957MHz) */
+ {0x31, 0xc2, 39}, /* 40 (39.77MHz) */
+ {0x11, 0x21, 30}, /* 41 (30MHz) }// NTSC 1024X768 */
+ {0x2E, 0x48, 25}, /* 42 (25.175MHz) }// ScaleLCD */
+ {0x24, 0x46, 25}, /* 43 (25.175MHz) */
+ {0x26, 0x64, 28}, /* 44 (28.322MHz) */
+ {0x37, 0x64, 40}, /* 45 (40.000MHz) */
+ {0xA1, 0x42, 108}, /* 46 (95.000MHz) }// QVGA */
+ {0x37, 0x61, 100}, /* 47 (100.00MHz) */
+ {0x78, 0x27, 108}, /* 48 (108.200MHz) */
+ {0xBF, 0xC8, 35 }, /* 49 (35.2MHz) */
+ {0x66, 0x43, 123}, /* 4A (122.61Mhz) */
+ {0x2C, 0x61, 80 }, /* 4B (80.350Mhz) */
+ {0x3B, 0x61, 108}, /* 4C (107.385Mhz) */
+ {0x69, 0x61, 191}, /* 4D (190.96MHz ) */
+ {0x4F, 0x22, 192}, /* 4E (192.069MHz) */
+ {0x28, 0x26, 322}, /* 4F (322.273MHz) */
+ {0x5C, 0x6B, 27}, /* 50 (27.74HMz) */
+ {0x57, 0x24, 126}, /* 51 (125.999MHz) */
+ {0x5C, 0x42, 148}, /* 52 (148.5MHz) */
+ {0x42, 0x61, 120}, /* 53 (120.839MHz) */
+ {0x62, 0x61, 178}, /* 54 (178.992MHz) */
+ {0x59, 0x22, 217}, /* 55 (217.325MHz) */
+ {0x29, 0x01, 300}, /* 56 (299.505Mhz) */
+ {0x52, 0x63, 74}, /* 57 (74.25MHz) */
+ {0xFF, 0x00, 0} /* End mark */
+};
+
+#define XGI301TVDelay 0x22
+#define XGI301LCDDelay 0x12
+
+static const unsigned char TVAntiFlickList[] = {/* NTSCAntiFlicker */
+ 0x04, /* ; 0 Adaptive */
+ 0x00, /* ; 1 new anti-flicker ? */
+
+ 0x04, /* ; 0 Adaptive */
+ 0x08, /* ; 1 new anti-flicker ? */
+
+ 0x04, /* ; 0 ? */
+ 0x00 /* ; 1 new anti-flicker ? */
+};
+
+
+static const unsigned char TVEdgeList[] = {
+ 0x00, /* ; 0 NTSC No Edge enhance */
+ 0x04, /* ; 1 NTSC Adaptive Edge enhance */
+ 0x00, /* ; 0 PAL No Edge enhance */
+ 0x04, /* ; 1 PAL Adaptive Edge enhance */
+ 0x00, /* ; 0 HiTV */
+ 0x00 /* ; 1 HiTV */
+};
+
+static const unsigned long TVPhaseList[] = {
+ 0x08BAED21, /* ; 0 NTSC phase */
+ 0x00E3052A, /* ; 1 PAL phase */
+ 0x9B2EE421, /* ; 2 PAL-M phase */
+ 0xBA3EF421, /* ; 3 PAL-N phase */
+ 0xA7A28B1E, /* ; 4 NTSC 1024x768 */
+ 0xE00A831E, /* ; 5 PAL-M 1024x768 */
+ 0x00000000, /* ; 6 reserved */
+ 0x00000000, /* ; 7 reserved */
+ 0xD67BF021, /* ; 8 NTSC phase */
+ 0xE986092A, /* ; 9 PAL phase */
+ 0xA4EFE621, /* ; A PAL-M phase */
+ 0x4694F621, /* ; B PAL-N phase */
+ 0x8BDE711C, /* ; C NTSC 1024x768 */
+ 0xE00A831E /* ; D PAL-M 1024x768 */
+};
+
+static const unsigned char NTSCYFilter1[] = {
+ 0x00, 0xF4, 0x10, 0x38, /* 0 : 320x text mode */
+ 0x00, 0xF4, 0x10, 0x38, /* 1 : 360x text mode */
+ 0xEB, 0x04, 0x25, 0x18, /* 2 : 640x text mode */
+ 0xF1, 0x04, 0x1F, 0x18, /* 3 : 720x text mode */
+ 0x00, 0xF4, 0x10, 0x38, /* 4 : 320x gra. mode */
+ 0xEB, 0x04, 0x25, 0x18, /* 5 : 640x gra. mode */
+ 0xEB, 0x15, 0x25, 0xF6 /* 6 : 800x gra. mode */
+};
+
+static const unsigned char PALYFilter1[] = {
+ 0x00, 0xF4, 0x10, 0x38, /* 0 : 320x text mode */
+ 0x00, 0xF4, 0x10, 0x38, /* 1 : 360x text mode */
+ 0xF1, 0xF7, 0x1F, 0x32, /* 2 : 640x text mode */
+ 0xF3, 0x00, 0x1D, 0x20, /* 3 : 720x text mode */
+ 0x00, 0xF4, 0x10, 0x38, /* 4 : 320x gra. mode */
+ 0xF1, 0xF7, 0x1F, 0x32, /* 5 : 640x gra. mode */
+ 0xFC, 0xFB, 0x14, 0x2A /* 6 : 800x gra. mode */
+};
+
+static const unsigned char xgifb_palmn_yfilter1[] = {
+ 0x00, 0xF4, 0x10, 0x38, /* 0 : 320x text mode */
+ 0x00, 0xF4, 0x10, 0x38, /* 1 : 360x text mode */
+ 0xEB, 0x04, 0x10, 0x18, /* 2 : 640x text mode */
+ 0xF7, 0x06, 0x19, 0x14, /* 3 : 720x text mode */
+ 0x00, 0xF4, 0x10, 0x38, /* 4 : 320x gra. mode */
+ 0xEB, 0x04, 0x25, 0x18, /* 5 : 640x gra. mode */
+ 0xEB, 0x15, 0x25, 0xF6, /* 6 : 800x gra. mode */
+ 0xFF, 0xFF, 0xFF, 0xFF /* End of Table */
+};
+
+static const unsigned char xgifb_yfilter2[] = {
+ 0xFF, 0x03, 0x02, 0xF6, 0xFC, 0x27, 0x46, /* 0 : 320x text mode */
+ 0x01, 0x02, 0xFE, 0xF7, 0x03, 0x27, 0x3C, /* 1 : 360x text mode */
+ 0xFF, 0x03, 0x02, 0xF6, 0xFC, 0x27, 0x46, /* 2 : 640x text mode */
+ 0x01, 0x02, 0xFE, 0xF7, 0x03, 0x27, 0x3C, /* 3 : 720x text mode */
+ 0xFF, 0x03, 0x02, 0xF6, 0xFC, 0x27, 0x46, /* 4 : 320x gra. mode */
+ 0xFF, 0x03, 0x02, 0xF6, 0xFC, 0x27, 0x46, /* 5 : 640x gra. mode */
+ 0x01, 0x01, 0xFC, 0xF8, 0x08, 0x26, 0x38, /* 6 : 800x gra. mode */
+ 0xFF, 0xFF, 0xFC, 0x00, 0x0F, 0x22, 0x28 /* 7 : 1024xgra. mode */
+};
+
+static const unsigned char XGI_NTSC1024AdjTime[] = {
+ 0xa7, 0x07, 0xf2, 0x6e, 0x17, 0x8b, 0x73, 0x53,
+ 0x13, 0x40, 0x34, 0xF4, 0x63, 0xBB, 0xCC, 0x7A,
+ 0x58, 0xe4, 0x73, 0xd0, 0x13
+};
+
+static const struct XGI301C_Tap4TimingStruct xgifb_tap4_timing[] = {
+ {0, {
+ 0x00, 0x20, 0x00, 0x00, 0x7F, 0x20, 0x02, 0x7F, /* ; C0-C7 */
+ 0x7D, 0x20, 0x04, 0x7F, 0x7D, 0x1F, 0x06, 0x7E, /* ; C8-CF */
+ 0x7C, 0x1D, 0x09, 0x7E, 0x7C, 0x1B, 0x0B, 0x7E, /* ; D0-D7 */
+ 0x7C, 0x19, 0x0E, 0x7D, 0x7C, 0x17, 0x11, 0x7C, /* ; D8-DF */
+ 0x7C, 0x14, 0x14, 0x7C, 0x7C, 0x11, 0x17, 0x7C, /* ; E0-E7 */
+ 0x7D, 0x0E, 0x19, 0x7C, 0x7E, 0x0B, 0x1B, 0x7C, /* ; EA-EF */
+ 0x7E, 0x09, 0x1D, 0x7C, 0x7F, 0x06, 0x1F, 0x7C, /* ; F0-F7 */
+ 0x7F, 0x04, 0x20, 0x7D, 0x00, 0x02, 0x20, 0x7E /* ; F8-FF */
+ }
+ }
+};
+
+static const struct XGI301C_Tap4TimingStruct PALTap4Timing[] = {
+ {600, {
+ 0x05, 0x19, 0x05, 0x7D, 0x03, 0x19, 0x06, 0x7E, /* ; C0-C7 */
+ 0x02, 0x19, 0x08, 0x7D, 0x01, 0x18, 0x0A, 0x7D, /* ; C8-CF */
+ 0x00, 0x18, 0x0C, 0x7C, 0x7F, 0x17, 0x0E, 0x7C, /* ; D0-D7 */
+ 0x7E, 0x16, 0x0F, 0x7D, 0x7E, 0x14, 0x11, 0x7D, /* ; D8-DF */
+ 0x7D, 0x13, 0x13, 0x7D, 0x7D, 0x11, 0x14, 0x7E, /* ; E0-E7 */
+ 0x7D, 0x0F, 0x16, 0x7E, 0x7D, 0x0E, 0x17, 0x7E, /* ; EA-EF */
+ 0x7D, 0x0C, 0x18, 0x7F, 0x7D, 0x0A, 0x18, 0x01, /* ; F0-F7 */
+ 0x7D, 0x08, 0x19, 0x02, 0x7D, 0x06, 0x19, 0x04 /* ; F8-FF */
+ }
+ },
+ {768, {
+ 0x08, 0x12, 0x08, 0x7E, 0x07, 0x12, 0x09, 0x7E, /* ; C0-C7 */
+ 0x06, 0x12, 0x0A, 0x7E, 0x05, 0x11, 0x0B, 0x7F, /* ; C8-CF */
+ 0x04, 0x11, 0x0C, 0x7F, 0x03, 0x11, 0x0C, 0x00, /* ; D0-D7 */
+ 0x03, 0x10, 0x0D, 0x00, 0x02, 0x0F, 0x0E, 0x01, /* ; D8-DF */
+ 0x01, 0x0F, 0x0F, 0x01, 0x01, 0x0E, 0x0F, 0x02, /* ; E0-E7 */
+ 0x00, 0x0D, 0x10, 0x03, 0x7F, 0x0C, 0x11, 0x04, /* ; EA-EF */
+ 0x7F, 0x0C, 0x11, 0x04, 0x7F, 0x0B, 0x11, 0x05, /* ; F0-F7 */
+ 0x7E, 0x0A, 0x12, 0x06, 0x7E, 0x09, 0x12, 0x07 /* ; F8-FF */
+ }
+ },
+ {0xFFFF, {
+ 0x04, 0x1A, 0x04, 0x7E, 0x02, 0x1B, 0x05, 0x7E, /* ; C0-C7 */
+ 0x01, 0x1A, 0x07, 0x7E, 0x00, 0x1A, 0x09, 0x7D, /* ; C8-CF */
+ 0x7F, 0x19, 0x0B, 0x7D, 0x7E, 0x18, 0x0D, 0x7D, /* ; D0-D7 */
+ 0x7D, 0x17, 0x10, 0x7C, 0x7D, 0x15, 0x12, 0x7C, /* ; D8-DF */
+ 0x7C, 0x14, 0x14, 0x7C, 0x7C, 0x12, 0x15, 0x7D, /* ; E0-E7 */
+ 0x7C, 0x10, 0x17, 0x7D, 0x7C, 0x0D, 0x18, 0x7F, /* ; EA-EF */
+ 0x7D, 0x0B, 0x19, 0x7F, 0x7D, 0x09, 0x1A, 0x00, /* ; F0-F7 */
+ 0x7D, 0x07, 0x1A, 0x02, 0x7E, 0x05, 0x1B, 0x02 /* ; F8-FF */
+ }
+ }
+};
+
+static const struct XGI301C_Tap4TimingStruct xgifb_ntsc_525_tap4_timing[] = {
+ {480, {
+ 0x04, 0x1A, 0x04, 0x7E, 0x03, 0x1A, 0x06, 0x7D, /* ; C0-C7 */
+ 0x01, 0x1A, 0x08, 0x7D, 0x00, 0x19, 0x0A, 0x7D, /* ; C8-CF */
+ 0x7F, 0x19, 0x0C, 0x7C, 0x7E, 0x18, 0x0E, 0x7C, /* ; D0-D7 */
+ 0x7E, 0x17, 0x10, 0x7B, 0x7D, 0x15, 0x12, 0x7C, /* ; D8-DF */
+ 0x7D, 0x13, 0x13, 0x7D, 0x7C, 0x12, 0x15, 0x7D, /* ; E0-E7 */
+ 0x7C, 0x10, 0x17, 0x7D, 0x7C, 0x0E, 0x18, 0x7E, /* ; EA-EF */
+ 0x7D, 0x0C, 0x19, 0x7E, 0x7D, 0x0A, 0x19, 0x00, /* ; F0-F7 */
+ 0x7D, 0x08, 0x1A, 0x01, 0x7E, 0x06, 0x1A, 0x02 /* ; F8-FF */
+ }
+ },
+ {600, {
+ 0x07, 0x14, 0x07, 0x7E, 0x06, 0x14, 0x09, 0x7D, /* ; C0-C7 */
+ 0x05, 0x14, 0x0A, 0x7D, 0x04, 0x13, 0x0B, 0x7E, /* ; C8-CF */
+ 0x03, 0x13, 0x0C, 0x7E, 0x02, 0x12, 0x0D, 0x7F, /* ; D0-D7 */
+ 0x01, 0x12, 0x0E, 0x7F, 0x01, 0x11, 0x0F, 0x7F, /* ; D8-DF */
+ 0x01, 0x10, 0x10, 0x00, 0x7F, 0x0F, 0x11, 0x01, /* ; E0-E7 */
+ 0x7F, 0x0E, 0x12, 0x01, 0x7E, 0x0D, 0x12, 0x03, /* ; EA-EF */
+ 0x7E, 0x0C, 0x13, 0x03, 0x7E, 0x0B, 0x13, 0x04, /* ; F0-F7 */
+ 0x7E, 0x0A, 0x14, 0x04, 0x7D, 0x09, 0x14, 0x06 /* ; F8-FF */
+ }
+ },
+ {0xFFFF, {
+ 0x09, 0x0F, 0x09, 0x7F, 0x08, 0x0F, 0x09, 0x00, /* ; C0-C7 */
+ 0x07, 0x0F, 0x0A, 0x00, 0x06, 0x0F, 0x0A, 0x01, /* ; C8-CF */
+ 0x06, 0x0E, 0x0B, 0x01, 0x05, 0x0E, 0x0B, 0x02, /* ; D0-D7 */
+ 0x04, 0x0E, 0x0C, 0x02, 0x04, 0x0D, 0x0C, 0x03, /* ; D8-DF */
+ 0x03, 0x0D, 0x0D, 0x03, 0x02, 0x0C, 0x0D, 0x05, /* ; E0-E7 */
+ 0x02, 0x0C, 0x0E, 0x04, 0x01, 0x0B, 0x0E, 0x06, /* ; EA-EF */
+ 0x01, 0x0B, 0x0E, 0x06, 0x00, 0x0A, 0x0F, 0x07, /* ; F0-F7 */
+ 0x00, 0x0A, 0x0F, 0x07, 0x00, 0x09, 0x0F, 0x08 /* ; F8-FF */
+ }
+ }
+};
+
+static const struct XGI301C_Tap4TimingStruct YPbPr750pTap4Timing[] = {
+ {0xFFFF, {
+ 0x05, 0x19, 0x05, 0x7D, 0x03, 0x19, 0x06, 0x7E, /* ; C0-C7 */
+ 0x02, 0x19, 0x08, 0x7D, 0x01, 0x18, 0x0A, 0x7D, /* ; C8-CF */
+ 0x00, 0x18, 0x0C, 0x7C, 0x7F, 0x17, 0x0E, 0x7C, /* ; D0-D7 */
+ 0x7E, 0x16, 0x0F, 0x7D, 0x7E, 0x14, 0x11, 0x7D, /* ; D8-DF */
+ 0x7D, 0x13, 0x13, 0x7D, 0x7D, 0x11, 0x14, 0x7E, /* ; E0-E7 */
+ 0x7D, 0x0F, 0x16, 0x7E, 0x7D, 0x0E, 0x17, 0x7E, /* ; EA-EF */
+ 0x7D, 0x0C, 0x18, 0x7F, 0x7D, 0x0A, 0x18, 0x01, /* ; F0-F7 */
+ 0x7D, 0x08, 0x19, 0x02, 0x7D, 0x06, 0x19, 0x04 /* F8-FF */
+ }
+ }
+};
+#endif
diff --git a/drivers/staging/xgifb/vb_util.c b/drivers/staging/xgifb/vb_util.c
new file mode 100644
index 000000000..be3437ca3
--- /dev/null
+++ b/drivers/staging/xgifb/vb_util.c
@@ -0,0 +1,42 @@
+#include "vgatypes.h"
+#include "vb_util.h"
+
+void xgifb_reg_set(unsigned long port, u8 index, u8 data)
+{
+ outb(index, port);
+ outb(data, port + 1);
+}
+
+u8 xgifb_reg_get(unsigned long port, u8 index)
+{
+ outb(index, port);
+ return inb(port + 1);
+}
+
+void xgifb_reg_and_or(unsigned long port, u8 index,
+ unsigned data_and, unsigned data_or)
+{
+ u8 temp;
+
+ temp = xgifb_reg_get(port, index); /* XGINew_Part1Port index 02 */
+ temp = (temp & data_and) | data_or;
+ xgifb_reg_set(port, index, temp);
+}
+
+void xgifb_reg_and(unsigned long port, u8 index, unsigned data_and)
+{
+ u8 temp;
+
+ temp = xgifb_reg_get(port, index); /* XGINew_Part1Port index 02 */
+ temp &= data_and;
+ xgifb_reg_set(port, index, temp);
+}
+
+void xgifb_reg_or(unsigned long port, u8 index, unsigned data_or)
+{
+ u8 temp;
+
+ temp = xgifb_reg_get(port, index); /* XGINew_Part1Port index 02 */
+ temp |= data_or;
+ xgifb_reg_set(port, index, temp);
+}
diff --git a/drivers/staging/xgifb/vb_util.h b/drivers/staging/xgifb/vb_util.h
new file mode 100644
index 000000000..9161de1d3
--- /dev/null
+++ b/drivers/staging/xgifb/vb_util.h
@@ -0,0 +1,9 @@
+#ifndef _VBUTIL_
+#define _VBUTIL_
+extern void xgifb_reg_set(unsigned long, u8, u8);
+extern u8 xgifb_reg_get(unsigned long, u8);
+extern void xgifb_reg_or(unsigned long, u8, unsigned);
+extern void xgifb_reg_and(unsigned long, u8, unsigned);
+extern void xgifb_reg_and_or(unsigned long, u8, unsigned, unsigned);
+#endif
+
diff --git a/drivers/staging/xgifb/vgatypes.h b/drivers/staging/xgifb/vgatypes.h
new file mode 100644
index 000000000..61fa10fd4
--- /dev/null
+++ b/drivers/staging/xgifb/vgatypes.h
@@ -0,0 +1,49 @@
+#ifndef _VGATYPES_
+#define _VGATYPES_
+
+#include <linux/fb.h> /* for struct fb_var_screeninfo for sis.h */
+#include "../../video/fbdev/sis/vgatypes.h"
+#include "../../video/fbdev/sis/sis.h" /* for LCD_TYPE */
+
+enum XGI_VB_CHIP_TYPE {
+ VB_CHIP_Legacy = 0,
+ VB_CHIP_301,
+ VB_CHIP_301B,
+ VB_CHIP_301LV,
+ VB_CHIP_302,
+ VB_CHIP_302B,
+ VB_CHIP_302LV,
+ VB_CHIP_301C,
+ VB_CHIP_302ELV,
+ VB_CHIP_UNKNOWN, /* other video bridge or no video bridge */
+ MAX_VB_CHIP
+};
+
+struct xgi_hw_device_info {
+ unsigned long ulExternalChip; /* NO VB or other video bridge*/
+ /* if ujVBChipID = VB_CHIP_UNKNOWN, */
+
+ void __iomem *pjVideoMemoryAddress;/* base virtual memory address */
+ /* of Linear VGA memory */
+
+ unsigned long ulVideoMemorySize; /* size, in bytes, of the
+ memory on the board */
+
+ unsigned char jChipType; /* Used to Identify Graphics Chip */
+ /* defined in the data structure type */
+ /* "XGI_CHIP_TYPE" */
+
+ unsigned char jChipRevision; /* Used to Identify Graphics
+ Chip Revision */
+
+ unsigned char ujVBChipID; /* the ID of video bridge */
+ /* defined in the data structure type */
+ /* "XGI_VB_CHIP_TYPE" */
+
+ unsigned long ulCRT2LCDType; /* defined in the data structure type */
+};
+
+/* Additional IOCTL for communication xgifb <> X driver */
+/* If changing this, xgifb.h must also be changed (for xgifb) */
+#endif
+